COPYING.TXT0000664000015300001460000000273511601155363011404 0ustar fdckermitTHE C-KERMIT 9.0 LICENSE Fri Jun 24 14:43:35 2011 Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + Neither the name of Columbia University nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ckaaaa.txt0000664000015300001460000003575711607037332011650 0ustar fdckermitckaaaa.txt July 2011 C-KERMIT VERSION 9.0.301 OVERVIEW OF FILES Communications software for UNIX and (Open)VMS. And in former versions also for: Stratus VOS, AOS/VS, QNX, Plan 9, OS-9, Apollo Aegis, and the Commodore Amiga. The Apple Macintosh, the Atari ST. The Kermit Project - Columbia University http://kermit.columbia.edu/ - kermit@columbia.edu Copyright (C) 1985, 2011, Trustees of Columbia University in the City of New York. All rights reserved. See the C-Kermit COPYING.TXT file or the copyright text in the ckcmai.c module for disclaimer and permissions. BRIEFLY: C-Kermit 9.0 has the OPEN SOURCE 3-clause MODIFIED BSD LICENSE. DOCUMENTATION C-Kermit is documented in the book "Using C-Kermit", Second Edition, by Frank da Cruz and Christine M. Gianone, Digital Press, ISBN 1-55558-164-1, supplemented by Web-based updates for C-Kermit 7.0, 8.0, and 9.0. PLATFORMS Security Name Included Last Updated Unix Yes 9.0.300 30 Jun 2011 (Open)VMS Yes 9.0.300 30 Jun 2011 Windows (K95) Yes 8.0.208 14 Mar 2003 (K95 2.1) OS/2 (K95) Yes 8.0.208 14 Mar 2003 (K95 2.1) DG AOS/VS No 7.0.196 1 Jan 2000 Stratus VOS No 7.0.196 1 Jan 2000 Bell Plan 9 No 7.0.196 1 Jan 2000 Microware OS-9 No 7.0.196 1 Jan 2000 Commodore Amiga No 7.0.196 1 Jan 2000 Macintosh No 5A(190) 16 Aug 1994 (Mac Kermit 0.991) Atari ST No 5A(189) 30 Jun 1993 QUICK START FOR FTP USERS If you have a Web browser, go to: http://www.columbia.edu/kermit/ckermit.html And take it from there. Otherwise... The definitive FTP source for Kermit software is kermit.columbia.edu. Kermit software obtained from other FTP sites is not necessarily complete or up to date, and may have been modified. C-Kermit for UNIX computers that have a C compiler and 'make' program: Directory kermit/archives, binary mode, file cku211.tar.Z or cku211.tar.gz This is a compressed tar archive of UNIX C-Kermit source code, makefile, and other files. It unpacks into its current directory, so download it into a fresh directory. Transfer in binary mode, uncompress (or gunzip), untar (tar xvf cku211.tar), and then give the appropriate "make" command to build for your UNIX system; read the comments in the makefile and ckuins.txt for further info. C-Kermit for VMS: If you have VMS UNZIP, get the file kermit/archives/ckv211.zip in binary mode, unzip -aa, and build with CKVKER.COM (@ckvker.com). Read the comments at the top of CKVKER.COM for details. Others: In the kermit/f or kermit/test directories under the appropriate prefixes, explained below. INSTALLATION Installation procedures depend on the operating system. Please read the CK?INS.TXT, if any, file for your operating system (?=U for UNIX, V for VMS, etc). Please note the naming and placement for the initialization files: CKERMIT.INI .kermrc in the user's home directory (UNIX). CKERMIT.INI in the user's home directory (other OS's). CKERMOD.INI .mykermrc in the user's home directory (UNIX). CKERMOD.INI elsewhere. DIALING DIRECTORIES Dialing directory files can be system-wide, per-group, or per-user, or any combination. For example, there can be a corporate wide directory shared by all users, a supplemental directory for each division or department, and a personal directory for each user. Simply be sure the dialing directory files are identified a SET DIAL DIRECTORY command in the user's (or the system-wide) C-Kermit initialization file, or in the environment variable (logical name, symbol) K_DIAL_DIRECTORY. (The standard initialization file looks by default in the user's home or login directory.) When installing C-Kermit on multiuser platforms from which users will dial out, you can also set environment variables for area code, country code, and the various dialing prefixes as described on page 478 of "Using C-Kermit" (second edition), so users don't have to worry about defining these items themselves. Network directories and service directories can also be set up in a similar manner. DOCUMENTATION In UNIX, the general C-Kermit man page (or one of the versions tailored for a specific platform, like HP-UX or Solaris) should be installed in the appropriate place. In VMS, the VMS help topic (CKVKER.HLP) should be installed as described in CKVINS.TXT. Plain-text documentation such as CKERMIT2.TXT should be put in whatever place people are accustomed to looking. FILES AND FILE NAMING CONVENTIONS C-Kermit is a family of Kermit programs for many different computer systems. The program shares a common set of system-independent file transfer protocol modules, written in the C language. System-dependent operations are collected into system-specific modules for each system. C-Kermit file names all start with the letters "CK", followed by a single letter indicating the subgroup. When referring to these files in the UNIX, AOS/VS, or VOS environments, use lowercase letters, rather than the uppercase letters shown here. Subgroups: _: Security/Authentication/Encryption code, possibly regulated by law a: General descriptive material and documentation b: BOO file encoders and decoders (obsolete) c: All platforms with C compilers d: Data General AOS/VS e: Reserved for "ckermit" files, like CKERMIT.INI, CKERMIT80.TXT f: (reserved) g: (reserved) h: (reserved) i: Commodore Amiga (Intuition) j: (unused) k: (unused) l: Stratus VOS m: Macintosh with Mac OS n: Microsoft Windows NT o: OS/2 and/or Microsoft Windows 95/98/ME/NT/2000/XP/... p: Bell Labs Plan 9 q: (reserved) r: DEC PDP-11 with RSTS/E (reserved) s: Atari ST GEMDOS (last supported in version 5A(189)) t: DEC PDP-11 with RT-11 (reserved) u: UNIX or environments with UNIX-like C libraries v: VMS and OpenVMS w: Wart (Lex-like preprocessor, used with all systems) x: (reserved) y: (reserved) z: (reserved) 0-3: (reserved) 4: IBM AS/400 (reserved but probably never will be used) 5-8: (reserved) 9: Microware OS-9 Examples: ckaaaa.txt - This file ckufio.c - File i/o for UNIX ckstio.c - Communications i/o for the Atari ST makefile - makefile for building UNIX C-Kermit ckpker.mk - makefile for building Plan 9 C-Kermit ckvker.com - build procedure for VMS C-Kermit IMPORTANT FILES (use lowercase names on UNIX, VOS, or AOS/VS): ckaaaa.txt - This file (overview of the C-Kermit files). For system-specific distributions, this will normally be replaced by a system-specific READ.ME file. ckermit70.txt - Updates: Supplement to "Using C-Kermit", 2nd Ed, for 7.0. ckermit80.txt - Updates: Supplement to "Using C-Kermit", 2nd Ed, for 8.0. ckututor.txt - C-Kermit Tutorial for Unix (plain text) ckcbwr.txt - "Beware file" (limitations, known bugs, hints), general. ckermit.ini - Standard initialization file (rename to .kermrc in UNIX, OS-9) ckermod.ini - Sample customization file (rename to .mykermrc in UNIX, OS-9) The following can be found at the Kermit FTP site: ckermit.kdd - Sample dialing directory file (rename to .kdd in UNIX, OS-9) ckermit.knd - Sample dialing directory file (rename to .knd in UNIX, OS-9) ckermit.ksd - Sample services directory file (rename to .ksd in UNIX, OS-9) ckedemo.ksc - Demonstration macros from "Using C-Kermit" ckepage.ksc - Ditto ckevt.ksc - Ditto UNIX-specific files: ckuins.txt - UNIX-specific installation instructions. ckubwr.txt - UNIX-specific beware file. ckuker.nr - "man page" for UNIX. VMS-specific files: ckvins.txt - VMS-specific installation instructions. ckvbwr.txt - VMS-specific beware file ckvker.hlp - VMS C-Kermit HELP topic (needs updating). DG AOS/VS-specific files: ckdins.txt - Data General AOS/VS C-Kermit installation instructions ckdbwr.txt - AOS/VS "beware" file ckd*.cli - Procedures for building AOS/VS C-Kermit The following files are of interest mainly to programmers and historians (find them at the Kermit ftp site in the kermit/f directory): ckcker.ann - Release announcements. ckccfg.txt - Configuration information (feature selection), general. ckcplm.txt - Program logic manual (for programmers). ckc300.txt - Program update history for edit 212-300 (C-Kermit 9.0). ckc211.txt - Program update history for edit 201-211. ckc200.txt - Program update history for edit 198-200 (big) ckc197.txt - Program update history for edit 195-197 (big) ckc190.txt - Program update history for edits 189-190 (big). ckc188.txt - Program update history, edits 179-188 (big). ckc178.txt - Program edit history, 5A edits through 178 (very big). ckcv4f.txt - Program edit history, version 4F. ckcv4e.txt - Program edit history, version 4E. BINARIES If you have FTP access to kermit.columbia.edu (also known as kermit.cc.columbia.edu, ftp.cc.columbia.edu), you can also retrieve various C-Kermit binaries from the directory kermit/bin/ck*.*, or more conveniently from the web page: http://www.columbia.edu/kermit/ck80binaries.html Test versions would be in kermit/test/bin/ck*.*. Be sure to transfer these files in binary mode. The READ.ME file in that directory explains what's what. SOURCE FILES The source files for the UNIX version (all UNIX versions) are available in kermit/archives/ckuNNN.tar.Z, approximately 1MB in size. Transfer this file in binary mode. This is a compressed tar archive. There is also a gzip'd version, cku211.tar.gz. To get the binary tar archive: mkdir kermit (at shell prompt, make a Kermit directory) cd kermit (make it your current directory) ftp kermit.columbia.edu (make an ftp connection) user: anonymous (log in as user "anonymous", lower case!) password: (use your email id as a password) cd kermit/archives (go to the archives directory) type binary (specify binary file transfer) get cku300.tar.Z (get the tar archive) (or get cku192.tar.gz) bye (disconnect and exit from ftp) uncompress cku300.tar.Z (at the shell prompt, uncompress the archive) tar xvf cku300.tar (extract the files from the tar archive) make xxx (build C-Kermit for your system) (where "xxx" is the makefile entry appropriate for your system.) All C-Kermit source and other text files are also kept separately in the kermit/f directory. The files necessary to build a particular implementation of C-Kermit are listed in the appropriate makefile or equivalent: UNIX: makefile (or rename ckuker.mak to makefile) 2.11 BSD: ckubs2.mak (rename to makefile), ckustr.sed Plan 9: ckpker.mk (rename to mkfile) Macintosh: ckmker.mak (rename to kermit.make, use MPW C 3.2) VMS: CKVKER.COM (DCL) (and optionally also CKVKER.MMS) or CKVOLD.COM (for VMS 4.x) Amiga: CKIKER.MAK (Aztec C) or CKISAS.MAK (SAS C) Atari ST: CKSKER.MAK OS-9: ck9ker.mak or ck9ker.gcc AOS/VS: ckdmak.cli, ckdcc.cli, ckdlnk.cli Stratus VOS: cklmak.cm Minimal source files for building selected versions (these patterns get all the files you need, and in some cases maybe a few extra): UNIX: ck[cuw]*.[cwh] (including QNX, Plan 9, and BeBox) UNIX: ck[cuw_]*.[cwh] (Unix with security modules) VMS: ck[cuwv]*.[cwh] VMS VMS: ck[cuwv_]*.[cwh] VMS with SSL/TLS Mac: ck[cuwm]*.[cwhr] Old Mac OS, not Mac OS X, which is UNIX. AOS/VS: ck[cuwd]*.[cwh] VOS: ck[cwhl]*.[cwh] Amiga: ck[cuwi]*.[cwh] Atari: ck[cuws]*.[cwh] OS-9: ck[cuw9]*.[cwha] Finally, here is a more detailed description of the C-Kermit file naming conventions. A C-Kermit filename has the form: CK. where: is described earlier in this file; is the file type (use lowercase on UNIX, VOS, or AOS/VS): c: C language source h: Header file for C language source w: Wart preprocessor source, converted by Wart (or Lex) to a C program r: Macintosh resource file (8-bit text) a: Assembler source txt: Plain text. nr: Nroff/Troff text formatter source for UNIX "man page" mss: Scribe text formatter source ps: Typeset material to be printed on a PostScript printer pdf: An Adobe PDF file hlp: A VMS Help topic ini: Initialization file ksc: A Kermit Script to be executed by the TAKE command kdd: A Kermit Dialing Directory knd: A Kermit Network Directory ksd: A Kermit Services Directory mak: A Makefile or other build procedure (often needs renaming) com: (VMS only) a DCL command procedure cli: (AOS/VS only) a command procedure cmd: (OS/2 only) a Rexx command procedure boo: "boo"-encoded executable program, decode with CKBUNB program. hex: "hex"-encoded executable program, decode with CKVDEH program (VMS only). hqx: BinHex'd Macintosh Kermit program, decode with BinHex version 4.0. uue: A uuencoded binary file, decode with uudecode or (DG only) CKDECO. def: An OS/2 linker definitions file. sh: A UNIX shell script. sed: A UNIX sed (editor) script. str: A file of character strings extracted from C-Kermit (BSD 2.1x only). is mnemonic (up to 3 characters) for what's in the file: NOTE: After C-Kermit 6.0, text filetypes such as .DOC and .HLP were changed to .TXT to avoid confusion in Windows-based Web browsers, which would otherwise mistake them for Microsoft Word or Windows Help documents. aaa: A "read-me" file, like this one ins: Installation instructions or procedures bwr: "Beware" file -- things to watch out for, hints and tips plm: Program Logic Manual ker: General C-Kermit definitions, information, documentation nnn: Digits: C-Kermit edit number (e.g. cku300.tar.gz) cmd: Command parsing con: CONNECT command cns: CONNECT command (UNIX only - version that uses select(), not fork()) deb: Debug/Transaction Log formats, Typedefs dia: Modem/Dialer control fio: System-dependent File I/O fns: Protocol support functions fn2: More protocol support functions (and FN3, ...) lib: Common library routines module mai: Main program net: Network i/o module pro: Protocol scr: SCRIPT command tel: Telnet protocol module tio: System-dependent communications i/o & control and interrupt handing sig: Signal handling module usr: Interactive/script user interface us2: More user interface (mainly help text) us3: Still more user interface (and USR4, USR5, USR6, USR7) usx: Common user interface functions usy: Command-line parsing xla: Character set translation module uni: Unicode support pty: Pseudoterminal support mdb: Malloc-debugging module (not included in real builds) str: Strings module (only for 2.xBSD) (End of ckaaaa.txt) makefile0000644000015300001460000134442311624220107011367 0ustar fdckermit# makefile / Makefile / ckuker.mak / CKUKER.MAK # # Sun Aug 21 10:33:08 2011 BUILDID=20110821 CKVER= "9.0.302" # # -- Makefile to build C-Kermit for UNIX and UNIX-like platforms -- # # Copyright (C) 1985, 2011, # Trustees of Columbia University in the City of New York. # All rights reserved. See the C-Kermit COPYING.TXT file or the # copyright text in the ckcmai.c module for disclaimer and permissions. # In case you can't find the COPYING.TXT file, it contains the # Simplified 3-Clause BSD License, which is an Open Source license. # # Author: Frank da Cruz, Columbia University # 612 West 115th Street, New York NY 10025-7799, USA # Email: fdc@columbia.edu # Web: http://kermit.columbia.edu/ # FTP: ftp://kermit.columbia.edu/kermit/ # # Note: Author is no longer at Columbia University or at the 115th Street # address effective 1 July 2011. The email address should still work, # as well as the website and FTP addresses, for the foreseeable future. # For new developments, also check: # # http://www.columbia.edu/~fdc/kermit/ # # Contributions from many others. Special thanks to Jeff Altman for the # secure-build targets, Peter Eichhorn, assyst GmbH, for the consolidated # HP-UX targets and the "uninstall" target, to Robert Lipe for the updated # and consolidated SCO UNIX / ODT / OSR5 targets, to Ric Anderson for the # IRIX 6.x targets, to Seth Theriault for major improvements to the # Mac OS X targets, and to Alexey Dokuchaev for FreeBSD 9.0. # # C-Kermit is written and produced by hand without any automated procedures # such as autoconf / automake / configure, although some of the targets below # (especially the linux target) inspect the environment and make some # decisions in the most portable way possible. The automated tools are not # used because (a) C-Kermit predates them, and (b) they are not portable to # all the platforms where C-Kermit must be (or once was) built, and (c) to # keep C-Kermit as independent as possible from external tools over which # we have no control. # # Most entries use the "xermit" target, which uses the select()-based CONNECT # module, ckucns.c. The "wermit" target uses the original fork()-based CONNECT # module, ckucon.c, which has some drawbacks but was portable to every Unix # variant whether it had TCP/IP or not (select() is part of the TCP/IP # library, which was not standard on older Unixes). If your target still uses # the "wermit" target, please try substituting the "xermit" one and if it # works, let us know (mailto:kermit-support@columbia.edu). When changing a # target over from wermit to xermit, also remove -DNOLOEARN. # # CAREFUL: Don't put the lowercase word "if", "define", or "end" as the first # word after the "#" comment introducer in the makefile, even if it is # separated by whitespace. Some versions of "make" understand these as # directives. Uppercase letters remove the danger, e.g. "# If you have..." # # WARNING: This is a huge makefile. Although this is less likely since the # turn of the century, some "make" programs might run out of memory. If this # happens to you, edit away the parts that do not apply to your platform and # try again. # # WARNING 2: In many cases this file invokes itself recursively, sometimes # several levels deep (as in the Linux targets); i.e. some targets are used # as 'subroutines' of other targets, with parameters passed by setting # environment variables. For that reason, don't use 'make -e'. # # Certain UNIX variations have their own separate makefiles: # . For 2.10 or 2.11 BSD on the DEC PDP-11, use ckubs2.mak. # . For Plan 9, use ckpker.mk. # # Separate build procedures are provided non-UNIX platforms: VMS, VOS, # AOS/VS, etc. See the ckaaaa.txt file or the Kermit website for details. # # # DIRECTIONS FOR UNIX # # Rename this file to "makefile" or "Makefile" if necessary. Pick out the # entry most appropriate for your UNIX version from the list below and then # give the appropriate "make" command, for example "make aix", "make macosx", # "make linux". If you experience any difficulties with the build procedure, # then please also read any comments that accompany the make entry itself # (search for the make entry name on the left margin). # # Other targets: # 'make install' is an installation script (read accompanying comments!). # 'make uninstall' undoes 'make install' (read accompanying comments!). # 'make clean' removes intermediate and object files. # 'make show' tells the default include and lib paths for secure builds. # # IMPORTANT: # For more detailed installation instructions, read the files ckuins.txt # and ckccfg.txt, also available at the Kermit website in HTML form: # http://www.columbia.edu/kermit/ckuins.html # http://www.columbia.edu/kermit/ckccfg.html # # For descriptions of known problems and limitations, # read the files ckcbwr.txt and ckubwr.txt (the "beware files") or: # http://www.columbia.edu/kermit/ckcbwr.html # http://www.columbia.edu/kermit/ckubwr.html # # Most targets build C-Kermit with its symbol table included. To reduce the # size of the executable program, add "LNKFLAGS=-s" to the end of your 'make' # command or to the makefile entry, or 'strip' the executable after # building. To further reduce the size after building, use 'mcs -d' if your # Unix version has such a command. For further details on size reduction, read # ckccfg.txt to find out how to remove features that you don't need. # # TCP/IP networking support: If your C-Kermit version does not include TCP/IP # networking, but your UNIX system does, try adding -DTCPSOCKET to the CFLAGS # of your makefile entry. If that doesn't work, look at some of the other # targets that include this flag for ideas about what libraries might need to # be included (typically -lsocket and/or -lBSD and/or -lnsl and/or -linet). # NOTE: In some cases (old versions of SCO or HP-UX), you might need not only # a C compiler, but also a "TCP/IP developers kit" for the required object # libraries and header files. # # Please report modifications, failures (preferably with fixes) or successes # to the author, fdc@columbia.edu. # # TARGETS FOR DIFFERENT UNIX PLATFORMS AND VERSIONS: # # + Marks those that have been built successfully for C-Kermit 9.0 or later. # - Those that once built OK but no longer do (e.g. too big). # ? Those that worked in a previous version but have not been tested recently. # -------------------------- # Some commonly used targets: # # + "make linux" should work for any version of Linux on any hardware. # + "make linux+ssl" ditto, with OpenSSL security added. # + "make linux+krb5" ditto, with Kerberos 5 security added. # + "make linux+krb5+ssl" Linux with OpenSSL and Kerberos 5. # + "make netbsd", NetBSD, any version. # + "make netbsd+ssl", NetBSD with OpenSSL 0.9.7 or later. # + "make netbsd+krb5", NetBSD with Kerberos 5. # + "make netbsd+krb5+ssl", NetBSD with Kerberos 5 and OpenSSL 0.9.7 or later. # ? "make freebsd1" for FreeBSD 1.x # ? "make freebsd2" for FreeBSD 2.x # + "make freebsd3" for FreeBSD 3.x # ? "make freebsd4" for FreeBSD 4.0 # + "make freebsd", FreeBSD 4.1 or later. # + "make freebsd+ssl", FreeBSD 5.0 or later with OpenSSL 0.9.7 or later. # + "make openbsd", OpenBSD 2.3 or later. # + "make openbsd+ssl", OpenBSD 2.3 or later with OpenSSL 0.9.7 or later. # + "make mirbsd", MirBSD. # + "make mirbsd+ssl", MirBSD with OpenSSL 0.9.7 or later. # + "make macosx" should work for any Mac OS X version 10.3.9 or later. # + "make macosx+krb5+openssl" Mac OS X 10.3.9 or later + Kerberos V + OpenSSL. # + "make aix" should work for any version of AIX 4.2 or later. # + "make aixg" should work for any version of AIX 4.2 or later, using gcc. # + "make aix+ssl" ditto, with OpenSSL (specifying SSLLIB and SSLINC) # + "make aix+ibmssl" ditto, with IBM OpenSSL # + "make solaris9", "make solaris10" for Solaris 9 or 10 with Sun cc. # + "make solaris9g", "make solaris10g" for Solaris 9 or 10 with gcc. # + "make solaris11" for Solaris 11 with Sun CC # + "make solaris11g" for Solaris 11 with gcc # + "make sco_osr600" for SCO OpenServer 6.0.0. # # For other current OSs such as Solaris, HP-UX, and SCO there are separate # targets for different combinations of OS version and compiler; see the # complete list. For older OS's see the complete list. If an old target # doesn't work in this release of C-Kermit you can get a previous release from # the Kermit FTP site: ftp://kermit.columbia.edu/kermit/ # # SECURE TARGETS (versions that support authentication and encryption) # are described after the following list. Search for ******* below. # # -------------------------- # Complete list (alphabetical): # ? for 386BSD (Jolix) 0.0, 0.1, "make 386bsd" (see comments in entry), # or (preferably, if it works) "make bsd44" or "make bsd44c". # ? for Acorn RISCiX, "make riscix" or "make riscix-gcc" # ? for Alliant FX/8 with Concentrix 4.1 or later, "make bsdlck" # ? for Altos 486, 586, 986 with Xenix 3.0, "make altos" # ? for Altos ACS68000, 8Mhz 68000, UNIX System 3 Rel 2, 512K, "make altos3" # ? for Amdahl UTS 2.4 on IBM 370 series & compatible mainframes, "make uts24" # ? for Amdahl UTSV IBM 370 series & compatible mainframes, "make utsv" # ? for Amdahl UTSV IBM 370 series mainframes with TCP/IP, "make utsvtcp" # ? for Amdahl mainframes with UNIX System V R 5.2.6b 580, "make sys3" # ? for Apollo Aegis 9.x, DOMAIN/IX 9.x, "make aegis" # (Last tested in C-Kermit 5A(189)) # ? for Apollo DOMAIN/IX, if the above fails, try "make apollobsd" # ? for Apollo with SR10.0 or later, BSD environment, "make sr10-bsd" # ? for Apollo with SR10.0 or later, System V environment, "make sr10-s5r3" # ? for Apple Macintosh II with A/UX pre-3.0, "make aux", "auxgcc" or "auxufs" # ? for Apple Macintosh with A/UX 3.0 and gcc, "make aux3gcc" or aux3gccc # ? for Apple PowerMac with MkLinux, "make mklinux" (read Linux entry first) # ? for Apple PowerMac with LinuxPPC, "make linuxppc" # ? for Apple Macintosh with Minix 1.5.10, "make minix68k" or "make minixc68" # ? for Apple Macintosh with Mac OS X 1.0 (Rhapsody), "make macosx10" # (no curses), "make macosx10c" (curses), or "make macosx10nc" (ncurses). # Or "make macosx10ncx" (ncurses but "make macosx10nc" doesn't work). # ? for Apple Macintosh with Mac OS X 10.2, "make macosx102nc" (ncurses). # ? for Apple Macintosh with Mac OS X 10.3, "make macosx103" # ? for Apple Macintosh with Mac OS X 10.3.9 or later, "make macosx" # ? for Arix System 90 with AT&T SVR3, "make sys5r3na" # - for AT&T 6300 with IN/ix, "make sys5" # - for AT&T 6300 PLUS, "make att6300" or (with no debugging) "make att6300nd" # ? for AT&T 6386 WGS UNIX PC, "make sys5r3" # ? for AT&T 3B2, 3B20 systems, "make att3b2". # for AT&T 3B1, 7300 UNIX PC (see notes with the entries): # In C-Kermit 7.0, only the gcc entries work: # ? "make sys3upcg", "make sys3upcgc", "make att351gm" # The others fail with "too many defines" (usually in ckuusr.h): # - "make sys3upc", "make sys3upcold", "make sys3upcc", "make sys3upcx", # "make sys3upcm", "make att351m" # ? for AT&T System III/System V R2 or earlier, "make sys3" or "make sys3nid" # ? for AT&T System III/System V with Honey DanBer UUCP, "make sys3hdb" # ? for AT&T System V on DEC VAX, "make sys3" or "make sys5r3" # ? for AT&T System V R3, use "make sys5r3" or "make sys5r3c" # ? for AT&T System V/386 R3.2 built on Interactive 4.1.1, "make sys5r32is". # ? for AT&T System V/386 R320.0 Versyss Systems, use "make sys5r3" # or "make sys5r3c". # ? for AT&T System V R4, "make sys5r4", "make sys5r4sx", or "make sys5r4nx", # or if the ANSI C function prototyping makes trouble, add -DNOANSI, # as in "sys5r4sxna" entry # ? for AT&T (USL) System V R4.2 use the sys5r4* entries. # ? for Atari Falcon with MiNT, "make posix" # ? for Atari ST with Minix ST 1.5.10.3, "make minix68k" or "make minixc68" # ? for BBN C/70 with IOS 2.0, "make c70" # ? for BeBox with Be OS 1.x DR7, "make beboxdr7" # Compiles OK but doesn't link with default linker which is limited to 64K. # Links OK with "Code Warrior Gold". Many hacks in the source code need # to be removed when DR8 and later come out. # (Last tested in C-Kermit 6.0) # - for BeBox with Be OS 1.x DR8, "make bebox" # (Needed functions missing from operating system and/or not working.) # - for Bell Labs UNIX Version 6 (6th Edition), there is no makefile entry. # ? for Bell Labs UNIX Version 7 (7th Edition), "make v7" (but see notes below) # (last built successfully in C-Kermit 5A188) # ? for Bell Labs Research UNIX Version 10, "make bellv10" # (last built successfully in C-Kermit 6.0) # ? for Bell Labs / Lucent Plan 9, use separate makefile ckpker.mk: # can be built for Intel, MIPS, 680x0, and PowerPC (last built C-Kermit 7.0) # ? for BSDI BSD/386 1.x, "make bsdi" # ? for BSDI BSD/OS 2.x, "make bsdi2" # ? for BSDI BSD/OS 3.0 or 3.1, "make bsdi3" # ? for BSDI BSD/OS 4.x, "make bsdi4" # ? for BSDI BSD/OS 4.x, to build a binary that also works on FreeBSD, # "make bsdix". # ? for Berkeley Unix 2.4, "make v7" (but read v7 material below) # ? for Berkeley Unix 2.9 (DEC PDP-11 or Pro-3xx), "make bsd29" # - for Berkeley Unix 2.10, use ckubs2.mak (a separate makefile) # - for Berkeley Unix 2.11, use ckubs2.mak (a separate makefile) # This makefile is too big. Read the instructions in ckubs2.mak. # "make -f ckubs2.mak bsd210" or "make -f ckubs2.mak bsd211". # (last built successfully in C-Kermit 6.0 - later versions too big) # ? for Berkeley Unix 2.11 "make -f ckubs2.mak bsd210noicp" (no command parser) # ? for Berkeley Unix 4.1, "make bsd41" # ? for Berkeley Unix 4.2 on VAX, "make bsd42" or "make bsd42c" # ? for Berkeley Unix 4.2 or 4.3 with HoneyDanBer UUCP, "make bsdhdb" # ? for Berkeley Unix 4.3 on VAX, "make bsd43", "make bsd43nc". # ? for Berkeley Unix 4.3 on VAX, no networking "make bsd43nonet. # ? for Berkeley Unix 4.3 without acucntrl program, "make bsd42" or "bsd42c" # NOTE: all the C-Kermit 7.0 full builds for old BSDs fail with # "too many defines" in CPP, even on big architectures like VAX. This # can be worked around with a clever ruse. See comments at target. # ? for Berkeley Unix 4.3, command-line only, "make bsdm". # ? for Berkeley Unix 4.3-Tahoe, same as 4.3 BSD # ? for Berkeley Unix 4.3-Reno, "make bsd43" or "make bsd44" or "make bsd44c" # ? for Berkeley Unix 4.3-Carson City, "make bsd44" or "make bsd44c" # ? for Berkeley Unix 4.4-Networking/2 or -Alpha, "make bsd44" or "make bsd44c" # ? for Berkeley Unix 4.4, "make bsd44" or "make bsd44c" # ? for Berkeley Unix 4.4-Lite, "make bsd44" or "make bsd44c" # ? for Bull DPX/2 with BOS/X, "make bulldpx2" # ? for Cadmus, "make sys3" # for Caldera, see SCO, Linux. # ? for Callan Unistar, "make sys3" # ? for CDC VX/VE 5.2.1 System V emulation, "make vxve" # ? for Charles River Data Systems Universe 680x0 with UNOS 9.2, maybe # also other UNOS versions, "make crds" # ? for CIE Systems 680/20 with Regulus, "make cie" # ? for Commodore Amiga 3000UX Sys V R4, "make sys5r4sx" # ? for Commodore Amiga 3000UX Sys V R4 and TCP/IP, "make svr4amiganet" # ? for Commodore Amiga with Minix 1.5.10, "make minix68k" of "make minixc68" # ? for Concurrent/Masscomp with RTU 4.0 or later, BSD environment, "make # rtubsd", "make rtubsd2", "make rtubsd3" (depending on where ndir.h # is stored, see entries below). # ? for Concurrent/Masscomp with RTU 4.0 or later, System V R2, "make rtus5" # ? for Concurrent (Perkin-Elmer) 3200 series, "make sys5". # ? for Concurrent (Perkin-Elmer) 3200 series with , "make ccop1" # ? for Concurrent PowerMAX OS SVR4, "make powermax" # ? for Consensys UNIX SV/386 R4V3, "make sys5r4sxtcpc" or "make sys5r4sx" # ? for Convergent with CTIX Sys V R2, "make sys5" # ? for Convergent with CTIX 6.4.1, "make ctix" # ? for Convex C1, "make convex" # ? for Convex C210 with Convex/OS 8, "make convex8" # ? for Convex C2 with Convex/OS 9.1, "make convex9" # ? for Convex C2 with Convex/OS 10.1 and gcc 2.x, "make convex10gcc" # ? for Cray Research X/MP or YMP or C90 with UNICOS 6.x (System V R3), # "make cray" # ? for Cray Research X/MP or YMP or C90 with UNICOS 7.x (System V R4), # "make cray" # ? for Cray Research X/MP or YMP or C90 with UNICOS 8.0 Alpha, "make cray8" # ? for Cray Research X/MP or Y-MP or C90 with UNICOS 9.0, "make cray9" # ? for Cray Computer Cray-2 or Cray3 with CSOS, "make craycsos" # ? for Cyber 910 (Silicon-Graphics Iris) with Irix 3.3, "irix33" # ? for Data General AViiON with DG/UX 5.4 before R3.00, "make dgux540" # or "make dgux540c" (compile ckwart separately if necessary) # ? for DG/UX 5.4 on AViiON Intel models, "make dgux540i" or dgux540ic. # ? for DG/UX 5.4R4.11 on AViiON, all models, "make dgux54411" # ? for DG/UX 5.4R4.20 on AViiON, all models, "make dgux54420" # ? for Data General AViiON with DG/UX 4.3x using Sys V-isms, "make dgux430" # ? for Data General AViiON with DG/UX 4.3x using BSD-isms, "make dgux430bsd" # ? for Data General AViiON, earlier UNIX versions, # "make sys5r3" (maybe compile ckwart separately, or "touch ckcpro.c") # ? for Data General MV systems with DG/UX, ??? # ? for Data General MV systems with MV/UX, use AOS/VS C-Kermit (CKDKER.MAK) # ? for Data General MV systems with AOS/VS, use CKDKER.MAK (last = C-K 7.0) # ? for DEC PDP-11 with Berkeley UNIX 2.x, see Berkeley UNIX 2.x. # ? for DEC PDP-11 with Mini-UNIX (Bell 6th Edition for PDP-11 with no MMU), # probably no way to fit C-Kermit without I&D space. # ? for DEC PDP-11 with Ultrix-11 3.x, ??? (probably needs overlays) # ? for DEC VAX with Ultrix 1.x "make bsd" # ? for DEC VAX with Ultrix 2.x "make ultrix2x" # ? for DEC VAX or DECstation with Ultrix 3.0, 3.1, "make ultrix3x" # ? for DECstation or VAX with Ultrix 4.0 or 4.1, "make ultrix40" # ? for DECstation or VAX with Ultrix 4.2, "make ultrix42" or "make ultrix42c" # ? for DECstation or VAX with Ultrix 4.x, POSIX world, "make posix" # ? for DECstation or VAX with Ultrix 4.3, "make ultrix43". # ? for DECstation or VAX with Ultrix 4.4, "make ultrix44". # ? for DECstation 5000/50, /150 or /260 (R4x00 MIPS CPU), Ultrix 4.3A or later # "make ultrix43-mips3" or "make ultrix43c-mips3" # ? for DECstation (MIPS) with Berkeley Sprite, "make bsd44"? # ? for DECstation (MIPS) with OSF/1 V1.0 to 1.3, "make dec-osf" # ? for DEC Alpha with OSF/1 1.0 to 1.3, "make dec-osf" # ? for DEC PC 486 with OSF/1, "make dec-osf" # ? for DEC Alpha with OSF/1 2.x, "make dec-osf20" # ? for DEC Alpha with OSF/1 3.0, "make dec-osf30" # ? for DEC Alpha with Digital UNIX 3.2, "make du32" # ? for DEC Alpha with Digital UNIX 4.0-4.0D, "make du40" or "make du40gcc" # ? for DEC Alpha with Digital UNIX 4.0E or higher, see Tru64. # + for DEC Alpha with any version of DU or OSF/1, "make dec-osf1" # - for DEC Pro-350 with Pro/Venix V1.x, "make provx1" (version 5A is too big) # ? for DEC Pro-380 with Pro/Venix V2.0 (Sys V), "make sys3" or "make sys3nid" # ? for DEC Pro-380 with 2.9, 2.10, or 2.11 BSD, "make bsd29" or "make bsd210" # for DEC PDP-11 with 2.xBSD (use separate makefile ckubs2.mak) # ? for Dell UNIX Issue 2.x (= USL Sys V/386 R4.x + fixes), "make dellsys5r4" # or "make dellsys5r4c" (last tested in C-Kermit 5A). # ? for DIAB DS90 with DNIX (any version) create an empty if # this file does not already exist (or add -DNOFILEH to the make entry). # ? for DIAB DS90 with DNIX 5.2 (Sys V.2) or earlier, "make dnix", # "make dnixnd", or (to add curses and TCP/IP) "make dnixnetc", # ? for DIAB DS90 with DNIX 5.3 (Sys V.3), "make dnix5r3" # ? for DIAB DS90 with DNIX 5.3 (Sys V.3) and TCP/IP, "make dnix5r3net" # ? for DIAB DS90 with DNIX 5.3 2.2 (Sys V.3), ANSI C, "make dnix5r3ansi" # or, to include TCP/IP, "make dnix5r3ansinet", # but you have to fix a bug in /usr/include/stdlib.h first: # change "extern void free(char *str);" to "extern void free(void *str);" # ? for Dolphin Server Technology Triton 88/17 with SV/88 R3.2, "make sv88r32" # ? for Encore Multimax 310, 510 with Umax 4.2, "make umax42" # ? for Encore Multimax 310, 510 with Umax 4.3, "make umax43" # ? for Encore Multimax 310, 510 with Umax V 2.2, use Berkeley cc, "make bsd" # ? for Encore 88K with Umax V 5.2, "make encore88k" # ? for ESIX System V R4.0.3 or 4.04 with TCP/IP support, "make esixr4" # NOTE: You can also build on Unixware 2.x with "make esixr4", and run # on ESIX, but there you must first: # ln /usr/lib/libsocket.so /usr/lib/libsocket.so.1 # ln /usr/lib/libnsl.so /usr/lib/libnsl.so.1 # (This worked for C-Kermit 6.0 but does not work for 7.0) # (But you can probably still build a non-networking version this way) # ? for Everex STEP 386/25 Rev G with ESIX Sys V R3.2D, "make sys5r3" # ? for Fortune 32:16, For:Pro 1.8, "make ft18" # ? for Fortune 32:16, For:Pro 2.1, "make ft21" # ? for FPS 500 with FPX 4.1, "made bsd" # ? for FreeBSD 1.0, "make freebsd1" # ? for FreeBSD 2.x, "make freebsd2" (ncurses) or "make freebsd2c" (curses) # ? for FreeBSD 3.x, "make freebsd3" (ncurses) or "make freebsd3c" (curses) # ? for FreeBSD 4.0, "make freebsd40" # ? for FreeBSD 4.1 or later, "make freebsd" # + NOTE: Just use "make freebsd" for any reasonably recent FreeBSD version. # ? for Harris HCX-2900, "make sys5r3" # ? for Harris Night Hawk 88K or 68K with CX/UX pre-6.1, "make sys5r3" # ? for Harris Night Hawk 88K or 68K with CX/UX 6.1 or later, "make cx_ux" # ? for Heurikon, "make sys3" # ? for HP-3000, MPE/ix, "make posix"? # ? for HP-9000 Series 300 with 4.4BSD, "make bsd44" # NOTE: Most of the HP-UX targets were tested successfully in 2010. # Verification needed for C-Kermit 9.0 Beta.01... # ? for HP-9000 Series 500, HP-UX 5.21 and no networking "make hpux0500" # ? for HP-9000 Series 500, HP-UX 5.21 with WIN/TCP 1.2 "make hpux0500wintcp" # ? for HP-9000 Series, HP-UX 6.5, without long filenames, # "make hpux0650", "make hpux0650c" or "make hpux0650tcpc" # ? for HP-9000 Series, HP-UX 7.0 or later no long filenames, "make hpux0700sf" # or (to include tcp/ip, curses, etc) "make hpux0700sftcpc" # ? for HP-9000 Series with HP-UX 7.0, TCP/IP,long filenames,"make hpux0700lfn" # ? for HP-9000 300/400 Series (680x0) with HP-UX 8.0, TCP/IP, "make hpux0800" # or "make hpux0800c" # ? for HP-9000 700/800 Series (PA-RISC), HP-UX 8.0, TCP/IP, "make hpux0800pa" # or "make hpux0800pac" # ? for HP-9000 Series with HP-UX 8.0, no TCP/IP, long filenames, # "make hpux0800notcp" # ? for HP-9000 Series, HP-UX 9.0 - 9.10, TCP/IP, curses, restricted compiler # (no optimization, no ANSI), all models, "make hpux0900". Read the # hpux0900 entry below for more info. # ? for HP-9000 700 and 800 Series, HP-UX 9.x, TCP/IP, curses, # HP optimizing ANSI C compiler, "make hpux0900o700". # ? for HP-9000 with Motorola CPUs, HP-UX 9.x, TCP/IP, curses, # HP optimizing ANSI C compiler, "make hpux0900mot". # ? for HP-9000 on other CPUs, HP-UX 9.x, TCP/IP, curses, # HP optimizing ANSI C compiler, "make hpux0900o". # ? for HP-9000 series, HP-UX 9.x, TCP/IP, curses, gcc, all models, # "make hpux0900gcc" # ? for HP-9000 700/800 Series, HP-UX 10.00,10.01,10.10,10.20,10.30, TCP/IP, # curses, restricted compiler (no optimization, no ANSI) "make hpux1000". # ? for HP-9000 700/800 Series, HP-UX 10.00,10.01,10.10,10.20,10.30, TCP/IP, # curses, HP ANSI/optimizing compiler "make hpux1000o" or "make hpux1000o+" # ? for HP-9000 HP-UX 10.00 or later with gcc, "make hpux1000gcc" # ? for Trusted HP-UX 10.xx "make hpux1000t", "make hpux1000to", # or make hpux1000to+" # ? for HP-9000 700/800 Series, HP-UX 11.00,TCP/IP,curses, restricted compiler # (no optimization, no ANSI) "make hpux1100". # ? for HP-9000 700/800 Series, HP-UX 11.00,TCP/IP,curses, restricted compiler # HP ANSI/optimizing compiler "make hpux1100o" or "make hpux1100o+" # ? for Trusted HP-UX 11.xx "make hpux1100t", "make hpux1100to", # make hpux1100to+" # ? for HP-9000 PA-RISC models with NeXTSTEP 3.3, "make nextquadfat". # ? for HP-9000 PA-RISC models with OPENSTEP/Mach 4.1, "make nextquadfat". # ? for IBM 370 Series with IX/370, "make ix370" # ? for IBM 370 Series with AIX/370 1.2, "make aix370" # ? for IBM 370 Series with AIX/370 3.0, "make aix370" # ? for IBM 370 Series with AIX/ESA 2.1, "make aixesa" # - for IBM PC/AT 286 & compatibles with Mark Williams Coherent OS, # command-line-only version, "make coherent" (version 5A & later too big) # ? for IBM PC 386 & compatibles with Mark Williams Coherent OS, # minimum interactive version, "make coherentmi" # ? for IBM PC 386 & compatibles with Mark Williams Coherent OS, # full interactive version, prior to v4.2, "make coherentmax" # ? for IBM PC 386 & compatibles with Mark Williams Coherent OS 4.2, # "make coherent42" # ? for IBM PC 386 & compatibles with LynxOS 2.0 or 2.1, "make lynx21" # ? for IBM PC 386 & compatibles with LynxOS 2.2, "make lynx" # - for IBM PC/AT & compatibles with original MINIX, "make minix" (too big) # ? for IBM PC family, 386-based, with MINIX/386 1.5, "make minix386" # or if you have GNU CC, "make minix386gcc" # ? for IBM PC family, 386-based, with MINIX 2.0, "make minix20" # ? for IBM PC family, 386-based, with MINIX 3.0, "make minix3" # + for IBM PC family, 386-based, with MINIX 3.0, "make minix315" # ? for IBM PS/2 with PS/2 AIX 1.0, 1.1, or 1.2, "make ps2aix" or ps2aixnetc. # ? for IBM PS/2 with PS/2 AIX 1.3, "make ps2aix3" # ? for IBM RISC System/6000 with AIX 3.0, "make aix30" # ? for IBM RISC System/6000 with AIX 3.1.x, "make aix31" # ? for IBM RISC System/6000 with AIX 3.2.0 thru 3.2.5, "make aix32" # ? for IBM RS/6000 or Power Series with AIX 4.1.x, "make aix41" # ? for IBM RS/6000 or Power Series with AIX 4.1.x with gcc, "make aix41g" # ? for IBM RS/6000 or Power Series with AIX 4.1 with X.25, "make aix41x25" # ? for IBM RS/6000 or Power Series with AIX 4.2 or later: "make aix" # (the following "make aixnn" targets are no longer necessary except for gcc) # ? for IBM RS/6000 or Power Series with AIX 4.2, "make aix42" # ? for IBM RS/6000 or Power Series with AIX 4.3, "make aix43" (or aix43gcc) # ? for IBM RS/6000 or Power Series with AIX 4.4, "make aix44" (or aix44gcc) # ? for IBM RS/6000 or Power Series with AIX 4.5, "make aix45" (or aix45gcc) # ? for IBM RS/6000 or Power Series with AIX 5.0, "make aix50" (or aix50gcc) # ? for IBM RS/6000 or Power Series with AIX 5.1, "make aix51" (or aix51gcc) # ? for IBM RS/6000 or Power Series with AIX 5.2, "make aix52" (or aix52gcc) # ? for IBM RS/6000 or Power Series with AIX 5.3, "make aix53" (or aix53gcc) # ? for IBM RS/6000 or Power Series with AIX 6.1, "make aix61" (or aix53gcc) # ? for IBM RT PC with AIX 2.1, "make sys3" # ? for IBM RT PC with AIX 2.2.1, "make rtaix" or "make rtaixc" # ? for IBM RT PC with ACIS 4.2, "make bsd" # ? for IBM RT PC with ACIS 4.3, "make rtacis" or "make bsd KFLAGS=-DNOANSI" # ? for IBM RT PC with 4.3BSD/Reno, "make bsd44" or "make bsd44c" # ? for ICL DRS400 or 400E, "make iclsys5r3" # ? for ICL DRS3000 (80486) with DRS/NX, "make iclsys5r4_486" # ? for ICL DRS6000 (SPARC) with DRS/NX, "make iclsys5r4" # ? for ICL DRS6000 (SPARC) with DRS/NX 4.2MP 7MPlus, "make iclsys5r4m+" # ? Ditto but with IKSD support included, "make iclsys5r4m+iksd" # ? for Integrated Solutions Inc V8S VME 68020, "make isi" # ? for Intel 302 with Bell Tech Sys V/386 R3.2, "make sys5r3" # ? for Intel Xenix/286, "make sco286" # ? for Interactive System III (PC/IX), "make pcix" or "make is3" # ? for Interactive System III (PC/IX) with gcc, "make is3gcc" # ? for Interactive 386/ix 1.0.6 with TCP/IP networking, "make is5r3net2" # ? for Interactive 386/ix 2.0.x, "make is5r3" or (POSIX) "make is5r3p" # ? for Interactive 386/ix 2.0.x with TCP/IP networking, "make is5r3net" # or "make is5r3net2" # ? for Interactive 386/ix 2.2.1, job control, curses, no net, gcc, # "make is5r3gcc" # ? for Interactive UNIX Sys V R3.2 V2.2 - 4.0 without TCP/IP, "make is5r3jc" # ? for Interactive UNIX Sys V R3.2 V2.2 - 4.0 with TCP/IP, "make is5r3netjc" # ? for Intergraph Clipper, "make clix" or "make clixnet" # ? for Jolix (see 386BSD) # + for Linux 1.2 and later, "make linux". Uses ncurses. This version # handles serial speeds up to 460800 bps, Linux FSSTD 1.2, TCP/IP, and # should work on both libc and glibc systems. For static linking, use # "make linux LNKFLAGS=-static". Please read the comments that accompany # the linux entry. As of 8.0.212 Dev.10, this also includes Large File # Support (LFS). # + for Linux builds that fail with "sys/select.h: No such file or directory", # "make linuxns" # + for Linux 1.2 and later but with curses.h and libcurses (rather than # ncurses.h and libncurses), use "make linuxc". # + for Linux 1.2 and later with no curses support at all, "make linuxnc". # + for Linux with no TCP/IP, "make linuxnotcp" # (The following Linux targets are historic and might not work...) # ? for Red Hat Linux 7.1 through RH9, fully configured (krb5, SSL, etc): # "make redhat71", "make redhat72", "make redhat73", "make redhat80" # "make redhat9" # NOTE: You must use this target for Red Hat 7.1 since it # also includes a workaround for its broken curses library. # WARNING: These targets create binaries that include code for # strong encryption and are therefore not exportable. DO NOT PUT # THESE BINARIES ON US OR CANADIAN WEB OR FTP SITES. # ? for Linux on PowerMac (Mklinux DR3), "make mklinux". # ? for Linux 1.2 and later, to build with egcs, "make linuxegcs". # ? for Linux with lcc compiler, no TCP/IP, "make linuxnotcp-lcc" # ? for Linux 1.0 or earlier, "make linux10". # (End old linux targets) # ? for Mach 2.6 on (anything, e.g. DECstation), "make bsd42" or "make bsd43". # ? for MachTen (Tenon) 2.1.1.D on (e.g.) Apple Powerbook, "make machten". # ? for Masscomp RTU AT&T System III, "make rtu" # for other Masscomp, see Concurrent. # ? for Microport SV/AT (System V R2), "make mpsysv" (last edit tested: 144) # ? for Microport SVR4 2.2, 3.1, or 4.1 "make sys5r4sx" # ? for Microsoft,IBM Xenix (/286, PC/AT, etc), "make xenix" or "make sco286" # ? for MIPS System with RISC/os (UMIPS) 4.52 = AT&T SVR3, "make mips" # or "make mipstcpc" # ? for MkLinux on Power Macintosh, "make mklinux" # ? for Modcomp 9730, Real/IX, "make sys5r3" (or modify to use gcc = GLS cc) # ? for Modcomp Realstar 1000 with REAL/IX D.1, "make sv88r32" # ? for Motorola Four Phase, "make sys3" or "make sys3nid" # ? for Motorola Delta System V/68 R3, "make sv68r3" # ? for Motorola Delta System V/68 R3V5, "make sv68r3v5" # ? for Motorola Delta System V/68 R3V5.1, "make sv68r3v51" # ? for Motorola Delta System V/68 R3V6 with NSE TCP/IP, "make sv68r3v6" # ? for Motorola Delta System V/88 R32, "make sv88r32" # ? for Motorola Delta System V/88 R40, "make sv88r40" # ? for Mt Xinu Mach386 on 386/486-based PCs, "make bsd43" # ? for NCR Tower 1632, OS 1.02, "make tower1" # ? for NCR Tower 1632 or Minitower with System V R2, "make sys3" # or "make sys3nv" # ? for NCR Tower 32, OS Release 1.xx.xx, "make tower32-1" # ? for NCR Tower 32, OS Release 2.xx.xx, "make tower32-2" # ? for NCR Tower 32, OS Releases based on Sys V R3, "make tower32" # ? for NCR Tower 32, OS Releases based on Sys V R3 with gcc "make tower32g" # ? for NCR System 3000, AT&T UNIX System V R4 2.0, "make sys5r4sxna" # ? for NCR System 3000, AT&T UNIX System V R4 2.0 with Wollongong TCP/IP, # "make sys5r4net2" or "make sys5r4net2c". # Some header files might be misplaced; try this: # ln /usr/include/netinet/in.h /usr/include/sys/in.h # ln /usr/include/arpa/inet.h /usr/include/sys/inet.h # ln /usr/include/sys/termiox.h /usr/include/termiox.h # ? for NCR System 3000, NCR UNIX 02.02.01, same as above. # ? for NCR MP-RAS System V R4 V2.03 or 3.02, "make mpras" or "make mprastcpc" # + for NetBSD any version on any architecture, "make netbsd" # + for NetBSD with OpenSSL, "make netbsd+ssl" # ? for NetBSD with ncurses specified instead of curses, "make netbsdn" # ? for NetBSD with all curses support omitted, "make netbsdnc" # ? for NeXT with NeXTSTEP 1.0 through 3.2, "make next" (on a NeXT) # ? for NeXT with NeXTSTEP 3.3, "make next33" # ? for NeXT with OPENSTEP/Mach 4.1, "make nextquadfat". # ? for NeXT with OPENSTEP/Mach 4.2, "make openstep42". # ? for NeXTSTEP/486, "make next" (on a PC) # ? for NeXTSTEP portable binary (runs on Intel or Motorola), "make nextfat" # ? for NeXTSTEP portable binary (Intel, Motorola, HP PA-RISC, or SPARC), # "make nextquadfat" # ? for Nixdorf Targon/31, "make t31tos40x" # ? for Norsk Data Uniline 88/17 with SV/88 R3.2, "make sv88r32" # for Novell UnixWare - see UnixWare # ? for OSF/1 (vanilla, from OS/F), "make posix" # ? for OkiStation 7300 Series, "make sys5r4sxtcp" # ? for Olivetti LSX-3020 with X/OS R.2.3, "make xos23" or "make xos23c" # + for OpenBSD, "make openbsd" (also see secure targets listed below). # ? for OPENSTEP/Mach 4.1, "make nextquadfat" (NeXT, Intel, PA-RISC, SPARC) # ? for OPENSTEP/Mach 4.2, "make openstep42" (tested on NeXT) # ? for Perkin-Elmer (Concurrent) 3200 series, "make sys5". # ? for Perkin-Elmer (Concurrent) 3200 series with , "make ccop1" # ? for Perkin-Elmer/Concurrent 3200 with Xelos R02, "make ccop1" # ? for PFU Compact A Series SX/A TISP V10/E50 (Japan), "make sxae50" # ? for Plexus, "make sys3" # ? for Pyramid 9XXX (e.g. 9845) or MIServer T series, OSx 4.4b thru 5.1, # "ucb make pyramid" or for HDB UUCP, "ucb make pyramid-hdb" or: # ? for Pyramid MIServer S or ES Series, DataCenter/OSx, "make pyrdcosx" # ? for Pyramid MIS-S MIPS R3000, DataCenter OSx System V R4, "make pyrdcosx" # ? for POSIX on anything, "make posix" (but adjustments might be necessary). # NOTE: this target is not very useful - many features are missing. # ? for Prime 8000 MIPS, SVR3, "make mips" or "make mipstcpc" # - for QNX 2.x (sorry we don't have a version of C-Kermit for QNX 2.x) # ? for QNX 4.0 or 4.1, 16-bit, on 286 PC, Watcom C 8.5, "make qnx16_41" # ? for QNX 4.21 - 4.22A (286+), and 4.23 (386+), or higher, 16-bit, # Watcom C 9.5x or higher, "make qnx16" # + for QNX 4.21-4.25, 32-bit, 386 or above, Watcom C 10.6, "make qnx32" # NOTE: ("make qnx" == "make qnx32") # ? for QNX Neutrino 2+, "make qnx_nto2+" (crosscompiled on QNX4 with Watcom C) # ? for QNX 6 = Neutrino 2.xx, "make qnx6" # ? for Ridge 32 (ROS3.2), "make ridge32" # ? for Samsung MagicStation, "make sys5r4" # ? for SCO Xenix 2.2.1 with development system 2.2 on 8086/8 "make sco86" # ? for SCO Xenix/286 2.2.1 with development system 2.2 on 80286, "make sco286" # NOTE: reportedly this makefile is too long for SCO Xenix/286 make, but it # works with "makeL", or if some of the other make entries are edited out. # ? for SCO Xenix/386 2.2.2, "make sco386" # ? for SCO Xenix/386 2.3.x, "make sco3r2" # ? for SCO Xenix/386 SCO 2.3.3 or 2.3.4 with gcc 1.37 or later, # "make sco386gcc" or (to add curses) "make sco386gccc". # ? for SCO Xenix/386 or UNIX/386 with Excelan TCP/IP, "make sco3r2net" # or (to add curses support) "make sco3r2netc" or "sco386netc" # + for SCO XENIX 2.3.4, "make sco234" or "make sco234c" to add curses. # ? for SCO XENIX 2.3.4 with SCO TCP/IP & curses, "make sco234netc". # ? for SCO Xenix 2.3.x with Racal-InterLan TCP/IP, "make sco3r2netri" # for other UNIX varieties with Racal Interlan TCP/IP, read sco3r2netri entry # ? for SCO Xenix 2.3.x with SCO (Lachman) TCP/IP, "make sco3r2lai" # or (to add curses) "make sco3r2laic" # for SCO UNIX... ALSO READ COMMENTS in the SCO UNIX entries for more info! # ? for SCO UNIX/386 3.2.0 or 3.2.1, "make sco3r2" or "make sco3r2x" # ? for SCO UNIX/386 3.2.2, "make sco3r22" or "make sco3r22gcc" # or "make sco3r22c" # ? for SCO UNIX/386 3.2.2 with SCO TCP/IP, "make sco3r22net" # or "make sco3r22netc" (curses) # ? for SCO ODT 1.1, "make sco3r22net" or "make sco3r22netc" (curses) # ? for SCO UNIX/386 3.2 V4.x, no network support, "make sco32v4" # ? or "make sco32v4ns" (this one uses no select() or sockets library) # ? for SCO UNIX/386 3.2 V4.x with TCP/IP, "make sco32v4net" # (also sco32v4gcc, sco32v4netgcc) # ? for SCO UNIX/386 3.2 V5.0 - see SCO OpenServer. # ? for SCO UNIX 3.2v4.x with TCP/IP, for Extended Acer File # System (EAFS), curses, ANSI C compilation, "make sco32v4net" # ? or (to use select()-based CONNECT module) "make sco32v4netx". # ? for SCO UNIX 3.2v4.2, "make sco-odt30" (includes TCP/IP). # ? for SCO MPX 3.0 - The SCO UNIX binary runs on the corresponding MPX system. # # NOTE: Also see below for other entries that are variations on these. # Also be sure to read the comments accompanying each SCO entry. # Also see Unixware section. # # ? for SCO ODT 2.0, "make sco32v4net" # ? for SCO ODT 3.0, "make sco-odt30" # ? for SCO OpenServer 5.0 (OSR5), "make sco32v500" # ? for SCO OpenServer 5.0 (OSR5) with networking, "make sco32v500net" # ? for SCO OpenServer 5.0 (OSR5), gcc, "make sco32v500gcc" # ? for SCO OpenServer 5.0 (OSR5), gcc, with networking, "make sco32v500netgcc" # ? for SCO OpenServer 5.0 (OSR5), as above, ELF, "make sco32v500netgccelf" # ? for SCO OpenServer 5.0.2, use "make sco32v502xxx" entries as above. # ? for SCO OpenServer 5.0.4, use "make sco32v504xxx" entries as above. # ? for SCO OpenServer 5.0.5, use "make sco32v505xxx" entries as above. # Use the sco32v505udkxxx entries if you have the UDK rather than /bin/cc. # ? for SCO OpenServer 5.0.6, use "make sco32v506xxx" entries as above. # ? for SCO OpenServer 5.0.6a,use "make sco32v506axxx" entries as above. # ? for SCO OpenServer 5.0.7, use "make sco32v507", "make sco32v507net" # ? for SCO (Univel) UnixWare 1.x, "make unixware" or "make unixwarenetc". # If there are problems with this in C-K 7+ see notes at unixware entry. # + for SCO OpenServer 6.0.0, "make sco_osr600" # ? for SCO UnixWare 2.0.x, "make uw20" # ? for SCO UnixWare 2.1.0, "make uw21" # ? for SCO UnixWare 2.1.3, "make uw213" # + for SCO UnixWare 7, "make uw7" (includes large file support) # ? for SCO UnixWare 7 with IKSD support, "make uw7iksd" or "make uw7iksdudk" # ? for SCO UnixWare 7 with OpenSSL, "make uw7ssl" # ? for SCO (Caldera) Open UNIX 8, "make ou8" # ? for Sharp Zaurus SL5500 PDA, "make zsl5500". # ? for Sequent with DYNIX/ptx 1.2.1, "make dynixptx12" # ? for Sequent with DYNIX/ptx 1.3 or 1.4 with TCP/IP, "make dynixptx13" # ? for Sequent with DYNIX/ptx 2.0 or 2.1 with TCP/IP, "make dynixptx20" # or "dynixptx20c" # ? for Sequent with DYNIX/ptx 2.1.6 on i486, "dynixptx216c" # ? for Sequent with DYNIX/ptx V4.1.3 with TCP/IP, "make dynixptx41c" # ? for Sequent with DYNIX/ptx V4.4.2 with TCP/IP, "make dynixptx44" # ? for Sequent Balance 8000 or B8 with DYNIX 3.0.xx, "make dynix3" # or "make dynix3noacu" # ? for Sequent Symmetry S81 with DYNIX 3.0.xx, "make dynix3" # ? for Sequent DYNIX 3.1.xx, "make dynix31" or "make dynix31c" # ? for Siemens/Nixdorf SINIX-L Intel V5.41, "make sinix541i" # + for Siemens/Nixdorf SINIX-N MIPS V5.42, "make sinix542" # ? for Siemens/Nixdorf SINIX-P MIPS V5.42 with gcc, "make sinix542g" # ? for Siemens/Nixdorf SINIX-Z Intel V5.42, "make sinix542i" # ? for Siemens/Nixdorf Reliant UNIX V5.43, "make sni543" # ? for Siemens/Nixdorf Reliant UNIX V5.44, "make sni544" # ? for Silicon Graphics Iris System V IRIX 3.2 or earlier, "make iris" # ? for Silicon Graphics Sys V R3 with IRIX 3.3 or later, "make sys5r3" # ? for Silicon Graphics Iris Indigo with IRIX 4.0 or 5.0, "make irix40" or # (to include Yellow Pages and Curses) "make irix40ypc" # ? for Silicon Graphics Iris Indigo or Elan with IRIX 4.0.x with microcode # optimization and -O4, "make irix40u" or "irix40uc" (and read notes # accompanying these entries). # ? for Silicon Graphics IRIX 5.1, "make irix51" or "irix51x" (no optimize) # ? for Silicon Graphics IRIX 5.2, "make irix52" # ? for Silicon Graphics IRIX 5.3, "make irix53" or "irix53x" (no optimize) # ? for Silicon Graphics IRIX 6.0, "make irix60". # ? for Silicon Graphics IRIX 6.2, "make irix62". # ? for Silicon Graphics IRIX 6.3, "make irix63". # ? for Silicon Graphics IRIX 6.4, "make irix64" or "make irix64gcc". # + for Silicon Graphics (SGI) IRIX 6.5, "make irix65" # + or "make irix65mips2" to force MIPS2, or "make irix65gcc" for GCC. # + for Silicon Graphics (SGI) IRIX 6.5, "make irix65" or "make irix65mips2" # ? for SGI IRIX 6.5 with SSL/TLS, SRP, and ZLIB "make irix65+ssl+srp+zlib" # ? for Solaris 2.0-2.3 on SPARC or Intel, SunPro CC, "make solaris2x", # ? or to add SunLink X.25 8.0x support, "make solaris2x25". # ? for Solaris 2.4 built with gcc, "make solaris24g". # ? for Solaris 2.0-2.3 on SPARC or Intel, GNU CC, "make solaris2xg". # ? for Solaris 2.4 with X.25, "make solaris24x25". # ? for Solaris 2.5 on SPARC or Intel, SunPro CC, "make solaris25". # ? or to add SunLink X.25 8.0x support, "make solaris25x25". # ? for Solaris 2.5 on SPARC or Intel, GNU CC, "make solaris25g". # ? for Solaris 2.6 on SPARC or Intel, "make solaris26". # ? for Solaris 7 on SPARC or Intel, SunPro CC, "make solaris7". # ? for Solaris 7 on SPARC or Intel, GNU CC, "make solaris7g". # ? for Solaris 8 on SPARC or Intel, SunPro CC, "make solaris8". # ? for Solaris 8 on SPARC or Intel, GNU CC, "make solaris8g". # + for Solaris 9 on SPARC (or Intel?), 32-bit, SunPro CC, "make solaris9". # + for Solaris 9 on SPARC (or Intel?), 32-bit, GNU CC, "make solaris9g". # ? for Solaris 9 on SPARC (or Intel?), 64-bit, GNU CC, "make solaris9g64". # + for Solaris 10 on SPARC (or Intel?), 32-bit, SunPro CC, "make solaris10". # + for Solaris 10 on SPARC 64-bit, SunPro CC, "make solaris10_64". # + for Solaris 10 on SPARC (or Intel?), 32-bit, GNU CC, "make solaris10g". # ? for Solaris 10 on SPARC (or Intel?), 64-bit, GNU CC, "make solaris10g64". # ? for Solbourne 4/500 with OS/MP 4 "make sunos4" # ? for Solbourne 4/500 with OS/MP 4.1 "make sunos41" or "make sunos41c" # ? for SONY NEWS with NEWS-OS 4.0.1C, "make sonynews" # ? for SONY NEWS with NEWS-OS 4.1.2C, "make sonynews" # ? for Sperry/UNISYS 5000/20, UTS V 5.2 3R1, "make sys5" # ? for Sperry/UNISYS 5000/30/35/50/55, UTS V 5.2 2.01, "make unisys5r2" # ? for Sperry/UNISYS 5000/80 with System V R3, "make sys5r3" # ? for Sperry/UNISYS 5000/95 with System V R3, "make sys5r3" # For UNISYS SVR3 it might be necessary to "make sys5r3 KFLAGS=-UDYNAMIC" # ? for Stardent 1520, "make sys5r3" # ? for Stratus FTX 2.x, try "make ftx" or else "make sys5r4" or "sys5r4sx" # ? for Stratus FTX 3.x, PA-RISC 1.0 or 2.0, "make ftx" or "make ftxtcp" # ? for Sun with Sun UNIX 3.5 and gcc, "make sunos3gcc" # ? for Sun with pre-4.0 SunOS versions, "make bsd" (or appropriate variant) # ? for Sun with SunOS 4.0, BSD environment, "make sunos4" # ? for Sun with SunOS 4.0, BSD, with SunLink X.25, make sunos4x25 # ? for Sun with SunOS 4.1 or 4.1.1, BSD environment, "make sunos41" # or "make sunos41c" (curses) or "make sunos41gcc" (compile with gcc) # ? for Sun with SunOS 4.1.x, BSD, with SunLink X.25 7.00 or earlier, # "make sunos41x25" or "make sunos41x25c" (curses) # ? for Sun with SunOS 4.1, 4.1.1, AT&T Sys V R3 environment, "make sunos41s5" # ? for Sun with SunOS 4.1.2, "make sunos41" or any of its variations. # NOTE: All SunOS 4.x systems -- Shared libraries are used by default. # If this causes problems, add -Bstatic to CFLAGS. # NOTE2: When building C-Kermit under SunOS for the BSD universe, # but /usr/5bin/cc is ahead of /usr/ucb/cc in your PATH, add # "CC=/usr/ucb/cc CC2=/usr/ucb/cc" to the make entry. # NOTE3: If an executable built on one type of Sun hardware does not work # on another type, rebuild the program from source on the target machine. # for Sun with Solaris 1.x use SunOS 4.1 entries. # for Sun with Solaris 2.0 and higher use Solaris entries. # + for Sun SPARC with Linux, "make linux" # ? for Sun SPARC with OPENSTEP/Mach 4.1, "make nextquadfat" # ? for Sun SPARC with OPENSTEP/Mach 4.2, "make openstep42" # - for Tandy 16/6000 with Xenix 3.0, "make trs16" (C-Kermit 7.0 is too big) # ? for Tektronix 6130/4132/43xx (e.g.4301) with UTek OS, "make utek" # or (for models without hardware flow control), "make uteknohwfc" # ? for Tektronix XD88 series with UTekV OS, "make utekvr3" # ? for Tri Star Flash Cache with Esix SVR3.2, "make sys5r3" # NOTE: The Tru64 builds have been failing since 2010, but "make dec-osf" is OK # ? for Tru64 UNIX 4.0E, "make tru64-40e" # ? for Tru64 UNIX 4.0F, "make tru64-40f" # ? for Tru64 UNIX 4.0G, "make tru64-40g" # ? for Tru64 UNIX 5.0A, "make tru64-50a" # ? for Tru64 UNIX 5.1A, "make tru64-51a" # ? for Tru64 UNIX 5.1B, "make tru64-51b" # ? for Unistar, "make sys5" # ? for Unisys S/4040 68040 CTIX SVR3.2 6.4.1, "make ctix" or "make sys5r3" # ? for Unisys U5000 UNIX SVR3 6.x, "make sys5r3" or "make sys5r3c" # ? for Unisys U6000 UNIX SVR4 1.x, "make sys5r4nx" or "make sys5r4nxnetc" # for Unisys ... (also see Sperry) # for Univel - see UnixWare # for Unixware - see SCO # ? for Valid Scaldstar, "make valid" # ? for Whitechapel MG01 Genix 1.3, "make white" # ? for Zilog ZEUS 3.21, "make zilog" # # The result should be a runnable program called "wermit" in the current # directory. After satisfactory testing, you can rename wermit to "kermit" # and put it in some directory that's in everybody's PATH, such as # /usr/local or /opt/local. # # To remove intermediate and object files, "make clean". # If your C compiler produces files with an extension other than "o", # then "make clean EXT=u", "make clean EXT=s", or whatever. # # To run lint on the source files, "make lintsun", "make lintbsd", # "make lints5", as appropriate. # # ****************************** # SECURE TARGETS # # Beginning with C-Kermit 7.0, secure targets are included, as are the # source modules (ckuat*.[ch], ck_*.[ch]) needed to build them. Secure # target names are like the regular names, but with security features # indicated by plus (+) signs. The features are: # # krb4 MIT Kerberos IV # krb5 MIT Kerberos V # openssl OpenSSL (SSL/TLS) # zlib ZLIB compression for SSL/TLS # srp Stanford Secure Remote Password # pam PAM (pluggable authentication module) # shadow Shadow Password File # # You can build these targets if you have the Kermit source files and the # required libraries (Kerberos, OpenSSL, SRP, etc) and header files. See: # http://www.columbia.edu/kermit/security.html # for specific details regarding supported versions. # # NOTE: OpenSSL 0.9.6 and earlier are not compatible with 0.9.7 and later. # C-Kermit code was originally designed for 0.9.6. To build with 0.9.7 you # must add -DOPENSSL_097 to avoid missing symbols in the DES library and to # use the entry points that were renamed to avoid conflict with Kerberos 4. # If you have OpenSSL 0.9.8, add -DOPENSSL_098, which is a synonym for # -DOPENSSL_097. If you have 1.0.0 or later, add -DOPENSSL_100, which is # another synonym. # In OpenSSL builds add -ldl if you get unresolved references for # dlopen, dlclose, dlsym, and/or dlerror. # # In order to build a secure version of Kermit, you need to know the location # of the header (include) files and libraries for the desired form of # security. Unless you specify a location, this makefile looks in /usr/local # and if the required files are not found, the build fails. # # If the secure headers and libraries are not on your computer, you have # to download and install them, for example from http://www.openssl.org . # # The following symbols are used to specify library and header file locations: # prefix = /usr/local srproot = $(prefix) sslroot = $(prefix) manroot = $(prefix) K4LIB=-L/usr/kerberos/lib K4INC=-I/usr/kerberos/include K5LIB=-L/usr/kerberos/lib K5INC=-I/usr/kerberos/include SRPLIB=-L$(srproot)/lib SRPINC=-I$(srproot)/include SSLLIB=-L$(sslroot)/ssl/lib SSLINC=-I$(sslroot)/ssl/include # To override these assignments; for example, if your OpenSSL files are # not in /usr/local/ssl, invoke the desired target like this: # # make solaris9+openssl "SSLINC=-I/opt/openssl-0.9.8k/include" \ # "SSLLIB=-L/opt/openssl-0.9.8k/lib" # # (don't set the variables and then do "make -e" because that breaks # chaining of makefile targets.) # # Here are some up-to-date secure targets as of Sep 2009: # # aix+openssl: IBM AIX 4.2 or later with OpenSSL # freebsd44+srp+openssl FreeBSD 4.4 with SRP and OpenSSL # freebsd50+openssl FreeBSD 5.0 with OpenSSL # hpux1100o+openssl: HP-UX 11.xx with OpenSSL # hpux1000gcc+openssl: HP-UX 10.xx with OpenSSL (build with gcc) # hpux1100gcc+openssl: HP-UX 11.xx with OpenSSL (build with gcc) # irix6x+krb5: IRIX 6.x with Kerberos V # irix65+krb5: etc etc... # solaris9+openssl Solaris 9,10, or 11 with Openssl (Sun cc) # solaris9g+openssl Solaris 9,10, or 11 with Openssl (gcc) # linux+ssl OpenSSL only # linux+krb5+ssl Linux with Kerberos 5 and OpenSSL # linux+krb5: Kerberos 5 only # # The following secure Linux targets have not been updated or tested recently. # linux+krb5+krb4: # linux+srp: # linux+srp+pam: # linux+srp+gmp: # linux+srp+gmp+no-des: # linux+srp+gmp-export: # linux+srp+gmp+pam: # linux+shadow+pam: # linux+openssl: # linux+openssl+shadow: # linux+openssl+zlib+shadow+pam: # linux+srp+openssl: # linux+krb5+krb4+srp: # linux+krb5+krb4+srp+openssl: # linux+krb5+krb4+openssl: # linux+krb5+krb4+openssl+shadow: # linux+krb5+krb4+openssl+zlib+shadow: # linux+krb5+krb4+srp-export: # linux+krb5+krb4+srp+pam: # linux+krb5+krb4+srp+openssl+pam-debug: # linux+krb5+krb4+srp+openssl+pam: # linux+krb5+krb4+srp+openssl+zlib+pam: # linux+krb5+krb4+openssl+shadow+pam: # linux+krb5+openssl+zlib+shadow+pam: # # The following have not been tested recently either and might # need adjustment. # # macosx+krb5+ssl: Mac OS X 10.3.9 or later + OpenSSL and Kerberos 5 # macosx103+secure: This one is probably redundant # netbsd+openssl: NetBSD with OpenSSL # openbsd30+ssl: OpenBSD 3.0 with OpenSSL # redhat71,redhat72,redhat73,redhat80,redhat9 (Krb5,OpenSSL,Showdow,PAM,Zlib) # sco32v500net+ssl: # sco32v505net+ssl: # solaris2x+krb4: # solaris2xg+krb4: # solaris2xg+openssl+pam+shadow: # solaris2xg+openssl+zlib+pam+shadow: # solaris2xg+krb5+krb4+openssl+shadow: # solaris25+krb4: # solaris25g+krb4: # solaris26g+openssl: # solaris8g+openssl+zlib+pam+shadow: # solaris8g+krb4: # solaris9g+openssl+zlib+pam+shadow: # solaris9g+openssl+shadow+pam+zlib # sunos41gcc+krb4: SunOS 4.1 built with gcc with Kerberos IV # sunos41gcc+openssl: SunOS 4.1 built with gcc with OpenSSL # sunos41gcc+krb4+openssl: ...with Kerberos IV and OpenSSL # sunos41gcc+krb4+openssl+zlib: ditto, plus ZLIB compression # sunos41gcc+krb4+srp+openssl+zlib: ditto, plus SRP # sunos41gcc+srp+openssl+zlib: # tru64-51b-openssl: Tru64 (Digital) Unix 5.1B with OpenSSL # uw7ssl Unixware 7 with SSL # ########################################################################### # # Compile and Link variables: # # EXT is the extension (file type) for object files, normally o. # See MINIX entry for what to do if another filetype must be used. # EXT=o #LNKFLAGS= SHAREDLIB= CC= cc CC2= cc MAKE= make SHELL=/bin/sh ########################################################################### # (Ancient) UNIX V7-specific variables. # These are set up for Perkin-Elmer 3230 V7 Unix: # PROC=proc DIRECT= NPROC=nproc NPTYPE=int BOOTFILE=/edition7 # # ( For old Tandy TRS-80 Model 16A or 6000 V7-based Xenix, use PROC=_proc, # DIRECT=-DDIRECT, NPROC=_Nproc, NPTYPE=short, BOOTFILE=/xenix ) # ########################################################################### # SAMPLE INSTALLATION SCRIPT # # Modify to suit your own computer's file organization and permissions. If # you don't have write access to the destination directories, "make install" # fails. In most cases, a real installation also requires you to chown / # chgrp the Kermit binary for the UUCP lockfile and/or tty devices, and # perhaps also to chmod +s the corresponding permission fields. # # Default binary, man, and doc directories are supplied below. You can # override them in your 'make' command. Examples: # # make install # Accept defaults. # make "INFODIR=/usr/share/lib/kermit" install # Override INFODIR default. # # You can also build and install in one step, e.g.: # # make linux install # # If you use the 'install' target to install C-Kermit, it creates an # UNINSTALL script that can be used to uninstall it. # WERMIT = makewhat BINARY = wermit DESTDIR = BINDIR = $(prefix)/bin MANDIR = $(manroot)/man/man1 MANEXT = 1 SRCDIR = INFODIR = CERTDIR = TEXTFILES = COPYING.TXT ckcbwr.txt ckubwr.txt ckuins.txt ckccfg.txt \ ckcplm.txt ckermit.ini ckermod.ini ckermit70.txt ckermit80.txt ALL = $(WERMIT) all: $(ALL) .c.o: $(CC) $(CFLAGS) -DKTARGET=\"$(KTARGET)\" -c $< #Clean up intermediate and object files clean: @echo 'Removing object files...' -rm -f ckcmai.$(EXT) ckucmd.$(EXT) ckuusr.$(EXT) ckuus2.$(EXT) \ ckuus3.$(EXT) ckuus4.$(EXT) ckuus5.$(EXT) ckcpro.$(EXT) ckcfns.$(EXT) \ ckcfn2.$(EXT) ckcfn3.$(EXT) ckuxla.$(EXT) ckucon.$(EXT) ckutio.$(EXT) \ ckufio.$(EXT) ckudia.$(EXT) ckuscr.$(EXT) ckwart.$(EXT) ckuusx.$(EXT) \ ckuusy.$(EXT) ckcnet.$(EXT) ckuus6.$(EXT) ckuus7.$(EXT) ckusig.$(EXT) \ ckucns.$(EXT) ckcmdb.$(EXT) ckuath.$(EXT) ckctel.$(EXT) ckclib.$(EXT) \ ckcuni.$(EXT) ck_crp.$(EXT) ck_ssl.$(EXT) ckupty.$(EXT) ckcftp.$(EXT) \ ckcpro.c wart show: @echo prefix=$(prefix) @echo srproot=$(srproot) @echo sslroot=$(sslroot) @echo manroot=$(manroot) @echo K4LIB=$(K4LIB) @echo K4INC=$(K4INC) @echo K5LIB=$(K5LIB) @echo K5INC=$(K5INC) @echo SRPLIB=$(SRPLIB) @echo SRPINC=$(SRPINC) @echo SSLLIB=$(SSLLIB) @echo SSLINC=$(SSLINC) @exit # Install C-Kermit after building -- IMPORTANT: Read the instructions above # (SAMPLE INSTALLATION SCRIPT). For SSL/TLS versions, ca_certs.pem file # should be installed in the appropriate place for your OpenSSL library, e.g.: # # cp ca_certs.pem /usr/local/ssl/ # cp ca_certs.pem /usr/share/ssl/ # # To make sure 'man' notices the new source file and doesn't keep # showing the old formatted version, remove the old formatted version, # something like this: # rm -f $(MANDIR)/../cat$(MANEXT)/kermit.$(MANEXT) # or this (which requires CATDIR to be defined): # rm -f $(CATDIR)/kermit.$(MANEXT) # # As of C-Kermit 8.0.205 this target also builds an UNINSTALL script, and # so it might be too long for some old Bourne shells, in which case you can # use a different shell: # # make SHELL=ksh install # make SHELL=/bin/posix/sh install # install: @echo Installing C-Kermit version $(CKVER)...;\ rm -f UNINSTALL;\ exec 3>./UNINSTALL;\ echo "# C-Kermit UNINSTALL script" >&3;\ echo "# `date`\n" >&3;\ echo "CKVER=$(CKVER)" >&3;\ echo "PrN Uninstalling C-Kermit version $(CKVER)..." >&3;\ echo DESTDIR=$(DESTDIR);\ if test -n "$(DESTDIR)"; then\ if test -d $(DESTDIR); then\ echo "$(DESTDIR) exists...\n";\ else\ echo "Creating $(DESTDIR)...";\ DESTDIR=`echo $(DESTDIR) | sed 's!/*$$!!'`;\ mkdir $$DESTDIR || exit 1;\ fi;\ chmod 755 $(DESTDIR) || exit 1;\ fi;\ echo BINARY=$(BINARY);\ if test -f $(BINARY); then\ ls -l $(BINARY);\ else\ echo "?$(BINARY) not found";\ exit 1;\ fi;\ if test -z "$(DESTDIR)$(BINDIR)"; then\ echo "Binary directory not specified";\ exit 1;\ fi;\ if test -d $(DESTDIR)$(BINDIR); then\ echo "$(DESTDIR)$(BINDIR) exists...";\ else\ echo "Creating $(DESTDIR)$(BINDIR)/...";\ mkdir $(DESTDIR)$(BINDIR) || exit 1;\ chmod 755 $(DESTDIR)$(BINDIR);\ fi;\ rm -f $(DESTDIR)$(BINDIR)/kermit;\ cp $(BINARY) $(DESTDIR)$(BINDIR)/kermit || exit 1;\ chmod 755 $(DESTDIR)$(BINDIR)/kermit || exit 1;\ rm -f $(DESTDIR)$(BINDIR)/kermit-sshsub;\ ln -s $(DESTDIR)$(BINDIR)/kermit\ $(DESTDIR)$(BINDIR)/kermit-sshsub || exit 1;\ echo 'set flag=f\nPrC Removing binaries' >&3;\ echo "RmF $(DESTDIR)$(BINDIR)/kermit-sshsub" >&3;\ echo "RmF $(DESTDIR)$(BINDIR)/kermit" >&3;\ if test -f ckermit.ini; then\ echo "#!$(BINDIR)/kermit" >\ $(DESTDIR)$(BINDIR)/_tmp.ini;\ cat ckermit.ini >> $(DESTDIR)$(BINDIR)/_tmp.ini;\ mv $(DESTDIR)$(BINDIR)/_tmp.ini\ $(DESTDIR)$(BINDIR)/ckermit.ini;\ chmod 755 $(DESTDIR)$(BINDIR)/ckermit.ini;\ echo "RmF $(DESTDIR)$(BINDIR)/ckermit.ini" >&3;\ fi;\ echo;\ echo 'EfM' >&3;\ echo "Kermit binary installed:";\ ls -l $(DESTDIR)$(BINDIR)/kermit\ $(DESTDIR)$(BINDIR)/kermit-sshsub\ $(DESTDIR)$(BINDIR)/ckermit.ini;\ echo;\ echo " WARNING: If C-Kermit is to be used for dialing out,";\ echo " you must change its owner and group and permissions";\ echo " to match the 'cu' program. See the ckuins.txt file";\ echo " for details.";\ echo;\ echo MANDIR=$(MANDIR);\ if test -n "$(DESTDIR)$(MANDIR)"; then\ if test -d $(DESTDIR)$(MANDIR); then\ echo "$(DESTDIR)$(MANDIR) exists...";\ else\ echo "Creating $(MANDIR)...";\ mkdir $(MANDIR) || exit 1;\ chmod 755 $(MANDIR) || exit 1;\ fi;\ rm -f $(DESTDIR)$(MANDIR)/kermit.$(MANEXT);\ cp ckuker.nr $(DESTDIR)$(MANDIR)/kermit.$(MANEXT) || exit 1;\ chmod 644 $(DESTDIR)$(MANDIR)/kermit.$(MANEXT) || exit 1;\ echo 'set flag=f\nPrC Removing man pages' >&3;\ echo "RmF $(DESTDIR)$(MANDIR)/kermit.$(MANEXT)" >&3;\ echo 'EfM' >&3;\ echo;\ else\ echo "Not installing man page!\n";\ fi;\ echo CERTDIR=$(CERTDIR);\ if test -n "$(CERTDIR)"; then\ if test -f ca_certs.pem; then\ if test -d $(CERTDIR); then\ echo "$(CERTDIR) exists...";\ else\ echo "Creating $(CERTDIR)...";\ mkdir $(CERTDIR) || exit 1;\ chmod 755 $(CERTDIR) || exit 1;\ fi;\ echo "Installing certificates file...";\ cp ca_certs.pem $(CERTDIR) || exit 1;\ echo 'set flag=f' >&3;\ echo 'PrC Removing certificates file' >&3;\ echo "RmF $(CERTDIR)/ca_certs.pem" >&3;\ echo 'EfM' >&3;\ echo;\ fi;\ else\ echo "Not installing certificates file!\n";\ fi;\ echo SRCDIR=$(DESTDIR)$(SRCDIR);\ if test -n "$(SRCDIR)"; then\ echo "Installing source files...";\ if test -d $(DESTDIR)$(SRCDIR); then\ echo "$(DESTDIR)$(SRCDIR) exists...";\ else\ echo "Creating $(DESTDIR)$(SRCDIR)/...";\ mkdir $(DESTDIR)$(SRCDIR) || exit 1;\ chmod 755 $(DESTDIR)$(SRCDIR);\ fi;\ echo "Copying source files to $(DESTDIR)$(SRCDIR)...";\ echo 'set flag=f\nPrC Removing source files' >&3;\ for TextFile in COPYING.TXT ck[cuw_]*.[cwh] makefile; do\ cp $$TextFile $(DESTDIR)$(SRCDIR)/ && echo ".\c";\ echo "RmF $(DESTDIR)$(SRCDIR)/$$TextFile" >&3;\ done; echo;\ echo 'EfM' >&3;\ ( cd $(DESTDIR)$(SRCDIR)/ &&\ ls -l COPYING.TXT ck[cuw_]*.[cwh] makefile );echo;\ else\ echo "Not installing source code!\n";\ fi;\ echo INFODIR=$(DESTDIR)$(INFODIR);\ if test -n "$(INFODIR)"; then\ echo "Installing info files...";\ if test -d $(DESTDIR)$(INFODIR); then\ echo "$(DESTDIR)$(INFODIR) exists...";\ else\ echo "Creating $(DESTDIR)$(INFODIR)/...";\ mkdir $(DESTDIR)$(INFODIR) || exit 1;\ chmod 755 $(DESTDIR)$(INFODIR);\ fi;\ echo "Copying text files to $(DESTDIR)$(INFODIR)...";\ echo 'set flag=f\nPrC Removing text files' >&3;\ FileCopyList='';\ for TextFile in $(TEXTFILES); do\ test -f $$TextFile || continue;\ cp $$TextFile $(DESTDIR)$(INFODIR) && echo ".\c" &&\ FileCopyList="$$FileCopyList $$TextFile";\ echo "RmF $(DESTDIR)$(INFODIR)/$$TextFile" >&3;\ done; echo;\ echo 'EfM' >&3;\ ( cd $(DESTDIR)$(INFODIR)/ && chmod 644 $$FileCopyList );\ ( cd $(DESTDIR)$(INFODIR)/ && pwd && ls -l $$FileCopyList );\ else\ echo "Not installing text files!\n";\ fi;\ echo "set flag=d\nPrN Removing empty dirs..." >&3;\ echo "RmD $(DESTDIR)$(BINDIR)" >&3;\ echo "RmD $(DESTDIR)$(SRCDIR)" >&3;\ echo "RmD $(DESTDIR)$(INFODIR)" >&3;\ echo "RmD $(CERTDIR)" >&3;\ echo "RmD $(MANDIR)" >&3;\ echo "RmD $(DESTDIR)" >&3;\ echo "EfM" >&3;\ echo "PrN C-Kermit version $(CKVER) is uninstalled!" >&3;\ echo C-Kermit version $(CKVER) installed! # UN-Install C-Kermit after building # Please to not remove the extra blanks before and after '{}' within the # functions. You would get syntax errors for some older Bourne shells! Best is # you don't change or remove anything. # uninstall: @if test ! -f UNINSTALL; then\ echo "?C-Kermit UNINSTALL data file not found!";\ exit 1;\ fi; \ X=`grep '^CKVER='$(CKVER)'$$' ./UNINSTALL || :`;\ if test -z "$$X"; then\ echo "?UNINSTALL file is not for C-Kermit version $(CKVER)";\ exit 2;\ fi;\ PrN () { echo "$$*"; };\ PrC () { echo "$$* \c"; };\ RmF () { test -f "$$1" && rm -f "$$1" && echo ".\c" && flag=F ; };\ RmD () { \ dir=$$1;\ while test -d "$$dir"; do\ rmdir "$$dir" 2>&- || return && echo "$$dir" && flag=D;\ dir=`echo "$$dir" | sed 's!/[^/]*/*$$!!'`;\ done; \ };\ EfM () { \ case "$$flag" in\ f) echo "- Nothing to remove!";;\ d) echo "Nothing to remove!";;\ F) echo " done";;\ D) echo "done";;\ esac; \ };\ while read Act Args; do\ case $$Act in\ EfM) EfM;;\ RmD) RmD $$Args;;\ RmF) RmF $$Args;;\ PrN) PrN $$Args;;\ PrC) PrC $$Args;;\ set) eval $$Args;;\ esac;\ done < ./UNINSTALL makewhat: @echo 'make what? You must tell which platform to make C-Kermit for.' @echo Examples: make linux, make aix, make solaris10, make hpux1100. @echo Please read the comments at the beginning of the makefile. ########################################################################### # # Dependencies Section: wermit: ckcmai.$(EXT) ckclib.$(EXT) ckucmd.$(EXT) ckuusr.$(EXT) ckuus2.$(EXT) \ ckuus3.$(EXT) ckuus4.$(EXT) ckuus5.$(EXT) ckuus6.$(EXT) \ ckuus7.$(EXT) ckuusx.$(EXT) ckuusy.$(EXT) ckcpro.$(EXT) \ ckcfns.$(EXT) ckcfn2.$(EXT) ckcfn3.$(EXT) ckuxla.$(EXT) \ ckucon.$(EXT) ckutio.$(EXT) ckufio.$(EXT) ckudia.$(EXT) \ ckuscr.$(EXT) ckcnet.$(EXT) ckctel.$(EXT) ckusig.$(EXT) \ ckcuni.$(EXT) ckupty.$(EXT) ckcftp.$(EXT) $(CC2) $(LNKFLAGS) -o wermit \ ckcmai.$(EXT) ckclib.$(EXT) ckutio.$(EXT) ckufio.$(EXT) \ ckcfns.$(EXT) ckcfn2.$(EXT) ckcfn3.$(EXT) ckuxla.$(EXT) \ ckcpro.$(EXT) ckucmd.$(EXT) ckuus2.$(EXT) ckuus3.$(EXT) \ ckuus4.$(EXT) ckuus5.$(EXT) ckuus6.$(EXT) ckuus7.$(EXT) \ ckuusx.$(EXT) ckuusy.$(EXT) ckuusr.$(EXT) ckucon.$(EXT) \ ckudia.$(EXT) ckuscr.$(EXT) ckcnet.$(EXT) ckctel.$(EXT) \ ckusig.$(EXT) ckcuni.$(EXT) ckupty.$(EXT) ckcftp.$(EXT) \ $(LIBS) # Preferred configuration with select()-based CONNECT xermit: ckcmai.$(EXT) ckclib.$(EXT) ckucmd.$(EXT) ckuusr.$(EXT) ckuus2.$(EXT) \ ckuus3.$(EXT) ckuus4.$(EXT) ckuus5.$(EXT) ckuus6.$(EXT) \ ckuus7.$(EXT) ckuusx.$(EXT) ckuusy.$(EXT) ckcpro.$(EXT) \ ckcfns.$(EXT) ckcfn2.$(EXT) ckcfn3.$(EXT) ckuxla.$(EXT) \ ckucns.$(EXT) ckutio.$(EXT) ckufio.$(EXT) ckudia.$(EXT) \ ckuscr.$(EXT) ckcnet.$(EXT) ckctel.$(EXT) ckusig.$(EXT) \ ckcuni.$(EXT) ckupty.$(EXT) ckcftp.$(EXT) ckuath.$(EXT) \ ck_crp.$(EXT) ck_ssl.$(EXT) $(CC2) $(LNKFLAGS) -o wermit \ ckcmai.$(EXT) ckclib.$(EXT) ckutio.$(EXT) ckufio.$(EXT) \ ckcfns.$(EXT) ckcfn2.$(EXT) ckcfn3.$(EXT) ckuxla.$(EXT) \ ckcpro.$(EXT) ckucmd.$(EXT) ckuus2.$(EXT) ckuus3.$(EXT) \ ckuus4.$(EXT) ckuus5.$(EXT) ckuus6.$(EXT) ckuus7.$(EXT) \ ckuusx.$(EXT) ckuusy.$(EXT) ckuusr.$(EXT) ckucns.$(EXT) \ ckudia.$(EXT) ckuscr.$(EXT) ckcnet.$(EXT) ckusig.$(EXT) \ ckctel.$(EXT) ckcuni.$(EXT) ckupty.$(EXT) ckcftp.$(EXT) \ ckuath.$(EXT) ck_crp.$(EXT) ck_ssl.$(EXT) $(LIBS) # Malloc Debugging version mermit: ckcmdb.$(EXT) ckcmai.$(EXT) ckclib.$(EXT) ckucmd.$(EXT) ckuusr.$(EXT) \ ckuus2.$(EXT) ckuus3.$(EXT) ckuus4.$(EXT) ckuus5.$(EXT) \ ckuus6.$(EXT) ckuus7.$(EXT) ckuusx.$(EXT) ckuusy.$(EXT) \ ckcpro.$(EXT) ckcfns.$(EXT) ckcfn2.$(EXT) ckcfn3.$(EXT) \ ckuxla.$(EXT) ckucon.$(EXT) ckutio.$(EXT) ckufio.$(EXT) \ ckudia.$(EXT) ckuscr.$(EXT) ckcnet.$(EXT) ckctel.$(EXT) \ ckusig.$(EXT) ckcuni.$(EXT) ckupty.$(EXT) ckcftp.$(EXT) $(CC2) $(LNKFLAGS) -o mermit ckcmdb.$(EXT) ckclib.$(EXT) ckcmai.$(EXT)\ ckutio.$(EXT) ckufio.$(EXT) ckcfns.$(EXT) ckcfn2.$(EXT) \ ckcfn3.$(EXT) ckuxla.$(EXT) ckcpro.$(EXT) ckucmd.$(EXT) \ ckuus2.$(EXT) ckuus3.$(EXT) ckuus4.$(EXT) ckuus5.$(EXT) \ ckuus6.$(EXT) ckuus7.$(EXT) ckuusx.$(EXT) ckuusy.$(EXT) \ ckuusr.$(EXT) ckucon.$(EXT) ckudia.$(EXT) ckuscr.$(EXT) \ ckcnet.$(EXT) ckctel.$(EXT) ckusig.$(EXT) ckcuni.$(EXT) \ ckupty.$(EXT) ckcftp.$(EXT) $(LIBS) # Kerberized Version - Subject to USA export restrictions. # NOTE: We don't use this any more -- As of 15 Feb 2003, the "xermit" # target is used for both secure and regular version. krbmit: ckcmai.$(EXT) ckclib.$(EXT) ckucmd.$(EXT) ckuusr.$(EXT) ckuus2.$(EXT) \ ckuus3.$(EXT) ckuus4.$(EXT) ckuus5.$(EXT) ckuus6.$(EXT) \ ckuus7.$(EXT) ckuusx.$(EXT) ckuusy.$(EXT) ckcpro.$(EXT) \ ckcfns.$(EXT) ckcfn2.$(EXT) ckcfn3.$(EXT) ckuxla.$(EXT) \ ckucns.$(EXT) ckutio.$(EXT) ckufio.$(EXT) ckudia.$(EXT) \ ckuscr.$(EXT) ckcnet.$(EXT) ckctel.$(EXT) ckusig.$(EXT) \ ckuath.$(EXT) ck_crp.$(EXT) ckcuni.$(EXT) ckupty.$(EXT) \ ckcftp.$(EXT) ck_ssl.$(EXT) $(CC2) $(LNKFLAGS) -o krbmit ckcmai.$(EXT) ckclib.$(EXT) \ ckutio.$(EXT) ckufio.$(EXT) ckcfns.$(EXT) ckcfn2.$(EXT) \ ckcfn3.$(EXT) ckuxla.$(EXT) ckcpro.$(EXT) ckucmd.$(EXT) \ ckuus2.$(EXT) ckuus3.$(EXT) ckuus4.$(EXT) ckuus5.$(EXT) \ ckuus6.$(EXT) ckuus7.$(EXT) ckuusx.$(EXT) ckuusy.$(EXT) \ ckuusr.$(EXT) ckucns.$(EXT) ckudia.$(EXT) ckuscr.$(EXT) \ ckcnet.$(EXT) ckctel.$(EXT) ckusig.$(EXT) ckuath.$(EXT) \ ck_crp.$(EXT) ckcuni.$(EXT) ckupty.$(EXT) ckcftp.$(EXT) \ ck_ssl.$(EXT) $(LIBS) krbmit-debug: ckcmai.$(EXT) ckclib.$(EXT) ckucmd.$(EXT) ckuusr.$(EXT) \ ckuus2.$(EXT) ckuus3.$(EXT) ckuus4.$(EXT) ckuus5.$(EXT) \ ckuus6.$(EXT) ckuus7.$(EXT) ckuusx.$(EXT) ckuusy.$(EXT) \ ckcpro.$(EXT) ckcfns.$(EXT) ckcfn2.$(EXT) ckcfn3.$(EXT) \ ckuxla.$(EXT) ckucns.$(EXT) ckutio.$(EXT) ckufio.$(EXT) \ ckudia.$(EXT) ckuscr.$(EXT) ckcnet.$(EXT) ckctel.$(EXT) \ ckusig.$(EXT) ckuath.$(EXT) ck_crp.$(EXT) ckcuni.$(EXT) \ ckupty.$(EXT) ck_ssl.$(EXT) ckcmdb.$(EXT) ckcftp.$(EXT) $(CC2) $(LNKFLAGS) -o krbmit ckcmdb.$(EXT) ckcmai.$(EXT) \ ckclib.$(EXT) ckutio.$(EXT) ckufio.$(EXT) ckcfns.$(EXT) \ ckcfn2.$(EXT) ckcfn3.$(EXT) ckuxla.$(EXT) ckcpro.$(EXT) \ ckucmd.$(EXT) ckuus2.$(EXT) ckuus3.$(EXT) ckuus4.$(EXT) \ ckuus5.$(EXT) ckuus6.$(EXT) ckuus7.$(EXT) ckuusx.$(EXT) \ ckuusy.$(EXT) ckuusr.$(EXT) ckucns.$(EXT) ckudia.$(EXT) \ ckuscr.$(EXT) ckcnet.$(EXT) ckctel.$(EXT) ckusig.$(EXT) \ ckuath.$(EXT) ck_crp.$(EXT) ckcuni.$(EXT) ckupty.$(EXT) \ ckcftp.$(EXT) ck_ssl.$(EXT) $(LIBS) # SRP(TM) Version - Subject to USA export restrictions. srpmit: ckcmai.$(EXT) ckclib.$(EXT) ckucmd.$(EXT) ckuusr.$(EXT) ckuus2.$(EXT) \ ckuus3.$(EXT) ckuus4.$(EXT) ckuus5.$(EXT) ckuus6.$(EXT) \ ckuus7.$(EXT) ckuusx.$(EXT) ckuusy.$(EXT) ckcpro.$(EXT) \ ckcfns.$(EXT) ckcfn2.$(EXT) ckcfn3.$(EXT) ckuxla.$(EXT) \ ckucns.$(EXT) ckutio.$(EXT) ckufio.$(EXT) ckudia.$(EXT) \ ckuscr.$(EXT) ckcnet.$(EXT) ckctel.$(EXT) ckusig.$(EXT) \ ckuath.$(EXT) ck_crp.$(EXT) ckcuni.$(EXT) ckupty.$(EXT) \ ckcftp.$(EXT) ck_ssl.$(EXT) $(CC2) $(LNKFLAGS) -o srpmit ckcmai.$(EXT) ckclib.$(EXT) \ ckutio.$(EXT) ckufio.$(EXT) ckcfns.$(EXT) ckcfn2.$(EXT) \ ckcfn3.$(EXT) ckuxla.$(EXT) ckcpro.$(EXT) ckucmd.$(EXT) \ ckuus2.$(EXT) ckuus3.$(EXT) ckuus4.$(EXT) ckuus5.$(EXT) \ ckuus6.$(EXT) ckuus7.$(EXT) ckuusx.$(EXT) ckuusy.$(EXT) \ ckuusr.$(EXT) ckucns.$(EXT) ckudia.$(EXT) ckuscr.$(EXT) \ ckcnet.$(EXT) ckctel.$(EXT) ckusig.$(EXT) ckuath.$(EXT) \ ck_crp.$(EXT) ckcuni.$(EXT) ckupty.$(EXT) ck_ssl.$(EXT) \ ckcftp.$(EXT) $(LIBS) # Kerberized Version - Not subject to USA export restrictions. krbmit-export: ckcmai.$(EXT) \ ckclib.$(EXT) ckucmd.$(EXT) ckuusr.$(EXT) ckuus2.$(EXT) \ ckuus3.$(EXT) ckuus4.$(EXT) ckuus5.$(EXT) ckuus6.$(EXT) \ ckuus7.$(EXT) ckuusx.$(EXT) ckuusy.$(EXT) ckcpro.$(EXT) \ ckcfns.$(EXT) ckcfn2.$(EXT) ckcfn3.$(EXT) ckuxla.$(EXT) \ ckucns.$(EXT) ckutio.$(EXT) ckufio.$(EXT) ckudia.$(EXT) \ ckuscr.$(EXT) ckcnet.$(EXT) ckctel.$(EXT) ckusig.$(EXT) \ ckuath.$(EXT) ckcuni.$(EXT) ckupty.$(EXT) ckcftp.$(EXT) $(CC2) $(LNKFLAGS) -o krbmit-export ckcmai.$(EXT) ckclib.$(EXT) \ ckutio.$(EXT) ckufio.$(EXT) ckcfns.$(EXT) ckcfn2.$(EXT) \ ckcfn3.$(EXT) ckuxla.$(EXT) ckcpro.$(EXT) ckucmd.$(EXT) \ ckuus2.$(EXT) ckuus3.$(EXT) ckuus4.$(EXT) ckuus5.$(EXT) \ ckuus6.$(EXT) ckuus7.$(EXT) ckuusx.$(EXT) ckuusy.$(EXT) \ ckuusr.$(EXT) ckucns.$(EXT) ckudia.$(EXT) ckuscr.$(EXT) \ ckcnet.$(EXT) ckctel.$(EXT) ckusig.$(EXT) ckuath.$(EXT) \ ckcuni.$(EXT) ckupty.$(EXT) ckcftp.$(EXT) $(LIBS) # SRP(TM) Version - Not subject to USA export restrictions. srpmit-export: ckcmai.$(EXT) \ ckclib.$(EXT) ckucmd.$(EXT) ckuusr.$(EXT) ckuus2.$(EXT) \ ckuus3.$(EXT) ckuus4.$(EXT) ckuus5.$(EXT) ckuus6.$(EXT) \ ckuus7.$(EXT) ckuusx.$(EXT) ckuusy.$(EXT) ckcpro.$(EXT) \ ckcfns.$(EXT) ckcfn2.$(EXT) ckcfn3.$(EXT) ckuxla.$(EXT) \ ckucns.$(EXT) ckutio.$(EXT) ckufio.$(EXT) ckudia.$(EXT) \ ckuscr.$(EXT) ckcnet.$(EXT) ckctel.$(EXT) ckusig.$(EXT) \ ckuath.$(EXT) ckcuni.$(EXT) ckupty.$(EXT) ckcftp.$(EXT) $(CC2) $(LNKFLAGS) -o srpmit-export ckcmai.$(EXT) ckclib.$(EXT) \ ckutio.$(EXT) ckufio.$(EXT) ckcfns.$(EXT) ckcfn2.$(EXT) \ ckcfn3.$(EXT) ckuxla.$(EXT) ckcpro.$(EXT) ckucmd.$(EXT) \ ckuus2.$(EXT) ckuus3.$(EXT) ckuus4.$(EXT) ckuus5.$(EXT) \ ckuus6.$(EXT) ckuus7.$(EXT) ckuusx.$(EXT) ckuusy.$(EXT) \ ckuusr.$(EXT) ckucns.$(EXT) ckudia.$(EXT) ckuscr.$(EXT) \ ckcnet.$(EXT) ckctel.$(EXT) ckusig.$(EXT) ckuath.$(EXT) \ ckcuni.$(EXT) ckupty.$(EXT) ckcftp.$(EXT) $(LIBS) ########################################################################### # man page... # ckuker.nr: @echo This target is obsolete. @echo The ckuker.nr file no longer needs any preprocessing. ########################################################################### # Dependencies for each module... # ckcmai.$(EXT): ckcmai.c ckcker.h ckcdeb.h ckcsym.h ckcasc.h ckcnet.h ckcsig.h \ ckuusr.h ckctel.h ckclib.h ckclib.$(EXT): ckclib.c ckclib.h ckcdeb.h ckcasc.h ckcsym.h ckcpro.$(EXT): ckcpro.c ckcker.h ckcdeb.h ckcsym.h ckcasc.h ckclib.h ckcpro.c: ckcpro.w wart ckcdeb.h ckcsym.h ckcasc.h ckcker.h ckcnet.h ckctel.h \ ckclib.h ./wart ckcpro.w ckcpro.c ckcfns.$(EXT): ckcfns.c ckcker.h ckcdeb.h ckcsym.h ckcasc.h ckcxla.h ckcuni.h \ ckuxla.h ckclib.h ckcnet.h ckcfn2.$(EXT): ckcfn2.c ckcker.h ckcdeb.h ckcsym.h ckcasc.h ckcxla.h \ ckuxla.h ckctel.h ckclib.h ckcnet.h ckcuni.h ckcfn3.$(EXT): ckcfn3.c ckcker.h ckcdeb.h ckcsym.h ckcasc.h ckcxla.h \ ckuxla.h ckclib.h ckcuni.h ckuxla.$(EXT): ckuxla.c ckcker.h ckcsym.h ckcdeb.h ckcxla.h ckuxla.h ckclib.h \ ckcuni.h ckcuni.$(EXT): ckcuni.c ckcdeb.h ckcker.h ckucmd.h ckcuni.h ckcxla.h ckuxla.h ckuusr.$(EXT): ckuusr.c ckucmd.h ckcker.h ckuusr.h ckcsym.h ckcdeb.h ckcxla.h \ ckuxla.h ckcasc.h ckcnet.h ckctel.h ckclib.h ckcuni.h ckuus2.$(EXT): ckuus2.c ckucmd.h ckcker.h ckuusr.h ckcdeb.h ckcxla.h ckuxla.h \ ckcasc.h ckcnet.h ckcsym.h ckctel.h ckclib.h ckcuni.h ckuus3.$(EXT): ckuus3.c ckucmd.h ckcker.h ckuusr.h ckcdeb.h ckcxla.h ckuxla.h \ ckcasc.h ckcnet.h ckcsym.h ckctel.h ckclib.h ckcuni.h ckuus4.$(EXT): ckuus4.c ckucmd.h ckcker.h ckuusr.h ckcdeb.h ckcxla.h ckuxla.h \ ckcasc.h ckcnet.h ckuver.h ckcsym.h ckctel.h ckclib.h ckcuni.h ckuus5.$(EXT): ckuus5.c ckucmd.h ckcker.h ckuusr.h ckcdeb.h ckcasc.h ckcnet.h \ ckcsym.h ckctel.h ckclib.h ckcxla.h ckuxla.h ckcuni.h ckuus6.$(EXT): ckuus6.c ckucmd.h ckcker.h ckuusr.h ckcdeb.h ckcasc.h ckcnet.h \ ckcsym.h ckctel.h ckclib.h ckuus7.$(EXT): ckuus7.c ckucmd.h ckcker.h ckuusr.h ckcdeb.h ckcxla.h ckuxla.h \ ckcasc.h ckcnet.h ckcsym.h ckctel.h ckclib.h ckcuni.h ckuusx.$(EXT): ckuusx.c ckcker.h ckuusr.h ckcdeb.h ckcasc.h ckcsym.h \ ckcsig.h ckcnet.h ckctel.h ckclib.h ckcxla.h ckuxla.h ckcuni.h ckuusy.$(EXT): ckuusy.c ckcker.h ckcdeb.h ckcasc.h ckcnet.h ckcsym.h ckctel.h \ ckclib.h ckucmd.$(EXT): ckucmd.c ckcasc.h ckucmd.h ckcdeb.h ckcsym.h ckctel.h ckclib.h ckufio.$(EXT): ckufio.c ckcdeb.h ckuver.h ckcsym.h ckclib.h \ ckcxla.h ckuxla.h ckcuni.h ckutio.$(EXT): ckutio.c ckcdeb.h ckcnet.h ckuver.h ckcsym.h ckctel.h ckclib.h ckucon.$(EXT): ckucon.c ckcker.h ckcdeb.h ckcasc.h ckcnet.h ckcsym.h ckctel.h \ ckclib.h ckucns.$(EXT): ckucns.c ckcker.h ckcdeb.h ckcasc.h ckcnet.h ckcsym.h ckctel.h \ ckclib.h ckcxla.h ckuxla.h ckcuni.h ckcnet.$(EXT): ckcnet.c ckcdeb.h ckcker.h ckcnet.h ckcsym.h ckcsig.h ckctel.h \ ckclib.h ckctel.$(EXT): ckcsym.h ckcdeb.h ckcker.h ckcnet.h ckctel.h ckclib.h # ck_off_t: ck_off_t.$(EXT) # $(CC) -o ck_off_t ck_off_t.$(EXT) wart: ckwart.$(EXT) $(CC) $(LNKFLAGS) -o wart ckwart.$(EXT) $(LIBS) ckcmdb.$(EXT): ckcmdb.c ckcdeb.h ckcsym.h ckclib.h ckwart.$(EXT): ckwart.c ckudia.$(EXT): ckudia.c ckcker.h ckcdeb.h ckucmd.h ckcasc.h ckcsym.h ckcsig.h \ ckcnet.h ckctel.h ckclib.h ckuscr.$(EXT): ckuscr.c ckcker.h ckcdeb.h ckcasc.h ckcsym.h ckcsig.h \ ckcnet.h ckctel.h ckclib.h ckusig.$(EXT): ckusig.c ckcasc.h ckcdeb.h ckcker.h ckcnet.h ckuusr.h \ ckcsig.h ckctel.h ckclib.h ckcftp.$(EXT): ckcftp.c ckcdeb.h ckcasc.h ckcker.h ckucmd.h ckuusr.h \ ckcnet.h ckctel.h ckcxla.h ckuxla.h ckcuni.h ckupty.$(EXT): ckupty.c ckupty.h ckcdeb.h ckuath.$(EXT): ckuath.c ckcdeb.h ckucmd.h ckuath.h ckuat2.h ckctel.h \ ckclib.h ckcnet.h ck_crp.$(EXT): ck_crp.c ckcdeb.h ckcnet.h ckuath.h ckclib.h ck_ssl.$(EXT): ck_ssl.c ckcdeb.h ckucmd.h ckuath.h ckuat2.h ckctel.h \ ckclib.h ck_ssl.h ########################################################################### # # Entries to make C-Kermit for specific systems. # # Put the ones that need short makefiles first. #Apollo Aegis 9.x. Includes TCP/IP support. #You can also add processor-dependent optimization switches like -M570. aegis: @echo Making C-Kermit $(CKVER) for Apollo Aegis 9.x... $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DBSD4 -DTCPSOCKET -DNOCSETS -DCK_CURSES -O $(KFLAGS)" \ "LIBS = -lcurses -ltermcap" #Apple Mac II, A/UX pre-3.0 #Warning, if "send *" doesn't work, try the auxufs makefile entry below. aux: @echo Making C-Kermit $(CKVER) for Macintosh A/UX... $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DAUX -DTCPSOCKET $(KFLAGS) -i -O" "LNKFLAGS = -i" #Apple Mac II, A/UX pre-3.0, compiled with gcc auxgcc: @echo Making C-Kermit $(CKVER) for Macintosh A/UX... $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DAUX -DTCPSOCKET -traditional $(KFLAGS) -i -O" \ "LNKFLAGS = " "CC = gcc" "CC2 = gcc" #Apple Mac II, A/UX, pre-3.0, but with ufs file volumes, uses . auxufs: @echo Making C-Kermit $(CKVER) for Macintosh A/UX... $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DAUX -DTCPSOCKET -DDIRENT $(KFLAGS) -i -O" "LNKFLAGS = -i" #Apple Mac II, A/UX 3.0, compiled with gcc aux3gcc: @echo Making C-Kermit $(CKVER) for Macintosh A/UX 3.0... $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DAUX -DHDBUUCP -DLFDEVNO -DTCPSOCKET -DDIRENT $(KFLAGS)" \ "LNKFLAGS = -s" "LIBS = $(LIBS)" \ "CC=gcc -pipe -traditional" "CC2=gcc -pipe -traditional" #Apple Mac II, A/UX 3.0, compiled with gcc, uses curses aux3cgcc: @echo Making C-Kermit $(CKVER) for Macintosh A/UX 3.0... $(MAKE) "MAKE=$(MAKE)" CC=$(CC) CC2=$(CC2) aux3gcc \ KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=$(KFLAGS) -DCK_CURSES" "LIBS = -lcurses $(LIBS)" # Tenon MachTen, tested on Apple Powerbook with MachTen 2.1.1.D. # NOTE: This doesn't do anything about UUCP. It only works if /usr/spool/uucp # has permission of 777, and dialout device is world read/writeable. machten: @echo Making C-Kermit $(CKVER) for MachTen... $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DBSD43 -DTCPSOCKET -DSIG_V -DNDGPWNAM -DCK_CURSES -O \ $(KFLAGS)" "LIBS=-lcurses -ltermcap" #Bell Labs Research UNIX V10 #Can't add TCP/IP because there is no sockets library. It would have to #be done using streams, but there is no code in C-Kermit for that. #Remove -DNOJC if desired (if your system has csh, ksh, or bash). bellv10: @echo Making C-Kermit $(CKVER) for Bell Labs Research UNIX V10... $(MAKE) wermit KTARGET=$${KTARGET-$(@)} \ "CFLAGS= -DBELLV10 -DBSD4 -DNDIR -DNOJC -DNOSYSIOCTLH -DNOSETREU \ -DNOCSETS -MINIDIAL $(KFLAGS)" # WARNING: The early BSD entries do not build in version 7.0 with the stock # BSD compiler: "Too many defines". Unless you can rebuild cpp to have more # space for defines, these builds must be accomplished by: # copying the /usr/include tree to someplace else, preprocessing there with cc # -E -I./include or whatever (plus all the same -D's, adding any necessary # -U/-D to override the architecture)), renaming the the resulting files back # to their original names, bringing them back to the original BSD system, and # running the make target there. This technique was used for 4.2 and 4.3 BSD # on a VAX in C-Kermit 7.0 (later, cpp on that machine was rebuilt to allow # more symbols, so the C-Kermit 8.0 build proceeds normally). #Berkeley Unix 4.1 bsd41: @echo Making C-Kermit $(CKVER) for 4.1BSD... $(MAKE) wermit KTARGET=$${KTARGET-$(@)} \ "CFLAGS= -DBSD41" "LIBS = -ljobs" #Berkeley 4.2, 4.3, also Ultrix-32 1.x, 2.x, 3.x, many others # Add -O, -s, etc, if they work. # If you have a version of BSD but signal() is void rather than int, # "make bsd KFLAGS=-DSIG_V". bsd42: @echo Making C-Kermit $(CKVER) for 4.2BSD... $(MAKE) xermit KTARGET=$${KTARGET-$(@)} \ "CFLAGS= -DBSD4 -DTCPSOCKET -DNOREALPATH -DNOTIMEH -DNOIKSD \ -DCK_CURSES -DSYSTIMEBH -DNOPUTENV -DNOANSI -DBIGBUFOK -DBSD42HACK \ $(KFLAGS)" "LIBS=-lcurses -ltermcap $(LIBS)" bsd: $(MAKE) CC=$(CC) CC2=$(CC2) bsd42 KTARGET=$${KTARGET-$(@)} #Berkeley Unix 4.2 or 4.3 with HoneyDanBer UUCP bsdhdb: @echo Making C-Kermit $(CKVER) for 4.2BSD with HDB UUCP... $(MAKE) CC=$(CC) CC2=$(CC2) bsd KTARGET=$${KTARGET-$(@)} \ "KFLAGS= -DHDBUUCP $(KFLAGS)" #Berkeley Unix 4.3 with acucntrl program, curses, TCP/IP included. bsd43: @echo Making C-Kermit $(CKVER) for 4.3BSD... $(MAKE) xermit KTARGET=$${KTARGET-$(@)} \ "CFLAGS= -DBSD4 -DBSD43 -DTCPSOCKET -DNOREALPATH -DNOTIMEH -DNOIKSD \ -DCK_CURSES -DACUCNTRL -DSYSTIMEBH -DNOPUTENV -DNOANSI -DBIGBUFOK \ -DBSD42HACK $(KFLAGS)" "LIBS=-lcurses -ltermcap $(LIBS)" #4.3BSD, curses excluded bsd43nc: @echo Making C-Kermit $(CKVER) for 4.3BSD... $(MAKE) xermit KTARGET=$${KTARGET-$(@)} \ "CFLAGS= -DBSD4 -DBSD43 -DTCPSOCKET -DNOREALPATH -DNOTIMEH \ -DACUCNTRL -DSYSTIMEBH -DNOIKSD -DNOPUTENV -DNOANSI -DBIGBUFOK \ -DBSD42HACK $(KFLAGS)" "LIBS=$(LIBS)" #4.3BSD, TCP/IP excluded. bsd43nonet: @echo Making C-Kermit $(CKVER) for 4.3BSD + curses... $(MAKE) xermit KTARGET=$${KTARGET-$(@)} \ "CFLAGS= -DBSD4 -DBSD43 -DTCPSOCKET -DNOREALPATH -DNOTIMEH -DNOIKSD \ -DCK_CURSES -DACUCNTRL -DSYSTIMEBH -DNOPUTENV -DNOANSI -DBIGBUFOK \ -DBSD42HACK -DNONET $(KFLAGS)" "LIBS=-lcurses -ltermcap $(LIBS)" #Berkeley Unix 4.2 or 4.3 with lock directory /usr/spool/uucp/LCK/LCK..ttyxx, #but without acucntrl program bsdlck: @echo Making C-Kermit $(CKVER) for 4.2BSD, /usr/spool/uucp/LCK/... $(MAKE) CC=$(CC) CC2=$(CC2) bsd KTARGET=$${KTARGET-$(@)} \ "KFLAGS= -DLCKDIR $(KFLAGS)" #Berkeley UNIX 4.4-Lite, 4.4-Encumbered, Net/2, etc (Post-Reno), #with TCP/IP networking. This was the basis for FreeBSD, NetBSD, OpenBSD, #BSDI, BSD/OS, and Mac OS X (each of which has its own set of targets that #are newer than this one). # #NOTE: This is not a pure POSIX configuration. Using -DPOSIX instead of # -DBSD44 prevents any kind of directory-reading (for wildcard expansion), #and disallows use of ENOTCONN symbol for detecting broken network #connections, and disallows RTS/CTS flow control, and would also require #definition of the appropriate UUCP lockfile convention. #Do not add -DCK_POSIX_SIG without reading first! For example, #sigsetjmp(), etc, tend to be defined but not implemented. # #NOTE: originally crypt was in libc - later it was unbundled. #Remove the LIBS clause to build on an early 4.4BSD platform. # bsd44: @echo Making C-Kermit $(CKVER) for 4.4BSD... $(MAKE) CC=$(CC) CC2=$(CC2) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DBSD44 -DTCPSOCKET $(KFLAGS) -O" "LIBS=-lcrypt" #Berkeley UNIX 4.4, as above, but with curses for fullscreen display #Please read notes for bsd44 entry just above. # NOTE: This one dumped core on the real 4.4BSD development system at # UC Berkeley (an HP-9000/300), so the no-curses version was used # for that one, which was unplugged years ago. bsd44c: @echo Making C-Kermit $(CKVER) for 4.4BSD with curses... $(MAKE) CC=$(CC) CC2=$(CC2) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DBSD44 -DCK_CURSES -DTCPSOCKET $(KFLAGS) -O" \ "LIBS= -lcurses -ltermcap -lcrypt $(LIBS)" #For FreeBSD 1.x. freebsd1: @echo 'Making C-Kermit $(CKVER) for FreeBSD...' $(MAKE) CC=$(CC) CC2=$(CC2) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DBSD44 -DCK_CURSES -DTCPSOCKET -DNOCOTFMC -funsigned-char \ -DFNFLOAT -DNOHTERMCAP -DNOREALPATH -DNOSYSCONF $(KFLAGS) -O -pipe" \ "LIBS= -lcurses -ltermcap -lm $(LIBS)" #FreeBSD 2.x with ncurses freebsd2: @echo 'Making C-Kermit $(CKVER) for FreeBSD 2.x with ncurses...' $(MAKE) CC=$(CC) CC2=$(CC2) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DBSD44 -DCK_NCURSES -DTCPSOCKET -DNOCOTFMC -DUSE_STRERROR \ -DTPUTSARGTYPE=int -DTPUTSARG1CONST -DFREEBSD2 -funsigned-char \ -DFNFLOAT $(KFLAGS) -O -pipe" \ "LIBS= -lncurses -ltermlib -lcrypt -lm $(LIBS)" #For FreeBSD 2.x -- Uses curses rather than ncurses freebsd2c: @echo 'Making C-Kermit $(CKVER) for FreeBSD 2.x with curses...' $(MAKE) CC=$(CC) CC2=$(CC2) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DBSD44 -DCK_CURSES -DTCPSOCKET -DNOCOTFMC -DUSE_STRERROR \ -DTPUTSARGTYPE=int -DTPUTSARG1CONST -DFREEBSD2 -DFNFLOAT \ -funsigned-char $(KFLAGS) -O -pipe" \ "LIBS= -lcurses -ltermlib -lcrypt -lm $(LIBS)" #FreeBSD 3.x with ncurses and uu_lock() #(Note: uu_lock() goes back to 2.2.2, but not necessarily 2.0) #OK 2011/08/20 FreeBSD 3.3 freebsd3: @echo 'Making C-Kermit $(CKVER) for FreeBSD 3.x with ncurses...' $(MAKE) CC=$(CC) CC2=$(CC2) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DBSD44 -DCK_NCURSES -DTCPSOCKET -DNOCOTFMC -funsigned-char \ -DTPUTSARGTYPE=int -DUSE_STRERROR -DFREEBSD3 -DUSE_UU_LOCK -DFNFLOAT \ $(KFLAGS) -O -pipe" \ "LIBS= -lncurses -lcrypt -lutil -lm $(LIBS)" #As above but with curses rather than ncurses. freebsd3c: @echo 'Making C-Kermit $(CKVER) for FreeBSD 3.x with curses...' $(MAKE) CC=$(CC) CC2=$(CC2) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DBSD44 -DCK_CURSES -DTCPSOCKET -DNOCOTFMC -DUSE_UU_LOCK \ -DTPUTSARGTYPE=int -DUSE_STRERROR -DFREEBSD3 $(KFLAGS) -DFNFLOAT \ -funsigned-char -pipe -O" \ "LIBS= -lcurses -lcrypt -lutil -lm $(LIBS)" #FreeBSD 4.0 with ncurses and uu_lock(). Note - there is no curses in 4.0. #ncurses 5.0 is broken requiring us to work around with setbuf(). freebsd40: @echo 'Making C-Kermit $(CKVER) for FreeBSD 4.x with ncurses...' $(MAKE) CC=$(CC) CC2=$(CC2) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DBSD44 -DCK_NCURSES -DTCPSOCKET -DNOCOTFMC -DFNFLOAT \ -funsigned-char -DTPUTSARGTYPE=int -DUSE_STRERROR -DFREEBSD4 \ -DNONOSETBUF -DUSE_UU_LOCK $(KFLAGS) -O -pipe" \ "LIBS= -lncurses -lcrypt -lutil -lm $(LIBS)" #FreeBSD 4.1 and above #Like FreeBSD 4.0 but without the NONOSETBUF hack and with CK_NEWTERM. #NOTE: This target definitely does not work for FreeBSD 3.3 in 9.0.302. #and it has not been tested on 4 or 5. #OK 2011/06/xx FreeBSD 3.3, 4,4, 4.7, and 8.2 #OK 2011/08/21 FreeBSD 3.3, 4.4, 6.4, 9.0 freebsd freebsd41 freebsd72 freebsd5 freebsd6 freebsd7 freebsd8 freebsd9: @echo 'Making C-Kermit $(CKVER) for FreeBSD 4.1 or later...' @if test `uname -r | cut -d . -f 1` -ge 8; then \ HAVE_FBSD8='-DFREEBSD8'; \ else HAVE_FBSD8=''; fi; \ if test `uname -r | cut -d . -f 1` -ge 9; then \ HAVE_FBSD9='-DFREEBSD9'; \ else HAVE_FBSD9=''; fi; \ if test -f /usr/include/utmpx.h ; \ then HAVE_UTMPX='-DHAVEUTMPX' ; \ else HAVE_UTMPX='' ; fi; \ $(MAKE) CC=$(CC) CC2=$(CC2) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DBSD44 -DCK_NCURSES -DCK_NEWTERM -DTCPSOCKET -DNOCOTFMC \ -DFREEBSD4 $$HAVE_FBSD8 $$HAVE_FBSD9 -DUSE_UU_LOCK -DFNFLOAT \ $$HAVE_UTMPX -DHERALD=\"\\\" `uname -rs`\\\"\" \ -funsigned-char -DTPUTSARGTYPE=int -DUSE_STRERROR $(KFLAGS) \ -O2 -pipe"\ "LIBS= -lncurses -lcrypt -lutil -lm $(LIBS)" #FreeBSD 5.0 or later with OpenSSL. #OK 2011/06/15 FreeBSD 4.7 and 8.2 #OK 2011/08/20 FreeBSD 9.0-CURRENT freebsd+ssl freebsd+openssl freebsd50+openssl: @echo 'Making C-Kermit $(CKVER) for FreeBSD with Kerberos 5...' @case `openssl version` in \ *0.9.7*) OPENSSLOPTION="-DOPENSSL_097" ;; \ *0.9.8*) OPENSSLOPTION="-DOPENSSL_098" ;; \ *1.[0-9].[0-9]*) OPENSSLOPTION="-DOPENSSL_100" ;; \ *) OPENSSLOPTION="" ;; \ esac; \ HAVE_DES=''; \ DES_LIB=''; \ if ls /usr/lib/libdes* > /dev/null 2> /dev/null || \ ls $(SSLLIB)/libdes* > /dev/null 2> /dev/null; then \ DES_LIB='-ldes'; \ HAVE_DES='-DCK_DES -DLIBDES'; \ echo "HAVE DES"; \ else echo "NO DES"; \ fi; \ $(MAKE) freebsd KTARGET=$${KTARGET:-$(@)} "CC = $(CC)" "CC2 = $(CC2)" \ KFLAGS="-DCK_AUTHENTICATION -DCK_SSL $(SSLINC) -DZLIB $$OPENSSLOPTION \ $$HAVE_DES $(KFLAGS)" "LNKFLAGS = $(LNKFLAGS)" \ "LIBS= -lncurses -lcrypt -lssl -lcrypto -lutil -lm \ $(SSLLIB) $$DES_LIB $(LIBS)"; \ if [ ! -f ./wermit ] || [ ./ckcmai.o -nt ./wermit ] ; then \ echo ""; \ echo "If build failed try:"; \ echo ""; \ echo " make clean ; make $${KTARGET:-$(@)} KFLAGS=-UCK_DES"; \ echo ""; \ fi #NetBSD 1.4.1 or later with vanity banner automated with uname #and automatic inclusion of large file support if it is available. #This target tested successfully on NetBSD 1.4.1, 1.5.2, and 2.0.3 (Jan 2006). #Fails on NetBSD 2.0 on Sun/3 mc68030 with gcc 3.3.3 unless optimization is #disabled on ckcfn2.c ("KFLAGS=-O0") (Letter O Digit Zero). #(This could be automated by testing `uname -m` for "sun3".) #OK: 2011/06/15 on NetBSD 1.5.2 and 5.1. #OK: 2011/08/21 on 5.1. netbsd netbsd2 netbsd15 netbsd16 old-netbsd: @echo Making C-Kermit $(CKVER) for NetBSD with curses... $(MAKE) CC=$(CC) CC2=$(CC2) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS=`grep fseeko /usr/include/stdio.h > /dev/null && \ echo '-D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64'` \ -DBSD44 -DCK_CURSES -DTCPSOCKET -DUSE_STRERROR -DHAVE_OPENPTY \ -funsigned-char -DHERALD=\"\\\" NetBSD `uname -r`\\\"\" \ -DCK_DTRCD -DCK_DTRCTS -DTPUTSARGTYPE=int -DFNFLOAT $(KFLAGS) -O" \ "LIBS= -lcurses -lcrypt -lm -lutil $(LIBS)" #NetBSD 1.4.1 or later with OpenSSL #OK: 2011/06/15 on NetBSD 5.1 (but not 1.5.2 with OpenSSL 0.9.5a) #OK: 2011/08/21 on 5.1. netbsd+ssl netbsd+openssl: @echo 'Making C-Kermit $(CKVER) for NetBSD+OpenSSL SSLLIB=$(SSLLIB)' @case `openssl version` in \ *0.9.7*) OPENSSLOPTION="-DOPENSSL_097" ;; \ *0.9.8*) OPENSSLOPTION="-DOPENSSL_098" ;; \ *1.[0-9].[0-9]*) OPENSSLOPTION="-DOPENSSL_100" ;; \ *) OPENSSLOPTION="" ;; \ esac; \ HAVE_DES=''; \ DES_LIB=''; \ if ls /usr/lib/libdes* > /dev/null 2> /dev/null || \ ls $(SSLLIB)/libdes* > /dev/null 2> /dev/null; then \ DES_LIB='-ldes'; \ HAVE_DES='-DCK_DES -DLIBDES'; \ echo "HAVE DES"; \ else echo "NO DES"; \ fi; \ $(MAKE) netbsd KTARGET=$${KTARGET:-$(@)} "CC = $(CC)" "CC2 = $(CC2)" \ "KFLAGS= -DCK_AUTHENTICATION -DCK_ENCRYPTION -DCK_CAST $$HAVE_DES \ -DCK_SSL -DCK_PAM -DZLIB -DNO_DCL_INET_ATON $$OPENSSLOPTION \ $(KFLAGS)" "LNKFLAGS = $(LNKFLAGS)" \ "LIBS= -L/usr/pkg/lib -R/usr/pkg/lib -lssl $$DES_LIB -lcurses \ -lcrypto -lcrypt -lz -lm -lpam -lutil $(LIBS)" #NetBSD with MIT Kerberos 5: # OK 2011/06/15 (once K5INC and K5LIB were set right). # NOT OK for Heimdal - Heimdal Kerberos support in C-Kermit needs work. # OK: 2011/08/21 on 5.1. netbsd+krb5: @echo 'Making C-Kermit $(CKVER) for NetBSD with Kerberos 5...' @case `openssl version` in \ *0.9.7*) OPENSSLOPTION="-DOPENSSL_097" ;; \ *0.9.8*) OPENSSLOPTION="-DOPENSSL_098" ;; \ *1.[0-9].[0-9]*) OPENSSLOPTION="-DOPENSSL_100" ;; \ *) OPENSSLOPTION="" ;; \ esac; \ HAVE_DES=''; \ DES_LIB=''; \ if ls /usr/lib/libdes* > /dev/null 2> /dev/null || \ ls $(SSLLIB)/libdes* > /dev/null 2> /dev/null; then \ DES_LIB='-ldes'; \ HAVE_DES='-DCK_DES -DLIBDES'; \ echo "HAVE DES"; \ else echo "NO DES"; \ fi; \ $(MAKE) netbsd KTARGET=$${KTARGET:-$(@)} "CC = $(CC)" "CC2 = $(CC2)" \ "KFLAGS= -DCK_AUTHENTICATION -DCK_ENCRYPTION -DCK_KERBEROS -DKRB5 \ -DCK_CAST $$HAVE_DES -DNOFTP_GSSAPI $(K5INC) $(K5INC)/krb5 \ $(KFLAGS)" \ "LIBS= $(K5LIB) -L/usr/pkg/lib -R/usr/pkg/lib -lcurses $$DES_LIB \ -lcrypto -lgssapi -lkrb5 -lm -lutil $(LIBS)" # NetBSD - With Kerberos 5 and SSL and Zlib. # OK: 2011/08/21 on 5.1 with MIT Kerberos. netbsd+krb5+ssl netbsd+krb5+openssl+zlib: @echo 'Making C-Kermit $(CKVER) for NetBSD+OpenSSL+Kerberos5...' @case `openssl version` in \ *0.9.7*) OPENSSLOPTION="-DOPENSSL_097" ;; \ *0.9.8*) OPENSSLOPTION="-DOPENSSL_098" ;; \ *1.[0-9].[0-9]*) OPENSSLOPTION="-DOPENSSL_100" ;; \ *) OPENSSLOPTION="" ;; \ esac; \ HAVE_DES=''; \ DES_LIB=''; \ if ls /usr/lib/libdes* > /dev/null 2> /dev/null || \ ls $(SSLLIB)/libdes* > /dev/null 2> /dev/null; then \ DES_LIB='-ldes'; \ HAVE_DES='-DCK_DES -DLIBDES'; \ echo "HAVE DES"; \ else echo "NO DES"; \ fi; \ $(MAKE) netbsd KTARGET=$${KTARGET:-$(@)} "CC = $(CC)" "CC2 = $(CC2)" \ "KFLAGS= -DCK_AUTHENTICATION -DCK_ENCRYPTION -DCK_CAST $$HAVE_DES \ -DCK_KERBEROS -DKRB5 -DNOFTP_GSSAPI $(K5INC) $(K5INC)/krb5 \ -DCK_SSL -DCK_PAM -DZLIB -DNO_DCL_INET_ATON $$OPENSSLOPTION \ $(KFLAGS)" "LNKFLAGS = $(LNKFLAGS)" \ "LIBS= $(K5LIB) -L/usr/pkg/lib -R/usr/pkg/lib -lssl $$DES_LIB \ -lcrypto -lcrypt -lgssapi -lkrb5 -lz -lm -lpam -lutil -lcurses $(LIBS)" #Special Security Enhanced NetBSD target with SRP, SSL, and zlib support. #To build this, you need to BUILD the pkgsrc srp_client package. After #you build it, you must go into work/srp-x.y.z/libkrypto and "bmake install" #then go to work/srp-x.y.z/libsrp and "bmake install". As of 2005Q3, the #pkgsrc install only installed the statically linked client applications. You #need to manually install the libraries to build your own applications. #NOT TESTED RECENTLY - probably needs work. netbsd+ssl+srp+zlib: @echo Making C-Kermit $(CKVER) for NetBSD with curses... $(MAKE) CC=$(CC) CC2=$(CC2) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DBSD44 -DCK_CURSES -DTCPSOCKET -DUSE_STRERROR -DNETBSD15 \ -DCK_DTRCD -DCK_DTRCTS -DTPUTSARGTYPE=int -DHAVE_OPENPTY \ -I/usr/include/openssl -I/usr/pkg/include \ -DCK_AUTHENTICATION -DCK_SRP -DPRE_SRP_1_4_5 -DCK_ENCRYPTION \ -DCK_CAST -DCK_DES -DLIBDES -DCK_SSL -DZLIB -DFNFLOAT $(KFLAGS) -O" \ "LIBS= -L/usr/pkg/lib -R/usr/pkg/lib -lcurses -lsrp -lgmp -ldes \ -lssl -lkrypto -lcrypto -lcrypt -lz -lm -lutil $(LIBS)" #NetBSD with curses left out (e.g. for use as IKSD). netbsdnc: @echo Making C-Kermit $(CKVER) for NetBSD with no curses... $(MAKE) CC=$(CC) CC2=$(CC2) netbsd KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-DNOCURSES" #NetBSD with ncurses requested explicitly rather than curses-which-is-ncurses netbsdn: @echo Making C-Kermit $(CKVER) for NetBSD with curses... $(MAKE) CC=$(CC) CC2=$(CC2) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS=`grep fseeko /usr/include/stdio.h > /dev/null && \ echo '-D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64'` \ -DBSD44 -DCK_CURSES -DTCPSOCKET -DUSE_STRERROR -DHAVE_OPENPTY \ -DHERALD=\"\\\" NetBSD `uname -r`\\\"\" \ -DCK_DTRCD -DCK_DTRCTS -DTPUTSARGTYPE=int -DFNFLOAT $(KFLAGS) -O" \ "LIBS= -L/usr/pkg/lib -lncurses -lcrypt -lm -lutil $(LIBS)" #OpenBSD before 2.3. #Uses ncurses as its curses so use -ltermlib, not -ltermcap #But it doesn't use uu_lock() which was introduced in OpenBSD 2.3. #For that use the next entry. #Add -DMAINTYPE=int if you get complaints about main: return type is not int. openbsdold: @echo Making C-Kermit $(CKVER) for OpenBSD... $(MAKE) CC=$(CC) CC2=$(CC2) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DBSD44 -DCK_CURSES -DCK_NEWTERM -DTCPSOCKET -DOPENBSD \ -DFNFLOAT -DNDSYSERRLIST $(KFLAGS) -O" "LIBS= -lcurses -ltermlib -lm" #OpenBSD 2.3 or later #Add -DMAINTYPE=int if you get complaints about main: return type is not int. #For C-Kermit 8.0 (Christian Weisgerber): # -ltermlib removed (presumably because -lcurses==ncurses already includes it) # -DUSE_UU_LOCK and -lutil added for uu_lock() # -DNDSYSERRLIST changed to -DUSE_STRERROR #If this gives you trouble use the previous entry. #NOTE: The openbsd and openbsd+ssl should be reworked to be like the #corresponding FreeBSD and NetBSD targets. The mirbsd targets should openbsd: @echo Making C-Kermit $(CKVER) for OpenBSD 2.3 or later... $(MAKE) CC=$(CC) CC2=$(CC2) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DBSD44 -DCK_CURSES -DCK_NEWTERM -DTCPSOCKET -DOPENBSD \ -DHERALD=\"\\\" OpenBSD `uname -r`\\\"\" \ -DUSE_UU_LOCK -DFNFLOAT -DUSE_STRERROR $(KFLAGS) -O" \ "LIBS= -lcurses -lutil -lm" #Better to chain to the openbsd target but... mirbsd: @echo Making C-Kermit $(CKVER) for OpenBSD 2.3 or later... $(MAKE) CC=$(CC) CC2=$(CC2) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DBSD44 -DCK_CURSES -DCK_NEWTERM -DTCPSOCKET -DOPENBSD \ -DHERALD=\"\\\" MirBSD `uname -r`\\\"\" \ -DUSE_UU_LOCK -DFNFLOAT -DUSE_STRERROR $(KFLAGS) -O" \ "LIBS= -lcurses -lutil -lm" #OpenBSD 3.0 or later includes OpenSSL #Add -DMAINTYPE=int if you get complaints about main: return type is not int. #For C-Kermit 8.0 (Christian Weisgerber): # -ltermlib removed (presumably because -lcurses==ncurses already includes it) # -DUSE_UU_LOCK and -lutil added for uu_lock() # -DNDSYSERRLIST changed to -DUSE_STRERROR #If this gives you trouble use the previous entry. openbsd+ssl: @echo Making C-Kermit $(CKVER) for OpenBSD 3.0 or later... $(MAKE) CC=$(CC) CC2=$(CC2) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DBSD44 -DCK_CURSES -DCK_NEWTERM -DTCPSOCKET -DOPENBSD \ -DHERALD=\"\\\" OpenBSD `uname -r`\\\"\" \ -DUSE_UU_LOCK -DFNFLOAT -DUSE_STRERROR -DCK_AUTHENTICATION \ -DCK_SSL $(KFLAGS) -O" \ "LIBS= -lcurses -lutil -lm -lssl -lcrypto" mirbsd+ssl: @echo Making C-Kermit $(CKVER) for OpenBSD 3.0 or later... $(MAKE) CC=$(CC) CC2=$(CC2) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DBSD44 -DCK_CURSES -DCK_NEWTERM -DTCPSOCKET -DOPENBSD \ -DHERALD=\"\\\" MirBSD `uname -r`\\\"\" \ -DUSE_UU_LOCK -DFNFLOAT -DUSE_STRERROR -DCK_AUTHENTICATION \ -DCK_SSL -DNO_DCL_INET_ATON $(KFLAGS) -O" \ "LIBS= -lcurses -lutil -lm -lssl -lcrypto" # make 386bsd 0.0new, posix # for 386bsd 0.1.24, change /usr/include/termios.h to #define NCCS if # _POSIX_SOURCE is #defined. (source: lewine, posix prgmrs guide, o`reilly) #NOTE: Lock directory is /var/spool/lock. Formerly, it was /var/spool/uucp, #but reportedly that was due to a typo in 'man tip'. 386bsd: @echo 'Making C-Kermit $(CKVER) for jolix 386BSD 0.0new and 0.1.24...' $(MAKE) CC=$(CC) CC2=$(CC2) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DPOSIX -DSETREUID -DPIDSTRING -DUSLEEP \ -D_386BSD -DCK_CURSES -DTCPSOCKET \ -DLOCK_DIR=\\\"/var/spool/lock\\\" \ $(KFLAGS) -O" "LNKFLAGS = -s" "LIBS = -lcurses -ltermcap" # Mac OS X 10 early versions. # For 10.3.9 and later, use the macosx target below. #Mac OS X 1.0 (Rhapsody, Darwin) -- TCP/IP but no curses. oldmacosx10: @echo Making C-Kermit $(CKVER) for `uname -s`... $(MAKE) CC=$(CC) CC2=$(CC2) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DMACOSX10 -DTCPSOCKET -DUSE_STRERROR -O $(KFLAGS)" #Mac OS X 1.0 (Rhapsody, Darwin) -- TCP/IP and curses. #Note: curses must be obtained separately. See next entry for ncurses. #Add "LIBS = -lcurses -ltermcap" if necessary (but reportedly it is not). oldmacosx10c: @echo Making C-Kermit $(CKVER) for `uname -s` + curses... $(MAKE) CC=$(CC) CC2=$(CC2) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DMACOSX10 -DCK_CURSES -DTPUTSFNTYPE=void -DTPUTSISVOID \ -DTCPSOCKET -DUSE_STRERROR -O $(KFLAGS)" #Mac OS X 1.0 (Rhapsody, Darwin) -- TCP/IP and ncurses. #Note: ncurses must be obtained separately. #In the event of trouble with this one try the next one. oldmacosx10nc: @echo Making C-Kermit $(CKVER) for `uname -s` + ncurses... $(MAKE) CC=$(CC) CC2=$(CC2) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DMACOSX10 -DCK_NCURSES -DTCPSOCKET -DUSE_STRERROR -O \ $(KFLAGS)" "LIBS= -lncurses $(LIBS)" #Mac OS X 10.2 (Jaguar) ncurses. oldmacosx102nc: @echo Making C-Kermit $(CKVER) for `uname -s` + ncurses... $(MAKE) CC=$(CC) CC2=$(CC2) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DMACOSX10 -DCK_NCURSES -DTCPSOCKET -DUSE_STRERROR -O \ $(KFLAGS) " "LIBS= -lncurses $(LIBS)" #The problem here is that if curses.h also exists, it conflicts with #ncurses.h and and we have fatal errors. If this happens to you, then #try this target. oldmacosx10ncx: @echo Making C-Kermit $(CKVER) for `uname -s` + ncurses... @rm -f ./curses.h; touch ./curses.h $(MAKE) CC=$(CC) CC2=$(CC2) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DMACOSX10 -DCK_NCURSES -DTCPSOCKET -DUSE_STRERROR \ -I. -O $(KFLAGS) " \ "LIBS= -lncurses $(LIBS)" @rm -f ./curses.h #Mac OS X 10.3 (Panther) - Assumes ncurses is installed. oldmacosx103: @echo Making C-Kermit $(CKVER) for `uname -s` + ncurses... $(MAKE) CC=$(CC) CC2=$(CC2) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DMACOSX10 -DMACOSX103 -DCK_NCURSES -DTCPSOCKET -DCKHTTP \ -DUSE_STRERROR -DUSE_NAMESER_COMPAT -O \ $(KFLAGS) " "LIBS= -lncurses -lresolv $(LIBS)" #Mac OS X 10.3 (Panther) with Kerberos 5 and SSL, assumes ncurses is installed. oldmacosx103+secure: @echo Making Secure C-Kermit $(CKVER) for `uname -s` + ncurses... $(MAKE) CC=$(CC) CC2=$(CC2) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DMACOSX10 -DMACOSX103 -DCK_NCURSES -DTCPSOCKET \ -DUSE_STRERROR -DUSE_NAMESER_COMPAT -O -DCK_PAM \ -DCK_AUTHENTICATION -DCK_KERBEROS -DKRB5 -DZLIB \ -DCK_ENCRYPTION -DCK_CAST -DCK_DES -DLIBDES -DCK_SSL \ $(KFLAGS) " "LIBS= -lssl -lcrypto -lkrb5 -lcom_err \ -lk5crypto -lgssapi_krb5 -lpam -lncurses -lresolv $(LIBS)" # THIS IS THE MAIN MAC OS X TARGET (the next one is for Kerberos/SSL builds). # Use this target for 10.3.9 (or maybe earlier) through 10.6 (maybe later) # on both Power and Intel architectures. This one uses utmp.h on 10.4 and # earlier and utmpx.h on 10.5 onwards. # Note: Mac OS X 10.5 and earlier are 32-bit; 10.6 and later 64-bit. # Note 2: As of C-Kermit 9.0 -NOUUCP is included by default because # Mac OS X doesn't support UUCP. To undo this, use KFLAGS=-UNOUUCP. #OK: 2011/06/14 (for 10.4.11, 10.5.8, 10.6.7) macosx macosx10 macosx10.3.9 macosx10.4 macosx10.5 macosx10.6: @MACOSNAME=`/usr/bin/sw_vers -productName`; \ MACOSV=`/usr/bin/sw_vers -productVersion`; \ echo Making C-Kermit $(CKVER) for $$MACOSNAME $$MACOSV... ; \ MACCPU=$$HOSTTYPE; \ if test `uname -r | cut -d . -f 1` -gt 8; \ then if test -f /usr/include/utmpx.h ; \ then HAVE_UTMPX='-DHAVEUTMPX -D_UTMPX_COMPAT' ; \ else HAVE_UTMPX='' ; fi ; fi; \ $(MAKE) CC=$(CC) CC2=$(CC2) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DMACOSX10 -DMACOSX103 -DCK_NCURSES -DTCPSOCKET -DCKHTTP \ -DUSE_STRERROR -DUSE_NAMESER_COMPAT -DNOCHECKOVERFLOW -DFNFLOAT \ -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 $$HAVE_UTMPX \ -funsigned-char -DNODCLINITGROUPS \ -DNOUUCP -O -DHERALD=\"\\\" $${MACOSNAME} $${MACOSV}\\\"\" \ -DCKCPU=\"\\\"$${MACCPU}\\\"\" \ $(KFLAGS)" "LIBS= -lncurses -lresolv $(LIBS)" # Mac OS X 10.3.9 or later with Kerberos 5 and OpenSSL... # NOTE: Apple has removed all support for DES in OpenSSL and Kerberos # in Mac OS X 10.6 and later. The DES flags are included or left out # automatically based on the Mac OS X version number. # See note about UUCP in previous target. #OK: 2009/11/16 (for 10.3.9, 10.4.11, 10.5.8, 10.6.1) #OK: 2011/06/14 (for 10.4.11, 10.5.8, 10.6.7) macosx+krb5+ssl macosx10.5+krb5+ssl macosx10.6+krb5+ssl \ macosx+krb5+openssl macosx10.5+krb5+openssl macosx10.6+krb5+openssl: @MACOSNAME=`/usr/bin/sw_vers -productName`; \ MACOSV=`/usr/bin/sw_vers -productVersion`; \ echo Making C-Kermit $(CKVER) for $$MACOSNAME $$MACOSV... ; \ MACCPU=$$HOSTTYPE; \ if test `uname -r | cut -d . -f 1` -gt 8; \ then if test -f /usr/include/utmpx.h ; \ then HAVE_UTMPX='-DHAVEUTMPX -D_UTMPX_COMPAT' ; \ else HAVE_UTMPX='' ; fi ; fi; \ if test `uname -r | cut -d . -f 1` -eq 7; \ then IS_MACOSX103='-DMACOSX103' ; \ else IS_MACOSX103='' ; fi; \ case $$MACOSV in \ 10.[012345].*) HAVE_DES='-DCK_DES -DLIBDES' ;; \ *.*) HAVE_DES='' ;; \ esac ; \ if test -x /usr/bin/krb5-config ; \ then HAVE_KRB5CONFIG=`/usr/bin/krb5-config --libs krb5 gssapi` ; \ else HAVE_KRB5CONFIG='-lgssapi_krb5 -lkrb5 -lk5crypto \ -lcom_err -lresolv' ; fi; \ $(MAKE) CC=$(CC) CC2=$(CC2) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DMACOSX10 $$IS_MACOSX103 -DCK_NCURSES -DTCPSOCKET \ -DUSE_STRERROR -DUSE_NAMESER_COMPAT -DNOCHECKOVERFLOW -DFNFLOAT \ -DCKHTTP -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 $$HAVE_UTMPX \ -DNODCLINITGROUPS -DCK_AUTHENTICATION -DCK_KERBEROS -DKRB5 -DZLIB \ -DCK_ENCRYPTION -DCK_CAST -DCK_SSL -DOPENSSL_098 $$HAVE_DES \ -DNOUUCP -DHERALD=\"\\\" $${MACOSNAME} $${MACOSV}\\\"\" \ -DCKCPU=\"\\\"$${MACCPU}\\\"\" \ -funsigned-char -O $(KFLAGS)" \ "LIBS= $$HAVE_KRB5CONFIG -lssl -lcrypto -lpam -lncurses $(LIBS)" # End of Mac OS X Section #Acorn RISCiX, based on ... #Berkeley Unix 4.2 or 4.3 with lock directory /usr/spool/uucp/LCK/LCK..ttyxx, #but without acucntrl program riscix: @echo Making C-Kermit $(CKVER) for RISCiX, /usr/spool/uucp/LCK..ttyxx $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DBSD42 -DBSD4 -DRISCIX -DNOCSETS \ -DLOCK_DIR=\\\"/usr/spool/uucp\\\" -DDIRENT -DCK_CURSES \ -DMAXSP=9024 -DMAXRD=9024 -DSBSIZ=9050 -DRBSIZ=9050 \ -DDFTTY=\\\"/dev/serial\\\" -DNOCSETS -DNOCYRIL \ -DNOANSI -w -O2 -fomit-frame-pointer" \ "LIBS= -lcurses -ltermcap " \ "CC= /usr/ucb/cc" \ "CC2= /usr/ucb/cc" #Acorn RISCiX, as above, but using gcc riscix-gcc: @echo Making C-Kermit $(CKVER) for RISCiX, /usr/spool/uucp/LCK..ttyxx $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DBSD42 -DBSD4 -DRISCIX -DNOCSETS \ -DLOCK_DIR=\\\"/usr/spool/uucp\\\" -DDIRENT -DCK_CURSES \ -DMAXSP=9024 -DMAXRD=9024 -DSBSIZ=9050 -DRBSIZ=9050 \ -DDFTTY=\\\"/dev/serial\\\" -DNOCSETS -DNOCYRIL \ -DNOANSI -w -O2 -fomit-frame-pointer" \ "LIBS= -lcurses -ltermcap " \ "CC= gcc -mbsd" \ "CC2= gcc -mbsd" #Convergent CTIX 6.4.1 ctix: @echo 'Making C-Kermit $(CKVER) for Convergent CTIX 6.4.1' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DSVR3 -DDIRENT -DTCPSOCKET -DHDBUUCP -DCK_CURSES \ -DNONAWS -DNOLEARN -DNOLONGLONG $(KFLAGS) -XO" \ "LNKFLAGS=-s" "LIBS=-lsocket -lcurses -lc_s" mcs -d wermit # The following makefile entry should work for any Harris Night Hawk system # (either 88k or 68k based) running release 6.1 or later of the CX/UX # operating system. This is a POSIX and ANSI-C compliant system which also # supports BSD networking. (Earlier CX/UX releases will probably work with # sys5r3, but this has not been verified). # cx_ux: @echo Making C-Kermit $(CKVER) for Harris Night Hawk CX/UX 6.1 or later $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS=-DPOSIX -DTCPSOCKET -DHDBUUCP -DPID_T=pid_t -DWAIT_T=int \ -Dd_ino=d_fileno -DUID_T=uid_t -DGID_T=gid_t -DNOLONGLONG \ $(KFLAGS) -Xa -O3 -g" "LNKFLAGS=-O3" #Intergraph Clipper, CLIX, job control, HDB UUCP. clix: @echo 'Making C-Kermit $(CKVER) for Intergraph CLIX...' $(MAKE) wermit "CC=acc" "CC2=acc" KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -w -DSVR3 -DCLIX -DDIRENT -DHDBUUCP -DNOSYSLOG -DUSE_MEMCPY \ -DNOGETUSERSHELL -DNOREALPATH -DNOLEARN $(KFLAGS) -O" \ "LNKFLAGS=" "LIBS= -lbsd" #As above + TCP/IP... clixnet: @echo 'Making networked C-Kermit $(CKVER) for Intergraph CLIX...' $(MAKE) wermit "CC=acc" "CC2=acc" KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -w -DSVR3 -DCLIX -DDIRENT -DHDBUUCP -DNOSYSLOG -DUSE_MEMCPY \ -DTCPSOCKET -DNOGETUSERSHELL -DNOLEARN -DNOREALPATH $(KFLAGS) -O" \ "LNKFLAGS=" "LIBS= -lbsd" #Mark Williams Coherent 286 or 386 on IBM PC family. #There is a 64K limit on program size, so this is a command-line only version. coherent: $(MAKE) "CFLAGS = -O -DCOHERENT -DNOANSI -DNOICP -DNOSETKEY -DNOLEARN \ -DNOCSETS -DNOHELP -DNODIAL -DNOSCRIPT -DNODEBUG -DNOTLOG -DNOXMIT \ -DNOMSEND -DNOFRILLS -DNOSYSIOCTLH -DSELECT_H $(KFLAGS) -VSUVAR" \ -DNOFLOAT KTARGET=$${KTARGET:-$(@)} wermit #Mark Williams Coherent 386 on IBM PC family. #This will make a "minimum interactive" version - no scripts, #no character sets, no help, no dial, no debug/transaction logging, no #transmit, msend, mail, type, etc. coherentmi: $(MAKE) "CFLAGS = -O -DCOHERENT -DNOANSI -DNOSETKEY -DNOLEARN \ -DNOSHOW -DNOCSETS -DNOHELP -DNODIAL -DNOSCRIPT -DNODEBUG -DNOTLOG \ -DNOXMIT -DNOMSEND -DNOFRILLS -DNOSYSIOCTLH -DNOSERVER -DNOUUCP \ -DNOSPL -DNOPUSH -DNOMDMHUP -DNOJC -DNOFDZERO -DNOESCSEQ -DNOFLOAT \ -DNOCMDL $(KFLAGS) -VSUVAR -DSELECT_H" KTARGET=$${KTARGET:-$(@)} \ wermit #Mark Williams Coherent 386 on IBM PC/AT family. coherentmax: $(MAKE) "CFLAGS = -O -DCOHERENT -DNOANSI -DSELECT_H -DNOLEARN \ -DNOFLOAT -DNOSYSIOCTLH $(KFLAGS) -VSUVAR" "LNKFLAGS = -O -s" \ KTARGET=$${KTARGET:-$(@)} wermit #Mark Williams Coherent 386 4.2. Includes curses but not TCP/IP. #Requires updates to the 4.2.10 compiler; the regular compiler fails to #to handle "complex expressions". NOFLOAT is so it can work on old PCs #without floating-point hardware. coherent42: $(MAKE) "CFLAGS = -T500000 -DNOFLOAT -DCOHERENT -DNOANSI -DSELECT \ -DNOSYSLOG -DDIRENT -DCK_CURSES -DCK_NEWTERM -DCK_WREFRESH -VSUVAR \ -DDCLGETCWD -DNOSYSIOCTLH -DNOINITGROUPS -DNOSYMLINK -DSELECT_H \ -DDCLGETCWD -O $(KFLAGS)" \ "LNKFLAGS = -O -s" KTARGET=$${KTARGET:-$(@)} \ "LIBS = -lsocket -lcurses" wermit #DEC Ultrix 2.x # Add -O, -DDYNAMIC, -s, etc, if they work. ultrix2x: @echo Making C-Kermit $(CKVER) for Ultrix 2.x ... $(MAKE) xermit KTARGET=$${KTARGET-$(@)} \ "CFLAGS= -DBSD4 -DTCPSOCKET -DDU2 -DNOGETUSERSHELL $(KFLAGS)" du2: $(MAKE) "MAKE=$(MAKE)" KTARGET=$${KTARGET-$(@)} ultrix2x #DEC Ultrix 3.0 and 3.1 ultrix30: @echo Making C-Kermit $(CKVER) for Ultrix 3.0... $(MAKE) xermit KTARGET=$${KTARGET-$(@)} \ "CFLAGS= -DBSD4 -DTCPSOCKET -DDIRENT -DSIG_V -DNOGETUSERSHELL \ -DULTRIX3 -DCK_CURSES $(KFLAGS) -O" "LIBS= -lcurses -ltermcap" du3: $(MAKE) "MAKE=$(MAKE)" KTARGET=$${KTARGET-$(@)} ultrix30 ultrix3x: $(MAKE) "MAKE=$(MAKE)" KTARGET=$${KTARGET-$(@)} ultrix30 #DEC Ultrix 4.0 or 4.1 on DECstation, VAXstation, VAX, etc. ultrix40: @echo Making C-Kermit $(CKVER) for Ultrix 4.0 or 4.1... $(MAKE) xermit KTARGET=$${KTARGET-$(@)} \ "CFLAGS= -DBSD4 -DTCPSOCKET -DSIG_V -DDU4 -DNOGETUSERSHELL \ $(KFLAGS) -Olimit 1450" "LNKFLAGS = -s" #DEC Ultrix 4.2-4.5 on DECstation, DECsystem, VAXstation, VAX, etc. #Like ultrix40, except now C compiler supports -O2 optimization. ultrix42: @echo Making C-Kermit $(CKVER) for Ultrix 4.2 or later... $(MAKE) xermit KTARGET=$${KTARGET-$(@)} \ "CFLAGS= -DBSD4 -DTCPSOCKET -DSIG_V -DNOGETUSERSHELL $(KFLAGS) \ -O2 -Olimit 1750" "LNKFLAGS = -s" du42: $(MAKE) "MAKE=$(MAKE)" KTARGET=$${KTARGET-$(@)} ultrix42 #DEC Ultrix 4.2-4.5 on DECstation, DECsystem, VAXstation, VAX, etc. #Like du42, but with curses support added and a couple features. ultrix42c: @echo Making C-Kermit $(CKVER) for Ultrix 4.2 or later... $(MAKE) xermit KTARGET=$${KTARGET-$(@)} \ "CFLAGS= -DBSD4 -DTCPSOCKET -DSIG_V -DNOGETUSERSHELL \ -DCK_CURSES -DNOIKSD $(KFLAGS)-G6 -O2 -Olimit 3000 " \ "LNKFLAGS = -s" "LIBS= -lcurses -ltermcap" ultrix43: $(MAKE) "MAKE=$(MAKE)" CC=$(CC) CC2=$(CC2) \ "KFLAGS=-DULTRIX43 $(KFLAGS)" KTARGET=$${KTARGET-$(@)} ultrix42c ultrix43notcp: $(MAKE) "MAKE=$(MAKE)" CC=$(CC) CC2=$(CC2) \ "KFLAGS=-DULTRIX43 -DNONET $(KFLAGS)" \ KTARGET=$${KTARGET-$(@)} ultrix42c # NOTE: need -DNODEBUG on MIPS to avoid relocation errors at link time. # Actually now (8.0) that we have discovered the -G option maybe debugging # can be put back. ultrix44: $(MAKE) "MAKE=$(MAKE)" CC=$(CC) CC2=$(CC2) \ "KFLAGS=-DULTRIX44 -G7 -DNODEBUG -DNETPTY -DNO_DEVTTY $(KFLAGS)" \ KTARGET=$${KTARGET-$(@)} ultrix42c ultrix45: $(MAKE) "MAKE=$(MAKE)" CC=$(CC) CC2=$(CC2) \ "KFLAGS=-DULTRIX45 $(KFLAGS)-DNETPTY -DNO_DEVTTY $(KFLAGS)" \ KTARGET=$${KTARGET-$(@)} ultrix42c du42c: $(MAKE) "MAKE=$(MAKE)" CC=$(CC) CC2=$(CC2) \ KTARGET=$${KTARGET-$(@)} ultrix42c #DEC Ultrix 4.3A or later on DECsystem and DECstation 5000/50, /150 or /260 #with MIPS R4x00 processor. The "-mips3" switch generates R4000-specific #code, which is faster and more compact, but *won't* run on earlier #DECsystems and DECstations. ultrix43-mips3: @echo Making C-Kermit $(CKVER) for Ultrix 4.3A or later, R4000 cpu... $(MAKE) xermit KTARGET=$${KTARGET-$(@)} \ "CFLAGS= -DBSD4 -DTCPSOCKET -DSIG_V -DNOGETUSERSHELL \ $(KFLAGS) -O2 -Olimit 1750 -mips3" "LNKFLAGS = -s -mips3" du43-mips3: $(MAKE) "MAKE=$(MAKE)" CC=$(CC) CC2=$(CC2) ultrix43-mips3 #DEC Ultrix 4.3A or later on MIPS R4x000 based systems. #Like ultrix43-mips3 but with curses support added ultrix43c-mips3: @echo Making C-Kermit $(CKVER) for Ultrix 4.3A or later, R4000 cpu... $(MAKE) xermit KTARGET=$${KTARGET-$(@)} \ "CFLAGS= -DBSD4 -DTCPSOCKET -DSIG_V -DNOGETUSERSHELL -DCK_CURSES \ $(KFLAGS) -O2 -Olimit 3000 -mips3" "LNKFLAGS = -s -mips3" \ "LIBS= -lcurses -ltermcap" du43c-mips3: $(MAKE) "MAKE=$(MAKE)" CC=$(CC) CC2=$(CC2) \ KTARGET=$${KTARGET-$(@)} ultrix43c-mips3 #DEC Ultrix 4.4 on DECstation 5000/50 or /150 with R4000 MIPS processor, #or 5000/260 with R4400. The "-mips3" switch generates R4000-specific code, #which is faster and more compact but *won't* run on earlier DECstations. ultrix44-mips3: @echo Making C-Kermit $(CKVER) for Ultrix 4.4, R4000 cpu ... $(MAKE) xermit KTARGET=$${KTARGET-$(@)} \ "CFLAGS= -DBSD4 -DTCPSOCKET -DSIG_V -DNOGETUSERSHELL \ $(KFLAGS) -O2 -Olimit 1450 -mips3" "LNKFLAGS = -s -mips3" du44-mips3: $(MAKE) "MAKE=$(MAKE)" CC=$(CC) CC2=$(CC2) \ KTARGET=$${KTARGET-$(@)} ultrix44c-mips3 #DEC Ultrix 4.2 on DECstation, VAXstation, VAX, etc, System V R4 environment ultrix42s5r4: @echo 'Making C-Kermit $(CKVER) for AT&T UNIX System V R4 on Ultrix...' $(MAKE) xermit KTARGET=$${KTARGET-$(@)} \ "CFLAGS = -O2 -Olimit 1500 -DSVR4 -DDIRENT -DHDBUUCP -DNOGETUSERSHELL \ -DTCPSOCKET $(KFLAGS)" "LNKFLAGS = -s" #OSF/1 osf osf1: $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DBSD4 -DOSF -D_BSD -DTCPSOCKET -DCK_ANSIC -DSIG_V \ -DCK_CURSES -DCK_RTSCTS -DFNFLOAT $(KFLAGS)" \ "LNKFLAGS = -s" "LIBS = $(LIBS) -lbsd -lcurses -ltermcap -lm" #DEC OSF/1 V1.0-1.3 on DECstation, VAX, Alpha, or PC. dec-osf dec-osf1: $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DBSD4 -DOSF -DOSF13 -D_BSD -DTCPSOCKET -DCK_ANSIC -DSIG_V \ -DNOREALPATH -DNOIKSD -DCK_CURSES -DCK_RTSCTS -DFNFLOAT -DNODEBUG \ -DNOUNICODE $(KFLAGS)" \ "LNKFLAGS = -non_shared" "LIBS = -lbsd -lcurses -ltermcap -lm" # This one causes "relocation out-of-range" errors in the linker. old-dec-osf: @echo Making C-Kermit $(CKVER) for DEC OSF/1 V1.x... @echo If you are building for DEC OSF/1 2.0, please use dec-osf20. @echo Remove or adjust -O2 and/or -Olimit if they cause trouble. $(MAKE) CC=$(CC) CC2=$(CC2) osf KTARGET=$${KTARGET:-$(@)} \ "KFLAGS= -O2 -Olimit 2400 $(KFLAGS)" #DEC OSF/1 2.0 on Alpha and probably nowhere else. #The only difference from OSF/1 is that optimization is omitted. #The optimized version gets strange runtime errors, like the PAUSE command #not working. Add "-unsigned" to make all chars unsigned. dec-osf20: @echo Making C-Kermit $(CKVER) for DEC OSF/1 V2.0... @echo Optimization omitted because it causes runtime errors. @echo See comments in makefile. $(MAKE) CC=$(CC) CC2=$(CC2) osf KTARGET=$${KTARGET:-$(@)} \ "KFLAGS= -DOSF20 $(KFLAGS)" dec-osf30: @echo Making C-Kermit $(CKVER) for DEC OSF/1 V3.0... $(MAKE) CC=$(CC) CC2=$(CC2) osf KTARGET=$${KTARGET:-$(@)} \ "KFLAGS= -DOSF30 -O2 -Olimit 2400 $(KFLAGS)" #Digital UNIX 3.2 # Must compile ckuus[6x].c separately without optimization otherwise # the optimizer dumps core - keep CFLAGS here in sync with those from osf. du32: @echo Making C-Kermit $(CKVER) for Digital UNIX 3.2... $(MAKE) CC=$(CC) CC2=$(CC2) ckuus6.$(EXT) \ "CFLAGS= -DBSD4 -DOSF -D_BSD -DTCPSOCKET -DCK_ANSIC -DSIG_V \ -DCK_CURSES -DCK_RTSCTS -DFNFLOAT -DOSF32 -DHDBUUCP $(KFLAGS)" $(MAKE) CC=$(CC) CC2=$(CC2) ckuusx.$(EXT) \ "CFLAGS= -DBSD4 -DOSF -D_BSD -DTCPSOCKET -DCK_ANSIC -DSIG_V \ -DCK_CURSES -DCK_RTSCTS -DFNFLOAT -DOSF32 -DHDBUUCP $(KFLAGS)" $(MAKE) CC=$(CC) CC2=$(CC2) osf \ "KFLAGS= -DOSF32 -DHDBUUCP -O2 -Olimit 3200 $(KFLAGS)" dec-osf32: $(MAKE) "MAKE=$(MAKE)" CC=$(CC) CC2=$(CC2) du32 \ KTARGET=$${KTARGET:-$(@)} #Digital UNIX 4.0 through 4.0D (use tru64 targets for 4.0E and above)... du40: @echo Making C-Kermit $(CKVER) for Digital UNIX 4.0... $(MAKE) CC=$(CC) CC2=$(CC2) osf KTARGET=$${KTARGET:-$(@)} \ "KFLAGS= -DOSF40 -DHDBUUCP -DFNFLOAT \ -unsigned -std1 -O3 -Olimit 2400 $(KFLAGS)" "LIBS=-lm" du40gcc: @echo Making C-Kermit $(CKVER) for Digital UNIX 4.0 with gcc ... $(MAKE) osf CC=gcc CC2=gcc KTARGET=$${KTARGET:-$(@)} \ "KFLAGS= -DOSF40 -DHDBUUCP $(KFLAGS)" #Tru64 Unix 4.0E tru64-40e: @echo Making C-Kermit $(CKVER) for Tru64 UNIX 4.0E... $(MAKE) CC=$(CC) CC2=$(CC2) osf KTARGET=$${KTARGET:-$(@)} \ "KFLAGS= -DOSF40 -DOSF40E -DTRU64 -DHDBUUCP -DFNFLOAT -DNOCOTFMC \ -unsigned -std1 -O3 -Olimit 2400 $(KFLAGS)" "LIBS=-lm" tru64-40f: @echo Making C-Kermit $(CKVER) for Tru64 UNIX 4.0F... $(MAKE) CC=$(CC) CC2=$(CC2) osf KTARGET=$${KTARGET:-$(@)} \ "KFLAGS= -DOSF40 -DOSF40F -DTRU64 -DHDBUUCP -DFNFLOAT -DNOCOTFMC \ -unsigned -std1 -O3 -Olimit 2400 $(KFLAGS)" "LIBS=-lm" tru64-40g: @echo Making C-Kermit $(CKVER) for Tru64 UNIX 4.0G... $(MAKE) CC=$(CC) CC2=$(CC2) osf KTARGET=$${KTARGET:-$(@)} \ "KFLAGS= -DOSF40 -DOSF40G -DTRU64 -DHDBUUCP -DFNFLOAT -DNOCOTFMC \ -unsigned -std1 -O3 -Olimit 2400 $(KFLAGS)" "LIBS=-lm" tru64-50a: @echo Making C-Kermit $(CKVER) for Tru64 UNIX 5.0A... $(MAKE) CC=$(CC) CC2=$(CC2) osf KTARGET=$${KTARGET:-$(@)} \ "KFLAGS= -DTRU64 -DOSF50 -DHDBUUCP \ -unsigned -std1 -O3 -Olimit 2400 $(KFLAGS)" tru64-51a: @echo Making C-Kermit $(CKVER) for Tru64 UNIX 5.1A... $(MAKE) CC=$(CC) CC2=$(CC2) osf KTARGET=$${KTARGET:-$(@)} \ "KFLAGS= -DTRU64 -DOSF50 -DOSF51A -DHDBUUCP \ -unsigned -std1 -O3 -Olimit 2400 $(KFLAGS)" tru64-51b: @echo Making C-Kermit $(CKVER) for Tru64 UNIX 5.1B... $(MAKE) CC=$(CC) CC2=$(CC2) osf KTARGET=$${KTARGET:-$(@)} \ "KFLAGS= -DTRU64 -DOSF50 -DOSF51A -DOSF51B -DHDBUUCP \ -unsigned -std1 -O3 -Olimit 2400 $(KFLAGS)" # Added 5.1b version with OpenSSL - CDW 6-13-2005... tru64-51b+openssl: @echo Making C-Kermit $(CKVER) for Tru64 UNIX 5.1b @echo including OpenSSL... $(MAKE) CC=$(CC) CC2=$(CC2) osf KTARGET=$${KTARGET:-$(@)} \ "KFLAGS= -DTRU64 -DOSF50 -DOSF51A -DOSF51B -DHDBUUCP \ -unsigned -std1 -O3 -Olimit 2400 \ -DCK_AUTHENTICATION -DCK_SSL $(SSLINC) $(KFLAGS)" \ "LIBS= $(SSLLIB) -rpath $(sslroot)/ssl/lib -lssl -lcrypto" du50: $(MAKE) CC=$(CC) CC2=$(CC2) tru64-50a KTARGET=$${KTARGET:-$(@)} du40-ridiculous-checking: @echo Making C-Kermit $(CKVER) for Digital UNIX 4.0. @echo Checking everything - assumes DECC... $(MAKE) CC=$(CC) CC2=$(CC2) osf KTARGET=$${KTARGET:-$(@)} \ "KFLAGS= -DOSF40 -DHDBUUCP -w0 -warnprotos -check -portable \ -unsigned -std1 -O3 -Olimit 1760 $(KFLAGS)" #Sequent DYNIX/ptx 1.2.1 dynixptx12: @echo Making C-Kermit $(CKVER) for Sequent DYNIX/ptx 1.2.1... $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DSVR3 -DDIRENT -DHDBUUCP -DPTX -DNOGETUSERSHELL -DNOLEARN \ -DPID_T=pid_t -DUID_T=uid_t -DGID_T=gid_t $(KFLAGS) -i -O" \ "LNKFLAGS = -i" #Sequent DYNIX/ptx 1.3 or 1.4 dynixptx13: @echo Making C-Kermit $(CKVER) for Sequent DYNIX/ptx 1.3 TCP/IP... $(MAKE) xermit "CFLAGS= -O KTARGET=$${KTARGET:-$(@)} \ -DSVR3 -DDIRENT -DHDBUUCP -DPTX -DCK_POLL -DNOGETUSERSHELL \ -DPID_T=pid_t -DUID_T=uid_t -DGID_T=gid_t -DTCPSOCKET $(KFLAGS) -i" \ "LNKFLAGS = -i" "LIBS = -lsocket -linet -lnsl" #Sequent DYNIX/ptx 2.0, ANSI C compilation #Should work on any hardware platform when DYNIX/ptx runs, including #386, 486, Pentium. dynixptx20: @echo 'Making C-Kermit $(CKVER) for POSIX, Sequent DYNIX/ptx 2.0...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DPOSIX -DHDBUUCP -DTCPSOCKET \ -DWAIT_T=int -DPTX -DNOGETUSERSHELL $(KFLAGS) -O" \ "LIBS = -lsocket -linet -lnsl" #Sequent DYNIX/ptx 2.0, ANSI C compilation, with curses dynixptx20c: @echo 'Making C-Kermit $(CKVER) for POSIX, Sequent DYNIX/ptx 2.0...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DPOSIX -DHDBUUCP -DTCPSOCKET -DWAIT_T=int -DPTX -DCK_CURSES \ -DCK_NEWTERM -DNOGETUSERSHELL $(KFLAGS) -O" \ "LIBS = -lsocket -linet -lnsl -lcurses -ltermcap" #Sequent DYNIX/ptx 2.1.6, 80486, ANSI C compilation, with curses: # -Xa -- use ANSI compiler. # -Wc,-pw -- suppress portability warnings. # -Wc,-i386 -- 80386 cpu. # -Wc,-i486 -- 80486 cpu. # -Wc,-P5 -- Pentium (default). # -Wc,-O3 -- highest optimization. # -Wa,-N17061 -- increase symbol table from default of 15013 for ckcuni.c. # Early versions of DYNIX/ptx 2.1.x may need -DCK_POLL instead of -DSELECT. # Add "$&" after the colon in the "xermit" target for parallel makes. dynixptx216c: @echo 'Making C-Kermit $(CKVER) for POSIX, Sequent DYNIX/ptx 2.1.6' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DPOSIX -DHDBUUCP -DDYNAMIC -DTCPSOCKET \ -DSELECT -DCK_REDIR -DCK_NAWS -DCK_WREFRESH -DSW_ACC_ID \ -DTCP_NODELAY=1 -DTRMBUFL=2048 -DBIGBUFOK -DHADDRLIST \ -DPTX -DCK_CURSES -DCK_NEWTERM -DNOGETUSERSHELL -DNOREALPATH \ $(KFLAGS) -Xa -Wc,-pw -Wc,-i486 -Wc,-O3 -Wa,-N17061" \ "LIBS = -lXbsd -lseq -lsocket -linet -lnsl -lmalloc -lm -lcurses" \ "LNKFLAGS = -s" #Sequent DYNIX/ptx 2.1.6, gcc 2.7.2.2, with curses: dynixptx216cgcc: @echo 'Making C-Kermit $(CKVER) for POSIX, Sequent DYNIX/ptx 2.1.6 gcc' $(MAKE) xermit "CC = gcc" "CC2 = gcc" KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DPOSIX -DHDBUUCP -DDYNAMIC -DTCPSOCKET \ -DSELECT -DCK_REDIR -DCK_NAWS -DCK_WREFRESH -DSW_ACC_ID \ -DTCP_NODELAY=1 -DTRMBUFL=2048 -DBIGBUFOK -DHADDRLIST \ -DPTX -DCK_CURSES -DCK_NEWTERM -DNOGETUSERSHELL -DNOREALPATH \ $(KFLAGS) -O3 -pipe -funsigned-char" \ "LIBS = -lXbsd -lseq -lsocket -linet -lnsl -lmalloc -lm -lcurses" \ "LNKFLAGS = -s" #Sequent DYNIX/ptx 4.0, ANSI C compilation, with curses dynixptx41c: @echo 'Making C-Kermit $(CKVER) for POSIX, Sequent DYNIX/ptx 4.0...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DPOSIX -DHDBUUCP -DTCPSOCKET \ -DWAIT_T=int -DPTX -DPTX4 -DCK_CURSES -DCK_NEWTERM \ -DNOGETUSERSHELL $(KFLAGS) -O" \ "LIBS = -lsocket -lnsl -lcurses -ltermcap" #Sequent DYNIX/ptx 4.4, ANSI C compilation, with curses dynixptx44: @echo 'Making C-Kermit $(CKVER) for POSIX, Sequent DYNIX/ptx 4.4...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DPTX -DPTX4 -DPOSIX -DHDBUUCP -DTCPSOCKET -DWAIT_T=int \ -DCK_CURSES -DCK_NEWTERM -DBIGBUFOK -DSELECT -DNOGETUSERSHELL \ $(KFLAGS) -O" "LIBS = -lsocket -lnsl -lcurses -ltermcap" #Sequent DYNIX 3.0.x dynix3: @echo Making C-Kermit $(CKVER) for Sequent DYNIX 3.0.x... $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DBSD43 -DACUCNTRL -DTCPSOCKET -O \ -DPWUID_T=int -DGID_T=int $(KFLAGS)" #Sequent DYNIX 3.0.x, no ACUCNTRL dynix3noacu: @echo Making C-Kermit $(CKVER) for Sequent DYNIX 3.0.x... $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DBSD43 -DLCKDIR -DTCPSOCKET -O \ -DUID_T=int -DGID_T=int $(KFLAGS)" #Sequent DYNIX 3.1.x dynix31: @echo Making C-Kermit $(CKVER) for Sequent DYNIX 3.1.x... $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -O -DDCLPOPEN -DLCKDIR -DBSD4 -DTCPSOCKET $(KFLAGS)" #Sequent DYNIX 3.1.2, as above but with curses, to be compiled by gcc 2.3.3. dynix31c: @echo 'Making C-Kermit $(CKVER) for Sequent DYNIX 3.1.2, curses...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -O2 -DDCLPOPEN -DACUCNTRL \ -DBSD43 -DTCPSOCKET -DCK_CURSES -DUID_T=int \ $(KFLAGS)" "LIBS= -lcurses -ltermcap" #Convex C1 with Berkeley Unix convex: @echo Making C-Kermit $(CKVER) for Convex C1 / BSD... $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DBSD4 -DNOLEARN $(KFLAGS) -Dmsleep=mnap" #Convex C210 with Convex/OS 8 convex8: @echo Making C-Kermit $(CKVER) for Convex C210 with OS 8 $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DBSD4 -DTCPSOCKET -DNODEBUG -DDIRENT -DNOFILEH \ $(KFLAGS) -DSIG_V -Dmsleep=mnap" #Convex C2 with Convex OS 9.1 (should also work with 8.1 or later) #with ANSI C compiler, uses BSD 4.3 uucp lockfile convention. convex9: @echo Making C-Kermit $(CKVER) for Convex C210 with OS 9.1 $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DPOSIX -DCONVEX9 -DNOIEXTEN -DDIRENT -DNOFILEH -DTCPSOCKET \ -D__STDC__ -DLCKDIR -Dmsleep=mnap -O -ext -tm c1 $(KFLAGS)" \ "LNKFLAGS = -ext" #Convex C2 with Convex OS 10.1 or later #with gcc 2.x C compiler convex10gcc: @echo Making C-Kermit $(CKVER) for Convex C2 with OS 10.1 using gcc $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DPOSIX -DCONVEX9 -DNOIEXTEN -DDIRENT -DNOFILEH -DTCPSOCKET \ -D__STDC__ -Dmsleep=mnap -O2 $(KFLAGS)" CC=gcc CC2=gcc #Cray X-MP or Y-MP UNICOS 6.x or 7.x. #NOTE: NPROC tells how many parallel makes to run. If your Cray has multiple #processors, you can set NPROC up to the number of CPUs, e.g. NPROC=16. cray: @echo 'Making C-Kermit $(CKVER) for Cray X/Y-MP UNICOS 6.x or 7.0... $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} NPROC=1 \ "CFLAGS= -DSVR3 -DDIRENT -DHDBUUCP -DTCPSOCKET $(KFLAGS) -O1" #Cray X-MP or Y-MP UNICOS 8.0 Alpha. cray8: @echo 'Making C-Kermit $(CKVER) for Cray X/Y-MP UNICOS 8.0 Alpha... $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} NPROC=1 \ "CFLAGS= -DSVR4 -DDIRENT -DHDBUUCP -DTCPSOCKET $(KFLAGS) -O1" #Cray X-MP or Y-MP UNICOS 9.0. #This one was executed successfully for C-Kermit 8.0.209. #Earlier versions of Unicos will probably need the same flags. cray9: @echo 'Making C-Kermit $(CKVER) for Cray X/Y-MP UNICOS 9.0... $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} NPROC=1 \ "CFLAGS= -DSVR4 -DDIRENT -DHDBUUCP -DNOLFDEVNO \ -DTCPSOCKET $(KFLAGS) -O1" #Cray-2 or Cray 3-CSOS #NOTE: NPROC tells how many parallel makes to run. If your Cray has multiple #processors, you can set NPROC up to the number of CPUs, e.g. NPROC=16. craycsos: @echo 'Making C-Kermit $(CKVER) for Cray-2/3 CSOS $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} NPROC=1 \ "CFLAGS= -DSVR3 -DDIRENT -DHDBUUCP -DTCPSOCKET \ $(KFLAGS) -DCK_ANSIC -DCK_CURSES" "LIBS=-lnet" #NeXTSTEP 1.0 through 3.2. #Includes fullscreen file transfer display (curses) and TCP/IP support. #Uses shared library to make executable program about 80K smaller. #Remove "LIBS = -lsys_s" if this causes trouble. next: @echo Making C-Kermit $(CKVER) for NeXTSTEP... @echo 'If you get errors in ckutio.c about w_S, w_T, etc,' @echo 'add KFGLAGS=-DNOREDIRECT to your make command.' $(MAKE) xermit CC=$(CC) CC2=$(CC2) KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DNEXT -DTCPSOCKET -DLCKDIR -DNOPUTENV -DFNFLOAT \ -pipe -DCK_CURSES $(KFLAGS) -O -w" "LIBS = -lsys_s -lcurses -ltermcap" nextc: $(MAKE) "MAKE=$(MAKE)" CC=$(CC) CC2=$(CC2) next \ KTARGET=$${KTARGET:-$(@)} nextg: $(MAKE) "MAKE=$(MAKE)" CC=$(CC) CC2=$(CC2) next \ KFLAGS=-Wall KTARGET=$${KTARGET:-$(@)} nextgc: $(MAKE) "MAKE=$(MAKE)" CC=$(CC) CC2=$(CC2) next \ KFLAGS=-Wall KTARGET=$${KTARGET:-$(@)} #NeXTSTEP 3.3. #Includes fullscreen file transfer display and TCP/IP. # You might have to add 1 line to 1 NeXT header file # to declare n_long as u_long by adding #include next33: @echo Making C-Kermit $(CKVER) for NeXTSTEP 3.3... $(MAKE) xermit CC=$(CC) CC2=$(CC2) KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DNEXT33 -DTCPSOCKET -DLCKDIR -DNOPUTENV -DFNFLOAT \ -pipe -DCK_CURSES $(KFLAGS) -O -w" "LIBS = -lsys_s -lcurses -ltermcap" #OPENSTEP 4.2 for Sparc, m680x0, HP PA-RISC, and Intel. #Includes fullscreen file transfer display and TCP/IP. #ckcpro.c compiled without optimization because it crashes the compiler. openstep42: @echo Making C-Kermit $(CKVER) for OPENSTEP 4.2... $(MAKE) ckcpro.$(EXT) \ "CFLAGS= -DOPENSTEP42 -DNEXT33 -DTCPSOCKET -DLCKDIR -DNOPUTENV \ -DFNFLOAT -pipe -DCK_CURSES $(KFLAGS) -w" $(MAKE) xermit CC=$(CC) CC2=$(CC2) KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DOPENSTEP42 -DNEXT33 -DTCPSOCKET -DLCKDIR -DNOPUTENV \ -DFNFLOAT -pipe -DCK_CURSES $(KFLAGS) -O -w" \ "LIBS = -lsys_s -lcurses -ltermcap" #NeXT with malloc debugger nextmd: @echo Making C-Kermit $(CKVER) for NeXT with malloc debugging... $(MAKE) mermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DNEXT -DTCPSOCKET -DLCKDIR -DNOPUTENV -DFNFLOAT \ -DCK_CURSES $(KFLAGS) -O -w -Dmalloc=dmalloc -Dfree=dfree -DMDEBUG" \ "LIBS = -lsys_s -lcurses -ltermcap" #Build for NeXTSTEP with "fat" binaries (MABs) that run on both Motorola #and Intel platforms. nextfat: $(MAKE) "MAKE=$(MAKE)" CC=$(CC) CC2=$(CC2) \ next KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-Wall -arch m68k -arch i386" "LNKFLAGS = -arch m68k -arch i386" #NeXTSTEP on Intel Platforms. next486: @echo Making C-Kermit $(CKVER) for NeXTSTEP on Intel Platforms... @echo 'If you get errors in ckutio.c about w_S, w_T, etc,' @echo 'add KFGLAGS=D-DNOREDIRECT to your make command.' $(MAKE) xermit CC=$(CC) CC2=$(CC2) KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DNEXT -DTCPSOCKET -DLCKDIR -DNOPUTENV -DFNFLOAT \ -DNODEBUG -O3 -fno-omit-frame-pointer -fschedule-insns2 -pipe \ -DCK_CURSES $(KFLAGS) -w" "LIBS = -lsys_s -lcurses -ltermcap" #Single binary that runs on NeXT 68030 and 68040, Intel, HP, and Sparc, #as well as on OpenStep/Mach. nextquadfat: $(MAKE) "MAKE=$(MAKE)" CC=$(CC) CC2=$(CC2) next \ KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-Wall -arch m68k -arch i386 -arch hppa -arch sparc" \ "LNKFLAGS = -arch m68k -arch i386 -arch hppa -arch sparc" #BeBox beboxdr7: @echo 'Making C-Kermit $(CKVER) for the BeBox...' @echo 'Link step will fail with default Metroworks linker 64K limit.' @echo 'Code Warrior Gold required to link big programs.' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CC=/boot/develop/tools/mwcc" "CC2=/boot/develop/tools/mwld" \ "CFLAGS= -DBEBOX -DBE_DR_7 -DPOSIX -DNOUUCP -DNOLEARN $(KFLAGS) -O" #BeBox BeOS DR7 only bebox: @echo 'Making C-Kermit $(CKVER) for BeBox...' @echo 'Link step will fail with default Metroworks linker 64K limit.' @echo 'Code Warrior Pro 3.0 for BeBox required to link big programs.' $(MAKE) wermit "CC=mwcc" "CC2=mwld" KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DBEBOX -DPOSIX -DNOLEARN -DNOUUCP $(KFLAGS) -O" #BeOS 4.5 #We have to use the wermit target because 'fd_set' is unknown. beos45: $(MAKE) wermit "CC=$(CC)" "CC2=$(CC2)" KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DBEOS -DBEOS45 -DPOSIX -DNOIKSD -DNOREALPATH -DSYSTIMEH \ -DNOCOTFMC -DNOUUCP -DNOLEARN $(KFLAGS) -O" \ "LIBS = $(LIBS)" #BeOS 4.5 beos45net: $(MAKE) CC=$(CC) CC2=$(CC2) beos45 \ "KFLAGS=-DTCPSOCKET -DNO_DNS_SRV $(KFLAGS)" "LIBS=-lnet -lnetapi" #Plan 9 from Bell Labs plan9: @echo 'C-Kermit for Plan 9 from Bell Labs - calling ckpker.mk...' make -f ckpker.mk #POSIX posix: @echo 'Making C-Kermit $(CKVER) for pure POSIX...' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DPOSIX -DNOUUCP -DNOLEARN $(KFLAGS) -O" # PowerMAX OS (SVR4) from Concurrent (tested on PowerMAX 5.1) powermax: @echo 'Making C-Kermit $(CKVER) for Concurrent PowerMAX OS...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -O -DSVR4 -DDIRENT -DHDBUUCP -DPOWERMAX \ -DNETPTY -DHAVE_STREAMS -DHAVE_GRANTPT -DHAVE_PTSNAME -DPUSH_PTEM \ -DPUSH_LDTERM -DPUSH_TTCOMPAT \ -DSTERMIOX -DTCPSOCKET -DCK_CURSES $(KFLAGS)" \ "LIBS= -lsocket -lnsl -lresolv -lcurses -lgen -lc -lucbc" #Berkeley Software Design Inc. BSDI # Substitute "LIBS= -lnewcurses -ltermcap" if desired. bsdi: @echo 'Making C-Kermit $(CKVER) for BSDI ...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DBSD44 -DSETREUID -DSW_ACC_ID -DBIGBUFOK -DFIXCRTSCTS \ -DTCPSOCKET -DCK_CURSES -DFNFLOAT $(KFLAGS) -O" \ "LIBS= -lcurses -ltermcap -lm" #Berkeley Software Design Inc. BSDI - has higher serial speeds than 1.x. bsdi2: $(MAKE) "MAKE=$(MAKE)" CC=$(CC) CC2=$(CC2) bsdi \ KTARGET=$${KTARGET:-$(@)} "KFLAGS=-DBSDI2 $(KFLAGS)" bsdi3: $(MAKE) "MAKE=$(MAKE)" CC=$(CC) CC2=$(CC2) bsdi \ KTARGET=$${KTARGET:-$(@)} "KFLAGS=-DBSDI2 -DBSDI3 $(KFLAGS)" bsdi4: $(MAKE) "MAKE=$(MAKE)" CC=$(CC) CC2=$(CC2) bsdi \ KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-DBSDI2 -DBSDI3 -DBSDI4 -DTPUTSFNTYPE=void -DTPUTSISVOID \ -DCKHTTP -m486 $(KFLAGS)" # (old name for the above) bsdiposix: $(MAKE) "MAKE=$(MAKE)" CC=$(CC) CC2=$(CC2) bsdi #Build a BSDI 4.x binary that also runs under FreeBSD (Terry Kennedy). #But watch out for details like serial-port locking. bsdix: $(MAKE) "MAKE=$(MAKE)" CC=$(CC) CC2=$(CC2) bsdi \ KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-DBSDI2 -DBSDI3 -DBSDI4 -DTPUTSFNTYPE=void -DTPUTSISVOID \ -m486 $(KFLAGS)" "LNKFLAGS=-static -Wl,-m,i386bsdi -Wl,-e,_start" #Pyramid 9XXX (e.g. 9845) or MIServer T series, OSx 4.4b thru 5.1 pyramid: @echo Making C-Kermit $(CKVER) for Pyramid Dual Port OSx ucb $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DBSD43 -DTCPSOCKET -DPYRAMID -O $(KFLAGS)" "LNKFLAGS = -s" #Pyramid Dual Port OSx using HoneyDanBer UUCP, curses and TCP pyramid-hdb: @echo Making C-Kermit $(CKVER) for Pyramid Dual Port OSx ucb $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DBSD43 -DTCPSOCKET -DHBDUUCP -DCK_CURSES -O $(KFLAGS)" \ "LNKFLAGS = -s" "LIBS = -lcurses -ltermcap" #Pyramid DC/OSx (UNIX System V R4). #Has , regular Berkeley sockets library, i.e. in.h and inet.h #are not misplaced in sys (rather than netinet and arpa, respectively). #Uses ANSI C. #NOTE: Remove -O and Olimit:2500 from CFLAGS if TELNET connections do not work. pyrdcosx: @echo 'Making C-Kermit $(CKVER) for Pyramid DC/OSx...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -Xa -O -DSVR4 -DDIRENT -DHDBUUCP -DSELECT -DNOGETUSERSHELL \ -DCK_CURSES -DSTERMIOX -DTCPSOCKET -DPYRAMID -K Olimit:3100 \ -DNO_DNS_SRV $(KFLAGS)" "LIBS= -lcurses -lsocket -lnsl" "LNKFLAGS = -s" #IBM's AIX 3.0 on IBM 370 mainframe, tested on AIX F44 thru F50. aix370: @echo Making C-Kermit $(CKVER) for IBM System/370 AIX 3.0... $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DAIX370 -DTCPSOCKET -DLCKDIR -DDIRENT $(KFLAGS)" \ "LIBS = -lbsd" #IBM's AIX/ESA 2.1 (OSF/1) on IBM mainframe aixesa: @echo Making C-Kermit $(CKVER) for IBM AIX/ESA... $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DAIXESA -DTCPSOCKET $(KFLAGS) -O" #IBM PS/2 with AIX 1.0 thru 1.3. # Reports indicate that -O switch must be omitted. # It is also possible that "make bsd" will work (reports welcome). # One report said "make LIBS=-lbsd bsd" did the trick. # NOTLOG is to get around a 'tlog' symbol defined in one of the headers. ps2aix: @echo 'Making C-Kermit $(CKVER) for IBM AIX 1.x PS/2...' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DATTSV -DNOREALPATH -DPS2AIX10 -DSIG_V \ -DNOUNICODE -DNOTLOG -DNOLEARN $(KFLAGS) -i" \ "LNKFLAGS = -i" ps2aixnetc: @echo 'Making C-Kermit $(CKVER) for IBM AIX 1.x PS/2...' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DATTSV -DNOREALPATH -DPS2AIX10 -DTCPSOCKET -DCK_CURSES \ -DSIG_V -DNOUNICODE -DNOTLOG -DNOLEARN $(KFLAGS) -i" \ "LIBS = -lcurses" "LNKFLAGS = -i" ps2aix3: $(MAKE) ps2aix KTARGET=$${KTARGET:-$(@)} #IBM RT PC with AIX 2.2.1, valid as of C-Kermit 8.0. #NOTLOG because of a conflict in . #This one has unique and strange lockfiles. # -O removed on purpose (8.0). # In case of "compiler error: symbol table full", increase the -Nn number. # In case of "compiler error: Constant pool too big", boost the -Np number. # Add -DNOPUTENV if putenv() causes trouble. # Put -DNOIKSD back if IKSD-related problems occur. rtaix: @echo 'Making C-Kermit $(CKVER) for IBM RT PC, AIX 2.2.1...' $(MAKE) xermit KTARGET=$${KTARGET-$(@)} \ "CFLAGS = -DATTSV -DRTAIX -DHDBUUCP -DDIRENT -DNOTLOG -DTCPSOCKET \ -DNOGETUSERSHELL -DCLSOPN -DNOREALPATH -DNOUNICODE -DBSD_INCLUDES \ -DUSE_LSTAT -DFNFLOAT -Nn2500 -Np1000 -Wq,-SJ2 -a -w $(KFLAGS)" \ "LIBS = -lm $(LIBS)" "LNKFLAGS = -s" #### IBM RT PC - these targets were last verified in C-Kermit 8.0.211. #IBM RT PC with AIX 2.2.1 + curses rtaixc: $(MAKE) rtaix CC=$(CC) CC2=$(CC2) "KFLAGS=-DCK_CURSES" "LIBS=-lcurses" #IBM RT PC with AIX (ACIS) 2.2.1 (BSD 4.3) # Add -O, -DDYNAMIC, -s, etc, if they work. rtacis: @echo Making C-Kermit $(CKVER) for RT PC with ACIS 2.2.1 = BSD 4.3... $(MAKE) xermit KTARGET=$${KTARGET-$(@)} \ "CFLAGS= -DBSD4 -DTCPSOCKET -DNOREALPATH -DNOIKSD -DNOPUTENV \ $(KFLAGS) -U__STDC__" "LNKFLAGS = -s" #### IBM AIX. The first two targets should work for any version of AIX #### from 4.2 onwards. The ones after that are for older versions or #### specific configurations, and/or with gcc. # This one should work for any AIX 4.2 or later: "make aix". # Other tags are for compatibility with old makefile targets. #OK: 2011/06/11 aix aix42 aix43 aix44 aix45 aix50 aix51 aix52 aix53 aix54 aix61: @echo Making C-Kermit $(CKVER) for IBM AIX... $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DAIXRS -DAIX41 -DAIX42 -DSVR4 -DSTERMIOX -DTCPSOCKET \ -DDIRENT -DCK_ANSIC -DCLSOPN -DCK_CURSES -DCK_NEWTERM -DFNFLOAT \ -DSELECT -DSELECT_H -DNOGETUSERSHELL -qmaxmem=16000 -O \ -DCKCPU=\\\"`uname -p`\\\" \ -DHERALD=\"\\\" IBM AIX `uname -v`.`uname -r`\\\"\" \ -D_LARGE_FILES $(KFLAGS)" "LNKFLAGS = -s" "LIBS=-lcurses -lm" # Same but using gcc instead of cc # This works but we get "gcc: unrecognized option '-qmaxmem-..'" each module. aixg: @echo "Using gcc..." $(MAKE) aix KTARGET=$${KTARGET:-$(@)} \ CC=gcc CC2=gcc "KFLAGS=-pipe -funsigned-char" # AIX 4.2 or later with OpenSSL 0.9.7 or later: "make aix+ssl" # For earlier OpenSSL remove -DOPENSSL_097 or add "KFLAGS=-UOPENSSL_097". # Synonym target names added to cover old redundant targets that were removed. # If SSL is not installed in the /usr/local tree (see SSLINC and SSLLIB # definitions near the top), you can specify the locations in your make # command as in this example: # # SSLINC=-I/opt/ssl/include SSLLIB=-L/opt/ssl/lib make -e aix+ssl # # To build with gcc use "make aix CC=gcc CC2=gcc", or "make aixg" # #OK: 2011/06/15 aix+ssl aix51+openssl aix52+openssl aix53+openssl: @echo "Making C-Kermit $(CKVER) for IBM AIX with OpenSSL..." @echo "SSLINC=$(SSLINC) SSLLIB=$(SSLLIB)" $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ CC=$(CC) CC2=$(CC2) \ "CFLAGS=-DAIXRS -DAIX41 -DAIX42 -DSVR4 -DSTERMIOX -DTCPSOCKET \ -DDIRENT -DCK_ANSIC -DCLSOPN -DCK_CURSES -DCK_NEWTERM -DFNFLOAT \ -D_LARGE_FILES -DSELECT -DSELECT_H -DNOGETUSERSHELL \ -DCKCPU=\\\"`uname -p`\\\" \ -DHERALD=\"\\\" IBM AIX `uname -v`.`uname -r`\\\"\" \ -DCK_AUTHENTICATION -DCK_SSL -DOPENSSL_097 $(SSLINC) $(KFLAGS)" \ "LNKFLAGS=-s" "LIBS=$(SSLLIB) -lssl -lcrypto -lcurses -lm -lcrypt" # AIX 5.3 or 6.1 or later with IBM OpenSSL, which is always in the directories # shown below so you don't have to set SSLINC and SSLLIB. If for some reason # the SSL include files and libraries are not in the places assumed, then use # "make aix+ssl" (just above) and set SSLINC and SSLLIB to indicate where the # SSL files are. To build with gcc use "make aix+ibmssl CC=gcc CC2=gcc". aix+ibmssl: @echo "Making C-Kermit $(CKVER) for IBM AIX 6.1 with OpenSSL..." @echo "If this fails use 'make aix+ss' and specify SSLINC and SSLLIB" $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ CC=$(CC) CC2=$(CC2) \ "CFLAGS=-DAIXRS -DAIX41 -DAIX42 -DSVR4 -DSTERMIOX -DTCPSOCKET \ -DDIRENT -DCK_ANSIC -DCLSOPN -DCK_CURSES -DCK_NEWTERM -DFNFLOAT \ -D_LARGE_FILES -DSELECT -DSELECT_H -DNOGETUSERSHELL \ -DCKCPU=\\\"`uname -p`\\\" \ -DHERALD=\"\\\" IBM AIX `uname -v`.`uname -r`\\\"\" \ -DCK_AUTHENTICATION -DCK_SSL -DOPENSSL_098 \ -I/usr/include/openssl $(KFLAGS)" \ "LNKFLAGS=-s" \ "LIBS=-L/usr/lib/openssl -lssl -lcrypto -lcurses -lm -lcrypt" # Old AIX versions... #IBM AIX 3.0, 3.1, or 3.2 for RISC System/6000. rs6000: @echo Making C-Kermit $(CKVER) for IBM AIX 3.0 or 3.1, RS/6000... $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DAIXRS -DTCPSOCKET -DSVR3 -DDIRENT -DCK_ANSIC \ -DCK_POLL -DCLSOPN -DSELECT_H -DNOTTYLOCK -O $(KFLAGS)" \ "LNKFLAGS = -s" #IBM AIX 3.0, 3.1, or 3.2 for RISC System/6000, with curses. rs6000c: @echo Making C-Kermit $(CKVER) for IBM AIX 3.0 or 3.1, RS/6000... $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DAIXRS -DTCPSOCKET -DSVR3 -DDIRENT -DCK_ANSIC \ -DCK_POLL -DCLSOPN -DCK_CURSES -DSELECT_H -DNOTTYLOCK -DNOREALPATH \ -O $(KFLAGS)" "LIBS= -lcurses -ltermcap" "LNKFLAGS = -s" aix30: $(MAKE) rs6000 CC=$(CC) CC2=$(CC2) KTARGET=$${KTARGET:-$(@)} aix31: $(MAKE) rs6000 CC=$(CC) CC2=$(CC2) KTARGET=$${KTARGET:-$(@)} #IBM AIX 3.2 for RISC System/6000. #In case of "subprogram too complex" warnings, add "-qmaxmem=16000" to CFLAGS. rs6aix32: @echo Making C-Kermit $(CKVER) for IBM AIX 3.2, RS/6000... $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DAIXRS -DTCPSOCKET -DSVR4 -DDIRENT -DCK_ANSIC -DNOREALPATH \ -DSELECT_H -DCLSOPN -DNOTTYLOCK -O $(KFLAGS)" "LNKFLAGS = -s" #IBM AIX 3.2 for RISC System/6000. rs6aix32c: @echo Making C-Kermit $(CKVER) for IBM AIX 3.2, RS/6000, TCP+curses... @echo In case of Subprogram Too Complex warnings, @echo add -qmaxmem=16000 to CFLAGS. $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DAIXRS -DTCPSOCKET -DSVR4 -DDIRENT -DCK_ANSIC -DNOREALPATH \ -DCLSOPN -DCK_CURSES -DSELECT_H -DNOTTYLOCK -O $(KFLAGS)" \ "LNKFLAGS = -s" "LIBS=-lcurses" aix32: $(MAKE) rs6aix32c KTARGET=$${KTARGET:-$(@)} #IBM AIX 4.1, 4.1.x on RISC System/6000 or Power Series. #Generates common binary for all platforms if using xlc (IBM C compiler). #When using gcc, add -mcpu=common to generate common binary. #Note that this one needs CK_NEWTERM. # Add -bbigtoc in case ld fails with TOC overflow. aix41: @echo Making C-Kermit $(CKVER) for IBM AIX 4.1.1 RS/6000 or PowerPC... $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DAIXRS -DAIX41 -DSVR4 -DSTERMIOX -DTCPSOCKET -DDIRENT \ -DCK_ANSIC -DCLSOPN -DCK_CURSES -DCK_NEWTERM -DSELECT -DSELECT_H \ -DNOGETUSERSHELL -qmaxmem=16000 -O $(KFLAGS)" \ "LNKFLAGS = -s" "LIBS=-lcurses" #Ditto but with gcc. #Remove "CC=gcc CC2=gcc" if you have gcc installed as cc. aix41g: @echo Making C-Kermit $(CKVER) for IBM AIX 4.1.1 RS/6000 or PowerPC... $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} "CC=gcc" "CC2=gcc" \ "CFLAGS= -DAIXRS -DAIX41 -DSVR4 -DSTERMIOX -DTCPSOCKET -DDIRENT \ -DCK_ANSIC -DCLSOPN -DCK_CURSES -DCK_NEWTERM -DSELECT -DSELECT_H \ -DNOGETUSERSHELL -O $(KFLAGS)" \ "LNKFLAGS = -s -Xlinker -bbigtoc" "LIBS=-lcurses" # Add -bbigtoc in case ld fails with TOC overflow. aix41+krb5+krb4: @echo Making C-Kermit $(CKVER) for IBM AIX 4.1.1 RS/6000 or PowerPC... $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DAIXRS -DAIX41 -DSVR4 -DSTERMIOX -DTCPSOCKET -DDIRENT \ -DCK_ANSIC -DCLSOPN -DCK_CURSES -DCK_NEWTERM -DSELECT -DSELECT_H \ -DCK_AUTHENTICATION -DCK_KERBEROS -DKRB5 -DKRB4 -DKRB524 \ -DCK_ENCRYPTION -DCK_DES $(K5INC) $(K5INC)/krb5 \ -DNOGETUSERSHELL -qmaxmem=16000 -O $(KFLAGS)" \ "LNKFLAGS = -s" \ "LIBS = $(K5LIB) -lcurses -lkrb4 -ldes425 -lkrb5 \ -lcom_err -lk5crypto -lgssapi_krb5" #Old name for "aix41". rs6aix41c: $(MAKE) aix41 KTARGET=$${KTARGET:-$(@)} #IBM AIX 4.1, 4.1.x, or 4.2 on RISC System/6000 or Power Series, # with X.25 support #Generates common binary for all platforms if using xlc (IBM C compiler). #When using gcc, add -mcpu=common to generate common binary. # Add -bbigtoc in case ld fails with TOC overflow. aix41x25: @echo Making C-Kermit $(CKVER) for IBM AIX 4.1.1 RS/6000 or PowerPC... $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DAIXRS -DAIX41 -DSVR4 -DSTERMIOX -DTCPSOCKET -DDIRENT \ -DCK_ANSIC -DCLSOPN -DCK_CURSES -DCK_NEWTERM -DSELECT -DSELECT_H \ -DIBMX25 -DDEBUG -DNOGETUSERSHELL -qmaxmem=16000 -g $(KFLAGS)" \ "LNKFLAGS = -g -bI:/lib/pse.exp" "LIBS=-lcurses -lodm -lcfg" -@echo "]0;kermit done\c" #As above but without -g in LNKFLAGS. # Add -bbigtoc in case ld fails with TOC overflow. aix41x25o: @echo Making C-Kermit $(CKVER) for IBM AIX 4.1.1 RS/6000 or PowerPC... $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DAIXRS -DAIX41 -DSVR4 -DSTERMIOX -DTCPSOCKET -DDIRENT \ -DCK_ANSIC -DCLSOPN -DCK_CURSES -DCK_NEWTERM -DSELECT -DSELECT_H \ -DIBMX25 -DNODEBUG -DNOGETUSERSHELL -qmaxmem=16000 $(KFLAGS)" \ "LNKFLAGS = -bI:/lib/pse.exp" "LIBS=-lcurses -lodm -lcfg" -@echo "]0;kermit done\c" #AIX 4.2 -- Use this target if the regular "make aix" doesn't work. # Must have CK_NEWTERM or echoing is lost after curses. # Add -bbigtoc in case ld fails with TOC overflow. As of C-Kermit 8.0.212, # all AIX builds 4.2 and later include large file support. oldaix42: @echo Making C-Kermit $(CKVER) for IBM AIX 4.2 or higher... $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DAIXRS -DAIX41 -DAIX42 -DSVR4 -DSTERMIOX -DTCPSOCKET \ -DDIRENT -DCK_ANSIC -DCLSOPN -DCK_CURSES -DCK_NEWTERM -DFNFLOAT \ -DSELECT -DSELECT_H -DNOGETUSERSHELL -qmaxmem=16000 -O \ -DCKCPU=\\\"`uname -p`\\\" \ -DHERALD=\\\"\ IBM\ AIX\ `uname -v`.`uname -r`\\\" \ -D_LARGE_FILES $(KFLAGS)" "LNKFLAGS = -s" "LIBS=-lcurses -lm" #AIX 4.3 - Use this target if the regular "make aix" doesn't work. # Must NOT have CK_NEWTERM or else C-Kermit hangs after curses. # -bbigtoc needed on some systems but not others to avoid TOC overflow. # "man ld" says -bbigtoc makes program run slower. oldaix43: @echo Making C-Kermit $(CKVER) for IBM AIX 4.3 or higher... $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DAIXRS -DAIX41 -DAIX43 -DSVR4 -DSTERMIOX -DTCPSOCKET \ -DDIRENT -DCK_ANSIC -DCLSOPN -DCK_CURSES -DSELECT -DSELECT_H \ -DFNFLOAT -DNOGETUSERSHELL -qmaxmem=16000 -bbigtoc -O $(KFLAGS)" \ "LNKFLAGS = -s" "LIBS=-lcurses -lm" #AIX 4.3 with IBM X.25. aix43x25: @echo "Making C-Kermit $(CKVER) for IBM AIX 4.3 with X.25..." $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DAIXRS -DAIX41 -DAIX43 -DSVR4 -DSTERMIOX -DTCPSOCKET \ -DDIRENT -DCK_ANSIC -DCLSOPN -DCK_CURSES -DSELECT -DSELECT_H \ -DFNFLOAT -DNOGETUSERSHELL -DIBMX25 \ -qmaxmem=16000 -bbigtoc -O $(KFLAGS)" \ "LNKFLAGS = -bI:/lib/pse.exp" "LIBS=-lcurses -lodm -lcfg -lm" #AIX 4.3 -- Must NOT have CK_NEWTERM or else C-Kermit hangs after curses. # -mminimal-toc needed on some systems but not others to avoid TOC overflow. # "man ld" says -bbigtoc makes program run slower. aix43g: @echo Making C-Kermit $(CKVER) for IBM AIX 4.3 gcc... $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} CC=gcc CC2=gcc \ "CFLAGS= -mminimal-toc -g -O -DAIXRS -DAIX41 -DAIX43 -DSVR4 \ -DDIRENT -DCK_ANSIC -DCLSOPN -DCK_CURSES -DSELECT -DSELECT_H \ -DSTERMIOX -DTCPSOCKET -DFNFLOAT -DNOGETUSERSHELL $(KFLAGS)" \ "LIBS=-lcurses -lm" aix43gcc: $(MAKE) aix43g # None of the following aix43gcc attempts work on a gcc-only AIX 4.3.3 box. # It just plain can't find the math routines (fmod, pow, exp, sqrt, log10,...) # Which is odd because nm /usr/lib/libC.a finds them... #in case aix43gcc can't find its math library... aix43gccx: @echo Making C-Kermit $(CKVER) for IBM AIX 4.3 gcc... $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} CC=gcc CC2=gcc \ "CFLAGS= -mminimal-toc -g -O -DAIXRS -DAIX41 -DAIX43 -DSVR4 \ -DDIRENT -DCK_ANSIC -DCLSOPN -DCK_CURSES -DSELECT -DSELECT_H \ -DSTERMIOX -DTCPSOCKET -DFNFLOAT -DNOGETUSERSHELL $(KFLAGS)" \ "LIBS= -L/usr/local/lib/gcc-lib/powerpc-ibm-aix4.3.1.0/2.95.2 \ -lcurses -bloadmap -bnoquiet" #in case aix43gccx can't find its math library... aix43gccy: @echo Making C-Kermit $(CKVER) for IBM AIX 4.3 gcc... $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} CC=gcc CC2=gcc \ "CFLAGS= -mminimal-toc -g -O -DAIXRS -DAIX41 -DAIX43 -DSVR4 \ -DDIRENT -DCK_ANSIC -DCLSOPN -DCK_CURSES -DSELECT -DSELECT_H \ -DSTERMIOX -DTCPSOCKET -DFNFLOAT -DNOGETUSERSHELL $(KFLAGS)" \ "LIBS= -lcurses -bloadmap -bnoquiet" #in case aix43gccx can't find its math library... aix43gccz: @echo Making C-Kermit $(CKVER) for IBM AIX 4.3 gcc... $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} CC=gcc CC2=gcc \ "CFLAGS= -mminimal-toc -g -O -DAIXRS -DAIX41 -DAIX43 -DSVR4 \ -DDIRENT -DCK_ANSIC -DCLSOPN -DCK_CURSES -DSELECT -DSELECT_H \ -DSTERMIOX -DTCPSOCKET -DFNFLOAT -DNOGETUSERSHELL $(KFLAGS)" \ "LIBS= -L. -lcurses -bloadmap -bnoquiet" #AIX 4.3 with MIT Kerberos 5 and Kerberos 4 compatibility mode # Must NOT have CK_NEWTERM or else C-Kermit hangs after curses. # -mminimal-toc needed on some systems but not others to avoid TOC overflow. # "man ld" says -bbigtoc makes program run slower. aix43gcc+krb5+krb4: @echo Making C-Kermit $(CKVER) for IBM AIX 4.3 or higher w/Kerberos... $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} CC=gcc CC2=gcc \ "CFLAGS= -mminimal-toc -g -O -DAIXRS -DAIX41 -DAIX43 -DSVR4 \ -DDIRENT -DCK_ANSIC -DCLSOPN -DCK_CURSES -DSELECT -DSELECT_H \ -DSTERMIOX -DTCPSOCKET -DFNFLOAT -DNOGETUSERSHELL \ -DCK_AUTHENTICATION -DCK_KERBEROS -DKRB5 -DKRB4 -DKRB524 \ -DCK_ENCRYPTION -DCK_DES -funsigned-char $(K5INC) $(K5INC)/krb5 \ $(KFLAGS)" \ "LIBS=$(K5LIB) -lcurses -lm -lkrb4 -ldes425 -lkrb5 \ -lcom_err -lk5crypto -lcrypt -lgssapi_krb5" #AIX 4.3 with MIT Kerberos 5, Kerberos 4 compatibility mode and OpenSSL # Must NOT have CK_NEWTERM or else C-Kermit hangs after curses. # -mminimal-toc needed on some systems but not others to avoid TOC overflow. # "man ld" says -bbigtoc makes program run slower. aix43gcc+krb5+krb4+openssl: @echo Making C-Kermit $(CKVER) for IBM AIX 4.3 or higher w/Kerberos... $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} CC=gcc CC2=gcc \ "CFLAGS= -mminimal-toc -g -O -DAIXRS -DAIX41 -DAIX43 -DSVR4 \ -DDIRENT -DCK_ANSIC -DCLSOPN -DCK_CURSES -DSELECT -DSELECT_H \ -DSTERMIOX -DTCPSOCKET -DFNFLOAT -DNOGETUSERSHELL \ -DCK_AUTHENTICATION -DCK_KERBEROS -DKRB5 -DKRB4 -DKRB524 \ -DCK_ENCRYPTION -DCK_DES -DCK_CAST -DLIBDES -DCK_SSL \ -funsigned-char $(K5INC) $(K5INC)/krb5 $(SSLINC) $(KFLAGS)" \ "LIBS=$(K5LIB) $(SSLLIB) -lssl -lcrypto \ -lcurses -lm -lkrb4 -ldes425 -lkrb5 -lcom_err -lk5crypto -lcrypt \ -lgssapi_krb5" aix43gcc+openssl: @echo Making C-Kermit $(CKVER) for IBM AIX 4.3 or higher w/OpenSSL... $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} CC=gcc CC2=gcc \ "CFLAGS= -mminimal-toc -g -O -DAIXRS -DAIX41 -DAIX43 -DSVR4 \ -DDIRENT -DCK_ANSIC -DCLSOPN -DCK_CURSES -DSELECT -DSELECT_H \ -DSTERMIOX -DTCPSOCKET -DFNFLOAT -DNOGETUSERSHELL \ -DCK_AUTHENTICATION -DCK_SSL -funsigned-char $(SSLINC) $(KFLAGS)" \ "LIBS=$(SSLLIB) -lssl -lcrypto -lcurses -lm -lcrypt" aix44gcc: $(MAKE) aix43g "KFLAGS=-DAIX44 $(KFLAGS)" \ KTARGET=$${KTARGET:-$(@)} aix45gcc: $(MAKE) aix43g "KFLAGS=-DAIX45 $(KFLAGS)" \ KTARGET=$${KTARGET:-$(@)} aix50gcc: $(MAKE) aix43g "KFLAGS=-DAIX50 $(KFLAGS)" \ KTARGET=$${KTARGET:-$(@)} aix51gcc: $(MAKE) aix43g "KFLAGS=-DAIX51 $(KFLAGS)" \ KTARGET=$${KTARGET:-$(@)} aix52gcc: $(MAKE) aix43g "KFLAGS=-DAIX52 $(KFLAGS)" \ KTARGET=$${KTARGET:-$(@)} aix53gcc: $(MAKE) aix43g "KFLAGS=-DAIX53 $(KFLAGS)" \ KTARGET=$${KTARGET:-$(@)} #Bull DPX/2 with BOS/X, like AIX/RS6000 bulldpx2: @echo Making C-Kermit $(CKVER) for Bull DPX/2 with BOS/X... $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DSVR3 -DDIRENT -DCK_ANSIC -DCKTYP_H= \ -DCK_POLL -DNOGETUSERSHELL -DCLSOPN -DNOLEARN -O $(KFLAGS)" \ "LNKFLAGS = -s" #Sun UNIX 3.5 with gcc 2.3.3. sunos3gcc: @echo Making C-Kermit $(CKVER) for Sun UNIX 3.5 and gcc... $(MAKE) xermit CC=gcc CC2=gcc KTARGET=$${KTARGET:-$(@)} \ CFLAGS="-g -O -DBSD4 -DTCPSOCKET $(KFLAGS)" #SunOS version 4.0, BSD environment, has saved original euid feature. # Add "CC=/usr/ucb/cc CC2=/usr/ucb/cc" if necessary. # Note: Including Unicode crashes the assembler in ckcuni.c. sunos4: @echo Making C-Kermit $(CKVER) for SunOS 4.0, BSD environment... $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -O -DSUNOS4 -DFNFLOAT -DNOUNICODE $(KFLAGS)" \ "LIBS=-lm" #As above, but with SunLink X.25 support sunos4x25: @echo SunLink X.25 support $(MAKE) "MAKE=$(MAKE)" sunos4 KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=$(KFLAGS) -DFNFLOAT -DSUNX25" \ "LIBS=-lm" #SUN OS version 4.1 - 4.1.3, BSD environment, has saved original euid feature. #Uses Honey DanBer UUCP. Requires presence of /usr/spool/locks directory. # /var/spool/ should be a symbolic link to /usr/spool/. # ... or 'make xermit "CC= /usr/ucb/cc " \' # Note: "xermit" means use the select() version of the CONNECT module. # Note for C-Kermit 9.0: Reportedly 'you need to modify the sys/ioctl.h # include file, i.e. comment out the "struct winsize" and "struct # ttysize". Otherwise there will be a conflict with sys/ttycom.h (included by # termios.h) which also declares these structs. But you need both includes.' sunos41: @echo Making C-Kermit $(CKVER) for SunOS 4.1 / BSD... $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -O -DSUNOS41 -DHDBUUCP -DNOUNICODE $(KFLAGS)" \ "LIBS= $(LIBS) -lresolv -lm" #As above, but compiled with gcc. Gives 24-32K size reduction #with gcc 2.1 or 2.2.2. CAUTION: make sure "fixincludes" has been run on #the include files, so gcc's are in sync with the regular Sun ones. #This includes the curses library for fullscreen file transfer display. #NDGPWNAM needed for GCC 2.5.6, not needed for 2.4.0, but it's uncertain #whether it will do any harm for 2.4.0 compilation -- if so, remove it. sunos41gcc: @echo Making C-Kermit $(CKVER) for SunOS 4.1 with gcc and curses... $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} "CC= gcc" "CC2= gcc" \ "CFLAGS= -O -DSUNOS41 -DHDBUUCP -DNDGPWNAM -DCK_CURSES -DFNFLOAT \ -funsigned-char $(KFLAGS)" "LIBS= -lcurses -ltermcap -lresolv -lm" # As above, but without -funsigned-char so I can see the warnings that # everybody else will get when they use ANSI compilers that don't have this # option (gsc = gcc signed char). sunos41gsc: @echo Making C-Kermit $(CKVER) for SunOS 4.1 with gcc and curses... $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} "CC= gcc" "CC2= gcc" \ "CFLAGS= -O -DSUNOS41 -DHDBUUCP -DNDGPWNAM -DCK_CURSES -DFNFLOAT \ $(KFLAGS)" "LIBS= -lcurses -ltermcap -lresolv -lm" #As above but with ckucon.c rather than ckucns.c (for testing only) sunos41gccfork: @echo Making C-Kermit $(CKVER) for SunOS 4.1 with gcc and curses... $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} "CC= gcc" "CC2= gcc" \ "CFLAGS= -O -DSUNOS41 -DHDBUUCP -DNDGPWNAM -DCK_CURSES -DFNFLOAT \ -DNOLEARN -funsigned-char $(KFLAGS)" \ "LIBS= -lcurses -ltermcap -lresolv -lm" #as above but configured for Kerberos IV sunos41gcc+krb4: @echo Making C-Kermit $(CKVER) for SunOS 4.1, gcc, curses, krb4... $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} "CC= gcc" "CC2= gcc" \ "CFLAGS= -O -DSUNOS41 -DHDBUUCP -DNDGPWNAM -DCK_CURSES -DFNFLOAT \ -DTCPSOCKET -DCK_AUTHENTICATION -DCK_KERBEROS -DKRB4 \ -DCK_ENCRYPTION -DCK_DES -DCK_CAST -DBIGBUFOK -funsigned-char \ $(K4INC) $(KFLAGS)" \ "LIBS= $(K4LIB) -lcurses -ltermcap -lresolv -lm -lkrb -ldes" #as above but configured for SSL/TLS sunos41gcc+openssl: @echo Making C-Kermit $(CKVER) for SunOS 4.1, gcc, curses, ssl... $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} "CC= gcc" "CC2= gcc" \ "CFLAGS= -O -DSUNOS41 -DHDBUUCP -DNDGPWNAM -DCK_CURSES -DFNFLOAT \ -DCK_AUTHENTICATION -funsigned-char \ -DCK_SSL -DTCPSOCKET -DBIGBUFOK $(SSLINC) $(KFLAGS)" \ "LIBS= $(SSLLIB) -lcurses -ltermcap -lresolv -lm -lssl -lcrypto" #as above but configured for Kerberos IV and SSL/TLS sunos41gcc+krb4+openssl: @echo Making C-Kermit $(CKVER) for SunOS 4.1, gcc, curses, krb4... $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} "CC= gcc" "CC2= gcc" \ "CFLAGS= -O -DSUNOS41 -DHDBUUCP -DNDGPWNAM -DCK_CURSES -DFNFLOAT \ -DCK_AUTHENTICATION -DCK_KERBEROS -DKRB4 -DCK_ENCRYPTION -DCK_DES \ -DCK_CAST -DCK_SSL -DLIBDES -DTCPSOCKET -DBIGBUFOK -funsigned-char \ $(K4INC) $(SSLINC) $(KFLAGS)" \ "LIBS= $(K4LIB) $(SSLLIB) \ -lcurses -ltermcap -lresolv -lm -lkrb -lssl -lcrypto" #as above but configured for Kerberos IV and ZLIB enabled SSL/TLS sunos41gcc+krb4+openssl+zlib: @echo Making C-Kermit $(CKVER) for SunOS 4.1, gcc, curses, krb4... $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} "CC= gcc" "CC2= gcc" \ "CFLAGS= -O -DSUNOS41 -DHDBUUCP -DNDGPWNAM -DCK_CURSES -DFNFLOAT \ -DCK_AUTHENTICATION -DCK_KERBEROS -DKRB4 -DCK_ENCRYPTION -DCK_DES \ -DCK_CAST -DCK_SSL -DLIBDES -DTCPSOCKET -DBIGBUFOK -funsigned-char \ -DZLIB $(K4INC) $(SSLINC) \ $(KFLAGS)" \ "LIBS= $(K4LIB) $(SSLLIB) \ -lcurses -ltermcap -lresolv -lm -lkrb -lssl -lcrypto -lz" #as above but configured for Kerberos IV and SRP and ZLIB enabled SSL/TLS sunos41gcc+krb4+srp+openssl+zlib: @echo "C-Kermit $(CKVER) SunOS 4.1: gcc,curses,krb4,srp,ssl,zlib..." $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} "CC= gcc" "CC2= gcc" \ "CFLAGS= -O -DSUNOS41 -DHDBUUCP -DNDGPWNAM -DCK_CURSES -DFNFLOAT \ -DCK_AUTHENTICATION -DCK_KERBEROS -DKRB4 -DCK_ENCRYPTION -DCK_DES \ -DCK_CAST -DCK_SSL -DLIBDES -DTCPSOCKET -DBIGBUFOK -funsigned-char \ -DZLIB -DCK_SRP $(K4INC) $(SRPINC) $(SSLINC) $(KFLAGS)" \ "LIBS= $(K4LIB) $(SRPLIB) $(SSLLIB) \ -lcurses -ltermcap -lresolv -lm -lkrb -lkrypto \ -lsrp -lssl -lcrypto -lz" #as above but configured for Kerberos IV and SRP and ZLIB enabled SSL/TLS sunos41gcc+srp+openssl+zlib: @echo "C-Kermit $(CKVER) SunOS 4.1: gcc,curses,srp,ssl,zlib..." $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} "CC= gcc" "CC2= gcc" \ "CFLAGS= -O -DSUNOS41 -DHDBUUCP -DNDGPWNAM -DCK_CURSES -DFNFLOAT \ -DCK_AUTHENTICATION -DCK_ENCRYPTION -DCK_DES \ -DCK_CAST -DCK_SSL -DLIBDES -DTCPSOCKET -DBIGBUFOK -funsigned-char \ -DZLIB -DCK_SRP $(SRPINC) $(SSLINC) \ $(KFLAGS)" \ "LIBS= $(SRPLIB) $(SSLLIB) \ -lcurses -ltermcap -lresolv -lm -lkrypto -lsrp -lssl -lcrypto -lz " #SUNOS 4.1 as sunos41 above, but also with curses support sunos41c: @echo Curses support $(MAKE) "MAKE=$(MAKE)" sunos41 KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=$(KFLAGS) -DCK_CURSES -DFNFLOAT " \ "LIBS= -lcurses -ltermcap" #As SunOS 4.1.x, gcc, configured as Internet Kermit Server. # . NOLOCAL removes capability to make connections # . TNCODE allows server-side Telnet negotiation. # . used to include -lpwent, why? # . used to include -L/usr/local/lib -lm, why? sunos41giks: @echo Making C-Kermit $(CKVER) for SunOS 4.1 with gcc for IKS... $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} "CC= gcc" "CC2= gcc" \ "CFLAGS= -O -DSUNOS41 -DNDGPWNAM -DFNFLOAT \ -DNOLOCAL -DTCPSOCKET -DTNCODE -DNOPUSH $(KFLAGS)" \ "LIBS= -lm -lresolv" #SUNOS 4.1 with SunLink X.25 support sunos41x25: @echo SunLink X.25 support $(MAKE) "MAKE=$(MAKE)" wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -O -DSUNOS41 -DHDBUUCP -DNOUNICODE -DFNFLOAT -DSUNX25 \ -DNOLEARN $(KFLAGS)" "LIBS= $(LIBS) -lresolv -lm" #SUNOS 4.1 with SunLink X.25 support and curses sunos41x25c: @echo SunLink X.25 support + curses $(MAKE) "MAKE=$(MAKE)" wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -O -DSUNOS41 -DHDBUUCP -DNOUNICODE -DFNFLOAT -DSUNX25 \ -DCK_CURSES -DNOLEARN $(KFLAGS)" \ "LIBS= $(LIBS) -lcurses -ltermcap -lresolv -lm" #SUN with Solaris 2.0 = SunOS 5.0. #Mostly the same as System V R4. Don't use this with later Solaris versions. solaris20: @echo 'Making C-Kermit $(CKVER) for Sun with Solaris 2.0 and curses...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -O -DSVR4 -DSOLARIS -DDIRENT -DHDBUUCP -DSTERMIOX \ -DTCPSOCKET -DCK_CURSES -DFNFLOAT -DCK_POLL $(KFLAGS)" \ "LIBS= -lsocket -lnsl -lcurses -ltermlib -lm" "LNKFLAGS = -s" #SUN with Solaris 2.0. #As above, but built with the gcc compiler from the Cygnus CD-ROM. solaris20g: @echo 'Making C-Kermit $(CKVER) for Sun Solaris 2.0, gcc, and curses..' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -O -DSVR4 -DSOLARIS -DDIRENT -DHDBUUCP -DSTERMIOX \ -DTCPSOCKET -DCK_CURSES -DCK_POLL -DFNFLOAT $(KFLAGS)" \ "LIBS= -lsocket -lnsl -lcurses -ltermlib -lm" "LNKFLAGS = -s" \ CC=/opt/cygnus-sol2-1.1/bin/gcc CC2=/opt/cygnus-sol2-1.1/bin/gcc #SunOS 5.1 = Solaris 2.1. #NOTE: A C compiler is no longer bundled with SunOS 5.1, so to compile C #programs, you might have to change your PATH to include the directory #/usr/ccs/bin AFTER the directory containing the compiler. SunPRO C is #installed by default in /opt/SUNWspro/bin. So a sample PATH might be: # # /usr/local/bin:/usr/bin:/opt/SUNWspro/bin:/usr/ccs/bin:\ # /usr/ucb:/usr/sbin:/sbin:. # # or: # # /usr/openwin/bin:/export/home/SUNWspro/bin:/usr/ccs/bin:/usr/sbin:/usr/bin. # #NOTE 2: Compilation with the Apogee C compiler (apcc) might not work, #because it refuses to allow "-Usun". Reportedly, newer releases of apcc #(such as 1.2.17) work OK, use: "make -e sunos51 CC=apcc CC2=apcc". solaris21: @echo 'Making C-Kermit $(CKVER) for SunOS 5.x....' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -O -Usun -DSVR4 -DSOLARIS -DDIRENT -DHDBUUCP -DFNFLOAT \ -DSELECT -DNODEBUG -DSTERMIOX $(KFLAGS)" "LIBS = -lm" "LNKFLAGS = -s" #Solaris 2.0 - 2.4, SunPro compiler, includes curses and TCP/IP. #When using SUNWspro CC 2.0.1 under Solaris 2.3, be sure all cc patches #are applied, otherwise corrupt or truncated object files can result. #To build, set your PATH as follows: # /usr/local/bin:/usr/bin:/opt/SUNWspro/bin:/usr/ccs/bin:\ # /usr/ucb:/usr/sbin:/sbin:. # or (depending on where the compiler has been installed): # /usr/openwin/bin:/export/home/SUNWspro/bin:/usr/ccs/bin:/usr/sbin:/usr/bin. #For additional optimization try using "-fast -xO4 -xdepend". solaris2x: @echo 'Making C-Kermit $(CKVER) for Solaris 2.x with SunPro cc...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -O -Usun -i -DSVR4 -DDIRENT -DSOLARIS -DHDBUUCP -DFNFLOAT \ -DSELECT -DCK_CURSES -DCK_NEWTERM -DSTERMIOX -DTCPSOCKET $(KFLAGS)" \ "LNKFLAGS = -s" "LIBS= -ltermlib -lsocket -lnsl -lm -lresolv" #as above but configured for Kerberos IV solaris2x+krb4: @echo 'Making C-Kermit $(CKVER) for Solaris 2.x, SunPro cc, krb4...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -O -Usun -i -DSVR4 -DDIRENT -DSOLARIS -DHDBUUCP -DFNFLOAT \ -DSELECT -DCK_CURSES -DCK_NEWTERM -DSTERMIOX -DTCPSOCKET \ -DCK_AUTHENTICATION -DCK_KERBEROS -DKRB4 \ -DCK_ENCRYPTION -DCK_DES -DCK_CAST $(K4INC) $(KFLAGS)" \ "LNKFLAGS = -s" \ "LIBS= $(K4LIB) -ltermlib -lsocket -lnsl -lm -lresolv -lkrb -ldes" #C-Kermit for Solaris 2.0-2.4 compiled with gcc, includes curses and TCP/IP. #Change -O2 to -O if -O2 gives trouble. #Remove -Usun if it causes trouble. #Your PATH should start with something like: # /usr/local/gnu/bin:/usr/ccs/bin: #Produces a huge executable -- strip with /usr/ccs/bin/strip (not Gnu strip). #Also don't add "LNKFLAGS = -s" -- strip manually instead. #Also note: this can NOT be linked statically - Sun makes it impossible. #And for Solaris 2.4, you might have to replace: # /usr/local/lib/gcc-lib/i486-sun-solaris2/2.4.5/include/sys/stat.h #with /usr/include/sys/stat.h. solaris2xg: @echo 'Making C-Kermit $(CKVER) for Solaris 2.x with GNU cc...' @echo 'Please read the comments that accompany the solaris2xg target.' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} CC=gcc CC2=gcc \ "CFLAGS = -g -O -Usun -DSVR4 -DSOLARIS -DSTERMIOX -DSELECT -DFNFLOAT \ -DCK_CURSES -DCK_NEWTERM -DDIRENT -DHDBUUCP -DTCPSOCKET $(KFLAGS)" \ "LIBS= -ltermlib -lsocket -lnsl -lm -lresolv $(LIBS)" #ditto but no curses. solaris2xgnc: @echo 'Making C-Kermit $(CKVER) for Solaris 2.x with GNU cc...' @echo 'Please read the comments that accompany the solaris2xg target.' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} CC=gcc CC2=gcc \ "CFLAGS = -g -O -Usun -DSVR4 -DSOLARIS -DSTERMIOX -DSELECT -DFNFLOAT \ -DDIRENT -DHDBUUCP -DTCPSOCKET $(KFLAGS)" \ "LIBS= -lsocket -lnsl -lm -lresolv $(LIBS)" #and with Kerberos IV solaris2xg+krb4: @echo 'Making C-Kermit $(CKVER) for Solaris 2.x with GNU cc, krb4...' @echo 'Please read the comments that accompany the solaris2xg target.' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} CC=gcc CC2=gcc \ "CFLAGS = -g -O -Usun -DSVR4 -DSOLARIS -DSTERMIOX -DSELECT -DFNFLOAT \ -DCK_CURSES -DCK_NEWTERM -DDIRENT -DHDBUUCP -DTCPSOCKET \ -DCK_AUTHENTICATION -DCK_KERBEROS -DKRB4 -DCK_ENCRYPTION \ -DCK_DES -DCK_CAST -DBIGBUFOK $(K4INC) $(KFLAGS)" \ "LIBS= $(K4LIB) -ltermlib -lsocket -lnsl -lm -lresolv -lkrb -ldes \ $(LIBS)" #and with OpenSSL,ZLIB,PAM,SHADOW solaris2xg+openssl+zlib+pam+shadow: @echo 'Making C-Kermit $(CKVER) for Solaris 2.x with gcc, OpenSSL...' @echo 'Please read the comments that accompany the solaris2xg target.' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} CC=gcc CC2=gcc \ "CFLAGS = -g -O -Usun -DSVR4 -DSOLARIS -DSTERMIOX -DSELECT -DFNFLOAT \ -DCK_CURSES -DCK_NEWTERM -DDIRENT -DHDBUUCP -DTCPSOCKET \ -DCK_AUTHENTICATION -DCK_SSL -DCK_PAM -DCK_SHADOW -DZLIB \ -DBIGBUFOK $(SSLINC) $(KFLAGS)" \ "LIBS= $(SSLLIB) -ltermlib \ -lsocket -lnsl -lm -lresolv -lssl -lcrypto -lpam -lz" #Ditto but with GCC 3.1 in which you have to specify 32-bit with -m32. #In Solaris 9 (and maybe 8) you'll also need specifiy the Library path. #Reportedly this can be done here, but only with: # crle -l /usr/lib:/usr/local/ssl/lib #prior to building. Note: 64-bit not tested with SSL. #For no-crypto 64-bit builds see the solaris9g64 target. solaris2xg32+openssl+zlib+pam+shadow: @echo 'Making C-Kermit $(CKVER) for Solaris 2.x with gcc, OpenSSL...' @echo 'Please read the comments that accompany the solaris2xg target.' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} CC="gcc -m32" CC2="gcc -m32" \ "CFLAGS = -g -O -Usun -DSVR4 -DSOLARIS -DSTERMIOX -DSELECT -DFNFLOAT \ -DCK_CURSES -DCK_NEWTERM -DDIRENT -DHDBUUCP -DTCPSOCKET \ -DCK_AUTHENTICATION -DCK_SSL -DCK_PAM -DCK_SHADOW -DZLIB \ -DBIGBUFOK $(SSLINC) $(KFLAGS)" \ "LIBS= $(SSLLIB) -ltermlib \ -lsocket -lnsl -lm -lresolv -lssl -lcrypto -lpam -lz" #and with Krb5,Krb4,OpenSSL,SHADOW solaris2xg+krb5+krb4+openssl+shadow: @echo 'Making C-Kermit $(CKVER) for Solaris 2.x with gcc,k5,k4,ssl...' @echo 'Please read the comments that accompany the solaris2xg target.' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} CC=gcc CC2=gcc \ "CFLAGS = -O -Usun -DSVR4 -DSOLARIS -DSTERMIOX -DSELECT -DFNFLOAT \ -DCK_CURSES -DCK_NEWTERM -DDIRENT -DHDBUUCP -DTCPSOCKET \ -DCK_AUTHENTICATION -DCK_KERBEROS -DKRB5 -DKRB4 -DKRB524 \ -DCK_ENCRYPTION -DCK_SSL -DCK_DES -DCK_CAST -DBIGBUFOK \ $(K5INC) $(K5INC)/krb5 $(SSLINC) $(KFLAGS)" \ "LIBS= $(K5LIB) $(SSLLIB) -ltermlib -lsocket -lnsl -lm -lresolv \ -lkrb4 -lssl -lcrypto -lgssapi_krb5 -lkrb5 -lcom_err -lk5crypto \ -ldes $(LIBS)" #and with OpenSSL solaris2xg+openssl+pam+shadow: @echo 'Making C-Kermit $(CKVER) for Solaris 2.x with gcc, OpenSSL...' @echo 'Please read the comments that accompany the solaris2xg target.' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} CC=gcc CC2=gcc \ "CFLAGS = -g -O -Usun -DSVR4 -DSOLARIS -DSTERMIOX -DSELECT -DFNFLOAT \ -DCK_CURSES -DCK_NEWTERM -DDIRENT -DHDBUUCP -DTCPSOCKET \ -DCK_AUTHENTICATION -DCK_SSL -DCK_PAM -DCK_SHADOW \ -DBIGBUFOK $(SSLINC) $(KFLAGS)" \ "LIBS= $(SSLLIB) -ltermlib \ -lsocket -lnsl -lm -lresolv -lssl -lcrypto -lpam" solaris2xg+openssl+zlib+srp+pam+shadow: @echo 'Making C-Kermit $(CKVER) for Solaris 2.x with gcc, OpenSSL...' @echo 'Please read the comments that accompany the solaris2xg target.' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} CC=gcc CC2=gcc \ "CFLAGS = -g -O -Usun -DSVR4 -DSOLARIS -DSTERMIOX -DSELECT -DFNFLOAT \ -DCK_CURSES -DCK_NEWTERM -DDIRENT -DHDBUUCP -DTCPSOCKET -DBIGBUFOK \ -DCK_AUTHENTICATION -DCK_ENCRYPTION -DCK_DES -DLIBDES -DCK_CAST \ -DCK_SSL -DCK_PAM -DCK_SHADOW -DZLIB -DCK_SRP $(SSLINC) $(KFLAGS)" \ "LIBS= $(SSLLIB) -ltermlib -lsocket -lnsl -lm -lresolv -lsrp -lssl \ -ldes -lkrypto -lcrypto -lpam -lz" solaris22g: $(MAKE) "MAKE=$(MAKE)" "KFLAGS=-DPOSIX_CRTSCTS $(KFLAGS)" solaris2xg \ KTARGET=$${KTARGET:-$(@)} solaris23g: $(MAKE) "MAKE=$(MAKE)" "KFLAGS=-DPOSIX_CRTSCTS $(KFLAGS)" solaris2xg \ KTARGET=$${KTARGET:-$(@)} #Solaris 2.4 built with gcc solaris24g: $(MAKE) "MAKE=$(MAKE)" KTARGET=$${KTARGET:-$(@)} \ solaris2xg "KFLAGS=-DSOLARIS24 -DPOSIX_CRTSCTS $(KFLAGS)" #Solaris 2.0-2.3, SunPro compiler, with SunLink X.25 support. #This will only run if user has /opt/SUNWconn/lib/libsockx25.so.1 #exists and can be dynamically linked. #NOTE: Do not change target to xermit -- it doesn't support X.25. solaris2x25: @echo 'Making C-Kermit $(CKVER) for Solaris 2.x+X.25 with SunPro cc...' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -O -i -Usun -DSVR4 -DSOLARIS -DDIRENT \ -DSUNX25 -DTCPSOCKET -DHDBUUCP -DFNFLOAT -DNOLEARN \ -DSELECT -DCK_CURSES -DCK_NEWTERM -DSTERMIOX $(KFLAGS)" \ "LNKFLAGS = -s" \ "LIBS= -ltermlib -L/opt/SUNWconn/lib -R/opt/SUNWconn/lib \ -lsockx25 -lsocket -lnsl -lm -lresolv" #Solaris 2.0-2.4, gcc, SunLink X.25 added. #NOTE: Can't use xermit target with X.25. solaris2xgx25: @echo 'Making C-Kermit $(CKVER) for Solaris 2.x + X.25 with GNU cc...' @echo 'Please read the comments that accompany the solaris2xg entry.' $(MAKE) wermit CC=gcc CC2=gcc KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -g -O -Usun -DSVR4 -DSOLARIS -DSTERMIOX -DSELECT -DSUNX25 \ -DCK_CURSES -DCK_NEWTERM -DDIRENT -DHDBUUCP -DTCPSOCKET -DFNFLOAT \ -DNOLEARN $(KFLAGS)" \ "LIBS= -ltermlib -lm -L/opt/SUNWconn/lib -R/opt/SUNWconn/lib \ -lsockx25 -lsocket -lnsl" #Solaris 2.4, SunPro compiler, with SunLink X.25 support. #This will only run if user has /opt/SUNWconn/lib/libsockx25.so.1 #exists and can be dynamically linked. solaris24x25: @echo 'Making C-Kermit $(CKVER) for Solaris 2.4+X.25 with SunPro cc...' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -O -i -Usun -DSVR4 -DSOLARIS -DSOLARIS24 -DDIRENT -DNOLEARN \ -DSUNX25 -DTCPSOCKET -DHDBUUCP -DFNFLOAT -DPOSIX_CRTSCTS \ -DSELECT -DCK_CURSES -DCK_NEWTERM -DSTERMIOX $(KFLAGS)" \ "LNKFLAGS = -s" \ "LIBS= -ltermlib -L/opt/SUNWconn/lib -R/opt/SUNWconn/lib \ -lsockx25 -lsocket -lnsl -lm -lresolv" #Solaris 2.5, SunPro compiler, with SunLink X.25 support. solaris25x25: @echo 'Making C-Kermit $(CKVER) for Solaris 2.5+X.25 with SunPro cc...' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -O -i -Usun -DSVR4 -DSOLARIS25 -DDIRENT -DSUNX25 \ -DTCPSOCKET -DHDBUUCP -DSELECT -DCK_CURSES \ -DCK_NEWTERM -DSTERMIOX -DFNFLOAT -DPOSIX_CRTSCTS -DNOLEARN \ -I/opt/SUNWconn/include $(KFLAGS)" \ "LIBS= -ltermlib -L/opt/SUNWconn/lib -R/opt/SUNWconn/lib \ -lsockx25 -lsocket -lnsl -lm -lresolv" solaris23: $(MAKE) "MAKE=$(MAKE)" solaris2x KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=$(KFLAGS)" solaris24: $(MAKE) "MAKE=$(MAKE)" solaris2x KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-DSOLARIS24 -DPOSIX_CRTSCTS $(KFLAGS)" # template for Solaris 2.5 and above. solaris25x: @echo 'Making C-Kermit $(CKVER) for Solaris 2.x with SunPro cc...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DFNFLOAT -O -Usun -i $(KFLAGS)" \ "LNKFLAGS = -s" \ "LIBS= -ltermlib -lsocket -lnsl -lm -lresolv $(LIBS)" #Solaris 2.5, SunPro compiler, curses, TCP/IP solaris25: $(MAKE) "MAKE=$(MAKE)" solaris25x KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-DSOLARIS25 $(KFLAGS)" #Solaris 2.5, SunPro compiler, curses, TCP/IP, Kerberos IV solaris25+krb4: $(MAKE) "MAKE=$(MAKE)" solaris25x+krb4 KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-DSOLARIS25 $(KFLAGS)" #Solaris 2.5 built with gcc solaris25g: $(MAKE) "MAKE=$(MAKE)" solaris2xg KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-funsigned-char -DSOLARIS25 $(KFLAGS)" #Solaris 2.5 built with gcc and Kerberos IV solaris25g+krb4: $(MAKE) "MAKE=$(MAKE)" solaris2xg+krb4 KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-funsigned-char -DSOLARIS25 $(KFLAGS)" #Solaris 2.5 built with gcc and Kerberos V/IV, SSL, ... solaris25g+krb5+krb4+openssl+shadow: $(MAKE) "MAKE=$(MAKE)" solaris2xg+krb5+krb4+openssl+shadow \ KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-funsigned-char -DSOLARIS25 $(KFLAGS)" #Solaris 2.5, gcc, SunLink X.25 added. solaris25gx25: $(MAKE) "MAKE=$(MAKE)" KTARGET=$${KTARGET:-$(@)} solaris2xgx25 \ "KFLAGS=-DSOLARIS25 $(KFLAGS)" #Solaris 2.6, gcc, SunLink X.25 added. solaris26gx25: $(MAKE) "MAKE=$(MAKE)" KTARGET=$${KTARGET:-$(@)} solaris2xgx25 \ "KFLAGS=-DSOLARIS26 -DCK_PAM -DCK_SHADOW $(KFLAGS)" \ "LIBS= -lpam" #Solaris 2.6, SunPro compiler, curses, TCP/IP solaris26: $(MAKE) "MAKE=$(MAKE)" solaris25x KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-DSOLARIS26 -DCK_PAM -DCK_SHADOW $(KFLAGS)" \ "LIBS= -lpam" #Solaris 2.6 with gcc solaris26g: $(MAKE) "MAKE=$(MAKE)" KTARGET=$${KTARGET:-$(@)} solaris2xg \ "KFLAGS= -DSOLARIS26 -DCK_PAM -DCK_SHADOW $(KFLAGS)" \ "LIBS = -lpam" #Solaris 2.6 with gcc and SSL solaris26g+openssl: $(MAKE) "MAKE=$(MAKE)" solaris2xg+openssl+pam+shadow \ KTARGET=$${KTARGET:-$(@)} "KFLAGS= -DSOLARIS26 $(KFLAGS)" #Solaris 2.6 with gcc, no curses (e.g. because libtermlib is missing). solaris26gnc: $(MAKE) "MAKE=$(MAKE)" KTARGET=$${KTARGET:-$(@)} solaris2xgnc \ "KFLAGS= -DSOLARIS26 -DCK_PAM -DCK_SHADOW $(KFLAGS)" \ "LIBS= -lpam" #Solaris 2.6, SunPro compiler, with SunLink X.25 support. solaris26x25: @echo 'Making C-Kermit $(CKVER) for Solaris 2.6+X.25 with SunPro cc...' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -O -i -Usun -DSVR4 -DSOLARIS26 -DDIRENT -DSUNX25 \ -DTCPSOCKET -DHDBUUCP -DSELECT -DCK_CURSES -DCK_PAM -DCK_SHADOW \ -DCK_NEWTERM -DSTERMIOX -DFNFLOAT -DPOSIX_CRTSCTS -DNOLEARN \ -I/opt/SUNWconn/include $(KFLAGS)" \ "LIBS= -ltermlib -L/opt/SUNWconn/lib -R/opt/SUNWconn/lib \ -lsockx25 -lsocket -lnsl -lm -lresolv -lpam" #Solaris 7 (2.7) with Sun CC solaris7: $(MAKE) "MAKE=$(MAKE)" solaris25x KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-DSOLARIS7 -DCK_PAM -DCK_SHADOW $(KFLAGS)" \ "LIBS= -lpam" #Solaris 7 with gcc (32-bit) solaris7g: $(MAKE) "MAKE=$(MAKE)" solaris2xg KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-DSOLARIS7 -DCK_PAM -DCK_SHADOW $(KFLAGS)" \ "LIBS= -lpam" #Solaris 7 with gcc + Kerberos IV (32-bit) solaris7g+krb4: $(MAKE) "MAKE=$(MAKE)" solaris2xg+krb4 KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-DSOLARIS7 -DCK_PAM -DCK_SHADOW $(KFLAGS)" \ "LIBS= -lpam" solaris7g+openssl+zlib+pam+shadow: $(MAKE) "MAKE=$(MAKE)" solaris2xg+openssl+zlib+pam+shadow \ KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-DSOLARIS7 -DCK_PAM -DCK_SHADOW $(KFLAGS)" #Solaris 7 with gcc + OpenSSL (32-bit) solaris7g+openssl+zlib+srp+pam+shadow: $(MAKE) "MAKE=$(MAKE)" solaris2xg+openssl+zlib+srp+pam+shadow \ KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-DSOLARIS7 -DCK_PAM -DCK_SHADOW $(KFLAGS)" #Solaris 8 solaris8: $(MAKE) "MAKE=$(MAKE)" solaris25x KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-DSOLARIS8 -DCK_PAM -DCK_SHADOW $(KFLAGS)" \ "LIBS= -lpam" #Solaris 8 with gcc (32-bit) solaris8g: $(MAKE) "MAKE=$(MAKE)" solaris2xg KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-DSOLARIS8 -DCK_PAM -DCK_SHADOW $(KFLAGS)" \ "LIBS= -lpam" # In OpenSSL builds add -ldl if you get unresolved references for # dlclose, dlsym, dlopen, dlerror. #Solaris 8 with gcc + OpenSSL (32-bit) solaris8g+openssl+zlib+pam+shadow: $(MAKE) "MAKE=$(MAKE)" solaris2xg+openssl+zlib+pam+shadow \ KTARGET=$${KTARGET:-$(@)} "KFLAGS=-DSOLARIS8 $(KFLAGS)" #Solaris 8 with gcc + Kerberos IV (32-bit) solaris8g+krb4: $(MAKE) "MAKE=$(MAKE)" solaris2xg+krb4 KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-DSOLARIS8 -DCK_PAM -DCK_SHADOW $(KFLAGS)" \ "LIBS= -lpam" solaris9nolfs: $(MAKE) "MAKE=$(MAKE)" solaris25x KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-DSOLARIS9 -DCK_PAM -DCK_SHADOW -DUSE_STRERROR $(KFLAGS)" \ "LIBS= -lpam" #Solaris 9 with malloc debugging solaris9md: $(MAKE) mermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DFNFLOAT -O -Usun -i -D_FILE_OFFSET_BITS=64 \ -DSOLARIS9 -Dmalloc=dmalloc -Dfree=dfree -DMDEBUG \ -DCK_PAM -DCK_SHADOW -DUSE_STRERROR $(KFLAGS)" \ "LIBS= -lpam -ltermlib -lsocket -lnsl -lm -lresolv" #Solaris 9 with gcc + OpenSSL + Shadow (32-bit) #Add -DOPENSSL_097 for OpenSSL 0.9.7 or later. solaris9g+openssl+shadow+pam+zlib: $(MAKE) "MAKE=$(MAKE)" solaris2xg+openssl+zlib+pam+shadow \ KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-DSOLARIS9 -DHDBUUCP -DDIRENT -D_FILE_OFFSET_BITS=64 \ -DNO_DCL_INET_ATON -DZLIB -DCK_PAM -DCK_SHADOW -DLIBDES $(KFLAGS)" \ "LIBS= -lpam -ldes425 -lz $(LIBS)" #Solaris 9 with gcc + OpenSSL + Kerberos 5 + Krb4 + Shadow (32-bit) #Add -DOPENSSL_097 for OpenSSL 0.9.7 or later. solaris9g+krb5+krb4+openssl+shadow+pam+zlib: $(MAKE) "MAKE=$(MAKE)" solaris2xg+krb5+krb4+openssl+shadow \ KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-DSOLARIS9 -DHDBUUCP -DDIRENT -D_FILE_OFFSET_BITS=64 \ -DNO_DCL_INET_ATON -DZLIB -DCK_PAM -DCK_SHADOW -DLIBDES $(KFLAGS)" \ "LIBS= -lpam -ldes -lz $(LIBS)" #Solaris 9 with gcc + Kerberos 4 and 5: solaris9g+krb5+krb4: $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} CC=gcc CC2=gcc \ "CFLAGS = -O -Usun -DSVR4 -DSOLARIS9 -DSTERMIOX -DSELECT -DFNFLOAT \ -DCK_CURSES -DCK_NEWTERM -DDIRENT -DHDBUUCP -DTCPSOCKET \ -DCK_AUTHENTICATION -DCK_KERBEROS -DKRB5 -DKRB4 -DKRB524 \ -D_FILE_OFFSET_BITS=64 \ -DCK_ENCRYPTION -DCK_DES -DCK_CAST -DBIGBUFOK \ $(K5INC) $(K5INC)/krb5 $(KFLAGS)" \ "LIBS= $(K5LIB) -ltermlib -lsocket -lnsl -lm -lresolv \ -lkrb4 -lcrypto -lgssapi_krb5 -lkrb5 -lcom_err -lk5crypto \ -ldes $(LIBS)" #Solaris 9, 10, or 11 with gcc... #Uses streams PTYs rather than BSD ptys as in C-Kermit 8.0 and earlier. #This target is chained to be the secure solaris9g+xxx targets below. solaris9g solaris10g solaris11g: @echo 'Making C-Kermit $(CKVER) for Solaris 9 or later with gcc' @case `uname -r` in \ 5.9) SOLARISVERSION="-DSOLARIS9" ;; \ 5.10) SOLARISVERSION="-DSOLARIS10" ;; \ 5.11) SOLARISVERSION="-DSOLARIS11" ;; \ *) SOLARISVERSION="-DSOLARIS" ;; \ esac ; \ $(MAKE) "MAKE=$(MAKE)" CC="gcc -m32" CC2="gcc -m32" xermit \ KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -g -O -Usun -DSVR4 $$SOLARISVERSION -DUSE_STRERROR \ -DSTERMIOX -DSELECT -DFNFLOAT -DCK_PAM -DCK_SHADOW -funsigned-char \ -DHAVE_STREAMS -DHAVE_GRANTPT -DHAVE_PTSNAME -DPUSH_PTEM \ -DPUSH_LDTERM -DPUSH_TTCOMPAT \ -DCK_CURSES -DCK_NEWTERM -DDIRENT -DHDBUUCP -DTCPSOCKET \ -D_FILE_OFFSET_BITS=64 $(KFLAGS)" \ "LIBS= -ltermlib -lsocket -lnsl -lm -lresolv -lpam $(LIBS)" #Solaris 9, 10, or 11 with gcc + Kerberos 5 + OpenSSL. #OK C-Kermit 9.0.301. solaris9g+krb5+ssl solaris10g+krb5+ssl solaris11g+krb5+ssl: @case `openssl version` in \ *0.9.7*) OPENSSLOPTION="-DOPENSSL_097" ;; \ *0.9.8*) OPENSSLOPTION="-DOPENSSL_098" ;; \ *1.[0-9].[0-9]*) OPENSSLOPTION="-DOPENSSL_100" ;; \ *) OPENSSLOPTION="" ;; \ esac ; \ HAVE_DES=''; \ DES_LIB=''; \ if ls $(SSLLIB)/libdes* > /dev/null 2> /dev/null; then \ DES_LIB='-ldes425'; \ HAVE_DES='-DCK_DES -DLIBDES'; \ echo "HAVE DES"; \ else echo "NO DES"; \ fi; \ GSSAPILIB=''; \ K5DIR=`echo $(K5LIB) | sed 's|-L||'`; \ echo K5DIR=$$K5DIR; \ if ls $$K5DIR/libgssapi_krb5* > /dev/null 2> /dev/null; then \ GSSAPILIB='-lgssapi_krb5'; \ else GSSAPILIB='-lgssapi'; \ fi; \ $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} CC=gcc CC2=gcc \ "CFLAGS = -O -Usun -DSVR4 -DSOLARIS9 -DSTERMIOX -DSELECT -DFNFLOAT \ -DCK_CURSES -DCK_NEWTERM -DDIRENT -DHDBUUCP -DTCPSOCKET -DBIGBUFOK \ -DCK_AUTHENTICATION -DCK_SSL -DZLIB -DCK_KERBEROS -DKRB5 \ -DCK_ENCRYPTION -DCK_CAST $$OPENSSLOPTION \ $$HAVE_DES $(SSLINC) $(K5INC) $(K5INC)/krb5 $(KFLAGS)" \ "LIBS= $(SSLLIB) $(K5LIB) -lz -lssl -ltermlib -lsocket -lnsl -lm \ -lresolv -lcrypto \ $$GSSAPILIB -lkrb5 -lcom_err -lk5crypto $$DES_LIB $(LIBS)" #Solaris 9, 10, or 11 with gcc, 64 bit build. #Peeking inside FILE struct not allowed in 64-bit world. #DON'T USE THIS ONE ON PC ARCHITECTURE - It compiles and links but won't run. #OK: 2009/09/25 (but not tested on Solaris 11) solaris9g64 solaris10g64 solaris11g64: @echo 'Making C-Kermit $(CKVER) for Solaris 9++ with gcc 64-bit' @case `uname -r` in \ 5.9) SOLARISVERSION="-DSOLARIS9" ;; \ 5.10) SOLARISVERSION="-DSOLARIS10" ;; \ 5.11) SOLARISVERSION="-DSOLARIS11" ;; \ *) SOLARISVERSION="-DSOLARIS" ;; \ esac ; \ $(MAKE) "MAKE=$(MAKE)" CC="gcc -m64" CC2="gcc -m64" xermit \ KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -g -O -Usun -funsigned-char \ -DSVR4 $$SOLARISVERSION -DNOARROWKEYS \ -DSTERMIOX -DSELECT -DFNFLOAT -DUSE_STRERROR -DCK_PAM -DCK_SHADOW \ -DHAVE_STREAMS -DHAVE_GRANTPT -DHAVE_PTSNAME -DPUSH_PTEM \ -DPUSH_LDTERM -DPUSH_TTCOMPAT \ -DCK_CURSES -DCK_NEWTERM -DDIRENT -DHDBUUCP -DTCPSOCKET $(KFLAGS)" \ "LIBS= -ltermlib -lsocket -lnsl -lm -lresolv -lpam $(LIBS)" #Solaris 9, 10, or 11 with SunPro CC #Uses streams PTYs rather than BSD ptys as in C-Kermit 8.0 and earlier. #This target is chained to by the secure targets below. #OK C-Kermit 9.0 solaris9 solaris10 solaris11: @echo 'Making C-Kermit $(CKVER) for Solaris 9 or later with Sun CC' @case `uname -r` in \ 5.9) SOLARISVERSION="-DSOLARIS9" ;; \ 5.10) SOLARISVERSION="-DSOLARIS10" ;; \ 5.11) SOLARISVERSION="-DSOLARIS11" ;; \ *) SOLARISVERSION="-DSOLARIS" ;; \ esac ; \ $(MAKE) "MAKE=$(MAKE)" xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -O -Usun -DSVR4 $$SOLARISVERSION -DUSE_STRERROR \ -DSTERMIOX -DSELECT -DFNFLOAT -DCK_PAM -DCK_SHADOW \ -DHAVE_STREAMS -DHAVE_GRANTPT -DHAVE_PTSNAME -DPUSH_PTEM \ -DPUSH_LDTERM -DPUSH_TTCOMPAT \ -DCK_CURSES -DCK_NEWTERM -DDIRENT -DHDBUUCP -DTCPSOCKET \ -D_FILE_OFFSET_BITS=64 $(KFLAGS)" \ "LIBS= $(LIBS) -ltermlib -lsocket -lnsl -lm -lresolv -lpam" # Solaris 9, 10, or 11 with OpenSSL built with Sun CC. # Here's an example of how to invoke this target in case your OpenSSL # headers and libraries are not in /usr/local: # # make solaris9+openssl "SSLINC=" "SSLLIB=" \ # "KFLAGS= -I/opt/openssl-0.9.8k/include -L/opt/openssl-0.9.8k/lib" # # Don't use 'make -e' because that inhibits passing of KFLAGS to # the base (solaris9) target. # #OK C-Kermit 9.0 solaris9+ssl solaris10+ssl solaris11+ssl \ solaris9+openssl solaris10+openssl solaris11+openssl: @echo 'Making C-Kermit $(CKVER) for Solaris 9/10/11 with OpenSSL: cc' @case `openssl version` in \ *0.9.7*) OPENSSLOPTION="-DOPENSSL_097" ;; \ *0.9.8*) OPENSSLOPTION="-DOPENSSL_098" ;; \ *1.[0-9].[0-9]*) OPENSSLOPTION="-DOPENSSL_100" ;; \ *) OPENSSLOPTION="" ;; \ esac ; \ HAVE_DES=''; \ DES_LIB=''; \ if ls $(SSLLIB)/libdes* > /dev/null 2> /dev/null; then \ DES_LIB='-ldes425'; \ HAVE_DES='-DCK_DES -DLIBDES'; \ echo "HAVE DES"; \ else echo "NO DES"; \ fi; \ $(MAKE) "MAKE=$(MAKE)" solaris9 KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-DCK_AUTHENTICATION -DCK_SSL -DZLIB $$HAVE_DES \ -DNO_DCL_INET_ATON $$OPENSSLOPTION $(SSLINC) $(KFLAGS)" \ "LIBS= $(SSLLIB) -lz -lssl $$DES_LIB -lcrypto $(LIBS)" # Solaris 9 or later with OpenSSL, built with gcc. # Remove -DNO_DCL_INET_ATON if inet_aton comes up missing. This target nicely # chains to the solaris{9,10,11}g target but for some reason it doesn't work if # you add the -DFORWARD_X option, thus the solaris9g+openssl+forward_x target. # #OK: 2011/06/14 solaris9g+ssl solaris10g+ssl solaris11g+ssl \ solaris9g+openssl solaris10g+openssl solaris11g+openssl: @echo 'Making C-Kermit $(CKVER) for Solaris 9/10/11 with OpenSSL: gcc' @case `openssl version` in \ *0.9.7*) OPENSSLOPTION="-DOPENSSL_097" ;; \ *0.9.8*) OPENSSLOPTION="-DOPENSSL_098" ;; \ *1.[0-9].[0-9]*) OPENSSLOPTION="-DOPENSSL_100" ;; \ *) OPENSSLOPTION="" ;; \ esac ; \ HAVE_DES=''; \ DES_LIB=''; \ if ls $(SSLLIB)/libdes* > /dev/null 2> /dev/null; then \ DES_LIB='-ldes425'; \ HAVE_DES='-DCK_DES -DLIBDES'; \ echo "HAVE DES"; \ else echo "NO DES"; \ fi; \ $(MAKE) "MAKE=$(MAKE)" solaris9g KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-DCK_AUTHENTICATION -DCK_SSL -DZLIB $$HAVE_DES \ -DNO_DCL_INET_ATON $$OPENSSLOPTION $(SSLINC) $(KFLAGS)" \ "LIBS= $(SSLLIB) -lz -lssl $$DES_LIB -lcrypto $(LIBS)" # Solaris 9 or later with gcc + OpenSSL + Shadow (32-bit). # Remove -DNO_DCL_INET_ATON if inet_aton comes up missing. # Includes long file support - not sure if this was available before Solaris 9. # Detects Solaris version automatically. # solaris9g+openssl+forward_x: @echo 'Making C-Kermit $(CKVER) for Solaris 9 or later with OpenSSL...' @case `openssl version` in \ *0.9.7*) OPENSSLOPTION="-DOPENSSL_097" ;; \ *0.9.8*) OPENSSLOPTION="-DOPENSSL_098" ;; \ *1.[0-9].[0-9]*) OPENSSLOPTION="-DOPENSSL_100" ;; \ *) OPENSSLOPTION="" ;; \ esac ; case `uname -r` in \ 5.9) SOLARISVERSION="-DSOLARIS9" ;; \ 5.10) SOLARISVERSION="-DSOLARIS10" ;; \ 5.11) SOLARISVERSION="-DSOLARIS11" ;; \ *) SOLARISVERSION="-DSOLARIS" ;; \ esac ; \ $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} CC=gcc CC2=gcc \ "CFLAGS = -g -O -Usun -DSVR4 $$SOLARISVERSION \ -DHAVE_STREAMS -DHAVE_GRANTPT -DHAVE_PTSNAME -DPUSH_PTEM \ -DPUSH_LDTERM -DPUSH_TTCOMPAT \ -DSTERMIOX -DSELECT -DFNFLOAT -DBIGBUFOK -D_FILE_OFFSET_BITS=64 \ -DCK_AUTHENTICATION -DCK_SSL -DCK_PAM -DCK_SHADOW -DZLIB -DLIBDES \ -DFORWARD_X $$OPENSSLOPTION $(SSLINC) $(KFLAGS)" \ "LIBS= $(SSLLIB) -lpam -ldes425 -lz -ltermlib \ -lsocket -lnsl -lm -lresolv -lssl -lcrypto -lpam -lz $(LIBS)" # These two should be folded in with the ones just above. #Solaris 9 with gcc 3.1 + OpenSSL (32-bit) solaris9g+openssl+zlib+pam+shadow: $(MAKE) "MAKE=$(MAKE)" solaris2xg32+openssl+zlib+pam+shadow \ KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-DSOLARIS9 -DUSE_STRERROR $(KFLAGS)" #Solaris 10 with gcc 3.1 + OpenSSL (32-bit) solaris10g+openssl+zlib+pam+shadow: $(MAKE) "MAKE=$(MAKE)" solaris2xg32+openssl+zlib+pam+shadow \ KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-DSOLARIS10 -DUSE_STRERROR $(KFLAGS)" #The following (old, old) sunosxxx entries are for debugging and testing only. sunos41x: $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -O -DSUNOS41 -DDIRENT -DNOTLOG -DNOMSEND \ -DNOUUCP -DNOSIGWINCH -DNOREDIRECT -DNOPUSH -DNOCCTRAP \ -DNOICP -DNOLOCAL $(KFLAGS)" #SunOS 4.1.x, debugging with Pure Software, Inc., Purify 2 (commercial runtime #error-detection software for catching wild array references, etc). #Before running the resulting wermit, you'll also need to define and export #the following environment variables (as in this example): #PURIFYHOME=/usr/local/purify ; export PURIFYHOME #PURIFYCACHEDIR=/tmp ; export PURIFYCACHEDIR sunos41cp: @echo Making C-Kermit $(CKVER) for SunOS 4.1 / BSD / Curses / Purify... $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CC2= purify -cache_dir=/usr/tmp cc" \ "CFLAGS= -g -DSUNOS41 -DHDBUUCP -DDIRENT -DTCPSOCKET \ -DSAVEDUID -DCK_CURSES $(KFLAGS)" \ "LIBS= -lcurses -ltermcap" #SunOS 4.1 with malloc debugger sunos41md: @echo Making C-Kermit $(CKVER) for SunOS 4.1 malloc debug... $(MAKE) mermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -O -DSUNOS41 -DHDBUUCP -DDIRENT -DTCPSOCKET \ -DSAVEDUID $(KFLAGS) -Dmalloc=dmalloc -Dfree=dfree -DMDEBUG" sunos41gmd: @echo Making C-Kermit $(CKVER) for SunOS 4.1 with gcc and curses... $(MAKE) mermit KTARGET=$${KTARGET:-$(@)} "CC= gcc " "CC2= gcc" \ "CFLAGS= -g -DSUNOS41 -DHDBUUCP -DDIRENT -DTCPSOCKET \ -DNDGPWNAM -DSAVEDUID -DCK_CURSES -DRLOGCODE \ $(KFLAGS) -Dmalloc=dmalloc -Dfree=dfree -DMDEBUG" \ "LIBS= -lcurses -ltermcap" #SunOS version 4.1, gcc, profiling with gprof, no debugging. #To get profile, "make sunos41p" (on Sun), then "./wermit". After running #wermit, "gprof ./wermit | lpr" (or whatever) to get execution profile. sunos41p: @echo Making C-Kermit $(CKVER) for SunOS 4.x with profiling... $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} "CC= gcc " "CC2= gcc" \ "CFLAGS= -DSUNOS41 -DNODEBUG -DSAVEDUID -DDIRENT -DTCPSOCKET \ -DNDGPWNAM $(KFLAGS) -pg" "LNKFLAGS = -pg" #SunOS version 4.1 or later, BSD environment, minimum features. sunos41min: @echo Minimum interactive $(MAKE) "MAKE=$(MAKE)" sunos41 KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-DNOSPL -DNOXMIT -DNOMSEND -DNOFRILLS -DNORETRY \ -DNODIAL -DNOHELP -DNODEBUG -DNOTLOG -DNOSCRIPT -DNOCSETS \ -DNOSHOW -DNOSETKEY -DNOUUCP -DNORECALL -DNOREDIRECT \ -DNOPUSH -DNOMDMHUP -DNOJC -DNOFDZERO -DNOESCSEQ \ -DNONET -DCK_SMALL -DNOCKSPEED -DNOCKTIMERS -DNOLOGIN \ -DNOCKXYZ -DNOKERBEROS -DNOMKDIR -DNOPATTERNS -DNOPERMS -DNOPIPESEND \ -DNORECURSIVE -DNORENAME -DNORESEND -DNOSETKEY \ -DNOTRIGGER -DNOTUNING $(KFLAGS)" "LNKFLAGS = -s" #SunOS version 4.1, BSD environment, min size, command-line only... sunos41m: @echo Minimum size $(MAKE) "MAKE=$(MAKE)" sunos41min KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-DNOICP $(KFLAGS)" #SunOS version 4.1, BSD environment, min size, cmd-line only, remote only... # sunos41mr: @echo Minimum size $(MAKE) "MAKE=$(MAKE)" sunos41min KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-DNOICP -DNOLOCAL $(KFLAGS)" #SunOS version 4.1, BSD environment, min size, interactive... sunos41mi: @echo Minimum size $(MAKE) "MAKE=$(MAKE)" sunos41min KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-DNOCMDL $(KFLAGS)" #SunOS version 4.1, BSD environment, min size, interactive, remote only... sunos41mir: @echo Minimum size $(MAKE) "MAKE=$(MAKE)" sunos41min KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-DNOCMDL -DNOLOCAL $(KFLAGS)" #SunOS 4.1, System V R3 environment (-i option omitted). sunos41s5: @echo Making C-Kermit $(CKVER) for SunOS 4.1 System V R3... @echo For testing purposes only - NOT for production use. @echo For a useable version, make sunos41 instead. $(MAKE) wermit "CC= /usr/5bin/cc " "CC2=/usr/5bin/cc " \ KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DSUN4S5 -DDIRENT -DHDBUUCP -DNOLEARN -DCK_POLL $(KFLAGS) -O" #As above, but with curses support sunos41s5c: @echo Making C-Kermit $(CKVER) for SunOS 4.1 System V R3... @echo Curses included. @echo For testing purposes only - NOT for production use. @echo For a useable version, make sunos41 instead. $(MAKE) wermit "CC= /usr/5bin/cc " "CC2=/usr/5bin/cc " \ KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DSUN4S5 -DDIRENT -DHDBUUCP -DNOLEARN \ -DCK_POLL -DCK_CURSES -DCK_NEWTERM $(KFLAGS) -O" "LIBS= -lcurses" #As above, but with curses support AND net support sunos41s5tcpc: @echo Making C-Kermit $(CKVER) for SunOS 4.1 System V R3... @echo TCP/IP and curses included. No debug log. @echo For testing purposes only - NOT for production use. @echo For a useable version, make sunos41 instead. $(MAKE) xermit "CC= /usr/5bin/cc " "CC2=/usr/5bin/cc " \ KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DSUN4S5 -DDIRENT -DHDBUUCP -DCK_POLL \ -DNODEBUG -DCK_CURSES -DCK_NEWTERM -DTCPSOCKET $(KFLAGS) -O" \ "LIBS= -lcurses -lresolv" # (End of SunOS test entries...) #Apollo with Domain SR10.0 or later, BSD environment #Reportedly, it might also help to add '-A,systype=bsd4.3' to CFLAGS. #Reportedly, there is also a problem with getc & putc macros that can #be handled by using '#ifdef apollo' somewhere to redefine them??? #On the other hand, other reports indicate that it works fine as-is. #NOTE: This entry was previously like this: # $(MAKE) wermit "CFLAGS= -DNOFILEH -DBSD4 $(KFLAGS) -Uaegis \ # -DTCPSOCKET -U__STDC__" #Reports (Dec 91) indicate SR10 has an ANSI-compliant C compiler, #in addition to an older one that claimed to be ANSI-compliant but wasn't. #The following make entry (plus checks that are made in ckcdeb.h) detect #which compiler is used and define the CK_ANSIC or NOANSI flags accordingly. sr10-bsd: @echo Making C-Kermit $(CKVER) for Apollo SR10.0 / BSD ... $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -O -DAPOLLOSR10 -DBSD43 -DTCPSOCKET -DCK_CURSES -DNOLEARN \ -Uaegis $(KFLAGS)" "LIBS= -lcurses -ltermcap" #Apollo with Domain SR10.0 or later, System V R3 environment. #Don't use the optimizer (-O), it causes problems at runtime. sr10-s5r3: @echo Making C-Kermit $(CKVER) for Apollo SR10.0 / Sys V R3 ... $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DNOFILEH -DSVR3 -DAPOLLOSR10 -DNOLEARN $(KFLAGS) \ -Uaegis -U__STDC__" #Apollo Domain/IX (untested, try this if sr10-bsd doesn't work) # -DTCPSOCKET can probably be added here. apollobsd: @echo Making C-Kermit $(CKVER) for Apollo Domain/IX... $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CC= /bin/cc " "CC2= /bin/cc " \ "CFLAGS= -DNOFILEH -DBSD4 -DAPOLLOBSD -DNOLEARN $(KFLAGS) -Uaegis" #Version 7 Unix (see comments near top of makefile) v7: @echo Making C-Kermit $(CKVER) for UNIX Version 7. @echo Read the makefile if you have trouble with this... $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS=-DV7 -DPROCNAME=\\\"$(PROC)\\\" \ -DBOOTNAME=\\\"$(BOOTFILE)\\\" -DNPROCNAME=\\\"$(NPROC)\\\" \ -DNPTYPE=$(NPTYPE) $(DIRECT) -DO_RDWR=2 -DO_NDELAY=0 -DO_SCCS_ID \ -DNOLEARN $(KFLAGS)" #AT&T UNIX System V R3, signal() is void rather than int. #Uses dirent.h and Honey DanBer UUCP. #Add the -i link option if necessary. #If you get errors like "ws_row undefined" in ckutio.c, add -DNONAWS. sys5r3: @echo 'Making C-Kermit $(CKVER) for AT&T UNIX System V R3...' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DSVR3 -DDIRENT -DHDBUUCP -DNOLEARN $(KFLAGS) -O" \ "LNKFLAGS=" #As above, plus curses. sys5r3c: @echo 'Making C-Kermit $(CKVER) for AT&T UNIX System V R3 + curses...' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DSVR3 -DDIRENT -DHDBUUCP -DCK_CURSES -DNONAWS -DNOLEARN \ $(KFLAGS) -O" "LNKFLAGS=" "LIBS = -ltermlib" #System V R3.2 for PCs built on Interactive UNIX SV/386 R4.x #but with all calls to dup2() disabled because generic SVR3 does not have dup2. # (The -linet library might not need to be in this one.) sys5r32is: @echo 'Making C-Kermit $(CKVER) for System V/386 R32 $(MAKE) wermit CC="$(CC)" CC2="$(CC2)" \ "CFLAGS = -DSVR3 -DDIRENT -DHDBUUCP -O -DNOCSETS -DNOREALPATH \ -DUID_T=ushort -DGID_T=ushort -DI386IX -DSVR3JC -DCK_CURSES -DNONAWS \ -DPOSIX_JC -DCK_REDIR -DCK_POLL -DDCLGETCWD -DNOFDZERO -DNOREDIRECT \ -DNOZEXEC -DNOLEARN $(KFLAGS)" "LIBS=-lcurses -lc_s -linet" #System V R3.2 for PCs built on Interactive UNIX SV/386 R4.x #but with all calls to dup2() disabled because generic SVR3 does not have dup2. #With TCP/IP added. sys5r32isnet: @echo 'Making C-Kermit $(CKVER) for System V/386 R32 + TCP/IP $(MAKE) wermit CC="$(CC)" CC2="$(CC2)" \ "CFLAGS = -DSVR3 -DDIRENT -DHDBUUCP -O -DNOCSETS -DNOREALPATH \ -DUID_T=ushort -DGID_T=ushort -DI386IX -DSVR3JC -DCK_CURSES -DNONAWS \ -DPOSIX_JC -DCK_REDIR -DCK_POLL -DDCLGETCWD -DNOFDZERO -DNOREDIRECT \ -DNOLEARN -DNOZEXEC -DTCPSOCKET $(KFLAGS)" "LIBS=-lcurses -lc_s -linet" iclsys5r3: make sys5r3 KTARGET=$${KTARGET:-$(@)} KFLAGS=-DICLSVR3 #AT&T UNIX System V R3. As above, but no ANSI prototyping. sys5r3na: @echo 'Making C-Kermit $(CKVER) for AT&T UNIX System V R3...' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DSVR3 -DDIRENT -DHDBUUCP -DNOANSI -DNOLEARN $(KFLAGS) -O" \ "LNKFLAGS=" #AT&T UNIX System V R3, for 3B computers with Wollongong TCP/IP. sys5r3net3b: @echo 'Making C-Kermit $(CKVER) for AT&T UNIX SVR3/3B/Wollongong...' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DSVR3 -DDIRENT -DHDBUUCP -DWOLLONGONG -DNOLEARN $(KFLAGS) \ -O" "LIBS= -lnet -lnsl_s" "LNKFLAGS =" #AT&T UNIX System V R3, signal() is void rather than int. #Uses dirent.h and Honey DanBer uucp, has . #Has for RTS/CTS flow control. sys5r3tx: @echo 'Making C-Kermit $(CKVER) for AT&T UNIX System V R3...' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DSVR3 -DDIRENT -DHDBUUCP -DTERMIOX -DNOLEARN \ $(KFLAGS) -i -O" "LNKFLAGS =" #AT&T UNIX System V R3, signal() is void rather than int. #Uses dirent.h and Honey DanBer uucp, has . #Has for RTS/CTS flow control. sys5r3sx: @echo 'Making C-Kermit $(CKVER) for AT&T UNIX System V R3...' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DSVR3 -DDIRENT -DHDBUUCP -DSTERMIOX -DNOLEARN \ $(KFLAGS) -i -O" "LNKFLAGS =" #AT&T UNIX System V R4. #Has . sys5r4: @echo 'Making C-Kermit $(CKVER) for AT&T UNIX System V R4...' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -O -DSVR4 -DDIRENT -DHDBUUCP -DTERMIOX -DNOLEARN $(KFLAGS)" \ "LNKFLAGS = -s" #AT&T UNIX System V R4 with Wollongong TCP/IP. #Has . sys5r4net: @echo 'Making C-Kermit $(CKVER) for System V R4 + Wollongong TCP/IP...' @echo ' If sockets-library routines are missing at link time, then' @echo ' try the sys5r4net2 entry.' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -O -DSVR4 -DDIRENT -DHDBUUCP -DNOLEARN \ -DTERMIOX -DWOLLONGONG $(KFLAGS)" "LNKFLAGS = -s" #As above, but needs libs included. sys5r4net2: @echo ' PLEASE READ ckuins.txt IF YOU GET MISSING HEADER FILES.' @echo ' (Search for WOLLONGONG...)' $(MAKE) sys5r4net KTARGET=$${KTARGET:-$(@)} "LIBS= -lsocket -lnsl" #As above plus curses. sys5r4net2c: echo 'Making C-Kermit $(CKVER) for System V R4 + Wollongong TCP/IP...' @echo ' PLEASE READ ckuins.txt IF YOU GET MISSING HEADER FILES.' @echo ' (Search for WOLLONGONG...)' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -O -DSVR4 -DDIRENT -DHDBUUCP -DNOLEARN \ -DTERMIOX -DWOLLONGONG -DCK_CURSES $(KFLAGS)" "LNKFLAGS = -s" \ "LIBS= -lsocket -lnsl -lcurses" #DELL UNIX System V R4. #Has , regular Berkeley sockets library, i.e. in.h and inet.h #are not misplaced in sys (rather than netinet and arpa, respectively). #Uses ANSI C constructs, advisory file locking on devices, etc. #Warning: -DSTERMIOX enables hardware flow control (RTS/CTS), but reportedly #this does not work with the normal drivers. However, it might still work #on non-Dell systems, or even Dell systems with different drivers installed. dellsys5r4: @echo 'Making C-Kermit $(CKVER) for DELL UNIX System V R4...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -O -DSVR4 -DDELL_SVR4 -DDIRENT -DHDBUUCP \ -DTCPSOCKET -DSTERMIOX -DCK_POLL $(KFLAGS)" \ "LIBS= -lsocket -lnsl" "LNKFLAGS = -s" #As above, curses support added... dellsys5r4c: @echo 'Making C-Kermit $(CKVER) for DELL UNIX System V R4...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -O -DSVR4 -DDELL_SVR4 -DDIRENT -DHDBUUCP \ -DTCPSOCKET -DSTERMIOX -DCK_CURSES -DCK_POLL \ $(KFLAGS)" "LIBS= -lsocket -lnsl -lcurses -ltermcap" "LNKFLAGS = -s" #Minimum interactive: As above, but with every conceivable option removed. dellsys5r4mi: @echo 'Making C-Kermit $(CKVER) for DELL UNIX System V R4...' @echo 'Minimum-size interactive' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -O -DSVR4 -DDELL_SVR4 -DDIRENT \ -UTCPSOCKET -DNOCMDL -DNOSPL -DNOXMIT -DCK_POLL \ -DNOMSEND -DNOFRILLS -DNODIAL -DNOHELP -DNODEBUG -DNOTLOG \ -DNOSCRIPT -DNOCSETS -DNOSHOW -DNOSETKEY -DNOSERVER -DNOUUCP \ -DNOPUSH -DNOMDMHUP -DNOJC -DNOFDZERO -DNOESCSEQ \ $(KFLAGS)" "LNKFLAGS = -s" #Command-line only version. dellsys5r4m: @echo 'Making C-Kermit $(CKVER) for DELL UNIX System V R4...' @echo 'Command-line only' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -O -DSVR4 -DDELL_SVR4 -DDIRENT \ -UTCPSOCKET -DNOICP -DNOFRILLS -DNODIAL -DNODEBUG -DNOTLOG -DNOCSETS \ -DNOSETKEY -DNOESCSEQ -DNOJC -DNOFDZERO -DCK_POLL \ $(KFLAGS)" "LNKFLAGS = -s" #AT&T UNIX System V R4. #Has . sys5r4sx: @echo 'Making C-Kermit $(CKVER) for AT&T UNIX System V R4...' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -O -DSVR4 -DDIRENT -DHDBUUCP -DSTERMIOX -DNOLEARN \ $(KFLAGS)" "LNKFLAGS = -s" "LIBS=$(LIBS)" #AT&T UNIX System V R4. #Has , regular Berkeley sockets library, i.e. in.h and inet.h #are not misplaced in sys (rather than netinet and arpa, respectively). #Uses ANSI C constructs, , etc etc. sys5r4sxtcp: @echo 'Making C-Kermit $(CKVER) for AT&T UNIX System V R4...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -O -DSVR4 -DDIRENT -DHDBUUCP \ -DSTERMIOX -DTCPSOCKET $(KFLAGS)" \ "LIBS= -lsocket -lnsl $(LIBS)" "LNKFLAGS= -s" #AT&T UNIX System V R4. #As above + curses. sys5r4sxtcpc: @echo 'Making C-Kermit $(CKVER) for AT&T UNIX System V R4...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -O -DSVR4 -DDIRENT -DHDBUUCP \ -DSTERMIOX -DCK_CURSES -DTCPSOCKET $(KFLAGS)" \ "LIBS= -lsocket -lnsl -lcurses -ltermcap $(LIBS)" "LNKFLAGS = -s" #AT&T UNIX System V R4. CONSENSYS SVR4.2-1. #Has , regular Berkeley sockets library, i.e. in.h and inet.h #are not misplaced in sys (rather than netinet and arpa, respectively). #Uses ANSI C constructs, , etc. # Fullscreen -DCK_CURSES added (with curses & termcap libs) # Submission by Robert Weiner/Programming Plus, rweiner@watsun.cc.columbia.edu sys5r4sxtcpf: @echo 'Making C-Kermit $(CKVER) for AT&T UNIX System V R4...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -O -DSVR4 -DDIRENT -DHDBUUCP \ -DSTERMIOX -DTCPSOCKET -DCK_CURSES $(KFLAGS)" \ "LIBS= -lsocket -lnsl -L/usr/ccs/lib -lcurses -ltermcap" \ "LIBS=$(LIBS)" "LNKFLAGS = -s" #Smallest possible version for System V R4 s5r4m: @echo Minimum size $(MAKE) "MAKE=$(MAKE)" sys5r4sx KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=$(KFLAGS) -DNODIAL -DNOHELP -DNODEBUG -DNOTLOG \ -DNOSCRIPT -DNOCSETS -DNOICP -DNOMSEND -UTCPSOCKET" "LNKFLAGS = -s" #Smallest possible interactive version of above s5r4mi: @echo Minimum interactive $(MAKE) "MAKE=$(MAKE)" sys5r4sx \ "KFLAGS=-DNOSPL -DNOXMIT -DNOMSEND -DNOFRILLS -DNOSHOW \ -DNODIAL -DNOHELP -DNODEBUG -DNOTLOG -DNOSCRIPT -DNOCSETS -DNOSETKEY \ -UTCPSOCKET $(KFLAGS)" "LNKFLAGS = -s" #AT&T UNIX System V R4, has #ANSI C function prototyping disabled. sys5r4sxna: @echo No ANSI C prototyping... $(MAKE) "MAKE=$(MAKE)" sys5r4sx KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=$(KFLAGS) -DNOANSI" #Stratus FTX. ftx: @echo 'Making C-Kermit $(CKVER) for Stratus FTX 3.x...' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -O -DSVR4 -DFTX -DDIRENT -DHDBUUCP -DSTERMIOX \ -DNOGETUSERSHELL -DNOLEARN +DA1.1 $(KFLAGS)" \ "LNKFLAGS = -s" "LIBS=$(LIBS)" #Stratus FTX + TCP/IP. ftxtcp: @echo 'Making C-Kermit $(CKVER) for Stratus FTX 3.x...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -O -DSVR4 -DFTX -DDIRENT -DHDBUUCP -DNOGETUSERSHELL \ -DSTERMIOX -DTCPSOCKET -DNO_DNS_SRV +DA1.1 $(KFLAGS)" \ "LIBS= -lsocket -lnsl $(LIBS)" "LNKFLAGS= -s" #NCR MP-RAS 2.03 or 3.02 mpras: @echo 'Making C-Kermit $(CKVER) for NCR MP-RAS...' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -O -DSVR4 -DNCRMPRAS -DDIRENT -DHDBUUCP -DSTERMIOX \ -DNOGETUSERSHELL -DUSE_FILE__CNT -DNOLEARN -DNO_DNS_SRV $(KFLAGS)" \ "LNKFLAGS = -s" "LIBS=$(LIBS)" #NCR MP-RAS 2.03 or 3.02 with TCP/IP and curses mprastcpc: @echo 'Making C-Kermit $(CKVER) for NCR MP-RAS + TCP/IP + curses...' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} "CFLAGS=-DTCPSOCKET \ -DCK_CURSES -DSVR4 -DNCRMPRAS -DDIRENT -DHDBUUCP -DSTERMIOX -DNOLEARN \ -DNOGETUSERSHELL -DNO_DNS_SRV DUSE_FILE__CNT -O $(KFLAGS)" \ "LNKFLAGS = -s" "LIBS= -lsocket -lnsl -lcurses -ltermcap $(LIBS)" #SINIX-L V5.41 - includes curses, tcp/ip - Use this one for i386. #This version of SINIX doesn't like fdopen() or popen(). sinix541: @echo 'Making C-Kermit $(CKVER) for Siemens/Nixdorf SINIX V5.41/i386' $(MAKE) ckcpro.$(EXT) "CFLAGS = -DSINIX -DSVR4 -DDIRENT -DHDBUUCP \ -DSTERMIOX -DCK_CURSES -DTCPSOCKET -DSELECT -DCK_ANSIC -DNO_DNS_SRV \ -DSNI541 -DNOGETUSERSHELL -DNONETCMD -DNOPOPEN -kansi -W0 $(KFLAGS)" $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DSINIX -DSVR4 -DDIRENT -DHDBUUCP -DNO_DNS_SRV -DNOPOPEN \ -DFNFLOAT -DSTERMIOX -DCK_CURSES -DTCPSOCKET -DSELECT -DCK_ANSIC \ -DSNI541 -DNOGETUSERSHELL -DNONETCMD -kansi -W0 -O $(KFLAGS)" \ "LIBS= -lsocket -lnsl -lcurses -ltermcap -lm" "LNKFLAGS = -s" sinix541i: $(MAKE) "MAKE=$(MAKE)" "KFLAGS=$(KFLAGS)" sinix541 #SINIX V5.42 - includes curses, tcp/ip, everything - Use this one for MIPS. # As of C-Kermit 7.1, optimization removed -- takes (literally) forever. sinix542: $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DSINIX -DSVR4 -DDIRENT -DHDBUUCP -DNO_DNS_SRV \ -DFNFLOAT -DSTERMIOX -DCK_CURSES -DTCPSOCKET -DSELECT -DCK_ANSIC \ -DSNI542 -DNOGETUSERSHELL -kansi -W0 $(KFLAGS)" \ "LIBS= -lsocket -lnsl -lcurses -ltermcap -lm" "LNKFLAGS = -s" #SINIX V5.42 gcc - includes curses, tcp/ip, everything. #This one was used to build the Pyramid-architecture RM600 version #on SINIX-P 5.42 A10 with gcc but should work for SINIX 5.42 on any other #architecture with gcc. sinix542g: $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} "CC=gcc" "CC2=gcc" \ "CFLAGS = -DSINIX -DSVR4 -DDIRENT -DHDBUUCP -DNO_DNS_SRV \ -DFNFLOAT -DSTERMIOX -DCK_CURSES -DTCPSOCKET -DSELECT -DCK_ANSIC \ -DSNI542 -DNOGETUSERSHELL $(KFLAGS)" \ "LIBS= -lsocket -lnsl -lcurses -ltermcap -lm" \ "LNKFLAGS = -s" #SINIX V5.42 - includes curses, tcp/ip, everything - Use this one for Intel. # (Note: SNI discontinued Intel support after 5.42.) sinix542i: @echo 'Making C-Kermit $(CKVER) for Siemens/Nixdorf SINIX-Z V5.42...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DSINIX -DSVR4 -DDIRENT -DHDBUUCP -DFNFLOAT -DSTERMIOX \ -DCK_CURSES -DTCPSOCKET -DSELECT -DCK_ANSIC -DNO_DNS_SRV -kansi \ -DSNI542 $(KFLAGS)" \ "LIBS= -lsocket -lnsl -lcurses -ltermcap -lm" \ "LNKFLAGS = -s" #Siemens Nixdorf Reliant UNIX V5.43 - includes curses, tcp/ip, everything: # . gettimeofday() suddenly has only one arg instead of two (GTODONEARG). # . The syntax of the Olimit specifier changed. # . The name was changed from SINIX to Reliant UNIX in version 5.43C. sni543: @echo 'Making C-Kermit $(CKVER) for Siemens/Nixdorf Reliant UNIX V5.43' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DSINIX -DSNI543 -DSVR4 -DDIRENT -DHDBUUCP \ -DSTERMIOX -DCK_CURSES -DTCPSOCKET -DSELECT -DCK_ANSIC -DGTODONEARG \ -DNO_DNS_SRV -kansi -W0 -O -F Olimit,3100 $(KFLAGS)" \ "LIBS= -lsocket -lnsl -lcurses -ltermcap" "LNKFLAGS = -s" #Siemens Nixdorf Reliant UNIX V5.44 - Like 5.43 but with different banner. sni544: @echo 'Making C-Kermit $(CKVER) for Siemens/Nixdorf Reliant UNIX V5.44' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DSINIX -DSNI544 -DSVR4 -DDIRENT -DHDBUUCP \ -DSTERMIOX -DCK_CURSES -DTCPSOCKET -DSELECT -DCK_ANSIC -DGTODONEARG \ -DNO_DNS_SRV -kansi -W0 -O -K Olimit,3100 $(KFLAGS)" \ "LIBS= -lsocket -lnsl -lcurses -ltermcap" "LNKFLAGS = -s" #Commodore Amiga with AT&T UNIX System V R4 and TCP/IP support. #Has . svr4amiganet: @echo 'Making C-Kermit $(CKVER) for Amiga SVR4 + TCP/IP...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} "CC=gcc" "CC2=gcc" \ "CFLAGS = -O -DSVR4 -DDIRENT -DHDBUUCP -DSTERMIOX \ -DTCPSOCKET -DCK_CURSES $(KFLAGS)" "LNKFLAGS = -s" \ "LIBS = -lsocket -lnsl -ltermlib" #SCO (Novell (Univel)) UnixWare 1.x or 2.0, no TCP/IP. #This assumes the Novell SDK 1.0, which has . #UnixWare users with the "Prime Time Freeware" CD-ROM SDK will probably have #to use the sys5r4 entry (no termiox.h file, so no hardware flow control). #Change -DSELECT to -DCK_POLL if -DSELECT causes problems. # NOTE: Unixware 1.x builds have not been tried in C-Kermit 7.0. unixware: $(MAKE) "MAKE=$(MAKE)" sys5r4sx KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-DOLD_UNIXWARE -DCK_NEWTERM -DSELECT -DNOGETUSERSHELL \ -DNOSYSLOG $(KFLAGS)" "LIBS=-lcrypt" #UnixWare 1.x or 2.0 with TCP/IP and curses. #fork()-based CONNECT - no high serial speeds. unixwarenetc: $(MAKE) "MAKE=$(MAKE)" sys5r4sxtcpc KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-DOLD_UNIXWARE -DCK_NEWTERM -DSELECT -DNOGETUSERSHELL \ -DNOSYSLOG $(KFLAGS)" "LIBS=-lcrypt -lresolv" uw10: $(MAKE) unixwarenetc KTARGET=$${KTARGET:-$(@)} "KFLAGS=$(KFLAGS)" #This is for Unixware 2.0.x only - use unixware21 for UW 2.1.x. #Has special library search and enables special kludge around library #foulup regarding vfork() (which Kermit doesn't use). Forces POSIX-style #hangup. unixware20: @echo 'Making C-Kermit $(CKVER) for UnixWare 2.0.x...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -O -DOLD_UNIXWARE -DUNIXWARE2 -DSELECT -DSVR4 -DDIRENT \ -DHDBUUCP -DBIGBUFOK -DNOGETUSERSHELL -DSTERMIOX -DCK_CURSES \ -DTCPSOCKET -DUW200 -DFNFLOAT -DCK_NEWTERM -DNOSYSLOG $(KFLAGS)" \ "LIBS= -lsocket -lnsl -lcurses -ltermcap -lcrypt -lgen -lm -lresolv" \ "LNKFLAGS = -s" uw20: $(MAKE) unixware20 KTARGET=$${KTARGET:-$(@)} "KFLAGS=$(KFLAGS)" #Adds big buffers ("large memory model") - otherwise the same as UnixWare 1.x. unixware21: @echo 'Making C-Kermit $(CKVER) for UnixWare 2.1.x...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -O -DUNIXWARE -DSELECT -DSVR4 -DDIRENT -DHDBUUCP -DBIGBUFOK \ -DNOSYSLOG -DSTERMIOX -DCK_CURSES -DTCPSOCKET \ -DCK_NEWTERM -DFNFLOAT -DUNIXWARE2 $(KFLAGS)" \ "LIBS= -lsocket -lnsl -lcurses -ltermcap -lcrypt -lm -lresolv \ $(LIBS)" "LNKFLAGS = -s" #Unixware 2.1.0 uw21: $(MAKE) unixware21 KTARGET=$${KTARGET:-$(@)} "KFLAGS=$(KFLAGS)" #Unixware 2.1.3 uw213: $(MAKE) unixware21 KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-DUSE_FILE__CNT $(KFLAGS)" #Unixware 2.1 with IKSD support uw21iksd: $(MAKE) unixware21 KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-DCK_SHADOW $(KFLAGS)" "LIBS= -lgen" #UnixWare 7 with tc[gs]etspeed() high serial speeds & select()-based CONNECT #and as of C-Kermit 8.0.212, large file support (LFS). #NOTE: This is the one we use. unixware7t: @echo 'Making C-Kermit $(CKVER) for UnixWare 7 with POSIX i/o...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -O -DUNIXWARE -DSELECT -DSVR4 -DDIRENT -DHDBUUCP -DBIGBUFOK \ -DFNFLOAT -DNOGETUSERSHELL -DSTERMIOX -DCK_CURSES -DTCPSOCKET -DPOSIX \ -DUW7 -DUSETCSETSPEED -DCK_NEWTERM -DNOLSTAT -DDCLTIMEVAL \ -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -DNEEDMDMDEFS $(KFLAGS)" \ "LIBS=-lsocket -lnsl -lcurses -ltermcap -lcrypt -lm -lresolv $(LIBS)" \ "LNKFLAGS = -s" #UnixWare 7 - select()-based CONNECT - no POSIX i/o - no high serial speeds. #In other words, just like the UnixWare 1 and 2 builds. unixware7x: @echo 'Making C-Kermit $(CKVER) for UnixWare 7...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -O -DUNIXWARE -DSELECT -DSVR4 -DDIRENT -DHDBUUCP -DBIGBUFOK \ -DUW7 -DNOGETUSERSHELL -DSTERMIOX -DCK_CURSES -DTCPSOCKET -DNOLSTAT \ -DFNFLOAT -DCK_NEWTERM $(KFLAGS)" \ "LIBS=-lsocket -lnsl -lcurses -ltermcap -lcrypt -lm -lresolv $(LIBS)" \ "LNKFLAGS = -s" #UnixWare 7 with POSIX cfset[oi]speed() to allow high serial speeds. #(but the high speeds don't work) unixware7p: @echo 'Making C-Kermit $(CKVER) for UnixWare 7 with POSIX i/o...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -O -DUNIXWARE -DSELECT -DSVR4 -DDIRENT -DHDBUUCP -DBIGBUFOK \ -DUW7 -DNOGETUSERSHELL -DSTERMIOX -DCK_CURSES -DTCPSOCKET -DPOSIX \ -DFNFLOAT -DCK_NEWTERM -DNOLSTAT $(KFLAGS)" \ "LIBS=-lsocket -lnsl -lcurses -ltermcap -lcrypt -lm -lresolv $(LIBS)" \ "LNKFLAGS = -s" # UnixWare 7 built with gcc - This does not work at all... # Reportedly gcc 2.8.1 is broken on Unixware 7. Try egcs? unixware7g: @echo 'Making C-Kermit $(CKVER) for UnixWare 7 with gcc...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CC = gcc" "CC2 = gcc" "LNKFLAGS = -s -shlib" "CFLAGS = -O -DUNIXWARE -DSELECT -DSVR4 -DDIRENT -DHDBUUCP -DBIGBUFOK \ -DUW7 -DNOGETUSERSHELL -DSTERMIOX -DCK_CURSES -DTCPSOCKET -DNOLSTAT \ -DFNFLOAT -DCK_NEWTERM $(KFLAGS)" \ "LIBS=-lsocket -lnsl -lcurses -ltermcap -lcrypt -lm -lresolv $(LIBS)" \ "LNKFLAGS = -s" unixware7: $(MAKE) "MAKE=$(MAKE)" "KFLAGS=$(KFLAGS)" unixware7t \ KTARGET=$${KTARGET:-$(@)} uw7: $(MAKE) "MAKE=$(MAKE)" "KFLAGS=$(KFLAGS)" unixware7t \ KTARGET=$${KTARGET:-$(@)} #SCO OpenUNIX 8.0 ou8: @echo 'Making C-Kermit $(CKVER) for Open UNIX 8...' $(MAKE) "MAKE=$(MAKE)" "KFLAGS=-DOU8 $(KFLAGS)" unixware7t \ KTARGET=$${KTARGET:-$(@)} #UnixWare 7 with OpenSSL uw7ssl uw7+ssl: @echo 'Making C-Kermit $(CKVER) for UnixWare 7 and OpenSSL...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -O -DCK_AUTHENTICATION -DCK_SSL -DCK_SHADOW \ -DUNIXWARE -DSELECT -DSVR4 -DDIRENT -DHDBUUCP -DBIGBUFOK \ -DFNFLOAT -DNOGETUSERSHELL -DSTERMIOX -DCK_CURSES -DTCPSOCKET -DPOSIX \ -DUW7 -DUSETCSETSPEED -DCK_NEWTERM -DNOLSTAT -DDCLTIMEVAL \ $(SSLINC) $(KFLAGS)" \ "LIBS=-lsocket -lnsl -lcurses -ltermcap -lcrypt -lm -lresolv \ -lgen -lcudk70 $(SSLLIB) -lssl -lcrypto $(LIBS)" \ "LNKFLAGS = -s" #As above but includes Shadow password support needed for IKSD. uw7iksd: $(MAKE) "MAKE=$(MAKE)" "KFLAGS=-DCK_SHADOW $(KFLAGS)" \ KTARGET=$${KTARGET:-$(@)} "LIBS= -lgen" unixware7t #As above but links with static API for realpath() so a binary built #with this target on UW7.1 will also work on 7.0. Requires SCO UDK #rather than the stock compiler. uw7iksdudk: $(MAKE) "MAKE=$(MAKE)" "KFLAGS=-DCK_SHADOW $(KFLAGS)" \ KTARGET=$${KTARGET:-$(@)} "LIBS= -lgen -lcudk70" unixware7t #ESIX SVR4.0.3 or 4.04 with TCP/IP support. #Has , ANSI C function prototyping disabled. #Add -m486 to CFLAGS if desired. esixr4: @echo 'Making C-Kermit $(CKVER) for ESIX SVR4 + TCP/IP...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -O -DSVR4 -DDIRENT -DHDBUUCP -DNOANSI \ -DSTERMIOX -DTCPSOCKET $(KFLAGS)" "LNKFLAGS = -s" \ "LIBS = -lsocket -lnsl" #AT&T UNIX System V R4. #Has , Wollongong WIN/TCP TCP/IP. sys5r4sxnet: @echo 'Making C-Kermit $(CKVER) for AT&T UNIX System V R4...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -O -DSVR4 -DDIRENT -DHDBUUCP \ -DSTERMIOX -DWOLLONGONG $(KFLAGS)" "LNKFLAGS = -s" #AT&T UNIX System V R4, no or . sys5r4nx: @echo 'Making C-Kermit $(CKVER) for AT&T UNIX System V R4...' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -O -DSVR4 -DDIRENT -DHDBUUCP -DNOLEARN $(KFLAGS)" \ "LNKFLAGS = -s" #AT&T UNIX System V R4, no or , curses, TCP/IP. sys5r4nxnetc: @echo 'Making C-Kermit $(CKVER) for AT&T UNIX System V R4...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -O -DSVR4 -DDIRENT -DHDBUUCP \ -DCK_CURSES -DTCPSOCKET $(KFLAGS)" \ "LIBS = -lcurses -lsocket -lnsl -ltcpip" \ "LNKFLAGS = -s" #AT&T UNIX System V R4, no or , Wollongong TCP/IP. sys5r4nxtwg: @echo 'Making C-Kermit $(CKVER) for AT&T UNIX System V R4...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -O -DSVR4 -DDIRENT -DHDBUUCP -DWOLLONGONG $(KFLAGS)" "LNKFLAGS = -s" #ICL UNIX System V R4.(DRS N/X) version :- #UNIX System V Release 4.0 ICL DRS 6000 (SPARC) #DRS/NX 6000 SVR4 Version 5 Level 1 Increment 4 #Has , regular Berkeley sockets library, i.e. in.h and inet.h #are not misplaced in sys (rather than netinet and arpa, respectively). #Uses ANSI C constructs, advisory file locking on devices, etc. #Remove -lnsl if it causes trouble. iclsys5r4: @echo 'Making C-Kermit $(CKVER) for ICL UNIX System V R4 (DRS N/X)' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -O -DSVR4 -DICL_SVR4 -DDIRENT -DHDBUUCP -DNOGETUSERSHELL \ -DSTERMIOX -DTCPSOCKET $(KFLAGS)" \ "LIBS= -lsocket -lnsl -lresolv " "LNKFLAGS = -s" #As above but for DRS/NX 4.2MP 7MPlus. iclsys5r4m+: @echo 'Making C-Kermit $(CKVER) for ICL UNIX System V R4 DRS/NX 4.2MP+' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -O -DSVR4 -DICL_SVR4 -DDIRENT -DHDBUUCP -DNOIKSD \ -DSTERMIOX -DTCPSOCKET $(KFLAGS)" \ "LIBS= -lsocket -lnsl -lm -lc -g -lgen " "LNKFLAGS = -s" #As above but for DRS/NX 4.2MP 7MPlus with IKSD support. iclsys5r4m+iksd: @echo 'Making C-Kermit $(CKVER) for ICL UNIX System V R4 DRS/NX 4.2MP+' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -O -DSVR4 -DICL_SVR4 -DDIRENT -DHDBUUCP -DNOGETUSERSHELL \ -DSTERMIOX -DTCPSOCKET $(KFLAGS)" \ "LIBS= -lsocket -lnsl -lm -lc -g -lgen -lresolv " "LNKFLAGS = -s" iclsys5r4_486: $(MAKE) "MAKE=$(MAKE)" iclsys5r4 KTARGET=$${KTARGET:-$(@)} #Data General DG/UX 4.30 (System V R3) for DG AViiON, with TCP/IP support. dgux430: @echo 'Making C-Kermit $(CKVER) for DG AViiON DG/UX 4.30...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -O -DDGUX430 -DSVR3 -DDIRENT -DTCPSOCKET \ -DNOINADDRX -DNOGETUSERSHELL $(KFLAGS)" #Data General DG/UX 4.30 for DG AViiON, with TCP/IP support with BSDisms. dgux430bsd: @echo 'Making C-Kermit $(CKVER) for DG AViiON DG/UX 4.30...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -O -DDGUX430 -D_BSD_SOURCE -DBSD4 \ -DNOINADDRX -DTCPSOCKET -DNOGETUSERSHELL $(KFLAGS)" #Data General DG/UX 5.4 (System V R4) for DG AViiON, with TCP/IP support. #Add -lsocket -lnsl if inet_addr comes up missing... #Hmmm - I really think CK_POLL can be removed from this one in which case #there is no difference between dgux540 and dgux540i. dgux540: @echo 'Making C-Kermit $(CKVER) for DG AViiON DG/UX 5.40...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -O -DDGUX540 -DDIRENT -DHDBUUCP -DNOINADDRX \ -DSTERMIOX -DTCPSOCKET -DCK_POLL -DNOGETUSERSHELL $(KFLAGS)" #Data General DG/UX 5.40 (System V R4) for Intel AViiON, with TCP/IP support. dgux540i: @echo 'Making C-Kermit $(CKVER) for DG AViiON DG/UX 5.40...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -O -DDGUX540 -DDIRENT -DHDBUUCP -DNOINADDRX \ -DSTERMIOX -DTCPSOCKET -DNOGETUSERSHELL $(KFLAGS)" \ "LIBS = -lsocket -lnsl" dgux54: make dgux540 KTARGET=$${KTARGET:-$(@)} #Data General DG/UX 5.4 (= System V R4) for DG AViiON, with TCP/IP support. # And curses. dgux540c: @echo 'Making C-Kermit $(CKVER) for DG AViiON DG/UX 5.4...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -O -DDGUX540 -DDIRENT -DHDBUUCP -DNOINADDRX \ -DSTERMIOX -DTCPSOCKET -DCK_CURSES -DCK_NEWTERM -DNOGETUSERSHELL \ $(KFLAGS)" "LIBS= -lcurses8 -ltermcap" "LNKFLAGS = -s" #As above but for Intel - only difference is name library names. dgux540ic: @echo 'Making C-Kermit $(CKVER) for DG AViiON DG/UX 5.40...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -O -DDGUX540 -DDIRENT -DHDBUUCP -DNOINADDRX \ -DSTERMIOX -DTCPSOCKET -DCK_CURSES -DCK_NEWTERM -DNOGETUSERSHELL \ $(KFLAGS)" "LIBS = -lsocket -lnsl -lcurses -ltermcap" dgux54c: make dgux540c KTARGET=$${KTARGET:-$(@)} #DG/UX 5.4R3.10 dgux54310: @echo 'Making C-Kermit $(CKVER) for DG AViiON DG/UX 5.4R3...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DDGUX540 -DDGUX54310 -DDIRENT -DHDBUUCP -DSELECT \ -DSTERMIOX -DTCPSOCKET -DCK_CURSES -DCK_NEWTERM -DNOGETUSERSHELL \ -DNOINADDRX $(KFLAGS)" "LIBS= -lcurses8 -ltermcap" "LNKFLAGS = -s" #DG/UX 5.4R4.10 - Includes everything. dgux54410: @echo 'Making C-Kermit $(CKVER) for DG/UX 5.4R4.10...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -O -DDGUX540 -DDGUX54410 -DDIRENT -DHDBUUCP -DSELECT \ -DSTERMIOX -DTCPSOCKET -DCK_CURSES -DCK_NEWTERM -DNOGETUSERSHELL \ -DNOINADDRX $(KFLAGS)" "LIBS = -lsocket -lnsl -lcurses -ltermcap" #DG/UX 5.4R4.11 - Includes everything. dgux54411: @echo 'Making C-Kermit $(CKVER) for DG/UX 5.4R4.11...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -O -DDGUX540 -DDGUX54411 -DDIRENT -DHDBUUCP -DSELECT \ -DSTERMIOX -DTCPSOCKET -DCK_CURSES -DCK_NEWTERM -DNOGETUSERSHELL \ -DNOINADDRX $(KFLAGS)" "LIBS = -lsocket -lnsl -lcurses -ltermcap" #DG/UX 5.4R4.20 - Includes everything. dgux54420: @echo 'Making C-Kermit $(CKVER) for DG/UX 5.4R4.20...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -O -DDGUX540 -DDGUX54420 -DDIRENT -DHDBUUCP -DSELECT \ -DSTERMIOX -DTCPSOCKET -DCK_CURSES -DCK_NEWTERM -DNOGETUSERSHELL \ -DNOINADDRX $(KFLAGS)" \ "LIBS = -lsocket -lresolv -lnsl -lcurses -ltermcap" #Silicon Graphics System V R3 with BSD file system (IRIS) iris: @echo Making C-Kermit $(CKVER) for Silicon Graphics IRIX pre-3.3... $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -O -DSVR3 -DLONGFN -DNOLEARN $(KFLAGS) -I/usr/include/bsd" \ "LIBS = -lbsd" #Silicon Graphics IRIS System V R3 irix33: @echo 'Making C-Kermit $(CKVER) for Silicon Graphics IRIX 3.3...' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DSVR3 -DDIRENT -DHDBUUCP -DNOLEARN $(KFLAGS) -O" \ "LNKFLAGS = -s" #Silicon Graphics Iris Indigo with IRIX 4.0.0 or 5.0... #Strict ANSI C compilation, TCP/IP support included irix40: @echo 'Making C-Kermit $(CKVER) for Silicon Graphics IRIX 4.0...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DIRIX40 -DSVR3 -DDIRENT -DHDBUUCP -DPWID_T=uid_t \ -DCK_ANSIC -DTCPSOCKET $(KFLAGS) -O -Olimit 1600 -I/usr/include/bsd" \ "LNKFLAGS = -s" #As above, but with fullscreen display (curses) and Sun Yellow Pages support. #NOTE: IRIX versions prior to 5 run COFF binaries. irix40ypc: @echo 'Making C-Kermit $(CKVER) for Silicon Graphics IRIX 4.0.' @echo 'Includes fullscreen file display and Sun Yellow Pages...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DIRIX40 -DSVR3 -DDIRENT -DHDBUUCP -DCK_CURSES \ -DPWID_T=uid_t -DCK_ANSIC -DTCPSOCKET $(KFLAGS) \ -O -Olimit 1600 -I/usr/include/bsd" \ "LIBS = -lcurses -lsun" "LNKFLAGS = -s" # Silicon Graphics Iris Series 4D/*, IRIX 4.0.x, -O4 ucode optimized. # Huge temporary file space needed for ucode optimizer. If you get an error # like "ugen: internal error writing to /tmp/ctmca08777: Error 0", define the # the TMPDIR environment variable to point to a file system that has more # space available, e.g. "setenv TMPDIR /usr/tmp". irix40u: @echo 'Making C-Kermit $(CKVER) for Silicon Graphics IRIX 4.0...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DIRIX40 -DSVR3 -DDIRENT -DHDBUUCP -DPWID_T=uid_t \ -DCK_ANSIC -DTCPSOCKET $(KFLAGS) -O4 -Olimit 1600" \ "LNKFLAGS=-O4 -Olimit 1600 -s" "EXT=u" # As above, with Curses Support added irix40uc: @echo 'Making C-Kermit $(CKVER) for Silicon Graphics IRIX 4.0...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DIRIX40 -DSVR3 -DDIRENT -DHDBUUCP -DPWID_T=uid_t \ -DCK_ANSIC -DCK_CURSES -DTCPSOCKET $(KFLAGS) -O4 -Olimit 1600" \ "LNKFLAGS=-O4 -Olimit 1600 -s" "EXT=u" "LIBS= -lcurses -ltermcap" #Silicon Graphics IRIX 5.x. #Yellow Pages and Curses support included. #IRIX version 5.x can run COFF or ELF binaries. irix51: @echo 'Making C-Kermit $(CKVER) for Silicon Graphics IRIX 5.x' @echo 'Includes fullscreen file display and Yellow Pages...' @echo 'Add -mips to CFLAGS specify a particular hardware target.' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DIRIX51 -DSVR4 -DDIRENT -DHDBUUCP -DCK_CURSES -DCK_NEWTERM \ -DPWID_T=uid_t -DCK_ANSIC -DTCPSOCKET -DSELECT -DNOGETUSERSHELL \ -DSYSTIMEH -DDCLPOPEN -DDCLFDOPEN $(KFLAGS) -ansi -O -Olimit 3000" \ "LIBS = -lcurses" "LNKFLAGS = -s" #Use this one if irix51 blows up due to lack of swap space or whatever. irix51x: @echo 'Making C-Kermit $(CKVER) for Silicon Graphics IRIX 5.x' @echo 'Includes fullscreen file display and Yellow Pages...' @echo 'Add -mips to CFLAGS specify a particular hardware target.' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DIRIX51 -DSVR4 -DDIRENT -DHDBUUCP -DCK_CURSES -DCK_NEWTERM \ -DPWID_T=uid_t -DCK_ANSIC -DTCPSOCKET -DSELECT -DNOGETUSERSHELL \ -DSYSTIMEH -DDCLPOPEN -DDCLFDOPEN $(KFLAGS)" \ "LIBS = -lcurses" "LNKFLAGS = -s" irix51ypc: $(MAKE) "MAKE=$(MAKE)" irix51 KTARGET=$${KTARGET:-$(@)} \ "KFLAGS= $(KFLAGS)" #IRIX 5.2 adds RTS/CTS irix52: $(MAKE) "MAKE=$(MAKE)" irix51 KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-DIRIX52 -DCK_RTSCTS $(KFLAGS)" irix53: $(MAKE) "MAKE=$(MAKE)" irix51 KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-DIRIX52 -DIRIX53 -DCK_RTSCTS $(KFLAGS)" irix53x: $(MAKE) "MAKE=$(MAKE)" irix51x KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-DIRIX52 -DIRIX53 -DCK_RTSCTS $(KFLAGS)" #Silicon Graphics IRIX 6.[024] common stuff. #Yellow Pages and Curses support included. #IRIX version 6.0 and later runs only ELF binaries. #Depends on code changes in ckcdeb.h that make -DIRIX6x define all #lower IRIX6x values and IRIX51. irix6x: @echo 'Includes fullscreen file display and Yellow Pages...' @echo 'Add -mips to specify a particular hardware target.' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DSVR4 -DDIRENT -DHDBUUCP -DNOGETUSERSHELL \ -DCK_CURSES -DCK_NEWTERM -DPWID_T=uid_t -DCK_ANSIC -DTCPSOCKET \ -DSELECT -DCK_RTSCTS -O $(KFLAGS)" \ "LIBS = -lcurses" "LNKFLAGS = -s $(LNKFLAGS)" #Silicon Graphics IRIX 6.0. irix60: @echo 'Making C-Kermit $(CKVER) for Silicon Graphics IRIX 6.0' @$(MAKE) "MAKE=$(MAKE)" \ "KFLAGS=-DIRIX60 -Olimit 2138 $(KFLAGS)" \ irix6x KTARGET=$${KTARGET:-$(@)} #Silicon Graphics IRIX 6.2. #Serial speeds > 38400 are available in IRIX 6.2 on O-class machines only. #Note: Olimit must be a number > 0. irix62: @echo 'Making C-Kermit $(CKVER) for Silicon Graphics IRIX 6.2' @$(MAKE) "MAKE=$(MAKE)" \ LNKFLAGS="-Wl,-woff,84" \ "KFLAGS=-DIRIX62 -Olimit 4700 $(KFLAGS)" \ irix6x KTARGET=$${KTARGET:-$(@)} #Silicon Graphics IRIX 6.3. irix63: @echo 'Making C-Kermit $(CKVER) for Silicon Graphics IRIX 6.3' @$(MAKE) "MAKE=$(MAKE)" irix62 KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-DIRIX63" #Silicon Graphics IRIX 6.4. # -woff,84 to linker stops complaints about no symbols loaded from # curses, and -woff 1110 stops complaints about unreachable "break;" # statements in ckcpro.c among others. # tested on SGI Octane, running IRIX 6.4 up to 115200 bps. # -Olimit 0 means infinite. irix64: @echo 'Making C-Kermit $(CKVER) for Silicon Graphics IRIX 6.4' @$(MAKE) "MAKE=$(MAKE)" \ LNKFLAGS="-Wl,-woff,84" \ "KFLAGS=-DIRIX64 -DCK_RTSCTS -Olimit 3000 -woff 1110 $(KFLAGS)" \ irix6x KTARGET=$${KTARGET:-$(@)} irix64gcc: @echo 'Making C-Kermit $(CKVER) for Silicon Graphics IRIX 6.4 gcc' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} "CC = gcc" "CC2 = gcc" \ "CFLAGS= -DSVR4 -DIRIX64 -DDIRENT -DHDBUUCP -DNOGETUSERSHELL \ -DSELECT -DPWID_T=uid_t -DTCPSOCKET -DNOCOTFMC \ -DCK_ANSIC -DCK_RTSCTS -DCK_NEWTERM -DCK_CURSES \ $(KFLAGS) -O" "LIBS= -lcurses -ltermcap -lcrypt" #Note the new Optimization option syntax for MIPSpro CC 7.2.1.2m. #See note on irix65gcc target about Large File Support (LFS). irix65: @echo 'Making C-Kermit $(CKVER) for SGI IRIX 6.5' @$(MAKE) "MAKE=$(MAKE)" LNKFLAGS="-Wl,-woff,84" \ "KFLAGS=-DIRIX65 -D_LARGEFILE_SOURCE -DCK_RTSCTS -OPT:Olimit=0 \ -woff 1110,1552,1174 $(KFLAGS)" \ irix6x KTARGET=$${KTARGET:-$(@)} #Build for those that have GCC instead of MIPSpro. # # Large File Support note: use the define _LARGEFILE_SOURCE to enable support # for files larger than 2GB. This may work on releases of Irix prior to # 6.5.xx. To verify, check the man page for fstat and verify that off_t is a # 64 bit value for an -n32 build. Also check the manpage for fseek and ftell # to verify that the fseek64 and ftell64 functions are provided. If so, then # LFS support should work and you can try adding -D_LARGEFILE_SOURCE to CFLAGS # for your selected Irix target. # irix65gcc: @echo 'Making C-Kermit $(CKVER) for SGI IRIX 6.5 with gcc' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CC = gcc" "CC2 = gcc" \ "CFLAGS= -DSVR4 -DIRIX65 -D_LARGEFILE_SOURCE -DDIRENT -DHDBUUCP \ -DNOGETUSERSHELL -DSELECT -DPWID_T=uid_t -DTCPSOCKET -DNOCOTFMC \ -DCK_ANSIC -DCK_RTSCTS -DCK_NEWTERM -DCK_CURSES \ $(KFLAGS) -O" "LIBS= -lcurses" # The 64-bit IRIX target works but presumably is no longer needed given the # large file support in the more portable and compact 32-bit version. irix65_64: @echo 'Making C-Kermit $(CKVER) 64-bit for SGI IRIX 6.5' @$(MAKE) "MAKE=$(MAKE)" LNKFLAGS="-Wl,-woff,84" \ "KFLAGS=-DIRIX65 -64 -DCK_RTSCTS -OPT:Olimit=0 -woff 1110,1552,1174 \ -DCK_64BIT $(KFLAGS)" \ irix6x KTARGET=$${KTARGET:-$(@)} #Dumb down to MIPS-2 if building on R5000 or higher... irix65mips2: @echo 'Making C-Kermit $(CKVER) for SGI IRIX 6.5 MIPS-2' @$(MAKE) "MAKE=$(MAKE)" LNKFLAGS="-o32 -mips2 -Wl,-woff,84" \ "KFLAGS=-DIRIX65 -DCK_RTSCTS -OPT:Olimit=0 -o32 -mips2 \ -woff 1110,1552,1174 $(KFLAGS)" \ irix6x KTARGET=$${KTARGET:-$(@)} #Special target that adds srp, ssl, and zlib support. This requires #that you have pkgsrc installed instead of Irix Freeware. See #NetBSD.org for pkgsrc for Irix. You will need to BUILD the srp_client #package yourself. Install it manually using the directions found #in the netbsds+ssl+srp+zlib target comments. irix65+ssl+srp+zlib: @echo 'Making C-Kermit $(CKVER) for IRIX 6.5 with gcc and SSL SRP ZLIB' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CC = gcc" "CC2 = gcc" \ "CFLAGS= -DIRIX65 -DSVR4 -DDIRENT -DHDBUUCP -DNOGETUSERSHELL -DSELECT \ -DTCPSOCKET -DNOCOTFMC -DCK_NEWTERM -DPWID_T=uid_t -DCK_ANSIC \ -I/usr/pkg/include -DCK_AUTHENTICATION -DCK_SRP -DPRE_SRP_1_4_5 \ -DCK_RTSCTS -DCK_NCURSES -DCK_ENCRYPTION -DCK_CAST -DCK_DES -DCK_SSL \ -DLIBDES -DZLIB -DFNFLOAT -I/usr/pkg/include/openssl $(KFLAGS) -O" \ "LIBS= -L/usr/pkg/lib -rpath /usr/pkg/lib -lncurses -lsrp -lgmp -ldes \ -lssl -lkrypto -lcrypto -lcrypt -lz -lm" irix6x+krb5: @echo 'Includes fullscreen file display and Yellow Pages...' @echo 'Add -mips to specify a particular hardware target.' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DSVR4 -DDIRENT -DHDBUUCP -DNOGETUSERSHELL \ -DCK_CURSES -DCK_NEWTERM -DPWID_T=uid_t -DCK_ANSIC -DTCPSOCKET\ -DSELECT -DCK_RTSCTS -O \ -DCK_AUTHENTICATION -DCK_KERBEROS -DKRB5 -DCK_ENCRYPTION -DCK_DES \ $(K5INC) $(K5INC)/krb5 $(KFLAGS)" \ "LIBS = -lcurses $(K5LIB) -ldes425 -lkrb5 \ -lcom_err -lcrypto -lcrypt -lgssapi_krb5" \ "LNKFLAGS = -s $(LNKFLAGS)" irix65+krb5: @echo 'Making C-Kermit $(CKVER) for SGI IRIX 6.5' @$(MAKE) "MAKE=$(MAKE)" \ LNKFLAGS="-Wl,-woff,84" \ "KFLAGS=-DIRIX65 -DCK_RTSCTS -OPT:Olimit=0 -woff 1110,1552,1174 \ $(KFLAGS)" \ irix6x+krb5 KTARGET=$${KTARGET:-$(@)} #In case they type "make sys5"... sys5: $(MAKE) "MAKE=$(MAKE)" sys3 KTARGET=$${KTARGET:-$(@)} #Generic ATT System III or System V (with I&D space) sys3: @echo 'Making C-Kermit $(CKVER) for AT&T UNIX System III' @echo 'or System V R2 or earlier...' @echo 'add -DNOMKDIR if mkdir is an undefined symbol.' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DATTSV -DNOUNICODE -DNOSYSLOG -DNOSYMLINK -DNOGETUSERSHELL \ -DNOINITGROUPS -DNOFTRUNCATE -DNOREALPATH -DNOLEARN $(KFLAGS) -i -O" \ "LNKFLAGS = -i" #Generic ATT System III or System V (no I&D space) sys3nid: @echo 'Making C-Kermit $(CKVER) for AT&T UNIX System III' @echo 'or System V R2 or earlier, no I&D space...' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DATTSV -DNOREALPATH -DNOUNICODE -DNOSYSLOG -DNOSYMLINK \ -DNOGETUSERSHELL -DNOINITGROUPS -DNOFTRUNCATE -DNOLEARN $(KFLAGS) -O" \ "LNKFLAGS =" #Generic ATT System III or System V R2 or earlier, "no void": #special entry to remove "Illegal pointer combination" warnings. sys3nv: @echo 'Making C-Kermit $(CKVER) for AT&T UNIX System III' @echo 'or System V R2 or earlier...' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DATTSV -DNOREALPATH -DNOUNICODE -DNOSYSLOG -DNOGETUSERSHELL \ -DNOSYMLINK -DNOFTRUNCATE -DNOINITGROUPS -DNOLEARN \ -Dvoid=int $(KFLAGS) -i -O" \ "LNKFLAGS = -i" # AT&T 7300 UNIX PC. As of C-Kermit 6.1, many of these entries don't work # any more due to "Out of memory" or "Too many defines" errors during # compilation, at least not on systems without lots of memory. The sys3upcgc # entry works (using gcc) with optimization removed, and might also work # with optimization enabled on machines with larger memories. #AT&T 7300/UNIX PC (3B1) systems, sys3 but special handling for internal modem. #Link with the shared library -- the conflict with openi in shared library #is solved with -Dopeni=xopeni. Note that the xermit target can't be used #for the Unix PC; there is no select(). sys3upc: @echo 'Making C-Kermit $(CKVER) for AT&T 7300 UNIX PC, shared lib...' @echo 'If shared lib causes trouble, use make sys3upcold.' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -O -DATT7300 -DNOMKDIR -DUSE_MEMCPY -DNOREALPATH -DNOLEARN \ -DNOSYSLOG -DNOSYMLINK -DNOGETUSERSHELL -DNOINITGROUPS -DNOFTRUNCATE \ -DNOREDIRECT -DNOGFTIMER -DNOUNICODE $(KFLAGS) -Dopeni=xopeni" \ "CC2 = ld /lib/crt0s.o /lib/shlib.ifile" "LNKFLAGS = -s" #AT&T 7300/Unix PC systems, minimum kermit for those with smaller amounts #of memory. sys3upcm: @echo Minimum interactive $(MAKE) "MAKE=$(MAKE)" sys3upc KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-DNOSPL -DNOFRILLS -DNOHELP -DNODEBUG -DNOTLOG -DNOCSETS \ -DNOSYSLOG -DNOSETKEY -DNOREALPATH" #AT&T 7300/UNIX PC (3B1) systems, with curses support. #Curses and the shared library don't get along, so we don't use the #shared library. We need to include CK_NEWTERM to avoid a conflict #with curses and buffering on stdout. Merged with submission by #Robert Weiner/Programming Plus, rweiner@watsun.cc.columbia.edu. #We don't need -Dopeni=xopeni since we're not using the shared library, #but we keep it to be consistent with the other entries. sys3upcc: @echo 'Making C-Kermit $(CKVER) for AT&T 7300 UNIX PC, curses...' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -O -DATT7300 -DNOREALPATH \ -DCK_CURSES -DCK_NEWTERM -DNOMKDIR -DNOREDIRECT -DNOGFTIMER -DNOLEARN \ -DNOSYSLOG -DNOSYMLINK -DNOGETUSERSHELL -DNOINITGROUPS -DNOFTRUNCATE \ -DUSE_MEMCPY -DNOUNICODE $(KFLAGS) -Dopeni=xopeni" \ "LIBS = -lcurses" "LNKFLAGS = -s" #Like sys3upcc but for AT&T UNIX 3.51m (released as a patch on Fix Disk 2), #adds hardware flow control. att351m: $(MAKE) "MAKE=$(MAKE)" sys3upcc KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-DCK_RTSCTS -DUNIX351M" #As above but with gcc. att351gm: $(MAKE) "MAKE=$(MAKE)" sys3upcgc KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-DCK_RTSCTS -DUNIX351M" #AT&T 7300 UNIX PC (3B1), as above, but no newterm(). sys3upcx: @echo 'Making C-Kermit $(CKVER) for AT&T 7300 UNIX PC, curses...' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -O -DATT7300 -DNOREALPATH -DNOUNICODE -DNOLEARN \ -DNOSYSLOG -DNOSYMLINK -DNOGETUSERSHELL -DNOINITGROUPS -DNOFTRUNCATE \ -DCK_CURSES -DNOMKDIR -DNOREDIRECT -DNOGFTIMER -DUSE_MEMCPY $(KFLAGS) \ -Dopeni=xopeni" "LIBS = -lcurses -ltermcap" "LNKFLAGS = -s" #AT&T 7300/UNIX PC (3B1) systems, with curses and shared library support. sys3upcshcc: @echo 'Making C-Kermit $(CKVER) for AT&T 7300 UNIX PC, shared lib...' @echo 'With curses. Requires shcc.' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -O -DATT7300 -DNOMKDIR -DNOREALPATH -DNOLEARN \ -DNOSYSLOG -DNOSYMLINK -DNOGETUSERSHELL -DNOINITGROUPS -DNOFTRUNCATE \ -DCK_NEWTERM -DCK_CURSES -DNOREDIRECT -DNOGFTIMER \ -DUSE_MEMCPY -DNOUNICODE $(KFLAGS) -Dopeni=xopeni" \ "LNKFLAGS = -i -s" "CC = shcc" "CC2 = shcc" "LIBS = -lcurses" #AT&T 7300/UNIX PC (3B1) systems, as above, no curses, but use gcc. sys3upcg: @echo 'Making C-Kermit $(CKVER) for AT&T 7300 UNIX PC...' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DATT7300 -DNOREDIRECT -DUSE_MEMCPY -DNOUNICODE -DNOLEARN \ -DNOSYSLOG -DNOSYMLINK -DNOGETUSERSHELL -DNOINITGROUPS -DNOFTRUNCATE \ -DNOGFTIMER -DNOMKDIR -DNOREALPATH $(KFLAGS) -Dopeni=xopeni" \ "CC = gcc" "CC2 = gcc" "LNKFLAGS = -s -shlib" #AT&T 7300/UNIX PC (3B1) systems, curses and gcc. #Optimization omitted -- add it back in if your machine has lots of memory. sys3upcgc: @echo 'Making C-Kermit $(CKVER) for AT&T 7300 UNIX PC, curses...' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DATT7300 -DNOREDIRECT -DUSE_MEMCPY -DNOGFTIMER -DNOUNICODE \ -DNOSYSLOG -DNOSYMLINK -DNOGETUSERSHELL -DNOINITGROUPS -DNOFTRUNCATE \ -DCK_CURSES -DCK_NEWTERM -DNOMKDIR -DNOREALPATH -DNOLEARN $(KFLAGS)" \ "CC = gcc" "CC2 = gcc" "LIBS = -lcurses" "LNKFLAGS = -s" #AT&T 7300/UNIX PC (3B1) systems, special handling for internal modem. #No FULLSCREEN file transfer display (curses). sys3upcold: @echo 'Making C-Kermit $(CKVER) for AT&T 7300 UNIX PC...' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DATT7300 -DNOMKDIR -DUSE_MEMCPY -DNOUNICODE -DNOLEARN \ -DNOSYSLOG -DNOSYMLINK -DNOGETUSERSHELL -DNOINITGROUPS -DNOFTRUNCATE \ -DNOGFTIMER -DNOREDIRECT -DNOREALPATH $(KFLAGS) -O" "LNKFLAGS = -i" #As above, but with gcc. mininum features - fits on a 400K UNIX PC floppy #after compression with room to spare; add -DNOSHOW or other -DNOxxxx items #to reduce size even further. sys3upcgm: @echo Minimum interactive $(MAKE) "MAKE=$(MAKE)" sys3upcg KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-DNOSPL -DNOFRILLS -DNOHELP -DNODEBUG -DNOTLOG -DNOCSETS \ -DNOSETKEY $(KFLAGS)" #This target is designed to create a version with the most features possible #that, after compression, still fits on a 400K UNIX PC floppy. sys3upcgfd: @echo 'Making C-Kermit $(CKVER) for AT&T 7300 UNIX PC floppy...' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DATT7300 -DNOREDIRECT -DUSE_MEMCPY -DNOSPL -DNOLEARN \ -DNOSYSLOG -DNOSYMLINK -DNOGETUSERSHELL -DNOINITGROUPS -DNOFTRUNCATE \ -DNOGFTIMER -DNOREALPATH -Dopeni=xopeni \ -DNOHELP -DNODEBUG -DNOTLOG -DNOCSETS -DNOSETKEY -DNOMKDIR $(KFLAGS)" \ "CC = gcc" "CC2 = gcc" "LNKFLAGS = -s" #AT&T 6300 PLUS (warning, -O might make it run out of space). #NOTE: Remove -DHDBUUCP if not using Honey DanBer UUCP. att6300: @echo 'Making C-Kermit $(CKVER) for AT&T 6300 PLUS...' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DATT6300 -DHDBUUCP -DNOFILEH -DNOREALPATH -DNOLEARN \ -DNOSYSLOG -DNOSYMLINK -DNOGETUSERSHELL -DNOINITGROUPS -DNOFTRUNCATE \ -DNOUNICODE $(KFLAGS) -O -Ml -i" "LNKFLAGS = -i -Ml" #As above, but with curses support. Debugging disabled to prevent thrashing. att6300c: @echo 'Making C-Kermit $(CKVER) for AT&T 6300 PLUS...' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DATT6300 -DHDBUUCP -DNOFILEH -DNOCSETS -DNOREALPATH \ -DNOSYSLOG -DNOSYMLINK -DNOGETUSERSHELL -DNOINITGROUPS -DNOFTRUNCATE \ -DCK_CURSES -DNODEBUG -DNOUNICODE -DNOLEARN $(KFLAGS) -O -Ml -i" \ "LNKFLAGS = -i -Ml" "LIBS = -lcurses" #AT&T 6300 PLUS with no curses, no debugging (about 34K smaller) # -Optimization saves about 20K too. att6300nd: @echo 'Making C-Kermit $(CKVER) for AT&T 6300 PLUS, no debugging...' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DATT6300 -DHDBUUCP -DNODEBUG -DNOFILEH -DNOREALPATH \ -DNOSYSLOG -DNOSYMLINK -DNOGETUSERSHELL -DNOINITGROUPS -DNOFTRUNCATE \ -DNOUNICODE -DNOLEARN $(KFLAGS) -O -i -Ml" "LNKFLAGS = -i -Ml" #AT&T 3B2 and maybe 3B20-series computers running AT&T UNIX System V R3. #This one was actually used to build C-Kermit 7.0 successfully on a 3B2/300. att3b2: @echo 'Making C-Kermit $(CKVER) for AT&T 3B2' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DATTSV -DNOREDIRECT -DUSE_MEMCPY \ -DNOTIMEVAL -DNOTIMEZONE -DMINIDIAL -DNOCHANNELIO -DNOBIGBUF \ -DNOSYSLOG -DNOSYMLINK -DNOGETUSERSHELL -DNOINITGROUPS -DNOFTRUNCATE \ -DNOGFTIMER -DNOREALPATH -Dopeni=xopeni -DNOFRILLS -DNOLEARN \ -DNOHELP -DNODEBUG -DNOTLOG -DNOCSETS -DNOSETKEY -DNOMKDIR $(KFLAGS)" \ "CC = gcc" "CC2 = gcc" "LNKFLAGS = -s" # The next two are likely not to work as-is. #AT&T 3B2, 3B20-series computers running AT&T UNIX System V. #This is just generic System V with Honey DanBer UUCP, so refer to sys3hdb. #Remove -DNONAWS if you can get away with it. att3bx: $(MAKE) "MAKE=$(MAKE)" sys3hdb KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=$(KFLAGS) -DNONAWS -DNOTIMEVAL" # 3Bx with charsets (except Unicode) but no curses. att3bx1: @echo 'Making C-Kermit $(CKVER) for AT&T 3B2 or 3B20' @echo 'with Honey DanBer UUCP no curses...' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DATTSV -DHDBUUCP $(KFLAGS) -DNOREDIRECT \ -DNOTIMEVAL -DNOTIMEZONE -DMINIDIAL -DNOCHANNELIO -DNOBIGBUF \ -DNOHELP -DNODEBUG -DNOGFTIMER -DNOLEARN \ -DNOSYSLOG -DNOSYMLINK -DNOGETUSERSHELL -DNOINITGROUPS -DNOFTRUNCATE \ -DNOREALPATH -DNOUNICODE -i" \ "CC = gcc" "CC2 = gcc" "LNKFLAGS = -i -s" #AT&T 3B2, 3B20-series computers running AT&T UNIX System V, #with fullscreen file transfer display. att3bxc: @echo 'Making C-Kermit $(CKVER) for AT&T 3B2 or 3B20' @echo 'with Honey DanBer UUCP and curses...' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DATTSV -DHDBUUCP -DNONAWS -DNOTIMEVAL $(KFLAGS) \ -DNOSYSLOG -DNOSYMLINK -DNOGETUSERSHELL -DNOINITGROUPS -DNOFTRUNCATE \ -DNOREALPATH -DCK_CURSES -DCK_NEWTERM -DNOUNICODE -DNOLEARN -i -O" \ "LNKFLAGS = -i" "LIBS=-lcurses" #3bx with curses but no charsets att3bxc3: @echo 'Making C-Kermit $(CKVER) for AT&T 3B2 or 3B20' @echo 'with Honey DanBer UUCP with curses... no CSETS' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DATTSV -DHDBUUCP $(KFLAGS) -DNOREDIRECT \ -DNOTIMEVAL -DNOTIMEZONE -DMINIDIAL -DNOCHANNELIO -DNOBIGBUF \ -DNOHELP -DNODEBUG -DNOGFTIMER -DNOLEARN \ -DNOSYSLOG -DNOSYMLINK -DNOGETUSERSHELL -DNOINITGROUPS -DNOFTRUNCATE \ -DNOREALPATH -DNOCSETS -DCK_CURSES -DCK_NEWTERM -i" \ "CC = gcc" "CC2 = gcc" "LNKFLAGS = -i -s" "LIBS = -lcurses" #Any System V R2 or earlier with Honey DanBer UUCP (same as above) sys3hdb: @echo 'Making C-Kermit $(CKVER) for AT&T UNIX System III' @echo 'or System V R2 or earlier with Honey DanBer UUCP...' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DATTSV -DHDBUUCP -DNOREALPATH -DNOUNICODE -DNOLEARN \ -DNOSYSLOG -DNOSYMLINK -DNOGETUSERSHELL -DNOINITGROUPS -DNOFTRUNCATE \ $(KFLAGS) -i -O" "LNKFLAGS = -i" #Sperry/UNISYS 5000 UTS V 5.2 (System V R2), Honey DanBer UUCP unisys5r2: @echo 'Making C-Kermit $(CKVER) for Sperry/UNISYS 5000 UTS V 5.2...' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DATTSV -DUNISYS52 -DHDBUUCP -DNOREALPATH -DNOUNICODE \ -DNOSYSLOG -DNOSYMLINK -DNOGETUSERSHELL -DNOINITGROUPS -DNOFTRUNCATE \ -DNOLEARN $(KFLAGS) -i -O" "LNKFLAGS = -i" #In case they say "make sys5hdb" instead of "make sys3hdb"... sys5hdb: $(MAKE) "MAKE=$(MAKE)" sys3hdb #Create the common header line for all hpux[5-11]* entries and above. This #extra entry is here because our header message length may differ for each #C-Kermit version. Don't use 'fold -s' for HP-UX 5.x - 7.x! This option is #available only for HP-UX 8.0 and above! hpux-header: @HPUX=`uname -r | sed -e 's/^[^1-9]*//' -e 's/\.00$$/.0/'` ; \ [ "$(MESSAGE0)" ] && MESSAGE1="$(MESSAGE0)" ; \ Message0='Making C-Kermit $(CKVER) for HP9000 HP-UX' ; \ Message1=$${MESSAGE1:='without any extra compiler optimization'} ; \ MessageH="$$Message0 $$HPUX" ; \ case $$HPUX in \ [567].*) echo "$$MessageH\n$$Message1" ;; \ *.*) echo "$$MessageH $${Message1}$(MESSAGE1A)" | fold -s ;; \ esac | sed -e 's/^ //' -e 's/ *$$//' # Peter E's updated HP-UX 5.xx entries Oct 2001. #HP-9000 500 HP-UX 5.xx, no TCP/IP. # Last known successful build: C-Kermit 8.0.206 2002/20/27. hpux0500: @MESSAGE0="no TCP/IP and no compiler optimization";\ MESSAGE0=$${MESSAGE1:-$$MESSAGE0} \ $(MAKE) hpux-header $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DHPUX -DHPUX5 -DHPUXPRE65 -DNOREDIRECT -DDCLGETCWD \ -DNOGETUSERSHELL -DNOGFTIMER -DNOSYSLOG -DNOTOMACROS -DNOLSTAT \ -DNOSYMLINK -DNOINITGROUPS -DNOUNICODE -DNOLEARN -DNOLONGLONG \ -DVOID=int -DCKVOID=int $(KFLAGS)" "LIBS = $(LIBS)" "LNKFLAGS = " #HP-9000 500 HP-UX 5.21 with Wollongong WIN/TCP 1.2 TCP/IP. #Requires /usr/wins/usr/include and /usr/lib/libnet.a from Wollongong. #Optimization skipped - takes forever. Really. # WARNING: this doesn't work if a file called "hpux0500" is on the disk. # Last known successful build: C-Kermit 8.0.206 2002/20/27. hpux0500wintcp: @MESSAGE1="with WIN/TCP but without any extra compiler optimization" \ $(MAKE) hpux0500 KTARGET=$${KTARGET:-$(@)} \ "KFLAGS = -DTCPSOCKET -DHPUX5WINTCP -DINADDRX -DNO_DNS_SRV \ -DNOMHHOST -DVOID=int -DCKVOID=int -DNOHADDRLIST -DNOLONGLONG \ -I/usr/wins/usr/include $(KFLAGS)" "LIBS = /usr/lib/libnet.a" #HP-UX 6.5, short filenames, no network and no curses support. #ckcpro, ckuusr, ckuus3 and others are broken out because they make the #optimizer run away. Note that the XERMIT target does not work with HP-UX 6.5! # #If you get compiler warnings like: #'Switch table overflow. Try the -Wc,-Nw option.' (for ckcuni.c, or #other files) increase the '...' value in '-Wc,-Nw...'! The default maximum #switch table stack (-Nw) is 250 table entries. ckcuni.c from Oct 16 2009 #needs 257 table entries (C-Kermit Version "9.0.299"). #OK: 2010/03/26 hpux0650: @$(MAKE) hpux-header @MESSAGE2=$${MESSAGE2:-'and NO network'}; \ echo "supporting: NO long filenames $$MESSAGE2." $(MAKE) KTARGET=$${KTARGET:-$(@)} \ ckuus3.$(EXT) ckuus4.$(EXT) ckuus5.$(EXT) ckuus6.$(EXT) \ ckuusr.$(EXT) ckuxla.$(EXT) ckcftp.$(EXT) ckcpro.$(EXT) \ "CFLAGS = -DHPUX -DHPUX6 -DSIG_V -DNOSYSLOG -DNOSELECT -DFNFLOAT \ -DDCLGETCWD -DNOGETUSERSHELL -DNO_DNS_SRV -DNOLEARN -DNOLONGLONG \ $(KFLAGS)" $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DHPUX -DHPUX6 -DSIG_V -DNOSYSLOG -DNOSELECT -DFNFLOAT \ -DDCLGETCWD -DNOGETUSERSHELL -DNO_DNS_SRV -DNOLEARN -DNOLONGLONG \ $(KFLAGS) -Wc,-Nw260 $(OFLAGS)" "LNKFLAGS = -s" "LIBS = -lm $(LIBS)" #Exactly as above, plus curses: #OK: 2009/10/06 hpux0650c: @MESSAGE2="and NO network but with curses" \ $(MAKE) hpux0650 KTARGET=$${KTARGET:-$(@)} \ "KFLAGS = -DCK_CURSES $(KFLAGS)" \ "LIBS = -lcurses" #Exactly as above, plus curses + network: #OK: 2009/10/02 hpux0650tcpc: @MESSAGE2="but with curses and with TCP/IP" \ $(MAKE) hpux0650 KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-DCK_CURSES -DTCPSOCKET -DNOHADDRLIST \ -DINTSELECT -DNOCKGETFQHOST $(KFLAGS)" \ "LIBS=-lcurses" #Exactly as hpux0650 but with compiler optimization: #OK: 2009/10/06 hpux0650o: @MESSAGE1="with compiler optimization" \ $(MAKE) hpux0650 KTARGET=$${KTARGET:-$(@)} \ "KFLAGS = $(KFLAGS)" "OFLAGS = -O" #Exactly as hpux0650c but with compiler optimization: #OK: 2009/10/06 hpux0650oc: @MESSAGE1="with compiler optimization" \ $(MAKE) hpux0650c KTARGET=$${KTARGET:-$(@)} \ "KFLAGS = $(KFLAGS)" "OFLAGS = -O" #Exactly as hpux0650tcpc but with compiler optimization: #OK: 2009/10/06 hpux0650otcpc: @MESSAGE1="with compiler optimization" \ $(MAKE) hpux0650tcpc KTARGET=$${KTARGET:-$(@)} \ "KFLAGS = $(KFLAGS)" "OFLAGS = -O" #Take this as startup entry for all 'non-optimized' files under HP-UX 7.x! #Make sure we don't call it with the '-O' option because this will blow up #the compiler! #OK: 2009/09/30 hpux0700noopt: @case "$(CFLAGS)" in \ *-O*) echo "Don't use CFLAGS= -O here!" ;; \ *) $(MAKE) KTARGET=$${KTARGET:-$(@)} \ ckuusr.$(EXT) ckuus3.$(EXT) ckuus4.$(EXT) ckuus5.$(EXT) \ ckuus6.$(EXT) ckuus7.$(EXT) ckuxla.$(EXT) \ ckcuni.$(EXT) ckcftp.$(EXT) ckcpro.$(EXT) \ ;; \ esac #HP-UX 7.0, no long filenames, no network support, no curses. #If you get compiler warnings like: #'Switch table overflow. Try the -Wc,-Nw option.' (for ckcuni.c, or #other files) increase the '...' value in '-Wc,-Nw...'! The default maximum #switch table stack (-Nw) is 250 table entries. ckcuni.c from Oct 16 2009 #needs 257 table entries (C-Kermit Version "9.0.299"). #OK: 2010/10/26 hpux0700sf: @$(MAKE) hpux-header @echo 'supporting: NO long filenames, NO network, NO curses.' $(MAKE) hpux0700noopt KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DHPUX -DHPUX7 -DSIG_V -DNOGETUSERSHELL -DFNFLOAT \ -DNO_DNS_SRV $(KFLAGS) -Wc,-Nw260" $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DHPUX -DHPUX7 -DSIG_V -DNOGETUSERSHELL -DFNFLOAT \ -DNO_DNS_SRV $(KFLAGS) -Wc,-Nw260 $(OFLAGS)" \ "LNKFLAGS = -s" "LIBS = -lm $(LIBS)" #Exactly as hpux0700sf but with compiler optimization: #OK: 2009/09/30 hpux0700osf: @MESSAGE1="with compiler optimization" \ $(MAKE) hpux0700sf KTARGET=$${KTARGET:-$(@)} \ "KFLAGS = $(KFLAGS)" "OFLAGS = -O" #HP-UX 7.0, short filenames, but with tcp/ip and curses. #To use this, you must have bought the ARPA Services Product from HP, and you #must have /usr/lib/libBSD.a. # #If you get compiler warnings like: #'Symbol table overflow. Try the -Wc,-Ns option.' (as for ckuus4.c or #other files) increase the '...' value in '-Wc,-Ns...'! The default maximum #symbol table size (-Ns) is 2000 table entries. ckuus4.c from Mar 12 2010 #needs 2031 table entries (C-Kermit Version "9.0.299"). #OK: 2010/03/24 hpux0700sftcpc: @$(MAKE) hpux-header @echo 'supporting: NO long filenames, \c' @echo 'but with networking, curses, HDB uucp...' $(MAKE) hpux0700noopt KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DHPUXDEBUG -DHPUX -DHPUX7 -DTCPSOCKET -DSIG_V \ -DCK_REDIR -DCK_RTSCTS -DCK_CURSES -DNOGETUSERSHELL -DFNFLOAT \ -DNO_DNS_SRV -DHDBUUCP -DLOCK_DIR=\\\"/usr/spool/uucp\\\" \ -DNOLONGLONG $(KFLAGS) -Wc,-Nw260,-Ns2040" $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DHPUXDEBUG -DHPUX -DHPUX7 -DTCPSOCKET -DSIG_V \ -DCK_REDIR -DCK_RTSCTS -DCK_CURSES -DNOGETUSERSHELL -DFNFLOAT \ -DNO_DNS_SRV -DHDBUUCP -DLOCK_DIR=\\\"/usr/spool/uucp\\\" \ -DNOLONGLONG $(KFLAGS) -Wc,-Nw260,-Ns2040 $(OFLAGS)" \ "LNKFLAGS = -s" "LIBS = -lm -lBSD -lcurses" #Exactly as above but with compiler optimization: #OK: 2009/09/30 hpux0700osftcpc: @MESSAGE1="with compiler optimization" \ $(MAKE) hpux0700sftcpc KTARGET=$${KTARGET:-$(@)} \ "KFLAGS = $(KFLAGS)" "OFLAGS = -O" #HP 9000 series 300/800 HP-UX 7.0, long filenames, network support, HDB uucp, #but NO curses. See comments in hpux0700sftcpc about TCP/IP support. # #If you get compiler warnings like: #'Symbol table overflow. Try the -Wc,-Ns option.' (as for ckuus4.c or #other files) increase the '...' value in '-Wc,-Ns...'! The default maximum #symbol table size (-Ns) is 2000 table entries. ckuus4.c from Mar 12 2010 #needs 2031 table entries (C-Kermit Version "9.0.299"). #OK: 2010/03/24 hpux0700lfn: @$(MAKE) hpux-header @echo 'supporting: long filenames, networking, HDB uucp$(MESSAGE2)...' $(MAKE) hpux0700noopt KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DHPUXDEBUG -DHPUX -DHPUX7 -DTCPSOCKET -DSIG_V -DFNFLOAT \ -DNOGETUSERSHELL -DNOSETBUF -DCK_REDIR -DCK_RTSCTS -DLONGFN \ -DNO_DNS_SRV -DDIRENT -DHDBUUCP -DLOCK_DIR=\\\"/usr/spool/uucp\\\" \ -DNOLONGLONG $(KFLAGS) -Wc,-Nw260,-Ns2040" $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DHPUXDEBUG -DHPUX -DHPUX7 -DTCPSOCKET -DSIG_V -DFNFLOAT \ -DNOGETUSERSHELL -DNOSETBUF -DCK_REDIR -DCK_RTSCTS -DLONGFN \ -DNO_DNS_SRV -DDIRENT -DHDBUUCP -DLOCK_DIR=\\\"/usr/spool/uucp\\\" \ -DNOLONGLONG $(KFLAGS) -Wc,-Nw260,-Ns2040 $(OFLAGS)" \ "LNKFLAGS = -s" "LIBS = -lm -lBSD $(LIBS)" #Exactly as above + curses. #OK: 2009/09/30 hpux0700lfnc: @MESSAGE2=', curses' \ $(MAKE) hpux0700lfn KTARGET=$${KTARGET:-$(@)} \ "KFLAGS = -DCK_CURSES $(KFLAGS)" \ "LIBS = -lcurses" #Exactly as above hpux0700lfn but with compiler optimization: #OK: 2009/09/30 hpux0700olfn: @MESSAGE1="with compiler optimization" \ $(MAKE) hpux0700lfn KTARGET=$${KTARGET:-$(@)} \ "KFLAGS = $(KFLAGS)" "OFLAGS = -O" #Exactly as above hpux0700lfnc but with compiler optimization: #OK: 2009/09/30 hpux0700olfnc: @MESSAGE1="with compiler optimization" \ $(MAKE) hpux0700lfnc KTARGET=$${KTARGET:-$(@)} \ "KFLAGS = $(KFLAGS)" "OFLAGS = -O" #HP 9000 Series 300 or 400, HP-UX 8.0, long filenames and TCP/IP support. #This one should also work on 700/800, but without PA-specific optimization. #In case -DCK_RTSCTS and -DCK_REDIR make trouble, remove them. #NOTE: ckcpro.c, ckuusr.c and ckuus3.c blow up the optimizer, so don't optimize #them. #For HP-UX 8.0 on Motorola CPUs, you might have to reinstall your kernel with #maxdsiz >= 0x03000000. But if physical memory is small, that still will not #help much. #OK: 2009/10/01 hpux0800: @$(MAKE) hpux-header @MESSAGE3=$${MESSAGE3:='TCP/IP'}; \ echo "supporting: long filenames, $$MESSAGE3, HDB UUCP$(MESSAGE2)..." $(MAKE) -B "CC=$(CC)" "CC2=$(CC2)" KTARGET=$${KTARGET:-$(@)} \ ckcpro.$(EXT) ckuusr.$(EXT) ckuus3.$(EXT) \ "CFLAGS = -DCK_REDIR -DHPUXDEBUG -DHPUX -DHPUX8 -DRENAME -DSIG_V \ -DNOSETBUF -DDIRENT -DCK_RTSCTS -DSTERMIOX -DLONGFN -DTCPSOCKET \ -DHDBUUCP -DNO_DNS_SRV -DLOCK_DIR=\\\"/usr/spool/uucp\\\" -DFNFLOAT \ -DNOLONGLONG $(KFLAGS)" $(MAKE) -B "CC=$(CC)" "CC2=$(CC2)" xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DCK_REDIR -DHPUXDEBUG -DHPUX -DHPUX8 -DRENAME -DSIG_V \ -DNOSETBUF -DDIRENT -DCK_RTSCTS -DSTERMIOX -DLONGFN -DTCPSOCKET \ -DHDBUUCP -DNO_DNS_SRV -DLOCK_DIR=\\\"/usr/spool/uucp\\\" -DFNFLOAT \ -DNOLONGLONG -DNODCLENDUSERSHELL $(KFLAGS) $(OFLAGS)" \ "LNKFLAGS = -s" "LIBS = -lm -lBSD $(LIBS)" #Exactly as above hpux0800 + curses. #OK: 2009/10/01 hpux0800c: @MESSAGE2=', curses' \ $(MAKE) hpux0800 KTARGET=$${KTARGET:-$(@)} \ "KFLAGS = $(KFLAGS) -DCK_CURSES" "LIBS = -lcurses" #HP 9000 HP-UX 8.0, no TCP/IP because /usr/lib/libBSD.a can't be found, #or TCP/IP header files missing. #OK: 2009/10/01 hpux0800notcp: @MESSAGE3='NO network, NO curses' \ $(MAKE) "MAKE=$(MAKE)" hpux0800 KTARGET=$${KTARGET:-$(@)} \ "KFLAGS = $(KFLAGS) -UTCPSOCKET" #Now the same as above hpux0800 but with compiler optimization #OK: 2009/10/01 hpux0800o: @MESSAGE1="with compiler optimization" \ $(MAKE) hpux0800 KTARGET=$${KTARGET:-$(@)} \ "KFLAGS = $(KFLAGS)" "OFLAGS = -O" #Exactly as above hpux0800 + curses and with compiler optimization. #OK: 2009/10/01 hpux0800oc: @MESSAGE1="with compiler optimization" \ $(MAKE) hpux0800c KTARGET=$${KTARGET:-$(@)} \ "KFLAGS = $(KFLAGS)" "OFLAGS = -O" "LIBS = -lcurses" #Exactly as above hpux0800notcp but with compiler optimization #OK: 2009/10/01 hpux0800onotcp: @MESSAGE1="with compiler optimization" \ $(MAKE) "MAKE=$(MAKE)" hpux0800notcp KTARGET=$${KTARGET:-$(@)} \ "KFLAGS = $(KFLAGS)" "OFLAGS = -O" #HP 9000 Series 700 or 800, HP-UX 8.0, long filenames and TCP/IP support. # Like the previous entries, but with PA-RISC-specific optimization. #OK: 2009/10/01 hpux0800pa: @MESSAGE1="with PA-RISC-specific optimization" \ $(MAKE) hpux0800 KTARGET=$${KTARGET:-$(@)} \ "KFLAGS = $(KFLAGS) +Obb1100" #As above, but with curses. #OK: 2009/10/01 hpux0800pac: @MESSAGE1="with PA-RISC-specific optimization" \ $(MAKE) hpux0800c KTARGET=$${KTARGET:-$(@)} \ "KFLAGS = $(KFLAGS) +Obb1100" #As above, but compiled with GCC 2.3.3. #OK: 2009/10/01 hpux0800pagcc: @MESSAGE1='using the gcc compiler' \ $(MAKE) hpux0800 KTARGET=$${KTARGET:-$(@)} \ "CC=gcc" "CC2=gcc" "KFLAGS = -funsigned-char $(KFLAGS)" #HP-UX 9.0, 9.01, 9.03, 9.04, 9.05, 9.07, 9.10 ..., + TCP/IP + curses, fully #configured. Use this entry with the restricted compiler: no optimization, no #ANSI support. If you get unresolved sockets library references at link time, #then try adding -lBSD to LIBS, or else remove -DTCPSOCKET to build a version #without TCP/IP support. # #Please note that we have to add the compiler option +DA1.0/+DA1.1 to avoid #core-dumps for large arguments in IF MATCH. The man page says these options #are default but C-Kermit dumps core without them! Therefore keep them #untouched. If you want to overwrite or disable the +DA1.0/+DA1.1 option use #'make hpux0900 OFLAGS=...'. An other possibility would be to create a new #kernel with maxssiz >= 0x01185000 (default maxssiz=0x00800000). #OK: 2009/09/24 hpux0900: @MESSAGE1A='. Read hpux0900 entry comments if you have trouble.' \ $(MAKE) hpux-header @case `uname -m` in \ */[34]*) KFLAGS='-DNOLONGLONG $(KFLAGS)' ;; \ */7*) AFLAGS='+DA1.1' ;; \ */8*) AFLAGS='+DA1.0' ;; \ esac ; \ OFLAGS=$${OFLAGS:-$$AFLAGS} ; \ $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DHPUXDEBUG -DHPUX9 -DSTERMIOX -DDIRENT -DUTIMEH \ -DNOSETBUF -DCK_CURSES -DTCPSOCKET -DRENAME -DCK_REDIR -DLONGFN \ -DHDBUUCP -DLOCK_DIR=\\\"/usr/spool/uucp\\\" -DFNFLOAT \ -DNODCLENDUSERSHELL $$KFLAGS $$OFLAGS" \ "LNKFLAGS = -s" "LIBS = -lm -lcurses" "CC=$(CC)" "CC2=$(CC2)" #Like hpux0900, but for the "value-added" compiler on all HP 9000 models. #Adds optimization and ANSI compilation: # +O2 makes smaller executable (= -O = Level-1 and global optimization) # +O3 adds interprocedural global optimization, makes bigger executable. # Please note: To support long-long we would need compiler switch '-Ae' but # this one works only on Risc systems. But the equivalant compiler flags # '-Aa -D_HPUX_SOURCE +e' works for Motorola and Risc. # If optimization fails on some modules, you can add: # +Obb, +Olimit , or +Onolimit, depending on your cc version, # where is a number, e.g. +Obb1200. In other words, if you get optimizer # warnings, add (for example) +Obb1200; if you still get optimizer warnings, # increase the number. Repeat until warnings go away. If your compiler # permits it, use +Onolimit. If optimizer blows up on ckcpro.c, see next entry. # Reportedly, on some configurations, such as HP9000/425e or /340, perhaps # depending on the amount of main memory, this entry might fail no matter what # you do ("Out of Memory", "cc: Fatal error in /lib/c.c1", etc). In that case # use "make hpux0900" (no "o"). #OK: 2009/09/24 hpux0900o: @MESSAGE1=$${MESSAGE1:-"with compiler optimization"} \ $(MAKE) hpux0900 KTARGET=$${KTARGET:-$(@)} \ "KFLAGS = $(KFLAGS) -Aa -DCK_ANSIC -D_HPUX_SOURCE +O2 +e" # For HP-UX 9.0 on Motorola CPUs, optimization of ckcpro.c tends to blow up # the compiler. You might have to reinstall your kernel with maxdsiz >= # 0x03000000. But if physical memory is small, that still will not help much. # In that case, use this entry to skip optimization of ckcpro.c. But for # C-Kermit 8.0.208 you need a kernel with maxdsiz >= 0x02000000 to compile an # optimized ckcftp.c. # Please note: To support long-long we would need compiler switch '-Ae' but # this one works only on Risc systems. But the equivalant compiler flags # '-Aa -D_HPUX_SOURCE +e' works for Motorola and Risc. hpux0900m68ko: @MESSAGE1='without compiler optimization for ckcpro.$(EXT) ...' \ $(MAKE) hpux-header $(MAKE) ckuusr.$(EXT) ckuus3.$(EXT) ckuus4.$(EXT) \ ckcftp.$(EXT) ckcpro.$(EXT) \ "CFLAGS = -DHPUXDEBUG -DHPUX9 -DSTERMIOX -DDIRENT \ -DNOSETBUF -DCK_CURSES -DTCPSOCKET -DRENAME -DCK_REDIR -DLONGFN \ -DHDBUUCP -DLOCK_DIR=\\\"/usr/spool/uucp\\\" -DFNFLOAT $(KFLAGS)" @echo @MESSAGE1="with compiler optimization for the rest" \ $(MAKE) hpux0900 KTARGET=$${KTARGET:-$(@)} \ "KFLAGS = $(KFLAGS) -Aa -DCK_ANSIC -D_HPUX_SOURCE +O2 +e" # Old name for hpux0900m68ko. hpux0900mot: $(MAKE) hpux0900m68ko KTARGET=$${KTARGET:-$(@)} "KFLAGS = $(KFLAGS)" #Like hpux0900o but with additional model-700/800-specific optimizations. # +ESlit = consolidate strings in read-only memory. # +ESfsc = inline millicode calls when comparing pointers. hpux0900o700: @echo 'If you get optimizer warnings \c' @echo 'try "make hpux0900o700 KFLAGS=+Obb1200"' @MESSAGE1="with PA-RISC-specific optimizations" \ $(MAKE) hpux0900o KTARGET=$${KTARGET:-$(@)} \ "KFLAGS = $(KFLAGS) +ESlit +ESsfc" #HP-UX 9.0, 9.01, 9.03, 9.04, 9.05, 9.07, 9.10 ..., + TCP/IP + curses, fully #configured, built with gcc, all models except 800 series. #You might need to add the include path for gcc headers, for example: # 'KFLAGS=-I/usr/gnu/lib/gcc-lib/hppa1.1-hp-hpux/2.4.5/include/' hpux0900gcc: @MESSAGE1='using the gcc compiler' \ $(MAKE) hpux0900 KTARGET=$${KTARGET:-$(@)} CC=gcc CC2=gcc \ "KFLAGS = -DCK_ANSIC $(KFLAGS)" \ "OFLAGS = -funsigned-char -O2 $(OFLAGS)" #HP-9000 HP-UX 10.0 + TCP/IP + curses, fully configured. #Use with restricted (bundled) compiler: no optimization, no ANSI support. #libcurses needed for fullscreen file xfer display in HP-UX 10.00 and 10.01. #libHcurses (NOT libcurses!) for fullscreen display, to work around fatal bugs #in HP-UX 10.10 and 10.20 curses. Maybe we could use lcurses for 10.30, since #the 10.10 curses problem is supposedly fixed in 10.30. # +DA1.0 = Generate PA-RISC 1.0 code that runs on both 700 and 800 models. # +DA1.1 = Generate PA-RISC 1.1 code that runs on both 700 and 800 models. # Note that HP-UX 10.20 and upwards do not support PA-RISC 1.0 systems. # And that as of Dec 2001, 11.00 and 11.11 are PA-only and 11.20 is IA64-only. # Later 11.2x releases are expected to be for both. Architecture can be # determined with the model command, at least in 10.20 and later... #For future releases, we need to include +DA1.1 for PA builds, so that a #binary built on PA 2.0 will still work on PA 1.1 machines, whereas +DA1.1 #must NOT be included for IA64 builds. #4 Jan 2006 - Added Large File Support (LFS). Large files (>2GB) are #possible in HP-UX 10.20 and later. The only change is to add: # -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 #to KFLAGS. These should be harmless in 10.00 and 10.10, if any of examples #of those still exist, but I have no way to test this hypothesis. #OK: 2009/11/16 hpux1000: @$(MAKE) hpux-header @LIBS='-lHcurses' ; \ AFLAGS='+DA1.1' ; \ case `uname -r` in \ [AB].10.0*) KFLAGS='-DHPUX1000 $(KFLAGS)' ; \ AFLAGS='+DA1.0' ; LIBS='-lcurses' ;; \ [AB].10.1*) KFLAGS='-DHPUX1010 -D__HP_CURSES $(KFLAGS)' ; \ ;; \ [AB].10.2*) KFLAGS='-DHPUX1020 -D__HP_CURSES $(KFLAGS)' ; \ ;; \ [AB].10.3*) KFLAGS='-DHPUX1030 -D__HP_CURSES $(KFLAGS)' ; \ ;; \ [AB].10.?*) KFLAGS='-DHPUX10XX -D__HP_CURSES $(KFLAGS)' ; \ ;; \ [AB].11.0*) KFLAGS='-DHPUX1100 -D__HP_CURSES $(KFLAGS)' ; \ ;; \ [AB].11.1*) KFLAGS='-DHPUX1100 -D__HP_CURSES $(KFLAGS)' ; \ ;; \ [AB].11.?*) KFLAGS='-DHPUX1100 -D__HP_CURSES $(KFLAGS)' ; \ AFLAGS='' ; LIBS='-lcurses' ;; \ esac ; \ OFLAGS=$${OFLAGS:-$$AFLAGS} ; \ $(MAKE) "SHELL=/usr/bin/sh" xermit KTARGET=$${KTARGET:-$(@)} \ "CC=$(CC)" "CC2=$(CC2)" \ "CFLAGS = -DHPUX10 -DDIRENT -DSTERMIOX -DCK_DSYSINI -DHDBUUCP \ -DCK_CURSES -DCK_WREFRESH -DTCPSOCKET -DCK_REDIR -DRENAME -DFNFLOAT \ -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 \ $$KFLAGS $$OFLAGS" \ "LNKFLAGS=-s $(LNKFLAGS)" "LIBS = -lm $$LIBS $(KLIBS)" # This is a kludge, copying hpux0900gcc and adapting hpux1000 # (add CC and CC2, drop the A1.[0||1]) # Builds w/ no compiler warnings but minimally tested. # #OK: 2009/09/21 hpux1000gcc: @MESSAGE1="using the gcc compiler $(MESSAGE1)" \ $(MAKE) hpux1000 KTARGET=$${KTARGET:-$(@)} CC=gcc CC2=gcc \ "KFLAGS = $(KFLAGS)" "OFLAGS = -DCK_ANSIC -funsigned-char -O2" # Trusted HP-UX 10 # echo KFLAGS=$(KFLAGS) YTARGET YTARGET=$(YTARGET) $(XTARGET) ; hpux1000t: @case "$(KTARGET)" in \ *+openssl | *+ssl) \ KENTRY=hpux1000o+openssl ;; \ *gcc) \ KENTRY=hpux1000gcc ;; \ *o+) KENTRY=hpux1000o+ ;; \ *o) KENTRY=hpux1000o ;; \ *) KENTRY=hpux1000 ;; \ esac ; \ MESSAGE1="and support for 'Trusted HP-UX'" \ $(MAKE) $$KENTRY KTARGET=$${KTARGET:-$(@)} \ "KFLAGS = $(KFLAGS) -DHPUX10_TRUSTED" "KLIBS=-lsec" hpux1000to: $(MAKE) hpux1000t KTARGET=$${KTARGET:-$(@)} hpux1000to+: $(MAKE) hpux1000t KTARGET=$${KTARGET:-$(@)} hpux1000tgcc: $(MAKE) hpux1000t KTARGET=$${KTARGET:-$(@)} hpux1000to+ssl hpux1000to+openssl: $(MAKE) hpux1000t KTARGET=$${KTARGET:-$(@)} hpux1000tgcc+ssl hpux1000tgcc+openssl: $(MAKE) hpux1000t KTARGET=$${KTARGET:-$(@)} #HP-9000 HP-UX 10.00 and higher with ANSI prototyping and optimization. #PA-RISC only, no Motorola or other hardware is support in HP-UX 10.00++. #The unbundled optional compiler is required. #Your path should start with /opt/ansic/bin. # -Wl,-Fw = Remove stack unwind table (info used by debuggers). # +O2 makes a smaller executable (= -O = Level-1 and global optimization). # +O3 adds interprocedural global optimization, makes a bigger executable. # +Onolimit allows all modules to be optimized, no matter how complex. But: # (a) +Onolimit does not seem to always be there in HP-UX 10.00, and: # (b) some modules might take hours on low-memory and/or slow systems. # The following are PA-RISC-specific optimizations: # +ESlit = Consolidate strings in read-only memory. # +ESfsc = Inline millicode calls when comparing pointers. # You might need to configure your kernel for a maxdsiz of 0x0B000000 (176MB) # or greater to prevent the optimizer from running out of space. # December 2001: +ESlit +ESsfc removed because not supported on IA64. # Somebody who cares can use 'model' to see whether it's PA-RISC or IA64 # and include the architecture-specific optimization flags. Also note: # +DA1.1 is PA-only. If this is included in in HP-UX 11.00 or later, # then +DS2.0 should be included too (but don't use +DS2.0 without +DA1.1, # or else the binary won't run on older PA hardware). #OK: 2009/09/21 hpux1000o: @case `uname -m` in \ ia64) ;; \ *) MFLAGS='+ESlit +ESsfc' ;; \ esac ; \ MESSAGE1="with PA-RISC-specific optimizations $(MESSAGE1)" \ $(MAKE) "SHELL=/usr/bin/sh" "PATH=/opt/ansic/bin:$$PATH" hpux1000 \ KTARGET=$${KTARGET:-$(@)} "KFLAGS = $(KFLAGS) \ -Ae -D_HPUX_SOURCE -DCK_ANSIC -DUTIMEH \ +O2 -Wl,-Fw $$MFLAGS" #Like hpux1000o but with "+Onolimit". #On 700 series set kernel parameter maxdsiz >= 0x0D000000 (=208MB). #Takes a long time. hpux1000o+: @MESSAGE1="and +Onolimit $(MESSAGE1)" KTARGET=$${KTARGET:-$(@)} \ $(MAKE) hpux1000o \ "KFLAGS = $(KFLAGS) +Onolimit" #HP-UX 10.xx + 11.xx with optimizing ANSI compiler and OpenSSL. #Define SSLLIB and SSLINC appropriately for your OpenSSL installation. #To overwrite the default SSLLIB and SSLINC settings you can also use the #command-line variable KSSLLIB and KSSLINC like: #make hpux1000o+openssl KSSLLIB=-L/opt/openssl/lib KSSLINC=-I/... #Ditto for the Zlib location. #This entry works for C-Kermit 8.0.206 on HP-UX 10.20 + 11.11 #with OpenSSL 0.9.6 + 0.9.7 #NOTE: an ANSI C compiler is required for the SSL interface. If you don't #have the HP Optimizing ANSI compiler, see the hpux1000gcc+openssl target #below. hpux1000o+ssl hpux1000o+openssl: @case "$(KTARGET)" in \ *gcc+*) \ KENTRY=hpux1000gcc ;; \ *) KENTRY=hpux1000o ;; \ esac ; \ case "$(KTARGET)" in \ *-zlib*) \ DZLIB= LZLIB= ;; \ *) DZLIB=-DZLIB LZLIB='-L/opt/zlib/lib -lz' ;; \ esac ; \ SSLINC=$${KSSLINC:-$(SSLINC)}; \ SSLLIB=$${KSSLLIB:-$(SSLLIB)}; \ MESSAGE1="and with OpenSSL $(MESSAGE1)" \ $(MAKE) $$KENTRY KTARGET=$${KTARGET:-$(@)} \ KFLAGS="-DCK_AUTHENTICATION -DCK_SSL -DOPENSSL_097 $$DZLIB \ $$SSLINC $(KFLAGS)" \ KLIBS="$(KLIBS) \ $$SSLLIB -lssl -lcrypto \ $$LZLIB \ " # Ditto but without Zlib: hpux1000o+ssl-zlib hpux1000o+openssl-zlib: @MESSAGE1="but without Zlib $(MESSAGE1)" \ $(MAKE) hpux1000o+ssl KTARGET=$${KTARGET:-$(@)} #HP-UX 10.00 or higher with OpenSSL 0.9.7. Compiled with gcc. #From Chris Chaney, NEC America Inc. His instructions: # (1) Install gcc version 3.2.3 & binutils version 2.13.2 # (used binary depot from http://hpux.cs.utah.edu/) # (2) Install gcc make version 3.80 from http://hpux.cs.utah.edu/ # # or: gcc 2.9.2000-12-1 from "Linux to hp-ux 11.0/11i porting kit version 1.0 # (2CD)" free from: http://www.software.hp.com # # (3) Install openSSL version 0.9.7b from http://www.software.hp.com # (4) Install flex version 2.5.4 from http://hpux.cs.utah.edu/ # (5) Install gmp version 3.1.1 from http://hpux.cs.utah.edu/ # #Note from Peter Eichhorn, assyst Munich. It works also without gcc make! hpux1000gcc+ssl hpux1000gcc+openssl: $(MAKE) hpux1000o+openssl KTARGET=$${KTARGET:-$(@)} # Ditto but without Zlib: hpux1000gcc+ssl-zlib hpux1000gcc+openssl-zlib: $(MAKE) hpux1000o+openssl-zlib KTARGET=$${KTARGET:-$(@)} # Same for HP-UX 11 hpux1100o+ssl hpux1100o+openssl: $(MAKE) hpux1000o+openssl KTARGET=$${KTARGET:-$(@)} #OK: 2009/09/26 hpux1100gcc+ssl hpux1100gcc+openssl: $(MAKE) hpux1000gcc+openssl KTARGET=$${KTARGET:-$(@)} hpux1100o+ssl-zlib hpux1100o+openssl-zlib: $(MAKE) hpux1000o+openssl-zlib KTARGET=$${KTARGET:-$(@)} hpux1100gcc+ssl-zlib hpux1100gcc+openssl-zlib: $(MAKE) hpux1000gcc+openssl-zlib KTARGET=$${KTARGET:-$(@)} # HP-UX 11 # Note: these are 32-bit builds even on IA64. # Adding +DD64 to CFLAGS produces 64-bit object files, # but the linker fails to find the needed 64-bit libs. #OK: 2009/09/26 hpux1100: $(MAKE) hpux1000 KTARGET=$${KTARGET:-$(@)} #OK: 2009/09/26 hpux1100o: $(MAKE) hpux1000o KTARGET=$${KTARGET:-$(@)} hpux1100o+: $(MAKE) hpux1000o+ KTARGET=$${KTARGET:-$(@)} #OK: 2009/09/26 hpux1100gcc: $(MAKE) hpux1000gcc KTARGET=$${KTARGET:-$(@)} # Trusted HP-UX 11 hpux1100t: $(MAKE) hpux1000t KTARGET=$${KTARGET:-$(@)} hpux1100to: $(MAKE) hpux1000to KTARGET=$${KTARGET:-$(@)} hpux1100to+: $(MAKE) hpux1000to+ KTARGET=$${KTARGET:-$(@)} hpux1100tgcc: $(MAKE) hpux1000tgcc KTARGET=$${KTARGET:-$(@)} hpux1100to+ssl hpux1100to+openssl: $(MAKE) hpux1000to+openssl KTARGET=$${KTARGET:-$(@)} hpux1100tgcc+ssl hpux1100tgcc+openssl: $(MAKE) hpux1000tgcc+openssl KTARGET=$${KTARGET:-$(@)} #Regulus on CIE Systems 680/20 cie: @echo 'Making C-Kermit $(CKVER) for CIE Systems 680/20 Regulus...' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DATTSV -DNOFILEH -DCIE -DNOLEARN $(KFLAGS) -O" "LNKFLAGS =" # Linux 1.2 or later with gcc, dynamic libraries, ncurses, TCP/IP. # # If your Linux system has curses rather than ncurses, use the linuxc # entry, or if that doesn't work, linuxnc. # # The Kermit "large memory model" is used by default to configure big packet # and script buffers, etc. For small-memory or limited-resource systems, # "make linux KFLAGS=-DNOBIGBUF". # # -DLINUXFSSTND (Linux File System Standard 1.2) gives UUCP lockfile /var/lock # with string pid. Remove this to get /usr/spool/uucp with int pid, used in # very early Linux versions. FSSTND 1.2 also says that the PID string in the # UUCP lock file has leading spaces. This is a change from FSSTND 1.0, which # used leading zeros. Add -DFSSTND10 to support FSSTND 1.0 instead of 1.2. # I hope subsequent editions of the file-system standard did not change these # again. # # Add -DOLINUXHISPEED (Old Linux High Speed support) to turn on an ugly kludge # in Linux 1.0 and earlier to support speeds of 57600 and 115200. Extremely # old Linux systems (pre-0.99pl15) will not support this. If OLINUXHISPEED is # not defined, then only the standard POSIX termios methods of setting the port # speed will be used, and in this case speeds can be as high as 460800 in most # modern Linux versions. # # -DCK_POSIX_SIG (POSIX signal handling) is good for Linux releases back to at # least 0.99.14; if it causes trouble for you, remove it from the CFLAGS. # # -pipe removes the need for temp files - remove it if it causes trouble. # # -funsigned-char makes all characters unsigned, as they should have been # in the first place. # # Add -DCK_DSYSINI if you want a shared system-wide init file. # # See http://www.columbia.edu/kermit/ckubwr.html about -DNOCOTFMC. # Better still, should read the entire Linux section of that document. # # The "linuxa" entry can be referenced directly on LIBC systems, but not # GLIBC, where -lcrypt is required. The "make linux" entry should normally # be used for all builds on all Linux distributions unless you have special # requirements, in which case keep reading. CK_NEWTERM added after 7.0b04 # due to new complaints about ncurses changing buffering of tty. linuxa: @echo 'Making C-Kermit $(CKVER) for Linux 1.2 or later...' @echo 'IMPORTANT: Read the comments in the linux section of the' @echo 'makefile if you have trouble.' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} "CC = gcc" "CC2 = gcc" \ "CFLAGS = -O -DLINUX -pipe -funsigned-char -DFNFLOAT -DCK_POSIX_SIG \ -DCK_NEWTERM -DTCPSOCKET -DLINUXFSSTND -DNOCOTFMC -DPOSIX \ -DUSE_STRERROR $(KFLAGS)" "LNKFLAGS = $(LNKFLAGS)" \ "LIBS = $(LIBS) -lm" # As above but with profiling linuxp: $(MAKE) linuxa KTARGET=$${KTARGET:-$(@)} "KFLAGS=$(KFLAGS) -pg" \ "LIBS=-pg -lcrypt -lresolv" #Linux. This entry should work for any Linux distribution on any platform, #32-bit or 64-bit, except for extremely ancient ones. Automatically detects: # . curses, ncurses, or no curses # . Old versus new pty handling (new == glibc 2.1++) # . Presence or absence of libcrypt.a and # . Presence or absence of libresolv.a # . Transitional Long File API for 32-bit platforms (SUS V2 UNIX 98 LFS). #Note: The HAVE_PTMX test was previously "if test -c /dev/ptmx" but this was #not sufficient for Debian 2.1, because although it had /dev/ptmx, it did not #have grantpt(), unlockpt(), or ptsname(), so has been changed to look for a #grantpt() prototype in the header files. Modified in 8.0.206 to allow for #libraries that contain .so's but no .a's, e.g. Mandrake 9.0. #HAVE_BAUDBOY added in 8.0.210 for Red Hat -- it's like AIX ttylock(). #Modified 17 Aug 2005 to use openpty() if available because the other stuff #dumps core in 64-bit ia64 and x86_64 builds. #Long file support for 32-bit builds added in 8.0.212 - if features.h contains #__USE_LARGEFILE64 then we set the flags that must be set before reading any #header files; on 32-bit platforms such as i386, this produces a 32-bit build #capable of accessing, sending, receiving, and managing long (> 2GB) files. #On 64-bit platforms, it does no harm. #As of 3 March 2009 we detect automatically if we have curses, ncurses, #or no curses at all. #Added HAVE_LOCKDEV as openSuSE >= 11.3 uses ttylock directly instead of #baudboy 2010/08/23 #OK: 2011/06/18 linux: @if test \ `grep grantpt /usr/include/*.h /usr/include/sys/*.h | wc -l` -gt 0; \ then if test -c /dev/ptmx; then HAVE_PTMX='-DHAVE_PTMX'; \ else HAVE_PTMX=''; fi; fi ; \ if test `grep openpty /usr/include/pty.h | wc -l` -gt 0; \ then HAVE_OPENPTY='-DHAVE_OPENPTY'; \ else HAVE_OPENPTY=''; fi ; \ HAVE_LIBCURSES=''; \ if test -f /usr/lib64/libncurses.so || \ test -f /usr/lib/libncurses.a || \ test -f /usr/lib/libncurses.so; then \ HAVE_LIBCURSES='-lncurses'; \ else if test -f /usr/lib64/libcurses.so || \ test -f /usr/lib/libcurses.a || \ test -f /usr/lib/libcurses.so; then \ HAVE_LIBCURSES='-lcurses'; fi; fi; \ HAVE_CURSES=''; \ if test -n '$$HAVE_LIBCURSES'; then \ if test -f /usr/include/ncurses.h; then \ HAVE_CURSES='-DCK_NCURSES -I/usr/include/ncurses'; \ else if test -f /usr/include/curses.h; then \ HAVE_CURSES='-DCK_CURSES'; \ fi; fi; fi; \ if test -f /usr/include/baudboy.h || test -f /usr/include/ttylock.h; \ then HAVE_LOCKDEV='-DHAVE_LOCKDEV' ; \ else HAVE_LOCKDEV='' ; fi ; \ if test -f /usr/include/baudboy.h ; \ then HAVE_BAUDBOY='-DHAVE_BAUDBOY' ; \ else HAVE_BAUDBOY='' ; fi ; \ $(MAKE) KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=$$HAVE_CURSES $$HAVE_PTMX $$HAVE_LOCKDEV \ $$HAVE_BAUDBOY $$HAVE_OPENPTY \ `grep __USE_LARGEFILE64 /usr/include/features.h > /dev/null && \ echo '-D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64'` \ `if test -f /usr/include/crypt.h; then echo -DHAVE_CRYPT_H; fi` \ $(KFLAGS)" \ "LIBS=$(LIBS) $$HAVE_LIBCURSES \ `if test -n '$$HAVE_OPENPTY'; then echo -lutil; fi` \ `if test -f /usr/lib64/libresolv.a || test -f /usr/lib64/libresolv.so \ || test -f /usr/lib/libresolv.a || test -f /usr/lib/libresolv.so \ || test -f /usr/lib/i386-linux-gnu/libresolv.a \ || test -f /usr/lib/i386-linux-gnu/libresolv.so \ || ls /lib/x86_64-linux-gnu/libresolv.* > /dev/null 2> /dev/null; \ then echo -lresolv; fi` \ `if test -f /usr/lib64/libcrypt.a || test -f /usr/lib64/libcrypt.so \ || test -f /usr/lib/libcrypt.a || test -f /usr/lib/libcrypt.so \ || ls /lib/x86_64-linux-gnu/libcrypt.* > /dev/null 2> /dev/null; \ then echo -lcrypt; fi` \ `if test -f /usr/lib64/liblockdev.a || \ test -f /usr/lib64/liblockdev.so || \ test -f /usr/lib/liblockdev.a || \ test -f /usr/lib/liblockdev.so; \ then echo -llockdev; fi`" \ linuxa # Linux + Shadow passwords + PAM # OK 2011/06/18 linux+shadow+pam: @echo 'Making C-Kermit $(CKVER) for Linux+Shadow+PAM...' $(MAKE) linux KTARGET=$${KTARGET:-$(@)} \ KFLAGS="-DCK_SHADOW -DCK_PAM $(KFLAGS)" \ "LNKFLAGS = $(LNKFLAGS)" \ "LIBS = -lpam" # Linux systems that have no . # (not tested in recent years, perhaps no longer needed) linuxns: $(MAKE) linux KTARGET=$${KTARGET:-$(@)} KFLAGS=-DNO_SYS_SELECT_H # Linux-script-only: # A minimum-size version for Linux that does only scripting and # serial communication -- no networks, no file transfer, no security. # OK 2011/06/18 linuxso: $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} "CC = gcc" "CC2 = gcc" \ "CFLAGS = -O -DLINUX -pipe -funsigned-char -DPOSIX -DCK_POSIX_SIG \ -DLINUXFSSTND -DNOCOTFMC -DNOXFER -DNODEBUG -DNOCSETS -DNOHELP \ -DNONET -DMINIDIAL -DNOSCRIPT -DNOIKSD -DNOPUSH $(KFLAGS)" \ "LNKFLAGS = $(LNKFLAGS)" "LIBS = " # Secure targets for Linux. These work on RHAS4, RHEL4, and RHEL5, # unlike some of the older targets that follow. They hook into the main Linux # target so we pick up all the other new stuff - large files, baudboy.h, the # appropriate pty interface, etc. # Linux with Kerberos 5. # Use "make linux+krb5 KFLAGS=-DNO_KRB5_INIT_ETS" if necessary. #OK 2011/06/16 on Fedora 14 with: # make linux+krb5 "LIBS=$LIBS /lib/libk5crypto.so.3 /lib/libcom_err.so.2" # On RHEL5: make linux+krb5 -UCK_DES linux+krb5: @echo 'Making C-Kermit $(CKVER) for Linux with Kerberos 5...' @case `openssl version` in \ *0.9.7*) OPENSSLOPTION="-DOPENSSL_097" ;; \ *0.9.8*) OPENSSLOPTION="-DOPENSSL_098" ;; \ *1.[0-9].[0-9]*) OPENSSLOPTION="-DOPENSSL_100" ;; \ *) OPENSSLOPTION="" ;; \ esac; \ HAVE_DES=''; \ DES_LIB=''; \ if ls /usr/lib/libdes* > /dev/null 2> /dev/null || \ ls $(SSLLIB)/libdes* > /dev/null 2> /dev/null; then \ DES_LIB='-ldes425'; \ HAVE_DES='-DCK_DES -DLIBDES'; \ echo "HAVE DES"; \ else echo "NO DES"; \ fi; \ K5CRYPTO=''; \ if ls /lib/libk5crypto* > /dev/null 2> /dev/null; then \ K5CRYPTO='-lk5crypto'; \ else if ls /usr/lib/libk5crypto* > /dev/null 2> /dev/null; then \ K5CRYPTO='-lk5crypto'; \ else if ls /usr/lib64/libk5crypto* > /dev/null 2> /dev/null; then \ K5CRYPTO='-lk5crypto'; \ fi; fi; fi; \ COM_ERR=''; \ if ls /lib/libcom_err* > /dev/null 2> /dev/null; then \ COM_ERR='-lcom_err'; \ fi; \ GSSAPILIB='-lgssapi'; \ if ls /lib/libgssapi_krb5* > /dev/null 2> /dev/null; then \ GSSAPILIB='-lgssapi_krb5'; \ else if ls /usr/lib/libgssapi_krb5* > /dev/null 2> /dev/null; then \ GSSAPILIB='-lgssapi_krb5'; \ else K5DIR=`echo $(K5LIB) | sed 's|-L||'`; \ if ls $$K5DIR/libgssapi_krb5* > /dev/null 2> /dev/null; then \ GSSAPILIB='-lgssapi_krb5'; \ fi; fi; fi; \ $(MAKE) linux KTARGET=$${KTARGET:-$(@)} "CC = gcc" "CC2 = gcc" \ "KFLAGS= -DCK_AUTHENTICATION -DCK_KERBEROS -DKRB5 $$OPENSSLOPTION \ -DCK_ENCRYPTION $$HAVE_DES $(K5INC) $(K5INC)/krb5 \ -I/usr/include/et $(KFLAGS)" "LNKFLAGS = $(LNKFLAGS)" \ "LIBS = $(K5LIB) $$DES_LIB -lcrypto $$GSSAPILIB -lkrb5 \ $$K5CRYPTO $$COM_ERR $(LIBS)" ; \ if [ ! -f ./wermit ] || [ ./ckcmai.o -nt ./wermit ] ; then \ echo ""; \ echo "If build failed try:"; \ echo ""; \ echo " make clean ; make $${KTARGET:-$(@)} KFLAGS=-UCK_DES"; \ echo ""; \ fi # Linux with Kerberos 5 and Kerberos 4. # Use "make linux+krb5 KFLAGS=-DNO_KRB5_INIT_ETS" if necessary. # Add "KFLAGS=-UCK_DES" if failure messages look DES-related. # UNTESTED (because I can't find a box with Krb4 and Krb5 installed) linux+krb5+krb4: @echo 'Making C-Kermit for Linux with Kerberos 4 and Kerberos 5' $(MAKE) linux+krb5 KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-DKRB4 -DKRB524 $(KFLAGS)" "LIBS=$(LIBS) -lkrb4" # Linux with OpenSSL # In Linux, SSL libs are often in /lib or /usr/lib and so found by default. # This targets takes into account the DES library might or might not # exist. If it does exist, however, the target will require some editing # if its basename is not libdes425. - fdc Tue Sep 21 14:28:00 2010 # IMPORTANT: Some Linux platforms have DES libraries but they are missing # functions used by Kermit. In that case you will get fatal errors at # link time involving routines such as des_ecb3_encrypt, des_random_seed, # and des_set_odd_parity. In that case, "make linux KFLAGS=-UCK_DES" # There's a new warning at the end that should come out if this happens, # and that should not come out if it didn't. # linux+ssl linux+openssl linux+openssl+zlib+shadow+pam linux+openssl+shadow: @echo 'Making C-Kermit $(CKVER) for Linux+OpenSSL SSLLIB=$(SSLLIB)' @case `openssl version` in \ *0.9.7*) OPENSSLOPTION="-DOPENSSL_097" ;; \ *0.9.8*) OPENSSLOPTION="-DOPENSSL_098" ;; \ *1.[0-9].[0-9]*) OPENSSLOPTION="-DOPENSSL_100" ;; \ *) OPENSSLOPTION="" ;; \ esac; \ HAVE_DES=''; \ DES_LIB=''; \ if ls /usr/lib/libdes* > /dev/null 2> /dev/null || \ ls $(SSLLIB)/libdes* > /dev/null 2> /dev/null; then \ DES_LIB='-ldes425'; \ HAVE_DES='-DCK_DES -DLIBDES'; \ echo "HAVE DES"; \ else echo "NO DES"; \ fi; \ $(MAKE) linux KTARGET=$${KTARGET:-$(@)} "CC = gcc" "CC2 = gcc" \ "KFLAGS= -DCK_AUTHENTICATION -DCK_ENCRYPTION -DCK_CAST $$HAVE_DES \ -DCK_SSL -DCK_PAM -DZLIB -DCK_SHADOW $$OPENSSLOPTION $(SSLINC) \ $(KFLAGS)" "LNKFLAGS = $(LNKFLAGS)" \ "LIBS = $(SSLLIB) -lssl $$DES_LIB -lcrypto -lpam -ldl -lz $(LIBS)" ; \ if [ ! -f ./wermit ] || [ ./ckcmai.o -nt ./wermit ] ; then \ echo ""; \ echo "If build failed try:"; \ echo ""; \ echo " make clean ; make $${KTARGET:-$(@)} KFLAGS=-UCK_DES"; \ echo ""; \ fi # Linux with Kerberos 5 and OpenSSL # OK 2011/05/16 # Add -UCK_DES if functions like des_ecb3_encrypt, es_random_seed, # come up missing at link time. linux+krb5+ssl linux+krb5+openssl: @echo 'Making C-Kermit $(CKVER) for Linux with Krb5 and OpenSSL...' @case `openssl version` in \ *0.9.7*) OPENSSLOPTION="-DOPENSSL_097" ;; \ *0.9.8*) OPENSSLOPTION="-DOPENSSL_098" ;; \ *1.[0-9].[0-9]*) OPENSSLOPTION="-DOPENSSL_100" ;; \ *) OPENSSLOPTION="" ;; \ esac; \ HAVE_DES=''; \ DES_LIB=''; \ if ls /usr/lib/libdes* > /dev/null 2> /dev/null || \ ls $(SSLLIB)/libdes* > /dev/null 2> /dev/null; then \ DES_LIB='-ldes425'; \ HAVE_DES='-DCK_DES -DLIBDES'; \ echo "HAVE DES"; \ else echo "NO DES"; \ fi; \ K5CRYPTO=''; \ if ls /lib/libk5crypto* > /dev/null 2> /dev/null; then \ K5CRYPTO='-lk5crypto'; \ else if ls /usr/lib/libk5crypto* > /dev/null 2> /dev/null; then \ K5CRYPTO='-lk5crypto'; \ else if ls /usr/lib64/libk5crypto* > /dev/null 2> /dev/null; then \ K5CRYPTO='-lk5crypto'; \ fi; fi; fi; \ COM_ERR=''; \ if ls /lib/libcom_err* > /dev/null 2> /dev/null; then \ COM_ERR='-lcom_err'; \ fi; \ GSSAPILIB='-lgssapi'; \ if ls /lib/libgssapi_krb5* > /dev/null 2> /dev/null; then \ GSSAPILIB='-lgssapi_krb5'; \ else if ls /usr/lib/libgssapi_krb5* > /dev/null 2> /dev/null; then \ GSSAPILIB='-lgssapi_krb5'; \ else K5DIR=`echo $(K5LIB) | sed 's|-L||'`; \ if ls $$K5DIR/libgssapi_krb5* > /dev/null 2> /dev/null; then \ GSSAPILIB='-lgssapi_krb5'; \ fi; fi; fi; \ $(MAKE) linux KTARGET=$${KTARGET:-$(@)} "CC = gcc" "CC2 = gcc" \ "KFLAGS= -DCK_AUTHENTICATION -DCK_KERBEROS -DKRB5 \ -DCK_SSL -DCK_PAM -DZLIB -DCK_SHADOW $$OPENSSLOPTION $(SSLINC) \ -DCK_ENCRYPTION $$HAVE_DES $(K5INC) $(K5INC)/krb5 \ -I/usr/include/et $(KFLAGS)" "LNKFLAGS = $(LNKFLAGS)" \ "LIBS = $(K5LIB) $(SSLLIB) -lssl $$DES_LIB -lpam -lz \ -lcrypto $$GSSAPILIB -lkrb5 $$K5CRYPTO $$COM_ERR $(LIBS)" ; \ if [ ! -f ./wermit ] || [ ./ckcmai.o -nt ./wermit ] ; then \ echo ""; \ echo "If build failed try:"; \ echo ""; \ echo " make clean ; make $${KTARGET:-$(@)} KFLAGS=-UCK_DES"; \ echo ""; \ fi # ::BEGIN_OLD_LINUX_TARGETS:: # The remaining Linux entries are for special or customized builds. They have # not been generalized ("subroutinized") like the ones above. Ideally, we # should allow for every combination of libc vs glibc, gcc vs egcs, curses vs # ncurses, Kerberos IV vs Kerberos V vs SRP (in any combination), and so on. # The best way to do this is to set KFLAGS and LIBS values and then chain to # the main "linux" target, as in the examples just above. To skip past all of # these old targets (and there are many) search for ::END_OLD_LINUX_TARGETS:: # (after this line). #Sharp Zaurus SL-5500 - Linux based zsl5500: @echo 'Making C-Kermit $(CKVER) for Sharp Zaurus SL-5500...' @touch ckcpro.c @touch wart $(MAKE) linuxnc KTARGET=$${KTARGET:-$(@)} "KFLAGS=-DZSL5500" \ "CC = gcc" "CC2 = gcc" #Mklinux DR3 has horrible bug in - see ckufio.c. mklinux: $(MAKE) KTARGET=$${KTARGET:-$(@)} "KFLAGS=-DUTMPBUG" \ "LIBS=-lcrypt -lresolv" linuxa #LinuxPPC 1999 linuxppc: @echo 'Making C-Kermit $(CKVER) for LinuxPPC 1999...' @if test -f /usr/lib/libcrypt.a; then \ if test -f /usr/lib/libresolv.a; then \ $(MAKE) KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=$(NCURSES_CPP) -DHAVE_CRYPT_H \ -DLOCK_DIR=\\\\\\"\"/var/lock/modem\\\\\\"\" $(KFLAGS)" \ "LIBS=-lncurses -lresolv -lcrypt" linuxa ; \ else \ $(MAKE) KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=$(NCURSES_CPP) -DHAVE_CRYPT_H \ -DLOCK_DIR=\\\\\\"\"/var/lock/modem\\\\\\"\" $(KFLAGS)" \ "LIBS=-lncurses -lcrypt" linuxa ; \ fi \ else \ if test -f /usr/lib/libresolv.a; then \ $(MAKE) KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=$(NCURSES_CPP) \ -DLOCK_DIR=\\\\\\"\"/var/lock/modem\\\\\\"\" $(KFLAGS)" \ "LIBS=-lncurses -lresolv" linuxa ; \ else \ $(MAKE) KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=$(NCURSES_CPP) \ -DLOCK_DIR=\\\\\\"\"/var/lock/modem\\\\\\"\" $(KFLAGS)" \ "LIBS=-lncurses" linuxa ; \ fi \ fi # Like "make linux" but built with egcs rather than gcc. # If you get "Internal compiler error xxx, output pipe has been closed", # try removing -pipe. linuxegcs: @echo 'Making C-Kermit $(CKVER) for Linux 1.2 or later with egcs...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} "CC = egcs" "CC2 = egcs" \ "CFLAGS = -O -DLINUX -pipe -funsigned-char \ -DPOSIX -DCK_POSIX_SIG -DCK_NCURSES -DNOCOTFMC \ -DTCPSOCKET -DLINUXFSSTND $(KFLAGS)" \ "LNKFLAGS = $(LNKFLAGS)" "LIBS = -lncurses -lcrypt -lresolv" #Linux on Intel PC with Cygnus or MIT Kerberos 5 1.2.1 (no K4 compatibility). linux+krb5-old: @echo 'Making C-Kermit $(CKVER) for Linux on Intel with Kerberos...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} "CC = gcc" "CC2 = gcc" \ "CFLAGS = -O -funsigned-char -pipe -DPOSIX -DLINUX -DNOCOTFMC \ -DCK_AUTHENTICATION -DCK_KERBEROS -DKRB5 \ -DCK_ENCRYPTION -DCK_DES -DCK_CURSES -DCK_POSIX_SIG \ -DTCPSOCKET -DLINUXFSSTND -DHAVE_CRYPT_H $(K5INC) $(K5INC)/krb5 \ $(KFLAGS)" "LNKFLAGS = $(LNKFLAGS)" \ "LIBS = $(K5LIB) -lncurses -ltermcap -ldes425 -lkrb5 \ -lcom_err -lk5crypto -lgssapi_krb5 -lcrypt -lresolv" # Linux on Intel PC with SRP 1.7.4 using GNU MP, Krypto, and Eric Young's # DES library. Remove the -DCK_DES, -DLIBDES and -ldes if you do not have # Eric Young's# libdes.a installed. # linux+srp+gmp: @echo 'Making C-Kermit $(CKVER) for Linux on i386 with SRP...' $(MAKE) srpmit KTARGET=$${KTARGET:-$(@)} "CC = gcc" "CC2 = gcc" \ "CFLAGS = -O -funsigned-char -pipe -DPOSIX -DLINUX -DNOCOTFMC \ -DCK_AUTHENTICATION -DCK_SRP \ -DCK_ENCRYPTION -DCK_CAST -DCK_DES -DLIBDES \ -DCK_CURSES -DCK_POSIX_SIG -DTCPSOCKET -DLINUXFSSTND -DHAVE_CRYPT_H \ $(SRPINC) $(KFLAGS)" "LNKFLAGS = $(LNKFLAGS)" \ "LIBS = $(SRPLIB) \ -lncurses -ltermcap -lsrp -lgmp -ldes -lkrypto -lcrypt -lresolv" linux+srp+gmp+no-des: @echo 'Making C-Kermit $(CKVER) for Linux on i386 with SRP ...' $(MAKE) srpmit KTARGET=$${KTARGET:-$(@)} "CC = gcc" "CC2 = gcc" \ "CFLAGS = -O -funsigned-char -pipe -DPOSIX -DLINUX -DNOCOTFMC \ -DCK_AUTHENTICATION -DCK_SRP \ -DCK_ENCRYPTION -DCK_CAST \ -DCK_CURSES -DCK_POSIX_SIG -DTCPSOCKET -DLINUXFSSTND -DHAVE_CRYPT_H \ $(SRPINC) $(KFLAGS)" "LNKFLAGS = $(LNKFLAGS)" \ "LIBS = $(SRPLIB) \ -lncurses -ltermcap -lsrp -lgmp -lkrypto -lcrypt -lresolv" linux+srp+gmp-export: @echo 'Making C-Kermit $(CKVER) for Linux on i386 with SRP...' $(MAKE) srpmit-export KTARGET=$${KTARGET:-$(@)} \ "CC = gcc" "CC2 = gcc" \ "CFLAGS = -O -funsigned-char -pipe -DPOSIX -DLINUX -DNOCOTFMC \ -DCK_AUTHENTICATION -DCK_SRP -DFNFLOAT \ -DCK_CURSES -DCK_POSIX_SIG -DTCPSOCKET -DLINUXFSSTND -DHAVE_CRYPT_H \ $(SRPINC) $(KFLAGS)" "LNKFLAGS = $(LNKFLAGS)" \ "LIBS = $(SRPLIB) \ -lncurses -ltermcap -lsrp -lgmp -lkrypto -lcrypt -lm -lresolv" linux+srp+gmp+pam: @echo 'Making C-Kermit $(CKVER) for Linux on i386 with SRP...' $(MAKE) srpmit KTARGET=$${KTARGET:-$(@)} "CC = gcc" "CC2 = gcc" \ "CFLAGS = -O -funsigned-char -pipe -DPOSIX -DLINUX -DNOCOTFMC \ -DCK_AUTHENTICATION -DCK_SRP \ -DCK_ENCRYPTION -DCK_CAST -DCK_DES -DLIBDES \ -DCK_CURSES -DCK_POSIX_SIG -DTCPSOCKET -DLINUXFSSTND -DHAVE_CRYPT_H \ -DCK_PAM -DFNFLOAT $(SRPINC) $(KFLAGS)" "LNKFLAGS = $(LNKFLAGS)" \ "LIBS = $(SRPLIB) -lncurses -ltermcap -lsrp -lgmp -ldes -lkrypto \ -lcrypt -lpam -ldl -lm -lresolv" #Linux on Intel PC with SRP 1.7.4 built with OpenSSL for Big Number Math #and Cryptographic functionality. # linux+srp: @echo 'Making C-Kermit $(CKVER) for Linux on i386 with SRP...' $(MAKE) srpmit KTARGET=$${KTARGET:-$(@)} "CC = gcc" "CC2 = gcc" \ "CFLAGS = -O -funsigned-char -pipe -DPOSIX -DLINUX -DNOCOTFMC \ -DCK_AUTHENTICATION -DCK_SRP \ -DCK_ENCRYPTION -DCK_CAST -DCK_DES -DLIBDES \ -DCK_CURSES -DCK_POSIX_SIG -DTCPSOCKET -DLINUXFSSTND -DHAVE_CRYPT_H \ $(SRPINC) $(SSLINC) $(KFLAGS)" "LNKFLAGS = $(LNKFLAGS)" \ "LIBS = $(SRPLIB) $(SSLLIB) \ -lncurses -ltermcap -lsrp -lkrypto -lcrypto -lcrypt -lresolv" linux+srp+pam: @echo 'Making C-Kermit $(CKVER) for Linux on i386 with SRP...' $(MAKE) srpmit KTARGET=$${KTARGET:-$(@)} "CC = gcc" "CC2 = gcc" \ "CFLAGS = -O -funsigned-char -pipe -DPOSIX -DLINUX -DNOCOTFMC \ -DCK_AUTHENTICATION -DCK_SRP \ -DCK_ENCRYPTION -DCK_CAST -DCK_DES -DLIBDES \ -DCK_CURSES -DCK_POSIX_SIG -DTCPSOCKET -DLINUXFSSTND -DHAVE_CRYPT_H \ -DCK_PAM -DFNFLOAT $(SRPINC) $(SSLINC) $(KFLAGS)" \ "LNKFLAGS = $(LNKFLAGS)" \ "LIBS = $(SRPLIB) $(SSLLIB) -lncurses -ltermcap -lsrp -lkrypto \ -lcrypto -lcrypt -lpam -ldl -lm -lresolv" #Linux on Intel PC with SRP and SSL/TLS. # # libsrp.a should be build with OpenSSL # Requires the Kerberos 1.2.2 or higher to be compiled with KRB4 compatibility. #Remove -ltermcap if it causes trouble e.g. in Debian 2.2. #If you have OpenSSL 0.9.7 or later, add -DOPENSSL_097 to KFLAGS. linux+srp+openssl: @echo 'Making C-Kermit $(CKVER) for Linux on i386 with KRB,SRP,SSL...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} "CC = gcc" "CC2 = gcc" \ "CFLAGS = -O -funsigned-char -pipe -DPOSIX -DLINUX -DNOCOTFMC \ -DCK_AUTHENTICATION -DCK_SRP \ -DCK_ENCRYPTION -DCK_CAST -DCK_DES -DLIBDES -DCK_SSL \ -DCK_CURSES -DCK_POSIX_SIG -DTCPSOCKET -DLINUXFSSTND -DHAVE_CRYPT_H \ $(SRPINC) $(SSLINC) $(KFLAGS)" "LNKFLAGS = $(LNKFLAGS)" \ "LIBS = $(SRPLIB) $(SSLLIB) \ -lncurses -ltermcap -lsrp -lssl -lkrypto -lcrypto \ -lcrypt -lresolv" #Linux on Intel PC with Cygnus or MIT Kerberos 5 1.2.2 and SRP. # # libsrp.a should be build with GNU MP (libgmp.a) # instead of AT&T CryptoLib (libcrypt.a) due to naming conflicts with # standard distribution Linux libraries. # Requires the Kerberos 1.2.2 or higher to be compiled with KRB4 compatibility. linux+krb5+krb4+srp: @echo 'Making C-Kermit $(CKVER) for Linux on i386 with KRB54+SRP...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} "CC = gcc" "CC2 = gcc" \ "CFLAGS = -O -funsigned-char -pipe -DPOSIX -DLINUX -DNOCOTFMC \ -DCK_AUTHENTICATION -DCK_SRP -DCK_KERBEROS -DKRB5 -DKRB4 -DKRB524 \ -DCK_ENCRYPTION -DCK_CAST -DCK_DES -DLIBDES \ -DCK_CURSES -DCK_POSIX_SIG -DTCPSOCKET -DLINUXFSSTND -DHAVE_CRYPT_H \ $(K5INC) $(K5INC)/krb5 $(SRPINC) \ $(KFLAGS)" "LNKFLAGS = $(LNKFLAGS)" \ "LIBS = $(K5LIB) $(SRPLIB) \ -lncurses -ltermcap -lsrp -lgmp -lgssapi_krb5 -lkrypto \ -ldes -lkrb4 -ldes425 -lkrb5 -lcom_err -lk5crypto -lcrypt -lresolv" #Linux on Intel PC with Cygnus or MIT Kerberos 5 1.2.2, SRP and SSL/TLS. # # libsrp.a should be build with OpenSSL # Requires the Kerberos 1.2.2 or higher to be compiled with KRB4 compatibility. # Requires OpenSSL 0.9.6a or higher #If you have OpenSSL 0.9.7 or later, add -DOPENSSL_097 to KFLAGS. linux+krb5+krb4+srp+openssl: @echo 'Making C-Kermit $(CKVER) for Linux on i386 with KRB,SRP,SSL...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} "CC = gcc" "CC2 = gcc" \ "CFLAGS = -O -funsigned-char -pipe -DPOSIX -DLINUX -DNOCOTFMC \ -DCK_AUTHENTICATION -DCK_SRP -DCK_KERBEROS -DKRB5 -DKRB4 -DKRB524 \ -DCK_ENCRYPTION -DCK_CAST -DCK_DES -DLIBDES -DCK_SSL \ -DCK_CURSES -DCK_POSIX_SIG -DTCPSOCKET -DLINUXFSSTND -DHAVE_CRYPT_H \ $(K5INC) $(K5INC)/krb5 $(SRPINC) $(SSLINC) \ $(KFLAGS)" "LNKFLAGS = $(LNKFLAGS)" \ "LIBS = $(K5LIB) $(SRPLIB) $(SSLLIB) \ -lncurses -ltermcap -lsrp \ -lkrb4 -lssl -lkrypto -lcrypto \ -lkrb5 -lcom_err -lk5crypto -lgssapi_krb5 -lcrypt -lresolv" #Linux on Intel PC with Cygnus or MIT Kerberos 5 1.2.2, SSL/TLS. # # libsrp.a should be build with OpenSSL # Requires the Kerberos 1.2.2 be compiled with KRB4 compatibility. #If you have OpenSSL 0.9.7 or later, add -DOPENSSL_097 to KFLAGS. linux+krb5+krb4+openssl: @echo 'Making C-Kermit $(CKVER) for Linux on i386 with KRB,SSL...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} "CC = gcc" "CC2 = gcc" \ "CFLAGS = -O -funsigned-char -pipe -DPOSIX -DLINUX -DNOCOTFMC \ -DCK_AUTHENTICATION -DCK_KERBEROS -DKRB5 -DKRB4 -DKRB524 \ -DCK_ENCRYPTION -DCK_CAST -DCK_DES -DLIBDES -DCK_SSL \ -DCK_CURSES -DCK_POSIX_SIG -DTCPSOCKET -DLINUXFSSTND -DHAVE_CRYPT_H \ $(K5INC) $(K5INC)/krb5 $(SSLINC) \ $(KFLAGS)" "LNKFLAGS = $(LNKFLAGS)" \ "LIBS = $(K5LIB) $(SSLLIB) \ -lncurses -ltermcap \ -lkrb4 -lssl -lcrypto -lkrb5 -lcom_err \ -lk5crypto -lgssapi_krb5 -lcrypt -lresolv" #Linux on Intel PC with Cygnus or MIT Kerberos 5 1.2.1, SSL/TLS. # # libsrp.a should be build with OpenSSL # Requires the Kerberos 1.2.2 be compiled with KRB4 compatibility. # If you have OpenSSL 0.9.7 or later, add -DOPENSSL_097 to KFLAGS. linux+krb5+krb4+openssl+shadow: @echo 'Making C-Kermit $(CKVER) for Linux on i386 with KRB,SSL...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} "CC = gcc" "CC2 = gcc" \ "CFLAGS = -O -funsigned-char -pipe -DPOSIX -DLINUX -DNOCOTFMC \ -DCK_AUTHENTICATION -DCK_KERBEROS -DKRB5 -DKRB4 -DKRB524 \ -DCK_ENCRYPTION -DCK_CAST -DCK_DES -DLIBDES -DCK_SSL -DCK_SHADOW \ -DCK_CURSES -DCK_POSIX_SIG -DTCPSOCKET -DLINUXFSSTND -DHAVE_CRYPT_H \ $(K5INC) $(K5INC)/krb5 $(SSLINC) \ $(KFLAGS)" "LNKFLAGS = $(LNKFLAGS)" \ "LIBS = $(K5LIB) $(SSLLIB) \ -lncurses -ltermcap \ -lkrb4 -lssl -lcrypto -lkrb5 -lcom_err \ -lk5crypto -lgssapi_krb5 -lcrypt -lresolv" #Linux on Intel PC with Cygnus or MIT Kerberos 5 1.2, SSL/TLS. # # libsrp.a should be build with OpenSSL # Requires the Kerberos 1.2.2 be compiled with KRB4 compatibility. # If you have OpenSSL 0.9.7 or later, add -DOPENSSL_097 to KFLAGS. linux+krb5+krb4+openssl+zlib+shadow: @echo 'Making C-Kermit $(CKVER) for Linux on i386 with KRB,SSL...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} "CC = gcc" "CC2 = gcc" \ "CFLAGS = -O -funsigned-char -pipe -DPOSIX -DLINUX -DNOCOTFMC \ -DCK_AUTHENTICATION -DCK_KERBEROS -DKRB5 -DKRB4 -DKRB524 -DZLIB \ -DCK_ENCRYPTION -DCK_CAST -DCK_DES -DLIBDES -DCK_SSL -DCK_SHADOW \ -DCK_CURSES -DCK_POSIX_SIG -DTCPSOCKET -DLINUXFSSTND -DHAVE_CRYPT_H \ $(K5INC) $(K5INC)/krb5 $(SSLINC) \ $(KFLAGS)" "LNKFLAGS = $(LNKFLAGS)" \ "LIBS = $(K5LIB) $(SSLLIB) \ -lncurses -ltermcap \ -lkrb4 -lssl -lcrypto -lkrb5 -lcom_err \ -lk5crypto -lgssapi_krb5 -lcrypt -lresolv -lz" linux+krb5+krb4+srp-export: @echo 'Making C-Kermit $(CKVER) for Linux on i386 with SRP...' $(MAKE) xermit-export KTARGET=$${KTARGET:-$(@)} \ "CC = gcc" "CC2 = gcc" \ "CFLAGS = -O -funsigned-char -pipe -DPOSIX -DLINUX -DNOCOTFMC \ -DCK_AUTHENTICATION -DCK_SRP -DCK_KERBEROS -DKRB5 -DKRB4 -DKRB524 \ -DCK_CURSES -DCK_POSIX_SIG -DTCPSOCKET -DLINUXFSSTND -DHAVE_CRYPT_H \ $(K5INC) $(K5INC)/krb5 $(SRPINC) \ $(KFLAGS)" "LNKFLAGS = $(LNKFLAGS)" \ "LIBS = $(SRPLIB) $(K5LIB) \ -lncurses -ltermcap -lsrp -lgmp -lkrb4 -ldes425 -lkrb5 -lgssapi_krb5 \ -lcom_err -lk5crypto -lkrypto -lcrypt -lresolv" linux+krb5+krb4+srp+pam: @echo 'Making C-Kermit $(CKVER) for Linux on i386 with SRP...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} "CC = gcc" "CC2 = gcc" \ "CFLAGS = -O -funsigned-char -pipe -DPOSIX -DLINUX -DNOCOTFMC \ -DCK_AUTHENTICATION -DCK_SRP -DCK_KERBEROS -DKRB5 -DKRB4 -DKRB524 \ -DCK_ENCRYPTION -DCK_CAST -DCK_DES -DLIBDES \ -DCK_CURSES -DCK_POSIX_SIG -DTCPSOCKET -DLINUXFSSTND -DHAVE_CRYPT_H \ -DCK_PAM $(K5INC) $(K5INC)/krb5 $(SRPINC) \ $(KFLAGS)" "LNKFLAGS = $(LNKFLAGS)" \ "LIBS = $(SRPLIB) $(K5LIB) \ -lncurses -ltermcap -lsrp -lgmp -ldes -lkrb4 -ldes425 -lkrb5 \ -lcom_err -lk5crypto -lgssapi_krb5 -lkrypto -lcrypt -lpam -ldl \ -lresolv" #Linux on Intel PC with Cygnus or MIT Kerberos 5 1.2.2, SRP and SSL/TLS. # and PAM. # # libsrp.a should be build with OpenSSL # Requires the Kerberos 1.2.2 be compiled with KRB4 compatibility. # If you have OpenSSL 0.9.7 or later, add -DOPENSSL_097 to KFLAGS. linux+krb5+krb4+srp+openssl+pam-debug: @echo 'Making C-Kermit $(CKVER) for Linux on i386 with KRB,SRP,SSL...' $(MAKE) xermit-debug KTARGET=$${KTARGET:-$(@)} "CC = gcc" "CC2 = gcc" \ "CFLAGS = -g -O -funsigned-char -pipe -DPOSIX -DLINUX -DNOCOTFMC \ -DCK_AUTHENTICATION -DCK_SRP -DCK_KERBEROS -DKRB5 -DKRB4 -DKRB524 \ -DCK_ENCRYPTION -DCK_CAST -DCK_DES -DLIBDES -DCK_SSL -DCK_PAM \ -DCK_CURSES -DCK_POSIX_SIG -DTCPSOCKET -DLINUXFSSTND -DHAVE_CRYPT_H \ -w -Dmalloc=dmalloc -Dfree=dfree -DMDEBUG $(K5INC) $(K5INC)/krb5 \ $(SRPINC) $(SSLINC) $(KFLAGS)" "LNKFLAGS = $(LNKFLAGS)" \ "LIBS = $(SRPLIB) $(K5LIB) $(SSLLIB) \ -lncurses -ltermcap -lsrp -lkrb4 -lssl -lkrypto -lcrypto \ -lkrb5 -lcom_err -lk5crypto -lgssapi_krb5 -lcrypt -lresolv -lpam -ldl" #Linux on Intel PC with Cygnus or MIT Kerberos 5 1.2.1, SRP and SSL/TLS. # and PAM. # # libsrp.a should be build with OpenSSL # Requires the Kerberos 1.2.2 be compiled with KRB4 compatibility. # If you have OpenSSL 0.9.7 or later, add -DOPENSSL_097 to KFLAGS. linux+krb5+krb4+srp+openssl+pam: @echo 'Making C-Kermit $(CKVER) for Linux on i386 with KRB,SRP,SSL...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} "CC = gcc" "CC2 = gcc" \ "CFLAGS = -g -O -funsigned-char -pipe -DPOSIX -DLINUX -DNOCOTFMC \ -DCK_AUTHENTICATION -DCK_SRP -DCK_KERBEROS -DKRB5 -DKRB4 -DKRB524 \ -DCK_ENCRYPTION -DCK_CAST -DCK_DES -DLIBDES -DCK_SSL -DCK_PAM \ -DCK_CURSES -DCK_POSIX_SIG -DTCPSOCKET -DLINUXFSSTND -DHAVE_CRYPT_H \ $(K5INC) $(K5INC)/krb5 $(SRPINC) $(SSLINC) \ $(KFLAGS)" "LNKFLAGS = $(LNKFLAGS)" \ "LIBS = $(SRPLIB) $(K5LIB) $(SSLLIB) \ -lm -lncurses -ltermcap -lsrp \ -lkrb4 -lssl -lkrypto -lcrypto -lgssapi_krb5 \ -lkrb5 -lcom_err -lk5crypto -lcrypt -lresolv -lpam -ldl" #Linux on Intel PC with Cygnus or MIT Kerberos 5 1.2.2, SRP, OpenSSL # with ZLIB and PAM # # libsrp.a should be build with OpenSSL # Requires the Kerberos 1.2.2 be compiled with KRB4 compatibility. # If you have OpenSSL 0.9.7 or later, add -DOPENSSL_097 to KFLAGS. linux+krb5+krb4+srp+openssl+zlib+pam: @echo 'Making C-Kermit $(CKVER) for Linux on i386 with KRB,SRP,SSL...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} "CC = gcc" "CC2 = gcc" \ "CFLAGS = -g -O -funsigned-char -pipe -DPOSIX -DLINUX -DNOCOTFMC \ -DCK_AUTHENTICATION -DCK_SRP -DCK_KERBEROS -DKRB5 -DKRB4 -DKRB524 \ -DCK_ENCRYPTION -DCK_CAST -DCK_DES -DLIBDES -DCK_SSL -DCK_PAM -DZLIB \ -DCK_CURSES -DCK_POSIX_SIG -DTCPSOCKET -DLINUXFSSTND -DHAVE_CRYPT_H \ $(K5INC) $(K5INC)/krb5 $(SRPINC) $(SSLINC) \ $(KFLAGS)" "LNKFLAGS = $(LNKFLAGS)" \ "LIBS = $(SRPLIB) $(K5LIB) $(SSLLIB) \ -lm -lncurses -ltermcap -lsrp \ -lkrb4 -lssl -lkrypto -lcrypto -lgssapi_krb5 \ -lkrb5 -lcom_err -lk5crypto -lcrypt -lresolv -lpam -ldl -lz" #Linux on Intel PC with Cygnus or MIT Kerberos 5 1.2.2, SRP, OpenSSL # with ZLIB, Shadow Passwords, and PAM # # libsrp.a should be build with OpenSSL # Requires the Kerberos 1.2.2 be compiled with KRB4 compatibility. # If you have OpenSSL 0.9.7 or later, add -DOPENSSL_097 to KFLAGS. linux+krb5+krb4+srp+openssl+zlib+shadow+pam: @echo 'Making C-Kermit $(CKVER) for Linux on i386 with KRB,SRP,SSL...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} "CC = gcc" "CC2 = gcc" \ "CFLAGS = -g -O -funsigned-char -pipe -DPOSIX -DLINUX -DNOCOTFMC \ -DCK_AUTHENTICATION -DCK_SRP -DCK_KERBEROS -DKRB5 -DKRB4 -DKRB524 \ -DCK_ENCRYPTION -DCK_CAST -DCK_DES -DLIBDES -DCK_SSL -DCK_PAM -DZLIB \ -DCK_CURSES -DCK_POSIX_SIG -DTCPSOCKET -DLINUXFSSTND -DHAVE_CRYPT_H \ -DCK_SHADOW $(K5INC) $(K5INC)/krb5 $(SRPINC) $(SSLINC) \ $(KFLAGS)" "LNKFLAGS = $(LNKFLAGS)" \ "LIBS = $(SRPLIB) $(K5LIB) $(SSLLIB) \ -lm -lncurses -ltermcap -lsrp -lkrypto \ -lkrb4 -lssl -lcrypto -lgssapi_krb5 \ -lkrb5 -lcom_err -lk5crypto -lcrypt -lresolv -lpam -ldl -lz" #Linux on Intel PC with Cygnus or MIT Kerberos 5 1.2.2, OpenSSL # with Shadow Passwords, PAM # # Requires the Kerberos 1.2.2 be compiled with KRB4 compatibility. linux+krb5+krb4+openssl+shadow+pam: @echo 'Making C-Kermit $(CKVER) for Linux on i386 with KRB,SSL,...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} "CC = gcc" "CC2 = gcc" \ "CFLAGS = -g -O -funsigned-char -pipe -DPOSIX -DLINUX -DNOCOTFMC \ -DCK_AUTHENTICATION -DCK_KERBEROS -DKRB5 -DKRB4 -DKRB524 \ -DCK_ENCRYPTION -DCK_CAST -DCK_DES -DLIBDES -DCK_SSL -DCK_PAM \ -DCK_CURSES -DCK_POSIX_SIG -DTCPSOCKET -DLINUXFSSTND -DHAVE_CRYPT_H \ -DCK_SHADOW $(K5INC) $(K5INC)/krb5 $(SSLINC) \ $(KFLAGS)" "LNKFLAGS = $(LNKFLAGS)" \ "LIBS = $(K5LIB) $(SSLLIB) \ -lm -lncurses -ltermcap \ -lkrb4 -lssl -lcrypto -lgssapi_krb5 \ -lkrb5 -lcom_err -lk5crypto -lcrypt -lresolv -lpam -ldl" #Linux on Intel PC with Cygnus or MIT Kerberos 5 1.2.2, OpenSSL # with ZLIB, Shadow Passwords, PAM # # libsrp.a should be build with OpenSSL # Requires the Kerberos 1.2.2 be compiled with KRB4 compatibility. # If you have OpenSSL 0.9.7 or later, add -DOPENSSL_097 to KFLAGS. linux+krb5+krb4+openssl+zlib+shadow+pam: @echo 'Making C-Kermit $(CKVER) for Linux on i386 with KRB,SRP,SSL...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} "CC = gcc" "CC2 = gcc" \ "CFLAGS = -g -O -funsigned-char -pipe -DPOSIX -DLINUX -DNOCOTFMC \ -DCK_AUTHENTICATION -DCK_KERBEROS -DKRB5 -DKRB4 -DKRB524 \ -DCK_ENCRYPTION -DCK_CAST -DCK_DES -DLIBDES -DCK_SSL -DCK_PAM -DZLIB \ -DCK_CURSES -DCK_POSIX_SIG -DTCPSOCKET -DLINUXFSSTND -DHAVE_CRYPT_H \ -DCK_SHADOW $(K5INC) $(K5INC)/krb5 $(SSLINC) \ $(KFLAGS)" "LNKFLAGS = $(LNKFLAGS)" \ "LIBS = $(K5LIB) $(SSLLIB) \ -lm -lncurses -ltermcap \ -lkrb4 -lssl -lcrypto -lgssapi_krb5 \ -lkrb5 -lcom_err -lk5crypto -lcrypt -lresolv -lpam -ldl -lz" #Red Hat 9 - full install includes Kerberos 5 (4 compat), PAM, SSL. #Also works around bug in curses in which terminal goes dead after #returning from file-transfer display. Assumes OpenSSL 0.9.7 or later. redhat9: @echo "Building SECURE Kermit for Red Hat 9.0..." $(MAKE) linux+krb5+krb4+openssl+zlib+shadow+pam \ KTARGET=$${KTARGET:-$(@)} "KFLAGS = -DRH90 -DOPENSSL_097 $(KFLAGS)" #Ditto plus SRP (which is not normally included with RH Linux). redhat9+srp: @echo "Building SECURE Kermit for Red Hat 9.0..." $(MAKE) linux+krb5+krb4+srp+openssl+zlib+shadow+pam \ KTARGET=$${KTARGET:-$(@)} "KFLAGS = -DRH90 -DOPENSSL_097 $(KFLAGS)" #For Red Hat AS 2.1 with OpenSSL redhat21+ssl: @echo "Building SECURE Kermit for Red Hat 2.1..." $(MAKE) linux+openssl+zlib+shadow+pam \ KTARGET=$${KTARGET:-$(@)} "KFLAGS = $(KFLAGS)" #Red Hat Linux 8.0 - full install includes Kerberos 5 (4 compat), PAM, SSL. #Also works around bug in curses in which terminal goes dead after #returning from file-transfer display. redhat80: @echo "Building SECURE Kermit for Red Hat 8.0..." $(MAKE) linux+krb5+krb4+openssl+zlib+shadow+pam \ KTARGET=$${KTARGET:-$(@)} "KFLAGS = -DRH80 $(KFLAGS)" redhat80+srp: @echo "Building SECURE Kermit for Red Hat 8.0..." $(MAKE) linux+krb5+krb4+srp+openssl+zlib+shadow+pam \ KTARGET=$${KTARGET:-$(@)} "KFLAGS = -DRH80 $(KFLAGS)" #Red Hat Linux 7.3 - full install includes Kerberos 5 (4 compat), PAM, SSL. #Also works around bug in curses in which terminal goes dead after #returning from file-transfer display. redhat73: @echo "Building SECURE Kermit for Red Hat 7.3..." $(MAKE) linux+krb5+krb4+openssl+zlib+shadow+pam \ KTARGET=$${KTARGET:-$(@)} "KFLAGS = -DRH73 $(KFLAGS)" redhat73+srp: @echo "Building SECURE Kermit for Red Hat 7.3..." $(MAKE) linux+krb5+krb4+srp+openssl+zlib+shadow+pam \ KTARGET=$${KTARGET:-$(@)} "KFLAGS = -DRH73 $(KFLAGS)" #Red Hat Linux 7.2 - full install includes Kerberos 5 (4 compat), PAM, SSL. #Also works around bug in curses in which terminal goes dead after #returning from file-transfer display. redhat72: @echo "Building SECURE Kermit for Red Hat 7.2..." $(MAKE) linux+krb5+krb4+openssl+zlib+shadow+pam \ KTARGET=$${KTARGET:-$(@)} "KFLAGS = -DRH72 $(KFLAGS)" redhat72+srp: @echo "Building SECURE Kermit for Red Hat 7.2..." $(MAKE) linux+krb5+krb4+srp+openssl+zlib+shadow+pam \ KTARGET=$${KTARGET:-$(@)} "KFLAGS = -DRH72 $(KFLAGS)" #Red Hat Linux 7.1 - full install includes Kerberos 5 (4 compat), PAM, SSL. #Also works around bug in curses in which terminal goes dead after #returning from file-transfer display. redhat71: @echo "Building SECURE Kermit for Red Hat 7.1..." $(MAKE) linux+krb5+krb4+openssl+zlib+shadow+pam \ KTARGET=$${KTARGET:-$(@)} "KFLAGS = -DRH71 $(KFLAGS)" redhat71+srp: @echo "Building SECURE Kermit for Red Hat 7.1..." $(MAKE) linux+krb5+krb4+srp+openssl+zlib+shadow+pam \ KTARGET=$${KTARGET:-$(@)} "KFLAGS = -DRH71 $(KFLAGS)" #Linux on Intel PC with Cygnus or MIT Kerberos 5 1.2.2, OpenSSL # with ZLIB and PAM and Shadow passwords linux+krb5+openssl+zlib+shadow+pam: @echo 'Making C-Kermit $(CKVER) for Linux on i386 with KRB5,SSL...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} "CC = gcc" "CC2 = gcc" \ "CFLAGS = -g -O -funsigned-char -pipe -DPOSIX -DLINUX -DNOCOTFMC \ -DCK_AUTHENTICATION -DCK_KERBEROS -DKRB5 -DCK_SHADOW -DHAVE_PTMX \ -DCK_ENCRYPTION -DCK_CAST -DCK_DES -DLIBDES -DCK_SSL -DCK_PAM -DZLIB \ -DCK_CURSES -DCK_POSIX_SIG -DTCPSOCKET -DLINUXFSSTND -DHAVE_CRYPT_H \ $(K5INC) $(K5INC)/krb5 $(SSLINC) \ $(KFLAGS)" "LNKFLAGS = $(LNKFLAGS)" \ "LIBS = $(K5LIB) $(SSLLIB) \ -lm -lncurses -ltermcap -lssl -lcrypto -lgssapi_krb5 \ -lkrb5 -lcom_err -lk5crypto -lcrypt -lresolv -lpam -ldl -lz" linuxnotcp: $(MAKE) linux KTARGET=$${KTARGET:-$(@)} "KFLAGS = -DNONET $(KFLAGS)" # "make linuxnotcp" with lcc (see http://www.cs.princeton.edu/software/lcc) # lcc does not understand various gcc extensions: # "__inline__" -- can be eliminated by adding "-D__inline__=" # "__asm__ and "long long" -- in header files, should be surrounded by # "#ifndef(__STRICT_ANSI__)"/"#endif" # however, TCP requires some __asm__ functions, so cannot be compiled linuxnotcp-lcc: @echo 'Making C-Kermit $(CKVER) for Linux with lcc ...' @echo 'Read comments in makefile for additional information.' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} "CC = lcc" "CC2 = lcc" \ "CFLAGS = -DLINUX -DPOSIX -DCK_CURSES -DCK_POSIX_SIG \ -UTCPSOCKET -DLINUXFSSTND -DNOLEARN $(KFLAGS)" \ "LNKFLAGS = $(LNKFLAGS)" "LIBS = -lcurses -ltermcap" # Linux 0.99.14 thru 1.0 with gcc, dynamic libraries, curses, TCP/IP. # For Linux 1.2 or later, use "make linux" (above). # # -DLINUXFSSTND (Linux File System Standard) gives UUCP lockfile /var/lock with # string pid. Remove this and get /usr/spool/uucp with int pid, which was used # in early Linux versions. # # If you get compiler errors regarding , add -DNOHISPEED. # # -DCK_POSIX_SIG (POSIX signal handling) is good for Linux releases back to at # least 0.99.14; if it causes trouble for you, just remove it. # # -DCK_CURSES: Here we link with the regular curses library. But you should # be using ncurses. Internally, the ckuusx.c module includes , but # this really should be . Thus if you have the new curses # material, you should either install it with the standard names, or else # create symbolic links from the standard names to the new ones. If you get # compile-time errors complaining about data definitions in termcap.h, it # means you have new kernel material mixed with older libc header files. To # fix, add "#include " to the file. Or if all this is # too confusing, create a new makefile entry based on this one, but with # -DCK_CURSES removed from CFLAGS and the entire LIBS= clause removed. # # But wait, there's more. On most Linux systems, -ltermcap must be included # in LIBS. But on others, the linker complains that libtermcap can't be # found. In that case, try removing -ltermcap from LIBS=. # # But wait, there's more. The format of the PID string in the UUCP lockfile # changed between Linux FSSTND 1.0 and 1.2. In the earlier standard, it had # leading zeros; in the second, it has leading spaces. By default this entry # uses the newer standard. To force the older one, add -DFSSTND10. # # "The nice thing about the Linux standard is there are so many to choose from" # # NOTE: Remove -DBIGBUFOK for small-memory or limited-resource systems. linux10: @echo 'Making C-Kermit $(CKVER) for Linux 1.0 or earlier...' @echo 'IMPORTANT: Read the comments in the linux section of the' @echo 'makefile if you get compilation or link errors.' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} "CC = gcc" "CC2 = gcc" \ "CFLAGS = -O -DPOSIX -DCK_CURSES -DCK_POSIX_SIG -DLINUX \ -DTCPSOCKET -DLINUXFSSTND -DOLINUXHISPEED -DNOLEARN $(KFLAGS)" \ "LNKFLAGS = $(LNKFLAGS)" "LIBS = -lcurses -ltermcap" #This version was used for Linux prior to C-Kermit 6.0.192. #Now the "Linux File System Standard" is considered standard, ditto TCP/IP. linuxold: @echo 'Making C-Kermit $(CKVER) for Linux...' @echo 'For FSSTND-recommended UUCP lockfiles, use:' @echo ' make linux "KFLAGS=-DLINUXFSSTND".' @echo 'Read comments in makefile for additional options.' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} "CC = gcc" "CC2 = gcc" \ "CFLAGS = -O -DLINUX -DPOSIX -DCK_CURSES -DCK_POSIX_SIG -DNOLEARN \ $(KFLAGS)" "LNKFLAGS = $(LNKFLAGS)" "LIBS = -lcurses -ltermcap" # ::END_OLD_LINUX_TARGETS:: # LynxOS 2.2 with GCC compiler, TCP/IP and fullscreen display. # Probably also works with Lynx 2.1, and maybe even Lynx 2.0. # -X means use termios serial drivers rather than BSD4.3-style sgtty drivers. # If you have trouble with this, try "make bsd KFLAGS=-DNOFDZERO". lynx: @echo 'Making C-Kermit $(CKVER) for LynxOS 2.2 with TCP/IP' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} "CC = gcc" "CC2 = gcc" \ "CFLAGS= -O -DPOSIX -DDIRENT -DSETREUID -DCK_CURSES -DTCPSOCKET \ -DCK_ANSIC -DLYNXOS -DNOLEARN" "LNKFLAGS = -X" "LIBS = -lcurses -lbsd" lynx22: $(MAKE) lynx KTARGET=$${KTARGET:-$(@)} "KFLAGS=$(KFLAGS)" # LynxOS 2.1 with GCC compiler 1.40 and TCP/IP. lynx21: @echo 'Making C-Kermit $(CKVER) for LynxOS 2.1 with TCP/IP' $(MAKE) kermit KTARGET=$${KTARGET:-$(@)} "CC = gcc" "CC2 = gcc" \ "CFLAGS= -O -DSETREUID -DTCPSOCKET -DCK_ANSIC -DBSD4 -DLYNXOS" \ "LIBS = -lbsd" #SCO Xenix 2.2.1 for IBM PC, XT, PS2/30, or other 8088 or 8086 machine #Should this not work, try some of the tricks from sco286. #NOTE: -DRENAME is omitted for early SCO Xenix releases because it didn't #exist, or its semantics were different from the later POSIX-compliant #version of rename(). sco86: @echo 'Making C-Kermit $(CKVER) for SCO Xenix/86...' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DXENIX -DNOFILEH -DNOIKSD -DNOUNICODE -DNOLEARN \ $(KFLAGS) -Dunix -F 3000 -i -M0me" \ "LNKFLAGS = -F 3000 -i -s -M0me" "LIBS = -lx" #SCO Xenix/286 2.2.1, e.g. for IBM PC/AT, PS/2 Model 50, etc. #Reportedly, this "make" can fail simply because of the size of this #makefile. If that happens, use "makeL", or edit out some of the #other entries. No debugging or character-set translation. sco286: @echo 'Making C-Kermit $(CKVER) for SCO Xenix/286...' @echo 'If make fails, try using makeL.' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -xenix -s -O -LARGE -DXENIX -DNOFILEH -Dunix -DRDCHK -DNAP \ -DNOIKSD -DNODEBUG -DNOTLOG -DNOCSETS -DNOLEARN \ $(KFLAGS) -F 3000 -i -M2let16" \ "LIBS = -lx" "LNKFLAGS = -xenix -s -O -LARGE -F 3000 -i -M2let16" #SCO Xenix/286 2.2.1, e.g. for IBM PC/AT, PS/2 Model 50, etc. #As above, but with HDBUUCP (This one might need fixing -- see sco286). sco286hdb: @echo 'Making C-Kermit $(CKVER) for SCO Xenix/286 with HDB UUCP...' @echo 'If make fails, try using makeL.' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -s -O -LARGE -DXENIX -DNOFILEH -Dunix -DRDCHK -DNAP \ -DHDBUUCP -DNOIKSD -DNOUNICODE -DNOLEARN \ $(KFLAGS) -F 3000 -i -M2let32" \ "LIBS = -lx" "LNKFLAGS = -s -O -LARGE -F 3000 -i -M2let32" #SCO Xenix/386 2.2.2 and 2.2.3 sco386: @echo 'Making C-Kermit $(CKVER) for SCO Xenix/386 2.2.2...' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DXENIX -DNOFILEH -DNOIKSD -DNOREDIRECT -DNOLEARN \ -Dunix -DRDCHK -DNAP -DNOUNICODE $(KFLAGS) -Otcl -M3e" \ "LNKFLAGS = -s" "LIBS = -lx" #SCO XENIX/386 2.2.3 with Excelan TCP/IP + curses. # NOTE: This one might need some work in C-Kermit 6.0. # You might need to include /usr/include/sys/types.h # containing "typedef char *caddr_t;". Then at least it compiles. sco386netc: @echo 'Making C-Kermit $(CKVER) for SCO Xenix/386 2.2.3 + Excelan TCP' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -I/usr/include/exos -DXENIX -DCK_CURSES -DNOUNICODE \ -Dunix -DRDCHK -DNAP -DTCPSOCKET -DEXCELAN -DNOJC -DNOMKDIR -DNOFILEH \ -DNOLEARN -DNOREDIRECT -DNOIKSD -DNO_DNS_SRV $(KFLAGS) -Otcl -M3e" \ "LNKFLAGS = -s" "LIBS = -lc -lx -lsocket -lcurses -ltermcap" #SCO XENIX/386 2.3.3 with gcc 1.37 or later... sco386gcc: @echo 'Making C-Kermit $(CKVER) for SCO Xenix/386 2.3.3, gcc...' @echo 'Add -D_NO_PROTOTYPE if you have trouble with Xenix header files' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} "CC = gcc" "CC2 = gcc" \ "CFLAGS= -O -DXENIX -DSVR3 -DNOFILEH -DHDBUUCP -DRDCHK -DNAP \ -DNOJC -DNODEBUG -DNOUNICODE -DNOLEARN $(KFLAGS) \ -traditional -fpcc-struct-return -fstrength-reduce \ -DM_BITFIELDS -DM_COFF -DM_I386 -DM_I86 -DM_I86SM \ -DM_INTERNAT -DM_SDATA -DM_STEXT -DM_SYS3 -DM_SYS5 \ -DM_SYSIII -DM_SYSV -DM_WORDSWAP -DM_XENIX -DNOIKSD -DNOREDIRECT \ -DPWID_T=int " "LNKFLAGS = -s" "LIBS = -lx" #As above, but with curses... sco386gccc: @echo 'Making C-Kermit $(CKVER) for SCO Xenix/386 2.3.3, gcc...' @echo 'Add -D_NO_PROTOTYPE if you have trouble with Xenix header files' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} "CC = gcc" "CC2 = gcc" \ "CFLAGS= -O -DXENIX -DSVR3 -DNOFILEH -DHDBUUCP -DRDCHK -DNAP \ -DNOJC -DNODEBUG -DCK_CURSES -DNOUNICODE -DNOLEARN $(KFLAGS) \ -traditional -fpcc-struct-return -fstrength-reduce \ -DM_BITFIELDS -DM_COFF -DM_I386 -DM_I86 -DM_I86SM -DNOREDIRECT \ -DM_INTERNAT -DM_SDATA -DM_STEXT -DM_SYS3 -DM_SYS5 \ -DM_SYSIII -DM_SYSV -DM_WORDSWAP -DM_XENIX -DNOIKSD \ -DPWID_T=int " "LNKFLAGS = -s" "LIBS = -lx -lcurses -ltermlib" #SCO UNIX (and ODT) entries... # #NOTE: All SCO UNIX entry LIBS should have "-lc_s -lc -lx" IN THAT ORDER (if #shared C library is desired), or else "-lc -lx" IN THAT ORDER. Use shared C #libraries to save memory, but then don't expect to run the resulting binary #on a different machine. When using -lc_s, you must also use -lc, because the #shared C library does not contain all of libc.a. And in all cases, -lc must #ALWAYS precede -lx. # #ANOTHER NOTE: -DRENAME is included in all SCO UNIX entries. Remove it if it #causes trouble. No harm is done by removing it (see ckuins.txt). # #AND ANOTHER: In theory, it should be possible to run SCO UNIX binaries on #SCO Xenix 2.3 and later. In practice, this might not work because of the #libraries, etc. Also, don't add the -link -z switch (which is supposed to #root out references to null pointers) because it makes UNIX binaries core #dump when they are run under Xenix. #NOTE: -Otcl removed and replaced by -O, since -Otcl produced incorrect code. #SCO UNIX/386 3.2.0, 3.2.1, and SCO Xenix 2.3.x sco3r2: @echo 'Making C-Kermit $(CKVER) for SCO UNIX/386 3.2.0 or 3.2.1 ...' @echo 'Warning: If make blows up, edit the makefile to join' @echo 'the following three continued lines into one line.' @echo 'Also, remove -DRENAME if _rename unresolved at link time.' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DXENIX -DSVR3 -DNOFILEH -DHDBUUCP -DRDCHK -DNAP -DNOLEARN \ -DRENAME -DNOIKSD -DNOJC $(KFLAGS) -O" \ "LNKFLAGS = -s" "LIBS = -lc -lx" #SCO UNIX/386 3.2.0 and SCO Xenix 2.3.x with Excelan TCP/IP support. #In case of compilation or runtime problems, try adding #"-DUID_T=int -DGID_T=int" to the CFLAGS. If that doesn't work, try #"-DUID_T=uid_t -DGID_T=gid_t". sco3r2net: @echo 'Making C-Kermit $(CKVER) for SCO UNIX/386 / Excelan...' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -I/usr/include/exos -DXENIX -DSVR3 -DNOFILEH -DNOLEARN \ -DHDBUUCP -DRDCHK -DNAP -DRENAME -DTCPSOCKET -DEXCELAN -DNOJC \ -DNOIKSD -DNOREDIRECT $(KFLAGS) -O" \ "LNKFLAGS = -s" "LIBS = -lc -lx -lsocket" #SCO UNIX/386 3.2.0 and SCO Xenix 2.3.x with Excelan TCP/IP support. #As above, with curses added. sco3r2netc: @echo 'Making C-Kermit $(CKVER) for SCO UNIX/386 / Excelan / curses...' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -I/usr/include/exos -DXENIX -DSVR3 -DNOFILEH -DNOLEARN \ -DHDBUUCP -DRDCHK -DNAP -DTCPSOCKET -DEXCELAN -DNOJC $(KFLAGS) \ -DRENAME -DCK_CURSES -DNOREDIRECT -DNOIKSD -O" "LNKFLAGS = -s" \ "LIBS = -lc -lx -lsocket -lcurses -ltermcap" #SCO UNIX 3.2.x or SCO Xenix 2.3.x with Racal InterLan TCP/IP support # Extra compile flags for other version of Racal InterLan TCP/IP: # Xenix286/NP621-286, use -Ml -DPARAMH -DINTERLAN -Di286 -DSYSV # Xenix386/NP621-386, use -DPARAMH -DINTERLAN -Di386 -DSYSV # ISC386ix/NP622I, use -DSYSV -Di386 # SCO Unix3.2/NP622S, use -DSYSV -Di386 -DSCO_UNIX # AT&T SVR3.2/NP622A, use -DSYSV -Di386 -DATT sco3r2netri: @echo 'Making C-Kermit $(CKVER) for SCO UNIX/386 / Racal InterLan...' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -I/usr/include/interlan -DXENIX -DNOFILEH -DHDBUUCP \ -DSVR3 -DRDCHK -DNAP -DTCPSOCKET -DPARAMH -DINTERLAN -Di386 -DSYSV \ -DRENAME -DNOREDIRECT -DNOIKSD -DNOJC -DNOLEARN $(KFLAGS) -Otcl -M3e" \ "LNKFLAGS = -s" "LIBS = -lc -lx -ltcp" # SCO XENIX/386 2.3.3 SysV with SCO TCP/IP # System V STREAMS TCP developed by Lachman Associates Inc and # Convergent Technologies. # -DRENAME removed since some reports indicate it is not supported # (whereas others say it is.) sco3r2lai: @echo 'Making C-Kermit $(CKVER) for SCO XENIX/386 2.3.3 + TCP/IP...' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DLAI_TCP -Di386 -DXENIX -DSVR3 -DNOFILEH -DHDBUUCP -DRDCHK \ -DNAP -DTCPSOCKET -DPWID_T=int -DNOREDIRECT -DNOIKSD -DNOLEARN \ $(KFLAGS) -Otcl -i -M3e" \ "LNKFLAGS = -i -s" "LIBS = -lc -lx -lsocket" sco3r2laic: @echo 'Making C-Kermit $(CKVER) for SCO XENIX/386 2.3.3 + TCP/IP...' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DLAI_TCP -Di386 -DXENIX -DSVR3 -DNOFILEH -DHDBUUCP -DRDCHK \ -DNAP -DTCPSOCKET -DCK_ANSIC -DCK_CURSES -DM_TERMINFO -DNOLEARN \ -DPWID_T=int -DNOREDIRECT -DNOIKSD $(KFLAGS) -Otcl -i -M3e" \ "LNKFLAGS = -i -s" "LIBS = -ltinfo -lc -lx -lsocket" #SCO UNIX/386 3.2v2 (POSIX job control), shared libraries. sco3r22: @echo 'Making C-Kermit $(CKVER) for SCO UNIX/386 3.2v2 ...' make wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DXENIX -DSVR3 -DNOFILEH -DHDBUUCP -DRDCHK -DNOLEARN \ -DNAP -DRENAME -DPID_T=pid_t -DPWID_T=int -DDIRENT -DNOIKSD \ -DNOREDIRECT $(KFLAGS) -O" \ "LNKFLAGS = -s" "LIBS = -lc_s -lc -lx" #SCO UNIX/386 3.2v2, POSIX job control, fullscreen file transfer display, #dynamic memory allocation, shared C library sco3r22c: @echo 'Making C-Kermit $(CKVER) for SCO UNIX/386 3.2v2 ...' @echo 'Warning: If make blows up, edit the makefile to join' @echo 'the following four continued lines into one line.' make wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DXENIX -DSVR3 -DNOFILEH -DHDBUUCP -DRDCHK -DNAP -DNOLEARN \ -DCK_CURSES -DDIRENT -DRENAME -DNOREDIRECT -DNOIKSD \ -DPID_T=pid_t -DPWID_T=int $(KFLAGS) -O" \ "LNKFLAGS = -s" "LIBS = -lcurses -lc_s -lc -lx" #SCO UNIX/386 3.2v2 with gcc 1.40 or later (POSIX job control) sco3r22gcc: @echo 'Making C-Kermit $(CKVER) for SCO UNIX/386 3.2v2, gcc' @echo 'Warning: If make blows up, edit the makefile to join' @echo 'the following seven continued lines into one line.' make wermit KTARGET=$${KTARGET:-$(@)} "CC = gcc" \ "CFLAGS= -O -DPOSIX -DXENIX -DSVR3 -DNOFILEH -DHDBUUCP -DRDCHK -DNAP \ -DNOLEARN -DRENAME -traditional -fpcc-struct-return -fstrength-reduce \ -DM_BITFIELDS -DM_COFF -DM_I386 -DM_I86 -DM_I86SM \ -DM_INTERNAT -DM_SDATA -DM_STEXT -DM_SYS3 -DM_SYS5 \ -DM_SYSIII -DM_SYSV -DM_UNIX -DM_WORDSWAP -DM_XENIX -Dunix \ -DPID_T=pid_t -DPWID_T=int -DNOREDIRECT -DNOIKSD $(KFLAGS) " \ "LNKFLAGS = -s" "LIBS = -lc_s -lc -lx" #SCO UNIX/386 3.2v2 (ODT 1.1) (POSIX job control) with SCO TCP/IP, shared libs #Requires SCO TCP/IP or ODT development system for telnet.h, etc. sco3r22net: @echo 'Making C-Kermit $(CKVER) for SCO UNIX/386 3.2.2 + TCP/IP...' @echo 'Warning: If make blows up, edit the makefile to join' @echo 'the following three continued lines into one line.' make xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DXENIX -DSVR3 -DNOFILEH -DHDBUUCP -DRDCHK -DNAP -DTCPSOCKET \ -DRENAME -DPID_T=pid_t -DPWID_T=int -DDIRENT -DNOREDIRECT -DNOIKSD \ $(KFLAGS) -O" "LNKFLAGS = -s" "LIBS = -lsocket -lc_s -lc -lx" #As above, but with curses for fullscreen file transfer display. #Requires SCO TCP/IP or ODT development system for telnet.h, etc. sco3r22netc: @echo 'Making C-Kermit $(CKVER) for SCO UNIX/386 3.2v2 + TCP/IP...' @echo 'Warning: If make blows up, edit the makefile to join' @echo 'the following three continued lines into one line.' make xermit KTARGET=$${KTARGET:-$(@)} "CFLAGS= \ -DXENIX -DSVR3 -DNOFILEH -DHDBUUCP -DRDCHK -DNAP -DTCPSOCKET -DRENAME \ -DCK_CURSES -DDIRENT -DNOIKSD -DNOREDIRECT \ -DPID_T=pid_t -DPWID_T=int -O $(KFLAGS)" \ "LNKFLAGS = -s" "LIBS = -lcurses -lsocket -lc_s -lc -lx" #SCO XENIX 2.3.4, no curses, no TCP/IP, no IKSD. #This one built and tested in C-Kermit 7.0. #lcfp is C library floating-point support. #Use -M3 to generate 32-bit i386 code instead of 16-bit segmented i286 code. #Use -Me to enable MS nonstandard keywords in system headers. #Use -W2 or W3 to increase the warning level. #OK: 2011/06/15 sco234: @echo 'Making C-Kermit $(CKVER) for SCO XENIX 2.3.4...' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DSCO32 -DXENIX -DNOFILEH -DHDBUUCP -DRDCHK -DNOLEARN \ -DNAP -DNOJC -DNOCOTFMC -DNOIKSD -DNOREDIRECT -DNOTNCODE -DNOGFTIMER \ -DNOTIMEVAL -DNOTIMEZONE -DNOSYMLINK -DSCO234 -DDCLGETCWD $(KFLAGS) \ -Otcl" "LNKFLAGS = -s" "LIBS = -lcfp -lc -lx" #SCO XENIX 2.3.4, no TCP/IP, no IKSD, but with curses. # Built and tested in C-Kermit 7.0. # Note: XENIX 2.3.4 does not have newterm() so no point in adding -DCK_NEWTERM. sco234c: @echo 'Making C-Kermit $(CKVER) for SCO XENIX 2.3.4 + curses...' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DSCO32 -DXENIX -DNOFILEH -DHDBUUCP -DRDCHK -DNOLEARN \ -DNAP -DNOJC -DNOCOTFMC -DNOIKSD -DNOREDIRECT -DNOTNCODE -DNOGFTIMER \ -DNOTIMEVAL -DNOTIMEZONE -DNOSYMLINK -DCK_CURSES -DSCO234 \ -DDCLGETCWD $(KFLAGS) -Otcl" \ "LNKFLAGS = -s" "LIBS = -lcfp -lc -ltinfo -lx" #SCO XENIX 2.3.4 with SCO TCP/IP and curses, no IKSD. # Built and tested in C-Kermit 7.0. TCP/IP works and curses works. # Previous versions of this target included -lmalloc, but this caused "error: # " _calloc : symbol defined more than once" at link time so I removed it. # Results are likely to vary depending on exactly which version of the SDK # and TCP/IP SDK you have. sco234netc: @echo 'Making C-Kermit $(CKVER) for SCO XENIX 2.3.4 + TCP + curses...' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DSCO32 -DXENIX -DNOFILEH -DHDBUUCP -DRDCHK -DNOLEARN \ -DNAP -DNOJC -DNOCOTFMC -DNOIKSD -DNOREDIRECT -DNOTNCODE -DNOGFTIMER \ -DNOTIMEVAL -DNOTIMEZONE -DNOSYMLINK -DCK_CURSES -DSCO234 \ -DDCLGETCWD -DTCPSOCKET -DNO_DNS_SRV $(KFLAGS) -Otcl" \ "LNKFLAGS = -s" "LIBS = -ltinfo -lsocket -lcfp -lc -lx" # SCO 3.2v4.x targets... # NOTE: Add -DDCLPOPEN and/or -DDCLFDOPEN to anySCO 3.2v4.x non-gcc entries # that complain about fdopen() or popen() at compile time. They compile OK # without these flags as of July 1999. However, the gcc entries seem to # need them, at least for gcc 2.7.2.2. # NOTE 2: To enable IKSD support, add: # -DCK_LOGIN -DNOGETUSERSHELL -DNOINITGROUPS # to CFLAGS (not tested). #SCO UNIX/386 3.2v4 (POSIX job control), curses, ANSI C compilation, # (EAFS) file system. Remove -lmalloc if it causes trouble. It was #put there to avoid core dumps caused by regular libc.a malloc. Add -J to make #all chars unsigned. This version uses select() for CONNECT and also has #high-precision timers and so might not work on non-TCP systems, in which case #sco32v4ns should be used instead. # If you get _ftime redefinition_ complaint, try adding -DODT30 to CFLAGS. sco32v4: @echo 'Making C-Kermit $(CKVER) for SCO UNIX/386 3.2v4...' make xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DCK_SCO32V4 -DNOFILEH -DHDBUUCP -DCK_CURSES -DM_TERMINFO \ -DNOANSI -DSELECT -DNOIKSD -DDCLGETCWD -NOLSTAT \ -DNOLINKBITS -DDCLGETCWD $(KFLAGS) -O" \ "LNKFLAGS = -s" "LIBS = -lcurses -lmalloc -lsocket -lc_s -lc -lx" # As above, but with no dependence on sockets library or select(). sco32v4ns: @echo 'Making C-Kermit $(CKVER) for SCO UNIX/386 3.2v4...' @echo 'No select() and no sockets library.' make wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DCK_SCO32V4 -DNOFILEH -DHDBUUCP -DCK_CURSES -DM_TERMINFO \ -DNOANSI -DNOIKSD -DNOGFTIMER -DCK_POLL -DNAP -DDCLGETCWD -DNOLSTAT \ -DNOLINKBITS -DDCLGETCWD -DNOLEARN -O $(KFLAGS)" \ "LNKFLAGS = -s" "LIBS = -lcurses -lmalloc -lc_s -lc -lx" #SCO UNIX/386 3.2v4 (POSIX job control), TCP/IP, curses, ANSI C compilation, # (EAFS) file system. With DIRENT, -lc must come before -lx. #Reportedly it's OK to add -DCK_REDIR and -DCK_WREFRESH, and to remove -lc_s. #Requires SCO TCP/IP development system or ODT for telnet.h, etc. #See sco32v4 above for additional comments. #NOTE: No more room for -Dxxx -- 25 seems to be the limit. Move some to #ckcdeb.h or somewhere... sco32v4net: @echo 'Making C-Kermit $(CKVER) for SCO UNIX/386 3.2v4...' @echo 'If you get _ftime redefinition_ complaint,' @echo 'use make sco-odt30.' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DNOFILEH -DHDBUUCP -DTCPSOCKET -DCK_ANSIC -DCK_CURSES \ -DNAP -DCK_WREFRESH -DNOLINKBITS -D_IBCS2 -DSELECT -DNOLSTAT \ -DDCLGETCWD -DCK_SCO32V4 -DNOIKSD -O \ $(KFLAGS)" "LNKFLAGS = $(LNKFLAGS) -s" \ "LIBS = $(LIBS) -lcurses -lsocket -lmalloc -lsocket -lc_s -lc -lx" #SCO UNIX/386 3.2v4 with gcc 1.40 or later, POSIX job control. #Also see comments in sco32r4 entry. sco32v4gcc: make xermit KTARGET=$${KTARGET:-$(@)} "CC = gcc" \ "CFLAGS= -O -DNOFILEH -DHDBUUCP -DNOANSI -DCK_CURSES -DM_TERMINFO \ -traditional -fpcc-struct-return -fstrength-reduce -funsigned-char \ -D_KR -D_NO_PROTOTYPE -D_SVID -DNOIKSD -DCK_SCO32V4 -DNOLINKBITS \ -DM_BITFIELDS -DM_COFF -DM_I386 -DM_I86 -DM_I86SM -DSELECT -DNOLSTAT \ -DM_INTERNAT -DM_SDATA -DM_STEXT -DM_SYS3 -DM_SYS5 -DDCLGETCWD \ -DM_SYSIII -DM_SYSV -DM_UNIX -DM_WORDSWAP -DM_XENIX -Dunix \ -DDCLPOPEN -DDCLFDOPEN $(KFLAGS) " \ "LNKFLAGS = -s" "LIBS = -lcurses -lsocket -lc_s -lc -lx" #SCO UNIX/386 3.2v4 (POSIX job control), TCP/IP, curses, ANSI C compilation, #Requires SCO TCP/IP or ODT development system for telnet.h, etc. # (EAFS) file system. With DIRENT, -lc must come before -lx. #gcc 1.40 or later. Also see comments in sco32r4 entry. sco32v4netgcc: make xermit KTARGET=$${KTARGET:-$(@)} "CC = gcc" "CC2=gcc" \ "CFLAGS= -O2 -DNOFILEH -DHDBUUCP -DSELECT -DNOLSTAT \ -DNOANSI -DTCPSOCKET -DCK_CURSES -DM_TERMINFO \ -D_KR -D_NO_PROTOTYPE -D_SVID -DNOIKSD -DCK_SCO32V4 -DNOLINKBITS \ -DM_BITFIELDS -DM_COFF -DM_I386 -DM_I86 -DM_I86SM -DDCLGETCWD \ -DM_INTERNAT -DM_SDATA -DM_STEXT -DM_SYS3 -DM_SYS5 \ -DM_SYSIII -DM_SYSV -DM_UNIX -DM_WORDSWAP -DM_XENIX -Dunix \ -DDCLPOPEN -DDCLFDOPEN $(KFLAGS)" \ "LNKFLAGS = -s" "LIBS = -lcurses -lsocket -lc_s -lc -lx" #As above but with bgcc BOUNDS CHECKING (for developers only). -lcheck has #bounds-checking replacements for malloc, memcpy, bcopy, etc, so must come #before -lsocket and -lc. sco32v4netbgcc: make xermit KTARGET=$${KTARGET:-$(@)} \ "CC = bgcc -pipe -m386" "CC2=bgcc -pipe -m386" \ "CFLAGS= -O1 -g -DNOFILEH -DHDBUUCP -DSELECT \ -DNOANSI -DTCPSOCKET -DCK_CURSES -DM_TERMINFO \ -D_KR -D_NO_PROTOTYPE -D_SVID -DNOIKSD -DCK_SCO32V4 -DNOLSTAT \ -DM_BITFIELDS -DM_COFF -DM_I386 -DM_I86 -DM_I86SM -DNOLINKBITS \ -DM_INTERNAT -DM_SDATA -DM_STEXT -DM_SYS3 -DM_SYS5 -DDCLGETCWD \ -DM_SYSIII -DM_SYSV -DM_UNIX -DM_WORDSWAP -DM_XENIX -Dunix \ -DDCLPOPEN -DDCLFDOPEN $(KFLAGS) " \ "LNKFLAGS = -g" "LIBS = -lcurses -lcheck -lsocket -lx" sco32v4netnd: @echo sco32v4net with no debug $(MAKE) "MAKE=$(MAKE)" sco32v4net KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=$(KFLAGS) -DNODEBUG -DNOTLOG" "LIBS=$(LIBS)" sco3r2netnd: @echo sco32v4netnd built for SCO XENIX 2.3 under SCO UNIX... @echo requires copying /lib/386/Slibc.a to /lib/386/Slibc_s.a and @echo getting /lib/386/Slibsocket.a from a XENIX devkit. @echo WARNING: poll/CK_POLL supported only on XENIX 2.3.4 echo For earlier XENIX systems, replace CK_POLL with RDCHK. $(MAKE) "MAKE=$(MAKE)" sco32v4netnd KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=$(KFLAGS) -x2.3 -DNORENAME -DNOSYMLINK" \ "LNKFLAGS = $(LNKFLAGS) -x2.3" \ "LIBS=-ldir -lcfp $(LIBS)" #SCO UNIX/386 3.2v4 (POSIX job control), TCP/IP, curses, ANSI C compilation, # (EAFS) file system. With DIRENT, -lc must come before -lx. #Reportedly it's OK to add -DCK_REDIR and -DCK_WREFRESH, and to remove -lc_s. #Requires SCO TCP/IP development system or ODT for telnet.h, etc. #See sco32v4 above for additional comments. # Note: "xermit" means use the select() version of the CONNECT module. sco32v4netx: @echo 'Making C-Kermit $(CKVER) for SCO UNIX/386 3.2v4...' @echo 'If you get _ftime redefinition_ complaint,' @echo 'use make sco-odt30.' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DNOFILEH -DHDBUUCP -DTCPSOCKET -DCK_ANSIC -DCK_CURSES -DNAP \ -DCK_WREFRESH -DNOLINKBITS -D_IBCS2 -DSELECT -DDCLGETCWD \ -DCK_SCO32V4 -DNOIKSD -DNOLSTAT -O $(KFLAGS)" \ "LNKFLAGS = $(LNKFLAGS) -s" \ "LIBS = $(LIBS) -lcurses -lsocket -lmalloc -lsocket -lc_s -lc -lx" sco32v4netndx: @echo sco32v4netx with no debug $(MAKE) "MAKE=$(MAKE)" sco32v4netx KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=$(KFLAGS) -DNODEBUG -DNOTLOG" "LIBS=$(LIBS)" sco3r2netndx: @echo sco32v4netndx built for SCO XENIX 2.3 under SCO UNIX... @echo requires copying /lib/386/Slibc.a to /lib/386/Slibc_s.a and @echo getting /lib/386/Slibsocket.a from a XENIX devkit. @echo WARNING: poll/CK_POLL supported only on XENIX 2.3.4 echo For earlier XENIX systems, replace CK_POLL with RDCHK. $(MAKE) "MAKE=$(MAKE)" sco32v4netndx KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=$(KFLAGS) -x2.3 -DNORENAME -DNOSYMLINK" \ "LNKFLAGS = $(LNKFLAGS) -x2.3" \ "LIBS=-ldir -lcfp $(LIBS)" sco-odt30: @echo SCO ODT 3.0 $(MAKE) "MAKE=$(MAKE)" sco32v4net KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=$(KFLAGS) -DODT30" #SCO OpenServer 5.0 (SCO UNIX 3.2v5.0) with SCO development tools, no TCP/IP. #SCO OSR5 is much more like standard System V than previous SCO releases. #The SCO development tools include TCP/IP, so this target is only for creating #artificially limited versions of kermit required by site policy rather than #the operating system. NOSYSLOG is included because syslog() requires the #sockets library. sco32v500: @echo Making C-Kermit $(CKVER) for SCO OpenServer Release 5... $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -O -DDIRENT -DHDBUUCP -DSVR4 -DCK_SCOV5 -DCK_RTSCTS \ -DCK_CURSES -DCK_WREFRESH -DCK_NEWTERM -DSELECT -DSELECT_H \ -DNOGETUSERSHELL -DNOLSTAT -DNOLINKBITS -DNOSYSLOG \ $(KFLAGS)" \ "LIBS=-lcurses $(LIBS)" "LNKFLAGS=$(LNKFLAGS)" sco32v5: $(MAKE) "MAKE=$(MAKE)" "KFLAGS=$(KFLAGS)" sco32v500 #SCO OpenServer 5.0 with networking, SCO development tools. #Networking libraries are now provided with the OS. sco32v500net: @echo Making C-Kermit $(CKVER) for SCO OpenServer Release 5... $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -O -DDIRENT -DHDBUUCP -DSVR4 -DCK_SCOV5 -DCK_RTSCTS \ -DCK_CURSES -DCK_WREFRESH -DCK_NEWTERM -DSELECT -DSELECT_H \ -DNOGETUSERSHELL -DNOLSTAT -DNOLINKBITS -DTCPSOCKET \ -DNO_DNS_SRV $(KFLAGS)" \ "LIBS=-lcurses -lsocket $(LIBS)" "LNKFLAGS=$(LNKFLAGS)" sco32v5net: $(MAKE) "MAKE=$(MAKE)" "KFLAGS=$(KFLAGS)" sco32v500net #SCO OpenServer 5.0 with networking and OpenSSL, SCO development tools. #Networking libraries are now provided with the OS. sco32v500net+ssl: @echo Making C-Kermit $(CKVER) for SCO OSR5 with OpenSSL... $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -O -DDIRENT -DHDBUUCP -DSVR4 -DCK_SCOV5 -DCK_RTSCTS \ -DCK_CURSES -DCK_WREFRESH -DCK_NEWTERM -DSELECT -DSELECT_H \ -DNOGETUSERSHELL -DNOLSTAT -DNOLINKBITS -DTCPSOCKET \ -DNO_DNS_SRV -DCK_AUTHENTICATION -DCK_SSL -DCK_TRIGGER \ $(SSLINC) $(SSLLIB) $(KFLAGS)" \ "LIBS=$(SSLLIB) -lcurses -lsocket -lssl -lcrypto $(LIBS)" \ "LNKFLAGS=$(LNKFLAGS)" #SCO OpenServer 5.0 with gcc, no networking. #Note: NOSYSLOG required for non-net entries because it requires sco32v500gcc: @echo Using gcc... $(MAKE) "MAKE=$(MAKE)" sco32v500CC=gcc CC2=gcc \ KTARGET=$${KTARGET:-$(@)} "KFLAGS= $(KFLAGS)" #SCO OpenServer 5.0 with networking, gcc. sco32v500netgcc: @echo TCP/IP networking added - using gcc... $(MAKE) "MAKE=$(MAKE)" sco32v500net CC=gcc CC2=gcc \ KTARGET=$${KTARGET:-$(@)} "KFLAGS=$(KFLAGS)" #SCO OpenServer 5.0 with networking, gcc, elf. sco32v500netgccelf: @echo TCP/IP networking added - using gcc, dynamic elf library $(MAKE) "MAKE=$(MAKE)" sco32v500net "CC=gcc" "CC2=gcc" \ KTARGET=$${KTARGET:-$(@)} "KFLAGS=-O3 -belf" "LNKFLAGS=-belf" sco32v502: $(MAKE) "MAKE=$(MAKE)" sco32v500 KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-DSCO_OSR502 $(KFLAGS)" #SCO OpenServer 5.0.2 with networking, SCO development tools. sco32v502net: @echo TCP/IP networking added... $(MAKE) "MAKE=$(MAKE)" sco32v500net KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-b elf -DSCO_OSR502 $(KFLAGS)" #SCO OpenServer 5.0.4 (SCO UNIX 3.2v5.0.4) with SCO development tools. #Like 5.0, but adds high serial speeds. First POSIX-based SCO version. #Note: the -O flag is deliberately omitted for /bin/cc (= /usr/ccs/bin/cc). sco32v504: @echo Making C-Kermit $(CKVER) for SCO OpenServer Release 5.0.4... $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DDIRENT -DHDBUUCP -DSVR4 -DCK_SCOV5 -DCK_RTSCTS \ -DSCO_OSR504 -b elf -DPOSIX \ -DCK_CURSES -DCK_WREFRESH -DCK_NEWTERM -DSELECT -DSELECT_H \ -DNOGETUSERSHELL -DNOLSTAT -DNOLINKBITS -DNOSYSLOG $(KFLAGS)" \ "LIBS=-lcurses $(LIBS)" "LNKFLAGS=$(LNKFLAGS)" #SCO OpenServer 5.0.4 with gcc, no networking. sco32v504gcc: @echo Using gcc... $(MAKE) "MAKE=$(MAKE)" sco32v504 "CC=gcc" "CC2=gcc" \ KTARGET=$${KTARGET:-$(@)} "KFLAGS= $(KFLAGS)" #SCO OpenServer 5.0.4 with networking. #SCO development tools (/bin/cc = /usr/ccs/bin/cc). #Optimization deliberately suppressed. sco32v504net: @echo Making C-Kermit $(CKVER) for SCO OpenServer Release 5.0.4... $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DDIRENT -DHDBUUCP -DSVR4 -DCK_SCOV5 -DCK_RTSCTS \ -DCK_CURSES -DCK_WREFRESH -DCK_NEWTERM -DSELECT -DSELECT_H \ -DNOGETUSERSHELL -DNOLSTAT -DNOLINKBITS -DTCPSOCKET \ -b elf -DSCO_OSR504 -DPOSIX -DNO_DNS_SRV $(KFLAGS)" \ "LIBS=-lcurses -lsocket $(LIBS)" "LNKFLAGS=$(LNKFLAGS)" #SCO OpenServer 5.0.4 with networking, gcc. sco32v504netgcc: @echo TCP/IP networking added - using gcc... @echo If gcc crashes on ckwart.c then build it by hand: @echo " gcc -o wart -DCK_SCOV5 ckwart.c" $(MAKE) "MAKE=$(MAKE)" sco32v500net "CC=gcc" "CC2=gcc" \ KTARGET=$${KTARGET:-$(@)} "KFLAGS=-DSCO_OSR504 -DPOSIX $(KFLAGS)" #SCO OpenServer 5.0.4 with networking, gcc, elf. sco32v504netgccelf: @echo TCP/IP networking added - using gcc, dynamic elf library $(MAKE) "MAKE=$(MAKE)" sco32v500net "CC=gcc" "CC2=gcc" KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-DSCO_OSR504 -DPOSIX -O3 -belf $(KFLAGS)" \ LNKFLAGS="-belf" #SCO OpenServer 5.0.5 (SCO UNIX 3.2v5.0.5) with SCO /bin/cc. #Like 5.0, but adds high serial speeds. First POSIX-based SCO version. #You might have to add "LIBS=-ltinfo" (some do, some don't). sco32v505: $(MAKE) "MAKE=$(MAKE)" sco32v500 KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-DSCO_OSR505 -DNOSHADOW -b elf -DPOSIX $(KFLAGS)" #SCO OpenServer 5.0.5 (SCO UNIX 3.2v5.0.5) with SCO UDK. #This one can't see the high serial speeds and anything to do with modem #signals doesn't work because UKD cc has its own alternative universe of #header files. sco32v505udk: $(MAKE) "MAKE=$(MAKE)" sco32v500 KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-DSCO_OSR505 -DDCLTIMEVAL -DNOSHADOW -b elf -DPOSIX $(KFLAGS)" #SCO OpenServer 5.0.5 with networking, SCO /bin/cc. #See comments with sco32v505 targets. sco32v505net: @echo TCP/IP networking added... $(MAKE) "MAKE=$(MAKE)" sco32v500net KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-DSCO_OSR505 -DNOSHADOW -b elf -DPOSIX $(KFLAGS)" #SCO OpenServer 5.0.5 with networking and OpenSSL, SCO /bin/cc. #See comments with sco32v505 targets. sco32v505net+ssl: @echo TCP/IP networking and OpenSSL added... $(MAKE) "MAKE=$(MAKE)" sco32v500net+ssl KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-DSCO_OSR505 -DNOSHADOW -b elf -DPOSIX $(KFLAGS) " \ "LIBS=$(SSLLIB) -lcurses -lsocket -lssl -lcrypto $(LIBS)" \ "LNKFLAGS=$(LNKFLAGS)" #SCO OpenServer 5.0.5 with networking, SCO UDK. #See comments with above sco32v505 targets. sco32v505udknet: @echo TCP/IP networking added... $(MAKE) "MAKE=$(MAKE)" sco32v500net KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-DSCO_OSR505 -DDCLTIMEVAL -DNOSHADOW -b elf -DPOSIX $(KFLAGS)" #SCO OpenServer 5.0.5 with gcc, no networking. sco32v505gcc: @echo Using gcc... $(MAKE) "MAKE=$(MAKE)" sco32v500 "CC=gcc" "CC2=gcc" \ KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-DSCO_OSR505 -DPOSIX -funsigned-char $(KFLAGS)" #SCO OpenServer 5.0.5 with gcc, no networking, no shadow passwords. sco32v505xgcc: @echo Using gcc... $(MAKE) "MAKE=$(MAKE)" sco32v500 "CC=gcc" "CC2=gcc" \ KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-DSCO_OSR505 -DNOSHADOW -DPOSIX -funsigned-char $(KFLAGS)" #SCO OpenServer 5.0.5 with networking, gcc. sco32v505netgcc: @echo TCP/IP networking added - using gcc... @echo If gcc crashes on ckwart.c then build it by hand: @echo " gcc -o wart -DCK_SCOV5 ckwart.c" $(MAKE) "MAKE=$(MAKE)" sco32v500net "CC=gcc" "CC2=gcc" \ KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-DSCO_OSR505 -DNOSHADOW -DPOSIX -funsigned-char $(KFLAGS)" #egcs is just like gcc but generates ELF by default. #Or you can include -melf (not -belf) to force it. sco32v505netegcs: $(MAKE) "MAKE=$(MAKE)" "KFLAGS=$(KFLAGS)" sco32v505netgcc \ KTARGET=$${KTARGET:-$(@)} #SCO OpenServer 5.0.5 with networking, gcc, elf. sco32v505netgccelf: @echo TCP/IP networking added - using gcc, dynamic elf library $(MAKE) "MAKE=$(MAKE)" sco32v500net "CC=gcc" "CC2=gcc" \ "KFLAGS=-DSCO_OSR505 -DPOSIX -funsigned-char -O3 -belf $(KFLAGS)" \ KTARGET=$${KTARGET:-$(@)} LNKFLAGS="-belf" #SCO OpenServer 5.0.6 with SCO /bin/cc. # Add -DDCLTIMEVAL when building with UDK. #Like 5.0.5. IMPORTANT: Use sco32v506a target for 5.0.6a. sco32v506: $(MAKE) "MAKE=$(MAKE)" sco32v500 KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-DSCO_OSR505 -DSCO_OSR506 -b elf -DPOSIX $(KFLAGS)" #SCO OpenServer 5.0.6 with networking, SCO /bin/cc. # Add -DDCLTIMEVAL when building with UDK. # IMPORTANT: Use sco32v506a target for 5.0.6a. sco32v506net: @echo TCP/IP networking added... $(MAKE) "MAKE=$(MAKE)" sco32v500net KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-DSCO_OSR505 -DSCO_OSR506 -b elf -DPOSIX $(KFLAGS)" #SCO OpenServer 5.0.6a, no networking, SCO development tools. #This one has patched sio drivers that, for the first time, #actually handle modem signals correctly. # Add -DDCLTIMEVAL when building with UDK. sco32v506a: $(MAKE) "MAKE=$(MAKE)" sco32v500 KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-DSCO_OSR505 -DSCO_OSR506 -DSCO_OSR506A -DNEEDMDMDEFS \ -b elf -DPOSIX $(KFLAGS)" #SCO OpenServer 5.0.6a with networking, SCO development tools. # Add -DDCLTIMEVAL when building with UDK. sco32v506anet: @echo TCP/IP networking added... $(MAKE) "MAKE=$(MAKE)" sco32v500net KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-DSCO_OSR505 -DSCO_OSR506 -DSCO_OSR506A -DNEEDMDMDEFS \ -b elf -DPOSIX $(KFLAGS)" #SCO OpenServer 5.0.7, no networking, SCO development tools. #Adds flags to make PTY and SSH commands work. These have been tested #only in 5.0.7 but probably they can also be added to earlier OSR5 targets. sco32v507: $(MAKE) "MAKE=$(MAKE)" sco32v500 KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-DSCO_OSR505 -DSCO_OSR506 -DSCO_OSR507 -DNEEDMDMDEFS \ -DHAVE_PTSNAME -DHAVE_PTMX -DHAVE_GRANTPT \ -b elf -DPOSIX $(KFLAGS)" #SCO OpenServer 5.0.7 as above but with networking. sco32v507net: @echo TCP/IP networking added... $(MAKE) "MAKE=$(MAKE)" sco32v500net KTARGET=$${KTARGET:-$(@)} \ "KFLAGS=-DSCO_OSR505 -DSCO_OSR506 -DSCO_OSR507 -DNEEDMDMDEFS \ -DHAVE_PTSNAME -DHAVE_PTMX -DHAVE_GRANTPT \ -b elf -DPOSIX $(KFLAGS)" #SCO OpenServer 6 (new target 30 Jan 2006) sco_osr600 sco600: @echo Making C-Kermit $(CKVER) for SCO OpenServer 6.0.0... $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -O -DDIRENT -DHDBUUCP -DSVR4 -DCK_SCOV5 -DCK_RTSCTS \ -DCK_CURSES -DCK_WREFRESH -DCK_NEWTERM -DSELECT -DSELECT_H \ -DNOGETUSERSHELL -DNOLSTAT -DNOLINKBITS -DTCPSOCKET \ -DNO_DNS_SRV -DSCO_OSR505 -DSCO_OSR506 -DSCO_OSR507 -DNEEDMDMDEFS \ -DHAVE_PTSNAME -DHAVE_PTMX -DHAVE_GRANTPT -DDCLTIMEVAL \ -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 \ -DSOCKOPT_T=socklen_t -DGSOCKNAME_T=size_t -DGPEERNAME_T=size_t \ -DHERALD=\"\\\" SCO OpenServer `uname -v`\\\"\" \ -b elf -DPOSIX $(KFLAGS)" \ "LIBS=-lcurses -lsocket $(LIBS)" "LNKFLAGS=$(LNKFLAGS)" #Tandy 16/6000 with Xenix 3.0 (16 bits) #C-Kermit 7.0 (and later) do not build here; "too many defines". #Add more -DNOxxx options to remove features if program won't load. #Successful operation is a function of program size, physical memory, #available swap space, etc. The following stripped-down configuration #seems to work on most Tandy 6000s. NOTE: "-+" means allow long variable #names, needed for C-Kermit 6.0 because some identifiers are not unique #within the first six characters. trs16: @echo 'Making C-Kermit $(CKVER) for Tandy 16/6000, Xenix 3.0...' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -+ -DATTSV -DTRS16 -DNOMKDIR -DDCLPOPEN -DCK_CURSES \ -DNODEBUG -DNOTLOG -DNOHELP -DNOSCRIPT -DNOCSETS -DNOIKSD \ -DNOREDIRECT -DNOSYSLOG -DNOPUTENV -DNOREALPATH -DNOLEARN \ $(KFLAGS) -O" "LIBS= -lcurses -ltermcap" "LNKFLAGS = -+ -n -s" # QNX 4.21 and above, 32-bit version, Watcom C32 10.6, fully configured, # except no job control because QNX 4.x does not support it. New NCURSES # library used instead of CURSES. # # -Oatx optimizes to favor speed over size: loop optimization, inline fn's. # -Os favors size over speed. Saves 30-40K out of about 1.75M. # -3r = generate 386 code with register-based arg passing. # -3s = generate 386 code with stack-based arg passing. # -ms = separate code & data 4GB segments (32-bit builds only). # -mf = flat memory model code+data in one 4GB segment (ditto). # -zc = place literal strings in code segment. # -N4M = Big stack (increase the digit upon SIGSEGVs at runtime). # chars are unsigned by default (-j makes them signed by default). # -NOUUCP is included because QNX doesn't use it. # Add these to the end if you like but they dump core on my QNX 4.25 system: # # @wermit -h >use.qnx # @usemsg wermit use.qnx # @rm use.qnx # # If you get warnings about HEADER or C_IN add -DNO_DNS_SRV. # OK 2011/06/14 qnx32: @echo 'Making C-Kermit $(CKVER) for QNX 4.2x, 32-bit...' $(MAKE) xermit \ "LNKFLAGS = -N4M -3r" \ "CFLAGS = -ms -3r -DQNX -DTCPSOCKET -DCK_CURSES -DNOGETUSERSHELL \ -DCK_WREFRESH -DCK_REDIR -DSELECT -DSELECT_H -DCK_RTSCTS -DNOJC \ -DNOINITGROUPS -DNOUUCP -DCK_ANSIC -DPID_T=pid_t -Oatx -zc $(KFLAGS)" \ "LIBS= -lsocket -lncurses -ltermcap" # As above but no networking since some QNX systems do not have TCP/IP # installed, or the TCP/IP developers kit, which includes all the needed # header files. This entry has not been tested on a QNX system that, in # fact, does not have TCP/IP installed; some adjustments might be necessary, # in particular regarding the use of select(): is -lsocket needed, can we # get the needed definitions from non-TCP/IP header files (FD_SET, etc)? qnx32nonet: @echo 'Making C-Kermit $(CKVER) for QNX 4.2x, 32-bit, no net...' $(MAKE) xermit \ "LNKFLAGS = -N4M -3r" \ "CFLAGS = -3r -ms -DQNX -DNONET -DNOIKSD -DCK_CURSES \ -DCK_WREFRESH -DCK_REDIR -DSELECT -DSELECT_H -DCK_RTSCTS -DNOJC \ -DNOUUCP -DCK_ANSIC -DPID_T=pid_t -Oatx -zc $(KFLAGS)" \ "LIBS= -lsocket -lncurses -ltermcap" @wermit -h >use.qnx @usemsg wermit use.qnx @rm use.qnx # Synonym for qnx32. qnx: $(MAKE) qnx32 "KFLAGS=$(KFLAGS)" # QNX 4.21 and above, 16-bit version, Watcom C 8.5 - and higher on i286 PCs # and above. # # IMPORTANT: Do not use Watcom C 10.6!!! # If you have it installed, add "-v9.52 to CFLAGS" # # NOTE: QNX 4.23 onward does not work on 286's anyway. # Stacksize 26000, objects larger than 100 bytes in their own segments, # string constants to the codesegment, etc. Fully configured except job ctrl. # This entry works for building a 16-bit executable on a 32-bit system, but # has not been tested on a 16-bit system. Uses large memory model, links # explicitly with large-model sockets library. Correct-model curses library # is chosen automatically. See comment in qnx32 entry about -DNOUUCP. # # WARNING: # # Watcom C prior to 10.6 never had released curses library. To link against it, # you must obtain ported free curses source from ftp://ftp.qnx.com/usr/free, # then compile and build library (cursesl.lib) and place it in /usr/lib. You # must also copy curses.h to /usr/include. Be aware that if you have Watcom # 10.6 installed, you should already have curses.h, which is the new ncurses # library. You must back it up and use free curses.h instead, since ncurses is # only for 32-bit applications and some definitions in these files are # different (e.g., clearok()). For safety, curses is not defined in build. # # In 7.0 -DNOHELP added to keep ckuus2.c from blowing up; NOCSETS and NOSPL # added because ckuus4 was blowing up, and NOFLOAT just because it seemed # dangerous (remove -DNOFLOAT if you want to try it), The result works OK # except for some mysterious beeps upon termination of the top-level keyword. # # Things to try next time we get in trouble: # . Change -zt100 to something smaller like -zt25 # . Change -Oatx to -Omilerat (enable stack checking) # . Maybe get rid of -v9.52 -- it's only there because we were warned. # qnx16: @echo 'Making C-Kermit $(CKVER) for QNX 4.21, 16-bit...' $(MAKE) xermit \ "LNKFLAGS = -2 -ml -N 26000" \ "CFLAGS = -2 -Oatx -zc -zt100 -ml -DQNX -DQNX16 -DNOUUCP -DNOHELP \ -DCK_REDIR -DSELECT -DSELECT_H -DNOJC -DNOGETUSERSHELL -DNOCSETS \ -v9.52 -DTCPSOCKET -DCK_RTSCTS -DCK_ANSIC -DNOINITGROUPS -DNOKVERBS \ -DNORANDOM -DNOCSETS -DNOSPL -DNOFLOAT -DPID_T=pid_t $(KFLAGS)" # QNX 4.1, 16-bit version, with Watcom C 8.5 on i286 PCs and above. # stacksize 26000, objects larger than 100 bytes in their own segments, # string constants to the codesegment, etc. Add -DNOUUCP if desired. qnx16_41: @echo 'Making C-Kermit $(CKVER) for QNX 4.1, 16-bit...' $(MAKE) xermit \ "LNKFLAGS = -mh -N 26000" "CFLAGS = -Wc,-fpc -Wc,-j -DNOGETUSERSHELL \ -Wc,-Ols -Wc,-zdf -Wc,-zc -Wc,-zt100 -mh -DPOSIX -DQNX -DDIRENT \ -DNOCYRIL -DNODEBUG -DNOMSEND -DMINIDIAL -DNOXMIT -DNOSCRIPT -DNOSPL \ -DNOSETKEY -DNOINITGROUPS -DQNX16 -DPID_T=pid_t $(KFLAGS)" # QNX Neutrino 2 (pwaechtler@qnx.de) crosscompiled on QNX 4.25. # Gets lots of compiler warnings. qnx_nto2+: @echo 'Making C-Kermit $(CKVER) for QNX Neutrino 2+ ' cc -o wart ckwart.c $(MAKE) xermit \ "CC = qcc -Vgcc_ntox86" \ "CC2 = qcc -Vgcc_ntox86" \ "LNKFLAGS = " \ "CFLAGS = -DNEUTRINO -DTCPSOCKET -DCK_CURSES -DNOGETUSERSHELL \ -DNOUUCP -DCK_WREFRESH -DCK_REDIR -DSELECT -DSELECT_H -DCK_RTSCTS \ -DNOJC -DNOINITGROUPS -DCK_ANSIC -DPID_T=pid_t -DUNIX -DDIRENT \ -DMYREAD -DBSD44ORPOSIX -DSVORPOSIX -DNDGPWNAM $(KFLAGS)" \ "LIBS= -lsocket -lncurses " # QNX 6 (= Neutrino 2.xx) native build (kirussel@cisco.com). qnx6: @echo 'Making C-Kermit $(CKVER) for QNX6' $(MAKE) xermit KTARGET=QNX6 \ "CFLAGS = -DPOSIX -DCK_POSIX_SIG -DNETPTY -DNOARROWKEYS \ -DUSE_TIOCSDTR -DBIGBUFOK -DCKMAXOPEN=100 -DRLOGCODE -DNOREALPATH \ -DMAXNAMLEN=48 -DQNX6 -DUSE_TERMIO -DINIT_SPTY \ -DCK_CURSES -DCK_WREFRESH -DCK_NEWTERM -DDYNAMIC \ -DTCPSOCKET -DNOGETUSERSHELL -DCK_REDIR -DSELECT -DSELECT_H \ -DCK_RTSCTS -DNOJC -DSVORPOSIX -DBSD44ORPOSIX -DNOUUCP -DCK_ANSIC \ $(KFLAGS) -O" \ "LIBS= -lsocket -lncurses" #MINIX/2.0 32 Bit version for intel 386+ running the POSIX-compliant MINIX # version 2.0 (The definition of fatal avoids a conflict with a symbol by # the same name in the curses library.) It is impossible to compile with # network support since Minix does not support Berkeley sockets. # Note: use chmem liberally on the compiler passes, make, and the final # kermit executable. (3 megabytes of memory for each is sufficient.) # From Terry McConnell, Syracuse U, and Will Rose. Will says: # The stacks for make and some compiler passes needed to be increased # with chmem as follows: # make 1MB # /usr/lib/em_cemcom.ansi 3MB # /usr/lib/em_opt 1MB # /usr/lib/i386/cg 1MB # /usr/lib/i386/as 1MB # The compiler temporary directory was set to /usr/tmp via the TMPDIR # environment variable; more than 1MB of temporary space was needed. # Kermit itself needs at least 1MB of stack. minix20: @echo 'Making C-Kermit $(CKVER) for MINIX 2.0/386...' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} EXT=o \ "CFLAGS= -wo -DV7 -DMINIX2 -DMINIX -DSIG_V -D_POSIX_SOURCE \ -DCKCPU=\\\"i-386\\\" -DNOIKSD -Dfatal=myfatal -DCK_CURSES -DNOLEARN \ -DNOSYSLOG -DUSE_MEMCPY -DNOREALPATH $(KFLAGS)" "LIBS= -lcurses" #MINIX/386 (PC Minix modified by Bruce Evans in Australia for 386 addressing) # For MINIX 1.5+ (but < 2.0) minix386: @echo 'Making C-Kermit $(CKVER) for MINIX/386...' @echo 'TOTALLY UNTESTED!' $(MAKE) wermit EXT=s KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DV7 -DMINIX -D_POSIX_SOURCE -DNOLEARN $(KFLAGS)" #MINIX/386 Minix modified by Bruce Evans in Australia to use 386 addressing minix386gcc: @echo 'Making C-Kermit $(CKVER) for MINIX/386 with gcc...' @echo 'TOTALLY UNTESTED!' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} "CC=gcc -g -O" "CC2=gcc -g" \ "CFLAGS= -DV7 -DMINIX -D_POSIX_SOURCE -DNOLEARN $(KFLAGS)" #MINIX - 68k version with ACK compiler. # If you have trouble compiling or running wart, "touch wart". # If it still doesn't work, "touch ckcpro.c". # The version configured below has many features removed, including # the TRANSMIT, MSEND, HELP, and SCRIPT commands, international # character set support, and the entire script programming language. # But it does have an interactive command parser. # Make sure make(1) has (at least) 100000 chmemory! # If you are using the Amsterdam C compiler, you might have to add "-D__ACK__". minix68k: @echo 'Making C-Kermit $(CKVER) for MINIX 68k with ACK...' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DV7 -DMINIX -D_MINIX -D_POSIX_SOURCE -DNOLEARN \ -DNODIAL -DNOHELP -DNODEBUG -DNOTLOG \ -DNOSCRIPT -DNOCSETS -DNOSPL $(KFLAGS) \ -DPID_T=pid_t -DUID_T=uid_t -DGID_T=gid_t -DSIG_V" #MINIX - 68k version with c68 compiler. # If you have trouble compiling or running wart, "touch wart" or # "touch ckcpro.c". Compiling ckudia.c (no -DNODIAL!) might fail. :-( # Give c68 250000 bytes of stack+heap; make sure make(1) has at least # 100000 chmemory. On a 1MB Atari ST this means that the recursive # call of make fails due to memory shortage. Try "make -n minixc68 >makeit", # followed by ". makeit". Otherwise, as above. minixc68: @echo 'Making C-Kermit $(CKVER) for MINIX 68k with c68...' $(MAKE) wermit "CC= cc -c68" KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DV7 -DMINIX -D_MINIX -D_POSIX_SOURCE -DNOLEARN \ -DNODIAL -DNOHELP -DNODEBUG -DNOTLOG \ -DNOSCRIPT -DNOCSETS -DNOSPL $(KFLAGS) \ -DPID_T=pid_t -DUID_T=uid_t -DGID_T=gid_t -DSIG_V" #MINIX - 68k version with c68 compiler. #A variation on the above that was recently (Sep 95) reported to work. minixc68a: @echo 'Making C-Kermit $(CKVER) for MINIX 68k with c68...' $(MAKE) wermit "CC= cc -c68" KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DV7 -DMINIX -D_MINIX -D_POSIX_SOURCE \ -DCK_ANSIC -DNODEBUG -DNOTLOG -DMINIDIAL -DEXTEN -DMYCURSES \ -DNOSCRIPT -DNOCSETS -DNOSPL -DNOJC -DDIRENT -DNOLEARN \ -DNOSETKEY -DNOESCSEQ $(KFLAGS) \ -DPID_T=pid_t -DUID_T=uid_t -DGID_T=gid_t -DSIG_V" #MIPS Computer Systems with UMIPS RISC/OS 4.52 = AT&T UNIX System V R3.0. #Remove -DNOJC if job control can be safely used. mips: @echo 'Making C-Kermit $(CKVER) for MIPS RISC/OS...' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DMIPS -DDIRENT -DCK_POLL -DNOJC -DNOLEARN -DPID_T=int \ -DGID_T=gid_t -DUID_T=uid_t -i -O1500 $(KFLAGS)" #As above, but with TCP/IP and fullscreen support. mipstcpc: @echo 'Making C-Kermit $(CKVER) for MIPS RISC/OS...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DMIPS -DDIRENT -DCK_POLL -DNOJC \ -DTCPSOCKET -DCK_CURSES -I/usr/include/bsd \ -DPID_T=int -DGID_T=gid_t -DUID_T=uid_t -i -O1500 $(KFLAGS)" \ "LIBS = -lcurses -lbsd" #Motorola Delta System V/68 R3, signal() is void rather than int. #Uses dirent.h and Honey DanBer uucp. Supports TCP/IP. #After building, use "mcs -d" to reduce size of the executable program. sv68r3: @echo 'Making C-Kermit $(CKVER) for Motorola UNIX System V/68 R3...' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DSVR3 -DSV68 -DDIRENT -DHDBUUCP -DNO_DNS_SRV -DTCPSOCKET \ -DNOUNICODE -DNOLEARN -DUSE_MEMCPY $(KFLAGS) -O" "LNKFLAGS =" #Motorola Delta System V/68 R3V5, signal() is void rather than int. #Uses dirent.h and Honey DanBer UUCP. Supports TCP/IP. #After building, use "mcs -d" to reduce size of the executable program. sv68r3v5: @echo 'Making C-Kermit $(CKVER) for Motorola UNIX System V/68 R3V5' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DSVR3 -DSV68 -DDIRENT -DHDBUUCP -DNO_DNS_SRV -DUSE_MEMCPY \ -DTCPSOCKET -DINADDRX -DNOUNICODE -DFNFLOAT -DNOLEARN $(KFLAGS) -O" \ "LNKFLAGS =" "LIBS = -linet -lm" #Motorola MVME147 System V/68 R3 V5.1. Requires gcc 2.1 to compile. #After building, use "mcs -d" to reduce size of the executable program. sv68r3v51: @echo 'Making C-Kermit $(CKVER) for Motorola UNIX System V/68 R3V5.1' $(MAKE) wermit "CC=gcc-delta" "CC2=gcc-delta" \ KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DSVR3 -DDIRENT -DHDBUUCP -DNODEBUG -DNO_DNS_SRV -DNOLEARN \ -DNOUNICODE -DFNFLOAT -DSV68 -DUSE_MEMCPY $(KFLAGS) \ -O2 -v -ftraditional" \ "LNKFLAGS = -s -v" "LIBS = -lm881 -lm" #Motorola MVME147 System V/68 R3V6. derived from Motorola Delta System R3V5. #Checked on larger Motorola System V/68 R3V6 (with NSE Network Services Ext.) #After building, use "strip" to reduce size of the executable program. # "LIBS = -lnsl" removed in C-Kermit 6.1 - put back if needed. # "LIBS = lm" added in 7.1/8.0 for floating-point math. # ckuusr.c clobbers the optimizer. sv68r3v6: @echo 'Making C-Kermit $(CKVER) for Motorola UNIX System V/68 R3V6' $(MAKE) ckuusr.$(EXT) KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DSV68R3V6 -DDIRENT -DHDBUUCP -DNOLOGIN -DNOINITGROUPS \ -DNOSYMLINK -DNOREDIRECT -DNOGFTIMER -DTCPSOCKET -DDCLGETCWD -DSV68 \ -DNO_DNS_SRV -DNOUNICODE -DFNFLOAT -DSELECT -DUSE_MEMCPY $(KFLAGS)" $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -O -DSV68R3V6 -DDIRENT -DHDBUUCP -DNOLOGIN -DNOINITGROUPS \ -DNOSYMLINK -DNOREDIRECT -DNOGFTIMER -DTCPSOCKET -DDCLGETCWD -DSV68 \ -DNO_DNS_SRV -DNOUNICODE -DFNFLOAT -DSELECT -DUSE_MEMCPY $(KFLAGS)" \ "LNKFLAGS =" "LIBS = -lm" #Motorola Delta System V/88 R32, signal() is void rather than int. #Uses dirent.h and Honey DanBer uucp. Needs for setting #file dates. Supports TCP/IP. #After building, use "mcs -d" to reduce size of the executable program. sv88r32: @echo 'Making C-Kermit $(CKVER) for Motorola UNIX System V/88 R32...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -DSV88R32 -DDIRENT -DHDBUUCP -DTCPSOCKET \ -DSYSUTIMEH -DCK_CURSES -DNOGETUSERSHELL -DGTODONEARG $(KFLAGS) -O" \ "LIBS= -lcurses -lresolv" "LNKFLAGS = -s" #Motorola Delta System V/88 R40. Has , regular Berkeley #sockets library, i.e. in.h and inet.h are not misplaced in sys (rather than #netinet and arpa, respectively). Uses ANSI C constructs, advisory file #locking on devices, etc. curses support added. Reportedly, the #/usr/include/sys/vnode.h file has a bug which must be fixed before this #makefile entry can work correctly. The "if DEBUG" directive at about line #320 must be changed to "ifdef DEBUG" (Reportedly, this was fixed in #in System V/88 R4.3). #After building, use "mcs -d" to reduce size of the executable program. sv88r40: @echo 'Making C-Kermit $(CKVER) for Motorola UNIX System V/88 R40...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -O -DSVR4 -DMOTSV88R4 -DDIRENT -DHDBUUCP -DSTERMIOX \ -DTCPSOCKET -DCK_CURSES -DNOGETUSERSHELL -DGTODONEARG -DFNFLOAT \ $(KFLAGS)" \ "LIBS= -lsocket -lnsl -lcurses -lresolv -lm" "LNKFLAGS = -s" #As above but without the floating-point math library. sv88r40nm: @echo 'Making C-Kermit $(CKVER) for Motorola UNIX System V/88 R40...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS = -O -DSVR4 -DMOTSV88R4 -DDIRENT -DHDBUUCP -DSTERMIOX \ -DTCPSOCKET -DCK_CURSES -DNOGETUSERSHELL -DGTODONEARG $(KFLAGS)" \ "LIBS= -lsocket -lnsl -lcurses -lresolv" "LNKFLAGS = -s" #As above but with floating-point math library support \ffp...() functions #and S-Expressions. #Olivetti X/OS R2.3, 3.x. #NOTES: # . If you build the executable on 2.x X/OS, it will also run on 3.x. # . If you build it on 3.x X/OS, it will NOT run on 2.x. # . Kermit can run with no privileges unless the uucp lines are protected, # in which case kermit must be owned by uucp with suid bit set: # chown uucp kermit ; chmod 4111 kermit. xos23: @echo 'Making C-Kermit $(CKVER) for Olivetti X/OS...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ 'CFLAGS=-OLM -DOXOS -DTCPSOCKET -DHDBUUCP $(KFLAGS)' \ "LIBS=" "LNKFLAGS=" #As above, but with curses. xos23c: @echo 'Making C-Kermit $(CKVER) for Olivetti X/OS with curses...' $(MAKE) xermit KTARGET=$${KTARGET:-$(@)} \ 'CFLAGS=-OLM -DOXOS -DTCPSOCKET -DHDBUUCP -DCK_CURSES $(KFLAGS)' \ "LIBS=-lcurses" "LNKFLAGS=" ckuuid: @echo 'building C-Kermit $(CKVER) set-UID/set-GID test programs' $(CC) -DANYBSD -DSAVEDUID -o ckuuid1 ckuuid.c $(CC) -DANYBSD -o ckuuid2 ckuuid.c $(CC) -DANYBSD -DNOSETREU -o ckuuid3 ckuuid.c $(CC) -DANYBSD -DSETEUID -DNOSETREU -o ckuuid4 ckuuid.c $(CC) -o ckuuid5 ckuuid.c @echo 'Read the top of ckuuid.c for directions...for testing' @echo 'you must make these programs setuid and setgid' ############################################################################ # A N T I Q U I T I E S # # The following are antique targets from C-Kermit 5A or earlier. They have # not been updated or tested in years. Most of them will need recent features # disabled, usually with some combination of -DNOUNICODE, -DNOIKSD, -DNOANSI, # -DNOCKGHNLHOST, -DNO_DNS_SRV, -DNOREDIRECT, -DNOREALPATH, -DNOCURSES, etc. # They are also missing the KTARGET=$${KTARGET:-$(@)} business. # For details see ckuins.txt and ckccfg.txt. # ############################################################################ #Berkeley Unix 2.8, 2.9 for PDP-11s with I&D space, maybe also Ultrix-11??? #C-Kermit(5A) is simply too large (even turning off almost every feature #available) to run without both I&D space plus overlays. The old comment #suggested running 'pcc' but that won't help. Changing 'cc' to 'ckustr.sed' #will cause a string extraction to be done, saving D space by moving strings #to a file. bsd29: @echo Making C-Kermit $(CKVER) for 2.8 or 2.9BSD. @echo Read the makefile if you have trouble with this... $(MAKE) ovwermit \ "CFLAGS= -DBSD29 -DNODEBUG -DNOTLOG -DNOCSETS -DNOHELP \ -DNOSCRIPT -DNOSPL -DNOXMIT -DNODIAL $(KFLAGS)" \ "LNKFLAGS= -i -lndir" "CC= cc " "CC2= cc" bsd210: @echo Please use ckubs2.mak to build C-Kermit $(CKVER) for 2.10BSD. bsd211: @echo Please use ckubs2.mak to build C-Kermit $(CKVER) for 2.11BSD. #Charles River Data Systems Universe with UNOS Version 9.2 crds: @echo 'Making C-Kermit $(CKVER) for Charles River Data Systems...' make xermit \ "CFLAGS = -DATTSV -DNOANSI -DDIRENT -DLONGFN -DTCPSOCKET \ -DLOCK_DIR=\\\"/usr/spool/uucp\\\" -DNOSETREU \ -Dsuspend=ksuspend $(KFLAGS) -O" "LNKFLAGS =" #Microport SV/AT for IBM PC/AT 286 and clones, System V R2. #The -O flag may fail on some modules (like ckuus2.c), in which case you #should compile them by hand, omitting the -O. If you get "hash table #overflow", try adding -DNODEBUG. #Also, reportedly this compiles better with gcc than with cc. mpsysv: @echo 'Making C-Kermit $(CKVER) for Microport SV/AT 286...' $(MAKE) wermit \ "CFLAGS= -DATTSV -DNOLEARN $(KFLAGS) -O -Ml" "LNKFLAGS = -Ml" #Microsoft "Xenix/286" e.g. for IBM PC/AT xenix: @echo 'Making C-Kermit $(CKVER) for Xenix/286' $(MAKE) wermit \ "CFLAGS= -DXENIX -DNOFILEH -DNOLEARN $(KFLAGS) -Dunix -F 3000 -i" \ "LNKFLAGS = -F 3000 -i" #PC/IX, Interactive Corp System III for IBM PC/XT pcix: @echo 'Making C-Kermit $(CKVER) for PC/IX...' $(MAKE) wermit \ "CFLAGS= -DPCIX -DISIII -DNOLEARN $(KFLAGS) \ -Dsdata=sdatax -O -i" "LNKFLAGS = -i" #Integrated Solutions Inc V8S VME 68020 isi: @echo Making C-Kermit $(CKVER) for 4.2BSD on ISI... $(MAKE) wermit "CC = cc" \ "CFLAGS= -DBSD4 -DTCPSOCKET -DINADDRX -DDCLPOPEN -DDEBUG -DNOSETREU \ -DCK_CURSES -DNOLEARN $(KFLAGS)" "LIBS = -lcurses -ltermcap" #Interactive Corp version of AT&T System III #is3: (very old, probably not sufficient for 5A or later) # @echo 'Making C-Kermit $(CKVER) for Interactive System III...' # make wermit "CFLAGS = -DISIII -Ddata=datax -O -i" "LNKFLAGS = -i" #The following should work, use it if you don't have gcc. #Use is3gcc if you have gcc. is3: @echo 'Making C-Kermit $(CKVER) for Interactive System III...' $(MAKE) wermit \ "CFLAGS= -DISIII $(KFLAGS) -Ddata=datax -DNAP -DHDBUUCP -DLOCK_DIR=\"/usr/spool/uucp\" -DSIGTYP=void -O -i" "LNKFLAGS = -i" #Interactive UNIX System V R3, no network support. Uses and Honey #DanBer UUCP. If this entry does not compile correctly, try any or all of the #following. These suggestions also apply more or less to the other is5r3xxx #entries that follow this one. # . Remove the UID_T and GID_T definitions, or change them as required. # . Change -DDIRENT to -DSDIRENT. # . Add -DSIGTYP=void. # . Remove -g from LNKFLAGS. # . Add -DNOANSI to remove compiler complaints about ANSI C constructions # . Add other -DNOxxx's to save space (e.g. -DNOCSETS) # See the next few makefile entries for related examples. # Also see sys5r32is for making a portable i386 SVR3 binary. is5r3: @echo 'Making C-Kermit $(CKVER) for Interactive 386/ix or later...' @echo 'If this does not work please read the makefile entry.' $(MAKE) wermit \ "CFLAGS = -DSVR3 -DDIRENT -DHDBUUCP -g -DNOCSETS -DNOREALPATH \ -DUID_T=ushort -DGID_T=ushort -DI386IX $(KFLAGS)" \ "LNKFLAGS = -g" #Interactive Corp System System V R3 with gcc is3gcc: @echo 'Making C-Kermit $(CKVER) for Interactive System V R3 / gcc...' $(MAKE) wermit CC=gcc CC2=gcc \ 'CFLAGS = -D_SYSV3 -DISIII -Ddata=datax -DNAP -DHDBUUCP -DNOREALPATH \ -DLOCK_DIR=\"/usr/spool/uucp\" -DSIGTYP=void -O' "LNKFLAGS =" #Interactive UNIX System V R3, POSIX variant. Untested. #Uses dirent.h and Honey DanBer uucp. Read comments in is5r3 entry. is5r3p: @echo 'Making C-Kermit $(CKVER) for Interactive 386/ix or later...' $(MAKE) wermit \ "CFLAGS= -DSVR3 -DDIRENT -DHDBUUCP -g -DNOCSETS -DNOREALPATH \ -DI386IX -DPOSIX $(KFLAGS)" "LNKFLAGS=" "LIBS=-lcposix" #Interactive UNIX SVR3 2.2.1, job control, curses, no net, gcc. is5r3gcc: $(MAKE) wermit CC=gcc CC2=gcc \ "CFLAGS=-g -posix -DSVR3 -DDIRENT -DNOREALPATH \ -DHDBUUCP -O -DNOCSETS -DI386IX -DSVR3JC -DCK_CURSES \ $(KFLAGS)" LNKFLAGS="-posix" LIBS="-lcurses -lc_s" #Interactive UNIX System V R3 with TCP/IP network support. #Needs -linet for net functions. signal() is void rather than int. #Uses dirent.h and Honey DanBer uucp. Read comments in is5r3 entry. #Also see is5r3net2 if you have trouble with this entry. is5r3net: @echo 'Making C-Kermit $(CKVER) for Interactive 386/ix...' @echo 'If this does not work please read the makefile entry.' $(MAKE) wermit CC="$(CC)" CC2="$(CC2)" \ "CFLAGS = -DSVR3 -DDIRENT -DHDBUUCP -DTCPSOCKET -DNOREALPATH \ -DI386IX $(KFLAGS) -O" "LIBS = -linet" is5r3netgcc: $(MAKE) is5r3net CC=gcc CC2=gcc #Interactive UNIX System V R3, no job control, signal() void rather than int. #Uses dirent.h and Honey DanBer uucp. Needs -linet for net functions. #Read comments in is5r3 entry. Use this entry if is5r3net fails. #Saves some space by stripping (-s) and using shared library (-lc_s). is5r3net2: @echo 'Making C-Kermit $(CKVER) for Interactive 386/ix...' $(MAKE) wermit \ "CFLAGS = -DSVR3 -DDIRENT -DHDBUUCP -DTCPSOCKET -DNOJC -DNOREALPATH \ -DSIGTYP=void -DNOANSI -DI386IX $(KFLAGS) -O" \ "LNKFLAGS= -s" "LIBS = -linet -lc_s" #Interactive UNIX System V R3 (version 2.2 or later) with job control & curses. #Uses dirent.h and Honey DanBer UUCP. is5r3jc: @echo 'Making C-Kermit $(CKVER) for Interactive Unix 2.2 or later...' $(MAKE) wermit CC="$(CC)" CC2="$(CC2)" \ "CFLAGS = -DSVR3 -DDIRENT -DHDBUUCP -O -DNOCSETS -DNOREALPATH \ -DUID_T=ushort -DGID_T=ushort -DI386IX -DSVR3JC -DCK_CURSES \ -DPOSIX_JC -DCK_REDIR -DCK_POLL -DDCLGETCWD \ $(KFLAGS)" "LIBS=-lcurses -lc_s -linet" is5r3jcgcc: $(MAKE) is5r3jc CC="gcc -DCK_ANSILIBS -DDCGPWNAM -O4" CC2=gcc \ KFLAGS="$(KFLAGS)" LNKFLAGS="$(LNKFLAGS)" #Sunsoft/Interactive UNIX System V R3 (version 2.2 or later) #with job control, curses, and TCP/IP networking. #Uses dirent.h and Honey DanBer UUCP. is5r3netjc: @echo 'Making C-Kermit $(CKVER) for Interactive Unix 2.2 or later...' $(MAKE) wermit CC="$(CC)" CC2="$(CC2)" \ "CFLAGS = -DSVR3 -DDIRENT -DHDBUUCP -O -DNOCSETS -DNOREALPATH \ -DUID_T=ushort -DGID_T=ushort -DI386IX -DSVR3JC -DCK_CURSES \ -DPOSIX_JC -DCK_REDIR -DTCPSOCKET -DSELECT \ $(KFLAGS)" "LIBS=-linet -lcurses -lc_s" is5r3netjcgcc: $(MAKE) is5r3netjc CC="gcc -DCK_ANSILIBS -DDCGPWNAM -O4" CC2=gcc \ KFLAGS="$(KFLAGS)" LNKFLAGS="$(LNKFLAGS)" #Masscomp System III rtu: @echo 'Making C-Kermit $(CKVER) for Masscomp RTU System III...' $(MAKE) wermit \ "CFLAGS= -UFIONREAD -DATTSV $(KFLAGS) -O" "LNKFLAGS =" "LIBS= -ljobs" #Masscomp/Concurrent RTU 4.0 or later, Berkeley environment. #Includes = /usr/include/ndir.h #Note "LIBS = -lndir" might not be necessary because of "ucb make". rtubsd: @echo 'Making C-Kermit $(CKVER) for Masscomp RTU 4.1A...' ucb make wermit \ "CFLAGS= -DBSD4 -DRTU -DNDIR -DHDBUUCP -DTCPSOCKET $(KFLAGS)" \ "LIBS = -lndir" #Masscomp/Concurrent RTU 4.0 or later, same as above, #Includes "usr/lib/ndir.h" #Note "LIBS = -lndir" might not be necessary because of "ucb make". rtubsd2: @echo 'Making C-Kermit $(CKVER) for Masscomp RTU 4.1A...' ucb make wermit \ "CFLAGS= -DBSD4 -DRTU -DXNDIR -DHDBUUCP $(KFLAGS)" \ "LIBS = -lndir" #Masscomp/Concurrent RTU 4.0 or later, same as above, #Includes #Note "LIBS = -lndir" might not be necessary because of "ucb make". rtubsd3: @echo 'Making C-Kermit $(CKVER) for Masscomp RTU 4.x BSD...' ucb make wermit "CFLAGS= -DBSD4 -DRTU -DHDBUUCP $(KFLAGS)" \ "LIBS = -lndir" #Masscomp/Concurrent RTU 4.0 or later, System V R2, using . #In case of problems, add back the -DRTU switch. #In case -DTCPSOCKET gives trouble, remove it. rtus5: @echo 'Making C-Kermit $(CKVER) for Masscomp RTU 4.x...' $(MAKE) wermit \ "CFLAGS= -DATTSV -DHDBUUCP -DDIRENT -DTCPSOCKET $(KFLAGS)" #Masscomp/Concurrent RTU 4.x, System V R3, using . #Use this one if rtus5 gives warnings about pointer type mismatches. #In case of problems, add back the -DRTU switch. rtus5r3: @echo 'Making C-Kermit $(CKVER) for Masscomp RTU Sys V R3...' $(MAKE) wermit "CFLAGS= -DSVR3 -DHDBUUCP -DDIRENT $(KFLAGS)" #DEC Pro-3xx with Pro/Venix V1.0 or V1.1 # Requires code-mapping on non-I&D-space 11/23 processor, plus some # fiddling to get interrupt targets into resident code section. # This almost certainly doesn't work any more. provx1: @echo 'Making C-Kermit $(CKVER) for DEC Pro-3xx, Pro/Venix 1.x...' $(MAKE) wart "CFLAGS= -DPROVX1 $(KFLAGS)" "LNKFLAGS= " $(MAKE) wermit "CFLAGS = -DPROVX1 -DNOFILEH -md780" \ "LNKFLAGS= -u _sleep -lc -md780" #Nixdorf Targon/31. #AT&T UNIX System V R3, signal() is void rather than int. #Uses dirent.h without Honey DanBer uucp. t31tos40x: @echo 'Making C-Kermit $(CKVER) for Targon/31 with TOS 4.0.xx...' $(MAKE) wermit \ "CFLAGS= -DSVR3 -DDIRENT $(KFLAGS) -O" \ "LNKFLAGS=" #NCR Tower 1632, OS 1.02 tower1: @echo 'Making C-Kermit $(CKVER) for NCR Tower 1632, OS 1.02...' $(MAKE) wermit "CFLAGS= -DTOWER1 $(KFLAGS)" #NCR Tower 32, OS Release 1.xx.xx tower32-1: @echo 'Making C-Kermit $(CKVER) for NCR Tower 32 Rel 1 System V R2...' @echo 'Add KFLAGS=-DISDIRBUG if you get errors about S_ISREG/S_ISDIR.' $(MAKE) wermit \ "CFLAGS = -DATTSV $(KFLAGS) -O" "LNKFLAGS = -n" #NCR Tower 32, OS Release 2.xx.xx tower32-2: @echo 'Making C-Kermit $(CKVER) for NCR Tower 32 Rel 2 System V R2...' $(MAKE) wermit \ "CFLAGS = -DATTSV -DHDBUUCP $(KFLAGS) -O2" \ "LNKFLAGS = -n" #NCR Tower 32, OS Releases based on System V R3 #Don't add -DNAP (doesn't work right) or -DRDCHK (not available in libc). tower32: @echo 'Making C-Kermit $(CKVER) for NCR Tower 32 System V R3...' $(MAKE) wermit \ "CFLAGS = -DSVR3 -DDIRENT -DHDBUUCP -DNOSYSIOCTLH $(KFLAGS) \ -DUID_T=ushort -DGID_T=ushort -O1" #NCR Tower 32, OS Releases based on System V R3 tower32g: @echo 'Making C-Kermit $(CKVER) for NCR Tower 32 System V R3, gcc...' $(MAKE) wermit "CC = gcc" \ "CFLAGS = -DSVR3 -DDIRENT -DHDBUUCP -DNOSYSIOCTLH $(KFLAGS) \ DUID_T=ushort -DGID_T=ushort -O -fstrength-reduce -fomit-frame-pointer" #Fortune 32:16, For:Pro 1.8 (mostly like 4.1bsd) ft18: @echo 'Making C-Kermit $(CKVER) for Fortune 32:16 For:Pro 1.8...' $(MAKE) wermit \ "CFLAGS= -DNODEBUG -DBSD4 -DFT18 -DNOFILEH $(KFLAGS) \ -DPID_T=short" #Fortune 32:16, For:Pro 2.1 (mostly like 4.1bsd). #The modules that break the optimizer are compiled separately. ft21: @echo 'Making C-Kermit $(CKVER) for Fortune 32:16 For:Pro 2.1...' $(MAKE) ckuusx.$(EXT) "CFLAGS= -DNODEBUG -DBSD4 -DFT21 -DNOFILEH \ -SYM 800 -DCK_CURSES $(KFLAGS) -DPID_T=short" \ "LNKFLAGS= -n -s" "LIBS= -lcurses -ltermcap -lv -lnet" $(MAKE) ckuxla.$(EXT) "CFLAGS= -DNODEBUG -DBSD4 -DFT21 -DNOFILEH \ -SYM 800 -DCK_CURSES $(KFLAGS) -DPID_T=short" \ "LNKFLAGS= -n -s" "LIBS= -lcurses -ltermcap -lv -lnet" $(MAKE) ckudia.$(EXT) "CFLAGS= -DNODEBUG -DBSD4 -DFT21 -DNOFILEH \ -SYM 800 -DCK_CURSES $(KFLAGS) -DPID_T=short" \ "LNKFLAGS= -n -s" "LIBS= -lcurses -ltermcap -lv -lnet" $(MAKE) wermit \ "CFLAGS= -O -DNODEBUG -DBSD4 -DFT21 -DNOFILEH -SYM 800 \ -DCK_CURSES $(KFLAGS) -DPID_T=short" \ "LNKFLAGS= -n -s" "LIBS= -lcurses -ltermcap -lv -lnet" #Valid Scaldstar #Berkeleyish, but need to change some variable names. valid: @echo 'Making C-Kermit $(CKVER) for Valid Scaldstar...' $(MAKE) wermit \ "CFLAGS= -DBSD4 -DNODEBUG -DNOTLOG -Dcc=ccx -DFREAD=1 $(KFLAGS)" #IBM IX/370 on IBM 370 Series mainframes #Mostly like sys3, but should buffer packets. ix370: @echo 'Making C-Kermit $(CKVER) for IBM IX/370...' $(MAKE) wermit "CFLAGS = -DIX370 -DATTSV $(KFLAGS) -i -O" \ "LNKFLAGS = -i" #Amdahl UTS 2.4 on IBM 370 series compatible mainframes. #Mostly like V7, but can't do initrawq() buffer peeking. uts24: @echo 'Making C-Kermit $(CKVER) for Amdahl UTS 2.4...' $(MAKE) wermit "CFLAGS=-DV7 -DPROCNAME=\\\"$(PROC)\\\" \ -DUTS24 -DBOOTNAME=\\\"$(BOOTFILE)\\\" -DNPROCNAME=\\\"$(NPROC)\\\" \ -DNPTYPE=$(NPTYPE) $(DIRECT) $(KFLAGS)" #Amdahl UTSV UNIX System V = System V R2 or earlier. utsv: @echo 'Making C-Kermit $(CKVER) for Amdahl UTSV...' $(MAKE) wermit \ "CFLAGS = -DUTSV $(KFLAGS) -i -O" "LNKFLAGS = -i" #Amdahl UTSV UNIX System V = System V R2 or earlier, with TCP sockets library. utsvtcp: @echo 'Making C-Kermit $(CKVER) for Amdahl UTSV w/tcp...' $(MAKE) wermit "CFLAGS = \ -DTCPSOCKET -DUTSV $(KFLAGS) -i -O" "LNKFLAGS = -i" \ "LIBS = -lsocket" #BBN C/70 with IOS 2.0 #Mostly Berkeley-like, but with some ATTisms c70: @echo 'Making C-Kermit $(CKVER) for BBN C/70 IOS 2.0...' $(MAKE) wermit "CFLAGS= -DBSD4 -DC70 $(KFLAGS)" #Zilog ZEUS 3.21 zilog: @echo 'Making C-Kermit $(CKVER) for Zilog Zeus 3.21...' $(MAKE) wermit \ "CFLAGS = -DATTSV -DZILOG -DNODEBUG $(KFLAGS) -i -O" \ "LNKFLAGS = -i -lpw" #Whitechapel MG-1 Genix 1.3 white: @echo 'Making C-Kermit $(CKVER) for Whitechapel MG-1 Genix 1.3...' @touch ckcpro.c $(MAKE) wermit "CFLAGS= -DBSD4 -Dzkself()=0 $(KFLAGS)" #Pixel 1000 pixel: @echo 'Making C-Kermit $(CKVER) for Pixel 1000...' $(MAKE) wermit "CFLAGS= -DBSD4 -Dzkself()=0 $(KFLAGS)" ptx: $(MAKE) "MAKE=$(MAKE)" dynixptx12 #CDC VX/VE 5.2.1 vxve: @echo 'Making C-Kermit $(CKVER) for CDC VX/VE 5.2.1...' $(MAKE) wermit \ "CFLAGS = -DATTSV -DVXVE -DNODEBUG -DNOTLOG $(KFLAGS) -i -O" \ "LNKFLAGS = -i" #DIAB DS90 or LUXOR ABC-9000 with pre-5.2 DNIX. Sys V with nap() and rdchk(). # nd = no opendir(), readdir(), closedir(), etc. # Some of the modules fail to compile with -O. dnixnd: @echo 'Making C-Kermit $(CKVER) for DIAB DS90 with very old DNIX 5.2.' $(MAKE) wermit \ "CFLAGS = -DATTSV -DNAP -DRDCHK -DDCLPOPEN \ -U__STDC__ $(KFLAGS)" #DIAB DS90 with DNIX 5.2. Sys V with nap() and rdchk(). # This one has opendir(), readdir(), closedir(), etc. # Some of the modules fail to compile with -O. dnix: @echo 'Making C-Kermit $(CKVER) for DIAB DS90 with old DNIX 5.2...' $(MAKE) wermit \ "CFLAGS = -DATTSV -DNAP -DRDCHK -DDIRENT \ -U__STDC__ $(KFLAGS)" #DIAB DS90 with DNIX 5.2. Sys V with nap() and rdchk(). # As above, but with curses and TCP/IP. # You might get complaints about redefinition of O_RDONLY, etc, because # of bugs in the DNIX header files, which can be fixed by adding #ifndef... # around the offending definitions in the header files. dnixnetc: @echo 'Making C-Kermit $(CKVER) for DIAB DS90 with old DNIX 5.2...' $(MAKE) wermit \ "CFLAGS = -DATTSV -DNAP -DRDCHK -DDIRENT \ -DTCPSOCKET -DCK_CURSES -I/usr/include/bsd -U__STDC__ $(KFLAGS)" \ "LIBS = -ln -lcurses" #DIAB DS90 with DNIX 5.3 or later, with HDB UUCP, nap() and rdchk(). dnix5r3: @echo 'Making C-Kermit $(CKVER) for DIAB DS90 with DNIX 5.3...' @echo 'with Honey DanBer UUCP' $(MAKE) wermit \ "CFLAGS = -DSVR3 -DHDBUUCP -DNAP -DRDCHK -DDIRENT \ -DCK_CURSES -DRENAME $(KFLAGS) -O" "LIBS= -lcurses" #DIAB DS90 with DNIX 5.3 or later, with HDB UUCP, nap() and rdchk() + TCP/IP dnix5r3net: @echo 'Making C-Kermit $(CKVER) for DIAB DS90 with DNIX 5.3...' @echo 'with Honey DanBer UUCP and TCP/IP' $(MAKE) wermit \ "CFLAGS = -DSVR3 -DHDBUUCP -DNAP -DRDCHK -DDIRENT \ -DTCPSOCKET -DCK_CURSES -DRENAME $(KFLAGS) -O \ -I/usr/include/bsd" "LIBS = -ln -lcurses" #DIAB DS90 with DNIX 5.3 2.2 or later, with HDB UUCP, nap() and rdchk(), #ANSI C compilation and libraries. #Note that for DNIX 5.3 2.2 you have to correct a bug in /usr/include/stdlib.h: #change "extern void free(char *str);" #to "extern void free(void *str);" #NOTE: This bug is reportedly fixed in DNIX 5.3 2.2.1. #Should you get fatal errors caused by harmless pointer-type mismatches, #like between signed and unsigned char, just remove -X7. dnix5r3ansi: @echo 'Making C-Kermit $(CKVER) for DIAB DS90 with DNIX 5.3...' @echo 'with ANSI C Honey DanBer UUCP' $(MAKE) wermit \ "CFLAGS = -DSVR3 -DDIAB -DHDBUUCP -DNAP -DRDCHK -DDIRENT \ -DCK_ANSILIBS -DCK_CURSES -DRENAME -O -X7 -X9 $(KFLAGS)" \ "LIBS= -lcurses" #DIAB DS90 with DNIX 5.3 2.2 or later, with HDB UUCP, nap() and rdchk(), # + TCP/IP, ANSI C compilation and libraries. #Should you get fatal errors caused by harmless pointer-type mismatches, #like between signed and unsigned char, just remove -X7. dnix5r3ansinet: @echo 'Making C-Kermit $(CKVER) for DIAB DS90 with DNIX 5.3...' @echo 'with ANSI C Honey DanBer UUCP' $(MAKE) wermit \ "CFLAGS = -DSVR3 -DDIAB -DHDBUUCP -DNAP -DRDCHK -DDIRENT \ -DTCPSOCKET -DCK_ANSILIBS -DCK_CURSES -DRENAME -O -X7 -X9 $(KFLAGS) \ -I/usr/include/bsd" "LIBS= -ln -lcurses" #Ridge 32 with ROS 3.2 ridge32: @echo 'Making C-Kermit $(CKVER) Ridge 32 ROS 3.2' $(MAKE) wermit \ "CFLAGS = -DATTSV -DNOFILEH -DNODEBUG -DNOTLOG $(KFLAGS) -i -O" \ "LNKFLAGS = -i" #Altos 486, 586, or 986 with Xenix 3.0 altos: @echo 'Making C-Kermit $(CKVER) for Altos x86 with Xenix 3.0...' $(MAKE) wermit \ "CFLAGS= -DATTSV -DA986 -DNODEBUG -DNOTLOG $(KFLAGS) -i -O" \ "LNKFLAGS= -i" #Altos 986 with Xenix 3.0, as above, but command-line only, minimal size. #For systems with small memories. It might also be necessary to chop certain #modules up into smaller pieces, e.g. ckuus3-6, because of symbol table #overflow. If this makefile is too big or complex for the Altos, compile #and link by hand or write shell scripts. altosc: @echo 'Making C-Kermit $(CKVER) for Altos x86 Xenix 3.0, remote...' $(MAKE) wermit \ "CFLAGS= -DATTSV -DA986 -DNODEBUG -DNOTLOG -DNOSCRIPT -DNODIAL \ -DNOCSETS -DNOANSI -DNOMSEND -DNOSPL -DNOICP $(KFLAGS) -Mm -O" \ "LNKFLAGS= -Mm -s" #Altos 986 with Xenix 3.0, as above, but interactive only, minimal size. altosi: @echo 'Making C-Kermit $(CKVER) for Altos x86 Xenix 3.0, local...' $(MAKE) wermit \ "CFLAGS= -DATTSV -DA986 -DNODEBUG -DNOTLOG -DNOSCRIPT -DNODIAL \ -DNOCSETS -DNOANSI -DNOMSEND -DNOSPL -DNOCMDL -DNOFRILLS -DNOHELP \ -DNOSETKEY $(KFLAGS) -Mm -O" "LNKFLAGS= -Mm -s" # Altos ACS68000 68000 System, UNIX System 3 Release 2, 512k memory. # also needs getcwd() external function; see ckuins.txt file. # also, sys/types.h needed modifying: # #ifdef __SYS_TYPES_H__, #define ..., #endif # also, ckuus2.c MUST be compiled NOOPT else symbol table is destroyed! # Submission by Robert Weiner/Programming Plus, rweiner@progplus.com. # altos3: @echo 'Making C-Kermit $(CKVER) for Altos ACS68k UNIX System III' $(MAKE) ckuus2.$(EXT) "CFLAGS = -DATTSV -DNOCSETS -DNOSETKEY -DNOJC \ -DNODIAL -DDCLPOPEN -DNOSCRIPT -DNOHELP $(KFLAGS) -i" $(MAKE) wermit \ "CFLAGS = -DATTSV -DNOCSETS -DNOSETKEY -DNOJC \ -DNODIAL -DDCLPOPEN -DNOSCRIPT -DNOHELP $(KFLAGS) -i -O" \ "LNKFLAGS = -i" "LIBS = getcwd.$(EXT)" #MINIX - Original PC version with 64K+64K limit. # Reportedly, the linker (asld) can run out of space while linking. The only # way around this is to make a copy of libc.a from which all modules that are # not used by Kermit are removed. If you have trouble compiling or running # wart, "touch wart". If that doesn't help, "touch ckcpro.c". # The version configured below has no interactive command parser. # If you can build this version successfully, maybe there will be room for # a minimal interactive command parser too; try replacing -DNOICP with # -DNOSPL, plus every other -DNOxxx flag there is, except for -DNOICP # (see ckccfg.txt). minix: @echo 'Making C-Kermit $(CKVER) for MINIX, no command parser...' @echo 'TOTALLY UNTESTED!' $(MAKE) wermit EXT=s \ "CFLAGS= -DV7 -DMINIX -i -D_MINIX -D_POSIX_SOURCE \ -DPID_T=pid_t -DUID_T=uid_t -DGID_T=gid_t -DSIG_V \ -DNOXMIT -DNOMSEND -DNOFRILLS -DNODIAL -DNOHELP -DNODEBUG -DNOTLOG \ -DNOSCRIPT -DNOCSETS -DNOICP -DNOSETKEY $(KFLAGS)" \ "LNKFLAGS= -i -T" #MINIX - PC version with 64K+64K limit, new (as yet unreleased) ACK 2.0 beta C #compiler, which outputs .o object files, rather than .s. But 'make' still #expects .s files, so must be patched to use .o. Tested on Minix 1.5.10. minix15: @echo 'Making C-Kermit $(CKVER) for MINIX (new ACK 2.0 compiler),' @echo 'no command parser... TOTALLY UNTESTED!' $(MAKE) wermit \ "CFLAGS= -DV7 -DMINIX -i -D_MINIX -D_POSIX_SOURCE \ -DPID_T=pid_t -DUID_T=uid_t -DGID_T=gid_t -DSIG_V -DNODIAL \ -DNOHELP -DNODEBUG -DNOTLOG -DNOSCRIPT -DNOCSETS -DNOICP $(KFLAGS)" \ "LNKFLAGS= -i -T" #MINIX3 - MINIX 3.0 (no VM) - May-Aug 2005 (not sure if this ever worked...) minix3: @echo 'Making C-Kermit $(CKVER) for MINIX3...' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DPOSIX -DNOUUCP -DNOLEARN $(KFLAGS) -DMINIX2 \ -DMINIX3 -DNO_PARAM_H -DNOSYSLOG -DNOGETUSERSHELL \ -DNOINITGROUPS -DNOFTRUNCATE -DNOARROWKEYS -DDNOREALPATH \ -DTCPSOCKET -DNOTIMEZONE -DNOFTP -DNO_DNS_SRV -O" #MINIX315 - MINIX 3 1.5 - January 2010 minix315: @echo 'Making C-Kermit $(CKVER) for Minix 3 1.5...' $(MAKE) wermit KTARGET=$${KTARGET:-$(@)} \ "CFLAGS= -DMINIX315 -DPOSIX -DNOUUCP -DNOJC -DNOLEARN $(KFLAGS) \ -DHAVE_OPENPTY -DNO_PARAM_H -DNOSYSLOG -DNOGETUSERSHELL \ -DSYSTIMEH -DNOINITGROUPS -DNOFTRUNCATE -DNOARROWKEYS -DNOREALPATH \ -DTCPSOCKET -DNOTIMEZONE -DNO_DNS_SRV -DNOFTP -O" #PFU Compact A Series UNIX System V R3, SX/A TISP V10/L50 (Japan) #Maybe the -i link option should be removed? sxae50: @echo 'Making C-Kermit $(CKVER) for PFU SX/A V10/L50...' $(MAKE) xermit \ "CFLAGS= -DSVR3 -DDIRENT -DsxaE50 -DTCPSOCKET $(KFLAGS) -i -O" \ "LNKFLAGS= " #Tektronix 6130, 4319, 4301, etc, with UTek OS, /usr/spool/uucp/LCK./... #The models that support hardware flow control. utek: @echo 'Making C-Kermit $(CKVER) for 4.2BSD/UTek, hardware flow control' $(MAKE) wermit \ "CFLAGS= -O -DLCKDIR -DBSD4 -DTCPSOCKET \ -DUTEK -DDCLPOPEN -DLOCK_DIR=\\\"/usr/spool/uucp/LCK.\\\" \ -DTRMBUFL=2048 -DCK_DTRCTS $(KFLAGS)" #Tektronix 4315, 4316, 4317 with UTek OS, /usr/spool/uucp/LCK./... #The models that do not fully support hardware flow control. uteknohwfc: @echo 'Making C-Kermit $(CKVER) for 4.2BSD/UTek, no h/w flow control' $(MAKE) wermit \ "CFLAGS= -O -DLCKDIR -DBSD4 -DTCPSOCKET \ -DUTEK -DDCLPOPEN -DLOCK_DIR=\\\"/usr/spool/uucp/LCK.\\\" \ -DTRMBUFL=2048 $(KFLAGS)" #Tektronix XD88 with UTekV OS utekvr3: @echo 'Making C-Kermit $(CKVER) for Tektronix XD88 UTekV R3...' $(MAKE) wermit \ "CFLAGS= -DSVR3 -DDIRENT -DHDBUUCP \ -DTCPSOCKET -DSYSUTIMEH -DCK_CURSES $(KFLAGS) -O" \ "LIBS= -lcurses" "LNKFLAGS= -s" #Perkin-Elmer 3200 Xelos R02 or earlier ccop1: @echo 'Making C-Kermit $(CKVER) for Xelos & Public Domain Dirent calls' @echo 'or System V R2 or earlier...' $(MAKE) wermit \ "CFLAGS = -DATTSV -Dvoid=int -DDIRENT -DCK_CURSES \ $(KFLAGS) -O" "LNKFLAGS =" "LIBS= -lcurses -ltermlib" #Encore, UMAX 4.3 (BSD) but without acucntrl program. encore: $(MAKE) "MAKE=$(MAKE)" umax43 "KFLAGS=$(KFLAGS)" #Encore, as above, but with curses file transfer display included. encorec: $(MAKE) "MAKE=$(MAKE)" umax43 "KFLAGS=-DCK_CURSES $(KFLAGS)" \ "LIBS= -lcurses -ltermcap" #Encore, UMAX 4.3 (BSD) but without acucntrl program. umax43: @echo Making C-Kermit $(CKVER) for Encore UMAX 4.3... $(MAKE) "MAKE=$(MAKE)" PARALLEL=4 xermit \ "CFLAGS= -DBSD43 -DENCORE -DTCPSOCKET $(KFLAGS) -O" #Encore, UMAX 4.2 (BSD) umax42: @echo Making C-Kermit $(CKVER) for Encore UMAX 4.2... $(MAKE) "MAKE=$(MAKE)" PARALLEL=4 xermit \ "CFLAGS= -DBSD4 -DENCORE -DTCPSOCKET $(KFLAGS) -O" #Encore 88K UMAX 5.3 with TCP/IP support encore88k: @echo 'Making C-Kermit $(CKVER) for Encore 88K UMAX V, TCP/IP...' $(MAKE) xermit \ "CFLAGS = -q ext=pcc -DSVR3 -DTCPSOCKET -DDIRENT \ -DNOGETID_PROTOS -DHDBUUCP $(KFLAGS) -O" "LNKFLAGS =" #Encore 88K UMAX 5.3 with TCP/IP support encore88kgcc: @echo 'Making C-Kermit $(CKVER) for Encore 88K UMAX V, TCP/IP, gcc...' $(MAKE) xermit CC=gcc CC2=gcc \ "CFLAGS = -DSVR3 -DTCPSOCKET -DDIRENT \ -DNOGETID_PROTOS -DHDBUUCP $(KFLAGS) -O" "LNKFLAGS =" #SONY NEWS, NEWS-OS 4.01C sonynews: @echo Making C-Kermit $(CKVER) for SONY NEWS-OS 4.01C... $(MAKE) xermit "CFLAGS= -DBSD43 -DACUCNTRL -DTCPSOCKET -O" #Run Lint on this mess for selected versions. #These are pretty much obsolete since ANSI C / gcc. lintsun: @echo 'Running Lint on C-Kermit $(CKVER) sources for SunOS version...' lint -x -DSUNOS4 -DDIRENT -DTCPSOCKET -DSAVEDUID \ ck[cu]*.c > ckuker.lint.sun lintbsd: @echo 'Running Lint on C-Kermit $(CKVER) sources for BSD 4.2 version..' lint -x -DBSD4 -DTCPSOCKET ck[cu]*.c > ckuker.lint.bsd42 lints5: @echo 'Running Lint on C-Kermit $(CKVER) sources for Sys V version...' lint -x -DATTSV ck[cu]*.c > ckuker.lint.s5 #Who remembers TECO? love: @echo 'Not war?' ckcbwr.txt0000664000015300001460000021624211624230427011707 0ustar fdckermit [1]The Columbia Crown The Kermit Project | Columbia University 612 West 115th Street, New York NY 10025 USA o [2]kermit@columbia.edu ...since 1981 [3]Home [4]Kermit 95 [5]C-Kermit [6]Scripts [7]Current [8]New [9]FAQ [10]Support As of: C-Kermit 9.0.300, 30 June 2011 This page last updated: Tue Jun 28 08:54:30 2011 (New York USA Time) IF YOU ARE READING A PLAIN-TEXT version of this document, it is a plain-text dump of a Web page. You can visit the original (and possibly more up-to-date) Web page here: [11]http://www.columbia.edu/kermit/ckcbwr.html This document contains platform-independent C-Kermit hints and tips. Also see the platform-specific C-Kermit hints and tips document for your platform, for example: [12]http://www.columbia.edu/kermit/ckubwr.html for Unix. This document also applies to [13]Kermit 95 for Windows, which is based on C-Kermit. [ [14]C-Kermit ] [ [15]TUTORIAL ] CONTENTS 0. [16]PATCHES 1. [17]INCOMPATIBLE CHANGES 2. [18]THE C-KERMIT COMMAND PARSER 3. [19]MULTIPLE SESSIONS 4. [20]NETWORK CONNECTIONS 5. [21]MODEMS AND DIALING 6. [22]DIALING HINTS AND TIPS 7. [23]TERMINAL SERVERS 8. [24]TERMINAL EMULATION 9. [25]KEY MAPPING 10. [26]FILE TRANSFER 11. [27]SCRIPT PROGRAMMING 0. PATCHES [ [28]Top ] [ [29]Contents ] [ [30]Next ] Source-level patches for C-Kermit 8.0.211: (None) 1. INCOMPATIBLE CHANGES [ [31]Top ] [ [32]Contents ] [ [33]Next ] These are not necessarily exhaustive lists. 1.1. C-Kermit 6.0 C-Kermit 6.0 was released 6 September 1996 and is completely documented in [34]Using C-Kermit, 2nd Edition. The following incompatible changes were made in C-Kermit 6.0: * Unless you tell C-Kermit otherwise, if a serial or network connection seems to be open, and you attempt to EXIT or to open a new connection, C-Kermit warns you that an active connection appears to be open and asks you if you really want to close it. If you do not want these warnings, add SET EXIT WARNING OFF to your customization file or script, or give this command at the prompt. * The default for SET { SEND, RECEIVE } PATHNAMES was changed from ON to OFF, to prevent unexpected creation of directories and depositing of incoming files in places you might not know to look. * The default for SET FILE INCOMPLETE was changed from DISCARD to KEEP to allow for file transfer recovery. * The default file-transfer block-check is now 3, rather than 1. If the other Kermit does not support this, the two will drop back to type 1 automatically unless the other Kermit fails to follow the protocol specification. * The default flow-control is now "auto" ("do the right thing for each type of connection"), not Xon/Xoff. * Backslash (\) is no longer a command continuation character. Only - (hyphen, dash) may be used for this in C-Kermit 6.0 and later. * Negative INPUT timeout now results in infinite wait, rather than 1 second. 1.2. C-Kermit 7.0 C-Kermit 7.0 was released 1 January 2000. Its new features are documented in the C-Kermit 7.0 Supplement, [35]http://www.columbia.edu/kermit/ckermit2.html. The following incompatible changes were made in C-Kermit 7.0: * The "multiline GET" command is gone. Now use either of the following forms instead: get remote-name local-name get /as-name:local-name remote-name If either name contains spaces, enclose it in braces (or, in C-Kermit 8.0, doublequotes). * To include multiple file specifications in a GET command, you must now use MGET rather than GET: mget file1 file2 file3 ... * C-Kermit 7.0 and later use FAST Kermit protocol settings by default. This includes "unprefixing" of certain control characters. Because of this, file transfers that worked with previous releases might not work in the new release especially against a non-Kermit-Project Kermit protocol implementation (but it is more likely that they will work, and much faster). If a transfer fails, you'll get a context-sensitive hint suggesting possible causes and cures. Usually SET PREFIXING ALL does the trick. * By default C-Kermit 7.0 and later send files in text or binary mode by looking at each file to see which is the appropriate mode. To restore the previous behavior, put SET TRANSFER MODE MANUAL and the desired SET FILE TYPE (TEXT or BINARY) in your C-Kermit initialization file. * The RESEND and REGET commands automatically switch to binary mode; previously if RESEND or REGET were attempted when FILE TYPE was TEXT, these commands would fail immediately, with a message telling you they work only when the FILE TYPE is BINARY. Now they simply do this for you. * SET PREFIXING CAUTIOUS and MINIMAL now both prefix linefeed (10 and 138) in case rlogin, ssh, or cu are "in the middle", since otherwise ~ might appear in Kermit packets, and this would cause rlogin, ssh, or cu to disconnect, suspend,escape back, or otherwise wreck the file transfer. Xon and Xoff are now always prefixed too, even when Xon/Xoff flow control is not in effect, since unprefixing them has proven dangerous on TCP/IP connections. * In UNIX, VMS, Windows, and OS/2, the DIRECTORY command is built into C-Kermit itself rather than implemented by running an external command or program. The built-in command might not behave the way the platform-specific external one did, but many options are available for customization. Of course the underlying platform-specific command can still be accessed with "!", "@", or "RUN" wherever the installation does not forbid. In UNIX, the "ls" command can be accessed directly as "ls" in C-Kermit. * SEND ? prints a list of switches rather than a list of filenames. If you want to see a list of filenames, use a (system-dependent) construction such as SEND ./? (for UNIX, Windows, or OS/2), SEND []? (VMS), etc. * In UNIX, OS-9, and Kermit 95, the wildcard characters in previous versions were * and ?. In C-Kermit 7.0 they are *, ?, [, ], {, and }, with dash used inside []'s to denote ranges and comma used inside {} to separate list elements. If you need to include any of these characters literally in a filename, precede each one with backslash (\). * SET QUIET { ON, OFF } is now on the command stack, just like SET INPUT CASE, SET COUNT, SET MACRO ERROR, etc, as described on p.458 of [36]Using C-Kermit, 2nd Edition. This allows any macro or command file to SET QUIET ON or OFF without worrying about saving and restoring the global QUIET value. For example, this lets you write a script that tries SET LINE on lots of devices until it finds one free without spewing out loads of error messages, and also without disturbing the global QUIET setting, whatever it was. * Because of the new "." operator (which introduces assignments), macros whose names begin with "." can not be invoked "by name". However, they still can be invoked with DO or \fexecute(). * The syntax of the EVALUATE command has changed. To restore the previous syntax, use SET EVALUATE OLD. * The \v(directory) variable now includes the trailing directory separator; in previous releases it did not. This is to allow constructions such as: cd \v(dir)data.tmp to work across platforms that might have different directory notation, such as UNIX, Windows, and VMS. * Prior to C-Kermit 7.0, the FLOW-CONTROL setting was global and sticky. In C-Kermit 7.0, there is an array of default flow-control values for each kind of connection, that are applied automatically at SET LINE/PORT/HOST time. Thus a SET FLOW command given before SET LINE/PORT/HOST is likely to be undone. Therefore SET FLOW can be guaranteed to have the desired effect only if given after the SET LINE/PORT/HOST command. * Character-set translation works differently in the TRANSMIT command when (a) the file character-set is not the same as the local end of the terminal character-set, or (b) when the terminal character-set is TRANSPARENT. 1.3. C-Kermit 8.0 The following incompatible changes were made in C-Kermit 8.0: * C-Kermit now accepts doublequotes in most contexts where you previously had to use braces to group multiple words into a single field, or to force inclusion of leading or trailing blanks. This might cause problems in contexts where you wanted the doublequote characters to be taken literally. Consult [37]Section 5 of the [38]C-Kermit 8.0 Update Notes for further information. * Using the SET HOST command to make HTTP connections is no longer supported. Instead, use the new [39]HTTP OPEN command. 1.4. C-Kermit 9.0 The [40]\fsplit() function is incredibly handy, it can do almost anything, up to and including parsing a LISP program (the underlying code is the basis of the [41]S-Expression interpreter). But did you ever try to use it to parse (say) a Tab-Separated-List (TSV file) or Comma-Separated-List (CSV)? It works as expected as long as the data contains only 7-bit characters. But if your data contains (say) Spanish or German or Russian text written in an 8-bit character set such as ISO 8859-1, every 8-bit character (any value 128-255) is treated as a break character. This is fixed in C-Kermit 9.0 by treating all 8-bit bytes as "include" characters rather than break characters, a total reversal of past behavior. I don't think it will affect anyone though, because if this had happened to anyone, I would have heard about it! Since most standard 8-bit character sets have control characters in positions 128-160, it might have made sense to keep 128-160 in the break set, but with the proliferation of Microsoft Windows code pages, there is no telling which 8-bit character is likely to be some kind of text, e.g. "smart quotes" or East European or Turkish accented letters. 2. THE C-KERMIT COMMAND PARSER [ [42]Top ] [ [43]Contents ] [ [44]Next ] [ [45]Previous ] Various command-related limits are shown in the following table, in which the sample values are for a "large memory model" build of C-Kermit, typical for modern platforms (Linux, Solaris, AIX, VMS, etc). You can see the values for your version of Kermit by giving the SHOW FEATURES command. The maximum length for a Kermit command (CMDBL) also determines the maximum length for a macro definition, since DEFINE is itself a command. The maximum length for a variable name is between 256 and 4096 characters, depending on the platform; for array declarations and references, that includes the subscript. Item Symbol Sample Value Definition Number of characters in a command CMDBL 32763 ckucmd.h Number of chars in a field of a command ATMBL 10238 ckucmd.h Nesting level for command files MAXTAKE 54 ckuusr.h Nesting level for macros MACLEVEL 128 ckuusr.h Nesting level for FOR / WHILE loops FORDEPTH 32 ckuusr.h Number of macros MAC_MAX 16384 ckuusr.h Size of INPUT buffer INPBUFSIZ 4096 ckuusr.h Maximum files to match a wildcard MAXWLD 102400 ckcdeb.h Filespecs in MSEND command MSENDMAX 1024 ckuusr.h Length for GOTO target label LBLSIZ 50 ckuusr.h \fexecute() recursion depth limit CMDDEP 64 ckucmd.h If you need to define a macro that is longer than CMDBL, you can break the macro up into sub-macros or rewrite the macro as a command file. In a pinch you can also redefine CMDBL and recompile C-Kermit. All of these numbers represent tradeoffs: the bigger the number, the more "powerful" Kermit in the corresponding area, but also the bigger the program image and possibly disk footprint, and the longer it takes to load and initialize. In the interactive command parser: * EMACS- or VI-style command line editing is not supported. * Editing keys are hardwired (Ctrl-U, Ctrl-W, etc). If you interrupt C-Kermit before it has issued its first prompt, it will exit. This means that you cannot interrupt execution of the initialization file, or of an "application file" (file whose name is given as the first command-line argument), or of an alternative initialization file ("-y filename"), and get to the prompt. There is, however, one exception to this rule: you *can* interrupt commands -- including TAKE commands -- given in the '-C "command list"' command-line argument and -- if there were no action commands among the command-line arguments -- you will be returned to the C-Kermit prompt. So, for example, if you want to start C-Kermit in such a way that it executes a command file before issuing its first prompt, and you also want to be able to interrupt the command file and get to the prompt, include a TAKE command for the desired command in the -C argument, for example: kermit -C "take dial.scr" At the command prompt, if you use the backslash (\) prefix to enter a control character, space, or question mark into a command literally, the backslash disappears and is replaced by the quoted character. If it was a control character, it is shown as a circumflex (^). This allows editing (backspace, delete, Ctrl-W) to work correctly even for control characters. Priot to C-Kermit 8.0, the only way to include a comma literally in a macro definition -- as opposed to having it separate commands within the definition -- is to enter its ASCII value (44) in backslash notation, e.g.: DEFINE ROWS RUN MODE CO80\{44}\%1 In C-Kermit 8.0 you can use constructions like this: DEFINE ROWS RUN MODE "CO80,\%1" If you quote special characters in a filename (e.g. in the SEND command), filename completion may seem to work incorrectly. For example, if you have a file whose name is a*b (the name really contains an asterisk), and you type "send a\\*", the "b" does not appear, nor will Ctrl-R redisplay the completed name correctly. But internally the file name is recognized anyway. Question-mark help does not work during execution of an ASKQ command. The question marks are simply accepted as text. In OUTPUT commands only, \B sends a BREAK signal, \L sends a Long BREAK signal, and \N sends a NUL (ASCII 0). BREAK and Long BREAK are special signals, not characters, and NUL is a character that normally cannot be included in a C string, since it is the C string terminator. If you really want to output a backslash followed by a B, an L, or an N (as is needed to configure certain modems, etc), double the backslash, e.g. "output \\B". In C-Kermit 7.0 or later, you can disarm and re-arm the special OUTPUT-command escapes (\B, \L, and \N) with SET OUTPUT SPECIAL-ESCAPES { OFF, ON }. When using the command-line processor ("kermit -l /dev/tty00 -b 19200", etc), note that in some cases the order of the command-line options makes a difference, contrary to the expectation that order of command-line options should not matter. For example, the -b option must be given after the -l option if it is to affect the device specified in the -l option. 3. MULTIPLE SESSIONS [ [46]Top ] [ [47]Contents ] [ [48]Next ] [ [49]Previous ] C-Kermit 7.0 and earlier do not support multiple sessions. When you SET LINE (or SET PORT, same thing) to a new device, or SET HOST to a new host, the previous SET LINE device or network host connection is closed, resulting in hangup of the modem or termination of the network connection. In windowing environments like HP-VUE, NeXTSTEP, Windows, OS/2, etc, you can run separate copies of Kermit in different windows to achieve multiple sessions. To achieve multiple sessions through a single serial port (e.g. when dialing up), you can install SLIP or PPP on your computer and then use C-Kermit's TCP/IP support over the SLIP or PPP connection, assuming you also have TCP/IP networking installed on your computer. C-Kermit 8.0 has the same restriction on SET LINE and SET HOST sessions: only one regular session (dialout, Telnet, etc) can be open at a time. However, version 8.0 adds two new kinds of sessions: FTP and HTTP; one or both of these can be open at the same as a regular session. 4. NETWORK CONNECTIONS [ [50]Top ] [ [51]Contents ] [ [52]Next ] [ [53]Previous ] FTP Client Bugs The Unix C-Kermit 8.0.206 FTP client had the following bugs at the time most of the 8.0.206 binaries were built for the C-Kermit 8.0 CDROM: 1. FTP MGET fails when directory segments contain wildcards, as in "ftp mget */data/*.dat". Work around by doing a separate MGET for each source directory. 2. FTP MGET can fail or produce random side effects if you have a TMPDIR or CK_TMP environment variable definition in effect, or a SET TEMP-DIRECTORY value, longer than 7 characters. Work around by giving a SET TEMP-DIRECTORY command with a short value, such as "/tmp". These two bugs are fixed in the source code that is included on the CDROM, and also in Kermit 95 2.1.1. You can tell if a C-Kermit 8.0.206 binary has these fixes by typing SHOW VERSION; if it says "FTP Client, 8.0.200, 24 Oct 2002" it has the fixes; if the edit number is less that 200, it doesn't, in which case can build a new binary from the source code (or contact us and we'll try to get get one for you). Making TCP/IP Connections Can Take a Long Time The most frequently asked question in many newsgroups is "Why does it take such a long time to make a Telnet connection to (or from) my (e.g.) Linux PC?" (this applies to Kermit as well as to regular Telnet clients): 1. Most Telnet servers perform reverse DNS lookups on the client for security and/or logging reasons. If the Telnet client's host cannot be found by the server's local DNS server, the DNS request goes out to the Internet at large, and this can take quite some time. The solution to this problem is to make sure that both client and host are registered in DNS. 2. C-Kermit itself performs reverse DNS lookups unless you tell it not to. This is to allow C-Kermit to let you know which host it is actually connected to in case you have made a connection to a "host pool" (multihomed host). You can disable C-Kermit's reverse DNS lookup with SET TCP REVERSE-DNS-LOOKUP OFF. 3. C-Kermit 7.0 and later strictly enforce Telnet protocol rules. One such rule is that certain negotiations must be responded to. If C-Kermit sends a such a negotiation and the host does not respond, C-Kermit waits a long time for the reply (in case the network is congested or the host is slow), but eventually will time out. To eliminate the waits (and therefore risk possible protocol mismatches -- or worse -- between Telnet client and server), tell C-Kermit to SET TELNET WAIT OFF (or include the /NOWAIT switch with the TELNET command). The Rlogin Client In multiuser operating systems such as UNIX and VMS, TCP/IP Rlogin connections are available only to privileged users, since "login" is a privileged socket. Assuming you are allowed to use it in the first place, it is likely to behave differently depending on what type of host you are rlogging in to, due to technical reasons having to do with conflicting interpretations of RFC793 (Out-Of-Band Data) and Rlogin (RFC1122)... "Specifically, the TCP urgent pointer in BSD points to the byte after the urgent data byte, and an RFC-compliant TCP urgent pointer points to the urgent data byte. As a result, if an application sends urgent data from a BSD-compatible implementation to an [54]RFC-1122 compatible implementation then the receiver will read the wrong urgent data byte (it will read the byte located after the correct byte in the data stream as the urgent data byte)." Rlogin requires the use of OOB data while Telnet does not. Therefore, it is possible for Telnet to work between all systems while BSD and System V TCP/IP implementations are almost always a bad mix. The Telnet Client On a TCP/IP TELNET connection, you should normally have PARITY set to NONE and (except in VMS C-Kermit) FLOW-CONTROL also set to NONE. If file transfer does not work with these settings (for example, because the remote TELNET server only gives a 7-bit data path), use SET PARITY SPACE. Do not use SET PARITY MARK, EVEN, or ODD on a TELNET connection -- it interferes with TELNET protocol. If echoing does not work right after connecting to a network host or after dialing through a TCP/IP modem server, it probably means that the TELNET server on the far end of the connection is executing the TELNET protocol incorrectly. After initially connecting and discovering incorrect echoing (characters are echoed twice, or not at all), escape back, give the appropriate SET DUPLEX command (FULL or HALF), and then CONNECT again. For a consistently misbehaving connection, you can automate this process in a macro or TAKE file. TELNET sessions are treated just like serial communications sessions as far as "terminal bytesize" and "command bytesize" are concerned. If you need to view and/or enter 8-bit characters during a TELNET session, you must tell C-Kermit to SET TERMINAL BYTESIZE 8, SET COMMAND BYTESIZE 8, and SET PARITY NONE. If you SET TELNET DEBUG ON prior to making a connection, protocol negotiations will be displayed on your screen. You can also capture them in the debug log (along with everything else) and then extract them easily, since all Telnet negotiations lines begin with (uppercase) "TELNET". The SSH Client C-Kermit does not have its own built-in SSH client; instead, in the Unix tradition, uses the external SSH client to do the SSH part, and Kermit does its thing on top -- file transfer, scripting, etc. Under certain circumstances that have not yet been identified, it sometimes happens that after making an SSH connection from C-Kermit, logging out from the remote host, and popping back to the local C-Kermit program, subsequent SSH commands file with a message like "Network type not supported". Starting a new copy of C-Kermit is the workaround. 5. MODEMS AND DIALING [ [55]Top ] [ [56]Contents ] [ [57]Next ] [ [58]Previous ] External modems are recommended because: * They don't need any special drivers. * They are less likely to interfere with normal operation of your computer. * You can use the lights and speaker to troubleshoot dialing. * You can share them among all types of computers. * You can easily turn them off and on when power-cycling seems warranted. * They are more likely to have manuals. Modems can be used by C-Kermit only when they are visible as or through a regular serial port device. Certain modems can not be used in this normal way on many kinds of computers: Winmodems, RPI modems, Controllerless modems, the IBM Mwave, etc; all of these require special drivers that perform some, most, or all of the modem's functions in software. Such drivers are generally NOT available in UNIX or other non-Windows (or non-OS/2, in the case of the Mwave) platforms. In order to dial a modem, C-Kermit must know the modem's repertoire of commands and responses. Each modem make and model is likely to have a different repertoire. Since Kermit has no way of knowing which kind of modem will be dialed, normally you have to tell it with a SET MODEM TYPE command, e.g.: set modem type usrobotics set line /dev/cua0 set speed 57600 dial 7654321 In the early days, there was a wide variety of modems and command languages. Nowadays, almost every modem uses the Hayes AT command set (but with some differences in the details) and its startup configuration includes error correction, data compression, and hardware (RTS/CTS) flow control. As long as C-Kermit is capable of hardware flow control (as it is on many, but not all, the platforms where it runs, since some operating systems don't support it), the modem can be dialed immediately, without lengthy configuration dialogs, and in fact this is what SET MODEM TYPE GENERIC-HIGH-SPEED does. In C-Kermit 8.0, GENERIC-HIGH-SPEED has become the default modem type, so now it is usually possible to SET LINE, SET SPEED, and DIAL without having to identify your modem. If this doesn't work, of course, then you might have to fall back to the traditional method: Give a SET MODEM TYPE for a specific modem first, then SET LINE, SET SPEED, and DIAL. An important change in C-Kermit 6.0 is that when you give a SET MODEM TYPE command to tell Kermit what kind of modem you have, Kermit also sets a number of other modem-related parameters automatically from its internal modem database. Thus, the order in which you give modem-related commands is significant, whereas in prior releases they could be given in any order. In particular, MODEM SPEED-MATCHING is set according to whether the modem is known to be capable of speed buffering. SET MODEM TYPE HAYES-2400 automatically turns SPEED-MATCHING ON, because when the Hayes 2400 reports a particular speed in its CONNECT message, that means its interface speed has changed to that speed, and C-Kermit's must change accordingly if it is to continue communicating. This might cause some confusion if you use "set modem type hayes" for dialing a more advanced type of modem. The new default for flow control is "auto", meaning "do the right thing for each type of connection". So (for example) if your version of C-Kermit supports SET FLOW RTS/CTS and your modem also supports RTS/CTS, then Kermit automatically sets its flow control to RTS/CTS and set modem's flow control to RTS/CTS too before attempting to use the modem. For these reasons, don't assume that "set modem type hayes" should be used for all modems that uses the Hayes AT command set. "set modem type hayes" really does mean Hayes 1200 or 2400, which in turn means no hardware flow control, and no speed buffering. This choice will rarely work with a modern high-speed modem. 6. DIALING HINTS AND TIPS [ [59]Top ] [ [60]Contents ] [ [61]Next ] [ [62]Previous ] If you have a high-speed, error-correcting, data-compressing, speed-buffering modem, you should fix the modem's interface speed as high as possible, preferably (at least) four times higher than its maximum connection (modulation) speed to allow compression to work at full advantage. In this type of setup, you must also have an effective means of flow control enabled between C-Kermit and the modem, preferably hardware (RTS/CTS) flow control. On platforms that do not support hardware flow control, it is usually possible to select software flow control (Xon/Xoff), and C-Kermit will do its best to set the modem for local Xon/Xoff flow control too (but then, of course, Ctrl-S and Ctrl-Q characters can not be transmitted on the connection). If you are having trouble dialing your modem, SET DIAL DISPLAY ON to watch the dialing interactions between C-Kermit and your modem. Consult Chapters 3-4 of [63]Using C-Kermit (2nd Ed) for modem-dialing troubleshooting instructions. The following sections offer some additional hints and tips. 6.1. Syntax If you want to dial a number that starts with #, you'll need to quote the "#" character (as \# or \{35}), since it is also a comment introducer: C-Kermit>dial #98765421-1-212-5551212 ; Looks like a comment ?You must specify a number to dial C-Kermit>dial \#98765421-1-212-5551212 ; Works OK C-Kermit>dial =#98765421-1-212-5551212 ; This works too When using a dialing directory, remember what happens if a name is not found: C-Kermit>dial xyzcorp Lookup: "xyzcorp" - not found - dialing as given This normally does no harm, but some modems might behave strangely when given dial strings that contain certain letters. For example, a certain German modem treats any dial string that contains the letter "s" as a command to fetch a number from its internal list, and replies OK to the ATD command, which is normally not a valid response except for partial dialing. To avoid this situation, use: lookup xyzcorp if success dial 6.2. The Carrier Signal Remember: In many C-Kermit implementations (depending on the underlying operating system -- mostly Windows, OS/2, and System-V-based UNIX versions, and in C-Kermit 7.0, also VMS), you can't CONNECT to a modem and type the modem's dialing command (like "ATDT7654321") manually, unless you first tell C-Kermit to: SET CARRIER-WATCH OFF This is because (in these implementations), the CONNECT command requires the modem's Carrier Detect (CD) signal to be on, but the CD signal doesn't come on until after dialing is complete. This requirement is what allows C-Kermit to pop back to its prompt automatically when the connection is hung up. See the description of SET CARRIER-WATCH in "Using C-Kermit". Similarly, if your dialed connection drops when CARRIER-WATCH is set to AUTO or ON, you can't CONNECT back to the (now disconnected) screen to see what might have happened unless you first SET CARRIER-WATCH OFF. But sometimes not even SET CARRIER-WATCH OFF will help in this situation: certain platforms (for example Unixware 2.1), once carrier drops, won't let the application do i/o with the device any more. In that case, if you want to use the device again, you have to CLOSE it and OPEN it again. Or you can have Kermit do this for you automatically by telling it to SET CLOSE-ON-DISCONNECT ON. 6.3. Dialing and Flow Control Don't SET FLOW RTS/CTS if your modem is turned off, or if it is not presenting the CTS signal. Otherwise, the serial device driver can get stuck waiting for this signal to appear. Most modern modems support RTS/CTS (if they support any hardware flow control at all), but some computers use different RS-232 circuits for the same purposes, e.g. DTR and CD, or DTR and CTS. In such cases, you might be able to make your computer work with your modem by appropriately cross-wiring the circuits in the cable connector, for example the computer's DTR to the modem's RTS, and modem's CD to the computer's CTS. HOWEVER, C-Kermit does not know you have done this. So if you have (say) SET FLOW DTR/CD, C-Kermit will make no attempt to tell the modem to use RTS/CTS. You probably did this yourself when you configured the modem. 6.4. The Dial Timeout If it takes your call longer to be completed than the timeout interval that C-Kermit calculates, you can use the SET DIAL TIMEOUT command to override C-Kermit's value. But beware: the modem has its own timeout for completing the call. If it is a Hayes-like modem, C-Kermit adjusts the modem's value too by setting register S7. But the maximum value for S7 might be smaller than the time you need! In that case, C-Kermit sets S7 to 0, 255, or other (modem-specific) value to signify "no timeout". If Kermit attempts to set register S7 to a value higher than your modem's maximum, the modem will say "ERROR" and you will get a "Failure to initialize modem" error. In that case, use SET DIAL TIMEOUT to override C-Kermit's calculation of the timeout value with the highest value that is legal for your modem, e.g. 60. 6.5. Escape Sequence Guard Time A "TIES" (Time-Independent Escape Sequence) modem does not require any guard time around its escape sequence. The following text: +++ATH0 if sent through a TIES modem, for example because you were uploading this file through it, could pop the modem back into command mode and make it hang up the connection. Later versions of the Telebit T1600 and T3000 (version LA3.01E firmware and later), and all WorldBlazers, use TIES. Although the probability of "+++" appearing in a Kermit packet is markedly lower than with most other protocols (see the [64]File Transfer section below), it can still happen under certain circumstances. It can also happen when using C-Kermit's TRANSMIT command. If you are using a Telebit TIES modem, you can change the modem's escape sequence to an otherwise little-used control character such as Ctrl-_ (Control-Underscore): AT S2=31 A sequence of three consecutive Ctrl-_ characters will not appear in a Kermit packet unless you go to extraordinary lengths to defeat more than a few of Kermit's built-in safety mechanisms. And if you do this, then you should also turn off the modem's escape-sequence recognition altogether: AT S48=0 S2=255 But when escape sequence recognition is turned off, "modem hangup" (+++ATH0) will not work, so you should also SET MODEM HANGUP RS232-SIGNAL (rather then MODEM-COMMAND). 6.6. Adaptive Dialing Some modems have a feature called adaptive dialing. When they are told to dial a number using Tone dialing, they check to make sure that dialtone has gone away after dialing the first digit. If it has not, the modem assumes the phone line does not accept Tone dialing and so switches to Pulse. When dialing out from a PBX, there is almost always a secondary dialtone. Typically you take the phone off-hook, get the PBX dialtone, dial "9" to get an outside line, and then get the phone company's dialtone. In a situation like this, you need to tell the modem to expect the secondary dialtone. On Hayes and compatible modems, this is done by putting a "W" in the dial string at the appropriate place. For example, to dial 9 for an outside line, and then 7654321, use ATDT9W7654321: SET PBX-OUTSIDE-PREFIX 9W (replace "9" with whatever your PBX's outside-line prefix is). 6.7. The Busy Signal Some phone companies are eliminating the busy signal. Instead, they issue a voice message such as "press 1 to automatically redial until the number answers, or...". Obviously this is a disaster for modem calls. If your service has this feature, there's nothing Kermit can do about it. Your modem will respond with NO CARRIER (after a long time) rather than BUSY (immediately), and Kermit will declare the call a failure, rather than trying to redial the same number. 6.8. Hanging Up There are two ways to hang up a modem: by turning off the serial port's DTR signal (SET MODEM HANGUP-METHOD RS232-SIGNAL) or sending the modem its escape sequence followed by its hangup command (SET MODEM HANGUP-METHOD MODEM-COMMAND). If one doesn't work, try the other. If the automatic hangup performed at the beginning of a DIAL command causes trouble, then SET DIAL HANGUP OFF. The HANGUP command has no effect when C-Kermit is in remote mode. This is on purpose. If C-Kermit could hang up its own controlling terminal, this would (a) most likely leave behind zombie processes, and (b) pose a security risk. If you DIAL a modem, disconnect, then SET HOST or TELNET, and then HANGUP, Kermit sends the modem's hangup command, such as "+++ATHO". There is no good way to avoid this, because this case can't reliably be distinguished from the case in which the user does SET HOST terminal-server, SET MODEM TYPE name, DIAL. In both cases we have a valid modem type selected and we have a network connection. If you want to DIAL and then later make a regular network connection, you will have to SET MODEM TYPE NONE or SET DIAL HANGUP OFF to avoid this phenomenon. 7. TERMINAL SERVERS [ [65]Top ] [ [66]Contents ] [ [67]Next ] [ [68]Previous ] Watch out for terminal server's escape character -- usually a control character such as Ctrl-Circumflex (Ctrl-^). Don't unprefix it in Kermit! Ciscos -- must often be told to "terminal download"... Cisco ASM models don't have hardware flow control in both directions. Many terminal servers only give you a 7-bit connection, so if you can't make it 8-bit, tell Kermit to "set parity space". The following story, regarding trouble transferring 8-bit files through a reverse terminal server, was contributed by an Annex terminal server user: Using C-Kermit on an HP 9000 712/80 running the HP-UX 10.00 operating system. The HP was connected to a Xylogics Annex MICRO-ELS-UX R7.1 8 port terminal server via ethernet. On the second port of the terminal server is an AT&T Paradyne 3810 modem, which is connected to a telephone line. There is a program which runs on the HP to establish a Telnet connection between a serial line on the Annex and a character special file on the HP (/dev file). This is an Annex specific program called rtelnet (reverse telnet) and is provided with the terminal server software. The rtelnet utility runs on top of the pseudo-terminal facility provided by UNIX. It creates host-originated connections to devices attached ot Annex serial ports. There are several command line arguments to be specified with this program: the IP address of the terminal server, the number of the port to attach to, and the name of the pseudo-device to create. In addition to these there are options to tell rtelnet how to operate on the connect: -b requests negotiation for Telnet binary mode, -d turns on socket-leve debugging, -f enables "connect on the fly" mode, -r removes the device-name if it already exists, etc. The most important of these to be specified when using 8 data bits and no parity, as we found out, was the -t option. This creates a transparent TCP connection to the terminal server. Again, what we assumed to be happening was that the rtelnet program encountered a character sequence special to itself and then "eating" those kermit packets. I think this is all of the information I can give you on the configuration, short of the values associated with the port on the terminal server. How to DIAL from a TCP/IP reverse terminal server (modem server): 1. (only if necessary) SET TELNET ECHO REMOTE 2. SET HOST terminal-server-ip-name-or-address [ port ] 3. SET MODEM TYPE modem-type 4. (only if necessary) SET DIAL HANGUP OFF 5. (for troubleshooting) SET DIAL DISPLAY ON 6. DIAL phone-number The order is important: SET HOST before SET MODEM TYPE. Since this is a Telnet connection, serial-port related commands such as SET SPEED, SET STOP-BITS, HANGUP (when MODEM HANGUP-METHOD is RS232), etc, have no effect. However, in C-Kermit 8.0, if the modem server supports [69]RFC-2217 Telnet Com-Port Control protocol, these commands do indeed take effect at the server's serial port. 8. TERMINAL EMULATION [ [70]Top ] [ [71]Contents ] [ [72]Next ] [ [73]Previous ] Except for the Windows, OS/2, and Macintosh versions, C-Kermit does not emulate any kind of terminal. Rather, it acts as a "semitransparent pipe", passing the characters you type during a CONNECT session to the remote host, and sending the characters received from the remote host to your screen. Whatever is controlling your keyboard and screen provides the specific terminal emulation: a real terminal, a PC running a terminal emulator, etc, or (in the case of a self-contained workstation) your console driver, a terminal window, xterm, etc. Kermit is semitransparent rather than fully transparent in the following ways: * During a TELNET ("set host") session, C-Kermit itself executes the TELNET protocol and performs TELNET negotiations. (But it does not perform TN3270 protocol or any other type of 3270 terminal emulation.) * If you have changed your keyboard mapping using SET KEY, C-Kermit replaces the characters you type with the characters or strings they are mapped to. * If you SET your TERMINAL CHARACTER-SET to anything but TRANSPARENT, C-Kermit translates your keystrokes (after applying any SET KEY definitions) before transmitting them, and translates received characters before showing them on your screen. * If your remote and/or local TERMINAL CHARACTER-SET is an ISO 646 7-bit national character set, such as German, French, Italian, Swedish, etc, or Short KOI used for Cyrillic, C-Kermit's CONNECT command automatically skips over ANSI escape sequences to avoid translating their characters. Only ANSI/ISO standard (VT100/200/300-like) 7-bit escape sequence formats are supported for this purpose, no proprietary schemes like H-P, Televideo, Tektronix, etc. * If your version of C-Kermit includes SET TERMINAL APC command, then C-Kermit's CONNECT command will handle APC escape sequences if TERMINAL APC is not set to OFF (which is the default). You can make C-Kermit fully transparent by starting it with the -0 (dash zero) command-line option. If you are running C-Kermit under a console driver, or in a terminal window, that emulates the VT100, and use C-Kermit to log in to a VMS system, the console driver or terminal window (not Kermit) is supposed to reply to the "what are you?" query (ESC Z) from the VAX. If it doesn't, and you can't make it do so, then you can (a) live with the "unknown terminal" problem; (b) tell VMS to SET TERMINAL/DEVICE=VT100; (c) program a key using SET KEY to send the appropriate sequence and then punch the key at the right time; or (d) use the VMSLOGIN macro that is defined in CKERMIT.INI to do this for you automatically. SET SESSION-LOG { TEXT, BINARY }, which is effective in UNIX and AOS/VS but not other C-Kermit versions, removes CR, DEL, NUL, XON, and XOFF characters (Using C-Kermit neglects to mention that XON and XOFF are removed). The TEXT-mode setting is ineffective during SCRIPT command execution, as well as on X.25 connections. 9. KEY MAPPING [ [74]Top ] [ [75]Contents ] [ [76]Next ] [ [77]Previous ] Except in the terminal-emulating versions, C-Kermit's key mapping facilities are limited to normal "ASCII" keys, and cannot be used with function keys, arrow keys, arcane key combinations, etc. Since C-Kermit runs on such a wide variety of hardware platforms (including, for example, more than 360 different UNIX platforms), it is not possible for C-Kermit to support every conceivable keyboard under every release of every UNIX (or VMS, or ...) product on every different kind of computer possibly under all manner of different console drivers, even if it had the means to do so. In technical terms, C-Kermit uses the read() function to read keystrokes, and read() returns a single byte (value 0 through 255). C-Kermit's SET KEY function applies to these single-byte codes. "Extended function" keys, such as F-keys, arrow keys, etc, usually return either a 2-byte "scan code" or else a character string (such as an escape sequence like " O p"). In both cases, C-Kermit has no way to tell the difference between such multibyte key values, and the corresponding series of single-byte key values. This could only be done by accessing the keyboard at a much lower level in a highly platform-dependent manner, probably requiring tens of thousands of lines of code to support even a sampling of the most popular workstation / OS combinations. However, most workstation console drivers (terminal emulation windows, etc) include their own key-mapping facility. For example in AIX, the AIXterm program (in whose window you would run C-Kermit) allows rebinding of the F1-F12 keys to arbitrary strings. The same is true of Xterm and DECterm windows, etc. Consult the technical documentation for your workstation or emulator. See sample Xterm (Xmodmap) mappings in the [78]Unix C-Kermit Hints and Tips document. The SET KEY command (except in Kermit 95) does not allow a key definition to be (or contain) the NUL (\0) character. 10. FILE TRANSFER [ [79]Top ] [ [80]Contents ] [ [81]Next ] [ [82]Previous ] C-Kermit 7.0 is the first release of C-Kermit to use fast (rather than robust and therefore slow) protocol defaults: long packets, sliding windows, control-character unprefixing, and streaming where possible. This makes most transfers (partner willing) dramatically faster "out of the box" but might break some combinations that worked before. If transfers with C-Kermit 7.0 or later fail where transfers worked with earlier C-Kermit versions, try the following (one at a time, in this order): 1. SET PREFIXING ALL: Disables control-character unprefixing. 2. SET STREAMING OFF: Disables streaming. 3. CAUTIOUS: Selects medium but cautious protocol settings. 4. ROBUST: this command reverts to the most conservative protocol settings. Execution of multiple file transfers by C-Kermit from a command file when in remote mode might exhibit long delays between each transfer. To avoid this, just include the command "SET DELAY 0" in your command file before any of the file-transfer commands. File transfer failures can occur for all sorts of reasons, most of them listed in Chapter 10 of [83]Using C-Kermit. The following sections touch on some that aren't. The [84]C-Kermit 7.0 Release Notes document SEND /COMMAND as taking an argument, but it doesn't. Instead of SEND /COMMAND:{some command}, use: SEND /COMMAND [ other switches such as /AS-NAME: ] command [ arguments... ] 10.1. Laptops Watch out for laptops and their assorted power-saver features; for example, a built-in modem's "auto timeout delay" hanging up the connection in the middle of a file transfer. Most modems, even if they have this feature, do not have it enabled by default. But if you experience otherwise inexplicable disconnections in the midst of your Kermit sessions, check the modem manual for such things as "idle timeout", "auto timeout", etc, and add the command to disable this feature to Kermit's init string for this modem. 10.2. NFS If uploading a large file to an NFS-mounted disk fails (or is painfully slow), try uploading it to a local disk (e.g. /tmp on Unix) and then copying to the NFS disk later. 10.3. Modems If you are dialing out and find that downloads work but uploads don't, try again with a lower serial-port speed. Case in point: dialing out on a certain PC from Linux at 115200 bps using a USR Courier 56K "V.Everything" external modem and RTS/CTS flow control. Downloads worked flawlessly, uploads stopped dead after the first few packets were sent. The modem lights showed constant retraining (ARQ light blinks slowly), and the CTS light was off 95% of the time, allowing nothing to get through. Reducing the serial port speed to 57600 bps made the problems go away. Evidently the PC in question has a very fast serial port, since dialing the same modem with a different PC at 115200 bps works without incident. 10.4. TCP/IP Connections If you have trouble transferring files over a TCP/IP connection, tell Kermit to SET PARITY SPACE and try again. If that doesn't work, also try a shorter packet length or smaller window size (to compensate for certain well-known broken Telnet servers), and/or SET RELIABLE OFF. 10.5. Multihop Connections If you have a multihop connection, with the interior nodes in CONNECT mode (Kermit, Telnet, Rlogin, or any other), you can expect (a) file transfer to be slower, and (b) the connection to be less transparent (to control characters, perhaps to the 8th bit) than a more direct connection. C-Kermit 7.0 and later have a "-0" (dash-zero) command-line option to make it 100% transparent in cases where it is to be used in the middle. 10.6. Recovery The recovery feature (RESEND command) that was added in version 5A(190) works only for binary-mode transfers. In order for this feature to be useful at all, the default for SET FILE INCOMPLETE was changed from DISCARD to KEEP. Otherwise an interrupted transfer would leave no partial file behind unless you had remembered to change the default. But now you have to pay closer attention to Kermit's messages to know whether a transfer succeeded or failed -- previously, if it failed, the file would not show up on the receiving end at all; in 5A(190) and later, you'll get a partial file which could easily be mistaken for the complete file unless you change the default back to DISCARD or read the screen messages, or keep a transaction log. 10.7. Filename Collisions SET FILE COLLISION BACKUP is the default. This means: * If you send the same file lots of times, there will be many backup files. There is no automatic mechanism within Kermit to delete them, no notion of a "version retention count", etc, but you can use the PURGE command to clean them up. * If a file arrives that has the same name as a directory, the file transfer fails because Kermit will not rename a directory. Send the file with another name, or use SET FILE COLLISION RENAME. * If the directory lacks write permission, the file transfer fails even if you have write access to the file that is being backed up; in that case, switch to SET FILE COLLISION OVERWRITE or APPEND, or send to a different directory. SET FILE COLLISION UPDATE depends on the date/time stamp in the attribute packet. However, this is recorded in local time, not Universal Time (GMT), and there is no indication of time zone. The time is expressed to the precision of 1 second, but some file systems do not record with this precision -- for example, MS-DOS records the file date/time only to the nearest 2 seconds. This might cause update operations to send more files than necessary. (This paragraph does NOT apply to UNIX, where, as of C-Kermit 7.0, C-Kermit pipes incoming mail and print material directly the mail or print program): When C-Kermit is receiving files from another Kermit program that has been given the MAIL or REMOTE PRINT command, C-Kermit follows the current filename collision action. This can be disconcerting if the action was (for example) BACKUP, because the existing file will be renamed, and the new file will be mailed (or printed) and then deleted. Kermit cannot temporarily change to RENAME because the file collision action occurs when the filename packet is received, and the PRINT or MAIL disposition only comes later, in the Attribute packet. Watch out for SET FILE COLLISION RENAME, especially when used in conjunction with recovery. Recall that this option (which is NOT the default) renames the incoming file if a file already exists with the same name (the default is to rename the previously existing file, and store the incoming file with its own name). It is strongly recommended that you do not use SET FILE COLLISION RENAME if you ever intend to use the recovery feature: * When the file is first received by C-Kermit, its name is changed if another file already has the same name. When you RESEND the same file after a failure, C-Kermit will probably try to append the re-sent portion to the wrong file. * Assuming that you get RESEND to work with FILE COLLISION RENAME, C-Kermit, when receiving the remainder of the file during a RESEND operation, will report back the wrong name. Nothing can be done about this because the name is reported back before the receiving Kermit program finds out that it is a recovery operation. Also watch out for DISABLE DELETE, since this implicitly sets FILE COLLISION to RENAME. And note tht DELETE is DISABLEd automatically any time you Kermit is in local mode (i.e. it makes a connection). Also note that for purposes of DISABLE and ENABLE, "set host *" connections do not count as local mode even though, strictly speaking, they are. 10.8. DOS Pathnames When referring to foreign MS-DOS, Windows, Atari ST, OS/2, or other file specifications that contain backslash characters in a C-Kermit command, you might have to double each backslash, for example: C-Kermit>get c:\\directory\\foo.txt This is because backslash is used in C-Kermit commands for introducing special character codes, variables, functions, etc. 10.9. Cancellation If attempting to cancel local-mode file reception at a very early stage (i.e. before data packets are exchanged) with X or Z does not work, use E or Ctrl-C instead, or wait until the first data packets are sent. If you cancel a transfer that is underway using X or Z, and a lot of window slots are in use, it might take a while for the cancellation to take effect, especially if you do this on the receiving end; that's because a lot of packets might already be on their way to you. In that case, just be patient and let Kermit "drain" them. If C-Kermit is sending a file, remote-mode packet-mode breakout (three consecutive Ctrl-C's by default) is not effective until after C-Kermit sends its first packet. If C-Kermit is receiving a file or is in server mode, it is effective right away. In the former case, the SET DELAY value determines the earliest time at which you can break out of packet mode. 10.10. Partner Peculiarities When one or both partners is on an SCO operating system such as OSR5, you might issue the command: mapchan -n to disable character-set conversion by the terminal driver. Similarly for AIX: setmaps -t NOMAP When using C-Kermit to transfer files with the HP48SX calculator, you must SET FLOW NONE. The HP48SX does not support flow control, and evidently also becomes confused if you attempt to use it. You might also need to use SET SEND PAUSE 100 (or other number). For greater detail about transferring files the HP-48, see: [85]http://www.columbia.edu/kermit/hp48.html Some communication programs have errors in their implementation of Kermit attribute packets. If you get an error message from your communication program like "Attribute error", tell C-Kermit to SET ATTRIBUTES OFF. Better yet, switch to a real Kermit program. Some communication software claims to implement Kermit sliding windows, but does so incorrectly. If sliding window transfers fail, set C-Kermit's window size to the smallest one that works, for example, SET WINDOW 1. For lots more detail about how to cope with defective Kermit partners, see: * [86]Coping with Faulty Kermit Implementations (C-Kermit 7.0 and later). * [87]Coping with Broken Kermit Partners (C-Kermit 8.0 and later). The UNIX version of C-Kermit discards carriage returns when receiving files in text mode. Thus, "bare" carriage returns (sometimes used to achieve overstriking) are lost. 11. SCRIPT PROGRAMMING [ [88]Top ] [ [89]Contents ] [ [90]Previous ] 11.1. Comments Versus the SCRIPT Command Remember that ";" and "#" introduce comments when (a) they are the first character on the line, or (b) they are preceded by at least one blank or tab within a line. Thus constructions like: INPUT 5 ; SCRIPT ~0 #--#--# must be coded using backslash notation to keep the data from being ignored: INPUT 5 \59 ; 59 is the decimal ASCII code for ";" SCRIPT ~0 \35--#--# ; 43 is the decimal ASCII code for "#" or, more simply: INPUT 5 \; ; Just quote the semicolon SCRIPT ~0 \#--#--# ; Just quote the "#" 11.2. Alphabetic Case and the INPUT Command INPUT and MINPUT caseless string comparisons do not work for non-ASCII (international) characters. Workaround: SET INPUT CASE OBSERVE. Even then, the "lexically less than" and "lexically greater than" operations (IF LLT, IF LGT) probably won't work as expected. The same is true for the case-conversion functions \Flower() and \Fupper(). C-Kermit does not know the collating sequence for different character sets and languages. (On the other hand, it might work depending on such items as how Kermit was linked, whether your operating supports "locales", etc) 11.3. NUL (0) Characters in C-Kermit Commands You can't include a NUL character (\0) in C-Kermit command text without terminating the character string in which it appears. For example: echo In these brackets [\0] is a NUL will echo "In these brackets [". This applies to ECHO, INPUT, OUTPUT, and all other commands (but you can represent NUL by "\N" in an OUTPUT string). This is because C-language strings are terminated internally by the NUL character, and it allows all of C-Kermit's string comparison and manipulation functions to work in the normal "C" way. To illustrate: INPUT 5 \0 is equivalent to: INPUT 5 and: INPUT 5 ABC\0DEF is equivalent to: INPUT 5 ABC INPUT operations discard and ignore NUL characters that arrive from the communication device, meaning that they do not figure into matching operations (e.g. AB matches AB); they are not deposited in the INPUT buffer (\v(input)); and they are not counted in \v(incount), with two exceptions: 1. An arriving NUL character restarts the INPUT SILENCE timer. 2. An arriving NUL character terminates the INPUT command with the SUCCESS condition if the INPUT command was given an empty search string. In this case \v(incount) is set to 1. Also, the \v(inchar) variable is null (completely empty) if the last INPUT character was NUL. That is, there is no way to tell only by looking at \v(inchar) the difference between a NUL that was INPUT and no INPUT at all. If the INPUT command succeeded but \v(inchar) is empty, then a NUL character was input. Also, \v(incount) will be set to 1. Here's a sample script fragment to read characters, possibly including NUL, from the communication connection and write them to a file: while true { input 1 ; read one byte if fail break ; timed out or connection closed fwrite /char \%c \v(inchar) ; record the byte } This works because when \v(inchar) is NUL, that's equivalent to FWRITE /CHAR having no text argument at all, in which case it writes a NUL character. \v(incount) and \v(inchar) are NOT affected by the CLEAR command. 11.4. \ffiles() and \fnextfile() Peculiarities The following script program: for \%i 1 \ffiles(oofa.*) 1 { send \fnextfile() } did not work as expected in C-Kermit 6.0 and earlier but does work in C-Kermit 7.0 and later. 11.5. Commands That Have Only Local Effect Certain settings are local to each command level, meaning that subordinate command levels (macros or command files) can change them without affecting their values at higher command levels. When a new command level is invoked, the value is inherited from the previous level. These settings are: CASE COUNT and \v(count) INPUT CASE INPUT TIMEOUT MACRO ERROR QUIET TAKE ERROR This arrangement allows CASE, TIMEOUT, and ERROR settings, which are used to control automatic exit from a command file or macro upon error, to be automatically restored when the command file or macro exits. The COUNT variable follows this rule too, which permits nested SET COUNT / IF COUNT loops, as in this example in which the inner loop counts down from the current COUNT value of the outer loop (try it): DEFINE INNER WHILE COUNT { WRITE SCREEN { Inner:}, SHOW COUNT } SET COUNT 5 WHILE COUNT { WRITE SCREEN Outer:, SHOW COUNT, DO INNER } Keep in mind that an inferior command level cannot manipulate the COUNT value held by a higher level. For example: DEFINE OOFA SHOW COUNT, IF COUNT GOTO LOOP SET COUNT 5 :LOOP OOFA ECHO Done results in an infinite loop; the COUNT value remains at 5 because it is never decremented at the same level at which it was set. 11.6. Literal Braces in Function Calls Since braces are used in function calls to indicate grouping, there is no way to pass literal braces to the function itself. Solution: Define a variable containing the string that has braces. Example: define \%a ab{cd echo \fsubstring(\%a) ab{cd If the string is to start with a leading brace and end with a closing brace, then double braces must appear around the string (which itself is enclosed in braces): define \%a {{{foo}}} echo \fsubstring(\%a) {foo} This also works for any other kind of string: define \%a {{ab{cd}} echo \fsubstring(\%a) ab{cd 11.7. Defining Variables on the C-Kermit Command Line To define variables on the C-Kermit command line, use the -C command-line option with one or more DEFINE or ASSIGN commands. Note that the C-Kermit command line must cope with the quoting rules of your shell. Examples: kermit -C "define \\%a foo, define phonenumber 7654321" In this case we follow UNIX quoting rules by doubling the backslash. Once C-Kermit starts, the \%a and \m(phonenumber) variables are defined as indicated and can be used in the normal way. In DOS or Windows or OS/2 the command would be: kermit -C "define \%%a foo, define phonenumber 7654321" Here we need to double the percent sign rather than the backslash because of DOS shell quoting rules. 11.8. Per-Character Echo Check with the OUTPUT Command Sometimes the OUTPUT command must be used to send commands or data to a device in "echoplex" mode, meaning that characters must be sent one at a time, and the next character can not be sent until the echo from the previous one has been received. For example, a certain PBX might have this characteristic. Let's say a Kermit script is used to program the PBX. If characters are sent too fast, they can be lost. It would seem that the command: SET OUTPUT PACING milliseconds could be used to take care of this, but the pacing interval is constant and must be set large enough to allow even the slowest echo to finish. If the script is large (an actual example is 14,000 lines long), this can cause it to take hours longer than it needs to. Here is a macro you can use to OUTPUT a string in an Echoplex environment: define XOUTPUT { local \%c \%i set output pacing 0 for \%i 1 \flen(\%*) 1 { asg \%c \fsubstr(\%*,\%i,1) output \%c input 2 \%c } } C-Kermit 7.0 or later is required. It sends one character at a time and then waits up to 2 seconds for the character to be echoed back, but continues to the next character as soon as the echo appears, so no time is wasted. You can add an IF FAIL clause after the INPUT in case you want to do something special about failure to detect an echo within the timeout period. Obviously you can also change the 2-second limit, and adjust the script in any other desired way. 11.9. Scripted File Transfer Sometimes a user complains that when she makes a connection by hand, logs in, and transfers a file, there are no problems, but when she scripts the the exact same sequence, the file transfer always fails after a few packets. Here's a scenario where this can happen: 1. Upon logging in to the remote computer, it sends a "What Are You?" escape sequence. 2. When you log in interactively, your terminal emulator sends the response. This is invisible to you; you don't know it's happening. 3. When you script the login, and begin a file transfer immediately upon logging in, the host still sends the "What Are You?" sequence. Kermit's INPUT ECHO setting is ON by default, so the escape sequence passes through to the terminal, and the terminal sends its response. But by this time Kermit has already started the file transfer. 4. By default, the local Kermit program examines the keyboard for interruption characters between every packet. The "What Are You" response is sitting in the keyboard buffer. Eventually Kermit will read a character such as "c" that is a valid interruption character, and the file transfer stops with "User canceled". The right way to handle this situation is to have your look for the "What Are You?" sequence and send the response itself, as described in Using C-Kermit, pp.429-431. Or you can work around it by telling the local Kermit to "set input echo off" and/or "set transfer interruption off". 11.10. Hexadecimal arithmetic... C-Kermit can do both integer and floating-point arithmetic, in both ordinary algebraic notation and in Lisp S-Expression notation. All arithmetic operators and functions operate only on decimal numbers. It is possible, however, to write scripts that operate on hexadecimal numbers. This is done by converting them to decimal prior to any arithmetic operations, and then converting them back to hexadecimal for display. Example: ; EVALUATE is a command that evaluates an arithmetic expression. ; See HELP EVALUATE for details. This is just for demonstration. ; Arithmetic expressions can be used in any context where a number ; can be used. Also, the special notation: ; ; .\%a ::= expression ; ; evaluations the expression and assigns the result to the variable. ; .\%a := fffe ; Set variable to hex value set eval old ; See HELP EVAL eval \fhex2n(\%a) ; Show value of variable eval \fhex2n(\%a) + 1 ; Show value of expression eval \fhex2n(\%a) + 2 ; Show value of expression .\%x ::= \fhex2n(\%a) + 1 ; Assign value of expression to variable echo \fn2hex(\%x) ; Display variable's value in hex .\%x ::= \fhex2n(\%a) + 2 : Ditto echo \fn2hex(\%x) .\%x ::= \fhex2n(\%a) | \fhex2n(ffff) ; Similarly for logical OR echo \fn2hex(\%x) .\%x ::= \fhex2n(\%a) & \fhex2n(ffff) ; and logical AND echo \fn2hex(\%x) By the way, you might be tempted to use Kermit's \xnn notation to plug hex numbers into arithmetic expressions but this doesn't work. That notation is strictly for bytes (hex representation of character values), not for numbers. 11.11. Other... Escape sequences (or any strings that contain control characters) can't be used as labels, GOTO targets, or SWITCH cases. [ [91]Top ] [ [92]Contents ] [ [93]C-Kermit Home ] [ [94]C-Kermit 8.0 Overview ] [ [95]Kermit Home ] __________________________________________________________________ C-Kermit 8.0 Unix Hints and Tips / [96]The Kermit Project / [97]kermit@columbia.edu / 30 June 2011 References 1. http://www.columbia.edu/ 2. mailto:kermit@columbia.edu 3. http://www.columbia.edu/kermit/index.html 4. http://www.columbia.edu/kermit/k95.html 5. http://www.columbia.edu/kermit/ckermit.html 6. http://www.columbia.edu/kermit/ckscripts.html 7. http://www.columbia.edu/kermit/current.html 8. http://www.columbia.edu/kermit/whatsnew.html 9. http://www.columbia.edu/kermit/faq.html 10. http://www.columbia.edu/kermit/support.html 11. http://www.columbia.edu/kermit/ckcbwr.html 12. http://www.columbia.edu/kermit/ckubwr.html 13. http://www.columbia.edu/kermit/k95.html 14. http://www.columbia.edu/kermit/ckermit.html 15. http://www.columbia.edu/kermit/ckututor.html 16. http://www.columbia.edu/kermit/ckcbwr.html#x0 17. http://www.columbia.edu/kermit/ckcbwr.html#x1 18. http://www.columbia.edu/kermit/ckcbwr.html#x2 19. http://www.columbia.edu/kermit/ckcbwr.html#x3 20. http://www.columbia.edu/kermit/ckcbwr.html#x4 21. http://www.columbia.edu/kermit/ckcbwr.html#x5 22. http://www.columbia.edu/kermit/ckcbwr.html#x6 23. http://www.columbia.edu/kermit/ckcbwr.html#x7 24. http://www.columbia.edu/kermit/ckcbwr.html#x8 25. http://www.columbia.edu/kermit/ckcbwr.html#x9 26. http://www.columbia.edu/kermit/ckcbwr.html#x10 27. http://www.columbia.edu/kermit/ckcbwr.html#x11 28. http://www.columbia.edu/kermit/ckcbwr.html#top 29. http://www.columbia.edu/kermit/ckcbwr.html#contents 30. http://www.columbia.edu/kermit/ckcbwr.html#x2 31. http://www.columbia.edu/kermit/ckcbwr.html#top 32. http://www.columbia.edu/kermit/ckcbwr.html#contents 33. http://www.columbia.edu/kermit/ckcbwr.html#x2 34. http://www.columbia.edu/kermit/ck60manual.html 35. http://www.columbia.edu/kermit/ckermit2.html 36. http://www.columbia.edu/kermit/ck60manual.html 37. http://www.columbia.edu/kermit/ckermit80.html#x5 38. http://www.columbia.edu/kermit/ckermit80.html 39. http://www.columbia.edu/kermit/ckermit80.html#x2.2 40. http://www.columbia.edu/kermit/ckermit80.html#x8.7.2 41. http://www.columbia.edu/kermit/ckermit80.html#x9 42. http://www.columbia.edu/kermit/ckcbwr.html#top 43. http://www.columbia.edu/kermit/ckcbwr.html#contents 44. http://www.columbia.edu/kermit/ckcbwr.html#x3 45. http://www.columbia.edu/kermit/ckcbwr.html#x1 46. http://www.columbia.edu/kermit/ckcbwr.html#top 47. http://www.columbia.edu/kermit/ckcbwr.html#contents 48. http://www.columbia.edu/kermit/ckcbwr.html#x4 49. http://www.columbia.edu/kermit/ckcbwr.html#x2 50. http://www.columbia.edu/kermit/ckcbwr.html#top 51. http://www.columbia.edu/kermit/ckcbwr.html#contents 52. http://www.columbia.edu/kermit/ckcbwr.html#x5 53. http://www.columbia.edu/kermit/ckcbwr.html#x3 54. ftp://ftp.isi.edu/in-notes/rfc1122.txt 55. http://www.columbia.edu/kermit/ckcbwr.html#top 56. http://www.columbia.edu/kermit/ckcbwr.html#contents 57. http://www.columbia.edu/kermit/ckcbwr.html#x6 58. http://www.columbia.edu/kermit/ckcbwr.html#x4 59. http://www.columbia.edu/kermit/ckcbwr.html#top 60. http://www.columbia.edu/kermit/ckcbwr.html#contents 61. http://www.columbia.edu/kermit/ckcbwr.html#x7 62. http://www.columbia.edu/kermit/ckcbwr.html#x5 63. http://www.columbia.edu/kermit/ck60manual.html 64. http://www.columbia.edu/kermit/ckcbwr.html#x10 65. http://www.columbia.edu/kermit/ckcbwr.html#top 66. http://www.columbia.edu/kermit/ckcbwr.html#contents 67. http://www.columbia.edu/kermit/ckcbwr.html#x8 68. http://www.columbia.edu/kermit/ckcbwr.html#x6 69. ftp://ftp.isi.edu/in-notes/rfc2217.txt 70. http://www.columbia.edu/kermit/ckcbwr.html#top 71. http://www.columbia.edu/kermit/ckcbwr.html#contents 72. http://www.columbia.edu/kermit/ckcbwr.html#x9 73. http://www.columbia.edu/kermit/ckcbwr.html#x7 74. http://www.columbia.edu/kermit/ckcbwr.html#top 75. http://www.columbia.edu/kermit/ckcbwr.html#contents 76. http://www.columbia.edu/kermit/ckcbwr.html#x10 77. http://www.columbia.edu/kermit/ckcbwr.html#x8 78. http://www.columbia.edu/kermit/ckubwr.html 79. http://www.columbia.edu/kermit/ckcbwr.html#top 80. http://www.columbia.edu/kermit/ckcbwr.html#contents 81. http://www.columbia.edu/kermit/ckcbwr.html#x11 82. http://www.columbia.edu/kermit/ckcbwr.html#x9 83. http://www.columbia.edu/kermit/ck60manual.html 84. http://www.columbia.edu/kermit/ckermi70.htm 85. http://www.columbia.edu/kermit/hp48.html 86. http://www.columbia.edu/kermit/ckermit70.html#x4.22 87. http://www.columbia.edu/kermit/ckermit80.html#x15 88. http://www.columbia.edu/kermit/ckcbwr.html#top 89. http://www.columbia.edu/kermit/ckcbwr.html#contents 90. http://www.columbia.edu/kermit/ckcbwr.html#x10 91. http://www.columbia.edu/kermit/ckcbwr.html#top 92. http://www.columbia.edu/kermit/ckcbwr.html#contents 93. http://www.columbia.edu/kermit/ckermit.html 94. http://www.columbia.edu/kermit/ck80.html 95. http://www.columbia.edu/kermit/index.html 96. http://www.columbia.edu/kermit/index.html 97. mailto:kermit@columbia.edu ckuins.txt0000664000015300001460000052567511624230414011741 0ustar fdckermit [1]The Columbia Crown The Kermit Project | Columbia University 612 West 115th Street, New York NY 10025 USA o [2]kermit@columbia.edu ...since 1981 [3]Home [4]Kermit 95 [5]C-Kermit [6]Scripts [7]Current [8]New [9]FAQ [10]Support C-Kermit 9.0 Installation Instructions and Options for Unix [ [11]Contents ] [ [12]C-Kermit ] [ [13]Kermit Home ] Frank da Cruz The Kermit Project Columbia University As of C-Kermit version: 9.0.302, 20 August 2011 This file last updated: Sun Aug 21 12:08:29 2011 (New York City time) IF YOU ARE READING A PLAIN-TEXT version of this document, it is a plain-text copy of a Web page. You can visit the original (and possibly more up-to-date) Web page here: [14]http://www.columbia.edu/kermit/ckuins.html CONTENTS [15]OVERVIEW 1. [16]INTERNET QUICK START 2. [17]INSTALLING FROM PACKAGES 3. [18]INSTALLING PREBUILT BINARIES 4. [19]BUILDING FROM SOURCE CODE 5. [20]INSTALLING THE KERMIT FILES 6. [21]INSTALLING UNIX C-KERMIT FROM DOS-FORMAT DISKETTES 7. [22]CHECKING THE RESULTS 8. [23]REDUCING THE SIZE OF THE EXECUTABLE PROGRAM IMAGE 9. [24]UNIX VERSIONS 10. [25]DIALING OUT AND COORDINATING WITH UUCP 11. [26]RUNNING UNIX C-KERMIT SETUID OR SETGID 12. [27]CONFIGURING UNIX WORKSTATIONS 13. [28]BIZARRE BEHAVIOR AT RUNTIME 14. [29]CRASHES AND CORE DUMPS 15. [30]SYSLOGGING 16. [31]BUILDING SECURE VERSIONS OF C-KERMIT 9.0 17. [32]INSTALLING C-KERMIT AS AN SSH SERVER SUBSYSTEM OVERVIEW [ [33]Top ] [ [34]Contents ] [ [35]Next ] WARNING: This document contains notes that have been accumulating since the mid 1980s. Many of the products and Unix versions mentioned here have not been heard of in a long while, but that does not necessarily mean they are not still running in some obscure nook. This file contains Unix-specific information. A lot of it. Unlike most other packages, C-Kermit tries very hard to be portable to every Unix variety (and every release of each one) known to exist, including many that are quite old, as well as to other platforms like VMS, AOS/VS, VOS, OS-9, the BeBox, the Amiga, etc. Since C-Kermit gets so deeply into the file system, i/o system, and other areas that differ radically from one Unix platform to the next, this means that a lot can go wrong when you try to install C-Kermit on (for example) a new release of a particular variety of Unix, in which certain things might have changed that C-Kermit depended upon. This file concentrates on installation. For a description of general configuration options for C-Kermit, please read the [36]Configurations Options document. For troubleshooting after installation, see the [37]General Hints and Tips and [38]Unix-Specific Hints and Tips documents. The latter, in particular, contains lots of information on lots of specific Unix platforms. If you want to work on the source code, see the [39]C-Kermit Program Logic Manual You may install C-Kermit: * From an "[40]install package", if one is available. * As a [41]prebuilt binary, if available, plus accompanying text files. * By building from [42]source code. 1. INTERNET QUICK START [ [43]Top ] [ [44]Contents ] [ [45]Next ] [ [46]Previous ] If your Unix computer is on the Internet and it has a C compiler, here's how to download, build, and install C-Kermit directly from the "tarballs" or Zip archives: 1. Make a fresh directory and cd to it. 2. Download the C-Kermit source code: [47]ftp://www.columbia.edu/kermit/archives/cku211.tar.Z (compress format) or [48]ftp://www.columbia.edu/kermit/archives/cku211.tar.gz (gunzip format). If those links don't work, FTP transfers are being blocked; try these HTTP links instead: [49]http://kermit.columbia.edu/ftp/archives/cku211.tar.Z (compress format) or [50]http://kermit.columbia.edu/ftp/archives/cku211.tar.gz (gunzip format). 3. Uncompress the compressed tar file with "uncompress" or "gunzip", according to which type of compressed file you downloaded. (If you don't understand this, you could download a (much larger) uncompressed tar archive directly: [51]ftp://www.columbia.edu/kermit/archives/cku211.tar or [52]http://kermit.columbia.edu/ftp/archives/cku211.tar 4. Now type "tar xvf cku211.tar" to unpack the individual files from the tar archive. 5. Type "rm cku211.tar" to get rid of the tar archive, which is no longer needed. 6. Read the comments at the top of the makefile to find out which target to use and then type the appropriate "make" command, such as "make linux", "make solaris8", etc. 7. This produces a binary in your current directory called "wermit". Start it by typing "./wermit" and [53]try it out to make sure it works. Then read [54]Section 5 for how to install it, or simply copy the wermit binary to the desired public directory, rename it to kermit, and give it the needed permissions (and, if it is going to be used to dial out, give it the same group and owner and permissions as the cu, tip, or minicom program). For secure installations, see [55]Sections 5 and [56]16. 2. INSTALLING FROM PACKAGES [ [57]Top ] [ [58]Contents ] [ [59]Next ] [ [60]Previous ] Various Unix varieties -- Linux, Solaris, AIX, etc -- now incorporate the idea of "install packages", and many users expect to find all new applications in this format. A selection of install packages might be available for any given release of C-Kermit, but there is a tradeoff between convenience and safety. Unix presents several notable problems to the builder of install packages: a. Since C-Kermit is portable to many non-Unix platforms (VMS, VOS, AOS/VS, etc), some of the files in the C-Kermit distribution do not fit into the Unix application model. In particular, C-Kermit includes some plain text files (described in [61]Section 5) and Unix has no standard place to put such files. Typical Unix package managers do not allow for them. Where should they go, and how will the user know where to find them? b. Installation of any program that will be used to make modem calls requires some important decisions from the installer regarding security and privilege. Item (b) is discussed at length in [62]Sections 10 and [63]11 of this document, but the package-related aspects are also given here. The basic problem is that Unix dialout devices and the UUCP "lock files" that regulate contention for them (described in [64]Section 10) are usually protected against "world". Therefore, the install procedure must either run as root in order to give the Kermit binary the required permissions, group, and/or owner, or else the dialout devices and associated directories must be open for group or world reading and writing. Otherwise, the Kermit program just installed WILL NOT WORK for dialing out. Thus, a well-crafted installation procedure should present the options and allow the installer to choose the method, if any, for regulating access to the dialout devices: a. Check the permissions of the lockfile directory and the dialout devices. If they do not allow group or world R/W access, then: b. "Your UUCP lockfile directory and/or dialout devices require privilege to access. You must either change their permissions or install Kermit with privileges." c. "If you wish to install Kermit with privileges, it will be given the same owner, group, and permissions as the cu program so it can use the dialout devices." (This is increasingly problematic as some newer Unix systems like Mac OS X don't have a cu program, or even a serial port!) d. If they choose (c) but the user is not root, give a message that the install procedure can be run only by root and then quit. It should go without saying, of course, that any binaries that are to be included in an install package should be built fresh on the exact platform (e.g. Red Hat 8.0 on Intel) for which the package is targeted; prebuilt binaries ([65]next section) from other sites are likely to have library mismatches. [66]CLICK HERE for more about building C-Kermit install packages. The Kermit Project does not have the resources or the expertise to make install packages for every platform. Most install packages, therefore, are contributed by others, and they do not necessarily follow the guidelines given above. Pay attention to what they do. If you are an end user who has obtained a C-Kermit install package for a particular platform, you should be aware that some additional steps might needed if you want to use Kermit to dial out. Read [67]Section 10 for details. 3. INSTALLING PREBUILT BINARIES [ [68]Top ] [ [69]Contents ] [ [70]Next ] [ [71]Previous ] Hundreds of prebuilt C-Kermit binaries are available on the CDROM in the BINARY tree [NOTE: The C-Kermit CDROM is still for version 7.0], and at our ftp site in the [72]kermit/bin area (with names starting with "ck"), also accessible on the [73]C-Kermit website. To install a prebuilt binary: a. Rename the binary to "wermit". b. Make sure it works; some tests are suggested in [74]Section 7. c. Follow steps (b) through (e) in [75]Section 4. d. Install related files as described in [76]Section 5. But first... Please heed the following cautions: a. If you pick the wrong binary, it won't work (or worse). b. Even when you pick the appropriate binary, it still might not work due to shared-library mismatches, etc. (see [77]Section 4.0). c. Don't expect a binary built on or for version n of your OS to work on version n - x (where x > 0). However, it is supposed to be safe to run a binary built on (or for) an older OS release on a newer one (but is [78]increasingly less so as time-honored principles of stability and backwards compatibility go fading into obscurity). Therefore, it is always better to build your own binary from source code ([79]next section) if you can. But since it is increasingly common for Unix systems (not to mention VMS and other OS's) to be delivered without C compilers, it is sometimes not possible. In such cases, try the most appropriate prebuilt binary or binaries, and if none of them work, [80]contact us and we'll see what we can do to help. 4. BUILDING FROM SOURCE CODE [ [81]Top ] [ [82]Contents ] [ [83]Next ] [ [84]Previous ] Also see: [85]Section 8 and [86]Section 9. C-Kermit is designed to be built and used on as many platforms as possible: Unix and non-Unix, old and new (and ancient), ANSI C and K&R. The Unix version does not use or depend on any external tools for building except the "make" utility, the C compiler, the linker, and the shell. It does not use any external automated configuration tools such as configure, autoconf, automake, libtool, etc. Everything in C-Kermit has been built by hand based on direct experience or reports or contributions from users of each platform. The [87]C-Kermit makefile contains the rules for building the program for each of the hundreds of different kinds of Unix systems that C-Kermit attempts to support. It covers all Unix variations since about 1980 -- pretty much everything after Unix V6. Separate makefiles are used for [88]Plan 9 and [89]2.x BSD. Prerequisites: * The C compiler, linker, and make program must be installed. * The C libraries and header files must be installed (*). * The C-Kermit source code and makefile in your current directory. * The C-Kermit text files ([90]Section 5) in your current directory. * This is becoming problematic in this new age of "selective installs" e.g. of Linux packages. C-Kermit builds will often fail because replying "no" to some obscure Linux installation option will result in missing libraries or header files. Ditto on platforms like AIX and Solaris that don't come with C compilers, and then later have gcc installed, but are still missing crucial libraries, like libm (math). Plus: * For TCP/IP networking support, the sockets library and related header files must be installed. * The math library for floating-point arithmetic support (can be deselected by adding -DNOFLOAT to CFLAGS and removing -lm from LIBS). * Many and varied security libraries for building a secure version (Kerberos, SSL/TLS, SRP, Zlib,...) These are required only if you select a secure target. * For the curses-based fullscreen file-transfer display, the curses or ncurses header file(s) and library, and probably also the termcap and/or termlib library. Note that the names and locations of these files and libraries are likely to change capriciously with every new release of your Unix product. If you discover that the C-Kermit build procedure fails because your curses and/or termxxx headers or libraries are not named or located as expected, please [91]let us know. In the meantime, work around by installing symlinks. * IMPORTANT: Modern Linux distributions might give you the choice during installation of whether to install the "ncurses development package" (perhaps called "ncurses-devel"). If you did not install it, you won't be able to build C-Kermit with curses support included. In this case, either go back and install ncurses, or else choose (or create) a non-curses makefile target for your platform. To install the ncurses developers tools in Red Hat Linux, do "apt-get install ncurses-developer" or if you have the CD: mount redhat cdrom goto RedHat/RPMS rpm -ivh ncurses-devel*.rpm or to have the exact name ls ncurse* and load as rpm -ivh filename then leave the cdrom and unmount it. * In AIX you might have to go back and install any or all of: bos.adt.base bos.adt.include bos.adt.lib bos.adt.libm bos.adt.utils from the first installation CD. Depending on where you got it, the makefile might need to be renamed from ckuker.mak to makefile. Directions: a. Type "make xxx" where xxx is the name of the makefile target most appropriate to your platform, e.g. "make linux", "make aix43", etc. Read the [92]comments at the top of the makefile for a complete list of available targets (it's a long list). b. Test the resulting 'wermit' file (see [93]Section 7 for suggestions). If it's OK, proceed; otherwise [94]notify us. NOTE: steps (c) through (e) can be accomplished using the [95]makefile 'install' target as described in [96]Section 5.4. c. Rename the 'wermit' file to 'kermit', copy it to the desired binary directory (such as /usr/local/bin or /opt/something), and if it is to be used for dialing out, give it the same owner, group, and permissions as the 'cu' program (IMPORTANT: read [97]Sections 10 and [98]11 for details). d. Install the man page, ckuker.nr, with your other man pages. e. Install the accompanying text files (see [99]Section 5). f. If you want C-Kermit to also offer a Telnet command-line personality, make a symbolic link as follows: cd directory-where-kermit-binary-is ln -s kermit telnet If you want C-Kermit to be the default Telnet client, make sure the directory in which you created the symlink is in the PATH ahead of the where the regular Telnet client is. g. If you want C-Kermit to also offer an FTP command-line personality, make a symlink called "ftp" as in (f). h. If you want C-Kermit to also offer an FTTP command-line personality, make a symlink called "http" as in (f). i. If you want to offer an Internet Kermit Service, follow the directions in the [100]IKSD Administrator's Guide. 4.0. Special Considerations for C-Kermit 8.0-9.0 [ [101]Top ] [ [102]Contents ] [ [103]Next ] Also see: [104]C-Kermit Configuration Options SECTION CONTENTS 4.1. [105]The Unix Makefile 4.2. [106]The C-Kermit Initialization File 4.3. [107]The 2.x BSD Makefile 4.4. [108]The Plan 9 Makefile 4.5. [109]Makefile Failures (Also see the [110]Configurations Options document, [111]Section 8). Lots of new features have been added in versions 7.0 and 8.0 that require access to new symbols, APIs, libraries, etc, and this will no doubt cause problems in compiling, linking, or execution on platforms where 6.0 and earlier built without incident. This section contains what we know as of the date of this file. The first category concerns the new Kermit Service Daemon (IKSD; see the [112]IKSD Administrator's Guide for details): The wtmp File When C-Kermit is started as an IKSD (under inetd), it makes syslog and wtmp entries, and also keeps its own ftpd-like log. The code assumes the wtmp log is /var/log/wtmp on Linux and /usr/adm/wtmp elsewhere. No doubt this assumption will need adjustment. Use -DWTMPFILE=path to override at compile time (there is also a runtime override). See [113]iksd.html for details. UTMP, utsname(), etc C-Kermit 7.0 gets as much info as it can about its job -- mainly for IKSD logging -- from utmp. But of course utmp formats and fields differ, and for that matter, there can be two different header files, and . Look for HAVEUTMPX and HAVEUTHOST in [114]ckufio.c and let me know of any needed adjustments. Password lookup IKSD needs to authenticate incoming users against the password list. In some cases, this requires the addition of -lcrypt (e.g. in Unixware 2.x). In most others, the crypt functions are in the regular C library. If you get "crypt" as an unresolved symbol at link time, add -lcrypt to LIBS. If your site has local replacement libraries for authentication, you might need a special LIBS clause such as "LIBS=-L/usr/local/lib -lpwent". These days most Unix systems take advantage of shadow password files or Pluggable Authentication Modules (PAM). If your system uses shadow passwords you must add -DCK_SHADOW to the CFLAGS list. If your system requires PAM you must add -DCK_PAM to the CFLAGS and -lpam -ldl to LIBS. getusershell() This is called by the IKSD at login time to see if a user has been "turned off". But many Unix platforms lack this function. In that case, you will get unresolved symbol reports at link time for _getusershell, _endusershell; to work around, add -DNOGETUSERSHELL. initgroups() This is called by IKSD after successful authentication. But some platforms do not have this function, so obviously it can't be called there, in which case add -DNOINITGROUPS. setreuid(), setreuid(), setregid() not found or "deprecated" Find out what your Unix variety wants you to use instead, and make appropriate substitutions in routine zvpass(), module [115]ckufio.c, and [116]let us know. printf() IKSD installs a printf() substitute to allow redirection of printf-like output to the connection. However, this can conflict with some curses libraries. In this case, separate binaries must be built for IKSD and non-IKSD use. If you encounter difficulties with any of the above, and you are not interested in running C-Kermit as an IKSD, then simply add NOIKSD to CFLAGS and rebuild. Example: make sco286 (get lots of errors) make clean make sco286 "KFLAGS=-DNOIKSD" Some non-IKSD things to watch out for: Return type of main() The main() routine is in [117]ckcmai.c. If you get complaints about "main: return type is not blah", define MAINTYPE on the CC command line, e.g.: make xxx "KFLAGS=-DMAINTYPE=blah (where blah is int, long, or whatever). If the complaint is "Attempt to return a value from a function of type void" then add -DMAINISVOID: make xxx "KFLAGS=-DMAINISVOID=blah DNS Service Records This feature allows a remote host to redirect C-Kermit to the appropriate socket for the requested service; e.g. if C-Kermit requests service "telnet" and the host offers Telnet service on port 999 rather than the customary port 23. If you get compile-time complaints about not being able to find , , or , add -DNO_DNS_SRV to CFLAGS. If you get link-time complaints about unresolved symbols res_search or dn_expand, try adding -lresolve to LIBS. \v(ipaddress) If "echo \v(ipaddress)" shows an empty string rather than your local IP address, add -DCKGHNLHOST to CFLAGS and rebuild. If this file can't be found at compile time, add -DNOREDIRECT to CFLAGS. This disables the REDIRECT and PIPE commands and anything else that needs the wait() system service. syslog() C-Kermit can now write syslog records. Some older platforms might not have the syslog facility. In that case, add -DNOSYSLOG. Others might have it, but require addition of -lsocket to LIBS (SCO OSR5 is an example). See [118]Section 15. putenv() If "_putenv" comes up as an undefined symbol, add -DNOPUTENV to CFLAGS and rebuild. "Passing arg1 of 'time' from incompatible pointer" This is a mess. See the mass of #ifdefs in the appropriate module, [119]ckutio.c or [120]ckufio.c. gettimeofday() Wrong number of arguments. On most platforms, gettimeofday() takes two arguments, but on a handful of others (e.g. Motorola System V/88 V4, SNI Reliant UNIX 5.43, etc) it takes one. If your version of gettimeofday() is being called with two args but wants one, add -DGTODONEARG. "Assignment makes pointer from integer without a cast" This warning might appear in [121]ckutio.c or [122]ckufio.c. (or elsewhere), and usually can be traced to the use of a system or library function that returns a pointer but that is not declared in the system header files even though it should be. Several functions are commonly associated with this error: + getcwd(): Add -DDCLGETCWD to CFLAGS and rebuild. + popen() : Add -DDCLPOPEN to CFLAGS and rebuild. + fdopen(): Add -DDCLFDOPEN to CFLAGS and rebuild. "Operands of = have incompatible types" "Incompatible types in assignment" If this comes from [123]ckcnet.c and comes from a statement involving inet_addr(), try adding -DINADDRX to CFLAGS. If that doesn't help, then try adding -DNOMHHOST. Complaints about args to get/setsockopt(), getpeername(), getsockname() These are all in [124]ckcnet.c. Different platforms and OS's and versions of the same OS change this all the time: int, size_t, unsigned long, etc. All the affected variables are declared according to #ifdefs within ckcnet.c, so find the declarations and adjust the #ifdefs accordingly. size_t In case of complaints about "unknown type size_t", add -DSIZE_T=int (or other appropriate type) to CFLAGS. 'tz' undefined Use of undefined enum/struct/union 'timezone' Left of 'tv_sec' specifies undefined struct/union 'timeval' And similar complaints in [125]ckutio.c: Add -DNOGFTIMER and/or -DNOTIMEVAL. Symlinks The new built-in DIRECTORY command should show symlinks like "ls -l" does. If it does not, check to see if your platform has the lstat() and readlink() functions. If so, add -DUSE_LSTAT and -DCKSYMLINK to CFLAGS and rebuild. On the other hand, if lstat() is unresolved at link time, add -DNOLSTAT to CFLAGS. If readlink() is also unresolved, add -DNOSYMLINK. realpath() Link-time complains about realpath() -- find the library in which it resides and add it to LIBS (example for Unixware 7.1: "-lcudk70") or add -DNOREALPATH to CFLAGS and rebuild. If built with realpath() but debug log file is truncated or mangled, ditto (some realpath() implementations behave differently from others). If built with realpath() and seemingly random core dumps occur during file path resolution, ditto. Failure to locate header file Usually happens on Linux systems that have the C compiler installed, but not the ncurses package (see comments about selective installs above). Go back and install ncurses, or use "make linuxnc" (Linux No Curses). "Can't find shared library libc.so.2.1" "Can't find shared library libncurses.so.3.0", etc... You are trying to run a binary that was built on a computer that has different library versions than your computer, and your computer's loader is picky about library version numbers. Rebuild from source on your computer. Time (struct tm) related difficulties: Errors like the following: "ckutio.c", line 11994: incomplete struct/union/enum tm: _tm "ckutio.c", line 11995: error: cannot dereference non-pointer type "ckutio.c", line 11995: error: assignment type mismatch "ckutio.c", line 11997: warning: using out of scope declaration: localtime "ckutio.c", line 11997: error: unknown operand size: op "=" "ckutio.c", line 11997: error: assignment type mismatch "ckutio.c", line 11998: error: undefined struct/union member: tm_year "ckutio.c", line 12000: error: undefined struct/union member: tm_mon "ckutio.c", line 12001: error: undefined struct/union member: tm_mday "ckutio.c", line 12002: error: undefined struct/union member: tm_hour "ckutio.c", line 12003: error: undefined struct/union member: tm_min "ckutio.c", line 12004: error: undefined struct/union member: tm_sec are due to failure to include the appropriate time.h header files. Unix platforms generally have one or more of the following: , , and . Any combination of these might be required. Defaults are set up for each makefile target. The defaults can be corrected on the CC command line by adding the appropriate definition from the following list to CFLAGS: -DTIMEH Include -DNOTIMEH Don't include -DSYSTIMEH Include -DNOSYSTIMEH Don't include -DSYSTIMEBH Include -DNOSYSTIMEBH Don't include Note that is relatively scarce in the System V and POSIX environments; the only platform of recent vintage where it was/is used is OSF/1 and its derivatives (Digital Unix and Tru64 Unix). Struct timeval and/or timezone not declared: In some cases, merely including the appropriate time.h header files is still not enough. POSIX.1 does not define the timeval struct, and so the items we need from the header are protected against us by #ifndef _POSIX_SOURCE or somesuch. In this case, we have to declare the timeval (and timezone) structs ourselves. To force this, include -DDCLTIMEVAL in CFLAGS. Warnings about dn_expand() Argument #4 WARNING: argument is incompatible with prototyp. It's the old char versus unsigned char stupidity again. Try to find a compiler switch like GCC's "-funsigned-char". Failing that, add -DCKQUERYTYPE=xxx to CFLAGS, where xxx is whatever 'man dn_expand' tells you the type of the 4th argument should be (presumably either char or unsigned char; in the latter case use CHAR to avoid confusion caused by multiple words. Switch Table Overflow (in [126]ckcuni.c) Add -DNOUNICODE to CFLAGS. Compile-time warnings about ck_out() or tgetstr() or tputs(): Easy solution: Add -DNOTERMCAP to CFLAGS. But then you lose the SCREEN function. Real solution: Try all different combinations of the following CFLAGS: -DTPUTSARGTYPE=char -DTPUTSFNTYPE=int -DTPUTSARGTYPE=int -DTPUTSFNTYPE=void Until the warnings go away, except maybe "ck_outc: return with a value in a function returning void", and in that case also add -DTPUTSISVOID. "Passing arg 1 of to tputs() makes pointer from integer without a cast": Add -DTPUTSARG1CONST to CFLAGS. "Undefined symbol: dup2" Add -DNOZEXEC to CFLAGS. "header file 'termcap.h' not found" Add -DNOHTERMCAP to CFLAGS. Other difficulties are generally of the "where is curses.h and what is it called this week?" variety (most easily solved by making symlinks in the include and lib directories), or overzealous complaints regarding type mismatches in function calls because of the totally needless and silly signed versus unsigned char conflict (*), etc. In any case, please send any compilation or linking warnings or errors to the author, preferably along with fixes. * C-Kermit does not use the signed property of chars at all anywhere, ever. So if all chars and char *'s can be made unsigned at compile time, as they can in gcc with "-funsigned-char", they should be. IMPORTANT: If you find any of these hints necessary for a particular make target (or you hit upon others not listed here), PLEASE SEND A REPORT TO: [127]kermit-support@columbia.edu 4.1. The Unix Makefile [ [128]Top ] [ [129]Contents ] [ [130]Section Contents ] [ [131]Next ] [ [132]Previous ] If your distribution does not contain a file with the name "makefile" or "Makefile", then rename the file called ckuker.mak to makefile: mv ckuker.mak makefile Then type "make xxx", where xxx is the platform you want to build C-Kermit for. These are listed in the [133]comments at the top of the makefile. For example, to build C-Kermit for Linux, type: make linux Here are some typical examples: Target Description linux Linux, any version on any hardware platform openbsd OpenBSD, any version on any hardware platform aix43 AIX 4.3 aix43g AIX 4.3, built with gcc solaris9 Solaris 9 solaris9g Solaris 9 built with gcc hpux1100 HP-UX 11-point-anything The makefile is quite long, and at least two versions of Unix, SCO Xenix/286 and 2.x BSD, cannot cope with its length. An attempt to "make sco286" gives the message "Make: Cannot alloc mem for env.. Stop". Solution: edit away some or all of the nonrelevant material from the makefile. (A separate version of the makefile is provided for BSD 2.x: ckubs2.mak but C-Kermit 8.0 can't be built for BSD 2.x -- it has simply grown too large.) Some make programs reportedly cannot handle continued lines (lines ending in backslash (\)). If you have a problem with the makefile, try editing the makefile to join the continued lines (remove the backslashes and the following linefeed). Other makefile troubles may occur because tabs in the makefile have somehow been converted to spaces. Spaces and tabs are distinct in Unix makefiles. Similarly, carriage returns might have been added to the end of each line, which also proves confusing to most Unix versions of make. Check to see if there are comments about your particular version in its makefile target itself. In a text editor such as EMACS or VI, search for the make entry name followed by a colon, e.g. "linux:" (if you really are building C-Kermit for Linux, do this now). Check to see if there are comments about your particular version in the [134]ckubwr.txt file ([135]CLICK HERE for the Web version). If you have trouble with building [136]ckwart.c, or running the resulting wart preprocessor program on [137]ckcpro.w: 1. Just "touch" the [138]ckcpro.c file that comes in the distribution and then give the "make" command again, or: 2. Compile ckwart.c "by hand": cc -o wart ckwart.c, or: 3. Try various other tricks. E.g. one Linux user reported that that adding the "static" switch to the rule for building wart fixed everything: wart: ckwart.$(EXT) $(CC) -static -o wart ckwart.$(EXT) $(LIBS) If your compiler supports a compile-time option to treat ALL chars (and char *'s, etc) as unsigned, by all means use it -- and send me email to let me know what it is (I already know about gcc -funsigned-char). To add compilation options (which are explained later in this document) to your makefile target without editing the makefile, include "KFLAGS=..." on the make command line, for example: make linux KFLAGS=-DNODEBUG make bsd "KFLAGS=-DKANJI -DNODEBUG -DNOTLOG -DDYNAMIC -UTCPSOCKET" Multiple options must be separated by spaces. Quotes are necessary if the KFLAGS= clause includes spaces. The KFLAGS are added to the end of the CFLAGS that are defined in the selected makefile target. For example, the "bsd" entry includes -DBSD4 -DTCPSOCKET, so the second example above compiles Kermit with the following options: -DBSD4 -DTCPSOCKET -DKANJI -DNODEBUG -DNOTLOG -DDYNAMIC -UTCPSOCKET (Notice how "-UTCPSOCKET" is used to negate the effect of the "-DTCPSOCKET" option that is included in the makefile target.) WARNING: Be careful with KFLAGS. If you build C-Kermit, change some files, and then run make again using the same make entry but specifying different KFLAGS than last time, make won't detect it and you could easily wind up with inconsistent object modules, e.g. some of them built with a certain option, others not. When in doubt, "make clean" first to make sure all your object files are consistent. Similarly, if you change CFLAGS, LIBS, or any other items in the makefile, or you rebuild using a different makefile target, "make clean" first. If you create a new makefile target, use static linking if possible. Even though this makes your C-Kermit binary bigger, the resulting binary will be more portable. Dynamically linked binaries tend to run only on the exact configuration and version where they were built; on others, invocation tends to fail with a message like: Can't find shared library "libc.so.2.1" 4.2. The C-Kermit Initialization File [ [139]Top ] [ [140]Contents ] [ [141]Section Contents ] [ [142]Next ] [ [143]Previous ] (This section is obsolete.) Read [144]Section 5 about the initialization file. 4.3. The 2.x BSD Makefile [ [145]Top ] [ [146]Contents ] [ [147]Section Contents ] [ [148]Next ] [ [149]Previous ] This section is obsolete. C-Kermit 6.0 was the last release that could be built on PDP-11 based BSD versions. 4.4. The Plan 9 Makefile [ [150]Top ] [ [151]Contents ] [ [152]Section Contents ] [ [153]Next ] [ [154]Previous ] Use the separate makefile [155]ckpker.mk. NOTE: The Plan 9 version of C-Kermit 8.0 has not yet been built. There should be no impediment to building it. However, even when built successfully, certain key features are missing, notably TCP/IP networking. 4.5. Makefile Failures [ [156]Top ] [ [157]Contents ] [ [158]Section Contents ] [ [159]Previous ] First, be sure the source files are stored on your current disk and directory with the right names (in lowercase). Second, make sure that the makefile itself does not contain any lines with leading spaces: indented lines must all start with horizontal TAB, and no spaces. Then make sure that your Unix PATH is defined to find the appropriate compiler for your makefile target. For example, on SunOS systems, "make sunos41" builds C-Kermit for the BSD environment, and assumes that /usr/ucb/cc will be used for compilation and linking. If your PATH has /usr/5bin ahead of /usr/ucb, you can have problems at compile or link time (a commonly reported symptom is the inability to find "ftime" during linking). Fix such problems by redefining your Unix PATH, or by specifying the appropriate "cc" in CC= and CC2= statements in your makefile target. During edits 166-167, considerable effort went into making C-Kermit compilable by ANSI C compilers. This includes prototyping all of C-Kermit's functions, and including the ANSI-defined system header files for system and library functions, as defined in K&R, second edition: , , (except in NeXTSTEP this is ), and . If you get warnings about any of these header files not being found, or about argument mismatches involving pid_t, uid_t, or gid_t, look in ckcdeb.h and make amendments. C-Kermit assumes it is being compiled by an ANSI-compliant C compiler if __STDC__ is defined, normally defined by the compiler itself. You can force ANSI compilation without defining __STDC__ (which some compilers won't let you define) by including -DCK_ANSIC on the cc command line. On the other hand, if your compiler defines __STDC__ but still complains about the syntax of Kermit's function prototypes, you can disable the ANSI-style function prototyping by including -DNOANSI on the command line. For SCO OpenServer, UNIX, ODT, and XENIX compilations, be sure to pick the most appropriate [160]makefile target, and be sure you have installed an SCO development system that is keyed to your exact SCO operating system release, down to the minor version (like 2.3.1). Also note that SCO distributes some of its libraries in encrypted form, and they must be decrypted before C-Kermit can be linked with them. If not, you might see a message like: ld: file /usr/lib/libsocket.a is of unknown type: magic number = 6365 To decrypt, you must supply a key (password) that came with your license. Call SCO for further info. If your compiler uses something other than int for the pid (process id) data type, put -DPID_T=pid_t or whatever in your CFLAGS. If you get complaints about unknown data types uid_t and gid_t, put -DUID_T=xxx -DGID_T=yyy in your CFLAGS, where xxx and yyy are the appropriate types. If your compilation fails because of conflicting or duplicate declarations for sys_errlist, add -DUSE_STRERROR or -DNDSYSERRLIST to CFLAGS. If your compilation dies because getpwnam() is being redeclared (or because of "conflicting types for getwpnam"), add -DNDGPWNAM to your CFLAGS. If that doesn't work, then add -DDCGPWNAM to your CFLAGS (see ckufio.c around line 440). If the compiler complains about the declaration of getpwnam() during an ANSI C compilation, remove the declaration from ckufio.c or change the argument in the prototype from (char *) to (const char *). If you get complaints that getpwuid() is being called with an improper type, put -DPWID_T=xx in your CFLAGS. If you get compile-time warnings that t_brkc or t_eofc (tchars structure members, used in BSD-based versions) are undefined, or structure-member- related warnings that might be traced to this fact, add -DNOBRKC to CFLAGS. If you get a linker message to the effect that _setreuid or _setregid is not defined, add -DNOSETREU to CFLAGS, or add -DCKTYP_H=blah to CFLAGS to make C-Kermit read the right -kind-of-file to pick up these definitions. If you get a message that _popen is undefined, add -DNOPOPEN to CFLAGS. If you get a complaint at compile time about an illegal pointer-integer combination in ckufio.c involving popen(), or at link time that _popen is an undefined symbol, add the declaration "FILE *popen();" to the function zxcmd() in ckufio.c (this declaration is supposed to be in ). If making this change does not help, then apparently your Unix does not have the popen() function, so you should add -DNOPOPEN to your make entry, in which case certain functions involving "file" i/o to the standard input and output of subprocesses will not be available. If your linker complains that _getcwd is undefined, you can add a getcwd() function to ckufio.c, or add it to your libc.a library using ar: #include char * getcwd(buf,size) char *buf; int size; { #ifndef NOPOPEN #ifdef DCLPOPEN FILE *popen(); #endif FILE *pfp; if (!buf) return(NULL); if (!(pfp = popen("pwd","r"))) return(NULL); fgets(buf,size-2,pfp); pclose(pfp); buf[strlen(buf)-1] = '\0'; return((char *)buf); #else buf[0] = '\0'; return(NULL); #endif /* NOPOPEN */ } #ifdef NOPOPEN FILE *popen(s,t) char *s,*t; { return(NULL); } #endif /* NOPOPEN */ If you get complaints about NPROC having an invalid value, add a valid definition for it (depends on your system), as in the cray entry. If you get some symbol that's multiply defined, it probably means that a variable name used by Kermit is also used in one of your system libraries that Kermit is linked with. For example, under PC/IX some library has a variable or function called "data", and the variable "data" is also used extensively by Kermit. Rather than edit the Kermit source files, just put a -D in the make entry CFLAGS to change the Kermit symbol at compile time. In this example, it might be -Ddata=xdata. Some symbol is defined in your system's header files, but it produces conflicts with, or undesired results from, Kermit. Try undefining the symbol in the makefile target's CFLAGS, for example -UFIONREAD. Some well-known symbol is missing from your system header files. Try defining in the makefile target's CFLAGS, for example -DFREAD=1. You get many warnings about pointer mismatches. This probably means that Kermit is assuming an int type for signal() when it should be void, or vice-versa. Try adding -DSIG_I (for integer signal()) or -DSIG_V (for void) to CFLAGS. Or just include KFLAGS=-DSIG_V (or whatever) in your "make" command, for example: make bsd KFLAGS=-DSIG_V You get many messages about variables that are declared and/or set but never used. It is difficult to avoid these because of all the conditional compilation in the program. Ignore these messages. Some of C-Kermit's modules are so large, or contain so many character string constants, or are so offensive in some other way, that some C compilers give up and refuse to compile them. This is usually because the -O (optimize) option is included in the make entry. If this happens to you, you can (a) remove the -O option from the make entry, which will turn off the optimizer for ALL modules; or (b) compile the offending module(s) by hand, including all the switches from make entry except for -O, and then give the appropriate "make" command again; or (c) increase the value of the -Olimit option, if your compiler supports this option; or (d) change the [161]makefile target to first compile each offending module explicitly without optimization, then compile the others normally (with optimization), for example: #Fortune 32:16, For:Pro 2.1 (mostly like 4.1bsd) ft21: @echo 'Making C-Kermit $(CKVER) for Fortune 32:16 For:Pro 2.1...' $(MAKE) ckuusx.$(EXT) "CFLAGS= -DNODEBUG -DBSD4 -DFT21 -DNOFILEH \ -SYM 800 \ -DDYNAMIC -DNOSETBUF -DCK_CURSES $(KFLAGS) -DPID_T=short" $(MAKE) ckuxla.$(EXT) "CFLAGS= -DNODEBUG -DBSD4 -DFT21 -DNOFILEH \ -SYM 800 \ -DDYNAMIC -DNOSETBUF -DCK_CURSES $(KFLAGS) -DPID_T=short" $(MAKE) ckudia.$(EXT) "CFLAGS= -DNODEBUG -DBSD4 -DFT21 -DNOFILEH \ -SYM 800 \ -DDYNAMIC -DNOSETBUF -DCK_CURSES $(KFLAGS) -DPID_T=short" $(MAKE) wermit "CFLAGS= -O -DNODEBUG -DBSD4 -DFT21 -DNOFILEH -SYM 800 \ -DDYNAMIC -DNOSETBUF -DCK_CURSES $(KFLAGS) -DPID_T=short" \ "LNKFLAGS= -n -s" "LIBS= -lcurses -ltermcap -lv -lnet" As an extreme example, some compilers (e.g. gcc on the DG AViiON) have been known to dump core when trying to compile ckwart.c with optimization. So just do this one "by hand": cc -o wart ckwart.c or: touch ckcpro.c and then give the "make" command again. Speaking of wart, it is unavoidable that some picky compilers might generate "statement unreachable" messages when compiling ckcpro.c. Unreachable statements can be generated by the wart program, which generates ckcpro.c automatically from [162]ckcpro.w, which translates lex-like state/input constructions into a big switch/case construction. Some function in Kermit wreaks havoc when it is called. Change all invocations of the function into a macro that evaluates to the appropriate return code that would have been returned by the function had it been called and failed, for example: -Dzkself()=0. Obviously not a good idea if the function is really needed. If you have just installed SunOS 4.1.2 or 4.1.3, you might find that C-Kermit (and any other C program) fails to link because of unresolved references from within libc. This is because of a mistake in Sun's /usr/lib/shlib.etc files for building the new libc. Change the libc Makefile so that the "ld" lines have "-ldl" at the end. Change the README file to say "mv xccs.multibyte. xccs.multibyte.o" and follow that instruction. 5. INSTALLING THE KERMIT FILES [ [163]Top ] [ [164]Contents ] [ [165]Next ] [ [166]Previous ] SECTION CONTENTS 5.1. [167]The C-Kermit Initialization File 5.2. [168]Text Files 5.3. [169]Installing the Kermit Files 5.4. [170]The Makefile Install Target The C-Kermit executable does not need any external files to run. Unlike, say, the cu program, which on most platforms is useless unless you (as root) edit the /usr/spool/uucp/Systems and /usr/spool/uucp/Devices files to supply whatever obscure and undocumented syntax is required to match some supposedly user-friendly mnemonic to the real pathname of whatever device you want to use, Kermit runs on its own without needing any external configuration files, and lets you refer to device (and network hosts and services) by their own natural undisguised names. Nevertheless, a number of external files can be installed along with the C-Kermit executable if you wish. These include configuration and customization files that are read by Kermit as well as documentation files to be read by people. All of this material is (a) optional, and (b) available on the Kermit website: [171]http://www.columbia.edu/kermit/ and usually in a more pleasant form, perhaps also with updated content. So if your computer is on the Internet, there is no need to install anything but the Kermit executable if users know how to find the Kermit website (and if they don't, Kermit's "help" command tells them). 5.1. The C-Kermit Initialization File In C-Kermit 7.0 and earlier, the standard initialization file was a key C-Kermit component because: a. It "loaded" the dialing and network directories. b. It defined all the macros and variables for the services directory. c. It defined macros for quickly changing Kermit's file-transfer performance tuning. The standard initialization file is quite long (more than 600 lines) and requires noticeable processing time (the slower the computer, the more noticeable), yet few people actually use the services directory, whose definition takes up most of its bulk. Meanwhile, in C-Kermit 8.0, many of the remaining functions of the standard initialization file are now built in; for example, the FAST, CAUTIOUS, and ROBUST commands. More to the point, many of the settings that could be made only in the initialization and customization files can now be picked up from environment variables. The first group identifies initialization and directory files: CKERMIT_INI The path of your Kermit initialization file, if any. This overrides the built-in search for $HOME/.kermrc. K_CHARSET The character set used for encoding local text files. Equivalent to SET FILE CHARACTER-SET. K_DIAL_DIRECTORY The full pathname of one or more Kermit dialing directory files. Equivalent to SET DIAL DIRECTORY. K_NET_DIRECTORY The full pathname of one or more Kermit network directory files. Equivalent to SET NETWORK DIRECTORY. K_INFO_DIRECTORY K_INFO_DIR The full pathname of a directory containing Kermit (if any) containing ckubwr.txt and other Kermit text files. Overrides Kermit's built-in search for this directory. The next group is related to dialing modems: K_COUNTRYCODE The telephonic numeric country code for this location, e.g. 1 for North America or 39 for Italy. It is recommended that this one be set for all users, system-wide. Not only is it used to process portable-format dialing directory entries, but it is also compared against Kermit's built-in list of "tone countries" to see if tone dialing can be used. Equivalent to Kermit's SET DIAL COUNTRY-CODE command. K_AREACODE The telephonic numeric area code for this location, e.g. 212 for Manhattan, New York, USA. Recommend this one also be set system-wide, so shared portable-format dialing directories will work automatically for everybody. Equivalent to Kermit's SET DIAL AREA-CODE command. K_DIAL_METHOD TONE or PULSE. Equivalent to Kermit's SET DIAL METHOD command. If a dial method is not set explicitly (or implicitly from the country code), Kermit does not specify a dialing method, and uses the modem's default method, which tends to be pulse. K_INTL_PREFIX The telephonic numeric international dialing prefix for this location. Equivalent to Kermit's SET DIAL INTL-PREFIX command. K_LD_PREFIX The telephonic numeric long-distance dialing prefix for this location. Equivalent to Kermit's SET DIAL LD-PREFIX command. K_PBX_ICP The telephonic numeric PBX internal call prefix for this location. Equivalent to Kermit's SET DIAL PBX-INSIDE-PREFIX command. K_PBX_OCP The telephonic numeric PBX external call prefix for this location. Equivalent to Kermit's SET DIAL PBX-OUTSIDE-PREFIX command. K_PBX_XCH The telephonic numeric PBX exchange (first part of the subscriber number). Equivalent to Kermit's SET DIAL PBX-EXCHANGE command. K_TF_AREACODE A list of one or more telephonic numeric toll-free area codes. K_TF_PREFIX The telephonic numeric toll-free dialing prefix, in case it is different from the long-distance prefix. Equivalent to Kermit's SET DIAL TF-PREFIX command. The final group includes well-known environment variables that are also used by Kermit: CDPATH Where the CD command should look for relative directory names. SHELL The path of your Unix shell. Used by the RUN (!) command to choose the shell to execute its arguments. USER Your Unix username. EDITOR The name or path of your preferred editor (used by the EDIT command). Equivalent to SET EDITOR. BROWSER The name or path of your preferred web browser (used by the BROWSE command). Equivalent to Kermit's SET BROWSER command. Does this mean the initialization file can be abolished? I think so. Here's why: * Kermit already does everything most people want it to do without one. * Important site-specific customizations can be done with global environment variables. * There is no longer any need for everybody to have to use the standard initialization file. * This means that your initialization file, if you want one, can contain your own personal settings, definitions, and preferences, rather than 600 lines of "standard" setups. * If you still want the services directory, you can either TAKE the standard initialization file (which must be named anything other than $HOME/.kermrc to avoid being executed automatically every time you start Kermit), or you can make it a kerbang script and execute it "directly" (the [172]makefile install target does this for you by putting ckermit.ini in the same directory as the Kermit binary, adding the appropriate Kerbang line to the top, and giving it execute permission). In fact, you can put any number of kerbang scripts in your PATH to start up C-Kermit in different ways, to have it adopt certain settings, make particular connections, execute complicated scripts, whatever you want. And in C-Kermit 9.0 the initialization has indeed been "retired" by renaming it to okermit.ini, and replaced by a stub ckermit.ini that doesn't do anything but print a message. Ditto for ckermod.ini. 5.2. Text Files These are entirely optional. Many of them are to be found at the Kermit website in HTML form (i.e. as Web pages with clickable links, etc), and very likely also more up to date. Plain-text files that correspond to Web pages were simply "dumped" by Lynx from the website to plain ASCII text. The format is whatever Lynx uses for this purpose. If you wish, you can install them on your computer as described in the [173]next section. [174]COPYING.TXT Copyright notice, permissions, and disclaimer. [175]ckermit.ini The standard initialization file, intended more for reference (in most cases) than actual use; see [176]Section 5.1. [177]ckermod.ini A sample customization file. [178]ckermit70.txt Supplement to [179]Using C-Kermit for version 7.0. Available on the Kermit website as: [180]http://www.columbia.edu/kermit/ckermit70.html [181]ckermit80.txt Supplement to [182]Using C-Kermit for version 8.0. Available on the Kermit website as: [183]http://www.columbia.edu/kermit/ckermit80.html [184]ckermit90.txt Supplement to [185]Using C-Kermit for version 9.0. Available on the Kermit website as: [186]http://www.columbia.edu/kermit/ckermit90.html [187]ckcbwr.txt The general C-Kermit hints and tips ("beware") file. Available on the Kermit website as: [188]http://www.columbia.edu/kermit/ckcbwr.html [189]ckubwr.txt The Unix-specific C-Kermit hints and tips file. Available on the Kermit website as: [190]http://www.columbia.edu/kermit/ckubwr.html [191]ckuins.txt Unix C-Kermit Installation Instructions (this file). Available on the Kermit website as: [192]http://www.columbia.edu/kermit/ckuins.html [193]ckccfg.txt C-Kermit compile-time configuration options. Available on the Kermit website as: [194]http://www.columbia.edu/kermit/ckccfg.html [195]ckcplm.txt The C-Kermit program logic manual. Available on the Kermit website as: [196]http://www.columbia.edu/kermit/ckcplm.html [197]ca_certs.pem Certificate Authority certificates for secure connections (see [198]Section 16). 5.3. Installing the Kermit Files There is an "install" target in the [199]makefile that you can use if you wish. However, since every site has its own layout and requirements, it is often better to install the Kermit files by hand. You don't have to use the makefile install target to install C-Kermit. This is especially true since not all sites build C-Kermit from source, and therefore might not even have the makefile. But you should read this section in any case. If your computer already has an older version of C-Kermit installed, you should rename it (e.g. to "kermit6" or "kermit7") so in case you have any trouble with the new version, the old one is still available. In most cases, you need to be root to install C-Kermit, if only to gain write access to directories in which the binary and manual page are to be copied. The C-Kermit binary should be installed in a directory that is in the users' PATH, but that is not likely to be overwritten when you install a new version of the operating system. A good candidate would be the /usr/local/bin/ directory, but the specific choice is site dependent. Example (assuming the appropriate Kermit binary is stored in your current directory as "wermit", e.g. because you just built it from source and that's the name the makefile gave it): mv wermit /usr/local/bin/kermit chmod 755 /usr/local/bin/kermit or (only after you finish reading this section!) simply: make install IMPORTANT: IF C-KERMIT IS TO BE USED FOR DIALING OUT, you must also do something to give it access to the dialout devices and lockfile directories. The 'install' target does not attempt to set Kermit's owner, group, and permissions to allow dialing out. This requires privileges, open eyes, and human decision-making. Please read [200]Sections 10 and [201]11 below, make the necessary decisions, and then implement them by hand as described in those sections. You should also install the man page, which is called ckuker.nr, in the man page directory for local commands, such as /usr/man/man1/, renamed appropriately, e.g. to kermit.1. This is also taken care of by "make install". Optionally, the text files listed in the [202]previous section can be placed in a publicly readable directory. Suggested directory names are: /usr/local/doc/kermit/ /usr/local/lib/kermit/ /usr/share/lib/kermit/ /opt/kermit/doc/ (or any of these without the "/kermit"). Upon startup, C-Kermit checks the following environment variables whose purpose is to specify the directory where the C-Kermit text files are, in the following order: K_INFO_DIRECTORY K_INFO_DIR If either of these is defined, C-Kermit checks for the existence of the ckubwr.txt file (Unix C-Kermit Hints and Tips). If not found, it checks the directories listed above (both with and without the "/kermit") plus several others to see if they contain the ckubwr.txt file. If found, various C-Kermit messages can refer the user to this directory. Finally, if you want to put the source code files somewhere for people to look at, you can do that too. 5.4. The Makefile Install Target The makefile "install" target does almost everything for you if you give it the information it needs by setting the variables described below. You can use this target if: * You downloaded the [203]complete C-Kermit archive and built C-Kermit from source; or: * You downloaded an [204]individual C-Kermit binary and the [205]C-Kermit text-file archive, and your computer has a "make" command. Here are the parameters you need to know: BINARY Name of the binary you want to install as "kermit". Default: "wermit". prefix (lower case) If you define this variable, its value is prepended to all the following xxxDIR variables (8.0.211 and later). DESTDIR If you want to install the Kermit files in a directory structure like /opt/kermit/bin/, /opt/kermit/doc/, /opt/kermit/src/, then define DESTIR as the root of this structure; for example, /opt/kermit. The DESTDIR string should not end with a slash. By default, DESTDIR is not defined. If it is defined, but the directory does not exist, the makefile attempts to create it, which might require you to be root. Even so, this can fail if any segments in the path except the last one do not already exist. WARNING: If the makefile creates any directories, it gives them a mode of 755, and the default owner and group. Modify these by hand if necessary. BINDIR Directory in which to install the Kermit binary (and the standard C-Kermit initialization file, if it is found, as a Kerbang script). If DESTDIR is defined, BINDIR must start with a slash. BINDIR must not end with a slash. If DESTDIR is defined, BINDIR is a subdirectory of DESTDIR. If BINDIR does not exist, the makefile attempts to create it as with DESTDIR. Default: /usr/local/bin. MANDIR Directory in which to install the C-Kermit manual page as "kermit" followed by the manual-chapter extension (next item). Default: /usr/man/man1. If MANDIR is defined, the directory must already exist. MANEXT Extension for the manual page. Default: 1 (digit one). SRCDIR Directory in which to install the C-Kermit source code. If DESTDIR is defined, this is a subdirectory of DESTDIR. Default: None. CERTDIR For secure builds only: Directory in which to install the ca_certs.pem file. This must be the verification directory used by programs that use the SSL libraries at your site. Default: none. Possibilities include: /usr/local/ssl, /opt/ssl, /usr/lib/ssl, . . . If CERTDIR is defined, the directory must already exist. INFODIR Directory in which to install the C-Kermit text files. If DESTDIR is defined, this is a subdirectory of DESTDIR. Default: None. If INFODIR is defined but does not exist, the makefile attempts to create it, as with DESTDIR. Examples: make install Installs "wermit" as /usr/local/bin/kermit with permissions 755, the default owner and group, and no special privileges. The manual page is installed as /usr/man/man1/kermit.1. Text files are not copied anywhere, nor are the sources. make MANDIR= install Just like "make install" but does not attempt to install the manual page. make DESTDIR=/opt/kermit BINDIR=/bin SRCDIR=/src INFODIR=/doc install Installs the Kermit binary "wermit" as /opt/kermit/bin/kermit, puts the source code in /opt/kermit/src, and puts the text files in /opt/kermit/doc, creating the directories if they don't already exist, and puts the man page in the default location. make BINDIR=/usr/local/bin CERTDIR=/usr/local/ssl install Installs the Kerberized Kermit binary "wermit" as /usr/local/bin/kermit, puts the CA Certificates file in /usr/local/ssl/, and the man page in the normal place. For definitive information, see the makefile. The following is excerpted from the 8.0.211 makefile: # The following symbols are used to specify library and header file locations # Redefine them to the values used on your system by: # . editing this file # . defining the values on the command line # . defining the values in the environment and use the -e option # prefix = /usr/local srproot = $(prefix) sslroot = $(prefix) manroot = $(prefix) K4LIB=-L/usr/kerberos/lib K4INC=-I/usr/kerberos/include K5LIB=-L/usr/kerberos/lib K5INC=-I/usr/kerberos/include SRPLIB=-L$(srproot)/lib SRPINC=-I$(srproot)/include SSLLIB=-L$(sslroot)/ssl/lib SSLINC=-I$(sslroot)/ssl/include ... WERMIT = makewhat BINARY = wermit DESTDIR = BINDIR = $(prefix)/bin MANDIR = $(manroot)/man/man1 MANEXT = 1 SRCDIR = INFODIR = CERTDIR = 6. INSTALLING UNIX C-KERMIT FROM DOS-FORMAT DISKETTES [ [206]Top ] [ [207]Contents ] [ [208]Next ] [ [209]Previous ] This section is obsolete. We don't distribute C-Kermit on diskettes any more because (a)there is no demand, and (b) it no longer fits. If you received a DOS-format diskette containing a binary executable C-Kermit program plus supporting text files, be sure to chmod +x the executable before attempting to run it. In version 5A(190) and later, all the text files on the C-Kermit DOS-format diskettes are in Unix format: LF at the end of each line rather than CRLF. This means that no conversions are necessary when copying to your Unix file system, and that all the files on the diskette, text and binary, can be copied together. The following comments apply to the DOS-format diskettes furnished with version 5A(189) and earlier or to other DOS-format diskettes you might have obtained from other sources. If you have received C-Kermit on MS-DOS format diskettes (such as those distributed by Columbia University), you should make sure that your DOS-to-Unix conversion utility (such as "dosread") both: (1) changes line terminators in all files from carriage-return linefeed (CRLF) to just linefeed (LF) (such as "dosread -a") and remove any Ctrl-Z's, and (2) that all filenames are converted from uppercase to lowercase. If these conversions were not done, you can use the following shell script on your Unix system to do them: ---(cut here)--- #!/bin/sh # # Shell script to convert C-Kermit DOS-format files into Unix format. # Lowercases the filenames, strips out carriage returns and Ctrl-Z's. # x=$1 # the name of the source directory y=$2 # the name of the target directory if [ $# -lt 2 ]; then echo "usage: $0 source-directory target-directory" exit 1 fi if cd $1 ; then echo "Converting files from $1 to $2" else echo "$0: cannot cd to $1" exit 1 fi for i in *; do j=`echo $i | tr 'A-Z' 'a-z'` echo $x/$i =\> $y/$j tr -d '\015\032' < $i > $y/$j done ---(cut here)--- Cut out this shell script, save it as "convert.sh" (or any other name you prefer), then "chmod +x convert.sh". Then, create a new, empty directory to put the converted files in, and then "convert.sh /xxx /yyy" where /xxx is the name of the directory where the PC-format files are, and /yyy is the name of the new, empty directory. The converted files will appear in the new directory. 7. CHECKING THE RESULTS [ [210]Top ] [ [211]Contents ] [ [212]Next ] [ [213]Previous ] First some quick checks for problems that can be easily corrected by recompiling with different options: DIRECTORY listing is garbage Permissions, size, and date are random garbage (but the filenames are correct) in a C-Kermit DIRECTORY listing. On some platforms, the lstat() function is present but simply doesn't work; try adding -DNOLSTAT to CFLAGS and rebuild. If that doesn't fix it, also add -DNOLINKBITS. If it's still not fixed, remove -DNOLSTAT and -DNOLINKBITS and add -DNOSYMLINK. curses When you make a connection with C-Kermit and transfer files using the fullscreen (curses) file-transfer display, and then get the C-Kermit> prompt back afterwards, do characters echo when you type them? If not, the curses library has altered the buffering of /dev/tty. Try rebuilding with KFLAGS=-DCK_NEWTERM. If it already has -DCK_NEWTERM in CFLAGS, try removing it. If that doesn't help, then rebuild with -DNONOSETBUF (yes, two NO's). If none of this works (and you can't fix the code), then either don't use the fullscreen display, or rebuild with -DNOCURSES. Ctrl-L or any SCREEN command crashes C-Kermit: Rebuild with -DNOTERMCAP. No prompt after CONNECT: After escaping back from CONNECT mode, does your C-Kermit> prompt disappear? (Yet, typing "?" still produces a command list, etc) In that case, add -DCKCONINTB4CB to CFLAGS and rebuild. Here is a more thorough checklist can use to tell whether your version of C-Kermit was built correctly for your Unix system, with hints on how to fix or work around problems: a. Start C-Kermit (usually by typing "./wermit" in the directory where you ran the makefile). Do you see the C-Kermit> prompt? If not, C-Kermit incorrectly deduced that it was running in the background. The test is in conbgt() in [214]ckutio.c. If you can fix it for your system, please send in the fix (Hint: read about "PID_T" below). Otherwise, you can force C-Kermit to foreground mode by starting it with the -z command line option, as in "kermit -z", or giving the interactive command SET BACKGROUND OFF. b. When you type characters at the C-Kermit prompt, do they echo immediately? If not, something is wrong with concb() and probably the other terminal mode settings routines in [215]ckutio.c. Be sure you have used the most appropriate make entry. c. At the C-Kermit> prompt, type "send ./?". C-Kermit should list all the files in the current directory. If not, it was built for the wrong type of Unix file system. Details below. In the meantime, try SET WILDCARD-EXPANSION SHELL as a workaround. d. CD to a directory that contains a variety of files, symlinks, and subdirectories and give a DIRECTORY command at the C-Kermit> prompt. Do the permissions, size, and date appear correct? If not see [216]Section 4.0. e. Assuming your platform supports long file names, create a file with a long name in your current directory, e.g.: $ touch thisisafilewithaveryveryveryveryveryveryveryverylooooooooongname (you might need to make it longer than this, perhaps as long as 257 or even 1025 characters). Check with ls to see if your version of Unix truncated the name. Now start C-Kermit and type "send thisis". Does Kermit complete the name, showing the same name as ls did? If not, wrong filesystem. Read on. f. Make sure that Kermit has the maximum path length right. Just type SHOW FILE and see what it says about this. If it is too short, there could be some problems at runtime. To correct, look in [217]ckcdeb.h to see how the symbol CKMAXPATH is set and make any needed adjustments. g. Send a file to your new Kermit program from a different Kermit program that is known to work. Is the date/timestamp of the new file identical to the original? If not, adjustments are needed in zstrdt() in [218]ckufio.c. h. Go to another computer (Computer B) from which you can send files to C-Kermit. Connect Computer B to the computer (A) where you are testing C-Kermit. Then: i. Send a file from B to A. Make sure it transferred OK and was created with the right name. j. Send a file from B to A, specifying an "as-name" that is very, very long (longer than the maximum name length on computer A). Check to make sure that the file was received OK and that its name was truncated to Computer A's maximum length. If not, check the MAXNAMLEN definition in [219]ckufio.c. k. Tell C-Kermit on Computer A to "set receive pathnames relative" and then send it a file from Computer B specifying an as-name that contains several directory segments: send foo dir1/dir2/dir3/foo Check to make sure that dir1/dir2/dir3/foo was created in Computer A's current directory (i.e. that three levels of directories were created). l. Repeat step k, but make each path segment in the pathname longer than Computer A's maximum name length. Make sure each directory name, and the final filename, were truncated properly. m. Type Ctrl-C (or whatever your Unix interrupt character is) at the prompt. Do you get "^C..." and a new prompt? If instead, you get a core dump (this shouldn't happen any more) "rm core" and then rebuild with -DNOCCTRAP added to your CFLAGS. If it did work, then type another Ctrl-C. If this does the same thing as the first one, then Ctrl-C handling is OK. Otherwise, the SIGINT signal is either not getting re-armed (shouldn't happen) or is being masked off after the first time it is caught, in which case, if your Unix is POSIX-based, try rebuilding C-Kermit with -DCK_POSIX_SIG. n. Type Ctrl-Z (or whatever your Unix suspend character is) to put C-Kermit in the background. Did it work? If nothing happened, then (a)your version of Unix does not support job control, or (b) your version of C-Kermit was probably built with -DNOJC. If your session became totally frozen, then you are probably running C-Kermit on a Unix version that supports job control, but under a shell that doesn't. If that's not the case, look in the congm() and psuspend() routines in [220]ckutio.c and see if you can figure out what's wrong. If you can't, rebuild with -DNOJC. o. Give a SET LINE command for a dialout device, e.g. "set line /dev/tty00". If you got some kind of permission or access denied message, go read [221]Section 10 and then come back here. p. After giving a successful SET LINE command, type "show comm" to see the communication parameters. Do they make sense? q. Type "set speed ?" and observe the list of available speeds. Is it what you expected? If not, see [222]Section 2) of the [223]Configurations Options document. r. Give a SET SPEED command to change the device's speed. Did it work? (Type "show comm" again to check.) s. Try dialing out: SET MODEM TYPE , SET LINE , SET SPEED , DIAL . If it doesn't work, keep reading. After dialing, can you REDIAL? t. If your version was built with TCP/IP network support, try the TELNET command. u. Transfer some files in remote mode on incoming asynchronous serial (direct or modem) connections, and on incoming network (telnet, rlogin, terminal server) connections. If you get lots of errors, try different SET FLOW settings on the remote Kermit program. v. Establish a serial connection from C-Kermit to another computer (direct or dialed) and transfer some files. If you have network support, do the same with a network connection. w. If your version was built with fullscreen file transfer display support, check that it works during local-mode file transfer. Also, check C-Kermit's operation afterwards: is the echoing funny? etc etc. If there are problems, see [224]Section 4. x. If your version was built with script programming language support, TAKE the ckedemo.ksc file to give it a workout. y. Does C-Kermit interlock correctly with UUCP-family programs (cu, tip, uucp, etc)? If not, read the section [225]DIALING OUT AND COORDINATING WITH UUCP below. z. Modem signals... Give a SET LINE command to a serial device and then type the SHOW MODEM command. If it says "Modem signals unavailable in this version of Kermit", then you might want to look at the ttgmdm() routine in [226]ckutio.c and add the needed code -- if indeed your version of Unix provides a way to get modem signals (some don't; e.g. modem signals are a foreign concept to POSIX, requiring politically incorrect workarounds). aa. If it says "Modem signals unavailable", then it is likely that the API for getting modem signals is provided, but it doesn't actually do anything (e.g. ioctl(ttyfd,TIOCMGET,&x) returns EINVAL). ab. In any case, it still should be able to manipulate the DTR signal. To test, SET LINE , SET MODEM NONE, and HANGUP. The DTR light should go out momentarily. If it doesn't, see if you can add the needed code for your system to the tthang() routine in [227]ckutio.c. ac. If your version of Kermit has the SET FLOW RTS/CTS command, check to see if it works: give Kermit this command, set your modem for RTS/CTS, transfer some files (using big packet and window sizes) and watch the RTS and CTS lights on the modem. If they go on and off (and Kermit does not get packet errors), then it works. If your version of Kermit does not have this command, but your version of Unix does support hardware flow control, take a look at the tthflow() command in [228]ckutio.c and see if you can add the needed code (see the section on [229]HARDWARE FLOW CONTROL below). (And please [230]send back any added code, so that others can benefit from it and it can be carried forward into future releases.) ad. If C-Kermit starts normally and issues its prompt, echoing is normal, etc, but then after returning from a CONNECT session, the prompt no longer appears, try rebuilding with -DCKCONINTB4CB. ae. (8.0.206 or later) Type some commands at the C-Kermit prompt. Can you use the Up-arrow and Down-arrow keys on your keyboard to access Kermit's command history? If not, and you're a programmer, take a look at the USE_ARROWKEYS sections of ckucmd.c. 8. REDUCING THE SIZE OF THE EXECUTABLE PROGRAM IMAGE [ [231]Top ] [ [232]Contents ] [ [233]Next ] [ [234]Previous ] Also see: [235]C-Kermit Configuration Options a. Many of C-Kermit's options and features can be deselected at compile time. The greatest savings at the least sacrifice in functionality is to disable the logging of debug information by defining NODEBUG during compilation. See the [236]Configurations Options document for further information. b. Use shared libraries rather than static linking. This is the default on many Unix systems anyway. However, executables built for dynamic linking with shared libraries are generally not portable away from the machine they were built on, so this is recommended if the binary is for your use only. c. Most Unix systems have a "strip" command to remove symbol table information from an executable program image. "man strip" for further information. The same effect can be achieved by including "-s" among the link flags when building C-Kermit. d. SCO, Interactive, and some other Unix versions have an "mcs" command. "mcs -d wermit" can be used to delete the contents of the ".comment" section from the executable program image. e. Many modern optimizers can be instructed to optimize for space rather than execution efficiency. Check the CFLAGS in the makefile target, adjust as desired. 9. UNIX VERSIONS [ [237]Top ] [ [238]Contents ] [ [239]Next ] [ [240]Previous ] SECTION CONTENTS 9.1 [241]Standards 9.1.1. [242]POSIX 9.1.2. [243]ANSI C 9.1.3. [244]Other Standards 9.2. [245]Library Issues 9.3. [246]Unix File System Peculiarities 9.4. [247]Hardware Flow Control 9.5. [248]Terminal Speeds 9.6. [249]Millisecond Sleeps 9.7. [250]Nondestructive Input Buffer Peeking 9.8. [251]Other System-Dependent Features 9.9. [252]Terminal Interruption There are several major varieties of Unix: Bell Laboratories Seventh Edition, AT&T System V, Berkeley Standard Distribution (BSD), and POSIX. Each has many, many subvarieties and descendents, and there are also hybrids that exhibit symptoms of two or more varieties, plus special quirks of their own. Seventh edition versions of C-Kermit include the compile-time option -DV7 in the CFLAGS string in the makefile target. Various V7-based implementations are also supported: -DCOHERENT, -DMINIX, etc. AT&T-based versions of Unix Kermit include the compile-time option -DATTSV (standing for AT∓T Unix System V). This applies to System III and to System V up to and including Release 2. For System V Release 3, the flag -DSVR3 should be used instead (which also implies -DATTSV). This is because the data type of signal() and several other functions was changed between SVR2 and SVR3. For System V Release 4, include -DSVR4 because of changes in UUCP lockfile conventions; this also implies -DSVR3 and -DATTSV. For BSD, the flag -BSDxx must be included, where xx is the BSD version number, for example BSD4 (for version 4.2 or later, using only 4.2 features), -DBSD41 (for BSD 4.1 only), -DBSD43 (for 4.3), -DBSD29 (BSD 2.9 for DEC PDP-11s). -DBSD44 is for 4.4BSD, which is the basis of FreeBSD, NetBSD, OpenBSD, BSDI, and Mac OS X, and which contains many POSIX features, and has little relation to 4.3BSD and earlier. For POSIX, include the flag -DPOSIX. POSIX defines a whole new set of terminal i/o functions that are not found in traditional AT&T or Berkeley implementations, and also defines the symbol _POSIX_SOURCE, which is used in many system and library header files, mainly to disable non-POSIX (i.e. useful) features. Note (circa 1997): In order to enable serial speeds higher than 38400 bps, it is generally necessary to add -DPOSIX (among other things), since the older terminal APIs can not accommodate the new speeds -- out o' bits. But this often also means wholesale conversion to POSIX APIs. In general, just try adding -DPOSIX and then see what goes wrong. Be wary of features disappearing: when _POSIX_SOURCE is defined, all sorts of things that were perfectly OK before suddenly become politically incorrect -- like reading modem signals, doing hardware flow control, etc. POSIX was evidently not designed with serial communication in mind! Case in point: In UnixWare 7.0, #define'ing POSIX causes strictness clauses in the header files to take effect. These prevent from defining the timeval and timezone structs, which are needed for all sorts of things (like select()). Thus, if we want the high serial speeds, we have to circumvent the POSIX clauses. Similarly in SCO OpenServer R5.0.4 where, again, we must use the POSIX APIs to get at serial speeds higher than 38400, but then doing so removes hardware flow control -- just when we need it most! In cases like this, dirty tricks are the only recourse (search for SCO_OSR504 in [253]ckutio.c for examples). For reasons like this, Unix implementations tend to be neither pure AT&T nor pure BSD nor pure POSIX, but a mixture of two or more of these, with "compatibility features" allowing different varieties of programs to be built on the same computer. In general, Kermit tries not to mix and match but to keep a consistent repertoire throughout. However, there are certain Unix implementations that only work when you mix and match. For example, the Silicon Graphics IRIX operating system (prior to version 3.3) is an AT&T Unix but with a BSD file system. The only way you can build Kermit successfully for this configuration is to include -DSVR3 plus the special option -DLONGFN, meaning "pretend I was built with -DBSDxx when it's time to compile file-related code". See the "iris" makefile target. 9.1. Standards [ [254]Top ] [ [255]Section Contents ] [ [256]Contents ] [ [257]Next ] SUBSECTION CONTENTS 9.1.1. [258]POSIX 9.1.2. [259]ANSI C 9.1.3. [260]Other Standards In edits 166-167 (1988-89), C-Kermit was heavily modified to try to keep abreast of new standards while still remaining compatible with old versions of C and Unix. There are two new standards of interest: ANSI C (as described in Kernighan and Ritchie, "The C Programming Language", Second Edition, Prentice Hall, 1988) and POSIX.1 (IEEE Standard 1003.1 and ISO/IEC 9945-1, 1990, "Portable Operating System Interface"). These two standards have nothing to do with each other: you can build C-Kermit with a non-ANSI compiler for a POSIX system, or for a non-POSIX system with with an ANSI compiler. 9.1.1. POSIX POSIX.1 defines a repertoire of system functions and header files for use by C language programs. Most notably, the ioctl() function is not allowed in POSIX; all ioctl() functions have been replaced by device-specific functions like tcsetattr(), tcsendbreak(), etc. Computer systems that claim some degree of POSIX compliance have made some attempt to put their header files in the right places and give them the right names, and to provide system library functions with the right names and calling conventions. Within the header files, POSIX-compliant functions are supposed to be within #ifdef _POSIX_SOURCE..#endif conditionals, and non-POSIX items are not within these conditionals. If Kermit is built with neither -D_POSIX_SOURCE nor -DPOSIX, the functions and header files of the selected version of Unix (or VMS, etc) are used according to the CFLAGS Kermit was built with. If Kermit is built with -D_POSIX_SOURCE but not -DPOSIX, then one of the -DBSD or -DATTSV flags (or one that implies them) must also be defined, but it still uses only the POSIX features in the system header files. This allows C-Kermit to be built on BSD or AT&T systems that have some degree of POSIX compliance, but still use BSD or AT&T specific features. The dilemma is this: it is often necessary to define _POSIX_SOURCE to get at new or modern features, such as high serial speeds and the APIs to deal with them. But defining _POSIX_SOURCE also hides other APIs that Kermit needs, for example the ones dealing with modem signals (others are listed just below). Thus all sorts of hideous contortions are often required to get a full set of features. The POSIX standard does not define anything about uucp lockfiles. "make posix" uses NO (repeat, NO) lockfile conventions. If your POSIX-compliant Unix version uses a lockfile convention such as HDBUUCP (see below), use the "posix" entry, but include the appropriate lockfile option in your KFLAGS on the "make" command line, for example: make posix "KFLAGS=-DHDBUUCP" POSIX.1 also lacks certain other features that Kermit needs. For example: * There is no defined way for an application to do wildcard matching of filenames. Kermit uses the inode in the directory structure, but POSIX.1 does not include this concept. (Later POSIX revisions include functions named (I think) glob() and fnmatch(), but these functions are not yet in Kermit, and might not be appropriate in any case.) * There is no POSIX mechanism for sensing or controlling modem signals, nor to enable RTS/CTS or other hardware flow control. * There is no select() for multiplexing i/o, and therefore no TCP/IP. * There is no way to check if characters are waiting in a communications device (or console) input buffer, short of trying to read them -- no select(), ioctl(fd,FIONREAD,blah), rdchk(), etc. This is bad for CONNECT mode and bad for sliding windows. * No way to do a millisecond sleep (no nap(), usleep(), select(), etc). * There is no popen(). So at this point, there cannot be one single fully functional POSIX form of C-Kermit unless it also has "extensions", as do Linux, QNX, etc. More on POSIX (quoting from a newsgroup posting by Dave Butenhof): Standards tend to look at themselves as "enabling". So POSIX standards say that, in order to use POSIX functions, a program must define some macro that will put the development environment in "POSIX mode". For the ancient POSIX 1003.1-1990, the symbol is _POSIX_SOURCE. For recent revisions, it's _POSIX_C_SOURCE with an appropriate value. POSIX 1003.1-1996 says that, to use its features in a portable manner, you must define _POSIX_C_SOURCE=199506L before including any header files. But for Solaris, or Digital Unix, the picture is different. POSIX is one important but small part of the universe. Yet POSIX unconditionally and unambiguously REQUIRES that, when _POSIX_C_SOURCE=199506L, ALL of the functions and definitions required by the standard, and NO others (except in specific restricted namespaces, specifically "_" followed by an uppercase letter or "__" followed by a lowercase letter) shall be visible. That kinda puts a cramp on BSD and SVID support, because those require names that are not in the "protected" POSIX namespaces. It's ILLEGAL to make those symbols visible, unless you've done something else that's beyond the scope of POSIX to allow the system to infer that you didn't really mean it. In most cases, you should just compile, with no standards-related macros defined. The system will make available every interface and definition that isn't incompatible with the "main stream". There may indeed be cases where two standards cross, and you really can't use both together. But, in general, they play nicely together as long as you don't do anything rash -- like telling the system that it's not allowed to let them. In the area of threads, both Solaris and Digital Unix support incompatible thread APIs. We have POSIX and DCE, they have POSIX and UI. The nasty areas are in the _r routines and in some aspects of signal behavior. You cannot compile a single source file that uses both semantics. That's life. It sounds as if Solaris defaults to the UI variants, but allows you to define this _POSIX_THREAD_SEMANTICS to get around it. We default to POSIX, and allow you to define _PTHREAD_USE_D4 (automatically defined by the cc "-threads" switch) to select the DCE thread variants. That default, because you're operating outside of any individual standard, is really just a marketing decision. 9.1.2. ANSI C [ [261]Top ] [ [262]Contents ] [ [263]Section Contents ] [ [264]Subsection Contents ] [ [265]Next ] [ [266]Previous ] The major difference between ANSI C and earlier C compilers is function prototyping. ANSI C allows function arguments to be checked for type agreement, and (when possible) type coercion in the event of a mismatch. For this to work, functions and their arguments must be declared before they are called. The form for function declarations is different in ANSI C and non-ANSI C (ANSI C also accepts the earlier form, but then does not do type checking). As of edit 167, C-Kermit tries to take full advantage of ANSI C features, especially function prototyping. This removes many bugs introduced by differing data types used or returned by the same functions on different computers. ANSI C features are automatically enabled when the symbol __STDC__ is defined. Most ANSI C compilers, such as GNU CC and the new DEC C compiler define this symbol internally. On the downside, ANSI C compilation increases the administrative/bureaucratic burden, spewing out countless unneeded warnings about mismatched types, especially when we are dealing with signed and unsigned characters, requiring casts everywhere to shut up the mindless complaints -- there is no use for signed chars in Kermit (or probably anywhere else). Some compilers, mercifully, include a "treat all chars as unsigned" option, and when available it should be used -- not only to stop the warnings, but also to avoid unhelpful sign extension on high-bit characters. To force use of ANSI C prototypes, include -DCK_ANSIC on the cc command line. To disable the use of ANSI prototypes, include -DNOANSI. 9.1.3. Other Standards [ [267]Top ] [ [268]Contents ] [ [269]Section Contents ] [ [270]Subsection Contents ] [ [271]Next ] [ [272]Previous ] As the years go by, standards with-which-all-must-comply continue to pile up: AES, XPG2, XPG3, XPG4, FIPS 151-2, successive generations of POSIX, OSF/1, X/Open, Spec 1170, UNIX95, Open Group UNIX98, ISO/IEC 9945 parts 1-4, ISO 9899, 88Open, OS 99, Single Unix Specification (SUS, [273]IEEE 1003.1-2001, not to mention "mature standards" like V7, 4.2/4.3BSD, System V R3 and R4 (SVID2 and SVID3), 4.4BSD (the basis for BSDI, OpenBSD, NetBSD, FreeBSD, Mac OS X etc), /usr/group, plus assorted seismic pronouncements of the neverending series of ephemeral corporate consortia, not to mention the libc-vs-glibc turmoil in the Linux arena and who knows what else. None of these standards simplifies life for portable applications like C-Kermit -- each one is simply one more environment to support (or circumvent, as in many cases these standards do more harm than good by denying access to facilities we need, e.g. as noted in above in [274]9.1.1). 9.2. Library Issues [ [275]Top ] [ [276]Contents ] [ [277]Section Contents ] [ [278]Subsection Contents ] [ [279]Next ] [ [280]Previous ] On most modern platforms, applications are -- and often must be -- dynamically linked. This has numerous advantages (smaller executables, ability to patch a library and thereby patch all applications that use it, etc), but also causes some headaches: most commonly, the library ID built into the executable at link time does not match the ID of the corresponding library on the target system, and so the loader refuses to let the application run. This problem only gets worse over time. In the Linux and *BSD world, we also have totally different libraries (each with their own names and numbering systems) that cover the same territory; for example, curses vs ncurses, libc versus glibc. Combinations proliferate and any given Unix computer might have any combination. For this reason it is becoming increasingly difficult to produce a "Linux binary" for a given architecture (e.g. PC or Alpha). There has to be a separate binary for (at least) every combination of curses vs ncurses and libc vs glibc. In such cases, the best advice is for every user to build C-Kermit from source code on the system where it will run. Too bad most commercial Unix vendors have stopped including C compilers with the operating system! 9.3. Unix File System Peculiarities [ [281]Top ] [ [282]Contents ] [ [283]Section Contents ] [ [284]Next ] [ [285]Previous ] Normally, including a BSD, System-V, POSIX, or DIRENT flag in the make entry selects the right file system code. But some versions of Unix are inconsistent in this regard, and building in the normal way either gives compiler or linker errors, or results in problems at runtime, typically failure to properly expand wildcard file specifications when you do something like "send *.*", or failure to recognize long filenames, as in "send filewithaveryveryveryveryverylongname". C-Kermit is supposed to know about all the various styles of Unix file systems, but it has to be told which one to use when you build it, usually in the makefile target CFLAGS as shown below, but you might also have to add something like -I/usr/include/bsd to CFLAGS, or something like -lbsd to LIBS. C-Kermit gives you the following CFLAGS switches to adapt to your file system's peculiarities: -DDIRENT - #include -DSDIRENT - #include -DNDIR - #include -DXNDIR - #include -DRTU - #include "/usr/lib/ndir.h", only if NDIR and XNDIR not defined. -DSYSUTIMH - #include for setting file creation dates. -DUTIMEH - #include for setting file creation dates. (Note, RTU should only be used for Masscomp RTU systems, because it also selects certain other RTU-specific features.) If none of these is defined, then is used. IMPORTANT: If your system has the file /usr/include/dirent.h then be sure to add -DDIRENT to your makefile target's CFLAGS. "dirent" should be used in preference to any of the others, because it supports all the features of your file system, and the others probably don't. Having selected the appropriate directory header file, you might also need to tell Kermit how to declare the routines and variables it needs to read the directory. This happens most commonly on AT&T System-V based UNIXes, particularly System V R3 and earlier, that provide long file and directory names (longer than 14 characters). Examples include certain releases of HP-UX, DIAB DNIX, older versions of Silicon Graphics IRIX, and perhaps also MIPS. In this case, try adding -DLONGFN to your makefile target. Another problem child is . Most Unix C-Kermit versions need to #include this file from within [286]ckufio.c and [287]ckutio.c, but some not only do not need to include it, but MUST not include it because (a) it doesn't exist, or (b) it has already been included by some other header file and it doesn't protect itself against multiple inclusion, or (c) some other reason that prevents successful compilation. If you have compilation problems that seem to stem from including this file, then add the following switch to CFLAGS in your makefile target: -DNOFILEH There are a few odd cases where must be included in one of the cku[ft]io.c files, but not the other. In that case, add the aforementioned switch, but go into the file that needs and add something like this: #ifdef XXX /* (where XXX is a symbol unique to your system) */ #undef NOFILEH #endif /* XXX */ before the section that includes . Kermit's SEND command expands wildcard characters "?" and "*" itself. Before version 5A, commands like "send *" would send all regular (non-directory) files, including "hidden files" (whose names start with "."). In version 5A, the default behavior is to match like the Bourne shell or the ls command, and not include files whose names start with dot. Such files can still be sent if the dot is included explicitly in the SEND command: "send .oofa, send .*". To change back to the old way and let leading wildcard characters match dot files, include the following in your CFLAGS: -DMATCHDOT (In C-Kermit 6.0, there is also a command to control this at runtime.) Complaints about data-type mismatches: * If you get compile-time complaints about data type mismatches for process-ID related functions like getpid(), add -DPID_T=pid_t. * If you get compile-time complaints about data type mismatches for user ID related functions like getuid(), add -DUID_T=uid_t. * If you get compile-time complaints about data type mismatches for user-ID related functions like getgid(), add -DGID_T=gid_t. * If you get compile-time complaints about data type mismatches for getpwuid(), add -DPWID_T=uid_t (or whatever it should be). File creation dates: C-Kermit attempts to set the creation date/time of an incoming file according to the date/time given in the file's attribute packet, if any. If you find that the dates are set incorrectly, you might need to build Kermit with the -DSYSUTIMEH flag, to tell it to include . If that doesn't help, look at the code in zstrdt() in [288]ckufio.c. 9.4. Hardware Flow Control [ [289]Top ] [ [290]Contents ] [ [291]Section Contents ] [ [292]Next ] [ [293]Previous ] Hardware flow control is a problematic concept in many popular Unix implementations. Often it is lacking altogether, and when available, the application program interface (API) to it is inconsistent from system to system. Here are some examples: a. POSIX does not support hardware flow control. b. RTS/CTS flow control support MIGHT be available for System V R3 and later if /usr/include/termiox.h exists (its successful operation also depends on the device driver, and the device itself, not to mention the [294]cable, etc, actually supporting it). If your SVR3-or-later Unix system does have this file, add: -DTERMIOX to your CFLAGS. If the file is in /usr/include/sys instead, add: -DSTERMIOX Note that the presence of this file does not guarantee that RTS/CTS will actually work -- that depends on the device-driver implementation (reportedly, many Unix versions treat hardware-flow-control related ioctl's as no-ops). c. Search ("grep -i") through /usr/include/*.h and /usr/include/sys/*.h for RTS or CTS and see what turns up. For example, in SunOS 4.x we find "CRTSCTS". Figuring out how to use it is another question entirely! In IBM AIX RS/6000 3.x, we have to "add" a new "line discipline" (and you won't find uppercase RTS or CTS symbols in the header files). d. NeXTSTEP and IRIX, and possibly others, support hardware flow control, but do not furnish an API to control it, and thus on these systems Kermit has no command to select it -- instead, a special device name must be used. (NeXTSTEP: /dev/cufa instead of /dev/cua; IRIX: /dev/ttyf00) See the routine tthflow() in [295]ckutio.c for details. If you find that your system offers hardware flow control selection under program control, you can add this capability to C-Kermit as follows: a. See if it agrees with one of the methods already used in tthflow(). if not, add new code, appropriately #ifdef'd. b. Add -DCK_RTSCTS to the compiler CFLAGS in your makefile target or define this symbol within the appropriate #ifdefs in [296]ckcdeb.h. To illustrate the difficulties with RTS/CTS, here is a tale from Jamie Watson , who added the RTS/CTS code for the RS/6000, about his attempts to do the same for DEC ULTRIX: "The number and type of hardware signals available to/from a serial port vary between different machines and different types of serial interfaces on each machine. This means that, for example, there are virtually no hardware signals in or out available on the DECsystem 3000/3100 series; on the DECsystem 5000/2xx series all modem signals in/out are present on both built-in serial ports; on the DECsystem 5100 some ports have all signals and some only have some; and so on... It looks to me as if this pretty well rules out any attempt to use hardware flow control on these platforms, even if we could figure out how to do it. The confusion on the user level about whether or not it should work for any given platform or port would be tremendous. And then it isn't clear how to use the hardware signals even in the cases where the device supports them." 9.5. Terminal Speeds [ [297]Top ] [ [298]Contents ] [ [299]Section Contents ] [ [300]Next ] [ [301]Previous ] The allowable speeds for the SET SPEED command are defined in [302]ckcdeb.h. If your system supports speeds that are not listed in "set speed ?", you can add definitions for them to ckcdeb.h. Then if the speed you are adding is one that was never used before in Kermit, such as 921600, you'll also need to add the appropriate keywords to spdtab[] in [303]ckuus3.c, and the corresponding case to ttsspd() in [304]ckutio.c. 9.6. Millisecond Sleeps [ [305]Top ] [ [306]Contents ] [ [307]Section Contents ] [ [308]Next ] [ [309]Previous ] There is no standard for millisecond sleeps, but at least five different functions have appeared in various Unix versions that can be used for this purpose: nap() (mostly in System V), usleep() (found at least in SunOS and NeXT OS), select() (found in 4.2BSD and later, and part of any TCP/IP sockets library), nanosleep(), and sginap(). If you have any of these available, pick one (in this order of preference, if you have more than one): -DSELECT: Include this in CFLAGS if your system has the select() function. -DNAP: Include this in CFLAGS if your system has the nap() function. -USLEEP: Include this in CFLAGS if your system has the usleep() function. NOTE: The nap() function is assumed to be a function that puts the process to sleep for the given number of milliseconds. If your system's nap() function does something else or uses some other units of time (like the NCR Tower 32, which uses clock-ticks), do not include -DNAP. Reportedly, all versions of System V R4 for Intel-based computers, and possibly also SVR3.2, include nap() as a kernel call, but it's not in the library. To include code to use it via syscall(3112,x), without having to include Xenix compatibility features, include the following compile-time option: -DNAPHACK 9.7. Nondestructive Input Buffer Peeking [ [310]Top ] [ [311]Contents ] [ [312]Section Contents ] [ [313]Next ] [ [314]Previous ] Some AT&T Unix versions have no way to check if input is waiting on a tty device, but this is a very important feature for Kermit. Without it, sliding windows might not work very well (or at all), and you also have to type your escape character to get Kermit's attention in order to interrupt a local-mode file transfer. If your system offers an FIONREAD ioctl, the build procedure should pick that up automatically and use it, which is ideal. If your system lacks FIONREAD but has a select() function, this can be used instead. If the build procedure fails to include it (SHOW FEATURES will list SELECT), then you can add it to your CFLAGS: -DSELECT Conversely, if the build procedure tries to use select() when it really is not there, add: -DNOSELECT Note: select() is not part of System V nor of POSIX, but it has been added to various System-V- and POSIX-based systems as an extension. Some System-V variations (SCO Xenix/UNIX/ODT and DIAB DNIX) include a rdchk() function that can be used for buffer peeking. It returns 0 if no characters are waiting and 1 if characters are waiting (but unlike FIONREAD, it does not tell the actual number). If your system has rdchk(), add: -DRDCHK: Include this in CFLAGS if your system has the rdchk() function. Otherwise, if your version of Unix has the poll() function (and the /usr/include/poll.h file) -- which appears to be a standard part of System V going back to at least SVR3, include: -DCK_POLL 9.8. Other System-Dependent Features [ [315]Top ] [ [316]Contents ] [ [317]Section Contents ] [ [318]Next ] [ [319]Previous ] Systems with might have the symbol IEXTEN defined. This is used to turn "extended features" in the tty device driver on and off, such as Ctrl-O to toggle output flushing, Ctrl-V to quote input characters, etc. In most Unix implementations, it should be turned off during Kermit operation, so if [320]ckutio.c finds this symbol, it uses it. This is necessary, at least, on BSDI. On some systems, however, IEXTEN is either misdefined or misimplemented. The symptom is that CR, when typed to the command processor, is echoed as LF, rather than CRLF. This happens (at least) on Convex/OS 9.1. The solution is to add the following symbol to the makefile target's CFLACS: -DNOIEXTEN However, in at least one Unix implementation, QNX 4.21, IEXTEN must be set before hardware flow control can be used. In edits 177 and earlier, workstation users noticed a "slow screen writing" phenomenon during interactive command parsing. This was traced to a setbuf() call in [321]ckutio.c that made console (stdout) writes unbuffered. This setbuf() call has been there forever, and could not be removed without some risk. Kermit's operation was tested on the NeXT in edit 178 with the setbuf() call removed, and the slow-writing symptom was cured, and everything else (command parsing, proper wakeup on ?, ESC, Ctrl-U, and other editing characters, terminal emulation, remote-mode and local-mode file transfer, etc) seemed to work as well as or better than before. In subsequent edits, this change was made to many other versions too, with no apparent ill effects. To remove the setbuf() call for your version of Kermit, add: -DNOSETBUF Later reports indicate that adding -DNOSETBUF has other beneficial effects, like cutting down on swapping when Kermit is run on workstations with small memories. But BEWARE: on certain small Unix systems, notably the AT&T 6300 and 3B1 (the very same ones that benefit from NOSETBUF), NOSETBUF seems to conflict with CK_CURSES. The program builds and runs OK, but after once using the curses display, echoing is messed up. In this case, we use a System-V specific variation in the curses code, using newterm() to prevent System V from altering the buffering. See makefile entries for AT&T 6300 and 3B1. The Unix version of C-Kermit includes code to switch to file descriptor zero (stdin) for remote-mode file transfer. This code is necessary to prevent Kermit from giving the impression that it is "idle" during file transfers, which, at some sites, can result in the job being logged out in the middle of an active file transfer by idle-job monitors. However, this feature can interfere with certain setups; for example, there is a package which substitutes a pty/tty pair for /dev/tty and sets file descriptor 0 to be read-only, preventing Kermit from sending packets. Or... When a Unix shell is invoked under the PICK environment, file descriptor 0 is inoperative. To remove this feature and allow Kermit to work in such environments, add the compile-time option: -DNOFDZERO On some versions of Unix, earlier releases of C-Kermit were reported to render a tty device unusable after a hangup operation. Examples include IBM AIX on the RT PC and RS/6000. A typical symptom of this phenomenon is that the DIAL command doesn't work, but CONNECTing to the device and dialing manually do work. A further test is to SET DIAL HANGUP OFF, which should make dialing work once by skipping the pre-dial hangup. However, after the connection is broken, it can't be used any more: subsequent attempts to DIAL the same device don't work. The cure is usually to close and reopen the device as part of the hangup operation. To do this, include the following compile-time option: -DCLSOPN Similarly, there is a section of code in ttopen(), which does another close(open()) to force the O_NDELAY mode change. On some systems, the close(open()) is required to make the mode change take effect, and apparently on most others it does no harm. But reportedly on at least one System V R4 implementation, and on SCO Xenix 3.2, the close(open()) operation hangs if the device lacks carrier, EVEN THOUGH the CLOCAL characteristic has just been set to avoid this very problem. If this happens to you, add this to your CFLAGS: -DNOCOTFMC or, equivalently, in your KFLAGS on the make command line. It stands for NO Close(Open()) To Force Mode Change. C-Kermit renames files when you give a RENAME command and also according to the current SET FILE COLLISION option when receiving files. The normal Unix way to rename a file is via two system calls: link() and unlink(). But this leaves open a window of vulnerability. Some Unix systems also offer an atomic rename(oldname,newname) function. If your version of Unix has this function, add the following to your CFLAGS: -DRENAME C-Kermit predefines the RENAME for several Unix versions in [322]ckcdeb.h (SVR4, SUNOS41, BSD44, AIXRS, etc). You can tell if rename() is being used if the SHOW FEATURES command includes RENAME in the compiler options list. If the predefined RENAME symbol causes trouble, then add NORENAME to your CFLAGS. Trouble includes: a. Linker complains that _rename is an unresolved symbol. b. Linking works, but Kermit's RENAME command doesn't work (which happens because older versions of rename() might have their arguments reversed). If rename() is not used, then Kermit uses link()/unlink(), which is equivalent except it is not atomic: there is a tiny interval in which some other process might "do something" to one of the files or links. Some Unix systems (Olivetti X/OS, Amdahl UTS/V, ICL SVR3, etc) define the S_ISREG and S_ISDIR macros incorrectly. This is compensated for automatically in [323]ckufio.c. Other systems might have this same problem. If you get a compile-time error message regarding S_ISREG and/or S_ISDIR, add the following to your CFLAGS: -DISDIRBUG Finally, here's a symbol you should NEVER define: -DCOMMENT It's used for commenting out blocks of code. If for some reason you find that your compiler has COMMENT defined, then add -UCOMMENT to CFLAGS or KFLAGS! Similarly, some header files have been known to define COMMENT, in which case you must add "#undef COMMENT" to each C-Kermit source module, after all the #includes. 9.9. Terminal Interruption [ [324]Top ] [ [325]Contents ] [ [326]Section Contents ] [ [327]Next ] [ [328]Previous ] When C-Kermit enters interactive command mode, it sets a Control-C (terminal keyboard interrupt = SIGINT) trap to allow it to return to the command prompt whenever the user types Control-C (or whatever is assigned to be the interrupt character). This is implemented using setjmp() and longjmp(). On some systems, depending on the machine architecture and C compiler and who knows what else, you might get "Memory fault (coredump)" or "longjmp botch" instead of the desired effect (this should not happen in 5A(190) and later). In that case, add -DNOCCTRAP to your CFLAGS and rebuild the program. Job control -- the ability to "suspend" C-Kermit on a Unix system by typing the "susp" character (normally Ctrl-Z) and then resume execution later (with the "fg" command) -- is a tricky business. C-Kermit must trap suspend signals so it can put the terminal back into normal mode when you suspend it (Kermit puts the terminal into various strange modes during interactive command parsing, CONNECT, and file transfer). Supporting code is compiled into C-Kermit automatically if includes a definition for the SIGTSTP signal. HOWEVER... some systems define this signal without supporting job control correctly. You can build Kermit to ignore SIGTSTP signals by including the -DNOJC option in CFLAGS. (You can also do this at runtime by giving the command SET SUSPEND OFF.) NOTE: As of version 5A(190), C-Kermit makes another safety check. Even if job control is available in the operating system (according to the numerous checks made in congm()), it will still disable the catching of SIGTSTP signals if SIGTSTP was set to SIG_IGN at the time C-Kermit was started. System V R3 and earlier systems normally do not support job control. If you have an SVR3 system that does, include the following option in your CFLAGS: -DSVR3JC On systems that correctly implement POSIX signal handling, signals can be handled more reliably than in Bell, Berkeley, or AT&T Unixes. On systems (such as QNX) that are "strictly POSIX", POSIX signal handling *must* be used, otherwise no signal will work more than once. If you have POSIX-based system and you find that your version of Kermit responds to Ctrl-C (SIGINT) or Ctrl-Z (SIGTSTP) only once, then you should add the following option to your CFLAGS: -DCK_POSIX_SIG But be careful; some POSIX implementations, notably 4.4BSD, include POSIX signal handling symbols and functions as "stubs" only, which do nothing. Look in for sigsetjmp and siglongjmp and read the comments. 10. DIALING OUT AND COORDINATING WITH UUCP [ [329]Top ] [ [330]Contents ] [ [331]Next ] [ [332]Previous ] The short version (general): In order for C-Kermit to be able to dial out from your Unix computer, you need to give it the same owner, group, and permissions as your other dialout programs, such as cu, tip, minicom, uucp, seyon, etc. The short version for Linux only: Since Red Hat 7.2, about 2002, Linux does not leave the lockfile handling to each application, but instead provides an external application, /usr/sbin/lockdev, that all applications should invoke when they need to access a serial port; lockdev locks and unlocks the port without requiring the application to have privileges, since the privileges on the lockfile directory are assigned to lockdev. C-Kermit 8.0.211 and later support this method. But C-Kermit still needs to be able to open the port itself, and therefore if the port's permissions do not allow read/write access to the general public, the general rule must still be followed: in the most common case, it must be SETGID to the group uucp (explained below). If a pre-8.0.211 version of C-Kermit is to be installed for use with serial ports on any version of Linux, it must still be installed as described in the following sections. The long version: Make sure your dialout line is correctly configured for dialing out (as opposed to login). The method for doing this is different for each kind of Unix. Consult your system documentation for configuring lines for dialing out (for example, Sun SPARCstation IPC users should read the section "Setting up Modem Software" in the Desktop SPARC Sun System and Network Manager's Guide, or the Terminals and Modems section of the HP manual, "Configuring HP-UX for Peripherals" (e.g. /usr/sbin/sam => Peripheral Devices => Terminals and Modems => Add Modem). Unlike most other multiuser, multitasking operating systems, Unix allows multiple users to access the same serial device at the same time, even though there is no earthly reason why two users should do this. When they do, user A will read some of the incoming characters, and user B will read the others. In all likelihood, neither user will see them all. Furthermore, User B can hang up User A's call, and so one. Rather than change Unix to enforce exclusive access to serial devices such as ttys, when it might still have been possible, Unix developers opted for a "lock file" mechanism. Any process that wants to open a tty device should first check to see if a file of a certain name exists, and if so, not to open the device. If the file does not exist, the process creates the file and then opens the device. When the process closes the device, it destroys the lockfile. This procedure was originated for use with Unix's UUCP, CU, and TIP programs, and so these lockfiles are commonly called "UUCP lockfiles" (UUCP = Unix-to-Unix Copy Program). As you can imagine, this method is riddled with pitfalls: * If a process does not observe the prevailing lockfile convention, then it can interfere with other "polite" processes. And in fact, very few Unix applications or commands handle lockfiles at all; an original design goal of Unix was that "everything is a file", and countless utilities operate on files directly (by opening them) or indirectly through redirection of standard i/o, without creating or looking for lockfiles. * If a process crashes while it has the device open, the lockfile is left behind, preventing further processes from using the device. * Various versions of Unix use different names for the lockfiles, put them in different directories, with different owners and groups and permissions, and specify their contents differently. * On a given platform, the lockfile conventions may change from one Unix release to the next (for example, SunOS 4.0 to 4.1) or, in the case of Linux, across different distributions. * The same tty device might have more than one name, and most lockfile conventions don't allow for this. Similarly for symbolic links. In an attempt to address the problem of "stale" lockfiles, most UUCP implementations put the PID (Process ID) of the creating process in the lockfile. Thus, another process that wants to open the corresponding device can check not only for the lockfile itself, but also can check the PID for validity. But this doesn't work well either: * PIDs are stored in diverse formats that change with every new release (short, integer, long, or string in any of various formats). If the reading program does not follow the same convention as the writing program, it can diagnose a valid PID to be invalid, and therefore not honor the lock. * PIDs recycle. If the lockfile was created by PID 1234, which later crashed without removing the lockfile, and then a new process 1234 exists a the time the lockfile is checked, the lockfile will be improperly taken as valid, and access to the device denied unnecessarily. Several techniques address the problem of multiple names for the same device: * Multiple lockfiles. For example, if the user opens a device through a symlink, a lockfile is created for both the symlink name and the true name (obtained from readlink()). However, when multiple drivers are installed for the same device (e.g. /dev/cua, /dev/cufa, etc), this approach won't work unless all applications *know* all the different names for the same device and make lockfiles for all of them, which is obviously not practical. * Lockfiles whose names are not based on the device name. These lockfiles generally have names like LK.inode/major/minor, where inode, major, and minor are numbers, which will always be the same for any physical device, no matter what its name. This form of lockfile is used in System V R4 and its derivatives, such as Solaris, UnixWare, etc. If lockfiles must be used (as opposed to, say, kernel-based locks), this would seem to be the most effective form. Most versions of Unix were not designed to accommodate third-party communications software; thus vendors of these Unix products feel no compunction about changing lockfile conventions from release to release, since they also change their versions of the cu, uucp, tip, etc, programs at the same time to match. And since the source code to these programs might not be published, it is difficult for makers of third-party products like C-Kermit to find out what the new conventions are. It also forces release of new versions of C-Kermit whenever the OS vendor makes a change like this. Some Unix vendors have taken a small step to simplify communications application development for their products: the inclusion of lockfile routines in the standard system C runtime libraries to shield the application from the details of lockfile management (IBM AIX is an example). When such routines are used, communications applications do not need modification when lockfile conventions change (although they will need recompiling if the routines are statically linked into the application). In the AIX example, the simple function calls ttylock(), ttyunlock(), and ttylocked() replace hundreds of lines of ugly code in C-Kermit that attempts to keep pace with every release of every Unix product over the last 20 years. Inclusion of ttylock() code occurs when: -DUSETTYLOCK is included in the CFLAGS. If such routines are available, they should be used. The rest of this section applies when they are not. To fit in with UUCP and other Unix-based serial-port communication software, C-Kermit must have the same idea as your system's uucp, cu, and tip programs about what the UUCP lock directory is called, what the lockfile itself is called, and what its contents should be. In most cases, C-Kermit preprocessor flags create the appropriate configuration at compile time if the appropriate makefile target was used (see [333]ckutio.c). The following CFLAGS options can be used to override the built-in configuration: -DLCKDIR Tells Kermit that the UUCP lock directory is /usr/spool/uucp/LCK. -DACUCNTRL Tells Kermit to use the BSD 4.3 acucntrl() program to turn off getty (login) on the line before using it, and restore getty when done. -DHDBUUCP Include this if your system uses Honey DanBer UUCP, in which the lockfile directory and format are relatively standardized. -DLOCK_DIR=\\\"/xxx/yyy\\\" Gives the lock directory name explicitly. The triple quoting is necessary. For example: CFLAGS= -DBSD4 -DLOCK_DIR=\\\"/usr/local/locks\\\" -DNODEBUG (NOTE: The triple quoting assumes this is a "top-level" make entry, and not a make entry that calls another one.) -DLFDEVNO The lockfile name uses the tty device inode and major and minor numbers: LK.dev.maj.min, as in Sys V R4, e.g. LK.035.044.008. When the LK.inode.major.minor form is used, a single lockfile is enough. Otherwise, a single lockfile rarely suffices. For example, in Linux, it is common to have a /dev/modem symbolic link to an actual dialout device, like /dev/cua0 or /dev/ttyS0, whose purpose is to hide the details of the actual driver from the user. So if one user opens /dev/modem, a lockfile called LCK..modem is created, which does not prevent another user from simultaneously opening the same device by its real name. On SCO Unix platforms, we have a slightly different problem: the same device is, by convention, known by "lowercase" and "uppercase" names, depending on whether it has modem control. So by convention, communications programs are supposed to create the lockfiles based on the lowercase name. But some programs don't follow this convention. In HP-UX, we have several different names for each serial device. And so on. For this reason, on platforms where the LK.inode.major.minor form is not used, C-Kermit also creates a secondary lockfile (which is simply a link to the first) if: a. The given device name is a symbolic link. The secondary link is based on the device's real name. b. On SCO: The device name is not a symbolic link, but it contains uppercase letters. The primary link is based on the lowercase name; the secondary link is based on the name that was given. c. On HP-UX: The device name starts with "cu". The primary link is based on the name that was given; the secondary link is based on the corresponding "ttyd" device, e.g. "LCK..cua0p0" and "LCK..ttyd0p0". NOTE: symlinks are not handled in HP-UX. Honey DanBer (HDB) UUCP, the basis of many UUCP implementations, has two characteristics: a. Lockfiles are kept in /usr/spool/locks/ (usually). b. A lockfile contains the process id (pid) in ASCII, rather than as an int. Non-HDB selections assume the lockfile contains the pid in int form (or, more precisely, in PID_T form, where PID_T is either int or pid_t, depending on your system's C library and header files). (b), by the way, is subject to interpretation: the numeric ASCII string may or may not be terminated by a newline, it may or may not have leading spaces (or zeros), and the number of leading spaces or zeros can differ, and the differences can be significant. Even if you build the program with the right lockfile option, you can still have problems when you try to open the device. Here are the error messages you can get from SET LINE, and what they mean: a. "Timed out, no carrier." This one is not related to lockfiles. It means that you have SET CARRIER ON xx, where xx is the number of seconds to wait for carrier, and carrier did not appear within xx seconds. Solution: SET CARRIER AUTO or OFF. b. "Sorry, access to lock denied." Kermit has been configured to use lockfiles, but (a)the lockfile directory is write-protected against you, or (b) it does not exist. The "access to lock denied" message will tell you the reason. If the directory does not exist, check to make sure Kermit is using the right name. Just because version n of your Unix used a certain lockfile directory is no guarantee that version n.1 does not use a different one. Workaround: ask the system administrator to install a symbolic link from the old name to the new name. Other solutions: (see below) c. "Sorry, access to tty device denied." The tty device that you specified in your SET LINE command is read/write protected against you. Solution: (see below) d. "Sorry, device is in use." The tty device you have specified is currently being used by another user. A prefatory message gives you an "ls -l" listing of the lockfile, which should show the username of the person who created it, plus a message "pid = nnn" to show you the process id of the user's program. Solutions: try another device, wait until the other user is finished, ask the other user to hurry up, or ask the system manager for help. e. "Sorry, can't open connection: reason". The device cannot be opened for some other reason, which is listed. f. "sh: /usr/lib/uucp/acucntrl: not found". This means your Kermit program was built with the -DACUCNTRL switch, but your computer system does not have the BSD 4.3 acucntrl program. Solution: install the acucntrl program if you have it, or rebuild Kermit without the -DACUCNTRL switch. There are two solutions for problems (b) and (c), both of which involve intervention by your Unix system administrator (superuser): a. Have the superuser change the permission of the lockfile directory and to the tty devices so that everyone on the system has read/write permission. su% chmod 777 /usr/spool/locks (or whatever the path is) su% chmod 666 /dev/ttyXX One risk here is that people can write lots of junk into the lockfile directory, delete other people's files in the lockfile directory, and intercept other people's data as it goes in and out of the tty device. The major danger here would be intercepting a privileged password. Of course, any user could write a short, ordinary, unprivileged program to do exactly the same thing if the tty device was world read/writeable. The other risk as that telephone calls are not controlled -- anybody on your system can make them, without having to belong to any particular group, and this could run up your phone bill. b. Use groups to regulate access. Normally the lockfile directory and and the dialout devices will have the same group (such as uucp). If so, then put everybody who's allowed to dial out into that group, and make sure that the lockfile directory and the tty devices have group read AND write permission. Example: su% chmod 770 /usr/spool/locks (or whatever the path is) su% chmod 660 /dev/ttyXX User whatever tool is available on your platform to add users to the appropriate group (e.g. edit the /etc/group file). c. Have the superuser change Kermit to run setuid and/or setgid to the owner and/or group of the lockfile directory and the tty devices if necessary), typically uucp (see [334]next section), but NOT root. Example: su% chown uucp kermit - or - chgrp uucp kermit su% chmod u+s kermit (setuid) - or - chmod g+s kermit (setgid) and then make sure the lockfile directory, and the tty devices, have owner (setuid) and/or group (setgid) write permission. For example: su% chmod o+rwx /usr/spool/uucp su% chown uucp /dev/ttyXX ; chmod 600 /dev/ttyXX In some cases, the owner and group must be distinct; the key point is that read/write access is required to both the UUCP lockfile directory and the tty itself. If you make C-Kermit setuid or setgid to root, it refuses to run: Fatal: C-Kermit setuid to root! Example: crw-r----- 1 uucp uucp 5, 67 Feb 11 06:23 /dev/cua3 drwxrwxr-x 3 root uucp 1024 Feb 11 06:22 /var/lock requires suid uucp to get read/write access on /dev/cua3 and sgid to get read/write access on /var/lock (since you can't set Kermit's uid or gid to root). The reason Kermit can't be setuid or setgid to root has to do with the fact that some Unix OS's can't switch user or group IDs in that case. Unfortunately, the prohibition against making Kermit setuid or setgid to root means that Unix C-Kermit can't be used to make rlogin connections by non-root users. (The rlogin port is privileged, which is why the regular rlogin command is setuid root -- which is safe because the rlogin program never has to create or access files like Kermit does.) For the lockfile mechanism to achieve its desired purpose -- prevention of access to the same tty device by more than one process at a time -- ALL programs on a given computer that open, read or write, and close tty devices must use the SAME lockfile conventions. Unfortunately, this is often not the case. Here is a typical example of how this can go wrong: In SunOS 4.0 and earler, the lockfile directory was /usr/spool/uucp; in 4.1 it was changed to /var/spool/locks in the quest for political correctness. Consequently, any third-party programs (such as C-Kermit) that were not modified to account for this change, recompiled, and reinstalled, did not use the same lockfiles as uucp, tip, etc, and so the entire purpose of the lockfile is defeated. What if your Unix system does not have UUCP installed? For example, you have a Unix workstation, and you do not use uucp, cu, or tip, or UUCP was not even supplied with your version of Unix (QNX is an example). In this case, you have two choices: a. If there may be more than one person running Kermit at the same time, competing for the same tty device, then create a special lockfile directory just for Kermit, for example, /usr/spool/kermit, and make sure you have read/write access to it. Then add the following to your makefile target CFLAGS, as shown earlier: -DLOCK_DIR=\\\"/usr/spool/kermit\\\" b. If you are the only user on your workstation, and no other processes will ever be competing with Kermit for the dialout tty device, then add -DNOUUCP to your makefile target's CFLAGS and rebuild Kermit. 11. RUNNING UNIX C-KERMIT SETUID OR SETGID [ [335]Top ] [ [336]Contents ] [ [337]Next ] [ [338]Previous ] Even if you don't intend to run C-Kermit setuid, somebody else might come along and chown and chmod it after it has been built. You should be sure that it is built correctly to run setuid on your system. For POSIX and AT&T Unix based versions, you don't have to do anything special. For 4.2 and 4.3 BSD-based Unix versions, you normally need not add anything special to the makefile. The program assumes that the setreuid() and setregid() functions are available, without which we cannot switch back and forth between real and effective uids. If "make" complains that _setreuid or _setregid is/are not defined, add -DNOSETREU to CFLAGS. In this case it is very likely (but not certain) that you cannot protect ttys and lockfiles against people and have them run Kermit setuid. If make does not complain about this, you should find out whether your BSD version (4.3 or other systems like SunOS 4.x that claim to include BSD 4.3 compatibility) includes the saved-setuid feature (see long notes under edit 146 in ckc178.upd). If it does, then add -DSAVEDUID to CFLAGS. IMPORTANT NOTE: Most Unix system documentation will not give you the required information. To determine whether your Unix system supplies the saved-original-effective-user/group-id feature, use the ckuuid.c program. Read and follow the instructions in the comments at the beginning. C-Kermit for 4.4BSD-based systems automatically use sete[ug]id(). See [339]ckutio.c. If you have a version of Unix that is not BSD-based, but which supplies the setreuid() and setregid() functions, and these are the only way to switch between real and effective uid, add -DSETREUID to your makefile target. WARNING: There are two calls to access() in [340]ckufio.c, by which Kermit checks to see if it can create an output file. These calls will not work correctly when (a)you have installed C-Kermit setuid or setgid on a BSD-based Unix system, and (b) the saved-original-effective-uid/gid feature is not present, and (c) the access() function always checks what it believes to be the real ID rather than the effective ID. This is the case, for example, in Olivetti X/OS and in NeXTSTEP. In such cases, you can force correct operation of access() calls by defining the symbol SW_ACC_ID at compile time in CFLAGS. If you have a version of Unix that does not allow a process to switch back and forth between its effective and real user and group ids multiple times, you probably should not attempt to run Kermit setuid, because once having given up its effective uid or gid (which it must do in order to transfer files, fork a shell, etc) it can never get it back, and so it can not use the original effective uid or gid to create or delete uucp lockfiles. In this case, you'll either have to set the permissions on your lockfile directory to make them publicly read/writable, or dispense with locking altogether. MORAL: Are you thoroughly sickened and/or frightened by all that you have just read? You should be. What is the real answer? Simple. Serial devices -- such as ttys and magnetic tapes -- in Unix should be opened with exclusive access only, enforced by the Unix kernel. Shared access has no conceivable purpose, legitimate or otherwise, except by privileged system programs such as getty. The original design dates from the late 1960s, when Unix was developed for laboratory use under a philosophy of trust by people within shouting distance of each other -- but even then, no useful purpose was served by this particular form of openness; it was probably more of a political statement. Since the emergence of Unix from the laboratory into the commercial market, we have seen every vestige of openness -- but this one -- stripped away. I'd like to see some influential Unix maker take the bold step of making the simple kernel change required to enforce exclusive access to serial devices. (Well, perhaps not so simple when bidirectionality must also be a goal -- but then other OS's like VMS solved this problem decades ago.) 12. CONFIGURING UNIX WORKSTATIONS [ [341]Top ] [ [342]Contents ] [ [343]Next ] [ [344]Previous ] On desktop workstations that are used by only the user at the console keyboard, C-Kermit is always used in local mode. But as delivered, C-Kermit runs in remote mode by default. To put it in local mode at startup, you can put a SET LINE command in your .mykermrc. You can also build C-Kermit to start up in local mode by default. To do this, include the following in the CFLAGS in your makefile target: -DDFTTY=\\\"/dev/ttyxx\\\" where ttyxx is the name of the device you will be using for communications. Presently there is no way of setting the default modem type at compile time, so use this option only for direct lines. C-Kermit does not work well on certain workstations if it is not run from within a terminal window. For example, you cannot start C-Kermit on a NeXT by launching it directly from NeXTstep. Similarly for Sun workstations in the Open Windows environment. Run Kermit in a terminal window. 13. BIZARRE BEHAVIOR AT RUNTIME [ [345]Top ] [ [346]Contents ] [ [347]Next ] [ [348]Previous ] See the "beware file", [349]ckubwr.txt, for hints about runtime misbehavior. This section lists some runtime problems that can be cured by rebuilding C-Kermit. The program starts, but there is no prompt, and certain operations don't work (you see error messages like "Kermit command error in background execution"). This is because Kermit thinks it is running in the background. See conbgt() in [350]ckutio.c. Try rebuilding Kermit with: -DPID_T=pid_t added to your CFLAGS. If that doesn't help, find out the actual data type for pids (look in types.h or similar file) and use it in place of "pid_t", for example: -DPID_T=short Unexplainable and inappropriate error messages ("Sockets not supported on this device", etc) have been traced in at least one case to a lack of agreement between the system header files and the actual kernel. This happened because the GNU C compiler (gcc) was being used. gcc wants to have ANSI-C-compliant header files, and so part of the installation procedure for gcc is (or was) to run a shell script called "fixincludes", which translates the system's header files into a separate set of headers that gcc likes. So far so good. Later, a new version of the operating system is installed and nobody remembers to run fixincludes again. From that point, any program compiled with gcc that makes use of header files (particularly ioctl.h) is very likely to misbehave. Solution: run fixincludes again, or use your system's regular C compiler, libraries, and header files instead of gcc. 14. CRASHES AND CORE DUMPS [ [351]Top ] [ [352]Contents ] [ [353]Next ] [ [354]Previous ] If C-Kermit consistently dumps core at the beginning of a file transfer, look in SHOW FEATURES for CKREALPATH. If found, rebuild with -DNOREALPATH and see if that fixes the problem (some UNIXes have realpath() but it doesn't work). Total failure of the Kermit program can occur because of bad memory references, bad system calls, or problems with dynamic memory allocation. First, try to reproduce the problem with debugging turned on: run Kermit with the -d command-line option (for example, "wermit -d") and then examine the resulting debug.log file. The last entry should be in the vicinity of the crash. In VMS, a crash automatically produces a "stack dump" which shows the routine where the crash occurs. In some versions of Unix, you can get a stack dump with "adb" -- just type "adb wermit core" and then give the command "$c", then Ctrl-D to quit (note: replace "wermit" by "kermit" or by the full pathname of the executable that crashed if it is not in the current directory). Or use gdb to get a backtrace, etc. In edit 186, one implementation, UNISYS 5000/95 built with "make sys5r3", has been reported to run out of memory very quickly (e.g. while executing a short initialization file that contains a SET DIAL DIRECTORY command). Debug logs show that malloc calls are failing, reason unknown. For this and any other implementation that gives error messages about "malloc failure" or "memory allocation failure", rebuild the program *without* the -DDYNAMIC CFLAGS definition, for example: make sys5r3 KFLAGS=-UDYNAMIC As of edit 169, C-Kermit includes a malloc() debugging package which you may link with the Kermit program to catch runtime malloc errors. See the makefile entries for sunos41md and nextmd for examples of how to select malloc debugging. Once you have linked Kermit with the malloc debugger, it will halt with an informative message if a malloc-related error occurs and, if possible, dump core. For this reason, malloc-debugging versions of Kermit should be built without the "-s" link option (which removes symbols, preventing analysis of the core dump). You have several ways to track down the malloc error: Analyze the core dump with adb. Or reproduce the problem with "log debug" and then look at the code around the last debug.log entry. If you have gcc, build the program with "-g" added to CFLAGS and then debug it with gdb, e.g. gdb wermit break main run .. set other breakpoints or watchpoints continue Watchpoints are especially useful for finding memory leaks, but they make the program run about a thousand times slower than usual, so don't set them until the last possible moment. When a watchpoint is hit, you can use the "where" command to find out which C-Kermit source statement triggered it. If you have the Pure Software Inc "Purify" product, see the sunos41cp makefile entry for an example of how to use it to debug C-Kermit. 15. SYSLOGGING [ [355]Top ] [ [356]Contents ] [ [357]Next ] [ [358]Previous ] "Syslogging" means recording selected information in the system log via the Unix syslog() facility, which is available in most Unix versions. Syslogging is not done unless C-Kermit is started with: --syslog:n on the command-line, where n is a number greater than 0 to indicate the level of syslogging. See [359]Section 4.2 of the [360]IKSD Administrator's Guide for details. Obviously you can't depend on users to include --syslog:3 (or whatever) on the command line every time they start C-Kermit, so if you want certain kinds of records to be recorded in the system log, you can build C-Kermit with forced syslogging at the desired level; for example, to record logins and dialouts: make linux KFLAGS=-DSYSLOGLEVEL=2 Levels 2 and 3 are the most likely candidates for this treatment. Level 2 forces logging of all successful dialout calls (e.g. for checking against or phone bills), and level 3 records all connections (SET LINE or SET HOST / TELNET / RLOGIN, etc) so you can see who is connecting out from your system, and to where, e.g. for security auditing. Level 2 and 3 records are equivalent to those in the connection log; see the [361]C-Kermit 7.0 Supplement) for a detailed description of the connection log. 16. BUILDING SECURE VERSIONS OF C-KERMIT 8.0 [ [362]Top ] [ [363]Contents ] [ [364]Next ] [ [365]Previous ] C-Kermit 7.0 and later may be built with Kerberos(TM) and/or SRP(TM) (Secure Remote Password) and/or SSL/TLS security for strong authentication and encryption of Internet connections. These security methods require external libraries that, in their binary forms, are restricted from export by USA law. See the [366]Kermit Security Reference) for details. C-Kermit binaries themselves are likewise restricted; the C-Kermit binaries that are available for public download on the Internet are not allowed to contain the security options. Sample makefile entries are provided for Linux and many other operating systems. A list of secure makefile entries is included in the Makefile. Complete instructions on building C-Kermit 8.0 with MIT Kerberos; Secure Remote Password; and/or OpenSSL can be found in the [367]Kermit Security Reference. SSL/TLS and Kerberos builds are increasingly problematic with the "deprecation" of DES. There is code to detect the presence or absence of DES in the OpenSSL builds, but it doesn't always work because sometimes the SSL libraries are present but routines are missing from them. * First of all remember that if your SSL and/or Kerberos header files and libraries are not in the default place, you'll need to override the assumed paths. To find out what the default places are type "make show", e.g.: [~/kermit] make show prefix=/usr/local srproot=/usr/local sslroot=/usr/local manroot=/usr/local K4LIB=-L/usr/kerberos/lib K4INC=-I/usr/kerberos/include K5LIB=-L/usr/kerberos/lib K5INC=-I/usr/kerberos/include SRPLIB=-L/usr/local/lib SRPINC=-I/usr/local/include SSLLIB=-L/usr/local/ssl/lib SSLINC=-I/usr/local/ssl/include [~/kermit] * You can override any or all of these by putting assignments on the 'make' command line; examples: make linux+krb5 \ "K5INC=-I/usr/include/" \ "K5LIB=-L/usr/lib64/" make solaris9g+ssl \ "SSLLIB=-L/opt/openssl-0.9.8q/lib" \ "SSLINC=-I/opt/openssl-0.9.8q/include" Or by setting and exporting environment variables prior to giving the 'make' command, as in this example in which (after Beta.01 was uploaded) C-Kermit was successfully linked with OpenSSL 1.0.0d, which was installed alongside OpenSSL 0.9.8r on the same computer. Note the use of the '-i' option instead of '-I' to force gcc to include the right header files (thanks to Nelson Beebe for this): export PATH=/usr/bin:$PATH export SSLINC=-isystem/usr/include export "SSLLIB=-L/usr/lib -Wl,-rpath,/usr/lib" make linux+ssl -i is explained in 'man gcc'; there is a change in what -I does that could have ramifications for many makefile targets, not just Kermit. And -Wl and -rpath are explained in 'man ld'; the idea is build a binary from which useful reports can be obtained with ldd. * Building with OpenSSL versions prior to 0.9.7 doesn't work, even though C-Kermit is designed to work with both the old and new versions. This could probably be fixed if anybody cares. * If a Kerberos or SSL build fails at link time because des_ecb3_encrypt, des_random_seed, and/or des_set_odd_parity come up missing, redo the build with -UCK_DES: make netbsd+krb5+ssl \ "K5INC=-I/usr/local/include" \ "K5LIB=-L/usr/local/kerblib" KFLAGS=-UCK_DES I suppose all the SSL and Kerberos targets could be recoded to figure this out automatically (i.e. that DES is installed but with some entry points missing), but it wouldn't be pretty. * Different Kerberos and OpenSSL distributions can be installed with different options; certain libraries might be missing or named differently (for example, libgssapi vs libgssapi_krb5). Some, but not all, of the C-Kermit makefile targets have been fixed to take some of these variations into account by testing for them, most notably the linux ones, linux+ssl, linux+krb5, and linux+krb5+ssl. Probably every target that builds with OpenSSL or Kerberos needs the same treatment but I won't have time. * Why doesn't C-Kermit just use Autoconf? Mainly because the makefile is full of targets for platforms that don't have Autoconf or any other tool like it. (Another reason is that I've always preferred that Kermit have the least dependencies possible on external toolsets.) Perhaps certain targets could be converted to use them, especially Linux because there are so many variations among distributions and versions. Anybody who wants to make, say, an Autoconf-based Linux target, be my guest, but bear in mind that one Linux target is supposed to work for all versions and distributions of Linux on all platforms. Well, one target for Linux by itself, another for Linux with OpenSSL, another for Linux with Kerberos 5, and another for Linux with Kerberos 5 and OpenSSL. Each of these is supposed to work on any Linux version with any version of Kerberos 5 or OpenSSL. Also note that Kerberos support is for the MIT version only, Heimdal and others are not supported (never have been). Of course anybody can pitch in and add or improve support for whatever they want. 17. INSTALLING C-KERMIT AS AN SSH SERVER SUBSYSTEM [ [368]Top ] [ [369]Contents ] [ [370]Previous ] This requires C-Kermit 8.0.206 or later and an SSH v2 server. If you list C-Kermit as a Subsystem in the SSH v2 server configuration file (as, for example, SFTP is listed), users can make SSH connections direct to a Kermit server as explained here: [371]http://www.columbia.edu/kermit/skermit.html The name and location of the SSH server configuration file depends on your platform, which SSH product(s) you have, etc. C-Kermit itself must be referred to in this file as "kermit-sshsub". On the host, install the C-Kermit 8.0.211 binary in the normal way. Then, in the same directory as the C-Kermit binary, make a symbolic link: ln -s kermit kermit-sshsub (Note: the "make install" makefile target does this for you.) Then in the sshd configuration file, add a line: Subsystem kermit /some/path/kermit-sshsub (where /some/path is the fully specified directory where the symlink is.) This is similar to the line that sets up the SFTP subsystem. Example: Subsystem sftp /usr/local/libexec/sftp-server Subsystem kermit /usr/local/bin/kermit-sshsub The mechanics might vary for other SSH servers; "man sshd" for details. The method shown here is used because the OpenSSH server does not permit the subsystem invocation to include command-line options. C-Kermit would have no way of knowing that it should enter Server mode if it were not called by a special name. [ [372]Top ] [ [373]Contents ] [ [374]C-Kermit Home ] [ [375]C-Kermit 9.0 Overview ] [ [376]Kermit Home ] __________________________________________________________________ C-Kermit 9.0 Unix Installation Instructions / The Kermit Project / Columbia University / 30 June 2011 References 1. http://www.columbia.edu/ 2. mailto:kermit@columbia.edu 3. http://www.columbia.edu/kermit/index.html 4. http://www.columbia.edu/kermit/k95.html 5. http://www.columbia.edu/kermit/ckermit.html 6. http://www.columbia.edu/kermit/ckscripts.html 7. http://www.columbia.edu/kermit/current.html 8. http://www.columbia.edu/kermit/whatsnew.html 9. http://www.columbia.edu/kermit/faq.html 10. http://www.columbia.edu/kermit/support.html 11. http://www.columbia.edu/kermit/ckuins.html#contents 12. http://www.columbia.edu/kermit/ckermit.html 13. http://www.columbia.edu/kermit/index.html 14. http://www.columbia.edu/kermit/ckuins.html 15. http://www.columbia.edu/kermit/ckuins.html#x0 16. http://www.columbia.edu/kermit/ckuins.html#x1 17. http://www.columbia.edu/kermit/ckuins.html#x2 18. http://www.columbia.edu/kermit/ckuins.html#x3 19. http://www.columbia.edu/kermit/ckuins.html#x4 20. http://www.columbia.edu/kermit/ckuins.html#x5 21. http://www.columbia.edu/kermit/ckuins.html#x6 22. http://www.columbia.edu/kermit/ckuins.html#x7 23. http://www.columbia.edu/kermit/ckuins.html#x8 24. http://www.columbia.edu/kermit/ckuins.html#x9 25. http://www.columbia.edu/kermit/ckuins.html#x10 26. http://www.columbia.edu/kermit/ckuins.html#x11 27. http://www.columbia.edu/kermit/ckuins.html#x12 28. http://www.columbia.edu/kermit/ckuins.html#x13 29. http://www.columbia.edu/kermit/ckuins.html#x14 30. http://www.columbia.edu/kermit/ckuins.html#x15 31. http://www.columbia.edu/kermit/ckuins.html#x16 32. http://www.columbia.edu/kermit/ckuins.html#x17 33. http://www.columbia.edu/kermit/ckuins.html#top 34. http://www.columbia.edu/kermit/ckuins.html#contents 35. http://www.columbia.edu/kermit/ckuins.html#x1 36. http://www.columbia.edu/kermit/ckccfg.html 37. http://www.columbia.edu/kermit/ckcbwr.html 38. http://www.columbia.edu/kermit/ckubwr.html 39. http://www.columbia.edu/kermit/ckcplm.html 40. http://www.columbia.edu/kermit/ckuins.html#x2 41. http://www.columbia.edu/kermit/ckuins.html#x3 42. http://www.columbia.edu/kermit/ckuins.html#x4 43. http://www.columbia.edu/kermit/ckuins.html#top 44. http://www.columbia.edu/kermit/ckuins.html#contents 45. http://www.columbia.edu/kermit/ckuins.html#x2 46. http://www.columbia.edu/kermit/ckuins.html#x0 47. ftp://www.columbia.edu/kermit/archives/cku211.tar.Z 48. ftp://www.columbia.edu/kermit/archives/cku211.tar.gz 49. http://kermit.columbia.edu/ftp/archives/cku211.tar.Z 50. http://kermit.columbia.edu/ftp/archives/cku211.tar.gz 51. ftp://www.columbia.edu/kermit/archives/cku211.tar 52. http://kermit.columbia.edu/ftp/archives/cku211.tar 53. http://www.columbia.edu/kermit/ckuins.html#x7 54. http://www.columbia.edu/kermit/ckuins.html#x5 55. http://www.columbia.edu/kermit/ckuins.html#x5 56. http://www.columbia.edu/kermit/ckuins.html#x16 57. http://www.columbia.edu/kermit/ckuins.html#top 58. http://www.columbia.edu/kermit/ckuins.html#contents 59. http://www.columbia.edu/kermit/ckuins.html#x3 60. http://www.columbia.edu/kermit/ckuins.html#x1 61. http://www.columbia.edu/kermit/ckuins.html#x5 62. http://www.columbia.edu/kermit/ckuins.html#X10 63. http://www.columbia.edu/kermit/ckuins.html#x11 64. http://www.columbia.edu/kermit/ckuins.html#x10 65. http://www.columbia.edu/kermit/ckuins.html#x3 66. http://www.columbia.edu/kermit/ck80packages.html 67. http://www.columbia.edu/kermit/ckuins.html#x10 68. http://www.columbia.edu/kermit/ckuins.html#top 69. http://www.columbia.edu/kermit/ckuins.html#contents 70. http://www.columbia.edu/kermit/ckuins.html#x4 71. http://www.columbia.edu/kermit/ckuins.html#x2 72. ftp://www.columbia.edu/kermit/bin/ 73. http://www.columbia.edu/kermit/ck80binaries.html 74. http://www.columbia.edu/kermit/ckuins.html#x7 75. http://www.columbia.edu/kermit/ckuins.html#build 76. http://www.columbia.edu/kermit/ckuins.html#x5 77. http://www.columbia.edu/kermit/ckuins.html#x4 78. http://www.columbia.edu/kermit/ckfaq.html#version 79. http://www.columbia.edu/kermit/ckuins.html#x4 80. mailto:kermit@columbia.edu 81. http://www.columbia.edu/kermit/ckuins.html#top 82. http://www.columbia.edu/kermit/ckuins.html#contents 83. http://www.columbia.edu/kermit/ckuins.html#x5 84. http://www.columbia.edu/kermit/ckuins.html#x3 85. http://www.columbia.edu/kermit/ckuins.html#x8 86. http://www.columbia.edu/kermit/ckuins.html#x9 87. ftp://kermit.columbia.edu/kermit/c-kermit/makefile 88. ftp://kermit.columbia.edu/kermit/c-kermit/ckpker.mk 89. ftp://kermit.columbia.edu/kermit/c-kermit/ckubsd.mak 90. http://www.columbia.edu/kermit/ckuins.html#x5 91. mailto:kermit-support@columbia.edu 92. ftp://kermit.columbia.edu/kermit/c-kermit/makefile 93. http://www.columbia.edu/kermit/ckuins.html#x7 94. mailto:kermit-support@columbia.edu 95. ftp://kermit.columbia.edu/kermit/c-kermit/makefile 96. http://www.columbia.edu/kermit/ckuins.html#x5.4 97. http://www.columbia.edu/kermit/ckuins.html#x10 98. http://www.columbia.edu/kermit/ckuins.html#x11 99. http://www.columbia.edu/kermit/ckuins.html#x5 100. http://www.columbia.edu/kermit/iksd.html 101. http://www.columbia.edu/kermit/ckuins.html#top 102. http://www.columbia.edu/kermit/ckuins.html#contents 103. http://www.columbia.edu/kermit/ckuins.html#x4.1 104. http://www.columbia.edu/kermit/ckccfg.html 105. http://www.columbia.edu/kermit/ckuins.html#x4.1 106. http://www.columbia.edu/kermit/ckuins.html#x4.2 107. http://www.columbia.edu/kermit/ckuins.html#x4.3 108. http://www.columbia.edu/kermit/ckuins.html#x4.4 109. http://www.columbia.edu/kermit/ckuins.html#x4.5 110. http://www.columbia.edu/kermit/ckccfg.html 111. http://www.columbia.edu/kermit/ckccfg.html#x8 112. http://www.columbia.edu/kermit/iksd.html 113. http://www.columbia.edu/kermit/iksd.html 114. ftp://kermit.columbia.edu/kermit/c-kermit/ckufio.c 115. ftp://kermit.columbia.edu/kermit/c-kermit/ckufio.c 116. mailto:kermit-support@columbia.edu 117. ftp://kermit.columbia.edu/kermit/c-kermit/ckcmai.c 118. http://www.columbia.edu/kermit/ckuins.html#x15 119. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c 120. ftp://kermit.columbia.edu/kermit/c-kermit/ckufio.c 121. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c 122. ftp://kermit.columbia.edu/kermit/c-kermit/ckufio.c 123. ftp://kermit.columbia.edu/kermit/c-kermit/ckcnet.c 124. ftp://kermit.columbia.edu/kermit/c-kermit/ckcnet.c 125. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c 126. ftp://kermit.columbia.edu/kermit/c-kermit/ckcuni.c 127. mailto:kermit-support@columbia.edu 128. http://www.columbia.edu/kermit/ckuins.html#top 129. http://www.columbia.edu/kermit/ckuins.html#contents 130. http://www.columbia.edu/kermit/ckuins.html#x4 131. http://www.columbia.edu/kermit/ckuins.html#x4.2 132. http://www.columbia.edu/kermit/ckuins.html#x4.0 133. ftp://kermit.columbia.edu/kermit/c-kermit/makefile 134. ftp://kermit.columbia.edu/kermit/c-kermit/ckubwr.txt 135. http://www.columbia.edu/kermit/ckubwr.html 136. ftp://kermit.columbia.edu/kermit/c-kermit/ckwart.c 137. ftp://kermit.columbia.edu/kermit/c-kermit/ckcpro.w 138. ftp://kermit.columbia.edu/kermit/c-kermit/ckcpro.c 139. http://www.columbia.edu/kermit/ckuins.html#top 140. http://www.columbia.edu/kermit/ckuins.html#contents 141. http://www.columbia.edu/kermit/ckuins.html#x4 142. http://www.columbia.edu/kermit/ckuins.html#x4.3 143. http://www.columbia.edu/kermit/ckuins.html#x4.1 144. http://www.columbia.edu/kermit/ckuins.html#x5 145. http://www.columbia.edu/kermit/ckuins.html#top 146. http://www.columbia.edu/kermit/ckuins.html#contents 147. http://www.columbia.edu/kermit/ckuins.html#x4 148. http://www.columbia.edu/kermit/ckuins.html#x4.4 149. http://www.columbia.edu/kermit/ckuins.html#x4.2 150. http://www.columbia.edu/kermit/ckuins.html#top 151. http://www.columbia.edu/kermit/ckuins.html#contents 152. http://www.columbia.edu/kermit/ckuins.html#x4 153. http://www.columbia.edu/kermit/ckuins.html#x4.5 154. http://www.columbia.edu/kermit/ckuins.html#x4.3 155. ftp://kermit.columbia.edu/kermit/c-kermit/ckpker.mk 156. http://www.columbia.edu/kermit/ckuins.html#top 157. http://www.columbia.edu/kermit/ckuins.html#contents 158. http://www.columbia.edu/kermit/ckuins.html#x4 159. http://www.columbia.edu/kermit/ckuins.html#x4.4 160. ftp://kermit.columbia.edu/kermit/c-kermit/makefile 161. ftp://kermit.columbia.edu/kermit/c-kermit/makefile 162. ftp://kermit.columbia.edu/kermit/c-kermit/ckcpro.w 163. http://www.columbia.edu/kermit/ckuins.html#top 164. http://www.columbia.edu/kermit/ckuins.html#contents 165. http://www.columbia.edu/kermit/ckuins.html#x6 166. http://www.columbia.edu/kermit/ckuins.html#x4 167. http://www.columbia.edu/kermit/ckuins.html#x5.1 168. http://www.columbia.edu/kermit/ckuins.html#x5.2 169. http://www.columbia.edu/kermit/ckuins.html#x5.3 170. http://www.columbia.edu/kermit/ckuins.html#x5.4 171. http://www.columbia.edu/kermit/ 172. http://www.columbia.edu/kermit/ckuins.html#x5.4 173. http://www.columbia.edu/kermit/ckuins.html#x5.3 174. ftp://kermit.columbia.edu/kermit/c-kermit/COPYING.TXT 175. ftp://kermit.columbia.edu/kermit/c-kermit/ckermit.ini 176. http://www.columbia.edu/kermit/ckuins.html#x5.1 177. ftp://kermit.columbia.edu/kermit/c-kermit/ckermod.ini 178. ftp://kermit.columbia.edu/kermit/c-kermit/ckermit70.txt 179. http://www.columbia.edu/kermit/ck60manual.html 180. http://www.columbia.edu/kermit/ckermit70.html 181. ftp://kermit.columbia.edu/kermit/c-kermit/ckermit80.txt 182. http://www.columbia.edu/kermit/ck60manual.html 183. http://www.columbia.edu/kermit/ckermit80.html 184. ftp://kermit.columbia.edu/kermit/c-kermit/ckermit90.txt 185. http://www.columbia.edu/kermit/ck60manual.html 186. http://www.columbia.edu/kermit/ckermit90.html 187. ftp://kermit.columbia.edu/kermit/c-kermit/ckcbwr.txt 188. http://www.columbia.edu/kermit/ckcbwr.html 189. ftp://kermit.columbia.edu/kermit/c-kermit/ckubwr.txt 190. http://www.columbia.edu/kermit/ckubwr.html 191. ftp://kermit.columbia.edu/kermit/c-kermit/ckuins.txt 192. http://www.columbia.edu/kermit/ckuins.html 193. ftp://kermit.columbia.edu/kermit/c-kermit/ckccfg.txt 194. http://www.columbia.edu/kermit/ckccfg.html 195. ftp://kermit.columbia.edu/kermit/c-kermit/ckcplm.txt 196. http://www.columbia.edu/kermit/ckcplm.html 197. ftp://kermit.columbia.edu/kermit/c-kermit/ca_certs.pem 198. http://www.columbia.edu/kermit/ckuins.html#x16" 199. ftp://kermit.columbia.edu/kermit/c-kermit/makefile 200. http://www.columbia.edu/kermit/ckuins.html#x? 201. http://www.columbia.edu/kermit/ckuins.html#x11 202. http://www.columbia.edu/kermit/ckuins.html#x5.2 203. http://www.columbia.edu/kermit/ckermit.html#download 204. http://www.columbia.edu/kermit/ckbinaries.html 205. http://www.columbia.edu/kermit/ckermit.html#download 206. http://www.columbia.edu/kermit/ckuins.html#top 207. http://www.columbia.edu/kermit/ckuins.html#contents 208. http://www.columbia.edu/kermit/ckuins.html#x7 209. http://www.columbia.edu/kermit/ckuins.html#x5 210. http://www.columbia.edu/kermit/ckuins.html#top 211. http://www.columbia.edu/kermit/ckuins.html#contents 212. http://www.columbia.edu/kermit/ckuins.html#x8 213. http://www.columbia.edu/kermit/ckuins.html#x6 214. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c 215. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c 216. http://www.columbia.edu/kermit/ckuins.html#x4.0 217. ftp://kermit.columbia.edu/kermit/c-kermit/ckcdeb.h 218. ftp://kermit.columbia.edu/kermit/c-kermit/ckufio.c 219. ftp://kermit.columbia.edu/kermit/c-kermit/ckufio.c 220. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c 221. http://www.columbia.edu/kermit/ckuins.html#x10 222. http://www.columbia.edu/kermit/ckccfg.html#x2 223. http://www.columbia.edu/kermit/ckccfg.html 224. http://www.columbia.edu/kermit/ckuins.html#x4 225. http://www.columbia.edu/kermit/ckuins.html#x10 226. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c 227. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c 228. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c 229. http://www.columbia.edu/kermit/ckuins.html#x9.4 230. mailto:kermit-support@columbia.edu 231. http://www.columbia.edu/kermit/ckuins.html#top 232. http://www.columbia.edu/kermit/ckuins.html#contents 233. http://www.columbia.edu/kermit/ckuins.html#x9 234. http://www.columbia.edu/kermit/ckuins.html#x7 235. http://www.columbia.edu/kermit/ckccfg.html 236. http://www.columbia.edu/kermit/ckccfg.html 237. http://www.columbia.edu/kermit/ckuins.html#top 238. http://www.columbia.edu/kermit/ckuins.html#contents 239. http://www.columbia.edu/kermit/ckuins.html#x10 240. http://www.columbia.edu/kermit/ckuins.html#x8 241. http://www.columbia.edu/kermit/ckuins.html#x9.1 242. http://www.columbia.edu/kermit/ckuins.html#x9.1.1 243. http://www.columbia.edu/kermit/ckuins.html#x9.1.2 244. http://www.columbia.edu/kermit/ckuins.html#x9.1.3 245. http://www.columbia.edu/kermit/ckuins.html#x9.2 246. http://www.columbia.edu/kermit/ckuins.html#x9.3 247. http://www.columbia.edu/kermit/ckuins.html#x9.4 248. http://www.columbia.edu/kermit/ckuins.html#x9.5 249. http://www.columbia.edu/kermit/ckuins.html#x9.6 250. http://www.columbia.edu/kermit/ckuins.html#x9.7 251. http://www.columbia.edu/kermit/ckuins.html#x9.8 252. http://www.columbia.edu/kermit/ckuins.html#x9.9 253. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c 254. http://www.columbia.edu/kermit/ckuins.html#top 255. http://www.columbia.edu/kermit/ckuins.html#x9 256. http://www.columbia.edu/kermit/ckuins.html#contents 257. http://www.columbia.edu/kermit/ckuins.html#x9.2 258. http://www.columbia.edu/kermit/ckuins.html#x9.1.1 259. http://www.columbia.edu/kermit/ckuins.html#x9.1.2 260. http://www.columbia.edu/kermit/ckuins.html#x9.1.3 261. http://www.columbia.edu/kermit/ckuins.html#top 262. http://www.columbia.edu/kermit/ckuins.html#contents 263. http://www.columbia.edu/kermit/ckuins.html#x9 264. http://www.columbia.edu/kermit/ckuins.html#x9.1 265. http://www.columbia.edu/kermit/ckuins.html#x9.1.3 266. http://www.columbia.edu/kermit/ckuins.html#x9.1.1 267. http://www.columbia.edu/kermit/ckuins.html#top 268. http://www.columbia.edu/kermit/ckuins.html#contents 269. http://www.columbia.edu/kermit/ckuins.html#x9 270. http://www.columbia.edu/kermit/ckuins.html#x9.1 271. http://www.columbia.edu/kermit/ckuins.html#x9.2 272. http://www.columbia.edu/kermit/ckuins.html#x9.1.2 273. http://www.opengroup.org/onlinepubs/007904975/ 274. http://www.columbia.edu/kermit/ckuins.html#x9.1.1 275. http://www.columbia.edu/kermit/ckuins.html#top 276. http://www.columbia.edu/kermit/ckuins.html#contents 277. http://www.columbia.edu/kermit/ckuins.html#x9 278. http://www.columbia.edu/kermit/ckuins.html#x9.1 279. http://www.columbia.edu/kermit/ckuins.html#x9.3 280. http://www.columbia.edu/kermit/ckuins.html#x9.1 281. http://www.columbia.edu/kermit/ckuins.html#top 282. http://www.columbia.edu/kermit/ckuins.html#contents 283. http://www.columbia.edu/kermit/ckuins.html#x9 284. http://www.columbia.edu/kermit/ckuins.html#x9.4 285. http://www.columbia.edu/kermit/ckuins.html#x9.2 286. ftp://kermit.columbia.edu/kermit/c-kermit/ckufio.c 287. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c 288. ftp://kermit.columbia.edu/kermit/c-kermit/ckufio.c 289. http://www.columbia.edu/kermit/ckuins.html#top 290. http://www.columbia.edu/kermit/ckuins.html#contents 291. http://www.columbia.edu/kermit/ckuins.html#x9 292. http://www.columbia.edu/kermit/ckuins.html#x9.5 293. http://www.columbia.edu/kermit/ckuins.html#x9.3 294. http://www.columbia.edu/kermit/cable.html 295. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c 296. ftp://kermit.columbia.edu/kermit/c-kermit/ckcdeb.h 297. http://www.columbia.edu/kermit/ckuins.html#top 298. http://www.columbia.edu/kermit/ckuins.html#contents 299. http://www.columbia.edu/kermit/ckuins.html#x9 300. http://www.columbia.edu/kermit/ckuins.html#x9.6 301. http://www.columbia.edu/kermit/ckuins.html#x9.4 302. ftp://kermit.columbia.edu/kermit/c-kermit/ckcdeb.h 303. ftp://kermit.columbia.edu/kermit/c-kermit/ckuus3.c 304. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c 305. http://www.columbia.edu/kermit/ckuins.html#top 306. http://www.columbia.edu/kermit/ckuins.html#contents 307. http://www.columbia.edu/kermit/ckuins.html#x9 308. http://www.columbia.edu/kermit/ckuins.html#x9.7 309. http://www.columbia.edu/kermit/ckuins.html#x9.5 310. http://www.columbia.edu/kermit/ckuins.html#top 311. http://www.columbia.edu/kermit/ckuins.html#contents 312. http://www.columbia.edu/kermit/ckuins.html#x9 313. http://www.columbia.edu/kermit/ckuins.html#x9.8 314. http://www.columbia.edu/kermit/ckuins.html#x9.6 315. http://www.columbia.edu/kermit/ckuins.html#top 316. http://www.columbia.edu/kermit/ckuins.html#contents 317. http://www.columbia.edu/kermit/ckuins.html#x9 318. http://www.columbia.edu/kermit/ckuins.html#x9.9 319. http://www.columbia.edu/kermit/ckuins.html#x9.7 320. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c 321. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c 322. ftp://kermit.columbia.edu/kermit/c-kermit/ckcdeb.h 323. ftp://kermit.columbia.edu/kermit/c-kermit/ckufio.c 324. http://www.columbia.edu/kermit/ckuins.html#top 325. http://www.columbia.edu/kermit/ckuins.html#contents 326. http://www.columbia.edu/kermit/ckuins.html#x9 327. http://www.columbia.edu/kermit/ckuins.html#x10 328. http://www.columbia.edu/kermit/ckuins.html#x9.8 329. http://www.columbia.edu/kermit/ckuins.html#top 330. http://www.columbia.edu/kermit/ckuins.html#contents 331. http://www.columbia.edu/kermit/ckuins.html#x11 332. http://www.columbia.edu/kermit/ckuins.html#x9 333. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c 334. http://www.columbia.edu/kermit/ckuins.html#x11 335. http://www.columbia.edu/kermit/ckuins.html#top 336. http://www.columbia.edu/kermit/ckuins.html#contents 337. http://www.columbia.edu/kermit/ckuins.html#x12 338. http://www.columbia.edu/kermit/ckuins.html#x10 339. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c 340. ftp://kermit.columbia.edu/kermit/c-kermit/ckufio.c 341. http://www.columbia.edu/kermit/ckuins.html#top 342. http://www.columbia.edu/kermit/ckuins.html#contents 343. http://www.columbia.edu/kermit/ckuins.html#x13 344. http://www.columbia.edu/kermit/ckuins.html#x11 345. http://www.columbia.edu/kermit/ckuins.html#top 346. http://www.columbia.edu/kermit/ckuins.html#contents 347. http://www.columbia.edu/kermit/ckuins.html#x14 348. http://www.columbia.edu/kermit/ckuins.html#x12 349. ftp://kermit.columbia.edu/kermit/c-kermit/ckubwr.txt 350. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c 351. http://www.columbia.edu/kermit/ckuins.html#top 352. http://www.columbia.edu/kermit/ckuins.html#contents 353. http://www.columbia.edu/kermit/ckuins.html#x15 354. http://www.columbia.edu/kermit/ckuins.html#x13 355. http://www.columbia.edu/kermit/ckuins.html#top 356. http://www.columbia.edu/kermit/ckuins.html#contents 357. http://www.columbia.edu/kermit/ckuins.html#x16 358. http://www.columbia.edu/kermit/ckuins.html#x14 359. http://www.columbia.edu/kermit/uiksd.html#x4.2 360. http://www.columbia.edu/kermit/uiksd.html 361. http://www.columbia.edu/kermit/ckermit2.html 362. http://www.columbia.edu/kermit/ckuins.html#top 363. http://www.columbia.edu/kermit/ckuins.html#contents 364. http://www.columbia.edu/kermit/ckuins.html#x17 365. http://www.columbia.edu/kermit/ckuins.html#x15 366. http://www.columbia.edu/kermit/security.html 367. http://www.columbia.edu/kermit/security80.html 368. http://www.columbia.edu/kermit/ckuins.html#top 369. http://www.columbia.edu/kermit/ckuins.html#contents 370. http://www.columbia.edu/kermit/ckuins.html#x16 371. http://www.columbia.edu/kermit/skermit.html 372. http://www.columbia.edu/kermit/ckuins.html#top 373. http://www.columbia.edu/kermit/ckuins.html#contents 374. http://www.columbia.edu/kermit/ckermit.html 375. http://www.columbia.edu/kermit/ck90updates.html 376. http://www.columbia.edu/kermit/index.html ckubwr.txt0000664000015300001460000076357111624230415011742 0ustar fdckermit [1]The Columbia Crown The Kermit Project | Columbia University 612 West 115th Street, New York NY 10025 USA o [2]kermit@columbia.edu ...since 1981 [3]Home [4]Kermit 95 [5]C-Kermit [6]Scripts [7]Current [8]New [9]FAQ [10]Support C-Kermit Unix Hints and Tips Frank da Cruz [11]The Kermit Project, [12]Columbia University As of: C-Kermit 9.0.302, 20 August 2011 This page last updated: Sun Aug 21 12:08:52 2011 (New York USA Time) IF YOU ARE READING A PLAIN-TEXT version of this document, note it is a plain-text dump of a Web page. You can visit the original (and possibly more up-to-date) Web page here: [13]http://www.columbia.edu/kermit/ckubwr.html Since the material in this file has been accumulating since 1985, some (much) of it might be dated. Known problems with C-Kermit 9.0 * Domain name resolution does not work in Solaris 10 or 11 (fixed in [14]9.0.301). * UUCP lockfile failure in FreeBSD 8 (fixed in [15]9.0.302). * Build failure FreeBSD 9 (fixed in [16]9.0.302). * Opening new SSH sessions after closing previous ones sometimes fails. * Heimdal Kerberos not supported. [ [17]C-Kermit ] [ [18]Installation Instructions ] [ [19]TUTORIAL ] CONTENTS 1. [20]INTRODUCTION 2. [21]PREBUILT C-KERMIT BINARIES 3. [22]PLATFORM-SPECIFIC NOTES 4. [23]GENERAL UNIX-SPECIFIC LIMITATIONS AND BUGS 5. [24]INITIALIZATION AND COMMAND FILES 6. [25]COMMUNICATION SPEED SELECTION 7. [26]COMMUNICATIONS AND DIALING 8. [27]HARDWARE FLOW CONTROL 9. [28]TERMINAL CONNECTION AND KEY MAPPING 10. [29]FILE TRANSFER 11. [30]EXTERNAL FILE TRANSFER PROTOCOLS 12. [31]SECURITY 13. [32]MISCELLANEOUS USER REPORTS 14. [33]THIRD-PARTY DRIVERS Quick Links: [ [34]Linux ] [ [35]*BSD ] [[36]Mac OS X] [ [37]AIX ] [ [38]HP-UX ] [ [39]Solaris ] [ [40]SCO ] 1. INTRODUCTION [ [41]Top ] [ [42]Contents ] [ [43]Next ] SECTION CONTENTS 1.1. [44]Documentation 1.2. [45]Technical Support 1.3. [46]The Year 2000 1.4. [47]The Euro THIS IS WHAT USED TO BE CALLED the "beware file" for the Unix version of C-Kermit, previously distributed as ckubwr.txt and, before that, as ckuker.bwr, after the fashion of old Digital Equipment Corporation (DEC) software releases that came with release notes (describing what had changed) and a "beware file" listing known bugs, limitations, "non-goals", and things to watch out for. The C-Kermit beware file has been accumulating since 1985, and it applies to many different hardware platforms and operating systems, and many versions of them, so it is quite large. Prior to C-Kermit 8.0, it was distributed only in plain-text format. Now it is available as a Web document with links, internal cross references, and so on, to make it easier to use. This document applies to Unix C-Kermit in general, as well as to specific Unix variations like [48]Linux, [49]AIX, [50]HP-UX, [51]Solaris, and so on, and should be read in conjunction with the [52]platform-independent C-Kermit beware file, which contains similar information, but applying to all versions of C-Kermit (VMS, Windows, OS/2, AOS/VS, VOS, etc, as well as to Unix). There is much in this document that is (only) of historical interest. The navigation links should help you skip directly to the sections that are relevant to you. Numerous offsite Web links are supposed to lead to further information but, as you know, Web links go stale frequently and without warning. If you can supply additional, corrected, updated, or better Web links, please feel free to [53]let me know. 1.1. Documentation [ [54]Top ] [ [55]Contents ] [ [56]Next ] C-Kermit 6.0 is documented in the book [57]Using C-Kermit, Second Edition, by Frank da Cruz and Christine M. Gianone, Digital Press, Burlington, MA, USA, ISBN 1-55558-164-1 (1997), 622 pages. This remains the definitive C-Kermit documentation. Until the third edition is published (sorry, there is no firm timeframe for this), please also refer to: [58]Supplement to Using C-Kermit, Second Edition, For C-Kermit 7.0 Thorough documentation of features new to version 7.0. [59]Supplement to Using C-Kermit, Second Edition, For C-Kermit 8.0 Thorough documentation of features new to version 8.0. [60]Supplement to Using C-Kermit, Second Edition, For C-Kermit 9.0 Thorough documentation of features new to version 9.0. 1.2. Technical Support [ [61]Top ] [ [62]Contents ] [ [63]Section Contents ] [ [64]Next ] [ [65]Previous ] For information on how to get technical support, please visit: [66]http://www.columbia.edu/kermit/support.html 1.3. The Year 2000 [ [67]Top ] [ [68]Contents ] [ [69]Section Contents ] [ [70]Next ] [ [71]Previous ] The Unix version of C-Kermit, release 6.0 and later, is "Year 2000 compliant", but only if the underlying operating system is too. Contact your Unix operating system vendor to find out which operating system versions, patches, hardware, and/or updates are required. (Quite a few old Unixes are still in operation in the new millenium, but with their date set 28 years in the past so at least the non-year parts of the calendar are correct.) As of C-Kermit 6.0 (6 September 1996), post-millenium file dates are recognized, transmitted, received, and reproduced correctly during the file transfer process in C-Kermit's File Attribute packets. If post-millenium dates are not processed correctly on the other end, file transfer still takes place, but the modification or creation date of the received file might be incorrect. The only exception would be if the "file collision update" feature is being used to prevent unnecessary transfer of files that have not changed since the last time a transfer took place; in this case, a file might be transferred unnecessarily, or it might not be transferred when it should have been. Correct operation of the update feature depends on both Kermit programs having the correct date and time. Of secondary importance are the time stamps in the transaction and/or debug logs, and the date-related script programming constructs, such as \v(date), \v(ndate), \v(day), \v(nday), and perhaps also the time-related ones, \v(time) and \v(ntime), insofar as they might be affected by the date. The \v(ndate) is a numeric-format date of the form yyyymmdd, suitable for both lexical and numeric comparison and sorting: e.g. 19970208 or 20011231. If the underlying operating system returns the correct date information, these variables will have the proper values. If not, then scripts that make decisions based on these variables might not operate correctly. Most date-related code is based upon the C Library asctime() string, which always has a four-digit year. In Unix, the one bit of code in C-Kermit that is an exception to this rule is several calls to localtime(), which returns a pointer to a tm struct, in which the year is presumed to be expressed as "years since 1900". The code depends on this assumption. Any platforms that violate it will need special coding. As of this writing, no such platforms are known. Command and script programming functions that deal with dates use C-Kermit specific code that always uses full years. 1.4. The Euro [ [72]Top ] [ [73]Contents ] [ [74]Section Contents ] [ [75]Previous ] C-Kermit 7.0 and later support Unicode (ISO 10646), ISO 8859-15 Latin Alphabet 9, PC Code Page 858, Windows Code Pages 1250 and 1251, and perhaps other character sets, that encode the Euro symbol, and can translate among them as long as no intermediate character-set is involved that does not include the Euro. 2. PREBUILT C-KERMIT BINARIES [ [76]Top ] [ [77]Contents ] [ [78]Next ] [ [79]Previous ] It is often dangerous to run a binary C-Kermit (or any other) program built on a different computer. Particularly if that computer had a different C compiler, libraries, operating system version, processor features, etc, and especially if the program was built with shared libraries, because as soon as you update the libraries on your system, they no longer match the ones referenced in the binary, and the binary might refuse to load when you run it, in which case you'll see error messages similar to: Could not load program kermit Member shr4.o not found or file not an archive Could not load library libcurses.a[shr4.o] Error was: No such file or directory (These samples are from AIX.) To avoid this problem, we try to build C-Kermit with statically linked libraries whenever we can, but this is increasingly impossible as shared libraries become the norm. It is often OK to run a binary built on an earlier OS version, but it is rarely possible (or safe) to run a binary built on a later one, for example to run a binary built under Solaris 8 on Solaris 2.6. Sometimes even the OS-or-library patch/ECO level makes a difference. A particularly insidious problem occurs when a binary was built on a version of the OS that has patches from the vendor (e.g. to libraries); in many cases you won't be able to run such a binary on an unpatched version of the same platform. When in doubt, build C-Kermit from the source code on the computer where it is to be run (if possible!). If not, ask us for a binary specific to your configuration. We might have one, and if we don't, we might be able to find somebody who will build one for you. 3. NOTES ON SPECIFIC UNIX VERSIONS [ [80]Top ] [ [81]Contents ] [ [82]Next ] [ [83]Previous ] SECTION CONTENTS 3.0. [84]C-KERMIT ON PC-BASED UNIXES 3.1. [85]C-KERMIT AND AIX 3.2. [86]C-KERMIT AND HP-UX 3.3. [87]C-KERMIT AND LINUX 3.4. [88]C-KERMIT AND NEXTSTEP 3.5. [89]C-KERMIT AND QNX 3.6. [90]C-KERMIT AND SCO 3.7. [91]C-KERMIT AND SOLARIS 3.8. [92]C-KERMIT AND SUNOS 3.9. [93]C-KERMIT AND ULTRIX 3.10. [94]C-KERMIT AND UNIXWARE 3.11. [95]C-KERMIT AND APOLLO SR10 3.12. [96]C-KERMIT AND TANDY XENIX 3.0 3.13. [97]C-KERMIT AND OSF/1 (DIGITAL UNIX) (TRU64 UNIX) 3.14. [98]C-KERMIT AND SGI IRIX 3.15. [99]C-KERMIT AND THE BEBOX 3.16. [100]C-KERMIT AND DG/UX 3.17. [101]C-KERMIT AND SEQUENT DYNIX 3.18. [102]C-KERMIT AND {FREE,OPEN,NET}BSD 3.19. [103]C-KERMIT AND MAC OS X 3.20. [104]C-KERMIT AND COHERENT The following sections apply to specific Unix versions. Most of them contain references to FAQs (Frequently Asked Questions), but these tend to be ephemeral. For possibly more current information see: [105]http://www.faqs.org [106]http://aplawrence.com/Unixart/newtounix.html One thread that runs through many of them, and implicitly perhaps through all, concerns the problems that occur when trying to dial out on a serial device that is (also) enabled for dialing in. The "solutions" to this problem are many, varied, diverse, and usually gross, involving configuring the device for bidirectional use. This is done in a highly OS-dependent and often obscure manner, and the effects (good or evil) are also highly dependent on the particular OS (and getty variety, etc). Many examples are given in the [107]OS-specific sections below. An important point to keep in mind is that C-Kermit is a cross-platform, portable software program. It was not designed specifically and only for your particular Unix version, or for that matter, for Unix in particular at all. It also runs on VMS, AOS/VS, VOS, and other non-Unix platforms. All the Unix versions of C-Kermit share common i/o modules, with compile-time #ifdef constructions used to account for the differences among the many Unix products and releases. If you think that C-Kermit is behaving badly or missing something on your particular Unix version, you might be right -- we can't claim to be expert in hundreds of different OS / version / hardware / library combinations. If you're a programmer, take a look at the source code and [108]send us your suggested fixes or changes. Or else just [109]send us a report about what seems to be wrong and we'll see what we can do. 3.0. C-KERMIT ON PC-BASED UNIXES [ [110]Top ] [ [111]Contents ] [ [112]Section Contents ] [ [113]Next ] Also see: [114]http://www.pcunix.com/. SECTION CONTENTS 3.0.1. [115]Interrupt Conflicts 3.0.2. [116]Windows-Specific Hardware 3.0.3. [117]Modems 3.0.4. [118]Character Sets 3.0.5. [119]Keyboard, Screen, and Mouse Access 3.0.6. [120]Laptops 3.0.1. Interrupt Conflicts [ [121]Top ] [ [122]Contents ] [ [123]Section Contents ] [ [124]Next ] PCs are not the best platform for real operating systems like Unix. The architecture suffers from numerous deficiencies, not the least of which is the stiflingly small number of hardware interrupts (either 7 or 15, many of which are preallocated). Thus adding devices, using multiple serial ports, etc, is always a challenge and often a nightmare. The free-for-all nature of the PC market and the lack of standards combined with the diversity of Unix OS versions make it difficult to find drivers for any particular device on any particular version of Unix. Of special interest to Kermit users is the fact that there is no standard provision in the PC architecture for more than 2 communication (serial) ports. COM3 and COM4 (or higher) will not work unless you (a) find out the hardware address and interrupt for each, (b) find out how to provide your Unix version with this information, and (c) actually set up the configuration in the Unix startup files (or whatever other method is used). Watch out for interrupt conflicts, especially when using a serial mouse, and don't expect to be able to use more than two serial ports. The techniques for resolving interrupt conflicts are different for each operating system (Linux, NetBSD, etc). In general, there is a configuration file somewhere that lists COM ports, something like this: com0 at isa? port 0x3f8 irq 4 # DOS COM1 com1 at isa? port 0x2f8 irq 3 # DOS COM2 The address and IRQ values in this file must agree with the values in the PC BIOS and with the ports themselves, and there must not be more than one device with the same interrupt. Unfortunately, due to the small number of available interrupts, installing new devices on a PC almost always creates a conflict. Here is a typical tale from a Linux user (Fred Smith) about installing a third serial port: ...problems can come from a number of causes. The one I fought with for some time, and finally conquered, was that my modem is on an add-in serial port, cua3/IRQ5. By default IRQ5 has a very low priority, and does not get enough service in times when the system is busy to prevent losing data. This in turn causes many resends. There are two 'fixes' that I know of, one is to relax hard disk interrupt hogging by using the correct parameter to hdparm, but I don't like that one because the hdparm man page indicates it is risky to use. The other one, the one I used, was to get 'irqtune' and use it to give IRQ5 the highest priority instead of nearly the lowest. Completely cured the problem. Here's another one from a newsgroup posting: After much hair pulling, I've discovered why my serial port won't work. Apparently my [PC] has three serial devices (two comm ports and an IR port), of which only two at a time can be active. I looked in the BIOS setup and noticed that the IR port was activated, but didn't realize at the time that this meant that COM2 was thereby de-activated. I turned off the IR port and now the serial port works as advertised. 3.0.2. Windows-Specific Hardware [ [125]Top ] [ [126]Contents ] [ [127]Section Contents ] [ [128]Next ] [ [129]Previous ] To complicate matters, the PC platform is becoming increasingly and inexorably Windows-oriented. More and more add-on devices are "Windows only" -- meaning they are incomplete and rely on proprietary Windows-based software drivers to do the jobs that you would expect the device itself to do. PCMCIA, PCI, or "Plug-n-Play" devices are rarely supported on PC-based Unix versions such as SCO; Winmodems, Winprinters, and the like are not supported on any Unix variety (with [130]a few exceptions). The self-proclaimed Microsoft PC 97 (or later) standard only makes matters worse since its only purpose to ensure that PCs are "optimized to run Windows 95 and Windows NT 4.0 and future versions of these operating systems". With the exception noted (the Lucent modem, perhaps a handful of others by the time you read this), drivers for "Win" devices are available only for Windows, since the Windows market dwarfs that of any particular Unix brand, and for that matter all Unixes (or for that matter, all non-Windows operating systems) combined. If your version of Unix (SCO, Linux, BSDI, FreeBSD, etc) does not support a particular device, then C-Kermit can't use it either. C-Kermit, like any Unix application, must access all devices through drivers and not directly because Unix is a real operating system. Don't waste time thinking that you, or anybody else, could write a Linux (or other Unix) driver for a Winmodem or other "Win" device. First of all, these devices generally require realtime control, but since Unix is a true multitasking operating system, realtime device control is not possible outside the kernel. Second, the specifications for these devices are secret and proprietary, and each one (and each version of each one) is potentially different. Third, a Winmodem driver would be enormously complex; it would take years to write and debug, by which time it would be obsolete. A more recent generation of PCs (circa 1999-2000) is marketed as "Legacy Free". One can only speculate what that could mean. Most likely it means it will ONLY run the very latest versions of Windows, and is made exclusively of Winmodems, Winprinters, Winmemory, and Win-CPU-fans (Legacy Free is a concept [131]pioneered by Microsoft). Before you buy a new PC or add-on equipment, especially serial ports, internal modems, or printers, make sure they are compatible with your version of Unix. This is becoming an ever-greater challenge; only a huge company like Microsoft can afford to be constantly cranking out and/or verifying drivers for the thousands of video boards, sound cards, network adapters, SCSI adapters, buses, etc, that spew forth in an uncontrolled manner from all corners of the world on a daily basis. With very few exceptions, makers of PCs assemble the various components and then verify them only with Windows, which they must do since they are, no doubt, preloading the PC with Windows. To find a modern PC that is capable of running a variety of non-Windows operating systems (e.g. Linux, SCO OpenServer, Unixware, and Solaris) is a formidable challenge requiring careful study of each vendor's "compatibility lists" and precise attention to exact component model numbers and revision levels. 3.0.3. Modems [ [132]Top ] [ [133]Contents ] [ [134]Section Contents ] [ [135]Next ] [ [136]Previous ] External modems are recommended: * They don't need any special drivers. * You can use the lights and speaker to troubleshoot dialing. * You can share them among all types of computers. * You can easily turn them off and on when power-cycling seems warranted. * They are more likely to have manuals. Internal PC modems (even when they are not Winmodems, which is increasingly unlikely in new PCs) are always trouble, especially in Unix. Even when they work for dialing out, they might not work for dialing in, etc. Problems that occur when using an internal modem can almost always be eliminated by switching to an external one. Even when an internal modem is not a Winmodem or Plug-n-Play, it is often a no-name model of unknown quality -- not the sort of thing you want sitting directly on your computer's bus. (Even if it does not cause hardware problems, it probably came without a command list, so no Unix software will know how to control it.) For more about Unix compatible modems, see: [137]http://www.idir.net/~gromitkc/winmodem.html Remember that PCs, even now -- more than two decades after they were first introduced -- are not (in general) capable of supporting more than 2 serial devices. Here's a short success story from a recent newsgroup posting: "I have a Diamond SupraSonic II dual modem in my machine. What I had to end up doing is buying a PS/2 mouse and port and install it. Had to get rid of my serial mouse. I also had to disable PnP in my computer bios. I was having IRQ conflicts between my serial mouse and 'com 3'. Both modems work fine for me. My first modem is ttyS0 and my second is ttyS1." Special third-party multiport boards such as [138]DigiBoard are available for certain Unix platforms (typically SCO, maybe Linux) that come with special platform-specific drivers. 3.0.4. Character Sets [ [139]Top ] [ [140]Contents ] [ [141]Section Contents ] [ [142]Next ] [ [143]Previous ] PCs generally have PC code pages such as CP437 or CP850, and these are often used by PC-based Unix operating systems, particularly on the console. These are supported directly by C-Kermit's SET FILE CHARACTER-SET and SET TERMINAL CHARACTER-SET commands. Some PC-based Unix versions, such as recent Red Hat Linux releases, might also support Microsoft Windows code pages such as CP1252, or even Latin Alphabet 1 itself (perhaps displayed with CP437 glyphs). (And work is in progress to support Unicode UTF8 in Linux.) Certain Windows code pages are not supported directly by C-Kermit, but since they are ISO Latin Alphabets with nonstandard "extensions" in the C1 control range, you can substitute the corresponding Latin alphabet (or other character set) in any C-Kermit character-set related commands: Windows Code Page Substitution CP 1004 Latin-1 CP 1051 HP Roman-8 Other Windows code pages are mostly (or totally) incompatible with their Latin Alphabet counterparts (e.g. CP1250 and Latin-2), and several of these are already supported by C-Kermit 7.0 and later (1250, 1251, and 1252). 3.0.5. Keyboard, Screen, and Mouse Access [ [144]Top ] [ [145]Contents ] [ [146]Section Contents ] [ [147]Next ] [ [148]Previous ] Finally, note that as a real operating system, Unix (unlike Windows) does not provide the intimate connection to the PC keyboard, screen, and mouse that you might expect. Unix applications can not "see" the keyboard, and therefore can not be programmed to understand F-keys, Editing keys, Arrow keys, Alt-key combinations, and the like. This is because: a. Unix is a portable operating system, not only for PCs; b. Unix sessions can come from anywhere, not just the PC's own keyboard and screen; and: c. even though it might be possible for an application that actually is running on the PC's keyboard and screen to access these devices directly, there are no APIs (outside of X) for this. 3.0.6. Laptops [ [149]Top ] [ [150]Contents ] [ [151]Section Contents ] [ [152]Previous ] (To be filled in . . .) 3.1. C-KERMIT AND AIX [ [153]Top ] [ [154]Contents ] [ [155]Section Contents ] [ [156]Next ] [ [157]Previous ] SECTION CONTENTS 3.1.1. [158]AIX: General 3.1.2. [159]AIX: Network Connections 3.1.3. [160]AIX: Serial Connections 3.1.4. [161]AIX: File Transfer 3.1.5. [162]AIX: Xterm Key Map For additional information see: * [163]http://www.emerson.emory.edu/services/aix-faq/ * [164]http://www.faqs.org/faqs/by-newsgroup/comp/comp.unix.aix.html * [165]http://www.cis.ohio-state.edu/hypertext/faq/usenet/aix-faq/top .html * [166]http://aixpdslib.seas.ucla.edu/ * [167]http://www.rootvg.net (AIX history) * [168]ftp://rtfm.mit.edu/pub/usenet/news.answers/aix-faq/part1 * [169]ftp://mirrors.aol.com/pub/rtfm/usenet-by-hierarchy/comp/unix/a ix and/or read the [170]comp.unix.aix newsgroup. ________________________________________________________________________ 3.1.1. AIX: General [ [171]Top ] [ [172]Contents ] [ [173]Section Contents ] [ [174]Next ] About AIX version numbers: "uname -a" tells the two-digit version number, such as 3.2 or 4.1. The three-digit form can be seen with the "oslevel" command (this information is unavailable at the API level and is reportedly obtained by scanning the installed patch list). Supposedly all three-digit versions within the same two-digit version (e.g. 4.3.1, 4.3.2) are binary compatible; i.e. a binary built on any one of them should run on all others, but who knows. Most AIX advocates tell you that any AIX binary will run on any AIX version greater than or equal to the one under which it was built, but experience with C-Kermit suggests otherwise. It is always best to run a binary built under your exact same AIX version, down to the third decimal place, if possible. Ideally, build it from source code yourself. Yes, this advice would be easier to follow if AIX came with a C compiler. ________________________________________________________________________ 3.1.2. AIX: Network Connections [ [175]Top ] [ [176]Contents ] [ [177]Section Contents ] [ [178]Next ] [ [179]Previous ] File transfers into AIX 4.2 or 4.3 through the AIX Telnet or Rlogin server have been observed to fail (or accumulate huge numbers of correctable errors, or even disconnect the session), when exactly the same kind of transfers into AIX 4.1 work without incident, as do such transfers into all non-AIX platforms on the same kind of connections (with a few exceptions noted elsewhere in this document). AIX 4.3.3 seems to be particularly fragile in this regard; the weakness seems to be in its pseudoterminal (pty) driver. High-speed streaming transfers work perfectly, however, if the AIX Telnet server and pty driver are removed from the picture; e.g, by using "set host * 3000" on AIX. The problem can be completely cured by replacing the IBM Telnet server with [180]MIT's Kerberos Telnet server -- even if you don't actually use the Kerberos part. Diagnosis: AIX pseudoterminals (which are controlled by the Telnet server to give you a login terminal for your session) have quirks that not even IBM knows about. The situation with AIX 5.x is not known, but if it has the same problem, the same cure is available. Meanwhile, the only remedy when going through the IBM Telnet server is to cut back on Kermit's performance settings until you find a combination that works: * SET STREAMING OFF * SET WINDOW-SIZE small-number * SET { SEND, RECEIVE } PACKET-LENGTH small-number * SET PREFIXING { CAUTIOUS, ALL } In some cases, severe cutbacks are required, e.g. those implied by the ROBUST command. Also be sure that the AIX C-Kermit on the remote end has "set flow none" (which is the default). NOTE: Maybe this one can also be addressed by starting AIX telnetd with the "-a" option. The situation with SSH connections is not known, but almost certainly the same. When these problems occur, the system error log contains: LABEL: TTY_TTYHOG IDENTIFIER: 0873CF9F Type: TEMP Resource Name: pts/1 Description TTYHOG OVER-RUN Failure Causes EXCESSIVE LOAD ON PROCESSOR Recommended Actions REDUCE SYSTEM LOAD. REDUCE SERIAL PORT BAUD RATE Before leaving the topic of AIX pseudoterminals, it is very likely that Kermit's PTY and SSH commands do not work well either, for the same reason that Telnet connections into AIX don't work well. A brief test with "pty rlogin somehost" got a perfectly usable terminal (CONNECT) session, but file-transfer problems like those just described. Reportedly, telnet from AIX 4.1-point-something to non-Telnet ports does not work unless the port number is in the /etc/services file; it's not clear from the report whether this is a problem with AIX Telnet (in which case it would not affect Kermit), or with the sockets library (in which case it would). The purported fix is IBM APAR IX61523. C-Kermit SET HOST or TELNET from one AIX 3.1 (or earlier) system to another won't work right unless you set your local terminal type to something other than AIXTERM. When your terminal type is AIXTERM, AIX TELNET sends two escapes whenever you type one, and the AIX telnet server swallows one of them. This has something to do with the "hft" device. This behavior seems to be removed in AIX 3.2 and later. ________________________________________________________________________ 3.1.3. AIX: Serial Connections [ [181]Top ] [ [182]Contents ] [ [183]Section Contents ] [ [184]Next ] [ [185]Previous ] In AIX 3, 4, or 5, C-Kermit won't be able to "set line /dev/tty0" (or any other dialout device) if you haven't installed "cu" or "uucp" on your system, because installing these is what creates the UUCP lockfile directory. If SET LINE commands always result in "Sorry, access to lock denied", even when C-Kermit has been given the same owner, group, and permissions as cu: -r-sr-xr-x 1 uucp uucp 67216 Jul 27 1999 cu and even when you run it as root, then you must go back and install "cu" from your AIX installation media. According to IBM's "From Strength to Strength" document (21 April 1998), in AIX 4.2 and later "Async supports speeds on native serial ports up to 115.2kbps". However, no API is documented to achieve serial speeds higher than 38400 bps. Apparently the way to do this -- which might or might not work only on the IBM 128-port multiplexer -- is: cxma-stty fastbaud /dev/tty0 which, according to "man cxma-stty": fastbaud Alters the baud rate table, so 50 baud becomes 57600 baud. -fastbaud Restores the baud rate table, so 57600 baud becomes 50 baud. Presumably (but not certainly) this extrapolates to 110 "baud" becomes 76800 bps, and 150 becomes 115200 bps. So to use high serial speeds in AIX 4.2 or 4.3, the trick would be to give the "cxma-stty fastbaud" command for the desired tty device before starting Kermit, and then use "set speed 50", "set speed 110", or "set speed 150" to select 56700, 76800, or 115200 bps. It is not known whether cxma-stty requires privilege. According to one report, "Further investigation with IBM seems to indicate that the only hardware capable of doing this is the 128-port multiplexor with one (or more) of the 16 port breakout cables (Enhanced Remote Async Node 16-Port EIA-232). We are looking at about CDN$4,000 in hardware just to hang a 56kb modem on there. Of course, we can then hang 15 more, if we want. This hardware combo is described to be good to 230.4kbps." Another report says (quote from AIX newsgroup, March 1999): The machine type and the adapter determine the speed that one can actually run at. The older microchannel machines have much slower crystal frequencies and may not go beyond 76,800. A feature put into AIX 421 allows one to key in non-POSIX baud rates and if the uart can support that speed, it will get set. this applies also to 43p's and beyond. 115200 is the max for the 43P's native serial port. As crytal frequencies continue to increase, the built-in serial ports speeds will improve. To use 'uucp' or 'ate' at the higher baud rates, configure the port for the desired speed, but set the speed of uucp or ate to 50. Any non-POSIX speeds set in the ttys configuration will the be used. In the case of the 128-port adapters or the ISA 8-port or PCI 8-port adapter, there are only a few higher baud rates. a. Change the port to enable high baud rates: + B50 for 57600 + B75 for 76800 + B110 for 115200 + B200 for 230000 b. chdev -l ttyX -a fastbaud=enable + For the 128 ports original style rans, only 57600 bps is supported. + For the new enhanced RANs, up to 230Kbps is supported. In AIX 2.2.1 on the RT PC with the 8-port multiplexer, SET SPEED 38400 gives 9600 bps, but SET SPEED 19200 gives 19200 (on the built-in S1 port). Note that some RS/6000s (e.g. the IBM PowerServer 320) have nonstandard rectangular 10-pin serial ports; the DB-25 connector is NOT a serial port; it is a parallel printer port. IBM cables are required for the serial ports, (The IBM RT PC also had rectangular serial ports -- perhaps the same as these, perhaps different.) If you dial in to AIX through a modem that is connected directly to an AIX port (e.g. on the 128-port multiplexer) and find that data is lost, especially when uploading files to the AIX system (and system error logs report buffer overruns on the port): 1. Make sure the port and modem are BOTH configured for hardware (RTS/CTS) flow control. The port is configured somewhere in the system configuration, outside of Kermit. 2. Tell C-Kermit to "set flow keep"; experimentation shows that SET FLOW RTS/CTS has no effect when used in remote mode (i.e. on /dev/tty, as opposed to a specify port device). 3. Fixes for bugs in the original AIX 4.2 tty (serial i/o) support and other AIX bugs are available from IBM at: [186]http://service.software.ibm.com/rs6000/ Downloads -> Software Fixes -> Download FixDist gets an application for looking up known problems. Many problems reported with bidirectional terminal lines on AIX 3.2.x on the RS/6000. Workaround: don't use bidirectional terminal lines, or write a shell-script wrapper for Kermit that turns getty off on the line before starting Kermit, or before Kermit attempts to do the SET LINE. (But note: These problems MIGHT be fixed in C-Kermit 6.0 and later.) The commands for turning getty off and on (respectively) are /usr/sbin/pdisable and /usr/sbin/penable. ________________________________________________________________________ 3.1.4. AIX: File Transfer [ [187]Top ] [ [188]Contents ] [ [189]Section Contents ] [ [190]Next ] [ [191]Previous ] Evidently AIX 4.3 (I don't know about earlier versions) does not allow open files to be overwritten. This can cause Kermit transfers to fail when FILE COLLISION is OVERWRITE, where they might work on other Unix varieties or earlier AIX versions. Transfer of binary -- and maybe even text -- files can fail in AIX if the AIX terminal has particular port can have character-set translation done for it by the tty driver. The following advice from a knowledgeable AIX user: [This feature] has to be checked (and set/cleared) with a separate command, unfortunately stty doesn't handle this. To check: $ setmaps input map: none installed output map: none installed If it says anything other than "none installed" for either one, it is likely to cause a problem with kermit. To get rid of installed maps: $ setmaps -t NOMAP However, I seem to recall that with some versions of AIX before 3.2.5, only root could change the setting. I'm not sure what versions - it might have only been under AIX 3.1 that this was true. At least with AIX 3.2.5 an ordinary user can set or clear the maps. On the same problem, another knowledgeable AIX user says: The way to get information on the NLS mapping under AIX (3.2.5 anyway) is as follows. From the command line type: lsattr -l tty# -a imap -a omap -E -H Replace the tty number for the number sign above. This will give a human readable output of the settings that looks like this; # lsattr -l tty2 -a imap -a omap -E -H attribute value description user_settable imap none INPUT map file True omap none OUTPUT map file True If you change the -H to a -O, you get output that can easily be processed by another program or a shell script, for example: # lsattr -l tty2 -a imap -a omap -E -O #imap:omap none:none To change the settings from the command line, the chdev command is used with the following syntax. chdev -l tty# -a imap='none' -a omap='none' Again substituting the appropriate tty port number for the number sign, "none" being the value we want for C-Kermit. Of course, the above can also be changed by using the SMIT utility and selecting devices - tty. (...end quote) In 2007 I noticed the following on high-speed SSH connections (local network) into AIX 5.3: streaming transfers into AIX just don't work. The same might be true for Telnet connections; I have no way to check. It appears that the AIX pty driver and/or the SSH (and possibly Telnet) server are not capable of receiving a steady stream of incoming data at high speed. Solution: unknown. Workaround: put "set streaming off" in your .kermrc or .mykermrc file, since streaming is the default for network connections. ________________________________________________________________________ 3.1.5. AIX: Xterm Key Map [ [192]Top ] [ [193]Contents ] [ [194]Section Contents ] [ [195]Previous ] Here is a sample configuration for setting up an xterm keyboard for VT220 or higher terminal emulation on AIX, courtesy of Bruce Momjian, Drexel Hill, PA. Xterm can be started like this: xterm $XTERMFLAGS +rw +sb +ls $@ -tm 'erase ^? intr ^c' -name vt220 \ -title vt220 -tn xterm-220 "$@" & --------------------------------------------------------------------------- XTerm*VT100.Translations: #override \n\ Home: string(0x1b) string("[3~") \n \ End: string(0x1b) string("[4~") \n vt220*VT100.Translations: #override \n\ Shift F1: string("[23~") \n \ Shift F2: string("[24~") \n \ Shift F3: string("[25~") \n \ Shift F4: string("[26~") \n \ Shift F5: string("[K~") \n \ Shift F6: string("[31~") \n \ Shift F7: string("[31~") \n \ Shift F8: string("[32~") \n \ Shift F9: string("[33~") \n \ Shift F10: string("[34~") \n \ Shift F11: string("[28~") \n \ Shift F12: string("[29~") \n \ Print: string(0x1b) string("[32~") \n\ Cancel: string(0x1b) string("[33~") \n\ Pause: string(0x1b) string("[34~") \n\ Insert: string(0x1b) string("[2~") \n\ Delete: string(0x1b) string("[3~") \n\ Home: string(0x1b) string("[1~") \n\ End: string(0x1b) string("[4~") \n\ Prior: string(0x1b) string("[5~") \n\ Next: string(0x1b) string("[6~") \n\ BackSpace: string(0x7f) \n\ Num_Lock: string(0x1b) string("OP") \n\ KP_Divide: string(0x1b) string("Ol") \n\ KP_Multiply: string(0x1b) string("Om") \n\ KP_Subtract: string(0x1b) string("OS") \n\ KP_Add: string(0x1b) string("OM") \n\ KP_Enter: string(0x1b) string("OM") \n\ KP_Decimal: string(0x1b) string("On") \n\ KP_0: string(0x1b) string("Op") \n\ KP_1: string(0x1b) string("Oq") \n\ KP_2: string(0x1b) string("Or") \n\ KP_3: string(0x1b) string("Os") \n\ KP_4: string(0x1b) string("Ot") \n\ KP_5: string(0x1b) string("Ou") \n\ KP_6: string(0x1b) string("Ov") \n\ KP_7: string(0x1b) string("Ow") \n\ KP_8: string(0x1b) string("Ox") \n\ KP_9: string(0x1b) string("Oy") \n ! Up: string(0x1b) string("[A") \n\ ! Down: string(0x1b) string("[B") \n\ ! Right: string(0x1b) string("[C") \n\ ! Left: string(0x1b) string("[D") \n\ *visualBell: true *saveLines: 1000 *cursesemul: true *scrollKey: true *scrollBar: true 3.2. C-KERMIT AND HP-UX [ [196]Top ] [ [197]Contents ] [ [198]Section Contents ] [ [199]Next ] [ [200]Previous ] SECTION CONTENTS 3.2.0. [201]Common Problems 3.2.1. [202]Building C-Kermit on HP-UX 3.2.2. [203]File Transfer 3.2.3. [204]Dialing Out and UUCP Lockfiles in HP-UX 3.2.4. [205]Notes on Specific HP-UX Releases 3.2.5. [206]HP-UX and X.25 REFERENCES For further information, read the [207]comp.sys.hp.hpux newsgroup. C-Kermit is included as part of the HP-UX operating system by contract between Hewlett Packard and Columbia University for HP-UX 10.00 and later. Each level of HP-UX includes a freshly built C-Kermit binary in /bin/kermit, which should work correctly. Binaries built for regular HP-UX may be used on Trusted HP-UX and vice-versa, except for use as IKSD because of the different authentication methods. Note that HP does not update C-Kermit versions for any but its most current HP-UX release. So, for example, HP-UX 10.20 has C-Kermit 6.0; 11.00 has C-Kermit 7.0, and 11.22 has 8.0. Of course, as with all software, older Kermit versions have bugs (such as buffer overflow vulnerabilities) that are fixed in later versions. From time to time, HP discovers one of these (long-ago fixed) bugs and issues a security alert for the older OS's, recommending some draconian measure to avoid the problem. The true fix in each situation is to install the current release of C-Kermit. 3.2.0. Common Problems [ [208]Top ] [ [209]Contents ] [ [210]Section Contents ] [ [211]Next ] Some HP workstations have a BREAK/RESET key. If you hit this key while C-Kermit is running, it might kill or suspend the C-Kermit process. C-Kermit arms itself against these signals, but evidently the BREAK/RESET key is -- at least in some circumstances, on certain HP-UX versions -- too powerful to be caught. (Some report that the first BREAK/RESET shows up as SIGINT and is caught by C-Kermit's former SIGINT handler even when SIGINT is currently set to SIG_IGN; the second kills Kermit; other reports suggest the first BREAK/RESET sends a SIGTSTP (suspend signal) to Kermit, which it catches and suspends itself. You can tell C-Kermit to ignore suspend signals with SET SUSPEND OFF. You can tell C-Kermit to ignore SIGINT with SET COMMAND INTERRUPTION OFF. It is not known whether these commands also grant immunity to the BREAK/RESET key (one report states that with SET SUSPEND OFF, the BREAK/RESET key is ignored the first four times, but kills Kermit the 5th time). In any case: 1. If this key is mapped to SIGINT or SIGTSTP, C-Kermit catches or ignores it, depending on which mode (CONNECT, command, etc) Kermit is in. 2. If it causes HP-UX to kill C-Kermit, there is nothing C-Kermit can do to prevent it. When HP-UX is on the remote end of the connection, it is essential that HP-UX C-Kermit be configured for Xon/Xoff flow control (this is the default, but in case you change it and then experience file-transfer failures, this is a likely reason). 3.2.1. Building C-Kermit on HP-UX [ [212]Top ] [ [213]Contents ] [ [214]Section Contents ] [ [215]Next ] [ [216]Previous ] This section applies mainly to old (pre-10.20) HP-UX version on old, slow, and/or memory-constrained hardware. During the C-Kermit 6.0 Beta cycle, something happened to ckcpro.w (or, more precisely, the ckcpro.c file that is generated from it) which causes HP optimizing compilers under HP-UX versions 7.0 and 8.0 (apparently on all platforms) as well as under HP-UX 9.0 on Motorola platforms only, to blow up. In versions 7.0 and 8.0 the problem has spread to other modules. The symptoms vary from the system grinding to a halt, to the compiler crashing, to the compilation of the ckcpro.c module taking very long periods of time, like 9 hours. This problem is handled by compiling the modules that tickle it without optimization; the new C-Kermit makefile takes care of this, and shows how to do it in case the same thing begins happening with other modules. On HP-UX 9.0, a kernel parameter, maxdsiz (maximum process data segment size), seems to be important. On Motorola systems, it is 16MB by default, whereas on RISC systems the default is much bigger. Increasing maxdsiz to about 80MB seems to make the problem go away, but only if the system also has a lot of physical memory -- otherwise it swaps itself to death. The optimizing compiler might complain about "some optimizations skipped" on certain modules, due to lack of space available to the optimizer. You can increase the space (the incantation depends on the particular compiler version -- see the [217]makefile), but doing so tends to make the compilations take a much longer time. For example, the "hpux0100o+" makefile target adds the "+Onolimit" compiler flag, and about an hour to the compile time on an HP-9000/730. But it *does* produce an executable that is about 10K smaller :-) In the makefile, all HP-UX entries automatically skip optimization of problematic modules. 3.2.2. File Transfer [ [218]Top ] [ [219]Contents ] [ [220]Section Contents ] [ [221]Next ] [ [222]Previous ] Telnet connections into HP-UX versions up to and including 11.11 (and possibly 11.20) tend not to lend themselves to file transfer due to limitations, restrictions, and/or bugs in the HP-UX Telnet server and/or pseudoterminal (pty) driver. In C-Kermit 6.0 (1996) an unexpected slowness was noted when transferring files over local Ethernet connections when an HP-UX system (9.05 or 10.00) was on the remote end. The following experiment was conducted to determine the cause. C-Kermit 6.0 was used; the situation is slightly better using C-Kermit 7.0's streaming feature and HP-UX 10.20 on the far end. The systems were HP-UX 10.00 (on 715/33) and SunOS 4.1.3 (on Sparc-20), both on the same local 10Mbps Ethernet, packet length 4096, parity none, control prefixing "cautious", using only local disks on each machine -- no NFS. In the C-Kermit 6.0 (ACK/NAK) case, the window size was 20; in the streaming case there is no window size (i.e. it is infinite). The test file was C-Kermit executable, transferred in binary mode. Conditions were relatively poor: the Sun and the local net heavily loaded; the HP system is old, slow, and memory-constrained. C-Kermit 6.0... C-Kermit 7.0... Local Remote ACK/NAK........ Streaming...... Client Server Send Receive Send Receive Sun HP 36 18 64 18 HP HP 25 15 37 16 HP Sun 77 83 118 92 Sun Sun 60 60 153 158 So whenever HP is the remote we have poor performance. Why? * Changing file display to CRT has no effect (so it's not the curses library on the client side). * Changing TCP RECV-BUFFER or SEND-BUFFER has little effect. * Telling the client to make a binary-mode connection (SET TELNET BINARY REQUESTED, which successfully negotiates a binary connection) has no effect on throughput. BUT... If I start HP-UX C-Kermit as a TCP service: set host * 3000 server and then from the client "set host xxx 3000", I get: C-Kermit 6.0... C-Kermit 7.0... Local Remote ACK/NAK........ Streaming...... Client Server Send Receive Send Receive Sun HP 77 67 106 139 HP HP 50 50 64 62 HP Sun 57 85 155 105 Sun Sun 57 50 321 314 Therefore the HP-UX telnet server or pty driver seems to be adding more overhead than the SunOS one, and most others. When going through this type of connection (a remote telnet server) there is little Kermit can do improve matters, since the telnet server and pty driver are between the two Kermits, and neither Kermit program can have any influence over them (except putting the Telnet connection in binary mode, but that doesn't help). (The numbers for the HP-HP transfers are lower than the others since both Kermit processes are running on the same slow 33MHz CPU.) Matters seem to have deteriorated in HP-UX 11. Now file transfers over Telnet connections fail completely, rather than just being slow. In the following trial, a Telnet connection was made from Kermit 95 to HP-UX 11.11 on an HP-9000/785/B2000 over local 10Mbps Ethernet running C-Kermit 8.00 in server mode (under the HP-UX Telnet server): Text........ Binary...... Stream Pktlen GET SEND GET SEND On 4000 Fail Fail Fail Fail Off 4000 Fail Fail Fail Fail Off 2000 OK Fail OK Fail On 2000 OK Fail OK Fail On 3000 Fail Fail Fail Fail On 2500 Fail Fail Fail Fail On 2047 OK Fail OK Fail On 2045 OK Fail OK Fail Off 500 OK OK OK OK On 500 OK Fail OK Fail On 240 OK Fail OK Fail As you can see, downloads are problematic unless the receiver's Kermit packet length is 2045 or less, but uploads work only with streaming disabled and the packet length restricted to 500. To force file transfers to work on this connection, the desktop Kermit must be told to: set streaming off set receive packet-length 2000 set send packet-length 500 However, if a connection is made between the same two programs on the same two computers over the same network, but this time a direct socket-to-socket connection bypassing the HP-UX Telnet server and pty driver (tell HP-UX C-Kermit to "set host /server * 3000 /raw"; tell desktop client program to "set host blah 3000 /raw"), everything works perfectly with the default Kermit settings (streaming, 4K packets, liberal control-character unprefixing, 8-bit transparency, etc): Text........ Binary...... Stream Pktlen GET SEND GET SEND On 4000 OK OK OK OK And in this case, transfer rates were approximately 900,000 cps. To verify that the behavior reported here is not caused by the new Kermit release, the same experiment was performed on a Telnet connection from the same PC over the same network to the old 715/33 running HP-UX 10.20 and C-Kermit 8.00. Text and binary uploads and downloads worked perfectly (albeit slowly) with all the default settings -- streaming, 4K packets, etc. 3.2.3. Dialing Out and UUCP Lockfiles in HP-UX [ [223]Top ] [ [224]Contents ] [ [225]Section Contents ] [ [226]Next ] [ [227]Previous ] HP workstations do not come with dialout devices configured; you have to do it yourself (as root). First look in /dev to see what's there; for example in HP-UX 10.00 or later: ls -l /dev/cua* ls -l /dev/tty* If you find a tty0p0 device but no cua0p0, you'll need to creat one if you want to dial out; the tty0p0 does not work for dialing out. It's easy: start SAM; in the main Sam window, double-click on Peripheral Device, then in the Peripheral Devices window, double-click on Terminals and Modems. In the Terminals and Modems dialog, click on Actions, then choose "Add modem" and fill in the blanks. For example: Port number 0, speed 57600 (higher speeds tend not to work reliably), "Use device for calling out", do NOT "Receive incoming calls" (unless you know what you are doing), leave "CCITT modem" unchecked unless you really have one, and do select "Use hardware flow control (RTS/CTS)". Then click OK. This creates cua0p0 as well as cul0p0 and ttyd0p0 If the following sequence: set line /dev/cua0p0 ; or other device set speed 115200 ; or other normal speed produces the message "?Unsupported line speed". This means either that the port is not configured for dialout (go into SAM as described above and make sure "Use device for calling out" is selected), or else that speed you have given (such as 460800) is supported by the operating system but not by the physical device (in which case, use a lower speed like 57600). In HP-UX 9.0, serial device names began to change. The older names looked like "/dev/cua00", "/dev/tty01", etc (sometimes with only one digit). The newer names have two digits with the letter "p" in between. HP-UX 8.xx and earlier have the older form, HP-UX 10.00 and later have the newer form. HP-UX 9.xx has the newer form on Series 800 machines, and the older form on other hardware models. The situation is summarized in the following table (the Convio 10.0 column applies to HP-UX 10 and 11). Converged HP-UX Serial I/O Filenames : TTY Mux Naming --------------------------------------------------------------------- General meaning Old Form S800 9.0 Convio 10.0 --------------------------------------------------------------------- tty* hardwired ports tty ttyp ttyp

diag:mux diag:mux --------------------------------------------------------------------- ttyd* dial-in modems ttyd ttydp ttydp

diag:ttydp diag:ttydp

--------------------------------------------------------------------- cua* auto-dial out cua cuap cuap

diag:cuap --------------------------------------------------------------------- cul* dial-out cul culp culp

diag:culp --------------------------------------------------------------------- = LU (Logical Unit) = Devspec (decimal card instance) or = Port

= Port For dialing out, you should use the cua or cul devices. When C-Kermit's CARRIER setting is AUTO or ON, C-Kermit should pop back to its prompt automatically if the carrier signal drops, e.g. when you log out from the remote computer or service. If you use the ttyp (e.g. tty0p0) device, the carrier signal should be ignored. The ttyp device should be used for direct connections where the carrier signal does not follow RS-232 conventions (use the cul device for hardwired connections through a true null modem). Do not use the ttydp device for dialing out. Kermit's access to serial devices is controlled by "UUCP lockfiles", which are intended to prevent different users using different software programs (Kermit, cu, etc, and UUCP itself) from accessing the same serial device at the same time. When a device is in use by a particular user, a file with a special name is created in: /var/spool/locks (HP-UX 10.00 and later) /usr/spool/uucp (HP-UX 9.xx and earlier) The file's name indicates the device that is in use, and its contents indicates the process ID (pid) of the process that is using the device. Since serial devices and the locks directory are not both publicly readable and writable, Kermit and other communication software must be installed setuid to the owner (bin) of the serial device and setgid to the group (daemon) of the /var/spool/locks directory. Kermit's setuid and setgid privileges are enabled only when opening the device and accessing the lockfiles. Let's say "unit" means a string of decimal digits (the interface instance number) followed (in HP-UX 10.00 and later) by the letter "p" (lowercase), followed by another string of decimal digits (the port number on the interface), e.g.: "0p0", "0p1", "1p0", etc (HP-UX 10.00 and later) "0p0", "0p1", "1p0", etc (HP-UX 9.xx on Series 800) "00", "01", "10", "0", etc (HP-UX 9.xx not on Series 800) "00", "01", "10", "0", etc (HP-UX 8.xx and earlier) Then a normal serial device (driver) name consists of a prefix ("tty", "ttyd", "cua", "cul", or possibly "cuad" or "culd") followed by a unit, e.g. "cua0p0". Kermit's treatment of UUCP lockfiles is as close as possible to that of the HP-UX "cu" program. Here is a table of the lockfiles that Kermit creates for unit 0p0: Selection Lockfile 1 Lockfile 2 /dev/tty0p0 LCK..tty0p0 (none) * /dev/ttyd0p0 LCK..ttyd0p0 (none) /dev/cua0p0 LCK..cua0p0 LCK..ttyd0p0 /dev/cul0p0 LCK..cul0p0 LCK..ttyd0p0 /dev/cuad0p0 LCK..cuad0p0 LCK..ttyd0p0 /dev/culd0p0 LCK..culd0p0 LCK..ttyd0p0 LCK.. (none) (* = Dialin device, should not be used.) In other words, if the device name begins with "cu", a second lockfile for the "ttyd" device, same unit, is created, which should prevent dialin access on that device. The case allows for symbolic links, etc, but of course it is not foolproof since we have no way of telling which device is really being used. When C-Kermit tries to open a dialout device whose name ends with a "unit", it searches the lockfile directory for all possible names for the same unit. For example, if user selects /dev/cul2p3, Kermit looks for lockfiles named: LCK..tty2p3 LCK..ttyd2p3 LCK..cua2p3 LCK..cul2p3 LCK..cuad2p3 LCK..culd2p3 If any of these files are found, Kermit opens them to find out the ID (pid) of the process that created them; if the pid is still valid, the process is still active, and so the SET LINE command fails and the user is informed of the pid so s/he can use "ps" to find out who is using the device. If the pid is not valid, the file is deleted. If all such files (i.e. with same "unit" designation) are successfully removed, then the SET LINE command succeeds; up to six messages are printed telling the user which "stale lockfiles" are being removed. When the "set line" command succeeds in HP-UX 10.00 and later, C-Kermit also creates a Unix System V R4 "advisory lock" as a further precaution (but not guarantee) against any other process obtaining access to the device while you are using it. If the selected device was in use by "cu", Kermit can't open it, because "cu" has changed its ownership, so we never get as far as looking at the lockfiles. In the normal case, we can't even look at the device to see who the owner is because it is visible only to its (present) owner. In this case, Kermit says (for example): /dev/cua0p0: Permission denied When Kermit releases a device it has successfully opened, it removes all the lockfiles that it created. This also happens whenever Kermit exits "under its own power". If Kermit is killed with a device open, the lockfile(s) are left behind. The next Kermit program that tries to assign the device, under any of its various names, will automatically clean up the stale lockfiles because the pids they contain are invalid. The behavior of cu and other communication programs under these conditions should be the same. Here, by the way, is a summary of the differences between the HP-UX port driver types from John Pezzano of HP: There are three types of device files for each port. The ttydXXX device file is designed to work as follows: 1. The process that opens it does NOT get control of the port until CD is asserted. This was intentional (over 15 years ago) to allow getty to open the port but not control it until someone called in. If a process wants to use the direct or callout device files (ttyXXX and culXXX respectively), they will then get control and getty would be blocked. This eliminated the need to use uugetty (and its inherent problems with lock files) for modems. You can see this demonstrated by the fact that "ps -ef" shows a ? in the tty column for the getty process as getty does not have the port yet. 2. Once CD is asserted, the port is controlled by getty (or the process handling an incoming call) if there was no process using the port. The ? in the "ps" command now shows the port. At this point, the port accepts data. Therefore you should use either the callout culXXX device file (immediate control but no data until CD is asserted) or the direct device file ttyXXX which gives immediate control and immediate data and which ignores by default modem control signals. The ttydXXX device should be used only for callin and my recommendation is to use it only for getty and uugetty. 3.2.4 Notes on Specific HP-UX Releases SECTION CONTENTS 3.2.4.1. [228]HP-UX 11 3.2.4.2. [229]HP-UX 10 3.2.4.3. [230]HP-UX 9 3.2.4.4. [231]HP-UX 8 3.2.4.5. [232]HP-UX 7 and Earlier 3.2.4.1. HP-UX 11 [ [233]Top ] [ [234]Contents ] [ [235]Section Contents ] [ [236]Next ] As noted in [237]Section 3.2.2, the HP-UX 11 Telnet server and/or pseudoterminal driver are a serious impediment to file transfer over Telnet connections into HP-UX. If you have a Telnet connection into HP-UX 11, tell your desktop Kermit program to: set streaming off set receive packet-length 2000 set send packet-length 500 File transfer speeds over connections from HP-UX 11 (dialed or Telnet) are not impeded whatsoever, and can go at whatever speed is allowed by the connection and the Kermit partner on the far end. PA-RISC binaries for HP-UX 10.20 or later should run on any PA-RISC system, S700 or S800, as long as the binary was not built under a later HP-UX version than the host operating system. HP-UX 11.00 and 11.11 are only for PA-RISC systems. HP-UX 11.20 is only for IA64 (subsequent HP-UX releases will be for both PA-RISC and IA64). To check binary compatibility, the following C-Kermit 8.0 binaries were run successfully on an HP-9000/785 with HP-UX 11.11: * Model 7xx HP-UX 10.20 * Model 8xx HP-UX 10.20 * Model 7xx HP-UX 11.00 * Model 8xx HP-UX 11.00 * Model 7xx HP-UX 11.11 * Model 8xx HP-UX 11.11 Binaries built under some of the earlier HP-UX releases, such as 9.05, might also work, but only if built for the same hardware family (e.g. s700). 3.2.4.2. HP-UX 10 [ [238]Top ] [ [239]Contents ] [ [240]Section Contents ] [ [241]Next ] [ [242]Previous ] Beginning in HP-UX 10.10, libcurses is linked to libxcurses, the new UNIX95 (X/Open) version of curses, which has some serious bugs; some routines, when called, would hang and never return, some would dump core. Evidently libxcurses contains a select() routine, and whenever C-Kermit calls what it thinks is the regular (sockets) select(), it gets the curses one, causing a segmentation fault. There is a patch for this from HP, PHCO_8086, "s700_800 10.10 libcurses patch", "shared lib curses program hangs on 10.10", "10.10 enhanced X/Open curses core dumps due to using wrong select call", 96/08/02 (you can tell if the patch is installed with "what /usr/lib/libxcurses.1"; the unpatched version is 76.20, the patched one is 76.20.1.2). It has been verified that C-Kermit works OK with the patched library, but results are not definite for HP-UX 10.20 or higher. To ensure that C-Kermit works even on non-patched HP-UX 10.10 systems, separate makefile entries are provided for HP-UX 10.00/10.01, 10.10, 10.20, etc, in which the entries for 10.10 and above link with libHcurses, which is "HP curses", the one that was used in 10.00/10.01. HP-UX 11.20 and later, however, link with libcurses, as libHcurses disappeared in 11.20. 3.2.4.3. HP-UX 9 [ [243]Top ] [ [244]Contents ] [ [245]Section Contents ] [ [246]Next ] [ [247]Previous ] HP-UX 9.00 and 9.01 need patch PHNE_10572 (note: this replaces PHNE_3641) for hptt0.o, asio0.o, and ttycomn.o in libhp-ux.a. Contact Hewlett Packard if you need this patch. Without it, the dialout device (tty) will be hung after first use; subsequent attempts to use will return an error like "device busy". (There are also equivalent patches for s700 9.03 9.05 9.07 (PHNE_10573) and s800 9.00 9.04 (PHNE_10416). When C-Kermit is in server mode, it might have trouble executing REMOTE HOST commands. This problem happens under HP-UX 9.00 (Motorola) and HP-UX 9.01 (RISC) IF the C-Shell is the login shell AND with the C-Shell Revision 70.15. Best thing is to install HP's Patch PHCO_4919 for Series 300/400 and PHCO_5015 for the Series 700/800. PHCO_5015 is called "s700_800 9.X cumulative csh(1) patch with memory leak fix" which works for HP-UX 9.00, 9.01, 9.03, 9.04, 9.05 and 9.07. At least you need C-Shell Revision 72.12! C-Kermit works fine -- including its curses-based file-transfer display -- on the console terminal, in a remote session (e.g. when logged in to the HP 9000 on a terminal port or when telnetted or rlogin'd), and in an HP-VUE hpterm window or an xterm window. 3.2.4.4. HP-UX 8 [ [248]Top ] [ [249]Contents ] [ [250]Section Contents ] [ [251]Next ] [ [252]Previous ] To make C-Kermit work on HP-UX 8.05 on a model 720, obtain and install HP-UX patch PHNE_0899. This patch deals with a lot of driver issues, particularly related to communication at higher speeds. One user reports: On HP-UX 8 DON'T install 'tty patch' PHKL_4656, install PHKL_3047 instead! Yesterday I tried this latest tty patch PHKL_4656 and had terrible problems. This patch should fix RTS/CTS problems. With text transfer all looks nice. But when I switched over to binary files the serial interface returned only rubish to C-Kermit. All sorts of protocol, CRC and packed errors I had. After several tests and after uninstalling that patch, all transfers worked fine. MB's of data without any errors. So keep your fingers away from that patch. If anybody needs the PHKL_3047 patch I have it here. It is no longer available from HP's patch base. 3.2.4.5. HP-UX 7 and Earlier [ [253]Top ] [ [254]Contents ] [ [255]Section Contents ] [ [256]Previous ] When transferring files into HP-UX 5 or 6 over a Telnet connection, you must not use streaming, and you must not use a packet length greater than 512. However, you can use streaming and longer packets when sending files from HP-UX on a Telnet connection. In C-Kermit 8.0, the default receive packet length for HP-UX 5 and 6 was changed to 500 (but you can still increase it with SET RECEIVE PACKET-LENGTH if you wish, e.g. for non-Telnet connections). Disable streaming with SET STREAMING OFF. The HP-UX 5.00 version of C-Kermit does not include the fullscreen file-transfer because of problems with the curses library. If HP-UX 5.21 with Wollongong TCP/IP is on the remote end of a Telnet connection, streaming transfers to HP-UX invariably fail. Workaround: SET STREAMING OFF. Packets longer than about 1000 should not be used. Transfers from these systems, however, can use streaming and/or longer packets. Reportedly, "[there is] a bug in C-Kermit using HP-UX version 5.21 on the HP-9000 series 500 computers. It only occurs when the controlling terminal is using an HP-27140 six-port modem mux. The problem is not present if the controlling terminal is logged into an HP-27130 eight-port mux. The symptom is that just after dialing successfully and connecting Kermit locks up and the port is unusable until both forks of Kermit and the login shell are killed." (This report predates C-Kermit 6.0 and might no longer apply.) 3.2.5. HP-UX and X.25 [ [257]Top ] [ [258]Contents ] [ [259]Section Contents ] [ [260]Previous ] Although C-Kermit presently does not include built-in support for HP-UX X.25 (as it does for the Sun and IBM X.25 products), it can still be used to make X.25 connections as follows: start Kermit and then telnet to localhost. After logging back in, start padem as you would normally do to connect over X.25. Padem acts as a pipe between Kermit and X.25. In C-Kermit 7.0, you might also be able to avoid the "telnet localhost" step by using: C-Kermit> pty padem address This works if padem uses standard i/o (who knows?). 3.3. C-KERMIT AND LINUX [ [261]Top ] [ [262]Contents ] [ [263]Section Contents ] [ [264]Next ] [ [265]Previous ] SECTION CONTENTS 3.3.1. [266]Problems Building C-Kermit for Linux 3.3.2. [267]Problems with Serial Devices in Linux 3.3.3. [268]Terminal Emulation in Linux 3.3.4. [269]Dates and Times 3.3.5. [270]Startup Errors 3.3.6. [271]The Fullscreen File Transfer Display (August 2010) Reportedly C-Kermit packages for certain Linux distributions such as Centos and Ubuntu have certain features disabled, for example the SSH command, SET HOST PTY /SSH, and perhaps anything else to do with SSH and/or pseudoterminals and who knows what else. If you download the regular package ("tarball") from the Kermit Project and build from it ("make linux"), everything is fine. C-Kermit in Ubuntu 10.04 and 9.10 was reported slow to start because it was trying to resolve the IP address 255.255.255.255. Later, also in recent Debian versions. The following is seen in the strace: write(3, "RESOLVE-ADDRESS 255.255.255.255\n", 32) This is not Kermit Project code. Turns out to be something in glibc's resolver, and can be fixed by changing /etc/nsswitch.conf, but it might break other software, such as [272]Avahi or anything (such as Gnome, Java, or Cups) that depends on it. I'm not sure where it happens; I don't think Kermit tries to get its IP address at startup time, only when it's needed or asked for, e.g. when making a connection or evaluating \v(ipaddress). REFERENCES For further information, read the [273]comp.os.linux.misc, [274]comp.os.linux.answers, and other Linux-oriented newsgroups, and see: The Linux Document Project (LDP) [275]http://www.tldp.org/ The Linux FAQ [276]http://www.tldp.org/FAQ/Linux-FAQ.html The Linux HOWTOs (especially the Serial HOWTO) [277]http://www.tldp.org/HOWTO/Serial-HOWTO.html [278]http://tldp.org/HOWTO/Modem-HOWTO.html [279]ftp://sunsite.unc.edu/pub/Linux/docs/HOWTO [280]ftp://tsx-11.mit.edu/pub/linux/docs/HOWTO [281]http://www.tldp.org/HOWTO/ [282]http://www.tldp.org/hmirrors.html Linux Vendor Tech Support Pages: [283]http://www.redhat.com/apps/support/ [284]http://www.debian.org/support [285]http://www.slackware.com/support/ [286]http://www.caldera.com/support/ [287]SUSE Linux Support [288]http://www.mandrake.com/support/ [289]http://www.turbolinux.com/support/ Linux Winmodem Support [290]http://www.linmodems.org/ Also see general comments on PC-based Unixes in [291]Section 3.0. What Linux version is it? -- "uname -a" supplies only kernel information, but these days it's the distribution that matters: Red Hat 7.3, Debian 2.2, Slackware 8.0, etc. Unfortunately there's no consistent way to get the distribution version. Usually it's in a distribution-specific file: Red Hat: /etc/issue or /etc/redhat-release Debian: /etc/debian_version Slackware: /etc/slackware-version (at least in later versions) Did you know: DECnet is available for Linux? See: [292]http://linux.dreamtime.org/decnet/ (But there is no support for it in C-Kermit -- anybody interested in adding it, please [293]let me know). Before proceeding, let's handle the some of the most frequently asked question in the Linux newsgroups: 1. Neither C-Kermit nor any other Linux application can use Winmodems, except in the [294]rare cases where Linux drivers have been written for them. See [295]Section 3.0.2 for details. 2. "Why does it take such a long time to make a telnet connection to (or from) my Linux PC?" (this applies to C-Kermit and to regular Telnet). Most telnet servers these days perform reverse DNS lookups on the client (for security and/or logging reasons). If the Telnet client's address cannot be found by the server's local DNS server, the DNS request goes out to the Internet at large, and this can take quite some time. The solution to this problem is to make sure that both client and host are registered in DNS, and that the registrations are exported. C-Kermit itself performs reverse DNS lookups unless you tell it not to; this is to allow C-Kermit to let you know which host it is actually connected to in case you have made a connection to a host pool (multihomed host). You can disable C-Kermit's reverse DNS lookup with SET TCP REVERSE-DNS-LOOKUP OFF. 3. (Any question that has the word "Telnet" in it...) The knee-jerk reaction is "don't use Telnet, use SSH!" There's nothing wrong with Telnet. In fact it's far superior to SSH as a protocol in terms of features and extensibility, not to mention platform neutrality. The issue lurking behind the knee-jerk reaction is security. SSH is thought to be secure, whereas Telnet is thought to be insecure. This is true for clear-text Telnet (because passwords travel in the clear across the network), but apparently few people realize that [296]secure Telnet clients and servers have been available for years, and these are more secure than SSH (for reasons explained [297]HERE). 4. (Any question that has the word "FTP" in it...) The knee-jerk reaction being "Don't use FTP, use SCP!" (or SFTP). Same answer as above, but moreso. SCP and SFTP are not only not platform neutral, they're diversity-hostile. They transfer files only in binary mode, which mangles text files across different platforms, to the same degree the platform's text-file record format and character set differ. An extreme example would be an Variable-Block format EBCDIC text file on an IBM mainframe, binary transfer of which to Unix would do you little good indeed. FTP was designed with diversity in mind and secure versions are available. 3.3.1. Problems Building C-Kermit for Linux [ [298]Top ] [ [299]Contents ] [ [300]Section Contents ] [ [301]Next ] Modern Linux distributions like Red Hat give you a choice at installation whether to include "developer tools". Obviously, you can't build C-Kermit or any other C program from source code if you have not installed the developer tools. But to confuse matters, you might also have to choose (separately) to install the "curses" or "ncurses" terminal control library; thus it is possible to install the C compiler and linker, but omit the (n)curses library and headers. If curses is not installed, you will not be able to build a version of C-Kermit that supports the fullscreen file-transfer display, in which case you'll need to use the "linuxnc" makefile target (nc = No Curses) or else install ncurses before building. There are all sorts of confusing issues caused by the many and varied Linux distributions. Some of the worst involve the curses library and header files: where are they, what are they called, which ones are they really? Other vexing questions involve libc5 vs libc6 vs glibc vs glibc2 (C libraries), gcc vs egcs vs lcc (compilers), plus using or avoiding features that were added in a certain version of Linux or a library or a distribution, and are not available in others. As of C-Kermit 8.0, these questions should be resolved by the "linux" makefile target itself, which does a bit of looking around to see what's what, and then sets the appropriate CFLAGS. 3.3.2. Problems with Serial Devices in Linux [ [302]Top ] [ [303]Contents ] [ [304]Section Contents ] [ [305]Next ] [ [306]Previous ] Also see: "man setserial", "man irqtune". And: [307]Sections 3.0, [308]6, [309]7, and [310]8 of this document. NOTE: Red Hat Linux 7.2 and later include a new API that allows serial-port arbitration by non-setuid/gid programs. This API has not yet been added to C-Kermit. If C-Kermit is to be used for dialing out on Red Hat 7.2 or later, it must still be installed as described in in Sections [311]10 and [312]11 of the [313]Installation Instructions. Don't expect it to be easy. Queries like the following are posted to the Linux newsgroups almost daily: Problem of a major kind with my Compaq Presario 1805 in the sense that the pnpdump doesn't find the modem and the configuration tells me that the modem is busy when I set everything by hand! I have , kernel 2.0.35. Using the Compaq tells me that the modem (which is internal) is on COM2, with the usual IRQ and port numbers. Running various Windows diagnostics show me AT-style commands exchanged so I have no reason to believe that it is a Winmodem. Also, the diagnostics under Win98 tell me that I am talking to an NS 16550AN. [Editor's note: This does not necessarily mean it isn't a Winmodem.] Under Linux, no joy trying to talk to the modem on /dev/cua1 whether via minicom, kppp, or chat; kppp at least tells me that tcgetattr() failed. Usage of setserial: setserial /dev/cua1 port 0x2F8 irq 3 autoconfig setserial -g /dev/cua1 tells me that the uart is 'unknown'. I have tried setting the UART manually via. setserial to 16550A, 16550, and the other one (8550?) (I didn't try 16540). None of these manual settings resulted in any success. A look at past articles leads me to investigate PNP issues by calling pnpdump but pnpdump returns "no boards found". I have looked around on my BIOS (Phoenix) and there is not much evidence of it being PNP aware. However for what it calls "Serial port A", it offers a choice of Auto, Disabled or Manual settings (currently set to Auto), but using the BIOS interface I tried to change to 'manual' and saw the default settings offered to be were 0x3F8 and IRQ 4 (COM1). The BIOS menus did not give me any chance to configure COM2 or any "modem". I ended up not saving any BIOS changes in the course of my investigations. You can also find out a fair amount about your PC's hardware configuration in the text files in /proc, e.g.: -r--r--r-- 1 root 0 Sep 4 14:00 /proc/devices -r--r--r-- 1 root 0 Sep 4 14:00 /proc/interrupts -r--r--r-- 1 root 0 Sep 4 14:00 /proc/ioports -r--r--r-- 1 root 0 Sep 4 14:00 /proc/pci From the directory listing they look like empty files, but in fact they are text files that you "cat": $ cat /proc/pci Bus 0, device 14, function 0: Serial controller: US Robotics/3Com 56K FaxModem Model 5610 (rev 1). IRQ 10. I/O at 0x1050 [0x1057]. $ setserial -g /dev/ttyS4 /dev/ttyS4, UART: 16550A, Port: 0x1050, IRQ: 10 $ cat /proc/ioports 1050-1057 : US Robotics/3Com 56K FaxModem Model 5610 1050-1057 : serial(auto) $ cat /proc/interrupts CPU0 0: 7037515 XT-PIC timer 1: 2 XT-PIC keyboard 2: 0 XT-PIC cascade 4: 0 XT-PIC serial 8: 1 XT-PIC rtc 9: 209811 XT-PIC usb-uhci, eth0 14: 282015 XT-PIC ide0 15: 6 XT-PIC ide1 Watch out for PCI, PCMCIA and Plug-n-Play devices, Winmodems, and the like (see cautions in [314]Section 3.0 Linux supports Plug-n-Play devices to some degree via the isapnp and pnpdump programs; read the man pages for them. (If you don't have them, look on your installation CD for isapnptool or download it from sunsite or a sunsite mirror or other politically correct location du jour). PCI modems do not use standard COM port addresses. The I/O address and IRQ are assigned by the BIOS. All you need to do to get one working, find out the I/O address and interrupt number with (as root) "lspci -v | more" and then give the resulting address and interrupt number to setserial. Even when you have a real serial port, always be wary of interrupt conflicts and similar PC hardware configuration issues: a PC is not a real computer like other Unix workstations -- it is generally pieced together from whatever random components were the best bargain on the commodity market the week it was built. Once it's assembled and boxed, not even the manufacturer will remember what it's made of or how it was put together because they've moved on to a new model. Their job is to get it (barely) working with Windows; for Linux and other OS's you are on your own. "set line /dev/modem" or "set line /dev/ttyS2", etc, results in an error, "/dev/modem is not a tty". Cause unknown, but obviously a driver issue, not a Kermit one (Kermit uses "isatty()" to check that the device is a tty, so it knows it will be able to issue all the tty-related ioctl's on it, like setting the speed & flow control). Try a different name (i.e. driver) for the same port, e.g. "set line /dev/cua2" or whatever. To find what serial ports were registered at the most recent system boot, type (as root): "grep tty /var/log/dmesg". "set modem type xxx" (where xxx is the name of a modem) followed by "set line /dev/modem" or "set line /dev/ttyS2", etc, hangs (but can be interrupted with Ctrl-C). Experimentation shows that if the modem is configured to always assert carrier (&C0) the same command does not hang. Again, a driver issue. Use /dev/cua2 (or whatever) instead. (Or not -- hopefully none of these symptoms occurs in C-Kermit 7.0 or later.) "set line /dev/cua0" reports "Device is busy", but "set line /dev/ttyS0" works OK. In short: If the cua device doesn't work, try the corresponding ttyS device. If the ttyS device doesn't work, try the corresponding cua device -- but note that Linux developers do not recommend this, and are phasing out the cua devices. From /usr/doc/faq/howto/Serial-HOWTO: 12.4. What's The Real Difference Between the /dev/cuaN And /dev/ttySN Devices? The only difference is the way that the devices are opened. The dialin devices /dev/ttySN are opened in blocking mode, until CD is asserted (ie someone connects). So, when someone wants to use the /dev/cuaN device, there is no conflict with a program watching the /dev/ttySN device (unless someone is connected of course). The multiple /dev entries, allow operation of the same physical device with different operating characteristics. It also allows standard getty programs to coexist with any other serial program, without the getty being retrofitted with locking of some sort. It's especially useful since standard Unix kernel file locking, and UUCP locking are both advisory and not mandatory. It was discovered during development of C-Kermit 7.0 that rebuilding C-Kermit with -DNOCOTFMC (No Close/Open To Force Mode Change) made the aforementioned problem with /dev/ttyS0 go away. It is not yet clear, however, what its affect might be on the /dev/cua* devices. As of 19 March 1998, this option has been added to the CFLAGS in the makefile entries for Linux ("make linux"). Note that the cua device is now "deprecated", and new editions of Linux will phase (have phased) it out in favor of the ttyS device. See (if it's still there): [315]http://linuxwww.db.erau.edu/mail_archives/linux-kernel/Mar_98/1441.html (no, of course it isn't; you'll have to use your imagination). One user reported that C-Kermit 7.0, when built with egcs 1.1.2 and run on Linux 2.2.6 with glibc 2.1 (hardware unknown but probably a PC) dumps core when given a "set line /dev/ttyS1" command. When rebuilt with gcc, it works fine. All versions of Linux seem to have the following deficiency: When a modem call is hung up and CD drops, Kermit can no longer read the modem signals; SHOW COMMUNICATIONS says "Modem signals not available". The TIOCMGET ioctl() returns -1 with errno 5 ("I/O Error"). The Linux version of POSIX tcsendbreak(), which is used by C-Kermit to send regular (275msec) and long (1.5sec) BREAK signals, appears to ignore its argument (despite its description in the man page and info topic), and always sends a regular 275msec BREAK. This has been observed in Linux versions ranging from Debian 2.1 to Red Hat 7.1. 3.3.3. Terminal Emulation in Linux [ [316]Top ] [ [317]Contents ] [ [318]Section Contents ] [ [319]Next ] [ [320]Previous ] C-Kermit is not a terminal emulator. For a brief explanation of why not, see [321]Section 3.0.5. For a fuller explanation, [322]ClICK HERE. In Unix, terminal emulation is supplied by the Window in which you run Kermit: the regular console screen, which provides Linux Console "emulation" via the "console" termcap entry, or under X-Windows in an xterm window, which gives VTxxx emulation. An xterm that includes color ANSI and VT220 emulation is available with Xfree86: [323]http://dickey.his.com/xterm/xterm.html Before starting C-Kermit in an xterm window, you might need to tell the xterm window's shell to "stty sane". To set up your PC console keyboard to send VT220 key sequences when using C-Kermit as your communications program in an X terminal window (if it doesn't already), create a file somewhere (e.g. in /root/) called .xmodmaprc, containing something like the following: keycode 77 = KP_F1 ! Num Lock => DEC Gold (PF1) keycode 112 = KP_F2 ! Keypad / => DEC PF1 keycode 63 = KP_F3 ! Keypad * => DEC PF3 keycode 82 = KP_F4 ! Keypad - => DEC PF4 keycode 111 = Help ! Print Screen => DEC Help keycode 78 = F16 ! Scroll Lock => DEC Do keycode 110 = F16 ! Pause => DEC Do keycode 106 = Find ! Insert => DEC Find keycode 97 = Insert ! Home => DEC Insert keycode 99 = 0x1000ff00 ! Page Up => DEC Remove keycode 107 = Select ! Delete => DEC Select keycode 103 = Page_Up ! End => DEC Prev Screen keycode 22 = Delete ! Backspace sends Delete (127) Then put "xmodmap filename" in your .xinitrc file (in your login directory), e.g. xmodmap /root/.xmodmaprc Of course you can move things around. Use the xev program to find out key codes. Console-mode keys are mapped separately using loadkeys, and different keycodes are used. Find out what they are with showkey. For a much more complete VT220/320 key mapping for [324]Xfree86 xterm, [325]CLICK HERE. 3.3.4. Dates and Times [ [326]Top ] [ [327]Contents ] [ [328]Section Contents ] [ [329]Next ] [ [330]Previous ] If C-Kermit's date-time (e.g. as shown by its DATE command) differs from the system's date and time: a. Make sure the libc to which Kermit is linked is set to GMT or is not set to any time zone. Watch out for mixed libc5/libc6 systems; each must be set independently. b. If you have changed your TZ environment variable, make sure it is exported. This is normally done in /etc/profile or /etc/TZ. 3.3.5. Startup Errors [ [331]Top ] [ [332]Contents ] [ [333]Section Contents ] [ [334]Next ] [ [335]Previous ] C-Kermit should work on all versions of Linux current through March 2003, provided it was built on the same version you have, with the same libraries and header files (just get the source code and "make linux"). Binaries tend not to travel well from one Linux machine to another, due to their many differences. There is no guarantee that a particular C-Kermit binary will not stop working at a later date, since Linux tends to change out from under its applications. If that happens, rebuild C-Kermit from source. If something goes wrong with the build process, look on the [336]C-Kermit website for a newer version. If you have the latest version, then [337]report the problem to us. Inability to transfer files in Red Hat 7.2: the typical symptom would be if you start Kermit and tell it to RECEIVE, it fails right away with "?/dev/tty: No such device or address" or "?Bad file descriptor". One report says this is because of csh, and if you change your shell to bash or other shell, it doesn't happen. Another report cite bugs in Red Hat 7.2 Telnetd "very seldom (if ever) providing a controlling tty, and lots of other people piled on saying they have the same problem.") A third theory is that this happens only when Linux has been installed without "virtual terminal support". A search of RedHat's errata pages shows a bug advisory (RHBA-2001-153) issued 13 November 2001, but updated 6 December, about this same symptom (but with tcsh and login.) Seems that login was not always assigning a controlling TTY for the session, which would make most use of "/dev/tty" somewhat less than useful. [338]http://www.redhat.com/support/errata/RHBA-2001-153.html Quoting: "Due to terminal handling problems in /bin/login, tcsh would not find the controlling terminal correctly, and a shell in single user mode would exhibit strange terminal input characteristics. This update fixes both of these problems." Since the Red Hat 5.1 release (circa August 1998), there have been numerous reports of prebuilt Linux executables, and particularly the Kermit RPM for Red Hat Linux, not working; either it won't start at all, or it gives error messages about "terminal type unknown" and refuses to initialize its curses support. The following is from the [339]Kermit newsgroup: From: rchandra@hal9000.buf.servtech.com Newsgroups: comp.protocols.kermit.misc Subject: Red Hat Linux/Intel 5.1 and ncurses: suggestions Date: 22 Aug 1998 15:54:46 GMT Organization: Verio New York Keywords: RedHat RPM 5.1 Several factors can influence whether "linux" is recognized as a terminal type on many Linux systems. 1. Your program, or the libraries it linked with (if statically linked), or the libraries it dynamically links with at runtime, are looking for an entry in /etc/termcap that isn't there. (not likely, but possible... I believe but am not certain that this is a very old practice in very old [n]curses library implementations to use a single file for all terminal descriptions.) 2. Your program, or the libraries...are looking for a terminfo file that just plain isn't there. (also not so likely, since many people in other recent message threads said that other programs work OK). 3. Your program, or the libraries...are looking for a terminfo file that is stored at a pathname that isn't expected by your program, the libraries--and so on. I forgot if I read this in the errata Web page or where exactly I discovered this (Netscape install? Acrobat install?), but it may just be that one libc (let's say for sake of argument, libc5, but I don't know this to be true) expects your terminfo to be in /usr/share/terminfo, and the other (let's say libc6/glibc) expects /usr/lib/terminfo. I remember that the specific instructions in this bugfix/workaround were to do the following or equivalent: cd /usr/lib ln -s ../share/terminfo ./terminfo or: ln -s /usr/share/terminfo /usr/lib/terminfo So what this says is that the terminfo database/directory structure can be accessed by either path. When something goes to reference /usr/lib/terminfo, the symlink redirects it to essentially /usr/share/terminfo, which is where it really resides on your system. I personally prefer wherever possible to use relative symlinks, because they still hold, more often than break, across mount points, particularly NFS mounts, where the directory structure may be different on the different systems. Evidently the terminfo file moved between Red Hat 5.0 and 5.1, but Red Hat did not include a link to let applications built prior to 5.1 find it. Users reported that installing the link fixes the problem. 3.3.6. The Fullscreen File Transfer Display [ [340]Top ] [ [341]Contents ] [ [342]Section Contents ] [ [343]Previous ] Starting with ncurses versions dated 1998-12-12 (about a year before ncurses 5.0), ncurses sets the terminal for buffered i/o, but unfortunately is not able to restore it upon exit from curses (via endwin()). Thus after a file transfer that uses the fullscreen file transfer display, the terminal no longer echos nor responds immediately to Tab, ?, and other special command characters. The same thing happens on other platforms that use ncurses, e.g. FreeBSD. Workarounds: * Use SET XFER DISPLAY BRIEF, CRT, SERIAL, or NONE instead of FULLSCREEN; or: * Rebuild with KFLAGS=-DNONOSETBUF (C-Kermit 8.0) In Red Hat 7.1, when using C-Kermit in a Gnome terminal window, it was noticed that when the fullscreen file transfer display exits (via endwin()), the previous (pre-file-transfer-display) screen is restored. Thus you can't look at the completed display to see what happened. This is a evidently a new feature of xterm. I can only speculate that initscreen() and endwin() must send some kind of special escape sequences that command xterm to save and restore the screen. To defeat this effect, tell Linux you have a vt100 or other xterm-compatible terminal that is not actually an xterm, or else tell Kermit to SET TRANSFER DISPLAY to something besides FULLSCREEN. 3.4. C-KERMIT AND NEXTSTEP [ [344]Top ] [ [345]Contents ] [ [346]Section Contents ] [ [347]Next ] [ [348]Previous ] Run C-Kermit in a Terminal, Stuart, or xterm window, or when logged in remotely through a serial port or TELNET connection. C-Kermit does not work correctly when invoked directly from the NeXTSTEP File Viewer or Dock. This is because the terminal-oriented gtty, stty, & ioctl calls don't work on the little window that NeXTSTEP pops up for non-NeXTSTEP applications like Kermit. CBREAK and No-ECHO settings do not take effect in the command parser -- commands are parsed strictly line at a time. "set line /dev/cua" works. During CONNECT mode, the console stays in cooked mode, so characters are not transmitted until carriage return or linefeed is typed, and you can't escape back. If you want to run Kermit directly from the File Viewer, then launch it from a shell script that puts it in the desired kind of window, something like this (for "Terminal"): Terminal -Lines 24 -Columns 80 -WinLocX 100 -WinLocY 100 $FONT $FONTSIZE \ -SourceDotLogin -Shell /usr/local/bin/kermit & C-Kermit does not work correctly on a NeXT with NeXTSTEP 3.0 to which you have established an rlogin connection, due to a bug in NeXTSTEP 3.0, which has been reported to NeXT. The SET CARRIER command has no effect on the NeXT -- this is a limitation of the NeXTSTEP serial-port device drivers. Hardware flow control on the NeXT is selected not by "set flow rts/cts" in Kermit (since NeXTSTEP offers no API for this), but rather, by using a specially-named driver for the serial device: /dev/cufa instead /dev/cua; /dev/cufb instead of /dev/cub. This is available only on 68040-based NeXT models (the situation for Intel NeXTSTEP implementations is unknown). NeXT-built 68030 and 68040 models have different kinds of serial interfaces; the 68030 has a Macintosh-like RS-422 interface, which lacks RTS and CTS signals; the 68040 has an RS-423 (RS-232 compatible) interface, which supports the commonly-used modem signals. WARNING: the connectors look exactly the same, but the pins are used in completely DIFFERENT ways -- different cables are required for the two kinds of interfaces. IF YOU GET LOTS OF RETRANSMISSIONS during file transfer, even when using a /dev/cuf* device and the modem is correctly configured for RTS/CTS flow control, YOU PROBABLY HAVE THE WRONG KIND OF CABLE. On the NeXT, Kermit reportedly (by TimeMon) causes the kernel to use a lot of CPU time when using a "set line" connection. That's because there is no DMA channel for the NeXT serial port, so the port must interrupt the kernel for each character in or out. One user reported trouble running C-Kermit on a NeXT from within NeXT's Subprocess class under NeXTstep 3.0, and/or when rlogin'd from one NeXT to another: Error opening /dev/tty:, congm: No such device or address. Diagnosis: Bug in NeXTSTEP 3.0, cure unknown. 3.5. C-KERMIT AND QNX [ [349]Top ] [ [350]Contents ] [ [351]Section Contents ] [ [352]Next ] [ [353]Previous ] See also: The [354]comp.os.qnx newsgroup. Support for QNX 4.x was added in C-Kermit 5A(190). This is a full-function implementation, thoroughly tested on QNX 4.21 and later, and verified to work in both 16-bit and 32-bit versions. The 16-bit version was dropped in C-Kermit 7.0 since it can no longer be built successfully (after stripping most most features, I succeeded in getting it to compile and link without complaint, but the executable just beeps when you run it); for 16-bit QNX 4.2x, use C-Kermit 6.0 or earlier, or else [355]G-Kermit. The 32-bit version (and the 16-bit version prior to C-Kermit 7.0) supports most of C-Kermit's advanced features including TCP/IP, high serial speeds, hardware flow-control, modem-signal awareness, curses support, etc. BUG: In C-Kermit 6.0 on QNX 4.22 and earlier, the fullscreen file transfer display worked fine the first time, but was fractured on subsequent file transfers. Cause and cure unknown. In C-Kermit 7.0 and QNX 4.25, this no longer occurs. It is not known if it would occur in C-Kermit 7.0 or later on earlier QNX versions. Dialout devices are normally /dev/ser1, /dev/ser2, ..., and can be opened explicitly with SET LINE. Reportedly, "/dev/ser" (no unit number) opens the first available /dev/sern device. Like all other Unix C-Kermit implementations, QNX C-Kermit does not provide any kind of terminal emulation. Terminal specific functions are provided by your terminal, terminal window (e.g. QNX Terminal or xterm), or emulator. QNX C-Kermit, as distributed, does not include support for UUCP line-locking; the QNX makefile entries (qnx32 and qnx16) include the -DNOUUCP switch. This is because QNX, as distributed, does not include UUCP, and its own communications software (e.g. qterm) does not use UUCP line locking. If you have a UUCP product installed on your QNX system, remove the -DNOUUCP switch from the makefile entry and rebuild. Then check to see that Kermit's UUCP lockfile conventions are the same as those of your UUCP package; if not, read the [356]UUCP lockfile section of the [357]Installation Instructions and make the necessary changes to the makefile entry (e.g. add -DHDBUUCP). QNX does, however, allow a program to get the device open count. This can not be a reliable form of locking unless all applications do it, so by default, Kermit uses this information only for printing a warning message such as: C-Kermit>set line /dev/ser1 WARNING - "/dev/ser1" looks busy... However, if you want to use it as a lock, you can do so with: SET QNX-PORT-LOCK { ON, OFF } This is OFF by default; if you set in ON, C-Kermit will fail to open any dialout device when its open count indicates that another process has it open. SHOW COMM (in QNX only) displays the setting, and if you have a port open, it also shows the open count. As of C-Kermit 8.0, C-Kermit's "open-count" form of line locking works only in QNX4, not in QNX6 (this might change in a future C-Kermit release). 3.6. C-KERMIT AND SCO [ [358]Top ] [ [359]Contents ] [ [360]Section Contents ] [ [361]Next ] [ [362]Previous ] SECTION CONTENTS 3.6.1. [363]SCO XENIX 3.6.2. [364]SCO UNIX and OSR5 3.6.3. [365]Unixware 3.6.4. [366]Open UNIX 8 REFERENCES * The comp.unix.sco.* newsgroups. * [367]Section 3.10 below for Unixware. * The following FAQs: The comp.sco.misc FAQ: [368]http://aplawrence.com/SCOFAQ/ Caldera (SCO) comp.unix.sco.programmer FAQ: [369]http://www.zenez.com/cgi-bin/scoprogfaq/faq.pl The UnixWare 7/OpenUNIX 8 FAQ: [370]http://www.zenez.com/cgi-bin/scouw7faq/faq.pl [371]http://zenez.pcunix.com/cgi-bin/scouw7faq/faq.pl High Speed Modems for SCO Unix: [372]http://pcunix.com/Unixart/modems.html The UnixWare FAQ [373]http://www.freebird.org/faq/ The UnixWare 1.x and 2.0 Programmer FAQ [374]http://www.freebird.org/faq/developer.html Caldera Support Knowledge Base [375]http://support.caldera.com/caldera [376]http://stage.caldera.com/ta/ Caldera (SCO) Technical Article Search Center [377]http://aplawrence.com/newtosco.html New to SCO (Tony Lawrence) The same comments regarding terminal emulation and key mapping apply to SCO operating systems as to all other Unixes. C-Kermit is not a terminal emulator, and you can't use it to map F-keys, Arrow keys, etc. The way to do this is with xmodmap (xterm) or loadkeys (console). For a brief explanation, see [378]Section 3.0.5. For a fuller explanation, [379]ClICK HERE. Also see general comments on PC-based Unixes in [380]Section 3.0. 3.6.1. SCO XENIX [ [381]Top ] [ [382]Contents ] [ [383]Section Contents ] [ [384]Next ] Old Xenix versions... Did you know: Xenix 3.0 is *older* than Xenix 2.0? In Xenix 2.3.4 and probably other Xenix versions, momentarily dropping DTR to hang up a modem does not work. DTR goes down but does not come up again. Workaround: Use SET MODEM HANGUP-METHOD MODEM-COMMAND. Anybody who would like to fix this is welcome to take a look at tthang() in [385]ckutio.c. Also: modem signals can not be read in Xenix, and the maximum serial speed is 38400. There is all sorts of confusion among SCO versions, particularly when third- party communications boards and drivers are installed, regarding lockfile naming conventions, as well as basic functionality. As far as lockfiles go, all bets are off if you are using a third-party multiport board. At least you have the source code. Hopefully you also have a C compiler :-) Xenix 2.3.0 and later claim to support RTSFLOW and CTSFLOW, but this is not modern bidirectional hardware flow control; rather it implements the original RS-232 meanings of these signals for unidirectional half-duplex line access: If both RTSFLOW and CTSFLOW bits are set, Xenix asserts RTS when it wants to send data and waits for CTS assertion before it actually starts sending data (also, reportedly, even this is broken in Xenix 2.3.0 and 2.3.1). 3.6.2. SCO UNIX AND OSR5 [ [386]Top ] [ [387]Contents ] [ [388]Section Contents ] [ [389]Next ] [ [390]Previous ] SCO systems tend to use different names (i.e. drivers) for the same device. Typically /dev/tty1a refers to a terminal device that has no modem control; open, read, write, and close operations do not depend on carrier. On the other hand, /dev/tty1A (same name, but with final letter upper case), is the same device with modem control, in which carrier is required (the SET LINE command does not complete until carrier appears, read/write operations fail if there is no carrier, etc). SCO OpenServer 5.0.5 and earlier do not support the reading of modem signals. Thus "show comm" does not list modem signals, and C-Kermit does not automatically pop back to its prompt when the modem hangs up the connection (drops CD). The ioctl() call for this is simply not implemented, at least not in the standard drivers. OSR5.0.6 attempts to deal with modem signals but fails; however OSR5.0.6a appears to function properly. Dialing is likely not to work well in SCO OpenServer 5.0.x because many of the serial-port APIs simply do not operate when using the standard drivers. For example, if DTR is dropped by the recommended method (setting speed to 0 for half a seconds, then restoring the speed), DTR and RTS go down but never come back up. When in doubt SET MODEM HANGUP-METHOD MODEM-COMMAND or SET DIAL HANGUP OFF. On the other hand, certain functions that might not (do not) work right or at all when using SCO drivers (e.g. high serial speeds, hardware flow control, and/or reading of modem signals) might work right when using third-party drivers. (Example: hardware flow control works, reportedly, only on uppercase device like tty1A -- not tty1a -- and only when CLOCAL is clear when using the SCO sio driver, but there are no such restrictions in, e.g., [391]Digiboard drivers). One user reports that he can't transfer large files with C-Kermit under SCO OSR5.0.0 and 5.0.4 -- after the first 5K, everything falls apart. Same thing without Kermit -- e.g. with ftp over a PPP connection. Later, he said that replacing SCO's SIO driver with FAS, an alternative communications driver, made the problem go away: [392]ftp://ftp.fu-berlin.de/pub/unix/driver/fas With regard to bidirectional serial ports on OpenServer 5.0.4, the following advice appeared on an SCO-related newsgroup: No amount of configuration information is going to help you on 5.0.4 unless it includes the kludge for the primary problem. With almost every modem, the 5.0.4 getty will barf messages and may or may not connect. There are 2 solutions and only one works on 5.0.4. Get the atdialer binary from a 5.0.0 system and substitute it for the native 5.0.4 atdialer. The other solution is to upgrade to 5.0.5. And, most of all, on any OpenServer products, do NOT run the badly broken Modem Manager. Configure the modems in the time honored way that dates back to Xenix. Use SCO-provided utilities for switching the directionality of a modem line, such as "enable" and "disable" commands. For example, to dial out on tty1a, which is normally set up for logins: disable tty1a kermit -l /dev/tty1a enable tty1a If a tty device is listed as an ACU in /usr/lib/uucp/Devices and is enabled, getty resets the ownership and permissions to uucp.uucp and 640 every time the device is released. If you want to use the device only for dialout, and you want to specify other owners or permissions, you should disable it in /usr/lib/uucp/Devices; this will prevent getty from doing things to it. You should also changes the device's file modes in /etc/conf/node.d/sio by changing fields 5-7 for the desired device(s); this determines how the devices are set if you relink the kernel. One SCO user of C-Kermit 5A(190) reported that only one copy of Kermit can run at a time when a Stallion Technologies multiport boards are installed. Cause, cure, and present status unknown (see [393]Section 14 for more info regarding Stallion). Prior to SCO OpenServer 5.0.4, the highest serial port speed supported by SCO was 38400. However, in some SCO versions (e.g. OSR5) it is possible to map rarely-used lower speeds (like 600 and 1800) to higher ones like 57600 and 115200. To find out how, go to [394]http://www.sco.com/ and search for "115200". In OSR5.0.4, serial speeds up to 921600 are supported through the POSIX interface; C-Kermit 6.1.193 or later, when built for OSR5.0.4 using /bin/cc (NOT the UDK, which hides the high-speed definitions from CPP), supports these speeds, but you might be able to run this binary on earlier releases to get the high serial speeds, depending on various factors, described by Bela Lubkin of SCO: Serial speeds under SCO Unix / Open Desktop / OpenServer ======================================================== Third party drivers (intelligent serial boards) may provide any speeds they desire; most support up to 115.2Kbps. SCO's "sio" driver, which is used to drive standard serial ports with 8250/16450/16550 and similar UARTs, was limited to 38400bps in older releases. Support for rates through 115.2Kbps was added in the following releases: SCO OpenServer Release 5.0.0 (requires supplement "rs40b") SCO OpenServer Release 5.0.2 (requires supplement "rs40a" or "rs40b") SCO OpenServer Release 5.0.4 or later SCO Internet FastStart Release 1.0.0 or later SCO supplements are at [395]ftp://ftp.sco.com/; the "rs40" series are under directory /Supplements/internet Kermit includes the high serial speeds in all OSR5 builds, but that does not necessarily mean they work. For example, on our in-house 5.0.5 system, SET SPEED 57600 or higher seems to succeed (no error occurs) but when we read the speed back the driver says it is 50. Similarly, 76800 becomes 75, and 115200 becomes 110. Testing shows the resulting speed is indeed the low one we read back, not the high one we asked for. Moral: Use speeds higher than 38400 with caution on SCO OSR5. Reportedly, if you have a script that makes a TCP/IP SET HOST (e.g. Telnet) connection to SCO 3.2v4.2 with TCP/IP 1.2.1, and then does the following: script $ exit hangup this causes a pseudoterminal (pty) to be consumed on the SCO system; if you do it enough times, it will run out of ptys. An "exit" command is being sent to the SCO shell, and a HANGUP command is executed locally, so the chances are good that both sides are trying to close the connection at once, perhaps inducing a race condition in which the remote pty is not released. It was speculated that this would be fixed by applying SLS net382e, but it did not. Meanwhile, the workaround is to insert a "pause" between the SCRIPT and HANGUP commands. (The situation with later SCO releases is not known.) SCO UNIX and OpenServer allow their console and/or terminal drivers to be configured to translate character sets for you. DON'T DO THIS WHEN USING KERMIT! First of all, you don't need it -- Kermit itself already does this for you. And second, it will (a) probably ruin the formatting of your screens (depending on which emulation you are using); and (b) interfere with all sorts of other things -- legibility of non-ASCII text on the terminal screen, file transfer, etc. Use: mapchan -n to turn off this feature. Note that there is a multitude of SCO entries in the makefile, many of them exhibiting an unusually large number of compiler options. Some people actually understand all of this. Reportedly, things are settling down with SCO OpenServer 5.x and Unixware 7 (and Open UNIX 8 and who knows what the next one will be -- Linux probably) -- the SCO UDK compiler is said to generate binaries that will run on either platform, by default, automatically. When using gcc or egcs, on the other hand, differences persist, plus issues regarding the type of binary that is generated (COFF, ELF, etc), and where and how it can run. All of this could stand further clarification by SCO experts. 3.6.3. Unixware [ [396]Top ] [ [397]Contents ] [ [398]Section Contents ] [ [399]Next ] [ [400]Previous ] Unixware changed hands several times before landing at SCO, and so has its [401]own section in this document. (Briefly: AT&T UNIX Systems Laboratories sold the rights to the UNIX name and to System V R4 (or R5?) to Novell; later Novell spun its UNIX division off into a new company called Univel, which eventually was bought by SCO, which later was bought by Caldera, which later sort of semi-spun-off SCO...) 3.6.4. Open UNIX 8 [ [402]Top ] [ [403]Contents ] [ [404]Section Contents ] [ [405]Previous ] SCO was bought by Caldera in 2000 or 2001 and evolved Unixware 7.1 into Caldera Open UNIX 8.00. It's just like Unixware 7.1 as far as Kermit is concerned (the Unixware 7.1 makefile target works for Open UNIX 8.00, and in fact a Unixware 7.1 Kermit binary built on Unixware 7.1 runs under OU8; a separate OU8 makefile target exists simply to generate an appropriate program startup herald). Open Unix is now defunct; subsequent releases are called UnixWare again (e.g. UnixWare 7.1.3). 3.7. C-KERMIT AND SOLARIS [ [406]Top ] [ [407]Contents ] [ [408]Section Contents ] [ [409]Next ] [ [410]Previous ] SECTION CONTENTS 3.7.1. [411]Serial Port Configuration 3.7.2. [412]Serial Port Problems 3.7.3. [413]SunLink X.25 3.7.4. [414]Sun Workstation Keyboard Mapping 3.7.5. [415]Solaris 2.4 and Earlier REFERENCES * The [416]comp.unix.solaris newsgroup * [417]http://access1.sun.com/ * [418]http://docs.sun.com/ * [419]http://www.sunhelp.com/ * [420]http://www.wins.uva.nl/pub/solaris/solaris2/ * [421]http://www.wins.uva.nl/cgi-bin/sfaq.cgi * [422]ftp://ftp.wins.uva.nl/pub/solaris * [423]http://www.science.uva.nl/pub/solaris/solaris2.html And about serial communications in particular, see "Celeste's Tutorial on Solaris 2.x Modems and Terminals": [424]http://www.stokely.com/ In particular: [425]http://www.stokely.com/unix.sysadm.resources/faqs.sun.html For PC-based Solaris, also see general comments on PC-based Unixes in [426]Section 3.0. Don't expect Solaris or any other kind of Unix to work right on a PC until you resolve all interrupt conflicts. Don't expect to be able to use COM3 or COM4 (or even COM2) until you have configured their addresses and interrupts. 3.7.1. Serial Port Configuration [ [427]Top ] [ [428]Contents ] [ [429]Section Contents ] [ [430]Section Contents ] [ [431]Next ] Your serial port can't be used -- or at least won't work right -- until it is enabled in Solaris. For example, you get a message like "SERIAL: Operation would block" when attempting to dial. This probably indicates that the serial port has not been enabled for use with modems. You'll need to follow the instructions in your system setup or management manual, such as (e.g.) the Desktop SPARC Sun System & Network Manager's Guide, which should contain a section "Setting up Modem Software"; read it and follow the instructions. These might (or might not) include running a program called "eeprom", editing some system configuration file (such as, for example: /platform/i86pc/kernel/drv/asy.conf and then doing a configuration reboot, or running some other programs like drvconfig and devlinks. "man eeprom" for details. Also, on certain Sun models like IPC, the serial port hardware might need to have a jumper changed to make it an RS-232 port rather than RS-423. eeprom applies only to real serial ports, not to "Spiff" devices (serial port expander), in which case setup with Solaris' admintool is required. Another command you might need to use is pmadm, e.g.: pmadm -d -p zsmon -s tty3 pmadm -e -p zsmon -s tty3 You can use the following command to check if a process has the device open: fuser -f /dev/term/3 In some cases, however (according to Sun support, May 2001) "It is still possible that a zombie process has hold of the port EVEN IF there is no lock file and the fuser command comes up empty. In that case, the only way to resolve the problem is by rebooting." If you can't establish communication through a serial port to a device that is not asserting CD (Carrier Detect), try setting the environment variable "ttya-ignore-cd" to "true" (replace "ttya" with the port name). 3.7.2. Serial Port Problems [ [432]Top ] [ [433]Contents ] [ [434]Section Contents ] [ [435]Next ] [ [436]Previous ] Current advice from Sun is to always the /dev/cua/x devices for dialing out, rather than the /dev/term/x. Nevertheless, if you have trouble dialing out with one, try the other. Reportedly, if you start C-Kermit and "set line" to a port that has a modem connected to it that is not turned on, and then "set flow rts/cts", there might be some (unspecified) difficulties closing the device because the CTS signal is not coming in from the modem. 3.7.3. SunLink X.25 [ [437]Top ] [ [438]Contents ] [ [439]Section Contents ] [ [440]Next ] [ [441]Previous ] The built-in SunLink X.25 support for Solaris 2.3/2.4./25 and SunLink 8.01 or 9.00 works OK provided the X.25 system has been installed and initialized properly. Packet sizes might need to be reduced to 256, maybe even less, depending on the configuration of the X.25 installation. On one connection where C-Kermit 6.0 was tested, very large packets and window sizes could be used in one direction, but only very small ones would work in the other. In any case, according to Sun, C-Kermit's X.25 support is superfluous with SunLink 8.x / Solaris 2.3. Quoting an anonymous Sun engineer: ... there is now no need to include any X.25 code within kermit. As of X.25 8.0.1 we support the use of kermit, uucp and similar protocols over devices of type /dev/xty. This facility was there in 8.0, and should also work on the 8.0 release if patch 101524 is applied, but I'm not 100% sure it will work in all cases, which is why we only claim support from 8.0.1 onwards. When configuring X.25, on the "Advanced Configuration->Parameters" screen of the x25tool you can select a number of XTY devices. If you set this to be > 1, press Apply, and reboot, you will get a number of /dev/xty entries created. Ignore /dev/xty0, it is a special case. All the others can be used exactly as if they were a serial line (e.g. /dev/tty) connected to a modem, except that instead of using Hayes-style commands, you use PAD commands. From kermit you can do a 'set line' command to, say, /dev/xty1, then set your dialing command to be "CALL 12345678", etc. All the usual PAD commands will work (SET, PAR, etc). I know of one customer in Australia who is successfully using this, with kermit scripts, to manage some X.25-connected switches. He used standard kermit, compiled for Solaris 2, with X.25 8.0 xty devices. 3.7.4. Sun Workstation Keyboard Mapping [ [442]Top ] [ [443]Contents ] [ [444]Section Contents ] [ [445]Next ] [ [446]Previous ] Hints for using a Sun workstation keyboard for VT emulation when accessing VMS, from the [447]comp.os.vms newsgroup: From: Jerry Leichter Newsgroups: comp.os.vms Subject: Re: VT100 keyboard mapping to Sun X server Date: Mon, 19 Aug 1996 12:44:21 -0400 > I am stuck right now using a Sun keyboard (type 5) on systems running SunOS > and Solaris. I would like to use EVE on an OpenVMS box with display back to > the Sun. Does anyone know of a keyboard mapping (or some other procedure) > which will allow the Sun keyboard to approximate a VT100/VT220? You can't get it exactly - because the keypad has one fewer key - but you can come pretty close. Here's a set of keydefs I use: keycode 101=KP_0 keycode 119=KP_1 keycode 120=KP_2 keycode 121=KP_3 keycode 98=KP_4 keycode 99=KP_5 keycode 100=KP_6 keycode 75=KP_7 keycode 76=KP_8 keycode 77=KP_9 keycode 52=KP_F1 keycode 53=KP_F2 keycode 54=KP_F3 keycode 57=KP_Decimal keycode 28=Left keycode 29=Right keycode 30=KP_Separator keycode 105=KP_F4 keycode 78=KP_Subtract keycode 8=Left keycode 10=Right keycode 32=Up keycode 33=Down keycode 97=KP_Enter Put this in a file - I use "keydefs" in my home directory and feed it into xmodmap: xmodmap - <$HOME/keydefs This takes care of the arrow keys and the "calculator" key cluster. The "+" key will play the role of the DEC "," key. The Sun "-" key will be like the DEC "-" key, though it's in a physically different position - where the DEC PF4 key is. The PF4 key is ... damn, I'm not sure where "key 105" is. I *think* it may be on the leftmost key of the group of four just above the "calculator" key cluster. I also execute the following (this is all in my xinitrc file): xmodmap -e 'keysym KP_Decimal = KP_Decimal' xmodmap -e 'keysym BackSpace = Delete BackSpace' \ -e 'keysym Delete = BackSpace Delete' xmodmap -e 'keysym KP_Decimal = Delete Delete KP_Decimal' xmodmap -e 'add mod1 = Meta_R' xmodmap -e 'add mod1 = Meta_L' Beware of one thing about xmodmap: Keymap changes are applied to the *whole workstation*, not just to individual windows. There is, in fact, no way I know of to apply them to individual windows. These definitions *may* confuse some Unix programs (and/or some Unix users). If you're using Motif, you may also need to apply bindings at the Motif level. If just using xmodmap doesn't work, I can try and dig that stuff up for you. 3.7.5. Solaris PPP Connections [ [448]Top ] [ [449]Contents ] [ [450]Section Contents ] [ [451]Next ] [ [452]Previous ] The following is a report from a user of C-Kermit 8.0 on Solaris 8 and 9, who had complained that while Kermit file transfers worked perfectly on direct (non-PPP) dialout connections, they failed miserably on PPP connections. We suggested that the PPP dialer probably was not setting the port and/or modem up in the same way that Kermit did: I want to get back on this and tell you what the resolution was. You pointed me in the direction of flow control, which turned out to be the key. Some discussion on the comp.unix.solaris newsgroup led to some comments from Greg Andrews about the need to use the uucp driver to talk to the modem (/dev/cua/a). I had to remind Greg that no matter what the manpages for the zs and se drivers say, the ppp that Sun released with Solaris 8 7/01, and has in Solaris 9, is a setuid root program, and simply trying to make a pppd call from user space specifying /dev/cua/a would fail because of permissions. Greg finally put the question to the ppp people, who came back with information that is not laid out anywhere in the docs available for Solaris users. Namely, put /dev/cua/a in one of the privileged options files in the /etc/ppp directory. That, plus resetting the OBP ttya-ignore-cd flag (this is Sun hardware) to false, seems to have solved the problems. While I note that I had installed Kermit suid to uucp to use /dev/cua/a on this particular box, it seems to run fine through /dev/term/a. Not so with pppd. With this change in place, I seem to be able to upload and download through telnet run on Kermit with the maximum length packets. I note that the window allocation display does show STREAMING, using telnet. Running ssh on Kermit, I see the standard 1 of 30 windows display, and note that there appears to be a buffer length limit between 1000 and 2000 bytes. Run with 1000, and it's tick-tock, solid as a rock. With 2000 I see timeout errors and RTS/CTS action on the modem. Kermit's packet-length and other controls let you make adjustments like this to get around whatever obstacles might be thrown up -- in this case (running Kermit over ssh), the underling Solaris PTY driver. 3.7.6. Solaris 2.4 and Earlier [ [453]Top ] [ [454]Contents ] [ [455]Section Contents ] [ [456]Previous ] C-Kermit can't be compiled successfully under Solaris 2.3 using SUNWspro cc 2.0.1 unless at least some of the following patches are applied to cc (it is not known which one(s), if any, fix the problem): * 100935-01 SparcCompiler C 2.0.1: bad code generated when addresses of two double arguments are involved * 100961-05 SPARCcompilers C 2.0.1: conditional expression with function returning structure gives wrong value * 100974-01 SparcWorks 2.0.1: dbx jumbo patch * 101424-01 SPARCworks 2.0.1 maketool SEGV's instantly on Solaris 2.3 With unpatched cc 2.0.1, the symptom is that certain modules generate truncated object files, resulting in many unresolved references at link time. The rest of the problems in this section have to do with bidirectional terminal ports and the Solaris Port Monitor. A bug in C-Kermit 5A ticked a bug in Solaris. The C-Kermit bug was fixed in version 6.0, and the Solaris bug was fixed in 2.4 (I think, or maybe 2.5). Reportedly, "C-Kermit ... causes a SPARCstation running Solaris 2.3 to panic after the modem connects. I have tried compiling C-Kermit with Sun's unbundled C compiler, with GCC Versions 2.4.5 and 2.5.3, with make targets 'sunos51', 'sunos51tcp', 'sunos51gcc', and even 'sys5r4', and each time it compiles and starts up cleanly, but without fail, as soon as I dial the number and get a 'CONNECT' message from the modem, I get: BAD TRAP kermit: Data fault kernel read fault at addr=0x45c, pme=0x0 Sync Error Reg 80 ... panic: Data Fault. ... Rebooting... The same modem works fine for UUCP/tip calling." Also (reportedly), this only happens if the dialout port is configured as in/out via admintool. If it is configured as out-only, no problem. This is the same dialing code that works on hundreds of other System-V based Unix OS's. Since it should be impossible for a user program to crash the operating system, this problem must be chalked up to a Solaris bug. Even if you SET CARRIER OFF, CONNECT, and dial manually by typing ATDTnnnnnnn, the system panics as soon as the modem issues its CONNECT message. (Clearly, when you are dialing manually, C-Kermit does not know a thing about the CONNECT message, and so the panic is almost certainly caused by the transition of the Carrier Detect (CD) line from off to on.) This problem was reported by many users, all of whom say that C-Kermit worked fine on Solaris 2.1 and 2.2. If the speculation about CD is true, then a possible workaround might be to configure the modem to leave CD on (or off) all the time. Perhaps by the time you read this, a patch will have been issued for Solaris 2.3. The following is from Karl S. Marsh, Systems & Networks Administrator, AMBIX Systems Corp, Rochester, NY: Environment: Solaris 2.3 Patch 101318-45 C-Kermit 5A(189) (and presumably this applies to 188 and 190 also). eeprom setting: ttya-rts-dtr-off=false ttya-ignore-cd=false ttya-mode=19200,8,n,8,- To use C-Kermit on a bidirectional port in this environment, do not use admintool to configure the port. Use admintool to delete any services running on the port and then quit admintool and issue the following command: pmadm -a -p zsmon -s ttyb -i root -fu -v 1 -m "`ttyadm -b -d /dev/term/b \ -l conttyH -m ldterm,ttcompat -s /usr/bin/login -S n`" [NOTE: This was copied from a blurry fax, so please check it carefully] where: -a = Add service -p = pmtag (zsmon) -s = service tag (ttyb) -i = id to be associated with service tag (root) -fu = create utmp entry -v = version of ttyadm -m = port monitor-specific portion of the port monitor administrative file entry for the service -b = set up port for bidirectional use -d = full path name of device -l = which ttylabel in the /etc/ttydefs file to use -m = a list of pushable STREAMS modules -s = pathname of service to be invoked when connection request received -S = software carrier detect on or off (n = off) "This is exactly how I was able to get Kermit to work on a bi-directional port without crashing the system." On the Solaris problem, also see SunSolve Bug ID 1150457 ("Using C-Kermit, get Bad Trap on receiving prompt from remote system"). Another user reported "So, I have communicated with the Sun tech support person that submitted this bug report [1150457]. Apparently, this bug was fixed under one of the jumbo kernel patches. It would seem that the fix did not live on into 101318-45, as this is EXACTLY the error that I see when I attempt to use kermit on my system." Later (Aug 94)... C-Kermit dialout successfully tested on a Sun4m with a heavily patched Solaris 2.3. The patches most likely to have been relevant: * 101318-50: SunOS 5.3: Jumbo patch for kernel (includes libc, lockd) * 101720-01: SunOS 5.3: ttymon - prompt not always visible on a modem connection * 101815-01: SunOS 5.3: Data fault in put() NULL queue passed from ttycommon_qfull() * 101328-01: SunOS 5.3: Automation script to properly setup tty ports prior to PCTS execution Still later (Nov 94): another user (Bo Kullmar in Sweden) reports that after using C-Kermit to dial out on a bidirectional port, the port might not answer subsequent incoming calls, and says "the problem is easy enough to fix with the Serial Port Manager; I just delete the service and install it again using the graphical interface, which underneath uses commands like sacadm and pmadm." Later Bo reports, "I have found that if I run Kermit with the following script then it works. This script is for /dev/cua/a, "-s a" is the last a in /dev/cua/a: #! /bin/sh kermit sleep 2 surun pmadm -e -p zsmon -s a 3.8. C-KERMIT AND SUNOS [ [457]Top ] [ [458]Contents ] [ [459]Section Contents ] [ [460]Next ] [ [461]Previous ] For additional information, see "Celeste's Tutorial on SunOS 4.1.3+ Modems and Terminals": [462]http://www.stokely.com/ For FAQs, etc, from Sun, see: * [463]http://access1.sun.com/ For history of Sun models and SunOS versions, see (should be all the same): * [464]http://www.ludd.luth.se/~bear/project/sun/sun.hardware.txt * [465]ftp://ftp.netcom.com/pub/ru/rubicon/sun.hdwr.ref * [466]ftp://ftp.intnet.net/pub/SUN/Sun-Hardware-Ref Sun SPARCstation users should read the section "Setting up Modem Software" in the Desktop SPARC Sun System & Network Manager's Guide. If you don't set up your serial ports correctly, Kermit (and other communications software) won't work right. Also, on certain Sun models like IPC, the serial port hardware might need to have a jumper changed to make it an RS-232 port rather than RS-423. Reportedly, C-Kermit does not work correctly on a Sun SPARCstation in an Open Windows window with scrolling enabled. Disable scrolling, or else invoke Kermit in a terminal emulation window (xterm, crttool, vttool) under SunView (this might be fixed in later SunOS releases). On the Sun with Open Windows, an additional symptom has been reported: outbound SunLink X.25 connections "magically" translate CR typed at the keyboard into LF before transmission to the remote host. This doesn't happen under SunView. SET CARRIER ON, when used on the SunOS 4.1 version of C-Kermit (compiled in the BSD universe), causes the program to hang uninterruptibly when SET LINE is issued for a device that is not asserting carrier. When Kermit is built in the Sys V universe on the same computer, there is no problem (it can be interrupted with Ctrl-C). This is apparently a limitation of the BSD-style tty driver. SunOS 4.1 C-Kermit has been observed to dump core when running a complicated script program under cron. The dump invariably occurs in ttoc(), while trying to output a character to a TCP/IP TELNET connection. ttoc() contains a write() call, and when the system or the network is very busy, the write() call can get stuck for long periods of time. To break out of deadlocks caused by stuck write() calls, there is an alarm around the write(). It is possible that the core dump occurs when this alarm signal is caught. (This one has not been observed recently -- possibly fixed in edit 190.) On Sun computers with SunOS 4.0 or 4.1, SET FLOW RTS/CTS works only if the carrier signal is present from the communication device at the time when C-Kermit enters packet mode or CONNECT mode. If carrier is not sensed (e.g. when dialing), C-Kermit does not attempt to turn on RTS/CTS flow control. This is because the SunOS serial device driver does not allow characters to be output if RTS/CTS is set (CRTSCTS) but carrier (and DSR) are not present. Workaround (maybe): SET CARRIER OFF before giving the SET LINE command, establish the connection, then SET FLOW RTS/CTS It has also been reported that RTS/CTS flow control under SunOS 4.1 through 4.1.3 works only on INPUT, not on output, and that there is a patch from Sun to correct this problem: Patch-ID# T100513-04, 20 July 1993 (this patch might apply only to SunOS 4.1.3). It might also be necessary to configure the eeprom parameters of the serial port; e.g. do the following as root at the shell prompt: eeprom ttya-ignore-cd=false eeprom ttya-rts-dtr-off=true There have been reports of file transfer failures on Sun-3 systems when using long packets and/or large window sizes. One user says that when this happens, the console issues many copies of this message: chaos vmunix: zs1: ring buffer overflow This means that SunOS is not scheduling Kermit frequently enough to service interrupts from the zs serial device (Zilog 8350 SCC serial communication port) before its input silo overflows. Workaround: use smaller packets and/or a smaller window size, or use "nice" to increase Kermit's priority. Use hardware flow control if available, or remove other active processes before running Kermit. SunLink X.25 support in C-Kermit 5A(190) was built and tested successfully under SunOS 4.1.3b and SunLink X.25 7.00. 3.9. C-KERMIT AND ULTRIX [ [467]Top ] [ [468]Contents ] [ [469]Section Contents ] [ [470]Next ] [ [471]Previous ] See also: The [472]comp.unix.ultrix and [473]comp.sys.dec newsgroups. There is no hardware flow control in Ultrix. That's not a Kermit deficiency, but an Ultrix one. When sending files to C-Kermit on a Telnet connection to a remote Ultrix system, you must SET PREFIXING ALL (or at least prefix more control characters than are selected by SET PREFIXING CAUTIOUS). Reportedly, DEC ULTRIX 4.3 is immune to C-Kermit's disabling of SIGQUIT, which is the signal that is generated when the user types Ctrl-\, which kills the current process (i.e. C-Kermit) and dumps core. Diagnosis and cure unknown. Workaround: before starting C-Kermit -- or for that matter, when you first log in because this applies to all processes, not just Kermit -- give the following Unix command: stty quit undef Certain operations driven by RS-232 modem signal do not work on DECstations or other DEC platforms whose serial interfaces use MMP connectors (DEC version of RJ45 telephone jack with offset tab). These connectors convey only the DSR and DTR modem signals, but not carrier (CD), RTS, CTS, or RI. Use SET CARRIER OFF to enable communication, or "hotwire" DSR to CD. The maximum serial speed on the DECstation 5000 is normally 19200, but various tricks are available (outside Kermit) to enable higher rates. For example, on the 5000/200, 19200 can be remapped (somehow, something to do with "a bit in the SIR", whatever that is) to 38400, but in software you must still refer to this speed as 19200; you can't have 19200 and 38400 available at the same time. 19200, reportedly, is also the highest speed supported by Ultrix, but NetBSD reportedly supports speeds up to 57600 on the DECstation, although whether and how well this works is another question. In any case, given the lack of hardware flow control in Ultrix, high serial speeds are problematic at best. 3.10. C-KERMIT AND UNIXWARE [ [474]Top ] [ [475]Contents ] [ [476]Section Contents ] [ [477]Next ] [ [478]Previous ] See also: * The Freebird Project (Unixware software repository) [479]http://www.freebird.org/ * The UnixWare FAQ: [480]http://www.freebird.org/faq/ * The following newsgroups: + [481]comp.unix.unixware.misc + [482]comp.unix.sco.misc. Also see general comments on PC-based Unixes in [483]Section 3.0. By the way, this section is separate from the SCO (Caldera) section because at the time this section was started, Unixware was owned by a company called Univel. Later it was sold to Novell, and then to SCO. Still later, SCO was sold to Caldera. In Unixware 2.0 and later, the preferred serial device names (drivers) are /dev/term/00 (etc), rather than /dev/tty00 (etc). Note the following correspondence of device names and driver characteristics: New name Old name Description /dev/term/00 /dev/tty00 ??? /dev/term/00h /dev/tty00h Modem signals and hardware flow control /dev/term/00m /dev/tty00m Modem signals(?) /dev/term/00s /dev/tty00s Modem signals and software flow control /dev/term/00t /dev/tty00t ??? Lockfile names use device.major.minor numbers, e.g.: /var/spool/locks/LK.7679.003.005 The minor number varies according to the device name suffix (none, h, m, s, or t). Only the device and major number are compared, and thus all of the different names for the same physical device (e.g. all of those shown in the table above) interlock effectively. Prior to UnixWare 7, serial speeds higher than 38400 are not supported. In UnixWare 7, we also support 57600 and 115200, plus some unexpected ones like 14400, 28800, and 76800, by virtue of a strange new interface, evidently peculiar to UnixWare 7, discovered while digging through the header files: tcsetspeed(). Access to this interface is allowed only in POSIX builds, and thus the UnixWare 7 version of C-Kermit is POSIX-based, unlike C-Kermit for Unixware 1.x and 2.x (since the earlier UnixWare versions did not support high serial speeds, period). HOWEVER, turning on POSIX features engages all of the "#if (!_POSIX_SOURCE)" clauses in the UnixWare header files, which in turn prevent us from having modem signals, access to the hardware flow control APIs, select(), etc -- in short, all the other things we need in communications software, especially when high speeds are used. Oh the irony. And so C-Kermit must be shamelessly butchered -- as it has been so many times before -- to allow us to have the needed features from the POSIX and non-POSIX worlds. See the UNIXWAREPOSIX sections of [484]ckutio.c. After the butchery, we wind up with Unixware 2.x having full modem-signal capability, but politically-correct Unixware 7.x lacking the ability to automatically detect a broken connection when carrier drops. Meanwhile the Unixware tcsetspeed() function allows any number at all (any long, 0 or positive) as an argument and succeeds if the number is a legal bit rate for the serial device, and fails otherwise. There is no list anywhere of legal speeds. Thus the SET SPEED keyword table ("set speed ?" to see it) is hardwired based on trial and error with all known serial speeds, the maximum being 115200. However, to allow for the possibility that other speeds might be allowed in the future (or with different port drivers), the SET SPEED command for UnixWare 7 only allows you to specify any number at all; a warning is printed if the number is not in the list, but the number is accepted anyway; the command succeeds if tcsetspeed() accepts the number, and fails otherwise. In C-Kermit 8.0 testing, it was noticed that the POSIX method for hanging up the phone by dropping DTR (set speed 0, pause, restore speed) did not actually drop DTR. The APIs do not return any error indication, but nothing happens. I changed tthang() to skip the special case I had made for Unixware and instead follow the normal path: if TIOCSDTR is defined use that, otherwise blah blah... It turns out TIOCSDTR *is* defined, and it works. So in Unixware (at least in 2.1.3) we can read modem signals, hangup by toggling DTR, and so on, BUT... But once the remote hangs up and Carrier drops, the API for reading modem signals ceases to function; although the device is still open, the TIOCMGET ioctl always raises errno 6 = ENXIO, "No such device or address". Old business: Using C-Kermit 6.0 on the UnixWare 1.1 Application Server, one user reported a system panic when the following script program is executed: set line /dev/tty4 set speed 9600 output \13 connect The panic does not happen if a PAUSE is inserted: set line /dev/tty4 set speed 9600 pause 1 output \13 connect This is using a Stallion EasyIO card installed as board 0 on IRQ 12 on a Gateway 386 with the Stallion-supplied driver. The problem was reported to Novell and Stallion and (reportedly) is now fixed. 3.11. C-KERMIT AND APOLLO SR10 [ [485]Top ] [ [486]Contents ] [ [487]Section Contents ] [ [488]Next ] [ [489]Previous ] Reportedly, version 5A(190), when built under Apollo SR10 using "make sr10-bsd", compiles, links, and executes OK, but leaves the terminal unusable after it exits -- the "cs7" or "cs8" (character size) parameter has become cs5. The terminal must be reset from another terminal. Cause and cure unknown. Suggested workaround: Wrap Kermit in a shell script something like: kermit @* stty sane 3.12. C-KERMIT AND TANDY XENIX 3.0 [ [490]Top ] [ [491]Contents ] [ [492]Section Contents ] [ [493]Next ] [ [494]Previous ] C-Kermit 7.0 was too big to be built on Tandy Xenix, even in a minimum configuration; version 6.0 is the last one that fits. Reportedly, in C-Kermit 6.0, if you type lots of Ctrl-C's during execution of the initialization file, ghost Kermit processes will be created, and will compete for the keyboard. They can only be removed via "kill -9" from another terminal, or by rebooting. Diagnosis -- something strange happening with the SIGINT handler while the process is reading the directory (it seems to occur during the SET PROMPT [\v(dir)] ... sequence). Cure: unknown. Workaround: don't interrupt C-Kermit while it is executing its init file on the Tandy 16/6000. 3.13. C-KERMIT AND OSF/1 (DIGITAL UNIX) (TRU64 UNIX) [ [495]Top ] [ [496]Contents ] [ [497]Section Contents ] [ [498]Next ] [ [499]Previous ] While putting together and testing C-Kermit 8.0, it was discovered that binaries built for one version of Tru64 Unix (e.g. 4.0G) might exhibit very strange behavior if run on a different version of Tru64 Unix (e.g. 5.1A). The typical symptom was that a section of the initialization file would be skipped, notably locating the dialing and/or network directory as well as finding and executing the customization file, ~/.mykermrc. This problem also is reported to occur on Tru64 Unix 5.0 (Rev 732) even when running a C-Kermit binary that was built there. However, the Tru64 5.1A binary works correctly on 5.0. Go figure. When making Telnet connections to a Digital Unix or Tru64 system, and your Telnet client forwards your user name, the Telnet server evidently stuffs the username into login's standard input, and you see: login: ivan Password: This is clearly going to play havoc with scripts that look for "login:". Workaround (when Kermit is your Telnet client): SET LOGIN USER to nothing, to prevent Kermit from sending your user ID. Before you can use a serial port on a new Digital Unix system, you must run uucpsetup to enable or configure the port. Evidently the /dev/tty00 and 01 devices that appear in the configuration are not usable; uucpsetup turns them into /dev/ttyd00 and 01, which are. Note that uucpsetup and other uucp-family programs are quite primitive -- they only know about speeds up to 9600 bps and their selection of modems dates from the early 1980s. None of this affects Kermit, though -- with C-Kermit, you can use speeds up to 115200 bps (at least in DU4.0 and later) and modern modems with hardware flow control and all the rest. Reportedly, if a modem is set for &S0 (assert DSR at all times), the system resets or drops DTR every 30 seconds; reportedly DEC says to set &S1. Digital Unix 3.2 evidently wants to believe your terminal is one line longer than you say it is, e.g. when a "more" or "man" command is given. This is has nothing to do with C-Kermit, but tends to annoy those who use Kermit or other terminal emulators to access Digital Unix systems. Workaround: tell Unix to "stty rows 23" (or whatever). Reportedly, there is some bizarre behavior when trying to use a version of C-Kermit built on one Digital Unix 4.0 system on another one, possibly due to differing OS or library revision levels; for example, the inability to connect to certain TCP/IP hosts. Solution: rebuild C-Kermit from source code on the system where you will be using it. Digital Unix tgetstr() causes a segmentation fault. C-Kermit 7.0 added #ifdefs to avoid calling this routine in Digital Unix. As a result, the SCREEN commands always send ANSI escape sequences -- even though curses knows your actual terminal type. Reportedly the Tru64 Unix 4.0E 1091 Telnet server does not tolerate streaming transfers into itself, at least not when the sending Kermit is on the same local network. Solution: tell one Kermit or the other (or both) to "set streaming off". This might or might be the case with earlier and/or later Tru64, Digital Unix, and OSF/1 releases. 3.14. C-KERMIT AND SGI IRIX [ [500]Top ] [ [501]Contents ] [ [502]Section Contents ] [ [503]Next ] [ [504]Previous ] See also: * The [505]comp.sys.sgi.misc and [506]comp.sys.sgi.admin newsgroups. [507]The SGI website * The SGI FAQ: + [508]http://www-viz.tamu.edu/~sgi-faq/ + [509]ftp://viz.tamu.edu/pub/sgi/faq/ About IRIX version numbers: "uname -a" tells the "two-digit" version number, such as "5.3" or "6.5". The three-digit form can be seen with "uname -R". (this information is unavailable at the simple API level). Supposedly all three-digit versions within the same two-digit version (e.g. 6.5.2, 6.5.3) are binary compatible; i.e. a binary built on any one of them should run on all others. The "m" suffix denotes just patches; the "f" suffix indicates that features were added. An IRIX binary built on lower MIPS model (Instruction Set Architecture, ISA) can run on higher models, but not vice versa: MIPS1 R3000 and below MIPS2 R4000 MIPS3 R4x00 MIPS4 R5000 and above Furthermore, there are different Application Binary Interfaces (ABIs): COFF 32 bits, IRIX 5.3, 5.2, 5.1, 4.x and below o32 ELF 32 bits, IRIX 5.3, 6.0 - 6.5 N32 ELF 32 bits, IRIX 6.2 - 6.5 N64 ELF 64 bits, IRIX 6.2 - 6.5 Thus a prebuilt IRIX binary works on a particular machine only if (a) the machine's IRIX version (to one decimal place) is equal to or greater than the version under which the binary was built; (b) the machine's MIPS level is greater or equal to that of the binary; and (c) the machine supports the ABI of the binary. If all three conditions are not satisfied, of course, you can build a binary yourself from source code since, unlike some other Unix vendors, SGI does supply a C compiler and libraries. SGI did not supply an API for hardware flow control prior to IRIX 5.2. C-Kermit 6.1 and higher for IRIX 5.2 and higher supports hardware flow control in the normal way, via "set flow rts/cts". For hardware flow control on earlier IRIX and/or C-Kermit versions, use the ttyf* (modem control AND hardware flow control) devices and not the ttyd* (direct) or ttym* (modem control but no hardware flow control) ones, and obtain the proper "hardware handshaking" cable from SGI, which is incompatible with the ones for the Macintosh and NeXT even though they look the same ("man serial" for further info) and tell Kermit to "set flow keep" and "set modem flow rts/cts". Serial speeds higher than 38400 are available in IRIX 6.2 and later, on O-class machines (e.g. Origin, Octane) only, and are supported by C-Kermit 7.0 and later. Commands such as "set speed 115200" may be given on other models (e.g. Iris, Indy, Indigo) but will fail because the OS reports an invalid speed for the device. Experimentation with both IRIX 5.3 and 6.2 shows that when logged in to IRIX via Telnet, that remote-mode C-Kermit can't send files if the packet length is greater than 4096; the Telnet server evidently has this restriction (or bug), since there is no problem sending long packets on serial or rlogin connections. However, it can receive files with no problem if the packet length is greater than 4096. As a workaround, the FAST macro for IRIX includes "set send packet-length 4000". IRIX 6.5.1 does not have this problem, so evidently it was fixed some time after IRIX 6.2. Tests show file-transfer speeds are better (not worse) with 8K packets than with 4K packets from IRIX 6.5.1. Reportedly some Indys have bad serial port hardware. IRIX 5.2, for example, needs patch 151 to work around this; or upgrade to a later release. Similarly, IRIX 5.2 has several problems with serial i/o, flow control, etc. Again, patch or upgrade. Reportedly on machines with IRIX 4.0, Kermit cannot be suspended by typing the suspend ("swtch") character if it was started from csh, even though other programs can be suspended this way, and even though the Z and SUSPEND commands still work correctly. This is evidently because IRIX's csh does not deliver the SIGTSTP signal to Kermit. The reason other programs can be suspended in the same environment is probably that they do not trap SIGTSTP themselves, so the shell is doing the suspending rather than the application. Also see notes about IRIX 3.x in the [510]C-Kermit for Unix Installation Instructions. If you have problems making TCP/IP connections in versions of IRIX built with GCC 2.95.2, see the bugs section of: [511]http://freeware.sgi.com/Installable/gcc-2.95.2.html. Reportedly, if you allow gcc to compile C-Kermit on Irix you should be aware that there might be problems with some of the network code. The specifics are at [512]http://freeware.sgi.com/Installable/gcc-2.95.2.html; scroll down to the "known bugs" section at the end of the document. 3.15. C-KERMIT AND THE BEBOX [ [513]Top ] [ [514]Contents ] [ [515]Section Contents ] [ [516]Next ] [ [517]Previous ] See also: The [518]comp.sys.be newsgroup. The BeBox has been discontinued and BeOS repositioned for PC platforms. The POSIX parts of BeOS are not finished, nor is the sockets library, therefore a fully functional version of C-Kermit is not possible. In version 6.0 of C-Kermit, written for BeOS DR7, it was possible to: * set line /dev/serial2 (and probably the other serial ports) * set speed 115200 (and at least some of the lower baud rates) * connect * set modem type hayes (and likely others, too) * dial phone-number * set send packet-length 2048 (other lengths for both send and receive) * set receive packet length 2048 * set file type binary (text mode works, too) (with remote kermit session in server mode) * put bedrop.jpg * get bedrop.jpg * get bedrop.jpg bedrop.jpg2 * finish, bye The following do not work: * kermit does not detect modem hangup * !/RUN/PUSH [commandline command] * Running kermit in remote mode * Using other protocols (x/y/zmodem) * TCP networking interface (Be's TCP/IP API has a ways to go, still) C-Kermit does not work on BeOS DR8 because of changes in the underlying APIs. Unfortunately not enough changes were made to allow the regular POSIX-based C-Kermit to work either. Note: the lack of a fork() service requires the select()-based CONNECT module, but there is no select(). There is a select() in DR8, but it doesn't work. C-Kermit 7.0 was built for BeOS 4.5 and works in remote mode. It does not include networking support since the APIs are still not there. It is not known if dialing out works, but probably not. Be experts are welcome to lend a hand. 3.16. C-KERMIT AND DG/UX [ [519]Top ] [ [520]Contents ] [ [521]Section Contents ] [ [522]Next ] [ [523]Previous ] Somebody downloaded the C-Kermit 6.0 binary built under DG/UX 5.40 and ran it under DG/UX 5.4R3.10 -- it worked OK except that file dates for incoming files were all written as 1 Jan 1970. Cause and cure unknown. Workaround: SET ATTRIBUTE DATE OFF. Better: Use a version of C-Kermit built under and for DG/UX 5.4R3.10. 3.17. C-KERMIT AND SEQUENT DYNIX [ [524]Top ] [ [525]Contents ] [ [526]Section Contents ] [ [527]Next ] [ [528]Previous ] Reportedly, when coming into a Sequent Unix (DYNIX) system through an X.25 connection, Kermit doesn't work right because the Sequent's FIONREAD ioctl returns incorrect data. To work around, use the 1-character-at-a-time version of myread() in ckutio.c (i.e. undefine MYREAD in ckutio.c and rebuild the program). This is unsatisfying because two versions of the program would be needed -- one for use over X.25, and the other for serial and TCP/IP connections. 3.18. C-KERMIT AND FREEBSD, OPENBSD, and NETBSD [ [529]Top ] [ [530]Contents ] [ [531]Section Contents ] [ [532]Next ] [ [533]Previous ] Some NebBSD users have reported difficulty escaping back from CONNECT mode, usually when running NetBSD on non-PC hardware. Probably a keyboard issue. NetBSD users have also reported that C-Kermit doesn't pop back to the prompt if the modem drops carrier. This needs to be checked out & fixed if possible. (All the above seems to work properly in C-Kermit 7.0 and later.) 3.19. C-KERMIT AND MAC OS X [ [534]Top ] [ [535]Contents ] [ [536]Section Contents ] [ [537]Next ] [ [538]Previous ] Mac OS X is Apple's 4.4BSD Unix variety, closely related to FreeBSD, but different. "uname -a" is singularly uninformative, as in Linux, giving only the Darwin kernel version number. The way to find out the actual Mac OS X version is with /usr/bin/sw_vers -productName /usr/bin/sw_vers -productVersion or: fgrep -A 1 'ProductVersion' /System/Library/CoreServices/SystemVersion.plist Here are some points to be aware of: * A big gotcha for Kermit users is that Mac OS X does not support serial ports and, as far as I can tell, doesn't support its built-in modem either, for anything other than making Internet connections. Macintoshes capable of running Mac OS X, such as the G5 and later, come without serial ports and without any APIs to support them, and also without the UUCP family of programs (including cu), nor any standard for serial-port lockfile directory. * Early versions of Mac OS X came without Curses, Termlib, or Terminfo libraries. Later versions seem to have ncurses (it would seem that Mac OS X 10.3.9 was the first mature and complete version of Mac OS X). Kermit uses curses for its file-transfer display. See elsewhere about curses-vs-ncurses confusion. * In the HFS+ file system, filenames are case-folded. Thus "makefile" and "Makefile" are the same file. So, for example, suppose you are sending two distinct files, Foo and FOO, from (say) Linux to Mac OS X. This will produce a file collision that will be handled according to Mac OS X C-Kermit's FILE COLLISION setting, which by default is BACKUP, so the Mac will wind up with files called FOO and Foo.~1~. * HSF+ files that are composed of a resource fork and a data fork... I doubt that C-Kermit does anything useful with them. There is no code in C-Kermit for traditional two-forked Macintosh files, but it could be added if there is any demand (code for this existed in [539]Mac Kermit, the old pre-Mac-OS-X Macintosh version of C-Kermit). * In case you want to transfer a traditional Macintosh text file (or data fork of a file that is plain text), you can use these C-Kermit commands: set file eol cr set file character-set apple-quickdraw send /text filename * File or pathnames that include spaces must be enclosed in either doublequotes or curly braces in C-Kermit commands. * Mac OS X can use a third-party package manager called "fink". Various fink packages for C-Kermit are floating around that are not standard releases. For example, there's a C-Kermit 8.0.201 package in which C-Kermit was modified (at least) to use a UUCP lockfile directory that does not exist on vanilla Mac OS X systems. Mac OS X and Serial Ports Apple is in the forefront of companies that believe serial ports have no use in the modern world and so has simply eliminated all traces of them from its machines and OS. But of course serial ports are still needed to connect not only to external modems, but also to the control ports of hubs, routers, terminal servers, PBXs, and similar devices, not to mention barcode readers, POS systems and components, speaking devices, hand calculators such as the HP48, automated factory-floor equipment, and scientific, medical, and lab equipment (to name a few). Among workers in these areas, there is a need to add serial ports back onto this platform, which is being filled by third-party products such as the [540]Keyspan High Speed USB Serial Adapter USA-19HS, which has a DB-9 male connector. To use the Keyspan device, you must install the accompanying device drivers, which winds up giving you serial ports with names like /dev/cu.USA19H3b1P1.1, /dev/cu.KeySerial1, /dev/tty.KeySerial1. C-Kermit 9.0 works "out of the box" with third-party serial ports on Mac OS X, because it is built by default ("make macosx") without the "UUCP lockfile" feature. If you have C-Kermit 9.0 on a personal Macintosh, you can skip the next section. Mac OS X Serial Ports with C-Kermit 8.0 and earlier In earlier versions of C-Kermit, you'll need to either build a special -DNOUUCP version, or deal with the UUCP port contention sytem in [541]all its glory (this is usually an exercise in futility because any other applications on your Mac that use the serial port will not necessarily follow the same conventions): 1. su (or sudo -s) chgrp xxxx /var/spool/lock chmod g+w /var/spool/lock chgrp xxxx /dev/cu.* (where xxxx is the name of the group for users to whom serial-port access is to be granted). Use "admin" or other existing group, or create a new group if desired. NB: In the absence of official guidance from Apple or anyone else, we choose /var/spool/lock as the lockfile directory because this directory (a) already exists on vanilla Mac OS X installations, and (b) it is the directory used for serial-port lockfiles on many other platforms. 2. Put all users who need access to the serial port in the same group. 3. Make sure the serial device files that are to be used by C-Kermit have group read-write permission and (if you care) lack world read-write permission, e.g.: chmod g+rw,o-rw /dev/cu.* If you do the above, then there's no need to become root to use Kermit, or to make Kermit suid or sgid. Just do this: chmod 775 wermit mv wermit /usr/local/kermit (or whatever spot is more appropriate, e.g. /usr/bin/). For greater detail about installation, [542]CLICK HERE. Alternatively, to build a pre-9.0 version of C-Kermit without UUCP lockfile support, set the NOUUCP flag; e.g. (for Mac OS 10.4): make macosx10.4 KFLAGS=-DNOUUCP This circumvents the SET PORT failure "?Access to lockfile directory denied". But it also sacrifices Kermit's ability to ensure that only one copy of Kermit can have the device open at a time, since Mac OS X is the same as all other varieties of Unix in that exclusive access to serial ports is not enforced in any way. But if it's for your own desktop machine that nobody else uses, a -DNOUUCP version might be adequate and preferable to the alternatives. To build C-Kermit 9.0 with UUCP support, do: make macosx KFLAGS=-UNOUUCP (note: "-U", not "-D). RS-232 versus RS-422 Meanwhile, back when Macs had serial ports, they were not RS-232 (the standard for connecting computers with nearby modems) but rather RS-422 or -423 (a standard for connecting serial devices over longer distances). Macintosh serial ports do not support modems well because they do not have enough wires (or more properly in the case RS-422/423, wire pairs) to convey a useful subset of modem signals. Keyspan also sells a [543]USB Twin Serial Adapter that gives you two Mini-Din8 RS-422 ports, that are no better (or worse) for communicating with modems or serial devices than a real Mac Din-8 port was. In essence, you get Data In, Data Out, and two modem signals. It looks to me as if the signals chosen by Keyspan are RTS and CTS. This gives you hardware flow control, but at the expense of Carrier Detect. Thus to use C-Kermit with a Keyspan USB serial port, you must tell C-Kermit to: set modem type none ; (don't expect a modem) set carrier-watch off ; (ignore carrier signal) set port /dev/cu.USA19H3b1P1.1 ; (open the port) set flow rts/cts ; (this is the default) set speed 57600 ; (or whatever) connect ; (or DIAL or whatever) Use Ctrl-\C in the normal manner to escape back to the C-Kermit> prompt. Kermit can't pop back to its prompt automatically when Carrier drops because there is no Carrier signal in the physical interface. Here's a typical sequence for connecting to Cisco devices (using a mixture of command-line options and interactive commands at the prompt): $ ckermit -l /dev/cu.USA19H3b1P1.1 -b 9600 C-Kermit> set carrier-watch off C-Kermit> connect Instructions for the built-in modem (if any) remain to be written due to lack of knowledge. If you can contribute instructions, hints, or tips, please [544]send them in. 3.20. C-KERMIT AND COHERENT [ [545]Top ] [ [546]Contents ] [ [547]Section Contents ] [ [548]Previous ] Also see: [549]http://www.uni-giessen.de/faq/archiv/coherent-faq.general/msg000 00.html Mark Williams COHERENT was perhaps the first commercial Unix-based operating system for PCs, first appearing about 1983 or -84 for the PC/XT (?), and popular until about 1993, when Linux took over. C-Kermit, as of version 8.0, is still current for COHERENT 386 4.2 (i.e. only for i386 and above). Curses is included, but lots of other features are omitted due to lack of the appropriate OS features, APIs, libraries, hardware, or just space: e.g. TCP/IP, floating-point arithmetic, learned scripts. Earlier versions of COHERENT ran on 8086 and 80286, but these are to small to build or run C-Kermit, but G-Kermit should be OK (as might be ancient versions of C-Kermit). You can actually build a version with floating point support -- just take -DNOFLOAT out of CFLAGS and add -lm to LIBS; NOFLOAT is the default because COHERENT tends to run on old PCs that don't have floating-point hardware. You can also add "-f" to CFLAGS to have it link in the floating-point emulation library. Also I'm not sure why -DNOLEARN is included, since it depends on select(), which COHERENT has. 4. GENERAL UNIX-SPECIFIC HINTS, LIMITATIONS, AND BUGS [ [550]Top ] [ [551]Contents ] [ [552]Next ] [ [553]Previous ] 4.1. Modem Signals There seems to be an escalating demand for the ability to control "dumb serial devices" (such as "smartcard readers", barcode readers, etc) by explicitly manipulating modem signals, particularly RTS. This might have been easy to do in DOS, where there is no operating system standing between the application and the serial device, but it is problematic in Unix, where modem signals are controlled by the serial device driver. If the driver does not provide an API for doing this, then the application can't do it. If it does provide an API, expect it to be totally different on each Unix platform, since there is no standard for this. 4.2. NFS Troubles Beginning with C-Kermit 6.0, the default C-Kermit prompt includes your current (working) directory; for example: [/usr/olga] C-Kermit> (In C-Kermit 7.0 the square braces were replaced by round parentheses to avoid conflicts with ISO 646 national character sets.) If that directory is on an NFS-mounted disk, and NFS stops working or the disk becomes unavailable, C-Kermit will hang waiting for NFS and/or the disk to come back. Whether you can interrupt C-Kermit when it is hung this way depends on the specific OS. Kermit has called the operating systems's getcwd() function, and is waiting for it to return. Some versions of Unix (e.g. HP-UX 9.x) allow this function to be interrupted with SIGINT (Ctrl-C), others (such as HP-UX 8.x) do not. To avoid this effect, you can always use SET PROMPT to change your prompt to something that does not involve calling getcwd(), but if NFS is not responding, C-Kermit will still hang any time you give a command that refers to an NFS-mounted directory. Also note that in some cases, the uninterruptibility of NFS-dependent system or library calls is considered a bug, and sometimes there are patches. For HP-UX, for example: replaced by: HP-UX 10.20 libc PHCO_8764 PHCO_14891/PHCO_16723 HP-UX 10.10 libc PHCO_8763 PHCO_14254/PHCO_16722 HP-UX 9.x libc PHCO_7747 S700 PHCO_13095 HP-UX 9.x libc PHCO_6779 S800 PHCO_11162 4.3. C-Kermit as Login Shell You might have reason to make C-Kermit the login shell for a specific user, by entering the pathname of Kermit (possibly with command-line switches, such as -x to put it in server mode) into the shell field of the /etc/passwd file. This works pretty well. In some cases, for "ultimate security", you might want to use a version built with -DNOPUSH (see the [554]Configurations Options document for this, but even if you don't, then PUSHing or shelling out from C-Kermit just brings up a new copy of C-Kermit (but warning: this does not prevent the user from explicitly running a shell; e.g. "run /bin/sh"; use NOPUSH to prevent this). 4.4. C-Kermit versus screen and splitvt C-Kermit file transfers will probably not work if attempted through the "splitvt" or GNU "screen" programs because the screen optimization (or at least, line wrapping, control-character absorption) done by this package interferes with Kermit's packets. The same can apply to any other environment in which the user's session is captured, monitored, recorded, or manipulated. Examples include the 'script' program (for making a typescript of a session), the Computronics PEEK package and pksh (at least versions of it prior to 1.9K), and so on. You might try the following -- what we call "doomsday Kermit" -- settings to push packets through even the densest and most obstructive connections, such as "screen" and "splitvt" (and certain kinds of 3270 protocol emulators): Give these commands to BOTH Kermit programs: SET FLOW NONE SET CONTROL PREFIX ALL SET RECEIVE PACKET-LENGTH 70 SET RECEIVE START 62 SET SEND START 62 SET SEND PAUSE 100 SET BLOCK B If it works, it will be slow. 4.5. C-Kermit versus DOS Emulators On Unix workstations equipped with DOS emulators like SoftPC, watch out for what these emulators do to the serial port drivers. After using a DOS emulator, particularly if you use it to run DOS communications software, you might have to reconfigure the serial ports for use by Unix. 4.6. C-Kermit versus Job Control Interruption by Ctrl-Z makes Unix C-Kermit try to suspend itself with kill(0,SIGTSTP), but only on platforms that support job control, as determined by whether the symbol SIGTSTP is defined (or on POSIX or SVR4 systems, if syconf(_SC_JOB_CONTROL) or _POSIX_JOB_CONTROL in addition to SIGTSTP). However, if Kermit is running under a login shell (such as the original Bourne shell) that does not support job control, the user's session hangs and must be logged out from another terminal, or hung up on. There is no way Kermit can defend itself against this. If you use a non-job control shell on a computer that supports job control, give a command like "stty susp undef" to fix it so the suspend signal is not attached to any particular key, or give the command SET SUSPEND OFF to C-Kermit, or build C-Kermit with -DNOJC. 4.7. Dates and Times Unix time conversion functions typically apply locale rules to return local time in terms of any seasonal time zone change in effect for the given date. The diffdate function assumes that the same timezone rules are in effect for both dates, but a date with timezone information will be converted to the local time zone in effect at the given time, e.g., a GMT specification will produce either a Standard Time or Daylight Savings Time, depending on which applies at the given time. An example using the 2001 seasonal change from EDT (-0400) to EST (-0500): C-Kermit> DATE 20011028 05:01:02 GMT ; EDT 20011028 01:01:02 C-Kermit> DATE 20011028 06:01:02 GMT ; EST 20011028 01:01:02 C-Kermit> but the implicit change in timezone offset is not recognized: C-Kermit> echo \fdiffdate(20011028 05:01:02 GMT, 20011028 06:01:02 GMT) +0:00 C-Kermit> Date/time arithmetic, offsets, delta times, and timezone support are new to C-Kermit 8.0, and might be expected to evolve and improve in subsequent releases. On some platforms, files downloaded with HTTP receive the current timestamp, rather than the HTTP "Last Modified" time (this can be fixed by including utime.h, e.g. in SunOS and Tru64...). 4.8. Pseudoterminals The SSH and PTY commands work by assigning a pseudoterminal and reading and writing from it. Performance varies according to the specific platform ranging from very fast to very flow. SSH and PTY commands can fail if (a) all pseudoterminals are in use; or (b) you do not have read/write access to the pseudoterminal that was assigned. An example of (b) was reported with the Zipslack Slackware Linux distribution, in which the pseudoterminals were created with crw-r--r-- permission, instead of crw-rw-rw-. 4.9. Miscellaneous * Reportedly, the Unix C-Kermit server, under some conditions, on certain particular systems, fails to log out its login session upon receipt of a BYE command. Before relying on the BYE command working, test it a few times to make sure it works on your system: there might be system configuration or security mechanisms to prevent an inferior process (like Kermit) from killing a superior one (like the login shell). * On AT&T 7300 (3B1) machines, you might have to "stty nl1" before starting C-Kermit. Do this if characters are lost during communications operations. * Under the bash shell (versions prior to 1.07 from CWRU), "pushing" to an inferior shell and then exiting back to Kermit leaves Kermit in the background such that it must be explicitly fg'd. This is reportedly fixed in version 1.07 of bash (and definitely in modern bash versions). 5. INITIALIZATION AND COMMAND FILES [ [555]Top ] [ [556]Contents ] [ [557]Next ] [ [558]Previous ] C-Kermit's initialization file for Unix is .kermrc (lowercase, starts with period) in your home directory, unless Kermit was built with the system-wide initialization-file option (see the [559]C-Kermit for Unix Installation Instructions). C-Kermit identifies your home directory based on the environment variable, HOME. Most Unix systems set this variable automatically when you log in. If C-Kermit can't find your initialization file, check your HOME variable: echo $HOME (at the Unix prompt) or: echo \$(HOME) (at the C-Kermit prompt) If HOME is not defined, or is defined incorrectly, add the appropriate definition to your Unix .profile or .login file, depending on your shell: setenv HOME full-pathname-of-your-home-directory (C-Shell, .login file) or: HOME=full-pathname-of-your-home-directory (sh, ksh, .profile file) export HOME NOTE: Various other operations depend on the correct definition of HOME. These include the "tilde-expansion" feature, which allows you to refer to your home directory as "~" in filenames used in C-Kermit commands, e.g.: send ~/.kermrc as well as the \v(home) variable. Prior to version 5A(190), C-Kermit would look for its initialization file in the current directory if it was not found in the home directory. This feature was removed from 5A(190) because it was a security risk. Some people, however, liked this behavior and had .kermrc files in all their directories that would set up things appropriately for the files therein. If you want this behavior, you can accomplish it in various ways, for example: * Create a shell alias, for example: alias kd="kermit -Y ./.kermrc" * Create a .kermrc file in your home directory, whose contents are: take ./.kermrc Suppose you need to pass a password from the Unix command line to a C-Kermit script program, in such a way that it does not show up in "ps" or "w" listings. Here is a method (not guaranteed to be 100% secure, but definitely more secure than the more obvious methods): echo mypassword | kermit myscript The "myscript" file contains all the commands that need to be executed during the Kermit session, up to and including EXIT, and also includes an ASK or ASKQ command to read the password from standard input, which has been piped in from the Unix 'echo' command, but it must not include a CONNECT command. Only "kermit myscript" shows up in the ps listing. 6. COMMUNICATION SPEED SELECTION [ [560]Top ] [ [561]Contents ] [ [562]Next ] [ [563]Previous ] Version-7 based Unix implementations, including 4.3 BSD and earlier and Unix systems based upon BSD, use a 4-bit field to record a serial device's terminal speed. This leaves room for 16 speeds, of which the first 14 are normally: 0, 50, 75, 110, 134.5, 150, 200, 300, 600, 1200, 1800, 2400, 4800, and 9600 The remaining two are usually called EXTA and EXTB, and are defined by the particular Unix implementation. C-Kermit determines which speeds are available on your system based on whether symbols for them are defined in your terminal device header files. EXTA is generally assumed to be 19200 and EXTB 38400, but these assumptions might be wrong, or they might not apply to a particular device that does not support these speeds. Presumably, if you try to set a speed that is not legal on a particular device, the driver will return an error, but this can not be guaranteed. On these systems, it is usually not possible to select a speed of 14400 bps for use with V.32bis modems. In that case, use 19200 or 38400 bps, configure your modem to lock its interface speed and to use RTS/CTS flow control, and tell C-Kermit to SET FLOW RTS/CTS and SET DIAL SPEED-MATCHING OFF. The situation is similar, but different, in System V. SVID Third Edition lists the same speeds, 0 through 38400. Some versions of Unix, and/or terminal device drivers that come with certain third-party add-in high-speed serial communication interfaces, use the low "baud rates" to stand for higher ones. For example, SET SPEED 50 gets you 57600 bps; SET SPEED 75 gets you 76800; SET SPEED 110 gets 115200. SCO ODT 3.0 is an example where a "baud-rate-table patch" can be applied that can rotate the tty driver baud rate table such that 600=57600 and 1800=115k baud. Similarly for Digiboard multiport/portservers, which have a "fastbaud" setting that does this. Linux has a "setserial" command that can do it, etc. More modern Unixes support POSIX-based speed setting, in which the selection of speeds is not limited by a 4-bit field. C-Kermit 6.1 incorporates a new mechanism for finding out (at compile time) which serial speeds are supported by the operating system that does not involve editing of source code by hand; on systems like Solaris 5.1, IRIX 6.2, and SCO OSR5.0.4, "set speed ?" will list speeds up to 460800 or 921600. In C-Kermit 7.0 and later: 1. If a symbol for a particular speed (say B230400 for 230400 bps) appears in whatever header file defines acceptable serial speeds (e.g. or or , etc), the corresponding speed will appear in C-Kermit's "set speed ?" list. 2. The fact that a given speed is listed in the header files and appears in C-Kermit's list does not mean the driver will accept it. For example, a computer might have some standard serial ports plus some add-on ones with different drivers that accept a different repertoire of speeds. 3. The fact that a given speed is accepted by the driver does not guarantee the underlying hardware can accept it. When Kermit is given a "set speed" command for a particular device, the underlying system service is called to set the speed; its return code is checked and the SET SPEED command fails if the return code indicates failure. Regardless of the system service return status, the device's speed is then read back and if it does not match the speed that was requested, an error message is printed and the command fails. Even when the command succeeds, this does not guarantee successful operation at a particular speed, especially a high one. That depends on electricity, information theory, etc. How long is the cable, what is its capacitance, how well is it shielded, etc, not to mention that every connection has two ends and its success depends on both of them. (With the obvious caveats about internal modems, is the cable really connected, interrupt conflicts, etc etc etc). Note, in particular, that there is a certain threshold above which modems can not "autobaud" -- i.e. detect the serial interface speed when you type AT (or whatever else the modem's recognition sequence might be). Such modems need to be engaged at a lower speed (say 2400 or 9600 or even 115200 -- any speed below their autobaud threshold) and then must be given a modem-specific command (which can be found in the modem manual) to change their interface speed to the desired higher speed, and then the software must also be told to change to the new, higher speed. For additional information, read [564]Section 9.5 of the Installation Instructions, plus any platform-specific notes in [565]Section 3 above. 7. COMMUNICATIONS AND DIALING [ [566]Top ] [ [567]Contents ] [ [568]Next ] [ [569]Previous ] 7.1. Serial Ports and Modems If you SET LINE to a serial port modem-control device that has nothing plugged in to it, or has a modem connected that is powered off, and you have not given a prior SET MODEM TYPE or SET CARRIER-WATCH OFF command, the SET LINE command is likely to hang. In most cases, you can Ctrl-C out. If not, you'll have to kill C-Kermit from another terminal. Similarly, if you give a SET MODEM TYPE HAYES (or USR, or any other modem type besides DIRECT, NONE, or UNKNOWN) and then SET LINE to an empty port, the subsequent close (implicit or explicit) is liable to hang or even crash (through no fault of Kermit's -- the hanging or crashing is inside a system call such as cfsetospeed() or close()). The SET CARRIER-WATCH command works as advertised only if the underlying operating system and device drivers support this feature; in particular only if a read() operation returns immediately with an error code if the carrier signal goes away or, failing that, if C-Kermit can obtain the modem signals from the device driver (you can tell by giving a "set line" command to a serial device, and then a "show communications" command -- if modem signals are not listed, C-Kermit won't be able to detect carrier loss, the WAIT command will not work, etc). Of course, the device itself (e.g. modem) must be configured appropriately and the cables convey the carrier and other needed signals, etc. If you dial out from Unix system, but then notice a lot of weird character strings being stuck into your session at random times (especially if they look like +++ATQ0H0 or login banners or prompts), that means that getty is also trying to control the same device. You'll need to dial out on a device that is not waiting for a login, or else disable getty on the device. As of version 7.0, C-Kermit makes explicit checks for the Carrier Detect signal, and so catches hung-up connections much better than 6.0 and earlier. However, it still can not be guaranteed to catch every ever CD on-to-off transition. For example, when the HP-UX version of C-Kermit is in CONNECT mode on a dialed connection and CARRIER-WATCH ON or AUTO, and you turn off the modem, HP-UX is stuck in a read() that never returns. (C-Kermit does not pop back to its prompt automatically, but you can still escape back.) If, on the other hand, you log out from the remote system, and it hangs up, and CD drops on the local modem, C-Kermit detects this and pops back to the prompt as it should. (Evidently there can be a difference between CD and DSR turning off at the same time, versus CD turning off while DSR stays on; experimentation with &S0/&S1/&S2 on your modem might produce the desired results). When Unix C-Kermit exits, it closes (and must close) the communications device. If you were dialed out, this will most likely hang up the connection. If you want to get out of Kermit and still use Kermit's communication device, you have several choices: 1. Shell out from Kermit or suspend Kermit, and refer to the device literally (as in "term -blah -blah < /dev/cua > /dev/cua"). 2. Shell out from Kermit and use the device's file descriptor which Kermit makes available to you in the \v(ttyfd) variable. 3. Use C-Kermit's REDIRECT command. 4. Use C-Kermit new EXEC /REDIRECT command. If you are having trouble dialing: 1. Make sure the dialout line is configured correctly. More about this below. 2. Make sure all necessary patches are installed for your operating system. 3. If you can't dial on a "bidirectional" line, then configure it for outbound-only (remove the getty) and try again. (The mechanisms -- if any -- for grabbing bidirectional lines for dialout vary wildly among Unix implementations and releases, and C-Kermit -- which runs on well over 300 different Unix variations -- makes no effort to keep up with them; the recommended method for coping with this situation is to wrap C-Kermit in a shell script that takes the appropriate actions.) 4. Make sure C-Kermit's SET DIAL and SET MODEM parameters agree with the modem you are actually using -- pay particular attention to SET DIAL SPEED-MATCHING. 5. If MODEM HANGUP-METHOD is set to RS232-SIGNAL, change it to MODEM-COMMAND. Or vice-versa. 6. Try SET DIAL HANGUP OFF before the DIAL command. Also, SET DIAL DISPLAY ON to watch what's happening. See [570]Section 8 of the [571]Installation Instructions. 7. Read pages 50-67 of [572]Using C-Kermit. 8. As a last resort, don't use the DIAL command at all; SET CARRIER OFF and CONNECT to the modem and dial interactively, or write a script program to dial the modem. Make sure your dialout line is correctly configured for dialing out (as opposed to login). The method for doing this is different for each kind of Unix system. Consult your system documentation for configuring lines for dialing out (for example, Sun SparcStation IPC users should read the section "Setting up Modem Software" in the Desktop SPARC Sun System & Network Manager's Guide; HP-9000 workstation users should consult the manual Configuring HP-UX for Peripherals, etc). Symptom: DIAL works, but a subsequent CONNECT command does not. Diagnosis: the modem is not asserting Carrier Detect (CD) after the connection is made, or the cable does not convey the CD signal. Cure: Reconfigure the modem, replace the cable. Workaround: SET CARRIER OFF (at least in System-V based Unix versions). For Berkeley-Unix-based systems (4.3BSD and earlier), Kermit includes code to use LPASS8 mode when parity is none, which is supposed to allow 8-bit data and Xon/Xoff flow control at the same time. However, as of edit 174, this code is entirely disabled because it is unreliable: even though the host operating system might (or might not) support LPASS8 mode correctly, the host access protocols (terminal servers, telnet, rlogin, etc) generally have no way of finding out about it and therefore render it ineffective, causing file transfer failures. So as of edit 174, Kermit once again uses rawmode for 8-bit data, and so there is no Xon/Xoff flow control during file transfer or terminal emulation in the Berkeley-based versions (4.3 and earlier, not 4.4). Also on Berkeley-based systems (4.3 and earlier), there is apparently no way to configure a dialout line for proper carrier handling, i.e. ignore carrier during dialing, require carrier thereafter, get a fatal error on any attempt to read from the device after carrier drops (this is handled nicely in System V by manipulation of the CLOCAL flag). The symptom is that carrier loss does not make C-Kermit pop back to the prompt automatically. This is evident on the NeXT, for example, but not on SunOS, which supports the CLOCAL flag. This is not a Kermit problem, but a limitation of the underlying operating system. For example, the cu program on the NeXT doesn't notice carrier loss either, whereas cu on the Sun does. On certain AT&T Unix systems equipped with AT&T modems, DIAL and HANGUP don't work right. Workarounds: (1) SET DIAL HANGUP OFF before attempting to dial; (2) If HANGUP doesn't work, SET LINE, and then SET LINE to totally close and reopen the device. If all else fails, SET CARRIER OFF. C-Kermit does not contain any particular support for AT&T DataKit devices. You can use Kermit software to dial in to a DataKit line, but C-Kermit does not contain the specialized code required to dial out from a DataKit line. If the Unix system is connected to DataKit via serial ports, dialout should work normally (e.g. set line /dev/ttym1, set speed 19200, connect, and then see the DESTINATION: prompt, from which you can connect to another computer on the DataKit network or to an outgoing modem pool, etc). But if the Unix system is connected to the DataKit network through the special DataKit interface board, then SET LINE to a DataKit pseudodevice (such as /dev/dk031t) will not work (you must use the DataKit "dk" or "dkcu" program instead). In C-Kermit 7.0 and later, you can make Kermit connections "though" dk or dkcu using "set line /pty". In some BSD-based Unix C-Kermit versions, SET LINE to a port that has nothing plugged in to it with SET CARRIER ON will hang the program (as it should), but it can't be interrupted with Ctrl-C. The interrupt trap is correctly armed, but apparently the Unix open() call cannot be interrupted in this case. When SET CARRIER is OFF or AUTO, the SET LINE will eventually return, but then the program hangs (uninterruptibly) when the EXIT or QUIT command (or, presumably, another SET LINE command) is given. The latter is probably because of the attempt to hang up the modem. (In edit 169, a timeout alarm was placed around this operation.) With SET DIAL HANGUP OFF in effect, the DIAL command might work only once, but not again on the same device. In that case, give a CLOSE command to close the device, and then another SET LINE command to re-open the same device. Or rebuild your version of Kermit with the -DCLSOPN compile-time switch. The DIAL command says "To cancel: Type your interrupt character (normally Ctrl-C)." This is just one example of where program messages and documentation assume your interrupt character is Ctrl-C. But it might be something else. In most (but not necessarily all) cases, the character referred to is the one that generates the SIGINT signal. If Ctrl-C doesn't act as an interrupt character for you, type the Unix command "stty -a" or "stty all" or "stty everything" to see what your interrupt character is. (Kermit could be made to find out what the interrupt character is, but this would require a lot of platform-dependent coding and #ifdefs, and a new routine and interface between the platform-dependent and platform-independent parts of the program.) In general, the hangup operation on a serial communication device is prone to failure. C-Kermit tries to support many, many different kinds of computers, and there seems to be no portable method for hanging up a modem connection (i.e. turning off the RS-232 DTR signal and then turning it back on again). If HANGUP, DIAL, and/or Ctrl-\H do not work for you, and you are a programmer, look at the tthang() function in ckutio.c and see if you can add code to make it work correctly for your system, and send the code to the address above. (NOTE: This problem has been largely sidestepped as of edit 188, in which Kermit first attempts to hang up the modem by "escaping back" via +++ and then giving the modem's hangup command, e.g. ATH0, when DIAL MODEM-HANGUP is ON, which is the default setting.) Even when Kermit's modem-control software is configured correctly for your computer, it can only work right if your modem is also configured to assert the CD signal when it is connected to the remote modem and to hang up the connection when your computer drops the DTR signal. So before deciding Kermit doesn't work with your modem, check your modem configuration AND the cable (if any) connecting your modem to the computer -- it should be a straight-through [573]modem cable conducting the signals FG, SG, TD, RD, RTS, CTS, DSR, DTR, CD, and RI. Many Unix systems keep aliases for dialout devices; for example, /dev/acu might be an alias for /dev/tty00. But most of these Unix systems also use UUCP lockfile conventions that do not take this aliasing into account, so if one user assigns (e.g.) /dev/acu, then another user can still assign the same device by referring to its other name. This is not a Kermit problem -- Kermit must follow the lockfile conventions used by the vendor-supplied software (cu, tip, uucp). The SET FLOW-CONTROL KEEP option should be given *before* any communication (dialing, terminal emulation, file transfer, INPUT/OUTPUT/TRANSMIT, etc) is attempted, if you want C-Kermit to use all of the device's preexisting flow-control related settings. The default flow-control setting is XON/XOFF, and it will take effect when the first communication-related command is given, and a subsequent SET FLOW KEEP command will not necessarily know how to restore *all* of the device's original flow-control settings. 7.2. Network Connections C-Kermit tries to use the 8th bit for data when parity is NONE, and this generally works on real Unix terminal (tty) devices, but it often does not work when the Unix system is accessed over a network via telnet or rlogin protocols, including (in many cases) through terminal servers. For example, an Encore computer with Annex terminal servers only gives a 7-bit path if the rlogin protocol is selected in the terminal server but it gives the full 8 bits if the proprietary RDP protocol is used. If file transfer does not work through a host to which you have rlogin'd, use "rlogin -8" rather than "rlogin". If that doesn't work, tell both Kermit programs to "set parity space". The Encore TELNET server does not allow long bursts of input. When you have a TELNET connection to an Encore, tell C-Kermit on the Encore to SET RECEIVE PACKET-LENGTH 200 or thereabouts. 8. HARDWARE FLOW CONTROL [ [574]Top ] [ [575]Contents ] [ [576]Next ] [ [577]Previous ] SET FLOW RTS/CTS is available in Unix C-Kermit only when the underlying operating system provides an Application Program Interface (API) for turning this feature on and off under program control, which turns out to be a rather rare feature among Unix systems. To see if your Unix C-Kermit version supports hardware flow control, type "set flow ?" at the C-Kermit prompt, and look for "rts/cts" among the options. Other common situations include: 1. The API is available, so "set flow rts/cts" appears as a valid C-Kermit command, but it doesn't do anything because the device driver (part of the operating system) was never coded to do hardware flow control. This is common among System V R4 implementations (details below). 2. The API is not available, so "set flow rts/cts" does NOT appear as a valid C-Kermit command, but you can still get RTS/CTS flow control by selecting a specially named device in your SET LINE command. Examples: + NeXTSTEP: /dev/cufa instead of /dev/cua, /dev/cufb instead of /dev/cub (68040 only; "man zs" for further info). + IRIX: /dev/ttyf2 instead of /dev/ttyd2 or /dev/ttym2 ("man 7 serial"). 3. The API is available, doesn't work, but a workaround as in (2) can be used. 4. The API is available, but Kermit doesn't know about it. In these cases, you can usually use an stty command to enable RTS/CTS on the device, e.g. "stty crtscts" or "stty ctsflow", "stty rtsflow", before starting Kermit, and then tell Kermit to SET FLOW KEEP. 5. No API and no special device drivers. Hardware flow control is completely unavailable. System V R4 based Unixes are supposed to supply a file, which gives Kermit the necessary interface to command the terminal driver to enable/disable hardware flow control. Unfortunately, but predictably, many implementations of SVR4 whimsically place this file in /usr/include/sys rather than /usr/include (where SVID clearly specifies it should be; see SVID, Third Edition, V1, termiox(BA_DEV). Thus if you build C-Kermit with any of the makefile entries that contain -DTERMIOX or -DSTERMIOX (the latter to select ), C-Kermit will have "set flow rts/cts" and possibly other hardware flow-control related commands. BUT... That does not necessarily mean that they will work. In some cases, the underlying functions are simply not coded into the operating system. WARNING: When hardware flow control is available, and you enable in Kermit on a device that is not receiving the CTS signal, Kermit can hang waiting for CTS to come up. This is most easily seen when the local serial port has nothing plugged in to it, or is connected to an external modem that is powered off. 9. TERMINAL CONNECTION AND KEY MAPPING [ [578]Top ] [ [579]Contents ] [ [580]Next ] [ [581]Previous ] C-Kermit is not a terminal emulator. Refer to page 147 of [582]Using C-Kermit, 2nd Edition: "Most versions of C-Kermit -- Unix, VMS, AOS/VS, VOS, etc -- provide terminal connection without emulation. These versions act as a 'semitransparent pipe' between the remote computer and your terminal, terminal emulator, console driver, or window, which in turn emulates (or is) a specific kind of terminal." The environment in which you run C-Kermit is up to you. If you are an X Windows user, you should be aware of an alternative to xterm that supports VT220 emulation, from Thomas E. Dickey: [583]http://dickey.his.com/xterm/xterm.html Unix C-Kermit's SET KEY command currently can not be used with keys that generate "wide" scan codes or multibyte sequences, such as workstation function or arrow keys, because Unix C-Kermit does not have direct access to the keyboard. However, many Unix workstations and/or console drivers provide their own key mapping feature. With xterm, for example, you can use 'xmodmap' ("man xmodmap" for details); here is an xterm mapping to map the Sun keyboard to DEC VT200 values for use with VT-terminal oriented applications like VMS EVE: keycode 101=KP_0 keycode 119=KP_1 keycode 120=KP_2 keycode 121=KP_3 keycode 98=KP_4 keycode 99=KP_5 keycode 100=KP_6 keycode 75=KP_7 keycode 76=KP_8 keycode 77=KP_9 keycode 52=KP_F1 keycode 53=KP_F2 keycode 54=KP_F3 keycode 57=KP_Decimal keycode 28=Left keycode 29=Right keycode 30=KP_Separator keycode 105=KP_F4 keycode 78=KP_Subtract keycode 8=Left keycode 10=Right keycode 32=Up keycode 33=Down keycode 97=KP_Enter Users of Linux consoles can use loadkeys ("man dumpkeys loadkeys keytables" for details. The format used by loadkeys is compatible with that used by Xmodmap, although it is not definitely certain that the keycodes are compatible for different keyboard types (e.g. Sun vs HP vs PC, etc). 10. FILE TRANSFER [ [584]Top ] [ [585]Contents ] [ [586]Next ] [ [587]Previous ] On most platforms, C-Kermit can not handle files longer than 2^31 or 2^32 bytes long, because it uses the traditional file i/o APIs that use 32-bit words to represent the file size. To accommodate longer files, we would have to switch to a new and different API. Unfortunately, each platform has a different one, a nightmare to handle in portable code. The C-Kermit file code was written in the days long before files longer than 2GB were supported or even contemplated in the operating systems where C-Kermit ran. If uploads (or downloads) fail immediately, give the CAUTIOUS command to Kermit and try again. If they still fail, then try SET PREFIXING ALL. If they still fail, try SET PARITY SPACE. If they still fail, try ROBUST. If reception (particularly of large files and/or binary files) begins successfully but then fail consistently after a certain amount of bytes have been sent, check: * Your ulimit ("ulimit -a") * The amount of available space on the target disk ("df ." or "df -k .") * Your personal disk quota (platform- and site-dependent) * The maximum file size on the receiver's file system (e.g. 2GB in old versions the Linux VFS file system, and/or in applications that have not been recoded to use new "large file" APIs). * If it's an NFS-mounted disk (if so, try uploading to a local disk) * Is there an "idle limit" on the receiving end? If none of these seem to explain it, then the problem is not size related, but reflects some clash between the file contents and the characteristics of the connection, in which case follow the instructions in the first paragraph of this section. Suppose two copies of Kermit are receiving files into the same directory, and the files have the same name, e.g. "foo.bar". Whichever one starts first opens an output file called "foo.bar". The second one sees there is already a foo.bar file, and so renames the existing foo.bar to foo.bar.~1~ (or whatever). When the first file has been received completely, Kermit goes to change its modification time and permissions to those given by the file sender in the Attribute packet. But in Unix, the APIs for doing this take a filename, not a file descriptor. Since the first Kermit's file has been renamed, and the second Kermit is using the original name, the first Kermit changes the modtime and permissions of the second Kermit's file, not its own. Although there might be a way to work around this in the code, e.g. using inode numbers to keep track of which file is which, this would be tricky and most likely not very portable. It's better to set up your application to prevent such things from happening, which is easy enough using the script language, filename templates, etc. Suppose you start C-Kermit with a command-line argument to send or receive a file (e.g. "kermit -r") and then type Ctrl-\c immediately afterwards to escape back and initiate the other end of the transfer, BUT your local Kermit's escape character is not Ctrl-\. In this case, the local Kermit passes the Ctrl-\ to the remote system, and if this is Unix, Ctrl-\ is likely to be its SIGQUIT character, which causes the current program to halt and dump core. Well, just about the first thing C-Kermit does when it starts is to disable the SIGQUIT signal. However, it is still possible for SIGQUIT to cause Kermit to quit and dump core if it is delivered while Kermit is being loaded or started, before the signal can be disabled. There's nothing Kermit itself can do about this, but you can prevent it from happening by disabling SIGQUIT in your Unix session. The command is usually something like: stty quit undef Unix C-Kermit does not reject incoming files on the basis of size. There appears to be no good (reliable, portable) way to determine in advance how much disk space is available, either on the device, or (when quotas or other limits are involved) to the user. Unix C-Kermit discards all carriage returns from incoming files when in text mode. If C-Kermit has problems creating files in writable directories when it is installed setuid or setgid on BSD-based versions of Unix such as NeXTSTEP 3.0, it probably needs to be rebuilt with the -DSW_ACC_ID compilation switch. If you SET FILE DISPLAY FULLSCREEN, and C-Kermit complains "Sorry, terminal type not supported", it means that the terminal library (termcap or termlib) that C-Kermit was built with does not know about a terminal whose name is the current value of your TERM environment variable. If this happens, but you want to have the fullscreen file transfer display, EXIT from C-Kermit and set a Unix terminal type from among the supported values that is also supported by your terminal emulator, or else have an entry for your terminal type added to the system termcap and/or terminfo database. If you attempt to suspend C-Kermit during local-mode file transfer and then continue it in the background (via bg), it will block for "tty output" if you are using the FULLSCREEN file transfer display. This is apparently a problem with curses. Moving a local-mode file transfer back and forth between foreground and background works correctly, however, with the SERIAL, CRT, BRIEF, or NONE file transfer displays. If C-Kermit's command parser no longer echoes, or otherwise acts strangely, after returning from a file transfer with the fullscreen (curses) display, and the curses library for your version of Unix includes the newterm() function, then try rebuilding your version of C-Kermit with -DCK_NEWTERM. Similarly if it echoes doubly, which might even happen during a subsequent CONNECT session. If rebuilding with -DCK_NEWTERM doesn't fix it, then there is something very strange about your system's curses library, and you should probably not use it. Tell C-Kermit to SET FILE DISPLAY CRT, BRIEF, or anything else other than FULLSCREEN, and/or rebuild without -DCK_CURSES, and without linking with (termlib and) curses. Note: This problem seemed to have escalated in C-Kermit 7.0, and -DCK_NEWTERM had to be added to many builds that previously worked without it: Linux, AIX 4.1, DG/UX, etc. In the Linux case, it is obviously because of changes in the (n)curses library; the cause in the other cases is not known. C-Kermit creates backup-file names (such as "oofa.txt.~1~") based on its knowledge of the maximum filename length on the platform where it is running, which is learned at compile time, based on MAXNAMLEN or equivalent symbols from the system header files. But suppose C-Kermit is receiving files on a Unix platform that supports long filenames, but the incoming files are being stored on an NFS-mounted file system that supports only short names. NFS maps the external system to the local APIs, so C-Kermit has no way of knowing that long names will be truncated. Or that C-Kermit is running on a version of Unix that supports both long-name and short-name file systems simultaneously (such as HP-UX 7.00). This can cause unexpected behavior when creating backup files, or worse. For example, you are sending a group of files whose names are differentiated only by characters past the point at which they would be truncated, each file will overwrite the previous one upon arrival. 11. EXTERNAL FILE TRANSFER PROTOCOLS [ [588]Top ] [ [589]Contents ] [ [590]Next ] [ [591]Previous ] SECTION CONTENTS 11.1. [592]C-Kermit as an External Protocol 11.2. [593]Invoking External Protocols from C-Kermit Unix C-Kermit can be used in conjunction with other communications software in various ways. C-Kermit can be invoked from another communications program as an "external protocol", and C-Kermit can also invoke other communication software to perform external protocols. This sort of operation makes sense only when you are dialing out from your Unix system (or making a network connection from it). If the Unix system is the one you have dialed in to, you don't need any of these tricks. Just run the desired software on your Unix system instead of Kermit. When dialing out from a Unix system, the difficulty is getting two programs to share the same communication device in spite of the Unix UUCP lockfile mechanism, which would normally prevent any sharing, and preventing the external protocol from closing (and therefore hanging up) the device when it exits back to the program that invoked it. 11.1. C-KERMIT AS AN EXTERNAL PROTOCOL [ [594]Top ] [ [595]Contents ] [ [596]Section Contents ] [ [597]Next ] (This section deleted; see [598]Using C-Kermit, 2nd Ed, Chapter 14.) "pcomm" is a general-purpose terminal program that provides file transfer capabilities itself (X- and YMODEM variations) and the ability to call on external programs to do file transfers (ZMODEM and Kermit, for example). You can tell pcomm the command to send or receive a file with an external protocol: Send Receive ZMODEM sz filename rz Kermit kermit -s filename kermit -r pcomm runs external programs for file transfer by making stdin and stdout point to the modem port, and then exec-ing "/bin/sh -c xxx" (where xxx is the appropriate command). However, C-Kermit does not treat stdin and stdout as the communication device unless you instruct it: Send Receive Kermit kermit -l 0 -s filename kermit -l 0 -r The "-l 0" option means to use file descriptor 0 for the communication device. In general, any program can pass any open file descriptor to C-Kermit for the communication device in the "-l" command-line option. When Kermit is given a number as the argument to the "-l" option, it simply uses it as a file descriptor, and it does not attempt to close it upon exit. Here's another example, for Seyon (a Linux communication program). First try the technique above. If that works, fine; otherwise... If Seyon does not give you a way to access and pass along the file descriptor, but it starts up the Kermit program with its standard i/o redirected to its (Seyon's) communications file descriptor, you can also experiment with the following method, which worked here in brief tests on SunOS. Instead of having Seyon use "kermit -r" or "kermit -s filename" as its Kermit protocol commands, use something like this (examples assume C-Kermit 6.0): For serial connections: kermit -YqQl 0 -r <-- to receive kermit -YqQl 0 -s filename(s) <-- to send one or more files For Telnet connections: kermit -YqQF 0 -r <-- to receive kermit -YqQF 0 -s filename(s) <-- to send one or more files Command line options: Y - skip executing the init file Q - use fast file transfer settings (default in 8.0) l 0 - transfer files using file descriptor 0 for a serial connection F 0 - transfer files using file descriptor 0 for a Telnet connection q - quiet - no messages r - receive s - send 11.2. INVOKING EXTERNAL PROTOCOLS FROM C-KERMIT [ [599]Top ] [ [600]Contents ] [ [601]Section Contents ] [ [602]Previous ] (This section is obsolete, but not totally useless. See Chapter 14 of [603]Using C-Kermit, 2nd Edition). After you have opened a communication link with C-Kermit's SET LINE (SET PORT) or SET HOST (TELNET) command, C-Kermit makes its file descriptor available to you in the \v(ttyfd) variable so you can pass it along to other programs that you RUN from C-Kermit. Here, for example, C-Kermit runs itself as an external protocol: C-Kermit>set modem type hayes C-Kermit>set line /dev/acu C-Kermit>set speed 2400 C-Kermit>dial 7654321 Call complete. C-Kermit>echo \v(ttyfd) 3 C-Kermit>run kermit -l \v(ttyfd) Other programs that accept open file descriptors on the command line can be started in the same way. You can also use your shell's i/o redirection facilities to assign C-Kermit's open file descriptor (ttyfd) to stdin or stdout. For example, old versions of the Unix ZMODEM programs, sz and rz, when invoked as external protocols, expect to find the communication device assigned to stdin and stdout with no option for specifying any other file descriptor on the sz or rz command line. However, you can still invoke sz and rz as exterior protocols from C-Kermit if your current shell ($SHELL variable) is ksh (the Korn shell) or bash (the Bourne-Again shell), which allows assignment of arbitrary file descriptors to stdin and stdout: C-Kermit> run rz <&\v(ttyfd) >&\v(ttyfd) or: C-Kermit> run sz oofa.zip <&\v(ttyfd) >&\v(ttyfd) In version 5A(190) and later, you can use C-Kermit's REDIRECT command, if it is available in your version of C-Kermit, to accomplish the same thing without going through the shell: C-Kermit> redirect rz or: C-Kermit> redirect sz oofa.zip A complete set of rz,sz,rb,sb,rx,sx macros for Unix C-Kermit is defined in the file ckurzsz.ini. It automatically chooses the best redirection method (but is redundant since C-Kermit 6.0, which now has built-in support for external protocols via its SET PROTOCOL command). Note that external protocols can be used on C-Kermit SET LINE or SET HOST connections only if they operate through standard input and standard output. If they open their own connections, Kermit can't redirect them over its own connection. 12. SECURITY [ [604]Top ] [ [605]Contents ] [ [606]Next ] [ [607]Previous ] As of version 7.0, C-Kermit supports a wide range of security options for authentication and encryption: Kerberos 4, Kerberos 5 / GSSAPI, SSL/TLS, and SRP. See the separate [608]security document for details. 13. MISCELLANEOUS USER REPORTS [ [609]Top ] [ [610]Contents ] [ [611]Next ] [ [612]Previous ] Date: Thu, 12 Mar 92 1:59:25 MEZ From: Walter Mecky Subject: Help.Unix.sw To: svr4@pcsbst.pcs.com, source@usl.com PRODUCT: Unix RELEASE: Dell SVR4 V2.1 (is USL V3.0) MACHINE: AT-386 PATHNAME: /usr/lib/libc.so.1 /usr/ccs/lib/libc.a ABSTRACT: Function ttyname() does not close its file descriptor DESCRIPTION: ttyname(3C) opens /dev but never closes it. So if it is called often enough the open(2) in ttyname() fails. Because the broken ttyname() is in the shared lib too all programs using it can fail if they call it often enough. One important program is uucico which calls ttyname for every file it transfers. Here is a little test program if your system has the bug: #include #include main() { int i = 0; while (ttyname(0) != NULL) i++; perror("ttyname"); printf("i=%d\n", i); } If this program runs longer than some seconds you don't have the bug. WORKAROUND: None FIX: Very easy if you have source code. Another user reports some more explicit symptoms and recoveries: > What happens is when invoking ckermit we get one of the following > error messages: > You must set line > Not a tty > No more processes. > One of the following three actions clears the problem: > shutdown -y -g0 -i6 > kill -9 the ttymon with the highest PID > Invoke sysadm and disable then enable the line you want to use. > Turning off respawn of sac -t 300 and going to getty's and uugetty's > does not help. > > Also C-Kermit reports "?timed out closing /dev/ttyxx". > If this happens all is well. ------------------------------ (Note: the following problem also occurs on SGI and probably many other Unix systems): From: James Spath To: Info-Kermit-Request@cunixf.cc.columbia.edu Date: Wed, 9 Sep 1992 20:20:28 -0400 Subject: C-Kermit vs uugetty (or init) on Sperry 5000 We have successfully compiled the above release on a Unisys/Sperry 5000/95. We used the sys5r3 option, rather than sys5r2 since we have VR3 running on our system. In order to allow dialout access to non-superusers, we had to do "chmod 666 /dev/tty###, where it had been -rw--w--w- (owned by uucp), and to do "chmod +w /usr/spool/locks". We have done text and binary file transfers through local and remote connections. The problem concerning uucp ownership and permissions is worse than I thought at first. Apparently init or uugetty changes the file permissions after each session. So I wrote the following C program to open a set of requested tty lines. I run this for any required outgoing line prior to a Kermit session. ------ cut here ------- /* opentty.c -- force allow read on tty lines for modem i/o */ /* idea from: restrict.c -- System 5 Admin book Thomas/Farrow p. 605 */ /* /jes jim spath {spath@jhunix.hcj.jhu.edu } */ /* 08-Sep-92 NO COPYRIGHT. */ /* this must be suid to open other tty lines */ /* #define DEBUG */ #define TTY "/dev/tty" #define LOK "/usr/spool/locks/LCK..tty" #include /* allowable lines: */ #define TOTAL_LINES 3 static char allowable[TOTAL_LINES][4] = { "200", "201", "300" }; static int total=TOTAL_LINES; int allow; /* states: */ #define TTY_UNDEF 0 #define TTY_LOCK 1 #define TTY_OKAY 2 main(argc, argv) int argc; char *argv[]; { char device[512]; char lockdev[512]; int i; if (argc == 1) { fprintf(stderr, "usage: open 200 [...]\n"); } while (--argc > 0 && (*++argv) != NULL ) { #ifdef DEBUG fprintf(stderr, "TRYING: %s%s\n", TTY, *argv); #endif sprintf(device, "%s%s", TTY, *argv); sprintf(lockdev, "%s%s", LOK, *argv); allow = TTY_UNDEF; i = 0; while (i <= total) { /* look at all defined lines */ #ifdef DEBUG fprintf(stderr, "LOCKFILE? %s?\n", lockdev); #endif if (access(lockdev, 00) == 0) { allow=TTY_LOCK; break; } #ifdef DEBUG fprintf(stderr, "DOES:%s==%s?\n", allowable[i], *argv); #endif if (strcmp(allowable[i], *argv) == 0) allow=TTY_OKAY; i++; } #ifdef DEBUG fprintf(stderr, "allow=%d\n", allow); #endif switch (allow) { case TTY_UNDEF: fprintf (stderr, "open: not allowed on %s\n", *argv); break; case TTY_LOCK: fprintf (stderr, "open: device locked: %s\n", lockdev); break; case TTY_OKAY: /* attempt to change mode on device */ if (chmod (device, 00666) < 0) fprintf (stderr, "open: cannot chmod on %s\n", device); break; default: fprintf (stderr, "open: FAULT\n"); } } exit (0); } 14. THIRD-PARTY DRIVERS [ [613]Top ] [ [614]Contents ] [ [615]Next ] [ [616]Previous ] Unix versions, especially those for PCs (SCO, Unixware, etc) might be augmented by third-party communication-board drivers from Digiboard, Stallion, etc. These can sometimes complicate matters for Kermit considerably since Kermit has no way of knowing that it is going through a possibly nonstandard driver. Various examples are listed in the earlier sections of this document; search for Stallion, Digiboard, etc. Additionally: * The Stallion Technologies EasyConnection serial board driver does not always report the state of DSR as low. From Stallion (October 1997): "Unfortunately, this is a bug in our driver. We have implemented all of the other TIOMC functions, eg DTR, DCD, RTS and CTS, but not DSR. Our driver should report the actual state of DSR on those of our cards that have a DSR signal. That the driver always reports DSR as not asserted (0), is a bug in the driver. The driver should be either reporting the state of DSR correctly on those cards that support DSR or as always asserted (1) on those cards that do not have a DSR signal. This will be fixed in a future version of our drivers; at this time I cannot say when this will be." And later, "As far as I can tell, we don't support the termios/termiox ioctls that relate specifically to DSR and RI; all the rest are supported. This will, as I mentioned earlier, be fixed in the next release of our ATA software." - World Wide Escalation Support, Stallion Technologies, Toowong QLD, [617]support@stallion.oz.au. Later (December 1997, from the same source): * We have now released a new version of the ATA software, version 5.4.0. This version fixes the problem with the states of the DSR and RI signals and how they were being reported by the driver. This is the problem that you reported in October. The DSR signal is reported correctly on those cards that support the DSR signal, such as the early revision of the EasyIO card and the EasyConnection 8D4 panel, and as always asserted on those cards that do not support the DSR signal in the hardware. The new driver is available from our Web site, [618]www.stallion.com, in the /drivers/ata5/UnixWare directory. [ [619]Top ] [ [620]Contents ] [ [621]C-Kermit Home ] [ [622]C-Kermit 8.0 Overview ] [ [623]Kermit Home ] __________________________________________________________________ C-Kermit 8.0 Unix Hints and Tips / [624]The Kermit Project / [625]Columbia University / [626]kermit@columbia.edu References 1. http://www.columbia.edu/ 2. mailto:kermit@columbia.edu 3. http://www.columbia.edu/kermit/index.html 4. http://www.columbia.edu/kermit/k95.html 5. http://www.columbia.edu/kermit/ckermit.html 6. http://www.columbia.edu/kermit/ckscripts.html 7. http://www.columbia.edu/kermit/current.html 8. http://www.columbia.edu/kermit/whatsnew.html 9. http://www.columbia.edu/kermit/faq.html 10. http://www.columbia.edu/kermit/support.html 11. http://www.columbia.edu/kermit/ 12. http://www.columbia.edu/ 13. http://www.columbia.edu/kermit/ckubwr.html 14. http://www.columbia.edu/kermit/ckdaily.html 15. http://www.columbia.edu/kermit/ckdaily.html 16. http://www.columbia.edu/kermit/ckdaily.html 17. http://www.columbia.edu/kermit/ckermit.html 18. http://www.columbia.edu/kermit/ckuins.html 19. http://www.columbia.edu/kermit/ckututor.html 20. http://www.columbia.edu/kermit/ckubwr.html#x1 21. http://www.columbia.edu/kermit/ckubwr.html#x2 22. http://www.columbia.edu/kermit/ckubwr.html#x3 23. http://www.columbia.edu/kermit/ckubwr.html#x4 24. http://www.columbia.edu/kermit/ckubwr.html#x5 25. http://www.columbia.edu/kermit/ckubwr.html#x6 26. http://www.columbia.edu/kermit/ckubwr.html#x7 27. http://www.columbia.edu/kermit/ckubwr.html#x8 28. http://www.columbia.edu/kermit/ckubwr.html#x9 29. http://www.columbia.edu/kermit/ckubwr.html#x10 30. http://www.columbia.edu/kermit/ckubwr.html#x11 31. http://www.columbia.edu/kermit/ckubwr.html#x12 32. http://www.columbia.edu/kermit/ckubwr.html#x13 33. http://www.columbia.edu/kermit/ckubwr.html#x14 34. http://www.columbia.edu/kermit/ckubwr.html#x3.3 35. http://www.columbia.edu/kermit/ckubwr.html#x3.18 36. http://www.columbia.edu/kermit/ckubwr.html#x3.19 37. http://www.columbia.edu/kermit/ckubwr.html#x3.1 38. http://www.columbia.edu/kermit/ckubwr.html#x3.2 39. http://www.columbia.edu/kermit/ckubwr.html#x3.7 40. http://www.columbia.edu/kermit/ckubwr.html#x3.6 41. http://www.columbia.edu/kermit/ckubwr.html#top 42. http://www.columbia.edu/kermit/ckubwr.html#contents 43. http://www.columbia.edu/kermit/ckubwr.html#x2 44. http://www.columbia.edu/kermit/ckubwr.html#x1.1 45. http://www.columbia.edu/kermit/ckubwr.html#x1.2 46. http://www.columbia.edu/kermit/ckubwr.html#x1.3 47. http://www.columbia.edu/kermit/ckubwr.html#x1.4 48. http://www.columbia.edu/kermit/ckubwr.html#x3.3 49. http://www.columbia.edu/kermit/ckubwr.html#x3.1 50. http://www.columbia.edu/kermit/ckubwr.html#x3.2 51. http://www.columbia.edu/kermit/ckubwr.html#x3.7 52. http://www.columbia.edu/kermit/ckcbwr.html 53. mailto:kermit-support@columbia.edu 54. http://www.columbia.edu/kermit/ckubwr.html#top 55. http://www.columbia.edu/kermit/ckubwr.html#contents 56. http://www.columbia.edu/kermit/ckubwr.html#x1.2 57. http://www.columbia.edu/kermit/ck60manual.html 58. http://www.columbia.edu/kermit/ckermit70.html 59. http://www.columbia.edu/kermit/ckermit80.html 60. http://www.columbia.edu/kermit/ckermit90.html 61. http://www.columbia.edu/kermit/ckubwr.html#top 62. http://www.columbia.edu/kermit/ckubwr.html#contents 63. http://www.columbia.edu/kermit/ckubwr.html#x1 64. http://www.columbia.edu/kermit/ckubwr.html#x1.3 65. http://www.columbia.edu/kermit/ckubwr.html#x1.1 66. http://www.columbia.edu/kermit/support.html 67. http://www.columbia.edu/kermit/ckubwr.html#top 68. http://www.columbia.edu/kermit/ckubwr.html#contents 69. http://www.columbia.edu/kermit/ckubwr.html#x1 70. http://www.columbia.edu/kermit/ckubwr.html#x1.4 71. http://www.columbia.edu/kermit/ckubwr.html#x1.2 72. http://www.columbia.edu/kermit/ckubwr.html#top 73. http://www.columbia.edu/kermit/ckubwr.html#contents 74. http://www.columbia.edu/kermit/ckubwr.html#x1 75. http://www.columbia.edu/kermit/ckubwr.html#x1.3 76. http://www.columbia.edu/kermit/ckubwr.html#top 77. http://www.columbia.edu/kermit/ckubwr.html#contents 78. http://www.columbia.edu/kermit/ckubwr.html#x3 79. http://www.columbia.edu/kermit/ckubwr.html#x1 80. http://www.columbia.edu/kermit/ckubwr.html#top 81. http://www.columbia.edu/kermit/ckubwr.html#contents 82. http://www.columbia.edu/kermit/ckubwr.html#x4 83. http://www.columbia.edu/kermit/ckubwr.html#x2 84. http://www.columbia.edu/kermit/ckubwr.html#x3.0 85. http://www.columbia.edu/kermit/ckubwr.html#x3.1 86. http://www.columbia.edu/kermit/ckubwr.html#x3.2 87. http://www.columbia.edu/kermit/ckubwr.html#x3.3 88. http://www.columbia.edu/kermit/ckubwr.html#x3.4 89. http://www.columbia.edu/kermit/ckubwr.html#x3.5 90. http://www.columbia.edu/kermit/ckubwr.html#x3.6 91. http://www.columbia.edu/kermit/ckubwr.html#x3.7 92. http://www.columbia.edu/kermit/ckubwr.html#x3.8 93. http://www.columbia.edu/kermit/ckubwr.html#x3.9 94. http://www.columbia.edu/kermit/ckubwr.html#x3.10 95. http://www.columbia.edu/kermit/ckubwr.html#x3.11 96. http://www.columbia.edu/kermit/ckubwr.html#x3.12 97. http://www.columbia.edu/kermit/ckubwr.html#x3.13 98. http://www.columbia.edu/kermit/ckubwr.html#x3.14 99. http://www.columbia.edu/kermit/ckubwr.html#x3.15 100. http://www.columbia.edu/kermit/ckubwr.html#x3.16 101. http://www.columbia.edu/kermit/ckubwr.html#x3.17 102. http://www.columbia.edu/kermit/ckubwr.html#x3.18 103. http://www.columbia.edu/kermit/ckubwr.html#x3.19 104. http://www.columbia.edu/kermit/ckubwr.html#x3.20 105. http://www.faqs.org/ 106. http://aplawrence.com/Unixart/newtounix.html 107. http://www.columbia.edu/kermit/ckubwr.html#x3 108. mailto:kermit-support@columbia.edu 109. http://www.columbia.edu/kermit/support.html 110. http://www.columbia.edu/kermit/ckubwr.html#top 111. http://www.columbia.edu/kermit/ckubwr.html#contents 112. http://www.columbia.edu/kermit/ckubwr.html#x3 113. http://www.columbia.edu/kermit/ckubwr.html#x3.1 114. http://www.pcunix.com/ 115. http://www.columbia.edu/kermit/ckubwr.html#x3.0.1 116. http://www.columbia.edu/kermit/ckubwr.html#x3.0.2 117. http://www.columbia.edu/kermit/ckubwr.html#x3.0.3 118. http://www.columbia.edu/kermit/ckubwr.html#x3.0.4 119. http://www.columbia.edu/kermit/ckubwr.html#x3.0.5 120. http://www.columbia.edu/kermit/ckubwr.html#x3.0.6 121. http://www.columbia.edu/kermit/ckubwr.html#top 122. http://www.columbia.edu/kermit/ckubwr.html#contents 123. http://www.columbia.edu/kermit/ckubwr.html#x3.0 124. http://www.columbia.edu/kermit/ckubwr.html#x3.0.2 125. http://www.columbia.edu/kermit/ckubwr.html#top 126. http://www.columbia.edu/kermit/ckubwr.html#contents 127. http://www.columbia.edu/kermit/ckubwr.html#x3.0 128. http://www.columbia.edu/kermit/ckubwr.html#x3.0.3 129. http://www.columbia.edu/kermit/ckubwr.html#x3.0.1 130. http://www.linmodems.org/ 131. http://www.microsoft.com/hwdev/platform/PCdesign/LR/default.asp 132. http://www.columbia.edu/kermit/ckubwr.html#top 133. http://www.columbia.edu/kermit/ckubwr.html#contents 134. http://www.columbia.edu/kermit/ckubwr.html#x3.0 135. http://www.columbia.edu/kermit/ckubwr.html#x3.0.4 136. http://www.columbia.edu/kermit/ckubwr.html#x3.0.2 137. http://www.idir.net/~gromitkc/winmodem.html 138. http://www.digi.com/ 139. http://www.columbia.edu/kermit/ckubwr.html#top 140. http://www.columbia.edu/kermit/ckubwr.html#contents 141. http://www.columbia.edu/kermit/ckubwr.html#x3.0 142. http://www.columbia.edu/kermit/ckubwr.html#x3.0.5 143. http://www.columbia.edu/kermit/ckubwr.html#x3.0.3 144. http://www.columbia.edu/kermit/ckubwr.html#top 145. http://www.columbia.edu/kermit/ckubwr.html#contents 146. http://www.columbia.edu/kermit/ckubwr.html#x3.0 147. http://www.columbia.edu/kermit/ckubwr.html#x3.0.6 148. http://www.columbia.edu/kermit/ckubwr.html#x3.0.4 149. http://www.columbia.edu/kermit/ckubwr.html#top 150. http://www.columbia.edu/kermit/ckubwr.html#contents 151. http://www.columbia.edu/kermit/ckubwr.html#x3.0 152. http://www.columbia.edu/kermit/ckubwr.html#x3.0.5 153. http://www.columbia.edu/kermit/ckubwr.html#top 154. http://www.columbia.edu/kermit/ckubwr.html#contents 155. http://www.columbia.edu/kermit/ckubwr.html#x3 156. http://www.columbia.edu/kermit/ckubwr.html#x3.2 157. http://www.columbia.edu/kermit/ckubwr.html#x3.0 158. http://www.columbia.edu/kermit/ckubwr.html#x3.1.1 159. http://www.columbia.edu/kermit/ckubwr.html#x3.1.2 160. http://www.columbia.edu/kermit/ckubwr.html#x3.1.3 161. http://www.columbia.edu/kermit/ckubwr.html#x3.1.4 162. http://www.columbia.edu/kermit/ckubwr.html#x3.1.5 163. http://www.emerson.emory.edu/services/aix-faq/ 164. http://www.faqs.org/faqs/by-newsgroup/comp/comp.unix.aix.html 165. http://www.cis.ohio-state.edu/hypertext/faq/usenet/aix-faq/top.html 166. http://aixpdslib.seas.ucla.edu/ 167. http://www.rootvg.net(AIXhistory)/ 168. ftp://rtfm.mit.edu/pub/usenet/news.answers/aix-faq/part1 169. ftp://mirrors.aol.com/pub/rtfm/usenet-by-hierarchy/comp/unix/aix 170. news:comp.unix.aix 171. http://www.columbia.edu/kermit/ckubwr.html#top 172. http://www.columbia.edu/kermit/ckubwr.html#contents 173. http://www.columbia.edu/kermit/ckubwr.html#x3.1 174. http://www.columbia.edu/kermit/ckubwr.html#x3.1.2 175. http://www.columbia.edu/kermit/ckubwr.html#top 176. http://www.columbia.edu/kermit/ckubwr.html#contents 177. http://www.columbia.edu/kermit/ckubwr.html#x3.1 178. http://www.columbia.edu/kermit/ckubwr.html#x3.1.3 179. http://www.columbia.edu/kermit/ckubwr.html#x3.1.1 180. http://www.columbia.edu/kermit/security.html#servers 181. http://www.columbia.edu/kermit/ckubwr.html#top 182. http://www.columbia.edu/kermit/ckubwr.html#contents 183. http://www.columbia.edu/kermit/ckubwr.html#x3.1 184. http://www.columbia.edu/kermit/ckubwr.html#x3.1.4 185. http://www.columbia.edu/kermit/ckubwr.html#x3.1.2 186. http://service.software.ibm.com/rs6000/ 187. http://www.columbia.edu/kermit/ckubwr.html#top 188. http://www.columbia.edu/kermit/ckubwr.html#contents 189. http://www.columbia.edu/kermit/ckubwr.html#x3.1 190. http://www.columbia.edu/kermit/ckubwr.html#x3.1.5 191. http://www.columbia.edu/kermit/ckubwr.html#x3.1.3 192. http://www.columbia.edu/kermit/ckubwr.html#top 193. http://www.columbia.edu/kermit/ckubwr.html#contents 194. http://www.columbia.edu/kermit/ckubwr.html#x3.1 195. http://www.columbia.edu/kermit/ckubwr.html#x3.1.4 196. http://www.columbia.edu/kermit/ckubwr.html#top 197. http://www.columbia.edu/kermit/ckubwr.html#contents 198. http://www.columbia.edu/kermit/ckubwr.html#x3 199. http://www.columbia.edu/kermit/ckubwr.html#x3.3 200. http://www.columbia.edu/kermit/ckubwr.html#x3.1 201. http://www.columbia.edu/kermit/ckubwr.html#x3.2.0 202. http://www.columbia.edu/kermit/ckubwr.html#x3.2.1 203. http://www.columbia.edu/kermit/ckubwr.html#x3.2.2 204. http://www.columbia.edu/kermit/ckubwr.html#x3.2.3 205. http://www.columbia.edu/kermit/ckubwr.html#x3.2.4 206. http://www.columbia.edu/kermit/ckubwr.html#x3.2.5 207. news:comp.sys.hp.hpux 208. http://www.columbia.edu/kermit/ckubwr.html#top 209. http://www.columbia.edu/kermit/ckubwr.html#contents 210. http://www.columbia.edu/kermit/ckubwr.html#x3.2 211. http://www.columbia.edu/kermit/ckubwr.html#x3.2.1 212. http://www.columbia.edu/kermit/ckubwr.html#top 213. http://www.columbia.edu/kermit/ckubwr.html#contents 214. http://www.columbia.edu/kermit/ckubwr.html#x3.2 215. http://www.columbia.edu/kermit/ckubwr.html#x3.2.2 216. http://www.columbia.edu/kermit/ckubwr.html#x3.2.0 217. ftp://kermit.columbia.edu/kermit/f/makefile 218. http://www.columbia.edu/kermit/ckubwr.html#top 219. http://www.columbia.edu/kermit/ckubwr.html#contents 220. http://www.columbia.edu/kermit/ckubwr.html#x3.2 221. http://www.columbia.edu/kermit/ckubwr.html#x3.2.3 222. http://www.columbia.edu/kermit/ckubwr.html#x3.2.1 223. http://www.columbia.edu/kermit/ckubwr.html#top 224. http://www.columbia.edu/kermit/ckubwr.html#contents 225. http://www.columbia.edu/kermit/ckubwr.html#x3.2 226. http://www.columbia.edu/kermit/ckubwr.html#x3.2.4 227. http://www.columbia.edu/kermit/ckubwr.html#x3.2.2 228. http://www.columbia.edu/kermit/ckubwr.html#x3.2.4.1 229. http://www.columbia.edu/kermit/ckubwr.html#x3.2.4.2 230. http://www.columbia.edu/kermit/ckubwr.html#x3.2.4.3 231. http://www.columbia.edu/kermit/ckubwr.html#x3.2.4.4 232. http://www.columbia.edu/kermit/ckubwr.html#x3.2.4.5 233. http://www.columbia.edu/kermit/ckubwr.html#top 234. http://www.columbia.edu/kermit/ckubwr.html#contents 235. http://www.columbia.edu/kermit/ckubwr.html#x3.2 236. http://www.columbia.edu/kermit/ckubwr.html#x3.2.4.2 237. http://www.columbia.edu/kermit/ckubwr.html#x3.2.2 238. http://www.columbia.edu/kermit/ckubwr.html#top 239. http://www.columbia.edu/kermit/ckubwr.html#contents 240. http://www.columbia.edu/kermit/ckubwr.html#x3.2.4 241. http://www.columbia.edu/kermit/ckubwr.html#x3.2.4.3 242. http://www.columbia.edu/kermit/ckubwr.html#x3.2.4.1 243. http://www.columbia.edu/kermit/ckubwr.html#top 244. http://www.columbia.edu/kermit/ckubwr.html#contents 245. http://www.columbia.edu/kermit/ckubwr.html#x3.2.4 246. http://www.columbia.edu/kermit/ckubwr.html#x3.2.4.4 247. http://www.columbia.edu/kermit/ckubwr.html#x3.2.4.2 248. http://www.columbia.edu/kermit/ckubwr.html#top 249. http://www.columbia.edu/kermit/ckubwr.html#contents 250. http://www.columbia.edu/kermit/ckubwr.html#x3.2.4 251. http://www.columbia.edu/kermit/ckubwr.html#x3.2.4.5 252. http://www.columbia.edu/kermit/ckubwr.html#x3.2.4.3 253. http://www.columbia.edu/kermit/ckubwr.html#top 254. http://www.columbia.edu/kermit/ckubwr.html#contents 255. http://www.columbia.edu/kermit/ckubwr.html#x3.2.4 256. http://www.columbia.edu/kermit/ckubwr.html#x3.2.4.4 257. http://www.columbia.edu/kermit/ckubwr.html#top 258. http://www.columbia.edu/kermit/ckubwr.html#contents 259. http://www.columbia.edu/kermit/ckubwr.html#x3.2 260. http://www.columbia.edu/kermit/ckubwr.html#x3.2.4 261. http://www.columbia.edu/kermit/ckubwr.html#top 262. http://www.columbia.edu/kermit/ckubwr.html#contents 263. http://www.columbia.edu/kermit/ckubwr.html#x3 264. http://www.columbia.edu/kermit/ckubwr.html#x3.4 265. http://www.columbia.edu/kermit/ckubwr.html#x3.2 266. http://www.columbia.edu/kermit/ckubwr.html#x3.3.1 267. http://www.columbia.edu/kermit/ckubwr.html#x3.3.2 268. http://www.columbia.edu/kermit/ckubwr.html#x3.3.3 269. http://www.columbia.edu/kermit/ckubwr.html#x3.3.4 270. http://www.columbia.edu/kermit/ckubwr.html#x3.3.5 271. http://www.columbia.edu/kermit/ckubwr.html#x3.3.6 272. http://en.wikipedia.org/wiki/Avahi_(software) 273. news:comp.os.linux.misc 274. news:comp.os.linux.answers 275. http://www.tldp.org/ 276. http://www.tldp.org/FAQ/Linux-FAQ.html 277. http://www.tldp.org/HOWTO/Serial-HOWTO.html 278. http://tldp.org/HOWTO/Modem-HOWTO.html 279. ftp://sunsite.unc.edu/pub/Linux/docs/HOWTO 280. ftp://tsx-11.mit.edu/pub/linux/docs/HOWTO 281. http://www.tldp.org/HOWTO/ 282. http://www.tldp.org/hmirrors.html 283. http://www.redhat.com/apps/support/ 284. http://www.debian.org/support 285. http://www.slackware.com/support/ 286. http://www.caldera.com/support/ 287. http://www.novell.com/support/microsites/microsite.do 288. http://www.mandrake.com/support/ 289. http://www.turbolinux.com/support/ 290. http://www.linmodems.org/ 291. http://www.columbia.edu/kermit/ckubwr.html#x3.0 292. http://linux.dreamtime.org/decnet/ 293. mailto:kermit-support@columbia.edu 294. http://www.linmodems.org/ 295. http://www.columbia.edu/kermit/ckubwr.html#x3.0.2 296. http://www.columbia.edu/kermit/security.html#servers 297. http://www.columbia.edu/kermit/sshclient.html 298. http://www.columbia.edu/kermit/ckubwr.html#top 299. http://www.columbia.edu/kermit/ckubwr.html#contents 300. http://www.columbia.edu/kermit/ckubwr.html#x3 301. http://www.columbia.edu/kermit/ckubwr.html#x3.3.2 302. http://www.columbia.edu/kermit/ckubwr.html#top 303. http://www.columbia.edu/kermit/ckubwr.html#contents 304. http://www.columbia.edu/kermit/ckubwr.html#x3.3 305. http://www.columbia.edu/kermit/ckubwr.html#x3.3.3 306. http://www.columbia.edu/kermit/ckubwr.html#x3.3.1 307. http://www.columbia.edu/kermit/ckubwr.html#x3.0 308. http://www.columbia.edu/kermit/ckubwr.html#x6 309. http://www.columbia.edu/kermit/ckubwr.html#x7 310. http://www.columbia.edu/kermit/ckubwr.html#x8 311. http://www.columbia.edu/kermit/ckuins.html#x10 312. http://www.columbia.edu/kermit/ckuins.html#x11 313. http://www.columbia.edu/kermit/ckuins.html 314. http://www.columbia.edu/kermit/ckubwr.html#x3.0 315. http://linuxwww.db.erau.edu/mail_archives/linux-kernel/Mar_98/1441.html 316. http://www.columbia.edu/kermit/ckubwr.html#top 317. http://www.columbia.edu/kermit/ckubwr.html#contents 318. http://www.columbia.edu/kermit/ckubwr.html#x3.3 319. http://www.columbia.edu/kermit/ckubwr.html#x3.3.4 320. http://www.columbia.edu/kermit/ckubwr.html#x3.3.2 321. http://www.columbia.edu/kermit/ckubwr.html#x3.0.5 322. http://www.columbia.edu/kermit/ckfaq.html#term 323. http://dickey.his.com/xterm/xterm.html 324. http://dickey.his.com/xterm/xterm.html 325. ftp://kermit.columbia.edu/kermit/f/xmodmap.txt 326. http://www.columbia.edu/kermit/ckubwr.html#top 327. http://www.columbia.edu/kermit/ckubwr.html#contents 328. http://www.columbia.edu/kermit/ckubwr.html#x3.3 329. http://www.columbia.edu/kermit/ckubwr.html#x3.3.5 330. http://www.columbia.edu/kermit/ckubwr.html#x3.3.3 331. http://www.columbia.edu/kermit/ckubwr.html#top 332. http://www.columbia.edu/kermit/ckubwr.html#contents 333. http://www.columbia.edu/kermit/ckubwr.html#x3.3 334. http://www.columbia.edu/kermit/ckubwr.html#x3.3.6 335. http://www.columbia.edu/kermit/ckubwr.html#x3.3.4 336. http://www.columbia.edu/kermit/ckermit.html 337. mailto:kermit-support@columbia.edu 338. http://www.redhat.com/support/errata/RHBA-2001-153.html 339. news:comp.protocols.kermit.misc 340. http://www.columbia.edu/kermit/ckubwr.html#top 341. http://www.columbia.edu/kermit/ckubwr.html#contents 342. http://www.columbia.edu/kermit/ckubwr.html#x3.3 343. http://www.columbia.edu/kermit/ckubwr.html#x3.3.5 344. http://www.columbia.edu/kermit/ckubwr.html#top 345. http://www.columbia.edu/kermit/ckubwr.html#contents 346. http://www.columbia.edu/kermit/ckubwr.html#x3 347. http://www.columbia.edu/kermit/ckubwr.html#x3.5 348. http://www.columbia.edu/kermit/ckubwr.html#x3.3 349. http://www.columbia.edu/kermit/ckubwr.html#top 350. http://www.columbia.edu/kermit/ckubwr.html#contents 351. http://www.columbia.edu/kermit/ckubwr.html#x3 352. http://www.columbia.edu/kermit/ckubwr.html#x3.6 353. http://www.columbia.edu/kermit/ckubwr.html#x3.4 354. news:comp.os.qnx 355. http://www.columbia.edu/kermit/gkermit.html 356. http://www.columbia.edu/kermit/ckuins.html#x10 357. http://www.columbia.edu/kermit/ckuins.html 358. http://www.columbia.edu/kermit/ckubwr.html#top 359. http://www.columbia.edu/kermit/ckubwr.html#contents 360. http://www.columbia.edu/kermit/ckubwr.html#x3 361. http://www.columbia.edu/kermit/ckubwr.html#x3.7 362. http://www.columbia.edu/kermit/ckubwr.html#x3.5 363. http://www.columbia.edu/kermit/ckubwr.html#x3.6.1 364. http://www.columbia.edu/kermit/ckubwr.html#x3.6.2 365. http://www.columbia.edu/kermit/ckubwr.html#x3.6.3 366. http://www.columbia.edu/kermit/ckubwr.html#x3.6.4 367. http://www.columbia.edu/kermit/ckubwr.html#x3.10 368. http://aplawrence.com/SCOFAQ/ 369. http://www.zenez.com/cgi-bin/scoprogfaq/faq.pl 370. http://www.zenez.com/cgi-bin/scouw7faq/faq.pl 371. http://zenez.pcunix.com/cgi-bin/scouw7faq/faq.pl 372. http://pcunix.com/Unixart/modems.html 373. http://www.freebird.org/faq/ 374. http://www.freebird.org/faq/developer.html 375. http://support.caldera.com/caldera 376. http://stage.caldera.com/ta/ 377. http://aplawrence.com/newtosco.html 378. http://www.columbia.edu/kermit/ckubwr.html#x3.0.5 379. http://www.columbia.edu/kermit/ckfaq.html#term 380. http://www.columbia.edu/kermit/ckubwr.html#x3.0 381. http://www.columbia.edu/kermit/ckubwr.html#top 382. http://www.columbia.edu/kermit/ckubwr.html#contents 383. http://www.columbia.edu/kermit/ckubwr.html#x3.6 384. http://www.columbia.edu/kermit/ckubwr.html#x3.6.1 385. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c 386. http://www.columbia.edu/kermit/ckubwr.html#top 387. http://www.columbia.edu/kermit/ckubwr.html#contents 388. http://www.columbia.edu/kermit/ckubwr.html#x3.6 389. http://www.columbia.edu/kermit/ckubwr.html#x3.6.3 390. http://www.columbia.edu/kermit/ckubwr.html#x3.6.1 391. http://www.digi.com/ 392. ftp://ftp.fu-berlin.de/pub/unix/driver/fas 393. http://www.columbia.edu/kermit/ckubwr.html#x14 394. http://www.sco.com/ 395. ftp://ftp.sco.com/ 396. http://www.columbia.edu/kermit/ckubwr.html#top 397. http://www.columbia.edu/kermit/ckubwr.html#contents 398. http://www.columbia.edu/kermit/ckubwr.html#x3.6 399. http://www.columbia.edu/kermit/ckubwr.html#x3.6.4 400. http://www.columbia.edu/kermit/ckubwr.html#x3.6.2 401. http://www.columbia.edu/kermit/ckubwr.html#x3.10 402. http://www.columbia.edu/kermit/ckubwr.html#top 403. http://www.columbia.edu/kermit/ckubwr.html#contents 404. http://www.columbia.edu/kermit/ckubwr.html#x3.6 405. http://www.columbia.edu/kermit/ckubwr.html#x3.6.3 406. http://www.columbia.edu/kermit/ckubwr.html#top 407. http://www.columbia.edu/kermit/ckubwr.html#contents 408. http://www.columbia.edu/kermit/ckubwr.html#x3 409. http://www.columbia.edu/kermit/ckubwr.html#x3.8 410. http://www.columbia.edu/kermit/ckubwr.html#x3.6 411. http://www.columbia.edu/kermit/ckubwr.html#x3.7.1 412. http://www.columbia.edu/kermit/ckubwr.html#x3.7.2 413. http://www.columbia.edu/kermit/ckubwr.html#x3.7.3 414. http://www.columbia.edu/kermit/ckubwr.html#x3.7.4 415. http://www.columbia.edu/kermit/ckubwr.html#x3.7.5 416. news:comp.unix.solaris 417. http://access1.sun.com/ 418. http://docs.sun.com/ 419. http://www.sunhelp.com/ 420. http://www.wins.uva.nl/pub/solaris/solaris2/ 421. http://www.wins.uva.nl/cgi-bin/sfaq.cgi 422. ftp://ftp.wins.uva.nl/pub/solaris 423. http://www.science.uva.nl/pub/solaris/solaris2.html 424. http://www.stokely.com/ 425. http://www.stokely.com/unix.sysadm.resources/faqs.sun.html 426. http://www.columbia.edu/kermit/ckubwr.html#x3.0 427. http://www.columbia.edu/kermit/ckubwr.html#top 428. http://www.columbia.edu/kermit/ckubwr.html#contents 429. http://www.columbia.edu/kermit/ckubwr.html#x3 430. http://www.columbia.edu/kermit/ckubwr.html#x3.7 431. http://www.columbia.edu/kermit/ckubwr.html#x3.7.2 432. http://www.columbia.edu/kermit/ckubwr.html#top 433. http://www.columbia.edu/kermit/ckubwr.html#contents 434. http://www.columbia.edu/kermit/ckubwr.html#x3.7 435. http://www.columbia.edu/kermit/ckubwr.html#x3.7.3 436. http://www.columbia.edu/kermit/ckubwr.html#x3.7.1 437. http://www.columbia.edu/kermit/ckubwr.html#top 438. http://www.columbia.edu/kermit/ckubwr.html#contents 439. http://www.columbia.edu/kermit/ckubwr.html#x3.7 440. http://www.columbia.edu/kermit/ckubwr.html#x3.7.4 441. http://www.columbia.edu/kermit/ckubwr.html#x3.7.2 442. http://www.columbia.edu/kermit/ckubwr.html#top 443. http://www.columbia.edu/kermit/ckubwr.html#contents 444. http://www.columbia.edu/kermit/ckubwr.html#x3.7 445. http://www.columbia.edu/kermit/ckubwr.html#x3.7.5 446. http://www.columbia.edu/kermit/ckubwr.html#x3.7.3 447. news:comp.os.vms 448. http://www.columbia.edu/kermit/ckubwr.html#top 449. http://www.columbia.edu/kermit/ckubwr.html#contents 450. http://www.columbia.edu/kermit/ckubwr.html#x3.7 451. http://www.columbia.edu/kermit/ckubwr.html#x3.7.6 452. http://www.columbia.edu/kermit/ckubwr.html#x3.7.4 453. http://www.columbia.edu/kermit/ckubwr.html#top 454. http://www.columbia.edu/kermit/ckubwr.html#contents 455. http://www.columbia.edu/kermit/ckubwr.html#x3.7 456. http://www.columbia.edu/kermit/ckubwr.html#x3.7.5 457. http://www.columbia.edu/kermit/ckubwr.html#top 458. http://www.columbia.edu/kermit/ckubwr.html#contents 459. http://www.columbia.edu/kermit/ckubwr.html#x3 460. http://www.columbia.edu/kermit/ckubwr.html#x3.9 461. http://www.columbia.edu/kermit/ckubwr.html#x3.7 462. http://www.stokely.com/ 463. http://access1.sun.com/ 464. http://www.ludd.luth.se/~bear/project/sun/sun.hardware.txt 465. ftp://ftp.netcom.com/pub/ru/rubicon/sun.hdwr.ref 466. ftp://ftp.intnet.net/pub/SUN/Sun-Hardware-Ref 467. http://www.columbia.edu/kermit/ckubwr.html#top 468. http://www.columbia.edu/kermit/ckubwr.html#contents 469. http://www.columbia.edu/kermit/ckubwr.html#x3 470. http://www.columbia.edu/kermit/ckubwr.html#x3.10 471. http://www.columbia.edu/kermit/ckubwr.html#x3.8 472. news:comp.unix.ultrix 473. news:comp.sys.dec 474. http://www.columbia.edu/kermit/ckubwr.html#top 475. http://www.columbia.edu/kermit/ckubwr.html#contents 476. http://www.columbia.edu/kermit/ckubwr.html#x3 477. http://www.columbia.edu/kermit/ckubwr.html#x3.11 478. http://www.columbia.edu/kermit/ckubwr.html#x3.9 479. http://www.freebird.org/ 480. http://www.freebird.org/faq/ 481. news:comp.unix.unixware.misc 482. news:comp.unix.sco.misc 483. http://www.columbia.edu/kermit/ckubwr.html#x3.0 484. ftp://kermit.columbia.edu/kermit/f/ckutio.c 485. http://www.columbia.edu/kermit/ckubwr.html#top 486. http://www.columbia.edu/kermit/ckubwr.html#contents 487. http://www.columbia.edu/kermit/ckubwr.html#x3 488. http://www.columbia.edu/kermit/ckubwr.html#x3.12 489. http://www.columbia.edu/kermit/ckubwr.html#x3.10 490. http://www.columbia.edu/kermit/ckubwr.html#top 491. http://www.columbia.edu/kermit/ckubwr.html#contents 492. http://www.columbia.edu/kermit/ckubwr.html#x3 493. http://www.columbia.edu/kermit/ckubwr.html#x3.13 494. http://www.columbia.edu/kermit/ckubwr.html#x3.11 495. http://www.columbia.edu/kermit/ckubwr.html#top 496. http://www.columbia.edu/kermit/ckubwr.html#contents 497. http://www.columbia.edu/kermit/ckubwr.html#x3 498. http://www.columbia.edu/kermit/ckubwr.html#x3.14 499. http://www.columbia.edu/kermit/ckubwr.html#x3.12 500. http://www.columbia.edu/kermit/ckubwr.html#top 501. http://www.columbia.edu/kermit/ckubwr.html#contents 502. http://www.columbia.edu/kermit/ckubwr.html#x3 503. http://www.columbia.edu/kermit/ckubwr.html#x3.15 504. http://www.columbia.edu/kermit/ckubwr.html#x3.13 505. news:comp.sys.sgi.misc 506. news:comp.sys.sgi.admin 507. http://www.sgi.com/ 508. http://www-viz.tamu.edu/~sgi-faq/ 509. ftp://viz.tamu.edu/pub/sgi/faq/ 510. http://www.columbia.edu/kermit/ckuins.html 511. http://freeware.sgi.com/Installable/gcc-2.95.2.html 512. http://freeware.sgi.com/Installable/gcc-2.95.2.html 513. http://www.columbia.edu/kermit/ckubwr.html#top 514. http://www.columbia.edu/kermit/ckubwr.html#contents 515. http://www.columbia.edu/kermit/ckubwr.html#x3 516. http://www.columbia.edu/kermit/ckubwr.html#x3.16 517. http://www.columbia.edu/kermit/ckubwr.html#x3.14 518. news:comp.sys.be 519. http://www.columbia.edu/kermit/ckubwr.html#top 520. http://www.columbia.edu/kermit/ckubwr.html#contents 521. http://www.columbia.edu/kermit/ckubwr.html#x3 522. http://www.columbia.edu/kermit/ckubwr.html#x3.17 523. http://www.columbia.edu/kermit/ckubwr.html#x3.15 524. http://www.columbia.edu/kermit/ckubwr.html#top 525. http://www.columbia.edu/kermit/ckubwr.html#contents 526. http://www.columbia.edu/kermit/ckubwr.html#x3 527. http://www.columbia.edu/kermit/ckubwr.html#x3.18 528. http://www.columbia.edu/kermit/ckubwr.html#x3.16 529. http://www.columbia.edu/kermit/ckubwr.html#top 530. http://www.columbia.edu/kermit/ckubwr.html#contents 531. http://www.columbia.edu/kermit/ckubwr.html#x3 532. http://www.columbia.edu/kermit/ckubwr.html#x3.19 533. http://www.columbia.edu/kermit/ckubwr.html#x3.17 534. http://www.columbia.edu/kermit/ckubwr.html#top 535. http://www.columbia.edu/kermit/ckubwr.html#contents 536. http://www.columbia.edu/kermit/ckubwr.html#x3 537. http://www.columbia.edu/kermit/ckubwr.html#x3.20 538. http://www.columbia.edu/kermit/ckubwr.html#x3.18 539. http://www.columbia.edu/kermit/mac.html 540. http://www.amazon.com/gp/product/B0000VYJRY?ie=UTF8&tag=aleidmoreldom-20&linkCode=as2&camp=1789&creative=390957&creativeASIN=B0000VYJRY 541. http://www.columbia.edu/kermit/ckuins.html#x10 542. http://www.columbia.edu/kermit/ckuins.html 543. http://www.amazon.com/gp/product/B000FX61MS?ie=UTF8&tag=aleidmoreldom-20&linkCode=as2&camp=1789&creative=390957&creativeASIN=B000FX61MS 544. mailto:kermit@columbia.edu 545. http://www.columbia.edu/kermit/ckubwr.html#top 546. http://www.columbia.edu/kermit/ckubwr.html#contents 547. http://www.columbia.edu/kermit/ckubwr.html#x3 548. http://www.columbia.edu/kermit/ckubwr.html#x3.19 549. http://www.uni-giessen.de/faq/archiv/coherent-faq.general/msg00000.html 550. http://www.columbia.edu/kermit/ckubwr.html#top 551. http://www.columbia.edu/kermit/ckubwr.html#contents 552. http://www.columbia.edu/kermit/ckubwr.html#x5 553. http://www.columbia.edu/kermit/ckubwr.html#x3 554. http://www.columbia.edu/kermit/ckccfg.html 555. http://www.columbia.edu/kermit/ckubwr.html#top 556. http://www.columbia.edu/kermit/ckubwr.html#contents 557. http://www.columbia.edu/kermit/ckubwr.html#x6 558. http://www.columbia.edu/kermit/ckubwr.html#x4 559. http://www.columbia.edu/kermit/ckuins.html 560. http://www.columbia.edu/kermit/ckubwr.html#top 561. http://www.columbia.edu/kermit/ckubwr.html#contents 562. http://www.columbia.edu/kermit/ckubwr.html#x7 563. http://www.columbia.edu/kermit/ckubwr.html#x5 564. http://www.columbia.edu/kermit/ckuins.html#9.5 565. http://www.columbia.edu/kermit/ckubwr.html#x3 566. http://www.columbia.edu/kermit/ckubwr.html#top 567. http://www.columbia.edu/kermit/ckubwr.html#contents 568. http://www.columbia.edu/kermit/ckubwr.html#x8 569. http://www.columbia.edu/kermit/ckubwr.html#x6 570. http://www.columbia.edu/kermit/ckuins.html#x8 571. http://www.columbia.edu/kermit/ckuins.html 572. http://www.columbia.edu/kermit/ck60manual.html 573. http://www.columbia.edu/kermit/cable.html 574. http://www.columbia.edu/kermit/ckubwr.html#top 575. http://www.columbia.edu/kermit/ckubwr.html#contents 576. http://www.columbia.edu/kermit/ckubwr.html#x9 577. http://www.columbia.edu/kermit/ckubwr.html#x7 578. http://www.columbia.edu/kermit/ckubwr.html#top 579. http://www.columbia.edu/kermit/ckubwr.html#contents 580. http://www.columbia.edu/kermit/ckubwr.html#x10 581. http://www.columbia.edu/kermit/ckubwr.html#x8 582. http://www.columbia.edu/kermit/ck60manual.html 583. http://dickey.his.com/xterm/xterm.html 584. http://www.columbia.edu/kermit/ckubwr.html#top 585. http://www.columbia.edu/kermit/ckubwr.html#contents 586. http://www.columbia.edu/kermit/ckubwr.html#x11 587. http://www.columbia.edu/kermit/ckubwr.html#x9 588. http://www.columbia.edu/kermit/ckubwr.html#top 589. http://www.columbia.edu/kermit/ckubwr.html#contents 590. http://www.columbia.edu/kermit/ckubwr.html#x12 591. http://www.columbia.edu/kermit/ckubwr.html#x10 592. http://www.columbia.edu/kermit/ckubwr.html#x11.1 593. http://www.columbia.edu/kermit/ckubwr.html#x11.2 594. http://www.columbia.edu/kermit/ckubwr.html#top 595. http://www.columbia.edu/kermit/ckubwr.html#contents 596. http://www.columbia.edu/kermit/ckubwr.html#x11 597. http://www.columbia.edu/kermit/ckubwr.html#x11.2 598. http://www.columbia.edu/kermit/ck60manual.html 599. http://www.columbia.edu/kermit/ckubwr.html#top 600. http://www.columbia.edu/kermit/ckubwr.html#contents 601. http://www.columbia.edu/kermit/ckubwr.html#x11 602. http://www.columbia.edu/kermit/ckubwr.html#x11.1 603. http://www.columbia.edu/kermit/ck60manual.html 604. http://www.columbia.edu/kermit/ckubwr.html#top 605. http://www.columbia.edu/kermit/ckubwr.html#contents 606. http://www.columbia.edu/kermit/ckubwr.html#x13 607. http://www.columbia.edu/kermit/ckubwr.html#x11 608. http://www.columbia.edu/kermit/security.html 609. http://www.columbia.edu/kermit/ckubwr.html#top 610. http://www.columbia.edu/kermit/ckubwr.html#contents 611. http://www.columbia.edu/kermit/ckubwr.html#x14 612. http://www.columbia.edu/kermit/ckubwr.html#x12 613. http://www.columbia.edu/kermit/ckubwr.html#top 614. http://www.columbia.edu/kermit/ckubwr.html#contents 615. http://www.columbia.edu/kermit/ckubwr.html#x15 616. http://www.columbia.edu/kermit/ckubwr.html#x14 617. mailto:support@stallion.oz.au 618. http://www.stallion.com/ 619. http://www.columbia.edu/kermit/ckubwr.html#top 620. http://www.columbia.edu/kermit/ckubwr.html#contents 621. http://www.columbia.edu/kermit/ckermit.html 622. http://www.columbia.edu/kermit/ck80.html 623. http://www.columbia.edu/kermit/index.html 624. http://www.columbia.edu/kermit/index.html 625. http://www.columbia.edu/ 626. mailto:kermit@columbia.edu ckcplm.txt0000664000015300001460000042166511624230415011711 0ustar fdckermit [1]The Columbia Crown The Kermit Project | Columbia University 612 West 115th Street, New York NY 10025 USA o [2]kermit@columbia.edu ...since 1981 [3]Home [4]Kermit 95 [5]C-Kermit [6]Scripts [7]Current [8]New [9]FAQ [10]Support C-Kermit Program Logic Manual Frank da Cruz [11]The Kermit Project As of: C-Kermit 9.0.300, 30 June 2011 Last update: Fri Jul 1 15:47:34 2011 IF YOU ARE READING A PLAIN-TEXT version of this document, note that this file is a plain-text dump of a Web page. You can visit the original (and possibly more up-to-date) Web page here: [12]http://www.columbia.edu/kermit/ckcplm.html CONTENTS 1. [13]INTRODUCTION 2. [14]FILES 3. [15]SOURCE CODE PORTABILITY AND STYLE 4. [16]MODULES 4.A. [17]Group A: Library Routines 4.B. [18]Group B: Kermit File Transfer 4.C. [19]Group C: Character-Set Conversion 4.D. [20]Group D: User Interface 4.E. [21]Group E: Platform-Dependent I/O 4.F. [22]Group F: Network Support 4.G. [23]Group G: Formatted Screen Support 4.H. [24]Group H: Pseudoterminal Support 4.I. [25]Group I: Security I. [26]APPENDIX I: FILE PERMISSIONS 1. INTRODUCTION The Kermit Protocol is specified in the book [27]Kermit, A File Transfer Protocol by Frank da Cruz, Digital Press / Butterworth Heinemann, Newton, MA, USA (1987), 379 pages, ISBN 0-932376-88-6. It is assumed the reader is familiar with the Kermit protocol specification. This file describes the relationship among the modules and functions of C-Kermit 5A and later, and other programming considerations. C-Kermit is designed to be portable to any kind of computer that has a C compiler. The source code is broken into many files that are grouped according to their function, as shown in the [28]Contents. C-Kermit has seen constant development since 1985. Throughout its history, there has been a neverending tug-of-war among: a. Functionality: adding new features, fixing bugs, improving performance. b. Adding support for new platforms or communication methods. c. "Buzzword 1.0 compliance". The latter category is the most frustrating, since it generally involves massive changes just to keep the software doing what it did before in some new setting: e.g. the K&R-to-ANSIC conversion (which had to be done, of course, without breaking K&R); Y2K (not a big deal in our case); the many and varied UNIX and other API "standards" with which to "comply". Upon first glance at the source code, you will probably be appalled. Many will be tempted to clean it up and modernize it. But as soon as you do, you are sure to break something. Remember that above all else, the C-Kermit code is portable to every Unix platform that ever existed, going back Unix V7 (1979)*, and to several other completely different and unrelated operating-system families such as DEC/HP VMS, DG AOS/VS, and Stratus VOS, as well as to some Unix offshoots like OS-9 and Plan 9 (from Outer Space). Every release of Kermit has been checked on every platform available -- the older the better! -- to make sure it still builds and runs. Even today (2011), there are modern Unix systems that have non-ANSI C compilers, foremost among them HP-UX (where an ANSI optimizing C compiler is available, but only as an expensive add-on). In a way, portability is the most important feature of C-Kermit and every effort should be made to preserve it through future releases. Voluminous edit histories are available going back to May 1985. The first versions of C-Kermit were done on our [29]DEC VAX-11/750 with Ultrix 1.0 and 2.0 (as well as departmental 750s with 4.2BSD**), DEC Pro-380 workstations (desktop PDP-11s) running 2.9BSD, which was [30]ported to the 380 by us. Later (1988 or so) on a big VAX 8650 with Ultrix, which became an 8700 (these no doubt weighed several tons), and finally a succession of non-DEC equipment: an Encore Multimax, 25 years worth of Suns, and now Linux on [31]HP Blades. We also had our own VMS development systems for some years. All this plus a generous assortment of departmental and offsite guest accounts on a multitude of platforms. Anyway, the edit histories: [32]ckc04e.txt C-Kermit 4.2(030) May 1985 to 4E(072) Jan 1989. [33]ckc04f.txt C-Kermit 4F(077) Arp 1989 to 4F(095) Aug 1989. [34]ckc168.txt Updates to C-Kermit 5A(168) for VMS Nov 1991 [35]ckc178.txt C-Kermit 5A(100) Jul 1989 to 5A(178) Jan 1992 [36]ckc188.txt C-Kermit 5A(188) development, 1992 [37]ckc189.txt C-Kermit 5A(189) development, 1993 [38]ckc192.txt C-Kermit 6.0(192) development, 1998 [39]ckc197.txt C-Kermit 7.0(197) development, 2000 [40]ckc200.txt C-Kermit 8.0.200 development, 2001 [41]ckc211.txt C-Kermit 8.0.201 through 8.0.209 2001-2004 [42]ckc300.txt C-Kermit 9.0.300 June 2011 _________________________________ * C-Kermit 6.0 was the last one to be built on V7, as I recall. The code should still be good for V7 but it probably has outgrown the 16-bit address space. In any case there is still a V7 makefile target and a V7 path through the forest of #ifdefs in the code if anybody is running V7 on an emulator and would like to try building C-Kermit. There is no support for V6 but that is only because no V6 system was ever found for development. Notice that some other 16-bit Unixes are supported in the code, including 2.9BSD and Tandy Xenix 3.0, but have not been tried since C-Kermit 6.0 ** C-Kermit 9.0.300 was built successfully on 4.2BSD about 25 years later, in June 2011. [ [43]Contents ] [ [44]C-Kermit ] [ [45]Kermit Home ] 2. FILES C-Kermit source files begin with the two letters "ck", for example ckutio.c. Filenames are kept short (6.3) for maximum portability and (obviously I hope) do not contain spaces or more than one period. The third character in the name denotes something about the function group and the expected level of portability: a General descriptive material and documentation (text) b BOO file encoders and decoders (obsolete) c All platforms with C compilers (*) d Data General AOS/VS e Reserved for "ckermit" files, like ckermit.ini, ckermit2.txt f (reserved) g (reserved) h (reserved) i Commodore Amiga (Intuition) j (unused) k (unused) l Stratus VOS m Macintosh with Mac OS 1-9 n (unused) o OS/2 and Microsoft Windows 9x/ME/NT/2000/XP/Vista/etc p Plan 9 from Bell Labs q (reserved) r DEC PDP-11 with RSTS/E (never used, open for reassignment) s Atari ST GEMDOS (last supported in version 5A(189)) t DEC PDP-11 with RT-11 (never used, open for reassignment) u Unix-based operating systems (*) v VMS and OpenVMS w Wart (Lex-like preprocessor, platform independent) x (reserved) y (reserved) z (reserved) 0-3 (reserved) 4 IBM AS/400 5-8 (reserved) 9 Microware OS-9 _ (underscore) Encryption modules (*) In fact there is little distinction between the ckc*.* and cku*.* categories. It would make more sense for all cku*.* modules to be ckc*.* ones, except ckufio.c, ckutio.c, ckucon.c, ckucns.c, and ckupty.c, which truly are specific to Unix. The rest (ckuus*.c, ckucmd.c, etc) are quite portable. One hint before proceeding: functions are scattered all over the ckc*.c and cku*.c modules, where function size has begun to take precedence over the desirability of grouping related functions together, the aim being to keep any particular module from growing disproportionately large. The easiest way (in UNIX) to find out in what source file a given function is defined is like this (where the desired function is foo()...): grep ^foo\( ck*.c This works because the coding convention has been to make function names always start on the left margin with their contents indented, for example: static char * foo(x,y) int x, y; { ... } Also note the style for bracket placement. This allows bracket-matching text editors (such as EMACS) to help you make sure you know which opening bracket a closing bracket matches, particularly when the opening bracket is above the visible screen, and it also makes it easy to find the end of a function (search for '}' on the left margin). Of course EMACS tags work nicely with this format too: $ cd kermit-source-directory $ etags ck[cu]*.c $ emacs Esc-X Visit-Tags-Table (but remember that the source file for ckcpro.c is [46]ckcpro.w!) Also: * Tabs should be set every 8 spaces, as on a VT100. * All lines must no more than 79 characters wide after tab expansion. * Note the distinction between physical tabs (ASCII 9) and the indentation conventions, which are: 4 for block contents, 2 for most other stuff (obviously this is not a portability issue, just style). [ [47]Contents ] [ [48]C-Kermit ] [ [49]Kermit Home ] 3. SOURCE CODE PORTABILITY AND STYLE C-Kermit was designed in 1985 as a platform-independent replacement for the earlier Unix Kermit. C-Kermit's design was expected to promote portability, and judging from the number of platforms to which it has been adapted since then, the model is effective, if not ideal (obviously if we had it all to do over, we'd change a few things). To answer the oft-repeated question: "Why are there so many #ifdefs?", it's because: * Many of them are related to feature selection and program size, and so need to be there anyway. * Those that treat compiler, library, platform, header-file, and similar differences have built up over time as hundreds of people all over the world adapted C-Kermit to their particular environments and sent back their changes. There might be more politically-correct ways to achieve portability, but this one is natural and proven. The basic idea is to introduce changes that can be selected by defining a symbol, which, if not defined, leaves the program exactly as it was before the changes. * Although it might be possible to "clean up" the "#ifdef mess", nobody has access to all the hundreds of platforms served by the #ifdefs to check the results. And to answer the second-most-oft-repeated question: "Why don't you just use GNU autoconfig / automake / autowhatever instead of hard-coding all those #ifdefs?" Answers: * The GNU tools are not available on all the platforms where C-Kermit must be built and I wouldn't necessarily trust them if they were. * Each platform is a moving target, so the tools themselves would need to updated before Kermit could be updated. * It would only add another layer of complexity to an already complex process. * Conversion at this point would not be practical unless there was a way to test the results on all the hundreds of platforms where C-Kermit is supposed to build. When writing code for the system-independent C-Kermit modules, please stick to the following coding conventions to ensure portability to the widest possible variety of C preprocessors, compilers, and linkers, as well as certain network and/or email transports. The same holds true for many of the "system dependent" modules too; particularly the Unix ones, since they must be buildable by a wide variety of compilers and linkers, new and old. This list does not purport to be comprehensive, and although some items on it might seem far-fetched, they would not be listed unless I had encountered them somewhere, some time. I wish I had kept better records so I could cite specific platforms and compilers. * Try to keep variable and function names unique within 6 characters, especially if they are used across modules, since 6 is the maximum for some old linkers (actually, this goes back to TOPS-10 and -20 and other old DEC OS's where C-Kermit never ran anyway; a more realistic maximum is probably somewhere between 8 and 16). We know for certain that VAX C has a 31-character max because it complains -- others might not complain, but just silently truncate, thus folding two or more routines/variables into one. * Keep preprocessor symbols unique within 8 characters; that's the max for some preprocessors (sorry, I can't give a specific example, but in 1988 or thereabouts, I had to change character-set symbols like TC_LATIN1 and TC_LATIN2 to TC_1LATIN and TC_2LATIN because the digits were being truncated and ignored on a platform where I actually had to build C-Kermit 5A; unfortunately I didn't note which platform -- maybe some early Ultrix version?) * Don't create preprocessor symbols, or variable or function names, that start with underscore (_). These are usually reserved for internal use by the compiler and header files. * Don't put #include directives inside functions or { blocks }. * Don't use the #if or #elif preprocessor constructions, only use #ifdef, #ifndef, #define, #undef, and #endif. * Put tokens after #endif in comment brackets, e.g. #endif /* FOO */. * Don't indent preprocessor statements - # must always be first char on line. * Don't put whitespace after # in preprocessor statements. * Don't use #pragma, even within #ifdefs -- it makes some preprocessors give up. * Same goes for #module, #if, etc - #ifdefs do NOT protect them. * Don't use logical operators in preprocessor constructions. * Avoid #ifdefs inside argument list to function calls (I can't remember why this one is here, but probably needn't be; we do this all the time). * Always cast strlen() in expressions to int: if ((int)strlen(foo) < x)... * Avoid typedefs; they might be portable but they are very confusing and there's no way to test for their presence or absence at compile time. Use preprocessor symbols instead if possible; at least you can test their definitions. * Unsigned long is not portable; use a preprocessor symbol (Kermit uses ULONG for this). * Long long is not portable. If you really need it, be creative. * Similarly 1234LL is not portable, nor almost any other constant modifier other than L. * Unsigned char is not portable, use CHAR (a preprocessor symbol defined in the Kermit header files) and always take precautions against character signage (more about this [50]below). * Don't use initializers with automatic arrays or structs: it's not portable. * Don't use big automatic arrays or structs in functions that might be called recursively; some platforms have fixed-size stacks (e.g. Windows 9x: 256K) and recursive functions crash with stack overflow. Even when there is not a compiler limitation, this causes memory to be consumed without bound, and can end up filling swap space. * Don't assume that struct assignment performs a copy, or that it even exists. * Don't use sizeof to get the size of an array; someone might come along later and and change it from static to malloc'd. Always use a symbol to refer to the array's size. * Don't put prototypes for static functions into header files that are used by modules that don't contain that function; the link step can fail with unresolved references (e.g. on AOS/VS). * Avoid the construction *++p (the order of evaluation varies; it shouldn't but at least one compiler had a bug that made me include this item). * Don't use triple assignments, like a = b = c = 0; (or quadruple, etc). Some compilers generate bad code for these, or crash, etc (some version of DEC C as I recall). * Some compilers don't allow structure members to have the same names as other identifiers. Try to give structure members unique names. * Don't assume anything about order of evaluation in boolean expressions, or that they will stop early if a required condition is not true, e.g.: if (i > 0 && p[i-1] == blah) can still dump core if i == 0 (hopefully this is not true of any modern compiler, but I would not have said this if it did not actually happen somewhere). * Don't have a switch() statement with no cases (e.g. because of #ifdefs); this is a fatal error in some compilers. * Don't put lots of code in a switch case; move it out to a separate function; some compilers run out of memory when presented with a huge switch() statement -- it's not the number of cases that matters; it's the overall amount of code. * Some compilers might also limit the number of switch() cases, e.g. to 254. * Don't put anything between "switch() {" and "case:" -- switch blocks are not like other blocks. * Don't jump into or out of switches. * Don't make character-string constants longer than about 250 bytes. Longer strings should be broken up into arrays of strings. * Don't write into character-string constants (obviously). Even when you know you are not writing past the end; the compiler or linker might have put them into read-only and/or shared memory, and/or coalesced multiple equal constants so if you change one you change them all. * Don't depend on '\r' being carriage return. * Don't depend on '\n' being linefeed or for that matter any SINGLE character. * Don't depend on '\r' and '\n' being different (e.g. as separate switch() cases). * In other words, don't use \n or \r to stand for specific characters; use \012 and \015 instead. * Don't code for "buzzword 1.0 compliance", unless "buzzword" is K&R and "1.0" is the first edition. * Don't use or depend on anything_t (size_t, pid_t, etc), except time_t, without #ifdef protection (time_t is the only one I've found that is accepted everywhere). This is a tough one because the same function might require (say) a size_t arg on one platform, whereas size_t is unheard of on another; or worse, it might require a totally different data type, like int or long or some other typedef'd thing. It has often proved necessary to define a symbol to stand for the type of a particular argument to a particular library or system function to get around this problem. * Don't use or depend on internationalization ("i18n") features, wchar_t, locales, etc, in portable code; they are not portable. Anyway, locales are not the right model for Kermit's multi-character-set support. Kermit does all character-set conversion itself and does not use any external libraries or functions. * In particular, don't use any library functions that deal with wide characters or Unicode in any form. These are not only nonportable, but a constantly shifting target (e.g. the ones in glibc). * Don't make any assumption about signal handler type. It can be void, int, long, or anything else. Always declare signal handlers as SIGTYP (see definition in ckcdeb.h and augment it if necessary) and always use SIGRETURN at exit points from signal handlers. * Signals should always be re-armed to be used again (this barely scratches the surface -- the differences between BSD/V7 and System V and POSIX signal handling are numerous, and some platforms do not even support signals, alarms, or longjmps correctly or at all -- avoid all of this if you can). * On the other hand, don't assume that signals are disarmed after being raised. In some platforms you have to re-arm them, in others they stay armed. * Don't call malloc() and friends from a signal handler; don't do anything but setting integer global variables in a signal handler. * malloc() does not initialize allocated memory -- it never said it did. Don't expect it to be all 0's. * Did You Know: malloc() can succeed and the program can still dump core later when it attempts to use the malloc'd memory? (This happens when allocation is deferred until use and swap space is full.) * memset(), memmove(), and memcpy() are not portable, don't use them without protecting them in ifdefs (we have USE_MEMCPY for this). bzero()/bcopy() too, except we're guaranteed to have bzero()/bcopy() when using the sockets library (not really). See examples in the source. * Don't assume that strncpy() stops on the first null byte -- most versions always copy the number of bytes given in arg 3, padding out with 0's and overwriting whatever was there before. Use C-Kermit ckstrncpy() if you want predictable non-padding behavior, guaranteed NUL-termination, and a useful return code. * DID YOU KNOW.. that some versions of inet_blah() routines return IP addresses in network byte order, while others return them local machine byte order? So passing them to ntohs() or whatever is not always the right thing to do. * Don't use ANSI-format function declarations without #ifdef CK_ANSIC, and always provide an #else for the non-ANSI case. * Use the Kermit _PROTOTYP() macro for declaring function prototypes; it works in both the ANSI and non-ANSI cases. * Don't depend on any other ANSI preprocessor features like "pasting" -- they are often missing or nonoperational. * Don't assume any C++ syntax or semantics. * Don't use // as a comment introducer. C is not C++. * Don't declare a string as "char foo[]" in one module and "extern char * foo" in another, or vice-versa: this causes core dumps. * With compiler makers falling all over themselves trying to outdo each other in ANSI strictness, it has become increasingly necessary to cast EVERYTHING. Especially char vs unsigned char. We need to use unsigned chars if we want to deal with 8-bit character sets, but most character- and string-oriented APIs want (signed) char arguments, so explicit casts are necessary. It would be nice if every compiler had a -funsigned-char option (as gcc does), but they don't. * a[x], where x is an unsigned char, can produce a wild memory reference if x, when promoted to an int, becomes negative. Cast it to (unsigned), even though it ALREADY IS unsigned. * Be careful how you declare functions that have char or long arguments; for ANSI compilers you MUST use ANSI declarations to avoid promotion problems, but you can't use ANSI declarations with non-ANSI compilers. Thus declarations of such functions must be hideously entwined in #ifdefs. Example: int /* Put character in server command buffer */ #ifdef CK_ANSIC putsrv(char c) #else putsrv(c) char c; #endif /* CK_ANSIC */ /* putsrv */ { *srvptr++ = c; *srvptr = '\0'; /* Make sure buffer is null-terminated */ return(0); } * Be careful how you return characters from functions that return int values -- "getc-like functions" -- in the ANSI world. Unless you explicitly cast the return value to (unsigned), it is likely to be "promoted" to an int and have its sign extended. * At least one compiler (the one on DEC OSF/1 1.3) treats "/*" and "*/" within string constants as comment begin and end. No amount of #ifdefs will get around this one. You simply can't put these sequences in a string constant, e.g. "/usr/local/doc/*.*". * Avoid putting multiple macro references on a single line, e.g.: putchar(BS); putchar(SP); putchar(BS) This overflows the CPP output buffer of more than a few C preprocessors (this happened, for example, with SunOS 4.1 cc, which evidently has a 1K macro expansion buffer). C-Kermit needs constant adjustment to new OS and compiler releases. Every new OS release shuffles header files or their contents, or prototypes, or data types, or levels of ANSI strictness, etc. Every time you make an adjustment to remove a new compilation error, BE VERY CAREFUL to #ifdef it on a symbol unique to the new configuration so that the previous configuration (and all other configurations on all other platforms) remain as before. Assume nothing. Don't assume header files are where they are supposed to be, that they contain what you think they contain, that they define specific symbols to have certain values -- or define them at all! Don't assume system header files protect themselves against multiple inclusion. Don't assume that particular system or library calls are available, or that the arguments are what you think they are -- order, data type, passed by reference vs value, etc. Be conservative when attempting to write portable code. Avoid all advanced features. If you see something that does not make sense, don't assume it's a mistake -- it might be there for a reason, and changing it or removing is likely to cause compilation, linking, or runtime failures sometime, somewhere. Some huge percentage of the code, especially in the platform-dependent modules, is workarounds for compiler, linker, or API bugs. But finally... feel free to violate any or all of these rules in platform-specific modules for environments in which the rules are certain not to apply. For example, in VMS-specific code, it is OK to use #if, because VAX C, DEC C, and VMS GCC all support it. [ [51]Contents ] [ [52]C-Kermit ] [ [53]Kermit Home ] 3.1. Memory Leaks The C language and standard C library are notoriously inadequate and unsafe. Strings are arrays of characters, usually referenced through pointers. There is no native string datatype. Buffers are fixed size, and C provides no runtime bounds checking, thus allowing overwriting of other data or even program code. With the popularization of the Internet, the "buffer exploit" has become a preferred method for hackers to hijack privileged programs; long data strings are fed to a program in hopes that it uses unsafe C library calls such as strcpy() or sprintf() to copy strings into automatic arrays, thus overwriting the call stack, and therefore the routine's return address. When such a hole is discovered, a "string" can be constructed that contains machine code to hijack the program's privileges and penetrate the system. This problem is partially addressed by the strn...() routines, which should always be used in preference to their str...() equivalents (except when the copy operation has already been prechecked, or there is a good reason for not using them, e.g. the sometimes undesirable side effect of strncpy() zeroing the remainder of the buffer). The most gaping whole, however, is sprintf(), which performs no length checking on its destination buffer, and is not easy to replace. Although snprintf() routines are starting to appear, they are not yet widespread, and certainly not universal, nor are they especially portable, or even full-featured. For these reasons, we have started to build up our own little library of C Library replacements, ckclib.[ch]. These are safe and highly portable primitives for memory management and string manipulation, such as: ckstrncpy() Like strncpy but returns a useful value, doesn't zero buffer. ckitoa() Opposite of atoi() ckltoa() Opposite of atol() ckctoa() Returns character as string ckmakmsg() Used with ck?to?() as a safe sprintf() replacement for up to 4 items ckmakxmsg() Like ckmakmsg() but accepts up to 12 items More about library functions in [54]Section 4.A. [ [55]Contents ] [ [56]C-Kermit ] [ [57]Kermit Home ] 3.2. The "char" vs "unsigned char" Dilemma This is one of the most aggravating and vexing characteristics of the C language. By design, chars (and char *'s) are SIGNED. But in the modern era, however, we need to process characters that can have (or include) 8-bit values, as in the ISO Latin-1, IBM CP 850, or UTF-8 character sets, so this data must be treated as unsigned. But some C compilers (such as those based on the Bell UNIX V7 compiler) do not support "unsigned char" as a data type. Therefore we have the macro or typedef CHAR, which we use when we need chars to be unsigned, but which, unfortunately, resolves itself to "char" on those compilers that don't support "unsigned char". AND SO... We have to do a lot of fiddling at runtime to avoid sign extension and so forth. Some modern compilers (e.g. IBM, DEC, Microsoft) have options that say "make all chars be unsigned" (e.g. GCC "-funsigned-char") and we use them when they are available. Other compilers don't have this option, and at the same time, are becoming increasingly strict about type mismatches, and spew out torrents of warnings when we use a CHAR where a char is expected, or vice versa. We fix these one by one using casts, and the code becomes increasingly ugly. But there remains a serious problem, namely that certain library and kernel functions have arguments that are declared as signed chars (or pointers to them), whereas our character data is unsigned. Fine, we can can use casts here too -- but who knows what happens inside these routines. [ [58]Contents ] [ [59]C-Kermit ] [ [60]Kermit Home ] 4. MODULES When C-Kermit is on the far end of a connection, it is said to be in remote mode. When C-Kermit has made a connection to another computer, it is in local mode. (If C-Kermit is "in the middle" of a multihop connection, it is still in local mode.) On another axis, C-Kermit can be in any of several major states: Command State Reading and writing from the job's controlling terminal or "console". In this mode, all i/o is handled by the Group E conxxx() (console i/o) routines. Protocol State Reading and writing from the communications device. In this mode, all i/o is handled by the Group E ttxxx() (terminal i/o) routines. Terminal State Reading from the keyboard with conxxx() routines and writing to the communications device with ttxxx() routines AND vice-versa. When in local mode, the console and communications device are distinct. During file transfer, Kermit may put up a file-transfer display on the console and sample the console for interruption signals. When in remote mode, the console and communications device are the same, and therefore there can be no file-transfer display on the console or interruptions from it (except for "in-band" interruptions such as ^C^C^C). [ [61]Contents ] [ [62]C-Kermit ] [ [63]Kermit Home ] 4.A. Group A: Library Functions Library functions, strictly portable, can be used by all modules on all platforms: [64]ckclib.h, [65]ckclib.c. (To be filled in... For now, see [66]Section 3.1 and the comments in ckclib.c.) [ [67]Contents ] [ [68]C-Kermit ] [ [69]Kermit Home ] 4.B. Group B: Kermit File Transfer The Kermit protocol kernel. These files, whose names start with "ckc are supposed to be totally portable C, and are expected to compile correctly on any platform with any C compiler. "Portable" does not mean the same as as "ANSI" -- these modules must compile on 10- and 20-year old computers, with C preprocessors, compilers, and/or linkers that have all sorts of restrictions. The Group B modules do not include any header files other than those that come with Kermit itself. They do not contain any library calls except from the standard C library (e.g. printf()). They most certainly do not contain any system calls. Files: [70]ckcsym.h For use by C compilers that don't allow -D on the command line. [71]ckcasc.h ASCII character symbol definitions. [72]ckcsig.h System-independent signal-handling definitions and prototypes. [73]ckcdeb.h Originally, debugging definitions. Now this file also contains all definitions and prototypes that are shared by all modules in all groups. [74]ckcker.h Kermit protocol symbol definitions. [75]ckcxla.h Character-set-related symbol definitions (see next section). [76]ckcmai.c The main program. This module contains the declarations of all the protocol-related global variables that are shared among the other modules. [77]ckcpro.w The protocol module itself, written in "wart", a lex-like preprocessor that is distributed with Kermit under the name CKWART.C. [78]ckcfns.c, [79]ckcfn2.c, [80]ckcfn3.c The protocol support functions used by the protocol module. [81]Group B modules may call upon functions from [82]Group E, but not from [83]Group D modules (with the single exception that the main program invokes the user interface, which is in Group D). (This last assertion is really only a conjecture.) [ [84]Contents ] [ [85]C-Kermit ] [ [86]Kermit Home ] 4.C. Group C: Character-Set Conversion Character set translation tables and functions. Used by the [87]Group B, protocol modules, but may be specific to different computers. (So far, all character character sets supported by C-Kermit are supported in [88]ckuxla.c and [89]ckuxla.h, including Macintosh and IBM character sets). These modules should be completely portable, and not rely on any kind of system or library services. [90]ckcxla.h Character-set definitions usable by all versions of C-Kermit. ck?xla.h Character-set definitions for computer "?", e.g. [91]ckuxla.h for UNIX, [92]ckmxla.h for Macintosh. [93]ck?xla Character-set translation tables and functions for computer "?", For example, CKUXLA.C for UNIX, CKMXLA.C for Macintosh. So far, these are the only two such modules. The UNIX module is used for all versions of C-Kermit except the Macintosh version. [94]ckcuni.h Unicode definitions [95]ckcuni.c Unicode module Here's how to add a new file character set in the original (non-Unicode modules). Assuming it is based on the Roman (Latin) alphabet. Let's call it "Barbarian". First, in ck?xla.h, add a definition for FC_BARBA (8 chars maximum length) and increase MAXFCSETS by 1. Then, in ck?xla.c: * Add a barbarian entry into the fcsinfo array. * Add a "barbarian" entry to file character set keyword table, fcstab. * Add a "barbarian" entry to terminal character set keyword table, ttcstab. * Add a translation table from Latin-1 to barbarian: yl1ba[]. * Add a translation table from barbarian to Latin-1: ybal1[]. * Add a translation function from Barbarian to ASCII: xbaas(). * Add a translation function from Barbarian to Latin-1: xbal1(). * Add a translation function from Latin-1 to Barbarian: xl1ba(). * etc etc for each transfer character set... * Add translation function pointers to the xls and xlr tables. Other translations involving Barbarian (e.g. from Barbarian to Latin-Cyrillic) are performed through these tables and functions. See ckuxla.h and ckuxla.c for extensive examples. To add a new Transfer Character Set, e.g. Latin Alphabet 9 (for the Euro symbol), again in the "old" character-set modules: In ckcxla.h: + Add a TC_xxxx definition and increase MAXTCSETS accordingly. In ck?xla.h (since any transfer charset is also a file charset): + Add an FC_xxxx definition and increase MAXFCSETS accordingly. In ck?xla.c: + Add a tcsinfo[] entry. + Make a tcstab[] keyword table entry. + Make an fcsinfo[] table entry. + Make an fcstab[] keyword table entry. + Make a tcstab[] keyword table entry. + If necessary, make a langinfo[] table entry. + Make entries in the function pointer arrays. + Provide any needed functions. As of C-Kermit 7.0, character sets are also handled in parallel by the new (and very large) Unicode module, ckcuni.[ch]. Eventually we should phase out the old way, described just above, and operate entirely in (and through) Unicode. The advantages are many. The disadvantages are size and performance. To add a character to the Unicode modules: In ckcuni.h: + (To be filled in...) In ckcuni.c: + (To be filled in...) [ [96]Contents ] [ [97]C-Kermit ] [ [98]Kermit Home ] 4.D. Group D: User Interface This is the code that communicates with the user, gets her commands, informs her of the results. It may be command-line oriented, interactive prompting dialog, menus and arrow keys, windows and mice, speech recognition, telepathy, etc. The one provided is command-and prompt, with the ability to read commands from various sources: the console keyboard, a file, or a macro definition. The user interface has three major functions: 1. Sets the parameters for the file transfer and then starts it. This is done by setting certain (many) global variables, such as the protocol machine start state, the file specification, file type, communication parameters, packet length, window size, character set, etc. 2. Displays messages on the user's screen during the file transfer, using the screen() function, which is called by the group-1 modules. 3. Executes any commands directly that do not require Kermit protocol, such as the CONNECT command, local file management commands, parameter-setting commands, FTP client commands, etc. If you plan to embed the [99]Group B, files into a program with a different user interface, your interface must supply an appropriate screen() function, plus a couple related ones like chkint() and intmsg() for handling keyboard (or mouse, etc) interruptions during file transfer. The best way to find out about this is to link all the C-Kermit modules together except the ckuu*.o and ckucon.o modules, and see which missing symbols turn up. C-Kermit's character-oriented user interface (as opposed to the Macintosh version's graphical user interface) consists of the following modules. C-Kermit can be built with an interactive command parser, a command-line-option-only parser, a graphical user interface, or any combination, and it can even be built with no user interface at all (in which case it runs as a remote-mode Kermit server). [100]ckucmd.h [101]ckucmd.c The command parsing primitives used by the interactive command parser to parse keywords, numbers, filenames, etc, and to give help, complete fields, supply defaults, allow abbreviations and editing, etc. This package is totally independent of Kermit, but does depend on the [102]Group E functions. [103]ckuusr.h Definitions of symbols used in Kermit's commands. ckuus*.c Kermit's interactive command parser, including the script programming language: [104]ckuusr.c (includes top-level keyword tables); [105]ckuus2.c (HELP command text); [106]ckuus3.c (most of the SET command); [107]ckuus4.c (includes variables and functions); ckuus[567].c (miscellaneous); [108]ckuusy.c The command-line-option parser. [109]ckuusx.c User interface functions common to both the interactive and command-line parsers. [110]ckuver.h Version heralds for different implementations. [111]ckuscr.c The (old, uucp-like) SCRIPT command [112]ckudia.c The DIAL command. Includes specific knowledge of many types of modems. Note that none of the above files is actually Unix-specific. Over time they have proven to be portable among all platforms where C-Kermit is built: Unix, VMS, AOS/VS, Amiga, OS-9, VOS, etc etc. Thus the third letter should more properly be "c", but changing it would be too confusing. ck?con.c, ckucns.c The CONNECT command. Terminal connection, and in some cases (Macintosh, Windows) also terminal emulation. NOTE: As of C-Kermit 7.0, there are two different CONNECT modules for UNIX: [113]ckucon.c -- the traditional, portable, fork()-based version -- and [114]ckucns.c, a new version that uses select() rather than forks so it can handle encryption. ckucns.c is the preferred version for Unix; ckucon.c is not likely to keep pace with it in terms of upgrades, etc. However, since select() is not portable to every platform, ckucon.c will be kept indefinitely for those platforms that can't use ckucns.c. NOTE: SunLink X.25 support is available only in ckucon.c. ck_*.*, ckuat*.* Modules having to do with authentication and encryption. Since the relaxation of USA export laws, they are included with the general source-code distribution. Secure C-Kermit binaries can be built using special targets in the standard makefile. However, secure prebuilt binaries may not be distributed. For other implementations, the files may, and probably do, have different names. For example, the Macintosh graphical user interface filenames start with "ckm". Kermit 95 uses the ckucmd and ckuus* modules, but has its own CONNECT command modules. And so on. Here is a brief description of C-Kermit's "user interface interface", from ckuusr.c. It is nowhere near complete; in particular, hundreds of global variables are shared among the many modules. These should, some day, be collected into classes or structures that can be passed around as needed; not only for purity's sake, but also to allow for multiple simultaneous communication sessions and or user interfaces. Our list of things to do is endless, and reorganizing the source is almost always at the bottom. The ckuus*.c modules (like many of the ckc*.c modules) depend on the existence of C library features like fopen, fgets, feof, (f)printf, argv/argc, etc. Other functions that are likely to vary among operating systems -- like setting terminal modes or interrupts -- are invoked via calls to functions that are defined in the [115]Group E platform-dependent modules, ck?[ft]io.c. The command line parser processes any arguments found on the command line, as passed to main() via argv/argc. The interactive parser uses the facilities of the cmd package (developed for this program, but, in theory, usable by any program). Any command parser may be substituted for this one. The only requirements for the Kermit command parser are these: 1. Set parameters via global variables like duplex, speed, ttname, etc. See [116]ckcmai.c for the declarations and descriptions of these variables. 2. If a command can be executed without the use of Kermit protocol, then execute the command directly and set the sstate (start state) variable to 0. Examples include SET commands, local directory listings, the CONNECT command. 3. If a command requires the Kermit protocol, set the following variables: sstate string data 'x' (enter server mode) (none) 'r' (send a 'get' command) cmarg, cmarg2 'v' (enter receive mode) cmarg2 'g' (send a generic command) cmarg 's' (send files) nfils, cmarg & cmarg2 OR cmlist 'c' (send a remote host command) cmarg cmlist is an array of pointers to strings. cmarg, cmarg2 are pointers to strings. nfils is an integer (hmmm, probably should be an unsigned long). cmarg can be: A filename string (possibly wild), or: a pointer to a prefabricated generic command string, or: a pointer to a host command string. cmarg2 is: The name to send a single file under, or: the name under which to store an incoming file; must not be wild. If it's the name for receiving, a null value means to store the file under the name it arrives with. cmlist is: A list of nonwild filenames, such as passed via argv. nfils is an integer, interpreted as follows: -1: filespec (possibly wild) in cmarg, must be expanded internally. 0: send from stdin (standard input). >0: number of files to send, from cmlist. The screen() function is used to update the screen during file transfer. The tlog() function writes to a transaction log (if TLOG is defined). The debug() function writes to a debugging log (if DEBUG is defined). The intmsg() and chkint() functions provide the user i/o for interrupting file transfers. [ [117]Contents ] [ [118]C-Kermit ] [ [119]Kermit Home ] 4.E. Group E: Platform-Dependent I/O Platform-dependent function definitions. All the Kermit modules, including the command package, call upon these functions, which are designed to provide system-independent primitives for controlling and manipulating devices and files. For Unix, these functions are defined in the files [120]ckufio.c (files), [121]ckutio.c (communications), and [122]ckusig.c (signal handling). For VMS, the files are [123]ckvfio.c, ckvtio.c, and [124]ckusig.c (VMS can use the same signal handling routines as Unix). It doesn't really matter what the files are called, except for Kermit distribution purposes (grouping related files together alphabetically), only that each function is provided with the name indicated, observes the same calling and return conventions, and has the same type. The Group E modules contain both functions and global variables that are accessed by modules in the other groups. These are now described. (By the way, I got this list by linking all the C-Kermit modules together except ckutio and ckufio. These are the symbols that ld reported as undefined. But that was a long time ago, probably circa Version 6.) 4.E.1. Global Variables char *DELCMD; Pointer to string containing command for deleting files. Example: char *DELCMD = "rm -f "; (UNIX) Example: char *DELCMD = "delete "; (VMS) Note trailing space. Filename is concatenated to end of this string. NOTE: DELCMD is used only in versions that do not provide their own built-in DELETE command. char *DIRCMD; Pointer to string containing command for listing files when a filespec is given. Example: char *DIRCMD = "/bin/ls -l "; (UNIX) Example: char *DIRCMD = "directory "; (VMS) Note trailing space. Filename is concatenated to end of this string. NOTE: DIRCMD is used only in versions that do not provide their own built-in DIRECTORY command. char *DIRCM2; Pointer to string containing command for listing files when a filespec is not given. (currently not used, handled in another way.) Example: char *DIRCMD2 = "/bin/ls -ld *"; NOTE: DIRCMD2 is used only in versions that do not provide their own built-in DIRECTORY command. char *PWDCMD; Pointer to string containing command to display current directory. Example: char *PWDCMD = "pwd "; NOTE: PWDCMD is used only in versions that do not provide their own built-in PWD command. char *SPACMD; Pointer to command to display free disk space in current device/directory. Example: char *SPACMD = "df ."; NOTE: SPACMD is used only in versions that do not provide their own built-in SPACE command. char *SPACM2; Pointer to command to display free disk space in another device/directory. Example: char *SPACM2 = "df "; Note trailing space. Device or directory name is added to this string. NOTE: SPACMD2 is used only in versions that do not provide their own built-in SPACE command. char *TYPCMD; Pointer to command for displaying the contents of a file. Example: char *TYPCMD = "cat "; Note trailing space. Device or directory name is added to this string. NOTE: TYPCMD is used only in versions that do not provide their own built-in TYPE command. char *WHOCMD; Pointer to command for displaying logged-in users. Example: char *WHOCMD = "who "; Note trailing space. Specific user name may be added to this string. int backgrd = 0; Flag for whether program is running in foreground (0) or background (nonzero). Background operation implies that screen output should not be done and that all errors should be fatal. int ckxech; Flag for who is to echo console typein: 1: The program (system is not echoing). 0: The OS, front end, terminal, etc (not this program). char *ckxsys; Pointer to string that names the computer and operating system. Example: char *ckxsys = " NeXT Mach 1.0"; Tells what computer system ckxv applies to. In UNIX Kermit, this variable is also used to print the program herald, and in the SHOW VERSION command. char *ckxv; Pointer to version/edit info of ck?tio.c module. Example: char *ckxv = "UNIX Communications Support, 6.0.169, 6 Sep 96"; Used by SHOW VERSION command. char *ckzsys; Like ckxsys, but briefer. Example: char *ckzsys = " 4.3 BSD"; Tells what platform ckzv applies to. Used by the SHOW VERSION command. char *ckzv; Pointer to version/edit info of ck?fio.c module. Example: char *ckzv = "UNIX File support, 6.0.113, 6 Sep 96"; Used by SHOW VERSION command. int dfflow; Default flow control. 0 = none, 1 = Xon/Xoff, ... (see FLO_xxx symbols in ckcdeb.h) Set by Group E module. Used by [125]ckcmai.c to initialize flow control variable. int dfloc; Default location. 0 = remote, 1 = local. Set by Group E module. Used by ckcmai.c to initialize local variable. Used in various places in the user interface. int dfprty; Default parity. 0 = none, 'e' = even, 'o' = odd, 'm' = mark, 's' = space. Set by Group E module. Used by ckcmai.c to initialize parity variable. char *dftty; Default communication device. Set by Group E module. Used in many places. This variable should be initialized the symbol CTTNAM, which is defined in ckcdeb.h, e.g. as "/dev/tty" for UNIX, "TT:" for VMS, etc. Example: char *dftty = CTTNAM; char *mtchs[]; Array of string pointers to filenames that matched the most recent wildcard match, i.e. the most recent call to zxpand(). Used (at least) by command parsing package for partial filename completion. int tilde_expand; Flag for whether to attempt to expand leading tildes in directory names (used in UNIX only, and then only when the symbol DTILDE is defined. int ttnproto; The protocol being used to communicate over a network device. Values are defined in ckcnet.h. Example: NP_TELNET is network protocol "telnet". int maxnam; The maximum length for a filename, exclusive of any device or directory information, in the format of the host operating system. int maxpath; The maximum length for a fully specified filename, including device designator, directory name, network node name, etc, in the format of the host operating system, and including all punctuation. int ttyfd; File descriptor of the communication device. -1 if there is no open or usable connection, including when C-Kermit is in remote mode. Since this is not implemented everywhere, references to it are in #ifdef CK_TTYFD..#endif. [ [126]Contents ] [ [127]C-Kermit ] [ [128]Kermit Home ] 4.E.2. Functions These are divided into three categories: file-related functions (B.1), communication functions (B.2), and miscellaneous functions (B.3). 4.E.2.1. File-Related Functions In most implementations, these are collected together into a module called ck?fio.c, where ? = "u" ([129]ckutio.c for Unix), "v" ([130]ckvtio.c for VMS), [131]etc. To be totally platform-independent, C-Kermit maintains its own file numbers, and provides the functions described in this section to deal with the files associated with them. The file numbers are referred to symbolically, and are defined as follows in ckcker.h: #define ZCTERM 0 /* Console terminal */ #define ZSTDIO 1 /* Standard input/output */ #define ZIFILE 2 /* Current input file for SEND command */ #define ZOFILE 3 /* Current output file for RECEIVE command */ #define ZDFILE 4 /* Current debugging log file */ #define ZTFILE 5 /* Current transaction log file */ #define ZPFILE 6 /* Current packet log file */ #define ZSFILE 7 /* Current session log file */ #define ZSYSFN 8 /* Input from a system function (pipe) */ #define ZRFILE 9 /* Local file for READ command */ (NEW) #define ZWFILE 10 /* Local file for WRITE command */ (NEW) #define ZMFILE 11 /* Auxiliary file for internal use */ (NEW) #define ZNFILS 12 /* How many defined file numbers */ In the descriptions below, fn refers to a filename, and n refers to one of these file numbers. Functions are of type int unless otherwise noted, and are listed mostly alphabetically. int chkfn(n) int n; Checks the file number n. Returns: -1: File number n is out of range 0: n is in range, but file is not open 1: n in range and file is open int iswild(filspec) char *filespec; Checks if the file specification is "wild", i.e. contains metacharacters or other notations intended to match multiple filenames. Returns: 0: not wild 1: wild. int isdir(string) char *string; Checks if the string is the name of an existing directory. The idea is to check whether the string can be "cd'd" to, so in some cases (e.g. DOS) it might also indicate any file structured device, such as a disk drive (like A:). Other nonzero returns indicate system-dependent information; e.g. in VMS isdir("[.FOO]") returns 1 but isdir("FOO.DIR;1") returns 2 to indicate the directory-file name is in a format that needs conversion before it can be combined with a filename. Returns: 0: not a directory (including any kind of error) 1: it is an existing directory char * zfcdat(name) char *name; Returns modification (preferably, otherwise creation) date/time of file whose name is given in the argument string. Return value is a pointer to a string of the form yyyymmdd hh:mm:ss, for example 19931231 23:59:59, which represents the local time (no timezone or daylight savings time finagling required). Returns the null string ("") on failure. The text pointed to by the string pointer might be in a static buffer, and so should be copied to a safe place by the caller before any subsequent calls to this function. struct zfnfp * zfnqfp(fn, buflen, buf) char * fn; int buflen; char * buf; Given the filename fn, the corresponding fully qualified, absolute filename is placed into the buffer buf, whose length is buflen. On failure returns a NULL pointer. On success returns a pointer to a struct zfnfp containing pointers to the full pathname and to just the filename, and an int giving the length of the full pathname. All references to this function in mainline code must be protected by #ifdef ZFNQFP..#endif, because it is not present in all of the ck*fio.c modules. So if you implement this function in a version that did not have it before, be sure to add #define ZFNQFP in the appropriate spot in ckcdeb.h or in the build-procedure CFLAGS. int zcmpfn(s1,s2) char * s2, * s2; Compares two filenames to see if they refer to the same. Internally, the arguments can be converted to fully qualified pathnames, e.g. with zfnqfp(), realpath(), or somesuch. In Unix or other systems where symbolic links exist, the link should be resolved before making the comparison or looking at the inodes. Returns: 0: Files are not identical. 1: Files are identical. int zfseek(pos) long pos; Positions the input pointer on the current input file to the given position. The pos argument is 0-based, the offset (distance in bytes) from beginning of the file. Needed for RESEND, PSEND, and other recovery operations. This function is not necessarily possible on all systems, e.g. record-oriented systems. It should only be used on binary files (i.e. files we are sending in binary mode) and stream-oriented file systems. Returns: -1: on failure. 0: On success. int zchdir(dirnam) char *dirnam; Changes current or default directory to the one given in dirnam. Returns: 0: On failure. 1: on success. long zchki(fn) char *fn; Check to see if file with name fn is a regular, readable, existing file, suitable for Kermit to send -- not a directory, not a symbolic link, etc. Returns: -3: if file exists but is not accessible (e.g. read-protected); -2: if file exists but is not of a readable type (e.g. a directory); -1: on error (e.g. file does not exist, or fn is garbage); >=0: (length of file) if file exists and is readable. Also see isdir(), zgetfs(). int zchkpid(pid) unsigned long pid; Returns: 1: If the given process ID (e.g. pid in UNIX) is valid and active 0: otherwise. long zgetfs(fn) char *fn; Gets the size of the given file, regardless of accessibility. Used for directory listings. Unlike zchki(), should return the size of any kind of file, even a directory. zgetfs() also should serve as a mini "get file info" function that can be used until we design a better one, by also setting some global variables: int zgfs_link = 1/0 = file is (not) a symbolic link. int zgfs_dir = 1/0 = file is (not) a directory. char linkname[] = if zgfs_link != 0, name of file link points to. Returns: -1: on error (e.g. file does not exist, or fn is garbage); >=0: (length of file) if file exists and is readable. int zchko(fn) char *fn; Checks to see if a file of the given name can be created. Returns: -1: if file cannot be created, or on any kind of error. 0: if file can be created. int zchkspa(fn,len) char *f; long len; Checks to see if there is sufficient space to store the file named fn, which is len bytes long. If you can't write a function to do this, then just make a dummy that always returns 1; higher level code will recover from disk-full errors. The receiving Kermit uses this function to refuse an incoming file based on its size, via the attribute mechanism. Returns: -1: on error. 0: if there is not enough space. 1: if there is enough space. int zchin(n,c) int n; int *c; Gets a character from file number n, return it in c (call with &c). Returns: -1: on failure, including EOF. 0: on success with character in c. int zchout(n,c) int n; char c; Writes the character c to file number n. Returns: -1: on error. 0: on success. int zclose(n) int n; Closes file number n. Returns: -1: on error. 1: on success. int zdelet(fn) char *name; Attempts to delete (remove, erase) the named file. Returns: -1: on error. 1: if file was deleted successfully. char * zgperm(char * f) Returns a pointer to the system-dependent numeric permissions/protection string for file f, or NULL upon failure. Used if CK_PERMS is defined. char * ziperm(char * f) Returns a pointer to the system-dependent symbolic permissions/protection string for file f, or NULL upon failure. Used if CK_PERMS is defined. Example: In UNIX zgperm(f) might return "100770", but ziperm() might return "-rwxrwx---". In VMS, zgperm() would return a hexadecimal string, but ziperm() would return something like "(RWED,RWED,RE,)". char * zgtdir() Returns a pointer to the name of the current directory, folder, etc, or a NULL pointer if the current directory cannot be determined. If possible, the directory specification should be (a) fully specified, e.g. as a complete pathname, and (b) be suitable for appending a filename. Thus, for example, Unix directory names should end with '/'. VMS directory names should look like DEV:[NAME] (rather than, say, NAME.DIR;1). char * zhome() Returns a pointer to a string containing the user's home directory, or NULL upon error. Should be formatted like zgtdir() (q.v.). int zinfill() Fill buffer from input file. This function is used by the macro zminchar(), which is defined in ckcker.h. zminchar() manages its own buffer, and calls zinfill() to fill it whenever it becomes empty. It is used only for sending files, and reads characters only from file number ZIFILE. zinfill() returns -1 upon end of file, -2 upon fatal error, and -3 upon timeout (e.g. when reading from a pipe); otherwise it returns the first character from the buffer it just read. int zkself() Kills the current job, session, process, etc, logs out, disappears. Used by the Kermit server when it receives a BYE command. On failure, returns -1. On success, does not return at all! This function should not be called until all other steps have been taken to close files, etc. VOID zstrip(fn,&fn2) char *fn1, **fn2; Strips device and directory, etc, from file specification fn, leaving only the filename (including "extension" or "filetype" -- the part after the dot). For example DUA0:[PROGRAMS]OOFA.C;3 becomes OOFA.C, or /usr/fdc/oofa.c becomes oofa.c. Returns a pointer to result in fn2. int zsetperm(char * file, unsigned int code) Set permissions of file to given system-dependent code. 0: On failure. 1: on success. int zsetroot(char * dir) Sets the root for the user's file access, like Unix chroot(), but does not require privilege. In Unix, this must be implemented entirely by Kermit's own file access routines. Returns: 1: Success -1: Invalid argument -2: -3: Internal error -4: Access to given directory denied -5: New root not within old root int zinroot(char * file) If no root is set (zsetroot()), returns 1. Otherwise, if given file is in the root, returns 1. Otherwise, returns 0. VOID zltor(fn,fn2) char *fn1, *fn2; Local-To-Remote filename translation. OBSOLETE: replaced by nzltor() (q.v.). Translates the local filename fn into a format suitable for transmission to an arbitrary type of computer, and copies the result into the buffer pointed to by fn2. Translation may involve (a) stripping the device and/or directory/path name, (b) converting lowercase to uppercase, (c) removing spaces and strange characters, or converting them to some innocuous alphabetic character like X, (d) discarding or converting extra periods (there should not be more than one). Does its best. Returns no value. name2 is a pointer to a buffer, furnished by the caller, into which zltor() writes the resulting name. No length checking is done. #ifdef NZLTOR VOID nzltor(fn,fn2,convert,pathnames,max) char *fn1,*fn2; int convert,pathnames,max; Replaces zltor(). This new version handles pathnames and checks length. fn1 and fn2 are as in zltor(). This version is called unconditionally for each file, rather than only when filename conversion is enabled. Pathnames can have the following values: PATH_OFF: Pathname, if any, is to be stripped PATH_REL: The relative pathname is to be included PATH_ABS: The full pathname is to be included After handling pathnames, conversion is done to the result as in the zltor() description if convert != 0; if relative or absolute pathnames are included, they are converted to UNIX format, i.e. with slash (/) as the directory separator. The max parameter specifies the maximum size of fn2. If convert > 0, the regular conversions are done; if convert < 0, minimal conversions are done (we skip uppercasing the letters, we allow more than one period, etc; this can be used when we know our partner is UNIX or similar). #endif /* NZLTOR */ int nzxpand(fn,flags) char *fn; int flags; Replaces zxpand(), which is obsolete as of C-Kermit 7.0. Call with: fn = Pointer to filename or pattern. flags = option bits: flags & ZX_FILONLY Match regular files flags & ZX_DIRONLY Match directories flags & ZX_RECURSE Descend through directory tree flags & ZX_MATCHDOT Match "dot files" flags & ZX_NOBACKUP Don't match "backup files" flags & ZX_NOLINKS Don't follow symlinks. Returns the number of files that match fn, with data structures set up so the first file (if any) will be returned by the next znext() call. If ZX_FILONLY and ZX_DIRONLY are both set, or neither one is set, files and directories are matched. Notes: 1. It is essential that the number returned by nzxpand() reflect the actual number of filenames that will be returned by znext() calls. In other words: for (n = nzxpand(string,flags); n > 0; n--) { znext(buf); printf("%s\n", buf); } should print all the file names; no more, no less. 2. In UNIX, DOS, OS-9, etc, where directories contain entries for themselves (.) and the superior directory (..), these should NOT be included in the list under any circumstances, including when ZX_MATCHDOT is set. 3. Additional option bits might be added in the future, e.g. for sorting (sort by date/name/size, reverse/ascending, etc). Currently this is done only in higher level code (through a hack in which the nzxpand() exports its filename array, which is not portable because not all OS's can use this mechanism). int zmail(addr,fn) char *addr, fn; Send the local, existing file fn as e-mail to the address addr. Returns: 0: on success 2: if mail delivered but temp file can't be deleted -2: if mail can't be delivered int zmkdir(path) char *path; The path can be a file specification that might contain directory information, in which the filename is expected to be included, or an unambiguous directory specification (e.g. in UNIX it must end with "/"). This routine attempts to create any directories in the given path that don't already exist. Returns 0 or greater success: no directories needed creation, or else all directories that needed creation were created successfully; the return code is the number of directories that were created. Returns -1 on failure to create any of the needed directories. int zrmdir(path) char *path; Attempts to remove the given directory. Returns 0 on success, -1 on failure. The detailed semantics are open -- should it fail if the directory contains any files or subdirectories, etc. It is probably best for this routine to behave in whatever manner is customary on the underlying platform; e.g. in UNIX, VMS, DOS, etc, where directories can not be removed unless they are empty. VOID znewn(fn,s) char *fn, **s; Transforms the name fn into a filename that is guaranteed to be unique. If the file fn does not exist, then the new name is the same as fn; Otherwise, it's different. this function does its best, returns no value. New name is created in caller's space. Call like this: znewn(old,&new);. The second parameter is a pointer to the new name. This pointer is set by znewn() to point to a static string in its own space, so be sure to the result to a safe place before calling this function again. int znext(fn) char *fn; Copies the next file name from a file list created by zxpand() into the string pointed to by fn (see zxpand). If no more files, then the null string is placed there. Returns 0 if there are no more filenames, with 0th element the array pointed to by fn set to NUL. If there is a filename, it is stored in the array pointed to by fn and a positive number is returned. NOTE: This is a change from earlier definitions of this function (pre-1999), which returned the number of files remaining; thus 0 was the return value when returning the final file. However, no mainline code ever depended on the return value, so this change should be safe. int zopeni(n,fn) int n; char *fn; Opens the file named fn for input as file number n. Returns: 0: on failure. 1: on success. int zopeno(n,fn,zz,fcb) int n; char *name; struct zattr *zz; struct filinfo *fcb; Attempts to open the named file for output as file number n. zz is a Kermit file attribute structure as defined in ckcdeb.h, containing various information about the file, including its size, creation date, and so forth. This function should attempt to honor as many of these as possible. fcb is a "file control block" in the traditional sense, defined in ckcdeb.h, containing information relevant to complicated file systems like VMS (RMS), IBM MVS, etc, like blocksize, record length, organization, record format, carriage control, etc. Returns: 0: on failure. 1: on success. int zoutdump() Dumps a file output buffer. Used with the macro zmchout() defined in ckcker.h. Used only with file number ZOFILE, i.e. the file that is being received by Kermit during file transfer. Returns: -1: on failure. 0: on success. int zprint(p,fn) char *p, *f; Prints the file with name fn on a local printer, with options p. Returns: 0: on success 3: if file sent to printer but can't be deleted -3: if file can't be printed int zrename(fn,fn2) char *fn, *fn2; Changes the name of file fn to fn2. If fn2 is the name of an existing directory, or a file-structured device, then file fn is moved to that directory or device, keeping its original name. If fn2 lacks a directory separator when passed to this function, an appropriate one is supplied. Returns: -1: on failure. 0: on success. int zcopy(source,dest) char * source, * dest; Copies the source file to the destination. One file only. No wildcards. The destination string may be a filename or a directory name. Returns: 0: on success. <0: on failure: -2: source file is not a regular file. -3: source file not found. -4: permission denied. -5: source and destination are the same file. -6: i/o error. -1: other error. char * zlocaltime(char *) Call with: "yyyymmdd hh:mm:ss" GMT/UTC date-time. Returns pointer to local date-time string "yyyymmdd hh:mm:ss" on success, NULL on failure. VOID zrtol(fn,fn2) char *fn, *fn2; Remote-To-Local filename translation. OBSOLETE: replaced by nzrtol(). Translates a "standard" filename to a local filename. For example, in Unix this function might convert an all-uppercase name to lowercase, but leave lower- or mix-case names alone. Does its best, returns no value. New name is in string pointed to by fn2. No length checking is done. #ifdef NZLTOR int nzrtol(fn,fn2,convert,pathnames,max) char *fn1,*fn2; int convert,pathnames,max; Replaces zrtol. Like zrtol but handles pathnames and checks length. See nzltor for detailed description of parameters. #endif /* NZLTOR */ int zsattr(xx) struct zattr *xx; Fills in a Kermit file attribute structure for the file which is to be sent, namely the currently open ZIFILE. Note that this is not a very good design, but we're stuck with it. Callers must ensure that zsattr() is called only on real files, not on pipes, internally generated file-like objects such as server REMOTE command responses, etc. Returns: -1: on failure. 0: on success with the structure filled in. If any string member is null, it should be ignored by the caller. If any numeric member is -1, it should be ignored by the caller. int zshcmd(s) char *s; s contains to pointer to a command to be executed by the host computer's shell, command parser, or operating system. If the system allows the user to choose from a variety of command processors (shells), then this function should employ the user's preferred shell. If possible, the user's job (environment, process, etc) should be set up to catch keyboard interruption signals to allow the user to halt the system command and return to Kermit. The command must run in ordinary, unprivileged user mode. If possible, this function should return -1 on failure to start the command, or else it should return 1 if the command succeeded and 0 if it failed. int pexitstatus zshcmd() and zsyscmd() should set this to the command's actual exit status code if possible. int zsyscmd(s) char *s; s contains to pointer to a command to be executed by the host computer's shell, command parser, or operating system. If the system allows the user to choose from a variety of command processors (shells), then this function should employ the system standard shell (e.g. /bin/sh for Unix), so that the results will always be the same for everybody. If possible, the user's job (environment, process, etc) should be set up to catch keyboard interruption signals to allow the user to halt the system command and return to Kermit. The command must run in ordinary, unprivileged user mode. If possible, this function should return -1 on failure to start the command, or else it should return 1 if the command succeeded and 0 if it failed. VOID z_exec(s,args) char * s; char * args[]; This one executes the command s (which is searched for using the system's normal searching mechanism, such as PATH in UNIX), with the given argument vector, which follows the conventions of UNIX argv[]: the name of the command pointed to by element 0, the first arg by element 1, and so on. A null args[] pointer indicates the end of the argument list. All open files must remain open so the exec'd process can use them. Returns only if unsuccessful. int zsinl(n,s,x) int n, x; char *s; Reads a line from file number n. Writes the line into the address s provided by the caller. Writing terminates when newline is read, but with newline discarded. Writing also terminates upon EOF or if length x is exhausted. Returns: -1: on EOF or error. 0: on success. int zsout(n,s) int n; char *s; Writes the string s out to file number n. Returns: -1: on failure. 0: on success. int zsoutl(n,s) int n; char *s; Writes the string s out to file number n and adds a line (record) terminator (boundary) appropriate for the system and the file format. Returns: -1: on failure. 0: on success. int zsoutx(n,s,x) int n, x; char *s; Writes exactly x characters from string s to file number n. If s has fewer than x characters, then the entire string s is written. Returns: -1: on failure. >= 0: on success, the number of characters actually written. int zstime(fn,yy,x) char *fn; struct zattr *yy; int x; Sets the creation date (and other attributes) of an existing file, or compares a file's creation date with a given date. Call with: fn: pointer to name of existing file. yy: Pointer to a Kermit file attribute structure in which yy->date.val is a date of the form yyyymmdd hh:mm:ss, e.g. 19900208 13:00:00, which is to be used for setting or comparing the file date. Other attributes in the struct can also be set, such as the protection/permission (See [132]Appendix I), when it makes sense (e.g. "yy->lprotect.val" can be set if the remote system ID matches the local one). x: A function code: 0 means to set the file's creation date as given. 1 means compare the date from the yy struct with the file's date. Returns: -1: on any kind of error. 0: if x is 0 and the file date was set successfully. 0: if x is 1 and date from attribute structure > file creation date. 1: if x is 1 and date from attribute structure <= file creation date. VOID zstrip(name,name2) char *name, **name2; Strips pathname from filename "name". Constructs the resulting string in a static buffer in its own space and returns a pointer to it in name2. Also strips device name, file version numbers, and other "non-name" material. int zxcmd(n,s) char *s; Runs a system command so its output can be accessed as if it were file n. The command is run in ordinary, unprivileged user mode. If n is ZSTDIO or ZCTERM, returns -1. If n is ZIFILE or ZRFILE, then Kermit reads from the command, otherwise Kermit writes to the command. Returns 0 on error, 1 on success. int zxpand(fn) char *fn; OBSOLETE: Replaced by nzxpand(), q.v. #ifdef ZXREWIND int zxrewind() Returns the number of files returned by the most recent nzxpand() call, and resets the list to the beginning so the next znext() call returns the first file. Returns -1 if zxpand has not yet been called. If this function is available, ZXREWIND should be defined; otherwise it should not be referenced. #endif /* ZXREWIND */ int xsystem(cmd) char *cmd; Executes the system command without redirecting any of its i/o, similar (well, identical) to system() in Unix. But before passing the command to the system, xsystem() ensures that all privileges are turned off, so that the system command executes in ordinary unprivileged user mode. If possible, xsystem() returns the return code of the command that was executed. 4.E.2.2. IKSD Variables and Functions These must be implemented in any C-Kermit version that is to be installed as an Internet Kermit Service Daemon (IKSD). IKSD is expected to be started by the Internet Daemon (e.g. inetd) with its standard i/o redirected to the incoming connection. int ckxanon; Nonzero if anonymous logins allowed. extern int inserver; Nonzero if started in IKSD mode. extern int isguest; Nonzero if IKSD and user logged in anonymously. extern char * homdir; Pointer to user's home directory. extern char * anonroot; Pointer to file-system root for anonymous users. Existing functions must make "if (inserver && isguest)" checks for actions that would not be legal for guests: zdelete(), zrmdir(), zprint(), zmail(), etc. int zvuser(name) char * name; Verifies that user "name" exists and is allowed to log in. If the name is "ftp" or "anonymous" and ckxanon != 0, a guest login is set up. Returns 0 if user not allowed to log in, nonzero if user may log in. int zvpass(string) char * string; Verifies password of the user from the most recent zvuser() call. Returns nonzero if password is valid for user, 0 if it isn't. Makes any appropriate system log entries (IKSD logins, failed login attempts, etc). If password is valid, logs the user in as herself (if real user), or sets up restricted anonymous access if user is guest (e.g. changes file-system root to anonroot and sets isguest = 1). VOID zsyslog() Begins any desired system logging of an IKSD session. VOID zvlogout() Terminates an IKSD session. In most cases this is simply a wrapper for exit() or doexit(), with some system logging added. 4.E.2.3. Privilege Functions These functions are used by C-Kermit to adapt itself to operating systems where the program can be made to run in a "privileged" mode, e.g. setuid or setgid in Unix. C-Kermit should NOT read and write files or start subprocesses as a privileged program. This would present a serious threat to system security. The security package has been installed to prevent such security breaches by turning off the program's special privileges at all times except when they are needed. In UNIX, the only need Kermit has for privileged status is access to the UUCP lockfile directory, in order to read, create, and destroy lockfiles, and to open communication devices that are normally protected against the user (see the [133]Unix C-Kermit Installation Instructions for discussion). Therefore, privileges should only be enabled for these operations and disabled at all other times. This relieves the programmer of the responsibility of putting expensive and unreliable access checks around every file access and subprocess creation. Strictly speaking, these functions are not required in all C-Kermit implementations, because their use (so far, at least) is internal to the Group E modules. However, they should be included in all C-Kermit implementations for operating systems that support the notion of a privileged program (UNIX, RSTS/E, what others?). int priv_ini() Determine whether the program is running in privileged status. If so, turn off the privileges, in such a way that they can be turned on again when needed. Called from sysinit() at program startup time. Returns: 0 on success nonzero on failure, in which case the program should halt immediately. int priv_on() If the program is not privileged, this function does nothing. If the program is privileged, this function returns it to privileged status. priv_ini() must have been called first. Returns: 0 on success nonzero on failure int priv_off() Turns privileges off (if they are on) in such a way that they can be turned back on again. Returns: 0 on success nonzero on failure int priv_can() Turns privileges off in such a way that they cannot be turned back on. Returns: 0 on success nonzero on failure int priv_chk() Attempts to turns privileges off in such a way that they can be turned on again later. Then checks to make sure that they were really turned off. If they were not really turned off, then they are canceled permanently. Returns: 0 on success nonzero on failure 4.E.2.4. Console-Related Functions These relate to the program's "console", or controlling terminal, i.e. the terminal that the user is logged in on and types commands at, or on a PC or workstation, the actual keyboard and screen. int conbin(esc) char esc; Puts the console into "binary" mode, so that Kermit's command parser can control echoing and other treatment of characters that the user types. esc is the character that will be used to get Kermit's attention during packet mode; puts this in a global place. Sets the ckxech variable. Returns: -1: on error. 0: on success. int concb(esc) char esc; Put console in "cbreak" (single-character wakeup) mode. That is, ensure that each console character is available to the program immediately when the user types it. Otherwise just like conbin(). Returns: -1: on error. 0: on success. int conchk() Returns a number, 0 or greater, the number of characters waiting to be read from the console, i.e. the number of characters that the user has typed that have not been read yet by Kermit. long congspd(); Returns the speed ("baud rate") of the controlling terminal, if known, otherwise -1L. int congks(timo) int timo; Get Keyboard Scancode. Reads a keyboard scan code from the physical console keyboard. If the timo parameter is greater than zero, then times out and returns -2 if no character appears within the given number of seconds. Upon any other kind of error, returns -1. Upon success returns a scan code, which may be any positive integer. For situations where scan codes cannot be read (for example, when an ASCII terminal is used as the job's controlling terminal), this function is identical to coninc(), i.e. it returns an 8-bit character value. congks() is for use with workstations whose keyboards have Alternate, Command, Option, and similar modifier keys, and Function keys that generate codes greater than 255. int congm() Console get modes. Gets the current console terminal modes and saves them so that conres() can restore them later. Returns 1 if it got the modes OK, 0 if it did nothing (e.g. because Kermit is not connected with any terminal), -1 on error. int coninc(timo) int timo; Console Input Character. Reads a character from the console. If the timo parameter is greater than zero, then coninc() times out and returns -2 if no character appears within the given number of seconds. Upon any other kind of error, returns -1. Upon success, returns the character itself, with a value in the range 0-255 decimal. VOID conint(f,s) SIGTYP (*f)(), (*s)(); Sets the console to generate an interrupt if the user types a keyboard interrupt character, and to transfer control the signal-handling function f. For systems with job control, s is the address of the function that suspends the job. Sets the global variable "backgrd" to zero if Kermit is running in the foreground, and to nonzero if Kermit is running in the background. See ckcdeb.h for the definition of SIGTYP. No return value. VOID connoi() Console no interrupts. Disable keyboard interrupts on the console. No return value. int conoc(c) char c; Writes character c to the console terminal. Returns: 0 on failure, 1 on success. int conol(s) char *s; Writes string s to the console. Returns -1 on error, 0 or greater on success. int conola(s) char *s[]; { Writes an array of strings to the console. Returns -1 on error, 0 or greater on success. int conoll(s) char *s; Writes string s to the console, followed by the necessary line termination characters to put the console cursor at the beginning of the next line. Returns -1 on error, 0 or greater on success. int conres() Restores the console terminal to the modes obtained by congm(). Returns: -1 on error, 0 on success. int conxo(x,s) int x; char *s; Write x characters from string s to the console. Returns 0 or greater on success, -1 on error. char * conkbg(); Returns a pointer to the designator of the console keyboard type. For example, on a PC, this function would return "88", "101", etc. Upon failure, returns a pointer to the empty string. 4.E.2.5. Communications Functions The communication device is the device used for terminal emulation and file transfer. It may or may not be the same device as the console, and it may or may not be a terminal (serial-port) device; it could also be a network connection. For brevity, the communication device is referred to here as the "tty". When the communication device is the same as the console device, Kermit is said to be in remote mode. When the two devices are different, Kermit is in local mode. int ttchk() Returns the number of characters that have arrived at the communication device but have not yet been read by ttinc(), ttinl(), and friends. If communication input is buffered (and it should be), this is the sum of the number of unread characters in Kermit's buffer PLUS the number of unread characters in the operating system's internal buffer. The call must be nondestructive and nonblocking, and as inexpensive as possible. Returns: 0: or greater on success, 0: in case of internal error, -1: or less when it determines the connection has been broken, or there is no connection. That is, a negative return from ttchk() should reliably indicate that there is no usable connection. Furthermore, ttchk() should be callable at any time to see if the connection is open. When the connection is open, every effort must be made to ensure that ttchk returns an accurate number of characters waiting to be read, rather than just 0 (no characters) or 1 (1 or more characters), as would be the case when we use select(). This aspect of ttchk's operation is critical to successful operation of sliding windows and streaming, but "nondestructive buffer peeking" is an obscure operating system feature, and so when it is not available, we have to do it ourselves by managing our own internal buffer at a level below ttinc(), ttinl(), etc, as in the UNIX version (non-FIONREAD case). An external global variable, clsondisc, if nonzero, means that if a serial connection drops (carrier on-to-off transition detected by ttchk()), the device should be closed and released automatically. int ttclos() Closes the communication device (tty or network). If there were any kind of exclusive access locks connected with the tty, these are released. If the tty has a modem connection, it is hung up. For true tty devices, the original tty device modes are restored. Returns: -1: on failure. 0: on success. int ttflui() Flush communications input buffer. If any characters have arrived but have not yet been read, discard these characters. If communications input is buffered by Kermit (and it should be), this function flushes Kermit's buffer as well as the operating system's internal input buffer. Returns: -1: on failure. 0: on success. int ttfluo() Flush tty output buffer. If any characters have been written but not actually transmitted (e.g. because the system has been flow-controlled), remove them from the system's output buffer. (Note, this function is not actually used, but it is recommended that all C-Kermit programmers add it for future use, even if it is only a dummy function that returns 0 always.) int ttgmdm() Looks for the modem signals CTS, DSR, and CTS, and returns those that are on in as its return value, in a bit mask as described for ttwmdm, in which a bit is on (1) or off (0) according to whether the corresponding signal is on (asserted) or off (not asserted). Return values: -3: Not implemented -2: if the line does not have modem control -1: on error >=0: on success, with bit mask containing the modem signals. long ttgspd() Returns the current tty speed in BITS (not CHARACTERS) per second, or -1 if it is not known or if the tty is really a network, or upon any kind of error. On success, the speed returned is the actual number of bits per second, like 1200, 9600, 19200, etc. int ttgwsiz() Get terminal window size. Returns -1 on error, 0 if the window size can't be obtained, 1 if the window size has been successfully obtained. Upon success, the external global variables tt_rows and tt_cols are set to the number of screen rows and number of screen columns, respectively. As this function is not implemented in all ck*tio.c modules, calls to it must be wrapped in #ifdef CK_TTGWSIZ..#endif. NOTE: This function must be available to use the TELNET NAWS feature (Negotiate About Window Size) as well as Rlogin. int tthang() Hang up the current tty device. For real tty devices, turn off DTR for about 1/3-1/2 second (or other length of time, depending on the system). If the tty is really a network connection, close it. Returns: -1: on failure. 0: if it does not even try to hang up. 1: if it believes it hung up successfully. VOID ttimoff() Turns off all pending timer interrupts. int ttinc(timo) int timo; (function is old, return codes are new) Reads one character from the communication device. If timo is greater than zero, wait the given number of seconds and then time out if no character arrives, otherwise wait forever for a character. Returns: -3: internal error (e.g. tty modes set wrong) -2: communications disconnect -1: timeout or other error >=0: the character that was read. It is HIGHLY RECOMMENDED that ttinc() be internally buffered so that calls to it are relatively inexpensive. If it is possible to to implement ttinc() as a macro, all the better, for example something like: #define ttinc(t) ( (--txbufn >= 0) ? txbuf[ttbufp++] : txbufr(t) ) (see description of txbufr() below) int ttinl(dest,max,timo,eol,start,turn) int max,timo,turn; CHAR *dest, eol, start; ttinl() is Kermit's packet reader. Reads a packet from the communications device, or up to max characters, whichever occurs first. A line is a string of characters starting with the start character up to and including the character given in eol or until the length is exhausted, or, if turn != 0, until the line turnaround character (turn) is read. If turn is 0, ttinl() *should* use the packet length field to detect the end, to allow for the possibility that the eol character appears unprefixed in the packet data. (The turnaround character is for half-duplex linemode connections.) If timo is greater than zero, ttinl() times out if the eol character is not encountered within the given number of seconds and returns -1. The characters that were input are copied into "dest" with their parity bits stripped if parity is not none. The first character copied into dest should be the start character, and the last should be the final character of the packet (the last block check character). ttinl() should also absorb and discard the eol and turn characters, and any other characters that are waiting to be read, up until the next start character, so that subsequent calls to ttchk() will not succeed simply because there are some terminators still sitting in the buffer that ttinl() didn't read. This operation, if performed, MUST NOT BLOCK (so if it can't be performed in a guaranteed nonblocking way, don't do it). On success, ttinl() returns the number of characters read. Optionally, ttinl() can sense the parity of incoming packets. If it does this, then it should set the global variable ttprty accordingly. ttinl() should be coded to be as efficient as possible, since it is at the "inner loop" of packet reception. ttinl() returns: -1: Timeout or other possibly correctable error. -2: Interrupted from keyboard. -3: Uncorrectable i/o error -- connection lost, configuration problem, etc. >=0: on success, the number of characters that were actually read and placed in the dest buffer, not counting the trailing null. int ttoc(c) char c; Outputs the character c to the communication line. If the operation fails to complete within two seconds, this function returns -1. Otherwise it returns the number of characters actually written to the tty (0 or 1). This function should only be used for interactive, character-mode operations, like terminal connection, script execution, dialer i/o, where the overhead of the signals and alarms does not create a bottleneck. (THIS DESCRIPTION NEEDS IMPROVEMENT -- If the operation fails within a "certain amount of time"... which might be dependent on the communication method, speed, etc. In particular, flow-control deadlocks must be accounted for and broken out of to prevent the program from hanging indefinitely, etc.) int ttol(s,n) int n; char *s; Kermit's packet writer. Writes the n characters of the string pointed to to by s. NOTE: It is ttol's responsibility to write ALL of the characters, not just some of them. Returns: -1: on a possibly correctable error (so it can be retried). -3: on a fatal error, e.g. connection lost. >=0: on success, the actual number of characters written (the specific number is not actually used for anything). int ttopen(ttname,lcl,modem,timo) char *ttname; int *lcl, modem, timo; Opens a tty device, if it is not already open. ttopen must check to make sure the SAME device is not already open; if it is, ttopen returns successfully without doing anything. If a DIFFERENT device is currently open, ttopen() must call ttclos() to close it before opening the new one. Parameters: ttname: character string - device name or network host name. lcl: If called with lcl < 0, sets value of lcl as follows: 0: the terminal named by ttname is the job's controlling terminal. 1: the terminal named by ttname is not the job's controlling terminal. If the device is already open, or if the requested device can't be opened, then lcl remains (and is returned as) -1. modem: Less than zero: this is the negative of the network type, and ttname is a network host name. Network types (from [134]ckcnet.h: NET_TCPB 1 TCP/IP Berkeley (socket) (implemented in [135]ckutio.c) NET_TCPA 2 TCP/IP AT&T (streams) (not yet implemented) NET_DEC 3 DECnet (not yet implemented) Zero or greater: ttname is a terminal device name. Zero means a direct connection (don't use modem signals). Positive means use modem signals depending on the current setting of ttcarr (see ttscarr()). timo: > 0: number of seconds to wait for open() to return before timing out. <=0: no timer, wait forever (e.g. for incoming call). For real tty devices, ttopen() attempts to gain exclusive access to the tty device, for example in UNIX by creating a "lockfile" (in other operating systems, like VMS, exclusive access probably requires no special action). Side effects: Copies its arguments and the tty file descriptor to global variables that are available to the other tty-related functions, with the lcl value altered as described above. Gets all parameters and settings associated with the line and puts them in a global area, so that they can be restored by ttres(), e.g. when the device is closed. Returns: 0: on success -5: if device is in use -4: if access to device is denied -3: if access to lock mechanism denied -2: upon timeout waiting for device to open -1: on other error int ttpkt(speed,flow,parity) long speed; int flow, parity; Puts the currently open tty device into the appropriate modes for transmitting and receiving Kermit packets. Arguments: speed: if speed > -1, and the device is a true tty device, and Kermit is in local mode, ttpkt also sets the speed. flow: if in the range 0-3, ttpkt selects the corresponding type of flow control. Currently 0 is defined as no flow control, 1 is Xon/Xoff, and no other types are defined. If (and this is a horrible hack, but it goes back many years and will be hard to eradicate) flow is 4, then the appropriate tty modes are set for modem dialing, a special case in which we talk to a modem-controlled line without requiring carrier. If flow is 5, then we require carrier. parity: This is simply copied into a global variable so that other functions (like ttinl, ttinc, etc) can use it. Side effects: Copies its arguments to global variables, flushes the terminal device input buffer. Returns: -1: on error. 0: on success. int ttsetflow(int) Enables the given type of flow control on the open serial communications device immediately. Arguments are the FLO_xxx values from ckcdeb.h, except FLO_DIAL, FLO_DIAX, or FLO_AUTO, which are not actual flow-control types. Returns 0 on success, -1 on failure. #ifdef TTSPDLIST long * ttspdlist() Returns a pointer to an array of longs, or NULL on failure. On success, element 0 of the array contains number, n, indicating how many follow. Elements 1-n are serial speeds, expressed in bits per second, that are legal on this platform. The user interface may use this list to construct a menu, keyword table, etc. #endif /* TTSPDLIST */ int ttres() Restores the tty device to the modes and settings that were in effect at the time it was opened (see ttopen). Returns: -1: on error. 0: on success. int ttruncmd(string) char * string; Runs the given command on the local system, but redirects its input and output to the communication (SET LINE, SET PORT, or SET HOST) device. Returns: 0: on failure. 1: on success. int ttscarr(carrier) int carrier; Copies its argument to a variable that is global to the other tty-related functions, and then returns it. The values for carrier are defined in ckcdeb.h: CAR_ON, CAR_OFF, CAR_AUTO. ttopen(), ttpkt(), and ttvt() use this variable when deciding how to open the tty device and what modes to select. The meanings are these: CAR_OFF: Ignore carrier at all times. CAR_ON: Require carrier at all times, except when dialing. This means, for example, that ttopen() could hang forever waiting for carrier if it is not present. CAR_AUTO: If the modem type is zero (i.e. the connection is direct), this is the same as CAR_OFF. If the modem type is positive, then heed carrier during CONNECT (ttvt mode), but ignore it at other times (packet mode, during SET LINE, etc). Compatible with pre-5A versions of C-Kermit. This should be the default carrier mode. Kermit's DIAL command ignores the carrier setting, but ttopen(), ttvt(), and ttpkt() all honor the carrier option in effect at the time they are called. None of this applies to remote mode (the tty device is the job's controlling terminal) or to network host connections (modem type is negative). int ttsndb() Sends a BREAK signal on the tty device. On a real tty device, send a real BREAK lasting approximately 275 milliseconds. If this is not possible, simulate a BREAK by (for example) dropping down some very low baud rate, like 50, and sending a bunch of null characters. On a network connection, do the appropriate network protocol for BREAK. Returns: -1: on error. 0: on success. int ttsndlb() Like ttsndb(), but sends a "Long BREAK" (approx 1.5 seconds). For network connections, it is identical to ttsndb(). Currently, this function is used only if CK_LBRK is defined (as it is for UNIX and VMS). int ttsspd(cps) int cps; For serial devices only, set the device transmission speed to (note carefully) TEN TIMES the argument. The argument is in characters per second, but transmission speeds are in bits per second. cps are used rather than bps because high speeds like 38400 are not expressible in a 16-bit int but longs cannot be used because keyword-table values are ints and not longs. If the argument is 7, then the bps is 75, not 70. If the argument is 888, this is a special code for 75/1200 split-speed operation (75 bps out, 1200 bps in). Returns: -1: on error, meaning the requested speed is not valid or available. >=0: on success (don't try to use this value for anything). int ttvt(speed,flow) long speed; int flow; Puts the currently open tty device into the appropriate modes for terminal emulation. The arguments are interpreted as in ttpkt(). Side effects: ttvt() stores its arguments in global variables, and sets a flag that it has been called so that subsequent calls can be ignored so long as the arguments are the same as in the last effective call. Other functions, such as ttopen(), ttclose(), ttres(), ttvt(), etc, that change the tty device in any way must unset this flag. In UNIX Kermit, this flag is called tvtflg. int ttwmdm(mdmsig,timo) int mdmsig, timo; Waits up to timo seconds for all of the given modem signals to appear. mdmsig is a bit mask, in which a bit is on (1) or off (0) according to whether the corresponding signal is to be waited for. These symbols are defined in ckcdeb.h: BM_CTS (bit 0) means wait for Clear To Send BM_DSR (bit 1) means wait for Data Set Ready BM_DCD (bit 2) means wait for Carrier Detect Returns: -3: Not implemented. -2: This line does not have modem control. -1: Timeout: time limit exceeded before all signals were detected. 1: Success. int ttxin(n,buf) int n; CHAR *buf; Reads x characters from the tty device into the specified buf, stripping parity if parity is not none. This call waits forever, there is no timeout. This function is designed to be called only when you know that at least x characters are waiting to be read (as determined, for example, by ttchk()). This function should use the same buffer as ttinc(). int txbufr(timo) int timo; Reads characters into the internal communications input buffer. timo is a timeout interval, in seconds. 0 means no timeout, wait forever. Called by ttinc() (and possibly ttxin() and ttinl()) when the communications input buffer is empty. The buffer should be called ttxbuf[], its length is defined by the symbol TXBUFL. The global variable txbufn is the number of characters available to be read from ttxbuf[], and txbufp is the index of the next character to be read. Should not be called if txbufn > 0, in which case the buffer does not need refilling. This routine returns: -2: Communications disconnect -1: Timeout >=0: A character (0 - 255) On success, the first character that was read, with the variables txbufn and txbufp set appropriately for any remaining characters. NOTE: Currently this routine is used internally only by the UNIX and VMS versions. The aim is to make it available to all versions so there is one single coherent and efficient way of reading from the communications device or network. 4.E.2.6. Miscellaneous system-dependent functions VOID ztime(s) char **s; Returns a pointer, s, to the current date-and-time string in s. This string must be in the fixed-field format associated with the C runtime asctime() function, like: "Sun Sep 16 13:23:45 1973\n" so that callers of this function can extract the different fields. The pointer value is filled in by ztime, and the data it points to is not safe, so should be copied to a safe place before use. ztime() has no return value. As a side effect, this routine can also fill in the following two external variables (which must be defined in the system-dependent modules for each platform): long ztusec: Fraction of seconds of clock time, microseconds. long ztmsec: Fraction of seconds of clock time, milliseconds. If these variables are not set by zstime(), they remain at their initial value of -1L. int gtimer() Returns the current value of the elapsed time counter in seconds (see rtimer), or 0 on any kind of error. #ifdef GFTIMER CKFLOAT gftimer() Returns the current value of the elapsed time counter in seconds, as a floating point number, capable of representing not only whole seconds, but also the fractional part, to the millisecond or microsecond level, whatever precision is available. Requires a function to get times at subsecond precision, as well as floating-point support. That's why it's #ifdef'd. #endif /* GFTIMER */ int msleep(m) int m; Sleeps (pauses, does nothing) for m milliseconds (a millisecond is one thousandth of a second). Returns: -1: on failure. 0: on success. VOID rtimer() Sets the elapsed time counter to zero. If you want to time how long an operation takes, call rtimer() when it starts and gtimer when it ends. rtimer() has no return value. #ifdef GFTIMER VOID rftimer() Sets the elapsed time counter to zero. If you want to time how long an operation takes, call rftimer() when it starts and gftimer when it ends. rftimer() has no return value. Note: rftimer() is to be used with gftimer() and rtimer() is to be used with gtimer(). See the rftimer() description. #endif /* GFTIMER */ int sysinit() Does whatever needs doing upon program start. In particular, if the program is running in any kind of privileged mode, turns off the privileges (see priv_ini()). Returns: -1: on error. 0: on success. int syscleanup() Does whatever needs doing upon program exit. Returns: -1: on error. 0: on success. int psuspend() Suspends the Kermit process, puts it in the background so it can be continued ("foregrounded") later. Returns: -1: if this function is not supported. 0: on success. [ [136]Contents ] [ [137]C-Kermit ] [ [138]Kermit Home ] 4.F. Group F: Network Support As of version 5A, C-Kermit includes support for several networks. Originally, this was just worked into the ttopen(), ttclos(), ttinc(), ttinl(), and similar routines in [139]ckutio.c. But this made it impossible to share this code with non-UNIX versions, like VMS, AOS/VS, OS/2, etc. So as of edit 168, network code has been separated out into its own module and header file, ckcnet.c and ckcnet.h: [140]ckcnet.h: Network-related symbol definitions. [141]ckcnet.c: Network i/o (TCP/IP, X.25, etc), shared by most platforms. [142]cklnet.c: Network i/o (TCP/IP, X.25, etc) specific to Stratus VOS. The routines and variables in these modules fall into two categories: 1. Support for specific network packages like SunLink X.25 and TGV MultiNet, and: 2. support for specific network virtual terminal protocols like CCITT X.3 and TCP/IP Telnet. Category (1) functions are analogs to the tt*() functions, and have names like netopen, netclos, nettinc, etc. Group A-D modules do not (and must not) know anything about these functions -- they continue to call the old Group E functions (ttopen, ttinc, etc). Category (2) functions are protocol specific and have names prefixed by a protocol identifier, like tn for telnet x25 for X.25. ckcnet.h contains prototypes for all these functions, as well as symbol definitions for network types, protocols, and network- and protocol- specific symbols, as well as #includes for the header files necessary for each network and protocol. The following functions are to be provided for networks that do not use normal system i/o (open, read, write, close): int netopen() To be called from within ttopen() when a network connection is requested. Calling conventions and purpose same as Group E ttopen(). int netclos() To be called from within ttclos() when a network connection is being closed. Calling conventions and purpose same as Group E ttclos(). int nettchk() To be called from within ttchk(). Calling conventions and purpose same as Group E ttchk(). int netflui() To be called from within ttflui(). Calling conventions and purpose same as Group E ttflui(). int netbreak() To send a network break (attention) signal. Calling conventions and purpose same as Group E ttsndbrk(). int netinc() To get a character from the network. Calling conventions same as Group E ttsndbrk(). int nettoc() Send a "character" (byte) to the network. Calling conventions same as Group E ttoc(). int nettol() Send a "line" (sequence of bytes) to the network. Calling conventions same as Group E ttol(). Conceivably, some systems support network connections simply by letting you open a device of a certain name and letting you do i/o to it. Others (like the Berkeley sockets TCP/IP library on UNIX) require you to open the connection in a special way, but then do normal i/o (read, write). In such a case, you would use netopen(), but you would not use nettinc, nettoc, etc. VMS TCP/IP products have their own set of functions for all network operations, so in that case the full range of netxxx() functions is used. The technique is to put a test in each corresponding ttxxx() function to see if a network connection is active (or is being requested), test for which kind of network it is, and if necessary route the call to the corresponding netxxx() function. The netxxx() function must also contain code to test for the network type, which is available via the global variable ttnet. [ [143]Contents ] [ [144]C-Kermit ] [ [145]Kermit Home ] 4.F.1. Telnet Protocol (This section needs a great deal of updating...) As of edit 195, Telnet protocol is split out into its own files, since it can be implemented in remote mode, which does not have a network connection: [146]ckctel.h: Telnet protocol symbol definitions. [147]ckctel.c: Telnet protocol. The Telnet protocol is supported by the following variables and routines: int tn_init Nonzero if telnet protocol initialized, zero otherwise. int tn_init() Initialize the telnet protocol (send initial options). int tn_sopt() Send a telnet option. int tn_doop() Receive and act on a telnet option from the remote. int tn_sttyp() Send terminal type using telnet protocol. 4.F.2. FTP Protocol (To be filled in...) See the [148]source file 4.F.3. HTTP Protocol (To be filled in...) 4.F.4. X.25 Networks These routines were written SunLink X.25 and have since been adapted to at least on one other: IBM AIXLink/X.25. int x25diag() Reads and prints X.25 diagnostics int x25oobh() X.25 out of band signal handler int x25intr() Sends X.25 interrupt packet int x25reset() Resets X.25 virtual circuit int x25clear() Clear X.25 virtual circuit int x25stat() X.25 status int setqbit() Sets X.25 Q-bit int resetqbit() Resets X.25 Q-bit int x25xin() Reads n characters from X.25 circuit. int x25inl() Read a Kermit packet from X.25 circuit. [ [149]Contents ] [ [150]C-Kermit ] [ [151]Kermit Home ] 4.F.5. Adding New Network Types Example: Adding support for IBM X.25 and Hewlett Packard X.25. First, add new network type symbols for each one. There are already some network types defined for other X.25 packages: NET_SX25 is the network-type ID for SunLink X.25. NET_VX25 is the network-type ID for VOS X.25. So first you should new symbols for the new network types, giving them the next numbers in the sequence, e.g.: #define NET_HX25 11 /* Hewlett-Packard X.25 */ #define NET_IX25 12 /* IBM X.25 */ This is in ckcnet.h. Then we need symbols to say that we are actually compiling in the code for these platforms. These would be defined on the cc command line: -DIBMX25 (for IBM) -DHPX25 (for HP) So we can build C-Kermit versions for AIX and HP-UX both with and without X.25 support (since not all AIX and IBM systems have the needed libraries, and so an executable that was linked with them might no load). Then in ckcnet.h: #ifdef IBMX25 #define ANYX25 #endif /* IBMX25 */ #ifdef HPX25 #define ANYX25 #endif /* HPX25 */ And then use ANYX25 for code that is common to all of them, and IBMX25 or HPX25 for code specific to IBM or HP. It might also happen that some code can be shared between two or more of these, but not the others. Suppose, for example, that you write code that applies to both IBM and HP, but not Sun or VOS X.25. Then you add the following definition to ckcnet.h: #ifndef HPORIBMX25 #ifdef HPX25 #define HPORIBMX25 #else #ifdef IBMX25 #define HPORIBMX25 #endif /* IBMX25 */ #endif /* HPX25 */ #endif /* HPORIBMX25 */ You can NOT use constructions like "#if defined (HPX25 || IBMX25)"; they are not portable. [ [152]Contents ] [ [153]C-Kermit ] [ [154]Kermit Home ] 4.G. Group G: Formatted Screen Support So far, this is used only for the fullscreen local-mode file transfer display. In the future, it might be extended to other uses. The fullscreen display code is in and around the routine screenc() in [155]ckuusx.c. In the UNIX version, we use the curses library, plus one call from the termcap library. In other versions (OS/2, VMS, etc) we insert dummy routines that have the same names as curses routines. So far, there are two methods for simulating curses routines: 1. In VMS, we use the Screen Management Library (SMG), and insert stubs to convert curses calls into SMG calls. 2. In OS/2, we use the MYCURSES code, in which the stub routines actually emit the appropriate escape sequences themselves. Here are the stub routines: int tgetent(char *buf, char *term) Arguments are ignored. Returns 1 if the user has a supported terminal type, 0 otherwise. Sets a global variable (for example, "isvt52" or "isdasher") to indicate the terminal type. VOID move(int row, int col) Sends the escape sequence to position the cursor at the indicated row and column. The numbers are 0-based, e.g. the home position is 0,0. int clear() Sends the escape sequence to clear the screen. int clrtoeol() Sends the escape sequence to clear from the current cursor position to the end of the line. In the MYCURSES case, code must be added to each of the last three routines to emit the appropriate escape sequences for a new terminal type. clearok(curscr), wrefresh() In real curses, these two calls are required to refresh the screen, for example after it was fractured by a broadcast message. These are useful only if the underlying screen management service keeps a copy of the entire screen, as curses and SMG do. C-Kermit does not do this itself. [ [156]Contents ] [ [157]C-Kermit ] [ [158]Kermit Home ] 4.H. Group H: Pseudoterminal Support (To be filled in...) But see: [159]these comments, and the source files [160]ckupty.h and [161]ckupty.c. 4.I. Group I: Security (To be filled in...) Meanwhile, see [162]security.html. [ [163]Contents ] [ [164]C-Kermit ] [ [165]Kermit Home ] APPENDIX I. FILE PERMISSIONS I.1. Format of System-Dependent File Permissions in A-Packets The format of this field (the "," attribute) is interpreted according to the System ID ("." Attribute). For UNIX (System ID = U1), it's the familiar 3-digit octal number, the low-order 9 bits of the filemode: Owner, Group, World, e.g. 660 = read/write access for owner and group, none for world, recorded as a 3-digit octal string. High-order UNIX permission bits are not transmitted. For VMS (System ID = D7), it's a 4-digit hex string, representing the 16-bit file protection WGOS fields (World,Group,Owner,System), in that order (which is the reverse of how they're shown in a directory listing); in each field, Bit 0 = Read, 1 = Write, 2 = Execute, 3 = Delete. A bit value of 0 means permission is granted, 1 means permission is denied. Sample: r-01-00-^A/!FWERMIT.EXE'" s-01-00-^AE!Y/amd/watsun/w/fdc/new/wermit.exe.DV r-02-01-^A]"A."D7""B8#119980101 18:14:05!#8531&872960,$A20B-!7(#512@ #.Y s-02-01-^A%"Y.5! A VMS directory listing shows the file's protection as (E,RWED,RED,RE) which really means (S=E,O=RWED,G=RED,W=RE), which is reverse order from the internal storage, so (RE,RED,RWED,E). Now translate each letter to its corresponding bit: RE=0101, RED=1101, RWED=1111, E=0010 Now reverse the bits: RE=1010, RED=0010, RWED=0000, E=1101 This gives the 16-bit quantity: 1010001000001101 This is the internal representation of the VMS file permission; in hex: A20B as shown in the sample packet above. The VMS format probably would also apply to RSX or any other FILES-11 system. I.2. Handling of Generic Protection To be used when the two systems are different (and/or do not recognize or understand each other's local protection codes). First of all, the book is wrong. This should not be the World protection, but the Owner protection. The other fields should be set according to system defaults (e.g. UNIX umask, VMS default protection, etc), except that no non-Owner field should give more permissions than the Owner field. [ [166]Top ] [ [167]Contents ] [ [168]C-Kermit Home ] [ [169]Kermit Home ] __________________________________________________________________ C-Kermit Program Logic Manual / [170]The Kermit Project / [171]kermit@columbia.edu / 30 June 2011 References 1. http://www.columbia.edu/ 2. mailto:kermit@columbia.edu 3. http://www.columbia.edu/kermit/index.html 4. http://www.columbia.edu/kermit/k95.html 5. http://www.columbia.edu/kermit/ckermit.html 6. http://www.columbia.edu/kermit/ckscripts.html 7. http://www.columbia.edu/kermit/current.html 8. http://www.columbia.edu/kermit/whatsnew.html 9. http://www.columbia.edu/kermit/ckfaq.html 10. http://www.columbia.edu/kermit/support.html 11. http://www.columbia.edu/kermit/ 12. http://www.columbia.edu/kermit/ckcplm.html 13. http://www.columbia.edu/kermit/ckcplm.html#x1 14. http://www.columbia.edu/kermit/ckcplm.html#x2 15. http://www.columbia.edu/kermit/ckcplm.html#x3 16. http://www.columbia.edu/kermit/ckcplm.html#x4 17. http://www.columbia.edu/kermit/ckcplm.html#x4.A 18. http://www.columbia.edu/kermit/ckcplm.html#x4.B 19. http://www.columbia.edu/kermit/ckcplm.html#x4.C 20. http://www.columbia.edu/kermit/ckcplm.html#x4.D 21. http://www.columbia.edu/kermit/ckcplm.html#x4.E 22. http://www.columbia.edu/kermit/ckcplm.html#x4.F 23. http://www.columbia.edu/kermit/ckcplm.html#x4.G 24. http://www.columbia.edu/kermit/ckcplm.html#x4.H 25. http://www.columbia.edu/kermit/ckcplm.html#x4.I 26. http://www.columbia.edu/kermit/ckcplm.html#xa1 27. http://www.amazon.com/gp/product/0932376886?ie=UTF8&tag=aleidmoreldom-20&linkCode=as2&camp=1789&creative=9325&creativeASIN=0932376886 28. http://www.columbia.edu/kermit/ckcplm.html#contents 29. http://www.columbia.edu/kermit/vax_11-750.jpg 30. http://www.columbia.edu/cu/computinghistory/hermit.html 31. http://www.columbia.edu/kermit/cudocs/ilosetup.html 32. ftp://kermit.columbia.edu/kermit/f/ckc04e.txt 33. ftp://kermit.columbia.edu/kermit/f/ckc04f.txt 34. ftp://kermit.columbia.edu/kermit/f/ckc168.txt 35. ftp://kermit.columbia.edu/kermit/f/ckc178.txt 36. ftp://kermit.columbia.edu/kermit/f/ckc188.txt 37. ftp://kermit.columbia.edu/kermit/f/ckc189.txt 38. ftp://kermit.columbia.edu/kermit/f/ckc192.txt 39. ftp://kermit.columbia.edu/kermit/f/ckc197.txt 40. ftp://kermit.columbia.edu/kermit/f/ckc200.txt 41. ftp://kermit.columbia.edu/kermit/f/ckc211.txt 42. ftp://kermit.columbia.edu/kermit/f/ckc300.txt 43. http://www.columbia.edu/kermit/ckcplm.html#contents 44. http://www.columbia.edu/kermit/ckermit.html 45. http://www.columbia.edu/kermit/index.html 46. ftp://kermit.columbia.edu/kermit/c-kermit/ckcpro.w 47. http://www.columbia.edu/kermit/ckcplm.html#contents 48. http://www.columbia.edu/kermit/ckermit.html 49. http://www.columbia.edu/kermit/index.html 50. http://www.columbia.edu/kermit/ckcplm.html#x3.2 51. http://www.columbia.edu/kermit/ckcplm.html#contents 52. http://www.columbia.edu/kermit/ckermit.html 53. http://www.columbia.edu/kermit/index.html 54. http://www.columbia.edu/kermit/ckcplm.html#x4.A 55. http://www.columbia.edu/kermit/ckcplm.html#contents 56. http://www.columbia.edu/kermit/ckermit.html 57. http://www.columbia.edu/kermit/index.html 58. http://www.columbia.edu/kermit/ckcplm.html#contents 59. http://www.columbia.edu/kermit/ckermit.html 60. http://www.columbia.edu/kermit/index.html 61. http://www.columbia.edu/kermit/ckcplm.html#contents 62. http://www.columbia.edu/kermit/ckermit.html 63. http://www.columbia.edu/kermit/index.html 64. ftp://kermit.columbia.edu/kermit/c-kermit/ckclib.h 65. ftp://kermit.columbia.edu/kermit/c-kermit/ckclib.c 66. http://www.columbia.edu/kermit/ckcplm.html#x3.1 67. http://www.columbia.edu/kermit/ckcplm.html#contents 68. http://www.columbia.edu/kermit/ckermit.html 69. http://www.columbia.edu/kermit/index.html 70. ftp://kermit.columbia.edu/kermit/c-kermit/ckcsym.h 71. ftp://kermit.columbia.edu/kermit/c-kermit/ckcasc.h 72. ftp://kermit.columbia.edu/kermit/c-kermit/ckcsig.h 73. ftp://kermit.columbia.edu/kermit/c-kermit/ckcdeb.h 74. ftp://kermit.columbia.edu/kermit/c-kermit/ckcker.h 75. ftp://kermit.columbia.edu/kermit/c-kermit/ckcxla.h 76. ftp://kermit.columbia.edu/kermit/c-kermit/ckcmai.c 77. ftp://kermit.columbia.edu/kermit/c-kermit/ckcpro.w 78. ftp://kermit.columbia.edu/kermit/c-kermit/ckcfns.c 79. ftp://kermit.columbia.edu/kermit/c-kermit/ckcfn2.c 80. ftp://kermit.columbia.edu/kermit/c-kermit/ckcfn3.c 81. http://www.columbia.edu/kermit/ckcplm.html#x4.B 82. http://www.columbia.edu/kermit/ckcplm.html#x4.E 83. http://www.columbia.edu/kermit/ckcplm.html#x4.D 84. http://www.columbia.edu/kermit/ckcplm.html#contents 85. http://www.columbia.edu/kermit/ckermit.html 86. http://www.columbia.edu/kermit/index.html 87. http://www.columbia.edu/kermit/ckcplm.html#x4.B 88. ftp://kermit.columbia.edu/kermit/c-kermit/ckuxla.c 89. ftp://kermit.columbia.edu/kermit/c-kermit/ckuxla.h 90. ftp://kermit.columbia.edu/kermit/c-kermit/ckcxla.h 91. ftp://kermit.columbia.edu/kermit/c-kermit/ckuxla.h 92. ftp://kermit.columbia.edu/kermit/c-kermit/ckmxla.h 93. ftp://kermit.columbia.edu/kermit/c-kermit/ck?xla 94. ftp://kermit.columbia.edu/kermit/c-kermit/ckcuni.h 95. ftp://kermit.columbia.edu/kermit/c-kermit/ckcuni.c 96. http://www.columbia.edu/kermit/ckcplm.html#contents 97. http://www.columbia.edu/kermit/ckermit.html 98. http://www.columbia.edu/kermit/index.html 99. http://www.columbia.edu/kermit/ckcplm.html#x4.B 100. ftp://kermit.columbia.edu/kermit/c-kermit/ckucmd.h 101. ftp://kermit.columbia.edu/kermit/c-kermit/ckucmd.c 102. http://www.columbia.edu/kermit/ckcplm.html#x4.E 103. ftp://kermit.columbia.edu/kermit/c-kermit/ckuusr.h 104. ftp://kermit.columbia.edu/kermit/c-kermit/ckuusr.c 105. ftp://kermit.columbia.edu/kermit/c-kermit/ckuus2.c 106. ftp://kermit.columbia.edu/kermit/c-kermit/ckuus3.c 107. ftp://kermit.columbia.edu/kermit/c-kermit/ckuus4.c 108. ftp://kermit.columbia.edu/kermit/c-kermit/ckuusy.c 109. ftp://kermit.columbia.edu/kermit/c-kermit/ckuusx.c 110. ftp://kermit.columbia.edu/kermit/c-kermit/ckuver.h 111. ftp://kermit.columbia.edu/kermit/c-kermit/ckuscr.c 112. ftp://kermit.columbia.edu/kermit/c-kermit/ckudia.c 113. ftp://kermit.columbia.edu/kermit/c-kermit/ckucon.c 114. ftp://kermit.columbia.edu/kermit/c-kermit/ckucns.c 115. http://www.columbia.edu/kermit/ckcplm.html#x4.E 116. ftp://kermit.columbia.edu/kermit/c-kermit/ckcmai.c 117. http://www.columbia.edu/kermit/ckcplm.html#contents 118. http://www.columbia.edu/kermit/ckermit.html 119. http://www.columbia.edu/kermit/index.html 120. ftp://kermit.columbia.edu/kermit/c-kermit/ckufio.c 121. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c 122. ftp://kermit.columbia.edu/kermit/c-kermit/ckusig.c 123. ftp://kermit.columbia.edu/kermit/c-kermit/ckvfio.c 124. ftp://kermit.columbia.edu/kermit/c-kermit/ckusig.c 125. ftp://kermit.columbia.edu/kermit/c-kermit/ckcmai.c 126. http://www.columbia.edu/kermit/ckcplm.html#contents 127. http://www.columbia.edu/kermit/ckermit.html 128. http://www.columbia.edu/kermit/index.html 129. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c 130. ftp://kermit.columbia.edu/kermit/c-kermit/ckvtio.c 131. http://www.columbia.edu/kermit/ckcplm.html#x2 132. http://www.columbia.edu/kermit/ckcplm.html#xa1 133. http://www.columbia.edu/kermit/ckuins.html 134. ftp://kermit.columbia.edu/kermit/c-kermit/ckcnet.h 135. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c 136. http://www.columbia.edu/kermit/ckcplm.html#contents 137. http://www.columbia.edu/kermit/ckermit.html 138. http://www.columbia.edu/kermit/index.html 139. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c 140. ftp://kermit.columbia.edu/kermit/c-kermit/ckcnet.h 141. ftp://kermit.columbia.edu/kermit/c-kermit/ckcnet.c 142. ftp://kermit.columbia.edu/kermit/c-kermit/cklnet.c 143. http://www.columbia.edu/kermit/ckcplm.html#contents 144. http://www.columbia.edu/kermit/ckermit.html 145. http://www.columbia.edu/kermit/index.html 146. ftp://kermit.columbia.edu/kermit/c-kermit/ckctel.h 147. ftp://kermit.columbia.edu/kermit/c-kermit/ckctel.c 148. ftp://kermit.columbia.edu/kermit/c-kermit/ckcftp.c 149. http://www.columbia.edu/kermit/ckcplm.html#contents 150. http://www.columbia.edu/kermit/ckermit.html 151. http://www.columbia.edu/kermit/index.html 152. http://www.columbia.edu/kermit/ckcplm.html#contents 153. http://www.columbia.edu/kermit/ckermit.html 154. http://www.columbia.edu/kermit/index.html 155. ftp://kermit.columbia.edu/kermit/c-kermit/ckuusx.c 156. http://www.columbia.edu/kermit/ckcplm.html#contents 157. http://www.columbia.edu/kermit/ckermit.html 158. http://www.columbia.edu/kermit/index.html 159. http://www.columbia.edu/kermit/ckermit90.html#LooseEnd 160. ftp://kermit.columbia.edu/kermit/f/ckupty.h 161. ftp://kermit.columbia.edu/kermit/f/ckupty.c 162. http://www.columbia.edu/kermit/security.html 163. http://www.columbia.edu/kermit/ckcplm.html#contents 164. http://www.columbia.edu/kermit/ckermit.html 165. http://www.columbia.edu/kermit/index.html 166. http://www.columbia.edu/kermit/ckcplm.html#top 167. http://www.columbia.edu/kermit/ckcplm.html#contents 168. http://www.columbia.edu/kermit/ckermit.html 169. http://www.columbia.edu/kermit/index.html 170. http://www.columbia.edu/kermit/index.html 171. mailto:kermit@columbia.edu ckccfg.txt0000664000015300001460000023263611624230415011656 0ustar fdckermit [1]The Columbia Crown The Kermit Project | Columbia University 612 West 115th Street, New York NY 10025 USA o [2]kermit@columbia.edu ...since 1981 [3]Home [4]Kermit 95 [5]C-Kermit [6]Scripts [7]Current [8]New [9]FAQ [10]Support C-Kermit Configuration Options As of: C-Kermit 9.0.300, 30 June 2011 This page last updated: Fri Jul 1 15:48:21 2011 (New York USA Time) IF YOU ARE READING A PLAIN-TEXT version of this document, note that this file is a plain-text dump of a Web page. You can visit the original (and possibly more up-to-date) Web page here: [11]http://www.columbia.edu/kermit/ckccfg.html [ [12]C-Kermit Home ] [ [13]Kermit Home ] CONTENTS 1. [14]FILE TRANSFER 2. [15]SERIAL COMMUNICATION SPEEDS 3. [16]FULLSCREEN FILE TRANSFER DISPLAY 4. [17]CHARACTER SETS 5. [18]APC EXECUTION 6. [19]PROGRAM SIZE 7. [20]MODEM DIALING 8. [21]NETWORK SUPPORT 9. [22]EXCEPTION HANDLING 10. [23]SECURITY FEATURES 11. [24]ENABLING SELECT() 12. [25]I/O REDIRECTION 13. [26]FLOATING-POINT NUMBERS, TIMERS, AND ARITHMETIC 14. [27]SPECIAL CONFIGURATIONS I. [28]SUMMARY OF COMPILE-TIME OPTIONS OVERVIEW This document describes configuration options for C-Kermit (5A and later). The major topics covered include program size (and how to reduce it), how to include or exclude particular features, notes on serial-port, modem, and network support, and a list of C-Kermit's compile-time options. For details about your particular operating system, also see the system-specific installation instructions file, such as the [29]C-Kermit Installation Instructions for Unix. [ [30]C-Kermit Home ] [ [31]Kermit Home ] 1. FILE TRANSFER [ [32]Top ] [ [33]Contents ] [ [34]Next ] [ [35]Previous ] Prior to version 7.0, C-Kermit was always built with the most conservative Kermit file-transfer protocol defaults on every platform: no control-character prefixing, 94-byte packets, and a window size of 1. Starting in version 7.0, fast settings are the default. To override these at compile time, include: -DNOFAST in the C compiler CFLAGS. Even with the fast defaults, C-Kermit automatically drops down to whatever window and packet sizes requested by the other Kermit, if these are smaller, when sending files (except for control-character unprefixing, which is not negotiated, and which is now set to CAUTIOUS rather than NONE at startup). C-Kermit's settings prevail when it is receiving. [ [36]C-Kermit Home ] [ [37]Kermit Home ] 2. SERIAL COMMUNICATION SPEEDS [ [38]Top ] [ [39]Contents ] [ [40]Next ] [ [41]Previous ] As of 6 September 1997, a new simplified mechanism for obtaining the list of legal serial interface speeds is in place: * If the symbol TTSPDLIST is defined, the system-dependent routine ttspdlist() is called at program initialization to obtain the list. * This symbol should be defined only for C-Kermit implementations that have implemented the ttspdlist() function, typically in the ck?tio.c module. See [42]ckutio.c for an example. * TTSPDLIST is automatically defined in [43]ckcdeb.h for UNIX. Add the appropriate #ifdefs for other platforms when the corresponding ttspdlist() functions are filled in. * If TTSPDLIST is (or normally would be) defined, the old code (described below) can still be selected by defining NOTTSPDLIST. The ttspdlist() function can obtain the speeds in any way that works. For example, based simply on #ifdef Bnnnn..#endif (in UNIX). Although it might be better to actually check each speed against the currently selected hardware interface before allowing it in the array, there is usually no passive and/or reliable and safe way to do this, and so it's better to let some speeds into the array that might not work, than it is to erroneously exclude others. Speeds that don't work are caught when the SET SPEED command is actually given. Note that this scheme does not necessarily rule out split speed operation, but effectively it does in C-Kermit as presently constituted since there are no commands to set input and output speed separately (except the special case "set speed 75/1200"). Note that some platforms, notably AIX 4.2 and 4.3, implement high serial speeds transparently to the application, e.g. by mapping 50 bps to 57600 bps, and so on. That's the whole deal. When TTSPDLIST is not defined, the following applies: Speeds are defined in two places: the SET SPEED keyword list in the command parser (as of this writing, in the [44]ckuus3.c source file), and in the system- dependent communications i/o module, ck?tio.c, functions ttsspd() (set speed) and ttgspd() (get speed). The following speeds are assumed to be available in all versions: 0, 110, 300, 600, 1200, 2400, 4800, 9600 If one or more of these speeds is not supported by your system, you'll need to change the source code (this has never happened so far). Other speeds that are not common to all systems have Kermit-specific symbols: Symbol Symbol Speed (bps) to enable to disable 50 BPS_50 NOB_50 75 BPS_75 NOB_75 75/1200 BPS_7512 NOB_7512 134.5 BPS_134 NOB_134 150 BPS_150 NOB_150 200 BPS_200 NOB_200 1800 BPS_1800 NOB_1800 3600 BPS_3600 NOB_3600 7200 BPS_7200 NOB_7200 14400 BPS_14K NOB_14K 19200 BPS_19K NOB_19K 28800 BPS_28K NOB_28K 38400 BPS_38K NOB_38K 57600 BPS_57K NOB_57K 76800 BPS_76K NOB_76K 115200 BPS_115K NOB_155K 230400 BPS_230K NOB_230K 460800 BPS_460K NOB_460K 921600 BPS_921K NOB_921K The [45]ckcdeb.h header file contains default speed configurations for the many systems that C-Kermit supports. You can override these defaults by (a) editing ckcdeb.h, or (b) defining the appropriate enabling and/or disabling symbols on the CC command line, for example: -DBPS_14400 -DNOB_115200 or the "make" command line, e.g.: make blah "KFLAGS=-DBPS_14400 -DNOB_115200" Note: some speeds have no symbols defined for them, because they have never been needed: 12.5bps, 45.5bps, 20000bps, etc. These can easily be added if required (but they will work only if the OS supports them). IMPORTANT: Adding one of these flags at compile time does not necessarily mean that you will be able to use that speed. A particular speed is usable only if your underlying operating system supports it. In particular, it needs to be defined in the appropriate system header file (e.g. in UNIX, cd to /usr/include and grep for B9600 in *.h and sys/*.h to find the header file that contains the definitions for the supported speeds), and supported by the serial device driver, and of course by the physical device itself. ALSO IMPORTANT: The list of available speeds is independent of how they are set. The many UNIXes, for example, offer a wide variety of APIs that are BSD-based, SYSV-based, POSIX-based, and purely made up. See the ttsspd(), ttgspd(), and ttspdlist() routines in [46]ckutio.c for illustrations. The latest entries in this horserace are the tcgetspeed() and ttsetspeed() routines found in UnixWare 7. Unlike other methods, they accept the entire range of integers (longs really) as speed values, rather than certain codes, and return an error if the number is not, in fact, a legal speed for the device/driver in question. In this case, there is no way to build a list of legal speeds at compile time, since no Bnnnn symbols are defined (except for "deprecated, legacy" interfaces like ioctl()) and so the legal speed list must be enumerated in the code -- see ttspdlist() in [47]ckutio.c. [ [48]C-Kermit Home ] [ [49]Kermit Home ] 3. FULLSCREEN FILE TRANSFER DISPLAY [ [50]Top ] [ [51]Contents ] [ [52]Next ] [ [53]Previous ] New to edit 180 is support for an MS-DOS-Kermit-like local-mode full screen file transfer display, accomplished using the curses library, or something equivalent (for example, the Screen Manager on DEC VMS). To enable this feature, include the following in your CFLAGS: -DCK_CURSES and then change your build procedure (if necessary) to include the necessary libraries. For example, in Unix these are usually "curses" or "ncurses" (and more recently, "ncursesw" and "slang"), perhaps also "termcap", "termlib", or "tinfo": "LIBS= -lcurses -ltermcap" "LIBS= -lcurses -ltermlib" "LIBS= -lncurses" "LIBS= -ltermlib" "LIBS= -ltinfo" "man curses" for further information, and search through the Unix [54]makefile for "CK_CURSES" to see many examples, and also see the relevant sections of the [55]Unix C-Kermit Installation Instructions, particularly Sections [56]4 and [57]9.2. There might still be a complication. Some implementations of curses reserve the right to alter the buffering on the output file without restoring it afterwards, which can leave Kermit's command processing in a mess when the prompt comes back after a fullscreen file transfer display. The typical symptom is that characters you type at the prompt after a local-mode file transfer (i.e. after seeing the curses file-transfer display) do not echo until you press the Return (Enter) key. If this happens to you, try adding -DCK_NEWTERM to your makefile target (see comments in screenc() in [58]ckuusx.c for an explanation). If that doesn't fix the problem, then use a bigger hammer and replace -DCK_NEWTERM with: -DNONOSETBUF which tells Kermit to force stdout to be unbuffered so CBREAK mode can work. In SCO Xenix and SCO UNIX, there are two separate curses libraries, one based on termcap and the other based on terminfo. The default library, usually terminfo, is established when the development system is installed. To manually select terminfo (at compile time): compile -DM_TERMINFO and link -ltinfo and to manually select termcap: compile -DM_TERMCAP and link -ltcap -ltermlib looks at M_TERMINFO and M_TERMCAP to decide which header files to use. /usr/lib/libcurses.a is a link to either libtinfo.a or libtcap.a. The C-Kermit compilation options must agree with the version of the curses library that is actually installed. NOTE: If you are doing an ANSI-C compilation and you get compile time warnings like the following: Warning: function not declared in ckuusx.c: wmove, printw, wclrtoeol, wclear, wrefresh, endwin, etc... it means that your file does not contain prototypes for these functions. The warnings should be harmless. New to edit 190 is the ability to refresh a messed-up full-screen display, e.g. after receiving a broadcast message. This depends on the curses package including the wrefresh() and clearok() functions and the curscr variable. If your version has these, or has code to simulate them, then add: -DCK_WREFRESH The curses and termcap libraries add considerable size to the program image (e.g. about 20K on a SUN-4, 40K on a 386). On some small systems, such as the AT&T 6300 PLUS, curses can push Kermit over the edge... even though it compiles, loads, and runs correctly, its increased size apparently makes it swap constantly, slowing it down to a crawl, even when the curses display is not in use. Some new makefile targets have been added to take care of this (e.g. sys3upcshcc), but similar tricks might be necessary in other cases too. On the curses file-transfer display, just below the "thermometer", is a running display of the transfer rate, as a flat quotient of file characters per elapsed seconds so far. You can change this to an average that gives greater weight to recent history (0.25 * instantaneous cps + 0.75 * historical cps) by adding -DCPS_WEIGHTED to your CFLAGS (sorry folks, this one is not worth a SET command). You can choose a second type of weighted average in which the weighting smooths out progressively as the transfer progresses by adding -DCPS_VINCE to -DCPS_WEIGHTED. An alternative to curses is also available at compile time, but should be selected if your version of Kermit is to be run in local mode only in an ANSI terminal environment, for example on a desktop workstation that has an ANSI console driver. To select this option in place of curses, define the symbol MYCURSES: -DMYCURSES instead of CK_CURSES. The MYCURSES option uses built-in ANSI (VT100) escape sequences, and depends upon your terminal or console driver to interpret them correctly. In some C-Kermit builds, we replace printf() via #define printf... However, this can cause conflicts with the [n]curses header files. Various hacks are required to get around this -- see [59]ckutio.c, [60]ckufio.c, [61]ckuusx.c, [62]ckucmd.c, etc. [ [63]C-Kermit Home ] [ [64]Kermit Home ] 4. CHARACTER SETS [ [65]Top ] [ [66]Contents ] [ [67]Next ] [ [68]Previous ] Since version 5A, C-Kermit has included support for conversion of character sets for Western European languages (i.e. languages that originated in Western Europe, but are now also spoken in the Western Hemisphere and other parts of the world), via ISO 8859-1 Latin Alphabet 1, for Eastern European languages (ISO Latin-2), Hebrew (and Yiddish), Greek, and Cyrillic-alphabet languages (ISO Latin/Cyrillic). Many file (local) character sets are supported: ISO 646 7-bit national sets, IBM code pages, Apple, DEC, DG, NeXT, etc. To build Kermit with no character-set translation at all, include -DNOCSETS in the CFLAGS. To build with no Latin-2, add -DNOLATIN2. To build with no Cyrillic, add -DNOCYRIL. To omit Hebrew, add -DNOHEBREW. If -DNOCSETS is *not* included, you'll always get LATIN1. To build with no KANJI include -DNOKANJI. There is presently no way to include Latin-2, Cyrillic, Hebrew, or Kanji without also including Latin-1. [69]Unicode support was added in C-Kermit 7.0, and it adds a fair amount of tables and code (and this is only a "Level 1" implementation -- a higher level would also require building in the entire Unicode database). On a PC with RH 5.2 Linux, building C-Kermit 7.0, we get the following sizes: NOCSETS NOUNICODE NOKANJI Before After [ ] [ ] [ ] 1329014 (Full) [ ] [ ] [ X ] 1325686 (Unicode but no Kanji) [ ] [ X ] [ ] 1158837 (All charsets except Unicode) [ X ] [ x ] [ x ] 1090845 (NOCSETS implies the other two) Note, by the way, that NOKANJI without NOUNICODE only removes the non-Unicode Kanji sets (Shift-JIS, EUC-JP, JIS-7, etc). Kanji is still representable in UCS-2 and UTF-8. [ [70]C-Kermit Home ] [ [71]Kermit Home ] 5. APC EXECUTION [ [72]Top ] [ [73]Contents ] [ [74]Next ] [ [75]Previous ] The Kermit CONNECT and INPUT commands are coded to execute Application Program Command escape sequences from the host: _\ where is a C-Kermit command, or a list of C-Kermit commands separated by commas, up to about 1K in length. To date, this feature has been included in the OS/2, Windows, VMS, OS-9, and Unix versions, for which the symbol: CK_APC is defined automatically in [76]ckuusr.h. For OS/2, APC is enabled at runtime by default, for UNIX it is disabled. It is controlled by the SET TERMINAL APC command. Configuring APC capability into a version that gets it by default (because CK_APC is defined in [77]ckuusr.h) can be overridden by including: -DNOAPC on the CC command line. C-Kermit's autodownload feature depends on the APC feature, so deconfiguring APC also disables autodownload (it doesn't use APC escape sequences, but uses the APC switching mechanism internally). [ [78]C-Kermit Home ] [ [79]Kermit Home ] 6. PROGRAM SIZE [ [80]Top ] [ [81]Contents ] [ [82]Next ] [ [83]Previous ] SECTION CONTENTS 6.1. [84]Feature Selection 6.2. [85]Changing Buffer Sizes 6.3. [86]Other Size-Related Items 6.4. [87]Space/Time Tradeoffs (Also see [88]Section 4) Each release of C-Kermit is larger than the last. On some computers (usually old ones) the size of the program prevents it from being successfully linked and loaded. On some others (also usually old ones), it occupies so much memory that it is constantly swapping or paging. In such cases, you can reduce C-Kermit's size in various ways, outlined in this section. The following options can cut down on the program's size at compile time by removing features or changing the size of storage areas. If you are reading this section because all you want is a small, fast, quick-to-load Kermit file-transfer application for the remote end of your connection, and the remote end is Unix based, take a look at G-Kermit: [89]http://www.columbia.edu/kermit/gkermit.html 6.1. Feature Selection Features can be added or removed by defining symbols on the CC (C compiler) command line. "-D" is the normal CC directive to define a symbol so, for example, "-DNODEBUG" defines the symbol NODEBUG. Some C compilers might use different syntax, e.g. "-d NODEBUG" or "/DEFINE=NODEBUG". For C compilers that do not accept command-line definitions, you can put the corresponding #define statements in the file ckcsym.h, for example: #define NODEBUG The following table shows the savings achieved when building C-Kermit 8.0 (Beta.04) with selected feature-deselection switches on an Intel-based PC with Red Hat Linux 7.0 and gcc 2.96. The sizes are for non-security builds. The fully configured non-security build is 2127408 bytes. Option Size Savings Effect NOICP 545330 74.4% No Interactive Command Parser (command-line only) NOLOCAL 1539994 27.6% No making connections. NOXFER 1551108 27.1% No file transfer. IKSDONLY 1566608 26.4% Internet Kermit Server only. NOCSETS 1750097 17.7% No character-set conversion. NOSPL 1800293 15.4% No Script Programming Language. NONET 1808575 15.0% No making network connections. NOUNICODE 1834426 13.8% No Unicode character-set conversion. NOHELP 1837877 13.6% No built-in help text. NODEBUG 1891669 11.1% No debug log. NOFRILLS 1918966 9.8% No "frills". NOFTP 1972496 7.3% No FTP client. NODIAL 1984488 6.7% No automatic modem dialing. NOPUSH 2070184 2.7% No shell access, running external programs, etc. NOIKSD 2074129 2.5% No Internet Kermit Server capability. NOHTTP 2082610 2.1% No HTTP client. NOFLOAT 2091332 1.7% No floating-point arithmetic. NOCHANNELIO 2095978 1.5% No FOPEN/FREAD/FWRITE/FCLOSE, etc. MINIDIAL 2098035 1.4% No built-in support for many kinds of modems. NOSERVER 2098987 1.3% No server mode. NOSEXP 2105898 1.0% No S-Expressions. NOPTY 2117743 0.5% No pseudoterminal support. NORLOGIN 2121089 0.3% No RLOGIN connections. NOOLDMODEMS 2124038 0.2% No built-in support for old kinds of modems. NOSSH 2125696 0.1% No SSH command. And here are a few combinations Options Size Savings Effect NODEBUG NOICP NOCSETS NOLOCAL 281641 86.7% No debug log, parser, character sets, or making connections. NOICP NOCSETS NOLOCAL 376468 82.3% No parser, character sets, or making connections. NOICP NOCSETS NONET 427510 79.9% No parser, character sets, or network connections. NOSPL NOCSETS 1423784 33.1% No script language, or character sets. -DNOFRILLS removes various command synonyms; the following top-level commands: CLEAR, DELETE, DISABLE, ENABLE, GETOK, MAIL, RENAME, TYPE, WHO; and the following REMOTE commands: KERMIT, LOGIN, LOGOUT, PRINT, TYPE, WHO. 6.2. Changing Buffer Sizes Most modern computers have so much memory that (a) there is no need to scrimp and save, and (b) C-Kermit, even when fully configured, is relatively small by today's standards. Two major factors affect Kermit's size: feature selection and buffer sizes. Buffer sizes affect such things as the maximum length for a Kermit packet, the maximum length for a command, for a macro, for the name of a macro, etc. Big buffer sizes are used when the following symbol is defined: BIGBUFOK as it is by default for most modern platforms (Linux, AIX 4 and 5, HP-UX 10 and 11, Solaris, etc) in [90]ckuusr.h. If your build does not get big buffers automatically (SHOW FEATURES tells you), you can include them by rebuilding with BIGBUFOK defined; e.g. in Unix: make xxxx KFLAGS=-DBIGBUFOK where xxxx is the makefile target. On the other hand, if you want to build without big buffers when they normally would be selected, use: make xxxx KFLAGS=-DNOBIGBUF There are options to control Kermit's packet buffer allocations. The following symbols are defined in [91]ckcker.h in such a way that you can override them by redefining them in CFLAGS: -DMAXSP=xxxx - Maximum send-packet length. -DMAXRP=xxxx - Maximum receive-packet length. -DSBSIZ=xxxx - Total allocation for send-packet buffers. -DRBSIZ=xxxx - Total allocation for receive-packet buffers. The defaults depend on the platform. Using dynamic allocation (-DDYNAMIC) reduces storage requirements for the executable program on disk, and allows more and bigger packets at runtime. This has proven safe over the years, and now most builds (e.g. all Unix, VMS, Windows, and OS/2 ones) use dynamic memory allocation by default. If it causes trouble, however, then omit the -DDYNAMIC option from CFLAGS, or add -DNODYNAMIC. 6.3. Other Size-Related Items To make Kermit compile and load successfully, you might have to change your build procedure to: a. Request a larger ("large" or "huge") compilation / code-generation model. This is needed for 16-bit PC-based UNIX versions (most or all of which fail to build C-Kermit 7.0 and later anyway). This is typically done with a -M and/or -F switch (see your cc manual or man page for details). b. Some development systems support overlays. If the program is too big to be built as is, check your loader manual ("man ld") to see if an overlay feature is available. See the 2.10/2.11 BSD example in the UNIX makefile. (Actually, as of version 7.0, C-Kermit is too big to build, period, even with overlays, on 2.xx BSD). c. Similarly, some small and/or segment-based architectures support "code mapping", which is similar to overlays (PDP11-based VENIX 1.0, circa 1984, was an example). See the linker documentation on the affected platform. It is also possible to reduce the size of the executable program file in several other ways: a. Include the -O (optimize) compiler switch if it isn't already included in your "make" entry (and if it works!). If your compiler supports higher levels of optimization (e.g. -O2 or higher number, -Onolimit (HP-UX), etc), try them; the greater the level of optimization, the longer the compilation and more likely the compiler will run out of memory. The latter eventuality, some compilers also provide command-line options to allocate more memory for the optimizer, like "-Olimit number" in Ultrix. b. If your platform supports shared libraries, change the make entry to take advantage of this feature. The way to do this is, of course, platform dependent; see the NeXT makefile target for an example. some platforms (like Solaris) do it automatically and give you no choice. But watch out: executables linked with shared libraries are less portable than statically linked executables. c. Strip the program image after building ("man strip" for further info), or add -s to the LNKFLAGS (UNIX only). This strips the program of its symbol table and relocation information. d. Move character strings into a separate file. See the 2.11 BSD target for an example. 6.4. Space/Time Tradeoffs There are more than 6000 debug() statements in the program. If you want to save both space (program size) and time (program execution time), include -DNODEBUG in the compilation. If you want to include debugging for tracking down problems, omit -DNODEBUG from the make entry. But when you include debugging, you have two choices for how it's done. One definition defines debug() to be a function call; this is cheap in space but expensive in execution. The other defines debug as "if (deblog)" and then the function call, to omit the function call overhead when the debug log is not active. But this adds a lot of space to the program. Both methods work, take your choice; IFDEBUG is preferred if memory is not a constraint but the computer is likely to be slow. The first method is the default, i.e. if nothing is done to the CFLAGS or in [92]ckcdeb.h (but in some cases, e.g. VMS, it is). To select the second method, include -DIFDEBUG in the compilation (and don't include -DNODEBUG). [ [93]C-Kermit Home ] [ [94]Kermit Home ] 7. MODEM DIALING [ [95]Top ] [ [96]Contents ] [ [97]Next ] [ [98]Previous ] -DNODIAL removes automatic modem dialing completely, including the entire [99]ckudia.c module, plus all commands that refer to dialing in the various ckuus*.c modules. -DMINIDIAL leaves the DIAL and related commands (SET/SHOW MODEM, SET/SHOW DIAL) intact, but removes support for all types of modems except CCITT, Hayes, Unknown, User-defined, Generic-high-speed, and None (= Direct). The MINIDIAL option cuts the size of the dial module approximately in half. Use this option if you have only Hayes or CCITT modems and don't want to carry the baggage for the other types. A compromise between full dialer support and MINIDIAL is obtained by removing support for "old" modems -- all the strange non-Hayes compatible 1200 and 2400 bps modems that C-Kermit has been carrying around since 1985 or so. To remove support for these modems, add -DNOOLDMODEMS to CFLAGS at compilation time. Finally, if you keep support for old modems, you will notice that their names appear on the "set modem ?" menu. That's because their names are, by default, "visible". But the list is confusing to the younger generation, who have only heard of modems from the V.32bis-and-later era. If you want to be able to use old modems, but don't want their names cluttering up menus, add this to CFLAGS: -DM_OLD=1 [ [100]C-Kermit Home ] [ [101]Kermit Home ] 8. NETWORK SUPPORT [ [102]Top ] [ [103]Contents ] [ [104]Next ] [ [105]Previous ] SECTION CONTENTS 8.1. [106]TCP/IP 8.2. [107]X.25 8.3. [108]Other Networks C-Kermit supports not only serial-port and modem connections, but also TCP/IP and X.25 network connections. Some versions support other network types too like DECnet, LAT, NETBIOS, etc. If you define the following symbol: NONET then all network support is compiled away. 8.1. TCP/IP SUBSECTION CONTENTS 8.1.1. [109]Firewalls 8.1.2. [110]Compilation and Linking Problems 8.1.3. [111]Enabling Host Address Lists 8.1.4. [112]Enabling Telnet NAWS 8.1.5. [113]Enabling Incoming TCP/IP Connections 8.1.6. [114]Disabling SET TCP Options C-Kermit's TCP/IP features require the Berkeley sockets library or equivalent, generally available on any Unix system, as well as in Windows 9x/NT, OS/2, VMS, AOS/VS, VOS, etc. The TCP/IP support includes built-in TELNET, FTP, and HTTP protocol. To select TCP/IP support, include -DTCPSOCKET in your makefile target's CFLAGS, or (in VMS) the appropriate variant (e.g. -DWOLLONGONG, -DMULTINET, -DEXCELAN, -DWINTCP, etc). The VMS and/or early Unix third-party TCP/IP products are often incompatible with each other, and sometimes with different versions of themselves. For example, Wollongong reportedly put header files in different directories for different UNIX versions: * in.h can be in either /usr/include/sys or /user/include/netinet. * telnet.h can be in either /usr/include/arpa or /user/include/netinet. * inet.h can be in either /usr/include/arpa or /user/include/sys. In cases like this, use the -I cc command-line option when possible; otherwise it's better to make links in the file system than it is to hack up the C-Kermit source code. Suppose, for example, Kermit is looking for telnet.h in /usr/include/arpa, but on your computer it is in /usr/include/netinet. Do this (as root, or get the system administrator to do it): cd /usr/include/arpa ln /usr/include/netinet/telnet.h telnet.h ("man ln" for details about links.) The network support for TCP/IP and X.25 is in the source files [115]ckcnet.h, [116]ckctel.c, [117]ckctel.c, [118]ckctel.h, [119]ckcftp.c, with miscellaneous SHOW commands, etc, in the various ckuus*.c modules, plus code in the ck*con.c or ckucns.c (CONNECT command) and several other modules to detect TELNET negotiations, etc. Within the TCPSOCKET code, some socket-level controls are included if TCPSOCKET is defined in the C-Kermit CFLAGS and SOL_SOCKET is defined in in the system's TCP-related header files, such as . These are: SET TCP KEEPALIVE SET TCP LINGER SET TCP RECVBUF SET TCP SENDBUF In addition, if TCP_NODELAY is defined, the following command is also enabled: SET TCP NODELAY (Nagle algorithm) See the [120]C-Kermit user documentation for descriptions of these commands. 8.1.1. Firewalls There exist various types of firewalls, set up to separate users of an internal TCP/IP network ("Intranet") from the great wide Internet, but then to let selected users or services get through after all. One firewall method is called SOCKS, in which a proxy server allows users inside a firewall to access the outside world, based on a permission list generally stored in a file. SOCKS is enabled in one of two ways. First, the standard sockets library is modified to handle the firewall, and then all the client applications are relinked (if necessary, i.e. if the libraries are not dynamically loaded) with the modified sockets library. The APIs are all the same, so the applications do not need to be recoded or recompiled. In the other method, the applications must be modified to call replacement routines, such as Raccept() instead of accept(), Rbind() instead of bind(), etc, and then linked with a separate SOCKS library. This second method is accomplished (for SOCKS4) in C-Kermit by including -DCK_SOCKS in your CFLAGS, and also adding: -lsocks to LIBS, or replacing -lsockets with -lsocks (depending on whether the socks library also includes all the sockets entry points). For SOCKS5, use -DCK_SOCKS5. Explicit firewall support can, in general, not be a standard feature or a feature that is selected at runtime, because the SOCKS library tends to be different at each site -- local modifications abound. The ideal situation occurs when firewalls are supported by the first method, using dynamically linked sockets-replacement libraries; in this case, all your TCP/IP client applications negotiate the firewall transparently. 8.1.2. Compilation and Linking Problems If you get a compilation error in [121]ckcnet.c, with a complaint like "incompatible types in assignment", it probably has something to do with the data type your system uses for the inet_addr() function, which is declared (usually) in . Kermit uses "unsigned long" unless the symbol INADDRX is defined, in which case "struct inaddr" is used instead. Try adding -DINADDRX to CFLAGS in your make entry, and if that fixes the problem, please send a report to kermit@columbia.edu. Compilation errors might also have to do with the data type used for getsockopt() and setsockopt() option-length field. This is normally an int, but sometimes it's a short, a long, or an unsigned any of those, or a size_t. To fix the compilation problem, add -DSOCKOPT_T=xxx to the CFLAGS in your makefile target, where xxx is the appropriate type (use "man getsockopt" or grep through your system/network header files to find the needed type). 8.1.3. Enabling Host Address Lists When you give Kermit an IP host name, it calls the socket routine gethostbyname() to resolve it. gethostbyname() returns a hostent struct, which might or might not not include a list of addresses; if it does, then if the first one fails, Kermit can try the second one, and so on. However, this will only work if the symbol "h_addr" is a macro defined as "h_addr_list[0]", usually in netdb.h. If it is, then you can activate this feature by defining the following symbol in CFLAGS: HADDRLIST 8.1.4. Enabling Telnet NAWS The Telnet Negotiation About Window Size (NAWS) option requires the ability to find out the terminal screen's dimensions. E.g. in Unix, we need something like ioctl(0, TIOCGWINSZ, ...). If your version of Kermit was built with NAWS capability, SHOW VERSIONS includes CK_NAWS among the compiler options. If it doesn't, you can add it by defining CK_NAWS at compile time. Then, if the compiler or linker complain about undefined or missing symbols, or there is no complaint but SHOW TERMINAL fails to show reasonable "Rows =, Columns =" values, then take a look at (or write) the appropriate ttgwsiz() routine. On the other hand, if CK_NAWS is defined by default for your system (in [122]ckcnet.h), but causes trouble, you can override this definition by including the -DNONAWS switch on your CC command line, thus disabling the NAWS feature. This appears to be needed at least on the AT&T 3B2, where in [123]ckutio.c, the routine ttgwsiz() finds that the TIOCGWINSZ symbol is defined but lacks definitions for the corresponding winsize struct and its members ws_col and ws_row. The UNIX version of C-Kermit also traps SIGWINCH, so it can send a NAWS to the Telnet server any time the local console terminal window size changes, e.g. when you stretch it with a mouse. The SIGWINCH-trapping code is enabled if SIGWINCH is defined (i.e. in signal.h). If this code should cause problems, you can disable it without disabling the NAWS feature altogether, by defining NOSIGWINCH at compile time. 8.1.5. Enabling Incoming TCP/IP Connections This feature lets you "set host * port" and wait for an incoming connection on the given port. This feature is enabled automatically at compile if TCPSOCKET is defined and SELECT is also defined. But watch out, simply defining SELECT on the cc command line does not guarantee successful compilation or linking (see [124]Section 11). If you want to disable incoming TCP/IP connections, then build C-Kermit with: -DNOLISTEN 8.1.6. Disabling SET TCP Options The main reason for this is because of header file / prototype conflicts at compile time regarding get- / setsockopt(). If you can't fix them (without breaking other builds), add the following in CFLAGS: -DNOTCPOPTS 8.2. X.25 X.25 support requires (a) a Sun, (b) the SunLink product (libraries and header files), and (c) an X.25 connection into your Sun. Similarly (in C-Kermit 7.0 or later) Stratus VOS and IBM AIX. In UNIX, special makefile targets sunos4x25 and sunos41x25 (for SUNOS 4.0 and 4.1, respectively), or aix41x25, are provided to build in this feature, but they only work if conditions (a)-(c) are met. To request this feature, include -DSUNX25 (or -DIBMX25) in CFLAGS. SUNX25 (or -DIBMX25) and TCPSOCKET can be freely mixed and matched, and selected by the user at runtime with the SET NETWORK TYPE command or SET HOST switches. 8.3. Other Networks Support for other networking methods -- NETBIOS, LAT, Named Pipes, etc -- is included in ck*net.h and ck*net.c for implementations (such as Windows or OS/2) where these methods are supported. Provision is made in the organization of the modules, header files, commands, etc, for addition of new network types such as DECnet, X.25 for other systems (HP-UX, VMS, etc), and so on. Send email to [125]kermit@columbia.edu if you are willing and able to work on such a project. [ [126]C-Kermit Home ] [ [127]Kermit Home ] 9. EXCEPTION HANDLING [ [128]Top ] [ [129]Contents ] [ [130]Next ] [ [131]Previous ] The C language setjmp/longjmp mechanism is used for handling exceptions. The jump buffer is of type jmp_buf, which almost everywhere is typedef'd as an array, in which case you should have no trouble compiling the exception-handling code. However, if you are building C-Kermit in/for an environment where jmp_buf is something other than an array (e.g. a struct), then you'll have to define the following symbol: JBNOTARRAY [ [132]C-Kermit Home ] [ [133]Kermit Home ] 10. SECURITY FEATURES [ [134]Top ] [ [135]Contents ] [ [136]Next ] [ [137]Previous ] Security, in the sense of secure authentication and strong encryption, can be built into versionf of C-Kermit for which the appropriate libraries and header files are available (Kerberos IV, Kerberos V, OpenSSL, SRP), as explained in great detail in the Kermit Security Reference . The following symbols govern C-Kermit's security features at build time: NO_AUTHENTICATION Means do not configure any TELNET AUTHENTICATION support. It implies NO_ENCRYPTION and undefines any of the auth and encrypt types. It does not undefine CK_SSL even though builds with CK_SSL cannot succeed without CK_AUTHENTICATION. (This will be supported in a future release. It will be needed to allow C-Kermit to be built only as an FTP client.) NO_KERBEROS Means do not compile in any KERBEROS support when CK_AUTHENTICATION has been defined. NO_SRP Do not compile in any SRP support when CK_AUTHENTICATION has been defined. NO_SSL Do not compile in any SSL/TLS support NO_ENCRYPTION Do not compile in any Telnet encryption support. It does not affect the use of SSL/TLS NOSSH Do not compile in any SSH support whether internal or external CK_AUTHENTICATION Telnet AUTHENTICATION support. (Also, required if SSL/TLS support is desired.) On most platforms this does not autodefine any authentication mechanisms such as Kerberos V, Kerberos IV, SRP, ... Those need to be defined separately. CK_KERBEROS Defined automatically when KRB4, KRB5, or KRB524 are defined. Implies that some version of Kerberos is in use. KRB4 Should be defined when Kerberos IV support is desired. KRB5 Should be defined when Kerberos V support is desired. KRB524 Should be defined if both Kerberos V and Kerberos IV are used and the Kerberos IV support is provided by the MIT Kerberos IV compatibility library in the current Kerberos 5 distribution. KRB5_U2U Should be defined if KRB5 is defined and Kerberos 5 User to User mode is desired. HEIMDAL Should be defined if Kerberos V support is provided by HEIMDAL. Support for this option is not complete in C-Kermit 8.0. Anyone interested in working on this should contact kermit-support. CK_SRP Should be defined if SRP support is desired. CK_ENCRYPTION Should be defined if TELNET ENCRYPTION option support is desired. This option does not define any particular encryption types. That should be done by defining CK_DES or CK_CAST. CK_DES Should be defined if either DES or 3DES Telnet Encryption option support is desired. LIBDES If CK_DES is defined and DES support is being provided by either Eric Young's libdes.a or OpenSSL 0.9.6x or earlier, this option must be defined. If it is not defined, it will be assumed that DES support is provided by the MIT Kerberos IV libraries. CK_CAST Should be defined if CAST Telnet Encryption option support is desired CK_SSL Should be defined if SSL/TLS support (OpenSSL) is desired. SSL_KRB5 If KRB5 is defined, and OpenSSL is built to support the Kerberos 5 ciphers, then you should define SSL_KRB5 NOSSLKRB5 If you are using OpenSSL 0.9.7 or higher and do not wish to build with support for Kerberos 5 TLS ciphers, this option must be defined. ZLIB If you are using OpenSSL 0.9.6 or higher and it has been compiled with support for ZLIB compression, this option should be defined to enable Kermit to properly enable the use of compression. SSHCMD Defined for C-Kermit to enable the use of external SSH clients from the Kermit command language SSHBUILTIN Defined for Kermit implementations that have integrated SSH support. Currently only Windows. ANYSSH Defined if either SSHCMD or SSHBUILTIN are defined. CK_SNDLOC Telnet Send Location support. NOSNDLOC Do not include Telnet Send Location support. CK_XDISPLOC Telnet X-Display Location support. Determines if the X-Display location information is sent to the Telnet server either via Telnet XDISPLOC or NEW-ENV options. NOXDISPLOC Do not include Telnet X-Display Location support. CK_FORWARD_X Telnet Forward X Windows Session Data option. Used to protect the privacy and integrity of X Windows Sessions when secure telnet sessions are in use. NOFORWARDX Do not include Telnet Forward X Windows Session Data option. Besides the strong forms of security listed above, C-Kermit also embodies various internal security features, including: NOPUSH Compiling with the NOPUSH symbol defined removes all the "shell escape" features from the program, including the PUSH, RUN, and SPAWN commands, the "!" and "@" command prefixes, OPEN !READ, OPEN !WRITE, job control (including the SUSPEND command), the REDIRECT command, shell/DCL escape from CONNECT mode, as well as the server's execution of REMOTE HOST commands (and, of course, the ENABLE HOST command). Add NODISPO to also prevent acceptance of incoming MAIL or REMOTE PRINT files. For UNIX, also be sure to read [138]Section 11 of the [139]Unix C-Kermit Installation Instructions. about set[ug]id configuration. Additional restrictions can be enforced when in server mode; read about the DISABLE command in the user manual. NOCCTRAP Compiling with NOCCTRAP prevents the trapping of SIGINT by Kermit. Thus if the user generates a SIGINT signal (e.g. by typing the system's interrupt character), Kermit will exit immediately, rather than returning to its prompt. NOPUSH and NOCCTRAP together allow Kermit to be run from restricted shells, preventing access to system functions. [ [140]C-Kermit Home ] [ [141]Kermit Home ] 11. ENABLING SELECT() [ [142]Top ] [ [143]Contents ] [ [144]Next ] [ [145]Previous ] Kermit works best if it can do nonblocking reads, nondestructive input buffer checking, and millisecond sleeps. All of these functions can be accomplished by the select() function, which, unfortunately, is not universally available. Furthermore, select() is required if incoming TCP/IP connections are to be supported. select() was introduced with Berkeley UNIX, rejected by AT&T for System V, but is gradually creeping in to all UNIX versions (and other operating systems too) by virtue of its presence in the sockets library, which is needed for TCP/IP. AT&T SVID for System V R4 includes select(), but that does not mean that all SVR4 implementations have it. Furthermore, even when select() is available, it might work only on socket file descriptors, but not on others like serial ports, pipes, etc. For example, in AOS/VS and BeOS, it works only with file descriptors that were created by socket() and opened by connect() or accept(). Other alternatives include poll() and rdchk(). Only one of these three functions should be included. The following symbols govern this: SELECT Use select() (BSD, or systems with sockets libraries) CK_POLL Use poll() (System V) RDCHK Use rdchk() (SCO XENIX and UNIX) If your system supports the select() function, but your version of C-Kermit does not, try adding: -DSELECT to the CFLAGS, and removing -DRDCHK or -DCK_POLL if it is there. If you get compilation errors, some adjustments to ck*tio.c and/or ck*net.c might be needed; search for SELECT (uppercase) in these files (note that there are several variations on the calling conventions for select()). Various macros and data types need to be defined in order to use select(). Usually these are picked up from or . But on some systems, they are in . In that case, add the following: -DSELECT_H to the CFLAGS to tell C-Kermit to #include . A good indication that you need to do this would be if you get compile-time complaints about "fd_set" or "FD_SET" not being declared or defined. In UNIX, the use of select() vs fork() in the CONNECT command is independent of the above considerations, and is governed by choosing a particular makefile target. As of C-Kermit 7.0, select() is also the preferred control mechanism for the CONNECT command. Unfortunately, the structures used by the original UNIX CONNECT command, based on fork(), and those used by select(), are so different, it was not practical to implement them both in one module. So the select()-based CONNECT command module for UNIX is [146]ckucns.c, and the fork-based one remains [147]ckucon.c. To choose the fork-based one, which is more portable (but slower and more fragile), use "wermit" as the make target. To choose the select-based one, use "xermit". Only do this if you can verify that the CONNECT command works on serial connections and PIPE connections as well as TCP connections. The select()-based Unix CONNECT module, ckucns.c, must be used if encryption is to be done, since the fork() version (ckucon.c) loses its ability to share vital state information between the two forks. Also note that the select() version is superior in many other ways too. For example, it recovers better from exterior killing, forced disconnections, etc, plus it goes faster. SHOW VERSIONS tells whether the CONNECT module uses fork() or select(). C-Kermit 8.0 adds learned script capability, which depends on select(). All the "wermit" based targets (as opposed to "xermit") had NOLEARN added to them. Whenever changing a target over from wermit to xermit, also remember to remove NOLEARN. [ [148]C-Kermit Home ] [ [149]Kermit Home ] 12. I/O REDIRECTION [ [150]Top ] [ [151]Contents ] [ [152]Next ] [ [153]Previous ] The REDIRECT command allows a local program to be run with its i/o redirected over the communications connection. Your version of C-Kermit has a REDIRECT command if it was built with the following CFLAG: -DCK_REDIR This, in turn, is possible only if the underlying API is there. In the case of UNIX this is just the wait() system call, so all UNIX versions get this feature as of 6.0.192 (earlier versions needed a header file defining the symbols WIFEXITED and WEXITSTATUS). As of version 7.0, file transfer can be done using pipes and filters. To enable this feature, #define PIPESEND (and fill in the code). To disable on systems where it is normally enabled, define NOPIPESEND. This feature is, of course, also disabled by building with NOPUSH (or giving the "nopush" command at runtime). C-Kermit 7.0 also adds the PIPE and SET HOST /COMMAND commands, which provide another form of redirection. This feature is selected with -DNETCMD. CK_RDIR must also be defined, since the same mechanisms are used internally. [ [154]C-Kermit Home ] [ [155]Kermit Home ] 13. FLOATING-POINT NUMBERS, TIMERS, AND ARITHMETIC [ [156]Top ] [ [157]Contents ] [ [158]Next ] [ [159]Previous ] Floating-point support was added in C-Kermit 7.0. Floating-point numbers are enabled internally, at least for use in high-precision file-transfer timers and statistics, unless the following symbol is defined at compile time: -DNOFLOAT This might be necessary on old PCs that do not have built-in floating-point hardware. When NOFLOAT is not defined, the following symbol tells which floating-point type to use: -DCKFLOAT=xxxx The value is either "double" (normal for 32- and 16-bit architectures) or "float" (normal for 64-bit architectures). C-Kermit can be configured to use high-precision file-transfer timers for more accurate statistics. This feature is enabled with: -DGFTIMER and disabled with: -DNOGFTIMER If you try to build with -DGFTIMER but you get compilation errors, either fix them (and send email to kermit@columbia.edu telling what you did), or else give up and use -DNOGFTIMER (or -DNOFLOAT) instead. Hint: depending on your machine architecture, you might have better luck using double than float as the data type for floating-point numbers, or vice versa. Look in [160]ckcdeb.h for the CKFLOAT definition. Floating-point arithmetic is also supported in the script programming language. First via the \fpp...() functions, such as \fppadd(), which adds two floating-point numbers, second in S-Expressions. Addition, subtraction, multiplication, and division are always available. But other functions such as logs, raising to powers, sines and cosines, etc, require the C Math library. To include user-level floating-point math you must put: -DFNFLOAT and in Unix you must link with the Math library: LIBS=".... -lm" In K95 and VMS, FNFLOAT is defined automatically if CKFLOAT is defined. In Unix, however, FNFLOAT must be added to each makefile target individually, because of the special linking instructions that must also be added to each target. Note: S-Expressions require FNFLOAT. [ [161]C-Kermit Home ] [ [162]Kermit Home ] 14. SPECIAL CONFIGURATIONS [ [163]Top ] [ [164]Contents ] [ [165]Previous ] As of C-Kermit 7.0, if you build C-Kermit normally, but with -DNOICP (No Interactive Command Parser), you get a program capable of making serial connections (but not dialing) and network connections (if TCPSOCKET or other network option included), and can also transfer files using Kermit protocol, but only via autodownload/upload. Furthermore, if you call the executable "telnet", it will act like Telnet -- using the command-line options. However, in this case there is nothing to escape back to, so if you type Ctrl-\c, it just prints a message to this effect. You can also build C-Kermit with -DNOXFER, meaning omit all the file-transfer features. This leaves you with a scriptable communications program that is considerably smaller than the full C-Kermit. [ [166]C-Kermit Home ] [ [167]Kermit Home ] APPENDIX I: SUMMARY OF COMPILE-TIME OPTIONS [ [168]Top ] [ [169]Contents ] These are the symbols that can be specified on the cc command line, listed alphabetically. Others are used internally, including those taken from header files, those defined by the compiler itself, and those inferred from the ones given below. Kermit's SHOW VERSIONS command attempts to display most of these. See [170]ckcdeb.h and [171]ckcnet.h for inference rules. For example SVR3 implies ATTSV, MULTINET implies TCPSOCKET, and so on. Here is the complete list of the Kermit-specific compile-time switches: ACUCNTRL Select BSD 4.3-style acucntrl() bidirectional tty control. aegis Build for Apollo Aegis (predefined on Apollo systems). AIX370 Build for IBM AIX/370 for IBM mainframes. AIXESA Build for IBM AIX/ESA for IBM mainframes. AIXPS2 Build for IBM AIX 3.0 for PS/2 series (never formally released). AIXRS Build for IBM AIX 3.x on RS/6000. AIX41 Build for IBM AIX 4.x on RS/6000. AMIGA Build for Commodore Amiga with Intuition OS. ATT6300 Build for AT&T 6300 PLUS. ATT7300 Build for AT&T 7300 UNIX PC (3B1). ATTSV Build for AT&T System III or V UNIX. AUX Build for Apple A/UX for the Macintosh. BIGBUFOK OK to use big buffers - "memory is not a problem" BPS_xxxx Enable SET SPEED xxxx BSD29 Build for BSD 2.9 or 2.10. BSD4 Build for BSD 4.2. BSD41 Build for BSD 4.1. BSD43 Build for BSD 4.3. BSD44 Build for BSD 4.4. C70 Build for BBN C/70. CIE Build for CIE Systems 680/20. CKCONINTB4CB Work around prompt-disappears after escape back from CONNECT. CKLEARN Build with support for learned scripts. CKLOGDIAL Enable connection log. CKMAXPATH Maximum length for a fully qualified filename. CKREGEX (misnomer) Include [...] or {xxx,xxx,xxx} matching in ckmatch(). CKSYSLOG Enable syslogging. CK_ANSIC Enable ANSI C constructs - prototypes, etc. CK_ANSILIBS Use header files for ANSI C libraries. CK_APC Enable APC execution by CONNECT module. CK_CURSES Enable fullscreen file transfer display. CK_DSYSINI Use system-wide init file, with name supplied by Kermit. CK_DTRCD DTR/CD flow control is available. CK_FAST Build with fast Kermit protocol defaults. CK_FORK_SIG UNIX only: signal() number for CONNECT module forks. CK_IFRO IF REMOTE command is available (and can run in remote mode). CK_INI_A System-wide init file takes precedence over user's. CK_INI_B User's init file takes precedence over the system-wide one. CK_LABELED Include support for SET FILE TYPE LABELED. CK_LBRK This version can send Long BREAK. CK_LINGER Add code to turn of TCP socket "linger" parameter. CK_MKDIR This version has a zmkdir() command to create directories. CK_NAWS Include TELNET Negotiate About Window Size support. CK_NEWTERM Use newterm() rather than initscr() to initialize curses. CK_PAM Include PAM authentication (might also require -lpam). CK_PCT_BAR Fullscreen file transfer display should include "thermometer". CK_POLL System-V or POSIX based UNIX has poll() function. CK_POSIX_SIG Use POSIX signal handing: sigjmp_buf, sigsetjmp, siglongjmp. CK_READ0 read(fd,&x,0) can be used to test TCP/IP connections. CK_REDIR Enable the REDIRECT command. CK_RESEND Include the RESEND command (needs zfseek() + append). CK_RTSCTS RTS/CTS flow control is available. CK_SHADOW Include support for shadow passwords (e.g. for IKSD authentication). CK_SOCKBUF Enable TCP socket-buffer-size-increasing code. CK_SOCKS UNIX only: Build with socks library rather than regular sockets CK_SOCKS5 UNIX only: Build with socks 5 lib rather than regular sockets CK_SPEED Enable control-character unprefixing. CK_SYSINI="xxxxx" Quoted string to be used as system-wide init file name. CK_TIMERS Build with support for dynamically calculated packet timeouts. CK_TMPDIR This version of Kermit has an isdir() function. CK_TTYFD Defined on systems where the communications connection file descriptor (ttyfd) can be passed to other processes as a command-line argument via \v(ttyfd). CK_URL Parse URLs as well as hostnames, etc. CK_XONXOFF Xon/Xoff flow control available. CK_XYZ Include support for XYZMODEM protocols. CK_WREFRESH Curses package includes wrefresh(),clearok() for screen refresh. CKFLOAT=type Floating-point data type, "double" or "float". CKTYP_H=xxx Force include of xxx as file. CLSOPN When hanging up a tty device, also close and reopen it. CMDDEP Maximum recursion depth for self-referential user-defined fn's. COHERENT Build for Mark Williams Coherent UNIX CONGSPD Define if this version has congspd() routine in ck?tio.c datageneral Build for Data General AOS/VS or AOS/VS II DCLPOPEN popen() is available but needs to be declared DEC_TCPIP Build with support for DEC TCP/IP (UCX) for (Open)VMS DGUX430 Build for DG/UX 4.30 DGUX540 Build for DG/UX 5.40 DEFPAR=x Default parity, 0, 'e', 'o', 'm', or 's'. DFTTY=xxx Default communications device name. DIRENT UNIX directory structure to be taken from . DIRPWDRP Prompt for password in REMOTE CWD command. DTILDE Include UNIX ~ notation for username/home-directory DYNAMIC Allocate file transfer packet buffers dynamically with malloc. ENCORE Build for Encore Multimax computers. EXCELAN Build with excelan TCP/IP. FNFLOAT Include floating-point math functions (logs, sin, cos, exp, etc) FT18 Build for Fortune For:Pro 1.8. FT21 Build for Fortune For:Pro 2.1. GEMDOS Build for Atari ST GEMDOS. GFTIMER Use high-precision floating-point file-transfer timers. GID_T=xxx Group IDs are of type xxx (usually int, short, or gid_t). HADDRLIST If gethostbyname() hostent struct contains a list of addresses. HDBUUCP Build with support for Honey DanBer UUCP. HPUX Build for Hewlett Packard HP-UX. HPUX9 Build for Hewlett Packard HP-UX 9.x. HPUX10 Build for Hewlett Packard HP-UX 10.x. HWPARITY Define if this version can SET PARITY HARDWARE { EVEN, ODD...} I386IX Build for Interactive System V R3. IFDEBUG Add IF stmts "if (deblog)" before "debug()" calls. INADDRX TCP/IP inet_addr() type is struct inaddr, not unsigned long. INTERLAN Build with support for Racal/Interlan TCP/IP. ISDIRBUG System defs of S_ISDIR and S_ISREG have bug, define ourselves. ISIII Build for Interactive System III. IX370 Build for IBM IX/370. KANJI Build with Kanji character-set translation support. LCKDIR UUCP lock directory is /usr/spool/uucp/LCK/. LFDEVNO UUCP lockfile name uses device numbers, as in SVR4. LINUXFSSTND For Linux, use FSSTND UUCP lockfile conventions (default). LOCK_DIR=xxx UUCP lock directory is xxx (quoted string). LOCKF Use lockf() (in addition to lockfiles) on serial lines LONGFN BSD long filenames supported using and opendir(). LYNXOS Build for Lynx OS 2.2 or later (POSIX-based). MAC Build for Apple Macintosh with Mac OS. MATCHDOT Make wildcards match filenames that start with period (.) MAXRP=number Maximum receive-packet length. MAXSP=number Maximum send-packet length. MDEBUG Malloc-debugging requested. MINIDIAL Minimum modem dialer support: CCITT, Hayes, Unknown, and None. MINIX Build for MINIX. MIPS Build for MIPS workstation. MULTINET Build with support for TGV MultiNet TCP/IP (VAX/VMS). M_UNIX Defined by SCO. NAP The nap() is available (conflicts with SELECT and USLEEP) NAPHACK The nap() call is available but only as syscall(3112,...) NDIR BSD long filenames supported using and opendir(). NDGPWNAM Don't declare getpwnam(). NDSYSERRLIST Don't declare sys_errlist[]. NEEDSELECTDEFS select() is available but we need to define FD_blah ourselves. NETCMD Build with support for SET HOST /COMMAND and PIPE commands. NEXT Build for NeXT Mach 1.x or 2.x or 3.0, 3.1, or 3.2. NEXT33 Build for NeXT Mach 3.3. NOANSI Disable ANSI C function prototyping. NOAPC Do not include CK_APC code. NOARROWKEYS Exclude code to parse ANSI arrow-key sequences. NOB_xxxx Disable SET SPEED xxxx NOBIGBUF Override BIGBUFOK when it is the default NOBRKC Don't try to refer to t_brkc or t_eof tchars structure members. NOCKFQHOSTNAME Exclude code to get fully qualified hostname in case it causes core dumps. NOCCTRAP Disable Control-C (SIGINT) trapping. NOCKSPEED Disable control-prefix removal feature (SET CONTROL). NOCKTIMERS Build without support for dynamic timers. NOCKXYZ Overrides CK_XYZ. NOCKREGEX Do not include [...] or {xxx,xxx,xxx} matching in ckmatch(). NOCMDL Build with no command-line option processing. NOCOTFMC No Close(Open()) To Force Mode Change (UNIX version). NOCSETS Build with no support for character set translation. NOCYRIL Build with no support for Cyrillic character set translation. NOCYRILLIC Ditto. NODEBUG Build with no debug logging capability. NODIAL Build with no DIAL or SET DIAL commands. NODISPO Build to always refuse incoming MAIL or REMOTE PRINT files. DNODISPLAY Build with no file-transfer display. NOESCSEQ Build with no support for ANSI escape sequence recognition. NOFAST Do not make FAST Kermit protocol settings the default. NOFDZERO Do not use file descriptor 0 for remote-mode file transfer. NOFILEH Do not #include . NOFLOAT Don't include any floating-point data types or operations. NOFRILLS Build with "no frills" (this should be phased out...) NOFTRUNCATE Include this on UNIXes that don't have ftruncate(). NOGETUSERSHELL Include this on UNIXes that don't have getusershell(). NOGFTIMER Don't use high-precision floating-point file-transfer timers. NOHEBREW Build with no support for Hebrew character sets. NOHELP Build with no built-in help. NOIKSD Build with IKSD support excluded. NOINITGROUPS Include this on UNIXes that don't have initgroups(). NOICP Build with no interactive command parser. NOJC Build with no support for job control (suspend). NOKANJI Build with no support for Japanese Kanji character sets. NOKVERBS Build with no support for keyboard verbs (\Kverbs). NOLATIN2 Build with no ISO Latin-2 character-set translation support. NOLEARN Build with no support for learned scripts. NOLINKBITS Use of S_ISLNK and _IFLNK untrustworthy; use readlink() instead. NOLOCAL Build without any local-mode features: No Making Connections. NOLOGDIAL Disable connection log. NOLOGIN Build without IKSD (network login) support. NOLSTAT Not OK to use lstat(). NOMDMHUP Build without "modem-specific hangup" (e.g. ATH0) feature. NOMHHOST Exclude the multihomed-host TCP/IP code (if compilation errors) NOMINPUT Build without MINPUT command. NOMSEND Build with no MSEND command. NONAWS Do not include TELNET Negotiate About Window Size support. NONET Do not include any network support. NONOSETBUF (See NOSETBUF) NOPARSEN Build without automatic parity detection. NOPIPESEND Disable file transfer using pipes and filters. NOPOLL Override CK_POLL definition. NOPOPEN The popen() library call is not available. NOPURGE Build with no PURGE command. NOPUSH Build with no escapes to operating system. NOREALPATH In UNIX, realpath() function is not available. NORECALL Disable the command-recall feature. NOREDIRECT Disable REDIRECT command. NORENAME Don't use rename() system call, use link()/unlink() (UNIX). NORESEND Build with no RESEND command. NORETRY Build with no command-retry feature. NOSCRIPT Build with no SCRIPT command. NOSELECT Don't try to use select(). NOSERVER Build with no SERVER mode and no server-related commands. NOSETBUF Don't make console writes unbuffered. NONOSETBUF DO make console writes unbuffered. NOSETREU setreuid() and/or setregid() not available. NOSHOW Build with no SHOW command (not recommended!). NOSIGWINCH Disable SIGWINCH signal trapping. NOSPL Build with no script programming language. NOSTAT Don't call stat() from mainline code. NOSYMLINK Include this for UNIXes that don't have readlink(). NOSYSIOCTLH Do not #include . NOSYSTIMEH Co not include . NOSYSLOG Disable syslogging code. NOTCPOPTS Build with no SET TCP options or underlying support. NOTLOG Build with no support for transaction logging. NOTM_ISDST Struct tm has no tm_isdst member. NOUNICODE Build with no support for Unicode character-set translation. NOURL Don't parse URLs NOUUCP Build with no UUCP lockfile support (dangerous!). NOWARN Make EXIT WARNING be OFF by default (otherwise it's ON). NOWREFRESH Override built-in definition of CK_WREFRESH (q.v.). NOXFER Build with no Kermit or other file-transfer protocols. NOXMIT Build with no TRANSMIT command. NOXPRINT Disables transparent print code. OLDMSG Use old "entering server mode" message (see [172]ckcmai.c). OLINUXHISPEED Build in old Linux hi-serial-speed code (for Linux <= 1.0). OPENBSD Build for OpenBSD. OS2 Build for OS/2. OSF Build for OSF/1. OSFPC Build for OSF/1 on a PC. OSF32 Digital UNIX 3.2 or later. OSF40 Build for Digital UNIX 4.0. OSF50 Build for Digital UNIX 5.0. OSK Build for OS-9. OXOS Build for Olivetti X/OS 2.3. PCIX Build for PC/IX PID_T=xxx Type for pids is xxx (normally int or pid_t). POSIX Build for POSIX: use POSIX header files, functions, etc. _POSIX_SOURCE Disable non-POSIX features. PROVX1 Build for Venix 1.0 on DEC Professional 3xx. PTX Build for Dynix/PTX PWID_T=xxx getpwid() type is xxx. RBSIZ=xxx Define overall size of receive-packet buffer (with DYNAMIC). RDCHK rdchk() system call is available. RENAME rename() system call is available (UNIX). RTAIX Build for AIX 2.2.1 on IBM RT PC. RTU Build for Masscomp / Concurrent RTU. SAVEDUID BSD or other non-AT&T UNIX has saved-setuid feature. SBSIZ=xxx Define overall size of send-packet buffer (use with DYNAMIC). SDIRENT Directory structure specified in . SELECT select() function available (conflicts with RDCHK and CK_POLL) SELECT_H Include for select()-related definitions. SETEUID BSD 4.4-style seteXid() functions available. SIG_V Type for signal() is void. Used to override normal assumption. SIG_I Type for signal() is int. Used to override normal assumption. SOCKOPT_T Override default data type for get/setsockopt() option length. SOLARIS Build for Solaris. SOLARIS25 Build for Solaris 2.5 or later. SONYNEWS Build for Sony NEWS-OS. STERMIOX is available. STRATUS Build for Stratus VOS. STRATUSX25 Include Stratus VOS X.25 support. SUN4S5 Build for SUNOS 4.x in the System V R3 environment. SUNOS4 Build for SUNOS 4.0 in the BSD environment. SUNOS41 Build for SUNOS 4.1 in the BSD environment. SUNX25 Build with support for SunLink X.25. SVR3 Build for AT&T System V Release 3. SVR3JC Allow job control support on System V Release 3 UNIX versions. SVR4 Build for AT&T System V Release 4. SW_ACC_ID UNIX only -- swap real & effective ids around access() calls. sxaE50 Build for PFU Compact A Series SX/A TISP. SYSLOGLEVEL=n Force syslogging at given level. SYSTIMEH Include . SYSUTIMEH Include for setting file dates (88OPEN) TCPSOCKET Build with support for TCP/IP via Berkeley sockets library. TERMIOX header file is available (mostly SVR4). TNCODE Include TELNET-specific code. TOWER1 Build for NCR Tower 1632 with OS 1.02. TRS16 Build for Tandy 16/6000. UID_T=xxx Type for uids is xxx (normally int or uid_t). UNIX Must be defined for all UNIX versions. UNIX351M AT&T UNIX 3.51m on the AT&T 7300 UNIX PC. USE_ARROWKEYS Include code to parse ANSI arrow-key sequences. USE_LSTAT OK to use lstat(). USE_MEMCPY Define this if memcpy()/memset()/memmove() available. USE_STRERROR Define this if strerror() is available. USLEEP usleep() system call available (conflicts with NAP & SELECT). UTEK Build for Tektronix workstations with UTEK OS. UTIMEH Include for setting file dates (SVR4, POSIX) UTS24 Build for Amdahl UTS 2.4. V7 Build for Version 7 UNIX. VMS Build for VAX/VMS. VOID=xxx VOID type for functions (int or void). VXVE Build for CDC VX/VE 5.2.1. WAIT_T=xxx Type of argument passed to wait(). WINTCP Build with Wollongong VAX/VMS TCP/IP (implies TCPSOCKET) WOLLONGONG Build with Wollongong UNIX TCP/IP (implies TCPSOCKET) XENIX Build for Xenix (SCO, Tandy, others). XNDIR Support for BSD long filenames via . XYZ_INTERNAL Support for XYZMODEM protocols is internal, not external. ZFCDAT Define this if zfcdat() function is available in Kermit. ZILOG Build for Zilog ZEUS. ZJDATE Has zjdate() function that converts date to Julian format. XPRINT Transparent print code included in CONNECT module. [ [173]Top ] [ [174]Contents ] [ [175]C-Kermit Home ] [ [176]Kermit Home ] __________________________________________________________________ C-Kermit Configuration Options / [177]The Kermit Project / [178]kermit@columbia.edu / 30 June 2011 References 1. http://www.columbia.edu/ 2. mailto:kermit@columbia.edu 3. http://www.columbia.edu/kermit/index.html 4. http://www.columbia.edu/kermit/k95.html 5. http://www.columbia.edu/kermit/ckermit.html 6. http://www.columbia.edu/kermit/ckscripts.html 7. http://www.columbia.edu/kermit/current.html 8. http://www.columbia.edu/kermit/whatsnew.html 9. http://www.columbia.edu/kermit/faq.html 10. http://www.columbia.edu/kermit/support.html 11. http://www.columbia.edu/kermit/ckccfg.html 12. http://www.columbia.edu/kermit/ckermit.html 13. http://www.columbia.edu/kermit/index.html 14. http://www.columbia.edu/kermit/ckccfg.html#x1 15. http://www.columbia.edu/kermit/ckccfg.html#x2 16. http://www.columbia.edu/kermit/ckccfg.html#x3 17. http://www.columbia.edu/kermit/ckccfg.html#x4 18. http://www.columbia.edu/kermit/ckccfg.html#x5 19. http://www.columbia.edu/kermit/ckccfg.html#x6 20. http://www.columbia.edu/kermit/ckccfg.html#x7 21. http://www.columbia.edu/kermit/ckccfg.html#x8 22. http://www.columbia.edu/kermit/ckccfg.html#x9 23. http://www.columbia.edu/kermit/ckccfg.html#x10 24. http://www.columbia.edu/kermit/ckccfg.html#x11 25. http://www.columbia.edu/kermit/ckccfg.html#x12 26. http://www.columbia.edu/kermit/ckccfg.html#x13 27. http://www.columbia.edu/kermit/ckccfg.html#x14 28. http://www.columbia.edu/kermit/ckccfg.html#xa1 29. http://www.columbia.edu/kermit/ckuins.html 30. http://www.columbia.edu/kermit/ckermit.html 31. http://www.columbia.edu/kermit/index.html 32. http://www.columbia.edu/kermit/ckccfg.html#top 33. http://www.columbia.edu/kermit/ckccfg.html#contents 34. http://www.columbia.edu/kermit/ckccfg.html#x2 35. http://www.columbia.edu/kermit/ckccfg.html#x0 36. http://www.columbia.edu/kermit/ckermit.html 37. http://www.columbia.edu/kermit/index.html 38. http://www.columbia.edu/kermit/ckccfg.html#top 39. http://www.columbia.edu/kermit/ckccfg.html#contents 40. http://www.columbia.edu/kermit/ckccfg.html#x3 41. http://www.columbia.edu/kermit/ckccfg.html#x1 42. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c 43. ftp://kermit.columbia.edu/kermit/c-kermit/ckcdeb.h 44. ftp://kermit.columbia.edu/kermit/c-kermit/ckuus3.c 45. ftp://kermit.columbia.edu/kermit/c-kermit/ckcdeb.h 46. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c 47. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c 48. http://www.columbia.edu/kermit/ckermit.html 49. http://www.columbia.edu/kermit/index.html 50. http://www.columbia.edu/kermit/ckccfg.html#top 51. http://www.columbia.edu/kermit/ckccfg.html#contents 52. http://www.columbia.edu/kermit/ckccfg.html#x4 53. http://www.columbia.edu/kermit/ckccfg.html#x2 54. ftp://kermit.columbia.edu/kermit/c-kermit/makefile 55. http://www.columbia.edu/kermit/ckuins.html 56. http://www.columbia.edu/kermit/ckuins.html#x4 57. http://www.columbia.edu/kermit/ckuins.html#x9.2 58. ftp://kermit.columbia.edu/kermit/c-kermit/ckuusx.c 59. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c 60. ftp://kermit.columbia.edu/kermit/c-kermit/ckufio.c 61. ftp://kermit.columbia.edu/kermit/c-kermit/ckuusx.c 62. ftp://kermit.columbia.edu/kermit/c-kermit/ckucmd.c 63. http://www.columbia.edu/kermit/ckermit.html 64. http://www.columbia.edu/kermit/index.html 65. http://www.columbia.edu/kermit/ckccfg.html#top 66. http://www.columbia.edu/kermit/ckccfg.html#contents 67. http://www.columbia.edu/kermit/ckccfg.html#x5 68. http://www.columbia.edu/kermit/ckccfg.html#x3 69. http://www.columbia.edu/kermit/unicode.html 70. http://www.columbia.edu/kermit/ckermit.html 71. http://www.columbia.edu/kermit/index.html 72. http://www.columbia.edu/kermit/ckccfg.html#top 73. http://www.columbia.edu/kermit/ckccfg.html#contents 74. http://www.columbia.edu/kermit/ckccfg.html#x6 75. http://www.columbia.edu/kermit/ckccfg.html#x4 76. ftp://kermit.columbia.edu/kermit/c-kermit/ckuusr.h 77. ftp://kermit.columbia.edu/kermit/c-kermit/ckuusr.h 78. http://www.columbia.edu/kermit/ckermit.html 79. http://www.columbia.edu/kermit/index.html 80. http://www.columbia.edu/kermit/ckccfg.html#top 81. http://www.columbia.edu/kermit/ckccfg.html#contents 82. http://www.columbia.edu/kermit/ckccfg.html#x7 83. http://www.columbia.edu/kermit/ckccfg.html#x5 84. http://www.columbia.edu/kermit/ckccfg.html#x6.1 85. http://www.columbia.edu/kermit/ckccfg.html#x6.2 86. http://www.columbia.edu/kermit/ckccfg.html#x6.3 87. http://www.columbia.edu/kermit/ckccfg.html#x6.4 88. http://www.columbia.edu/kermit/ckccfg.html#x4 89. http://www.columbia.edu/kermit/gkermit.html 90. ftp://kermit.columbia.edu/kermit/c-kermit/ckuusr.h 91. ftp://kermit.columbia.edu/kermit/c-kermit/ckcker.h 92. ftp://kermit.columbia.edu/kermit/c-kermit/ckcdeb.h 93. http://www.columbia.edu/kermit/ckermit.html 94. http://www.columbia.edu/kermit/index.html 95. http://www.columbia.edu/kermit/ckccfg.html#top 96. http://www.columbia.edu/kermit/ckccfg.html#contents 97. http://www.columbia.edu/kermit/ckccfg.html#x8 98. http://www.columbia.edu/kermit/ckccfg.html#x6 99. ftp://kermit.columbia.edu/kermit/c-kermit/ckudia.c 100. http://www.columbia.edu/kermit/ckermit.html 101. http://www.columbia.edu/kermit/index.html 102. http://www.columbia.edu/kermit/ckccfg.html#top 103. http://www.columbia.edu/kermit/ckccfg.html#contents 104. http://www.columbia.edu/kermit/ckccfg.html#x9 105. http://www.columbia.edu/kermit/ckccfg.html#x7 106. http://www.columbia.edu/kermit/ckccfg.html#x8.1 107. http://www.columbia.edu/kermit/ckccfg.html#x8.2 108. http://www.columbia.edu/kermit/ckccfg.html#x8.3 109. http://www.columbia.edu/kermit/ckccfg.html#x8.1.1 110. http://www.columbia.edu/kermit/ckccfg.html#x8.1.2 111. http://www.columbia.edu/kermit/ckccfg.html#x8.1.3 112. http://www.columbia.edu/kermit/ckccfg.html#x8.1.4 113. http://www.columbia.edu/kermit/ckccfg.html#x8.1.5 114. http://www.columbia.edu/kermit/ckccfg.html#x8.1.6 115. ftp://kermit.columbia.edu/kermit/c-kermit/ckcnet.h 116. ftp://kermit.columbia.edu/kermit/c-kermit/ckctel.c 117. ftp://kermit.columbia.edu/kermit/c-kermit/ckctel.c 118. ftp://kermit.columbia.edu/kermit/c-kermit/ckctel.h 119. ftp://kermit.columbia.edu/kermit/c-kermit/ckcftp.c 120. http://www.columbia.edu/kermit/ckermit.html 121. ftp://kermit.columbia.edu/kermit/c-kermit/ckcnet.c 122. ftp://kermit.columbia.edu/kermit/c-kermit/ckcnet.h 123. ftp://kermit.columbia.edu/kermit/c-kermit/ckutio.c 124. http://www.columbia.edu/kermit/ckccfg.html#x11 125. mailto:kermit@columbia.edu 126. http://www.columbia.edu/kermit/ckermit.html 127. http://www.columbia.edu/kermit/index.html 128. http://www.columbia.edu/kermit/ckccfg.html#top 129. http://www.columbia.edu/kermit/ckccfg.html#contents 130. http://www.columbia.edu/kermit/ckccfg.html#x10 131. http://www.columbia.edu/kermit/ckccfg.html#x8 132. http://www.columbia.edu/kermit/ckermit.html 133. http://www.columbia.edu/kermit/index.html 134. http://www.columbia.edu/kermit/ckccfg.html#top 135. http://www.columbia.edu/kermit/ckccfg.html#contents 136. http://www.columbia.edu/kermit/ckccfg.html#x11 137. http://www.columbia.edu/kermit/ckccfg.html#x9 138. http://www.columbia.edu/kermit/ckuins.html#x11 139. http://www.columbia.edu/kermit/ckuins.html 140. http://www.columbia.edu/kermit/ckermit.html 141. http://www.columbia.edu/kermit/index.html 142. http://www.columbia.edu/kermit/ckccfg.html#top 143. http://www.columbia.edu/kermit/ckccfg.html#contents 144. http://www.columbia.edu/kermit/ckccfg.html#x12 145. http://www.columbia.edu/kermit/ckccfg.html#x10 146. ftp://kermit.columbia.edu/kermit/c-kermit/ckucns.c 147. ftp://kermit.columbia.edu/kermit/c-kermit/ckucon.c 148. http://www.columbia.edu/kermit/ckermit.html 149. http://www.columbia.edu/kermit/index.html 150. http://www.columbia.edu/kermit/ckccfg.html#top 151. http://www.columbia.edu/kermit/ckccfg.html#contents 152. http://www.columbia.edu/kermit/ckccfg.html#x13 153. http://www.columbia.edu/kermit/ckccfg.html#x11 154. http://www.columbia.edu/kermit/ckermit.html 155. http://www.columbia.edu/kermit/index.html 156. http://www.columbia.edu/kermit/ckccfg.html#top 157. http://www.columbia.edu/kermit/ckccfg.html#contents 158. http://www.columbia.edu/kermit/ckccfg.html#x14 159. http://www.columbia.edu/kermit/ckccfg.html#x12 160. ftp://kermit.columbia.edu/kermit/c-kermit/ckcdeb.h 161. http://www.columbia.edu/kermit/ckermit.html 162. http://www.columbia.edu/kermit/index.html 163. http://www.columbia.edu/kermit/ckccfg.html#top 164. http://www.columbia.edu/kermit/ckccfg.html#contents 165. http://www.columbia.edu/kermit/ckccfg.html#x13 166. http://www.columbia.edu/kermit/ckermit.html 167. http://www.columbia.edu/kermit/index.html 168. http://www.columbia.edu/kermit/ckccfg.html#top 169. http://www.columbia.edu/kermit/ckccfg.html#contents 170. ftp://kermit.columbia.edu/kermit/c-kermit/ckcdeb.h 171. ftp://kermit.columbia.edu/kermit/c-kermit/ckcnet.h 172. ftp://kermit.columbia.edu/kermit/c-kermit/ckcmai.c 173. http://www.columbia.edu/kermit/ckccfg.html#top 174. http://www.columbia.edu/kermit/ckccfg.html#contents 175. http://www.columbia.edu/kermit/ckermit.html 176. http://www.columbia.edu/kermit/index.html 177. http://www.columbia.edu/kermit/index.html 178. mailto:kermit@columbia.edu ckuker.nr0000644000015300001460000017362211624031410011511 0ustar fdckermit.\" @(#) kermit.1 9.0.302 2011/08/20 Columbia University .TH KERMIT 1 "JULY 2011" "User Manuals" .na .SH NAME kermit \- .B C\(hyKermit 9.0: transport\(hy and platform\(hyindependent interactive and scriptable communications software. .IP This document is intended to give the beginner sufficient information to make basic (if not advanced) use of C\(hyKermit 9.0. Although it might be rather long for a Unix manual page, it's still far shorter than the C\(hyKermit manual, which should be consulted for advanced topics such as customization, character\(hysets, scripting, etc. We also attempt to provide a clear structural overview of C\(hyKermit's many capabilities, functional areas, states, and modes and their interrelation, that should be helpful to beginners and veterans alike, as well as to those upgrading to version 9.0 from earlier releases. .PP This document is also available as a Web page at: .IP http://www.columbia.edu/kermit/ckututor.html .SH DESCRIPTION C\(hyKermit is an all\(hypurpose communications software package from the Kermit Project at Columbia University that: .PP .nf \(bu Is portable to many platforms, Unix and non\(hyUnix alike. .br \(bu Can make both serial and network connections. .br \(bu Can conduct interactive terminal sessions over its connection. .br \(bu Can transfer text or binary files over the same connection. .br \(bu Can convert character sets in the terminal session. .br \(bu Can convert character sets during text\(hyfile file transfer. .br \(bu Is customizable in every aspect of its operation. .fi .PP C\(hyKermit is a modem program, a Telnet client, an Rlogin client, an FTP client, an HTTP client, and on selected platforms, also an X.25 client. It can make its own secure Internet connections using IETF\(hyapproved security methods including Kerberos IV, Kerberos V, SSL/TLS, and SRP and it can also make SSH connections through your external SSH client application. It can be the far\(hyend file\(hytransfer or client/server partner of your desktop Kermit client. It can also accept incoming dialed and network connections. It can even be installed as an Internet service on its own standard TCP socket, 1649 [RFC2839, RFC2840]. .PP And perhaps most important, everything you can do "by hand" (interactively) with C\(hyKermit, can be "scripted" (automated) using its built\(hyin cross\(hyplatform transport\(hyindependent script programming language, which happens to be identical to its interactive command language. .PP This manual page offers an overview of C\(hyKermit 9.0 for Unix ("Unix" is an operating system family that includes AIX, DG/UX, FreeBSD, HP\(hyUX, IRIX, Linux, Mac OS X, NetBSD, OpenBSD, Open Server, Open Unix, QNX, Solaris, SunOS, System V R3, System V R4, Tru64 Unix, Unixware, Xenix, and many others). For thorough coverage, please consult the published C\(hyKermit manual and supplements (see DOCUMENTATION below). For further information about C\(hyKermit, Kermit software for other platforms, and Kermit manuals, visit the Kermit Project website: .PP http://www.columbia.edu/kermit/ .PP This is a longer\(hythan\(hyaverage manual page, and yet it barely scratches the surface. Don't be daunted. C\(hyKermit is a large and complex package, evolving over decades of practice and experience, but that doesn't mean it's hard to learn or use. Its most commonly used functions are explained here with pointers to additional information elsewhere. .SH SYNOPSIS .B kermit [ .I filename .B ] [ .I options .B ] [ {=,\-\-,+} .I text .B ] ] .PP or: .PP .B kermit .I URL .PP If the first command\(hyline argument is the name of a file, interactive\(hymode commands are executed from the file. The '=' (or "\-\-") argument tells Kermit not to parse the remainder of the command line, but to make the words following '=' available as \e%1, \e%2, ... \e%9. The "+" argument is like "=" but for use in "kerbang scripts" (explained below). A second command\(hyline format allows the one and only argument to be a Telnet, FTP, HTTP, or IKSD URL. .PP Order of execution: .TP 1. The command file (if any). .TP .nf 2. The initialization file, if any, unless suppressed with \-Y. .fi .TP 3. The customization file (if it is executed by the initialization file). .TP 4. The command\(hyline URL (if any, and if so, execution stops here). .TP 5. Command\(hyline options (if any). .TP 6. Interactive commands. .PP Some command\(hyline options can cause actions (such as \-s to send a file); others just set parameters. If any action options are included on the command line, Kermit exits when finished unless also given the \-S ("stay") option. If no action options are given, no initialization or command files contained an EXIT or QUIT command, and no fatal errors occurred, Kermit issues its prompt and waits for you to type commands. .IP Bear in mind that C\(hyKermit can be built with selected features disabled, and also that certain features are not available on all platforms. For example, C\(hyKermit can't be built with TCP/IP support on a platform that does not have TCP/IP header files and libraries (and even if Kermit does include TCP/IP support, it can't be used to make TCP/IP connections on a computer that does not have a TCP/IP stack installed). If your version of C\(hyKermit lacks a feature mentioned here, use its SHOW FEATURES command to see what might have been excluded. .PP C\(hyKermit has three kinds of commands: regular single\(hyletter command\(hyline options, extended\(hyformat command\(hyline options, and interactive commands. .PP Like most Unix commands, C\(hyKermit can be be given options on the command line. But C\(hyKermit also can be used interactively by giving it commands composed of words, which are more intuitive than cryptic command\(hyline options, and more flexible too. In other words, you don't have to use C\(hyKermit's command\(hyline options, but they are available if you want to. (By the same token, you don't have to use its interactive commands either \(hy\(hy you can use either or both in any combination.) .PP C\(hyKermit is generally installed in the PATH as "kermit", and therefore is invoked by typing the word "kermit" (lowercase) at the shell prompt, and then pressing the Return or Enter key. If you wish to include command\(hyline options, put them after the word "kermit" but before pressing Return or Enter, separated by spaces, for example: .PP $ kermit \-s ckermit.tar.gz .PP ('$' is the shell prompt; "kermit \-s ckermit.tar.gz" is what you type, followed by Return or Enter.) .SH OPTIONS Here is a list of C\(hyKermit's single\(hyletter command\(hyline options, which start with a single dash (\-), in ASCII ("alphabetical") order. Alphabetic case is significant (\-A is not the same as \-a). Action options are tagged "ACTION". .TP \-0 (digit zero) 100% transparent Connect state for "in\(hythe\(hymiddle" operation: 8 bits, no parity, no escape character, everything passes through. .TP \-8 (digit eight) Connection is 8\(hybit clean (this is the default in C\(hyKermit 8.0 and later). Equivalent to the EIGHTBIT command, which in turn is a shortcut for SET TERMINAL BYTESIZE 8, SET COMMAND BYTESIZE 8, SET PARITY NONE. .TP \-9 arg (digit nine) Make a connection to an FTP server. Equivalent to the FTP OPEN command. Argument: IP\(hyaddress\(hyor\(hyhostname[:optional\(hyTCP\(hyport]. NOTE: C\(hyKermit also has a separate FTP command\(hyline personality, with regular FTP\(hylike command\(hyline syntax. More about this below. .TP \-A Kermit is to be started as an Internet service (IKSD) (only from inetd.conf). .TP \-B Kermit is running in Batch or Background (no controlling terminal). To be used in case Kermit doesn't automatically sense its background status. Equivalent to the SET BACKGROUND ON command. .TP \-C arg Interactive\(hymode Commands to be executed. Argument: Commands separated by commas, list in doublequotes. .TP \-D arg Delay before starting to send in Remote mode. Equivalent to the SET DELAY command. Argument: Number of seconds. .TP \-E Exit automatically when connection closes. Equivalent to SET EXIT ON\-DISCONNECT ON. .TP \-F arg Use an open TCP connection. Argument: Numeric file descriptor of open TCP connection. Also see: \-j, \-J. .TP \-G arg (ACTION) Get file(s) from server, send contents to standard output, which normally would be piped to another process. Argument: Remote file specification, in quotes if it contains metacharacters. Also see: \-g, \-k. .TP \-H Suppress program startup Herald and greeting. .TP \-I Tell Kermit it has a reliable connection, to force streaming to be used where it normally would not be. Equivalent to the SET RELIABLE ON command. .TP \-J arg (ACTION) "Be like Telnet." Like \-j but implies \-E. Argument: IP hostname/address optionally followed by service. NOTE: C\(hyKermit also has a separate Telnet command\(hyline personality, with regular Telnet\(hylike command\(hyline syntax. More about this below. .TP \-L Recursive directory descent for files in \-s option. .TP \-M arg My user name (for use with Telnet, Rlogin, FTP, etc). Equivalent to the SET LOGIN USER command. Argument: Username string. .TP \-O (ACTION) (Uppercase letter O) Be a server for One command only. Also see: \-x. .TP \-P Don't convert file (Path) names of transferred files. Equivalent to SET FILE NAMES LITERAL. .TP \-Q Quick Kermit protocol settings. Equivalent to the FAST command. This is the default in C\(hyKermit 7.0 and later. .TP \-R Remote\(hyonly (this just makes IF REMOTE true). .TP \-S Stay (enter command parser after action options). .TP \-T Force Text mode for file transfer; implies \-V. Equivalent to SET TRANSFER MODE MANUAL, SET FILE TYPE TEXT. .TP \-V Disable automatic per\(hyfile text/binary switching. Equivalent to SET TRANSFER MODE MANUAL. .TP \-Y Skip (don't execute) the initialization file. .TP \-a arg As\(hyname for file(s) in \-s, \-r, or \-g. Argument: As\(hyname string (alternative filename). When receiving files, this can be a directory name. .TP \-b arg Speed for serial device. Equivalent to SET SPEED. Argument: Numeric Bits per second for serial connections. .TP \-c (ACTION) Enter Connect state before transferring files. .TP \-d Create a debug.log file with detailed debugging information (a second \-d adds timestamps). Equivalent to LOG DEBUG but takes effect sooner. .TP \-e arg Maximum length for incoming Kermit file\(hytransfer packets. Equivalent to SET RECEIVE PACKET\-LENGTH. Argument: Length in bytes. .TP \-f (ACTION) Send a FINISH command to a Kermit server. .TP \-g arg Get file(s) from a Kermit server. Argument: File specification on other computer, in quotes if it contains metacharacters. Equivalent to GET. Also see: \-a, \-G, \-r. .TP \-h (ACTION) Print Help text for single\(hyletter command\(hyline options (pipe thru 'more' to prevent scrolling). .TP \-i Force binary (Image) mode for file transfer; implies \-V. Equivalent to SET TRANSFER MODE MANUAL, SET FILE TYPE BINARY. .TP \-j arg Make a TCP/IP connection. Argument: IP host name/address and optional service name or number. Equivalent to the TELNET command. Also see: \-J, \-F. .TP \-k (ACTION) Receive file(s) to standard output, which normally would be piped to another process. Also see: \-r, \-G. .TP \-l arg (Lowercase letter L) Make a connection on the given serial communications device. Equivalent to the SET LINE (SET PORT) command. Argument: Serial device name, e.g. /dev/ttyS0. .TP \-m arg Modem type for use with the \-l device. Equivalent to the SET MODEM TYPE command. Argument: Modem name as in SET MODEM TYPE command, e.g. "usrobotics". .TP \-n (ACTION) Enter Connect state after transferring files (historical). .TP \-p arg Parity. Equivalent to the SET PARITY command. Argument: One of the following: e(ven), o(dd), m(ark), n(one), s(pace). .TP \-q Quiet (suppress most messages). Equivalent to SET QUIET ON. .TP \-r (ACTION) Receive file(s). Equivalent to the RECEIVE command. Argument: (none, but see \-a) .TP \-s arg Send file(s). Argument: One or more local file specifications. Equivalent to the SEND command. Also see: \-a. .TP \-t (Historical) Xon (Ctrl\-Q) Turnaround character for half\(hyduplex connections (used on serial linemode connections to old mainframes). Equivalent to SET DUPLEX HALF, SET HANDSHAKE XON. .TP \-v arg Window size for Kermit protocol (ignored when streaming). Equivalanet to SET WINDOW\-SIZE. Argument: Number, 1 to 32. .TP \-w Incoming files Write over existing files. Equivalent to SET FILE COLLISION OVERWRITE. .TP \-x (ACTION) Enter server mode. Equivalent to the SERVER command. Also see: \-O. .TP \-y arg Alternative initialization file. Argument: Filename. .TP \-z Force foreground behavior. To be used in case Kermit doesn't automatically sense its foreground status. Equivalent to the SET BACKGROUND OFF command. .PP Extended command\(hyline options (necessary because single\(hyletter ones are about used up) start with two dashes (\-\-), with words rather than single letters as option names. If an extended option takes an argument, it is separated from the option word by a colon (:). Extended options include: .TP \-\-bannerfile:filename File to display upon startup or IKSD login. .TP \-\-cdfile:filename File to be sent for display to the client when server changes directory (filename is relative to the changed\(hyto directory). .TP \-\-cdmessage:{on,off} Enable/disable the server CD message feature. .TP \-\-help Prints usage message for extended options. .TP \-\-helpfile:filename Designates a file containing custom text to replace the top\(hylevel HELP command. .TP \-\-nointerrupts Disables keyboard interrupts. .TP \-\-noperms Disables the Kermit protocol file Permissions attribute, to prevent transmission of file permissions (protection) from sender to receiver. .TP \-\-version (ACTION) C\(hyKermit prints its version number. .PP Plus several other IKSD\(hyOnly options described at: .PP http://www.columbia.edu/kermit/iksd.html .PP See the file\(hytransfer section for examples of command\(hyline invocation. .SH COMMAND LANGUAGE C\(hyKermit's interactive command language is the subject of a 622\(hypage book and another several hundred pages of updates, far too much for a manual page. But it's not hard to get started. At the shell prompt, just type "kermit" to get C\(hyKermit's interactive command prompt: .PP .nf $ kermit (/current/directory) C\-Kermit> .fi .PP Begin by typing "help" (and then press the Return or Enter key) for a top\(hylevel overview, read it, and go from there. Your second command should probably be "intro" (introduction). Note the prompt shows your current directory (unless you tell Kermit to prompt you with something else). .PP Interactive commands are composed mainly of regular English words, usually in the form of imperative sentences, such as: .PP send oofa.txt .PP which tells Kermit to send (transfer) the file whose name is oofa.txt, or: .PP set transfer mode automatic .PP which sets Kermit's "transfer mode" to "automatic" (whatever that means). .PP While typing commands, you can abbreviate, ask for help (by pressing the "?" key anywhere in a command), complete keywords or filenames (with the Tab or Esc key), and edit your typing with Backspace or Delete, Ctrl\-W, Ctrl\-U, etc. You can also recall previous commands, save your command history, and who knows what else. Give the INTRO command for details. .PP C\(hyKermit has hundreds of commands, and they can be issued in infinite variety and combinations, including commands for: .nf .PP \(bu Making connections (SET LINE, DIAL, TELNET, SSH, FTP, ...) .br \(bu Breaking connections (HANGUP, CLOSE) .br \(bu Transferring files (SEND, GET, RECEIVE, MOVE, RESEND, ...) .br \(bu Establishing preferences (SET) .br \(bu Displaying preferences (SHOW) .br \(bu Managing local files (CD, DELETE, MKDIR, DIR, RENAME, TYPE, ...) .br \(bu Managing remote files (RCD, RDEL, RMKDIR, RDIR, ...) .br \(bu Using local files (FOPEN, FCLOSE, FREAD, FWRITE) .br \(bu Programming (TAKE, DEFINE, IF, FOR, WHILE, SWITCH, DECLARE, ...) .br \(bu Interacting with the user (ECHO, ASK, ...) .br \(bu Interacting with a remote computer (INPUT, OUTPUT, ...) .br \(bu Interacting with local programs (RUN, EXEC, PTY, ...) .br \(bu Logging things (LOG SESSION, LOG PACKETS, LOG DEBUG, ...) .PP .fi And of course QUIT or EXIT to get out and HELP to get help, and for programmers: loops, decision making, variables, arrays, associative arrays, integer and floating point arithmetic, macros, built\(hyin and user\(hydefined functions, string manipulation, pattern matching, block structure, scoping, recursion, and all the rest. To get a list of all C\(hyKermit's commands, type a question mark (?) at the prompt. To get a description of any command, type HELP followed by the name of the command, for example: .PP help send .PP The command interruption character is Ctrl\-C (hold down the Ctrl key and press the C key). .PP The command language "escape character", used to introduce variable names, function invocations, and so on, is backslash (\). If you need to include a literal backslash in a command, type two of them, e.g.: .PP get c:\ek95\ek95custom.ini .SS Command Files, Macros, and Scripts A file containing Kermit commands is called a Kermit command file or Kermit script. It can be executed with Kermit's TAKE command: .PP (/current/dir) C\-Kermit> take commandfile .PP (where "commandfile" is the name of the command file). Please don't pipe a command file into Kermit's standard input (which might or might not work); if you have Kermit commands in a file, tell Kermit to TAKE the file. .PP In Unix only, a Kermit command file can also be executed directly by including a "kerbang" line as the first line of the file: .PP #!/usr/local/bin/kermit + .PP That is, a top line that starts with "#!", followed immediately by the full path of the Kermit executable, and then, if the Kermit script is to be given arguments on the command line, a space and a plus sign. The script file must also have execute permission: .PP chmod +x commandfile .PP Except for the " +" part, this is exactly the same as you would do for a shell script, a Perl script, etc. Here's a simple but useless example script that regurgitates its arguments (up to three of them): .PP #!/usr/local/bin/kermit + if defined \e%1 echo "Argument 1: \e%1" if defined \e%2 echo "Argument 2: \e%2" if defined \e%3 echo "Argument 3: \e%3" if defined \e%4 echo "etc..." exit .PP If this file is stored in your current directory as "commandfile", then: .PP ./commandfile one two three four five .PP prints: .PP Argument 1: one Argument 2: two Argument 3: three etc... .PP This illustrates the basic structure of a standalone Kermit script: the "kerbang line", then some commands. It should end with "exit" unless you want the Kermit prompt to appear when it is finished. \e%1 is the first argument, \e%2 the second, and so on. .PP You can also create your own commands by defining named macros composed of other Kermit commands (or macros). For example: .PP .nf define mydelete { local trash assign trash \ev(home)trashcan/ if not defined \e%1 end 1 "Delete what?" if wild \e%1 { end 1 "Deleting multiple files is too scary" } if not exist \e%1 end 1 "I can't find \e%1" if not directory \em(trash) { mkdir \em(trash) if fail end 1 "No trash can" } rename /list \e%1 \em(trash) } define myundelete { local trash assign trash \ev(home)trashcan/ if not defined \e%1 end 1 "Undelete what?" if wild \e%1 { end 1 "Undeleting multiple files is too hard" } if not directory \em(trash) end 1 "No trash can" if not exist \em(trash)\e%1 { end 1 "I can't find \e%1 in trash can" } rename /list \em(trash)\e%1 . } .PP .fi These sample macros are not exactly production quality (they don't handle filenames that include path segments, they don't handle multiple files, etc), but you get the idea: you can pass arguments to macros, and they can check them and make other kinds of decisions. If you put the above lines into your initialization or customization file (explained below), you'll have MYDELETE and MYUNDELETE commands available every time you start Kermit, at least as long as you don't suppress execution of the initialization file. (Exercise for the reader: Make these macros generally useful: remove limitations, add trashcan display, browsing, emptying, etc.) .PP Kerbang scripts execute without the initialization file. This to keep them portable and also to make them start faster. If you want to write Kerbang scripts that depend on the initialization file, include the command .PP take \ev(home).kermrc .PP at the desired spot in the script. By the way, \ev(xxx) is a built\(hyin variable (xxx is the variable name, "home" in this case). To see what built\(hyin variables are available, type "show variables" at the C\(hyKermit prompt. To see what else you can show, type "show ?". \em(xxx) is a user defined variable (strictly speaking, it is a macro used as a variable). .SS Command List C\(hyKermit has more than 200 top\(hylevel commands, and some of these, such as SET, branch off into hundreds of subcommands of their own, so it's not practical to describe them all here. Instead, here's a concise list of the most commonly used top\(hylevel commands, grouped by category. To learn about each command, type "help" followed by the command name, e.g. "help set". Terms such as Command state and Connect state are explained in subsequent sections. .PP Optional fields are shown in [ brackets ]. "filename" means the name of a single file. filespec means a file specification that is allowed to contain wildcard characters like '*' to match groups of files. options are (optional) switches like /PAGE, /NOPAGE, /QUIET, etc, listed in the HELP text for each command. Example: .PP .nf send /recursive /larger:10000 /after:\-1week /except:*.txt * .fi .PP which can be read as "send all the files in this directory and all the ones underneath it that are larger than 10000 bytes, no more than one week old, and whose names don't end with ".txt". .SS Basic Commands .RS .TP HELP Requests top\(hylevel help. .TP HELP command Requests help about the given command. .TP INTRODUCTION Requests a brief introduction to C\(hyKermit. .TP LICENSE Displays the C\(hyKermit software copyright and license. .TP VERSION Displays C\(hyKermit's version number. .TP EXIT [ number ] Exits from Kermit with the given status code. Synonyms: QUIT, E, Q. .TP TAKE filename [ parameters... ] Executes commands from the given .TP LOG item [ filename ] Keeps a log of the given item in the given file. .TP [ DO ] macro [ parameters... ] Executes commands from the given macro. .TP SET parameter value Sets the given parameter to the given value. .TP SHOW category Shows settings in a given category. .TP STATUS Tells whether previous command succeeded or failed. .TP DATE [ date\(hyand/or\(hytime ] Shows current date\(hytime or interprets given date\(hytime. .TP RUN [ extern\(hycommand [ parameters... ] Runs the given external command. Synonym: !. .TP EXEC [ extern\(hycommand [ params... ] Kermit overlays itself with the given command. .TP SUSPEND Stops Kermit and puts it in the background. Synonym: Z. .RE .SS Local File Management .RS .TP TYPE [ options ] filename Displays the contents of the given file. .TP MORE [ options ] filename Equivalent to TYPE /PAGE (pause after each screenful). .TP CAT [ options ] filename Equivalent to TYPE /NOPAGE. .TP HEAD [ options ] filename Displays the first few lines of a given file. .TP TAIL [ options ] filename Displays the last few lines of a given file. .TP GREP [ options ] pattern filespec Displays lines from files that match the pattern. Synonym: FIND. .TP DIRECTORY [ options ] [filespec ] Lists files (built\(hyin, many options). .TP LS [ options ] [ filespec ] Lists files (runs external "ls" command). .TP DELETE [ options ] [ filespec ] Deletes files. Synonym: RM. .TP PURGE [ options ] [ filespec ] Removes backup (*.~n~) files. .TP COPY [ options ] [ filespecs... ] Copies files. Synonym: CP. .TP RENAME [ options ] [ filespecs... ] Renames files. Synonym: MV. .TP CHMOD [ options ] [ filespecs... ] Changes permissions of files. .TP TRANSLATE filename charsets [ filename ] Converts file's character set. Synonym: XLATE. .TP CD Changes your working directory to your home directory. .TP CD directory Changes your working directory to the one given. .TP CDUP Changes your working directory one level up. .TP PWD Displays your working directory. .TP BACK Returns to your previous working directory. .TP MKDIR [ directory ] Creates a directory. .TP RMDIR [ directory ] Removes a directory. .RE .SS Making Connections .RS .TP SET LINE [ options ] devicename Opens the named serial port. Synonym: SET PORT. .TP OPEN LINE [ options ] devicename Same as SET LINE. Synonym: OPEN PORT. .TP SET MODEM TYPE [ name ] Tells Kermit what kind of modem is on the port. .TP DIAL [ number ] Tells Kermit to dial the given phone number with the modem. .TP REDIAL Redials the most recently dialed phone number. .TP ANSWER Waits for and answers an incoming call on the modem. .TP AUTHENTICATE [ parameters... ] Performs secure authentication on a TCP/IP connection. .TP SET NETWORK TYPE { TCP/IP, X.25, ... } Selects network type for subsequent SET HOST commands. .TP SET HOST [ options ] host [ port ] Opens a network connection to the given host and port. .TP SET HOST * port Waits for an incoming TCP/IP connection on the given port. .TP TELNET [ options ] host Opens a Telnet connection to the host and enters Connect state. .TP RLOGIN [ options ] host Opens an Rlogin connection to the host and enters Connect state. .TP IKSD [ options ] host Opens a connection to an Internet Kermit Service. .TP SSH [ options ] host Opens an SSH connection to the host and enters Connect state. .TP FTP OPEN host [ options ] Opens an FTP connection to the host. .TP HTTP [ options ] OPEN host Opens an HTTP connection to the host. .TP PTY external\(hycommand Runs the command on a pseudoterminal as if it were a connection. .TP PIPE external\(hycommand Runs the command through a pipe as if it were a connection. .RE .SS Using Connections .RS .TP CONNECT [ options ] Enters Connect (terminal) state. Synonym: C. .TP REDIRECT command Redirects the given external command over the connection. .TP TELOPT command Sends a Telnet protocol command (Telnet connections only). .TP Ctrl\-\eC "Escapes back" from Connect state to Command state. .TP Ctrl\-\eB (In Connect state) Sends a BREAK signal (serial or Telnet). .TP Ctrl\-\e! (In Connect state) Enters inferior shell; "exit" to return. .TP Ctrl\-\e? (In Connect state) Shows a menu of other escape\(hylevel options. .TP Ctrl\-\eCtrl\-\e (In Connect state) Type two Ctrl\-Backslashes to send one of them. .TP SET ESCAPE [ character ] Changes Kermit's Connect\(hystate escape character. .RE .SS Closing Connections .RS .TP HANGUP Hangs up the currently open serial\(hyport or network connection. .TP CLOSE Closes the currently open serial\(hyport or network connection. .TP SET LINE (with no devicename) Closes the currently open serial\(hyport or network connection. .TP SET HOST (with no hostname) Closes the currently open serial\(hyport or network connection. .TP FTP CLOSE Closes the currently open FTP connection. .TP HTTP CLOSE Closes the currently open HTTP connection. .TP EXIT Also closes all connections. Synonym: QUIT. .TP SET EXIT WARNING OFF Suppresses warning about open connections on exit or close. .RE .SS File Transfer .RS .TP SEND [ options ] filename [ as\(hyname ] Sends the given file. Synonym: S. .TP SEND [ options ] filespec Sends all files that match. .TP RESEND [ options ] filespec Resumes an interrupted SEND from the point of failure. .TP RECEIVE [ options ] [ as\(hyname ] Waits passively for files to arrive. Synonym: R. .TP LOG TRANSACTIONS [ filename ] Keeps a record of file transfers. .TP FAST Use fast file\(hytransfer settings (default). .TP CAUTIOUS Use cautious and less fast file\(hytransfer settings. .TP ROBUST Use ultra\(hyconservative and slow file\(hytransfer settings. .TP STATISTICS [ options ] Gives statistics about the most recent file transfer. .TP WHERE After transfer: "Where did my files go?". .TP TRANSMIT [ options ] [ofilename ] Sends file without protocol. Synonym: XMIT. .TP LOG SESSION [ filename ] Captures remote text or files without protocol. .TP SET PROTOCOL [ name... ] Tells Kermit to use an external file\(hytransfer protocol. .TP FTP { PUT, MPUT, GET, MGET, ... } FTP client commands. .TP HTTP { PUT, GET, HEAD, POST, ... } HTTP client commands. .RE .SS Kermit Server .RS .TP ENABLE, DISABLE Controls which server features can be used by clients. .TP SET SERVER Sets parameters prior to entering Server state. .TP SERVER Enters Server state. .RE .SS Client of Kermit or FTP Server .RS .TP [ REMOTE ] LOGIN [ user password ] Logs in to a Kermit server or IKSD that requires it. .TP [ REMOTE ] LOGOUT Logs out from a Kermit server or IKSD. .TP SEND [ options ] filename [ as\(hyname ] Sends the given file to the server. Synonyms: S, PUT. .TP SEND [ options ] filespec Sends all files that match. .TP RESEND [ options ] filespec Resumes an interrupted SEND from the point of failure. .TP GET [ options ] remote\(hyfilespec Asks the server to send the given files. Synonym: G. .TP REGET [ options ] remote\(hyfilespec Resumes an interrupted GET from the point of failure. .TP REMOTE CD [ directory ] Asks server to change its working directory. Synonym: RCD. .TP REMOTE PWD [ directory ] Asks server to display its working directory. Synonym: RPWD. .TP REMOTE DIRECTORY [ filespec... ] Asks server to send a directory listing. Synonym: RDIR. .TP REMOTE DELETE [ filespec... ] Asks server to delete files. Synonym: RDEL. .TP REMOTE [ command... ] (Many other commands: "remote ?" for a list). .TP MAIL [ options ] filespec Sends file(s) to be delivered as e\(hymail (Kermit only). .TP FINISH Asks the server to exit server state (Kermit only). .TP BYE Asks the server to log out and close the connection. .RE .SS Script Programming .PP .RS DEFINE, DECLARE, UNDEFINE, UNDECLARE, ASSIGN, EVALUATE, SEXPRESSION, ARRAY, SORT, INPUT, OUTPUT, IF, FOR, WHILE, SWITCH, GOTO, ECHO, ASK, GETC, GETOK, ASSERT, WAIT, SLEEP, FOPEN, FREAD, FWRITE, FCLOSE, STOP, END, RETURN, LEARN, SHIFT, TRACE, VOID, INCREMENT, DECREMENT, ... For these and many more you'll need to consult the manual and supplements, and/or visit the Kermit Script Library, which also includes a brief tutorial. Hint: HELP LEARN to find out how to get Kermit to write simple scripts for you. .RE .PP Many of Kermit's commands have synonyms, variants, relatives, and so on. For example, MSEND is a version of SEND that accepts a list of file specifications to be sent, rather than just one file specification, and MPUT is a synonym of MSEND. MOVE means to SEND and then DELETE the source file if successful. MMOVE is like MOVE, but accepts a list of filespecs, and so on. These are described in the full documentation. .PP Use question mark to feel your way through an unfamiliar command, as in this example: .PP .nf C\-Kermit> remote ? One of the following: assign directory kermit print rmdir cd exit login pwd set copy help logout query space delete host mkdir rename type C\-Kermit> remote set ? One of the following: attributes file retry transfer block\-check receive server window C\-Kermit> remote set file ? One of the following: character\-set incomplete record\-length collision names type C\-Kermit> remote set file names ? One of the following: converted literal C\-Kermit> remote set file names literal C\-Kermit> .PP .fi This is called menu on demand: you get a menu when you want one, but menus are not forced on you even when know what you're doing. Note that you can also abbreviate most keywords, and you can complete them with the Tab or Esc key. Also note that ? works for filenames too, and that you can use it in the middle of a keyword or filename, not just at the beginning. For example, "send x?" lists all the files in the current directory whose names start with 'x'. .SH INITIALIZATION FILE In its default configuration, C\(hyKermit executes commands from a file called .kermrc in your home directory when it starts, unless it is given the \-Y or \-y command\(hyline option. Custom configurations might substitute a shared system\(hywide initialization file. The SHOW FILE command tells what initialization file, if any, was used. The standard initialization file "chains" to an individual customization file, .mykermc, in the home directory, in which each user can establish her/his own preferences, define macros, and so on. .PP Since execution of the initialization file (at least the standard one) makes C\(hyKermit take longer to start, it might be better not to have an initialization file, especially now that Kermit's default startup configuration is well attuned to modern computing and networking \(hy\(hy in other words, you no longer have do anything special to make Kermit transfers go fast. So instead of having an initialization file that is executed every time Kermit starts, you might consider making one or more kerbang scripts (with names other that .kermrc) that do NOT include an "exit" command, and invoke those when you need the settings, macro definitions, and/or scripted actions they contain, and invoke C\(hyKermit directly when you don't. .PP To put it another way... We still distribute the standard initialization file since it's featured in the manual and backwards compatibility is important to us. But there's no harm in not using it if you don't need the stuff that's in it (services directory, dialing directory, network directory, and associated macro definitions). On the other hand, if there are settings or macros you want in effect EVERY time you use Kermit, the initialization file (or the customization file it chains to) is the place to put them, because that's the only place Kermit looks for them automatically each time you start it. .SH MODES OF OPERATION Kermit is said to be in Local mode if it has made a connection to another computer, e.g. by dialing it or establishing a Telnet connection to it. The other computer is remote, so if you start another copy of Kermit on the remote computer, it is said to be in Remote mode (as long as it has not made any connections of its own). The local Kermit communicates over the communications device or network connection, acting as a conduit between the the remote computer and your keyboard and screen. The remote Kermit is the file\(hytransfer partner to the local Kermit and communicates only through its standard input and output. .PP At any moment, a Kermit program can be in any of the following states. It's important to know what they are and how to change from one to the other. .TP Command state In this state, Kermit reads commands from: .sp \(bu Your keyboard; or: .br \(bu A file, or: .br \(bu A macro definition. .sp You can exit from Command state back to Unix with the EXIT or QUIT command (same thing). You can enter Connect state with any of various commands (CONNECT, DIAL, TELNET, etc). You can enter file transfer state with commands like SEND, RECEIVE, and GET. You can enter Server state with the SERVER command. The TAKE command tells Kermit to read and execute commands from a file. The (perhaps implied) DO command tells Kermit to read and execute commands from a macro definition. While in Command state, you can interrupt any command, macro, or command file by typing Ctrl\-C (hold down the Ctrl key and press the C key); this normally brings you back to the prompt. .TP Shell state You can invoke an inferior shell or external command from the Kermit command prompt by using the PUSH, RUN (!), EDIT, or BROWSE command. While the inferior shell or command is active, Kermit is suspended and does nothing. Return to Kermit Command state by exiting from the inferior shell or application. .TP Connect state In this state, which can be entered only when in Local mode (i.e. when Kermit has made a connection to another computer), Kermit is acting as a terminal to the remote computer. Your keystrokes are sent to the remote computer and characters that arrive over the communication connection are displayed on your screen. This state is entered when you give a CONNECT, DIAL, TELNET, RLOGIN, or IKSD command. You can return to command state by logging out of the remote computer, or by typing: .sp Ctrl\-\ec .sp That is: Hold down the Ctrl key and press the backslash key, then let go of the Ctrl key and press the C key. This is called escaping back. Certain other escape\(hylevel commands are also provided; type Ctrl\-\e? for a list. For example, you can enter Shell state with: .sp Ctrl\-\e! .sp To send a Ctrl\-\e to the host while in Connect state, type two of them in a row. See HELP CONNECT and HELP SET ESCAPE for more info. .TP Local file\(hytransfer state In this state, Kermit is sending packets back and forth with the other computer in order to transfer a file or accomplish some other file\(hyrelated task. And at the same time, it is displaying its progress on your screen and watching your keyboard for interruptions. In this state, the following single\(hykeystroke commands are accepted: .sp .RS .TP X Interrupt the current file and go on to the next (if any). .TP Z Interrupt the current file and skip all the rest. .TP E Like Z but uses a "stronger" protocol (use if X or Z don't work). .TP Ctrl\-C Interrupt file\(hytransfer mode (use if Z or E don't work). .sp .RE Kermit returns to its previous state (Command or Connect) when the transfer is complete or when interrupted successfully by X, Z, E, or Ctrl\-C (hold down the Ctrl key and press the C key). .TP Remote file\(hytransfer state In this state, Kermit is exchanging file\(hytransfer packets with its local partner over its standard i/o. It leaves this state automatically when the transfer is complete. In case you find your local Kermit in Connect state and the remote one in File\(hytransfer state (in which it seems to ignore your keystrokes), you can usually return it to command state by typing three Ctrl\-C's in a row. If that doesn't work, return your local Kermit to Command state (Ctrl\-\e C) and type "e\(hypacket" and then press the Return or Enter key; this forces a fatal Kermit protocol error. .TP Remote Server state This is like Remote File\(hytransfer state, except it never returns automatically to Command state. Rather, it awaits further instructions from the client program; that is, from your Local Kermit program. You can return the Remote Server to its previous state by issuing a "finish" command to the client, or if you are in Connect state, by typing three Ctrl\-C's in a row. You can tell the server job to log out and break the connection by issuing a "bye" command to the client. .TP Local Server state Like Remote\(hyServer state, but in local mode, and therefore with its file\(hytransfer display showing, and listening for single\(hykey commands, as in Local File\(hytransfer state. Usually this state is entered automatically when a remote Kermit program gives a GET command. .sp C\(hyKermit, Kermit 95, and MS\(hyDOS Kermit all can switch automatically from Connect state to Local File\(hytransfer state when you initiate a file transfer from the remote computer by starting Kermit and telling it to send or get a file, in which case, Connect state is automatically resumed after the file transfer is finished. .sp Note that C\(hyKermit is not a terminal emulator. It is a communications application that you run in a terminal window (e.g. console or Xterm). The specific emulation, such as VT100, VT220, Linux Console, or Xterm, is provided by the terminal window in which you are running C\(hyKermit. Kermit 95 and MS\(hyDOS Kermit, on the other hand, are true terminal emulators. Why is C\(hyKermit not a terminal emulator? CLICK HERE to read about it. .SH MAKING CONNECTIONS Here is how to make different kinds of connections using interactive Kermit commands (as noted above, you can also make connections with command\(hyline options). Note that you don't have to make connections with Kermit. It can also be used on the far end of a connection as the remote file transfer and management partner of your local communications software. .TP Making a Telnet Connection At the C\(hyKermit command prompt, simply type: .sp .nf telnet foo.bar.com .fi .sp (substituting desired hostname or address). You can also include a port number: .sp .nf telnet xyzcorp.com 3000 ; .fi .sp If the connection is successful, Kermit automically enters Connect state. When you logout from the remote host, Kermit automatically returns to its prompt. More info: HELP TELNET, HELP SET TELNET, HELP SET TELOPT. Also see the IKSD section below. .TP Making an Rlogin connection This is just like Telnet, except you have to be root to do it because Rlogin uses a privileged TCP port: .sp .nf rlogin foo.bar.com .fi .sp More info: HELP RLOGIN. .TP Making an SSH Connection Unlike Telnet and Rlogin, SSH connections are not built\(hyin, but handled by running your external SSH client through a pseudoterminal. Using C\(hyKermit to control the SSH client gives you all of Kermit's features (file transfer, character\(hyset conversion, scripting, etc) over SSH. .sp ssh foo.bar.com .sp More info: HELP SSH, HELP SET SSH. .TP Dialing with a Modem If it's an external modem, make sure it is connected to a usable serial port on your computer with a regular (straight\(hythrough) modem cable, and to the telephone jack with a telephone cable, and that it's turned on. Then use these commands: .sp .nf set modem type usrobotics ; Or other supported type set line /dev/ttyS0 ; Specify device name set speed 57600 ; Or other desired speed set flow rts/cts ; Most modern modems support this set dial method tone ; (or pulse) dial 7654321 ; Dial the desired number .fi .sp Type "set modem type ?" for a list of supported modem types. If you omit the SET MODEM TYPE command, the default type is "generic\(hyhigh\(hyspeed", which should work for most modern AT\(hycommand\(hyset modems. If the line is busy, Kermit redials automatically. If the call does not succeed, use "set dial display on" and try it again to watch what happens. If the call succeeds, Kermit enters Connect state automatically and returns to its prompt automatically when you log out from the remote computer or the connection is otherwise lost. .sp You can also dial from a modem that is accessible by Telnet, e.g. to a reverse terminal server. In this case the command sequence is: .sp .nf set host ts.xxx.com 2000 ; Terminal\(hyserver and port set modem type usrobotics ; Or other supported type set dial method tone ; (or pulse) dial 7654321 ; Dial the desired number .fi .sp If the terminal server supports the Telnet Com Port Option, RFC 2217, you can also give serial\(hyport related commands such as SET SPEED, SET PARITY, and so on, and Kermit relays them to the terminal server using the protocol specified in the RFC. .sp More info: HELP SET MODEM, HELP SET LINE, HELP SET SPEED, HELP SET FLOW, HELP DIAL, HELP SET DIAL, HELP SET MODEM, HELP SET CARRIER\-WATCH, SHOW COMMUNICATIONS, SHOW MODEM, SHOW DIAL. .TP Direct Serial Port Connect the two computers, A and B, with a null modem cable (or two modem cables interconnected with a null\(hymodem adapter or modem eliminator). From Computer A: .sp .nf set modem type none ; There is no modem set line /dev/ttyS0 ; Specify device name set carrier\-watch off ; If DTR CD are not cross\(hyconnected set speed 57600 ; Or other desired speed set flow rts/cts ; If RTS and CTS are cross\(hyconnected set parity even ; (or "mark" or "space", if necessary) set stop\-bits 2 ; (rarely necessary) set flow xon/xoff ; If you can't use RTS/CTS connect ; Enter Connect (terminal) state .fi .sp This assumes Computer B is set up to let you log in. If it isn't, you can run a copy of Kermit on Computer B and follow approximately the same directions. More info: As above plus HELP CONNECT. .PP With modems or direct serial connections, you might also have to "set parity even" (or "mark" or "space") if it's a 7\(hybit connection. .PP Of the connection types listed above, only one can be open at a time. However, any one of these can be open concurrently with an FTP or HTTP session. Each connection type can be customized to any desired degree, scripted, logged, you name it. See the manual. .PP NOTE: On selected platforms, C\(hyKermit also can make X.25 connections. See the manual for details. .SH TRANSFERRING FILES WITH KERMIT There is a widespread and persistent belief that Kermit is a slow protocol. This is because, until recently, it used conservative tuning by default to make sure file transfers succeeded, rather than failing because they overloaded the connection. Some extra commands (or command\(hyline options, like \-Q) were needed to make it go fast, but nobody bothered to find out about them. Also, it takes two to tango: most non\(hyKermit\(hyProject Kermit protocol implementations really ARE slow. The best file\(hytransfer partners for C\(hyKermit are: another copy of C\(hyKermit (7.0 or later) and Kermit 95. These combinations work well and they work fast by default. MS\(hyDOS Kermit is good too, but you have to tell it to go fast (by giving it the FAST command). .PP Furthermore, all three of these Kermit programs support "autodownload" and "autoupload", meaning that when they are in Connect state and a Kermit packet comes in from the remote, they automatically switch into file transfer mode. .PP And plus, C\(hyKermit and K95 also switch automatically between text and binary mode for each file, so there is no need to "set file type binary" or "set file type text", or to worry about files being corrupted because they were transferred in the wrong mode. .PP What all of these words add up to is that now, when you use up\(hyto\(hydate Kermit software from the Kermit Project, file transfer is not only fast, it's ridiculously easy. You barely have to give any commands at all. .TP Downloading Files Let's say you have Kermit 95, C\(hyKermit, or MS\(hyDOS Kermit on your desktop computer, with a connection to a Unix computer that has C\(hyKermit installed as "kermit". To download a file (send it from Unix to your desktop computer), just type the following command at your Unix shell prompt: .sp kermit \-s oofa.txt .sp (where oofa.txt is the filename). If you want to send more than one file, you can put as many filenames as you want on the command line, and they can be any combination of text and binary: .sp kermit \-s oofa.txt oofa.zip oofa.html oofa.tar.gz .sp and/or you can use wildcards to send groups of files: .sp kermit \-s oofa.* .sp If you want to send a file under an assumed name, use: .sp kermit \-s friday.txt \-a today.txt .sp This sends the file friday.txt but tells the receiving Kermit that its name is today.txt. In all cases, as noted, when the file transfer is finished, your desktop Kermit returns automatically to Connect state. No worries about escaping back, re\(hyconnecting, text/binary mode switching. Almost too easy, right? .TP Uploading Files To upload files (send them from your desktop computer to the remote Unix computer) do the same thing, but use the \-g (GET) option instead of \-s: .sp kermit \-g oofa.txt .sp This causes your local Kermit to enter server mode; then the remote Kermit program requests the named file and the local Kermit sends it and returns automatically to Connect state when done. .sp If you want to upload multiple files, you have have use shell quoting rules, since these aren't local files: .sp .nf kermit \-g "oofa.txt oofa.zip oofa.html oofa.tar.gz" kermit \-g "oofa.*" .fi .sp If you want to upload a file but store it under a different name, use: .sp kermit \-g friday.txt \-a today.txt .TP Kermit Transfers the Old\(hyFashioned Way If your desktop communications software does not support autoupload or autodownload, or it does not include Kermit server mode, the procedure requires more steps. .sp To download a file, type: .sp kermit \-s filename .sp on the host as before, but if nothing happens automatically in response to this command, you have to switch your desktop communications software into Kermit Receive state. This might be done by escaping back using keyboard characters or hot keys (Alt\-x is typical) and/or with a command (like RECEIVE) or a menu. When the file transfer is complete, you have to go back to Connect state, Terminal emulation, or whatever terminology applies to your desktop communications software. .sp To upload a file, type: .sp kermit \-r .sp on the host (rather than "kermit \-g"). This tells C\(hyKermit to wait passively for a file to start arriving. Then regain the attention of your desktop software (Alt\-x or whatever) and instruct it to send the desired file(s) with Kermit protocol. When the transfer is finished, return to the Connect or Terminal screen. .TP If File Transfer Fails Although every aspect of Kermit's operation can be finely tuned, there are also three short and simple "omnibus tuning" commands you can use for troubleshooting: .RS .TP FAST Use fast file\(hytransfer settings. This has been the default since C\(hyKermit 7.0 now that most modern computers and connections support it. If transfers fail with fast settings, try . . . .TP CAUTIOUS Use cautious but not paranoid settings. File transfers, if they work, will go at medium speed. If not, try . . . .TP ROBUST Use the most robust, resilient, conservative, safe, and reliable settings. File transfers will almost certainly work, but they will be quite slow (of course this is a classic tradeoff; ROBUST was C\(hyKermit's default tuning in versions 6.0 and earlier, which made everybody think Kermit protocol was slow). If ROBUST doesn't do the trick, try again with SET PARITY SPACE first in case it's not an 8\(hybit connection. .RE .sp Obviously the success and performance of a file transfer also depends on C\(hyKermit's file transfer partner. Up\(hyto\(hydate, real Kermit Project partners are recommended because they contain the best Kermit protocol implementations and because we can support them in case of trouble. .sp If you still have trouble, consult Chapter 10 of Using C\(hyKermit, or send email to kermit\(hysupport@columbia.edu. .TP Advanced Kermit File\(hyTransfer Features Obviously there is a lot more to Kermit file transfer, including all sorts of interactive commands, preferences, options, logging, debugging, troubleshooting, and anything else you can imagine but that's what the manual and updates are for. Here are a few topics you can explore if you're interested by Typing HELP for the listed commands: .RS .TP Logging transfers: LOG TRANSACTIONS (HELP LOG) .TP Automatic per\(hyfile text/binary mode switching: SET TRANSFER MODE { AUTOMATIC, MANUAL } (HELP SET TRANSFER). .TP Cross\(hyplatform recursive directory tree transfer: SEND /RECURSIVE, GET /RECURSIVE (HELP SEND, HELP GET). .TP File collision options: SET FILE COLLISION { OVERWRITE, BACKUP, DISCARD, ... } (HELP SET FILE). .TP Update: Transfer only files that changed since last time: SET FILE COLLISION UPDATE (HELP SET FILE). .TP Filename selection patterns: (HELP WILDCARD). .TP Flexible file selection: SEND (or GET) /BEFORE /AFTER /LARGER /SMALLER /TYPE /EXCEPT, ... .TP Character\(hyset conversion: SET { FILE, TRANSFER } CHARACTER\-SET, ASSOCIATE, ... .TP File/Pathname control: SET { SEND, RECEIVE } PATHNAMES, SET FILE NAMES. .TP Atomic file movement: SEND (or GET) /DELETE /RENAME /MOVE\-TO .TP Transferring to/from standard i/o of other commands: SEND (or GET) /COMMAND .TP Recovery of interrupted transfer from point of failure: RESEND, REGET (HELP RESEND, HELP REGET). .RE .TP Non\(hyKermit File Transfer You can also use C\(hyKermit to transfer files with FTP or HTTP Internet protocols; see below. .sp On a regular serial or Telnet connection where the other computer doesn't support Kermit protocol at all, you have several options. For example, if your desktop communications software supports Zmodem, use "rz" and "sz" on the host rather than Kermit. But if Kermit is your desktop software, and you are using it to make calls or network connections to other computers that don't support Kermit protocol (or that don't have a good implementation of it), then if your computer also has external X, Y, or Zmodem programs that are redirectable, Kermit can use them as external protocols. HELP SET PROTOCOL for details. .sp You can also capture "raw" data streams from the other computer with LOG SESSION (HELP LOG and HELP SET SESSION\-LOG for details), and you can upload files without any protocol at all with TRANSMIT (HELP TRANSMIT, HELP SET TRANSMIT). .SH KERMIT'S BUILT\(hyIN FTP AND HTTP CLIENTS Kermit's FTP client is like the regular Unix FTP client that you're used to, but with some differences: .TP \(bu It has lots more commands and features. .TP \(bu Each FTP command must be prefixed with "ftp", for example "ftp open", "ftp get", "ftp bye", etc (this is not strictly true, but until you're more familiar with it, it's best to follow this rule). .TP \(bu Commands like "cd", "directory", etc, execute locally, not on the server. Use "ftp cd", "ftp dir", etc, to have them act on the server. .TP \(bu You can have an FTP session and a regular Kermit serial or Telnet session open at the same time. .TP \(bu FTP sessions can be fully automated. .PP Pending publication of the next edition of the manual, the Kermit FTP client is thoroughly documented at the Kermit Project website: .sp http://www.columbia.edu/kermit/ftpclient.html .sp You also can use HELP FTP and HELP SET FTP to get descriptions of Kermit's FTP\(hyrelated commands. .PP The HTTP client is similar to the FTP one, except you prefix each command with HTTP instead of FTP: HTTP OPEN, HTTP GET, HTTP PUT, HTTP CLOSE, etc. Type HELP HTTP for details, or visit the to view the manual supplements. HTTP connections can be open at the same time as regular serial or Telnet connections and FTP connections. So Kermit can manage up to three types connections simultaneously. .SH INTERNET KERMIT SERVICE C\(hyKermit can be configured and run as an Internet service (called IKSD), similar to an FTP server (FTPD) except you can (but need not) interact with it directly, plus it does a lot more than an FTP server can do. The TCP port for IKSD is 1649. It uses Telnet protocol. C\(hyKermit can be an Internet Kermit Server, or it can be a client of an IKSD. You can make connections from C\(hyKermit to an IKSD with any of the following commands: .sp .nf telnet foo.bar.edu 1649 telnet foo.bar.edu kermit ; if "kermit" is listed in /etc/services iksd foo.bar.edu .fi .sp The IKSD command is equivalent to a TELNET command specifying port 1649. For more information about making and using connections to an IKSD, see: .sp http://www.columbia.edu/kermit/cuiksd.html .sp You can run an Internet Kermit Service on your own computer too (if you are the system administrator). For instructions, see: .sp http://www.columbia.edu/kermit/iksd.html .SH SECURITY All of C\(hyKermit's built\(hyin TCP/IP networking methods (Telnet, Rlogin, IKSD, FTP, and HTTP) can be secured by one or more of the following IETF\(hyapproved methods: .PP \(bu MIT Kerberos IV .br \(bu MIT Kerberos V .br \(bu SSL/TLS .br \(bu Stanford SRP .PP For complete instructions see: .PP http://www.columbia.edu/kermit/security.html .PP And as noted previously, you can also make SSH connections with C\(hyKermit if you already have an SSH client installed. .SH ALTERNATIVE COMMAND\(hyLINE PERSONALITIES When invoked as "kermit" or any other name besides "ftp" or "telnet", C\(hyKermit has the command\(hyline options described above in the OPTIONS section. However, if you invoke C\(hyKermit as "telnet" or "ftp", it changes its command\(hyline personality to match. This can be done (among other ways) with symbolic links (symlinks). For example, if you want C\(hyKermit to be your regular Telnet client, or the Telnet helper of your Web browser, you can create a link like the following in a directory that lies in your PATH ahead of the regular telnet program: .sp ln \-s /usr/local/bin/kermit telnet .sp Now when you give a "telnet" command, you are invoking Kermit instead, but with its Telnet command\(hyline personality so, for example: .sp telnet xyzcorp.com .sp Makes a Telnet connection to xyzcorp.com, and Kermit exits automatically when the connection is closed (just like the regular Telnet client). Type "telnet \-h" to get a list of Kermit's Telnet\(hypersonality command\(hyline options, which are intended to be as compatible as possible with the regular Telnet client. .PP Similarly for FTP: .sp ln \-s /usr/local/bin/kermit ftp .sp And now type "ftp \-h" to see its command\(hyline options, and command lines just like you would give your regular FTP client: .sp ftp xyzcorp.com .sp but with additional options allowing an entire session to be specified on the command line. Finally, if Kermit's first command\(hyline option is a Telnet, FTP, IKSD, or HTTP URL, Kermit automatically makes the appropriate kind of connection and, if indicated by the URL, takes the desired action: .TP kermit telnet:xyzcorp.com Opens a Telnet session .TP kermit telnet://olga@xyzcorp.com Ditto for user olga .TP kermit ftp://olga@xyzcorp.com/public/oofa.zip Downloads a file .TP kermit kermit://kermit.columbia.edu/kermit/f/READ.ME Ditto for IKSD .TP kermit iksd://kermit.columbia.edu/kermit/f/READ.ME (This works too) .TP kermit http://www.columbia.edu/kermit/index.html Grabs a web page .fi .SH LICENSE C\(hyKermit has an unusual license, but a fair and sensible one since the Kermit Project must support itself out of revenue: it's not a BSD license, not GPL, not Artistic, not commercial, not shareware, not freeware. It can be summed up like this: if you want C\(hyKermit for your own use, you can download and use it without cost or license (but we'd appreciate it if you would purchase the manual). But if you want to sell C\(hyKermit or bundle it with a product or otherwise distribute it in a commercial setting EXCEPT WITH AN OPEN\(hySOURCE OPERATING SYSTEM DISTRIBUTION such as Linux, FreeBSD, NetBSD, or OpenBSD, you must license it. To see the complete license, give the LICENSE command at the prompt, or see the COPYING.TXT file distributed with C\(hyKermit 7.0 or later, or download it from .sp ftp://kermit.columbia.edu/kermit/c\-kermit/COPYING.TXT .sp Send licensing inquiries to kermit@columbia.edu. .SH BUGS See the following files for listings of known bugs, limitations, workarounds, hints and tips: .TP ckcbwr.txt General C\(hyKermit bugs, hints, tips. .TP ckubwr.txt Unix\(hyspecific C\(hyKermit bugs, hints, tips. .PP Report bugs and problems by email to: .sp kermit\-support@columbia.edu. .sp Before requesting technical support, please read the hints here: .sp http://www.columbia.edu/kermit/support.html .sp and also read the C\(hyKermit Frequently Asked Questions: .sp http://www.columbia.edu/kermit/ckfaq.html .SH OTHER TOPICS There's way more to C\(hyKermit than we've touched on here \(hy\(hy troubleshooting, customization, character sets, dialing directories, sending pages, script writing, and on and on, all of which are covered in the manual and updates and supplements. For the most up\(hyto\(hydate information on documentation (or updated documentation itself) visit the Kermit Project website: .sp http://www.columbia.edu/kermit/ .PP There you will also find Kermit software packages for other platforms: different Unix varieties, Windows, DOS, VMS, IBM mainframes, and many others: 20+ years' worth. .SH DOCUMENTATION AND UPDATES The manual for C\(hyKermit is: .TP .I Using C\(hyKermit Frank da Cruz and Christine M. Gianone, Second Edition, Digital Press / Butterworth\(hyHeinemann, Woburn, MA, 1997, 622 pages, ISBN 1\-55558\-164\-1. This is a printed book. It covers C\(hyKermit 6.0. .TP The C\(hyKermit 7.0 Supplement http://www.columbia.edu/kermit/ckermit70.html .TP The C\(hyKermit 8.0 Supplement http://www.columbia.edu/kermit/ckermit80.html .TP The C\(hyKermit 9.0 Supplement http://www.columbia.edu/kermit/ckermit90.html .PP Visit C\(hyKermit home page: .sp http://www.columbia.edu/kermit/ckermit.html .sp to learn about new versions, Beta tests, and other news; to read case studies and tutorials; to download source code, install packages, and prebuilt binaries for many platforms. Also visit: .TP http://www.columbia.edu/kermit/scriptlib.html The Kermit script library and tutorial .TP http://www.columbia.edu/kermit/newfaq.html The Kermit FAQ (Frequently Asked Questions about Kermit) .TP http://www.columbia.edu/kermit/ckfaq.html The C\(hyKermit FAQ (Frequently Asked Questions about C\(hyKermit) .TP http://www.columbia.edu/kermit/telnet.html C\(hyKermit Telnet client documentation .TP http://www.columbia.edu/kermit/security.html C\(hyKermit security documentation (Kerberos, SSL/TLS, etc) .TP http://www.columbia.edu/kermit/cuiksd.html Internet Kermit Service user documentation .TP http://www.columbia.edu/kermit/iksd.html Internet Kermit Service administrator documentation .TP http://www.columbia.edu/kermit/studies.html Case studies. .TP http://www.columbia.edu/kermit/support.html Technical support. .TP http://www.columbia.edu/kermit/k95tutorial.html Kermit 95 tutorial. .TP comp.protocols.kermit.misc The Kermit newsgroup (unmoderated). .SH FILES .TP COPYING.TXT C\(hyKermit license. .TP ~/.kermrc Initialization file. .TP ~/.mykermrc Customization file. .TP ~/.kdd Kermit dialing directory (see manual). .TP ~/.knd Kermit network directory (see manual). .TP ~/.ksd Kermit services directory (see manual). .TP ca_certs.pem Certificate Authority certifcates used for SSL connections. .TP ckuins.txt Installation instructions for Unix. Also at http://www.columbia.edu/kermit/ckuins.html. .TP ckcbwr.txt General C\(hyKermit bugs, hints, tips. .TP ckubwr.txt Unix\(hyspecific C\(hyKermit bugs, hints, tips. .TP ckcplm.txt C\(hyKermit program logic manual. .TP ckccfg.txt C\(hyKermit compile\(hytime configuration options. .TP ssh (in your PATH) SSH connection helper. .TP rz, sz, etc. (in your PATH) external protocols for XYZmodem. .TP /var/spool/locks (or whatever) UUCP lockfile for dialing out (see installation instructions). .SH AUTHORS .TP Software Frank da Cruz and Jeffrey E Altman, .br 1985\(hypresent, with contributions from hundreds of others all over the world. .TP Documentation Frank da Cruz .TP Address .nf The Kermit Project \(hy Columbia Univerity 612 West 115th Street New York NY 10025\-7799 USA .fi .TP E\(hyMail kermit@columbia.edu .TP Web http://www.columbia.edu/kermit/ .fi .br ckututor.txt0000664000015300001460000025224711624230415012316 0ustar fdckermit [1]The Columbia Crown The Kermit Project | Columbia University 612 West 115th Street, New York NY 10025 USA o [2]kermit@columbia.edu ...since 1981 [3]Home [4]Kermit 95 [5]C-Kermit [6]Scripts [7]Current [8]New [9]FAQ [10]Support C-KERMIT 9.0 UNIX MANUAL PAGE AND TUTORIAL Frank da Cruz [11]The Kermit Project, [12]Columbia University [ [13]PDF version ] [ [14]Nroff version ] This document is intended to give the beginner sufficient information to make basic (if not advanced) use of C-Kermit 9.0. Although it might be rather long for a Unix manual page (about 1600 lines), it's still far shorter than the [15]C-Kermit manual, which should be consulted for advanced topics such as customization, character-sets, scripting, etc. We also attempt to provide a clear structural overview of C-Kermit's many capabilities, functional areas, states, and modes and their interrelation, that should be helpful to beginners and veterans alike, as well as to those upgrading to the new release. Thanks to Christine Gianone for her work on this document before she was laid off in 2005. Most recent update: Fri Jul 1 14:12:31 2011 CONTENTS * [16]DESCRIPTION * [17]SYNOPSIS * [18]OPTIONS * [19]COMMAND LANGUAGE * [20]INITIALIZATION FILE * [21]MODES OF OPERATION * [22]MAKING CONNECTIONS * [23]TRANSFERRING FILES WITH KERMIT * [24]KERMIT CLIENT/SERVER CONNECTIONS * [25]KERMIT'S BUILT-IN FTP AND HTTP CLIENTS * [26]INTERNET KERMIT SERVICE * [27]SECURITY * [28]ALTERNATIVE COMMAND-LINE PERSONALITIES * [29]LICENSE * [30]OTHER TOPICS * [31]DOCUMENTATION AND UPDATES * [32]FILES * [33]AUTHORS DESCRIPTION [34]Top [35]Contents [36]Next [37]C-Kermit is an all-purpose communications software package from the [38]Kermit Project at [39]Columbia University that: * Is portable to many platforms, Unix and non-Unix alike. * Can make both serial and network connections. * Can conduct interactive terminal sessions over its connection. * Can transfer text or binary files over the same connection. * Can convert text-file character sets in terminal mode or file transfer. * Is customizable in every aspect of its operation. C-Kermit is a modem program, a Telnet client, an Rlogin client, an FTP client, an HTTP client, and on selected platforms, also an X.25 client. It can make its own secure Internet connections using IETF-approved security methods including Kerberos IV, Kerberos V, SSL/TLS, and SRP and it can also make SSH (Secure Shell) connections through your external SSH client application. It can be the far-end file-transfer or client/server partner of your desktop Kermit client. It can also accept incoming dialed and network connections. It can even be installed as an Internet service on its own standard TCP socket, 1649 [[40]RFC2839, [41]RFC2840]. And perhaps most important, everything you can do "by hand" (interactively) with C-Kermit, can be "scripted" (automated) using its built-in cross-platform transport-independent script programming language, which happens to be identical to its interactive command language. This manual page offers an overview of C-Kermit 9.0 for Unix ("Unix" is an operating system family that includes AIX, DG/UX, FreeBSD, HP-UX, IRIX, Linux, Mac OS X, NetBSD, OpenBSD, Open Server, Open Unix, QNX, Solaris, SunOS, System V R3, System V R4, Tru64 Unix, Unixware, Xenix, and many others). For thorough coverage, please consult the published C-Kermit manual and supplements (see [42]DOCUMENTATION below). For further information about C-Kermit, Kermit software for other platforms, and Kermit manuals, visit the Kermit Project website: [43]http://www.columbia.edu/kermit/ This is a longer-than-average manual page, and yet it barely scratches the surface. Don't be daunted. C-Kermit is a large and complex package, evolving over decades of practice and experience, but that doesn't mean it's hard to learn or use. Its most commonly used functions are explained here with pointers to additional information elsewhere. [44]Kermit Home [45]C-Kermit Home [46]C-Kermit FAQ SYNOPSIS [47]Top [48]Contents [49]Next [50]Previous Usage: kermit [filename] [-x arg [-x arg]...[-yyy]..] [ {=,--,+} text ] ] Or: kermit URL * -x is an option requiring an argument; * -y is an option with no argument. If the first command-line argument is the name of a file, interactive-mode commands are executed from the file. The '=' (or "--") argument tells Kermit not to parse the remainder of the command line, but to make the words following '=' available as \%1, \%2, ... \%9. The "+" argument is like "=" but for use in "kerbang scripts" (explained [51]below). A second command-line format allows the one and only argument to be a [52]Telnet, FTP, HTTP, or IKSD URL. Order of execution: 1. [53]The command file (if any). 2. [54]The initialization file, if any, unless suppressed with -Y. 3. [55]The customization file (if it is executed by the initialization file). 4. [56]The command-line URL (if any, and if so, execution stops here). 5. [57]Command-line options (if any). 6. [58]Interactive commands. Some command-line options can cause actions (such as -s to send a file); others just set parameters. If any action options are included on the command line, Kermit exits when finished unless also given the -S ("stay") option. If no action options are given, no initialization or command files contained an EXIT or QUIT command, and no fatal errors occurred, Kermit issues its prompt and waits for you to type commands. Bear in mind that C-Kermit can be built with selected features disabled, and also that certain features are not available on all platforms. For example, C-Kermit can't be built with TCP/IP support on a platform that does not have TCP/IP header files and libraries (and even if Kermit does include TCP/IP support, it can't be used to make TCP/IP connections on a computer that does not have a TCP/IP stack installed). If your version of C-Kermit lacks a feature mentioned here, use its SHOW FEATURES command to see what might have been excluded. C-Kermit has three kinds of commands: regular single-letter command-line options, extended-format command-line options, and interactive commands. [59]Kermit Home [60]C-Kermit Home [61]C-Kermit FAQ OPTIONS [62]Top [63]Contents [64]Next [65]Previous <- (Most people should click Next to skip around this section...) Like most Unix commands, C-Kermit can be be given options on the command line. But C-Kermit also can be used interactively by giving it [66]commands composed of words, which are more intuitive than cryptic command-line options, and more flexible too. In other words, you don't have to use C-Kermit's command-line options, but they are available if you want to. (By the same token, you don't have to use its interactive commands either -- you can use either or both in any combination.) C-Kermit is generally installed in the PATH as "kermit", and therefore is invoked by typing the word "kermit" (lowercase) at the shell prompt, and then pressing the Return or Enter key. If you wish to include command-line options, put them after the word "kermit" but before pressing Return or Enter, separated by spaces, for example: $ kermit -s ckermit.tar.gz ('$' is the shell prompt; "kermit -s ckermit.tar.gz" is what you type, followed by Return or Enter.) Here is a list of C-Kermit's single-letter command-line options, which start with a single dash (-), in ASCII ("alphabetical") order. Alphabetic case is significant (-A is not the same as -a). The Action? column contains Y for action options and N for non-action options. Option Action? Description -0 N (digit zero) 100% transparent Connect state for "in-the-middle" operation: 8 bits, no parity, no escape character, everything passes through. -8 N (digit eight) Connection is 8-bit clean (this is the default in C-Kermit 9.0). Equivalent to the EIGHTBIT command, which in turn is a shortcut for SET TERMINAL BYTESIZE 8, SET COMMAND BYTESIZE 8, SET PARITY NONE. -9 arg N (digit nine) Make a connection to an FTP server. Equivalent to the FTP OPEN command. Argument: IP-address-or-hostname[:optional-TCP-port]. NOTE: C-Kermit also has a separate FTP command-line personality, with regular FTP-like command-line syntax. [67]More about this below. -A N Kermit is to be started as an Internet service (IKSD) (only from inetd.conf). -B N Kermit is running in Batch or Background (no controlling terminal). To be used in case Kermit doesn't automatically sense its background status. Equivalent to the SET BACKGROUND ON command. -C arg N Interactive-mode Commands to be executed. Argument: Commands separated by commas, list in doublequotes. -D arg N Delay before starting to send in Remote mode. Equivalent to the SET DELAY command. Argument: Number of seconds. -E N Exit automatically when connection closes. Equivalent to SET EXIT ON-DISCONNECT ON. -F arg N Use an open TCP connection. Argument: Numeric file descriptor of open TCP connection. Also see: -j, -J. -G arg Y Get file(s) from server, send contents to standard output, which normally would be piped to another process. Argument: Remote file specification, in quotes if it contains metacharacters. Also see: -g, -k. -H N Suppress program startup Herald and greeting. -I N Tell Kermit it has a reliable connection, to force streaming to be used where it normally would not be. Equivalent to the SET RELIABLE ON command. -J arg N "Be like Telnet." Like -j but implies -E. Argument: IP hostname/address optionally followed by service. NOTE: C-Kermit also has a separate Telnet command-line personality, with regular Telnet-like command-line syntax. [68]More about this below. -L N Recursive directory descent for files in -s option. -M arg N My user name (for use with Telnet, Rlogin, FTP, etc). Equivalent to the SET LOGIN USER command. Argument: Username string. -O Y (Uppercase letter O) Be a server for One command only. Also see: -x. -P N Don't convert file (Path) names of transferred files. Equivalent to SET FILE NAMES LITERAL. -Q N Quick Kermit protocol settings. Equivalent to the FAST command. This is the default in C-Kermit 7.0 and later. -R N Remote-only (this just makes IF REMOTE true). -S N Stay (enter command parser after action options). -T N Force Text mode for file transfer; implies -V. Equivalent to SET TRANSFER MODE MANUAL, SET FILE TYPE TEXT. -V N Disable automatic per-file text/binary switching. Equivalent to SET TRANSFER MODE MANUAL. -Y N Skip (don't execute) the initialization file. -a arg N As-name for file(s) in -s, -r, or -g. Argument: As-name string (alternative filename). When receiving files, this can be a directory name. -b arg N Speed for serial device. Equivalent to SET SPEED. Argument: Numeric Bits per second for serial connections. -c Y Enter Connect state before transferring files. -d N Create a debug.log file with detailed debugging information (a second -d adds timestamps). Equivalent to LOG DEBUG but takes effect sooner. -e arg N Maximum length for incoming Kermit file-transfer packets. Equivalent to SET RECEIVE PACKET-LENGTH. Argument: Length in bytes. -f Y Send a FINISH command to a Kermit server. -g arg N Get file(s) from a Kermit server. Argument: File specification on other computer, in quotes if it contains metacharacters. Equivalent to GET. Also see: -a, -G, -r. -h Y Print Help text for single-letter command-line options (pipe thru 'more' to prevent scrolling). -i N Force binary (Image) mode for file transfer; implies -V. Equivalent to SET TRANSFER MODE MANUAL, SET FILE TYPE BINARY. -j arg N Make a TCP/IP connection. Argument: IP host name/address and optional service name or number. Equivalent to the TELNET command. Also see: -J, -F. -k Y Receive file(s) to standard output, which normally would be piped to another process. Also see: -r, -G. -l arg N (Lowercase letter L) Make a connection on the given serial communications device. Equivalent to the SET LINE (SET PORT) command. Argument: Serial device name, e.g. /dev/ttyS0. -m arg N Modem type for use with the -l device. Equivalent to the SET MODEM TYPE command. Argument: Modem name as in SET MODEM TYPE command, e.g. "usrobotics". -n Y Enter Connect state after transferring files (historical). -p arg N Parity. Equivalent to the SET PARITY command. Argument: One of the following: e(ven), o(dd), m(ark), n(one), s(pace). -q N Quiet (suppress most messages). Equivalent to SET QUIET ON. -r Y Receive file(s). Equivalent to the RECEIVE command. Argument: (none, but see -a) -s arg N Send file(s). Argument: One or more local file specifications. Equivalent to the SEND command. Also see: -a. -t N (Historical) Xon (Ctrl-Q) Turnaround character for half-duplex connections (used on serial linemode connections to old mainframes). Equivalent to SET DUPLEX HALF, SET HANDSHAKE XON. -v arg N Window size for Kermit protocol (ignored when streaming). Equivalent to SET WINDOW-SIZE. Argument: Number, 1 to 32. -w N Incoming files Write over existing files. Equivalent to SET FILE COLLISION OVERWRITE. -x Y Enter server mode. Equivalent to the SERVER command. Also see: -O. -y arg N Alternative initialization file. Argument: Filename. -z N Force foreground behavior. To be used in case Kermit doesn't automatically sense its foreground status. Equivalent to the SET BACKGROUND OFF command. Extended command-line options (necessary because single-letter ones are about used up) start with two dashes (--), with words rather than single letters as option names. If an extended option takes an argument, it is separated from the option word by a colon (:). Extended options include: Option Description --bannerfile:filename File to display upon startup or IKSD login. --cdfile:filename File to be sent for display to the client when server changes directory (filename is relative to the changed-to directory). --cdmessage:{on,off} Enable/disable the server CD message feature. --help Prints usage message for extended options. --helpfile:filename Designates a file containing custom text to replace the top-level HELP command. --nointerrupts Disables keyboard interrupts. --noperms Disables the Kermit protocol file Permissions attribute, to prevent transmission of file permissions (protection) from sender to receiver. Plus several other [69]IKSD-Only options. See the [70]file-transfer section for examples of command-line invocation. COMMAND LANGUAGE [71]Top [72]Contents [73]Next [74]Previous * [75]Command Files, Macros, and Scripts * [76]Command List C-Kermit's interactive command language is the subject of a [77]622-page book and another several hundred pages of updates, far too much for a manual page. But it's not hard to get started. At the shell prompt, just type "kermit" to get C-Kermit's interactive command prompt: $ kermit (/current/directory) C-Kermit> Begin by typing "help" (and then press the Return or Enter key) for a top-level overview, read it, and go from there. Your second command should probably be "intro" (introduction). Note the prompt shows your current directory (unless you tell Kermit to prompt you with something else). Interactive commands are composed mainly of regular English words, usually in the form of imperative sentences, such as: send oofa.txt which tells Kermit to send (transfer) the file whose name is oofa.txt, or: set transfer mode automatic which sets Kermit's "transfer mode" to "automatic" (whatever that means). While typing commands, you can abbreviate, ask for help (by pressing the "?" key anywhere in a command), complete keywords or filenames (with the Tab or Esc key), and edit your typing with Backspace or Delete, Ctrl-W, Ctrl-U, etc. You can also recall previous commands, save your command history, and who knows what else. Give the INTRO command for details. C-Kermit has hundreds of commands, and they can be issued in infinite variety and combinations, including commands for: * Making connections (SET LINE, DIAL, TELNET, SSH, FTP, CONNECT, ...) * Breaking connections (HANGUP, CLOSE) * Transferring files (SEND, GET, RECEIVE, MOVE, RESEND, ...) * Establishing preferences (SET) * Displaying preferences (SHOW) * Managing local files (CD, DELETE, MKDIR, DIRECTORY, RENAME, TYPE, ...) * Managing remote files (RCD, RDEL, RMKDIR, RDIR, ...) * Using local files (FOPEN, FCLOSE, FREAD, FWRITE) * Programming (TAKE, DEFINE, IF, FOR, WHILE, SWITCH, DECLARE, ...) * Interacting with the user (ECHO, ASK, ...) * Interacting with a remote computer (INPUT, OUTPUT, ...) * Interacting with local programs (RUN, EXEC, PTY, ...) * Logging things (LOG SESSION, LOG PACKETS, LOG DEBUG, ...) And of course QUIT or EXIT to get out and HELP to get help, and for programmers: loops, decision making, variables, arrays, associative arrays, integer and floating point arithmetic, macros, built-in and user-defined functions, string manipulation, pattern matching, block structure, scoping, recursion, and all the rest. To get a list of all C-Kermit's commands, type a question mark (?) at the prompt. To get a description of any command, type HELP followed by the name of the command, for example: help send The command interruption character is Ctrl-C (hold down the Ctrl key and press the C key). The command language "escape character", used to introduce variable names, function invocations, and so on, is backslash (\). If you need to include a literal backslash in a command, type two of them, e.g.: get c:\\k95\\k95custom.ini Command Files, Macros, and Scripts A file containing Kermit commands is called a Kermit command file or Kermit script. It can be executed with Kermit's TAKE command: (/current/dir) C-Kermit> take commandfile (where "commandfile" is the name of the command file). Please don't pipe a command file into Kermit's standard input (which might or might not work); if you have Kermit commands in a file, tell Kermit to TAKE the file. In Unix only, a Kermit command file can also be executed directly by including a "kerbang" line as the first line of the file: #!/usr/local/bin/kermit + That is, a top line that starts with "#!", followed immediately by the full path of the Kermit executable, and then, if the Kermit script is to be given arguments on the command line, a space and a plus sign. The script file must also have execute permission: chmod +x commandfile Except for the " +" part, this is exactly the same as you would do for a shell script, a Perl script, etc. Here's a simple but useless example script that regurgitates its arguments (up to three of them): #!/usr/local/bin/kermit + if defined \%1 echo "Argument 1: \%1" if defined \%2 echo "Argument 2: \%2" if defined \%3 echo "Argument 3: \%3" if defined \%4 echo "etc..." exit If this file is stored in your current directory as "commandfile", then: ./commandfile one two three four five prints: Argument 1: one Argument 2: two Argument 3: three etc... This illustrates the basic structure of a standalone Kermit script: the "kerbang line", then some commands. It should end with "exit" unless you want the Kermit prompt to appear when it is finished. \%1 is the first argument, \%2 the second, and so on. You can also create your own commands by defining named macros composed of other Kermit commands (or macros). Here's a simple example: define mydial { set modem type usrobotics set port /dev/ttyS0 if fail end 1 set speed 57600 dial \%1 if success connect } This shows how you can combine many commands into one command, "mydial" in this case (you can use any name you like, provided it does not clash with the name of a built-in command). When this macro definition is in effect, you can type commands like: mydial 7654321 and it executes all the commands in macro definition, substituting the first operand ("7654321") for the formal parameter ("\%1") in the definition. This saves you from having to type lots of commands every time you want to make a modem call. One way to have the macro definition in effect is to type the definition at the Kermit prompt. Another way is to store the definition in a file and TAKE the file. If you want the definition to be in effect automatically every time you start Kermit, put the definition in your initialization or customization file (explained [78]below). Here's a somewhat more ambitious example: define mydelete { local trash assign trash \v(home)trashcan/ if not defined \%1 end 1 "Delete what?" if wild \%1 end 1 "Deleting multiple files is too scary" if not exist \%1 end 1 "I can't find \%1" if not directory \m(trash) { mkdir \m(trash) if fail end 1 "No trash can" } rename /list \%1 \m(trash) } define myundelete { local trash assign trash \v(home)trashcan/ if not defined \%1 end 1 "Undelete what?" if wild \%1 end 1 "Undeleting multiple files is too hard" if not directory \m(trash) end 1 "No trash can" if not exist \m(trash)\%1 end 1 "I can't find \%1 in trash can" rename /list \m(trash)\%1 . } These macros are not exactly production quality (they don't handle filenames that include path segments, they don't handle multiple files, etc), but you get the idea: you can pass arguments to macros, they can check them and make other kinds of decisions, and the commands themselves are relatively intuitive and intelligible. If you put the above lines into your initialization or customization file, you'll have MYDELETE and MYUNDELETE commands available every time you start Kermit, at least as long as you don't suppress execution of the initialization file. (Exercise for the reader: Make these macros generally useful: remove limitations, add trashcan display, browsing, emptying, etc.) Kerbang scripts execute without the initialization file. This to keep them portable and also to make them start faster. If you want to write Kerbang scripts that depend on the initialization file, include the command take \v(home).kermrc at the desired spot in the script. By the way, \v(xxx) is a built-in variable (xxx is the variable name, "home" in this case). To see what built-in variables are available, type "show variables" at the C-Kermit prompt. To see what else you can show, type "show ?". \m(xxx) is a user defined variable (strictly speaking, it is a macro used as a variable). Command List C-Kermit has more than 200 top-level commands, and some of these, such as SET, branch off into hundreds of subcommands of their own, so it's not practical to describe them all here. Instead, here's a concise list of the most commonly used top-level commands, grouped by category. To learn about each command, type "help" followed by the command name, e.g. "help set". Terms such as Command state and Connect state are explained in subsequent sections. Optional fields are shown in [ italicized brackets ]. filename means the name of a single file. filespec means a file specification that is allowed to contain wildcard characters like '*' to match groups of files. options are (optional) switches like /PAGE, /NOPAGE, /QUIET, etc, listed in the HELP text for each command. Example: send /recursive /larger:10000 /after:-1week /except:*.txt * which can be read as "send all the files in this directory and all the ones underneath it that are larger than 10000 bytes, no more than one week old, and whose names don't end with ".txt". Basic Commands HELP Requests top-level help. HELP command Requests help about the given command. INTRODUCTION Requests a brief introduction to C-Kermit. LICENSE Displays the C-Kermit software copyright and license. VERSION Displays C-Kermit's version number. EXIT [ number ] Exits from Kermit with the given status code. Synonyms: QUIT, E, Q. TAKE filename [ parameters... ] Executes commands from the given file. LOG item [ filename ] Keeps a log of the given item in the given file. [ DO ] macro [ parameters... ] Executes commands from the given macro. SET parameter value Sets the given parameter to the given value. SHOW category Shows settings in a given category. STATUS Tells whether previous command succeeded or failed. DATE [ date-and/or-time ] Shows current date-time or interprets given date-time. RUN [ extern-command [ parameters... ] Runs the given external command. Synonym: !. EXEC [ extern-command [ params... ] Kermit overlays itself with the given command. SUSPEND Stops Kermit and puts it in the background. Synonym: Z. Local File Management TYPE [ options ] filename Displays the contents of the given file. MORE [ options ] filename Equivalent to TYPE /PAGE (pause after each screenful). CAT [ options ] filename Equivalent to TYPE /NOPAGE. HEAD [ options ] filename Displays the first few lines of a given file. TAIL [ options ] filename Displays the last few lines of a given file. GREP [ options ] pattern filespec Displays lines from files that match the pattern. Synonym: FIND. DIRECTORY [ options ] [ filespec ] Lists files (built-in, many options). LS [ options ] [ filespec ] Lists files (runs external "ls" command). DELETE [ options ] [ filespec ] Deletes files. Synonym: RM. PURGE [ options ] [ filespec ] Removes backup (*.~n~) files. COPY [ options ] [ filespecs... ] Copies files. Synonym: CP. RENAME [ options ] [ filespecs... ] Renames files. Synonym: MV. CHMOD [ options ] [ filespecs... ] Changes permissions of files. TRANSLATE filename charsets filename ] Converts file's character set. Synonym: XLATE. CD Changes your working directory to your home directory. CD directory Changes your working directory to the one given. CDUP Changes your working directory one level up. PWD Displays your working directory. BACK Returns to your previous working directory. MKDIR [ directory ] Creates a directory. RMDIR [ directory ] Removes a directory. Making Connections SET LINE [ options ] devicename Opens the named serial port. Synonym: SET PORT. OPEN LINE [ options ] devicename Same as SET LINE. Synonym: OPEN PORT. SET MODEM TYPE [ name ] Tells Kermit what kind of modem is on the port. DIAL [ number ] Tells Kermit to dial the given phone number with the modem. REDIAL Redials the most recently dialed phone number. ANSWER Waits for and answers an incoming call on the modem. AUTHENTICATE [ parameters... ] Performs secure authentication on a TCP/IP connection. SET NETWORK TYPE { TCP/IP, X.25, ... } Selects network type for subsequent SET HOST commands. SET HOST [ options ] host [ port ] Opens a network connection to the given host and port. SET HOST [ options ] * port Waits for an incoming TCP/IP connection on the given port. TELNET [ options ] host Opens a Telnet connection to the host and enters Connect state. RLOGIN [ options ] host Opens an Rlogin connection to the host and enters Connect state. IKSD [ options ] host Opens a connection to an Internet Kermit Service. SSH [ options ] host Opens an SSH connection to the host and enters Connect state. FTP OPEN host [ options ] Opens an FTP connection to the host. HTTP [ options ] OPEN host Opens an HTTP connection to the host. PTY external-command Runs the command on a pseudoterminal as if it were a connection. PIPE external-command Runs the command through a pipe as if it were a connection. Using Connections CONNECT [ options ] Enters Connect (terminal) state. Synonym: C. REDIRECT command Redirects the given external command over the connection. TELOPT command Sends a Telnet protocol command (Telnet connections only). Ctrl-\C "Escapes back" from Connect state to Command state. Ctrl-\B (In Connect state) Sends a BREAK signal (serial or Telnet). Ctrl-\! (In Connect state) Enters inferior shell; "exit" to return. Ctrl-\? (In Connect state) Shows a menu of other escape-level options. Ctrl-\Ctrl-\ (In Connect state) Type two Ctrl-Backslashes to send one of them. SET ESCAPE [ character ] Changes Kermit's Connect-state escape character. Closing Connections HANGUP Hangs up the currently open serial-port or network connection. CLOSE Closes the currently open serial-port or network connection. SET LINE (with no devicename) Closes the currently open serial-port or network connection. SET HOST (with no hostname) Closes the currently open serial-port or network connection. FTP CLOSE Closes the currently open FTP connection. HTTP CLOSE Closes the currently open HTTP connection. EXIT Also closes all connections. Synonym: QUIT. SET EXIT WARNING OFF Suppresses warning about open connections on exit or close. File Transfer SEND [ options ] filename [ as-name ] Sends the given file. Synonym: S. SEND [ options ] filespec Sends all files that match. RESEND [ options ] filespec Resumes an interrupted SEND from the point of failure. RECEIVE [ options ] [ as-name ] Waits passively for files to arrive. Synonym: R. LOG TRANSACTIONS [ filename ] Keeps a record of file transfers. FAST Use fast file-transfer settings (default). CAUTIOUS Use cautious and less fast file-transfer settings. ROBUST Use ultra-conservative and slow file-transfer settings. STATISTICS [ options ] Gives statistics about the most recent file transfer. WHERE After transfer: "Where did my files go?". TRANSMIT [ options ] [ filename ] Sends file without protocol. Synonym: XMIT. LOG SESSION [ filename ] Captures remote text or files without protocol. SET PROTOCOL [ name... ] Tells Kermit to use an external file-transfer protocol. FTP { PUT, MPUT, GET, MGET, ... } FTP client commands. HTTP { PUT, GET, HEAD, POST, ... } HTTP client commands. Kermit Server ENABLE, DISABLE Controls which features can be used by clients. SET SERVER Sets parameters prior to entering Server state. SERVER Enters Server state. Client of Kermit or FTP Server [ REMOTE ] LOGIN [ user password ] Logs in to a Kermit server or IKSD that requires it. [ REMOTE ] LOGOUT Logs out from a Kermit server or IKSD. SEND [ options ] filename [ as-name ] Sends the given file to the server. Synonyms: S, PUT. SEND [ options ] filespec Sends all files that match. RESEND [ options ] filespec Resumes an interrupted SEND from the point of failure. GET [ options ] remote-filespec Asks the server to send the given files. Synonym: G. REGET [ options ] remote-filespec Resumes an interrupted GET from the point of failure. REMOTE CD [ directory ] Asks server to change its working directory. Synonym: RCD. REMOTE PWD [ directory ] Asks server to display its working directory. Synonym: RPWD. REMOTE DIRECTORY [ filespec... ] Asks server to send a directory listing. Synonym: RDIR. REMOTE DELETE [ filespec... ] Asks server to delete files. Synonym: RDEL. REMOTE [ command... ] (Many other commands: "remote ?" for a list). MAIL [ options ] filespec Sends file(s) to be delivered as e-mail (Kermit only). FINISH Asks the server to exit server state (Kermit only). BYE Asks the server to log out and close the connection. Script Programming DEFINE, DECLARE, UNDEFINE, UNDECLARE, ASSIGN, EVALUATE, SEXPRESSION, ARRAY, SORT, INPUT, OUTPUT, IF, FOR, WHILE, SWITCH, GOTO, ECHO, ASK, GETC, GETOK, ASSERT, WAIT, SLEEP, FOPEN, FREAD, FWRITE, FCLOSE, STOP, END, RETURN, LEARN, SHIFT, TRACE, VOID, INCREMENT, DECREMENT, ... For these and many more you'll need to consult the [79]manual and supplements, and/or visit the [80]Kermit Script Library, which also includes a brief tutorial. Hint: HELP LEARN to find out how to get Kermit to write simple scripts for you. Many of Kermit's commands have synonyms, variants, relatives, and so on. For example, MSEND is a version of SEND that accepts a list of file specifications to be sent, rather than just one file specification, and MPUT is a synonym of MSEND. MOVE means to SEND and then DELETE the source file if successful. MMOVE is like MOVE, but accepts a list of filespecs, and so on. These are described in the [81]full documentation. Use question mark to feel your way through an unfamiliar command, as in this example (the part you type is underlined): C-Kermit> remote ? One of the following: assign delete help login print rename space cd directory host logout pwd rmdir type copy exit kermit mkdir query set who C-Kermit> remote set ? One of the following: attributes file retry transfer block-check receive server window C-Kermit> remote set file ? One of the following: character-set incomplete record-length collision names type C-Kermit> remote set file names ? One of the following: converted literal C-Kermit> remote set file names literal C-Kermit> This is called menu on demand: you get a menu when you want one, but menus are not forced on you even when know what you're doing. Note that you can also abbreviate most keywords, and you can complete them with the Tab or Esc key. Also note that ? works for filenames too, and that you can use it in the middle of a keyword or filename, not just at the beginning. For example, "send x?" lists all the files in the current directory whose names start with 'x'. [82]Kermit Home [83]C-Kermit Home [84]C-Kermit FAQ INITIALIZATION FILE [85]Top [86]Contents [87]Next [88]Previous In its default configuration, C-Kermit executes commands from a file called .kermrc in your home directory when it starts, unless it is given the -Y or -y command-line option. Custom configurations might substitute a shared system-wide initialization file. The SHOW FILE command tells what initialization file, if any, was used. The standard initialization file "chains" to an individual customization file, .mykermc, in the home directory, in which each user can establish her/his own preferences, define macros, and so on. Since execution of the initialization file (at least the standard one) makes C-Kermit take longer to start, it might be better not to have an initialization file, especially now that Kermit's default startup configuration is well attuned to modern computing and networking -- in other words, you no longer have do anything special to make Kermit transfers go fast. So instead of having an initialization file that is executed every time Kermit starts, you might consider making one or more kerbang scripts (with names other that .kermrc) that do NOT include an "exit" command, and invoke those when you need the settings, macro definitions, and/or scripted actions they contain, and invoke C-Kermit directly when you don't. To put it another way... We still distribute the standard initialization file since it's featured in the manual and backwards compatibility is important to us. But there's no harm in not using it if you don't need the stuff that's in it (services directory, dialing directory, network directory, and associated macro definitions). On the other hand, if there are settings or macros you want in effect EVERY time you use Kermit, the initialization file (or the customization file it chains to) is the place to put them, because that's the only place Kermit looks for them automatically each time you start it. [89]Kermit Home [90]C-Kermit Home [91]C-Kermit FAQ MODES OF OPERATION [92]Top [93]Contents [94]Next [95]Previous Kermit is said to be in Local mode if it has made a connection to another computer, e.g. by dialing it or establishing a Telnet connection to it. The other computer is remote, so if you start another copy of Kermit on the remote computer, it is said to be in Remote mode (as long as it has not made any connections of its own). The local Kermit communicates over the communications device or network connection, acting as a conduit between the the remote computer and your keyboard and screen. The remote Kermit is the file-transfer partner to the local Kermit and communicates only through its standard input and output. At any moment, a Kermit program can be in any of the following states. It's important to know what they are and how to change from one to the other. Command state In this state, Kermit reads commands from: + Your keyboard; or: + A file, or: + A macro definition. You can exit from Command state back to Unix with the EXIT or QUIT command (same thing). You can enter Connect state with any of various commands (CONNECT, DIAL, TELNET, etc). You can enter file transfer state with commands like SEND, RECEIVE, and GET. You can enter Server state with the SERVER command. The TAKE command tells Kermit to read and execute commands from a file. The (perhaps implied) DO command tells Kermit to read and execute commands from a macro definition. While in Command state, you can interrupt any command, macro, or command file by typing Ctrl-C (hold down the Ctrl key and press the C key); this normally brings you back to the prompt. Shell state You can invoke an inferior shell or external command from the Kermit command prompt by using the PUSH, RUN (!), EDIT, or BROWSE command. While the inferior shell or command is active, Kermit is suspended and does nothing. Return to Kermit Command state by exiting from the inferior shell or application. Connect state In this state, which can be entered only when in Local mode (i.e. when Kermit has made a connection to another computer), Kermit is acting as a terminal to the remote computer. Your keystrokes are sent to the remote computer and characters that arrive over the communication connection are displayed on your screen. This state is entered when you give a CONNECT, DIAL, TELNET, RLOGIN, or IKSD command. You can return to command state by logging out of the remote computer, or by typing: Ctrl-\c That is: Hold down the Ctrl key and press the backslash key, then let go of the Ctrl key and press the C key. This is called escaping back. Certain other escape-level commands are also provided; type Ctrl-\? for a list. For example, you can enter Shell state with: Ctrl-\! To send a Ctrl-\ to the host while in Connect state, type two of them in a row. See HELP CONNECT and HELP SET ESCAPE for more info. Local file-transfer state In this state, Kermit is sending packets back and forth with the other computer in order to transfer a file or accomplish some other file-related task. And at the same time, it is displaying its progress on your screen and watching your keyboard for interruptions. In this state, the following single-keystroke commands are accepted: X Interrupt the current file and go on to the next (if any). Z Interrupt the current file and skip all the rest. E Like Z but uses a "stronger" protocol (use if X or Z don't work). Ctrl-C Interrupt file-transfer mode (use if Z or E don't work). Kermit returns to its previous state (Command or Connect) when the transfer is complete or when interrupted successfully by X, Z, E, or Ctrl-C (hold down the Ctrl key and press the C key). Remote file-transfer state In this state, Kermit is exchanging file-transfer packets with its local partner over its standard i/o. It leaves this state automatically when the transfer is complete. In case you find your local Kermit in Connect state and the remote one in File-transfer state (in which it seems to ignore your keystrokes), you can usually return it to command state by typing three Ctrl-C's in a row. If that doesn't work, return your local Kermit to Command state (Ctrl-\ C) and type "e-packet" and then press the Return or Enter key; this forces a fatal Kermit protocol error. Remote Server state This is like Remote File-transfer state, except it never returns automatically to Command state. Rather, it awaits further instructions from the client program; that is, from your Local Kermit program. You can return the Remote Server to its previous state by issuing a "finish" command to the client, or if you are in Connect state, by typing three Ctrl-C's in a row. You can tell the server job to log out and break the connection by issuing a "bye" command to the client. Local Server state Like Remote-Server state, but in local mode, and therefore with its file-transfer display showing, and listening for single-key commands, as in Local File-transfer state. Usually this state is entered automatically when a remote Kermit program gives a GET command. C-Kermit, Kermit 95, and MS-DOS Kermit all can switch automatically from Connect state to Local File-transfer state when you initiate a file transfer from the remote computer by starting Kermit and telling it to send or get a file, in which case, Connect state is automatically resumed after the file transfer is finished. Note that C-Kermit is not a terminal emulator. It is a communications application that you run in a terminal window (e.g. console or Xterm). The specific emulation, such as VT100, VT220, Linux Console, or Xterm, is provided by the terminal window in which you are running C-Kermit. Kermit 95 and MS-DOS Kermit, on the other hand, are true terminal emulators. Why is C-Kermit not a terminal emulator? [96]CLICK HERE to read about it. [97]Kermit Home [98]C-Kermit Home [99]C-Kermit FAQ MAKING CONNECTIONS [100]Top [101]Contents [102]Next [103]Previous Here is how to make different kinds of connections using interactive Kermit commands (as noted above, you can also make connections with command-line options). Note that you don't have to make connections with Kermit. It can also be used on the far end of a connection as the remote file transfer and management partner of your local communications software. Making a Telnet Connection At the C-Kermit command prompt, simply type: telnet foo.bar.com ; Substitute desired host name or address. telnet xyzcorp.com 3000 ; You can also include a port number. If the connection is successful, Kermit automatically enters Connect state. When you logout from the remote host, Kermit automatically returns to its prompt. More info: HELP TELNET, HELP SET TELNET, HELP SET TELOPT. Also see the [104]IKSD section below. Making an Rlogin connection This is just like Telnet, except you have to be root to do it because Rlogin uses a privileged TCP port: rlogin foo.bar.com ; Substitute desired host name or address. More info: HELP RLOGIN. Making an SSH Connection Unlike Telnet and Rlogin, SSH connections are not built-in, but handled by running your external SSH client through a pseudoterminal. Using C-Kermit to control the SSH client gives you all of Kermit's features (file transfer, character-set conversion, scripting, etc) over SSH. ssh foo.bar.com ; Substitute desired host name or address. More info: HELP SSH, HELP SET SSH. Dialing with a Modem If it's an external modem, make sure it is connected to a usable serial port on your computer with a regular (straight-through) [105]modem cable, and to the telephone jack with a telephone cable, and that it's turned on. Then use these commands: set modem type usrobotics ; Or other supported type set line /dev/ttyS0 ; Specify device name set speed 57600 ; Or other desired speed set flow rts/cts ; Most modern modems support this set dial method tone ; (or pulse) dial 7654321 ; Dial the desired number Type "set modem type ?" for a list of supported modem types. If you omit the SET MODEM TYPE command, the default type is "generic-high-speed", which should work for most modern AT-command-set modems. If the line is busy, Kermit redials automatically. If the call does not succeed, use "set dial display on" and try it again to watch what happens. If the call succeeds, Kermit enters Connect state automatically and returns to its prompt automatically when you log out from the remote computer or the connection is otherwise lost. You can also dial from a modem that is accessible by Telnet, e.g. to a reverse terminal server. In this case the command sequence is: set host ts.xxx.com 2000 ; Terminal-server and port set modem type usrobotics ; Or other supported type set dial method tone ; (or pulse) dial 7654321 ; Dial the desired number If the terminal server supports the Telnet Com Port Option, [106]RFC 2217, you can also give serial-port related commands such as SET SPEED, SET PARITY, and so on, and Kermit relays them to the terminal server using the protocol specified in the RFC. More info: HELP SET MODEM, HELP SET LINE, HELP SET SPEED, HELP SET FLOW, HELP DIAL, HELP SET DIAL, HELP SET MODEM, HELP SET CARRIER-WATCH, SHOW COMMUNICATIONS, SHOW MODEM, SHOW DIAL. Direct Serial Port Connect the two computers, A and B, with a [107]null modem cable (or two modem cables interconnected with a null-modem adapter or modem eliminator). From Computer A: set modem type none ; There is no modem set line /dev/ttyS0 ; Specify device name set carrier-watch off ; If DTR and CD are not cross-connected set speed 57600 ; Or other desired speed set flow rts/cts ; If RTS and CTS are cross-connected set flow xon/xoff ; If you can't use RTS/CTS set parity even ; (or "mark" or "space", if necessary) set stop-bits 2 ; (rarely necessary) connect ; Enter Connect (terminal) state This assumes Computer B is set up to let you log in. If it isn't, you can run a copy of Kermit on Computer B and follow approximately the same directions. More info: As above plus HELP CONNECT. With modems or direct serial connections, you might also have to "set parity even" (or "mark" or "space") if it's a 7-bit connection. Of the connection types listed above, only one can be open at a time. However, any one of these can be open concurrently with an [108]FTP or HTTP session. Each connection type can be customized to any desired degree, scripted, logged, you name it. See the manual. NOTE: On selected platforms, C-Kermit also can make X.25 connections. See the manual for details. [109]Kermit Home [110]C-Kermit Home [111]C-Kermit FAQ TRANSFERRING FILES WITH KERMIT [112]Top [113]Contents [114]Next [115]Previous * [116]Downloading Files * [117]Uploading Files * [118]Kermit Transfers the Old-Fashioned Way * [119]If File Transfer Fails * [120]Advanced Kermit File Transfer Features * [121]Non-Kermit File Transfer There is a [122]widespread and persistent belief that Kermit is a slow protocol. This is because, until recently, it used conservative tuning by default to make sure file transfers succeeded, rather than failing because they overloaded the connection. Some extra commands (or command-line options, like -Q) were needed to make it go fast, but nobody bothered to find out about them. Also, it takes two to tango: most non-Kermit-Project Kermit protocol implementations really ARE slow. The best file-transfer partners for C-Kermit are: another copy of [123]C-Kermit (7.0 or later) and [124]Kermit 95. These combinations work well and they work fast by default. MS-DOS Kermit is good too, but you have to tell it to go fast (by giving it the FAST command). Furthermore, all three of these Kermit programs support "autodownload" and "autoupload", meaning that when they are in Connect state and a Kermit packet comes in from the remote, they automatically switch into file transfer mode. And plus, C-Kermit and K95 also switch automatically between text and binary mode for each file, so there is no need to "set file type binary" or "set file type text", or to worry about files being corrupted because they were transferred in the wrong mode. What all of these words add up to is that now, when you use up-to-date Kermit software from the Kermit Project, file transfer is not only fast, it's ridiculously easy. You barely have to give any commands at all. Downloading Files Let's say you have [125]Kermit 95, [126]C-Kermit, or [127]MS-DOS Kermit on your desktop computer, with a connection to a Unix computer that has C-Kermit installed as "kermit". To download a file (send it from Unix to your desktop computer), just type the following command at your Unix shell prompt: kermit -s oofa.txt (where oofa.txt is the filename). If you want to send more than one file, you can put as many filenames as you want on the command line, and they can be any combination of text and binary: kermit -s oofa.txt oofa.zip oofa.html oofa.tar.gz and/or you can use wildcards to send groups of files: kermit -s oofa.* If you want to send a file under an assumed name, use: kermit -s friday.txt -a today.txt This sends the file friday.txt but tells the receiving Kermit that its name is today.txt. In all cases, as noted, when the file transfer is finished, your desktop Kermit returns automatically to Connect state. No worries about escaping back, re-connecting, text/binary mode switching. Almost too easy, right? Uploading Files To upload files (send them from your desktop computer to the remote Unix computer) do the same thing, but use the -g (GET) option instead of -s: kermit -g oofa.txt This causes your local Kermit to enter server mode; then the remote Kermit program requests the named file and the local Kermit sends it and returns automatically to Connect state when done. If you want to upload multiple files, you have have use shell quoting rules, since these aren't local files: kermit -g "oofa.txt oofa.zip oofa.html oofa.tar.gz" kermit -g "oofa.*" If you want to upload a file but store it under a different name, use: kermit -g friday.txt -a today.txt Kermit Transfers the Old-Fashioned Way If your desktop communications software does not support autoupload or autodownload, or it does not include Kermit server mode, the procedure requires more steps. To download a file, type: kermit -s filename on the host as before, but if nothing happens automatically in response to this command, you have to switch your desktop communications software into Kermit Receive state. This might be done by escaping back using keyboard characters or hot keys (Alt-x is typical) and/or with a command (like RECEIVE) or a menu. When the file transfer is complete, you have to go back to Connect state, Terminal emulation, or whatever terminology applies to your desktop communications software. To upload a file, type: kermit -r on the host (rather than "kermit -g"). This tells C-Kermit to wait passively for a file to start arriving. Then regain the attention of your desktop software (Alt-x or whatever) and instruct it to send the desired file(s) with Kermit protocol. When the transfer is finished, return to the Connect or Terminal screen. If File Transfer Fails Although every aspect of Kermit's operation can be finely tuned, there are also three short and simple "omnibus tuning" commands you can use for troubleshooting: FAST Use fast file-transfer settings. This has been the default since C-Kermit 7.0 now that most modern computers and connections support it. If transfers fail with fast settings, try . . . CAUTIOUS Use cautious but not paranoid settings. File transfers, if they work, will go at medium speed. If not, try . . . ROBUST Use the most robust, resilient, conservative, safe, and reliable settings. File transfers will almost certainly work, but they will be quite slow (of course this is a classic tradeoff; ROBUST was C-Kermit's default tuning in versions 6.0 and earlier, which made everybody think Kermit protocol was slow). If ROBUST doesn't do the trick, try again with SET PARITY SPACE first in case it's not an 8-bit connection. Obviously the success and performance of a file transfer also depends on C-Kermit's file transfer partner. Up-to-date, real [128]Kermit Project partners are recommended because they contain the best Kermit protocol implementations and because [129]we can support them in case of trouble. If you still have trouble, consult Chapter 10 of [130]Using C-Kermit, or send email to [131]kermit-support@columbia.edu. Advanced Kermit File-Transfer Features Obviously there is a lot more to Kermit file transfer, including all sorts of interactive commands, preferences, options, logging, debugging, troubleshooting, and anything else you can imagine but that's what the [132]manual and updates are for. Here are a few topics you can explore if you're interested by Typing HELP for the listed commands: Logging transfers: LOG TRANSACTIONS (HELP LOG) Automatic per-file text/binary mode switching: SET TRANSFER MODE { AUTOMATIC, MANUAL } (HELP SET TRANSFER). Cross-platform recursive directory tree transfer: SEND /RECURSIVE, GET /RECURSIVE (HELP SEND, HELP GET). File collision options: SET FILE COLLISION { OVERWRITE, BACKUP, DISCARD, ... } (HELP SET FILE). Update mode (only transfer files that changed since last time): SET FILE COLLISION UPDATE (HELP SET FILE). Filename selection patterns: (HELP WILDCARD). Flexible file selection: SEND (or GET) /BEFORE /AFTER /LARGER /SMALLER /TYPE /EXCEPT, ... Character-set conversion: SET { FILE, TRANSFER } CHARACTER-SET, ASSOCIATE, ... File/Pathname control: SET { SEND, RECEIVE } PATHNAMES, SET FILE NAMES. Atomic file movement: SEND (or GET) /DELETE /RENAME /MOVE-TO Transferring to/from standard i/o of other commands: SEND (or GET) /COMMAND Recovery of interrupted transfer from point of failure: RESEND, REGET (HELP RESEND, HELP REGET). Non-Kermit File Transfer You can also use C-Kermit to transfer files with FTP or HTTP Internet protocols; [133]see below. On a regular serial or Telnet connection where the other computer doesn't support Kermit protocol at all, you have several options. For example, if your desktop communications software supports Zmodem, use "rz" and "sz" on the host rather than Kermit. But if Kermit is your desktop software, and you are using it to make calls or network connections to other computers that don't support Kermit protocol (or that don't have a good implementation of it), then if your computer also has external X, Y, or Zmodem programs that are redirectable, Kermit can use them as external protocols. HELP SET PROTOCOL for details. You can also capture "raw" data streams from the other computer with LOG SESSION (HELP LOG and HELP SET SESSION-LOG for details), and you can upload files without any protocol at all with TRANSMIT (HELP TRANSMIT, HELP SET TRANSMIT). [134]Kermit Home [135]C-Kermit Home [136]C-Kermit FAQ KERMIT CLIENT/SERVER CONNECTIONS [137]Top [138]Contents [139]Next [140]Previous On any kind of connection you can make with Kermit -- serial, TCP/IP, X.25, etc -- you can set up a convenient client/server relationship between your Kermit client (the one that made the connection) and the Kermit program on the far end of the connection (the remote Kermit) by putting the remote Kermit in server mode. This is normally done by giving it a SERVER command, or by starting it with the -x command-line option. In some cases ([141]Internet Kermit Service, SSH connections to a Kermit subsystem, or specially configured hosts), there is already a Kermit server waiting on the far end. Here is a quick synopsis of the commands you can give to the client for interacting with the server: SEND [ switches ] filename Sends the named file to the server. The filename can include wildcards. Lots of switches are available for file selection, etc. Type HELP SEND at the client prompt for details. GET [ switches ] filename Asks the server to send the named file. The filename can include wildcards. Type HELP GET at the client prompt for details. BYE Terminates the server and closes your connection to it. FINISH Terminates the server. If you started the server yourself, this leaves the remote host at its shell prompt. If it was a dedicated server (such as IKSD or an SSH subsystem), FINISH is equivalent to BYE. SET LOCUS { LOCAL, REMOTE, AUTO } (C-Kermit 8.0.201 and later, K95 1.1.21 and later) This tells the client whether file-management commands like CD, PWD, DIRECTORY, DELETE, MKDIR, etc, should be executed locally or by the server. In this type of connection, the default is LOCAL. Use SET LOCUS REMOTE if you want Kermit to behave like an FTP client, in which case these commands are executed remotely, and their local versions must have an L prefix: LCD, LPWD, LDIRECTORY, etc. When LOCUS is LOCAL, then the remote versions must have an R prefix: RCD, RPWD, RDIRECTORY, etc. HELP SET LOCUS for details. SHOW COMMAND to see current locus. The following commands are affected by SET LOCUS: CD, LCD, RCD Change (working, current) directory. HELP CD for details. CDUP, LCDUP, RCDUP CD one level up. DIRECTORY, LDIRECTORY, RDIRECTORY Produce a directory listing. Many options are available for local listings. HELP DIRECTORY for details. DELETE, LDELETE, RDELETE Deletes files or directories. Many options available, HELP DELETE. RENAME, LRENAME, RRENAME Renames files or directories. Many options available, HELP RENAME. MKDIR, LMKDIR, RMKDIR Creates a directory. HELP MKDIR. RMDIR, LRMDIR, RRMDIR Removes a directory. HELP RMDIR. There are dozens -- maybe hundreds -- of other commands, described in the built-in help, on the website, and/or in the published or online manuals. But even if you don't have access to documentation, you can "set locus remote" and then use pretty much the same commands you would use with any FTP client. [142]Kermit Home [143]C-Kermit Home [144]C-Kermit FAQ KERMIT'S BUILT-IN FTP AND HTTP CLIENTS [145]Top [146]Contents [147]Next [148]Previous Kermit's FTP client is like the regular Unix FTP client that you're used to, but with some differences: * It has lots more commands and features. * You can have an FTP session and a regular Kermit serial or Telnet session open at the same time. * FTP sessions can be fully automated. By default Kermit's FTP client tries its best to present the same user interface as a regular FTP client: PUT, GET, DIR, CD, BYE, etc, should work the same, even though some of these commands have different meaning in Kermit-to-Kermit connections; for example, CD, DIR, RENAME, etc, in Kermit act locally, whereas in FTP they are commands for the server. This might cause some confusion, but as in all things Kermit, you have total control: * The [149]SET LOCUS command lets you specify where file management commands should be executed -- locally or remotely -- for any kind of connection. * Any FTP command can be prefixed with the word "FTP" to remove any ambiguity. Pending publication of the next edition of the manual, the Kermit FTP client is thoroughly documented at the Kermit Project website: [150]http://www.columbia.edu/kermit/ftpclient.html You also can use HELP FTP and HELP SET FTP to get descriptions of Kermit's FTP-related commands. The HTTP client is similar to the FTP one, except you prefix each command with HTTP instead of FTP: HTTP OPEN, HTTP GET, HTTP PUT, HTTP CLOSE, etc. Type HELP HTTP for details, or visit the to view the [151]manual supplements. HTTP connections can be open at the same time as regular serial or Telnet connections and FTP connections. So Kermit can manage up to three types connections simultaneously. [152]Kermit Home [153]C-Kermit Home [154]C-Kermit FAQ [155]FTP Client [156]HTTP Client INTERNET KERMIT SERVICE [157]Top [158]Contents [159]Next [160]Previous C-Kermit can be configured and run as an Internet service (called IKSD), similar to an FTP server (FTPD) except you can (but need not) interact with it directly, plus it does a lot more than an FTP server can do. The TCP port for IKSD is 1649. It uses Telnet protocol. C-Kermit can be an Internet Kermit Server, or it can be a client of an IKSD. You can make connections from C-Kermit to an IKSD with any of the following commands: telnet foo.bar.edu 1649 telnet foo.bar.edu kermit ; if "kermit" is listed in /etc/services iksd foo.bar.edu The IKSD command is equivalent to a TELNET command specifying port 1649. For more information about making and using connections to an IKSD, see: [161]http://www.columbia.edu/kermit/cuiksd.html You can run an Internet Kermit Service on your own computer too (if you are the system administrator). For instructions, see: [162]http://www.columbia.edu/kermit/iksd.html [163]Kermit Home [164]C-Kermit Home [165]C-Kermit FAQ SECURITY [166]Top [167]Contents [168]Next [169]Previous All of C-Kermit's built-in TCP/IP networking methods (Telnet, Rlogin, IKSD, FTP, and HTTP) can be secured by one or more of the following IETF-approved methods: * MIT Kerberos IV * MIT Kerberos V * SSL/TLS * Stanford SRP For complete instructions see: [170]http://www.columbia.edu/kermit/security.html And as noted previously, you can also make SSH connections with C-Kermit if you already have an SSH client installed. [171]Kermit Home [172]C-Kermit Home [173]C-Kermit FAQ ALTERNATIVE COMMAND-LINE PERSONALITIES [174]Top [175]Contents [176]Next [177]Previous When invoked as "kermit" or any other name besides any of the special ones, C-Kermit has the command-line options described above in the [178]OPTIONS section. However, if you invoke C-Kermit using any of the following names: telnet Telnet client ftp FTP client http HTTP client https Secure HTTP client Kermit's command-line personality changes to match. This can be done (among other ways) with symbolic links (symlinks). For example, if you want C-Kermit to be your regular Telnet client, or the Telnet helper of your Web browser, you can create a link like the following in a directory that lies in your PATH ahead of the regular telnet program: ln -s /usr/local/bin/kermit telnet Now when you give a "telnet" command, you are invoking Kermit instead, but with its Telnet command-line personality so, for example: telnet xyzcorp.com Makes a Telnet connection to xyzcorp.com, and Kermit exits automatically when the connection is closed (just like the regular Telnet client). Type "telnet -h" to get a list of Kermit's Telnet-personality command-line options, which are intended to be as compatible as possible with the regular Telnet client. Similarly for FTP: ln -s /usr/local/bin/kermit ftp And now type "ftp -h" to see its command-line options, and use command lines just like you would give your regular FTP client: ftp -n xyzcorp.com but with additional options allowing an entire session to be specified on the command line, as explained in the C-Kermit [179]FTP client documentation. And similarly for HTTP: ln -s /usr/local/bin/kermit http ./http -h ./http www.columbia.edu -g kermit/index.html Finally, if Kermit's first command-line option is a Telnet, FTP, IKSD, or HTTP URL, Kermit automatically makes the appropriate kind of connection and, if indicated by the URL, takes the desired action: kermit telnet:xyzcorp.com ; Opens a Telnet session kermit telnet://olga@xyzcorp.com ; Ditto for user olga kermit ftp://olga@xyzcorp.com/public/oofa.zip ; Downloads a file kermit kermit://kermit.columbia.edu/kermit/f/READ.ME ; Ditto for IKSD kermit iksd://kermit.columbia.edu/kermit/f/READ.ME ; (This works too) kermit http://www.columbia.edu/kermit/index.html ; Grabs a web page kermit https://wwws.xyzcorp.com/secret/plan.html ; Grabs a secure web page [180]Kermit Home [181]C-Kermit Home [182]C-Kermit FAQ LICENSE [183]Top [184]Contents [185]Next [186]Previous On or before 30 June 2011, barring unforeseen circumstances, [187]C-Kermit 9.0 will be released with the [188]Revised 3-Clause BSD License. This is a certified [189]Open Source license, and it means that C-Kermit no longer needs to be licensed for commercial redistribution. Technical support for Kermit software will not be available from Columbia University after June 30th. [190]Kermit Home [191]C-Kermit Home [192]C-Kermit FAQ OTHER TOPICS [193]Top [194]Contents [195]Next [196]Previous There's way more to C-Kermit than we've touched on here -- troubleshooting, customization, character sets, dialing directories, sending pages, script writing, and on and on, all of which are covered in the manual and updates and supplements. For the most up-to-date information on documentation (or updated documentation itself) visit the Kermit Project website: [197]http://www.columbia.edu/kermit/ There you will also find [198]Kermit software packages for other platforms: different Unix varieties, Windows, DOS, VMS, IBM mainframes, and many others: 20+ years' worth. [199]Kermit Home [200]C-Kermit Home [201]C-Kermit FAQ DOCUMENTATION AND UPDATES [202]Top [203]Contents [204]Next [205]Previous The manual for C-Kermit is: 1. Frank da Cruz and Christine M. Gianone, [206]Using C-Kermit, Second Edition, Digital Press / Butterworth-Heinemann, Woburn, MA, 1997, 622 pages, ISBN 1-55558-164-1. This is a printed book, now also available as a [207]Kindle E-Book. It covers C-Kermit 6.0. 2. The C-Kermit 7.0 Supplement: [208]http://www.columbia.edu/kermit/ckermit70.html 3. The C-Kermit 8.0 Supplement: [209]http://www.columbia.edu/kermit/ckermit80.html 4. The C-Kermit 9.0 Supplement: [210]http://www.columbia.edu/kermit/ckermit90.html The C-Kermit home page is here: [211]http://www.columbia.edu/kermit/ckermit.html Visit this page to learn about new versions, Beta tests, and other news; to read case studies and tutorials; to download source code, install packages, and [212]prebuilt binaries for many platforms. Also visit: [213]http://www.columbia.edu/kermit/scriptlib.html The Kermit script library and tutorial [214]http://www.columbia.edu/kermit/newfaq.html The Kermit FAQ (Frequently Asked Questions about Kermit) [215]http://www.columbia.edu/kermit/ckfaq.html The C-Kermit FAQ (Frequently Asked Questions about C-Kermit) [216]http://www.columbia.edu/kermit/security.html The Kermit security reference. [217]http://www.columbia.edu/kermit/telnet.html C-Kermit Telnet client documentation. [218]http://www.columbia.edu/kermit/studies.html Case studies. [219]http://www.columbia.edu/kermit/ckcbwr.html General C-Kermit Hints and Tips. [220]http://www.columbia.edu/kermit/ckubwr.html Unix C-Kermit Hints and Tips. [221]http://www.columbia.edu/kermit/ckvbwr.html VMS C-Kermit Hints and Tips. [222]http://www.columbia.edu/kermit/ckuins.html Unix C-Kermit Installation Instructions [223]http://www.columbia.edu/kermit/ckvins.html VMS C-Kermit Installation Instructions [224]http://www.columbia.edu/kermit/support.html Technical support. [225]http://www.columbia.edu/kermit/k95tutorial.html Kermit 95 tutorial (this document). [226]comp.protocols.kermit.misc The Kermit newsgroup (unmoderated). [227]Kermit Home [228]C-Kermit Home [229]C-Kermit FAQ FILES [230]Top [231]Contents [232]Next [233]Previous [234]The Revised 3-Clause License C-Kermit license. [235]~/.kermrc Initialization file. [236]~/.mykermrc Customization file. ~/.kdd Kermit dialing directory (see manual). ~/.knd Kermit network directory (see manual). ~/.ksd Kermit services directory (see manual). [237]ckuins.html Installation instructions for Unix. [238]ckcbwr.html General C-Kermit bugs, hints, tips. [239]ckubwr.html Unix-specific C-Kermit bugs, hints, tips. [240]ckcplm.html C-Kermit program logic manual. [241]ckccfg.html C-Kermit compile-time configuration options. ssh (in your PATH) SSH connection helper. rz, sz, etc. (in your PATH) external protocols for XYZmodem. /var/spool/locks (or whatever) UUCP lockfile for dialing out (see [242]installation instructions). [243]Kermit Home [244]C-Kermit Home [245]C-Kermit FAQ AUTHORS [246]Top [247]Contents [248]Previous Frank da Cruz and Jeffrey E Altman The Kermit Project - Columbia Univerity 612 West 115th Street New York NY 10025-7799 USA 1985-present, with contributions from hundreds of others all over the world. __________________________________________________________________ C-Kermit 9.0 Unix Manual Page and Tutorial / [249]kermit@columbia.edu / 1 July 2011 References 1. http://www.columbia.edu/ 2. mailto:kermit@columbia.edu 3. http://www.columbia.edu/kermit/index.html 4. http://www.columbia.edu/kermit/k95.html 5. http://www.columbia.edu/kermit/ckermit.html 6. http://www.columbia.edu/kermit/ckscripts.html 7. http://www.columbia.edu/kermit/current.html 8. http://www.columbia.edu/kermit/whatsnew.html 9. http://www.columbia.edu/kermit/ckfaq.html 10. http://www.columbia.edu/kermit/support.html 11. http://www.columbia.edu/kermit/ 12. http://www.columbia.edu/ 13. http://www.columbia.edu/kermit/ckututor.pdf 14. ftp://kermit.columbia.edu/kermit/test/text/ckuker.nr 15. http://www.amazon.com/gp/product/1555581641?ie=UTF8&tag=aleidmoreldom-20&linkCode=as2&camp=1789&creative=9325&creativeASIN=1555581641 16. http://www.columbia.edu/kermit/ckututor.html#description 17. http://www.columbia.edu/kermit/ckututor.html#synopsis 18. http://www.columbia.edu/kermit/ckututor.html#options 19. http://www.columbia.edu/kermit/ckututor.html#commands 20. http://www.columbia.edu/kermit/ckututor.html#initfile 21. http://www.columbia.edu/kermit/ckututor.html#modes 22. http://www.columbia.edu/kermit/ckututor.html#connections 23. http://www.columbia.edu/kermit/ckututor.html#transfer 24. http://www.columbia.edu/kermit/ckututor.html#server 25. http://www.columbia.edu/kermit/ckututor.html#ftp 26. http://www.columbia.edu/kermit/ckututor.html#iksd 27. http://www.columbia.edu/kermit/ckututor.html#security 28. http://www.columbia.edu/kermit/ckututor.html#personae 29. http://www.columbia.edu/kermit/ckututor.html#license 30. http://www.columbia.edu/kermit/ckututor.html#other 31. http://www.columbia.edu/kermit/ckututor.html#documentation 32. http://www.columbia.edu/kermit/ckututor.html#files 33. http://www.columbia.edu/kermit/ckututor.html#authors 34. http://www.columbia.edu/kermit/ckututor.html#top 35. http://www.columbia.edu/kermit/ckututor.html#contents 36. http://www.columbia.edu/kermit/ckututor.html#synopsis 37. http://www.columbia.edu/kermit/ckermit.html 38. http://www.columbia.edu/kermit/ 39. http://www.columbia.edu/ 40. ftp://ftp.isi.edu/in-notes/rfc2839.txt 41. ftp://ftp.isi.edu/in-notes/rfc2840.txt 42. http://www.columbia.edu/kermit/ckututor.html#documentation 43. http://www.columbia.edu/kermit/ 44. http://www.columbia.edu/kermit/ 45. http://www.columbia.edu/kermit/ckermit.html 46. http://www.columbia.edu/kermit/ckfaq.html 47. http://www.columbia.edu/kermit/ckututor.html#top 48. http://www.columbia.edu/kermit/ckututor.html#contents 49. http://www.columbia.edu/kermit/ckututor.html#options 50. http://www.columbia.edu/kermit/ckututor.html#synopsis 51. http://www.columbia.edu/kermit/ckututor.html#kerbang 52. http://www.columbia.edu/kermit/ckututor.html#personae 53. http://www.columbia.edu/kermit/ckututor.html#kerbang 54. http://www.columbia.edu/kermit/ckututor.html#initfile 55. http://www.columbia.edu/kermit/ckututor.html#initfile 56. http://www.columbia.edu/kermit/ckututor.html#personae 57. http://www.columbia.edu/kermit/ckututor.html#options 58. http://www.columbia.edu/kermit/ckututor.html#commands 59. http://www.columbia.edu/kermit/ 60. http://www.columbia.edu/kermit/ckermit.html 61. http://www.columbia.edu/kermit/ckfaq.html 62. http://www.columbia.edu/kermit/ckututor.html#top 63. http://www.columbia.edu/kermit/ckututor.html#contents 64. http://www.columbia.edu/kermit/ckututor.html#commands 65. http://www.columbia.edu/kermit/ckututor.html#description 66. http://www.columbia.edu/kermit/ckututor.html#commands 67. http://www.columbia.edu/kermit/ckututor.html#personae 68. http://www.columbia.edu/kermit/ckututor.html#personae 69. http://www.columbia.edu/kermit/ckututor.html#iksd 70. http://www.columbia.edu/kermit/ckututor.html#transfer 71. http://www.columbia.edu/kermit/ckututor.html#top 72. http://www.columbia.edu/kermit/ckututor.html#contents 73. http://www.columbia.edu/kermit/ckututor.html#initfile 74. http://www.columbia.edu/kermit/ckututor.html#options 75. http://www.columbia.edu/kermit/ckututor.html#kerbang 76. http://www.columbia.edu/kermit/ckututor.html#cmdlist 77. http://www.columbia.edu/kermit/ckututor.html#documentation 78. http://www.columbia.edu/kermit/ckututor.html#initfile 79. http://www.columbia.edu/kermit/ckututor.html#documentation 80. http://www.columbia.edu/kermit/ckscripts.html 81. http://www.columbia.edu/kermit/ckututor.html#documentation 82. http://www.columbia.edu/kermit/ 83. http://www.columbia.edu/kermit/ckermit.html 84. http://www.columbia.edu/kermit/ckfaq.html 85. http://www.columbia.edu/kermit/ckututor.html#top 86. http://www.columbia.edu/kermit/ckututor.html#contents 87. http://www.columbia.edu/kermit/ckututor.html#modes 88. http://www.columbia.edu/kermit/ckututor.html#commands 89. http://www.columbia.edu/kermit/ 90. http://www.columbia.edu/kermit/ckermit.html 91. http://www.columbia.edu/kermit/ckfaq.html 92. http://www.columbia.edu/kermit/ckututor.html#top 93. http://www.columbia.edu/kermit/ckututor.html#contents 94. http://www.columbia.edu/kermit/ckututor.html#connections 95. http://www.columbia.edu/kermit/ckututor.html#initfile 96. http://www.columbia.edu/kermit/ckfaq.html#term 97. http://www.columbia.edu/kermit/ 98. http://www.columbia.edu/kermit/ckermit.html 99. http://www.columbia.edu/kermit/ckfaq.html 100. http://www.columbia.edu/kermit/ckututor.html#top 101. http://www.columbia.edu/kermit/ckututor.html#contents 102. http://www.columbia.edu/kermit/ckututor.html#transfer 103. http://www.columbia.edu/kermit/ckututor.html#modes 104. http://www.columbia.edu/kermit/ckututor.html#iksd 105. http://www.columbia.edu/kermit/cable.html 106. ftp://ftp.isi.edu/in-notes/rfc2217.txt 107. http://www.columbia.edu/kermit/cable.html 108. http://www.columbia.edu/kermit/ckututor.html#ftp 109. http://www.columbia.edu/kermit/ 110. http://www.columbia.edu/kermit/ckermit.html 111. http://www.columbia.edu/kermit/ckfaq.html 112. http://www.columbia.edu/kermit/ckututor.html#top 113. http://www.columbia.edu/kermit/ckututor.html#contents 114. http://www.columbia.edu/kermit/ckututor.html#server 115. http://www.columbia.edu/kermit/ckututor.html#connections 116. http://www.columbia.edu/kermit/ckututor.html#download 117. http://www.columbia.edu/kermit/ckututor.html#upload 118. http://www.columbia.edu/kermit/ckututor.html#oldfashioned 119. http://www.columbia.edu/kermit/ckututor.html#trouble 120. http://www.columbia.edu/kermit/ckututor.html#advanced 121. http://www.columbia.edu/kermit/ckututor.html#nonkermit 122. http://www.columbia.edu/kermit/kermit.html#notslow 123. http://www.columbia.edu/kermit/ckermit.html 124. http://www.columbia.edu/kermit/k95.html 125. http://www.columbia.edu/kermit/k95.html 126. http://www.columbia.edu/kermit/ckermit.html 127. http://www.columbia.edu/kermit/mskermit.html 128. http://www.columbia.edu/kermit/ 129. http://www.columbia.edu/kermit/support.html 130. http://www.columbia.edu/kermit/ckmanual.html 131. mailto:kermit-support@columbia.edu 132. http://www.columbia.edu/kermit/ckututor.html#documentation 133. http://www.columbia.edu/kermit/ckututor.html#ftp 134. http://www.columbia.edu/kermit/ 135. http://www.columbia.edu/kermit/ckermit.html 136. http://www.columbia.edu/kermit/ckfaq.html 137. http://www.columbia.edu/kermit/ckututor.html#top 138. http://www.columbia.edu/kermit/ckututor.html#contents 139. http://www.columbia.edu/kermit/ckututor.html#ftp 140. http://www.columbia.edu/kermit/ckututor.html#transfer 141. http://www.columbia.edu/kermit/ckututor.html#iksd 142. http://www.columbia.edu/kermit/ 143. http://www.columbia.edu/kermit/ckermit.html 144. http://www.columbia.edu/kermit/ckfaq.html 145. http://www.columbia.edu/kermit/ckututor.html#top 146. http://www.columbia.edu/kermit/ckututor.html#contents 147. http://www.columbia.edu/kermit/ckututor.html#iksd 148. http://www.columbia.edu/kermit/ckututor.html#transfer 149. http://www.columbia.edu/kermit/ckututor.html#server 150. http://www.columbia.edu/kermit/ftpclient.html 151. http://www.columbia.edu/kermit/ckututor.html#documentation 152. http://www.columbia.edu/kermit/ 153. http://www.columbia.edu/kermit/ckermit.html 154. http://www.columbia.edu/kermit/ckfaq.html 155. http://www.columbia.edu/kermit/ckermit3.html#x3 156. http://www.columbia.edu/kermit/ckermit3.html#x2.2 157. http://www.columbia.edu/kermit/ckututor.html#top 158. http://www.columbia.edu/kermit/ckututor.html#contents 159. http://www.columbia.edu/kermit/ckututor.html#security 160. http://www.columbia.edu/kermit/ckututor.html#ftp 161. http://www.columbia.edu/kermit/cuiksd.html 162. http://www.columbia.edu/kermit/iksd.html 163. http://www.columbia.edu/kermit/ 164. http://www.columbia.edu/kermit/ckermit.html 165. http://www.columbia.edu/kermit/ckfaq.html 166. http://www.columbia.edu/kermit/ckututor.html#top 167. http://www.columbia.edu/kermit/ckututor.html#contents 168. http://www.columbia.edu/kermit/ckututor.html#personae 169. http://www.columbia.edu/kermit/ckututor.html#iksd 170. http://www.columbia.edu/kermit/security.html 171. http://www.columbia.edu/kermit/ 172. http://www.columbia.edu/kermit/ckermit.html 173. http://www.columbia.edu/kermit/ckfaq.html 174. http://www.columbia.edu/kermit/ckututor.html#top 175. http://www.columbia.edu/kermit/ckututor.html#contents 176. http://www.columbia.edu/kermit/ckututor.html#license 177. http://www.columbia.edu/kermit/ckututor.html#iksd 178. http://www.columbia.edu/kermit/ckututor.html#options 179. http://www.columbia.edu/kermit/ckermit3.html#x3.1.2 180. http://www.columbia.edu/kermit/ 181. http://www.columbia.edu/kermit/ckermit.html 182. http://www.columbia.edu/kermit/ckfaq.html 183. http://www.columbia.edu/kermit/ckututor.html#top 184. http://www.columbia.edu/kermit/ckututor.html#contents 185. http://www.columbia.edu/kermit/ckututor.html#other 186. http://www.columbia.edu/kermit/ckututor.html#personae 187. http://www.columbia.edu/kermit/ck90.html 188. http://www.columbia.edu/kermit/cu-bsd-license.html 189. http://www.opensource.org/ 190. http://www.columbia.edu/kermit/ 191. http://www.columbia.edu/kermit/ckermit.html 192. http://www.columbia.edu/kermit/ckfaq.html 193. http://www.columbia.edu/kermit/ckututor.html#top 194. http://www.columbia.edu/kermit/ckututor.html#contents 195. http://www.columbia.edu/kermit/ckututor.html#documentation 196. http://www.columbia.edu/kermit/ckututor.html#license 197. http://www.columbia.edu/kermit/ 198. http://www.columbia.edu/kermit/howtoget.html 199. http://www.columbia.edu/kermit/ 200. http://www.columbia.edu/kermit/ckermit.html 201. http://www.columbia.edu/kermit/ckfaq.html 202. http://www.columbia.edu/kermit/ckututor.html#top 203. http://www.columbia.edu/kermit/ckututor.html#contents 204. http://www.columbia.edu/kermit/ckututor.html#files 205. http://www.columbia.edu/kermit/ckututor.html#other 206. http://www.columbia.edu/kermit/ckmanual.html 207. http://www.amazon.com/gp/product/B002ACPF9M?ie=UTF8&tag=aleidmoreldom-20&linkCode=as2&camp=1789&creative=390957&creativeASIN=B002ACPF9M 208. http://www.columbia.edu/kermit/ckermit70.html 209. http://www.columbia.edu/kermit/ckermit80.html 210. http://www.columbia.edu/kermit/ckermit90.html 211. http://www.columbia.edu/kermit/ckermit.html 212. http://www.columbia.edu/kermit/ck80binaries.html 213. http://www.columbia.edu/kermit/scriptlib.html 214. http://www.columbia.edu/kermit/newfaq.html 215. http://www.columbia.edu/kermit/ckfaq.html 216. http://www.columbia.edu/kermit/security.html 217. http://www.columbia.edu/kermit/telnet.html 218. http://www.columbia.edu/kermit/studies.html 219. http://www.columbia.edu/kermit/ckcbwr.html 220. http://www.columbia.edu/kermit/ckubwr.html 221. http://www.columbia.edu/kermit/ckvbwr.html 222. http://www.columbia.edu/kermit/ckuins.html 223. http://www.columbia.edu/kermit/ckvins.html 224. http://www.columbia.edu/kermit/support.html 225. http://www.columbia.edu/kermit/k95tutorial.html 226. news:comp.protocols.kermit.misc 227. http://www.columbia.edu/kermit/ 228. http://www.columbia.edu/kermit/ckermit.html 229. http://www.columbia.edu/kermit/ckfaq.html 230. http://www.columbia.edu/kermit/ckututor.html#top 231. http://www.columbia.edu/kermit/ckututor.html#contents 232. http://www.columbia.edu/kermit/ckututor.html#authors 233. http://www.columbia.edu/kermit/ckututor.html#documentation 234. http://www.columbia.edu/kermit/cu-bsd-license.html 235. ftp://kermit.columbia.edu/kermit/c-kermit/ckermit.ini 236. ftp://kermit.columbia.edu/kermit/c-kermit/ckermod.ini 237. http://www.columbia.edu/kermit/ckuins.html 238. http://www.columbia.edu/kermit/ckcbwr.html 239. http://www.columbia.edu/kermit/ckubwr.html 240. http://www.columbia.edu/kermit/ckcplm.html 241. http://www.columbia.edu/kermit/ckccfg.html 242. http://www.columbia.edu/kermit/ckuins.html 243. http://www.columbia.edu/kermit/ 244. http://www.columbia.edu/kermit/ckermit.html 245. http://www.columbia.edu/kermit/ckfaq.html 246. http://www.columbia.edu/kermit/ckututor.html#top 247. http://www.columbia.edu/kermit/ckututor.html#contents 248. http://www.columbia.edu/kermit/ckututor.html#files 249. mailto:kermit@columbia.edu ckermit70.txt0000664000015300001460000273505011624230415012243 0ustar fdckermit [1]The Columbia Crown The Kermit Project | Columbia University 612 West 115th Street, New York NY 10025 USA o [2]kermit@columbia.edu ...since 1981 [3]Home [4]Kermit 95 [5]C-Kermit [6]Scripts [7]Current [8]New [9]FAQ [10]Support Supplement to [11]Using C-Kermit , 2nd Edition For C-Kermit 7.0 As of C-Kermit version: 7.0.196 This file created: 8 February 2000 This file last updated: Mon Aug 8 10:39:18 2011 Authors: Frank da Cruz and Christine M. Gianone Address: The Kermit Project Columbia University 612 West 115th Street New York NY 10025-7799 USA Fax: +1 (212) 662-6442 E-Mail: [12]kermit-support@columbia.edu Web: [13]http://www.columbia.edu/kermit/ Or: [14]http://www.kermit-project.org/ Or: [15]http://www.columbia.nyc.ny.us/kermit/ NOTICES This document: Copyright © 1997, 2000, Frank da Cruz and Christine M. Gianone. All rights reserved. Kermit 95: Copyright © 1995, 2000, Trustees of Columbia University in the City of New York. All rights reserved. C-Kermit: Copyright © 1985, 2000, Trustees of Columbia University in the City of New York. All rights reserved. See the C-Kermit [16]COPYING.TXT file or the copyright text in the [17]ckcmai.c module for disclaimer and permissions. When Kerberos(TM) and/or SRP(TM) (Secure Remote Password) and/or SSL protocol are included: Portions Copyright © 1990, Massachusetts Institute of Technology. Portions Copyright © 1991, 1993 Regents of the University of California. Portions Copyright © 1991, 1992, 1993, 1994, 1995 by AT&T. Portions Copyright © 1997, Stanford University. Portions Copyright © 1995-1997, Eric Young . For the full text of the third-party copyright notices, see [18]Appendix V. WHAT IS IN THIS FILE This file lists changes made to C-Kermit since the second edition of the book [19]Using C-Kermit was published and C-Kermit 6.0 was released in November 1996. Use this file as a supplement to the second edition of Using C-Kermit until the third edition is published some time in 2000. If the "most recent update" shown above is long ago, contact Columbia University to see if there is a newer release. For further information, also see the [20]CKCBWR.TXT ("C-Kermit beware") file for hints, tips, tricks, restrictions, frequently asked questions, etc, plus the system-specific "beware file", e.g. [21]CKUBWR.TXT for UNIX, [22]CKVBWR.TXT for VMS, etc, and also any system-specific update files such as KERMIT95.HTM for Kermit 95 (in the DOCS\MANUAL\ subdirectory of your K95 directory). This Web-based copy of the C-Kermit 7.0 update notes supersedes the plain-text CKERMIT2.TXT file. All changes after 19 January 2000 appear only here in the Web version. If you need an up-to-date plain-text copy, use your Web browser to save this page as plain text. ABOUT FILENAMES In this document, filenames are generally shown in uppercase, but on file systems with case-sensitive names such as UNIX, OS-9, and AOS/VS, lowercase names are used: [23]ckubwr.txt, [24]ckermit70.txt, etc. ADDITIONAL FILES Several other files accompany this new Kermit release: SECURITY.TXT Discussion of Kermit's new authentication and encryption features: + [25]Plain-text version + [26]HTML (hypertext) version IKSD.TXT How to install and manage an Internet Kermit Service Daemon. + [27]Plain-text version + [28]HTML (hypertext) version Also see [29]cuiksd.htm for instructions for use. TELNET.TXT A thorough presentation of Kermit's new advanced Telnet features and controls. + [30]Plain-text version + [31]HTML (hypertext) version THE NEW C-KERMIT LICENSE The C-Kermit license was rewritten for version 7.0 to grant automatic permission to packagers of free operating-system distributions to include C-Kermit 7.0. Examples include Linux (GNU/Linux), FreeBSD, NetBSD, etc. The new license is in the [32]COPYING.TXT file, and is also displayed by C-Kermit itself when you give the VERSION or COPYRIGHT command. The new C-Kermit license does not apply to [33]Kermit 95. ACKNOWLEDGMENTS Thanks to Jeff Altman, who joined the Kermit Project in 1995, for much of what you see in C-Kermit 7.0, especially in the networking and security areas, and his key role in designing and implementing the Internet Kermit Service Daemon. And special thanks to Lucas Hart for lots of help with the VMS version; to Peter Eichhorn for continuous testing on the full range of HP-UX versions and for a consolidated set of HP-UX makefile targets; and to Colin Allen, Mark Allen, Roger Allen, Ric Anderson, William Bader, Mitch Baker, Mitchell Bass, Nelson Beebe, Gerry Belanger, Jeff Bernsten, Mark Berryman, John Bigg, Volker Borchert, Jonathan Boswell, Tim Boyer, Frederick Bruckman, Kenneth Cochran, Jared Crapo, Bill Delaney, Igor Sobrado Delgado, Clarence Dold, Joe Doupnik, John Dunlap, Max Evarts, Patrick French, Carl Friedberg, Carl Friend, Hirofumi Fujii, Andrew Gabriel, Gabe Garza, Boyd Gerber, David Gerber, George Gilmer, Hunter Goatley, DJ Hagberg, Kevin Handy, Andy Harper, Randolph Herber, Sven Holström, Michal Jaegermann, Graham Jenkins, Dick Jones, Terry Kennedy, Robert D Keys, Nick Kisseberth, Igor Kovalenko, David Lane, Adam Laurie, Jeff Liebermann, Eric Lonvick, Hoi Wan Louis, Arthur Marsh, Gregorie Martin, Peter Mauzey, Dragan Milicic, Todd Miller, Christian Mondrup, Daniel Morato, Dat Nguyen, Herb Peyerl, Jean-Pierre Radley, Steve Rance, Stephen Riehm, Nigel Roles, Larry Rosenman, Jay S Rouman, David Sanderson, John Santos, Michael Schmitz, Steven Schultz, Bob Shair, Richard Shuford, Fred Smith, Michael Sokolov, Jim Spath, Peter Szell, Ted T'so, Brian Tillman, Linus Torvalds, Patrick Volkerding, Martin Vorländer, Steve Walton, Ken Weaverling, John Weekley, Martin Whitaker, Jim Whitby, Matt Willman, Joellen Windsor, Farrell Woods, and many others for binaries, hosting, reviews, suggestions, advice, bug reports, and all the rest over the 3+ year C-Kermit 7.0 development cycle. Thanks to Russ Nelson and the board of the Open Software Initiative ([34]http://www.opensource.org) for their cooperation in developing the new C-Kermit license and to the proprietors of those free UNIX distributions that have incorporated C-Kermit 7.0 for their cooperation and support, especially FreeBSD's Jörg Wunsch. NOTE TO KERMIT 95 USERS Kermit 95 and C-Kermit share the same command and scripting language, the same Kermit file-transfer protocol implementation, and much else besides. Like the book [35]Using C-Kermit, this file concentrates on the aspects of C-Kermit that are common to all versions: UNIX, VMS, Windows, OS/2, VOS, AOS/VS, etc. Please refer to your Kermit 95 documentation for information that is specific to Kermit 95. C-Kermit 7.0 corresponds to Kermit 95 1.1.19. C-KERMIT VERSIONS AND VERSION NUMBERS "C-Kermit" refers to all the many programs that are compiled in whole or in part from common C-language source code, comprising: * A Kermit file transfer protocol module * A command parser and script execution module * A modem-dialing module * A network support module * A character-set translation module. and several others. These "system-independent" modules are combined with system-dependent modules for each platform to provide the required input/output functions, and also in some cases overlaid with an alternative user interface, such as Macintosh Kermit's point-and-click interface, and in some cases also a terminal emulator, as Kermit 95. The C-Kermit version number started as 1.0, ... 3.0, 4.0, 4.1 and then (because of confusion at the time with Berkeley UNIX 4.2), 4B, 4C, and so on, with the specific edit number in parentheses, for example 4E(072) or 5A(188). This scheme was used through 5A(191), but now we have gone back to the traditional numbering scheme with decimal points: major.minor.edit; for example 7.0.196. Internal version numbers (the \v(version) variable), however, are compatible in C-Kermit 5A upwards. Meanwhile, C-Kermit derivatives for some platforms (Windows, Macintosh) might go through several releases while C-Kermit itself remains the same. These versions have their own platform-specific version numbers, such as Kermit 95 1.1.1, 1.1.2, and so on. C-Kermit Version History: 1.0 1981-1982 Command-line only, 4.2 BSD UNIX only 2.0 (*) (who remembers...) 3.0 May 1984 Command-line only, supports several platforms 4.0-4.1 Feb-Apr 1985 (*) First interactive and modular version 4C(050) May 1985 4D(060) April 1986 4E(066) August 1987 Long packets 4E(068) January 1988 4E(072) January 1989 4F(095) August 1989 (*) Attribute packets 5A(188) November 1992 Scripting, TCP/IP, sliding windows (1) 5A(189) September 1993 Control-char unprefixing 5A(190) October 1994 Recovery 5A(191) April 1995 OS/2 only 6.0.192 September 1996 Intelligent dialing, autodownload, lots more (2) 6.1.193 1997-98 (*) Development only 6.1.194 June 1998 K95 only - switches, directory recursion, more 7.0.195 August 1999 IKSD + more (CU only as K95 1.1.18-CU) 7.0.196 1 January 2000 Unicode, lots more (*) Never formally released (4.0 was a total rewrite) (1) Using C-Kermit, 1st Edition (2) Using C-Kermit, 2nd Edition CONTENTS I. [36]C-KERMIT DOCUMENTATION II. [37]NEW FEATURES (0) [38]INCOMPATIBILITIES WITH PREVIOUS RELEASES (1) [39]PROGRAM AND FILE MANAGEMENT AND COMMANDS 1.0. [40]Bug fixes 1.1. [41]Command Continuation 1.2. [42]Editor Interface 1.3. [43]Web Browser and FTP Interface 1.4. [44]Command Editing 1.5. [45]Command Switches 1.5.1. [46]General Switch Syntax 1.5.2. [47]Order and Effect of Switches 1.5.3. [48]Distinguishing Switches from Other Fields 1.5.4. [49]Standard File Selection Switches 1.5.5. [50]Setting Preferences for Different Commands 1.6. [51]Dates and Times 1.7. [52]Partial Completion of Keywords 1.8. [53]Command Recall 1.9. [54]EXIT Messages 1.10. [55]Managing Keyboard Interruptions 1.11. [56]Taming the Wild Backslash -- Part Deux 1.11.1. [57]Background 1.11.2. [58]Kermit's Quoting Rules 1.11.3. [59]Passing DOS Filenames from Kermit to Shell Commands 1.11.4. [60]Using Variables to Hold DOS Filenames 1.11.5. [61]Passing DOS Filenames as Parameters to Macros 1.11.6. [62]Passing DOS File Names from Macro Parameters to the D OS Shell 1.11.7. [63]Passing DOS Filenames to Kermit from the Shell 1.12. [64]Debugging 1.13. [65]Logs 1.14. [66]Automatic File-Transfer Packet Recognition at the Command Pro mpt 1.15. [67]The TYPE Command 1.16. [68]The RESET Command 1.17. [69]The COPY and RENAME Commands 1.18. [70]The MANUAL Command 1.19. [71]String and Filename Matching Patterns 1.20. [72]Multiple Commands on One Line 1.21. [73]What Do I Have? 1.22. [74]Generalized File Input and Output 1.22.1. [75]Why Another I/O System? 1.22.2. [76]The FILE Command 1.22.3. [77]FILE Command Examples 1.22.4. [78]Channel Numbers 1.22.5. [79]FILE Command Error Codes 1.22.6. [80]File I/O Variables 1.22.7. [81]File I/O Functions 1.22.8. [82]File I/O Function Examples 1.23. [83]The EXEC Command 1.24. [84]Getting Keyword Lists with '?' (2) [85]MAKING AND USING CONNECTIONS 2.0. [86]SET LINE and SET HOST Command Switches 2.1. [87]Dialing 2.1.1. [88]The Dial Result Message 2.1.2. [89]Long-Distance Dialing Changes 2.1.3. [90]Forcing Long-Distance Dialing 2.1.4. [91]Exchange-Specific Dialing Decisions 2.1.5. [92]Cautions about Cheapest-First Dialing 2.1.6. [93]Blind Dialing (Dialing with No Dialtone) 2.1.7. [94]Trimming the Dialing Dialog 2.1.8. [95]Controlling the Dialing Speed 2.1.9. [96]Pretesting Phone Number Conversions 2.1.10. [97]Greater Control over Partial Dialing 2.1.11. [98]New DIAL-related Variables and Functions 2.1.12. [99]Increased Flexibility of PBX Dialing 2.1.13. [100]The DIAL macro - Last-Minute Phone Number Conversions 2.1.14. [101]Automatic Tone/Pulse Dialing Selection 2.1.15. [102]Dial-Modifier Variables 2.1.16. [103]Giving Multiple Numbers to the DIAL Command 2.2. [104]Modems 2.2.1. [105]New Modem Types 2.2.2. [106]New Modem Controls 2.3. [107]TELNET and RLOGIN 2.3.0. [108]Bug Fixes 2.3.1. [109]Telnet Binary Mode Bug Adjustments 2.3.2. [110]VMS UCX Telnet Port Bug Adjustment 2.3.3. [111]Telnet New Environment Option 2.3.4. [112]Telnet Location Option 2.3.5. [113]Connecting to Raw TCP Sockets 2.3.6. [114]Incoming TCP Connections 2.4. [115]The EIGHTBIT Command 2.5. [116]The Services Directory 2.6. [117]Closing Connections 2.7. [118]Using C-Kermit with External Communication Programs 2.7.0. [119]C-Kermit over tn3270 and tn5250 2.7.1. [120]C-Kermit over Telnet 2.7.2. [121]C-Kermit over Rlogin 2.7.3. [122]C-Kermit over Serial Communication Programs 2.7.4. [123]C-Kermit over Secure Network Clients 2.7.4.1. [124]SSH 2.7.4.2. [125]SSL 2.7.4.3. [126]SRP 2.7.4.4. [127]SOCKS 2.7.4.5. [128]Kerberos and SRP 2.8. [129]Scripting Local Programs 2.9. [130]X.25 Networking 2.9.1. [131]IBM AIXLink/X.25 Network Provider Interface for AIX 2.9.2. [132]HP-UX X.25 2.10. [133]Additional Serial Port Controls 2.11. [134]Getting Access to the Dialout Device 2.12. [135]The Connection Log 2.13. [136]Automatic Connection-Specific Flow Control Selection 2.14. [137]Trapping Connection Establishment and Loss 2.15. [138]Contacting Web Servers with the HTTP Command (3) [139]TERMINAL CONNECTION 3.1. [140]CONNECT Command Switches 3.2. [141]Triggers 3.3. [142]Transparent Printing 3.4. [143]Binary and Text Session Logs (4) [144]FILE TRANSFER AND MANAGEMENT 4.0. [145]Bug Fixes, Minor Changes, and Clarifications 4.1. [146]File-Transfer Filename Templates 4.1.1. [147]Templates in the As-Name 4.1.2. [148]Templates on the Command Line 4.1.3. [149]Post-Transfer Renaming 4.2. [150]File-Transfer Pipes and Filters 4.2.1. [151]Introduction 4.2.1.1. [152]Terminology 4.2.1.2. [153]Notation 4.2.1.3. [154]Security 4.2.2. [155]Commands for Transferring from and to Pipes 4.2.2.1. [156]Sending from a Command 4.2.2.2. [157]Receiving to a Command 4.2.3. [158]Using File-Transfer Filters 4.2.3.1. [159]The SEND Filter 4.2.3.2. [160]The RECEIVE Filter 4.2.4. [161]Implicit Use of Pipes 4.2.5. [162]Success and Failure of Piped Commands 4.2.6. [163]Cautions about Using Pipes to Transfer Directory Trees 4.2.7. [164]Pipes and Encryption 4.2.8. [165]Commands and Functions Related to Pipes 4.2.8.1. [166]The OPEN !READ and OPEN !WRITE Commands 4.2.8.2. [167]The REDIRECT Command 4.2.8.3. [168]Receiving Mail and Print Jobs 4.2.8.4. [169]Pipe-Related Functions 4.3. [170]Automatic Per-File Text/Binary Mode Switching 4.3.1. [171]Exceptions 4.3.2. [172]Overview 4.3.3. [173]Commands 4.3.4. [174]Examples 4.4. [175]File Permissions 4.4.1. [176]When ATTRIBUTES PROTECTION is OFF 4.4.1.1. [177]Unix 4.4.1.2. [178]VMS 4.4.2. [179]When ATTRIBUTES PROTECTION is ON 4.4.2.1. [180]System-Specific Permissions 4.4.2.1.1. [181]UNIX 4.4.2.1.2. [182]VMS 4.4.2.2. [183]System-Independent Permissions 4.5. [184]File Management Commands 4.5.1. [185]The DIRECTORY Command 4.5.2. [186]The CD and BACK Commands 4.5.2.1. [187]Parsing Improvements 4.5.2.2. [188]The CDPATH 4.5.3. [189]Creating and Removing Directories 4.5.4. [190]The DELETE and PURGE Commands 4.6. [191]Starting the Remote Kermit Server Automatically 4.7. [192]File-Transfer Command Switches 4.7.1. [193]SEND Command Switches 4.7.2. [194]GET Command Switches 4.7.3. [195]RECEIVE Command Switches 4.8. [196]Minor Kermit Protocol Improvements 4.8.1. [197]Multiple Attribute Packets 4.8.2. [198]Very Short Packets 4.9. [199]Wildcard / File Group Expansion 4.9.1. [200]In UNIX C-Kermit 4.9.2. [201]In Kermit 95 4.9.3. [202]In VMS, AOS/VS, OS-9, VOS, etc. 4.10. [203]Additional Pathname Controls 4.11. [204]Recursive SEND and GET: Transferring Directory Trees 4.11.1. [205]Command-Line Options 4.11.2. [206]The SEND /RECURSIVE Command 4.11.3. [207]The GET /RECURSIVE Command 4.11.4. [208]New and Changed File Functions 4.11.5. [209]Moving Directory Trees Between Like Systems 4.11.6. [210]Moving Directory Trees Between Unlike Systems 4.12. [211]Where Did My File Go? 4.13. [212]File Output Buffer Control 4.14. [213]Improved Responsiveness 4.15. [214]Doubling and Ignoring Characters for Transparency 4.16. [215]New File-Transfer Display Formats 4.17. [216]New Transaction Log Formats 4.17.1. [217]The BRIEF Format 4.17.2. [218]The FTP Format 4.18. [219]Unprefixing NUL 4.19. [220]Clear-Channel Protocol 4.20. [221]Streaming Protocol 4.20.1. [222]Commands for Streaming 4.20.2. [223]Examples of Streaming 4.20.2.1. [224]Streaming on Socket-to-Socket Connections 4.20.2.2. [225]Streaming on Telnet Connections 4.20.2.3. [226]Streaming with Limited Packet Length 4.20.2.4. [227]Streaming on Dialup Connections 4.20.2.5. [228]Streaming on X.25 Connections 4.20.3. [229]Streaming - Preliminary Conclusions 4.21. [230]The TRANSMIT Command 4.22. [231]Coping with Faulty Kermit Implementations 4.22.1. [232]Failure to Accept Modern Negotiation Strings 4.22.2. [233]Failure to Negotiate 8th-bit Prefixing 4.22.3. [234]Corrupt Files 4.22.4. [235]Spurious Cancellations 4.22.5. [236]Spurious Refusals 4.22.6. [237]Failures during the Data Transfer Phase 4.22.7. [238]Fractured Filenames 4.22.8. [239]Bad File Dates 4.23. [240]File Transfer Recovery 4.24. [241]FILE COLLISION UPDATE Clarification 4.25. [242]Autodownload Improvements (5) [243]CLIENT/SERVER 5.0. [244]Hints 5.1. [245]New Command-Line Options 5.2. [246]New Client Commands 5.3. [247]New Server Capabilities 5.3.1. [248]Creating and Removing Directories 5.3.2. [249]Directory Listings 5.4. [250]Syntax for Remote Filenames with Embedded Spaces 5.5. [251]Automatic Orientation Messages upon Directory Change 5.6. [252]New Server Controls 5.7. [253]Timeouts during REMOTE HOST Command Execution (6) [254]INTERNATIONAL CHARACTER SETS 6.0. [255]ISO 8859-15 Latin Alphabet 9 6.1. [256]The HP-Roman8 Character Set 6.2. [257]Greek Character Sets 6.3. [258]Additional Latin-2 Character Sets 6.4. [259]Additional Cyrillic Character Sets 6.5. [260]Automatic Character-Set Switching 6.6. [261]Unicode 6.6.1. [262]Overview of Unicode 6.6.2. [263]UCS Byte Order 6.6.2. [264]UCS Transformation Formats 6.6.3. [265]Conformance Levels 6.6.4. [266]Relationship of Unicode with Kermit's Other Character Sets 6.6.5. [267]Kermit's Unicode Features 6.6.5.1. [268]File Transfer 6.6.5.2. [269]The TRANSLATE Command 6.6.5.3. [270]Terminal Connection 6.6.5.4. [271]The TRANSMIT Command 6.6.5.5. [272]Summary of Kermit Unicode Commands 6.7. [273]Client/Server Character-Set Switching (7) [274]SCRIPT PROGRAMMING 7.0. [275]Bug Fixes 7.1. [276]The INPUT Command 7.1.1. [277]INPUT Timeouts 7.1.2. [278]New INPUT Controls 7.1.3. [279]INPUT with Pattern Matching 7.1.4. [280]The INPUT Match Result 7.2. [281]New or Improved Built-In Variables 7.3. [282]New or Improved Built-In Functions 7.4. [283]New IF Conditions 7.5. [284]Using More than Ten Macro Arguments 7.6. [285]Clarification of Function Call Syntax 7.7. [286]Autodownload during INPUT Command Execution 7.8. [287]Built-in Help for Functions. 7.9. [288]Variable Assignments 7.9.1. [289]Assignment Operators 7.9.2. [290]New Assignment Commands 7.10. [291]Arrays 7.10.1. [292]Array Initializers 7.10.2. [293]Turning a String into an Array of Words 7.10.3. [294]Arrays of Filenames 7.10.4. [295]Automatic Arrays 7.10.5. [296]Sorting Arrays 7.10.6. [297]Displaying Arrays 7.10.7. [298]Other Array Operations 7.10.8. [299]Hints for Using Arrays 7.10.9. [300]Do-It-Yourself Arrays 7.10.10. [301]Associative Arrays 7.11. [302]OUTPUT Command Improvements 7.12. [303]Function and Variable Diagnostics 7.13. [304]Return Value of Macros 7.14. [305]The ASSERT, FAIL, and SUCCEED Commands. 7.15. [306]Using Alarms 7.16. [307]Passing Arguments to Command Files 7.17. [308]Dialogs with Timed Responses 7.18. [309]Increased Flexibility of SWITCH Case Labels 7.19. "[310]Kerbang" Scripts 7.20. [311]IF and XIF Statement Syntax 7.20.1. [312]The IF/XIF Distinction 7.20.2. [313]Boolean Expressions (The IF/WHILE Condition) 7.21. [314]Screen Formatting and Cursor Control 7.22. [315]Evaluating Arithmetic Expressions 7.23. [316]Floating-Point Arithmetic 7.24. [317]Tracing Script Execution 7.25. [318]Compact Substring Notation 7.26. [319]New WAIT Command Options 7.26.1. [320]Waiting for Modem Signals 7.26.2. [321]Waiting for File Events 7.27. [322]Relaxed FOR and SWITCH Syntax (8) [323]USING OTHER FILE TRANSFER PROTOCOLS (9) [324]COMMAND-LINE OPTIONS 9.0. [325]Extended-Format Command-Line Options 9.1. [326]Command Line Personalities 9.2. [327]Built-in Help for Command Line Options 9.3. [328]New Command-Line Options (10) [329]C-KERMIT AND G-KERMIT III. [330]APPENDICES III.1. [331]Character Set Tables III.1.1. [332]The Hewlett Packard Roman8 Character Set III.1.2. [333]Greek Character Sets III.1.2.1. [334]The ISO 8859-7 Latin / Greek Alphabet III.1.2.2. [335]The ELOT 927 Character Set III.1.2.3. [336]PC Code Page 869 III.2. [337]Updated Country Codes IV. [338]ERRATA & CORRIGENDA: Corrections to "Using C-Kermit" 2nd Edition. V. [339]ADDITIONAL COPYRIGHT NOTICES I. C-KERMIT DOCUMENTATION The user manual for C-Kermit is: Frank da Cruz and Christine M. Gianone, [340]Using C-Kermit, Second Edition, Digital Press / Butterworth-Heinemann, Woburn, MA, 1997, 622 pages, ISBN 1-55558-164-1. [341]CLICK HERE for reviews. The present document is a supplement to Using C-Kermit 2nd Ed, not a replacement for it. US single-copy price: $52.95; quantity discounts available. Available in bookstores or directly from Columbia University: The Kermit Project Columbia University 612 West 115th Street New York NY 10025-7799 USA Telephone: +1 (212) 854-3703 Fax: +1 (212) 662-6442 Domestic and overseas orders accepted. Price: US $44.95 (US, Canada, and Mexico). Shipping: $4.00 within the USA; $15.00 to all other countries. Orders may be paid by MasterCard or Visa, or prepaid by check in US dollars. Add $65 bank fee for checks not drawn on a US bank. Do not include sales tax. Inquire about quantity discounts. You can also order by phone from the publisher, Digital Press / [342]Butterworth-Heinemann, with MasterCard, Visa, or American Express: +1 800 366-2665 (Woburn, Massachusetts office for USA & Canada) +44 1865 314627 (Oxford, England distribution centre for UK & Europe) +61 03 9245 7111 (Melbourne, Vic, office for Australia & NZ) +65 356-1968 (Singapore office for Asia) +27 (31) 2683111 (Durban office for South Africa) A [343]German-language edition of the First Edition is also available: Frank da Cruz and Christine M. Gianone, C-Kermit - Einführung und Referenz, Verlag Heinz Heise, Hannover, Germany (1994). ISBN 3-88229-023-4. Deutsch von Gisbert W. Selke. Price: DM 88,00. Verlag Heinz Heise GmbH & Co. KG, Helstorfer Strasse 7, D-30625 Hannover. Tel. +49 (05 11) 53 52-0, Fax. +49 (05 11) 53 52-1 29. The [344]Kermit file transfer protocol is specified in: Frank da Cruz, Kermit, A File Transfer Protocol, Digital Press, Bedford, MA, 1987, 379 pages, ISBN 0-932376-88-6. US single-copy price: $39.95. Availability as above. News and articles about Kermit software and protocol are published periodically in the journal, [345]Kermit News. Subscriptions are free; contact Columbia University at the address above. Online news about Kermit is published in the [346]comp.protocols.kermit.announce and [347]comp.protocols.kermit.misc newsgroups. II. NEW FEATURES Support for the Bell Labs Plan 9 operating system was added to version 6.0 too late to be mentioned in the book (although it does appear on the cover). Specific changes and additions are grouped together by major topic, roughly corresponding to the chapters of [348]Using C-Kermit. 0. INCOMPATIBILITIES WITH PREVIOUS RELEASES 1. C-Kermit 7.0 uses FAST Kermit protocol settings by default. This includes "unprefixing" of certain control characters. Because of this, file transfers that worked with previous releases might not work in the new release (but it is more likely that they will work, and much faster). If a transfer fails, you'll get a context-sensitive hint suggesting possible causes and cures. Usually SET PREFIXING ALL does the trick. 2. C-Kermit 7.0 transfers files in BINARY mode by default. To restore the previous behavior, put SET FILE TYPE TEXT in your C-Kermit initialization file. 3. No matter whether FILE TYPE is BINARY or TEXT by default, C-Kermit 7.0 now switches between text and binary mode automatically on a per-file basis according to various criteria, including (a) which kind of platform is on the other end of the connection (if known), (b) the version of Kermit on the other end, and (c) the file's name (see [349]Section 4, especially [350]4.3). To disable this automatic switching and restore the earlier behavior, put SET TRANSFER MODE MANUAL in your C-Kermit initialization file. To disable automatic switching for a particular transfer, include a /TEXT or /BINARY switch with your SEND or GET command. 4. The RESEND and REGET commands automatically switch to binary mode; previously if RESEND or REGET were attempted when FILE TYPE was TEXT, these commands would fail immediately, with a message telling you they work only when the FILE TYPE is BINARY. Now they simply do this for you. See [351]Section 4.23 for additional (important) information. 5. SET PREFIXING CAUTIOUS and MINIMAL now both prefix linefeed (10 and 138) in case rlogin, ssh, or cu are "in the middle", since otherwise ~ might appear in Kermit packets, and this would cause rlogin, ssh, or cu to disconnect, suspend, escape back, or otherwise wreck the file transfer. Xon and Xoff are now always prefixed too, even when Xon/Xoff flow control is not in effect, since unprefixing them has proven dangerous on TCP/IP connections. 6. In UNIX, VMS, Windows, and OS/2, the DIRECTORY command is built into C-Kermit itself rather than implemented by running an external command or program. The built-in command might not behave the way the platform-specific external one did, but many options are available for customization. Of course the underlying platform-specific command can still be accessed with "!", "@", or "RUN" wherever the installation does not forbid. In UNIX, the "ls" command can be accessed directly as "ls" in C-Kermit. See [352]Section 4.5.1 for details. 7. SEND ? prints a list of switches rather than a list of filenames. If you want to see a list of filenames, use a (system-dependent) construction such as SEND ./? (for UNIX, Windows, or OS/2), SEND []? (VMS), etc. See [353]Sections 1.5 and [354]4.7.1. 8. In UNIX, OS-9, and Kermit 95, the wildcard characters in previous versions were * and ?. In C-Kermit 7.0 they are *, ?, [, ], {, and }, with dash used inside []'s to denote ranges and comma used inside {} to separate list elements. If you need to include any of these characters literally in a filename, precede each one with backslash (\). See [355]Section 4.9. 9. SET QUIET { ON, OFF } is now on the command stack, just like SET INPUT CASE, SET COUNT, SET MACRO ERROR, etc, as described on p.458 of [356]Using C-Kermit, 2nd Edition. This allows any macro or command file to SET QUIET ON or OFF without worrying about saving and restoring the global QUIET value. For example, this lets you write a script that tries SET LINE on lots of devices until it finds one free without spewing out loads of error messages, and also without disturbing the global QUIET setting, whatever it was. 10. Because of the new "." operator (which introduces assignments), macros whose names begin with "." can not be invoked "by name". However, they still can be invoked with DO. 11. The syntax of the EVALUATE command has changed. See [357]Section 7.9.2. To restore the previous syntax, use SET EVALUATE OLD. 12. The \v(directory) variable now includes the trailing directory separator; in previous releases it did not. This is to allow constructions such as: cd \v(dir)data.tmp to work across platforms that might have different directory notation, such as UNIX, Windows, and VMS. 13. Prior to C-Kermit 7.0, the FLOW-CONTROL setting was global and sticky. In C-Kermit 7.0, there is an array of default flow-control values for each kind of connection, that are applied automatically at SET LINE/PORT/HOST time. Thus a SET FLOW command given before SET LINE/PORT/HOST is likely to be undone. Therefore SET FLOW can be guaranteed to have the desired effect only if given after the SET LINE/PORT/HOST command. 14. Character-set translation works differently in the TRANSMIT command when (a) the file character-set is not the same as the local end of the terminal character-set, or (b) when the terminal character-set is TRANSPARENT. 1. PROGRAM AND FILE MANAGEMENT AND COMMANDS 1.0. Bug Fixes The following patches were issued to correct bugs in C-Kermit 6.0. These are described in detail in the 6.0 PATCHES file. All of these fixes have been incorporated in C-Kermit 6.1 (never released except as K95 1.1.16-17) and 7.0. 0001 All UNIX C-Kermit mishandles timestamps on files before 1970 0002 Solaris 2.5++ Compilation error on Solaris 2.5 with Pro C 0003 All VMS CKERMIT.INI Fix for VMS 0004 VMS/VAX/UCX 2.0 C-Kermit 6.0 can't TELNET on VAX/VMS with UCX 2.0 0005 All C-Kermit Might Send Packets Outside Window 0006 All MOVE from SEND-LIST does not delete original files 0007 Solaris 2.5++ Higher serial speeds on Solaris 2.5 0008 All C-Kermit application file name can't contain spaces 0009 AT&T 7300 UNIXPC setuid and hardware flow-control problems 0010 Linux on Alpha Patch to make ckutio.c compile on Linux/Alpha 0011 OS-9/68000 2.4 Patch to make ck9con.c compile on OS-9/68000 2.4 0012 MW Coherent 4.2 Patches for successful build on Coherent 4.2 0013 SINIX-Y 5.43 "delay" variable conflicts with 0014 VMS/VAX/CMU-IP Subject: Patches for VAX/VMS 5.x + CMU-IP 0015 All XECHO doesn't flush its output 0016 VMS CD and other directory operations might not work 0017 Linux 1.2.x++ Use standard POSIX interface for high serial speeds 0018 UNIX SET WILDCARD-EXPANSION SHELL dumps core 0019 All Hayes V.34 modem init string problem 0020 All READ command does not fail if file not open 0021 All Problems with long function arguments 0022 All Certain \function()s can misbehave 0023 All X MOD 0 crashes program 0024 All Internal bulletproofing for lower() function 0025 OpenBSD Real OpenBSD support for C-Kermit 6.0 0026 All Incorrect checks for macro/command-file nesting depth 0027 All ANSWER doesn't automatically CONNECT 0028 All Overzealous EXIT warning 0029 All OUTPUT doesn't echo when DUPLEX is HALF 0030 All Minor problems with REMOTE DIRECTORY/DELETE/etc 0031 All CHECK command broken 0032 All Problem with SET TRANSMIT ECHO 0033 UNIX, VMS, etc HELP SET SERVER says too much 0034 All READ and !READ too picky about line terminators 0035 All END from inside SWITCH doesn't work 0036 All Problem telnetting to multihomed hosts 0037 All Redirection failures in REMOTE xxx > file REDIRECT was missing in many UNIX C-Kermit implementations; in version 7.0, it should be available in all of them. 1.1. Command Continuation Comments that start with ";" or "#" can no longer be continued. In: ; this is a comment - echo blah the ECHO command will execute, rather than being taken as a continuation of the preceding comment line. This allows easy "commenting out" of commands from macro definitions. However, the text of the COMMENT command can still be continued onto subsequent lines: comment this is a comment - echo blah As of version 6.0, backslash is no longer a valid continuation character. Only hyphen should be used for command continuation. This is to make it possible to issue commands like "cd a:\" on DOS-like systems. As of version 7.0: * You can quote a final dash to prevent it from being a continuation character: echo foo\- This prints "foo-". The command is not continued. * You can enter commands such as: echo foo - ; this is a comment interactively and they are properly treated as continued commands. Previously this worked only in command files. 1.2. Editor Interface SET EDITOR name [ options ] Lets you specify a text-editing program. The name can be a fully specified pathname like /usr/local/bin/emacs19/emacs, or it can be the name of any program in your PATH, e.g. "set editor emacs". In VMS, it must be a DCL command like "edit", "edit/tpu", "emacs", etc. If an environment variable EDITOR is defined when Kermit starts, its value is the default editor. You can also specify options to be included on the editor command line. Returns to Kermit when the editor exits. EDIT [ filename ] If the EDIT command is given without a filename, then if a previous filename had been given to an EDIT command, it is used; if not, the editor is started without a file. If a filename is given, the editor is started on that file, and the filename is remembered for subsequent EDIT commands. SHOW EDITOR Displays the full pathname of your text editor, if any, along with any command line options, and the file most recently edited (and therefore the default filename for your next EDIT command). Related variables: \v(editor), \v(editopts), \v(editfile). 1.3. Web Browser and FTP Interface C-Kermit includes an FTP command, which simply runs the FTP program; C-Kermit does not include any built-in support for Internet File Transfer Protocol, nor any method for interacting directly with an FTP server. In version 7.0, however, C-Kermit lets you specify your FTP client: SET FTP-CLIENT [ name [ options ] ] The name is the name of the FTP executable. In UNIX, Windows, or OS/2, it can be the filename of any executable program in your PATH (e.g. "ftp.exe" in Windows, "ftp" in UNIX); elsewhere (or if you do not have a PATH definition), it must be the fully specified pathname of the FTP program. If the name contains any spaces, enclose it braces. Include any options after the filename; these depend the particular ftp client. The Web browser interface is covered in the following subsections. 1.3.1. Invoking your Browser from C-Kermit BROWSE [ url ] Starts your preferred Web browser on the URL, if one is given, otherwise on the most recently given URL, if any. Returns to Kermit when the browser exits. SET BROWSER [ name [ options ] ] Use this command to specify the name of your Web browser program, for example: "set browser lynx". The name must be in your PATH, or else it must be a fully specified filename; in VMS it must be a DCL command. SHOW BROWSER Displays the current browser, options, and most recent URL. Related variables: \v(browser), \v(browsopts), \v(browsurl). Also see [358]Section 2.15: Contacting Web Servers with the HTTP Command. 1.3.2. Invoking C-Kermit from your Browser The method for doing this depends, of course, on your browser. Here are some examples: Netscape on UNIX (X-based) In the Options->Applications section, set your Telnet application to: xterm -e /usr/local/bin/kermit/kermit -J %h %p (replace "/usr/local/bin/kermit/kermit" by C-Kermit's actual pathname). -J is C-Kermit's command-line option to "be like Telnet"; %h and %p are Netscape placeholders for hostname and port. Lynx on UNIX As far as we know, this can be done only at compile time. Add the following line to the Lynx userdefs.h file before building the Lynx binary: #define TELNET_COMMAND "/opt/bin/kermit -J" And then add lines like the following to the Lynx.cfg file: DOWNLOADER:Kermit binary download:/opt/bin/kermit -i -V -s %s -a %s:TRUE DOWNLOADER:Kermit text download:/opt/bin/kermit -s %s -a %s:TRUE UPLOADER:Kermit binary upload:/opt/bin/kermit -i -r -a %s:TRUE UPLOADER:Kermit text upload:/opt/bin/kermit -r -a %s:TRUE UPLOADER:Kermit text get:/opt/bin/kermit -g %s:TRUE UPLOADER:Kermit binary get:/opt/bin/kermit -ig %s:TRUE But none of the above is necessary if you make C-Kermit your default Telnet client, which you can do by making a symlink called 'telnet' to the C-Kermit 7.0 binary. See [359]Section 9.1 for details. 1.4. Command Editing Ctrl-W ("Word delete") was changed in 7.0 to delete back to the previous non-alphanumeric, rather than all the way back to the previous space. 1.5. Command Switches As of version 7.0, C-Kermit's command parser supports a new type of field, called a "switch". This is an optional command modifier. 1.5.1. General Switch Syntax A switch is a keyword beginning with a slash (/). If it takes a value, then the value is appended to it (with no intervening spaces), separated by a colon (:) or equal sign (=). Depending on the switch, the value may be a number, a keyword, a filename, a date/time, etc. Examples: send oofa.txt ; No switches send /binary oofa.zip ; A switch without a value send /protocol:zmodem oofa.zip ; A switch with a value (:) send /protocol=zmodem oofa.zip ; A switch with a value (=) send /text /delete /as-name:x.x oofa.txt ; Several switches Like other command fields, switches are separated from other fields, and from each other, by whitespace, as shown in the examples just above. You can not put them together like so: send/text/delete/as-name:x.x oofa.txt (as you might do in VMS or DOS, or as we might once have done in TOPS-10 or TOPS0-20, or PIP). This is primarily due to ambiguity between "/" as switch introducer versus "/" as UNIX directory separator; e.g. in: send /delete/as-name:foo/text oofa.txt Does "foo/text" mean the filename is "foo" and the transfer is to be in text mode, or does it mean the filename is "foo/text"? Therefore we require whitespace between switches to resolve the ambiguity. (That's only one of several possible ambiguities -- it is also conceivable that a file called "text" exists in the path "/delete/as-name:foo/"). In general, if a switch can take a value, but you omit it, then either a reasonable default value is supplied, or an error message is printed: send /print:-Plaserwriter oofa.txt ; Value included = print options send /print oofa.txt ; Value omitted, OK send /mail:kermit@columbia.edu oofa.txt ; Value included = address send /mail oofa.txt ; Not OK - address required ?Address required Context-sensitive help (?) and completion (Esc or Tab) are available in the normal manner: C-Kermit> send /pr? Switch, one of the following: /print /protocol C-Kermit> send /protocol:? File-transfer protocol, one of the following: kermit xmodem ymodem ymodem-g zmodem C-Kermit> send /protocol:kermit If a switch takes a value and you use completion on it, a colon (:) is printed at the end of its name to indicate this. If it does not take a value, a space is printed. Also, if you type ? in a switch field, switches that take values are shown with a trailing colon; those that don't take values are shown without one. 1.5.2. Order and Effect of Switches The order of switches should not matter, except that they are evaluated from left to right, so if you give two switches with opposite effects, the rightmost one is used: send /text /binary oofa.zip ; Sends oofa.zip in binary mode. Like other command fields, switches have no effect whatsoever until the command is entered (by pressing the Return or Enter key). Even then, switches affect only the command with which they are included; they do not have global effect or side effects. 1.5.3. Distinguishing Switches from Other Fields All switches are optional. A command that uses switches lets you give any number of them, including none at all. Example: send /binary oofa.zip send /bin /delete oofa.zip send /bin /as-name:mupeen.zip oofa.zip send oofa.zip But how does Kermit know when the first "non-switch" is given? It has been told to look for both a switch and for something else, the data type of the next field (filename, number, etc). In most cases, this works well. But conflicts are not impossible. Suppose, for example, in UNIX there was a file named "text" in the top-level directory. The command to send it would be: send /text But C-Kermit would think this was the "/text" switch. To resolve the conflict, use braces: send {/text} or other circumlocutions such as "send //text", "send /./text", etc. The opposite problem can occur if you give an illegal switch that happens to match a directory name. For example: send /f oofa.txt There is no "/f" switch (there are several switches that begin with "/f", so "/f" is ambiguous). Now suppose there is an "f" directory in the root directory; then this command would be interpreted as: Send all the files in the "/f" directory, giving each one an as-name of "oofa.txt". This could be a mistake, or it could be exactly what you intended; C-Kermit has no way of telling the difference. To avoid situations like this, spell switches out in full until you are comfortable enough with them to know the minimum abbreviation for each one. Hint: use ? and completion while typing switches to obtain the necessary feedback. 1.5.4. Standard File Selection Switches The following switches are used on different file-oriented commands (such as SEND, DIRECTORY, DELETE, PURGE) to refine the selection of files that match the given specification. /AFTER:date-time Select only those files having a date-time later than the one given. See [360]Section 1.6 for date-time formats. Synonym: /SINCE. /NOT-AFTER:date-time Select only those files having a date-time not later than (i.e. earlier or equal to) the one given. Synonym: /NOT-SINCE. /BEFORE:date-time Select only those files having a date-time earlier than the one given. /NOT-BEFORE:date-time Select only those files having a date-time not earlier than (i.e. later or equal to) the one given. /DOTFILES UNIX and OS-9 only: The filespec is allowed to match files whose names start with (dot) period. Normally these files are not shown. /NODOTFILES (UNIX and OS-9 only) Don't show files whose names start with dot (period). This is the opposite of /DOTFILES, and is the default. Note that when a directory name starts with a period, the directory and (in recursive operations) all its subdirectories are skipped. /LARGER-THAN:number Only select files larger than the given number of bytes. /SMALLER-THAN:number Only select files smaller than the given number of bytes. /EXCEPT:pattern Specifies that any files whose names match the pattern, which can be a regular filename, or may contain "*" and/or "?" metacharacters (wildcards), are not to be selected. Example: send /except:*.log *.* sends all files in the current directory except those with a filetype of ".log". Another: send /except:*.~*~ *.* sends all files except the ones that look like Kermit or EMACS backup files (such as "oofa.txt.~17~") (of course you can also use the /NOBACKUP switch for this). The pattern matcher is the same one used by IF MATCH string pattern ([361]Section 7.4), so you can test your patterns using IF MATCH. If you need to match a literal * or ? (etc), precede it by a backslash (\). If the pattern contains any spaces, it must be enclosed in braces: send /except:{Foo bar} *.* The pattern can also be a list of up to 8 patterns. In this case, the entire pattern must be enclosed in braces, and each sub-pattern must also be enclosed in braces; this eliminates the need for designating a separator character, which is likely to also be a legal filename character on some platform or other, and therefore a source of confusion. You may include spaces between the subpatterns but they are not necessary. The following two commands are equivalent: send /except:{{ck*.o} {ck*.c}} ck*.? send /except:{{ck*.o}{ck*.c}} ck*.? If a pattern is to include a literal brace character, precede it with "\". Also note the apparent conflict of this list format and the string-list format described in [362]Section 4.9.1. In case you want to include a wildcard string-list with braces on its outer ends as an /EXCEPT: argument, do it like this: send /except:{{{ckuusr.c,ckuus2.c,ckuus6.c}}} ckuus*.c 1.5.5. Setting Preferences for Different Commands Certain oft-used commands offer lots of switches because different people have different requirements or preferences. For example, some people want to be able to delete files without having to watch a list of the deleted files scroll past, while others want to be prompted for permission to delete each file. Different people prefer different directory-listing styles. And so on. Such commands can be tailored with the SET OPTIONS command: SET OPTIONS command [ switch [ switch [ ... ] ] ] Sets each switch as the default for the given command, replacing the "factory default". Of course you can also override any defaults established by the SET OPTIONS command by including the relevant switches in the affected command any time you issue it. SHOW OPTIONS Lists the commands that allows option-setting, and the options currently in effect, if any, for each. Switches that have synonyms are shown under their primary name; for example. /LOG and /VERBOSE are shown as /LIST. Commands for which options may be set include DIRECTORY, DELETE, PURGE, and TYPE. Examples: SET OPTIONS DIRECTORY /PAGE /NOBACKUP /HEADING /SORT:DATE /REVERSE SET OPTIONS DELETE /LIST /NOHEADING /NOPAGE /NOASK /NODOTFILES SET OPTIONS TYPE /PAGE Not necessarily all of a command's switches can be set as options. For example, file selection switches, since these would normally be different for each command. Put the desired SET OPTIONS commands in your C-Kermit customization file for each command whose default switches you want to change every time you run C-Kermit. 1.6. Dates and Times Some commands and switches take date-time values, such as: send /after:{8-Feb-2000 10:28:01} Various date-time formats are acceptable. The rules for the date are: * The year must have 4 digits. * If the year comes first, the second field is the month. * The day, month, and year may be separated by spaces, /, -, or underscore. * The month may be numeric (1 = January) or spelled out or abbreviated in English. If the date-time string contains any spaces, it must be enclosed in braces. Examples of legal dates: Interpretation: 2000-Feb-8 8 February 2000 {2000 Feb 8} 8 February 2000 2000/Feb/8 8 February 2000 2000_Feb_8 8 February 2000 2000-2-8 8 February 2000 2000-02-08 8 February 2000 8-Feb-2000 8 February 2000 08-Feb-2000 8 February 2000 12/25/2000 25 December 2000 25/12/2000 25 December 2000 The last two examples show that when the year comes last, and the month is given numerically, the order of the day and month doesn't matter as long as the day is 13 or greater (mm/dd/yyyy is commonly used in the USA, whereas dd/mm/yyyy is the norm in Europe). However: 08/02/2000 Is ambiguous and therefore not accepted. If a date is given, the time is optional and defaults to 00:00:00. If the time is given with a date, it must follow the date, separated by space, /, -, or underscore, and with hours, minutes, and seconds separated by colon (:). Example: 2000-Feb-8 10:28:01 Represents 8 February 2000, 10:28:01am If a date is not given, the current date is used and a time is required. Time format is hh:mm:ss or hh:mm or hh in 24-hour format, or followed by "am" or "pm" (or "AM" or "PM") to indicate morning or afternoon. Examples of times that are acceptable: Interpretation: 3:23:56 3:23:56am 3:23:56am 3:23:56am 3:23:56pm 3:23:56pm = 15:23:56 15:23:56 3:23:56pm = 15:23:56 3:23pm 3:23:00pm = 15:23:00 3:23PM 3:23:00pm = 15:23:00 3pm 3:00:00pm = 15:00:00 Examples of legal date-times: send /after:{8 Feb 2000 10:28:01} send /after:8_Feb_2000_10:28:01 send /after:8-Feb-2000/10:28:01 send /after:2000/02/08/10:28:01 send /after:2000/02/08_10:28:01 send /after:2000/02/08_10:28:01am send /after:2000/02/08_10:28:01pm send /after:2000/02/08_10:28pm send /after:2000/02/08_10pm send /after:10:00:00pm send /after:10:00pm send /after:10pm send /after:22 Finally, there is a special all-numeric format you can use: yyyymmdd hh:mm:ss For example: 20000208 10:28:01 This is Kermit's standard date-time format (based on ISO 8601), and is accepted (among other formats) by any command or switch that requires a date-time, and is output by any function whose result is a calendar date-time. There are no optional parts to this format and it must be exactly 17 characters long, punctuated as shown (except you can substitute underscore for space in contexts where a single "word" is required). The time is in 24-hour format (23:00:00 is 11:00pm). This is the format returned by \fdate(filename), so you can also use constructions like this: send /after:\fdate(oofa.txt) which means "all files newer than oofa.txt". Besides explicit dates, you can also use the any of the following shortcuts: TODAY Stands for the current date at 00:00:00. TODAY 12:34:56 Stands for the current date at the given time. YESTERDAY Stands for yesterday's date at 00:00:00. A time may also be given. TOMORROW Stands for tomorrow's date at 00:00:00. A time may also be given. + number { DAYS, WEEKS, MONTHS, YEARS } [ time ] Is replaced by the future date indicated, relative to the current date. If the time is omitted, 00:00:00 is used. Examples: +3days, +2weeks, +1year, +37months. - number { DAYS, WEEKS, MONTHS, YEARS } [ time ] Is replaced by the past date indicated, relative to the current date. If the time is omitted, 00:00:00 is used. The time can be separated from the date shortcut by any of the same separators that are allowed for explicit date-times: space, hyphen, slash, period, or underscore. In switches and other space-delimited fields, use non-spaces to separate date/time fields, or enclose the date-time in braces, e.g.: purge /before:-4days_12:00:00 purge /before:{- 4 days 12:00:00} Of course you can also use variables: define \%n 43 purge /before:-\%ndays_12:00:00 Shortcut names can be abbreviated to any length that still distinguishes them from any other name that can appear in the same context, e.g. "TOD" for today, "Y" for yesterday. Also, the special abbreviation "wks" is accepted for WEEKS, and "yrs" for "YEARS". (To see how to specify dates relative to a specific date, rather than the current one, see the [363]\fmjd() function description below.) You can check date formats with the DATE command. DATE by itself prints the current date and time in standard format: yyyymmdd hh:mm:ss. DATE followed by a date and/or time (including shortcuts) converts it to standard format if it can understand it, otherwise it prints an error message. The following variables and functions deal with dates and times; any function argument designated as "date-time" can be in any of the formats described above. \v(day) The first three letters of the English word for the current day of the week, e.g. "Wed". \fday(date-time) The first three letters of the English word for day of the week of the given date. If a time is included, it is ignored. Example: \fday(8 Feb 1988) = "Mon". \v(nday) The numeric day of the week: 0 = Sunday, 1 = Monday, ..., 6 = Saturday. \fnday(date-time) The numeric day of the week for the given date. If a time is included, it is ignored. Example: \fnday(8 Feb 1988) = "1". \v(date) The current date as dd mmm yyyy, e.g. "08 Feb 2000" (as in this example, a leading zero is supplied for day-of-month less than 10). \v(ndate) The current date in numeric format: yyyymmdd, e.g. "20000208". \v(time) The current time as hh:mm:ss, e.g. "15:27:14". \ftime(time) The given free-format date and/or time (e.g. "3pm") returns the time (without the date) converted to hh:mm:ss 24-hour format, e.g. "15:00:00" (the date, if given, is ignored). \v(ntime) The current time as seconds since midnight, e.g. "55634". \v(tftime) The elapsed time of the most recent file-transfer operation in seconds. \v(intime) The elapsed time for the most recent INPUT command to complete, in milliseconds. \fntime(time) The given free-format date and/or time is converted to seconds since midnight (the date, if given, is ignored). This function replaces \ftod2secs(), which is now a synonym for \fntime(). Unlike \ftod2secs(), \fntime() allows a date to be included, and it allows the time to be in free format (like 3pm), and it allows the amount of time to be more than 24 hours. E.g. \fntime(48:00:00) = 172800. Example of use: set alarm \fntime(48:00:00) ; set alarm 48 hours from now. \fn2time(seconds) The given number of seconds is converted to hh:mm:ss format. \fdate(filename) Returns the modification date-time of the given file in standard format: yyyymmdd hh:mm:ss. \fcvtdate(date-time) Converts a free-format date and/or time to Kermit standard format: yyyymmdd hh:mm:ss. If no argument is given, returns the current date-time in standard format. If a date is given but no time, the converted date is returned without a time. If a time is given with no date, the current date is supplied. Examples: \fcvtdate(4 Jul 2000 2:21:17pm) = 20000704 14:21:17 \fcvtdate() = 20000704 14:21:17 (on 4 Jul 2000 at 2:21:17pm). \fcvtd(4 Jul 2000) = 20000704 \fcvtd(6pm) = 20000704 18:00:00 (on 4 Jul 2000 at 6:00pm). \fdayofyear(date-time) \fdoy(date-time) Converts a free-format date and/or time to yyyyddd, where ddd is the 3-digit day of the year, and 1 January is Day 1. If a time is included with the date, it is returned in standard format. If a date is included but no time, the date is returned without a time. If a time is given with no date, the time is converted and the current date is supplied. If no argument is given, the current date-time is returned. Synonym: \fdoy(). Examples: \fddayofyear(4 Jul 2000 2:21:17pm) = 2000185 14:21:17 \fdoy() = 2000185 14:21:17 (on 4 Jul 2000 at 2:21:17pm). \fdoy(4 Jul 2000) = 2000185 \fdoy(6pm) = 2000185 18:00:00 (on 4 Jul 2000 at 6:00pm). Note: The yyyyddd day-of-year format is often erroneously referred to as a Julian date. However, a true Julian date is a simple counting number, the number of days since a certain fixed day in the past. [364]See \fmjd() below. \fdoy2date(date-time) Converts a date or date-time in day-of-year format to a standard format date. A yyyyddd-format date must be supplied; time is optional. The given date is converted to yyyymmdd format. If a time is given, it is converted to 24-hour format. Examples: \fdoy2date(2000185) = 20000704 \fdoy2(2000185 3pm) = 20000704 15:00:00 \fmjd(date-time) Converts free-format date and/or time to a Modified Julian Date (MJD), the number of days since 17 Nov 1858 00:00:00. If a time is given, it is ignored. Examples: \fmjd(4 Jul 2000) = 50998 \fmjd(17 Nov 1858) = 0 \fmjd(16 Nov 1858) = -1 \fmjd2date(mjd) Converts an MJD (integer) to standard date format, yyyymmdd: \fmjd2(50998) = 4 Jul 1998 \fmjd2(0) = 17 Nov 1858 \fmjd2(-1) = 16 Nov 1858 \fmjd2(-365) = 17 Nov 1857 MJDs are normal integers and, unlike DOYs, may be added, subtracted, etc, with each other or with other integers, to obtain meaningful results. For example, to find out the date 212 days ago: echo \fmjd2date(\fmjd()-212) Constructions such as this can be used in any command where a date-time is required, e.g.: send /after:\fmjd2date(\fmjd()-212) to send all files that are not older than 212 days (this is equivalent to "send /after:-212days"). MJDs also have other regularities not exhibited by other date formats. For example, \fmodulus(\fmjd(any-date),7) gives the day of the week for any date (where 4=Sun, 5=Mon, ..., 3=Sat). (However, it is easier to use \fnday() for this purpose, and it gives the more conventional result of 0=Sun, 1=Mon, ..., 6=Sat). Note that if MJDs are to be compared, they must be compared numerically (IF <, =, >) and not lexically (IF LLT, EQUAL, LGT), whereas DOYs must be compared lexically if they include a time (which contains ":" characters); however, if DOYs do not include a time, they may also be compared numerically. In any case, lexical comparison of DOYs always produces the appropriate result, as does numeric comparison of MJDs. The same comments apply to sorting. Also note that DOYs are fixed length, but MJDs can vary in length. However, all MJDs between 3 April 1886 and 30 Aug 2132 are 5 decimal digits long. (MJDs become 6 digits long on 31 Aug 2132, and 7 digits long on 13 Oct 4596). 1.7. Partial Completion of Keywords Partial completion of keywords was added in C-Kermit 7.0. In prior versions, if completion was attempted (by pressing the Esc or Tab key) on a string that matched different keywords, you'd just get a beep. Now Kermit completes up to the first character where the possibly matching keywords differ and then beeps. For example: C-Kermit> send /n which matches /NOT-BEFORE and /NOT-AFTER, now completes up to the dash: C-Kermit> send /not- Partial completion works for filenames too (as it has for some years). 1.8. Command Recall C-Kermit has had a command history buffer for some time, which could be scrolled interactively using control characters or (in Kermit 95 only) arrow keys. Version 7.0 adds a REDO command that allows the most recent command matching a given pattern to be re-executed: { REDO, RR, ^ } [ pattern ] Search the command history list for the most recent command that matches the given pattern, and if one is found, execute it again. The pattern can be a simple string (like "send"), in which case the last SEND command is re-executed. Or it can contain wildcard characters "*" and/or "?", which match any string and any single character, respectively (note that "?" must be preceded by backslash to override its normal function of giving help), and in most C-Kermit versions may also include [] character lists and {} string lists (see [365]Section 4.9). The match works by appending "*" to the end of the given pattern (if you didn't put one there yourself). Thus "redo *oofa" becomes "redo *oofa*" and therefore matches the most recent command that contains "oofa" anywhere within the command. If you want to inhibit the application of the trailing "*", e.g. to force matching a string at the end of a command, enclose the pattern in braces: redo {*oofa} matches the most recent command that ends with "oofa". REDO commands themselves are not entered into the command history list. If no pattern is given, the previous (non-REDO) command is re-executed. The REDOne command is reinserted at the end of the command history buffer, so the command scrollback character (Ctrl-P, Ctrl-B, or Uparrow) can retrieve it. Examples: C-Kermit> echo foo foo C-Kermit> show alarm (no alarm set) C-Kermit> echo blah blah C-Kermit> redo ; Most recent command blah C-Kermit> redo s ; Most recent command starting with "s" (no alarm set) C-Kermit> redo echo f ; Most recent command starting with "echo f" foo C-Kermit> redo *foo ; Most recent command that has "foo" in it foo C-Kermit> ; Scroll back C-Kermit> echo foo ; The REDOne command is there C-Kermit> redo {*foo} ; Most recent command that ends with "foo" foo C-Kermit> Since REDO, REDIAL, and REDIRECT all start the same way, and RED is the designated non-unique abbreviation for REDIAL, REDO must be spelled out in full. For convenience, RR is included as an invisible easy-to-type synonym for REDO. You can also use the "^" character for this: C-Kermit> ^ ; Most recent command C-Kermit> ^ s ; Most recent command starting with "s" C-Kermit> ^s ; Ditto (space not required after "^"). C-Kermit> ^*foo ; Most recent command that has "foo" in it. C-Kermit> ^{*foo} ; Most recent command ends with "foo". Unlike the manual command-history-scrolling keys, the REDO command can be used in a script, but it's not recommended (since the command to be REDOne might not be found, so if the REDO command fails, you can't tell whether it was because REDO failed to find the requested command, or because the command was found but it failed). 1.9. EXIT Messages The EXIT and QUIT commands now accept an optional message to be printed. This makes the syntax of EXIT and QUIT just like END and STOP: { EXIT, QUIT, END, STOP } [ status-code [ message ] ] where status-code is a number (0 indicating success, nonzero indicating failure). This is handy in scripts that are never supposed to enter interactive mode: dial 7654321 if fail exit 1 Can't make connection - try again later. Previously this could only be done in two steps: dial 7654321 xif fail { echo Can't make connection - try again later, exit 1 } A status code must be included in order to specify a message. In the case of EXIT and QUIT, the default status code is contained in the variable \v(exitstatus), and is set automatically by various events (file transfer failures, etc; it can also be set explicitly with the SET EXIT STATUS command). If you want to give an EXIT or QUIT command with a message, but without changing the exit status from what it normally would have been, use the \v(exitstatus) variable, e.g.: exit \v(exitstatus) Goodbye from \v(cmdfile). The EXIT status is returned to the system shell or whatever other process invoked C-Kermit, e.g. in UNIX: C-Kermit> exit 97 bye bye bye bye $ echo $? 97 $ 1.10. Managing Keyboard Interruptions When C-Kermit is in command or file-transfer mode (as opposed to CONNECT mode), it can be interrupted with Ctrl-C. Version 7.0 adds the ability to disarm the Ctrl-C interrupt: SET COMMAND INTERRUPT { ON, OFF } COMMAND INTERRUPT is ON by default, meaning the Ctrl-C can be used to interrupt a command or a file transfer in progress. Use OFF to disable these interruptions, and use it with great caution for obvious reasons. SET TRANSFER INTERRUPT { ON, OFF } This can be used to disable keyboard interruption of file transfer when C-Kermit is in local mode, or to re-enable it after it has been disabled. This applies to the X, Z, E, and similar keys as well as to the system interrupt character, usually Ctrl-C. This is distinct from SET TRANSFER CANCELLATION, which tells whether packet mode can be exited by sending a special sequence of characters. Several other commands can be interrupted by pressing any key while they are active. Version 7.0 adds the ability to disable this form of interruption also: SET INPUT CANCELLATION { ON, OFF } Whether an INPUT command in progress can be interrupted by pressing a key. Normally ON. Setting INPUT CANCELLATION OFF makes INPUT commands uninterruptible except by Ctrl-C (unless COMMAND INTERRUPTION is also OFF). SET SLEEP CANCELLATION { ON, OFF } Whether a SLEEP, PAUSE, or WAIT command in progress can be interrupted by pressing a key. Normally ON. Setting SLEEP CANCELLATION OFF makes these commands uninterruptible except by Ctrl-C (unless COMMAND INTERRUPTION is also OFF). Synonyms: SET PAUSE CANCELLATION, SET WAIT CANCELLATION. So to make certain a script is not interruptible by the user, include these commands: SET TRANSFER INTERRUPT OFF SET SLEEP CANCELLATION OFF SET INPUT CANCELLATION OFF SET COMMAND INTERRUPTION OFF Make sure to turn them back on afterwards if interruption is to be re-enabled. When a PAUSE, SLEEP, WAIT, or INPUT command is interrupted from the keyboard, the new variable \v(kbchar) contains a copy of the (first) character that was typed and caused the interruption, provided it was not the command interrupt character (usually Ctrl-C). If these commands complete successfully or time out without a keyboard interruption, the \v(kbchar) variable is empty. The \v(kbchar) variable (like any other variable) can be tested with: if defined \v(kbchar) command The command is executed if the variable is not empty. The \v(kbchar) variable can be reset with WAIT 0 (PAUSE 0, SLEEP 0, etc). 1.11. Taming The Wild Backslash -- Part Deux [366]Using C-Kermit, 2nd Edition, contains a brief section, "Taming the Wild Backslash", on page 48, which subsequent experience has shown to be inadequate for Kermit users intent on writing scripts that deal with Windows, DOS, and OS/2 filenames, in which backslash (\) is used as the directory separator. This section fills in the blanks. 1.11.1. Background The Kermit command language shares a certain unavoidable but annoying characteristic with most other command languages that are capable of string replacement, namely the necessity to "quote" certain characters when you want them to be taken literally. This is a consequence of the facts that: 1. One or more characters must be set aside to denote replacement, rather than acting as literal text. 2. We have only 96 printable characters to work with in ASCII, which is still the only universally portable character set. 3. There is no single printable character that is unused everywhere. 4. Variables are not restricted to certain contexts, as they are in formal programming languages like C and Fortran, but can appear anywhere at all within a command, and therefore require special syntax. Thus there can be conflicts. To illustrate, the standard UNIX shell uses dollar sign ($) to introduce variables. So the shell command: echo $TERM displays the value of the TERM variable, e.g. vt320. But suppose you want to display a real dollar sign: echo The price is $10.20 This causes the shell to evaluate the variable "$1", which might or might not exist, and substitute its value, e.g.: The price is 0.20 (in this case the $1 variable had no value.) This is probably not what you wanted. To force the dollar sign to be taken literally, you must apply a "quoting rule", such as "precede a character by backslash (\) to force the shell to take the character literally": echo The price is \$10.20 The price is $10.20 But now suppose you want the backslash AND the dollar sign to be taken literally: echo The price is \\$10.20 This doesn't work, since the first backslash quotes the second one, thereby leaving the dollar sign unquoted again: The price is \0.20 Quoting the dollar sign requires addition of a third backslash: echo The price is \\\$10.20 The price is \$10.20 The first backslash quotes the second one, and the third backslash quotes the dollar sign. Every command language -- all UNIX shells, VMS DCL, DOS Batch, AOS/VS CLI, etc etc -- has similar rules. UNIX shell rules are probably the most complicated, since many printable characters -- not just one -- are special there: dollar sign, single quote, double quote, backslash, asterisk, accent grave, number sign, ampersand, question mark, parentheses, brackets, braces, etc -- practically every non-alphanumeric character needs some form of quoting if it is to be taken literally. And to add to the confusion, the UNIX shell offers many forms of quoting, and many alternative UNIX shells are available, each using slightly different syntax. 1.11.2. Kermit's Quoting Rules Kermit's basic quoting rules are simple by comparison (there are, of course, additional syntax requirements for macro definitions, command blocks, function calls, etc, but they are not relevant here). The following characters are special in Kermit commands: Backslash (\) Introduces a variable, or the numeric representation of a special character, or a function, or other item for substitution. If the backslash is followed by a digit or by any of the following characters: x, o, d, m, s, f, v, $, %, &, :, { this indicates a special substitution item; otherwise the following character is to be taken literally (exceptions: \ at end of line is taken literally; \n, \b, and \n are special items in the OUTPUT command only). Semicolon (;) (Only when at the beginning of a line or preceded by at least one space or tab) Introduces a comment. Number sign (#) (Only when at the beginning of a line or preceded by at least one space or tab) Just like semicolon; introduces a comment. Question mark (?) (Only at the command prompt - not in command files or macros) Requests context-sensitive help. To force Kermit to take any of these characters literally, simply precede it by a backslash (\). Sounds easy! And it is, except when backslash also has a special meaning to the underlying operating system, as it does in DOS, Windows, and OS/2, where it serves as the directory separator in filenames such as: D:\K95\KEYMAPS\READ.ME Using our rule, we would need to refer to this file in Kermit commands as follows: D:\\K95\\KEYMAPS\\READ.ME But this would not be obvious to new users of Kermit software on DOS, Windows, or OS/2, and it would be annoying to seasoned ones. Thus MS-DOS Kermit and Kermit 95 go to rather extreme lengths to allow the more natural notation, as in: send d:\k95\keymaps\read.me The reason this is tricky is that we also need to allow for variables and other expressions introduced by backslash in the same command. For example, suppose \%a is a variable whose value is "oofa" (without the quotes). What does the following command do? send d:\%a Does it send the file named "oofa" in the current directory of the D: disk, or does it send a file named "%a" in the root directory of the D: disk? This is the kind of trouble we get into when we attempt to bend the rules in the interest of user friendliness. (The answer is: if the variable \%a has definition that is the name of an existing file, that file is sent; if a file d:\%a exists, it is sent; otherwise if both conditions are true, the variable takes precedence, and the literal filename can be forced by quoting: \\%a.) In Kermit 95 (but not MS-DOS Kermit), we also bend the rules another way by allowing you to use forward slash (/) rather than backslash (\) as the directory separator: send d:/k95/keymaps/read.me This looks more natural to UNIX users, and in fact is perfectly acceptable to the Windows 95/98/NT and OS/2 operating systems on the API level. BUT (there is always a "but") the Microsoft shell, COMMAND.COM, for Windows 95/98 and NT does not allow this notation, and therefore it can not be used in any Kermit command -- such as RUN -- that invokes the Windows command shell AND your command shell is COMMAND.COM or any other shell that does not allow forward slash as directory separator (some alternative shells do allow this). NOTE: There exists a wide variety of alternative shells from third parties that do not have this restriction. If you are using a shell that accepts forward slash as a directory separator, you can stop reading right now -- UNLESS (there is always an "unless") you want your scripts to be portable to systems that have other shells. Also note that some Windows shells might actually REQUIRE forward slashes (instead of backslashes) as directory separators; we do not treat this situation below, but the treatment is obvious -- use slash rather backslash as the directory separator. 1.11.3. Passing DOS Filenames from Kermit to Shell Commands The following Kermit commands invoke the system command shell: RUN (and its synonyms ! and @) REDIRECT PIPE Each of these commands takes a shell command as an operand. These shell commands are not, and can not be, parsed by Kermit since Kermit does not know the syntax of shell commands, and so can't tell the difference between a keyword, a filename, a variable, a switch, or other item. Therefore the rules can not be bent since Kermit doesn't know where or how to bend them. To illustrate (using the regular Windows shell): run c:\\windows\\command\\chkdsk.exe works OK, but: run c:/windows/command/chkdsk.exe is not accepted by COMMAND.COM. But: run c:\windows\command\chkdsk.exe results in Kermit applying its quoting rules before sending the text to the shell. Since "w" and "c" are not in the list of backslash-item codes, the backslash means "take the following character literally". Thus, by the time this filename gets to the Windows shell, it has become: c:windowscommandchkdsk.exe which is probably not what you wanted. (If "w" and "c" were in the list, the results could be even stranger.) Even more confusing is the case where a directory or filename starts with one or more digits: run c:\123\lotus.exe in which "\123" is the Kermit notation for ASCII character 123, which happens to be left brace ({), resulting in "c:{lotus.exe". So when passing filenames to a Windows shell, always use double backslashes as directory separators, to ensure that the shell gets single backslashes: run c:\\windows\\command\\chkdsk.exe run c:\\123\\lotus.exe Similar problems might occur with the built-in EDIT, BROWSE, and FTP commands. These commands result in Kermit building a shell command internally to invoke the associated helper program; the form of this command might conflict with the form demanded by certain alternative shells. 1.11.4. Using Variables to Hold DOS Filenames Now to the next level. Suppose you want to write a script in which filenames are parameters, and therefore are stored in variables. Example: define \%f c:\windows\command\chkdsk.exe ... run \%f Obviously this won't work for the reasons just noted; the RUN command requires directory separators be coded as double backslashes: define \%f c:\\windows\\command\\chkdsk.exe ... run \%f This will work; no surprises here. However, if you had used ASSIGN rather than DEFINE, you might have been surprised after all; review pages 348-349 of [367]Using C-Kermit (2nd Ed) for the difference between DEFINE and ASSIGN. We have said that any Kermit 95 or MS-DOS Kermit command that parses filenames itself -- SEND, for example -- does not require double backslashes since it knows it is parsing a filename. So since the following works: send c:\windows\command\chkdsk.exe Should the following also work? define \%f c:\windows\command\chkdsk.exe ... send \%f Answer: No. Why? Because \%f is evaluated "recursively", to allow for the possibility that its definition contains further variable references. This is true of all "backslash-percent-letter" (or -digit) variables, and also for array references. So \%f becomes c:\windows\command\chkdsk.exe, which becomes c:windowscommandchkdsk.exe. The trick here is to use the "other" kind of variable, that is evaluated only "one level deep" rather than recursively: define filename c:\windows\command\chkdsk.exe ... send \m(filename) Similarly if you want to prompt the user for a filename: ask filename { Please type a filename: } Please type a filename: c:\windows\command\chkdsk.exe send \m(filename) 1.11.5. Passing DOS Filenames as Parameters to Macros Suppose you want to pass a DOS filename containing backslashes as a parameter to a Kermit macro. This raises two issues: 1. Parameters to macros are "just text" and so are fully evaluated before they are passed to the macro. 2. Once inside the macro, the formal parameters \%1, \%2, ... \%9 are the type of variable that is evaluated recursively. Thus a DOS filename is ruined once in the act of parsing the macro invocation, and again when referring to it from within the macro. To illustrate, suppose "test" is a macro. Then in the invocation: test c:\mydir\blah.txt "c:mydirblah.txt" is assigned to \%1. However, if we double the backslashes: test c:\\mydir\\blah.txt "c:\mydir\blah.txt" is assigned to \%1. But then when you refer to \%1 in the macro, it is evaluated recursively, resulting in "c:mydirblah.txt". To illustrate: define test echo \%1 test c:\mydir\blah.txt c:mydirblah.txt test c:\\mydir\\blah.txt c:mydirblah.txt test c:\\\\mydir\\\\blah.txt c:\mydir\blah.txt Let's address each part of the problem separately. First, inside the macro. You can use the \fcontents() function to force a backslash-percent variable (such as a macro argument) to be evaluated one level deep instead of recursively, for example: define test echo { The filename is "\fcontents(\%1)"} test c:\mydir\blah.txt ; We don't expect this to work The filename is "c:mydirblah.txt" ; and it doesn't. test c:\\mydir\\blah.txt ; But this does... The filename is "c:\mydir\blah.txt" Thus if the filename arrives inside the macro with single backslashes, the backslashes are preserved if you always refer to the parameter through the \fcontents() function. Now how to ensure that backslashes are not stripped or misinterpreted when passing a filename to a macro? This brings us back to what we learned in earlier sections: 1. If it is a literal filename, either double the backslashes, or (if the filename is to be used only within Kermit itself and not passed to a DOS shell, or it is to be passed to an alternative shell that accepts forward slash as a directory separator), use forward slash instead of backslash as the directory separator. 2. If it is a variable that contains a filename, make sure you use a macro-style variable name, rather than a backslash-percent-character name. Examples: define test echo \fcontents(\%1) define filename c:\mydir\blah.txt test c:\\mydir\\blah.txt ; Literal filename with double backslashes c:\mydir\blah.txt test c:/mydir/blah.txt ; Literal filename with forward slashes c:/mydir/blah.txt test \m(filename) ; Variable c:\mydir\blah.txt But what if you don't like these rules and you still want to pass a literal filename containing single backslashes to a macro? This is possible too, but a bit tricky: turn command quoting off before invoking the macro, and then turn it back on inside the macro. Example: define test set command quoting on, echo \fcontents(\%1) set command quoting off test c:\mydir\blah.txt c:\mydir\blah.txt Upon return from the macro, command quoting is back on (since the macro turned it on). Obviously this trick can not be used if the filename is stored in a variable, since it prevents the variable from being evaluated. 1.11.6. Passing DOS File Names from Macro Parameters to the DOS Shell Now suppose you need to pass a DOS filename to a macro, and the macro needs to pass it, in turn, to the Windows shell via (say) Kermit's RUN command. This works too: define xrun run \fcontents(\%1) xrun c:\\windows\\command\\chkdsk.exe (or you can use the SET COMMAND QUOTING OFF / ON technique described above to avoid the double backslashes.) But.. xrun c:/windows/command/chkdsk.exe does not work if the Windows shell does not recognize "/" as a directory separator. If there is a chance that a filename might be passed to the macro in this form, the macro will need to convert it to a form acceptable to the shell: define xrun run \freplace(\fcontents(\%1),/,\\) Here we replace all occurrences (if any) of "/" in the argument with "\" prior to issuing the RUN command. Of course, in order to specify "\" as a literal character in the \freplace() argument list, we have to double it. 1.11.7. Passing DOS Filenames to Kermit from the Shell As noted in the manual, the \&@[] array contains Kermit's command-line arguments. Suppose one of these arguments, say \&@[3], is a DOS filename such as C:\FOO\BAR\BAZ\OOFA.TXT. (Note: In C-Kermit 7.0 and K95 1.1.18 and later, command-line arguments after "=" or "--" are also available in the top-level \%1..9 variables; see [368]Section 7.5.) Of course you can eliminate any problems by using forward slashes rather than backslashes in the filename, but sometimes this is not possible, as when the Kermit command line is being generated by another program than can only generate "native" format DOS filenames. As noted in the manual, "\%x" variables and \&x[] arrays are always evaluated "all the way" (recursively). If the contents of one of these variables contains backslashes, this causes another level of evaluation. There is another kind of variable, which is evaluated only "one level deep". You can use this to prevent interpretation of the backslashes in the filenames. Example: assign filename \fcontents(\&@[3]) ; Transfer contents ... send \m(filename) Or, more simply: send \fcontents(\&@[3]) 1.12. Debugging The debug log is produced when you give a "log debug" command. This is normally done at the request of the Kermit help desk, for forwarding to the Kermit developers for analysis as a last resort in troubleshooting problems. (Last resort because it can grow quite huge in a very short time.) In cases where timing information is critical to understanding a problem, you can tell C-Kermit to put a timestamp on each debug log line by giving the command: SET DEBUG TIMESTAMP ON At any time before or after activating the debug log (SET DEBUG TIMESTAMP OFF turns off timestamping). Timestamps can be turned off and on as desired while logging. Obviously, they increase the size and growth rate of the log significantly, and so should be used sparingly. Timestamps are of the form hh:mm:ss.xxx, where .xxx is thousands of a second (but is included only on platforms that include this feature). 1.13. Logs In UNIX C-Kermit and in K-95, you can now direct any log to a pipe. This not only lets you send your logs to places other than disk files, but also lets you customize them to any desired degree. LOG { DEBUG, PACKETS, SESSION, TRANSACTION, CONNECTION } { file, pipe } ... A "pipe" is the name of a command, preceded by a vertical bar. If the pipe contains any spaces, it must be enclosed in braces. Here are some examples for UNIX (always remember the importance of getting the UNIX shell quoting rules right): LOG TRANSACTIONS |lpr This sends the transaction log to the default UNIX printer, rather than to a file (use "lp" rather than "lpr" if necessary). LOG TRANSACTIONS {| myfilter > t.log} For those who don't like the format of the transaction log, or want to extract certain information from it; write your own output filter. LOG SESSION {| lpr -Plaserwriter} This sends the session log to a specific UNIX printer, rather than to a file. Note the braces around the pipeline. These are required because it contains spaces. LOG DEBUG {| tail -100 > debug.log} This causes the debug log file to contain only the final 100 lines. Suppose C-Kermit crashes under some unpredictable circumstances, and you need a debug log to catch it in the act. But the debug log can grow to huge proportions very quickly, possibly filling up the disk. Piping the debug log through "tail" results in keeping only the last 100 lines (or other number of your choice). LOG DEBUG {| grep "^TELNET" > debug.log} This one shows how to log only Telnet negotiations. Piping the debug log through grep or egrep lets you log only specific information, rather than everything. "man grep" for further info. LOG DEBUG {| gzip -c > debug.log.gz} Creates a full debug log, but compressed by gzip to save space. LOG PACKETS {| tr "\\01" "X" | cut -c9- > packet.log} This one writes the regular packet log, but translates the Ctrl-A that starts each packet to the letter "X" and removes the s-nn-nn- notation from the beginning of each line. Note the double backslash (normal Kermit quoting rules). "man tr" and "man cut" for further info. See [369]Section 2.12 for information about the new connection log. 1.14. Automatic File-Transfer Packet Recognition at the Command Prompt Beginning in version 7.0, C-Kermit can recognize Kermit (and in some cases also Zmodem) file-transfer packets while at its command prompt. This is convenient (for example), if you escaped back from a remote Kermit program and told the local Kermit program to send a file, but forgot to tell the remote Kermit program to receive it (and the local Kermit did not have the "send a Kermit receive command" feature available). This feature is controlled by the following command: SET COMMAND AUTODOWNLOAD { ON, OFF } When ON, which is the default, the command parser recognizes Kermit packets when Kermit is in remote mode. An S packet makes it go into receive mode, an I packet makes it go into server mode. When OFF, packet recognition is disabled and the behavior when a packet is received at the command prompt is as it was in C-Kermit 6.1 and earlier (namely to print an error message). COMMAND AUTODOWNLOAD is the command-mode equivalent of TERMINAL AUTODOWNLOAD, which is effective during CONNECT mode. 1.15. The TYPE Command The TYPE command now accepts a selection of optional switches ([370]Section 1.5), and also sets several variables. Syntax: TYPE [ switches... ] filename Variables: \v(ty_ln) Line number of current line (during TYPE command; see /PREFIX) \v(ty_lc) Line count of file most recently TYPEd. \v(ty_mc) Match count of file most recently TYPEd (see /MATCH). Switches: /PAGE If /PAGE is included, Kermit pauses at the end of each screenful and issues a "more?" prompt. You may press the space bar to view the next page (screenful), or press "q" or "n" to return to the C-Kermit prompt. If this switch is given, it overrides the COMMAND MORE-PROMPTING setting for this command only. If it is not given, paging is according to COMMAND MORE-PROMPTING. /NOPAGE Do not pause at the end of each screenful; show the whole file (or all selected lines) at once. If this switch is given, it overrides the COMMAND MORE-PROMPTING setting for this command only. If it is not given, paging is according to COMMAND MORE-PROMPTING. /HEAD[:n] Only show the first n lines of the file (where n is a number). If n is omitted, 10 is used. /TAIL[:n] Only show the last n lines of the file (where n is a number). If nis omitted, 10 is used. Note: /HEAD and /TAIL can't be combined; if you give both switches, only the most recent one is used. /MATCH:pattern Only type lines from the file that match the given pattern (see [371]Section 4.9.1 for pattern notation). UNIX users familiar with grep should note a significant difference: there is no implied "*" at the beginning and end of the pattern. Thus: TYPE /MATCH:foo Lists lines whose entire contents are "foo". TYPE /MATCH:foo* Lists lines that start with "foo". TYPE /MATCH:*foo Lists lines that end with "foo". TYPE /MATCH:*foo* Lists lines that have "foo" anywhere in them. /HEAD and /TAIL apply after /MATCH, so "type /tail:20 /match:x*" shows the last 20 lines in the file that start with "x". /PREFIX:string Print the given string at the beginning of each line. The string may be a constant, a variable, or a quoted variable. If it's an unquoted variable, its value at the time the TYPE command was given is used as a constant. If it is a quoted variable, it is re-evaluated for each line; a useful variable for this context is \v(ty_ln) (the line number of the current line being typed). If the prefix is to include spaces, it must be enclosed in braces. Examples: type /prefix:{oofa.txt: } /match:*thing* oofa.txt Prints all lines in oofa.txt that contain "thing" with the filename itself as the prefix (similar to UNIX grep). type /prefix:{\v(time). } oofa.txt Prefixes each line of oofa.txt with the time at which the TYPE command was given (one backslash) type /prefix:{\\v(time). } oofa.txt Prefixes each line of oofa.txt with the time at which that line is being typed (two backslashes). type /prefix:{\\v(ty_ln). } oofa.txt Prefixes each line of oofa.txt with its line number. type /prefix:{\\flpad(\\v(ty_ln),4). } oofa.txt Same as the previous example, except the line number is right-adjusted in a 4-column field. /WIDTH[:n] Truncates each line at column n (which must be a number) prior to printing it. This option can be used for long lines when you don't want them to wrap. If nis omitted, your current screen width is used. /COUNT Counts lines and -- if /MATCH was included, matches -- but does not print any lines from the file. The line and match count is shown at the end, and the variables \v(ty_lc) and \v(ty_lm) are set accordingly. SET OPTIONS TYPE { /PAGE, /NOPAGE, /WIDTH:n } Sets the paging default for TYPE commands, which can be overridden in any particular TYPE command by including the desired switch. If a TYPE command is given with no switch, and no SET OPTIONS TYPE selection is in effect, paging is according to your COMMAND MORE-PROMPTING setting (SHOW COMMAND). 1.16. The RESET Command The RESET command, added in 7.0, closes all open files and logs, but does not affect the open connection (if any). 1.17. The COPY and RENAME Commands As of C-Kermit 7.0, in the UNIX version only, the COPY and RENAME commands are built in and do not call the underlying platform's COPY or RENAME command. This allows them to work in "NOPUSH" versions and other circumstances where it can't access system commands, and it allows file copying and renaming to be done portably in scripts. The characteristics of the built-in COPY or RENAME include: * It fails if the source file is a directory or is wild or lacks read access. * It fails if the source file is the destination file. * It allows the destination file to be a directory, in which case the source file is copied (or renamed) into it with the same name. * It overwrites an existing destination file if its permission allows. * It sets the new file's permission according to umask but also carries forward the source file's execute permission bits if the destination file did not already exist. * It fails if interrupted by Ctrl-C. * Upon error, it prints an appropriate message. * It returns standardized error codes that can be tested by IF SUCCESS / FAIL. These commands now also accept the following switches: /LIST (/LOG, /VERBOSE) = Print "file1 => file2 (OK)" (or error message). /NOLIST (/NOLOG, /QUIET) = Don't print anything (except error messages). /NOLIST is the default. The same built-in code is used by the UNIX C-Kermit server to execute REMOTE COPY commands (except in this case no switches are available). The COPY command also accepts the following additional switches. When any of these are given (and they can be used in any combination except /SWAP and /APPEND), some of the checks listed above are relaxed, and thus it might be possible to get into trouble in certain cases, e.g. when the source and target files are the same file: /APPEND = Append source file to destination file. /SWAP-BYTES = Swap bytes (see [372]Section 6.6.5). /FROMB64 = Decode the source file from Base64 encoding. /TOB64 = Encode the target file in Base64. Base64 is the encoding commonly used for enclosures in Internet email. 1.18. The MANUAL Command The MANUAL command can be used to access the appropriate Kermit manual or other manual. The general syntax is: MANUAL [ string ] If the string is omitted, C-Kermit asks the underlying system to access the C-Kermit manual using whatever method is appropriate for the system. The specific action depends on the system. In UNIX, a "man" command is issued; "kermit" is the default argument but other manual topics may be specified. If the "man" command allows index or string searching, the appropriate syntax may be included. In Kermit 95, the MANUAL command brings up the HTML online K95 manual. In VMS and elsewhere, "man" is simply translated to "help", with a default argument of "kermit"; other and/or additional arguments may be included according to the definition of the system's "help" command. Correct operation of the "man" command in C-Kermit depends on the appropriate man page or help topic having been installed in the right place with the right permissions and format. 1.19. String and Filename Matching Patterns A pattern is a string that includes special notation for matching classes or sequences of characters. C-Kermit 7.0 / K95 1.1.19 supports patterns in several places: * Filenames ([373]Section 4.9) * SWITCH case labels ([374]Section 7.18) * The new IF MATCH statement ([375]Section 7.4) * TYPE /MATCH ([376]Section 1.15) * SET FILE TEXT-PATTERNS and BINARY-PATTERNS ([377]Section 4.3) * The \fsearch() and \farraylook() functions ([378]Sections 7.3 and [379]7.10.7) * The \fpattern() function used with [M,RE]INPUT ([380]Section 7.1) Patterns are also called wildcards, especially when used for filename matching. C-Kermit's pattern syntax is explained in [381]Section 4.9.1, and also by the HELP WILDCARDS command. 1.20. Multiple Commands on One Line As of C-Kermit 7.0, commands can be grouped together on one line by separating the commands with commas and enclosing the list in braces. For example: C-Kermit> { echo One, echo Two, echo Three } C-Kermit> do { echo One, echo Two, echo Three } Command lists can be nested: [ do ] { echo One, echo Two, if true { echo A, echo B}, echo Three } and the END command works as it does in macros: [ do ] { echo One, echo Two, if true end, echo Three } The "one line" stricture is, of course, pliant to line-continuation conventions, namely that lines ending in hyphen (-) or left brace ({) are to be continued. Thus the first example can also be rendered: [ do ] { echo One echo Two echo Three } (the "do" is optional). 1.21. What Do I Have? C-Kermit can be built for hundreds of different platforms with practically countless configuration options. Certain commands might not be available in certain configurations, etc. Even on the same platform, different builds are possible: "maximum functionality", "minimum size", "maximum performance", and so on. You can find out a lot about the configuration of your C-Kermit program with the SHOW FEATURES command. Of course, a lot of what it says, especially in the bottom part, might seem like gibberish, but can be deciphered with a Rosetta Stone (such as the C-Kermit source or the [382]ckccfg.txt file). In any case, the output from SHOW FEATURES might easily explain why some expected feature is missing, or some buffer is smaller than expected. Here's a sample of the bottom section for the SunOS version: C-Kermit 7.0.196, 1 Jan 2000 Major optional features included: Network support (type SHOW NET for further info) Telnet Kermit Option Hardware flow control External XYZMODEM protocol support Latin-1 (West European) character-set translation Latin-2 (East European) character-set translation Cyrillic (Russian, Ukrainian, etc) character-set translation Greek character-set translation Hebrew character-set translation Japanese character-set translation Unicode character-set translation Pseudoterminal control REDIRECT command RESEND command Fullscreen file transfer display Control-character unprefixing Streaming Autodownload Major optional features not included: No Kerberos(TM) authentication No SRP(TM) (Secure Remote Password) protocol No Secure Sockets Layer (SSL) protocol No Transport Layer Security (TLS) protocol No encryption No X Windows forwarding Host info: Machine: sun4m Model: (unknown) OS: SunOS OS Release: 4.1.3_U1 OS Version: 4 Target: sunos41gsc GCC version: 2.7.2 Compiled Dec 31 1999 10:38:54, options: __GNUC__ __STDC__ _POSIX_JOB_CONTROL _SC_JOB_CONTROL ARRAYREFLEN=1024 BIGBUFOK BROWSER BSD4 CK_ANSIC CK_APC CK_AUTODL CK_CURSES CK_DNS_SRV CK_ENVIRONMENT CK_FAST CK_LOGIN CK_MKDIR CK_NAWS CK_PCT_BAR CK_PERMS CK_RECALL CK_RTSCTS CK_SPEED CK_TIMERS CK_TMPDIR CK_TTGWSIZ CK_TTYFD CK_WREFRESH CKEXEC CKFLOAT=double CKGHNLHOST ckmaxfiles=64 CKMAXOPEN=64 CKMAXPATH=1023 CKREALPATH CKREGEX CKSYSLOG CKTUNING CMDBL=32763 CMDDEP=64 CONGSPD DCMDBUF DIRENT DYNAMIC FNFLOAT FORDEPTH=32 GFTIMER HADDRLIST HDBUUCP IFDEBUG IKS_OPTION IKSDB IKSDCONF INBUFSIZE=32768 INPBUFSIZ=4096 MAC_MAX=16384 MACLEVEL=128 MAXDDIR=32 MAXDNUMS=4095 MAXGETPATH=128 MAXTAKE=54 MAXWLD=102400 MSENDMAX=1024 NETCMD NETCONN NETPTY NOKVERBS NOSETBUF OBUFSIZE=32768 PARSENSE PATTERNS PIPESEND RENAME RLOGCODE SAVEDUID SELECT SIG_V SOL_SOCKET sparc STREAMING sun SUNOS4 SYSTIMEH TCPSOCKET TIMEH TLOG TNCODE TTLEBUF TTSPDLIST UIDBUFLEN=256 UNIX UNPREFIXZERO USE_LSTAT USE_MEMCPY VNAML=4096 WHATAMI XFRCAN Z_MAXCHAN=46 z_maxchan=46 ZXREWIND byte order: big endian sizeofs: int=4 long=4 short=2 char=1 char*=4 float=4 double=8 floating-point: precision=16 rounding=1 Without going into detail about what all the notation means, notice a couple things: * The Options section shows symbols ("macros") in effect during compilation, together with their values (for those that have values). The options are listed in alphabetical order to make any particular option easier to find. * MAXWLD is the maximum number of files that a wildcard can expand to. * Anything starting with "NO" is a feature (or something other than a feature) that has been deliberately "compiled out", or omitted. * Important items for script writers include: CMDBL=32763 (the size of the command buffer and therefore the maximum length for a macro or variable definition; CMDDEP=64 (the limit on recursion depth); FORDEPTH=32 (the nesting limit on FOR loops); INBUFSIZE=32768 (the size of the INPUT command circular buffer); MAC_MAX=16384 (the maximum number of macros), etc. See the [383]ckccfg.txt file for details. 1.22. Generalized File Input and Output C-Kermit 7.0 adds a new generalized I/O system for stream files, augmenting (and to some extent, overlapping with) the older OPEN, READ, WRITE, and CLOSE commands. In the new file i/o system, which can be used simultaneously with the old one, all commands are grouped together under the new FILE keyword, and some related functions and variables are added. 1.22.1. Why Another I/O System? The well-known LOG, OPEN, READ, WRITE, and CLOSE commands have the following restrictions: 1. Only one READ file and one WRITE file can be open at a time. 2. The READ and WRITE commands are strictly line oriented. 3. These commands can not be used with binary files. 4. They do not support read/write access or random access. 5. The syntax is a bit counterintuitive for programmers. The new file i/o system allows multiple files to be open at once, in any desired combination of modes (read/write/append) supported by the operating system, for line, block (record), or character i/o, for sequential or random access, using consistent syntax and conventions. The new system, however, does not replace the old one, since the old system still must be used for: 1. The session, packet, debug, transaction, and connection logs. 2. Reading and writing commands rather than files. 3. Existing scripts. The new system works only with regular files, not with commands or pipes or mailboxes or pseudoterminals. No special provisions are made in the FILE commands for handling devices or network connections, nor for preventing you from trying to open them; if the underlying operating system treats them like regular stream disk files, the FILE commands (except, of course SEEK, REWIND, and COUNT) might work with them. (In C programming terms, the FILE commands are, at present, nothing more than a front end to fopen() / fread() / fwrite() / fclose() and friends, which are a portable API to sequential files, but this might change in the future for platforms like VMS and VOS that have more complicated file systems.) Definitions: Channel A number assigned to a file when it is opened, by which it must be referred to in all input/output operations. Read/Write Pointer The current position in an open file, expressed as the 0-based byte count from the beginning. 1.22.2. The FILE Command FILE keyword [ switches ] channel [ data ] The keyword specifies the function: FILE OPEN, FILE READ, FILE WRITE, FILE CLOSE, etc. For convenience (and for familiarity to C programmers), the two-word FILE commands can be shortened to the single words FOPEN, FREAD, FWRITE, FCLOSE, and so on. Switches are optional, and modify or amplify the requested file function. As in C, Fortran, and other programming languages, open files are referred to by "channels", integers such as 0, 1, 2, 3, and so on. A channel number is assigned when you open a file. The number of available channels depends on the underlying operating system, and can be seen in the variable: \v(f_max) or by giving the FILE LIST (FLIST) command. Channels are discussed in greater detail in [384]Section 1.22.4. FILE command errors can be caught with IF FAIL after the FILE command. In addition, the \v(f_error) variable is set to the completion code of the command: 0 if no error, or a negative number if there was an error. The error codes are listed in [385]Section 1.22.5. The command to open a file is: FILE OPEN [ switches ] variable filename Opens a file for the type of access specified by the switches, or for read-only access if no switches are given. Upon success, a channel number is assigned to this file and stored in the given variable so you can refer to the open file in subsequent i/o commands. If the file can not be opened, the FILE OPEN command fails. Synonym: FOPEN. The FILE OPEN switches are: /READ Open the file for read access. If no switches are given, /READ is assumed. If the file does not exist or can't be opened for read access, the FILE OPEN command fails. /WRITE Allow writing. If a file of the same name already exists, it is overwritten unless /READ or /APPEND is also included. If a file of the given name does not exist, it is created. /APPEND Equivalent to /WRITE, except that if the file exists, it is not destroyed. The read/write pointer is set to the end of the file, so unless you change it with FILE SEEK or REWIND (see below), the first FILE WRITE command adds to the end of the file, preserving what was there already. If /WRITE is also given, it is ignored. /BINARY Open the file in "binary" mode, rather than text mode. This switch is meaningless (but still can be used) in UNIX. In VMS, Windows, and OS/2, it inhibits end-of-line processing and conversion, and so should be used for binary files and/or files that are to be accessed in record or character mode rather than line by line. The variable for the channel number can be any kind of variable: the \%x kind, a macro name, or an array element. But it must be a variable, not a number -- C-Kermit assigns the channel number; you can't tell it what number to use. Example: FILE OPEN \%c oofa.txt ; Open oofa.txt for reading. IF FAIL exit 1 Can't open oofa.txt ; Always check to see if it worked. ECHO oofa.txt: channel = \%c If the file oofa.txt is opened successfully, a channel number is assigned to the variable \%c. Here's another example using a macro name for the channel number: FILE OPEN channel oofa.txt ; Open oofa.txt for reading. IF SUCCESS ECHO oofa.txt: channel = \m(channel) Switches can be combined when it makes sense and the underlying operating system allows it. For example, to open a file in binary mode for reading and writing (sometimes called "update"): FILE OPEN /READ /WRITE /BINARY \%c budget.db Some combinations might be allowed, others not. For example /READ /APPEND will usually not be allowed. /WRITE /APPEND is treated as /APPEND. A major advantage of the new system over the older one is that you can have multiple files open at once. Suppose, for example, that you want to open all the files in a certain directory at once: .\%n := \ffiles(/usr/olga*,&f) ; Get file list into array. if ( > \%n \v(f_max) ) { ; Make sure there aren't too many. exit 1 {\v(dir): \%n = Too many files} } declare \&c[\%n] ; Make array for channel numbers. for \%i 1 \%n 1 { ; Loop to open every file... file open \&c[\%i] \&f[\%i] ; Try to open this one if fail exit 1 Open error: \&f[\%i] ; Check for failure } If this loop completes successfully, the \&c[] array will contain \%n channel numbers of open files in elements 1 through \%n. Any file that you open with FILE OPEN stays open until Kermit exits, or you close it explicitly. The command to close a file is: FILE CLOSE { ALL, channel } If a channel number is given and the channel refers to an open file, the file is closed and the channel is freed for reuse; if the channel does not refer to an open file, an error message is printed and the command fails. If ALL is specified instead of a specific channel, all files opened with FILE OPEN are closed and if all open files were closed successfully (even if no files were open), the command succeeds; if any open file could not be closed, the command fails; however, all open files that could be closed are still closed. Synonym: FCLOSE. FILE CLOSE might fail because, for example, the disk filled up or a quota was exceeded. Example: fopen /write \%c new.txt ; Open new.txt for writing. if fail exit 1 ; Check for error. fclose \%c ; Close the file we just opened. This creates a 0-length file called new.txt. Note that FILE OPEN /WRITE (without /READ or /APPEND) always creates a new file, and therefore destroys any file with the same name that might already exist (assuming you have permission to delete it). To avoid overwriting existing files, simply check first: if exist new.txt exit 1 {Fatal - new.txt already exists} fopen /write \%c new.txt if fail ... The next two commands give information about open files: FILE STATUS channel Tells the name of the file, if any, open on the given channel and the switches it was opened with. The read/write pointer is also shown; this is where the next read or write will occur; "[EOF]" is shown if the current position in the open file is the end -- i.e. the next read will fail if the file was opened in /READ mode; the next write will add material to the end. The current line number (0-based) is also shown if known. The FILE STATUS command succeeds if the channel is open, and fails if there is no open file on the given channel, or if the channel number is invalid or out of range. Synonym: FSTATUS. FILE LIST Lists the channel number and name of each open file, along with its OPEN modes (R, W, A, B, RW, etc) and its current read/write pointer or "[EOF]" if it is at the end. Also tells the number of files currently opened with FILE OPEN, plus the maximum number of open files allowed by the system and the maximum number allowed for FILE OPEN. Synonym: FLIST. Next come the commands for reading and writing files: FILE READ [ switches ] channel [ variable ] Reads data from the file on the given channel number into the variable, if one was given; if no variable was given, the result is printed on the screen. IMPORTANT: The variable should normally be a macro name rather than a \%x or \&x[] variable if you want backslash characters in the file to be taken literally (see pp.408-412 of [386]Using C-Kermit for an explanation; you can also read into a \%x or \&x[] variable, but then you must remember to protect future references to by \fcontents() if you don't want C-Kermit to process any backslashes it might contain). The desired amount of data (according to the switches) is read from the file at the current read/write pointer, and upon completion the read/write position is updated to first byte after the data that was read, no matter what switches were given. Synonym: FREAD. FILE WRITE [ switches ] channel text Writes the given text to the file on the given channel number. The text, of course, can be literal text or a variable, or any combination. If the text might contain leading or trailing spaces, it must be enclosed in braces if you want to preserve them. Synonym: FWRITE. Before proceeding, a caution about the NUL character. C-Kermit is so named because it is a Kermit program written in the C language. In C, character strings are represented as a sequence of non-NUL bytes terminated by a NUL byte (a byte in which all bits are 0). Thus a C string can not contain NUL bytes; it always ends with the first NUL byte. C-Kermit variables are implemented as C strings and therefore can't contain NUL bytes either, so the FILE READ and FILE WRITE commands do not handle files or strings that contain NUL bytes, except when the /CHARACTER switch is included with the FILE READ or WRITE command, or when /LPAD:0 or /RPAD:0 is given with the FILE WRITE command; these switches are explained below. Also note that Kermit can not be used read or write binary numbers in the machine's internal format (integer or floating-point); in general, numbers can be processed only when represented as numeric or floating-point strings. FILE READ switches are: /LINE Specifies that a line of text is to be read. A line is defined according to the underlying operating system's text-file format. For example, in UNIX a line is a sequence of characters up to and including a linefeed, or the end of the file, which ever comes first. The line terminator (if any) is removed before assigning the text to the variable. If no switches are included with the FILE READ command, /LINE is assumed. Normally this switch should not be used with files opened in /BINARY mode (but nothing prevents it either). /SIZE:number Specifies that the given number of bytes (characters) is to be read. The actual number of bytes returned will be less if the end of file is reached (or a NUL byte is encountered). For example, if a file is 514 bytes long, FILE READ /SIZE:512 returns 512 bytes the first time and 2 bytes the second time. FILE READ /SIZE provides a kind of "record i/o" for files that do not necessarily contain lines. The resulting block of characters is assigned to the variable without any editing. Synonym: /BLOCK. /CHARACTER Equivalent to /SIZE:1. If FILE READ /CHAR succeeds but the variable is empty, this indicates a NUL byte was read. Synonym: BYTE. FILE WRITE switches are: /LINE Specifies that an appropriate line terminator is to be added to the end of the text. If no switches are included, /LINE is assumed. /SIZE:number Specifies that the given number of bytes (characters) is to be written. If the given text is longer than the requested size, it is truncated; if is shorter, it is padded according /LPAD and /RPAD switches. Synonym: /BLOCK. /LPAD[:value] If /SIZE was given, but the text is shorter than the requested size, the text is padded on the left with sufficient copies of the character whose ASCII value is given to write the given length. If no value is specified, 32 (the code for Space) is used. The value can also be 0 to write the indicated number of NUL bytes. If /SIZE was not given, this switch is ignored. /RPAD[:value] Like LPAD, but pads on the right. /CHARACTER Specifies that one character should be written. If the text is empty or not given, a NUL character is written; otherwise the first character of text is given. Synonym: /BYTE. /STRING Specifies that the text is to be written as-is, with no terminator added. Here's an example in which we copy a text file line by line: file open /read \%c oofa.txt ; Open input file if fail exit 1 Can't open input file ; Check that it's open file open /write \%d new.txt ; Open output file if fail exit 1 Can't open output file ; Check while true { ; Loop to copy lines file read /line \%c line ; Read a line if fail break ; Assume failure = end of file file write /line \%d {\m(line)} ; Write the line to output file if fail exit 1 Write failure ; Failure here is fatal } file close \%c ; Close the two files file close \%d Note that since /LINE is the default for both FILE READ and FILE WRITE, it can be omitted as in the following example, where we also use the short names for the FILE commands. fopen /read \%c oofa.txt ; Open input file if fail exit 1 Can't open input file ; Check that it's open fopen /write \%d new.txt ; Open output file if fail exit 1 Can't open output file ; Check while true { ; Loop to copy lines fread \%c line ; Read a line if fail break ; Assume failure = end of file fwrite \%d {\m(line)} ; Write the line to output file if fail exit 1 Write failure ; Failure here is fatal } fclose \%c ; Close the two files fclose \%d Here's the same example using "record i/o" (the open and close sequences are are omitted since they are the same as above). The result is the same, but execution is much faster: while true { ; Loop to copy blocks fread /size:512 \%c block ; Read a block into \%a if fail break ; Assume failure = end of file fwrite /string \%d {\m(block)} ; Write the block to output file if fail exit 1 Write failure ; Failure here is fatal } Although record i/o is faster, it should not be used in line-oriented applications, since it returns arbitrary chunks of the file to your script, rather than lines. In this example, FWRITE /STRING is used rather than FWRITE /SIZE:512 to avoid the last output block being padded beyond the original file's length. A file can also be copied character by character, but this is much slower than line i/o and VERY much slower than block i/o: while true { ; Loop to copy blocks fread /char \%c c ; Read a character into c if fail break ; Assume failure = end of file fwrite /char \%d {\m(c)} ; Write character to output file if fail exit 1 Write failure ; Failure is fatal } Although character i/o is slow, it is the only way to process files that contain NUL characters (i.e. bytes composed of only zero bits). In the example above, when "fread /char \%c c" returns a NUL, the c variable is empty. But since the FREAD /CHAR command did not fail, we know the result was really a NUL. FWRITE /CHAR, when given an empty variable (or no variable at all) writes a NUL. Thus the loop above will copy any file at all (very slowly). In non-copying applications, NULs are detected like this: fread /char \%c c if fail (do something) if not def c (a NUL byte was read) Finally some advanced file operations: FILE FLUSH channel For output files only: commits all previous writes to disk, in case the computer was buffering them. Synonym: FFLUSH. FILE COUNT [ { /BYTES, /LINES, /LIST, /NOLIST } ] channel By default, or if the /BYTES switch is given, counts the bytes in the file, if any, open on the given channel. If the /LINES switch is given, counts lines in the file. If the /LIST switch is given, the result is printed. If the /NOLIST switch is given, the result is not printed. /QUIET is a synonym for /NOLIST. If neither /LIST nor /NOLIST is given, the result is printed if the command is given at top level, i.e. not from a command file or macro. In all cases, the result of the most recent FILE COUNT command is stored in the variable \v(f_count). Note that FILE COUNT /LINE works (and can only work) by reading the entire file; expect it to take some time if the file is large. Synonym: FCOUNT. FILE REWIND channel Moves the read/write pointer to the beginning of the file. Equivalent to FILE SEEK channel 0. Synonym: FREWIND. FILE SEEK [ switches ] channel { [{+,-}]number, LAST, EOF } Moves the read/write pointer for the file on this channel to the given position, which may be a byte (character) number or a line number, expressed in either absolute or relative terms. Switches: /BYTE The number given is a byte number. Synonym: /CHARACTER. /LINE The number given is a line number. /ABSOLUTE The number given is absolute. /RELATIVE The number given is relative to the current position. By default, or if the /BYTE switch is given, the number is a byte number (0 = first byte). If /LINE is given, the number is a line number (0 = first line). EOF means to move to the end of the file. LAST means to move to the last line or character of the file, depending on whether it's a line or character seek. If neither the /RELATIVE nor the /ABSOLUTE switch is given, then if a signed number is given, the motion is relative to the current position. An expression that evaluates to a negative number is not considered signed for this purpose; that is, a sign (+ or -) must be included as the first character of the number in the command itself to force a relative seek (in the absence of /RELATIVE or /ABSOLUTE). If the number has no sign, or if the /ABSOLUTE switch is given, the number represents an absolute position (relative to the beginning of the file). Subsequent FILE READs or WRITEs will take place at the new position. If the read/write pointer is placed after the end of the file, a subsequent FILE READ will fail, but a FILE WRITE will succeed (possibly creating a file with "holes"). If a FILE SEEK /BYTE command is given, the current line becomes unknown (unless the position is 0) and subsequent FILE SEEK /RELATIVE /LINE commands will fail until the next non-relative FILE SEEK /LINE command is given. Synonym: FSEEK. An absolute FILE SEEK to a negative position fails silently, as does a relative seek to a position before the beginning of the file. A caution about relative SEEKs: remember that the number is relative to the current position. Whenever you read or write, this changes the position. In each of the following examples, assume the file open on channel \%c is positioned at line n (the FREAD target variable is omitted for lack of space): { FREAD \%c, FSEEK /LINE \%c -1, FREAD \%c } <-- Reads line n twice { FREAD \%c, FSEEK /LINE \%c +0, FREAD \%c } <-- Reads lines n and n+1 { FREAD \%c, FSEEK /LINE \%c +1, FREAD \%c } <-- Reads lines n and n+2 { FREAD \%c, FSEEK /LINE \%c -2, FREAD \%c } <-- Reads lines n and n-1 { FREAD \%c, FSEEK /LINE \%c -3, FREAD \%c } <-- Reads lines n and n-2 Another caution: Using FSEEK and FREAD /SIZE to repeatedly read the same disk block (e.g. when sampling a database record that is frequently updated) might not give you updated disk blocks due to the internal buffering and caching of the C library (this probably varies from one platform/compiler combination to another). If necessary you can force a fresh disk read with a close/open sequence: FCLOS \%c FOPEN \%c samefilename FSEEK \%c samespot FREAD /SIZE:howmanybytes \%c variable 1.22.3. FILE Command Examples To read the last 10 lines of a text file into an array: fopen /read \%c oofa.txt ; Open the file if fail exit 1 Can't open oofa.txt ; Always check for failure dcl \&a[10] ; Declare a 10-element array fcount /line \%c ; Count lines in the file fseek /line \%c \v(f_count)-10 ; Seek to 10 lines from the end if fail exit 1 Can't seek ; Check for failure for \%i 1 10 1 { fread \%c \&a[\%i] } ; Read the last 10 lines fclose \%c ; Close the file Note that blank lines show up as empty (undefined) array elements, for example if you give a "show array a" command at this point. This is normal. You can still use these elements; e.g.: for \%i 1 10 1 { echo \%i. \&a[\%i] } ; Display the 10 lines Here is how to read the last line of a file (already open on channel \%c): fseek /line \%c last ; Seek directly to last line Alternatively: fseek /line \%c eof ; Seek to end of file fseek /line \%c -1 ; Seek to beginning of last line Alternatively: fcount /line \%c ; Count the file's lines fseek /line \%c \v(f_count)-1 ; Seek to last line fread \%c ; Read it To read every other line from the file (using relative SEEK), skipping the first line: fopen /read \%c oofa.txt ; Open the file while ( success ) { ; Loop through lines fseek /line \%c +1 ; Skip a line if success fread \%c ; Read & display a line } fclose \%c ; Close the file Here is how to read the lines of a file in reverse order: fopen /read \%c oofa.txt ; Open if fail exit 1 ; Check fseek /line \%c last ; Seek to last line while success { ; Loop fread \%c ; Read line fseek /line \%c -2 ; Seek backwards two lines } fclose \%c ; Close the file The loop works because a relative SEEK outside the file fails. It is also possible to use block i/o to manage random-access files with fixed-length records (as long as they don't contain NUL characters). Suppose, for example, you have a file of "card image" records with fixed-field information about customers, such as: Name: Columns 1-32 (column numbers are 1-based) Address: Columns 33-72 Balance: Columns 73-80 The records are indexed by customer number, starting with 0. There are no line terminators separating them. Therefore the record for customer number n starts at position nx 80 (\%n*80). Now suppose we received a payment from customer number 173 and want to update the balance: .\%n = 173 ; Customer (record) number .\%a = 12.72 ; Amount fopen /read /write \%c customer.db ; Open the file if fail stop 1 OPEN FAILED: \f_errmsg() ; Check fseek /byte \%c 80*\%n ; Seek to record fread /size:80 \%c r ; Read the record if fail stop 1 READ FAILED: \f_errmsg() ; Check (IMPORTANT) .\%b := \fright(\m(r),8) ; Extract the balance .\%b := \ffpadd(\%b,\%a,2) ; Add the new payment if fail stop 1 ARITHMETIC ERROR: \%b/\%a ; Catch bad records .r := {\fleft(\m(r),72)\flpad(\%b,8)} ; Update the record fseek /byte \%c 80*\%n ; Reposition to same spot fwrite /size:80 \%c {\m(r)} ; Replace the record if fail stop 1 WRITE FAILED: \f_errmsg() ; Check fclose \%c ; Close the file REMEMBER: Using FILE SEEK to move beyond the end of file can result in a file with holes when writing; when reading, an end-of-file error will occur -- be sure to check for it. 1.22.4. Channel Numbers C-Kermit's channel numbers are integers from 0 to some platform-dependent limit, such as 46 or 1985 (the value of \v(f_max)). This is the limit placed by the operating system on the number of files that may be opened by one process or user or job, minus the standard input, output, and error files, and minus the number of files reserved by C-Kermit for logs, OPEN READ and WRITE, and file transfer (and maybe some command files -- the \v(f_max) number can't be exact). Although you must include a variable in the FILE OPEN command, to which the channel number is assigned, you don't have to use a variable in the other FILE commands if you know what the number is -- you can just put the number. This saves you a few keystrokes when typing commands at the prompt: fopen \%c oofa.txt flist 0. /usr/olga.oofa.txt (R) 0 This tells the channel number is 0 (the number on the left is the channel file's channel number). Of course you can also find it by echoing the variable: echo \%c 0 Or with "fstatus \%c". Now you can type commands like: fread 0 to read a line from the file. Obviously, however, using digits rather than a variable for the channel number would be poor practice in a script. If in commands like: fread \%c \%a you have trouble remembering which variable is which, note that the channel number is, indeed, a number. Anywhere C-Kermit accepts a number it can also accept an expression, so you can put parentheses around the channel number to remind you it's the channel number and not the variable into which data is to be read: fread (\%c) \%a Normally channel numbers are assigned sequentially as 0, 1, 2, ... up to the limit. However, once you start closing files, there can be holes in the sequence. New channels are assigned to fill in the holes. Thus you can't depend on channel numbers being in any particular sequence. 1.22.5. FILE Command Errors Each FILE command sets the variable \v(f_error) to one of the following values: 0 = No error -1 = System error -2 = Attempt to read after end of file -3 = Channel not open -4 = Channel number out of range (negative or too large) -5 = Numeric argument (size, ...) out of range -6 = File not found -7 = Bad or missing filename -8 = Too many files are already open (FILE OPEN only) -9 = Forbidden operation (e.g. write to a read-only file) -10 = Access denied -11 = Illegal combination of OPEN modes (FILE OPEN only) -12 = Buffer overflow -13 = Current line number unknown (for relative line seeks) -14 through -98: Reserved. -99 = Requested operation not implemented in this version of C-Kermit -999 = Unknown error When \v(f_error) is -1, this means the FILE command failed because because of a system error, in which case you can examine the following variables: \v(errno) = System error number. \v(errstring) = Error message corresponding to \v(errno). A special function is available for translating the \v(f_error) code to an error message string: \f_errmsg([code]) If the code is -1, returns error message of the most recent system error; otherwise if the code is a valid \v(f_error) value, the associated message is returned. If the code is omitted, the status message corresponding to the current \v(f_error) value is returned. A FILE command that fails prints the appropriate error message automatically, except when the command is READ or SEEK and the error is -2 (end of file); in that case, the command still fails, but does not print a message. This allows constructions such as: fopen \%c oofa.txt while success { fread \%c } fclose \%c to work as expected, i.e. without an annoying message when the end of file is reached. 1.22.6. File I/O Variables The variables associated with the file i/o package are: \v(f_count) Result of the most recent FILE COUNT (FCOUNT) command. \v(f_error) Numeric error code of most recent FILE command (0 = no error). \v(f_max) Maximum number of files open simultaneously. 1.22.7. File I/O Functions Some of the FILE commands can also be issued as function calls, which makes script writing a bit more convenient, especially for C programmers. Also, several functions are provided that do not have command equivalents. Each of these functions takes a channel number as the first argument. These functions do not work for OPEN { READ, !READ, WRITE, !WRITE, and APPEND } files. \f_status(channel) Returns 0 if the channel is not open, otherwise a number between 1 and 15 which is the sum of the OPEN modes: 1 = /READ 2 = /WRITE 4 = /APPEND 8 = /BINARY The remaining functions work only for open channels. Each of these functions can fail for the applicable reasons listed in [387]Section 1.22.5. For instructions on handling function errors, see [388]Section 7.12. \f_pos(channel) Returns the file's current read/write pointer (0-based). There is no FILE command equivalent. \f_line(channel) Returns the file's current line number (0-based), if known, otherwise -1. There is no FILE command equivalent. The line number is known as long as no character or block i/o has been done on the channel. \f_handle(channel) Returns the "file handle" of the file. That is, it translates the portable C-Kermit channel number into a system-specific file handle or number that can be passed to other programs on the same platform. In UNIX this is a file descriptor. There is no FILE command equivalent. \f_eof(channel) Returns 1 if the read/write pointer of the file on the given channel is at the end of the file, 0 otherwise. Convenient in WHILE statements, e.g.: while not \f_eof(\%c) { fread \%c } \f_getchar(channel) Equivalent to FREAD /CHAR. Returns the character actually read. If \f_getchar() does not fail but the return value is empty, this means a NULL character was read. \f_getline(channel) Equivalent to FREAD /LINE. Returns the line actually read, but with the line terminator stripped. If \f_getline() does not fail but the return value is empty, this normally means an empty line was read. \f_getblock(channel,n) Equivalent to FREAD /SIZE:n. Returns the block of characters actually read. If the returned block is smaller than n, it indicates either that the end of file was reached or a NUL character is in the block. \f_putchar(channel,c) Equivalent to FWRITE /CHARACTER. Writes the character c. If c contains more than one character, only the first is written. If c is empty a NUL is written. Returns the number of characters written on success, or a negative error code upon failure. \f_putline(channel,string) Equivalent to FWRITE /LINE. Writes the string and adds the appropriate line termination character or sequence. If the string is empty or omitted, an empty line is written. Returns the number of characters written on success, or a negative error code upon failure. \f_putblock(channel,string) Equivalent to FWRITE /STRING. Writes the string as given. If the string is empty or omitted, nothing is written. Returns the number of characters written on success, or a negative error code upon failure. 1.22.8. File I/O Function Examples fopen /read \%c oofa.txt ; Open our favorite file for reading if failure exit 1 ; Check that it's open while not \f_eof(\%c) { ; Loop until EOF .line := \f_getline(\%c) ; Get a line if success echo {\m(line)} ; Echo it } if not \f_eof(\%c) { ; Check reason for loop exit exit 1 File Error: \f_errmsg() ; If not EOF say so. } frewind \%c ; Rewind the file while not \f_eof(\%c) { ; Same thing but with block i/o .block := \f_getblock(\%c,256) ; (much faster than line i/o) if success xecho {\m(block)} } frewind \%c ; Rewind again while not \f_eof(\%c) { ; Same deal but with character i/o .c := \f_getchar(\%c) ; (much slower than line i/o) if success xecho {\m(c)} } close \%c To close all open files (equivalent to FCLOSE ALL): for \%i 0 \v(f_max)-1 1 { if \f_status(\%i) fclose \%i } 1.23. The EXEC Command The EXEC command is available only in UNIX. EXEC [ /REDIRECT ] command [ arg1 [ arg2 [ ... ] ] Runs the given command with the arguments in such a way that the command replaces C-Kermit in memory, and C-Kermit ceases to execute. EXEC is like RUN, except instead of returning to C-Kermit when finished, the command returns to whatever process invoked Kermit. In the normal case, no files are closed, so the EXEC'd command inherits the open files, read/write pointers, working directory, process ID, user ID (unless command is SUID), group ID (unless command is SGID), groups, etc. (In UNIX, the EXEC command is simply a front end for execvp().) If the /REDIRECT switch is included, then if a connection is open (SET LINE or SET HOST), it becomes the standard input and output of the EXEC'd program. If no connection is open, the /REDIRECT switch has no effect. For example to use C-Kermit for PPP dialing in Linux: set modem type usr ; Specify the kind of modem you have set line /dev/ttyS1 ; Specify the device it's connected to set speed 57600 ; and the speed set flow rts/cts ; and flow control. set dial retries 100 ; Try the dial sequence up to 100 times. dial {{9-212-555-1212}{9-212-555-1213}{9-212-555-1214}{9-212-555-1215}} if fail exit 1 for \%i 1 16 1 { ; Try up to 16 times to get login prompt input 10 Login: ; Wait 10 sec for it to appear if success break ; Got it - proceed... output \13 ; Send a carriage return and try again } if ( > \%i 16 ) stop 1 NO LOGIN PROMPT lineout \(myuserid) ; Send user ID input 30 assword: ; Wait for Password prompt if fail stop 1 NO PASSWORD PROMPT lineout \m(mypassword) ; Send the password. exec /redirect pppd ; Replace ourselves with pppd. In this example we assume that the script has already set up the myuserid and mypassword variables -- normally the password should be prompted for, rather than stored on disk. Notice the advantages over the well-known "chat script": * You don't have to control the modem itself with AT commands; Kermit's DIAL command does this for you. * You can have Kermit automatically redial as many times as you want until it gets a connection (if this is legal in your country). * You can have Kermit fetch the number or numbers from a dialing directory. * You can have Kermit cycle through a list of phone numbers (this is new in C-Kermit 7.0; see [389]Section 2.1.16) without having to enter the numbers in a dialing directory. * Dialing is location-independent; you can use the same script to dial from different areas or countries. * Once the connection is made, the full power of Kermit's script language is available to manage the dialog with the terminal server or other device that answers the phone call. NOTE: PPP and SLIP dialing are not available in Windows 95/98/NT/2000, whose APIs do not provide a method for an application to hand over a connection to the PPP or SLIP driver. 1.24. Getting Keyword Lists with '?' Suppose you type "te" at the C-Kermit> 6.0 prompt and then Esc or Tab to request keyword completion. Kermit beeps, indicating that more than one command starts with "te". But if you type '?' to see what they are, Kermit shows only "telnet". So why the beep? Because of invisible keywords like "telopt", "terminal", and "text". Lots of keywords are invisible because they are either synonyms for other keywords or else esoteric options to be used only in special circumstances, so we don't want them cluttering up the menus. But then there is no way for you to discover them. So in C-Kermit 7.0, if you type '?' AFTER the beginning of a keyword field, then invisible keywords are shown too: C-Kermit> te C-Kermit> te? Command, one of the following: telnet telopt terminal text C-Kermit>te But if '?' is typed at the beginning of a field, only visible keywords are shown, as before (so, in this example, if '?' is typed at the C-Kermit> prompt, "telnet" is the only command shown that starts with "te"). 2. MAKING AND USING CONNECTIONS The SET LINE, SET HOST, and SET PORT (a synonym for SET LINE) commands have new synonyms, in which the word SET is replaced by the word OPEN: OPEN LINE, etc. There is no new functionality here, but OPEN is a better verb, since SET generally takes no action, whereas these commands actually try to open a connection. Furthermore, there is the symmetry with CLOSE. 2.0. SET LINE and SET HOST Command SwitchesThe SET LINE (SET PORT) and SET HOST commands now allow switches before the device or host name, in most cases, and under certain circumstances, also at the end. The new syntax is backwards compatible with the previous syntax; thus SET LINE, SET PORT, and SET HOST commands in command files written for C-Kermit 6.0 or earlier still work. The expanded syntax is: { OPEN, SET } { LINE, PORT, HOST } [ switches ] device-or-address [ switches ] The first group of switches is: /NETWORK-TYPE:{TCP/IP,X.25,PIPE,PTY...} When more than one network type is available, this lets you specify the type of network to use for this connection without affecting your global SET NETWORK TYPE. See [390]Section 2.7 about pipes and ptys. /USERID:[string] This switch is equivalent to SET LOGIN USERID. If a string is given, it sent to host during Telnet negotiations; if this switch is given but the string is omitted, no user ID is sent to the host. If this switch is not given, your current LOGIN USERID (\v(userid) value), if any, is sent. Unlike most other switches, this one is "sticky", since the value must persist throughout the session in case the server requests the ID string at a later time. /CONNECT Enter CONNECT mode immediately and automatically after the device or connection is open. On serial devices, however, when CARRIER-WATCH is not OFF, wait up to 1 second for the Carrier Detect signal to appear before trying to connect, to give the device time to react DTR, which might have been off prior to opening the device. /SERVER Enter server mode immediately and automatically after the device or connection is open. Treatment of carrier is the same as for /CONNECT. /WAIT /NOWAIT For Telnet connections only: Like SET TELNET WAIT { ON, OFF }, but applies only to this connection, and in fact applies only when OPENing this connection (which is usually the only place it matters). Typically you would use TELNET /NOWAIT to make a connection to a misbehaving Telnet server that does not reply to negotiations as required by the Telnet protocol definition. Note: /CONNECT and /SERVER switches are not available in the RLOGIN and TELNET commands, since these commands already include an implicit /CONNECT and preclude automatic entry into server mode. The /CONNECT and /SERVER switches are especially useful with "set host *" connections. For example, suppose you want to start a Kermit server on socket 3000 of your TCP host. Normally you would have to give the command: set host * 3000 and then wait for a connection to come in, and only then could you give the SERVER command (or else define a macro to do this, and then execute the macro). Now you can do it in one step: set host /server * 3000 This tells C-Kermit to wait for the connection and then enter server mode once it comes in, no matter how long it takes. Similarly, "set host /conn *" can be used to wait for a "chat" connection to come in. Another set of switches is available in VMS only, for use only with SET LINE: /SHARE Allows the SET LINE device to be opened in shared mode. Normally it makes no sense to open a serial device in shared mode, but it's necessary when C-Kermit is running in an environment such as DECIntact, that opens your job's controlling terminal in such a way that C-Kermit can't open it too, unless it enables SHARE privilege. Note: SHARE privilege is required. /NOSHARE Requires that the SET LINE device not be in use by any other process in order for it to be successfully opened by C-Kermit. If neither /SHARE nor /NOSHARE is specified, /NOSHARE is used. The second group of switches is: /NO-TELNET-INIT Do not send initial Telnet negotiations even if this is a Telnet port. /RAW-SOCKET This is a connection to a raw TCP socket ([391]Section 2.3.5). /RLOGIN Use Rlogin protocol even if this is not an Rlogin port. /TELNET Send initial Telnet negotiations even if this is not a Telnet port. As of C-Kermit 7.0 and K95 1.1.19, the TELNET command includes an implicit /TELNET switch. So if you TELNET to a non-TELNET port, Kermit sends initial Telnet negotiations. This makes sense, since that's what "telnet" means. If you want to make a connection to a non-Telnet port without sending initial Telnet negotiations, use: set host [ /connect ] name-or-address port or: telnet name-or-address port /no-telnet-init Additional switches might be added in the future; type "set host ?" or "set line ?" to see a current list. 2.1. Dialing Automatic redialing is illegal or restricted in many countries, so until C-Kermit 7.0, it was disabled by default, i.e. until a SET DIAL RETRIES command was given. In C-Kermit 7.0, if no SET DIAL RETRIES command has been given, a default is picked dynamically at DIAL time based on the calling country code, if known. At this writing, the only country code known to have no restrictions on automatic redialing is 1. So in this case a limit of 10 is chosen; otherwise 1. If you have not given an explicit SET DIAL RETRIES command, SHOW DIAL shows the value as "(auto)", and then the value actually used is shown when you give the DIAL command. As of C-Kermit 7.0, automatic redialing is automatically canceled if the call could not be placed because no dialtone was detected. 2.1.1. The Dial Result Message If DIAL DISPLAY is not ON, the "Call complete" message now shows the modem's call result message, for example: Dialing: ... Call complete: "CONNECT 31200/ARQ/V32/LAPM/V42BIS" The exact format and contents of this message, of course, depends on the make, model, and configuration of your modem, so use your modem manual to interpret it. The call result message is also available in C-Kermit's \v(dialresult) variable. C-Kermit> echo \v(dialresult) CONNECT 31200/ARQ/V32/LAPM/V42BIS C-Kermit> echo Speed = \fword(\v(dialresult),2) Speed = 31200 C-Kermit> Suppose your modem reports the modulation speed as shown above and you want to ensure your call is completed at (say) 24000 bps or more. You can use a little macro to do the job: define HSDIAL { ; High Speed DIAL local \%s if < \v(argc) 1 if not def \v(dialnumber) end 1 Usage: \%0 number set dial retries 100 set dial interval 1 while true { dial \%* if fail end 1 DIAL failed. asg \%s \fword(\v(dialresult),2) if def \%s if numeric \%s if not < \%s 24000 break } } (See [392]Section 7.5 about the \%* variable.) 2.1.2. Long-Distance Dialing Changes Due to the glut of cell phones, pagers, fax machines, ISPs, etc, area codes and dialing rules are changing all the time. In the North American Numbering Plan (NANP) countries (USA, Canada, etc), area codes are split or overlayed with increasing frequency, and 10- and 11-digit dialing is gradually becoming the norm for local calls. Changes are occurring In Europe, too, partly for these reasons and partly because of some new EC rules. In France, effective 18 October 1996, all calls, even local ones, must be dialed with an area code. French area codes are presently 1-digit numbers, 1-6, and the long-distance dialing prefix is 0. All calls within France are considered long distance and begin with 01, 02, ..., 06. Effective 1 May 1997, all calls within the US state of Maryland, even local ones, must be dialed with an area code but without the long-distance prefix -- this is the now widely-known North American phenomenon of "ten digit dialing". The same is happening elsewhere -- many cities in Florida adopted 10-digit dialing in 1998. In Italy beginning 19 June 1998, all calls to fixed (as opposed to mobile) numbers must be prefixed by 0. When calling into Italy from outside, the 0 must follow the country code (39). Calls to cell phones, however, must be placed without the 0. Then on 29 December 2000, the 0 will become a 4 (for calling fixed numbers) and a prefix of 3 must used for calling mobile phones. More info at: http://www.telecomitalia.it/npnn/. In Spain, effective 4 April 1998, with hard cutover on 18 July 1998, all calls within the country must be dialed with 9 digits, and all calls from outside Spain must also be dialed with 9 digits (after the country code, 34). The new 9-digit numbers all begin with "9". More info at: [393]http://www.telefonica.es/cambiodenumeracion/ Several new dialing features and commands have been added in version 6.1 and 7.0 to address these changes. C-Kermit 6.0 and Kermit 95 1.1.11 and earlier handle the French situation via a reasonable subterfuge (setting the local area code to a nonexistent one), but did not handle "ten-digit dialing" well at all; the recommended technique was to change the long-distance dialing prefix to nothing, but this defeated Kermit's "list numbers for one name" feature when the numbers were in different locations. For example: set dial ld-prefix dial onlineservice where "onlineservice" is a dialing directory entry name corresponding to entries that are in (say) Maryland as well as other states, would not correctly dial the numbers not in Maryland. A new command lets you specify a list of area codes to be considered local, except that the area code must be dialed: SET DIAL LC-AREA-CODES [ areacode [ areacode [ areacode [ ... ] ] ] ] The list may include up to 32 area codes. If a number is called whose area code is in this list, it is dialed WITHOUT the long-distance prefix, but WITH the area code. So in Maryland, which (last time we looked) has two area codes, 410 and 301, the setup would be: SET DIAL LC-AREA-CODES 410 301 Example: SET DIAL LD-PREFIX 1 SET DIAL AREA-CODE 301 SET DIAL LC-AREA-CODES 410 301 <-- Area codes in 10-digit dialing region DIAL +1 (301) 765-4321 <-- Dials 3017654321 (local with area code) DIAL +1 (410) 765-4321 <-- Dials 4107654321 (local with area code) DIAL +1 (212) 765-4321 <-- Dials 12127654321 (long distance) The SET DIAL LC-AREA-CODES command does not replace the SET DIAL AREA-CODE command. The latter specifies the area code you are dialing from. If the called number is in the same area code, then the area code is dialed if it is also in the LC-AREA-CODES list, and it is not dialed otherwise. So if "301" had not appeared in the LC-AREA-CODES list in the previous example: SET DIAL LD-PREFIX 1 SET DIAL AREA-CODE 301 SET DIAL LC-AREA-CODES 410 <-- Area codes in 10-digit dialing region DIAL +1 (301) 765-4321 <-- Dials 7654321 (local) DIAL +1 (410) 765-4321 <-- Dials 4107654321 (local with area code) DIAL +1 (212) 765-4321 <-- Dials 12127654321 (long distance) The new Kermit versions also add a Local Call Prefix and Local Call Suffix, in case you have any need for it. These are added to the beginning and of local phone numbers (i.e. numbers that are not long-distance or international). Examples: SET DIAL LD-PREFIX 1 SET DIAL LC-PREFIX 9 SET DIAL LC-SUFFIX * SET DIAL LC-AREA-CODES 410 <-- Area codes in 10-digit dialing region SET DIAL AREA-CODE 301 DIAL +1 (301) 765-4321 <-- Dials 97654321* (local) DIAL +1 (410) 765-4321 <-- Dials 94107654321* (local with area code) DIAL +1 (212) 765-4321 <-- Dials 12127654321 (long distance) 2.1.3. Forcing Long-Distance Dialing Suppose a number is in your country and area, but for some reason you need to dial it long-distance anyway (as is always the case in France). There have always been various ways to handle this: 1. Temporarily set your area code to a different (or nonexistent or impossible) one (but this required knowledge of which area codes were nonexistent or impossible in each country). 2. Dial the number literally instead of using the portable format, but this defeats the purpose of the portable dialing directory. Now there is also a new command that, very simply, can force long-distance dialing: SET DIAL FORCE-LONG-DISTANCE { ON, OFF } If a call is placed to a portable phone number within the same country code as the calling number, it is dialed with the long-distance prefix and the area code if FORCE-LONG-DISTANCE is ON. If OFF, the regular rules and procedures apply. Example (France): SET DIAL COUNTRY-CODE 33 SET DIAL AREA-CODE 6 SET DIAL FORCE-LONG-DISTANCE ON (In fact, SET DIAL COUNTRY-CODE 33 automatically sets DIAL FORCE-LONG-DISTANCE ON...) Example (USA, for a certain type of reverse-charge calling in which the called number must always be fully specified): SET DIAL PREFIX 18002666328$ ; 1-800-COLLECT SET DIAL COUNTRY-CODE 1 SET DIAL AREA-CODE 212 SET DIAL FORCE-LONG-DISTANCE ON Example (Toronto, where calls to exchange 976 within area code 416 must be dialed as long distance, even when you are dialing from area code 416): SET DIAL COUNTRY-CODE 1 SET DIAL AREA-CODE 416 SET DIAL FORCE-LONG-DISTANCE ON DIAL +1 (416) 976-xxxx If dialing methods were consistent and sensible, of course it would be possible to always dial every domestic call as if it were long distance. But in many locations this doesn't work or if it does, it costs extra. The following macro can be used for dialing any given number with forced long-distance format: define LDIAL { local \%x set dial force-long-distance on dial \%* asg \%x \v(success) set dial force-long-distance off end \%x } (See [394]Section 7.5 about the \%* variable.) 2.1.4. Exchange-Specific Dialing Decisions This applies mainly to the North American Numbering Plan (NANP). Refer to the section "Alternative notations" in [395]Using C-Kermit 2nd Edition, pages 106-107, and the story about Toronto on page 110. Using the new LC-AREA-CODES list, we can address the problem by treating the exchange as part of the area code: SET DIAL LD-PREFIX 1 SET DIAL AREA-CODE 416 SET DIAL LC-AREA-CODES 905276 DIAL +1 416 765 4321 <-- 7654321 (local) DIAL +1 905 276 4321 <-- 9052764321 (local with area code) DIAL +1 905 528 4321 <-- 19055284321 (long distance) The same technique can be used in Massachusetts (story at top of page 111) and in any other place where dialing to some exchanges within a particular area code is local, but to others in the same area code is long distance. 2.1.5. Cautions about Cheapest-First Dialing Kermit does not maintain a knowledge base of telephony information; it only provides the tools to let you enter a phone number in a standard format and dial it correctly from any location in most cases. In particular, Kermit does not differentiate the charging method from the dialing method. If a call that is DIALED as long-distance (e.g. from 212 to 718 in country code 1) is not CHARGED as long distance, we have no way of knowing that without keeping a matrix of charging information for every area-code combination within every country, and any such matrix would be obsolete five minutes after it was constructed. Thus, "cheapest-first" sorting is only as reliable as our assumption that the charging method follows the dialing method. A good illustration would be certain online services that have toll-free dialup numbers which they charge you a premium (in your online service bill) for using. 2.1.6. Blind Dialing (Dialing with No Dialtone) C-Kermit's init string for Hayes-like modems generally includes an X4 command to enable as many result codes as possible, so that Kermit can react appropriately to different failure reasons. One of the result codes that X4 enables is "NO DIALTONE". A perhaps not obvious side effect of enabling this result code that the modem must hear dialtone before it will dial. It is becoming increasingly necessary to force a modem to dial even though it does not hear a dialtone on the phone line; for example, with PBXs that have strange dialtones, or with phone systems in different countries, or with ISDN phones, etc. This is called "blind dialing". C-Kermit 7.0 has two new commands to cope with this situation: SET DIAL IGNORE-DIALTONE { ON, OFF } OFF (the default) means to tell the modem to wait for dialtone before dialing. ON means to enable "blind dialing", i.e. tell the modem NOT to wait for dialtone before dialing. Generally this is accomplished by sending ATX3 to the modem just prior to dialing. SET MODEM TYPE xxx and then SHOW MODEM displays Kermit's built-in "ignore dialtone" command. SET DIAL COMMAND IGNORE-DIALTONE text This lets you change the built-in ignore-dialtone command (such as ATX3) to whatever you choose, in case the built-in one does not work, or another command works better. Notes: 1. The ignore-dialtone command is not sent unless SET DIAL IGNORE-DIALTONE is ON. 2. The ATX3 command generally disables not only NO DIALTONE, but also BUSY. So this will prevent Kermit from detecting when the line is busy. This is a property of the modem, not of Kermit. 2.1.7. Trimming the Dialing Dialog The command: SET MODEM COMMAND action [ command ] is used to override Kermit's built-in modem commands for each action, for each kind of modem in its internal database. If you include a command, this is used instead of the built-in one. If you omit the command, this restores the original built-in command. If you want to omit the command altogether, so Kermit doesn't send the command at all, or wait for a response, use: SET MODEM COMMAND action {} That is, specify a pair of empty braces as the command, for example: SET MODEM COMMAND ERROR-CORRECTION ON {} 2.1.8. Controlling the Dialing Speed The rate at which characters are sent to the modem during dialing is normally controlled by the built-in modem database. You might want to override this if Kermit seems to be dialing too slowly, or it is sending characters to the modem faster than the modem handle them. A new command was added for this in C-Kermit 7.0: SET DIAL PACING number Specifies the number of milliseconds (thousandths of seconds) to pause between each character when sending commands to the modem during DIAL or ANSWER command execution. 0 means no pause at all, -1 (the default) or any other negative number means to use the value from the database. Any number greater than 0 is the number of milliseconds to pause. HINT: You might also need to control the rate at which the modem generates Touch Tones during dialing, for example when sending a numeric page. There are two ways to do this. One way is to insert pause characters into the dialing string. For modems that use the AT command set, the pause character is comma (,) and causes a 2-second pause. On most modems, you can use the S8 register to change the pause interval caused by comma in the dialing string. The other way is to set your modem's tone generation interval, if it has a command for that. Most AT-command-set modems use S11 for this; the value is in milliseconds. For example on USR modems: ATS11=200 selects an interval of 200 milliseconds to separate each dialing tone. Hint: To add S-Register settings or other commands to your dialing procedure, use the new SET MODEM COMMAND PREDIAL-INIT command ([396]Section 2.2.2). 2.1.9. Pretesting Phone Number Conversions The LOOKUP command now accepts telephone numbers as well as directory-entry names, for example: LOOKUP +1 (212) 7654321 When given a phone number, LOOKUP prints the result of converting the phone number for dialing under the current dialing rules. For example, if my country code is 1 and my area code is 212, and I am dialing out from a PBX whose outside-line prefix is "93,": C-Kermit> lookup +1 (212) 7654321 +1 (212) 7654321 => 93,7654321 C-Kermit> You can also use the \fdialconvert(phone-number) function ([397]Section 2.1.11) to do this programmatically: C-Kermit> echo "\fdialconvert(+1 (212) 7654321)" "93,7654321" C-Kermit> So the new LOOKUP behaves as follows: LOOKUP portable-format-phone-number Displays how the number would actually be dialed Sets FAILURE if there was a conversion error, otherwise SUCCESS. LOOKUP literal-format-phone-number Displays the same literal-format-phone-number Always sets SUCCESS. LOOKUP dialing-directory-name Displays all matching entries and converts portable phone numbers. Sets SUCCESS if at least one entry was found, otherwise FAILURE. LOOKUP =anything Displays "=anything" and sets SUCCESS. There is, at present, no programmatic way to fetch numbers from the dialing directory. This will be considered for a future release. 2.1.10. Greater Control over Partial Dialing The following rules now apply to partial dialing: * Phone number transformations based on country and area code, application of prefixes, etc, are performed only on the first PDIAL. * Each PDIAL argument is looked up in the dialing directory, so it is possible have directory entries for pieces of phone numbers or other information. * Suffixes are not applied automatically, since there is no way for C-Kermit to know in which PDIAL segment you want them to be applied. However, the suffix that *would* have been applied, based on the dialing rules that were invoked when processing the first PDIAL command, is stored in the variable: \v(dialsuffix) which you can include in any subsequent PDIAL or DIAL commands. Example: pdial {\m(my_long_distance_pager_number_part_1)} pdial {\m(my_long_distance_pager_number_part_2)} pdial {\v(dialsuffix)} pdial {\m(my_long_distance_pager_number_part_3)} pdial {@\m(numeric_pager_code)#} 2.1.11. New DIAL-related Variables and Functions \fdialconvert(s) s is a phone number in either literal or portable format (not a dialing directory entry name). The function returns the dial string that would actually be used by the DIAL command when dialing from the current location, after processing country code, area code, and other SET DIAL values, and should be the same as the result of LOOKUP when given a telephone number. \v(dialsuffix) Contains the suffix, if any, that was applied in the most recent DIAL command, or the suffix that would have been applied in the most recent PDIAL command. Use this variable to send the dial suffix at any desired point in a PDIAL sequence. \v(dialtype) A number indicating the type of call that was most recently placed. Can be used after a normal DIAL command, or after the first PDIAL command in a PDIAL sequence. Values are: -2: Unknown because TAPI handled the phone number translation. -1: Unknown because some kind of error occurred. 0: Internal within PBX. 1: Toll-free. 2: Local within calling area. 3: Unknown (e.g. because a literal-format phone number was given). 4: Long distance within country. 5: International \v(dialcount) The current value of the DIAL retry counter, for use in a DIAL macro ([398]Section 2.1.13). \v(d$px) PBX Exchange (see [399]Section 2.1.12). Other dial-related variables, already documented in [400]Using C-Kermit (or other sections of this document, e.g. [401]Section 2.1.1), include \v(dialnumber), \v(dialstatus), etc. A convenient way to display all of them is: show variable dial ; hint: abbreviate "sho var dial" This shows the values of all the variables whose names start with "dial". Also "show variable d$" (to show the \v(d$...) variables). 2.1.12. Increased Flexibility of PBX Dialing Refer to [402]Using C-Kermit, 2nd Edition, pages 107-108. Recall that three commands are needed to configure C-Kermit for dialing from a PBX: SET DIAL PBX-EXCHANGE number SET DIAL PBX-INSIDE-PREFIX number SET DIAL PBX-OUTSIDE-PREFIX number Unfortunately, this model does not accommodate PBXs that have more than one exchange. For example our PBX at Columbia University (which must handle more than 10,000 phones) has 853-xxxx and 854-xxxx exchanges. Beginning in C-Kermit 7.0, the SET DIAL PBX-EXCHANGE command accepts a list of exchanges, e.g.: SET DIAL PBX-EXCHANGE 853 854 (multiple exchanges are separated by spaces, not commas). So now when dialing a portable-format number that has the same country and area codes as those of your dialing location, C-Kermit compares the exchange of the dialed number with each number in the PBX Exchange list (rather than with a single PBX Exchange number, as it did formerly) to determine whether this is an internal PBX number or an external call. If it is an external call, then the PBX Outside Prefix is applied, and then the normal dialing rules for local or long-distance calls. If it is an inside call, the exchange is replaced by the PBX Inside Prefix. But if the PBX has more than one exchange, a single fixed PBX Inside Prefix is probably not sufficient. For example, at Columbia University, we must dial 3-xxxx for an internal call to 853-xxxx, but 4-xxxx for a call to 854-xxxx. That is, the inside prefix is the final digit of the exchange we are dialing. For this reason, C-Kermit 7.0 provides a method to determine the inside prefix dynamically at dialing time, consisting of a new variable and new syntax for the SET DIAL PBX-INSIDE-PREFIX command: \v(d$px) This variable contains the exchange that was matched when a PBX internal call was detected. For example, if the PBX exchange list is "853 854" and a call is placed to +1 (212) 854-9999, \v(d$px) is set to 854. SET DIAL PBX-INSIDE-PREFIX \fxxx(...) If the PBX Inside Prefix is defined to be a function, its evaluation is deferred until dialing time. Normally, this would be a string function having \v(d$px) as an operand. Of course, you can still specify a constant string, as before. So given the following setup: SET DIAL COUNTRY-CODE 1 SET DIAL AREA-CODE 212 SET DIAL PBX-OUTSIDE-PREFIX 93, SET DIAL PBX-EXCHANGE 853 854 SET DIAL PBX-INSIDE-PREFIX \fright(\v(d$px),1) The following numbers give the results indicated: Number Result +1 (212) 854-9876 4-9876 +1 (212) 853-1234 3-1234 +1 (212) 765-4321 93,765-4321 +1 (333) 765-4321 93,1333765-4321 Furthermore, the K_PBX_XCH environment variable may now be set to a list of exchanges to automatically initialize C-Kermit's PBX exchange list, for example (in UNIX ksh or bash): export K_PBX_XCH="853 854" (Quotes required because of the space.) Of course, this variable can also be set to a single exchange, as before: export K_PBX_XCH=853 2.1.13. The DIAL macro - Last-Minute Phone Number Conversions After a DIAL or LOOKUP command is given, a list of phone numbers is assembled from the dialing directory (if any), with all location-dependent conversion rules applied as described in Chapter 5 of [403]Using C-Kermit. However, additional conversions might still be required at the last minute based on local or ephemeral conditions. So that you can have the final word on the exact format of the dial string, C-Kermit 7.0 lets you pass the converted string through a macro of your own design for final processing before dialing. The relevant command is: SET DIAL MACRO [ name ] Specifies the name of a macro to be run on each phone number after all built-in conversions have been applied, just before the number is dialed. If no name is given, no macro is run. The phone number, as it would have been dialed if there were no dial macro, is passed to the macro. The dial macro can do anything at all (except start a file transfer). However, the normal use for the macro would be to modify the phone number. For this reason the phone number is passed to the macro as argument number 1 (\%1). To cause a modified number to be dialed, the macro should terminate with a RETURN statement specifying a return value. To leave the number alone, the macro should simply end. Example: define xxx return 10108889999$\%1 set dial macro xxx dial xyzcorp This defines a DIAL MACRO called xxx, which puts an access code on the front of the number. Another example might be: def xxx if equal "\v(modem)" "hayes-1200" return \freplace(\%1,$,{,,,,,}) set dial macro xxx dial xyzcorp which replaces any dollar-sign in the dial string by a series of five commas, e.g. because this particular modem does not support the "wait for bong" feature (remember that commas that are to be included literally in function arguments must be enclosed in braces to distinguish them from the commas that separate the arguments) and when the IF condition is not satisfied, the macro does not return a value, and so the number is not modified. Then when a DIAL command is given referencing a dialing directory entry, "xyzcorp". The macro is automatically applied to each matching number. Numerous dial-, modem-, communications-, and time-related variables are available for decision making your dial macro. Type SHOW VARIABLES for a list. Of particular interest is the \v(dialcount) variable, which tells how many times the DIAL command gone through its retry loop: 1 on the first try, 2 on the second, 3 on the third, and so on, and the \v(dialresult) and \v(dialstatus) variables. Here are some other applications for the DIAL MACRO (from users): * Phone numbers in the dialing directory are formatted with '-' for readability, but some modems don't like the hyphens, so the DIAL macro is used to remove them before dialing; e.g 0090-123-456-78-99 becomes 00901234567899: "def xxx return \freplace(\%1,-)". * To set some specific modem (or other) options depending on the called customer or telephone number. * Choosing the most appropriate provider based on (e.g.) time of day, or cycling through a list of providers in case some providers might be busy. To illustrate the final item, suppose you have a choice among many phone service providers; the provider is chosen by dialing an access code before the number. Different providers might be better (e.g. cheaper) for certain times of day or days of the week, or for dialing certain locations; you can use the DIAL macro to add the access for the most desirable provider. Similarly, when the same number might be reached through multiple providers, it's possible that one provider might not be able to complete the call, but another one can. In that case, you can use the DIAL macro to switch providers each time through the DIAL loop -- that's where the \v(dialcount) variable comes in handy. The following command can be used to debug the DIAL macro: SET DIAL TEST { ON, OFF } Normally OFF, so the DIAL command actually dials. When ON, the DIAL command performs all lookups and number conversions, and then goes through the number list and retry loop, but instead of actually dialing, lists the numbers it would have called if none of the DIAL attempts succeeded (or more precisely, every number was always busy). 2.1.14. Automatic Tone/Pulse Dialing Selection SET DIAL METHOD { AUTO, DEFAULT, PULSE, TONE } Chooses the dialing method for subsequent calls. Prior to version 7.0, C-Kermit's DIAL METHOD was DEFAULT by default, meaning it does not specify a dialing method to the modem, but relies on the modem to have an appropriate default dialing method set. So, for example, when using Hayes compatible modems, the dial string would be something like ATD7654321, rather than ATDT7654321 or ATDP7654321. In C-Kermit 7.0 and K95 1.1.19, the dial method can be set from the environment variable: K_DIAL_METHOD when Kermit starts. The values can be TONE, PULSE, or DEFAULT, e.g. (UNIX): set K_DIAL_METHOD=TONE; export K_DIAL_METHOD In the absence of a K_DIAL_METHOD definition, the new default SET DIAL METHOD is AUTO rather than DEFAULT. When DIAL METHOD is AUTO and the local country code is known, then if tone dialing is universally available in the corresponding area, tone dialing is used; if dialing from a location where pulse dialing is mandatory, pulse dialing is used. The "tone country" and "pulse country" lists are preloaded according to our knowledge at the time of release. You can see their contents in the SHOW DIAL listing. You can change the lists with: SET DIAL TONE-COUNTRIES [ cc [ cc [ ... ] ] ] Replaces the current TONE-COUNTRIES list with the one given. Each cc is a country code; separate them with spaces (not commas). Example: set dial tone-countries 1 358 44 46 49 If no country codes are given, the current list, if any, is removed, in which case SET DIAL METHOD AUTO is equivalent to SET DIAL METHOD DEFAULT. SET DIAL PULSE-COUNTRIES [ cc [ cc [ ... ] ] ] Replaces the current PULSE-COUNTRIES list with the one give. Syntax and operation is like SET DIAL TONE-COUNTRIES. If the same country code appears in both lists, Pulse takes precedence. The SET DIAL TONE- and PULSE-COUNTRIES commands perform no verification whatsoever on the cc's, since almost any syntax might be legal in some settings. Furthermore, there is no facility to edit the lists; you can only replace the whole list. However, since the only purpose of these lists is to establish a basis for picking tone or pulse dialing automatically, all you need to override the effect of the list is to set a specific dialing method with SET DIAL METHOD TONE or SET DIAL METHOD PULSE. 2.1.15. Dial-Modifier Variables As of C-Kermit 7.0, dial modifiers are available in the following variables: \v(dm_lp) Long pause \v(dm_sp) Short pause \v(dm_pd) Pulse dial \v(dm_td) Tone dial \v(dm_wa) Wait for answer \v(dm_wd) Wait for dialtone \v(dm_rc) Return to command mode You can use these in your dial strings in place of hardwired modifiers like "@", ",", etc, for increased portability of scripts. Example: C-Kermit>set modem type usrobotics C-Kermit>sho variables dm \v(dm_lp) = , \v(dm_sp) = / \v(dm_pd) = P \v(dm_td) = T \v(dm_wa) = @ \v(dm_wd) = W \v(dm_rc) = ; C-Kermit>exit 2.1.16. Giving Multiple Numbers to the DIAL Command Prior to C-Kermit 7.0, the only way to give a DIAL command a list of phone numbers to try until one answers was to create a dialing directory that had multiple entries under the same name, and then use that entry name in the DIAL command. Now a list of numbers can be given to the DIAL command directly in the following format: dial {{number1}{number2}{number3}...} This is the same list format used by SEND /EXCEPT: and other commands that allow a list where normally a single item is given. Restrictions on this form of the DIAL command are: * The first two braces must be adjacent; spacing is optional thereafter. * Each number must be an actual number to dial, not a dialing directory entry. * Dialing directory entries may not contain number lists in this format. In all other respects, the numbers are treated as if they had been fetched from the dialing directory; they can be in literal or portable format, etc. Example: dial {{7654321} {+1 (212) 5551212} { 1-212-5556789 }} The list can be any length at all, within reason. This feature is especially handy for use with the K95 Dialer, allowing a list of phone numbers to be specified in the Telephone Number box without having to set up or reference a separate dialing directory. You can also use it to add commonly-dialed sequences as variables in your C-Kermit customization file, e.g.: define work {{7654321}{7654322}{7654323}} and then: dial {\m(work)} (the variable name must be enclosed in braces). Or more simply: define work dial {{7654321}{7654322}{7654323}} and then: work 2.2. Modems 2.2.1. New Modem Types Since C-Kermit 6.0: atlas-newcom-33600ifxC Atlas/Newcom 33600 att-keepintouch AT&T KeepinTouch PCMCIA V.32bis Card Modem att-1900-stu-iii AT&T Secure Data STU-III Model 1900 att-1910-stu-iii AT&T Secure Data STU-III Model 1910 bestdata Best Data cardinal Cardinal V.34 MVP288X series. compaq Compaq Data+Fax (e.g. in Presario) fujitsu Fujitsu Fax/Modem Adapter generic-high-speed Any modern error-correcting data-compressing modem itu-t-v25ter/v250 ITU-T (CCITT) V.25ter (V.250) standard command set megahertz-att-v34 Megahertz AT&T V.34 megahertz-xjack Megahertz X-Jack motorola-codex Motorola Codex 326X Series motorola-montana Motorola Montana mt5634zpx Multitech MT5634ZPX rockwell-v90 Rockwell V.90 56K rolm-244pc Siemens/Rolm 244PC (AT command set) rolm-600-series Siemens/Rolm 600 Series (AT command set) spirit-ii QuickComm Spirit II suprasonic SupraSonic V288+ supra-express-v90 Supra Express V.90 One of the new types, "generic-high-speed" needs a bit of explanation. This type was added to easily handle other types that are not explicitly covered, without going through the bother of adding a complete user-defined modem type. This one works for modern modems that use the AT command set, on the assumption that all the default ("factory") settings of the modem (a) are appropriate for Kermit, (b) include error correction, data compression, and speed buffering; and (c) are recallable with the command AT&F. If the command to recall your modem's profile is not AT&F, use the SET MODEM COMMAND INIT-STRING command to specify the appropriate modem command. The default init-string is AT&F\13 (that is, AT, ampersand, F, and then carriage return); a survey of about 20 modern modem types shows they all support this, but they might mean different things by it. For example, the USR Sportster or Courier needs AT&F1 (not AT&F, which is equivalent to AT&F0, which recalls an inappropriate profile), so for USR modems: set modem type generic-high-speed set modem command init AT&F1\13 Of course, USR modems already have their own built-in modem type. But if you use this one instead, it will dial faster because it has fewer commands to give to the modem; in that sense "&F1" is like a macro that bundles numerous commands into a single one. See your modem manual for details about factory profiles and commands to recall them. WARNING: Do not use the generic-high-speed modem type in operating systems like VMS where hardware flow control is not available, at least not unless you change the init string from AT&F\13 to something else that enables local Xon/Xoff or other appropriate type of flow control. Also see [404]Section 2.1.7 for additional hints about making dialing go faster. 2.2.2. New Modem Controls SET MODEM CAPABILITIES list In C-Kermit 7.0, this command automatically turns MODEM SPEED-MATCHING OFF if SB (Speed Buffering) is in the list, and turns it ON if SB is absent. SET MODEM COMMAND PREDIAL-INIT [ text ] Commands to be sent to the modem just prior to dialing. Normally none. SET MODEM SPEAKER { ON, OFF } Determines whether modem speaker is on or off while call is being placed. ON by default. Note: This command does not provide fine-grained control over when the speaker is on or off. Normally, ON means while the call is being placed, until the point at which carrier is successfully established. If your modem has a different speaker option that you want to choose, then use the SET MODEM COMMAND SPEAKER ON text command to specify this option. SET MODEM COMMAND SPEAKER { ON, OFF } [ text ] Specify or override the commands to turn your modem's speaker on and off. SET MODEM VOLUME { LOW, MEDIUM, HIGH } When MODEM SPEAKER is on, select volume. Note: In some modems, especially internal ones, these commands have no effect; this is a limitation of the particular modem, not of Kermit. SET MODEM COMMAND VOLUME { LOW, MEDIUM, HIGH } [ text ] Specify or override the commands to set your modem's speaker volume. SET MODEM COMMAND IGNORE-DIALTONE [ text ] The command to enable blind dialing ([405]Section 2.1.6). SET MODEM ESCAPE-CHARACTER code Has been augmented to allow codes of 0 or less: < 0 means the escape mechanism is disabled. = 0 means to use (restore) the default value from the modem database. > 0 and < 128 is a literal value to be used instead of the default one. > 127 means the escape mechanism is disabled. This affects "modem hangup". When the escape mechanism is disabled, but SET MODEM HANGUP-METHOD is MODEM-COMMAND, it sends the hangup command immediately, without the +++ business first. This is useful (for example) when sending lots of numeric pages, a process in which never we go online, and so never need to escape back. Eliminating the unnecessary pauses and escape sequence allows a lot more pages to be sent per unit time. Recall that C-Kermit can dial modems to which it is connected via TCP/IP (Telnet or Rlogin) as described on page 126 of [406]Using C-Kermit, 2nd Ed. In this case the MODEM HANGUP-METHOD should be MODEM-COMMAND, since RS-232 signals don't work over TCP/IP connections. As noted in the manual, such connections are set up by the following sequence: set host host [ port ] set modem type name dial number But this can cause complications when you use Kermit to switch between serial and TCP/IP connections. In the following sequence: set host name set modem type name set port name the first two commands obey the rules for dialing out over Telnet. However, the SET PORT command requires that Kermit close its current (Telnet) connection before it can open the serial port (since Kermit can only have one connection open at a time). But since a modem type was set after the "set host" command was given, Kermit assumes it is a Telnet dialout connection and so sends the modem's hangup sequence is sent to the Telnet host. To avoid this, close the network connection explicitly before opening the serial one: set host name close set modem type name set port name 2.3. TELNET and RLOGIN For additional background, please also read the [407]TELNET.TXT file, also available on the Web in [408]HTML format. Cautions: * If making a Telnet connection with C-Kermit takes a very long time, like over a minute, whereas the system Telnet program makes the same connection immediately, try including the /NOWAIT switch: C-Kermit> telnet /nowait hostname See [409]TELNET.TXT or [410]TELNET.HTM for details. If it also takes a very long time to make a Telnet connection with system Telnet, then the delay is most likely caused by reverse DNS lookups when your host is not properly registered in DNS. * When supplying numeric IP addresses to C-Kermit or to any other application (regular Telnet, Rlogin, etc), do not include leading 0's in any fields unless you intend for those fields to be interpreted as octal (or hex) numbers. The description of the Internet address interpreter (the sockets library inet_addr() routine) includes these words: All numbers supplied as "parts" in a "." notation may be decimal, octal, or hexadecimal, as specified in the C language (that is, a leading 0x or 0X implies hexadecimal; otherwise, a leading 0 implies octal; otherwise, the number is interpreted as decimal). To illustrate, 128.59.39.2 and 128.059.039.002 are not the same host! Even though most of the fields contain non-octal digits. Using system Telnet (not Kermit): $ telnet 128.059.039.002 Trying 128.49.33.2 ... Of course the same thing happens with Kermit because it uses (as it must) the same system service for resolving network addresses that Telnet, FTP, and all other TCP/IP applications use. * The RLOGIN section on page 123 does not make it clear that you can use the SET TELNET TERMINAL-TYPE command to govern the terminal type that is reported by C-Kermit to the RLOGIN server. * Note that the SET TCP commands described on pages 122-123 might be absent; some platforms that support TCP/IP do not support these particular controls. New commands: TELOPT { AO, AYT, BREAK, CANCEL, EC, EL, EOF, EOR, GA, IP, DMARK, DO, DONT, NOP, SB, SE, SUSP, WILL, WONT } This command was available previously, but supported only DO, DONT, WILL, and WONT. Now it lets you send all the Telnet protocol commands. Note that certain commands do not require a response, and therefore can be used as nondestructive "probes" to see if the Telnet session is still open; e.g.: set host xyzcorp.com ... telopt nop if fail stop 1 Connection lost SET TCP ADDRESS [ ip-address ] Specifies the IP address of the computer that C-Kermit is running on. Normally this is not necessary. The exception would be if your machine has multiple network adapters (physical or virtual) with a different address for each adapter AND you want C-Kermit to use a specific address when making outgoing connections or accepting incoming connections. SET TCP DNS-SERVICE-RECORDS { ON, OFF } Tells C-Kermit whether to try to use DNS SRV records to determine the host and port number upon which to find an advertised service. For example, if a host wants regular Telnet connections redirected to some port other than 23, this feature allows C-Kermit to ask the host which port it should use. Since not all domain servers are set up to answer such requests, this feature is OFF by default. SET TCP REVERSE-DNS-LOOKUP { ON, OFF, AUTO } Tells Kermit whether to perform a reverse DNS lookup on TCP/IP connections. This allows Kermit to determine the actual hostname of the host it is connected to, which is useful for connections to host pools, and is required for Kerberos connections to host pools and for incoming connections. If the other host does not have a DNS entry, the reverse lookup could take a long time (minutes) to fail, but the connection will still be made. Turn this option OFF for speedier connections if you do not need to know exactly which host you are connected to and you are not using Kerberos. AUTO, the default, means the lookup is done on hostnames, but not on numeric IP addresses. SET TELNET WAIT-FOR-NEGOTIATIONS { ON, OFF } Each Telnet option must be fully negotiated either On or Off before the session can continue. This is especially true with options that require sub-negotiations such as Authentication, Encryption, and Kermit; for proper support of these options Kermit must wait for the negotiations to complete. Of course, Kermit has no way of knowing whether a reply is delayed or not coming at all, and so will wait a minute or more for required replies before continuing the session. If you know that Kermit's Telnet partner will not be sending the required replies, you can set this option of OFF to avoid the long timeouts. Or you can instruct Kermit to REFUSE specific options with the SET TELOPT command. SET TELOPT [ { /CLIENT, /SERVER } ] option { ACCEPTED, REFUSED, REQUESTED, REQUIRED } [ { ACCEPTED, REFUSED, REQUESTED, REQUIRED } ] SET TELOPT lets you specify policy requirements for Kermit's handling of Telnet option negotiations. Setting an option is REQUIRED causes Kermit to offer the option to the peer and disconnect if the option is refused. REQUESTED causes Kermit to offer an option to the peer. ACCEPTED results in no offer but Kermit will attempt to negotiate the option if it is requested. REFUSED instructs Kermit to refuse the option if it is requested by the peer. Some options are negotiated in two directions and accept separate policies for each direction; the first keyword applies to Kermit itself, the second applies to Kermit's Telnet partner; if the second keyword is omitted, an appropriate (option-specific) default is applied. You can also include a /CLIENT or /SERVER switch to indicate whether the given policies apply when Kermit is the Telnet client or the Telnet server; if no switch is given, the command applies to the client. Note that some of Kermit's Telnet partners fail to refuse options that they do not recognize and instead do not respond at all. In this case it is possible to use SET TELOPT to instruct Kermit to REFUSE the option before connecting to the problem host, thus skipping the problematic negotiation. Use SHOW TELOPT to view current Telnet Option negotiation settings. SHOW TELNET displays current Telnet settings. 2.3.0. Bug Fixes If "set host nonexistent-host" was given (and it properly failed), followed by certain commands like SEND, the original line and modem type were not restored and C-Kermit thought that it still had a network hostname; fixed in 7.0. 2.3.1. Telnet Binary Mode Bug Adjustments SET TELNET BUG BINARY-ME-MEANS-U-TOO { ON, OFF } was added to edit 192 after the book was printed. Also SET TELNET BUG BINARY-U-MEANS-ME-TOO. The default for both is OFF. ON should be used when communicating with a Telnet partner (client or server) that mistakenly believes that telling C-Kermit to enter Telnet binary mode also means that it, too, is in binary mode, contrary to the Telnet specification, which says that binary mode must be negotiated in each direction separately. 2.3.2. VMS UCX Telnet Port Bug Adjustment A new command, SET TCP UCX-PORT-BUG, was added for VMS versions with UCX (DEC TCP/IP), applying only to early versions of UCX, like 2.2 or earlier. If you try to use VMS C-Kermit to make a Telnet connection using a port name (like "telnet", which is used by default), the underlying UCX getservbyname() function might return the service number with its bytes swapped and the connection will fail. If "telnet hostname 23" works, then your version of UCX has this bug and you can put "set tcp ucx-port-bug on" in your CKERMIT.INI file to get around it. 2.3.3. Telnet New Environment Option The TELNET NEW-ENVIRONMENT option ([411]RFC 1572) is supported as 7.0. This option allows the C-Kermit Telnet client to send certain well-known variables to the Telnet server, including USER, PRINTER, DISPLAY, and several others. This feature is enabled by default in Windows and OS/2, disabled by default elsewhere. The command to enable and disable it is: SET TELNET ENVIRONMENT { ON, OFF } When ON, and you Telnet to another computer, you might (or might not) notice that the "login:" or "Username:" prompt does not appear -- that's because your username was sent ahead, in which case the remote system might prompt you only for your password (similar to Rlogin). Use "set telnet environment off" to defeat this feature, particularly in scripts where the dialog must be predictable. You can also use this command to specify or override specific well-known environment variable values: SET TELNET ENVIRONMENT { ACCT,DISPLAY,JOB,PRINTER,SYSTEMTYPE,USER } [ text ] 2.3.4. Telnet Location Option The TELNET LOCATION option ([412]RFC 779) is supported in 7.0. This option allows the C-Kermit Telnet client to send a location string to the server if the server indicates its willingness to accept one. If an environment variable named LOCATION exists at the time C-Kermit starts, its value is used as the location string. If you want to change it, use: SET TELNET LOCATION text If you omit the text from this command, the Telnet location feature is disabled. SET TELNET ENVIRONMENT DISPLAY is used to set the DISPLAY variable that is sent to the host, as well as the XDISPLAY location. 2.3.5. Connecting to Raw TCP Sockets The SET HOST and TELNET commands now accept an optional switch, /RAW-SOCKET, at the end, only if you first give a host and a port. Example: set host xyzcorp.com 23 /raw-socket set host 128.49.39.2:2000 /raw-socket telnet xyzcorp.com 3000 /raw Without this switch, C-Kermit behaves as a Telnet client when (a) the port is 23 or 1649, or (b) the port is not 513 and the server sent what appeared to be Telnet negotiations -- that is, messages starting with 0xFF (IAC). With this switch, Kermit should treat all incoming bytes as raw data, and will not engage in any Telnet negotiations or NVT CRLF manipulations. This allows transparent operation through (e.g.) raw TCP ports on Cisco terminal servers, through the 'modemd' modem server, etc. 2.3.6. Incoming TCP Connections Accomplished via SET HOST * port, were introduced in C-Kermit 6.0, but for UNIX only. In Version 7.0, they are also available for VMS. 2.4. The EIGHTBIT Command EIGHTBIT is simply a shorthand for: SET PARITY NONE, SET TERMINAL BYTESIZE 8, SET COMMAND BYTESIZE 8; that is, a way to set up an 8-bit clean connection in a single command. 2.5. The Services Directory Chapter 7 of [413]Using C-Kermit does not mention the ULOGIN macro, which is used by our sample services directory, CKERMIT.KND. Unlike UNIXLOGIN, VMSLOGIN, etc, this one is for use with systems that require a user ID but no password. Therefore it doesn't prompt for a password or wait for a password prompt from the remote service. In version 7.0, the CALL macro was changed to not execute a SET MODEM TYPE command if the given modem type was the same as the current one; otherwise the new SET MODEM TYPE command would overwrite any customizations that the user had made to the modem settings. Ditto for SET LINE / SET PORT and SET SPEED. 2.6. Closing Connections Until version 7.0, there was never an obvious and general way to close a connection. If a serial connection was open, it could be closed by "set line" or "set port" (giving no device name); if a network connection was open, it could be closed by "set host" (no host name). In version 7.0, a new command closes the connection in an obvious and straightforward way, no matter what the connection type: CLOSE [ CONNECTION ] The CLOSE command was already present, and required an operand such as DEBUG-LOG, WRITE-FILE, etc, and so could never be given by itself. The new CONNECTION operand is now the default operand for CLOSE, so CLOSE by itself closes the connection, if one is open, just as you would expect, especially if you are a Telnet or Ftp user. Also see the description of the new SET CLOSE-ON-DISCONNECT command in [414]Section 2.10. 2.7. Using C-Kermit with External Communication Programs C-Kermit 7.0 includes a new ability to create and conduct sessions through other communications programs. Two methods are available: 1. Pty (pseudoterminal): The external program is run on a "pseudoterminal", which is controlled by Kermit. This method works with practically any external program, but it is not portable. At this writing, it works only on some (not all) UNIX versions, and not on any non-UNIX platforms. 2. Pipe: The external program's standard input and output are redirected through a "pipe" controlled by Kermit. This method is relatively portable -- it should work across all UNIX versions, and it also works in Windows and OS/2 -- but it is effective only when the external program actually uses standard i/o (and many don't). The two methods are started differently but are used the same way thereafter. The purpose of this feature is to let you use C-Kermit services like file transfer, character-set translation, scripting, automatic dialing, etc, on connections that Kermit can't otherwise make itself. This feature is the opposite of the REDIRECT feature, in which C-Kermit makes the connection, and redirects an external (local) command or program over this connection. In a pty or pipe connection, C-Kermit runs and controls a local command or program, which makes the connection. (The same method can be used to simply to control a local program without making a connection; see [415]Section 2.8.) To find out if your version of Kermit includes PTY support, type "show features" and look for NETPTY in the alphabetical list of options. For pipes, look for NETCMD. The commands are: SET NETWORK TYPE PTY or SET NETWORK TYPE PIPE SET HOST command where command is any interactive command. If the command does not use standard i/o, you must use SET NETWORK TYPE PTY. Notes: * COMMAND is an invisible synonym for PIPE. * The command and its arguments are case-sensitive in UNIX. The SET NETWORK TYPE, SET HOST sequence sets the given network type for all subsequent SET HOST commands until another SET NETWORK TYPE command is given to change it. You can also use the new /NETWORK-TYPE:PTY or /NETWORK-TYPE:PIPE (or simply /PIPE or /PTY) switches on the SET HOST command itself: SET HOST /NETWORK-TYPE:PIPE command ; These two are the same SET HOST /PIPE command SET HOST /NETWORK-TYPE:PTY command ; Ditto SET HOST /PTY command These are like SET NETWORK TYPE followed by SET HOST, except they apply only to the connection being made and do not change the global network type setting (see [416]Section 1.5 about the difference between switches and SET commands). Include any command-line options with the command that might be needed, as in this example where C-Kermit uses another copy of itself as the communications program: SET HOST /PIPE /CONNECT kermit -YQJ xyzcorp.com IMPORTANT: In Unix, wildcards and redirectors are interpreted by the shell. If you want to run a program with (say) SET HOST /PTY with its i/o redirected or with wildcard file arguments, you will need to invoke the shell too. Example: SET HOST /PTY {sh -c "crypt < foo.x"} SET HOST /PTY {sh -c "grep somestring *.txt"} As usual, if you include the /CONNECT switch, SET HOST enters CONNECT mode immediately upon successful execution of the given command. Therefore new commands are available as a shorthand for SET HOST /CONNECT /PTY and /PIPE: PTY [ command ] PIPE [ command ] The PTY and PIPE commands work like the TELNET and RLOGIN commands: they set up the connection (in this case, using the given command) and then enter CONNECT mode automatically (if the PIPE or PTY command is given without a command, it continues the current session if one is active; otherwise it gives an error message). The PIPE command is named after the mechanism by which C-Kermit communicates with the command: UNIX pipes. C-Kermit's i/o is "piped" through the given command. Here is a typical example: PIPE rlogin -8 xyzcorp.com This is equivalent to: SET HOST /PIPE rlogin -8 xyzcorp.com CONNECT and to: SET HOST /PIPE /CONNECT rlogin -8 xyzcorp.com IMPORTANT: If you are writing a script, do not use the PIPE, PTY, TELNET, or RLOGIN command unless you really want C-Kermit to enter CONNECT mode at that point. Normally SET HOST is used in scripts to allow the login and other dialogs to be controlled by the script itself, rather than by an actively participating human at the keyboard. Throughput of pty and pipe connections is limited by the performance of the chosen command or program and by the interprocess communication (IPC) method used and/or buffering capacity of the pipe or pty, which in turn depends on the underlying operating system. In one trial (on SunOS 4.1.3), we observed file transfer rates over an rlogin connection proceeding at 200Kcps for downloads, but only 10Kcps for uploads on the same connection with the same settings (similar disparities were noted in HP-UX). Examination of the logs revealed that a write to the pipe could take as long as 5 seconds, whereas reads were practically instantaneous. On the other hand, using Telnet as the external program rather than rlogin, downloads and uploads were better matched at about 177K each. Most external communication programs, like C-Kermit itself, have escape characters or sequences. Normally these begin with (or consist entirely of) a control character. You must be sure that this control character is not "unprefixed" when uploading files, otherwise the external program will "escape back" to its prompt, or close the connection, or take some other unwanted action. When in CONNECT mode, observe the program's normal interaction rules. Of course C-Kermit's own escape character (normally Ctrl-\) is active too, unless you have taken some action to disable it. On PTY connections, the underlying PTY driver is not guaranteed to be transparent to control characters -- for example, it might expand tabs, translate carriage returns, generate signals if it sees an interrupt character, and so on. Similar things might happen on a PIPE connection. For this reason, if you plan to transfer files over a PTY or PIPE connection, tell the file sender to: SET PREFIXING ALL This causes all control characters to be prefixed and transmitted as printable ASCII characters. If the external connection program is not 8-bit clean, you should also: SET PARITY SPACE This causes 8-bit data to be encoded in 7 bits using single and/or locking shifts. And if it does not make a reliable connection (such as those made by Telnet, Rlogin, Ssh, etc), you should: SET STREAMING OFF This forces C-Kermit to treat the connection as unreliable and to engage in its normal ACK/NAK protocol for error detection and correction, rather than "streaming" its packets, as it normally does on a network connection ([417]Section 4.20). In some cases, buffer sizes might be restricted, so you might also need to reduce the Kermit packet length to fit; this is a trial-and-error affair. For example, if transfers always fail with 4000-byte packets, try 2000. If that fails too, try 1000, and so on. The commands are: SET RECEIVE PACKET-LENGTH number This tells the file receiver to tell the file sender the longest packet length it can accept. SET SEND PACKET-LENGTH number This tells the file sender not to send packets longer than the given length, even if the receiver says longer ones are OK. Of course, if the receiver's length is shorter, the shorter length is used. If none of this seems to help, try falling back to the bare minimum, lowest-common-denominator protocol settings: ROBUST No sliding windows, no streaming, no control-character unprefixing, packet length 90. And then work your way back up by trial and error to get greater throughput. Note that when starting a PIPE connection, and the connection program (such as telnet or rlogin) prints some greeting or information messages before starting the connection, these are quite likely to be printed with a stairstep effect (linefeed without carriage return). This is because the program is not connected with the UNIX terminal driver; there's not much Kermit can do about it. Once the connection is made, everything should go back to normal. This shouldn't happen on a PTY connection because a PTY is, indeed, a terminal. On a similar note, some connection programs (like Solaris 2.5 rlogin) might print lots of error messages like "ioctl TIOCGETP: invalid argument" when used through a pipe. They are annoying but usually harmless. If you want to avoid these messages, and your shell allows redirection of stderr, you can redirect stderr in your pipe command, as in this example where the user's shell is bash: PIPE rlogin xyzcorp.com 2> /dev/null Or use PTY rather than PIPE, since PTY is available on Solaris. 2.7.0. C-Kermit over tn3270 and tn5250 Now you can make a connection from C-Kermit "directly" to an IBM mainframe and transfer files with it, assuming it has Kermit-370 installed. Because tn3270 is neither 8-bit clean nor transparent to control characters, you must give these commands: SET PREFIXING ALL ; Prefix all control characters SET PARITY SPACE ; Telnet connections are usually not 8-bit clean and then: SET HOST /PTY /CONNECT tn3270 abccorp.com or simply: pty tn3270 abccorp.com SET HOST /PIPE does not work in this case, at least not for file transfer. File transfer does work, however, with SET HOST /PTY, provided you use the default packet length of 90 bytes; anything longer seems to kill the session. You can also make connections to IBM AS/400 computers if you have a tn5250 program installed: pty tn5250 hostname In this case, however, file transfer is probably not in the cards since nobody has ever succeeded in writing a Kermit program for the AS/400. Hint: define tn3270 { check pty if fail end 1 Sorry - no PTY support... pty tn3270 \%* } Similarly for tn5250. Note that CHECK PTY and CHECK PIPE can be used in macros and scripts to test whether PTY or PIPE support is available. 2.7.1. C-Kermit over Telnet Although C-Kermit includes its own Telnet implementation, you might need to use an external Telnet program to make certain connections; perhaps because it has access or security features not available in C-Kermit itself. As noted above, the only precautions necessary are usually: SET PREFIXING ALL ; Prefix all control characters SET PARITY SPACE ; Telnet connections might not be 8-bit clean and then: SET HOST /PTY (or /PIPE) /CONNECT telnet abccorp.com or, equivalently: PTY (or PIPE) telnet abccorp.com 2.7.2. C-Kermit over Rlogin C-Kermit includes its own Rlogin client, but this can normally be used only if you are root, since the rlogin TCP port is privileged. But ptys and pipes let you make rlogin connections with C-Kermit through your computer's external rlogin program, which is normally installed as a privileged program: SET PREFIXING ALL and then: SET HOST /PTY (or /PIPE) /CONNECT rlogin -8 abccorp.com or, equivalently: PTY (or PIPE) rlogin -8 abccorp.com The "-8" option to rlogin enables transmission of 8-bit data. If this is not available, then include SET PARITY SPACE if you intend to transfer files. Note that the normal escape sequence for rlogin is Carriage Return followed by Tilde (~), but only when the tilde is followed by certain other characters; the exact behavior depends on your rlogin client, so read its documentation. 2.7.3. C-Kermit over Serial Communication Programs Ptys and pipes also let you use programs that make serial connections, such as cu or tip. For example, C-Kermit can be used through cu to make connections that otherwise might not be allowed, e.g. because C-Kermit is not installed with the required write permissions to the dialout device and the UUCP lockfile directory. Suppose your UUCP Devices file contains an entry for a serial device tty04 to be used for direct connections, but this device is protected against you (and Kermit when you run it). In this case you can: SET CONTROL PREFIX ALL PTY (or PIPE) cu -l tty04 (Similarly for dialout devices, except then you also need to include the phone number in the "cu" command.) As with other communication programs, watch out for cu's escape sequence, which is the same as the rlogin program's: Carriage Return followed by Tilde (followed by another character to specify an action, like "." for closing the connection and exiting from cu). 2.7.4. C-Kermit over Secure Network Clients DISCLAIMER: There are laws in the USA and other countries regarding use, import, and/or export of encryption and/or decryption or other forms of security software, algorithms, technology, and intellectual property. The Kermit Project attempts to follow all known statutes, and neither intends nor suggests that Kermit software can or should be used in any way, in any location, that circumvents any regulations, laws, treaties, covenants, or other legitimate canons or instruments of law, international relations, trade, ethics, or propriety. For secure connections or connections through firewalls, C-Kermit 7.0 can be a Kerberos, SRP, and/or SOCKS client when built with the appropriate options and libraries. But other application-level security acronyms and methods -- SSH, SSL, SRP, TLS -- pop up at an alarming rate and are (a) impossible to keep up with, (b) usually mutually incompatible, and (c) have restrictions on export or redistribution and so cannot be included in C-Kermit itself. However, if you have a secure text-based Telnet (or other) client that employs one of these security methods, you can use C-Kermit "through" it via a pty or pipe. 2.7.4.1. SSH C-Kermit does not and can not incorporate SSH due to licensing, patent, and USA export law restrictions. The UNIX SSH client does not use standard input/output, and therefore can be used only by Kermit's PTY interface, if one is present. The cautions about file transfer, etc, are the same as for Rlogin. Example: SET PREFIXING ALL PTY ssh XYZCORP.COM Or, for a scripted session: SET PREFIXING ALL SET HOST /PTY ssh XYZCORP.COM Hint: define ssh { check pty if fail end 1 Sorry - no PTY support... pty ssh \%* } 2.7.4.2. SSL Secure Sockets Layer (SSL) is another TCP/IP security overlay, this one designed by and for Netscape. An SSL Telnet client is available for UNIX from the University of Queensland. More info at: [418]http://www.psy.uq.oz.au/~ftp/Crypto/ Interoperability with C-Kermit is unknown. C-Kermit also includes its own built-in SSL/TLS support, but it is not exportable; [419]CLICK HERE file for details. 2.7.4.3. SRP SRP(TM) is Stanford University's Secure Remote Password protocol. An SRP Telnet client is available from Stanford: [420]http://srp.stanford.edu/srp/ Stanford's SRP Telnet client for UNIX has been tested on SunOS and works fine with C-Kermit, as described in [421]Section 2.7.1, e.g. SET PREFIX ALL PTY (or PIPE) srp-telnet xenon.stanford.edu C-Kermit itself can be built as an SRP Telnet client on systems that have libsrp.a installed; the C-Kermit support code, however, may not be exported outside the USA or Canada. 2.7.4.4. SOCKS C-Kermit can be built as a SOCKS-aware client on systems that have a SOCKS library. See section 8.1.1 of the [422]ckccfg.txt file. C-Kermit 7.0 can also be run over SOCKSified Telnet or rlogin clients with SET NETWORK TYPE COMMAND. Suppose the Telnet program on your system is SOCKS enabled but C-Kermit is not. Make Kermit connections like this: SET PREFIX ALL PTY (or PIPE) telnet zzz.com 2.7.4.5. Kerberos UNIX C-Kermit can be built with MIT Kerberos IV or V authentication and encryption. Instructions are available in a [423]separate document. Additional modules are required that can not be exported from the USA to any country except Canada, by US law. If you have Kerberos installed but you don't have a Kerberized version of C-Kermit, you can use ktelnet as C-Kermit's external communications program to make secure connections without giving up C-Kermit's services: SET PREFIX ALL PTY (or PIPE) ktelnet cia.gov 2.8. Scripting Local Programs If your version of Kermit has PTY support built in, then any text-based program can be invoked with SET HOST /PTY or equivalent command and controlled using the normal sequence of OUTPUT, INPUT, IF SUCCESS commands (this is the same service that is provided by the 'expect' program, but controlled by the Kermit script language rather than Tcl). When PTY service is not available, then any program that uses standard input and output can be invoked with SET HOST /PIPE. Here's an example in which we start an external Kermit program, wait for its prompt, give it a VERSION command, and then extract the numeric version number from its response: set host /pty kermit -Y if fail stop 1 {Can't start external command} input 10 C-Kermit> if fail stop 1 {No C-Kermit> prompt} output version\13 input 10 {Numeric: } if fail stop 1 {No match for "Numeric:"} clear input input 10 \10 echo VERSION = "\fsubstr(\v(input),1,6)" output exit\13 This technique could be used to control any other interactive program, even those that do screen formatting (like Emacs or Vi), if you can figure out the sequence of events. If your Kermit program doesn't have PTY support, then the commands are restricted to those using standard i/o, including certain shells, interactive text-mode "hardcopy" editors like ex, and so on. If you are using the PTY interface, you should be aware that it runs the given program or command directly on the pty, without any intervening shell to interpret metacharacters, redirectors, etc. If you need this sort of thing, include the appropriate shell invocation as part of your command; for example: pty echo * just echoes "*"; whereas: pty ksh -c "echo *" echoes all the filenames that ksh finds matching "*". Similarly for redirection: set host /pty ksh -c "cat > foo" ; Note: use shell quoting rules here set transmit eof \4 transmit bar And for that matter, for built-in shell commands: set host /pty ksh -c "for i in *; do echo $i; done" The PIPE interface, on the other hand, invokes the shell automatically, so: pipe echo * prints filenames, not "*". 2.9. X.25 Networking X.25 networking is documented in [424]Using C-Kermit, 2nd Edition. When the book was published, X.25 was available only in SunOS, Solaris, and Stratus VOS. Unlike TCP/IP, X.25 APIs are not standardized; each vendor's X.25 libraries and services (if they have them at all) are unique. This section describes new additions. 2.9.1. IBM AIXLink/X.25 Network Provider Interface for AIX Support for X.25 was added via IBM's Network Provider Interface (NPI), AIXLink/X.25 1.1, to the AIX 4.x version of C-Kermit 7.0. Unfortunately, AIXLink/X.25 is a rather bare-bones facility, lacking in particular any form of PAD support (X.3, X.28, X.29). Thus, the AIX version of C-Kermit, when built to include X.25 networking, has neither a PAD command, nor a SET PAD command. The same is true for the underlying AIX system: no PAD support. Thus it is not possible to have an interactive shell session over an X.25 connection into an AIX system (as far as we know), even from X.25-capable Kermit versions (such as Solaris or VOS) that do include PAD support. Thus the X.25 capabilities in AIX C-Kermit are limited to peer-to-peer connections, e.g. from a C-Kermit client to a C-Kermit server. Unlike the Solaris, SunOS, and VOS versions, the AIX version can accept incoming X.25 connections: set network type x.25 if fail stop 1 Sorry - no X.25 support ; Put any desired DISABLE or ENABLE or SET commands here. set host /server * if fail stop 1 X.25 "set host *" failed And then access it from the client as follows: set network type x.25 if fail stop 1 Sorry - no X.25 support set host xxxxxxx ; Specify the X.25/X.121 address if fail stop 1 Can't open connection And at this point the client can use the full range of client commands: SEND, GET, REMOTE xxx, FINISH, BYE. The AIX version also adds two new variables: \v(x25local_nua) The local X.25 address. \v(x25remote_nua) The X.25 address of the host on the other end of the connection. C-Kermit's AIX X.25 client has not been tested against anything other than a C-Kermit X.25 server on AIX. It is not known if it will interoperate with C-Kermit servers on Solaris, SunOS, or VOS. To make an X.25 connection from AIX C-Kermit, you must: set x25 call-user-data xxxx where xxxx can be any even-length string of hexadecimal digits, e.g. 123ABC. 2.9.2. HP-UX X.25 Although C-Kermit presently does not include built-in support for HP-UX X.25, it can still be used to make X.25 connections as follows: start Kermit and tell it to: set prefixing all set parity space pty padem address This should work in HP-UX 9.00 and later (see [425]Section 2.7). If you have an earlier HP-UX version, or the PTY interface doesn't work or isn't available, try: set prefixing all set parity space pipe padem address Failing that, use Kermit to telnet to localhost and then after logging back in, start padem as you would normally do to connect over X.25. 2.10. Additional Serial Port Controls C-Kermit 7.0 adds the following commands for greater control over serial ports. These commands are available only in C-Kermit versions whose underlying operating systems provide the corresponding services (such as POSIX and UNIX System V), and even then their successful operation depends on the capabilities of the specific device and driver. SET DISCONNECT { ON, OFF } On a SET LINE or SET PORT connection with SET CARRIER ON or AUTO, if the carrier signal drops during the connection, indicating that the connection has been lost, and C-Kermit notices it, this setting governs what happens next. With SET DISCONNECT OFF, which is consistent with previous behavior, and therefore the default, C-Kermit continues to keep the device open and allocated. With SET DISCONNECT ON, C-Kermit automatically closes and releases the device when it senses a carrier on-to-off transition, thus allowing others to use it. However, it remains the default device for i/o (DIAL, REDIAL, INPUT, SEND, CONNECT, etc), so if a subsequent i/o command is given, the device is reopened if it is still available. When it has been automatically closed in this manner, SHOW COMMUNICATIONS puts "(closed)" after its name, and in UNIX, the lockfile disappears -- both from SHOW COMM and from the lockfile directory itself. Synonym: SET CLOSE-ON-DISCONNECT. SET EXIT ON-DISCONNECT { ON, OFF } Like DISCONNECT, but makes the program exit if a connection drops. Note that SET CLOSE-ON-DISCONNECT and SET EXIT ON-DISCONNECT apply only to connections that drop; they do not apply to connections that can't be made in the first place. For example, they have no effect when a SET LINE, SET HOST, TELNET, or DIAL command fails. HANGUP If [CLOSE-ON-]DISCONNECT is ON, and the HANGUP command is given on a serial device, and the carrier signal is no longer present after the HANGUP command, the device is closed and released. SET PARITY HARDWARE { EVEN, ODD } Unlike SET PARITY { EVEN, ODD, MARK, SPACE }, which selects 7 data bits plus the indicated kind of parity (to be done in software by Kermit itself), SET PARITY HARDWARE selects 8 data bits plus even or odd parity, to be done by the underlying hardware, operating system, or device driver. This command is effective only with a SET LINE or SET PORT device. That is, it has no effect in remote mode, nor on network connections. There is presently no method for selecting 8 data bits plus mark or space parity. If hardware parity is in effect, the variable \v(hwparity) is set to "even" or "odd". Note: some platforms might also support settings of SPACE, MARK, or NONE. SET STOP-BITS { 1, 2 } This tells the number of 1-bits to insert after an outbound character's data and parity bits, to separate it from the next character. Normally 1. Choosing 2 stop bits should do no harm, but will slow down serial transmission by approximately 10 percent. Historically, 2 stop bits were used with Teletypes (at 110 bps or below) for print-head recovery time. There is presently no method for choosing any number of stop bits besides 1 and 2. SET SERIAL [ dps ] dps stands for Data-bits, Parity, Stop-bits. This is the notation familiar to many people for serial port configuration: 7E1, 8N1, 7O2, etc. The data bits number also becomes the TERMINAL BYTESIZE setting. The second character is E for Even, O for Odd, M for Mark, S for Space, or N for None. The list of available options depends on the capabilities of the specific platform. If dps is omitted, 8N1 is used. Type "set serial ?" for a list of available choices. Examples: SET SERIAL 7E1 Equivalent to SET PARITY EVEN, SET STOP-BITS 1, SET TERM BYTE 7. SET SERIAL 8N1 Equivalent to SET PARITY NONE, SET STOP-BITS 1, SET TERM BYTE 8. SET SERIAL 7E2 Equivalent to SET PARITY EVEN and SET STOP-BITS 2, SET TERM BYTE 7. SET SERIAL 8E2 Same as SET PARITY HARDWARE EVEN, SET STOP-BITS 2, SET TERM BYTE 8. SET SERIAL Same as SET PARITY NONE and SET STOP-BITS 1, SET TERM BYTE 8. Notes: * The SET SERIAL xx2 options are available only in Kermit versions where the SET PARITY HARDWARE command is also available. (SHOW FEATURES includes "HWPARITY" in its options list.) * The SET SERIAL 7xx and 8N1 options affect the software parity setting, even for network connections. * As noted in the manual, selecting 8 data bits does not give you 8-bit terminal sessions in CONNECT mode unless you also SET TERMINAL BYTESIZE 8. The default terminal bytesize remains 7, to protect against the situation where the remote host is generating parity but you don't know about it. If the terminal bytesize was 8 by default and you CONNECTed to such a host, you would see only garbage on your screen. * If you do not give a SET STOP-BITS or SET SET SERIAL command, C-Kermit does not attempt to set the device's stop bits; instead, it uses whatever setting the device uses when not given explicit instructions about stop bits. SHOW COMMUNICATIONS displays the current settings. Stop bits and hardware parity are shown only for SET PORT / SET LINE (serial) devices, since they do not apply to network connections or to remote mode. STOP-BITS is shown as "(default)" if you have not given an explicit SET STOP-BITS or SET SERIAL command. The \v(serial) variable shows the SET SERIAL setting (8N1, 7E1, etc). 2.11. Getting Access to the Dialout Device This section is for UNIX only; note the special words about QNX at the end. Also see [426]Section 2.0 for SET LINE switches, particularly the /SHARE switch for VMS only. C-Kermit does its best to obey the UUCP lockfile conventions of each platform (machine, operating system, OS version) where it runs, if that platform uses UUCP. But simply obeying the conventions is often not good enough, due to the increasing likelihood that a particular serial device might have more than one name (e.g. /dev/tty00 and /dev/term/00 are the same device in Unixware 7; /dev/cua and /dev/cufa are the same device in NeXTSTEP), plus the increasingly widespread use of symlinks for device names, such as /dev/modem. C-Kermit 7.0 goes to greater lengths than previous versions to successfully interlock with other communications program (and other instances of Kermit itself); for example, by: * Creation of dual lockfiles whenever a symlink is used; one for the link name and one for the real name. * Creation of dual lockfiles in HP-UX according to HP rules. * Creation of dual uppercase/lowercase lockfile names in SCO UNIX/ODT/OSR5. * The use of ttylock() in versions of AIX where it works. * The use, wherever possible, of lockfile names based on inode/major/minor device number rather than device name. See the [427]ckuins.txt and [428]ckubwr.txt files for details. QNX is almost unique among UNIX varieties in having no UUCP programs nor UUCP-oriented dialout-device locking conventions. QNX does, however, allow a program to get the device open count. This can not be a reliable form of locking unless all applications do it (and they don't), so by default, Kermit uses this information only for printing a warning message such as: C-Kermit>set line /dev/ser1 WARNING - "/dev/ser1" looks busy... However, if you want to use it as a lock, you can do so with: SET QNX-PORT-LOCK { ON, OFF } QNX-PORT-LOCK is OFF by default; if you set in ON, C-Kermit fails to open any dialout device when its open count indicates that another process has it open. SHOW COMM (in QNX only) displays the setting, and if you have a port open, it also shows the current open count (with C-Kermit's own access always counting as 1). 2.12. The Connection Log C-Kermit 7.0 adds the ability to log connections, so you can see where you've been and have a record of calls you've made. A connection is defined as any communications session that is begun by SET LINE, SET PORT, DIAL, SET HOST, TELNET, or RLOGIN. Connections are not logged unless you request it; the command is: LOG CX [ filename [ { NEW, APPEND } ] ] Enables logging of connections in the given file. If the trailing { NEW, APPEND } keyword is omitted, the file is opened for appending; i.e. new records are written to the end. If NEW is specified, a new file is created; if a file of the same name already existed, it is overwritten. If the filename is omitted, CX.LOG in your home (login) directory is used (note: uppercase). To accept all defaults, just use "log connections" (or "l c" for short). Synonym: LOG CONNECTIONS. CLOSE CX-LOG This closes the connection log if it was open. (Note, the CLOSE CONNECTION command closes the connection itself). SHOW CX This shows your current connection, if any, including the elapsed time (since you opened it). Synonym: SHOW CONNECTION. \v(cx_time) This variable shows the elapsed time of your current connection, or if there is no current connection, of your most recent connection, of if there have been no connections, 0. The connection contains one line per connection, of the form: yyyymmdd hh:mm:ss username pid p=v [ p=v [ ... ] ] where the timestamp (in columns 1-18) shows when the connection was made; username is the login identity of the person who made the connection; pid is Kermit's process ID when it made the connection. The p's are parameters that depend on the type of connection, and the v's are their values: T = Connection Type (TCP, SERIAL, DIAL, DECNET, etc). H = The name of the Host from which the connection was made. N = Destination phone Number or Network host name or address. D = Serial connections only: Device name. O = Dialed calls only: Originating country code & area code if known. E = Elapsed time in hh:mm:ss format (or hhh:mm:ss, etc). If you always want to keep a connection log, simply add: log connections to your C-Kermit customization file. Note, however, that if you make a lot of connections, your CX.LOG will grow and grow. You can handle this by adding a "logrotate" procedure like the following to your customization file, before the "log connections" command: define LOGROTATE { ; Define LOGROTATE macro local \%i \%m \%d \%n \%f MAX def MAX 4 ; How many months to keep if not def \%1 - ; No argument given end 1 \%0: No filename given if not = 1 \ffiles(\%1) - ; Exactly 1 file must match end 1 \%0: \%1 - File not found .\%d := \fsubstr(\fdate(\%1),1,6) ; Arg OK - get file year & month if = \%d - ; Compare file year & month \fsubstr(\v(ndate),1,6) - ; with current year & month end 0 ; Same year & month - done rename \%1 \%1.\%d ; Different - rename file .\%n := \ffiles(\%1.*) ; How many old files if < \%n \m(MAX) end 0 ; Not enough to rotate .\%m := \%1.999999 ; Initial compare string for \%i 1 \%n 1 { ; Loop thru old logs .\%f := \fnextfile() ; Get next file name if llt \%f \%m .\%m := \%f ; If this one older remember it } delete \%m ; Delete the oldest one } log connections ; Now open the (possibly new) log logrotate \v(home)CX.LOG ; Run the LOGROTATE macro As you can see, this compares the yyyymm portion of the modification date (\fdate()) of the given file (\%1) with the current yyyymm. If they differ, the current file has the yyyymm suffix (from its most recent modification date) appended to its name. Then we search through all other such files, find the oldest one, and delete it. Thus the current log, plus the logs from the most recent four months, are kept. This is all done automatically every time you start C-Kermit. On multiuser systems, it is possible to keep a single, shared, system-wide connection log, but this is not recommended since (a) it requires you keep a publicly write-accessible logfile (a glaring target for mischief), and (b) it would require each user to log to that file and not disable logging. A better method for logging connections, in UNIX at least, is syslogging (see [429]ckuins.txt Section 15 and [430]Section 4.2 of the [431]IKSD Administration Guide for details). 2.13. Automatic Connection-Specific Flow Control Selection Beginning in C-Kermit 7.0, the appropriate flow-control method for each connection type is kept in a table, for example: Remote: NONE Modem: RTS/CTS Direct-Serial: NONE TCPIP: NONE The size of the table and values for each connection type can vary from platform to platform. Type "set flow ?" for a list of available flow-control types. The table is used to automatically select the appropriate kind of flow control whenever you make a connection. You can display the table with: SHOW FLOW-CONTROL The defaults are as follows: Remote: NONE or XON/XOFF. This is because C-Kermit is not allowed to find out what type of connection the incoming user has (*). No kind of flow control will work on every kind of connection, including (unexpectedly) KEEP, which we would have liked to use, but not turning off flow control at the remote end during file transfer on TCP/IP connections is fatal to the transfer (except in VMS and HP-UX, where it must be set to Xon/Xoff!) Therefore if you are dialing in to a serial port on a server (UNIX or VMS) where C-Kermit is running, you will need to tell C-Kermit to "set flow keep" before transferring files (assuming the modem and port are configured correctly by the system administrator; otherwise you'll need to give a specific kind of flow control, e.g. "set flow xon/xoff"), so in this case C-Kermit will not disable flow control, as it must do when you are coming via Telnet (directly or through a terminal server, except on VMS and HP-UX). Modem: This applies when you dial out with a modem. In this case, the MODEM FLOW-CONTROL setting takes affect after the SET FLOW setting, so it can pick the most appropriate flow control for the combination of the particular modem and the computer/port/driver that is dialing. Direct-Serial: The default here is NONE because C-Kermit has no way of knowing what kind of flow control, if any, is or can be done by the device at the other end of the connection. RTS/CTS would be a bad choice here, because if the CTS signal is not asserted, the connection will hang. And since direct connections are often made with 3-wire cables, there is a good chance the CTS signal will not be received. TCPIP: NONE, since TCP and IP provide their own flow control transparently to the application, except in VMS, where Xon/Xoff is the default due to the requirements of the VMS TCP/IP products. Other networks: NONE, since networks should provide their flow control transparently to the application. (*) This is possibly the worst feature of UNIX, VMS, and other platforms where C-Kermit runs. If C-Kermit was able to ask the operating system what kind of connection it had to the user, it could set up many things for you automatically. You can modify the default-flow-control table with: SET FLOW-CONTROL /xxx { NONE, KEEP, RTS/CTS, XON/XOFF, ... } where "xxx" is the connection type, e.g. SET FLOW /REMOTE NONE SET FLOW /DIRECT RTS/CTS If you leave out the switch, SET FLOW works as before, choosing the flow control method to be used on the current connection: SET FLOW XON/XOFF Thus, whenever you make a connection with SET PORT, SET LINE, DIAL, SET HOST, TELNET, RLOGIN, etc, an appropriate form of flow control is selected automatically. You can override the automatic selection with a subsequent SET FLOW command, such as SET FLOW NONE (no switch included). The flow control is changed automatically too when you give a SET MODEM TYPE command. For example, suppose your operating system (say Linux) supports hardware flow control (RTS/CTS). Now suppose you give the following commands: set line /dev/ttyS2 ; Automatically sets flow to NONE set modem type usr ; Automatically sets flow to RTS/CTS set modem type rolm ; Doesn't support RTS/CTS so now flow is XON/XOFF IMPORTANT: This new feature tends to make the order of SET LINE/HOST and SET FLOW commands matter, where it didn't before. For example, in VMS: SET FLOW KEEP SET LINE TTA0: the SET LINE undoes the SET FLOW KEEP command; the sequence now must be: SET FLOW /DIRECT KEEP SET LINE TTA0: or: SET LINE TTA0: SET FLOW KEEP 2.14. Trapping Connection Establishment and Loss If you define a macro called ON_OPEN, it is executed any time that a SET LINE, SET PORT, SET HOST, TELNET, RLOGIN or similar command succeeds in opening a connection. The argument is the host or device name (as shown by SHOW COMMUNICATIONS, and the same as \v(line)). This macro can be used for all sorts of things, like automatically setting connection- or host-specific parameters when the connection is opened. Example: def ON_OPEN { switch \%1 { :abccorp.com, set reliable off, break :xyzcorp.com, set receive packet-length 1000, break etc etc... } } If you define a macro called ON_CLOSE, it will be executed any time that a SET LINE, SET PORT, SET HOST, TELNET, RLOGIN or any other kind of connection that C-Kermit has made is closed, either by the remote or by a local CLOSE, HANGUP, or EXIT command or other local action, such as when a new connection is opened before an old one was explicitly closed. As soon as C-Kermit notices the connection has been closed, the ON_CLOSE macro is invoked at (a) the top of the command parsing loop, or (b) when a connection is closed implicitly by a command such as SET LINE that closes any open connection prior to making a new connection, or (c) when C-Kermit closes an open connection in the act of exiting. The ON_CLOSE macro was inspired by the neverending quest to unite Kermit and SSH. In this case using the "tunnel" mechanism: def TUNNEL { ; \%1 = host to tunnel to local \%p if not def \%1 stop 1 assign tunnelhost \%1 ; Make global copy undef on_close set macro error off close connection ; Ignore any error open !read tunnel start \%1 read \%p ; Get port number if fail stop 1 Tunnel failure: \%1 close read if fail stop 1 Tunnel failure: \%1 ; See [432]Section 4.2.8.1 assign on_close { ; Set up close handler echo Closing tunnel: \m(tunnelhost) !tunnel stop \m(tunnelhost) undef on_close } set host localhost:\%p /telnet if success end 0 undef on_close stop 1 Connection failure: \%1 } In this case, when the connection stops, we also need to shut down the tunnel, even if it is at a later time after TUNNEL has finished executing. This way we can escape back, reconnect, transfer files, and so on until the connection is broken by logging out from the remote, or by explicitly closing it, or by EXITing from C-Kermit, at which time the tunnel is shut down. When the connection is closed, no matter how, the ON_CLOSE macro executes and then undefines (destroys) itself, since we don't want to be closing tunnels in the future when we close subsequent connections. Other such tricks can be imagined, including ending ON_CLOSE with a STOP command to force the command stack to be peeled all the way back to the top, for example in a deeply nested script that depends on the connection being open: def on_close { stop 1 CONNECTION LOST } When C-Kermit invokes the ON_CLOSE macro, it supplies one argument (\%1): the reason the connection was closed as a number, one of the following: 2 - Fatal failure to negotiate a required item on a network connection. 1 - Closed by C-Kermit command. 0 - All others (normally closed by remote). which may be used for any purpose; for example, to add a comment to the connection log: def on_close { local \%m if not open cx end 0 switch \%1 { :0, .\%m = Closed by remote, break :1, .\%m = Closed by me, break :2, .\%m = Network protocol negotiation failure, break } if def \%m writeln cx {# \%m} } 2.15. Contacting Web Servers with the HTTP Command C-Kermit 7.0 (at this writing, the UNIX version only) supports direct contact and interaction with Web servers via HTTP 1.0 protocol. To make a connection, use Kermit's normal method for making a TCP/IP connection, but specify the HTTP port: SET HOST host http [ switches ] where host is the IP hostname or address, and http is the name of the TCP port for the Web server. Relevant switches include: /RAW Treat the connection as a transparent binary pipe. This switch may be required if a port other than 'http' is used. /SSL Make an secure private connection with SSL (only if SSL support is included in your version of Kermit). In this case the port name might need to be https rather than http, e.g. "set host secureserver.xyzcorp.com https /ssl". /TLS Make an secure private connection with TLS (only if TLS support is included in your version of Kermit). In this case the port name would be https rather than http. Then you can issue an HTTP command. In most cases, the server closes the connection when the command is complete. Example: SET HOST www.columbia.edu http IF FAIL EXIT 1 Can't contact server HTTP GET kermit/index.html At this point the connection is closed, since that's how HTTP 1.0 works. If you want to perform additional operations, you must establish a new connection with another SET HOST command. The HTTP command acts as a client to the Web server, except instead of displaying the results like a Web browser would, it stores them. Any HTTP command can (but need not) include any or all of the following switches: /AGENT:user-agent Identifies the client to the server; "C-Kermit" or "Kermit-95" by default. /HEADER:header-line Used for specifying any optional headers. A list of headers is provided using braces for grouping: /HEADER:{{tag:value}{tag:value}...} For a listing of valid tag value and value formats see [433]RFC 1945: Hypertext Transfer Protocol -- HTTP/1.0. A maximum of eight headers may be specified. /USER:name In case a page requires a username for access. /PASSWORD:password In case a page requires a password for access. /ARRAY:arrayname Tells Kermit to store the response headers in the given array, one line per element. The array need not be declared in advance. Example: C-Kermit? http /array:c get kermit/index.html C-Kermit? show array c Dimension = 9 1. Date: Fri, 26 Nov 1999 23:12:22 GMT 2. Server: Apache/1.3.4 (Unix) 3. Last-Modified: Mon, 06 Sep 1999 22:35:58 GMT 4. ETag: "bc049-f72-37d441ce" 5. Accept-Ranges: bytes 6. Content-Length: 3954 7. Connection: close 8. Content-Type: text/html As you can see, the header lines are like MIME e-mail header lines: identifier, colon, value. The /ARRAY switch is the only method available to a script to process the server responses for a POST or PUT command. The HTTP commands are: HTTP [ switches ] GET remote-filename [ local-filename ] Retrieves the named file. If a local-filename is given, the file is stored locally under that name; otherwise it is stored with its own name. HTTP [ switches ] HEAD remote-filename local-filename Like GET except without actually getting the file; instead it gets only the headers, storing them into the given file, whose name must be given, one line per header item, as shown above in the /ARRAY: switch description. HTTP [ switches ] INDEX remote-directory [ local-filename ] Retrieves the file listing for the given server directory. NOTE: This command is not supported by most Web servers. HTTP [ switches ] POST [ /MIME-TYPE:type ] local-file remote-file Used to send a response as if it were sent from a form. The data to be posted must be read from a file. HTTP [ switches ] PUT [ /MIME-TYPE:type ] local-file remote-file Uploads a local file to a server file. HTTP [ switches ] DELETE remote-filename Instructs the server to delete the specified filename. 3. TERMINAL CONNECTION 3.1. CONNECT Command Switches The following switches (see [434]Section 1.5) were added to the CONNECT command in 7.0: /QUIETLY Don't print the "Connecting to..." or "Back at..." messages. CQ is an invisible command synonym for CONNECT /QUIETLY. /TRIGGER:string Specify a trigger or triggers ([435]Section 3.2) effective for this CONNECT command only, temporarily overriding any current SET TERMINAL TRIGGER values ([436]Section 3.2). Note: Other switches might also be available; type "connect ?" for a list, "help connect" for a description of each. 3.2. Triggers Triggers were added for UNIX, VMS, AOS/VS, and K95 in C-Kermit 7.0. SET TERMINAL TRIGGER string Tells C-Kermit to look for the given string during all subsequent CONNECT sessions, and if seen, to return to command mode automatically, as if you had escaped back manually. If the string includes any spaces, you must enclose it in braces. Example: set terminal trigger {NO CARRIER} Comparisons are made after character-set translation. If a string is to include a literal brace character, precede it with a backslash: ; My modem always makes this noise when the connection is lost: set terminal trigger |||ppp\{\{\{\{UUUUUUU If you want Kermit to look for more than one string simultaneously, use the following syntax: set terminal trigger {{string1}{string2}...{stringn}} In this case, C-Kermit will return to command mode automatically if any of the given strings is encountered. Up to 8 strings may be specified. If the most recent return to command mode was caused by a trigger, the new variable, \v(trigger), shows the trigger value; otherwise \v(trigger) is empty. The SHOW TRIGGER command displays the SET TERMINAL TRIGGER values as well as the \v(trigger) value. 3.3. Transparent Printing As noted in the manual, C-Kermit's CONNECT command on UNIX is not a terminal emulator, but rather a "semitransparent pipe" between the terminal or emulator you are using to access C-Kermit, and the remote host to which C-Kermit is connected. The "semitransparent" qualifier is because of character-set translation as well as several actions taken by the emulator in response to the characters or strings that pass through it, such as APCs, Kermit packets (autodownload), triggers, etc. The UNIX version of C-Kermit 7.0 adds another such action: Transparent printing, also called Controller printing (as distinct from Autoprint or line or screen print). It is intended mainly for use on UNIX workstation consoles (as opposed to remote logins), but with some care can also be used when accessing C-Kermit remotely. Transparent printing is related to APC by sharing C-Kermit's built-in ANSI escape-sequence parser to detect "printer on" and "printer off" sequences from the host. When the printer-on sequence is received, all subsequent arriving characters -- including NUL, control characters, and escape sequences -- are sent to the SET PRINTER device instead of to your screen until the printer-off sequence is received, or you escape back, whichever happens first. These bytes are not translated or modified or filtered in any way by Kermit (except for possibly stripping of the 8th bit, as noted below), but if filtering or translation is desired, this can be accomplished by your SET PRINTER selection (e.g. by choosing a pipeline of filters). By default, your SET PRINTER device is your default UNIX printer, but it can also be a file, a command, or the null device (which causes all printer material to be discarded). See [437]Using C-Kermit, 2nd Ed., p.41 for details. Transparent printing is controlled by the command: SET TERMINAL PRINT { ON, OFF } When ON, transparent-print sequences are obeyed, and printing occurs on the system where C-Kermit is running. When OFF, transparent print sequences are ignored and passed through to your actual terminal or emulator, along with the data they enclose. OFF is the default, for compatibility with earlier C-Kermit releases. As noted in the manual, when the current SET PRINTER device is a file, transparent-print material is appended to it; the file is not overwritten. SET TERMINAL BYTESIZE { 7, 8 } SET PARITY { EVEN, ODD, MARK, SPACE, NONE } If the terminal bytesize is 7, or PARITY is not NONE, the 8th bit of each byte is stripped prior to printing. The transparent-print escape sequences are: [5i Printer On. Send all subsequent incoming bytes to the printer without any kind of filtering, translation, or alteration. Note: stands for ASCII character number 27 (decimal), Escape. [4i Printer Off. Resume displaying incoming bytes on the screen. These are the same sequences used by DEC VT100 and higher terminals and other ANSI X3.64 and ISO 6429 compatible terminals. There is no provision for selecting other printer-control sequences. Restrictions: 1. You must SET TERM TRANSPARENT-PRINT ON before you can use this feature. 2. Only the 7-bit forms of the escape sequences are supported. The 8-bit CSI C1 control is not recognized. 3. Autoprint is not supported, since this requires a full-fledged terminal emulator with direct access to the screen. 4. The start-print and stop-print sequences pass through to the screen (there is no way to avoid this without causing unacceptable delays or deadlocks in CONNECT mode). Thus if your terminal or emulator also supports transparent printing via these same sequences, an empty file will be sent to its printer. Normally this has no effect. Point (4) is similar to the situation with autodownload and APC -- when you have several Kermit clients in a chain, you should take care that these features are enabled in only one of them. Example 1: set printer {|lpr -Plaser} ; Specify the printer (if not default). set term print on ; Enable transparent printing. set term byte 8 ; Enable 8-bit characters. connect ; Enter CONNECT mode. Example 2: set printer /home/users/olga/printer.log ; Send printer material to a file. Example 3: set printer {| grep -v ^Received | lpr} ; Filter out some lines Then use "pcprint" or "vtprint" commands on the host to initiate transparent print operations. See [438]Using C-Kermit, 2nd Ed., p.406 for details. Here is a sample "pcprint" shell script for UNIX: #!/bin/sh echo -n '[5i' if [ $# -eq 0 ]; then cat else cat $* fi echo -n '[4i' # (end) (Replace "" by the actual ASCII Escape character and "" by the ASCII Formfeed character). If you always want transparent printing enabled, put "set term print on" in your C-Kermit customization file (~/.mykermrc in UNIX). The "set term bytesize" selection, however, is a property of each separate connection. 3.4. Binary and Text Session Logs C-Kermit 7.0 corrects an oversight in earlier releases, in which binary session logs (SET SESSION-LOG BINARY) translated character sets and performed various formatting transformations (e.g. "newline mode") before writing characters to the session log. In C-Kermit 7.0, binary-mode session logging writes characters as they come in, before anything (other that parity-bit stripping) is done to them. Text-mode session logging records the characters after processing. 4. FILE TRANSFER Every file is transferred either in text mode (which implies record-format and character-set translation) or binary mode (in which each byte is sent literally without any kind of conversion). The mode in which a file is transferred is controlled by (a) the default mode, in the absence of any other indications; (b) the SET FILE TYPE command; (c) various automatic mechanisms based on client/server negotiations, directory information or filename patterns, etc. The default FILE TYPE was changed from TEXT to BINARY in C-Kermit 7.0 because: * Transferring a text file in binary mode does less damage than transferring a binary file in text mode. * Only binary-mode transfers can be recovered from the point of failure. * The automatic transfer-mode mechanisms switch to text mode on a per-file basis anyway, so only those files that are not covered by the automatic mechanisms are affected. * All file transfers on the Web are done in binary mode, so people are accustomed to it and expect it. 4.0. BUG FIXES, MINOR CHANGES, AND CLARIFICATIONS 4.0.0. Filenames with Spaces Filenames that contain spaces are a major nuisance to a program like Kermit, whose command language is line- and word-oriented, in which words are separated by spaces and a filename is assumed to be a "word". In general (unless noted otherwise in the description of a particular command), there is only one way to refer to such files in Kermit commands, and that is to enclose the name in braces: send {this file} Tells Kermit to send the file whose name is "this file" (two words, no quotes). Of course, various circumlocutions are also possible, such as: define \%a this file send \%a BUT, perhaps contrary to expectation, you can't use "\32" to represent the space: send this\32file does not work. Why? Because the Kermit parser, which must work on many operating systems including Windows, has no way of knowing what you mean by "this\32file". Do you mean a file whose name is "this file" in the current directory? Or do you mean a file whose name is "32file" in the "this" subdirectory of the current directory? Guessing won't do here; Kermit must behave consistently and deterministically in all cases on all platforms. Note that you can't use Esc or Tab within {...} for filename completion, or question mark to get a filename list. However, you can include wildcards; for example: send {* *} sends all files whose name contains a space. All things considered, it is best to avoid spaces in file and directory names if you can. Also see [439]Section 5.4 on this topic. 4.0.1. Packet out of Window C-Kermit 6.0 could send packets "out of window" if the window size was greater than 1 and ACKs had arrived out of order. Fixed in 6.1. 4.0.2. MOVE after ADD SEND-LIST ADD SEND-LIST followed by MOVE did not delete original files; fixed in 6.1. Carrier loss was not detected during transfer; in 7.0 C-Kermit checks for this (but results can not be guaranteed). In any case, the protocol will eventually time out if the connection is lost. 4.0.3. GET and RECEIVE As-Names In 5A(190) through 6.0.192, the GET and RECEIVE as-name did not properly override the RECEIVE PATHNAMES setting. In 7.0 it does. 4.0.4. New Brief Statistics Listing Version 7.0 adds a /BRIEF switch to the STATISTICS command, to display a short file-transfer statistics report. /BRIEF is now the default. Use /VERBOSE to see the full display, which is about 25 lines long. 4.0.5. Improved FAST Command The preinstalled definition of the FAST macro did not take enough factors into account. Now it sets packet lengths and window sizes appropriate to the configuration. Furthermore, in IRIX only, it might restrict the SEND packet length to 4000, to work around a bug in the IRIX Telnet server, depending on the IRIX version (see [440]ckubwr.txt, IRIX section). To see the built-in definition of the FAST macro, type "show macro fast". To change it, simply define it to be whatever you want -- it's just a macro, like any other. 4.0.6. The SET SEND BACKUP Command Version 7.0 adds SET SEND BACKUP { ON, OFF }. This tells whether backup files should be sent. Backup files are the ones created by Kermit (and EMACS, and possibly other applications) to preserve old copies of files when creating new ones with the same name. Kermit does this when receiving a file and its FILE COLLISION setting is BACKUP (or RENAME, in which case it the new file gets the backup name). On most platforms, the backup name is formed by adding: .~n~ to the end of the filename, where "n" is a number. For example, if the original file is oofa.txt, a backup file might be called: oofa.txt.~1~ (or oofa.txt.~2~, etc). If you SET SEND BACKUP OFF, this tells Kermit not to send files that have backup names. Normally, SET SEND BACKUP is ON (as shown by SHOW PROTOCOL), and backup files are sent if their names match the SEND file specification. Also see PURGE, SET FILE COLLISION, SEND /NOBACKUP, DIRECTORY /[NO]BACKUP. 4.0.7. The SET { SEND, RECEIVE } VERSION-NUMBERS Command VMS Only. Normally when sending files, VMS C-Kermit strips the version number. For example, if the file is FOO.BAR;34, the name is sent as FOO.BAR (without the ";34"). If you want to keep version numbers on when sending files, use SET SEND VERSION-NUMBERS ON. The effect depends on the receiver. Normally when receiving files, and an incoming filename includes a VMS-style version number (such as FOO.BAR;34) VMS C-Kermit strips it before trying to create the new file; this way the new file receives the next highest version number in the customary manner for VMS. If you want version numbers on incoming filenames to be used in creating the new files, use SET RECEIVE VERSION-NUMBERS ON. Normally these commands would be effective only when VMS C-Kermit is exchanging files with a non-VMS Kermit program, since VMS-to-VMS transfers use labeled mode unless you have gone out of your way to defeat it. Example: You want to send all versions of all files in the current directory from a VMS C-Kermit client to a UNIX C-Kermit server. Use: set send version-numbers on send *.*;* The resulting Unix files will have VMS-style version numbers as part of their name, for example "foo.bar;1", "foo.bar;2", etc. Now suppose you want to send these files from Unix to another VMS system and preserve the version numbers. Again we have a Unix C-Kermit server and VMS C-Kermit client. Give these commands to the client: set receive version-numbers on get * 4.0.8. The SET { SEND, RECEIVE } { MOVE-TO, RENAME-TO } Commands These commands are persistent global versions of the /MOVE-TO: and /RENAME-TO: switches of the SEND, GET, and RECEIVE commands. They should normally be used only when setting up a dedicated transaction-processing application, in which each file is to be moved or renamed immediately after, and only if, it is transferred successfully, so that (for example) an independent, concurrent process can notice when new files appear and process them immediately without having to guess whether they are complete. 4.0.9. SET FILE INCOMPLETE AUTO SET FILE INCOMPLETE { KEEP, DISCARD }, which tells whether to keep or discard incompletely received files, has a new option, AUTO, which is also the default. It means KEEP the incomplete file if the transfer is in binary mode, otherwise DISCARD it. This reduces the chances that a subsequent recovery operation (RESEND, REGET, etc) could produce a corrupt file, since recovery works only for binary-mode transfers. 4.1. FILE-TRANSFER FILENAME TEMPLATES File-transfer filename templates allow files to be renamed automatically by the file sender, the receiver, or both, during transfer of groups of files. 4.1.1. Templates in the As-Name Prior to C-Kermit 6.1 and Kermit 95 1.1.12 the only options that could be used to affect the names of files being transferred were SET FILENAMES { LITERAL, CONVERTED } and SET { SEND, RECEIVE } PATHNAMES { ON, OFF }, plus the "as-name" feature of the SEND (MOVE, etc) and RECEIVE commands. Previously, the as-name could be used only for a single file. For example: SEND FOO BAR would send the file FOO under the name BAR, but: SEND *.TXT anything was not allowed, since it would give the same name to each file that was sent. When receiving: RECEIVE FOO would rename the first incoming file to FOO before storing it on the disk, but subsequent files would not be renamed to FOO, since this would result in overwriting the same file repeatedly. Instead, they were stored under the names they arrived with. Beginning in C-Kermit 6.1 and Kermit 95 1.1.12, it is possible to specify as-names in SEND, RECEIVE, and related commands even for file groups. This is accomplished by using replacement variables in the as-name, along with optional material such character-string functions and/or constant strings. An as-name containing replacement variables is called a filename template. The key to filename templates is the new variable: \v(filename) During file transfer it is replaced by the name of each file currently being transferred (after transfer, it is the name of the last file transferred). So, for example: send *.txt \v(filename).new sends each file with its own name, but with ".new" appended to it. Of course if the name already contains periods, this could confuse the file receiver, so you can also achieve fancier effects with constructions like: send *.txt \freplace(\v(filename),.,_).new which replaces all periods in the original filename by underscores, and then appends ".new" to the result. So, for example, oofa.txt would be sent as oofa_txt.new. Another new variable that is useful in this regard is \v(filenumber), which is the ordinal number of the current file in the file group, so you can also: send *.txt FILE\flpad(\v(filenum),2,0) resulting in a series of files called FILE00, FILE01, FILE02, etc. (At the end of the transfer, \v(filenum) tells the number of files that were transferred). If you specify a constant as-name when sending a file group: send *.txt thisnameonly Kermit complains and asks you to include replacement variables in the as-name. You should generally use \v(filename) or \v(filenumber) for this purpose, since other variables (with the possible exception of date/time related variables) do not change from one file to the next. But Kermit accepts any as-name at all that contains any kind of variables for file group, even if the variable will not change. So: send *.txt \%a is accepted, but all files are sent with the same name (the value of \%a, if it has one and it is constant). If the variable has no value at all, the files are sent under their own names. Of course, the value of \%a in the previous example need not be constant: define \%a FILE\flpad(\v(filenum),2,0)_at_\v(time) send *.txt \%a The RECEIVE command, when given without an as-name, behaves as always, storing all incoming files under the names they arrive with, subject to SET FILE NAME and SET RECEIVE PATHNAMES modifications ([441]Section 4.10). However, when an as-name is given in the RECEIVE command, it is applied to all incoming files rather than to just the first. If it does not contain replacement variables, then the current FILE COLLISION setting governs the result. For example: receive foo will result in incoming files named foo, foo.~1~, foo.~2~, and so on, with the default FILE COLLISION setting of BACKUP. If it does contain replacement variables, of course they are used. When receiving files, the \v(filename) variable refers to the name that was received in the incoming file-header packet, BEFORE any processing by SET FILE NAMES or SET RECEIVE PATHNAMES. Since the filenames in file-header packets are usually in uppercase, you would need to convert them explicitly if you want them in lowercase, e.g.: receive \flower(\v(filename)).new 4.1.2. Templates on the Command Line On the command-line, use templates as shown above as the -a option argument, bearing in mind the propensity of UNIX and perhaps other shells to treat backslash as a shell escape character. So in UNIX (for example): kermit -s oofa.* -a x.\\v(filenum) By the way, this represents a change from 6.0 and earlier releases in which the as-name (-a argument or otherwise) was not evaluated by the command parser. Thus, for example, in VMS (where the shell does not care about backslashes), it was possible to: kermit -s oofa.txt -a c:\tmp\oofa.txt Now backslashes in the as-name must be quoted not only for the shell (if necessary) but also for Kermit itself: kermit -s oofa.txt -a c:\\tmp\\oofa.txt ; Kermit only kermit -s oofa.txt -a c:\\\\tmp\\\\oofa.txt ; Shell and Kermit You can also use the \fliteral() function for this: kermit -s oofa.txt -a \fliteral(c:\tmp\oofa.txt) ; Kermit only kermit -s oofa.txt -a \\fliteral(c:\\tmp\\oofa.txt) ; Shell and Kermit 4.1.3. Post-Transfer Renaming Filename templates are now also useful in SET { SEND, RECEIVE } RENAME-TO and in the /RENAME-TO: switch, that can be given to the SEND, GET, or RECEIVE commands; this is similar to an as-name, but is effective on a per-file basis if and only if the file was transferred successfully. MOVE-TO and RENAME-TO address a requirement commonly stated for transaction processing and similar systems. Suppose, for example, a central system "X" accepts connections from multiple clients simultaneously; a process on X waits for a file to appear and then processes the file. This process must have a way of knowing when the file has been completely and successfully transferred before it starts to process it. This can be accomplished easily using C-Kermit's SET { SEND, RECEIVE } { MOVE-TO, RENAME-TO } command or /MOVE-TO: or /RENAME-TO: switches, described in [442]Sections 4.7.1 through [443]4.7.3. Here's an example for the client side, in which files to be sent are placed in a certain directory (/usr/olga/tosend in this example) by another process when they are ready to go. This might be in a hospital or big doctor's office, where medical insurance claims are entered at a number of workstations, and then deposited in the "tosend" directory, from which they are sent to a claims clearinghouse. We assume the connection is already made and a Kermit server is on the other end. local srcdir findir ; Declare local (automatic) variables assign srcdir /usr/olga/tosend ; Local source directory (files to send) assign findir /usr/olga/sent ; Where to move files after they are sent log transactions ; Keep a log of transfers cd \m(srcdir) ; Change to the source directory while true { ; Loop forever... send /move-to:\m(findir) * ; Send all files sleep 60 ; Sleep a minute } ; Go back and do it again Note how simple this is. Once each file is sent, it is moved so it won't be sent again (you could also use SEND /RENAME-TO: or even SEND /DELETE). If a transfer fails, the file is not moved and so we try again to send it next time around. If there are no files to send, the SEND command does nothing but a message is printed; you can avoid the message by checking first to see if any files are in the directory: while true { ; Loop forever... if > \ffiles(*) 0 - ; If there are any files send /move-to:\m(findir) * ; send them. sleep 60 ; Sleep a minute. } ; Go back and do it again. It's even simpler on the server side (here again we assume the connection is already in place): local rcvdir findir ; Declare local (automatic) variables assign rcvdir /usr/ivan/tmp ; Temporary receiving directory assign findir /usr/ivan/new ; Where to move files after reception log transactions ; Keep a log of transfers cd \m(rcvdir) ; Change to the source directory set receive move-to \m(findir) ; Declare move-to directory. server ; Enter server mode. A separate process (e.g. the medical claim-form decoder) can look for files appearing in the /usr/ivan/new directory and process them with every confidence that they have been completely received. Note that the use of MOVE-TO can result in moved files overwriting one another (the application would normally avoid this by assigning each transaction a unique, e.g. based on customer number and claim number). But if filename collisions are a possibility in your application, RENAME-TO might be a better choice; you can use any variables you like in the template to ensure uniqueness of the RENAME-TO filename; for example: SET RECEIVE RENAME-TO \v(filename)_\v(ndate)_\v(ntime)_\v(userid)_\v(pid) 4.2. FILE-TRANSFER PIPES AND FILTERS 4.2.1. INTRODUCTION Beginning in C-Kermit 6.1 and Kermit 95 1.1.12, it is possible to send from a command, or "pipe", as well as from a file, and to receive to a pipe or command. In a typical example, we might want to transfer an entire directory tree from one UNIX system to another (but without using the methods described in [444]Sections 4.3 , [445]4.10, [446]4.11, and [447]4.15). We could do this in multiple steps as follows: 1. Create a tar archive of the desired directory tree 2. Compress the tar archive 3. Transfer it in binary mode to the other computer 4. Decompress it 5. Extract the directory tree from the tar archive But this is inconvenient and it requires a temporary file, which might be larger than we have room for. The new pipe-transfer feature lets you do such things in a single step, and without intermediate files. Additional new features, also discussed here, let you specify pre- and post- processing filters for outbound and incoming files, and give you a way to insert the output from shell or system commands into C-Kermit commands. The file-transfer related features are available only with Kermit protocol, not with any external protocols, nor with K95's built-in XYZMODEM protocols (because XYZMODEM recovers from transmission errors by rewinding the source file, and you can't rewind a pipe). This section begins by discussing the simple and straightforward use of these features in UNIX, in which pipes and input/output redirection are a fundamental component and therefore "just work", and then goes on to discuss their operation in Windows and OS/2, where matters are much more complicated. 4.2.1.1. TERMINOLOGY Standard Input This is a precise technical term denoting the normal source of input for a command or program, which is the keyboard of your terminal by default, but which can be redirected to a file or pipe. Stdin Abbreviation for Standard Input. Standard Output A precise technical term denoting the normal destination for output from a command or program, which is your terminal screen by default, but which can be redirected to a file. Stdout Abbreviation for Standard Output. Stdio Abbreviation for Standard Input / Standard Output. I/O Abbreviation for Input / Output. Shell Text-based system command processor, such as the UNIX shell, DOS COMMAND.COM, etc. Pipe A mechanism by which the standard output of one program is sent to the standard input of another. Pipeline A series of programs connected by pipes. 4.2.1.2. NOTATION In command descriptions, "command" is replaced by a shell or system command or pipeline. The command names specified in these commands are interpreted by your shell, just as if you were typing them at the shell prompt, and so if they are in your PATH, they will be found in the expected manner. Therefore you don't have to specify complete pathnames for commands that are programs (but it shouldn't hurt if you do). The normal notation for I/O redirection is as follows: < Read Stdin from the given file. > Send Stdout to the given file. | Send Stdout from the command on the left to the command on the right. Examples: sort < foo > bar Sorts the lines in file "foo" and writes the results to file "bar" grep -c "some text" *.txt | grep -v ":0" | sort | pr -3 | lpr This is a command pipeline composed of 5 commands: grep -c "some text" *.txt Looks in all files whose names end with ".txt" for the string "some text" and writes to Stdout the names of each file followed by a colon and the number of occurrences in each. grep -v ":0" Prints to Stdout the lines from Stdin that do NOT contain the string ":0", in this case, it removes the names of files that do not contain "some text". sort Sorts the lines from Stdin alphabetically to Stdout. pr -3 Arranges the lines from Stdin in three columns. lpr Prints its Stdin on the default printer. Note that the Kermit features described here work only with commands that use Stdio. If you attempt to use them with commands whose input and output can not be redirected, Kermit will most likely get stuck. Kermit has no way of telling how an external command works, nor what the syntax of the shell is, so it's up to you to make sure you use these features only with redirectable commands. The quoting rules of your shell apply to the command. Thus in UNIX, where C-Kermit tries to use your preferred shell for running commands, shell "metacharacters" within commands must be escaped if they are to be taken literally, using the methods normal for your shell. For example, the UNIX tr (translate) command must have its arguments in quotes: tr "[a-z]" "[A-Z]" otherwise the shell is likely to replace them by all filenames that match, which is probably not what you want. This is also true when using your shell directly, and has nothing to do with Kermit. 4.2.1.3. SECURITY Some sites might not wish to allow access to system commands or external programs from within Kermit. Such access, including all the features described here, can be disabled in various ways: 1. When building from source code, include -DNOPUSH among the CFLAGS. 2. At runtime, give the NOPUSH command. 3. For server mode, give the DISABLE HOST command. 4. Implicit use of pipes can be disabled as described in [448]Section 4.2.4. Note: 3 and 4 are not necessary if you have done 1 or 2. 4.2.2. Commands for Transferring from and to Pipes SEND /COMMAND sends data from a command or command pipeline, and RECEIVE /COMMENT writes data to a command or pipeline. The GET /COMMAND command asks a server to send material, and then writes the incoming material to a command or pipeline. These features, along with switches (like "/COMMAND", described in [449]Section 4.7) are new to C-Kermit 6.1. The following synonyms are also provided: CSEND = SEND /COMMAND CRECEIVE = RECEIVE /COMMAND CGET = GET /COMMAND None of these commands can be used if a SEND or RECEIVE FILTER (respectively, [450]Section 4.2.3) is in effect, or if a NOPUSH command ([451]Section 4.2.1.3) has been given, or if the current protocol is not Kermit. 4.2.2.1. Sending from a Command SEND /COMMAND command [ as-name ] SEND /AS-NAME:as-name /COMMAND command CSEND command [ as-name ] These three forms are the same. They work like the SEND command, but instead of sending a file, it sends the standard output of the given command, either under the command's own name, or else with the given as-name. If the command contains spaces, it must be enclosed in braces. Braces should also be used for the as-name if it contains spaces. If braces are included around either the command or the as-name, they are removed after parsing but before use. As with SEND, the transfer is in text or binary mode according the current FILE TYPE setting, unless you override the global transfer mode by including a /TEXT or /BINARY switch. The command must require no input. When sending from a command or pipeline, C-Kermit has no way of knowing in advance how much data will be sent, and so it can not send the size to the other Kermit in the Attribute packet, and so the receiving Kermit has no way of displaying "percent done" or a progress bar (thermometer). Examples that make sense in text mode (illustrated by common UNIX commands): SEND /COMMAND finger CSEND finger sends the current "finger" listing (who's logged in) under the name "finger". The two forms "send /command" and "csend" are equivalent; we won't bother showing them both in the rest of the examples. SEND /COMMAND:{finger} CSEND {finger} Same as previous example (braces are removed from "{finger}"). SEND /COMMAND:{ finger } CSEND { finger } Same as previous example, but note that the spaces are kept. This does not prevent the shell from running the "finger" program, but its output is sent under the name " finger " (with a leading and trailing space). SEND /COMMAND:finger /AS-NAME:userlist CSEND finger userlist sends the current finger listing under the name "userlist". SEND /COMMAND:{finger | sort -r} /AS-NAME:userlist CSEND {finger | sort -r} userlist sends the current finger listing, sorted in reverse order, under the name "userlist". The braces are needed to distinguish the command from the as-name. SEND /COMMAND:{finger | sort -r} /AS-NAME:{userlist} CSEND {finger | sort -r} {userlist} Same as previous example (braces are removed from "{userlist}"). SEND /COMMAND:{finger | sort -r} /AS-NAME:{\freplace(\v(filename),\32,_)} CSEND {finger | sort -r} {\freplace(\v(filename),\32,_)} Like the previous example, but sends the output of the command under the name of the command, but with all spaces (\32) replaced by underscores, so the as-name is "finger_|_sort_-r". Examples that make sense in binary mode (three equivalent forms are shown): SEND /COMMAND /BINARY {tar cf - . | gzip -c} mydir.tar.gz SEND /COMMAND /BINARY /AS-NAME:mydir.tar.gz {tar cf - . | gzip -c} CSEND /BINARY {tar cf - . | gzip -c} mydir.tar.gz Makes a tar archive of the current directory, compresses it with the GNU gzip program, and sends it as "mydir.tar.gz". The other Kermit can, of course, just store it as a file, or it can use CRECEIVE to uncompress and dearchive it as part of the transfer process. When using a "pipeline" of commands in the command field, obviously, the first command must not require any input, and the last command should produce some output, and all intermediate commands should get some input and produce some output. 4.2.2.2. Receiving to a Command RECEIVE /COMMAND command CRECEIVE command This is like RECEIVE, except incoming material is written to the standard input of the given command, in text or binary mode according to the normal rules for file reception. Be sure to include a redirector to a file (if the command normally writes to standard output), or the output of the command won't go anywhere. The command may contain spaces; braces are not needed, but they are removed if used. WARNING: C-Kermit has no way of knowing anything about the command, or even whether it is a command. Thus this command will always cause C-Kermit to enter protocol mode, as long as some text is specified in the command field. However, if the text does not correspond to a command, the transfer will eventually fail with a message such as "Error writing data" or "Failure to close file". Examples for text mode (in UNIX): RECEIVE /COMMAND sort -r > reverse.txt CRECEIVE sort -r > reverse.txt The text that is received is sorted in reverse order and stored in the file "reverse.txt". The two forms shown are equivalent. RECEIVE /COMMAND {sort -r > reverse.txt} CRECEIVE {sort -r > reverse.txt} The same as the previous example; if braces are included, they are simply removed. RECEIVE /COMMAND {sort -r > \flower(\v(filename)).reverse} CRECEIVE {sort -r > \flower(\v(filename)).reverse} Same but stores result under the incoming filename, lowercased, and with ".reverse" appended to it. RECEIVE /COMMAND sort CRECEIVE sort Does nothing useful, since the output of sort has nowhere to go. RECEIVE /COMMAND sort -r | pr -3 | lpr -Plaserjet CRECEIVE sort -r | pr -3 | lpr -Plaserjet The text that is received is sorted in reverse order, arranged into three columns, and sent to the "laserjet" printer. Examples for binary mode: RECEIVE /COMMAND:{gunzip -c | tar xf -} CRECEIVE {gunzip -c | tar xf -} Assuming the data that is received is a compressed tar archive, uncompresses the archive and passes it to tar for extraction. In this case the braces are needed because otherwise the final "-" would be taken as a command continuation character (see [452]Using C-Kermit, 2nd Edition, p.33). GET /COMMAND remote-file command GET /COMMAND /AS-NAME:command remote-file CGET remote-file command This command tells the Kermit client to send a GET request for the given remote file to a Kermit server. Unlike GET, however, the incoming material is written to a command, rather than to a file. If the remote-file or the command contain spaces, they must be enclosed in braces. The same cautions about the command apply as for CRECEIVE. Examples (for UNIX): GET /COMMAND oofa.txt sort -r > oofa.new GET /COMMAND {oofa.txt} {sort -r > oofa.new} CGET oofa.txt sort -r > oofa.new CGET {oofa.txt} {sort -r > oofa.new} These four are equivalent. Each of them requests the server to send its "oofa.txt" file, and as it arrives, it is sorted in reverse order and written to "oofa.new". GET /COMMAND {profile exec a} lpr GET /COMMAND {profile exec a} {lpr} GET /COMMAND /AS-NAME:lpr {profile exec a} GET /COMMAND /AS-NAME:{lpr} {profile exec a} GET /COMMAND /AS:lpr {profile exec a} CGET {profile exec a} lpr CGET {profile exec a} {lpr} Here the remote filename contains spaces so it MUST be enclosed in braces. As it arrives it is sent to the lpr program for printing. Braces are optional around "lpr" since it contains no spaces. GET /COMMAND *.txt {cat >> new.txt} GET /AS-NAME:{cat >> new.txt} /COMMAND *.txt CGET *.txt {cat >> new.txt} This gets all the ".txt" files from the server and concatenates them all into a single "new.txt" file on the client. GET /COMMAND *.txt {echo \v(filename)>>new.txt;cat>>new.txt} CGET *.txt {echo \v(filename)>>new.txt;cat>>new.txt} As above, but inserts each file's name before its contents. 4.2.3. Using File-Transfer Filters The commands described in [453]Section 4.2.2 let you send the output of a command, or receive data into a command. But what if you want to specify preprocessing for files that you send, or postprocessing of files that you receive, even when multiple files are involved? For this you need a way to specify send and receive filters. The commands are SET SEND FILTER and SET RECEIVE FILTER; SHOW PROTOCOL displays the current settings. 4.2.3.1. The SEND Filter SET SEND FILTER [ command ] This command specifies a command to be run on any file that you SEND (or MOVE, MSEND, etc). It also applies to files sent when in server mode, in response to GET commands, but not to the results of REMOTE commands like REMOTE DIRECTORY, REMOTE TYPE, REMOTE HOST, etc. The command may be, but need not be, enclosed in braces; if it is, the braces are stripped before use. The output of this command is sent, rather than the file itself. The current FILE TYPE setting (TEXT or BINARY) applies to the output of the command. The command must contain at least one instance of \v(filename), for which the name of the actual file is substituted. If the command is omitted, the send filter is removed and files are sent in the normal manner. The SET SEND FILTER sets up a "global" filter -- that is, one that applies to all subsequent file-sending commands until you change or remove it. You can also specify a "local" filter to be used in a specific file-sending command by using the /FILTER switch (see [454]Section 1.5); for example: SEND /FILTER:command [ other-switches ] filename Besides \v(filename), you can include any other script programming notation in the send filter: variable names, array references, calls to built-in string or other functions, and so on. These are evaluated during file transfer, NOT during parsing, and they are evaluated separately for each file. When the SEND or MOVE (SEND /DELETE) command is used with a send filter, the output from the filter is sent under the file's original name unless you specify an "as-name" or template. The Attribute packet (if any) contains the original file's attributes (size, creation date, etc). So (for example) if the filter changes the file's size, the progress thermometer might be wrong. (We can't send the size of the output from the filter, because it is not known until the transfer is finished.) If you prefer that the size not be sent, use "set attributes size off". You can not use send filters with RESEND (SEND /RECOVER) or PSEND (SEND /START). Examples for text mode: SET SEND FILTER sort -r \v(filename) ; Braces may be omitted SET SEND FILTER {sort -r \v(filename)} ; Braces may be included SEND *.txt This sends every file in the current directory whose name ends with ".txt" under its own name, but with its lines sorted in reverse order. SEND /FILTER:{sort -r \v(filename)} *.txt Same as above, but the filter applies only to this SEND command. Braces are required in this case. SET SEND FILTER {sort -r \v(filename)} SEND oofa.txt reverse.txt Sends the oofa.txt file with its lines sorted in reverse order under the name "reverse.txt". SET SEND FILTER {sort -r \v(filename)} SEND oofa.* \v(filename).reverse Sends all the oofa.* files with their lines sorted in reverse order; each file is sent under its own name but with ".reverse" appended to it. SET SEND FILTER {tr "[a-z]" "[A-Z]" < \v(filename)} SEND *.txt Sends all ".txt" files under their own names, but uppercasing their contents. Note that the SEND FILTER applies not only to files that are sent with SEND, MOVE, MSEND, etc, but also to files sent by the C-Kermit server in response to GET requests. Examples for binary mode: SET SEND FILTER {gzip -c \v(filename)} SEND /BINARY oofa.txt oofa.txt.gz Sends the oofa.txt file, compressed by gzip, as oofa.txt.gz. SEND /BINARY /FILTER:{gzip -c \v(filename)} oofa.txt oofa.txt.gz As above, but the filter applies only to this SEND command. SET SEND FILTER {gzip -c \v(filename)} SEND /BINARY oofa.* \fupper(\replace(\v(filename),.,_)).GZ Sends all the oofa.* files, compressed by gzip, each under its own name, but with the name uppercased, all periods within the name converted to underscores, and ".GZ" appended to it. So, for example, "oofa.txt" is sent as "OOFA_TXT.GZ". In the gzip examples, note that the amount of data that is sent is normally less than the original file size because gzip compresses the file. But Kermit sends the original file size ahead in the attribute packet anyway (unless you tell it not too). Thus the transfer will probably appear to terminate early, e.g. when the receiver's file-transfer display thermometer is only at 40%. If this annoys you, tell Kermit to "set attribute length off". On the other hand, you can use the final position of the thermometer as a measure of the effectiveness of compression. 4.2.3.2. The RECEIVE Filter SET RECEIVE FILTER [ command ] This command specifies that the given command will be run on any file that is received before it is written to disk. The command may be, but need not be, enclosed in braces; if it is the braces are stripped before use. The following two commands are equivalent: SET RECEIVE FILTER sort -r > \v(filename) SET RECEIVE FILTER {sort -r > \v(filename)} The RECEIVE filter command may contain a "\v(filename)" sequence to be replaced by the incoming filename from the file header packet, but it is not required. However you must use it whenever your filter would normally write to Stdout, otherwise its output will be lost. The RECEIVE filter command may contain one or more "\v(filename)" sequence to be replaced by the incoming filename from the file header packet, but it is not required. However you must use it whenever your filter would normally write to Stdout, otherwise its output will be lost. RECEIVE /FILTER:command and GET /FILTER:command can also be used to specify a filter to be used for only one file-transfer operation. UNIX examples for text mode: SET RECEIVE FILTER lpr RECEIVE All the files that are received are sent to the default UNIX print spooler. RECEIVE /FILTER:lpr Same as above, except the lpr filter is used only with this RECEIVE command. RECEIVE lpr This is probably not what you want; it creates a file called lpr. SET RECEIVE FILTER {sort -r > \v(filename)} RECEIVE Stores each incoming file with its lines sorted in reverse order, under its own name. RECEIVE /FILTER:{sort -r > \v(filename)} As above, but the filter is used only for this RECEIVE command. SET RECEIVE FILTER sort -r > \v(filename) RECEIVE reverse.txt Stores each incoming file with its lines sorted in reverse order, under the name "reverse.txt". The actual result depends on the FILE COLLISION setting. If it is OVERWRITE and multiple files arrive, then each incoming file destroys the previous one. If it is BACKUP (the default), filename conflicts are resolve by adding "version numbers" to the filenames: reverse.txt, reverse.txt.~1~, reverse.txt.~2~, etc. SET RECEIVE FILTER sort -r > \v(filename) RECEIVE \v(filename).reverse Stores each incoming file with its lines sorted in reverse order, under the name it arrived with, but with ".reverse" appended to it. SET RECEIVE FILTER sort -r > \v(filename) RECEIVE \flower(\v(filename)).reverse Like the previous example, but ensures that the filename is lowercase. Examples for binary mode: SET RECEIVE FILTER gunzip -c > \v(filename) RECEIVE This receives one or more presumably compressed file and uncompresses each one into a file having the same name it was sent with. For example, if the file is sent with the name OOFA.TXT.GZ, it is stored with that name, even after decompression. SET RECEIVE FILTER gunzip -c > \v(filename) RECEIVE \flower(\fsubstring(\v(filename),1,\flength(\v(filename))-3)) Like the previous example, but the resulting filename has its rightmost three characters removed from it and the remainder is lowercased. So if the incoming filename is OOFA.TXT.GZ, it is stored as oofa.txt after decompression. Of course you don't want to type such long hideous commands, so we have also introduced several new functions: \fstripx(string[,character]) This function removes the rightmost segment of the string that starts with the given character. If no character is given, period (.) is used. Thus it is most conveniently used for stripping the extension from a filename (or the decimal portion from a floating-point number written in US/UK style). Examples: \fstripx(OOFA.TXT.GZ) => OOFA.TXT \fstripx(OOFA.TXT.GZ,.) => OOFA.TXT \fstripx(OOFA.TXT.GZ,X) => OOFA.T \fstripx(\fstripx(OOFA.TXT.GZ)) => OOFA \fstripx($100.00) => $100 \fstripn(string,number) Removes the rightmost number characters from the string. Examples: \fstripn(OOFA.TXT.GZ) => OOFA.TXT.GZ \fstripn(OOFA.TXT.GZ,3) => OOFA.TXT \fstripn(OOFA.TXT.GZ,7) => OOFA \fstripb(string[,c1[,c2]]) Strips enclosing matching braces, brackets, parentheses, or quotes from the string. The second argument, c1, specifies which kind of enclosure to look for; if not specified, any enclosing (), [], <>, {}, "", '', or `' are removed. If c1 is specified and c2 is not, then if c1 is an opening brace, bracket, or parenthesis, the matching closing one is supplied automatically as c2. If both c1 and c2 are specified, then to be stripped the string must begin with c1 and end with c2. If the string is not enclosed in the indicated manner, the result is the original string. Examples: \fstripb("abc") => abc \fstripb([abc]) => abc \fstripb([abc) => [abc \fstripb() => abc \fstripb(,[) => \fstripb((abc)) => abc \fstripb((abc),[) => (abc) \fstripb((abc),{(}) => abc \fstripb(+abc+) => +abc+ \fstripb(+abc+,+) => abc \fstripb(+abc+,+,^) => +abc+ \fstripb(+abc^,+,^) => abc \fstripb('abc') => abc \fstripb(`abc') => abc \fstripb(``abc'') => `abc' \fstripb(\fstripb(``abc'')) => abc Notice the special syntax required for including a literal parenthesis in the argument list. As the last two examples illustrate, \fstripb() strips only one level at at a time; nesting can be used to strip a small fixed number of levels; loops can be used to strip larger or indeterminate numbers of levels. \flop(string[,char]) Removes the leftmost segment of the string that ends with the given character. If no character is given, period (.) is used. Examples: \flop(OOFA.TXT.GZ) => TXT.GZ \flop(OOFA.TXT.GZ,.) => TXT.GZ \flop(OOFA.TXT.GZ,X) => T.GZ To remove the leftmost number characters, just use \fsubstring(s,number+1). To return the rightmost number characters, use \fright(s,number). So the hideous example: receive \flower(\fsubstring(\v(filename),1,\flength(\v(filename))-3)) can now be written as: receive \flower(\fstripx(\v(filename))) That is, the filename stripped of its extension and then lowercased. This is not only shorter and less hideous, but also does not depend on the length of the extension being 3. Note that when a receive filter is in effect, this overrides your FILE COLLISION setting, since Kermit has no way of knowing what the final destination filename will be (because it does not know, and can not be expected to know, the syntax of every version of every command shell on every platform on the planet). 4.2.4. Implicit Use of Pipes If you wish, C-Kermit can also examine incoming filenames to see if they start with "!", and if so, the subsequent text is treated as a command to read from or write to. For example, if a Kermit client is given the following command: get {!finger | sort} the server on the other end, if it supports this feature, will run the "finger" program, pipe its standard output to the "sort" program, and send sort's standard output back to you. Similarly, if you: send oofa.txt !sort -r > oofa.new or, equivalently: send oofa.txt {!sort -r > oofa.new} or: send /as-name:{!sort -r > oofa.new} oofa.txt this has the receiver send the contents of the incoming oofa.txt file to the sort program, which sorts the text in reverse order and stores the result in oofa.new. This use of the exclamation mark should be familiar to UNIX users as the "bang" feature that lets you run an external application or command from within another application. Kermit's "bang" feature is disabled by default, since it is not unheard for filenames to actually begin with "!". So if you want to use this feature, you must enable it with the following command: SET TRANSFER PIPES { ON, OFF } ON enables the recognition of "!" notation in incoming filenames during file transfer as an indicator that the remaining text is the name of a command. OFF, the default, disables this feature and uses the text as a filename in the normal fashion. This command does NOT affect SEND /COMMAND, GET /COMMAND, CSEND, etc. So using a combination of CSEND (SEND /COMMAND) and the "bang" feature, you can transfer a directory tree all in one command (assuming the remote Kermit supports pipe transfers and has them enabled): CSEND {tar cf - . | gzip -c} {!gunzip -c | tar xf -} or: SEND /COMMAND:{tar cf - . | gzip -c} /as:{!gunzip -c | tar xf -} Pay close attention to the syntax. Braces are needed around the command because it contains spaces; braces are needed around the as-name because it ends with "-". The as-name must begin with "!" or receiving Kermit will not recognize it as a command. The CSEND command must NOT begin with "!" unless you are running a command whose name really does start that character. Similarly, you have a Kermit server send a directory tree to be unpacked on the client end: CGET {!tar cf - . | gzip -c} {gunzip -c | tar xf -} or: GET /COMMAND {!tar cf - . | gzip -c} /as:{gunzip -c | tar xf -} Notice how, in this case, the bang is required in the remote command, to distinguish it from a filename, but not in the local command, since by definition of CGET (or GET /COMMAND), it is known to be a command. SEND and RECEIVE FILTERs supersede the bang feature. For example, if a file arrives under the name "!gunzip -c | tar xf -", but the receiving Kermit also has been given a command like: set receive filter sort -r > \v(filename) then the incoming data will be sorted rather than gunzipped. Finally, if SET TRANSFER PIPES is ON (and in this case, this must be done in your C-Kermit initialization file), you can send from a pipe on the C-Kermit command line: kermit -s "!finger | sort -r" -a userlist In this case the "filename" contains spaces and so must be quoting using your shell's quoting rules. 4.2.5. Success and Failure of Piped Commands Commands or programs started by Kermit as a result of CSEND or CRECEIVE commands, CGET, SEND /COMMAND, REDIRECT commands (see [455]Section 4.2.8.2), implicit use of pipes, RUN commands, and so forth, should return their exit status codes to the Kermit command that caused them to be run, and therefore IF SUCCESS and IF FAILURE tests after these commands should work as expected. For example: CSEND blah < filename should fail if there is no command called "blah" or if there is no file called "filename". However, this is not foolproof and sometimes C-Kermit might think a command succeeded when it failed, or vice versa. This is most likely to happen when the highly system-dependent methods that Kermit must use to determine a command's exit status code do not supply the right information. It can also happen because some commands might define success and failure differently from what you expect, or because you are using a pipeline composed of many commands, and one of them fails to pass failing exit status codes up the chain. The most likely culprit is the shell itself, which in most cases must be interposed between Kermit and any external program to be run. In any case, you can examine the following variable to find out the exit status code returned to Kermit by the process most recently run by any command that runs external commands or programs, including CSEND, CRECEIVE, REDIRECT, RUN, etc: \v(pexitstat) In UNIX, Windows and OS/2, the value should be -2 if no command has been run yet, 0 if the most recent command succeeded, -1, -3, or -4 if there was an internal error, and a positive number returned by the command itself if the command failed. If the number is in the range 1-127, this is the program's exit status code. If it is 128 or greater, this is supposed to indicate that the command or program was interrupted or terminated from outside itself. In Windows 95 and 98, the return values of the default shell are unreliable; various third-party shells can be used to work around this deficiency. In VMS, it is the actual exit status code of the command that was run. This is an odd number if the command succeeded, and an even number if it failed. You can see the associated message as follows: run write sys$output f$message(\v(pexitstat)) Or, more conveniently, use the new Kermit function: echo \ferrstring(\v(pexitstat)) which converts a system error code (number) to the corresponding message. 4.2.6. Cautions about Using Pipes to Transfer Directory Trees Although utilities such as tar and zip/unzip might be available on different platforms (such as UNIX and Windows), this does not necessarily mean you can use them successfully to transfer directory trees between unlike platforms. For example: CSEND {tar cf - . | gzip -c} {!gunzip -c | tar xf -} when used from UNIX to Windows will have satisfactory results for binary files, but not for text files. UNIX text files have lines ending with Linefeed (LF) only, whereas Windows text files have lines ending in Carriage Return and Linefeed (CRLF). Thus any text files that were in the archive formed by the first tar command will be unpacked by the second tar command in their original form, and will display and print incorrectly in Windows (except in applications that have been explicitly coded to handle UNIX-format text files). On the other hand if you told gzip to use "text mode" to do record format conversion (assuming there was a way to tell it, as there is with most "zip" programs), this would destroy any binary files in the archive. Furthermore, if the archive contains text files that are written in languages other than English, the "special" (accented and/or non-Roman) characters are NOT translated, and are therefore likely show up as gibberish on the target system. For example, West European languages are usually encoded in ISO Latin Alphabet 1 in UNIX, but in PC code page 850 on the PC. Capital A with acute accent is code point 193 (decimal) Latin-1, but 181 in CP850. So A-acute in the UNIX file becomes Middle Box Bottom on the PC, and similarly for all the other special characters, and for all other languages -- Greek, Russian, Hebrew, Japanese, etc. So when transferring text files between unlike platforms, you should use direct Kermit file transfers so Kermit can apply the needed record-format and character-set transformations. Use pipelines containing archivers like tar or zip only if all the files are binary or the two systems use the same record format and character set for text files. Also see [456]Sections 4.3, [457]4.10, [458]4.11, and [459]4.15 for how to transfer directory trees between both like and unlike systems directly with Kermit. 4.2.7. Pipes and Encryption Of course pipelines could be used for encrypted file transfers, assuming proper precautions could be taken concerning the transmission of the key. But there is rarely a good way to do this. To illustrate using UNIX crypt: csend {crypt key < filename} {!crypt key > filename} Or, more ambitiously: csend {tar cf - . | gzip -c | crypt key} {!crypt key | gunzip -c | tar xf -} transmits the key in the file header packet as part of the (clear-text) remote command, defeating the entire purpose of encrypting the file data. But if you are connected in terminal mode to the remote computer and type: creceive {crypt key > filename} at the remote Kermit prompt, you have also transmitted the key in clear text over the communications link. At present, the only secure way to use CSEND and CRECEIVE with an encryption filter is to have a human operator at both ends, so the key does not have to be transmitted. Theoretically it would be possible to use PGP software (Pretty Good Privacy, by Phil Zimmerman, Phil's Pretty Good Software) to avoid key transmission (since PGP uses separate public and private key and "lets you communicate securely with people you've never met, with no secure channels needed for prior exchange of keys"), but the specific method has yet to be worked out. HINT: See the PGP User's Guide, e.g. at: [460]http://www.telstra.com.au/docs/PGP/ Especially the topic "Using PGP as a UNIX-Style Filter": [461]http://www.telstra.com.au/docs/PGP/pgpdoc2/pgpdoc2_17.html In any case, better and more convenient security options are now available: Kerberos authentication and encryption ([462]CLICK HERE for details) and the new ability to run C-Kermit "though" other communication programs, described in [463]Section 2.7. 4.2.8. Commands and Functions Related to Pipes 4.2.8.1. The OPEN !READ and OPEN !WRITE Commands These are described in [464]Using C-Kermit, and are generally useful with reading output from commands that produce more than one line on their standard output, or writing multiple lines into commands that accept them on their standard input. In C-Kermit 7.0 CLOSE !READ is accepted as a synonym for CLOSE READ, and CLOSE !WRITE for CLOSE WRITE. Testing the success and failure of these commands, however, can be a bit tricky. Consider: open !read lalaskjfsldkfjsldkfj (where "lalaskjfsldkfjsldkfj" is neither a valid command nor the name of a program or script that can be run). OPEN !READ, in UNIX at least, translates this into execl(shellpath,shellname,"-c",command). This means it starts your preferred shell (e.g. from the SHELL environment variable) and asks it to execute the given command. It must be this way, because your command can be a either an internal shell command (which only your shell can execute) or an external command, which only your shell knows how to find (it knows your PATH and interprets, etc). Therefore unless OPEN !READ can't start your shell, it always succeeds. Continuing with the nonexistent-command example: C-Kermit> open !read lalaskjfsldkfjsldkfj C-Kermit> status SUCCESS C-Kermit> read line C-Kermit> status SUCCESS C-Kermit> echo "\m(line)" "bash: lalaskjfsldkfjsldkfj: command not found" C-Kermit> close read C-Kermit> status FAILURE C-Kermit> In other words, the failure can not be detected on OPEN, since the OPEN command succeeds if it can start your shell. It can't be detected on READ, since all this does is read output from the shell, which in this case happens to be an error message. However, failure IS detected upon close, since this is the occasion upon which the shell gives Kermit its exit status code. For an illustration of this situation, see [465]Section 2.14. 4.2.8.2. The REDIRECT Command A second method of I/O redirection is offered by the REDIRECT command. This is a rather advanced and tricky feature that is presently supported only in UNIX C-Kermit, in OS-9 C-Kermit, and in Kermit 95. Syntax: REDIRECT command Runs the given command, sending its standard output to the current communications channel (SET LINE, SET PORT, or SET HOST connection), and reading its standard input from the same connection. Works only in local mode -- i.e. a connection is required -- and then only if the given command uses Standard I/O. Example: redirect finger runs the local "finger" command and sends its output over the connection as plain text, where presumably there is a process set up to read it. Another example: redirect finger | sort -r shows the use of a pipeline. Note: REDIRECT differs from CSEND/CRECEIVE in two important ways: (1) it does not use the Kermit protocol, and (2) it uses a bidirectional pipe rather than a one-way pipe. The primary use of the REDIRECT command is to run external protocols, such as sz/rz in UNIX for ZMODEM, when they work over Standard I/O(*). Example: set host xyzcorp.com (login, etc) redirect sz oofa.zip lets you make a Telnet connection with C-Kermit and then do a ZMODEM transfer over it. ZMODEM protocol messages go both ways over the same connection simultaneously. It is possible to use C-Kermit on UNIX as your PPP dialer and then to REDIRECT the connection to the PPP software, but C-Kermit 7.0 offers a better approach to PPP dialing in its new EXEC command ([466]Section 1.23). In theory, you can also redirect an interactive process. For example, suppose you tell Kermit 95 to wait for an incoming TCP/IP connection: set host * 3000 and then tell C-Kermit on UNIX to: set host kermit95hostname 3000 redirect ksh and then tell Kermit 95 to CONNECT: now you are talking to the UNIX K-shell; you can give commands (pwd, ls, etc) and see the results. In practice, the K-shell's terminal modes are messed up because (a) it is not going through the Unix terminal driver, and (b) it is "smart" and knows it is being redirected, and so acts in a decidedly inhospitable manner (other applications like EMACS, vi, etc, simply refuse to run if their standard i/o has been redirected). (*) The publicly-distributed sz/rz programs do not work as clients. However, Omen Technology does offer an up-to-date redirectable client XYZMODEM program called crzsz. 4.2.8.3. Receiving Mail and Print Jobs As of 7.0, and in UNIX only, files that are sent to C-Kermit as mail (when the other Kermit uses a MAIL or SEND /MAIL command) or to be printed (via REMOTE PRINT or SEND /PRINT) are now piped directly to the mail or print program, rather than written to temporary files and then mailed or printed and then deleted. This has the advantages of (a) not requiring a temporary file, and (b) allowing mail to have a proper subject in place of the filename. Temporary files were bad not only because they required (a) space, and (b) writability of the current directory, but also because using them could result in wiping out an existing file. See [467]Section 4.7 for more about SEND /MAIL and SEND /PRINT. 4.2.8.4. Pipe-Related Functions The \fcommand(command) function runs the given shell or system command and returns the command's standard output as its value (with any newline characters stripped from the end), unless the result is too long, in which case it returns the empty string. The maximum length for the result is at least 1022 bytes, and it might be longer on some platforms. Examples (UNIX): C-Kermit> echo "\fcommand(date)" "Fri Apr 18 13:31:42 1997" C-Kermit> echo "\fcommand(finger | wc -l)" ; how many users logged in? " 83" C-Kermit> evaluate \fcommand(finger | wc -l) * 2 166 C-Kermit> echo Welcome to \fcommand(tty) on \fcommand(date) Welcome to /dev/ttyre on Fri Apr 18 13:31:42 1997 C-Kermit> echo "\fcommand(ls oofa.*)" "oofa.c oofa.h oofa.o" C-Kermit> cd /directory-with-thousands-of-files C-Kermit> echo "\fcommand(ls -l)" ; This would be too long "" C-Kermit> If a command's output would be too long, you can use the other, more laborious method of reading from a command: OPEN !READ command, READ each line, CLOSE !READ. The \frawcommand(command) function is identical to \fcommand(command), except it does not remove trailing newline characters: C-Kermit> echo "\frawcommand(date)" "Fri Apr 18 13:31:42 1997 " C-Kermit> echo "\frawcommand(ls oofa.*)" "oofa.c oofa.h oofa.o " C-Kermit> Use \frawcommand() if you want to retain the final line terminators, or if the command's output is "binary". But remember that if the result of this (or any other) function contains any NUL (ASCII code 0) characters, the first NUL will terminate the result string because this is how C strings work (it's "C-Kermit", remember?). These functions are useful not only locally, but also in the client/server arena. If you need to get the results from a system command on the server end into a variable on the client end, just do: [ remote ] query kermit command(date) The result is in the local \v(query) variable; see [468]Using C-Kermit, 2nd Ed., pp.359-360 for details. 4.3. Automatic Per-File Text/Binary Mode Switching When transferring files between like systems (e.g. UNIX-to-UNIX), binary mode can be used for all files unless character-set translation is needed, and in fact Kermit programs of recent vintage recognize each others' platforms and switch to binary mode automatically when it is appropriate (e.g. DOS to OS/2, or UNIX to UNIX). (Exception: LABELED mode is chosen for VMS-to-VMS and OS/2-to-OS/2 transfers so complex file formats can be preserved.) On a client/server connection between like systems, the transfer mode is currently determined by the file sender, rather than always by the client. If the client is sending, it controls the transfer mode. If a GET command is sent to the server, the server sends all files in binary mode if its TRANSFER CHARACTER-SET is TRANSPARENT; otherwise it uses text mode for text files (according to its text-pattern list) and binary mode for binary files. Of course, the client can control the server's transfer character-set with the REMOTE SET TRANSFER CHARACTER-SET command. When transferring files between unlike systems, however, (e.g. UNIX-to-DOS), some files (such as executable program images) must be transferred in binary mode but others (such as plain-text files) must be transferred in text mode so their record format and character sets can be appropriately converted. If a binary file is transferred in text mode, it is ruined. If a text file is transferred in binary mode, then at the very least, its format can be incorrect; at worst it is also corrupted because its character set was not converted (in extreme cases the corruption is total, e.g. because one system is ASCII-based and the other EBCDIC). 4.3.1. Exceptions VMS C-Kermit, when sending files to a non-VMS system, switches to text or binary mode automatically for each file, based on the record format in the file's directory entry; thus the mechanisms described in this section do not apply to VMS C-Kermit, yet the effect is the same: automatic text/binary mode switching when VMS C-Kermit is sending files. See the VMS Appendix of [469]Using C-Kermit for details. Kermit versions that support LABELED or IMAGE transfer mode are likewise not affected by this feature when one of those modes is selected (normally used only when transferring between like systems). Kermit versions that support file-transfer pipes and filters are not affected by this feature when pipes or filters are used, since the output of a pipe or filter (such as gzip) is likely to require transfer in a different mode than the original file. Finally, SEND /TEXT or SEND /BINARY will force files to be sent in the indicated mode, overriding all automatic transfer-mode-choosing mechanisms. 4.3.2. Overview Suppose you give C-Kermit a command like: SEND *.* And suppose the pattern *.* matches a mixture of text files (such as program source code) and binary files (such os object modules or executable programs). C-Kermit 6.0 and earlier (except on VMS) send all files in the same mode: whatever you said in your most recent SET FILE TYPE command, or else whatever mode was chosen automatically according to the rules on page 236 of Using C-Kermit, 2nd Ed. But when text and binary files are mixed in the same group, and the files are being transferred to an unlike system (e.g. UNIX to IBM Mainframe), this results in corruption of either all the text files or all the binary files. Stream-oriented file systems such as in UNIX and DOS do not record any information about the file to tell us whether the file should be transferred in binary or text mode, making it impossible to select the transfer mode for each file in a group automatically with any certainty. However, we can use some fairly-well established file naming conventions for this purpose. C-Kermit 7.0 lets you provide lists of filename patterns that are used to separately determine the file type for each individual file being transferred. A pattern is a string, possibly containing the special characters "*" (asterisk, which matches any string of zero of more characters) and/or "?" (question mark, which matches any single character). For example "a*b" matches all files whose names start with "a" and end with "b", such as "ab", "arb", "ababababab", etc, but not "abba". And "a?b" matches any file whose name starts with "a", ends with "b", and is exactly 3 characters long. NOTE: When typing commands at the C-Kermit prompt, you must prefix "?" with \ to override its normal function of giving help. (Also see [470]Section 4.9 for additional pattern-matching notations that might be available in your version of C-Kermit.) When you have specified filename recognition patterns, C-Kermit can transfer the ones whose names match any of the binary-mode patterns in binary mode, and those with names that match any of the text-mode patterns in text mode, and those whose names match neither in the prevailing mode you have chosen, or that was chosen automatically via peer recognition. 4.3.3. Commands SET FILE PATTERNS { ON, OFF, AUTO } This tells Kermit whether to do per-file filename pattern-matching to determine text or binary mode. The normal and default setting is AUTO, which means to use pattern lists to switch transfer mode only when it is certain that the other Kermit program supports automatic notification of transfer mode (via Attribute packets) on a per-file basis (this information is obtained automatically during protocol startup negotiation). ON means to always determine the transfer mode from the filename and pattern list when sending files. Use OFF to disable this feature (without resetting your pattern lists). Also note that if you have selected LABELED file transfer (SET FILE TYPE LABELED), this takes precedence over filename-matching patterns and all files are sent in labeled mode. SET TRANSFER MODE MANUAL Disables the use of filename patterns, no matter what the FILE PATTERNS setting. REMOTE SET TRANSFER MODE MANUAL Client command to disable automatic transfer mode, and therefore also filename patterns, in the server. Synonym: REMOTE SET XFER MODE MANUAL. { GET, SEND, etc } { /BINARY, /TEXT } Including a /BINARY or /TEXT (or, where supported, /IMAGE or /LABELED) switch with a file-transfer command changes the transfer mode to manual for that command only, and therefore disables patterns that that command. SET FILE BINARY-PATTERNS [ pattern [ pattern [ pattern ... ] ] ] A list of zero or more patterns, separated by spaces (not commas). Letters in a pattern are case-sensitive if the underlying filenames are case sensitive (as in UNIX), and case-insensitive otherwise (as in Windows). If a file's name is matched by any pattern in the list and SET FILE PATTERNS is ON, the file is sent in binary mode. Examples: SET FILE BINARY-PATTERNS *.gz *.Z *.tar *.zip *.o *.so *.a *.out ; UNIX SET FILE BINARY-PATTERNS *.EXE *.ZIP *.OBJ *.COM ; DOS or OS/2 or Windows If a pattern contains spaces, enclose it in braces. SET FILE TEXT-PATTERNS [ pattern [ pattern [ pattern ... ] ] ] Like SET FILE BINARY-PATTERNS, but the patterns choose text files rather than binary ones. Examples: SET FILE TEXT-PATTERNS *.TXT *.KSC *.HTM* *.BAT ; DOS, Windows, OS/2 ADD BINARY-PATTERNS [ pattern [ pattern [ pattern ... ] ] ] Adds one or more patterns to the BINARY-PATTERN list. ADD TEXT-PATTERNS [ pattern [ pattern [ pattern ... ] ] ] Adds one or more patterns to the TEXT-PATTERN list. REMOVE BINARY-PATTERNS [ pattern [ pattern [ pattern ... ] ] ] Removes one or more patterns from the BINARY-PATTERN list. The given patterns are matched with the ones in the BINARY-PATTERNS list with case sensitivity if the underlying file system has case-sensitive names (as do UNIX and OS-9), otherwise with case independence. REMOVE TEXT-PATTERNS [ pattern [ pattern [ pattern ... ] ] ] Removes one or more patterns from the TEXT-PATTERN list. SHOW PATTERNS Displays the current pattern selections. Whenever you give a SET FILE BINARY-PATTERNS or SET FILE TEXT-PATTERNS command, the previous list is replaced. If you give one of these commands without a pattern list, the previous list is removed. When patterns are active and files are being sent, text patterns (if any) are applied first (but only if not RESENDing and not sending in LABELED mode), then binary patterns, so if the same pattern appears in both lists, binary mode is chosen. 4.3.4. Examples Here's an example that might be used when sending files from UNIX: set file type binary set file text-patterns *.c *.h *.w *.txt makefile set file binary-patterns *.o msend makefile wermit wart ck*.[cwho] ck*.txt Note that "wermit" and "wart" do not match any patterns so they are sent in the prevailing mode, which is binary. Also note the use of "makefile" as a pattern that does not contain any wildcard characters (there is no other convention to distinguish among "wermit" and "wart", which are binary executables, and "makefile", which is a text file, purely by their names). Most C-Kermit implementations have a default pattern list built in, which includes patterns that are almost certain to succeed in picking the right transfer mode. Others are omitted due to ambiguity. For example ".hlp", and ".ini" are generally binary types in Windows but text types everywhere else. NOTE: ".doc", used for decades to denote plain-text documentation files, now more often than not denotes a Microsoft Word Document, so ".doc" is now considered a binary type since it does less harm to transfer a plain-text document in binary mode than it does to transfer an MS Word file in text mode (except when IBM mainframes are involved!) ANOTHER NOTE: ".com" files are binary in DOS-like operating systems, but they are text (DCL command procedures) in VMS. VMS C-Kermit sends .COM files in text mode; K95 sends them in binary mode. If you download a .COM file from VMS to DOS or Windows, and then upload it to another VMS system, be sure to use SEND /TEXT to preserve its textness. You can see the default pattern list by starting C-Kermit without its initialization file (e.g. "kermit -Y") and using the SHOW PATTERNS command. If you will be depending on this feature, be sure to examine the list carefully in conjunction with the applications that you use. The default pattern list does not take "backup files" into account because (a) people usually don't want to transfer them; and (b) it would make the pattern lists more than twice as long. For example, we would need to include both *.txt and *.txt.~[0-9]*~ for ".txt" files, and similarly for all the others. Instead, you can use SEND /NOBACKUP (or SET SEND BACKUP OFF) to skip over all backup files. Put your most commonly-used safe pattern declarations in your C-Kermit customization file (ckermod.ini, .mykermrc, k95custom.ini, etc). As noted, SET FILE PATTERNS is ON by default. Sometimes, however, it is desirable, or necessary, to force files to be sent in a particular mode, and often this must be done from the command line (e.g. when using Kermit as a download helper in a Web browser like Lynx). The -V command-line options is equivalent to SET FILE PATTERNS OFF and SET TRANSFER MODE MANUAL. Example: kermit -Vis oofa.txt forces oofa.txt to be sent in binary mode, even though ".txt" might match a text pattern. 4.4. File Permissions "Permissions" refers to a code associated with a file that specifies who is allowed to access it, and in what manner. For example, the owner, the members of one or more groups, the system administrator, and everybody else, might be allowed various combinations of Read, Write, Append, Execute, or Listing access. The permission code goes by different names on different platforms. In UNIX, it might be called the filemode. In VMS, it is called the file protection (or protection mask). The comments in this section presently apply only to the UNIX and VMS versions of C-Kermit, to which these features were added in version 7.0; the DOS, Windows, and OS/2 file systems embody no notions of protection, and so MS-DOS Kermit and Kermit 95 do not send file permissions, and ignore them when received. The permissions for a received file are determined by a combination of the file transfer mode (VMS-to-VMS transfers only), whether a file of the same name exists already, whether permissions of the file are received in the file attribute packet, and the setting of ATTRIBUTES PROTECTION. The default for ATTRIBUTES PROTECTION is ON. If no attributes are received, the effect is the same as if attributes PROTECTION were OFF. For VMS-to-VMS transfers, the default LABELED mode simply copies the protection code from source to destination. 4.4.1. When ATTRIBUTES PROTECTION is OFF If no file of the same name exists, system defaults determine the permissions of the new file. Otherwise, the actions taken depend on the current FILE COLLISION setting: BACKUP, OVERWRITE, RENAME, etc, as documented in [471]Using C-Kermit. But now the new file (if it is created at all) automatically inherits the permissions (mode bits) of the existing file in a way that is appropriate for the platform. 4.4.1.1. Unix All mode bits are inherited except the directory bit, since the incoming file can not possibly be a directory. (In any case, it is not possible to receive a file that has the same name as an existing directory unless FILE COLLISION is set to RENAME). 4.4.1.2. VMS Files with the same name as an existing file, transferred in modes other than LABELED between VMS systems, inherit the protection of the prior version. 4.4.2 When ATTRIBUTES PROTECTION is ON File permissions can be conveyed as part of the file transfer process, in accordance with the Kermit protocol definition. If the file sender puts system-dependent and/or system-independent versions of the file protection (permissions) into the Attribute (A) packet, the file receiver can set the new file's permissions from them. Otherwise, the permissions are set the same as for ATTRIBUTES PROTECTION OFF. When the incoming A packet contains system-dependent permissions, the file receiver checks to see if the sender has the same system ID (e.g. both the sending and receiving systems are UNIX, or both are VMS); if so, it decodes and uses the system-dependent permissions; otherwise it uses the generic ones (if any) and applies them to the owner field, setting the other fields appropriately as described in the following sections. Setting the incoming file's protection from the A packet is controlled by SET ATTRIBUTES PROTECTION (or PERMISSION), which is ON by default, and its status is displayed by SHOW ATTRIBUTES. The main benefit of this feature is to not have to "chmod +x" an executable file after transfer from UNIX to UNIX. Its cross-platform benefits are less evident, perhaps to retain the status of the Unix 'x' bit on a VMS system, for subsequent transfer back to a Unix system. 4.4.2.1. System-Specific Permissions System-specific file permissions are used when the two Kermit programs recognize each other as running on the same type of system. For example, both are running under some form of UNIX (it doesn't matter which UNIX variation -- HP-UX, Solaris, AIX, etc -- all use the same scheme for file permissions); or both are running under VMS (even if one is on an Alpha and the other on a VAX, and/or one is old and the other is new). 4.4.2.1.1. UNIX UNIX supports three categories of users, File Owner, Group, and World, and three types of file access permission: Read, Write, and Execute. Thus, a UNIX file's permissions are expressed in 9 bits. The system-dependent permission string for UNIX is a 3-digit octal string, the low-order 9 bits of the st_mode member of the stat struct; we deliberately chop off the "file format" bits because they are not permissions, nor do we convey the setuid/setgid bits, lock bit, sticky bit, etc. 4.4.2.1.2. VMS VMS supports four categories of users, System, File Owner, Group, and World, and four types of file access permission: Read, Write, Execute, and Delete. Thus, a VMS file's permissions are expressed in 16 bits. The system-dependent protection string for VMS is a 4-digit hexadecimal string, corresponding to the internal-format protection word of the file (RWED for each of World,Group,Owner,System). A new file normally gets all 16 protection bits from the original file of the same name. Note: VMS-to-VMS transfers take place in LABELED mode when the two C-Kermits recognize each other's platform as VMS (unless you have disabled LABELED-mode transfers). In this case, all of a file's attributes are preserved in the transfer and the protection mask (and other information) is taken from the file's internal information, and this takes precedence over any information in the Attribute packets. You can defeat the automatic switching into LABELED mode (if you want to) with SET TRANSFER MODE MANUAL. 4.4.2.2. System-Independent Permissions The system-independent ("generic") protection is used when the system IDs of the two Kermit programs do not agree (e.g. one is UNIX, the other is VMS). The generic protection attribute includes the following permissions (not all are applicable to every file system): Read, Write, Append, Execute, Delete, Search. The generic permissions are derived from the owner permissions of the source file, thus, a Unix 'w' permission becomes VMS Write,Delete. The Owner field of the new file's permissions is set from the incoming generic protection attribute. In UNIX, the Group and World permissions are set according to your umask, except that execute permission is NOT set in these fields if it was not also set in the generic protection (and consequently, is set in the Owner field). In VMS, the System, Group, and World permissions are set according to the process default file permission (as shown in VMS by SHOW PROTECTION), except that no permissions are allowed in these fields that are not included in the generic permissions. Note that the VMS and UNIX interpretations of Execute permission are not identical. In UNIX, a file (binary executable, shell script, etc) may not be executed unless it has Execute permission, and normally files that are not intended for execution do not have Execute permission. In VMS, Read permission implicitly supplies Execute capability. Generally files that have Read permission also have explicit Execute permission, but files (binary executables, DCL command procedures) that have Read permission and not Execute permission can still be executed. 4.5. File Management Commands 4.5.1. The DIRECTORY Command Prior to C-Kermit 7.0, the DIRECTORY command always ran an external system command (such as "ls" on UNIX) or program to product the directory listing. This had certain advantages, mostly that you could include system-dependent options for customized listings, e.g. on UNIX: dir -lt c* | more or in VMS: directory /size/date/protection/except=*.obj oofa.*;0 This approach, however, carries some disadvantages: C-Kermit can't return SUCCESS or FAILURE status for (e.g.) "dir foo" according to whether the file "foo" exists; and it runs an inferior process, which might be a problem in some environments for resource and/or security reasons, and won't work at all in a "nopush" environment (e.g. one in which C-Kermit is configured to forbid access to exterior commands and programs, e.g. in a VMS "captive account"). In C-Kermit 7.0 on VMS and UNIX, and in K95 1.1.19 and later, the DIRECTORY command is internal to Kermit. It can be run in a "nopush" environment and returns SUCCESS or FAILURE status appropriately. In UNIX it prints all dates and times in a consistent way (unlike ls). In VMS it prints precise file sizes, rather than "blocks". It offers several formatting and other options, but it is not necessarily more flexible than the corresponding external commands or programs (the UNIX "ls" program, the VMS "directory" command). The syntax is: DIRECTORY [ switch [ switch [ ... ] ] ] [ filespec ] If no filespec is given, all files in the current directory are listed. Optional switches include all the standard file-selection switches presented in [472]Section 1.5.4, plus: /ALL Show both regular files and directories; this is the default. /ARRAY:x Instead of displaying a directory listing, put the files that would have been shown (based on the filespec and other selection switches) in the given array. The array need not (and should not) be predeclared; if the array already exists, it is destroyed and reused. The array name can be a single letter, like "a", or any fuller form, such as "&a", "\&a", "\&a[]", etc. If the /ARRAY switch is included, the following other switches are ignored: /BRIEF, /VERBOSE, /HEADING, /PAGE, /ENGLISHDATE, /ISODATE, /XFERMODE, /MESSAGE, /SORT, /REVERSE, /ASCENDING. In other words, only file selection switches are meaningful with /ARRAY: /FILES, /DIRECTORIES, /ALL, /DOTFILES, /NOBACKUP, /RECURSIVE, /SMALLER, /LARGER, /AFTER, /BEFORE, /EXCEPT, etc. The resulting array has the number of files (n) as its 0th element, and the filenames in elements 1 through n Example: dir /array:&a /files /nobackup /after:19990101 /larger:10000 [ab]* show array &a /FILES Only show regular files. /DIRECTORIES Only show directories. /BACKUP In UNIX, OS-9, K-95, and other versions that support SET FILE COLLISION BACKUP and create backup files by appending .~n~ to the filename (where "n" is a number), /BACKUP means to include these files in directory listings. This is the default. /NOBACKUP This is the opposite of /BACKUP: that is, do not include backup files in the listing. /BRIEF List filenames only; use a compact format, as many filenames as will fit across the screen (based on the longest name). A brief listing is always sorted alphabetically. /VERBOSE List one file per line, and include date, size, and (in UNIX only) permissions of each file. This is the opposite of /BRIEF, and is the default. /PAGE Pause at the end of each screenful and give a "more?" prompt, even if SET COMMAND MORE-PROMPTING is OFF. /NOPAGE Don't pause at the end of each screenful and give a "more?" prompt, even if SET COMMAND MORE-PROMPTING is ON. If neither /PAGE or /NOPAGE is given, paging is according to the prevailing COMMAND MORE-PROMPTING setting (which can be displayed with SHOW COMMAND). /ENGLISHDATE Show dates in dd-mmm-yyyy format; mmm is the first three letters of the English month name. /ISODATE Show dates in yyyy-mm-dd format; mm is the month number, 1-12. This is the opposite of /ENGLISHDATE, and is the default. /HEADINGS Print a heading before the listing and a summary at the end. /NOHEADINGS Don't print a heading before the listing or a summary at the end. This is the opposite of /HEADINGS, and is the default. /XFERMODE Only in Kermit programs that support SET FILE PATTERNS. If this switch is included, and the filename matches any FILE BINARY-PATTERN ([473]Section 4.3), "(B)" is printed after the filename; otherwise, if it matches a FILE TEXT-PATTERN, "(T)" is printed. /NOXFERMODE Don't display transfer-mode indicators. This is the opposite of /XFERMODE and is the default. /RECURSIVE Show files not only in the given directory, but also in its subdirectories (if any), their subdirectories, etc. /NORECURSIVE Don't show files in subdirectories. This is the opposite of /RECURSIVE, and is the default. /MESSAGE:text This lets you specify a short text string to be appended to the end of each directory listing line (a space is supplied automatically). If the text contains any spaces, enclose it in braces, e.g. /MESSAGE:{two words}. /NOMESSAGE Don't append any message to the end of each directory listing line (default). /SORT:[{NAME,SIZE,DATE}] Sort the listing by name, size, or date. If the /SORT switch is given but the "sort-by" keyword is omitted, the listing is sorted by name. /SORT:NAME /ASCENDING (alphabetic sort by name) is the default. /NOSORT Don't sort the listing. Files are listed in whatever order they are supplied by the operating system, e.g. inode order in UNIX. /REVERSE If the /SORT switch is given, reverse the order of the sort. Synonym: /DESCENDING. /ASCENDING If the /SORT switch is given, sort the listing in normal order. This is the opposite of /REVERSE and is the default. Note that most of the DIRECTORY-specific switches come in pairs, in which one member of a pair (e.g. /NOHEADINGS) is the opposite of the other (e.g. /HEADINGS). If you always want to use certain options, you can set them with the SET OPTIONS DIRECTORY command ([474]Section 1.5.5). Use SHOW OPTIONS to list the options currently in effect. To make the desired options apply every time you run C-Kermit, put a SET OPTIONS DIRECTORY command in your C-Kermit customization file, specifying the desired options. Options set in this manner apply to every subsequent DIRECTORY command. Of course, if you include switches in a DIRECTORY command, these override any defaults, built-in or custom. Example: DIRECTORY ; Use "factory defaults" SET OPTIONS DIRECTORY /SORT:SIZE /REVERSE /HEADINGS ; Customize defaults DIRECTORY ; Use customized defaults DIR /SORT:NAME ; Override customized default SORT key SET OPT DIR /RECURS ; Add /RECURSIVE to customized defaults DIR /ASCEND ; Override customized default SORT order Notes: * Only a single sort key is supported; there is presently no way to have multiple sort keys. * If the /BRIEF switch is given, all other switches (except /[NO]RECURSIVE, /[NO]DOTFILES, /DIRECTORIES, /FILES, and /ALL) are ignored. * /SORT:anything gives incorrect results if any files have lengths greater than 10 digits (i.e. that are more than 9999999999 bytes long, i.e. if they are 10GB or more in size) because the overlong length field causes the date and name fields to be misaligned. * /SORT:NAME is redundant in VMS since VMS returns filenames in alphabetic order anyway. * /SORT:NAME ignores alphabetic case on platforms where case does not matter in filenames, but this works only for unaccented Roman letters A-Z. * /SORT:NAME is currently based on code values, and so works fine for ASCII, but will probably produce unexpected results for files with non-ASCII or 8-bit characters in their names. (Locale-based sorting raises rather significant issues of portability, size, performance, etc.) * /SORT:DATE works right only for ISO-format dates, not English ones. * /SORT:SIZE sorts the size field lexically. On some platforms (e.g. Windows), the size of a directory file is listed as "

" rather than as a number; in this case, the "" files are gathered at the end (or beginning, depending on the sort order) of the listing. * /RECURSIVE is accepted but ignored in AOS/VS. Use the normal system-specific filespec notation, e.g. "dir #.txt". * /RECURSIVE has no affect when a full, absolute pathname is given; e.g. "dir /recursive /tmp/foo" (where "foo" is a regular file) only shows the "/tmp/foo" file. If you want to see all "foo" files in the /tmp tree, do "cd /tmp" and then "dir /recursive foo". * If a file size of -1 is shown, or date-time of 0000-00-00 00:00:00, this means the file was located, but access to information about the file was denied to C-Kermit. * In VMS, if FOO.DIR;1 is a directory within your current directory, "directory foo" and "directory [.foo]" list the files in the [.FOO] subdirectory, but "directory foo.dir" lists the directory file itself; similarly for "*.dir" versus "[.*]", etc. * In UNIX, if "foo" is a directory within your current directory, "directory foo" lists the files in the foo directory. If you want to list the foo directory file itself, put an asterisk at the end: "dir foo*". Hint: How to find the biggest files in a directory tree: cd xxx ; (root of tree) directory /sort:size /recursive /reverse /dotfiles /page Another hint: If you often use several different directory-listing formats, define macro shortcuts for them: DEFINE WD DIRECTORY /SORT:DATE /REVERSE \%* ; Reverse chronological order DEFINE SD DIRECTORY /SORT:SIZE /REVERSE \%* ; Reverse order of size DEFINE ND DIRECTORY /SORT:NAME /ASCEND \%* ; Alphabetical by name DEFINE DL DIR /DIR /SORT:NAME /ASCEND \%* ; Alphabetical directory list Put these definitions in your C-Kermit customization file. Note that "\%*" ([475]Section 7.5) in these definitions lets you include other switches in your macro invocations, e.g.: wd /headings *.txt Of course you can still access your external directory listing program by using RUN or "!", e.g. in VMS: run directory /size/date/protection/except=*.obj oofa.*;0 or: !dir /size/date/prot/exc=*.obj oofa.*;0 In UNIX, use "!ls" or just "ls" (which is a special synonym for "!ls"). 4.5.2. The CD and BACK Commands In C-Kermit 7.0, the CD command has a new friend, the BACK command. BACK means "CD to my previous current directory". A second BACK brings you back to where you were before the first one; thus successive BACK commands switch back and forth between two directories. 4.5.2.1. Parsing Improvements The CD command, as well as other commands that parse a directory name, were changed in 7.0 to provide all the expected functions: completion on Tab or Esc, directory-name lists on ?, etc. Other affected commands include SET SERVER GET-PATH, SET TEMP-DIRECTORY, SET FILE DOWNLOAD-DIRECTORY, and SPACE. CD and REMOTE CD also now work with logical names. In VMS, the situation is a bit complicated since a directory name can look like "DEV:", "[FOO.BAR]", "DEV:[FOO.BAR]", "[FOO]BAR.DIR;1", etc. Completion and ?-help might not always work, but they do in many cases. Examples: cd ? Lists all subdirectories of the current directory cd []? Ditto cd k? Ditto, but only those starting with K cd [foo]? Lists all subdirectories of the [FOO] directory cd [-]? Lists all subdirectories of the superior directory cd [--]? Lists all subdirectories of the directory 2 levels up cd [...]? Lists all directories below the current one cd [foo.? Does not work. C-Kermit allows all of the following in VMS: cd bar CD to subdirectory BAR of the current directory cd .bar Ditto cd [.bar] Ditto cd bar.dir etc... cd bar.dir; cd bar.dir;1 cd [foo.bar] cd cd bar.baz This can go more than 1 level deep... cd dir: (where logical name DIR is defined as [FOO.BAR]) As well as the following: cd .. Go up one level as in UNIX cd . The current directory cd My login directory Note that "cd -" (go up one level) does not work as expected, because "-" is Kermit's command continuation character. However, "cd [-]", and " cd {-}" have the desired effect (and so does "cd ..", which is easier to type). 4.5.2.2. The CDPATH The CD command in the UNIX, Windows, OS/2, and VMS versions of C-Kermit, as of version 6.1 / 1.1.12, searches the CDPATH for the given directory, if it is not absolute and if a CDPATH environment variable is defined. Example (in UNIX ksh or bash): $ export CDPATH=$HOME:$HOME/kermit:/tmp Now if you give a "cd xxx" command, no matter what your current directory is, if the "xxx" directory is not a subdirectory of your current directory, then the xxx subdirectory of your home directory is used or if that does not exist, then the xxx subdirectory of the kermit subdirectory of your home directory is used or if that does not exist, then /tmp/xxx is used. This is how the ksh "cd" command works, and now the C-Kermit CD command works the same way. In VMS, you can define CDPATH to be a list of directories that contain actual directory delimiters, and/or logical names representing directories, using commas to separate them, e.g.: $ define cdpath [HOME],[SOMEOTHERDIR],[HOME.MISC] $ define cdpath SYS$LOGIN:,DISK1:[HOME],DISK2:[SCRATCH.IVAN] Example: $ define cdpath SYS$LOGIN:,[IVAN],[OLAF],[OLGA.MISC] $ kermit DISK1:[OLGA] C-Kermit> cd blah tries the BLAH subdirectory of the user's login directory, then [OLGA.BLAH], [IVAN.BLAH], [OLAF.BLAH], and [OLGA.MISC.BLAH], in that order, using the first one it finds, failing if it finds none. In C-Kermit 7.0, you may also set the CDPATH from the Kermit prompt: SET CD PATH path Allows the CD PATH to be set from within C-Kermit. SHOW CD shows the CD path and all other information relevant to the CD command. 4.5.2.3. CD Messages Whenever you change directory, you can have C-Kermit display a "Read Me" file from the new directory automatically. The commands are: SET CD MESSAGE { ON, OFF, FILE list } ON enables this feature; OFF (the default) disables it. File lets you specify the name of the "Read Me" file. A list of names to look for can be given in the following format: {{name1}{name2}{name3}{...}} e.g.: SET SERVER CD-MESSAGE FILE {{./.readme}{README.TXT}{READ.ME}} The default list of CD-message files is system dependent. SHOW CD shows your current directory, previous directory, CD path, and CD message info. 4.5.3. Creating and Removing Directories The MKDIR command now allows you to create multiple directories at once: C-Kermit> mkdir a/b/c/d creates the directory a in the current directory (if it doesn't exist already), and then creates subdirectory b in the a directory (if it didn't exist already), and so on. If you use MKDIR to try to create a directory that already exists, C-Kermit will print a warning ("?Directory already exists"), but the MKDIR command will still succeed. If you want to avoid the warning message, use IF DIRECTORY first to check if the directory already exists. The RMDIR command, however, will not remove more than one directory, nor will it remove a directory that contains any files. (There is, as yet, no RMDIR /RECURSIVE command, although one might be added later.) In VMS, these commands (like CD) are more forgiving of your syntax than is the DCL command shell; "mkdir oofa" is equivalent to "mkdir [.oofa]" and so on. Also in VMS, you'll find that C-Kermit's RMDIR command is easier than deleting a directory in DCL, since it automatically first gives it owner delete permission if you are the owner. 4.5.4. The DELETE and PURGE Commands The DELETE command now offers a selection of switches, and has a new companion, the PURGE command. First, DELETE: DELETE [ switches... ] filespec Deletes the file or files that match the filespec, which may contain wildcards ([476]Section 4.9). Optional switches include the standard file-selection switches presented in [477]Section 1.5.4, plus: /ASK Before deleting each file, ask permission interactively. Answers are Yes or OK (delete the file), No (don't delete it), or Quit (stop executing the DELETE command). /NOASK Don't ask permission to delete each file. /LIST List each file and show whether it was deleted. Synonyms: /LOG, /VERBOSE. /NOLIST Don't list files while deleting them. Synonyms: /NOLOG, /QUIET. /HEADING Print a heading and summary line. /NOHEADING Don't print a heading and summary line. /PAGE When listing, pause at the end of each screenful and give the "More?" prompt. If you reply "n" (no), the DELETE command terminates. /SIMULATE Do everything implied by the given switches and filespec, except do not actually delete any files. This lets you preview which files would be deleted; implies /LIST. Now the PURGE command: PURGE [ switches... ] [ filespec ] (VMS only) Runs the DCL PURGE command. Switches and filespec, if any, are passed directly to DCL without parsing or verification. Deletes excess versions of the given (or all) files. The rest of this section does not apply to VMS. PURGE [ switches... ] [ filespec ] (UNIX only) Deletes "backup files" that match the filespec, which may contain wildcards ([478]Section 4.9). If no filespec is given, all backup files in the current directory are selected (subject to modification by any switches). Do not include backup notation in the filespec. Explanation: To avoid destroying preexisting files when a new file arrives that has the same name, C-Kermit backs up the old file by appending a "backup number" to its name. In UNIX, the backup suffix consists of a period, a tilde, a number, and another tilde. For example, if a file called oofa.txt exists and a new oofa.txt file arrives, the original is renamed to oofa.txt.~1~. If another oofa.txt file arrives, the existing one is renamed to oofa.txt.~2~. And so on. This system is compatible with the one used by EMACS. Thus over time, if you receive a lot of files with C-Kermit or edit them with EMACS, backup files can build up. The new PURGE command lets you clean out accumulated backup files: Optional switches include the standard file-selection switches presented in [479]Section 1.5.4, plus all the switches listed above for the DELETE command, plus: /KEEP:n Retains the n most recent (highest-numbered) backup files for each file. For example, if oofa.txt, oofa.txt.~1~, oofa.txt.~2~, oofa.txt.~10~, oofa.txt.~12~, and oofa.txt.~100~ exist, "purge /keep:2 oofa.txt" deletes oofa.txt.~1~, oofa.txt.~2~, and oofa.txt.~10~, and keeps oofa.txt, oofa.txt.~12~, and oofa.txt.~100~. If /KEEP is given without a number, one (the highest numbered) backup file is kept. CAUTION: The PURGE command should be used only when *.~*~ files truly are backup files. This is the case for EMACS, and it is the DEFAULT for C-Kermit. However, if C-Kermit's FILE COLLISION has been set to RENAME, newly received files will look like backup files. In that case, don't use the PURGE command or you'll be removing new files rather than old ones. (Use SHOW FILE to find the FILE COLLISION setting.) The PURGE command is presently available only in UNIX. The command succeeds if it deleted any files, or if it deleted no files but there were no errors. It fails if it deleted no files and there were errors (i.e. deletion was attempted but failed). In VMS, backup file versions are handled automatically by the OS, and a PURGE command can be used at the VMS prompt to clean them up. If you want certain switches to be supplied automatically with each DELETE or PURGE command, you can set them with SET OPTIONS ([480]Section 1.5.5) and you can display any such settings with SHOW OPTIONS. Of course you can override them on a per-command basis by including switches in your PURGE or DELETE command. Also see SET FILE COLLISION, SHOW FILE, SEND /NOBACKUP, SET SEND BACKUP, and DIRECTORY /[NO]BACKUP. 4.6. Starting the Remote Kermit Server Automatically As noted on pages 275-276 of [481]Using C-Kermit 2nd edition, you can have Kermit send "kermit receive" commands automatically when it is in local mode and you give a SEND or similar command, to start the remote Kermit receiver in case it is not already started. The "kermit receive" commands are specified by: SET PROTOCOL KERMIT binary-receive-command text-receive-command As of version 7.0, a Kermit protocol option has been added to send a string to the host in advance of any Kermit packets when you give a GET-class or REMOTE command. This will switch the remote C-Kermit into the appropriate mode or, if the remote system is at a system command (shell) prompt, execute the string on the remote system. The new syntax of the SET PROTOCOL KERMIT command is: SET PROTOCOL KERMIT [ s1 [ s2 [ s3 ] ] ] where: Default Meaning s1 {kermit -ir} Remote "kermit receive in binary mode" command. s2 {kermit -r} Remote "kermit receive in text mode" command. s3 {kermit -x} Remote "start kermit server" command. NOTE: If the remote Kermit is 6.0, the following are recommended for fast startup and high-performance file transfer (see Appendix I in [482]Using C-Kermit, second Edition, for command-line options): s1 kermit -YQir (Kermit receive binary, skip init file, fast.) s2 kermit -YQTr (Kermit receive text, skip init file, fast.) s3 kermit -YQx (Kermit server, skip init file, fast.) If the remote is C-Kermit 7.0 or later, change the -x option (enter server mode) to -O (uppercase letter O), which means "enter server mode for One transaction only); this way, it is not stuck in server after the transfer. Also note that the Q is redundant in version 7.0, since fast Kermit protocol settings are now the default. Note that in case the C-Kermit executable is called "wermit" or "ckermit" you can change "kermit" in the strings above to "wermit" or "ckermit" and C-Kermit 7.0 or later will recognize these as synonyms for "kermit", in case it is at its command prompt when one of these strings is sent to it. 4.7. File-Transfer Command Switches Over the years, various new methods of transferring a file have accumulated, until we had, in addition to the SEND command, also MOVE (send and then delete), MAIL (send as email), REMOTE PRINT (send to be printed), CSEND (send the output of a command), PSEND (send a part of a file), BSEND (send in binary mode), RESEND (resume an interrupted SEND), etc etc. Similarly: GET, REGET, CGET, RETRIEVE, and so on. Not only is it confusing to have different names for these commands, many of which are not real words, but this also does not allow all combinations, like "send a file as mail, then delete it". In C-Kermit 7.0, the SEND, GET, and RECEIVE commands were restructured to accept modifier switches (switches are explained in [483]Section 1.5). 4.7.1. SEND Command Switches Without switches, the SEND command still works exactly as before: send oofa.txt ; send a single file send oofa.* ; send multiple files send oofa.txt x.x ; send oofa.txt as x.x (tell receiver its name is x.x) send ; send from SEND-LIST But now the following modifier switches may be included between "send" and the filename. Zero, one, two, or more switches may be included in any combination that makes sense. Switch names (such as /BINARY) can be abbreviated, just like any other keywords. Most of these switches work only when using Kermit protocol (/TEXT and /BINARY are the exceptions). /AFTER:date-time Specifies that only those files modified (or, in VMS, created) after the given date-time (see [484]Section 1.6) are to be sent. Examples: send /text /after:{2-Feb-1997 10:28:30} *.txt send /text /after:\fdate(oofa.txt) *.txt Synonym: /SINCE. /ARRAY:arrayname Specifies that instead of sending a file, C-Kermit is to send the contents of the given array. Since an array does not have a filename, you should include an /AS-NAME switch to specify the name under which the array is to be sent (if you do not, the name "_array_x_" is used, where 'x' is replaced by the array designator). See [485]section 7.10 for array-name syntax. As noted in that section, you can also include a range to have a segment of the array sent, rather than the whole thing; for example: "send /array:&a[100:199]". It is strongly recommended that you accompany the /ARRAY switch with a /TEXT or /BINARY switch to force the desired transfer mode, since otherwise the various automatic mechanisms might switch to binary mode when you really wanted text, or vice versa. In text mode a line terminator is added to the end of each array element, but not in binary mode. For details and examples see [486]Section 7.10.11. /AS-NAME:text Specifies "text" as the name to send the file under. You can also still specify the as-name as the second filename on the SEND command line. The following two commands are equivalent: send oofa.txt oofa.new send /as:oofa.new oofa.txt /BEFORE:date-time Specifies that only those files modified (or, in VMS, created) before the given date-time ([487]Section 1.6) are to be sent. /BINARY Performs this transfer in binary mode without affecting the global transfer mode, overriding not only the FILE TYPE and TRANSFER MODE settings, but also the FILE PATTERN setting, but for this SEND command only. In other words, SEND /BINARY means what it says: send the file in binary mode, regardless of any other settings. Example: set file type text ; Set global transfer mode to text send /binary oofa.zip ; Send a file in binary send oofa.txt ; This one is sent in text mode /COMMAND SEND /COMMAND is equivalent to CSEND ([488]Section 4.2.2) -- it says to send the output from a command, rather than the contents of a file. The first "filename" on the SEND command line is interpreted as the name of a command; the second (if any) is the as-name. Examples: send /command {grep Sunday oofa.txt} sunday.txt send /as-name:sunday.txt /command {grep Sunday oofa.txt} send /bin /command {tar cf - . | gzip -c} {!gunzip -c | tar xf -} /DELETE Deletes the file (or each file in the group) after it has been sent successfully (but does not delete it if it was not sent successfully). SEND /DELETE is equivalent to MOVE. Has no effect when used with /COMMAND. Example: send /delete *.log /DOTFILES (UNIX and OS-9 only) Normally files whose names begin with "." are skipped when matching wildcards that do not also begin with ".". Include /DOTFILES to force these files to be included too. /RECURSIVE Descend the through the directory tree when locating files to send. Automatically sets /PATHNAMES:RELATIVE. Explained in [489]Section 4.11 . /EXCEPT:pattern See [490]Section 1.5.4. /NOBACKUP This means to skip backup files when sending, even if they match the SEND file specification. This is equivalent to using SEND /EXCEPT and including *.~[0-9]*~ in the exception list (or *.~*~ if Kermit was built without pattern-matching support; see [491]Section 4.9.1). Including this switch is equivalent to giving SET SEND BACKUP OFF ([492]Section 4.0.6) prior to SEND, except its effect is local to the SEND command with which it was given. /NODOTFILES The opposite of /DOTFILES (q.v.) /FILENAMES:{CONVERTED,LITERAL} Use this switch to override the current global SET FILE NAMES setting for this transfer only. /FILTER:command This specifies a filter to pass the file through before sending it. See the [493]section on file-transfer pipes and filters. The /FILTER switch applies only to the file-transfer command it is given with; it does not affect the global SEND FILTER setting, if any. /IMAGE VMS: Sends in image mode. Non-VMS: same as /BINARY. /LABELED VMS and OS/2 only: Sends in labeled mode. /LARGER-THAN:number Specifies that only those files that are longer than the given number of bytes are to be sent. /LISTFILE:filename Specifies that the files to be sent are listed in a file with the given filename. The file contains one filename per line. These filenames are not checked in any way; each filename is taken and does not use or depend on any Kermit-specific syntax. In particular, backslashes are not treated specially, leading and trailing spaces are not stripped, etc. However, if a filename contains wildcards, they are expanded. Example: If a file named files.txt contains the following lines: blah.txt oofa* x.x (but without leading or trailing spaces), then the C-Kermit command "send /listfile:files.txt" will send the files blah.txt, x.x, and all files whose names start with "oofa", assuming the files exist and are readable. The /LISTFILE switch, can, of course, be used with other switches when it makes sense, for example, /EXCEPT, /BINARY, /AFTER, /SMALLER, /MOVE-TO, /DELETE, /AS-NAME with a template, etc. /MAIL:address Sends the file as e-mail to the given address or addresses. "send /mail:address filename" is equivalent to "mail filename address". You can include multiple addresses separated by commas. Examples: send /mail:kermit-support@columbia.edu packet.log send /mail:cmg,fdc,jrd oofa.txt As with any switch argument, if the address or address list contains any spaces, you must enclose it in braces. The format of the addresses must agree with that understood by the mail-sending program on the receiver's computer. /MOVE-TO:directory-name Specifies that after each (or the only) source file is sent successfully, and ONLY if it is sent successfully, it should be moved to the named directory. If the directory name contains spaces, enclose it in braces. If the directory does not exist, it is created if possible; if it can't be created, the command fails and an error message is printed. Example: send /text /move-to:/users/olga/backup/ *.txt /NOT-AFTER:date-time Specifies that only those files modified at or before the given date and time are to be sent. /NOT-BEFORE:date-time Specifies that only those files modified at or after the given date and time are to be sent. /PATHNAMES:{OFF,ABSOLUTE,RELATIVE} Use this switch to override the current global SET SEND PATHNAMES setting for this transfer only. /PATHNAMES:ABSOLUTE or RELATIVE also sets /FILENAMES:LITERAL (also for this transfer only) since pathnames are not sent otherwise. /RENAME-TO:text Specifies that after the (or each) source file is sent successfully, and ONLY if it is sent successfully, it should be renamed to the name given. If the name contains spaces, enclose it in braces. If a file group is being sent, then the "text" must contain a variable reference such as \v(filename) (see [494]Section 4.1). Example: send /rename-to:ok_\v(filename) *.* This sends each file in the current directory and if it was sent successfully, changes its name to begin with "ok_". /SMALLER-THAN:number Specifies that only those files that are smaller than the given number of bytes are to be sent. /SUBJECT:text Subject for email. Actually, this is just a synonym for /AS-NAME. If the text includes spaces, you must enclose it in braces. If you don't specify a subject (or as-name), the name of the file is used as the subject. Example: send /mail:kermit-support@columbia.edu /subj:{As requested} packet.log /PRINT:options Sends the file to be printed, optionally specifying options for the printer. Equivalent to REMOTE PRINT filename options. Examples: send /print oofa.txt ; No options. send /print:/copies=3 oofa.txt ; "/copies=3" is a VMS PRINT switch. send /print:-#3 oofa.txt ; "-#3" is a UNIX lpr switch. /PROTOCOL:name Uses the given protocol to send the file (Kermit, Zmodem, etc) for this transfer without changing global protocol. Only available in Kermit 95, UNIX, and OS-9. Example: set protocol kermit ; Set global protocol send /proto:zmodem /bin oofa.zip ; Send just this file with Zmodem send oofa.txt ; This file is sent with Kermit /QUIET When sending in local mode, this suppresses the file-transfer display. /RECOVER Used to recover from a previously interrupted transfer; SEND /RECOVER is equivalent to RESEND. Recovery only works in binary mode; SEND /RECOVER and RESEND include an implied /BINARY switch. Even then, recovery will successful only if (a) the original (interrupted) transfer was also in binary mode, or (b) if it was in text mode, the two Kermit programs run on platforms where text-mode transfers are not length-changing. /STARTING:number Starts sending the file from the given byte position. SEND /STARTING:n filename is equivalent to PSEND filename n. /TEXT Performs this transfer in text mode without affecting the global transfer mode, overriding not only the FILE TYPE and TRANSFER MODE settings, but also the FILE PATTERN setting, for this SEND command only. In other words, SEND /TEXT really send the file in text mode, regardless of any other settings or negotiations. About mail... Refer to [495]Section 4.7.1. The same rules apply as for file transfer. If you are mailing multiple files, you can't use an as-name (in this case, a subject) unless it contains replacement variables like \v(filenum). For example, if you: send /mail:somebody@xyz.com *.txt Then each file will arrive as a separate email message with its name as the subject. But if you: send /mail:somebody@xyz.com /subject:{Here is a file} *.txt Then each file message will have the same subject, which is probably not what you want. You can get around this with constructions like: send /mail:somebody@xyz.com /subject:{Here is \v(filename)} *.txt which embed the filename in the subject. The MOVE, CSEND, MAIL, and RESEND commands now also accept the same switches. And the switches are also operative when sending from a SEND-LIST (see [496]Using C-Kermit, 2nd Ed, pp.191-192), so, for example, it is now possible to SEND /PRINT or SEND /MAIL from a SEND-LIST. The MSEND and MMOVE commands also take switches, but not all of them. With these commands, which take an arbitrary list of filespecs, you can use /BINARY, /DELETE, /MAIL, /PRINT, /PROTOCOL, /QUIET, /RECOVER, and /TEXT (and /IMAGE or /LABELED, depending on the platform). MMOVE is equivalent to MSEND /DELETE. (If you want to send a group of files, but in mixed transfer modes with per-file as-names, use ADD SEND-LIST and then SEND.) The MSEND/MMOVE switches come before the filenames, and apply to all of them: msend /print /text *.log oofa.txt /etc/motd If you type any of these commands (SEND, CSEND, MSEND, etc) followed by a question mark (?), you will see a list of the switches you can use. If you want to see a list of filenames, you'll need to type something like "send ./?" (UNIX, OS/2, Windows, etc), or "send []?" (VMS), etc. Of course, you can also type pieces of a filename (anything that does not start with "/") and then "?" to get a list of filenames that start that way; e.g. "send x.?" still works as before. In UNIX, where "/" is also the directory separator, there is usually no ambiguity between a fully-specified pathname and a switch, except when a file in the root directory has the same name as a switch (as noted in [497]Section 1.5): send /etc/motd ; Works as expected send /command ; ??? The second example interprets "/command" as a switch, not a filename. To send a file actually called "command" in the root directory, use: send {/command} or other system-dependent forms such as //command, /./command, c:/command, etc, or cd to / and then "send command". 4.7.2. GET Command Switches Without switches, the GET command still works about the same as before: get oofa.txt ; GET a single file get oofa.* ; GET multiple files However, the mechanism for including an "as-name" has changed. Previously, in order to include an as-name, you were required to use the "multiline" form of GET: get remote-filespec local-name This was because the remote filespec might contain spaces, and so there would be no good way of telling where it ended and where the local name began, e.g: get profile exec a foo But now since we can use {braces} for grouping, we don't need the multiline GET form any more, and in fact, support for it has been removed. If you give a GET command by itself on a line, it fails and an error message is printed. The new form is: GET [ switches... ] remote-name [ local-name ] Ask the server to send the file whose name is remote-name. If the optional local-name is given, store it locally under this name. If the remote-name or local-name contains spaces, they must be enclosed in braces: get {profile exec a} foo get oofa.txt {~/My Files/Oofa text} If you want to give a list of remote file specifications, use the MGET command: MGET [ switches... ] remote-name [ remote-name [ remote-name ... ] ] Ask the server to send the files whose names are given. Now you can also include modifier switches between GET or MGET and the remote-name; most of the same switches as SEND: /AS-NAME:text Specifies "text" as the name to store the incoming file under. (This switch is not available for MGET.) You can also still specify the as-name as the second filename on the GET command line. The following two commands are equivalent: get oofa.txt oofa.new get /as:oofa.new oofa.txt /BINARY Tells the server to send the given file(s) in binary mode without affecting the global transfer mode. Example: set file type text ; Set global transfer mode to text get /binary oofa.zip ; get a file in binary mode get oofa.txt ; This one is transferred in text mode Or, perhaps more to the point: get /binary foo.txt ; where "*.txt" is a text-pattern This has the expected effect only if the server is C-Kermit 7.0 or later or K95 1.1.19 or later. /COMMAND GET /COMMAND is equivalent to CGET ([498]Section 4.2.2) -- it says to receive the file into the standard input of a command, rather than saving it on disk. The /AS-NAME or the second "filename" on the GET command line is interpreted as the name of a command. Examples: get /command sunday.txt {grep Sunday oofa.txt} get /command /as-name:{grep Sunday oofa.txt} sunday.txt get /bin /command {!gunzip -c | tar xf -} {tar cf - . | gzip -c} /DELETE Asks the Kermit server to delete the file (or each file in the group) after it has been transferred successfully (but not to delete it if it was not sent successfully). GET /DELETE is equivalent to RETRIEVE. Example: get /delete *.log /EXCEPT:pattern Specifies that any files whose names match the pattern, which can be a regular filename, or may contain "*" and/or "?" metacharacters, are to be refused upon arrival. To specify multiple patterns (up to 8), use outer braces around the group, and inner braces around each pattern: /EXCEPT:{{pattern1}{pattern2}...} See the description of SEND /EXCEPT in [499]Section 4.7.1 for examples, etc. Refusal is accomplished using the Attribute Rejection mechanism (reason "name"), which works only when Attribute packets have been successfully negotiated. /FILENAMES:{CONVERTED,LITERAL} Use this switch to override the current global SET FILE NAMES setting for this transfer only. /FILTER:command This specifies a filter to pass the incoming file through before writing to disk. See the [500]section on file-transfer pipes and filters. The /FILTER switch applies only to the file-transfer command it is given with; it does not affect the global RECEIVE FILTER setting, if any. /IMAGE VMS: Transfer in image mode. Non-VMS: same as /BINARY. /LABELED VMS and OS/2 only: Specifies labeled transfer mode. /MOVE-TO:directory This tells C-Kermit to move each file that is successfully received to the given directory. Files that are not successfully received are not moved. By default, files are not moved. /PATHNAMES:{OFF,ABSOLUTE,RELATIVE,AUTO} Use this switch to override the current global SET RECEIVE PATHNAMES setting for this transfer only. /PATHNAMES:ABSOLUTE or RELATIVE also sets /FILENAMES:LITERAL (also for this transfer only) since incoming pathnames would not be treated as pathnames otherwise. See [501]Section 4.10. /QUIET When sending in local mode, this suppresses the file-transfer display. /RECOVER Used to recover from a previously interrupted transfer; GET /RECOVER is equivalent to REGET. Recovery only works in binary mode; SEND /RECOVER and RESEND include an implied /BINARY switch. Even then, recovery will successful only if (a) the original (interrupted) transfer was also in binary mode, or (b) if it was in text mode, the two Kermit programs run on platforms where text-mode transfers are not length-changing. /RECURSIVE Tells the server that the GET file specification applies recursively. This switch also automatically sets /PATHNAMES:RELATIVE in both the server AND the client. When used in conjunction with /DELETE, this "moves" a directory tree from the server's computer to the client's computer (except that only regular files are deleted from the server's computer, not directories; thus the original directories will be left, but will contain no files). Note that all servers that support /RECURSIVE do not necessarily do so in combination with other switches, such as /RECOVER. (Servers that do include C-Kermit 7.0 and later, K95 1.1.19 and later.) /RENAME-TO:string This tells C-Kermit to rename each file that is successfully received to the given string. Files that are not successfully received are not renamed. By default, files are not renamed. The string can be a literal string, which is appropriate when only one file is being received, or it can contain one or more variables that are to be evaluated at the time each file is received, such as \v(filename), \v(filenumber), \v(ntime), \v(pid), \v(user), etc. WARNING: if you give a literal string and more than one file arrives, each incoming file will be given the same name (but SET FILE COLLISION BACKUP or RENAME can be used to keep the incoming files from overwriting each other). /TEXT Tells the server to perform this transfer in text mode without affecting its global transfer mode. See /BINARY for additional info. The /MAIL and /PRINT options are not available (as they are for SEND), but you can use /COMMAND to achieve the same effect, as in these UNIX examples: get /command oofa.txt {mail kermit@columbia.edu} get /command oofa.txt lpr In OS/2 or Windows, you can GET and print like this: get oofa.txt prn The CGET, REGET, RETRIEVE commands also accept the same switches as GET. CGET automatically sets /COMMAND; REGET automatically sets /RECOVER and /BINARY, and RETRIEVE automatically sets /DELETE. 4.7.3. RECEIVE Command Switches Without switches, the RECEIVE command still works as before: receive ; Receives files under their own names receive /tmp ; Ditto, but into the /tmp directory r ; Same as "receive" receive foo.txt ; Receives a file and renames to foo.txt Now you can also include modifier switches may be included between "receive" and the as-name; most of the same switches as GET: /AS-NAME:text Specifies "text" as the name to store the incoming file under. You can also still specify the as-name as a filename on the command line. The following two commands are equivalent: r oofa.new r /as:oofa.new /BINARY Performs this transfer in binary mode without affecting the global transfer mode. NOTE: This does not override the incoming filetype (as it does with GET), so this switch is useful only if ATTRIBUTE TYPE is OFF, or if the other Kermit does not send a TYPE (text or binary) attribute. In any case, it has no affect whatsoever on the file sender. /COMMAND RECEIVE /COMMAND is equivalent to CRECEIVE ([502]Section 4.2.2) -- it says to receive the file into the standard input of a command, rather than saving it on disk. The /AS-NAME or the "filename" on the RECEIVE command line is interpreted as the name of a command. r /command {grep Sunday oofa.txt} r /command /as-name:{grep Sunday oofa.txt} r /bin /command {tar cf - . | gzip -c} /EXCEPT:pattern Specifies that any files whose names match the pattern, which can be a regular filename, or may contain "*" and/or "?" metacharacters, are to be refused upon arrival. To specify multiple patterns (up to 8), use outer braces around the group, and inner braces around each pattern: /EXCEPT:{{pattern1}{pattern2}...} See the description of SEND /EXCEPT in [503]Section 4.7.1 for examples, etc. Refusal is accomplished using the Attribute Rejection mechanism (reason "name"), which works only when Attribute packets have been successfully negotiated. /FILENAMES:{CONVERTED,LITERAL} Use this switch to override the current global SET FILE NAMES setting for this transfer only. /FILTER:command This specifies a filter to pass the incoming file through before writing to disk. See the [504]section on file-transfer pipes and filters. The /FILTER switch applies only to the file-transfer command it is given with; it does not affect the global RECEIVE FILTER setting, if any. /IMAGE VMS: Transfer in image mode. Non-VMS: same as /BINARY. See comments under RECEIVE /BINARY. /LABELED VMS and OS/2 only: Specifies labeled transfer mode. See comments under RECEIVE /BINARY. /MOVE-TO:directory This tells C-Kermit to move each file that is successfully received to the given directory. Files that are not successfully received are not moved. By default, files are not moved. /PATHNAMES:{ABSOLUTE,RELATIVE,OFF,AUTO} Use this switch to override the current global SET RECEIVE PATHNAMES setting for this transfer only. See [505]Section 4.10. /RECURSIVE When used with the RECEIVE command, /RECURSIVE is simply a synonym for /PATHNAMES:RELATIVE. /RENAME-TO:string This tells C-Kermit to rename each file that is successfully received to the given string. Files that are not successfully received are not renamed. By default, files are not renamed. The string can be a literal string, which is appropriate when only one file is being received, or it can contain one or more variables that are to be evaluated at the time each file is received, such as \v(filename), \v(filenumber), \v(ntime), \v(pid), \v(user), etc. WARNING: if you give a literal string and more than one file arrives, each incoming file will be given the same name (but SET FILE COLLISION BACKUP or RENAME can be used to keep the incoming files from overwriting each other). /QUIET When receiving in local mode, this suppresses the file-transfer display. /TEXT Receives in text mode without affecting the global transfer mode. See comments under RECEIVE /BINARY. The /MAIL and /PRINT options are not available, but you can use /COMMAND to achieve the same effect, as in these UNIX examples: r /command {mail kermit@columbia.edu} r /command lpr In OS/2 or Windows, you can RECEIVE and print like this: receive prn The CRECEIVE command now also accepts the same switches. 4.8. Minor Kermit Protocol Improvements 4.8.1. Multiple Attribute Packets C-Kermit 7.0 now sends more than one Attribute packet if a file's attributes do not fit into a single packet of the negotiated length. If a particular attribute (such as file creation date-time) does not fit within the negotiated length (which will only happen when the negotiated length is around 20 or less), that attribute is not sent at all. 4.8.2. Very Short Packets There are certain situations where extremely short packets must be used; 20 or 30 bytes at most. This can happen when one or more devices along the communication path have very small buffers and lack an effective means of flow control. Examples are sometimes cited involving radio modems. When the maximum packet length is shorter than certain packets that would be sent, those packets are either truncated or else broken up into multiple packets. Specifically: 1. Parameter negotiation packets (I, S, and their ACKs) are truncated to the negotiated length. Any parameters that do not fit are reset to their default values. There is no provision in the Kermit protocol for fragmentation and reassembly of parameter strings. 2. File header packets (containing the filename) are simply truncated. There is no provision in the Kermit protocol for fragmentation and reassembly of filenames. 3. Attribute packets are fragmented and reassembled as described in 4.8.1 without loss of data, except in case a field will not fit at all in the negotiated length (the longest attribute is usually the date and time of file creation/modification) because of the rule that attributes may not be broken across packets. 4. Data packets and other packets are unaffected -- they can be as short as they need to be, within reason. 4.9. Wildcard / File Group Expansion "Wildcard" refers to the notation used in filenames to specify a group of files by pattern matching. 4.9.1. In UNIX C-Kermit Prior to C-Kermit 7.0, C-Kermit was capable of expanding wildcard strings containing only the "metacharacters" '*' and '?': * Matches any sequence of zero or more characters. For example: "ck*.c" matches all files whose names start with "ck" and end with ".c", including "ck.c". ? Matches any single character. For example, "ck?.c" matches all files whose names are exactly 5 characters long and start with "ck" and end with ".c". When typing commands at the prompt, you must precede any question mark to be used for matching by a backslash (\) to override the normal function of question mark, which is providing menus and file lists. C-Kermit 7.0 adds the additional features that users of ksh, csh, and bash are accustomed to: [abc] Square brackets enclosing a list of characters matches any single character in the list. Example: ckuusr.[ch] matches ckuusr.c and ckuusr.h. [a-z] Square brackets enclosing a range of characters; the hyphen separates the low and high elements of the range. For example, [a-z] matches any character from a to z. [acdm-z] Lists and ranges may be combined. This example matches a, c, d, or m through z. {string1,string2,...} Braces enclose a list of strings to be matched. For example: ck{ufio,vcon,cmai}.c matches ckufio.c, ckvcon.c, or ckcmai.c. The strings may themselves contain metacharacters, bracket lists, or indeed, other lists of strings, but (when matching filenames) they may not contain directory separators. Thus, the metacharacters in filenames (and in any other field that can be a pattern, such as the IF MATCH pattern, SEND or GET exception lists, etc) are: * ? [ { And within braces only, comma (,) is a metacharacter. To include a metacharacter in a pattern literally, precede it with a backslash '\' (or two if you are passing the pattern to a macro). Examples: send a*b ; Send all files whose names start with 'a' and end with 'b'. send a?b ; Ditto, but the name must be exactly three characters long. send a[a-z]b ; Ditto, but the second character must be a lowercase letter. send a[x\-z]b ; Ditto, except the second character must be 'x', '-', or 'y'. send a[ghi]b ; Ditto, except the second character must be 'g', 'h', or 'i'. send a[?*]b ; Ditto, except the second character must be '?' or '*'. send a[\?\*]b ; Same as previous. send *?[a-z]* ; All files with names containing at least one character ; that is followed by a lowercase letter. Or, more practically: send ck[cuw]*.[cwh] ; Send the UNIX C-Kermit source files. To refer to the C-Kermit sources files and makefile all in one filespec: {{makefile,ck[cuw]*.[cwh]}} (NOTE: if the entire pattern is a {stringlist}, you must enclose it it TWO pairs of braces, since the SEND command strips the outer brace pair, because of the "enclose in braces if the filename contains spaces" rule). If the makefile is called ckuker.mak: ck[cuw]*.{[cwh],mak} (NOTE: double braces are not needed here since the pattern does not both begin and end with a brace.) To add in all the C-Kermit text files: ck[cuw]*.{[cwh],mak,txt} All of these features can be used anywhere you would type a filename that is allowed to contain wildcards. When you are typing at the command prompt, an extra level of quoting is required for the '?' character to defeat its regular function of producing a list of files that match what you have typed so far, for example: send ck[cu]? lists all the files whose names start with ckc and cku. If you quote the question mark, it is used as a pattern-matching character, for example: send ck\?[ft]io.c sends all the file and communications i/o modules for all the platforms: ckufio.c, ckutio.c, ckvfio.c, ckvtio.c, etc. If, however, a filename actually contains a question mark and you need to refer to it on the command line, you must use three (3) backslashes. For example, if the file is actually called ck?fio.c, you would use: send ck\\\?fio.c Further notes on quoting: * A single backslash is sufficient for quoting a special character at the command prompt or in a command file. However, when passing patterns to macros you'll need double backslashes, and when referring to these patterns within the macro, you'll need to use \fcontents(\%1) (see [506]Section 1.11.5). You should enclose macro argument references in braces in case grouped arguments were passed. Example: define ismatch { if match {\fcont(\%1)} {\fcont(\%2)} { end 0 MATCH } else { end 1 NO MATCH } } ismatch ab*yz a*\\**z ; Backslash must be doubled ismatch {abc def xyz} *b*e*y* ; Braces must be used for grouping * Watch out for possible conflicts between {} in filename patterns and {} used for grouping multiple words into a single field, when the pattern has outer braces. For example, in: if match {abc xyz} {a* *z} echo THEY MATCH braces must be used to group "abc xyz" into a single string. Kermit strips off the braces before comparing the string with the pattern. Therefore: if match makefile {makefile,Makefile} echo THEY MATCH does not work, but: if match makefile {{makefile,Makefile}} echo THEY MATCH does. * If you use a pattern that has outer braces, like {*.txt,*.doc}, in a field that accepts a pattern list (like SEND /EXCEPT:xxx), you'll need to add two extra sets of outer braces: send /except:{{{*.txt,*.doc}}} *.* C-Kermit's new pattern matching capabilities are also used when C-Kermit is in server mode, so now you can send requests such as: get ck[cuw]*.[cwh] to a C-Kermit server without having to tell it to SET WILD SHELL first. Previously this would have required: mget ckc*.c ckc*.w ckc*.h cku*.c cku*.w cku*.h ckw*.c ckw*.w ckw*.h The new pattern matching features make SET WILD SHELL redundant, and barring any objections, it will eventually be phased out. (One possible reason for retaining it would be as an escape mechanism when Kermit does not understand the underlying file system.) By the way, patterns such as these are sometimes referred to as "regular expressions", but they are not quite the same. In a true regular expression (for example), "*" means "zero or more repetitions of the previous item", so (for example), "([0-9]*)" would match zero or more digits in parentheses. In Kermit (and in most shells), this matches one digit followed by zero or more characters, within parentheses. Here are some hints: * Although you can't match any sequence of digits (or letters, etc), you can match (say) 1, 2, or 3 of them in row. For example, the following pattern matches Kermit backup files (with backup numbers from 1 to 999): *.~{[1-9],[1-9][0-9],[1-9][0-9][0-9]}~ * There is presently no NOT operator, so no way to match any character or string EXCEPT the one(s) shown. In other wildcarding news... * You may now "send xxx" where "xxx" is a directory name, and this will send all the files from the directory xxx, as if you had typed "send xxx/*". You can also use the special shorthand "send ." to send all the files from the current directory. * To easily skip over backup files (the ones whose names end like .~22~) when sending, you can use SEND /NOBACKUP (see [507]Section 4.0.6 for details). * When choosing Kermit to expand wildcards, rather than the shell, you can choose whether "dot files" -- files whose names begin with ".", which are normally "invisible" -- should be matched: SET WILD KERMIT /NO-MATCH-DOT-FILES (this is the default) SET WILD KERMIT /MATCH-DOT-FILES (this allows matching of "." files) or include the /DOTFILES or /NODOTFILES switch on the command you are using, such as SEND or DIRECTORY. * Commands such as DIRECTORY and SEND allow recursive directory traversal. There are also new functions for this to use in scripts. See [508]Section 4.11 for details. When building file lists in UNIX, C-Kermit follows symbolic links. Because of this, you might encounter any or all of the following phenomena: * Multiple copies of the same file; e.g. one from its real directory and others from links to its real directory, if both the real directory and the links to it are in the wildcard expansion list. * A command might unexpectedly "hang" for a long time because an NFS link might not be responding, or the directory you are looking at contains a link to a huge directory tree (example: "directory /recursive /etc" when /etc/spool is a symlink to /var/spool, which is a large organization's incoming email directory, containing tens of thousands of subdirectories). The size of the file list that Kermit can build is limited in most C-Kermit implementations. The limit, if any, depends on the implementation. Use the SHOW FEATURES command and look in the alphabetized options list for MAXWLD to see the value. 4.9.2. In Kermit 95 Kermit 95 1.1.19 and later uses the same pattern matching syntax as in UNIX, but (as always) you will encounter numerous difficulties if you use backslash (\) as the directory separator. In any command where K95 parses filenames itself (that is, practically any file-oriented command except RUN), you can use forward slash (/) as the directory separator to avoid all the nasty conflicts. 4.9.3. In VMS, AOS/VS, OS-9, VOS, etc. Platforms other than UNIX, Windows 95/98/NT, and OS/2 have their own filename matching capabilities that are, in general, different from Kermit's built-in ones and in any case might conflict with them. For example, [] encloses directory names in VMS. Nevertheless you can still use all the pattern-matching capabilities described in [509]Section 4.9.1 by loading a file list into an array (e.g. with \ffiles(*,&a), see [510]Section 4.11.3) and then using IF MATCH on the members. 4.10. Additional Pathname Controls In version 6.0 and earlier, C-Kermit's SET { SEND, RECEIVE } PATHNAMES command had only ON and OFF as options. In version 7.0, there are more choices: SET SEND PATHNAMES OFF When sending a file, strip all disk/directory information from the name. Example: "send /usr/olga/letters/oofa.txt" sends the file as "oofa.txt". This applies to actual filenames, not to any as-name you might specify. SET SEND PATHNAMES RELATIVE When sending a file, leave the pathname on as given. For example, if your current directory is /usr/olga, "send letters/oofa.txt" sends the file as "letters/oofa.txt", not "/usr/olga/letters/oofa.txt" or "letters.txt". SET SEND PATHNAMES ABSOLUTE When sending a file, convert its name to the full, absolute local pathname. For example, if your current directory is /usr/olga, "send letters/oofa.txt" sends the file as "/usr/olga/letters/oofa.txt". NOTE: Even with this setting, device and/or node names are not included. For example, in VMS, any node or device name is stripped; in Windows or OS/2, any disk letter is stripped. SET RECEIVE PATHNAMES OFF When receiving a file, strip all disk/directory information from the name before attempting to store it. This applies to incoming filename, not to any as-name you might specify. Example: If a file arrives under the name "/usr/olga/letters/oofa.txt" it is stored simply as "oofa.txt" in your download directory or, if no download directory has been specified, in your current directory. SET RECEIVE PATHNAMES RELATIVE When receiving a file, leave the pathname on as it appears in the incoming name, but if the incoming name appears to be absolute, make it relative to your current or download directory. Examples: + "oofa.txt" is stored as "oofa.txt". + "letters/oofa.txt" is stored as "letters/oofa.txt"; the "letters" subdirectory is created if it does not already exist. + "/usr/olga/letters/oofa.txt" is stored as "usr/olga/letters/oofa.txt" in your current or download directory, and the "usr", "usr/olga", etc, directories are created if they do not exist. SET RECEIVE PATHNAMES ABSOLUTE The incoming filename is used as given. Thus it cannot be stored unless the given path (if any) already exists or can be created. In this case, node, device, or disk designations are NOT stripped, since they most likely were given explicitly by the user as an as-name, meant to be used as given. SET RECEIVE PATHNAMES AUTO This is the default, and means RELATIVE if the sender tells me it is a recursive transfer, OFF otherwise. Set FILE NAMES CONVERTED now also affects pathnames too. When PATHNAMES are RELATIVE or ABSOLUTE and FILE NAMES are CONVERTED, the file sender converts its native directory-name format to UNIX format, and the file receiver converts from UNIX format to its native one; thus UNIX format is the common intermediate representation for directory hierarchies, as it is in the ZIP/UNZIP programs (which is why ZIP archives are transportable among, UNIX, DOS, and VMS). Here's an example in which a file is sent from Windows to UNIX with relative pathnames and FILE NAMES CONVERTED: Source name Intermediate name Destination Name C:\K95\TMP\OOFA.TXT K95/TMP/OOFA.TXT k95/tmp/oofa.txt In a more complicated example, we send the same file from Windows to VMS: Source name Intermediate name Destination Name C:\K95\TMP\OOFA.TXT K95/TMP/OOFA.TXT [.K95.TMP]OOFA.TXT (Note that disk letters and device designations are always stripped when pathnames are relative). As you can imagine, as more and more directory formats are considered, this approach keeps matters simple: on each platform, Kermit must know only its own local format and the common intermediate one. In most cases, the receiver can detect which format is used automatically. 4.11. Recursive SEND and GET: Transferring Directory Trees C-Kermit 7.0 in selected versions (UNIX, VMS, VOS, AOS/VS, Windows, and OS/2 at this writing) now permits the SEND command to traverse directories "recursively" if you ask it to; that is, to send files from the current or specified directory and all of its subdirectories too, and their subdirectories, etc. (Some other commands can do this too, including DIRECTORY.) This feature is new to UNIX, Windows, VOS, and OS/2. VMS and AOS/VS have always included "wildcard" or "template" characters that allow this, and in this case, recursive directory traversal could happen behind Kermit's back, i.e. Kermit does not have to do it itself (in VMS, the notation is "[...]" or "[directory...]"; in AOS/VS is "#"). In C-Kermit 7.0, however, SEND /RECURSIVE is supported by C-Kermit itself for VMS. 4.11.1. Command-Line Options To descend a directory tree when sending files, use the -L command-line option to indicate that the send operation is to be recursive, and include a name or pattern to be sent. When giving a pattern, you should enclose it in quotes to prevent the shell from expanding it. Examples: $ kermit -Ls "/usr/olga/*" # send all of Olga's files in all her directories $ kermit -Ls foo.txt # send all foo.txt files in this directory tree $ kermit -Ls "*.txt" # send all .txt files in this directory tree $ kermit -Ls "letters/*" # send all files in the letters directory tree $ kermit -Ls letters # send all files in the letters directory tree $ kermit -Ls "*" # send all files in this directory tree $ kermit -Ls . # UNIX only: send all files in this directory tree $ kermit -s . # UNIX only: a filename of . implies -L If you let the shell expand wildcards, Kermit only sends files whose names match files in the current or given directory, because the shell replaces an unquoted wildcard expression with the list of matching files -- and the shell does not build recursive lists. Note that the "." notation for the tree rooted at the current directory is allowed only in UNIX, since in Windows and OS/2, it means "*.*" (nonrecursive). 4.11.2. The SEND /RECURSIVE Command If you include the /RECURSIVE switch in a SEND (or MOVE, or similar) command, it means to descend the current or specified directory tree searching for files whose names match the given name or pattern. Since this is not terribly useful unless you also include pathnames with the outbound files, the /RECURSIVE switch also includes an implicit /PATHNAMES:RELATIVE switch (which you can undo by including an explicit /PATHNAMES switch after the /RECURSIVE switch). Examples: SEND /RECURSIVE * Sends all of the files in the current directory and all the files in all of its subdirectories, and all of their subdirectories, etc, including their relative pathnames. Empty directories are not sent. SEND /RECURSIVE /PATHNAMES:ABSOLUTE * Sends all of the files in the current directory and all the files in all of its subdirectories, and all of their subdirectories, etc, including their absolute pathnames. SEND /RECURSIVE /PATHNAMES:OFF * Sends all of the files in the current directory and all the files in all of its subdirectories, and all of their subdirectories, etc, without pathnames. SEND /RECURSIVE /usr/olga/* Sends all of the files in the /usr/olga directory and all the files in all of its subdirectories, and all of their subdirectories, etc. SEND /RECURSIVE /usr/olga (or /usr/olga/) Same as above. If the name is a directory name (with or without a trailing slash), its files are sent, and those of its subdirectories, and their subdirectories, etc (see [511]Section 4.9). SEND /RECURSIVE /TEXT /usr/olga/*.txt As above, but only files whose names end with ".txt" are sent, and they are sent in text mode (as they would be by default anyway if SET FILE PATTERNS is ON or AUTO). SEND . UNIX only: Send all the files in the current directory. SEND /RECURSIVE . UNIX only: Sends all of the files in the current directory and all of its subdirectories, etc ([512]Section 4.9). The /RECURSIVE switch is different from most other switches in that its effect is immediate (but still local to the command in which it is given), because it determines how filenames are to be parsed. For example, "send *.txt" fails with a parse error ("No files match") if there are no *.txt files in the current directory, but "send /recursive *.txt" succeeds if there are ".txt" files anywhere in the tree rooted at the current directory. The /RECURSIVE switch also affects the file lists displayed if you type "?" in a filename field. "send ./?" lists the regular files in the current directory, but "send /recursive ./?" lists the entire directory tree rooted at the current directory. 4.11.3. The GET /RECURSIVE Command In a client/server setting, the client can also request a recursive transfer with: GET /RECURSIVE [ other switches ] remote-filespec [ local-spec ] In which remote file specification can be a directory name, a filename, a wildcard, or any combination. If the local-spec is not given (and PATHNAMES are RELATIVE), incoming files and directories go into the current local directory. If local-spec is given and is a directory, it becomes the root of the tree into which the incoming files and directories are placed. If local-spec has the syntax of a directory name (e.g. in UNIX it ends with /), C-Kermit creates the directory and then places the incoming files into it. If local-spec is a filename (not recommended), then all incoming files are stored with that name with collisions handled according to the FILE COLLISION setting. Again, the normal method for transferring directory trees uses relative pathnames, and this is the default when the sender has been given the /RECURSIVE switch. The action at the receiver depends on its RECEIVE PATHNAMES setting. The default is AUTO, meaning that if the sender tells it to expect a recursive transfer, then it should automatically switch to relative pathnames for this transfer only; otherwise it obeys the RECEIVE PATHNAMES setting of OFF, ABSOLUTE, or RELATIVE. What happens if a file arrives that has an absolute pathname, when the receiver has been told to use only relative pathnames? As a security precaution, in this case the receiver treats the name as if it was relative. For example, if a file arrives as: /usr/olga/oofa.txt The receiver creates a "usr" subdirectory in its current directory, and then an "olga" subdirectory under the "usr" subdirectory in which to store the incoming file. Suppose, however there is a sequence of directories: /usr/olga/a/b/c/d/ in which "a" contains nothing but a subdirectory "b", which in turn contains nothing but a subdirectory "c", which in turn contains nothing but a subdirectory "d", which contains nothing at all. Thus there are no files in the "/usr/olga/a/" tree, and so it is not sent, and therefore it is not reproduced on the target computer. 4.11.4. New and Changed File Functions C-Kermit 7.0 adds the following functions: \ffiles(pattern[,&a]) This function has been changed to match only regular files in the current or given directory, and to take an optional array name as a second argument (explained below). \fdirectories(pattern[,&a]) Returns the number of directories that match the given pattern. If the pattern does not include a directory, then the search is performed in the current directory. \frfiles(pattern[,&a]) Returns the number of files in the current or given directory and all of its subdirectories, and their subdirectories, etc, that match the given pattern. Warning -- this one can take quite some time if performed at the root of a large directory tree. \frdirectories(pattern[,&a]) Returns the number of directories in the current or given directory and all of its subdirectories, and their subdirectories, etc, that match the given pattern. Each of these functions builds up a list of files to be returned by the \fnextfile() function, just as \ffiles() always has done. (This can also be done with the /ARRAY switch of the DIRECTORY command; see [513]Sections 4.5.1 and [514]7.10). Each of these functions can be given an array name as an optional second argument. If an array name is supplied, the array will contain the number of files as its 0th element, and the filenames in elements 1 through last. If the array already existed, its previous contents are lost. For example, if the current directory contains two files, oofa.txt and foo.bar, then "\ffiles(*,&a)" creates an array \&a[] with a dimension of 2, containing the following elements: \&a[0] = 2 \&a[1] = oofa.txt \&a[2] = foo.bar If no files match the specification given in the first argument, the array gets a dimension of 0, which is the same as undeclaring the array. Note that the order in which the array is filled (and in which \fnextfile() returns filenames) is indeterminate (but see [515]Section 7.10.5). Here's an example that builds and prints a list of all the file whose names end in .txt in the current directory and all its descendents: asg \%n \frfiles(*.txt) declare \&a[\%n] for \%i 1 \%n 1 { asg \&a[\%i] \fnextfile() echo \flpad(\%i,4). "\&a[\%i]" } Alternatively, using the array method, and then printing the filenames in alphabetic order (see [516]Section 7.10.3 and [517]7.10.5): asg \%n \frfiles(*.txt,&a) sort &a for \%i 1 \%n 1 { echo \flpad(\%i,4). "\&a[\%i]" } Or even more simply: asg \%n \frfiles(*.txt,&a) sort &a show array &a As noted elsewhere, the file lists built by \ffiles(), \frfiles(), etc, are now "safe" in the sense that SEND and other file-related commands can reference \fnextfile() without resetting the list: set send pathnames relative for \%i 1 \frfiles(*.txt) 1 { asg \%a \fnextfile() echo Sending \%a... send \%a if fail break } Copying to an array (as shown on p.398 of [518]Using C-Kermit 2nd Ed) is no longer necessary. 4.11.5. Moving Directory Trees Between Like Systems 4.11.5.1. UNIX to UNIX Transferring a directory tree from one computer to another replicates the file sender's arrangement of files and directories on the file receiver's computer. Normally this is done using relative pathnames, since the user IDs might not be identical on the two computers. Let's say both computers are UNIX based, running C-Kermit 7.0 or later. On the sending computer (leaving out the connection details, etc): C-Kermit> cd /usr/olga C-Kermit> send /recursive . The /RECURSIVE switch tells C-Kermit to descend through the directory tree and to include relative pathnames on outbound filenames. On the receiving computer: C-Kermit> mkdir olgas-files ; Make a new directory. C-Kermit> cd olgas-files ; CD to it. C-Kermit> receive /recursive ; = /PATHNAMES:RELATIVE Each Kermit program recognizes that the other is running under UNIX and switches to binary mode and literal filenames automatically. Directories are automatically created on the receiving system as needed. File dates and permissions are automatically reproduced from source to destination. 4.11.5.2. VMS to VMS To send recursively from VMS, simply include the /RECURSIVE switch, for example at the sender: $ kermit C-Kermit> cd [olga] C-Kermit> send /recursive *.*;0 And at the receiver: C-Kermit> cd [.olga] C-Kermit> receive /recursive The notation "..." within directory brackets in VMS means "this directory and all directories below it"; the /RECURSIVE switch, when given to the sender, implies the use of "..." in the file specification so you don't have to include "..."; but it makes no difference if you do: $ kermit C-Kermit> send /recursive [olga...]*.*;0 And at the receiver: C-Kermit> cd [.olga] C-Kermit> receive /recursive In either case, since both systems recognize each other as VMS, they switch into LABELED transfer mode automatically. 4.11.6. Moving Directory Trees Between Unlike Systems There are several difficulties with recursive transfers between unlike systems: * File formats can be different, especially text files character sets and record formats. This can now be handled by using SET FILE PATTERN, SET FILE TEXT-PATTERNS, and SET FILE BINARY-PATTERNS ([519]Section 4.3). * File naming conventions are different. For example, one system might allow (and use) longer filenames than the other. You can tell Kermit how to handle file names with the normal "set file names" and "set file collision" mechanisms. Most modern Kermits are fairly tolerant of illegal filenames and should not fail simply because of an incoming filename; rather, it will do its best to convert it to a recognizable and unique legal filename. * Directory notations can be different, e.g. backslashes instead of slashes, brackets, parentheses, spaces, etc. But this is now handled by converting pathnames to a standard format during transfer ([520]Section 4.10). So now, for the first time, it is possible to send directory trees among any combination of UNIX, DOS, Windows, OS/2, VMS, AOS/VS, etc. Here's an example sending files from an HP-UX system (where text files are encoded in the HP Roman8 character set) to a PC with K95 (where text files are encoded in CP850): Sender: cd xxx ; CD to root of source tree set file type binary ; Default transfer mode set file character-set hp-roman8 ; Local character set for text files set xfer character-set latin1 ; Transfer character set set file patterns on ; Enable automatic file-type switching... set file binary-patterns *.Z *.gz *.o ; based on these patterns... set file text-patterns *.txt *.c *.h ; for binary and text files. send /recursive * ; Send all the file in this directory tree Receiver: cd yyy ; CD to root of destination tree set file character-set cp850 ; Local character set for text files receive /pathnames:relative ; Receive with pathnames Notes: * Replace "xxx" and "yyy" with the desired directories. * Replace the file character sets appropriately. * Change the patterns as needed (or just use the built-in default lists). * SEND /RECURSIVE also implies /PATHNAMES:RELATIVE. * The file sender tells the file receiver the transfer mode of each file. * The file sender tells the file receiver the transfer character set. * By default, destination file dates will be the same as on the source. * Many of the settings shown might already be set by default. * See [521]Sections 4.3, [522]4.10, and [523]4.15 for additional explanation. If you are refreshing an existing directory on the destination computer, use "set file collision update" or other appropriate file collision option to handle filename collisions. 4.12. Where Did My File Go? Now that Kermit can be started by clicking on desktop icons (thus obscuring the concept of "current directory"), and can have a download directory, and can create directories for incoming files on the fly, etc, sometimes it is easy to lose a file after transfer. Of course, if you keep a transaction log: LOG TRANSACTIONS it will record the fate and final resting place of each file. But in case you did not keep a log, the new command: WHERE added in C-Kermit 7.0, gives you as much information as it has about the location of the last files transferred, including the pathname reported by the receiving Kermit, if any, when C-Kermit is the sender. This information was also added to SHOW FILE in somewhat less detail. 4.13. File Output Buffer Control (UNIX only). The new command SET FILE OUTPUT lets you control how incoming files are written to disk: SET FILE OUTPUT BUFFERED [ size ] Chooses buffered file output; this is the default. UNIX does its normal sort of disk buffering. The optional size specifies Kermit's own file output buffer size, and therefore the frequency of disk accesses (write() system calls) -- the bigger the size, the fewer the disk accesses. SET FILE OUTPUT UNBUFFERED [ size ] This forces each file output write() call to actually commit the data to disk immediately. Choosing this option will usually slow file reception down. SET FILE OUTPUT BLOCKING Write() calls should not return until they are complete. This is the normal setting, and it lets Kermit detect disk-write errors immediately. SET FILE OUTPUT NONBLOCKING Write() calls should return immediately. This can speed up file reception, but also delay the detection of disk-write errors. Experimentation with these parameters should be harmless, and might (or might not) have a perceptible, even dramatic, effect on performance. 4.14. Improved Responsiveness In version 7.0, C-Kermit's file-transfer protocol engine has been tuned for additional speed and responsiveness. * Binary-mode transfers over 8-bit connections, a very common case, are now handled in a special way that minimizes overhead. * SET TRANSFER CRC-CALCULATION is now OFF by default, rather than ON. (This affects only the overall per-transfer CRC, \v(crc16), not the per-packet CRCs) * Connection loss during file transfer is now detected immediately in most cases on Internet connections and on serial connections when CARRIER-WATCH is not set to OFF. 4.15. Doubling and Ignoring Characters for Transparency The following commands were added in 7.0, primarily to allow successful file transfer through ARPAnet TACs and with Honeywell DPS6 systems, but can be used in any setting where they might be needed: SET SEND DOUBLE-CHAR { [ char [ char [ ... ] ] ], NONE } Tells C-Kermit to double the specified characters (use decimal notation) in packets that it sends. For example, if you are sending files through a device that uses @ as an escape character, but allows you to send a single copy of @ through by doubling it, use "set send double 64". SET RECEIVE IGNORE-CHAR [ char [ char [ ... ] ] ] Tells C-Kermit to ignore the specified character(s) in incoming packets. Use this, for example, when something between the sender and receiver is inserting linefeeds for wrapping, NULs for padding, etc. 4.16. New File-Transfer Display Formats SET TRANSFER DISPLAY { BRIEF, CRT, FULLSCREEN, NONE, SERIAL } Selects the file-transfer display format. BRIEF is the new one. This writes one line to the screen per file, showing the file's name, transfer mode, size, the status of the transfer, and when the transfer is successful, the effective data rate in characters per second (CPS). Example: SEND ckcfn3.o (binary) (59216 bytes): OK (0.104 sec, 570206 cps) SEND ckcfns.o (binary) (114436 bytes): OK (0.148 sec, 772006 cps) SEND ckcmai.c (text) (79147 bytes): OK (0.180 sec, 438543 cps) SEND ckcmai.o (binary) (35396 bytes): OK (0.060 sec, 587494 cps) SEND ckcnet.o (binary) (62772 bytes): REFUSED SEND ckcpro.o (binary) (121448 bytes): OK (0.173 sec, 703928 cps) SEND ckcpro.w (text) (63687 bytes): OK (0.141 sec, 453059 cps) SEND makefile (text) (186636 bytes): OK (0.444 sec, 420471 cps) SEND wermit (binary) (1064960 bytes): OK (2.207 sec, 482477 cps) Note that transfer times are now obtained in fractional seconds, rather than whole seconds, so the CPS figures are more accurate (the display shows 3 decimal places, but internally the figure is generally precise to the microsecond). 4.17. New Transaction Log Formats The new command: SET TRANSACTION-LOG { VERBOSE, FTP, BRIEF [ separator ] } lets you choose the format of the transaction log. VERBOSE (the default) indicates the traditional format described in the book. BRIEF and FTP are new. This command must be given prior to the LOG TRANSACTION command if a non-VERBOSE type is desired. 4.17.1. The BRIEF Format BRIEF chooses a one-line per file format suitable for direct importation into databases like Informix, Oracle, or Sybase, in which: * Each record has 8 fields. * Fields are separated by a non-alphanumeric separator character. * The default separator character is comma (,). * Any field containing the separator character is enclosed in doublequotes. * The final field is enclosed in doublequotes. The fields are: 1. Date in yyyymmdd format 2. Time in hh:mm:ss format 3. Action: SEND or RECV 4. The local filename 5. The size of the file 6. The transfer mode (text, binary, image, labeled) 7. The status of the transfer: OK or FAILED 8. Additional status-dependent info, in doublequotes. Examples: 20000208,12:08:52,RECV,/u/olga/oofa.txt,5246,text,OK,"0.284sec 18443cps" 20000208,12:09:31,SEND,/u/olga/oofa.exe,32768,binary,OK,"1.243sec 26362cps" 20000208,12:10:02,SEND,"/u/olga/a,b",10130,text,FAILED,"Refused: date" Note how the filename is enclosed in doublequotes in the final example, because it contains a comma. To obtain BRIEF format, you must give the SET TRANSACTION-LOG BRIEF command before the LOG TRANSACTIONS command. (If you give them in the opposite order, a heading is written to the log by the LOG command.) 4.17.2. The FTP Format SET TRANSACTION-LOG FTP (available only in UNIX) chooses a format that is compatible with the WU-FTPD (Washington University FTP daemon) log, and so can be processed by any software that processes the WU-FTPD log. It logs only transfers in and out, both successful and failed (but success or failure is not indicated, due to lack of a field in the WU-FTPD log format for this purpose). Non-transfer events are not recorded. Unlike other logs, the FTP-format transaction log is opened in append mode by default. This allows you to easily keep a record of all your kermit transfers, and it also allows the same log to be shared by multiple simultaneous Kermit processes or (permissions permitting) users. You can, of course, force creation of a new logfile by specifying the NEW keyword after the filename, e.g. log transactions oofa.log new All records in the FTP-style log are in a consistent format. The first field is fixed-length and contains spaces; subsequent fields are variable length, contain no spaces, and are separated by one or more spaces. The fields are: Timestamp This is an asctime-style timestamp, example: "Wed Sep 16 20:19:05 1999" It is always exactly 24 characters long, and the subfields are always in fixed positions. Elapsed time The whole number of seconds required to transfer the file, as a string of decimal digits, e.g. "24". Connection The name of the network host to which C-Kermit is connected, or the name of the serial device through which it has dialed (or has a direct connection), or "/dev/tty" for transfers in remote mode. Bytes transferred The number of bytes transferred, decimal digits, e.g. "1537904". Filename The name of the file that was transferred, e.g. "/pub/ftp/kermit/a/README.TXT". If the filename contains any spaces or control characters, each such character is replaced by an underscore ('_') character. Mode The letter 'b' if the file was transferred in binary mode, or 'a' if it was transferred in text (ASCII) mode. Options This field always contains an underscore ('_') character. Direction The letter 'o' if the file was transferred Out, and 'i' if the file was transferred In. User class The letter 'r' indicates the file was transferred by a Real user. User identification The ID of the user who transferred the file. Server identification The string "kermit". This distinguishes a Kermit transfer log record from a WU-FTPD record, which contains "ftp" in this field. Authentication class The digit '1' if we know the user's ID on the client system, otherwise '0'. Currently, always '0'. Authenticated user If the authentication class is '1', this is the user's ID on the client system. Otherwise it is an asterisk ('*'). Currently it is always an asterisk. Examples: Thu Oct 22 17:42:48 1998 0 * 94 /usr/olga/new.x a _ i r olga kermit 0 * Thu Oct 22 17:51:29 1998 1 * 147899 /usr/olga/test.c a _ o r olga kermit 0 * Thu Oct 22 17:51:44 1998 1 * 235 /usr/olga/test.o b _ i r olga kermit 0 * Fri Oct 23 12:10:25 1998 0 * 235 /usr/olga/x.ksc a _ o r olga kermit 0 * Note that an ftp-format transaction log can also be selected on the Kermit command line as follows: kermit --xferfile:filespec This is equivalent to: SET TRANSACTION-LOG FTP LOG TRANSACTIONS filespec APPEND Conceivably it could be possible to have a system-wide shared Kermit log, except that UNIX lacks any notion of an append-only file; thus any user who could append to the log could also delete it (or alter it). This problem could be worked around using setuid/setgid tricks, but these would most likely interfere with the other setuid/setgid tricks C-Kermit must use for getting at dialout devices and UUCP logfiles. 4.18. Unprefixing NUL As of 6.1.193 Alpha.10, C-Kermit can finally send and receive file-transfer packets in which NUL (ASCII 0) is unprefixed (no more NUL-terminated packets!). NUL is, of course, extremely prevalent in binary files such as executables, and this has been a significant source of packet overhead. For example, when transferring itself (the SunOS C-Kermit executable) with minimal prefixing and 9000-byte packets, we see: File size: 1064960 Packet chars with 0 prefixed: 1199629 overhead = 12.65% Packet chars with 0 unprefixed: 1062393 overhead = -0.03% Transfer rates go up accordingly, not only because of the reduced amount of i/o, but also because less computation is required on each end. 4.19. Clear-Channel Protocol Now that C-Kermit itself is capable of sending and receiving any byte at all on a clear channel ([524]Section 4.18), it is, for the first time, in a position to negotiate a clear channel with the other Kermit, giving it permission (but not requiring it) to unprefix any and all characters that it knows are safe. In general this means all but the Kermit start-of-packet character (normally Ctrl-A), Carriage Return (not only Kermit's end-of-packet character, but also treated specially on Telnet NVT links), and IAC (255, also special to Telnet). By default, C-Kermit will say it has a clear channel only if it has opened a TCP socket. Since the Kermit program on the far end of a TCP/IP connection generally does not know it has a TCP/IP connection, it will not announce a clear channel unless it has been told to do so. The command is: SET CLEAR-CHANNEL { ON, OFF, AUTO } AUTO is the default, meaning that the clear-channel status is determined automatically from the type of connection. ON means to announce a clear channel, OFF means not to announce it. Use SHOW STREAMING ([525]Section 4.20) to see the current CLEAR-CHANNEL status. Synonym: SET CLEARCHANNEL. CLEAR-CHANNEL is also set if you start C-Kermit with the -I switch (see [526]Section 4.20). Whenever a clear channel is negotiated, the resulting control-character unprefixing is "sticky"; that is, it remains in effect after the transfer so you can use SHOW CONTROL to see what was negotiated. You can also see whether a clear channel was negotiated in the STATISTICS /VERBOSE Display. The advantage of the clear channel feature is that it can make file transfers go faster automatically. The disadvantage would be file-transfer failures if the channel is not truly clear, for example if C-Kermit made a Telnet connection to a terminal server, and then dialed out from there; or if C-Kermit made an Rlogin connection to host and then made a Telnet connection from there to another host. If a file transfer fails on a TCP/IP connection, use SHOW CONTROL to check whether control characters became unprefixed as a result of protocol negotiations, and/or SHOW STREAMING ([527]Section 4.20) to see if "clear-channel" was negotiated. If this happened, use SET CLEAR-CHANNEL OFF and SET PREFIXING CAUTIOUS (or whatever) to prevent it from happening again. 4.20. Streaming Protocol A new Kermit protocol option called "streaming" was added in C-Kermit 7.0. The idea is that if the two Kermit partners have a reliable transport (such as TCP/IP or X.25) between them, then there is no need to send ACKs for Data packets, or NAKs, since a reliable transport will, by definition, deliver all packets in order and undamaged. On such a connection, streaming cuts down not only on Kermit program overhead (switching back and forth between reading and sending packets), but also tends to make the underlying transport use itself more efficiently (e.g. by defeating the Nagle algorithm and/or Delayed ACK stratagem of the TCP layer). Furthermore, it allows transfers to work smoothly on extremely slow network congestions that would otherwise cause timeouts and retransmissions, and even failure when the retry limit was exceeded. The trick is knowing when we can stream: 1. If C-Kermit has opened a TCP socket or X.25 connection, it offers stream. 2. If C-Kermit has been started with the -I (uppercase) option, or if it has been told to SET RELIABLE ON, it offers to stream. 3. If C-Kermit is in remote mode, and has been told to SET RELIABLE AUTO (or ON), it always offers to stream, and also always agrees to stream, if the other Kermit offers. Unless you take explicit actions to override the defaults, this allows the local Kermit (the one that made the connection, and so knows whether it's reliable) to control streaming. (Note that an offer to stream also results in a Clear-Channel announcement if CLEAR-CHANNEL is set to AUTO; see [528]Section 4.19.) When BOTH Kermits offer to stream, then they stream; otherwise they don't. Thus streaming-capable Kermit programs interoperate automatically and transparently with nonstreaming ones. If the two Kermits do agree to stream, you'll see the word "STREAMING" on the fullscreen file-transfer display in the Window Slots field. You can also find out afterwards with the STATISTICS or SHOW STREAMING commands. WARNING: Automatic choice of streaming is based on the assumption of a "direct" end-to-end network connection; for example, a Telnet or Rlogin connection from host A to host B, and transferring files between A and B. However, if your connection has additional components -- something "in the middle" (B) that you have made a network connection to, which makes a separate connection to the destination host (C), then you don't really have a reliable connection, but C-Kermit has no way of knowing this; transferring files between A and C will probably fail. In such cases, you'll need to tell the *local* C-Kermit to "set reliable off" before transferring files (it does no good to give this command to the remote Kermit since the local one controls the RELIABLE setting). Streaming is like using an infinite window size, with no timeouts and no tolerance for transmission errors (since there shouldn't be any). It relies on the underlying transport for flow control, error correction, timeouts, and retransmission. Thus it is very suitable for use on TCP/IP connections, especially slow or bursty ones, since Kermit's packet timeouts won't interfere with the transfer -- each packet takes as long to reach its destination as it takes TCP to deliver it. If TCP can't deliver the packet within its own timeout period (over which Kermit has no control), it signals a fatal error. Just like FTP. Streaming goes much faster than non-streaming when a relatively small packet length is used, and it tends to go faster than non-streaming with even the longest packet lengths. The Kermit window size is irrelevant to streaming protocol, but still might affect performance in small ways since it can result in different paths through the code. The definition of "reliable transport" does not necessarily demand 8-bit and control-character transparency. Streaming can work with parity and/or control-character prefixing just as well (but not as fast) as without them; in such cases you can leave RELIABLE set to ON, but set CLEARCHANNEL and/or PARITY appropriately. Maximum performance -- comparable to and often exceeding FTP -- is achieved on socket-to-socket connections (in which the considerable overhead of the terminal driver and Telnet or Rlogin server is eliminated) with long packets and the new "brief" file-transfer display ([529]Section 4.16). 4.20.1. Commands for Streaming SET RELIABLE { ON, OFF, AUTO } SET RELIABLE ON tells Kermit that it has a reliable transport. SET RELIABLE OFF tells Kermit the transport is not reliable. SET RELIABLE AUTO tells Kermit that it should SET RELIABLE ON whenever it makes a reliable connection (e.g. TELNET or SET HOST on a TCP/IP or X.25 network), and when in remote mode it should believe the transport is reliable if the other Kermit says it is during Kermit protocol negotiation. AUTO is the default; the Kermit program that makes the connection knows whether it is reliable, and tells the remote Kermit. The RELIABLE setting has several effects, including: * It can affect the timeouts used during normal ACK/NAK protocol. * It can affect the clear-channel announcement. * It can affect streaming. If you TELNET or SET HOST somewhere, this includes an implicit SET RELIABLE ON command. The -I command-line option is equivalent to SET RELIABLE ON. Since SET RELIABLE ON (and -I) also implies SET CLEAR CHANNEL ON, you might find that in certain cases you need to tell Kermit that even though the connection is reliable, it doesn't have a clear channel after all: SET CLEAR-CHANNEL OFF SET PREFIXING CAUTIOUS ; or whatever... You can control streaming without affecting the other items with: SET STREAMING { ON, OFF, AUTO } AUTO is the default, meaning streaming will occur if Kermit has made a TCP/IP connection or if RELIABLE is ON (or it was started with the -I command line option). OFF means don't stream; ON means offer to stream no matter what. 4.20.2. Examples of Streaming Here we look at the use and behavior of streaming on several different kinds of connections, and compare its performance with non-streaming transfers. 4.20.2.1. Streaming on Socket-to-Socket Connections Here we get streaming automatically when both Kermit programs are capable of it, since they both make socket connections. For example, on the far end: C-Kermit> set host * 3000 C-Kermit> server and on the near end: C-Kermit> set host foo.bar.xyz.com 3000 (now give SEND and GET command) All subsequent file transfers use streaming automatically. Here are the results from 84 trials, run on a production network, disk-to-disk, in which a 1-MB binary file (the SunOS C-Kermit Sparc executable) was sent from a Sun Sparc-10 with SunOS 4.1.3 to an IBM Power Server 850 with AIX 4.1, socket-to-socket, over a 10Mbps 10BaseT Ethernet, using minimal control-character unprefixing, window sizes from 10 to 32, and packet sizes from 1450 to 9010: Streaming Nonstreaming Max CPS 748955 683354 Min CPS 221522 172491 Mean CPS 646134 558680 Median CPS 678043 595874 Std Dev 101424 111493 Correlations: CPS and window size: -0.036 CPS and packet length: 0.254 CPS and streaming: 0.382 Note that the relationship between streaming and throughput is significantly stronger than that between CPS and window size or packet length. Also note that this and all other performance measurements in this section are snapshots in time; the results could be much different at other times when the load on the systems and/or the network is higher or lower. In a similar socket-to-socket trial, but this time over a wide-area TCP/IP connection (from New York City to Logan, Utah, about 2000 miles), the following results were obtained: Streaming Nonstreaming Max CPS 338226 318203 Min CPS 191659 132314 Mean CPS 293744 259240 Median CPS 300845 273271 Std Dev 41914 52351 Correlations: CPS and window size: 0.164 CPS and packet length: 0.123 CPS and streaming: 0.346 4.20.2.2. Streaming on Telnet Connections In this case the local copy of Kermit is told to TELNET or SET HOST, and so it knows it has a reliable connection and -- unless it has been told not to -- will offer to stream, and the other Kermit program, since it has STREAMING set to AUTO, agrees. Since we have a reliable connection, we'll also get control-character unprefixing automatically because of the new clear-channel protocol ([530]Section 4.19). Any errors that occur during streaming are fatal to the transfer. The message is "Transmission error on reliable link". Should this happen: 1. Check the remote Kermit's flow control setting (SHOW COMMUNICATIONS). If it is NONE, change it to XON/XOFF, or vice versa. If it is XON/XOFF (or you just changed it to XOFF/XOFF), make sure the file sender is prefixing the XON and XOFF characters. In the most drastic case, use "set prefix all" to force prefixing of all control characters. 2. The remote Telnet server might chop off the 8th bit. In that case, tell C-Kermit to "set parity space". Or, you might be able to force the Telnet to allow eight-bit data by telling C-Kermit to "set telopt binary request accept" -- that is, request the Telnet server to enter binary mode, and accept binary-mode bids from the server. 3. The remote Telnet server might have a buffering limitation. If a and b don't cure the problem, tell the file receiver to "set receive packet-length 1000" (or other number -- use the largest one that works). This too, is no different from the non-streaming case (more about this in [531]Section 4.20.2.3). And remember you can continue interrupted binary-mode transfers where they left off with the RESEND (= SEND /RECOVER) command. Here are the figures for the same 84 trials between the same Sun and IBM hosts as in 4.20.2.1, on the same network, but over a Telnet connection rather than socket-to-socket: Streaming Nonstreaming Max CPS 350088 322523 Min CPS 95547 173152 Mean CPS 321372 281830 Median CPS 342604 291469 Std Dev 40503 29948 Correlations: CPS and window size: 0.001 CPS and packet length: 0.152 CPS and streaming: 0.128 Here the effect is not as emphatic as in the socket-to-socket case, yet on the whole streaming tends to be beneficial. Additional measurements on HP-UX using C-Kermit 7.0 Beta.06: Windowing Streaming HP-UX 8->8 not tested 14Kcps HP-UX 8->9 not tested 76Kcps HP-UX 8->10 36Kcps 66Kcps HP-UX 9->9 not tested 190Kcps HP-UX 9->10 160Kcps 378Kcps 4.20.2.3. Streaming with Limited Packet Length The IRIX telnet server (at least the ones observed in IRIX 5.3 and 6.2) does not allow Kermit to send packets longer than 4096 bytes. Thus when sending from IRIX C-Kermit when it is on the remote end of a Telnet connection, the packet length must be 4K or less. Trials in this case (in which packet lengths range from 1450 to 4000) show a strong advantage for streaming, which would be evident in any other case where the packet length is restricted, and stronger the shorter the maximum packet length. Streaming Nonstreaming Max CPS 426187 366870 Min CPS 407500 276517 Mean CPS 415226 339168 Median CPS 414139 343803 Std Dev 6094 25851 Correlations: CPS and window size: 0.116 CPS and packet length: 0.241 CPS and streaming: 0.901 4.20.2.4. Streaming on Dialup Connections Here "dialup" refers to a "direct" dialup connection, not a SLIP or PPP connection, which is only a particular kind of TCP/IP connection. Attempt this at your own risk, and then only if (a) you have error-correcting modems, and (b) the connections between the modems and computers are also error-free, perfectly flow-controlled, and free of interrupt conflicts. Streaming can be used effectively and to fairly good advantage on such connections, but remember that the transfer is fatal if even one error is detected (also remember that should a binary-mode transfer fail, it can be recovered from the point of failure with RESEND). To use streaming on an unreliable connection, you must tell both Kermits that the connection is reliable: kermit -I or: C-Kermit> set reliable on In this case, it will probably be necessary to prefix some control characters, for example if your connection is through a terminal server that has an escape character. Most Cisco terminal servers, for example, require Ctrl-^ (30, as well as its high-bit equivalent, 158) to be prefixed. To unprefix these, you'll need to defeat the "clear channel" feature: C-Kermit> set reliable on C-Kermit> set clear-channel off C-Kermit> set prefixing none C-Kermit> set control prefix 1 13 30 158 ; and whatever else is necessary Dialup trials were done using fixed large window and packet sizes. They compare uploading and downloading of two common types of files, with and without streaming. Configuration: HP-9000/715/33 -- 57600bps, RTS/CTS -- USR Courier V.34 -- V.34+V.42, 31200bps -- USR V.34+ Rackmount -- 57600bps, RTS/CTS -- Cisco terminal server -- Solaris 2.5.1. Packet size = 8000, Window Size = 30, Control Character Unprefixing Minimal (but including the Cisco escape character). Since this is not a truly reliable connection, a few trials failed when a bad packet was received (most likely due to UART overruns); the failure was graceful and immediate, and the message was informative. The results of ten successful trials uploading and downloading the two files with and without streaming are: Streaming.. Off On Upload 5194 5565 txt (= C source code, 78K) 3135 3406 gz (= gzip file, compressed, 85K) Download 5194 5565 txt 3041 3406 gz Each CPS figure is the mean of 10 results. A brief test was also performed on a LAT-based dialout connection from a VAX 3100 with VMS 5.5 to a USR Courier V.34 connected to a DECserver 700 at 19200 bps. The 1-MB Sparc executable downloaded from a Sun to the VAX at 1100cps without streaming and 1900cps with streaming, using 8000-byte packets, 30 window slots, and minimal prefixing in both cases. 4.20.2.5. Streaming on X.25 Connections We have only limited access to X.25 networks. One trial was performed in which the 1MB Solaris 2.4 Sparc executable was transferred over a SunLink X.25 connection; nothing is known about the actual physical connection. With a packet length of 8000 and a window size of 30, the file transferred at 6400 cps (using a maximum of 6 window slots). With the same packet length, but with streaming, it transferred without mishap at 6710 cps, about 5% faster. 4.20.3. Streaming - Preliminary Conclusions The results vary with the particular connection, but are good overall. Although numerous lower-level tricks can be used to improve performance on specific platforms or connection methods, streaming occurs at a high, system-independent level of the Kermit protocol and therefore can apply to all types of platforms and (reliable) connections transparently. 4.21. The TRANSMIT Command Prior to C-Kermit 7.0, the TRANSMIT command transmitted in text or binary mode according to SET FILE TYPE { TEXT, BINARY }. But now that binary mode is likely to be the default for protocol transfers, it is evident that this not also an appropriate default for TRANSMIT, since binary-mode TRANSMIT is a rather specialized and tricky operation. Therefore, TRANSMIT defaults to text mode always, regardless of the FILE TYPE setting. C-Kermit 7.0 expands the capabilities of the TRANSMIT command by adding the following switches (see [532]Section 1.5). The new syntax is: TRANSMIT [ switches... ] filename Zero or more switches may be included: /PIPE When /PIPE is included, "filename" is interpreted as a system command or program whose output is to be sent. Synonym: /COMMAND. Example: transmit /pipe finger You may enclose the command in braces, but you don't have to: xmit /pipe {ls -l | sort -r +0.22 -0.32 | head} /BINARY Transmits the file (or pipe output) in binary mode. /TEXT Transmits the file (or pipe output) in line-oriented text mode. Current FILE CHARACTER-SET and TERMINAL CHARACTER-SET selections govern translation. Default. /TRANSPARENT Specifies text mode without character-set translation, no matter what the FILE and TERMINAL CHARACTER-SET selections are. /NOWAIT This is equivalent to SET TRANSMIT PROMPT 0, but for this TRANSMIT command only. Applies only to text mode; it means to not wait for any kind of echo or turnaround character after sending a line before sending the next line. (Normally Kermit waits for a linefeed.) When TRANSMIT ECHO is ON, C-Kermit tries to read back the echo of each character that is sent. Prior to C-Kermit 7.0, 1 second was allowed for each echo to appear; if it didn't show up in a second, the TRANSMIT command would fail. Similarly for the TRANSMIT PROMPT character. However, with today's congested Internet connections, etc, more time is often needed: SET TRANSMIT TIMEOUT number Specifies the number of seconds to wait for an echo or the prompt character when TRANSMIT PROMPT is nonzero; the default wait is 1 second. If you specify 0, the wait is indefinite. When a timeout interval of 0 is specified, and a desired echo or prompt does not show up, the TRANSMIT command will not terminate until or unless you interrupt it with Ctrl-C; use SET TRANSMIT TIMEOUT 0 with caution. Note: to blast a file out the communications connection without any kind of synchronization or timeouts or other manner of checking, use: SET TRANSMIT ECHO OFF SET TRANSMIT PROMPT 0 (or include the /NOWAIT switch) SET TRANSMIT PAUSE 0 TRANSMIT [ switches ] filename In this case, text-file transmission is not-line oriented and large blocks can be sent, resulting in a significant performance improvement over line-at-at-time transmission. Successful operation depends (even more than usual for the TRANSMIT command!) on a clean connection with effective flow control. For details on TRANSMIT and character sets, see [533]Section 6.6.5.4. 4.22. Coping with Faulty Kermit Implementations Kermit protocol has been implemented in quite a few third-party commercial, shareware, and freeware software packages, with varying degrees of success. In most cases operation is satisfactory but slow -- only the bare minimum subset of the protocol is available -- short packets, no sliding windows, no attributes, etc. In other cases, the implementation is incorrect, resulting in failures at the initial negotiation stage or corrupted files. C-Kermit 7.0 and Kermit 95 1.1.19 include some new defense mechanisms to help cope with the most common situations. However, bear in mind there is only so much we can do in such cases -- the responsibility for fixing the problem lies with the maker of the faulty software. 4.22.1. Failure to Accept Modern Negotiation Strings The published Kermit protocol specification states that new fields can be added to the parameter negotiation string. These are to be ignored by any Kermit implementation that does not understand them; this is what makes the Kermit protocol extensible. Unfortunately, some Kermit implementations become confused (or worse) when receiving a negotiation string longer than the one they expect. You can try working around such problems by telling Kermit to shorten its negotiation string (and thus disable the corresponding new features): SET SEND NEGOTIATION-STRING-MAX-LENGTH number Try a number like 10. If that doesn't work, try 9, 8, 7, 6, and so on. 4.22.2. Failure to Negotiate 8th-bit Prefixing The published Kermit protocol specification states that 8th-bit prefixing (which allows transfer of 8-bit data over a 7-bit connection) occurs if the file sender puts a valid prefix character (normally "&") in the 8th-bit-prefix field of the negotiation string, and the receiver puts either a letter "Y" or the same prefix character. At least one faulty Kermit implementation exists that does not accept the letter "Y". To force C-Kermit / K-95 to reply with the other Kermit's prefix character rather than a "Y", give the following (invisible) command: SET Q8FLAG ON Use SET Q8FLAG OFF to restore the normal behavior. 4.22.3. Corrupt Files Refer to [534]Section 4.22.2. Some Kermit implementations mistakenly interpret the "Y" as a prefix character. Then, whenever a letter Y appears in the data, the Y and the character that follows it are replaced by a garbage character. At this writing, we are not sure if there is any solution, but try "set send negotiation-string-max-length 6" and/or "set q8flag on". File corruption can also occur when control characters within the file data are sent without prefixing, as at least some are by default in C-Kermit 7.0 and K-95. Some Kermit implementations do not handle incoming "bare" control characters. To work around, "set prefixing all". 4.22.4. Spurious Cancellations The Kermit protocol specification states that if an ACK to a Data packet contains X in its data field, the transfer of the current file is canceled, and if it contains a Z, the entire transfer is canceled. At least one overzealous Kermit implementation applies this rule to non-Data packets as well, the typical symptom being that any attempt to transfer a file whose name begins with X or Z results in cancellation. This is because the file receiver typically sends back the name under which it stored the file (which might not be the same as the name it was sent with) in the ACK to the File Header packet. This is information only and should not cause cancellation. To work around the problem, use: SET F-ACK-BUG { ON, OFF } ON tells Kermit not to send back the filename in the ACK to the file header packet as it normally would do (OFF puts Kermit back to normal after using ON). A variation on the this bug occurs in an obscure Kermit program for MUMPS: When this Kermit program sends a file called (say) FOO.BAR, it requires that the ACK to its F packet contain exactly the same name, FOO.BAR. However, C-Kermit likes to send back the full pathname, causing the MUMPS Kermit to fail. SET F-ACK-BUG ON doesn't help here. So a separate command has been added to handle this situation: SET F-ACK-PATH { ON, OFF } Normally it is ON (regardless of the SET SEND PATHNAMES setting). Use SET F-ACK-PATH OFF to instruct Kermit to send back only the filename without the path in the ACK to the F packet. 4.22.5. Spurious Refusals Some Kermit implementations, notably PDP-11 Kermit 3.60 and earlier, have bugs in their handling of Attribute packets that can cause unwarranted refusal of incoming files, e.g. based on date or size. This can be worked around by telling one or both of the Kermit partners to: SET ATTRIBUTES OFF 4.22.6. Failures during the Data Transfer Phase This can be caused by control-character unprefixing ([535]Section 4.22.3 ), and fixed by: SET PREFIXING ALL It can also have numerous other causes, explained in Chapter 10 of [536]Using C-Kermit: the connection is not 8-bit transparent (so use "set parity space" or somesuch), inadequate flow control, etc. Consult the manual. 4.22.7. Fractured Filenames At least one well-known PC-based communications package negotiates data compression, which (according to the protocol specification) applies to both the filename and the file data, but then fails to decompress the filename. Example: C-Kermit sends a file called R000101.DAT (where 000101 might be non-Y2K-wise YYMMDD notation), and the package in question stores the files as R~#0101.DAT. Workaround: Tell C-Kermit to SET REPEAT COUNTS OFF. 4.22.8. Bad File Dates At least one well-known PC-based communications package negotiates the passing of file timestamps from sender to receiver, but when it is sending files, it always gives them a timestamp of 1 February 1970. Workaround: tell C-Kermit to SET ATTRIBUTE DATE OFF. You don't get the file's real date, but you also don't get 1 Feb 1970; instead the file gets the current date and time. 4.23. File Transfer Recovery Prior to C-Kermit 7.0, RESEND (SEND /RECOVER) and REGET (GET /RECOVER) refused to work if FILE TYPE was not BINARY or the /BINARY switch was not included. Now these commands include an implied /BINARY switch, meaning they set the file type to binary for the duration of the command automatically. In the client/server arrangement, this also forces the server into binary mode (if it is C-Kermit 7.0 or greater, or K95 1.1.19 or greater) so the recovery operation proceeds, just as you asked and expected. BUT... Just as before, the results are correct only under the following conditions: * If the prior interrupted transfer was also in binary mode; or: * If the prior transfer was in text mode and the other computer was a "like platform" (e.g. UNIX-to-UNIX, Windows-to-Windows, DOS-to-Windows) AND there was no character-set translation (i.e. TRANSFER CHARACTER-SET was TRANSPARENT). Note that these circumstances are more likely to obtain in C-Kermit 7.0, in which: * The default FILE TYPE in C-Kermit 7.0 is BINARY. * The default FILE INCOMPLETE setting is AUTO, which means KEEP if the transfer is in binary mode, DISCARD otherwise. * C-Kermit 7.0, Kermit 95 1.1.17, and MS-DOS Kermit 3.15 and later can recognize "like platforms" and switch into binary mode automatically. Transfers between like platforms are always binary unless character-set translation has been requested, and then is still binary for all files whose names match a binary pattern, unless the automatic mechanisms have been disabled (with a /TEXT switch, or with SET TRANSFER MODE MANUAL). * SEND /BINARY and GET /BINARY always force binary-mode transfers, even when FILE TYPE is TEXT, even when TRANSFER MODE is AUTOMATIC, even when PATTERNS are ON and the file's name matches a text pattern. But also note that the automatic client/server transfer-mode adjustments do not work with versions of C-Kermit prior to 7.0 or K95 prior to 1.1.16. If the prior transfer was in text mode: * If text-mode transfers between the two platforms are "length-changing" (as they are between UNIX -- which terminates text lines with LF -- and DOS or Windows -- which terminates text lines with CRLF), the recovered file will be corrupt. * If text-mode transfers between the two platforms are not length-changing, but character-set translation was active in the prior transfer, the result will be a file in which the first part has translated characters and the second part does not. But in C-Kermit 7.0 and K95 1.1.19 and later, incompletely transferred text files are not kept unless you change the default. But if you have done this, and you have an incompletely transferred text file, you'll need to: * Transfer the whole file again in text mode, or: * Use SEND /STARTING-AT: to recover the transfer at the correct point; but you have to find out what that point is, as described in the manual. Kermit has no way of knowing whether the previous transfer was in text or binary mode so it is your responsibility to choose the appropriate recovery method. If you use C-Kermit to maintain parallel directories on different computers, using SET FILE COLLISION to transfer only those files that changed since last time, and the files are big enough (or the connection slow enough) to require SEND /RECOVER to resume interrupted transfers, you should remember that SEND /RECOVER (RESEND) overrides all FILE COLLISION settings. Therefore you should use SEND /RECOVER (RESEND) only on the file that was interrupted, not the file group. For example, if the original transfer was initiated with: SEND * and was interrupted, then after reestablishing your connection and starting the Kermit receiver with SET FILE COLLISION UPDATE on the remote end, use the following sequence at the sender to resume the transfer: SEND /RECOVER name-of-interrupted-file and then: SEND * (In C-Kermit 7.0 and later, \v(filename) contains the name of the file most recently transferred, as long you have not EXITed from Kermit or changed directory, etc. 4.24. FILE COLLISION UPDATE Clarification In UNIX, file modification dates are used when comparing the file date with the date in the attribute packet. In VMS, however, the file creation date is used. These two policies reflect the preferences of the two user communities. Also, remember that the file date/time given in the attribute packet is the local time at the file sender. At present, no timezone conversions are defined in or performed by the Kermit protocol. This is primarily because this feature was designed at a time when many of the systems where Kermit runs had no concept of timezone, and therefore would be unable to convert (say, to/from GMT or UTC or Zulu time). As a consequence, some unexpected results might occur when transferring files across timezones; e.g. commands on the target system that are sensitive to file dates might not work (UNIX "make", backups, etc). Timezone handling is deferred for a future release. 4.25. Autodownload Improvements Refer to pages 164-165 of [537]Using C-Kermit about the hazards of autodownload when C-Kermit is "in the middle". As of C-Kermit 7.0, no more hazards. If C-Kermit has TERMINAL AUTODOWNLOAD ON and it detects a packet of the current protocol type (Kermit or Zmodem), it "erases" the visual aspect of the packet that would be seen by the terminal (or, more to the point, the emulator, such as K95). This way, only C-Kermit goes into RECEIVE mode, and not also the terminal emulator through which C-Kermit is accessed. And therefore, it is no longer necessary to SET TERMINAL AUTODOWNLOAD OFF to prevent multiple Kermits from going into receive mode at once, but of course it is still necessary to ensure that, when you have multiple Kermits in a chain, that the desired one receives the autodownload. The defaults have not been changed; Kermit 95 still has autodownload ON by default, and C-Kermit has it OFF by default. 5. CLIENT/SERVER 5.0. Hints If you use SET SERVER GET-PATH to set up your server, and the GET-PATH does not include the server's current directory, clients can become quite confused. For example, "remote dir oofa.txt" shows a file named oofa.txt, but "get oofa.txt" fails. In this situation, you should either DISABLE DIR or make your GET-PATH include the current directory. 5.1. New Command-Line Options The -G command-line option is like -g (GET), except the incoming file is sent to standard output rather than written to disk. The -I option ("Internet") is used to tell a remote C-Kermit program that you are coming in via Internet Telnet or Rlogin and therefore have a reliable connection. The -I option is equivalent to SET RELIABLE ON and SET FLOW NONE. The -O option ("Only One") tells C-Kermit to enter server mode but then exit after the first client operation. See [538]Section 9.3 for details. 5.2. New Client Commands BYE and FINISH no longer try to do anything if a connection is not active. Thus a sequence like "hangup" followed by "bye" or "finish" will no longer get stuck in a long timeout-and-retransmission cycle, nor will it try to open a new connection. REMOTE EXIT Similar to FINISH, except it ensures that the Kermit server program exits back to the operating system or shell prompt. (FINISH would return it to its interactive prompt if it was started in interactive mode, and would cause it to exit if it entered server mode via command-line option.) When C-Kermit is to be the server, you can use { ENABLE, DISABLE } EXIT to control the client's access to this feature. REMOTE MKDIR directory-name Tells the client to ask the server to create a directory with the given name, which can be absolute or relative. The syntax of the directory name depends on the Kermit server (see [539]next section); in all cases, it can be in the syntax of the system where the server is running (UNIX, VMS, DOS, etc) but newer servers also accept UNIX syntax, no matter what the underlying platform. The server will not execute this command if (a) it does not understand it, (b) a DISABLE MKDIR command has been given, or (c) a DISABLE CWD command has been given; otherwise, the command is executed, but will fail if the directory can not be created, in which cases most servers will attempt to return a message giving the reason for failure. The REMOTE MKDIR command succeeds if the remote directory is created, or if it already exists and therefore does not need to be created, and fails otherwise. REMOTE RMDIR directory-name Tells the client to ask the server to remove (delete) a directory with the given name. The same considerations apply as for REMOTE MKDIR. REMOTE SET FILE INCOMPLETE { DISCARD, KEEP, AUTO } Previously this was only available in its earlier form, REMOTE SET INCOMPLETE (no FILE). The earlier form is still available, but invisible. Also, AUTO was added, meaning KEEP if in binary mode, DISCARD otherwise. REMOTE SET TRANSFER MODE { AUTOMATIC, MANUAL } Tells the client to ask the server to set the given file-transfer mode. Automatic means (roughly): if the client and the server are running on the same kind of computer (e.g. both are on UNIX), then use binary mode automatically; if the system types are different, use some other method to automatically determine text or binary mode, such as filename pattern matching. MANUAL means, in this context, obey the client's FILE TYPE setting (TEXT or BINARY). Synonym: REMOTE SET XFER MODE. [ REMOTE ] QUERY KERMIT function(args...) Prior to C-Kermit 7.0, the arguments were not evaluated locally. Thus it was not possible to have the server run the function with client-side variables as arguments. Now: define \%a oofa.* remote query kermit files(\%a) ; Client's \%a remote query kermit files(\\%a) ; Server's \%a [ REMOTE ] LOGIN [ user [ password ] ] LOGIN is now a synonym for REMOTE LOGIN. LOGOUT This command, when given in local mode, is equivalent to REMOTE LOGOUT. When given at the IKSD prompt, it logs out the IKSD. When given at the C-Kermit prompt when it has no connection, it does nothing. Note that in C-Kermit 7.0, the REMOTE (or R) prefix is not required for QUERY, since there is no local QUERY command. The new top-level QUERY command does exactly what REMOTE QUERY (RQUERY) does. All REMOTE commands now have single-word shortcuts: Shortcut Full Form RASG REMOTE ASSIGN RCD REMOTE CD RCOPY REMOTE COPY RDEL REMOTE DELETE RDIR REMOTE DIRECTORY REXIT REMOTE EXIT RHELP REMOTE HELP RHOST REMOTE HOST RPWD REMOTE PWD RSET REMOTE SET etc. The R prefix is not applied to LOGIN because there is already an RLOGIN command with a different meaning. It is not applied to LOGOUT either, since LOGOUT knows what to do in each case, and for symmetry with LOGIN. 5.2.1. Remote Procedure Definitions and Calls This is nothing new, but it might not be obvious... REMOTE ASSIGN and REMOTE QUERY may be used to achieve remote procedure execution. The remote procedure can be defined locally or remotely. A remote procedure call is accomplished as noted in the previous section: [ remote ] query kermit function-name(args...) This invokes any function that is built in to the Kermit server, e.g.: [ remote ] query kermit size(foo.bar) returns the size of the remote file, foo.bar. Now note that C-Kermit includes an \fexecute() function, allowing it to execute any macro as if it were a built-in function. So suppose MYMACRO is the name of a macro defined in the server. You can execute it from the client as follows (the redundant "remote" prefix is omitted in the remaining examples): query kermit execute(mymacro arg1 arg2...) The return value, if any, is the value of the RETURN command that terminated execution of the macro, for example: define addtwonumbers return \feval(\%1+\%2) The client invocation would be: query kermit execute(addtwonumbers 3 4) 7 The result ("7" in this case) is also assigned to the client's \v(query) variable. To execute a remote system command or command procedure (shell script, etc) use: query kermit command(name args...) Finally, suppose you want the client to send a macro to the server to be executed on the server end. This is done as follows: remote assign macroname definition query kermit execute(macroname arg1 arg2...) Quoting is required if the definition contains formal parameters. 5.3. New Server Capabilities 5.3.1. Creating and Removing Directories The C-Kermit 7.0 server responds to REMOTE MKDIR and REMOTE RMDIR commands. The directory name may be in either the native format of the server's computer, or in UNIX format. For example, a server running on VMS with a current directory of [IVAN] can accept commands from the client like: remote mkdir olga ; Makes [IVAN.OLGA] (nonspecific format) remote mkdir .olga ; Makes [IVAN.OLGA] (VMS format without brackets) remote mkdir olga/ ; Makes [IVAN.OLGA] (UNIX relative format) remote mkdir /ivan/olga ; Makes [IVAN.OLGA] (UNIX absolute format) remote mkdir [ivan.olga] ; Makes [IVAN.OLGA] (VMS absolute format) remote mkdir [.olga] ; Makes [IVAN.OLGA] (VMS relative format) 5.3.1.1. Creating Directories If a directory name is given that contains more than one segment that does not exist, the server attempts to create all the segments. For example, if the client says: REMOTE MKDIR letters/angry a "letters" subdirectory is created in the server's current directory if it does not already exist, and then an "angry" subdirectory is created beneath it, if it does not already have one. This can repeated to any reasonable depth: REMOTE MKDIR a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/z/y/z 5.3.1.2. Removing Directories When attempting to execute a REMOTE RMDIR, the server can remove only a single directory, not an entire sequence or tree. The system service that is called to remove the directory generally requires not only that the server process has write delete access, but also that the directory contain no files. In the future, a REMOTE RMDIR /RECURSIVE command (and the accompanying protocol) might be added. For now, use the equivalent REMOTE HOST command(s), if any. 5.3.2. Directory Listings Directory listings are generated by C-Kermit itself, rather than by running the underlying system's directory command. Some control over the listing format can be obtained with the SET OPTIONS DIRECTORY command ([540]Section 4.5.1). The following options affect listings sent by the server: /[NO]HEADING, /[NO]DOTFILES, and /[NO]BACKUP. In UNIX and VMS, the listing is always sorted by filename. There is, at present, no protocol defined for the client to request listing options of the server; this might be added in the future. The server's directory listings are in the following format: Protection or permissions: In UNIX and OS-9, this is a 10-character field, left adjusted. In VMS it is a 22-character field, left-adjusted. In each case, the protection / permission codes are shown in the server platform's native format. In other operating systems, this field is not shown. Size in bytes: This is always a 10-character field. The file's size is shown as a decimal number, right adjusted in the field. If the file is a directory and its size can not be obtained, the size is shown as "". Two blanks follow this field. Date: Always in yyyy-mm-dd hh:mm:ss numeric format, and therefore 19 characters long. If the file's date/time can't be obtained, zeros (0) are shown for all the digits. This field is followed by two blanks. Filename: This field extends to the end of the line. Filenames are shown relative to the server's current directory. In UNIX, symbolic links are shown as they are in an "ls -l" listing as "linkname -> filename". In UNIX and VMS, listings are returned by the server in alphabetical order of filename. There are presently no other sort or selection options. However, since these are fixed-field listings, all fields can be used as sort keys by external sort programs. Note, in particular, that the format used for the date allows a normal lexical on that field to achieve the date ordering. For example, let's assume we have a UNIX client and a UNIX server. In this case, the server's listing has the date in columns 22-40, and thus could be sorted by the UNIX sort program using "sort +0.22 -0.40" or in reverse order by "sort +0.22 -0.40r". Since the UNIX client can pipe responses to REMOTE commands through filters, any desired sorting can be accomplished this way, for example: C-Kermit> remote directory | sort +0.22 -0.40 You can also sort by size: C-Kermit> remote directory | sort +0.11 -0.19 You can use sort options to select reverse or ascending order. "man sort" (in UNIX) for more information. And of course, you can pipe these listings through any other filter of your choice, such as grep to skip unwanted lines. 5.4. Syntax for Remote Filenames with Embedded Spaces C-Kermit and K95, when in server mode, assume that any spaces in the file specification in an incoming GET command are filename separators. Thus if the client gives a command like: get {oofa.txt oofa.bin} or, equivalently: mget oofa.txt oofa.bin the server tries to send the two files, oofa.txt and oofa.bin. But what if you want the server to send you a file named, say: D:\HP OfficeJet 500\Images\My Pretty Picture Dot PCL How does the server know this is supposed to be one file and not seven? In this case, you need to the send file name to the server enclosed in either curly braces: {D:\HP OfficeJet 500\Images\My Pretty Picture Dot PCL} or ASCII doublequotes: "D:\HP OfficeJet 500\Images\My Pretty Picture Dot PCL" The method for doing this depends on your client. If your client is C-Kermit 7.0, any recent version of Kermit 95, or MS-DOS Kermit 3.16, then you have to enclose the name in braces just so the client can parse it, so to send braces or doublequotes to the server, you must put them inside the first, outside pair of braces. And you also need to double the backslashes to prevent them from being interpreted: get {{D:\\HP OfficeJet 500\\Images\\My Pretty Picture Dot PCL}} get {"D:\\HP OfficeJet 500\\Images\\My Pretty Picture Dot PCL"} To get around the requirement to double backslashes in literal filenames, of course you can also use: set command quoting off get {{D:\HP OfficeJet 500\Images\My Pretty Picture Dot PCL}} get {"D:\HP OfficeJet 500\Images\My Pretty Picture Dot PCL"} set command quoting on If you are giving a "kermit" command to the UNIX shell, you have to observe the shell's quoting rules, something like this: kermit -ig "{D:\HP OfficeJet 500\Images\My Pretty Picture Dot PCL}" Here, the quotes go on the outside so UNIX will pass the entire filename, spaces, braces, and all, as a single argument to Kermit, and the backslashes are not doubled because (a) the UNIX shell ignores them since they are in a quoted string, and (b) Kermit ignores them since the interactive command parser is not activated in this case. 5.5. Automatic Orientation Messages upon Directory Change C-Kermit 7.0, when acting as a server, can send an orientation message to the client whenever the server directory changes. For example, when the client gives a REMOTE CD command, the server sends the contents of the new directory's "Read Me" file to the client's screen. The following commands govern this feature: SET SERVER CD-MESSAGE FILE name Given to the servr, allows the message-file name to be specified at runtime. A list of names to look for can be given in the following format: {{name1}{name2}{name3}{...}} e.g. SET SERVER CD-MESSAGE FILE {{./.readme}{README.TXT}{READ.ME}} REMOTE SET SERVER CD-MESSAGE { ON, OFF } Given to the client, lets the client control whether the server sends automatic CD messages. SHOW SERVER Given to server, includes CD-Message status. The default CD message file name is system dependent. SHOW CD or SHOW SERVER displays the list. Also see [541]Section 4.5.2. 5.6. New Server Controls DISABLE ENABLE Allows the server to configured such that DISABLEd features can not be re-enabled by any means -- e.g. if the client is somehow able to get the server into command mode. Once DISABLEd, ENABLE can not be re-ENABLEd. SET SERVER IDLE-TIMEOUT seconds This was available previously in Kermit 95 only. Now it can be used in C-Kermit also to specify a maximum number of seconds the server is allowed to be idle before exiting server mode. 0 seconds means no idle timeout. In C-Kermit (but not K-95), SET SERVER TIMEOUT and SET SERVER IDLE-TIMEOUT are mutually exclusive -- you can have one or the other (or neither), but not both. (Server timeouts are for the benefit of primitive Kermit clients that are not capable of timing out on their own; to our knowledge, no such clients are still in circulation.) SET SERVER KEEPALIVE { ON, OFF } (See next section). 5.7. Timeouts during REMOTE HOST Command Execution Prior to C-Kermit 7.0, the C-Kermit server would block waiting for output from a system command invoked via REMOTE HOST from the client. If the system command took a long time to execute, the client would time out and send NAK packets. If the command took too long, the client would reach its retry limit and give up. Even if it didn't, the NAKs would cause unnecessary retransmissions. In version 7.0, the C-Kermit server (VMS and select()-capable UNIX versions only), sends "keepalive packets" (empty data packets) once per second while waiting for the system command to complete. This procedure should be entirely transparent to the Kermit client, and should prevent the unwanted timeouts and NAKs. When C-Kermit 7.0 itself (or K95 1.1.19) is the client, it prints dots to show the keepalive packets. The keepalive feature can be turned off and on with: SET SERVER KEEPALIVE { ON, OFF } Normally it should be on. Turn it off it if causes trouble with the client, or if it seems to slow down the server (as it might on some platforms under certain circumstances). 6. INTERNATIONAL CHARACTER SETS Support for several new single-byte character sets was added in C-Kermit 7.0. Unicode / ISO 10646 is not yet supported, but is a high priority for forthcoming releases. 6.0. ISO 8859-15 Latin Alphabet 9 To accommodate the Euro currency symbol, and to correct several other longstanding problems with ISO Latin Alphabet 1, ISO 8859-15 Latin Alphabet 9 was issued in May 1998. It is supported by C-Kermit 7.0 as a transfer character set, a file character set, and a terminal character set. Translations that preserve the new characters are available between Latin-9 and several other sets including: PC Code Page 858 (Western European languages, similar to CP850) Windows Code Page 1252 (Western European languages, similar to Latin-1) Windows Code Page 1250 (Eastern European languages, similar to Latin-2) The Latin-9 transfer character set also allows for the OE digraph character, used primarily in French, to be preserved in transfers involving the DEC MCS or NeXT character sets. The Euro character is also present in the Universal Character Set, described in [542]Section 6.6. 6.1. The HP-Roman8 Character Set The HP-Roman8 character set is supported in C-Kermit 6.0 and later but was omitted from Table VII-4 in the 2nd Edition of Using C-Kermit due to lack of space. It is listed in [543]Appendix III. 6.2. Greek Character Sets Greek character sets were added in 6.1: SET FILE CHARACTER-SET { CP869, ELOT927, GREEK-ISO } SET TRANSFER CHARACTER-SET { GREEK-ISO } GREEK-ISO is ISO 8859-7, which the same as ELOT 928. The new Greek character sets are listed in [544]Appendix III. 6.3. Additional Latin-2 Character Sets The following have been added as FILE and TERMINAL CHARACTER-SETs: MAZOVIA-PC A PC code page used in Poland, equivalent to CP437, but with 18 substitutions needed for Polish. CP1250 The Windows Latin 2 Code Page. Equivalent to ISO 8859-2, but with different encoding. 6.4. Additional Cyrillic Character Sets The following have been added as FILE and TERMINAL CHARACTER-SETs: BULGARIA-PC This is the Cyrillic PC code page used in Bulgaria, where it is called Code Page 856. It is attributed to a company called DATEC, Inc, but CP856 is not a proper designation, since it refers to a Hebrew Code Page (see the IBM Registry). CP855 This PC Code Page contains all the Cyrillic letters that are also in ISO 8859-5, and is therefore useful for non-Russian Cyrillic text (Ukrainian, Belarusian, etc), unlike CP866, which has a smaller repertoire of Cyrillic letters. CP1251 The Windows Cyrillic Code Page. Equivalent to CP855, but with different encoding. KOI8R An extension to "Old KOI-8" that adds upper and lower case Cyrillic letter Io (looks like Roman E with diaeresis) plus a selection of box-drawing characters to columns 8 through 11, which are vacant in original Old KOI-8. KOI8-R is used for the Russian language. It is specified in [545]RFC 1489. KOI8U A similar extension of Old KOI-8, but for Ukrainian. It is specified in [546]RFC 2319. 6.5. Automatic Character-Set Switching Prior to version 7.0, C-Kermit's file character-set always had to be set explicitly. In 7.0 and later, it is set automatically when: 1. This feature is enabled (as it is unless you disable it). 2. An incoming text-mode transfer includes a transfer-character-set announcer and you have not previously given a SET FILE CHARACTER-SET command. In this case, C-Kermit switches to an appropriate file character set. For example, on an HP-UX workstation, an incoming Latin-1 file automatically selects HP-Roman8 for the local copy of the file; in Data General AOS/VS, it would select DG International. 3. You give a SET TRANSFER CHARACTER-SET command without having previously specified a FILE CHARACTER-SET. An appropriate file character-set is chosen automatically. In addition, when you give a SET FILE CHARACTER-SET command, the appropriate transfer character-set is automatically chosen, to be used when you are sending files (but this does not override the one announced by the sender when you are receiving files). You might not agree about what is "appropriate", so of course you can disable or change all of the above actions. You can disable (or re-enable) the new automatic character-set switching feature in each direction separately: SET RECEIVE CHARACTER-SET-SELECTION { AUTOMATIC, MANUAL } AUTOMATIC is the default, causing the behavior described above when an incoming file arrives. Choose MANUAL to defeat this behavior and force your current FILE CHARACTER-SET setting to be used, no matter what it is. Note that SET RECEIVE CHARACTER-SET MANUAL does not disable recognition of the incoming transfer character-set announcer, and translation from the corresponding character-set to your current file character-set. To disable that, use SET ATTRIBUTE CHARACTER-SET OFF. SET SEND CHARACTER-SET-SELECTION { AUTOMATIC, MANUAL } Again AUTOMATIC is the default, causing the behavior described above when you give a SET { FILE, TRANSFER } CHARACTER-SET command. Use MANUAL to allow you to specify the transfer and file character-sets independently. SHOW CHARACTER-SETS Tells settings of { SEND, RECEIVE } CHARACTER-SET-SELECTION. Normally, however, it is more convenient to leave automatic switching active, and change any associations that are not appropriate for your application, area, or country. The commands are: SHOW ASSOCIATIONS This command lists all the associations in each direction: for each possible transfer character-set, it lists the associated file character-set, and vice versa. These are two separate and independent lists. ASSOCIATE TRANSFER-CHARACTER-SET name1 [ name2 ] Changes the association for the transfer character-set name1 to be the file character-set name2. If name2 is omitted, automatic switching is disabled for this transfer character-set only. ASSOCIATE FILE-CHARACTER-SET name1 [ name2 ] Changes the association for the file character-set name1 to be the transfer character-set name2. If name2 is omitted, automatic switching is disabled for this file character-set only. 6.6. UNICODE C-Kermit 7.0 adds support for Unicode, the Universal Character Set, for: * File Transfer (SEND, RECEIVE, GET, etc) * Terminal connection (CONNECT) * Unguarded file capture (LOG SESSION) * Unguarded file transmission (TRANSMIT) * Local file character-set conversion (TRANSLATE) C-Kermit is not, however, a "Unicode application" in the sense that its commands, messages, or user interface are Unicode. Rather, it is "Unicode aware" in its ability to handle and convert Unicode text in the course of file transfer and terminal connection, and you can also use Kermit to convert local files between Unicode and other character sets. TLA's: BMP - Base Multilingual Plane BOM - Byte Order Mark CJK - Chinese, Japanese, and Korean ISO - International Standards Organization TLA - Three-Letter Acronym UCS - Universal Character Set UTF - UCS Transformation Format Unicode and ISO 10646 are the coordinated and compatible corporate and international standards for the Universal Character Set (UCS). Unlike single-byte and even most multibyte character sets, the UCS can represent all characters in every existing writing system. A flat plain-text file encoded in some form of UCS can contain any mixture of English, Spanish, Italian, German, Hebrew, Arabic, Greek, Russian, Armenian, Georgian, Japanese, Chinese, Korean, Vietnamese, Tibetan, Hindi, Bengali, Tamil, Thai, Ethiopic, and so on, plus scientific and mathematical notation, as well as texts in Runes, Ogham, Glagolitic, and other historic scripts. The UCS already covers these scripts and many more, but it's an evolving standard with efforts underway to accommodate even more languages and writing systems. Support is growing for native UCS use on many platforms and in many applications. The goal of the framers of the UCS is for it to replace ASCII, the ISO Latin Alphabets, ISCII, VISCII, the Chinese, Japanese, and Korean (CJK) multibyte sets, etc, as well as the many private character sets in use today, in other words to become *the* Universal Character Set. Until that time, however, conversions between existing sets and the UCS will be necessary when moving text between platforms and applications. Now Kermit can help. 6.6.1. Overview of Unicode For a more complete picture, please visit: [547]http://www.unicode.org/ and access the various online introductions, FAQs, technical reports, and other information. For greater depth, order the latest version of the published Unicode Standard. The following overview contains a great many oversimplifications and perhaps an opinion or two. At present, the UCS is a 16-bit (2-byte) character set, but with provisions to grow to a 4-byte set. UCS-2 refers to the two-byte set, also called the Base Multilingual Plane (BMP), in which each character has 16 bits, and therefore there are 2^16 = 65536 possible characters. The first 128 characters are the same as US ASCII (C0 control characters and DEL included), the next 32 are the C1 control characters of ISO 6429, and the next 96 are the Right Half of ISO 8859-1 Latin Alphabet 1. The remaining tens of thousands of characters are arranged newly for the UCS, usually (but not always) in sections corresponding to existing standards, such as ISO Latin/Cyrillic, often plus additional characters not appearing in the existing standards due to lack of space (or other reasons). ISO 10646 allows for additional planes, e.g. for Egyptian hieroglyphics or ancient (or other esoteric) CJK characters, but these planes are not yet defined and so we will say nothing more about them here, except that their use will require the 4-byte form of UCS, called UCS-4, in some form (more about "forms" in [548]Section 6.6.2). Unicode and ISO 10646 are constantly under revision, mainly to add new characters. The Unicode revision is denoted by a version number, such as 1.0, 1.1, 2.0, 3.0. The ISO 10646 standard revision is identified by Edition (such as ISO 10646-1 1993), plus reference to any amendments. The first versions of these standards included encodings for Korean Hangul syllables (Jamos); these encodings were changed in version 1.1 of Unicode and by Amendment 5 to ISO 10646-1. The Unicode Technical Committee and the ISO acknowledge that this was a bad thing to do, and promise never change encodings or character names again, since this poses serious problems for conformance and data interchange. A UCS-2 value is customarily written like this: U+xxxx where "xxxx" represents four hexadecimal digits, 0-9 and A-F. For example, U+0041 is "A", U+00C1 is A-acute, U+042F is uppercase Cyrillic "Ya", U+FB4F is Hebrew Ligature Alef Lamed, and U+FFFD is the special character that means "not a character". Most characters from widely-used alphabetic writing systems such as the West European ones, Cyrillic, Greek, Hebrew, Vietnamese, etc, are available in "precomposed" form; for example Uppercase Latin Letter A with Acute Accent is a single character (as it is in Latin-1). However, the UCS also permits composition of a base character with one or more nonspacing diacritics. This means the same character can be represented in more than one way, which can present problems in many application areas, including transfer and character-set conversion of text. Conversion from ASCII or Latin-1 to UCS-2 text is "trivial": simply insert a NUL (0) byte before each ASCII or Latin-1 byte. Converting in the reverse direction (provided the UCS-2 file contains only U+0000 to U+00FF) is equally simple (if we ignore the issue of composition): remove every second (NUL) byte. Conversion of other character sets to and from UCS, however, requires tables or algorithms specific to each set. Nevertheless, the relatively transparent upwards compatibility from ASCII and Latin-1, in which a very large share of the world's textual data is encoded, gives the UCS an entree onto existing platforms. But the 2-byte format and the preponderance of NUL and other control bytes in UCS-2 text pose problems for current applications and transmission methods. And to make matters worse, different hardware platforms store UCS-2 characters in different byte order. Thus a UCS-2 file transferred by FTP (or accessed via NFS, etc) between two computers with different architecture might have its bytes in the wrong order (or worse; see [549]Section 6.6.5.1 ). 6.6.2. UCS Byte Order Consider the number 1. In an 8-bit byte, this would be represented by the following series of 0- and 1-bits: +-----------------+ | 0 0 0 0 0 0 0 1 | +-----------------+ Therefore in a 16-bit "word" the representation might be: +-----------------+-----------------+ | 0 0 0 0 0 0 0 0 | 0 0 0 0 0 0 0 1 | +-----------------+-----------------+ Now consider the number 256, which is 2 to the 8th power. The binary representation is 100000000 (1 followed by 8 zeros). 256 would go into a 16-bit word like this: +-----------------+-----------------+ | 0 0 0 0 0 0 0 1 | 0 0 0 0 0 0 0 0 | +-----------------+-----------------+ When a computer works this way, it is said to be Big Endian, meaning it puts the most significant (biggest) byte first (on the "left") in a 16-bit word, and the least significant byte second (on the right). However, some other computers have the opposite arrangement, called Little Endian, in which 1 is: +-----------------+-----------------+ | 0 0 0 0 0 0 0 1 | 0 0 0 0 0 0 0 0 | +-----------------+-----------------+ and 256 is: +-----------------+-----------------+ | 0 0 0 0 0 0 0 0 | 0 0 0 0 0 0 0 1 | +-----------------+-----------------+ Computers such as Sparc, MIPS, PA-RISC, and PowerPC are Big Endian, whereas the PC and the Alpha are Little Endian. Endianness has never been an issue with 7- or 8-bit characters, but it is with UCS characters. It can be a tricky business to share or transfer a UCS-2 file between two different kinds of computers. To alleviate (but not entirely solve) the problem, UCS-2 files are supposed to begin with the Unicode character U+FEFF, Zero-Width No-Break Space (ZWNBS). This is a kind of "no-op" (note: any such assertion must normally be qualified with many "but ifs" and "excepts" which are omitted here in the interest of brevity). If the bytes are reversed the ZWNBS becomes U+FFFE, which is not (and never will be) a defined UCS character. U+FEFF at the beginning of a UCS file is therefore called a Byte Order Mark, or BOM. Any application that creates a UCS-2 (or UTF-16, or UCS-4) file should include a BOM, and any application that reads one should test for a BOM, and if one is found, infer the byte order from it. This is a convention, however -- not a standard or a requirement -- and applications vary in their ability to handle BOMs and "backwards" UCS-2 files. Note that a BOM is useful only at the beginning of a file. If you append one UCS-2 file to another, and both have BOMs, the internal BOM is no longer a BOM. And if the byte orders of the two files differ, then either the first part or the second will be backwards. (Various other undesirable effects might also occur, not discussed here.) 6.6.2. UCS Transformation Formats UCS textual data can be modified in various ways for transmission or storage. Any officially sanctioned method of doing this is called a UCS Transformation Format, or UTF. One such method, called UTF-16, is essentially identical with UCS-2 except that it designates certain code values as "escape sequences" (called surrogate pairs) to access characters in other planes without having to use full UCS-4. We won't discuss UTF-16 further here, since at the moment there are no other planes. Several other UTF's (such as UTF-1, UTF-2, and UTF-7) have fallen into disuse and are not discussed here. The most important transformation format today is UTF-8. UTF-8, so called because it "serializes" UCS-2 data into a stream of 8-bit bytes, is designed to allow the UCS to work with present-day communications gear, computers, and software. The most important properties of UTF-8 are that byte order is constant (no byte swapping) and all (7-bit) ASCII characters represent themselves. Therefore conversion between ASCII and UTF-8 is no conversion at all, and applications or platforms (such as Plan 9 from Bell Labs) that use UTF-8 "for everything" can still run traditional ASCII-only applications and be accessed from them. In particular, unlike UCS-2, ASCII characters are not padded with NUL bytes. But also unlike UCS-2, there is no transparency for Latin-1 or any other non-ASCII character set. Every non-ASCII UCS-2 character is represented by a sequence of 2 or 3 UTF-8 bytes. Thus UTF-8 is more compact than UCS-2 for text containing a preponderance of ABC's (or other ASCII characters), about the same as UCS-2 for other alphabetic scripts (Cyrillic, Roman, Greek, etc), and larger than UCS-2 for Chinese, Japanese, and Korean. The UTF-8 uncoding of the UCS has been adopted by the Internet as the preferred character set for new applications, and is gradually being retrofitted into traditional applications like FTP ([550]RFC 2640). 6.6.3. Conformance Levels Although the Unicode and ISO 10646 standards both describe the same character set, these standards differ in many ways, including their stated requirements for conformance and their classification of conformance levels. Kermit has always abided by ISO character-set standards, including ISO character-set designation and invocation methods. In adapting Unicode, therefore, we had to choose from among the available ISO designations which, in turn, correspond with ISO 10646 conformance levels. At present, Kermit claims the lowest conformance level, 1, meaning (roughly) that it does not handle combining forms and it does not handle Korean Hangul Jamos (just as, at present, it does not handle Korean in general). Note that ISO 10646 Conformance Levels 1 and 2 sidestep the issue of the code changes for Korean Hangul by announcing non-support for Hangul regardless of encoding. ISO 10646 Conformance Level 1 is approximately equivalent to Unicode Normalization Form C (described in Unicode Technical Report 15, incorporated into Unicode 3.0). As noted in [551]Section 6.6.2, Kermit does not claim to support UTF-16 at the present time, hence the UCS-2 nomenclature. Kermit treats surrogates just as if they were any other UCS-2 characters, rather than as escapes to other planes, which means that (except when converting between UCS-2 and UTF-8) they are translated to "error" characters, since (a) no other planes are defined yet (and if they were, no other character sets supported by Kermit would encode their characters), and (b) no valid surrogate character corresponds to any other UCS-2 character. A minor yet significant aspect of Unicode 3.0 and some recent perturbation of ISO 10646-1 (probably Amendment 18, "Symbols and Other Characters") is the addition of the Euro Sign at U+20AC. As noted in [552]Section 6.0, Kermit's "Euro compliance" includes conversion between Latin Alphabet 9 and various PC code pages. Text can also be converted between UCS-2 or UTF-8 and any other Euro-compliant character set (Latin-9, CP858, CP1250, CP1252) without loss of the Euro Sign. 6.6.4. Relationship of Unicode with Kermit's Other Character Sets Kermit's character sets are divided into two groups: single-byte sets (such as Roman, Hebrew, Cyrillic, Greek) and multibyte (various Japanese sets). The two groups are distinct since one normally would not expect to convert Kanji ideograms to Roman (or other) letters, or vice versa. Unicode character-set conversion works with both groups, but obviously the result depends on the repertoires of the source and destination character-sets both including the characters in the file. For example, you can translate a Hungarian text file between Latin-2 and Unicode, but not between (say) Unicode and Latin/Greek. By the same token you can convert Japanese text from Shift-JIS or EUC or JIS-7 to Unicode and back, but you can't convert the same file to (say) Latin-1 if it contains Japanese characters. JIS-7 is equivalent to DEC Kanji and ISO-2022-JP except that the latter two do not support halfwidth Katakana. Kermit treats all three of these sets the same way, i.e. as JIS-7. As noted, Kermit presently does not handle combining diacritics, and so will not correctly convert UCS files that use them into a single-byte character set. For example, if a UCS file contains Latin Capital Letter A (U+0041) followed by Combining Acute Accent (U+0301), the result will be a two-character sequence, A followed by another character. This is what is meant by Conformance Level 1. (The situation grows worse with multiple diacritics, since they can occur in any order.) A higher level of conformance is possible, in which "canonical equivalences" are handled via algorithms and databases, at some (perhaps considerable) cost in performance, since a fair amount of additional code must be executed for every character during data transfer (database lookup, sorting of combining sequences into canonical order, etc). This can be added in future releases if there is a need (but in many cases, pre- and postpostprocessing might be a better option). Within these constraints, Kermit converts between the UCS and its other character sets. For example, a mixture of Russian and English (and/or Dutch, or Latin) text can bet converted between the UCS and ISO Latin/Cyrillic or KOI-8. But since Kermit does not presently support Arabic character-set conversion, the new availability of UCS conversion does not mean that Kermit can convert from Arabic UCS text to some other character set, because Kermit does not support any other character set that includes Arabic. Ditto for Thai, Armenian, Georgian, Tibetan, Chinese, Korean, etc. However, Kermit CAN convert Arabic (or any other script) between UCS-2 and UTF-8. Considering Cyrillic more carefully, note that the UCS also contains numerous Cyrillic characters not found in any of the Cyrillic sets (ISO Latin/Cyrillic, KOI8, CP866, etc) that Kermit supports; characters needed for Abkhasian, Yakut, Tatar, Bashkir, Altaic, Old Church Slavonic, etc; UCS text containing any of these historic or "extended" Cyrillic characters can not be converted to any of Kermit's current single-byte Cyrillic sets without loss. The situation is similar for Greek, Hebrew, etc, and even worse for Japanese since Unicode contains thousands of Kanjis that are lacking from the Japanese character sets based on JIS X 0208, such as EUC-JP, JIS-7, and Shift-JIS. In general, when converting from UCS to a single-byte set, there is always the possibility of data loss, just as there is when converting from any larger set to a smaller one. For example, if a UCS file contains Devanagari characters, these characters will be lost when converting to (say) Latin-1, just as Roman vowels with acute accents are lost when converting from Latin-1 (an 8-bit set) to German ISO 646 (a 7-bit set). 6.6.5. Kermit's Unicode Features C-Kermit can convert between UCS-2 or UTF-8 and any of its other character sets, and also between UCS-2 and UTF-8. When converting between UCS-2 or UTF-8 and a non-Unicode character set (such as Latin-1), the UCS Line Separator (LS, U+2028) and Paragraph Separator (PS, U+2029) characters are converted to the appropriate line terminator (CR, LF, or CRLF). When converting from a non-Unicode set to UCS-2 or UTF-8, however, line terminators are not converted to LS or PS. This is in accordance with the recommendations of Unicode Technical Report #13. When C-Kermit starts, it tests the native byte order of the computer. You can see the result in the SHOW FEATURES or SHOW FILE display. It's also available in the variable \v(byteorder): 0 means Big Endian, 1 means Little Endian. When UCS-2 is involved in file transfer or translation, the following commands tell C-Kermit what to do about byte order: SET FILE UCS BYTE-ORDER { BIG-ENDIAN, LITTLE-ENDIAN } This is for reading UCS-2 files that don't have a BOM, and also for writing UCS-2 files. If this command is not given, the machine's native byte order is used when writing UCS-2 files, and also when reading UCS-2 files that don't have a BOM. SET FILE UCS BOM { ON, OFF } This setting is used when creating UCS-2 files. A BOM is added at the beginning by default. Use OFF to not add the BOM. This command has no affect when writing files. COPY /SWAP-BYTES sourcefile destinationfile Use this for fixing a UCS-2 file whose bytes are in the wrong order. Use SHOW FILE to display the FILE UCS settings. Please note, again, that C-Kermit's user interface, including its script language, is not internationalized in any way. String comparisons, case conversion, and so on, work only for US ASCII (comparisons for equality work with other sets, but not lexically-greater-or-less-than or caseless comparisons; even comparisons for equality can fail when composed characters or byte order are involved). String functions such as \findex() and \fsubstring() that reference byte positions do just that; they won't work with UTF-8 text that contains any non-ASCII characters, and they will not work with UCS-2 text at all since they use C strings internally, which are NUL-terminated. These are just a few examples to illustrate that neither Unicode nor any other character-set beyond ASCII is supported at the user-interface, command, or scripting level in this version of C-Kermit. 6.6.5.1. File Transfer Kermit supports both UCS-2 and UTF-8 as file and transfer character sets in text-mode file transfer. To select UCS-2 or UTF-8 as a file character-set, use: SET FILE CHARACTER-SET { UCS2, UTF8 } If you want to send a UCS-2 text file (or save an incoming file in UCS-2 format), tell Kermit to: SET FILE CHARACTER-SET UCS2 and if you want to send a UTF-8 text file (or store an incoming file in UTF-8 format), tell Kermit to: SET FILE CHARACTER-SET UTF8 When sending UCS-2 files, Kermit determines the byte order from the BOM, if there is one (and if there is a BOM, it is stripped, i.e. not sent). If there is no BOM, the byte order is the one specified in the most recent SET FILE UCS BYTE-ORDER command, if any, otherwise the computer's native byte order is assumed. When storing incoming files as UCS-2, the byte order is according SET FILE UCS BYTE-ORDER, if given, otherwise the native one; a BOM is written according to SET FILE UCS BOM. A transfer character-set should be chosen that includes all of the characters in the source file. So, for example, if you are sending a UCS-2 file containing only German-language text, your transfer character-set can be Latin-1, Latin-2, Latin-9, UCS-2, or UTF-8. But if you are sending a file that contains a combination of Hebrew and Greek, your transfer character-set must be UCS-2 or UTF-8 if you don't want to lose one script or the other. Furthermore, the transfer character-set must be one that is supported by the receiving Kermit program. Since UCS support is new, it is possible that the other Kermit program (if it supports character sets at all) does not support it, but does support single-byte sets such as Latin-1, Latin/Cyrillic, etc. To select UCS-2 or UTF-8 as a transfer character-set, use: SET TRANSFER CHARACTER-SET { UCS2, UTF8 } It is up to the receiving Kermit program to convert the transfer format to its own local format, if necessary. If it does not understand the UTF-8 or UCS-2 transfer character-set, and your file can not be adequately represented by any single-byte transfer character-set (such as Latin-1 or Latin/Cyrillic) then, if UTF-8 format is acceptable on the receiving computer, use UTF-8 as the transfer character-set, with the receiver told to "set unknown-char keep", or with the sender told to "set attribute char off". If you want the file to be stored in UCS-2 format at the receiver, send it it binary mode if the source file is also UCS-2, or else use the TRANSLATE command (next section) to convert it to UCS-2 first, then send it in binary mode. You should not use UCS-2 as a transfer character-set in text-mode transfers to Kermit programs that don't support it, because they are likely to corrupt the result the same way FTP would (see the final paragraph of this section). When UCS-2 is the transfer character set, it always goes into Kermit packets in Big Endian format, with no BOM. As always, the transfer character-set is announced by the sender to the receiver. The announcement for UCS-2 is "I162" (ISO Registration 162 = UCS-2 Level 1) and by definition it is Big Endian (the standards say that when UCS-2 is serialized into bytes, the order must be Big Endian). The announcement for UTF-8 is "I190" (UTF-8 Level 1). When receiving a file whose transfer character-set is UCS-2 or UTF-8, you must choose the appropriate file character set for the result. There is no way Kermit can do this for you automatically, since UCS data can be in any script at all, or any combination. In general, UTF-8 or UCS-2 should be chosen as a transfer character-set if the source file is also encoded in some form of UCS and it contains more than one script. But there are other situations where where UTF-8 or UCS-2 offer advantages. For example, suppose the source file is on a NeXTstation and the destination file is on VMS. Both the NeXT and the DEC Multinational character sets include the French OE digraph, but Latin-1 does not. Therefore French words containing this character might not arrive intact when Latin-1 is the transfer character-set, but will with UTF-8 or UCS-2, since the UCS includes the OE digraph (but so does Latin-9). UCS-2 should be chosen as a transfer character-set only for Japanese text files that contain a large preponderance of Kanji, since in this case (and only this case) UCS-2 (two bytes per Kanji) is more efficient than UTF-8 (three bytes per Kanji). The same will be true for Chinese and Korean when they are supported by Kermit. UCS-2 should never be used as a transfer character-set with a transfer partner that does not support UCS-2 since this can cause file corruption (see last paragraph in this section). Note that Kermit's repeat-count compression is 100% ineffective for UCS-2, and is also ineffective for multibyte characters in UTF-8 and EUC-JP; this is because repeat-compression is a transport-level mechanism that operates on a per-byte basis; it has no knowledge of the distinction between a byte and a character. When C-Kermit starts, it sets up associations ([553]Section 6.5) for incoming files whose transfer character sets are UCS-2 or UTF-8 appropriately for the platform so that the file character-set for the incoming file is UCS-2 in Windows and UTF-8 elsewhere. Otherwise, C-Kermit does not make any default associations for UCS-2 or UTF-8, but of course you may add or change associations to suit your needs and preferences by including the appropriate ASSOCIATE commands in your Kermit startup file. For example, if you are a PC user and deal only with text written in Greek and English, you can: ASSOCIATE TRANSFER-CHARACTER-SET UTF8 CP869 ASSOCIATE TRANSFER-CHARACTER-SET UCS2 CP869 ASSOCIATE FILE-CHARACTER-SET CP869 UTF8 Note that when file transfer involves conversion between a single-byte character set and UCS-2 or UTF-8, the file-transfer thermometer and estimated time left might be inaccurate, since they are based on the source file size, not the transfer encoding. This is purely a cosmetic issue and does not effect the final result. (And is not, strictly speaking, a bug; Kermit protocol presently includes no method for the sender to furnish an "estimated transfer size" to the receiver, and in any case any such guess could be as far off as the file size, given the many other factors that come into play, such as compression and prefixing). A caution about FTP and UCS-2. As noted previously, if you transfer a UCS-2 file with FTP in binary mode between two computers with opposite Endianness, the result will have its bytes in the wrong order. However, if you use FTP to transfer a UCS-2 file in "ascii" (text) mode to ANY computer, even if it is identical to yours, the result will be corrupted because FTP's line-terminator conversions do not account for UCS-2. The same holds when sending from a UCS-aware Kermit program to an older Kermit program in text mode with a transfer character-set of UCS-2. So use UCS-2 as a transfer character-set ONLY with a UCS-2-aware Kermit partner. 6.6.5.2. The TRANSLATE Command In Kermit versions that have Unicode support included, TRANSLATE now always goes through Unicode; that is, the source set is converted to UCS-2 and thence to the target set. This is a major improvement, since in prior releases, C-Kermit had to pick the "most appropriate" transfer character-set as the intermediate set, and this would result in the loss of any characters that the source and target sets had in common but were lacking from the intermediate set (for example the OE digraph when translating from NeXT to DEC MCS through Latin-1). This never happens when Unicode is the intermediate set because Unicode is a superset of all other character sets supported by Kermit. A more dramatic example would be translation between Cyrillic PC code page 866 and KOI8-R ([554]Section 6.4); formerly all the line- and box-drawing characters would be lost (since ISO 8859-5 does not have any); now the ones that these two sets have in common are preserved. UCS-2 and UTF-8 are now both supported as source-file and destination-file character sets by C-Kermit's TRANSLATE command, for example: translate oofa.txt ucs2 latin1 oofa-l1.txt translates oofa.txt from UCS-2 to Latin-1, storing the result as oofa-l1.txt. Similarly: translate oofa.txt utf8 latin1 oofa-l1.txt translate oofa.txt latin1 ucs2 oofa-ucs2.txt translate oofa.txt latin1 utf8 oofa-utf8.txt translate oofa.txt ucs2 utf8 oofa-utf8.txt translate oofa.txt utf8 ucs2 oofa-ucs2.txt Treatment of the UCS-2 BOM is exactly the same as for file transfer. Note that if a UCS-2 source file is in the "wrong" byte order and lacks a BOM, and you don't tell Kermit about it with SET FILE UCS BYTE-ORDER, the result of the translation is total gibberish. Recall that you can use COPY /SWAP-BYTES to switch the byte order of an errant UCS-2 file (or any other file for that matter, if you can think of a reason to). Also note that: translate oofa.txt ucs2 ucs2 new.txt Produces a result in the native (or SET FILE UCS) byte-order as long as oofa.txt has a BOM. As a side benefit of the Unicode work, the TRANSLATE command now works for the first time also for all Japanese character sets that Kermit supports. In other words, if you have a Japanese text file in any of the following encodings: EUC-JP Shift-JIS JIS-7 UCS-2 UTF-8 You can use the TRANSLATE command to convert to any other encoding from the same list. 6.6.5.3. Terminal Connection The CONNECT command now allows UTF-8 as a local or remote terminal character-set: SET TERMINAL CHARACTER-SET { ..., UTF8 } { ..., UTF8 } SET TERMINAL REMOTE-CHARACTER-SET { ..., UTF8 } SET TERMINAL LOCAL-CHARACTER-SET { ..., UTF8 } (Recall that Kermit's terminal character-set has two "ends" -- the set used on the host to which Kermit is connected, and the set used on the local keyboard and screen.) UCS-2 is not supported as a terminal character-set (either end) since (a) it is not used that way anywhere to our knowledge, and (b) the problems of Endianness and the high likelihood of loss of synchronization make it impractical. (Telecommunications is byte-oriented; if one byte, or any odd number of bytes, is lost because of buffer overruns, circuit resets, etc (or likewise if a burst of noise appears that takes the guise of an odd number of bytes), the byte order of the subsequent data stream will be backwards; unlike UTF-8 and traditional byte-based character sets, UCS-2 is not "self synchronizing".) UTF-8 does not have byte-order or synchronization problems and is growing in popularity as a terminal character set as well as in other application areas. It allows a single terminal session to use multiple scripts (Roman, Cyrillic, Greek, etc) without ISO 2022 character-set switching (which terminal emulators like Kermit 95 can handle but few host applications understand or use), and meshes nicely with the Unicode screen fonts that are beginning to appear. UTF-8 was first used in Plan 9 and soon will be available in Linux. It will probably spread from there (Unicode in some form is, of course, also used in Windows NT, but only internally -- not for access from outside). To use UTF-8 or any other character set that uses 8-bit bytes in your terminal session, be sure to tell C-Kermit to: SET TERMINAL BYTESIZE 8 SET COMMAND BYTESIZE 8 SET PARITY NONE (or use the shortcut command, EIGHTBIT, which does all three at once). In a setup where your local Kermit program uses a single-byte character set such as PC Code Page 850 and the remote host uses UTF-8: SET TERM CHAR UTF8 CP850 or: SET TERM REMOTE CHAR UTF8 SET TERM LOCAL CHAR CP850 all works as expected. UTF-8 text on the remote displays correctly on your screen, and when you type CP850 characters, they are translated to UTF-8 sequences for transmission, and the echo from the host is translated from UTF-8 back to CP850. Telnet negotiations and autodownload take place before any character-set translation and work as before. The session log (if text mode was selected for it) contains only the local terminal character-set. And so on. Kermit merely supplies translations from UTF-8 to your local terminal character-set (this includes treating UTF-8 Line Separator and Paragraph separator as CRLF). However, Kermit does does not, at present, perform "canonicalization" of composed sequences, nor does it automatically execute bidirectionality algorithms for display of mixed-direction text (e.g. Hebrew and English). Such presentation issues, like all others in the terminal-host regime, are left to the host. By the way, C-Kermit also allows UTF-8 to be the local end of the terminal character-set, but so far this code is not tested, since we don't have a UTF-8 console or terminal to work with. However, it can be stated without doubt that C-Kermit's key mapping will not work for UTF-8 values, since (a) the key map is indexed by 8-bit byte values and (b) C-Kermit reads keystrokes a byte at a time (these comments do not apply to K95, which has direct access to the keyboard and can read "wide" keycodes and uses them to index a "wide" keymap). Restrictions: As noted, the CONNECT command does not support UCS-2 as a REMOTE TERMINAL character-set. Neither does it support the Japanese sets EUC-JP, JIS-7, and Shift-JIS. Support for the Japanese sets (and possibly Chinese and Korean too) might be added in a future release. Since the TRANSMIT command (next section) uses the same REMOTE TERMINAL character-sets as the CONNECT command, it has the same restrictions. 6.6.5.4. The TRANSMIT Command As described in Chapter 15 of [555]Using C-Kermit and [556]Section 4.21 of this document, the TRANSMIT command can be used to upload a file without protocol, more or less as if you were typing it on your keyboard while connected to the host. When TRANSMITting in text mode, the file's character set is converted to the host's unless you have SET TERMINAL CHARACTER-SET TRANSPARENT, or you include the new TRANSMIT switch, /TRANSPARENT. Before C-Kermit 7.0, the file character-set was assumed to be the same as the local end of the terminal character-set, and the TRANSMIT command used the same translations as the CONNECT command, ignoring the file character-set. In C-Kermit 7.0, that assumption (a poor one to begin with) can no longer be made, since UCS-2 can be a file character-set but not a terminal character-set. So now the file's character-set is given by your most recent SET FILE CHARACTER-SET command. The host's character set is the remote end of your most recent SET TERMINAL CHARACTER-SET command: SET TERMINAL CHARACTER-SET remote-set [ local-set ] or: SET TERMINAL REMOTE-CHARACTER-SET remote-set The TRANSMIT command converts each source-file character from the FILE character-set to the REMOTE TERMINAL character-set, and then transmits the translated characters according to your SET TRANSMIT preferences (Chapter 15). If you have SET TRANSMIT ECHO ON, and the host is echoing the transmitted characters, the echos are converted from the remote terminal character-set to the local terminal character-set. [ A picture would help... ] Confused? Let's work through an example. Suppose your local computer is a NeXTstation, on which text files are encoded in the NeXT character set, and that the remote computer is a Data General AViiON, which uses the Data General International character set. Further suppose that you are logged in to the NeXT from a VT220 terminal which uses the DEC Multinational character set. You need to convert the file from NeXT encoding to DG encoding and convert the echoes from DG encoding to DEC encoding. So on the NeXT, tell C-Kermit to: eightbit set file character-set next set term character-set dg-international dec-mcs transmit /text nextdata.txt (This assumes you have some sort of collection process already set up on the Data General, such as a text editor or the venerable "cat > foo". The EIGHTBIT command is equivalent to SET TERMINAL BYTESIZE 8, SET COMMAND BYTESIZE 8, SET PARITY NONE.) To further complicate matters, suppose your local terminal character set is the same as the remote one, so you don't need terminal character-set translation, but you need to TRANSMIT a file that is in a different character set and you want it translated to the host set. In this case, use SET TERM CHARACTER-SET to actually specify the character set used on each end, rather than specifying TRANSPARENT: eightbit set file character-set ucs2 set term character-set latin1 latin1 transmit /text ucs2data.txt The distinction between: SET TERMINAL CHARACTER-SET xxx yyy (where xxx and yyy are the same set) and: SET TERMINAL CHARACTER-SET TRANSPARENT is new to C-Kermit 7.0, but affects only the TRANSMIT command. The TRANSMIT command currently does nothing special with UCS-2/UTF-8 Line and Paragraph Separator characters; more experience is required to find out how these behave in a genuine Unicode terminal-host setting. Restrictions: As noted, the TRANSMIT command translates from the FILE character-set to the REMOTE TERMINAL character-set. This rules out translations to any character set that is not supported as a REMOTE TERMINAL character-set, such as UCS-2, EUC-JP, JIS-7, and Shift-JIS. 6.6.5.5. Summary of Kermit Unicode Commands Specifying file character-set and byte order: SET FILE CHARACTER-SET { ..., UCS2, UTF8 } REMOTE SET FILE CHARACTER-SET { ..., UCS2, UTF8 } (See next section) SET FILE UCS BOM { ON, OFF } SET FILE UCS BYTE-ORDER { BIG-ENDIAN, LITTLE-ENDIAN } Specifying the transfer character-set: SET TRANSFER CHARACTER-SET { ..., UCS-2, UTF-8 } REMOTE SET TRANSFER CHARACTER-SET { ..., UCS-2, UTF-8 } Specifying the terminal character-set: SET TERMINAL CHARACTER-SET { ..., UTF8 } { ..., UTF8 } SET TERMINAL REMOTE-CHARACTER-SET { ..., UTF8 } SET TERMINAL LOCAL-CHARACTER-SET { ..., UTF8 } Displaying settings: SHOW FILE SHOW TRANSFER SHOW TERMINAL SHOW CHARACTER-SETS Commands that use these settings include: SEND, RECEIVE, GET, etc. CONNECT TRANSMIT LOG SESSION Converting files: TRANSLATE infile { ..., UCS-2, UTF-8 } { ..., UCS-2, UTF-8 } outfile COPY /SWAP-BYTES infile outfile 6.7. Client/Server Character-Set Switching A simple mechanism has been added to allow the client to change the server's FILE CHARACTER-SET: REMOTE SET FILE CHARACTER-SET name The client asks the server to change its file character-set to the one given. The name must match one of the server's file character-set names. For convenience, C-Kermit uses its own file character-set keyword list for parsing this command so you can use ? for help and Tab or Esc for completion. However, since the server might have a different repertoire (or even use different names for the same sets), C-Kermit accepts any string you supply and sends it to the server. The server, if it supports this command (C-Kermit 7.0 and K95 1.1.19 do), sets its file character-set as requested, and also disables automatic character-set switching ([557]Section 6.5). If the server does not support this command or if it does not support the given character set, the REMOTE SET FILE CHARACTER-SET command fails. Here's an example that sends a Japanese text file encoded in Shift-JIS to a server using every combination of Kermit's Japanese-capable file and transfer character sets: dcl \&x[] = euc ucs2 utf8 ; transfer character-sets dcl \&y[] = eu uc ut ; 2-letter abbreviations for them dcl \&f[] = shift euc jis7 ucs2 utf8 ; file character-sets dcl \&g[] = sj eu j7 uc ut ; 2-letter abbreviations set file char shift-jis ; local file character-set is Shift-JIS for \%i 1 \fdim(&x) 1 { ; for each transfer character-set... set xfer char \&x[\%i] ; set it for \%j 1 \fdim(&f) 1 { ; for each remote file character-set... remote set file char \&f[\%j] ; set it if fail exit 1 SERVER REJECTED CHARSET send /text meibo-sj.html meibo-sj-\&y[\%i]-\&g[\%j].txt ; send the fil e if fail exit 1 TRANSFER FAILED } } The Kermit-370 server does not support REMOTE SET FILE CHARACTER-SET, but since it supports REMOTE KERMIT commands, you can get the same effect with REMOTE KERMIT SET FILE CHARACTER-SET name. 7. SCRIPT PROGRAMMING (Also see [558]Section 2.8, Scripting Local Programs.) 7.0. Bug Fixes The following script programming bugs were fixed in C-Kermit 7.0: * IF EXIST and IF DIRECTORY were fixed to properly strip braces from around their arguments, so "if directory {C:\Program Files}", etc, would work as expected. However, this means that if the file or directory name is actually enclosed in braces, the braces must be doubled. * The READ command did not fail if the READ file wasn't open; now it does. * The READ command refused to read the last or only line of a file if it did not end with a proper line terminator; now it does. * The END command, when given from within a SWITCH statement, did not exit from the current macro or command file; instead it just terminated the SWITCH. 7.1. The INPUT Command 7.1.1. INPUT Timeouts The description of the INPUT command on page 422 fails to mention the following two points about the timeout (which apply to C-Kermit 6.0 and later): 1. "INPUT -1 text" (or "INPUT \%x text", where \%x is any variable whose value is -1 or less) means "wait forever". This form of the INPUT command fails only if it is interrupted, since it will never time out. 2. INPUT 0 performs a nonblocking read of material that has already arrived but has not yet been read, and succeeds immediately if the target string is found, or fails immediately if it is not found. The same points apply to MINPUT. REINPUT ignores its timeout parameter. 7.1.2. New INPUT Controls The following new INPUT controls were added in version 7.0: SET INPUT AUTODOWNLOAD { ON, OFF } Explained in [559]Section 7.7. SET INPUT CANCELLATION { ON, OFF } This governs whether an INPUT command can be canceled by "pressing any key" on the keyboard. Normally it can be, in which case the INPUT command fails immediately and \v(instatus) is set to 2, indicating interruption. SET INPUT CANCELLATION OFF disables keyboard cancellations; thus if the search text is not encountered, the INPUT command will run for its entire timeout interval. SET INPUT CANCELLATION OFF does not disable interruption by Ctrl-C, however; every command needs an emergency exit. (If you really want to disable interruption by Ctrl-C, use SET COMMAND INTERRUPTION OFF.) Also see [560]Section 7.2 for any new variables related to INPUT. 7.1.3. INPUT with Pattern Matching C-Kermit 7.0 allows INPUT, MINPUT, and REINPUT targets to be a pattern (explained in [561]Sections 1.19 and [562]4.9). This solves a long-standing problem illustrated by the following scenario: a certain company has a bank of TCP/IP modem servers, with hostnames server1, server2, server3, and so on. Each server's prompt is its name, followed by a colon (:), for example "Server72:". Without INPUT patterns, it would be rather difficult to wait for the prompt. The brute force approach: minput 20 Server1: Server2: Server3: ... (enumerating each one) is subject to failure whenever a new server is added. A more subtle approach: input 20 Server if fail ... input 2 : is liable to false positives, e.g. "Welcome to the XYZ Corp Modem Server. Please read the following message:"... With patterns, you can match the prompt with "Server*:" (which doesn't solve the "false positives" problem, but certainly is more compact than the brute force method), or with more specific patterns such as "Server[1-9]:" and "Server[1-9][0-9]:", or equivalently: Server{[1-9],[1-9][0-9]}: meaning the word "Server" followed by a single digit (1-9) or by two digits representing a number from 1 to 99, followed by a colon. INPUT pattern matching has been added in a way that does not interfere with existing scripts. No new commands or switches are used. The simple rule is: if an INPUT search target is the argument of the (new) \fpattern() function, it is a pattern. Otherwise it is taken literally, as before. For example: input 5 a*b searches for an 'a' followed by an asterisk ('*'), followed by a 'b'. But: input 5 \fpattern(a*b) searches for an 'a' followed by anything at all up to and including the first 'b'. This means that any search target to INPUT, MINPUT, or REINPUT can be a pattern or a literal string, and in particular that MINPUT can accommodate any mixture of patterns and literal strings. In selecting patterns, note that: * A leading '*' is always implied so there is no need to include one. * A trailing '*' is meaningless and ignored. * A '*' by itself matches the first character that arrives. A syntax note: If your pattern is a selection list, meaning a list of alternatives separated by commas and enclosed in braces, then the outer braces will be stripped by various levels of parsers, so you must include three of each: input 10 \fpattern({{{abc,mno,xyz}}}) Note that this is equivalent to: minput 10 abc mno xyz except for the setting of the \v(minput) variable. And a caution: INPUT pattern matching has a limitation that you probably never noticed with literal-string matching, namely that there is a limit on the size of the match. For example, if the pattern is "a*b", the match will succeed if the 'a' and 'b' are not separated by more than (say) 8K bytes, but will fail if they are farther apart than that. In such cases, it better to use two INPUTs (e.g. "input 10 a" and then "input 100 b"). 7.1.4. The INPUT Match Result The result of any INPUT, MINPUT, or REINPUT command, no matter whether the search targets are patterns or literal strings, is available in the new \v(inmatch) variable. For example: minput 10 cat \fpattern([dh]og) if success echo MINPUT matched "\v(inmatch)" This is especially useful when a pattern was matched, since it makes the string that matched the pattern available to Kermit; there would be no way to get it otherwise. After an INPUT command, you can view all the INPUT-related variables by typing "show variables in" (abbreviate as "sho var in"), which shows the values of all built-in variables whose names start with "in". 7.2. New or Improved Built-In Variables \v(blockcheck) Current BLOCK-CHECK setting, 1, 2, 3, or 4. 4 is the code for BLANK-FREE-2. \v(byteorder) The machine's byte order: 0 = Big Endian, 1 = Little Endian. \v(cmdbufsize) The length of the command buffer, which is the maximum size for a macro, a command, a variable, or anything else in C-Kermit's script language. \v(ctty) The device name of C-Kermit's controlling (login) terminal. \v(filename) Described in [563]Sections 4.1 and [564]4.2. \v(filenumber) Described in [565]Sections 4.1 and [566]4.2. \v(filespec) As of C-Kermit 7.0, contains fully qualified filenames rather than (usually) relative ones. \v(return) Now holds the END n value of the macro that most recently returned, in case END was used rather than RETURN. \v(editor) Pathname of preferred text editor \v(editopts) Command-line options for editor \v(editfile) File most recently edited \v(browser) Pathname of preferred Web browser \v(browsopts) Command-line options for Web browser \v(browsurl) URL most recently given to Web browser \v(dialtype) Type of call most recently placed (see [567]Section 2.1.11). \v(kbchar) The character, if any, that was typed at the keyboard to to interrupt the most recent PAUSE, SLEEP, WAIT, MSLEEP, or INPUT command; empty if the most recent such command was not interrupted from the keyboard. \v(lockdir) UNIX only - The name of the UUCP lockfile directory, if known, otherwise "(unknown)". \v(lockpid) UNIX only - PID of process that owns the communication port that you tried to open with a SET LINE command that failed because the port was in use, otherwise empty. This variable is set with every SET LINE command. \v(cx_time) If no connection (SET HOST, SET LINE, DIAL, TELNET, etc) is active, this is 0. If a connection is active, this is the number of seconds since the connection was made. \v(hwparity) If hardware parity is in effect, this variable gives its value, such as "even" or "odd" (in which case, the \v(parity) variable will be "none"). Otherwise this variable is empty. \v(serial) Current serial port settings in 8N1 format ([568]Section 2.10). \v(errno) In UNIX, the current value of the C runtime errno variable, which is quite volatile (meaning that often an "interesting" error code can be overwritten by some other library call or system service that sets errno before you have a chance to look at it). In VMS, the error code returned by the system or library call that most recently failed (success codes are not saved). Not available in other operating systems. \v(errstring) The UNIX or VMS system error message that corresponds to \v(errno). Not available in all OS's. Also see [569]\ferrstring(). \v(setlinemsg) The error message, if any, from the most recent SET LINE, SET PORT, SET HOST, TELNET, or other connection-making command. This is not necessarily the same as \v(errstring) since these commands might fail without generating a system error code, for example (in UNIX) because a lockfile existed indicating the device was assigned by another user. \v(exitstatus) The exit status C-Kermit would return if it exited now. \v(pexitstat) The exit status of the inferior process most recently invoked by C-Kermit (by RUN, !, REDIRECT, SEND /COMMAND, etc). In VMS, this code can be given to \ferrstring() to get the corresponding error message (in UNIX, program/command return codes are not the same as system error codes). Not available in operating systems other than UNIX and VMS. See [570]Section 4.2.5 for details. \v(inmatch) The incoming string of characters, if any, that matched the most recent INPUT, REINPUT, or MINPUT command. \v(intime) The number of milliseconds (thousandths of seconds) it took for the most recent INPUT command to find its match, or -1 if no INPUT command has been given yet. If the INPUT command timed out, the value is approximately equal to 1000 times the INPUT timeout. If INPUT failed for some other reason, the value is undefined (\v(instatus) gives INPUT completion status). If your version of C-Kermit is built without high-precision floating-point timers, this number will always be a multiple of 1000. \v(inwait) The number of seconds specified as the timeout in the most recent INPUT command. \v(dialsuffix) Dialing suffix for use during PDIAL sequence; see [571]Section 2.1.10. \v(pid) UNIX, VMS, and K95 only. C-Kermit's primary process ID, numeric, decimal. If you want to show it in hex, use \fn2hex(\v(pid)) If you want to show it in octal, use \fn2octal(\v(pid)). \v(printer) Current printer name or SET PRINTER value. \v(p_ctl) Control prefix char \v(p_8bit) 8-bit prefix char (if parity not none) \v(p_rpt) Repeat prefix char (if repeat compression enabled) \v(herald) Kermit's version herald \v(test) Kermit's test version, if any, or 0 if this is not a test version. Typical values for test versions are "Alpha.03" or "Beta.14". \v(sendlist) The number of entries in the SEND-LIST, 0 if none. Note: entries do not necessarily correspond to files, since an entry might contain wildcards. Also note that the value does not go back to 0 after the files in the list are sent. To reset this variable, use CLEAR SEND-LIST. The purpose of this variable is to determine if a SEND command, when given without any filenames, will be legal. Example: xif \v(sendlist) { send } else { send oofa.txt } \v(trigger) If the most recent CONNECT session was terminated automatically by a trigger, this variable contains the trigger value. \v(ty_ln) TYPE line number (during TYPE) \v(ty_lc) TYPE line count (after TYPE) \v(ty_mc) TYPE match count (after TYPE) \v(xferstat) Status of most recent file transfer: -1: No transfer yet 0: Succeeded 1: Failed \v(xfermsg) If the most recent file transfer failed, this is the reason. If it succeeded, \v(xfermsg) is an empty string. \v(tftime) Total elapsed time of most recent file transfer operation, in seconds. \v(textdir) Directory that holds (or is supposed to hold) Kermit text files such as installation instructions, release notes, update notes, read-me files, "beware" files, etc. \v(name) The name with which the Kermit program was invoked, e.g. "kermit", "wermit", "k95", "k2", etc (see [572]Section 9.1). \v(osname) Name of operating system on computer where C-Kermit is running, obtained at runtime (from uname or equivalent). \v(osversion) Version of operating system on computer where C-Kermit is running, obtained at runtime (from uname or equivalent). \v(osrelease) Release of operating system on computer where C-Kermit is running, obtained at runtime (from uname or equivalent). \v(model) The specific hardware model of the computer where C-Kermit is running, if known. \v(math_pi) The value of Pi (see [573]Section 7.23) \v(math_e) The value of e (see [574]Section 7.23) \v(math_precision) How many significant digits in a floating-point number. \v(f_count) Result of the most recent FILE COUNT (FCOUNT) command. \v(f_error) Numeric error code of most recent FILE command. \v(f_max) Maximum number of files open simultaneously. The math constants are given in the precision of underlying computer's floating-point arithmetic. Note the distinction between \v(osname), \v(osversion), and \v(platform); the latter refers to the platform for which and/or upon which C-Kermit was built, as opposed to the one on which it is actually running. Also note that each operating system can, and probably will, interpret and fill in the os* variables differently, or not at all. The SHOW VARIABLES command now accepts a variable name, prefix, or pattern: show variables Shows all variables. show variables t Shows all variables that start with "t". show variables *ver* Shows all variables whose names contain "ver". show variables *ver Ditto (an implied "*" is appended). 7.3. New or Improved Built-In Functions The following new file-i/o functions are explained in [575]Section 1.22. \f_status(channel) Status of file open on channel \f_pos(channel) Read/write (byte) pointer of given file \f_line(channel) Current line of file \f_handle(channel) Handle of file \f_eof(channel) Whether given file is at EOF \f_getchar(channel) Read a char from given file \f_getline(channel) Read a line from given file \f_getblock(channel,n) Read a block from given file \f_putchar(channel,c) Write a char to given file \f_putline(channel,string) Write a line to given file \f_putblock(channel,string) Write a block to given file The following new date-time-related functions are explained in [576]Section 1.6: \fday() Returns day of week of given date \fnday() Returns numeric day of week of given date \ftime() Returns time portion of given date-time \fntime() Converts time to seconds since midnight \fn2time() Converts seconds since midnight to hh:mm:ss \fcvtdate(date-time) Converts free-format date to yyyymmdd hh:mm:ss \fdayofyear(date-time) Converts date to yyyyddd (day-of-year) format \fdoy(date-time) Synonym for \fdayofyear() \fdoy2date(dayofyear) Converts yyyyddd to yyyymmdd \fmjd(date-time) Converts free-format date to Modified Julian Date \fmjd2date(mjd) Converts modified Julian date to yyyymmdd The new floating-point arithmetic functions are explained in [577]Section 7.23. f1 and f2 are floating-point (real) numbers; d is the number of decimal places to show: \ffpabsolute(f1,d) Absolute value of f1 \ffpadd(f1,f2,d) f1 + f1 \ffpcosine(f1,d) Cosine of f1 \ffpdivide(f1,f2,d) f1 divided by f2 \ffpexp(f1,d) e to the f1 power \ffpint(f1) Integer part of f1 \ffplog10(f1,d) Log base 10 of f1 \ffplogn(f1,d) Natural log of f1 \ffpmaximum(f1,f2,d) Maximum of f1 and f2 \ffpminimum(f1,f2,d) Minimum of f1 and f2 \ffpmodulus(f1,f2,d) Modulus of f1 and f2 \ffpmultiply(f1,f2,d) Product of f1 and f2 \ffpraise(f1,f2,d) Raise f1 to power f2 \ffpround(f1,d) Round f1 to d places \ffpsine(f1,d) Sine of f1 \ffpsqrt(f1,d) Square root of f1 \ffpsubtract(f1,f2,d) f2 - f1 \ffptangent(f1,d) Tangent of f1 Integer number functions: \fabsolute(n) Absolute value of integer n. \frandom(n) Returns a random integer between 0 and n-1. \fradix(s,n1,n2) If the string s is an integer in radix n1, the result is the same number expressed in radix n2, where n1 and n2 may be any number from 2 through 36, expressed as decimal numbers, or variables (etc) that evaluate to decimal numbers. For the source and result, the digits of any radix, r, are the first r characters in the sequence 0-9,a-z (case doesn't matter for the letters). The string s may have a sign, + or -; if it starts with a minus (-) sign, the result also has a minus sign. The \fradix() function does not work with floating-point numbers. It does not reveal the internal storage format of a number; for example, \fradix(-1,10,16) is -1, not something like FFFFFFFFFF. If all three arguments are not given, or if n1 or n2 are not numbers between 2 and 36 inclusive, or s is not a number in radix n1, an error occurs and the empty string is returned. \fradix() also does not offer extended-precision arithmetic; number values are limited to those expressed as a long integer in the architecture of the underlying computer, usually 32 or 64 bits. If you give it an argument whose absolute value is larger than can be held in an unsigned long, the result is -1. The next four are shorthand functions for decimal/hexadecimal and decimal/octal number conversion: \fn2hex(n) Returns the hexadecimal (base 16) representation of the integer n. This is different from \fhexify(s), which treats its argument as a string rather than a number. The result is always left-padded with 0's to make its length even. Examples: \n2hex(0) = "00" \fhexify(0) = "30" \n2hex(255) = "ff" \fhexify(255) = "323535" \n2hex(256) = "0100" \fhexify(256) = "323536" \fhex2n(x) Converts hexadecimal number x to decimal equivalent decimal number. This is the inverse of \fn2hex(). Equivalent to \fradix(s,16,10). \fn2octal(n) Returns the octal (base 8) representation of the number n. Examples: \n2octal(0) = "0" \n2oct(255) = "377" \n2oct(256) = "400" Equivalent to \fradix(n,10,8). \foct2n(n) Returns the decimal representation of the given octal number, n. The inverse of \fn2octal(). Equivalent to \fradix(n,8,10). String functions: \s(name[n:m]) Equivalent to \fsubstring(\m(name),n,m) ([578]Section 7.24). \:(name[n:m]) Equivalent to \fsubstring(name,n,m) (where "name" is any \-quantity) ([579]Section 7.24). \fleft(s,n) The leftmost ncharacters of string s; equivalent to \fsubstring(s,1,n). \fstripx(string,char) Returns the part of the string up to the rightmost occurrence, if any, of the given character. The default character is period (.) Examples: \fstripx(foo/bar,/) = "foo" \fstripx(foo/bar/baz,/) = "foo/bar" \fstripx(autoexec.bat,.) = "autoexec" \fstripx(autoexec.bat) = "autoexec" \fstripx(fstripx(foo/bar/baz,/),/) = "foo" \flop(string,character) Returns the portion of the string starting after the first occurrence of the given character. The default character is period (.) Examples: \flop(autoexec.bat) = "bat" \flop(baz.foo/bar) = "foo/bar" \flop(baz.foo/bar,/) = "bar \fstripn(string,n) Returns the string with ncharacters removed from the end. Example: \fstripn(12345678,3) = "12345" (For more discussion of \fstripx(), \fstripn(), and \flop() see [580]Section 4.2.3). \fb64encode(s) Returns the Base-64 encoding of the string s. \fb64decode(s) Returns the decoding of the Base-64 string s. Fails if s is not a Base-64 string, or if its length is not a multiple of 4. Note that if any of the result bytes are null (0), the result string stops there. There is no way to represent strings that contain null bytes in C-Kermit (the same is true for \funhexify()). \fword(s1,n,s2,s3) Extracts word number nfrom string s1. By default, a "word" is any sequence of ASCII letters or digits; nis 1-based. If n is omitted, "1" is used. Examples: \fword(one two three) = "one" \fword(one two three,1) = "one" \fword(one two three,2) = "two" \fword(one two three,3) = "three" and: \fword(\v(dialresult),2) = "31200" is "31200" if \v(dialresult) is (e.g.) "CONNECT 31200/ARQ/V32/LAPM/V42BIS". If you include s2, this replaces the default break set. For example, suppose you have a string \%a whose value is: $150.00 $300.00 $39.95 and you want each dollar amount to be a word; use: \fword(\%a,\%n,{ }) This returns dollar amount number \%n, e.g. "$300.00" for \%n = 2. "{ }" denotes a space (you must enclose it in braces, otherwise it is squeezed out). Note that ASCII control characters are always included in the break set; you don't have to specify them (and you can't not specify them). The optional s3 argument lists characters (even control characters) that normally would be considered separators that you want included in words. So the dollars-and-cents example could also be handled this way: \fword(\%a,\%n,,$.) in other words, use the default separator list, but remove "$" and "." from it so they will be considered part of a word. Note that since 8-bit characters are not ASCII, they act as break characters unless you put them in the include list. Suppose, for example, you have a file in which each line is a Tab-separated list of words, numbers, or phrases that might contain punctuation, special characters like $ and @, 8-bit bit characters, etc (like something that might have been exported from a spreadsheet or database), and you want to split only on Tab; here is a way (\m(line) is a line read from the file): undef keep for \%i 1 255 1 { if == \%i 9 continue .keep := \m(keep)\fchar(\%i) } while true { fread /line \%c line if fail break .\%n := \fsplit(\m(line),&a,\9,\m(keep)) ... } This problem is addressed in [581]C-Kermit 9.0. \fsplit(s1,&a,s2,s3) This is like \fword(), except instead of extracting and returning a particular word from string s1, it counts the words and optionally assigns them to the array whose identifying letter, a-z, is given after the "&" in the second argument, with the first word going into element 1, the second into element 2, and so on. The rules regarding break and include lists (s2 and s3) are exactly the same as for \fword(). \fsplit() returns the number of words that were assigned, which is either the number of words in the string, or the dimension of the array, whichever is less. If the array is not declared, \fsplit() creates it and returns a number which is both the number of words in s1 and the dimension of the new array. Examples: declare \&w[20] ; (Optional.) ... read \%s ; \%s is "This is a sentence with seven words." ... echo "\fsplit(\%s)" ; This would print "7". echo "\fsplit(\%s,&w)" ; Ditto, and also assigns them to array \&w[]. echo "\&w[7]" ; This would print "words". If the line contained fields that were delimited by colon (:), you would use \fsplit(\%s,&w,:). If the fields were delimited by comma, then you would use \fsplit(\%s,&w,{,}); in this case the literal comma must be enclosed in braces to distinguish it from the comma that separates function arguments. To get a word count without loading an array, but still specify break and/or include lists, leave the array argument empty: echo "\fsplit(\%s,,:)" ; Use colon as the separator. WARNINGS: 1. If you use the same array repeatedly, \fsplit() leaves any trailing members undisturbed. For example: dcl \&w[10] \fsplit(1 2 3 4 5,&w) ; Sets \&w[1] thru \&w[5]. \fsplit(a b c,&w) ; Sets \&w[1]-[3] leaving [4]-[5] as they were. 2. If you allow \fsplit to create the array (by not declaring it first), it is dimensioned to the number of elements it was created with: \fsplit(1 2 3,&x) ; Creates an array \&x[] with 3 elements. \fsplit(a b c d e,&x) ; This overflows the array. Thus if you want to use \fsplit() repeatedly on the same array, either dimension it in advance to the maximum expected size (and then some -- more efficient), or else destroy it after each use (to allow for unexpectedly large arguments). Example using a dynamic array: fopen /read \%c some-file if fail ... set function error on ; See [582]Section 7.12 while true { dcl \&w[] ; Destroy \&[w] each time thru the loop fread /line \%c \%a if fail break asg \%x \fsplit(\%a,&w) if fail ... ; (do what you want with \&w[] here...) } fclose \%c \frindex(s1,s2,n) The "n" argument to \frindex() now works consistently (in mirror image) with the corresponding \findex() argument. In each case, the (n-1)-most characters of s2 are ignored in the search; for findex, this means the starting position of the search is n (the default n is 1, and 0 is treated like 1). For \frindex() it means the default starting point is: length(s2) - length(s1) - n (with the same defaults for n). \fsearch(pattern,string[,position]) Exactly like \findex(), except with a pattern (see [583]Section 7.9) rather than a literal string. \frsearch(pattern,string[,position]) Exactly like \frindex(), except with a pattern rather than a literal string. File Functions: \ffiles(), \fnextfile() It is no longer necessary to copy the file list to an array before use, as shown on p.398 of [584]Using C-Kermit 2nd Edition. \ffiles() and friends now make their own safe copies of the file list. Thus constructions like the following are now possible: for \%i 1 \ffiles(*.txt) 1 { send \fnextfile() } The same is true for the new function \frfiles(), \fdirectories(), and \frdirectories(), described in [585]Section 4.11.3. But note that each reference to \fnextfile() still gets you the next file. So "if newer \fnextfile() foo.txt send \fnextfile()" compares one file's age with that of foo.txt, and then sends an entirely different file. If you're going to refer to the same file more than once, assign it to a variable: asg \%f \fnextfile() if newer \%f foo.txt send \%f (note: assign, not define). Also note that \ffiles(), \frfiles(), \fdirectories(), and \frdirectories() all now accept on optional 2nd argument: the name of an array to load with the resulting file or directory list, explained in [586]Section 4.11.3. So you can also load an array with the filelist when you need to refer to the same file more than once: for \%i 1 \ffiles(*,&a) 1 { if newer \&a[\%i] foo.txt send \&a[\%i] } \fpermissions(file) Returns the platform-specific permissions string for the file, such as "-rw-rw-r--" in UNIX or "(RWE,RWE,RE,E)" in VMS. \fdirname(f) Given a file specification f, this function returns the complete pathname of directory the file is in. Array Functions: \fdimension(&a) Returns the dimension declared for the array whose identifying letter, a-z, or special character "_" or "@", is given after the "&" in the argument. If the array is not declared, 0 is returned. Note that when used with the macro argument vector array, \&_[] (see [587]Section 7.5), the value of this function is one less than \v(argc), and when used with the C-Kermit command-line argument vector array, \&@[], it is equal to the \v(args) variable. Examples: echo \fdimension(&a) ; Not declared. 0 declare \&a[12] ; Now it's declared. echo \fdim(&a) 12 \farraylook(pattern,arrayname) Looks in the given array for the pattern and returns the index of the first element that matches, if any, or -1 if none match. The arrayname can include a range specifier to restrict to search to a segment of the array, e.g. \farraylook(*xyz*,&a[32:63]). For greater detail see [588]Section 7.10.7. \ftablelook(keyword,arrayname[,delimiter]) Looks in the given "table", which must be sorted, for the given keyword. Returns the index of the table element that uniquely matches the given keyword, or -1 if none match, or -2 if more than 1 match. For greater detail see [589]Section 7.10.7. Other new functions: \fip2hex(s) Converts a dotted decimal IP address to an 8-digit hexadecimal number. \fip2hex(128.59.39.2) = 803b2702. \fhex2ip(x) Converts an 8-digit hexadecimal IP address to dotted decimal form, e.g. \fhex2ip(803b2702) = 128.59.39.2. The inverse of \fip2hex(). \fcommand() \frawcommand() These run an external command and return its output; see [590]Section 4.2.8.4. \fdialconvert(s) s is a phone number in either literal or portable format (not a dialing directory entry name). The function returns the dial string that would actually be used when dialing from the current location (after processing country code, area code, and other SET DIAL values). \ferrstring(n) Returns the system error message associated with the (numeric) error code n. UNIX and VMS only. Use in conjunction with \v(errno) or \v(pexitstat). See [591]Section 4.2.5 for a usage example. Note: This function doesn't work in Windows because there is not a consistent error-code-to-message mapping; error code "x" means something completely different depending on whether it comes from the C runtime library, Winsock, a Windows-32 API, TAPI, etc, \fpattern(s) Used in INPUT, REINPUT, and MINPUT commands to denote search strings that are to be treated as patterns rather than literally. Also see [592]Section 7.8 on built-in help for functions. 7.4. New IF Conditions IF AVAILABLE feature command Executes the command if the given feature is available. Presently used only to determine if specific authentication and encryption options are available. Type "if available ?" to see which features may be tested. IF FLOAT f1 command Executes command if f1 is a legal floating point number (which includes integers). Use this to preverify arguments for the \ffp...() floating-point arithmetic functions, e.g. "if float \%1 echo \ffpint(\%1)". IF == n1 n2 command Synonym for "if =" (numeric equality). Note that as of C-Kermit 7.0, this and all other numeric comparison operators also work for floating-point numbers. IF != n1 n2 command Executes the command if n1 and n2 are both numbers or variables containing numbers and the value of n1 is not equal to the value of n2. This is equivalent to "if not = n1 n2". IF <= n1 n2 command Executes the command if n1 and n2 are both numbers or variables containing numbers and the value of n1 is less than or equal to the value of n2. This is equivalent to "if not > n1 n2". IF >= n1 n2 command Executes the command if n1 and n2 are both numbers or variables containing numbers and the value of n1 is greater than or equal to the value of n2. Equivalent to "if not < n1 n2". IF COMMAND word command Executes the command if word is a built-in C-Kermit command. Example: if not command copy define { copy run copy \%1 \%2 }". This defines a COPY macro that runs an external COPY command if COPY is not already a built-in command. IF LOCAL command Executes the command if Kermit is in local mode, i.e. if it has a SET LINE, SET PORT, or SET HOST (TELNET, RLOGIN, etc) device or connection open. Does not execute the command if in remote mode. IF MATCH string pattern command Executes the command if the string matches the pattern. For a description of the syntax for the pattern, see [593]Section 4.9.1. If you want to test if the string contains pattern, use IF \fsearch(pattern,string). IF OPEN { DEBUG-LOG, SESSION-LOG, TRANSACTION-LOG, ... } command Executes the command if the given file is open, fails if it is not open. Type IF OPEN ? for a complete list of files that can be checked (all the files that can be opened with the OPEN or LOG commands). IF QUIET command Executes the command if SET QUIET is ON, and does not execute it if SET QUIET is OFF. Example: IF NOT QUIET ECHO { This is a message.}. IF READABLE name Succeeds if name is the name of an existing file or directory that is readable. IF WRITEABLE name Succeeds if name is the name of an existing file or directory that is writable, e.g.: if not writeable \v(lockdir) echo Please read installation instructions! IF FLAG command This tests a user-settable condition, which can mean anything you like. SET FLAG ON causes subsequent IF FLAG commands to succeed; SET FLAG OFF causes them to fail. One way to use it would be for debugging your scripts; precede any debugging statements with IF FLAG. Then SET FLAG on to debug your script, SET FLAG OFF to run it without debugging. Another common use is for causing an inner loop to cause an outer loop to exit. IF C-KERMIT command C-Kermit, but not Kermit 95 or MS-DOS Kermit, executes the command. IF K-95 command Kermit 95, but not C-Kermit or MS-DOS Kermit, executes the command. IF MS-KERMIT command MS-DOS Kermit, but not C-Kermit or Kermit 95, executes the command. 7.5. Using More than Ten Macro Arguments The \v(argc) variable now gives the actual number of arguments, even if the number is greater than 9: C-Kermit> define xx echo \v(argc) C-Kermit> xx a b c d e f g h i j k l m n o p q r s t u v w x y z 27 Remember that \v(argc) includes the name of the macro itself, so it is always at least 1, and is always 1 greater than the actual number of arguments. As in versions 6.0 and earlier, if more than 9 arguments are given, only the first nine are assigned to the variables \%1..\%9. The \&_[] array, discussed on page 353 of [594]Using C-Kermit, 2nd ed, now holds all the arguments, up to some implementation-dependent limit (64 or greater), rather than only the first 9. To illustrate: the following macro tells the number of arguments it was called with and then prints them: define show_all_args { local \%i echo \&_[0] - Number of arguments: \feval(\v(argc)-1) for \%i 1 \v(argc)-1 1 { echo \flpad(\%i,3). "\&_[\%i]" } } Within a macro \&_[0], like \%0, contains the name of the macro. At top level, the \&_[] array is filled as follows: * If the first argument on the C-Kermit command line was a filename, or C-Kermit was invoked from a "Kerbang" script ([595]Section 7.19), element 0 contains the filename, and elements 1 through \v(argc)-1 hold the remaining command-line arguments. * Otherwise the program name goes in element 0, and elements 1 through \v(argc)-1 hold any arguments that were included after "--" or "=" The new \%* variable, when used within a macro, is replaced by the text that followed the macro name in the macro invocation. If no arguments were given, \%* is replaced by the empty string. Examples: C-Kermit> define xx echo [\%*] C-Kermit> define \%a oofa C-Kermit> xx [] C-Kermit> xx \%a [oofa] C-Kermit> xx a [a] C-Kermit> xx a b [a b] C-Kermit> xx a b c [a b c] C-Kermit> xx a b c d e f g h i j k l m n o p q r s t u v w x y z [a b c d e f g h i j k l m n o p q r s t u v w x y z] Note that \%* can not be used at top level, since Kermit does not have access to the raw command line (only to its elements separately, after they have been processed by the shell and the C library). C-Kermit 7.0 also adds a SHIFT command: SHIFT [ number ] Shifts the macro arguments (except argument 0) the given number of places to the left and adjusts \v(argc) accordingly. The default number is 1. To illustrate, suppose macro XXX is invoked as follows: xxx arg1 arg2 arg3 Then inside XXX, \%1 is "arg1", \%2 is "arg2", and \%3 is "arg3". After a SHIFT command is given inside XXX, then \%1 is "arg2", \%2 is "arg3", and \%3 is empty. \%0 (the name of the macro) remains unchanged. If more than 9 arguments were given, then arguments are shifted into the \%1..9 variables from the argument vector array. At top level, the SHIFT command operates on the \&_[] array and \%1..9 variables; the \&@[] array is not affected. See [596]Section 7.16 for details. The \%* variable is not affected by the SHIFT command. 7.6. Clarification of Function Call Syntax Spaces are normally stripped from the front and back of each function argument; to prevent this enclose the argument in braces: \fsplit(\%a,&a,{ }) However, function calls that contain spaces can make trouble when the function is to be used in a "word" field, since space separates words. For example: for \%i 1 \fsplit(\%a,&a,{ }) 1 { echo \%i. "\&a[\%i]" } In most cases, the trouble can be averted by enclosing the function reference in braces: for \%i 1 {\fsplit(\%a,&a,{ })} 1 { echo \%i. "\&a[\%i]" } or by replacing spaces with \32 (the ASCII code for space): for \%i 1 \fsplit(\%a,&a,\32) 1 { echo \%i. "\&a[\%i]" } Braces are also used in function calls to indicate grouping. For example: \fsubstring(abcd,2,2) = "bc" But suppose "abcd" needed to contain a comma: \fsubstring(ab,cd,2,2) This would cause an error, since "cd" appears to be the second argument, when really you want the first "2" to be the second argument. Braces to the rescue: \fsubstring({ab,cd},2,2) = "b," Similarly, leading and trailing spaces are stripped from each argument, so: \fsubstring( abcd ,2,2) = "bc" but braces preserve them: \fsubstring({ abcd },2,2) = "ab" Given these special uses for braces, there is no way to pass literal braces to the function itself. For example: \fsubstring(ab{cd,2,2) causes an error. So if you need a function to include braces, define a variable containing the string that has braces. Example: define \%a ab{cd \fsubstring(\%a,2,2) = "b{" If the string is to start with a leading brace and end with a closing brace, then double braces must appear around the string (which itself is enclosed in braces): define \%a {{{foo}}} \fsubstring(\%a) = "{foo}" This also works for any other kind of string: define \%a {{ab{cd}} echo \fsubstring(\%a) = "ab{cd" 7.7. Autodownload during INPUT Command Execution As of 6.1 / 1.1.12, C-Kermit can be told to look for incoming Kermit (or Zmodem) packets during execution of an INPUT command. By default (for consistency with earlier releases), this is not done. You can enable this feature with: SET INPUT AUTODOWNLOAD ON (and disable it again with OFF.) One possible use for this feature is as a server mode with a time limit: INPUT 3600 secret-string-to-end-the-INPUT-command In this example, any GET, SEND, or REMOTE commands received within one hour (3600 seconds) of when the INPUT command was issued will be executed. Here's another example, in which we want to stay open until 11:30pm, or until interrupted by seven consecutive Ctrl-C (\3) characters: INPUT 23:30:00 \3\3\3\3\3\3\3 The INPUT AUTODOWNLOAD setting is displayed by SHOW SCRIPTS or SHOW INPUT. 7.8. Built-in Help for Functions. Beginning in C-Kermit 7.0, you may obtain a description of the calling conventions and return values of any built-in function, such as \fsubstring(), with the new HELP FUNCTION command; give the function's name without the leading "\f", e.g. "help func substring". You can use ?, completion, and abbreviation in the normal manner. 7.9. Variable Assignments 7.9.1. Assignment Operators Programmers accustomed to languages such as C or Fortran might find Kermit's method of assigning values to variables unnatural or awkward. Beginning in C-Kermit 7.0, you can use the following alternative notation: .name = value is equivalent to DEFINE name value .name := value is equivalent to ASSIGN name value .name ::= value is equivalent to ASSIGN name \feval(value) When the command begins with a period (.), this indicates an assignment. The name can be a macro name, a \%{digit,letter} variable, or an array element. There can be space(s) between "." and the name. Examples: .\%a = This is a string ; Same as "define \%a This is a string" echo \%a This is a string .xxx = \%a ; Same as "define xxx \%a" echo \m(xxx) \%a .xxx := \%a ; Same as "assign xxx \%a" echo \m(xxx) This is a string declare \&a[2] ; Use with arrays... define \%i 2 .\&a[1] = first .\&a[\%i] = second The following sequence illustrates the differences among three levels of evaluation: .\%x = 2 ; Define a variable to have a numeric value .\%y = (3 + \%x) ; Define another variable as an arithmetic expression .xxx = 4 * \%y ; "=" simply copies the right-hand side. echo \m(xxx) 4 * \%y .xxx := 4 * \%y ; ":=" evaluates the variables first, then copies. echo \m(xxx) 4 * (3 + 2) .xxx ::= 4 * \%y ; "::=" evaluates the expression, then copies. echo \m(xxx) 20 You can also use this syntax to clear (undefine) a variable: .\%a = oofa ; Define the variable echo "\%a" "oofa" .\%a ; Clear the variable echo "\%a" "" Extra credit: Can you guess what happens below when the file "abc" does not exist? fopen /read \%c abc if fail ... 7.9.2. New Assignment Commands Recall the DEFINE and ASSIGN commands, and their hidden counterparts, _DEFINE and _ASSIGN. The former take the variable name literally, the latter evaluate the variable-name field to form the variable name dynamically. Examples: DEFINE \%x foo ; Sets the value of the variable \%x to "foo". DEFINE \%a \%x ; Sets the value of the variable \%a to "\%x". _DEFINE x_\%a \%x ; Sets the value of the variable x_foo to "\%x". ASSIGN \%a \%x ; Sets the value of the variable \%a to the "foo". _ASSIGN x_\%a \%x ; Sets the value of the variable x_foo to "foo". This concept has been carried over to the remaining variable-assignment commands: EVALUATE, INCREMENT, and DECREMENT: EVALUATE variablename expression Evaluates the arithmetic expression and assigns its value to the variable whose name is given. Example: "eval \%a 1+1" assigns "2" to \%a. _EVALUATE metaname expression Evaluates the arithmetic expression and assigns its value to the variable whose name is computed from the given metaname. Example: "eval foo<\%a>::\%1 \%2 * (\%3 + \%4)" assigns the value of "\%2 * (\%3 + \%4)" to the variable whose name is computed from "foo<\%a>::\%1". INCREMENT variablename [ expression ] Evaluates the arithmetic expression and adds its value to the value of the variable whose name is given. Example: "increment \%a". _INCREMENT metaname [ expression ] Evaluates the arithmetic expression and adds its value to the value of the variable whose name is computed from the given metaname. Example: "_increment Words::\%1.count[\%2]". DECREMENT variablename [ expression ] Evaluates the arithmetic expression and subtracts its value from the value of the variable whose name is given. _DECREMENT metaname [ expression ] Evaluates the arithmetic expression and subtracts its value from the value of the variable whose name is computed from the given metaname. WARNING: The syntax of the EVALUATE command has changed since C-Kermit 6.0 and K95 1.1.17. Previously, it did not include a variable name, only an expression. To restore the old behavior, use SET EVALUATE OLD. To return to the new behavior after restoring the old behavior, use SET EVALUATE NEW. NOTE: There are no analogs to the "_" commands for the operators described in [597]Section 7.9.1; those operators can not be used to assign values to variables whose names must be computed. 7.10. Arrays C-Kermit 7.0 adds lots of new array-related features, and groups them together under the NEW ARRAY command: ARRAY { CLEAR, COPY, DECLARE, DESTROY, RESIZE, SHOW, SORT } In each of the ARRAY commands, wherever an array name is expected, "short forms" may be used. For example, all of the following are acceptable: array show \&a[] (or SHOW ARRAY...) array show &a[] array show a[] array show &a array show a In addition, ranges are accepted in the ARRAY COPY, ARRAY CLEAR, ARRAY SET, ARRAY SHOW, and ARRAY SORT commands: array clear \&a[16] ; Clears 16 thru end array clear &a[16] ; Ditto array clear a[16] ; Ditto array clear \&a[16:32] ; Clears 16 thru 32 array clear &a[16:32] ; Ditto array clear a[16:32] ; Ditto When using array names as function arguments, you must omit the "\" and you must include the "&". You may optionally include empty brackets. Examples: \fsplit(\%a,a) ; Bad \fsplit(\%a,\&a) ; Bad \fsplit(\%a,&a[3]) ; Bad \fsplit(\%a,&a) ; Good \fsplit(\%a,&a[]) ; Good 7.10.1. Array Initializers Beginning in C-Kermit 7.0, you may initialize an array -- in whole or in part -- in its declaration: [ ARRAY ] DECLARE array-name[size] [ = ] [ value1 [ value2 [...] ] ] For compatibility with versions 5A and 6.0, the ARRAY keyword is optional. DECLARE can also be spelled DCL. Initializers are (a) optional, (b) start with element 1, (c) must be enclosed in braces if they contain spaces, and (d) are evaluated according to normal rules by the DECLARE command prior to assignment. Thus the assignments made here are the same as those made by the ASSIGN command. This allows you to initialize array elements from the values of other variables. If you actually want to initialize an array element to variable's name, as opposed to its value, use double backslashes (as in "\\&a", "\\v(time)", etc). The size (dimension) of the array is optional. If the size is omitted, as in "\&a[]", then the array sizes itself to the number of initializers; if there are no initializers the array is not declared or, if it was declared previously, it is destroyed. If a size is given, any extra elements in the initialization list are discarded and ignored. NOTE: Unlike in C, the list of initializers is NOT enclosed in braces. Instead, braces are used to group multiple words together. So: ARRAY DECLARE \&a[] = { one two three } would create an array with two elements (0 and 1), with element 1 having the value " one two three ". Examples: ARRAY DECLARE \&a[16] Declares the array \&a with 17 elements (0 through 16), in which all elements are initially empty. If the array \&a[] existed before, the earlier copy is destroyed. ARRAY DECLARE &a[16] ARRAY DECLARE a[16] ARRAY DCL \&a[16] ARRAY DCL &a[16] ARRAY DCL a[16] DECLARE \&a[16] DECLARE &a[16] DECLARE a[16] DCL \&a[16] DCL &a[16] DCL a[16] All of the above are the same as the first example. ARRAY DECLARE \&a[16] = alpha beta {gamma delta} Declares the array \&a with 17 elements (0 through 16), initializing \&a[1] to "alpha", \&a[2] to "beta", and \&a[3] to "gamma delta". The remaining elements are empty. ARRAY DECLARE \&a[] = alpha beta {gamma delta} Same as the previous example, but the array is automatically dimensioned to 3. ARRAY DECLARE \&a[3] = alpha beta {gamma delta} epsilon zeta Too many initializers; only the first three are kept. ARRAY DECLARE \&a[0] ARRAY DECLARE \&a[] ARRAY DECLARE &a[] ARRAY DECLARE &a ARRAY DECLARE a DECLARE \&[0] DECLARE a DCL a All of these are equivalent. Each destroys \&a[] if it exists. Declaring an array with a dimension of 0 is the same as ARRAY DESTROY arrayname. ARRAY DECLARE \&a[] = \%1 \%2 \%3 Declares the array \&a with 3 elements (0 through 3), initializing \&a[1] to the value of \%1, \&a[2] to the value of \%2, and \&a[3] to the value of \%3. In this case, any reference to one of these array elements is replaced by the value of the corresponding \%n variable at the time the declaration was executed (immediate evaluation; the array element's value does not change if the initializer variable's value changes). ARRAY DECLARE \&a[] = \\%1 \\%2 \\%3 Declares the array \&a with 3 elements (0 through 3), initializing \&a[1] to the string "\%1", \&a[2] to "\%2", and \&a[3] to "\%3". In this case any reference to one of these array elements is replaced by the CURRENT value of the corresponding \%n variable (deferred evaluation -- the array element's value follows the value of the initializer variable). The equal sign (=) preceding the initializer list is optional, but is recommended for clarity. If you need to initialize element 1 to a literal equal sign, use two of them, separated by a space, as in this example: ARRAY DECLARE \&a[] = = + - * / Remember, element 0 is not initialized by the DECLARE command. To initialize element 0, use a regular DEFINE or ASSIGN command: ARRAY DECLARE \&a[] one two three four five six seven eight nine DEFINE \&a[0] zero Finally, remember that every command level has its own local array, \&_[], containing all the macro arguments (\%0, \%1, ...) for that level. See [598]Section 7.5 for details. 7.10.2. Turning a String into an Array of Words The \fsplit(s1,&a,s2,s3) function assigns the words of string s1 to successive elements of the array (beginning with element 1) whose identifying letter, a-z, is given after the "&" in the second argument, using break and include characters given in s2 and s3. See [599]Section 7.3 for details. 7.10.3. Arrays of Filenames See [600]Section 4.11.3 for news about how \ffiles() and related functions can assign a list of filenames to an array. To recapitulate briefly here: \ffiles(*,&a) assigns all files that match the first argument to the array denoted by the second argument. If the array has not been declared, it is declared automatically, with exactly the number of elements needed to hold the file list; if it was previously declared, it is destroyed and reused. The filenames are assigned starting at array element 1. Element 0 holds the number of files in the list. The DIRECTORY command ([601]Section 4.5.1) can also create filename arrays if you give it the /ARRAY: switch; this allows selection criteria beyond whether the filename matches the given pattern. All functions and commands that create filename arrays store the number of filenames, n, as element 0 of the array, and the filenames as elements 1 through n. 7.10.4. Automatic Arrays In a command file or macro, you can now have local (automatic) arrays. Just give the name followed by empty subscript brackets (no spaces inside the brackets please) in a LOCAL command, and then declare the array: LOCAL \%a \&a[] oofa ARRAY DECLARE \&a[32] = value1 value2 value3 ... This declares the scalar variable \%a, the array \&a[], and the macro name "oofa" to be local, and then declares the new local copy of \&a[] with 32 elements, perhaps assigning some initial values. When C-Kermit exits from the command file or macro containing these command, the previous \&a[] array is restored (and if there was no \&a[] at any higher level, this will still be true). The process can be repeated to any level. Thus it is now safe to write scripts or macros containing arrays without danger of interfering with global arrays of the same name. Just as scalars are inherited by lower command levels, so are arrays. So, for example, if \&a[] is declared at top level, all lower levels will see it unless they include a "local \&a[]" statement, in which case all levels at and beneath the level where the LOCAL statement was executed will see the local copy. This too can be repeated to any level. On the other hand, if you DECLARE an array at a lower command level without also making it LOCAL, this replaces the copy that was declared at the lowest command level above this one. 7.10.5. Sorting Arrays Although arrays can be sorted using FOR loops as shown on page 383 of Using C-Kermit, 2nd Ed., this involves quite a bit of repetitive interpretation by the command parser, and so can be slow for large arrays. For this reason, C-Kermit 7.0 adds a built-in SORT command: ARRAY SORT [ switches ] array [ array2 ] Sorts the given array in place. Sorting is strictly lexical (string based). The array name can be given fully, e.g. "\&a[]", or the "\" and/or "&" and/or brackets can be omitted, e.g. "array sort \&a[]", "sort &a", "sort a". Also, a range can be indicated in the brackets as noted in [602]Section 7.10, to restrict the sort to a range of elements (equivalent to the /RANGE switch, described just below), e.g. "array sort &a[20:30]". A second array may be specified. If it is, and if it is at least as big as the first array, it is sorted according to the first array. For a sample application, see [603]Section 7.10.10. See [604]Section 1.5 for an explanation of switches. The optional switches are: /CASE:{ON,OFF} /CASE:ON means that alphabetic case is significant in comparisons; uppercase letters are sorted before lowercase ones. /CASE:OFF means case is ignored, e.g. "A" is the same as "a". If this switch is not given, sorting is according the current SET CASE setting. /KEY:n Comparison begins at position n(1-based) in each string. If no key is given, the entire strings are compared. Only one key can be given. If an array element is shorter than the key value, n, that element is considered empty for comparison purposes, and therefore lexically less than any element at least ncharacters long. /NUMERIC If this switch is included, it means sorting should be numeric, rather than lexical. The sort key is the string starting at the key position, skipping any leading blanks or tabs, and then as much of the string from that point on that fits the definition of "numeric", terminating at the first character that does not qualify. A numeric string has an optional sign (+ or -) followed by one or more digits, and (if your version of Kermit was built with floating-point support; see [605]Section 7.23 ) zero or one decimal point (period). If both /CASE and /NUMERIC are given, /NUMERIC takes precedence. /RANGE:n[:m] Sort elements nthrough m of the array. By default, the entire array from element 1 to its dimensioned size is sorted, which might produce surprising results if the array is not full; see example in [606]Section 7.10.7. If ":m" is omitted from the range, the dimensioned size is used. Thus, to sort an entire array, \&a[], including its 0th element, use "sort /range:0 &a". You can also sort any desired section of an array, e.g. "sort /range:10:20 &a" or "sort /range:\%i:\%j-1 &b". As noted above, you can also specify a range in the array-name brackets. If you specify a range in the array-name brackets AND with a /RANGE switch, the ones in the brackets take precedence. /REVERSE Sort in reverse order. If this switch is not given, the array is sorted in ascending order. Remember that numeric switch arguments can be numbers, arithmetic expressions, or variables whose values are numbers or expressions, as illustrated in the /RANGE examples above. A typical sorting application might be to list students' test scores in descending order. Suppose you had the following records: olaf 65 olga 98 ivan 83 xena 100 (and so on) stored in array \&s[] (e.g. by reading them from a file as illustrated in [607]section 7.10.7). In these records, the student's name is in columns 1-9 and the score in 10-12. So to rearrange the list in descending order of score: sort /key:10 /reverse &s Then to list your top five students: for \%i 1 5 1 { echo \&s[\%i] } Or more simply (see next section): show array a[1:5] To illustrate the difference between a lexical and a numeric sort, suppose you have the following records (the lines that are numbered, starting at column 1) in array \&a[]: Column 1 2 12345678901234567890 1. Ivan 10.0 2. Olaf 9.95 3. Olga 101.5 ARRAY SORT /KEY:10 &a[] would order them 3,1,2, but ARRAY SORT /KEY:10 /NUMERIC &a[] would order them 2,1,3. 7.10.6. Displaying Arrays The SHOW ARRAY command (or ARRAY SHOW) now accepts an optional array-name argument: SHOW ARRAY \&a[] (you can leave off the \, the \&, and/or the []'s if you like; "show array a" is equivalent to "show array \&a[]"). When an array is specified, its dimension is shown and all defined (non-empty) elements are listed. Example: assign \%n \ffiles(*,&a) ; Fill an array with filenames ([608]Section 4.11.3) show array \&a[] ; Show the array we just read array show \&a[] ; Same as previous array sort \&a[] ; Sort the array array show \&a[] ; Show it after sorting array show \&a ; Show it again array show &a ; Show it again array show a ; Show it again (The final four commands demonstrate the alternative forms that are accepted for the array name.) If you SHOW ARRAY without giving an array name, all defined arrays are listed by name and dimension, but their contents are not shown. You can also show a piece of an array by including a subscript or range within the array brackets: array show \&a[5] ; Shows \&a[5] array show &a[3:8] ; Shows \&a[3] through \&a[8] array show a[:\%n-1] ; Shows \&a[0] through \&a[\%n-1] 7.10.7. Other Array Operations ARRAY DESTROY arrayname Destroys and undeclares the named array. Subscripts or ranges are not accepted in this command. ARRAY COPY array1 array2 Copies the first array to the second array. If the target array has not been declared, it is created automatically with the same size as the first. If it has been declared, it will be used as declared; if the source array is larger, only as much of it as will fit is copied to the target array. Syntax for array1 and array2 is as in ARRAY SHOW (SHOW ARRAY). Example: .\%n := \ffiles(*,&a) ; Create and load array A with a file list. array copy &a &b ; Copy array A to array B. The ARRAY COPY command also lets you copy pieces of arrays by including range specifiers, as in these examples: ARRAY COPY \&a[4:27] \&b This copies \&a[] elements 4-27 to \&b[] elements 1-23, creating \&b[] if necessary or, if \&b[] is already declared, stopping early if its size is less than 23. ARRAY COPY \&a[4:27] \&b[12] This copies \&a[] elements 4-27 to \&b[] elements 12-35, creating \&b[] if necessary or, if \&b[] is already declared, stopping early if its size is less than 35. ARRAY COPY \&a[4:27] \&b[12:14] This copies \&a[] elements 4-6 to \&b[] elements 12-14, creating \&b[] if necessary or, if \&b[] is already declared, stopping early if its size is less than 14. ARRAY COPY \&a[17:] \&b This copies all the elements of \&a[] starting with 17 until the last to \&b[], creating \&b[] if necessary or, if \&b[] is already declared, stopping early if \&b[] is not big enough. Suppose, for example, you have a script whose arguments are string1, string2, and a list of files, whose job is to change all occurrences of string1 to string2 in each of the files. But if the list of files is omitted, then "*" (all files in the current directory) is assumed: if < \v(argc) 3 exit 1 "Usage: \%0 string1 string2 [ files ]" if not def \%3 { .n := \ffiles(*,&a) } else { array copy &_[3:] &a .n := \fdim(&a) } for i 1 n 1 { ! cat \&a[i] | sed -e "s|\%1|\%2|g" > /tmp/_x rename /tmp/_x \&a[i] } ARRAY COPY \&a[17] \&b Same as previous example. ARRAY CLEAR arrayname Sets all the elements of the array to the empty value. You may also include a range specifier to clear only a selected portion of the array; for example "array clear \&a[37:214]". If the range is out of bounds, only the part of the array that is in bounds is cleared. ARRAY SET arrayname [ value ] Sets all the elements of the array to the given value. If no value is given, the array is cleared. You may also include a range specifier to set only a selected portion of the array; for example "array set \&a[1:9] -1". If the range is out of bounds, only the part of the array that is in bounds is set. ARRAY RESIZE arrayname size Resizes the given array. If the size is greater than the array's current dimension, new empty elements are added to the end. If the size is less than the current dimension, the extra elements are discarded. Note: If you have stored the array size in element 0, ARRAY RESIZE does not change this value. Alternative notation: ARRAY RESIZE arrayname[size]. For a practical example, see [609]Section 7.10.11. \farraylook(pattern,arrayname) This function returns the index of the first element of the given array that matches the given pattern (for details about pattern syntax, see [610]section 4.9). The array name can include a range specification to restrict the search to a given segment of the array. If no elements match the pattern, -1 is returned. \ftablelook(keyword,arrayname[,delimiter]) Looks in the given "table", which must be sorted, for the given keyword. The keyword need not be spelled out in full. Pattern-matching characters should not be included as part of the keyword. The function returns the index of the table element that uniquely matches the given keyword, or -1 if none match, or -2 if more than 1 match. A "table" is an array that is sorted in lexical order; each of its elements may contain multiple fields, delimited by the given delimiter character or, if no delimiter is specified, a colon (:). The \farraylook() function does exactly what you tell it. If you give it a pattern that does not include wildcard characters (such as *, ?, etc), it requires an exact match. For example: \farraylook(oofa,&a) searches for the first element of \&a[] whose value is "oofa". But: \farraylook(oofa*,&a) finds the first element whose value starts with "oofa", and; \farraylook(*oofa,&a) finds the first element whose value ends with "oofa", and; \farraylook(*oofa*,&a) finds the first element whose value contains "oofa". Here's a simple demonstration of looking up patterns in arrays: local \&a[] \%x \%n declare \&a[] = zero one two three four five six seven eight nine ten while true { .\%x = 1 .\%n = 0 ask \%a { Pattern? } if not def \%a exit 0 Done. while <= \%x \fdim(&a) { .\%x := \farraylook(\%a,&a[\%x]) if ( < \%x 0 ) break echo \flpad(\%x,3). \&a[\%x] increment \%x increment \%n } if ( < \%n 1 ) echo Pattern not found - "\%a" } The array need not be sorted. When a pattern is given, a search is performed; if there is a match, the matching element's index and the element itself are printed, and the search begins again at the next element. Thus each matching element is printed. If none match, the "Pattern not found" message is printed. The process repeats for as many patterns as the user wants to type, and terminates when the user types an empty pattern. Now let's build a little command parser, consisting of a keyword table, and a loop to look up the user's commands in it with \ftablelook(). In this case the array elements have "fields" separated by colon (:) -- a keyword and a value. Keyword tables must be sorted if \tablelook() is to work right, so after declaring and initializing the table array, we sort it. local \&k[] \%a \%i \%n array declare \&k[] = drive:9 do:8 discuss:7 live:6 spend:5 help:4 quit:0 array sort &k ; Make sure array is sorted echo Type "help" for help. ; Print greeting & instructions while true { ; Loop to get commands undefine \%a while not defined \%a { ; Get a command ask \%a { Command? } } .\%n := \ftablelook(\%a,&k) ; Look up the command switch \%n { ; Handle errors :-1, echo Not found - "\%a" ; Doesn't match continue :-2, echo Ambiguous - "\%a" ; Matches too many continue } switch \fword(\&k[\%n],2) { ; Dispatch according to value :9, echo Driving..., break :8, echo Doing..., break :7, echo Discussing..., break :6, echo Living..., break :5, echo Spending..., break :4, echo { Commands (may be abbreviated):} for \%i 1 \fdim(&k) 1 { echo { \%i. \fword(\&k[\%i],1) } } break :0, exit 0 Bye! :default, stop 1 Internal error } } In this example, keywords are "drive", "do", "discuss", etc, and their values are unique numbers (values need not be numbers, and there need not be only one value -- there can be 0, 1, 2, or more of them). The user types a command, which can be the whole word (like "help") or any abbreviation (like "hel", "he", or just "h"). If this does not match any keywords, \ftablelook() returns -1; if it matches more than one (as would "d"), it returns -2. Otherwise the array index is returned, 1 or higher. Given the array index \%n, we can get the table values as follows: \fword(\&k[\%n],1) is the keyword (first field) \fword(\&k[\%n],2) is the value (second field, in this case a number) In our example, we use the value (number) as the SWITCH variable. As noted, \fablelook() expects the array elements to contain multiple fields separated by colon (:) (or other character that you specify, e.g. \ftablelook(\%a,&a,^)) and when matching the keyword, ignores the first delimiter and everything after it. 7.10.8. Hints for Using Arrays C programmers are accustomed to out-of-bounds array references causing core dumps or worse. In C-Kermit: * A reference to an an out-of-bounds array element returns the empty string. * An attempt to set the value of an array element that is out of bounds or that has not been declared simply fails. C programmers expect an array of size n to have elements 0 through n-1. Fortran programmers expect the same array to have elements 1 through n. C-Kermit accommodates both styles; when you declare an array of size n, it has n+1 elements, 0 through n, and you can use the array in your accustomed manner, 0-based or 1-based. However, note that C-Kermit has certain biases towards 1-based arrays: * Assignment of file lists starts with element 1 ([611]Section 7.10.3). * Assignment by \fsplit() starts with element 1 ([612]Section 7.3). * Array initialization skips the 0th element. To initialize a 0-based array, use something like this: declare \&a[3] = one two three .\&a[0] = zero * The ARRAY SORT command skips the 0th element unless you include /RANGE:0 * The SHIFT command ignores element 0 of the \&_[] array. The distinction between an array's dimensioned size and the number of elements in the array is important when sorting. To illustrate: declare \&a[100] ; Declare array &a with 100 elements fopen /read \%c oofa.txt ; Open a file if fail... for \%i 1 \fdim(&a) 1 { ; Read the file into the array fread \%c \&a[\%i] if fail break } fclose \%c if > \%i \fdim(&a) end 1 File has too many lines for array. .\%n ::= \%i - 1 echo File has \%n line(s). Let's say the file had 95 lines. This leaves elements 96-100 of the array empty. Now suppose you sort the array and write out the result: sort &a ; Sort the whole array fopen /write \%o oofa.txt.sorted ; Open an output file if fail ... for \%i 1 \%n 1 { ; Write out 95 records fwrite /line \%o \&a[\%i] if fail end 1 Write error } close write You might be surprised at the contents of "oofa.txt.sorted" -- five empty elements, 96-100, floated to the top of the array in the sort, and since your write loop only had 95 iterations, the final 5 lines of the sorted file are lost. Therefore, when dealing with partially filled arrays -- especially when sorting them -- remember to specify the number of elements. A handy way of recording an array's "true" size is to put it in the 0th element. That way, it "travels with the array". To illustrate (continuing the previous example at the "close read" statement): close read if > \%i \fdim(&a) end 1 File has too many lines for array. .\&a[0] ::= \%i - 1 ; Assign number of lines to \&a[0]. echo File has \&a[0] line(s). sort /range:1:\&a[0] &a open write oofa.txt.sorted if fail ... for \%i 1 \&a[0] 1 { writeln file \&a[\%j] if fail end 1 Write error } close write Note the SORT switch, /RANGE:1:\&a[0]. This keeps the sort 1-based, and uses element 0 of the array as its size indicator. Finally, note that even though some commands or functions might put a size in array element 0, no built-in functions or commands depend on a size actually being there. Thus you are perfectly free to replace the size with something else and treat the array as 0-based. 7.10.9. Do-It-Yourself Arrays Kermit's \&x[] arrays are nice because of the accompanying built-in functionality -- ARRAY commands, built-in functions that load and search arrays, automatic evaluation of arithmetic expressions within the subscript brackets, and so on. Yet they also have certain limitations: 1. Except when created by dynamic loading (e.g. by \ffiles()) they must be declared and dimensioned in advance. 2. Indices must be numeric, positive, and in range. 3. There can be only one dimension. Matrices or other higher-dimensioned arrays are not available. But none of this is to say you can't invent any kind of data structure you like. In [613]Section 7.9.2 you can see some examples. Here's another (courtesy of Dat Thuc Nguyen), in which a pair of matrices is created and then added: no dimensioning necessary. .row = 4 .col = 9 ; MACRO TO PRINT A MATRIX define PMATRIX { echo Matrix \%1: for \%r 1 \m(row) 1 { for \%c 1 \m(col) 1 { xecho \flpad(\m(\%1[\%r][\%c]),4) } echo } echo } ; CREATE MATRICES A AND B for \%r 1 \m(row) 1 { for \%c 1 \m(col) 1 { _eval A[\%r][\%c] \%r + \%c _eval B[\%r][\%c] \%r * \%c } } ; CREATE MATRIX C = SUM OF MATRIX A AND MATRIX B for \%r 1 \m(row) 1 { for \%c 1 \m(col) 1 { _eval C[\%r][\%c] \m(A[\%r][\%c]) + \m(B[\%r][\%c]) } } pmatrix A ; Print Matrix A pmatrix B ; Print Matrix B pmatrix C ; Print Matrix C In the example, we use matrix-like notation to create macros with names like "A[1][1]", "B[3][7]", and so on. 7.10.10. Associative Arrays An associative array is a special kind of Do-It-Yourself array. It differs from a regular array in that its indices need not be numbers -- they can be anything at all -- words, filenames, names of months, any character string at all, and that it doesn't have to be (and in fact can't be) declared. An associative array element is simply a macro whose name ends with an index enclosed in angle brackets, for example: file More formally: basename An associative array is a collection of all associative array elements that have the same basename. Any number of associative arrays, each with any number of elements, can exist at the same time. An associative array element can be assigned a value, such as "1", just like any other macro: define file 1 ; Give "file" the value "1". or: assign file \%a ; Give it the value of the variable \%a. However, since an associative array element is a macro, it may not have an empty (null) value, since assigning an empty value to a macro undefines the macro. You can refer to the value of an associative array element using the familiar notation for macro values: echo \m(file) ; Echo the value of "file". Associative arrays are most useful, however, when the value of the index is a variable. In that case, you must use the "hidden" forms of the DEFINE or ASSIGN commands that evaluate the macro name before making the assignment (see [614]Using C-Kermit, page 457). Example: define \%f oofa.txt _define file<\%f> 1 echo file<\%f> = \m(file<\%f>) prints: file = 1 and then: _increment file<\%f> echo file<\%f> = \m(file<\%f>) prints: file = 2 What are associative arrays good for? The classic example is "word counts": finding the number of times each word is used in a text without knowing in advance what the words are. Without associative arrays, your program would have to build a table of some kind, and every time a word was encountered, look it up in the table to find its position and counter, or add it to the table if it wasn't found -- a time-consuming and laborious process. Associative arrays, however, let you use the word itself as the table index and therefore sidestep all the table building and lookups. Let's work through a practical example. Suppose you have a file-transfer log in which each line is composed of a number of blank-separated fields, and the 9th field is a filename (which happens to be the format of certain FTP server logs, as well as of C-Kermit's new FTP-format transaction log, described in [615]Section 4.17.2), for example: Wed Jul 14 09:35:31 1999 22 xx.mit.edu 13412 /pub/ftp/mm/intro.txt .... and you want to find out how many times each file was transferred. The following code builds an associative array, file<>, containing the counts for each file: local name line max \%c \%n ; Declare local variables fopen /read \%c /var/log/ftpd.log ; Open the log file ([616]Section 1.22) if fail exit 1 Can't open log ; Check while true { ; Loop for each record fread /line \%c line ; Read a line if fail break ; Check for end of file .name := \fword(\m(line),9,{ }) ; Get 9th field = filename (Sec 7.3) _increment file<\m(name)> ; Increment its counter (Sec 7.9.2) } fclose \%c ; Close file when done. Note that _INCREMENT (and INCREMENT, and [_]DECREMENT) treat an empty (i.e. nonexistent) variable as having a value of 0, and therefore creates the variable with a value of 1. At this point, if you told Kermit to "show macro file<", it would list the associative array. But since you don't necessarily know the names of the files in the array, or even how many elements are in the array, how can you use it in a script program? The idea of creating macro names that include character-string indices enclosed in angle brackets is perfectly arbitrary and doesn't depend on any Kermit features that weren't already there -- we could just as easily have used some other notation, such as "file[index]", "file:index", or "file.index", and the code above would have worked just as well (with the corresponding syntax adjustments). But to be able to use an associative array in a program after the array is built, we need a method of accessing all its elements without knowing in advance what they are. That's where the chosen notation comes in. First of all, any macro name that ends with "" (where "xxx" is any string) is case sensitive, unlike all other macro names, which are case independent. To illustrate, "file" and "file" are two distinct macros, whereas "OOFA", "Oofa", and "oofa", when used as macro names, are all the same. Second, the new \faaconvert() function converts an associative array (that is, all macros with names of the form "base" that have the same "base" part) into a pair of regular arrays and returns the number of elements: \faaconvert(name,&a[,&b]) "name" is the name of the associative array, without the angle brackets or index ("file" in our example). The second argument is the name of a regular array in which to store the indices of the associative array (filenames in our example); if an array of this name already exists, it is destroyed unless the array is LOCAL. The third argument is the name of another regular array in which to store the values (the counts in our example), with the same rules about array name collisions. If you care only about the indices and not the values, you can omit the third argument to \faaconvert(). In any case, the associative array is converted, not copied: its elements are moved to the specified regular arrays, so after conversion the original associative array is gone. As with other array-loading functions, \faaconvert() sets element 0 of each array to the number of elements in the array. To continue our example: .max := 0 ; Maximum count .\%n := \faaconvert(file,&a,&b) ; Convert for \%i 1 \%n 1 { ; Loop through values echo \flpad(\%i,3). \&a[\%i]: \&b[\%i] ; Echo this pair if ( > \&b[\%i] \m(max) ) { ; Check for new maximum .name := \&a[\%i] .max := \&b[\%i] } } echo Most popular file: \m(name), accesses: \m(max) This lists the files and counts and then announces which file has the highest count. Now suppose you want to sort the array pair created from an associative array. In our example, \&a[] contains filenames, and \&b[] contains the associated counts. Here we take advantage of the ARRAY SORT command's ability to sort a second array according to the first one: array sort /reverse /numeric &b &a ; Descending sort by count Now to see the top five files and their counts: echo The top 5 files are: for \%i 1 5 1 { ; Loop through top 5 values echo \flpad(\%i,3). \&a[\%i]: \&b[\%i] ; Echo this pair } 7.10.11. Transferring Array Contents to Other Computers The SEND /ARRAY:arrayname command ([617]Section 4.7.1) allows you to send the contents of any array, or any contiguous segment of it, in either text or binary mode to another computer, using Kermit protocol. When used in conjunction with C-Kermit's other features (the array features described in this section; the file i/o package from [618]Section 1.22; its decision-making, pattern-matching, and string manipulation capabilities, and so on) the possibilities are endless: extracts of large files, remote database queries, ..., all without recourse to system-dependent mechanisms such UNIX pipes and filters, thus ensuring cross-platform portability of scripts that use these features. When sending an array in text mode, Kermit appends a line terminator to each array element, even empty ones, and it also converts the character set from your current FILE character-set to your current TRANSFER character-set, if any. No conversions are made or line terminations added in binary mode. For example, the following array: dcl \&a[] = One Two Three Four Five Six is sent as six lines, one word per line, in text mode, and as the bare unterminated string "OneTwoThreeFourFiveSix" in binary mode. You should always include a /TEXT or /BINARY switch in any SEND /ARRAY command to force the desired transfer mode, otherwise you're likely to be surprised by the effects described in [619]Section 4.3. Here are some examples: send /text /array:\&a[] Sends the entire contents of the array \&a[] in text mode. Since an as-name is not included, the receiver is told the filename is _array_a_. send /text /array:&a[] send /text /array:a[] send /text /array:&a send /text /array:a These are all equivalent to the previous example. send /text /array:&a /as-name:foo.bar As above, but the array is sent under the name foo.bar. send /text /array:&a[100:199] /as:foo.bar As above, but only the elements from 100 through 199 are sent. In text-mode transfers, character sets are translated according to your current settings, just as for text files. In binary mode, of course, there is no character-set translation or other conversion of any kind. But remember that array elements can not contain the NUL (ASCII 0) character, since they are implemented as NUL-terminated strings. Here's an example that shows how to send all the lines (up to 1000 of them) from a file animals.txt that contain the words "cat", "dog", or "hog" (see [620]Section 4.9 about pattern matching): declare \&a[1000] fopen /read \%c animals.txt if fail exit 1 .\%i = 0 while true { fread \%c line if fail break if match {\m(line)} {*{cat,[dh]og}*} { increment \%i if ( > \%i \fdim(&a) ) break .\&a[\%i] := \m(line) } } fclose \%c send /array:a[1:\%i] /text Note that we are careful to send only the part of the array that was filled, not the entire array, because there are likely to be lots of unused elements at the end, and these would be sent as blank lines otherwise. This example raises an interesting question: what if we want to send ALL the matching lines, even if there are more than 1000 of them, but we don't know the number in advance? Clearly the problem is limited by Kermit's (and the computer's) memory. If there are a thousand trillion matching lines, they most likely will not fit in memory, and in this case the only solution is to write them first to a temporary file on mass storage and then send the temporary file and delete it afterwards. However, when the selection is likely to fit in memory, the once-familiar technique of initial allocation with extents can be used: if match {\m(line)} {*{cat,[dh]og}*} { increment \%i if ( > \%i \fdim(&a) ) { array resize a \fdim(&a)+100 if fail stop 1 MEMORY FULL echo NEW DIMENSION: \fdim(&a) } .\&a[\%i] := \m(line) } This grows the array in chunks of 100 as needed. 7.11. OUTPUT Command Improvements LINEOUT [ text ] This command is exactly like OUTPUT, except it supplies a carriage return at the end of the text. "lineout exit" is exactly the same as "output exit\13". SET OUTPUT SPECIAL-ESCAPES { ON, OFF } This command lets you tell C-Kermit whether to process \N, \L, and \B specially in an OUTPUT command, as distinct from other \ sequences (such as \%a, \13, \v(time), etc). Normally the special escapes are handled. Use SET OUTPUT SPECIAL-ESCAPES OFF to disable them. Disabling special escapes is necessary in situations when you need to transmit lines of data and you have no control over what is in the lines. For example, a file oofa.txt that contains: This is a file It has \%a variables in it And it has \B in it. And it has \L in it. And it has \N in it. And this is the last line. can be sent like this: local line set output special-escapes off fopen /read \%c oofa.txt if fail stop 1 Can't open oofa.txt while success { fread \%c line if fail break ; Add filtering or processing commands here... output \m(line)\13 } 7.12. Function and Variable Diagnostics In C-Kermit 6.0 and earlier, the only diagnostic returned by a failing function call was an empty value, which (a) could not be distinguished from an empty value returned by a successful function call; (b) did not give any indication of the cause of failure; and (c) did not cause the enclosing statement to fail. C-Kermit 7.0 corrects these deficiencies. SET FUNCTION DIAGNOSTICS { ON, OFF } when ON, allows built-in functions to return diagnostic messages when improperly referenced, instead of an empty string. FUNCTION DIAGNOSTICS are ON by default. When OFF, improperly referenced functions continue to return an empty string. This command also affects built-in variables; in this case, an error message is returned only if the variable does not exist. When FUNCTION DIAGNOSTICS are ON, the error message is also printed. For variables, the only message is: where "name" is the name of the nonexistent variable. For functions, the diagnostic message is: where "message" is replaced by a message, and "name" is replaced by the function name, e.g. . Messages include: ARG_BAD_ARRAY An argument contains a malformed array reference. ARG_BAD_DATE An argument contains a malformed date and/or time. ARG_BAD_PHONENUM An argument contains a malformed telephone number. ARG_BAD_VARIABLE An argument contains a malformed \%x variable. ARG_INCOMPLETE An argument is incomplete (e.g. a broken Base64 string). ARG_EVAL_FAILURE An argument could not be evaluated (internal error). ARG_NOT_ARRAY An argument references an array that is not declared. ARG_NOT_NUMERIC An argument that must be integer contains non-digits. ARG_NOT_FLOAT An argument has bad floating-point number format. ARG_NOT_VARIABLE An argument that must be a variable is not a variable. ARG_OUT_OF_RANGE An argument's numeric value is too big or too small, or an argument contains illegal characters (e.g. a hex or Base-64 string). ARG_TOO_LONG An argument's value is too long. ARRAY_FAILURE Failure to create an array. DIVIDE_BY_ZERO Execution of the function would cause division by zero. FLOATING_POINT_OP Execution error in a floating-point operation. FILE_NOT_FOUND Filename argument names a file that can't be found. FILE_NOT_READABLE Filename argument is not a regular file. FILE_NOT_ACCESSIBLE Filename argument names a file that is read-protected. FILE_ERROR Other error with filename argument. FILE_NOT_OPEN A file function was given a channel that is not open. FILE_ERROR_-n A file function got error -n ([621]Section 1.22). LOOKUP_FAILURE Error looking up function (shouldn't happen). MALLOC_FAILURE Failure to allocate needed memory (shouldn't happen). NAME_AMBIGUOUS The function is not uniquely identified. MISSING_ARG A required argument is missing. NO_SUCH_FUNCTION An argument references a function that is not defined. NO_SUCH_MACRO An argument references a macro that is not defined. RESULT_TOO_LONG The result of a function is too long. UNKNOWN_FUNCTION Internal error locating function (shouldn't happen). Examples: assign \%m \fmod() ? echo "\fcontents(\%m)" "" echo \fmod(3,x) ? echo \fmod(3,4-2*2) ? Notice the use of \fcontents() in echoing the value of a variable that contains a returned error message. That's because the error message includes the name of the variable or function that failed, so you must use \fcontents() to prevent it from being evaluated again -- otherwise the same error will occur. The handling of function and variable errors is controlled by: SET FUNCTION ERROR { ON, OFF } Tells whether invalid function calls or variable references should cause command errors. FUNCTION ERROR is ON by default. When ON, and an error is diagnosed in a built-in function or variable, the command that includes the function call or variable reference fails. The failing command can be handled in the normal way with IF FAILURE / IF SUCCESS, SET TAKE ERROR, or SET MACRO ERROR. When FUNCTION DIAGNOSTICS is OFF, there is no error message. SHOW SCRIPTS displays the current FUNCTION DIAGNOSTICS and ERROR settings. 7.13. Return Value of Macros In C-Kermit 5A and 6.0, there are two ways to return one level from a macro: RETURN value and END number text. When RETURN is used, the value, which can be a number or a text string, is assigned to \v(return). When END was used, however, \v(return) was not set. SUCCESS/FAILURE was set according to whether the number was zero, and the text was printed, but the actual value of the number was lost. In C-Kermit 7.0, the END number is available in the \v(return) variable. 7.14. The ASSERT, FAIL, and SUCCEED Commands. The ASSERT command is just like the IF command, but without a command to execute. It simply succeeds or fails, and this can be tested by a subsequent IF SUCCESS or IF FAILURE command. Example: ASSERT = 1 1 IF SUCCESS echo 1 = 1. The FAIL command does nothing, but always fails. The SUCCEED command does nothing, but always succeeds. These commands are handy in debugging scripts when you want to induce a failure (or success) that normally would not occur, e.g. for testing blocks of code that normally are not executed. 7.15. Using Alarms Alarms may be set in two ways: SET ALARM number Sets an alarm for the given number of seconds "from now", i.e. in the future, relative to when the SET ALARM command was given. Examples: set alarm 60 ; 60 seconds from now set alarm +60 ; The same as "60" set alarm -60 ; Not legal - you can't set an alarm in the past. set alarm 60*60 ; 60 minutes from now. set alarm \%a+10 ; You can use variables, etc. SET ALARM hh:mm:ss Sets an alarm for the specified time. If the given time is earlier than the current time, the alarm is set for the given time in the next day. You may give the time in various formats: set alarm 15:00:00 ; 3:00:00pm set alarm 3:00:00pm ; 3:00:00pm set alarm 3:00pm ; 3:00:00pm set alarm 3pm ; 3:00:00pm SHOW ALARM Displays the current alarm, if any, in standard date-time format (see [622]Section 1.6): yyyymmdd hh:mm:ss. IF ALARM command Executes the command if an alarm has been set and the alarm time has passed. IF ALARM { command-list } [ ELSE { command-list } ] Executes the command-list if an alarm has been set and the alarm time has passed. Otherwise, if an ELSE part is given, its command-list is executed. CLEAR ALARM Clears the alarm. Only one alarm may be set at a time. Example: Suppose you have a script that is always running, and that transfers files periodically, and that keeps a transaction log. Suppose you want to start a new transaction log each day: log transactions \v(date).log set alarm 00:00:00 ; Set an alarm for midnight while true { ; Main script loop xif alarm { ; If the alarm time is past... close transactions ; Close current log log transactions \v(date).log ; Start new one pause 1 ; To make sure 00:00:00 is past set alarm 00:00:00 ; Set a new alarm } ; put the rest of the script here... } Note that IF ALARM -- no matter whether it succeeds or fails -- does NOT clear an expired alarm. Thus, once an alarm has expired, every IF ALARM will succeed until the alarm is cleared (with the CLEAR ALARM command) or reset with a new SET ALARM command. 7.16. Passing Arguments to Command Files Beginning in version 7.0, C-Kermit accepts arguments on the TAKE command line, for example: C-Kermit> take oofa.ksc one two {this is three} four This automatically sets the variables \%1 through \%9 to the arguments, and \%0 to the name of the file, in this case: \%0 = /usr/olga/oofa.ksc \%1 = one \%2 = two \%3 = this is three \%4 = four and \%5..\%9 are undefined (empty). Arguments past the ninth are available in the \&_[] argument-vector array ( [623]Section 7.5). The variables are those at the current macro level. Thus, if the TAKE command is executed from within a macro, the macro's arguments are replaced by those given on the TAKE command line (but only if at least one argument is given). The command shown above is exactly equivalent to: assign \%0 /usr/olga/oofa.ksc assign \%1 one assign \%2 two assign \%3 this is three assign \%4 four assign \%5 assign \%6 assign \%7 assign \%8 assign \%9 take oofa.ksc Remember, the variables \%0..\%9 are on the macro call stack, and command files are independent of the macro stack. Thus, if a command file TAKEs another command file and passes arguments to it, the variables are changed from that point on for both files, and so forth for all levels of nested command files without intervening macro invocations. It would have been possible to change C-Kermit to use the overall command stack, rather than the macro stack, for arguments -- this would have made TAKE work exactly like DO, which is "nicer", but it would also have broken countless existing scripts. However, the new SHIFT command ([624]Section 7.5) makes it possible to create an alternative TAKE command that does indeed save and restore the argument variables at its own level around execution of a command file: define mtake { local \%f assign \%f \fcontents(\%1) shift take \%f } C-Kermit 7.0 also supports a new, easier way to pass arguments to scripts from the system command line: kermit filename arg1 arg2 arg3 ... in which arg1, arg2, arg3 (etc) are arguments for the script (whose filename is given), and are assigned to \%1, \%2, ... \%9. The filename is assigned to \%0. This applies equally to "Kerbang" scripts in UNIX ([625]Section 7.19). For example, suppose you have a file called "showargs" containing the following lines: #!/usr/local/bin/kermit + echo Hello from \%0 show args exit (except not indented, since the "#!" line must be on the left margin). If you give this file execute permission: chmod +x showargs then you can run it exactly as you would run a UNIX shell script, e.g.: $ showargs one two three Hello from /usr/olga/showargs Top-level arguments (\v(argc) = 4): \&_[0] = /usr/olga/showargs \&_[1] = one \&_[2] = two \&_[3] = three Furthermore, the \&_[] array now contains the filename, if one was given as the first command line argument, or it is a "Kerbang" script, in element 0. Otherwise element 0 is program name, and elements 1 through \v(argc)-1 contain the command-line arguments, if any, that appear after "--" or "=", if any. This array is saved and restored around macro calls; recall that inside macros it contains the macro argument vector (allowing you to access arguments programmatically, and to have more than 9 of them). At top level, notice the difference between the \&@[] and \&_[] arrays. The former includes C-Kermit options; the latter omits them. 7.17. Dialogs with Timed Responses The ASK, ASKQ, GETOK, and GETC commands (let's call them the "ASK-class commands") let you write scripts that carry on dialogs with the user, asking them for text, a Yes/No answer, or a character, respectively. Prior to C-Kermit 7.0, these questions would always wait forever for an answer. In C-Kermit 7.0, you may specify a time limit for them with the new command: SET ASK-TIMER number Sets a time-limit on ASK-CLASS commands to the given number of seconds. If the number is 0 or less, there is no time limit and these commands wait forever for a response. Any timer that is established by this command remains in effect for all future ASK-class commands until another SET ASK-TIMER command is given (e.g. with a value of 0 to disable ASK timeouts). IF ASKTIMEOUT command An ASK-class command that times out returns a failure status. You can test explicitly for a timeout with: 7.18. Increased Flexibility of SWITCH Case Labels Prior to C-Kermit 7.0 / K95 1.1.19, the case labels in SWITCH statements were string constants. Now case labels can be variables, function calls, or any mixture of these with each other and/or with regular characters. Furthermore, after the case label is evaluated, it is treated not as a string constant, but as a pattern against which the SWITCH variable is matched ([626]Section 4.9.1). This introduces a possible incompatibility with previous releases, since the following characters in case labels are no longer taken literally: \ * ? [ { Any scripts that previously included any of these characters in case labels must now quote them with backslash (\). 7.19. "Kerbang" Scripts In UNIX only, Kermit scripts can be stored in files and run "directly", without starting Kermit first (as noted on page 467 of the manual), just as a shell script can be "run" as if it were a program. This section amplifies on that idea a bit, and presents some new aspects of version 7.0 that make it easier to write and run Kermit scripts directly. NOTE: On non-UNIX platforms, such as VMS or Windows, Kerbang scripts can be run as "kermit + scriptfilename arg1 arg2 arg3 ...". Windows 95/98/NT file associations do not allow for the passing of parameters. In VMS, however, you can achieve the Kerbang effect by defining a symbol, as in this example: $ autotelnet :== "$SYS$TOOLS:KERMIT.EXE + AUTOTELNET.KSC" and then running the script like any other command: $ autotelnet xyzcorp.com myuserid See [627]Section 9.3 for an explanation of the "+" symbol. UNIX shell scripts can specify which shell should run them by including a "shebang" line at the top, e.g.: #!/bin/sh (but not indented; the shebang line must be on the left margin). The term "shebang" is a contraction of "shell" and "bang". "Bang" is a slang word for the exclamation mark ("!"); "shebang" itself is an American slang word used in in the phrase "the whole shebang". We can run Kermit scripts directly too, by including a "shebang" line that names Kermit as the "shell"; thus we call these "Kerbang" scripts. This mechanism has been considerably simplified in C-Kermit 7.0 to facilitate C-Kermit's use a scripting tool just like any of the UNIX shells or scripting languages. The rules are the same as for shell scripts: 1. The first line of the Kermit script must begin with "#!" immediately followed by the full pathname of the program that will execute the script (in this case, C-Kermit rather than a UNIX shell), followed by any Kermit command-line options. To suppress execution of the C-Kermit initialization file and to make command line arguments available to the script, the final option should be "+": #!/usr/local/bin/kermit + Some users have reported that in some circumstances a space might be necessary after the plus sign; this depends on your shell -- it has nothing to do with Kermit. In most cases, no space is needed. 2. The file must have execute permission (granted via "chmod +x filename"). When C-Kermit is invoked from a Kerbang script (or from the system prompt with a "+" command-line argument, which amounts to the same thing), the following special rules apply: 1. The C-Kermit initialization file is NOT executed automatically. If you want it to be executed, include a TAKE command for it in the script, e.g. "take \v(home).kermrc". (In previous releases, the initialization file was always executed, with no way to prevent it except for the user to include Kermit-specific command line options which had nothing to do with the script). Many scripts have no need for the standard Kermit initialization file, which is quite lengthy and not only delays startup of the script, but also spews forth numerous messages that are most likely unrelated to the script. 2. If the initialization file is not executed, neither is your customization file, since the initialization file is the command file from which the customization file is TAKEn. Again, you can include a TAKE command for the initialization file if desired, or for the customization file by itself, or for any other file. 3. C-Kermit does not process command-line arguments at all. Instead, it passes all words on the command line after the "+" to the script as \%0 (the script name), \%1..\%9 (the first nine arguments), as well as in the argument vector array \&_[]. The variable \v(argc) is set to the total number of "words" (as passed by the shell to Kermit) including the script name. Quoting and grouping rules are those of the shell. 4. At any point where the script terminates, it must include an EXIT command if you want it to exit back to the shell; otherwise C-Kermit enters interactive prompting mode when the script terminates. The EXIT command can include a numeric status to be returned to the shell (0, 1, etc), plus an optional message. Here is a simple Kerbang script that prints its arguments: #/usr/local/bin/kermit + echo Hello from \%0 for \%i 0 \v(argc)-1 1 { echo \%i. "\&_[\%i]" } exit 0 Save this file as (say) "showargs", then give it execute permission and run it (the \&_[] array is the same as \%0..\%9, but allows you to refer to argument variables programmatically; see [628]Section 7.5). (Yes, you could substitute SHOW ARGUMENTS for the loop.) $ chmod +x showargs $ ./showargs one "this is two" three The script displays its arguments: Hello from /usr/olga/showargs 0. "/usr/olga/showargs" 1. "one" 2. "this is two" 3. "three" $ Notice that no banners or greetings are printed and that startup is instantaneous, just like a shell script. Also notice that grouping of arguments is determined by *shell* quoting rules, not Kermit ones, since the command line is parsed by the shell before Kermit ever sees it. Of course you can put any commands at all into a Kerbang script. It can read and write files, make connections, transfer files, anything that Kermit can do -- because it *is* Kermit. And of course, Kerbang scripts can also be executed from the Kermit prompt (or from another script) with a TAKE command; the Kerbang line is ignored since it starts with "#", which is a comment introducer to Kermit just as it is to the UNIX shell. In VMS and other non-UNIX platforms, the Kerbang line has no effect and can be omitted. It might be desirable for a script to know whether it has been invoked directly from the shell (as a Kerbang script) or by a TAKE command given to the Kermit prompt or in a Kermit command file or macro. This can be done as in this example: #!/usr/local/bin/kermit + assign \%m \fbasename(\%0) define usage { exit 1 {usage: \%m phonenumber message} } define apage { (definition of APAGE...) } ; (See [629]book pp.454-456) xif equal "\%0" "\v(cmdfil)" { if not def \%1 usage if not def \%2 usage apage {\%1} {\%2} exit \v(status) } In a Kerbang script, \%0 and \v(cmdfile) are the same; both of them are the name of the script. When a script is invoked by a Kermit TAKE command, \%0 is the name of the Kermit program, but \v(cmdfile) is the name of the script. In the example above, a macro called APAGE is defined. If the script was invoked directly, the APAGE macro is also executed. Otherwise, it is available for subsequent and perhaps repeated use later in the Kermit session. An especially handy use for Kerbang scripts is to have the initialization file itself be one. Since the standard initialization file is rather long and time-consuming to execute, it is often overkill if you want to start Kermit just to transfer a file. Of course there are command-line switches to suppress initialization-file execution, etc, but another approach is to "run" the initialization file when you want its features (notably the services directory), and run C-Kermit directly when you don't. A setup like this requires that (a) the C-Kermit initialization file is configured as a Kerbang script (has #!/path.../kermit as first line), has execute permission, and is in your PATH; and (b) that you don't have a .kermrc file in your login directory. 7.20. IF and XIF Statement Syntax The IF command has been improved in two significant ways in C-Kermit 7.0, described in the following subsections. All changes are backwards compatible. 7.20.1. The IF/XIF Distinction The distinction between IF and XIF is no longer important as of C-Kermit 7.0. You should be able to use IF in all cases (and of course, also XIF for backwards compatibility). In the past, IF was used for single-command THEN parts, followed optionally by a separate ELSE command: IF condition command1 ; THEN part ELSE command2 ; ELSE part whereas XIF was required if either part had multiple commands: XIF condition { command, command, ... } ELSE { command, command, ... } The syntactic differences were primarily that IF / ELSE was two commands on two separate lines, whereas XIF was one command on one line, and that XIF allowed (and in fact required) braces around its command lists, whereas IF did not allow them. Furthermore, the chaining or nesting of parts and conditions was inconsistent. For example, the IF command could be used like this: IF condition command ELSE IF condition command ELSE IF condition command ELSE IF condition command ... but XIF could not. C-Kermit 7.0 accepts the old syntax and executes it the same as previous versions, but also accepts a new unified and more convenient syntax: IF condition command-list [ ELSE command-list ] or: IF condition command-list ELSE command-list in which the ELSE part is optional, and where command-list can be a single command (with or without braces around it) or a list of commands enclosed in braces. Examples: Example 1: IF condition { command1, command2 } ELSE { command3, command4 } Example 2 (same as Example 1): IF condition { command1 command2 } ELSE { command3 command4 } Example 3 (same as 1 and 2): IF condition { command1 command2 } ELSE { command3, command4 } Example 4 (same as 1-3): IF condition { command1 command2 } ELSE { command3 command4 } Example 5 (ELSE can be followed by another command): IF condition1 { command1 command2 } ELSE IF condition2 { command3 command4 } ELSE { command5 command6 } Example 5 suggests other possibilities: IF condition { command1 command2 } ELSE FOR variable initial final increment { command3 command4 } And this too is possible, except for some non-obvious quoting considerations: dcl \&a[6] = one two three four five six IF < \%n 3 { echo \\%n is too small: \%n } ELSE FOR \\%i 1 \\%n 1 { echo \\%i. \\&a[\\%i] } (The loop variable must be quoted in this context to prevent premature evaluation.) Many C programmers prefer to code IF-ELSE, WHILE, FOR, and SWITCH with the block-open bracket on its own line. This does not work in Kermit: IF condition ; THIS FORMAT DOES NOT NOT WORK { command1 command2 } ELSE { command3 command4 } Explanation: the Kermit command language is line oriented; each line is a command, each command is a line. The first line above, having no hint of continuation, is an incomplete command, yet syntactically correct -- an IF statement with an empty THEN part. Interestingly enough, since the next line begins with "{" it is a block that (in [630]C-Kermit 8.0 and later) is a block that is executed unconditionally. Thus the commands in the THEN part are executed regardless of whether the condition is true -- not what you wanted! The new block syntax used in the IF, WHILE, FOR, and SWITCH commands employs certain tricks to allow multiple lines to be treated as a single line: * Any line ending with "{" (ignoring whitespace and comments) marks the beginning of a block; * Any line beginning with "}" (ignoring whitespace) marks the end of a block; * Line breaks within a block separate commands; the comma is implied by the line end. Thus: IF condition { command1 command2 } ELSE { command3 command4 } is "assembled" into: IF condition { command1, command2 } ELSE { command3, command4 } Note the addition of commas to separate commands within blocks. As always, if you need continue a command onto additional lines, you can end the continued lines with the continuation character, "-". You can also do this if you want to put opening brackets on their own line: IF condition - { command1 command2 } ELSE - { command3 command4 } 7.20.2. Boolean Expressions (The IF/WHILE Condition) Prior to C-Kermit 7.0, the IF and WHILE commands accepted only a single Boolean ("true or false") assertion, e.g. "if > \%m 0 command" or "if exist filename command". There was no way to form Boolean expressions and, in particular, nothing that approached a Boolean OR function (AND could be simulated by concatenating IF statements: "if condition1 if condition2.."). C-Kermit 7.0 (and K95 1.1.19) allow grouping of Boolean assertions using parentheses and combining them using AND (or &&) and OR (or ||). Each of these operators -- including the parentheses -- is a field and must be set off by spaces. AND has higher precedence than OR, NOT has higher precedence than AND, but parentheses can be used to force any desired order of evaluation. The old syntax is still accepted. Here are some examples: define \%z 0 ; Define some variables define \%n 1 ; for use in the examples. if > \%n \%z echo \%n is greater. ; Original format - still accepted. if ( > \%n \%z ) echo \%n is greater. ; Parentheses may be used in 7.0. if ( > \%n \%z && not = \%z 0 ) ... ; Two assertions combined with AND. if ( > \%n \%z and not = \%z 0 ) ... ; Same as previous ("and" = "&&"). if ( > \%n \%z || not = \%z 0 ) ... ; Two assertions combined with OR. if ( > \%n \%z or not = \%z 0 ) ... ; Same as previous ("or" = "||"). if ( > \%n \%z || != \%z 0 ) ... ; Ditto ("!=" = "not ="). while ( 1 ) { ... } ; Just like C. Notice the spaces around all operators including the parentheses -- these are required. The following examples show how parentheses can be used to alter the precedence of the AND and OR operators: if ( false || false && false || true ) ,.. ; True if ( false || ( false && false ) || true ) ... ; Same as previous if ( ( false || false ) && ( false || true ) ) ... ; False Similarly for NOT: if ( not true && false ) ... ; False (NOT binds to TRUE only) if ( ( not true ) && false ) ... ; Same as previous if ( not ( true && false ) ) ... ; True (NOT binds to (TRUE && FALSE)) Notes: 1. The syntax of the Boolean expression itself has not changed; each expression begins with a keyword or token such as "EXIST", ">", or "=", etc; operators such as "<", "=", and ">" do not go between their operands but precede them as before; this might be called "reverse reverse Polish notation"; it allows deterministic on-the-fly parsing of these expressions at the C-Kermit> prompt as well as in scripts, and allows ?-help to be given for each item when IF or WHILE commands are typed at the prompt. 2. Parentheses are required when there is more than one Boolean assertion. 3. Parentheses are not required, but are allowed, when there is only one Boolean assertion. 4. Evaluation of Boolean assertions occurs left to right, but the resulting Boolean expression is evaluated afterwards according to the rules of precedence. All Boolean assertions are always evaluated; there is no "early stopping" property and therefore no question about when or if side effects will occur -- if any Boolean assertion has side effects, they will always occur. (Early stopping is, however, possible with the [631]S-Expression IF introduced in C-Kermit 8.0.) Constructions of arbitrary complexity are possible, within reason. Also see [632]Section 7.4 for new IF / WHILE conditions. 7.21. Screen Formatting and Cursor Control C-Kermit 7.0 adds a simple way to create formatted screens, the SCREEN command: SCREEN { CLEAR, CLEOL, MOVE-TO row [ column ] } Performs screen-formatting actions. Correct operation of these commands depends on proper terminal setup on both ends of the connection -- mainly that the host terminal type is set to agree with the kind of terminal or the emulation you are viewing C-Kermit through. The UNIX version uses terminfo or termcap (not curses); the VMS version uses SMG; K-95 uses its built in screen manager. SCREEN CLEAR Moves the cursor to home position and clears the entire screen. Synonyms: CLEAR COMMAND-SCREEN ALL (K-95 only), CLS, CLEAR SCREEN. SCREEN CLEOL Clears from the current cursor position to the end of the line. Synonym: CLEAR COMMAND-SCREEN EOL (K-95 only) SCREEN MOVE-TO row column Moves the cursor to the indicated row and column. The row and column numbers are 1-based, so on a 24x80 screen the home position is 1 1 and the lower right corner is 24 80. If a row or column number is given that too large for what Kermit or the operating system thinks is your screen size, the appropriate number is substituted. These escape sequences used by these commands depends on the platform. In UNIX, your TERM environment variable is used to query the terminfo/termcap database; if the query fails, ANSI/VT100 sequences are used. In VMS, the SMG library is used, which sends sequences based on your VMS terminal type. K95 does its own screen control. On other platforms (such as AOS/VS, VOS, etc), screen formatting is not supported, and the SCREEN command does nothing. The three SCREEN actions can be used in scripts to produce menus, formatted screens, dynamic displays, etc. Related variables include: \v(terminal) The type terminal C-Kermit thinks you have. \v(rows) The number of rows C-Kermit thinks your terminal has. \v(columns) The number of columns C-Kermit thinks your terminal has. And functions: \fscrncurx() The current X coordinate of the cursor (K-95 only). \fscrncury() The current Y coordinate of the cursor (K-95 only). \fscrnstr(x,y,n) The string of length nat position (x,y) (K-95 only). And commands: ECHO string Writes string + CRLF at the current cursor position. XECHO string Writes string at current cursor position; CRLF not supplied. GETC v prompt Issues prompt, reads one character into variable v, no echo. And special characters: Ctrl-L At the C-Kermit> command prompt, or in a C-Kermit command, works like Return or Enter, but also clears the screen Example 1: A macro that prints a message \%1 at cursor position (\%2,\%3): define MSG { if not def \%3 def \%3 0 ; Default column to 0 if > \v(argc) 2 screen move \%2 \%3 ; Move to given row/col (if any) screen cleol ; Clear to end of line if def \%1 xecho \fcontents(\%1) ; Print message (if any) } Example 2: A macro put the cursor on the bottom screen line, left margin: define BOT { screen move \v(rows) 0 } Example 3: A macro to center message \%1 on line \%2. define CENTER { if not def \%2 def \%2 1 .\%x ::= (\v(cols)-\flen(\%1))/2 msg {\%1} {\%2} {\%x} } Example 4: A simple menu (building on Examples 1-3): def \%c 0 ; Menu choice variable screen clear ; Clear the screen center {Welcome to This Menu} 2 ; Display the menu msg {Choices:} 4 msg { 1. File} 6 msg { 2. Edit} 7 msg { 3. Exit} 8 while ( != \%c 3 ) { ; Read and verify choice while true { ; Keep trying till we get a good one screen move 10 ; Move to line 10 screen cleol ; Clear this line getc \%c {Your choice: } ; Prompt and get and echo 1 character xecho \%c if ( not numeric \%c ) { msg {Not numeric - "\%c"} 12, continue } if ( >= \%c 1 && <= \%c 3 ) break msg {Out of range - "\%c"} 12 } switch \%c { ; Valid choice - execute it. :1, msg {Filing... } 12, break :2, msg {Editing...} 12, break :3, msg {Exiting...} 12, break } } echo Bye ; Exit chosen - say goodbye. bot ; Leave cursor at screen bottom. exit ; And exit. Similar scripts can work over the communication connection; substitute INPUT and OUTPUT for GETC and ECHO/XECHO. 7.22. Evaluating Arithmetic Expressions A new arithmetic operator was added to the list recognized by the EVALUATE command, the \feval() function, and which can also be used anywhere else arithmetic expressions are accepted (numeric command fields, array subscripts, etc): Prefix "!" This operator inverts the "truth value" of the number or arithmetic expression that follows. If the value of the operand is 0, the result is 1. If the value is nonzero, the result is 0. Examples: set eval old evaluate 0 0 evaluate !0 1 evaluate !3 0 evaluate !(-3) 0 .\%a = 1 .\%b = 0 evaluate !(\%a|\%b) 0 evaluate !(\%a&\%b) 1 evaluate !(!(\%a&\%b)) 0 Note the distinction between Prefix ! (invert truth value) and Suffix ! (factorial). Also the distinction between Prefix ! and Prefix ~ (which inverts all the bits in its operand). Also note that prefix operators (!, -, and ~) can not be adjacent unless you use parentheses to separate them, as shown in the final example above. 7.23. Floating-Point Arithmetic For a more convenient way of dealing with floating-point numbers than the one described here, see the [633]C-Kermit 8.0 update notes, the section on [634]S-Expressions. C-Kermit 7.0 adds limited support for floating-point numbers (numbers that have fractional parts, like 3.141592653). This support is provided through a small repertoire of functions and in Boolean expressions that compare numbers, but does not apply to number parsing in general, or to expression evaluation, array subscripts, the INCREMENT and DECREMENT commands, or in any context other than those listed in this section. A floating point number has an optional sign (+ or -), followed by a series of decimal digits containing either zero or one period (.) character, which is the decimal point. The use of comma or any other character besides period as a decimal point is not supported. Scientific notation is not supported either. Examples of legal floating-point numbers: 0 Integers can be used 1 Ditto 2. A decimal point without decimal digits 3.0 A decimal point with decimal digits 3.141592653 Ditto -4.0 A negative sign can be included +5.0 A positive sign can be included Examples of notations that are not accepted: 1,000,000 Separators can not be used 1.000.000 Ditto (or multiple decimal points) 6.022137E23 No scientific notation 6.62606868e-34 Ditto 12.5+6.25 No "bare" expressions You can use IF FLOAT test a string or variable to see if it's in acceptable floating-point format. Example: ask \%f { Type a number: } if not def \%f .\%f = 0.0 if not float \%f stop 1 Invalid floating-point number: "\%f" C-Kermit's floating-point support, like its support for whole numbers (integers), relies on the capabilities of the underlying computer. Your computer has only a limited amount of precision for numbers, depending on its architecture. Thus floating-point numbers that have too many digits will not be accurate; adding a very small number to a very large one might have no effect at all; and so on. For details, read a text on numerical analysis. Example: .\%a = 11111111111111111111 ; A long number .\%b = 22222222222222222222 ; Another one echo \ffpadd(\%a,\%b) ; Add them - the result should be all 3's 33333333333333330000.0 ; See the result In this example, the computer has 16 digits of precision; after that, the (low-order) digits are set to 0, since the computer doesn't know what they really are. In fact, the computer returns random digits, but Kermit sets all digits beyond the computer's precision to 0. C-Kermit's floating-point functions have names of the form "\ffpxxx(args)" ("\f" for function, "fp" for floating-point), where "xxx" is replaced by the name of the function, such as "sqrt", and "args" is the argument list, consisting of one or two floating-point numbers (depending on the function), and an optional "d" argument that says now many decimal places should be shown in the result. Example: \ffpdiv(10,3,1) returns "3.3" \ffpdiv(10,3,2) returns "3.33" \ffpdiv(10,3,3) returns "3.333" and so on, up to the precision of the computer. If the decimal-places argument is less than zero, the fractional part of the result is truncated: \ffpdiv(10,3,-1) returns "3". If the decimal-places argument is 0, or is omitted, C-Kermit returns as many decimal places as are meaningful in the computer's floating-point precision, truncating any extraneous trailing 0's: \ffpdiv(10,8) returns "1.25". \ffpdiv(10,4) returns "2.5". \ffpdiv(10,2) returns "5.0". \ffpdiv(10,3) returns "3.333333333333333" (for 16-digit precision). There is no way to request that a floating-point function return a decimal point but no decimal places. However, this is easy enough to accomplish in other ways, for example by supplying it outside the function call: echo \ffpadd(\%a,\%b,-1). Kermit's floating-point functions always round the result for the requested number of decimal places when the "d" argument is given and has a value greater than 0 (see the description of \ffpround() just below). Floating-point arguments can be constants in floating-point format or variables whose values are floating-point numbers. If a floating-point argument is omitted, or is a variable with no value, 0.0 is supplied automatically. Example: def \%x 999.999 undef \%y echo \ffpmin(\%x,\%y) 0.0 Or equivalently: echo \ffpmin(999.999) 0.0 The floating-point functions are: \ffpround(f1,d) Returns f1 rounded to d decimal places. For this function only, d = 0 (or d omitted) has a special meaning: return the integer part of f1 rounded according to the fractional part. Examples: \ffpround(2.74653,-1) returns "2" (fraction truncated, no rounding). \ffpround(2.74653,0) returns "3" (integer part is rounded). \ffpround(2.74653) returns "3" (d omitted same as d = 0). \ffpround(2.74653,1) returns "2.7". \ffpround(2.74653,2) returns "2.75". \ffpround(2.74653,3) returns "2.747". \ffpround(2.74653,4) returns "2.7465", etc. \ffpadd(f1,f2,d) Returns the sum of f1 and f2. \ffpsubtract(f1,f2,d) Subtracts f2 from f1 and returns the result. \ffpmultiply(f1,f2,d) Returns the product of f1 and f2. \ffpdivide(f1,f2,d) If f2 is not 0, divides f1 by f2 and returns the quotient. If f2 is 0, a DIVIDE_BY_ZERO error occurs. \ffpraise(f1,f2,d) If f1 = 0 and f2 <= 0, or if f1 < 0 and f2 has a fractional part, an ARG_OUT_OF_RANGE error occurs; otherwise f1 raised to the f2 power is returned. \ffpsqrt(f1,d) If f1 >= 0, returns the square root of f1; otherwise ARG_OUT_OF_RANGE. \ffpabsolute(f1,d) Returns the absolute value of f1 (i.e. f1 without a sign). This is the floating-point analog of \fabsolute(n1). \ffpint(f1) Returns the integer part of f1. Equivalent to \ffpround(f1,-1). \ffpexp(f1,d) The base of natural logarithms, e (2.718282...), raised to the f1 power. \ffplogn(f1,d) The natural logarithm of f1 (the power to which e must be raised to obtain f1). \ffplog10(f1,d) The base-10 logarithm of f1 (the power to which 10 must be raised to obtain f1). \ffpmodulus(f1,f2,d) If f2 is not 0, the remainder after dividing f1 by f2. If f2 is 0, a DIVIDE_BY_ZERO error occurs. This is the floating-point analog of \fmod(n1,n2). \ffpmaximum(f1,f2,d) Returns the maximum of f1 and f2. This is the floating-point analog of \fmax(n1,n2). \ffpminimum(f1,f2,d) Returns the minimum of f1 and f2. This is the floating-point analog of \fmin(n1,n2). \ffpsine(f1,d) Returns the sine of f1 radians. \ffpcosine(f1,d) Returns the cosine of f1 radians. \ffptangent(f1,d) Returns the tangent of f1 radians. Note that all of these functions can be used with integer arguments. If you want an integer result, specify d = -1 (to truncate) or feed the result to \ffpround(xxx,0) (to round). Floating-point numbers (or variables or functions that return them) can be used in Boolean expressions (see [635]Section 7.20.2) that compare numbers: = x y != x y < x y > x y <= x y >= x y In these examples, x and y can be either integers or floating-point numbers in any combination. In an arithmetic comparison of an integer and a floating-point number, the integer is converted to floating-point before the comparison is made. Examples: .\%t = 3.000000000 .\%f = 3.141592653 .\%i = 3 if > \%f \%i echo Pi is greater. if = \%t \%i echo "\%i" = "\%t". A floating-point number can also be used in: IF number command where the command is executed if the number is nonzero. If the number is floating-point, the command is not executed if the number is 0.0, and is executed otherwise. Floating-point numbers can be sorted using ARRAY SORT /NUMERIC (see [636]Section 7.10.5 ). Two floating-point constants are provided: \v(math_pi) = Pi (3.141592653...) \v(math_e) = e, the base of natural logarithms (2.71828...) These are given to the computer's precision, e.g. 16 digits. This number itself is available in a variable: \v(math_precision) How many significant digits in a floating-point number. 7.24. Tracing Script Execution The TRACE command is handy for debugging scripts. TRACE [ { /ON, /OFF } ] [ { ASSIGNMENTS, COMMAND-LEVEL, ALL } ] Selects tracing of the given object. Optional switches are /ON and /OFF. If no switch is given, /ON is implied. The trace objects are ASSIGNMENTS, COMMAND-LEVEL, and ALL. The default object is ALL, meaning to select all trace objects (besides ALL). Thus TRACE by itself selects tracing of everything, as does TRACE /ON, and TRACE /OFF turns off all tracing. When tracing of ASSIGNMENTS is on, every time the value of any user-defined variable or macro changes, C-Kermit prints one of the following: >>> name: "value" The name of the variable or macro followed by the new value in quotes. This includes implicit macro-parameter assignments during macro invocation. >>> name: (undef) This indicates that the variable or macro has been undefined. <<< name: "value" For RETURN statements: the name of the macro and the return value. <<< name: (null) For RETURN statements that include no value or an empty value. When tracing of COMMAND-LEVEL is on, C-Kermit prints: [n] +F: "name" Whenever a command file is entered, where "n" is the command level (0 = top); the name of the command file is shown in quotes. [n] +M: "name" Whenever a macro is entered; "n" is the command level. The name of the macro is shown in quotes. [n] -F: "name" Whenever a command file is reentered from below, when a macro or command file that it has invoked has returned. [n] -M: "name" Whenever a macro is reentered from below. For other debugging tools, see SHOW ARGS, SHOW STACK, SET TAKE, SET MACRO, and of course, ECHO. 7.25. Compact Substring Notation It is often desirable to extract a substring from a string which is stored in a variable, and for this we have the \fsubstring() function, which is used like this: define \%a 1234567890 echo \fsubstring(\%a,3,4) ; substring from 3rd character length 4 3456 or like this with macro-named variables: define string 1234567890 echo \fsubstring(\m(string),3,4) 3456 C-Kermit 7.0 adds a pair of alternative compact notations: \:(variablename[start:length]) <-- Substring of variable's value \s(macroname[start:length]) <-- Substring of macro's definition These are exactly equivalent to using \fsubstring(), except more compact to write and also faster since evaluation is in one step instead of two. The "\:()" notation can be used with any Kermit variable, that is, almost anything that starts with a backslash: \:(\%a[2:6]) <-- equivalent to \fsubstring(\%a,2,6) \:(\&x[1][2:6]) <-- equivalent to \fsubstring(\&x[1],2,6) \:(\m(foo)[2:6]) <-- equivalent to \fsubstring(\m(foo),2,6) \:(\v(time)[2:6]) <-- equivalent to \fsubstring(\v(time),2,6) \:(\$(TERM)[2:6]) <-- equivalent to \fsubstring(\$(TERM),2,6) \:(ABCDEFGH[2:6]) <-- equivalent to \fsubstring(ABCDEFGH,2,6) Whatever appears between the left parenthesis and the left bracket is evaluated and then the indicated substring of the result is returned. The "\s()" notation is the same, except after evaluating the variable, the result is treated as a macro name and is looked up in the macro table. Then the indicated substring of the macro definition is returned. Example: define testing abcdefghijklmnopqrstuvwxyz define \%a testing \s(testing[2:6]) --> bcdefg \:(testing[2:6]) --> esting \:(\%a[2:6]) --> esting \s(\%a[2:6]) --> bcdefg Note that the following two examples are equivalent: \:(\m(foo)[2:6]) \s(foo[2:6]) The first number in the brackets is the 1-based starting position. If it is omitted, or less than 1, it is treated as 1. If it is greater than the length of the string, an empty string is returned. The second number is the length of the desired substring. If the second number is omitted, is less than 0, or would be past the end of the string, then "through the end of the string" is assumed. If it is 0, the empty string is returned. If the brackets are empty or omitted, the original string is returned. The starting position and length need not be literal numbers; they can also be variables, functions, arithmetic expressions, or even other \s() or \:() quantities; anything that evaluates to a number, for example: \s(block[1025:\fhex2n(\s(block[\%b:\%n+4]))/2]) Syntactically, \m(name) and \s(name) differ only in that the sequence [*] at the end of the name (where * is any sequence of 0 or more characters) is treated as substring notation in \s(name), but is considered part of the name in \m(name) (to see why, see [637]Section 7.10.9). 7.26. New WAIT Command Options The WAIT command has been extended to allow waiting for different kinds of things (formerly it only waited for modem signals). Now it also can wait for file events. 7.26.1. Waiting for Modem Signals The previous syntax: WAIT time { CD, DSR, RTS, RI, ... } has changed to: WAIT time MODEM-SIGNALS { CD, DSR, RTS, RI, ... } However, the previous syntax is still accepted. The behavior is the same in either case. 7.26.2. Waiting for File Events The new WAIT option: WAIT time FILE { CREATION, DELETION, MODIFICATION } filename lets you tell Kermit to wait the given amount of time (or until the given time of day) for a file whose name is filename to be created, deleted, or modified, respectively. The filename may not contain wildcards. If the specified event does not occur within the time limit, or if WAIT CANCELLATION is ON and you interrupt from the keyboard before the time is up, the WAIT command fails. If the event is MODIFICATION and the file does not exist, the command fails. Otherwise, if the given event occurs within the time limit, the command succeeds. Examples: WAIT 600 FILE DELETION oofa.tmp Wait up to 10 minutes for file oofa.tmp to disappear. WAIT 23:59:59 FILE MOD orders.db Wait until just before midnight for the orders.db file to be changed. Example: Suppose you want to have the current copy of /etc/motd on your screen at all times, and you want to hear a bell whenever it changes: def \%f /etc/motd ; The file of interest. while 1 { ; Loop forever... cls ; Clear the screen. echo \%f: \v(date) \v(time)... ; Print 2-line heading... echo if ( not exist \%f ) { ; If file doesn't exist, echo \%f does not exist... ; print message, wait 600 file creat \%f ; and wait for it to appear. continue } beep ; Something new - beep. type /head:\v(rows-2) \%f ; Display the file if fail exit 1 \%f: \ferrstring() ; (checking for errors). wait 999 file mod \%f ; Wait for it to change. } This notices when the file is created, deleted, or modified, and acts only then (or when you interrupt it with); the time shown in the heading is the time of the most recent event (including when the program started). See [638]Section 1.10, where the \v(kbchar) variable is explained. This lets you modify a loop like the one above to also accept single-character commands, which interrupt the WAIT, and dispatch accordingly. For example: wait 999 file mod \%f ; Wait for the file to change. if defined \v(kbchar) { ; Interrupted from keyboard? switch \v(kbchar) { ; Handle the keystroke... :q, exit ; Q to Quit :h, echo blah blah, break ; H for Help :default, beep, continue ; Anything else beep and ignore } } This lets you write event-driven applications that wait for up to three events at once: a file or modem event, a timeout, and a keystroke. 7.27. Relaxed FOR and SWITCH Syntax For consistency with the extended IF and WHILE syntax, the FOR and SWITCH control lists may (but need not be) enclosed in parentheses: FOR ( \%i 1 \%n 1 ) { command-list... } SWITCH ( \%c ) { command-list... } In the FOR command, the increment item can be omitted if the control list is enclosed in parentheses, in which case the increment defaults appropriately to 1 or -1, depending on the values of the first two variables. As with IF, the parentheses around the FOR-command control list must be set off by spaces (in the SWITCH command, the spaces are not required since the SWITCH expression is a single arithmetic expression). Also, outer braces around the command list are supplied automatically if you omit them, e.g.: FOR ( \%i 1 %n 1 ) echo \%i 8. USING OTHER FILE TRANSFER PROTOCOLS In C-Kermit 7.0, alternative protocols can be selected using switches. Switches are described in [639]Section 1.5; the use of protocol-selection switches is described in [640]Section 4.7.1. Example: send /binary /protocol:zmodem x.tar.gz Note that file transfer recovery works only with Kermit and Zmodem protocols. With Zmodem, recovery can be initiated only by the sender. Only pre-1988 versions of the publicly-distributed sz/rz programs use Standard I/O; those released later than that do not use Standard I/O and therefore do not work with REDIRECT. However, Omen Technology does offer an up-to-date redirectable version called crzsz, which must be licensed for use: "Unix Crz and Csz support XMODEM, YMODEM, and ZMODEM transfers when called by dial-out programs such as Kermit and certain versions of cu(1). They are clients designed for this use. "Crz and Csz are Copyrighted shareware programs. Use of these programs beyond a brief evaluation period requires registration. Please print the "mailer.rz" file, fill out the form and return same with your registration." To use the crzsz programs as your external XYZMODEM programs in C-Kermit, follow the instructions in the book, but put a "c" before each command, e.g.: set protocol zmodem {csz %s} {csz -a %s} crz crz crz crz To use Zmodem protocol over Telnet or other non-transparent connections, you might need to add the -e (Escape) option: set protocol zmodem {csz -e %s} {csz -e -a %s} crz crz crz crz 9. COMMAND-LINE OPTIONS 9.0. Extended-Format Command-Line Options Standard UNIX command line options are a single letter. C-Kermit has run out of letters, so new options are in a new extended format: --word[:arg] where a keyword (rather than a single letter) specifies the function, and if an argument is to be included, it is separated by a colon (or equal sign). Most of the new extended-format command-line options are only for use with the Internet Kermit Service Daemon; see the [641]IKSD Administration Guide for details. However, several of them are also general in nature: --nointerrupts Disables keyboard interrupts that are normally enabled, which are usually Ctrl-C (to interrupt a command) and Ctrl-Z (UNIX only, to suspend C-Kermit). --help Lists the extended command-line options that are available in your version of C-Kermit. If any options seem to be missing, that is because your copy of C-Kermit was built with compile-time options to deselect them. --helpfile:filename Specifies the name of a file to be displayed if the user types HELP (not followed by a specific command or topic), in place of the built-in top-level help text. The file need not fit on one screen; more-prompting is used if the file is more than one screen long if COMMAND MORE-PROMPTING is ON, as it is by default. --bannerfile:filename The name of a file containing a message to be printed after the user logs in, in place of the normal message (Copyright notice, "Type HELP or ? for help", "Default transfer mode is...", etc). --cdmessage:{on,off,0,1,2} For use in the Server-Side Server configuration; whenever the client tells the server to change directory, the server sends the contents of a "read me" file to the client's screen. This feature is On by default, and operates only in client/server mode when ON or 1. If set to 2 or higher, it also operates when the CD command is given at the IKSD> prompt. Synonym: --cdmsg. --cdfile:filename When cdmessage is on, this is the name of the "read me" file to be sent. Normally you would specify a relative (not absolute) name, since the file is opened using the literal name you specified, after changing to the new directory. Example: --cdfile:READ.ME You can also give a list of up to 8 filenames by (a) enclosing each filename in braces, and (b) enclosing the entire list in braces. Example: --cdfile:{{./.readme}{READ.ME}{aaareadme.txt}{README}{read-this- first}} When a list is given, it is searched from left to right and the first file found is displayed. The default list for UNIX is: {{./.readme}{README.TXT}{READ.ME}} 9.1. Command Line Personalities Beginning in version 7.0, if the C-Kermit binary is renamed to "telnet" (or TELNET.EXE, telnet.pr, etc, depending on the platform), it accepts the Telnet command line: telnet [ host [ port ] ] In Unix, you can achieve the same effect with a symlink: cd /usr/bin mv telnet oldtelnet ln -ls /usr/local/bin/kermit telnet When installed in this manner, C-Kermit always reads its initialization file. If no host (and therefore no port) is given, C-Kermit starts in interactive prompting mode. If a host is given as the first command-line argument, C-Kermit makes a connection to it. The host argument can be an IP host name or address, or the name of a TCP/IP entry in your C-Kermit network directory. If a port is given, it is used. If a port is not given, then if the hostname was found in your network directory and port was also listed there, then that port is used. Otherwise port 23 (the Telnet port) is used. When C-Kermit is called "telnet" and it is invoked with a hostname on the command line, it exits automatically when the connection is closed. While the connection is open, however, you may escape back and forth as many times as you like, transfer files, etc. An rlogin personality is also available, but it is less useful, at least in UNIX and VMS, where the Rlogin TCP port is privileged. The new variable \v(name) indicates the name with which C-Kermit was invoked ("kermit", "wermit", "k95", "telnet", etc). 9.2. Built-in Help for Command Line Options "kermit -h", given from the system prompt, lists as many command-line options as will fit on a standard 24x80 screen. For more comprehensive help, use the interactive HELP OPTIONS command that was added in C-Kermit 7.0: HELP OPTIONS Explains how command-line options work, their syntax, etc. HELP OPTIONS ALL Lists all command-line options and gives brief help about each one. HELP OPTION x Gives brief help about option "x". HELP EXTENDED-OPTIONS Lists the available extended-format command-line options. HELP EXTENDED-OPTION xxx Gives help for the specified extended option. 9.3. New Command-Line Options Command-line options added since C-Kermit 6.0 are: + (plus sign by itself): The next argument is the name of a script to execute; all subsequent arguments are ignored by C-Kermit itself, but passed to the script as top-level copies of \%1, \%2, etc; the \&_[] is also set accordingly. \%0 and \&_[0] become the name of the script file, rather than the pathname of the C-Kermit program, which is its normal value. Primarily for use in the top line of "Kerbang" scripts in UNIX (see [642]Section 7.19). Example from UNIX command line: $ kermit [ regular kermit args ] + filename Sample first line of Kerbang script: #!/usr/local/bin/kermit + -- (two hyphens surrounded by whitespace) Equivalent to "=", for compatibility with UNIX getopt(1,3). -G GET (like -g), but send the incoming file to standard output. Example: "kermit -G oofa.txt | lpr" retrieves a file from your local computer (providing it is running a Kermit program that supports the autodownload feature and has it enabled) and prints it. -O equivalent to -x (start up in server mode), but exits after the first client command has been executed (mnemonic: O = Only One). This one is handy replacing "kermit -x" in the "automatically start Kermit on the other end" string: set protocol kermit {kermit -ir} {kermit -r} {kermit -x} since -x leaves the remote Kermit in server mode after the transfer, which can be confusing, whereas -O makes it go away automatically after the transfer. -L Recursive, when used in combination with -s (mnemonic: L = Levels). In UNIX or other environments where the shell expands wildcards itself, the -s argument, if it contains wildcards, must be quoted to prevent this, e.g.: kermit -L -s "*.c" In UNIX only, "kermit -L -s ." means to send the current directory tree. See [643]Sections 4.10 and [644]4.11 about recursive file transfer. -V Equivalent to SET FILE PATTERNS OFF ([645]Section 4.3) and SET TRANSFER MODE MANUAL. In other words, take the FILE TYPE setting literally. For example, "kermit -VT oofa.bin" means send the file in Text mode, no matter what its name is and no matter whether a kindred spirit is recognized at the other end of the connection. -0 (digit zero) means "be 100% transparent in CONNECT mode". This is equivalent to the following series of commands: SET PARITY NONE, SET COMMAND BYTESIZE 8, SET TERMINAL BYTESIZE 8, SET FLOW NONE, SET TERM ESCAPE DISABLED, SET TERM CHAR TRANSPARENT, SET TERM AUTODOWNLOAD OFF, SET TERM APC OFF, SET TELOPT KERMIT REFUSE REFUSE. 10. C-KERMIT AND G-KERMIT Every multifunctioned and long-lived software program grows in complexity and size over time to meet the needs and requests of its users and the demands of the underlying technology as it changes. Eventually users begin to notice how big the application has grown, how much disk space it occupies, how long it takes to load, and they start to long for the good old days when it was lean and mean. Not long after that they begin asking for a "light" version that only does the basics with no frills. And so it is with C-Kermit. A "light" version of Kermit was released (for UNIX only) in December 1999 under the GNU General Public License; thus it is called G-Kermit (for GNU Kermit). All it does is send and receive files, period. You can find it at: [646]http://www.columbia.edu/kermit/gkermit.html Where the C-Kermit 7.0 binary might be anywhere from 1 to 3 million bytes in size, the G-Kermit binary ranges from 30K to 100K, depending on the underlying architecture (RISC vs CISC, etc). G-Kermit and C-Kermit may reside side-by-side on the same computer. G-Kermit does not make connections; it does not have a script language; it does not translate character sets. G-Kermit may be used instead of C-Kermit when: * It is on the remote end. * Files are to be transferred in binary mode or in text mode without character-set translation. * File timestamps don't need to be preserved. In such cases G-Kermit might be preferred since it generally starts up faster, and yet transfers files just as fast on most (but not necessarily all) kinds of connections; for example, it supports streaming ([647]Section 4.20). G-Kermit is also handy for bootstrapping. It is easier to load on a new computer than C-Kermit -- it fits on a floppy diskette with plenty of room to spare. Thus if you have (say) an old PC running (say) SCO Xenix and no network connection, you can download the Xenix version of G-Kermit to (say) a DOS or Windows PC, copy it to diskette, read the diskette on Xenix with "dosread", and then use G-Kermit to receive C-Kermit (which does not fit on a diskette). If diskettes aren't an option, other bootstrapping methods are possible too -- see the [648]G-Kermit web page for details. III. APPENDICES III.1. Character Set Tables III.1.1. The Hewlett Packard Roman8 Character Set dec col/row oct hex description 160 10/00 240 A0 (Undefined) 161 10/01 241 A1 A grave 162 10/02 242 A2 A circumflex 163 10/03 243 A3 E grave 164 10/04 244 A4 E circumflex 165 10/05 245 A5 E diaeresis 166 10/06 246 A6 I circumflex 167 10/07 247 A7 I diaeresis 168 10/08 250 A8 Acute accent 169 10/09 251 A9 Grave accent 170 10/10 252 AA Circumflex accent 171 10/11 253 AB Diaeresis 172 10/12 254 AC Tilde accent 173 10/13 255 AD U grave 174 10/14 256 AE U circumflex 175 10/15 257 AF Lira symbol 176 11/00 260 B0 Top bar (macron) 177 11/01 261 B1 Y acute 178 11/02 262 B2 y acute 179 11/03 263 B3 Degree Sign 180 11/04 264 B4 C cedilla 181 11/05 265 B5 c cedilla 182 11/06 266 B6 N tilde 183 11/07 267 B7 n tilde 184 11/08 270 B8 Inverted exclamation mark 185 11/09 271 B9 Inverted question mark 186 11/10 272 BA Currency symbol 187 11/11 273 BB Pound sterling symbol 188 11/12 274 BC Yen symbol 189 11/13 275 BD Paragraph 190 11/14 276 BE Florin (Guilder) symbol 191 11/15 277 BF Cent symbol 192 12/00 300 C0 a circumflex 193 12/01 301 C1 e circumflex 194 12/02 302 C2 o circumflex 195 12/03 303 C3 u circumflex 196 12/04 304 C4 a acute 197 12/05 305 C5 e acute 198 12/06 306 C6 o acute 199 12/07 307 C7 u acute 200 12/08 310 C8 a grave 201 12/09 311 C9 e grave 202 12/10 312 CA o grave 203 12/11 313 CB u grave 204 12/12 314 CC a diaeresis 205 12/13 315 CD e diaeresis 206 12/14 316 CE o diaeresis 207 12/15 317 CF u diaeresis 208 13/00 320 D0 A ring 209 13/01 321 D1 i circumflex 210 13/02 322 D2 O with stroke 211 13/03 323 D3 AE digraph 212 13/04 324 D4 a ring 213 13/05 325 D5 i acute 214 13/06 326 D6 o with stroke 215 13/07 327 D7 ae digraph 216 13/08 330 D8 A diaeresis 217 13/09 331 D9 i grave 218 13/10 332 DA O diaeresis 219 13/11 333 DB U diaeresis 220 13/12 334 DC E acute 221 13/13 335 DD i diaeresis 222 13/14 336 DE German sharp s 223 13/15 337 DF O circumflex 224 14/00 340 E0 A acute 225 14/01 341 E1 A tilde 226 14/02 342 E2 a tilde 227 14/03 343 E3 Icelandic Eth 228 14/04 344 E4 Icelandic eth 229 14/05 345 E5 I acute 230 14/06 346 E6 I grave 231 14/07 347 E7 O acute 232 14/08 350 E8 O grave 233 14/09 351 E9 O tilde 234 14/10 352 EA o tilde 235 14/11 353 EB S caron 236 14/12 354 EC s caron 237 14/13 355 ED U acute 238 14/14 356 EE Y diaeresis 239 14/15 357 EF y diaeresis 240 15/00 360 F0 Icelandic Thorn 241 15/01 361 F1 Icelandic thorn 242 15/02 362 F2 Middle dot 243 15/03 363 F3 Greek mu 244 15/04 364 F4 Pilcrow sign 245 15/05 365 F5 Fraction 3/4 246 15/06 366 F6 Long dash, horizontal bar 247 15/07 367 F7 Fraction 1/4 248 15/08 370 F8 Fraction 1/2 249 15/09 371 F9 Feminine ordinal 250 15/10 372 FA Masculine ordinal 251 15/11 373 FB Left guillemot 252 15/12 374 FC Solid box 253 15/13 375 FD Right guillemot 254 15/14 376 FE Plus or minus sign 255 15/15 377 FF (Undefined) III.1.2. Greek Character Sets III.1.2.1. The ISO 8859-7 Latin / Greek Alphabet = ELOT 928 dec col/row oct hex description 160 10/00 240 A0 No-break space 161 10/01 241 A1 Left single quotation mark 162 10/02 242 A2 right single quotation mark 163 10/03 243 A3 Pound sign 164 10/04 244 A4 (UNUSED) 165 10/05 245 A5 (UNUSED) 166 10/06 246 A6 Broken bar 167 10/07 247 A7 Paragraph sign 168 10/08 250 A8 Diaeresis (Dialytika) 169 10/09 251 A9 Copyright sign 170 10/10 252 AA (UNUSED) 171 10/11 253 AB Left angle quotation 172 10/12 254 AC Not sign 173 10/13 255 AD Soft hyphen 174 10/14 256 AE (UNUSED) 175 10/15 257 AF Horizontal bar (Parenthetiki pavla) 176 11/00 260 B0 Degree sign 177 11/01 261 B1 Plus-minus sign 178 11/02 262 B2 Superscript two 179 11/03 263 B3 Superscript three 180 11/04 264 B4 Accent (tonos) 181 11/05 265 B5 Diaeresis and accent (Dialytika and Tonos) 182 11/06 266 B6 Alpha with accent 183 11/07 267 B7 Middle dot (Ano Teleia) 184 11/08 270 B8 Epsilon with accent 185 11/09 271 B9 Eta with accent 186 11/10 272 BA Iota with accent 187 11/11 273 BB Right angle quotation 188 11/12 274 BC Omicron with accent 189 11/13 275 BD One half 190 11/14 276 BE Upsilon with accent 191 11/15 277 BF Omega with accent 192 12/00 300 C0 iota with diaeresis and accent 193 12/01 301 C1 Alpha 194 12/02 302 C2 Beta 195 12/03 303 C3 Gamma 196 12/04 304 C4 Delta 197 12/05 305 C5 Epsilon 198 12/06 306 C6 Zeta 199 12/07 307 C7 Eta 200 12/08 310 C8 Theta 201 12/09 311 C9 Iota 202 12/10 312 CA Kappa 203 12/11 313 CB Lamda 204 12/12 314 CC Mu 205 12/13 315 CD Nu 206 12/14 316 CE Ksi 207 12/15 317 CF Omicron 208 13/00 320 D0 Pi 209 13/01 321 D1 Rho 210 13/02 322 D2 (UNUSED) 211 13/03 323 D3 Sigma 212 13/04 324 D4 Tau 213 13/05 325 D5 Upsilon 214 13/06 326 D6 Phi 215 13/07 327 D7 Khi 216 13/08 330 D8 Psi 217 13/09 331 D9 Omega 218 13/10 332 DA Iota with diaeresis 219 13/11 333 DB Upsilon with diaeresis 220 13/12 334 DC alpha with accent 221 13/13 335 DD epsilon with accent 222 13/14 336 DE eta with accent 223 13/15 337 DF iota with accent 224 14/00 340 E0 upsilon with diaeresis and accent 225 14/01 341 E1 alpha 226 14/02 342 E2 beta 227 14/03 343 E3 gamma 228 14/04 344 E4 delta 229 14/05 345 E5 epsilon 230 14/06 346 E6 zeta 231 14/07 347 E7 eta 232 14/08 350 E8 theta 233 14/09 351 E9 iota 234 14/10 352 EA kappa 235 14/11 353 EB lamda 236 14/12 354 EC mu 237 14/13 355 ED nu 238 14/14 356 EE ksi 239 14/15 357 EF omicron 240 15/00 360 F0 pi 241 15/01 361 F1 rho 242 15/02 362 F2 terminal sigma 243 15/03 363 F3 sigma 244 15/04 364 F4 tau 245 15/05 365 F5 upsilon 246 15/06 366 F6 phi 247 15/07 367 F7 khi 248 15/08 370 F8 psi 249 15/09 371 F9 omega 250 15/10 372 FA iota with diaeresis 251 15/11 373 FB upsilon with diaeresis 252 15/12 374 FC omicron with diaeresis 253 15/13 375 FD upsilon with accent 254 15/14 376 FE omega with accent 255 15/15 377 FF (UNUSED) III.1.2.2. The ELOT 927 Character Set dec col/row oct hex description 32 02/00 40 20 SPACE 33 02/01 41 21 EXCLAMATION MARK 34 02/02 42 22 QUOTATION MARK 35 02/03 43 23 NUMBER SIGN 36 02/04 44 24 DOLLAR SIGN 37 02/05 45 25 PERCENT SIGN 38 02/06 46 26 AMPERSAND 39 02/07 47 27 APOSTROPHE 40 02/08 50 28 LEFT PARENTHESIS 41 02/09 51 29 RIGHT PARENTHESIS 42 02/10 52 2A ASTERISK 43 02/11 53 2B PLUS SIGN 44 02/12 54 2C COMMA 45 02/13 55 2D HYPHEN, MINUS SIGN 46 02/14 56 2E PERIOD, FULL STOP 47 02/15 57 2F SOLIDUS, SLASH 48 03/00 60 30 DIGIT ZERO 49 03/01 61 31 DIGIT ONE 50 03/02 62 32 DIGIT TWO 51 03/03 63 33 DIGIT THREE 52 03/04 64 34 DIGIT FOUR 53 03/05 65 35 DIGIT FIVE 54 03/06 66 36 DIGIT SIX 55 03/07 67 37 DIGIT SEVEN 56 03/08 70 38 DIGIT EIGHT 57 03/09 71 39 DIGIT NINE 58 03/10 72 3A COLON 59 03/11 73 3B SEMICOLON 60 03/12 74 3C LESS-THAN SIGN, LEFT ANGLE BRACKET 61 03/13 75 3D EQUALS SIGN 62 03/14 76 3E GREATER-THAN SIGN, RIGHT ANGLE BRACKET 63 03/15 77 3F QUESTION MARK 64 04/00 100 40 COMMERCIAL AT SIGN 65 04/01 101 41 CAPITAL LETTER A 66 04/02 102 42 CAPITAL LETTER B 67 04/03 103 43 CAPITAL LETTER C 68 04/04 104 44 CAPITAL LETTER D 69 04/05 105 45 CAPITAL LETTER E 70 04/06 106 46 CAPITAL LETTER F 71 04/07 107 47 CAPITAL LETTER G 72 04/08 110 48 CAPITAL LETTER H 73 04/09 111 49 CAPITAL LETTER I 74 04/10 112 4A CAPITAL LETTER J 75 04/11 113 4B CAPITAL LETTER K 76 04/12 114 4C CAPITAL LETTER L 77 04/13 115 4D CAPITAL LETTER M 78 04/14 116 4E CAPITAL LETTER N 79 04/15 117 4F CAPITAL LETTER O 80 05/00 120 50 CAPITAL LETTER P 81 05/01 121 51 CAPITAL LETTER Q 82 05/02 122 52 CAPITAL LETTER R 83 05/03 123 53 CAPITAL LETTER S 84 05/04 124 54 CAPITAL LETTER T 85 05/05 125 55 CAPITAL LETTER U 86 05/06 126 56 CAPITAL LETTER V 87 05/07 127 57 CAPITAL LETTER W 88 05/08 130 58 CAPITAL LETTER X 89 05/09 131 59 CAPITAL LETTER Y 90 05/10 132 5A CAPITAL LETTER Z 91 05/11 133 5B LEFT SQUARE BRACKET 92 05/12 134 5C REVERSE SOLIDUS, BACKSLASH 93 05/13 135 5D RIGHT SQUARE BRACKET 94 05/14 136 5E CIRCUMFLEX ACCENT 95 05/15 137 5F UNDERSCORE 96 06/00 140 60 ACCENT GRAVE 97 06/01 141 61 GREEK LETTER ALPHA 98 06/02 142 62 GREEK LETTER BETA 99 06/03 143 63 GREEK LETTER GAMMA 100 06/04 144 64 GREEK LETTER DELTA 101 06/05 145 65 GREEK LETTER EPSILON 102 06/06 146 66 GREEK LETTER ZETA 103 06/07 147 67 GREEK LETTER ETA 104 06/08 150 68 GREEK LETTER THETA 105 06/09 151 69 GREEK LETTER IOTA 106 06/10 152 6A GREEK LETTER KAPPA 107 06/11 153 6B GREEK LETTER LAMDA 108 06/12 154 6C GREEK LETTER MU 109 06/13 155 6D GREEK LETTER NU 110 06/14 156 6E GREEK LETTER KSI 111 06/15 157 6F GREEK LETTER OMICRON 112 07/00 160 70 GREEK LETTER PI 113 07/01 161 71 GREEK LETTER RHO 114 07/02 162 72 GREEK LETTER SIGMA 115 07/03 163 73 GREEK LETTER TAU 116 07/04 164 74 GREEK LETTER UPSILON 117 07/05 165 75 GREEK LETTER FI 118 07/06 166 76 GREEK LETTER XI 119 07/07 167 77 GREEK LETTER PSI 120 07/08 170 78 GREEK LETTER OMEGA 121 07/09 171 79 SPACE 122 07/10 172 7A SPACE 123 07/11 173 7B LEFT CURLY BRACKET, LEFT BRACE 124 07/12 174 7C VERTICAL LINE, VERTICAL BAR 125 07/13 175 7D RIGHT CURLY BRACKET, RIGHT BRACE 126 07/14 176 7E TILDE 127 07/15 177 7F RUBOUT, DELETE III.1.2.3. PC Code Page 869 (to be filled in...) III.2. Updated Country Codes Date: Mon, 7 Apr 1997 23:23:49 EDT From: Dave Leibold Newsgroups: comp.dcom.telecom Subject: Ex-USSR Country Codes Profile Organization: TELECOM Digest Ex-USSR Country Codes Profile 4 April 1997 Below is a summary of the country codes that have formed in the wake of the USSR dissolution, along with some updated findings and reports. Additional or corrected information on any of these nations would be welcome (c/o dleibold@else.net). * Kyrgyz Republic country code 996 will take effect, at least in Canada, effective 1 May 1997, according to CRTC Telecom Order 97-464, based on Stentor Tariff Notice 433. There is no indication whether there will be a permissive dialing period involved or for how long such a permissive operation would remain. * Country code 992 was reported as a recent assignment for Tajikistan, which will be moving from country code 7 at some unknown time. * Uzbekistan has its own country code assignment, but I have no information if this is in service yet or what implementation dates have been set. * Kazakstan does not have a known separate country code assignment at present. It remains in country code 7 for the time being. * Russia seems destined to keep country code 7. * Recent news reports speak of some agreements forming between Russia and Belarus. While there is no outright reunification yet, there is expected to be much closer ties between the two nations. Whether this will lead to a reunification of telephone codes remains to be seen. In the table, "Effective" means the date at which the country code began service (which could vary according to the nation). "Mandatory" means the date at which the country code 7 is invalid for calls to that nation. There are a number of question marks since exact dates have not been collected in all cases. CC Nation Effective Mandatory Notes 370 Lithuania 1993? ??? Announced Jan 1993 371 Latvia 1993? ??? 372 Estonia 1 Feb 1993? March 1993? 373 Moldova 1993? ??? Announced Jan 1993 374 Armenia 1 May 1995 1 July 1995 Announced Jan 1995 (ITU) 375 Belarus 16 Apr 1995 1997? 380 Ukraine 16 Apr 1995 Oct 1995? 7 Kazakstan (no known changes) 7 Russia (presumably not changing) 992 Tajikistan ??? ??? Announced 1996-7? 993 Turkmenistan 3 Jan 1997 3 Apr 1997 Canada as of 29 Nov 1996 994 Azerbaijan Sept 1994? ??? Announced 1992 995 Georgia 1994? ??? ref: Telecom Digest Oct 1994 996 Kyrgyz Republic 1 May 1997 ??? ref: Stentor Canada/CRTC 998 Uzbekistan ??? ??? Announced 1996? (ITU) Details courtesy Toby Nixon, ITU, Stentor (Canada), CRTC (Canada), TELECOM Digest (including information collected for the country code listings). IV. ERRATA & CORRIGENDA The following errors in [649]Using C-Kermit, Second Edition, first printing, have been noted. First, some missing acknowledgements for C-Kermit 6.0: JE Jones of Microware for help with OS-9, Nigel Roles for his help with Plan 9, Lucas Hart for help with VMS and Digital UNIX, Igor Kovalenko for his help with QNX. And later, to Susan Kleinmann for her help with Debian Linux packaging; Patrick Volkerding for his help with Slackware Linux packaging; Jim Knoble for his help with Red Hat Linux packaging; and to dozens of others for sending individual C-Kermit binaries for varied and diverse platforms. Thanks to James Spath for both binaries and reporting many of the typos noted below. Also to Dat Thuc Nguyen for spotting several typos. PAGE REMARKS COVER "COS" is a misprint. There is no COS. Pretend it says "SCO" or "VOS". (This is fixed in the second printing.) xxi Second line: Fred Smith's affiliation should be Computrition. 83 Change "commands other" to "commands as other" (1st paragraph) 87 Change "The the" to "The" (2nd paragraph) 92 "set modem-type user-defined supra" should be "set modem type ..." 95 Change "VI" to "vi" (1st paragraph) 96 Change "it it" to "it is" (1st paragraph) 97 Change "advantage a literal" to "advantage of a literal" (2nd paragraph) 102 The call-waiting example would be better as SET DIAL PREFIX *70W (rather than "*70,") because the former will not cause an incorrect call to be placed with pulse dialing. 123 Third paragraph from bottom: "..otherwise if a your local username.." should be "..otherwise your local username..". 160 Delete the "it" between "and" and "to" (2nd paragraph) 185 In "When TRANSFER DISPLAY is OFF, C-Kermit skips the display...", "OFF" should be "NONE". 187 The last paragraph says the "A command" is ignored, should be "S". 194 Change "it known" to "it is known" (4th paragraph). 235 In C-Kermit 7.0, the syntax of the GET command changed. MGET now must be used to get a list of files and there is no more multiline GET command. 268 Last paragraph: "effect" should be "affect". 275 In the SET PROTOCOL KERMIT description, the following sentence is incorrect and should be removed: 'If you omit the commands, the default ones are restored: "kermit -ir" and "kermit -r" respectively". The correct information is given at the bottom of page 281. 279 9th line. The decimal value of ST is 156, not 155. 295 In the stepping stones, skip ahead to Chapter 17 on p. 327. 298 Table 16-2, Portuguese entry. Column 4/00 should show section sign, not acute accent. 316 Other languages written in the Hebrew alphabet include Karaim (a Turkic language spoken in Lithuania and Poland), Judeo-Kurdish, and Judeo- Georgian. 332 UNDEFINE definition, change "This just" to "This is just". 344 It might be necessary to set the modem's pulse generation rate when sending numeric pages; most Hayes compatible modems use the S11 register for this. 350 Delete "is" from between "It" and "ceases" (4th paragraph) 351 Top - both occurrences of "print \%a" should be "echo \%a". 364 \v(input) and \v(query) out of alphabetical order. 378 In the MYSEND macro, "if not \m(rc) goto bad" should be: "if \m(rc) goto bad" (remove the "not"). 382-383 It should be stated that the loop control variable must be of the \%a type, or else an array element; macro names can not be used for this. 383 In line 3, "\%f[\%i]" should be "\&f[\%i]". 383 In the sort example, it should be stated that the array is 1-based. 387 Change "You can list" to "You can get a list" (5th paragraph) 393 \Fverify() description. The 3rd sentence could be stated more clearly as "If all characters in string2 are also in string1, 0 is returned." 398 Copying \ffiles() results to an array before is not required as of C-Kermit 7.0 (see [650]Section 7.3). 403 In "(\%a + 3) * (\%b 5)", a minus sign is missing between b and 5. 407 C-Kermit 7.0 no longer supports multiline GET. Change "get, \%1, \%2" to "get {\%1} {\%2}" or "get /as:{\%2} {\%1}". 409 READ example while loop should be: while success { echo \m(line), read line } 409 "WRITE file" should be "WRITE keyword" (you can't put a filename there) (The same applies to WRITE-LINE / WRITELN). 414 \Funhexify() missing from Table 18-3. 425 MINPUT definition, change 2nd "text2" to "text3". 436 Several lines are missing from the UNIXLOGIN macro listing. After the "xif fail" block, insert: out \%1\13 ; Send username, carriage return inp 5 Password: ; Wait 5 sec for this prompt if fail end 1 No password prompt pause ; Wait a sec out \%2\13 ; Send password 440 Change "set terminal byteszie" to "set terminal bytesize". Change "input Password:" to "input 10 Password". 448 Franchise script: "access line" should be "access \m(line)". 453 There are two incorrectly coded IF statements in the DELIVER macro definition. Replace both occurrences of "if > \%1 \%3 {" with "xif > \%i \%3 {" (replace "if" by "xif" and "\%1" with "\%i"). 453 "the the" (last paragraph) should be "the". 454 EOT (last paragraph) is End of Transmission, not End of Text. 457 _DEFINE definition: "name constructed" should be "name is constructed". 457 "macro for and" (last paragraph) should be "macro and". 459 Should explain that \v(user) is a legal abbreviation of \v(userid). 480 Figure II-2 is backwards; the least-significant bit is transmitted first, then up to the highest, and the parity bit last. 534 The VMS Appendix section on Odd Record Lengths no longer applies; C-Kermit 7.0 handles odd record lengths as well as even ones. 559 Table VIII-3, Portuguese entry. Column 4/00 should show section sign, not acute accent. 560-563 HP-Roman8 missing from Table VII-4; there wasn't room to squeeze it in. It is listed in section II(6). 565 "d stroke" in Table VII-5 has the wrong appearance; the stem should be upright. The letter shown in the table is actually a lowercase Icelandic eth, which has a curved stem. 601-604 BeBox, BeOS, Plan 9, and probably others not listed in trademarks. 604 The words "SCRIBE TEXT FORMATTER" appear at the end of the last sentence of the first paragraph of the Colophon. They should have been in the Index. Index: Missing entries: SET { SEND, RECEIVE } PATHNAMES, Call waiting, ... \F() Page 605, add also 413-414 \Fbreak 389 \Fcapitalize 390 \Fchecksum 414 \Fcrc16 414 \Fexecute 414 \Fhexify 390 \Fltrim 391 \Frepeat 392 \Fspawn 392 \Ftod2secs 399 \v() built_in Page 606, add also 361-364 \v(_line) 354, 361 \v(apcactive) 361 \v(charset) 362 \v(cpu) 362 \v(crc16) 357, 362 \v(d$xxx) add page 362 \v(dialnumber) 362 \v(dialresult) 362 \v(errno) 362 \v(errstring) 362 \v(exedir) 362 \v(inidir) 363 \v(ipaddress) 363 \v(keyboard) 363 \v(macro) 363 \v(minput) 363 \v(m_xxx) 94, 363 \v(password) 364 \v(query) 364 \v(prompt) 364 \v(speed) 356, 364 \v(startup) 364 \v(status) 364 \v(sysid) 364 \v(system) 364 \v(fsize) at lower half page 606 should read \v(tfsize) \v(xversion) 364 BEEP Command 40 SET FLOW 62, 212 Figure II-5 on page 493. The pin assignments of the Mini Din-8 connector are not described anywhere. As noted in the text, these tend to vary from vendor to vendor. One common arrangement is: 1. HSKout (Handshake out -- definition depends on software) 2. HSKin (Handshake in or external clock) 3. TxD- 4. Not used 5. RxD- 6. TxD+ 7. Not used 8. RxD+ Note the "balanced pairs" for Receive Data (RxD) and Transmit Data (TxD), and the utter lack of modem signals. These connectors follow the RS-423 standard, rather than RS-232. In some arrangements, Pin 1 is used for DTR and Pin 2 for CD; in others Pin 1 is RTS and Pin 2 is CTS. Please send reports of other errors to the authors, as well as suggestions for improvements, additional index entries, and any other comments: [651]kermit@columbia.edu APPENDIX V. ADDITIONAL COPYRIGHT NOTICES The following copyrights cover some of the source code used in the development of C-Kermit, Kermit 95, or Kermit 95 support libraries. /*****************************************************************************/ /* */ /* Copyright (c) 1995 by Oy Online Solutions Ltd. */ /* */ /* Distribution of this source code is strictly forbidden. Use of this */ /* source code is granted to the University of Columbia C-Kermit project */ /* to be distributed in binary format only. Please familiarize yourself */ /* with the accompanying LICENSE.P file. */ /* */ /*****************************************************************************/ used for Xmodem, Ymodem, and Zmodem protocol in Kermit 95 (p95.dll, p2.dll) Copyright (c) 1997 Stanford University The use of this software for revenue-generating purposes may require a license from the owners of the underlying intellectual property. Specifically, the SRP-3 protocol may not be used for revenue-generating purposes without a license. Within that constraint, permission to use, copy, modify, and distribute this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notices and this permission notice appear in all copies of the software and related documentation. THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL STANFORD BE LIABLE FOR ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. Used for Secure Remote Password (TM) protocol (SRP) in C-Kermit, Kermit 95 (k95.exe, k2.exe, k95crypt.dll, k2crypt.dll) Copyright 1990 by the Massachusetts Institute of Technology. All Rights Reserved. Export of this software from the United States of America may require a specific license from the United States Government. It is the responsibility of any person or organization contemplating export to obtain such a license before exporting. WITHIN THAT CONSTRAINT, permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of M.I.T. not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. M.I.T. makes no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. Used for Telnet Authentication Option, Telnet Encryption Option, and Kerberos (TM) authentication in C-Kermit, Kermit 95 (k95.exe, k2.exe, k95crypt.dll, k2crypt.dll) Copyright (c) 1991, 1993 The Regents of the University of California. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. All advertising materials mentioning features or use of this software must display the following acknowledgement: This product includes software developed by the University of California, Berkeley and its contributors. 4. Neither the name of the University nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Used for Telnet Authentication Option, Telnet Encryption Option, and Kerberos (TM) authentication in C-Kermit, Kermit 95 (k95.exe, k2.exe, k95crypt.dll, k2crypt.dll) Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com) All rights reserved. This package is an DES implementation written by Eric Young (eay@cryptsoft.com). The implementation was written so as to conform with MIT's libdes. This library is free for commercial and non-commercial use as long as the following conditions are aheared to. The following conditions apply to all code found in this distribution. Copyright remains Eric Young's, and as such any Copyright notices in the code are not to be removed. If this package is used in a product, Eric Young should be given attribution as the author of that the SSL library. This can be in the form of a textual message at program startup or in documentation (online or textual) provided with the package. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. All advertising materials mentioning features or use of this software must display the following acknowledgement: This product includes software developed by Eric Young (eay@cryptsoft.com) THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. The license and distribution terms for any publically available version or derivative of this code cannot be changed. i.e. this code cannot simply be copied and put under another distribution license [including the GNU Public License.] The reason behind this being stated in this direct manner is past experience in code simply being copied and the attribution removed from it and then being distributed as part of other packages. This implementation was a non-trivial and unpaid effort. Used DES encryption in Kermit 95 (k95crypt.dll, k2crypt.dll) __________________________________________________________________ * This is version 1.1 of CryptoLib * * The authors of this software are Jack Lacy, Don Mitchell and Matt Blaze * Copyright (c) 1991, 1992, 1993, 1994, 1995 by AT&T. * Permission to use, copy, and modify this software without fee * is hereby granted, provided that this entire notice is included in * all copies of any software which is or includes a copy or * modification of this software and in all copies of the supporting * documentation for such software. * * NOTE: * Some of the algorithms in cryptolib may be covered by patents. * It is the responsibility of the user to ensure that any required * licenses are obtained. * * * SOME PARTS OF CRYPTOLIB MAY BE RESTRICTED UNDER UNITED STATES EXPORT * REGULATIONS. * * * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR AT&T MAKE ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. Used for Big Number library in Kermit 95 (k95crypt.dll, k2crypt.dll). [ [652]Top ] [ [653]C-Kermit ] [ [654]Kermit Home ] __________________________________________________________________ CKERMIT70.HTM / The Kermit Project / Columbia University / 8 Feb 2000 / Last update: 8 Aug 2011 References 1. http://www.columbia.edu/ 2. mailto:kermit@columbia.edu 3. http://www.columbia.edu/kermit/index.html 4. http://www.columbia.edu/kermit/k95.html 5. http://www.columbia.edu/kermit/ckermit.html 6. http://www.columbia.edu/kermit/ckscripts.html 7. http://www.columbia.edu/kermit/current.html 8. http://www.columbia.edu/kermit/whatsnew.html 9. http://www.columbia.edu/kermit/faq.html 10. http://www.columbia.edu/kermit/support.html 11. http://www.amazon.com/gp/product/1555581641?ie=UTF8&tag=aleidmoreldom-20&linkCode=as2&camp=1789&creative=9325&creativeASIN=1555581641 12. mailto:kermit-support@columbia.edu 13. http://www.columbia.edu/kermit/ 14. http://www.kermit-project.org/ 15. http://www.columbia.nyc.ny.us/kermit/ 16. ftp://kermit.columbia.edu/kermit/f/COPYING.TXT 17. ftp://kermit.columbia.edu/kermit/f/ckcmai.c 18. http://www.columbia.edu/kermit/ckermit70.html#xv 19. http://www.columbia.edu/kermit/ckb2.htm 20. ftp://kermit.columbia.edu/kermit/f/ckcbwr.txt 21. ftp://kermit.columbia.edu/kermit/f/ckubwr.txt 22. ftp://kermit.columbia.edu/kermit/f/ckvbwr.txt 23. ftp://kermit.columbia.edu/kermit/f/ckubwr.txt 24. ftp://kermit.columbia.edu/kermit/f/ckermit70.txt 25. ftp://kermit.columbia.edu/kermit/f/security.txt 26. http://www.columbia.edu/kermit/security.htm 27. ftp://kermit.columbia.edu/kermit/f/iksd.txt 28. http://www.columbia.edu/kermit/iksd.htm 29. http://www.columbia.edu/kermit/cuiksd.htm 30. ftp://kermit.columbia.edu/kermit/f/telnet.txt 31. http://www.columbia.edu/kermit/telnet.htm 32. ftp://kermit.columbia.edu/kermit/f/COPYING.TXT 33. http://www.columbia.edu/kermit/k95.html 34. http://www.opensource.org/ 35. http://www.columbia.edu/kermit/ckb2.htm 36. http://www.columbia.edu/kermit/ckermit70.html#xi 37. http://www.columbia.edu/kermit/ckermit70.html#xii 38. http://www.columbia.edu/kermit/ckermit70.html#x0 39. http://www.columbia.edu/kermit/ckermit70.html#x1 40. http://www.columbia.edu/kermit/ckermit70.html#x1.0 41. http://www.columbia.edu/kermit/ckermit70.html#x1.1 42. http://www.columbia.edu/kermit/ckermit70.html#x1.2 43. http://www.columbia.edu/kermit/ckermit70.html#x1.3 44. http://www.columbia.edu/kermit/ckermit70.html#x1.4 45. http://www.columbia.edu/kermit/ckermit70.html#x1.5 46. http://www.columbia.edu/kermit/ckermit70.html#x1.5.1 47. http://www.columbia.edu/kermit/ckermit70.html#x1.5.2 48. http://www.columbia.edu/kermit/ckermit70.html#x1.5.3 49. http://www.columbia.edu/kermit/ckermit70.html#x1.5.4 50. http://www.columbia.edu/kermit/ckermit70.html#x1.5.5 51. http://www.columbia.edu/kermit/ckermit70.html#x1.6 52. http://www.columbia.edu/kermit/ckermit70.html#x1.7 53. http://www.columbia.edu/kermit/ckermit70.html#x1.8 54. http://www.columbia.edu/kermit/ckermit70.html#x1.9 55. http://www.columbia.edu/kermit/ckermit70.html#x1.10 56. http://www.columbia.edu/kermit/ckermit70.html#x1.11 57. http://www.columbia.edu/kermit/ckermit70.html#x1.11.1 58. http://www.columbia.edu/kermit/ckermit70.html#x1.11.2 59. http://www.columbia.edu/kermit/ckermit70.html#x1.11.3 60. http://www.columbia.edu/kermit/ckermit70.html#x1.11.4 61. http://www.columbia.edu/kermit/ckermit70.html#x1.11.5 62. http://www.columbia.edu/kermit/ckermit70.html#x1.11.6 63. http://www.columbia.edu/kermit/ckermit70.html#x1.11.7 64. http://www.columbia.edu/kermit/ckermit70.html#x1.12 65. http://www.columbia.edu/kermit/ckermit70.html#x1.13 66. http://www.columbia.edu/kermit/ckermit70.html#x1.14 67. http://www.columbia.edu/kermit/ckermit70.html#x1.15 68. http://www.columbia.edu/kermit/ckermit70.html#x1.16 69. http://www.columbia.edu/kermit/ckermit70.html#x1.17 70. http://www.columbia.edu/kermit/ckermit70.html#x1.18 71. http://www.columbia.edu/kermit/ckermit70.html#x1.19 72. http://www.columbia.edu/kermit/ckermit70.html#x1.20 73. http://www.columbia.edu/kermit/ckermit70.html#x1.21 74. http://www.columbia.edu/kermit/ckermit70.html#x1.22 75. http://www.columbia.edu/kermit/ckermit70.html#x1.22.1 76. http://www.columbia.edu/kermit/ckermit70.html#x1.22.2 77. http://www.columbia.edu/kermit/ckermit70.html#x1.22.3 78. http://www.columbia.edu/kermit/ckermit70.html#x1.22.4 79. http://www.columbia.edu/kermit/ckermit70.html#x1.22.5 80. http://www.columbia.edu/kermit/ckermit70.html#x1.22.6 81. http://www.columbia.edu/kermit/ckermit70.html#x1.22.7 82. http://www.columbia.edu/kermit/ckermit70.html#x1.22.8 83. http://www.columbia.edu/kermit/ckermit70.html#x1.23 84. http://www.columbia.edu/kermit/ckermit70.html#x1.24 85. http://www.columbia.edu/kermit/ckermit70.html#x2 86. http://www.columbia.edu/kermit/ckermit70.html#x2.0 87. http://www.columbia.edu/kermit/ckermit70.html#x2.1 88. http://www.columbia.edu/kermit/ckermit70.html#x2.1.1 89. http://www.columbia.edu/kermit/ckermit70.html#x2.1.2 90. http://www.columbia.edu/kermit/ckermit70.html#x2.1.3 91. http://www.columbia.edu/kermit/ckermit70.html#x2.1.4 92. http://www.columbia.edu/kermit/ckermit70.html#x2.1.5 93. http://www.columbia.edu/kermit/ckermit70.html#x2.1.6 94. http://www.columbia.edu/kermit/ckermit70.html#x2.1.7 95. http://www.columbia.edu/kermit/ckermit70.html#x2.1.8 96. http://www.columbia.edu/kermit/ckermit70.html#x2.1.9 97. http://www.columbia.edu/kermit/ckermit70.html#x2.1.10 98. http://www.columbia.edu/kermit/ckermit70.html#x2.1.11 99. http://www.columbia.edu/kermit/ckermit70.html#x2.1.12 100. http://www.columbia.edu/kermit/ckermit70.html#x2.1.13 101. http://www.columbia.edu/kermit/ckermit70.html#x2.1.14 102. http://www.columbia.edu/kermit/ckermit70.html#x2.1.15 103. http://www.columbia.edu/kermit/ckermit70.html#x2.1.16 104. http://www.columbia.edu/kermit/ckermit70.html#x2.2 105. http://www.columbia.edu/kermit/ckermit70.html#x2.2.1 106. http://www.columbia.edu/kermit/ckermit70.html#x2.2.2 107. http://www.columbia.edu/kermit/ckermit70.html#x2.3 108. http://www.columbia.edu/kermit/ckermit70.html#x2.3.0 109. http://www.columbia.edu/kermit/ckermit70.html#x2.3.1 110. http://www.columbia.edu/kermit/ckermit70.html#x2.3.2 111. http://www.columbia.edu/kermit/ckermit70.html#x2.3.3 112. http://www.columbia.edu/kermit/ckermit70.html#x2.3.4 113. http://www.columbia.edu/kermit/ckermit70.html#x2.3.5 114. http://www.columbia.edu/kermit/ckermit70.html#x2.3.6 115. http://www.columbia.edu/kermit/ckermit70.html#x2.4 116. http://www.columbia.edu/kermit/ckermit70.html#x2.5 117. http://www.columbia.edu/kermit/ckermit70.html#x2.6 118. http://www.columbia.edu/kermit/ckermit70.html#x2.7 119. http://www.columbia.edu/kermit/ckermit70.html#x2.7.0 120. http://www.columbia.edu/kermit/ckermit70.html#x2.7.1 121. http://www.columbia.edu/kermit/ckermit70.html#x2.7.2 122. http://www.columbia.edu/kermit/ckermit70.html#x2.7.3 123. http://www.columbia.edu/kermit/ckermit70.html#x2.7.4 124. http://www.columbia.edu/kermit/ckermit70.html#x2.7.4.1 125. http://www.columbia.edu/kermit/ckermit70.html#x2.7.4.2 126. http://www.columbia.edu/kermit/ckermit70.html#x2.7.4.3 127. http://www.columbia.edu/kermit/ckermit70.html#x2.7.4.4 128. http://www.columbia.edu/kermit/ckermit70.html#x2.7.4.5 129. http://www.columbia.edu/kermit/ckermit70.html#x2.8 130. http://www.columbia.edu/kermit/ckermit70.html#x2.9 131. http://www.columbia.edu/kermit/ckermit70.html#x2.9.1 132. http://www.columbia.edu/kermit/ckermit70.html#x2.9.2 133. http://www.columbia.edu/kermit/ckermit70.html#x2.10 134. http://www.columbia.edu/kermit/ckermit70.html#x2.11 135. http://www.columbia.edu/kermit/ckermit70.html#x2.12 136. http://www.columbia.edu/kermit/ckermit70.html#x2.13 137. http://www.columbia.edu/kermit/ckermit70.html#x2.14 138. http://www.columbia.edu/kermit/ckermit70.html#x2.15 139. http://www.columbia.edu/kermit/ckermit70.html#x3 140. http://www.columbia.edu/kermit/ckermit70.html#x3.1 141. http://www.columbia.edu/kermit/ckermit70.html#x3.2 142. http://www.columbia.edu/kermit/ckermit70.html#x3.3 143. http://www.columbia.edu/kermit/ckermit70.html#x3.4 144. http://www.columbia.edu/kermit/ckermit70.html#x4 145. http://www.columbia.edu/kermit/ckermit70.html#x4.0 146. http://www.columbia.edu/kermit/ckermit70.html#x4.1 147. http://www.columbia.edu/kermit/ckermit70.html#x4.1.1 148. http://www.columbia.edu/kermit/ckermit70.html#x4.1.2 149. http://www.columbia.edu/kermit/ckermit70.html#x4.1.3 150. http://www.columbia.edu/kermit/ckermit70.html#x4.2 151. http://www.columbia.edu/kermit/ckermit70.html#x4.2.1 152. http://www.columbia.edu/kermit/ckermit70.html#x4.2.1.1 153. http://www.columbia.edu/kermit/ckermit70.html#x4.2.1.2 154. http://www.columbia.edu/kermit/ckermit70.html#x4.2.1.3 155. http://www.columbia.edu/kermit/ckermit70.html#x4.2.2 156. http://www.columbia.edu/kermit/ckermit70.html#x4.2.2.1 157. http://www.columbia.edu/kermit/ckermit70.html#x4.2.2.2 158. http://www.columbia.edu/kermit/ckermit70.html#x4.2.3 159. http://www.columbia.edu/kermit/ckermit70.html#x4.2.3.1 160. http://www.columbia.edu/kermit/ckermit70.html#x4.2.3.2 161. http://www.columbia.edu/kermit/ckermit70.html#x4.2.4 162. http://www.columbia.edu/kermit/ckermit70.html#x4.2.5 163. http://www.columbia.edu/kermit/ckermit70.html#x4.2.6 164. http://www.columbia.edu/kermit/ckermit70.html#x4.2.7 165. http://www.columbia.edu/kermit/ckermit70.html#x4.2.8 166. http://www.columbia.edu/kermit/ckermit70.html#x4.2.8.1 167. http://www.columbia.edu/kermit/ckermit70.html#x4.2.8.2 168. http://www.columbia.edu/kermit/ckermit70.html#x4.2.8.3 169. http://www.columbia.edu/kermit/ckermit70.html#x4.2.8.4 170. http://www.columbia.edu/kermit/ckermit70.html#x4.3 171. http://www.columbia.edu/kermit/ckermit70.html#x4.3.1 172. http://www.columbia.edu/kermit/ckermit70.html#x4.3.2 173. http://www.columbia.edu/kermit/ckermit70.html#x4.3.3 174. http://www.columbia.edu/kermit/ckermit70.html#x4.3.4 175. http://www.columbia.edu/kermit/ckermit70.html#x4.4 176. http://www.columbia.edu/kermit/ckermit70.html#x4.4.1 177. http://www.columbia.edu/kermit/ckermit70.html#x4.4.1.1 178. http://www.columbia.edu/kermit/ckermit70.html#x4.4.1.2 179. http://www.columbia.edu/kermit/ckermit70.html#x4.4.2 180. http://www.columbia.edu/kermit/ckermit70.html#x4.4.2.1 181. http://www.columbia.edu/kermit/ckermit70.html#x4.4.2.1.1 182. http://www.columbia.edu/kermit/ckermit70.html#x4.4.2.1.2 183. http://www.columbia.edu/kermit/ckermit70.html#x4.4.2.2 184. http://www.columbia.edu/kermit/ckermit70.html#x4.5 185. http://www.columbia.edu/kermit/ckermit70.html#x4.5.1 186. http://www.columbia.edu/kermit/ckermit70.html#x4.5.2 187. http://www.columbia.edu/kermit/ckermit70.html#x4.5.2.1 188. http://www.columbia.edu/kermit/ckermit70.html#x4.5.2.2 189. http://www.columbia.edu/kermit/ckermit70.html#x4.5.3 190. http://www.columbia.edu/kermit/ckermit70.html#x4.5.4 191. http://www.columbia.edu/kermit/ckermit70.html#x4.6 192. http://www.columbia.edu/kermit/ckermit70.html#x4.7 193. http://www.columbia.edu/kermit/ckermit70.html#x4.7.1 194. http://www.columbia.edu/kermit/ckermit70.html#x4.7.2 195. http://www.columbia.edu/kermit/ckermit70.html#x4.7.3 196. http://www.columbia.edu/kermit/ckermit70.html#x4.8 197. http://www.columbia.edu/kermit/ckermit70.html#x4.8.1 198. http://www.columbia.edu/kermit/ckermit70.html#x4.8.2 199. http://www.columbia.edu/kermit/ckermit70.html#x4.9 200. http://www.columbia.edu/kermit/ckermit70.html#x4.9.1 201. http://www.columbia.edu/kermit/ckermit70.html#x4.9.2 202. http://www.columbia.edu/kermit/ckermit70.html#x4.9.3 203. http://www.columbia.edu/kermit/ckermit70.html#x4.10 204. http://www.columbia.edu/kermit/ckermit70.html#x4.11 205. http://www.columbia.edu/kermit/ckermit70.html#x4.11.1 206. http://www.columbia.edu/kermit/ckermit70.html#x4.11.2 207. http://www.columbia.edu/kermit/ckermit70.html#x4.11.3 208. http://www.columbia.edu/kermit/ckermit70.html#x4.11.4 209. http://www.columbia.edu/kermit/ckermit70.html#x4.11.5 210. http://www.columbia.edu/kermit/ckermit70.html#x4.11.6 211. http://www.columbia.edu/kermit/ckermit70.html#x4.12 212. http://www.columbia.edu/kermit/ckermit70.html#x4.13 213. http://www.columbia.edu/kermit/ckermit70.html#x4.14 214. http://www.columbia.edu/kermit/ckermit70.html#x4.15 215. http://www.columbia.edu/kermit/ckermit70.html#x4.16 216. http://www.columbia.edu/kermit/ckermit70.html#x4.17 217. http://www.columbia.edu/kermit/ckermit70.html#x4.17.1 218. http://www.columbia.edu/kermit/ckermit70.html#x4.17.2 219. http://www.columbia.edu/kermit/ckermit70.html#x4.18 220. http://www.columbia.edu/kermit/ckermit70.html#x4.19 221. http://www.columbia.edu/kermit/ckermit70.html#x4.20 222. http://www.columbia.edu/kermit/ckermit70.html#x4.20.1 223. http://www.columbia.edu/kermit/ckermit70.html#x4.20.2 224. http://www.columbia.edu/kermit/ckermit70.html#x4.20.2.1 225. http://www.columbia.edu/kermit/ckermit70.html#x4.20.2.2 226. http://www.columbia.edu/kermit/ckermit70.html#x4.20.2.3 227. http://www.columbia.edu/kermit/ckermit70.html#x4.20.2.4 228. http://www.columbia.edu/kermit/ckermit70.html#x4.20.2.5 229. http://www.columbia.edu/kermit/ckermit70.html#x4.20.3 230. http://www.columbia.edu/kermit/ckermit70.html#x4.21 231. http://www.columbia.edu/kermit/ckermit70.html#x4.22 232. http://www.columbia.edu/kermit/ckermit70.html#x4.22.1 233. http://www.columbia.edu/kermit/ckermit70.html#x4.22.2 234. http://www.columbia.edu/kermit/ckermit70.html#x4.22.3 235. http://www.columbia.edu/kermit/ckermit70.html#x4.22.4 236. http://www.columbia.edu/kermit/ckermit70.html#x4.22.5 237. http://www.columbia.edu/kermit/ckermit70.html#x4.22.6 238. http://www.columbia.edu/kermit/ckermit70.html#x4.22.7 239. http://www.columbia.edu/kermit/ckermit70.html#x4.22.8 240. http://www.columbia.edu/kermit/ckermit70.html#x4.23 241. http://www.columbia.edu/kermit/ckermit70.html#x4.24 242. http://www.columbia.edu/kermit/ckermit70.html#x4.25 243. http://www.columbia.edu/kermit/ckermit70.html#x5 244. http://www.columbia.edu/kermit/ckermit70.html#x5.0 245. http://www.columbia.edu/kermit/ckermit70.html#x5.1 246. http://www.columbia.edu/kermit/ckermit70.html#x5.2 247. http://www.columbia.edu/kermit/ckermit70.html#x5.3 248. http://www.columbia.edu/kermit/ckermit70.html#x5.3.1 249. http://www.columbia.edu/kermit/ckermit70.html#x5.3.2 250. http://www.columbia.edu/kermit/ckermit70.html#x5.4 251. http://www.columbia.edu/kermit/ckermit70.html#x5.5 252. http://www.columbia.edu/kermit/ckermit70.html#x5.6 253. http://www.columbia.edu/kermit/ckermit70.html#x5.7 254. http://www.columbia.edu/kermit/ckermit70.html#x6 255. http://www.columbia.edu/kermit/ckermit70.html#x6.0 256. http://www.columbia.edu/kermit/ckermit70.html#x6.1 257. http://www.columbia.edu/kermit/ckermit70.html#x6.2 258. http://www.columbia.edu/kermit/ckermit70.html#x6.3 259. http://www.columbia.edu/kermit/ckermit70.html#x6.4 260. http://www.columbia.edu/kermit/ckermit70.html#x6.5 261. http://www.columbia.edu/kermit/ckermit70.html#x6.6 262. http://www.columbia.edu/kermit/ckermit70.html#x6.6.1 263. http://www.columbia.edu/kermit/ckermit70.html#x6.6.2 264. http://www.columbia.edu/kermit/ckermit70.html#x6.6.2 265. http://www.columbia.edu/kermit/ckermit70.html#x6.6.3 266. http://www.columbia.edu/kermit/ckermit70.html#x6.6.4 267. http://www.columbia.edu/kermit/ckermit70.html#x6.6.5 268. http://www.columbia.edu/kermit/ckermit70.html#x6.6.5.1 269. http://www.columbia.edu/kermit/ckermit70.html#x6.6.5.2 270. http://www.columbia.edu/kermit/ckermit70.html#x6.6.5.3 271. http://www.columbia.edu/kermit/ckermit70.html#x6.6.5.4 272. http://www.columbia.edu/kermit/ckermit70.html#x6.6.5.5 273. http://www.columbia.edu/kermit/ckermit70.html#x6.7 274. http://www.columbia.edu/kermit/ckermit70.html#x7 275. http://www.columbia.edu/kermit/ckermit70.html#x7.0 276. http://www.columbia.edu/kermit/ckermit70.html#x7.1 277. http://www.columbia.edu/kermit/ckermit70.html#x7.1.1 278. http://www.columbia.edu/kermit/ckermit70.html#x7.1.2 279. http://www.columbia.edu/kermit/ckermit70.html#x7.1.3 280. http://www.columbia.edu/kermit/ckermit70.html#x7.1.4 281. http://www.columbia.edu/kermit/ckermit70.html#x7.2 282. http://www.columbia.edu/kermit/ckermit70.html#x7.3 283. http://www.columbia.edu/kermit/ckermit70.html#x7.4 284. http://www.columbia.edu/kermit/ckermit70.html#x7.5 285. http://www.columbia.edu/kermit/ckermit70.html#x7.6 286. http://www.columbia.edu/kermit/ckermit70.html#x7.7 287. http://www.columbia.edu/kermit/ckermit70.html#x7.8 288. http://www.columbia.edu/kermit/ckermit70.html#x7.9 289. http://www.columbia.edu/kermit/ckermit70.html#x7.9.1 290. http://www.columbia.edu/kermit/ckermit70.html#x7.9.2 291. http://www.columbia.edu/kermit/ckermit70.html#x7.10 292. http://www.columbia.edu/kermit/ckermit70.html#x7.10.1 293. http://www.columbia.edu/kermit/ckermit70.html#x7.10.2 294. http://www.columbia.edu/kermit/ckermit70.html#x7.10.3 295. http://www.columbia.edu/kermit/ckermit70.html#x7.10.4 296. http://www.columbia.edu/kermit/ckermit70.html#x7.10.5 297. http://www.columbia.edu/kermit/ckermit70.html#x7.10.6 298. http://www.columbia.edu/kermit/ckermit70.html#x7.10.7 299. http://www.columbia.edu/kermit/ckermit70.html#x7.10.8 300. http://www.columbia.edu/kermit/ckermit70.html#x7.10.9 301. http://www.columbia.edu/kermit/ckermit70.html#x7.10.10 302. http://www.columbia.edu/kermit/ckermit70.html#x7.11 303. http://www.columbia.edu/kermit/ckermit70.html#x7.12 304. http://www.columbia.edu/kermit/ckermit70.html#x7.13 305. http://www.columbia.edu/kermit/ckermit70.html#x7.14 306. http://www.columbia.edu/kermit/ckermit70.html#x7.15 307. http://www.columbia.edu/kermit/ckermit70.html#x7.16 308. http://www.columbia.edu/kermit/ckermit70.html#x7.17 309. http://www.columbia.edu/kermit/ckermit70.html#x7.18 310. http://www.columbia.edu/kermit/ckermit70.html#x7.19 311. http://www.columbia.edu/kermit/ckermit70.html#x7.20 312. http://www.columbia.edu/kermit/ckermit70.html#x7.20.1 313. http://www.columbia.edu/kermit/ckermit70.html#x7.20.2 314. http://www.columbia.edu/kermit/ckermit70.html#x7.21 315. http://www.columbia.edu/kermit/ckermit70.html#x7.22 316. http://www.columbia.edu/kermit/ckermit70.html#x7.23 317. http://www.columbia.edu/kermit/ckermit70.html#x7.24 318. http://www.columbia.edu/kermit/ckermit70.html#x7.25 319. http://www.columbia.edu/kermit/ckermit70.html#x7.26 320. http://www.columbia.edu/kermit/ckermit70.html#x7.26.1 321. http://www.columbia.edu/kermit/ckermit70.html#x7.26.2 322. http://www.columbia.edu/kermit/ckermit70.html#x7.27 323. http://www.columbia.edu/kermit/ckermit70.html#x8 324. http://www.columbia.edu/kermit/ckermit70.html#x9 325. http://www.columbia.edu/kermit/ckermit70.html#x9.0 326. http://www.columbia.edu/kermit/ckermit70.html#x9.1 327. http://www.columbia.edu/kermit/ckermit70.html#x9.2 328. http://www.columbia.edu/kermit/ckermit70.html#x9.3 329. http://www.columbia.edu/kermit/ckermit70.html#x10 330. http://www.columbia.edu/kermit/ckermit70.html#xiii 331. http://www.columbia.edu/kermit/ckermit70.html#xiii.1 332. http://www.columbia.edu/kermit/ckermit70.html#xiii.1.1 333. http://www.columbia.edu/kermit/ckermit70.html#xiii.1.2 334. http://www.columbia.edu/kermit/ckermit70.html#xiii.1.2.1 335. http://www.columbia.edu/kermit/ckermit70.html#xiii.1.2.2 336. http://www.columbia.edu/kermit/ckermit70.html#xiii.1.2.3 337. http://www.columbia.edu/kermit/ckermit70.html#xiii.2 338. http://www.columbia.edu/kermit/ckermit70.html#xiv 339. http://www.columbia.edu/kermit/ckermit70.html#xv 340. http://www.columbia.edu/kermit/ckb2.htm 341. http://www.columbia.edu/kermit/ckbreviews.html 342. http://www.bhusa.com/ 343. http://www.columbia.edu/kermit/manuals.html#ckde 344. http://www.columbia.edu/kermit/manuals.html#ktb 345. http://www.columbia.edu/kermit/news.html 346. news:comp.protocols.kermit.announce 347. news:comp.protocols.kermit.misc 348. http://www.columbia.edu/kermit/ckb2.htm 349. http://www.columbia.edu/kermit/ckermit70.html#x4 350. http://www.columbia.edu/kermit/ckermit70.html#x4.3 351. http://www.columbia.edu/kermit/ckermit70.html#x4.23 352. http://www.columbia.edu/kermit/ckermit70.html#x4.5.1 353. http://www.columbia.edu/kermit/ckermit70.html#x1.5 354. http://www.columbia.edu/kermit/ckermit70.html#x4.7.1 355. http://www.columbia.edu/kermit/ckermit70.html#x4.9. 356. http://www.columbia.edu/kermit/ckb2.htm 357. http://www.columbia.edu/kermit/ckermit70.html#x7.9.2 358. http://www.columbia.edu/kermit/ckermit70.html#x2.15 359. http://www.columbia.edu/kermit/ckermit70.html#x9.1 360. http://www.columbia.edu/kermit/ckermit70.html#x1.6 361. http://www.columbia.edu/kermit/ckermit70.html#x7.4 362. http://www.columbia.edu/kermit/ckermit70.html#x4.9.1 363. http://www.columbia.edu/kermit/ckermit70.html#mjd 364. http://www.columbia.edu/kermit/ckermit70.html#mjd 365. http://www.columbia.edu/kermit/ckermit70.html#x4.9 366. http://www.columbia.edu/kermit/ckb2.htm 367. http://www.columbia.edu/kermit/ckb2.htm 368. http://www.columbia.edu/kermit/ckermit70.html#x7.5 369. http://www.columbia.edu/kermit/ckermit70.html#x2.12 370. http://www.columbia.edu/kermit/ckermit70.html#x1.5 371. http://www.columbia.edu/kermit/ckermit70.html#x4.9.1 372. http://www.columbia.edu/kermit/ckermit70.html#x6.6.5 373. http://www.columbia.edu/kermit/ckermit70.html#x4.9 374. http://www.columbia.edu/kermit/ckermit70.html#x7.18 375. http://www.columbia.edu/kermit/ckermit70.html#x7.4 376. http://www.columbia.edu/kermit/ckermit70.html#x1.15 377. http://www.columbia.edu/kermit/ckermit70.html#x4.3 378. http://www.columbia.edu/kermit/ckermit70.html#x7.3 379. http://www.columbia.edu/kermit/ckermit70.html#x7.10.7 380. http://www.columbia.edu/kermit/ckermit70.html#x7.1 381. http://www.columbia.edu/kermit/ckermit70.html#x4.9.1 382. ftp://kermit.columbia.edu/kermit/f/ckccfg.txt 383. ftp://kermit.columbia.edu/kermit/f/ckccfg.txt 384. http://www.columbia.edu/kermit/ckermit70.html#x1.22.4 385. http://www.columbia.edu/kermit/ckermit70.html#x1.22.5 386. http://www.columbia.edu/kermit/ckb2.htm 387. http://www.columbia.edu/kermit/ckermit70.html#x1.22.5 388. http://www.columbia.edu/kermit/ckermit70.html#x7.12 389. http://www.columbia.edu/kermit/ckermit70.html#x2.1.16 390. http://www.columbia.edu/kermit/ckermit70.html#x2.7 391. http://www.columbia.edu/kermit/ckermit70.html#x2.3.5 392. http://www.columbia.edu/kermit/ckermit70.html#x7.5 393. http://www.telefonica.es/cambiodenumeracion/ 394. http://www.columbia.edu/kermit/ckermit70.html#x7.5 395. http://www.columbia.edu/kermit/ckb2.htm 396. http://www.columbia.edu/kermit/ckermit70.html#x2.2.2 397. http://www.columbia.edu/kermit/ckermit70.html#x2.1.11 398. http://www.columbia.edu/kermit/ckermit70.html#x2.1.13 399. http://www.columbia.edu/kermit/ckermit70.html#x2.1.12 400. http://www.columbia.edu/kermit/ckb2.htm 401. http://www.columbia.edu/kermit/ckermit70.html#x2.1.1 402. http://www.columbia.edu/kermit/ckb2.htm 403. http://www.columbia.edu/kermit/ckb2.htm 404. http://www.columbia.edu/kermit/ckermit70.html#x2.1.7 405. http://www.columbia.edu/kermit/ckermit70.html#x2.1.6 406. http://www.columbia.edu/kermit/ckb2.htm 407. ftp://kermit.columbia.edu/kermit/f/telnet.txt 408. http://www.columbia.edu/kermit/telnet.htm 409. ftp://kermit.columbia.edu/kermit/f/telnet.txt 410. http://www.columbia.edu/kermit/telnet.htm 411. ftp://ftp.isi.edu/in-notes/rfc1572.txt 412. ftp://ftp.isi.edu/in-notes/rfc779.txt 413. http://www.columbia.edu/kermit/ckb2.htm 414. http://www.columbia.edu/kermit/ckermit70.html#x2.10 415. http://www.columbia.edu/kermit/ckermit70.html#x2.8 416. http://www.columbia.edu/kermit/ckermit70.html#x1.5 417. http://www.columbia.edu/kermit/ckermit70.html#x4.20 418. http://www.psy.uq.oz.au/~ftp/Crypto/ 419. http://www.columbia.edu/kermit/security.htm 420. http://srp.stanford.edu/srp/ 421. http://www.columbia.edu/kermit/ckermit70.html#x2.7.1, 422. ftp://kermit.columbia.edu/kermit/f/ckccfg.txt 423. http://www.columbia.edu/kermit/security.htm 424. http://www.columbia.edu/kermit/ckb2.htm 425. http://www.columbia.edu/kermit/ckermit70.html#x2.7 426. http://www.columbia.edu/kermit/ckermit70.html#x2.0 427. ftp://kermit.columbia.edu/kermit/f/ckuins.txt 428. ftp://kermit.columbia.edu/kermit/f/ckubwr.txt 429. ftp://kermit.columbia.edu/kermit/f/ckuins.txt 430. http://www.columbia.edu/kermit/iksd.html#x4.2 431. http://www.columbia.edu/kermit/iksd.html 432. http://www.columbia.edu/kermit/ckermit70.html#x4.2.8.1 433. ftp://ftp.isi.edu/in-notes/rfc1945.txt 434. http://www.columbia.edu/kermit/ckermit70.html#x1.5 435. http://www.columbia.edu/kermit/ckermit70.html#x3.2 436. http://www.columbia.edu/kermit/ckermit70.html#x3.2 437. http://www.columbia.edu/kermit/ckb2.htm 438. http://www.columbia.edu/kermit/ckb2.htm 439. http://www.columbia.edu/kermit/ckermit70.html#x5.4 440. ftp://kermit.columbia.edu/kermit/f/ckubwr.txt 441. http://www.columbia.edu/kermit/ckermit70.html#x4.10 442. http://www.columbia.edu/kermit/ckermit70.html#x4.7.1 443. http://www.columbia.edu/kermit/ckermit70.html#x4.7.3 444. http://www.columbia.edu/kermit/ckermit70.html#x4.3 445. http://www.columbia.edu/kermit/ckermit70.html#x4.10 446. http://www.columbia.edu/kermit/ckermit70.html#x4.11 447. http://www.columbia.edu/kermit/ckermit70.html#x4.15 448. http://www.columbia.edu/kermit/ckermit70.html#x4.2.4 449. http://www.columbia.edu/kermit/ckermit70.html#x4.7 450. http://www.columbia.edu/kermit/ckermit70.html#x4.2.3 451. http://www.columbia.edu/kermit/ckermit70.html#x4.2.1.3 452. http://www.columbia.edu/kermit/ckb2.htm 453. http://www.columbia.edu/kermit/ckermit70.html#x4.2.2 454. http://www.columbia.edu/kermit/ckermit70.html#x1.5 455. http://www.columbia.edu/kermit/ckermit70.html#x4.2.8.2 456. http://www.columbia.edu/kermit/ckermit70.html#x4.3 457. http://www.columbia.edu/kermit/ckermit70.html#x4.10 458. http://www.columbia.edu/kermit/ckermit70.html#x4.11 459. http://www.columbia.edu/kermit/ckermit70.html#x4.15 460. http://www.telstra.com.au/docs/PGP/ 461. http://www.telstra.com.au/docs/PGP/pgpdoc2/pgpdoc2_17.html 462. http://www.columbia.edu/kermit/security.htm 463. http://www.columbia.edu/kermit/ckermit70.html#x2.7 464. http://www.columbia.edu/kermit/ckb2.htm 465. http://www.columbia.edu/kermit/ckermit70.html#x2.14 466. http://www.columbia.edu/kermit/ckermit70.html#x1.23 467. http://www.columbia.edu/kermit/ckermit70.html#x4.7 468. http://www.columbia.edu/kermit/ckb2.htm 469. http://www.columbia.edu/kermit/ckb2.htm 470. http://www.columbia.edu/kermit/ckermit70.html#x4.9 471. http://www.columbia.edu/kermit/ckb2.htm 472. http://www.columbia.edu/kermit/ckermit70.html#x1.5.4 473. http://www.columbia.edu/kermit/ckermit70.html#x4.3 474. http://www.columbia.edu/kermit/ckermit70.html#x1.5.5 475. http://www.columbia.edu/kermit/ckermit70.html#x7.5 476. http://www.columbia.edu/kermit/ckermit70.html#x4.9 477. http://www.columbia.edu/kermit/ckermit70.html#x1.5.4 478. http://www.columbia.edu/kermit/ckermit70.html#x4.9 479. http://www.columbia.edu/kermit/ckermit70.html#x1.5.4 480. http://www.columbia.edu/kermit/ckermit70.html#x1.5.5 481. http://www.columbia.edu/kermit/ckb2.htm 482. http://www.columbia.edu/kermit/ckb2.htm 483. http://www.columbia.edu/kermit/ckermit70.html#x1.5 484. http://www.columbia.edu/kermit/ckermit70.html#x1.6 485. http://www.columbia.edu/kermit/ckermit70.html#x7.10 486. http://www.columbia.edu/kermit/ckermit70.html#x7.10.11 487. http://www.columbia.edu/kermit/ckermit70.html#x1.6 488. http://www.columbia.edu/kermit/ckermit70.html#x4.2.2 489. http://www.columbia.edu/kermit/ckermit70.html#x4.11 490. http://www.columbia.edu/kermit/ckermit70.html#x1.5.4 491. http://www.columbia.edu/kermit/ckermit70.html#x4.9.1 492. http://www.columbia.edu/kermit/ckermit70.html#x4.0.6 493. http://www.columbia.edu/kermit/ckermit70.html#x4.2 494. http://www.columbia.edu/kermit/ckermit70.html#x4.1 495. http://www.columbia.edu/kermit/ckermit70.html#x4.7.1 496. http://www.columbia.edu/kermit/ckb2.htm 497. http://www.columbia.edu/kermit/ckermit70.html#x1.5 498. http://www.columbia.edu/kermit/ckermit70.html#x4.2.2 499. http://www.columbia.edu/kermit/ckermit70.html#x4.7.1 500. http://www.columbia.edu/kermit/ckermit70.html#x4.2 501. http://www.columbia.edu/kermit/ckermit70.html#x4.10 502. http://www.columbia.edu/kermit/ckermit70.html#x4.2.2 503. http://www.columbia.edu/kermit/ckermit70.html#x4.7.1 504. http://www.columbia.edu/kermit/ckermit70.html#x4.2 505. http://www.columbia.edu/kermit/ckermit70.html#x4.10 506. http://www.columbia.edu/kermit/ckermit70.html#x1.11.5 507. http://www.columbia.edu/kermit/ckermit70.html#x4.0.6 508. http://www.columbia.edu/kermit/ckermit70.html#x4.11 509. http://www.columbia.edu/kermit/ckermit70.html#x4.9.1 510. http://www.columbia.edu/kermit/ckermit70.html#x4.11.3 511. http://www.columbia.edu/kermit/ckermit70.html#x4.9 512. http://www.columbia.edu/kermit/ckermit70.html#x4.9 513. http://www.columbia.edu/kermit/ckermit70.html#x4.5.1 514. http://www.columbia.edu/kermit/ckermit70.html#x7.10 515. http://www.columbia.edu/kermit/ckermit70.html#x7.10.5 516. http://www.columbia.edu/kermit/ckermit70.html#x7.10.3 517. http://www.columbia.edu/kermit/ckermit70.html#x7.10.5 518. http://www.columbia.edu/kermit/ckb2.htm 519. http://www.columbia.edu/kermit/ckermit70.html#x4.3 520. http://www.columbia.edu/kermit/ckermit70.html#x4.10 521. http://www.columbia.edu/kermit/ckermit70.html#x4.3 522. http://www.columbia.edu/kermit/ckermit70.html#x4.10 523. http://www.columbia.edu/kermit/ckermit70.html#x4.15 524. http://www.columbia.edu/kermit/ckermit70.html#x4.18 525. http://www.columbia.edu/kermit/ckermit70.html#x4.20 526. http://www.columbia.edu/kermit/ckermit70.html#x4.20 527. http://www.columbia.edu/kermit/ckermit70.html#x4.20 528. http://www.columbia.edu/kermit/ckermit70.html#x4.19 529. http://www.columbia.edu/kermit/ckermit70.html#x4.16 530. http://www.columbia.edu/kermit/ckermit70.html#x4.19 531. http://www.columbia.edu/kermit/ckermit70.html#x4.20.2.3 532. http://www.columbia.edu/kermit/ckermit70.html#x1.5 533. http://www.columbia.edu/kermit/ckermit70.html#x6.6.5.4 534. http://www.columbia.edu/kermit/ckermit70.html#x4.22.2 535. http://www.columbia.edu/kermit/ckermit70.html#x4.22.3 536. http://www.columbia.edu/kermit/ckb2.htm 537. http://www.columbia.edu/kermit/ckb2.htm 538. http://www.columbia.edu/kermit/ckermit70.html#x9.3 539. http://www.columbia.edu/kermit/ckermit70.html#x5.2.1 540. http://www.columbia.edu/kermit/ckermit70.html#x4.5.1 541. http://www.columbia.edu/kermit/ckermit70.html#x4.5.2 542. http://www.columbia.edu/kermit/ckermit70.html#x6.6 543. http://www.columbia.edu/kermit/ckermit70.html#xiii 544. http://www.columbia.edu/kermit/ckermit70.html#xiii 545. ftp://ftp.isi.edu/in-notes/rfc1489.txt 546. ftp://ftp.isi.edu/in-notes/rfc2319.txt 547. http://www.unicode.org/ 548. http://www.columbia.edu/kermit/ckermit70.html#x6.6.2 549. http://www.columbia.edu/kermit/ckermit70.html#x6.6.5.1 550. ftp://ftp.isi.edu/in-notes/rfc2640.txt 551. http://www.columbia.edu/kermit/ckermit70.html#x6.6.2 552. http://www.columbia.edu/kermit/ckermit70.html#x6.0 553. http://www.columbia.edu/kermit/ckermit70.html#x6.5 554. http://www.columbia.edu/kermit/ckermit70.html#x6.4 555. http://www.columbia.edu/kermit/ckb2.htm 556. http://www.columbia.edu/kermit/ckermit70.html#x4.21 557. http://www.columbia.edu/kermit/ckermit70.html#x6.5 558. http://www.columbia.edu/kermit/ckermit70.html#x2.8 559. http://www.columbia.edu/kermit/ckermit70.html#x7.7 560. http://www.columbia.edu/kermit/ckermit70.html#x7.2 561. http://www.columbia.edu/kermit/ckermit70.html#x1.19 562. http://www.columbia.edu/kermit/ckermit70.html#x4.9 563. http://www.columbia.edu/kermit/ckermit70.html#x4.1 564. http://www.columbia.edu/kermit/ckermit70.html#x4.2 565. http://www.columbia.edu/kermit/ckermit70.html#x4.1 566. http://www.columbia.edu/kermit/ckermit70.html#x4.2 567. http://www.columbia.edu/kermit/ckermit70.html#x2.1.11 568. http://www.columbia.edu/kermit/ckermit70.html#x2.10 569. http://www.columbia.edu/kermit/ckermit70.html#ferrstring 570. http://www.columbia.edu/kermit/ckermit70.html#x4.2.5 571. http://www.columbia.edu/kermit/ckermit70.html#x2.1.10 572. http://www.columbia.edu/kermit/ckermit70.html#x9.1 573. http://www.columbia.edu/kermit/ckermit70.html#x7.23 574. http://www.columbia.edu/kermit/ckermit70.html#x7.23 575. http://www.columbia.edu/kermit/ckermit70.html#x1.22 576. http://www.columbia.edu/kermit/ckermit70.html#x1.6 577. http://www.columbia.edu/kermit/ckermit70.html#x7.23 578. http://www.columbia.edu/kermit/ckermit70.html#x7.24 579. http://www.columbia.edu/kermit/ckermit70.html#x7.24 580. http://www.columbia.edu/kermit/ckermit70.html#x4.2.3 581. http://www.columbia.edu/kermit/ck90.html 582. http://www.columbia.edu/kermit/ckermit70.html#x7.12 583. http://www.columbia.edu/kermit/ckermit70.html#x7.9 584. http://www.columbia.edu/kermit/ckb2.htm 585. http://www.columbia.edu/kermit/ckermit70.html#x4.11.3 586. http://www.columbia.edu/kermit/ckermit70.html#x4.11.3 587. http://www.columbia.edu/kermit/ckermit70.html#x7.5 588. http://www.columbia.edu/kermit/ckermit70.html#x7.10.7 589. http://www.columbia.edu/kermit/ckermit70.html#x7.10.7 590. http://www.columbia.edu/kermit/ckermit70.html#x4.2.8.4 591. http://www.columbia.edu/kermit/ckermit70.html#x4.2.5 592. http://www.columbia.edu/kermit/ckermit70.html#x7.8 593. http://www.columbia.edu/kermit/ckermit70.html#x4.9.1 594. http://www.columbia.edu/kermit/ckb2.htm 595. http://www.columbia.edu/kermit/ckermit70.html#x7.19 596. http://www.columbia.edu/kermit/ckermit70.html#x7.16 597. http://www.columbia.edu/kermit/ckermit70.html#x7.9.1 598. http://www.columbia.edu/kermit/ckermit70.html#x7.5 599. http://www.columbia.edu/kermit/ckermit70.html#x7.3 600. http://www.columbia.edu/kermit/ckermit70.html#x4.11.3 601. http://www.columbia.edu/kermit/ckermit70.html#x4.5.1 602. http://www.columbia.edu/kermit/ckermit70.html#x7.10 603. http://www.columbia.edu/kermit/ckermit70.html#x7.10.10 604. http://www.columbia.edu/kermit/ckermit70.html#x1.5 605. http://www.columbia.edu/kermit/ckermit70.html#x7.23 606. http://www.columbia.edu/kermit/ckermit70.html#x7.10.7 607. http://www.columbia.edu/kermit/ckermit70.html#x7.10.7 608. http://www.columbia.edu/kermit/ckermit70.html#x4.11.3 609. http://www.columbia.edu/kermit/ckermit70.html#x7.10.11 610. http://www.columbia.edu/kermit/ckermit70.html#x4.9 611. http://www.columbia.edu/kermit/ckermit70.html#x7.10.3 612. http://www.columbia.edu/kermit/ckermit70.html#x7.3 613. http://www.columbia.edu/kermit/ckermit70.html#x7.9.2 614. http://www.columbia.edu/kermit/ckb2.htm 615. http://www.columbia.edu/kermit/ckermit70.html#x4.17.2 616. http://www.columbia.edu/kermit/ckermit70.html#x1.22 617. http://www.columbia.edu/kermit/ckermit70.html#x4.7.1 618. http://www.columbia.edu/kermit/ckermit70.html#x1.22 619. http://www.columbia.edu/kermit/ckermit70.html#x4.3 620. http://www.columbia.edu/kermit/ckermit70.html#x4.9 621. http://www.columbia.edu/kermit/ckermit70.html#x1.22 622. http://www.columbia.edu/kermit/ckermit70.html#x1.6 623. http://www.columbia.edu/kermit/ckermit70.html#x7.5 624. http://www.columbia.edu/kermit/ckermit70.html#x7.5 625. http://www.columbia.edu/kermit/ckermit70.html#x7.19 626. http://www.columbia.edu/kermit/ckermit70.html#x4.9.1 627. http://www.columbia.edu/kermit/ckermit70.html#x9.3 628. http://www.columbia.edu/kermit/ckermit70.html#x7.5 629. http://www.columbia.edu/kermit/ckb2.htm 630. http://www.columbia.edu/kermit/ckermit80.html 631. http://www.columbia.edu/kermit/ckermit80.html#x9 632. http://www.columbia.edu/kermit/ckermit70.html#x7.4 633. http://www.columbia.edu/kermit/ckermit80.html 634. http://www.columbia.edu/kermit/ckermit80.html#x9 635. http://www.columbia.edu/kermit/ckermit70.html#x7.20.2 636. http://www.columbia.edu/kermit/ckermit70.html#x7.10.5 637. http://www.columbia.edu/kermit/ckermit70.html#x7.10.9 638. http://www.columbia.edu/kermit/ckermit70.html#x1.10 639. http://www.columbia.edu/kermit/ckermit70.html#x1.5 640. http://www.columbia.edu/kermit/ckermit70.html#x4.7.1 641. http://www.columbia.edu/kermit/iksd.html 642. http://www.columbia.edu/kermit/ckermit70.html#x7.19 643. http://www.columbia.edu/kermit/ckermit70.html#x4.10 644. http://www.columbia.edu/kermit/ckermit70.html#x4.11 645. http://www.columbia.edu/kermit/ckermit70.html#x4.3 646. http://www.columbia.edu/kermit/gkermit.html 647. http://www.columbia.edu/kermit/ckermit70.html#x4.20 648. http://www.columbia.edu/kermit/gkermit.html 649. http://www.columbia.edu/kermit/ckb2.htm 650. http://www.columbia.edu/kermit/ckermit70.html#x7.3 651. mailto:kermit@columbia.edu 652. http://www.columbia.edu/kermit/ckermit70.html#top 653. http://www.columbia.edu/kermit/ckermit.html 654. http://www.columbia.edu/kermit/index.html ckermit80.txt0000664000015300001460000161414011620007602012233 0ustar fdckermit [1]The Columbia Crown The Kermit Project | Columbia University 612 West 115th Street, New York NY 10025 USA o [2]kermit@columbia.edu ...since 1981 [3]Home [4]Kermit 95 [5]C-Kermit [6]Scripts [7]Current [8]New [9]FAQ [10]Support C-Kermit 8.0 Update Notes [ [11]Contents ] [ [12]C-Kermit ] [ [13]Kermit Home ] Second Supplement to [14]Using C-Kermit, 2nd Edition For C-Kermit 8.0 As of C-Kermit version: 8.0.211 Date of C-Kermit release: 10 April 2003 This file last updated: Mon Sep 13 08:52:36 2010 * IF YOU ARE READING A PLAIN-TEXT version of this document, note that it is a plain-text dump of a Web page. You can visit the original (and possibly more up-to-date) Web page here: [15]http://www.columbia.edu/kermit/ckermit80.html * If you are reading the HTML version of this file with a GUI Web browser, the features added since C-Kermit 8.0.201 are shown in red if your browser and monitor permit. Features that were new to versions 8.0.200 and 201 are in black. Authors: Frank da Cruz and Christine M. Gianone Address: The Kermit Project Columbia University 612 West 115th Street New York NY 10025-7799 USA Fax: +1 (212) 662-6442 E-Mail: [16]kermit-support@columbia.edu Web: [17]http://www.columbia.edu/kermit/ Or: [18]http://www.kermit-project.org/ Or: [19]http://www.columbia.nyc.ny.us/kermit/ NOTICES This document: Copyright © 1997, 2002, Frank da Cruz and Christine M. Gianone. All rights reserved. Kermit 95: Copyright © 1995, 2002, Trustees of Columbia University in the City of New York. All rights reserved. C-Kermit: Copyright © 1985, 2002, Trustees of Columbia University in the City of New York. All rights reserved. See the C-Kermit [20]COPYING.TXT file or the copyright text in the [21]ckcmai.c module for disclaimer and permissions. When Kerberos(TM) and/or SRP(TM) (Secure Remote Password) and/or SSL/TLS protocol are included: Portions Copyright © 1990, Massachusetts Institute of Technology. Portions Copyright © 1991, 1993 Regents of the University of California. Portions Copyright © 1991, 1992, 1993, 1994, 1995 by AT&T. Portions Copyright © 1997, Stanford University. Portions Copyright © 1995-1997, Eric Young . For the full text of the third-party copyright notices, see [22]Appendix V. WHAT IS IN THIS FILE This file lists changes made to C-Kermit since version 7.0 was released in January 2000. Use this file as a supplement to: * The second edition of [23]Using C-Kermit; and: * The [24]C-Kermit 7.0 Update Notes. Also available in plain-text form as [25]ckermit70.txt. until the third edition of Using C-Kermit is published. We apologize for the scattered documentation and will consolidate it when we are able. ADDITIONAL FILES Several other files accompany this new Kermit release: [26]ckututor.html C-Kermit Tutorial (for Unix). Also distributed in Nroff form as [27]ckuker.nr, the Unix C-Kermit manual page. [28]security.htm Discussion of Kermit's new authentication and encryption features, updated for C-Kermit 8.0. [29]telnet.htm Detailed documentation of Kermit's Telnet client, updated for C-Kermit 8.0. [30]ftpscripts.html Tutorial: Writing FTP automation scripts [31]ckcbwr.html Platform-independent C-Kermit hints and tips. Also distributed in plain text form as [32]ckcbwr.txt [33]ckubwr.html Unix-specific C-Kermit hints and tips. Also distributed in plain text form as [34]ckubwr.txt. [35]ckvbwr.html VMS-specific C-Kermit hints and tips. Also distributed in plain text form as [36]ckvbwr.txt. [37]ckuins.html Unix C-Kermit installation instructions. Also distributed in plain text form as [38]ckuins.txt. [39]ckvins.html VMS C-Kermit installation instructions. Also distributed in plain text form as [40]ckvins.txt. [41]ckccfg.html Compile-time configuration options. Also distributed in plain text form as [42]ckccfg.txt. [43]ckcplm.html C-Kermit Program Logic Manual. Also distributed in plain text form as [44]ckcplm.txt. [45]iksd.html Internet Kermit Service Administrators Guide for Unix. [46]skermit.html C-Kermit as an SSH Subsystem (SFTP server replacement). [ [47]Top ] [ [48]C-Kermit ] [ [49]Kermit Home ] CONTENTS [50]0. WHAT'S NEW [51]1. FIXES SINCE VERSION 7.0.196 [52]2. SSH AND HTTP [53]2.1. SSH Connections [54]2.2. HTTP Connections [55]2.2.1. HTTP Command Switches [56]2.2.2. HTTP Action Commands [57]2.2.3. HTTP Headers [58]2.2.4. Secure HTTP Connections [59]2.2.5. HTTP Variables [60]2.2.6. The HTTP Command-Line Personality [61]3. THE BUILT-IN FTP CLIENT [62]3.1. Making and Managing FTP Connections [63]3.1.1. Kermit Command-Line Options for FTP [64]3.1.2. The FTP Command-Line Personality [65]3.1.3. The FTP URL Interpreter [66]3.1.4. Interactive FTP Session Establishment [67]3.2. Making Secure FTP Connections [68]3.3. Setting FTP Preferences [69]3.4. Managing Directories and Files [70]3.5. Uploading Files With FTP [71]3.5.1. FTP PUT Switches [72]3.5.2. Update Mode [73]3.5.3. Recovery [74]3.6. Downloading Files With FTP [75]3.6.1. FTP GET Switches [76]3.6.2. Filename Collisions [77]3.6.3. Recovery [78]3.7. Translating Character Sets [79]3.7.1. Character Sets and Uploading [80]3.7.2. Character Sets and Downloading [81]3.8. FTP Command Shortcuts [82]3.9. Dual Sessions [83]3.10. Automating FTP Sessions [84]3.10.1. FTP-Specific Variables and Functions [85]3.10.2. Examples [86]3.10.3. Automating Secure FTP Connections [87]3.11. Advanced FTP Protocol Features [88]4. FILE SCANNING [89]5. FILE AND DIRECTORY NAMES CONTAINING SPACES [90]6. OTHER COMMAND PARSING IMPROVEMENTS [91]6.1. Grouping Macro Arguments [92]6.2. Directory and File Name Completion [93]6.3. Passing Arguments to Command Files [94]6.4. More-Prompting [95]6.5. Commas in Macro Definitions [96]6.6. Arrow Keys [97]7. NEW COMMANDS AND SWITCHES [98]8. SCRIPTING IMPROVEMENTS [99]8.1. Performance and Debugging [100]8.2. Using Macros as Numeric Variables [101]8.3. New IF Conditions [102]8.4. The ON_UNKNOWN_COMMAND and ON_CD Macros [103]8.5. The SHOW MACRO Command [104]8.6. Arrays [105]8.7. New or Improved Built-in Variables and Functions [106]8.8. The RETURN and END Commands [107]8.9. UNDEFINing Groups of Variables [108]8.10. The INPUT and MINPUT Commands [109]8.11. Learned Scripts [110]8.12. Pattern Matching [111]8.13. Dates and Times [112]8.14. Trapping Keyboard Interruption [113]9. S-EXPRESSIONS [114]9.1. What is an S-Expression? [115]9.2. Integer and Floating-Point-Arithmetic [116]9.3. How to Use S-Expressions [117]9.4. Summary of Built-in Constants and Operators [118]9.5. Variables [119]9.6. Assignments and Scope [120]9.7. Conditional Expressions [121]9.8. Extensibility [122]9.9. Examples [123]9.10. Differences from Algebraic Notation [124]9.11.Differences from Lisp [125]10. FILE TRANSFER [126]11. MODEMS AND DIALING [127]12. TERMINAL CONNECTION [128]13. CHARACTER SETS [129]14. DIALOUT FROM TELNET TERMINAL SERVERS [130]15. COPING WITH BROKEN KERMIT PARTNERS [131]16. NEW COMMAND-LINE OPTIONS [132]17. LOGS [ [133]Top ] [ [134]C-Kermit ] [ [135]Kermit Home ] 0. WHAT'S NEW The Initialization and Customization Files C-Kermit 8.0 now supports specification of the initialization file name (path) in an environment variable, CKERMIT_INI. It also relies far less than before on the initialization for functioning. See [136]Section 5 of the Unix C-Kermit [137]installation instructions for details. As of version 8.0.201, C-Kermit also executes your customization file (if you have one) even if the initialization file was not found. Previously, the customization file was executed by a TAKE command in the initialization file (and it still is, if an initialization is found). Incompatible Changes As always, we do our best to avoid changes that break existing scripts. However, C-Kermit 8.0 does include a rather pervasive syntax change that might alter the behavior of scripts that depend on the previous behavior. As described in [138]Section 5, C-Kermit now accepts doublequotes in most contexts where you previously had to use braces to group multiple words into a single field, or to force inclusion of leading or trailing blanks. Most noticeably, in C-Kermit 7.0 and earlier: echo {this is a string} would print: this is a string whereas: echo "this is a string" printed: "this is a string" In C-Kermit 8.0, both print: this is a string To force the doublequotes to be treated as part of the string, use either of the following forms: echo {"this is a string"} echo ""this is a string"" Similarly, to force braces to be treated as part of the string: echo "{this is a string}" echo {{this is a string}} Other incompatibilities: 1. Using the SET HOST command to make HTTP connections is no longer supported. Instead, use the new HTTP OPEN command, described in [139]Section 2.2. C-Kermit 7.1 Alpha.01 (8 December 2000) Its major new features are those listed in the [140]Table of Contents: the FTP client, file scanning, command parsing and scripting improvements, S-Expressions, and support for the Telnet Com Port Option, plus wider availability of the Kerberos, SSL/TLS, and SRP security options for secure Internet connections. C-Kermit 7.1.199 Alpha.02 (4 January 2001) + C-Kermit now accepts [141]FTP, TELNET, and IKSD URLs as its first command-line argument. + Character-set translation added to the FTP client for [142]filenames. + Optional [143]setting of date of incoming files by FTP [M]GET from the server date. + [144]FTP CHECK filename added to let FTP client check the existence of a file on the server. + [145]FTP GET /NAMELIST:filename added to get list of server filenames into a local file. + [146]FTP [M]PUT /SERVER-RENAME:template added to make server rename a file as indicated by the template after it has arrived completely. + FTP [M]GET /SERVER-RENAME:template added to make server rename a file as indicated by the template after it has been sent completely. + FTP [147]VDIRECTORY added for getting verbose directory listings from TOPS-20. + [148]FTP TYPE TENEX added for transferring 8-bit binary files with PDP-10s. + Added [149]automatic text/binary mode switching for FTP [M]GET, based on filename patterns (e.g. *.zip, *.gz, *.exe are binary; *.txt, *.c are text). + [150]SET SEND I-PACKETS OFF added for coping with Kermit servers that do not support I packets. + A new option was added to [151]\fword() and \fsplit() for parsing comma-separated lists that might contain empty elements. + Bug fixes including: o {} or "" could not be used as expected to represent the empty string. o ,- on a line by itself in a macro definition caused subsequent statements to be skipped. o FTP [M]GET didn't work right if path segments were included in the filespec. o FTP MGET, if interrupted, did not clear its file list. o Various problems with FTP PUT /AS-NAME that nobody noticed. o Some FTP messages and displays interfered with each other. o Parsing of YESTERDAY, TODAY, and TOMORROW in date-time fields was broken. o Automatic old-to-new dialing directory format conversion was broken on VMS. o Various source-code portability problems fixed. + Improvement of various HELP and SHOW messages. C-Kermit 7.1.199 Alpha.04 (1 April 2001) + Big changes: o Changed default modem type from NONE to GENERIC. o Generic dialing now sends no init string at all. o Changed default terminal bytesize from 7 to 8. + New features: o SET SESSION-LOG TIMESTAMPED-TEXT for timestamped session log. + New modem types: o Conexant modem family o Lucent VENUS chipset o PCTel V.90 chipset o Zoom V.90 o Zoom V.92 + FTP client: o FTP OPEN /PASSIVE and /ACTIVE switches added. o Now works with servers that that don't include path in NLST response. o Fixed SEND /RECURSIVE not to follow symlinks (UNIX). o SET FTP VERBOSE-MODE default is now OFF instead of ON. + Kermit protocol: o Fixed what I hope is the last "Receive window full" error. o SET PREFIXING or SET CONTROL PREFIX now automatically sets CLEARCHANNEL OFF. o Fixed incorrect report of number of files transferred at end of transfer. o Fixed SEND /RECURSIVE not to follow symlinks (UNIX). + UNIX: o HTTP and shadow passwords enabled for SCO 5.0.6. o Even with SET FILENAMES CONVERTED, spaces were still accepted in incoming filenames; now they are converted to underscores. o Added support for compile-time mktemp()/mkstemp() selection. + VMS: o Session-log format for scripted sessions fixed. + Scripting: o Fixed \frdir() not to follow symlinks (UNIX). o Fixed \fday() not to dump core for dates prior to 17 Mar 1858. + General: o "Closing blah..." message upon exit could not be suppressed. o Added /PAGE and /NOPAGE to DELETE switches. o Added GO response for DELETE /ASK (delete all the rest without asking). o Added GO response to "more?" prompt (for multi-page screen output). o Updated HELP texts. C-Kermit 7.1.199 Beta.01 (10 May 2001) + FTP client verbosity adjustments. + Bug with generic modem dialing pausing several secs fixed. + SET HOST /USER:, SET LOGIN USERID, etc, fixed when given no user ID. + A couple \v(dm_blah) dial modifier variables added. + "--version" command-line switch added. + Fixed NetBSD serial-port DTR handling. + Lots of syntax cleanups for Flexelint and gcc -Wall. + Fixed modem-type aliases to not take precedence over real names. + Fixed funny treatment of doublequotes by ECHO command. + Enabled SET SESSION-LOG for VMS and other non-UNIX platforms. + Fixed changing direction in command history buffer. + Fixed handling of IKSD URLs. + Made sure DELETE prints a message if it got any errors. C-Kermit 8.0.200 Beta.02 (28 June 2001) + Major version number increased from 7 to 8. + [152]SSH command. + More-consistent Kermit protocol defaults. + CONNECT idle timeout and action selection. + CONNECT status variable. + A way to allocate more space for filename lists. + Pseudoterminal handler fixed for late-model Linuxes. + Command-line option -dd for timestamped debug log. + Download directory now works for external protocols too. + GREP /COUNT:variable. + SET ATTRIBUTE RECORD-FORMAT { OFF, ON }. + Bug fixes. C-Kermit 8.0.200 Beta.03 (9 Sep 2001) + [153]HTTP 1.1 connections and scripting + [154]ON_CTRLC macro for trapping Ctrl-C in scripts + [155]Date-time parsing improvements, timezones, comparison, arithmetic + [156]Pattern-matching improvements + FTP improvements + SET EXIT HANGUP { ON, OFF } + SET FILE EOF { CTRL-Z, LENGTH } + ASK[Q] /TIMEOUT + Bug fixes + New platforms C-Kermit 8.0.200 Beta.04 (16 Nov 2001) + [157]New Unix man page + [158]New Unix installation instructions + SET TELOPT policies are now enforced on non-Telnet ports if the server begins Telnet negotiations. + SET TERMINAL IDLE-ACTION { TELNET-NOP, TELNET-AYT }. + UUCP lockfile creation race condition fixed. + Dialout, modem signals, hangup, hardware flow control, etc, tested extensively on many platforms, numerous problems fixed. + Improved hints when dialing fails. + SET STOP-BITS 2 can now be given without SET FLOW HARDWARE. + Major improvements in RFC 2217 Telnet Com-Port Control. + Improved ability to REDIAL a modem server port. + kermit -h now shows the command name in the usage usage string. + kermit -h now shows ALL command-line options. + kermit -s blah, where blah is a symlink, now works. + --noperms command-line option = SET ATTRIBUTE PERMISSIONS OFF. + HTTP and HTTPS URLs now supported on the command line. + An http command-line personality is now available. + Initialization file streamlined to load faster, anachronisms removed. + Updated NEWS, INTRO, HELP text, SHOW commands. In particular, see SHOW COMM, HELP SET LINE, HELP WAIT. + Date/time arithmetic routines converted from floating-point to integer arithmetic (internally) for greater accuracy and portability. + Quoted strings containing commas no longer break macro execution. + Dynamic Kermit file-transfer timeouts are now much more aggressive. + New "hot keys" to turn debug.log on/off during file transfer. + Improved hints when file transfer fails. + FTP CD orientation messages are now printed. + -R now accepted on the FTP command line to request Recursion. + -m allows Active or Passive mode to be chosen on the FTP command line. + -dd on the FTP command line creates a timestamped debug.log. + FTP command-line security options filled in. + Improved automatic text/binary mode switching for MGET. + Removed spurious error messages that sometimes occur during MGET. + DIRECTORY, GREP, TYPE, HEAD, and TAIL now have a /OUTPUT:file option. + TYPE /NUMBER adds line numbers. + CAT = TYPE /NOPAGE; MORE = TYPE /PAGE. + GETOK ?-help fixed. + \v(timestamp) (= "\v(ndate) \v(time)") + \v(hour) (hour of the day, 0-23) + \funix2dospath() converts a UNIX path (/) to a DOS one (\). + \fdos2unixpath() converts a DOS (Windows, OS/2) path to a UNIX one. + \fkeywordval() parses name=value pair, allows macro keyword parameters. + We now make every attempt to not write passwords to the debug.log. + New Certificate Authority certificates file, includes the Kermit Project at Columbia University so you can access our IKSD securely. + Secure targets improved and better documented in Unix makefile. + All Linux (libc and glibc) builds consolidated under "make linux". + HP-UX makefile targets now have consistent names. + New aix50 and aix51 targets added. C-Kermit 8.0.200 Final (12 Dec 2001) + Remote/local-mode confusion on some platforms introduced in Beta.04, fixed. + Many of the makefile targets adjusted, new ones added. + New "make install" target should please most people. + New command: SHOW IKSD. + FTP over TLS. + Last-minute touchups to text messages, HELP text, etc. + Enable modem-signal reading for SCO OSR5 and Unixware 7. + Special superfast TRANSMIT /BINARY /NOECHO /NOWAIT mode added. + Fixed PBX dialing in unmarked-area-code case. + Improved SHOW COMMUNICATIONS tells lockfile directory, typical dialout device name. + Some FTP OPEN command parsing problems fixed. + Some errors in date arithmetic fixed. + New command: SET TERMINAL AUTODOWNLOAD { ..., ERROR { STOP, CONTINUE } } + New command: HELP FIREWALL. + SET MODEM HANGUP-METHOD DTR added as synonym for RS232-SIGNAL + Support for secure URL protocols added: telnets:, ftps:, https:. C-Kermit 8.0.201 (8 Feb 2002) + Installability as an [159]SSH v2 Subsystem. + [160]SET LOCUS command. + [161]L-versions of CD, DIR, DELETE, MKDIR, etc, to force local execution. + [162]USER and ACCOUNT added as synonyms for FTP USER and FTP ACCOUNT. + [163]SHOW VARIABLES now accepts a list of variables. + Rudimentary support for [164]Caller ID when receiving phone calls. + Up/Down [165]Arrow-key navigation of command history buffer. + [166]Automatic execution of customization file if init file is missing. C-Kermit 8.0.206 Beta.01 (11 Oct 2002) New commands: o ORIENTATION lists location-related variables and their values. o KCD changes to special directories by their symbolic names ("kcd ?" for a list). o SET CD HOME path to specify home directory for CD and KCD commands. o CONTINUE given at top level is equivalent to END -- handy when PROMPT'ed out of a script, to continue the script. New switches or operands for existing commands: o GETOK /TIMEOUT o ASK, ASKQ, GETOK /QUIET (suppresses error message on timeout) o COPY /APPEND now allows concatenating multiple source files into one dest file. o SET TCP { HTTP-PROXY, SOCKS-SERVER } /USER, /PASSWORD. o DIRECTORY command now accepts multiple filespecs, e.g. "dir a b c". SET QUIET ON now also applies to: o SET HOST connection progress messages. o "Press the X or E key to cancel" file-transfer message. o REMOTE CD response. o REMOTE LOGIN response. Improvements and new features: o Numerous FTP client fixes and new features, listed below. o C-Kermit, when in remote mode at the end of a file transfer, now prints a one-line "where" message. Control with SET TRANSFER REPORT. o Unix makefile "install" target now creates an UNINSTALL script. o Improved operation and performance on RFC 2217 Telnet connections. o Improved CONNECT (interactive terminal connection) performance. o HELP text updated for many commands. New or fixed makefile targets: o Solaris 9 (several variations) o Concurrent PowerMAX o Mac OS X 10.2 o FreeBSD 1.0 o FreeBSD 4.6, 5.0 o AIX 5.2, 5.3 Bugs fixed (general): o Failure to run in VMS Batch fixed. o LDIRECTORY fixed to run Kermit's built-in DIRECTORY command rather than an external one. o Fixed Solaris and other SVORPOSIX builds to find out their full hostnames rather than just the "uname -n" name. o Fixed some problems matching strings that start with ".". o Fixed some problems matching pattern that contain {a,b,c} lists. o Fixed erroneous reporting of text-mode reception as binary when sender did not report the file size (cosmetic only). o Many problems with SWITCH statements fixed. o Fixed SET OPTIONS DIRECTORY /DOTFILES to work for server too. o Fixed DELETE to print an error message if the file was not found. o Fixed SET CONTROL UNPREFIX ALL and SET PREFIXING NONE to do the same thing. o Fixed bugs executing macros from within the ON_EXIT macro. o \fday() and \fnday() fixed for dates prior to 17 Nov 1858. o Serial speed-changing bug in Linux fixed. o "Unbalanced braces" script parsing errors when using \{number} fixed. o "if defined \v(name)" fixed to behave as described in the book. o Fixed Problems caused by LOCAL variables whose names are left substrings of macro names. o The INPUT command was fixed to honor the PARITY setting. o Fixed bug with COPY to existing file that is longer than source file. o REINPUT command failed to strip braces/quotes around its target string. o Network directory lookups didn't work for SSH connections. o REMOTE SET { FILE, TRANSFER } CHARACTER-SET fixed. o Closed some holes whereby an incompletely received file was not deleted when SET FILE INCOMPLETE is DISCARD, e.g. when the Kermit is hung up upon. o SET XFER CHARACTER-SET TRANSPARENT fixed to do the same as SET XFER TRANSLATION OFF. o SET HOST PTY (e.g. SSH) connection fixed to pass along window-size changes. o C-Kermit search path for TAKE files was accidentally disabled. FTP client bugs fixed: o Character set translation was broken on little-endian (e.g. PC) architectures. o FTP PUT /SERVER-RENAME:, /RENAME-TO:, /MOVE-TO: switches were sticky. o Make SET TRANSFER MODE MANUAL apply to FTP. o Make SET FILE INCOMPLETE { KEEP, DISCARD } apply to FTP. o FTP MGET /UPDATE handled equal times incorrectly. o FTP MGET /RECOVER fixed to ignore file dates, use only size. o FTP MGET /RECOVER sometimes downloaded files it didn't need to. o FTP downloads with TRANSFER DISPLAY BRIEF could give misleading error messages. o FTP MGET temp file not deleted if FTP DEBUG set to OFF after it was ON. o LOCUS not switched back when FTP connection is lost. o Set incoming file date even if it was not completely received. o FTP MGET sent SIZE and MDTM commands even when it didn't have to. o FTP MGET sent SIZE and MDTM commands even when it knew they wouldn't work. o FTP MGET failed if no files were selected for download. o FTP MGET a* b* c* would fail to get any c*'s if no b*'s existed. o Big problems canceling MGET with Ctrl-C. o Some extraneous LOCUS dialogs squelched. o Some inconsistencies in SET FTP FILENAMES AUTO fixed. o Fixed file-descriptor pileup after multiple MGETs when using mkstemp(). o Fixed "mget foo", where foo is a directory name. FTP improvements: o New [167]FTP protocol features added (FEAT, MLSD). o FTP MGET /RECURSIVE now works as expected if server supports MLSD. o FTP MGET /DATES-DIFFER to download if local and remote file dates differ. o FTP DATES default changed to ON. o FTP MPUT, MGET /EXCEPT now allows up to 64 patterns (up from 8). o Top-level SITE and PASSIVE commands added for convenience. o MGET /COLLISION:APPEND /AS-NAME:newfile *.* puts all remote files into one local file. o SET FTP SERVER-TIME-OFFSET for when server has wrong timezone set. o Allow for alternative server interpretations of [M]MPUT /UNIQUE. o SET FTP ANONYMOUS-PASSWORD lets you specify the default anonymous password. o Allow "GET /RECURSIVE path/file" to force local subdirectory creation. o SET FTP DISPLAY is like SET TRANSFER DISPLAY but applies only to FTP. o FTP { ENABLE, DISABLE } new-protocol-feature-name. o FTP MGET /NODOTFILES. o Debug log now records FTP commands and responses in grep-able format. [ [168]Top ] [ [169]Contents ] [ [170]C-Kermit ] [ [171]Kermit Home ] 1. FIXES SINCE VERSION 7.0.196 First, the changes from 7.0.196 to 7.0.197... Source and makefile tweaks to get successful builds on platforms that were not available in time for the 7.0 release: * 4.2BSD * 4.3BSD * AIX 4.3 * AT&T 3B2 and 3B20 * BeOS 4.5 * CLIX * Interactive UNIX System V/386 R3.2 V4.1.1 * OS-9/68000 * OSF/1 1.3. * PS/2 AIX 1.2.1 * SCO OSR5.0.x * SCO Xenix 2.3.4 * SINIX 5.41/Intel * Stratus FTX * Stratus VOS * SunOS 4.1 with X.25 * Ultrix 4.2 * Unixware 2.0 There were no functional changes from 196 to 197. Fixes applied after C-Kermit 7.0.197 was released: Source code: Big flexelint and "gcc -Wall" audit and cleanup. Configuration: * Solaris RTS/CTS (hardware flow control) didn't work. * BSDI RTS/CTS worked only in one direction. * FreeBSD 4.0 with ncurses 5.0 broke interactive command parsing. * QNX-32 build lacked -DBIGBUFOK so couldn't execute big macros. Connections: * SET HOST /PTY didn't work on some platforms. * Broken SET HOST /USER:xxx /PASSWORD:yyy /ACCOUNT:zzz switches fixed. * Transparent printing was broken in Unix. * ANSWER 0 (wait forever) didn't work. * Some problems in Multitech modem command strings. * Spurious "?Sorry, can't condition console terminal" errors. * Disabling modem command strings by setting them to nothing broke dialing. * SET DIAL TIMEOUT value was usually ignored. * SET DIAL METHOD PULSE didn't work. * Certain modem commands, if changed, not refreshed if modem type changed. * SET SESSION-LOG command was missing from VMS. * VMS session log format fixed for scripts. * HANGUP by dropping DTR didn't work in NetBSD. * SET FLOW /AUTO versus SET FLOW confusion fixed. * Spurious secondary Solaris lockfile removed. * SCO OSR5 DTR On/Off hangup. * UUCP lockfile race condition. Commands and scripts: * Missing CAUTIOUS and FAST commands restored. * Broken PTY command in late-model Linuxes fixed (API changed). * Fixed off-by-one error in command recall when switching direction. * Fixed recall of commands that contain '?'. * COPY /SWAP-BYTES didn't work on some architectures. * Various combinations of COPY switches didn't work. * Various problems with COPY or RENAME with a directory name as target. * SHIFT didn't decrement \v(argc) if used within IF, ELSE, or SWITCH block. * SHIFT didn't affect the \%* variable. * Divide by zero improperly handled in some \function()s. * Problems with RETURN from right-recursive functions. * FSEEK /LINE \%c LAST didn't work if already at end. * Some buffer vulnerabilities and potential memory leaks were discovered and fixed. * \frdirectory() fixed not to follow symbolic links. * SET EXIT WARNING OFF fixed to work when EXIT given in a script. * Missing DELETE and MKDIR error message fixed. * \fday() core dump for ancient dates fixed. File transfer: * SEND /COMMAND was broken. * CRECEIVE was broken (but RECEIVE /COMMAND was OK). * Quoting wildcard chars in filenames didn't work. * Problems canceling streaming file transfers with X or Z. * Problems shifting between streaming and windowing file transfer. * Non-FULL file-transfer displays erroneously said STREAMING when not. * An active SEND-LIST prevented GET from working. * SET SERVER GET-PATH interpretation of relative names like "." was wrong. * The MAIL command was broken. * "kermit -s *" might have skipped some files. * Transaction log entries were not made for external protocol transfers. * File count report fixed to show number of files actually transferred. * Fixed filename conversion to convert spaces to underscores. * Made SET PREFIXING / SET CONTROL PREFIX also adjust CLEARCHANNEL. * More "Receive window full" errors fixed. * Broken terminal buffering after curses display in Solaris fixed. * SET FILE INCOMPLETE DISCARD did not work in all cases. * Packet log changed to reformat the start-of-packet character printably. * Dynamic timeouts could grow ridiculously large. Character sets: * Hebrew-7 translations missed the letter Tav. * C1 area of CP1252 was ignored. * SET TRANSFER CHARACTER-SET TRANSPARENT could give garbage translations. * TRANSLATE might not work on Little Endian architectures. * Insufficient range checking in certain TRANSLATE operations. The following bugs in C-Kermit 8.0.200 were fixed in 8.0.201: * An obscure path through the code could cause the Unix version of C-Kermit to dump core during its startup sequence. This happened to only one person, but now it's fixed. * When C-Kermit 8.0 is in Kermit server mode and the client says "get blah", where blah (on the server) is a symlink rather than a real file, the server unreasonably refused to send the linked-to file. * When C-Kermit is an FTP client and says "get foo/bar" (i.e. a filename that includes one or more path segments), it failed to accept the incoming file (this happened only with GET, not MGET). * Array references should be case insensitive but only lowercase array letters were accepted. * SHOW VARIABLES dumped core on \v(sexpression) and \v(svalue). * Spurious refusals of remote directory listings if the remote server's date was set in the past. * In AIX, and maybe elsewhere too, Kermit's COPY command always failed with "Source and destination are the same file" when the destination file didn't exist. * The VMS version of C-Kermit did not work in Batch or when SPAWN'd. To compound the problem, it also pretty much ignored the -B and -z command-line options, whose purpose is to work around such problems. * C-Kermit 8.0 could not be built on IRIX 5.x. * The C-Kermit 8.0 build for QNX6 said it was an "(unknown version)". Other fixes are listed in the [172]previous section. [ [173]Top ] [ [174]Contents ] [ [175]C-Kermit ] [ [176]Kermit Home ] 2. SSH AND HTTP 2.1. SSH Connections This section does not apply to [177]Kermit 95 2.0, which has its own built-in SSH client, which is documented [178]SEPARATELY. On most UNIX platforms, C-Kermit can make SSH (Secure SHell) connection by running the external SSH command or program through its pseudoterminal interface. The command is: SSH text Tells Kermit to start the external SSH client, passing the given text to it on the command line. Normally the text is just the hostname, but it can be anything else that is acceptable to the ssh client. If the command succeeds, the connection is made and Kermit automatically enters CONNECT (terminal) mode. You can use the SSH command to make a connection to any host that has an SSH server. Kermit's SSH command gives you all the features of Kermit on an SSH connection: command language, file transfer, character-set translation, scripting, and all the rest. By default, C-Kermit invokes SSH with "-e none", which disables the ssh escape character and makes the connection transparent for purposes of file transfer. You can, however, change the SSH invocation to whatever else you might need (an explicit path, additional command-line arguments, etc) with: SET SSH COMMAND text Specifies the system command that Kermit's SSH command should use to invoke the external SSH client. Use this command to supply a specific path or alternative name, or to include different or more command-line options. In most cases, these connections work quite well. They can be scripted like any other connection, and file transfer goes as fast as, or faster than, on a regular Telnet connection. In some cases, however, the underlying pseudoterminal driver is a limiting factor, resulting in slow or failed file transfers. Sometimes you can work around such problems by reducing the Kermit packet length. Note that Kermit does not consider SSH connections to be reliable, so it does not offer to use streaming in Kermit protocol transfers (but you can force it with SET RELIABLE or SET STREAMING if you wish). The SSH command is like the TELNET command: it enters CONNECT mode automatically when the connection is made. Therefore, to script an SSH connection, use: set host /pty ssh -e none [ other-options ] host if fail ... to make the connection. Here's a sequence that can be used to make a connection to a given host using Telnet if the host accepts it, otherwise SSH: if not defined \%1 exit 1 Usage: \%0 host set quiet on set host \%1 23 /telnet if fail { set host /pty ssh -l \m(user) -e none \%1 if fail exit 1 \%1: Telnet and SSH both fail echo SSH connection to \%1 successful } else { echo Telnet connection to \%1 successful } In SSH v2, it is possible to make an SSH connection direct to a Kermit server system if the host administrator has configured the SSH server to allow this; [179]CLICK HERE for details. Since Kermit uses external ssh client software, and since there are different ssh clients (and different releases of each one), the exact command to be used to make an SSH/Kermit connection can vary. Here is the command for the OpenSSH 3.0.2p1 client: set host /pipe ssh -e none [ -l username ] -T -s hostname kermit Example: set host /pipe ssh -e none -l olga -T -s hq.xyzcorp.com kermit The SSH client might or might not prompt you for a password or other information before it makes the connection; this depends on your SSH configuration (your public and private keys, your authorized hosts file, etc). Here's a brief synopsis of the OpenSSH client command syntax ("man ssh" for details): -e none This tells the SSH client to use no escape character. Since we will be transferring files across the connection, we don't want the connection to suddenly block because some character in the data. -l username This is the username on the remote host. You can omit the -l option and its argument if your local and remote usernames are the same. If they are different, you must supply the remote username. -T This tells the SSH client to tell the SSH server not to allocate a pseudoterminal. We are not making a terminal connection, we don't need a terminal, and in fact if a terminal were allocated on the remote end, the connection would not work. -s ... kermit This tells the SSH client to tell the SSH server to start the specified subsystem ("kermit") once the connection is made. The subsystem name comes after the hostname. hostname The IP host name or address of the desired host. You might want to include other or additional ssh command-line options; "man ssh" explains what they are. Here are some examples for the OpenSSH 3.0.2p1 client: -oClearAllForwardings yes -oForwardAgent no -oForwardX11 no -oFallbackToRsh no These ensure that a secure connection is used and that the connection used for file transfer is not also used for forwarding other things that might be specified in the ssh_config file. -oProtocol 2 (i.e. SSH v2) Ensures that the negotiated protocol supports subsystems. Once you have an SSH connection to a Kermit server, it's just like any other connection to a Kermit server (and very similar to a connection to an FTP server). You give the client file transfer and management commands for the server, and the server executes them. Of course you can also give the client any other commands you wish. [ [180]SSH Kermit Server Subsystem ] [ [181]Kermit 95 Built-in SSH Client ] 2.2. HTTP Connections Hypertext Transfer Protocol, or HTTP, is the application protocol of the World Wide Web (WWW), used between Web browsers (clients) and Web servers. It allows a client to get files from websites, upload files to websites, delete files from websites, get information about website directories and files, and interact with server-side CGI scripts. C-Kermit includes an HTTP client capable of both clear-text and secure HTTP connections, that can do all these tasks and can be automated through the Kermit scripting language. Although C-Kermit 7.0 could make HTTP connections to Web servers, it could do so only when no other connection was open, and the procedure was somewhat awkward. C-Kermit 8.0 improves matters by: * Allowing an HTTP connection to be open at the same time as a regular SET LINE or SET HOST connection, and also at the same time as an FTP connection ([182]Section 3); * Upgrading the HTTP protocol level from 1.0 to 1.1, thus allowing for persistent connections, in which a series of commands can be sent on the same connection, rather than only one as in HTTP 1.0 (and C-Kermit 7.0); * Providing for "one-shot" URL-driven HTTP operations such as GET or PUT. * Providing a distinct HTTP command-line personality. Persistent HTTP connections are managed with the following commands: HTTP [ switches ] OPEN [ security-options ] host-or-url [ port ] Opens a persistent connection to the specified host (IP host name or address) on the specified port. If any switches (options, listed in the next section) are included, their values are saved and used for all subsequent HTTP action commands on the same connection. If no port is specified, HTTP (80) is used. A Uniform Resource Locator (URL, [183]RFC 1738) can be given instead of a hostname (or address) and port (but the URL can not include a directory/file path). The security options are explained [184]below. The HTTP OPEN command replaces the C-Kermit 7.0 SET HOST hostname HTTP command, which no longer works with HTTP GET and related commands. HTTP CLOSE Closes any open HTTP connection and clears any saved switch values. A URL starts with a protocol name, which must be http or https in this case; optionally includes a username and password; and must contain a host name or address: protocol://[user[.password]]@host[:port][URI] HTTP is Hypertext Transfer Protocol. HTTPS is the secure (SSL/TLS) version of HTTP. The TCP service port is derived from the protocol prefix (so normally the ":port" field is omitted). Thus the URL protocol name specifies a default TCP service port and the URL user and password fields can take the place of the /USER and /PASSWORD switches ([185]Section 2.2.1). The optional URI is a "compact string of characters for identifying an abstract or physical resource" ([186]RFC 2396), such as a file. It must begin with a slash (/); if the URI is omitted, "/" is supplied. Examples: http open http://www.columbia.edu/ Equivalent to http open www.columbia.edu or http open www.columbia.edu http. http open https://olga.secret@www1.xyzcorp.com/ Equivalent to http /user:olga /pass:secret open www1.xyzcorp.com https. Persistence is accomplished unilaterally by C-Kermit 8.0. An HTTP 1.0 server closes the connection after each action. Although HTTP 1.1 allows multiple actions on the same connection, an HTTP 1.1 server tends to close the connection if it is idle for more than a few seconds, to defend itself against denial-of-service attacks. But when you use Kermit's HTTP OPEN command to create a connection, Kermit reopens it automatically (if necessary) for each HTTP action until you close it with HTTP CLOSE, regardless of the server's HTTP protocol version, or how many times it closes the connection. Firewalls can be negotiated through proxies with the following commands: SET TCP HTTP-PROXY [ host[:port] ] If a host (by hostname or IP address) is specified, Kermit uses it as a proxy server when attempting outgoing TCP connections -- not only HTTP connections, but all TCP/IP connections, Telnet and FTP included. This allows Kermit to adapt to the HTTP firewall penetration method (as opposed to other methods such as SOCKS4). If no hostname or ip-address is specified, any previously specified Proxy server is removed. If no port number is specified, the "http" service is used. This command must be given before the HTTP OPEN command if a proxy is to be used or canceled. HTTP [ switches ] CONNECT host[:port] Instructs the HTTP server to act as a proxy, establishing a connection to the specified host (IP hostname or address) on the given port (80 = HTTP by default) and to redirect all data transmitted between Kermit and itself to the given host for the life of the connection. This command is to be used only for debugging HTTP proxy connections. If a proxy connection is required, instruct Kermit to use the proxy with the SET TCP HTTP-PROXY command. 2.2.1. HTTP Command Switches HTTP switches, like all other switches, are optional. When HTTP switches are included with the HTTP OPEN command, they apply automatically to this and all subsequent HTTP actions (GET, PUT, ...) on the same connection until an HTTP CLOSE command is given. So if you include switches (or the equivalent URL fields, such as user and password) in the HTTP OPEN command, you can omit them from subsequent commands on the same connection. If the connection has closed since your last command, it is automatically reopened with the same options. If you include switches with an HTTP action command (such as GET or PUT), they apply only to that command. /USER:name To be used in case a page requires a username for access. The username is sent with page requests. If it is given with the OPEN command it is saved until needed. If a username is included in a URL, it overrides the username given in the switch. CAUTION: Username and password (and all other information, including credit card numbers and other material that you might prefer to protect from public view) are sent across the network in clear text on regular HTTP connections, but authentication is performed securely on HTTPS connections. /PASSWORD:text To be used in case a web page requires a password for access. The password is sent with page requests. If it is given with the OPEN command it is saved until needed. If a password is given in a URL, it overrides the one given here. CAUTION: (same as for /USER:). /AGENT:user-agent Identifies the client to the server. Overrides the default agent string, which is "C-Kermit" (for C-Kermit) or "Kermit-95" (for Kermit 95). /ARRAY:array-designator Tells Kermit to store the response headers in the given array, one line per element. The array need not be declared in advance. Example: /array:&a. /TOSCREEN Tells Kermit to display any response text on the screen. It applies independently of the output file specification; thus it is possible to have the server response go to the screen, a file, both, or neither. /HEADER:header-item(s) Used for specifying any optional headers to be sent with HTTP requests. /HEADER:tag:value To send more than one header, use braces for grouping: /HEADER:{{tag:value}{tag:value}...} For a list of valid tags and value formats see [187]RFC 2616, "Hypertext Transfer Protocol -- HTTP/1.1". A maximum of eight headers may be specified. 2.2.2. HTTP Action Commands HTTP actions can occur within a persistent connection, or they can be self-contained ("connectionless"). A persistent HTTP connection begins with an HTTP OPEN command, followed by zero or more HTTP action commands, and is terminated with an HTTP CLOSE command: http open www.columbia.edu if failure stop 1 HTTP OPEN failed: \v(http_message) http get kermit/index.html if failure stop 1 HTTP GET failed: \v(http_message) (more actions possible here...) http close A self-contained HTTP action occurs when a URL is given instead of a remote file name to an HTTP action command. In this case, Kermit makes the HTTP connection, takes the action, and then closes the connection. If an HTTP connection was already open, it is closed silently and automatically. http get http://www.columbia.edu/kermit/index.html Kermit's HTTP action commands are as follows. Switches may be included with any of these to override switch (or default) values given in the HTTP OPEN command. HTTP [ switches ] GET remote-filename [ local-filename ] Retrieves the named file from the server specified in the most recent HTTP OPEN command for which a corresponding HTTP CLOSE command has not been given. The filename may not include wildcards (HTTP protocol does not support them). If no HTTP OPEN command is in effect, this form of the HTTP GET command fails. The default local filename is the same as the remote name, but with any pathname stripped. For example, the command http get kermit/index.html stores the file in the current local directory as index.html. If the /HEADERS: switch is included, information about the file is also stored in the specified array (explained in [188]Section 2.2.3). All files are transferred in binary mode. HTTP does not provide for record-format or character-set conversion. HTTP [ switches ] GET url [ local-filename ] When HTTP GET is given a URL rather than a filename, Kermit opens a connection to the designated server (closing any previously open HTTP connection), gets the file, and then closes the connection. If the URL does not include a filename, index.html is supplied. This is the self-contained one-step "connectionless" method for getting a file from a Web server. The data is not interpreted; HTTP GET is like "lynx -source" rather than "lynx -dump". In the remaining HTTP action commands, the distinction between a remote filename and a URL are the same as in the HTTP GET command. HTTP [ switches ] HEAD remote-filename-or-url [ local-filename ] Like GET except without actually getting the file; instead it retrieves only the headers. If the /ARRAY: or /TOSCREEN switch is included, there is no default local output filename but you can still specify one. If neither of these switches is included, the default local filename is the same as the remote filename, but with any path stripped and with ".head" appended. The HEAD command can be used in a script with the /ARRAY: switch to retrieve information about the requested resource to determine whether the resource should actually be retrieved with a subsequent GET request. HTTP [ switches ] INDEX remote-directory-or-url [ local-filename ] Asks the server to send a listing of the files in the given server directory. This command is not supported by most Web servers. Even when it is supported, there is no standard format for the listing. HTTP [ switches ] POST [ /MIME-TYPE:type ] source-file remote-path-or-url [ result-file ] Sends data to a process running on the remote host; the result is usually an HTML file but could be anything. The data to be posted must be read from a local file (the source-file). If a result file is specified, Kermit stores the server's response in it. HTTP [ switches ] PUT [ MIME-TYPE:type ] local-file [ remote-file-or-url [ result-file ] ] Uploads a local file to the server. Only the name of a single file can be given; wildcards (and group transfers) are not supported by HTTP protocol. If no remote filename is given, the file is sent with the same name as the local file, but with any pathname stripped. HTTP [ switches ] DELETE remote-file-or-url [ local-result-file ] Asks the server to delete the specified single file. If a result file is specified, it will contain any response data returned by the server. Note the limitations of HTTP protocol compared to (say) FTP or Kermit. There is no command for changing directories, no standard way to get file or directory lists, no way to transfer file groups by using wildcard notation, etc, and therefore no good way to (say) fetch all pages, descend through subdirectories, perform automatic updates, etc. There is no assurance a connection will stay open and, as noted, there is no provision for data conversion between unlike platforms. The data's MIME headers can be used for postprocessing. 2.2.3. HTTP Headers Each HTTP request and response contains a set of name/value pairs called headers. HTTP headers are specified in [189]RFC 2616. For example, an HTTP GET request for /index.html on www.columbia.edu contains the following headers: GET /index.html HTTP/1.1 Host: www.columbia.edu:80 User-agent: C-Kermit 8.0 Authorization: Basic base64-encoded-username-password These might be followed by any others specified with a /HEADERS: switch: Accept: image/gif, image/x-xbitmap, image/jpeg, *.* Accept-Encoding: gzip Accept-Language: en Accept-Charset: iso-8859-1,utf-8 Cookie: cookie-data The server sends back a short report about the file prior to sending the file contents. Example: HTTP/1.1 200 OK Date: Fri, 24 Aug 2001 21:09:39 GMT Server: Apache/1.3.4 (Unix) Last-Modified: Mon, 06 Aug 2001 21:16:13 GMT ETag: "1fa137-10d7-3b6f091d" Accept-Ranges: bytes Content-Length: 4311 Content-Type: text/html If you want to have this information available to a Kermit script you can use the /ARRAY switch to have Kermit put it in array, one line per array element. Example: set exit warning off http open www.columbia.edu if fail exit 1 Can't reach server http /array:&a get /index.html if fail exit 1 Can't get file echo Header lines: \fdim(&a) for \%i 1 \fdim(&a) 1 { echo \%i. \&a[\%i] } Note that the "Date:" item is the current date and time; the "Last-Modified:" item is the file's modification date and time. An example showing how to use this information is presented in [190]Section 8.13.7. 2.2.4. Secure HTTP Connections SSL/TLS (Secure Sockets Layer / Transport Layer Security) is the protocol used to secure HTTP, SMTP, and other Internet applications. See the [191]C-Kermit Reference Section 5.4 for an introduction to SSL/TLS. To make a secure HTTP connection, you need: 1. A secure client (a version of C-Kermit or Kermit 95 with SSL/TLS security built in). Type "check ssl" at the Kermit prompt to make sure you have it. 2. A secure server to connect to. 3. The CA Root Certificate used to authenticate the server to the client. (see [192]Section 15 of the security reference for an introduction to certificates). And you must make a connection to the secure HTTP port: service name HTTPS, port number 443 (as opposed to service HTTP, port 80). You can also make secure connections to other ports by including the /TLS or /SSL switch with the HTTP OPEN command, if the host supports SSL/TLS on the given port: The quality of the SSL/TLS connection depends on the cipher suite. There are several possibilities: Anonymous cipher suite: If an anonymous cipher suite is negotiated, the connection is encrypted but there is no authentication. This connection is subject to a Man-In-The-Middle (MITM) attack. X.509 certificate on the server: When you connect to certain secure servers, an X.509 certificate is returned. This certificate is issued to a special hostname, something like www1.xyzcorp.com or wwws.xyzcorp.com (rather than the normal www.xyzcorp.com). It is signed by the host's Certificate Authority (CA). If the host certificate is configured on the client, it can be used to verify the certificate received from the server. If the certificate it verified as authentic, a check is made to ensure it has not expired and it was issued to the host you were attempting to connect to. If you had asked to connect to (say) www.xyzcorp.com but were given a certificate for www1.xyzcorp.com, you would be prompted for permission to continue. If the verification succeeded, the connection would be encrypted with one-way (server-to-client) authentication. This connection is not subject to a MITM attack. If a username and password are transmitted over this connection, they are not subject to interception. However, the standard risks associated with passing the password to the host for verification apply; for example, if the host has been compromised, the password will be compromised. X.509 client certificate: If a connection has been established with an X.509 server certificate, the server can ask the client to send a certificate of its own. This certificate must be verified against a CA Root certificate. The certificate itself (or subject info from the certificate) is used to determine the authorization for the client, and if successful, the username and password need not be sent to the server. Kerberos 5: Instead of using X.509 certificates, Kerberos 5 can be used to perform the authentication and key exchange. In this situation, there is mutual authentication between the client and server. The Kerberos 5 principal is used by the server to look up the appropriate authorization data. There is no need to send username and password. An HTTP connection is made with the HTTP OPEN command: HTTP [ switches ] OPEN [ { /SSL, /TLS } ] host [ port ] If /SSL or /TLS switches are included (these are synonyms), or if the service is HTTPS or the port is 443, a secure connection is attempted using the current authentication settings; see HELP SET AUTHENTICATION for details ([193]Section 6.2 of the security reference). If the no /SSL or /TLS switch is included but the port is 443 or the service is HTTPS, a secure connection is attempted. If an /SSL or /TLS switch is included but a port is not specified, an SSL/TLS connection is attempted on the default port (80). Certificates are covered in the separate [194]Kermit Security Reference for C-Kermit 8.0. You should let Kermit know to verify certificates with the SET AUTHENTICATION TLS command. For example: SET AUTHENTICATION TLS CRL-DIR directory Specifies a directory that contains certificate revocation files where each file is named by the hash of the certificate that has been revoked. SET AUTHENTICATION TLS CRL-FILE filename Specifies a file that contains a list of certificate revocations. SET AUTHENTICATION TLS VERIFY-DIR directory Specifies a directory that contains root CA certificate files used to verify the certificate chains presented by the peer. Each file is named by a hash of the certificate. SET AUTHENTICATION TLS VERIFY-FILE filename Specifies a file that contains root CA certificates to be used for verifying certificate chains. SET AUTHENTICATION TLS VERIFY OFF Tells Kermit not to require a certificate and accept any certificate that is presented regardless of whether it is valid. There are many other options; see the security document for details. Now suppose you need need to fetch the file denoted by the following URL: https://myuserid:mypassword@wwws.xyzcorp.com/clients/info/secret.html Once you have set up the handling of certificates as desired, you can use the following Kermit commands: http /user:myuserid /password:mypassword open www1.xyzcorp.com https if success { http get /clients/info/secret.html http close } As another example, let's say that you have a web form you need to populate with three fields: red,white and blue.
You can handle this with the HTTP POST command. The data to be posted is stored in the local file data.txt. Red=seven stripes&White=six stripes&Blue=fifty stars and the response from the server will be stored into response.txt. http open www.xyzcorp.com http if success { http /array:c post data.txt /cgi-bin/form.cgi response.txt http close } In this scenario, the Common Gateway Interface (CGI) sends a response whether it succeeds or fails in a script-dependent manner. The script can either report success and enclose the response data; or it might send a 302 Found error which indicates that the "Location:" header should be used to determine the URL at which the data can be found. 2.2.5. HTTP Variables \v(http_code) The HTTP protocol code number of the most recent server reply, e.g. 404 for "not found". \v(http_connected) 1 when an HTTP connection is open, 0 when there is no HTTP connection. \v(http_host) If an HTTP connection is open, the hostname:port, e.g. www.columbia.edu:80; otherwise, empty. \v(http_message) Server error message, if any, from most recent HTTP command. \v(http_security) A list of the security parameters and values for the current connection, if any. Empty if the connection is not to a secure server, or there is no connection. To display all the HTTP variables at once, type SHOW VAR HTTP: C-Kermit> http open www.columbia.edu C-Kermit> http get lkjlkjlkjlkj C-Kermit> sho var http \v(http_code) = 404 \v(http_connected) = 1 \v(http_host) = www.columbia.edu:80 \v(http_message) = Not Found \v(http_security) = NULL C-Kermit> 2.2.6. The HTTP Command-Line Personality If you invoke C-Kermit with the name "http" or "https", you can use a special set of HTTP-specific command-line options. You can do this by creating a symbolic linke "http" or "https" to the C-Kermit 8.0 executable, or by having a separate copy of it called "http" or "https". Here's the usage message ("http -h"): Usage: ./http host [ options... ] -h This message. -d Debug to debug.log. -S Stay (issue command prompt when done). -Y Do not execute Kermit initialization file. -q Quiet (suppress most messages). -u name Username. -P password Password. -g pathname Get remote pathname. -p pathname Put remote pathname. -H pathname Head remote pathname. -l pathname Local path for -g, -p, and -H. -z opt[=value] Security options... cert=file Client certificate file certsok Accept all certificates key=file Client private key file secure Use SSL verify=n 0 = none, 1 = peer , 2 = certificate required The "host" argument is the name of a Web host, e.g. www.columbia.edu. The action options are -p, -g, and -H. If you give an action option, Kermit does the action and then exits. If you give a host without an action option, Kermit makes an HTTP connection to the host and then gives you the C-Kermit prompt. Here's a simple example that fetches a publicly readable Web page: http www.columbia.edu -g kermit/index.html If you need to access a website for which a username and password are required, you can supply them on the command line with -u and -P. If you include a username but omit the password, Kermit prompts you for it: http www.columbia.edu -u olga -p kermit/index.html -l index.html Password: Note that when PUT'ing files to websites, you have to supply both the -p (remote pathname) and -l (local path) options. If your version of Kermit is built with SSL/TLS security, you can also use the -z option to make secure HTTP (https) connections. Finally, as noted in [195]Section 16, you can also give a URL instead of a host name and options. [ [196]Top ] [ [197]Contents ] [ [198]C-Kermit Home ] [ [199]Kermit Home ] 3. KERMIT'S BUILT-IN FTP CLIENT 3.1. [200]Making and Managing FTP Connections 3.2. [201]Making Secure FTP Connections 3.3. [202]Setting FTP Preferences 3.4. [203]Managing Directories and Files 3.5. [204]Uploading Files With FTP 3.6. [205]Downloading Files With FTP 3.7. [206]Translating Character Sets 3.8. [207]FTP Command Shortcuts 3.9. [208]Dual Sessions 3.10. [209]Automating FTP Sessions 3.11. [210]Advanced FTP Protocol Features Earlier versions of C-Kermit and K95 included an FTP command, but it simply invoked an external FTP client. Now, by popular demand, Kermit includes its own built-in FTP client that offers the following advantages over traditional FTP clients (and its previous interface to them): * Any of Kermit's built-in [211]security methods can be used to establish and conduct secure FTP sessions with [212]FTP servers that support these methods. (Security modules can be subject to export restrictions.) * Kermit's FTP client uses "passive mode" by default to avoid blockage by firewalls and network address translators. Of course active mode can be chosen too when needed. * [213]Character sets can be translated as part of the transfer process even when the FTP server does not support character-set translation, including to/from the new Internet standard international character set, [214]Unicode UTF-8. This includes both the file's name and (for text files only) its contents. * All of C-Kermit's [215]file-selection mechanisms are available: size, date, name patterns and lists, exception lists, etc. * [216]Atomic file movement capabilities are provided (delete, move, or rename files automatically after successful transfer). * The correct file type, "ascii" (i.e. text) or binary, is chosen automatically for each file (explained in [217]Section 4), and any mixture of text and binary files can be sent in a single operation, even across platforms. * Update mode ("don't bother transferring files that didn't change since last time") and recovery (resumption of an interrupted transfer from the point of failure) are available in both directions. * When uploading files from UNIX to UNIX, the file's permissions can be preserved if desired. * Recursive directory-tree PUTs are supported between any two platforms that have tree-structured file systems. Recursive GETs are supported between like platforms if the server cooperates and between like or unlike platforms if the server supports MLSD ([218]Section 3.11). * When receiving files, all of Kermit's file collision actions are available: backup, update, refuse, rename, etc. * Multi-file transfers can be interrupted on a per-file basis, automatically skipping to the next file. * FTP sessions are [219]fully scriptable. * An entire FTP session (connect, login, CD, upload or download, logout) can be specified on the command line without using a script. * All of Kermit's logging options and formats are available to keep an accurate and complete record of each connection and file transfer, and to aid in troubleshooting. * All of Kermit's file-transfer display options are available (fullscreen, brief, CRT, serial, none). And best of all: * Kermit doesn't give you those annoying per-file prompts every time you start a multi-file transfer without remembering to give a "prompt" command first :-). [ [220]Top ] [ [221]FTP Top ] [ [222]FTP Client Overview ] [ [223]FTP Script Tutorial ] [ [224]C-Kermit Home ] [ [225]Kermit Home ] 3.1. Making and Managing FTP Connections Each copy of Kermit can have one FTP connection open at a time. FTP connections are independent of regular terminal connections; a terminal connection (serial or network via SET LINE, DIAL, SET HOST, TELNET, etc) may be, but need not be, open at the same time as an FTP connection, and terminal connections can also be closed, and new connections opened, without interfering with the FTP connection (and vice versa). Thus, for example, Kermit can have an FTP connection and a TELNET connection open to the same host simultaneously, using the TELNET connection (e.g.) to send mail or take other desired actions as various FTP actions complete. Of course, each copy of Kermit can do only one thing at a time, so it can't (for example) transfer a file with FTP and another file with Kermit protocol simultaneously. A Kermit FTP session can be established by [226]command-line options, by [227]URL, or by [228]interactive commands. 3.1.1. Kermit Command-Line Options for FTP The new command-line option '-9' (sorry, we're out of letters) can be used when starting C-Kermit, telling it to make an FTP connection: kermit -9 hostname or if a non-default FTP port is needed: kermit -9 hostname:port You can also specify the username on the command line with the -M ("My User ID") option that was already there for other connection types: kermit -9 hostname -M olga If you specify the username on the command line, Kermit uses it when making the connection and does not prompt you for it (but it does prompt you for the password if one is required). Once the connection is made, you get the regular Kermit prompt, and can give interactive commands such as the ones described below. When you give a BYE command, Kermit closes the session and exits, just as a regular FTP client would do. If you don't want Kermit to exit when you give a BYE command, include the -S ("Stay") option on the command line. Other Kermit command-line options that are not specific to non-FTP connections should affect the FTP session in the expected ways; for example, -i and -T force binary and text mode transfers, respectively. File transfers can not be initiated on the "kermit -9" command line; for that you need to use Kermit's FTP personality (next section) or you can use URLs ([229]Section 3.1.3). 3.1.2. The FTP Command-Line Personality If you want to replace your regular FTP client with C-Kermit, you can make a link called "ftp" to the C-Kermit binary (or you can store a copy of the C-Kermit binary under the name "ftp"). When C-Kermit is invoked with a program name of "ftp" (or "FTP", case doesn't matter), it assumes the command-line personality of the regular FTP client: ftp [ options ] hostname [ port ] In this case the options are like those of a regular FTP client: -d Debug: enables debug messages and creates a debug.log file. -n No autologin: Kermit should not send your user ID automatically. -t Packet trace: accepted but is treated the same as -d. -v Verbose: accepted but ignored (operation is verbose by default). -i Not interactive: accepted but ignored. and the hostname can also be a URL (explained in [230]Section 3.1.3). To specify a non-default TCP port for the FTP server, include the port number or name after the hostname. There are also some bonus options that allow you to execute an entire FTP session from the shell command line, as long as you don't include the -n option. These are not available with regular FTP clients, and at least one of these options (-g) conflicts with UNIX ftp (where -g means "no globbing", which does not apply to Kermit), and some of them (like the options above) also conflict with regular Kermit command-line options: -m mode = "passive" (default) or "active" -Y Don't execute the Kermit initialization file [1] -q Quiet, suppresses all but error messages [1] -S Stay, don't exit automatically [1] -A Autologin anonymously [2] -u name Username for autologin [2] (synonym: -M [1]) -P password Password for autologin (see cautions below) [2] -D directory cd after autologin [2] -b Binary mode [2] -a Text ("ascii") mode [2] (synonym: -T [1]) -R Recursive (works with -p) [4] -p files Files to put (upload) after autologin [2] (synonym: -s [1]) -g files Files to get (download) after autologin [3] [1] Same as Kermit, not available in regular FTP clients. [2] Conflicts with Kermit, not available in regular FTP clients. [3] Same as Kermit, conflicts with regular FTP clients. [4] Conflicts with Kermit, available in some FTP clients. Fancier options such as restart, character-set translation, filename collision selection, automatic move/rename/delete, etc, are not available from the command line; for these you can use the commands described in the following sections. The -R option might also work with -g (GET) but that depends on the server. The following security options are also available, explained in [231]Section 3.2: -k realm Kerberos 4 realm [4] -f Kerberos 5 credentials forwarding [4] -x autoencryption mode [4] -c cipher SRP cipher type [4] -H hash SRP encryption hash [4] -z option Security options [4] If you include -A or specify a name of "anonymous" or "ftp", you are logged in anonymously and, in the absence of -P, Kermit automatically supplies a password of "user@host", where "user" is your local user ID, and "host" is the hostname of the computer where Kermit is running. If you do not include -p or -g, Kermit enters command mode so you can type commands or execute them from a script. If you include -p or -g, Kermit attempts to transfer the specified files and then exits automatically at the end of the transfer unless you also included -S (Stay). It uses the "brief" file transfer display (one line per file) unless you include the -q option to suppress it. When uploading files with -p, Kermit switches automatically between text and binary mode for each file. When downloading, you can either specify a particular mode (text or binary) to be used for all the files, or you can let Kermit select the type for each file automatically, based on its name (see [232]Sections 3.5 and [233]3.6 for greater detail). In UNIX be sure to quote any wildcard characters to prevent the shell from expanding them, as shown in the examples just below. Filename collisions are handled according Kermit's FILE COLLISION setting (if specified in your Kermit customization file; otherwise the default, which is BACKUP). It should go without saying that the -P option should be used with caution. In addition to the well-known risks of transmitting plaintext passwords over the Internet, in this case the password also echos to the screen if you type it, and can be seen in ps and w listings that show the user's currently active command and command-line arguments. Thus command-line FTP sessions are most appropriate for secure or anonymous connections (those that do not require passwords). Here's an example in which you download the latest C-Kermit "tarball" from the Columbia University FTP archive: ftp -A kermit.columbia.edu -bg kermit/archives/ckermit.tar.gz This assumes that "ftp" is a symbolic link to C-Kermit. It logs you in anonymously and gets the ckermit.tar.gz file in binary mode from the kermit/archives directory. Here's a slightly more ambitious example that illustrates CD'ing to the desired server directory to get a group of files in text mode (in this case the C-Kermit source files): ftp -A kermit.columbia.edu -D kermit/f -ag "ck[cuw]*.[cwh]" makefile In this case we CD to the kermit/f directory so we don't have to include it in each file specification, and we quote the ck[cuw]*.[cwh] specification so the shell doesn't expand it, since we have to pass it as-is to the server. Note also that the quotes don't go around the entire file list; only around each file specification that needs to be quoted. Here's one more example, that uploads a debug log file in binary mode to the Kermit incoming directory (as we might ask you to do when following up on a problem report): ftp -A kermit.columbia.edu -D kermit/incoming -bp debug.log In this case the -D option is required to tell the server where to put the incoming file. Unless the -Y option is included, your Kermit initialization file (.mykermrc in UNIX, K95.INI in Windows) is executed before the command line options, so you can set any FTP-related preferences there, as described in the subsequent sections. 3.1.3. The FTP URL Interpreter If Kermit is invoked with either its regular personality (as "kermit") or its FTP personality (as "ftp"), you can also give a URL (Universal Resource Locator) instead of a hostname and options, with or without a username and password: ftp ftp://user:password@host/path ftp ftp://user@host/path ftp ftp://@host/path (or ftp://:@host/path) ftp ftp://host/path kermit ftp://host/path If the FTP personality is used, the service must be "ftp". In all cases, a hostname or address must be included. If a user is included but no password, you are prompted for the password. If a path (filename) is included: * If "@" is included without a user, Kermit prompts for the username and password. * If no user and no "@" are included, "anonymous" is used. * GET is assumed. If no path (and no action options) are included, an interactive FTP session is started, as in this example: ftp ftp://kermit.columbia.edu If a path is included, but a username is not included, "anonymous" is used and an appropriate user@host password is supplied automatically. If authentication is successful, Kermit attempts to GET the file indicated by the path or, if the path is the name of a directory, it asks the server for a directory listing. In both cases, Kermit disconnects from the server and exits after the operation is complete (unless you have included the -S option on the command line). Here's an example that gets a listing of the Kermit directory at the Kermit ftp site: ftp ftp://kermit.columbia.edu/kermit/ This example gets the top-level READ.ME file from the same directory: ftp ftp://kermit.columbia.edu/kermit/READ.ME Here's the same example, but requesting a text-mode transfer: ftp -T ftp://kermit.columbia.edu/kermit/READ.ME This illustrates that you can mix command-line options and URLs if you desire. Here's an example that logs in as a (fictitious) real user to get a file: ftp ftp://olga@ftp.xyzcorp.com/resume.txt The password is not included, so Kermit prompts for it. This scheme allows Kermit to be used as the FTP helper of other applications, such as Web browsers, with all its advantages over other FTP clients (especially the ones that are built in to most Web browsers), e.g. that it can be given wildcards, and it can pick text and binary mode automatically for each file. HINT: suppose somebody sends you an FTP URL in email, or you see it in some text. If your terminal screen supports copy/paste, copy the url, and then at the shell prompt type "kermit", a space, and then paste the URL, e.g.: $ kermit ftp://alpha.greenie.net/pub/mgetty/source/1.1/mgetty1.1.27-O "$ is the shell prompt; the part you type is underlined, the rest is pasted in. Kermit does the rest. 3.1.4. Interactive FTP Session Establishment As you read this and the following sections, bear in mind that any command that can be given at the prompt can also be used in a script program. Kermit's script programming language is the same as its interactive command language. [234]CLICK HERE if you would like to learn a bit more about script writing. An FTP session is established with the FTP OPEN command: FTP [ OPEN ] [ { /SSL, /TLS } ] hostname [ switches ] [ port ] Opens an FTP connection to the given host on the given port and, if FTP AUTOLOGIN is ON, also logs you in to the server, prompting for username and password if necessary. If no port is specified, the regular FTP protocol port (21) is used. The OPEN keyword is optional (unless the hostname conflicts with one of the FTP command keywords, which you can list by typing "ftp ?"). The hostname can be an IP host name, numeric IP address, or if you have a network directory active (SET NETWORK DIRECTORY; see Chapter 6 of [235]Using C-Kermit), an entry name in the directory. In the latter case, if the given hostname matches exactly one entry, the associated name or address is used; if it matches more than one, Kermit cycles through them until one is found that can be opened; if it matches none, then the hostname is used as-is. If a directory is active but you want to bypass directory lookup, include an "=" sign at the beginning of the hostname, and/or use a numeric IP address. When an FTP connection is opened, the default file-transfer mode is set to binary if the client and server platforms are alike (e.g. both of them are some kind of UNIX), and to text ("ascii") if they are not alike. This has no particular effect for uploading since Kermit automatically switches between text and binary mode for each file, but might be important for downloading. The connection is also set to Stream mode and File structure. Record- or page-oriented file transfers are not supported by C-Kermit's FTP client. The optional FTP OPEN switches are: /ANONYMOUS Logs you in anonymously, automatically supplying username "anonymous" and user@host as the password, based on your local user and host names. /NOLOGIN Overrides SET FTP AUTOLOGIN ON for this connection only. /USER:name Uses the given username to log you in, thus avoiding the Name: prompt. Overrides SET FTP AUTOLOGIN OFF for this connection only. /PASSWORD:text Uses the given text as your password, thus avoiding the Password: prompt. This switch is not recommended for use in script files, which would be a security risk. /ACCOUNT:text Uses the given text as your account (or secondary password, depending on the requirements of the server; most servers do not require or accept an account name). If an account is not supplied, you are not prompted for one. /PASSIVE Opens the connection in passive mode. Passive mode is the default in Kermit's FTP client, unlike in most others, since it works better through firewalls. The /PASSIVE and /ACTIVE switches apply only to the connection that is being opened, and do not affect the global FTP PASSIVE-MODE setting. /ACTIVE Opens the connection in active mode. Use this switch if the server does not support passive mode, or use the command SET FTP PASSIVE-MODE OFF. /NOINIT Added in C-Kermit 8.0.201. Tells C-Kermit not to send REST, STRU, FEAT, and MODE commands to the server when the connection is opened, since these have been reported to cause confusion in certain servers. When a username or password is missing, a prompt is issued at the controlling terminal and you must type the response; the response can not be scripted. Use the switches to avoid prompts, or one of the secure authentication methods described in the next section, or see [236]SET FTP AUTOLOGIN and the [237]FTP USER and similar commands described later in this section. Examples: ftp open kermit.columbia.edu /anonymous ; Open and log in anonymously ftp kermit.columbia.edu /anonymous ; The OPEN keyword can be omitted ftp xyzcorp.com ; Open and maybe prompt for username ftp xyzcorp.com /user:olga ; Open and log in as olga ftp testing.abccorp.com 449 ; Specify a special TCP port number ftp testing.abccorp.com /user:olaf /password:secret 449 The FTP OPEN command succeeds if a connection was opened to the server (even if the given username and password were not valid) and fails otherwise (see [238]Section 3.8 for details). When your FTP session is complete, you can terminate it as follows: FTP BYE Closes the FTP connection if one was open. The FTP prefix can be omitted if no other connection is open at the same time (see [239]Section 3.8 for details). If a connection log is active, an FTP record is written to it. If Kermit was started with the -9 command-line option or with its FTP command-line personality, and the -S (Stay) option was not given, AND there is no other active connection, the FTP BYE command also exits, just as it does on a regular FTP client. Synonyms: FTP CLOSE, FTP QUIT (but if the FTP prefix is omitted from QUIT, this becomes the regular Kermit QUIT command, which is equivalent to EXIT; i.e. it closes the connection and exits from Kermit). The following commands can be used to achieve greater control over the connection and login process: SET FTP ANONYMOUS-PASSWORD text Allows you to choose the password text to be sent automatically by Kermit when you open an FTP connection with the /ANONYMOUS switch. SET FTP AUTOLOGIN { ON, OFF } If you give this command prior to opening an FTP connection, it controls whether Kermit tries to log you in automatically as part of the connection process. Normally ON, which means the username and password are sent automatically (and prompted for if they are not yet known). When OFF, FTP OPEN connects to the server without logging in. OFF is equivalent to the -n command-line option when using Kermit's FTP command-line personality. FTP USER name [ password [ account ] ] Used to log in to an FTP server to which a connection has been made without autologin, or when autologin failed. If the password is furnished on the command line, it is used; otherwise you are prompted for a password. An account may also be furnished if required by the server; it is not required by Kermit and is not prompted for if omitted. Synonyms: USER, FTP LOGIN. FTP ACCOUNT text Sends an account name to a server that supports accounts. If the server does not support accounts, an error response occurs. If the server does support accounts, the account is accepted if it is valid and rejected if it is not. The account might be used for charging purposes or it might be a secondary password, or it might be used for any other purpose, such as an access password for a particular disk. Servers that support accounts might or might not allow or require the account to be sent prior to login; usually it is sent after login, if at all. Synonym: ACCOUNT. Example: set ftp autologin off ; One thing at a time please ftp xyzcorp.com ; Try to make the connection if fail exit 1 FTP connection failed ; Check that it was made ftp user olga secret ; Now log in to the server if fail exit 1 FTP login failed ; Check that it worked ftp account 103896854 ; Login OK - send account if fail echo WARNING - FTP ACCT failed ; Warn if problem ... ; (have session here) bye ; Log out and disconnect The following commands are used to control or get information about the FTP connection. Any particular FTP server does not necessarily support all of them. FTP RESET Terminates a user session but leaves the connection open, allowing a new login via FTP USER. FTP IDLE [ number ] Most FTP servers automatically log you out and and disconnect your session if there has been no activity for a certain amount of time. Use this command to ask the server to set its idle limit to the given number of seconds. Omit the number to ask the server to inform you of its current idle limit. FTP STATUS [ filename ] Asks the FTP server to send information about the current session. The result is a free-format report that might include server identification, username and login time, FTP protocol settings, and file-transfer statistics. If a filename is given, the server is supposed to send detailed information about the file. FTP SYSTEM Asks the FTP server to identify its operating system (Listed in Internet Assigned Numbers, Operating System Names). Examples: UNIX, VMS, VM/CMS, WINDOWS-NT. Unfortunately many variations are allowed (e.g. LINUX-2.0, LINUX-2.2, FREEBSD, ULTRIX, etc, instead of UNIX; WINDOWS-NT-3, WINDOWS-NT-3.5, WINDOWS-NT-3.51, WINDOWS-NT-4, etc). The report might also include other information like "Type L8", "Type I", or "Type A", indicating the file-transfer mode. FTP HELP [ keyword [ keyword [ ... ] ] Asks the server to list the commands it supports. The response is usually cryptic, listing FTP command mnemonics, not the commands used by the client (since the server has no way of knowing anything about the client's user interface). For example, the PUT command is STOR in FTP protocol. If a keyword is given, which should be an FTP protocol command, slightly-more- detailed help is given about the corresponding command (if the FTP server supports this feature). Examples: "ftp help", "ftp help stor". FTP SITE text (Advanced) Sends an FTP SITE (site-specific) command. Usually this means that the FTP server is asked to run an external command with the given arguments. You might be able to find out what SITE commands are available by sending "ftp help site" to the server, but in general the availability of and response to SITE commands is (not surprisingly) site specific. FTP QUOTE text (Advanced) Sends an FTP command in FTP protocol format. Use this command to send commands to the server that the FTP client might not know about. SHOW FTP Lists client (Kermit) FTP settings and information. Also SHOW CONNECTION, SHOW COMMUNICATIONS. HELP FTP [ keyword ] Asks Kermit to list and describe its built-in FTP commands. HELP SET FTP [ keyword ] Asks Kermit to list and describe its built-in SET FTP commands. [ [240]Top ] [ [241]FTP Top ] [ [242]C-Kermit Home ] [ [243]Kermit Home ] 3.1.5. Making Proxy Connections The simplist form of firewall traversal is the HTTP CONNECT command. The CONNECT command was implemented to allow a public web server which usually resides on the boundary between the public and private networks to forward HTTP requests from clients on the private network to public web sites. In order to allow secure web connections to be established, the CONNECT command works by authenticating the client with a username/password and then establishing a tunnel to the desired host. Many web servers support the CONNECT command and it can be configured to allow outgoing connections to authenticated user to any TCP/IP hostname/port combination accessible to the web server. The limitations of HTTP CONNECT is that it can only be used for outgoing connections for protocols that are implemented using TCP/IP. Protocols such as Kerberos authentication that use UDP/IP cannot be tunneled using HTTP CONNECT. Kermit provides support for the use of HTTP CONNECT proxy services with the command: SET TCP HTTP-PROXY [/USER:username /PASSWORD:password] hostname/ip-address[:po rt] When a port is not specified the default port configured on the HTTP server is used. This is frequently port 443. When a hostname is specified, it is resolved using the DNS available to the web server. 3.2. Making Secure FTP Connections Also see: [244]Accessing IBM Information Exchange with Kermit. In the previous section, you can see several examples of traditional insecure authentication: username and password sent across the network in clear text. Of course this is bad practice on at least two counts: (1) storing passwords in files (such as script files) gives access to the target systems to anybody who can obtain read access to your scripts; and (2) sending this information over the network leaves it open to interception by network sniffers or compromised hosts. Because of the increasing need for security on the Internet, FTP servers are beginning to appear that offer secure forms of authentication, in which no information is sent over the network that would allow anyone who intercepts it to usurp your identity and gain your access rights. Kermit provides an equivalent form of FTP security for each type of IETF standard security implemented in Telnet. These include GSSAPI-KERBEROS5, KERBEROS4, Secure Remote Password (SRP), and Transport Layer Security (SSL and TLS). It does not presently include SSL tunneling nor any form of SSH v1 or v2. When Kermit is built with the necessary libraries, secure FTP connections are attempted by default, in which all connections are authenticated and the command and data channels are private. The use of authentication and encryption for FTP connections can be adjusted with the commands listed below, which are available only if your version of Kermit was built with the corresponding security options and libraries: SET FTP AUTHTYPE { AUTOMATIC, GSSAPI-KRB5, KERBEROS4, SRP, SSL, TLS } Specifies an ordered list of authentication methods to be attempted when AUTOAUTHENTICATION is ON. The default list is: GSSAPI-KRB5, SRP, KERBEROS_V4, TLS, SSL. If none of the selected methods are supported by the server, an insecure login is used as a fallback. Note, by the way, that SSL or TLS can be used to secure an anonymous connection. SET FTP AUTOAUTHENTICATION { ON, OFF } Tells whether authentication should be negotiated by the FTP OPEN command. Default is ON. Use SET FTP AUTOAUTHENTICATION OFF to force a clear-text, unencrypted connection to FTP servers (such as the one at the Kermit FTP site) that normally would try to negotiate secure authentication and encryption. SET FTP AUTOENCRYPTION { ON, OFF } Tells whether encryption (privacy) should be negotiated by the FTP OPEN command, which can happen only if secure authentication is also negotiated. Default is ON. SET FTP AUTOLOGIN { ON, OFF } Tells Kermit whether to try logging in automatically when you make an FTP connection, as opposed to letting you do it "by hand" with the FTP USER command. SET FTP COMMAND-PROTECTION-LEVEL { CLEAR, CONFIDENTIAL, PRIVATE, SAFE } Determines the level of protection applied to the command channel: CLEAR Data is sent in plaintext and not protected against tampering. CONFIDENTIAL Data is encrypted but not protected against tampering. PRIVATE Data is encrypted and is protected against tampering. SAFE Data is sent in plaintext but protected against tampering. The default is PRIVATE. SET FTP CREDENTIAL-FORWARDING { ON, OFF } Tells whether end-user credentials are to be forwarded to the server if supported by the authentication method (GSSAPI-KRB5 only). This is often required to allow access to distributed file systems (e.g. AFS.) SET FTP DATA-PROTECTION-LEVEL { CLEAR, CONFIDENTIAL, PRIVATE, SAFE } Tells what level of protection is applied to subsequent data channels. The meanings of the protection-level keywords are the same as for SET FTP COMMAND-PROTECTION-LEVEL. The default is PRIVATE. SET FTP SRP CIPHER name Specifies the cipher to be used for encryption when SRP authentication is in use. The list of possible choices is computed based on the capabilities of the local SRP library and includes NONE plus zero or more of the following: BLOWFISH_ECB CAST5_ECB DES_ECB DES3_ECB BLOWFISH_CBC CAST5_CBC DES_CBC DES3_CBC BLOWFISH_CFB64 CAST5_CFB64 DES_CFB64 DES3_CFB64 BLOWFISH_OFB64 CAST5_OFB64 DES_OFB64 DES3_OFB64 The default is DES3_ECB. SET FTP SRP HASH name Specifies the hash to be used for data protection when SRP authentication is in use. The choices are MD5 and SHA. The default is SHA. Command-line options: -k name Specifies the realm to be used with Kerberos 4 authentication (= SET AUTH K4 REALM name). -f Enables forwarding of Kerberos 5 credentials to the host when using GSSAPI authentication (= SET AUTH K5 FORWARDABLE ON). -x Enables autoencryption (= SET FTP AUTOENCRYPTION ON). -c cipher Specifies the kind of cipher to be used for encryption with SRP authentication. Equivalent to SET FTP SRP CIPHER, with the same choices. If this option is not given, CAST5_CBC is used. -H hash Specifies the hash to be used for encryption with SRP authentication. Equivalent to SET FTP SRP HASH, with the same choices. If this option is not given, SHA is used. -z debug Turns on SSL/TLS debugging. -z secure Requires secure connection. -z certsok Says to accept all certificates without checking validity. -z verify=n Sets certificate verification mode to the given number, n: 0 = no verification 1 = verify certificate if presented 2 = require verification of certificate -z cert=filename Specifies a file containing a client certificate to be presented to the FTP server. -z key=filename Specifies a file containing a private key matching the client certificate. -z !krb4 (nokrb4) Disables the use of Kerberos 4. -z !gss -z nogss Disables the use of GSSAPI - Kerberos 5. -z !srp -z nosrp Disables use of SRP. -z !ssl -z nossl Disables the use of SSL. -z !tls -z notls Disables the use of TLS. Caution: If your FTP connection is secured via AUTH TLS, it is not possible to interrupt a file transfer. This is a limitation of all known FTP servers that support AUTH TLS. Note that when using certain security methods, such as SSL or TLS, you may be prompted to confirm or verify certain actions or conditions, for example, whether to accept self-signed certificates. This can interfere with unattended operation of scripts; see [245]Section 3.10. [ [246]Top ] [ [247]FTP Top ] [ [248]C-Kermit Home ] [ [249]Kermit Home ] 3.3. Setting FTP Preferences FTP preferences can be set globally and persistently with the commands in the following sections; many of these can also be overridden on a per-command basis with switches that have the same name. 3.3.1. Logs, Messages, and Other Feedback You can control the amount of feedback received from your FTP session with the commands in this section. First, you can create a log of your FTP transfers with the following commands: SET TRANSACTION-LOG { VERBOSE, FTP, BRIEF } Selects the log format. VERBOSE is the default, and is described in [250]the manual. FTP chooses a WU-FTPD format, the same as is used by the popular FTP server. BRIEF creates per-file records in comma-separated-list format. For greater detail, see [251]Section 4.17 of the [252]C-Kermit 7.0 Update Notes. LOG TRANSACTIONS filename Records FTP (or Kermit, or any other protocol) uploads and downloads in the given file using the format selected by the most recent SET TRANSACTION-LOG command, if any, or else the default format. FTP screen messages and displays are controlled by the following commands: SET TRANSFER DISPLAY { FULLSCREEN, CRT, SERIAL, BRIEF, NONE, OFF } FTP transfers use Kermit's normal file-transfer display styles. Use this command to choose the desired format; the default on most platforms is FULLSCREEN. The display is automatically disabled if Kermit is running in the background or in batch. BRIEF is always used for command-line initiated transfers (unless suppressed by -q). While a file-transfer is in progress, you can interrupt it in the normal Kermit way by typing one of the following keys or key combinations: X - Cancel current file but go on to the next one (if any). Z - Cancel the entire transfer. Ctrl-L or Ctrl-W - Refresh the file-transfer display (if any). SET FTP DISPLAY { FULLSCREEN, CRT, SERIAL, BRIEF, NONE, OFF } Like SET TRANSFER DISPLAY, but applies only to FTP connections, and does not affect Kermit- or other protocol file transfers. SET QUIET { ON, OFF } This command applies to Kermit in general, not just FTP. OFF by default; when ON, it suppresses most messages from most commands as well as the file-transfer display. SET FTP PROGRESS-MESSAGES { ON, OFF } Tells whether Kermit should print locally-generated feedback messages for each non-file-transfer command. ON by default. SET FTP VERBOSE-MODE { ON, OFF } Tells whether to display all responses from the FTP server. OFF by default. This shows all responses to all commands, except when the file-transfer display is active, and unless you have SET QUIET ON. When OFF, responses are shown only for commands such as FTP PWD whose purpose is to display a response. SET FTP DEBUG { ON, OFF } Tells whether local client debugging information should be displayed. OFF by default. When ON, the commands that are sent to the server are shown, as well as its responses (even if VERBOSE-MODE is OFF), plus additional informational messages are printed regarding the progress of secure operations. Also, the temporary file created by the [253]MGET command is not deleted so you can see what's in it. Set all of these to OFF when silent running is desired. 3.3.2. Operational Preferences FTP DISABLE new-protocol-feature-name FTP ENABLE new-protocol-feature-name Explained in [254]Section 3.11. SET FTP AUTOLOGIN { ON, OFF } If you give this command prior to opening an FTP connection, it controls whether Kermit tries to log you in automatically as part of the connection process. Normally ON, which means the username and password are sent automatically (and prompted for if they are not yet known). When OFF, FTP OPEN connects to the server without logging in. OFF is equivalent to the -n command-line option when using Kermit's FTP command-line personality. See [255]Section 3.1.4 for usage. SET FTP PASSIVE-MODE { ON, OFF } ON by default, to avoid random TCP port assignment for data connections, which can prevent FTP protocol from working through firewalls and network address translators (for more on these topics, see the [256]Kermit security reference. Set to OFF in case the FTP server does not support passive mode, or in case the client has problems with it (it has been observed, for example, that when using passive mode, the SCO XENIX 2.3.4 TCP/IP stack hangs in the connect() call forever). Synonyms: PASSIVE [ ON ], PASSIVE OFF, PASV [ ON ], PASV OFF. SET FTP SEND-PORT-COMMANDS { ON, OFF } This command determines whether the FTP client sends a new PORT command to the server when accepting incoming data connections (as when not using passive mode.) When PASSIVE-MODE is OFF and SET SEND-PORT is OFF, the port that was originally specified is reused. This is the default behavior for normal FTP clients but it is not compatible with many firewalls. SET FTP CHARACTER-SET-TRANSLATION { ON, OFF } Whether to translate character sets when transferring files with FTP (explained in [257]Section 3.7). OFF by default. SET FTP SERVER-CHARACTER-SET name Tells Kermit the character set used by the FTP server, UTF-8 by default ([258]Section 3.7). SET FTP SERVER-TIME-OFFSET delta-time Tells Kermit to apply the given [259]delta time to file timestamps provided by the server for its files; for use when (for example) the server does not have its timezone set correctly. SET FTP ERROR-ACTION { PROCEED, QUIT } When transferring a group of files with FTP, and an error occurs with one of the files, Kermit normally goes on the next file. Use SET FTP ERROR-ACTION to QUIT to make Kermit stop the transfer immediately and fail if an error occurs with any single file in the group. Example: you have given Kermit a list of files to send, and one of the files can not be found, or read permission is denied. Note that canceling a file by typing 'X' during transfer is not considered an error (if you want to cancel the entire transfer, type 'Z' or Ctrl-C). SET FTP PERMISSIONS { AUTO, ON, OFF } When uploading files with PUT or MPUT, this tells whether Kermit should send each file's permissions. The default is OFF, which means not to send permissions, in which case the uploaded file's permissions are set by the FTP server according to its own criteria. ON means to send them, AUTO means to send them only if the client (Kermit) and server are on like platforms (e.g. both UNIX). This command has no effect when downloading, since the FTP protocol does not include a way for the server to inform the client of a file's permissions. Also see [260]FTP PUT /PERMISSIONS. Note that setting permissions after uploading is likely to work (correctly or at all) only when the client and server platforms are alike (e.g. both of them are some form of UNIX). Also note that Windows files don't have permissions. Also see [261]FTP CHMOD. SET FTP DATES { ON, OFF } When downloading files with GET or MGET, this tells whether Kermit should try to set the received file's date from the server's date. FTP DATES is ON by default. Note, however, that FTP protocol does not allow date preservation when uploading. So at best, SET FTP DATES ON can work only when downloading, and then only when the server agrees to furnish file dates. SET FTP FILENAMES { AUTO, CONVERTED, LITERAL } When uploading (sending) files, this tells whether to convert outbound filenames to "common form". This means allowing only one period in a name, uppercasing any lowercase letters, replacing spaces by underscores, etc. AUTOMATIC is the default, meaning LITERAL when client and server are the same type of system (e.g. UNIX) and CONVERTED otherwise. Special case: if the setting is AUTOMATIC and the client is not UNIX and the server identifies itself as UNIX, Kermit uses a less-strict form of conversion, in which lowercase letters are not uppercased and the filename can contain any number of periods, but spaces are still converted to underscore. When receiving, conversion generally means to change all-uppercase names to lowercase and spaces to underscore. SET FTP UNIQUE-SERVER-NAMES { ON, OFF } Applies only to uploads. Tells the server to create new, unique names for incoming files that have the same names as existing files. OFF by default, in which case the server overwrites existing files with new files of the same name. When ON, the server uses its own built-in method for creating new names for incoming files; for example, appending a period (.) and a number to the name. CAUTION: Use this option only if you do not need to refer to the file after it is uploaded, since FTP protocol provides no mechanism for the client to find out what name was assigned by the server. SET FTP COLLISION { ... } When downloading, what to do if an incoming file has the same name as an existing file. Options are the same as for SET FILE COLLISION. If this command is not given, Kermit's regular FILE COLLISION setting is used. If this command is given, it overrides the FILE COLLISION setting for FTP transfers only. See [262]Section 3.6.2 for details. SET FTP TYPE { TEXT, BINARY, TENEX } Changes the default transfer mode. When sending (uploading) files, this command has no effect unless you disable automatic text/binary mode switching ([263]Section 4) with SET FILE SCAN OFF or SET TRANSFER MODE MANUAL. When receiving (downloading) files, this command establishes the transfer mode to be used when a filename does not match any of Kermit's text or binary filename patterns, unless you use SET FTP GET-FILETYPE-SWITCHING or SET TRANSFER MODE MANUAL to disable automatic switching, in which case, this command establishes the transfer mode for all downloaded files. In all cases, however, the FTP TYPE can be overridden in any GET or PUT command by including a /TEXT (/ASCII), /BINARY, or /TENEX switch. The FTP TYPE is independent of the Kermit FILE TYPE setting. TENEX is used for sending 8-bit binary files to 36-bit platforms such as TOPS-10, TOPS-20, and TENEX, and getting them back again. Synonym: ASCII = TEXT. Note: there is also an FTP TYPE command, which does what SET FTP TYPE does but also sends a TYPE command to the server immediately if the given type is different from the current one. If you want want specific FTP preference settings to be in effect for all your Kermit FTP sessions, put the desired SET FTP commands in your Kermit customization file (~/.mykermrc in UNIX, K95CUSTOM.INI in Windows). [ [264]Top ] [ [265]FTP Top ] [ [266]C-Kermit Home ] [ [267]Kermit Home ] 3.4. Managing Directories and Files In Kermit, commands for directory and file management can refer to: * The local computer * A remote computer when you have a connection to a Kermit server or IKSD. * A remote computer when you have a connection to an FTP server. (There can also be an HTTP connection, but the commands in this section don't apply to HTTP connections.) Thus in general, each such command comes in three forms: 1. With no prefix in C-Kermit 8.0.200, it refers to the local computer (CD, DIR, etc). In C-Kermit 8.0.201 and later, however, the "locus" switches to automatically to the remote FTP server when you make an FTP connection (see the SET LOCUS description [268]Section 7); thus C-Kermit 8.0.201 acts almost exactly like a regular FTP client when it has an FTP connection, yet still acts like itself on other kinds of connections. 2. With the REMOTE prefix, it is for a Kermit server (REMOTE CD, REMOTE DIR). 3. With the FTP prefix, it's for an FTP server (FTP CD, FTP DIR). 4. Also see [269]Section 3.8, which explains "R-commands" and "L-commands". Kermit's FTP file and directory management commands are as follows. When an R-command is included in the Synonyms list, be sure to read [270]Section 3.8 about rules for use of R-commands. FTP CD [ directory ] Tells the FTP server to change its default (working) directory to the one given, which usually must be expressed in the syntax of the server platform (UNIX, VMS, etc). If the directory is not specified, the result depends on the FTP server -- it might complain that the command is illegal, or it might change to your original login directory. Synonyms: FTP CWD (Change Wording Directory); RCD. FTP CDUP Tells the FTP server to change its default (working) directory to the parent directory of its current one (equivalent to "cd .." in UNIX, or "cd [-]" in VMS). Synonyms: RCDUP, FTP UP. FTP PWD Asks the FTP server to report ("print") its current working directory. Synonym: RPWD. FTP MKDIR directory Asks the FTP server to create the directory whose name is given. In general, the name must be in the syntax of the server's file system, and it must be either absolute (a full pathname) or relative to the server's current (working) directory. This command fails if the directory can't be created for any reason, including that it exists already. Synonym: RMKDIR. FTP RMDIR directory Asks the FTP server to remove the directory whose name is given. The rules are the same as for MKDIR, plus in most cases, the server will not remove any directory unless it is empty. Synonym: RRMDIR. FTP DIRECTORY [ filespec ] [ redirectors ] Tells the FTP server to send a directory listing of the specified files. If no filespec is given, the server lists all files in its current working directory. The results are in whatever format the server chooses to send them. You can use UNIX-like redirectors to send the listing to a file or a pipeline, exactly as with the regular Kermit client/server REMOTE DIRECTORY command ([271]Using C-Kermit, Chapter 11). Synonym: RDIRECTORY. Examples: ftp dir ; Show listing of all files on screen ftp dir *.txt ; List *.txt files on screen ftp dir *.txt > somefile ; Put listing in somefile ftp dir *.txt >> somefile ; Append listing to somefile ftp dir *.txt | sort > somefile ; Put sorted listing in somefile ftp dir | more ; Runs list through "more" ftp dir | sort | more ; Runs list through "sort" and "more" FTP VDIRECTORY [ filespec ] [ redirectors ] "Verbose" directory. This is an alternative FTP DIRECTORY command primarily for use with DECSYSTEM-20 (TOPS-20) FTP servers, which send only filenames when given a DIRECTORY command; the VDIRECTORY command makes them also send file sizes, dates, and attributes. FTP CHECK filespec Asks the FTP server whether the given file exists or, if the filespec contains wildcards, if any files match, and this command succeeds or fails accordingly. FTP MODTIME filename Asks the FTP server, via the not-yet-standard FTP MDTM command, to send the modification date and time of the given file. The response should be a numeric string in the format: yyyymmddhhmmssxxxxx... where yyyy is the year, mm is the month, dd is the day, hh is the hour (0-23), mm is the minute, ss is the second, and xxx... is the optional fraction of the second (0 or more digits). The date and time is expressed in UTC (GMT, Zulu, Zero-Meridian). The result is available programmatically in the [272]\v(ftp_message) variable, and is understandable by Kermit's date-time switches and functions. For example, suppose we want to upload all local files that are newer than a particular file on the server: C-Kermit> ftp modtime signpost C-Kermit> echo \v(ftp_message) 20010807113542.014 C-Kermit> ftp mput /after:\v(ftp_message)GMT * Note that we must append "GMT" to the date-time string to let the /AFTER switch know the time is GMT rather than local. FTP SIZE filename Asks the FTP server to send the size (in bytes) of the given file. The result might vary depending on whether the current FTP TYPE is binary or text ("ascii"). For a reliable byte count, do FTP TYPE BINARY first. The result is available programmatically in the [273]\v(ftp_message) variable. FTP CHMOD permissions filename Tells the FTP server to set the permissions (protection) of the given file to the ones given. The permissions and filename must be given in whatever syntax is required by the server. Example (for a UNIX-based FTP server): ftp chmod 664 oofa.txt Not all servers support this command. For non-UNIX-based servers, you might need to use FTP QUOTE or FTP SITE and the appropriate platform-specific FTP server command. FTP UMASK [ number ] This command is probably specific to UNIX-based servers; it sets the UNIX "umask", which is the default permissions mask for new (in this case, incoming) files. Crudely put, the UNIX umask is an octal representation of a binary number in in which a 1 bit stands for a permission bit that must be 0, and a 0 bit stands for a permission bit that can be 0 or 1 depending on other factors, such as the permissions of the parent directory. Example: "umask 007" requires that new files are created without read/write/execute world permission. If the number is not specified, the server's current umask is reported. FTP RENAME filename newname Asks the FTP server to rename the file whose name is "filename" to "newname". Works only for one file; can not be used with wildcards. The server's interpretation of "newname" can vary (in some cases it must be a filename, in others perhaps it can also be a directory name, in which case if the filename denote a regular file, the file might be moved to the given directory). Some servers might allow files to be renamed ("moved") between physical disks or partitions, others might not. Synonym: RRENAME. FTP DELETE [ switches ] filespec [ filespec [ ... ] ] Tells the FTP server to delete the file or files listed. Each file specification may, but need not, contain wildcard characters to match multiple files. File specifications and wildcard syntax must be those of the server. Any file specifications that contain spaces must be enclosed in braces or doublequotes. FTP DELETE switches are: /ERROR-ACTION: /FILENAMES: /NOBACKUPFILES /QUIET /EXCEPT: /LARGER-THAN: /NODOTFILES /NOPAGE /PAGE /RECURSIVE /SMALLER-THAN: When used with FTP DELETE, the /RECURSIVE switch deletes files but not directories, and furthermore depends on the server providing recursive file lists, which is not the normal behavior. For further details, see the descriptions of these switches in [274]Section 3.6. Synonyms: FTP MDELETE (Kermit makes no distinction between DELETE and MDELETE); RDELETE. FTP TYPE { TEXT, BINARY, TENEX } Tells the FTP server to change its file-transfer type to the one given, immediately. See [275]SET FTP TYPE for details. [ [276]Top ] [ [277]FTP Top ] [ [278]C-Kermit Home ] [ [279]Kermit Home ] 3.5. Uploading Files With FTP Uploading means sending files from the client (Kermit) to the FTP server. The basic command for uploading files with FTP is PUT: FTP PUT [ switches ] [ filespec [ as-name ] ] Uploads (sends) the file or files that match the file specification, which may include wildcards, to the server. If no filespec is given, the names of files to send are taken from the /LISTFILE: file, if any, otherwise from the SEND-LIST, if any. Unless you go out of your way to prevent it, Kermit determines the transfer mode (text or binary) for each file automatically, and switches automatically on a per-file basis. If an as-name is given, the file is sent under that name instead of its own (if an as-name is given with a wildcard filespec, the result is a bit more complicated, and is explained later in this section). Unlike normal FTP clients, Kermit does not prompt you by default (or at all) for each file; it just sends them, just as it does with Kermit protocol. The filespec can be a literal filename or a Kermit pattern, described in: [280]http://www.columbia.edu/kermit/ckermit70.html#x4.9 Kermit patterns are equivalent to C-Shell patterns and provide a fair amount of flexibility in selecting which files to send, which is augmented by the file-selection switches presented in [281]Section 3.5.1. FTP MPUT [ switches ] filespec [ filespec [ ... ] ] FTP MPUT is just like FTP PUT except it allows you to give more than one file specification, and it does not allow an as-name in the file list. However, as-names can be given to either PUT or MPUT with the /AS-NAME: switch. If a PUT or MPUT command results in one file being uploaded, it succeeds if the file is uploaded completely and fails otherwise. If more than one file is selected for upload, success or failure depends on the [282]FTP ERROR-ACTION setting; if it is PROCEED (the default setting), then the [M]PUT command succeeds if at least one of the files was completely uploaded, and fails otherwise, If FTP ERROR-ACTION is QUIT, the [M]PUT command succeeds if all selected files were uploaded successfully, and fails if any file failed. FTP uploads may be interrupted just like Kermit uploads. While the transfer is in progress, type: X to interrupt the current file and go on to the next file. Z to cancel the current file and all remaining files. ^C (Control-C): Like Z, but might act more quickly. MPUT may be used as in regular FTP clients, but it is not required to send multiple files; in Kermit it is required only if you want to give multiple file specifications. Examples: ftp put oofa.txt ; Send a single file oofa.txt ftp put oofa.txt budget.txt ; Send single file oofa.txt as budget.txt ftp put *.txt ; Send all *.txt files ftp mput *.txt ; Send all *.txt files (same as "put *.txt") ftp mput *.txt foo.bar ; Send all *.txt files plus foo.bar The distinction between PUT and MPUT is important only when more than one filespec is given, just like the distinction between Kermit SEND and MSEND: ftp put oofa.txt budget.txt ; Send oofa.txt AS budget.txt ftp mput oofa.txt budget.txt ; Send oofa.txt AND budget.txt If the source file specification includes any path segments, for example: put /tmp/oofa.txt put subdir/another/andanother/oofa.txt the path portion is stripped from the filename that is sent to the server. However, if an as-name contains a path, it is retained. Examples: ftp put /usr/doc/oofa.txt ; Send as "oofa.txt". ftp put oofa.txt /tmp/oofa.txt ; Send as "/tmp/oofa.txt" The latter example sends the file oofa.txt from your current local directory to the server's /tmp directory. This works only if the server uses the same directory notation that you used in the as-name AND the given directory already exists on the server AND if you have write access to it. Use caution when uploading from a case-sensitive file system, such as UNIX, to a file system that is not case sensitive, such as Windows or VMS. If you have two files in UNIX, AA and aa and upload both of them, the second one will overwrite the first. The only way around this provided by FTP protocol is its "unique server names" feature (SET FTP UNIQUE-SERVER-NAMES or the /UNIQUE switch described below). 3.5.1. FTP PUT Switches FTP PUT and MPUT are similar in format and behavior to the regular Kermit SEND and MSEND commands, and they allow most of the same optional switches: C-Kermit>ftp put ? Filename, or switch, one of the following: /after: /larger-than: /rename-to: /array: /listfile: /server-character-set: /as-name: /local-character-set: /server-rename-to: /before: /move-to: /simulate /binary /nobackupfiles /smaller-than: /command /nodotfiles /tenex /delete /nofollowlinks /text /dotfiles /not-after: /transparent /error-action: /not-before: /type: /except: /permissions: /update /filenames: /quiet /unique-server-names /filter: /recover /followlinks /recursive Since most of these switches are common to Kermit's SEND and MSEND commands, they described only briefly here. For greater detail see: [283]http://www.columbia.edu/kermit/ckermit70.html#x1.5 (explanation of switches) [284]http://www.columbia.edu/kermit/ckermit70.html#x4.7 (file-transfer switches) First the file-selection switches: /AFTER:date-time /BEFORE:date-time /NOT-AFTER:date-time /NOT-BEFORE:date-time Only send those files modified on or after or before the given date and time. These switches can be combined to select files modified between two date/times. Various date-time formats are accepted; if the date-time contains spaces, it must be enclosed in braces or doublequotes. See [285]http://www.columbia.edu/kermit/ckermit70.html#x1.6 and [286]Section 8.13 of this document for details about date-time formats. Examples: ftp put /after:{1 jan 2000 0:00:00} * ftp put /after:-5days * /LARGER-THAN:number /SMALLER-THAN:number Only send files larger (smaller) than the given number of bytes (octets). These switches can be combined to select files in a certain size range. /TYPE:{TEXT,BINARY} Only send files that are the given type, which is determined for each file just before sending it by file scanning. BINARY includes TENEX; if you have included a /TENEX switch, or previously given a [SET] FTP TYPE TENEX command, binary files are sent in TENEX, rather than BINARY mode. /[NO]DOTFILES [Don't] include files whose names begin with dot (.). By default, such files are not included unless your filespec explicitly mentions them. /NOBACKUPFILES Don't include files whose names end with .~nnn~, where nnn is a number, e.g. oofa.txt.~27~. These are backup files created by Kermit, EMACS, and other applications. By default, backup files are included. /NOFOLLOWLINKS (UNIX only) Skip over symbolic links rather than following them (default). This applies to wildcard and/or recursive [M]PUTs; if a single filename is given, and it happens to be a symbolic link, the file it points to is sent. /FOLLOWLINKS (UNIX only) Always follow (resolve) symbolic links, even in wildcard or recursive [M]PUTs. Use with caution. Watch out for circular links, endless loops, etc. /EXCEPT:pattern Exception list -- don't send files whose names match the given pattern. See [287]Section 1.5.4 of the [288]C-Kermit 7.0 Update Notes for details. If you want to exclude a directory from a recursive [M]PUT, use /EXCEPT:{dirname/*}. /RECURSIVE Sends the desired files from the current (or given) directory, plus all directories beneath it, including empty directories, replicating the directory structure on the server. No special capabilities are required in the server, but of course your login ID on the server must have the appropriate access and permission to create directories. Recursive PUTs work not only between like platforms (e.g. UNIX to UNIX) but also between unlike ones (e.g. UNIX to VMS or Windows), in which case text-file format differences are handled by Kermit's automatic text/binary mode switching ([289]Section 4) and character-set translation ([290]Section 3.7). Synonym: /SUBDIRECTORIES. /UPDATE Send only files that have changed since last time ([291]Section 3.5.2). /ARRAY:arrayname The "file" to be sent is an array, or a segment of one, rather than a real file. In this case the other selection switches don't apply. The array contents are sent in text mode, and each array element is treated as a line. Example: ftp put /as-name:array.txt /array:&a (or, to send a segment of the array, /array:&a[100:199]). If you don't include an /AS-NAME, a name of "_array_x_" is used (where x is the array letter). If you include this switch, most other switches are meaningless and ignored. /COMMAND The "file" to be sent is the standard output of a command, rather than a real file. It is sent in text or binary mode according to the prevailing FTP TYPE, which can be overridden with a /TEXT or /BINARY switch. Example: Example: ftp put /command /as-name:{userlist} {finger | sort -r} /LISTFILE:filename Tells Kermit to obtain the list of files to be sent from the file whose name is given. This file must contain one file specification (which may be wild) per line. If the list includes files from different directories, such as a recursive listing of a directory tree, the paths are recreated on the server (if possible) if you include the /RECURSIVE switch; otherwise all the files are sent to the current directory on the server. Now the other switches: /AS-NAME:text If a single file is being sent, send it with the given text as its name. If multiple files are being sent, the text must be a template that includes variables such as \v(filename), \v(filenumber), \v(ntime), to allow dynamic creation of each name. The same applies to the as-name field of the FTP PUT command. If this switch is not included (and an as-name is not included as the second filename to PUT), each file is sent with its own name. /BINARY /TEXT /TENEX Forces this upload to take place in the given mode, regardless of the current FTP TYPE setting, and without automatic text/binary switching. /ASCII is a synonym for /TEXT. /FILTER:command Specifies that the file(s) is/are to be passed through the given command or pipeline on their way to the server. Example: ftp put /binary /filter:{gzip -c \v(filename)} /as-name:\v(filename).gz * /TRANSPARENT /LOCAL-CHARACTER-SET:name /SERVER-CHARACTER-SET:name Character-set translation for text files, explained in [292]Section 3.7. /ERROR-ACTION:{PROCEED,QUIT} Overrides the prevailing [293]FTP ERROR-ACTION for the duration of this PUT or MPUT command only. /RECOVER Resume an interrupted transfer where from the point of interruption (explained in [294]Section 3.5.2). Synonym: /RESTART. /DELETE Tells Kermit to delete each source file immediately after, and only if, it has been uploaded completely and successfully. This, in effect, moves the file from the client to the server. /MOVE-TO:directory Tells Kermit to move each source file to the named local directory after, and only if, it has been uploaded completely and successfully. /RENAME-TO:template Tells Kermit to rename each (local) source file according to the given template after, and only if, it has been uploaded completely and successfully. The template works as in /AS-NAME. /SERVER-RENAME-TO:template Tells Kermit to ask the server to rename each file according to the given template as soon as, and only if, it has been received completely and successfully. The template works as in /AS-NAME. Requires write and rename access on the server, so doesn't usually work with (e.g.) anonymous uploads to public incoming areas where the permissions don't allow renaming. Examples: ftp mput /server-rename:\v(filename).ok * Appends ".ok" to each filename on the server when it's finished uploading. ftp mput /as-name:\v(filename).tmp /server-rename:\v(filename) * This is the reverse of the previous example; it uses a temporary name while uploading is in progress and reverts the file to its real name when uploading is complete. ftp mput /as-name:\v(filename) /server-rename:../final/\v(filename) * Moves the file from the working directory to a final directory when the upload is complete, but in this case you have to know the pathname syntax of the server. If the rename fails, the [M]PUT command fails according to the [295]FTP ERROR-ACTION selection. /FILENAMES:{AUTOMATIC,CONVERTED,LITERAL} Overrides the [296]FTP FILENAMES setting for this upload only. /PERMISSIONS:{ON,OFF} Overrides the [297]FTP PERMISSIONS setting for this upload only. /UNIQUE Tells Kermit to tell the server to give [298]unique names to incoming files that would otherwise overwrite existing files that have the same name. This switch conflicts with /UPDATE, /RECOVER, /PERMISSIONS, and /SERVER-RENAME since the client has no way of knowing the name assigned by the server. /QUIET Don't display file-transfer progress or statistics. /SIMULATE Shows which files would be sent without actually sending them. Useful (for example) with /UPDATE (next section). The results are shown in the file-transfer display (if it is not disabled) and in the transaction log (if one is active). Hint: use SET TRANSFER DISPLAY BRIEF. 3.5.2. Update Mode When you include the /UPDATE switch, this means to skip sending any file that already exists on the server if the local file's modification date/time is not later than that of the corresponding file on the server. Here is a typical application for update mode: Suppose that on Computer A, you maintain a large set of files (say, a collection of Web pages and graphics images, or the source files for a software application), and you need to keep a parallel copy on another Computer, B. Of course you could upload the entire collection every day: cd source-directory ftp computerb.xyzcorp.com ( authentication details... ) ftp cd target-directory ftp put [ switches ] * But if the total size is large or the network slow, this would be unnecessarily time-consuming. Worse, if other users or sites had to update whenever new files appeared in B's directory, this would cause them unnecessary work. By including the /UPDATE switch: ftp put /update [ other-switches ] * only those files that changed since last time are uploaded. Here's how it works. For each local file that is selected for uploading: * The remote filename is determined in the normal way, according to the [299]FTP FILENAMES setting, /FILENAMES switch, or the as-name, if any. * Kermit sends an MDTM (modification time) command for the corresponding remote filename to the server. * If the server does not understand the MDTM command, the file is sent. * If the server can't find a file with the given name, the file is sent. * If the local file's modification time is later than that of the remote file, the file is sent. * Otherwise -- the remote file exists but its modification time is equal to or earlier than that of the local file -- the file is skipped. All time comparisons take place in Coordinated Universal Time (UTC)([300]1), also known as GMT or Zulu time: Timezone 0; standard time, without daylight savings. WARNING: Some FTP servers, such as Novell NWFTPD.NLM, ignore or misimplement the FTP specification and send local time rather than UTC. Update mode is useful only when always used in the same direction. When you upload (PUT) a file with FTP, the destination file receives the current timestamp on the server's computer, not the original file's timestamp ([301]2). If you try to FTP PUT /UPDATE the same file again, it will be skipped (as expected) since the remote copy is newer. However, if you try to FTP GET /UPDATE the same file ([302]Section 3.6), it will be transferred for the same reason. To check the availability of PUT /UPDATE on a particular connection, issue an FTP MODTIME command for a file that is known to exist on the server. If it succeeds, PUT /UPDATE should work and in that case, you can run a procedure like the one above every day: the first time, it sends all the files; after that, it sends only the ones that changed. If a transaction log is active, a notation is included for any files that are skipped. Notes: 1. Why is Coordinated Universal Time abbreviated UTC? From the [303]National Institute of Standards and Technology FAQ: "In 1970 the Coordinated Universal Time system was devised by an international advisory group of technical experts within the International Telecommunication Union (ITU). The ITU felt it was best to designate a single abbreviation for use in all languages in order to minimize confusion. Since unanimous agreement could not be achieved on using either the English word order, CUT, or the French word order, TUC, the acronym UTC was chosen as a compromise." 2. The Kermit FTP client is unusual in that, when downloading only, it can set the received file's date from the file's date on the server, but this should not affect the update feature. When uploading to an FTP server, however, there is no mechanism for the client to set the date of the uploaded file on the server. 3.5.3 Recovery Suppose that while you are uploading a large file over a slow connection, the connection is lost before the entire file is transferred. With most FTP clients, you would have to start over, thus resending the portion of the file that was sent already, and that is already on the server. But Kermit's /RECOVER switch (Synonym: /RESTART) lets you continue an interrupted transfer from the point of failure, thus transferring only the part that wasn't sent already. The prerequisites for recovery are: * The transfer must be in BINARY mode, or else the client and server must reside on like systems (e.g. both on some form of UNIX). * The FTP server must support the SIZE command. Here's how it works. When you include the /RECOVER switch: * Kermit checks for conflicting switches, such as /UPDATE and /UNIQUE; if /RECOVER is given with these switches an error occurs. If /RECOVER is given in other circumstances where it could serve no useful purpose (e.g. with arrays, pipes, or filters), it is ignored. If the switch is accepted, then for each selected file: * If it is not binary (determined by scanning) and the client and server are not on like platforms, recovery is canceled (the entire file is sent). Otherwise: * A SIZE command is sent for the file (using its remote name). If the reply indicates the file was not found, or the SIZE command was not understood, or any other kind of error, recovery is canceled. Otherwise: * A MDTM (modification time) command is sent for the file. If a valid reply is received, and the modification time of the local file is later than that of the remote file, recovery is canceled. Otherwise: * If the sizes of the two files are identical, the file is not sent. Otherwise: * Kermit seeks to the recovery spot in the local file, tells the server to APPEND the data which is about to arrive to the remote file, and then sends the data starting at the recovery point. To safeguard file integrity, recovery is not attempted unless all the preconditions are met. For the widest possible usefulness, APPEND is used rather than RESTART. For stream transfers (the only kind that Kermit supports) the results are the same. By design, the /RECOVER switch can be included with any FTP PUT or MPUT command, even if it specifies a group of files. This allows you to resume an interrupted batch transfer from where it left off. The files that were already completely sent are skipped, the file that was interrupted is recovered, and the remaining files are uploaded. By the way, it doesn't matter how the original partial file was uploaded -- FTP, Kermit, Zmodem, etc: as long as the preconditions are met, it can be recovered with FTP PUT /RECOVER, or for that matter also using Kermit protocol and SEND /RECOVER. A word of caution, however, when the original upload was in text mode with character-set translation ([304]Section 3.7): * If the original upload involved a translation from one single-byte character set to another (e.g. Code Page 850 to Latin-1), recovery is safe if you specify the same translations for the recovery. If you don't, the resulting file will contain a mixture of character sets. * If the original upload involved a translation that changed the size of the file (e.g. from an alphabetic Code Page or Latin Alphabet to Unicode, or vice versa), recovery is NOT safe, even if you specify the same translations. Kermit has no way of knowing anything about the previous upload. As a safeguard, an error occurs if you include /RECOVER and also specify a character-set of UCS2 or UTF8, since recovery can't possibly work in that situation. Otherwise, it's up to you to avoid unsafe recovery operations. [ [305]Top ] [ [306]FTP Top ] [ [307]C-Kermit Home ] [ [308]Kermit Home ] 3.6. Downloading Files With FTP Although uploading files with Kermit's FTP client is just as easy and flexible as sending files with Kermit protocol, the same is not always true for downloading because FTP servers lack some of the capabilities of a Kermit server: * If you want to get more than one file, you have to use MGET, not GET, since the underlying FTP protocol is different in the two cases. Kermit can't "autodetect" which one you mean, as it can with PUT and MPUT, since it can't be expected to know the wildcard syntax of the remote platform and/or FTP server (the same is true for all other FTP clients). To complicate matters, FTP protocol now includes two underlying mechanisms (NLST and MLSD) for accomplishing MGET operations and, as explained in [309]Section 3.11, the two behave differently. * Automatic text-binary mode switching is not done by the server. It can be done by the client (Kermit), but in this case it is not based on a file scan (since there is no way for Kermit prescan a server file), but rather on the filename, using C-Kermit 7.0 [310]filename patterns. * Some options that are available with FTP PUT can not be used with FTP [M]GET or don't work the same way: /PERMISSIONS (FTP protocol has no mechanism for this). /[NOT-]BEFORE, /[NOT-]AFTER (because of the timezone problem). /RECOVER works only in binary mode. /RECURSIVE has limited utility. The commands for downloading are: SET FILE DOWNLOAD-DIRECTORY [ directory ] As with Kermit transfers, this command, if given, tells C-Kermit where to store incoming files in the absence of a specific as-name. If not given, incoming files are stored as indicated by the as-name, if any, otherwise in the current directory, just as with Kermit transfers. The more verbose transfer display formats give the full pathname of each received file, and, in case you have trouble finding a downloaded file afterwards, its full path is also listed in the transaction log (if you kept one), and you can also ask Kermit where it went with the [311]WHERE command. SET FTP GET-FILETYPE-SWITCHING { ON, OFF } ON by default, causing Kermit to switch automatically into text or binary mode for each file based on whether its name matches a text pattern or binary pattern. Set this OFF, or use a /TEXT, /BINARY, or /TENEX switch to defeat this feature. Use SHOW PATTERNS to see the current pattern list. [ FTP ] GET [ switches ] filename [ as-name ] Asks the server to send the given file, and if it comes, stores it locally under the given as-name, if any, otherwise under its original name (modified according to the selected filename conversion option), in your download directory, if you have specified one, otherwise in the directory indicated in the as-name, if any, otherwise in your current directory. If you accidentally use a wildcard in the filename ("get *.txt") the server will reply with a message like "File not found" (unless there is a file whose name actually is "*.txt"). If FTP GET-FILETYPE-SWITCHING is ON, and in the absence of any GET switches to override it, the file is transferred in binary mode if it matches any of Kermit's binary name patterns, and in text mode if it matches any of Kermit's text name patterns, and in the prevailing FTP TYPE if it matches none of these patterns. [ FTP ] MGET [ switches ] filespec [ filespec [ filespec [ ... ] ] ] Like GET, but for multiple files. One or more file specifications can be given, and any or all (or none) of them can contain wildcards or can be directory names. The file list may not include an as-name, but you can still give one with the /AS-NAME: switch. In both the FTP GET and MGET commands, any filenames that contain spaces must be enclosed in braces or doublequotes (see [312]Section 5 for details). FTP downloads may be interrupted just like Kermit transfers. While the transfer is in progress, type: * X to interrupt the current file and go on to the next file. * Z (or Control-C) to cancel the current file and all remaining files. Before proceeding, a brief word about temporary files. In FTP protocol, the MGET command works by requesting a file list from the server, and then (internally) issuing a GET command (FTP RETR protocol directive) for each file. The file list returned by the server can be any size at all, so in case it is huge, we don't store it in memory; instead we put it in a temporary file. For troubleshooting purposes, you should be aware of two points: 1. The location of the temporary file is chosen according the TMP or TEMP environment variables. If neither of these variables is defined, you might need to define it. In case there is not enough space on the indicated disk or partition for the server's file list, you might need to either clean up the temporary area, or redefine the environment variable to indicate a different area that has sufficient space. 2. If you want to look at the list yourself, use SET FTP DEBUG ON. This tells Kermit to (a) give you the full pathname of the temporary file at the end of each MGET command, and (b) not to delete it, as it normally does. 3.6.1. FTP GET Switches The following switches are available with FTP GET and MGET: /TEXT Specifies a text-mode transfer. Overrides the global FTP TYPE setting and filename pattern-matching for the duration of the current command only, All files are downloaded in text mode. Synonym: /ASCII. /BINARY Specifies a binary-mode transfer. Overrides the global FTP TYPE setting and filename pattern-matching for the duration of the current command only. All files are downloaded in binary mode. /TENEX Like /BINARY but specifies a special binary transfer mode to be used when getting 8-bit binary files from a 36-bit platform such as TOPS-10, TOPS-20, or TENEX. All files are downloaded in the special binary mode. /RECOVER This instructs Kermit to try to recover an incomplete download from the point of failure. Works only in binary mode, and only if the server supports the (not-yet-standard) FTP "REST" directive. See [313]Section 3.6.3 for details. Synonym: /RESTART. /FILENAMES:{CONVERTED,LITERAL} Overrides the [314]FTP FILENAMES (filename conversion) setting for this download only, forcing incoming filenames to be either converted or taken literally. /AS-NAME:text For GET, this is equivalent to giving an as-name after the filename. For MGET, this is the only way to specify alternative names for the incoming files. With MGET, the /AS-NAME text should (must) contain a Kermit variable, usually \v(filename) or \v(filenumber). Example: mget /text /as-name:\v(filename).new *.c This gets all ".c" files and stores them with " .new" appended to their names. See the [315]C-Kermit 7.0 Update Notes for details. /COMMAND This specifies that the incoming file is to be written to the standard input of a command, rather than to a file. The command name is the as-name from the GET command or the /AS-NAME argument. If you need to refer to the incoming file's name in the command, use \v(filename). See the description of the regular Kermit [316]GET /COMMAND command for details and examples. /QUIET Transfers the files quietly; don't put up a file-transfer display. /ERROR-ACTION:{QUIT,PROCEED} This switch affects only MGET. If an error occurs with a particular file, this tells whether to go on to the next file (PROCEED) or to stop right away and fail (QUIT). The default is PROCEED. The file selection switches are: /EXCEPT:{pattern} or /EXCEPT:{{pattern}{pattern}{...}} Exception list for MGET; skip downloading any file whose name matches any of the given patterns (when using the second format, up to 64 patterns may be specified). [317]CLICK HERE for syntax details. /SMALLER-THAN:number Download only files whose size is smaller than the given number of bytes (octets). Requires that the FTP server support the SIZE or MLSD directive. /LARGER-THAN:number Download only files whose size is greater than the given number of bytes. Requires that the FTP server support the SIZE or MLSD directive. /NOBACKUPFILES During MGET, don't download any files whose names end with backup suffixes (.~n~ where n is a number). /NODOTFILES During MGET, don't download any files whose names begin with period (.). Equivalent to /EXCEPT:{.*}. /LISTFILE:local-filename The given file contains a list of files to GET, one per line. Filenames in the listfile can contain wildcard characters in the syntax of the server. There is no limit on the number of lines in the listfile. /NAMELIST:local-filename If this switch is given, then instead of actually retrieving the selected files, the GET command retrieves a list of the names of the files that would be retrieved, and places it in the specified file. The resulting file is an ordinary text file, with one filename per line, suitable for reading by a person, or processing by a computer program, including Kermit itself (FOPEN / FREAD / FWRITE / FCLOSE), and as /FILELIST: file. If the filename is omitted or given as "-" (dash, hyphen), the list goes to the screen. NOTE: if you want a copy of the complete list sent by the server, use SET FTP DEBUG ON, perform an MGET, and the temporary file containing the list will be kept rather than deleted (and Kermit tells you its name). /UPDATE, /COLLISION:keyword Explained in [318]Section 3.6.2. /RECURSIVE This means to try to download an entire directory tree, rather than just files from a particular directory. In fact, FTP protocol does not provide a method to request a recursive download (unless the server supports MLSD; see [319]Section 3.11), so this works only if the FTP server does it anyway, without being asked, as some do. In this case, Kermit detects that names in the returned file list contain directory separators, and therefore attempts to create the needed directories as the files arrive. But this can work only if the server is on the same kind of platform as the client, so the pathname syntax can be recognized, and also because the server does not switch between text and binary mode, which would be vital for cross-platform transfers. Use with caution. Synonym: /SUBDIRECTORIES. Even when the server does not provide recursive file lists, [M]GET /RECURSIVE forces Kermit to replicate any directory structure implied or expressed by the server's file list. For example: get somepath/somefile Gets the file named somefile from the server's somepath directory and puts it Kermit's current (or download) directory, whereas: get /recursive somepath/somefile creates the path locally and then puts the file in it. Similarly for MGET: mget */data/* downloads all the files in all the data subdirectories of all the subdirectories of the server's current directory and stores them locally in Kermit's current (or download) directory, whereas: mget /recursive */data/* re-creates the server's directory structure locally. The FTP protocol does not include explicit mechanisms for recursion, so Kermit builds upon what is available. Although an Internet draft describes a mechanism ("MLSD") that would allow protocol-driven recursion, similar to Kermit's File Attribute packets (circa 1984), it has not yet attained RFC or standard status, and servers are not yet widely available that offer this feature. In the meantime, the effectiveness of MGET /RECURSIVE depends on the FTP server implementation. If the server returns a recursive list in response to the standard NLST command (whose behavior is ill-defined), Kermit's FTP MGET /RECURSIVE command uses it to re-create the remote directory tree locally. If the server supports MLSD, C-Kermit 8.0.206 and Kermit 95 2.1 and later are able to sense it automatically and use it, as described below in [320]Section 3.11. The /BEFORE:, /AFTER:, /NOT-BEFORE:, and /NOT-AFTER: switches are not available for downloading because of the confusion with timezones. Would the given times be in the local timezone, the server's timezone, or GMT? The FTP server's directory listings show its own local times but since we don't know what timezone the server is in, there's no way to reconcile our local times with the server's. Similarly, /PERMISSIONS can't be preserved in downloads because FTP protocol provides no means of querying the server for a file's permission. Source-file disposition switches: /DELETE Each file that is downloaded successfully is to be deleted from the server. Requires the appropriate file access rights on the server. /SERVER-RENAME-TO:template Asks the server to rename each (remote) source file immediately after, and only if, it is sent correctly. See [321]PUT /SERVER-RENAME-TO: for details. Destination-file disposition switches: /TO-SCREEN Displays the incoming file on the screen rather than storing it on disk. If this switch is given, the /RENAME-TO and /MOVE-TO switches are ignored, the file-transfer display is suppressed, and the given file(s) is/are shown on the screen. Can be used with /FILTER, e.g. get /text /to-screen /filter:more oofa.txt In fact, you should always use /TO-SCREEN with /FILTER or /COMMAND when the command would result in displaying the incoming file on the screen; otherwise C-Kermit would have no way of knowing to suppress its file transfer display (since it can't be expected to know what the command or filter does). /RENAME-TO:template Each file that is downloaded is to be renamed as indicated if and only if it was received completely and without error. The template can be literal text or can contain variables that are evaluated for each file. For MGET, the text must contain variables; for GET it can be a literal string. The \v(filename) variable contains the name of the current file, so: ftp mget /rename-to:\v(filename).ok * causes each file that is successfully downloaded to have ".ok" appended to its name. For details see [322]Section 4.1 of the [323]C-Kermit 7.0 Update Notes. /MOVE-TO:text Just like /RENAME-TO:, except the text denotes the name of a directory to which successfully downloaded files are to be moved. If the directory does not exist, it is created. The file transfer display does not show the /MOVE-TO or /RENAME-TO value, since the incoming file has not yet been moved or renamed. 3.6.2. Filename Collisions What should happen if an incoming file has the same name as an existing file in the same directory? By default, Kermit's FILE COLLISION setting applies: BACKUP, RENAME, UPDATE, DISCARD, etc, as described in [324]Using C-Kermit. Kermit's default FILE COLLISION setting is BACKUP (rename the existing file and store the incoming file under its own name) and therefore this is also the default FTP collision action. The name under which an incoming file is to be stored is determined as follows: * If an as-name was given, the as-name is used. Otherwise: * If the client and server platforms are alike or [325]FTP FILENAMES is set to LITERAL (or the /FILENAMES:LITERAL switch was given for this download), the incoming filename is used literally. Otherwise: * The incoming filename is converted to a form that is friendly to the local platform. For UNIX, for example, incoming filenames that are all uppercase (as they might be from, say, VMS or an IBM mainframe) are converted to lowercase. If the resulting name coincides with the name of a local file that already exists, we have a filename collision. Collisions are handled according to the currently selected collision action: SET FTP COLLISION { BACKUP, RENAME, UPDATE, DISCARD, APPEND, OVERWRITE } This establishes a filename collision for FTP, separate from the Kermit one. The initial FTP collision setting is inherited from Kermit's FILE COLLISION setting when the first FTP command is given, but subsequent changes to Kermit's FILE COLLISION setting do not affect the FTP COLLISION setting. SHOW FTP tells the current FTP COLLISION setting. FTP GET /COLLISION:{BACKUP,RENAME,UPDATE,DISCARD,APPEND,OVERWRITE} Overrides the current FTP COLLISION action for this download only. FTP GET /UPDATE This is equivalent to GET /COLLISION:UPDATE, and is included for symmetry with PUT /UPDATE FTP GET /UPDATE and /COLLISION:UPDATE mean to download only those files whose modification dates on the server are later than those on the client. Date-time comparisons are done in Coordinated Universal Time (UTC, GMT, ZULU). The command: FTP MGET /COLLISION:APPEND /AS-NAME:newfilename *.* Downloads all matching remote files into a single local file (in whatever order the server sends them). 3.6.3. Recovery Recovery is available for downloads too, but there are some differences from the uploading case described in [326]Section 3.5.3: * The transfer must be in BINARY mode. It can not be in text mode, even if the FTP server is on the same kind of platform as Kermit, and even if there is no character-set translation. The original download must also have been in binary mode. * The FTP server must support the REST ("restart") directive. Unfortunately, this is not a standard command; at this writing, it is described only in an Internet Draft, not an RFC or Internet Standard, but nevertheless it is found in several popular FTP servers, such as [327]ProFTPD. Here's how download recovery works: * Kermit checks for conflicting switches, such as /UPDATE, /COMMAND, or /FILTER. If /RECOVER is given with these switches an error occurs. * The prevailing transfer mode (SET FTP TYPE) must be BINARY. If it is not, the /BINARY switch must have been included with the FTP [M]GET command. If the /RECOVER switch is accepted, then for each selected file: * A SIZE command is sent for the file (using its remote name). If the reply indicates the file was not found, or the SIZE command was not understood, or any other kind of error, recovery is canceled (i.e. the entire file is downloaded). * If the sizes of the two files are identical, the file is not sent. Otherwise: * Kermit sends the REST directive to the server, indicating the size of the local file. If the server responds affirmatively, Kermit opens the local file in append mode and appends the incoming data to it. Otherwise, recovery is canceled and the entire file is downloaded. The /RECOVER switch can be included with any FTP GET or MGET command, even if it specifies a group of files. This lets you resume an interrupted batch transfer from where it left off. The files that were already completely sent are skipped, the file that was interrupted is recovered, and the remaining files are uploaded. BUT... unlike with uploading, where this can be done with any mixture of text and binary files, when downloading, it can only be done if all the files are binary. It doesn't matter how the original partial file was downloaded -- FTP, Kermit, HTTP, Zmodem, etc: as long as the preconditions are met, it can be recovered with FTP [M]GET /RECOVER, or for that matter also with GET /RECOVER (using Kermit protocol). [ [328]Top ] [ [329]FTP Top ] [ [330]C-Kermit Home ] [ [331]Kermit Home ] 3.7. Translating Character Sets A possibly unique feature of Kermit's FTP client is its ability to convert character sets when transferring files in text mode, independent of the capabilities of the FTP server, as well as to translate the character sets of filenames regardless of transfer mode. For compatibility with existing FTP clients, and because there is a certain performance penalty, Kermit won't do this unless you ask for it. If you enable this feature, you need to inform Kermit of the character set (to be) used on the server and in some cases (explained below) also the local file character set. This discussion assumes you know a bit about character sets (as you must if you have to use them); see Chapter 16 of [332]Using C-Kermit for a detailed treatment. The Kermit commands for FTP character-set conversion are: SET FTP CHARACTER-SET-TRANSLATION { ON, OFF } Whether to translate character sets when transferring text files with FTP. OFF by default. Set this to ON to enable character-set translation for subsequent FTP uploads and downloads. SET FTP SERVER-CHARACTER-SET [333]name Text character set (to be) used by the server. Most FTP servers are ignorant of character sets, so all translations are done unilaterally by Kermit's FTP client. This means that when downloading files, you must know in advance the character-set used in the files you are downloading (and in their names). When uploading, you must specify the character-set to which local filenames and text-file contents are to be translated for transmission to the server. If you SET FTP CHARACTER-SET-TRANSLATION ON but do not specify an FTP SERVER-CHARACTER-SET, [334]UTF8 is used, since this is the new Internet standard international character set; it is upwards compatible with ASCII and it encompasses most written languages and therefore does not favor any particular group of people, as any other default would do. If you SET FTP SERVER-CHARACTER-SET to something (anything) when FTP CHARACTER-SET TRANSLATION is OFF, this also sets the latter ON. SET FILE CHARACTER-SET [335]name This is the regular Kermit (non-FTP-specific) command for identifying the character set (to be) used in local text files and filenames. TO REITERATE: If you SET FTP CHARACTER-SET TRANSLATION ON but do not specify an FTP SERVER-CHARACTER-SET, outbound text files are converted to UTF-8 and inbound text files are assumed to be UTF-8. If this is not appropriate, be sure to also specify the desired FTP SERVER-CHARACTER-SET. You can use "special" (non-ASCII) characters in filenames in all the client / server file management commands (FTP MKDIR, RMDIR, DIRECTORY, VDIRECTORY, DELETE, etc), and also in file-transfer commands. When giving commands such as FTP DIR (RDIR) and FTP PWD (RPWD), the reply is translated too, so you can read it. In this example, the client and server use entirely different codes to represent the special characters of German: C-Kermit> ftp xyzcorp.de /anonymous C-Kermit> set ftp server-character-set latin1 C-Kermit> set file character-set german C-Kermit> rcd Städte C-Kermit> rpwd "/pub/ftp/Städte is current directory" C-Kermit> rdir -rw-rw---- 1 olaf 54018 Jan 6 17:58 Adenbüttel.txt -rw-rw---- 1 ursula 373 Jan 5 15:19 Aßlar.txt -rw-rw---- 1 gisbert 482 Jan 5 15:20 Blowatz.txt -rw-rw---- 1 gudrun 124 Jan 5 15:19 Böblingen.txt -rw-rw---- 1 olga 14348 Jan 7 14:23 Köln.txt When the client and server file systems use different character sets, you should take care to use only those characters that the two sets share in common when creating filenames or text-file contents. For example, PC code pages contain a lot line- and box-drawing characters, and sometimes "smart quotes", etc, that are not found in ISO standard 8-bit character sets. You should be especially careful to avoid using such characters in filenames. [ [336]C-Kermit Character Sets ] 3.7.1. Character Sets and Uploading Kermit's PUT and MPUT commands include full file-scanning capabilities, as described in [337]Section 4. Thus if FTP CHARACTER-SET-TRANSLATION is ON and your character-set associations are set up appropriately, Kermit automatically switches on a per-file basis between text and binary mode, and for each text file between your chosen 7-bit text character set (e.g. ASCII or ISO 646 German), 8-bit text (e.g. Latin-1 or Japanese EUC), UCS-2, and UTF-8, and converts each of these automatically to the server character-set, and furthermore automatically differentiates between the Little and Big Endian forms of UCS-2, always sending in Big Endian form. WARNING: It is not advisable to use UCS-2 (or any Unicode transformation other than UTF-8) "on the wire", i.e. as a server character set. Most FTP servers are not able to cope with it, since it contains lots of 0 (NUL) characters. If you do use it, Kermit does not translate filenames to or from UCS-2, for reasons well known to C programmers (for example, UNIX APIs assume filename strings are NUL-terminated). [338]UTF-8 is the preferred (and standard) Unicode format for the Internet. FTP character-set translations differ from the regular Kermit ones by not restricting translations to a file-character-set / transfer-character-set pair. You can have Kermit's FTP client translate between any pair of character sets it knows about. You can see the list of supported character sets by typing either of the following: set ftp server-character-set ? set file character-set ? A typical list looks like this ([339]CLICK HERE for an explanation of the names): C-Kermit>set file char ? One of the following: ascii cp869-greek hebrew-7 mazovia-pc british cyrillic-iso hebrew-iso next-multinational bulgaria-pc danish hp-roman8 norwegian canadian-french dec-kanji hungarian portuguese cp1250 dec-multinational iso2022jp-kanji shift-jis-kanji cp1251-cyrillic dg-international italian short-koi cp1252 dutch jis7-kanji spanish cp437 elot927-greek koi8 swedish cp850 elot928-greek koi8r swiss cp852 euc-jp koi8u ucs2 cp855-cyrillic finnish latin1-iso utf8 cp858 french latin2-iso cp862-hebrew german latin9-iso cp866-cyrillic greek-iso macintosh-latin C-Kermit> Thus you can translate not only between private sets (like PC code pages) and standard ones (like Latin-1) as in Kermit protocol, but also between any given pair of private sets (e.g. CP852 and Mazovia). All conversions go through Unicode as the intermediate character set, resulting in a minimum of character loss, since Unicode is a superset of all other character sets known to Kermit. In addition to the SET commands listed above, the FTP PUT and MPUT commands include switches that apply only to the current command: /LOCAL-CHARACTER-SET:name /SERVER-CHARACTER-SET:name Use these switches to force a particular translation. These switches override the global FTP CHARACTER-SET-TRANSLATION and SERVER-CHARACTER-SET settings and also character-set differentiation by file scanning for the duration of the PUT or MPUT command. The file scan is still performed, however, to determine whether the file is text or binary; thus these switches do not affect binary files unless you also include the /TEXT switch to force all files to be treated as text. In other words, if you include one or both of these switches with a PUT or MPUT command, they are used. Similarly, the /TRANSPARENT switch disables character-set translation for the PUT or MPUT command despite the prevailing FTP CHARACTER-SET-TRANSLATION and SERVER-CHARACTER-SET settings. When uploading, the FILE CHARACTER-SET setting is ignored unless you have forced Kermit not to [340]scan local files by including a /TEXT or /BINARY switch with your [M]PUT command, or by disabling automatic text/binary switching in some other way. Examples: 1. Suppose you have a CP852 (East European) text file that you want to upload and store in ISO Latin Alphabet 2 encoding: ftp put /local-char:cp852 /server-char:latin2 magyar.txt 2. Suppose you always want your text files converted to Latin-2 when uploading with FTP. Then put: set ftp server-character-set latin2 in your Kermit customization file, and then you can omit the /SERVER-CHARACTER-SET: switch from your FTP PUT commands: ftp put /local-char:cp852 magyar.txt 3. Now suppose that all the text files on your PC are written in Hungarian, but they have a variety of encodings, and you don't want to have to include the /LOCAL-CHARACTER-SET: switch on every FTP PUT command, or (more to the point) you want to be able to send a mixture of these files all at once. Put these commands in your Kermit customization file: set ftp server-character-set latin2 ; ISO 8859-2 set file default 7-bit-character-set hungarian ; ISO 646 Hungarian set file default 8-bit-character-set cp852 ; PC East European Code Page and now PUT and MPUT will automatically detect and switch among ISO 646 Hungarian, Code Page 852, UTF-8, and UCS-2 encodings, translating each one to Latin-2 for uploading: ftp put *.txt And since binary files are also detected automatically, the latter can be simplified to: ftp put * even when "*" matches a diverse collection of binary and text files, because translations are skipped automatically for binary files. 3.7.2. Character Sets and Downloading The commands and switches are the same as for uploading, but automatic character-set switching works differently, since Kermit can't scan the server files in advance. Instead, the transfer mode (text or binary) is based on the filenames; each name is compared with Kermit's list of text name patterns and binary name patterns. If the name matches a binary pattern (for example, if the filename is oofa.tar.gz and one of the filename patterns is "*.gz"), the file is downloaded in binary mode; otherwise if it matches a text pattern (e.g. oofa.txt matches "*.txt"), it is transferred in text ("ascii") mode. Otherwise, it is transferred in the prevailing FTP TYPE. In C-Kermit 8.0, the pattern lists used with FTP GET are not the same lists used with Kermit transfers, and can not be viewed with SHOW PATTERNS, nor adjusted with ADD and REMOVE TEXT-PATTERNS and BINARY-PATTERNS, or SET FILE TEXT-PATTERNS and BINARY-PATTERNS. Configuration of the FTP patterns list will be added in a future release. Examples: get /server-char:latin1 /local-char:cp850 Grüße.txt In this command, the filename contains special characters, which you enter using whatever character set your local computer uses, in this case PC Code Page 850 (cp850). The command tells Kermit (in case it didn't know already from its FILE CHARACTER-SET setting) that the local character set is cp850 and the server's character-set is ISO 8859-1 Latin Alphabet 1 (latin1). Kermit translates the filename from cp850 to latin1 and sends the latin1 name to the server. Since it's a text file (matches "*.txt"), its contents are translated to cp850 on arrival, and it is saved with a cp850 name. mget /text /server:latin1 /local:utf8 *.txt This command: + Tells C-Kermit that the server's files are encoded in ISO 8859-1 Latin Alphabet 1. + Tells C-Kermit to translate the incoming files into Unicode UTF-8 for storage. + Asks the server to send all ".txt" files in text mode. mget /server:latin1 /local:utf8 * Tells Kermit to get all files from the server's directory, switching between text and binary mode based on the filename. The names of all the files are translated (to UTF-8 in this case), but contents are translated (also to UTF-8) only for text files. Note that any pair of 8-bit character sets is likely to have some incompatibilities. Any characters in the source file that do not have equivalents in the destination file's character set are converted to question marks. This applies to both filenames and to text file contents. Also note that the server's ability to accept special characters in filenames depends on the particular server. For example: get Grüße.txt works with WU-FTPD, but: mget Grüß*.txt does not. 3.7.3. RFC2640 [341]RFC2640, July 1999, specifies a method by which the FTP client and server can negotiate the use of UTF8. However, RFC2640-capable servers are rare to nonexistent at this writing, and in any case you don't need them to be able to transfer text in UTF8. C-Kermit lets you upload and download text files in any character set it knows about, converting to or from any other character set it knows about, without the knowledge, permission, or cooperation of the server, and regardless of its capabilities. [ [342]Top ] [ [343]FTP Top ] [ [344]C-Kermit Home ] [ [345]Kermit Home ] 3.8. FTP Command Shortcuts C-Kermit's FTP client coexists with other C-Kermit functions by requiring the "ftp" prefix for each FTP-related command: FTP OPEN, FTP GET, FTP BYE, and so on. For interactive use, however, this can be rather awkward and sometimes surprising, for example when a GET command starts a Kermit GET rather than an FTP GET. In fact, many Kermit commands might just as easily apply to an FTP connection: GET, PUT (SEND), BYE, and CLOSE. The following command lets you choose how these commands are interpreted: SET GET-PUT-REMOTE { AUTO, KERMIT, FTP } Controls the orientation of GET, PUT, REMOTE and other file-transfer and client/server commands that might apply to either Kermit or FTP. The default setting is AUTO, meaning that these commands apply to FTP if an FTP connection is open, and to Kermit otherwise. KERMIT means they always apply to Kermit, FTP means they always apply to FTP. Here is a complete list of affected commands: Kermit Command FTP Equivalent (none) FTP [ OPEN ] LOGIN FTP USER LOGOUT FTP RESET BYE FTP BYE FINISH FTP BYE CLOSE FTP BYE HANGUP FTP BYE BINARY FTP TYPE BINARY TEXT (or ASCII) FTP TYPE ASCII SEND (or PUT) FTP PUT MSEND (or MPUT) FTP MPUT RESEND FTP PUT /RECOVER CSEND FTP PUT /COMMAND GET FTP GET MGET FTP MGET REGET FTP GET /RECOVER REMOTE HELP (RHELP) FTP HELP REMOTE CD (RCD) FTP CD (CWD) REMOTE PWD (RPWD) FTP PWD REMOTE DIRECTORY (RDIR) FTP DIRECTORY REMOTE DELETE (RDEL) FTP DELETE REMOTE MKDIR (RMKDIR) FTP MKDIR REMOTE RMDIR (RRMDIR) FTP RMDIR REMOTE RENAME (RRENAME) FTP RENAME REMOTE TYPE (RTYPE) FTP TYPE REMOTE EXIT (REXIT) FTP BYE The commands in the right-hand column always access FTP. The commands in the left column can access either Kermit protocol or FTP: * When GET-PUT-REMOTE is set to KERMIT, or to AUTO when there is no FTP connection, the commands in the left-hand column access Kermit protocol, and those right-hand column are required for FTP. * When GET-PUT-REMOTE is set to FTP, or to AUTO when there is an active FTP connection, the commands in the left-hand column access the FTP connection and can not be used to access Kermit protocol. In this case, if you want to be able to use both Kermit protocol and the FTP connection, you must SET GET-PUT-REMOTE KERMIT, and then use the FTP commands in the right-hand column to access the FTP connection. Note that file-management commands such as DIRECTORY, DELETE, CD, PWD, MKDIR, RMDIR, HELP, RENAME, COPY, TYPE, and so on, always apply locally, no matter what kind of connection you have. This is the opposite of most FTP clients, where these commands are intended for the server, and require an "L" prefix for local execution (e.g. "dir" gets a directory listing from the server, "ldir" gets a local directory listing). To illustrate with the CD command and a typical UNIX FTP client: Client Server Change Local Directory Change Remote Directory FTP FTP lcd cd (cwd) Kermit Kermit cd rcd, remote cd Kermit FTP cd ftp cd, rcd, remote cd Also note that not all REMOTE commands are useful with FTP, since FTP servers do not offer the corresponding functions. These include: * REMOTE ASSIGN - FTP servers don't have variables * REMOTE COPY - FTP servers don't copy files * REMOTE HOST - FTP servers don't execute host (shell) commands * REMOTE KERMIT - FTP servers don't execute Kermit commands * REMOTE PRINT - FTP servers don't print files * REMOTE QUERY - FTP servers don't have variables * REMOTE SET - FTP servers don't have Kermit settings * REMOTE WHO - FTP servers don't send user lists Finally note that command shortcuts do not apply to the HELP command. For help about an FTP command, use (for example) "help ftp delete", not "help delete" or "help rdelete". [ [346]Top ] [ [347]FTP Top ] [ [348]C-Kermit Home ] [ [349]Kermit Home ] 3.9. Dual Sessions You can have an FTP session open at the same time as a regular Kermit SET LINE or SET HOST (terminal) session. In this case, the default SET GET-PUT-REMOTE AUTO setting should ensure that all "two-faced" commands like GET, PUT, REMOTE, HANGUP, BYE, etc, apply to the Kermit session, and all commands for the FTP session must include the FTP prefix. To be absolutely certain, you can use SET GET-PUT-REMOTE KERMIT. ftp foo.bar.baz.com if fail ... (log in) set host foo.bar.baz.com if fail ... (log in) Now you have both an FTP and Telnet connection to the same host (of course they could also be to different hosts, and you could also have a direct or dialed serial connection instead of a Telnet connection). Now assuming you have a Kermit server on the far end of the Kermit connection: rcd incoming ; Changes Kermit server's directory (= REMOTE CD) ftp cd incoming ; Changes FTP server's directory put oofa.txt ; Sends a file on the Kermit connection ftp put oofa.txt ; Sends a file on the FTP connection bye ; Shuts down the Kermit connection ftp bye ; Shuts down the FTP connection Note that PUT and SEND are synonyms for both FTP and Kermit connections. You can also establish dual sessions on the Kermit command line: kermit -j host1 -9 host2 This makes a Telnet connection to host1 and an FTP connection to host2. [ [350]Top ] [ [351]FTP Top ] [ [352]C-Kermit Home ] [ [353]Kermit Home ] 3.10. Automating FTP Sessions Most of Kermit's scripting features can be used to make and control FTP sessions: FOR and WHILE loops, IF-ELSE and SWITCH constructions, variables, arrays, built-in functions, and all the rest. You can't use INPUT, MINPUT, OUTPUT, CLEAR, or SCRIPT on an FTP session, but these are not needed since the FTP protocol is well defined. [354]CLICK HERE for an FTP scripting tutorial. 3.10.1. FTP-Specific Variables and Functions The following variable tells whether an FTP connection is open: \v(ftp_connected) 1 if there is an active FTP connection, 0 if there isn't. The FTP OPEN command sets: \v(ftp_host) The host to which the most recent FTP connection was made. \v(ftp_security) The security method negotiated for the current FTP session. The value is "NULL" when no security is used. Other possibilities are GSSAPI, KERBEROS_V4, SSL, TLS, and SRP. Also see \v(authname), \v(authstate), and \v(authtype). See [355]3.2. Making Secure FTP Connections. \v(ftp_server) The OS type (UNIX, VMS, etc) of the FTP server host. The FTP USER command (or FTP OPEN /USER:, or FTP with automatic login) sets: \v(ftp_loggedin) 1 if you are logged in to an FTP server, 0 if you are not. The current COMMAND-PROTECTION-LEVEL and DATA-PROTECTION-LEVEL values are reflected in: \v(ftp_cpl) \v(ftp_dpl) The values are "clear", "confidential", "safe" or "private". See [356]3.2. Making Secure FTP Connections. The FTP GET-PUT-REMOTE setting is reflected in: \v(ftp_getputremote) The values are "auto", "ftp", or "kermit". Every FTP command sets the \v(success) variable, as well as the following two FTP-specific variables: \v(ftp_code) The standardized numeric FTP protocol code from the server's response to the last client command, a 3-digit decimal number defined in [357]RFC959. Briefly: 1xx = Positive Preliminary Reply 2xx = Positive Completion Reply 3xx = Positive Intermediate Reply 4xx = Transient Negative Completion Reply 5xx = Permanent Negative Completion Reply \v(ftp_message) The text message, if any, from the server's response to the last client command. If the most recent response had multiple lines, this variable has only the final line. These messages are not standardized and vary in format and content from server to server. Synonym: \v(ftp_msg). FTP file transfers set the regular Kermit transfer status variables: \v(cps) Characters per second of most recent transfer. \v(filespec) File specification used in most recent transfer. \v(fsize) Size of file most recently transferred. \v(tfsize) Total size of file group most recently transferred. \v(xferstatus) Status of most recent transfer (0 = success, 1 = failure). \v(tftime) Elapsed time of most recent transfer, in seconds. During an FTP transfer, the per-file variables are: \v(filename) Name of current file. \v(filenumber) Ordinal file number in group (1, 2, 3, ...) 3.10.2. Examples Let's begin with a simple example showing how to log in, send some files, and log out: define error if fail { ftp bye, stop 1 Error: \%1 } set transact brief log t ftp ftp.xyzcorp.com /anonymous if fail stop 1 Connection failed if not \v(ftp_loggedin) stop 1 Login failed ftp cd incoming error {ftp cd} cd upload error {local cd} ftp put /delete * error {put} ftp bye First we define an error handling macro to be used after the connection is made. Then we set up a brief-format transaction log to keep a record of our file transfers. Then we make a connection to the host and log in anonymously. The "if fail" command checks whether the connection was made. The "if not" command checks whether login was successful. Obviously the script should not continue unless both tests succeed. Next we change to the server's 'incoming' directory and to our own 'upload' directory, and send all the files that are in it (they can be any mixture of text and binary files), deleting each source file automatically after it is successfully uploaded. Each of these operations is checked with the ERROR macro, which prevents the script from continuing past a failure. Finally we close the FTP session with the "bye" command. Just like any other Kermit script, this one can be used in many ways: * It can be stored in a file, and Kermit can be told to TAKE the file. * In UNIX, it can be a "[358]kerbang" script and therefore run directly from the shell prompt or as a cron job. We could have used command shortcuts like "rcd", "put", and "bye", but since they can be ambiguous under certain circumstances, it is better to avoid them in scripts; they are intended mainly for convenience during interactive use. However, if you wish to use the shortcuts in a script, you can do it this way (error handling omitted for brevity): local \%t ; Declare a local temporary variable assign \%t \v(ftp_getputremote) ; Save current FTP GET-PUT-REMOTE setting set ftp get-put-remote ftp ; Choose FTP orientation ftp xyzcorp.com /anonymous ; Open an FTP connection get oofa.txt ; GET a file put foo.bar ; PUT a file rdel yesterday.log ; Delete a file on the server bye ; Log out and disconnect from server. set ftp get-put-remote \%t ; Restore previous GET-PUT-REMOTE setting Of course, FTP scripts can also be written as macros. This lets you pass parameters such as hostnames, usernames, and filenames to them: define doftpget { if < \v(argc) 4 end 1 Usage: \%0 host user remotefile [ localfile ] ftp \%1 /user:\%2 if fail end 1 FTP OPEN \%1 failed if not \v(ftp_loggedin) end 1 FTP LOGIN failed ftp get {\%3} {\%4} if fail end 1 FTP GET \%3 failed ftp bye } Add this definition to your Kermit customization file, and it will always be available when you start Kermit. This macro lets you download a file with FTP by giving a single command, e.g.: doftpget xyzcorp.com anonymous oofa.txt 3.10.3. Automating Secure FTP Sessions Often when making secure connections, you are prompted interactively for certain information or permission to proceed. These prompts can stop an automated procedure. To avoid them, you must give the appropriate commands to disable them, and/or supply the prompted-for information beforehand. Here are a few hints: * Make sure that SET TAKE ERROR and SET MACRO ERROR are both OFF. This is the default, but in case you have set either one of these ON in your script or initialization file, this makes the script halt on any kind of error. Normally you would want to check each operation for success or failure and take appropriate action. * On SSL and TLS connections, you may be asked whether it is OK to proceed with a connection to server that presents a self-signed certificate. You can use the SET AUTHENTICATION SSL (or TLS) VERIFY or SET AUTH SSL (or TLS) CERTS-OK commands to avoid this prompt by not requesting a certificate from the peer. * (More to be added...) [ [359]Top ] [ [360]FTP Top ] [ [361]FTP Script Tutorial ] [ [362]C-Kermit Home ] [ [363]Kermit Home ] 3.11. Advanced FTP Protocol Features The remainder of the FTP documentation (through the end of Section 3) is new to C-Kermit 8.0.206, but we leave it in black to prevent headaches. Except for titles. * [364]TERMINOLOGY * [365]FEATURE NEGOTIATION * [366]USING MGET: NLST VERSUS MLSD * [367]EXAMPLES * [368]REFERENCES The new releases of [369]C-Kermit (8.0.206) and [370]Kermit 95 (2.1) support new FTP protocol features from RFC 2389 as well as most of what's in the Elz and Hethmon Extensions to FTP Internet Draft (see [371]References). Some of these features, such as SIZE (request a file's size), MDTM (request file's modification time), and REST (restart interrupted transfer) have been widely implemented in FTP clients and servers for years (as well as in the initial release of the Kermit FTP clients). Others such as FEAT and MLSD are rarely seen and are new to the upcoming Kermit releases. TVFS (Trivial Virtual File Store) is supported implicitly, and the UTF-8 character-set is already fully supported at the protocol and data-interchange level. For Kermit users, the main benefit of the new FTP protocol extensions is the ability to do recursive downloads. But the extensions also introduce complications and tradeoffs that you should be aware of. Of course Kermit tries to "do the right thing" automatically in every case for backwards compatibility. But (as noted later) some cases are inherently ambiguous and/or can result in nasty surprises, and for those situations new commands and switches are available to give you precise control over Kermit's behavior, in case the defaults don't produce the desired results. 3.11.1. Terminology Command-line FTP clients such as Kermit (as well as the traditional FTP programs found on Unix, VMS, ..., even Windows) have commands like PUT, MPUT, GET, MGET, and BYE, which they convert into zero or more FTP protocol commands, such as NLST, RETR, QUIT. For clarity, we'll use "command" to refer to commands given by the user to the FTP client, and "directive" for FTP protocol commands sent by the FTP client to the FTP server. 3.11.2. Feature Negotiation New FTP protocol features are negotiated by the client sending a FEAT directive and the server responding with a list of (new) features it supports, or else with an error indication if it does not support the FEAT directive at all, in which case the client has to guess which new features it supports (Kermit guesses that it supports SIZE and MDTM but not MLST). Note that the MLST feature includes MLSD, which is not listed separately as a feature. Guessing is nice when it works, but sometimes it doesn't, and some FTP servers become confused when you send them a directive they don't understand, or they do something you didn't want, sometimes to the point of closing the connection. For this reason, Kermit lets you override default or negotiated features with the following new commands: FTP { ENABLE, DISABLE } FEAT Enables or disables the automatic sending of a FEAT directive upon connection to an FTP server. Note that FTP [ OPEN ] /NOINIT also inhibits sending the FEAT directive (and several others) for the connection being OPEN'd, but without necessarily disabling FEAT for subsequent connections in the same Kermit instance. FEAT is ENABLED by default, in which case many FTP servers are likely to reply: 500 'FEAT': command not understood which is normally harmless (but you never know). (In C-Kermit 8.0.208, this error message is suppressed unless you SET FTP DEBUG ON.) FTP ENABLE { MDTM, MLST, SIZE } Enables the given directive for implicit use by the FTP GET and MGET commands in case it has been disabled or erroneously omitted by the server in its FEAT response. Note: MLSD can be used in the FTP ENABLE and DISABLE commands as a synonym for MLST. YOU MUST GIVE THIS COMMAND AFTER MAKING THE FTP CONNECTION. FTP DISABLE { MDTM, MLST, SIZE } Disables implicit use of the given directive by GET or MGET in case it causes problems; for example, because it makes multifile downloads take too long or the server announces it erroneously or misimplements it. Use DISABLE FEAT before making a connection to prevent Kermit from sending the FEAT directive as part of its initial sequence. Note that disabling FEAT, SIZE, or MDTM does not prevent you from executing explicit FTP FEATURES, FTP SIZE, or FTP MODTIME commands. Also note that disabling SIZE prevents PUT /RESTART (recovery of interrupted uploads) from working. YOU MUST GIVE THIS COMMAND AFTER MAKING THE FTP CONNECTION. To enable or disable more than one feature, use multiple FTP ENABLE or FTP DISABLE commands. The SHOW FTP command shows which features are currently enabled and disabled. FTP FEATURES This command sends a FEAT directive to the server. In case you have been disabling and enabling different features, this resynchronizes Kermit's feature list with the server's. If the server does not support the FEAT directive, Kermit's feature list is not changed. FTP OPTIONS directive Informational only: the server tells what options, if any, it supports for the given directive, e.g. MLST. Fails if the server does not support the OPTS directive or if the directive for which options are requested is not valid. The directive is case-insensitive. FTP SIZE filename Sends a SIZE directive to the server for the given file. The filename must not contain wildcards. The server responds with an error if the file can't be found, is not accessible, or the SIZE directive is not supported, otherwise with the length of the file in bytes, which Kermit displays and also makes available to you in its \v(ftp_message) variable. If the directive is successful, Kermit (re-)enables it for internal use by the GET and MGET directives on this connection. FTP MODTIME filename Works just like the SIZE directive except it sends an MDTM directive. Upon success, the server sends modification date-time string, which Kermit interprets for you and also makes available in its \v(ftp_message) variable. Whenever a SIZE or MDTM directive is sent implicitly and rejected by the server because it is unknown, Kermit automatically disables it. 3.11.3. Using MGET: NLST versus MLSD When you give an MGET command to an FTP client, it sends a request to the FTP server for a list of files, and then upon successful receipt of the list, goes through it and issues a RETR (retrieve) directive for each file on the list (or possibly only for selected files). With the new FTP protocol extensions, now there are two ways to get the list of files: the NLST directive, which has been part of FTP protocol since the beginning, and the new MLSD directive, which is new and not yet widely implemented. When NLST is used and you give a command like "mget *.txt", the FTP client sends: NLST *.txt and the server sends back a list of the files whose names match, e.g. foo.txt bar.txt baz.txt Then when downloading each file, the client sends SIZE (if it wants have a percent-done display) and MDTM (if it wants to set the downloaded file's timestamp to match that of the original), as well as RETR (to retrieve the file). But when MLSD is used, the client is not supposed to send the filename or wildcard to the server; instead it sends an MLSD directive with no argument (or the name of a directory), and the server sends back a list of all the files in the current or given directory; then the client goes through the list and checks each file to see if it matches the given pattern, the rationale being that the user knows only the local conventions for wildcards and not necessarily the server's conventions. So with NLST the server interprets wildcards; with MLSD the client does. The interpretation of NLST wildcards by the server is not necessarily required or even envisioned by the FTP protocol definition (RFC 959), but in practice most clients and servers work this way. The principal advantage of MLSD is that instead of sending back a simple list of filenames, it sends back a kind of database in which each entry contains a filename together with information about the file: type, size, timestamp, and so on; for example: size=0;type=dir;perm=el;modify=20020409191530; bin size=3919312;type=file;perm=r;modify=20000310140400; bar.txt size=6686176;type=file;perm=r;modify=20001215181000; baz.txt size=3820092;type=file;perm=r;modify=20000310140300; foo.txt size=27439;type=file;perm=r;modify=20020923151312; foo.zip (etc etc...) (If the format of the file list were the only difference between NLST and MLSD, the discussion would be finished: it would always be better to use MLSD when available, and the MGET user interface would need no changes. But there's a lot more to MLSD than the file-list format; read on...) The client learns whether the server supports MLSD in FEAT exchange. But the fact that the server supports MLSD doesn't mean the client should always use it. It is better to use MLSD: * On connections where the server imposes a time penalty for every command, e.g. the Red Hat Rawhide server. With MLSD, the client needs to send only one command (RETR) per file, whereas NLST requires three (SIZE, RETR, and MDTM). Suppose there is a 30-second delay for each command and 1000 files are to be fetched; in that case, MLSD saves 60,000 seconds = 1000 minutes = 16 hours and 40 minutes. * For recursive downloads since there is no dependable way to download directory trees with NLST. But it is better to use NLST: * If you want only a couple short files out of a large directory. In this case, NLST is the better choice since the server sends a list of only the files you want, not a list of (say) a million files, which can make a big difference on slow connections. For example, suppose your wildcard matches three files of 1K each, but the million-file listing is 80MB long, and your connection is through a modem. The overhead of using MLSD is practically infinite. * If the server supports wildcarding features not known to the client, but that can be used to achieve desirable effects otherwise unobtainable, such as "[dir...]*.txt" in VMS or AOS/VS "except" clauses. * If you have been given a wildcard string by an FTP site administrator for fetching a specific group of files out of a larger directory, e.g. "mget ck[cuw]*.[cwh] makefile", that is expected to work with any client (an FTP site administrator can't be expected to know the wildcard syntax of every FTP client). But when using MLSD there are complications: * MLSD wants either a blank argument (meaning the current directory) or else the name of a specific directory. The client must not send it a wildcard or a filename. * But if the user's command is "mget xxx", how does the client know whether to send "xxx" in the MLSD directive? It might be the name of a directory on on the server, in which case it should be sent, or it might be the name of a file on the server (or a wildcard), in which case it must not be sent. Since the client knows its own wildcard syntax, then in most cases it would be right to send "MLSD" with no argument if xxx is wild, and to send "MLSD xxx" if it is not. * But suppose the server's file system allows filename characters that correspond with the client's wildcard syntax? For example: "[abc]" could be either a valid VMS directory name or a wildcard pattern used by the FTP client. What should the client do with "mget [abc]"? In this case there must be a way for the user to force sending the MGET argument as the MLSD argument. * If "xxx" is a regular file in the server's current directory, "mget xxx" works with NLST but not with MLSD. To further complicate matters, NLST can (in theory) work just like MLSD: if sent with a blank argument or a directory name, it is supposed to return a complete list of files in the current or given directory, which the client can match locally against some pattern. It is not known if any FTP server or client does this but nevertheless, it should be possible since this behavior can be inferred from RFC 959. In view of these considerations, and given the need to preserve the traditional FTP client command structure and behavior so the software will be usable by most people: 1. The MGET command should produce the expected result in the common cases, regardless of whether NLST or MLSD is used underneath. 2. For anomalous cases, the user needs a way to control whether the MGET argument is sent to the server or kept for local use. 3. At the same time, the user might need a way to send a directory name to the server, independent of any wildcard pattern. 4. The user needs a way to force NLST or MLSD for a given MGET command. By default, Kermit's MGET command uses MLSD if MLST is reported by the server in its FEAT list. When MLSD is used, the filespec is sent to the server if it is not wild (according to Kermit's own definition of "wild" since it can't possibly know the server's definition). If the filespec is wild it is held for local use to select files from the list returned by the server. If MLST is not reported by the server or is disabled, Kermit sends the MGET filespec with the NLST directive. The default behavior can be overridden globally with FTP DISABLE MLST, which forces Kermit to use NLST to get file lists. And then for situations in which MLSD is enabled, the following MGET switches can be used to override the defaults for a specific MGET operation: /NLST Forces the client to send NLST. Example: mget /nlst foo.* /MLSD Forces the client to send MLSD (even if MLST is disabled). Example: mget /mlsd foo.* /MATCH:pattern When this switch is given, it forces the client to hold the pattern for local use against the returned file list. If a remote filespec is also given (e.g. the "blah" in "mget /match:*.txt blah"), then it is sent as the NLST or MLSD argument, presumably to specify the directory whose files are to be listed. When the /MATCH switch is not given, the MGET filespec is sent to the server if the directive is NLST or if the filespec is not wild. Examples: Command: With NLST: With MLSD: mget NLST MLSD mget *.txt NLST *.txt MLSD mget foo NLST foo MLSD foo mget /match:*.txt NLST MLSD mget /match:*.txt foo NLST foo MLSD foo In other words, the pattern is always interpreted locally unless MGET uses NLST and no /MATCH switch was given. 3.11.4. Examples 3.11.4.1. Downloading a Single File There are no choices here, just use the FTP GET command. Kermit always sends the RETR directive, and possibly SIZE and/or MDTM. The small advantage of using MLST in this case is outweighed by the risk and effort of coding a special case. 3.11.4.2. Downloading a Group of Files from a Single Directory This case presents tradeoffs, especially on slow connections: * For downloading all or most of the files in a directory, MLSD is better because it eliminates the need to send SIZE and MDTM for each file. No special actions are required in this case; Kermit uses MLSD automatically if the server supports it (unless you have disabled it). * For a small number of files from a large directory, NLST is better because it bypasses downloading of a potentially huge file list prior to the files themselves. If you have a connection to a server that supports MLSD, use the /NLST switch to force NLST: mget /nlst t[1234].h * If the server supports MLSD but does not support separate SIZE or MDTM directives, and you need the size and/or timestamp information, MLSD is better; no special actions required. * If the server supports MLSD but does not support the "size" and "modify" facts, but it does support the SIZE or MDTM directives, and you need the size and/or timestamp information, NLST is better. 3.11.4.3. Downloading a Directory Tree MLSD is the only choice for recursive downloads; they rarely, if ever, work with NLST (the few cases where they do work rely on extra-protocol "secret" notations for the NLST argument). No special actions are required to force MLSD when the server supports it, unless you have disabled it. Examples: MGET /RECURSIVE This tells the server to send all files and directories in the tree rooted at its current directory. MGET /RECURSIVE *.txt This tells the server to send all *.txt files in the tree rooted at its current directory. MGET /MLSD /RECURSIVE *.txt Same as the previous example but forces Kermit to send MLSD in case it was disabled, or in case the server is known to support it even though it did not announce it in its FEAT listing. MGET /RECURSIVE /MATCH:*.zip archives Tells the server to send all ZIP files in the tree rooted at its "archives" directory. MGET /RECURSIVE /MATCH:* [abc] The server is running on VMS and you want it to send all the files in the directory tree rooted at [ABC]. But since "[abc]" looks just like a wildcard, you have to include a /MATCH: switch to force Kermit to send "[abc]" as the MLSD argument. In all cases in which the /RECURSIVE switch is included, the server's tree is duplicated locally. Although MLSD allows recursion and NLST does not, the MLSD specification places a heavy burden on the client; the obvious, straightforward, and elegant implementation (depth-first, the one that Kermit currently uses) requires as many open temporary files as the server's directory tree is deep, and therefore client resource exhaustion -- e.g. exceeding the maximum number of open files -- is a danger. Unfortunately MLSD was not designed with recursion in mind. (Breadth-first traversal could be problematic due to lack of sufficient navigation information.) Of course all of Kermit's other MGET switches can be used too, e.g. for finer-grained file selection (by date, size, etc), for moving or renaming files as they arrive, to override Kermit's automatic per-file text/binary mode switching, to pass the incoming files through a filter, to convert text-file character sets, and so on. 3.11.4.4. NLST/MLSD Summary Table Here's a table summarizing MGET behavior when the server supports both NLST and MLSD. /NLST and /MLSD switches are included for clarity to indicate which protocol is being used, and the expected effects. In practice you can omit the /NLST and /MLSD switches and the Kermit client chooses the appropriate or desired protocol as described above. Sample commands presume a Unix file system on the server, but of course the server can have any file system or syntax at all. User's Command FTP Sends Remarks mget /nlst NLST Gets a list of all the files in the server's current and downloads each file. The list includes names only, so Kermit also must send SIZE and MDTM directives if size and timestamp information is required (this is always true of NLST). Sending NLST without an argument is allowed by the RFC959 NLST definition and by the Kermit FTP client, but might not work with other clients, and also might not work with every server. mget /nlst foo NLST foo If "foo" is a directory, this gets a list of all the files from the server's "foo" directory and downloads each file; otherwise this downloads the file named "foo" (if any) from the server's current directory. mget /nlst *.txt NLST *.txt Gets a list of the files in the server's current directory whose names match the pattern *.txt, and then downloads each file from the list. Because we are using NLST, we send the filespec (*.txt) to the server and the server interprets any wildcards. mget /nlst foo/*.txt NLST foo/*.txt Gets a list of the files in the server's "foo" directory whose names match the pattern *.txt, and then downloads each file from the list (server interprets wildcards). mget /nlst /match:*.txt NLST Gets a list of all the files in the server's current directory and then downloads each one whose name matches the pattern *.txt (client interprets wildcards). mget /nlst /match:*.txt foo NLST foo Gets a list of all the files in the server's "foo" directory and then downloads each one whose name matches the pattern *.txt (client interprets wildcards). mget /mlsd MLSD Gets a list of all the files from the server's current directory and then downloads each one. The list might include size and timestamp information, in which case Kermit does not need to send SIZE and MDTM directives for each file (this is always true of MLSD). mget /mlsd foo MLSD foo Gets a list of all the files from the server's "foo" directory (where the string "foo" does not contain wildcards) and then downloads each one. If "foo" is a regular file and not a directory, this command is supposed to fail, but some servers have been observed that send the file. mget /mlsd *.txt MLSD Gets a list of all the files from the server's current directory and then downloads only the ones whose names match the pattern "*.txt". Because we are using MLSD and the MGET filespec is wild, we do not send the filespec to the server, but treat it as though it had been given in a /MATCH: switch and use it locally to match the names in the list. mget /mlsd foo/*.txt MLSD This one won't work because MLSD requires that the notions of server directory and filename-matching pattern be separated. However, the client, which can't be expected to know the server's file-system syntax, winds up sending a request that the server will (or should) reject. mget /mlsd /match:*.txt MLSD Gets a list of all the files from the server's current directory and then downloads only the ones whose names match the pattern "*.txt" (client interprets wildcards). mget /mlsd /match:*.txt foo MLSD foo If "foo" is a directory on the server, this gets a list of all the files from the server's "foo" directory and then downloads only the ones whose names match the pattern "*.txt" (client interprets wildcards). This leaves the server CD'd to the "foo" directory; there's no way the client can restore the server's original directory because MLSD doesn't give that information, and since the client can not be expected to know the server's file-system syntax, it would not be safe to guess. If "foo" is a regular file, MLSD fails. mget /mlsd foo bar MLSD This one is problematic. You're supposed to be able to give MGET a list a filespecs; in this case we name two directories. The client must change the server's directory to "foo" to get the list of files, and then the files themselves. But then it has no way to return to the server's previous directory in order to do the same for "bar", as explained in the previous example. mget /mlsd /match:* [abc] MLSD [abc] Including a /MATCH: switch forces [abc] to be sent to the server even though the client would normally think it was a wildcard and hold it for local interpretation. In this example, [abc] might be a VMS directory name. mget /mlsd /match:* t*.h MLSD t*.h Contrary to the MLSD specification, some MLSD-capable FTP servers do interpret wildcards. This form of the MGET command can be used to force a wildcard to be sent to the server for interpretation. When MLSD is used implicitly (that is, without an /MLSD switch given to force the use of MLSD) and an MGET command such as "mget foo/*.txt" fails, Kermit automatically falls back to NLST and tries again. 3.11.5. References 1. Postel, J., and J. Reynolds, File Transfer Protocol (FTP), RFC 959, October 1985: [372]ftp://ftp.isi.edu/in-notes/rfc959.txt. 2. Hethmon, P, and R. Elz, Feature negotiation mechanism for the File Transfer Protocol, RFC 2389, August 1998: [373]ftp://ftp.isi.edu/in-notes/rfc2389.txt. 3. Elz, R, and P. Hethmon, Extensions to FTP, Internet Draft draft-ietf-ftpext-mlst-16.txt, September 2002: [374]http://www.ietf.org/internet-drafts/draft-ietf-ftpext-mlst-16. txt. 4. [375]The Kermit FTP Client (overview). [ [376]Top ] [ [377]FTP Top ] [ [378]C-Kermit Home ] [ [379]Kermit Home ] 4. FILE SCANNING A new feature called file scanning is used in various contexts to determine if a file is text or binary, and if it is text, what kind of text. The overhead of file scanning is surprisingly tolerable, usually about a quarter second per file. File scanning is now used instead of filename patterns unless you SET FILE SCAN OFF, which restores the previous behavior. The primary benefit of file scanning is in file transfer. For all practical purposes, now you can stop worrying about whether a file should be sent in binary or text mode, or about sending mixtures of text and binary files in a single operation, or configuring and fine-tuning your lists of binary-file and text-file name patterns: now it all just works. File scanning is done by the file sender, which determines the type of each file before it sends it and informs the receiver (Kermit or FTP server) of the type. File scanning is NOT done by the receiver, because it is the sender's responsibility to determine each file's type, send the file in the right mode, and inform the receiver of the mode. If both transfer partners are capable of this (or any other) form of automatic text/binary mode switching, then files can be sent in both directions with no worries about corruption due to inappropriate transfer mode. (As noted in [380]Section 3, FTP servers don't do this, so this discussion does not apply when using Kermit to download from an FTP server.) The rest of this section is mainly for the curious. If you don't read it and simply accept all defaults, every file you send should go in the appropriate mode automatically. As always, however, for character-set translation to work for 7- and 8-bit character-set files, the appropriate SET FILE CHARACTER-SET command(s) must have been executed to identify their encoding (Kermit's default file character-set is neutral ASCII except on platforms like HP-UX or DG/UX, where the default file character-set is known). And of course, receiving is another matter -- obviously the other Kermit must also send each file in the appropriate mode. Scanning is more reliable than filename patterns simply because filenames are not reliable indicators of the file's contents. Classic examples include ".doc" files, which are binary if Microsoft Word documents but text on most other platforms, and ".com" files, which are binary on DOS and Windows but text on VMS. Anyway, nobody knows the naming conventions (if any) of all the applications (and persons!) on your computer. Scanning, on the other hand, determines each file's type by inspecting its contents rather than just looking at its name. Also, file patterns -- even when they work as intended -- categorize each file only as text or binary, whereas file scanning can make finer distinctions: BINARY Binary data, not to be converted in any way. Examples include binary machine code (executable programs), graphics images (GIF, JPG, etc), compressed files (Z, GZ, etc), archives and packages (ZIP, TAR, RPM, etc), object files and libraries (OBJ, DLL, etc). 7-BIT TEXT Text encoded in a 7-bit character set such as ASCII or one of the ISO 646 national versions. Kermit has no way to tell which character is used, only that it's 7-bit text. Typical examples include program source code, README files, Perl or Kermit scripts, plain-text email, HTML, TeX, and various textual encodings of binary files: Hex, Base64, etc. When sending such files, the FILE DEFAULT 7BIT-CHARACTER-SET is used as the file character-set, and then the appropriate transfer character set is chosen from the associations list (ASSOCIATE, SHOW ASSOCIATIONS). 8-BIT TEXT Text encoded in an 8-bit character set such as Latin-1, Latin-2, Latin/Hebrew, Latin/Cyrillic, KOI8, HP-Roman8, JIS X 0208, Code Page 437, or Code Page 1252. Again, Kermit has no way of knowing which particular set is in use, only that it's 8-bit text. When sending such files, the FILE DEFAULT 8BIT-CHARACTER-SET is used as the file character-set, and then the appropriate transfer character set is chosen from the associations list. UCS2 TEXT Unicode in its basic form, 16 bits (2 octets) per character. When sending such files, UCS2 is the file character-set and the byte order is identified automatically; the appropriate transfer character set is chosen from the associations list. Normally this would be UTF8. UTF-16 is not supported yet; Kermit's Unicode translations are restricted to Plane 0, the Base Multilingual Plane (BMP). UTF8 TEXT Unicode in its 8-bit transformation format. When sending such files, UTF8 is the file character-set; the appropriate transfer character set is chosen from the associations list, normally UCS2 or UTF8. File scanning is available in UNIX C-Kermit, in K-95, and to a limited extent, in VMS C-Kermit (full scanning is problematic in VMS because even plain-text files might contain binary record-format information). The relevant commands are: SET TRANSFER MODE { AUTOMATIC, MANUAL } Tells whether the file-transfer mode (text or binary) should be set by automatic or "manual" means. AUTOMATIC is the default, which allows any of the automatic methods that are enabled to do their jobs: FILE SCAN, FILE PATTERNS, peer recognition, etc. MANUAL lets you control the transfer mode with the SET FILE TYPE commands. As always, /TEXT and /BINARY switches on your file-transfer commands override all other methods; if you give one of these switches, scanning is not done. SHOW TRANSFER displays the current TRANSFER MODE setting. SET FILE SCAN { ON [ number ], OFF } Turns this feature on and off. It's ON by default. When OFF, the previous rules apply (SET FILE PATTERNS, etc). When ON is given, you can also specify a number of bytes to be scanned. The default is 49152 (= 48K). If a negative number is given, the entire file is scanned, no matter how big, for maximum certainty (for example, a PostScript file that appears to be plain text might include an embedded graphic past the normal scanning limit). SHOW FILE displays the current FILE SCAN setting. SET FILE DEFAULT 7BIT-CHARACTER-SET name Tells the 7-bit character-set to use if scanning identifies a 7-bit text file, e.g. GERMAN. SHOW FILE displays the current SET FILE DEFAULT settings. So does SHOW CHARACTER-SETS. SET FILE DEFAULT 8BIT-CHARACTER-SET name Tells the 8-bit character-set to use if scanning identifies an 8-bit text file, e.g. LATIN1. SHOW FILE and SHOW CHARACTER-SET display this. ASSOCIATE FILE-CHARACTER-SET fcs tcs When sending files and a file character-set (fcs) is identified by scanning, this tells C-Kermit which transfer character-set (tcs) to translate it to. It also allows C-Kermit to set the appropriate transfer character-set automatically whenever you give a SET FILE CHARACTER-SET command. ASSOCIATE TRANSFER-CHARACTER-SET tcs fcs When receiving files and a file arrives whose transfer character-set (tcs) is announced by the sender, this command tells C-Kermit which file character-set (fcs) to translate it to. It also allows C-Kermit to set the appropriate file character-set whenever you give a SET TRANSFER CHARACTER-SET command. SET FILE CHARACTER-SET name When given for a 7-bit set, also sets FILE DEFAULT 7BIT-CHARACTER-SET to the same set. When given for an 8-bit set, also sets FILE DEFAULT 8BIT-CHARACTER-SET to the same set. If an ASSOCIATE FILE-CHARACTER-SET command has been given for this set, also sets the corresponding transfer character-set. DIRECTORY /XFERMODE [ filespec ] Performs a file scan of the given files, listing the result for each file. If FILE SCAN is OFF but PATTERNS are ON, the result shown according to the current FILE TEXT-PATTERNS and BINARY-PATTERNS, and are restricted to (B) and (T). When FILE SCAN is ON, the results are: (B) Binary (T)(7BIT) Text: 7-bit (T)(8BIT) Text: 8-bit (T)(UTF8) Text: Unicode UTF8 (T)(UCS2BE) Text: Unicode UCS2 Big Endian (T)(UCS2LE) Text: Unicode UCS2 Little Endian So you can use DIR /XFER to get a preview of how each file in a selected group will be transferred. Everything to the right of the (B) or (T) is new. If FILE SCAN is OFF, you only get the (B) or (T) as before. Note: Big and Little Endian refer to the ordering of bytes within a computer word. Big Endian architecture is standard and is used on most non-PC computers. Little Endian architecture is used on PCs. To illustrate file-transfer with scanning, suppose you have a directory containing a mixture of text and binary files, and each text file can be 7-bit German ISO 646, 8-bit Latin-1, or Unicode in any of the following forms: UCS2 Little Endian, UCS2 Big Endian, or UTF8 ([381]UTF-16 is not supported yet). Assuming all the built-in defaults are in effect, the following three commands do the job: set file char german ; This sets the default for 7-bit text files set file char latin1 ; This sets the default for 8-bit text files send * Each file is sent in the appropriate mode (text or binary), with text files converted to the appropriate transfer character-set and labeled so the receiver can convert them according to its own local conventions. By the way, what if you want to inhibit character-set translation but still allow automatic text/binary mode switching? Previously, you could simply SET TRANSFER CHARACTER-SET TRANSPARENT. But now with file scanning, the file and transfer character-sets are set automatically per file. A new command was added for this purpose: SET TRANSFER TRANSLATION { ON, OFF } Enables and disables file-transfer character-set translation. It is enabled by default. When TRANSFER TRANSLATION is OFF but FILE SCAN is ON, files are still scanned to see if they are text or binary, but no character-set translation is done when they text: only the normal record-format conversion. Like all SET commands, SET TRANSFER TRANSLATION is global and persistent. You can also force a particular file-transfer command (SEND, MSEND, GET, RECEIVE, TRANSMIT, etc) to not translate without affecting the global translation settings by including the new /TRANSPARENT switch, e.g. send /transparent oofa.txt As of C-Kermit 8.0.206, SET TRANSFER CHARACTER-SET TRANSPARENT implies SET TRANSFER TRANSLATION OFF. File scanning is also used in the TYPE command. The source file type and character set are determined as above, and then the file is automatically converted to your display character-set, line by line. In Kermit 95, the display character-set is Unicode, perhaps converted to your current console code page; in other versions of C-Kermit, it is your current file character-set. Thus if you have the following set appropriately: SET FILE CHARACTER-SET (necessary in Unix but not K95) SET FILE DEFAULT 7BIT CHARACTER-SET SET FILE DEFAULT 8BIT CHARACTER-SET then you should be able to TYPE any text file and see something reasonable. For example, in Unix, if your DEFAULT 7BIT-CHARACTER-SET is ITALIAN and your DEFAULT 8BIT-CHARACTER-SET is LATIN1, and your FILE CHARACTER-SET is LATIN1, you can TYPE an Italian ISO 646 file, a Latin-1 file, or any kind of Unicode file, and have it translated automatically to Latin-1 for your display. In the GUI version of Kermit 95, you can see mixtures of many different scripts if the file is UTF8 or UCS2: Roman, Cyrillic, Hebrew, Greek, Armenian, Georgian, etc, all on the same screen at once. File scanning also adds a new criterion for file selection, i.e. to select only text (or binary) files. Several commands now include a new switch, /TYPE:{BINARY,TEXT,ALL}. BINARY means select only binary regular files (not directories). TEXT means select only text files. ALL means don't scan; select all files. Examples: SEND /TYPE:BINARY *.* Sends only binary files, skipping over text files. NOTE: File scanning is NOT done when using external protocols (because the external protocol programs, such as sz, are processing each file, not Kermit). DIRECTORY /TYPE:TEXT Lists only text files but not binary files. DELETE /TYPE:BINARY foo.* Deletes all foo.* files that are regular binary files but does not delete any text files. CHMOD /TYPE:BINARY 775 * (UNIX) Changes the permissions of all binary files to 775. When FILE SCAN is OFF and FILE PATTERNS are ON, behavior is as before with PATTERNS ON, but with some improvements: * Pathnames are now stripped prior to pattern matching. * Backup suffixes (like .~3~) are stripped prior to pattern matching. [ [382]Top ] [ [383]Contents ] [ [384]C-Kermit Home ] [ [385]Kermit Home ] 5. FILE AND DIRECTORY NAMES CONTAINING SPACES Prior to the introduction of the graphical user interface (GUI), it was inconceivable that file or directory names could contain spaces, because space is a field delimiter in all command languages. GUIs, however, use dialog boxes for filenames, so there is never any question of distinguishing a filename from adjacent fields -- because there are no adjacent fields -- and therefore it has become quite common on computers that have GUIs to have file and directory names composed of multiple words. Of course this poses problems for command shells and other text-oriented programs. Most command shells address these problems by allowing such names to be enclosed in doublequotes, e.g.: cd "c:\Program Files" C-Kermit previously used braces for this: cd {c:\Program Files} which was not what most people expected. And even when braces were used, Kermit had difficulties with completion, file menus, and so forth, within braced fields. C-Kermit 8.0 allows either doublequotes or braces to be used for grouping: send "this file" send {this file} rename "this file" "that file" rename {this file} "that file" rename "this file" {that file} cd {Program Files} cd "Program Files" Note that the doublequotes or brackets must enclose the whole file or directory specification: "c:\My Directory" not: c:\"My Directory" In C-Kermit 8.0, you can also use completion on these filenames, in which case Kermit supplies the quotes (or braces) automatically. Example (in which the current directory contains only one file whose name starts with "th" and its full name is "this file" (without the quotes, but with the space)): cat th Kermit repaints the filename field like this: cat "this file" That is, it backspaces over the original "th" and then writes the filename in doublequotes. If completion is only partial, Kermit still supplies the quotes, but in this case also beeps. To continue the filename, you must first backspace over the closing quote. The closing quote is supplied in this case to make sure that you can see the spaces, especially if they are trailing. For example, if the current directory contains two files whose names start with "th", and their fill names are "this file" and "this other file": cat th Kermit prints: cat "this " If it didn't print the closing quote, you would probably wonder why it was beeping. Also, if you begin a filename field with a doublequote or opening brace, now you can use completion or get ?-help; this was never possible before. C-Kermit>type "thi? Input file specification, one of the following: this file this other file C-Kermit>type "thi_ [ [386]Top ] [ [387]Contents ] [ [388]C-Kermit Home ] [ [389]Kermit Home ] 6. OTHER COMMAND PARSING IMPROVEMENTS 6.1. Grouping Macro Arguments Doublequotes now can be used in macro invocations to group arguments containing spaces, where previously only braces could be used: define xx show args xx one "this is two" three Result: Macro arguments at level 0 (\v(argc) = 4): \%0 = xx \%1 = one \%2 = this is two \%3 = three Also, you can now quote braces and quotes in macro args (this didn't work before). Examples: xx "{" ; The argument is a single left brace xx {"} ; The argument is a doublequote character In case this new behavior interferes with your scripts, you can restore the previous behavior with: SET COMMAND DOUBLEQUOTING OFF 6.2. Directory and File Name Completion C-Kermit 8.0 also includes better completion for directory names, e.g. in the CD command. If the name typed so far uniquely matches a directory name, it is completed (as before), but now if the directory contains any subdirectories, completion is partial (allowing you to supply additional path segments without backspacing); otherwise it is complete. Completion has also been improved for file and directory names that contain not only spaces (as described above) but also "metacharacters" such as asterisk (*) and tilde (~): now the field is repainted if necessary. For example, if the current directory contains only one file whose name contains "blah", then in: type *blah "*blah" is replaced by the filename. In earlier releases, the part typed so far was left on the command line (and in the history buffer), so even when the original command worked, the recalled version would not. Similarly for ~ (the nearly-universal Unix notation for username): type ~olga/x is repainted as (e.g.): type /users/home/olga/x(Beep) Speaking of command history, the new SHOW HISTORY command shows your command history and recall buffer. SAVE COMMAND HISTORY saves it into a file of your choice. 6.3. Passing Arguments to Command Files The method for passing arguments to command files has been improved. Prior to C-Kermit 7.0 there was no provision for doing this. In C-Kermit 7.0, the TAKE command was changed to allow arguments to be given after the filename: take commandfile arg1 arg2 ... This was accomplished by replacing the current \%1, \%2, etc, with the given arguments, since a new set of macro argument variables is created only when a macro is executed, not a command file. It is much more intuitive, however, if arguments to command files worked like those to macros: the command file sees the arguments as its own \%1, \%2, etc, but the caller's variables are not disturbed. C-Kermit 8.0 accomplishes this by automatically creating an intermediate temporary macro to start the command file (if any arguments were given), thus creating a new level of arguments as expected. 6.4. More-Prompting The familiar --more?-- prompt that appears at the end of each screenful of command-response output now accepts a new answer: G (Go) meaning "show all the rest without pausing and asking me any more questions". P (Proceed) is a synonym for G. 6.5. Commas in Macro Definitions As noted in the [390]C-Kermit manual, comma is used to separate commands in a macro definition. Even when the macro is defined on multiple lines using curly-brace block-structure notation without commas, the definition is still stored internally as a comma-separated list of commands. Therefore special tricks are needed to include a comma in a command. The classic example is: define foo { (some command) if fail echo Sorry, blah failed... } This would result in Kermit trying to execute a "blah" command. This could always be handled by enclosing the text in braces: define foo { (some command) if fail echo {Sorry, blah failed...} } but doublequotes (more intuitive) should have worked too. Now they do: define foo { (some command) if fail echo "Sorry, blah failed..." } 6.6. Arrow Keys As of version 8.0.201, C-Kermit on most platforms lets you access the command history buffer with arrow keys, just as you always could with control characters. The restrictions are: 1. Only Up and Down arrow keys are accepted. 2. Only 7-bit ANSI arrow-key sequences are understood (ESC followed by [ or uppercase letter O, followed by uppercase letter A or (up) B (down). This change was made to facilitate command recall in Linux-based PDAs that don't have a Control key, or at least not one that's easily (or always) accessible, such as the Sharp Zaurus SL5500. [ [391]Top ] [ [392]Contents ] [ [393]C-Kermit Home ] [ [394]Kermit Home ] 7. NEW COMMANDS AND SWITCHES See [395]Section 4 for more about file scanning and the /TYPE: switch. ASK[Q] [ /TIMEOUT:number /QUIET /DEFAULT:text ] variable [ prompt ] The new optional /TIMEOUT: switch for ASK and ASKQ causes the command to time out and and fail if no response is given within the specified number of seconds, 1 or greater (0 or less means no timeout, wait forever). This works just like SET ASK-TIMER, except its effect is local to the ASK command with which it is given and it does not disturb the global ask timer setting. The new /QUIET switch tells Kermit not to print an error message if the ASK or ASKQ command times out waiting for a response. Version 8.0.211 adds the /DEFAULT:text switch for ASK-Class commands (ASK, ASKQ, and GETOK). This lets you supply a default answer in case the user supplies an empty answer or the /TIMEOUT: switch was included and the time limit expired without an answer. In both these cases, the command succeeds. CAT filename Equivalent to TYPE /NOPAGE. CDUP Changes Kermit's local working directory to the parent of the current one. Equivalent to "cd .." in UNIX or Windows, "cd [-]" in VMS, "cd ^" in AOS/VS, etc; in other words, it's a platform-independent way of moving one level up in a directory tree. CHMOD [ switches ] permission files UNIX only. Sets file permissions for one or more files or directories. The permission must be given as an octal number, e.g. 664, 755. Switches: /DIRECTORIES, /FILES, /NOLIST, /PAGE, /DOTFILES, /LIST, /NOPAGE, /RECURSIVE, /TYPE:{TEXT,BINARY,ALL}, /SIMULATE. The /TYPE: switch allows selection of only text or binary files. For example, if you have a mixture of source files and executables, you can use "chmod /files /type:text 664" to give owner/group read/write and world read permission to the text files, and "chmod /files /type:binary 775" to give the same plus execute permission to the executables. Use /SIMULATE to see which files would be affected, without actually changing their permissions. CLEAR KEYBOARD-BUFFER Flushes any as-yet unread characters from the keyboard input buffer. Useful for flushing typeahead in scripts. CONTINUE When given at an interactive command prompt that was reached by issuing a PROMPT command (described in this section) from a script, this command returns to the script, continuing its execution at the command after the PROMPT command. In this context, CONTINUE is simply a more-intuitive synonym for END. COPY, RENAME, and TRANSLATE These commands now work on file groups if the target filename is a directory, e.g. "copy oofa.* ..", "rename * ~olga/tmp/" COPY /APPEND source destination The source file specification can now include wildcards, in which case all of the source files that match will go into the destination file in alphabetical order by name. DELETE /ASK Asks permission to delete each file before deleting it. In C-Kermit 7.0, the answers were "yes" (or "ok") and "no". C-Kermit 8.0 adds "go" (meaning, delete all the rest without asking) and "quit" (cancel the DELETE command and return to the prompt). DELETE /DIRECTORIES Deletes not only files but also directories. DELETE /RECURSIVE Deletes all files that match the given file specification in the current (or given) directory and all directories beneath it. DELETE /SUMMARY Prints only the number of files deleted and total size freed, without listing each file. DELETE /TREE Shorthand for DELETE /RECURSIVE /DIRECTORIES /DOTFILES/. Equivalent to Windows DELTREE or Unix "rm -Rf". If no file specification is given, the contents of the current directory, plus all of its subdirectories and their contents, are deleted. DELETE /TYPE:BINARY Delete only regular binary files (requires FILE SCAN ON). DELETE /TYPE:TEXT Delete only regular text files (requires FILE SCAN ON). DIRECTORY [ switches ] [ filespec [ filespec [ filespec ... ] ] ] The DIRECTORY command now accepts more than one file specification; e.g. "directory moon.txt sun.doc stars.*". DIRECTORY /NORECURSIVE xxx If xxx is a directory name, forces listing of the directory itself rather than its contents. DIRECTORY /FOLLOWLINKS xxx (UNIX only) Tells the DIRECTORY command to follow symbolic links. This not the default because it can cause endless loops. DIRECTORY /NOFOLLOWLINKS xxx (UNIX only) Tells the DIRECTORY command not to follow symbolic links, but rather, merely to list them. This is the default. DIRECTORY /OUTPUT:filename Sends the results of the DIRECTORY command to the given file. DIRECTORY /SUMMARY Prints only the number of directories and files and the total size, without listing each file. DIRECTORY /TYPE:{TEXT,BINARY} Shows only files of the selected type, based on file scan. DIRECTORY /XFERMODE Now shows results of file scan (see [396]Section 4). FOPEN [ switches ] channel filename As of version 8.0.211, FOPEN allows /dev/tty as a filename in Unix-based operating systems. FREAD /TRIM (8.0.211) Trims any trailing blanks or tabs from the item (such as a line of text) that it has read. FREAD /UNTABIFY (8.0.211) Converts Horizontal Tab characters to the appropriate number of spaces, based on VT100-like tab stops (1,9,17,25,...). GREP [ switches ] pattern files Similar to Unix grep command: displays file lines that match the given [397]pattern. Switches: /COUNT[:variable] Don't show the matching lines, just tell how many lines match. If a variable name is specified, the count is stored in the given variable. /DOTFILES Include files whose names begin with dot. /LINENUMBERS Show line numbers of matching lines. /NAMEONLY only list the names of files that contain matching lines, but not the lines themselves. /NOBACKUP Skip backup files. /NOCASE Ignore alphabetic case while pattern matching. /NODOTFILES skip files whose names start with dot (period). /NOLIST Suppress output but set SUCCESS or FAILURE according to search result. /NOMATCH Look for lines that do not match the pattern. /NOPAGE Don't pause between screens of output. /OUTPUT:filename Write results into the given file. /PAGE Pause between screens of output. /RECURSIVE Search files in subdirectories too. /TYPE:{TEXT,BINARY} Search only files of the specified type. Synonyms: FIND, SEARCH. GETOK /TIMEOUT:n /QUIET /DEFAULT:text The new /QUIET switch instructs GETOK, when given a timeout, not to print an error message if it times out. As of 8.0.211, a default answer can be supplied (see ASK). HEAD [ switches ] filename Equivalent to TYPE /HEAD [ other-switches ] filename. HELP DATE Explains date-time formats, including timezone notation and delta times. HELP FIREWALLS Explains the firewall negotiation capabilities of your version of Kermit. KCD [ symbolic-directory-name ] Changes Kermit's working directory to the named symbolic directory, such as such as exedir, inidir, startup, download, or and home. Type "kcd ?" for a list of symbolic directory names known to your copy of Kermit, or give the new ORIENTATION command for a more detailed explanation. If you give a KCD command without a directory name, Kermit returns to its "home" directory, which is determined in some way that depends on the underlying operating system, but which you can redefine with the (new) SET CD HOME command. Your home directory is shown by SHOW CD and it's also the value of the \v(home) variable. LICENSE Displays the C-Kermit license. L-commands When Kermit has a connection to a Kermit or FTP server, file management commands such as CD, DIRECTORY, and DELETE might be intended for the local computer or the remote server. C-Kermit 8.0.200 and earlier always executes these commands on the local computer. If you want them executed by the remote server, you have to prefix them with REMOTE (e.g. REMOTE CD) or use special R-command aliases (e.g. RCD = REMOTE CD, RDIR = REMOTE DIR, etc). But this feels unnatural to FTP users, who expect unprefixed file management commands to be executed by the remote server, rather than locally. C-Kermit 8.0.201 adds automatic locus switching to present an FTP-like interface for FTP connections and the normal Kermit interface for Kermit connections, and a SET LOCUS command (described below) to control whether or how this is done. For when LOCUS is REMOTE, a new set of commands was added for local management: LCD (Local CD), LDIR (Local DIR), etc. These are described below under SET LOCUS. MORE filename Equivalent to TYPE /PAGE. ORIENTATION Displays symbolic directory names and the corresponding variable names and values. The symbolic names, such as exedir, inidir, startup, download, and home, can be used as arguments to the new KCD command. PROMPT [ text ] For use in a macro or command file: enters interactive command mode within the current context ([398]Section 8.1). If the optional text is included, the prompt is set to it. The text can include variables, functions, etc, as in the SET PROMPT command. They are evaluated each time the prompt is printed. Unlike the SET PROMPT command, the text argument applies only to the current command level. Thus you can have different prompts at different levels. REMOTE SET MATCH { DOTFILE, FIFO } { ON, OFF } Allows the client to tell the server whether wildcards sent to the server should match dot files (files whose names begin with period) or FIFOs (named pipes). See SET MATCH. SET ATTRIBUTE RECORD-FORMAT { ON, OFF } Allows control of the Kermit's Record-Format attribute. Set this to OFF in case incoming file are refused due to unknown or invalid record formats if you want to accept the file anyway (and, perhaps, postprocess it to fix its record format). SET CD HOME [ directory ] Specifies the target directory for the CD and KCD commands, when they are given without an argument, and also sets the value of the \v(home) variable. SET EXIT HANGUP { OFF, ON } Normally ON, meaning that when Kermit exits, it also explicitly hangs up the current SET LINE / SET PORT serial port according to the current SET MODEM TYPE and SET MODEM HANGUP METHOD, and closes the port device if it was opened by Kermit in the first place (as opposed to inherited). SET EXIT HANGUP OFF tells Kermit not to do this. This can't prevent the operating system from closing the device when Kermit exits (and it's a "last close") but if the port or modem have been conditioned to somehow ignore the close and keep the connection open, at least Kermit itself won't do anything explicit to hang it up or close it. SET FILE EOF { CTRL-Z, LENGTH } Specifies the end-of-file detection method to be used by C-Kermit when sending and receiving text files, and in the TYPE and similar text-file oriented commands. The normal and default method is LENGTH. You can specify CTRL-Z when handling CP/M or MS-DOS format text files, in which a Ctrl-Z (ASCII 26) character within the file marks the end of the file. SET FILE LISTSIZE number Allocates space for the given number of filenames to be filled in by the wildcard expander. The current number is shown by SHOW FILE. If you give a command that includes a filename containing a wildcard (such as "*") that matches more files that Kermit's list has room for, you can adjust the list size with this command. SET FILE STRINGSPACE number Allocates space for the given amount of filename strings for use by the wildcard expander. The current number is shown by SHOW FILE. The number is the total number of bytes of all the file specifications that match the given wildcard. If you need to process a bigger list of files than your computer has memory for, you might be able use an external file list. The Kermit SEND and the FTP PUT and GET commands accept a /LISTFILE: switch, which gives the name of a file that contains the list of files to be transferred. Example for UNIX: !find . -print | grep / > /tmp/names ftp put /update /recursive /listfile:/tmp/names SET LOCUS { AUTO, LOCAL, REMOTE } Added in C-Kermit 8.0.201. Sets the locus for unprefixed file management commands such as CD, DIRECTORY, MKDIR, etc. When LOCUS is LOCAL these commands act locally and a REMOTE (or R) prefix (e.g. REMOTE CD, RCD, RDIR) is required to send file management commands to a remote server. When LOCUS is REMOTE, an L prefix is required to issue local file management commands (e.g. LCD, LDIR). The word LOCAL can't be used as a prefix since it is already used for declaring local variables. LOCUS applies to all types of connections, and thus is orthogonal to SET GET-PUT-REMOTE, which selects between Kermit and FTP for remote file-transfer and management commands. The default LOCUS is AUTO, which means we switch to REMOTE whenever an FTP connection is made, and to LOCAL whenever a non-FTP connection is made, and switch back accordingly whenever a connection is closed. So by default, Kermit behaves in its traditional manner unless you make an FTP connection, in which case it acts like a regular FTP client (but better :-) LOCUS applies to the following commands: Unprefixed Remote Local Description CD (CWD) RCD LCD Change (Working) Directory CDUP RCDUP LCDUP CD Up PWD RPWD LPWD Print Working Directory DIRECTORY RDIR LDIR Request a directory listing DELETE RDEL LDEL Delete (a) file(s) RENAME RREN LREN Rename a file MKDIR RMKDIR LMKDIR Create a directory RMDIR RRMDIR LRMDIR Remove a directory SET MATCH { DOTFILE, FIFO } { ON, OFF } Whether C-Kermit filename patterns (wildcards) should match filenames that start with dot (period), or (Unix only) FIFOs (named pipes). The defaults are to skip dotfiles in Unix but match them elsewhere, and to skip FIFOs. Applies to both interactive use and to server mode, when the server receives wildcards, e.g. in a GET command. Also see REMOTE SET MATCH. SET OPTIONS DIRECTORY /DOTFILES Now works for server listings too (UNIX only). Give this command prior to having Kermit enter server mode, and then it will show files whose names begin with dot (period) when sent a REMOTE DIRECTORY command. SET QUIET ON (as well as the -q command-line option) Now applies also to: + SET HOST connection progress messages. + "Press the X or E key to cancel" file-transfer message. + REMOTE CD response. + REMOTE LOGIN response. SET RECEIVE PERMISSIONS { ON, OFF } Tells C-Kermit whether to set the permissions of incoming files (received with Kermit protocol) from the permissions supplied in the file's Attribute packet (if any). Normally ON. Also see SET SEND PERMISSIONS. SET ROOT directory Like UNIX chroot, without requiring privilege. Sets the root for file access, does not allow reference to or creation of files outside the root, and can't be undone. SET SEND PERMISSIONS { ON, OFF } Tells C-Kermit whether to include file permissions in the attributes it includes with each file when sending with Kermit protocol. Also see SET RECEIVE PERMISSIONS. SET TCP { HTTP-PROXY, SOCKS-SERVER } /USER:name /PASSWORD:text These commands now allow specification of username and password. SET TERMINAL . . . (See [399]Section 12.) SET TRANSFER MESSAGE [ text ] Sets an initial text message to be displayed in the file-transfer display. The transfer message is automatically deleted once used, so must be set each time a message a desired. Any variables in the message are evaluated at the time the SET command is given. If the optional text is omitted, any transfer message that is currently set is removed. Synonym: SET XFER MSG. SHOW TRANSFER displays it if it has been set but not yet used. SHOW COMMUNICATIONS In C-Kermit 8.0, SHOW COMMUNICATIONS, when given in remote mode (i.e. before any connection has been established), tells the typical dialout device name for the particular platform on which it's running (e.g. TXA0: for VMS, or /dev/cua0p0 for HP-UX). On Unix platforms, it also tells the name of the lockfile directory. This way, you have an idea of what the SET LINE device name should look like, and if the SET LINE command fails, you know the name of the directory or device that is protected against you. SHOW VARIABLES [ name [ name [ ... ] ] ] In C-Kermit 8.0.201 you can request values of a list of built-in (\v(xxx)) variables. Each name is a pattern, as before, but now it a free pattern rather than an anchored one (explained in [400]Section 8.12) so now "show var date time" shows the values of all variables whose names include the strings "date" or "time". TAIL [ switches ] filename Equivalent to TYPE /TAIL [ other-switches ] filename. TRANSMIT /NOECHO [ other switches ] filename The /NOECHO switch is equivalent to giving the command SET TRANSMIT ECHO OFF prior to the TRANSMIT command, except the switch affects only the command with which it was given and does not affect the prevailing global setting. TRANSMIT /NOWAIT [ other switches ] filename The /NOWAIT switch is equivalent to giving the command SET TRANSMIT PROMPT 0 prior to the TRANSMIT command, except the switch affects only the command with which it was given and does not affect the prevailing global setting. TRANSMIT /NOWAIT /NOECHO /BINARY [ other switches ] filename When the TRANSMIT command is given with the /NOWAIT, /NOECHO, and /BINARY switches, this activates a special "blast the whole file out the communications connection all at once" mode that Kermit didn't have prior to version 8.0. There has been increasing demand for this type of transmission with the advent of devices that expect image (e.g. .JPG) or sound (e.g. .MP3) files as raw input. The obvious question is: how does the receiving device know when it has the whole file? This depends on the device, of course; usually after a certain amount of time elapses with nothing arriving, or else when Kermit hangs up or closes the connection. TYPE /CHARACTER-SET:name Allows you to specify the character set in which the file to be typed is encoded. TYPE /NUMBER Adds line numbers. TYPE /OUTPUT:filename Sends the results of the TYPE command to the given file. TYPE /TRANSLATE-TO:name Used in conjunction with TYPE /CHARACTER-SET:xxx; allows you to specify the character set in which the file is to be displayed. TYPE /TRANSPARENT Used to disable character-set translation in the TYPE command, which otherwise can take place automatically based on file scanning, even when /CHARACTER-SET and /TRANSLATE-TO switches are not given. VOID text Parses the text, evaluating any backslash items in it (such as function calls) but doesn't do anything further, except possibly printing error messages. Useful for invoking functions that have side effects without using or printing their direct results, e.g. "void \fsplit(\%a,&a)". Symbolic Links in UNIX The UNIX versions of C-Kermit have had /FOLLOWLINKS and /NOFOLLOWLINKS switches added to several commands to control the treatment of symbolic links. Different commands deal differently with symbolic links: Kermit SEND, FTP MPUT /NOFOLLOWLINKS is the default, which means symbolic links are skipped entirely. The alternative, /FOLLOWLINKS, should be used with caution, since an innocent link might point to a whole file system, or it might cause a loop. There is no way in Kermit or FTP protocol to send the link itself. We either skip them or follow them; we can't duplicate them. DIRECTORY /NOFOLLOWLINKS is the default, which means the DIRECTORY command lists symbolic links in a way that shows they are links, but it does not follow them. The alternative, /FOLLOWLINKS, follows links and gives information about the linked-to directories and files. DELETE, RMDIR The DELETE command does not have link-specific switches. DELETE never follows links. If you tell Kermit to delete a symbolic link, it deletes the link itself, not the linked-to file. Ditto for RMDIR. COPY The COPY command behaves just like the UNIX cp command; it always follows links. RENAME The RENAME command behaves just like the UNIX mv command; it operates on links directly rather than following. [ [401]Top ] [ [402]Contents ] [ [403]C-Kermit Home ] [ [404]Kermit Home ] 8. OTHER SCRIPTING IMPROVEMENTS 8.1. Performance and Debugging A command cache for frequently used commands plus some related optimizations increases the speed of compute-bound scripts by anywhere from 50% to 1000%. The new PROMPT command can be used to set breakpoints for debugging scripts. If executed in a command file or macro, it gives you an interactive command prompt in the current context of the script, with all its variables, arguments, command stack, etc, available for examination or change, and the ability to resume the script at any point (END resumes it, Ctrl-C or STOP cancels it and returns to top level). The new Ctrl-C trapping feature ([405]Section 8.14) lets you intercept interruption of scripts. This can be used in combination with the PROMPT command to debug scripts. Example: define ON_CTRLC { echo INTERRUPTED BY CTRL-C... echo The command stack has not yet been rolled back: show stack echo Type Ctrl-C again or use the END command to return to top level. prompt Debug> } Adding this ON_CTRL definition to your script lets you interrupt it at any point and get prompt that is issued at the current command level, so you can query local variables, etc. [ [406]Top ] [ [407]Contents ] [ [408]C-Kermit Home ] [ [409]Kermit Home ] 8.2. Using Macros as Numeric Variables A macro is a way to assign a value to a name, and then use the name to refer to the value. Macros are used in two ways in Kermit: as "subroutines" or functions composed of Kermit commands, which are executed, or as variables to hold arbitrary values -- text, numbers, filenames, etc. When a macro is to be executed, its name is given as if it were a C-Kermit command, optionally preceded by the word "do". When a macro is used as a variable, it must be "escaped" with \m(xxx) (or equivalent function, e.g. \s(xxx), \:(xxx), \fdefinition(xxx)), where xxx is the macro name, for example: define filename /usr/olga/oofa.txt send \m(filename) Of course variables can also hold numbers: define size 17 declare \&a[\m(size)] ... define index 3 if ( == \m(index) 3 ) echo The third value is: \&a[\m(index)] evaluate index (\m(index) * 4) if ( > \m(index) \m(size) ) echo Out of range! But these are contexts in which only numbers are valid. C-Kermit 8.0 has been changed to treat non-escaped non-numeric items in strictly numeric contexts as macro names. So it is now possible (but not required) to omit the \m(...) notation and just use the macro name in these contexts: define size 17 declare \&a[size] ... define index 3 if ( == index 3 ) echo The third value is: \&a[index] evaluate index (index * 4) if ( > index size ) echo Out of range! This is especially nice for loops that deal with arrays. Here, for example, is a loop that reverses the order of the elements in an array. Whereas formerly it was necessary to write: .\%n ::= \fdim(&a) for \%i 1 \%n/2 1 { .tmp := \&a[\%n-\%i+1] .\&a[\%n-\%i+1] := \&a[\%i] .\&a[\%i] := \m(tmp) } Recoding this to use macro names "i" and "n" instead of the backslash variables \%i and \%n, we have: .n ::= \fdim(&a) for i 1 n/2 1 { .tmp := \&a[n-i+1] .\&a[n-i+1] := \&a[i] .\&a[i] := \m(tmp) } which reduces the backslash count to less than half. The final statement in the loop could be written ".\&a[i] ::= tmp" if the array contained only numbers (since ::= indicates arithmetic expression evaluation). Also, now you can use floating-point numbers in integer contexts (such as array subscripts), in which case they are truncated to an integer value (i.e. the fractional part is discarded). Examples of numeric contexts include: * Array subscripts. * Any numeric function argument. * Right-hand side of ::= assignments. * EVALUATE command or \fevaluate() function expression. * The INCREMENT or DECREMENT by-value. * IF =, >, <, !=, >=, and <= comparands. * The IF number construct. * FOR-loop variables. * STOP, END, and EXIT status codes. * The INPUT timeout value. * PAUSE, WAIT, SLEEP, MSLEEP intervals. * The SHIFT argument. * Numeric switch arguments, e.g. TYPE /WIDTH:number, SEND /LARGER:number. * SCREEN MOVE-TO row and column number. * Various SET DIAL parameters (timeout, retry limit, etc). * Various SET SEND or RECEIVE parameters (packet length, window size, etc). * Various other SET parameters. and: * S-Expressions (explained in [410]Section 9). Macro names used in numeric contexts must not include mathematical operators. Although it is legal to create a macro called "foo+bar", in a numeric context this would be taken as the sum of the values of "foo" and "bar". Any such conflict can be avoided, of course, by enclosing the macro name in \m(...). [ [411]Top ] [ [412]Contents ] [ [413]C-Kermit Home ] [ [414]Kermit Home ] 8.3. New IF Conditions Several new IF conditions are available: IF DECLARED arrayname Explained in [415]Section 8.6. IF KBHIT Allows a script to test whether a key was pressed without actually trying to read it. IF KERBANG (Unix only) True if Kermit was started from a Kerbang script. This is useful for knowing how to interpret the \&@[] and \&_[] argument vector arrays, and under what conditions to exit. IF INTEGER n This is just a synonym for IF NUMERIC, which is true if n contains only digits (or, if n is a variable, its value contains only digits). By contrast, IF FLOAT n succeeds if n is a floating-point number OR an integer (or a variable with floating-point or integer value). Therefore, IF FLOAT should be used whenever any kind of number is acceptable, whereas IF INTEGER (or IF NUMERIC) when only an integer can be used. [ [416]Top ] [ [417]Contents ] [ [418]C-Kermit Home ] [ [419]Kermit Home ] 8.4. The ON_UNKNOWN_COMMAND Macro The new ON_UNKNOWN_COMMAND macro, if defined, is executed whenever you give a command that is not known to C-Kermit; any operands are passed as arguments. Here are some sample definitions: DEF ON_UNKNOWN_COMMAND telnet \%1 ; Treat unknown commands as hostnames DEF ON_UNKNOWN_COMMAND dial \%1 ; Treat unknown commands phone numbers DEF ON_UNKNOWN_COMMAND take \%1 ; Treat unknown commands as filenames DEF ON_UNKNOWN_COMMAND !\%* ; Treat unknown commands as shell commands The ON_CD macro, if defined, is executed whenever Kermit is given a CD (change directory) command (8.0.211). Upon entry to this macro, the directory has already changed and the new directory string is available in the \v(directory) variable, and also as the first argument (\%1). [ [420]Top ] [ [421]Contents ] [ [422]C-Kermit Home ] [ [423]Kermit Home ] 8.5. The SHOW MACRO Command The SHOW MACRO command has been changed to accept more than one macro name: (setq a 1 b 2 c 3) show mac a b c a = 1 b = 2 c = 3 An exact match is required for each name (except that case doesn't matter). If you include wildcard characters, however, a pattern match is performed: show mac [a-c]*x shows all macros whose names start with a, b, or c, and end with x. [ [424]Top ] [ [425]Contents ] [ [426]C-Kermit Home ] [ [427]Kermit Home ] 8.6. Arrays A clarification regarding references to array names (as opposed to array elements): You can use array-name "abbreviations" like &a only in contexts that expect array names, like ARRAY commands or array-name function arguments such as the second argument of \fsplit(). In a LOCAL statement, however, you have to write \&a[], since "local &a" might refer to a macro named "&a". In function arguments, however, you MUST use the abbreviated form: \fsplit(\%a,&a) or \fsplit(\%a,&a[]). If you include the backslash (as in "\fsplit(\%a,\&a[])") a parse error occurs. Here are the new array-related commands: IF DECLARED arrayname Allows a script to test whether an array has been declared. The arrayname can be a non-array backslash variable such as \%1 or \m(name), in which case it is evaluated first, and the result is treated as the array name. Otherwise, arrayname is treated as in the ARRAY commands: it can be a, &a, &a[], \&a, \&a[], \&a[3], \&a[3:9], etc, with the appropriate results in each case. Synonym: IF DCL. UNDECLARE arrayname UNDECLARE is a new top-level command to undeclare an array. Previously this could only be done with "declare \&a[0]" (i.e. re-declare the array with a dimension of 0). ARRAY LINK linkname arrayname Creates a symbolic link from the array named by linkname (which must be the name of an array that is not yet declared in the current context) to the array named by arrayname (which must the name of a currently declared array that is not itself a link, or a variable containing the name of such an array). The two names indicate the same array: if you change an array element, the change is reflected in the link too, and vice versa. If you undeclare the link, the real array is unaffected. If you undeclare the real array, all links to it disappear. If you resize an array (directly or through a link), all links to it are updated automatically. Array links let you pass array names as arguments to macros. For example, suppose you had a program that needed to uppercase all the elements of different arrays at different times. You could write a macro to do this, with the array name as an argument. But without array links, there would be no way to refer to the argument array within the macro. Array links make it easy: define arrayupper { local \&e[] \%i array link \&e[] \%1 for i 1 \fdim(&e) 1 { .\&e[i] := \fupper(\&e[i]) } } declare \&a[] = these are some words arrayupper &a show array &a The macro declares the array link LOCAL, which means it doesn't conflict with any array of the same name that might exist outside the macro, and that the link is destroyed automatically when the macro exits. This works, by the way, even if the link name and the macro argument name are the same, as long as the link is declared LOCAL. As noted, you can't make a link to a nonexistent array. So when writing a macro whose job is to create an array whose name is passed as an argument, you must declare the array first (the size doesn't matter as long as it's greater than 0). Example: define tryme { ; Demonstration macro local \&e[] ; We only need this inside the macro array link \&e[] \%1 ; Make local link shift ; Shift argument list void \fsplit(\%*,&e) ; Split remainder of arg list into array } declare \&a[1] ; Declare target array in advance tryme &a here are some words ; Invoke the macro with array name and words show array a ; See the results One final improvement allows the macro itself to declare the array (this was not possible in earlier Kermit releases): if the array name in the DECLARE command is a variable (and not an array name), or includes variables, the resulting value is used as the array name. So: define tryme { ; Demonstration macro declare \%1[1] ; Preliminary declaration for target array local \&e[] ; We only need this inside the macro array link \&e[] \%1 ; Make local link shift ; Shift argument list void \fsplit(\%*,&e) ; Split remainder of arg list into array } tryme &a here are some words ; Invoke the macro with array name and words show array a ; See the results The SHOW ARRAY command now indicates whether an array name is a link. Also see the descriptions of [428]\fjoin() and [429]\fsplit(), plus [430]Section 8.10 on the MINPUT command, which shows how an entire array (or segment of it) can be used as the MINPUT target list. [ [431]Top ] [ [432]Contents ] [ [433]C-Kermit Home ] [ [434]Kermit Home ] 8.7. New or Improved Built-in Variables and Functions The following new built-in variables are available: \v(buildid) A date string like "20000808" indicating when C-Kermit was b uilt. \v(ftime) Current time, secs since midnight, including fraction of sec ond. \v(iprompt) The current SET PROMPT value \v(sexp) The most recent S-Expression (see [435]Section 9) \v(sdepth) The current S-Expression invocation depth ([436]Section 9) \v(svalue) The value of the most recent S-Expression ([437]Section 9) \v(ftp_code) Most recent FTP response code ([438]Section 3) \v(ftp_connected) FTP connection status ([439]Section 3) \v(ftp_cpl) FTP Command Protection Level ([440]Section 3.2) \v(ftp_dpl) FTP Data Protection Level ([441]Section 3.2) \v(ftp_getputremote) The current SET GET-PUT-REMOTE setting ([442]Section 3.8) \v(ftp_host) Name or IP address of FTP server ([443]Section 3) \v(ftp_loggedin) FTP login status ([444]Section 3) \v(ftp_message) Most recent FTP response message ([445]Section 3) \v(ftp_security) FTP Security method ([446]Section 3.2) \v(ftp_server) OS type of FTP server ([447]Section 3) \v(http_code) Most recent HTTP response code \v(http_connected) HTTP connection status \v(http_host) Name or IP address of HTTP server \v(http_message) Most recent HTTP response message \v(http_security) TLS cipher used to secure the HTTP session \v(hour) Hour of the day, 0 to 23. \v(timestamp) Equivalent to "\v(ndate) \v(time)". \v(log_debug) Current debug log file, if any. \v(log_packet) Current packet log file, if any. \v(log_session) Current session log file, if any. \v(log_transaction) Current transaction log file, if any. \v(log_connection) Current connection log file, if any. The following new or improved built-in functions are available: \fcmdstack() Allows programmatic access to the command stack. \fcvtdate() [448]Section 8.13, format options added \fdelta2secs() [449]Section 8.13 \fdostounixpath(s1) Converts a DOS filename to Unix format. \fsplit() Now allows grouping/nesting in source string. \fword() Allows the same grouping and nesting. \fjoin(&a,s1,n1,n2) Copies an array into a single string. \fsubstitute(s1,s2,s3) Substitutes characters within a string. \freplace() Has new 4th "occurrence" argument. \fsexpression() Evaluates an S-Expression (explained in [450]Section 9 ). \ftrim(), \fltrim() Now trim CR and LF by default, as well as SP and Tab. \funixtodospath(s1) Converts a Unix filename to DOS format. \fkeywordval(s1,c1) Assigns values to keywords (macros) (explained below). Most functions that have "2" in their names to stand for the word "to" can now also be written with "to", e.g. "\fdelta2secs()," \fdeltatosecs()." \funtabify(string) (New to 8.0.211) Replaces Horizontal Tab characters in the given string with spaces based on VT100-like tab stops. \fverify(s1,s2,n) As of version 8.0.211, returns -1 if s2 is an empty string. Previously it returned 0, making \fverify(abc,\%a) look as if \%a was a string composed of a's, b's, and/or c's when in fact it contained nothing. \fcode(string) As of version 8.0.211, returns 0 if string is empty or missing. Previously it returned the empty string, which made it unsafe to use in arithmetic or boolean expressions. \v(inscale) New to version 8.0.211, its value is the INPUT SCALE-FACTOR ([451]Section 8.10), default 1.0. 8.7.1. The \fkeywordval() Function \fkeywordval(s1,c1) is new to C-Kermit 8.0. Given a string s1 of the form "name=value", it creates a macro with the given name and assigns it the given value. If no value appears after the equal sign, any existing macro of the given name is undefined. Blanks are automatically trimmed from around the name and value. The optional c1 parameter is the assignment operator character, equal sign (=) by default. This function is handy for processing keyword parameters or any other form of parameter-value pair. Suppose, for example, you want to write a macro that accepts keyword parameters rather than positional ones: define MYDIAL { local \%i modem hangup method device speed number def number 5551234 ; Assign default parameter values def speed 57600 def modem usrobotics def hangup rs232 def method tone def country 1 for \%i 1 \v(argc)-1 1 { ; Parse any keyword parameters... if not \fkeywordval(\&_[\%i]) end 1 Bad parameter: "\&_[\%i]" } set dial country \m(country) set modem type \m(modem) set modem hang \m(hangup) set dial method \m(tone) set line \m(device) if fail stop 1 set speed \m(speed) if fail stop 1 show comm set dial display on dial \m(number) if success connect } In this example, all the defaults are set up inside the macro, and therefore it can be invoked with no parameters at all. But if you want to have the macro dial a different number, you can supply it as follows: mydial number=7654321 You can supply any number of keyword parameters, and you can give them in any order: mydial number=7654321 hangup=modem speed=115200 8.7.2. The \fsplit(), \fjoin(), and \fword() Functions \fjoin(&a,s1,n1,n2) is also new; it creates a string from an array (or a piece of one). &a is the name of the array (a range specifier can be included); s1 is a character or string to separate each element in the result string (can be omitted, in which case the elements are not separated at all), and n1 is a grouping mask, explained below. If s1 is empty or not specified, the array elements are separated with spaces. If you want the elements concatenated with no separator, include a nonzero n2 argument. Given the array: declare \&a[] = 0 1 2 3 4 5 6 7 8 9 you can get effects like this: \fjoin(&a) 0 1 2 3 4 5 6 7 8 9 \fjoin(&a,:) 0:1:2:3:4:5:6:7:8:9 \fjoin(&a,{,}) 0,1,2,3,4,5,6,7,8,9 \fjoin(&a,...) 0...1...2...3...4...5...6...7...8...9 \fjoin(&a,,,1) 0123456789 \fsplit(), \fword(), \fstripb(), and \fjoin() accept a "grouping mask" argument, n1, which is a number from 0 to 63, in which: 1 = "" doublequotes 2 = {} braces 4 = '' singlequotes 8 = () parentheses 16 = [] square brackets 32 = <> angle brackets These can be OR'd (added) together to make any number 0-63 (-1 is treated the same as 63, 0 means no grouping). If a bit is on, the corresponding kind of grouping is selected. (If more than 1 bit is set for \fjoin(), only the lowest-order one is used.) If you include the same character in the grouping mask and the include list, the grouping mask takes precedence. Example: def \%a a "b c d" e \fsplit(\%a,&a[],,,-1) = 3 <-- doublequote used for grouping \fsplit(\%a,&a[],,",-1) = 3 <-- doublequote still used for grouping Nesting of matched left and right grouping characters (parentheses, braces, and brackets, but not quotes) is recognized. Example: def \%a a (b c n o) p \fsplit(\%a,&a,,,0) = 16 (no grouping) \fsplit(\%a,&a,,,2) = 15 (braces only) \fsplit(\%a,&a,,,16) = 11 (square brackets only) \fsplit(\%a,&a,,,32) = 7 (angle brackets only) \fsplit(\%a,&a,,,63) = 3 (all) \fsplit(\%a,&a,,,-1) = 3 (all) \fsplit() and \fjoin() are "reciprocal" functions. You can split a string up into an array and join it back into a new string that is equivalent, as long as \fsplit() and \fjoin() are given equivalent grouping masks, except that the type of braces might change. Example: def \%a a {b c [d e] f g} "h i" j m echo STRING=[\%a] echo WORDS=\fsplit(\%a,&a,,,-1) show array a asg \%b \fjoin(&a,{ },2) echo JOIN =[\%b] echo WORDS=\fsplit(\%b,&b,,,-1) show array b The arrays a and b are identical. The strings a and b are as follows: \%a: a {b c [d e] f g} "h i" j m \%b: a {b c [d e] f g} {h i} j {k l} m It is possible to quote separator grouping characters with backslash to override their grouping function. And of course to include backslash itself in the string, it must be quoted too. Furthermore, each backslash must be doubled, so the command parser will still pass one backslash to \fsplit() for each two that it sees. Here are some examples using \fsplit() with a grouping mask of 8 (treat parentheses as grouping characters). String Result a b c d e f 6 a b\\ c d e f 5 a b (c d e) f 4 a b \\(c d e\\) f 6 a b \\\\(c d e\\\\) f 7 \fsplit() has also been changed to create its array (if one is given) each time it is called, so now it can be conveniently called in a loop without having to redeclare the array each time. Incidentally... Sometimes you might want to invoke \fsplit() in a situation where you don't care about its return value, e.g. when you just want to fill the array. Now you can "call" \fsplit() or any other function with the new [452]VOID command: void \fsplit(\%a,&a) \fsplit() and \fjoin() also accept a new, optional 6th argument, an options flag, a number that can specify a number of options. So far there is just one option, whose value is 1: separator-flag Normally separators are collapsed. So, for example, \fword(Three little words,2) returns "little" (the second word). Space is a separator, but there are multiple spaces between each word. If the value 1 is included in the option flag, however, each separator counts. If two separators are adjacent, an empty word is produced between them. This is useful for parsing (e.g.) comma-separated lists exported from databases or spreadsheets. 8.7.3. The \fcmdstack() Function The new \fcmdstack() function gives access to the command stack: \fcmdstack(n1,n2) Arguments: n1 is the command stack level. If omitted, the current level, \v(cmdlevel), is used. n2 is a function code specifying the desired type of information: 0 (default) = name of object at level n1. 1 (nonzero) = object type (0 = prompt; 1 = command file; 2 = macro). The default for n2 is 0. The name associated with prompt is "(prompt)". Here's a loop that can be included in a macro or command file to show the stack (similar to what the SHOW STACK command does): for \%i \v(cmdlevel) 0 -1 { echo \%i. [\fcmdstack(\%i,1)] \fcmdstack(\%i,0) } In this connection, note that \v(cmdfile) always indicates the most recently invoked active command file (if any), even if that file is executing a macro. Similarly, \v(macro) indicates the most recently invoked macro (if any), even if the current command source is not a macro. The name of the "caller" of the currently executing object (command file or macro) is: \fcmdstack(\v(cmdlevel)-1) and its type is: \fcmdstack(\v(cmdlevel)-1,1) To find the name of the macro that invoked the currently executing object, even if one or more intermediate command files (or prompting levels) are involved, use a loop like this: for \%i \v(cmdlevel)-1 0 -1 { if = \fcmdstack(\%i,1) 2 echo CALLER = \fcmdstack(\%i,0) } Of course if you make a macro to do this, the macro must account for its own additional level: define CALLER { for \%i \v(cmdlevel)-2 0 -1 { if = \fcmdstack(\%i,1) 2 return \fcmdstack(\%i,0) } return "(none)" } The built-in variable \v(cmdsource) gives the current command source as a word ("prompt", "file", or "macro"). 8.7.4. The VOID Command VOID is like ECHO in that all functions and variables in its argument text are evaluated. but it doesn't print anything (except possibly an error message if a function was invocation contained or resulted in any errors). VOID sets FAILURE if it encounters any errors, SUCCESS otherwise. [ [453]Top ] [ [454]Contents ] [ [455]C-Kermit Home ] [ [456]Kermit Home ] 8.8. The RETURN and END Commands The execution of a macro is terminated in any of the following ways: * With an END [ number [ message ] ] command. If a number is given, the macro succeeds if the number is 0, and fails if it is not zero; if a number is not given, the macro succeeds. * With a STOP command, which works just like END except it peels back the command stack all the way to top level. * With a RETURN [ text ] command, in which case the macro always succeeds. * By running out of commands to execute, in which case the macro succeeds or fails according the most recently executed command that sets success or failure. The same considerations apply to command files invoked by the TAKE command. If a macro does not execute any commands that set success or failure, then invoking the macro does not change the current SUCCESS/FAILURE status. It follows, then, that the mere invocation of a macro does not change the SUCCESS/FAILURE status either. This makes it possible to write macros to react to the status of other commands (or macros), for example: define CHKLINE { if success end 0 stop 1 SET LINE failed - please try another device. } set modem type usrobotics set line /dev/cua0 chkline set speed 57600 dial 7654321 By the way, none of this is news. But it was not explicitly documented before, and C-Kermit 7.0 and earlier did not always handle the RETURN statement as it should have. [ [457]Top ] [ [458]Contents ] [ [459]C-Kermit Home ] [ [460]Kermit Home ] 8.9. UNDEFINing Groups of Variables The UNDEFINE command, which previously accepted one variable name, now accepts a list of them, and also accepts wildcard notation to allow deletion of variables that match a given pattern. UNDEFINE [ switches ] name [ name [ name [ ... ] ] ] Undefines the variables whose names are given. Up to 64 names may be given in one UNDEFINE command. If you omit the switches and include only one name, the UNDEFINE command works as before. Switches include: /MATCHING Specifies that the names given are to treated as patterns rather than literal variable names. Note: pattern matching can't be used with array references; use the ARRAY command to manipulate arrays and subarrays. /LIST List the name of each variable to be undefined, and whether it was undefined successfully ("ok" or "error"), plus a summary count at the end. /SIMULATE List the names of the variables that would be deleted without actually deleting them. Implies /LIST. The UNDEFINE command fails if there were any errors and succeeds otherwise. The new _UNDEFINE command is like UNDEFINE, except the names are assumed to be variable names themselves, which contain the names (or parts of them) of the variables to be undefined. For example, if you have the following definitions: define \%a foo define foo This is some text then: undef \%a undefines the variable \%a, but: _undef \%a undefines the macro foo. Normal Kermit patterns are used for matching; metacharacters include asterisk, question mark, braces, and square brackets. Thus, when using the /MATCHING switch, if the names of the macros you want to undefine contain any of these characters, you must quote them with backslash to force them to be taken literally. Also note that \%* is not the name of a variable; it is a special notation used within a macro for "all my arguments". The command "undef /match \%*" deletes all \%x variables, where x is 0..9 and a..z. Use "undef /match \%[0-9]" to delete macro argument variables or "undef /match \%[i-n]" to delete a range of \%x variables. [ [461]Top ] [ [462]Contents ] [ [463]C-Kermit Home ] [ [464]Kermit Home ] 8.10. The INPUT and MINPUT Commands As of C-Kermit 8.0.211, the INPUT and MINPUT commands accept a switch: [M]INPUT /NOMATCH timeout The /NOMATCH switch allows INPUT or MINPUT to read incoming material for the specified amount of time, without attempting to match it with any text or patterns. When this switch is included, the [M]INPUT command succeeds when the timeout interval expires, with \v(instatus) set to 1, meaning "timed out", or fails upon interruption or i/o error. Also in version 8.0.211, there is a new way to apply a scale factor to [M]INPUT timeouts: SET INPUT SCALE-FACTOR floating-point-number This scales all [M]INPUT timeouts by the given factor, allowing time-sensitive scripts to be adjusted to changing conditions such as congested networks or different-speed modems without having to change each INPUT-class command. This affects only those timeouts that are given in seconds, not as wall-clock times. Although the scale factor can have a fractional part, the INPUT timeout is still an integer. The new built-in variable \v(inscale) tells the current INPUT SCALE-FACTOR. The MINPUT command can be used to search the incoming data stream for several targets simultaneously. For example: MINPUT 8 one two three waits up to 8 seconds for one of the words "one", "two", or "three" to arrive. Words can be grouped to indicate targets that contain spaces: MINPUT 8 nineteen twenty "twenty one" And of course you can also use variables in place of (or as part of) the target names: MINPUT 8 \%a \&x[3] \m(foo) Until now you had to know the number of targets in advance when writing the MINPUT statement. Each of the examples above has exactly three targets. But suppose your script needs to look for a variable number of targets. For this you can use arrays and \fjoin(), described in [465]Section 8.7. Any number of \fjoin() invocations can be included in the MINPUT target list, and each one is expanded into the appropriate number of separate targets each time the MINPUT command is executed. Example: declare \&a[10] = one two three minput 10 foo \fjoin(&a) bar This declares an array of ten elements, and assigns values to the first three of them. The MINPUT command looks for these three (as well as the words "foo" and "bar"). Later, if you assign additional elements to the array, the same MINPUT command also looks for the new elements. If an array element contains spaces, each word becomes a separate target. To create one target per array element, use \fjoin()'s grouping feature: dcl \&a[] = {aaa bbb} {ccc ddd} {xxx yyy zzz} minput 10 \fjoin(&a) <-- 7 targets minput 10 \fjoin(&a,,2) <-- 3 targets [ [466]Top ] [ [467]Contents ] [ [468]C-Kermit Home ] [ [469]Kermit Home ] 8.11. Learned Scripts C-Kermit now includes a simple script recorder that monitors your commands, plus your actions during CONNECT mode, and automatically generates a script program that mimics what it observed. You should think of this feature as a script-writing ASSISTANT since, as you will see [470]later in this section, the result generally needs some editing to make it both secure and flexible. The script recorder is controlled by the new LEARN command: LEARN [ /ON /OFF /CLOSE ] [ filename ] If you give a filename, the file is opened for subsequent recording. The /ON switch enables recording to the current file (if any); /OFF disables recording. /CLOSE closes the current script recording file (if any). If you give a filename without any switches, /ON is assumed. The /OFF and /ON switches let you turn recording off and on during a session without closing the file. When recording: * All commands that you type (or recall) at the prompt are recorded in the file except: + LEARN commands are not recorded. + The CONNECT command is not recorded. + The TELNET command is converted to SET HOST /NETWORK:TCP. * Commands obtained from macros or command files are not recorded. * During CONNECT: + Every line you type is converted to an OUTPUT command. + The last prompt before any line you type becomes an INPUT command. + Timeouts are calculated automatically for each INPUT command. + A PAUSE command is inserted before each OUTPUT command just to be safe. Thus the script recorder is inherently line-oriented. It can't be used to script character-oriented interactions like typing Space to a "More?" prompt or editing a text file with VI or EMACS. But it has advantages too; for example it takes control characters into account that might not be visible to you otherwise, and it automatically converts control characters in both the input and output streams to the appropriate notation. It can tell, for example that the "$ " prompt on the left margin in UNIX is really {\{13}\{10}$ }, whereas in VMS it might be {\{13}\{10}\{13}$ }. These sequences are detected and recorded automatically. A learned script should execute correctly when you give a TAKE command for it. However, it is usually appropriate to edit the script a bit. The most important change would be to remove any passwords from it. For example, if the script contains: INPUT 9 {\{13}\{10}Password: } IF FAIL STOP 1 INPUT timeout PAUSE 1 OUTPUT bigsecret\{13} you should replace this by something like: INPUT 9 {\{13}\{10}Password: } IF FAIL STOP 1 INPUT timeout ASKQ pswd Please type your password: PAUSE 1 OUTPUT \m(pswd)\{13} The LEARN command can't do this for you since it knows nothing about "content"; it only knows about lines and can't be expected to parse or understand them -- after all, the Password prompt might be in some other language. So remember: if you use the LEARN command to record a login script, be sure edit the resulting file to remove any passwords. Also be sure to delete any backup copies your editor or OS might have made of the file. Other manual adjustments might also be appropriate: * If the target of an INPUT command can vary, you can replace the INPUT command with MINPUT and the appropriate target list, and/or the target with a \fpattern(). For example, suppose you are dialing a number that can be answered by any one of 100 terminal servers, whose prompts are ts-00>, ts-01>, ts-02>, ... ts-99>. The script records a particular one of these, but you want it to work for all of them, so change (e.g.): INPUT 10 ts-23> ; or whatever to: INPUT 10 \fpattern(ts-[0-9][0-9]>) * The INPUT timeout values are conservative, but they are based only on a single observation; you might need to tune them. * The PAUSE commands might not be necessary, or the PAUSE interval might need adjustment. * In case you made typographical errors during recording, they are incorporated in your script; you can edit them out if you want to. Here is a sample script generated by Kermit ("learn vms.ksc") in which a Telnet connection is made to a VMS computer, the user logs in, starts Kermit on VMS, sends it a file, and then logs out: ; Scriptfile: vms.ksc ; Directory: /usr/olga ; Recorded: 20001124 15:21:23 SET HOST /NETWORK:TCP vms.xyzcorp.com IF FAIL STOP 1 Connection failed INPUT 7 {\{13}\{10}\{13}Username: } IF FAIL STOP 1 INPUT timeout PAUSE 1 OUTPUT olga\{13} INPUT 3 {\{13}\{10}\{13}Password: } IF FAIL STOP 1 INPUT timeout PAUSE 1 OUTPUT secret\{13} INPUT 18 {\{13}\{10}\{13}$ } IF FAIL STOP 1 INPUT timeout PAUSE 1 OUTPUT set default [.incoming]\{13} INPUT 12 {\{13}\{10}\{13}$ } IF FAIL STOP 1 INPUT timeout PAUSE 1 OUTPUT kermit\{13} INPUT 15 {\{13}\{10}\{13}ALTO:[OLGA.INCOMING] C-Kermit>} IF FAIL STOP 1 INPUT timeout PAUSE 1 OUTPUT receive\{13} send myfile.txt INPUT 18 {\{13}\{10}\{13}ALTO:[OLGA.INCOMING] C-Kermit>} IF FAIL STOP 1 INPUT timeout PAUSE 1 OUTPUT exit\{13} INPUT 6 {\{13}\{10}\{13}$ } IF FAIL STOP 1 INPUT timeout PAUSE 1 OUTPUT logout\{13} close exit The commands generated by Kermit during CONNECT (INPUT, IF FAIL, PAUSE, and OUTPUT) have uppercase keywords; the commands typed by the user are in whatever form the user typed them (in this case, lowercase). [ [471]Top ] [ [472]Contents ] [ [473]C-Kermit Home ] [ [474]Kermit Home ] 8.12. Pattern Matching A pattern is a character string that is used to match other strings. Patterns can contain metacharacters that represent special actions like "match any single character", "match zero or more characters", "match any single character from a list", and so on. The best known application of patterns is in file specifications that contain wildcards, as in "send *.txt", meaning "send all files whose names end with .txt". Patterns are also used in increasingly many other ways, to the extent it is useful to point out certain important distinctions in the ways in which they are used: Anchored Patterns If an anchored pattern does not begin with "*", it must match the beginning of the string, and if it does not end with "*", it must match the end of the string. For example, the anchored pattern "abc" matches only the string "abc", not "abcde" or "xyzabc" or "abcabc". The anchored pattern "abc*" matches any string that starts with "abc"; the anchored pattern "*abc" matches any string that ends with "abc"; the anchored pattern "*abc*" matches any string that contains "abc" (including any that start and/or end with it). Floating Patterns A floating pattern matches any string that contains a substring that matches the pattern. In other words, a floating pattern has an implied "*" at the beginning and end. You can anchor a floating pattern to the beginning by starting it with "^", and you can anchor it to the end by ending it with "$" (see examples below). Wildcards A wildcard is an anchored pattern that has the additional property that "*" does not match directory separators. This terminology lets us describe Kermit's commands with a bit more precision. When a pattern is used for matching filenames, it is a wildcard, except in the TEXT-PATTERNS and BINARY-PATTERNS lists and /EXCEPT: clauses, in which case directory separators are not significant (for example, a BINARY-PATTERN of "*.exe" matches any file whose name ends in .exe, no matter how deeply it might be buried in subdirectories). When Kermit parses a file specification directly, however, it uses the strict wildcard definition. For example, "send a*b" sends all files whose names start with "a" and end with "b" in the current directory, and not any files whose names end with "b" that happen to be in subdirectories whose names start with "a". And as noted, wildcards are anchored, so "delete foo" deletes the file named "foo", and not all files whose names happen to contain "foo". Most other patterns are anchored. For example: if match abc bc ... does not succeed (and you would be surprised if it did!). In fact, the only floating patterns are the ones used by commands or functions that search for patterns in files, arrays, or strings. These include: * The GREP and TYPE /MATCH commands. * The \fsearch(), \frsearch(), and \farraylook() functions. Thus these are the only contexts in which explicit anchors ("^" and "$") may be used: grep abc *.txt Prints all lines containing "abc" in all files whose names end with ".txt". grep ^abc *.txt Prints all lines that start with "abc" in all ".txt" files. grep abc$ *.txt Prints all lines that end with "abc" in all ".txt" files. grep ^a*z$ *.txt Prints all lines that start with "a" and end with "z" in all ".txt" files. Similarly for TYPE /PAGE, /fsearch(), /frsearch(), and \farraylook(). Here is a brief summary of anchored and floating pattern equivalences: Anchored Floating abc ^abc$ *abc abc$ abc* ^abc *abc* abc [ [475]Top ] [ [476]Contents ] [ [477]C-Kermit Home ] [ [478]Kermit Home ] 8.13. Dates and Times C-Kermit's comprehension of date-time formats is considerably expanded in version 8.0. Any command that reads dates, including the DATE command itself, or any switch, such as the /BEFORE: and /AFTER: switches, or any function such as \fcvtdate(), now can understand dates and times expressed in any ISO 8601 format, in Unix "asctime" format, in FTP MDTM format, and in practically any format used in RFC 822 or RFC 2822 electronic mail, with or without timezones, and in a great many other formats as well. HELP DATE briefly summarizes the acceptable date-time formats. Furthermore, C-Kermit 8.0 includes a new and easy-to-use form of date-time arithmetic, in which any date or time can be combined with a "delta time", to add or subtract the desired time interval (years, months, weeks, days, hours, minutes, seconds) to/from the given date. And new functions are available to compare dates and to compute their differences. As you can imagine, all this requires quite a bit of "syntax". The basic format is: [ date ] [ time ] [ delta ] Each field is optional, but in most cases (depending on the context) there must be at least one field. If a date is given, it must come first. If no date is given, the current date is assumed. If no time is given, an appropriate time is supplied depending on whether a date was supplied. If no delta is given, no arithmetic is done. If a delta is given without a date or time, the current date and time are used as the base. Date-time-delta fields are likely to contain spaces (although they need not; space-free forms are always available). Therefore, in most contexts -- and notably as switch arguments -- date-time information must be enclosed in braces or doublequotes, for example: send /after:"8-Aug-2001 12:00 UTC" *.txt Kermit's standard internal format for dates and times is: yyyymmdd hh:mm:ss for example: 20010208 10:28:01 Date-times can always be given in this format. yyyy is the 4-digit year, mm is the two-digit month (1-12; supply leading zero for Jan-Sep), dd is the 2-digit day (leading zero for 1-9), hh is the hour (0-23), mm the minute (0-59), ss the second (0-59), each with leading zero if less than the field width. The date and time can be separated by a space, an underscore, a colon, or the letter T. The time is in 24-hour format. Thus the various quantities are at the following fixed positions: Position Contents 1-4 Year (4 digits, 0000-9999) 5-6 Month (2 digits, 1-12) 7-8 Day (2 digits, 1-31) 9 Date-Time Separator (space, :, _, or the letter T) 10-11 Hour (2 digits, 0-23) 12 Hour-Minute Separator (colon) 13-14 Minute (2 digits, 0-59) 15 Minute-Second Separator (colon) 16-17 Second (2 digits, 0-59) Example: 19800526 13:07:12 26 May 1980, 13:07:12 (1:07:12PM) This is the format produced by the DATE command and by any function that returns a date-time. It is suitable for lexical comparison and sorting, and for use as a date-time in any Kermit command. When this format is given as input to a command or function, various date-time separators (as noted) are accepted: 19800526 13:07:12 26 May 1980, 13:07:12 (1:07:12PM) 20010208_10:28:35 2 February 2001, 10:28:35 AM 18580101:12:00:00 1 January 1858, noon 20110208T00:00:00 2 February 2011, midnight Certain other special date-time formats that are encountered on computer networks are recognized: Asctime Format This is a fixed format used by Unix, named after Unix's asctime() ("ASCII time") function. It is always exactly 24 characters long. Example: Fri Aug 10 16:38:01 2001 Asctime with Timezone This is like Asctime format, but includes a 3-character timezone between the time and year. It is exactly 28 characters long. Example: Fri Aug 10 16:38:01 GMT 2001 E-Mail Format E-mail date-time formats are defined in [479]RFC 2822 with a fair amount of flexibility and options. The following examples are typical of e-mails and HTTP (web-page) headers: Sat, 14 Jul 2001 11:49:29 (No timezone) Fri, 24 Mar 2000 14:19:59 EST (Symbolic timezone) Tue, 26 Jun 2001 10:19:45 -0400 (EDT) (GMT Offset + comment) FTP MDTM Format This is the date-time format supplied by FTP servers that support the (not yet standard but widely used nevertheless) MDTM command, by which the FTP client asks for a file's modification time: yyyymmddhhmmss[.ffff] where yyyy is the 4-digit year, mm is the 2-digit month, and so on, exactly 14 digits long. An optional fractional part (fraction of second) may also be included, separated by a decimal point (period). Kermit rounds to the nearest second. Example: 20020208102835.515 (8 February 2002 10:28:36 AM) 8.13.1. The Date The date, if given, must precede the time and/or delta, and can be in many, many formats. For starters, you can use several symbolic date names in place of actual dates: NOW This is replaced by the current date and time. The time can not be overriden (if you want to supply a specific time, use TODAY rather than NOW). TODAY This is replaced by the current date and a default time of 00:00:00 is supplied, but can be overridden by a specific time; for example, if today is 8 February 2002, then "TODAY" is "20020802 00:00:00" but "TODAY 10:28" is "20020802 10:28:00". TOMORROW Like TODAY, but one day later (if today is 8 February 2002, then "TOMORROW" is "20020803 00:00:00" but "TOMORROW 16:30" is "20020803 16:30:00"). YESTERDAY Like TODAY, but one day earlier. MONDAY, TUESDAY, WEDNESDAY, ..., SUNDAY The date on the given day of the week, today or later. A default time of 00:00:00 is supplied but can be overridden. Example: "SATURDAY 12:00" means next Saturday (or today, if today is Saturday) at noon. You can give an explicit date in almost any conceivable format, but there are some rules: * If a date is given, it must have three fields: day, month, and year; the order can vary (except that the month can not be last). * If names are used for days, months, etc, they must be English. * The year must lie between 0000 and 9999, inclusive. * All calendar calculations use Gregorian dating, so calculated dates for years prior to 1582 (or later, depending on the country) will not agree with historical dates. Other forms of dating (e.g. Hebrew, Chinese) are not supported. Various date-field separators are accepted: hyphen, slash, space, underscore, period. The same field separator (if any) must be used in both places; for example 18-Sep-2001 but not 18-Sep/2001. Months can be numeric (1-12) or English names or abbreviations. Month name abbreviations are normally three letters, e.g. Apr, May, Jun, Jul. Capitalization doesn't matter. Here are a few examples: 18 Sep 2001 (English month, abbreviated) 18 September 2001 (English month, spelled out) 2001 Sept 18 (Year, month, day) 18-Sep-2001 (With hyphens) 18/09/2001 (All numeric with slashes) 18.09.2001 (Ditto, with periods) 18_09_2001 (Ditto, with underscores) 09/18/2001 (See below) 2001/09/18 (See below) September 18, 2001 (Correspondence style) Sep-18-2001 (Month-day-year) 20010918 (Numeric, no separators) You can also include the day of the week with a specific date, in which case it is accepted (if it is a valid day name), but not verified to agree with the given date: Tue, 18 Sep 2001 (Abbreviated, with comma) Tue,18 Sep 2001 (Comma but no space) Tue 18 Sep 2001 (Abbreviated, no comma) Tuesday 18 Sep 2001 (Spelled out) Tuesday, 18 Sep 2001 (etc) Friday, 18 Sep 2001 (Accepted even if not Friday) In all-numeric dates with the year last, such as 18/09/2001, Kermit identifies the year because it's 4 digits, then decides which of the other two numbers is the month or day based on its value. If both are 12 or less and are unequal, the date is ambiguous and is rejected. In all-numeric dates with the year first, the second field is always the month and the third is the day. The month never comes last. A date with no separators is accepted only if it is all numeric and has exactly eight digits, and is assumed to be in yyyymmdd format. 20010918 (18-Sep-2001 00:00:00) or 14 digits (as in FTP MDTM format): 20010918123456 (18-Sep-2001 12:34:56) You can always avoid ambiguity by putting the year first, or by using an English, rather than numeric, month. A date such as 09/08/2001 would be ambiguous but 2001/09/08 is not, nor is 09-Aug-2001. Until the late 1990s, it was common to encounter 2-digit years, and these are found to this day in old e-mails and other documents. Kermit accepts these dates if they have English months, and interprets them according to the windowing rules of [480]RFC 2822: "If a two digit year is encountered whose value is between 00 and 49, the year is interpreted by adding 2000, ending up with a value between 2000 and 2049. If a two digit year is encountered with a value between 50 and 99, or any three digit year is encountered, the year is interpreted by adding 1900." If you need to specify a year prior to 1000, use leading zeros to ensure it is not misinterpreted as a "non-Y2K-compliant" modern year: 7-Oct-77 (19771007 00:00:00) 7-Oct-0077 (00771007 00:00:00) 8.13.2. The Time The basic time format is hh:mm:dd; that is hours, minutes, seconds, separated by colons, perhaps with an optional fractional second separated by a decimal point (period). The hours are in 24-hour format; 12 is noon, 13 is 1pm, and so on. Fields omitted from the right default to zero. Fields can be omitted from the left or middle by including the field's terminating colon. Examples: 11:59:59 (11:59:59 AM) 11:59 (11:59:00 AM) 11 (11:00:00 AM) 11:59:59.33 (11:59:59 AM) 11:59:59.66 (Noon) 03:21:00 (3:21:00 AM) 3:21:00 (3:21:00 AM) 15:21:00 (3:21:00 PM) :21:00 (00:21:00 AM) ::01 (00:00:01 AM) 11::59 (11:00:59 AM) Leading zeros can be omitted, but it is customary and more readable to keep them in the minute and second fields: 03:02:01 (03:02:01 AM) 3:02:01 (03:02:01 AM) 3:2:1 (03:02:01 AM) AM/PM notation is accepted if you wish to use it: 11:59:59 (11:59:59 AM) 11:59:59AM (11:59:59 AM) 11:59:59A.M. (11:59:59 AM) 11:59:59am (11:59:59 AM) 11:59:59a.m. (11:59:59 AM) 11:59:59PM (11:59:59 PM = 23:59:59) 11:59:59P.M. (11:59:59 PM = 23:59:59) 11:59:59pm (11:59:59 PM = 23:59:59) 11:59:59p.m. (11:59:59 PM = 23:59:59) You can omit the colons if you wish, in which case Kermit uses the following rules to interpret the time: 1. 6 digits is hh:mm:ss, e.g. 123456 is 12:34:56. 2. 5 digits is h:mm:ss, e.g. 12345 is 1:23:45. 3. 4 digits is hh:mm, e.g. 1234 is 12:34. 4. 3 digits is h:mm, e.g. 123 is 1:23. 5. 2 digits is hh, e.g. 12 is 12:00. 6. 1 digit is h (the hour), e.g. 1 is 1:00. Examples: 1 (01:00:00 AM) 10 (10:00:00 AM) 230 (02:30:00 AM) 230pm (02:30:00 PM = 14:30:00) 1115 (11:15:00 AM) 2315 (11:15:00 PM = 23:15:00 PM) 23150 (02:31:50 AM) 231500 (23:15:00 PM) 8.13.3. Time Zones If a time is given, it can (but need not) be followed by a time zone designator. If no time zone is included, the time is treated as local time and no timezone conversions are performed. The preferred time zone designator is the UTC Offset, as specified in [481]RFC 2822: a plus sign or minus sign immediately followed by exactly four decimal digits, signifying the difference in hh (hours) and mm (minutes) from Universal Coordinated Time (UTC, also known as Greenwich Mean Time, or GMT), with negative numbers to the West and positive numbers to the East. For example: Fri, 13 Jul 2001 12:54:29 -0700 indicates a local time of 12:54:29 that is 07 hours and 00 minutes behind (less than, East of) Universal Time. The space is optional, so the example could also be written as: Fri, 13 Jul 2001 12:54:29-0700 The following symbolic time zones are also accepted, as specified by [482]RFC 2822 and/or in ISO 8601: GMT = +0000 Greenwich Mean Time Z = +0000 Zulu (Zero Meridian) Time UTC = +0000 Universal Coordinated Time UT = +0000 Universal Time EDT = -0400 Eastern (USA) Daylight Time EST = -0500 Eastern (USA) Standard Time CDT = -0500 Central (USA) Daylight Time CST = -0600 Central (USA) Standard Time MDT = -0600 Mountain (USA) Daylight Time MST = -0700 Mountain (USA) Standard Time PDT = -0700 Pacific (USA) Daylight Time PST = -0800 Pacific (USA) Standard Time Note that GMT, Z, UTC, and UT all express the same concept: standard (not daylight) time at the Zero Meridian. UTC, by the way, is an international standard symbol and does not correspond to the order of the English words, Universal Coordinated Time, but it happens to have the same initial letters as these words. Of course hundreds of other symbolic timezones and variations exist, but they are not standardized, and are therefore not supported by Kermit. When a time zone is included with a time, the time is converted to local time. In case the conversion crosses a midnight boundary, the date is adjusted accordingly. Examples converting to EST (Eastern USA Standard Time = -0500): 11:30:00 = 11:30:00 11:30:00 EST = 11:30:00 11:30:00 GMT = 06:30:00 11:30:00 PST = 14:30:00 11:30:00Z = 06:30:00 11:30PM GMT = 18:30:00 11:30 -0500 = 11:30:00 11:30 -0800 = 08:30:00 11:30 +0200 = 04:30:00 Unlike most of Kermit's other date-time conversions, timezone knowledge (specifically, the offset of local time from UTC) is embodied in the underlying operating system, not in Kermit itself, and any conversion errors in this department are the fault of the OS. For example, most UNIX platforms do not perform conversions for years prior to 1970. 8.13.4. Delta Time Date/time expressions can be composed of a date and/or time and a delta time, or a delta time by itself. When a delta time is given by itself, it is relative to the current local date and time. Delta times have the following general format: {+,-}[number units][hh[:mm[:ss]]] In other words, a delta time always starts with a plus or minus sign, which is followed by a "part1", a "part2", or both. The "part1", if given, specifies a number of days, weeks, months, or years; "part2" specifies a time in hh:mm:ss notation. In arithmetic terms, these represents some number of days or other big time units, and then a fraction of a day expressed as hours, minutes, and seconds; these are to be added to or subtracted from the given (or implied) date and time. The syntax is somewhat flexible, as shown by the following examples: +1 day (Plus one day) +1day (Ditto) +1d (Ditto) + 1 day (Ditto) + 1 day 3:00 (Plus one day and 3 hours) +1d3:00 (Ditto) +1d3 (Ditto) +3:00:00 (Plus 3 hours) +3:00 (Ditto) +3 (Ditto) +2 days (Plus 2 days) -12 days 7:14:22 (Minus 12 days, 7 hours, 14 minutes, and 22 seconds) The words "week", "month", and "year" can be used like "day" in the examples above. A week is exactly equivalent to 7 days. When months are specified, the numeric month number of the date is incremented or decremented by the given number, and the year and day adjusted accordingly if necessary (for example, 31-Jan-2001 +1month = 03-Mar-2001 because February does not have 31 days). When years are specified, they are added or subtracted to the base year. Examples (assuming the current date is 10-Aug-2001 and the current time is 19:21:11): 18-Sep-2001 +1day (20010918 00:00:00) today +1day (20010811 00:00:00) now+1d (20010811 19:21:11) + 1 day (20010811 19:21:11) + 1 day 3:14:42 (20010811 22:35:54) + 7 weeks (20010928 19:21:11) +1d3:14:42 (20010811 22:35:54) +1w3:14:42 (20010817 22:35:54) +1m3:14:42 (20010910 22:35:54) +1y3:14:42 (20020810 22:35:54) 2 feb 2001 + 10 years (20110208 00:00:00) 2001-02-08 +10y12 (20110208 12:00:00) 31-dec-1999 23:59:59+00:00:01 (20000101 00:00:00) 28-feb-1996 +1day (19960229 00:00:00) (leap year) 28-feb-1997 +1day (19970301 00:00:00) (nonleap year) 28-feb-1997 +1month (19970328 00:00:00) 28-feb-1997 +1month 11:59:59 (19970328 11:59:59) 28-feb-1997 +20years (20170228 00:00:00) 28-feb-1997 +8000years (99970228 00:00:00) For compatibility with VMS, the following special delta-time format is also accepted: +number-hh:mm:ss -number-hh:mm:ss (no spaces). The hyphen after the number indicates days. It corresponds exactly to the Kermit notation: +numberdhh:mm:ss -numberdhh:mm:ss The following forms all indicate exactly the same date and time: 18-Sep-2001 12:34:56 +1-3:23:01 18-Sep-2001 12:34:56 +1d3:23:01 18-Sep-2001 12:34:56 +1 day 3:23:01 and mean "add a day plus 3 hours, 23 minutes, and 1 second" to the given date. Note that delta times are not at all the same as UTC offsets; the former specifies an adjustment to the given date/time and the latter specifies that the local time is a particular distance from Universal Time, for example: 11-Aug-2001 12:34:56 -0800 (20010811 16:34:56 -- UTC Offset) 11-Aug-2001 12:34:56 -08:00 (20010811 04:34:56 -- Delta time) If you give a time followed by a modifier that starts with a + or - sign, how does Kermit know whether it's a UTC offset or a delta time? It is treated as a UTC offset if the sign is followed by exactly four decimal digits; otherwise it is a delta time. Examples (for USA Eastern Daylight Time): 11-Aug-2001 12:34:56 -0800 (20010811 16:34:56 -- UTC Offset) 11-Aug-2001 12:34:56 -08:00 (20010811 04:34:56 -- Delta time) 11-Aug-2001 12:34:56 -800 (20010811 04:34:56 -- Delta time) 11-Aug-2001 12:34:56 -8 (20010811 04:34:56 -- Delta time) The first example says that at some unknown place which is 8 hours ahead of Universal Time, the time is 12:34:56, and this corresponds to 16:34:56 in Eastern Daylight time. The second example says to subtract 8 hours from the local time. The third and fourth are delta times because, even though a colon is not included, the time does not consist of exactly 4 digits. When a delta time is written after a timezone, however, there is no ambiguity and no syntax distinction is required: 11-Aug-2001 12:34:56 -0800 -0800 (20010811 08:34:56) 11-Aug-2001 12:34:56 -0800 -08:00 (Ditto) 11-Aug-2001 12:34:56 -08:00 -08:00 (Illegal) 8.13.5. The DATE Command Obviously a great many combinations of date, time, time zone, and delta time are possible, as well as many formatting options. The purpose of all this flexibility is to comply with as many standards as possible -- Internet RFCs, ISO standards, and proven corporate standards -- as well as with notations commonly used by real people, in order that dates and times from the widest variety of sources can be assigned to a variable and used in any date-time field in any Kermit command. You can test any date-and/or-time format with the DATE command, which converts it to standard yyyymmdd hh:mm:ss format if it is understood, or else gives an explicit error message (rather than just "BAD DATE" as in previous C-Kermit releases) to indicate what is wrong with it. Examples (on Tuesday, 31 July 2001 in New York City, Eastern Daylight Time, UTC -0400): DATE command argument Result 12:30 20010731 12:30:00 12:30:01 20010731 12:30:01 12:30:01.5 20010731 12:30:02 1230 20010731 12:30:00 230 20010731 02:30:00 230+1d 20010801 02:30:00 230+1d3:00 20010801 05:30:00 20010718 19:21:15 20010718 19:21:15 20010718_192115 20010718 19:21:15 20010718T192115 20010718 19:21:15 18 Jul 2001 +0400 20010717 23:59:59 18 Jul 2001 192115 20010718 19:21:15 18 Jul 2001 192115.8 20010718 19:21:16 18-Jul-2001T1921 20010718 19:21:00 18-Jul-2001 1921Z 20010718 15:21:00 18-Jul-2001 1921 GMT 20010718 15:21:00 18-Jul-2001 1921 UTC 20010718 15:21:00 18-Jul-2001 1921 Z 20010718 15:21:00 18-Jul-2001 1921Z 20010718 15:21:00 18-Jul-2001 1921 -04:00:00 20010718 19:21:00 21-Jul-2001_08:20:00am 20010721 08:20:00 21-Jul-2001_8:20:00P.M. 20010721 20:20:00 Fri Jul 20 11:26:25 2001 20010720 11:26:25 Fri Jul 20 11:26:25 GMT 2001 20010720 07:26:25 Sun, 9 Apr 2000 06:46:46 +0100 20000409 01:46:46 Sunday, 9 Apr 2000 06:46:46 +0100 20000409 01:46:46 now 20010731 19:41:12 today 20010731 00:00:00 today 09:00 20010731 09:00:00 tomorrow 20010801 00:00:00 tomorrow 09:00 20010801 09:00:00 tomorrow 09:00 GMT 20010801 05:00:00 yesterday 20010730 00:00:00 yesterday 09:00 20010730 09:00:00 + 3 days 20010803 00:00:00 +3 days 20010803 00:00:00 +3days 20010803 00:00:00 + 3days 20010803 00:00:00 + 3 days 09:00 20010803 09:00:00 + 2 weeks 20010814 00:00:00 + 1 month 20010831 00:00:00 - 7 months 20001231 00:00:00 + 10 years 20110731 00:00:00 friday 20010803 00:00:00 saturday 20010804 00:00:00 sunday 20010805 00:00:00 monday 20010806 00:00:00 tuesday 20010731 00:00:00 wednesday 20010801 00:00:00 thursday 20010802 00:00:00 friday 07:00 20010803 07:00:00 thursday 1:00pm 20010802 13:00:00 thursday 1:00pm GMT 20010802 09:00:00 Thu, 10 Nov 94 10:50:47 EST 19941110 10:50:47 Fri, 20 Oct 1995 18:35:15 -0400 (EDT) 19951020 18:35:15 31/12/2001 20011231 00:00:00 12/31/2001 20011231 00:00:00 2001-July-20 20010720 00:00:00 2001-September-30 20010930 00:00:00 30-September-2001 20010930 00:00:00 Sep 30, 2001 12:34:56 20010930 12:34:56 September 30, 2001 20010930 00:00:00 September 30, 2001 630 20010930 06:30:00 September 30 2001 630 20010930 06:30:00 Sep-30-2001 12:34:59 20010930 12:34:59 20010807113542.014 20010807 11:35.42 20010807113542.014Z 20010807 07:35:42 8.13.6. New Date-Time Functions In the following descriptions, date-time function arguments are the same free-format date-time strings discussed above, with the same defaults for missing fields. They are automatically converted to standard format internally prior to processing. \fcvtdate(d1) Converts the date-time d1 to standard format and local time. This function is not new, but now it accepts a wider range of argument formats that can include timezones and/or delta times. If the first argument is omitted, the current date and time are assumed. The optional second argument is a format code for the result: n1 = 1: yyyy-mmm-dd hh:mm:ss (mmm = English 3-letter month abbreviation) n1 = 2: dd-mmm-yyyy hh:mm:ss (ditto) n1 = 3: yyyymmddhhmmss (all numeric) \futcdate(d1) Converts the date-time d1 to Universal Coordinated Time (UTC), also known as GMT or Zulu or Zero-Meridian time. The default d1 is NOW. If d1 is a valid date-time, the UTC result is returned in standard format, yyyymmdd hh:ss:mm. \fcmpdates(d1,d2) Compares two free-format date-times, d1 and d2, and, if both arguments are valid, returns a number: -1 if d1 is earlier than (before) d2; 0 if d1 is the same as d2; 1 if d1 is later than (after) d2. \fdiffdates(d1,d2) Computes the difference between two free-format date-times, d1 and d2. If both arguments are valid, returns a delta time which is negative if d1 is earlier than (before) d2 and positive otherwise. If d1 and d2 are equal, the result is "+0:00". Otherwise, the result consists of the number of days, hours, minutes, and seconds that separate the two date-times. If the number of days is zero, it is omitted. If the number of days is nonzero but the hours, minutes, and seconds are all zero, the time is omitted. if the seconds are zero, they are omitted. \fdelta2secs(dt) Converts a delta time to seconds. For example, "+1d00:00:01" to 86401. Valid delta times must start with a + or - sign. Days are accepted as time units, but not years, months, or weeks. If the result would overflow a computer long word (as would happen with 32-bit long words when the number of days is greater than 24854), the function fails. HINT: Although Kermit has a number of built-in date and time variables, it doesn't have a single one suitable for writing a timestamp. For this you would normally use something like "\v(ndate) \v(time)". But \fcvtdate() (with no arguments) is equivalent: it returns the current date and time in yyyymmdd hh:mm:ss format, suitable for time stamping. 8.13.7. Date-Time Programming Examples Here's a macro that converts any date-time to UTC, which you might use if C-Kermit didn't already have a \futcdate() function: define utcdate { .local := \fcvtdate(\%*) ; 1. .tmp := \fcvtdate(\m(local)UTC) ; 2. .offset := \fdiffdate(\m(local),\m(tmp)) ; 3. .utc := \fcvtdate(\m(local)\m(offset)) ; 4. sho mac utc ; 5. } Brief explanation: Line 1 converts the macro argument, a free-format date-time, to standard-format local time. Line 2 appends the "UTC" timezone to the local time and converts the result to local time. In other words, we take the same time as the local time, but pretend it's UTC time, and convert it to local time. For example, if New York time is 4 hours ahead of UTC, then 6:00pm New York time is 2:00pm UTC. Line 3 gets the difference of the two results (e.g. "+04:00"). Line 4 appends the difference (delta time) to the local time, and converts it again, which adds (or subtracts) the UTC offset to the given time. Line 5 displays the result. Here's a script that opens a web page, gets its headers into an array, scans the array for the "Last-Modified:" header, and interprets it: http open www.columbia.edu if fail stop 1 HTTP OPEN failed http /array:a head index.html /dev/null if fail stop 1 HTTP GET failed show array a for \%i 1 \fdim(&a) 1 { .\%x := \findex(:,\&a[\%i]) if not \%x continue .tag := \fleft(\&a[\%i],\%x-1) .val := \fltrim(\fsubstr(\&a[\%i],\%x+1)) if ( eq "\m(tag)" "Last-Modified" ) { echo HTTP Date: \m(val) .rdate := \fcvtdate(\m(val)) echo {Standard Date (local): \m(rdate)} echo {Standard Date (UTC): \futcdate(\m(rdate))} break } } http close The result: HTTP Date: Mon, 13 Aug 2001 20:05:42 GMT Standard Date (local): 20010813 16:05:42 Standard Date (UTC): 20010813 20:05:42 As you can see, Kermit had no trouble decoding the date-time-string from the website, converting to local time, and converting back to UTC with no conflicts or loss of information. If it had been in any other known format, the result would have been the same. Now suppose we want to download the web page only if it is newer than our local copy. The \fdate(filename) function (which returns the modification date-time of the given file) and the new \fcmpdates() function make it easy. Insert the following just before the BREAK statement: if ( < 0 \fcmpdates(\m(rdate),\fdate(index.html)) ) { echo GETTING index.html... http get index.html index.html if success echo HTTP GET OK } else { echo index.html: no update needed } http close exit This says, "if 0 is less than the comparison of the remote file date and the local file date, get the remote file, otherwise skip it." And it automatically reconciles the time-zone difference (if any). It would be nice to be able to extend this script into a general-purpose website updater, but unfortunately HTTP protocol doesn't provide any mechanism for the client to ask the server for a list of files, recursive or otherwise. [ [483]Top ] [ [484]Contents ] [ [485]C-Kermit Home ] [ [486]Kermit Home ] 8.14. Trapping Keyboard Interruption Normally when you type Ctrl-C and Kermit is in command mode (as opposed to CONNECT mode) with COMMAND INTERRUPTION ON (as it is unless you have set it OFF), Kermit interrupts any command that is currently in progress, and if a command file or macro is executing, rolls the command stack back to top level, closing all open command files, deactivating all macros, deallocating all local variables and arrays, and leaving you at the command prompt. Suppose, however, you want certain actions to occur when a script is interrupted; for example, closing open files, writing log entries, or displaying summary results. You can do this by defining a macro named ON_CTRLC. When Ctrl-C is detected, and a macro with this name is defined, Kermit executes it from the current command level, thus giving it full access to the environment in which the interruption occurred, including local variables and open files. Only when the ON_CTRLC macro completes execution is the command stack rolled back to top level. Once the ON_CTRLC macro is defined, it can be executed only once. This is to prevent recursion if the user types Ctrl-C while the ON_CTRLC macro is executing. If you type Ctrl-C while the Ctrl-C macro is active, this does not start a new copy of ON_CTRLC; rather, it returns to the top-level command prompt. After the ON_CTRLC macro returns, it has been removed from the macro table so if you want to use it again or install a different Ctrl-C trap, you must execute a new DEFINE ON_CTRLC command. In any case, as always when you interrupt a script with Ctrl-C, its completion status is FAILURE. Normally the ON_CTRLC macro would be defined in the command file or macro to which it applies, and should be declared LOCAL. This way, if the command file or macro completes successfully without being interrupted, the ON_CTRLC definition disappears automatically. Otherwise the definition would still be valid and the macro would be executed, probably out of context, the next time you typed Ctrl-C. Here's a simple example of a command file that sets a Ctrl-C trap for itself: local on_ctrlc ; Make Ctrl-C trap local to this command file. define on_ctrlc { ; Define the ON_CTRLC macro. echo Interrupted at \v(time). echo Iterations: \%n } xecho Type Ctrl-C to quit for \%n 1 999 1 { ; Prints a dot every second until interrupted. sleep 1 xecho . } echo Finished normally at \v(time) ; Get here only if not interrupted. decrement \%n echo Iterations: \%n This prints a summary no matter whether it completes normally or is interrupted from the keyboard. In both cases the trap is automatically removed afterwards. For an example of how to use ON_CTRLC to debug scripts, see [487]Section 8.1. [ [488]Top ] [ [489]Contents ] [ [490]C-Kermit Home ] [ [491]Kermit Home ] 9. S-EXPRESSIONS This section is primarily for those who want to write calculation-intensive scripts, especially if they require floating-point arithmetic, and/or for those who are familiar with the LISP programming language. Ever since C-Kermit version 5 was released in 1988, scripting has been one of its major attractions, and arithmetic is a key part of it. Versions 5 and 6 included integer arithmetic only, using traditional algebraic notation, e.g.: echo \fevaluate(3*(2+7)/2) 13 C-Kermit 7.0 added support for floating-point arithmetic, but only through function calls: echo \ffpdivide(\ffpmultiply(3.0,\ffpadd(2.0,7.0)),2.0) 13.5 C-Kermit 8.0 introduces a third form of arithmetic that treats integers and floating-point numbers uniformly, is easier to read and write, and executes very quickly: (/ (* 3 (+ 2 7)) 2) 13.5 But first some background. The Kermit command and scripting language differs from true programming languages (such as C or Fortran) in many ways; one of the most prominent differences is the way in which variables are distinguished from constants. In a command language, words are taken literally; for example, the Unix shell: cat foo.bar displays the file named foo.bar. Whereas in a programming language like C, words are assumed to be variables: s = foo.bar; /* Assigns the value of foo.bar to the variable s */ To make a programming language take words literally, you have to quote or "escape" them: s = "foo.bar"; /* Assigns a pointer to the string "foo.bar" to the variable s */ The opposite holds for command languages: to get them to treat a word as a variable rather than a constant, you have to escape them. For example, in the Unix shell: foo=123 ; Assign value 123 to variable foo. echo foo ; Prints "foo" echo $foo ; Prints "123" And in Kermit: define foo 123 ; Assign value 123 to variable foo. echo 123 ; This prints "123". echo foo ; This prints "foo". echo \m(foo) ; This prints "123". In other words, character strings (such as "foo" above) are interpreted as literal strings, rather than variable names, except in special commands like DEFINE that deal specifically with variable names (or in numeric contexts as explained in [492]Section 8.2). The special "escape" character (dollar sign ($) for the shell, backslash (\) for Kermit) indicates that a variable is to be replaced by its value. The requirement to escape variable names in command languages normally does not impose any special hardship, but can add a considerable notational burden to arithmetic expressions, which are typically full of variables. Especially in Kermit when floating point numbers are involved, where you must use special \ffpxxx() functions, e.g. "\ffpadd(\m(a),\m(b))" rather than the simple "+" operator to add two floating-point numbers together, because the original arithmetic handler doesn't support floating point (this might change in the future). To illustrate, the general formula for the area of a triangle is: sqrt(s * (s - a) * (s - b) * (s - c)) where a, b, and c are the lengths of the triangle's three sides and: s = (a + b + c) / 2 Except in special cases (e.g. a = 3, b = 4, c = 5), the result has a fractional part so the computation must be done using floating-point arithmetic. We can create a Kermit 7.0 function for this as follows: def area { local s t1 t2 t3 assign s \ffpdiv(\ffpadd(\ffpadd(\%1,\%2),\%3),2.0) assign t1 \ffpsub(\m(s),\%1) assign t2 \ffpsub(\m(s),\%2) assign t3 \ffpsub(\m(s),\%3) return \ffpsqrt(\ffpmul(\m(s),\ffpmul(\m(t1),\ffpmul(\m(t2),\m(t3))))) } But as you can see, this is rather cumbersome. Note, in particular, that arithmetic functions like \ffpadd(), \ffpmul(), etc, take exactly two operands (like their symbolic counterparts + and *), so obtaining the product of three or more numbers (as we do in this case) is awkward. Using the alternative S-Expression notation, we can reduce this to a form that is both easier to read and executes faster (the details are explained later): def newarea { (let s (/ (+ \%1 \%2 \%3) 2.0)) (sqrt (* s (- s \%1) (- s \%2) (- s \%3))) } In both examples, the \%1..3 variables are the normal Kermit macro arguments, referenced by the normal escaping mechanism. For increased readability, we can also assign the macro arguments \%1, \%2, and \%3 to the letters a, b, and c corresponding to our formula: def newarea { (let a \%1 b \%2 c \%3) (let s (/ (+ a b c) 2.0)) (sqrt (* s (- s a) (- s b) (- s c))) } And now the Kermit function reads almost like the original formula. Here Kermit behaves more like a regular programming language. In an S-Expression, macro names need not be escaped when they are used as the names of numeric variables. [ [493]Top ] [ [494]Contents ] [ [495]C-Kermit Home ] [ [496]Kermit Home ] 9.1. What is an S-Expression? The S-Expression concept is borrowed from the Lisp programming language. "S-Expression" is short for Symbolic Expression (itself sometimes shortened to SEXP). S-Expressions provide a kind of Alternative Mini-Universe within the Kermit command language when the regular rules don't apply, a universe enclosed in parentheses. C-Kermit does not pretend to be a full Lisp interpreter; only the arithmetic parts of Lisp have been incorporated: S-Expressions that operate on numbers and return numeric values (plus extensibility features described in [497]Section 9.8, which allow some degree of string processing). An S-Expression is a list of zero or more items, separated by spaces, within parentheses. Examples: () (1) (a) (+ a 1) (* 2 a b) If the S-Expression is empty, it has the NIL (empty) value. If it is not empty and the first item is an operator (such as + or *), there can be zero or more subsequent items, called the operands: (+ 1 2) Here the operator is "+" and the operands are "1" and "2", and the value of the S-Expression is the value of the operation (in this case 3). The operator always comes first, which is different from the familiar algebraic notation; this because S-Expression operators can have different numbers of operands: (+ 1) (+ 1 2) (+ 1 2 3 4 5 6 7 8 9) If the first item in the S-Expression is not an operator, then it must be a variable or a number (or a macro; see [498]Section 9.8), and the S-Expression can only contain one item; in this case, the S-Expression's value is the value of the variable or number: (a) (3) Operands can be numbers, variables that have numeric values, functions that return numbers, or other S-Expressions. To illustrate an S-Expression within an S-Expression, observe that: (+ 1 2) is equivalent to any of the following (plus an infinite number of others): (+ 1 (+ 1 1)) (+ (- 3 2) (/ 14 (+ 3 4))) S-Expressions can be nested to any reasonable level; for example, the value of the following S-Expression is 64: (- (* (+ 2 (* 3 4)) (- 9 (* 2 2))) 6) Operators have no precedence, implied or otherwise, since they can't be mixed. The only exceptions are unary + and -, which simply indicate the sign of a number: (* 3 -1) Order of evaluation is specified entirely by parentheses, which are required around each operator and its operands: (+ a (* b c)) instead of (a + b * c). S-Expressions provide a simple and isolated environment in which Kermit's macro names can be used without the \m(...) escaping that is normally required. Given: define a 1 define b 2 define c 3 Then: (+ \m(a) \m(b) \m(c)) is equivalent to: (+ a b c) Within an S-Expression, as in other strictly numeric contexts ([499]Section 8.2), any operand that starts with a letter is treated as a Kermit macro name. In this context, abbreviations are not accepted; variable names must be spelled out in full. Alphabetic case is not significant; "a" and "A" are the same variable, but both are different from "area". Of course, regular Kermit variables and functions can be used in S-Expressions in the normal ways: (* \v(math_pi) (^ \%r 2)) ; Area of a circle with radius \%r (+ \fjoin(&a)) ; Sum of all elements of array \&a[] [ [500]Top ] [ [501]Contents ] [ [502]C-Kermit Home ] [ [503]Kermit Home ] 9.2. Integer and Floating-Point-Arithmetic Normally, if all numbers in an S-Expression are integers, the result is an integer: (+ 1 1) ; Result is 2 (/ 9 3) ; Result is 3 If any of the operands is floating point, however, the result is also floating point: (+ 1 1.0) ; Result is 2.0 (/ 9.0 3) ; Result is 3.0 If all the operands are integers but the result has a fractional part, the result is floating point: (/ 10 3) ; Result is 3.333333333333333 To force an integer result in such cases, use the TRUNCATE operator: (truncate (/ 10 3)) ; Result is 3 Similarly, to force a computation to occur in floating point, you can coerce one of its operands to FLOAT: (+ 1 (float 1)) ; Result is 2.0 The result is also floating point if the magnitude of any integer operand, intermediate result, or the result itself, is larger than the maximum for the underlying machine architecture: (^ 100 100) If the result is too large even for floating-point representation, "Infinity" is printed; if it is too small to be distinguished from 0, 0.0 is returned. Large numbers can be used and large results generated, but they are accurate only to the precision of the underlying machine. For example, the result of: (+ 111111111111111111111 222222222222222222222) should be 333333333333333333333, but 333333333333333300000.0 is produced instead if the machine is accurate to only about 16 decimal digits, even with coercion to floating-point. The order of magnitude is correct but the least significant digits are wrong. The imprecise nature of the result is indicated by the ".0" at the end. Contrast with: (+ 111111111 222222222) which produces an exact integer result. [ [504]Top ] [ [505]Contents ] [ [506]C-Kermit Home ] [ [507]Kermit Home ] 9.3. How to Use S-Expressions S-Expressions may be given as commands to C-Kermit. Any command whose first character is "(" (left parenthesis) is interpreted as an S-Expression. If you enter an S-Expression at the C-Kermit> prompt, its result is printed: C-Kermit>(/ 10.0 3) 3.333333333333333 C-Kermit> If an S-Expression is executed within a macro or command file, its value is not printed. However, you can control the printing action with: SET SEXPRESSION ECHO { AUTO, ON, OFF } AUTO is the default, meaning print the value at top level only; ON means always print the value; OFF means never print it. In any case, the value of the most recent S-Expression (and the S-Expression itself) may be accessed programmatically through the following variables: \v(sexpression) The S-Expression most recently executed. \v(svalue) The value of the S-Expression most recently executed. Besides issuing S-Expressions as commands in themselves, you can also execute them anywhere within a Kermit command, but in this case they must be enclosed in a function call (otherwise they are taken literally): \fsexpression(s) The argument "s" is an S-Expression; the outer parentheses may be omitted. The value of the S-Expression is returned. Note that since S-Expressions usually contain spaces, some form of grouping or quoting might be needed in some contexts: echo \fsexpression((+ 1 1)) ; Outer parentheses may be included echo \fsexpr(+ 1 1) ; Outer parentheses may be omitted echo Value = "\fsexp(+ 1 a)" ; Can be embedded in strings echo Value = \&a[\fsexp(/ b 2)] ; Can be used in array subscripts if = {\fsexp(+ 1 1)} 2 { ; Braces needed here for grouping echo One plus one still equals two } The IF statement illustrates how to use S-Expressions as (or in) IF or WHILE conditions: * Although S-Expressions and IF conditions are similar in appearance, they are not interchangeable. Therefore you must use \fsexpr() to let Kermit know it's an S-Expression rather than a regular IF condition, or a boolean or algebraic expression within an IF condition. * In contexts where a single "word" is expected, you must enclose the \fsexp() invocation in braces if the S-Expression contains spaces (and most of them do). If an S-Expression is the last command executed in a macro, its value becomes the return value of the macro; no RETURN command is needed. Example: def newarea { (let s (/ (+ \%1 \%2 \%3) 2.0)) (sqrt (* s (- s \%1) (- s \%2) (- s \%3))) } This is equivalent to (but more efficient than): def newarea { (let s (/ (+ \%1 \%2 \%3) 2.0)) return \fsexp(sqrt (* s (- s \%1) (- s \%2) (- s \%3))) } When an S-Expression is entered as a command -- that is, the first nonblank character of the command is a left parenthesis -- then it is allowed to span multiple lines, as many as you like, until the first left parenthesis is matched: (let s (/ (+ \%1 \%2 \%3 ) 2.0 ) ) (sqrt (* s (- s \%1) (- s \%2) (- s \%3) ) ) The S-Expression concept lends itself easily to embedding and recursion, but the depth to which recursion can occur is limited by the resources of the computer (memory size, address space, swap space on disk) and other factors. There is no way that C-Kermit can know what this limit is, since it varies not only from computer to computer, but also from moment to moment. If resources are exhausted by recursion, C-Kermit simply crashes; there's no way to trap this error. However, you can set a depth limit on S-Expressions: SET SEXPRESSION DEPTH-LIMIT number Limits the number of times the S-Expression reader can invoke itself without returning to the given number. The default limit is 1000. This limit applies to S-Expressions embedded within other S-Expressions as well as to S-Expressions that invoke recursive macros. If the limit is exceeded, Kermit prints "?S-Expression depth limit exceeded" and returns to its prompt. More about recursion in [508]Section 9.8. You can also test the depth programmatically: \v(sdepth) The current S-Expression invocation depth. The depth includes both nesting level and recursion. For example, in: (foo (foo (foo (foo (foo))))), the innermost (foo) is at depth 5. Help, completion, and syntax checking are not available within an S-Expression. If you type ? within an S-Expression, it says: C-Kermit>(? S-Expression ("help sexp" for details) As it says, typing "help sexp" will display a brief help text. The SHOW SEXPRESSION command displays current SET SEXPRESSION settings and related information. [ [509]Top ] [ [510]Contents ] [ [511]C-Kermit Home ] [ [512]Kermit Home ] 9.4. Summary of Built-in Constants and Operators Three constants are built in: * PI, whose value is the value of pi (the quotient of circumference of any circle and its diameter, 3.141592653...) to the underlying machine's precision; * T, which always has the value 1, which signifies truth in Kermit logical expressions or S-Expressions; * NIL, which always has the empty value, and can serve as a False truth value. These constants are specific to S-Expressions and are not visible outside them. They may not be used as the target of an assignment. So, for example: (setq t 0) Fails assign t 0 Succeeds but this is not the same T! E (the base of natural logarithms, 2.7182818184...) is not built in since it is not intrinsic in most Lisp dialects. If you want E to be the base of natural logarithms you can: (setq e (exp 1)) Operators are either symbols (such as "+") or words. Words must be spelled out in full, not abbreviated. Differences of alphabetic case are ignored. The most basic operation in S-Expressions is evaluation: EVAL [ s-expression or variable or number [ another [ another ... ] ] ] Evaluates its operands and returns the value of the last one evaluated. Examples: (eval) 0 (eval 1) 1 (eval a) value of a (eval (+ 1 a)) value of a+1 (eval (setq a 1) (setq b (+ a 0.5))) value of b (= a+0.5) You can use "." as a shorthand for EVAL: (.) (. 1) (. a) (. (+ 1 a)) (. (setq a 1) (setq b (+ a 0.5))) Opposite of EVAL is the operator that suppresses evaluation of its operand: QUOTE item The value (quote item) is "item". If the item is itself an S-Expression, the result is the S-Expression with the outer parentheses stripped. Examples: (quote) (illegal) (quote a) a (quote hello) hello (quote (this is a string)) this is a string (quote this is a string) (illegal) A shorthand notation is also accepted for quoting: 'a is equivalent to (quote a). And therefore: '(a b c) is equivalent to (quote (a b c)). More about quoting in [513]Section 9.8. STRING item Is a combination of EVAL and QUOTE. It evaluates the item as an S-Expression, and then puts quotes around the result (more about this in [514]Section 9.8). The following operators assign values to variables: SETQ [ variable [ value [ variable [ value [ ... ] ] ] ] ] Applies to global variables. For each variable given: if a value is not given, the variable is undefined. If a value is given, assigns the value to the variable. The value may be a number, a variable, or anything that resolves to a number including an S-Expression. Returns the value of the last assignment. Examples: (setq) Does nothing, returns NIL. (setq a) Undefines a, returns NIL. (setq a 1) Assigns 1 to a, returns 1. (setq a 1 b 2) Assigns 1 to a, 2 to b, returns 2. (setq a 1 b 2 c) Assigns 1 to a, 2 to b, undefines c, returns NIL. To undefine a variable that is not the final one in the list, give it a value of "()" or NIL: (setq a () b 2) Undefines a, assigns 2 to b, returns 2. (setq a nil b 2) Ditto. Note that a variable can be used right away once it has a value: (setq a 1 b a) Assigns 1 to a, the value of a (1) to b, returns 1. The results of SETQ (when used with macro names) can be checked conveniently with SHOW MACRO, e.g: show mac a b c LET [ variable [ value [ variable [ value [ ... ] ] ] ] ] Like SETQ, but applies to local variables. Note that "local" is used in the Kermit sense, not the Lisp sense; it applies to the current Kermit command level, not to the current S-Expression. If you want to use SETQ or LET to assign a value to a backslash variable such as \%a or \&a[2], you must double the backslash: (setq \\%a 3) (setq \\%b (+ \%a 1)) (setq \\&a[2] (setq (\\%c (+ \%a \%b)))) In other words: * Double the backslash when you want to indicate the variable's NAME; * Don't double the backslash when you want its VALUE. See [515]Section 9.6 for a fuller explanation of variable syntax and scope. Here's a summary table of arithmetic operators; in the examples, a is 2 and b is -1.3: Operator Description Example Result + Adds all operands (0 or more) (+ a b) 0.7 - Subtracts all operands (0 or more) (- 9 5 2 1) 1 * Multiplies all operands (0 or more) (* a (+ b 1) 3) -1.80 / Divides all operands (2 or more) (/ b a 2) -0.325 ^ Raise given number to given power (^ 3 2) 9 ++ Increments variables (++ a 1.2) 3.2 -- Decrements variables (-- a) 1 ABS Absolute value of 1 operand (abs (* a b 3)) 7.8 MAX Maximum of all operands (1 or more) (max 1 2 3 4) 4 MIN Minimum of all operands (1 or more) (min 1 2 3 4) 1 MOD (%) Modulus of all operands (1 or more) (mod 7 4 2) 1 FLOAT Convert an integer to floating-point (float 1) 1.0 TRUNCATE Integer part of floating-point operand (truncate 3.333) 3 CEILING Ceiling of floating-point operand (ceiling 1.25) 2 FLOOR Floor of floating-point operand (floor 1.25) 1 ROUND Operand rounded to nearest integer (round 1.75) 2 SQRT Square root of 1 operand (sqrt 2) 1.414.. EXP e (2.71828..) to the given power (exp -1) 0.367.. SIN Sine of angle-in-radians (sin (/ pi 2)) 1.0 COS Cosine of angle-in-radians (cos pi) -1.0 TAN Tangent of angle-in-radians (tan pi) 0.0 LOG Natural log (base e) of given number (log 2.7183) 1.000.. LOG10 Log base 10 of given number (log10 1000) 3.0 The ++ and -- operators are also assignment operators and work just like SETQ and LET in their interpretations of operators and operands, but: * Each target variable must already be defined and have a numeric value; * The assignment value is the amount by which to increment or decrement the variable. * If an assignment value is not given, 1 is used. If you include more than one variable-value pair in a ++ or -- expression, every variable (except, optionally, the last) must be followed by a value. Examples: (++ a) Equivalent to (setq a (+ a 1)) and to (++ a 1) (++ a 2) Equivalent to (setq a (+ a 2)) (-- a (* 2 pi)) Equivalent to (setq a (- a (* 2 pi))) (++ a 1 b 1 c 1 d) Equivalent to four SETQs incrementing a,b,c,d by 1. Another group of operators forms the predicates. These return a "truth value", in which 0 (or NIL) is false, and 1 or any other nonzero number is true. Operator Description Example Result = (or ==) Operands are equal (= 1 1.0) 1 != Operands are not equal (!= 1 1.0) 0 < Operands in strictly ascending order (< 1 2 3) 1 <= Operands in ascending order (<= 1 1 2 3) 1 > Operands in strictly descending order (> 3 2 1) 1 >= Operands in descending order (<= 3 3 2 1) 1 AND (&&) Operands are all true (and 1 1 1 1 0) 0 OR (||) At least one operand is true (or 1 1 1 1 0) 1 XOR Logical Exclusive OR (xor 3 1) 0 NOT (!) Reverses truth value of operand (not 3) 0 The Exclusive OR of two values is true if one value is true and the other value is false. And another group operates on bits within an integer word: Operator Description Example Result & Bitwise AND (& 7 2) 2 | Bitwise OR (| 1 2 3 4) 7 # Bitwise Exclusive OR (# 3 1) 2 ~ Reverses all bits (~ 3) -4 These operators coerce their operands to integer by truncation if necessary. The result of bit reversal is hardware dependent. The final category of operator works on truth values: Operator Description Example Result IF Conditional evaluation (if (1) 2 3) 2 IF (predicate) (s1) [ (s2) ] The IF operator is similar to Kermit's IF command. If the predicate is true (i.e. evaluates to a nonzero number), the first S-Expression (s1) is evaluated and its value is returned. Otherwise, if (s2) is given, it is evaluated and its value returned; if (s2) is not given, nothing happens and the NIL (empty) value is returned. You can group multiple expressions in the s1 and s2 expressions using EVAL (or "."): (if (< a 0) (eval (setq x 0) (setq y 0)) (eval (setq x a) (setq y b))) or equivalently: (if (< a 0) (. (setq x 0) (setq y 0)) (. (setq x a) (setq y b))) Each operator has its own requirement as to number and type of operands. In the following table, "number" means any kind of number -- integer or floating-point -- or a variable, function, macro, or S-Expression that returns a number; "vname" means variable name, "fpnumber" means a floating-point number (or anything that resolves to one), and "integer" means integer (or anything that resolves to one). "truthvalue" means anything that resolves to a value of zero or an empty value (which indicates false) or a nonzero value (which indicates true). "any" means any kind of value, including none at all. Operator Number of operands Type of operands Returns EVAL (.) 0 or more S-Expression Last value (default NIL) STRING 1 S-Expression string QUOTE (') 1 word string SETQ 0 or more vname value pairs Last value (default NIL) LET 0 or more vname value pairs Last value (default NIL) + 0 or more number number (default 0) - 0 or more number number (default 0) * 0 or more number number (see note (1)) / 2 or more number number ^ 2 or more number number ++ 1 or more vname value pairs Result of last increment -- 1 or more vname value pairs Result of last decrement ABS 1 number number MAX 1 or more number number MIN 1 or more number number MOD (%) 2 number number FLOAT 1 number fpnumber TRUNCATE 1 number integer CEILING 1 number integer FLOOR 1 number integer ROUND 1 number integer SQRT 1 number fpnumber EXP 1 number fpnumber SIN 1 number fpnumber COS 1 number fpnumber TAN 1 number fpnumber LOG 1 number fpnumber LOG10 1 number fpnumber = (==) 1 or more number truthvalue != 1 or more number truthvalue < 1 or more number truthvalue <= 1 or more number truthvalue > 1 or more number truthvalue >= 1 or more number truthvalue AND (&&) 1 or more truthvalue truthvalue OR (||) 1 or more truthvalue truthvalue XOR 2 truthvalue truthvalue NOT (!) 1 truthvalue truthvalue & 1 or more number (see note 2) integer | 1 or more number (see note 2) integer # 2 number (see note 2) integer ~ 1 number (see note 2) integer IF 2 or 3 truthvalue,any,any any Operators that don't require any arguments return the default values shown. 1. The value of "*", when used as an operand, is initially "1" and the value of the most recent S-Expression thereafter, as in Franz Lisp. This is handy when doing a series of calculations by hand: C-Kermit>(* 13272.42 0.40) 5308.968 C-Kermit>(/ * 2) 2654.4840 C-Kermit> 2. The bitwise operators coerce their operands to integer by truncation. [ [516]Top ] [ [517]Contents ] [ [518]C-Kermit Home ] [ [519]Kermit Home ] 9.5. Variables As noted elsewhere in this discussion, all backslash items (variables such as \%a, macro parameters such as \%1, array elements such as \&a[\%i], built-in variables such as \v(ndate), built-in functions such as \fjoin(), macro names enclosed in \m(), \s(), or \:(), etc) are evaluated at "top level" before the S-Expression is sent to the S-Expression reader. To use a backslash variable as the target of an assignment (e.g. by SETQ, LET, ++, or --), you must double the backslash, e.g. (setq \\%r 1234). This is discussed at greater length in the next section. Thus S-Expression reader generally deals only with macro names (not backslash items) as variables. It is important to understand how the reader handles macro names. There are fundamentally two kinds of S-Expressions: those that contain a single element, such as: (foo) and those that contain more than one element: (foo a b c) If an S-Expression contains only one element, and it is the name of a macro, the macro's definition is examined. If the definition is a number (integer or floating-point, positive or negative), then this becomes the value of the expression. If the definition starts with ' (apostrophe), then the quoted word or string is the value of the expression (explained in [520]Section 9.8). Otherwise, the macro is assumed to be composed of Kermit commands (possibly including S-Expressions), which are executed. If the macro has a RETURN value, or it executes an S-Expression as its last command, the result becomes the value of the S-Expression; otherwise the result is empty. For S-Expressions that contain more than one element, and the first element is the name of a macro, then this macro is executed with the arguments that are given, after the arguments are evaluated by the S-Expression reader. Likewise, If the first element is a built-in operator, then it is applied to the operands after they are evaluated. In both cases, each operand is fed to the S-Expression reader recursively for evaluation. If an operand is a number or a quoted string, it is used as-is. But if it's a macro name, this degenerates into the first case, and the previous paragraph applies. Examples: define foo 123 (foo) Result: 123 define foo 'abc (foo) Result: abc define foo '(one two three) (foo) Result: one two three define foo return \frandom(1000) (foo) Result: 713 (or other number) define foo (+ a b) (foo) Result: The sum of a and b A more difficult example: define foo abc (foo) Result: ??? The result in the last example depends on the definition of abc: * If it has no definition, an error occurs; otherwise: * If the definition is an S-Expression, the result is the S-Expression's value; otherwise: * If the definition consists of Kermit commands, they are executed. But in this case "(foo)" produces the empty result, because it doesn't RETURN anything. The use of macros as S-Expression operators is described in [521]Section 9.8. [ [522]Top ] [ [523]Contents ] [ [524]C-Kermit Home ] [ [525]Kermit Home ] 9.6. Assignments and Scope The assignment operators SETQ and LET apply to global and local variables, respectively. SETQ and LET are standard Lisp operators adapted to Kermit scoping rules. When the operands are numeric or arithmetic, SETQ is equivalent to Kermit's EVALUATE command: (setq a (+ 1 2)) evaluate a 1 + 2 When the operand is a string, SETQ is equivalent to DEFINE: (setq a '(this is a string)) define a this is a string In the first case, both statements create a macro named "a" with a value of 3. But in neither case is the macro "a" necessarily global. If either of these commands executes in an environment (i.e. macro invocation level) where a "local a" command has been given, the "a" macro is global to that environment, but is not visible outside it. LET is equivalent to the Kermit LOCAL command, followed by the corresponding EVALUATE: (let a (+ 1 2)) is equivalent to: local a evaluate a 1 + 2 Again, "local" in this context applies to the Kermit macro invocation stack, not to the S-Expression nesting level. To illustrate, recall our "newarea" macro: def newarea { (let a \%1 b \%2 c \%3) (let s (/ (+ a b c) 2.0)) (sqrt (* s (- s a) (- s b) (- s c))) } Because SETQ and LET expressions return a value, they can be placed within a larger S-Expression. In this case we can replace the first reference to the "s" variable by its defining expression: def newarea { (let a \%1 b \%2 c \%3) (sqrt (* (let s (/ (+ a b c) 2.0)) (- s a) (- s b) (- s c))) } This would not work if LET were local to the S-Expression, but it works nicely in the context of Kermit macros. The previous definition is equivalent to: def newarea { local a b c s (setq a \%1 b \%2 c \%3) (sqrt (* (setq s (/ (+ a b c) 2.0)) (- s a) (- s b) (- s c))) } In both cases, the variables a, b, c, and s are local to the "newarea" macro, and global within it. Multiple assignments can be handled in several ways. Here is the obvious way to initialize a series of variables to the same value: (setq a 0) (setq b 0) (setq c 0) (setq s 0) Here is a more compact and efficient way of doing the same thing: (setq a 0 b 0 c 0 s 0) However, in case the value was more complex, it's better to put only one copy of it in the S-Expression; in this case we rely on the fact that SETQ returns the value of its last assignment: (setq a (setq b (setq c (setq s (* x (^ y 2)))))) Similarly, to set a series of variables to x, x+1, x+2, ... (setq c (+ (setq b (+ (setq a (+ (setq s x) 1)) 1)) 1)) In the last example, you can see why "last" does not always correspond to "rightmost" (the leftmost variable "c" is assigned last). If you are working with backslash variables like \%a or array elements like \&a[1], remember two rules: 1. Don't put spaces inside array brackets. 2. You must double the backslash when using SETQ, LET, ++, or -- to assign a value to a backslash variable. Examples of assigning to a backslash variable: (setq x 1) (setq \\%a 0) (setq \\&a[x+1] 1) (++ \\%x) (-- \\&a[x+2]) Examples of referring to a backslash variable's value: (setq a (+ \%a 1)) (setq b (+ \%a \&a[1])) (++ a \%x) (-- b \&a[1]) The special notation is required because all backslashed items (\%x variables, array elements, built-in \v(xxx) variables, and \fxxx() function invocations) are evaluated in a single pass BEFORE the S-Expression is executed; any other approach would result in unacceptable performance. So, for example, in: declare \&a[] = 1 2 3 define \%x 4 define \%y 0 (setq \\%y (+ \%x \&a[1])) the S-Expression becomes: (setq \%y (+ 4 1)) before it is sent to the S-Expression evaluator. If the backslash had not been doubled on the assignment target, the result would have been: (setq 0 (+ 4 1)) which is illegal because you can't assign a value to a number. Conversely, if backslashes were doubled on right-hand-side values: (setq \\%y (+ \\%x \\&a[1]) this too, would give an error (not numeric - "\%x"). If you omit the double backslash in the assignment target, the result depends on whether the variable already has a value: (setq \%a (* 3 3)) If \%a has a non-numeric single-word value, then this becomes the name of the variable that is assigned by SETQ. To illustrate: define \%a foo echo \%a foo (setq \%a (* 3 3)) echo \%a foo show macro foo foo = 9 If \%a has no value, a numeric value, or a multiword value, an "invalid assignment" error occurs. [ [526]Top ] [ [527]Contents ] [ [528]C-Kermit Home ] [ [529]Kermit Home ] 9.7. Conditional Expressions The IF operator provides a compact form of decision-making within S-Expressions. An IF expression can stand wherever a number might stand, as long is it returns a number. Here's a quick way to obtain the average value of all the elements in an array that contains only numbers: (/ (+ \fjoin(&a)) (float \fdim(&a))) This results in a "Divide by zero" error if the array is empty. If you want to define the average value of an empty array to be 0 instead of getting an error, you can use IF to check the array size: (if \fdim(&a) (/ (+ \fjoin(&a)) (float \fdim(&a))) 0) or equivalently: (if (not \fdim(&a)) 0 (/ (+ \fjoin(&a)) (float \fdim(&a)))) Of course, IF can fit anywhere else into an S-Expression: (setq a (+ b (if (< c 0) 0 c))) and the IF expression can be as complex as you like: (setq a (+ b (if (and (or (> x 0) (> y 0)) (< c 0) (> d 1) (!= e 0)) 1 0))) and the "then" and "else" parts can contain multiple S-Expressions enclosed within (EVAL ...): (if x (eval (...) (...) (...)) (eval (...) (...) (...))) AND and OR operators are guaranteed to "short circuit". If any operand of AND is false, none of the subsequent operands is evaluated; likewise, if an OR operand is true, no further operands are evaluated. Bear in mind that the S-Expression IF is not the same as Kermit IF; the condition is only allowed to be an S-Expression or a variable or number, not the whole list of possibilities you see when you type "if ?" at the C-Kermit> prompt. But keep reading... [ [530]Top ] [ [531]Contents ] [ [532]C-Kermit Home ] [ [533]Kermit Home ] 9.8. Extensibility To extend the capabilities of S-Expressions, you can use Kermit macro names as operators, with the following limitations: * The macro must not have the same name as a built-in operator. * You must use the full macro name, not an abbreviation. And with the following enhancement: * If the last statement executed by the macro is an S-Expression, its value is returned automatically. In other words: define bump (++ \%1) is equivalent to: define bump return \fsexpression(++ \%1) Here's an example in which we define a FIBONACCI operator that returns the nth element, n >= 0, of the Fibonacci series, 0 1 1 2 3 5 8 13 21 34 55, . . ., in which the first element is 0, the second is 1, and each subsequent element is the sum of the two before it. This series was devised by Leonardo Pisano, Filius Bonacci (Fibonacci for short) in 1202 to describe how fast rabbits can breed, and also forms the basis for the Golden Mean, the branching behavior of plants, the spiral of a nautilus shell, etc. (Thanks to [534]Dat Thuc Nguyen for December 2003 corrections to this section!) We can write a FIBONACCI function as a macro easily with S-Expressions: define FIBONACCI { (if (== \%1 0) 0 (if (== \%1 1) 1 (+ (fibonacci (- \%1 2)) (fibonacci (- \%1 1))))) } You can read this as: If the argument (\%1) is 0, return a result of 0; if it is 1, return 1; otherwise: return the sum of fibonacci(argument - 2) and fibonacci(argument - 1) Note that a RETURN statement is not needed, since S-Expressions automatically set the return value of their containing macros. For comparison, here's how it would be coded without S-Expressions: define FIBONACCI { if == \%1 0 { return 0 } else if == \%1 1 { return 1 } else { return \feval(\fexec(fibonacci \feval(\%1-2)) - + \fexec(fibonacci \feval(\%1-1))) } } Now we can use the FIBONACCI function (whichever way you write it) just as if it were a built-in operator: (fibonacci 6) Or: (setq a 10) (fibonacci a) Within S-Expressions only (not outside them), S-Expressions themselves can be used as macro arguments: (setq a 2 b 4) (setq x (fibonacci (* a b ))) The value of the S-Expression (in this case "8"), and not the S-Expression itself, is sent to the macro. Your macro is responsible for argument validation and error handling. A robust Fibonacci macro would be more like this: define FIBONACCI { if < \v(argc) 2 end 1 ?\%0: Missing argument if > \v(argc) 2 end 1 ?\%0: Too many arguments if not integer \%1 end 1 ?\%0: Integers only if < \%1 1 end 1 ?\%0: Argument out of range (if (== \%1 0) 0 (if (== \%1 1) 1 (+ (fibonacci (- \%1 2)) (fibonacci (- \%1 1))))) } Recall that "END nonzero-number [ message ]" causes a macro invocation to fail. When the macro is the operator in an S-Expression, this makes the S-Expression fail too. Also note that our Fibonacci macro is just an illustration, not a practical example. Since it is recursive (calls itself), it won't work for large arguments because the call stack can exceed available memory. See [535]Section 9.9.2 for a practical alternative. Kermit macros, when used as S-Expression operators, can do anything at all except initiate file transfers: they can print messages on the screen, read and write files, interact with the user, and so on. For example, here's a macro ASKME that asks you to enter a number, makes sure that you did, and then returns its value for use in the S-Expression: define ASKME { local \%n while true { ask \%n { Number: } if not def \%n continue if not numeric \%n { echo Not numeric - "\%n" continue } break } return \%n } (setq a (* 2 (askme))) ; Get number from user, double it, assign result to a. Here's a macro you can use to validate that a number is in a given range: define inrange { if != \v(argc) 4 end 1 ?\%0: Wrong number of arguments if ( < \%1 \%2 || > \%1 \%3 ) return 0 return 1 } The first argument is the number to be checked, the second is the minimum acceptable value, the third is the maximum. You can use this (for example) in IF conditions: define yes echo \%1 IS OK define no echo \%1 IS NOT OK (setq a -1 b 999) (if (inrange a 0 100) (yes a) (no a)) (if (inrange b -1000 +1000) (yes b) (no b)) This is just an illustration, of course; there's already a built-in operator to let you do range checking without help from macros: (if (<= 0 a 100) (yes a) (no a)) (if (<= -1000 b +1000) (yes b) (no b)) To send string parameters to a macro, some kind of quoting is required to tell the S-Expression parser to take a given "word" literally rather than replacing it by its value. For this we use the Lisp QUOTE operator: define length return \flength(\%1) (length (quote abcdefghijklmnopqrstuvwxyz)) 26 This causes the string "abcdefghijklmnopqrstuvwxyz" to be sent literally to the LENGTH macro. Kermit, like Lisp, also offers a shortcut for QUOTE, that lets us quote a word by prefixing it with a single quote (') character, also called apostrophe (ASCII 39): (length 'abcdefghijklmnopqrstuvwxyz) 26 The two forms are equivalent. How the macro treats its arguments is up to the macro. In the example above, the argument is treated as a literal string. However, it can also be treated as a variable name: define string This is a string define length return \flength(\m(\%1)) (length 'string) 16 Note the construct \m(\%1). This means "the value of the macro whose name is the value of \%1". The value of \%1 in this case is the word "string", and the value of the macro whose name is "string" is "This is a string". What if the macro takes multiple arguments, or a variable number of them? Here's a simple macro that prints a phrase that includes its arguments: define complain echo It's too \%*! (Recall that \%* means "all arguments".) It can be called in the traditional way: complain hot Result: "It's too hot!" complain cold and wet Result: "It's too cold and wet!" Or from an S-Expression if you quote the arguments: (complain 'hot) Result: "It's too hot!" (complain 'cold 'and 'wet) Result: "It's too cold and wet!" To group multiple words into a single argument, use parentheses: (complain (quote (cold and wet))) Result: "It's too cold and wet!" (complain '(cold and wet)) Result: "It's too cold and wet!" Note the difference: (complain 'cold 'and 'wet) Three arguments (complain '(cold and wet)) One argument Since the COMPLAIN macro uses \%* to refer to all its arguments, no matter how many, it doesn't care which form you use. But it makes a difference in cases where the macro refers to its arguments individually. To illustrate, let's consider a macro that receives the name of a macro and its argument list and executes it with its arguments, without knowing how many arguments there are. The following LOOP macro is used to execute the given macro with the given argument list the requested number of times: def loop { local i, for i 1 \%1 1 do \%2 \%3 } Within the LOOP macro, the first argument (\%1) is the loop count, \%2 is the macro name, and \%3 is the argument list. When the LOOP macro is invoked traditionally like this: loop 3 complain hot it prints "It's too hot!" three times. To invoke it from an S-Expression, you must quote both the macro name as well as the argument, since in this case the macro name itself is an argument: (loop 3 'complain 'hot) Now what if you need to send different or variable numbers of arguments to the LOOP macro? The LOOP macro can handle it already, provided you group the arguments into LOOP's third argument (\%3). In Kermit syntax, without grouping: loop 3 complain cold and wet prints "It's too cold!" three times ("and wet" is lost); but with grouping (either of the following two forms): loop 3 complain {cold and wet} loop 3 complain "cold and wet" the LOOP macro prints "It's too cold and wet!" three times as desired. To do the same thing in an S-Expression, just use the Lisp forms of quoting instead of the Kermit forms; the following two are equivalent: (loop 3 'complain (quote (cold and wet))) (loop 3 'complain '(cold and wet)) Here's a similar example in which we write a macro that shows both the name and the value of one or more other macros, whose names are given as arguments (similar to "show macro"): define display { local \%i for \%i 1 \v(argc)-1 1 { echo \&_[\%i] = \m(\&_[\%i]) } } (Recall that \&_[] is the macro's argument vector array, equivalent to \%1, \%2, ...) The DISPLAY macro can be used in S-Expressions like this: (setq a 1 b 2 c 3) (display 'a 'b 'c 'd) which prints: a = 1 b = 2 c = 3 d = The names must be quoted to prevent their evaluation before they are sent to the macro. This ability to pass variables "by name" to macros, rather than by value, lets you write macros that change the values of argument variables. For example, here's a macro that doubles the value of its argument variable: define double (++ \%1 \%1) which you can call like this: (setq a 12) (double 'a) In the macro, \%1 is replace by the variable name "a"; "(++ a a)" adds "a" to itself, and sets the value of "a" to the result. There are no built-in operators other than QUOTE, ', and STRING for handling strings in S-Expressions, but using just these, plus macros that use Kermit's regular string-handling features, you can easily extend S-Expressions to do string manipulation: define len return \flen(\%1) Returns length of argument string define cap return \fupper(\%1) Uppercase argument string define rev return \freverse(\%1) Reverses argument string define sub return \fsubstr(\%1,\%2,\%3) Returns substring of arg string (len '(this is a string)) Result: 16 (rev '(this is a string)) Result: gnirts a si siht (rev (cap '(this is a string))) Result: GNIRTS A SI SIHT (sub (rev (cap '(this is a string))) 5 9) Result: TS A SI S You can assign a string to a macro name as follows: (setq foo '(this is a string)) (setq foo (quote (this is a string))) The two are exactly equivalent. In both cases, the macro "foo" has the value: '(this is a string) so when it is retrieved it can be identified as a string rather than a number or commands to be executed. Thus: (setq foo (quote (this is a string))) show macro foo foo = '(this is a string) (foo) this is a string Note the different results for "show macro foo" and "(foo)". The former shows the internal definition; the latter evaluates the variable, which removes the quoting. And perhaps more important, note that if the apostrophe and surrounding parentheses were not stored as part of the definition, (foo) would try to execute "this is a string" as a command. Given the assignment above, the following work as expected: (len foo) Result: 16 (rev foo) Result: gnirts a si siht (rev (cap foo)) Result: GNIRTS A SI SIHT (sub (rev (cap foo)) 5 8) Result: TS A SI S Note that, unlike built-in S-Expression operators that return numbers or truth values, these operators return strings. If you want to assign their return values to other variables, you can do so: (setq bar (rev (cap foo))) Result: GNIRTS A SI SIHT But now the S-Expression processor doesn't know the value of "bar" is supposed to be a string, rather than a macro to execute. For this you need one final special operator, STRING. The STRING operator takes an S-Expression as an operand, evaluates it, and then returns its value enclosed in '(), so you can use the value as a string is subsequent S-Expressions. Use STRING for referencing macros that return strings: (setq bar (string (rev (cap foo)))) Result: '(GNIRTS A SI SIHT) STRING is like QUOTE, except that it evaluates its operand before applying the quoting, rather than taking the operand literally. To reference backslash variables or functions that return string values, you must use the regular quoting mechanisms: (setq time '(\v(time))) (setq date '(\v(date))) assign \%r this is a string (setq s1 '(\%r)) That's because backslash items are evaluated BEFORE the S-Expression parser ever sees them, and the values of \v(time) and so on are not valid S-Expressions, so STRING won't like them. Finally a brief word on the touchy topic of quoting. Suppose you want to include (say) literal parentheses in a string that will later be processed by the S-Expression reader (or \fsplit() or \fword()). Normally, you can't do this because parentheses are meaningful in these contexts. To defeat the normal parsing rules, you can quote the parentheses with backslash. However, due to the many levels of string processing involved, a surprisingly large amount of backslashes might be required, for example: (setq s '(a b (c d) \\\\\\\\\\\\\\\\(e f (g h) x\\\\\\\\\\\\\\\\) j k)) This is nearly impossible to explain(*). Instead, just remember two points: * In situations like this, it's better to use DEFINE to create the string, rather than SETQ. The example above requires only double backslashes when DEFINE is used: define s '(a b (c d) \\(e f (g h) x\\) j k) * The level of quoting depends on how many levels of evaluation the string must pass through, which is not always obvious. However, the number of backslashes required in any given situation is always a power of 2. So if 1 doesn't work, try 2; if 2 doesn't work, try 4; if 4 doesn't work, try 8, 16, 32, and so on. Considerations like this apply in any scripting language (shell, Tcl, Perl, Python, etc). The situation is known as "Quoting Hell". (*) If you really want an explanation, here it is: * Every SEXP has its backslash items evaluated in a single pass at top level before being passed to the SEXP reader, so \%1, \v(ftime), etc, can be evaluated up front, freeing the SEXP reader of having to know about such things, which in turn makes it much more efficient. Therefore one level of quoting is lost right away, and therefore you must double each backslash that is to be used as a quote. * When the SEXP reader sees '\', it treats it as a quote; discards it and keeps the next character. Thus '\\' becomes '\'. This would be the end of it, except that: * The SEXP reader must call itself recursively on its operands, so we must double any quotes in the operands: 2^2 = 4. * If the result is to be passed as an argument to a macro, the backslashes must again be doubled, because the macro processor evaluates the arguments before sending them to the macro: 2^3 = 8. * If the macro itself is to see the quotes, rather than just the result of the quoting, the quotes must be doubled again: 2^4 = 16. Moral: To create string constants in which grouping characters must be quoted, use DEFINE rather than SETQ. [ [536]Top ] [ [537]Contents ] [ [538]C-Kermit Home ] [ [539]Kermit Home ] 9.9. Examples 9.9.1. Statistics The following program computes statistics -- means, maxima, minima, variance, standard deviation, and correlation -- from data stored in parallel arrays, \&x[] and \&y[], which can contain any mixture of integer and floating-point numbers: positive, negative, or zero. Array setup and validation are not shown. Except for the traditional FOR loop and printing the results at the end, the entire computation is done with S-Expressions: ; Initialize sums, maxima, minima, and number of elements (setq xsum 0 ysum 0 xsum2 0 ysum2 0 xysum 0) (setq xmin (setq xmax \&x[1]) ymin (setq ymax \&y[1])) (setq n \fdim(&x)) ; Loop through elements and accumulate sums, maxima, and minima for i 1 n 1 { (setq x \&x[i] y \&y[i]) ; Notational convenience (setq xmax (max xmax x) ymax (max ymax y)) ; X and Y maxima (setq xmin (min xmin x) ymin (min ymin y)) ; X and Y minima (++ xsum x ysum y) ; X and Y sums (++ xsum2 (^ x 2) ysum2 (^ y 2)) ; Sum of X and Y squares (++ xysum (* x y)) ; Sum of XY products } ; Calculate results (setq xmean (/ xsum n) ymean (/ ysum n)) ; Mean X and Y (setq xss (- xsum2 (/ (^ xsum 2) n))) ; Intermediate values (setq yss (- ysum2 (/ (^ ysum 2) n))) (setq xyss (- xysum (/ (* xsum ysum) n))) (setq xvar (/ xss n) yvar (/ yss n)) ; X and Y variance (setq sdx (sqrt xvar) sdy (sqrt yvar)) ; Std deviation in X and Y (setq tmp (* xss yss)) (setq cc (if tmp (/ xyss (sqrt tmp)) 1.0)) ; Correlation coefficient show macro xmean ymean xvar yvar sdx sdy cc ; Print the results The final "if tmp" check accounts for the possibility that both arrays contain all 0's. Results can also be printed with "echo CC = \m(cc)", or any other desired way. Interestingly, if we had not needed the sum of the squares and products, we could have obtained the sums, maxima, and minima of the X's and Y's without a loop like this: (setq xsum (+ \fjoin(&x)) ysum (+ \fjoin(&y))) (setq xmax (max \fjoin(&x)) ymax (max \fjoin(&y))) (setq xmin (min \fjoin(&x)) ymin (min \fjoin(&y))) Any Kermit function that returns numbers or lists of numbers can be included in an S-Expression as an operand. 9.9.2. Practical Fibonacci Series The recursive Fibonacci example given previously is simple and elegant, but not very useful since it causes memory occupation to grow each time it calls itself, until eventually both physical memory and disk swap space are filled and the program crashes. Even for small arguments, like 17, execution time can be prohibitive: (setq t1 \v(ftime)) (setq result (fibonacci 17)) (setq t2 (- \v(ftime) t1)) echo FIBONACCI(17) = \m(result): TIME = \ffpround(t2,3) prints (on a certain rather slow computer): FIBONACCI(17) = 1597: TIME = 5.861 Any recursive function can be recoded iteratively. The result is not as pretty, but execution is far less expensive: define FIBITER { (if (== \%3 0) (\%2) (fibiter (+ \%1 \%2) \%1 (- \%3 1))) } define FIBONACCI { (fibiter 1 0 \%1) } Here's the result on the same computer for the same argument of 17: FIBONACCI(17) = 1597: TIME = 0.015 (47 times faster.) Execution time increases proportionally to the size of the argument in the iterative case, whereas in the recursive case it goes up geometrically, quickly reaching infinity. [ [540]Top ] [ [541]Contents ] [ [542]C-Kermit Home ] [ [543]Kermit Home ] 9.10. Differences from Algebraic Notation In C-Kermit: * Algebraic notation uses infix operators and normal rules of operator precedence, with parentheses used to force exceptions to the rules; many operations can be included in an expression. S-Expressions use prefix operators with no intrinsic precedence; each operation is enclosed in parentheses, and the arrangement of parentheses determines precedence. * Algebraic infix operators require two operands; S-Expression prefix operators can accept a variable number of operands. * You can use algebraic notation anywhere that C-Kermit accepts a number, e.g. "echo \&a[((1+1)*2-1]", but you can use S-Expressions only as top-level commands. You can, however, use either algebraic or S-Expressions anywhere at all by enclosing them in \fevaluate() or \fsexpression(), respectively. * You can use any mixture of integer and floating-point numbers in S-Expressions, but only integers are permitted in algebraic expressions. Outside of S-Expressions, floating point arithmetic is supported only by \ffp...() function calls. * Operators and operands in S-Expressions must be separated by spaces, e.g. "(+ a b)". Spaces are not required in algebraic expressions: "((a+b)*c)". * When assigning values to backslash variables (such as \%x or \&a[2]) using SETQ or LET, you must double the backslash. [ [544]Top ] [ [545]Contents ] [ [546]C-Kermit Home ] [ [547]Kermit Home ] 9.11. Differences from Lisp * Kermit has a lot of built-in operators not found in Lisp: ++, ^, etc. * Most dialects of real Lisp do not allow S-Expressions that don't start with an operator, for example: (a) This expression can cause an error in Lisp (even if "a" has a value), but is acceptable in Kermit, where it returns the value of the variable "a". Similarly, (1) returns the value "1". * In real Lisp, EVAL requires exactly one operand. In Kermit, it can have 0, 1, 2, or more operands. It returns the value of the last operand evaluated. * Real Lisp SETQ and LET usually require an even number of operands. Kermit allows an odd number, in which case the last (or only) variable is undefined (i.e. deleted, destroyed). * Kermit does not support ratios such as "7/8". Some Lisp dialects accept ratios as numbers, and generate ratios when told to divide two integers whose quotient is not a whole number; e.g. in Common Lisp: [13] USER(37): (/ (+ 1 2 3 4) 3) 10/3 [13] USER(38): * The result of (/ 10 3) is 3.333.... Some Lisp dialects truncate the result to 3 since both operands are integers, some don't; some give the result as a ratio. C-Kermit always gives a floating point result when there is a fractional part. If you want an integer result, you can use TRUNCATE, FLOOR, or CEILING, e.g. (truncate (/ 10 3)). * There is currently no "bignum" support. Large numbers can be used and large results generated, but (as noted in [548]Section 9.2) they are accurate only to the precision of the underlying machine. \v(math_precision) gives the machine precision as a number of decimal digits, e.g. 16. * Scientific notation for floating-point numbers is not supported. If the magnitude of a number is greater than the precision of the underlying hardware, the less-significant digits are shown but their values are meaningless. If it the number is too small to be represented internally, it is shown as "0.0". * Many Lisp features are omitted: List processing (CAR, CDR, etc), DEFUN, Lisp-specific control structures, and so on. [ [549]Top ] [ [550]Contents ] [ [551]C-Kermit Home ] [ [552]Kermit Home ] 10. FILE TRANSFER New commands and switches: SET TRANSFER REPORT { OFF, ON } Enables or disables the (new) one-line message printed by Kermit after a remote-mode file transfer to indicate the source and destination file, complete with path, to let you know where the file went. SEND /TYPE:{TEXT,BINARY} Sends only files of the given type (see [553]Section 4). SEND /NOFOLLOWLINKS: (UNIX only) Skip over symbolic links rather than following them (default). This applies to wildcard and/or recursive SENDs; if a single filename is given, and it happens to be a symbolic link, the file it points to is sent. SEND /FOLLOWLINKS: (UNIX only) Follow (resolve) symbolic links. Watch out for circular links, endless loops, etc. SET SEND I-PACKETS { OFF, ON } When sending commands to a Kermit server, this tells whether command packets should be preceded by an I (information) packet, which is used to synchronize parameters prior to executing the command. Normally ON. The only reason to set this OFF is for communicating with buggy Kermit servers that misbehave when an I packet is sent to them. There is also a SET RECEIVE I-PACKETS command, but presently it has no effect. SET TRANSFER MESSAGE [ text ] Sets an initial message to be shown in the Last Message field of the fullscreen file-transfer display. SET TRANSFER TRANSLATION { ON, OFF } Inhibits or re-enables text-file transfer character-set translation globally. { SEND, MSEND, GET, RECEIVE } /TRANSPARENT Inhibits character-set translation for this transfer only. { GET, RECEIVE } /PIPES:{ON,OFF} Overrides global TRANSFER PIPES setting for this transfer only; ON allows incoming files with names like "!tar xf -" to be opened as pipelines rather than regular files. The following new "hot keys" are available when Kermit's file-transfer display is visible: D: Turn on debugging, open "debug.log" if not already open. d: Turn off debugging but leave log open (if it was open). T: Turn on debug-log timestamps. t: Turn off debug-log timestamps. Other improvements: * SET FILE DOWNLOAD-DIRECTORY now works for external protocols (e.g. sz/rz) too. * Improved automatic per-file text/binary switching, described in [554]Section 4. * When sending a file group (e.g. "send *.*"), failure to open a file is no longer fatal; now C-Kermit simply goes ahead to the next file. * Transaction log entries are now made for external protocols too. [ [555]Top ] [ [556]Contents ] [ [557]C-Kermit Home ] [ [558]Kermit Home ] 11. MODEMS AND DIALING In C-Kermit 8.0, the default modem type for dialing has changed from NONE (= DIRECT, meaning no modem) to GENERIC. This change should have no impact on direct connections. For dialing, it means that, unless you SET MODEM TYPE to a specific type, such as USROBOTICS or CONEXANT, Kermit assumes: 1. The modem uses the Hayes AT command set. 2. The modem supports error correction, data compression, and hardware flow control and is already configured to use them. In fact, Kermit assumes the modem is completely configured, and therefore does not send it an initialization string or any configuration commands. Instead, it sends only the simplest and most portable commands: ATQ0V1 Give dial result codes. ATDTnumber Dial the number. (or ATD or ATDP, as appropriate). The new defaults work for direct connections and for most modern modems on most platforms, and they work much faster than "full-treatment" dialing. If the new defaults don't work for you, or if you need to perform explicit modem configurations or interactions, then set a specific modem type and use the SET MODEM and SET DIAL commands as documented in Using C-Kermit. WARNING: Don't use the generic modem on hosts that do not support RTS/CTS flow control. If Xon/Xoff is in use on the serial port, you'll need to select a particular modem type so Kermit knows what command to give it to enable Xon/Xoff flow control between itself and your serial port. The following new modem types were added in C-Kermit 8.0: lucent: Lucent Venus chipset pctel: PCTel V.90 chipset conexant: Conexant (ex-Rockwell) modem family zoom-v32bis: New name for "Zoom" zoom-v34 Zoom V.34 zoom-v90 Zoom V.90 56K zoom-v92: Zoom V.92 with V.44 data compression zoltrix-v34: New name for "zoltrix" zoltrix-hsp-v90: Synonym for PCTel zoltrix-hcf-v90: Synonym for ITU-T-V250 smartlink-v90: Synonym for usrobotics (same chipset) acer-v90: Synonym for Rockwell-v90 New DIAL-related variables: \v(dm_hf): Dial modifier: Wait for Hook-Flash. \v(dm_wb): Dial modifier: Wait for Bong. Finally, if dialing fails, Kermit now prints a context-sensitive hint suggesting possible reasons and remedies. Added in C-Kermit 8.0.201: Rudimentary support for Caller ID, for use with the ANSWER command. If the modem reports Caller ID information, Kermit stores it in variables that you can access after the call is answered: \v(callid_date) The date of the call \v(callid_time) The time of the call \v(callid_name) The name of the caller \v(callid_nmbr) The telephone number of the caller \v(callid_mesg) A message The format of these items depends on the originating and answering phone companies and the modems and their configuration. Not very many modems support Caller ID, and those that do (a) tend to have it disabled by default, and (b) use different commands to enable it. A quick survey shows of some current models shows: - USR V.90: No - ITU-T V.250: No - Lucent Venus: No - Diamond Supra: #CID=1 - Rockwell 56K: #CID=1 - PCTEL: #CID=1 - Zoltrix: +VCID=1 - Conexant: +VCID=1 To use Kermit's Caller ID feature, you have to set the modem to wait for at least two rings before answering, and you have to give the command to enable Caller ID; for example (after choosing a modem with SET MODEM TYPE): set modem command autoanswer on ATS0=2#CID=1\{13} set modem command autoanswer on ATS0=2+VCID=1\{13} These commands can be undone with: set modem command autoanswer on ATS0=1#CID=0\{13} set modem command autoanswer on ATS0=1+VCID=0\{13} Kermit presently has no built-in knowledge of the Caller ID capabilities or commands of the modems in its database. Since the variables can be accessed only after the call is answered, the only way to refuse a call is to answer it, inspect the variables, and then hang it up if desired. [ [559]Top ] [ [560]Contents ] [ [561]C-Kermit Home ] [ [562]Kermit Home ] 12. TERMINAL CONNECTION Now that 7-bit connections are no longer the norm, the default terminal bytesize (also called "data size" or "word size") in C-Kermit 8.0 is 8 bits, rather than 7 bits as it was in C-Kermit 7.0 and earlier: SET ESCAPE character This command, which specifies your CONNECT-mode escape character, allows you to specify any ASCII control character in a variety of formats. C-Kermit 8.0.201 now also lets you specify any 8-bit value, 128-255, as the escape character. In the SET ESCAPE command, you can type the 8-bit character literally or you can enter its numeric code. Here are examples that you can enter from a terminal or console that uses the ISO Latin-1 character set: C-Kermit> set escape à C-Kermit> set escape 195 C-Kermit> show escape Escape character: Code 195 (Ã): enabled C-Kermit> Both of these commands set the escape character value to 195 (decimal), which happens to be uppercase letter A with Tilde in Latin-1. SHOW ESCAPE and SHOW TERMINAL show the value, as does the CONNECT message. SET TERMINAL AUTODOWNLOAD ERROR { STOP, CONTINUE } When Kermit has a terminal connection to another computer, and a file transfer is initiated automatically because a Kermit packet was received in CONNECT mode (i.e. in the terminal screen), this command tells what Kermit should do if the transfer fails. The default is to STOP, which leaves Kermit in command mode with its file-transfer display showing, so you can see that the transfer failed and why. If you SET TERMINAL AUTODOWNLOAD ERROR CONTINUE, this causes Kermit to return automatically to its terminal screen (i.e. resume its CONNECT session) as if the transfer had succeeded; this can be desirable if the entire session is under control of a host-based script. SET TERMINAL BYTESIZE { 7, 8 } The byte size to use during CONNECT and INPUT command execution, which can be more restrictive than the bytesize implied by the current PARITY setting, but not less restrictive. In C-Kermit 7.0 and earlier, the terminal bytesize was 7 by default to protect against the likelihood that parity was in use on the connection without the user's knowledge. When the terminal bytesize is 8 (as it is in C-Kermit 8.0 and later), the user will see garbage in this (increasingly unlikely) situation. Note that 8 data bits are required for most character sets other than ASCII: Latin-1, UTF-8, and so on. A new command has been added to produce timestamped session logs: SET TERMINAL SESSION-LOG TIMESTAMPED-TEXT Records the terminal session in text mode (like SET TERMINAL SESSION-LOG TEXT) but adds a timestamp at the beginning of each line. The timestamp format is hh:mm:ss.nnn, and indicates the time at which the first character of the line appeared. In most UNIX versions (those built with the select()-capable CONNECT module -- pretty much all the ones that have or could have TELNET included), an idle timeout feature has been added: SET TERMINAL IDLE-TIMEOUT number If the number is not 0, then Kermit is to take an action when the given amount of time passes with no activity during CONNECT mode. If the number is positive it is the maximum number of idle seconds; if number is negative it represents milliseconds (thousandths of seconds). If 0 is given as the number, there are no idle timeouts. Synonym: SET TERMINAL IDLE-LIMIT. SET TERMINAL IDLE-ACTION { RETURN, HANGUP, EXIT, OUTPUT [ string ] } The action to be taken upon an idle timeout in CONNECT mode. RETURN to the prompt, HANGUP the connection, EXIT from Kermit, or OUTPUT the given string (if no string is given, a NUL (ASCII 0) character is sent). SET TERMINAL IDLE-ACTION { TELNET-NOP, TELNET-AYT } Actions that can be selected on Telnet connections only, that might be useful if idle limits are enforced by the Telnet server or in the TCP/IP protocol: TELNET-NOP sends a "NO Operation" (do-nothing) command, which causes no response from the server; TELNET-AYT sends an "Are You There" message to the server, which should make the server send back a message. Neither of these actions interferes with your remote session. SET TERMINAL IDLE-ACTION is useful for connections to hosts or services that automatically log you out after a certain amount of idle time, e.g.: set term idle-timeout 300 set term idle-action output \32 sends a space (as if you had pressed the space bar) every 300 seconds (five minutes) while there is no activity (32 is the ASCII code for space). When C-Kermit returns from CONNECT to command mode, the reason for the transition is given in a new variable, \v(cx_status): 0 No CONNECT command given yet. 1 User escaped back manually. 2 A trigger string was encountered. 3 IKSD entered server mode. 4 Application Program Command received from host. 5 Idle timeout. 6 Telnet protocol error. 7 Keystroke macro. 8 Time limit exceeded. 100 Internal error. 101 Carrier required by not detected. 102 I/O error on connection. 103 Disconnected by host. 104 Disconnected by user. 105 Session limit exceeded. 106 Rejected due to Telnet policy. 107 Received kill signal. Values 100 and above indicate there is no connection. [ [563]Top ] [ [564]Contents ] [ [565]C-Kermit Home ] [ [566]Kermit Home ] 13. CHARACTER SETS See the section on [567]file scanning above, and the section on character-set conversion in [568]FTP. Also: * True support for CP1252 (rather than treating it as Latin-1). * Proper handling of C1 values when converting ISO 8-bit text to UTF-8. * TYPE /CHARACTER-SET: /TRANSLATE-TO: allows specific translations. * The TRANSLATE command now works on multiple files. * K_CHARSET environment variable to set the file character-set. * SET TRANSFER TRANSLATION OFF. * FTP client character-set translation ([569]Section 3.7). [ [570]Top ] [ [571]Contents ] [ [572]C-Kermit Home ] [ [573]Kermit Home ] 14. DIALOUT FROM TELNET TERMINAL SERVERS For years, C-Kermit has supported dialing out from Telnet modem servers (also called reverse terminal servers or access servers), but until now there was no way for Kermit to control the communication parameters (speed, parity, etc) on the serial port of the terminal server; it had to use whatever was there. But now, if you make a connection to a server that supports the Telnet Com Port Control Option, [574]RFC 2217, you have the same degree of control as you would have over a serial port on the computer where Kermit is running: SET SPEED, SET FLOW, SET PARITY, SET STOP-BITS, SHOW COMM, WAIT, SET CARRIER-WATCH, the modem-signal variables, sending Break, and so on, apply to the connection between the terminal server and the modem. For example, using a Cisco Access Server 2509, where specifying a TCP port in the 6000's selects a serial port that can be used for dialing out: set host xxx 6001 ; xxx is the IP hostname or address of the server (log in if necessary) ; With a script or by hand set modem type usr ; Tell Kermit what kind of modem it has set speed 57600 ; This affects the server's port set flow rts/cts ; Ditto dial 7654321 The modem server might or might not require a login sequence. It might also allow for automatic authentication, e.g. via Kerberos tickets. NOTE: If the modem server requires a login sequence, then REDIAL might not work as expected. When you have a Telnet Com Port connection, your SET SPEED and SET FLOW options change automatically to reflect the capabilities of the server, rather than those of your local computer. See the configuration manual for your server for additional information. For example, how to set up the server to drop the Telnet connection automatically when the telephone call is hung up (e.g. "autohangup" on Cisco models). For a Linux-based Telnet Com-Port server, click the Sredird link: [ [575]Top ] [ [576]Contents ] [ [577]Sredird ] [ [578]C-Kermit Home ] [ [579]Kermit Home ] 15. COPING WITH BROKEN KERMIT PARTNERS There are lots of faulty Kermit protocol implementations out there, found mainly in 3rd-party products ranging from communications software packages to file-transfer functions embedded within devices. This topic is covered [580]HERE for C-Kermit 7.0, but C-Kermit 8.0 adds some additional tricks. SET ATTRIBUTE RECORD-FORMAT { ON, OFF } Allows control of the Kermit's Record-Format attribute. Set this to OFF in case incoming file are refused due to unknown or invalid record formats if you want to accept the file anyway. SET ATTRIBUTES OFF This is not a new trick, but it was recently discovered that the Kermit implementation embedded within a certain kind of punching/bending machine (Salvagnini if you must know) hangs upon reception of standard format Kermit attributes when receiving files. When sending files, it sends attributes of its own, one per A-packet, which is slightly unusual but legal. When receiving files from C-Kermit, K95, MS-DOS Kermit, Kermit-370, etc, it simply exits upon reception of the first A-packet; apparently it was not coded according to the protocol specification, which allows multiple attributes per A-packet. Solution: tell the file sender to SET ATTRIBUTES OFF. SET SEND I-PACKETS { ON, OFF } A Kermit server is supposed to accept I-packets; this is how the client lets the server know its capabilities and preferences before sending a command. Apparently there is at least one Kermit server implementation that does not accept I-packets, and does not properly respond with an Error packet if it gets one. To get around such situations in C-Kermit 8.0, you can use SET SEND I-PACKETS OFF to inhibit the sending of I packets. In this case, the client must be able to adjust to the server's configuration, rather than the other way around as we are used to. SET PROTOCOL KERMIT {} {} {} C-Kermit 6.0 and later automatically send "autoupload" and "autodownload" commands when in local mode and you give a file transfer command. For example, if you tell kermit to "send oofa.txt", Kermit sends "kermit -r" and a carriage return, in case you had forgotten to start Kermit on the far end and told it to receive a file. If a Kermit program had already been started on the far end, it should harmlessly absorb this string. However, some Kermit programs violate the Kermit protocol definition and treat such strings as Kermit packets even though they are not. In such cases, give this command to set the Kermit protocol autoupload and download strings to nothing, which tells Kermit not to send them. (This is not a new feature, but it was not previously included in the "Coping" section of the documentation.) [ [581]Top ] [ [582]Contents ] [ [583]C-Kermit Home ] [ [584]Kermit Home ] 16. NEW COMMAND-LINE OPTIONS kermit -h Now prints a complete listing of its command-line options, rather than an abbreviated list squeezed into a 24x80 space. -dd Debug, like -d but adds timestamps --version Shows C-Kermit version number. --noperms Equivalent to SET ATTRIBUTE PROTECTION OFF. Kermit now accepts a selection of URLs (Universal Resource Locators) as its first command-line argument. These are: telnet:hostname Makes a Telnet connection to the given host (IP hostname or address). ftp://[user[:password]@]hostname[/path...] Makes an FTP connection to the given host (IP hostname or address). If a username is given, Kermit tries to log you in; if a password is given, it is used; if not, you are prompted for one. If no username is given, an anonymous login is performed. If a pathname is included, Kermit tries to GET the given file. See [585]Section 3.1.3 for details. ftps://[user[:password]@]hostname[/path...] Makes a secure FTP connection over SSL. telnets://[user[:password]@]hostname Makes a secure Telnet connection over SSL. kermit://[user[:password]@]hostname[/path...] Makes a connection to an [586]Internet Kermit Server. http://[user[:password]@]hostname[/path...] Makes a connection to Web server. https://[user[:password]@]hostname[/path...] Makes a connection to secure Web server. [ [587]Top ] [ [588]Contents ] [ [589]C-Kermit Home ] [ [590]Kermit Home ] 17. LOGS In C-Kermit 8.0, we make an effort to keep passwords out of the debug log. This can never be 100% effective, but it's better than before, when there were no precautions at all. Whenever Kermit knows it's prompting for, parsing, or transmitting a password, it temporarily turns off logging and then turns it back on afterwards. This keeps the debug log password-free in most common cases, but there can be no guarantees. As noted elsewhere, the new "-dd" command-line option selects a timestamped debug log (equivalent to "set debug timestamps on", "log debug debug.log"). C-Kermit 8.0 also supports a new timestamped session log via "set session-log timestamped-text", "log session". There have been requests for other kinds of logs, for example a command log. These might be added at some point. One person wanted to be able to log commands with timestamps, but only commands issued at the prompt, not commands from files or macros, and also wanted a header line at the beginning showing the date, user, and host. This can be done as follows: .filename := \v(home)commands.log ; (for example) fopen /write \%c \m(filename) if success { fwrite /line \%c \v(date): User=\v(user) Host=\v(host) fclose \%c set debug timestamps on log debug {| grep "CMD(P)" >> \m(filename)} append } [ [591]Top ] [ [592]Contents ] [ [593]C-Kermit Home ] [ [594]Kermit Home ] __________________________________________________________________ C-Kermit 8.0 Update Notes / [595]The Kermit Project / Columbia University / 15 Dec 2003 - 13 Sep 2010 References 1. http://www.columbia.edu/ 2. mailto:kermit@columbia.edu 3. http://www.columbia.edu/kermit/index.html 4. http://www.columbia.edu/kermit/k95.html 5. http://www.columbia.edu/kermit/ckermit.html 6. http://www.columbia.edu/kermit/ckscripts.html 7. http://www.columbia.edu/kermit/current.html 8. http://www.columbia.edu/kermit/whatsnew.html 9. http://www.columbia.edu/kermit/faq.html 10. http://www.columbia.edu/kermit/support.html 11. http://www.columbia.edu/kermit/ckermit80.html#contents 12. http://www.columbia.edu/kermit/ckermit.html 13. http://www.columbia.edu/kermit/index.html 14. http://www.amazon.com/gp/product/1555581641?ie=UTF8&tag=aleidmoreldom-20&linkCode=as2&camp=1789&creative=9325&creativeASIN=1555581641 15. http://www.columbia.edu/kermit/ckermit80.html 16. mailto:kermit-support@columbia.edu 17. http://www.columbia.edu/kermit/ 18. http://www.kermit-project.org/ 19. http://www.columbia.nyc.ny.us/kermit/ 20. ftp://kermit.columbia.edu/kermit/f/COPYING.TXT 21. ftp://kermit.columbia.edu/kermit/f/ckcmai.c 22. http://www.columbia.edu/kermit/ckermit80.html#xv 23. http://www.columbia.edu/kermit/ck60manual.html 24. http://www.columbia.edu/kermit/ckermi70.html 25. ftp://kermit.columbia.edu/kermit/f/ckermit70.txt 26. http://www.columbia.edu/kermit/ckututor.html 27. ftp://kermit.columbia.edu/kermit/f/ckuker.nr 28. http://www.columbia.edu/kermit/security.htm 29. http://www.columbia.edu/kermit/telnet.htm 30. http://www.columbia.edu/kermit/ftpscripts.html 31. http://www.columbia.edu/kermit/ckcbwr.html 32. ftp://kermit.columbia.edu/kermit/f/ckcbwr.txt 33. http://www.columbia.edu/kermit/ckubwr.html 34. ftp://kermit.columbia.edu/kermit/f/ckubwr.txt 35. http://www.columbia.edu/kermit/ckvbwr.html 36. ftp://kermit.columbia.edu/kermit/f/ckvbwr.txt 37. http://www.columbia.edu/kermit/ckuins.html 38. ftp://kermit.columbia.edu/kermit/f/ckuins.txt 39. http://www.columbia.edu/kermit/ckvins.html 40. ftp://kermit.columbia.edu/kermit/f/ckvins.txt 41. http://www.columbia.edu/kermit/ckccfg.html 42. ftp://kermit.columbia.edu/kermit/f/ckccfg.txt 43. http://www.columbia.edu/kermit/ckcplm.html 44. ftp://kermit.columbia.edu/kermit/f/ckcplm.txt 45. http://www.columbia.edu/kermit/iksd.html 46. http://www.columbia.edu/kermit/skermit.html 47. http://www.columbia.edu/kermit/ckermit80.html#top 48. http://www.columbia.edu/kermit/ckermit.html 49. http://www.columbia.edu/kermit/index.html 50. http://www.columbia.edu/kermit/ckermit80.html#x0 51. http://www.columbia.edu/kermit/ckermit80.html#x1 52. http://www.columbia.edu/kermit/ckermit80.html#x2 53. http://www.columbia.edu/kermit/ckermit80.html#x2.1 54. http://www.columbia.edu/kermit/ckermit80.html#x2.2 55. http://www.columbia.edu/kermit/ckermit80.html#x2.2.1 56. http://www.columbia.edu/kermit/ckermit80.html#x2.2.2 57. http://www.columbia.edu/kermit/ckermit80.html#x2.2.3 58. http://www.columbia.edu/kermit/ckermit80.html#x2.2.4 59. http://www.columbia.edu/kermit/ckermit80.html#x2.2.5 60. http://www.columbia.edu/kermit/ckermit80.html#x2.2.6 61. http://www.columbia.edu/kermit/ckermit80.html#x3 62. http://www.columbia.edu/kermit/ckermit80.html#x3.1 63. http://www.columbia.edu/kermit/ckermit80.html#x3.1.1 64. http://www.columbia.edu/kermit/ckermit80.html#x3.1.2 65. http://www.columbia.edu/kermit/ckermit80.html#x3.1.3 66. http://www.columbia.edu/kermit/ckermit80.html#x3.1.4 67. http://www.columbia.edu/kermit/ckermit80.html#x3.2 68. http://www.columbia.edu/kermit/ckermit80.html#x3.3 69. http://www.columbia.edu/kermit/ckermit80.html#x3.4 70. http://www.columbia.edu/kermit/ckermit80.html#x3.5 71. http://www.columbia.edu/kermit/ckermit80.html#x3.5.1 72. http://www.columbia.edu/kermit/ckermit80.html#x3.5.2 73. http://www.columbia.edu/kermit/ckermit80.html#x3.5.3 74. http://www.columbia.edu/kermit/ckermit80.html#x3.6 75. http://www.columbia.edu/kermit/ckermit80.html#x3.6.1 76. http://www.columbia.edu/kermit/ckermit80.html#x3.6.2 77. http://www.columbia.edu/kermit/ckermit80.html#x3.6.3 78. http://www.columbia.edu/kermit/ckermit80.html#x3.7 79. http://www.columbia.edu/kermit/ckermit80.html#x3.7.1 80. http://www.columbia.edu/kermit/ckermit80.html#x3.7.2 81. http://www.columbia.edu/kermit/ckermit80.html#x3.8 82. http://www.columbia.edu/kermit/ckermit80.html#x3.9 83. http://www.columbia.edu/kermit/ckermit80.html#x3.10 84. http://www.columbia.edu/kermit/ckermit80.html#x3.10.1 85. http://www.columbia.edu/kermit/ckermit80.html#x3.10.2 86. http://www.columbia.edu/kermit/ckermit80.html#x3.10.3 87. http://www.columbia.edu/kermit/ckermit80.html#x3.11 88. http://www.columbia.edu/kermit/ckermit80.html#x4 89. http://www.columbia.edu/kermit/ckermit80.html#x5 90. http://www.columbia.edu/kermit/ckermit80.html#x6 91. http://www.columbia.edu/kermit/ckermit80.html#x6.1 92. http://www.columbia.edu/kermit/ckermit80.html#x6.2 93. http://www.columbia.edu/kermit/ckermit80.html#x6.3 94. http://www.columbia.edu/kermit/ckermit80.html#x6.4 95. http://www.columbia.edu/kermit/ckermit80.html#x6.5 96. http://www.columbia.edu/kermit/ckermit80.html#x6.6 97. http://www.columbia.edu/kermit/ckermit80.html#x7 98. http://www.columbia.edu/kermit/ckermit80.html#x8 99. http://www.columbia.edu/kermit/ckermit80.html#x8.1 100. http://www.columbia.edu/kermit/ckermit80.html#x8.2 101. http://www.columbia.edu/kermit/ckermit80.html#x8.3 102. http://www.columbia.edu/kermit/ckermit80.html#x8.4 103. http://www.columbia.edu/kermit/ckermit80.html#x8.5 104. http://www.columbia.edu/kermit/ckermit80.html#x8.6 105. http://www.columbia.edu/kermit/ckermit80.html#x8.7 106. http://www.columbia.edu/kermit/ckermit80.html#x8.8 107. http://www.columbia.edu/kermit/ckermit80.html#x8.9 108. http://www.columbia.edu/kermit/ckermit80.html#x8.10 109. http://www.columbia.edu/kermit/ckermit80.html#x8.11 110. http://www.columbia.edu/kermit/ckermit80.html#x8.12 111. http://www.columbia.edu/kermit/ckermit80.html#x8.13 112. http://www.columbia.edu/kermit/ckermit80.html#x8.14 113. http://www.columbia.edu/kermit/ckermit80.html#x9 114. http://www.columbia.edu/kermit/ckermit80.html#x9.1 115. http://www.columbia.edu/kermit/ckermit80.html#x9.2 116. http://www.columbia.edu/kermit/ckermit80.html#x9.3 117. http://www.columbia.edu/kermit/ckermit80.html#x9.4 118. http://www.columbia.edu/kermit/ckermit80.html#x9.5 119. http://www.columbia.edu/kermit/ckermit80.html#x9.6 120. http://www.columbia.edu/kermit/ckermit80.html#x9.7 121. http://www.columbia.edu/kermit/ckermit80.html#x9.8 122. http://www.columbia.edu/kermit/ckermit80.html#x9.9 123. http://www.columbia.edu/kermit/ckermit80.html#x9.10 124. http://www.columbia.edu/kermit/ckermit80.html#x9.11 125. http://www.columbia.edu/kermit/ckermit80.html#x10 126. http://www.columbia.edu/kermit/ckermit80.html#x11 127. http://www.columbia.edu/kermit/ckermit80.html#x12 128. http://www.columbia.edu/kermit/ckermit80.html#x13 129. http://www.columbia.edu/kermit/ckermit80.html#x14 130. http://www.columbia.edu/kermit/ckermit80.html#x15 131. http://www.columbia.edu/kermit/ckermit80.html#x16 132. http://www.columbia.edu/kermit/ckermit80.html#x17 133. http://www.columbia.edu/kermit/ckermit80.html#top 134. http://www.columbia.edu/kermit/ckermit.html 135. http://www.columbia.edu/kermit/index.html 136. http://www.columbia.edu/kermit/ckuins.html#x5 137. http://www.columbia.edu/kermit/ckuins.html 138. http://www.columbia.edu/kermit/ckermit80.html#x5 139. http://www.columbia.edu/kermit/ckermit80.html#x2.2 140. http://www.columbia.edu/kermit/ckermit80.html#contents 141. http://www.columbia.edu/kermit/ckermit80.html#x15 142. http://www.columbia.edu/kermit/ckermit80.html#x3.7 143. http://www.columbia.edu/kermit/ckermit80.html#ftpdates 144. http://www.columbia.edu/kermit/ckermit80.html#ftpcheck 145. http://www.columbia.edu/kermit/ckermit80.html#ftpnamelist 146. http://www.columbia.edu/kermit/ckermit80.html#srvrename 147. http://www.columbia.edu/kermit/ckermit80.html#ftpvdir 148. http://www.columbia.edu/kermit/ckermit80.html#setftptype 149. http://www.columbia.edu/kermit/ckermit80.html#x3.6 150. http://www.columbia.edu/kermit/ckermit80.html#x15 151. http://www.columbia.edu/kermit/ckermit80.html#x8.7 152. http://www.columbia.edu/kermit/ckermit80.html#x2.1 153. http://www.columbia.edu/kermit/ckermit80.html#x2.2 154. http://www.columbia.edu/kermit/ckermit80.html#x8.14 155. http://www.columbia.edu/kermit/ckermit80.html#x8.13 156. http://www.columbia.edu/kermit/ckermit80.html#x8.13 157. http://www.columbia.edu/kermit/ckututor.html 158. http://www.columbia.edu/kermit/ckuins.html 159. http://www.columbia.edu/kermit/skermit.html 160. http://www.columbia.edu/kermit/ckermit80.html#setlocus 161. http://www.columbia.edu/kermit/ckermit80.html#lcommands 162. http://www.columbia.edu/kermit/ckermit80.html#ftpuser 163. http://www.columbia.edu/kermit/ckermit80.html#showvar 164. http://www.columbia.edu/kermit/ckermit80.html#callerid 165. http://www.columbia.edu/kermit/ckermit80.html#x6.6 166. http://www.columbia.edu/kermit/ckermit80.html#x0 167. http://www.columbia.edu/kermit/ckermit80.html#x3.11 168. http://www.columbia.edu/kermit/ckermit80.html#top 169. http://www.columbia.edu/kermit/ckermit80.html#contents 170. http://www.columbia.edu/kermit/ckermit.html 171. http://www.columbia.edu/kermit/index.html 172. http://www.columbia.edu/kermit/ckermit80.html#x0 173. http://www.columbia.edu/kermit/ckermit80.html#top 174. http://www.columbia.edu/kermit/ckermit80.html#contents 175. http://www.columbia.edu/kermit/ckermit.html 176. http://www.columbia.edu/kermit/index.html 177. http://www.columbia.edu/kermit/k95.html 178. http://www.columbia.edu/kermit/sshclient.html 179. http://www.columbia.edu/kermit/skermit.html 180. http://www.columbia.edu/kermit/skermit.html 181. http://www.columbia.edu/kermit/sshclien.htm 182. http://www.columbia.edu/kermit/ckermit80.html#x3 183. ftp://ftp.isi.edu/in-notes/rfc1738.txt 184. http://www.columbia.edu/kermit/ckermit80.html#x2.2.2 185. http://www.columbia.edu/kermit/ckermit80.html#x2.2.1 186. ftp://ftp.isi.edu/in-notes/rfc2396.txt 187. ftp://ftp.isi.edu/in-notes/rfc2616.txt 188. http://www.columbia.edu/kermit/ckermit80.html#x2.2.3 189. ftp://ftp.isi.edu/in-notes/rfc2616.txt 190. http://www.columbia.edu/kermit/ckermit80.html#x8.13.7 191. http://www.columbia.edu/kermit/security.htm#x5.4 192. http://www.columbia.edu/kermit/security.htm#x15 193. http://www.columbia.edu/kermit/security.htm#x6.2 194. http://www.columbia.edu/kermit/security.html 195. http://www.columbia.edu/kermit/ckermit80.html#x16 196. http://www.columbia.edu/kermit/ckermit80.html#top 197. http://www.columbia.edu/kermit/ckermit80.html#contents 198. http://www.columbia.edu/kermit/ckermit.html 199. http://www.columbia.edu/kermit/index.html 200. http://www.columbia.edu/kermit/ckermit80.html#x3.1 201. http://www.columbia.edu/kermit/ckermit80.html#x3.2 202. http://www.columbia.edu/kermit/ckermit80.html#x3.3 203. http://www.columbia.edu/kermit/ckermit80.html#x3.4 204. http://www.columbia.edu/kermit/ckermit80.html#x3.5 205. http://www.columbia.edu/kermit/ckermit80.html#x3.6 206. http://www.columbia.edu/kermit/ckermit80.html#x3.7 207. http://www.columbia.edu/kermit/ckermit80.html#x3.8 208. http://www.columbia.edu/kermit/ckermit80.html#x3.9 209. http://www.columbia.edu/kermit/ckermit80.html#x3.10 210. http://www.columbia.edu/kermit/ckermit80.html#x3.11 211. http://www.columbia.edu/kermit/security.htm 212. http://www.columbia.edu/kermit/security.htm#servers 213. http://www.columbia.edu/kermit/ckcsets.html 214. http://www.columbia.edu/kermit/unicode.html 215. http://www.columbia.edu/kermit/ckermi70.htm#x1.5.4 216. http://www.columbia.edu/kermit/case10.html 217. http://www.columbia.edu/kermit/ckermit80.html#x4 218. http://www.columbia.edu/kermit/ckermit80.html#x3.11 219. http://www.columbia.edu/kermit/ftpscripts.html 220. http://www.columbia.edu/kermit/ckermit80.html#top 221. http://www.columbia.edu/kermit/ckermit80.html#ftp 222. http://www.columbia.edu/kermit/ftpclient.html 223. http://www.columbia.edu/kermit/ftpscripts.html 224. http://www.columbia.edu/kermit/ckermit.html 225. http://www.columbia.edu/kermit/index.html 226. http://www.columbia.edu/kermit/ckermit80.html#x3.1.1 227. http://www.columbia.edu/kermit/ckermit80.html#x3.1.3 228. http://www.columbia.edu/kermit/ckermit80.html#x3.1.4 229. http://www.columbia.edu/kermit/ckermit80.html#x3.1.3 230. http://www.columbia.edu/kermit/ckermit80.html#x3.1.3 231. http://www.columbia.edu/kermit/ckermit80.html#x3.2 232. http://www.columbia.edu/kermit/ckermit80.html#x3.5 233. http://www.columbia.edu/kermit/ckermit80.html#x3.6 234. http://www.columbia.edu/kermit/ftpscripts.html 235. http://www.columbia.edu/kermit/ckb2.htm 236. http://www.columbia.edu/kermit/ckermit80.html#ftpautolog 237. http://www.columbia.edu/kermit/ckermit80.html#ftpuser 238. http://www.columbia.edu/kermit/ckermit80.html#x3.8 239. http://www.columbia.edu/kermit/ckermit80.html#x3.8 240. http://www.columbia.edu/kermit/ckermit80.html#top 241. http://www.columbia.edu/kermit/ckermit80.html#ftp 242. http://www.columbia.edu/kermit/ckermit.html 243. http://www.columbia.edu/kermit/index.html 244. http://www.columbia.edu/kermit/ibm_ie.html 245. http://www.columbia.edu/kermit/ckermit80.html#x3.10 246. http://www.columbia.edu/kermit/ckermit80.html#top 247. http://www.columbia.edu/kermit/ckermit80.html#ftp 248. http://www.columbia.edu/kermit/ckermit.html 249. http://www.columbia.edu/kermit/index.html 250. http://www.columbia.edu/kermit/ck60manual.html 251. http://www.columbia.edu/kermit/ckermit70.html#x4.17 252. http://www.columbia.edu/kermit/ckermit70.html 253. http://www.columbia.edu/kermit/ckermit80.html#x3.6 254. http://www.columbia.edu/kermit/ckermit80.html#x3.11 255. http://www.columbia.edu/kermit/ckermit80.html#x3.1.4 256. http://www.columbia.edu/kermit/security.html 257. http://www.columbia.edu/kermit/ckermit80.html#x3.7 258. http://www.columbia.edu/kermit/ckermit80.html#x3.7 259. http://www.columbia.edu/kermit/ckermit80.html#x8.13.4 260. http://www.columbia.edu/kermit/ckermit80.html#permswitch 261. http://www.columbia.edu/kermit/ckermit80.html#ftpchmod 262. http://www.columbia.edu/kermit/ckermit80.html#x3.6.2 263. http://www.columbia.edu/kermit/ckermit80.html#x4 264. http://www.columbia.edu/kermit/ckermit80.html#top 265. http://www.columbia.edu/kermit/ckermit80.html#ftp 266. http://www.columbia.edu/kermit/ckermit.html 267. http://www.columbia.edu/kermit/index.html 268. http://www.columbia.edu/kermit/ckermit80.html#x7 269. http://www.columbia.edu/kermit/ckermit80.html#x3.8 270. http://www.columbia.edu/kermit/ckermit80.html#x3.8 271. http://www.columbia.edu/kermit/ckb2.htm 272. http://www.columbia.edu/kermit/ckermit80.html#x3.10 273. http://www.columbia.edu/kermit/ckermit80.html#x3.10 274. http://www.columbia.edu/kermit/ckermit80.html#x3.6 275. http://www.columbia.edu/kermit/ckermit80.html#setftptype 276. http://www.columbia.edu/kermit/ckermit80.html#top 277. http://www.columbia.edu/kermit/ckermit80.html#ftp 278. http://www.columbia.edu/kermit/ckermit.html 279. http://www.columbia.edu/kermit/index.html 280. http://www.columbia.edu/kermit/ckermit70.html#x4.9 281. http://www.columbia.edu/kermit/ckermit80.html#x3.5.1 282. http://www.columbia.edu/kermit/ckermit80.html#erroraction 283. http://www.columbia.edu/kermit/ckermit70.html#x1.5 284. http://www.columbia.edu/kermit/ckermit70.html#x4.7 285. http://www.columbia.edu/kermit/ckermit70.html#x1.6 286. http://www.columbia.edu/kermit/ckermit80.html#x8.13 287. http://www.columbia.edu/kermit/ckermi70.htm#x1.5.4 288. http://www.columbia.edu/kermit/ckermi70.htm 289. http://www.columbia.edu/kermit/ckermit80.html#x4 290. http://www.columbia.edu/kermit/ckermit80.html#x3.7 291. http://www.columbia.edu/kermit/ckermit80.html#x3.5.2 292. http://www.columbia.edu/kermit/ckermit80.html#x3.7 293. http://www.columbia.edu/kermit/ckermit80.html#erroraction 294. http://www.columbia.edu/kermit/ckermit80.html#x3.5.2 295. http://www.columbia.edu/kermit/ckermit80.html#erroraction 296. http://www.columbia.edu/kermit/ckermit80.html#ftpfilenames 297. http://www.columbia.edu/kermit/ckermit80.html#ftpperms 298. http://www.columbia.edu/kermit/ckermit80.html#ftpunique 299. http://www.columbia.edu/kermit/ckermit80.html#ftpfilenames 300. http://www.columbia.edu/kermit/ckermit80.html#note_utc 301. http://www.columbia.edu/kermit/ckermit80.html#note_date 302. http://www.columbia.edu/kermit/ckermit80.html#x3.6 303. http://www.boulder.nist.gov/timefreq/faq/faq.htm#10: 304. http://www.columbia.edu/kermit/ckermit80.html#x3.7 305. http://www.columbia.edu/kermit/ckermit80.html#top 306. http://www.columbia.edu/kermit/ckermit80.html#ftp 307. http://www.columbia.edu/kermit/ckermit.html 308. http://www.columbia.edu/kermit/index.html 309. http://www.columbia.edu/kermit/ckermit80.html#x3.11 310. http://www.columbia.edu/kermit/ckermi70.htm#x4.3 311. http://www.columbia.edu/kermit/ckermit70.html 312. http://www.columbia.edu/kermit/ckermit80.html#x5 313. http://www.columbia.edu/kermit/ckermit80.html#x3.6.3 314. http://www.columbia.edu/kermit/ckermit80.html#ftpfilenames 315. http://www.columbia.edu/kermit/ckermi70.htm#x4.1 316. http://www.columbia.edu/kermit/ckermi70.htm#x4.2.2 317. http://www.columbia.edu/kermit/ckermi70.htm#x1.5.4 318. http://www.columbia.edu/kermit/ckermit80.html#x3.6.2 319. http://www.columbia.edu/kermit/ckermit80.html#x3.11 320. http://www.columbia.edu/kermit/ckermit80.html#x3.11 321. http://www.columbia.edu/kermit/ckermit80.html#srvrename 322. http://www.columbia.edu/kermit/ckermi70.htm#x4.1 323. http://www.columbia.edu/kermit/ckermi70.htm 324. http://www.columbia.edu/kermit/ckb2.htm 325. http://www.columbia.edu/kermit/ckermit80.html#ftpfilenames 326. http://www.columbia.edu/kermit/ckermit80.html#x3.5.3 327. http://www.proftpd.net/ 328. http://www.columbia.edu/kermit/ckermit80.html#top 329. http://www.columbia.edu/kermit/ckermit80.html#ftp 330. http://www.columbia.edu/kermit/ckermit.html 331. http://www.columbia.edu/kermit/index.html 332. http://www.columbia.edu/kermit/ckb2.htm 333. http://www.columbia.edu/kermit/ckcsets.html 334. http://www.columbia.edu/kermit/unicode.html 335. http://www.columbia.edu/kermit/ckcsets.html 336. http://www.columbia.edu/kermit/ckcsets.html 337. http://www.columbia.edu/kermit/ckermit80.html#x4 338. http://www.columbia.edu/kermit/utf8.html 339. http://www.columbia.edu/kermit/ckcsets.html 340. http://www.columbia.edu/kermit/ckermit80.html#x4 341. ftp://ftp.isi.edu/in-notes/rfc2640.txt 342. http://www.columbia.edu/kermit/ckermit80.html#top 343. http://www.columbia.edu/kermit/ckermit80.html#ftp 344. http://www.columbia.edu/kermit/ckermit.html 345. http://www.columbia.edu/kermit/index.html 346. http://www.columbia.edu/kermit/ckermit80.html#top 347. http://www.columbia.edu/kermit/ckermit80.html#ftp 348. http://www.columbia.edu/kermit/ckermit.html 349. http://www.columbia.edu/kermit/index.html 350. http://www.columbia.edu/kermit/ckermit80.html#top 351. http://www.columbia.edu/kermit/ckermit80.html#ftp 352. http://www.columbia.edu/kermit/ckermit.html 353. http://www.columbia.edu/kermit/index.html 354. http://www.columbia.edu/kermit/ftpscripts.html 355. http://www.columbia.edu/kermit/ckermit80.html#x3.2 356. http://www.columbia.edu/kermit/ckermit80.html#x3.2 357. ftp://ftp.isi.edu/in-notes/rfc959.txt 358. http://www.columbia.edu/kermit/ckscripts.html 359. http://www.columbia.edu/kermit/ckermit80.html#top 360. http://www.columbia.edu/kermit/ckermit80.html#ftp 361. http://www.columbia.edu/kermit/ftpscript.html 362. http://www.columbia.edu/kermit/ckermit.html 363. http://www.columbia.edu/kermit/index.html 364. http://www.columbia.edu/kermit/ckermit80.html#x3.11.1 365. http://www.columbia.edu/kermit/ckermit80.html#x3.11.2 366. http://www.columbia.edu/kermit/ckermit80.html#x3.11.3 367. http://www.columbia.edu/kermit/ckermit80.html#x3.11.4 368. http://www.columbia.edu/kermit/ckermit80.html#x3.11.5 369. http://www.columbia.edu/kermit/ckermit.html 370. http://www.columbia.edu/kermit/k95.html 371. http://www.columbia.edu/kermit/ckermit80.html#x3.11.5 372. ftp://ftp.isi.edu/in-notes/rfc959.txt 373. ftp://ftp.isi.edu/in-notes/rfc2389.txt 374. http://www.ietf.org/internet-drafts/draft-ietf-ftpext-mlst-16.txt 375. http://www.columbia.edu/kermit/ftpclient.html 376. http://www.columbia.edu/kermit/ckermit80.html#top 377. http://www.columbia.edu/kermit/ckermit80.html#ftp 378. http://www.columbia.edu/kermit/ckermit.html 379. http://www.columbia.edu/kermit/index.html 380. http://www.columbia.edu/kermit/ckermit80.html#x3 381. http://www.columbia.edu/kermit/ckermit80.html#ucs2 382. http://www.columbia.edu/kermit/ckermit80.html#top 383. http://www.columbia.edu/kermit/ckermit80.html#contents 384. http://www.columbia.edu/kermit/ckermit.html 385. http://www.columbia.edu/kermit/index.html 386. http://www.columbia.edu/kermit/ckermit80.html#top 387. http://www.columbia.edu/kermit/ckermit80.html#contents 388. http://www.columbia.edu/kermit/ckermit.html 389. http://www.columbia.edu/kermit/index.html 390. http://www.columbia.edu/kermit/ckb2.htm 391. http://www.columbia.edu/kermit/ckermit80.html#top 392. http://www.columbia.edu/kermit/ckermit80.html#contents 393. http://www.columbia.edu/kermit/ckermit.html 394. http://www.columbia.edu/kermit/index.html 395. http://www.columbia.edu/kermit/ckermit80.html#x4 396. http://www.columbia.edu/kermit/ckermit80.html#x4 397. http://www.columbia.edu/kermit/ckermit80.html#x8.12 398. http://www.columbia.edu/kermit/ckermit80.html#x8.1 399. http://www.columbia.edu/kermit/ckermit80.html#x12 400. http://www.columbia.edu/kermit/ckermit80.html#x8.12 401. http://www.columbia.edu/kermit/ckermit80.html#top 402. http://www.columbia.edu/kermit/ckermit80.html#contents 403. http://www.columbia.edu/kermit/ckermit.html 404. http://www.columbia.edu/kermit/index.html 405. http://www.columbia.edu/kermit/ckermit80.html#x8.14 406. http://www.columbia.edu/kermit/ckermit80.html#top 407. http://www.columbia.edu/kermit/ckermit80.html#contents 408. http://www.columbia.edu/kermit/ckermit.html 409. http://www.columbia.edu/kermit/index.html 410. http://www.columbia.edu/kermit/ckermit80.html#x9 411. http://www.columbia.edu/kermit/ckermit80.html#top 412. http://www.columbia.edu/kermit/ckermit80.html#contents 413. http://www.columbia.edu/kermit/ckermit.html 414. http://www.columbia.edu/kermit/index.html 415. http://www.columbia.edu/kermit/ckermit80.html#x8.6 416. http://www.columbia.edu/kermit/ckermit80.html#top 417. http://www.columbia.edu/kermit/ckermit80.html#contents 418. http://www.columbia.edu/kermit/ckermit.html 419. http://www.columbia.edu/kermit/index.html 420. http://www.columbia.edu/kermit/ckermit80.html#top 421. http://www.columbia.edu/kermit/ckermit80.html#contents 422. http://www.columbia.edu/kermit/ckermit.html 423. http://www.columbia.edu/kermit/index.html 424. http://www.columbia.edu/kermit/ckermit80.html#top 425. http://www.columbia.edu/kermit/ckermit80.html#contents 426. http://www.columbia.edu/kermit/ckermit.html 427. http://www.columbia.edu/kermit/index.html 428. http://www.columbia.edu/kermit/ckermit80.html#fjoin 429. http://www.columbia.edu/kermit/ckermit80.html#fsplit 430. http://www.columbia.edu/kermit/ckermit80.html#x8.10 431. http://www.columbia.edu/kermit/ckermit80.html#top 432. http://www.columbia.edu/kermit/ckermit80.html#contents 433. http://www.columbia.edu/kermit/ckermit.html 434. http://www.columbia.edu/kermit/index.html 435. http://www.columbia.edu/kermit/ckermit80.html#x9 436. http://www.columbia.edu/kermit/ckermit80.html#x9 437. http://www.columbia.edu/kermit/ckermit80.html#x9 438. http://www.columbia.edu/kermit/ckermit80.html#x3 439. http://www.columbia.edu/kermit/ckermit80.html#x3 440. http://www.columbia.edu/kermit/ckermit80.html#x3.2 441. http://www.columbia.edu/kermit/ckermit80.html#x3.2 442. http://www.columbia.edu/kermit/ckermit80.html#x3.8 443. http://www.columbia.edu/kermit/ckermit80.html#x3 444. http://www.columbia.edu/kermit/ckermit80.html#x3 445. http://www.columbia.edu/kermit/ckermit80.html#x3 446. http://www.columbia.edu/kermit/ckermit80.html#x3.2 447. http://www.columbia.edu/kermit/ckermit80.html#x3 448. http://www.columbia.edu/kermit/ckermit80.html#x8.13 449. http://www.columbia.edu/kermit/ckermit80.html#x8.13 450. http://www.columbia.edu/kermit/ckermit80.html#x9 451. http://www.columbia.edu/kermit/ckermit80.html#x8.10 452. http://www.columbia.edu/kermit/ckermit80.html#x8.7.4 453. http://www.columbia.edu/kermit/ckermit80.html#top 454. http://www.columbia.edu/kermit/ckermit80.html#contents 455. http://www.columbia.edu/kermit/ckermit.html 456. http://www.columbia.edu/kermit/index.html 457. http://www.columbia.edu/kermit/ckermit80.html#top 458. http://www.columbia.edu/kermit/ckermit80.html#contents 459. http://www.columbia.edu/kermit/ckermit.html 460. http://www.columbia.edu/kermit/index.html 461. http://www.columbia.edu/kermit/ckermit80.html#top 462. http://www.columbia.edu/kermit/ckermit80.html#contents 463. http://www.columbia.edu/kermit/ckermit.html 464. http://www.columbia.edu/kermit/index.html 465. http://www.columbia.edu/kermit/ckermit80.html#x8.7 466. http://www.columbia.edu/kermit/ckermit80.html#top 467. http://www.columbia.edu/kermit/ckermit80.html#contents 468. http://www.columbia.edu/kermit/ckermit.html 469. http://www.columbia.edu/kermit/index.html 470. http://www.columbia.edu/kermit/ckermit80.html#scriptedit 471. http://www.columbia.edu/kermit/ckermit80.html#top 472. http://www.columbia.edu/kermit/ckermit80.html#contents 473. http://www.columbia.edu/kermit/ckermit.html 474. http://www.columbia.edu/kermit/index.html 475. http://www.columbia.edu/kermit/ckermit80.html#top 476. http://www.columbia.edu/kermit/ckermit80.html#contents 477. http://www.columbia.edu/kermit/ckermit.html 478. http://www.columbia.edu/kermit/index.html 479. ftp://ftp.isi.edu/in-notes/rfc2822.txt 480. ftp://ftp.isi.edu/in-notes/rfc2822.txt 481. ftp://ftp.isi.edu/in-notes/rfc2822.txt 482. ftp://ftp.isi.edu/in-notes/rfc2822.txt 483. http://www.columbia.edu/kermit/ckermit80.html#top 484. http://www.columbia.edu/kermit/ckermit80.html#contents 485. http://www.columbia.edu/kermit/ckermit.html 486. http://www.columbia.edu/kermit/index.html 487. http://www.columbia.edu/kermit/ckermit80.html#x8.1 488. http://www.columbia.edu/kermit/ckermit80.html#top 489. http://www.columbia.edu/kermit/ckermit80.html#contents 490. http://www.columbia.edu/kermit/ckermit.html 491. http://www.columbia.edu/kermit/index.html 492. http://www.columbia.edu/kermit/ckermit80.html#x8.2 493. http://www.columbia.edu/kermit/ckermit80.html#top 494. http://www.columbia.edu/kermit/ckermit80.html#contents 495. http://www.columbia.edu/kermit/ckermit.html 496. http://www.columbia.edu/kermit/index.html 497. http://www.columbia.edu/kermit/ckermit80.html#x9.8 498. http://www.columbia.edu/kermit/ckermit80.html#x9.8 499. http://www.columbia.edu/kermit/ckermit80.html#x8.2 500. http://www.columbia.edu/kermit/ckermit80.html#top 501. http://www.columbia.edu/kermit/ckermit80.html#contents 502. http://www.columbia.edu/kermit/ckermit.html 503. http://www.columbia.edu/kermit/index.html 504. http://www.columbia.edu/kermit/ckermit80.html#top 505. http://www.columbia.edu/kermit/ckermit80.html#contents 506. http://www.columbia.edu/kermit/ckermit.html 507. http://www.columbia.edu/kermit/index.html 508. http://www.columbia.edu/kermit/ckermit80.html#x9.8 509. http://www.columbia.edu/kermit/ckermit80.html#top 510. http://www.columbia.edu/kermit/ckermit80.html#contents 511. http://www.columbia.edu/kermit/ckermit.html 512. http://www.columbia.edu/kermit/index.html 513. http://www.columbia.edu/kermit/ckermit80.html#x9.8 514. http://www.columbia.edu/kermit/ckermit80.html#x9.8 515. http://www.columbia.edu/kermit/ckermit80.html#x9.6 516. http://www.columbia.edu/kermit/ckermit80.html#top 517. http://www.columbia.edu/kermit/ckermit80.html#contents 518. http://www.columbia.edu/kermit/ckermit.html 519. http://www.columbia.edu/kermit/index.html 520. http://www.columbia.edu/kermit/ckermit80.html#x9.8 521. http://www.columbia.edu/kermit/ckermit80.html#x9.8 522. http://www.columbia.edu/kermit/ckermit80.html#top 523. http://www.columbia.edu/kermit/ckermit80.html#contents 524. http://www.columbia.edu/kermit/ckermit.html 525. http://www.columbia.edu/kermit/index.html 526. http://www.columbia.edu/kermit/ckermit80.html#top 527. http://www.columbia.edu/kermit/ckermit80.html#contents 528. http://www.columbia.edu/kermit/ckermit.html 529. http://www.columbia.edu/kermit/index.html 530. http://www.columbia.edu/kermit/ckermit80.html#top 531. http://www.columbia.edu/kermit/ckermit80.html#contents 532. http://www.columbia.edu/kermit/ckermit.html 533. http://www.columbia.edu/kermit/index.html 534. mailto:thucdat@hotmail.com 535. http://www.columbia.edu/kermit/ckermit80.html#x9.9.2 536. http://www.columbia.edu/kermit/ckermit80.html#top 537. http://www.columbia.edu/kermit/ckermit80.html#contents 538. http://www.columbia.edu/kermit/ckermit.html 539. http://www.columbia.edu/kermit/index.html 540. http://www.columbia.edu/kermit/ckermit80.html#top 541. http://www.columbia.edu/kermit/ckermit80.html#contents 542. http://www.columbia.edu/kermit/ckermit.html 543. http://www.columbia.edu/kermit/index.html 544. http://www.columbia.edu/kermit/ckermit80.html#top 545. http://www.columbia.edu/kermit/ckermit80.html#contents 546. http://www.columbia.edu/kermit/ckermit.html 547. http://www.columbia.edu/kermit/index.html 548. http://www.columbia.edu/kermit/ckermit80.html#x9.2 549. http://www.columbia.edu/kermit/ckermit80.html#top 550. http://www.columbia.edu/kermit/ckermit80.html#contents 551. http://www.columbia.edu/kermit/ckermit.html 552. http://www.columbia.edu/kermit/index.html 553. http://www.columbia.edu/kermit/ckermit80.html#x4 554. http://www.columbia.edu/kermit/ckermit80.html#x4 555. http://www.columbia.edu/kermit/ckermit80.html#top 556. http://www.columbia.edu/kermit/ckermit80.html#contents 557. http://www.columbia.edu/kermit/ckermit.html 558. http://www.columbia.edu/kermit/index.html 559. http://www.columbia.edu/kermit/ckermit80.html#top 560. http://www.columbia.edu/kermit/ckermit80.html#contents 561. http://www.columbia.edu/kermit/ckermit.html 562. http://www.columbia.edu/kermit/index.html 563. http://www.columbia.edu/kermit/ckermit80.html#top 564. http://www.columbia.edu/kermit/ckermit80.html#contents 565. http://www.columbia.edu/kermit/ckermit.html 566. http://www.columbia.edu/kermit/index.html 567. http://www.columbia.edu/kermit/ckermit80.html#x4 568. http://www.columbia.edu/kermit/ckermit80.html#x3.7 569. http://www.columbia.edu/kermit/ckermit80.html#x3.7 570. http://www.columbia.edu/kermit/ckermit80.html#top 571. http://www.columbia.edu/kermit/ckermit80.html#contents 572. http://www.columbia.edu/kermit/ckermit.html 573. http://www.columbia.edu/kermit/index.html 574. ftp://ftp.isi.edu/in-notes/rfc2217.txt 575. http://www.columbia.edu/kermit/ckermit80.html#top 576. http://www.columbia.edu/kermit/ckermit80.html#contents 577. ftp://kermit.columbia.edu/kermit/sredird/ 578. http://www.columbia.edu/kermit/ckermit.html 579. http://www.columbia.edu/kermit/index.html 580. http://www.columbia.edu/kermit/ckermi70.htm#x4.22 581. http://www.columbia.edu/kermit/ckermit80.html#top 582. http://www.columbia.edu/kermit/ckermit80.html#contents 583. http://www.columbia.edu/kermit/ckermit.html 584. http://www.columbia.edu/kermit/index.html 585. http://www.columbia.edu/kermit/ckermit80.html#x3.1.3 586. http://www.columbia.edu/kermit/cuiksd.html 587. http://www.columbia.edu/kermit/ckermit80.html#top 588. http://www.columbia.edu/kermit/ckermit80.html#contents 589. http://www.columbia.edu/kermit/ckermit.html 590. http://www.columbia.edu/kermit/index.html 591. http://www.columbia.edu/kermit/ckermit80.html#top 592. http://www.columbia.edu/kermit/ckermit80.html#contents 593. http://www.columbia.edu/kermit/ckermit.html 594. http://www.columbia.edu/kermit/index.html 595. http://www.columbia.edu/kermit/index.html ckermit90.txt0000664000015300000120000031543111624230556012046 0ustar fdcstaff [1]The Columbia Crown The Kermit Project | Columbia University 612 West 115th Street, New York NY 10025 USA o [2]kermit@columbia.edu ...since 1981 [3]Home [4]Kermit 95 [5]C-Kermit [6]Scripts [7]Current [8]New [9]FAQ [10]Support [11]CLICK HERE to read about some of these items. [12]Table of platforms [13]Book: Using C-Kermit [14]Download C-Kermit 9.0 C-Kermit 9.0 Update Notes Note: C-Kermit 9.0.301 contains a correction that applies only to Solaris 10 and 11. C-Kermit 9.0.302 contains corrections that apply only to FreeBSD 8 and 9. * [15]Large Files * [16]How to Test Large-File Transfer * [17]Arithmetic with Large Integers * [18]FORCE-3 Packet Protocol * [19]Variable Evaluation * [20]The RENAME Command You Always Wanted * [21]Other New Features * [22]Incompatibilities * [23]What's Not In C-Kermit 9.0 * [24]And a Loose End * [25]Demonstration: Secure POP mail fetcher * [26]Demonstration: HP Switch Configuration Backup * [27]Demonstration: HP iLO Blade Configuration * [28]Demonstration: IBM/Rolm/Siemens CBX Management * [29]Demonstration: CSV and TSV Files * [30]Demonstration Scripts for Webmasters This is the third supplement to [31]Using C-Kermit, Second Edition. I apologize for the scattered nature of the information and I hope I can organize it and gather it all into one place for easy and definitive reference some day. It's a big job so it depends on the demand. For the time being the definitive reference and introduction is the book (which is now available also in a [32]Kindle Edition), plus the [33]C-Kermit 7.0 update, [34]C-Kermit 8.0 update, and now this one. Plus tons of other web pages on this site, sample script programs, and so on. In version 6.0, C-Kermit was a pretty powerful and flexible communication program with scripting capabilities. By version 9.0, I'd like to think of it more as a scripting language with built-in communications. You can get an idea of the kinds of programs you can write in Kermit language [35]here. You can develop programs quickly because it's an interactive program, not a compiler. The scripting language is the command language. Kind of like the Unix shell but "somewhat" less cryptic, including concepts not only from C but from PL/I, Snobol, LISP, Bliss, and Smalltalk. The language itself is built upon the command language of the much-loved [36]DECSYSTEM-20 from the 1970s and 80s, the Clipper Ship of the Text Era. (Text is not a bad word. Those of us who can touch-type and who are proficient in text-based computing environments like Unix shell or VMS DCL are likely to be orders of magnitude more productive than users of GUIs.) Thanks to (at least) Jeff Altman, William Bader, Ian Beckwith, Nelson Beebe, Gerry Belanger, Joop Boonen, Rob Brown, Christian Corti, Alexey Dokuchaev, John Dunlap, Peter Eichhorn, Carl Friedberg, Terry Kennedy, Günter Knauf, Jason Lehr, Arthur Marsh, Lewis McCarthy, Gary Mills, Ed Ravin, Jonathan Reams, Mike Rechtman, Mark Sapiro, Steven Schweda (SMS), Kinjal Shah, Michael Sokolov, Andy Tanenbaum, Seth Theriault, Zach A. Thomas, Martin Vorländer, and Eric Weaver for assistance, and to Hewlett-Packard Company for support. - Frank da Cruz [37]fdc@columbia.edu, 30 June 2011 P.S. It occurred to me just before the end of the day that maybe I should back up the Kermit website on DVD, just in case. Using [38]Kermit 95 on the desktop over an SSH connection to the Unix file system where the website resides, I made a fresh directory on the PC, CD'd to it, and on Unix cd'd to the Website directory, and told C-Kermit 9.0 to: C-Kermit> send /recursive /dotfiles /nobackup * and it re-created the website directory tree in the PC directory, text files correctly converted to Windows format and binary files correctly left as-is. The /dotfiles switch means to include files such as .htaccess whose names start with a dot (period), and the /nobackup switch means to skip backup files created by EMACs (such as index.html.~243~). And then I did the same with the FTP sites, about 8GB in all. Watching the file-transfer display was kind of like having 30 years of my life flash before my eyes in a few minutes. Then I copied the two directories to DVD (the FTP site had to be split over 2 DVDs). The whole operation took under half an hour. The directory tree on the CD is directly usable in Windows, Unix, or any other operating system (unlike if I had transferred the files all in binary mode or all in text mode, or if I had made, say, a gzipped tar archive or a zip archive). I believe that, to this day, Kermit is the only software that can do this. If someday I have to upload from these DVDs to Unix, VMS, or any other operating system, it can be done exactly the same way, with any necessary conversions on text files done automatically, and binary files left intact, recursively through a whole very large directory tree. What's New in General Very briefly, the major items: * [39]Open Source license. * [40]64-bit file access and transfer and 64-bit integer arithmetic on most common platforms. * Support for recent releases of Linux, Mac OS X, *BSD, etc ([41]see table). * Support for newer OpenSSL releases up to and including 1.0.0d ([42]see table). * [43]Strengthened error checking for file transfer under extremely harsh conditions. * [44]Simplified semantics for variables used in scripts. * Super-handy [45]extensions to the RENAME command. * Other scripting improvements including support for reading and writing [46]CSV and TSV files. * [47]MIME character-set names are now recognized. * Improved logging and debugging (see demo [48]here). * Lots more described or listed below, and [49]here. Open Source License C-Kermit 9.0 has the [50]Revised 3-Clause BSD License, an open source license approved by OSI, the [51]Open Source Initiative. Large Files Kermit is, first and foremost, a file-transfer program. One might expect it to be able to transfer any kind of file, but that has been decreasingly the case as file sizes began to cross the 2 gigabyte threshold. The biggest change since C-Kermit 8.0.211 is support for large files on platforms that support them. A "large file" is one whose size is greater than 2^31-1 (2,147,483,647) bytes (2GB-1); that is, one whose size requires more than 31 bits to represent. Before now, Kermit was able to access such files only on 100% 64-bit platforms such as Digital Unix, later known as Tru64 Unix. In the new release, Kermit takes advantage of the X/Open Single UNIX Specification Version 2 (UNIX 98) Large File Support (LFS) specification, which allows 32-bit platforms to create, access, and manage files larger than 2GB. Accommodating large files required code changes in many modules, affecting not only file transfer, but also file management functions from directory listings to local file manipulation, plus the user interface itself to allow entry and display of large numbers. All this had to be done in a way that would not affect pure 32-bit builds on platforms that do not support large files. Large file support is summarized in the [52]Table of Platforms; entries in Yellow (32-bit builds that support 64-bit integers) and Green (64-bit builds) support large files. Note that VMS C-Kermit and Kermit 95 for Windows have always been able to transfer large files. However their user interface used 32-bit integers for statistics and the file transfer display. In C-Kermit 9.0 Alpha.03, VMS C-Kermit on 64-bit platforms (Alpha and Itanium) should now give correct statistics and progress displays. (We'll see about Kermit 95 later.) How to Test Large-File Transfer Several methods are available for testing large-file transfers: * By transferring a real file that is more than 2147483648 bytes long (a file whose length requires more than 31 bits to express); or to be totally sure, that is longer than 4294967296 bytes (32 bits or more). Or to be double super sure, longer than 8589934592 (33 bits). * If you don't have such a file or there is not sufficient disk space for such a file, you can create a special kind of file that takes up one block on the disk but appears to be 4.3GB long by compiling and running [53]THIS C PROGRAM on Linux, Solaris, HP-UX, or other Unix platform that supports large files. Kermit or FTP or any other file transfer program will transfer the result (BIGFILE) in such a way as to actually put 4.3GB (or other desired size; see source) on the wire. * You can use Kermit's CALIBRATE feature to transfer a large file that doesn't exist. At the receiver, use RECEIVE /CALIBRATE. At the sender, use SEND /CALIBRATE:length, e.g.: (At remote kermit...) $ kermit -Y C-Kermit> receive /calibrate (Return to local kermit...) Ctrl-\c C-Kermit> send /calibrate:4300000000 This sends a simulated file 4.3GB in length, that does not exist on the sender and will not take up any disk space on the receiver. SEND /CALIBRATE: accepts big numbers only in Kermit versions that support them (this does not include Kermit 95 on Windows). This method tests only Kermit's ability to express and understand large file sizes, but does not test Kermit's file-system interface, since no files are involved. Arithmetic with Large Integers Because large file support requires the availability of a 64-bit signed integer data type, other aspects of C-Kermit were adapted to use it too, most notably Kermit's algebraic expression evaluator and its [54]S-Expression interpreter, on all platforms that support large files (those listed as 64 or 32/64 in the Word column of the [55]table). In fact, every Kermit command that parses a number in any field can now parse a large number on those platforms. S-Expressions can now be forced to operate with integers only, without floating-point conversion or having to explicitly truncate each result; as an example. see the revised [56]Easter date calculation script. FORCE-3 Packet Protocol The Kermit protocol has proven itself over the past 30 years to be robust in terms of surviving harsh transmission environments and delivering the data correctly and completely. In these times of Internet everywhere and error-correcting modems in the few places where the Internet isn't, few people even recall the kinds of difficult conditions that were common when the Kermit protocol was first developed: noisy telephone lines, serial interfaces that drop characters, lack of transparency to control or 8-bit characters, absence of flow control, "bare" modems without error correction. But the Internet is not everywhere, and not all modems are error-correcting. Perhaps the most difficult trial so far for Kermit or any other protocol is the [57]EM-APEX project, in which floats are dropped into the ocean from an aircraft into the path of a hurricane; these floats dive into the water measuring current, temperature, and salinity at different depths and then surface to phone home, sending the data to land stations using Kermit protocol over non-error-correcting 300bps [58]Iridium satellite modems, with high seas and winds battering the floats and heavy ([59]sometimes electrical) storms between the modem and the satellite. Because of the transmission speed and long distances involved, the transfers were very slow. The Kermit software in the floats is [60]Embedded Kermit, which did not implement sliding windows, which would have sped up the flow considerably. John Dunlap, engineer at the University of Washington's Applied Physics Laboratory, undertook the task of adding sliding windows to E-Kermit. For testing, he rigged up a [61]simulator in which Kermit transfers take place over a connection with different amounts of noise and delay. He found that occasionally, a transfer would appear to succeed, but the received file would be corrupt. According to the Kermit protocol definition, the first packet always has block-check type 1, a 6-bit checksum, which is the only block check type that all Kermit implementations are required to support; thus any Kermit partner can process this packet. This packet itself can negotiate a higher level of checking, such that subsequent packets have (say) block-check type 3, a 16-bit cyclic redundancy check (CRC) encoded as three printable 7-bit ASCII characters. The 16-bit CRC can catch all errors of certain kinds (single-bit, double-bit, bursts of 16 bits or less), and more than 99.9984741210937% of all other possible errors. John's simulations revealed that file corruption could occur undetected when the initial packet was corrupted in such a way that a parameter or capability byte was changed and the checksum also changed to make the packet appear to be correct, thus allowing the transfer to proceed with the two Kermit partners out of sync as to packet encoding and interpretation (the chances of two such errors producing a seemingly valid packet are about 1 in 6000 when using the 6-bit checksum). For example, the compression technique might be misnegotiated and then the receiver might store incoming data without decompressing it. The solution is a new option, selected by: BLOCK-CHECK TYPE 5 to require a type 3 block check (16-bit CRC) on every packet, including the initial ones, thus reducing the probability of a misnegotiation by many orders of magnitude. THIS PARAMETER CAN NOT BE NEGOTIATED. Each Kermit program must be given the "set block 5" command prior to transfer. That's because normally every Kermit program expects the first packet to have a 6-bit checksum, and if the first packet has a 3-byte, 16-bit CRC, the packet receiver will think it is corrupted. In practice, however, it is possible to code the packet receiver "cheat" by reading the packet data before verifying the block check. Thus when the receiver is C-Kermit 9.0 or later or E-Kermit 1.7 or later, it is only necessary to give the "set block 5" command to the file sender, and the receiver will check for a FORCE-3 first packet. If the receiver does not support this feature, however, the initial packet will be be rejected (after several retries) and the file transfer will not take place. There is no attempt to "back off" to normal behavior. CAPTION: Table 4. Kermit Protocol Packet Block Check Types Type Command Bytes Status Explanation 1 SET BLOCK 1 1 Required in all Kermit implementations. Negotiated. 6-bit checksum, suitable for good connections. 2 SET BLOCK 2 2 Optional, negotiated. 12-bit checksum. 64 times stronger than type 1. 3 SET BLOCK 3 3 Optional, negotiated. 16-bit CRC. BLANK-FREE-2 SET BLOCK 4 2 Optional, negotiated. 12-bit checksum, two nonblank bytes. FORCE-3 SET BLOCK 5 3 Optional, not negotiated. 16-bit CRC forced all packets. BLANK-FREE-2 is for environments where Kermit packets are treated as lines of text, and in which trailing blanks can be stripped; for example, when transferring files with an IBM mainframe through a 3270 protocol converter. [62]E-Kermit 1.7 Variable Evaluation Does the strange behavior of Kermit's \%x variables puzzle or annoy you? Kermit software development has been a collaborative project over the years, with contributions coming in from almost every country and every sector of the economy - academic, corporate, government. Thus not all versions, and not all features of a given version, are a product of systematic design. One example was the introduction of variables for text substitution, first in a version of MS-DOS Kermit that was sent in by someone somewhere (I could look it up, but no time...) Although the design of the notation for variable names (table below) is mine, the underlying code was contributed. In that code there was only one kind of variable, and if I recall correctly the variable name was a backslash followed by a single letter, for example \a, \b, etc. The contributed code evaluated these variables recursively, meaning if the definition of a variable contained variable references, then these were resolved when dereferencing the variable, and the process would continue as deep down as necessary to resolve the thing fully. This was sometimes handy, but it had one severe drawback: There was no way to use variables in a straightforward way to represent strings that contained literal backslashes; for example, DOS or Windows pathnames. This gave rise to all kinds of quoting rules and conventions (e.g. doubling backslashes or forcing single-level evaluation with \\fcontents()), and also to the introduction of other kinds of variables that were evaluated one level deep, rather than recursively. To accommodate coexistence of different kinds of variables as well as "escape sequences" for representing control and 8-bit characters, the syntax for variable names was extended to include three elements: the leading backslash, then a single character indicating the type of variable, and then the name of the variable in a format corresponding to the type designator, as shown in this somewhat simplified table: CAPTION: Table 1. Variable-name Syntax in Kermit Notation Meaning \000 - \255 8-bit character constant (decimal) \d000 - \d255 Alternative notation for 8-bit character (byte) constant (decimal) \o000 - \o377 8-bit character constant (octal) \x00 - \xff 8-bit character constant (hexadecimal) \%a - \%z Scalar variable, evaluated recursively. \%0 - \%9 Macro argument, scalar, evaluated recursively. \&a - \%& Array name \&a[x] Array reference, evaluated recursively (x is any constant or variable) \v(name) Built-in scalar variable, evaluated one level deep. \m(name) User-defined scalar variable, evaluated one level deep. \$(name) An environment variable, evaluated one level deep. \s(name[n:m]) Compact substring notation, evaluated one level deep. \fname(args...) Built-in function with zero or more arguments. \\ Literal backslash \N OUTPUT command only: NUL, ASCII 0 \B OUTPUT command only: BREAK (250ms, for serial connections) \L OUTPUT command only: Long BREAK (1.5sec, ditto) Variable names in Kermit are case-independent. The simplifications in the table are that the notation for decimal and octal bytes can have from one to three digits, and can include braces to separate them from text digits, e.g. \7, \{123}, \o{50}. Hex bytes too, except they must always have exactly two hex digits, 0-9a-f. Array indices must be, or must evaluate to, numbers (floating point numbers are truncated). Associative arrays are also available (dynamic arrays with arbitrary text as subscript), but they are really just a variation on \m() variables (read about associative arrays [63]here). Also, there are some alternative notations for compact substring notation. We didn't want to have lots of "distinguished" characters, as the UNIX shell does; one is enough, clarity over brevity. Although the notation can be a bit cumbersome, we can use the \m(name) form to circumvent the overevaluation in most contexts. But macro arguments are always assigned to the \%0-9 variables, and thus always evaluated recursively, making it difficult and confusing to pass (e.g.) Windows pathnames as arguments to macros. The same is true for array elements, especially in contexts where they are used to return results from built-in functions (for example, \fsplit() used to return the elements of a [64]comma-separated value list if any of the values contained backslashes). An even worse scenario is when macro arguments are passed from one macro to another; for some graphic illustrations see [65]Taming the Wild Backslash - Part Deux from the [66]C-Kermit 7.0 Update Notes. We can't just change how variables are evaluated because that would break existing scripts. But we can always add Yet Another SET Command: SET COMMAND VARIABLE-EVALUATION { RECURSIVE, SIMPLE } This applies only to \%a-z and \%0-9 variables and to \&a-z[] arrays (since all other kinds of variables are evaluated only one level deep). The default, of course, for backwards compatibility, is RECURSIVE. SIMPLE forces the evaluation of these variables to return their literal contents, without further evaluation: * An exception is made in the case of array subscripts, because changing how they are evaluated could break a lot of scripts, and anyway there should never be any harm in evaluating them recursively because their final value is always (or should be) numeric, not some string that might contain backslashes. * The VARIABLE-EVALUATION setting is on the command stack. Thus you can give this command in a macro, command file, or user-defined function without affecting the calling environment. * The new \frecurse() function forces recursive evaluation of its argument regardless of the VARIABLE-EVALUATION setting. The argument can be any string (or nothing at all); all the variables in the string, even \m() ones, are evaluated recursively: def \%a 1 \%b 3 def \%b 2 def xx easy as \%a show mac xx echo \frecurse(\m(xx)) easy as 1 2 3 echo \frecurse(it's as easy as \m(xx)) it's as easy as easy as 1 2 3 * The new \v(vareval) built-in variable contains the current setting (recursive or simple) at the current command-stack level. Here's a short script for illustration: define path c:\users\fdc\somefile.txt define test1 { # Normal recursive argument evaluation echo \%0: arg=\%1 } define test2 { # Simple argument evaluation set var simple echo \%0: arg=\%1 } test1 \m(path) test2 \m(path) exit And here's the result: ? test2: arg=c:\users\fdc\somefile.txt The first line might seem surprising, but under the normal rules (see table above) \f indicates a function call, with the letters following the 'f' being the name of the function. But there is no function by that name... and if there were, you probably didn't intend to call it! SET COMMAND VARIABLE-EVALUATION SIMPLE has no effect on constants, only on variables. Note how \m(path) is defined. The DEFINE command assigns the literal value of its argument to the named variable (see Table 3 below), thus in this case no special syntax is needed. But in other contexts, you must double the backslashes or use the \fliteral() function to use literal backslashes in data: test2 c:\\users\\fdc\\somefile.txt test2 \fliteral(c:\users\fdc\somefile.txt) C-Kermit 9.0 adds a new notation for \fliteral() which also has certain advantages over it: \q(string): test2 \q(c:\users\fdc\somefile.txt) Since \fliteral() is a function, its argument list (the text within parentheses) has special syntax of its own, in which commas and braces are treated specially and introduce another set of quoting problems. \q(string) doesn't have these problems. The only consideration is that parentheses must be balanced or else quoted (preceded by backslash), or represented as numeric character entities (left paren = \40, (right paren = \41). Or else hold the value in a simple variable as we did with \\m(path) above. SET COMMAND VARIABLE-EVALUATION SIMPLE is a big change and might have repercussions that didn't show up in the initial tests; a lot more testing is needed. On the topic of variables, let's summarize in one place the ways in which values can be explicitly assigned to variables. There is nothing new here except the table itself: CAPTION: Table 2. Variable Assignment in Kermit Command Shorthand Explanation DEFINE name value .name = value The literal value becomes the contents of the named variable; variables names in the value are copied without evaluation. This command is for defining macros that take parameters, as well as for defining simple variables, especially if the values contain backslashes. _DEFINE name value Like DEFINE but the name is evaluated before use. ASSIGN name value .name := value The value is evaluated and the result becomes the contents of the named variable. _ASSIGN name value Like ASSIGN but the name is evaluated before use. EVALUATE name expression .name ::= value The expression (in regular algebraic notation) is evaluated arithmetically and the result becomes the contents of the named variable. If the expression contains any variables they are evaluated first. _EVALUATE name expression Like EVALUATE but the name is evaluated before use. INCREMENT name expression Evaluates the variables in the expression, then evaluates the expression arithmetically, and then adds the value to the contents of the named variable, which must be a number or an algebraic expression. If the expression is empty, a value of 1 is used. _INCREMENT name expression Like INCREMENT but the name is evaluated before use. DECREMENT name expression Evaluates the variables in the expression, then evaluates the expression arithmetically, and then subtracts the value from the contents of the named variable, which must be a number or an algebraic expression. If the expression is empty, a value of 1 is used. _DECREMENT name expression Like DECREMENT but the name is evaluated before use. DECLARE name = list An array declaration can include an initializer list; items in the list are evaluated before assignment. This can be defeated by doubling any backslashes or enclosing individual arguments in \fliteral(). DO name arguments name arguments When invoking a macro with a DO command (or an implied one), the arguments are evaluated, then assigned to \%1, \%2, etc, and the macro's name to \%0. (SETQ name value) Kermit also includes a mini-[67]LISP interpreter Variables are evaluated automatically in Kermit commands simply by referencing them, according to rules given in Table 1. The following functions can be used to change how a a particular variable is evaluated: CAPTION: Table 3. Kermit Functions for Evaluating Variables Function Argument Description \fcontents() \%x or \&x[y] Evaluates the variable or array element (which normally would be evaluated recursively) one level deep. \fdefinition() name If the argument is a \%x variable or an array element, it is evaluated to get the name; otherwise the argument is the name. Its definition is returned with no recursion. \m() name Equivalent to \fdefinition(). \frecurse() \m(name) Forces recursive evaluation of a macro definition (a.k.a. long variable name). NOTE: \frecurse() can operate on any kind of variable as well as on any string containing any mixture of variables. C-Kermit's RENAME Command C-Kermit's RENAME command, which is used for changing the names of local files or for moving files locally, has two basic forms: RENAME [ optional-switches ] oldfilename newfilename This form lets you change the name of a single file from oldfilename to newfilename. Example: rename thismonth.log lastmonth.log RENAME [ optional-switches ] filespec directoryname This form lets you move (without renaming) one or more files (all the files that match the filespec, which may contain wildcard characters such as "*") to the given directory. Example: rename *.txt ~/textfiles/ Traditionally, the optional switches have been: RENAME /LIST oldname newname Display the old and new name for each file while renaming. Synonyms: /LOG, /VERBOSE. Example: rename /list *.txt ~/textfiles/ RENAME /NOLIST oldname newname Don't display the old and new name for each file while renaming. This is the default behavior. Synonyms: /NOLOG, /QUIET. Example: rename /nolist *.txt ~/textfiles/ Reminder: Every switch starts with a slash (/) and must be preceded by a space. New RENAME Features for C-Kermit 9.0 A series of new options (switches) have been added to let you change the names of multiple files at once by case conversion, string substitution, or character-set conversion, and optionally also move them to a different directory: /LOWER: Convert the filename to lowercase /UPPER: Convert the filename to uppercase /CONVERT: Change the filename's character encoding /REPLACE: Do string substitutions on the filename If the source-file specification includes a path or directory, any changes are applied to the filenames only, not to the directory or path specification. Since name changes, when applied to many files at once, can have consequences that are not easily undone, there are also some new controls, safeguards, and conveniences: RENAME /SIMULATE This switch tells Kermit to show you what the RENAME command would do without actually doing it. /SIMULATE implies /LIST. RENAME /COLLISION:{FAIL,SKIP,OVERWRITE} This switch governs Kermit's behavior when renaming multiple files, and any of the names would collide with the name of a file that already exists. The default, for compatibility with earlier releases of C-Kermit, is OVERWRITE, i.e. write over the existing file. The other two protect existing files. SKIP means to skip (not rename) the file that would cause the collision, and proceed to the next file, if any. FAIL means that no files will be renamed if there would be any collisions; for this Kermit makes two passes, checking each new name it constructs for existence before starting the second pass (however, there is no guarantee that in the second pass, it won't create the same new name for more than one file; in that case, it will stop before executing the second rename). Example: rename /simulate /collision:proceed * ~/tmp/ Reminder: In switches such as /COLLISION that take arguments (operands), the switch name and its argument(s) are separated by a colon (:) with no intervening spaces. Also remember that Kermit keywords can always be abbreviated by leaving off characters from the right, as long as the result is still unique in its context. Thus "ren /col:f" would be equivalent to "rename /collision:fail". You can change the following preferences for the RENAME command with the new SET RENAME command: SET RENAME LIST { ON, OFF } Tells the RENAME command whether to list its actions if you don't include a /LIST or /NOLIST or equivalent switch. SET RENAME COLLISION { FAIL, OVERWRITE, SKIP } Tells the RENAME command how to handle filename collisions in the absence of a /COLLISION switch. That is, it replaces the default action of OVERWRITE with action of your choosing, which is then used in any RENAME command that does not include an explicit /COLLISION switch. SHOW RENAME Displays the current SET RENAME settings. Changing the Case of Filenames RENAME /UPPER:{ALL,LOWER} filespec [ directory ] RENAME /LOWER:{ALL,UPPER} filespec [ directory ] These switches let you change the alphabetic case of letters in all the files whose names match the filespec. If a directory name is given after the filespec, then the files are also moved to the given directory. By default, all files that match the given filespec have their names changed (if necessary). This is what the ALL argument means, e.g.: RENAME /LOWER:ALL * RENAME /LOWER * You can use either form: RENAME /LOWER is equivalent to RENAME /LOWER:ALL. The other argument (/LOWER:UPPER or /UPPER:LOWER) means to leave mixed-case filenames alone, and rename only those files whose names contain letters of only the given case. Examples: RENAME /UPPER:ALL foo.bar Changes the filename to FOO.BAR. RENAME /UPPER foo.bar Same as "rename /upper:all foo.bar". RENAME /UPPER foo.bar ~/old/ Renames foo.bar to FOO.BAR and moves it to the user's old directory (Unix). RENAME /LOWER * Changes the names of all files to have only lowercase letters. RENAME /LOWER:UPPER * Changes the names of only those files whose names contain no lowercase letters to have only lowercase letters. For example, FOO.BAR would be changed, Foo.Bar would not be changed. foo.bar would not be changed either because it's already all lowercase. RENAME /LOWER:UPPER * ~/new/ Same as the previous example, but also moves each file to the user's new directory (whether it was renamed or not). Case conversion works reliably for ASCII characters only. Kermit uses the C library for this, which on any given platform might or might not handle non-ASCII letters, and if it does, then how it works would normally depend on your locale definitions (the LC_CTYPE and/or LANG environment variable in Unix). When non-ASCII letters are not handled by the C library, the RENAME command does change their case. For example, Olga_Tañón.txt might become OLGA_TAñóN.TXT. String Replacement in Filenames The RENAME command also lets you change filenames by string substitution. RENAME /FIXSPACES[:String] filespec [ directory ] Replaces all spaces in each matching filename by the given string, if any, or if none is given, by underscore. Examples: RENAME /FIX * RENAME /FIXSPACES:_ * RENAME /FIXSPACES:"" * RENAME /FIXSPACES:<040> * The first two are equivalent, replacing each space with underscore; a file called "My Favorite Photo.jpg" becomes "My_Favorite_Photo.jpg". The third example removes all spaces ("MyFavoritePhoto.jpg"). The fourth replaces each space with the string "<040>" ("My<040>Favorite<040>Photo.jpg"). RENAME /REPLACE:{{String1}{String2}} filespec [ directory ] Renames each matching file by changing occurrences of String1 in its name to String2. If a directory specification is included, the file is also moved to the given directory (even if the name was not changed). Note that in this case, the curly braces are part of the command. Example: RENAME /REPLACE:{{.jpeg}{.jpg}} * changes all *.jpeg files to *.jpg. By default, RENAME /REPLACE changes all occurrences of String1 in each filename to String2 so, for example, if you had a file called abcjpegxyz.jpeg, the command just shown would change its name to abcjpgxyz.jpg. For greater control and flexibility, the /REPLACE: switch argument can take several distinct forms: RENAME /REPLACE:String1 filespec [ directory ] This means to remove all occurrences of String1 from the given filenames name. It is equivalent to /REPLACE:{{String1}{}}. A handy use for this option is to remove spaces from filenames. RENAME /REPLACE:{{String1}{String2}} filespec [ directory ] As already noted, this replaces every occurrence of String1 with String2 in each filename. Alphabetic case in string matching is done according to the current SET CASE setting. RENAME /REPLACE:{{ }{_}} filespec [ directory ] This replaces all spaces in the given filenames with underscore, equivalent to RENAME /FIXSPACES. RENAME /REPLACE:{{String1}{String2}{Options}} filespec [ directory ] Options can be included that add more control to the process. The option string is a sequence of characters; each character in the string is an option. The choices are: A String matching is to be case-sensitive, regardless of SET CASE. a String matching is to be case-independent, regardless of SET CASE. ^ String replacement will occur only at the beginning of the filename. $ String replacement will occur only at the end of the filename. 1 Only the first occurrence of the string will be replaced. 2 Only the second occurrence of the string will be replaced. 3 4 5 6 7 8 ... 9 Only the ninth occurrence of the string will be replaced. - (hyphen, minus sign) Before a digit: occurrences will be counted from the right. ~ (tilde) Before digit or minus sign: all occurrences but the given one will be replaced. The tilde modifier works only with single-byte character sets such as ASCII, CP437, ISO 8859-1, etc, but not with multibyte character sets such as UCS2, UTF8, or any of the Japanese Kanji sets. Here are some examples showing how to use the /REPLACE options: RENAME /REPLACE:{{foo}{bar}{^}} * For all files whose names start with "foo", replaces the "foo" at the beginning with "bar". RENAME /REPLACE:{{}{New-}{^}} * Prepends "New-" to the name of each file. RENAME /REPLACE:{{.jpeg}{.jpg}{$}} * Replaces ".jpeg" at the end of each filename with ".jpg". RENAME /REPLACE:{{}{-Old}{$}} * Appends "-Old" to the name of each file. RENAME /REPLACE:{{foo}{bar}{a}} * Replaces "foo", "FOO", "Foo", "fOO", etc, with "bar" in each filename. RENAME /REPLACE:{{foo}{bar}{A}} * Replaces only (lowercase) "foo" in filenames with "bar". RENAME /REPLACE:{{a}{XX}} * Changes every "a" to "XX". For example a file called "a.a.a.a" would become "XX.XX.XX.XX". RENAME /REPLACE:{{a}{X}{2}} Changes only the second "a" to "X". For example a file called "a.a.a.a" would become "a.X.a.a". RENAME /REPLACE:{{a}{X}{-1}} Changes only the final "a" in the filename (it doesn't have to be at the end) to "X". For example a file called "a.b.a.c.a.d" would become "a.b.a.c.X.d". RENAME /REPLACE:{{foo}{NOTFOO}{-2}} Changes the second-to-last "foo" (if any) in the filename to "NOTFOO". RENAME /REPLACE:{{foo}{}{-2}} Deletes the second-to-last "foo" (if any) from the filename. RENAME /REPLACE:{{.}{_}{~1}} Changes all but the first period to an underscore; for example, "a.b.c.d.e" would become "a.b_c_d_e". RENAME /REPLACE:{{.}{_}{~-1}} Changes all but the final period to an underscore; for example, "a.b.c.d.e" would become "a_b_c_d.e". In the Options field, digits (and their modifiers), ^, and $ are mutually exclusive. If you include more than one of these in the option string, only the last one is used. Similarly for 'a' and 'A': RENAME /REPLACE:{{foo}{bar}{Aa2$^}} * This replaces "foo" with "bar" no matter what combination of upper and lower case letters are used in "foo" ('a' overrides 'A' in the option string), but only if "foo" is at the beginning of the filename ('^' overrides '$' and '2'). If you give an /UPPER or /LOWER switch and a /REPLACE switch in the same RENAME command, the /REPLACE action occurs first, then the case conversion: RENAME /REPLACE:{{foo}{bar}} /UPPER * /tmp For each file: changes all occurrences of "foo" in the name to "bar", then converts the result to uppercase, and then moves the file to the /tmp directory. So (for example) "foot.txt" would become "/tmp/BART.TXT". Changing the Character Encoding of Filenames As you know, text is represented on the computer as a series of numbers, with a given number corresponding to a given character according to some convention or standard. Filenames are represented the same way. The trouble is, different computers, or even different applications on the same computer, might use different standards or conventions ("character sets") for representing the same characters. Usually ASCII is safe, but anything beyond that -- non-ASCII characters such as accented or non-Roman letters -- is likely to vary. Sometimes you have text that's in the "wrong" character set and you need to convert it to something you can can use. Kermit has always been able to handle this as part of file transfer and terminal emulation, as well as being able to convert text files locally with its TRANSLATE command. Now there's a way to convert filenames too, for example after copying files from a CD that uses a different encoding: RENAME /CONVERT:charset1:charset2 filespec [ directory ] Converts filenames from the first character set to the second one. The two character sets can be chosen from the SET FILE CHARACTER-SET list; for complete details see [68]this page. For example suppose you have a file called "Olga_Tañón.txt" on a computer where ISO 8859-1 Latin Alphabet 1 is used, and you have transported it (e.g. on CDROM) to another computer where the text encoding is UTF8. Maybe you also have a lot of other files with similar names in the same directory. You can convert the filenames to UTF8 like this: RENAME /CONVERT:latin1:utf8 * /CONVERT can not be combined with /UPPER, /LOWER, or /REPLACE. You should NOT use UCS2 for filenames since this encoding is not compatible with C strings used in Unix and elsewhere. RENAME /CONVERT affects only the filename, not the file's contents. You can use the TRANSLATE command to convert the encoding of the contents of a text file. Other New Features See the [69]C-Kermit Daily Builds page for details. Very briefly: * Perhaps most important, modernized makefile targets for the major Unix platforms: Linux, Mac OS X, AIX, Solaris, etc. These are somewhat automated; not autoconf exactly, but they cut down significantly on redundant targets. For example, one single "linux" target works on many (hopefully all) different Linux configurations, where before different targets were required for different combinations of (e.g.) curses / ncurses / no curses; 32-bit / 64-bit; different feature sets and library locations. (Separate targets are still required for Kerberos and/or SSL builds, but they are "subroutinized".) * Bigger buffers, more storage for commands, macros, scripts, strings, and filename expansion in 64-bit versions and in 32-bit versions that support large files. * User-settable FTP timeout, works on both the data and control connection. * FTP access to ports higher than 16383. * Built-in FTP client for VMS. This is the [70]same FTP client Unix C-Kermit has had since version 8.0, minimally adapted to VMS by SMS, supporting binary and Stream_LF file transfer only (in other words, nothing to handle RMS files), but otherwise fully functional (and scriptable) and theoretically capable of making connections secured by SSL (at least it compiles and links OK with SSL - HP SSL 1.3 in this case). * Large file support in VMS, also by SMS. Alpha and Itanium only (not VAX). VMS C-Kermit was already able to transfer large files, but the file-transfer display (numbers and progress bar) and statistics were wrong because they used ints. In the present Alpha test release, this is an optional feature requested by including the "f" option in P1. * New PUTENV command that allows Kermit to pass environment variables to subprocesses (Unix only, "help putenv"). * New TOUCH command, many file selection options ("help touch"). * New DIRECTORY command options and switches (/TOP, /COUNT; HDIRECTORY, WDIRECTORY...). To see the ten biggest files in the current directory: "dir /top:10 /sort:size /reverse *" or equivalently, "hdir /top:10 *". WDIR lists files in reverse chronological order, shorthand for "dir /sort:date /reverse". * New command FSEEK /FIND:string-or-pattern, seeks to the first line in an FOPEN'd file that contains the given string or matches the given pattern. Example: Suppose you have a file of lines like this: quantity description... in which the first "word" is a number, followed by a description (for example, the name of an item). Here is how to use FSEEK to quickly get the total quantity of any given item, which is passed as a parameter (either a literal string or a pattern) on the command line: #!/usr/local/bin/kermit + if not def \%1 exit 1 Usage: \fbasename(\%0) string-or-pattern .filename = /usr/local/data/items.log # Substitute the actual filename set case off # Searches are case-independent fopen /read \%c \m(filename) # Open the file if fail exit 1 "\m(filename): \v(errstring)" # Fail: exit with error message .total = 0 # OK: Initialize the total echo Searching "\%1"... while true { fseek /line /relative /find:\%1 \%c 0 # Get next line that has target if fail break # Failure indicates EOF fread /line \%c line # Read it if fail break # (shouldn't happen) increment total \fword(\m(line),1) # Increment the total } fclose \%c # Close the file echo Total for "\%1" : \m(total) # Print the result exit 0 The syntax of the FSEEK command in this example indicates that each search should start relative to the current file line. Since Kermit is an interpretive language, FSEEK is a lot faster than FREAD'ing each line and checking it for the target, especially for big files. An especially handy use for FSEEK is for use with potentially huge sequentially timestamped logs, to seek directly to the date-time where you want to start processing. Some other improvements for the FOPEN/FREAD/FWRITE/FCLOSE family of commands are included also (performance, bug fixes, convenience features), listed in the [71]change log. (Prior to 9.0.299 Alpha.02, the FSEEK /FIND: command always started from the top.) * MIME synonyms for character-set names: A new equivalence between MIME names and Kermit names for character sets, with a new table showing the supported sets [72]HERE (this feature is also illustrated in the [73]Weblog script). * Unix C-Kermit SET TERMINAL TYPE now passes its arguments to subprocesses as an environment variable. * SET SESSION-LOG TEXT now strips out ANSI escape sequences from the session log. * For interacting with POP servers over clear-text or SSL-secured connections: + New SSL and TLS "raw" connections (no Telnet protocol). + New INPUT command options for reading and capturing (perhaps while scanning) continuous incoming text, such as INPUT /NOWRAP (explained [74]HERE). + New \femailaddress() command to extract the e-mail address from an Internet mail message To: or From: line, used in fetching mail from POP servers. + Improved date parsing commands and functions for parsing the different date formats that can appear in e-mail. + Production scripts for fetching mail from a secure POP server, available [75]HERE. * Various features added to make Kermit more useful for writing CGI scripts such as INPUT /COUNT:n to INPUT exactly n characters (useful for reading form data). * New \fpictureinfo() function for getting orientation and dimensions of JPG and GIF images, described [76]HERE. * New \fgetpidinfo() function for testing whether a given process exists. * \fkwdvalue() function fixed to allow multiword values. * New function \fcount(s1,s2) to tell the number of occurrences of s1 in s2. * New \flopx() function returns rightmost field from string (such as a file's extension). * New function \ffunction(s1) to tell whether a built-in s1 function exists. * New \fsqueeze(s1) function removes leading and trailing whitespace from string s1, changes tabs to spaces, squeezing each run of repeated whitespace characters to a single space. * Compact substring notation: \s(somestring[12:18]) is the same as \fsubstring(\m(somestring),12,18), i.e. the substring starting at position 12, 18 characters long. \s(somestring[12_18]) means characters 12 through 18 of the string (7 characters). Also, \s(somestring[17.]) returns character number 17 of somestring. * The string indexing functions now accept an optional trailing argument specifying the occurrence number of the target string. Likewise, \fword() can fetch words from the right as well as the left. * The COPY command in Unix C-Kermit has a new /PRESERVE switch, equivalent to Unix "cp -p". * ASKQ /ECHO:c can be used to make the characters the user types echo as the character c, e.g. asterisk when typing a password. * IF LINK filename to test if the filename is a symlink. * Ctrl-K, when typed at the command parser, replaces itself with most recently entered file specification. * In Unix, the ability to log a terminal session to a serial port, for use with speaking devices or serial printers; described [77]HERE. Also for the same purpose, SET SESSION-LOG NULL-PADDED-LINES for a speech synthesizer than needed this. * Adaptation to OpenSSL 0.9.8 and 1.0.0. * Lifted the restriction on having a remote Kermit program send REMOTE commands to the local. A very big ex-client needed to be able to do this (branches would connect to headquarters and upload files; HQ would then download patches, a REMOTE HOST command was necessary to allow the remote headquarters machines to install the patches on the local client; of course the client first has to ENABLE HOST because this is a risky scenario). The reason for the restriction was that the server, upon receiving any REMOTE command would send the results (output) back to the client as a file transfer with "destination screen", but of course the remote has no screen. * Added XMESSAGE, which is to [78]MESSAGE as XECHO is ECHO: it outputs a string with no line terminator DEBUG MESSAGE is ON. * Fixed \frecurse() to not dump core when invoked with no arguments. * Improved text for HELP FUNCTION SPLIT and HELP FUNCTION WORD. * Patches for Debian 6.0 "Squeeze" from Ian Beckwith. * \fcontents(\&a[3]) got an error if the array was declared but its dimension was less than 3. Now it simply returns and empty string. * \fsplit(), when parsing lines from CSV and TSV files, was treating backslash in the data the same way it treats backslash in Kermit commands. This was fixed to treat backslash like any other character. * Builds for Solaris 9 and later now use streams ptys rather then the old BSD-style ptys. Thanks to Gary Mills for this one, who noticed that he couldn't have more than 48 C-Kermit SSH sessions going at once and figured out why. * As noted [79]below DES encryption is being retired from many platforms and libraries that once used it. I changed the Solaris and Linux OpenSSL builds to account for this by testing for it. I probably should also add a OMITDES option to omit DES even if it is installed, but "KFLAGS=-UCK_DES" seems to do the job for now. * I changed the Linux build to test for the OpenSSL version (like the Solaris version already did), rather than assuming OpenSSL 0.9.7. * A couple minor changes for Tru64 Unix 5.1B from Steven Schweda but we still have some trouble on that platform. As a workaround "make osf1" can be used there. * Unix makefile and man page are now included in the Zip distribution. * \fjoin(), which is the inverse function of fsplit() now accepts CSV and TSV as a second argument, to transform an array into a comma-separated or tab-separated value list, as described [80]HERE. * Even in 2010, Unix distributions continue to change their UUCP lockfile conventions. C-Kermit 9.0 contains support from Joop Boonen for OpenSuSE >= 11.3 and recent Debian, which no longer have baudboy.h, which first appeared in Red Hat 7.2 in 2003. * From Lewis McCarthy: Based on code inspection, C-Kermit appears to have an SSL-related security vulnerability analogous to that identified as CVE-2009-3767 (see e.g. [81]http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2009-3767). I'm attaching a patch for this issue relative to the revision of ck_ssl.c obtained from a copy of [82]http://www.columbia.edu/kermit/ftp/test/tar/x.zip downloaded on 2010/07/30, which I believe is the latest. When this flaw was first widely publicized at last year's Black Hat conference, it was claimed that some public certificate authorities had indeed issued certificates that could be used to exploit this class of vulnerability. As far as I know they have not revealed specifically which public CA(s) had been found issuing such certificates. Some references: + [83]http://www.mseclab.com/?p=180 + [84]http://www.theregister.co.uk/2009/07/30/universal_ssl_cert ificate/ * Peter Eichhorn reported that "RENAME ../x ." didn't work; fixed now. * If only one file is FOPEN'd, FCLOSE given with no arguments would close it; this was a "convenience feature" that turned out to be dangerous. For safety FCLOSE has to require a specific channel number or the word ALL. * Added \fstrcmp(s1,s2,case,start,length), which has the advantage over IF EQU,LGT,LLT that case sensitivity can be specified as a function arg, and also substrings can be specified. * New built-in functions: \fcvtcsets(string,cs1,cs2) Function to convert a string from one character set to another. \fdecodehex(string[,prefix]) Function to decode a string containing hex escapes. \fstringtype(string) Function to tell whether a string is 7-bit, 8-bit, or UTF-8. For the motivation for these features and an application that uses them to analyze web logs, see the Weblog script below. * Lazy IF Conditions: Now you can do this: define foo some number if foo command instead of this: define foo some number if \m(foo) command Of course the old way still works too. But watch out because if the variable name is the same as a symbolic IF condition (for example COUNT), it won't do what you expected. (IF COUNT was used for loop control in early versions of MS-DOS Kermit, before it got real FOR and WHILE loops; it was added to C-Kermit for compatibility, and it can't be removed because that could break existing scripts). * Escape sequences are now stripped from text-mode session logs not only in CONNECT sessions but also in whatever is logged by the INPUT command; described in the [85]next section. * New commands for selectively issuing progress or debugging messages from scripts, also described in the next section. * Fix from [86]John Dunlap to prevent the fixed packet-timeout interval from going to an unexpected value. * Alpha.04 fixes a problem with FTP connections made from 64-bit Unix platforms. All the other changes in this section were to Alpha.03. * Relaunching a closed SSH connection with the CONNECT command is now possible, as it always has been with Telnet and other connection types; suggested by Peter Eichhorn (needs testing). * A symbol conflict fixed that prevented successful build on [87]FreeBSD 8.0. * Fixes from Christian Corti for building on SunOS 4.1. * New aixg target for building on AIX with gcc. * New aix+ibmssl target. This is nice because the IBM-supplied SSL libraries and header files are in a known location; no need to [88]set environment variables giving their locations. * "Large File Support" is now included by default on Alpha and IA64 hardware on VMS 7.3 and later, and it should work much better than before. * Kermit's internal FTP client is now included by default in any build that also includes TCP/IP networking. At present, the FTP client seems to work well for binary-mode transfers; text (ASCII) mode transfers still need some work. In builds that also include Secure Sockets Layer (SSL) security (next item) the FTP client should be able to make securely authenticated and encrypted connections. * In network builds that request OpenSSL support, e.g.: $ @ckvker "" "" "CK_SSL" the OpenSSL version is detected automatically and the appropriate compile-time options are emitted (such as OPENSSL_DISABLE_OLD_DES_SUPPORT). * Preliminary / limited support for the ODS-5 file system on VMS 7.2 and later, Alpha and Itanium only (needs testing): Filenames can be mixed case and can be longer. * Support for older and older VMS versions. * In the VMS build procedure, CKVKER.COM, the "i" option in P1 now means don't include the internal FTP client, and the "f" option means do not include "Large File" support. Large File support in VMS really only applies to the file-transfer display and statistics, which would go out of whack as soon as the byte count overflowed 31 bits because this is C-Kermit, built with the C compiler and the C library (runtime system), which did not support long integers until VMS 7.3. * The [89]LISP Operator ROUND now takes an optional second argument that specifies the number of places to round to, e.g. (ROUND dollars 2) rounds dollars to 2 decimal places. * Improved pattern matching in many commands for both strings and filenames. * Various minor new features, plus numerous bug fixes and speedups. Incompatibilities A top priority for new Kermit software releases has always been backwards compatibility. A script written for a previous Kermit release should run the same way in the new release. There's one exception this time. The [90]\fsplit() function is incredibly handy, it can do almost anything, up to and including parsing a LISP program (the underlying code is the basis of the [91]S-Expression interpreter). But did you ever try to use it to parse (say) a Tab-Separated-List (TSV file) or Comma-Separated-List (CSV)? It works as expected as long as the data contains only 7-bit characters. But if your data contains (say) Spanish or German or Russian text written in an 8-bit character set such as ISO 8859-1, every 8-bit character (any value 128-255) is treated as a break character. This is fixed in C-Kermit 9.0 by treating all 8-bit bytes as "include" characters rather than break characters, a total reversal of past behavior. I don't think it will affect anyone though, because if this had happened to anyone, I would have heard about it! Since most standard 8-bit character sets have control characters in positions 128-160, it might have made sense to keep 128-160 in the break set, but with the proliferation of Microsoft Windows code pages, there is no telling which 8-bit character is likely to be some kind of text, e.g. "smart quotes" or East European or Turkish accented letters. What's Not In C-Kermit 9.0 Some large projects that were contemplated have not been done, including: * IPv6. Honestly, there has been zero demand for this, and it would be a lot of work and disruption to the code base. Volunteers welcome, I guess. It could be a CS project. * A database interface - MySQL or ODBC. For this one, there is some demand but I haven't had a chance to even look into it. * There's a looming issue with DES encryption; major vendors are removing it from their platforms, starting with Apple in Mac OS X 10.6, with Microsoft to follow suit. A secure version of Kermit can be built without DES, but in limited testing successful connections were spotty (e.g. with Kerberos 5). * Cleaning up the Unix makefile. It has 25 years' worth of targets in it. It is very likely safe to remove most of them, since (a) most old platforms have gone away by now, or have been upgraded, due to hacking vulnerabilities; (b) the market has consolidated considerably; and (c) most of the new features of C-Kermit 9.0, such as large files, won't be of any use on older platforms and previous C-Kermit versions will remain available. * Packages. Everybody wants an install package custom made for their own computer, Linux RPMs being the prime example but far from the only one. These will come, I suppose (especially with some Linux sites having a policy against installing any application that does not come as an RPM). In the meantime, here's a page that describes some Kermit-specific issues in package construction: [92]ckpackages.html. And a Loose End... Using External File-Transfer Protocols on Secure Connections After C-Kermit 8.0.212 Dev.27 (2006/12/22), I spent a big chunk of time trying to solve a particular problem that some of you have complained about and others might be familiar with: If you use C-Kermit to make a secure Telnet connection to another host (e.g. with Telnet SSL/TLS, Kerberos, or SRP) and then attempt to transfer a file using an external protocol such as Zmodem, it doesn't work. That's because as coded (through 8.0.211), C-Kermit simply starts the external protocol in a fork with its standard i/o redirected to the connection. This completely bypasses the encryption and decryption that is done by C-Kermit itself, and of course it doesn't work. The same thing occurs if you use the REDIRECT command. The routine that handles this is ttruncmd() in ckutio.c. In order to allow (say) Zmodem transfers on secure connections, it is necessary for C-Kermit to interpose itself between the external Zmodem program and the connection, decrypting the incoming stream before feeding it to Zmodem and encrypting Zmodem's output before sending out the connection. In principal, this is simple enough. We open a pseudoterminal pair ("master" and "slave") for Zmodem's i/o and we create a fork and start Zmodem in it; we read from the fork pty's standard output, encrypt, and send to the net; we read from the net, decrypt, and write to the fork pty's standard input. In practice, it's not so simple. First of all, pseudoterminals (ptys) don't seem to interface correctly with certain crucial APIs, at least not in the OS's I have tried (Mac OS X, Linux, NetBSD, etc), such as select(). And i/o with the pty often - perhaps always - fails to indicate errors when they occur; for example, when the fork has exited. But, even after coding around the apparent uselessness of select() for multiplexing pty and net, and using various tricks to detect when the external protocol exits and what its exit status is, I'm still left with a show-stopping problem: I just simply can not download (receive) a file with Zmodem, which is the main thing that people would probably want to do. I can send files just fine, but not receive. The incoming stream is delivered to Zmodem (to the pty slave) but upon arrival at the Zmodem process itself, pieces are always missing and/or corrupt. Yet I can receive files just fine if I use Kermit itself (C-Kermit or G-Kermit) as the external protocol, rather than Zmodem. I can think of two reasons why this might be the case: 1. Zmodem sends all 8-bit bytes and control codes in the clear, and maybe the pty is choking on them because it thinks it is a real terminal. But Zmodem puts its controlling terminal into raw mode. And C-Kermit puts the pty into raw mode too, just for good measure. If any 0xFF codes are in the Zmodem data stream, and it's a Telnet session, Kermit does any needed byte stuffing/unstuffing automatically. Anyway, if I tell Zmodem to prefix everything, it makes no difference. 2. Zmodem is a streaming protocol and perhaps the pty driver can't keep up with a sustained stream of input at network speeds. What would be the method of flow control? I can vary the size of the i/o buffers used for writing to the pty, and get different effects, but I am not able to get a clean download, no matter what buffer size I use. write()'ing to the pty does not return an error, and I can't see the errors because they happen on the master side. It's as if the path between the pty slave and master lacks flow control; I deliver a valid data stream to the pty slave and the master gets bits and pieces. This impression is bolstered somewhat by the "[93]man 7 pty" page in HP-UX, which talks about some special modes for ptys that turn off all termio processing and guarantee a flow-controlled reliable stream of bytes in both directions - a feature that seems to be specific to HP-UX, and exactly the one we need everywhere. Well, in Pass One I used C-Kermit's existing pty routines from ckupty.[ch], which are well-proven in terms of portability and of actually working. They are currently used by SET HOST /PTY for making terminal connections to external processes. But these routines are written on the assumption that the pty is to be accessed interactively, and maybe they are setting the fork/pty arrangement up in such a way that that's not suitable for file transfer. The Pass One routine is called xttptycmd() in ckutio.c. So in Pass Two I made a second copy of the routine, yttptycmd(), that manages the pty and fork itself, so all the code is in one place and it's simple and understandable. But it still doesn't work for Zmodem downloads. In this routine, I use openpty() to get the pty pair, which is not portable, so I can have access to both the master and slave pty file descriptors. This version can be used only a platforms that have openpty(): Linux, Mac OS X, NetBSD, etc. In Pass Three, zttptycmd(), I tried using pipes instead of ptys, in case ptys are simply not up to this task (but that can't be true because if I make a Telnet or SSH connection into a host, I can send files to it with Zmodem, and the remote Zmodem receiver is, indeed, running on a pty). But pipes didn't work either. In Pass Four, I extracted the relevant routines into a standalone program based on yttptycmd() (the openpty() version, for simplicity), which I tested on Mac OS X, the idea being to rule out any "environmental" effects of running inside the C-Kermit process. There was no difference -- Kermit transfers (with C-Kermit itself as the external protocol) worked; Zmodem transfers (neither sz or lsz) did not. Well, it's a much longer story. As the external protocol, I've tried rzsz, crzsz, and lrzsz. We know that some of these have quirks regarding standard i/o, etc, which is one of the reasons for using ptys in the first place, and i/o does work - just not reliably. Anyway, the 1100 lines or so of [94]ckc299.txt, starting just below where it says "--- Dev.27 ---" tell the full story. At this point I have to give up and move on; it might be more productive to let somebody else who has more experience with ptys take a look at it - if indeed anyone still cares about being able to do Zmodem transfers over secure Telnet connections. C-Kermit 9.0 contains the three new routines (and some auxiliary ones), but they are not compiled or called unless you build it specially: make targetname KFLAGS=-DXTTPTYCMD (builds with xttptycmd()) make targetname KFLAGS=-DYTTPTYCMD (builds with yttptycmd()) make targetname KFLAGS=-DZTTPTYCMD (builds with zttptycmd()) These are all in [95]ckutio.c. As noted, the second one works only for Linux, FreeBSD, NetBSD, and Mac OS X, because it uses non-POSIX, non-portable openpty(). If you want to try it on some other platform that has openpty(), you can build it like this: make targetname "KFLAGS=-DYTTPTYCMD -DHAVE_OPENPTY" (and let me know, so I can have HAVE_OPENPTY predefined for that platform too). The best strategy to get this working, I think, would be to concentrate on yttptycmd(), which is the simpler of the two pty-based routines. If it can be made to work, then we'll see if we can retrofit it to use the ckupty.c routines so it will be portable to non-BSD platforms. By the way, if you build with any of [XYZ]TTPTYCMD defined, then the selected routine will always be used in place of ttruncmd(). This is to allow testing on all kinds of connections, not just secure ones, in both local and remote mode. Once the thing works, if it ever does, I'll add the appropriate tests and/or commands. By default, in the initial test release, C-Kermit 9.0 uses ttruncmd() on serial connections and ttyptycmd() on network connections. Even when a network connection is not encrypted, Kermit still needs to handle the network protocol, e.g. the quoting of 0xff bytes on Telnet connections. Demonstration: Fetch Mail from POP Server Secured by SSL [96]pop.ksc is a fully elaborated production script for fetching one's mail from a POP3 server over a connection secured by SSL. For explanation and documentation, [97]CLICK HERE. [98]mailcheck is a wrapper for the pop.ksc script, which collects your password one time, and then checks for new mail every 5 minutes (or other selected interval) and calls pop.ksc to fetch it if there is any. Demonstration: HP Switch Configuration Backup A common use for Kermit software is to make automated backups of the configuration of network switches and routers, such as those made by Cisco or Hewlett-Packard (although [99]tftp can be used for this, it is not available in all such devices; Kermit, however, works with those that have tftp as well as those that don't). Typically a backup can be done by making a Telnet, SSH, or serial connection to the device with Kermit and giving a command such as "show config" at the command-line prompt of the device with Kermit's session log activated. The result is a list of the commands that were used to establish the current configuration, suitable for feeding back to the device's console (e.g. with C-Kermit's TRANSMIT command) to reestablish the same configuration or to duplicate it on another device. At an HP installation it was noted, however, that while the HP switches (various ProCurve models) produced the desired list of commands, they were interspersed with escape sequences for special effects, thus rendering the recorded sessions unsuitable for feeding back into the switches. C-Kermit 9.0 introduces a new feature to strip the offending sequences out of a session log, leaving just the text. The command SET SESSION-LOG TEXT activates this feature. In C-Kermit 9.0 Alpha.02 and earlier, escape sequence stripping occurred only while logging interactive (CONNECT) sessions; beginning with Alpha.03 it is done also for data that is read by INPUT commands and therefore works for scripts too. A sample HP Switch Configuration Backup script is [100]HERE, and its data file is [101]HERE. This script also illustrates some other new features of Alpha.03: MESSAGE text This lets you put debugging messages in your script that can be displayed or not, according to SET DEBUG MESSAGE (below). This way you don't have to change your script for debugging. Hint: In Unix, invoke the script like this: $ DEBUG=1 scriptname arg1 arg2... and then include the following command in your script: if defined \$(DEBUG) set debug message on XMESSAGE text Like MESSAGE but prints the text with no line terminator, so it can be continued by subsequent messages. SET DEBUG MESSAGE { ON, OFF, STDERR } ON means MESSAGE commands should print to standard output; OFF means they shouldn't print anything; STDERR means the messages should be printed to [102]stderr. DEBUG MESSAGE is OFF by default, i.e. unless you SET it to ON or STDERR. IF DEBUG command Executes the command if SET DEBUG MESSAGE is not OFF. The \v(lastcommand) variable This variable contains the previous command. You can use it in debugging and error message to show (for example) exactly what the command was that just failed, without having to make a copy of the command: set host somehost.somecompany.com if fail exit 1 "FATAL - \v(lastcommand)" which, if the SET HOST command fails, prints "FATAL - set host somehost.somecompany.com" and then exits with status 1 (which normally indicates failure). Demonstration: HP iLO Blade Configuration [103]THIS DOCUMENT describes a script in production use at Columbia University for configuring and deploying racks full of HP blade servers through their "integrated Lights Out" (iLO) management interface, bypassing the tedious and error-prone process of configuring the servers one by one through the vendor-provided point-and-click Web interface, which is ill-suited to configuring large numbers of blades. The script illustrates some of C-Kermit 9.0's new features; source code is available through the link. The code is apt to change from time to time as new requirements surface. Demonstration: IBM/Rolm/Siemens CBX Management [104]THIS DOCUMENT describes a suite of scripts (some in production, some in development) used to manage the Columbia campus 20,000-line main telephone switch, along with about 10 satellite switches at off-campus locations. These switches are 1980s technology*, their management consoles are serial ports. Access is via Telnet to reverse terminal servers. The scripts allow for interactive sessions as well as automatic production (and in some cases formatting) of different reports required by different groups at different intervals. These scripts replace a whole assortment of ad-hoc ProComm ASPECT scripts that were scattered all over the place, with passwords embedded. The new scripts are intended to be run from a centralized server where there is a single well-secured configuration file, and where they can be used on demand, or in cron jobs. They are modular so code duplication is minimal. __________________________ * Of course the University is deploying new technology but the but the old system will be used in parallel for some time to come. Demonstration: CSV and TSV Files Contents * [105]Reading a CSV or TSV Record and Converting it to an Array * [106]Using \fjoin() to create a Comma- or Tab-Separated Value List from an Array * [107]Using CSV or TSV Files Comma-Separated Value (CSV) format is commonly output by spreadsheets and databases when exporting data into plain-text files for import into other applications. Here are the details: Comma-Separated List Syntax 1. Each record is a series of fields. 2. Records are in whatever format is used by the underlying file system for lines of text. 3. Fields within records are separated by commas, with zero or more whitespace characters (space or tab) before and/or after the comma; such whitespace is considered part of the separator. 4. Fields with embedded commas must be enclosed in ASCII doublequote characters. 5. Fields with leading or trailing spaces must be enclosed in ASCII doublequotes. 6. Any field may be enclosed in ASCII doublequotes. 7. Fields with embedded doublequotes must be enclosed in doublequotes and each interior doublequote is doubled. Here is an example: aaa, bbb, has spaces,,"ddd,eee,fff", " has spaces ","Muhammad ""The Greatest"" A li" The first two are regular fields. The second is a field that has an embedded space but in which any leading or trailing spaces are to be ignored. The fourth is an empty field, but still a field. The fifth is a field that contains embedded commas. The sixth has leading and trailing spaces. The last field has embedded quotation marks. Prior to C-Kermit 9.0 Alpha.06, C-Kermit did not handle CSV files according to the specification above. Most seriously, there was no provision for a separator to be surrounded by whitespace that was to be considered part of the separator. Also there was no provision for quoting doublequotes inside of a quoted string. Reading a CSV record Now the \fsplit() function can handle any CSV-format string if you include the symbolic include set "CSV" as the 4th parameter. To illustrate, this program: def xx { echo [\fcontents(\%1)] .\%9 := \fsplit(\fcontents(\%1), &a, \44, CSV) for \%i 1 \%9 1 { echo "\flpad(\%i,3). [\&a[\%i]]" } echo "-----------" } xx {a,b,c} xx { a , b , c } xx { aaa,,ccc," with spaces ",zzz } xx { "1","2","3","","5" } xx { this is a single field } xx { this is one field, " and this is another " } xx { name,"Mohammad ""The Greatest"" Ali", age, 67 } xx { """field enclosed in doublequotes""" } exit gives the following results: [a,b,c] 1. [a] 2. [b] 3. [c] ----------- [ a , b , c ] 1. [a] 2. [b] 3. [c] ----------- [ aaa,,ccc," with spaces ",zzz ] 1. [aaa] 2. [] 3. [ccc] 4. [ with spaces ] 5. [zzz] ----------- [ "1","2","3","","5" ] 1. [1] 2. [2] 3. [3] 4. [] 5. [5] ----------- [ this is a single field ] 1. [this is a single field] ----------- [ this is one field, " and this is another " ] 1. [this is one field] 2. [ and this is another ] ----------- [ name,"Mohammad ""The Greatest"" Ali", age, 67 ] 1. [name] 2. [Mohammad "The Greatest" Ali] 3. [age] 4. [67] ----------- [ """field enclosed in doublequotes""" ] 1. ["field enclosed in doublequotes"] ----------- The separator \44 (comma) must still be specified as the break set (3rd \fsplit() parameter). When "CSV" is specified as the include set: * The Grouping Mask is automatically set to 1 (which specifies that the ASCII doublequote character (") is used for grouping; * The Separator Flag is automatically set to 1 so that adjacent field separators will not be collapsed; * All bytes (values 0 through 255) other than the break character are added to the include set; * Any leading whitespace is stripped from the first element unless it is enclosed in doublequotes; * Any trailing whitespace is trimmed from the end of the last element unless it is enclosed in doublequotes; * If the separator character has any spaces or tabs preceding it or following it, they are ignored and discarded; * The separator character is treated as an ordinary data character if it appears in a quoted field; * A sequence of two doublequote characters ("") within a quoted field is converted to a single doublequote. There is also a new TSV symbolic include set, which is like CSV except without the quoting rules or the stripping of whitespace around the separator because, by definition, TSV fields do not contain tabs. Of course you can specify any separator(s) you want with either the CSV, TSV, or ALL symbolic include sets. For example, if you have a TSV file in which you want the spaces around each Tab to be discarded, you can use: \fsplit(variable, &a, \9, CSV) \9 is Tab. The new symbolic include sets can also be used with \fword(), which is just like \fsplit() except that it retrieves the nth word from the argument string, rather than an array of all the words. In C-Kermit you can get information about these or any other functions with the HELP FUNCTION command, e.g.: C-Kermit> help func word Function \fword(s1,n1,s2,s3,n2,n3) - Extracts a word from a string. s1 = source string. n1 = word number (1-based) counting from left; if negative, from right. s2 = optional break set. s3 = optional include set (or ALL, CSV, or TSV). n2 = optional grouping mask. n3 = optional separator flag: 0 = collapse adjacent separators; 1 = don't collapse adjacent separators. \fword() returns the n1th "word" of the string s1, according to the criteria specified by the other parameters. The BREAK SET is the set of all characters that separate words. The default break set is all characters except ASCII letters and digits. ASCII (C0) control characters are treated as break characters by default, as are spacing and punctuation characters, brackets, and so on, and all 8-bit characters. The INCLUDE SET is the set of characters that are to be treated as parts of words even though they normally would be separators. The default include set is empty. Three special symbolic include sets are also allowed: ALL (meaning include all bytes that are not in the break set) CSV (special treatment for Comma-Separated-Value records) TSV (special treatment for Tab-Separated-Value records) For operating on 8-bit character sets, the include set should be ALL. If the GROUPING MASK is given and is nonzero, words can be grouped by quotes or brackets selected by the sum of the following: 1 = doublequotes: "a b c" 2 = braces: {a b c} 4 = apostrophes: 'a b c' 8 = parentheses: (a b c) 16 = square brackets: [a b c] 32 = angle brackets: Nesting is possible with {}()[]<> but not with quotes or apostrophes. Returns string: Word number n1, if there is one, otherwise an empty string. Also see: HELP FUNCTION SPLIT C-Kermit> Using \fjoin() to create Comma- or Tab-Separated Value Lists from Arrays In C-Kermit 9.0, \fsplit()'s inverse function, [108]\fjoin() received the capability of converting an array into a comma-separated or a tab-separated value list. Thus, given a CSV, if you split it into an array with \fsplit() and then join the array with \fjoin(), giving each function the new CSV parameter in the appropriate argument position, the result will be will be equivalent to the original, according to the CSV definition. It might not be identical, because if the result had extraneous spaces before or after the separating commas, these are discarded, but that does not affect the elements themselves. The new syntax for \fjoin() is: \fjoin(&a,CSV) Given the array \&a[] or any other valid array designator, joins its elements into a comma-separated list according to the [109]rules listed above. \fjoin(&a,TSV) Joins the elements of the given array into a tab-separated list, also described above. [110]Previous calling conventions for \fjoin() are undisturbed, including the ability to specify a portion of an array, rather than the whole array: declare \&a[] = 1 2 3 4 5 6 7 8 9 echo \fjoin(&a[3:7],CSV) 3,4,5,6,7 Using \fsplit() and \fjoin() it is now possible to convert a comma-separated value list into a tab-separated value list, and vice versa (which is not a simple matter of changing commas to tabs or vice versa). Applications for CSV Files Databases such as MS Access or MySQL can export tables or reports in CSV format, and then Kermit can read the resulting CSV file and do whatever you like with it; typically something that could not be done with the database query language itself (or that you didn't know how to do that way): create reports or datasets based on complex criteria or procedures, edit or modify some fields, etc, and then use \fjoin() to put each record back in CSV form so it can be reimported into a spreadsheet or database. Here is a simple example in which we purge all records of customers who have two or more unpaid bills. The file is sorted so that each license purchase record is followed by its annual maintenance payment records in chronological order. #!/usr/local/bin/kermit .filename = somefile.csv # Input file in CSV format fopen /read \%c \m(filename) # Open it if fail exit # Don't go on if open failed copy \m(filename) ./new # Make a copy of the file .oldserial = 00000000000 # Multiple records for each serial number .zeros = 0 # Unpaid bill counter while true { # Loop fread /line \%c line # Get a record if fail exit # End of file .n := \fsplit(\m(line),&a,\44,CSV) # Split the fields into an array if not equ "\m(oldserial)" "\&a[6]" { # Have new serial number? # Remove all records for previous serial number # if two or more bills were not paid... if > \m(zeros) 1 { grep /nomatch \m(oldserial) /output:./new2 ./new rename ./new2 ./new } .oldserial := \&a[6] # To detect next time serial number changes .zeros = 0 # Reset unpaid bill counter } if equ "\&a[5]" "$0.00" { # Element 5 is amount paid increment zeros # If it's zero, count it. } } fclose \%c Rewriting the file multiple times is inelegant, but this is a quick and dirty use-once-and-discard script, so elegance doesn't count. The example is interesting in that it purges certain records based on the contents of other records. Maybe there is a way to do this directly with SQL, but why use SQL when you can use Kermit? Here is the same task but this time no shelling out, and this time we do change and add some fields and then join the result back into a CSV record and write it out to a new file. The object is to create a record for each license that shows not only the date and purchase price of the license but also the date and amount of the last maintenance payment, and to add new fields for sorting by anniversary (month and day): #!usr/local/bin/kermit + cd ~/somedirectory # CD to appropriate directory if fail exit 1 # Make sure we did .filename := \%1 # Filename from command line if not def filename { # If none give usage message exit 1 "Usage: \%0: infile [ outfile ]" } fopen /read \%c \m(filename) # Open the input CSV file if fail exit # Make sure we did .output := \%2 # Output filename from command line if not def output { # Supply one if not given .output := New_\m(filename) } fopen /write \%o \m(output) # Open output file if fail exit # Check that we did .serial = 00000000000 # Initialize serial number .licenses = 0 # and license counter fread /line \%c line # First line is column labels if fail exit # Check fwrite /line \%o "\m(line),AMM_DD,AYYYY" # Write new labels line # Remaining lines are license purchases (K95B) followed by zero or more # maintenance invoices (K95BM) for each license. .datepaid = 00/00/0000 # Initialize last maint payment date .amtpaid = $0.00 # Initialize last maint payment amount set flag off # For remembering we're at end of file while not flag { # Loop to read all records fread /line \%c line # Read a record if fail set flag on # If EOF set flag for later .n := \fsplit(\m(line),&a,\44,CSV) # Break record into array if ( flag || equ "\&a[3]" "K95B" ) { # License or EOF if fail exit 1 "FAILED: \v(lastcommand)" if licenses { # If this is not the first license .\&x[5] := \m(amtpaid) # Substitute most recent amount paid .\&x[21] := \m(datepaid) # Substitute most recent date paid void \fsplit(\&x[18],&d,/) # Break up original (anniversary) date # and put mm_dd and yyyy in separate fields for sorting... fwrite /line \%o "\fjoin(&x,CSV),\flpad(\&d[1],2,0)_\flpad(\&d[2],2, 0),\&d[3]" if fail exit 1 WRITE # Check for error xecho . # Show progress as one dot per record } if flag break # We're at EOF so we're finished increment licenses # New license - count it array copy &a &x # Keep this record while reading next .serial := \&a[6] # Remember serial number .datepaid = 00/00/0000 # Initial maintenance payment date .amtpaid = $0.00 # and amount continue # and go back to read next record } if not eq "\m(serial)" "\&a[6]" { # Catch out-of-sequence record echo echo "SEQUENCE: \m(serial)..\&a[6]: \&a[7] [\&a[1]]" continue } if equ "\&a[5]" "" .\&a[5] = $0.00 # If amount is empty make it $0.00 if not equ "\&a[5]" "$0.00" { # If amount is not $0.00 .datepaid := \&a[21] # remember date paid .amtpaid := \&a[5] # and amount paid } } fclose ALL # Done - close all files and exit exit 0 Done. The result imports back into Excel, where it can be sorted, formatted, or otherwise manipulated as desired. Using CSV Files: Extending Kermit's Data Structures Now that we can parse a CSV record, what would we do with a CSV file - that is, a sequence of records? If we needed all the data available at once, we would want to load it into a matrix of (row,column) values. But Kermit doesn't have matrices. Or does it? Kermit has several built-in data types, but you can invent your own data types as needed using Kermit's macro feature: define variablename value For example: define alphabet abcdefghijklmnopqrstuvwxyz This defines a macro named alphabet and gives it the value abcdefghijklmnopqrstuvwxyz. A more convenient notation (added in C-Kermit 7.0, see [111]Table 2) for this is: .alphabet = abcdefghijklmnopqrstuvwxyz The two are exactly equivalent: they make a literal copy the "right hand side" as the value of the macro. Then you can refer to the macro anywhere in a Kermit command as "\m(macroname)": echo "Alphabet = \m(alphabet)" There is a second way to define a macro, which is like the first except that the right-hand side is evaluated first; that is, any variable references or function calls in the right-hand side are replaced by their values before the result is assigned to the macro. The command for this is ASSIGN rather than DEFINE: define alphabet abcdefghijklmnopqrstuvwxyz assign backwards \freverse(\m(alphabet)) echo "Alphabet backwards = \m(backwards)" which prints: Alphabet backwards = zyxwvutsrqponmlkjihgfedcba This kind of assignment can also be done like this: .alphabet = abcdefghijklmnopqrstuvwxyz .backwards := \freverse(\m(alphabet)) [112]Any command starting with a period is an assignment, and the operator (= or :=) tells what to do with the right-hand side before making the assignment. In both the DEFINE and ASSIGN commands, the variable name itself is taken literally. It is also possible, however, to have Kermit compute the variable name. This is done (as described in [113]Using C-Kermit, 2nd Ed., p.457), using parallel commands that start with underscore: _DEFINE and _ASSIGN (alias _DEF and _ASG). These are just like DEFINE and ASSIGN except they evaluate the variable name before making the assignment. For example: define \%a one _define \%a\%a\%a 111 would create a macro named ONEONEONE with a value of 111, and: define \%a one define number 111 _assign \%a\%a\%a \m(number) would create the same macro with the same value, but: define \%a one define number 111 _define \%a\%a\%a \m(number) would give the macro a value of "\m(number)". You can use the _ASSIGN command to create any kind of data structure you want; you can find some examples in the [114]Object-Oriented Programming section of the [115]Kermit Script Library. In the following program we use this capability to create a two-dimensional array, or matrix, to hold the all the elements of the CSV file, and then to display the matrix: fopen /read \%c data.csv # Open CSV file if fail exit 1 .\%r = 0 # Row .\%m = 0 # Maximum columns while true { fread /line \%c line # Read a record if fail break # End of file .\%n := \fsplit(\m(line),&a,\44,CSV) # Split record into items incr \%r # Count this row for \%i 1 \%n 1 { # Assign items to this row of matrix _asg a[\%r][\%i] \&a[\%i] } if > \%i \%m { .\%m := \%i } # Remember width of widest row } fclose \%c # Close CSV file decrement \%m # (because of how FOR loop works) echo MATRIX A ROWS: \%r COLUMNS: \%m # Show the matrix for \%i 1 \%r 1 { # Loop through rows for \%j 1 \%m 1 { # Loop through columns of each row xecho "\flpad(\m(a[\%i][\%j]),6)" } echo } exit 0 The matrix is called a and its elements are a[1][1], a[1][2], a[1][3], ... a[2][1], etc, and you can treat this data structure exactly like a two-dimensional array, in which you can refer to any element by its "X and Y coordinates". For example, if the CSV file contained numeric data you could compute row and column sums using simple FOR loops and Kermit's built-in one-dimensional array data type: declare \&r[\%r] # Make an array for the row sums declare \&c[\%m] # Make an array for the column sums for \%i 1 \%r 1 { # Loop through rows for \%j 1 \%m 1 { # Loop through columns of each row increment \&r[\%i] \m(a[\%i][\%j]) # Accumulate row sum increment \&c[\%j] \m(a[\%i][\%j]) # Accumulate column sum } } Note that the sum arrays don't have to be initialized to zero because Kermit's INCREMENT command treats empty definitions as zero. Demonstration Scripts for Webmasters These scripts all use new features of C-Kermit 9.0. [116]ksitemap A C-Kermit 9.0 script to build sitemap.xml for a website, complete with Google image extensions (this is the file used by webmasters to get their sites crawled and indexed optimally). [117]The Weblog Script Reads a web log, extracts the Google searches, normalizes the search strings, and prints the top 20 searches, along with their counts. [118]The Amazon Script Reads an Amazon Associate orders report and lists the products according to the number of orders for each, or the number of clicks on each. [119]Photoalbum Makes a website from a collection of JPG images. [120]Home [121]Kermit 95 [122]C-Kermit [123]Scripts [124]Current [125]New [126]FAQ [127]Support C-Kermit 9.0 / [128]The Kermit Project / [129]Columbia University / [130]kermit@columbia.edu / [131]validate References 1. http://www.columbia.edu/ 2. mailto:kermit@columbia.edu 3. http://www.columbia.edu/kermit/index.html 4. http://www.columbia.edu/kermit/k95.html 5. http://www.columbia.edu/kermit/ckermit.html 6. http://www.columbia.edu/kermit/ckscripts.html 7. http://www.columbia.edu/kermit/current.html 8. http://www.columbia.edu/kermit/whatsnew.html 9. http://www.columbia.edu/kermit/faq.html 10. http://www.columbia.edu/kermit/support.html 11. http://www.columbia.edu/cu/computinghistory/books/#menagerie 12. http://www.columbia.edu/kermit/ck90tables.html 13. http://www.amazon.com/gp/product/1555581641?ie=UTF8&tag=aleidmoreldom-20&linkCode=as2&camp=1789&creative=9325&creativeASIN=1555581641 14. http://www.columbia.edu/kermit/ckermit.html#download 15. http://www.columbia.edu/kermit/ckermit90.html#LargeFiles 16. http://www.columbia.edu/kermit/ckermit90.html#TestLargeFiles 17. http://www.columbia.edu/kermit/ckermit90.html#Bignums 18. http://www.columbia.edu/kermit/ckermit90.html#force3 19. http://www.columbia.edu/kermit/ckermit90.html#Vareval 20. http://www.columbia.edu/kermit/ckermit90.html#rename 21. http://www.columbia.edu/kermit/ckermit90.html#Other 22. http://www.columbia.edu/kermit/ckermit90.html#Incompatibilities 23. http://www.columbia.edu/kermit/ckermit90.html#NotIn9.0 24. http://www.columbia.edu/kermit/ckermit90.html#LooseEnd 25. http://www.columbia.edu/kermit/ckermit90.html#pop 26. http://www.columbia.edu/kermit/ckermit90.html#HPswitch 27. http://www.columbia.edu/kermit/ckermit90.html#iLO 28. http://www.columbia.edu/kermit/ckermit90.html#Rolm 29. http://www.columbia.edu/kermit/ckermit90.html#CSV 30. http://www.columbia.edu/kermit/ckermit90.html#Otherdemos 31. http://www.columbia.edu/kermit/ck60manual.html 32. http://www.amazon.com/gp/product/B002ACPF9M?ie=UTF8&tag=aleidmoreldom-20&linkCode=as2&camp=1789&creative=390957&creativeASIN=B002ACPF9M 33. http://www.columbia.edu/kermit/ckermit70.html 34. http://www.columbia.edu/kermit/ckermit80.html 35. http://www.columbia.edu/kermit/ckscripts.html 36. http://www.columbia.edu/cu/computinghistory/dec20.html 37. mailto:fdc@columbia.edu 38. http://www.columbia.edu/kermit/k95.html 39. http://www.columbia.edu/kermit/cu-bsd-license.html 40. http://www.columbia.edu/kermit/ckermit90.html#LargeFiles 41. http://www.columbia.edu/kermit/ck90tables.html 42. http://www.columbia.edu/kermit/ck90tables.html 43. http://www.columbia.edu/kermit/ckermit90.html#force3 44. http://www.columbia.edu/kermit/ckermit90.html#Vareval 45. http://www.columbia.edu/kermit/ckrename.html 46. http://www.columbia.edu/kermit/csv.html 47. http://www.columbia.edu/kermit/csetnames.html 48. http://www.columbia.edu/kermit/ckermit90.html#HPswitch 49. http://www.columbia.edu/kermit/ckdaily.html 50. http://www.columbia.edu/kermit/cu-bsd-license.html 51. http://www.opensource.org/ 52. http://kermit.columbia.edu/ck90tables.html#LF 53. ftp://kermit.columbia.edu/kermit/utils/bigfile.c 54. http://www.columbia.edu/kermit/ckermit80.html#x9 55. http://www.columbia.edu/kermit/ck90tables.html#LF 56. ftp://kermit.columbia.edu/kermit/scripts/ckermit/easter2 57. http://www.columbia.edu/kermit/em-apex.html 58. http://www.iridium.com/ 59. http://science1.nasa.gov/science-news/science-at-nasa/2006/09jan_electrichurricanes/ 60. http://www.columbia.edu/kermit/ek.html 61. ftp://kermit.columbia.edu/kermit/ek/simirid/ 62. http://www.columbia.edu/kermit/ek.html 63. http://www.columbia.edu/kermit/ckermit70.html#x7.10.10 64. http://www.columbia.edu/kermit/csv.html 65. http://www.columbia.edu/kermit/ckermit70.html#x1.11 66. http://www.columbia.edu/kermit/ckermit70.html 67. http://www.columbia.edu/kermit/ckermit80.html#x9 68. http://www.columbia.edu/kermit/csetnames.html 69. http://www.columbia.edu/kermit/ckdaily.html 70. http://www.columbia.edu/kermit/ftpclient.html 71. http://www.columbia.edu/kermit/ckdaily.html 72. http://www.columbia.edu/kermit/csetnames.html 73. http://www.columbia.edu/kermit/ckermit90.html#Otherdemos 74. http://www.columbia.edu/kermit/input_nowrap.html 75. http://www.columbia.edu/~fdc/mm/index.html 76. http://www.columbia.edu/kermit/photoalbum.html 77. http://www.columbia.edu/~fdc/kermit/logserial.html 78. http://www.columbia.edu/kermit/ckermit90.html#message 79. http://www.columbia.edu/kermit/ckermit90.html#NotIn9.0 80. http://www.columbia.edu/kermit/csv.html#join 81. http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2009-3767 82. http://www.columbia.edu/kermit/ftp/test/tar/x.zip 83. http://www.mseclab.com/?p=180 84. http://www.theregister.co.uk/2009/07/30/universal_ssl_certificate/ 85. http://www.columbia.edu/kermit/ckermit90.html#HPswitch 86. http://www.columbia.edu/kermit/em-apex.html 87. http://www.freebsd.org/releases/8.0R/announce.html 88. http://www.columbia.edu/kermit/security81.html#x4.2.3 89. http://www.columbia.edu/kermit/ckermit80.html#x9 90. http://www.columbia.edu/kermit/ckermit80.html#x8.7.2 91. http://www.columbia.edu/kermit/ckermit80.html#x9 92. http://www.columbia.edu/kermit/ckpackages.html 93. http://docs.hp.com/en/B9106-90013/pty.7.html 94. http://www.columbia.edu/kermit/test/text/ckc299.txt 95. http://www.columbia.edu/kermit/test/text/ckutio.c 96. http://www.columbia.edu/~fdc/mm/pop 97. http://www.columbia.edu/~fdc/mm/ 98. http://www.columbia.edu/~fdc/mm/mailcheck 99. http://en.wikipedia.org/wiki/Trivial_File_Transfer_Protocol 100. http://www.columbia.edu/kermit/ftp/scripts/ckermit/gethpconfig 101. http://www.columbia.edu/kermit/ftp/scripts/ckermit/TestSwitches.txt 102. http://en.wikipedia.org/wiki/Standard_streams 103. http://kermit.columbia.edu/cudocs/ilosetup.html 104. http://www.columbia.edu/kermit/cudocs/cbx.html 105. http://www.columbia.edu/kermit/ckermit90.html#record 106. http://www.columbia.edu/kermit/ckermit90.html#join 107. http://www.columbia.edu/kermit/ckermit90.html#file 108. http://www.columbia.edu/kermit/ckermit80.html#fjoin 109. http://www.columbia.edu/kermit/ckermit90.html#rules 110. http://www.columbia.edu/kermit/ckermit80.html#fjoin 111. http://www.columbia.edu/kermit/ckermit90.html#varasg 112. http://www.columbia.edu/kermit/ckermit70.html#x7.9 113. http://www.amazon.com/gp/product/1555581641?ie=UTF8&tag=aleidmoreldom-20&linkCode=as2&camp=1789&creative=9325&creativeASIN=1555581641 114. http://www.columbia.edu/kermit/ckscripts.html#oops 115. http://www.columbia.edu/kermit/ckscripts.html 116. http://www.columbia.edu/kermit/ksitemap.html 117. http://www.columbia.edu/kermit/weblog.html 118. http://kermit.columbia.edu/ftp/scripts/ckermit/amazon 119. http://www.columbia.edu/kermit/photoalbum.html 120. http://www.columbia.edu/kermit/index.html 121. http://www.columbia.edu/kermit/k95.html 122. http://www.columbia.edu/kermit/ckermit.html 123. http://www.columbia.edu/kermit/ckscripts.html 124. http://www.columbia.edu/kermit/current.html 125. http://www.columbia.edu/kermit/whatsnew.html 126. http://www.columbia.edu/kermit/faq.html 127. http://www.columbia.edu/kermit/support.html 128. http://www.columbia.edu/kermit/index.html 129. http://www.columbia.edu/ 130. mailto:kermit@columbia.edu 131. http://validator.w3.org/check?uri=http%3A%2F%2Fkermit.columbia.edu%2Fckermit90.html ckc302.txt0000644000015300001460000140626411624177351011433 0ustar fdckermitC-KERMIT 9.0 CHANGE LOG (Changes since 8.0.207 / K95 2.1.3 January 2003) Chronological order. Go to the bottom to find the newest edits. F. da Cruz, The Kermit Project, Columbia University, NYC. Last update: 28 June 2011. FTP USER, FTP ACCOUNT, plus the various prompts and switches for FTP username, password, and account all neglected to strip quotes, and in most cases quotes are necessary to specify a username that contains spaces. ckcftp.c, 15 Jan 2003. FTP MPUT f1 f2 f3... gets a parse error if any of the fn's do not match an existing file. This is bad for scripts. In doftpput(), cmfdb() looks for keywords (switches) or CMIFI. When it hits CMIFI, it exits from the initial parse loop and then does additional cmifi()s in a loop until done. The most obvious fix is to parse each field with cmfdb(CMIFI,CMFLD), i.e. fall back to CMFLD if CMIFI doesn't match anything. Then if CMFLD was used, we don't add the filespec to the list. This is a rather big change but it seems to work. No error messages or failures happen for non-matching fields, but an error message is printed (and the MPUT command fails) if none of the fields match any files. This fix got in too late for 2.1.3; workaround: use C-Shell like wildcard list (ftp mput "{*.abc,foo.*}"). ckcftp.c, 16 Jan 2003. GREP did not pass its pattern through the expander, thus variables could not be used for patterns. This must have been an oversight -- I can't find anything in my notes about it. Fixed in dogrep(): ckuus6.c, 24 Jan 2003. New makefile target for HP-UX 11.xx with OpenSSL from Tapani Tarvainen. makefile, 31 Jan 2003. From Jeff: . Avoid core dump when dereferencing tnc_get_signature(): ckuus4.c. . Bump version numbers to 8.0.208, 2.1.4: ckcmai.c. Added /NOLOGIN to FTP [OPEN]. ckcftp.c, 10 Feb 2003. Don't dump core if FTP DEBUG is ON and FTP OPEN does not include a service. openftp(): ckcftp.c, 10 Feb 2003. HELP PATTERN text incorrectly identified commands and functions with floating and anchored patterns. The corrected lists are: Floating: GREP, TYPE /MATCH:, /EXCEPT: patterns, \farraylook(), Anchored: IF MATCH, file-matching wildcards, \fsearch(), \frsearch() ckuus2.c, 10 Feb 2003. INPUT n \fpattern(xxx) did not work for case-independent comparisons. Fixed in doinput(): ckuus4.c, 10 Feb 2003. It seems \fpattern() didn't work with MINPUT at all. There was no code to handle \fpattern() in the MINPUT parse loop, so it never worked. The code had to be totally rewritten to use cmfld() in a loop, rather than cmtxt() and then cksplit(). Furthermore, whenever any of the fields was an \fjoin(), this had to be split. ckuusr.c, 10 Feb 2003. Macro replacement via \m() and \fdefinition() does not work as advertised (i.e. case sensitively) for associative array elements; e.g. \m(xxx) is treated the same as \m(xxx), contrary to section 7.10.10 of the C-Kermit 7.0 update notes, and to the fact that the two really do exist separately. Fixed by adding a static function isaarray(s) which succeeds if s is an associative array reference and fails otherwise, and then having \m() and \fdef() call mxxlook() (case-sensitive lookup) if isaarray(), otherwise (as before) mxlook()). ckuus4.c, 11 Feb 2003. Fixed FTP OPEN to allow the /USER switch to override SET FTP AUTOLOGIN OFF, just as /NOLOGIN overrides SET FTP AUTOLOGIN ON. ckcftp.c, 11 Feb 2003. In K95, "set key \1234 \27H" (any SET KEY command in which the first char of the definition was backslash, and the ONLY character after the backslash quantity was an uppercase letter, that letter would be lowercased). Diagnosis: xlookup() poking its argument (see notes from July 2000). Jeff sent a fix. ckucmd.c, 15 Feb 2003. Ran my S-Expression torture test to make sure Sexps still worked. They do, except the bitwise & and | operators were broken, e.g. (& 7 2) and (| 1 2 4) get "Invalid operand" errors. Jeff's code had added an early failure return from the lookup loop when when a single-byte keyword matched a keyword that started with the same byte but was more than one byte long. So "&" would hit "&&" and fail instead of continuing its search (xlookup tables aren't sorted so there can be no early return). Fixed in xlookup(): ckucmd.c, 16 Feb 2003. Got rid of "krbmit" target from makefile. It's still there, but we don't use it any more. All secure targets now use "xermit", and produce a binary called wermit, just like the regular ones do (except the old ckucon.c ones). Non-secure targets, since they don't define any of the security symbols, wind up compiling and linking to (mostly) empty security modules. makefile, 15 Feb 2003. Added \fcvtdate(xxx,3) to format its result in MDTM format (yyyymmddhhmmss, all numeric, no spaces or punctuation). Of course these numeric strings are too big to be 32-bit numbers and are useless for arithmetic, but they're useful for lexical comparison, etc. ckuus[24].c, 16 Feb 2003. The following FTP commands did not set FAILURE when they failed: RMDIR, CD, CDUP, Fixed in the corresponding doftpblah() routines. ckcftp.c, 16 Feb 2003. RENAME would sometimes not print an error message when it failed, e.g. in K95 when the destination file already existed. ckuus6.c, 17 Feb 2003. Fixed COPY error messages, which did not come out in standard format when /LIST was not included. ckuus6.c, 17 Feb 2003. Fixed #ifdefs in ck_crp.c to allow nonsecure builds on old platforms like System V/68 R3. 19 Feb 2003. Similar treatment for ck_ssl.c. 20 Feb 2003. From Jeff, 21 Feb 2003: . AIX53 and AIX52 symbols for ckcdeb.h, makefile. . New gcc targets for various AIX 4.x/5.x versions: makefile. . Copyright date updates: ck_crp.c, ck_ssl.c. . ENABLE/DISABLE QUERY broken because keyword table out of order: ckuusr.c. . Fixed the use of HTTP proxies for HTTP [RE]OPEN for Unix: ckcnet.c. Also for K95 only: Allow file transfer when K95 is invoked on the remote end of a connection to a Pragma Systems Terminal Server connection; automatically SET EXIT HANGUP OFF when invoked with open port handle ("k95 -l nnnn"). "cd a*" failed even when "a*" matched only one directory. Fixed in cmifi(): ckucmd.c, 21 Feb 2003. In the Unix version, replace "extern int errno;" with "#include " if __GLIBC__ is defined, since glibc now defines a thread-specific errno. ckcdeb.h, 26 Feb 2003. Added #ifdefs to skip compilation of ckuath.c in nonsecure builds. Tested by building both secure and regular versions in Linux. ckuath.c, 26 Feb 2003. Ran the build-in-84-different-configurations script on Linux to make sure it still builds with all different combinations of feature selection options. All OK. 26 Feb 2003. Built on VMS. Needed to add a prototype for mxxlook*() to ckuusr.h; built OK otherwise. 26 Feb 2003. From Jeff: More #ifdef shuffling for nonsecure builds: ckuath.c, ck_ssl.c, 27 Feb 2003. Added code to ensure \v(download) ends in a directory separator in Unix, Windows, and OS/2. ckuus7.c, 27 Feb 2003. Added code to K95 zfnqfp() to tack on directory separator when returning a directory name. ckofio.c, 27 Feb 2003. Somehow an old copy of ckuath.c popped to replace the new one. Put the new one back. 28 Feb 2003. From Jeff: Fix typo in my K95 zfnqfp() code from yesterday; fixes for handling UNCs uniformly, no matter which way their slashes are leaning. ckofio.c, 28 Feb 2003. At Jeff Mezei's suggestion, separate text and binary mode open sequences for VMS session log. ckvfio.c, 28 Feb 2003. Added freebsd48 target for FreeBSD 4.8. makefile, 1 Mar 2003. Changed Mac OS X entries to include -DUSE_STRERROR. makefile, 2 Mar 2003. Fixed GETOK /GUI to evaluate its text argument. ckuus6.c, 3 Mar 2003. Jeff fixed the K95 Dialer QUICK dialog to (a) allow templates, and (b) have a Save-As option. 3 Mar 2003. Jeff fixed a problem with the Xmodem-CRC checksum being crunched whenever there was a retransmission. 7 Mar 2003. Added target/banner for Tru64 5.1B. makefile, ckuver.h, 5 Mar 2003. In Unix, the zcopy() routine (used by the COPY command) reset the user's umask to 0 for the remainder of the Kermit process lifetime. The bug was in ckufio.c 8.0.194, 24 Oct 2002, and is fixed in ckufio.c 8.0.195, 6 Mar 2003. Of course this happened after building 155 C-Kermit 8.0.208 binaries. (But before officially releasing 8.0.208.) In the VMS version, changed: while ((n--) && xx_inc(2) > -1) ; to: while ((n--) && xx_inc(2) >= 0) ; to suppress the "...is being compared with a relational operator to a constant whose value is not greater than zero" warning. ckvtio.c, 7 Mar 2002. Added a debug call to dologend in hopes of catching overzealous Locus switching, which seems to happen only in K95. ckuus3.c, 7 Mar 2002. Rebuilt binaries for some of the more current Unix releases: AIX 4.3.3-5.1, Solaris 7-9 , Red Hat 7.0-8.0, Slackware 8.1, Freebsd 4.7-4.8, NetBSD 1.6, OpenBSD 3.2, Unixware 7.1.3, Open Unix 8, OSR5.0.6a, etc. A Unix binary with COPY umask fix shows a 6 Mar 2003 date for "UNIX File support" in SHOW VERSIONS; a binary without the fix shows 24 Oct 2002. C-Kermit 8.0.208 dated 14 March 2003 released on 10 March 2003. ---8.0.208--- From Jeff 13 Mar 2003: . Updated SSL module allows importation of tickets from host. . freebsd50+openssl target: makefile. . FTP PUT /PERMISSIONS error message for K95: ckcftp.c. Fixed MINPUT to strip quotes or braces from around targets (this was broken on Feb 10th). Thanks to Jason Heskett for discovering and reporting this (killer) bug. ckuusr.c, 14 Mar 2003. Changed version number to 209 Dev.00. ckcmai.c, 14 Mar 2003. While debugging the alphapage script, I found that the command "minput 8 \6\13 \21\13 \13\27\4\13 \30\13" gets "?Not confirmed" in 8.0.208 and 8.0.209, but not in 206 and earlier. This problem too was introduced on Feb 10th by changing MINPUT parsing from cmtxt() followed by cksplit() to cmfld() in a loop. cmfld() uses setatm() to return its result and of course setatm() breaks on \13. Changing setatm() not to do this would break everything else. But cmfld() has no arguments that let us tell it to do anything different in this case. Changing the API would be a disaster. The only solution is to add an "MINPUT ACTIVE" (minputactive) global variable that tells cmfld() to tell setatm() not to break on CR. Now MINPUT with braced targets containing CR and/or LF works in 209, 206, and 201 (but not 208). ckucmd.c, ckuusr.c, ckuus5.c, 15 Mar 2003. MINPUT n \fjoin(&a) works OK if all the members of \&a[] are text strings, but if they are strings of control chars (as above), they don't get separated by the spaces. For example in: dcl \&a[] = "\4\5" "\6\7" xxx minput 10 \fjoin(&a) MINPUT gets two targets: "aaa" and "\4\5 \6\7 xxx". The bug was in the cksplit() call in the \fjoin() case of MINPUT: it needed to specify an include set consisting of all the control characters except NUL. ckuusr.c, 16 Mar 2003. But there's still a problem: dcl \&a[] = "\4\5\13\10" "\6\7" "xxx" creates an array whose first member is "^D^E (one doublequote included). But if braces are used instead, there's no problem. Same deal as MINPUT: cmfld() breaks on CR or LF, thus the end quote is lost. If I set minputactive for DECLARE initializers too, that fixes it. Is there any reason not to do this? Can't think of any (famous last words)... ckuusr.c, 16 Mar 2003. Since it has multiple applications, changed the flag's name from minputactive to keepallchars. ckucmd.c, ckuus[r5].c, 16 Mar 2003. \v(exedir) wasn't being set correctly (it included the program name as well as the directory). Fixed in getexedir(): ckuus4.c, 16 Mar 2003. SET CARRIER-WATCH "auto matic" (spurious space in supplied keyword). Cosmetic only; it still worked. Fixed in setdcd(): ckuus3.c, 16 Mar 2003. "directory a b c" listed too many files -- all files whose names END WITH a, b, or c, rather than the files whose names WERE a, b, or c. Diagnosis: The filespec is changed into a pattern: {a,b,c}, which is the correct form. It is passed to nzxpand(), which goes through the directory getting filenames and sending each one to ckmatch() with the given pattern. ckmatch() receives the correct pattern but then prepends a "*" -- that's not right. It's not just in filename matching either. The following succeeds when it shouldn't: if match xxxxc {{a,b,c}} Changing ckmatch() to not prepend the "*" to each segment fixes the command above but breaks lots of others. Running through the "match" torture-test script shows the problem occurs only when the {a,b,c} list is the entire pattern, and not embedded within a larger pattern. Testing for this case fixed the problem. ckmatch(): ckclib.c, 16 Mar 2003. Fixed FTP MODTIME to not print anything if QUIET ON. ckcftp.c, 16 Mar 2003. Picked up a new ckuath.c from Jeff, not sure what the changes are. 16 Mar 2003. Did a few regular and secure builds to make sure I didn't wreck anything. Changed version number to 209 (final). ckcmai.c, 16 Mar 2003. Jason Heskett found another bug: if you define a macro FOO inside the definition of another macro BAR, and FOO's definition includes an odd number of doublequotes (such as 1), FOO's definition absorbs the rest of BAR's definition. Example: def TEST { .foo = {X"} sho mac foo } do test sho mac foo Results in: foo = {X"}, sho mac foo Diagnosis: the TEST definition becomes: def TEST .foo = {X"}, sho mac foo and the macro reader is erroneously treating the doublequote as an open quote, and then automatically closes the quote at the end of the definition. The error is that a doublequote should be significant only at the beginning of a field. But the macro reader isn't a command parser; it doesn't know what a field is -- it's just looking for commas and skipping over quoted ones. First we have to fix an oversight: SET COMMAND DOUBLEQUOTING OFF should have worked here, but it wasn't tested in this case. Fixed in getncm(): ckuus5.c, 17 Mar 2003. There are only certain cases where it makes sense to treat doublequotes as significant: . An open quote must be at the beginning or preceded by a space. . A close quote is only at the end or else followed by a space. This too was fixed in getncm(): ckuus5.c, 17 Mar 2003. A fix from Jeff SSL/TLS FTP data decoding. ckcftp.c, 18 Mar 2003. Tried building C-Kermit on a Cray Y-MP with UNICOS 9.0. "int suspend", declared in ckcmai.c and used in many modules, conflicts with: unistd.h:extern int suspend __((int _Category, int _Id)); The "=Dsuspend=xsuspend" trick doesn't work for this; there is no way around the conflict other than to rename the variable: ckcmai.c, ckutio.c, ckuus[35xy].c. 26 Mar 2003. VMS and K95 not affected. OK that gets us past ckcmai.c... Then in ckutio.c I had to add a new #ifdef around the LFDEVNO setting, because the Cray didn't have mkdev.h. Could not find a Cray-specific manifest symbol, so I made a new makefile target (cray9) that sets this symbol. Having done this I have no idea what kind of lockfile would be created, but I also doubt if anybody dials out from a Cray. The binary should run a C90, J90, or Y-MP. makefile, 26 Mar 2003. Added a target for SCO OSR5.0.7. makefile, ckuver.h, 30 Mar 2003. Changed since 208: makefile ckuver.h ckcmai.c ckclib.c ckcftp.c ckucmd.c ckuus*.c ckutio.c. ---8.0.209--- From Mark Sapiro, a fix for the March 17th doublequote fix, getncm(): ckuus5.c, 4 Apr 2003. From Jeff, 29 Apr 2003: . Corrected target for HP-UX 11.00 + OpenSSL: makefile, . Do not allow WILL AUTH before WONT START_TLS: ckctel.h ckctel.c . Add hooks for SFTP and SET/SHOW SFTP: ckcdeb.h ckuusr.h ckuusr.c ckuus3.c . Add SKERMIT ckuusr.h ckuusr.c . Add ADM-5 terminal emulation: ckuus7.c, ckuus5.c . Uncomment and update HELP SET SSH V2 AUTO-REKEY: ckuus2.c . Enable IF TERMINAL-MACRO and IF STARTED-FROM-DIALER for C-Kermit: ckuus6.c . Fix conflicting NOSCROLL keyword definition: ckuusr.h . Set ttname when I_AM_SSH: ckuusy.c . Add extended arg parsing for SSH, Rlogin, Telnet: ckuusy.c, ckuus4.c . Security updates: ckuath.c, ck_ssl.c . Change K95 version number to 2.2.0: ckcmai.c . Save K95 term i/o state before executing keyboard macro: ckuus4.c . Add tests for SSH Subsystem active during INPUT/OUTPUT/CONNECT: ckuus[45].c . Enable K95 SET SSH V2 AUTO-REKEY: ckuus3.c SFTP and SET SFTP subcommands are implemented up to the case statements. Files of mine that Jeff hadn't picked up: ckuver.h ckcftp.c ckutio.c ckuusx.c (just minor changes for last build-all) On 4 Jan 2003, SET RECEIVE MOVE-TO was changed to convert is argument to an absolute path, which made it impossible to specify a relative path, then move to different directories and have it apply relatively to each directory. Changed this as follows: . Parser uses cmtxt() rather than cmdir() so it won't fail at parse time. . If path is absolute, we fail at parse time if directory doesn't exist. . In reof() we run the the path through xxstring (again, in case deferred evaluation of variables is desired) and then, if not null, use it. . If the directory doesn't exist, rename() fails and reof() returns -4, resulting in a protocol error (this is not a change). We do NOT create the directory on the fly. I also fixed SET SEND/RECEIVE RENAME-TO to parse with cmtxt() rather than cmdir(), since it's parsing a text template, not a directory name, e.g. "set receive rename-to file-\v(time)-v(date)-\v(pid)". This was totally broken, since when I don't know. We don't call xxstring() in this parse, so evaluation is always deferred -- I'd better not change this. ckuus7.c, ckcfns.c, 1 May 2003. From Jeff, Sat May 3 14:15:23 2003: . Pick up the right isascii definition for K95: ckctel.c . malloc... ckuath.c (new safe malloc routines for K95) . Add author listing: ckuus5.c . SSH Heartbeat support (K95 only): ckuus[23].c . Prescan --height and --width to avoid window resizing at startup: ckuusy.c . Add checks for fatal() or doexit() called from sysinit(): ckuusx.c . Move some K95-specific definitions to ckoker.h: ckcdeb.h . Add support for ON_CD macro in zchdir(): ckufio.c . Add a command to let FTP client authenticate with SSLv2: ckcftp.c . Fix parsing of FTP file facts like "UNIX.mode": ckcftp.c ON_CD will need some explaining (to be done). It's implemented for Unix, VMS, WIndows, and OS/2. The FTP file facts fix came from first exposure to the new OpenBSD FTP server: ftp://ftp7.usa.openbsd.org/pub/os/OpenBSD/3.3/i386/ The period in "UNIX.mode" caused an erroneous word break, adding junk to the filename. About the malloc changes, Jeff says "K95 is not behaving well in low memory environments. I'm not sure that C-Kermit does much better. The program does not crash but it certainly does not behave the way the user expects it to. I'm beginning to think that any malloc() error should be treated as fatal." Not visible in these changes because it's in K95-specific modules: Jeff made SET ATTRIBUTES OFF and SET ATTRIBUTES DATE OFF apply to XYZMODEM transfers. From Jeff, 11 May 2003: . Add support for SSH Keepalive to relevant SET command (K95): ckuus3.c . Reduce max overlapped i/o requests from 30 to 7 (K95): ckuus7.c . Don't call sysinit() in fatal(): ckuusx.c. . Some new conditionalizations for SSL module: ck_ssl.c The doublequote-parsing fixes from March and April broke the SWITCH statement, which is implemented by internally defining, then executing, a macro. If I drop back to the old dumb handling of doublequotes, everything is fixed except the problem of March 17th. But can we really expect getncm() to pre-guess what the parser is going to do? getncm()'s only job is to find command boundaries, which are represented by commas. Commas, however, is needed IN commands too. We take a comma literally if it is quoted with \, or is inside a matched pair of braces, parens, or doublequotes. It is not unreasonable to require a doublequote in a macro definition to be prefixed by \ when it is to be taken literally. The proper response to Jason Heskett's complaint of March 17th should have been to leave the code alone and recommand an appropriate form of quoting: def TEST { .foo = {X\"} sho mac foo } And this is what I have done. Another reason for sticking with the old method is that it's explainable. The "improved" method, even if it worked, would be be impossible to explain. Btw, in testing this I noticed that the switch-test script made 8.0.201 dump core. Today's version is fine. The problem with quoted strings inside of IF {...} clauses and FOR and WHILE loops is fixed too. Perhaps "unbroken" would be a better word. ckuus5.c, 11 May 2003. Vace discovered that FTP MGET /EXCEPT:{... (with an unterminated /EXCEPT list) could crash Kermit. Fixed in ckcftp.c, 11 May 2003. CONTINUE should not affect SUCCESS/FAILURE status. ckuusr.c, 11 May 2003. Fixed an oversight that goes back 15 years. While \{123} is allowed for decimal codes, \x{12} and \o{123} were never handled. ckucmd.c, 11 May 2003. Added support for Red Hat and /usr/sbin/lockdev. Supposedly this allows Kermit to be installed without setuid or setgid bits and still be able to lock and use the serial device. Compiles and starts, but not tested. ckcdeb.h, makefile, ckutio.c, ckuus5.c, 16 May 2003. From Jeff: FTP ASCII send data to host when FTP /SSL was in use was broken. ftp_dpl is set to Clear when FTP /SSL is in use. This was causing the data to be written to the socket with send() instead of the OpenSSL routines. ckcftp.c, ckuath.c, 21 May 2003. From Jeff: Stuff for Kerberos 524: ckcdeb.h. Fixes for FTP; "FTP ASCII send data did not properly compute the end of line translations. On Unix (and similar platforms) the end of line was correct for no character sets but incorrect when character sets were specified. On Windows/OS2, the end of line was correct when character sets were specified and incorrect when they were not. On MAC, both were broken. Also, FTP Send Byte counts were incorrect when character sets were specified." ckcftp.c. 17 Jun 2003. From Jeff: fixes to HTTP /AGENT: and /USER: switch action: ckcnet.c ckuus3.c ck_crp.c ckcftp.c ckuus2.c ckuusy.c ckuusr.c ckcnet.h, 21 Jun 2003. From Jeff: Fix SET DIALER BACKSPACE so it can override a previous SET KEY (e.g. from INI file): ckuus7.c. Some SSL/TLS updates: ck_ssl.c. HTTP support for VMS and other VMS improvements (e.g. a way to not have to hardwire the C-Kermit version number into the build script) from Martin Vorlaender: ckcnet.h, ckuus[r3].c, ckcdeb.h, ckvtio.c, ckcnet.c, ckvker.com. Built on Solaris (gcc/ansi) and SunOS (cc/k&r). The new VMS script tests the VMS version and includes HTTP support only for VMS 6.2 or later. 2 Jul 2003. Tried to build on our last VMS system but it seems to be dead. Looks like a head crash (makes really loud noises, boot says DKA0 not recognized) (fooey, I just paid good money to renew the VMS license). Tried building at another site with: Process Software MultiNet V4.3 Rev A-X, Compaq AlphaServer ES40, OpenVMS AXP V7.3 Compaq C V6.4-008 on OpenVMS Alpha V7.3 Had to make a few corrections to ckvker.com. But still, compilation of ckcnet.c bombs, indicating that the SELECT definition somehow got lost somewhere since the 209 release (i.e. no SELECT type is defined so it falls thru to "SELECT is required for this code"). But I don't see anything in ckcdeb.h or ckcnet.[ch] that would explain this. Not ckvker.com either (putting the old one back gives the same result). OK, I give up, maybe it's just that I haven't tried building it on MultiNet recently. What about UCX? Aha, builds fine there except for warnings about mlook, dodo, and parser in ckvfio.c (because of ON_CD) -- I suppose I have #include ... (done) Anyhow it builds OK and the HTTP code is active and almost works (HTTP OPEN works; HTTP GET seems to succeed but creates an empty file every time). Tried building under MultiNet at another installation; same bad result. OK so why won't it build for MultiNet? Comparing ckcnet.c with the 209 version, not a single #ifdef or #include is changed. Tried building with p3="NOHTTP" -- builds OK, aha. Where's the problem? Not ckcnet.h... Not ckcdeb.h... OK I give up, will revisit this next time I get time to do anything with the code. Later Jeff said "Martin did not implement VMS networking for the HTTP code. All he did was activate the #define HTTP which happens to work because his connections are using SSL/TLS connections. http_inc(), http_tol(), etc have no support for VMS networking regardless of whether it is UCX or MULTINET. The vast majority of HTTP connections are not secured by SSL/TLS. It makes no sense to support HTTP on VMS until someone is willing to either do the work or pay have the work done to implement VMS networking in that code base." So the fix is to not enable HTTP for VMS after all. Removed the CKHTTP definition for VMS from ckcdeb.h, 6 Jul 2003. Fixed ckvfio.c to #include (instead of ) to pick up missing prototypes. 6 Jul 2003. From Arthur Marsh: solaris2xg+openssl+zlib+srp+pam+shadow and the corresponding Solaris 7 target. makefile, 6 Jul 2003. Remove duplicate #includes for , , and from ckcftp.c. 6 Jul 2003. Add -DUSE_MEMCPY to Motorola SV/68 targets because of shuffled #includes in ckcftp.c. 8 Jul 2003. From Jeff: Fix problems mixing SSL and SRP without Kerberos. Plus a few minor #define comment changes and a reshuffling of #defines in ckcdeb.h to allow me to build on X86 Windows without Kerberos. ckcdeb.h, ck_crp.c, ckuath.c, 10 Jul 2003. From Jeff: updated ckuat2.h and ckuath.c, 29 Jul 2003. Mats Peterson noticed that a very small Latin-1 file would be incorrectly identified as UCS-2 by scanfile(). Fixed in ckuusx.c, 29 Jul 2003. Fixed ACCESS macro definition to account for the fact that FIND is now a built-in command. ckermit.ini, 30 Jul 2003. From Jeff: Fix for typo in urlparse() (svc/hos): ckuusy.c, 18 Aug 2003. From Jeff: Redhat9 makefile targets (needed for for OpenSSL 0.9.7): makefile, 19 Aug 2003. GREP /NOLIST and /COUNT did too much magic, with some undesirable fallout: "GREP /NOLIST /COUNT:x args" printed "file:count" for each file. "GREP /COUNT:x /NOLIST args" did not print "file:count", but neither did it set the count variable. Removed the magic. Also one of the GREP switches, /LINENUMBERS, was out of order. Fixed in ckuus6.c, 20 Aug 2003. From Jeff: "Reorganizing code to enable building with different subsets of options; a few typos corrected as well." ckcdeb.h, ckuver.h (for RH9), ckcnet.c, ckuus7.c, ckuus3.c: 24 Aug 2003. Scanfile misidentified a big PDF file as text because the first 800K of it *was* text (most other PDF files were correctly tagged as binary). Fixed by adding a check for the PDF signature at the beginning of the file. scanfile(): ckuusx.c, 25 Aug 2003. Ditto for PostScript files, but conservatively. Signature at beginning of file must begin with "%!PS-Ado". If it's just "%!" (or something nonstandard like "%%Creator: Windows PSCRIPT") we do a regular scan. Also added "*.ps" to all binary filename patterns. ckuusx.c, 4 Sep 2003. Ditto (but within #ifndef NOPCLSCAN) for PCL (E) and PJL (%) files, but no binpatterns (note: ".PCL" is the extension for TOPS-20 EXEC scripts). ckuusx.c, 4 Sep 2003. Added comments about OpenSSL 0.9.7 to all linux+openssl targets. makefile, 4 Sep 2003. From Jeff: Added - #define ALLOW_KRB_3DES_ENCRYPT. When this symbol is defined at compilation Kermit will allow non-DES session keys to be used during Telnet Auth. These session keys can then be used for Telnet Encrypt. The reason this is not compiled on by default is that the MIT Kerberos Telnet does not follow the RFC for constructing keys for ENCRYPT DES when the keys are longer than 8 bytes in length. ckuath.c, ckuus5.c, 4 Sep 2003. "ftp mget a b c" succeeded if one or more of the files did not exist, even with "set ftp error-action proceed". This is because the server's NLST file list does not include any files that don't exist, so the client never even tries to get them. Fortunately, the way the code is structured, this one was easy to fix. ckcftp.c, 14 Sep 2003. From Jeff: Corrected code in ckcnet.c to ensure that Reverse DNS Lookups are not performed if tcp_rdns is OFF. Fixed ck_krb5_getrealm() to actually return the realm of the credentials cache and not the default realm specified in the krb5.conf file. Previously krb5_cc_get_principal() was not being called. Fixed ck_krb5_is_tgt_valid() to test the TGT in the current ccache and not the TGT constructed from the default realm. ckcnet.c, ckuath.c, 14 Sep 2003. Marco Bernardi noticed that IF DIRECTORY could produce a false positive if the argument directory had previously been referenced but then removed. This is because of the clever isdir() cache that was added to speed up recursion through big directory trees. Changed IF DIRECTORY to make a second check (definitive but more expensive) if isdir() succeeds, and changed the directory-deleting routine, ckmkdir(), to flush the directory cache (UNIX only -- this also should be done in K95 but it's not critical). This was done by adding a routine, clrdircache() to ckufio.c, which sets prevstat to -1 and prevpath[0] to NUL. ckcfn3.c, ckuus6.c, ckufio.c, 18 Sep 2003. Marco reported the second fix still didn't work for him (even though it did for me). Rather than try to figure out why, I concluded that the directory cache is just not safe: a directory found a second ago might have been deleted or renamed not only by Kermit but by some other process. Why did I add this in the first place? The log says: Some debug logs showed that isdir() is often called twice in a row on the same file. Rather than try to sort out clients, I added a 1-element cache to Unix isdir(). ckufio.c, 24 Apr 2000. Experimentation with DIR and DIR /RECURSIVE does not show this happening at all. So I #ifdef'd out the directory cache (see #ifdef ISDIRCACHE in ckufio.c; ISDIRCACHE is not defined) and backed off the previous changes: ckufio.c, ckcfn3.c, ckuus6.c, 28 Sep 2003. From Jeff: Replace the compile time ALLOW_KRB_3DES_ENCRYPT with a run-time command SET TELNET BUG AUTH-KRB5-DES which defaults to ON: ckctel.[ch], ckuus[234].c, ck_crp.c, ckuath.c. 4 Oct 2003. Allow DIAL RETRIES to be any positive number, and catch negative ones. Also added code to check for atoi() errors (e.g. truncation). At least on some platforms (e.g. Solaris) atoi() is supposed to set errno, but it doesn't. ckuus3.c, ckucmd.c, 4 Oct 2003. Added /DEFAULT: to ASK-class commands (ASK, ASKQ, GETOK): . For popups: no way to send defaults to popup_readtext() or popup_readpass(). . For GUI ASK[Q], pass default to gui_txt_dialog(). . For GUI GETOK, convert "yes" "ok" or "no" default to number for uq_ok(). . For Text GETOK, add default to cmkey(). . For Text ASK[Q], add default to cmtxt(). . For GETC, GETKEY, and READ: no changes. GETOK, ASK, and ASKQ with /TIMEOUT: no longer fail when the timer goes off if a /DEFAULT was supplied. The GUI functions (uq_blah) don't seem to support timeouts. Only the text version has been tested. ckuus[26].c, 4 Oct 2003. From Jeff: add /DEFAULT: for popups. ckuus6.c. 6 Oct 2003. Change SET DIAL INTERVAL to be like SET DIAL RETRIES. ckuus[34].c, 6 Oct 2003. Added target for HP-UX 10/11 + OpenSSL built with gcc, from Chris Cheney. Makefile, 12 Oct 2003. From Jeff, 6 Nov 2003: . #ifdef adjustments: ckcftp.c, ckcdeb.h . Fix spurious consumption of first byte(s) on Telnet connection: ckctel.c . Another HP PJL test for scanfile: ckuusx.c. . K95: Recognize DG4xx protected fields in DG2xx emulation: ckuus7.c. . Add SSLeay version display to SHOW AUTH command: ckuus7.c . Improved SET MOUSE CLEAR help text: ckuus2.c. . Improved Kverbs help text: ckuus2.c (+ new IBM-3151 Kverbs). . Some changes to ck_ssl.c, ckuath.c. From PeterE, 10 Nov 2003: . Improved HP-UX 10/11 makefile targets for OpenSSL. . #ifdef fix for OpenSSL on HP-UX: ck_ssl.c. Another new makefile from PeterE with improved and integrated HP-UX targets. 12 Nov 2003. A couple fixes to the solaris9g+krb5+krb4+openssl+shadow+pam+zlib target from Jeff. Added a solaris9g+openssl+shadow+pam+zlib target. makefile, 21 Nov 2003. From Jeff, 30 Nov 2003: . Fix SEND /MOVE-TO: ckuusr.c. . Fix K95 SET TITLE to allow quotes/braces around text: ckuus7.c. . Improved "set term autodownload ?" response: ckuus5.c. . Fix SHOW FEATURES to specify the protocol for encryption: ckuus5.c . Make {SEND, RECEIVE} {MOVE-TO, RENAME-TO} work for XYZMODEM (K95 only). From Jeff: 7 Jan 2004: . At one point Frank started to add a timer parameter to the uq_txt() function but he only did it for the non-ANSI compilers. I added it for the ANSI compilers, fixed the prototypes and provided a default value easily changed DEFAULT_UQ_TIMEOUT: ckcker.h, ckuus[36].c, ck_ssl.c, ckcftp.c, ckuath.c. . Fixed SET TERMINAL DEBUG ON (typo in variable name): ckuus7.c. . Fixed BEEP INFORMATION; previously it made no sound, now uses MB_ICONQUESTION. ckuusx.c. From Ian Beckwith (Debianization), 7 Jan 2004: . Search dir/ckermit for docs, as well as dir/kermit in cmdini(): ckuus5.c. . New linux+krb5+krb4+openssl+shadow+pam target (kitchen sink minus SRP, which Debian does not distribute): makefile. ? Mangles the DESTDIR support in makefile to install into a staging area: makefile (I didn't take this one yet). Updated copyright notices for 2004, all modules. 7 Jan 2004. Added INPUT /NOMATCH, allowing INPUT to be used for a fixed amount of time without attempting to match any text or patterns, so it's no longer necessary to "input 600 STRING_THAT_WILL_NEVER_COME". If /NOMATCH is included, INPUT succeeds if the timeout expires, with \v(instatus) = 1 (meaning "timed out"); fails upon interruption or i/o error. ckuusr.h, ckuus[r24].c, 7 Jan 2004. Added SET INPUT SCALE-FACTOR . This scales all INPUT timeouts by the given factor, allowing time-sensitive scripts to be adjusted to changing conditions such as congested networks or different-speed modems without having to change each INPUT-class command. This affects only those timeouts that are given in seconds, not as wall-clock times. Although the scale factor can have a fractional part, the INPUT timeout is still an integer. Added this to SHOW INPUT, and added a \v(inscale) variable for it. ckuusr.h, ckuus[r257].c, 7 Jan 2004. undef \%a, \fverify(abc,\%a) returns 0, which makes it look as if \%a is a string composed of a's, b's, and/or c's, when in fact it contains nothing. Changed \fverify() to return -1 in this case. ckuus4.c, 12 Jan 2004. \fcode(xxx) returned an empty string if its argument string was empty. This makes it unsafe to use in arithmetic or boolean expressions. Changed it to return 0 if its argument was missing, null, or empty. ckuus4.c, 12 Jan 2004. Updated \verify() and \fcode() help text. ckuus2.c, 12 Jan 2004. While setting up IKSD, Ian Beckwith noticed that including the --initfile: option caused Kermit to start parsing its own Copyright string as if it were the command line, and eventually crash. I couldn't reproduce on Solaris / Sparc but I could in Linux / i386 (what Ian is using) -- a change from Jeff on 28 Apr 2003 set the command-line arg pointer to a literal empty string in prescan() about line 1740 of of ckuus4.c; the pointer is incremented next time thru the loop, resulting in random memory being referenced. Fixed by setting the pointer to NULL instead of "". ckuus4.c, 12 Jan 2004. declare \&a[999999999999999] would dump core on some platforms. atoi() or whatever would truncate the dimension to maxint. When we add 1 to the result, we get a negative number, which is used as an index, loop test, etc. Fixed both dodcl() and dclarray() to check for (n+1 < 0). ckuus[r5].c, 12 Jan 2004. Unix zchki() would fail on /dev/tty, which is unreasonable. This prevented FOPEN /READ from reading from the terminal. zchki() already allowed for /dev/null, so I added /dev/tty to the list of specials. Ditto for FOPEN /WRITE and zchko(). ckufio.c 13 Jan 2004. Added untabify() routine to ckclib.[ch], 13 Jan 2004. Added FREAD /TRIM and /UNTABIFY. ckuus[27].c, 13 Jan 2004. Added \funtabify(). ckuusr.h, ckuus[24].c, 13 Jan 2004. Dat Nguyen noticed that (setq u 'p') followed by (u) dumped core. This was caused by an over-clever optimization that skipped mallocs for short literals, but then went on later to try to free one that hadn't been malloc'd. Fixed in dosexp(): ckuus3.c, 14 Jan 2004. Catch another copyright date. ckuus5.c, 14 Jan 2004. Fixed SWITCH to work even when SET COMMAND DOUBLEQUOTE OFF (from Mark Sapiro). ckuus5.c, 15 Jan 2004. Changed version to 8.0.211 so scripts can test for recently added features. ckcmai.c, 15 Jan 2004. Fixed a glitch in K95 "help set port". ckuus2.c, 20 Jan 2004. Fix from Jeff: Connections to a TLS-aware protocol which require a reconnect upon certificate verification failure could not reconnect if the connection was initiated from the command line or via a URL. ckctel.c ckcmai.c ckuusr.c ckuus7.c ckuusy.c, 20 Jan 2004. From Alex Lewin: makefile target and #ifdef for Mac OS X 10.3 (Panther): makefile, ckcnet.c, 7 Feb 2004. Added KFLAGS to sco32v507 targets to make PTY and SSH commands work. The same flags could probably also be added to earlier OSR5 targets but they have not been tested there. makefile, 7 Feb 2004. Checked a complaint that "LOCAL &a" did not make array \&a[] local. Indeed it did not, and can not. You have to use the full syntax in the LOCAL command, "LOCAL \&a[]", or else it doesn't know it's not a macro named &a. 7 Feb 2004. Fixed some confusion in creating IKSD database file and temp-file names. I was calling zfnqfp() without remembering that the path member of the returned struct included the filename, so to get just the directory name, I needed to strip the filename from the right. ckuusy.c, 2 Mar 2004. New ckuath.c, ck_ssl.c from Jeff. 2 Mar 2004. Updated Jeff's affiliation in VERSION command text. ckuusr.c, 2 Mar 2004. Designation changed from Dev.00 to Beta.01. ckcmai.c, 2 Mar 2004. Fixed zrename() syslogging -- it had success and failure reversed. Beta.02: ckufio.c, 4 Mar 2004. Problem: when accessing IKSD via a kermit:// or iksd:// URL, and a user ID is given but no password, doxarg() set the password to "" instead of leaving it NULL, but all the tests in dourl() are for NULL. Fixed in doxarg(): ckuusy.c, 5 Mar 2004. The logic in dourl() about which macro to construct (login and connect, login and get directory listing, or login and fetch a file) was a bit off, so all three cases were not handled. ckcmai.c, 5 Mar 2004. Trial Beta builds: . HP-UX B.11.11 PA-RISC . HP-UX B.11.23 IA64 . Tru64 4.0G Alpha . Tru64 5.1B Alpha . Debian 3.0 i386 . Red Hat ES 2.1 i386 . Slackware 9.1 i386 . VMS 7.3-1 Alpha + UCX 5.3 . VMS 7.3-1 Alpha no TCP/IP . VMS 7.3 Alpha MultiNet 4.3 A-X . SCO UnixWare 7.1.4 i386 . SCO OSR5.0.7 i386 . Solaris 9 Sparc Fixed compiler warning in doxarg() caused by typo (NULL instead of NUL) in the 5 March doxarg() edit. ckuusy.c, 9 Mar 2004. IKSD (kermit://) command-line URLs did not work right if the client had already preauthenticated with Kerberos or somesuch because they tried to log in again with REMOTE LOGIN. The macros constructed in doxarg() needed to check \v(authstate) before attempting REMOTE LOGIN. ckcmai.c, 10 Mar 2004. Added ckuker.nr to x.sh (ckdaily upload) and updated ckuker.nr with current version number and dates. 10 Mar 2004. Replaced hardwired references to /usr/local in makefile with $(prefix) (which defaults to /usr/local, but can be overridden on the command line), suggested by Nelson Beebe for use with Configure. 10 Mar 2004. From Nelson Beebe: In the Kermit makefile in the install target commands, line 981 reads: cp $(BINARY) $(DESTDIR)$(BINDIR)/kermit || exit 1;\ Could you please add this line before it: rm -f $(DESTDIR)$(BINDIR)/kermit;\ Some sites (mine included) keep multiple versions of software around, with hard links between $(prefix)/progname and $(prefix)/progname-x.y.z. Failure to remove the $(prefix)/progname at "make install" time then replaces the old $(prefix)/progname-x.y.z with the new one, destroying an old version that the site wanted to be preserved. makefile, 10 Mar 2004. Minor syntax and typo fixes (mostly prototypes): ckcdeb.h, ckcfns.c, ckclib.c, ckufio.c, ckuusr.h, ckuusx.c, 10 Mar 2004. (I still have a few more to do.) Added CC=$(CC) CC2=$(CC2) to many (but not all) makefile targets that reference other makefile targets. On some platforms (notably AIX, Solaris, SunOS) there are specific targets for different compilers, so I skipped those. makefile, 10 Mar 2004. Added error checking to kermit:// URL macros, so they don't plow ahead after the connection is closed. ckcmai.c, 11 Mar 2004. Added FreeBSD 4.9 and 5.1 targets (only the herald is affected). makefile, ckuver.h, 11 Mar 2004. Added "LIBS=-lcrypt" to bsd44 targets since nowadays crypt is almost always unbundled from libc. Also added explanatory notes. makefile, 11 Mar 2004. Changed MANDIR to default to $(manroot)/man/man1, and manroot to default to $(prefix). More adding of CC=$(CC) clauses: {Free,Net,Open}BSD, 4.4BSD. makefile, 11 Mar 2004. Miscellaneous cleanups: ckuusx.c, ckcnet.c, ckufio.c, 11 Mar 2004. Corrected the check in the linux target to see if /usr/include/crypt.h exists, and if so to define HAVE_CRYPT_H, which is used in ckcdeb.h to #include to get the prototype for crypt() and prevent bogus conversions on its return type on 64-bit platforms (the previous test wasn't quite right and the resulting symbol wasn't spelled right). makefile, 12 Mar 2004. From Jeff, 14 Mar 2004: . Initialize localuidbuf[] in tn_snenv(): ckctel.c. . Remove remote-mode checks in hupok() for K95G only (why?): ckuus3.c. . Add help text for new K95-only TYPE /GUI switches: ckuus2.c. . TYPE /GUI parsing, ...: ckuusr.c. . TYPE /GUI action, dotype(): ckuus6.c . Change Jeff's affiliation: most modules. 20 Mar 2004: Looked into adding long file support, i.e. handling files more than 2GB (or 4GB) long. Discovered very quickly this would be a major project. Each platform has a different API, or environment, or transition plan, or whatever -- a nightmare to handle in portable code. At the very least we'll need to convert a lot of Kermit variables from long or unsigned long to some new Kermit type, which in turn is #defined or typedef'd appropriately for each platform (to off_t or size_t or whatever). Then we have to worry about the details of open() vs fopen(); printf() formats (%lld vs %Ld vs %"PRId64"...), platforms like HP-UX where you might have to use different APIs for different file systems on the same computer, etc. We'll need to confront this soon, but let's get a good stable 8.0.211 release out first! Meanwhile, for future reference, here are a few articles: General: http://freshmeat.net/articles/view/709/ Linux: http://www.ece.utexas.edu/~luo/linux_lfs.html HP-UX: http://devrsrc1.external.hp.com/STK/partner/lg_files.pdf Solaris: http://wwws.sun.com/software/whitepapers/wp-largefiles/largefiles.pdf Looked into FTP timeouts. It appears I can just call empty() (which is nothing more than a front end for select()) with the desired timeout before any kind of network read. If it returns <= 0, we have a timeout. This is not quite the same as using alarm() / signal() around a recv() (which could get stuck) but alarm() / signal() are not not used in the FTP module and are not naturally portable to Windows, but select() is already in use in the FTP module for both Unix and Windows. This form of timeout could be used portably for both command response and data reads. What about writes to the command or data socket? They can get stuck for hours and hours without returning too, but the select() approach won't help here -- we need the actual send() or recv() to time out, or be wrapped in an alarm()/signal() kind of mechanism. But if we can do that for sends, we can also do it for receives. Better check with Jeff before I start programming anything. 20 Mar 2004. Later: Decided to postpone the above two projects (ditto IPv6) until after 8.0.211 is released because both will have major impacts on portability. Grumble: all i/o APIs should have been designed from the beginning with a timeout parameter. To this day, hardly any have this feature. 3-4 Apr 2004: More 8.0.211 Beta.02+ test builds: . FreeBSD 3.3 . FreeBSD 4.4 . Linux Debian 2.1 . Linux RH 6.1 . Linux RH 7.1 . Linux RH 7.2 . Linux RH 9 (with 84 different combinations of feature selection) . Linux SuSE 6.4 . Linux SuSE 7.0 . NetBSD 1.4.1 . NetBSD 1.5.2 . OpenBSD 2.5 . OpenBSD 3.0 . QNX 4.25 . SCO UnixWare 2.1.3 . SCO UnixWare 7.1.4 . SCO OpenServer 5.0.7 . SCO XENIX 2.3.4 (no TCP) Changes needed: None. Problem: SCO XENIX 2.3.4 network build failed in the FTP module with header-file syntax and conflicting-definitions trouble. I'm not going to try to fix it; 8.0.209 built OK with FTP, so we'll just keep that one available. Got access to VMS 8.1 on IA64. Building the nonet version of C-Kermit required minor modifications to ckvvms.h, ckv[ft]io.c, and ckvcon.c, to account for a third architecture. Also to SHOW FEATURES in ckuus5.c. Once that was done, the UCX 5.5 version built OK too. Starts OK, makes Telnet connection OK, sends files. Has some obvious glitches though -- "stat" after a file transfer reports 0 elapsed time (in fact it was 00:09:48) and 1219174400 cps (when in fact it was 10364). This doesn't happen on the Alpha. Btw, the IA64 binary is twice as big as the Alpha one. Changed to Beta.03. 5 Apr 2004. Fixed the ckdaily script to include the makefile and man page in the Zip file (they were not included because the Zip file was intended mainly for VMS users, but some Unix users prefer Zip to tar.gz). 6 Apr 2004. Traced problems in VMS/IA64 statistics report to rftimer()/gftimer() in ckvtio.c, which use sys$ and lib$ calls to figure elapsed time. These work on VAX and Alpha but not IA64. Sent a report to the chief engineer of the IA64 VMS port; he says it's probably a bug in VMS 8.1 (which is not a real release); he'll make sure it's fixed in 8.2. As an experiment, tried swapping in the Unix versions of these routines (which call gettimeofday() etc). They seem work just fine (it hung a couple times but I think that's because the underlying system hung too; trying it later on a new connection, it was fine; however I noticed a BIG discrepancy in throughput between sending and receiving). Moved definitions for VMS64BIT and VMSI64 to ckcdeb.h so all modules can use them and added them to the SHOW FEATURES display. Added VMSV80 definition to build procedure. Beta.03+. ckcdeb.h, ckcuus5.c, ckcvvms.h, ckvtio.c, ckvker.com, 6 Apr 2004. While doing the build-all, I noticed the VMS version did not build with Multinet or older UCX versions, always with the same errors -- undeclared variables, undefined symbols, all TCP/IP related. This didn't happen a couple weeks ago... Somehow the order of #includes was messed up -- ckuusr.h depended on symbols that are defined in ckcnet.h, but ckcnet.h was being included after ckuusr.h... this was compounded by two missing commas in ckvker.com. 11 Apr 2004. Removed Beta designation, released as 8.0.211, 10 Apr 2004. I had somehow lost the edit to ckutio.c that changed the UUCP lockfile for Mac OS X from /var/spool/uucp to /var/spool/lock. So I slipped it in and re-uploaded version 8.0.211. You can tell the difference because SHOW VERSIONS has 17 Apr 2004 for the Communications I/O module. Also the 10.3 executable now has a designer banner: "Mac OS X 10.3". makefile, ckuver.h, ckutio.c, ckuus[45].c, 17 Apr 2004. ---8.0.211--- Removed "wermit" from "make clean" (how did it get there?). makefile. From Jeff, applied 10 May 2004. . Rearrange #ifdefs that define OS/2-only features. ckcdeb.h. . Fix two strncat()s that should have been ckstrncat()s. ckuus7.c. . Fix two strncat()s that should have been ckstrncat()s. ckuus4.c. . Fix one strncat(). ckcfns.c. . SET FTP CHAR ON used backwards byte order when output to screen. ckcfns.c. . Fix two strncat()s. ckuus3.c. . Add SET NETWORK TYPE NAMED-PIPE for K95. ckuus3.c. . Add "No active connections" message to hupok(). ckuus3.c. . Fix many strncat()s. ckcnet.c. . Fix some strncat()s. ckcftp.c . Make FTP port unsigned short for 16383 < port < 65536. ckcftp.c. . Improvements to FTP USER command. ckcftp.c. . Fix FEAT parsing to allow for various forms of whitespace. ckcftp.c. S-Expression (AND FOO BAR) would not short-circuit if FOO's value was 0, even though short-circuiting code has been there since Day 1. Similarly for (OR BAR FOO). Turns out the first operand was a special case that bypassed the short-circuit check. Fixed in dosexp(): ckuus3.c, 10 May 2004. Red Hat 7.3 (and maybe others) referenced open() without first ensuring it was declared. The declaration is in , which is after in ckutio.c series of #includes. Made a special case for this. ckutio.c (see comments), 10 May 2004. If the local Kermit's parity is set to SPACE and then a file arrives via autodownload, automatic parity detection improperly switches it to NONE. Fixed in rpack() by switching parity automatically only if parchk() returns > 0 (rather than > -1), since NONE and SPACE are indistinguishable. A bigger problem still remains: autodownload does not work at all if the sender is using actual parity bits (even, odd, or mark) and the receiver's parity is NONE. ckcfn2.c, 10 May 2004. When a DIAL MACRO is defined and the phone number is comprised of more than one "word" (i.e. contains spaces), the dial macro loses the second and subsequent words after the first call. Fixed in xdial() by inserting quotes around phone number before passing it to xdial(). ckuus6.c, 10 May 2004. DIAL MACRO fix was not right; the quotes were kept as part of the phone number and sent to the modem. dodo() pokes its argument to separate the macro argument string into its component arguments. xdial() is called repeatedly on the same string, so after the first time, a NUL has been deposited after the first word of the telephone number. The fix is to have xdial() create a pokeable copy of its argument string before calling dodo(dial-macro,args...). It might seem odd that dodo pokes its argument, but making copies would be would be prohibitive in space and time. ckuus6.c, 23 May 2004. FTP CD did not strip braces or quotes from around its argument. Fixed in doftprmt(): ckcftp.c, 23 May 2004. Added client side of REMOTE MESSAGE/RMESSAGE/RMSG: ckuus[r27].c, 23 May 2004. Server side of REMOTE MESSAGE: ckcpro.w, 23 May 2004. From Dave Sneddon: an updated CKVKER.COM containing a fix where the COMPAQ_SSL symbol was not defined but later referenced which generated an undefined symbol error. ckvker.com, 5 Jan 2005. From Andy Tanenbaum (28 May 2005): . Fix an errant prototype in ckcker.h and ckucmd.h - () instead of (void). . Add support for MINIX 3.0. makefile, ckutio.c, ckufio.c, ckuver.h. Fixed messed-up sndhlp() call which apparently had been jiggered to compensate for the bad prototype which has now been fixed, ckcpro.w, 12 Jun 2005. From Jeff (12 June 2005): . Security updates. ck_ssl.c, ck_crp.c, ckuath.c. . Fix bug in K95 SET PRINTER CHARACTER-SET. ckuus3.c. . Add printer character-set to K95 SHOW PRINTER display. ckuus5,c . Add SET MSKERMIT FILE-RENAMING to K95. ckuus7.c, ckuusr.h. . Add help for K95 SET MSKERMIT. ckuus2.c. . Add SET GUI CLOSE to K95. ckuusr.h, ckuus2.c, ckuus3.c . Add help text for K95 SET GUI MENUBAR and TOOLBAR. ckuus2.c. . Add --noclose command-line option for K95. ckuusy.c . Add PAM support for Mac OS X. ckufio.c. . Add GSSAPI support for Mac OS X. ckcftp.c. . Pick up more URL options. ckcker.h, ckuusy.c. . Fix bug in delta-time calculation across year boundary. ckucmd.c. . Add Secure Endpoints to copyright notices. ckcmai.c. . Fix FTP HELP to override unverbose setting. ckcftp.c. . Fix assorted minor typos. From Matthias Kurz: automatic herald generation for NetBSD 2.0 and later, "make netbsd2". ckuver.h, makefile, 12 Jun 2005. Added SET TERMINAL LF-DISPLAY, like CR-DISPLAY but for linefeed rather than carriage return. ckuusr.h, ckuus[257x].c, 12 Jun 2005. Made a command-line option --unbuffered to do what the -DNONOSETBUF compile-time option does, i.e. force unbuffered console i/o. Unix only. ckuusr.h, ckuusy.c, ckutio.c, 12 Jun 2005. Fixed getiact() (which displays TERM IDLE-ACTION setting) to display space as \{32}. ckuus7.c, 12 Jun 2005. Added LMV as a synonym for LRENAME, which is itself a synonym for LOCAL RENAME. ckuusr.c, 12 Jun 2005. Put HELP SET TERMINAL DG-UNIX-MODE text where it belonged. ckuus2.c, 12 Jun 2005. Added IF LINK (Unix only) to test if a filename is a symlink. Uses the most simpleminded possible method, calls readlink() to see if it succeeds or fails. No other method is dependable across different Unixes. This code should be portable because I already use readlink() elsewhere within exactly the same #ifdefs. ckufio.c, ckuus2.c, ckuus6.c, 12 Jun 2005. Fixed a bug in which \fdir() wouldn't work when its argument was the nonwild name of a directory file. zxpand(): ckufio.c, 12 Jun 2005. Made \fdirectory() a synonym for \fdirectories(). Made \fdir() an acceptable abbreviation for these, even though it clashes with \fdirname(), which still works as before. ckuus4.c, 12 Jun 2005. Added the long-needed \flopx() function, to return rightmost pieces of strings, such as file extensions. \fstripx() and \flopx() are the orthogonal functions we need to pick filenames apart from the right: \stripx(foo.tar.gz) = foo.tar; flopx(foo.tar.gz) = gz. ckuusr.h, ckuusr.c, ckuus2.c, 12 Jun 2005. Removed reference to defunct fax number, ckcmai.c, 12 Jun 2005. Added -DHAVE_PTMX to linux+krb5+openssl+zlib+shadow+pam. From Timothy Folks. makefile, 12 Jun 2005. Built on Solaris 9 and NetBSD 2.0. From Jeff: New build target for Mac OS X 10.3 with Kerberos 5 and SSL. makefile, 14 Jun 2005. Fixed error in ckuver.h NetBSD #ifdefs. 15 Jun 2005. Fixed SET TERMINAL IDLE-ACTION OUTPUT to work as documented, namely if the output string is empty, to send a NUL. Previously there was no way to make it send a NUL. ckuus7.c, 15 Jun 2005. Suppose (in Unix, for example) a filename contains wildcard characters, such as {abc}.txt. When referring to such a file (e.g. in a SEND command), these characters can be quoted, e.g. \{abc\}.txt. But if the file list has been obtained programmatically, e.g. stored in an array, there is no way, short of tedious, complicated, and error-prone string processing, to reference the file. For this we need a way to disable wildcard processing. I added { ON, OFF } choices for the SET WILD and SHOW FILE commands: ckuusr.h, ckuus[234].c. { ON, OFF } turns wildcarding off and on without affecting the { KERMIT, SHELL } agent choice; it does this by setting a new and separate global variable, wildena. Added semantics to ckufio.c. Crude but effective. It might have been more Unixlike to add Yet Another form of quoting but we have enough of that already (later maybe I'll add a \function() for this). Needs to be propagated to Windows and VMS. 15 Jun 2005. Improved and fixed typos in HELP WILDCARD and HELP PATTERN. ckuus2.c, 15 Jun 2005. The GREP command, and probably anything else that uses ckmatch() for pattern matching, failed on patterns like */[0-3]*.html. The [a-b] handler, when failing to match at the current position, neglected to back up the pattern and try again on the remainder of the string. I also fixed another case, in which matching a literal string a*b?c against the pattern a[*?]*[?*]c caused ckmatch() to recurse until it blew up. ckclib.c, 16 Jun 2005. Added builds and designer banner for Solaris 10. makefile, ckuver.h, 27 Jun 2005. Defined CKHTTP for NetBSD, the HTTP code builds and works fine there. ckcdeb.h, 2 Jul 2005. Added #ifndef OSF40..#endif around definition of inet_aton() in ck_ssl() to allow building in Tru64. Added tru64-51b+openssl to makefile. 15 Jul 2005. HTTP GET would fail if the URL contained any metacharacters, no matter how much you quoted them. Although it uses cmfld() to parse the (partial) URL, it then uses cmofi() to get the output filename, which by default is the "filename" from the URL, which might be something like "rankem.asp?id=1639". cmofi() refuses to accept unquoted metacharacters in "filenames" and that's what happens in this case if the output filename is not specified. Worked around this by disabling wildcard processing around HTTP GET using the new "wildena" variable from June 15th. ckuusr.c, 18 Jul 2005. Fixed the June 16th fix to the pattern matcher. I fixed a real problem, but I made an unrelated optimization that introduced new ones. ckclib.c, 18 Jul 2005. Added missing help text for \fb64encode() and \fb64decode(). ckuus2.c, 18 Jul 2005. Changed SET WILD OFF help text to warn that this setting prevents the creation of backup files (later I'll have to see if something more useful can be done about this). ckuus2.c, 18 Jul 2005. Built OK on Mac OS X 10.4.2 using macosx103 target (but with some "signedness" warnings in ckcnet.c and ckcftp.c). Built on Unixware 7.1.4 with uw7 target. 27-28 Jul 2005. Added -DCKHTTP to Mac OS X 10.3-.4 KFLAGS. Makefile, 4 Aug 2005. Built on BSDI 4.3.1. Added -DCKHTTP. Compact substring notation extended to accept not only start:length but also start-end notation. Thus \s(foo[12:18]) means the substring of foo starting at position 12 of length 18, and tne new \s(foo[12-18]) means the substring of foo starting at position 12 and ending with position 18. Ditto for \:(\%a), etc. ckuus4.c, 9 Aug 2005. See correspondence with Mark Sapiro, Nov 2003 and Sep 2004, about certain variations on IF syntax having been broken by the introduction of "immediate macros" circa 1999. It seems the problem -- variables not being expanded -- always occurs in the ELSE part when (a) the IF condition is false; (b) the ELSE command is "standalone", i.e. expressed as a separate command after the IF command (original C-Kermit 5A syntax), and (c) its command list is a block. This would suggest the problem is in the XXELS parser. Going back to 1999, I find this: Fixed a problem Jim Whitby noticed with quoting in ELSE statements. This problem was introduced when I unified IF and XIF, and occurs only when ELSE begins on a line, followed by a { command list } rather than a single command. The solution (gross) was to make a special version of pushcmd() (called pushqcmd()) for this situation, which doubles backslashes while copying, BUT ONLY IF it's a command list (i.e. starts with "{"); otherwise we break lots of other stuff. Result passes Jim's test and still passes ckedemo.ksc and iftest.ksc. ckucmd.c, ckuus6.c, 27 Sep 99. I undid this change and it made no difference to all the other IF constructions (in fact, it fixed an unrelated one that was broken, so now iftest scores 54 out of 54, instead of 53). However, it does not fix the ELSE problem; in fact it pushes it all the way in the other direction: The opposite occurs any time you try to execute an immediate macro inside a macro or any other { block }: not only is the variable evaluated, it is evaluated into nothing. It looks like this happens only in immediate macros, i.e. *commands* that start with '{'. So maybe we really have two isolated problems, that can each be fixed. The situation is illustrated by this simple script: def xx { if false { echo \%1, echo \%2 } else { echo \%3, echo \%4 } } xx one two three four With pushqcmd() it echoes the variable names literally; with pushcmd() it echoes empty lines. Since ELSE, when its argument is a block, dispatches to the immediate-macro handler, it seems we have unified the two problems, so fixing one should fix the other. The problem is that we define a new temporary macro and then call dodo() to execute it. But if the definition contains macro arguments, we have added a new level of macro invocation, thus wiping out the current level of args. The cure is to expand the variables in the immediate macro in the current context, before executing it. This means simply changing the cmtxt() call that reads the immediate macro to specify xxsting as its processing function, rather than NULL, which is used for real macros to defer their argument evaluation until after the macro entered. ckuusr.c, 11 Aug 2005. Added a new makefile target, macosx10.4, for Mac OS X 10.4. This one uses an undocumented trick to get the otherwise unavailable-except-by-clicking Mac OS X version number (in this case 10.4.2) and stuff it into the HERALD string. makefile, 11 Aug 2005. Built OK on Solaris 9, Solaris 10 (with a few implicit declaration warnings in ckuusx.c), Mac OS X 10.4.2 (with some warnings in ckcnet.c and ckcftp.c), Mac OS X 10.3.9 (also using the macos10.4 entry, which gets the right version number, and gets no warnings at all), RH Enterprise Linux AS4 on AMD x86_64, Tru64 Unix 4.0F, SCO UnixWare 7.1.4 For docs and/or scriptlib: Unix C-Kermit can be a stdin/out filter. The trick is to use the ASK, ASKQ, or GETC command for input, specifying no prompt, and ECHO or XECHO for output, e.g.: while true { ask line if fail exit 0 echo \freverse(\m(line)) } exit 0 FOPEN didn't do anything with the channel number if the open failed, so any subsequent command that tried to reference it would get a parse error it was undefined or non-numeric, not very helpful. Changed FOPEN to set the channel number to -1 if the file can't be opened. Now subsequent operations on the channel fail with "Channel -1: File not open". I also added two magic channel numbers: -8 means that any FILE command (besides OPEN and STATUS) on that channel is a noop that succeeds silently; -9 is a noop that fails silently. So now it's possible to simply set a channel number to one of these values to disable i/o to certain file without getting lots of error messages. dofile(): ckuus7.c, 12 Aug 2005. Added automatic herald construction for UnixWare 7. makefile, 12 Aug 2005. Unix isdir() never allowed for arguments that started with tilde, so gave incorrect results for ~/tmp/ or ~fdc. The problem was mainly invisible since most commands that parsed file or directory names used cmifi(), cmdir(), etc, which did the conversions themselves. But IF DIRECTORY was an exception, since its operand had to be treated as just text, and then tested after it was parsed. ckufio.c, 13 Aug 2005. Fixed the following: "ckuusx.c", line 8959: warning: implicit function declaration: ckgetpeer "ckufio.c", line 1869: warning: implicit function declaration: ttwait "ckufio.c", line 2941: warning: implicit function declaration: mlook "ckufio.c", line 2943: warning: implicit function declaration: dodo "ckufio.c", line 2944: warning: implicit function declaration: parser "ckcftp.c", line 2625: warning: implicit function declaration: delta2sec "ckcftp.c", line 4071: warning: no explicit type given for parameter: prm "ckcftp.c", line 8389: warning: no explicit type given for parameter: brief ckuusx.c, ckufio.c, ckcftp.c, ckucmd.h. 13 Aug 2005. Unbuffered stdout code has never worked because the setbuf(stdout,NULL) call has to occur before the stdout has been used. The reason it's needed is that some Kermit code writes to stderr (which is unbuffered) and other code writes to stdout, and therefore typescripts can come out jumbled. Robert Simmons provided the needed clue when he insisted it worked only when executed at the very beginning of main(). So I moved the code to that spot. But since now we also want to make unbuffered a runtime (command-line) option, I had to do a clunky by-hand pre-prescan inline in main() to look thru argv[], even before prescan() was called. ckcmai.c, ckutio.c, ckuusy.c, 13 Aug 2005. (Now that this works, it might be a good idea to remove all use of stderr from Kermit.) Managed, after some finagling, to build a 64-bit version on Solaris 10 at Utah Math with Sun cc. (Can't make any gcc builds at all, 32- or 64-bit, they all blow up in .) New target: solaris10_64. makefile, 15 Aug 2005. The 64-bit Solaris 10 version compiles and links OK and transfers files in remote mode. It can make FTP connections and use them, but Telnet connections always fail with "network unreachable". This is with all default libs and include files. Nelson has a separate set in /usr/local, which he references explicitly in all his 64-bit builds, but using these makes no difference. Some data type is wrong in ckcnet.c. But telnet works fine in 64-bit Linux and Tru64 builds. Debug logs trace the difference to netopen() (of course), the spot where we test the results of inet_addr(), which is already marked suspicious for 64-bit builds. It seems that inet_addr() is of type in_addr_t, which in turn is u_int32, i.e. an unsigned 32-bit int. Yet the man page says that failure is indicated by returning -1. I guess this doesn't matter in 32-bit builds, but in the 64-bit world, the test for failure didn't work right. I made a Solaris-specific workaround, and checked that it works in both 32-bit and 64-builds. I really hate typedefs. ckcnet.c, 15 Aug 2005. Changed the plain-text version (as opposed to the popup or GUI version - the GUI version, at least, already does this) of ASKQ to echo keystrokes asterisks rather than simply not echo anything, so it's easier to see what you're doing, the effects of editing, etc. Experimental; for now, there's no way to disable this. Not sure if there needs to be. Anyway, to get this working required a fair amount of cleaning up of gtword(), which was echoing different ways in different places. ckuus6.c, ckucmd.c, 15 Aug 2005. Added a solaris9_64 target for building a 64-bit version on Solaris 9 with Sun cc. Verified, using the DIR command and \fsize() function on a 4.4GB file, that the Solaris 64-bit version of Kermit gets the size correctly, and that it can copy such a file (thus its fopen/fread/fwrite/fclose interface works right). Initiated a large-file transfer between here and Utah over SSH and verified that it puts the correct file size in the A packet when sending; the right quantities are shown on the file transfer display (file size CPS, percent done, etc). But even at 5Mb/sec, it takes a good while to transfer 4.4GB, more than 2 hours (not streaming; 30 window slots, 4K packets, maybe it would go faster with streaming)... After an hour or so, it filled up the partition and gave up (gracefully) before it reached the 2GB frontier (drained its pending packets, closed the partial file). Restarted at 12:54, this time with streaming and 8K packets (the speed wasn't significantly different). This time it transferred 95% of the file (4187660288 bytes) before failing because the disk filled up. Went to Utah and started a transfer between two Solaris 10/Sparc hosts; this goes about 8 times faster. The transfer completed successfully after 17m41s. All fields in the f.t. display looked right the whole time. Then I verified various other 64-bit combinations transferring the same 4.4GB file: To................ From Sol Amd i64 Tru Sol OK OK OK OK Sol = Solaris 10 / Sparc Amd OK Amd = AMD x86_64 RH Enterprise Linux AS4 i64 OK i64 = Intel IA64, RH 2.1AS Tru Tru = Tru64 Unix 4.0F Alpha (The other combinations are difficult to test for logistical reasons.) Tried sending the same long file with Kermit's FTP client. It chugged along for a while until I stopped it; it would have taken hours to complete. There is no indication that it wouldn't have worked, assuming the FTP server could also handle long files, which who knows. Anyway, Kermit showed all the right data on the display screen. 17 Aug 2005. On AMD x86_64 and IA64 native 64-bit Linux builds, the pty routines did not work at all. ptsname() dumped core. If I commented out ptsname(), then the next thing dumped core. The same code works on the other 64-bit builds. Poking around, I see that this version of Linux has an openpty() function, which I could try using instead of the current API -- grantpty(), etc. Then I see that openpty() is already coded into Kermit's pty module, conditionalized under HAVE_OPENPTY, which has never before been defined for any build. I added a test to the makefile linux target (look for the openpty() prototype in , if found define HAVE_OPENPTY as a CFLAG and also add -lutil to LNKFLAGS). Works fine on the problem builds, and also on previously working 32-bit builds. makefile, 17 Aug 2005. Fixed a bug in the ASKQ echo asterisks code, which made the VMS version of C-Kermit always echo asterisks. Turns out that some code in the main parse loop to reset command-specific flags was in the wrong place, which had other effects too, for example ASKQ temporarily turns off debug logging as a security measure, but the code to turn it back on was skipped in most cases. Some other side effects related to the DIRECTORY and CD commands might have been possible but I haven't seen them. ckuus[56].c, 23 Aug 2005. Problem reported when sending a file to VMS when the name in the F packet starts with a device specification and does not include a directory field, and PATHNAMES are RELATIVE. Example: dsk:foo.bar becomes f_oo.bar. The code assumes that if there is a device field, it is followed by a directory field, and it inserts a dot after the '[', which in this case is not there. Later the dot becomes '_' because of the only-one-dot rule. Solution: only insert the dot if there really is an opening bracket. nzrtol(): ckvfio.c, 23 Aug 2005. A report on the newsgroup complains that C-Kermit and K95 servers were sending REMOTE DIR listings with only #J line terminators, rather than #M#J. Yet all the other REMOTE xxx responses arrived with #M#J. snddir() was neglecting to switch to text mode. ckcfns.c, 26 Aug 2005. Back to long files. What happens if 32-bit Kermit is sent a long file? It gets an A-packet that looks like this: ^A_"A."U1""B8#120050815 18:28:03!'42920641*4395073536,#775-!7@ )CP The 32-bit receiver reacts like so: gattr length[4395073536]=100106240 the first number being the string from the A-packet, the second being the value of the long int it was converted to by atol(). Clearly not equal in this case. When this happens Kermit should reject the file instead of accepting it and then getting a horrible error a long time later. Added code to gattr() to convert the result of atol() back to a string and compare it with the original string; if they're not equal, reject the file on the assumption that the only reason this could happen is overflow. Also some other code in case the sender sends the only LENGTHK attribute. Now files whose lengths are too big for a long int are rejected right away, provided the sender sends the length in an A packet ahead of the file itself. If this new code should ever cause a problem, it can be bypassed with SET ATTRIBUTE LENGTH OFF. ckcfn3.c, 26 Aug 2005. As I recall from when I was testing this a few weeks ago, when the too-big length is not caught at A-packet time, the transfer fails more or less gracefully when the first attempt is made to write past the limit. I went to doublecheck this by sending a big file from the 64-bit Solaris10 version to a 32-bit Mac OS X version that does not have today's code. The Mac thinks the incoming file is 2GB long when it's really 4GB+. But in this case, something new happens! Although the percent done and transfer rate go negative, the file keeps coming. It would seem that Mac OS X lets us create long files without using any special APIs. The transfer runs to completion. Mac OS X Kermit says SUCCESS (but gets the byte count and cps wrong, of course). But then a STATUS command says FAILURE. The file was, however, transferred successfully; it is exactly the same length and compares byte for byte with the original. This tells me that in the Mac OS X version -- and how many others like it??? -- today's rejection code should not be enabled. Meanwhile I put today's new code in #ifndef NOCHECKOVERFLOW..#endif, and defined this symbol in the Mac OS X 10.4 target. Over time, I'll have to find out what other platforms have this characteristic. And of course I'll also have to do something about file-transfer display, statistics, and status. makefile, ckcfn3.c, 26 Aug 2005. From now on I'm going to bump the Dev.xx number each time I upload a new ckdaily. This one will be Dev.02. ckckmai.c, 26 Aug 2005. Got rid of all the extraneous FreeBSD 4 and 5 build targets. Now there's one (freebsd) for all FreeBSD 4.1 and later. makefile, 27 Aug 2005. Mac OS X 10.4 (Tiger) is a 64-bit OS. Building C-Kermit 0n 10.4.2 without any special switches stilll gives a 32-bit executable. Ditto building with -mpowerpc64. Further investigation turned up a tip sheet on MySQL that says you have to include all of these: -mpowerpc64 -mcpu=G5 -mtune=G5 -arch ppc64. That did the trick. New makefile target: macosx10.4_64. But the 10.4.2 system I tried did not have 64-bit [n]curses or resolv libs, so this build has no -DNOCURSES -DNO_DNS_SRV. makefile, 27 Aug 2005. Created a symbol CK_64BIT to indicate true 64-bit builds at compile time. Added 64-bit announcement to the startup herald and the VERSION text. ckcdeb.h, ckuus[r5].c, 27 Aug 2005. Added a built-in variable \v(bits) to indicate the size of the build (16, 32, 64, or whatever else sizeof() might report). ckuusr.h, ckuus4.c, 27 Aug 2005. Got rid of all the warnings in 64-bit Mac OS X about args to getsockopt(), getsockname(), and getpeername(), and the comparisons on the return value of inet_addr(). ckcnet.[ch], 27 Aug 2005. Now to check the effects on other builds... Linux on AMD64: ok. Linux on IA64: ok. Linux on i386: ok. Mac OS X 10.3.9 32-bit: ok. Solaris 10 64-bit: ok. Solaris 9 32-bit: ok. Tru64 4.0F: ok. FreeBSD 4.11: ok. FreeBSD 5.4 ia64 (64-bit): ok. FreeBSD 5.4 i386 (32-bit): ok. The Tru64 5.1B build totally blew up because they have their own unique sockopt/etc length-argument data type (int!), so I had to roll back on using socklen_t for this in all 64-bit builds. Checked to make sure it still builds on Tru64 4.0F after this change (it does). ckcnet.h, 27 Aug 2005. The HP-UX 11i/ia64 build comes out to be 32-bit but thinks it's 64-bit. CK_64BIT is set because __ia64 is defined. So how do I actually make a 64-bit HP-UX build? I tried adding +DD64 to CFLAGS, and this generates 64-bit object files but linking fails to find the needed 64-bit libs (e.g. -lm). For now I added an exception for HPUX to the CK_64BIT definition section. ckcdeb.h, 27 Aug 2005. Took the time to verify my recollection about the "graceful failure" on a regular Pentium Linux system when receiving a too-big file... OK, it's not exactly graceful. It gets a "File size limit exceeded" error; the message is printed in the middle of the file-transfer display, apparently not by Kermit, and Kermit exits immediately. Looks like a trap... Yup. "File size limit exceeded" is SIGXFSZ (25). What happens if we set it to SIG_IGN? Just the right thing: The receiver gets "Error writing data" at 2147483647 bytes, sends E-packet to sender with this message, and recovers with total grace (drains packet buffers, returns to prompt). ckutio.c, 27 Aug 2005. Backed off from rejecting a file because its announced size overflows a long. Now instead, I set the file size to -2 (a negative size means the size is unknown, but we have always used -1 for this; -2 means "unknown and probably too big"). In this case, the f-t display says: File Size: POSSIBLY EXCEEDS LOCAL FILE SIZE LIMIT then the user can interrupt it with X or whatever, or can let it run and see if maybe (as in the case of Mac OS X) it will be accepted anyway. This way, we skip all the bogus calculations of percent done, time remaining, etc. ckcfn3.c, ckuusx.c, 27 Aug 2005. Discovered that VMS C-Kermit on Alpha and IA64 is a 32-bit application; sizeof(long) == sizeof(char *) == 4. Tried adding /POINTER_SIZE=64 to VMS DECC builds on Alpha and IA64, but the results aren't great. Tons of warnings about pointer size mismatches between Kermit pointers and RMS ones, and the executable doesn't run. It appears that access to long files would require a lot of hacking, similar to what's needed for 32-bit Linux. --- Dev.02: 27 Aug 2005 --- From Jeff, 28 Aug 2005. . Fix SSH GLOBAL-KNOWN-HOSTS-FILE / USER-KNOWN-HOSTS-FILE parsing, ckuus3.c. . Pick up K95STARTFLAGS from environment, ckuus4.c. . Fix some typos in command-line processing (-q), ckuus4.c. . Be sure to suppress herald if started with -q, ckuus7.c. . Fix ssh command-line switches, ckuusy.c. Eric Smutz complained that HTTP POST was adding an extraneous blank line, which prevented his application from successfully posting. RFC 2616 states (in Section 4.1): In the interest of robustness, servers SHOULD ignore any empty line(s) received where a Request-Line is expected. In other words, if the server is reading the protocol stream at the beginning of a message and receives a CRLF first, it should ignore the CRLF. Certain buggy HTTP/1.0 client implementations generate extra CRLF's after a POST request. To restate what is explicitly forbidden by the BNF, an HTTP/1.1 client MUST NOT preface or follow a request with an extra CRLF. This seems pretty clear. One section of code in http_post() (just above the postopen: label) was appending a CRLF to a buffer whose last already was terminated by CRLF, and then appended a second CRLF; thus two empty lines. I removed the second one. ckcnet.c, 28 Aug 2005. I looked into the 64-bitness of NetBSD, it seems to be like Linux and FreeBSD on 64-bit hardware, i.e. you just build it there and it works, at least on Alpha and AMD64, going back to NetBSD 1.4 or 1.5. But I don't have access to any of these for verification and documentation on the Web is scanty. Checked PeterE's complaint again of warnings in ckutio.c about parameter list of get[ug]id() and gete[ug]id(). When I "make hpux1100o" on HP-UX 11.11 (PA-RISC), there are definitely no warnings. He says the same thing happens on 10.xx, but I don't have access to that any more. I also did "make hpux1100o" on HP-UX 11.23 (11i v2) (PA-RISC), also no warnings. (Except in both cases, a warning about a comment within a comment in /usr/include/sys/ptyio.h). On HP-UX 11i v2 on Itanium, however, there are TONS of warnings, mostly of the "variable set but never used" kind. Also "dollar sign used in identifier". Tracking this last one down, I see it's complaining about code that's in #ifdefs for other platforms, such as Apollo Aegis. Is "aegis" defined in HP-UX 11i v2/IA64? No! (It would show up in SHOW FEATURES if it was.) Some phase of the compiler is complaining about code that it should be skipping (and that, in fact, it *is* skipping it because the build is successful). It's as if cc is running lint for me but not telling lint which macros are defined and which are not. Verified that 64-bit linking fails in the same way for HP-UX 11i v2 on both IA64 and PA-RISC. Sent a query to HP. Compiling ckcnet.c and ckcftp.c got the familiar sockopt-related warnings on HP-UX 11i v2; turns out it is just like Tru64 Unix in using an int for the length argument. Added another special case and the warnings went away. ckcnet.h, 28 Aug 2005. Added some stuff to SHOW FEATURES to see what kinds of macros are exposed (e.g. INT_MAX, LONG_MAX, LLONG_MAX, etc) and also show sizeof(long long) and sizeof(off_t). Building this code all over the place will give me an idea of how widespread these data types are, and to what extent I can tell whether they are available from clues in the header files. (At first glance, it appears that I'm not picking up , but adding an #include for it is just asking for trouble.) No complaints about long long or off_t from Solaris 9 or recent Linuxes. ckuus5.c, 28 Aug 2005. Fixed a warning in HP-UX 10 and 11 stemming from some old-style prototypes in ckutio.c for get[re][gu]id(). ckutio.c, 29 Aug 2005. Updated minix3 target from Andy Tanenbaum. makefile, 29 Aug 2005. PeterE confirms that "long long" and off_t are available in all HP-UX 10 and 11, and in HP-UX 9 on PA-RISC but not Motorola. 30 Aug 2005. Got 64-bit builds to work on HP-UX. According to my notes, John Bigg of HP said (in 1999) that HP-UX 10.30 and later require PA-RISC 1.1, and do not work on PA-RISC 1.0. But is PA 1.0 64-bit or what? Today, Alex McKale of HP said "The 64-bit binaries will work on all machines that have the same or later release of HP-UX (excluding PA-RISC 1.1 machines)". Still need clarification... Maybe it's that all IA64 builds can be 64-bit but I need dual builds for PA-RISC. Meanwhile I started transfer of a 4GB+ file from Solaris to HP-UX 11i but it exceeded some quota on the HP long before it approached the 2G point. It failed cleanly and up until then it was working fine (numbers, stats, etc). 30 Aug 2005. Support of large files in 32-bit builds began in 10.20. 64-bit application support began in 11.00, but not all machines that run 11.00 support 64 bits. About long files, see HP /usr/share/doc/lg_files.txt. PeterE found that certain patterns can still make Kermit loop; example: if match T01011-00856-21-632-073 *[abc] { echo GOOD } else { echo BAD } if match T01011-00856-21-632-073 *[a-z] { echo GOOD } else { echo BAD } The minimum offending pattern is * followed immediately by an [xxx] construction, followed by anything else, including nothing. Previous versions of Kermit handled this one correctly, without looping (but failed certain matches that should have succeeded). The new section of code I added on 15 June, upon failure to match, advances the string pointer and backs up the pattern to the previous pattern, and starts again (recursively). However, there needed to be a corresponding check at entry for an empty target string. ckmatch(): ckclib.c, 12 Sep 2005. PeterE discovered that "kermit -y filethatdoesnotexit" gives an erroneous error message that names the user's customization, rather than the name given on the command line. doinit(): ckuus5.c, 12 Sep 2005. FREAD does not get an error if it tries to read a record or file or piece of file that is too big for its buffer. In particular, FREAD /SIZE:xxx seems to succeed even if less than xxx was read. It should fail unless, perhaps, it successfully read up to the end of the file. Furthermore, if xxx is bigger than the file buffer size, it should complain. The buffer is line[LINBUFSIZ], 32K. The lack of failure was due to code in dofile() that adjusted the given size silently if it was greater than the buffer size, which I removed, and also added a check when parsing the /SIZE: switch. dofile(): ckuus7.c, 12 Sep 2005. That still didn't help with FREAD /SIZE:n returning less than n bytes, even when they were available. That's because the underlying routine, z_in(), didn't check fread()'s return code, which is the number of bytes read. If fread() has smaller buffers, it needs to be called in a loop. z_in(): ckuus7.c, 12 Sep 2005. Flen() fails on strings of length 8192 or more. The limitation is in the callers of zzstring, which seem to be specifying an 8K buffer, in this case fneval(). The operable symbols are FNVALL (max length of value returned by a function) and MAXARGLEN (maximum length of an argument to a function). I changed both of these for BIGBUFOK builds to be CMDBL. Buffers can never be infinite, there has to be a limit. It's important to make everything work consistently within that limit, and to make something useful happen when the limit is exceeded. At this point, I can probably also increase the limits for modern 32-bit systems, and certainly for 64-bit ones. Also there's no point in worrying about 16-bit platforms any more; earlier C-Kermit versions can still be used on them if necessary. ckuusr.h, 12 Sep 2005. Special #ifdefs for finding resolv.h and nameser.h in MINIX3 from Andy Tanenbaum. ckcnet.c, 20 Sep 2005. PeterE noticed that ckmatch(), even though it works pretty well now, does a lot of extra and unnecessary recursion after determining the string and pattern do not match, at least when the pattern is of the form *[abc]. After several false starts I was able reduce this effect to a minor level (but not eliminate it all together) by changing a while loop into a do loop. ckmatch(): ckclib.c, 15 Oct 2005. Added -DNOLONGLONG to HP-UX 8.00 and earlier builds, and to Motorola-based HP-UX 9.00 builds. This is simply to inhibit the test for whether "long long" is supported by the compiler, since when it isn't, the module containing the test won't compile. makefile, ckuus5.c, 16 Oct 2005. Making ASKQ always echo asterisks is a bad idea, because when it doesn't echo, it's the perfect way to read silently from stdin, e.g. in a CGI script (INPUT can also be used for this but it's not as straightforward). So I put the default for ASKQ back to no echoing, then gave ASKQ its own switch table, which is the same as for ASK with the addition of an /ECHO:x switch, which tells what character to echo. ckucmd.c, ckuus[26].c, 17 Oct 2005. Fixed a bug in FTP GET /COMMAND filename commandname; it always dumped core dereferencing a null string (the nonexistent local asname). ckcftp.c, 17 Oct 2005. For docs: if you don't like the funny business that happens when you type an IF command at the prompt, use XIF instead and it won't happen. Also note that commands like "if xxx { echo blah } else { echo blah blah }" don't work when typed at the prompt; you have to use XIF for this. Back to ckmatch()... Under certain conditions (e.g. patterns like *[abc]) failure to match would not stop the recursion because the string and pattern arguments are on the stack, as they must be, so there was no way for level n-1 to know that level n had detected a definitive nonmatch and that no further attempts at matching were required. The right way to handle this is to recode the whole thing as coroutines, the cheap way out is with a global static flag. Works perfectly, in the sense that the match.ksc test results are identical to what they were before and the extra backing up and recursion are eliminated. (The Oct 15th fix wasn't really a fix, it broke a couple of cases.) ckclib.c, 20 Oct 2005. ckuus7.c(2987): warning #267: the format string requires additional arguments (in PURGE command); fixed 20 Oct 2005. From Andy Tanenbaum, final changes for MINIX3: #ifdef out the inline definitions for gettimeofday() and readlink(). ckutio.c, 23 Oct 2005. From Jeff: struct gss_trials initializers changed from gss_mech_krb5 to ck_gss_mech_krb5. ckcftp.c, 23 Oct 2005. From Jeff: some improvements to K95 GUI SHOW TERMINAL. ckuus5.c, 23 Oct 2005. Found and corrected some misplaced #ifdefs in shofeat(), ckuus5.c, 23 Oct 2005. --- Dev.03 --- Fixed a compiler warning in a debug() statement in zzstring() by adding parens. ckuus4.c, 24 Oct 2005. Added -DNOLONGLONG to sv68r3v6 target, makefile, 25 Oct 2005. New makefile targets for HP-UX from PeterE to handle the 'long long' situation. 26 Oct 2005. From Jeff: changes to support OpenSSL 0.9.8, ck_ssl.h. ckcasc.h has had short names defined for ASCII control characters for 20-some years but now they are causing conflicts, so EM becomes XEM (also for OpenSSL 0.9.8). Changed K95's default terminal type from VT320 to VT220 because VT320 termcaps/terminfos are disappearing from Unix hosts: ckuus7.c. Reorganize the data-types section of SHOW FEATURES to add more macro tests for integral sizes and to provide for the proper printf formatting in order to allow the sizes to be output ("You are going to need to be careful because %llx is not supported on all platforms. On Windows, it is the same as %lx, 32 bits"): ckuus5.c, 26 Oct 2005. Defined NOLONGLONG ckcdeb.h for various old platforms where we know we are never going to need 64-bit ints (even if they support a long long datatype, chances are pretty slim they supported 64-bit file sizes). ckcdeb.h, 26 Oct 2005. PeterE noticed that GOTO targets can only be 50 characters long. This was by design, a long time ago, on the assumption that nobody would make longer labels. But in SWITCH statements, case labels can be variables that expand to anything at all. If we chop them off at 50, we might execute the wrong case. Changed the maximum label size to be 8K, and added code to dogoto() to check when a label or target is too long and fail, to prevent spurious GOTO or SWITCH results. ckuusr.h, ckuus[r6].c, 26 Oct 2005. Testing revealed there was still a problem with SWITCH case labels that were variables that expanded into long strings. Turns out that I was being too clever when I decided that, if the SWITCH macro was n1 characters long and the case-label search target was n2 characters long, I only had to search the first n1-n2+1 characters of the macro definition. That was true before I allowed case labels to be variables, but not any more! Fixed in dogoto(): ckuus5.c, 26 Oct 2005. --- Dev.04 --- Dev.04 didn't actually contain Jeff's data-type changes to shofeat(), I think I saved the wrong buffer in EMACS... Fixed now. 27 Oct 2005. PeterE corrected a typo in the HP-UX 7.00 makefile target. 27 Oct 2005. PeterE had been reporting problems stress-testing the new SWITCH code, but only on HP-UX 9, primarily stack overrun. Turns out to be the HP-UX 9 optimizing compiler's fault. No optimization, no problems. PeterE found that even when dogoto() detects a string that is too long and fails, this does not stop SWITCH from producing a result, which can not possibly be trusted. Changed the part of dogoto() that handles this to not just fail, but also to exit the script immediately and return to top level. ckuus6.c, 28 Oct 2005. An idea popped into my head after having typed too many commands like "dir ck[cuw]*.[cwh]" to check the list of matching files, and then having to retype the same filespec in a SEND command: Why not unleash some unused control character such as Ctrl-K to spit out the most recently entered input filespec? It was easy, just a few lines in cmifi2() and gtword(), plus a couple declarations. To see all the changes, search for "lastfile" (all the new code is protected by #ifndef NOLASTFILE). ckucmd.c, 28 Oct 2005. I added a new variable \v(lastfilespec) that expands to the same last filespec, for use in scripts. ckuusr.h, ckuus4.c, 28 Oct 2005. The Unix version of C-Kermit failed to put anything in the session log if SET TERMINAL DEBUG ON. Rearranged the pertinent clause so logging happens independent of TERMINAL DEBUG. For now, since the user who noticed this wanted debug format to go into the session log, that's what I do. The alternative would be to just log the raw incoming stream as usual, or to add Yet Another SET Command to choose. ckucns.c, 11 Nov 2005. Fixed HELP INTRO text. ckuus2.c, 11 Nov 2005. Added NOLONGLONG for SV68. ckcdeb.h, 11 Nov 2005. --- Dev.05 --- Added a debug() statement in FTP secure_getbyte() to see what's going on with Muhamad Taufiq Tajuddin's 205-byte-per-second FTP/SSL downloads. --- Dev.06 --- Result: nothing, SSL_get_error() does not report any errors. Suggested testing SSL_read()'s return code, if 0 don't update the screen. Created a new data type CK_OFF_T in ckcdeb.h that will eventually resolve to whatever each platform uses for file sizes and offsets. ckcdeb.h, 17 Nov 2005. Made a new library routine ckfstoa() that converts a file size or offset to a string. This is to solve the problem with having to use different printf() formats for different representations of file size (int, long, long long, off_t, signed, unsigned, etc). Replaced a few printf("%l",size) with printf("%s",ckfstoa(size)) with the expected results. This is just a start, the definitions will need adjustment for many platforms, variables need to be redeclared, and all the offending printf's (and printw's) will have to hunted down and converted. ckclib.[ch], ckuus4.c, 17 Nov 2005. Built a minimal version on Linux with: make linux "KFLAGS=-DNOLOCAL -DNOICP -DNOCSETS -DNODEBUG" Worked fine, result was 260K on i686. 21 Nov 2005. Discovered that Kermit's date parser, contrary to the documentation, failed to handle strings like "Wed, 13 Feb 2002 17:43:02 -0800 (PST)", which are commonly found in email. This was because of an overzealous and misguided check in the code; once removed, all was well. ckucmd.c, 26 Nov 2005. Added a new format code 4 to \fcvtdate() to emit asctime() format, used in BSD-format email message envelopes (i.e. the "From " line). shuffledate(), ckucmd.c, ckuus[24].c, 26 Nov 2005. Added a new function \femailaddress(). Given a From: or Sender: header line from an RFC2822-format email address, extracts and returns the actual email address, such as kermit@columbia.edu. ckuusr.h, ckuus[42].c, 26 Nov 2005. Using the new functions, I wrote a script to fetch mail from a POP3 server over a TLS connection. But the line-at-a-time input (needed for changing line terminators and byte-stuffing text lines that start with "From ") is slow, 17 sec to read 29 messages totaling 175K. Added INPUT /CLEAR so INPUT can be started with a clean buffer without requiring a separate CLEAR INPUT command. ckuusr.h, ckuus[r24].c, 27 Nov 2005. One thing that INPUT was never able to do well was read and save the complete incoming data stream. That's because, while waiting for its target, the buffer might overflow wrap around. Yet there was never a way to tell it to stop when its buffer fills up and let me save it. I added a /NOWRAP switch that does this. If the buffer fills up before any other completion criterion is met, INPUT returns failure, but with \v(instatus) set to 6 (the next available instatus value). Thus a program that wants to read and save (say) an email message from a POP server, which could be any length at all, and which terminates with . could do this: set flag off while open connection { input /nowrap 10 \13\10.\13\10 # Wait for . if success { frwrite /string \%o {\freplace(\v(input),\13\10.\13\10,\13\10)} set flag on break } else if ( == \v(instatus) 6 || == \v(instatus) 1 ) { frwrite /string \%o {\v(input)} continue } break } if flag (handle success) Note carefully the braces around the FWRITE text; without them, trailing spaces would be lost. Previously the only way to INPUT an entire data stream without losing anything (assuming it was ordinary lines of text that were not "too long"), was line-by-line: while open connection { input /clear 10 \13\10 if fail break if eq "\v(input)" "$ \13\10" break fwrite /string \%o {\freplace(\v(input),\13\10,\10)} } The new code is 3 times faster using the default INPUT buffer length of 4K. Raising it to 16K makes it 3.6 times faster (not worth it). Changing the POP3 script to use INPUT /NOWRAP makes it about twice as fast (it does more; it has to do all the byte-stuffing and unstuffing). 27 Nov 2005. Changed ssl_display_xxx() to just return if SET QUIET ON. Otherwise there is no way to suppress the messages. Also protected a previously unprotected printf("[SSL - OK]\r\n"); by if ( ssl_verbose_flag ). ck_ssl.c, 28 Nov 2005. Discovered that FOPEN /APPEND doesn't work if the file doesn't exist. It uses cmiofi() which is a super-hokey front end to cmifi2(). I had code to call it but for some reason it was commented out, with a note to the effect it didn't work. I uncommented it but that didn't help much. So I wrote an entirely new cmiofi() that works exactly as it should, using chained FDBs, _CMIFI to _CMOFI (I think the original cmiofi() predated chained FDBs). ckuus7.c, ckucmd.c, 29 Nov 2005. Getting rid of the awful hacks required to call cmiofi() meant I also had to change the EDIT command, which is the only other place where it's used. Unfortunately now it's no longer possible to give EDIT without a filename (to just start an empty editor) but I doubt anyone will notice. ckuusr.c, 29 Nov 2005. IF KERBANG didn't always work right. If a kerbang script TAKEs another kerbang script, the second one should have IF KERBANG false, but it didn't. Added a check for \v(cmdlevel) == 1. Now you can write a wrapper that runs a kerbang script in a loop, and the latter can use IF KERBANG to know whether to EXIT (if called at top level) or END (if called by another script, thus allowing -- in this case -- the loop to continue). ckuus6.c, 29 Nov 2005. Changed \flop() and flopx() functions to take a third argument, a number signifying at which occurrence of the break character to lop, so: \flopx(sesame.cc.columbia.edu) = edu \flopx(sesame.cc.columbia.edu,,2) = columbia.edu ckuus[24].c, 1 Dec 2005. Built OK on VMS 7.2-1 with MultiNet 4.4. Built with and without OpenSSL on Linux OK, ditto Solaris 9. Built OK on RH Linux AS4 on X86_64 (64-bit); "show var fsize" (using new ckfstoa()) works OK there. Also Mac OS X 10.3.9 (32-bit), Tru64 UNIX 4.0F (64-bit), HP-UX 11iv2 (64-bit) (picky new compiler spews out tons of useless warnings), FreeBSD 6.0 on ia64 (64-bit). --- Dev.07 --- Changed "make netbsd" to be a synonym for "make netbsd2" because the original netbsd target was ancient. Renamed it to netbsd-old. makefile, 3 Dec 2005. Updated INPUT and MINPUT help text. ckuus2.c, 3 Dec 2005. Discovered that on a SET PORT /SSL connection, Kermit treats incoming 0xff data bytes (e.g. sent from the POP server) as IACs and goes into Telnet negotiations. Jeff says "You will need to implement NP_SSLRAW and NP_TLSRAW that do the same as NP_TCPRAW but negotiate SSL or TLS as appropriate." This was not as easy as it sounded, because apparently a lot of the Telnet code is used by SSL and TLS even when Telnet protocol is not being executed. I wound up doing this as follows: I added /SSL-RAW and /TLS-RAW to the switch table. Rather than disable Telnet, they do exactly what the /SSL and /TLS switches do, but also set a special flag. This flag is checked in only two place: netclos() (to prevent Kermit from sending TELNET LOGOUT when closing the connection), and tn_doop() (to prevent Kermit from reacting to incoming IACs; it makes tn_doop() return(3), which means "quoted IAC", which causes the caller to keep the IAC as data). ckcnet.h, ckctel.h, ckctel.c, ckuus7.c, 4 Dec 2005. The INPUT command did not account for tn_doop() returning 3. Fixed in doinput(), ckuus4.c, 4 Dec 2005. Added another debug() statement in FTP secure_getbyte() to see what's going on with Muhamad Taufiq Tajuddin's 205-byte-per-second FTP/SSL downloads, plus new code to test SSL_read()'s return code (byte count); if 0 don't update the screen. ckcftp.c, 4 Dec 2005. --- Dev.08 --- Fixed a typo in the non-ANSIC definition of ckfstoa(). ckclib.c, 7 Dec 2005. Our Ctrl-C trap (the ON_CTRLC macro) wasn't working for kerbang files. Rearranged some code to make it work. ckcmai.c, 8 Dec 2005. Started converting code to use CK_OFF_T for file sizes and offsets, and all [s]printf's to replace "%ld" or whatever with "%s", and the size variable with a call to ckfstoa(). Since I haven't actually changed the definition of CK_OFF_T from what all the size variables were to begin with (i.e. long), it shouldn't do any harm. So far just ckcfn3.c 10 Dec 2005. An updated HP-UX 9.xx makefile target from PeterE to fix a core dump that happens on that platform due to insufficient resources. 14 Dec 2005. Added debug() statements to http_blah() routines to tell whether the connection is "chunked". There seems to be a bad performance problem. ckcnet.c, 14 Dec 2005. PeterE complained about ugly DIRECTORY error message, ?No files match - "{blah}". The braces are used internally in case the user typed more than one filespec. I changed the error message to remove them. Ditto DELETE. ckuus6.c, 15 Dec 2005. The problem with HTTP downloads is that Kermit always does single-character read() or socket_read() calls (or the SSL equivalent); see http_inc(). I added buffering code for non-SSL connections only but it's gross because it has to swap ttyfd and httpfd before calling nettchk(). I tried making a nettchk() clone that accepts a file descriptor as an argument but it didn't work because too many other routines that are invoked directly or implicitly by nettchk() (such as in_chk()) are still hardwired to use ttyfd. HTTP GETs are now 20 times faster on the local network (the improvement is less dramatic over a clogged Internet). ckcnet.[ch], 15 Dec 2005. --- Dev.09 --- HTTP file-descriptor swapping is not thread safe. Doing it right, of course, is a big deal, so for now I just don't define HTTP_BUFFERING for Windows. ckcnet.c, 15 Dec 2005. Noticed that HTTP not included in FreeBSD and OpenBSD builds. Fixed in ckcdeb.h, 22 Dec 2005. Fleshed out 32/64-bit data type definitions and changed struct zattr (file attribute structure) members length and lengthk to have the new CK_OFF_T type. Changed final arguments of debug() and tlog() to be the new LONGLONG type. ckcdeb.h, 22 Dec 2005. Changed ckfstoa() to return a signed number in string form, rather than an unsigned one. That's because off_t is signed (thank goodness). Added the inverse function, ckatofs() so we can convert file sizes and offsets back and forth between binary number and string. ckclib.c, 22 Dec 2005. Changed Attribute Packet reader to convert incoming file size attribute with ckatofs() rather than atol(). ckcfn3.c, 22 Dec 2005. Converted debug(), tlog(), ckscreen(), etc, to handle potentially "long long" arguments by making their "n" argument CK_OFF_T. ckuusx.c, ckcdeb.h, 22 Dec 2005. Converted the rest of the source files to use CK_OFF_T for all file size and offset and byte-count related variables, and converted all references to these variables in printfs to go through ckfstoa(). Then I built it on Linux/i386 with: make linux "KFLAGS=-D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64" which makes off_t be 64 bits and magically makes all the regular file APIs use 64-bit sizes and offsets without changing the API calls in the source code. It's going to be a lot of work to get through all the kinks but I was able to send a long file, do directory listings of long files, do \fsize(longfile), etc. When it sends a file, the length is shown correctly in the A packet. If the receiver does not support big numbers, it receives the file OK anyway, without showing the size, the thermometer, or percent done (and then will get an error when the file keeps coming after the 2G mark). Kermit 95 actually refuses long files for "Size", but only if the announced is less than 2^63 bytes. When today's Linux version receives a file, it shows the length correctly in the file-transfer display, as well as percent done, thermometer, etc. Also built this version on true 64-bit Linux, and it worked fine. Many files changed, 22 Dec 2005. For the record, this API is specified in X/Open's Single UNIX Specification Version 2, which is branded as UNIX 98. It is called Large File Support, or LFS, and was developed at the Large File Summit. It looks like the operative feature-test macro in glibc for transitional large file support is __USE_LARGEFILE64. So if this is defined, we can also supply _LARGEFILE_SOURCE and _FILE_OFFSET_BITS=64 automatically for 32-bit Linux builds. But there's a Catch-22, you don't know if this is defined until you read the header files, but you have to define _LARGEFILE_SOURCE and _FILE_OFFSET_BITS before you read the header files. Maybe it's good enough to grep through for __USE_LARGEFILE64. makefile, 23 Dec 2005. Checked this on true 64-bit Linux. The same symbols are defined in CFLAGS, but they do no harm; it builds without complaint and works fine. 24 Dec 2005. Built it on Red Hat Linux 6.1 from 1999. This picked up the long file support too. Guess 6.1 isn't old enough to not have it! Kermit seems to work OK on regular files but I don't have enough disk space to create a long file, and my bigfile.c program (which creates a long file containing only 1 byte) doesn't work ("fseeko: invalid argument"). It looks like parts of this API were visible in Linux before they were actually working. 24 Dec 2005. Converted all fseek() and ftell() to macros that expand to fseek() and ftell() or fseeko() and ftello() depending on whether _LARGEFILE_SOURCE is defined. ckufio.c, ckuus7.c, ckuusx.c, 24 Dec 2005. Made a CK_OFF_T version of cmnum(). It would be a very big deal to just change cmnum() to return a new type, so another idea is to rename cmnum() to something else, cmnumw(), change its result argument to CK_OFF_T, and then make a stub cmnum() to call it to get an int, then call cmnumw() explicitly any time we need a big number. ckucmd.c, 24 Dec 2005. Calling cmnumw() directly requires changes to each routine that uses it. The INCREMENT and DECREMENT commands, for example, required changes to doincr(), varval(), and incvar(), and all references to them. ckuusr.[ch], ckuus[56].c, 24 Dec 2005. Calling cmnumw() in chained FDBs required defining a new function code, _CMNUW, adding a new member to the OFDB struct for returning wide results, and adding a new case to cmfdb(). ckucmd.[ch], 24 Dec 2005. Changed FSEEK and FCOUNT to use the new chained FDB interface, now we can seek and look past 2GB. ckuus7.c, 24 Dec 2005. Next come switches, which store their results in a struct stringint. This struct was defined in each module where it was used (ckuus[r367].c, ckcftp.c). I moved the definition to ckuusr.h and added a wval member, which can be referenced by any switch-parsing code that calls cmnumw(). 24 Dec 2005. Changed SEND /CALIBRATE:n to allow big values of n. This makes it possible to test the protocol aspects of long-file transfer without actually having a long file handy. ckuusr.c, 24 Dec 2005. SEND /SMALLER-THAN:n, SEND /LARGER-THAN:n, and and SEND /START:n also now allow large values of n. ckuusr.c, 24 Dec 2005. Changed the algebraic expression evaluator to use wide values. ckuus5.c, 24 Dec 2005. Fixed ckfstoa() to handle the case when n is negative and (0 - n) is also negative, which happens for numbers 2^(n-1) or greater, where n is the number of bits in the word size we're dealing with, e.g. 64, in which case 2^63 has its sign bit set so seems to be negative. In such cases, ckfstoa() returns "OVERFLOW" instead of a numeric string. We'll have to see how this plays out but I think it's better to cause a parse error and stop things dead than to return a spurious number. ckclib.c, 24 Dec 2005. Converted the S-Expression handler to use wide integers. ckuus3.c, 24 Dec 2005. Took all the LONGLONG stuff out of ckcdeb.h, we don't need it. All of these changes result in 64-bit arithmetic (more or less) on 32-bit Linux, as well as on true 64-bit platforms. Rebuilt today's code on Solaris 9 in the 32-bit and 64-bit worlds, on Red Hat 6.1, Red Hat AS4.2. I haven't bothered trying a 32/64 hybrid build for Solaris, since I can build a pure 64-bit version there. Quick tests show the large-number arithmetic works OK in all cases except, of course, on pure 32-bit builds (unfortunately I can't find a running Linux system old enough to verify this for Linux, but it's true for other 32-bit platforms). 24 Dec 2005. Tried building a hybrid version on Solaris 9 after all since the LFS API is ostensibly the same as for Linux: make solaris9 "KFLAGS=-D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64" It built smoothly and the resulting binary is 2.5MB compared to 3.4MB for the 100% 64-bit version. Looks like a keeper. For now, added solaris9lfs and solaris10lfs entries to the makefile but if these work on PCs we can make these the regular entries for Solaris 9 and 10. 27 Dec 2005. Built on Mac OS X 10.4 with the regular target. It seems that in that case, off_t is 64 bits anyway. Noticed that a lot of stuff didn't work, like exponentiation in S-Expressions. Tried building it as above, which worked, and now CK_OFF_T is 64 bits instead of 32, but (^ 2 30) is still 2.0. In fact 2-to-the-any-power is 2.0. It seems that the Mac OS X version did not have FNFLOAT defined. It also seems that every test in dosexp() like: if (result != fpresult) fpflag++; should have been protected by #ifdef FNFLOAT..#endif /* FNFLOAT */ -- a double-ended break, as they say in the nuclear power industry. ckuus3.c, 27 Dec 2005. Added GREP /EXCEPT:pattern. ckuus[26].c, 27 Dec 2005. Fixed a problem with uninitialized pv[].wval (switch-parsing parameter-value) members that showed up on certain platforms or with certain compilers. Now the Mac OS X 10.4 version works. ckuus[r367].c, ckcftp.c, 28 Dec 2005. Built on Unixware 7.1.1, a pure 32-bit build, seems fine. Rebuilt on Red Hat AS 4.2 just to make sure I didn't break anything, it's OK. No testing on HP-UX, etc, because HP testdrive file system is full, can't upload anything. 29 Dec 2005. Commented out the SHOW FEATURES section that displays constants like INT_MAX, CHAR_MAX, etc, because printing each value in the appropriate format is too tricky, and we don't need them anyway. ckuus5.c, 29 Dec 2005. Updated ckvfio.c to use CK_OFF_T for the relevant variables. Built and tested on VMS/Alpha 7.2: file transfer in remote mode; making a Telnet connection and then local-mode file transfer; S-Expressions, all OK. Also built a no-net version OK. 29 Dec 2005. Built and tested on Red Hat AS4 AMD X86_64, used it to upload new sources to FreeBSD 4.11. Built on FreeBSD 4.11/i386. Here's another one where off_t is 64 bits, even though long is 32 bits. But it seems to work ok, not sure why, when CK_OFF_T is 32 bits. There is no _LARGEFILE_SOURCE stuff in the header files. 29 Dec 2005. Built on Mac OS X 10.3.9 using the new macosx10.4 target to pick up LFS. Works fine. Built on Red Hat Linux 4WS on IA64 (64-bit). Now this one is odd, stat() fails on big files. It happens also if I use the "linuxnolfs" target, which does not define _USE_LARGEFILE or _FILE_OFFSET_BITS=64. DIRECTORY BIGFILE shows the size as -1, but if "log debug", it says "no files match", i.e. different behavior, observer effect. I hate when that happens. Let's see if that's an anomaly... Built on Tru64 Unix 4.0F (64-bit Alpha). It sees long files just fine. Rebuilt and checked on x86_64 again... fine. OK, let's not worry about IA64 yet. Another small fix to the HP-UX 9.0 target from PeterE. makefile, 29 Dec 2005. ---Dev.10--- Code adjustments from Jeff, mainly to the SSL and TLS Raw mode code from several weeks ago, plus changing some data types in the security code to CK_OFF_T, plus a different data type for CK_OFF_T for K95 because Windows size_T isn't signed. This presumably will allow large-number arithmetic but it will not give large file access because that will require replacing all C library file i/o calls (esp. in ckofio.c) with native Windows APIs. Build on Solaris 9 with and without SSL and on Linux RH AS4.2 with and without SSL. ck_crp.c, ck_ssl.c, ck_ssl.h, ckcdeb.h, ckcftp.c, ckcmai.c, ckcnet.c, ckcnet.h, ckctel.c, ckuat2.h, ckuus4.c, ckuus7.c, ckuusr.c, 30 Dec 2005. It was reported that WRITE SESSION always returned a failure status, even when it succeeded. The problem was that Unix versions of zsout() and zsoutl(), for the session log only, were using write() and returning write()'s return code, which is different from what zsout() and zsoutl() are documented to return. Also plugged a couple potential holes in zsoutx() that I noticed while I was in the neighborhood. ckufio.c, 30 Dec 2005. Added FSEEK /FIND:pattern. This form of FSEEK accepts all the other switches and arguments and performs the desired seek. Then, if the seek was successful, it starts from that point and reads through the file, line by line, searching for the first line that contains the given string or matches the given (unanchored) pattern and, if found, sets the file pointer to the beginning of that line. Useful, e.g., for very long timestamped logs, where you want to start processing at a certain date or time; searching for a particular string is much faster than doing date comparisons on each line. ckuus[27].c, 30 Dec 2005. It was annoying me that FILE STATUS (FSTATUS) required a channel number to be given even if only one file was open, so I supplied the correct default in that case. ckuus7.c, 30 Dec 2005. INPUT /NOWRAP, added recently, is used for efficiently copying the INPUT stream intact, but it's not good for matching because if the INPUT target is broken between the end of the previous buffer and the beginning of the next one, the context is lost and the match does not occur. I thought of several ways around this, but they all involve saving a huge amount of context -- old input buffers, the arrays of target strings and corresponding match positions, etc. The alternative is fairly simple but it's not transparent to the user. Here's what I did in a POP script: .eom := "\13\10.\13\10" set flag off # FLAG ON = success while ( open connection && not flag ) { .oldinput := \fright(\v(input),8) # Save tail of previous INPUT buffer input /clear /nowrap 4 \m(eom) # Get new INPUT buffer if success { # INPUT matched - good .s := {\freplace(\v(input),\m(eom),\13\10)} set flag on } else { # No match .s := \v(input) # Check if target crossed the border .oldinput := \m(oldinput)\fsubstr(\v(input),1,8) if \findex(\m(eom),\m(oldinput)) set flag on } ... } I think this will be easier to explain than any dangerous and grotesque magic I might put into doinput() itself. For now, added a few words about this to HELP INPUT. ckuus2.c, 30 Dec 2005. Back to the pattern matcher. Noticed that "IF MATCH index.html [a-hj-z]*" succeeded when it should have failed. In ckmatch(), the clist section needed one more clause: it can't float the pattern if an asterisk does not occur in the pattern before the clist. This change fixes the problem without breaking any other cases that weren't already broken, most of which involve slists, i.e. {string,string,string,...}. ckclib.c, 30 Dec 2005. Tried FSEEK /FIND: on a largish file (over 100,000 lines), using it to seek to a line near the end. It took 0.756 seconds, compared with Unix grep, which did the same thing in 0.151 sec. That's because C-Kermit is using ckmatch(). But if the search target is not a pattern, it should be a bit faster to use ckindex(). Yup, 0.554 sec, a 36% improvement. Can't expect to compete with grep, though; it's highly tuned for its single purpose. ckclib.[ch], ckuus7.c, 1 Jan 2006. Updated visible copyright dates to 2006: ckcmai.c, ckuus2.c, ckuus5.c, 1 Jan 2006. Noticed that NetBSD 2.0.3 has 64-bit off_t, and that _LARGEFILE_SOURCE is mentioned in . Tried building Kermit with _LARGEFILE_SOURCE added to CFLAGS, it's good. Added it to the netbsd target. makefile, 1 Jan 2006. Fixed typo, #ifdef CK_NOLONGLONG in ckuus5.c should have been #ifndef CK_LONGLONG (which, it turns out, we don't use anyway). 2 Jan 2005. Observed that FreeBSD 4.x has a 64-bit off_t, but does not use the _LARGEFILE_SOURCE convention. Reasoning that all versions of FreeBSD have off_t (I was able to check back to FreeBSD 3.3), I simply #define CK_OFF_T to be off_t in ckcdeb.h within #ifdef __FreeBSD__ .. #endif. Another one down. This can be done for any platform that is guaranteed to have off_t. Turns out FreeBSD 3.3 has 64-bit off_t too. 2 Jan 2005. OpenBSD, same as FreeBSD. Also, added OS-version-getting thing to makefile target for the program herald, as in the other BSDs. Built on OpenBSD 2.5 from 1998, it has 64-bit off_t too. ckcdeb.h, makefile, 2 Jan 2005. Dumping the command stack every time there's an error is really too much. I added SET COMMAND ERROR-DISPLAY {0,1,2,3} to set the verbosity level of error messages. Only level 3 dumps the stack. ckuus[235].c, 2 Jan 2005. Built on HP-UX 11.11 with _LARGEFILE_SOURCE and _FILE_OFFSET_BITS=64. The result works fine as far as I can tell. It sees big files, it can open them, seek to positions past the 2^31 boundary. It can send large files. It can do large-number arithmetic (^ 2 62). The only problem is that during compilation, every single modules warns: cc: "/usr/include/sys/socket.h", line 504: warning 562: Redeclaration of "sendfile" with a different storage class specifier: "sendfile" will have internal linkage. cc: "/usr/include/sys/socket.h", line 505: warning 562: Redeclaration of "sendpath" with a different storage class specifier: "sendpath" will have internal linkage. These warnings should be perfectly harmless since they are not coming from C-Kermit code, nor does C-Kermit use either one of those functions. These warnings don't come out in HP-UX 11i v2, but on that one we get tons and tons of picky compiler warnings (variables set but not used, defined but not referenced, etc). A couple, however, turned out to be valid; one case of "expression has no effect", and two of "string format incompatible with data type" (I missed a couple file-size printfs). There were also numerous warnings about signedness mismatch or sign conversion of constants like IAC (0xff). Does the HP-UX Optimizing Compiler have a compiler flag to make all chars unsigned? Yes, +uc, but the man page says "Be careful when using this option. Your application may have problems interfacing with HP-UX system libraries and other libraries that do not use this option". Sigh, better not use it. After reviewing "HP-UX Large Files White Paper Version 1.4" and HP's "Writing Portable Code" documents, I added -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 to the hpux1000 target, which is the basis for all HP-UX 10.00 and later builds. Large files are available in HP-UX 10.20 and later. 10.00 and 10.10 were not real releases, and anyway these flags should be harmless there unless the large-file implementation was only partly done. Built OK on both PA-RISC and IA64, optimized and plain. makefile, 4 Jan 2006. Built on FreeBSD 6.0 on IA64. All OK except I got a warning about the argument passed to time() in logwtmp() in ckufio.c. This section had already been partially fixed; thus I put the improved version into #ifdef CK_64BIT, which is our newly available symbol that should be automatically defined for any true 64-bit build. ckufio.c, 4 Jan 2006. Finally got around to testing Jeff's changes to SSL/TLS RAW mode from December 30th against our POP server. It didn't work, couldn't log in. Tried backing off the ckctel.c changes first; that allowed login and communication, but it did not suppress activation of Telnet protocol whenever a 0xff byte arrived. Backed off the rest of the changes and now all is OK again. ckctel.c, ckcnet.c, ckuus7.c, 9 Jan 2006. Built on NetBSD 1.4.1 (1999), found that it did not like the large file assumption -- fseeko() and ftello() do not exist; added a clause to the netbsd target to check for fseeko and not define _LARGEFILE_SOURCE if not found. Oddly enough, off_t is 64 bits anyway, but it doesn't look like the APIs are half-done. For example, stat() uses off_t (64 bits) for the file length, but fseek() uses long (32 bits) and there is no 64-bit analog. Anyway the new netbsd target works on both 1.4.1 and 1.5.2 (no large files) and on 2.0.3 (large files). makefile, 9 Jan 2006. Built on QNX-32 4.25, which has no large file support. Got a few strange compiler (WatCom) warnings, but it built and runs OK. Noticed that file transfers into QNX over a Telnet connection can't use streaming, but that's nothing new to this version; same thing happens with C-Kermit 7.0. 9 Jan 2006. Built on IRIX 6.5. I didn't bother with large files there because it does not support the _LARGEFILE_SOURCE interface; you have to change all the APIs at the source level from blah() to blah64(). Seems to work fine as a 32-bit app even though its off_t is 64 bits. Tried a pure 64-bit IRIX 6.5 build but it dies in ckcnet.c when it hits SOCKOPT_T and GSOCKNAME_T with "The identifier 'socklen_t' is undefined". Looks like I no longer have access to SCO OSR5. Made a pure 32-bit build on SCO UnixWare 7.1.4, all OK. Found that this version also supports LFS, added it to the uw7 target. makefile, 9 Jan 2006. --- Dev.11 --- Evidently the HP-UX bundled (non-ANSI non-optimizing) compiler doesn't like long integers in switch expressions. Changed three examples of these in the S-expression code. ckuus3.c, 10 Jan 2006. A section of tstats() where GFTIMER isn't defined (e.g. on Motorola sv68r3v6) was garbled. Fixed in ckcfn2.c, 10 Jan 2006. A fix for setting 921600 bps on Linux from Paul Fulghum, Microgate Systems Ltd. ttgspd(): ckutio.c, 11 Jan 2006. Noticed that when I changed the compact substring notation code back on August 9th, I broke the ability to use arithmetic expressions within the brackets, which explains some rather odd behavior I saw with some of my scripts. Looking more deeply into this, I also see that all the parsers I have been using up to now for this, as well as for array bounds pairs, have been inadequate because they never allowed for nested constructions, such as a member of a bounds pair that itself was an array element, possibly with another array element as a subscript. I wrote a new routine for this, called boundspair(), which is like arraybounds() except it accepts an extra argument, an array of characters that can serve as bounds-pair delimiters, and it returns the pair separator that was encountered in another new argument. For the alternative substring notation for [startpos-endpos] I had to change the delimiter from '-' to '_' because '-' can be used in arithmetic but '_' is not a recognized operator. This is so I can parse, e.g. [a:b] or [a_b] in the same context, and then find out which form was used, e.g. \s(line[9:12]) or \s(line[9_12]); the first string is 4 bytes long, the second is 12. Everything seems to be OK now. \s(line[10]) gives everything starting at 10, but \s(line[10:0]) gives the null string. Bad syntax in the bounds pairs results in a null string; missing pieces of the bounds pair result in defaults that should be compatible with previous behavior. ckuus[45].c, ckuusr.h, 13 Jan 2005. Changed arraybounds() to call boundspair(). This was a rather drastic change, not strictly necessary, but I think I got all the kinks out. ckuus5.c, 13 Jan 2005. Changes from PeterE to the makefile for HP-UX 6 and 7, to accommodate bigger symbol tables, etc. 19 Jan 2005. Determined that SCO OSR5.0.6 (and earlier) do(es) not support large files. Don't know about 5.0.7. 30 Jan 2005. Created a new build target for SCO OSR6.0.0. Gets the exact 6.x.x version dynamically. Supports large files and big-number arithmetic via CK_OFF_T. The sockopt() family of functions changed the data types of some of their arguments since OSR5. It was already possible to define SOCKOPT_T and GSOCKNAME_T from the command line but I had to add code to also allow this for GPEERNAME_T too. ckcnet.c, makefile, 30 Jan 2005. Apparently, ever since C-Kermit 7.0 was released, it has never been possible to use a variable for the as-name in a RECEIVE command in Kermit 95. This is because evaluation of the as-name field was deferred until after we could check whether it might be a directory name (which, in Windows, could start with a backslash). This little bit of magic was not a good idea, magic hardly ever is. I changed the code to evaluate both as-name fields in the normal way. If they want to receive to a directory called "\%1", they'll just have to spell it differently. The workaround is to turn the whole command into a macro and evaluate it before executing it, e.g.: assign xx receive /as-name:\%1 do xx ckuus6.c, 1 Feb 2006. Built OK on FreeBSD 6.1 on AMD64. Adjusted some copyrights and date stamps. ckcmai.c, makefile, 8 Feb 2006. --- Dev.12 --- Fixed a signed/unsigned char warning in the new boundspair() calling code in the compact substring notation handler. ckuus4.c, 9 Feb 2006. Removed a spurious extra linux+openssl label from the makefile, added solaris10g_64 synonym. 9 Feb 2006. Satisfied myself that LFS is OK on Solaris 10 i386, and I'm going to assume it's also OK on Solaris 9. Made LFS standard for all Solaris 9 and 10 builds (including the secure ones) except the explicitly 64-bit ones, and made the provisional solarisXXlfs targets into synonyms. makefile, 9 Feb 2006. --- Dev.13 --- Further attempts at SSL/TLS message suppression when QUIET is ON. ck_ssl.c, 16 Feb 2006. From J.Scott Kasten: (quote...) I just uploaded a patch to /kermit/incoming. The file name is "jsk-patch-for-cku211.diff". I have also included the patch as ASCII text in this email below. This patch may be applied to the cku211.tar.gz source code via: cd cku211, patch -p1 <../jsk-patch-for-cku211.diff The patch adds 4 new build targets: netbsdwoc - a stripped no curses target for iksd used. netbsdse - security enhanced target with srp, ssl, and zlib. irix65gcc - build on SGI Irix 6.5 platform using gcc. irix65se - security enhanced target with srp, ssl, and zlib. The patch fixes one build target: irix64gcc - The "-s" option is not supported by gcc under Irix. I thank all of you in the Kermit Project for such a fine utility. I recently had to get a 16 MB file overseas across a spotty communications link to repair a computer remotely. Kermit was the only thing that could do the job, so I wanted to contribute these patches back to the mainstream to say thanks. This digitally signed email is a binding contract that officially assigns the rights to the source code patch (shown below) that I developed to the Kermit Project at Columbia University. (...end quote) ck_ssl.c, makefile, 23 Feb 2006. Changed the new NetBSD target names to be consistent with the conventions used in most other targets: netbsdwoc -> netbsdnc netbsdse -> netbsd+ssl+srp+zlib irix65se -> irix65+ssl+srp+zlib and removed old, now superfluous, NetBSD targets (old-netbsd, netbst15, netbst16), leaving synonym labels in their place. Also updated (crudely) the Linux target variations (curses instead of nocurses, no curses at all) to be (appropriately modified) copies of the current linux target. It would be nicer to combine them, but this gets the job done. makefile, 23 Feb 2006. --- Dev.14 --- Fixed the HELP command when used with tokens like @, ^, #, and ;. The first two had been omitted from the table. The second two required a new path into the guts of the parser, since comments are normally stripped at a very low level. ckuus[r2].c, ckucmd.c, 24 Feb 2006. Built on AIX 5.1 ("make aix51") without incident. Then I tried: make aix51 "KFLAGS=-D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64" This had no effect. I found the relevant document ath the IBM website. It says to use -D_LARGE_FILES instead. I added this to the AIX 4.2 target since (a) IBM says large files are supported by AIX 4.2 and later, and (b) all Kermit AIX targets past 4.2 use the 4.2 one. Plus a clause to make sure CK_OFF_T is defined appropriately. ckcdeb.h, makefile, 6 Mar 2006. Added a 32-bit aix51+openssl target. Builds OK, works fine (tested against our SSL POP server). Tried I tried adding -D_LARGE_FILES. It seems to work fine, so we'll keep it. Cleaned up the other aix5blah entries a bit also. makefile, 6 Mar 2006. Fixes from J. Scott Kasten to the IRIX 6.4 and 6.5 makefile targets. They were badly wrong. makefile, 6 Mar 2006. The reason Kermit was looping on directories in IRIX was a classic "double-ended break". The makefile targets failed to define DIRENT so Kermit was open/read on directories rather than opendir()/readdir(). But then it was also failing to account for the fact that read() would return -1 on error. The makefile fix adds -DDIRENT, and the read() case in traverse() now properly terminates its loop on error. ckufio.c, 6 Mar 2006. --- Dev.14 --- In response to a complaint that C-Kermit would not build on HP-UX 11 with OpenSSL, I tried it myself on both 11.11/PA-RISC and 11i v2/Itanium. It built OK on both but I had to add a new target (hpux1000o+openssl-nozlib) for no Zlib since these boxes did not have it installed. makefile, 9 Mar 2006. Added OpenSSL version number display to SHOW FEATURES. ckuus5.c, 9 Mar 2006. Gavin Graham noticed that FTP [M]GET /DELETE /MOVE-TO: was rejected with "?Sorry, /DELETE conflicts with /MOVE or /RENAME". This check belongs in the PUT code but not in the GET code. Commented it out and tested the result. The combination is now accepted but then Kermit refuses the incoming file as if it had been given a /SMALLER-THAN: or /LARGER-THAN: switch, which it didn't happen. Turns out there was one more place where I wasn't initializing the new "wide int" member of the switch-parsing pv[] struct. Once this was fixed, the /MOVE-TO part still didn't work. Turned out the /DELETE case was part of a long if-else-if-else- chain, which effectively made /DELETE and /MOVE-TO: or /RENAME-TO: mutually exclusive. Fixed this, now it works fine. ckcftp.c, 13 Mar 2006. Got access to AIX 5.3, built there, all OK, including large files. 13 Mar 2006. --- Dev.16 --- Patches from Mark Sapiro to suppress getsockopt() and getsockname() warnings in Mac OS X. ckcnet.[ch], 18 Mar 2006. In response to a complaint from Clarence Dold, tried "make redhat9" (which is the rather dated target that tried to include all forms of security) on RH Linux AS4.3, it failed miserably. I made a new makefile target, removing Kerberos IV and got a lot farther. But then in ckcftp.c, the following struct definition: struct { CONST gss_OID_desc * CONST * mech_type; char *service_name; } gss_trials[] = { { &ck_gss_mech_krb5, "ftp" }, { &ck_gss_mech_krb5, "host" }, }; refers to a variable, ck_gss_mech_krb5, that is not defined anywhere. Up above, however, is a static definition for gss_mech_krb5, so I changed the struct definition to match. Next, in ckuath.c, the compiler could not find the com_err.h file. Turns out in Linux this is in a subdirectory, et, so we have to add a -I clause to the makefile target for this. Made a target for Linux+SSL only. Made a target for Linux+Krb5 only; this required moving an #ifdef in ckuus7.c to prevent an unguarded reference to SSLEAY_VERSION. New targets: linux+krb5+ssl, linux+krb5, linux+krb5. ckcftp.c, ckuus7.c, makefile, 27 Mar 2006. New targets of HP-UX 10/11 with OpenSSL from PeterE. makefile, 27 Mar 2006. Added large file/integer support to SHOW FEATURES. ckuus5.c, 27 Mar 2006. Built OK on Solaris 9 and 10 with gcc (someone was complaining that this didn't work, but that was 8.0.211). Started build on a Sun 3/80 mc68030 with NetBSD 2.0 and gcc 3.3.3. But it died with an assembler error in ckcfn2.c (compiler bug). 27 Mar 2006. --- Dev.17 --- NebBSD 2.0 build completed by turning off optimization on ckcfn2.c ("KFLAGS=-O0"). Result supports 64-bit ints and, presumably, large files. uname -p = "m68k", -m = "sun3". 29 Mar 2006. Corrected an omission in applying PeterE's updates to the HP-UX targets. makefile, 28 Mar 2006. solaris2xg+krb5+krb4+openssl+shadow: Tried resurrecting the solaris2xg+krb5+krb4+openssl+shadow target. It asks to link with libdes but there is no libdes. Removed -ldes from the target, now at least it builds and runs wart. The compilation blows up in ckcftp.c for missing header files: ckcftp.c:462: kerberosIV/krb.h: No such file or directory ckcftp.c:500: gssapi/gssapi_generic.h: No such file or directory ckcftp.c:501: gssapi/gssapi_krb5.h: No such file or directory Got a bit farther by adding appropriate -I's and -L's to KFLAGS but it still dies compiling (or linking?) ckcftp.c, but it doesn't say exactly why. OK, deferred. Added SET SEXPRESSION TRUNCATE-ALL-RESULTS { ON, OFF }. This can be used for force integer arithmetic in any kind of calculation that requires it, such as date calculations. This is a global setting, not on any kind of stack. Also, updated SHOW SEXP and added HELP SET SEXP which wasn't there before. ckuus[23].c, 30 Mar 2006. To make the RENAME command a bit more useful, need to add some switches. But it shares a switch table, qvswtab[], with some other commands. Broke this off into its own switch table. ckuus6.c, 17 Apr 2006. Added RENAME switch values that can be used in the same table with the DELETE switch values, which are shared by many commands. ckuusr.h, 17 Apr 2006. Discovered that the RENAME command could be entered without any arguments and it would still succeed. Fixed in dorenam(): ckuus6.c, 17 Apr 2006. Added parsing for RENAME /UPPER:option (to uppercase the file name(s)), /LOWER:option (to lowercase), and /REPLACE:{{s1}{s2}} (to do string replacement on the filename(s)), but not the semantics. When any of these switches is given, the target ("to") name is not parsed; they act on the source name. The /LOWER: switch takes keyword args to specify whether it should act only only files that have all UPPER case latters, or on ALL files (i.e., including files with mixed-case names); similarly for the /UPPER: switch. There is some creative parsing allowing these to be given with or without a colon and keyword argument, which works fine except if you include the colon but no argument, execute the command (which works fine), and then recall the command. I haven't yet decided about the interaction among these switches. Clearly if /UPPER is given after /LOWER, it overrides. But if /UPPER (or /LOWER) is given with /REPLACE, what should happen? ckuus6.c, 17 Apr 2006. Filled in actions for RENAME /UPPER: and /LOWER: for the single file case, and tested all combinations of switch values and filename configurations. Once that was OK, moved the code out into a separate routine, renameone(), and then called it from both the single-file case and the multifile case. ckuus6.c, 19 Apr 2006. Added RENAME /SIMULATE. Filled in the code for string replacement, needs testing. ckuus6.c, 20 Apr 2006. Changed /REPLACE options to allow a negative number to specify an occurrence from the right, so -1 means the last occurrence, -2 means the next-to-last, etc. ckuus6.c, 24 Apr 2006. Added RENAME /COLLISION:{OVERWRITE,PROCEED,FAIL}. This is implemented but not tested. ckuus6.c, 24 Apr 2006. Worked on RENAME /COLLISION:FAIL. I decided it was less than useful to ... Added SET RENAME { COLLISION, LIST } to let user change default collision and listing actions. ckuusr.[ch], ckuus[36].c, 25 Apr 2006. Experimented with parsing for /CONVERT:cset1:cset2. The problem here is that there is no straightforward way for a switch to have multiple arguments. Or is there...? If I parse cset1 with cmswi() rather than cmkey(), it almost works; the only problem is that the character-set keywords don't have CM_ARG set, so they don't know to stop on, and ignore, a colon. If I make a copy of the table and set CM_ARG in the flags field for each keyword, it works fine: if I Tab in the first name, it fills itself out, supplies a colon, and waits for the second name. So in the code, the first time that RENAME /CONVERT is invoked, I put code to copy fcstab[] and set CM_ARG in each flags field. Works fine, and now we know how to make a switch that takes multiple arguments. ckuus6.c, 24 Apr 2006. I thought I had a function to convert the character set of a string but I don't, so actually implementing /CONVERT: will be difficult. Actually the parsing wasn't that easy either. It works OK interactively, but not in a TAKE file. To make a long story short, I had to change gtword() and cmkey2() to not require "/" at the beginning of a switch, and then to parse arguments-that-are-followed-by-other-arguments as if they were switches, so that they can end with colon rather than space. This might seem dangerous, but switches always have "/" at the beginning, so the check is superfluous. ckucmd.c, 26 Apr 2006. Back to /CONVERT... Once I was able to get the code to call cvtstring() I was able to debug it (at first it was skipping every second character). And now we have a general-purpose string-translating function we can call from anywhere. Requires that C-Kermit be built with Unicode support. ckuus6.c, 26 Apr 2006. Added SHOW RENAME. ckuusr.h, ckuus[r5].c, 26 Apr 2006. Conditionalized some Unix/Windows assumptions in renameone() so the code could work in VMS. ckuus6.c, 2 May 2006. Added RENAME /FIXSPACES to change all spaces in the filename(s) to underscore or any other character or string that is given. This is just a special case of RENAME /REPLACE:{{ }{x}} with easier syntax. ckuusr.h, ckuus6.c, 2 May 2006. Added an "all-but" control to the /REPLACE options: /REPLACE:{{.}{_}{~1}} means replace all but the first (this one works); /REPLACE:{{.}{_}{~-1}} means replace all but the last (this one not yet). ckuus6.c, 2 May 2006. Filled in the second one ("all but" the given occurrence). The algorithm is simply to reverse the three strings and then use the same code as we use in the left-right-case, and then unreverse the result. At first I used yystring() for this but yikes, what a bad design! So I made a better string-reversal routine, gnirts(), for this (luckily yystring() is only used in one place, for which its design is appropriate). ckuus6.c, 3-4 May 2006. Added code to handle the case where the file being renamed includes a path specification. In this case we separate the path, apply the renaming functions to the filename only, and then at the end rejoin the original filename with the path, and join the new name with same path or, if a destination directory was given, with that. ckuus6.c, 4 May 2006. Added HELP SET RENAME and updated HELP RENAME. ckuus2.c, 4 May 2006. "Tom Violin" (Tom Hansen) noticed that the first time you FOPEN a file, Kermit's memory consumption goes way up. In fact there's a warning to that effect in the code, where, upon first open, a potentially big array of potentially big structs is allocated. I rewrote the code to allocate each array member (struct ckz_file) as needed, i.e. when a file is opened, and to free it when the file is closed (or the open fails). This was actually quite a lot of work, which is why I didn't do it the first time around: every single "." had to be changed to "->". Every check for a valid channel first had to check if the channel's struct was allocated and every other reference to z_file[i]->anything had to be prechecked that z_file[i] was not a NULL pointer. Also I made some improvements to FILE STATUS, and I fixed FILE CLOSE to default the channel number if only one channel was open, as I did for FILE STATUS a while back. ckuus7.c, Cinco de mayo 2006. Ran my old BUILDS script that builds C-Kermit with about 100 different combinations of feature-selection switches. Fixed a few small glitches so now they all build OK (except can't do NOANSI builds any more on recent Linuxes because of varargs()). ckuus3.c, ckuus5.c, ckuus6.c, ckuus7.c, ckucmd.c, ckcfns.c, 6 May 2006. Fixed RENAME /LOWER and /UPPER, when given with no colon or argument, to default to ALL. ckuus6.c, 13 May 2006. Built on VMS 7.2-1, tested new RENAME command there; seems to be OK. 13 May 2006. --- Dev.18 --- I wanted to test large files against RESEND but I don't have access to any system that can run C-Kermit and that also has enough space for a large file. I created a "fake" large file on Linux (3G hole plus 1 byte), and sent it over a localhost connection, and interrupted it repeatedly and then initiated a RESEND at the sender. In each case, it picked up where it left off. But before the 2G boundary was crossed the disk filled up. Inconclusive. 14 May 2006. PeterE got a warning in the new FILE OPEN code when building in HP-UX 9. I added a cast, built on HP-UX 11, no more complaint. However there are warnings about internal vs external bindings of sendpath and sendfile in every module. Too bad, these are not Kermit tokens, it's a conflict in HP's header files. Marc Sapiro doesn't see them; probably it's something on the HP testdrive site. ckuus7.c, 17 May 2006. Fixed the tru64-51b+openssl target -- the terminating doublequote of KFLAGS was missing -- and also the osf target, which failed to import the LIBS definition from whatever other target invoked it. Now the SSL build goes OK on Tru64 5.1B. Replaced x.tar.z in the download areas without declaring a new Dev number. The new one has a makefile with today's date. Software engineering at its best! makefile, 18 May 2006. Scott Kasten noted that the estimated-time-remaining calculation would go bonkers on LFS systems when RESENDing a large file. It looks like the shocps() and shoetl() functions escaped the CK_OFF_T conversion. I made what seemed to be the right adjustments, and then was lucky enough to find a computer that had enough free disk space for me to send a large file, interrupt it several times, resend it, all seems to be OK. 28 May 2006. Later Scott verified these changes independently for Linux, but the problems in IRIX remain. Patches from Scott Kasten for large files on IRIX 6.5: ckcdeb.h, makefile, 12 Jun 2006. --- Dev.19 --- Added a new function for dealing with JPGs and GIFs: \fpicture(filename,&a) returns 0 if file not recognized or can't be opened; returns 1 if landscape, 2 if portrait or square. If array given, element 1 is width, element 2 is height. ckuusr.h, ckuus4.c, 19 Jun 2006. Scott Kasten reports that the FTP client can transfer large files OK, at least in Linux, but has trouble with recovery: . Kermit takes a very long time to start the transfer, sometimes over 30 minutes. Suspect the ftp server is counting the bytes in a long file? Or maybe it's a text-mode transfer and it's counting the lines? Probably in response to Kermit's SIZE command. . The size shown in the FT display is wrong by a random amount. And of course so are the progress bar, percent done, and time remaining. . The file, however, is transferred correctly. REGET works correctly too. I tried setting up a test scenario locally but our Solaris FTP server does not support large files: FTP SENT [SIZE BIGFILE] FTP RCVD [550 BIGFILE: not a plain file.] FTP SENT [PASV] FTP RCVD [227 Entering Passive Mode (128,59,48,24,246,37)] FTP SENT [RETR BIGFILE] FTP RCVD [550 BIGFILE: Value too large for defined data type.] Created the same 3GB on a Tru64 Unix system that allows FTP access. Made the connection from C-Kermit on Solaris (32-bit with LFS): 16:46:12.908 FTP SENT [SIZE BIGFILE] 16:46:12.947 FTP RCVD [213 3000000001] Note that it takes less than half a second to get the reply. Now I start the download and then interrupt it at about 2%: 16:46:12.979 FTP SENT [TYPE I] 16:46:13.174 FTP RCVD [200 Type set to I.] 16:46:13.226 FTP SENT [PASV] 16:46:13.262 FTP RCVD [227 Entering Passive Mode (15,170,178,171,11,37)] 16:46:13.299 FTP SENT [RETR BIGFILE] 16:46:13.337 FTP RCVD [150 Opening BINARY mode data connection for BIGFILE..] 16:47:24.895 FTP RCVD [426 Transfer aborted. Data connection closed.] 16:47:24.934 FTP RCVD [226 Abort successful] 16:47:24.991 FTP SENT [MDTM BIGFILE] 16:47:25.028 FTP RCVD [213 20060706204458] Now I do a REGET: 16:51:55.321 FTP SENT [PASV] 16:51:55.357 FTP RCVD [227 Entering Passive Mode (15,170,178,171,11,43)] 16:51:55.394 FTP SENT [REST 122736640] 16:51:55.430 FTP RCVD [350 Restarting at 122736640. Send STORE or RETRIEVE..] 16:51:55.431 FTP SENT [RETR BIGFILE] 16:51:55.469 FTP RCVD [150 Opening BINARY mode data connection for BIGFILE..] This worked perfectly, as far as I can tell; the FT display picked up in the right place; the thermometer, percent done, and estimated time remaining were the same as when we left off last time. I did the same thing several more times, everything was OK. It would have taken a really long time to let this run to completion, but I think this demonstrates that Scott's symptoms are server-dependent. No changes. 6 July 2006. Checked current code on VMS 8.2-1 on IA64 / UCX 5.5, builds fine. No changes. Updated listing at HP. 6 July 2006. Checked FTP GET of large file in ASCII mode against Tru64 FTP server. It was fine, and there was no delay in the server's response to our SIZE command (as there would be if it were scanning the entire file to count how many bytes would be required to send it in text mode). 7 Jul 2006. Tested FTP PUT big file against Tru64, OK. Ditto FTP RESEND big file: C-Kermit>resend BIGFILE PUT BIGFILE (binary) (3000000001 bytes)---> PASV 227 Entering Passive Mode (15,170,178,171,13,186) ---> SIZE BIGFILE 213 343211280 ---> MDTM BIGFILE 213 20060707141243 ---> APPE BIGFILE 150 Opening BINARY mode data connection for BIGFILE (128.59.59.56,45470). Made REPUT a synonym for RESEND. ckuusr.c, 7 Jul 2006. Added FTP REPUT and FTP RESEND since previously there was no FTP-prefixed command for recovering uploads, only the regular RESEND command, which might not have been obvious to people. ckcftp.c, 7 Jul 2006. Added help text for FTP RESEND and REPUT and amended RESEND help text. ckcftp.c, ckuus2.c, 7 Jul 2006. Changed name of \fpicture() to \fpictureinfo() and added help text. By the way, ImageMagick can do this too: identify -format "%w %h" dscf0520.jpg. The advantage of having it in Kermit is that not everybody has ImageMagick. ckuus[24].c, 7 Jul 2006. Changed the numeric comparisons = < > <= >= != to allow long integers by changing the data type to CK_OFF_T, etc. ckuus6.c, 7 Jul 2006. Noticed that \fkeywordvalue(foo=this is a string) only kept the first word. Fixed it to keep the whole definition. Also added \fkwvalue() as a briefer synonym. ckuus4.c, 7 Jul 2006 Sometimes we want to check if a file's status before we've FOPEN'd it, in which case the channel variable is likely to be empty and \f_status(\%c) would get an error. Making the obvious change didn't fix this, however. It turns out that the function evaluator failed to adjust argn (argument count) when trailing arguments were empty, and argn was being used in this case, and probably others, to test whether an argument existed. I added code to adjust argn to reflect the number of arguments up to and including the rightmost non-empty one. ckuus4.c, 7 Jul 2006. Fixed \fstripb() to not dump core if second argument is missing. ckuus4.c, 7 Jul 2006. Discovered that it was not obvious what pattern to use to match strings enclosed in square brackets. "if match [abc] \[*\]" didn't work. Neither did various other tricks like NCRs for the brackets. However, "if match [abc] \\[*\\]" does work. Trying to fix this would no doubt break 100 other things, so let's call it a feature. 7 Jul 2006. Added \fgetpidinfo(n) to return info about a process ID; for now it simply returns 1 if the process is alive and 0 if not (or -1 if the argument is bad or missing or on any kind of error). ckuusr.h, ckuus[24].c, 7 Jul 2006. The "where-did-my-file-go" message seemed to be ending with a LF rather than CRLF, probably because the terminal modes had not yet been restored, leaving the next prompt hanging below it, rather than on the left margin, if C-Kermit exited immediately after the transfer. Fixed by changing all \n's to \r\n's in wheremsg(): ckcpro.w, 8 Jul 2006. Added \v(lastkwval) so we can retrieve programmatically the keyword most recently processed by \fkeywordval(). ckuusr.h, ckuus4.c, 9 Jul 2006. --- Dev.20 --- Added #ifdef SV68, #include , #endif because Unix System V/68 on Motorola choked on the SEEK_CUR reference without it. ckuus4.c, 10 Jul 2006. Make \fkeywordval(xxx) undefine xxx (i.e. when a keyword is given with no value). This way command-line keywords will always override preexisting default definitions, whether they have a value or not, which makes it easier to parse command lines like "foo=bar blah xx=yy". ckuus[24].c, 12 Jul 2006. On 29 Nov 2005 I changed IF KERBANG to solve a problem (see entry for that date), but introduced a new one; namely that you can't have (e.g.) a FATAL macro that uses IF KERBANG to decide whether to EXIT all the way or STOP back to the prompt. Changed it again, this time to require not that the command level be 1, but that the command *file* level be 0 (i.e. that we are in the top-level command file, irrespective of the command or macro level, but not in a subfile). ckuus6.c, 12 Jul 2006. It is unhelpful when Kermit gets a syntax error in the middle of a big compound statement block (e.g. FOR or WHILE loop) and dumps out the whole thing in an error message. I changed the two places where this can happen to call a new routine that, instead of dumping out the entire cmdbuf, checks its length first and if it's more than a line long, truncates it and adds an ellipsis. ckuus6.c, 12 Jul 2006. The new RENAME command didn't give very good error messages, e.g. if the filespec didn't match any files. Fixed in dorenam(): ckuus6.c, 12 Jul 2006. Fixed DIR /TOP to work if the /TOP:n argument was omitted, defaulting to 10. domydir(): ckuus6.c, 12 Jul 2006. Added DIR /COUNT:v to count the number of files that match the given criteria and store result in the variable v. ckuusr.h, ckuus[r26].c, 24 Aug 2006. Added HDIRECTORY as an invisible synonym for DIR /SORT:SIZE /REVERSE. Can be used with other switches, of course, so (e.g.) HD /TOP shows the ten biggest files. ckuusr.h, ckuus[r26].c, 24 Aug 2006. DIR /FOLLOWLINKS and /NOFOLLOWLINKS always did the same thing; the switch was ignored, a symlink is always followed. Fixed in ckuus6.c, 24 Aug 2006. Added DIR /NOLINKS, which means don't show or count symlinks at all. ckuusr.h, ckuus[r26].c, 24 Aug 2006. Build on Solaris 9 and NetBSD 3.0, 24 Aug 2006. Added a missing definition for LOCK_DIR in the Linux HAVE_BAUDBOY case, suggested by Gerry Belanger. ckutio.c, 6 Oct 2006. Suggested by Jim Crapuchettes: \v(dialmessage) is the text string corresponding to \v(dialstatus). ckuusr.h, ckuus4.c, 6 Oct 2006. Soewono Effendi sent code for exit sequence to leave DTR on; this amounted to unsetting HPUCL in c_cflag. I did it a simpler way, hopefully portable to all Unixes, but who knows at this late date. The code is inside #ifndef CK_NOHUPCL..#endif in case it causes trouble. It is executed if SET EXIT HANGUP is OFF and a serial port was open at the time Kermit exits (or closes it explicitly). ttclos(): ckutio.c, 6 Oct 2006. Built on Solaris9/Sparc; FreeBSD 6.2/AMD64; NetBSD 3.0/i386; HP-UX 11i v2; SCO OSR6.00. --- Dev.21 --- Added netbsd+openssl target to makefile. Built OK (NetBSD 3.0, OpenSSL 0.9.7d) except with some warnings in ck_crp.c. Connects and logs in OK to a secure site. 10 Oct 2006. Added a debug statement to ftp_hookup() to record the TCP port that was used. ckcftp.c, 11 Oct 2006. Built with OpenSSL 0.9.7l on Solaris 9. Built with OpenSSL 0.9.8d on Solaris 9; connects and logs in to a secure site. 11 Oct 2006. The new RENAME command didn't work if both the source and destination names included directory segments, e.g. "rename /tmp/foo ~/bar" (see notes of 4 May 2006). This was fixed in renameone() by a special case in which the second argument is given but it is a filename, not a directory name. ckuus6.c, 11 Oct 2006. Fixed unguarded reference to dialmsg[] for \fdialmessage(), noticed by Gerry Belanger. ckuus4.c, 12 Oct 2006. Added a TOUCH command that does what UNIX touch does: creates the file if it does not exist, updates the timestamp if it does. If a wildcard is given, it operates only on existing files. It shares the DIRECTORY command parser, so all the same file selection switches can be given. ckuusr.[ch], ckuus[26].c, 12 Oct 2006. PeterE noticed that if you FOPEN a file, do some seeks or reads, then FCLOSE it, then FOPEN it again (or open a different one), some of the old information is still there (e.g. current line number). This is an artifact of the changes of May 4th. Now the file closing and opening routines are a bit more careful about scrubbing and initializing the file info struct. ckuus7.c, 12 Oct 2006. --- Dev.22 --- Built OK on Red Hat Linux AS4 with both "make linux" and "make linuxnc". 15 Oct 2006. DIRECTORY /BRIEF ignored file selection switches and always listed all files. This was because of how I cleverly called filhelp() (the routine that lists matching files when ? is typed in a filename field) and, of course, filhelp() doesn't know anything about the DIRECTORY command's file selections. Changed filhelp() to accept all the args needed for passing along to fileselect(), renamed it to xfilhelp(), and made a filhelp() stub that chains to xfilhelp() with null selections. ckcker.h, ckucmd.[ch], ckuus6.c, 29 Nov 2006. SHOW CONNECTION for an SSH connection said the connection type was "NET" rather than "SSH". Fixed in dolognet(): ckuus3.c, 29 Nov 2006. SHOW CONNECTION didn't show the TCP port number. This command works by parsing the current connection log entry string, which doesn't have a field for this, but which sometimes shows the port number as part of the hostname (but more often not). Added code to dolognet() to log the TCP port number, if known. This involved adding a gettcpport() function to ckcnet.c. ckcnet.[ch], ckuus3.c, 29 Nov 2006. This was impossible: def \%1 upper, echo \f\%1(abc) -- i.e. to "compose" a function name. Fixed in zzstring(). But note that it's still not possible to do this: def \%1 \fupper, echo \%1(abc) -- because at the point where "\fupper" is encountered, which is automatically fed to fneval(), the argument list hasn't been read yet. ckuus4.c, 29 Nov 2006. The meaningless Lisp command (=) would cause Kermit to hang. Due to some idiosyncracy in the parser, it would see this as ((=) and would go into "wait for the closing paren" mode. There was already a hack in the code to compensate for this, but it didn't work. I fixed the hack but I don't understand the real problem. Anyway, comparing Kermit with real (Franz) Lisp I discovered that comparison operators do not require two arguments, as Kermit has been doing, although they do require at least one. I changed Kermit to not require two, so now all the comparison predicates behave exactly like Franz Lisp, including getting an error if there are no args). ckuus[r3].c, 29 Nov 2006. From to-do list: Make a way to inhibit pattern matching in SWITCH labels. It's already there; just quote the wildcard characters; the only trick is that for some reason (such as that SWITCH is really an internally defined macro), a double quote is needed: switch \%1 { :a\\*z, echo literally "a*z", break :abcxyz, echo literally "abcxyz", break :a*z, echo a...z, break :default, echo NO MATCH } In first case, the asterisk is taken literally; in the third it's a metacharacter and the label matches any string that starts with 'a' and ends with 'z'. Array initialization would quit early if any initializers were undefined, e.g. "decl \&a[] = \%a \%b \%c" would stop at the first element if \%b was not defined, even though \%c might be defined. Fixed in dodcl(): ckuusr.c, 30 Nov 2006. DIR /ARRAY:a filespec, when the filespec does not match any files, terminates with the array undeclared. It would be better to return a declared but empty array (\&a[0] = 0). The code is already there to do that, but isn't working. And yet "declare \&a[0]" does indeed create a 0-element array ("show array" shows a dimension of 0). Turns out there were two problems; one was the careless recycling of a local variable ("array"), resulting in failure to create \&a[] (but not any other array). Fixed in domydir(): ckuus6.c, 30 Nov 2006. The other problem was that dclarray(), when called with an array name and a dimension of zero, does two different things depending on whether the array already existed. There is still a fair amount of confusion about whether a dimension of 0 indicates an array with 1 element (as it should) or a nonexistent array. We call dclarray() with a size of 0 to undeclare an array but we also need to able able to declare an array with only element 0. I changed dclarray() to treat a negative dimension as a command to destroy the array, and 0 or positive as a command to create the array with the given dimension. ckuus[r56].c, 30 Nov 2006. Next problem: when chkarray() returns 0, this should not be interpreted to mean the array does not exist. Looks like the only place this happened was in \fcontents(); fixed in ckuus4.c, 30 Nov 2006. If we include file selectors with DIR /ARRAY:&a and some of the files that match the given filespec but don't fit the selectors, the array's dimension is bigger than its number of elements. Added code at the end of domydir() to resize the array so \fdim() returns the number of filenames in the array, and also made sure that element 0 contains that number too. ckuus6.c, 30 Nov 2006. This would be a nice elegant way to loop over a bunch of files, if it worked: for \%i 1 \ffiles(*) 1 { rename \fnextfile() xxx_\flpad(\%i,3,0) } But in this loop, Kermit skips every other file (beginning with the first) and then runs out of files halfway through the loop. Why? Because in commands like RENAME and DELETE, the filename parser is in a chained FDB with the switch parser. First the switch parser, cmswi(), gets its hands on \fnextfile(), passing it through the evaluator and thus getting the first filename, which it then sees is not a switch, so now the field is parsed by the next parser in the chain, cmifi(), which causes \fnextfile() to be executed again. In fact, the FOR loop has nothing to do with; the same thing happens like this: void \ffiles(*) delete \fnextfile() This deletes not the first file, but the second one. Obviously users can be told not to refer to \fnextfile() in chained-fdb fields: for \%i 1 \ffiles(*) 1 { .f := \fnextfile(), delete \m(f) } but this is hardly intuitive. I had some clever ideas of how to make \fnextfile() work as expected in this context but it's way too much magic. Better to simply document that \fnextfile() is "deprecated" and the array format should be used: for \%i 1 \ffiles(*,&a) 1 { delete \&a[\%i] } The difference is, an array element doesn't change every time it's referred to! Added a /PRESERVE switch to the COPY command to preserve the timestamp and permissions of the file. I did this using the Kermit APIs so it should work for any version of C-Kermit or K95. ckuus[26].c, 30 Nov 2006. Added COPY /OVERWRITE:{ALWAYS,NEVER,OLDER,NEWER} to control name collisions when copying across directories. ckuus[26].c, 1 Dec 2006. --- Dev.23 --- Fixed a bug in SET TELNET PROMPT-FOR-USERID, SET AUTH KERBEROS[45] PROMPT, and SET AUTH SRP PROMPT in which the user's string was compared with a literal (s == ""), reported by Pavol Rusnak. Worse, empty strings (if the test succeeded) were turned into null pointers, and then fed to strlen(). Fixed in ckuus3.c, 5 Dec 2006. Added an optional 4th argument to \findex(), \frindex(), \fsearch(), and \frsearch(): the desired occurrence number of the searched-for string. \frsearch() was a bit tricky. ckuus[24].c, 7 Dec 2006. Added \fcount(s1,s2) to tell the number of occurrences of s1 in s2. ckuus[24].c, 8 Dec 2006. Added \ffunction(s1) to tell if a given built-in function is available. ckuus[24].c, 8 Dec 2006. Changed RENAME /COLLISION:PROCEED to be /COLLISION:SKIP, which is clearer. ckuus[26].c, 8 Dec 2006. For communication protocols: INPUT /COUNT:n to read exactly n characters without any matching. Can be used, for example, with CONTENT_LENGTH in CGI scripts; NUL characters are counted but not collected. ckuusr.[ch], ckuus4.c, 8 Dec 2006. There was a bad bug in the date-parsing routines; it's been there for years. If a date string includes a timezone, e.g. "Sat, 9 Dec 2006 19:26:23 EST", and converting to GMT changes the date, the variables for day, month, and year (which are used later) were not updated, and the final result was a day off. Fixed in cmcvtdate(): ckucmd.c, 10 Dec 2006. Built OK with SSL/TLS. Tested with the POP script, found that I broke INPUT when adding the /COUNT feature; there was a path through the code that could leave the "anychar" variable unset and therefore random. Fixed in doinput(). The POP script, which does not use /COUNT, works again and so does a new CGI script, which does use /COUNT. ckuus4.c, 10 Dec 2006. Supplied a missing comma in the help-text array for HELP SET TERMINAL, which resulted in bad formatting in K95 around SET SNI-FIRMWARE-VERSIONS. ckuus2.c, 10 Dec 2006. Made "help locus" a synonym for "help set locus". ckuusr.[ch], ckuus2.c, 11 Dec 2006. This morning the Columbia FTP server was malfunctioning in a perfect way for me to implement and test an FTP timeout mechanism. The server would close the data connection after sending the file, but the client never saw the close and was stuck forever in a recv(). I added code to do a select() on the data connection prior to entering the recv(), with a timeout on the select() that the user can establish with SET FTP TIMEOUT. Built and tested on Solaris 9, clear-text FTP. Also built cleanly for FTPS and tested against a server that does not hang; I don't have access to an FTPS server that would tickle the timeout code. ckcftp.c, 11 Dec 2006. --- Dev.24 --- Fixed a bug in the INPUT /COUNT: parser: the array of search strings was never initialized, which didn't matter before, but with /COUNT:, if the first element was not a NULL pointer, we'd treat it as a search string, and then if it happened to match something in the input stream, the operation would stop before the count was exhausted. Fixed by (a) initializing the array, and (b) ignoring any search strings if /COUNT: was given. ckuusr.c, 13 Dec 2006. Removed a debug() statement from zsattr() that suddenly started making some version of gcc complain, reported by Gerry Belanger. ckufio.c, 13 Dec 2006. --- Dev.25 --- Some casts for the 3 interior args of the new select() call in ckcftp.c for HP-UX 9. 14 Dec 2006. Changed \fkeywordvalue() to accept a string rather than a single word as its second argument, so that more than one separator could be specified, and to return -1 on error, 0 if it found nothing, 1 if given a keyword but no value, and 2 if there was a keyword and a value. dokwval(): ckuus[24].c, 14 Dec 2006. Checked FTP timeout on command channel with FTP DIRECTORY of a big directory using a path into our ftp server that preserves the hanging behavior. The timeout was actually working, but the failure condition wasn't propagating back to the user, and there was no error message. Fixed in doftprecv2() and failftprecv2(): ckcftp.c, 15 Dec 2006. Added the obvious timeout checks for FTP uploads, but I have no way to test the code since our misbehaving FTP server does not hang when receiving files, only when sending them. But uploads work both with and without a timeout set, so at least no harm is done. ckcftp.c, 17 Dec 2006. When downloading with FILE DESTINATION NOWHERE (= /CALIBRATE), Kermit still checked the size of the incoming file and refused it if there wasn't enough free disk space, on platforms (such as VMS) where zchkspa()) actually works; reported by Bob Gezelter. ckcfn3.c, 18 Dec 2006. Built on Mac OS X 10.4.8 and NetBSD 3.1_RC3, all OK. 19 Dec 2006. --- Dev.26 --- Built on VMS 7.3-2/Alpha. Had to squelch a couple compiler warnings by changing some ints in the new \fpictureinfo() code from unsigned to signed, and fix a typo in the prototype for the new gettcpport() function. ckcnet.h, ckuus4.c, 22 Dec 2006. --- Dev.27 --- Parameterized pty routines and all references to them for file descriptor, rather than to use global ttyfd, thus allowing ptys to be created for different purposes. Tested on Solaris 9 and Mac OS X 10.4.8, with "set host /connect /pty emacs" (fine in both cases), and (more to the point) "set host /connect /pty kermit" -- here we make a connection from one Kermit process to another and transfer a file; works fine and wasn't especially slow either; a good sign. ckcdeb.h, ckutio.c, ckupty.c, 22 Dec 2006. Created a new version of ttruncmd() called ttyptycmd(), which works by calling do_pty() to get a pty to run the command on, and then in a loop, reads from the pty and writes to the net and reads from the net and writes to the pty, using select() to which of those it should do on each pass. First cut just uses single-byte reads and writes. Tested using Kermit itself as an external protocol. Works but slowly: 6000cps. Zmodem doesn't work at all. ckutio.c, 24 Dec 2006. Changed single-character read() and write() to buffered reads and writes, with ttxin() and ttol() used for network i/o. Using Kermit as the external protocol, this gives 450Kcps (about 1/3 normal on this connection). But now there's a problem: the loop doesn't know when to stop. How does it know when the process that is running on the pty has exited? With single character read()'s that are executed unconditionally when select() says the pty has data waiting, as in the first pass, I get EIO if there actually isn't any, and can exit the loop. But now, to avoid blocking, I call in_chk() to see how much data is waiting, and I don't try to read anything if it says nothing is waiting. If the process associated with the pty file descriptor has terminated, in_chk() would presumably get some kind of error, but it doesn't. I changed do_pty to return the pid of the fork where it execs its command so we can check the pid with kill(pid,0) when in_chk() of the pty says 0, but this doesn't help either; it seems like the process is not exiting, but of course it is. I could not find any legitimate way to test when the pty fork terminated. Select() always says the pty file descriptor was ready, no matter what. Select() never reports an exception on the pty file descriptor; in_chk(ptyfd) returns 0 and not an error. read(ptyfd,...) gets 0 but not an error. fcntl(ptyfd,...) doesn't get an error. Finally I tried write(ptyfd,c,0) and this indeed gets EIO (i/o error). With this, using Kermit as the external protocol works fine in Solaris but I tend to think this trick will not be very portable (it isn't). 24 Dec 2006. Made ttptycmd() use a more intelligent buffering scheme, fixed a few things about how I was setting up the select() call that should address some of yesterday's problems. Still doesn't work but it's progress. A: 25 Dec 2006. Debugging yesterday's code... Still, the error conditions are never set, we never detect when the pty closes. In Solaris, if select() says ptyfd is ready to read but in_chk() says there are no characters there, we can treat this as a loop-exit condition. But in NetBSD, in_chk() always says 0 when used on a pty (but works OK on a serial or net connection). Realized I could not use in_chk() on the pty because there is too much baggage with the communication path -- myread(), etc etc) -- so I replaced this with a simple ioctl(ptyfd,FIONREAD,&n). This works fine in Solaris but always returns 0 in NetBSD, despite what the man page says (i.e. that this function can be used on any file descriptor). OK, let's see.... select() does not return useful results. It says characters are waiting on ptyfd when they are not, and it never detects the closure of the pty..... Well of course not, because we are the ones who have to close it. Just because the process has stopped doesn't mean the pty is closed. So we're back to square one, how do we know when to close it? ckupty.c seems to keep the process ID in a global variable, pty_fork_pid (which is not the same as the pid now returned by do_pty(), which is useless, but I don't understand why). But it doesn't matter because when we kill(pty_fork_pid,0), we still get no error of any kind, even after we know the process has exited. I am completely flummoxed. select() lies, and even if it didn't, there is simply no completion criterion. In the loop, select() always says that the pty is ready to read. To be continued. 26 Dec 2006. Back to Square One, single-byte reads and writes. . This works for both ripple and Kermit. . Doesn't work for Zmodem but we'll deal with that later. . In this case FD_ISSET(ptyfd) is still true after pty closes. But the ensuing read() gets EIO so we know the pty is gone. That means the same thing should happen in the buffered version, no? Yes; I went back to the buffered version and replaced all the other nonworking tests by a blocking read of 1 byte on the pty and this detects the termination. Now: . ripple works perfectly (of course it's only one-way). . Kermit fails Let's call the remote, forked, redirected, external Kermit A and its local partner B. A sends its S-packet, B receives it OK and Acks. A apparently does not receive the ACK in time, so sends the S again, but OK. followed immediately by the F. B Acks the F. A sends the A, B Acks it. But now A sends a piece of the previous F packet and the the first piece of a D packet. Clearly the buffering is messed up. Sure enough, there was an extraneous statement incrementing a read pointer in a write section. Removing that cleared up the problems with Kermit, now we can send and receive substantial files efficiently in remote mode. Zmodem seems to work too, except that at the beginning a bunch of "**B0800000000022d"'s are stuffed into Kermit's command buffer, so after the transfer we get some error messages. In local mode, over a Telnet connection, Kermit works fine. Zmodem works OK too except it doesn't finish right, so at the very end rz on the far end is still waiting for something; if I cancel out of it with ^X^X^X^X^X, it deletes the file. So there still is something wrong with the termination test. Also you don't see anything on your screen when running Kermit or Zmodem this way. That's to be expected, since they are using stdio for the transfer, so they can't also be displaying progress or other messages. Built this on NetBSD again... Seems to work this time, but has trouble finishing, like Zmodem. Hmmm, on closer examination, it turns out that since in_chk() always returns 0 on the ptyfd, we fall into our new single-byte read code, so it's really slow, like 10K cps on a connection where 1M is the norm. 27 Dec 2006. Switched the pty from buffer peeking (FIONREAD) and blocking reads to to nonblocking reads (O_NONBLOCK / O_NDELAY). Works just fine on NetBSD except now we no longer get EIO at the end when trying to read from the pty process that has exited. In fact, we're back to square one again. not ioctl(), not fcntl(), not select(), not even read() gets an i/o error after the pty process exits. But in NetBSD, we have to use nonblocking reads because ... Hmmmm, maybe switch the fd between blocking and nonblocking for the test... Nope, NetBSD seems to be hopeless (later, Ed Ravin confirmed that similar problems have been observed with other applications that try to do this). Switching to Linux, I see that yesterday's Solaris code (blocking reads) works exactly the same way on Linux. Tried today's O_NDELAY method on Solaris. It works perfectly. And then I moved this one to Linux and it works perfectly there too. Except in both cases we have the weird thing with Zmodem at the end, but I think that's because rz/sz don't use standard i/o. On NetBSD, it still hangs at the end. Turns out that testing the pid works in NetBSD, even though it didn't in Solaris. Turns out read() gets an i/o error in Solaris and Linux but not in NetBSD. So checking the read result first, and then checking the pid if read() got zero bytes catches all three. 28 Dec 2006. Now the question of return code. In the original ttruncmd() function, we do a fork() and a wait(). When the external protocol program finishes, wait() gives us its return code and we can pass it on through \v(pexitstat) as well ttruncmd's own return code. But ttptycmd() has to interact with the pty continuously, so it can't just sit back and wait() for it. Instead we have to detect when the process has exited and then call waitpid() on the fork pid, before shutting down the pty. Tested on Solaris using Kermit as the external protocol and then inducing failure, or letting it run to completion. FAILURE and SUCCESS set appropriately in each case. Tested with Zmodem too, works OK except for the aforementioned cosmetic glitch at the end. Tested on NetBSD, all OK. To make K5 connection to Panix from Spam: set telnet debug on authenticate K5 init /realm:PANIX.COM /password:xxxxx set host shell.panix.com 23 /k5login Good... Now I try to send a file from Spam to Panix over the K5 connection using Kermit itself as the external protocol. It fails. Inspection of the debug log on the far side shows that the S-Packet was received correctly, good! This means we are reading the clear-text S-Packet from the external Kermit program, and that ttol() is encrypting appropriately. The remote Kermit sends the Ack and goes to read the next packet: ttinl() calls myfillbuf() and: SVORPOSIX myfillbuf calling read() SVORPOSIX myfillbuf=0 <-- read returns 0 SVORPOSIX myfillbuf ttcarr=2 SVORPOSIX myfillbuf errno=0 <-- and reports no error HEXDUMP: mygetbuf read (-3 bytes) mygetbuf errno=0 ttinl myread failure, n=-3 ttinl myread errno=0 ttinl non-EINTR -3[closing] This happens because myfillbuf() deliberately returns -3 when read() gets 0 bytes. I don't understand why this happens but the real problem is yet to come. The local Kermit (the one that has made the secure connection and is running the external protocol through ttptycmd()) eventually figures out that the transfer failed and when we reconnect, we get total garbage -- the encryption either stopped happening, or got out of sync. Looking at the local debug log, ttol() is doing its job, converting the initial "kermit -r\13" from plaintext to cyphertext, as shown by the hexdumps. Then it enters ttptycmd()... Hmmmm, wait, how can it send the "kermit -r" before it starts the external protocol? Never mind, worry about that later... Anyway, ttptycmd() says: ttptycmd loop top have_pty=1 ttptycmd loop top have_net=1 ttptycmd FD_SET ptyfd in ttptycmd FD_SET ttyfd in ttptycmd nfds=5 ttptycmd select=1 ttptycmd FD_ISSET ttyfd in ... ttptycmd in_chk(ttyfd) n=11 ttptycmd ttxin n=11 ttxin() asks for 11 bytes, myfillbuf() gets 11 bytes, and hexdump() shows the cyphertext, there doesn't seem to be any decrypting going on. Hmmm, it looks like the regular code calls ttinc() in a loop, rather than ttxin(). Maybe ttxin() doesn't have decryption hooks. No, that's not it, the code is there, but the Kermit packet reader does not use ttxin(), it uses ttinl(). But of course we can't use that for external protocols because it's designed only to read Kermit packets. Substituting a loop of ttinc()s for the ttxin() call fixes things (and strangely enough, it seems to be faster). And now we have our first external protocol transfer over a secure connection (external Kermit program, Linux over Kerberos 5 to NetBSD). Zmodem worked too for a short file but "something happens" with longer ones. 29 Dec 2006. New makefile target for Linux with Kerberos 5, linux+krb5, that doesn't include anything extra from SSL or other security methods (but apparently it is still necessary to include -DOPENSSL_097 in order to get the right names for the DES routines?). Ditto netbsd+krb5 for NetBSD, except in this case -DOPENSSL_097 is not necessary. makefile, 30 Dec 2006. Note to myself: On Panix: export LD_LIBRARY_PATH=/usr/local/kerblib make netbsd+krb5 "K5LIB=-L/usr/local/kerblib" "K5INC=-I/usr/local/include" Can't telnet-k5 from newly built Kermit on NetBSD; partway through the negotiations, just after "TELNET RCVD SB ENCRYPTION SUPPORT DES_CFB64 DES_OFB64 IAC SE" it dumps core. The last two lines in debug.log after this are: tn_sb[len]=5 encrypt_support[cnt]=2 Rebuilding with -DOPENSSL_097 doesn't change anything. Ed Ravin said they have two different Kerberos installations, Heimdal and MIT; maybe some mixup between the two explains the problem (Jeff concurs). The core dump occurs in ck_crp: encrypt_support(): debug(F100,"XXX ep not NULL","",0); type = ep->start ? (*ep->start)(DIR_ENCRYPT, 0) : 0; <-- Here debug(F101,"XXX new type","",type); Anyway, I can log in with Kerberos 5 to Panix OK from Columbia (sesame) using 8.0.201. So let's try to resurrect the Solaris version with everything: solaris9g+krb5+krb4+openssl+shadow+pam+zlib I hunted around to find where the current library and header file directories were... Last time I tried this (March 2006) it bombed, not finding libdes. Instead we have /opt/kerberos5125/lib/libdes425.a. Made a new cu-specific target that includes this; now we get farther; it blows up in ckcftp.c with tons of errors and warnings, which we can worry about later. Building again with -DNOFTP, it gets to ckuath.c (the first security module) and: ckuath.c:151:18: error: krb5.h: No such file or directory ckuath.c:152:21: error: profile.h: No such file or directory ckuath.c:153:21: error: com_err.h: No such file or directory ckuath.c:176:28: error: kerberosIV/krb.h: No such file or directory In file included from /opt/openssl-0.9.8d/include/openssl/des.h:101, from ckuath.c:219: Found krb5.h in /opt/kerberos5125/include/krb5.h, added a -I for this directory ... Now we get lots of warnings in ckuath.c, but it completes OK, then we wind up bombing out in ck_crp.c; I don't know why -- there are all the same warnings (related to argument passing to DES functions), but no errors. I have no clue. Tried to resurrect the solaris2x+krb4 target; this required changing -lkrb to -lkrb4 and -ldes to -ldes425. Lots of warnings in ckutio.c, ckcnet.c, ckctel.c, then it bombs out in ckcftp.c because it can't find krb.h. I found it, adjusted the -I flags, but now it bombs because krb.h itself #includes , which of course it can't find because the brackets mean it's looking in /usr/include/kerberosIV/, which, of course, the sys folks have removed. Giving up on Solaris again. Later, Jeff said "Solaris does not publicly export the krb5 libraries. You need to build the MIT Kerberos libraries separately and link to them." 30 December 2006. Changed copyright date to 2007. ckcmai.c, 1 Jan 2007. With Ed Ravin's help, successfully built C-Kermit with Kerberos 5 and OpenSSL (netbsd+krb5+openssl+zlib), but it does not make K5 connections; it gets hung up in the Telnet negotiations. 3 Jan 2007. Downloaded MIT Kerberos 5 v1.4.4 to Solaris 9, 54MB worth. This is just so I can build a Kerberized C-Kermit for testing ttyptycmd(). Ran the configure program, got a few warnings but it didn't fail (should it?) Did "make install", specifying a private directory but it failed immediately with "cannot stat libkrb5support.so.0.0: No such file or directory". OK, I tried. 3 Jan 2007. Made a new makefile target for Mac OS X, macosx10.4+krb5+ssl, ran it on Mac OS X 10.4.8. It bombs out in ckcftp.c with: ckcftp.c:551: error: static declaration of 'gss_mech_krb5' follows non-static declaration /usr/include/gssapi/gssapi_krb5.h:76: error: previous declaration of 'gss_mech_krb5' was here". Ditto for gss_mech_krb5_old, gss_nt_krb5_name, and gss_nt_krb5_principal. Tried again with -DNOFTP. We get lots of warnings in the network modules, but they complete. But ck_ssl.c bombed with a conflict between its own declarations of encrypt_output and decrypt_input and the ones in ckuat2.h; removed the prototypes from the latter (as Jeff advised) it built OK and it works OK too. Built with FTP too, but with link-time warnings about the aforementioned gss_* symbols. #ifdef'd them out (gss_mech_krb5, gss_mech_krb5_old, gss_mech_name, and gss_mech_principal) for MACOSX, where these symbols are exported by the library. Now it all compiles and links OK, and runs OK too. 3 Jan 2007. Spent a day hunting around for a version of Zmodem that would build and execute on Mac OS X, finally found one. Now at last I could try a Zmodem external-protocol transfer over a secure connection. But phooey, C-Kermit's pty support didn't work on this box. Kermit finds master /dev/ptypa OK, then in ptyint_void_association() tries to open /dev/tty but gets ERRNO=6 "device not configured" (which is apparently OK, because the same thing happens on other platforms where this works), then tries to open slave /dev/ttypa and gets ERRNO=13 "permission denied" because, indeed, I don't have r/w permission on the device. Left a message. 4 Jan 2007. Changed TRANSMIT /BINARY output buffer size from 252 to 508 to avoid TCP fragmentation. Need to add a SET command for this later. ckuus4.c, 5 Jan 2007. Found another Mac where the ptys weren't protected against me, make a K5 connection and transferred a largish file with Zmodem with zero glitches, except it was kind of slow, 84K cps. Well, we're doing single-character reads on the net (ttinc()'s instead of ttxin()). Hmmm, but then I did it again and got 2.2Mcps. Success was reported, but it actually didn't work; it only sent the first quarter of the file.... Oh well, at least now we have a testbed. 5 Jan 2007. Tried again, saw that the file is actually transferred instantly but then we're not picking up the protocol at the end. Theory: after the transfer finishes, we come back to the prompt on the remote host, which means we have something to read from the net and write to the pty, but the pty has already exited. AFTER THE PTY IS GONE, WE DO NOT WANT TO READ FROM THE NET ANY MORE. Adding this test makes Kermit succeed right away when sending the same largish file, with a transfer rate of 4M cps, that's better. But the rz program on the far end is evidently not receiving the goodbye handshake from the receiver, because it sits there foreever in its *B09002402009418 mode until I ^X^X^X^X^X out of it, at which point it deletes the file it already received, not very helpful. In the code, I read from the pty if the pty is open and there is room in the buffer. This means that when we get to the end, either there is no room in the buffer (unlikely) or the last bit sent by sz before exiting was cut off when the fork closed. Why do we get in this fix only with Zmodem and not with Kermit? In Mac OS X, after sz exits, we get ERRNO=5 if we try to write to the pty, but we still get no errors after that if we try to read from it. Still, prior to this we did more than 20 unproductive nonblocking reads from the pty (no error, no bytes) without incident; there did not seem to be anything waiting. In fact, the last thing we read from the pty were the text messages that are issued at the end of the transfer: "rz 3.73 1-30-03 finished." After which it pauses a second and spits out a message about UNREGISTERED COPY. Figured out how to build lrzsz, in hopes that the previous problems were with rzsz and crzsz's fiddling with file descriptors, but I get the same behavior. Which is good, I guess, because if I can fix one, I fix them all. Or not... Testing lrz by itself (not under C-Kermit), I see that it doesn't work at all with Kermit's own Zmodem implementation. OK, here's one problem: at the end of the transfer, the Omen Zmodems print stuff like "Please read the license agreement", Kermit dutifully reads this from the pty and sends it to the host, the host shell says "Please: command not found", issues its prompt again, which Kermit reads, feeds to the pty, and apparently the pty echoes it, so we send it back to the host, and there ensues an infinite loop of getty babble until the pty closes. Now, there ought to be a way to make the external protocol shut up, like Kermit's -q(uiet) flag, but these are unregistered versions so you can't shut up the messages. In fact, the transfer works, but the getty babble at the end ruins the experience. Now I'm beginning to wonder how any of these programs ever worked as external protocols. Hmmm, now that I try it, I see the same thing happens the old way, when using ttruncmd() rather than ttptycmd(). Reading the crzsz documentation I see it says that messages come out on stderr. OK, that's progress. In ckupty.c I try redirecting 2 to /dev/null. Well good, this filters out the messages from csz, but we still get getty babble on the prompt. In the debug log, we read the last bunch of stuff from net, 618 bytes of Zmodem stuff... Now what happens? Zmodem on the remote exits, the host prints its prompt. Kermit, of course, reads the prompt from the net, now come to the bottom of the loop and we have 7 bytes to write to the pty, and no error condition, so we continue the loop. select() says that the pty is ready for writing. We write the 7 bytes and and get no error. Loop again, this time select() says the pty has data waiting. Sure enough we get the prompt back, and send it to the net, and thus begins the getty babble. There are two causes for this: 1. crzsz does not exit immediately; it sleeps for 10 seconds after printing its nag message. 2. During this interval the pty seems to be echoing what is sent to it. csz is not echoing; I checked. Anyway, removing the pause doesn't seem to make a difference. ttptycmd() needs to: . TELL the pty module to redirect stderr to /dev/null . SET PTY TO NOECHO (master or slave?) Tried setting the pty to noecho: termbuf.c_lflag &= ~(ECHO|ECHOE|ECHOK); and this seemed to stop the getty babble. After the file transfer, I read back the prompt from the host shell, I write the prompt bytes to the pty; there is no error. And now select() simply hangs forever (or times out if a timeout is set). The question here is: why didn't writing to the pty produce an error? And, because we never detect the pty has exited, we can't set a good return code. 5 Jan 2007. Moved pty fork testing to a separate routine, pty_get_status(), and added a call to it from the place where we time out, in case the fork terminated; then we can get and return its status. 6 Jan 2007. Added calls to pty_get_status() to every place where we suspect a pty error, tried again with lrzsz, crzsz, and regular rzsz. All three work, but in each case waitpid() indicates that the sz program gave exit code 1 (failure). ckutio.c, 7 Jan 2007. Changing the subject... On my test system, every time I execute ttptycmd(), I get "permission denied" on /dev/ttyp3. Then I run it again and get to ttyp4 which is OK. I wanted to skip past any pty for which I lack permission and try the next without raising an error. Added debugging code: 16:25:23.524 pty_getpty() pty master open error[/dev/ptyp0]=5 16:25:23.524 pty_getpty() pty master open error[/dev/ptyp1]=5 16:25:23.524 pty_getpty() pty master open error[/dev/ptyp2]=5 16:25:23.524 pty_getpty() found pty master[/dev/ptyp3] 16:25:23.524 pty_getpty() slavebuf [2][/dev/ttyp3] So it already was skipping past open errors; ttyp3 was opened successfully. The problem is that ptyp3 is rw-rw-rw-, but the corresponding master, ttyp3, is rw--r----. It seems the code assumes that if the master can be opened, then so can the corresponding slave. Unfortunately, the code is not structured to allow us to skip ahead to the next master if the slave can't be opened. 7 Jan 2007. Spent a couple hours trying to rearrange the code in the pty module to skip past inaccessible slaves but it was a rabbit hole, not worth it, backed off. 8 Jan 2008. Tried an upload over a secure connection using lsz. Unexpectedly, this time it worked; not only was the file (about 0.5MB) transferred correctly, but Kermit detected the fork's termination and got the pid's exit status, and, for the first time, correctly reported a successful transfer. I have no idea why this works today and not yesterday. More tests; it works most of the time. It works with csz and with regular sz too. (days later...) ckucns.c seems to do the right thing; it recognize the ZSTART string, activates the Zmodem-Receive APC, and returns. doconect() sees the APC and begins to execute it. The RECEIVE command results in a call to the GET command parser, doxget() (IS THAT RIGHT?), then comes a ttflui(), which throws away a bunch of stuff. Finally we get to ttptycmd(), we get a pty and run lrz in it, select() says stuff is waiting from the pty, but read returns 0, errno 0. Skipping the ttflui() in doxget() if the protocol was not Kermit didn't seem to make difference. ckuus6.c, 8 Jan 2007. The problem is that in this case, reads from the pty never get anything (no data, no error), write always gets an error. It's as if the pty was not being set up right, or we're using the wrong file descriptor. And if we skip the autodownload? Same thing. OK, putting downloads aside for a moment, let's get uploads working as well as possible. At this point we have the odd situation (at least in this configuration) that the upload succeeds, but now for some reason we are unable to read the exit status from the process, even though this was working before, so ttptycmd() returns 0 (failure), yet Kermit reports success. Well, it turns out that kill(pty_fork_pid,0) was gumming up the works. If we use only waitpid() all is well, I think. waitpid() with WNOHANG returns -1 with status -1 errno 0 if the pid has not exited, and it returns the pid and status > -1 if the process has exited. Fixed pty_get_status() to do it this way. ckutio.c, 7 Jan 2007. Let's move this from Mac OS to NetBSD and see how it works. Well, the file transfer was just fine, but then I used some sexps to calculate the elapsed time and transfer rate, and Kermit hung in dosexp(). Fine, ignoring that... The debug log shows that ttptycmd() gets the pty OK, master and slave, the i/o goes smoothly, and waitpid() does its job perfectly. Solaris, same deal; ttruncmd() goes smoothly, but then the sexps afterward get "Arithmetic exception". Turns out there was a BAD bug in dosexp() that allowed an integer division by 0 to occur under certain circumstances; it's always been there. Fixed in dosexp(): ckuus3.c, 8 Jan 2007. After noticing a few problems running the pop.ksc script in production over the past year, rewrote \femailaddress() to be more reliable and a lot simpler. ckuus4.c, 9 Jan 2007. Back to ttptycmd()... When we left off, we could send but not receive. Set up a test case using Kermit as the external protocol for receiving a short file. If I SET STREAMING OFF and use short packets, it actually does work, so it's not a complete failure to function, but apparently a lack of flow control for the pty. Began by completing the parameterization of the pty module, so it can be called for interactive use (fc 0) or for running protocols (1). Confirmed that everything works at least as well as before (e.g. "set host /pty emacs" vs external protocols). ckcdeb.h, ckutio.c, ckupty.c, 9 Jan 2007. Found in HP-UX "man 7 pty" a description of ioctl(fd,TIOCTTY,fc) which is exactly what we want: fc 0 turns off all termio processing and guarantees an uninterrupted, unmolested, flow-controlled stream of bytes in both directions. This function also exists in Linux, but not in Solaris, NetBSD, or Mac OS X (TIOCNOTTY is not what we want, it does something else entirely). Another possibility is TIOCREMOTE, which "causes input to the pseudoterminal to be flow controlled and not input edited, regardless of the terminal mode". This one exists in at least HPUX, NetBSD, Solaris, and Mac OS X. Solaris: builds OK, but at runtime we get ENOTTY ("Inappropriate ioctl for device"). By the time this happens, it's hard to tell from the code whether the fd we're using is for the master or the slave; TIOCREMOTE can be used only on the master. Close inspection shows that I am indeed doing that; ptyfd as seen by ttptycmd() is truly the master, i.e. the /dev/ptyXX device, not the /dev/ttyXX device (the slave fd can't be seen at all, as it exists only in a separate fork). OK, so now we know that TIOCREMOTE can't be used on Solaris. NetBSD: Somehow, whether as a result of today's fiddling or the phase of the moon, the code in pty_open_slave() that tries to open /dev/tty started failing on NetBSD ("Device not configured"). Changing it to be run only if fc == 0 (which doesn't seem to hurt anything), once again I get ENOTTY on the TIOCREMOTE ioctl. Zmodem works but Kermit totally fails (the fork exits immediately with an exit code of 0, even though it didn't do anything). Mac OS X: Exactly the same sequence and results as NetBSD. Linux: It did not execute the new ioctl at all; apparently the TIOC symbols are hidden or not exported or something. Where we stand: . Downloads don't work . Uploads got slow again . Kermit doesn't work at all as an external protocol Actually if I take the debugging out it goes fast, but it doesn't finish. All today's work on ttptycmd() looks like a dead end. To roll back to yesterday: cp ckutio.c-20070108 ckutio.c cp ckupty.c-20070108 ckupty.c cp ckupty.h-20070108 ckupty.h or to continue with today's: cp ckutio.c-20070109 ckutio.c cp ckupty.c-20070109 ckupty.c cp ckupty.h-20070109 ckupty.h Comparing Monday's and Tuesday's pty-related code, the differences are: 1. Passing of function code to and among pty modules. 2. Skipping the TIOCSCTTY ioctl and the open("/dev/tty") test. 3. Attempting to put pty in TIOCTTY or TIOCREMOTE mode. Commenting out 2 and 3 should put us back where we were on Monday if the parameterization was done right. And with this, on Solaris, downloading with Kermit external protocol works but slowly, 8K cps, with or without debugging. Debug log does not show any obvious bottlenecks; select() takes anywhere between no time at all and 0.1 seconds to return. If I increase the pty-net buffer size from 1K to 4K, the rate goes up to 55K cps. If I make it 8K I get 136K cps. With 16K I get 346K cps. 32K: 395K cps -- this last one isn't worth the doubling. But at 24K I get 490K cps, sometimes twice that. Let's stick with 24K for now. Downloading with Zmodem (rzsz) works at the same rate, but now we're back to seeing the getty babble (Several "**B0800000000022d") at the end. 10 Jan 2007. Moving to Mac OS X, everything works the same as on Solaris, except I don't get the Zmodem getty babble there, not even with Omen rzsz. Tested sends in both remote and local mode, the latter over a secure Kerberos 5 Telnet connection, using C-Kermit, rzsz, lrzsz, and crzsz, all good. 10 Jan 2007. Now we're back where we were yesterday morning, but with better throughput. The big issue then was receiving files. But yikes, now it works! Not only that, I got a transfer rate of 2.1M cps. That's using Kermit protocol, streaming, and big (4K) packets. Which didn't work before. Not a fluke either, I uploaded bigger and bigger files up to 6MB, they all went smoothly, at rates between 1 and 2 MBps. 10 Jan 2007. Not so great in Zmodem land, however. If I start the external-protocol receiver on the far end, escape back and start a Zmodem send... nothing. If I leave the remote C-Kermit at its prompt (where it supposed to recognize the Zmodem start string), still nothing. On the other hand, if I do it with a script instead of by hand: def xx output take blah\13, send /proto:zmodem \%1 it works, at least intermittently. But that's in remote mode. We won't be using this in remote mode. In local mode, where we have a secure connection to another computer, it seems we can read from the pty and write to the net, but we time out waiting to read from the net; nothing arrives. Well, we know that i/o works both ways, so there is some kind of screwup with the Zmodem protocol start itself. Increasing the (still hardwired timeout) from 5 to 22sec and driving the whole process with a script so as to avoid autodownload as well as manual dexterity effects... It just sits there forever, way longer than 22 sec. ^C'ing out, I see that sz was indeed started on the far end and the protocol was executing. But it looks like the receiver (the one running under ttptycmd()) is getting trashed packets, because (a) it seems to be sending the same thing over and over again, and (b) sometimes it waits as long as 10 seconds before anything arrives from the remote. Maybe I was too impatient; I interrupted it after 4 minutes but it seems to have been making some progress. Whenever there was data available to read from the net, it was always 65 bytes, and it was not actually the same data over and over. This is using lrz as the external protocol. crz gets a bit farther. In this case we read up to 24K at a gulp, but the amount varies a lot. It looks like we took in about 1.2MB of Zmodem protocol data, but were only able to output the first 20K of the file. Clearly there were lots of errors. In the end, the crz exits with status 1 (failure). Anyway it looks like we're back at needing to find a way to accomplish something like TIOCREMOTE on the pty, which is where we came in. 10 Jan 2007. Without any way to make the pty transparent and flow controlled, it would seem to make sense to write to the pty in smaller chunks than we do to the net. I left the read-from-pty-write-to-net buffer at 24K and changed the read-from-net-write-to-pty buffer to 48 bytes. Upload using lsz worked but took about 3 minutes. Actually it didn't work. On the local end it seemed to work, but the file did not appear on the remote end. Tried this several times, each time with different results, adding more debugging each time. The problem this time was that the pty read could get EWOULDBLOCK. Changed the code to not treat this as an error, now Zmodem uploads are solid again except I never got EWOULDBLOCK again either, even though I repeated the same upload about 1000 times (with throughput of over 2MBps even with debugging on), so the test for it has not been exercised. OK, uploads still work. Back to downloading... The very first pty read gets 0 bytes, followed by the fork test that shows that it exited with exit status 2. Next we try starting sz with some different options on the far end: -q: quiet (no messages): for some reason this gets totally stuck. it looks as if this option is misdocumented; sz seems to be sending the letter C (as in Xmodem 1K or whatever) -e: escape (all control chars): first attempt to read pty finds the process gone with exit status 2. -k: send 1k blocks: this one didn't stop immediately. It reads 48 bytes from net, writes 48 to the pty with no error. Then reads 21 bytes from the pty, writes them to the net OK. Then reads 48 bytes from net, writes them to pty OK, reads 21 from pty, writes to net OK, etc etc... It appears to have worked but (final read from pty returned 0, fork test showed lrz exited with status 0), but only 754 bytes were received from the net when the file is 420K... Well this only goes to show that the faster we shove stuff into the pty, the worse it gets. Zmodem downloads won't work unless we can make the pty transparent and flow-controlled. So to summarize today's developments: . separated in/out buffer sizes . handled EWOULDBLOCK . found out that sz options don't help much 11 Jan 2007. Next day. This has got to be the most delicate code ever, it's like Whack-A-Mole, fix A and B pops up. Even without touching it, something that worked perfectly a 2:00 doesn't work at all an hour later. Maybe I could have used pipes instead of ptys, but pipes have problems of their own. There has to be a way to do this. The telnet server, the SSH server, etc -- they all run on ptys, and we can upload files to them with Kermit. Why? Because Kermit puts its terminal into all the right modes using the time-honored methods of ttpkt() and ttvt(). Perhaps all we need is a copy of ttpkt() that operates on the pty. On that theory, let's go back to Kermit as the external protocol. It's important to suppress all messages and displays. With that, uploads work fine, no hitches. Downloads: We fail right away. The debug log shows the Kermit program that we are starting in the pty says: "" - Invalid command-line option, type "kermit -h" for help. But of course we are not giving it an invalid command-line option. Switching to gkermit for the external protocol, now we see that no matter command-line options we use, we read 0d 0d 0a from the pty and then the next time we go to read from the pty we get 0 bytes and waitpid() says the program has exited with status 1. Why should downloading be different from uploading? ttptycmd has no idea, it does everything the same. The only difference would seem to be which side sends first, but even that tends to get washed out by each program's startup messages. Downloading with Kermit worked 2 days ago, what's different now? The buffer sizes. Putting the net-to-pty back up to 24K (from 48 bytes)... Now it works again. Conclusion: Kermit conditions the pty correctly, Zmodem does not. Therefore ttruncmd() must duplicate what ttpkt() does. Or not. Because rz works fine on ssh/telnet ptys too. But not on our pty. lrz exits immediately with status code 2 = 01000 but there are no clues in the lrz.c source code, I don't even see this exit status set anywhere. Unredirecting stderr, I see that the error is "lrz: garbage on command line". Why do both Kermit and Zmodem sometimes think they are receiving an invalid command line? If I could capture the garbage... Side trip #1: ("pty.log",O_WRONLY) gives "no such file or directory". Changed this to ("pty.log",O_CREAT,0644) and now it doesn't get an error, and it creates the file, but not with 0644 permissions, and with nothing written in it. How come nothing works? Fine, the debug log shows that ttptycmd() receives the correct string (e.g. "lrz -v"). It passes it to do_pty() correctly, and do_pty() passes it to exec_cmd(), which runs cksplit() on it, coming up (in this case) with "lrz" and "-v", which is right, and then: args = q->a_head + 1; execvp(args[0],args); execvp() wants the args array to have a null element at the end. cksplit() does indeed do that, or at least the code is there. Added code to exec_cmd() to verify the argument list and that it is null-terminated. So far it is. Anyway, we have traffic between the Zmodem partners, but no joy. Commenting out the bit that redirects stderr, now I can see it on my screen in real time: lrz waiting to receive.Retry 0: Bad CRC Retry 0: Got ERROR Retry 0: TIMEOUT Retry 0: TIMEOUT Retry 0: TIMEOUT Retry 0: TIMEOUT etc etc, forever. Trying sz -e on the far end, I get: Retry 0: Bad CRC Retry 0: Garbage count exceeded Retry 0: Garbage count exceeded Retry 0: Garbage count exceeded Retry 0: Garbage count exceeded Retry 0: Garbage count exceeded Retry 0: Garbage count exceeded Retry 0: Garbage count exceeded ... Retry 0: Got ERROR Retry 0: Bad CRC Retry 0: Got ERROR Retry 0: Got ERROR lrz: xxufio.c removed. So apparently it's not a matter of escaping. Trying some other stuff, I caught the command-line problem in the act: lrz: garbage on commandline Try `lrz --help' for more information. Debug log shows: cksplit result[lrz]=1 cksplit result[-v]=2 exec_cmd arg[lrz]=0 exec_cmd arg[-v]=1 exec_cmd arg[]=2 An empty string at the end instead of a null pointer. I really do not see any way that could happen, but rather than dig into cksplit() again after all these years I added a test for this in exec_cmd(), which, of course after adding it, never encountered this behavior again. Fiddled with pty buffer size again. Made it 512 bytes instead of 24K. Zmodem downloads are the same (Rety 0: TIMEOUT, over and over). But I don't see what the problem is -- every time we receive n bytes from the net, we write n bytes successfully to the pty and there are no errors. But it also looks like the remote sender is sending the file header over and over because it's not receiving an acknowledgment. If we're not losing data, then maybe it's a transparency problem. Tried uncommenting the TIOCblah stuff I commented out before. Now instead of only timeouts I get: lrz waiting to receive.Retry 0: Bad CRC Retry 0: Got ERROR Retry 0: Bad CRC Retry 0: Got ERROR Retry 0: Bad CRC Retry 0: Got ERROR Retry 0: TIMEOUT which is odd because the TIOCREMOTE ioctl failed with errno 14, EFAULT, bad address, which should indicate it had no effect. We're still receiving data from the remote in tiny chunks (from 12 to 65 bytes), apparently the same stuff (file header), and writing them to the pty successfully but nothing... Looked at cloning ttpkt() for the pty, but these stupid routines use global tty mode structs so it's not going to be easy. Well, we got exactly nowhere today, but I think I'll leave stderr as it is so users will see some feedback; no reason not to. WHY DO KERMIT DOWNLOADS WORK AND ZMODEM NOT? Is it 8-bit transparency? Up til now I've been testing with text files. If I try to download a binary what happens? Fails after 99 seconds. Packet log from the far end shows that as soon as the first packet containing 8-bit data is sent, everything stops. At least I got one of these: 17:23:56.475 exec_cmd arg[gkermit]=0 17:23:56.475 exec_cmd arg[-qr]=1 17:23:56.475 exec_cmd arg[]=2 17:23:56.475 exec_cmd SUBSTITUTING NULL=2 <-- the code I just added Doing this again shows the same thing on the near end. All the 7-bit-only packets are sent and acknowledged OK. Three 8-bit data packets arrive and nothing else happens after that. This is with G-Kermit. The same thing happens with C-Kermit receiving. But if I change C-Kermit's .kermrc to turn off streaming and use a short packet length: The transfer works, even though it's sending 8-bit bytes. So the problem is not 8-bit data after all, per se. Facts: . Kermit can receive streaming transfers of 7-bit files. . Kermit can not receive streaming transfers of 8-bit files. . Kermit can receive nonstreaming transfers of 8-bit files with short packets. . Kermit can receive nonstreaming transfers of 8-bit files with 1K packets. . Kermit can receive nonstreaming transfers of 8-bit files with 4K packets. So it's the combination of streaming and 8-bit data? 12 Jan 2007. As a test I made a new routine pty_make_raw() that does cfmakeraw() (a nonportable "POSIX-like" function known to be used on ptys in applications that do approximately what we're attempting). Results: Solaris: errno 25 - inappropriate ioctl for device. This happens even when we try to get the terminal modes with tcgetattr(), which is completely nuts. We pass it the file descriptor of the pty master, which is supposed to work. But in Mac OS X, there are no errors. But downloads still don't work; lots of errors but the pattern is different. Using a very small buffer: Retry 0: Bad CRC Retry 0: Garbage count exceeded Retry 0: Garbage count exceeded Retry 0: Got TIMEOUT Retry 0: TIMEOUT Retry 0: Bad CRC Retry 0: Bad CRC Retry 0: Garbage count exceeded Retry 0: Garbage count exceeded Retry 0: TIMEOUT Retry 0: Got ERROR Retry 0: TIMEOUT Retry 0: Garbage count exceeded Retry 0: Garbage count exceeded Retry 0: Bad CRC Using a bigger buffer: Retry 0: Garbage count exceeded Retry 0: Garbage count exceeded Retry 0: Garbage count exceeded Retry 0: Garbage count exceeded Retry 0: Garbage count exceeded Retry 0: Garbage count exceeded (several screensful) Various other combinations... Nothing seems to work. Insight: telnetd does exactly what we want to do, sort of. But it uses TIOCPKT, so every time it reads from pty, it receives one control byte and then the data bytes, which would complicate our buffering scheme considerably. Anyway the TIOCPKT ioctl() fails on Mac OS X with 14 "Bad address". Also see: snoopserver.c (found in Google). It seems to do things in a slightly different way -- it sets stdout to raw and then dups it to the slave side of the pty? Maybe it's a mistake to use the ckupty.c routines. They are designed for creating and accessing an interactive session. Maybe just copy one of the other programs. 18 Jan 2007. Tried going back to blocking rather than nonblocking reads to see if it would make a difference, after all the other changes. Nope. OK, let's look at some of these other programs... snoopserver.c. I don't know exactly what this is or where it's from or what platform it runs on and there are no comments to speak of, but it does approximately what ttptycmd() does. To get a pty it uses openpty(): if (openpty(&pty, &tty, NULL, NULL, NULL) == -1) then creates a fork. In the fork, it closes the pty (master) and manipulates the modes of the tty (slave), dups tty to be stdio, and then doex execv() on the command. Meanwhile the upper fork closes the tty (slave), gets the attributes of stdin, using atexit() to have them automatically restored on exit. Then it sets stdin to raw mode and enters the select() loop on stdin, the pty master, and the net. It uses regular blocking reads. It does not use TIOCPKT or anything like it. openpty() is supported on: Linux, Mac OS X, NetBSD, FreeBSD, ... openpty() is NOT supported on: Solaris, HP-UX, ... 1. Try copying the pty code, but keep everything else the same. I did this; it compiles and starts OK, upper fork (ttptycmd) debug log shows no errors, but nothing happens. Logs show that the Kermit program that is started in the subfork seems to die as soon as it reaches eof on its init file. The good news, at least, is that select() doesn't report report that the pty is ready to be read. Clearly the file descriptors aren't being assigned as expected, or as before. In ckupty.c getptyslave() dup2's the slave fd to 0 and 1. The new code does exactly the same thing. Debug log makes it look like the forked kermit is not receiving its command line. But now I'm not even sure that the forked kermit started at all. ps from another terminal doesn't show it. 19 Jan 2007: Noticed that in snoopserver, the select() calls use standard input and output file descriptors, rather than the pty master. Made that change... In doing that I had to look at every file descriptor in every line of code and discovered a couple mistakes, fixed them, put back the original code but with the fixes, tried it, but no change; can upload OK but still can't download with Zmodem without lots of errors and ultimate failure. Going back to the alternative version and trying to get the the file descriptors sorted out, now it appears that the external Kermit program never even starts in the lower fork. After a bit more fiddling I sort that out, but now when the lower Kermit program goes to open "/dev/tty" it gets errno 6 "Device not configured". Forcing it to use stdio with "-l 0", it gets past this and actually sends its first packet. But the Kermit on top reads nothing from the pty. Next, I change the pty fd from STDIN_FILENO and STDOUT_FILENO to slavefd. No difference. Next I comment out the dup2() calls. This time I get some action. The transfer starts, but only one packet comes. Log shows that the lower Kermit sends its S packet. The upper Kermit receives the ACK but the lower Kermit never gets it. The write to the pty succeeds, no error. Different combinations give different results. If write to master and read from the slave, I get packets in both directions but tons of errors.... This happens only if I comment out the dup2()'s. 25 Jan 2007: After leaving it sit for a while, and realizing that what I'm trying to do has to be possible because so much other software does the same thing (e.g. Telnet servers), I put things back to how they were originally -- the upper fork (Kermit) uses the master and the lower fork the slave. The upper fork puts the master in raw mode, the lower fork puts the slave in raw mode. The lower fork dup2's the slave fd to stdin/out. Send file in remote mode using external Kermit: works OK but select() times out at the end. This means that the self-contained pty code in ttptycmd() is sorted out -- all the file descriptors go to the right place, etc, and now we can use this routine as a testbed, rather than the original ckupty.c-based one. But send with lsz, csz, and regular rz: Nothing happens, times out after 0 bytes of i/o. Once again, Kermit works, Zmodem doesn't. The reason for running Zmodem in a pty is so its i/o will work as it does on a terminal, no matter how it may fiddle the file descriptors. So why don't we see a single byte come out? Commenting out pty_make_raw(), I get a successful Zmodem send using lsz. csz manages to get the filename across, but then gets stuck. regular sz, on the other hand, works perfectly. Testing csz by itself (not under Kermit), I see it fails in exactly the same way ("Got phony ZEOF", etc). OK, forget crzsz. OK, let's move to local mode over a Kerberized Telnet connection... Uploading (sending) with external Kermit protocol... works. Downloading (receiving) with external Kermit protocol... works. Uploading with sz... works. Downloading with lrz... Gets tons of errors and fails. Running pty_make_raw() on the slave but not on the master: no difference. Running pty_make_raw() on the master but not on the slave: no difference. Back where we started... Either: . Zmodem is overdriving the pty, no matter what modes we put it in. . It's a transparency problem. Theoretically we should be able to test these by using different sz switches: -q: quiet (should always use this) -e: escape all control characters -B n: Buffer n bytes (rather than whole file) -L n: Packet length -l n: Frame length (>= packet length) -w n: Window size -4: 4K blocksize (doesn't help) -q by itself doesn't help. -q -e, this one worked but still got about 100 errors and was very slow. -q -e -l 200 -L 100, failed fast and bad. -q -e -w 1. Failed quickly. -q -e -w 1 -B 100. Eventually failed. -q -w 1, Eventually failed. -q -l 1024, this gets much more errors, definitely need -e. -q -e -l 1024, got pretty far before failing. -q -e -w 1 -l 1024, also got pretty far before failing. -q -e, this one got farthest of all, about 48K, before getting errors. In the latter combinations that work somewhat better, we always get up to 16K, or 32K, or 48K, before the errors start coming out and piling up. Sometimes the errors are recoverable and we receive as much as 300K successfully before giving up. Now that we have data flowing pretty well (but not well enough), tried reinstating pty_make_raw(), but it hurt more than helped. As a sanity check, I tried transferring from the same host over the same kind of connection (Kerberized Telnet) directly to K95's built-in Zmodem protocol, and that worked fine. So the problem is definitely in the pty. Or more precisely, where Kermit writes incoming net data to the pty master. 26 Jan 2007: Tried changing the size of the net-to-pty buffer from 24K to 1K. Result: total failure. Set both buffers to 1K. Still total failure. Set both to 4K: now we get about 45K of data, then failure. Put them both back to 24K, still fails totally -- the same code that worked pretty well yesterday. Actually, no downloads work, not even Kermit, not even of text files. 27 Jan 2007: Since I have not been able to find a way to make ptys work for this, I made a third copy of this routine, this time using pipes instead of ptys. The disadvantage here is that if the external protocol does not use stdio, the pipes won't work, but one thing a time... Inferior Kermit starts in lower fork, but when it tries to send its first packet it gets errno=9 EBADF, Bad File Descriptor. Substituting G-Kermit as the external protocol, which is simpler, reveals that the problem is that the external protocol gets errors when it tries to manipulate the its stdio file descriptors with ioctls, etc; these are not valid for a pipe. The pipe mechanism itself works. If I take out the test for ttpkt() failing in gkermit, the file transfer works OK. Trying Zmodem... Sending works OK; receiving works a lot better than with ptys (it got 360K into the file before failing). Making the buffers smaller, doesn't help. I'm starting to wonder if the problem might be in my buffering code, rather than in the pty or pipe interface... Try making a version that does single-character reads and writes. This one reads the first packet from the lower Kermit and sends it. It is recognized by the other Kermit, which sends an ACK. We see the ^A of the ACK, but then select() times out on the next character -- OF COURSE: because at a lower level, it has already been read. We have to check the myread buffer, and then call select() only if it's empty. Making this change: . SEND with G-Kermit works (but very slowly). . SEND with lsz works but gets a lot of errors, eventually succeeds. Let's work our way back... With the same changes to the buffered pipe version: . SEND with G-Kermit/streaming works (fast). . SEND with lsz works too (fast), but we get gubbish at the end. . RECEIVE with Kermit fails because "/dev/tty is not a terminal device". . RECEIVE with rsz... lots of errors ("garbage count exceeded") but succeeded. But maybe now we're seeing pipe artifacts, so going back one more step to the version that gets its own pty and starts its own fork: . SEND with G-Kermit/Streaming works (fast) but select() times out at the end. Another breakthrough: Moved the write pieces to below the read pieces. This is what was preventing the buffer reset code from working -- with the writes done before the reads, we never catch up and can never reset the buffers. . SEND with G-Kermit/streaming works (fast) (but there's a pause at the end) . SEND with lsz works (fast) (but there's a pause at the end) . RECEIVE with rsz... lots of errors ("garbage count exceeded") and fails. . RECEIVE with Kermit -- nothing happens (it thinks it succeeded), then we reconnect, terminal sees S packet and goes into autodownload From the log it looks like ttpkt() fails in the lower Kermit. Switching this with the hacked G-Kermit... it gets "transmission error on reliable link". Tried again with real Kermit below, this time with "-l 0" and not streaming. This was actually working, but slowly, I don't see any NAKs in the packet log, but then select() timed out. 28 Jan 2007: Restored both the calls to pty_make_raw(): . SEND with C-Kermit streaming works, but slow (54Kcps) . Ditto, but with debugging off -- hangs forever. . Ditto, but using G-Kermit instead of C-Kermit -- also hangs forever. Backed off on calling pty_make_raw(). Same thing. Reduced size of net-to-pty buffer. Same thing. 15 Feb 2007... Decided to give up on this and publish it as is, in hopes that somebody with more experience with ptys can make it work, because I'm just going in circles. So today I just have to get the code into shape so people could choose among the three alternative routines. The second one, yttyptycmd(), is the one that uses openpty(), which is not portable, so it can be enabled only for Mac OS X, NetBSD, FreeBSD, and Linux, or by also defining HAVE_OPENPTY at compile time. Anyway, if you build Kermit in the normal way, you get the regular behavior -- ttruncmd() is used to execute external protocols. If you build it with -DXTTPTYCMD, you get the first version of ttptycmd(); with -DYTTPTYCMD the second, and with -DZTTPTYCMD the third. (Then some interruptions, then...) From Jeff, fix hostname comparison in X.509 certificate checking to work right in the case of names that contain no periods. dNSName_cmp(): cl_ssl.c, 21 Feb 2007. John Dunlap noticed some strange behavior when transferring files between home base and the EM-APEX oceanographic floats via satellite... long story, but every so often the transfer would get stuck for a long time, and it happened only when C-Kermit was sending a file and received two or more packets (Ack or Nak) back to back from the float. Years ago I added some lookahead code to ttinl() to clear the input buffer of any interpacket junk so that, in the windowing case, we wouldn't be tricked next time around into thinking a packet was waiting to be read when there wasn't. The code, which has been there for a while, was a bit fractured; luckily, it would be executed only when the debug log was active so it didn't have much effect. The problem was that if the SOP came immediately after the EOP, it could be missed because the loop read the next character before checking the current one. Fixed by rearranging the loop. Also I changed it so it would execute in all cases, not only when the debug log was active. Also, cleaned up a bunch of confusing #ifdefs and removed some chunks that had been commented out for years, decades maybe. ttinl(): ckutio.c, 21-22 Feb 2007. Added NOW keyword info to HELP DATE, plus a tip about how to convert to UTC; suggested by Arthur Marsh. ckuus2.c, 22 Feb 2007. When an FTP client sends NLST to the server and no matching files are found, the server is supposed to respond with an error message on the control channel and nothing on the data channel. However it seems that at least one server sends the error message back on the data channel, as if it were a filename ("/bin/ls: blah: No such file or directory"), and on the control channel there is no error indication ("226 ASCII Transfer complete"). At this point remote_files() has a listfile and, if a match pattern was given, it looks through list to see if any of the lines match the given filename, e.g. "blah". This makes FTP CHECK give false positives. The problem (diagnosed by Jeff) is that the match pattern was not given in this case, so it takes some random default action, resulting in the spurious success return. Fixed by using the user's string as the pattern. Not tested, however, since I don't have access to a server that behaves this way. ckcftp.c, 22 Feb 2007. If an external-protocol file transfer fails, don't print Kermit-specific hints. ckuus5.c, 22 Feb 2007. One more time with ttinl(). Got rid of the "csave" junk, which never could have worked (which is no doubt why it was in a debugging section). The problem was that saving the beginning of the next packet locally did not synchronize with the buffer clearing (ttflui()) done at a higher level, between calls to ttinl(). So now, the lookahead code, if it finds the beginning an as-yet unread packet, puts it back at the head of the input queue. This way, if the protocol engine clears the input buffer, it will get the whole packet, not just the part after the SOH. ckutio.c, 24 Feb 2007. From Steven M Schweda, Saint Paul, MN: adaptation of large file support to VMS (it was already possible to transfer large files in VMS C-Kermit but the file-transfer display and statistics were wrong). And a minimal adaptation of the FTP client to VMS -- no RMS, no special VMS file stuff, Stream_LF and binary files only, developed and tested only with UCX. SSL/TLS is supported. The source-code changes are minimal; most have nothing to do with VMS, but with header files, prototypes, and data types (e.g. ftp_port int rather than short, various signed/unsigned conflicts) to shut up compiler warnings. Some of these could be dangerous in terms of portability; I've marked them with /* SMS 2007/02/15 */. ckcfns.c, ckcnet.h, ck_ssl.h, ckuus3.c, ckuus4.c, ckvfio.c, ckcftp.c, ckvker.mms (which was rewritten to actually reflect the source module dependencies), ckvker.com (also heavily modified). ckvker.com (the "makefile" for VMS C-Kermit) now includes "F" and "I" option flags for the large File and Internal ftp features, plus better handling of Vax/Alpha/IA64 distinction. 26 Feb 2007. Changed NetBSD targets to include -DHAVE_OPENPTY and -lutil, so they can use openpty(). makefile, 26 Feb 2007. Built on Solaris without and with SSL OK. Built on NetBSD with Kerberos 5, OK. Built on Mac OS X 10.4, regular version, OK. Built on Mac OS X 10.4 with SSL and Kerberos 5, OK. On VMS 7.2-1/Alpha with MultiNet 4.4A-X... 'CC' 'CCOPT' KSP:ckuus3 %DCL-W-TKNOVF, command element is too long - shorten \CKUUS4.OBJ "'CC' 'CCOPT' KSP:ckuus4" "KSP:ckuus4.c KSP:ckcsym.h KSP:ckcdeb.h KSP:ckclib.h" "KSP:ckcasc.h KSP:ckcker.h KSP:ckcnet.h KSP:ckvioc.h" "KSP:ckctel.h KSP:ckuusr.h KSP:ckucmd.h KSP:ckuver.h" "KSP:ckcxla.h KSP:ckuxla.h KSP:ckcuni.h KSP:ckuath.h" The new rule for ckuus4.c was too long. I removed one file from the dependency list (ckcxla.h, which will probably never change again) and that made it OK. Built Nonet and Net versions OK, but this is without the new stuff. "make f" (large-file support) on VMS 7.2-1... 'CC' 'CCOPT' KSP:ckuus4 if (CKFSEEK(fp,(CK_OFF_T)j,SEEK_CUR) != 0) { ........................^ %CC-I-IMPLICITFUNC, In this statement, the identifier "fseeko" is implicitly declared as a function. Ditto for ftello and fseeko in various other places, and then fseeko and ftello come up up undefined at link time. The rule for ckcftp in "make i" (Internal FTP support) had the same problem. I removed ckcxla.h from its dependency list too, but "utime" comes up undeclared at compile time and undefined at link time. Verdict: neither one of the two new features can be used in VMS 7.2 or earlier, but the code still builds OK if you don't ask for them. VMS 8.3 on IA64... Can't build anything: %MMS-F-BADTARG, Specified target (WERMIT) does not exist in description file 27 Feb 2007: Changed CKVKER.COM to keep all its dependencies but use a shorter logical name (Steven M Schweda). The problem on VMS 8.3 is that MMS now supports case-sensitive file systems, and so it can't find anything. Workaround: bypass MMS (include "m" in P1). With this, "@ckvker.com ifm" builds OK on HP Testdrive, but I can't test the new features since outbound connections are not allowed there. As for fseeko(), ftello(), and utime(), they simply are not available prior to VMS 7.3. It would probably be a good idea to test for this in CKVKER.COM, but actually it is possible to install newer C's and CRTLs on older VMS versions, so don't stand in their way. 28 Feb 2007: With additional changes from SMS, and then some further adjustments, I was able to build the FTP version on VMS 7.2-1. First I tested it with GET of a binary file, but it transferred it in text mode. After a few more attempts with PUT and GET, it crashed with "floating/decimal divide by zero" in ckscreen, ckuusx.c line 27859. Of course, that's the listing line, not the source line, and I don't have a listing. To get a listing, I deleted CKUUSX.OBJ and then did: $ make i "" "" "/LIST" Surprisingly, it recompiled everything. Anyway, the divide by zero happened in a section of code where the divisor was not checked, but it was a section of code we should not have been executing at all, since the file-transfer display was fullscreen, and this was in the "brief" section. Anyway, I added the needed check. Again, it recompiles everything. Maybe there's no MMS on grumpy -- right, there isn't. ANYWAY... Try to GET a binary file like this: binary ---> TYPE I 200 Type set to I. get gkermit ---> TYPE A 200 Type set to A. ---> SIZE gkermit 550 gkermit: file too large for SIZE. GET gkermit (text) (-1 bytes)---> TYPE A Anyway... "get /binary gkermit" downloads it, seemingly correctly (the byte count is right). But "put /binary gkermit.;1" results in a 0-length GKERMIT file being sent. Here's the debug log: FTP PUT gnfile[DISK$MSA4:[C.FDC.NET]gkermit.;1]=1 ftp putfile flg[DISK$MSA4:[C.FDC.NET]gkermit.;1]=0 zltor fncnv[DISK$MSA4:[C.FDC.NET]gkermit.;1]=-1 FTP PUT nzltor[GKERMIT] zfnqfp 1[DISK$MSA4:[C.FDC.NET]gkermit.;1]=675 zfnqfp 2[DISK$MSA4:[C.FDC.NET]GKERMIT.;1]=31 zfnqfp 3[DISK$MSA4:[C.FDC.NET]GKERMIT.;1]=31 zrelnam result 2[gkermit.;1] ftp sendrequest restart[DISK$MSA4:[C.FDC.NET]gkermit.;1]=0 openi name[DISK$MSA4:[C.FDC.NET]gkermit.;1] openi sndsrc=-1 openi file number=2 zopeni[DISK$MSA4:[C.FDC.NET]gkermit.;1]=2 zopeni fp=0 chkfn=2 chkfn return=0 zopeni fixed file format - using blk I/O zopeni binary flag at open=1 zopeni ifile_bmode=1 zopeni binary=1 zopeni RMS operations completed ok openi zopeni 1[DISK$MSA4:[C.FDC.NET]gkermit.;1]=1 ftpcmd cmd[PASV] FTP SENT [PASV] FTP RCVD [227 Entering Passive Mode (166,84,1,2,233,216)] initconn connect ok FTP SENT [STOR GKERMIT] FTP RCVD [150 Opening BINARY mode data connection for 'GKERMIT'.] doftpsend2 ftpcode[STOR]=150 Here is where the file is supposed to be read and sent but there is nothing in the log between the "doftpsend2 ftpcode" line and the following line. rftimer status=1 gftimer status 1=1 gftimer status 2=1409025 gftimer status 3=1409025 gftimer s[0.000000] zclose n=2 chkfn=2 chkfn return=1 zclose ZIFILE RMS operations completed ok ftp getreply lcs=0 ftp getreply rcs=-1 ftp getreply fc=0 FTP RCVD [226 Transfer complete.] ftp getreply[226 Transfer complete.]=2 doftpsend2 ok=0 Everything is OK up until we go to send the file, then it behaves as if we got EOF immediately and so closes the data connection, and reports success; an empty copy of the file is left on the far end. Starting over with a text file.... PUT LOGIN.COM gets another divide by zero. But it happened in the code I just fixed, which is impossible. Swell. I recompiled everything and this time the upload worked, and downloading it again worked too. But a binary file still can't be uploaded. Trying to upload a text file after doing this seems to succeed (reports the right number of bytes sent) but nothing appears on the far side. SUMMARY: To download a text file: GET /ASCII blah.txt (/ASCII is optional) To download a binary file: GET /BINARY blah.bin (/BINARY is required) To upload a text file: PUT blah.txt (/ASCII switch not needed) To upload a binary file: PUT /BINARY blah.bin (doesn't work) Problems: . Why doesn't BINARY "stick"? . Why don't binary uploads work? The culprit seems to be the VMS version of zxin(). In the FTP module, zxin() is called only when sending binary files. In VMS, zxin() is just a front end for C-Library fread(). It probably needs to do just do zminchar() in a loop, like binary mode does, but calling zzout instead of xxout. Or something like that. FINISH THIS TOMORROW (debug on grumpy). 2 Mar 2007: New logs from John Dunlap. ema-1636-log-0175.dbg: C-Kermit uploads a short file. It receives an Ack for the Z packet it just sent, tailgated by the beginning of a Nak for the next packet. When the second SOH is encountered, it is put back in the myread queue. Then the protocol engine, to which we return the Ack, says, "I have the packet I wanted so I'm clearing the buffer", and away go the first two bytes of the Nak from the myread buffer. Then, having just received the Ack of our Z packet, we send our B, and go to read the reply. in_chk finds 0 in the myread buffer (which we just cleared) and 6 waiting to be read from the comms channel, which it does, obtaining the remaining 6 bytes of the Nak, which it properly discards. (The reason this is proper is that, having already received the Ack for the last packet it sent, no Ack or Nak that arrives subsequently -- in the non-windowing case -- could possibly affect what it does next.) Since it hasn't yet found a good packet, it keeps reading, and now it finds the Ack to the B, as soon as it showed up. This is how it's supposed to work. No time was lost because of anything that C-Kermit did. ema-1636-log-0174.dbg: C-Kermit uploads a short file. It sends Data packet #3 and receives the Ack followed immediately by the first 3 bytes of a Nak for packet 4. When it gets to the SOH of the second packet, it pushes it back in the queue. Again, input() flushes the input buffer (myread queue and device buffer). C-Kermit detects EOF on the file it is sending, and sends the Z packet. Then it reads the remaining bytes of the Nak, which it discards, and then it finds the Ack for Z which comes in 23 seconds later, sends the B, gets a Nak for the B, sends the B again, gets the Ack for the B 4 seconds later, and done. Again, it's working right and losing no time. The question remains: what would happen if the protocol engine did not clear the buffer? Would ttinl() retrieve all packets in sequence even when they come back to back? To test this, I had C-Kermit send a file using 30 window slots and observed the stream of Acks in the reverse direction: HEXDUMP: mygetbuf read (16 bytes) 01 25 23 59 2f 52 39 0d | 01 25 24 59 2b 26 31 0d .%#Y/R9. .%$Y+&1. ttinl lookahead my_count=9 ttinl lookahead removed=^M ttinl lookahead pushback SOP=^A HEXDUMP: ttinl got (7 bytes) 01 25 23 59 2f 52 39 | .%#Y/R9 RECEIVE BUFFERS: buffer inuse address length data type seq flag retries 0 1 29212 9667 0 Y 3 0 [%#Y] ... in_chk my_count=8 ... ttinl lookahead my_count=1 ttinl lookahead removed=^M HEXDUMP: ttinl got (7 bytes) 01 25 24 59 2b 26 31 | .%$Y+&1 RECEIVE BUFFERS: buffer inuse address length data type seq flag retries 0 1 29212 9667 0 Y 4 0 [%$Y] Here we can see that the pushed-back SOH was properly retrieved next time around, and the tailgating Ack was not lost. This scenario repeats itself 212 times in the log, and there are no screwups. Back to VMS FTP... The problem with sending binary files is that zxin() uses C-Library fopen()/fread() instead of RMS, so it can't access the input file, which was opened by zopeni(), which is totally RMS-ified in VMS C-Kermit. For VMS only, I replaced the zxin() loop by a zminchar() loop like the one used in text mode, except without the character set or record-format conversion. Tested by PUT /BINARY of some binary files, which worked fine. ckcftp.c, 2 Mar 2007. Next problem... VMS C-Kermit ftp client sending binary files in text mode. Variation 1: We just send the file. zopeni() is supposed to detect that it's a binary file and automatically set the mode. And it does: zopeni fixed file format - using blk I/O zopeni binary flag at open=0 zopeni ifile_bmode=1 zopeni binary=0 zopeni autoswitch from TEXT to BINARY zopeni RMS operations completed ok but then in gnfile(): if (!server || (server && ((whatru & WMI_FLAG) == 0))) binary = gnf_binary; /* Restore prevailing transfer mode */ Well, since VMS sets text/binary mode automatically when sending files, this code can (and should) be skipped in VMS. gnfile(): ckcfns.c, 2 Mar 2007. Variation 2: BINARY or SET FILE TYPE BINARY doesn't force binary mode. But SET FTP TYPE BINARY does. But BINARY does indeed call doftptyp() so what's the problem? We do indeed set ftp_typ to 1 but it gets reset somewhere before we call zopeni(). But then zopeni() puts it back to 1. Tracing through a transfer, it looks like all of this works right, it's only that the file transfer display says TEXT when the transfer is really in binary mode. This is because screen() is called before openi(). I wonder if we can call scrft() from the ftp module... No, that would be too easy. OK, sendrequest calls openi() and sets the file mode; putfile() calls screen(SCR_FN), which prints the transfer mode. But putfile calls sendrequest() after it puts up the screen that says the file type. So it looks like sendrequest() has to call screen(SCR_FN) again if it changes the file type. OK, that did it. ckuusx.c, ckcftp.c, 2 Mar 2007. The BINARY and TEXT (ASCII) commands do not inhibit automatic type switching in VMS. They don't in Unix either. They never have. Should they? I think so, otherwise what good are they? Plus we want the Kermit FTP client to behave like the others. I added code for this but it doesn't work, due to the layers and layers of text/binary detection and switching and if-this-but-then-if-that... Anyway, no harm done. The normal rule is: when you PUT a file, Kermit figures out on a per-file basis whether to use text or binary mode unless you include a /TEXT (/ASCII) or /BINARY switch in the PUT (or MPUT) command. ckuus[r3].c, ckcftp.c, 2 Mar 2007. Wed Mar 7 16:21:13 2007 WROTE SHORT TEST PROGRAM for ttruncmd (the openpty version) on Mac OS X. On dulce: ~/kermit/ttpty.c / ttpty.sh. It starts the external protocol in the lower fork. The command to run is a command-line argument. Sending and receiving files with Kermit works OK. But again, the standalone program totally fails when I use sz or lsz as the external protocol. So it looks like we can rule out any environmental effects of running the code inside C-Kermit. Mon Mar 12 16:52:20 2007: Put some effort into making ttpty.c more useful; added a debug log. Found that for some reason, at least on Mac OS X, select() always timed out at the the end. I added a SIGCHLD alarm and that seems to handle the fork exit condition very nicely. Now we can send (say) a 3MB file at good speed on Ethernet (1Mcps) considering the debugging, etc, and it terminates instantly. But when sending a file into ttptycmd (with "gkermit -r"), things go wrong at the end -- the Z packet is never acknowledged. This is reproducible. Maybe this is a good lead.... The log shows that select() timed out, even though the gkermit fork had not yet exited (or finished). It looks like gkermit sent the Ack, ttpty.c read it from the pty and sent it out the net: 0003: read pty=8 <-- read Ack from pty 0003: loop top have_pty=1 0003: loop top have_net=1 0003: FD_SET pty_in 0003: FD_SET ttyfd in 0003: FD_SET ttyfd out=8 0003: nfds=5 0003: select=1 0003: FD_ISSET ttyfd out 0003: write net=8 <-- send ack to net 0003: loop top have_pty=1 0003: loop top have_net=1 0003: FD_SET pty_in 0003: FD_SET ttyfd in 0003: nfds=5 0009: select=0 0009: select timeout - have_pty=1 But Ack never arrived. This is a streaming transfer. But nope, streaming is not the problem. If I disable streaming ("gkermit -Sr"), we hang in in the middle of sending the data. If I use small packets, we don't hang: 1000 is OK, 2000 is not. In fact, the cutoff is 1024. OK, TBC... Wed 14 Mar 2007: Receiving a file thru ttpty "gkermit -e 1200 -Srd" produces a debug log that shows that gkermit gets a lot of EAGAIN errors when it tries to read from its stdin. In fact, it takes 6 tries (read() calls) to read the S packet (27 bytes). Then when the first data packet arrives (1200 bytes), read() never returns even one single byte. The timeout interval is 15 seconds and it times out repeatedly. Added a primitive hex dump to the ttpty debug log for each read/write (showing only the first 24 characters and the last character, so it fits on one line). Tried uploading a file. The S, F, and A packets (short) are received and Ack'd OK, but then ttpty select() times out, never receiving even one byte from the D packet. Clearly, when the pty driver receives a burst of > 1K bytes, stops working. As before, if I limit the packets to < 1K, it works fine. Can I send an 8-bit binary file? Nope. ttpty reads the binary data just fine from the net and writes it exactly as it was received to the pty, but the first time we write an 8-bit byte, we never hear back from the PTY again. But the log shows that when the initial 7-bit packets from the pty, it looks like the PTY is not in rawmode, because these packets end with ^J rather than ^M. Calling pty_make_raw() on the masterfd and slavefd explicitly, however, doesn't change anything. It doesn't matter if I do this in the lower fork or the upper fork. So maybe it's the actual semantics of pty_make_raw() that are wrong. Thu 15 Mar 2007: Went thru all the terminal mode flags in Mac OS X; didn't help. Changed hex dump to show whole packet. Put hex dump routine in a private copy of G-Kermit. Tried to transfer an 8-bit file, logging both ttpty and gkermit. Compared what ttpty received on stdin with what it sent to the pty (same) and what was received by G-Kermit (same). Then I realized that my little test program was not putting its controlling terminal into raw mode; when I did that, I could upload binary files (streaming, 2MB/sec). And with Zmodem too (with rz; lrz doesn't work for some reason). Looking back at the original in ckutio.c, I see that ttptycmd() never called ttpkt(). Maybe that was the trouble all along. (Yup, but maybe not the whole trouble.) Moving back to C-Kermit and the original ttptycmd() routine, adding the call to ttpkt(), and stripping out a lot of cruft, and moving the pty_make_raw() code to ckupty.c, Kermit uploads and downloads (streaming) work fine in Solaris. Zmodem sends a file, but then the transfer hangs at the very end, as if the signoff protocol were lost. This happens on Solaris. If I move back to Mac OS X, everything works just fine. Then, making a Kerberized connection from the Mac to NetBSD, I can send files from the Mac with both Zmodem and Kermit. Receiving... Kermit OK. Zmodem... Nope. "rz: Persistent CRC or other ERROR" (and created a 265MB debug.log!) Fri 16 Mar 2007: ttptycmd() was for sending files with Zmodem across encrypted connections. But it occurred to me that it's necessary for clear-text connections too; e.g. Telnet, where 0xff has to be doubled. Of course Zmodem doesn't do that itself, so there's no way Zmodem external protocol could work when executed over a Telnet connection, and in fact it doesn't. I wonder why I ever thought it did. Wed 21 Mar 2007: Back to where we left off a week ago. Trying C-Kermit's ttptycmd() on the Mac again, in remote mode: . G-Kermit send txt (kst): OK 832Kcps . G-Kermit recv txt (kr): OK 425Kcps . G-Kermit send bin (ksb): OK 1000Kcps . G-Kermit recv bin (kr): OK 188Kcps And Zmodem: . sz txt (zst): OK 563Kcps . sz bin (zsb): OK 714Kcps . rz txt (zr): OK 863Kcps . rz bin (zr): OK 198Kcps So in remote mode, everything works. Now let's try a clear-text Telnet connection... . G-Kermit send txt (kst): OK 841Kcps . G-Kermit recv txt (krt): OK 391Kcps . G-Kermit send bin (ksb): OK 811Kcps . G-Kermit recv bin (krb): OK 171Kcps And Zmodem over the same clear-text telnet connection: . sz txt (zst): OK 91Kcps (*) Kermit is sending sz messages like "sz 3.73 1-30-03 finished." to the host, which tries to execute them, after the transfer is finished. Of course "sz" is a command, but: sz: cannot open 3.73: No such file or directory sz: cannot open 1-30-03: No such file or directory sz: cannot open finished.: No such file or directory Did I lose that code that dis-redirects stderr when I went back to using the pty code from the ckupty module? No, it's there and it's being executed. Apparently the copy of sz I have is writing its "finished" message to stdout because "sz blah 2> /dev/null" does not suppress it. Starting again with lsz instead of sz: . sz txt (lzst): OK 413Kcps . sz bin (lzsb): OK FAILED (*) . rz txt (lzrt): OK . rz bin (lzrb): OK (*) Sigh. Using lsz, we get "garbage count exceeded" errors and eventual failure. But using regular sz, we get the extraneous message that starts sz on the far tend, and the resulting getty babble. But even without changing the code, it will work one minute, and then fail consistently the next. For example, I was able to send files with sz successfully over and over, but with the getty babble at the end. Then, after trying lsz and then going back to sz, every attempt at sending a file quits with "Got ZCAN". The difference has to be that Kermit always does at least some minimal encoding of C0/C1 control characters such NUL and DEL and IAC, and I doubt that Zmodem does. http://zssh.sourceforge.net/ says: If file transfer is initiated but never completes (ie a line like : Bytes Sent: 0/ 513 BPS:0 ETA 00:00 Retry 0: Got ZCAN can be seen, but transfer never completes), chances are the pty/tty on one of the systems are not 8-bit clean. (Linux is 8-bit clean, NetBSD is not). Using the -e (escape) option of rz should solve this problem. It doesn't, at least not with lrz. And yes, the receiving end happens to be NetBSD. But it looks like the zssh people have been down this road too. But with rz and sz, it worked. Once. Twice. Three times. But of course, with the getty babble at the end. This can be taken care of by doing: rz -eq ; cat > foo which puts "sz 3.73 1-30-03 finished" and any other messages in foo (but you have to type ^D to finish the cat). Using this method I was also able to send an 8K binary file that contained a test pattern of all 256 possible byte values. Then I tried a 3MB binary executable. All OK. So here we go again: . sz txt (zst): OK . sz bin (zsb): OK . rz txt (zrt): . rz bin (zrb): Downloading fails about halfway through a fairly large file. I tried an even bigger file, guaranteed to be 100% ASCII; same thing -- halfway through: "rz: Persistent CRC or other ERROR". But it worked with a smaller version of the same file (82K versus 2MB). Tried again with the bigger version, it failed in exactly the same way at exactly the same spot: byte number 1048320. But this is just ASCII text so it can't be a transparency problem. Substituting another plain ASCII file of the same size but totally different contents, it doesn't fail (2.36MB). Back to the previous file, it fails again, but in a different spot (832960). So it's not totally deterministic. To round things out, I tried downloading the binary test-pattern file; it's only 8K. This failed. -4, --try-4k go up to 4K blocksize -B, --bufsize N buffer N bytes (N==auto: buffer whole file) -e, --escape escape all control characters (Z) -E, --rename force receiver to rename files it already has -L, --packetlen N limit subpacket length to N bytes (Z) -l, --framelen N limit frame length to N bytes (l>=L) (Z) Tried again with "sz -L 256 -B 256 -4aeq". Doesn't change anything. NOTE: Mac OS X rz 3.73 1-30-03 does not support -e. NetBSD rz 0.12.20 does support -e. Thu 22 Mar 2007: It occurs to me that ttpkt() might still be a problem; maybe it's the network connection and not the pty that is not transparent enough. To test this theory I did "stty raw ; stty -a" and then copied all of the flag values into ttpkt in the BSD44ORPOSIX section: . rz txt (zrt): OK (2.36MB file, worked 2 out of 3 times) . rz bin (zrb): "rz: Persistent CRC or other ERROR" A little more fiddling with the flags and I got the 8K binary test pattern to SEEM to download OK (in the sense that rz gave a 0 return code) but the file itself was truncated, always at 224. If I changed the test pattern file to not include any bytes with value 224 (0xe0) or 255 (0xff), the download worked. So we have a transparency problem somewhere. The debug log shows that all byte values are being received from the network correctly so the problem has to occur when we try to feed them to the pty. But no amount of twiddling with the termios flags seems to let these characters pass through. Of course, since they are not in the C0 or C1 control range, "sz -e" doesn't quote them (which it does by prefixing with Ctrl-X and then adding 0x40 to the byte value so (e.g.) NUL becomes ^X@. Note that 255 does not cause problems because it coincides with the IAC character; the remote Telnet server doubles outbound IACs, and Kermit's ttinc() undoubles them automatically (as the log shows). Trying to send a different file (a C-Kermit binary) shows that 255 is the real killer; the file is truncated where the first one appears (at about 6K), even though some 224's precede it. Going back to the remote-mode test, I see the same thing happens with the binary test-pattern file, if I send it from K95 direct to rz-under-C-Kermit-in-remote-mode. So it has nothing to do with C-Kermit having a network connection. Yet if I send the same file direct from K95 to rz, it goes OK and the result is not truncated, so it's not Zmodem either. The data arrives to C-Kermit intact, so the failure is definitely in writing it to the rz process through the slave and master ptys. BUT if I send the same file from K95 to rz-under-ttpty, that works. What's the difference? Suppose I just transplant ttpty literally into C-Kermit... It makes no difference. When receiving the test-pattern, it truncates it in exactly the same place. Well, all this is on Mac OS X. What if I move it to a different platform? OK, building on Solaris and following the exact same procedure, ttptycmd() doesn't even use the network connection. I think that's because rzsz on Solaris is hardwired to use the controlling terminal and can't be redirected, even in a pty? Moved to NetBSD. . sz txt (zst): Failed ("Got ZCAN") . sz bin (zsb): . rz txt (zrt): OK . rz bin (zrb): Well, this is a big mess. Sending doesn't work (or sometimes it does but reports that it didn't). Receiving... well, actually it's the same thing; the file is completely transferred but then the final protocol handshake is lost. The local C-Kermit returns to its prompt, but rz is still running: Retry 0: Got TIMEOUT Retry 0: TIMEOUT Retry 0: Got TIMEOUT Retry 0: TIMEOUT Retry 0: Got TIMEOUT Retry 0: TIMEOUT Retry 0: Got TIMEOUT Retry 0: TIMEOUT Retry 0: Got TIMEOUT Retry 0: TIMEOUT Retry 0: Got TIMEOUT I don't see how that is even possible. Even after I exit from Kermit the messages keep coming, even though ps doesn't show the rz process anywhere. Looking at the code, I see a place where end_pty() was still commented out from the ttpty.c episode, I uncommented it. But still: . sz txt (zst): Fails ("Got ZCAN") . sz bin (zsb): Fails instantly (but with no diagnostic) . rz txt (zrt): OK . rz bin (zrb): Fails with tons of "Bad CRC", "Garbage Count exceeded" Conclusion for the day: I think this is hopeless. Even if I can get it to work somewhere, the results depend on the exact Zmodem software, how it uses stdin/out vs stderr versus getting its own nonredirectable file descriptor, versus the Zmodem version on the other end and which options are available on each, versus the pty and select() quirks on each platform, and on and on. It will be so hard to explain and to set up that nobody would ever use it. It would be better to just implement Zmodem internally. Fri 23 Mar 2007: Went back to the small test program, ttpty.c. Tried setting both the master and the slave pty to rawmode, even though I have never seen any other software that did this. I had it receive the binary test pattern file; it worked. I made a bigger test-pattern file, 3MB, containing single, double, and triple copies of each byte in byte order and in random order, this one was accepted too. So it would seem that the ckupty.c module is something to avoid after all. It's full of stuff I don't understand and probably should not undo. So changing C-Kermit's ttptycmd() to manage its own pty again, using openpty() (which is not portable), I got it all to work in remote mode: Kermit text/binary up/down and Zmodem text/binary up/down. But in local mode on the client side of a Telnet connection... zst: OK, but we still get the getty babble at the end that starts sz. zsb: OK, ditto. This is with the 3MB test-pattern file. zrt: Not OK -- "Persistent CRC or other ERROR" zrb: Not OK -- got the cutoff at 224 again "Persistent CRC or other ERROR" It's close. But actually this was still with USE_CKUPTY_C defined. When I undefined it, it was back to being totally broken. Start over. (Check the new cfmakeraw() code.) Tue 27 Mar 2007: Starting over. Back to ttpty.c. Let's verify, VERY CAREFULLY, that it really does work, using the most stressful of the four tests: sending the big (3.2768MB) binary test pattern from K95 into rz through ttpty, logging everything. ttpty definitely receives the big file smoothly with no errors or hiccups when I have it set to use the master side of the pty for i/o. The application program (Zmodem in this case) runs on the slave, and the network and/or control program communicates with the master. This implies that Zmodem controls the terminal modes of the slave, and ttpty should be concerned with those of the master. Doing it this way in ttpty confirms this. Fine. But if I tell ttpty to SEND a file with sz, nothing happens. Ditto with lsz. Select times out waiting for input from the pty. But if I manually tell K95 to RECEIVE /PROTOCOL:ZMODEM it works OK. Somehow sz's initial B000000 string is being swallowed somewhere, and it's waiting for a reply from the receiver. sigh... But "ttpty gkermit -s filename" works fine. What's the difference? It has nothing to do with stdout vs stderr; sz is not writing to stderr at all. Is it some timing thing between the forks? Aha. It's that I change the modes of the pty master in one fork while sz is already starting in the other fork. OK, good, now for the first time we have Kermit and Zmodem both able to upload and download a large worst-case binary test-pattern file... in remote mode. Now taking today's lessons and fitting them back into C-Kermit so I can try it local mode... Using G-Kermit as the external protocol, first in remote mode... All good: text/binary up/down. The "halting problem" is solved by SIGCHLD, which catches fork termination instantly and lets ttptycmd() know there is no more pty. Zmodem: zst: OK zsb: OK zrt: OK zrb: OK That's a first. Next, repeat in local mode, in which C-Kermit is the client and has made a Telnet connection to another host over a secure (Kerberos V) connection: kst: OK zst: ... ksb: OK krt: OK krb: OK It seems we can never end a day on a high note. Somehow I seem to have broken regular internal Kermit protocol transfers over encrypted connections -- the en/decryption engine loses sync. But they still work OK over a clear-text Telnet connection. Today's code in ~/80/dulce.tar (27 Mar 2007). Added makefile target solaris10g+openssl. Gathered all the standard CFLAGS for Solaris into cdcdeb.h so they don't have to be included in every single makefile target for Solaris. On local Solaris 10 host OpenSSL is in /opt/openssl-0.9.8e/. Tried the new makefile target, works OK. Also made solaris10+openssl for Sun CC, but couldn't test it because I can't find any Solaris 10 host that has Sun CC. Built with gcc at another site that has OpenSSL 0.9.8f-dev, all OK. ckcdeb.h, makefile, 24 Jun 2007. It occurs to me that Kermit transfers on secure connections might have been broken by the changes I made back in February to ttinl() for John Dunlap. Here, for the first time, we invoke myunrd() to push a byte back into the input queue, and there is also some funny business with "csave", which changed, and which an old comment notes that it has to be treated specially when encrypting. So it could be that the broken Kermit transfer has nothing to do with the work on external protocols, and that putting back the previous ttinl() will fix it. But now I can't seem to make a Kerberized connection from Panix to Panix, even though I can make one from Columbia to Panix. This means I have to build a Kerberized binary from the current source code on either Solaris or Mac OS X. Trying Solaris first... [~/solaris9k5/mk5.sh] This didn't work the first time due to undefined krb5_init_ets, which is referenced if MIT_CURRENT is not defined (it should be for Kerberos 5 1.05 and later and we have 1.42 here), tried again with -DMIT_CURRENT=1... Nope, that one totally blew up in ck_crp.c. Later, Jeff says krb5_init_ets is a no-op in Kerberos 1.4.x and later, so I added an #ifdef (NO_KRB5_INIT_ETS) for skipping it; now it builds and runs OK. ckuath.c, makefile, 9 Jul 2007. Meanwhile, using C-Kermit on Mac OS X, which makes the Kerberized connection just fine, but still has the problem transferring files over it. Packet log shows: s-00-01-^A9 Sz/ @-#Y3~Z! z0___F"U1@A^M r-00-01-^A9 Y~/ @-#Y3~^>J)0___J"U1@I s-01-01-^A(!Fx.x)(V^M r-xx-08- S-01-08-^A(!Fx.x)(V^M r-xx-08- S-01-08-^A(!Fx.x)(V^M r-xx-16- Note that S packet is sent, received, and Ack'd OK. The F packet is sent but is never Ack'd. Tried this several times and noticed that it's just receiving that is screwed up, not sending. After ^C'ing out of the transfer, I can still type commands, and they are executed on the far end, but the results coming back are gibberish. Mon Jul 9 16:08:22 2007 (come back to this later... substitute Dev.27 ttinl for current one and see if the problem goes away, and if so, conditionalize the new code for clear-text connections). Built C-Kermit with Kerberos 5 on Solaris with a version of ckutio.c that uses the old ttinl() and transferred a file OK over a Kerberized connection. So now it's just a matter of reconciling the old and new ttinl. The easiest way to do this is to have new ttinl() chain to old ttinl() if the connection is encrypted, which is what I did and it works fine. At some point the two versions of ttinl() should be reconciled. ckutio.c, 12 Aug 2007. There was a function, islink(), used in only one place (ckuus6.c) that had the same name as a commonly used scalar variable, and it was missing a prototype. Changed its name to isalink() and added the prototype (Unix only), ckuus6.c, ckufio.c, ckcdeb.h. 12 Aug 2007. Revisiting the ASCII and BINARY top-level commands, which are supposed to be like in other FTP clients, but don't seem to have any effect. I added a new routine to the FTP module, doftpglobaltype(), that sets the global, sticky, permanent transfer mode (ASCII or BINARY) (TENEX could be added to if anybody asks). These commands (now that they work) are different from SET FTP TYPE { ASCII, BINARY }, which set the *default* transfer mode when automatic switching fails for a given file. ckuusr.c, ckcftp.c, 12 Aug 2007. (notify: Matt ) Even though the code hasn't changed, suddenly we're getting: "ckuusx.c", line 5682: warning: implicit function declaration: tgetent "ckuusx.c", line 6183: warning: implicit function declaration: tgetstr "ckuusx.c", line 6262: warning: implicit function declaration: tputs "ckuusx.c", line 6266: warning: implicit function declaration: tgoto in ckuusx.c on Solaris 9. is still in /usr/include, dated 2002. A quick search shows the missing functions are hiding in , which until now was included only in Linux. Added a USE_TERM_H clause. No, that doesn't help, the prototypes are not selected at compile time; there are #ifdefs in that file that skip over these prototypes. I had to put them in the code under #ifdef BUG999..#endif (I could have used a longer name like #ifdef ADD_PROTOTYPES_FOR_CURSES_FUNCTIONS, but that would not be portable). ckuusx.c, 12 Aug 2007. Also: "ckuusx.c", line 9232: warning: implicit function declaration: creat This is called in the IKSD database code, used for getting a lockfile. creat() is a Unixism in code that is supposed to be portable. But IKSD only runs on Unix and Windows, so I assume the Windows C library has a creat() function. Anyway, suddenly the Solaris header files seem to have blocked whatever path previously existed to the creat() prototype (which is in ), so I added an #include in the appropriate spot. ckuusx.c, 12 Aug 2007. Kermit functions for converting the number base -- \fradix(), \fhexify(), \unfhexify() -- did not work with big numbers; ckradix() was missed in the CK_OFF_T conversion. Fixed in ckclib.c, 12 Aug 2007. Updated the help text for ASCII, BINARY, and SET FTP TYPE to clarify the semantics. ckuus2.c, ckcftp.c, 12 Aug 2007. Error messages were printed upon failure to open any of the four log file, even with SET QUIET ON. Fixed in ckuus4.c, 12 Aug 2007. Built OK on NetBSD 1.3_RC3. Tried to build secure version but the libraries had disappeared. 13 Aug 2007. Built OK on Mac OS X 10.4.9. Tried the secure version, macosx10.4+krb5+ssl. Here we get the usual pile of "pointer targets in passing argument 1 of (function name) differ in signedness", regarding security functions, but it built OK. 13 Aug 2007. Reconciling the two ttinl's... On encrypted connections myread() returns encrypted bytes; ttinl() has to decrypt them; it wasn't doing this in the lookahead section so I fixed it. The new code works on both encrypted and clear-text connections. I removed the chaining to oldttinl(), and oldttinl() itself. ckutio.c, 13 Aug 2007. (Wouldn't it make more sense and be more efficient and less confusing for myfillbuf() to do the decrypting?) When C-Kermit uses Zmodem as an external protocol, it doesn't seem to scan files before sending them to set text or binary mode appropriately. It's that external protocols bypass Kermit's whole "get next file" mechanism; the (possibly wild) filespec is simply passed to the external protocol program. Changing this would be a very big deal. But if only one file is being sent (the filespec is not wild) it's easy enough to check. I added this to the external protocols section of the protocol module. It can be overridden in any of the regular ways (/TEXT or /BINARY switch on SEND command, SET PATTERNS OFF, SET TRANSFER MODE MANUAL, etc). ckcpro.w, 13 Aug 2007. [FTP SEND /RECURSIVE] Peter Crowley reported a problem with FTP recursive uploads getting the directory tree wrong when the previous pathname was a left substring of the new pathname (e.g. foo/bar/ and foo/bar2/). The logic did not handle this case and created the bar2 directory as a subdirectory of bar, rather than as a parallel directory. Fixed in syncdir() and tested with various edge cases. ckcftp.c 14 Aug 2007. notify Added CD messages to FTP BRIEF display to track the ups and downs of recursive uploads. ckcftp.c, 14 Aug 2007. The OUTPUT command gave a misleading error message ("Connection to xxx not open") when used on a serial port that was, indeed, open but was not presenting the Carrier signal, when CARRIER-WATCH was not OFF. Added a new message for this, and some others. ckuus5.c, 14 Aug 2007. Sending from the command line, e.g. kermit -s foo, did not give an informative error message if the file could not be found or opened. Fixed in ckuusy.c, 14 Aug 2007. OK, back to ttptycmd.... It seems that back on March 27th, I got everything working but I thought that there was still something wrong with it because an unrelated problem so I put it aside. The version of ttpty.c from that date worked OK, and it looks like I updated ckutio.c from it, but that version of ckutio.c was put aside. Since then I have been working on the ckutio.c version that was NOT put aside and so now I have to reconcile the two: ~/80/ttypty/20070327/ckutio.c ~/80/ckutio.c As a first cut I did this simply by replacing the contents of the #ifdef CK_REDIR section of the latter with that of the former. Of course in Solaris this comes up with openpty() implicitly declared at compile time and unresolved at link time. So the first task is to get HAVE_OPENPTY defined for platforms that have it and have the others use the ttruncmd(). For starters I put an #ifdef block in ckcdeb.h that defines HAVE_OPENPTY for Linux, FreeBSD, NetBSD, OpenBSD, and Mac OS X. Ones that don't have openpty() include AIX, HP-UX, and Solaris. Others like SCO I don't know but I doubt it. The real solution is to get the ckupty.c module to work but one thing at a time... This version is supposed work with secure builds on the openpty() platforms, and on the others like Solaris, if an external protocol is attempted on a secure (encrypted) connection, an error message is printed and the command fails. ckutio.c, 14 Aug 2007. How to test? Apparently I did all my testing on Panix before, and that's where all my Zmodem builds are, but now when I build a Kerberized version (which works if I do it on the right pool host), it won't make a local connection, and there is no other place I can connect to that has a Kerberized Telnet server. I can, however, connect to Panix from here, using the same code, but on Mac OS X... Slight detour: Got access to AIX again (5.3.0.0). Picky compiler, some things needed fixing.... Also it says "1506-507 (W) No licenses available. Contact your program supplier to add additional users. Compilation will proceed shortly" and of course it goes kind of slow. For some reason, I can't do streaming transfers into AIX over a local network (to its SSH server), but windowed transfers are OK. Anyway, noting that we've been using the same basic makefile target since AIX 4.2, changing nothing but the version herald, I made a new target, simply "aix", that picks up the AIX version automatically and sets the herald from it. Ditto for aix+openssl, but on this host requires setting SSLINC and SSLLIB to /opt/ssl/include and /opt/ssl/lib. Also the make program here was extremely sensitive to spacing so I had to make some minor edits to get the link step to work for the SSL version. ckuusy.c, makefile, 14-15 Aug 2007. Got rid of the special Panix secure NetBSD target, replaced it with a regular one, which is invoked in the normal way by defining K5INC and K5LIB to point to to where the stuff is hidden. Cleaned up and modernized the comments in the makefile a bit. makefile 15 Aug 2007. Changed some data types and added some casts to ckctel.c to do away with tons of "pointer targets in passing argument 1 of 'xxx' differ in signedness" warnings. 15 Aug 2007. Set up Mac OS X as the testbed for ttptycmd(), with Panix as the remote partner over a Kerberos 5 connection. The first test is to send a 300K text file with gkermit as the external protocol. It worked fine, and the debug log showed all the right components were active (namely encryption and ttptycmd) [kermit/zmodem send/receive text/binary]: Kermit Zmodem kst OK zst OK ksb OK zsb OK krt OK zrt OK krb OK zrb Failed "rz: Persistent CRC or other ERROR" We've seen this before. The problem is 0xff, Telnet IAC, as I proved to myself by constructing a 3MB file that contained every byte but 0xff in every mixture and order and transferring it successfully over the same connection. Presumably the Telnet server is doubling IACs, whereas of course rz is not undoubling, thus the CRC error. This is progress. 15 Aug 2007. Log shows that indeed every IAC in the source file arrives doubled. Adding code to remove the first IAC of every adjacent pair, a small test file with different-length runs of IACs transfers OK. The 3MB all.bin file does not. Starting over... I can receive a big text file with Zmodem OK. The 3.2MB binary test pattern that contains no IACs failed after 1.8MB, but the part that it transferred was OK. A second try, almost the whole thing arrived, it stopped just 584 bytes short of the end. Could be that file size is a separate problem. Making a new copy exactly 1MB long... Well, that's interesting, this one too stopped just short of the end. And again, the same thing. When connecting back to the host, the last Zmodem packet can be seen on the screen; it's as if the local Zmodem exited before reading the last packet... But OK, if I change the options on the remote sz sender to use small blocks, etc, then it works. Now, changing from the 1MB no-IAC-binary test pattern, to the 1MB all-values test pattern, we fail after 81K. But the part that was transferred is correct. Second try, same thing, but 57K. Third: 40K. Each time, upon connecting back, the session is completely dead. IF I HAVE TO undouble IACs for incoming files, don't I have to double them going out? To send a block to net we just call ttol(), but ttol() doesn't do any doubling (because Kermit protocol always quotes 0xff). To see what happens, I changed the ttol() call to ttoc() in a loop that doubles IACs. I tested this by sending the full 3.2MB test pattern, which worked fine. For receiving, it's slow but it works OK with files that don't contain IACs (my concern was that IACs might appear in outbound files or in Zmodem protocol messages). It receives the 1MB no-IAC test pattern, so there are no problems with protocol or timing. But the full test pattern always gets cut off, but at different points, as before, with the remote session dead. Changing the Zmodem receiver from rz to lrz on the local end (since the sender on the remote end is lsz) does not change the behavior. Anyway, I went back and replaced the byte loop with something more efficient, and it goes about 20 times faster. But this doesn't help either, it only makes it fail faster. But aha, what if a doubled IAC is broken across successive pty reads -- we have to make the "previous character" memory persistent. Well, that was a good insight, but it still didn't fix it. The log shows the IAC handling code is working fine. What does sz say? Capturing its stderr to a file... "Retry 1: Got ZCAN". Next time: "Retry 1: Got TIMEOUT". Next time: Got ZCAN. Trying different Zmodem options... apparently I don't need to use short blocks. But I do need to use -e, probably because of Telnet NVT treatment of carriage return; without -e, there is a "persistent CRC error". -O disables timeouts, but this makes no difference. OK, we still have two Big Problems: 1. When a long file has no IACs, the final < 1K of the file is not received. 2. When a long file has IACs, the transfer generally stops very early. Problem 1: the transfer consistently fails less than 1K from the end of the file. Upon CONNECT back to the host, a big Zmodem packet is sitting there waiting to be read, which means ttptycmd()'s copy of rz is terminating early. Can we catch it in the debug log? Doing this takes forever and writes a GB to the disk... And then the problem doesn't happen. Also, I can receive a HUGE text file almost instantly with no errors at all. Switching to lrz on the receiving end, now I see the error messages, about 300 lines like this: Retry 0: Garbage count exceeded Bytes received: 872352/1000000 BPS:85464 ETA 00:01 Retry 0: Bad CRC Bytes received: 892448/1000000 BPS:86690 ETA 00:01 Retry 0: Bad CRC Retry 0: Garbage count exceeded Retry 0: Garbage count exceeded Retry 0: Garbage count exceeded Retry 0: Garbage count exceeded Retry 0: Garbage count exceeded Retry 0: Garbage count exceeded Retry 0: Got ERROR Bytes received: 898336/1000000 BPS:84293 ETA 00:01 Retry 0: Bad CRC Retry 0: Garbage count exceeded Retry 0: Garbage count exceeded Bytes received: 900384/1000000 BPS:83751 ETA 00:01 Bad escape sequence 2fRe try 0: Bad data subpacket Bytes received: 941472/1000000 BPS:86191 ETA 00:00 Retry 0: Bad CRC Retry 0: Garbage count exceeded Even when it succeeds, it gets these. But if I receive a text file, no matter how big, no errors or retries or timeouts at all. So it appears that there is only one problem: a big-time lack of transparency regarding 8-bit and/or control characters. The odd thing is, it's not that the characters can't get through -- they all can -- but they seem to cause transitory blockages. 16 Aug 2007. Cleaned up the remaining pointer signedness warnings in ckutio.c, but this was a mistake, it broke Kerberos connections completely. Undid the changes. ckutio.c, 17 Aug 2007. Changed all return() in the fork()==0 section of ttptycmd() to exit(). ckutio.c, 17 Aug 2007. Tried explicitly setting the slave pty to rawmode. Makes no difference. Tried using the Mac OS X (curses) raw() function, and also system("stty raw"); still no difference. Tried doing all of these in different combinations and orders. I found one combination that cuts the errors about in half, and the transfer of the no-IAC test pattern almost always succeeds (but it's slow). Anyway, it doesn't help much with the test pattern that contains IACs. Well, the code is more solid than it was before but functionally we have not advanced much if we can't download a binary file with Zmodem! On the other hand, we can upload them, and we can transfer text files in both directions, which is an improvement over the previous situation, in which the entire session would hang due to loss of synchronization of the encryption stream. Tried adding -funsigned-char to CFLAGS of Mac OS X target. It does not make the "signedness" warnings go away and it doesn't change the runtime symptoms. I tried a simpler version of pty_make_raw(), the one from Serg Iakovlev, but it was a total failure. That's encouraging though, because it indicates that pty_make_raw() is the right place to be working. Then I made pty_make_raw() set or unset every single terminal flag explicitly. This made no difference, but didn't hurt anything either. Then I made pty_make_raw() explicitly set all the c_cc[] characters to 0 (but left c_cc[VMIN] as 1). This made no difference either. I checked pty_make_raw() against ttpkt() and the only difference I found in the terminal flags is that ttpkt() sets IGNPAR thinking it means "ignore parity errors" when really it means "discard any character that has a parity error" (at least according to Iakovlev) -- exactly the opposite. But I tried it both ways, no difference. 17 Aug 2007. I noticed that even Zmodem text receives can fail. They don't get any errors, they just get cut off shortly before the end. (But usually they succeed, and fast too, like 500K cps). What if I don't call pty_make_raw() at all on the slave pty? zrt: EESSSSSSSS: 80% good (E = stopped just before end but no other errors) zrb no-IAC test pattern, short blocks: 1. S/5 (success with 5 screens of errors. 2. S/7 3. S/7 4. S/6 5. E/7 (failed just before end) 6. S/7 7. S/6 8. S/6 9. S/6 10. S/4 So, lots of errors, but it recovered 90% of the time. Next, same thing, but without requesting short blocks: 1. E/5 2. S/5 3. E/4 4. S/5 5. S/5 6. S/5 7. X/0 (hard failure right away: "Got ZCAN" 8. S/5 9. S/5 10. S/5 So it doesn't look like short blocks make that much difference. Now what if I turn off prefixing? Bad CRC, fails immediately every time. Putting back pty_make_raw(slave), it still fails hard. Tried a new strategy with pty_make_raw(): rather than modify existing flags, I set all flags to 0, and then turn on only those few that we need like CS8. Now we get only 2.5 screens of errors instead 4-7 and the transfer rate is higher for binary files (all of the previous ones were under 100K CPS, while for text files it was 400-500K CPS): 1. S/2 195669 CPS 2. S/2 194720 3. E/3 4. S/2 192550 5. S/3 192325 6. S/3 145066 7. S/2 200689 8. S/3 188948 9. S/2 209461 10. S/3 181991 I noticed that there was no TIOCSTTY ioctl in the pty/fork setup sequence, which is recommended somewhere, so I tried that and it was a disaster; the entire session hung. I took it back out. 18 Aug 2007. Tried some transfers over a clear-text (not encrypted) connection with the same results: smooth, fast transfer of a big text file (400K cps); rocky but successful transfer of the no-IAC binary pattern file (135K cps). Switching back to ttruncmd(), the same binary file is received at 1.5M cps, and the no-IAC binary file totally fails after too many "Bad CRC"s; and we already know that any file that contains IACs will fail. One might say that ttptycmd() is better in every respect than ttruncmd() except in speed (when it works). Let's see if ttyptycmd still works in remote mode (to local K95): . sz / text works, but slowly. . lsz / text works but some weird errors are reported. . lsz / binary / no IAC doesn't work at all (CRC-32 mismatch for a header; Unexpected control character ignored: 13, etc). . sz / binary / no IAC works OK but slow. . sz / binary / full test pattern with IAC works OK but slow. . Sending text into rz fails completely. What about ttruncmd() in remote mode? . send /text works, fast. . send /binary works, fast. . receive /text works, not so fast but not bad. . receive /binary works, not so fast but not bad. So we use ttruncmd() for remote mode, and we use it for local mode serial-port and modem connections, and we use ttptycmd() on network connections because (a) they might be encrypted, and (b) even if they are not, they use some protocol that we have to handle, e.g. Telnet, Rlogin. 19 Aug 2007. Discovered that Sending binary files no longer works. Text is OK, binary transfers don't even start. This happens on both encrypted and clear-text connections. ttptycmd() is being used in both cases. But oddly enough, receiving binary still works as before. What did I break, and when? Oh, it was just the script, when I changed it from using sz to lsz. Putting it back to sz makes it work, even with the full 3.2MB binary pattern with IACs. I backed off the changes I made to ckctel.c to suppress some warnings, in view of the fact that similar changes to ckutio.c broke things so badly. 19 Aug 2007. If sz is not given the -e flag, it sends control characters bare, except ^P, ^Q, ^S, and ^X. ^X is the control prefix, so ^A is sent ^X followed by A. With -e, all C0 control chars are prefixed, but with ^X, which is, of course, a control character. Interestingly, the C1 analogs of ^P, ^Q, ^S (but not ^X and, unfortunately, not IAC) are also prefixed. -e makes no difference for 8-bit characters. If we have a Telnet connection and the server is in ASCII (NVT) mode, CR is always followed by LF or NUL. Well, it seems the server is putting us (Kermit) in binary mode in this case, but staying in ASCII mode itself. Added code to handle NVT byte stuffing and unstuffing in each direction independently, according to the TRANSMIT_BINARY state in that direction. I made a file containing just the bytes 0-31 and 127 and 128-159 and 255 (66 bytes all together) and sending it from the host to C-Kermit, the local log shows that every control character was received correctly and all TELNET conversions were done right -- NUL removed after CR (and only after CR); IAC removed after IAC (and only after an IAC meant as a quote). For the first time, I can receive the 1MB all-values test pattern, but there are still tons of (correctable) CRC errors, so the transfer rate is really awful, like about 5% of what we get with a text file (25Kcps instead of 500). Further experimentation shows that the fundamental transparency problem is fixed; we can receive short files (say, 1K or less) containing absolutely any byte values in any combination with no errors at all. But once the file size reaches (say) 10K, we get CRC errors, like one every 2 or 3K of data. These are not deterministic. In successive transfers of the same file, they come in different spots. It's tempting to blame pty buffer overruns, but then text files would show the same behavior. When a binary file size exceeds, say, 1MB, the chances of successful completion go way down, independent of whether my external protocol is rz or lrz. I like lrz better because the error reports come out on the screen as the transfer is going on. Trying to download a real-world binary file -- a 2.2MB C-Kermit executable -- I get 4500 error messages but the transfer eventually succeeds, with an effective throughput of 21Kcps. Actually it turns out that "sz -a somebigtextfile" (2.2MB) also gets a lot of CRC errors. The -e flag (escape all control characters) makes the same big text file transfer with few or no errors. It's not sure-fire. Sometimes no errors, sometimes one or two, and sometimes a fatal error that kills the transfer. With binary files... a 32K binary file seems to make it every time. 40K fails about 50% of the time. 48K fails 60% and every time it fails, it has created a partial file of exactly 32K (32768 bytes). 96K fails 9 out of 10 times, when it fails, the partial file is always 0 bytes, or 32768, or 65536, but that just means that rz's file output buffer is 32K. Why, then, do binary files cause trouble if it is not a solid transparency problem? If a certain file can get through once, why can't it get through every time? When a character arrives at the pty, the pty driver probably takes a different path through its code, checking the terminal flags that would affect that character. I tried making Kermit's network read buffers very small but, surprisingly, this made things worse. I also tried making them very much bigger, which didn't help either. 24K still seems to be the right size. So, is it that some characters take longer to process than others? So long that data is lost due to lack of flow control between TCP and the pty? One way to test this theory is to slow Zmodem down. I tried "-l 32" which, according to the man page, tells sz to "wait for the receiver to acknowledge correct data every N (32 <= N <= 1024) characters. This may be used to avoid network over-run when XOFF flow control is lacking." Makes no difference. I also tried the -w (Window) switch, ditto. In fact there are all sorts of options to set the "window size", "packet length", "block size", and "frame length", but with no explanation of what these mean or how they are related. If I crank everything down to minimum value: lsz q -L 32 -l 32 -w 1 I get 50% success with the 96K file instead of 10%. Adding -e, oddly enough, made it worse. I also tried setting the environment variable ZNULLS to different numbers like 512, no help there either. I tried making the read-from-net-write-to-pty buffer small (1K) but leaving the pty-to-net one big. This improves chances of success, but it's intolerably slow (3Kcps when the connection is capable of 500K). I also changed the write-to-pty operation from a single write() call of possibly many K characters to a byte loop, one write() per byte. Same result: success (but still about 300 recoverable errors), throughput 3Kcps. 20 Aug 2007. With ttptycmd() configured to write to the pty in a byte loop, it is possible to delay each write. Adding a 10msec delay per character results in a transfer that runs at about 20 cps and (for the 96K test file) would take about 80 minutes to complete. And yet it still gets just as many errors. So it's not a matter of timing either. The errors come, on average, every file 388 bytes, but not at regular intervals. I tried the TIOCREMOTE ioctl on the pty master, as discussed somewhat obliquely in the Mac OS X "man pty" page; "This mode causes input to the pseudo terminal to be flow controlled and not input edited (regardless of the terminal mode)" -- sounds like just the ticket but it made no difference. Actually, looking at a man page on another OS (Solaris), it says this is only for lines of text, EOLs are supplied, so that would mess up the protocol. So remember: don't use this. Tried without O_NDELAY; the behavior was the same but the speed was much slower. Tried switching back to the ckupty.c routines on Mac OS X and found that it works now the same as with openpty(), except that I seem to get more getty babble at the end. But this means I can run some tests on Solaris. I moved the entire test environment from Mac OS X 10.4.9 to Solaris 9. But it doesn't work at all. Trying to figure out the ckupty.c modules again. . do_pty() calls pty_getpty() which returns in arg1 the fd of the pty master. . Then it creates a pipe as a way to tell when the child dies . Then it creates a fork: - The parent does a blocking read from the pipe - The child calls getptyslave() to get the pty slave and writes one byte to the pipe and then execs the command it's supposed to run Note that the file descriptor of the slave is known only to the lower fork. Therefore the lower fork is the one that has to set all the tty modes, etc. I took care of all that but the ckupty.c method doesn't work at all on Solaris. But it works "fine" on Mac OS X (the 32K all-bytes test file transfers instantly with no errors, but the 96K one errors out). The problem on Solaris is that pty_make_raw() fails on the masterfd (but not on the slavefd) with errno 25 "ioctl inappropriate for device". It doesn't matter whether I do it in ckupty.c or ckutio.c. I found a web page on kde.org that says Solaris does not allow tcget/setattr() on a pty master. But the Sun "knowledge base" is not open to the public. Well, presumably changes made to the slave are reflected in the master (comments in Solaris telnetd seem to confirm this...) Let's come back to Solaris later. Moving to a Linux with lrzsz installed... Built a Kerberos 5 version with USE_CKUPTY_C. Like on Mac OS X, it transfers short files OK and chokes on longer ones. Switched to openpty(), it behaves the same. So the problems on Mac OS X are evidently not OS-specific, which is good I guess, since that means finding the way around them will apply to more than one platform. 21 Aug 2007. Look into TIOCSCTTY again. On System V based OS's, opening a pty acquires a controlling terminal automatically. On BSD-based OS's, no; you have to use the TIOCSCTTY on the slave file descriptor to give it one. I'm not sure why a controlling terminal would be needed, except that without one, the virtual device "/dev/tty" does not exist for the process that runs on the pty, and maybe the application that runs there (e.g. rzsz) checks for it. On the downside, having a controlling terminal opens the process up to terminal interrupts like SIGINT and SIGQUIT. Until now I have not been using this ioctl(). Results (in Linux): With TIOCSCTTY: 96K all-bytes test: 11 screens of errors, then success Without TIOCSCTTY: exactly the same. Tried the same thing with TIOCNOTTY instead of TIOCSCTTY, with exactly the same results (no effect whatsoever). There has to be a way to make this work, because Zmodem works through telnetd, which basically the same thing as ttptycmd(): a relay between the network and a pty. ttptycmd() is like telnetd backwards. Modern telnetds are not much help; they don't access ptys or the network directly, they go through "mux" devices so I can't see what they're doing to get transparency and flow control. An old BSD telnetd uses packet mode but that would be a big deal... I tried ignoring various signals like SIGTTOU and SITSTP, since some Telnet clients do this. No effect, no difference. Anyway, in Linux the transfers almost always finish OK despite the many errors. There is just some trick I'm missing to make the pty accept a stream of arbitrary bytes without hiccuping. What about Solaris, which uses ckupty.c? In streams-based OS's, where line disciplines and whatnot are pushed on top of the pty, it looks like the pty module saves the file descriptor of the "bare" slave pty (as 'spty') before pushing things onto it, and then later uses spty rather than the regular slave pty file descriptor when getting/setting terminal modes. I'm not sure what this is all about but it's definitely SysVish... It happens if STREAMSPTY is defined, but I noticed that STREAMSPTY is never defined anywhere. I tried defining it so we take an entirely different path through the code. It made absolutely no difference. Then I noticed that HAVE_STREAMS is not defined for Solaris either. Tried defining it, but the session didn't work at all, no i/o. Removing the HAVE_STREAMS definition but keeping the STREAMSPTY defined, I rebuilt and tried "set host /connect /pty emacs". I got an EMACS screen but could not type anything into it, which means that STREAMSPTY should not be defined either. Removed the definition and "set host /pty" works again. So what's the problem with ttptycmd()? In fact, ttptycmd() works on Solaris with Kermit as the external protocol, but not with Zmodem, not even with text files. So again, there is no fundamental problem with the code or the logic, it's Just A Matter Of Transparency to control and/or 8-bit characters -- some trick I don't know about. Looking at the Solaris debug log... I see that ckupty.c is calling init_termbuf() to set the tty modes of the master, not the slave, and set_termbuf() to set them, but you can't do that in Solaris, error 25. This is in getptyslave(). Shouldn't getptyslave() be setting the tty modes of the slave, not the master? I changed it to do this, but like all other changes, it made no difference. I checked to make sure that after the change, "set host /pty /connect emacs" still worked and it did. And then what... I had some code to redirect stderr in ckupty.c that was not being executing due to a typo. When I fixed the typo, poof, Zmodem binary transfers started working, or working as well as they work in Linux and Mac OS X. It turns out that if I don't redirect stderr, sz and rz just don't work. But lsz and lrz do. But if I do redirect it, I don't see the progress messages from lsz/lrz. 22 Aug 2007. Built on HP-UX 11i v3 (B.11.31 U ia64) with optimizing compiler, got tons of picky warnings, but it finished and linked and runs OK. Many of the warnings were like this: "ckucns.c", line 1606: warning #2068-D: integer conversion resulted in a change of sign: tnopt[0] = (CHAR) IAC; IAC is defined as 255 in ckctel.h. If I define it as 0xff, I don't get the warnings. I changed the definitions of all the Telnet commands to be in hex notation rather than decimal. If cuts way down on the HP-UX warnings and doesn't seem to cause problems elsewhere. ckctel.h, 23 Aug 2007. Now it looks like Solaris is working but then it hangs at the end. It appears as if the ckupty.c module is blocking SIGCHLD. Debug log shows that when the transfer is complete, we received IAC DM (Telnet Data Mark) after sz's last gasp and before the shell prompt is printed. But calling tn_doop() in this case is a mistake because we are reading the number of bytes that we know are available in a counted loop, but tn_doop() would consume an unknown number of bytes and we would never know when to exit the loop. Anyway, C-Kermit doesn't do anything with DM. Skipping over tn_doop() (and not writing out the Telnet command bytes) fixes the hanging condition at the end, even though SIGCHLD is never raised. ckutio.c, 23 Aug 2007. Some tests, Solaris to NetBSD over K5. zst sends ascii.txt, a 2.36MB ascii text file (Kcps / Errors). zrt receives the same file: zst 587/0 526/0 542/0 434/0 423/0 zrt 827/0 800/0 847/0 FAIL 610/0 So text is good. Binary not so good. Here we transfer the 1MB all-bytes pattern file. zrb receives it successfully, but with 1248 errors, at only 15Kcps. Sending the same file out always fails: Begin 20070823 16:32:07: SEND BINARY all2.bin [sz] Sending: all2.bin Bytes Sent: 5600/1000000 BPS:12446 ETA 01:19 FAILURE End 20070823 16:32:13 Elapsed time: 6.617992999999842 cps = 151103.2121067556 lsz: caught signal 1; exiting Decided to move to Linux but found that something is screwed up in Linux C-Kermit with tilde expansion: send ~/testfiles/all.bin doesn't expand at all (but it did yesterday!). The problem was in the ancient, ancient realuid/setuid handling code; real_uid() no longer works in Linux. I worked around this in whoami() by setting ruid to getuid() if real_uid() returned a negative number. Maybe dangerous, worry about it later. ckufio.c, 23 Aug 2007. ANYWAY... after fixing that, I tested zsb on Linux, and it's broken there too, using openpty(), so it's nothing to do with ckupty.c. After sending the first Zmodem data packet, it just hangs, nothing comes back. In text mode it gets farther, but then the same thing happens. Captured stderr from rz on the far end: Bytes received: 608/1000000 BPS:21137 ETA 00:47 Retry 0: Bad CRC Bytes received: 864/1000000 BPS:23540 ETA 00:42 Retry 0: Bad CRC Bytes received: 1120/1000000 BPS:25003 ETA 00:39 Retry 0: Bad CRC Bytes received: 5696/1000000 BPS:56988 ETA 00:17 Retry 0: Bad CRC Bytes received: 9120/1000000 BPS:62227 ETA 00:15 Retry 0: Bad CRC Bytes received: 9376/1000000 BPS:60766 ETA 00:16 Retry 0: Bad CRC Bytes received: 9632/1000000 BPS:60361 ETA 00:16 Retry 0: Got TIMEOUT Retry 0: Sender Canceled Retry 0: Got ZCAN The local sz, however, doesn't give any error message. ZCAN means: "other end canceled session by sending 5 ^X's" (or user typed them). What actually happens is that ttptycmd()'s select() times out waiting for something from the Zmodem partner and ttptycmd() itself kills the sz fork with SIGHUP. When lsz receives SIGHUP it sends the ZCAN. So the real problem is that after some point we're not receiving anything. I changed the timeout from 4 seconds to 30 seconds and now I see it just stops for long periods of time and then resumes. The lrz log on the receiving end shows tons of timouts, CRC errors, and other errors. The local log shows that lsz wound up sending ZCAN (2 x (10 x ^H, 10 x ^X)). Moving on to another problem... Turns out Ctrl-C (SIGINT) is working right after all. Since I'm using my test scripts like kerbang scripts, Ctrl-C exits through trap(), as it should, closing the connection and cleaning up. If I start Kermit and tell it to TAKE the script, then Ctrl-C brings me back to the prompt with the connection still open (as it should). However, until now I haven't done anything about the fork or the ptys. Added code to trap() to kill the fork and close the master pty. ckuusx.c, 24 Aug 2007. Added code to try to break the deadlock. If select() times out, but we have stuff to write either to the pty or the net, try to do it anyway, even though select() did not say we could. But this doesn't help because when select() times out we don't have anything to write. The problem is that after receiving that last packet from the remote rz, the local lsz doesn't seem to do anything, as if the lower fork wasn't running (and to confirm this hypothesis, sometimes I noticed that when I Ctrl-C'd out of this, the transfer would take off again). Backing up and testing with gkermit rather than zmodem: kst ripple.txt [824K] OK kst ascii.txt [1359K] OK krt ripple.txt -- FAILED It seems that we can't handle streaming. If I set up krt to disable streaming on receipt, it works OK. krt ripple.txt [824K] OK krb all2.bin [1000K] OK So here we have no trouble sending but big trouble receiving unless we disable streaming. Whereas with Zmodem we have trouble receiving. But this wasn't happening before, what changed? Using C-Kermit on the far end to receive the file with debug log on, I see that it is sending 4K data packet after 4K data packet, with the local gkermit silent, as expected. About midway through the transfer, the local Kermit sends an error packet "Transmission error on reliable link". Looking at G-Kermit's debug log... It receives the first five 4K data packets OK, but gets a CRC error on the fifth one, and sends the Error packet. So it has received a stream of 20-some thousand bytes OK and then messes up. That number sounds a lot like ttptycmd()'s buffer size. I changed the buffer sizes to be different: Read from pty and write to net: 4K Read from net and write to pty: 1K This time it received the first 4K packet and failed on the second one. Then I increased the buffers to 98K each, expecting to receive lots more packets successfully but it bombed out on the 5th one. But that's good, it confirms there's no logic error in the buffer management. Just to make sure, though, let's set the buffer size smaller than the packet size and disable streaming. In this case we get 4 good data packets and a CRC error on the 5th one and so we request retransmission, and the next 8 times it arrives it gets a different CRC error, but the 9th copy is OK. Then the next packet comes and it gets a CRC error every time. And this is nothing but plain ASCII text. Switching to remote mode: REMOTE=1 kk kst (after tricking myself because it was using ttruncmd() for this...) I see that nothing works at all. What did I break? 24 Aug 2007. Fixed ttptycmd() to restore console modes after a remote-mode transfer. ckutio.c, 25 Aug 2007. Noticed that error codes like ESRCH are not available in all modules. That's because of some complicated in #ifdefs in ckcdeb.h that wind up not always #including . But I notice that ckutio.c includes it unconditionally with no ill effects, and so does ckvfio.c. Does any version of Unix at all not have ? Added a catch-all clause to ckcdeb.h to #include (in UNIX only) if, after the other clauses, ESRCH was still not defined. ckcdeb.h, 25 Aug 2007. Now back to debugging ttptycmd()... Remote-mode transfers with ttptycmd() were broken in two places, maybe as long as 2 weeks ago (this would have affected non-network transfers too, which I can't test any more). The logic was missing in a couple places for the non-network and/or non-Telnet and/or non-encrypting connections (if statements with no else parts). Fixed in ckutio.c, 25 Aug 2007. Testing remote mode: kst OK zst OK ksb OK zsb OK krt OK zrt OK krb OK zrb OK Functionally it all works but there are hitches with Zmodem as always. When sending to K95: . If I send with lsz, there are hundreds of "Subpacket too long" errors, and the transfer is very slow, but it succeeds. . If I send with the 1994 Omen version of sz, transmission is instantaneous and without errors, but then it hangs at the end. . If I bypass C-Kermit and send direct from lsz or sz, both work fine. So clearly the ptys are getting in the way. The hanging at the end would be caused by the sz process closing before its last output reached the master pty. It would need to do some form of flushing and/or pausing at the end but there's nothing I can do about that; these programs were not designed to be used in this way. Anyway, it only seems to happen with files longer than 100K. For local mode, testing in Solaris over our Kerberos 5 connection again: gkermit lrzsz kst OK zst FAIL ksb OK zsb FAIL krt OK zrt OK but with errors krb OK zrb FAIL If I use Omen rzsz as the external protocol (e.g. with zst), it blocks redirection and it sends the file to my terminal, rather than over the connection. This would probably be because it finds out the device name of the job's controlling terminal and opens it, to prevent redirection. This is hard to prevent in Solaris because there is no TIOCSTTY ioctl(). Supposedly the same thing is accomplished by closing and reopening the slave pty after doing setsid(). I added code to do this, but it made no difference. (If I use lsz instead of sz, it is indeed redirected, but jams up after about 15K.) ckupty.c, 27 Aug 2007. On Mac OS X with sz 3.73 1-30-03, however, the redirection works, so I assume it would also work in Linux, FreeBSD, NetBSD, etc, too. Doing the full test suite on Mac OS X: gkermit lrzsz rzsz kst OK zst FAIL (1) OK ksb OK zsb FAIL (2) OK krt OK zrt OK (3) OK for 100K file, fails for longer. krb OK zrb FAIL (4) OK (1MB all-bytes test pattern) (1) 64K file OK every time; 100K file fails every time. (2) 10K file fails every time. (3) Succeeds with 800K file but gets a few recoverable errors. (4) Succeeds with 48K binary file with some errors, fails with longer ones. So actually it looks pretty good, it's just that lrzsz messes up. When sending with lsz if I include -L 512 it sends the 100K test file with no errors, but still chokes on longer ones. Testing on Mac OS X again, but this time over a clear-text Telnet connection: gkermit lrzsz rzsz kst OK zst FAIL(1) OK ksb OK zsb FAIL(2) OK krt OK zrt OK(3) OK krb OK zrb FAIL(4) OK (1) Almost worked, finished 777K out of 824K without errors. (2) Got tons of errors, failed in first 30K out of 1000K. (3) OK for 100K file but fails for larger. (4) OK for 48K binary fail but fails for larger. Maybe see if we can do without the OPENPTY part. TOMORROW -- just clean up the code, add some SET / SHOW / HELP commands, document it, and move on. Note: In K95, SET WINDOW sets the Zmodem packet length, 32 - 1024, multiple of 64. SEE ~/80/external.txt Changed ftp port from int to unsigned int. ckcftp.c, 30 Aug 2007. Tried again to build KRB4/KRB5/SSL/TLS version for Solaris 9. Had to update the build procedure again, of course, because of new file and directory names, but ran into problems anyway because the cu-solaris9g+krb5+krb4+openssl+shadow+pam+zlib target was calling another target that did not know about the hardwired pathnames. Integrated the two targets and tried building again. It actually compiled ok (but with lots of warnings from the security modules), but failed at link time with krb5_init_ets not found; fixed that with an #ifdef NO_KRB5_INIT_ETS, now it builds OK but without the ftp client. Tried building it WITH the FTP and that was OK too, no changes needed except to the build procedure. 12 Feb 2008, that is: C-Kermit 8.0.212 : 20080212. Tried to build with -DCK_SRP and -lsrp but: hash_supported ckcftp.o hash_getdescbyname ckcftp.o hash_getdescbyid ckcftp.o cipher_getdescbyname ckcftp.o krypto_delete ckcftp.o krypto_new ckcftp.o cipher_supported ckcftp.o krypto_msg_priv ckcftp.o krypto_msg_safe ckcftp.o hash_getlist ckcftp.o cipher_getlist ckcftp.o cipher_getdescbyid ckcftp.o Sent mail to Tom Wu and backed off for now. makefile, 14 Feb 2008. (Tom Wu never answered; seems like SRP is defunct.) The ".blah = xxx" form of variable assignment only worked for variables names of length 22 or less, noticed and fixed by Wolfram Sang. ckucmd.c, 5 Mar 2008. In "set host /pty ssh ..." connections, the INPUT command suddenly stopped working. This is in Solaris 9. It happens with all 8.0.* versions of C-Kermit, so it's nothing to do with ttptycmd(). Added some debug() statements but they don't show anything. Turns out there wasn't a problem after all. Wed Mar 26 16:04:53 2008 Changed cmifi() to not print "?No files match" (or whatever) if SET QUIET ON. ckucmd.c, 26 Mar 2008. Added \v(remoteip) for the IP address of the host we're connected to, and \v(inmessage) for INPUT status messages corresponding to \v(instatus). ckuusr.h, ckcmai.c, ckuus[24].c, 26 Mar 2008. Made \fkeywordval() strip braces/quotes from the right-hand side so we can handle things like: password="stringwithspaceatend " ckuus4.c, 6 Aug 2008. Added invisible PUTENV command for UNIX only. Value should not be enclosed in doublequotes. Requires lge \v(buildid) 20080826. ckuusr.[ch], 26 Aug 2008. Added SET VARIABLE-EVALUATION { RECURSIVE, SIMPLE }. This is highly experimental, but also highly desirable if it works out. SIMPLE inhibits the default recursive method of evaluating \%x and \&x[] variables, which is, quite frankly, nuts and makes programming in Kermit at best counterintuitive. I made an exception in the case of array subscripts, because changing how they are evaluated could break a lot of scripts, and anyway there should never be any harm in evaluating them recursively because their final value is always (or should be) numeric, not some string that might contain backslashes. The SET VAR setting is on the stack, just like SET QUIET (it follows the quiet/xquiet code in ckuus[356].c), so macros or command files that change it can't break the script that invokes them. Added \frecurse() to force recursive evaluation of a \%x or \&x[] variable regardless of the VARIABLE-EVALUATION setting. Added \v(vareval) to allow programmatic setting to current setting. Tested on Solaris 9 but should be totally portable. ckuusr.[ch], ckuus[356].c, 11 Sep 2008. From Günter Knauf: 64-bit builds were failing on SuSE Linux because libresolv and libcrypt were in lib64 rather than lib; updated the tests in the linux makefile target to find them. makefile, 12 Jan 2009. Tried building on Red Hat Enterprise Linux Server release 5.3 64-bit. There is no curses or ncurses. "make linuxnc" compiled OK but collapsed at link time looking for crypt(), res_search(), and dn_expand(). Turned out the linuxnc (and linuxc) targets needed the same treatment as the Linux one for 64-bit Linuxes. makefile, 3 Mar 2009. Consolidated the linux targets so we no longer need three separate ones for curses, ncurses, and no curses. "make linux" works ok on computers with and without (n)curses. "make linux+ssl", ditto. "linux+krb5+ssl builds OK but needs -DNO_KRB5_INIT_ETS". Makefile, 3 Mar 2009. Fixed copyright date announced in herald, ckuus5.c, 3 Mar 2009. Patch from Seth Theriault to avoid deprecation warning for utmp references in ckufio.c in Mac OS X 10.5 (later, this became a consolidated makefile target that works automatically for at least Mac OS X 10.3.9 through 10.5.6). makefile, ckufio.c, 28 April 2009. zshcmd() (the function used by RUN and ! to run external commands) was not falling back as expected in Linux RHEL4/5 if SHELL was not defined in the environment. Also in all Unix versions, there was no indication if a RUN/! command failed (other than the return code) because the specified shell didn't exist or was not executable (e.g. the SHELL environment variable was misdefined). Now it prints the name of the offending shell and the reason it couldn't be executed (Not found, Permission denied, etc). ckufio.c, 28 April 2009. There is no easy way to get the last field of string; for example, the extension from a filename, which might have any number of fields. In general we want to be able to get "word number n" counting from the right; \fword() lacks this ability. Now if you give it a negative word number, that says to count from the right; for example \fword(one two three four five, -2) returns "four". ckclib.c, ckuusr.c, 14 May 2009. Fixed a typo in the aix51+openssl (SSLLIBS should have been SSLLIB). From Jason Lehr. makefile, 27 May 2009. Updated the linux+openssl+zlib+shadow+pam target to chain to the new main Linux target. A bunch of other ones remain un-updated. makefile, 12 Jun 2009. Updates to the new Mac OS X 10.5 target from Seth Theriault (which is supposed to work on all Mac OS 10-point-anything) to avoid warnings that came up on on Mac OS 10.4.11/Intel. Once this one is proven we should be able to remove/consolidate lots of other ones. makefile, 12 Jun 2009. C-Kermit disables SSL with the message "?OpenSSL libraries do not match required version." if the version of OpenSSL that Kermit was built with is not exactly the same as the version that is loaded dynamically at runtime. This is actually the proper behavior, since APIs are not guaranteed not to change between OpenSSL versions prior to 1.0.0. Made the error message more informative. ck_ssl.c, 26 Aug 2009, and again 28 Aug 2009. AIX 6.1 is out, it is really just a new name for AIX 5.4. Added makefile targets, plus for the first I made AIX 4.2 and later figure out its version number in the makefile target so we don't have to keep adding new -DAIXnn sections to the code, and also get its hardware name (e.g. "powerpc") from uname at make time, rather than hardwiring "rs6000" as I did before. Consolidated all AIX 4.2 and later targets so now just "make aix" or "make aix+ssl" can be used. Except not the gcc ones as they have some quirks so I'd rather not disturb them. Tested this on AIX 5.3. makefile, 28 Aug 2009. From Kinjal Shah, a correction to the Linux makefile entry that allows it find the 64-bit curses or ncurses library. makefile, 29 Aug 2009. Renamed aix4[23]: to oldaix4[23]: in makefile to fix the warning messages I didn't notice before. I didn't want to remove them because they have some special things that might still be needed, if anybody still has these AIX versions. makefile, 29 Aug 2009. Built on RHEL 5.3 64-bit, regular and with OpenSSL 0.9.8e. 31 Aug 2009. Built on NetBSD 5.0.1/i386, regular and with OpenSSL 0.9.9-dev, 1 Sep 2009. Changed SSL message to mention LD_LIBRARY_PATH (Solaris), SHLIB_PATH (HP-UX), LIBPATH (AIX), or LD_LIBRARY_PATH (Linux). ck_ssl.c, 3 Sep 2009 Noticed that "make linux+openssl" fails to include -lutil a link time, which it needs for openpty(). That's because this target is obsolete. I renamed it to be oldlinux+openssl and added linux+openssl as a synonym for linux+ssl. makefile, 3 Sep 2009. Tested linux+openssl+zlib+shadow+pam, it's OK. Also linux+krb5. Also linux+krb5+ssl. makefile, 3 Sep 2009. Tried building on Solaris 9 with OpenSSL 0.9.8k with solaris9g+openssl+shadow+pam+zlib, it failed like so: ck_ssl.c:2875: error: conflicting types for 'inet_aton' /usr/include/arpa/inet.h:52: previous declaration of 'inet_aton' was here make[2]: [ck_ssl.o] Error 1 make[2]: Leaving directory hmt/sirius1/prv0/kd/fdc/solaris9ssl' make[1]: [solaris2xg+openssl+zlib+pam+shadow] Error 2 make[1]: Leaving directory hmt/sirius1/prv0/kd/fdc/solaris9ssl' make: [solaris9g+openssl+shadow+pam+zlib] Error 2 The problem was caused by including an inet_aton() function ck_ssl.c for the benefit of platforms that don't have one in their libraries. This is defeated by including NO_DCL_INET_ATON in KFLAGS. I added this, but then I thought it would be a good idea to automatically sense the OpenSSL version so we can automatically set OPENSSL_097 or OPENSSL_098 rather than bombing out, so I added code to do that too, and also to set the Solaris version number: 9, 10, or 11. The new entry is solaris9g+openssl. ckcdeb.h, makefile, 3 Sep 2009. Fixed a complaint in ckufio.c about implicit declaration of initgroups. ckufio.c, 4 Sep 2009. Built on Solaris 10 with gcc and Sun CC using new solaris{9,10,11} target that is like the new solaris{9,10,11}g one but without the gccisms. makefile, 4 Sep 2009. Changed solaris{9,10,11}g+ssl target to set only the SSL-specific things and then chain to the main solaris{9,10,11}g target. Tested OK on Solaris 9 and 10. makefile, 4 Sep 2009. Created solaris{9,10,11}+ssl target that is exactly like the solaris{9,10,11}g+ssl except it chains to the solaris{9,10,11} target instead of the solaris{9,10,11}g one. That is, it builds an SSL version of C-Kermit using Sun CC rather than gcc. makefile, 4 Sep 2009. Tried building on HP-UX 10.20, bundled (non-ANSI) compiler ("make hpux1000"). This failed until I: . Moved a struct initialization out of setextern(), ckuus3.c. . Removed an ANSIism from the declaration of sigchld_handler() in ckutio.c . Added a cast to strcmp() in zvuser(), ckufio.c. Builds OK now. Built OK with "hpux1000o" (the ANSI compiler) too. And with "hpux1000gcc". Couldn't test "hpux1000o+openssl". 21 Sep 2009. The Sony Playstation 2 and 3 are 64-bit PowerPC platforms that can run Linux if it is installed as an "other OS" on its hard disk; and the Linux kernel since 2.6.21 supports the PS3 without any patching required. Pawel Rogocz reported that "make linuxppc" (one of the old targets that has not yet been integrated into the main "linux" target) compiles OK on 2.6.29-ydl61.3 (Yellow Dog Linux release 6.2 'Pyxis'), but fails at link time because 'openpty' isn't found, because -lutil was not included, because that part was added only to the main linux target. I asked him to try "make linux" and he sent back a transcript in which there were thousands of errors from the curses code ckuusx.c. Later I tried it myself and it built without a hitch. My theory is that between then and now, a missing piece of the ncurses library (/usr/include/ncursesw) was installed. 21 Sep 2009. HP-UX 9.05 on PA-RISC 9000/712 building with hpux0900 (bundled compiler): . ckutio.c compilation failed with PENDIN and FLUSHO not defined in pty_make_raw(). I dummied definitions for them to handle this situation on this or any other platform where it might crop up. ckutio.c, 24 Sep 2009. . Ditto for the PTY module, + IMAXBEL. ckupty.c, 24 Sep 2009. . References to endusershell() were fatal in the bundled compiler. Changed the hpux0900 target to define NODCLENDUSERSHELL, and put a special case in ckufio.c to not put a cast in front of the call if NODCLENDUSERSHELL is defined. Now it builds and links OK. makefile, ckufio.c, 24 Sep 2009. HP-UX 9.05 on PA-RISC 9000/712 building with hpux0900o (optimizing compiler): . Warnings in ckutio.c at line 14860 about arguments to select (pointers are not assignment-compatible). "man select" says arguments are ints. Defining INTSELECT fixes these warnings but results in fatal errors later around line 14881 and others in the area involving FD_SET. This was too involved so I put it back as it was. 24 Sep 2009. Built OK on Solaris 10 with Sun CC. A couple warnings about implicit function declarations for curses routines because apparently they aren't declared in curses.h. Tuff. 25 Sep 2009. Tried building on Solaris 10 with Sun CC and OpenSSL 0.9.8k, and this uncovered various loose ends in the solaris9+openssl target, which I fixed. makefile, 25 Sep 2005. Fixed four typos in printfs in ck_ssl.c, \% instead of just %. 25 Sep 2009. Squelched 20-some complaints about a character array being referred to directly instead of by a pointer, plus several other similar nits to get rid of all the compilation warnings on Solaris 10 with Sun C 5.8 Patch 121015-06 2007/10/03. ckctel.c, ckctel.h, 25 Sep 2009. Built the result on the same Solaris 10 system with gcc 4.2.4 using the new solari10g+openssl target, working out a few kinks here too. makefile, 25 Sep 2009. Made consolidated Solaris 9/10/11 64-bit targets for gcc, solaris9g64, solaris10g64, solaris11g64, tested on Solaris 10 Sparc. makefile, 25 Sep 2009. Made consolidated Solaris 9/10/11 64-bit targets for Sun cc: solaris9_64, solaris10_64, solaris11_64. These simply set a couple flags and chain to the main solaris9 target. makefile, 25 Sep 2009. Removed a bunch of old superfluous Solaris 9 and 10 targets: oldsolaris9, oldsolaris9lfs, solaris9g64 solaris9g_64, oldsolaris10 old solaris10lfs, oldsolaris10+openssl, oldsolaris10g+openssl, solaris10_64, oldsolaris10g, solaris10g_64, solaris10g64. There are still plenty more to prune but it's a start. makefile, 25 Sep 2009. Added or fixed some missing prototypes in ckctel.h: fwdx_send_xauth_to_xserver(), fwdx_parse_displayname. 25 Sep 2009. Improved the instructions for building secure versions in the makefile, using this example: make solaris9+openssl "SSLINC=-I/opt/openssl-0.9.8k/include" \ "SSLLIB=-L/opt/openssl-0.9.8k/lib" makefile, http://kermit.columbia.edu/security.html, 25 Sep 2009. Built on HP-UX 11.11, 26 Sep 2009: . make hpux1100 (ok) . make hpux1100gcc (ok) . make hpux1100o (gets a lot of warnings about sendpath and sendfile, because they are also declared in , but builds OK) . make hpux1000gcc+openssl \ SSLINC=-I/opt/openssl/include SSLLIB=-L/opt/openssl/lib Note: sendpath and sendfile are not Kermit symbols. The warnings are coming from socket.h: 'Redeclaration of "sendfile" with a different storage class specifier'. This is nothing new; see notes of 2-4 Jan 2005. From Peter Eichhorn: . Update to makefile to make current code build OK on HP-UX 8.00. . Changes to format of some hints to make them more copy-and-pastable. makefile, ckuu5.c, 28 Sep 2009. From Peter Eichhorn: Changes to HP-UX 7.0 target to increase the switch table stack size, which was overflowing. makefile, 30 Sep 2009 HP-UX 6.5 (1989), "make hpux0650tcpc"... (8:19...) Needed to not include arpa/inet.h (which doesn't exist) and not use host address lists (add -DNOHADDRLIST), which gets us past ckcnet.c, but in ckcftp.c we bomb out on FD_SETSIZE undefined. Somehow we worked around this in ckcnet.c. Patched in a definition in ckcftp.c, and also added -DINTSELECT to compiler flags. Compiles ok, bombs at link time on bcopy, bzero, FD_ZERO, FD_SET, FD_ISSET. Now it compiles and links OK but dumps core when started. Added -DNOCKGETFQHOST, rebuilt from scratch (takes 35 minutes). It starts OK, but it dumps core when given a "telnet xxx" command, where xxx is a hostname. However, it works OK if an IP address is used: "telnet 123.45.6.78". It took all day to track this down, but now it's fixed (see the #ifdef HPUX6 sections of ckcnet.c). So now (for the first time, I think) we have both telnet and ftp in HP-UX 6.x, if anyone cares. ckcnet.[ch], ckcftp.c, makefile, 2 Oct 2009. Changed default SET TERMINAL TYPE type for K95 from vt320 to vt220. This is because Unix OS's such as Solaris have dropped vt320 as a terminal type. settrmtyp(), ckuus7.c, 5 Oct 2009. I moved the PUTENV command code, which was inline, to a function, doputenv(). ckuus[r7].c, ckuusr.h, 5 Oct 2009. Changed the UNIX version of SET TERMINAL TYPE to take a value and then do the equivalent of "export TERM=value" by calling doputenv(). This sets \$(TERM) correctly and passes its value along to inferior processes. However, to make this take effect within Kermit itself (for the fullscreen file transfer display and for the SCREEN command, Ctrl-L, etc) I also had to reinitialize the curses database, which is tricky because normally if you feed it an unknown terminal name, it just exits. ckuus7.c, 5 Oct 2009. Changed the little-known and little-used RESET command (which closes all open files) to also put command echoing back to normal in case it got messed up somehow (as in HP-UX 6.5, upon returning from PUSH). ckuusx.c, 5 Oct 2009. For Unix, increased string buffer sizes for wildcard expansion for all platforms that have BIGBUFOK defined from 500000 (0.5M) to 10000000 (10M) bytes, and for 64-bit builds to 2000000000 (2G) bytes. No point making it bigger than that because malloc's argument is a size_t, which is an int. ckufio.c, 5 Oct 2009. Built on Mac OS X 10.4.11, required one minor adjustment to the makefile (-DNODCLINITGROUPS). This was using the macosx10.5 target, which is supposed to be universal like the linux and netbsd targets, but not yet proven. Also built a 64-bit version (-mpowerpc64 -mcpu=G5 -mtune=G5 -arch ppc64); it compiles and links OK but won't start: "Bad CPU Type in executable". Fix later... makefile, 5 Oct 2009. Changes from Seth Theriault to suppress signed vs unsigned char warnings in Mac OS 10.5.8 from gcc4, and a new makefile target for Mac OS X (presumably 10.3.9 or later) + Kerberos 5 and OpenSSL. ckutio.c, ckuath.c, ckctel.c, ckcnet.c, ckcftp.c, ck_crp.c, makefile, 6 Oct 2009. Later I had to back out of these, because although it made for a clean build, in the resulting executable SSL connections didn't work. Tue Oct 6 17:23:27 2009 FTP address resolution is broken, but ftp_hookup() hasn't changed. So... (see the #ifdef HPUX6 sections of ckcnet.c) (I did, and I rolled back some of the changes from the other day, but it made no difference.) Putting back the ckcftp.c from a few weeks ago makes no difference. Putting back the ckcnet.c from a few weeks ago makes no difference. Added patches from Seth Theriault so macosx10.5+krb5+openssl would build on Mac OS X 10.3.9. makefile, ckcftp.c, 7 Oct 2009. Built today's code on Linux RHEL4, NetBSD 5.0.1, Solaris 9, and Mac OS X 10.4.11, both with and without SSL. The NetBSD system has OpenSSL 0.9.9-dev. 7 Oct 2009. In Mac OS X 10.6, the following symbols are unresolved at link time: _des_key_sched, _des_new_random_key, _des_ecb_encrypt, _des_init_random_number_generator, _des_fixup_key_parity. This is with OpenSSL 0.9.8k. But it doesn't happen on other platforms that have 0.9.8k. Added SET SESSION-LOG NULL-TERMINATED-TEXT. This is for the benefit of a speech synthesizer that will speak a line of text only after receiving a NUL character. A more general solution would be to define a filter or whatever, but who has time. ckuus[23x].c, 7 Oct 2009. Consolidated Mac OS X targets, and removed experimental 64-bit ones, because they never could work in 10.5 and earlier because 64-bit libs are missing, and 10.6 and later are 64-bit automatically. makefile, 8 Oct 2009. Built on Mac OS X 10.6.1. It came out automatically as a 64-bit build because __LP64__ is defined somewhere that I can't find. But this explains why the 0.9.8k on 10.6 comes up with missing symbols when the 0.9.8k lib 10.5 (or on Solaris or on Linux) does not: it's a different library: "Mach-O 64-bit dynamically linked shared library x86_64", rather than "Mach-O dynamically linked shared library ppc". Probably the 64-bit version has some things #ifdef'd out. Added -m32 to the CFLAGS and LNKFLAGS for the macosx+krb5+openssl targets, and it built OK one time. But then the errors came back. makefile, 8 Oct 2009. Updated C-Kermit installation for Mac OS X in ckuwr.html on the website. 8 Oct 2009. Tried some things to get around the problem with OpenSSL in Mac OS X 10.6, to no avail. Asked Jeff. He said, "MacOS X no longer includes DES anywhere on the system. Not for SSL, not for Kerberos, not for anything. This will increasingly become the situation on new operating systems. Windows 7 and 2008 R2 will also ship with no DES." Sure enough, the Mac OS X Server Upgrading and Migrating document for 10.6 says, "Mac OS X Server v10.6 does not support single DES encryption. It supports AES 128 and 256 encryption types. However, during a migration or upgrade from v10.4 to v10.6, servers that were Kerberized by the v10.5 Open Directory server will not use the AES 128 or 256 encryption types. To use the AES 128 or 256 encryption types you must re-Kerberize all servers." 12 Oct 2009. DES and 3DES encryption can be excluding removing the -DCK_DES flag. I removed this one and -DLIBDES (and -m32) and this makes a working 64-bit version. Then I added code to the macosx+krb5+openssl target to use these flags if the Mac OS X version was 10.5 or less and leave them out for 10.6 or later. Tested on 10.4.11 and 10.6.1. A better way to do it might have been "nm -gj libssl.dylib | grep des_", but that gives the same results on 10.4 and 10.6. Also, 10.6 still has /usr/include/ssl/des.h. makefile, 13 Oct 2009. Next issue: In file included from ckutio.c:15674: /usr/lib/gcc/i386-redhat-linux/3.4.6/include/varargs.h:4:2: #error "GCC no longer implements ." /usr/lib/gcc/i386-redhat-linux/3.4.6/include/varargs.h:5:2: #error "Revise your code to use ." The problem occurs when trying to force a non-ANSIC build with GCC. Changing the source file to include instead of doesn't help because evidently requires an ANSI C compiler. Nothing can be done about this. 13 Oct 2009. Next issue: Can't compile ckcftp.c with -DNOCSETS or -DNOSPL; some #ifdef/#endif doesn't match up. Sigh, this is the hardest kind of thing to debug. There's 17,622 lines of code in this module and no tool that I know of.... Wait, I wrote one. But it shows all the #if/#ifdef/#ifndef's and #endifs matching up just fine. Backing off to ckcftp.c of a few days ago (before char / unsigned char casts were added), I see that it builds OK, so I backed off to that one, but put back the special case #ifdef for MACOSX103 declaring CONST gss_OID_desc, and it builds OK (the other stuff was purely cosmetic, when will I learn?). ckcftp.c, 13 Oct 2009. Protected cvtstring() and related functions with #ifdef NOCSETS..#endif, and ditto for the character-set conversion code in dorename(). ckuus6.c, 13 Oct 2009. Fixed an #endif /* TNCODE */ that was a line too low in ttptycmd(), causing -DNONET builds to fail. ckutio.c, 13 Oct 2009. There was a reference to doputenv() that wasn't guarded by #ifndef NOPUTENV, fixed in ckuus7.c, 13 Oct 2009. Moved doputenv() and settermtyp() out of an #ifdef NOLOCAL section because these are useful even when not making connections. ckuus7.c, 13 Oct 2009. Moved havelfs declaration outside of #ifdef NOXFER because it was also used for other things. ckcmai.c, 13 Oct 2009. COPY /PRESERVE depended on code from the Kermit protocol module, which is omitted in -DNOXFER builds. Disabled COPY /PRESERVE in -DNOXFER builds. ckuus6.c, 14 Oct 2009. SHOW PROTOCOL code for external protocols had to be #ifdef'd out for -DNOPUSH builds. ckuus4.c, 14 Oct 2009. There was some confusion between "No XYZMODEM" and "No external protocols"; cleared up in ckuus3.c, 14 Oct 2009. After all that, 86 different combinations of feature selections built OK on Linux. And the Kerberized version (K5) works OK on Linux for Telnet and FTP. 14 Oct 2009. Changed version number to 9.0. All modules, 16 Oct 2009. Need to make LOG SESSION log to a tty. Right now "log session /dev/ttyKeySerial1" says "Write permission denied" even though the device is crw-rw-rw-. This happens in zchko(), which is called by cmofi(). The problem is that /dev/ is not writeable. I added a Unix-only clause that attempts to open the file for write access using open(), in order to get a file descriptor, which then can be passed to isatty() to check if it's a tty, and if so, to allow access. And then close it. I tested this on Mac OS X as follows: log session /dev/ttyKeySerial1 telnet somehost The Mac's serial port was connected to the serial port of another computer where Kermit displayed the incoming characters in CONNECT mode. Glitches: 1. The port has to be set up as desired in advance, outside of Kermit. 2. log session /dev/ttyKeySerial1 will hang if any required modem signals are not present when the port is opened. 3. Bypasses lockfile mechanism - so we do this only if -DNOUUCP. For (2), I tried setting O_NDELAY / O_NONBLOCK, and this allowed zchko() to continue, but then it freezes in the subsequent fopen(). So I changed zopeno() to also check if the device is a serial port, and if so, to open() it with O_NDELAY / O_NONBLOCK, and then convert the file descriptor into a file pointer with fdopen(). Now for the speaking device that needs lines to be terminated by NUL... set session-log binary <-- need to put these in SHOW LOG set session-log null-padded (and in HELP SET LOG) set line /dev/ttyKeySerial1 This part works. This feature is enabled only for -DNOUUCP builds because serial ports aren't like other Unix files; we would have to create a lockfile, but we can't do that... actually, ttlock() takes a name as an argument, but ttunlck() does not, so there would be no way to remove the lock. Anyway, there is only one API for configuring the port (speed, flow control, etc) and it only works with the SET LINE device, not any random file. To fix this would require massive redesign and changes. ckuus[23].c, ckufio.c, 19-20 Oct 2009. I made -DNOUUCP the default for Mac OS X, since everybody winds up building it that way anyhow. To undo this, do "make macosx KFLAGS=-UNOUUCP". makefile, 21 Oct 2009. Changed SET SESSION-LOG TEXT to strip out ANSI escape sequences; previously there wasn't that much difference between TEXT and BINARY logs. It's still not perfect; for example it doesn't delete characters that the user erased. (Made sure this still builds with -DNOESCSEQ.) ckucns.c, 22 Oct 2009. Changed SHOW LOG to show the SET SESSION-LOG settings, as well as SET DEBUG, which was not shown before. ckuus5.c, 22 Oct 2009. If a series of PUTENV commands is given, each new one undoes the previous one, so only the last definition is seen by the new fork (or by Kermit itself). Turns out you can't feed automatic variables to putenv(); they have to be static, so to allow for multiple PUTENV commands Kermit has to maintain an array of static strings. ckuus7.c, 6 Nov 2009. From Seth Theriault, a better way for the makefile to determine the Mac OS X version number; there's a program for this, sw_ver. makefile, 6 Nov 2009. Peter Eichhorn reported that file-transfer failure hints were not coming out since Dev.27. The only change I made since then was to skip them if the file-transfer protocol was not Kermit. I was using the wrong variable in the tests, 'proto' instead of 'protocol'. ckuus5.c, 6 Nov 2009. Changed Mac OS X targets to correctly extract the Mac OS major version from uname -r in order to choose correctly between utmp and utmpx; this wasn't working in 10.6.1. makefile, 6 Nov 2009. Fix from Seth T. for an oversight in the previous edit. Also add MACOSX103 to "show features" display. makefile, ckuus5.c, 10 Nov 2009. Added REJECT as a synonym for DISCARD in SET FILE COLLISION; it's more intuitive and more accurate. ckuus[27].c, 15 Nov 2009. \fsplit() and \fword() always break on 8-bit characters unless you explicitly put every single 8-bit value into the include set, e.g. (for a TSV file): undef include for \%i 128 255 1 { if == \%i 9 continue .include := \m(include)\fchar(\%i) } .\%n := \fsplit(\m(line),&a,\9,\m(include)) I changed cksplit() to treat all 8-bit bytes 128-255 as non-break characters by default. It might have made more sense to do this for 160-255 (since 128-159 are traditionally C1 control characters) but thanks to Microsoft tradition is out the window. To treat one or more 8-bit characters as break characters, put them in the break set. This might break some scripts, but I doubt it because this flaw was so awful that if anyone had come up against they would have let me know. ckclib.c, 16 Nov 2009. Changed the netbsd target to set -funsigned-char, since cc on NetBSD is actually gcc. makefile, 16 Nov 2009. Changed macosx targets to get the CPU type from the HOSTTYPE environment variable. Also added getenv("HOSTTYPE") as a last-resort method to set the \v(cpu) variable at runtime (maybe it should be the first resort?)... ckuus4.c, makefile, 16 Nov 2009. Made sure the solaris9_64 and solaris10 targets still work. 16 Nov 2009. Made sure the current source package builds OK on HP-UX 10.20... Got a lot of "warning 6062: Optdriver: Exceeding compiler resource limits in xxx; some optimizations skipped. Use +Onolimit if override desired" but it builds OK. Tested long file transfer; works OK. 17 Nov 2009. Built on FreeBSD 7.2 with and without OpenSSL, all OK. 17 Nov 2009. Built on NetBSD 5.0.1 with and without OpenSSL, all OK, but netbsd+krb5 fails with "can't find -lgssapi_krb5"; worked around this with "K5LIB=-L/usr/local/kerblib" (where the lib actually is on this host) but then it failed with "ckcftp.c:13868: error: 'gss_nt_service_name' undeclared". 17 Nov 2009. I found a VMS 6.2 system... Takes a loooong time to build there. In ckuusy.c, DEC C didn't like the prototypes and declarations of dorlgarg() and dotnarg() as static so I made them not static. But that didn't help, now it fails at the very end, saying the final #ifdef is an invalid statement. It looks like an #ifdef mismatch that affects only VMS. I ran my #ifdef matcher, it turned up nothing. I substituted a copy of ckuusy.c from 2007, it comes up with the same errors. Then I substituted the copy from 8.0.211 from 2004, and this one compiled OK and, miraculously, the whole mess even linked OK and runs OK. The Alpha binary is 2.84MB. Now I have 4500 lines of code to compare.... I went through the two files line by line and I can't see a single thing wrong. I gave up and tried building the TCP/IP version. It builds fine except for ckuusy.c, with the utterly useless error message: #endif /* NOCMDL */ ...................^ %CC-E-BADSTMT, Invalid statement. Indicating the last line in the file. Just for the heck of it, I put another line after that one: /* This is a test */ and got: /* This is a test */ ....................^ %CC-E-BADSTMT, Invalid statement. So it is not objecting to anything in the file. Trying the old LISP trick, I put an extraneous closing bracket after that. Success! Honestly, I don't see anything wrong with file. It's DEC C V5.3-006. I suspect a C bug. I'll leave it like this for now until I get access to some other VMS versions. Another clue is that when building the network version I get a horrible warning I never saw before from a module that hasn't been touched in a very long time (ckvrtl.c). Also, in the network version, I note that the FTP code is not compiled in. We have to try this again with some command-line switches, but it'll do for now. ckuusy.c, 18 Nov 2009. ---C-Kermit 9.0 Alpha.01--- From Steven Schweda (SMS), the real solution for the VMS closing brace problem, it wasn't a DECC bug, it was a me bug. ckuusy.c, 20 Nov 2009. Rediscovered the new VMS build options: f for Long Files, i for Internal FTP. "make mnf" doesn't work on VMS 6.2, it looks like the VMS definition for CK_OFF_T got lost. Same thing with "make mfi". Come back to this later. From Gerry Belanger, a fix to INPUT /COUNT:n. ckuus4.c, 26 Nov 2009. Added \fsqueeze(s), returns string s with leading and trailing whitespace removed, Tabs converted to Spaces, and multiple spaces converted to single spaces. For now, ASCII only, no options. ckuusr.h, ckuus[24].c, 27 Nov 2009. I wrote a Kermit script to read a big file of addresses on Solaris 9, \fsqueeze()ing each line. After about 14000 lines, there was a malloc failure in getnct() (the command-file reader). There's nothing wrong with \fsqueeze(), the failure is on a deeper level, because the same thing happens if I use \fupper() (which is structurally identical to \fsqueeze()) in the same script. The problem is not in getnct() either, because every malloc() is freed (I checked). On the other hand, the same script (with \fupper() instead of \fsqueeze() completes OK in C-Kermit 8.0.201. If I remove the function call (\fsqueeze() or \fupper()) from the script, it also runs OK in 9.0. This seems to point the finger at fnevel(), which contains countless malloc's and free's. But comparing fneval() between 8.0.211 and 9.0, I don't see any difference that would explain this behavior -- nothing at all that involves malloc(), makstr(), or free(). Nor any pertinent change in the caller (zzstring) of fneval(). 27 Nov 3009. Another problem is that when this happens, the error is not caught (e.g. by the IF FAIL statement after the command that contains the function call); instead, C-Kermit returns immediately to its prompt. 27 Nov 2009. It could simply be that some of the buffers we allocate are much bigger now. But again, I don't see much difference between 8.0.211 and 9.0; we were already allocating 32K command-related buffers (malloc() takes a size_t, and size_t is an int almost everywhere). I built the same source on NetBSD and ran the same script (with \fqueeze()), and it worked fine. Let's worry about this later, if it comes up. 27 Nov 2009. Built OK on Silicon Graphics IRIX 6.5 R10000; regular build OK, SSL and Kerberos builds failed. 30 Nov 3009. Tried to build on Digital Unix 4.0F but it blew up in ckutio.c, apparently not recognizing any of the terminal struct symbols from termios.h. Tried again with gcc, same thing. Tried explicitly #including within #ifdef TRU64, same thing. What could have changed? 30 Nov 2009. Built OK on Linux RHEL5.4/Itanium-2, make linux. The secure build required "FLAGS=-DNO_KRB5_INIT_ETS" and built OK. 30 Nov 2009. Built OK on Digital Unix 4.0F using "make osf" instead of "make tru64-40f". I don't know why the specific target doesn't work, but it's not worth chasing down. 2 Dec 2009. Built OK on MirBSD 10, despite a lot of gratuitous compiler warnings. Built OK on MirBSD 10, OpenBSD 4.5, and Fedora 10. 3 Dec 2009. (Various other successful Unix builds in these weeks...) Built on VMS 7.2 and 8.3 with and without TCP/IP, no problems. 11 Jan 2010. Built on VMS 8.3 with "make fi" to include the FTP client and long-file support (mid Jan 2010). Built on VMS 8.3 with UXC 5.6 and HP SSL 1.3, which is OpenSSL 0.9.7e. It compiled and linked OK but when I tried to make an FTP SSL connection it crashed in SSL$LIBSSL_SHR, which is called from ssl_auth(), after having had TLS accepted as an authentication type, but before actually authenticating. In Unix: 19. ftp open ftp.somecompany.com /user:pge.com/test_quota /password:xxxxxx Connected to ftp.somecompany.com. 220-Somecompany FTP v6.0 for WinSock ready... 220 Welcome to the online storage FTP server. Please check the main web site for system announcements and AUP. (O) ---> AUTH TLS 234 AUTH command OK. Initializing SSL connection. TLS accepted as authentication type SSL DEBUG ACTIVE =>START SSL/TLS connect on COMMAND In VMS: 19. ftp open ftp.somecompany.com /user:pge.com/test_quota /password:xxxxxx Connected to ftp.somecompany.com. 220 Somecompany FTP v6.0 for WinSock ready... ---> AUTH TLS 234 AUTH command OK. Initializing SSL connection. TLS accepted as authentication type SSL DEBUG ACTIVE %SYSTEM-F-ACCVIO, access violation, reason mask=04, virtual address=FFFFFFFF8001A120, PC=000000000068B118, PS=0000001B Note: The Unix version received the second 220 response, the VMS version did not. That's odd, it's the same code... 25 Jan 2010. Added some essential details to the HELP FSEEK text. ckuus2.c, 25 Jan 2010. Discovered that the result returned by \fsearch() is totally unreliable. This is probably too hard to fix. FSEEK did not pay attention to SET CASE, searches were always case sensitive. Fixed in ckuus7.c, 26 Jan 2010. FSEEK failed to find anything if the search pattern was matched in the first line of the file. Fixed in ckuus7.c, 26 Jan 2010. \fword() and \fsplit().... Another change, but not backwards-incompatible. One may now put the word ALL (just like that, all uppercase) as the include set (4th argument) to indicate that there will be no break characters other than those explicitly given in the break set, e.g. \fsplit(\m(xx),&a,:,ALL) breaks a line only on a colon (:), nothing else. The original rules for cksplit() were more than a little counterintuitive: the default break set is all non alphanums, and the default include set is all alphanums, so if you wanted to parse (say) a CSV file, breaking only on comma, you had to think of all the characters you wanted to keep. This way you just say ALL. ckclib.c, 26 Jan 2010. Speaking of CSV files... How can you put comma as a function argument when comma is the function-argument separator? Use one of these forms: \fsplit(\m(xx),&a,",",ALL) \fsplit(\m(xx),&a,{,},ALL) \fsplit(\m(xx),&a,\44,ALL) \fsplit(\m(xx),&a,\fchar(44),ALL) From John Dunlap, U. of Washington Applied Physics Lab: 'When "stty -a < /dev/ttyS0 | grep crtscts" shows "crtscts" (not "-crtscts") and when using a three wire serial interface and when asking kermit to not use flow control (set flow none) then "ckutio.c1" (see attachments) fails while "ckutio.c" works. The result of "diff -u ckutio.c1 ckutio.c" is attached as "diffs"'. ckutio.c, 26 Jan 2010. Changed the year from 2009 to 2010 in the modules I worked on today and in the heralds, etc. ckckmai.c, ckuus5.c, ckutio.c, ckclib.c, ckuus7.c, 26 Jan 2010. Built on Linux Fedora Core 3, regular and with OpenSSL 0.9.7a. Built on Ubuntu 9.4 OK, but SSL and Kerberos builds failed due to not finding libs and/or header files. I'm sure this could be fixed... 27 Jan 2010. Added SSL, KRB4, and KRB5 to the startup herald for versions that were built with SSL, Kerberos 4, or Kerberos 5. Built OK on Fedora 3 with linux+krb5+ssl and new banner shows correctly. ckuus5.c, 27 Jan 2010. Set NO_KRB5_INIT_ETS by default in ckuath.h since krb5_init_ets() is a no-op in Kerberos 1.4.x and later and in some installations it can't be found, which clobbers the build. ckuath.h, 27 Jan 2010. Adapted to MINIX 3 1.5, the first version that has virtual memory according to Andy T, who should know. On earlier versions (e.g. MINIX 3 1.2) any attempt to build C-Kermit causes the compiler to crash. Now the compiler doesn't crash but it spews out countless warnings about old-fashioned function declarations that I don't get anywhere else. The real problems came in ckutio.c where numerous symbols were undefined at compile time and the POSIX function tcgetpgrp() was not found at link time, even though there is a prototype for it in the MINIX header files, and there is no alternative (since POSIX doesn't let us use ioctl()). Also note that there is some confusion over the compile-time symbols MINIX, MINIX2, MINIX3, and MINIX315. You would expect MINIX to mean "any version of MINIX" but in some parts of ckutio.c it means MINIX 1.0. I sincerely doubt that C-Kermit 9.0 can be built on any version of Minix before 3.1.5 so I removed the confusion and made MINIX mean "any Minix". It builds on 3.1.5 OK now, except for the FTP client. This can probably be fixed but... Modules changed: ckcdeb.h, ckuver.h, ckcmai.c, ckuus5.c, ckutio.c, 1 Feb 2010. Later.. Andy says MINIX does not support job control, so no program is ever in the background. That settles that! 1 Feb 2010. Built OK on Minix, Linux, Mac OS X, Solaris 9, NetBSD 5.0.1... 1 Feb 2010. ---C-Kermit 9.0 Alpha.02--- From Christian Corti at Uni-Stuttgart.de: fixes to allow building on SunOS 4.1, which once was my main development platform but which is long-gone from here. ckupty.c, ckutio.c, 9 Feb 2010. (He says it is also necessary to comment out the "struct winsize" and "struct ttysize" in sys/ioctl.h; otherwise there will be a conflict with sys/ttycom.h (included by termios.h) which also declares these structs. But you need both includes.') From John Dunlap, a fix for Kermit protocol fixed packet-timeout interval going to a unexpected value (missing else clause in two places). ckcfn2.c, 9 Feb 2010. Added an aixg target to build on AIX with gcc when gcc is not installed as cc, and also added CC=$(CC) CC2=$(CC) clauses to the aix and aix+ssl targets. Wow, AIX really loses bigtime when receiving files through its ssh server. Streaming can't be used, sliding windows recover from errors but there are tons of them using the default 4K packets; 500 works much better. Built with IBM cc and gcc, and also tested (successfully) the new aix+ibmssl target, in which the OpenSSL headers and libs are in a standard place. makefile, 9 Feb 2010. In ckupty.h, make the #include be #ifndef SUNOS41. From Christian Corti. 10 Feb 2010. Built on VMS E8.4. 12 Feb 2010. Tried to build on a real VAX-11/785 but the machine seems to be seriously wedged. 12-15 Feb 2010. Added note to CKVKER.COM to the effect the the 'f' option has no effect on VAX architecture. 15 Feb 2010. Moved the #include "ckvrtl.h" in the FTP module to below the include for utime.h, because building the VMS version with the 'i' option (meaning "include internal ftp client") results in "struct utimbuf tp" erroring out because struct utimbuf is not defined yet (at least in some version of VMS with some version of C). From Rob Brown, ckcftp.c, 20 Feb 2010. From Martin Vorlaender: new code in VMS C-Kermit build procedure to detect OpenSSL version automatically. ckvker.com, 22 Feb 2010. Added code to INPUT command to strip ANSI escape sequences. It's activated by SET SESSION-LOG TEXT. ckuusr.h: added prototype for chkaes(); ckucon.c, ckucns.c: made inesc[] and oldesc[] global instead of static; ckuus4.c: doinput() code for skipping escape sequences. 1 Mar 2010. Peter Eichhorn complained that if you make an ssh connection with Kermit, then log out from the ssh host, and then use a "connect" command to make a new connection to the same host (which you can do with Telnet), Kermit says (e.g.): DNS Lookup... Can't get address for ssh -e none somehostname Sorry, can't open ssh -e none somehostname: Error 0 I added code to detect and handle this case and it seems to work OK, even though it's kind of a hack. ckuusr.[ch], ckuus7.c, 1 Mar 2010. There has never been a clean way to put debugging messages (ECHO commands) in a script which are executed only if debugging is desired and ignored otherwise. You'd have to set a random variable and test it, or define a macro or whatever. To make this more straightforward, I added SET DEBUG MESSAGE ON/OFF/STDERR, and added a new MESSAGE (syn: MSG) command for printing debugging messages to stdout if SET DEBUG MESSAGE is ON or to stderr if SET DEBUG MESSAGE is STDERR. ckcmai.c, ckuus[r23].c, 12 Mar 2010. Also for debugging and error messages, I added \v(lastcommmand) so that the command that failed can be included in an IF FAIL or DEBUG error message. This works even for commands that have syntax errors. ckuusr.h, ckuus5.c, ckucmd.c, 12 Mar 2010. From SMS for VMS: 'Added/documented P3 options INTSELECT, OLDFIB, OLDIP. Disabled (commented out) automatic definition of NOSETTIME for VMS before V7.2 (vms_ver .lts. "VMS_V72").' ckcdeb.h, ckcftp.c, ckcnet.c, ckuus[2567].c, ckvfio.c, ckvker.com, ckvrtl.[ch], 15 Mar 2010. Exposed inesc[] and oldesc[] for VMS, so new INPUT command escape-sequence stripping can work (really, chkaes() and related global variables should be moved out of ck[uvd]con.c/ckucns.c and into a common module; do that later). ckuusr.h, ckvcon.c, 15 Mar 2010. Built OK on Solaris9, Mac OS X 10.4.11, RHEL4 (32-bit), RHEL5 (64-bit), AIX 5.3, SCO OpenServr 6.0.0... 15 Mar 2010. Not so good on VMS, turns out I made a typo in one of the VMS updates (#ifndef OLDIP instead of #ifdef...). ckcnet.c, 16 Mar 2010. More from SMS for VMS, 16 Mar 2010: . Set MAXPATH correctly for VMS, ckcdeb.h. . NAM -> NAML, QIO replaces system( "SET PROTECTION"), bugfixes in cvtdir() and nzltor(), ... (See comments): ckvfio.c, new ckvrms.h. (The RMS code in ckvfio.c was almost totally rewritten) . Moved "NAMX$*" (and related) macros to ckvrms.h, and renamed to "NAMX_*" (and similar "$" -> "_"), moved "FIB_*" macros from ckvrtl.c. These changes are mainly to accommodate the ODS5 file system, which has longer and mixed-case filenames, and also to execute certain commands (e.g. for setting file protection, deleting directories) directly instead of using a system() command. Built OK on VMS 8.3 (with and without network support). 16 Mar 2010. Failed to build on VMS 6.2. 16 Mar 2010. FreeBSD 8.0 has a hexdump() prototype that conflicts with the hexdump macro defined in ckcdeb.h. Since the same thing is likely to happen elsewhere, I changed the Kermit macro to ckhexdump as well all references to it: ckcdeb.h, ckcftp.c, ckcnet.c, ckctel.c, ckuath.c, ckutio.c, 16 Mar 2010. Built OK on Digital Unix Tru-64 4.0E using "make osf", 16 Mar 2010. Tried again to build Digital Unix Tru64 4.0E using "make tru64-40e", but something prevents it from picking up the termios symbols and it blows up in ckutio.c, whereas this used to work in earlier C-Kermit versions. This is the only Tru64 system I still have access to, so I can't tell if it's a local peculiarity or what. Note that POSIX is not defined for this build. But if I define it, I get into trouble with "struct timeval". Tried again with "KFLAGS=-DPOSIX -DNOTIMEVAL" but that doesn't help. Tried "make dec-osf" and that worked OK but oddly enough it makes a Kermit with less features than "make osf". 16 Mar 2010. To go with MESSAGE and SET DEBUG MESSAGE, I added IF DEBUG, which is true if SET DEBUG MESSAGE is not OFF and false otherwise. ckuusr.h, ckuus6.c, 16 Mar 2010. From SMS: Corrections to my merging of SMS's changes, ckcftp.c, ckvrtl.h. Builds OK on VMS 6.2 now. Also did an SSL build on VMS 8.3 with OpenSSL m0.9.7e and "OPENSSL_DISABLE_OLD_DES_SUPPORT" was included in P3 automatically by Martin V's addition to ckvker.com. 17 Mar 2010. From SMS: #include earlier for VMS in ckcdeb.h to pick up off_t before it is referenced. This allows C-Kermit to compile on VMS/Alpha 6.2 but linking fails on fseeko() and ftello() (and yet, a functional executable is created, and FSEEK works right). Builds the same way with no problems at all on VMS 8.3 / Alpha. In this case we get the full 64-bit arithmetic... Well, 62 bits: ATLAS::C-Kermit>( ^ 2 63) 9223372036854775000.0 ATLAS::C-Kermit>( ^ 2 62) 4611686018427387904 whereas on VMS 6.2 we get integers only up to (^ 2 30). 17 Mar 2010. Changed the VMS build procedure to enable large file support automatically for non-VAX and VMS 7.3 or greater. No reason not to include this feature. Changed the sense of the F option to DISABLE large file support in the unlikely case that C-Kermit is being built on a suitable platform but the C library is older than VMS73_ACRTL-V0200, in which case fseeko() and ftello() will come up missing at link time. ckvker.com, 18 Mar 2010. Changed VMS build procedure to include the FTP client in any network build by default. Changed the sense of the I option to exclude the FTP client, in case anybody would want to do that. ckvker.com, 18 Mar 2010. From SMS: updated dependencies in CKVKER.COM, fix the "don't reinclude me" clause in CKVRTL.H. 19 Mar 2010. Built OK on VMS 6.2 and 8.3 with and without networking. Large file support included automatically in VMS 8.3 FTP client included automatically in both network builds. 19 Mar 2010. Changed hexdump() to ckhexdump() in ck_crp.c, which I missed before. 19 Mar 2010. ---C-Kermit 9.0 Alpha.03--- In HP-UX with the bundled-non ANSI compiler, we get warnings about functions such as endusershell(), which are declared void in the header files. But in non-ANSI builds we defind VOID to be int rather than void, so our prototypes are wrong. I checked that HP-UX 9, 10, and 11 all have void datatype and changed the definition of VOID to void in those cases. ckcdeb.h, 29 Mar 2010. Fixed a typo in a debug() statement in cksplit() that was causing some warnings. ckclib.c, 29 Mar 2010. Ditto in tls_load_certs(). ck_ssl.c, 29 Mar 2010. "make hpux1000o+ssl" files with: /usr/ccs/bin/ld: Unsatisfied symbols: __umoddi3 (code) __udivdi3 (code) __eprintf (code) It appears that OpenSSL (0.9.7c in this case) requires -lgcc. And indeed hpux1000gcc+ssl builds fine. 29 Mar 2010. There are various warnings in the SSL code in ckutio.c, ckcftp.c, and ckcnet.c about pointers not being assignment compatible, but I have learned from experience not to try to fix these (see notes from 6 Oct 2009). 29 Mar 2010. connect(s, (struct sockaddr *)&hisctladdr, sizeof (hisctladdr)): In FTP, this doesn't work on RHEL5 / Mac OX X 6.1/2 64-bit. But the connect() in Telnet works. On Mac OS X 6.2 I tried changing the socket() call to be like the one in ckcnet.c for Telnet, but it made no difference. On a RHEL5.4 system on i386, FTP works fine, so it's not the Red Hat version. On Digital Unix 4.0E 64-bit, same thing: 11:23:10.722 ftp_hookup[kermit.columbia.edu]=21 11:23:10.722 ftp hookup A[kermit.columbia.edu] 11:23:10.722 ftp hookup C[kermit.columbia.edu] 11:23:10.722 ftp hookup socket=4 11:23:10.722 ftp hookup HADDRLIST 11:23:10.723 ftp hookup connect failed=13 11:23:10.723 ftp hookup bad 13 = Permission denied: [EACCESS] Search permission is denied for a component of the path prefix; or write access to the named socket is denied. On Gentoo Linux, also on Alpha, the errno is 51: Network is unreachable. Clearly some data type in the sockets structs is out of whack. The third connect() argument is "address length". The address is a struct sockaddr. About the third argument, RHEL5 "man connect" says: The third argument of connect() is in reality an int (and this is what 4.x BSD and libc4 and libc5 have). Some POSIX confusion resulted in the present socklen_t, also used by glibc. See also accept(2). Building on RHEL5 on x86_64, where size_t is 8 and socklen_t is 4, I get a warning: ckcftp.c: In function 'ftp_hookup': ckcftp.c:14667: warning: comparison is always true due to limited range of data Referring to: if (hisctladdr.sin_addr.s_addr != (unsigned long) -1) This seems to be the problem; if I remove the (unsigned long) cast (in two places), the problem goes away. Actually what I should be comparing it with is INADDR_NONE, which is defined appropriately in some header file, e.g. as 0xffffffff. Also I define it explicitly as -1 if it is not defined in any header file (as is the case in Solaris 9). Tested OK on 64-bit RHEL5, 32-bit RHEL5, Digital Unix 4.0E 64-bit, Solaris 9 32-bit, Mac OS X 10.4.11 32-bit, Mac OS X 10.6.3 64-bit, AIX 5.3, Gentoo Linux 2.6.31 on Alpha 64-bit, NetBSD 5.0.1 32-bit.... ckcftp.c, 29 Mar 2010. ---C-Kermit 9.0 Alpha.04--- Yesterday's VOID redefinition caused problems for HP-UX in ckuusx.c, in the curses section where VOID is undef'd and not used to avoid a conflict with curses.h. As a workaround I defined a new macro CKVOID with the same definition as VOID and used it in the offending section of ckuusx. The real solution is to replace all references to VOID with CKVOID (since VOID is increasingly likely to cause conflicts), but a mass search and replace is not without risks. ckcdeb.h, ckuusx.c, 30 Mar 2010. Changed VOID and CKVOID definition to be 'void' for all HP-UX (verified by PeterE back to HP-UX 6.5, 1989). Still need to check this on HP-UX 5.21; if that's an exception it can be done in the makefile. ckcdeb.h, 30 Mar 2010. The change I made to allow CONNECT to reestablish a previous SSH connection prevented a new SSH connection to a different host to be made. Fixed in ckuus7.c, 30 Mar 2010. Fixed mistaken extern declarations of krb4_errno and krb5_errno as strings in nvlook(); they are ints. Built OK on Mac OS X 10.6.3. ckuus4.c, 30 Mar 2010. A fix to Trusted HP-UX makefile target from PeterE, to account for the equivalence of +openssl and +ssl as target suffixes. 30 Mar 2010. Added a new function \fcvtcsets(string,cset1,cset1) that converts a string from one character set to another. The csets are File Character-Set names. ckuus4.c, 31 Mar 2010. Added a new function \fdecodehex(string,prefix) that decodes a string containing prefixed hex bytes. Default prefix is %%, but any prefix of one of two chars (such as % or 0x) can be specified. ckuusr.h, ckclib.h, ckclib.c, ckuusr.c, 31 Mar 2010. Richard Nolde reports that Kermit can't find -lpam on Fedora 12 because it's in /lib rather than /usr/lib. RHEL5 has symlinks, FC12 should too. Added a note to the makefile. 1 Apr 2010. Build on Solaris 11 for the first time. Had to adjust ckuver.h to get the version herald right. This was on a box that reported its architecture as i86pc. 1 Apr 2010. Added MIME character-set names as invisible synonyms in the file and terminal character-set tables, fcstab[] and tcstab[]. Note that not all the character sets known to Kermit are registered in MIME. But at least now MIME-registered character sets can be referred to by their MIME names, e.g. ISO-8859-1, ISO646-ES, IBM437, WINDOWS-1252. These are not listed if you type ? in a field that is parsing them, unless you type a letter first, e.g. "i?" lists ISO- and IBM set names. Later maybe I'll make parallel tables, or keyword attribute bit that says whether a name is MIME or not. The real benefit of this change is that now Kermit can take its character-set names from external sources like email headers or web logs. ckuxla.c, 1 Apr 2010. Changed the IF command to accept a bare macro name its condition. This will parse and execute correctly if the macro is defined and if it has a numeric value, or if it is not defined, in which case it evaluates to 0 (FALSE). If it is defined but has a non-numeric value, a parse error occurs. ckuus6.c, 2 Apr 2010. Added \fstringtype() function. Given a string argument, it tells whether the string is 7bit, 8bit, utf8, binary, etc. ckuusr.h, ckuus[4x].c, 2 Apr 2010. Did a few builds to make sure there were no booboos. Solaris 9, NetBSD 5.01, Linux RHEL4, HP-UX 10.20 (non-ANSI compiler and ANSI optimizing compiler), Mac OS X 10.4.11, SCO OSR 6.00. 5 Apr 2010. ---C-Kermit 9.0 Alpha.05--- Increased maximum variable name length from 4K to 16K. Verified that too-long names are caught and recovered from correctly. ckuusr.h, 6 Apr 2010. Implemented a new \fsplit() option for parsing CSV files, which turns out to be a little complicated, because the separator is not just a comma, but a comma and all its surrounding spaces. Also there are special quoting rules for fields with embedded commas and fields with embedded quotes. ckclib.c, 7 Apr 2010. ---C-Kermit 9.0 Alpha.06--- VMS changes from SMS. They build OK, Kermit file transfers are still OK, but FTP text-mode GETs always hang on the 10th 8K network read. Couldn't get a debug log this time. ckcmai.c, ckvfio.c, ckvrms.h, ckvker.com. 8 Apr 2010. Changing VNAML from 4K to 16K broke the build on HP-UX 9. Put it back to 4K. 9 Apr 2010. John Dunlap, running days-long stress tests between E-Kermit and C-Kermit, found a bug in the packet-reading and -decoding code: If a NAK packet arrives with its length field corrupted to indicate a bigger size, and there are enough bytes following in the pipeline, ttinl() will return a too-long packet (if there are not enough bytes waiting to be read, then ttinl() will properly time out). In the bad case rpack() trusts the packet length, uses it as the basis for computation of the block-check length, which is then used to access memory that might not be there, causing (at least on John's Linux system) a segmentation fault. John added the normal clause to check the result of the block-check calculation, and I changed ttinl() to always break on the eol character (normally carriage return), since this can never appear in a packet, even if we "set control unprefix all". Also added a check to ttinl() to protect against length fields corrupted into illegal values. ckcfn2.c, ckutio.c, 13 Apr 2010. From Lewis McCarthy: Based on code inspection, C-Kermit appears to have an SSL-related security vulnerability analogous to that identified as CVE-2009-3767 (see e.g. http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2009-3767). I'm attaching a patch for this issue relative to the revision of ck_ssl.c obtained from a copy of http://www.columbia.edu/kermit/ftp/test/tar/x.zip downloaded on 2010/07/30, which I believe is the latest. When this flaw was first widely publicized at last year's Black Hat conference, it was claimed that some public certificate authorities had indeed issued certificates that could be used to exploit this class of vulnerability. As far as I know they have not revealed specifically which public CA(s) had been found issuing such certificates. Some references: http://www.mseclab.com/?p=180 http://www.theregister.co.uk/2009/07/30/universal_ssl_certificate/ Patches added to ck_ssl.c, 4 Aug 2010. Peter Eichhorn reported that "RENAME ../x ." didn't work. This is a side effect of the changes of 2006 to the RENAME command, there was a little confusion in the renameone() routine; fixed in ckuus6.c, 4 Aug 2010. If only one file is FOPEN'd, FCLOSE given with no arguments would close it. Turns out to be a bad idea. Example: program with an input and output file, try to close the output file before it is opened by just typing FCLOSE; this can mess up the input file. For safety FCLOSE has to require a channel number or ALL. ckuus7.c, 4 Aug 2010. Added \fstrcmp(s1,s2,case,start,length), which has the advantage over IF EQU,LGT,LLT that case sensitivity can be specified as a function arg, and also substrings can be specified. ckuusr.h, ckuus[24].c, 5 Aug 2010. The CSV feature of Alpha.06 had a subtle flaw, namely that if the last item in a comma separated list was enclosed within doublequotes with a trailing space after the closing doublequote, a spurious empty final element would be created in the result array. Fixed in cksplit(), ckclib.c, 5 Aug 2010. ---Alpha.07--- The CSV feature of \fsplit() splits a comma-separated list into an array. To turn the array back into a comma separated list, \fjoin(&a,\44,1) almost works, except for elements contain literal doublequotes, such as: Mohammad "The Greatest" Ali This calls for making a symbolic CSV argument for \fjoin() like the one that was made for \fsplit(): \fjoin(&a,CSV). Also \fjoin(&a,TSV) for Tab-separated list. Thus if Kermit reads a record in CSV format, splits it into an array, and then joins the array back into a CSV record, the result will be equivalent to the original, according to the CSV definition. It might not be identical, because if the result had extraneous spaces before or after the separating commas, these are discarded, but that does not affect the elements themselves. Furthermore it is now possible to convert a comma-separated list into a tab-separated list, and vice versa (which is not a simple matter of changing commas to tabs or vice versa). ckuus4.c, 12 Aug 2010. From Joop Boonen 26 Juli 2010: "Added HAVE_LOCKDEV as openSuSE >= 11.3 uses lockdev but not baudboy. They use ttylock directly. The program code has been added so the the program works without a problem." makefile, ckcdeb.h, ckutio.c, ckuus5.c, 23 Aug 2010. ---Alpha.08--- From Gary Mills at the U of Manitoba: convert Solaris version from BSD ptys to streams ptys because there are only 48 BSD-style ptys and he was running out. No code changes needed, the only change necessary was to add the following flags to the makefile target: -DHAVE_STREAMS -DHAVE_GRANTPT -DHAVE_PTSNAME -DPUSH_PTEM -DPUSH_LDTERM -DPUSH_TTCOMPAT makefile, ckcmai.c, 21 Sep 2010. Testing this in Solaris 9 I see that the DES library disappeared. Added code to the solaris9 targets (also used by Solaris 10 and 11) to check for this. makefile, 21 Sep 2010. The Solaris target checked the OpenSSL version automatically to set the right flag, the Linux target didn't. Put the OpenSSL-version testing code in the Linux target too. makefile, 21 Sep 2010. A couple minor changes to the tru64-51b makefile targets from Steven Schweda but there still are some problems with the Tru64 Unix builds. makefile, 21 Sep 2010. ---Alpha.09--- \fcontents(\&a[3]) got an error if the array was declared but its dimension was less than 3, which is bad when dealing with (say) an array created dynamically by \fsplit(), which might or might not have a third element. In case it doesn't -- i.e. in case we are referring to an out of range element of any array that is declared -- we should just return a null string, as we do with other types of variables that are not defined. For that matter, ditto even if the array is not declared; what useful purpose is served by throwing an error in this case? ckuus4.c, 30 Dec 2010. cksplit() treats \ as a quoting character. If the source string contains backslashes, they are swallowed (or, if doubled, one is kept). That's not good for parsing external data, such as lines read from files, where there are no quoting rules. This came up when parsing CSV files; as a workaround, I made \fsplit() treat backslash as an ordinary character for CSV and TSV splitting (a better solution might be yet another argument that specifies a quote character). ckclib.c, 30 Dec 2010. Began converting C-Kermit to Open Source with the Simplified 3-Clause BSD license. Updated the text for the INTRO, LICENSE, NEWS, and SUPPORT commands. Fixed things so the copyright year to be displayed is defined in one place (ck_cryear in ckcmai.c), rather than hardwired into text strings all over the place. COPYING.TXT, ckcmai.c, ckuus[256].c, 2 Jun 2011. When I added MIME synonyms for Kermit character-set names, I left a bogus entry in the tables ("windows-1251") that was in the wrong place alphabetically, thus preventing most references to file character-set names from working right. Removed the bogus entry. ckuxla.c, 2 Jun 2011. Most combinations work OK, but not translating Cyrillic text from UTF-8 to Latin/Cyrillic, and probably the same would be true for any case of converting from UTF-8 or UCS-2 to anything else. The problem was in xgnbyte(), which converts the input stream from the specified character to UCS2; it needed to make a special case for when the input file was already Unicode. Believe it or not, this problem occurred at least as far back as 8.0.201 (9.5 years ago) and nobody noticed. So if the fix isn't perfect probably nobody will notice that either. ckcfns.c, 3 Jun 2011. The SET BLOCK CHECK command did not parse all the items in its keyword list. Fixed in ckuus3.c, 3 Jun 2011. For EM-APEX ocean floats project, where buoys in stormy waters have to transmit data through an earth satellite using non-error-correcting modems, John Dunlap ran exhaustive stress tests of Kermit protocol transfers through a simulated connection that injected errors and delays and identified a weakness in Kermit protocol when it is used under extremely bad conditions: If a data byte of the S packet (or its Ack) is corrupted and the 1-byte checksum is also corrupted in such a way that that the checksum matches the corrupted data, the two Kermit programs will disagree as to the negotiated parameters. For example, if file Sender's RPT field is changed from '~' to '^', the receiver will decode the packet incorrectly. Ditto for most of the other parameters. The result is that a corrupted file is received but reported correct. John suggested a new mode of operation in which the Type 3 block check is used for all packets. Such a mode can not be negotiated because the negotiation packet itself is assumed by all Kermit programs to have a 1-byte checksum. Added SET BLOCK-CHECK 5 to the parser (with invisible synonym FORCE-3". ckuus3.c, 3 Jun 2011. Added supporting code for SET BLOCK 5: ckcfn[23].c, ckcpro.w, ckcmai.c, ckuus3.c, 3 Jun 2011. Added code to skip the heuristic that S and I packets always have block check type 1. File transfer OK between two C-Kermits with SET BLOCK 5. rpack(): ckcfn2.c, 5 Jun 2011. Made the file receiver put "5" in the block-check-type in its ACK to the S-Packet. spar(): ckcfns.c, 5 Jun 2011. Now the question is: Can we make the file receiver automatically and safely recognize a three-byte block check on an incoming S or I packet? It's tricky because the block check field is not self-identified, it's just the last "n" characters of string indicated by the length field, so correct decoding of the packet depends on stateful knowledge of "n". How about this: rpack() already knows what type of packet it is, so if it's an S or I packet and the 8th byte of the data field is "5" and last 3 bytes, when interpreted as the CRC, match the packet contents, then we accept the packet and switch to BLOCK 5 mode. On the other hand, if the "5" was put there by corruption, the CRC should catch the error. In that case we NAK the packet and presumably get a different version back. There would be no reason to try to re-read the same packet with a different block check, because the "5" could not possibly be there legitimately unless it had a 3-byte CRC. To be clear, this is cheating. We read the packet contents before we know the packet is correct, then we check that it *is* correct. I made the 4-line change to rpack() and it works OK in the absence of transmission errors. ckcfn2.c, 3 Jun 2011. So the various combinations should work as desired: . Sender and receiver both support and are told to SET BLOCK 5 ("SB5"). . Sender SB5, but receiver doesn't support it (errors out). . Sender SB5, receiver supports it but wasn't told (auto-recognizes it). . Receiver SB5 but sender no (errors out). Note in the last case, the receiver should NOT automatically fall back to standard behavior because if the user said SET BLOCK 5 that means every packet MUST be protected by CRC to prevent the I/S packets from being corrupted. Installed new HELP SET BLOCK-CHECK text. ckuus2.c, 5 Jun 2011. Autodownload didn't work when the S or I packet had a 3-byte block check because kstart() checked it for a 1-byte checksum. Fixed in kstart(), ckcfn2.c, 6 Jun 2011. However, older Kermit versions and programs that claim to do "autodownload" will never recognize this type of packet. No big deal since even if they did, the transfer would fail anyway. Added 'FORCE 3' to E-Kermit, called it EK 1.7. The option is "-b 5". Works OK for sending and receiving, both with and without the new option. Also works with "-b 5" if you send an S packet to it with '5' in the BCT field. Changes were minimal, I have them all in ek17.diff. I could probably also make a new G-Kermit in about 10 minutes, but who cares about G-Kermit... We already have two useful Kermit programs that interoperate with the new protocol. 6 Jun 2011. Replaced the very inadequate help texts for functions \fword() and \fsplit() with new ones. ckuus2.c, 6 Jun 2011. There were a couple reports of file corruption that I was saving for later. Now that now is later I dug up the messages, files, and logs and it turns out that nobody had reported a reproducible case of Kermit corrupting a file. There have been non-reproducible cases though, almost certainly due to corruption of the S or I packet or its ACK, which is why we now have SET BLOCK 5. Even with BLOCK CHECK 5, there is no guarantee that the same thing won't happen, it is just far less likely. Even if we added a 32-bit CRC or even 64-bit one, there would still be a small chance it could happen. 7 Jun 2011: Corrected various #ifdefs (or lack of them) when building C-Kermit with different combinations of feature-selection options such as NOCSETS, NOICP, NOLOCAL, NOSPL, NOUNICODE, etc. ckcfns.c ckcmai.c ckcxla.h ckuus2.c ckuus4.c ckuus5.c ckuus6.c ckuusr.c, 7 Jun 2011. After running the script that does all these builds (84 of them) I ran it again to make sure that none of the changes broke builds that succeeded before the changes were made. Built OK on Solaris9 ("make solaris9") Ditto with Krb5 and OpenSSL 0.9.8q ("make solaris9g+openssl+shadow+pam+zlib") Built OK on Mac OS X 10.4.11 ("make macosx"). Also "make macosx+krb5+openssl. Built OK on Linux RHEL4 ("make linux"). Built OK on Linux RHEL4 with OpenSSL 0.9.7a ("make linux+ssl"). Built OK on Linux RHEL5 ("make linux"). "make linux+ssl" fails on RHEL5 because of DES, even though the target tests for the presence or absence of the DES libraries. In this case the libraries are there but they lack the functions des_ecb3_encrypt, des_random_seed, and des_set_odd_parity. The build succeeds as: make linux+ssl KFLAGS=-UCK_SSL Since DES is now considered harmful, Jeff Altman suggests that all OpenSSL builds, even for old versions, should omit it ("If you are building with openssl and no kerberos or srp, just disable DES. Disabling DES will impact telnet and rlogin but it won't matter if you have no ability to negotiate a session key"). From Ian Beckwith, patches for Debian Linux: . Change all '-' to '\(hy' in man page (new pedantry): ckuker.nr. . Make IKSD authentication (using PAM) ask for a password when an invalid username has been given, to avoid disclosing which account names are valid: ckufio.c, ckuus7.c. . Fix spelling errors: ckcftp.c, ckuus2.c, ckuker.nr, ckcpro.w, ckuusr.h. . Patch makefile to support install to a staging area with DESTDIR. . Some other patches (mainly for typos) were for plain-text documentation files that were generated from Web pages; I updated the web pages. A big corporate C-Kermit user has an application where a local C-Kermit makes a connection to a remote one, uploads some files, and then if the server has any new patch files for the local, it sends the patches and does a REMOTE HOST command to run the patch program. This stopped working in C-Kermit 6.0 or 7.0 when I put a check to prevent it, because "it makes no sense to send REMOTE commands to the local end, because the results are sent back to the remote to be displayed on its screen but it has no screen". That may be true, but if the user needs to control the local from the remote, they should be able to. I removed the checks. This doesn't solve the problem of where the output goes; ideally it would go to the local screen but I don't see any elegant and simple way to make that change. However the output redirectors can still be used with the REMOTE command so the results can be captured to a remote file, which could then be sent. ckuus7.c, 7 Jun 2011. Changed SET VARIABLE-EVALUATION to SET COMMAND VARIABLE-EVALUATION, but left the former version available. ckuusr.c, 9 Jun 2011. Documented the SET COMMAND VARIABLE-EVALUATION command, which I added in 2008. ck90.html, 9 Jun 2011. Renamed all old Mac OS X makefile targets to have the prefix "old" to avoid confusing them with the current targets, and made macosx10 a synonym for macosx, so those who used previous makefiles will get a current target without having to know the new name. makefile, 9 Jun 2011. Added XMESSAGE, which is to MESSAGE as XECHO is ECHO: prints the text without a line terminator, so it can be continued by subsequent [X]MESSAGE commands. ckuusr.[ch], 9 Jun 2011. Back to "make linux+ssl" on RHEL5... I took the coward's way out and added code to the makefile target to check whether the build worked (somebody let me know if there is a better way to check), and if not to give a message suggesting they "make clean ; make linux+ssl KFLAGS=-UCK_DES". makefile, 9 Jun 2011. Noticed that \frecurse() would dump core if called with no arguments. Fixed in ckuus4.c, 9 Jun 2011. Added \q() as an alternative to the more verbose \fliteral() for quoting strings that contain characters (like \) that would otherwise be significant to Kermit. It's more efficient because it isn't a function call, and 'q' is an intuitive letter to mean 'quote'. It also works better than \fliteral() because functions treat commas and braces specially. ckuus4.c, 10 Jun 2011. Built OK on VMS 8.3 on Alpha, no net. DEC C caught a couple glitches in the new code that gcc didn't catch, which I fixed. ckuus[25].c, 10 Jun 2011. Built OK on VMS 8.3 on Alpha with Multinet 5.3. The SSL build failed but I'm not going to worry about it. 10 Jun 2011. Built OK on NetBSD 5.1. 10 Jun 2011. Tried to resurrect my old "build-all" machine, an IBM Netfinity 3500 from 1997 with 20-some mountable bootable hard disks with lots of 1990s OS's on them. No dice. I can see the BIOS but not the hard disks. The configuration is still correct because it tries to boot from the mountable hard disk, but it fails (I tried six different ones). Tried to resurrect my old Siemens Nixdorf RM 200 MIPS machine. Booted OK, headless even, but makes a hellish high-pitched whine, like a dentist drill. It's pretty slow too. "make sinix542" (for SINIX 5.4.2) bombed at link time on no rdchk(). Fixed by #including . ckutio.c, 10 Jun 2011. Tried to resurrect my old SCO Xenix 2.3.4 machine, also headless. Amazingly it still works; it can't use a monitor but I can Telnet to it. Had to tweak some #ifdefs but I got a no-net version built successfully. According to my notes, it hasn't been possible to build with TCP/IP since C-Kermit 8.0, but how many people ever had SCO Xenix 2.3.4 with TCP/IP anyway? Anyway we still have the binaries for C-Kermit 7.0. ckuus4.c, 10 Jun 2011. Built OK on AIX 5.3. Built OK on Solaris 10. 11 Jun 2011. Tried harder to revive the build-all machine, now it sort of works, but not all of the bootable OS's work. Built C-Kermit 9.0 OK on OpenBSD 3.0. Built OK on QNX 4.25 but had to #ifdef references to IXANY in ckutio.c and ckupty. Built OK on NetBSD 1.5.1 (2000). Tried "make netbsd+ssl" on this one, it's OpenSSL 0.9.5a 1 Apr 2000, but it bombs out in ckuath.c, no big deal. Another problem in NetBSD 1.5.2 is that even though off_t is 8, CK_OFF_T is 4. Worth noting but not worth fixing unless someone else notices. 13 Jun 2011. SuSE 7.0... boots OK but telnet server doesn't work. Can telnet out but it's too flaky, connection drops if I try to transfer a file. OpenBSD 2.5 [1999] OK. Red Hat 7.1 OK. Red Hat 7.1 with OpenSSL 0.9.6 not OK, same error as with 0.9.5a: ckuath.c In file included from ck_ssl.h:48, from ckuath.c:225: /usr/include/openssl/des.h:77: warning: redefinition of `Block' ckuat2.h:86: warning: `Block' previously declared here /usr/include/openssl/des.h:83: redefinition of `struct des_ks_struct' /usr/include/openssl/des.h:91: warning: redefinition of `Schedule' ckuat2.h:90: warning: `Schedule' previously declared here So it appears that OpenSSL support is broken for pre-0.9.7. Tried building it again with -UCK_SSL (since the errors are originating from from des.h)... But it still failed exactly the same way. I found #includes for des.h in ckuath.c and and ck_ssl.h and #ifdef'd them out, but it still fails: In file included from /usr/include/openssl/evp.h:89, from /usr/include/openssl/x509.h:67, from /usr/include/openssl/ssl.h:69, from ck_ssl.h:51, from ckuath.c:227: /usr/include/openssl/des.h:77: warning: redefinition of `Block' ckuat2.h:86: warning: `Block' previously declared here /usr/include/openssl/des.h:83: redefinition of `struct des_ks_struct' /usr/include/openssl/des.h:91: warning: redefinition of `Schedule' ckuat2.h:90: warning: `Schedule' previously declared here Built OK on Debian 2.1. 13 Jun 2011. On FreeBSD 4.4, it blows up with: ckufio.c: In function vpass': ckufio.c:8201: conflicting types for 'initgroups' /usr/include/unistd.h:154: previous declaration of 'initgroups' ckufio.c:8201: warning: extern declaration of 'initgroups' doesn't match global one. Fixed by defining NODCLINITGROUPS for FreeBSD in ckufio.c. It might not be the right fix, but I don't have a lot of other FreeBSD versions to compare with. Anyway now it builds OK on 4.4, and also on FreeBSD 3.3. ckufio.c, 13 Jun 2011. Tried to build on SCO Open Server 5.0.7 but it fails at link time because it can't find rdchk(). But it's supposed to be there! Come back to this later... Red Hat 6.1 i386 32/64 linux 2332545 Red Hat 7.1 i386 32/64 linux 2368528 Red Hat EL4 i386 32/74 linux 2363067 Red Hat EL5.6 i386 64 linux 2371279 Solaris9 sparc 32/64 solaris9 2849896 Solaris9+ssl sparc 32/64 solaris9 5021764 Solaris10 sparc 32/64 solaris10 2855776 QNX i386 32 qnx32 2012323 NetBSD 1.5.1 i386 32/64 netbsd 2198055 NetBSD 5.1 i386 32/64 netbsd 2159863 OpenBSD 2.5 i386 32/64 openbsd 2236036 Mac OS X 10.6.7 x86_64 64 macosx 2.7M Mac OS X 10.4.11 ppc 32/64 macosx 2496304 Debian 2.1 i386 32/64 linux 2213221 FreeBSD 4.4 i386 32/64 freebsd 2291333 FreeBSD 3.3 i386 32/64 freebsd 2147370 SINIX 5.42 mips 32 sinix542 3319325 (1995) SCO Unixware 2.1.3 i386 32 uw213 2242176 SCO OSR6.0.0 i386 32/64 sco_osr600 2368300 More builds, 14 June 2011: VMS 6.2 alpha 32 make mn 2556928 No TCP/IP VMS 6.2 alpha 32 make m 3112960 UCX 4.0 Solaris 11 i386 32/64 solaris11 2823860 Solaris 11 i386 32/64 solaris11+ssl 2993660 OpenSSL 0.9.8l NetBSD 5.1 i386 32/64 netbsd+krb5 2307855 Kerberos 5 Linux Slackware 12.1.0 i386 32/65 linux 2175754 Linux Fedora 14 i386 32/64 linux 2256514 Linux Fedora 14 i386 32/64 linux+ssl ....... OpenSSL 1.0.0d Linux Fedora 14 i386 32/64 linux+krb 2449614 (*) (*) make linux+krb5 "LIBS=$LIBS /lib/libk5crypto.so.3 /lib/libcom_err.so.2" Noticed that netbsd+ssl build on NetBSD 5.1 said "NetBSD 1.5" in its banner. Fixed by replacing the old hardwired target with the new "subroutinized" target a'la linux+ssl and adapting it to NetBSD. makefile, 15 Jun 2011. Same deal for Kerberos 5, make a new netbsd+krb5 target and it builds ok, at least once one figures out where the Kerberos headers and libs are. makefile, 15 Jun 2011. Same deal for the netbsdnc target, now it simply defined NOCURSES and chains to the main netbsd target. makefile, 15 Jun 2011. Tried to build with Kerberos 5 on Solaris, fails because the DES library no longer exists. This one is beyond me, sorry. Made new targets for MirBSD, mirbsd and mirbsd+ssl, makefile 15 Jun 2011. In OpenSUSE 11.2 with OpenSSL 0.9.8r we bomb on undefined references from various DES library routines. Builds OK without DES. Various linux+krb5 builds fail because can't find -lgssapi_krb5 SSL builds with OpenSSL < 0.9.7 fail even though there is code to support the older SSL. Fixed some printf %ld vs int instances in the sizeofs section of SHOW FEATURES. ckuus5.c, 15 Jun 2011. Fixed the new linux+ssl target to actually use the SSLINC and SSLLIBS definitions, oops. makefile, 15 Jun 2011. 15 June 2011 builds (Beta.01): AIX 5.3 ppc 32/64 aix+ssl 3283846 OpenSSL 0.9.8m NetBSD 5.1 i386 32/64 netbsd 2159863 NetBSD 5.1 i386 32/64 netbsd+ssl 2350274 OpenSSL 0.9.9-dev NetBSD 5.1 i386 32/64 netbsd+krb5 2349627 MIT Krb5 1.6.3 FreeBSD 8.2 i386 32/64 freebsd 2298414 FreeBSD 8.2 i386 32/64 freebsd+ssl 2448961 OpenSSL 0.9.8q OpenBSD 4.7 i386 32/64 openbsd 2266132 OpenBSD 4.7 i386 32/64 openbsd+ssl 2409263 OpenSSL 0.9.8k MirBSD 10 i386 32/64 mirbsd 2216601 MirBSD 10 i386 32/64 mirbsd+ssl 2358318 OpenSSL 0.9.8r OpenSuse 11.2 x86_64 64 linux 2348468 OpenSuse 11.2 x86_64 64 linux+ssl (*) 2546540 OpenSSL 0.9.8r RHEL 5.6 ia64 64 linux 4390687 RHEL 5.6 ia64 64 linux+ssl (*) 4775007 OpenSSL 0.9.8e Ubuntu 9.10 i386 32/64 linux 2275523 Ubuntu 9.10 i386 32/64 linux+ssl 2466708 OpenSSL 0.9.8r Gentoo 1.12.13 ppc 32/64 linux 2386597 Gentoo 1.12.13 ppc64 64 linux 2749015 Gentoo 1.12.13 ppc64 64 linux+ssl 3002150 OpenSSL 0.9.8r Gentoo 1.12.13 sparc 32/64 linux 2478382 Gentoo 1.12.13 sparc 32/64 linux+ssl 2690499 OpenSSL 0.9.8r Solaris 9 sparc 32/64 solaris9 2849896 Solaris 10 i386 32/64 solaris10 2837620 IRIX 6.5 R10000 32/64 irix65 2869704 * and KFLAGS=-UCK_DES Tried building on NetBSD 5.1 with Heimdal Kerberos using: make netbsd+krb5 \ "KFLAGS=-DHEIMDAL" \ "K5INC=-I/usr/include" \ "K5LIB=-L/usr/lib" It found all its headers OK, but it blew up in ckuath.c. Small wonder, ckccfg.html says: HEIMDAL Should be defined if Kerberos V support is provided by HEIMDAL. Support for this option is not complete in C-Kermit 8.0. Anyone interested in working on this should contact kermit-support. 'krb5-config --version' gives the MIT Kerberos 5 version number. Make a new netbsd+krb5+ssl target based on the combination of the new netbsd+ssl and netbsd+krb5 targets. There were lots of warnings in the compilation but no errors, but it produced an executable that starts and does normal things but I have no idea if the SSL or Kerberos functions work. makefile, 16 Jun 2011. Changed the cu-solaris9-krb5 target to test for the presence of DES because DES isn't there, to see if this would allow a Kerberos build to proceed. And it worked, amazing. At least the build completed, I have no way to test the Kerberos part. makefile, 16 Jun 2011. Updated the solaris9+ssl target to do the DES testing. makefile, 16 Jun 2011. Updated cu-solaris+krb5 target to test whether the GSSAPI library is called libgassapi or libgassapi_krb5. makefile, 16 Jun 2011. Added lots of tests to the Linux Kerberos 5 entries, linux+krb5 and linux+krb5+ssl, because some have libk5crypto and some don't; some have libcom_err and some don't; and some have libgssapi_krb5 (e.g. RHEL5, OpenSuse 11.2) whereas others have libgssapi (Gentoo). 16 June 2011 builds (Beta.01): NetBSD 5.1 i386 32/64 netbsd+krb5+ssl 2451757 OpenSSL 0.9.9 MIT Krb5 1.6.3 Solaris 9 sparc 32/64 solaris9+krb5 2543036 MIT Kerberos 5 1.7.1 Solaris 9 sparc 32/64 solaris9+ssl 5021544 OpenSSL 0.9.8q (gcc) Gentoo... ppc 32/64 linux 2386597 Gentoo... ppc 32/64 linux+ssl 2593561 OpenSSL 0.9.8r Gentoo... ppc64 64 linux 2749015 Gentoo... ppc64 64 linux+ssl 3002150 OpenSSL 0.9.8r RHEL5 x86_64 64 linux+krb5 (*) 2563878 MIT Kerberos 5 1.6.1 RHEL5 x86_64 64 linux+krb5+ssl(*) 2563878 MIT Kerberos 5 1.6.1 Fedora 14 i386 32/64 linux+krb5+ssl 2539891 MIT Krb5 + OpenSSL 0.9.8r * KFLAGS=-UCK_DES --- C-Kermit 9.0.299 Beta.01 --- sizeof() can return a long or an int, so neither printf("%d",sizeof(blah)); or printf("%ld",sizeof(blah)); can be used everywhere. Changed the "sizeofs" section of SHOW FEATURES in the dumbest (and therefore most portable) way to squelch the warnings. ckuus5.c, 17 Jun 2011. From John Dunlap: "Watching the server screen led me to offer a cosmetic patch for ckuusx.c. I noticed that the server screen said it was "RESENDING" when it really wasn't. The attached patch emits blanks to insure that old labels are completely erased." ckuusx.c, 17 Jun 2011. Nelson Beebe found two places where I had SSLLIBS in the makefile instead of SSLLIB. makefile, 18 Jun 2011. More important he knew how to force gcc to load the right header files for OpenSSL 1.0.0d (by using '-isystem' rather than '-I'). Previously it was using the 0.9.8r header files but linking with the 1.0.0d libraries. This is not in the sources or makefile; it's done when giving the 'make' command: export PATH=/usr/bin:$PATH export SSLINC=-isystem/usr/include export "SSLLIB=-L/usr/lib -Wl,-rpath,/usr/lib" make linux+ssl Folded the previous linux+openssl+zlib+shadow+pam and linux+openssl+shadow targets into linux+ssl. Checked the linuxso (scripting only) target, builds OK, 600K. Made new subroutinized linux+krb5+krb4 target but can't find anyplace to test it. Made new subroutinized linux+shadow+pam target, works fine on RHEL4. Revised comments and lists again. makefile, 18 Jun 2011. For the pluggable-disk OS's that boot OK but lack a working network, I rigged up a serial connection using a DB9-FF null modem cable, and then a DB9-MF modem cable to make it reach. I don't see any modem signals on either end, but the data goes through OK. COM1 on the desktop PC, /dev/ttyS1 or whatever on Lab. Since there are no modem signals, can't use RTS/CTS. At 57600bps with Xon/Xoff, 500-byte packets and sliding windows, transfers work OK at about 5000cps using 5 window slots; takes 8 minutes to transfer the gzipped C-Kermit tarball. Kermit to the rescue. 19 Jun 2011. Transferred the tarball over serial ports to SCO OSR5.0.5 at 38.4Kbps, the highest speed supported, 12 minutes, no errors, 3300cps. Unpack, make sco32v505udk, OK. Also built the TCP/IP version and it almost made an outbound connection, but only once (not a Kermit problem but something with the TCP/IP stack). 19 Jun 2011. Ditto for Solaris 2.6/i386, except 57.6Kbps, 4K-byte packets, no problem. Solaris 8/i386, ditto. 19 Jun 2011. SCO OpenServer 5.0.5 i386 32 sco32v505udk 1940964 No TCP/IP SCO OpenServer 5.0.5 i386 32 sco32v505udknet 2314668 With TCP/IP Sun Solaris 2.6 i386 32 solaris26g 4661368 Sun Solaris 8 i386 32 solaris8g 4675432 When using compact substring notation, \s(xx[4]) returns the whole string xx starting at position 4, but \s(xx[4:]) returns an empty string. Fixed the latter to be like the former. ckuus5.c, 20 Jun 2010. Really it would have been nicer if \s(xx[4]) returned a single character, the 4th character of xx, but it's too late now. Added another "separator" character '.' (period) for that: \s(xx[4.]) is the 4th character of xx. ckuus4.c, 20 Jun 2010. Back to SCO OSR5.0.7... This failed before because 'rdchk' came up unknown at link time, unlike all previous OSR5's, that used rdchk() in place of the FIONREAD ioctl. Added #ifdefs to make a special case for 5.0.7. I'm not sure this is the best way, but this is the minimal change to get it to work. If anybody cares, maybe the same can be done for previous OSR5 releases. ckutio.c, 20 Jun 2010 (search for SCO_OSR507). SCO OpenServer 5.0.7 i386 32 sco32v507 1895724 No TCP/IP SCO OpenServer 5.0.7 i386 32 sco32v507net 2246792 With TCP/IP Checked current code on RHEL4, found that my GSSAPI-lib finding makefile target didn't look in enough places; added some more. makefile, 21 Jun 2011. Got reports back on HPUX from Peter Eichhorn, almost all good on HP-UX 7, 8, 9, 10, and 11. 21-22 Jun 2011. Got access to Debian 5.0 and 7-to-be ("Wheezy/Sid"). Regular 'make linux' is OK in Debian 5, but in 7 can't find crypt, res_search, or dn_expand; had to add more library search clauses to 'make linux'. makefile, 21 Jun 2011. In Debian 7.0, libk5crypto could not be found without adding another clause to 'make linux+krb5'. That done, the SSL build (1.0.0d) was OK, as well as the krb5+ssl one. makefile, 21 Jun 2011. I found a Linux box that had both Kerberos 4 and 5 installed and tried 'make linux+krb5+krb4', which failed because of missing DES functions. Tried 'make linux+krb5+krb4 KFLAGS=-UCK_DES', but that fails too, even though it doesn't fail for Kerberos 5 alone, so probably some Krb4 code is making unguarded calls to the DES routines. What is really needed is a way to completely strip all DES references from any given build, code and makefile, a big deal. 21 Jun 2011. Fixed some typos in COPYING.TXT (noticed by Ian Beckwith). 24 Jun 2011. Got access to perhaps the last living 4.3BSD VAX system. It doesn't have SEEK_CUR so I had to #ifdef out the \fpicture() function. Aside from that, no problems. ckuus4.c, 24 Jun 2011. I had been wanting the S-Expression (ROUND x) to allow a second argument n, which, if given, tells where the rounding should occur. If n is positive, the number is rounded to n decimal places. If zero, it is rounded to the nearest integet. If positive, the number is rounded to the nearest power of 10; e.g. -2 means "to the nearest hundred". If ROUND is used as before, with one argument, it works as before. ckclib.c, ckuus3.c, 25 Jun 2011. From Arthur Marsh, a few more directories to test for libresolv in Linux. makefile, 26 Jun 2011. From Martin Vorlaender, a fix for the VMS file-transfer display and statistics, a place where a file length wasn't being cast to CK_OFF_T in zchki(). ckvfio.c, 28 Jun 2011. Updated version to 9.0.300 and removed the Beta designation. ckcmai.c, makefile, 28 Jun 2011. Removed solaris9_64 target from makefile. It builds but it doesn't work at all. 30 Jun 2011. --- C-Kermit 9.0.300 --- On Solaris 10 and 11, DNS lookups don't work. It seems these Solaris versions have INADDRX and INADDRX_NONE defined, thus triggering the code in ckcnet.c, ckucns.c, and ckcftp.c #ifdef'd on these symbols, but that code doesn't work in this case. This happens building with gcc as well as with Sun cc. Put #ifdefs in ckcnet.h to undefine these symbols (if they are defined after including all the header files) for Solaris. I didn't bother trying to differentiate the Solaris versions because the symbols are not defined in Solaris 9 or earlier, and they should not be used in Solaris 10 or 11. ckcnet.h, 6 July 2011. From SMS: To avoid the %CC-W-PTRMISMATCH1 complaints from ck_ssl.c, add two (harmless) type casts at lines 2460 and 2773, 6 July 2011. Built and tested on Solaris 9, Solaris 10, and RHEL5. 6 July 2011. --- C-Kermit 9.0.301 Beta.01 --- Updated version text and date. ckcmai.c, makefile, 11 July 2011. --- C-Kermit 9.0.301 --- After the initial release I made some small changes that affect only HP-UX 5.x: added -DVOID=void and -DCKVOID=void to the hpux0500 makefile targets, and put #ifdefs around #include , which (in the WinTCP case) didn't protect itself against multiple inclusion (which is happening in other header files, not in Kermit). makefile, ckucmd.c, ckucon.c, ckutio.c, ckufio.c, ckcnet.c, ckcftp.c, 14 July 2011. In the new copyright notice, copied from the BSD license template, one instance of "the " was not replaced by "Columbia University". Fixed in ckcmai.c, 19 July 2011. Added another search term for lk5crypto in the linux+krb5 targets. makefile, 20 July 2011. Added and successfully used a new solaris9+krb5+ssl target. makefile, 8 Aug 2011. FreeBSD 9 switched from utmp to utmpx, which broke compilation of Kermit on that version. Furthermore, the UUCP lockfile convensions changed in FreeBSD 8, which did not prevent C-Kermit from compiling, but any attempt to lock a terminal or pty device would fail. Thanks to Alexey Dokuchaev "danfe" for finding and patching the problems. I undid the patches and fixed the code so it didn't need to be patched, except for some declarations in the ck_crp.c module, which I felt had better not be changed without thoroughly testing the changes on dozens of different platforms, which I don't have time to do (in any case, it builds OK on FreeBSD 9 without the patch). In particular I made completely new makefile targets for FreeBSD 4.0 and later, which automatically detect FreeBSD 8 and FreeBSD 9 to enable the appropriate feature tests in the code, for a regular build and a build with OpenSSL. These changes should affect only FreeBSD. ckutio.c, ckufio.c, ck_crp.c, ckuus5.c, makefile, 20 Aug 2011. Changed the version to 9.0.302, ckcmai.c, 20 Aug 2011. Fixed freebsd+ssl and netbsd+ssl, netbsd+krb5, and netbsd+krb5+ssl targets to have CC=$(CC) instead of CC=gcc; ditto for CC2, and adjusted line breaks in freebsd and freebsd+ssl targets. makefile, 21 Aug 2011. --- 9.0.302 --- --------------------------------- *************************** ckermit.ini0000664000015300001460000000047411602371545012033 0ustar fdckermitCOMMENT - Standard C-Kermit initialization file ; echo echo The very long standard initialization file that was distributed echo with C-Kermit 6, 7, and 8 is no longer recommended as "standard", echo since its features were little used. It is still available in echo the C-Kermit distribution as ockermit.ini. echo ckermod.ini0000664000015300001460000000051011602371623012005 0ustar fdckermit; File CKERMOD.INI, Sample C-Kermit 7.0 customization file. ; echo echo The very long standard initialization file that was distributed echo with C-Kermit 6, 7, and 8 is no longer recommended as "standard", echo since its features were little used. It is still available in echo the C-Kermit distribution as ockermod.ini. echo ockermit.ini0000664000015300001460000005136707726132607012227 0ustar fdckermitCOMMENT - Standard C-Kermit initialization file ; ; For C-Kermit Version: 8.0 ; ; Filename: ; .kermrc (UNIX, OS-9, Aegis) ; CKERMIT.INI (OS/2, VMS, OpenVMS, AOS/VS, Atari ST, Commodore Amiga) ; ckermit.ini (Stratus VOS) ; K95.INI (Kermit 95 -- but this big version is not used there) ; K2.INI (Kermit/2 -- but ditto) ; ; Authors: ; Frank da Cruz, Christine M. Gianone, Jeffrey Altman ; Columbia University, New York, NY 10025-7799, USA ; ; This is the standard and recommended C-Kermit 8.0 initialization file. To ; override settings or definitions made in this file, to add new settings or ; definitions, or to make any other desired customizations, create a separate, ; personal customization file called: ; ; .mykermrc (UNIX, OS-9, Aegis, BeBox, Plan 9) ; CKERMOD.INI (OS/2, VMS, OpenVMS, AOS/VS, Atari ST, Commodore Amiga) ; ckermod.ini (VOS) ; ; You can also define the customization filename in an environment ; variable (logical name in VMS), CKERMOD, which takes precedence over ; the names shown above. ; ; WHAT THIS FILE DOES: ; ; . Defines your default dialing directory name: ; .kdd for UNIX, OS-9 and Aegis; CKERMIT.KDD for other operating systems. ; You can override this with the environment variable K_DIAL_DIRECTORY ; . Defines your default network directory name: ; .knd for UNIX, OS-9 and Aegis; CKERMIT.KND for other operating systems. ; You can override this with the environment variable K_NET_DIRECTORY ; . Defines your default services directory name: ; .ksd for UNIX, OS-9 and Aegis; CKERMIT.KSD for other operating systems. ; You can override this with environment variable K_SERVICE_DIRECTORY. ; . Defines your customization file name (name given above) ; . Performs system-dependent setups for UNIX, VMS, OS/2, etc. ; . Defines VTPRINT macros for use with K95, MS-DOS Kermit, etc. ; . If you have a services directory, all the macros needed to use it are ; defined. If you don't have a services directory, the macros are not ; defined and Kermit starts faster. ; . Executes your personal customization file, if you have one. ; NOTE: Your customization file is NOT executed by Kermit itself; it is ; executed by this file. ; ; In UNIX, with C-Kermit 7.0 and later, you can store this file with a name ; other than .kermrc, and it will not be executed automatically, but, if you ; give this file execute permission, you can execute directly because of the ; "kerbang line" at the top, whenever you want all of the above actions to ; occur. The kerbang line must reflect the actual full path of the Kermit ; 7.0-or-later executable. ; ; C-Kermit 6.0 is documented in the book "Using C-Kermit", 2nd Edition, ; by Frank da Cruz and Christine M. Gianone, 1997, Digital Press / ; Butterworth-Heinemann, ISBN 1-55558-164-1. New features of subsequent ; versions are documented at the Kermit website: ; http://www.columbia.edu/kermit/ ; ; Everything after this point depends on the script programming language. ; The CHECK command terminates this command file immediately if the script ; programming language (IF command) is not configured. ; set take error on ; This makes CHECK quit if no script language. check if ; Do we have an IF command? If not, quit now. set take error off ; Back to normal. local _sd _servicedir _xp ; Declare local variables. COMMENT - C-Kermit version 6.0 or later required. ; asg _xp \v(xprogram) if not def _xp asg _xp \v(program) if not equal "\m(_xp)" "C-Kermit" - stop 1 \v(cmdfile): This initialization file is only for C-Kermit. echo Executing \v(cmdfile) for \v(system)... if < \v(version) 60000 - stop 1 \v(cmdfile): C-Kermit 6.0 or later required. forward \v(system) ; First do system-dependent items... :unknown ; Should not happen Stop 1 Error: System type unknown! :Aegis ; Apollo Aegis and :UNIX ; UNIX, all versions asg _myinit - \v(home).mykermrc ; Customization filename if remote forward COMMON ; Skip local-mode items if "-R" asg _dialdir - \v(home).kdd ; C-Kermit dialing directory asg _netdir - \v(home).knd ; C-Kermit network directory asg _servicedir - \v(home).ksd ; C-Kermit services directory forward COMMON ; End of UNIX section :OS9/68K ; OS-9 asg _myinit - \v(home).mykermrc ; Customization filename if remote forward COMMON asg _dialdir - \v(home).kdd ; C-Kermit dialing directory asg _netdir - \v(home).knd ; C-Kermit network directory asg _servicedir - \v(home).ksd ; C-Kermit services directory else set file display crt forward COMMON ; End of OS-9 section :VMS ; VMS and OpenVMS forward COMMON :OS/2 ; Kermit 95 :WIN32 echo This initialization file is not for use with K95. forward COMMON ; End of OS/2 section :AOS/VS ; Data General AOS/VS set window 1 ; Sliding windows don't work set file char dg-international ; File character-set set xfer char latin1 ; Transfer character-set set file display crt ; File transfer fisplay def cli push ; Escape to CLI def reset - ; Macro to reset DG DASHER terminal run write [!ascii 236 306 301] forward COMMON ; End of AOS/VS section :Amiga ; Commodore Amiga def cls echo \27[H\27[2J ; CLS command to clear the screen set file char latin1 ; Use Latin Alphabet 1 for file transfer set xfer char latin1 ; ... forward COMMON ; End of Amiga section :Atari_ST ; Atari ST def cls echo \27H\27J ; Clear screen a`la VT52 set server display on ; Show file xfer display in server mode too set server timeout 15 ; Nonzero required for ^C interruption! forward COMMON ; End of Atari ST section :Macintosh ; Apple Macintosh set server display on ; Show file xfer display in server mode too. forward COMMON :Stratus_VOS ; Stratus VOS asg _myinit \v(home)ckermod.ini if remote forward COMMON asg _dialdir \v(home)ckermit.kdd asg _netdir \v(home)ckermit.knd asg _servicedir \v(home)ckermit.ksd forward COMMON ; End of Stratus VOS section :COMMON ; For all systems ; Define macros that are useful when running C-Kermit in remote mode. ; These macros serve no purpose on local-mode-only versions such as ; OS/2, Macintosh, Amiga, and Atari ST Kermit, so we skip defining them ; for those systems. ; if not = 0 \findex(\v(system),WIN32:OS/2:Macintosh:Amiga:Atari_ST) - forward files ; VTPRINT macro. Print a file on your PC's local printer. def VTPRINT echo \27[5i, type \%1, echo \27[4i ; or if your printer needs a formfeed to force the page out: ; def VTPRINT def echo \27[5i, type \%1, echo \12\27[4i ; Macros for host-initiated file transfer using APC: ; NOT NEEDED ANY MORE because of autodownload/autoupload. ; Remove the following FORWARD command to reinstate these definitions: :FILES ; Get customization and directory file names. Environment variables take ; precedence, so you do not have to edit this file to change these filenames. ; if def \$(CKERMOD) assign _myinit \$(CKERMOD) if not def _myinit assign _myinit \v(home)CKERMOD.INI if remote forward CUSTOM ; Skip all this if -R given on command line if def \$(K_NET_DIRECTORY) assign _netdir \$(K_NET_DIRECTORY) if not def _netdir assign _netdir \v(home)CKERMIT.KND if def \$(K_DIAL_DIRECTORY) assign _dialdir \$(K_DIAL_DIRECTORY) if not def _dialdir assign _dialdir \v(home)CKERMIT.KDD CHECK DIAL ; Is there a DIAL command? xif fail { ; No. echo DIAL disabled forward CUSTOM } CHECK NETWORK xif success { xif exist \m(_netdir) { set net directory \m(_netdir) echo { Network directory is \m(_netdir) } } } if eq "\v(name)" "telnet" forward CUSTOM xif exist \m(_dialdir) { set dial directory \m(_dialdir) echo { Dial directory is \m(_dialdir) } } COMMENT - Services directory if def \$(K_SERVICE_DIRECTORY) assign _servicedir \$(K_SERVICE_DIRECTORY) if not def _servicedir assign _servicedir \v(home)CKERMIT.KSD ; If no services directory is found skip all the big macro definitions and ; go straight to the bottom, where we execute the customization file. if not exist \m(_servicedir) forward custom echo { Services directory is \m(_servicedir)} def MAX_SVCS 200 ; Adjust this if you have more entries define _sd 0 ; Assume no services directory open read \m(_servicedir) ; Try to open services directory file xif success { declare \&d[\m(MAX_SVCS)] ; It's open, declare directory array for \%i 1 \m(MAX_SVCS) 1 { ; Read the lines into the array read \&d[\%i] if fail break } close read xif > \%i \m(MAX_SVCS) { echo Too many entries in services directory echo { Maximum is \m(MAX_SVCS).} echo { Change definition of MAX_SVCS in \v(cmdfile) to allow more. } echo { Services directory disabled.} } else { asg \&d[0] \feval(\%i - 1) define _sd 1 } } xif not \m(_sd) { def access echo { Services directory not available.} asg list \m(access) } else { def FIND { set case off for \%i 1 \&d[0] 1 { if eq {\%1} {\fsubstr(\&d[\%i],1,\flen(\%1))} break } if not > \%i \&d[0] return \&d[\%i] } def LIST { xif > \v(argc) 1 { do find \%1 if def \v(return) echo \v(return) else echo \%1: Not found } else { echo \&d[0] items in services directory: for \%i 1 \&d[0] 1 { echo \fcont(\&d[\%i]) } } } def SPLIT { asg _word1 \%1, asg _word2 \%2 } def DOACCESS { ; (Used internally by ACCESS macro) do \%5 \%6 \%7 \%8 \%9 ; Do the connection macro if fail end 1 split \%3 ; Get words from \%3 asg \%3 \m(_word1) asg \%2 \m(_word2) do \%3 \%4 {\%1} \%2 ; Login macro, userid, password, prompt } def ACCESS { if not defined \%1 end 1 access what? ; Check service do find \%1 ; Look it up if success doaccess {\%2} \v(return) ; OK, try it else end 1 "\%1" not in services directory ; Not found if fail end 1 ; DOACCESS failed? xif eq \v(cmdlevel) 1 { echo echo ACCESS: Login succeeded - CONNECTing... show escape output \13 connect /quietly } } } :CONNECTION ; Macros for making connections COMMENT - SERIAL macro. Arguments: ; \%1 = device name ; \%2 = speed ; def SERIAL { if < \v(argc) 3 ; All arguments given? end 1 Usage: SERIAL device speed ; No. set line \%1 ; OK, try to SET LINE. if failure - ; If this failed, end 1 Can't open device: \%1 ; print message and quit. set speed \%2 ; Try to set the speed. if fail end 1 Unsupported speed: \%2 ; Failed. echo Connection successful. ; Succeeded. } COMMENT - NET macro. Arguments: ; \%1 = network type ; \%2 = host name or address ; def NET { if < \v(argc) 3 end 1 Usage: NET network host set network type \%1 if fail end 1 unsupported network: \%1 set login user ; Don't send user ID. set host \%2 if fail end 1 Can't reach host: \%2 echo Connection successful. } COMMENT - CALL macro. Arguments: ; ; \%1 = modem type ; \%2 = device name ; \%3 = speed ; \%4 = phone number ; def CALL { if < \v(argc) 5 - ; All arguments present? end 1 Usage: CALL modem device speed number xif not equal {\v(modem)} {\%1} { ; Set modem type set modem \%1 if fail end 1 unknown modem type: \%1 } xif not equal {\v(line)} {\%2} { ; Communication device set line \%2 if fail end 1 can't open device: \%2 } xif not equal {\v(speed)} {\%3} { ; Communication speed set speed \%3 if fail end 1 unsupported speed: \%3 } dial \%4 ; Dial the number if fail end 1 Can't place call: \%4 end 0 Connection successful. } COMMENT - TCPCALL macro. Arguments: ; ; \%1 = server name:port ; \%2 = modem type ; \%3 = phone number ; def TCPCALL { if < \v(argc) 4 - ; All arguments present? end 1 Usage: TCPCALL server[:port] modem number set net type tcp/ip ; Which network to use if fail end 1 unsupported network: tcp/ip set host \%1 ; Access server and port if fail end 1 can't access server \%1 set modem \%2 ; Set modem type if fail end 1 unknown modem type: \%2 dial \%3 ; Dial the number if fail end 1 Can't place call: \%3 end 0 Connection successful. } COMMENT - SPRINT macro. Arguments: ; \%1 = Service name or address ; def SPRINT { if < \v(argc) 2 end 1 Usage: \%0 service set input timeout proceed output @D\13 input 10 TERMINAL= if fail end 1 No terminal prompt out D1\13 inp 10 @ if fail end 1 No atsign prompt output c \%1\13 input 10 CONNECTED if fail end 1 Can't access \%1 from SprintNet } COMMENT - ULOGIN macro. For logging into systems where user ID is required ; but there is no password. Arguments: ; \%1 = UNIX user ID ; define ULOGIN { if < \v(argc) 2 end 1 Usage: \%0 userid set input timeout proceed ; Handle timeouts ourselves set case on ; Case is important in UNIX minput 5 login: Username: {User ID:} {User Name:} out \%1\13 ; Send username, carriage return end 0 } COMMENT - VMSLOGIN macro. Arguments: ; \%1 = VMS user ID ; \%2 = Password. If password not supplied, it is prompted for. ; \%3 = System prompt. If omitted a default is supplied. ; define VMSLOGIN { if < \v(argc) 2 end 1 Usage: \%0 userid [ password [ prompt ] ] while not defined \%2 { askq \%2 { \%1's password: } } set parity none ; Set communication parameters set duplex full set handshake none set input timeout proceed ; Handle timeouts ourselves in 5 Username: ; Is prompt already there? xif fail { ; No. for \%i 1 3 1 { ; Try 3 times to get it. out \13 ; Send carriage return in 5 Username: ; Look for prompt if success break ; Success, go log in } if > \%i 3 end 1 No Username prompt } out \%1\13 ; Send username, carriage return inp 5 Password: ; Wait 5 sec for this prompt if fail end 1 No password prompt pause ; Wait a sec out \%2\13 ; Send password xif not emulation { ; No emulator built in? set input echo off ; Protect terminal from this minput 10 {\27Z} {\27[c} {\27[0c} ; Get terminal ID query xif success { ; Got one output \27[\?1c ; Send VT100 terminal ID in 2 \27[6n ; Screen dimension query? if succ out \27[\v(rows);\v(cols)R ; Send dimensions } set input echo on ; Echo input again } if not def \%3 - ; If we were not given a prompt asg \%3 {\v(prompt)} ; use the SET LOGIN PROMPT value if not def \%3 - ; If we still don't have a prompt asg \%3 {\13$\32} ; use this one as the default reinp 0 \%3 ; Did we INPUT the prompt already? if fail inp 60 \%3 ; No, look now. if fail end 1 } COMMENT - UNIXLOGIN macro. Arguments: ; \%1 = UNIX user ID ; \%2 = Password. If password not supplied, it is prompted for. ; \%3 = System prompt. If omitted a default is supplied. ; define UNIXLOGIN { local \%m \%i if < \v(argc) 2 - end 1 Usage: \%0 userid [ password [ prompt ] ] while not defined \%2 { askq \%2 { \%1's password: } } set input echo on set parity none ; Set communication parameters. set duplex full set handshake none set input timeout proceed ; Handle timeouts ourselves set case on ; Case is important in UNIX def \%m 10 ; Waiting time for INPUT for \%i 1 5 1 { minput \%m login: {ssword:} {Password for \%1:} if success break output \B\13 \%m ::= 6-\%1 } if > \%i 5 end 1 {No response from host} xif = \v(minput) 1 { ; Have username prompt output \%1\13 ; Send username minput 5 {ssword:} {ssword for \%1:} ; Wait for password prompt if fail end 1 {No password prompt} } pause ; Wait a sec out \%2\13 ; Send password if not def \%3 - ; If we were not given a prompt asg \%3 {\v(prompt)} ; use the SET LOGIN PROMPT value if not def \%3 - ; If we still don't have a prompt asg \%3 {\10$ } ; use this one as the default reinp 0 \%3 ; Did we INPUT the prompt already? if fail inp 60 \%3 ; No, look now. if fail end 1 } COMMENT - VMLINELOGIN macro. Arguments: ; \%1 = User ID ; \%2 = Password ; define VMLINELOGIN { if < \v(argc) 2 - end 1 Usage: \%0 userid [ password ] while not defined \%2 { askq \%2 { \%1's password: } } set parity mark ; Set communication parameters set flow none set handshake xon set duplex half set input timeout quit ; Don't bother with IF FAILURE input 10 BREAK KEY ; Look for BREAK KEY prompt pause 1 ; Wait a second output \B ; Send BREAK input 10 .\17, output logon \%1\13 ; Now log in input 10 .\17, output \%2\13 ; Send password input 10 .\17, output \13 ; Send carriage return input 10 .\17, output \13 ; Send another one end 0 } COMMENT - VMFULLOGIN macro. Arguments: ; \%1 = User ID ; \%2 = Password ; define VMFULLOGIN { if < \v(argc) 2 - end 1 Usage: \%0 userid [ password ] while not defined \%2 { askq \%2 { \%1's password: } } set input timeout quit ; Quit if INPUT fails set parity even ; Set communication parameters set duplex full set handshake none set flow xon/xoff out \13 ; Send carriage return inp 5 TERMINAL TYPE: ; Get terminal-type prompt out vt-100\13 ; Just send "vt-100" inp 20 RUNNING ; Get RUNNING message pau 1 ; Wait one second out \%1\9\%2\13 ; Send user ID, tab, password out \13\13 ; Two more carriage returns end 0 } COMMENT - CISLOGIN macro. Arguments: ; \%1 = CompuServe User ID ; \%2 = Password ; \%3 = Prompt ; define CISLOGIN { if < \v(argc) 2 - end 1 Usage: \%0 userid [ password [ prompt ] ] while not defined \%2 { askq \%2 { \%1's password: } } set terminal bytesize 7 ; No 8-bit characters set input timeout quit ; Skip the IF FAILURE's output \13 ; Send initial carriage return input 5 Host Name: ; Look for Host Name prompt output cis\13 ; Send "cis" and carriage return input 5 User ID: ; Look for User ID prompt output \%1\13 ; Send ID and carriage return input Password: ; Look for Password prompt output \%2\13 ; Send password and CR if not def \%3 asg \%3 \v(prompt) if not def \%3 asg \%3 {CompuServe Information Service} input 30 \%3 end 0 } COMMENT - DOWLOGIN macro. Arguments: ; \%1 = Dow Jones Password ; define DOWLOGIN { while not defined \%1 { ; Get password askq \%1 { Dow Jones password: } } set input timeout proceed input 20 SERVICE PLEASE\?\?\?\? ; Look for Dow prompt if fail end 1 No service prompt out djnr\13 ; Select DJNR input 10 @@@@@@@@ ; Get password prompt if fail end 1 No password prompt pause 1 ; Wait a second, then... output \%1\13 ; send password and CR input 30 ENTER QUERY ; Get DJNR query prompt if fail end 1 No main query prompt pause 1 } COMMENT - DJNRSPRINT macro: Log in to Dow Jones via SprintNet. ; def djnrsprint sprint dow, if success dowlogin COMMENT - NOLOGIN macro. Does nothing. Use when login not required. ; def nologin comment :CUSTOM ; Customization file ; In VMS and OpenVMS, allow for system-wide site customizations xif equal "\v(system)" "VMS" { xif exist CKERMIT_INI:CKERMIT.SYS { echo Executing CKERMIT_INI:CKERMIT.SYS take CKERMIT_INI:CKERMIT.SYS } } ; Execute user's personal customization file xif exist \m(_myinit) { ; If it exists, echo Executing \m(_myinit)... ; print message, take \m(_myinit) ; and TAKE the file. } ; Finish up with traditional greeting. if < \v(ntime) 43200 echo Good Morning! else if < \v(ntime) 61200 echo Good Afternoon! else echo Good Evening. End ; of C-Kermit 8.0 initialization file. ockermod.ini0000664000015300001460000001336007432523443012200 0ustar fdckermit; File CKERMOD.INI, Sample C-Kermit 7.0 customization file. ; ; This file, which is ONLY A SAMPLE, should be called: ; ; .mykermrc (UNIX, OS-9, Aegis, BeBox, Plan 9) ; CKERMOD.INI (VMS, OpenVMS, AOS/VS, OS/2, Amiga, Atari ST) ; ckermod.ini (Stratus VOS) ; ; This file is executed automatically by the standard C-Kermit initialization ; file, CKERMIT.INI (or .kermrc). This file is not executed by C-Kermit itself ; unless the initialization file is not found. ; ; MODify this file to suit your needs and preferences, and install it in your ; home directory. Or replace it entirely with a new file. ; ; The design of this sample customization file lets you fill in a section for ; each different operating system where you run C-Kermit. ; ; In UNIX, if you give this file execute permission and make sure the top ; line indicates the full path of the C-Kermit 7.0-or-later executable, you ; can execute this file directly, as if it was a shell script, except it is ; interpreted by Kermit rather than the shell. This lets you have as many ; different startup files as you like, each suited to a particular purpose. ; ; Authors: Christine Gianone, Frank da Cruz, Jeffrey Altman, ; The Kermit Project, Columbia University. ; Creation: 23 November 1992 for C-Kermit 5A(188). ; Modified: 30 June 1993 for edit 189. ; 04 October 1994 for edit 190. ; 17 April 1995 for edit 191. ; 6 September 1996 for version 6.0, edit 192. ; 1 January 2000 for version 7.0, edit 196. ; 14 October 2001 for version 8.0, edit 200. ECHO ECHO Executing SAMPLE C-Kermit customization file \v(cmdfile) for \v(system)... ECHO { Please edit this file to reflect your needs and preferences.} ECHO ; ; ... and then remove the ECHO commands above. COMMENT - Settings that apply to all the systems I use: ; set delay 1 ; I escape back quickly set dial display on ; I like to watch C-Kermit dial ; Dialing locale and method ; ; SET DIAL COUNTRY-CODE 1 ; Uncomment and replace with yours ; SET DIAL AREA-CODE 000 ; Uncomment and replace with yours ; SET DIAL LD-PREFIX 1 ; Uncomment and replace with yours ; SET DIAL INTL-PREFIX 011 ; Uncomment and replace with yours ; SET DIAL METHOD TONE ; Uncomment and replace with PULSE if necessary ; SET DIAL DIRECTORY ... ... ; List dialing directory files here if < \v(version) 600192 - stop 1 \v(cmdfile): C-Kermit 6.0.192 or later required. set take error on ; Make errors fatal temporarily check if ; Do we have an IF command? set take error off ; Yes we do, back to normal ; The ON_EXIT macro is executed automatically when C-Kermit exits. ; Define as desired. ; define ON_EXIT echo Returning you to \v(system) now. ; System-independent quick dialing macro. Depends on having the ; macros MYMODEM, MYPORT, and (optionally) MYSPEED defined in the ; system-dependent sections below. ; define MYDIAL { if not defined MYMODEM end 1 {\%0: Modem type not defined.} set modem type \m(MYMODEM) if fail end 1 {\%0: \m(MYMODEM): Unsupported modem type.} if not defined MYPORT end 1 {\%0: Communication port not defined.} set port \m(MYPORT) if fail end 1 {\%0: SET PORT \m(MYPORT) failed.} if defined MYFLOW set flow \m(MYFLOW) if fail end 1 {\%0: SET FLOW \m(MYFLOW) failed.} if defined MYSPEED set speed \m(MYSPEED) if fail end 1 {\%0: SET SPEED \m(MYSPEED) failed.} dial \%1\%2\%3\%4\%5\%6\%7\%8\%9 end \v(status) } forward \v(system) ; Go execute system-dependent commands :UNIX ; UNIX, all versions... define MYPORT /dev/cua ; My dialing environment define MYMODEM usr ; Replace these by what you actually have define MYSPEED 57600 ; ; If you want all your downloads to go to the same directory, no matter ; what your current directory is, uncomment and edit the following command: ; ; set file download-directory ~/download ; Download directory for UNIX ; Put other UNIX-specific commands here... end ; End of UNIX section :VMS ; VMS and OpenVMS define MYPORT TXA0: ; My dialing environment define MYMODEM usr ; Replace these by what you actually have define MYSPEED 57600 ; set file download-directory [\$(USER).DOWNLOAD] ; Download directory for VMS ; Put other VMS-specific commands here... end ; End of VMS section :WIN32 ; Windows and OS/2 customizations... :OS/2 define MYPORT COM1 ; My dialing environment define MYMODEM usr ; Replace these by what you actually have define MYSPEED 57600 set command byte 8 ; Use 8 bits between Kermit and console set xfer char latin1 ; Use Latin-1 for text file transfer set term char latin1 ; And use Latin-1 during CONNECT mode ; set file download-directory C:\DOWNLOADS end :OS9/68K ; OS-9/68000 define MYPORT /t3 ; My dialing environment define MYMODEM usr ; Replace these by what you actually have define MYSPEED 9600 ; set file download-directory ~/downloads end ; End of OS-9 section :AOS/VS ; Data General AOS/VS define MYPORT @con3 ; My dialing environment define MYMODEM usr ; Replace these by what you actually have define MYSPEED 9600 ; set file download-directory \v(home)DOWNLOADS end ; And so on, you get the idea... ; Fill in the sections that apply to you. :Stratus_VOS ; Stratus VOS :Amiga ; Commodore Amiga :Atari_ST ; Atari ST :Macintosh ; Apple Macintosh :unknown ; Others ; (End of CKERMOD.INI) ck_crp.c0000644000015300001460000051002711624020335011267 0ustar fdckermitchar *ckcrpv = "Encryption Engine, 9.0.117, 19 Mar 2010"; /* C K _ C R P . C - Cryptography for C-Kermit" Copyright (C) 1998, 2010, Trustees of Columbia University in the City of New York. All rights reserved. See the C-Kermit COPYING.TXT file or the copyright text in the ckcmai.c module for disclaimer and permissions. Author: Jeffrey E Altman (jaltman@secure-endpoints.com) Secure Endpoints Inc., New York City */ #define CK_CRP_C #ifdef CK_DES #ifdef CK_SSL #ifndef LIBDES #define LIBDES #endif /* LIBDES */ #endif /* CK_SSL */ #endif /* CK_DES */ #ifdef CRYPT_DLL #define CK_AUTHENTICATION #define CK_ENCRYPTION #define CK_DES #define CK_CAST #ifndef LIBDES #define LIBDES #endif /* LIBDES */ #define TELCMDS /* to define name array */ #define TELOPTS /* to define name array */ #define ENCRYPT_NAMES #endif /* CRYPT_DLL */ #include "ckcsym.h" #include "ckcdeb.h" #include "ckcnet.h" #ifdef DEBUG #undef DEBUG #endif /* DEBUG */ #ifdef CK_AUTHENTICATION #ifdef CK_ENCRYPTION #define ENCRYPTION #ifdef CK_DES #define DES_ENCRYPTION #endif /* CK_DES */ #ifdef CK_CAST #define CAST_ENCRYPTION #endif /* CK_CAST */ #ifdef COMMENT #define CAST_EXPORT_ENCRYPTION #endif /* COMMENT */ #endif /* CK_ENCRYPTION */ #endif /* CK_AUTHENTICATION */ #ifdef CK_ENCRYPTION #include "ckucmd.h" /* For struct keytab definition */ #include "ckuath.h" #include "ckuat2.h" #ifdef MIT_CURRENT #include #endif /* MIT_CURRENT */ #include #include #ifdef OS2 #include #ifdef OS2ONLY #include #endif /* OS2ONLY */ #include "ckosyn.h" #else /* OS2 */ static char * tmpstring = NULL; #endif /* OS2 */ #ifndef CAST_OR_EXPORT #ifdef CAST_ENCRYPTION #define CAST_OR_EXPORT #endif /* CAST_ENCRYPTION */ #ifdef CAST_EXPORT_ENCRYPTION #define CAST_OR_EXPORT #endif /* CAST_EXPORT_ENCRYPTION */ #endif /* CAST_OR_EXPORT */ #ifdef MACOSX #undef LIBDES #endif /* MACOSX */ #ifdef CRYPT_DLL int cmd_quoting = 0; #ifndef TELOPT_MACRO int telopt_index(opt) int opt; { if ( opt >= 0 && opt <= TELOPT_SEND_URL ) return(opt); else if ( opt >= TELOPT_PRAGMA_LOGON && opt <= TELOPT_PRAGMA_HEARTBEAT ) return(opt-89); else return(NTELOPTS); } int telopt_ok(opt) int opt; { return((opt >= TELOPT_BINARY && opt <= TELOPT_SEND_URL) || (opt >= TELOPT_PRAGMA_LOGON && opt <= TELOPT_PRAGMA_HEARTBEAT)); } CHAR * telopt(opt) int opt; { if ( telopt_ok(opt) ) return(telopts[telopt_index(opt)]); else return("UNKNOWN"); } #endif /* TELOPT_MACRO */ static int (*p_ttol)(char *,int)=NULL; static int (*p_dodebug)(int,char *,char *,CK_OFF_T)=NULL; static int (*p_dohexdump)(char *,char *,int)=NULL; static void (*p_tn_debug)(char *)=NULL; static int (*p_vscrnprintf)(char *, ...)=NULL; static void * p_k5_context=NULL; static unsigned long (*p_reqtelmutex)(unsigned long)=NULL; static unsigned long (*p_reltelmutex)(void)=NULL; unsigned long RequestTelnetMutex(unsigned long x) { if ( p_reqtelmutex ) return p_reqtelmutex(x); return 0; } unsigned long ReleaseTelnetMutex(void) { if ( p_reltelmutex ) return p_reltelmutex(); return 0; } int ttol(char * s, int n) { if ( p_ttol ) return(p_ttol(s,n)); else return(-1); } int dodebug(int flag, char * s1, char * s2, CK_OFF_T n) { if ( p_dodebug ) return(p_dodebug(flag,s1,s2,n)); else return(-1); } int dohexdump( char * s1, char * s2, int n ) { if ( p_dohexdump ) p_dohexdump(s1,s2,n); return(0); } void tn_debug( char * s ) { if ( p_tn_debug ) p_tn_debug(s); } static char myprtfstr[4096]; int Vscrnprintf(const char * format, ...) { int i, len, rc=0; char *cp; va_list ap; va_start(ap, format); #ifdef NT rc = _vsnprintf(myprtfstr, sizeof(myprtfstr)-1, format, ap); #else /* NT */ rc = vsprintf(myprtfstr, format, ap); #endif /* NT */ va_end(ap); if ( p_vscrnprintf ) return(p_vscrnprintf(myprtfstr)); else return(-1); } int #ifdef CK_ANSIC tn_hex(CHAR * buf, int buflen, CHAR * data, int datalen) #else /* CK_ANSIC */ tn_hex(buf, buflen, data, datalen) CHAR * buf; int buflen; CHAR * data; int datalen; #endif /* CK_ANSIC */ { int i = 0, j = 0, k = 0; CHAR tmp[8]; #ifdef COMMENT int was_hex = 1; for (k=0; k < datalen; k++) { if (data[k] < 32 || data[k] >= 127) { sprintf(tmp,"%s%02X ",was_hex?"":"\" ",data[k]); was_hex = 1; } else { sprintf(tmp,"%s%c",was_hex?"\"":"",data[k]); was_hex = 0; } ckstrncat(buf,tmp,buflen); } if (!was_hex) ckstrncat(buf,"\" ",buflen); #else /* COMMENT */ if (datalen <= 0 || data == NULL) return(0); for (i = 0; i < datalen; i++) { ckstrncat(buf,"\r\n ",buflen); for (j = 0 ; (j < 16); j++) { if ((i + j) < datalen) sprintf(tmp, "%s%02x ", (j == 8 ? "| " : ""), (CHAR) data[i + j] ); else sprintf(tmp, "%s ", (j == 8 ? "| " : "") ); ckstrncat(buf,tmp,buflen); } ckstrncat(buf," ",buflen); for (k = 0; (k < 16) && ((i + k) < datalen); k++) { sprintf(tmp, "%s%c", (k == 8 ? " " : ""), isprint(data[i + k]) ? data[i + k] : '.' ); ckstrncat(buf,tmp,buflen); } i += j - 1; } /* end for */ ckstrncat(buf,"\r\n ",buflen); #endif /* COMMENT */ return(strlen(buf)); } #ifdef COMMENT #define ttol dll_ttol #define dodebug dll_dodebug #define dohexdump dll_dohexdump #define tn_debug dll_tn_debug #define Vscrnprintf dll_vscrnprintf #endif /* COMMENT */ char tn_msg[TN_MSG_LEN], hexbuf[TN_MSG_LEN]; /* from ckcnet.c */ int deblog=1, debses=1, tn_deb=1; #else /* CRYPT_DLL */ extern char tn_msg[], hexbuf[]; /* from ckcnet.c */ extern int deblog, debses, tn_deb; #ifdef MIT_CURRENT extern krb5_context k5_context; #endif /* MIT_CURRENT */ #endif /* CRYPT_DLL */ #ifdef LIBDES #ifdef MACOSX #define des_new_random_key ck_des_new_random_key #define des_set_random_generator_seed ck_des_set_random_generator_seed #define des_key_sched ck_des_key_sched #define des_ecb_encrypt ck_des_ecb_encrypt #define des_string_to_key ck_des_string_to_key #define des_fixup_key_parity ck_des_fixup_key_parity #endif /* MACOSX */ #ifndef UNIX #define des_new_random_key des_random_key #define des_set_random_generator_seed des_random_seed #endif /* UNIX */ #define des_fixup_key_parity des_set_odd_parity #ifdef OPENSSL_097 #define OPENSSL_ENABLE_OLD_DES_SUPPORT #include #endif /* OPENSSL_097 */ #else /* LIBDES */ #ifdef UNIX #define des_set_random_generator_seed(x) des_init_random_number_generator(x) #endif /* UNIX */ #ifdef OS2 #define des_new_random_key ck_des_new_random_key #define des_set_random_generator_seed ck_des_set_random_generator_seed #define des_key_sched ck_des_key_sched #define des_ecb_encrypt ck_des_ecb_encrypt #define des_string_to_key ck_des_string_to_key #define des_fixup_key_parity ck_des_fixup_key_parity #endif /* OS2 */ #endif /* LIBDES */ #ifdef CK_DES /* This code comes from Eric Young's libdes package and is not part */ /* of the standard MIT DES library that is part of Kerberos. However, */ /* it is extremely useful. So we add it here. */ /* Weak and semi week keys as take from * %A D.W. Davies * %A W.L. Price * %T Security for Computer Networks * %I John Wiley & Sons * %D 1984 * Many thanks to smb@ulysses.att.com (Steven Bellovin) for the reference * (and actual cblock values). */ #define NUM_WEAK_KEY 16 static Block weak_keys[NUM_WEAK_KEY]={ /* weak keys */ {0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01}, {0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFE}, {0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F}, {0xE0,0xE0,0xE0,0xE0,0xE0,0xE0,0xE0,0xE0}, /* semi-weak keys */ {0x01,0xFE,0x01,0xFE,0x01,0xFE,0x01,0xFE}, {0xFE,0x01,0xFE,0x01,0xFE,0x01,0xFE,0x01}, {0x1F,0xE0,0x1F,0xE0,0x0E,0xF1,0x0E,0xF1}, {0xE0,0x1F,0xE0,0x1F,0xF1,0x0E,0xF1,0x0E}, {0x01,0xE0,0x01,0xE0,0x01,0xF1,0x01,0xF1}, {0xE0,0x01,0xE0,0x01,0xF1,0x01,0xF1,0x01}, {0x1F,0xFE,0x1F,0xFE,0x0E,0xFE,0x0E,0xFE}, {0xFE,0x1F,0xFE,0x1F,0xFE,0x0E,0xFE,0x0E}, {0x01,0x1F,0x01,0x1F,0x01,0x0E,0x01,0x0E}, {0x1F,0x01,0x1F,0x01,0x0E,0x01,0x0E,0x01}, {0xE0,0xFE,0xE0,0xFE,0xF1,0xFE,0xF1,0xFE}, {0xFE,0xE0,0xFE,0xE0,0xFE,0xF1,0xFE,0xF1}}; int ck_des_is_weak_key(key) Block key; { int i; for (i=0; i unsigned long unix_time_gmt_unixsec (usecptr) unsigned long *usecptr; { struct timeval now; (void) gettimeofday (&now, (struct timezone *)0); if (usecptr) *usecptr = now.tv_usec; return now.tv_sec; } void des_set_random_generator_seed(Block B) { des_random_seed(B); return; } #ifdef COMMENT /* added to openssl in 0.9.5 */ void des_fixup_key_parity(Block B) { des_set_odd_parity(B); return; } #endif /* COMMENT */ int des_new_random_key(Block B) { int rc=0; /* WARNING: This might need to have the "rc = " removed because this is VOID in later, and maybe even all, versions. */ rc = des_random_key(B); return(rc); } #endif /* MACOSX */ #endif /* LIBDES */ #endif /* UNIX */ #endif /* CK_DES */ /* * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* based on @(#)encrypt.c 8.1 (Berkeley) 6/4/93 */ /* * Copyright (C) 1990 by the Massachusetts Institute of Technology * * Export of this software from the United States of America may * require a specific license from the United States Government. * It is the responsibility of any person or organization contemplating * export to obtain such a license before exporting. * * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and * distribute this software and its documentation for any purpose and * without fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright notice and * this permission notice appear in supporting documentation, and that * the name of M.I.T. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior * permission. M.I.T. makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. */ #include /* * These function pointers point to the current routines * for encrypting and decrypting data. */ /* NOTE: These next two might need to have the "static " removed */ static VOID (*encrypt_output) P((unsigned char *, int)); static int (*decrypt_input) P((int)); #ifdef DEBUG static int encrypt_debug_mode = 1; static int encrypt_verbose = 1; #else static int encrypt_verbose = 1; static int encrypt_debug_mode = 0; #endif static char dbgbuf [16384]; static int decrypt_mode = 0; static int encrypt_mode = 0; static int autoencrypt = 1; static int autodecrypt = 1; static int havesessionkey = 0; static kstream EncryptKSGlobalHack = NULL; static int EncryptType = ENCTYPE_ANY; #define typemask(x) ((x) > 0 ? 1 << ((x)-1) : 0) static long i_support_encrypt = typemask(ENCTYPE_DES_CFB64) | typemask(ENCTYPE_DES_OFB64); static long i_support_decrypt = typemask(ENCTYPE_DES_CFB64) | typemask(ENCTYPE_DES_OFB64); static long i_wont_support_encrypt = 0; static long i_wont_support_decrypt = 0; #define I_SUPPORT_ENCRYPT (i_support_encrypt & ~i_wont_support_encrypt) #define I_SUPPORT_DECRYPT (i_support_decrypt & ~i_wont_support_decrypt) static long remote_supports_encrypt = 0; static long remote_supports_decrypt = 0; /* Make sure that this list is in order of algorithm strength */ /* as it determines the search order for selecting specific */ /* encryption choices. All CFB modes must come before OFB modes. */ static Encryptions encryptions[] = { #ifdef DES_ENCRYPTION { "DES3_CFB64", ENCTYPE_DES3_CFB64, des3_cfb64_encrypt, des3_cfb64_decrypt, des3_cfb64_init, des3_cfb64_start, des3_cfb64_is, des3_cfb64_reply, des3_cfb64_session, des3_cfb64_keyid, NULL }, #endif /* DES_ENCRYPTION */ #ifdef CAST_ENCRYPTION #ifndef CAST_EXPORT_ENCRYPTION { "CAST128_CFB64", ENCTYPE_CAST128_CFB64, cast_cfb64_encrypt, cast_cfb64_decrypt, cast_cfb64_init, cast_cfb64_start, cast_cfb64_is, cast_cfb64_reply, cast_cfb64_session, cast_cfb64_keyid, NULL }, #endif #endif #ifdef DES_ENCRYPTION { "DES_CFB64", ENCTYPE_DES_CFB64, cfb64_encrypt, cfb64_decrypt, cfb64_init, cfb64_start, cfb64_is, cfb64_reply, cfb64_session, cfb64_keyid, NULL }, #endif /* DES_ENCRYPTION */ #if defined (CAST_EXPORT_ENCRYPTION) || defined(CAST_ENCRYPTION) { "CAST5_40_CFB64", ENCTYPE_CAST5_40_CFB64, castexp_cfb64_encrypt, castexp_cfb64_decrypt, castexp_cfb64_init, castexp_cfb64_start, castexp_cfb64_is, castexp_cfb64_reply, castexp_cfb64_session, castexp_cfb64_keyid, NULL }, #endif /* CAST_ENCRYPTION */ #ifdef DES_ENCRYPTION { "DES3_OFB64", ENCTYPE_DES3_OFB64, des3_ofb64_encrypt, des3_ofb64_decrypt, des3_ofb64_init, des3_ofb64_start, des3_ofb64_is, des3_ofb64_reply, des3_ofb64_session, des3_ofb64_keyid, NULL }, #endif /* DES_ENCRYPTION */ #ifdef CAST_ENCRYPTION #ifndef CAST_EXPORT_ENCRYPTION { "CAST128_OFB64", ENCTYPE_CAST128_OFB64, cast_ofb64_encrypt, cast_ofb64_decrypt, cast_ofb64_init, cast_ofb64_start, cast_ofb64_is, cast_ofb64_reply, cast_ofb64_session, cast_ofb64_keyid, NULL }, #endif #endif #ifdef DES_ENCRYPTION { "DES_OFB64", ENCTYPE_DES_OFB64, ofb64_encrypt, ofb64_decrypt, ofb64_init, ofb64_start, ofb64_is, ofb64_reply, ofb64_session, ofb64_keyid, NULL }, #endif /* DES_ENCRYPTION */ #if defined (CAST_EXPORT_ENCRYPTION) || defined(CAST_ENCRYPTION) { "CAST5_40_OFB64", ENCTYPE_CAST5_40_OFB64, castexp_ofb64_encrypt, castexp_ofb64_decrypt, castexp_ofb64_init, castexp_ofb64_start, castexp_ofb64_is, castexp_ofb64_reply, castexp_ofb64_session, castexp_ofb64_keyid, NULL }, #endif /* CAST_ENCRYPTION */ { 0,0,0,0,0,0,0,0,0,0,0 } }; int get_crypt_table( struct keytab ** pTable, int * pN ) { int i=0,n=0; if ( *pTable ) { for ( i=0 ; i < *pN ; i++ ) free( (*pTable)[i].kwd ) ; free ( *pTable ) ; } *pTable = NULL; *pN = 0; /* How many encryption types do we have? */ while ( encryptions[n].name ) n++; if ( n ) { *pTable = malloc( sizeof(struct keytab) * (n+2) ) ; if ( !(*pTable) ) return(0); #ifdef OS2 (*pTable)[0].kwd =strdup("automatic"); #else /* OS2 */ makestr(&tmpstring,"automatic"); (*pTable)[0].kwd = tmpstring; tmpstring = NULL; #endif /* OS2 */ (*pTable)[0].kwval = ENCTYPE_ANY; (*pTable)[0].flgs = 0; #ifdef OS2 (*pTable)[1].kwd =strdup("none"); #else /* OS2 */ makestr(&tmpstring,"none"); (*pTable)[1].kwd = tmpstring; tmpstring = NULL; #endif /* OS2 */ (*pTable)[1].kwval = 999; (*pTable)[1].flgs = 0; (*pN) = 2; for ( i=0 ; i < n ; i++ ) { char * newstr = NULL, * p; int newval = encryptions[i].type; int j = 0, len = 0; #ifdef OS2 newstr = strdup(encryptions[i].name); strlwr(newstr); #else /* OS2 */ makestr(&tmpstring,encryptions[i].name); newstr = tmpstring; tmpstring = NULL; for (p = newstr; *p; p++) if (isupper(*p)) *p = tolower(*p); #endif /* OS2 */ for (j = 0; j < (*pN); j++) { int tempval = 0; char * tempstr = NULL; if ( strcmp( (*pTable)[j].kwd, newstr ) > 0 ) { tempval = (*pTable)[j].kwval; tempstr = (*pTable)[j].kwd; (*pTable)[j].kwd = newstr ; (*pTable)[j].kwval = newval; newval = tempval; newstr = tempstr; (*pTable)[j].flgs = 0; } } (*pTable)[*pN].kwd = newstr ; (*pTable)[*pN].kwval = newval; (*pTable)[*pN].flgs = 0 ; (*pN)++ ; } } else { *pTable = malloc( sizeof(struct keytab) * 2 ) ; if ( !(*pTable) ) return(0); #ifdef OS2 (*pTable)[0].kwd =strdup("automatic"); #else /* OS2 */ makestr(&tmpstring,"automatic"); (*pTable)[0].kwd = tmpstring; tmpstring = NULL; #endif /* OS2 */ (*pTable)[0].kwval = ENCTYPE_ANY; (*pTable)[0].flgs = 0; #ifdef OS2 (*pTable)[1].kwd =strdup("none"); #else /* OS2 */ makestr(&tmpstring,"none"); (*pTable)[1].kwd = tmpstring; tmpstring = NULL; #endif /* OS2 */ (*pTable)[1].kwval = 999; (*pTable)[1].flgs = 0; (*pN) = 2; } return(*pN); } static unsigned char str_send[64] = { IAC, SB, TELOPT_ENCRYPTION, ENCRYPT_SUPPORT }; static unsigned char str_suplen = 0; static unsigned char str_start[72] = { IAC, SB, TELOPT_ENCRYPTION }; static unsigned char str_end[] = { IAC, SB, TELOPT_ENCRYPTION, 0, IAC, SE }; _PROTOTYP(int encrypt_request_end, (VOID)); _PROTOTYP(int encrypt_request_start, (VOID)); _PROTOTYP(int encrypt_enc_keyid, (unsigned char *, int)); _PROTOTYP(int encrypt_dec_keyid, (unsigned char *, int)); _PROTOTYP(int encrypt_support, (unsigned char *, int)); _PROTOTYP(int encrypt_start, (unsigned char *, int)); _PROTOTYP(int encrypt_end, (VOID)); _PROTOTYP(int encrypt_ks_stream,(struct kstream_data_block *, /* output */ struct kstream_data_block *)); /* input */ _PROTOTYP(int decrypt_ks_stream,(struct kstream_data_block *, /* output */ struct kstream_data_block *)); /* input */ int #ifdef CK_ANSIC encrypt_ks_stream(struct kstream_data_block *i, struct kstream_data_block *o) #else encrypt_ks_stream(i,o) struct kstream_data_block *i; struct kstream_data_block *o; #endif { /* * this is really quite bogus, since it does an in-place encryption... */ if (encrypt_output) { encrypt_output(i->ptr, i->length); return 1; } return 0; } int #ifdef CK_ANSIC decrypt_ks_stream(struct kstream_data_block *i, struct kstream_data_block *o) #else decrypt_ks_stream(i,o) struct kstream_data_block *i; struct kstream_data_block *o; #endif { unsigned int len; /* * this is really quite bogus, since it does an in-place decryption... */ if (decrypt_input) { for (len = 0 ; len < i->length ; len++) ((unsigned char *)i->ptr)[len] = decrypt_input(((unsigned char *)i->ptr)[len]); return 1; } return 0; } int #ifdef CK_ANSIC decrypt_ks_hack(unsigned char *buf, int cnt) #else decrypt_ks_hack(buf,cnt) unsigned char *buf; int cnt; #endif { int len; /* * this is really quite bogus, since it does an in-place decryption... */ for (len = 0 ; len < cnt ; len++) buf[len] = decrypt_input(buf[len]); #ifdef DEBUG ckhexdump("decrypt ks hack", buf, cnt); #endif return 1; } /* * parsedat[0] == the suboption we might be negotiating, */ int #ifdef CK_ANSIC encrypt_parse(unsigned char *parsedat, int end_sub) #else encrypt_parse(parsedat,end_sub) unsigned char *parsedat; int end_sub; #endif { int rc = 0; switch(parsedat[1]) { case ENCRYPT_START: rc = encrypt_start(parsedat + 2, end_sub - 2); break; case ENCRYPT_END: rc = encrypt_end(); break; case ENCRYPT_SUPPORT: rc = encrypt_support(parsedat + 2, end_sub - 2); break; case ENCRYPT_REQSTART: rc = encrypt_request_start(); break; case ENCRYPT_REQEND: /* * We can always send an REQEND so that we cannot * get stuck encrypting. We should only get this * if we have been able to get in the correct mode * anyhow. */ rc = encrypt_request_end(); break; case ENCRYPT_IS: rc = encrypt_is(parsedat + 2, end_sub - 2); break; case ENCRYPT_REPLY: rc = encrypt_reply(parsedat + 2, end_sub - 2); break; case ENCRYPT_ENC_KEYID: rc = encrypt_enc_keyid(parsedat + 2, end_sub - 2); break; case ENCRYPT_DEC_KEYID: rc = encrypt_dec_keyid(parsedat + 2, end_sub - 2); break; default: rc = -1; break; } return(rc); } /* XXX */ Encryptions * #ifdef CK_ANSIC findencryption(int type) #else findencryption(type) int type; #endif { Encryptions *ep = encryptions; if (!(I_SUPPORT_ENCRYPT & remote_supports_decrypt & typemask(type))) return(0); while (ep->type && ep->type != type) ++ep; return(ep->type ? ep : 0); } Encryptions * #ifdef CK_ANSIC finddecryption(int type) #else finddecryption(type) int type; #endif { Encryptions *ep = encryptions; if (!(I_SUPPORT_DECRYPT & remote_supports_encrypt & typemask(type))) return(0); while (ep->type && ep->type != type) ++ep; return(ep->type ? ep : 0); } #define MAXKEYLEN 64 static struct key_info { unsigned char keyid[MAXKEYLEN]; int keylen; int dir; int *modep; Encryptions *(*getcrypt)(); } ki[2] = { { { 0 }, 0, DIR_ENCRYPT, &encrypt_mode, findencryption }, { { 0 }, 0, DIR_DECRYPT, &decrypt_mode, finddecryption }, }; VOID #ifdef CK_ANSIC encrypt_init(kstream iks, int type) #else encrypt_init(iks, type) kstream iks; int type; #endif { Encryptions *ep = encryptions; i_support_encrypt = i_support_decrypt = 0; remote_supports_encrypt = remote_supports_decrypt = 0; i_wont_support_encrypt = i_wont_support_decrypt = 0; encrypt_mode = 0; decrypt_mode = 0; encrypt_output = NULL; decrypt_input = NULL; ki[0].keylen = 0; memset(ki[0].keyid,0,MAXKEYLEN); ki[1].keylen = 0; memset(ki[1].keyid,0,MAXKEYLEN); havesessionkey = 0; autoencrypt = 1; autodecrypt = 1; EncryptKSGlobalHack = iks; EncryptType = type; str_send[0] = IAC; str_send[1] = SB; str_send[2] = TELOPT_ENCRYPTION; str_send[3] = ENCRYPT_SUPPORT; str_suplen = 4; while (ep->type) { if ( EncryptType == ENCTYPE_ANY || EncryptType == ep->type ) { #ifdef DEBUG if (encrypt_debug_mode) { sprintf(dbgbuf, ">>>I will support %s\n", ENCTYPE_NAME(ep->type)); /* safe */ debug(F110,"encrypt_init",dbgbuf,0); } #endif i_support_encrypt |= typemask(ep->type); i_support_decrypt |= typemask(ep->type); if ((i_wont_support_decrypt & typemask(ep->type)) == 0) if ((str_send[str_suplen++] = ep->type) == IAC) str_send[str_suplen++] = IAC; } if (ep->init) (*ep->init)(0); ++ep; } str_send[str_suplen++] = IAC; str_send[str_suplen++] = SE; } VOID #ifdef CK_ANSIC encrypt_send_support(VOID) #else encrypt_send_support() #endif { Encryptions *ep = encryptions; #ifdef CK_SSL if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) return; #endif /* CK_SSL */ str_send[0] = IAC; str_send[1] = SB; str_send[2] = TELOPT_ENCRYPTION; str_send[3] = ENCRYPT_SUPPORT; str_suplen = 4; while (ep->type) { if ( EncryptType == ENCTYPE_ANY || EncryptType == ep->type ) { #ifdef DEBUG if (encrypt_debug_mode) { sprintf(dbgbuf, ">>>I will support %s\n", ENCTYPE_NAME(ep->type)); /* safe */ debug(F110,"encrypt_send_support",dbgbuf,0); } #endif if ((i_wont_support_decrypt & typemask(ep->type)) == 0) if ((str_send[str_suplen++] = ep->type) == IAC) str_send[str_suplen++] = IAC; } ++ep; } str_send[str_suplen++] = IAC; str_send[str_suplen++] = SE; /* * If the user has requested that decryption start * immediatly, then send a "REQUEST START" before * we negotiate the type. */ if (autodecrypt) encrypt_send_request_start(); if (deblog || tn_deb || debses) { int i; sprintf(tn_msg,"TELNET SENT SB %s SUPPORT ", TELOPT(TELOPT_ENCRYPTION)); /* safe */ for ( i=4;i 0) { debug(F101,"XXX cnt","",cnt); type = *typelist++; debug(F101,"XXX type","",type); debug(F101,"XXX ENCTYPE_ANY","",ENCTYPE_ANY); if ( EncryptType == ENCTYPE_ANY || EncryptType == type ) { #ifdef DEBUG if (encrypt_debug_mode) { sprintf(dbgbuf, ">>>Remote supports %s (%d)\n", ENCTYPE_NAME(type), type); /* safe */ debug(F110,"encrypt_support",dbgbuf,0); } #endif if ((type < ENCTYPE_CNT) && (I_SUPPORT_ENCRYPT & typemask(type))) { remote_supports_decrypt |= typemask(type); if (use_type == 0) use_type = type; } } } debug(F101,"XXX use_type","",use_type); if (use_type) { ep = findencryption(use_type); if (!ep) { debug(F111,"encrypt_support","findencryption == NULL",use_type); return(-1); } debug(F100,"XXX ep not NULL","",0); type = ep->start ? (*ep->start)(DIR_ENCRYPT, 0) : 0; debug(F101,"XXX new type","",type); #ifdef DEBUG if (encrypt_debug_mode) { sprintf(dbgbuf, ">>>(*ep->start)() %s returned %d (%s)\n", ENCTYPE_NAME(use_type), type, ENCRYPT_NAME(type)); /* safe */ debug(F110,"encrypt_support",dbgbuf,0); } #endif if (type < 0) { debug(F111,"encrypt_support","type < 0",type); return(-1); } encrypt_mode = use_type; if (type == 0) encrypt_start_output(use_type); debug(F111,"encrypt_support","success",type); return(0); } debug(F111,"encrypt_support","failed",use_type); return(-1); } int #ifdef CK_ANSIC encrypt_is(unsigned char *data, int cnt) #else encrypt_is(data, cnt) unsigned char *data; int cnt; #endif /* CK_ANSIC */ { Encryptions *ep; register int type, ret; if (--cnt < 0) return(-1); type = *data++; if (type < ENCTYPE_CNT) remote_supports_encrypt |= typemask(type); if (!(ep = finddecryption(type))) { #ifdef DEBUG if (encrypt_debug_mode) { sprintf(dbgbuf, ">>>encrypt_is: " "Can't find type %s (%d) for initial negotiation\n", ENCTYPE_NAME_OK(type) ? ENCTYPE_NAME(type) : "(unknown)", type); /* safe */ debug(F110,"encrypt_is",dbgbuf,0); } #endif return(-1); } if (!ep->is) { #ifdef DEBUG if (encrypt_debug_mode) { sprintf(dbgbuf, ">>>encrypt_is: " "No initial negotiation needed for type %s (%d)\n", ENCTYPE_NAME_OK(type) ? ENCTYPE_NAME(type) : "(unknown)", type); /* safe */ debug(F110,"encrypt_is",dbgbuf,0); } #endif ret = 0; } else { ret = (*ep->is)(data, cnt); #ifdef DEBUG if (encrypt_debug_mode) { sprintf(dbgbuf, "encrypt_is: " "(*ep->is)(%x, %d) returned %s(%d)\n", data, cnt, (ret < 0) ? "FAIL " : (ret == 0) ? "SUCCESS " : "MORE_TO_DO ", ret); /* safe */ debug(F110,"encrypt_is",dbgbuf,0); } #endif } if (ret < 0) { autodecrypt = 0; return(-1); } else { decrypt_mode = type; if (ret == 0 && autodecrypt) { encrypt_send_request_start(); } } return(0); } int #ifdef CK_ANSIC encrypt_reply(unsigned char *data, int cnt) #else encrypt_reply(data, cnt) unsigned char *data; int cnt; #endif { Encryptions *ep; register int ret, type; if (--cnt < 0) return(-1); type = *data++; if (!(ep = findencryption(type))) { #ifdef DEBUG if (encrypt_debug_mode) { sprintf(dbgbuf, ">>>Can't find type %s (%d) for initial negotiation\n", ENCTYPE_NAME_OK(type) ? ENCTYPE_NAME(type) : "(unknown)", type); /* safe */ debug(F110,"encrypt_reply",dbgbuf,0); } #endif return(-1); } if (!ep->reply) { #ifdef DEBUG if (encrypt_debug_mode) { sprintf(dbgbuf, ">>>No initial negotiation needed for type %s (%d)\n", ENCTYPE_NAME_OK(type) ? ENCTYPE_NAME(type) : "(unknown)", type); /* safe */ debug(F110,"encrypt_reply",dbgbuf,0); } #endif ret = 0; } else { ret = (*ep->reply)(data, cnt); #ifdef DEBUG if (encrypt_debug_mode) { sprintf(dbgbuf, "(*ep->reply)(%x, %d) returned %s(%d)\n", data, cnt, (ret < 0) ? "FAIL " : (ret == 0) ? "SUCCESS " : "MORE_TO_DO ", ret); /* safe */ debug(F110,"encrypt_reply",dbgbuf,0); } #endif } #ifdef DEBUG if (encrypt_debug_mode) { sprintf(dbgbuf, ">>>encrypt_reply returned %d\n", ret); /* safe */ debug(F110,"encrypt_reply",dbgbuf,0); } #endif if (ret < 0) { autoencrypt = 0; return(-1); } else { encrypt_mode = type; if (ret == 0 && autoencrypt) encrypt_start_output(type); } return(0); } /* * Called when a ENCRYPT START command is received. */ int #ifdef CK_ANSIC encrypt_start(unsigned char *data, int cnt) #else encrypt_start(data, cnt) unsigned char *data; int cnt; #endif { Encryptions *ep; if (!decrypt_mode) { /* * Something is wrong. We should not get a START * command without having already picked our * decryption scheme. Send a REQUEST-END to * attempt to clear the channel... */ encrypt_send_request_end(); printf("Authentication error!\n%s\n", "Warning, Cannot decrypt input stream!!!"); return(-1); } if (ep = finddecryption(decrypt_mode)) { if ( decrypt_input != ep->input ) { decrypt_input = ep->input; EncryptKSGlobalHack->decrypt = decrypt_ks_stream; EncryptKSGlobalHack->decrypt_type = ep->type; if (encrypt_verbose) { sprintf(dbgbuf, "Input is now decrypted with type %s", ENCTYPE_NAME(decrypt_mode)); /* safe */ debug(F110,"encrypt_start",dbgbuf,0); printf("%s\n",dbgbuf); } #ifdef DEBUG if (encrypt_debug_mode) { sprintf(dbgbuf, ">>>Start to decrypt input with type %s", ENCTYPE_NAME(decrypt_mode)); /* safe */ debug(F110,"ck_crp",dbgbuf,0); } #endif } } else { char buf[1024]; sprintf(buf, "Warning, Cannot decrypt type %s (%d)!!!", ENCTYPE_NAME_OK(decrypt_mode) ? ENCTYPE_NAME(decrypt_mode) : "(unknown)", decrypt_mode); /* safe */ printf("Authentication error!\n%s\n",buf); encrypt_send_request_end(); return(-1); } return(0); } int #ifdef CK_ANSIC encrypt_dont_support(int type) #else encrypt_dont_support(type) int type; #endif { i_wont_support_encrypt |= typemask(type); i_wont_support_decrypt |= typemask(type); return(0); } int #ifdef CK_ANSIC encrypt_session_key(Session_Key *key, int server) #else encrypt_session_key(key, server) Session_Key *key; int server; #endif { Encryptions *ep = encryptions; if (havesessionkey) return(0); havesessionkey = 1; while (ep->type) { debug(F111,"encrypt_session_key",ep->name,ep->type); if (ep->session) { if ((*ep->session)(key, server) < 0) { i_wont_support_encrypt |= typemask(ep->type); i_wont_support_decrypt |= typemask(ep->type); } } ++ep; } debug(F111,"encrypt_session_key (done)",ep->name,ep->type); return(0); } /* * Called when ENCRYPT END is received. */ int #ifdef CK_ANSIC encrypt_end(VOID) #else encrypt_end() #endif { decrypt_input = NULL; EncryptKSGlobalHack->decrypt = NULL; EncryptKSGlobalHack->decrypt_type = ENCTYPE_ANY; #ifdef DEBUG if (encrypt_debug_mode) { sprintf(dbgbuf, ">>>Input is back to clear text"); /* safe */ debug(F110,"encrypt_end",dbgbuf,0); } #endif if (encrypt_verbose) { sprintf(dbgbuf, "Input is now clear text"); /* safe */ debug(F110,"encrypt_end",dbgbuf,0); printf("%s\n",dbgbuf); } return(0); } /* * Called when ENCRYPT REQUEST-END is received. */ int #ifdef CK_ANSIC encrypt_request_end(VOID) #else encrypt_request_end() #endif { encrypt_send_end(); return(0); } /* * Called when ENCRYPT REQUEST-START is received. If we receive * this before a type is picked, then that indicates that the * other side wants us to start encrypting data as soon as we * can. */ int #ifdef CK_ANSIC encrypt_request_start(VOID) #else encrypt_request_start() #endif { if (encrypt_mode != 0) encrypt_start_output(encrypt_mode); return(0); } static unsigned char str_keyid[(MAXKEYLEN*2)+5] = { IAC, SB, TELOPT_ENCRYPTION }; _PROTOTYP(int encrypt_keyid,(struct key_info *,unsigned char *,int)); int #ifdef CK_ANSIC encrypt_enc_keyid(unsigned char *keyid, int len) #else encrypt_enc_keyid(keyid, len) unsigned char *keyid; int len; #endif { return(encrypt_keyid(&ki[1], keyid, len)); } int #ifdef CK_ANSIC encrypt_dec_keyid(unsigned char *keyid, int len) #else encrypt_dec_keyid(keyid, len) unsigned char *keyid; int len; #endif /* CK_ANSIC */ { return(encrypt_keyid(&ki[0], keyid, len)); } int #ifdef CK_ANSIC encrypt_keyid(struct key_info *kp, unsigned char *keyid, int len) #else encrypt_keyid(kp, keyid, len) struct key_info *kp; unsigned char *keyid; int len; #endif { Encryptions *ep; int dir = kp->dir; register int ret = 0; if (!(ep = (*kp->getcrypt)(*kp->modep))) { if (len == 0) return(-1); kp->keylen = 0; } else if (len == 0 || len > MAXKEYLEN) { /* * Empty option or Key too long, indicates a failure. */ if (kp->keylen == 0) return(-1); kp->keylen = 0; if (ep->keyid) (void)(*ep->keyid)(dir, kp->keyid, &kp->keylen); } else if ((len != kp->keylen) || (memcmp(keyid, kp->keyid, len) != 0)) { /* * Length or contents are different */ kp->keylen = len; memcpy(kp->keyid, keyid, len); /* length < MAXKEYLEN */ if (ep->keyid) (void)(*ep->keyid)(dir, kp->keyid, &kp->keylen); } else { if (ep->keyid) ret = (*ep->keyid)(dir, kp->keyid, &kp->keylen); if ((ret == 0) && (dir == DIR_ENCRYPT) && autoencrypt) encrypt_start_output(*kp->modep); return(0); } encrypt_send_keyid(dir, kp->keyid, kp->keylen, 0); return(0); } int #ifdef CK_ANSIC encrypt_send_keyid(int dir, unsigned char *keyid, int keylen, int saveit) #else encrypt_send_keyid(dir, keyid, keylen, saveit) int dir; unsigned char *keyid; int keylen; int saveit; #endif { unsigned char *strp; #ifdef CK_SSL if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) return(0); #endif /* CK_SSL */ str_keyid[3] = (dir == DIR_ENCRYPT) ? ENCRYPT_ENC_KEYID : ENCRYPT_DEC_KEYID; if (saveit && keylen <= MAXKEYLEN) { struct key_info *kp = &ki[(dir == DIR_ENCRYPT) ? 0 : 1]; memcpy(kp->keyid, keyid, keylen); kp->keylen = keylen; } for (strp = &str_keyid[4]; keylen > 0; --keylen) { if ((*strp++ = *keyid++) == IAC) *strp++ = IAC; } *strp++ = IAC; *strp++ = SE; if (deblog || tn_deb || debses) { int i; sprintf(tn_msg,"TELNET SENT SB %s %s ", TELOPT(TELOPT_ENCRYPTION), (dir == DIR_ENCRYPT) ? "ENC-KEYID" : "DEC-KEYID"); /* safe */ tn_hex((CHAR *)tn_msg,TN_MSG_LEN,&str_keyid[4],strp-str_keyid-2-4); ckstrncat(tn_msg,"IAC SE",TN_MSG_LEN); debug(F100,tn_msg,"",0); if (tn_deb || debses) tn_debug(tn_msg); } #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif ttol(str_keyid, strp - str_keyid); #ifdef OS2 ReleaseTelnetMutex(); #endif return(0); } VOID #ifdef CK_ANSIC encrypt_auto(int on) #else encrypt_auto(on) int on; #endif { if (on < 0) autoencrypt ^= 1; else autoencrypt = on ? 1 : 0; } VOID #ifdef CK_ANSIC decrypt_auto(int on) #else decrypt_auto(on) int on; #endif { if (on < 0) autodecrypt ^= 1; else autodecrypt = on ? 1 : 0; } VOID #ifdef CK_ANSIC encrypt_start_output(int type) #else encrypt_start_output(type) int type; #endif { Encryptions *ep; register unsigned char *p; register int i; #ifdef CK_SSL if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) return; #endif /* CK_SSL */ if (!(ep = findencryption(type))) { #ifdef DEBUG if (encrypt_debug_mode) { sprintf(dbgbuf, ">>>Can't encrypt with type %s (%d)\n", ENCTYPE_NAME_OK(type) ? ENCTYPE_NAME(type) : "(unknown)", type); /* safe */ debug(F110,"encrypt_start_output",dbgbuf,0); } #endif return; } if (ep->start) { i = (*ep->start)(DIR_ENCRYPT, 0); #ifdef DEBUG if (encrypt_debug_mode) { sprintf(dbgbuf, ">>>Encrypt start: %s (%d) %s\n", (i < 0) ? "failed" : "initial negotiation in progress", i, ENCTYPE_NAME(type)); /* safe */ debug(F110,"encrypt_start_output",dbgbuf,0); } #endif if (i) return; } if ( encrypt_output != ep->output ) { p = str_start; *p++ = IAC; *p++ = SB; *p++ = TELOPT_ENCRYPTION; *p++ = ENCRYPT_START; for (i = 0; i < ki[0].keylen; ++i) { if (( *p++ = ki[0].keyid[i]) == IAC) *p++ = IAC; } *p++ = IAC; *p++ = SE; if (deblog || tn_deb || debses) { int i; sprintf(tn_msg,"TELNET SENT SB %s START ", TELOPT(TELOPT_ENCRYPTION)); /* safe */ tn_hex((CHAR *)tn_msg,TN_MSG_LEN,&str_start[4],p-str_start-2-4); ckstrncat(tn_msg,"IAC SE",TN_MSG_LEN); debug(F100,tn_msg,"",0); if (tn_deb || debses) tn_debug(tn_msg); } #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif ttol(str_start, p - str_start); #ifdef OS2 ReleaseTelnetMutex(); #endif /* * If we are already encrypting in some mode, then * encrypt the ring (which includes our request) in * the old mode, mark it all as "clear text" and then * switch to the new mode. */ encrypt_output = ep->output; EncryptKSGlobalHack->encrypt = encrypt_ks_stream; EncryptKSGlobalHack->encrypt_type = type; encrypt_mode = type; #ifdef DEBUG if (encrypt_debug_mode) { sprintf(dbgbuf, ">>>Started to encrypt output with type %s", ENCTYPE_NAME(type)); /* safe */ debug(F110,"encrypt_start_output",dbgbuf,0); } #endif if (encrypt_verbose) { sprintf(dbgbuf, "Output is now encrypted with type %s", ENCTYPE_NAME(type)); /* safe */ debug(F110,"encrypt_start_output",dbgbuf,0); printf("%s\n",dbgbuf); } } } VOID #ifdef CK_ANSIC encrypt_send_end(VOID) #else encrypt_send_end() #endif { #ifdef CK_SSL if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) return; #endif /* CK_SSL */ if (!encrypt_output) return; str_end[0] = IAC; str_end[1] = SB; str_end[2] = TELOPT_ENCRYPTION; str_end[3] = ENCRYPT_END; str_end[4] = IAC; str_end[5] = SE; if (deblog || tn_deb || debses) { int i; sprintf(tn_msg,"TELNET SENT SB %s END IAC SE", TELOPT(TELOPT_ENCRYPTION)); /* safe */ debug(F100,tn_msg,"",0); if (tn_deb || debses) tn_debug(tn_msg); } #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif ttol(str_end, sizeof(str_end)); #ifdef OS2 ReleaseTelnetMutex(); #endif encrypt_output = 0; EncryptKSGlobalHack->encrypt = NULL; EncryptKSGlobalHack->encrypt_type = ENCTYPE_ANY; #ifdef DEBUG if (encrypt_debug_mode) { sprintf(dbgbuf, ">>>Output is back to clear text"); /* safe */ debug(F110,"encrypt_send_end",dbgbuf,0); } #endif if (encrypt_verbose) { sprintf(dbgbuf, "Output is now clear text"); /* safe */ debug(F110,"encrypt_send_end",dbgbuf,0); printf("%s\n",dbgbuf); } } VOID #ifdef CK_ANSIC encrypt_send_request_start(VOID) #else encrypt_send_request_start() #endif { register unsigned char *p; register int i; #ifdef CK_SSL if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) return; #endif /* CK_SSL */ p = str_start; *p++ = IAC; *p++ = SB; *p++ = TELOPT_ENCRYPTION; *p++ = ENCRYPT_REQSTART; for (i = 0; i < ki[1].keylen; ++i) { if (( *p++ = ki[1].keyid[i]) == IAC) *p++ = IAC; } *p++ = IAC; *p++ = SE; if (deblog || tn_deb || debses) { int i; sprintf(tn_msg,"TELNET SENT SB %s REQUEST-START ", TELOPT(TELOPT_ENCRYPTION)); /* safe */ tn_hex((CHAR *)tn_msg,TN_MSG_LEN,&str_start[4],p-str_start-2-4); ckstrncat(tn_msg,"IAC SE",TN_MSG_LEN); debug(F100,tn_msg,"",0); if (tn_deb || debses) tn_debug(tn_msg); } #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif ttol(str_start, p - str_start); #ifdef OS2 ReleaseTelnetMutex(); #endif if (encrypt_debug_mode) { sprintf(dbgbuf, ">>>Request input to be encrypted\n"); /* safe */ debug(F110,"encrypt_send_request_start",dbgbuf,0); } } VOID #ifdef CK_ANSIC encrypt_send_request_end(VOID) #else encrypt_send_request_end() #endif { #ifdef CK_SSL if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) return; #endif /* CK_SSL */ str_end[0] = IAC; str_end[1] = SB; str_end[2] = TELOPT_ENCRYPTION; str_end[3] = ENCRYPT_REQEND; str_end[4] = IAC; str_end[5] = SE; if (deblog || tn_deb || debses) { int i; sprintf(tn_msg,"TELNET SENT SB %s REQEND IAC SE", TELOPT(TELOPT_ENCRYPTION)); /* safe */ debug(F100,tn_msg,"",0); if (tn_deb || debses) tn_debug(tn_msg); } #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif ttol(str_end, sizeof(str_end)); #ifdef OS2 ReleaseTelnetMutex(); #endif if (encrypt_debug_mode) { sprintf(dbgbuf, ">>>Request input to be clear text\n"); /* safe */ debug(F110,"encrypt_send_request_end",dbgbuf,0); } } int #ifdef CK_ANSIC encrypt_is_encrypting(VOID) #else encrypt_is_encrypting() #endif { if (encrypt_output) return 1; return 0; } int #ifdef CK_ANSIC encrypt_is_decrypting(VOID) #else encrypt_is_decrypting() #endif { if (decrypt_input) return 1; return 0; } #ifdef DEBUG void encrypt_debug(mode) int mode; { encrypt_debug_mode = mode; } #endif #ifdef CK_DES /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* based on @(#)enc_des.c 8.1 (Berkeley) 6/4/93 */ #define CFB 0 #define OFB 1 #define NO_SEND_IV 1 #define NO_RECV_IV 2 #define NO_KEYID 4 #define IN_PROGRESS (NO_SEND_IV|NO_RECV_IV|NO_KEYID) #define SUCCESS 0 #define xFAILED -1 Schedule test_sched; struct des_stinfo { Block str_output; Block str_feed; Block str_iv; Block str_ikey; #ifdef MIT_CURRENT unsigned char str_keybytes[8]; krb5_keyblock str_key; #else /* MIT_CURRENT */ Schedule str_sched; int str_index; #endif /* MIT_CURRENT */ int str_flagshift; }; struct des_fb { #ifndef MIT_CURRENT Block krbdes_key; Schedule krbdes_sched; #endif /* MIT_CURRENT */ Block temp_feed; unsigned char fb_feed[64]; int need_start; int state[2]; int keyid[2]; int once; #ifdef MIT_CURRENT int validkey; #endif /* MIT_CURRENT */ struct des_stinfo streams[2]; }; static struct des_fb des_fb[2]; struct des3_stinfo { Block str_output; Block str_feed; Block str_iv; Block str_ikey[3]; Schedule str_sched[3]; int str_index; int str_flagshift; }; struct des3_fb { #ifndef MIT_CURRENT Block krbdes_key[3]; Schedule krbdes_sched[3]; #endif /* MIT_CURRENT */ Block temp_feed; unsigned char fb_feed[64]; int need_start; int state[2]; int keyid[2]; int once; #ifdef MIT_CURRENT int validkey; #endif /* MIT_CURRENT */ struct des3_stinfo streams[2]; }; static struct des3_fb des3_fb[2]; struct keyidlist { char *keyid; int keyidlen; char *key; int keylen; int flags; } keyidlist [] = { { "\0", 1, 0, 0, 0 }, /* default key of zero */ { 0, 0, 0, 0, 0 } }; #define KEYFLAG_MASK 03 #define KEYFLAG_NOINIT 00 #define KEYFLAG_INIT 01 #define KEYFLAG_OK 02 #define KEYFLAG_BAD 03 #define KEYFLAG_SHIFT 2 #define SHIFT_VAL(a,b) (KEYFLAG_SHIFT*((a)+((b)*2))) #define FB64_IV 1 #define FB64_IV_OK 2 #define FB64_IV_BAD 3 #define FB64_CHALLENGE 4 #define FB64_RESPONSE 5 void fb64_stream_iv P((Block, struct des_stinfo *)); void fb64_init P((struct des_fb *)); static int fb64_start P((struct des_fb *, int, int)); int fb64_is P((unsigned char *, int, struct des_fb *)); int fb64_reply P((unsigned char *, int, struct des_fb *)); static int fb64_session P((Session_Key *, int, struct des_fb *)); void fb64_stream_key P((Block, struct des_stinfo *)); int fb64_keyid P((int, unsigned char *, int *, struct des_fb *)); #ifdef MIT_CURRENT static void #ifdef CK_ANSIC ecb_encrypt(struct des_stinfo *stp, Block in, Block out) #else /* CKANSIC */ ecb_encrypt(stp, in, out) struct des_stinfo *stp; Block in; Block out; #endif /* CK_ANSIC */ { krb5_error_code code; krb5_data din; krb5_enc_data dout; din.length = 8; din.data = in; dout.ciphertext.length = 8; dout.ciphertext.data = out; dout.enctype = ENCTYPE_UNKNOWN; #ifdef CRYPT_DLL code = krb5_c_encrypt(*p_k5_context, &stp->str_key, 0, 0, &din, &dout); #else /* CRYPT_DLL */ code = krb5_c_encrypt(k5_context, &stp->str_key, 0, 0, &din, &dout); #endif /* CRYPT_DLL */ /* XXX I'm not sure what to do if this fails */ if (code) com_err("libtelnet", code, "encrypting stream data"); } #endif /* MIT_CURRENT */ void cfb64_init(server) int server; { fb64_init(&des_fb[CFB]); des_fb[CFB].fb_feed[4] = ENCTYPE_DES_CFB64; des_fb[CFB].streams[0].str_flagshift = SHIFT_VAL(0, CFB); des_fb[CFB].streams[1].str_flagshift = SHIFT_VAL(1, CFB); } void ofb64_init(server) int server; { fb64_init(&des_fb[OFB]); des_fb[OFB].fb_feed[4] = ENCTYPE_DES_OFB64; des_fb[CFB].streams[0].str_flagshift = SHIFT_VAL(0, OFB); des_fb[CFB].streams[1].str_flagshift = SHIFT_VAL(1, OFB); } void fb64_init(fbp) register struct des_fb *fbp; { memset((void *)fbp, 0, sizeof(*fbp)); fbp->state[0] = fbp->state[1] = xFAILED; fbp->fb_feed[0] = IAC; fbp->fb_feed[1] = SB; fbp->fb_feed[2] = TELOPT_ENCRYPTION; fbp->fb_feed[3] = ENCRYPT_IS; } /* * Returns: * -1: some error. Negotiation is done, encryption not ready. * 0: Successful, initial negotiation all done. * 1: successful, negotiation not done yet. * 2: Not yet. Other things (like getting the key from * Kerberos) have to happen before we can continue. */ int cfb64_start(dir, server) int dir; int server; { return(fb64_start(&des_fb[CFB], dir, server)); } int ofb64_start(dir, server) int dir; int server; { return(fb64_start(&des_fb[OFB], dir, server)); } static int fb64_start(fbp, dir, server) struct des_fb *fbp; int dir; int server; { int x; unsigned char *p; register int state; switch (dir) { case DIR_DECRYPT: /* * This is simply a request to have the other side * start output (our input). He will negotiate an * IV so we need not look for it. */ state = fbp->state[dir-1]; if (state == xFAILED) state = IN_PROGRESS; break; case DIR_ENCRYPT: state = fbp->state[dir-1]; if (state == xFAILED) state = IN_PROGRESS; else if ((state & NO_SEND_IV) == 0) break; #ifdef MIT_CURRENT if (!fbp->validkey) { fbp->need_start = 1; break; } #else /* MIT_CURRENT */ if (!VALIDKEY(fbp->krbdes_key)) { fbp->need_start = 1; break; } #endif /* MIT_CURRENT */ state &= ~NO_SEND_IV; state |= NO_RECV_IV; /* * Create a random feed and send it over. */ #ifdef MIT_CURRENT { krb5_data d; krb5_error_code code; d.data = fbp->temp_feed; d.length = sizeof(fbp->temp_feed); #ifdef CRYPT_DLL if (code = krb5_c_random_make_octets(*p_k5_context,&d)) return(xFAILED); #else /* CRYPT_DLL */ if (code = krb5_c_random_make_octets(k5_context,&d)) return(xFAILED); #endif /* CRYPT_DLL */ } #else /* MIT_CURRENT */ des_new_random_key(fbp->temp_feed); des_ecb_encrypt(fbp->temp_feed, fbp->temp_feed, fbp->krbdes_sched, 1); #endif /* MIT_CURRENT */ p = fbp->fb_feed + 3; *p++ = ENCRYPT_IS; p++; *p++ = FB64_IV; for (x = 0; x < sizeof(Block); ++x) { if (( *p++ = fbp->temp_feed[x]) == IAC) *p++ = IAC; } *p++ = IAC; *p++ = SE; if (deblog || tn_deb || debses) { int i; sprintf(tn_msg, "TELNET SENT SB %s IS %s FB64_IV ", TELOPT(fbp->fb_feed[2]), enctype_names[fbp->fb_feed[4]]); /* safe */ tn_hex((CHAR *)tn_msg,TN_MSG_LEN,&fbp->fb_feed[6], (p-fbp->fb_feed)-2-6); ckstrncat(tn_msg,"IAC SE",TN_MSG_LEN); debug(F100,tn_msg,"",0); if (tn_deb || debses) tn_debug(tn_msg); } #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif ttol(fbp->fb_feed, p - fbp->fb_feed); #ifdef OS2 ReleaseTelnetMutex(); #endif break; default: return(xFAILED); } return(fbp->state[dir-1] = state); } /* * Returns: * -1: some error. Negotiation is done, encryption not ready. * 0: Successful, initial negotiation all done. * 1: successful, negotiation not done yet. */ int cfb64_is(data, cnt) unsigned char *data; int cnt; { return(fb64_is(data, cnt, &des_fb[CFB])); } int ofb64_is(data, cnt) unsigned char *data; int cnt; { return(fb64_is(data, cnt, &des_fb[OFB])); } int fb64_is(data, cnt, fbp) unsigned char *data; int cnt; struct des_fb *fbp; { unsigned char *p; register int state = fbp->state[DIR_DECRYPT-1]; if (cnt-- < 1) goto failure; #ifdef CK_SSL if (!TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) #endif /* CK_SSL */ switch (*data++) { case FB64_IV: if (cnt != sizeof(Block)) { #ifdef DEBUG if (encrypt_debug_mode) printf("CFB64: initial vector failed on size\r\n"); #endif state = xFAILED; goto failure; } #ifdef DEBUG if (encrypt_debug_mode) { printf("CFB64: initial vector received\r\n"); printf("Initializing Decrypt stream\r\n"); } #endif fb64_stream_iv((void *)data, &fbp->streams[DIR_DECRYPT-1]); p = fbp->fb_feed + 3; *p++ = ENCRYPT_REPLY; p++; *p++ = FB64_IV_OK; *p++ = IAC; *p++ = SE; if (deblog || tn_deb || debses) { int i; sprintf(tn_msg, "TELNET SENT SB %s REPLY %s FB64_IV_OK ", TELOPT(fbp->fb_feed[2]), enctype_names[fbp->fb_feed[4]]); /* safe */ tn_hex((CHAR *)tn_msg,TN_MSG_LEN,&fbp->fb_feed[6], (p-fbp->fb_feed)-2-6); ckstrncat(tn_msg,"IAC SE",TN_MSG_LEN); debug(F100,tn_msg,"",0); if (tn_deb || debses) tn_debug(tn_msg); } #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif ttol(fbp->fb_feed, p - fbp->fb_feed); #ifdef OS2 ReleaseTelnetMutex(); #endif state = IN_PROGRESS; break; default: #if 0 if (encrypt_debug_mode) { printf("Unknown option type: %d\r\n", *(data-1)); printf("\r\n"); } #endif /* FALL THROUGH */ failure: /* * We failed. Send an FB64_IV_BAD option * to the other side so it will know that * things failed. */ p = fbp->fb_feed + 3; *p++ = ENCRYPT_REPLY; p++; *p++ = FB64_IV_BAD; *p++ = IAC; *p++ = SE; if (deblog || tn_deb || debses) { int i; sprintf(tn_msg, "TELNET SENT SB %s REPLY %s FB64_IV_BAD ", TELOPT(fbp->fb_feed[2]), enctype_names[fbp->fb_feed[4]]); /* safe */ tn_hex((CHAR *)tn_msg,TN_MSG_LEN,&fbp->fb_feed[6], (p-fbp->fb_feed)-2-6); ckstrncat(tn_msg,"IAC SE",TN_MSG_LEN); debug(F100,tn_msg,"",0); if (tn_deb || debses) tn_debug(tn_msg); } #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif ttol(fbp->fb_feed, p - fbp->fb_feed); #ifdef OS2 ReleaseTelnetMutex(); #endif break; } return(fbp->state[DIR_DECRYPT-1] = state); } /* * Returns: * -1: some error. Negotiation is done, encryption not ready. * 0: Successful, initial negotiation all done. * 1: successful, negotiation not done yet. */ int cfb64_reply(data, cnt) unsigned char *data; int cnt; { return(fb64_reply(data, cnt, &des_fb[CFB])); } int ofb64_reply(data, cnt) unsigned char *data; int cnt; { return(fb64_reply(data, cnt, &des_fb[OFB])); } int fb64_reply(data, cnt, fbp) unsigned char *data; int cnt; struct des_fb *fbp; { register int state = fbp->state[DIR_ENCRYPT-1]; if (cnt-- < 1) goto failure; switch (*data++) { case FB64_IV_OK: fb64_stream_iv(fbp->temp_feed, &fbp->streams[DIR_ENCRYPT-1]); if (state == xFAILED) state = IN_PROGRESS; state &= ~NO_RECV_IV; encrypt_send_keyid(DIR_ENCRYPT, (unsigned char *)"\0", 1, 1); break; case FB64_IV_BAD: memset(fbp->temp_feed, 0, sizeof(Block)); fb64_stream_iv(fbp->temp_feed, &fbp->streams[DIR_ENCRYPT-1]); state = xFAILED; break; default: #if 0 if (encrypt_debug_mode) { printf("Unknown option type: %d\r\n", data[-1]); printf("\r\n"); } #endif /* FALL THROUGH */ failure: state = xFAILED; break; } return(fbp->state[DIR_ENCRYPT-1] = state); } int cfb64_session(key, server) Session_Key *key; int server; { return(fb64_session(key, server, &des_fb[CFB])); } int ofb64_session(key, server) Session_Key *key; int server; { return(fb64_session(key, server, &des_fb[OFB])); } static int fb64_session(key, server, fbp) Session_Key *key; int server; struct des_fb *fbp; { int rc=0; int use2keys; struct des_stinfo * s_stream; struct des_stinfo * c_stream; if(server) { s_stream = &fbp->streams[DIR_ENCRYPT-1]; c_stream = &fbp->streams[DIR_DECRYPT-1]; } else { s_stream = &fbp->streams[DIR_DECRYPT-1]; c_stream = &fbp->streams[DIR_ENCRYPT-1]; } if (!key || key->length < sizeof(Block)) { CHAR buf[80]; sprintf((char *)buf,"Can't set DES session key (%d < %d)", key ? key->length : 0, sizeof(Block)); /* safe */ #ifdef DEBUG if (encrypt_debug_mode) printf("%s\r\n",buf); #endif debug(F110,"fb64_session",buf,0); return(-1); } use2keys = (key->type == SK_DES || key->length < 2 * sizeof(Block)) ? 0 : 1; #ifdef MIT_CURRENT if(use2keys) { memcpy((void *) fbp->keybytes, (void *) (key->data + sizeof(Block)), sizeof(Block)); des_fixup_key_parity(fbp->); fb64_stream_key(fbp->krbdes_key, s_stream); } memcpy((void *)fbp->krbdes_key, (void *)key->data, sizeof(Block)); if (key->type != SK_DES) des_fixup_key_parity(fbp->krbdes_key); if(!use2keys) fb64_stream_key(fbp->krbdes_key, s_stream); fb64_stream_key(fbp->krbdes_key, c_stream); fbp->validkey = 1; fb64_stream_key(key->data, &fbp->streams[DIR_ENCRYPT-1]); fb64_stream_key(key->data, &fbp->streams[DIR_DECRYPT-1]); #else /* MIT_CURRENT */ if(use2keys) { memcpy((void *) fbp->krbdes_key, (void *) (key->data + sizeof(Block)), sizeof(Block)); des_fixup_key_parity(fbp->krbdes_key); fb64_stream_key(fbp->krbdes_key, s_stream); } memcpy((void *)fbp->krbdes_key, (void *)key->data, sizeof(Block)); if (key->type != SK_DES) des_fixup_key_parity(fbp->krbdes_key); if(!use2keys) fb64_stream_key(fbp->krbdes_key, s_stream); fb64_stream_key(fbp->krbdes_key, c_stream); if (fbp->once == 0) { des_set_random_generator_seed(fbp->krbdes_key); fbp->once = 1; } memset(fbp->krbdes_sched,0,sizeof(Schedule)); ckhexdump("fb64_session_key",fbp->krbdes_key,8); rc = des_key_sched(fbp->krbdes_key, fbp->krbdes_sched); if ( rc == -1 ) { printf("?Invalid DES key specified for encryption\n"); debug(F110,"fb64_session_key", "invalid DES Key specified for encryption",0); } else if ( rc == -2 ) { printf("?Weak DES key specified for encryption\n"); debug(F110,"fb64_session_key", "weak DES Key specified for encryption",0); } else if ( rc != 0 ) { printf("?Key Schedule not created by encryption\n"); debug(F110,"fb64_session_key", "Key Schedule not created by encryption",0); } ckhexdump("fb64_session_key schedule",fbp->krbdes_sched,8*16); #endif /* MIT_CURRENT */ /* * Now look to see if krbdes_start() was was waiting for * the key to show up. If so, go ahead an call it now * that we have the key. */ if (fbp->need_start) { fbp->need_start = 0; fb64_start(fbp, DIR_ENCRYPT, server); } return(0); } /* * We only accept a keyid of 0. If we get a keyid of * 0, then mark the state as SUCCESS. */ int cfb64_keyid(dir, kp, lenp) int dir, *lenp; unsigned char *kp; { return(fb64_keyid(dir, kp, lenp, &des_fb[CFB])); } int ofb64_keyid(dir, kp, lenp) int dir, *lenp; unsigned char *kp; { return(fb64_keyid(dir, kp, lenp, &des_fb[OFB])); } int fb64_keyid(dir, kp, lenp, fbp) int dir, *lenp; unsigned char *kp; struct des_fb *fbp; { register int state = fbp->state[dir-1]; if (*lenp != 1 || (*kp != '\0')) { *lenp = 0; return(state); } if (state == xFAILED) state = IN_PROGRESS; state &= ~NO_KEYID; return(fbp->state[dir-1] = state); } #if 0 void fb64_printsub(data, cnt, buf, buflen, type) unsigned char *data, *buf, *type; int cnt, buflen; { char lbuf[64]; register int i; char *cp; buf[buflen-1] = '\0'; /* make sure it's NULL terminated */ buflen -= 1; switch(data[2]) { case FB64_IV: sprintf(lbuf, "%s_IV", type); cp = lbuf; goto common; case FB64_IV_OK: sprintf(lbuf, "%s_IV_OK", type); cp = lbuf; goto common; case FB64_IV_BAD: sprintf(lbuf, "%s_IV_BAD", type); cp = lbuf; goto common; case FB64_CHALLENGE: sprintf(lbuf, "%s_CHALLENGE", type); cp = lbuf; goto common; case FB64_RESPONSE: sprintf(lbuf, "%s_RESPONSE", type); cp = lbuf; goto common; default: sprintf(lbuf, " %d (unknown)", data[2]); cp = lbuf; common: for (; (buflen > 0) && (*buf = *cp++); buf++) buflen--; for (i = 3; i < cnt; i++) { sprintf(lbuf, " %d", data[i]); for (cp = lbuf; (buflen > 0) && (*buf = *cp++); buf++) buflen--; } break; } } void cfb64_printsub(data, cnt, buf, buflen) unsigned char *data, *buf; int cnt, buflen; { fb64_printsub(data, cnt, buf, buflen, "CFB64"); } void ofb64_printsub(data, cnt, buf, buflen) unsigned char *data, *buf; int cnt, buflen; { fb64_printsub(data, cnt, buf, buflen, "OFB64"); } #endif void fb64_stream_iv(seed, stp) Block seed; register struct des_stinfo *stp; { int rc=0; memcpy(stp->str_iv, seed, sizeof(Block)); memcpy(stp->str_output, seed, sizeof(Block)); memset(stp->str_sched,0,sizeof(Schedule)); ckhexdump("fb64_stream_iv",stp->str_ikey,8); #ifndef MIT_CURRENT rc = des_key_sched(stp->str_ikey, stp->str_sched); if ( rc == -1 ) { printf("?Invalid DES key specified for encryption\r\n"); debug(F110,"fb64_stream_iv", "invalid DES Key specified for encryption",0); } else if ( rc == -2 ) { printf("?Weak DES key specified for encryption\r\n"); debug(F110,"fb64_stream_iv", "weak DES Key specified for encryption",0); } else if ( rc != 0 ) { printf("?Key Schedule not created by encryption\r\n"); debug(F110,"fb64_stream_iv", "Key Schedule not created by encryption",0); } ckhexdump("fb64_stream_iv schedule",stp->str_sched,8*16); #endif /* MIT_CURRENT */ stp->str_index = sizeof(Block); } void fb64_stream_key(key, stp) Block key; register struct des_stinfo *stp; { int rc = 0; #ifdef MIT_CURRENT memcpy(stp->str_keybytes, key, sizeof(Block)); stp->str_key.length = 8; stp->str_key.contents = stp->str_keybytes; /* the original version of this code uses des ecb mode, but it only ever does one block at a time. cbc with a zero iv is identical */ stp->str_key.enctype = ENCTYPE_DES_CBC_RAW; #else /* MIT_CURRENT */ memcpy(stp->str_ikey, key, sizeof(Block)); memset(stp->str_sched,0,sizeof(Schedule)); ckhexdump("fb64_stream_key",key,8); rc = des_key_sched(key, stp->str_sched); if ( rc == -1 ) { printf("?Invalid DES key specified for encryption\r\n"); debug(F110,"fb64_stream_key", "invalid DES Key specified for encryption",0); } else if ( rc == -2 ) { printf("?Weak DES key specified for encryption\r\n"); debug(F110,"fb64_stream_key", "weak DES Key specified for encryption",0); } else if ( rc != 0 ) { printf("?Key Schedule not created by encryption\r\n"); debug(F110,"fb64_stream_key", "Key Schedule not created by encryption",0); } ckhexdump("fb64_stream_key schedule",stp->str_sched,8*16); #endif /* MIT_CURRENT */ memcpy(stp->str_output, stp->str_iv, sizeof(Block)); stp->str_index = sizeof(Block); } /* * DES 64 bit Cipher Feedback * * key --->+-----+ * +->| DES |--+ * | +-----+ | * | v * INPUT --(--------->(+)+---> DATA * | | * +-------------+ * * * Given: * iV: Initial vector, 64 bits (8 bytes) long. * Dn: the nth chunk of 64 bits (8 bytes) of data to encrypt (decrypt). * On: the nth chunk of 64 bits (8 bytes) of encrypted (decrypted) output. * * V0 = DES(iV, key) * On = Dn ^ Vn * V(n+1) = DES(On, key) */ void cfb64_encrypt(s, c) register unsigned char *s; int c; { register struct des_stinfo *stp = &des_fb[CFB].streams[DIR_ENCRYPT-1]; register int index; index = stp->str_index; while (c-- > 0) { if (index == sizeof(Block)) { Block b; #ifdef MIT_CURRENT ecb_encrypt(stp, stp->str_output, b); #else /* MIT_CURRENT */ des_ecb_encrypt(stp->str_output, b, stp->str_sched, 1); #endif /* MIT_CURRENT */ memcpy(stp->str_feed,b,sizeof(Block)); index = 0; } /* On encryption, we store (feed ^ data) which is cypher */ *s = stp->str_output[index] = (stp->str_feed[index] ^ *s); s++; index++; } stp->str_index = index; } int cfb64_decrypt(data) int data; { register struct des_stinfo *stp = &des_fb[CFB].streams[DIR_DECRYPT-1]; int index; if (data == -1) { /* * Back up one byte. It is assumed that we will * never back up more than one byte. If we do, this * may or may not work. */ if (stp->str_index) --stp->str_index; return(0); } index = stp->str_index++; if (index == sizeof(Block)) { Block b; #ifdef MIT_CURRENT ecb_encrypt(stp, stp->str_output, b); #else /* MIT_CURRENT */ des_ecb_encrypt(stp->str_output, b, stp->str_sched, 1); #endif /* MIT_CURRENT */ memcpy(stp->str_feed, b, sizeof(Block)); stp->str_index = 1; /* Next time will be 1 */ index = 0; /* But now use 0 */ } /* On decryption we store (data) which is cypher. */ stp->str_output[index] = data; return(data ^ stp->str_feed[index]); } /* * DES 64 bit Output Feedback * * key --->+-----+ * +->| DES |--+ * | +-----+ | * +-----------+ * v * INPUT -------->(+) ----> DATA * * Given: * iV: Initial vector, 64 bits (8 bytes) long. * Dn: the nth chunk of 64 bits (8 bytes) of data to encrypt (decrypt). * On: the nth chunk of 64 bits (8 bytes) of encrypted (decrypted) output. * * V0 = DES(iV, key) * V(n+1) = DES(Vn, key) * On = Dn ^ Vn */ void ofb64_encrypt(s, c) register unsigned char *s; int c; { register struct des_stinfo *stp = &des_fb[OFB].streams[DIR_ENCRYPT-1]; register int index; index = stp->str_index; while (c-- > 0) { if (index == sizeof(Block)) { Block b; #ifdef MIT_CURRENT ecb_encrypt(stp, stp->str_feed, b); #else /* MIT_CURRENT */ des_ecb_encrypt(stp->str_feed, b, stp->str_sched, 1); #endif /* MIT_CURRENT */ memcpy(stp->str_feed,b,sizeof(Block)); index = 0; } *s++ ^= stp->str_feed[index]; index++; } stp->str_index = index; } int ofb64_decrypt(data) int data; { register struct des_stinfo *stp = &des_fb[OFB].streams[DIR_DECRYPT-1]; int index; if (data == -1) { /* * Back up one byte. It is assumed that we will * never back up more than one byte. If we do, this * may or may not work. */ if (stp->str_index) --stp->str_index; return(0); } index = stp->str_index++; if (index == sizeof(Block)) { Block b; #ifdef MIT_CURRENT ecb_encrypt(stp, stp->str_feed, b); #else /* MIT_CURRENT */ des_ecb_encrypt(stp->str_feed, b, stp->str_sched, 1); #endif /* MIT_CURRENT */ memcpy(stp->str_feed, b, sizeof(Block)); stp->str_index = 1; /* Next time will be 1 */ index = 0; /* But now use 0 */ } return(data ^ stp->str_feed[index]); } void des3_fb64_stream_iv P((Block, struct des3_stinfo *)); void des3_fb64_init P((struct des3_fb *)); static int des3_fb64_start P((struct des3_fb *, int, int)); int des3_fb64_is P((unsigned char *, int, struct des3_fb *)); int des3_fb64_reply P((unsigned char *, int, struct des3_fb *)); static int des3_fb64_session P((Session_Key *, int, struct des3_fb *)); void des3_fb64_stream_key P((Block *, struct des3_stinfo *)); int des3_fb64_keyid P((int, unsigned char *, int *, struct des3_fb *)); void des3_cfb64_init(server) int server; { des3_fb64_init(&des3_fb[CFB]); des3_fb[CFB].fb_feed[4] = ENCTYPE_DES3_CFB64; des3_fb[CFB].streams[0].str_flagshift = SHIFT_VAL(0, CFB); des3_fb[CFB].streams[1].str_flagshift = SHIFT_VAL(1, CFB); } void des3_ofb64_init(server) int server; { des3_fb64_init(&des3_fb[OFB]); des3_fb[OFB].fb_feed[4] = ENCTYPE_DES3_OFB64; des3_fb[CFB].streams[0].str_flagshift = SHIFT_VAL(0, OFB); des3_fb[CFB].streams[1].str_flagshift = SHIFT_VAL(1, OFB); } void des3_fb64_init(fbp) register struct des3_fb *fbp; { memset((void *)fbp, 0, sizeof(*fbp)); fbp->state[0] = fbp->state[1] = xFAILED; fbp->fb_feed[0] = IAC; fbp->fb_feed[1] = SB; fbp->fb_feed[2] = TELOPT_ENCRYPTION; fbp->fb_feed[3] = ENCRYPT_IS; } /* * Returns: * -1: some error. Negotiation is done, encryption not ready. * 0: Successful, initial negotiation all done. * 1: successful, negotiation not done yet. * 2: Not yet. Other things (like getting the key from * Kerberos) have to happen before we can continue. */ int des3_cfb64_start(dir, server) int dir; int server; { return(des3_fb64_start(&des3_fb[CFB], dir, server)); } int des3_ofb64_start(dir, server) int dir; int server; { return(des3_fb64_start(&des3_fb[OFB], dir, server)); } static int des3_fb64_start(fbp, dir, server) struct des3_fb *fbp; int dir; int server; { int x; unsigned char *p; register int state; switch (dir) { case DIR_DECRYPT: /* * This is simply a request to have the other side * start output (our input). He will negotiate an * IV so we need not look for it. */ state = fbp->state[dir-1]; if (state == xFAILED) state = IN_PROGRESS; break; case DIR_ENCRYPT: state = fbp->state[dir-1]; if (state == xFAILED) state = IN_PROGRESS; else if ((state & NO_SEND_IV) == 0) break; if (!VALIDKEY(fbp->krbdes_key[0]) || !VALIDKEY(fbp->krbdes_key[1]) || !VALIDKEY(fbp->krbdes_key[2]) ) { fbp->need_start = 1; break; } state &= ~NO_SEND_IV; state |= NO_RECV_IV; /* * Create a random feed and send it over. */ des_new_random_key(fbp->temp_feed); #ifdef LIBDES des_ecb3_encrypt(fbp->temp_feed, fbp->temp_feed, fbp->krbdes_sched[0], fbp->krbdes_sched[1], fbp->krbdes_sched[2], 1); #else /* LIBDES */ des_ecb_encrypt(fbp->temp_feed, fbp->temp_feed, fbp->krbdes_sched[0], 1); des_ecb_encrypt(fbp->temp_feed, fbp->temp_feed, fbp->krbdes_sched[1], 0); des_ecb_encrypt(fbp->temp_feed, fbp->temp_feed, fbp->krbdes_sched[2], 1); #endif /* LIBDES */ p = fbp->fb_feed + 3; *p++ = ENCRYPT_IS; p++; *p++ = FB64_IV; for (x = 0; x < sizeof(Block); ++x) { if (( *p++ = fbp->temp_feed[x]) == IAC) *p++ = IAC; } *p++ = IAC; *p++ = SE; if (deblog || tn_deb || debses) { int i; sprintf(tn_msg, "TELNET SENT SB %s IS %s FB64_IV ", TELOPT(fbp->fb_feed[2]), enctype_names[fbp->fb_feed[4]]); /* safe */ tn_hex((CHAR *)tn_msg,TN_MSG_LEN,&fbp->fb_feed[6], (p-fbp->fb_feed)-2-6); ckstrncat(tn_msg,"IAC SE",TN_MSG_LEN); debug(F100,tn_msg,"",0); if (tn_deb || debses) tn_debug(tn_msg); } #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif ttol(fbp->fb_feed, p - fbp->fb_feed); #ifdef OS2 ReleaseTelnetMutex(); #endif break; default: return(xFAILED); } return(fbp->state[dir-1] = state); } /* * Returns: * -1: some error. Negotiation is done, encryption not ready. * 0: Successful, initial negotiation all done. * 1: successful, negotiation not done yet. */ int des3_cfb64_is(data, cnt) unsigned char *data; int cnt; { return(des3_fb64_is(data, cnt, &des3_fb[CFB])); } int des3_ofb64_is(data, cnt) unsigned char *data; int cnt; { return(des3_fb64_is(data, cnt, &des3_fb[OFB])); } int des3_fb64_is(data, cnt, fbp) unsigned char *data; int cnt; struct des3_fb *fbp; { unsigned char *p; register int state = fbp->state[DIR_DECRYPT-1]; if (cnt-- < 1) goto failure; #ifdef CK_SSL if (!TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) #endif /* CK_SSL */ switch (*data++) { case FB64_IV: if (cnt != sizeof(Block)) { #ifdef DEBUG if (encrypt_debug_mode) printf("DES3_FB64: initial vector failed on size\r\n"); #endif state = xFAILED; goto failure; } #ifdef DEBUG if (encrypt_debug_mode) { printf("DES3_FB64: initial vector received\r\n"); printf("Initializing Decrypt stream\r\n"); } #endif des3_fb64_stream_iv((void *)data, &fbp->streams[DIR_DECRYPT-1]); p = fbp->fb_feed + 3; *p++ = ENCRYPT_REPLY; p++; *p++ = FB64_IV_OK; *p++ = IAC; *p++ = SE; if (deblog || tn_deb || debses) { int i; sprintf(tn_msg, "TELNET SENT SB %s REPLY %s FB64_IV_OK ", TELOPT(fbp->fb_feed[2]), enctype_names[fbp->fb_feed[4]]); /* safe */ tn_hex((CHAR *)tn_msg,TN_MSG_LEN,&fbp->fb_feed[6], (p-fbp->fb_feed)-2-6); ckstrncat(tn_msg,"IAC SE",TN_MSG_LEN); debug(F100,tn_msg,"",0); if (tn_deb || debses) tn_debug(tn_msg); } #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif ttol(fbp->fb_feed, p - fbp->fb_feed); #ifdef OS2 ReleaseTelnetMutex(); #endif state = IN_PROGRESS; break; default: #if 0 if (encrypt_debug_mode) { printf("Unknown option type: %d\r\n", *(data-1)); printf("\r\n"); } #endif /* FALL THROUGH */ failure: /* * We failed. Send an FB64_IV_BAD option * to the other side so it will know that * things failed. */ p = fbp->fb_feed + 3; *p++ = ENCRYPT_REPLY; p++; *p++ = FB64_IV_BAD; *p++ = IAC; *p++ = SE; if (deblog || tn_deb || debses) { int i; sprintf(tn_msg, "TELNET SENT SB %s REPLY %s FB64_IV_BAD ", TELOPT(fbp->fb_feed[2]), enctype_names[fbp->fb_feed[4]]); /* safe */ tn_hex((CHAR *)tn_msg,TN_MSG_LEN,&fbp->fb_feed[6], (p-fbp->fb_feed)-2-6); ckstrncat(tn_msg,"IAC SE",TN_MSG_LEN); debug(F100,tn_msg,"",0); if (tn_deb || debses) tn_debug(tn_msg); } #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif ttol(fbp->fb_feed, p - fbp->fb_feed); #ifdef OS2 ReleaseTelnetMutex(); #endif break; } return(fbp->state[DIR_DECRYPT-1] = state); } /* * Returns: * -1: some error. Negotiation is done, encryption not ready. * 0: Successful, initial negotiation all done. * 1: successful, negotiation not done yet. */ int des3_cfb64_reply(data, cnt) unsigned char *data; int cnt; { return(des3_fb64_reply(data, cnt, &des3_fb[CFB])); } int des3_ofb64_reply(data, cnt) unsigned char *data; int cnt; { return(des3_fb64_reply(data, cnt, &des3_fb[OFB])); } int des3_fb64_reply(data, cnt, fbp) unsigned char *data; int cnt; struct des3_fb *fbp; { register int state = fbp->state[DIR_ENCRYPT-1]; if (cnt-- < 1) goto failure; switch (*data++) { case FB64_IV_OK: des3_fb64_stream_iv(fbp->temp_feed, &fbp->streams[DIR_ENCRYPT-1]); if (state == xFAILED) state = IN_PROGRESS; state &= ~NO_RECV_IV; encrypt_send_keyid(DIR_ENCRYPT, (unsigned char *)"\0", 1, 1); break; case FB64_IV_BAD: memset(fbp->temp_feed, 0, sizeof(Block)); des3_fb64_stream_iv(fbp->temp_feed, &fbp->streams[DIR_ENCRYPT-1]); state = xFAILED; break; default: #if 0 if (encrypt_debug_mode) { printf("Unknown option type: %d\r\n", data[-1]); printf("\r\n"); } #endif /* FALL THROUGH */ failure: state = xFAILED; break; } return(fbp->state[DIR_ENCRYPT-1] = state); } int des3_cfb64_session(key, server) Session_Key *key; int server; { return(des3_fb64_session(key, server, &des3_fb[CFB])); } int des3_ofb64_session(key, server) Session_Key *key; int server; { return(des3_fb64_session(key, server, &des3_fb[OFB])); } static int des3_fb64_session(key, server, fbp) Session_Key *key; int server; struct des3_fb *fbp; { int rc=0,i=0; int keys2use=0; struct des3_stinfo * s_stream; struct des3_stinfo * c_stream; if(server) { s_stream = &fbp->streams[DIR_ENCRYPT-1]; c_stream = &fbp->streams[DIR_DECRYPT-1]; } else { s_stream = &fbp->streams[DIR_DECRYPT-1]; c_stream = &fbp->streams[DIR_ENCRYPT-1]; } keys2use = key->length / sizeof(Block); if (!key || (key->type == SK_DES) || (keys2use < 2)) { CHAR buf[80]; sprintf((char *)buf,"Can't set 3DES session key (%d < %d)", key ? key->length : 0, 2 * (int)sizeof(Block)); /* safe */ #ifdef DEBUG if (encrypt_debug_mode) printf("%s\r\n",buf); #endif debug(F110,"des3_fb64_session",buf,0); return(-1); } debug(F111,"des3_fb64_session","keys2use",keys2use); /* Compute the first set of keys / key order */ switch ( keys2use ) { case 2: memcpy((void *)fbp->krbdes_key[0], (void *)key->data, sizeof(Block)); memcpy((void *) fbp->krbdes_key[1],(void *)(key->data + sizeof(Block)), sizeof(Block)); memcpy((void *)fbp->krbdes_key[2], (void *)key->data, sizeof(Block)); break; case 3: default: memcpy((void *)fbp->krbdes_key[0], (void *)key->data, sizeof(Block)); memcpy((void *) fbp->krbdes_key[1],(void *)(key->data + sizeof(Block)), sizeof(Block)); memcpy((void *) fbp->krbdes_key[2], (void *) (key->data + 2*sizeof(Block)), sizeof(Block)); break; } ckhexdump("des3_session_key key->data",key->data,sizeof(Block)); ckhexdump("des3_session_key fbp->krbdes_key[0]", fbp->krbdes_key[0], sizeof(Block) ); if (fbp->once == 0) { des_set_random_generator_seed(fbp->krbdes_key[0]); fbp->once = 1; } for ( i=0;i<3;i++ ) des_fixup_key_parity(fbp->krbdes_key[i]); des3_fb64_stream_key(fbp->krbdes_key, s_stream); /* Compute the second set of keys / key order */ switch ( keys2use ) { case 2: memcpy((void *) fbp->krbdes_key[0],(void *)(key->data + sizeof(Block)), sizeof(Block)); memcpy((void *)fbp->krbdes_key[1], (void *)key->data, sizeof(Block)); memcpy((void *) fbp->krbdes_key[2],(void *)(key->data + sizeof(Block)), sizeof(Block)); break; case 3: memcpy((void *) fbp->krbdes_key[0],(void *)(key->data + sizeof(Block)), sizeof(Block)); memcpy((void *) fbp->krbdes_key[1], (void *) (key->data + 2*sizeof(Block)), sizeof(Block)); memcpy((void *)fbp->krbdes_key[2], (void *)key->data, sizeof(Block)); break; case 4: memcpy((void *) fbp->krbdes_key[0],(void *)(key->data + sizeof(Block)), sizeof(Block)); memcpy((void *) fbp->krbdes_key[1], (void *) (key->data + 3*sizeof(Block)), sizeof(Block)); memcpy((void *)fbp->krbdes_key[2], (void *)key->data, sizeof(Block)); break; case 5: memcpy((void *) fbp->krbdes_key[0],(void *)(key->data + sizeof(Block)), sizeof(Block)); memcpy((void *) fbp->krbdes_key[1], (void *) (key->data + 3*sizeof(Block)), sizeof(Block)); memcpy((void *)fbp->krbdes_key[2], (void *)(key->data + 4*sizeof(Block)), sizeof(Block)); break; case 6: memcpy((void *) fbp->krbdes_key[0], (void *) (key->data + 3*sizeof(Block)), sizeof(Block)); memcpy((void *)fbp->krbdes_key[1], (void *)(key->data + 4*sizeof(Block)), sizeof(Block)); memcpy((void *) fbp->krbdes_key[2], (void *) (key->data + 5 *sizeof(Block)), sizeof(Block)); break; } for ( i=0;i<3;i++ ) des_fixup_key_parity(fbp->krbdes_key[i]); des3_fb64_stream_key(fbp->krbdes_key, c_stream); /* now use the second set of keys to build the default Key Schedule */ /* which is used for generating the IV. */ for ( i=0;i<3;i++ ) { memset(fbp->krbdes_sched[i],0,sizeof(Schedule)); rc = des_key_sched(fbp->krbdes_key[i], fbp->krbdes_sched[i]); if ( rc == -1 ) { printf("?Invalid DES key specified for encryption [DES3,%s]\r\n", server?"server":"client"); debug(F110,"des3_fb64_stream_iv", "invalid DES Key specified for encryption",0); } else if ( rc == -2 ) { printf("?Weak DES key specified for encryption\r\n"); debug(F110,"des3_fb64_stream_iv", "weak DES Key specified for encryption",0); } else if ( rc != 0 ) { printf("?Key Schedule not created by encryption\r\n"); debug(F110,"des3_fb64_stream_iv", "Key Schedule not created by encryption",0); } ckhexdump("des3_fb64_session_key schedule",fbp->krbdes_sched[i],8*16); } /* * Now look to see if krbdes_start() was was waiting for * the key to show up. If so, go ahead an call it now * that we have the key. */ if (fbp->need_start) { fbp->need_start = 0; des3_fb64_start(fbp, DIR_ENCRYPT, server); } return(0); } /* * We only accept a keyid of 0. If we get a keyid of * 0, then mark the state as SUCCESS. */ int des3_cfb64_keyid(dir, kp, lenp) int dir, *lenp; unsigned char *kp; { return(des3_fb64_keyid(dir, kp, lenp, &des3_fb[CFB])); } int des3_ofb64_keyid(dir, kp, lenp) int dir, *lenp; unsigned char *kp; { return(des3_fb64_keyid(dir, kp, lenp, &des3_fb[OFB])); } int des3_fb64_keyid(dir, kp, lenp, fbp) int dir, *lenp; unsigned char *kp; struct des3_fb *fbp; { register int state = fbp->state[dir-1]; if (*lenp != 1 || (*kp != '\0')) { *lenp = 0; return(state); } if (state == xFAILED) state = IN_PROGRESS; state &= ~NO_KEYID; return(fbp->state[dir-1] = state); } #if 0 void des3_fb64_printsub(data, cnt, buf, buflen, type) unsigned char *data, *buf, *type; int cnt, buflen; { char lbuf[64]; register int i; char *cp; buf[buflen-1] = '\0'; /* make sure it's NULL terminated */ buflen -= 1; switch(data[2]) { case FB64_IV: sprintf(lbuf, "%s_IV", type); cp = lbuf; goto common; case FB64_IV_OK: sprintf(lbuf, "%s_IV_OK", type); cp = lbuf; goto common; case FB64_IV_BAD: sprintf(lbuf, "%s_IV_BAD", type); cp = lbuf; goto common; case FB64_CHALLENGE: sprintf(lbuf, "%s_CHALLENGE", type); cp = lbuf; goto common; case FB64_RESPONSE: sprintf(lbuf, "%s_RESPONSE", type); cp = lbuf; goto common; default: sprintf(lbuf, " %d (unknown)", data[2]); cp = lbuf; common: for (; (buflen > 0) && (*buf = *cp++); buf++) buflen--; for (i = 3; i < cnt; i++) { sprintf(lbuf, " %d", data[i]); for (cp = lbuf; (buflen > 0) && (*buf = *cp++); buf++) buflen--; } break; } } void des3_cfb64_printsub(data, cnt, buf, buflen) unsigned char *data, *buf; int cnt, buflen; { des3_fb64_printsub(data, cnt, buf, buflen, "CFB64"); } void des3_ofb64_printsub(data, cnt, buf, buflen) unsigned char *data, *buf; int cnt, buflen; { des3_fb64_printsub(data, cnt, buf, buflen, "OFB64"); } #endif void des3_fb64_stream_iv(seed, stp) Block seed; register struct des3_stinfo *stp; { int rc=0, i = 0;; memcpy(stp->str_iv, seed, sizeof(Block)); memcpy(stp->str_output, seed, sizeof(Block)); for ( i=0;i<3;i++ ) { memset(stp->str_sched[i],0,sizeof(Schedule)); ckhexdump("des3_fb64_stream_iv",stp->str_ikey[i],8); rc = des_key_sched(stp->str_ikey[i], stp->str_sched[i]); if ( rc == -1 ) { printf("?Invalid DES key specified for encryption [DES3 iv]\r\n"); debug(F110,"des3_fb64_stream_iv", "invalid DES Key specified for encryption",0); } else if ( rc == -2 ) { printf("?Weak DES key specified for encryption\r\n"); debug(F110,"des3_fb64_stream_iv", "weak DES Key specified for encryption",0); } else if ( rc != 0 ) { printf("?Key Schedule not created by encryption\r\n"); debug(F110,"des3_fb64_stream_iv", "Key Schedule not created by encryption",0); } ckhexdump("des3_fb64_stream_iv schedule",stp->str_sched[i],8*16); } stp->str_index = sizeof(Block); } void des3_fb64_stream_key(key, stp) Block * key; register struct des3_stinfo *stp; { int rc = 0, i = 0; for ( i=0;i<3;i++ ) { memcpy(stp->str_ikey[i], key[i], sizeof(Block)); memset(stp->str_sched[i],0,sizeof(Schedule)); ckhexdump("des3_fb64_stream_key",key[i],8); rc = des_key_sched(key[i], stp->str_sched[i]); if ( rc == -1 ) { printf("?Invalid DES key specified for encryption [DES3 key]\r\n"); debug(F110,"des3_fb64_stream_key", "invalid DES Key specified for encryption",0); } else if ( rc == -2 ) { printf("?Weak DES key specified for encryption\r\n"); debug(F110,"des3_fb64_stream_key", "weak DES Key specified for encryption",0); } else if ( rc != 0 ) { printf("?Key Schedule not created by encryption\r\n"); debug(F110,"des3_fb64_stream_key", "Key Schedule not created by encryption",0); } ckhexdump("des3_fb64_stream_key schedule",stp->str_sched[i],8*16); } memcpy(stp->str_output, stp->str_iv, sizeof(Block)); stp->str_index = sizeof(Block); } /* * DES3 64 bit Cipher Feedback * * key1 key2 key3 * | | | * v v v * +-------+ +-------+ +-------+ * +->| DES-e |->| DES-d |->| DES-e |-- + * | +-------+ +-------+ +-------+ | * | v * INPUT --(-------------------------------->(+)+---> DATA * | | * +------------------------------------+ * * * Given: * iV: Initial vector, 64 bits (8 bytes) long. * Dn: the nth chunk of 64 bits (8 bytes) of data to encrypt (decrypt). * On: the nth chunk of 64 bits (8 bytes) of encrypted (decrypted) output. * * V0 = DES-e(DES-d(DES-e(iV, key1),key2),key3) * On = Dn ^ Vn * V(n+1) = DES-e(DES-d(DES-e(On, key1),key2),key3) */ void des3_cfb64_encrypt(s, c) register unsigned char *s; int c; { register struct des3_stinfo *stp = &des3_fb[CFB].streams[DIR_ENCRYPT-1]; register int index; index = stp->str_index; while (c-- > 0) { if (index == sizeof(Block)) { Block b; #ifdef LIBDES des_ecb3_encrypt(stp->str_output, b, stp->str_sched[0], stp->str_sched[1], stp->str_sched[2], 1); #else /* LIBDES */ des_ecb_encrypt(stp->str_output, b, stp->str_sched[0], 1); des_ecb_encrypt(stp->str_output, b, stp->str_sched[1], 0); des_ecb_encrypt(stp->str_output, b, stp->str_sched[2], 1); #endif /* LIBDES */ memcpy(stp->str_feed,b,sizeof(Block)); index = 0; } /* On encryption, we store (feed ^ data) which is cypher */ *s = stp->str_output[index] = (stp->str_feed[index] ^ *s); s++; index++; } stp->str_index = index; } int des3_cfb64_decrypt(data) int data; { register struct des3_stinfo *stp = &des3_fb[CFB].streams[DIR_DECRYPT-1]; int index; if (data == -1) { /* * Back up one byte. It is assumed that we will * never back up more than one byte. If we do, this * may or may not work. */ if (stp->str_index) --stp->str_index; return(0); } index = stp->str_index++; if (index == sizeof(Block)) { Block b; #ifdef LIBDES des_ecb3_encrypt(stp->str_output, b, stp->str_sched[0], stp->str_sched[1], stp->str_sched[2], 1); #else /* LIBDES */ des_ecb_encrypt(stp->str_output, b, stp->str_sched[0], 1); des_ecb_encrypt(stp->str_output, b, stp->str_sched[1], 0); des_ecb_encrypt(stp->str_output, b, stp->str_sched[2], 1); #endif /* LIBDES */ memcpy(stp->str_feed, b, sizeof(Block)); stp->str_index = 1; /* Next time will be 1 */ index = 0; /* But now use 0 */ } /* On decryption we store (data) which is cypher. */ stp->str_output[index] = data; return(data ^ stp->str_feed[index]); } /* * DES3 64 bit Output Feedback * * * key1 key2 key3 * | | | * v v v * +-------+ +-------+ +-------+ * +->| DES-e |->| DES-d |->| DES-e |-- + * | +-------+ +-------+ +-------+ | * +------------------------------------+ * v * INPUT ------------------------------------->(+) ----> DATA * * Given: * iV: Initial vector, 64 bits (8 bytes) long. * Dn: the nth chunk of 64 bits (8 bytes) of data to encrypt (decrypt). * On: the nth chunk of 64 bits (8 bytes) of encrypted (decrypted) output. * * V0 = DES-e(DES-d(DES-e(iV, key1),key2),key3) * V(n+1) = DES-e(DES-d(DES-e(Vn, key1),key2),key3) * On = Dn ^ Vn */ void des3_ofb64_encrypt(s, c) register unsigned char *s; int c; { register struct des3_stinfo *stp = &des3_fb[OFB].streams[DIR_ENCRYPT-1]; register int index; index = stp->str_index; while (c-- > 0) { if (index == sizeof(Block)) { Block b; #ifdef LIBDES des_ecb3_encrypt(stp->str_feed, b, stp->str_sched[0], stp->str_sched[1], stp->str_sched[2], 1); #else /* LIBDES */ des_ecb_encrypt(stp->str_output, b, stp->str_sched[0], 1); des_ecb_encrypt(stp->str_output, b, stp->str_sched[1], 0); des_ecb_encrypt(stp->str_output, b, stp->str_sched[2], 1); #endif /* LIBDES */ memcpy(stp->str_feed,b,sizeof(Block)); index = 0; } *s++ ^= stp->str_feed[index]; index++; } stp->str_index = index; } int des3_ofb64_decrypt(data) int data; { register struct des3_stinfo *stp = &des3_fb[OFB].streams[DIR_DECRYPT-1]; int index; if (data == -1) { /* * Back up one byte. It is assumed that we will * never back up more than one byte. If we do, this * may or may not work. */ if (stp->str_index) --stp->str_index; return(0); } index = stp->str_index++; if (index == sizeof(Block)) { Block b; #ifdef LIBDES des_ecb3_encrypt(stp->str_feed, b, stp->str_sched[0], stp->str_sched[1], stp->str_sched[2], 1); #else /* LIBDES */ des_ecb_encrypt(stp->str_output, b, stp->str_sched[0], 1); des_ecb_encrypt(stp->str_output, b, stp->str_sched[1], 0); des_ecb_encrypt(stp->str_output, b, stp->str_sched[2], 1); #endif /* LIBDES */ memcpy(stp->str_feed, b, sizeof(Block)); stp->str_index = 1; /* Next time will be 1 */ index = 0; /* But now use 0 */ } return(data ^ stp->str_feed[index]); } #endif /* CK_DES */ #ifdef CK_CAST /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Copyright (c) 1997 Stanford University * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided * that the above copyright notices and this permission notice appear in * all copies of the software and related documentation. * * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. * * IN NO EVENT SHALL STANFORD BE LIABLE FOR ANY SPECIAL, INCIDENTAL, * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #ifdef __STDC__ #include #endif /* * cast.h * Author: Tom Wu * * Type and function declarations for CAST. */ #ifndef _CAST_H_ #define _CAST_H_ #ifndef P #ifdef __STDC__ #define P(x) x #else #define P(x) () #endif /* __STDC__ */ #endif /* P */ #ifndef LITTLE_ENDIAN #ifndef BIG_ENDIAN #ifndef WORDS_BIGENDIAN #define LITTLE_ENDIAN 1 #endif /* WORDS_BIGENDIAN */ #endif /* BIG_ENDIAN */ #endif /* LITTLE_ENDIAN */ typedef unsigned int uint32; /* Must be 32 bits */ typedef uint32 * uint32p; typedef unsigned char uint8; typedef uint8 * uint8p; typedef struct { struct CastSubkeyPair { uint32 Km; uint32 Kr; } K[16]; int ksize; } CastKeySched; /* * cast*_key_sched(schedule, key) * * Initializes the CAST key schedule "schedule" according to the given key. * The different setup routines accept different length keys: * * ck_cast5_40_key_sched: 40-bit/5-byte (12 round) keys * ck_cast5_64_key_sched: 64-bit/8-byte (12 round) keys * ck_cast5_80_key_sched: 80-bit/10-byte (12 round) keys * ck_cast128_key_sched: 128-bit/16-byte (16 round) keys */ extern void ck_cast5_40_key_sched P((CastKeySched *, uint8 *)); extern void ck_cast5_64_key_sched P((CastKeySched *, uint8 *)); extern void ck_cast5_80_key_sched P((CastKeySched *, uint8 *)); extern void ck_cast128_key_sched P((CastKeySched *, uint8 *)); /* * ck_cast_ecb_encrypt(output, input, schedule, mode) * ck_cast_ecb_crypt(data, schedule, mode) * * Encrypts the 64-bit "input" according to the CAST key schedule * "schedule" and places the result in "output". If "mode" is 0, * ck_cast_ecb_encrypt will encrypt, otherwise it will decrypt. * "Output" and "input" can point to the same memory, in which case * en/decryption will be performed in place. * * ck_cast_ecb_crypt accepts input in the form of an array of two * 32-bit words and performs encryption/decryption in place. */ extern void ck_cast_ecb_encrypt P((uint8 *, uint8 *, CastKeySched *, int)); extern void ck_cast_ecb_crypt P((uint32 *, CastKeySched *, int)); #endif /* CAST_H */ extern encrypt_debug_mode; #define CFB_40 0 #define OFB_40 1 #ifdef CAST_EXPORT_ENCRYPTION #define FB_CNT 2 #else #define CFB_128 2 #define OFB_128 3 #define FB_CNT 4 #endif #define NO_SEND_IV 1 #define NO_RECV_IV 2 #define NO_KEYID 4 #define IN_PROGRESS (NO_SEND_IV|NO_RECV_IV|NO_KEYID) #define SUCCESS 0 #define cFAILED -1 struct cast_fb { Block temp_feed; unsigned char fb_feed[64]; int key_isset; int need_start; int state[2]; struct cast_stinfo { Block str_output; Block str_feed; Block str_iv; CastKeySched str_sched; int str_index; } streams[2]; }; static struct cast_fb cast_fb[FB_CNT]; #define FB64_IV 1 #define FB64_IV_OK 2 #define FB64_IV_BAD 3 static void cast_fb64_stream_iv P((Block, struct cast_stinfo *)); static void cast_fb64_init P((struct cast_fb *)); static int cast_fb64_start P((struct cast_fb *, int, int)); static int cast_fb64_is P((unsigned char *, int, struct cast_fb *)); static int cast_fb64_reply P((unsigned char *, int, struct cast_fb *)); static int cast_fb64_session P((Session_Key *, int, struct cast_fb *, int)); static void cast_fb64_stream_key P((Block, struct cast_stinfo *, int)); static int cast_fb64_keyid P((int, unsigned char *, int *, struct cast_fb *)); static void _cast_cfb64_encrypt P((unsigned char *,int, struct cast_stinfo *)); static int _cast_cfb64_decrypt P((int, struct cast_stinfo *)); static void _cast_ofb64_encrypt P((unsigned char *,int, struct cast_stinfo *)); static int _cast_ofb64_decrypt P((int, struct cast_stinfo *)); #ifndef CAST_EXPORT_ENCRYPTION void cast_cfb64_init(server) int server; { cast_fb64_init(&cast_fb[CFB_128]); cast_fb[CFB_128].fb_feed[4] = ENCTYPE_CAST128_CFB64; } void cast_ofb64_init(server) int server; { cast_fb64_init(&cast_fb[OFB_128]); cast_fb[OFB_128].fb_feed[4] = ENCTYPE_CAST128_OFB64; } #endif void castexp_cfb64_init(server) int server; { cast_fb64_init(&cast_fb[CFB_40]); cast_fb[CFB_40].fb_feed[4] = ENCTYPE_CAST5_40_CFB64; } void castexp_ofb64_init(server) int server; { cast_fb64_init(&cast_fb[OFB_40]); cast_fb[OFB_40].fb_feed[4] = ENCTYPE_CAST5_40_OFB64; } static void cast_fb64_init(fbp) register struct cast_fb *fbp; { memset((void *)fbp, 0, sizeof(*fbp)); fbp->key_isset = 0; fbp->state[0] = fbp->state[1] = cFAILED; fbp->fb_feed[0] = IAC; fbp->fb_feed[1] = SB; fbp->fb_feed[2] = TELOPT_ENCRYPTION; fbp->fb_feed[3] = ENCRYPT_IS; } /* * Returns: * -1: some error. Negotiation is done, encryption not ready. * 0: Successful, initial negotiation all done. * 1: successful, negotiation not done yet. * 2: Not yet. Other things (like getting the key from * Kerberos) have to happen before we can continue. */ #ifndef CAST_EXPORT_ENCRYPTION int cast_cfb64_start(dir, server) int dir; int server; { return(cast_fb64_start(&cast_fb[CFB_128], dir, server)); } int cast_ofb64_start(dir, server) int dir; int server; { return(cast_fb64_start(&cast_fb[OFB_128], dir, server)); } #endif int castexp_cfb64_start(dir, server) int dir; int server; { return(cast_fb64_start(&cast_fb[CFB_40], dir, server)); } int castexp_ofb64_start(dir, server) int dir; int server; { return(cast_fb64_start(&cast_fb[OFB_40], dir, server)); } static int cast_fb64_start(fbp, dir, server) struct cast_fb *fbp; int dir; int server; { Block b; int x; unsigned char *p; register int state; switch (dir) { case DIR_DECRYPT: /* * This is simply a request to have the other side * start output (our input). He will negotiate an * IV so we need not look for it. */ state = fbp->state[dir-1]; if (state == cFAILED) state = IN_PROGRESS; break; case DIR_ENCRYPT: state = fbp->state[dir-1]; if (state == cFAILED) state = IN_PROGRESS; else if ((state & NO_SEND_IV) == 0) break; if (!fbp->key_isset) { fbp->need_start = 1; break; } state &= ~NO_SEND_IV; state |= NO_RECV_IV; #ifdef DEBUG if (encrypt_debug_mode) printf("Creating new feed\r\n"); #endif /* * Create a random feed and send it over. */ ck_cast_ecb_encrypt(fbp->temp_feed, fbp->temp_feed, &fbp->streams[dir-1].str_sched, 0); p = fbp->fb_feed + 3; *p++ = ENCRYPT_IS; p++; *p++ = FB64_IV; for (x = 0; x < sizeof(Block); ++x) { if ((*p++ = fbp->temp_feed[x]) == IAC) *p++ = IAC; } *p++ = IAC; *p++ = SE; ttol(fbp->fb_feed, p - fbp->fb_feed); break; default: return(cFAILED); } return(fbp->state[dir-1] = state); } /* * Returns: * -1: some error. Negotiation is done, encryption not ready. * 0: Successful, initial negotiation all done. * 1: successful, negotiation not done yet. */ #ifndef CAST_EXPORT_ENCRYPTION int cast_cfb64_is(data, cnt) unsigned char *data; int cnt; { return(cast_fb64_is(data, cnt, &cast_fb[CFB_128])); } int cast_ofb64_is(data, cnt) unsigned char *data; int cnt; { return(cast_fb64_is(data, cnt, &cast_fb[OFB_128])); } #endif int castexp_cfb64_is(data, cnt) unsigned char *data; int cnt; { return(cast_fb64_is(data, cnt, &cast_fb[CFB_40])); } int castexp_ofb64_is(data, cnt) unsigned char *data; int cnt; { return(cast_fb64_is(data, cnt, &cast_fb[OFB_40])); } static int cast_fb64_is(data, cnt, fbp) unsigned char *data; int cnt; struct cast_fb *fbp; { int x; unsigned char *p; Block b; register int state = fbp->state[DIR_DECRYPT-1]; if (cnt-- < 1) goto failure; #ifdef CK_SSL if (!TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) #endif /* CK_SSL */ switch (*data++) { case FB64_IV: if (cnt != sizeof(Block)) { #ifdef DEBUG if (encrypt_debug_mode) printf("FB64: initial vector failed on size\r\n"); #endif state = cFAILED; goto failure; } #ifdef DEBUG if (encrypt_debug_mode) printf("FB64: initial vector received\r\n"); if (encrypt_debug_mode) printf("Initializing Decrypt stream\r\n"); #endif cast_fb64_stream_iv((void *)data, &fbp->streams[DIR_DECRYPT-1]); p = fbp->fb_feed + 3; *p++ = ENCRYPT_REPLY; p++; *p++ = FB64_IV_OK; *p++ = IAC; *p++ = SE; ttol(fbp->fb_feed, p - fbp->fb_feed); state = IN_PROGRESS; break; default: /* unknown option type */ /* FALL THROUGH */ failure: /* * We failed. Send an FB64_IV_BAD option * to the other side so it will know that * things failed. */ p = fbp->fb_feed + 3; *p++ = ENCRYPT_REPLY; p++; *p++ = FB64_IV_BAD; *p++ = IAC; *p++ = SE; ttol(fbp->fb_feed, p - fbp->fb_feed); break; } return(fbp->state[DIR_DECRYPT-1] = state); } /* * Returns: * -1: some error. Negotiation is done, encryption not ready. * 0: Successful, initial negotiation all done. * 1: successful, negotiation not done yet. */ #ifndef CAST_EXPORT_ENCRYPTION int cast_cfb64_reply(data, cnt) unsigned char *data; int cnt; { return(cast_fb64_reply(data, cnt, &cast_fb[CFB_128])); } int cast_ofb64_reply(data, cnt) unsigned char *data; int cnt; { return(cast_fb64_reply(data, cnt, &cast_fb[OFB_128])); } #endif int castexp_cfb64_reply(data, cnt) unsigned char *data; int cnt; { return(cast_fb64_reply(data, cnt, &cast_fb[CFB_40])); } int castexp_ofb64_reply(data, cnt) unsigned char *data; int cnt; { return(cast_fb64_reply(data, cnt, &cast_fb[OFB_40])); } static int cast_fb64_reply(data, cnt, fbp) unsigned char *data; int cnt; struct cast_fb *fbp; { int x; unsigned char *p; Block b; register int state = fbp->state[DIR_ENCRYPT-1]; if (cnt-- < 1) goto failure; switch (*data++) { case FB64_IV_OK: cast_fb64_stream_iv(fbp->temp_feed, &fbp->streams[DIR_ENCRYPT-1]); if (state == cFAILED) state = IN_PROGRESS; state &= ~NO_RECV_IV; encrypt_send_keyid(DIR_ENCRYPT, (unsigned char *)"\0", 1, 1); break; case FB64_IV_BAD: memset(fbp->temp_feed, 0, sizeof(Block)); cast_fb64_stream_iv(fbp->temp_feed, &fbp->streams[DIR_ENCRYPT-1]); state = cFAILED; break; default: #if 0 if (encrypt_debug_mode) { printf("Unknown option type: %d\r\n", data[-1]); printd(data, cnt); printf("\r\n"); } #endif /* FALL THROUGH */ failure: state = cFAILED; break; } return(fbp->state[DIR_ENCRYPT-1] = state); } #ifndef CAST_EXPORT_ENCRYPTION int cast_cfb64_session(key, server) Session_Key *key; int server; { return(cast_fb64_session(key, server, &cast_fb[CFB_128], 1)); } int cast_ofb64_session(key, server) Session_Key *key; int server; { return(cast_fb64_session(key, server, &cast_fb[OFB_128], 1)); } #endif int castexp_cfb64_session(key, server) Session_Key *key; int server; { return(cast_fb64_session(key, server, &cast_fb[CFB_40], 0)); } int castexp_ofb64_session(key, server) Session_Key *key; int server; { return(cast_fb64_session(key, server, &cast_fb[OFB_40], 0)); } #define CAST128_KEYLEN 16 /* 128 bits */ #define CAST5_40_KEYLEN 5 /* 40 bits */ static int cast_fb64_session(key, server, fbp, fs) Session_Key *key; int server; struct cast_fb *fbp; int fs; { int klen; unsigned char * kptr; if(fs) klen = CAST128_KEYLEN; else klen = CAST5_40_KEYLEN; if (!key || key->length < klen) { CHAR buf[80]; sprintf((char *)buf,"Can't set CAST session key (%d < %d)", key ? key->length : 0, klen); /* safe */ #ifdef DEBUG if (encrypt_debug_mode) printf("%s\r\n",buf); #endif debug(F110,"cast_fb64_session",buf,0); return(cFAILED); } if(key->length < 2 * klen) kptr = key->data; else kptr = key->data + klen; if(server) { cast_fb64_stream_key(kptr, &fbp->streams[DIR_ENCRYPT-1], fs); cast_fb64_stream_key(key->data, &fbp->streams[DIR_DECRYPT-1], fs); } else { cast_fb64_stream_key(kptr, &fbp->streams[DIR_DECRYPT-1], fs); cast_fb64_stream_key(key->data, &fbp->streams[DIR_ENCRYPT-1], fs); } /* Stuff leftovers into the feed */ if(key->length >= 2 * klen + sizeof(Block)) memcpy(fbp->temp_feed, key->data + 2 * klen, sizeof(Block)); else { #ifdef COMMENT /* This is a better way of erasing the password */ /* but we do not want to link in libsrp */ t_random(fbp->temp_feed, sizeof(Block)); #else memset(fbp->temp_feed, 0, sizeof(Block)); #endif } fbp->key_isset = 1; /* * Now look to see if cast_fb64_start() was was waiting for * the key to show up. If so, go ahead an call it now * that we have the key. */ if (fbp->need_start) { fbp->need_start = 0; cast_fb64_start(fbp, DIR_ENCRYPT, server); } return(0); } /* * We only accept a keyid of 0. If we get a keyid of * 0, then mark the state as SUCCESS. */ #ifndef CAST_EXPORT_ENCRYPTION int cast_cfb64_keyid(dir, kp, lenp) int dir, *lenp; unsigned char *kp; { return(cast_fb64_keyid(dir, kp, lenp, &cast_fb[CFB_128])); } int cast_ofb64_keyid(dir, kp, lenp) int dir, *lenp; unsigned char *kp; { return(cast_fb64_keyid(dir, kp, lenp, &cast_fb[OFB_128])); } #endif int castexp_cfb64_keyid(dir, kp, lenp) int dir, *lenp; unsigned char *kp; { return(cast_fb64_keyid(dir, kp, lenp, &cast_fb[CFB_40])); } int castexp_ofb64_keyid(dir, kp, lenp) int dir, *lenp; unsigned char *kp; { return(cast_fb64_keyid(dir, kp, lenp, &cast_fb[OFB_40])); } static int cast_fb64_keyid(dir, kp, lenp, fbp) int dir, *lenp; unsigned char *kp; struct cast_fb *fbp; { register int state = fbp->state[dir-1]; if (*lenp != 1 || (*kp != '\0')) { *lenp = 0; return(state); } if (state == cFAILED) state = IN_PROGRESS; state &= ~NO_KEYID; return(fbp->state[dir-1] = state); } static void cast_fb64_printsub(data, cnt, buf, buflen, type) unsigned char *data, *buf, *type; int cnt, buflen; { char lbuf[64]; register int i; char *cp; buf[buflen-1] = '\0'; /* make sure it's NULL terminated */ buflen -= 1; switch(data[2]) { case FB64_IV: sprintf(lbuf, "%s_IV", type); cp = lbuf; goto common; case FB64_IV_OK: sprintf(lbuf, "%s_IV_OK", type); cp = lbuf; goto common; case FB64_IV_BAD: sprintf(lbuf, "%s_IV_BAD", type); cp = lbuf; goto common; default: sprintf(lbuf, " %d (unknown)", data[2]); cp = lbuf; common: for (; (buflen > 0) && (*buf = *cp++); buf++) buflen--; for (i = 3; i < cnt; i++) { sprintf(lbuf, " %d", data[i]); for (cp = lbuf; (buflen > 0) && (*buf = *cp++); buf++) buflen--; } break; } } void cast_cfb64_printsub(data, cnt, buf, buflen) unsigned char *data, *buf; int cnt, buflen; { cast_fb64_printsub(data, cnt, buf, buflen, "CFB64"); } void cast_ofb64_printsub(data, cnt, buf, buflen) unsigned char *data, *buf; int cnt, buflen; { cast_fb64_printsub(data, cnt, buf, buflen, "OFB64"); } static void cast_fb64_stream_iv(seed, stp) Block seed; register struct cast_stinfo *stp; { memcpy((void *)stp->str_iv, (void *)seed, sizeof(Block)); memcpy((void *)stp->str_output, (void *)seed, sizeof(Block)); stp->str_index = sizeof(Block); } static void cast_fb64_stream_key(key, stp, fs) unsigned char * key; register struct cast_stinfo *stp; int fs; { #ifndef CAST_EXPORT_ENCRYPTION if(fs) ck_cast128_key_sched(&stp->str_sched, key); else #endif ck_cast5_40_key_sched(&stp->str_sched, key); memcpy((void *)stp->str_output, (void *)stp->str_iv, sizeof(Block)); stp->str_index = sizeof(Block); } /* * CAST 64 bit Cipher Feedback * * key --->+------+ * +->| CAST |--+ * | +------+ | * | v * INPUT --(---------->(+)+---> DATA * | | * +--------------+ * * * Given: * iV: Initial vector, 64 bits (8 bytes) long. * Dn: the nth chunk of 64 bits (8 bytes) of data to encrypt (decrypt). * On: the nth chunk of 64 bits (8 bytes) of encrypted (decrypted) output. * * V0 = CAST(iV, key) * On = Dn ^ Vn * V(n+1) = CAST(On, key) */ #ifndef CAST_EXPORT_ENCRYPTION void cast_cfb64_encrypt(s, c) register unsigned char *s; int c; { _cast_cfb64_encrypt(s, c, &cast_fb[CFB_128].streams[DIR_ENCRYPT-1]); } #endif void castexp_cfb64_encrypt(s, c) register unsigned char *s; int c; { _cast_cfb64_encrypt(s, c, &cast_fb[CFB_40].streams[DIR_ENCRYPT-1]); } static void _cast_cfb64_encrypt(s, c, stp) register unsigned char *s; int c; register struct cast_stinfo *stp; { register int index; index = stp->str_index; while (c-- > 0) { if (index == sizeof(Block)) { Block b; ck_cast_ecb_encrypt(b, stp->str_output, &stp->str_sched, 0); memcpy((void *)stp->str_feed, (void *)b, sizeof(Block)); index = 0; } /* On encryption, we store (feed ^ data) which is cypher */ *s = stp->str_output[index] = (stp->str_feed[index] ^ *s); s++; index++; } stp->str_index = index; } #ifndef CAST_EXPORT_ENCRYPTION int cast_cfb64_decrypt(data) int data; { return _cast_cfb64_decrypt(data, &cast_fb[CFB_128].streams[DIR_DECRYPT-1]); } #endif int castexp_cfb64_decrypt(data) int data; { return _cast_cfb64_decrypt(data, &cast_fb[CFB_40].streams[DIR_DECRYPT-1]); } static int _cast_cfb64_decrypt(data, stp) int data; register struct cast_stinfo *stp; { int index; if (data == -1) { /* * Back up one byte. It is assumed that we will * never back up more than one byte. If we do, this * may or may not work. */ if (stp->str_index) --stp->str_index; return(0); } index = stp->str_index++; if (index == sizeof(Block)) { Block b; ck_cast_ecb_encrypt(b, stp->str_output, &stp->str_sched, 0); memcpy((void *)stp->str_feed, (void *)b, sizeof(Block)); stp->str_index = 1; /* Next time will be 1 */ index = 0; /* But now use 0 */ } /* On decryption we store (data) which is cypher. */ stp->str_output[index] = data; return(data ^ stp->str_feed[index]); } /* * CAST 64 bit Output Feedback * * key --->+------+ * +->| CAST |--+ * | +------+ | * +------------+ * v * INPUT --------->(+) ----> DATA * * Given: * iV: Initial vector, 64 bits (8 bytes) long. * Dn: the nth chunk of 64 bits (8 bytes) of data to encrypt (decrypt). * On: the nth chunk of 64 bits (8 bytes) of encrypted (decrypted) output. * * V0 = CAST(iV, key) * V(n+1) = CAST(Vn, key) * On = Dn ^ Vn */ #ifndef CAST_EXPORT_ENCRYPTION void cast_ofb64_encrypt(s, c) register unsigned char *s; int c; { _cast_ofb64_encrypt(s, c, &cast_fb[OFB_128].streams[DIR_ENCRYPT-1]); } #endif void castexp_ofb64_encrypt(s, c) register unsigned char *s; int c; { _cast_ofb64_encrypt(s, c, &cast_fb[OFB_40].streams[DIR_ENCRYPT-1]); } static void _cast_ofb64_encrypt(s, c, stp) register unsigned char *s; int c; register struct cast_stinfo *stp; { register int index; index = stp->str_index; while (c-- > 0) { if (index == sizeof(Block)) { Block b; ck_cast_ecb_encrypt(b, stp->str_feed, &stp->str_sched, 0); memcpy((void *)stp->str_feed, (void *)b, sizeof(Block)); index = 0; } *s++ ^= stp->str_feed[index]; index++; } stp->str_index = index; } #ifndef CAST_EXPORT_ENCRYPTION int cast_ofb64_decrypt(data) int data; { return _cast_ofb64_decrypt(data, &cast_fb[OFB_128].streams[DIR_DECRYPT-1]); } #endif int castexp_ofb64_decrypt(data) int data; { return _cast_ofb64_decrypt(data, &cast_fb[OFB_40].streams[DIR_DECRYPT-1]); } static int _cast_ofb64_decrypt(data, stp) int data; register struct cast_stinfo *stp; { int index; if (data == -1) { /* * Back up one byte. It is assumed that we will * never back up more than one byte. If we do, this * may or may not work. */ if (stp->str_index) --stp->str_index; return(0); } index = stp->str_index++; if (index == sizeof(Block)) { Block b; ck_cast_ecb_encrypt(b, stp->str_feed, &stp->str_sched, 0); memcpy((void *)stp->str_feed, (void *)b, sizeof(Block)); stp->str_index = 1; /* Next time will be 1 */ index = 0; /* But now use 0 */ } return(data ^ stp->str_feed[index]); } /* * Copyright (c) 1997 Stanford University * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided * that the above copyright notices and this permission notice appear in * all copies of the software and related documentation. * * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. * * IN NO EVENT SHALL STANFORD BE LIABLE FOR ANY SPECIAL, INCIDENTAL, * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * cast.c * Author: Tom Wu * * An implementation of the CAST-128 encryption algorithm, as * specified in RFC 2144. */ /* The first four S-boxes are for encryption/decryption */ static uint32 S1[] = { 0x30fb40d4, 0x9fa0ff0b, 0x6beccd2f, 0x3f258c7a, 0x1e213f2f, 0x9c004dd3, 0x6003e540, 0xcf9fc949, 0xbfd4af27, 0x88bbbdb5, 0xe2034090, 0x98d09675, 0x6e63a0e0, 0x15c361d2, 0xc2e7661d, 0x22d4ff8e, 0x28683b6f, 0xc07fd059, 0xff2379c8, 0x775f50e2, 0x43c340d3, 0xdf2f8656, 0x887ca41a, 0xa2d2bd2d, 0xa1c9e0d6, 0x346c4819, 0x61b76d87, 0x22540f2f, 0x2abe32e1, 0xaa54166b, 0x22568e3a, 0xa2d341d0, 0x66db40c8, 0xa784392f, 0x004dff2f, 0x2db9d2de, 0x97943fac, 0x4a97c1d8, 0x527644b7, 0xb5f437a7, 0xb82cbaef, 0xd751d159, 0x6ff7f0ed, 0x5a097a1f, 0x827b68d0, 0x90ecf52e, 0x22b0c054, 0xbc8e5935, 0x4b6d2f7f, 0x50bb64a2, 0xd2664910, 0xbee5812d, 0xb7332290, 0xe93b159f, 0xb48ee411, 0x4bff345d, 0xfd45c240, 0xad31973f, 0xc4f6d02e, 0x55fc8165, 0xd5b1caad, 0xa1ac2dae, 0xa2d4b76d, 0xc19b0c50, 0x882240f2, 0x0c6e4f38, 0xa4e4bfd7, 0x4f5ba272, 0x564c1d2f, 0xc59c5319, 0xb949e354, 0xb04669fe, 0xb1b6ab8a, 0xc71358dd, 0x6385c545, 0x110f935d, 0x57538ad5, 0x6a390493, 0xe63d37e0, 0x2a54f6b3, 0x3a787d5f, 0x6276a0b5, 0x19a6fcdf, 0x7a42206a, 0x29f9d4d5, 0xf61b1891, 0xbb72275e, 0xaa508167, 0x38901091, 0xc6b505eb, 0x84c7cb8c, 0x2ad75a0f, 0x874a1427, 0xa2d1936b, 0x2ad286af, 0xaa56d291, 0xd7894360, 0x425c750d, 0x93b39e26, 0x187184c9, 0x6c00b32d, 0x73e2bb14, 0xa0bebc3c, 0x54623779, 0x64459eab, 0x3f328b82, 0x7718cf82, 0x59a2cea6, 0x04ee002e, 0x89fe78e6, 0x3fab0950, 0x325ff6c2, 0x81383f05, 0x6963c5c8, 0x76cb5ad6, 0xd49974c9, 0xca180dcf, 0x380782d5, 0xc7fa5cf6, 0x8ac31511, 0x35e79e13, 0x47da91d0, 0xf40f9086, 0xa7e2419e, 0x31366241, 0x051ef495, 0xaa573b04, 0x4a805d8d, 0x548300d0, 0x00322a3c, 0xbf64cddf, 0xba57a68e, 0x75c6372b, 0x50afd341, 0xa7c13275, 0x915a0bf5, 0x6b54bfab, 0x2b0b1426, 0xab4cc9d7, 0x449ccd82, 0xf7fbf265, 0xab85c5f3, 0x1b55db94, 0xaad4e324, 0xcfa4bd3f, 0x2deaa3e2, 0x9e204d02, 0xc8bd25ac, 0xeadf55b3, 0xd5bd9e98, 0xe31231b2, 0x2ad5ad6c, 0x954329de, 0xadbe4528, 0xd8710f69, 0xaa51c90f, 0xaa786bf6, 0x22513f1e, 0xaa51a79b, 0x2ad344cc,0x7b5a41f0, 0xd37cfbad, 0x1b069505, 0x41ece491, 0xb4c332e6, 0x032268d4, 0xc9600acc, 0xce387e6d, 0xbf6bb16c, 0x6a70fb78, 0x0d03d9c9, 0xd4df39de, 0xe01063da, 0x4736f464, 0x5ad328d8, 0xb347cc96, 0x75bb0fc3, 0x98511bfb, 0x4ffbcc35, 0xb58bcf6a, 0xe11f0abc, 0xbfc5fe4a, 0xa70aec10, 0xac39570a,0x3f04442f, 0x6188b153, 0xe0397a2e, 0x5727cb79, 0x9ceb418f, 0x1cacd68d, 0x2ad37c96, 0x0175cb9d, 0xc69dff09, 0xc75b65f0, 0xd9db40d8, 0xec0e7779, 0x4744ead4, 0xb11c3274, 0xdd24cb9e, 0x7e1c54bd, 0xf01144f9, 0xd2240eb1, 0x9675b3fd, 0xa3ac3755, 0xd47c27af, 0x51c85f4d, 0x56907596, 0xa5bb15e6,0x580304f0, 0xca042cf1, 0x011a37ea, 0x8dbfaadb, 0x35ba3e4a, 0x3526ffa0, 0xc37b4d09, 0xbc306ed9, 0x98a52666, 0x5648f725, 0xff5e569d, 0x0ced63d0, 0x7c63b2cf, 0x700b45e1, 0xd5ea50f1, 0x85a92872, 0xaf1fbda7, 0xd4234870, 0xa7870bf3, 0x2d3b4d79, 0x42e04198, 0x0cd0ede7, 0x26470db8, 0xf881814c,0x474d6ad7, 0x7c0c5e5c, 0xd1231959, 0x381b7298, 0xf5d2f4db, 0xab838653, 0x6e2f1e23, 0x83719c9e, 0xbd91e046, 0x9a56456e, 0xdc39200c, 0x20c8c571, 0x962bda1c, 0xe1e696ff, 0xb141ab08, 0x7cca89b9, 0x1a69e783, 0x02cc4843, 0xa2f7c579, 0x429ef47d, 0x427b169c, 0x5ac9f049, 0xdd8f0f00, 0x5c8165bf }; static uint32 S2[] = { 0x1f201094, 0xef0ba75b, 0x69e3cf7e, 0x393f4380, 0xfe61cf7a, 0xeec5207a, 0x55889c94, 0x72fc0651, 0xada7ef79, 0x4e1d7235, 0xd55a63ce, 0xde0436ba, 0x99c430ef, 0x5f0c0794, 0x18dcdb7d, 0xa1d6eff3, 0xa0b52f7b, 0x59e83605, 0xee15b094, 0xe9ffd909, 0xdc440086, 0xef944459, 0xba83ccb3, 0xe0c3cdfb, 0xd1da4181, 0x3b092ab1, 0xf997f1c1, 0xa5e6cf7b, 0x01420ddb, 0xe4e7ef5b, 0x25a1ff41, 0xe180f806, 0x1fc41080, 0x179bee7a, 0xd37ac6a9, 0xfe5830a4, 0x98de8b7f, 0x77e83f4e, 0x79929269, 0x24fa9f7b, 0xe113c85b, 0xacc40083, 0xd7503525, 0xf7ea615f, 0x62143154, 0x0d554b63, 0x5d681121, 0xc866c359, 0x3d63cf73, 0xcee234c0, 0xd4d87e87, 0x5c672b21, 0x071f6181, 0x39f7627f, 0x361e3084, 0xe4eb573b, 0x602f64a4, 0xd63acd9c, 0x1bbc4635, 0x9e81032d, 0x2701f50c, 0x99847ab4, 0xa0e3df79, 0xba6cf38c, 0x10843094, 0x2537a95e, 0xf46f6ffe, 0xa1ff3b1f, 0x208cfb6a, 0x8f458c74, 0xd9e0a227, 0x4ec73a34, 0xfc884f69, 0x3e4de8df, 0xef0e0088, 0x3559648d, 0x8a45388c, 0x1d804366, 0x721d9bfd, 0xa58684bb, 0xe8256333, 0x844e8212, 0x128d8098, 0xfed33fb4, 0xce280ae1, 0x27e19ba5, 0xd5a6c252, 0xe49754bd, 0xc5d655dd, 0xeb667064, 0x77840b4d, 0xa1b6a801, 0x84db26a9, 0xe0b56714, 0x21f043b7, 0xe5d05860, 0x54f03084, 0x066ff472, 0xa31aa153, 0xdadc4755, 0xb5625dbf, 0x68561be6, 0x83ca6b94, 0x2d6ed23b, 0xeccf01db, 0xa6d3d0ba, 0xb6803d5c, 0xaf77a709, 0x33b4a34c, 0x397bc8d6, 0x5ee22b95, 0x5f0e5304, 0x81ed6f61, 0x20e74364, 0xb45e1378, 0xde18639b, 0x881ca122, 0xb96726d1, 0x8049a7e8, 0x22b7da7b, 0x5e552d25, 0x5272d237, 0x79d2951c, 0xc60d894c, 0x488cb402, 0x1ba4fe5b, 0xa4b09f6b, 0x1ca815cf, 0xa20c3005, 0x8871df63, 0xb9de2fcb, 0x0cc6c9e9, 0x0beeff53, 0xe3214517, 0xb4542835, 0x9f63293c, 0xee41e729, 0x6e1d2d7c, 0x50045286, 0x1e6685f3, 0xf33401c6, 0x30a22c95, 0x31a70850, 0x60930f13, 0x73f98417, 0xa1269859, 0xec645c44, 0x52c877a9, 0xcdff33a6, 0xa02b1741, 0x7cbad9a2, 0x2180036f, 0x50d99c08, 0xcb3f4861, 0xc26bd765, 0x64a3f6ab, 0x80342676, 0x25a75e7b, 0xe4e6d1fc, 0x20c710e6, 0xcdf0b680, 0x17844d3b, 0x31eef84d, 0x7e0824e4, 0x2ccb49eb, 0x846a3bae, 0x8ff77888, 0xee5d60f6, 0x7af75673, 0x2fdd5cdb, 0xa11631c1, 0x30f66f43, 0xb3faec54, 0x157fd7fa, 0xef8579cc, 0xd152de58, 0xdb2ffd5e, 0x8f32ce19, 0x306af97a, 0x02f03ef8, 0x99319ad5, 0xc242fa0f, 0xa7e3ebb0, 0xc68e4906, 0xb8da230c, 0x80823028, 0xdcdef3c8, 0xd35fb171, 0x088a1bc8, 0xbec0c560, 0x61a3c9e8, 0xbca8f54d, 0xc72feffa, 0x22822e99, 0x82c570b4, 0xd8d94e89, 0x8b1c34bc, 0x301e16e6, 0x273be979, 0xb0ffeaa6, 0x61d9b8c6, 0x00b24869, 0xb7ffce3f, 0x08dc283b, 0x43daf65a, 0xf7e19798, 0x7619b72f, 0x8f1c9ba4, 0xdc8637a0, 0x16a7d3b1, 0x9fc393b7, 0xa7136eeb, 0xc6bcc63e, 0x1a513742, 0xef6828bc, 0x520365d6, 0x2d6a77ab, 0x3527ed4b, 0x821fd216, 0x095c6e2e, 0xdb92f2fb, 0x5eea29cb, 0x145892f5, 0x91584f7f, 0x5483697b, 0x2667a8cc, 0x85196048, 0x8c4bacea, 0x833860d4, 0x0d23e0f9, 0x6c387e8a, 0x0ae6d249, 0xb284600c, 0xd835731d, 0xdcb1c647, 0xac4c56ea, 0x3ebd81b3, 0x230eabb0, 0x6438bc87, 0xf0b5b1fa, 0x8f5ea2b3, 0xfc184642, 0x0a036b7a, 0x4fb089bd, 0x649da589, 0xa345415e, 0x5c038323, 0x3e5d3bb9, 0x43d79572, 0x7e6dd07c, 0x06dfdf1e, 0x6c6cc4ef, 0x7160a539, 0x73bfbe70, 0x83877605, 0x4523ecf1 }; static uint32 S3[] = { 0x8defc240, 0x25fa5d9f, 0xeb903dbf, 0xe810c907, 0x47607fff, 0x369fe44b, 0x8c1fc644, 0xaececa90, 0xbeb1f9bf, 0xeefbcaea, 0xe8cf1950, 0x51df07ae, 0x920e8806, 0xf0ad0548, 0xe13c8d83, 0x927010d5, 0x11107d9f, 0x07647db9, 0xb2e3e4d4, 0x3d4f285e, 0xb9afa820, 0xfade82e0, 0xa067268b, 0x8272792e, 0x553fb2c0, 0x489ae22b, 0xd4ef9794, 0x125e3fbc, 0x21fffcee, 0x825b1bfd, 0x9255c5ed, 0x1257a240, 0x4e1a8302, 0xbae07fff, 0x528246e7, 0x8e57140e, 0x3373f7bf, 0x8c9f8188, 0xa6fc4ee8, 0xc982b5a5, 0xa8c01db7, 0x579fc264, 0x67094f31, 0xf2bd3f5f, 0x40fff7c1, 0x1fb78dfc, 0x8e6bd2c1, 0x437be59b, 0x99b03dbf, 0xb5dbc64b, 0x638dc0e6, 0x55819d99, 0xa197c81c, 0x4a012d6e, 0xc5884a28, 0xccc36f71, 0xb843c213, 0x6c0743f1, 0x8309893c, 0x0feddd5f, 0x2f7fe850, 0xd7c07f7e, 0x02507fbf, 0x5afb9a04, 0xa747d2d0, 0x1651192e, 0xaf70bf3e, 0x58c31380, 0x5f98302e, 0x727cc3c4, 0x0a0fb402, 0x0f7fef82, 0x8c96fdad, 0x5d2c2aae, 0x8ee99a49, 0x50da88b8, 0x8427f4a0, 0x1eac5790, 0x796fb449, 0x8252dc15, 0xefbd7d9b, 0xa672597d, 0xada840d8, 0x45f54504, 0xfa5d7403, 0xe83ec305, 0x4f91751a, 0x925669c2, 0x23efe941, 0xa903f12e, 0x60270df2, 0x0276e4b6, 0x94fd6574, 0x927985b2, 0x8276dbcb, 0x02778176, 0xf8af918d, 0x4e48f79e, 0x8f616ddf, 0xe29d840e, 0x842f7d83, 0x340ce5c8, 0x96bbb682, 0x93b4b148, 0xef303cab, 0x984faf28, 0x779faf9b, 0x92dc560d, 0x224d1e20, 0x8437aa88, 0x7d29dc96, 0x2756d3dc, 0x8b907cee, 0xb51fd240, 0xe7c07ce3, 0xe566b4a1, 0xc3e9615e, 0x3cf8209d, 0x6094d1e3, 0xcd9ca341, 0x5c76460e, 0x00ea983b, 0xd4d67881, 0xfd47572c, 0xf76cedd9, 0xbda8229c, 0x127dadaa, 0x438a074e, 0x1f97c090, 0x081bdb8a, 0x93a07ebe, 0xb938ca15, 0x97b03cff, 0x3dc2c0f8, 0x8d1ab2ec, 0x64380e51, 0x68cc7bfb, 0xd90f2788, 0x12490181, 0x5de5ffd4, 0xdd7ef86a, 0x76a2e214, 0xb9a40368, 0x925d958f, 0x4b39fffa, 0xba39aee9, 0xa4ffd30b, 0xfaf7933b, 0x6d498623, 0x193cbcfa, 0x27627545, 0x825cf47a, 0x61bd8ba0, 0xd11e42d1, 0xcead04f4, 0x127ea392, 0x10428db7, 0x8272a972, 0x9270c4a8, 0x127de50b, 0x285ba1c8, 0x3c62f44f, 0x35c0eaa5, 0xe805d231, 0x428929fb, 0xb4fcdf82, 0x4fb66a53, 0x0e7dc15b, 0x1f081fab, 0x108618ae, 0xfcfd086d, 0xf9ff2889, 0x694bcc11, 0x236a5cae, 0x12deca4d, 0x2c3f8cc5, 0xd2d02dfe, 0xf8ef5896, 0xe4cf52da, 0x95155b67, 0x494a488c, 0xb9b6a80c, 0x5c8f82bc, 0x89d36b45, 0x3a609437, 0xec00c9a9, 0x44715253, 0x0a874b49, 0xd773bc40, 0x7c34671c, 0x02717ef6, 0x4feb5536, 0xa2d02fff, 0xd2bf60c4, 0xd43f03c0, 0x50b4ef6d, 0x07478cd1, 0x006e1888, 0xa2e53f55, 0xb9e6d4bc, 0xa2048016, 0x97573833, 0xd7207d67, 0xde0f8f3d, 0x72f87b33, 0xabcc4f33, 0x7688c55d, 0x7b00a6b0, 0x947b0001, 0x570075d2, 0xf9bb88f8, 0x8942019e, 0x4264a5ff, 0x856302e0, 0x72dbd92b, 0xee971b69, 0x6ea22fde, 0x5f08ae2b, 0xaf7a616d, 0xe5c98767, 0xcf1febd2, 0x61efc8c2, 0xf1ac2571, 0xcc8239c2, 0x67214cb8, 0xb1e583d1, 0xb7dc3e62, 0x7f10bdce, 0xf90a5c38, 0x0ff0443d, 0x606e6dc6, 0x60543a49, 0x5727c148, 0x2be98a1d, 0x8ab41738, 0x20e1be24, 0xaf96da0f, 0x68458425, 0x99833be5, 0x600d457d, 0x282f9350, 0x8334b362, 0xd91d1120, 0x2b6d8da0, 0x642b1e31, 0x9c305a00, 0x52bce688, 0x1b03588a, 0xf7baefd5, 0x4142ed9c, 0xa4315c11, 0x83323ec5, 0xdfef4636, 0xa133c501, 0xe9d3531c, 0xee353783 }; static uint32 S4[] = { 0x9db30420, 0x1fb6e9de, 0xa7be7bef, 0xd273a298, 0x4a4f7bdb, 0x64ad8c57, 0x85510443, 0xfa020ed1, 0x7e287aff, 0xe60fb663, 0x095f35a1, 0x79ebf120, 0xfd059d43, 0x6497b7b1, 0xf3641f63, 0x241e4adf, 0x28147f5f, 0x4fa2b8cd, 0xc9430040, 0x0cc32220, 0xfdd30b30, 0xc0a5374f, 0x1d2d00d9, 0x24147b15, 0xee4d111a, 0x0fca5167, 0x71ff904c, 0x2d195ffe, 0x1a05645f, 0x0c13fefe, 0x081b08ca, 0x05170121, 0x80530100, 0xe83e5efe, 0xac9af4f8, 0x7fe72701, 0xd2b8ee5f, 0x06df4261, 0xbb9e9b8a, 0x7293ea25, 0xce84ffdf, 0xf5718801, 0x3dd64b04, 0xa26f263b, 0x7ed48400, 0x547eebe6, 0x446d4ca0, 0x6cf3d6f5, 0x2649abdf, 0xaea0c7f5, 0x36338cc1, 0x503f7e93, 0xd3772061, 0x11b638e1, 0x72500e03, 0xf80eb2bb, 0xabe0502e, 0xec8d77de, 0x57971e81, 0xe14f6746, 0xc9335400, 0x6920318f, 0x081dbb99, 0xffc304a5, 0x4d351805, 0x7f3d5ce3, 0xa6c866c6, 0x5d5bcca9, 0xdaec6fea, 0x9f926f91, 0x9f46222f, 0x3991467d, 0xa5bf6d8e, 0x1143c44f, 0x43958302, 0xd0214eeb, 0x022083b8, 0x3fb6180c, 0x18f8931e, 0x281658e6, 0x26486e3e, 0x8bd78a70, 0x7477e4c1, 0xb506e07c, 0xf32d0a25, 0x79098b02, 0xe4eabb81, 0x28123b23, 0x69dead38, 0x1574ca16, 0xdf871b62, 0x211c40b7, 0xa51a9ef9, 0x0014377b, 0x041e8ac8, 0x09114003, 0xbd59e4d2, 0xe3d156d5, 0x4fe876d5, 0x2f91a340, 0x557be8de, 0x00eae4a7, 0x0ce5c2ec, 0x4db4bba6, 0xe756bdff, 0xdd3369ac, 0xec17b035, 0x06572327, 0x99afc8b0, 0x56c8c391, 0x6b65811c, 0x5e146119, 0x6e85cb75, 0xbe07c002, 0xc2325577, 0x893ff4ec, 0x5bbfc92d, 0xd0ec3b25, 0xb7801ab7, 0x8d6d3b24, 0x20c763ef, 0xc366a5fc, 0x9c382880, 0x0ace3205, 0xaac9548a, 0xeca1d7c7, 0x041afa32, 0x1d16625a, 0x6701902c, 0x9b757a54, 0x31d477f7, 0x9126b031, 0x36cc6fdb, 0xc70b8b46, 0xd9e66a48, 0x56e55a79, 0x026a4ceb, 0x52437eff, 0x2f8f76b4, 0x0df980a5, 0x8674cde3, 0xedda04eb, 0x17a9be04, 0x2c18f4df, 0xb7747f9d, 0xab2af7b4, 0xefc34d20, 0x2e096b7c, 0x1741a254, 0xe5b6a035, 0x213d42f6, 0x2c1c7c26, 0x61c2f50f, 0x6552daf9, 0xd2c231f8, 0x25130f69, 0xd8167fa2, 0x0418f2c8, 0x001a96a6, 0x0d1526ab, 0x63315c21, 0x5e0a72ec, 0x49bafefd, 0x187908d9, 0x8d0dbd86, 0x311170a7, 0x3e9b640c, 0xcc3e10d7, 0xd5cad3b6, 0x0caec388, 0xf73001e1, 0x6c728aff, 0x71eae2a1, 0x1f9af36e, 0xcfcbd12f, 0xc1de8417, 0xac07be6b, 0xcb44a1d8, 0x8b9b0f56, 0x013988c3, 0xb1c52fca, 0xb4be31cd, 0xd8782806, 0x12a3a4e2, 0x6f7de532, 0x58fd7eb6, 0xd01ee900, 0x24adffc2, 0xf4990fc5, 0x9711aac5, 0x001d7b95, 0x82e5e7d2, 0x109873f6, 0x00613096, 0xc32d9521, 0xada121ff, 0x29908415, 0x7fbb977f, 0xaf9eb3db, 0x29c9ed2a, 0x5ce2a465, 0xa730f32c, 0xd0aa3fe8, 0x8a5cc091, 0xd49e2ce7, 0x0ce454a9, 0xd60acd86, 0x015f1919, 0x77079103, 0xdea03af6, 0x78a8565e, 0xdee356df, 0x21f05cbe, 0x8b75e387, 0xb3c50651, 0xb8a5c3ef, 0xd8eeb6d2, 0xe523be77, 0xc2154529, 0x2f69efdf, 0xafe67afb, 0xf470c4b2, 0xf3e0eb5b, 0xd6cc9876, 0x39e4460c, 0x1fda8538, 0x1987832f, 0xca007367, 0xa99144f8, 0x296b299e, 0x492fc295, 0x9266beab, 0xb5676e69, 0x9bd3ddda, 0xdf7e052f, 0xdb25701c, 0x1b5e51ee, 0xf65324e6, 0x6afce36c, 0x0316cc04, 0x8644213e, 0xb7dc59d0, 0x7965291f, 0xccd6fd43, 0x41823979, 0x932bcdf6, 0xb657c34d, 0x4edfd282, 0x7ae5290c, 0x3cb9536b, 0x851e20fe, 0x9833557e, 0x13ecf0b0, 0xd3ffb372, 0x3f85c5c1, 0x0aef7ed2 }; /* Encrypt/decrypt one 64-bit block of data */ void ck_cast_ecb_encrypt(out, in, sched, mode) uint8p out; uint8p in; CastKeySched * sched; int mode; /* zero means encrypt */ { uint32 t[2]; #ifdef LITTLE_ENDIAN t[0] = (in[0] << 24) | (in[1] << 16) | (in[2] << 8) | in[3]; t[1] = (in[4] << 24) | (in[5] << 16) | (in[6] << 8) | in[7]; #else t[0] = *(uint32p) in; t[1] = *(uint32p) (in + 4); #endif ck_cast_ecb_crypt(t, sched, mode); #ifdef LITTLE_ENDIAN out[0] = (t[0] >> 24) & 0xff; out[1] = (t[0] >> 16) & 0xff; out[2] = (t[0] >> 8) & 0xff; out[3] = t[0] & 0xff; out[4] = (t[1] >> 24) & 0xff; out[5] = (t[1] >> 16) & 0xff; out[6] = (t[1] >> 8) & 0xff; out[7] = t[1] & 0xff; #else *(uint32p) out = t[0]; *(uint32p) (out + 4) = t[1]; #endif } void ck_cast_ecb_crypt(data, sched, mode) uint32p data; CastKeySched * sched; int mode; { register uint32 L, R, temp; register struct CastSubkeyPair * kp; register uint8p Ia, Ib, Ic, Id; uint32 I; #ifdef LITTLE_ENDIAN Id = (uint8p) &I; Ic = Id + 1; Ib = Ic + 1; Ia = Ib + 1; #else Ia = (uint8p) &I; Ib = Ia + 1; Ic = Ib + 1; Id = Ic + 1; #endif L = data[0]; R = data[1]; #define type0(left,right) \ temp = kp->Km + right;\ I = (temp << kp->Kr) | (temp >> (32 - kp->Kr));\ left ^= ((S1[*Ia] ^ S2[*Ib]) - S3[*Ic]) + S4[*Id]; #define type1(left,right) \ temp = kp->Km ^ right;\ I = (temp << kp->Kr) | (temp >> (32 - kp->Kr));\ left ^= ((S1[*Ia] - S2[*Ib]) + S3[*Ic]) ^ S4[*Id]; #define type2(left,right) \ temp = kp->Km - right;\ I = (temp << kp->Kr) | (temp >> (32 - kp->Kr));\ left ^= ((S1[*Ia] + S2[*Ib]) ^ S3[*Ic]) - S4[*Id]; if(mode) { #ifndef CAST_EXPORT_ENCRYPTION if(sched->ksize > 10) { kp = &sched->K[15]; type0(L, R); --kp; type2(R, L); --kp; type1(L, R); --kp; type0(R, L); --kp; } else #endif kp = &sched->K[11]; type2(L, R); --kp; type1(R, L); --kp; type0(L, R); --kp; type2(R, L); --kp; type1(L, R); --kp; type0(R, L); --kp; type2(L, R); --kp; type1(R, L); --kp; type0(L, R); --kp; type2(R, L); --kp; type1(L, R); --kp; type0(R, L); } else { kp = &sched->K[0]; type0(L, R); ++kp; type1(R, L); ++kp; type2(L, R); ++kp; type0(R, L); ++kp; type1(L, R); ++kp; type2(R, L); ++kp; type0(L, R); ++kp; type1(R, L); ++kp; type2(L, R); ++kp; type0(R, L); ++kp; type1(L, R); ++kp; type2(R, L); ++kp; #ifndef CAST_EXPORT_ENCRYPTION if(sched->ksize > 10) { type0(L, R); ++kp; type1(R, L); ++kp; type2(L, R); ++kp; type0(R, L); } #endif } data[0] = R; data[1] = L; } /* The last four S-boxes are for key schedule setup */ static uint32 S5[] = { 0x7ec90c04, 0x2c6e74b9, 0x9b0e66df, 0xa6337911, 0xb86a7fff, 0x1dd358f5, 0x44dd9d44, 0x1731167f, 0x08fbf1fa, 0xe7f511cc, 0xd2051b00, 0x735aba00, 0x2ab722d8, 0x386381cb, 0xacf6243a, 0x69befd7a, 0xe6a2e77f, 0xf0c720cd, 0xc4494816, 0xccf5c180, 0x38851640, 0x15b0a848, 0xe68b18cb, 0x4caadeff, 0x5f480a01, 0x0412b2aa, 0x259814fc, 0x41d0efe2, 0x4e40b48d, 0x248eb6fb, 0x8dba1cfe, 0x41a99b02, 0x1a550a04, 0xba8f65cb, 0x7251f4e7, 0x95a51725, 0xc106ecd7, 0x97a5980a, 0xc539b9aa, 0x4d79fe6a, 0xf2f3f763, 0x68af8040, 0xed0c9e56, 0x11b4958b, 0xe1eb5a88, 0x8709e6b0, 0xd7e07156, 0x4e29fea7, 0x6366e52d, 0x02d1c000, 0xc4ac8e05, 0x9377f571, 0x0c05372a, 0x578535f2, 0x2261be02, 0xd642a0c9, 0xdf13a280, 0x74b55bd2, 0x682199c0, 0xd421e5ec, 0x53fb3ce8, 0xc8adedb3, 0x28a87fc9, 0x3d959981, 0x5c1ff900, 0xfe38d399, 0x0c4eff0b, 0x062407ea, 0xaa2f4fb1, 0x4fb96976, 0x90c79505, 0xb0a8a774, 0xef55a1ff, 0xe59ca2c2, 0xa6b62d27, 0xe66a4263, 0xdf65001f, 0x0ec50966, 0xdfdd55bc, 0x29de0655, 0x911e739a, 0x17af8975, 0x32c7911c, 0x89f89468, 0x0d01e980, 0x524755f4, 0x03b63cc9, 0x0cc844b2, 0xbcf3f0aa, 0x87ac36e9, 0xe53a7426, 0x01b3d82b, 0x1a9e7449, 0x64ee2d7e, 0xcddbb1da, 0x01c94910, 0xb868bf80, 0x0d26f3fd, 0x9342ede7, 0x04a5c284, 0x636737b6, 0x50f5b616, 0xf24766e3, 0x8eca36c1, 0x136e05db, 0xfef18391, 0xfb887a37, 0xd6e7f7d4, 0xc7fb7dc9, 0x3063fcdf, 0xb6f589de, 0xec2941da, 0x26e46695, 0xb7566419, 0xf654efc5, 0xd08d58b7, 0x48925401, 0xc1bacb7f, 0xe5ff550f, 0xb6083049, 0x5bb5d0e8, 0x87d72e5a, 0xab6a6ee1, 0x223a66ce, 0xc62bf3cd, 0x9e0885f9, 0x68cb3e47, 0x086c010f, 0xa21de820, 0xd18b69de, 0xf3f65777, 0xfa02c3f6, 0x407edac3, 0xcbb3d550, 0x1793084d, 0xb0d70eba, 0x0ab378d5, 0xd951fb0c, 0xded7da56, 0x4124bbe4, 0x94ca0b56, 0x0f5755d1, 0xe0e1e56e, 0x6184b5be, 0x580a249f, 0x94f74bc0, 0xe327888e, 0x9f7b5561, 0xc3dc0280, 0x05687715, 0x646c6bd7, 0x44904db3, 0x66b4f0a3, 0xc0f1648a, 0x697ed5af, 0x49e92ff6, 0x309e374f, 0x2cb6356a, 0x85808573, 0x4991f840, 0x76f0ae02, 0x083be84d, 0x28421c9a, 0x44489406, 0x736e4cb8, 0xc1092910, 0x8bc95fc6, 0x7d869cf4, 0x134f616f, 0x2e77118d, 0xb31b2be1, 0xaa90b472, 0x3ca5d717, 0x7d161bba, 0x9cad9010, 0xaf462ba2, 0x9fe459d2, 0x45d34559, 0xd9f2da13, 0xdbc65487, 0xf3e4f94e, 0x176d486f, 0x097c13ea, 0x631da5c7, 0x445f7382, 0x175683f4, 0xcdc66a97, 0x70be0288, 0xb3cdcf72, 0x6e5dd2f3, 0x20936079, 0x459b80a5, 0xbe60e2db, 0xa9c23101, 0xeba5315c, 0x224e42f2, 0x1c5c1572, 0xf6721b2c, 0x1ad2fff3, 0x8c25404e, 0x324ed72f, 0x4067b7fd, 0x0523138e, 0x5ca3bc78, 0xdc0fd66e, 0x75922283, 0x784d6b17, 0x58ebb16e, 0x44094f85, 0x3f481d87, 0xfcfeae7b, 0x77b5ff76, 0x8c2302bf, 0xaaf47556, 0x5f46b02a, 0x2b092801, 0x3d38f5f7, 0x0ca81f36, 0x52af4a8a, 0x66d5e7c0, 0xdf3b0874, 0x95055110, 0x1b5ad7a8, 0xf61ed5ad, 0x6cf6e479, 0x20758184, 0xd0cefa65, 0x88f7be58, 0x4a046826, 0x0ff6f8f3, 0xa09c7f70, 0x5346aba0, 0x5ce96c28, 0xe176eda3, 0x6bac307f, 0x376829d2, 0x85360fa9, 0x17e3fe2a, 0x24b79767, 0xf5a96b20, 0xd6cd2595, 0x68ff1ebf, 0x7555442c, 0xf19f06be, 0xf9e0659a, 0xeeb9491d, 0x34010718, 0xbb30cab8, 0xe822fe15, 0x88570983, 0x750e6249, 0xda627e55, 0x5e76ffa8, 0xb1534546, 0x6d47de08, 0xefe9e7d4 }; static uint32 S6[] = { 0xf6fa8f9d, 0x2cac6ce1, 0x4ca34867, 0xe2337f7c, 0x95db08e7, 0x016843b4, 0xeced5cbc, 0x325553ac, 0xbf9f0960, 0xdfa1e2ed, 0x83f0579d, 0x63ed86b9, 0x1ab6a6b8, 0xde5ebe39, 0xf38ff732, 0x8989b138, 0x33f14961, 0xc01937bd, 0xf506c6da, 0xe4625e7e, 0xa308ea99, 0x4e23e33c, 0x79cbd7cc, 0x48a14367, 0xa3149619, 0xfec94bd5, 0xa114174a, 0xeaa01866, 0xa084db2d, 0x09a8486f, 0xa888614a, 0x2900af98, 0x01665991, 0xe1992863, 0xc8f30c60, 0x2e78ef3c, 0xd0d51932, 0xcf0fec14, 0xf7ca07d2, 0xd0a82072, 0xfd41197e, 0x9305a6b0, 0xe86be3da, 0x74bed3cd, 0x372da53c, 0x4c7f4448, 0xdab5d440, 0x6dba0ec3, 0x083919a7, 0x9fbaeed9, 0x49dbcfb0, 0x4e670c53, 0x5c3d9c01, 0x64bdb941, 0x2c0e636a, 0xba7dd9cd, 0xea6f7388, 0xe70bc762, 0x35f29adb, 0x5c4cdd8d, 0xf0d48d8c, 0xb88153e2, 0x08a19866, 0x1ae2eac8, 0x284caf89, 0xaa928223, 0x9334be53, 0x3b3a21bf, 0x16434be3, 0x9aea3906, 0xefe8c36e, 0xf890cdd9, 0x80226dae, 0xc340a4a3, 0xdf7e9c09, 0xa694a807, 0x5b7c5ecc, 0x221db3a6, 0x9a69a02f, 0x68818a54, 0xceb2296f, 0x53c0843a, 0xfe893655, 0x25bfe68a, 0xb4628abc, 0xcf222ebf, 0x25ac6f48, 0xa9a99387, 0x53bddb65, 0xe76ffbe7, 0xe967fd78, 0x0ba93563, 0x8e342bc1, 0xe8a11be9, 0x4980740d, 0xc8087dfc, 0x8de4bf99, 0xa11101a0, 0x7fd37975, 0xda5a26c0, 0xe81f994f, 0x9528cd89, 0xfd339fed, 0xb87834bf, 0x5f04456d, 0x22258698, 0xc9c4c83b, 0x2dc156be, 0x4f628daa, 0x57f55ec5, 0xe2220abe, 0xd2916ebf, 0x4ec75b95, 0x24f2c3c0, 0x42d15d99, 0xcd0d7fa0, 0x7b6e27ff, 0xa8dc8af0, 0x7345c106, 0xf41e232f, 0x35162386, 0xe6ea8926, 0x3333b094, 0x157ec6f2, 0x372b74af, 0x692573e4, 0xe9a9d848, 0xf3160289, 0x3a62ef1d, 0xa787e238, 0xf3a5f676, 0x74364853, 0x20951063, 0x4576698d, 0xb6fad407, 0x592af950, 0x36f73523, 0x4cfb6e87, 0x7da4cec0, 0x6c152daa, 0xcb0396a8, 0xc50dfe5d, 0xfcd707ab, 0x0921c42f, 0x89dff0bb, 0x5fe2be78, 0x448f4f33, 0x754613c9, 0x2b05d08d, 0x48b9d585, 0xdc049441, 0xc8098f9b, 0x7dede786, 0xc39a3373, 0x42410005, 0x6a091751, 0x0ef3c8a6, 0x890072d6, 0x28207682, 0xa9a9f7be, 0xbf32679d, 0xd45b5b75, 0xb353fd00, 0xcbb0e358, 0x830f220a, 0x1f8fb214, 0xd372cf08, 0xcc3c4a13, 0x8cf63166, 0x061c87be, 0x88c98f88, 0x6062e397, 0x47cf8e7a, 0xb6c85283, 0x3cc2acfb, 0x3fc06976, 0x4e8f0252, 0x64d8314d, 0xda3870e3, 0x1e665459, 0xc10908f0, 0x513021a5, 0x6c5b68b7, 0x822f8aa0, 0x3007cd3e, 0x74719eef, 0xdc872681, 0x073340d4, 0x7e432fd9, 0x0c5ec241, 0x8809286c, 0xf592d891, 0x08a930f6, 0x957ef305, 0xb7fbffbd, 0xc266e96f, 0x6fe4ac98, 0xb173ecc0, 0xbc60b42a, 0x953498da, 0xfba1ae12, 0x2d4bd736, 0x0f25faab, 0xa4f3fceb, 0xe2969123, 0x257f0c3d, 0x9348af49, 0x361400bc, 0xe8816f4a, 0x3814f200, 0xa3f94043, 0x9c7a54c2, 0xbc704f57, 0xda41e7f9, 0xc25ad33a, 0x54f4a084, 0xb17f5505, 0x59357cbe, 0xedbd15c8, 0x7f97c5ab, 0xba5ac7b5, 0xb6f6deaf, 0x3a479c3a, 0x5302da25, 0x653d7e6a, 0x54268d49, 0x51a477ea, 0x5017d55b, 0xd7d25d88, 0x44136c76, 0x0404a8c8, 0xb8e5a121, 0xb81a928a, 0x60ed5869, 0x97c55b96, 0xeaec991b, 0x29935913, 0x01fdb7f1, 0x088e8dfa, 0x9ab6f6f5, 0x3b4cbf9f, 0x4a5de3ab, 0xe6051d35, 0xa0e1d855, 0xd36b4cf1, 0xf544edeb, 0xb0e93524, 0xbebb8fbd, 0xa2d762cf, 0x49c92f54, 0x38b5f331, 0x7128a454, 0x48392905, 0xa65b1db8, 0x851c97bd, 0xd675cf2f }; static uint32 S7[] = { 0x85e04019, 0x332bf567, 0x662dbfff, 0xcfc65693, 0x2a8d7f6f, 0xab9bc912, 0xde6008a1, 0x2028da1f, 0x0227bce7, 0x4d642916, 0x18fac300, 0x50f18b82, 0x2cb2cb11, 0xb232e75c, 0x4b3695f2, 0xb28707de, 0xa05fbcf6, 0xcd4181e9, 0xe150210c, 0xe24ef1bd, 0xb168c381, 0xfde4e789, 0x5c79b0d8, 0x1e8bfd43, 0x4d495001, 0x38be4341, 0x913cee1d, 0x92a79c3f, 0x089766be, 0xbaeeadf4, 0x1286becf, 0xb6eacb19, 0x2660c200, 0x7565bde4, 0x64241f7a, 0x8248dca9, 0xc3b3ad66, 0x28136086, 0x0bd8dfa8, 0x356d1cf2, 0x107789be, 0xb3b2e9ce, 0x0502aa8f, 0x0bc0351e, 0x166bf52a, 0xeb12ff82, 0xe3486911, 0xd34d7516, 0x4e7b3aff, 0x5f43671b, 0x9cf6e037, 0x4981ac83, 0x334266ce, 0x8c9341b7, 0xd0d854c0, 0xcb3a6c88, 0x47bc2829, 0x4725ba37, 0xa66ad22b, 0x7ad61f1e, 0x0c5cbafa, 0x4437f107, 0xb6e79962, 0x42d2d816, 0x0a961288, 0xe1a5c06e, 0x13749e67, 0x72fc081a, 0xb1d139f7, 0xf9583745, 0xcf19df58, 0xbec3f756, 0xc06eba30, 0x07211b24, 0x45c28829, 0xc95e317f, 0xbc8ec511, 0x38bc46e9, 0xc6e6fa14, 0xbae8584a, 0xad4ebc46, 0x468f508b, 0x7829435f, 0xf124183b, 0x821dba9f, 0xaff60ff4, 0xea2c4e6d, 0x16e39264, 0x92544a8b, 0x009b4fc3, 0xaba68ced, 0x9ac96f78, 0x06a5b79a, 0xb2856e6e, 0x1aec3ca9, 0xbe838688, 0x0e0804e9, 0x55f1be56, 0xe7e5363b, 0xb3a1f25d, 0xf7debb85, 0x61fe033c, 0x16746233, 0x3c034c28, 0xda6d0c74, 0x79aac56c, 0x3ce4e1ad, 0x51f0c802, 0x98f8f35a, 0x1626a49f, 0xeed82b29, 0x1d382fe3, 0x0c4fb99a, 0xbb325778, 0x3ec6d97b, 0x6e77a6a9, 0xcb658b5c, 0xd45230c7, 0x2bd1408b, 0x60c03eb7, 0xb9068d78, 0xa33754f4, 0xf430c87d, 0xc8a71302, 0xb96d8c32, 0xebd4e7be, 0xbe8b9d2d, 0x7979fb06, 0xe7225308, 0x8b75cf77, 0x11ef8da4, 0xe083c858, 0x8d6b786f, 0x5a6317a6, 0xfa5cf7a0, 0x5dda0033, 0xf28ebfb0, 0xf5b9c310, 0xa0eac280, 0x08b9767a, 0xa3d9d2b0, 0x79d34217, 0x021a718d, 0x9ac6336a, 0x2711fd60, 0x438050e3, 0x069908a8, 0x3d7fedc4, 0x826d2bef, 0x4eeb8476, 0x488dcf25, 0x36c9d566, 0x28e74e41, 0xc2610aca, 0x3d49a9cf, 0xbae3b9df, 0xb65f8de6, 0x92aeaf64, 0x3ac7d5e6, 0x9ea80509, 0xf22b017d, 0xa4173f70, 0xdd1e16c3, 0x15e0d7f9, 0x50b1b887, 0x2b9f4fd5, 0x625aba82, 0x6a017962, 0x2ec01b9c, 0x15488aa9, 0xd716e740, 0x40055a2c, 0x93d29a22, 0xe32dbf9a, 0x058745b9, 0x3453dc1e, 0xd699296e, 0x496cff6f, 0x1c9f4986, 0xdfe2ed07, 0xb87242d1, 0x19de7eae, 0x053e561a, 0x15ad6f8c, 0x66626c1c, 0x7154c24c, 0xea082b2a, 0x93eb2939, 0x17dcb0f0, 0x58d4f2ae, 0x9ea294fb, 0x52cf564c, 0x9883fe66, 0x2ec40581, 0x763953c3, 0x01d6692e, 0xd3a0c108, 0xa1e7160e, 0xe4f2dfa6, 0x693ed285, 0x74904698, 0x4c2b0edd, 0x4f757656, 0x5d393378, 0xa132234f, 0x3d321c5d, 0xc3f5e194, 0x4b269301, 0xc79f022f, 0x3c997e7e, 0x5e4f9504, 0x3ffafbbd, 0x76f7ad0e, 0x296693f4, 0x3d1fce6f, 0xc61e45be, 0xd3b5ab34, 0xf72bf9b7, 0x1b0434c0, 0x4e72b567, 0x5592a33d, 0xb5229301, 0xcfd2a87f, 0x60aeb767, 0x1814386b, 0x30bcc33d, 0x38a0c07d, 0xfd1606f2, 0xc363519b, 0x589dd390, 0x5479f8e6, 0x1cb8d647, 0x97fd61a9, 0xea7759f4, 0x2d57539d, 0x569a58cf, 0xe84e63ad, 0x462e1b78, 0x6580f87e, 0xf3817914, 0x91da55f4, 0x40a230f3, 0xd1988f35, 0xb6e318d2, 0x3ffa50bc, 0x3d40f021, 0xc3c0bdae, 0x4958c24c, 0x518f36b2, 0x84b1d370, 0x0fedce83, 0x878ddada, 0xf2a279c7, 0x94e01be8, 0x90716f4b, 0x954b8aa3 }; static uint32 S8[] = { 0xe216300d, 0xbbddfffc, 0xa7ebdabd, 0x35648095, 0x7789f8b7, 0xe6c1121b, 0x0e241600, 0x052ce8b5, 0x11a9cfb0, 0xe5952f11, 0xece7990a, 0x9386d174, 0x2a42931c, 0x76e38111, 0xb12def3a, 0x37ddddfc, 0xde9adeb1, 0x0a0cc32c, 0xbe197029, 0x84a00940, 0xbb243a0f, 0xb4d137cf, 0xb44e79f0, 0x049eedfd, 0x0b15a15d, 0x480d3168, 0x8bbbde5a, 0x669ded42, 0xc7ece831, 0x3f8f95e7, 0x72df191b, 0x7580330d, 0x94074251, 0x5c7dcdfa, 0xabbe6d63, 0xaa402164, 0xb301d40a, 0x02e7d1ca, 0x53571dae, 0x7a3182a2, 0x12a8ddec, 0xfdaa335d, 0x176f43e8, 0x71fb46d4, 0x38129022, 0xce949ad4, 0xb84769ad, 0x965bd862, 0x82f3d055, 0x66fb9767, 0x15b80b4e, 0x1d5b47a0, 0x4cfde06f, 0xc28ec4b8, 0x57e8726e, 0x647a78fc, 0x99865d44, 0x608bd593, 0x6c200e03, 0x39dc5ff6, 0x5d0b00a3, 0xae63aff2, 0x7e8bd632, 0x70108c0c, 0xbbd35049, 0x2998df04, 0x980cf42a, 0x9b6df491, 0x9e7edd53, 0x06918548, 0x58cb7e07, 0x3b74ef2e, 0x522fffb1, 0xd24708cc, 0x1c7e27cd, 0xa4eb215b, 0x3cf1d2e2, 0x19b47a38, 0x424f7618, 0x35856039, 0x9d17dee7, 0x27eb35e6, 0xc9aff67b, 0x36baf5b8, 0x09c467cd, 0xc18910b1, 0xe11dbf7b, 0x06cd1af8, 0x7170c608, 0x2d5e3354, 0xd4de495a, 0x64c6d006, 0xbcc0c62c, 0x3dd00db3, 0x708f8f34, 0x77d51b42, 0x264f620f, 0x24b8d2bf, 0x15c1b79e, 0x46a52564, 0xf8d7e54e, 0x3e378160, 0x7895cda5, 0x859c15a5, 0xe6459788, 0xc37bc75f, 0xdb07ba0c, 0x0676a3ab, 0x7f229b1e, 0x31842e7b, 0x24259fd7, 0xf8bef472, 0x835ffcb8, 0x6df4c1f2, 0x96f5b195, 0xfd0af0fc, 0xb0fe134c, 0xe2506d3d, 0x4f9b12ea, 0xf215f225, 0xa223736f, 0x9fb4c428, 0x25d04979, 0x34c713f8, 0xc4618187, 0xea7a6e98, 0x7cd16efc, 0x1436876c, 0xf1544107, 0xbedeee14, 0x56e9af27, 0xa04aa441, 0x3cf7c899, 0x92ecbae6, 0xdd67016d, 0x151682eb, 0xa842eedf, 0xfdba60b4, 0xf1907b75, 0x20e3030f, 0x24d8c29e, 0xe139673b, 0xefa63fb8, 0x71873054, 0xb6f2cf3b, 0x9f326442, 0xcb15a4cc, 0xb01a4504, 0xf1e47d8d, 0x844a1be5, 0xbae7dfdc, 0x42cbda70, 0xcd7dae0a, 0x57e85b7a, 0xd53f5af6, 0x20cf4d8c, 0xcea4d428, 0x79d130a4, 0x3486ebfb, 0x33d3cddc, 0x77853b53, 0x37effcb5, 0xc5068778, 0xe580b3e6, 0x4e68b8f4, 0xc5c8b37e, 0x0d809ea2, 0x398feb7c, 0x132a4f94, 0x43b7950e, 0x2fee7d1c, 0x223613bd, 0xdd06caa2, 0x37df932b, 0xc4248289, 0xacf3ebc3, 0x5715f6b7, 0xef3478dd, 0xf267616f, 0xc148cbe4, 0x9052815e, 0x5e410fab, 0xb48a2465, 0x2eda7fa4, 0xe87b40e4, 0xe98ea084, 0x5889e9e1, 0xefd390fc, 0xdd07d35b, 0xdb485694, 0x38d7e5b2, 0x57720101, 0x730edebc, 0x5b643113, 0x94917e4f, 0x503c2fba, 0x646f1282, 0x7523d24a, 0xe0779695, 0xf9c17a8f, 0x7a5b2121, 0xd187b896, 0x29263a4d, 0xba510cdf, 0x81f47c9f, 0xad1163ed, 0xea7b5965, 0x1a00726e, 0x11403092, 0x00da6d77, 0x4a0cdd61, 0xad1f4603, 0x605bdfb0, 0x9eedc364, 0x22ebe6a8, 0xcee7d28a, 0xa0e736a0, 0x5564a6b9, 0x10853209, 0xc7eb8f37, 0x2de705ca, 0x8951570f, 0xdf09822b, 0xbd691a6c, 0xaa12e4f2, 0x87451c0f, 0xe0f6a27a, 0x3ada4819, 0x4cf1764f, 0x0d771c2b, 0x67cdb156, 0x350d8384, 0x5938fa0f, 0x42399ef3, 0x36997b07, 0x0e84093d, 0x4aa93e61, 0x8360d87b, 0x1fa98b0c, 0x1149382c, 0xe97625a5, 0x0614d1b7, 0x0e25244b, 0x0c768347, 0x589e8d82, 0x0d2059d1, 0xa466bb1e, 0xf8da0a82, 0x04f19130, 0xba6e4ec0, 0x99265164, 0x1ee7230d, 0x50b2ad80, 0xeaee6801, 0x8db2a283, 0xea8bf59e }; /* Initialize a key schedule from a 128-bit key */ static void cast_key_sched(sched, key) CastKeySched * sched; uint8p key; { uint8p x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, xA, xB, xC, xD, xE, xF; uint8p z0, z1, z2, z3, z4, z5, z6, z7, z8, z9, zA, zB, zC, zD, zE, zF; uint32 X03, X47, X8B, XCF, Z03, Z47, Z8B, ZCF; #ifdef LITTLE_ENDIAN x3 = (uint8p) &X03; x2 = x3 + 1; x1 = x2 + 1; x0 = x1 + 1; x7 = (uint8p) &X47; x6 = x7 + 1; x5 = x6 + 1; x4 = x5 + 1; xB = (uint8p) &X8B; xA = xB + 1; x9 = xA + 1; x8 = x9 + 1; xF = (uint8p) &XCF; xE = xF + 1; xD = xE + 1; xC = xD + 1; z3 = (uint8p) &Z03; z2 = z3 + 1; z1 = z2 + 1; z0 = z1 + 1; z7 = (uint8p) &Z47; z6 = z7 + 1; z5 = z6 + 1; z4 = z5 + 1; zB = (uint8p) &Z8B; zA = zB + 1; z9 = zA + 1; z8 = z9 + 1; zF = (uint8p) &ZCF; zE = zF + 1; zD = zE + 1; zC = zD + 1; #else x0 = (uint8p) &X03; x1 = x0 + 1; x2 = x1 + 1; x3 = x2 + 1; x4 = (uint8p) &X47; x5 = x4 + 1; x6 = x5 + 1; x7 = x6 + 1; x8 = (uint8p) &X8B; x9 = x8 + 1; xA = x9 + 1; xB = xA + 1; xC = (uint8p) &XCF; xD = xC + 1; xE = xD + 1; xF = xE + 1; z0 = (uint8p) &Z03; z1 = z0 + 1; z2 = z1 + 1; z3 = z2 + 1; z4 = (uint8p) &Z47; z5 = z4 + 1; z6 = z5 + 1; z7 = z6 + 1; z8 = (uint8p) &Z8B; z9 = z8 + 1; zA = z9 + 1; zB = zA + 1; zC = (uint8p) &ZCF; zD = zC + 1; zE = zD + 1; zF = zE + 1; #endif #ifdef LITTLE_ENDIAN *x0 = key[0]; *x1 = key[1]; *x2 = key[2]; *x3 = key[3]; *x4 = key[4]; *x5 = key[5]; *x6 = key[6]; *x7 = key[7]; *x8 = key[8]; *x9 = key[9]; *xA = key[10]; *xB = key[11]; *xC = key[12]; *xD = key[13]; *xE = key[14]; *xF = key[15]; #else X03 = *(uint32p) key; X47 = *(uint32p) (key + 4); X8B = *(uint32p) (key + 8); XCF = *(uint32p) (key + 12); #endif /* First half of key schedule */ Z03 = X03 ^ S5[*xD] ^ S6[*xF] ^ S7[*xC] ^ S8[*xE] ^ S7[*x8]; Z47 = X8B ^ S5[*z0] ^ S6[*z2] ^ S7[*z1] ^ S8[*z3] ^ S8[*xA]; Z8B = XCF ^ S5[*z7] ^ S6[*z6] ^ S7[*z5] ^ S8[*z4] ^ S5[*x9]; ZCF = X47 ^ S5[*zA] ^ S6[*z9] ^ S7[*zB] ^ S8[*z8] ^ S6[*xB]; sched->K[0].Km = S5[*z8] ^ S6[*z9] ^ S7[*z7] ^ S8[*z6] ^ S5[*z2]; sched->K[1].Km = S5[*zA] ^ S6[*zB] ^ S7[*z5] ^ S8[*z4] ^ S6[*z6]; sched->K[2].Km = S5[*zC] ^ S6[*zD] ^ S7[*z3] ^ S8[*z2] ^ S7[*z9]; sched->K[3].Km = S5[*zE] ^ S6[*zF] ^ S7[*z1] ^ S8[*z0] ^ S8[*zC]; X03 = Z8B ^ S5[*z5] ^ S6[*z7] ^ S7[*z4] ^ S8[*z6] ^ S7[*z0]; X47 = Z03 ^ S5[*x0] ^ S6[*x2] ^ S7[*x1] ^ S8[*x3] ^ S8[*z2]; X8B = Z47 ^ S5[*x7] ^ S6[*x6] ^ S7[*x5] ^ S8[*x4] ^ S5[*z1]; XCF = ZCF ^ S5[*xA] ^ S6[*x9] ^ S7[*xB] ^ S8[*x8] ^ S6[*z3]; sched->K[4].Km = S5[*x3] ^ S6[*x2] ^ S7[*xC] ^ S8[*xD] ^ S5[*x8]; sched->K[5].Km = S5[*x1] ^ S6[*x0] ^ S7[*xE] ^ S8[*xF] ^ S6[*xD]; sched->K[6].Km = S5[*x7] ^ S6[*x6] ^ S7[*x8] ^ S8[*x9] ^ S7[*x3]; sched->K[7].Km = S5[*x5] ^ S6[*x4] ^ S7[*xA] ^ S8[*xB] ^ S8[*x7]; Z03 = X03 ^ S5[*xD] ^ S6[*xF] ^ S7[*xC] ^ S8[*xE] ^ S7[*x8]; Z47 = X8B ^ S5[*z0] ^ S6[*z2] ^ S7[*z1] ^ S8[*z3] ^ S8[*xA]; Z8B = XCF ^ S5[*z7] ^ S6[*z6] ^ S7[*z5] ^ S8[*z4] ^ S5[*x9]; ZCF = X47 ^ S5[*zA] ^ S6[*z9] ^ S7[*zB] ^ S8[*z8] ^ S6[*xB]; sched->K[8].Km = S5[*z3] ^ S6[*z2] ^ S7[*zC] ^ S8[*zD] ^ S5[*z9]; sched->K[9].Km = S5[*z1] ^ S6[*z0] ^ S7[*zE] ^ S8[*zF] ^ S6[*zC]; sched->K[10].Km = S5[*z7] ^ S6[*z6] ^ S7[*z8] ^ S8[*z9] ^ S7[*z2]; sched->K[11].Km = S5[*z5] ^ S6[*z4] ^ S7[*zA] ^ S8[*zB] ^ S8[*z6]; X03 = Z8B ^ S5[*z5] ^ S6[*z7] ^ S7[*z4] ^ S8[*z6] ^ S7[*z0]; X47 = Z03 ^ S5[*x0] ^ S6[*x2] ^ S7[*x1] ^ S8[*x3] ^ S8[*z2]; X8B = Z47 ^ S5[*x7] ^ S6[*x6] ^ S7[*x5] ^ S8[*x4] ^ S5[*z1]; XCF = ZCF ^ S5[*xA] ^ S6[*x9] ^ S7[*xB] ^ S8[*x8] ^ S6[*z3]; sched->K[12].Km = S5[*x8] ^ S6[*x9] ^ S7[*x7] ^ S8[*x6] ^ S5[*x3]; sched->K[13].Km = S5[*xA] ^ S6[*xB] ^ S7[*x5] ^ S8[*x4] ^ S6[*x7]; sched->K[14].Km = S5[*xC] ^ S6[*xD] ^ S7[*x3] ^ S8[*x2] ^ S7[*x8]; sched->K[15].Km = S5[*xE] ^ S6[*xF] ^ S7[*x1] ^ S8[*x0] ^ S8[*xD]; /* Second half of key schedule - just like first half */ Z03 = X03 ^ S5[*xD] ^ S6[*xF] ^ S7[*xC] ^ S8[*xE] ^ S7[*x8]; Z47 = X8B ^ S5[*z0] ^ S6[*z2] ^ S7[*z1] ^ S8[*z3] ^ S8[*xA]; Z8B = XCF ^ S5[*z7] ^ S6[*z6] ^ S7[*z5] ^ S8[*z4] ^ S5[*x9]; ZCF = X47 ^ S5[*zA] ^ S6[*z9] ^ S7[*zB] ^ S8[*z8] ^ S6[*xB]; sched->K[0].Kr = (S5[*z8] ^ S6[*z9] ^ S7[*z7] ^ S8[*z6] ^ S5[*z2]) & 0x1f; sched->K[1].Kr = (S5[*zA] ^ S6[*zB] ^ S7[*z5] ^ S8[*z4] ^ S6[*z6]) & 0x1f; sched->K[2].Kr = (S5[*zC] ^ S6[*zD] ^ S7[*z3] ^ S8[*z2] ^ S7[*z9]) & 0x1f; sched->K[3].Kr = (S5[*zE] ^ S6[*zF] ^ S7[*z1] ^ S8[*z0] ^ S8[*zC]) & 0x1f; X03 = Z8B ^ S5[*z5] ^ S6[*z7] ^ S7[*z4] ^ S8[*z6] ^ S7[*z0]; X47 = Z03 ^ S5[*x0] ^ S6[*x2] ^ S7[*x1] ^ S8[*x3] ^ S8[*z2]; X8B = Z47 ^ S5[*x7] ^ S6[*x6] ^ S7[*x5] ^ S8[*x4] ^ S5[*z1]; XCF = ZCF ^ S5[*xA] ^ S6[*x9] ^ S7[*xB] ^ S8[*x8] ^ S6[*z3]; sched->K[4].Kr = (S5[*x3] ^ S6[*x2] ^ S7[*xC] ^ S8[*xD] ^ S5[*x8]) & 0x1f; sched->K[5].Kr = (S5[*x1] ^ S6[*x0] ^ S7[*xE] ^ S8[*xF] ^ S6[*xD]) & 0x1f; sched->K[6].Kr = (S5[*x7] ^ S6[*x6] ^ S7[*x8] ^ S8[*x9] ^ S7[*x3]) & 0x1f; sched->K[7].Kr = (S5[*x5] ^ S6[*x4] ^ S7[*xA] ^ S8[*xB] ^ S8[*x7]) & 0x1f; Z03 = X03 ^ S5[*xD] ^ S6[*xF] ^ S7[*xC] ^ S8[*xE] ^ S7[*x8]; Z47 = X8B ^ S5[*z0] ^ S6[*z2] ^ S7[*z1] ^ S8[*z3] ^ S8[*xA]; Z8B = XCF ^ S5[*z7] ^ S6[*z6] ^ S7[*z5] ^ S8[*z4] ^ S5[*x9]; ZCF = X47 ^ S5[*zA] ^ S6[*z9] ^ S7[*zB] ^ S8[*z8] ^ S6[*xB]; sched->K[8].Kr = (S5[*z3] ^ S6[*z2] ^ S7[*zC] ^ S8[*zD] ^ S5[*z9]) & 0x1f; sched->K[9].Kr = (S5[*z1] ^ S6[*z0] ^ S7[*zE] ^ S8[*zF] ^ S6[*zC]) & 0x1f; sched->K[10].Kr = (S5[*z7] ^ S6[*z6] ^ S7[*z8] ^ S8[*z9] ^ S7[*z2]) & 0x1f; sched->K[11].Kr = (S5[*z5] ^ S6[*z4] ^ S7[*zA] ^ S8[*zB] ^ S8[*z6]) & 0x1f; X03 = Z8B ^ S5[*z5] ^ S6[*z7] ^ S7[*z4] ^ S8[*z6] ^ S7[*z0]; X47 = Z03 ^ S5[*x0] ^ S6[*x2] ^ S7[*x1] ^ S8[*x3] ^ S8[*z2]; X8B = Z47 ^ S5[*x7] ^ S6[*x6] ^ S7[*x5] ^ S8[*x4] ^ S5[*z1]; XCF = ZCF ^ S5[*xA] ^ S6[*x9] ^ S7[*xB] ^ S8[*x8] ^ S6[*z3]; sched->K[12].Kr = (S5[*x8] ^ S6[*x9] ^ S7[*x7] ^ S8[*x6] ^ S5[*x3]) & 0x1f; sched->K[13].Kr = (S5[*xA] ^ S6[*xB] ^ S7[*x5] ^ S8[*x4] ^ S6[*x7]) & 0x1f; sched->K[14].Kr = (S5[*xC] ^ S6[*xD] ^ S7[*x3] ^ S8[*x2] ^ S7[*x8]) & 0x1f; sched->K[15].Kr = (S5[*xE] ^ S6[*xF] ^ S7[*x1] ^ S8[*x0] ^ S8[*xD]) & 0x1f; } /* Initialize with a full-strength 128-bit key */ #ifndef CAST_EXPORT_ENCRYPTION void ck_cast128_key_sched(sched, key) CastKeySched * sched; uint8 * key; { sched->ksize = 16; cast_key_sched(sched, key); } #endif /* Handle reduced-keysize variants */ static void cast5_key_sched(sched, key, sz) CastKeySched * sched; uint8 * key; int sz; { uint8 buf[16]; sched->ksize = sz; memset(buf, 0, sizeof(buf)); memcpy(buf, key, sz); cast_key_sched(sched, buf); } /* 40, 64, and 80-bit keys - all use 12 rounds */ void ck_cast5_40_key_sched(sched, key) CastKeySched * sched; uint8 * key; { cast5_key_sched(sched, key, 5); } #ifndef CAST_EXPORT_ENCRYPTION void ck_cast5_64_key_sched(sched, key) CastKeySched * sched; uint8 * key; { cast5_key_sched(sched, key, 8); } void ck_cast5_80_key_sched(sched, key) CastKeySched * sched; uint8 * key; { cast5_key_sched(sched, key, 10); } #endif /* CAST_EXPORT_ENCRYPTION */ #endif /* CK_CAST */ #ifdef CRYPT_DLL static char * ck_crypt_dll_version() { return(ckcrpv); } int crypt_dll_init( struct _crypt_dll_init * init ) { #ifdef LIBDES extern int des_check_key; extern void libdes_dll_init(struct _crypt_dll_init *); des_check_key = 1; #endif /* LIBDES */ if ( init->version >= 1 ) { p_ttol = init->p_ttol; p_dodebug = init->p_dodebug; p_dohexdump = init->p_dohexdump; p_tn_debug = init->p_tn_debug; p_vscrnprintf = init->p_vscrnprintf; if ( init->version == 1 ) return(1); } if ( init->version >= 2 ) { /* This is a k5_context but we don't want to include krb5.h */ p_k5_context = (void *) init->p_k5_context; if ( init->version == 2 ) return(1); } if ( init->version >= 3 ) { init->p_install_funcs("encrypt_parse",encrypt_parse); init->p_install_funcs("encrypt_init",encrypt_init); init->p_install_funcs("encrypt_session_key",encrypt_session_key); init->p_install_funcs("encrypt_send_request_start", encrypt_send_request_start ); init->p_install_funcs("encrypt_request_start",encrypt_request_start); init->p_install_funcs("encrypt_send_request_end", encrypt_send_request_end ); init->p_install_funcs("encrypt_request_end",encrypt_request_end); init->p_install_funcs("encrypt_send_end",encrypt_send_end); init->p_install_funcs("encrypt_send_support",encrypt_send_support); init->p_install_funcs("encrypt_is_encrypting",encrypt_is_encrypting); init->p_install_funcs("encrypt_is_decrypting",encrypt_is_decrypting); init->p_install_funcs("get_crypt_table",get_crypt_table); init->p_install_funcs("des_is_weak_key",ck_des_is_weak_key); libdes_dll_init(init); if (init->version == 3) return(1); } if ( init->version >= 4 ) { init->p_install_funcs("crypt_dll_version",ck_crypt_dll_version); if (init->version == 4) return(1); } if ( init->version >= 5 ) { p_reqtelmutex = init->p_reqtelmutex; p_reltelmutex = init->p_reltelmutex; if (init->version == 5) return(1); } if ( init->version >= 6 ) { init->p_install_funcs("encrypt_dont_support",encrypt_dont_support); if ( init->version == 6 ) return(1); /* when adding new versions; migrate the next two lines */ init->version = 6; return(1); } return(0); } #undef malloc #undef realloc #undef free #undef strdup static void fatal(char *msg) { if (!msg) msg = ""; printf(msg); exit(1); /* Exit indicating failure */ } void * kmalloc(size_t size) { void *ptr; if (size == 0) { fatal("kmalloc: zero size"); } ptr = malloc(size); if (ptr == NULL) { fatal("kmalloc: out of memory"); } return ptr; } void * krealloc(void *ptr, size_t new_size) { void *new_ptr; if (new_size == 0) { fatal("krealloc: zero size"); } if (ptr == NULL) new_ptr = malloc(new_size); else new_ptr = realloc(ptr, new_size); if (new_ptr == NULL) { fatal("krealloc: out of memory"); } return new_ptr; } void kfree(void *ptr) { if (ptr == NULL) { printf("kfree: NULL pointer given as argument"); return; } free(ptr); } char * kstrdup(const char *str) { size_t len; char *cp; if (str == NULL) { fatal("kstrdup: NULL pointer given as argument"); } len = strlen(str) + 1; cp = kmalloc(len); if (cp) memcpy(cp, str, len); return cp; } #endif /* CRYPT_DLL */ #endif /* CK_ENCRYPTION */ ck_des.c0000644000015300001460000000511410025114441011246 0ustar fdckermit/* C K _ D E S . C - libDES interface for Kermit 95" Copyright (C) 1998, 2001, Trustees of Columbia University in the City of New York. The C-Kermit software may not be, in whole or in part, licensed or sold for profit as a software product itself, nor may it be included in or distributed with commercial products or otherwise distributed by commercial concerns to their clients or customers without written permission of the Office of Kermit Development and Distribution, Columbia University. This copyright notice must not be removed, altered, or obscured. Author: Jeffrey E Altman (jaltman@secure-endpoints.com) */ /* This file contains wrappers so that the following functions will be imported into the k95crypt.dll/k2crypt.dll files in such a form that they can be re-exported to k95.exe/k2.exe. This subset of the DES library is needed to provide DES based Kerberos authentication. */ #ifdef LIBDES /* The following is specific to my installation, but since I'm the only one */ /* that uses this file ... */ #include "ckcdeb.h" #include "ckuath.h" #define CK_DES_C #include "ckuat2.h" #ifdef NT #ifdef _M_ALPHA #include #else #include #endif #else #include #endif int libdes_random_key(des_cblock B) { des_random_key(B); return(0); } void libdes_random_seed(des_cblock B) { des_random_seed(B); } void libdes_key_sched(des_cblock * B, des_key_schedule S) { des_key_sched(B,S); } void libdes_ecb_encrypt(des_cblock * B1, des_cblock * B2, des_key_schedule S, int n) { des_ecb_encrypt(B1,B2,S,n); } int libdes_string_to_key(char * s, des_cblock * B) { des_string_to_key(s,B); return(0); } void libdes_fixup_key_parity(des_cblock * B) { des_set_odd_parity(B); } void libdes_pcbc_encrypt(des_cblock *input, des_cblock *output, long length, des_key_schedule schedule, des_cblock *ivec, int enc) { des_pcbc_encrypt(input,output,length,schedule,ivec,enc); } void libdes_dll_init(struct _crypt_dll_init * init) { init->p_install_funcs("libdes_random_key",libdes_random_key); init->p_install_funcs("libdes_random_seed",libdes_random_seed); init->p_install_funcs("libdes_key_sched",libdes_key_sched); init->p_install_funcs("libdes_ecb_encrypt",libdes_ecb_encrypt); init->p_install_funcs("libdes_string_to_key",libdes_string_to_key); init->p_install_funcs("libdes_fixup_key_parity",libdes_fixup_key_parity); init->p_install_funcs("libdes_pcbc_encrypt",libdes_pcbc_encrypt); } #endif /* LIBDES */ ck_ssl.c0000644000015300001460000044015311605056444011317 0ustar fdckermitchar *cksslv = "SSL/TLS support, 9.0.227, 04 Aug 2010"; /* C K _ S S L . C -- OpenSSL Interface for C-Kermit Copyright (C) 1985, 2010, Trustees of Columbia University in the City of New York. All rights reserved. See the C-Kermit COPYING.TXT file or the copyright text in the ckcmai.c module for disclaimer and permissions. Author: Jeffrey E Altman (jaltman@secure-endpoints.com) Secure Endpoints Inc., New York City Provides: . Telnet Auth SSL option compatible with Tim Hudson's hack. . Telnet START_TLS option . Configuration of certificate and key files . Certificate verification and revocation list checks . Client certificate to user id routine Note: This code is written to be compatible with OpenSSL 0.9.6[abcdefgh] and 0.9.7 beta 5 (and, presumably, later). It will also compile with version 0.9.5 although that is discouraged due to security weaknesses in that release. */ #include "ckcsym.h" #include "ckcdeb.h" #ifdef CK_SSL #include "ckcnet.h" #include "ckuath.h" #include #include #ifdef UNIX #include #ifndef FREEBSD4 #include #endif /* FREEBSD4 */ #endif /* UNIX */ #ifdef DEC_TCPIP #include #include #endif /* DEC_TCPIP */ #ifdef OS2 extern char exedir[]; #ifdef NT char * GetAppData(int); #endif #endif /* OS2 */ extern int quiet; /* fdc - Mon Nov 28 11:44:15 2005 */ static int ssl_installed = 1; #endif /* CK_SSL */ int ck_ssh_is_installed() { #ifdef SSHBUILTIN #ifdef SSLDLL #ifdef NT extern HINSTANCE hCRYPTO; #else /* NT */ extern HMODULE hCRYPTO; #endif /* NT */ debug(F111,"ck_ssh_is_installed","hCRYPTO",hCRYPTO); return(ssl_installed && (hCRYPTO != NULL)); #else /* SSLDLL */ return(ssl_installed); #endif /* SSLDLL */ #else return 0; #endif } int #ifdef CK_ANSIC ck_ssleay_is_installed(void) #else ck_ssleay_is_installed() #endif { #ifdef CK_SSL #ifdef SSLDLL #ifdef NT extern HINSTANCE hSSL, hCRYPTO; #else /* NT */ extern HMODULE hSSL, hCRYPTO; #endif /* NT */ debug(F111,"ck_ssleay_is_installed","hSSL",hSSL); debug(F111,"ck_ssleay_is_installed","hCRYPTO",hCRYPTO); return(ssl_installed && (hSSL != NULL) && (hCRYPTO != NULL)); #else /* SSLDLL */ return(ssl_installed); #endif /* SSLDLL */ #else /* CK_SSL */ return(0); #endif /* CK_SSL */ } #ifdef CK_SSL #include "ckcker.h" #include "ckucmd.h" /* For struct keytab */ #include "ckctel.h" #include "ck_ssl.h" #ifdef UNIX #include /* Password file for home directory */ #endif /* UNIX */ #ifdef OS2 #include #endif /* OS2 */ #ifdef OS2ONLY #include "ckotcp.h" #endif /* OS2ONLY */ #ifdef SSLDLL int ssl_finished_messages = 0; #else /* SSLDLL */ #ifdef OPENSSL_VERSION_NUMBER int ssl_finished_messages = (OPENSSL_VERSION_NUMBER >= 0x0090581fL); #else !ERROR This module requires OpenSSL 0.9.5a or higher #endif /* OPENSSL_VERSION_NUMBER */ #endif /* SSLDLL */ static int auth_ssl_valid = 0; static char *auth_ssl_name = 0; /* this holds the oneline name */ char ssl_err[SSL_ERR_BFSZ]=""; BIO *bio_err=NULL; X509_STORE *crl_store = NULL; #ifndef NOFTP #ifndef SYSFTP SSL *ssl_ftp_con = NULL; SSL_CTX *ssl_ftp_ctx = NULL; SSL *ssl_ftp_data_con = NULL; int ssl_ftp_active_flag = 0; int ssl_ftp_data_active_flag = 0; #endif /* SYSFTP */ #endif /* NOFTP */ #ifndef NOHTTP SSL *tls_http_con = NULL; SSL_CTX *tls_http_ctx = NULL; int tls_http_active_flag = 0; int ssl_http_initialized = 0; #endif /* NOHTTP */ SSL_CTX *ssl_ctx = NULL; SSL *ssl_con = NULL; int ssl_debug_flag = 0; int ssl_verbose_flag = 0; int ssl_only_flag = 0; int ssl_raw_flag = 0; int ssl_active_flag = 0; int ssl_verify_flag = SSL_VERIFY_PEER; int ssl_certsok_flag = 0; char *ssl_rsa_cert_file = NULL; char *ssl_rsa_cert_chain_file = NULL; char *ssl_rsa_key_file = NULL; char *ssl_dsa_cert_file = NULL; char *ssl_dsa_cert_chain_file = NULL; char *ssl_dh_key_file = NULL; char *ssl_crl_file = NULL; char *ssl_crl_dir = NULL; char *ssl_verify_file = NULL; char *ssl_verify_dir = NULL; char *ssl_dh_param_file = NULL; char *ssl_cipher_list = NULL; char *ssl_rnd_file = NULL; SSL_CTX *tls_ctx = NULL; SSL *tls_con = NULL; int tls_only_flag = 0; int tls_raw_flag = 0; int tls_active_flag = 0; int ssl_initialized = 0; int ssl_verify_depth = -1; /* used to track depth in verify routines */ /* compile this set to 1 to negotiate SSL/TLS but not actually start it */ int ssl_dummy_flag=0; extern int inserver; extern int debses; extern int accept_complete; extern char szHostName[], szUserNameRequested[], szUserNameAuthenticated[]; _PROTOTYP(int X509_to_user,(X509 *, char *, int)); static int verbosity = 0; /* Message control */ static VOID setverbosity() { verbosity = ssl_verbose_flag; if (quiet) verbosity = 0; } int #ifdef CK_ANSIC ssl_server_verify_callback(int ok, X509_STORE_CTX * ctx) #else /* CK_ANSIC */ ssl_server_verify_callback(ok, ctx) int ok; X509_STORE_CTX *ctx; #endif /* CK_ANSIC */ { static char *saved_subject=NULL; char *subject=NULL, *issuer=NULL; int depth,error; X509 *xs = NULL; if ( ssl_certsok_flag ) return(1); setverbosity(); error=X509_STORE_CTX_get_error(ctx); depth=X509_STORE_CTX_get_error_depth(ctx); xs=X509_STORE_CTX_get_current_cert(ctx); if (depth==0) { /* clear things */ if (saved_subject!=NULL) { free(saved_subject); saved_subject=NULL; } if (auth_ssl_name!=NULL) { free(auth_ssl_name); auth_ssl_name=NULL; } } if (ssl_debug_flag && !inserver) { printf("ssl:server_verify_callback:depth=%d ok=%d err=%d-%s\r\n", depth,ok,error,X509_verify_cert_error_string(error)); } /* first thing is to have a meaningful name for the current * certificate that is being verified ... and if we cannot * determine that then something is seriously wrong! */ makestr(&subject, (char *)X509_NAME_oneline(X509_get_subject_name(xs),NULL,0)); makestr(&issuer, (char *)X509_NAME_oneline(X509_get_issuer_name(xs),NULL,0)); if (!subject || !subject[0] || !issuer || !issuer[0]) { ok = 0; goto return_time; } if (verbosity && !inserver && depth != ssl_verify_depth) { printf("[%d] Certificate Subject:\r\n%s\r\n",depth,subject); printf("[%d] Certificate Issuer:\r\n%s\r\n",depth,issuer); ssl_verify_depth = depth; } /* make sure that the certificate that has been presented */ /* has not been revoked (if we have been given a CRL. */ ok = ssl_verify_crl(ok, ctx); /* if we have any form of error in secure mode we reject the connection */ if (error!=X509_V_OK) { if (inserver) { #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_LI && ckxlogging) { cksyslog(SYSLG_LI, 0, "X.509 Certificate verify failure", (char *) subject, (char *)X509_verify_cert_error_string(error) ); } #endif /* CKSYSLOG */ } else { if ( ssl_verify_flag & (SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT)) printf("Error: "); else printf("Warning: "); switch (error) { case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: printf("Certificate is self signed.\r\n"); break; case X509_V_ERR_CERT_HAS_EXPIRED: printf("Certificate has expired.\r\n"); break; case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: printf( "Certificate issuer's certificate isn't available locally.\r\n"); break; case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE: printf("Unable to verify leaf signature.\r\n"); break; case X509_V_ERR_CERT_REVOKED: printf("Certificate revoked.\r\n"); break; default: printf("Error %d while verifying certificate.\r\n", ctx->error); break; } } ok = !(ssl_verify_flag & SSL_VERIFY_FAIL_IF_NO_PEER_CERT); } else { /* if we got all the way to the top of the tree then * we *can* use this certificate for a username to * match ... in all other cases we must not! */ auth_ssl_name = saved_subject; saved_subject = NULL; } return_time: /* save the name if at least the first level is okay */ if (depth == 0 && ok) makestr(&saved_subject,subject); /* clean up things */ if (subject!=NULL) free(subject); if (issuer!=NULL) free(issuer); return ok; } int #ifdef CK_ANSIC ssl_client_verify_callback(int ok, X509_STORE_CTX * ctx) #else ssl_client_verify_callback(ok, ctx) int ok; X509_STORE_CTX *ctx; #endif { char subject[256]="", issuer[256]=""; int depth, error, len; X509 *xs; setverbosity(); xs=X509_STORE_CTX_get_current_cert(ctx); error=X509_STORE_CTX_get_error(ctx); depth=X509_STORE_CTX_get_error_depth(ctx); if ( ssl_debug_flag ) printf("ssl:client_verify_callback:depth=%d ok=%d err=%d-%s\r\n", depth,ok,error,X509_verify_cert_error_string(error)); if ( ssl_certsok_flag ) { ok = 1; } /* first thing is to have a meaningful name for the current * certificate that is being verified ... and if we cannot * determine that then something is seriously wrong! */ #ifdef XN_FLAG_SEP_MULTILINE X509_NAME_print_ex(bio_err,X509_get_subject_name(xs),4, XN_FLAG_SEP_MULTILINE); len = BIO_read(bio_err,subject,256); subject[len < 256 ? len : 255] = '\0'; if (!subject[0]) { ERR_print_errors(bio_err); len = BIO_read(bio_err,ssl_err,SSL_ERR_BFSZ); ssl_err[len < SSL_ERR_BFSZ ? len : SSL_ERR_BFSZ] = '\0'; uq_ok("X.509 Subject Name unavailable", ssl_err, 1, NULL, 0); ok=0; goto return_time; } X509_NAME_print_ex(bio_err,X509_get_issuer_name(xs),4, XN_FLAG_SEP_MULTILINE); len = BIO_read(bio_err,issuer,256); issuer[len < 256 ? len : 255] = '\0'; if (!issuer[0]) { ERR_print_errors(bio_err); len = BIO_read(bio_err,ssl_err,SSL_ERR_BFSZ); ssl_err[len < SSL_ERR_BFSZ ? len : SSL_ERR_BFSZ] = '\0'; uq_ok("X.509 Issuer Name unavailable", ssl_err, 1, NULL, 0); ok=0; goto return_time; } #else /* XN_FLAG_SEP_MULTILINE */ X509_NAME_oneline(X509_get_subject_name(xs),subject,256); if (!subject[0]) { int len; ERR_print_errors(bio_err); len = BIO_read(bio_err,ssl_err,SSL_ERR_BFSZ); ssl_err[len < SSL_ERR_BFSZ ? len : SSL_ERR_BFSZ] = '\0'; uq_ok("X.509 Subject Name unavailable", ssl_err, 1, NULL, 0); ok=0; goto return_time; } X509_NAME_oneline(X509_get_issuer_name(xs),issuer,256); if (!issuer[0]) { int len; ERR_print_errors(bio_err); len = BIO_read(bio_err,ssl_err,SSL_ERR_BFSZ); ssl_err[len < SSL_ERR_BFSZ ? len : SSL_ERR_BFSZ] = '\0'; uq_ok("X.509 Issuer Name unavailable", ssl_err, 1, NULL, 0); ok=0; goto return_time; } #endif /* XN_FLAG_SEP_MULTILINE */ if (verbosity && depth != ssl_verify_depth) { printf("[%d] Certificate Subject:\r\n%s\r\n",depth,subject); printf("[%d] Certificate Issuer:\r\n%s\r\n",depth,issuer); ssl_verify_depth = depth; } ok = ssl_verify_crl(ok, ctx); if ( !ok ) { char prefix[1024]; /* if the server is using a self signed certificate then * we need to decide if that is good enough for us to * accept ... */ switch ( error ) { case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: { if (ssl_verify_flag & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) { /* make 100% sure that in secure more we drop the * connection if the server does not have a * real certificate! */ ckmakxmsg(prefix,1024, "Error: Server has a self-signed certificate\n", "[",ckitoa(depth),"] Certificate Subject=\n",subject, "\n[",ckitoa(depth),"] Certificate Issuer=\n",issuer, NULL,NULL,NULL); uq_ok(prefix, "Rejecting Connection", 1, NULL, 0); /* sometimes it is really handy to be able to debug things * and still get a connection! */ if (ssl_debug_flag) { printf("SSL: debug -> ignoring cert required!\r\n"); ok=1; } else { ok=0; } goto return_time; } else if (ssl_verify_flag != SSL_VERIFY_NONE) { ckmakxmsg(prefix,1024, "Warning: Server has a self-signed certificate\n", "[",ckitoa(depth),"] Certificate Subject=\n",subject, "\n[",ckitoa(depth),"] Certificate Issuer=\n",issuer, NULL,NULL,NULL); ok = uq_ok(prefix, "Continue? (Y/N) ", 3, NULL, 0); if ( ok < 0 ) ok = 0; goto return_time; } } case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: if (ssl_verify_flag & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) { /* make 100% sure that in secure more we drop the * connection if the server does not have a * real certificate! */ ckmakxmsg(prefix,1024, "Error: ", (char *)X509_verify_cert_error_string(error), "\nCertificate Issuer=\n",issuer, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL); uq_ok(prefix, "Rejecting Connection", 1, NULL, 0); /* sometimes it is really handy to be able to debug things * and still get a connection! */ if (ssl_debug_flag) { printf("SSL: debug -> ignoring cert required!\r\n"); ok=1; } else { ok=0; } goto return_time; } else if (ssl_verify_flag != SSL_VERIFY_NONE) { ckmakxmsg(prefix,1024, "Warning: ", (char *)X509_verify_cert_error_string(error), "\nCertificate Issuer=\n",issuer, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL); ok = uq_ok(prefix, "Continue (Y/N)", 3, NULL, 0); goto return_time; } break; case X509_V_ERR_CERT_NOT_YET_VALID: case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: if (ssl_verify_flag & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) { int len; /* make 100% sure that in secure more we drop the * connection if the server does not have a * real certificate! */ ASN1_TIME_print(bio_err,X509_get_notBefore(xs)); len = BIO_read(bio_err,ssl_err,SSL_ERR_BFSZ); ssl_err[len < SSL_ERR_BFSZ ? len : SSL_ERR_BFSZ] = '\0'; ckmakxmsg(prefix,1024, "Error: ", (char *)X509_verify_cert_error_string(error), "\nCertificate Subject=\n",subject, "\nnotBefore=",ssl_err, NULL,NULL,NULL,NULL,NULL,NULL); uq_ok(prefix, "Rejecting Connection", 1, NULL, 0); /* sometimes it is really handy to be able to debug things * and still get a connection! */ if (ssl_debug_flag) { printf("SSL: debug -> ignoring cert required!\r\n"); ok=1; } else { ok=0; } goto return_time; } else if (ssl_verify_flag != SSL_VERIFY_NONE) { int len; ASN1_TIME_print(bio_err,X509_get_notBefore(xs)); len = BIO_read(bio_err,ssl_err,SSL_ERR_BFSZ); ssl_err[len < SSL_ERR_BFSZ ? len : SSL_ERR_BFSZ] = '\0'; ckmakxmsg(prefix,1024, "Warning: ", (char *)X509_verify_cert_error_string(error), "\nCertificate Subject=\n",subject, "\n notBefore=",ssl_err, NULL,NULL,NULL,NULL,NULL,NULL); ok = uq_ok(prefix, "Continue (Y/N)", 3, NULL, 0); } break; case X509_V_ERR_CERT_HAS_EXPIRED: case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: if (ssl_verify_flag & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) { int len; /* make 100% sure that in secure more we drop the * connection if the server does not have a * real certificate! */ ASN1_TIME_print(bio_err,X509_get_notAfter(xs)); len = BIO_read(bio_err,ssl_err,SSL_ERR_BFSZ); ssl_err[len < SSL_ERR_BFSZ ? len : SSL_ERR_BFSZ] = '\0'; ckmakxmsg(prefix,1024, "Error: ", (char *)X509_verify_cert_error_string(error), "\nCertificate Subject=\n",subject, "\n notAfter=",ssl_err, NULL,NULL,NULL,NULL,NULL,NULL); uq_ok(prefix, "Rejecting Connection", 1, NULL, 0); /* sometimes it is really handy to be able to debug things * and still get a connection! */ if (ssl_debug_flag) { printf("SSL: debug -> ignoring cert required!\r\n"); ok=1; } else { ok=0; } goto return_time; } else if (ssl_verify_flag != SSL_VERIFY_NONE) { int len; ASN1_TIME_print(bio_err,X509_get_notAfter(xs)); len = BIO_read(bio_err,ssl_err,SSL_ERR_BFSZ); ssl_err[len < SSL_ERR_BFSZ ? len : SSL_ERR_BFSZ] = '\0'; ckmakxmsg(prefix,1024, "Warning: ", (char *)X509_verify_cert_error_string(error), "\nCertificate Subject=\n",subject, "\n notAfter=",ssl_err, NULL,NULL,NULL,NULL,NULL,NULL); ok = uq_ok(prefix, "Continue (Y/N)", 3, NULL, 0); } break; case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: /* * When an SSL server sends its certificates to the client there * are two" conventions": one is to send the complete certificate * chain and the other is to send the whole chain apart from the * root. * * You don't usually need the root because the root is normally * stored and trusted locally. * * So if you get the whole chain it will complain about the self * signed certificate whereas if the root is missing it says it * can't find the issuer certificate. */ if (ssl_verify_flag & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) { /* make 100% sure that in secure more we drop the * connection if the server does not have a * real certificate! */ ckmakxmsg(prefix,1024, "Error: ", (char *)X509_verify_cert_error_string(error), "\nCertificate Issuer=\n",issuer, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL); uq_ok(prefix, "Rejecting Connection", 1, NULL, 0); /* sometimes it is really handy to be able to debug things * and still get a connection! */ if (ssl_debug_flag) { printf("SSL: debug -> ignoring cert required!\r\n"); ok=1; } else { ok=0; } goto return_time; } else if (ssl_verify_flag != SSL_VERIFY_NONE) { ckmakxmsg(prefix,1024, "Warning: ", (char *)X509_verify_cert_error_string(error), "\nCertificate Issuer=\n",issuer, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL); ok = uq_ok(prefix, "Continue (Y/N)", 3, NULL, 0); #ifdef NT if (ok) { /* if the user decides to accept the certificate * offer to store it for future connections in * the user's private store */ ok = uq_ok( "Do you wish to store the certificate to verify future connections?", "Continue (Y/N)", 3, NULL, 0); if (ok) ck_X509_save_cert_to_user_store(xs); } #endif /* NT */ } break; case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE: case X509_V_ERR_UNABLE_TO_GET_CRL: case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE: case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE: case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY: case X509_V_ERR_CERT_SIGNATURE_FAILURE: case X509_V_ERR_CRL_SIGNATURE_FAILURE: case X509_V_ERR_CRL_NOT_YET_VALID: case X509_V_ERR_CRL_HAS_EXPIRED: case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD: case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD: case X509_V_ERR_OUT_OF_MEM: case X509_V_ERR_CERT_CHAIN_TOO_LONG: case X509_V_ERR_CERT_REVOKED: case X509_V_ERR_APPLICATION_VERIFICATION: default: if (ssl_verify_flag & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) { /* make 100% sure that in secure mode we drop the * connection if the server does not have a * real certificate! */ ckmakxmsg(prefix,1024, "Error: ", (char *)X509_verify_cert_error_string(error), "\nCertificate Subject=\n",subject, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL); uq_ok(prefix, "Rejecting Connection", 1, NULL, 0); /* sometimes it is really handy to be able to debug things * and still get a connection! */ if (ssl_debug_flag) { printf("SSL: debug -> ignoring cert required!\r\n"); ok=1; } else { ok=0; } goto return_time; } else if (ssl_verify_flag != SSL_VERIFY_NONE) { ckmakxmsg(prefix,1024, "Warning: ", (char *)X509_verify_cert_error_string(error), "\nCertificate Subject=\n",subject, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL); ok = uq_ok(prefix, "Continue (Y/N)", 3, NULL, 0); } break; } } return_time: if ( ssl_debug_flag ) printf("ssl:client_verify_callback => ok: %d\r\n",ok); return ok; } VOID #ifdef CK_ANSIC ssl_client_info_callback(const SSL *s, int where, int ret) #else ssl_client_info_callback(s,where,ret) const SSL *s; int where; int ret; #endif /* CK_ANSIC */ { if (inserver || !ssl_debug_flag) return; setverbosity(); switch ( where ) { case SSL_CB_CONNECT_LOOP: printf("SSL_connect:%s %s\r\n", SSL_state_string((SSL *)s),SSL_state_string_long((SSL *)s)); break; case SSL_CB_CONNECT_EXIT: if (ret == 0) { printf("SSL_connect:failed in %s %s\r\n", SSL_state_string((SSL *)s),SSL_state_string_long((SSL *)s)); } else if (ret < 0) { printf("SSL_connect:error in %s %s\r\n", SSL_state_string((SSL *)s),SSL_state_string_long((SSL *)s)); } break; case SSL_CB_ACCEPT_LOOP: printf("SSL_accept:%s %s\r\n", SSL_state_string((SSL *)s),SSL_state_string_long((SSL *)s)); break; case SSL_CB_ACCEPT_EXIT: if (ret == 0) { printf("SSL_accept:failed in %s %s\r\n", SSL_state_string((SSL *)s),SSL_state_string_long((SSL *)s)); } else if (ret < 0) { printf("SSL_accept:error in %s %s\r\n", SSL_state_string((SSL *)s),SSL_state_string_long((SSL *)s)); } break; case SSL_CB_READ_ALERT: printf("SSL_read_alert\r\n"); break; case SSL_CB_WRITE_ALERT: printf("SSL_write_alert\r\n"); break; case SSL_CB_HANDSHAKE_START: printf("SSL_handshake:%s %s\r\n", SSL_state_string((SSL *)s),SSL_state_string_long((SSL *)s)); break; case SSL_CB_HANDSHAKE_DONE: printf("SSL_handshake:%s %s\r\n", SSL_state_string((SSL *)s),SSL_state_string_long((SSL *)s)); break; } } #ifdef USE_CERT_CB /* Return 1, client cert is available */ /* Return 0, no client cert is available */ /* Return -1, callback must be called again. SSL_want_x509_lookup() == 1 */ int #ifdef CK_ANSIC ssl_client_cert_callback(SSL * s, X509 ** x509, EVP_PKEY ** pkey) #else /* CK_ANSIC */ ssl_client_cert_callback(s, x509, pkey) SSL * s; X509 ** x509; EVP_PKEY ** pkey; #endif /* CK_ANSIC */ { setverbosity(); if ( ssl_debug_flag ) { const char * cipher_list=SSL_get_cipher(s); printf("ssl_client_cert_callback called (%s)\r\n", cipher_list?cipher_list:"UNKNOWN"); } #ifdef COMMENT if ( s == tls_con ) { if (tls_load_certs(tls_cts,tls_con,0)) { *x509 = SSL_get_certificate(s); *pkey = SSL_get_privatekey(s); return(1); } } else if ( s == ssl_con ) { if (tls_load_certs(ssl_ctx,ssl_con,0)) { *x509 = SSL_get_certificate(s); *pkey = SSL_get_privatekey(s); return(1); } } return(0); #else /* COMMENT */ return(0); #endif /* COMMENT */ } #endif /* USE_CERT_CB */ #ifndef MS_CALLBACK #define MS_CALLBACK #endif /* MS_CALLBACK */ static RSA MS_CALLBACK * #ifdef CK_ANSIC tmp_rsa_cb(SSL * s, int export, int keylength) #else /* CK_ANSIC */ tmp_rsa_cb(s,export,keylength) SSL *s; int export; int keylength; #endif /* CK_ANSIC */ { static RSA *rsa_tmp=NULL; #ifndef NO_RSA if (rsa_tmp == NULL) { if (ssl_debug_flag) printf("Generating temporary (%d bit) RSA key...\r\n",keylength); rsa_tmp=RSA_generate_key(keylength,RSA_F4,NULL,NULL); if (ssl_debug_flag) printf("\r\n"); } #else /* NO_RSA */ if (ssl_debug_flag) printf("Unable to generate temporary RSA key...\r\n"); #endif return(rsa_tmp); } #ifndef NO_DH static unsigned char dh512_p[]={ 0xE9,0x4E,0x3A,0x64,0xFA,0x65,0x5F,0xA6,0x44,0xC7,0xFC,0xF1, 0x16,0x8B,0x11,0x11,0x7A,0xF0,0xB2,0x49,0x80,0x56,0xA3,0xF8, 0x0F,0x7D,0x01,0x68,0x5D,0xF6,0x8A,0xEA,0x8C,0xDD,0x01,0xDC, 0x43,0x18,0xE0,0xC4,0x89,0x80,0xE6,0x2D,0x44,0x77,0x45,0xFD, 0xBA,0xFC,0x43,0x35,0x12,0xC0,0xED,0x32,0xD3,0x16,0xEF,0x51, 0x09,0x44,0xA2,0xDB, }; static unsigned char dh512_g[]={ 0x05, }; static unsigned char dh768_p[]={ 0x8B,0x2A,0x8C,0x6C,0x0F,0x87,0xC7,0x34,0xEE,0x2E,0xFB,0x60, 0x94,0xB3,0xBF,0x95,0xBA,0x84,0x74,0x86,0xEA,0xE0,0xA4,0x33, 0xE0,0x8F,0x7C,0x79,0x5C,0x62,0xE2,0x91,0xC5,0x6D,0x68,0xB9, 0x6C,0x5E,0x4E,0x94,0x0C,0x8E,0x56,0x8E,0xEB,0x98,0x7C,0x6E, 0x0E,0xF2,0xD5,0xAA,0x22,0x27,0x3F,0x0F,0xAF,0x10,0xB5,0x0B, 0x16,0xCC,0x05,0x27,0xBB,0x58,0x6D,0x61,0x4B,0x2B,0xAB,0xDC, 0x6A,0x15,0xBC,0x36,0x75,0x4D,0xEC,0xAB,0xFA,0xB6,0xE1,0xB1, 0x13,0x70,0xD8,0x77,0xCD,0x5E,0x51,0x77,0x81,0x0D,0x77,0x43, }; static unsigned char dh768_g[]={ 0x05, }; static unsigned char dh1024_p[]={ 0xA4,0x75,0xCF,0x35,0x00,0xAF,0x3C,0x17,0xCE,0xB0,0xD0,0x52, 0x43,0xA0,0x0E,0xFA,0xA2,0xC9,0xBE,0x0B,0x76,0x7A,0xD9,0x2E, 0xF4,0x97,0xAC,0x02,0x24,0x69,0xF6,0x36,0x4F,0xAB,0xCC,0x43, 0xC1,0x74,0xFF,0xA3,0xD4,0x04,0x0F,0x11,0x2B,0x6D,0x8C,0x47, 0xC9,0xCF,0x40,0x93,0x9B,0x7D,0x1E,0x52,0x85,0xB2,0x17,0x55, 0x9C,0xF2,0x41,0x02,0x2A,0x9D,0x5F,0x24,0x22,0xC6,0x04,0xC4, 0xAB,0x92,0x6D,0xC7,0xC8,0xF3,0x41,0x58,0x6C,0x86,0xFD,0xB8, 0x0F,0x2D,0xDD,0xBF,0xA8,0x40,0x0C,0x58,0xC8,0xF2,0x3F,0x18, 0xEF,0xF1,0x93,0x3E,0xBA,0x16,0x41,0xBE,0x32,0x6C,0xC5,0x63, 0xFF,0x8A,0x02,0x3D,0xAC,0xD5,0x5A,0x49,0x64,0x34,0x14,0x2E, 0xFB,0x2E,0xE7,0x39,0x1A,0x0F,0x3C,0x33, }; static unsigned char dh1024_g[]={ 0x05, }; static unsigned char dh1536_p[]={ 0xA3,0x2B,0x75,0x0E,0x7B,0x31,0x82,0xCA,0xF2,0xFC,0xF3,0x3D, 0xCE,0x5F,0xCD,0x5B,0x95,0xF6,0x2F,0xA4,0x5D,0x08,0x26,0xD2, 0x5F,0xC0,0x3F,0xC5,0xD8,0xA2,0xFE,0x83,0x26,0xBC,0xEB,0x7D, 0xF0,0x4E,0xD2,0xA6,0xBB,0x3C,0x88,0x63,0xCE,0x98,0xDE,0x08, 0xE2,0xE1,0xAF,0xE2,0x38,0xA8,0xFA,0x68,0x76,0x8D,0xBF,0xDF, 0xBB,0x30,0x15,0xFE,0xBD,0x22,0xCC,0x03,0x4E,0x5E,0x33,0xA3, 0x6D,0xD6,0x68,0x12,0x97,0x17,0x4B,0xB5,0x84,0x5F,0x5F,0xA3, 0x5C,0x2F,0xA4,0x10,0xC1,0xAD,0xBF,0xAC,0x30,0xCA,0x47,0x64, 0x63,0xFE,0xEE,0xEE,0xA1,0x64,0x73,0x70,0xAA,0xF9,0xFE,0xC6, 0xAD,0x5E,0xF6,0xF3,0x9C,0xDF,0x34,0x53,0x34,0x72,0xA6,0xA4, 0xBB,0x81,0x5A,0x43,0x41,0xFD,0x41,0x05,0x5B,0x77,0x7B,0x84, 0x03,0xFA,0x8A,0xFA,0xF7,0x8E,0x0F,0xCB,0x51,0xA2,0xB8,0x45, 0xFF,0x59,0x42,0xEF,0xCF,0xF6,0x25,0x37,0xE2,0x6D,0xFF,0x69, 0x11,0xF5,0x77,0x59,0x79,0x1C,0x5F,0x05,0xFC,0x7A,0x65,0x81, 0x03,0x4A,0x78,0xC6,0xE9,0x48,0x73,0xF6,0x10,0xBC,0x99,0x1C, 0xEE,0x44,0x2F,0x8B,0x70,0xCA,0xA8,0xB6,0x02,0x83,0x3E,0x0B, }; static unsigned char dh1536_g[]={ 0x05, }; static unsigned char dh2048_p[]={ 0xFA,0x4E,0xE4,0x3B,0xFA,0xC1,0x87,0xDD,0xE7,0xC6,0x8B,0xE6, 0x13,0x85,0xBC,0x9B,0x2B,0x8B,0x5B,0x46,0xBB,0x8B,0x86,0x6D, 0xD7,0xB6,0xD5,0x49,0xC5,0x54,0xF2,0x3E,0xD2,0x39,0x64,0x9B, 0x0E,0x33,0x39,0x8F,0xFA,0xFA,0xD9,0x78,0xED,0x34,0x82,0x29, 0x37,0x58,0x4D,0x5D,0x40,0xCB,0x69,0xE3,0x8A,0x9F,0x17,0x0C, 0x01,0x23,0x6B,0x05,0x01,0xAF,0x33,0xDE,0xDF,0x1A,0xBB,0x7B, 0x6A,0x9F,0xD8,0xED,0x8D,0x5E,0x44,0x19,0x5B,0xE0,0xB6,0x23, 0xF9,0x7A,0x96,0x6E,0x94,0x33,0x31,0x49,0xBA,0x84,0xD5,0x12, 0xD7,0x6D,0xDC,0x35,0x54,0x64,0xA3,0xD8,0x04,0x26,0xC5,0xAF, 0x7F,0xE3,0xFE,0x6F,0xBE,0xD5,0x17,0x72,0x4B,0xA6,0xD0,0xA7, 0x5F,0x18,0xF5,0xF0,0x2D,0x11,0x9A,0xF6,0xD5,0x3B,0x6C,0x61, 0x3C,0x6F,0x8E,0x09,0x4F,0x2C,0xE1,0x26,0x06,0x51,0xB3,0x19, 0x85,0x85,0x13,0xF9,0xC2,0x6E,0x80,0x28,0x9E,0x8A,0xA0,0x01, 0x46,0xD1,0x85,0x44,0x8C,0xE6,0xEE,0x7E,0x1E,0x17,0x3D,0xBA, 0x54,0xFF,0xE8,0x0E,0xDD,0x51,0xF3,0x74,0x7F,0x0D,0x0B,0xAB, 0xCA,0x84,0x8D,0x24,0x5D,0x56,0xD4,0x47,0x02,0xFC,0x93,0x9F, 0xAE,0x9B,0x5C,0xDB,0x63,0xEB,0x65,0x01,0x38,0xC2,0x7B,0x30, 0x1E,0x17,0x1C,0x75,0xF5,0x16,0x3B,0x4F,0x5F,0x41,0x32,0xB5, 0xFF,0x9E,0x61,0xFD,0xD2,0x62,0x6E,0xFD,0x8A,0x28,0x93,0x59, 0x2D,0x70,0x14,0x4D,0xE1,0x86,0xD5,0x90,0xB4,0xDF,0x72,0x71, 0xE0,0xB4,0xD0,0xD6,0x82,0x3A,0x4A,0x04,0x58,0x32,0x0B,0xD3, 0x51,0x13,0x32,0x63, }; static unsigned char dh2048_g[]={ 0x02, }; static DH * get_dh512() { DH *dh=NULL; if ((dh=DH_new()) == NULL) return(NULL); dh->p=BN_bin2bn(dh512_p,sizeof(dh512_p),NULL); dh->g=BN_bin2bn(dh512_g,sizeof(dh512_g),NULL); if ((dh->p == NULL) || (dh->g == NULL)) return(NULL); return(dh); } static DH * get_dh768() { DH *dh=NULL; if ((dh=DH_new()) == NULL) return(NULL); dh->p=BN_bin2bn(dh768_p,sizeof(dh768_p),NULL); dh->g=BN_bin2bn(dh768_g,sizeof(dh768_g),NULL); if ((dh->p == NULL) || (dh->g == NULL)) return(NULL); return(dh); } static DH * get_dh1024() { DH *dh=NULL; if ((dh=DH_new()) == NULL) return(NULL); dh->p=BN_bin2bn(dh1024_p,sizeof(dh1024_p),NULL); dh->g=BN_bin2bn(dh1024_g,sizeof(dh1024_g),NULL); if ((dh->p == NULL) || (dh->g == NULL)) return(NULL); return(dh); } static DH * get_dh1536() { DH *dh=NULL; if ((dh=DH_new()) == NULL) return(NULL); dh->p=BN_bin2bn(dh1536_p,sizeof(dh1536_p),NULL); dh->g=BN_bin2bn(dh1536_g,sizeof(dh1536_g),NULL); if ((dh->p == NULL) || (dh->g == NULL)) return(NULL); return(dh); } static DH * get_dh2048() { DH *dh=NULL; if ((dh=DH_new()) == NULL) return(NULL); dh->p=BN_bin2bn(dh2048_p,sizeof(dh2048_p),NULL); dh->g=BN_bin2bn(dh2048_g,sizeof(dh2048_g),NULL); if ((dh->p == NULL) || (dh->g == NULL)) return(NULL); return(dh); } #endif /* NO_DH */ static DH MS_CALLBACK * #ifdef CK_ANSIC tmp_dh_cb(SSL * s, int export, int keylength) #else /* CK_ANSIC */ tmp_dh_cb(s,export,keylength) SSL *s; int export; int keylength; #endif /* CK_ANSIC */ { static DH *dh_tmp=NULL; BIO *bio=NULL; #ifndef NO_DH if (dh_tmp == NULL) { if (ssl_dh_param_file && (bio=BIO_new_file(ssl_dh_param_file,"r")) != NULL) dh_tmp=PEM_read_bio_DHparams(bio,NULL,NULL,NULL); if (bio != NULL) BIO_free(bio); if ( dh_tmp == NULL ) { if ( keylength < 768 ) dh_tmp = get_dh512(); else if ( keylength < 1024 ) dh_tmp = get_dh768(); else if ( keylength < 1536 ) dh_tmp = get_dh1024(); else if ( keylength < 2048 ) dh_tmp = get_dh1536(); else dh_tmp = get_dh2048(); } } #else /* NO_DH */ if (ssl_debug_flag) printf("DH not supported...\r\n"); #endif /* NO_DH */ return(dh_tmp); } static void ssl_display_comp(SSL * ssl) { if ( quiet ) /* fdc - Mon Nov 28 11:44:15 2005 */ return; if ( !ck_ssleay_is_installed() ) return; if (ssl == NULL) return; if (ssl->expand == NULL || ssl->expand->meth == NULL) printf("Compression: None\r\n"); else { printf("Compression: %s\r\n",ssl->expand->meth->name); } } int #ifdef CK_ANSIC ssl_display_connect_details(SSL * ssl_con, int server, int verbose) #else /* CK_ANSIC */ ssl_display_connect_details(ssl_con,server,verbose) SSL *ssl_con; int server; int verbose; #endif /* CK_ANSIC */ { X509 *peer; SSL_CIPHER * cipher; const char *cipher_list; char buf[512]=""; if ( quiet ) /* fdc - Mon Nov 28 11:44:15 2005 */ return(0); if ( !ck_ssleay_is_installed() ) return(0); if ( inserver && !tn_deb ) return(0); /* the cipher list *can* be NULL ... useless but it happens! */ cipher = SSL_get_current_cipher(ssl_con); cipher_list = SSL_CIPHER_get_name(cipher); SSL_CIPHER_description(cipher,buf,sizeof(buf)); if (cipher_list==NULL) cipher_list=""; printf("[TLS - %s",buf); ssl_display_comp(ssl_con); if ( server ) { cipher_list=SSL_get_shared_ciphers(ssl_con,buf,512); if (cipher_list==NULL) cipher_list=""; printf("[TLS - shared ciphers=%s]\r\n", cipher_list); } if ( server || tn_deb ) { peer=SSL_get_peer_certificate(ssl_con); if (peer != NULL) { X509_NAME_oneline(X509_get_subject_name(peer),buf,512); printf("[TLS - subject=%s]\r\n",buf); X509_NAME_oneline(X509_get_issuer_name(peer),buf,512); printf("[TLS - issuer=%s]\r\n",buf); /* X509_free(peer); */ } else if (!tls_is_krb5(0)) { if ( !sstelnet && !tcp_incoming ) { printf("[TLS - No certificate provided.]\r\n"); printf( "[TLS - The identity of the host could not be verified.]\r\n"); } } } return(0); } /* * Use SSL_CTX_set_default_passwd_cb_userdata(SSL_CTX *, void * userdata) * to set the value of the userdata. We are going to use it to store the * prompt. */ int #ifdef CK_ANSIC ssl_passwd_callback(char *buf, int len, int rwflag, VOID * userdata) #else /* CK_ANSIC */ ssl_passwd_callback(buf,len,rwflag,userdata) char * buf; int len; int rwflag; VOID *userdata; #endif /* CK_ANSIC */ { extern char pwbuf[]; extern int pwflg, pwcrypt; int ok; char *prompt=NULL; if ( pwbuf[0] && pwflg ) { int n; n = ckstrncpy(buf,pwbuf,len); #ifdef OS2 if ( pwcrypt ) ck_encrypt((char *)buf); #endif /* OS2 */ return(n); } if ( userdata == NULL ) prompt="Enter certificate passphrase: "; else prompt=(char*)userdata; ok = uq_txt(NULL,prompt,2,NULL,buf,len,NULL,DEFAULT_UQ_TIMEOUT); return(ok > 0 ? strlen(buf) : 0); } /* Attempts to load certificate data into the TLS context structures */ /* Returns 1 on success; 0 on failure */ int tls_load_certs(SSL_CTX * ctx, SSL * con, int server) { int rc = 1; if ( !ck_ssleay_is_installed() ) return(0); debug(F110,"tls_load_certs","SSL_CTX",0); debug(F110,"tls_load_certs","SSL",0); debug(F110,"tls_load_certs","server",0); if ( con ) { if (ssl_rsa_cert_file) { if ( ssl_debug_flag ) printf("Loading RSA certificate into SSL\r\n"); rc = SSL_use_certificate_file(con, ssl_rsa_cert_file, X509_FILETYPE_PEM); if (!rc) { if ( !quiet || ssl_debug_flag ) printf("Error loading certificate from %s\r\n", ssl_rsa_cert_file); } else { if (!ssl_rsa_key_file || !ssl_rsa_key_file[0]) makestr(&ssl_rsa_key_file,ssl_rsa_cert_file); rc = SSL_use_PrivateKey_file(con, ssl_rsa_key_file, X509_FILETYPE_PEM); if (!rc) rc = SSL_use_PrivateKey_file(con, ssl_rsa_cert_file, X509_FILETYPE_PEM); if (!rc) { if ( !quiet || ssl_debug_flag ) printf("Error loading key from %s\r\n", ssl_rsa_key_file); } else { rc = SSL_check_private_key(con); if (!rc) { if ( ssl_debug_flag ) printf( "Private key does not match the certificate public key\r\n"); } } } } if (ssl_dsa_cert_file) { if ( ssl_debug_flag ) printf("Loading DSA certificate into SSL\r\n"); rc = SSL_use_certificate_file(con, ssl_dsa_cert_file, X509_FILETYPE_PEM); if (!rc) { if ( ssl_debug_flag ) { printf("Error loading certificate from %s\r\n", ssl_dsa_cert_file); } } else { if (!ssl_dh_key_file || !ssl_dh_key_file[0]) makestr(&ssl_dh_key_file,ssl_dsa_cert_file); rc = SSL_use_PrivateKey_file(con, ssl_dh_key_file, X509_FILETYPE_PEM); if (!rc) rc = SSL_use_PrivateKey_file(con, ssl_dsa_cert_file, X509_FILETYPE_PEM); if (!rc) { if ( !quiet || ssl_debug_flag ) { printf("Error loading key from %s\r\n", ssl_dh_key_file); } } else { rc = SSL_check_private_key(con); if (!rc) { if ( ssl_debug_flag ) printf( "Private key does not match the certificate public key\n"); } } } } } else { if (ssl_rsa_cert_file) { if ( ssl_debug_flag ) printf("Loading RSA certificate into SSL\r\n"); rc = SSL_CTX_use_certificate_file(ctx, ssl_rsa_cert_file, X509_FILETYPE_PEM); if (!rc) { if ( !quiet || ssl_debug_flag ) printf("Error loading certificate from %s\r\n", ssl_rsa_cert_file); } else { if (!ssl_rsa_key_file || !ssl_rsa_key_file[0]) makestr(&ssl_rsa_key_file,ssl_rsa_cert_file); rc = SSL_CTX_use_PrivateKey_file(ctx, ssl_rsa_key_file, X509_FILETYPE_PEM); if (!rc) rc = SSL_CTX_use_PrivateKey_file(ctx, ssl_rsa_cert_file, X509_FILETYPE_PEM); if (!rc) { if ( ssl_debug_flag ) printf("Error loading key from %s\r\n",ssl_rsa_key_file); } else { rc = SSL_CTX_check_private_key(ctx); if (!rc) { if ( ssl_debug_flag ) printf( "Private key does not match the certificate public key\r\n"); } } } } if (ssl_dsa_cert_file) { if ( ssl_debug_flag ) printf("Loading DSA certificate into SSL\r\n"); rc = SSL_CTX_use_certificate_file(ctx, ssl_dsa_cert_file, X509_FILETYPE_PEM); if (!rc) { if ( ssl_debug_flag ) { printf("Error loading certificate from %s\r\n", ssl_dsa_cert_file); } } else { if (!ssl_dh_key_file || !ssl_dh_key_file[0]) makestr(&ssl_dh_key_file,ssl_dsa_cert_file); rc = SSL_CTX_use_PrivateKey_file(ctx, ssl_dh_key_file, X509_FILETYPE_PEM); if (!rc) rc = SSL_CTX_use_PrivateKey_file(ctx, ssl_dsa_cert_file, X509_FILETYPE_PEM); if (!rc) { if ( ssl_debug_flag ) printf("Error loading key from %s\r\n",ssl_dh_key_file); } else { rc = SSL_CTX_check_private_key(ctx); if (!rc) { if ( ssl_debug_flag ) printf( "Private key does not match the certificate public key\n"); } } } } } if (ssl_rsa_cert_chain_file && server) { int skip1st = 0; if (ssl_debug_flag) printf("Loading RSA Certificate Chain into SSL\r\n"); if (!ckstrcmp(ssl_rsa_cert_chain_file,ssl_rsa_cert_file,-1, #ifdef OS2 0 #else 1 #endif /* OS2 */ )) skip1st = 1; rc = SSL_CTX_use_certificate_chain_file(ctx,ssl_rsa_cert_chain_file); if (!rc && ssl_debug_flag) printf("Error loading RSA Certificate Chain into SSL\r\n"); } if (ssl_dsa_cert_chain_file && server) { int skip1st = 0; if (ssl_debug_flag) printf("Loading DSA Certificate Chain into SSL\r\n"); if (!ckstrcmp(ssl_dsa_cert_chain_file,ssl_dsa_cert_file,-1, #ifdef OS2 0 #else 1 #endif /* OS2 */ )) skip1st = 1; rc = SSL_CTX_use_certificate_chain_file(ctx,ssl_dsa_cert_chain_file); if (!rc && ssl_debug_flag) printf("Error loading DSA Certificate Chain into SSL\r\n"); } return(rc); } VOID #ifdef CK_ANSIC ssl_once_init(void) #else ssl_once_init() #endif /* CK_ANSIC */ { COMP_METHOD * cm; char * s; if ( !ck_ssleay_is_installed() ) return; /* OpenSSL does not provide for ABI compatibility between releases prior to version 1.0.0. If the version does not match, it is not safe to assume that any function you call takes the same parameters or does the same thing with them. Removing this test prior to the OpenSSL 1.0.0 release will result in an increase in unexplained or incorrect behaviors. The test should be revised once OpenSSL 1.0.0 is released and we see what its claims are as to ABI compatibility. */ debug(F111,"Kermit built for OpenSSL",OPENSSL_VERSION_TEXT,SSLEAY_VERSION_NUMBER); #ifndef OS2ONLY debug(F111,"OpenSSL Library",SSLeay_version(SSLEAY_VERSION), SSLeay()); debug(F110,"OpenSSL Library",SSLeay_version(SSLEAY_BUILT_ON),0); debug(F110,"OpenSSL Library",SSLeay_version(SSLEAY_CFLAGS),0); debug(F110,"OpenSSL Library",SSLeay_version(SSLEAY_PLATFORM),0); /* The following test is suggested by Richard Levitte */ if (((OPENSSL_VERSION_NUMBER ^ SSLeay()) & 0xffffff0f) #ifdef OS2 || ckstrcmp(OPENSSL_VERSION_TEXT,(char *)SSLeay_version(SSLEAY_VERSION),-1,1) #endif /* OS2 */ ) { ssl_installed = 0; debug(F111,"OpenSSL Version does not match. Built with", SSLeay_version(SSLEAY_VERSION),SSLEAY_VERSION_NUMBER); printf("?OpenSSL libraries do not match required version:\r\n"); printf(" . C-Kermit built with %s\r\n",OPENSSL_VERSION_TEXT); printf(" . Version found %s\r\n",SSLeay_version(SSLEAY_VERSION)); printf(" OpenSSL versions prior to 1.0.0 must be the same.\r\n"); s = "R"; #ifdef SOLARIS printf(" Set CD_LIBRARY_PATH for %s.\r\n",OPENSSL_VERSION_TEXT); s = " Or r"; #endif /* SOLARIS */ #ifdef HPUX printf(" Set SHLIB_PATH for %s.\r\n",OPENSSL_VERSION_TEXT); s = " Or r"; #endif /* HPUX */ #ifdef AIX printf(" Set LIBPATH for %s.\r\n",OPENSSL_VERSION_TEXT); s = " Or r"; #endif /* AIX */ #ifdef LINUX printf(" Set LD_LIBRARY_PATH for %s.\r\n",OPENSSL_VERSION_TEXT); s = " Or r"; #endif /* LINUX */ printf(" %sebuild C-Kermit from source on this computer to make \ versions agree.\r\n",s); #ifdef KTARGET { char * s; s = KTARGET; if (!s) s = ""; if (!*s) s = "(unknown)"; printf(" C-Kermit makefile target: %s\r\n",s); } #endif /* KTARGET */ printf(" Or if that is what you did then try to find out why\r\n"); printf(" the program loader (image activator) is choosing a\r\n"); printf(" different OpenSSL library than the one specified in \ the build.\r\n\r\n"); printf(" All SSL/TLS features disabled.\r\n\r\n"); bleep(BP_FAIL); #ifdef SSLDLL ck_ssl_unloaddll(); ck_crypto_unloaddll(); #endif /* SSLDLL */ return; } #endif /* OS2ONLY */ /* init things so we will get meaningful error messages * rather than numbers */ SSL_load_error_strings(); #ifdef SSHBUILTIN OPENSSL_add_all_algorithms_noconf(); #else /* SSL_library_init() only loads those ciphers needs for SSL */ /* These happen to be a similar set to those required for SSH */ /* but they are not a complete set of ciphers provided by the */ /* crypto library. */ SSL_library_init(); #endif /* SSHBUILTIN */ #ifdef ZLIB cm = COMP_zlib(); if (cm != NULL && cm->type != NID_undef) { SSL_COMP_add_compression_method(0xe0, cm); /* EAY's ZLIB ID */ } #endif /* ZLIB */ cm = COMP_rle(); if (cm != NULL && cm->type != NID_undef) SSL_COMP_add_compression_method(0xe1, cm); /* EAY's RLE ID */ /* Ensure the Random number generator has enough entropy */ if ( !RAND_status() ) { char buffer[256]=""; char randombytes[256]; int rc1 = -1, rc2 = 1; /* assume failure and success */ debug(F110,"ssl_once_init","!RAND_status()",0); if ( ssl_rnd_file == NULL ) { debug(F110,"ssl_rnd_file","ssl_rnd_file is NULL",0); RAND_file_name(buffer,256); if ( buffer[0] ) makestr(&ssl_rnd_file, buffer); else makestr(&ssl_rnd_file,".rnd"); } debug(F110,"ssl_rnd_file",ssl_rnd_file,0); rc1 = RAND_egd(ssl_rnd_file); debug(F111,"ssl_once_init","RAND_egd()",rc1); if ( rc1 <= 0 ) { rc2 = RAND_load_file(ssl_rnd_file, -1); debug(F111,"ssl_once_init","RAND_load_file()",rc1); } if ( rc1 <= 0 && !rc2 ) { time_t t = time(NULL); int tlen = sizeof(time_t); int pid = getpid(); int plen = sizeof(int); int n; #ifndef RAND_MAX #define RAND_MAX 0x7FFF #endif debug(F110,"ssl_once_init","calling RAND_seed()",0); RAND_seed((unsigned char *)&t, tlen); RAND_seed((unsigned char *)&pid, plen); srand((unsigned int)t); sprintf(buffer, "%.0f", (((double)(rand()%RAND_MAX)/RAND_MAX)* (sizeof(randombytes)-128-1))); n = (atoi(buffer)+1)%(sizeof(randombytes)-128-1); RAND_seed(randombytes, 128); } if ( !RAND_status() ) { debug(F110,"ssl_once_init","Unable to initialize PRNG",0); printf(" Unable to load 'random state'\n"); printf(" SSL and TLS are unavailble.\n"); printf(" Use SET AUTH SSL RANDOM-FILE command to provide random data.\n"); printf(" Specified file will be overwritten with new random data after use.\n"); return; } if ( ssl_rnd_file ) { int rc = RAND_write_file(ssl_rnd_file); debug(F111,"ssl_once_init","RAND_write_file()",rc); } } #ifdef NT // Initialize additional OID types for use when saving certs to a file OBJ_create("2.99999.3","SET.ex3","SET x509v3 extension 3"); #endif /* NT */ /* make sure we have somewhere we can log errors to */ bio_err=BIO_new(BIO_s_mem()); debug(F100,"ssl_once_init() complete","",0); } int #ifdef CK_ANSIC ssl_tn_init(int mode) #else ssl_tn_init(mode) int mode; #endif /* CK_ANSIC */ { #ifdef KRB5 extern char * k5_keytab; extern char * krb5_d_srv; #endif /* KRB5 */ static int last_ssl_mode = -1; SSL * ssl_conx=NULL, * tls_conx=NULL; ssl_initialized = 0; if ( !ck_ssleay_is_installed() ) return(0); debug(F111,"ssl_tn_init","mode",mode); if (ssl_debug_flag) printf("SSL_DEBUG_FLAG on\r\n"); if (last_ssl_mode != mode) { if (ssl_ctx) { SSL_CTX_free(ssl_ctx); ssl_ctx = NULL; } if (tls_ctx) { SSL_CTX_free(tls_ctx); tls_ctx = NULL; } } if ( (last_ssl_mode != mode) || !ssl_ctx || !tls_ctx ) { if ( mode == SSL_CLIENT ) { ssl_ctx=(SSL_CTX *)SSL_CTX_new(SSLv23_client_method()); /* This can fail because we do not have RSA available */ if ( !ssl_ctx ) { debug(F110,"ssl_tn_init","SSLv23_client_method failed",0); ssl_ctx=(SSL_CTX *)SSL_CTX_new(SSLv3_client_method()); } if ( !ssl_ctx ) { debug(F110,"ssl_tn_init","SSLv3_client_method failed",0); last_ssl_mode = -1; return(0); } #ifndef COMMENT tls_ctx=(SSL_CTX *)SSL_CTX_new(TLSv1_client_method()); #else /* COMMENT */ tls_ctx=(SSL_CTX *)SSL_CTX_new(SSLv23_client_method()); /* This can fail because we do not have RSA available */ if ( !tls_ctx ) { debug(F110,"ssl_tn_init","SSLv23_client_method failed",0); tls_ctx=(SSL_CTX *)SSL_CTX_new(SSLv3_client_method()); } #endif /* COMMENT */ if ( !tls_ctx ) { debug(F110,"ssl_tn_init","TLSv1_client_method failed",0); last_ssl_mode = -1; return(0); } #ifdef USE_CERT_CB SSL_CTX_set_client_cert_cb(ssl_ctx,ssl_client_cert_callback); SSL_CTX_set_client_cert_cb(tls_ctx,ssl_client_cert_callback); #endif /* USE_CERT_CB */ } else if (mode == SSL_SERVER) { /* We are a server */ ssl_ctx=(SSL_CTX *)SSL_CTX_new(SSLv23_server_method()); /* This can fail because we do not have RSA available */ if ( !ssl_ctx ) { debug(F110,"ssl_tn_init","SSLv23_server_method failed",0); ssl_ctx=(SSL_CTX *)SSL_CTX_new(SSLv3_server_method()); } if ( !ssl_ctx ) { debug(F110,"ssl_tn_init","SSLv3_server_method failed",0); last_ssl_mode = -1; return(0); } #ifdef COMMENT tls_ctx=(SSL_CTX *)SSL_CTX_new(TLSv1_server_method()); #else /* COMMENT */ tls_ctx=(SSL_CTX *)SSL_CTX_new(SSLv23_server_method()); /* This can fail because we do not have RSA available */ if ( !tls_ctx ) { debug(F110,"ssl_tn_init","SSLv23_server_method failed",0); tls_ctx=(SSL_CTX *)SSL_CTX_new(TLSv1_server_method()); } #endif /* COMMENT */ if ( !tls_ctx ) { debug(F110,"ssl_tn_init","TLSv1_server_method failed",0); last_ssl_mode = -1; return(0); } } else /* Unknown mode */ return(0); if ( !inserver ) { SSL_CTX_set_default_passwd_cb(ssl_ctx, (pem_password_cb *)ssl_passwd_callback); SSL_CTX_set_default_passwd_cb(tls_ctx, (pem_password_cb *)ssl_passwd_callback); } /* for SSL switch on all the interoperability and bug * workarounds so that we will communicate with people * that cannot read poorly written specs :-) * for TLS be sure to prevent use of SSLv2 */ SSL_CTX_set_options(ssl_ctx,SSL_OP_ALL|SSL_OP_NO_SSLv2); SSL_CTX_set_options(tls_ctx, SSL_OP_NO_SSLv2|SSL_OP_SINGLE_DH_USE|SSL_OP_EPHEMERAL_RSA); SSL_CTX_set_info_callback(ssl_ctx,ssl_client_info_callback); SSL_CTX_set_info_callback(tls_ctx,ssl_client_info_callback); #ifndef COMMENT /* Set the proper caching mode */ if ( mode == SSL_SERVER ) { SSL_CTX_set_session_cache_mode(ssl_ctx,SSL_SESS_CACHE_SERVER); SSL_CTX_set_session_cache_mode(tls_ctx,SSL_SESS_CACHE_SERVER); } else { SSL_CTX_set_session_cache_mode(ssl_ctx,SSL_SESS_CACHE_CLIENT); SSL_CTX_set_session_cache_mode(tls_ctx,SSL_SESS_CACHE_CLIENT); } SSL_CTX_set_session_id_context(ssl_ctx,(CHAR *)"1",1); SSL_CTX_set_session_id_context(tls_ctx,(CHAR *)"2",1); #else /* COMMENT */ SSL_CTX_set_session_cache_mode(ssl_ctx,SSL_SESS_CACHE_OFF); SSL_CTX_set_session_cache_mode(tls_ctx,SSL_SESS_CACHE_OFF); #endif /* COMMENT */ } /* The server uses defaults for the certificate files. */ /* The client does not. */ if (mode == SSL_SERVER) { char cert_filepath[1024]; const char * defdir = NULL; DH * dh = NULL; defdir = getenv("SSL_CERT_DIR"); if ( !defdir ) { #ifdef OS2 defdir = exedir; #else /* OS2 */ defdir = X509_get_default_cert_dir(); #endif /* OS2 */ debug(F110,"ssl_tn_init - setting default directory to",defdir,0); } if ( !defdir ) defdir = ""; if (!ssl_rsa_cert_file) { /* we need to know the fullpath to the location of the * certificate that we will be running with as we cannot * be sure of the cwd when we are launched */ sprintf(cert_filepath,"%s/%s",defdir,"telnetd-rsa.pem"); if (zchki(cert_filepath) > 0) makestr(&ssl_rsa_cert_file,cert_filepath); } if (ssl_rsa_cert_file && !ssl_rsa_key_file) { /* we need to know the fullpath to the location of the * certificate that we will be running with as we cannot * be sure of the cwd when we are launched */ sprintf(cert_filepath,"%s/%s",defdir,"telnetd-rsa-key.pem"); if (zchki(cert_filepath) > 0) makestr(&ssl_rsa_key_file,cert_filepath); } if (!ssl_dsa_cert_file) { /* we need to know the fullpath to the location of the * certificate that we will be running with as we cannot * be sure of the cwd when we are launched */ sprintf(cert_filepath,"%s/%s",defdir,"telnetd-dsa.pem"); if (zchki(cert_filepath) > 0) makestr(&ssl_dsa_cert_file,cert_filepath); } if (ssl_dsa_cert_file && !ssl_dh_key_file) { /* we need to know the fullpath to the location of the * certificate that we will be running with as we cannot * be sure of the cwd when we are launched */ sprintf(cert_filepath,"%s/%s",defdir,"telnetd-dsa-key.pem"); if (zchki(cert_filepath) > 0) makestr(&ssl_dh_key_file,cert_filepath); } if (!ssl_crl_dir) { /* we need to know the fullpath to the location of the * certificate that we will be running with as we cannot * be sure of the cwd when we are launched */ sprintf(cert_filepath,"%s/crl",defdir); if (zchki(cert_filepath) > 0) makestr(&ssl_crl_dir,cert_filepath); } if (ssl_only_flag && !tls_load_certs(ssl_ctx,ssl_con,1)) { debug(F110,"ssl_tn_init","Unable to load SSL certs",0); last_ssl_mode = -1; return(0); } if (tls_only_flag && !tls_load_certs(tls_ctx,tls_con,1)) { debug(F110,"ssl_tn_init","Unable to load TLS certs",0); last_ssl_mode = -1; return(0); } if ( (last_ssl_mode != mode) || !ssl_ctx || !tls_ctx ) { /* we may require a temp 512 bit RSA key because of the * wonderful way export things work ... if so we generate * one now! */ SSL_CTX_set_tmp_rsa_callback(ssl_ctx, tmp_rsa_cb); SSL_CTX_set_tmp_dh_callback( ssl_ctx, tmp_dh_cb); SSL_CTX_set_tmp_rsa_callback(tls_ctx, tmp_rsa_cb); SSL_CTX_set_tmp_dh_callback( tls_ctx, tmp_dh_cb); dh = tmp_dh_cb(NULL,0,512); SSL_CTX_set_tmp_dh(ssl_ctx,dh); SSL_CTX_set_tmp_dh(tls_ctx,dh); /* The following code is only called if we are using a * certificate with an RSA public key and where the * certificate has a key length less than 512 bits or is * marked for signing only. This is so we can support * the greatest legal privacy level with exportable clients. */ if (SSL_CTX_need_tmp_RSA(ssl_ctx) || SSL_CTX_need_tmp_RSA(tls_ctx)) { RSA *rsa; if ( ssl_debug_flag ) printf("Generating temp (512 bit) RSA key ...\r\n"); rsa=RSA_generate_key(512,RSA_F4,NULL,NULL); if ( ssl_debug_flag ) printf("Generation of temp (512 bit) RSA key done\r\n"); if (SSL_CTX_need_tmp_RSA(ssl_ctx)) { if (!SSL_CTX_set_tmp_rsa(ssl_ctx,rsa)) { if ( ssl_debug_flag ) printf( "Failed to assign generated temp RSA key to SSL!\r\n"); } } if (SSL_CTX_need_tmp_RSA(tls_ctx)) { if (!SSL_CTX_set_tmp_rsa(tls_ctx,rsa)) { if ( ssl_debug_flag ) printf( "Failed to assign generated temp RSA key to TLS!\r\n"); } } RSA_free(rsa); if ( ssl_debug_flag ) printf("Assigned temp (512 bit) RSA key\r\n"); } } } /* make sure we will find certificates in the standard * location ... otherwise we don't look anywhere for * these things which is going to make client certificate * exchange rather useless :-) * In OS2, default values for ssl_verify_file and ssl_verify_path. */ #ifdef OS2 #ifdef NT { /* The defaults in the SSL crypto library are not appropriate for OS/2 */ char path[CKMAXPATH]; ckmakmsg(path,CKMAXPATH,exedir,"certs",NULL,NULL); if (isdir(path) && SSL_CTX_load_verify_locations(tls_ctx,NULL,path) == 1) { debug(F110,"ssl_tn_init certificate verify dir",path,0); if (ssl_debug_flag) printf(" Certificate Verification Directory: %s\r\n",path); SSL_CTX_load_verify_locations(ssl_ctx,NULL,path); } ckmakmsg(path,CKMAXPATH,GetAppData(1),"kermit 95/certs",NULL,NULL); if (isdir(path) && SSL_CTX_load_verify_locations(tls_ctx,NULL,path) == 1) { debug(F110,"ssl_tn_init certificate verify dir",path,0); if (ssl_debug_flag) printf(" Certificate Verification Directory: %s\r\n",path); SSL_CTX_load_verify_locations(ssl_ctx,NULL,path); } ckmakmsg(path,CKMAXPATH,GetAppData(0),"kermit 95/certs",NULL,NULL); if (isdir(path) && SSL_CTX_load_verify_locations(tls_ctx,NULL,path) == 1) { debug(F110,"ssl_tn_init certificate verify dir",path,0); if (ssl_debug_flag) printf(" Certificate Verification Directory: %s\r\n",path); SSL_CTX_load_verify_locations(ssl_ctx,NULL,path); } ckmakmsg(path,CKMAXPATH,exedir,"ca_certs.pem",NULL,NULL); if (zchki(path) > 0 && SSL_CTX_load_verify_locations(tls_ctx,path,NULL) == 1) { debug(F110,"ssl_tn_init certificate verify file",path,0); if (ssl_debug_flag) printf(" Certificate Verification File: %s\r\n",path); SSL_CTX_load_verify_locations(ssl_ctx,path,NULL); } ckmakmsg(path,CKMAXPATH,GetAppData(1),"kermit 95/ca_certs.pem",NULL,NULL); if (zchki(path) > 0 && SSL_CTX_load_verify_locations(tls_ctx,path,NULL) == 1) { debug(F110,"ssl_tn_init certificate verify file",path,0); if (ssl_debug_flag) printf(" Certificate Verification File: %s\r\n",path); SSL_CTX_load_verify_locations(ssl_ctx,path,NULL); } ckmakmsg(path,CKMAXPATH,GetAppData(0),"kermit 95/ca_certs.pem",NULL,NULL); if (zchki(path) > 0 && SSL_CTX_load_verify_locations(tls_ctx,path,NULL) == 1) { debug(F110,"ssl_tn_init certificate verify file",path,0); if (ssl_debug_flag) printf(" Certificate Verification File: %s\r\n",path); SSL_CTX_load_verify_locations(ssl_ctx,path,NULL); } } #else /* NT */ { /* The defaults in the SSL crypto library are not appropriate for OS/2 */ char path[CKMAXPATH]; ckmakmsg(path,CKMAXPATH,exedir,"certs",NULL,NULL); if (isdir(path) && SSL_CTX_load_verify_locations(tls_ctx,NULL,path) == 1) { debug(F110,"ssl_tn_init certificate verify dir",path,0); if (ssl_debug_flag) printf(" Certificate Verification Directory: %s\r\n",path); SSL_CTX_load_verify_locations(ssl_ctx,NULL,path); } ckmakmsg(path,CKMAXPATH,exedir,"ca_certs.pem",NULL,NULL); if (zchki(path) > 0 && SSL_CTX_load_verify_locations(tls_ctx,path,NULL) == 1) { debug(F110,"ssl_tn_init certificate verify file",path,0); if (ssl_debug_flag) printf(" Certificate Verification File: %s\r\n",path); SSL_CTX_load_verify_locations(ssl_ctx,path,NULL); } } #endif /* NT */ #else /* OS2 */ SSL_CTX_set_default_verify_paths(ssl_ctx); SSL_CTX_set_default_verify_paths(tls_ctx); #endif /* OS2 */ if (ssl_verify_file) { if (zchki(ssl_verify_file) > 0 && SSL_CTX_load_verify_locations(tls_ctx,ssl_verify_file,NULL) == 1) { debug(F110,"ssl_tn_init certificate verify file",ssl_verify_file,0); if (ssl_debug_flag) printf(" Certificate Verification File: %s\r\n",ssl_verify_file); SSL_CTX_load_verify_locations(ssl_ctx,ssl_verify_file,NULL); } } if (ssl_verify_dir && isdir(ssl_verify_dir)) { if (SSL_CTX_load_verify_locations(tls_ctx,NULL,ssl_verify_dir) == 1) { debug(F110,"ssl_tn_init certificate verify dir",ssl_verify_dir,0); if (ssl_debug_flag) printf(" Certificate Verification Directory: %s\r\n",ssl_verify_dir); SSL_CTX_load_verify_locations(ssl_ctx,NULL,ssl_verify_dir); } } if (mode == SSL_SERVER) { SSL_CTX_set_verify(ssl_ctx, ssl_verify_flag?ssl_verify_flag|SSL_VERIFY_CLIENT_ONCE:0, ssl_server_verify_callback); SSL_CTX_set_verify(tls_ctx, ssl_verify_flag?ssl_verify_flag|SSL_VERIFY_CLIENT_ONCE:0, ssl_server_verify_callback); } else { SSL_CTX_set_verify(ssl_ctx,ssl_verify_flag, ssl_client_verify_callback); SSL_CTX_set_verify(tls_ctx,ssl_verify_flag, ssl_client_verify_callback); } /* Free the existing CRL Store */ if (crl_store) { X509_STORE_free(crl_store); crl_store = NULL; } /* set up the new CRL Store */ crl_store = X509_STORE_new(); if (crl_store) { #ifdef OS2 char path[CKMAXPATH]; ckmakmsg(path,CKMAXPATH,exedir,"crls",NULL,NULL); if (isdir(path) && X509_STORE_load_locations(crl_store,NULL,path) == 1) { debug(F110,"ssl_tn_init crl dir",path,0); if (ssl_debug_flag) printf(" CRL Directory: %s\r\n",path); } #ifdef NT ckmakmsg(path,CKMAXPATH,GetAppData(1),"kermit 95/crls",NULL,NULL); if (isdir(path) && X509_STORE_load_locations(crl_store,NULL,path) == 1) { debug(F110,"ssl_tn_init crl dir",path,0); if (ssl_debug_flag) printf(" CRL Directory: %s\r\n",path); } ckmakmsg(path,CKMAXPATH,GetAppData(0),"kermit 95/crls",NULL,NULL); if (isdir(path) && X509_STORE_load_locations(crl_store,NULL,path) == 1) { debug(F110,"ssl_tn_init crl dir",path,0); if (ssl_debug_flag) printf(" CRL Directory: %s\r\n",path); } #endif /* NT */ ckmakmsg(path,CKMAXPATH,exedir,"ca_crls.pem",NULL,NULL); if (zchki(path) > 0 && X509_STORE_load_locations(crl_store,path,NULL) == 1) { debug(F110,"ssl_tn_init crl file",path,0); if (ssl_debug_flag) printf(" CRL File: %s\r\n",path); } #ifdef NT ckmakmsg(path,CKMAXPATH,GetAppData(1),"kermit 95/ca_crls.pem",NULL,NULL); if (zchki(path) > 0 && X509_STORE_load_locations(crl_store,path,NULL) == 1) { debug(F110,"ssl_tn_init crl file",path,0); if (ssl_debug_flag) printf(" CRL File: %s\r\n",path); } ckmakmsg(path,CKMAXPATH,GetAppData(0),"kermit 95/ca_crls.pem",NULL,NULL); if (zchki(path) > 0 && X509_STORE_load_locations(crl_store,path,NULL) == 1) { debug(F110,"ssl_tn_init crl file",path,0); if (ssl_debug_flag) printf(" CRL File: %s\r\n",path); } #endif /* NT */ #endif /* OS2 */ if (ssl_crl_file || ssl_crl_dir) { if (ssl_crl_file && zchki(ssl_crl_file) > 0 && X509_STORE_load_locations(crl_store,ssl_crl_file,NULL) == 1) { debug(F110,"ssl_tn_init crl file",ssl_crl_file,0); if (ssl_debug_flag) printf(" CRL File: %s\r\n",ssl_crl_file); } if (ssl_crl_dir && isdir(ssl_crl_dir) && X509_STORE_load_locations(crl_store,NULL,ssl_crl_dir) == 1) { debug(F110,"ssl_tn_init crl dir",ssl_crl_dir,0); if (ssl_debug_flag) printf(" CRL Directory: %s\r\n",ssl_crl_dir); } } #ifndef OS2 else { X509_STORE_set_default_paths(crl_store); } #endif /* OS2 */ } #ifndef COMMENT ssl_conx = ssl_con; ssl_con=(SSL *)SSL_new(ssl_ctx); if ( !ssl_con ) { debug(F110,"ssl_tn_init","SSL_new(ssl_con) failed",0); last_ssl_mode = -1; ssl_con = ssl_conx; return(0); } if (ssl_conx) { if ( mode == SSL_CLIENT ) { SSL_set_session(ssl_con, SSL_get_session(ssl_conx)); } #ifdef SSL_KRB5 if (ssl_conx->kssl_ctx) { kssl_ctx_free(ssl_conx->kssl_ctx); ssl_conx->kssl_ctx = NULL; } #endif /* SSL_KRB5 */ SSL_free(ssl_conx); ssl_conx = NULL; } tls_conx = tls_con; tls_con=(SSL *)SSL_new(tls_ctx); if ( !tls_con ) { debug(F110,"ssl_tn_init","SSL_new(tls_con) failed",0); last_ssl_mode = -1; tls_con = tls_conx; return(0); } if (tls_conx) { if ( mode == SSL_CLIENT ) SSL_set_session(tls_con, SSL_get_session(tls_conx)); #ifdef SSL_KRB5 if (tls_conx->kssl_ctx) { kssl_ctx_free(tls_conx->kssl_ctx); tls_conx->kssl_ctx = NULL; } #endif /* SSL_KRB5 */ SSL_free(tls_conx); tls_conx = NULL; } #else /* COMMENT */ /* I don't know why this does not work to reuse the connection. */ if ( ssl_con ) { SSL_clear(ssl_con); SSL_set_session(ssl_con,NULL); SSL_set_accept_state(ssl_con) ; } else { ssl_con=(SSL *)SSL_new(ssl_ctx); if (!ssl_con) { debug(F110,"ssl_tn_init","SSL_new(ssl_ctx) failed",0); last_ssl_mode = -1; ssl_con = ssl_conx; return(0); } } if ( tls_con ) { SSL_clear(tls_con); SSL_set_session(tls_con,NULL); SSL_set_accept_state(tls_con) ; } else { tls_con=(SSL *)SSL_new(tls_ctx); if ( !tls_con ) { debug(F110,"ssl_tn_init","SSL_new(tls_ctx) failed",0); last_ssl_mode = -1; tls_con = tls_conx; return(0); } } #endif /* COMMENT */ #ifdef SSL_KRB5 #ifndef KRB5_SERVICE_NAME #define KRB5_SERVICE_NAME "host" #endif if (ssl_con->kssl_ctx == NULL) ssl_con->kssl_ctx = kssl_ctx_new(); if (tls_con->kssl_ctx == NULL) tls_con->kssl_ctx = kssl_ctx_new(); if (mode == SSL_SERVER) { if (ssl_con->kssl_ctx != NULL) kssl_ctx_setstring(ssl_con->kssl_ctx, KSSL_KEYTAB, k5_keytab); if (tls_con->kssl_ctx != NULL) kssl_ctx_setstring(tls_con->kssl_ctx, KSSL_KEYTAB, k5_keytab); } else { if (ssl_con->kssl_ctx != NULL) kssl_ctx_setstring(ssl_con->kssl_ctx, KSSL_SERVER, szHostName); if (tls_con->kssl_ctx != NULL) kssl_ctx_setstring(tls_con->kssl_ctx, KSSL_SERVER, szHostName); } kssl_ctx_setstring(ssl_con->kssl_ctx, KSSL_SERVICE, krb5_d_srv ? krb5_d_srv : KRB5_SERVICE_NAME); kssl_ctx_setstring(tls_con->kssl_ctx, KSSL_SERVICE, krb5_d_srv ? krb5_d_srv : KRB5_SERVICE_NAME); #endif /* SSL_KRB5 */ if (ssl_cipher_list) { SSL_set_cipher_list(ssl_con,ssl_cipher_list); SSL_set_cipher_list(tls_con,ssl_cipher_list); } else { char * p; if (p = getenv("SSL_CIPHER")) { SSL_set_cipher_list(ssl_con,p); SSL_set_cipher_list(tls_con,p); } else { SSL_set_cipher_list(ssl_con,DEFAULT_CIPHER_LIST); SSL_set_cipher_list(tls_con,DEFAULT_CIPHER_LIST); } } ssl_verify_depth = -1; if ( ssl_debug_flag ) printf("SSL/TLS init done!\r\n"); ssl_initialized = 1; last_ssl_mode = mode; debug(F110,"ssl_tn_init","done",0); return(1); } #ifndef NOHTTP int #ifdef CK_ANSIC ssl_http_init(char * hostname) #else ssl_http_init(hostname) char * hostname; #endif /* CK_ANSIC */ { #ifdef KRB5 extern char * k5_keytab; extern char * krb5_d_srv; #endif /* KRB5 */ SSL * tls_conx=NULL; ssl_http_initialized = 0; if ( !ck_ssleay_is_installed() ) return(0); debug(F110,"ssl_http_init",hostname,0); if (ssl_debug_flag) printf("SSL_DEBUG_FLAG on\r\n"); if (!tls_http_ctx ) { #ifdef COMMENT /* too many web servers still do not support TLSv1 */ tls_http_ctx=(SSL_CTX *)SSL_CTX_new(TLSv1_client_method()); #else /* COMMENT */ tls_http_ctx=(SSL_CTX *)SSL_CTX_new(SSLv23_client_method()); /* This can fail because we do not have RSA available */ if ( !tls_http_ctx ) { debug(F110,"ssl_http_init","SSLv23_client_method failed",0); tls_http_ctx=(SSL_CTX *)SSL_CTX_new(SSLv3_client_method()); } #endif /* COMMENT */ if ( !tls_http_ctx ) { debug(F110,"ssl_http_init","TLSv1_client_method failed",0); return(0); } #ifdef USE_CERT_CB SSL_CTX_set_client_cert_cb(tls_http_ctx,ssl_client_cert_callback); #endif /* USE_CERT_CB */ } SSL_CTX_set_default_passwd_cb(tls_http_ctx, (pem_password_cb *)ssl_passwd_callback); /* for SSL switch on all the interoperability and bug * workarounds so that we will communicate with people * that cannot read poorly written specs :-) * for TLS be sure to prevent use of SSLv2 */ SSL_CTX_set_options(tls_http_ctx, SSL_OP_NO_SSLv2|SSL_OP_SINGLE_DH_USE|SSL_OP_EPHEMERAL_RSA); SSL_CTX_set_info_callback(tls_http_ctx,ssl_client_info_callback); #ifndef COMMENT SSL_CTX_set_session_cache_mode(tls_http_ctx,SSL_SESS_CACHE_CLIENT); SSL_CTX_set_session_id_context(tls_http_ctx,(CHAR *)"3",1); #else /* COMMENT */ SSL_CTX_set_session_cache_mode(tls_http_ctx,SSL_SESS_CACHE_OFF); #endif /* COMMENT */ /* make sure we will find certificates in the standard * location ... otherwise we don't look anywhere for * these things which is going to make client certificate * exchange rather useless :-) */ #ifdef OS2 #ifdef NT { /* The defaults in the SSL crypto library are not appropriate for OS/2 */ char path[CKMAXPATH]; ckmakmsg(path,CKMAXPATH,exedir,"certs",NULL,NULL); if (SSL_CTX_load_verify_locations(tls_http_ctx,NULL,path) == 0) { debug(F110,"ssl_http_init unable to load path",path,0); if (ssl_debug_flag) printf("?Unable to load verify-dir: %s\r\n",path); } ckmakmsg(path,CKMAXPATH,GetAppData(1),"kermit 95/certs",NULL,NULL); if (SSL_CTX_load_verify_locations(tls_http_ctx,NULL,path) == 0) { debug(F110,"ssl_http_init unable to load path",path,0); if (ssl_debug_flag) printf("?Unable to load verify-dir: %s\r\n",path); } ckmakmsg(path,CKMAXPATH,GetAppData(0),"kermit 95/certs",NULL,NULL); if (SSL_CTX_load_verify_locations(tls_http_ctx,NULL,path) == 0) { debug(F110,"ssl_http_init unable to load path",path,0); if (ssl_debug_flag) printf("?Unable to load verify-dir: %s\r\n",path); } ckmakmsg(path,CKMAXPATH,exedir,"ca_certs.pem",NULL,NULL); if (SSL_CTX_load_verify_locations(tls_http_ctx,path,NULL) == 0) { debug(F110,"ssl_http_init unable to load path",path,0); if (ssl_debug_flag) printf("?Unable to load verify-file: %s\r\n",path); } ckmakmsg(path,CKMAXPATH,GetAppData(1),"kermit 95/ca_certs.pem",NULL,NULL); if (SSL_CTX_load_verify_locations(tls_http_ctx,path,NULL) == 0) { debug(F110,"ssl_http_init unable to load path",path,0); if (ssl_debug_flag) printf("?Unable to load verify-file: %s\r\n",path); } ckmakmsg(path,CKMAXPATH,GetAppData(0),"kermit 95/ca_certs.pem",NULL,NULL); if (SSL_CTX_load_verify_locations(tls_http_ctx,path,NULL) == 0) { debug(F110,"ssl_http_init unable to load path",path,0); if (ssl_debug_flag) printf("?Unable to load verify-file: %s\r\n",path); } } #else /* NT */ { /* The defaults in the SSL crypto library are not appropriate for OS/2 */ char path[CKMAXPATH]; ckmakmsg(path,CKMAXPATH,exedir,"certs",NULL,NULL); if (SSL_CTX_load_verify_locations(tls_http_ctx,NULL,path) == 0) { debug(F110,"ssl_http_init unable to load path",path,0); if (ssl_debug_flag) printf("?Unable to load verify-dir: %s\r\n",path); } ckmakmsg(path,CKMAXPATH,exedir,"ca_certs.pem",NULL,NULL); if (SSL_CTX_load_verify_locations(tls_http_ctx,path,NULL) == 0) { debug(F110,"ssl_http_init unable to load path",path,0); if (ssl_debug_flag) printf("?Unable to load verify-file: %s\r\n",path); } } #endif /* NT */ #else /* OS2 */ SSL_CTX_set_default_verify_paths(tls_http_ctx); #endif /* OS2 */ if (ssl_verify_file && SSL_CTX_load_verify_locations(tls_http_ctx,ssl_verify_file,NULL) == 0) { debug(F110,"ssl_http_init unable to load ssl_verify_file",ssl_verify_file,0); if (ssl_debug_flag) printf("?Unable to load verify-file: %s\r\n",ssl_verify_file); } if (ssl_verify_dir && SSL_CTX_load_verify_locations(tls_http_ctx,NULL,ssl_verify_dir) == 0) { debug(F110,"ssl_http_init unable to load ssl_verify_dir",ssl_verify_dir,0); if (ssl_debug_flag) printf("?Unable to load verify-dir: %s\r\n",ssl_verify_dir); } SSL_CTX_set_verify(tls_http_ctx,ssl_verify_flag, ssl_client_verify_callback); /* Free the existing CRL Store */ if (crl_store) { X509_STORE_free(crl_store); crl_store = NULL; } /* set up the new CRL Store */ crl_store = X509_STORE_new(); if (crl_store) { #ifdef OS2 char path[CKMAXPATH]; ckmakmsg(path,CKMAXPATH,exedir,"crls",NULL,NULL); if (X509_STORE_load_locations(crl_store,NULL,path) == 0) { debug(F110,"ssl_http_init unable to load dir",path,0); if (ssl_debug_flag) printf("?Unable to load crl-dir: %s\r\n",path); } #ifdef NT ckmakmsg(path,CKMAXPATH,GetAppData(1),"kermit 95/crls",NULL,NULL); if (X509_STORE_load_locations(crl_store,NULL,path) == 0) { debug(F110,"ssl_http_init unable to load dir",path,0); if (ssl_debug_flag) printf("?Unable to load crl-dir: %s\r\n",path); } ckmakmsg(path,CKMAXPATH,GetAppData(0),"kermit 95/crls",NULL,NULL); if (X509_STORE_load_locations(crl_store,NULL,path) == 0) { debug(F110,"ssl_http_init unable to load dir",path,0); if (ssl_debug_flag) printf("?Unable to load crl-dir: %s\r\n",path); } #endif /* NT */ ckmakmsg(path,CKMAXPATH,exedir,"ca_crls.pem",NULL,NULL); if (X509_STORE_load_locations(crl_store,path,NULL) == 0) { debug(F110,"ssl_http_init unable to load file",path,0); if (ssl_debug_flag) printf("?Unable to load crl-file: %s\r\n",path); } #ifdef NT ckmakmsg(path,CKMAXPATH,GetAppData(1),"kermit 95/ca_crls.pem",NULL,NULL); if (X509_STORE_load_locations(crl_store,path,NULL) == 0) { debug(F110,"ssl_http_init unable to load file",path,0); if (ssl_debug_flag) printf("?Unable to load crl-file: %s\r\n",path); } ckmakmsg(path,CKMAXPATH,GetAppData(0),"kermit 95/ca_crls.pem",NULL,NULL); if (X509_STORE_load_locations(crl_store,path,NULL) == 0) { debug(F110,"ssl_http_init unable to load file",path,0); if (ssl_debug_flag) printf("?Unable to load crl-file: %s\r\n",path); } #endif /* NT */ #endif /* OS2 */ if (ssl_crl_file || ssl_crl_dir) { if (ssl_crl_file && X509_STORE_load_locations(crl_store,ssl_crl_file,NULL) == 0) { debug(F110,"ssl_http_init unable to load ssl_crl_file",ssl_crl_file,0); if (ssl_debug_flag) printf("?Unable to load crl-file: %s\r\n",ssl_crl_file); } if (ssl_crl_dir && X509_STORE_load_locations(crl_store,NULL,ssl_crl_dir) == 0) { debug(F110,"ssl_http_init unable to load ssl_crl_dir",ssl_crl_dir,0); if (ssl_debug_flag) printf("?Unable to load crl-dir: %s\r\n",ssl_crl_dir); } } else { X509_STORE_set_default_paths(crl_store); } } #ifndef COMMENT tls_conx = tls_http_con; tls_http_con=(SSL *)SSL_new(tls_http_ctx); if ( !tls_http_con ) { debug(F110,"ssl_http_init","SSL_new(tls_http_con) failed",0); tls_http_con = tls_conx; return(0); } if (tls_conx) { SSL_set_session(tls_http_con, SSL_get_session(tls_conx)); #ifdef SSL_KRB5 if (tls_conx->kssl_ctx) { kssl_ctx_free(tls_conx->kssl_ctx); tls_conx->kssl_ctx = NULL; } #endif /* SSL_KRB5 */ SSL_free(tls_conx); tls_conx = NULL; } #else /* COMMENT */ /* I don't know why this does not work to reuse the connection. */ if ( tls_http_con ) { SSL_clear(tls_http_con); SSL_set_session(tls_http_con,NULL); SSL_set_accept_state(tls_http_con) ; } else { tls_http_con=(SSL *)SSL_new(tls_http_ctx); if ( !tls_http_con ) { debug(F110,"ssl_http_init","SSL_new(tls_http_ctx) failed",0); tls_http_con = tls_conx; return(0); } } #endif /* COMMENT */ #ifdef SSL_KRB5 #ifndef KRB5_SERVICE_NAME #define KRB5_SERVICE_NAME "host" #endif if (tls_http_con->kssl_ctx == NULL) tls_http_con->kssl_ctx = kssl_ctx_new(); if (tls_http_con->kssl_ctx != NULL) kssl_ctx_setstring(tls_http_con->kssl_ctx, KSSL_SERVER, hostname); kssl_ctx_setstring(tls_http_con->kssl_ctx, KSSL_SERVICE, krb5_d_srv ? krb5_d_srv : KRB5_SERVICE_NAME); #endif /* SSL_KRB5 */ if (ssl_cipher_list) SSL_set_cipher_list(tls_http_con,ssl_cipher_list); else { char * p; if (p = getenv("SSL_CIPHER")) { SSL_set_cipher_list(tls_http_con,p); } else { SSL_set_cipher_list(tls_http_con,DEFAULT_CIPHER_LIST); } } ssl_verify_depth = -1; if ( ssl_debug_flag ) printf("SSL/TLS init done!\r\n"); ssl_http_initialized = 1; return(1); } #endif /* NOHTTP */ char * ssl_get_dNSName(ssl) SSL * ssl; { static char *dns = NULL; X509 *server_cert = NULL; int i; X509_EXTENSION *ext = NULL; STACK_OF(GENERAL_NAME) *ialt = NULL; GENERAL_NAME *gen = NULL; if ( dns ) { free(dns); dns = NULL; } if (server_cert = SSL_get_peer_certificate(ssl)) { if ((i = X509_get_ext_by_NID(server_cert, NID_subject_alt_name, -1))<0) return NULL; if (!(ext = X509_get_ext(server_cert, i))) return NULL; X509V3_add_standard_extensions(); if (!(ialt = X509V3_EXT_d2i(ext))) return NULL; for (i = 0; i < sk_GENERAL_NAME_num(ialt); i++) { gen = sk_GENERAL_NAME_value(ialt, i); if (gen->type == GEN_DNS) { if (!gen->d.ia5 || !gen->d.ia5->length) break; if (strlen((char *)gen->d.ia5->data) != gen->d.ia5->length) { /* Ignoring IA5String containing null character */ continue; } dns = malloc(gen->d.ia5->length + 1); if (dns) { memcpy(dns, gen->d.ia5->data, gen->d.ia5->length); dns[gen->d.ia5->length] = 0; } break; } } X509V3_EXT_cleanup(); } cleanup: if (ialt) sk_GENERAL_NAME_free(ialt); if (server_cert) X509_free(server_cert); return dns; } char * ssl_get_commonName(ssl) SSL * ssl; { static char name[256]; int name_text_len; int err; X509 *server_cert; name_text_len = 0; if (server_cert = SSL_get_peer_certificate(ssl)) { name_text_len = X509_NAME_get_text_by_NID(X509_get_subject_name(server_cert), NID_commonName, name, sizeof(name)); X509_free(server_cert); } if (name_text_len <= 0) { /* Common Name was empty or not retrieved */ err = 0; } else if (strlen(name) != name_text_len) { /* Ignoring Common Name containing null character */ err = 0; } else { err = 1; } if (err > 0) return name; else return NULL; } char * ssl_get_issuer_name(ssl) SSL * ssl; { static char name[256]; X509 *server_cert; name[0] = '\0'; if (server_cert = SSL_get_peer_certificate(ssl)) { X509_NAME_oneline(X509_get_issuer_name(server_cert),name,sizeof(name)); X509_free(server_cert); return name; } else { #ifdef COMMENT fprintf(stderr, "Warning: No certificate from server!\r\n"); #endif /* COMMENT */ return NULL; } } char * ssl_get_subject_name(ssl) SSL * ssl; { static char name[256]; X509 *server_cert; name[0] = '\0'; if (server_cert = SSL_get_peer_certificate(ssl)) { X509_NAME_oneline(X509_get_subject_name(server_cert),name,sizeof(name)); X509_free(server_cert); return name; } else return NULL; } #ifdef COMMENT #ifdef CK_SSL && !(ck_ssleay_is_installed() && (tls_active_flag || ssl_active_flag) && ssl_anonymous_cipher(tls_active_flag?tls_con:ssl_con)) #endif /* CK_SSL */ int ssl_anonymous_cipher(ssl) SSL * ssl; { X509 * cert; if (sstelnet) cert = SSL_get_certificate(ssl); else cert = SSL_get_peer_certificate(ssl); if ( cert ) { X509_free(cert); return 0; } return 1; } #endif /* COMMENT */ /* This one is (very much!) based on work by Ralf S. Engelschall . Comments by Ralf. */ int ssl_verify_crl(int ok, X509_STORE_CTX *ctx) { X509_OBJECT obj; X509_NAME *subject = NULL; X509_NAME *issuer = NULL; X509 *xs = NULL; X509_CRL *crl = NULL; X509_REVOKED *revoked = NULL; X509_STORE_CTX * store_ctx = NULL; long serial; BIO *bio = NULL; int i, n, rc; char *cp; char *cp2; /* * Unless a revocation store for CRLs was created we * cannot do any CRL-based verification, of course. */ if (!crl_store) return ok; store_ctx = X509_STORE_CTX_new(); if ( !store_ctx ) return(ok); /* * Determine certificate ingredients in advance */ xs = X509_STORE_CTX_get_current_cert(ctx); subject = X509_get_subject_name(xs); issuer = X509_get_issuer_name(xs); /* * OpenSSL provides the general mechanism to deal with CRLs but does not * use them automatically when verifying certificates, so we do it * explicitly here. We will check the CRL for the currently checked * certificate, if there is such a CRL in the store. * * We come through this procedure for each certificate in the certificate * chain, starting with the root-CA's certificate. At each step we've to * both verify the signature on the CRL (to make sure it's a valid CRL) * and it's revocation list (to make sure the current certificate isn't * revoked). But because to check the signature on the CRL we need the * public key of the issuing CA certificate (which was already processed * one round before), we've a little problem. But we can both solve it and * at the same time optimize the processing by using the following * verification scheme (idea and code snippets borrowed from the GLOBUS * project): * * 1. We'll check the signature of a CRL in each step when we find a CRL * through the _subject_ name of the current certificate. This CRL * itself will be needed the first time in the next round, of course. * But we do the signature processing one round before this where the * public key of the CA is available. * * 2. We'll check the revocation list of a CRL in each step when * we find a CRL through the _issuer_ name of the current certificate. * This CRLs signature was then already verified one round before. * * This verification scheme allows a CA to revoke its own certificate as * well, of course. */ /* * Try to retrieve a CRL corresponding to the _subject_ of * the current certificate in order to verify it's integrity. */ memset((char *)&obj, 0, sizeof(obj)); X509_STORE_CTX_init(store_ctx, crl_store, NULL, NULL); rc = X509_STORE_get_by_subject(store_ctx, X509_LU_CRL, subject, &obj); X509_STORE_CTX_cleanup(store_ctx); crl = obj.data.crl; if (rc > 0 && crl != NULL) { /* * Verify the signature on this CRL */ if (X509_CRL_verify(crl, X509_get_pubkey(xs)) <= 0) { fprintf(stderr, "Invalid signature on CRL!\n"); X509_STORE_CTX_set_error(ctx, X509_V_ERR_CRL_SIGNATURE_FAILURE); X509_OBJECT_free_contents(&obj); X509_STORE_CTX_free(store_ctx); return 0; } /* * Check date of CRL to make sure it's not expired */ i = X509_cmp_current_time(X509_CRL_get_nextUpdate(crl)); if (i == 0) { fprintf(stderr, "Found CRL has invalid nextUpdate field.\n"); X509_STORE_CTX_set_error(ctx, X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD); X509_OBJECT_free_contents(&obj); X509_STORE_CTX_free(store_ctx); return 0; } if (i < 0) { fprintf(stderr, "Found CRL is expired - revoking all certificates until you get updated CRL.\n" ); X509_STORE_CTX_set_error(ctx, X509_V_ERR_CRL_HAS_EXPIRED); X509_OBJECT_free_contents(&obj); X509_STORE_CTX_free(store_ctx); return 0; } X509_OBJECT_free_contents(&obj); } /* * Try to retrieve a CRL corresponding to the _issuer_ of * the current certificate in order to check for revocation. */ memset((char *)&obj, 0, sizeof(obj)); X509_STORE_CTX_init(store_ctx, crl_store, NULL, NULL); rc = X509_STORE_get_by_subject(store_ctx, X509_LU_CRL, issuer, &obj); X509_STORE_CTX_free(store_ctx); /* calls X509_STORE_CTX_cleanup() */ crl = obj.data.crl; if (rc > 0 && crl != NULL) { /* * Check if the current certificate is revoked by this CRL */ n = sk_X509_REVOKED_num(X509_CRL_get_REVOKED(crl)); for (i = 0; i < n; i++) { revoked = sk_X509_REVOKED_value(X509_CRL_get_REVOKED(crl), i); if (ASN1_INTEGER_cmp(revoked->serialNumber, X509_get_serialNumber(xs)) == 0) { serial = ASN1_INTEGER_get(revoked->serialNumber); cp = X509_NAME_oneline(issuer, NULL, 0); free(cp); X509_STORE_CTX_set_error(ctx, X509_V_ERR_CERT_REVOKED); X509_OBJECT_free_contents(&obj); return 0; } } X509_OBJECT_free_contents(&obj); } return ok; } char * tls_userid_from_client_cert(ssl) SSL * ssl; { static char cn[256]; static char *r = cn; int err; X509 *client_cert; if (client_cert = SSL_get_peer_certificate(ssl)) { /* call the custom function */ err = X509_to_user(client_cert, cn, sizeof(cn)); X509_free(client_cert); if (err) return r = NULL; else return r; } else return r = NULL; } unsigned char ** tls_get_SAN_objs(SSL * ssl, int type) /* returns NULL or an array of malloc'ed objects of type `type' from the server's * subjectAltName, remember to free() them all! */ { #define NUM_SAN_OBJS 64 static unsigned char *objs[NUM_SAN_OBJS]; unsigned char **rv = NULL; X509 *server_cert = NULL; int i, j; X509_EXTENSION *ext = NULL; STACK_OF(GENERAL_NAME) *ialt = NULL; GENERAL_NAME *gen = NULL; memset(objs, 0, sizeof(objs)); if (server_cert = SSL_get_peer_certificate(ssl)) { if ((i = X509_get_ext_by_NID(server_cert, NID_subject_alt_name, -1)) < 0) goto eject; if (!(ext = X509_get_ext(server_cert, i))) goto eject; X509V3_add_standard_extensions(); if (!(ialt = X509V3_EXT_d2i(ext))) goto eject; rv = objs; for (i = 0, j = 0; i < sk_GENERAL_NAME_num(ialt) && j < NUM_SAN_OBJS - 2; i++) { gen = sk_GENERAL_NAME_value(ialt, i); /* The use of V_ASN1_CONTEXT_SPECIFIC is because OpenSSL 0.9.6 defined its * types | V_ASN1_CONTEXT_SPECIFIC. 0.9.7 does not. In case, we are built * with one and linked to the other we use this hack. */ if ((gen->type | V_ASN1_CONTEXT_SPECIFIC) == (type | V_ASN1_CONTEXT_SPECIFIC)) { if (!gen->d.ia5 || !gen->d.ia5->length) break; if (strlen((char *)gen->d.ia5->data) != gen->d.ia5->length) { /* Ignoring IA5String containing null character */ continue; } objs[j] = malloc(gen->d.ia5->length + 1); if (objs[j]) { memcpy(objs[j], gen->d.ia5->data, gen->d.ia5->length); objs[j][gen->d.ia5->length] = 0; j++; } } } X509V3_EXT_cleanup(); } eject: if (ialt) sk_GENERAL_NAME_free(ialt); if (server_cert) X509_free(server_cert); return rv; } static int dNSName_cmp(const char *host, const char *dNSName) { int c1 = 1, c2 = 1, num_comp, rv = -1; char *p, *p1, *p2, *host_copy=NULL, *dNSName_copy=NULL; /* first we count the number of domain name components in both parameters. * they should be equal many, or it's not a match */ p = (char *) host; while (p = strstr(p, ".")) { c1++; p++; } p = (char *) dNSName; while (p = strstr(p, ".")) { c2++; p++; } if (c1 != c2) return -1; num_comp = c1; makestr(&host_copy,host); makestr(&dNSName_copy,dNSName); if (host_copy == NULL || dNSName_copy == NULL) goto eject; /* make substrings by replacing '.' with '\0' */ p = dNSName_copy; while (p = strstr(p, ".")) { *p = '\0'; p++; } p = host_copy; while (p = strstr(p, ".")) { *p = '\0'; p++; } /* compare each component */ p1 = host_copy; p2 = dNSName_copy; for (; num_comp; num_comp--) { if (!ckmatch(p2, p1,0,1)) /* failed match */ goto eject; p1 += strlen(p1) + 1; p2 += strlen(p2) + 1; } /* match ok */ rv = 0; eject: if (dNSName_copy) free(dNSName_copy); if (host_copy) free(host_copy); return rv; } static int show_hostname_warning(char *s1, char *s2) { char prefix[1024]; int ok = 1; setverbosity(); ckmakxmsg(prefix,1024, "Warning: Hostname (\"", s1, "\") does not match server's certificate (\"", s2, "\")", NULL,NULL,NULL,NULL,NULL,NULL,NULL); if (ssl_verify_flag) ok = uq_ok(prefix, "Continue? (Y/N) ", 3, NULL, 0); else if (verbosity) printf(prefix); return(ok); } #ifndef OSF50 #ifndef HPUX10 #ifndef HPUX1100 #ifndef SCO_OSR505 #ifndef OpenBSD #ifndef FREEBSD4 #ifndef NETBSD15 #ifndef LINUX #ifndef AIX41 #ifndef UW7 #ifndef IRIX65 #ifndef SOLARIS9 #ifndef SOLARIS8 #ifndef SOLARIS7 #ifndef MACOSX #ifdef DEC_TCPIP #define inet_aton INET_ATON #endif /* DEC_TCPIP */ #ifndef NO_DCL_INET_ATON static int inet_aton(char * ipaddress, struct in_addr * ia) { struct stringarray * q; union { unsigned long l; unsigned char b[4]; } dummy; q = cksplit(1,0,ipaddress,".","0123456789abcdefACDEF",8,0,0); if (q->a_size == 4) { dummy.b[0] = atoi(q->a_head[1]); dummy.b[1] = atoi(q->a_head[2]); dummy.b[2] = atoi(q->a_head[3]); dummy.b[3] = atoi(q->a_head[4]); ia->s_addr = dummy.l; return(ia->s_addr != 0); } return(0); } #endif /* NO_DCL_INET_ATON */ #endif /* MACOSX */ #endif /* SOLARIS7 */ #endif /* SOLARIS8 */ #endif /* SOLARIS9 */ #endif /* IRIX65 */ #endif /* UW7 */ #endif /* AIX41 */ #endif /* LINUX */ #endif /* NETBSD15 */ #endif /* FREEBSD4 */ #endif /* OpenBSD */ #endif /* SCO_OSR505 */ #endif /* HPUX1100 */ #endif /* HPUX10 */ #endif /* OSF50 */ int ssl_check_server_name(SSL * ssl, char * hostname) /* returns 0 if hostname and server's cert matches, else -1 */ { char * commonName; unsigned char ** dNSName; unsigned char ** ipAddress; struct in_addr ia; int rv; setverbosity(); if (verbosity && !inserver) { if (dNSName = tls_get_SAN_objs(ssl,GEN_DNS)) { int i = 0; for (i = 0; dNSName[i]; i++) { printf("Certificate[0] altSubjectName DNS=%s\r\n",dNSName[i]); free(dNSName[i]); } } if (ipAddress = tls_get_SAN_objs(ssl,GEN_IPADD)) { int i = 0; char *server_ip; struct in_addr ia; for (i = 0; ipAddress[i]; i++) { if (ipAddress[i]) { ia.s_addr = *(unsigned long *)ipAddress[i]; server_ip = inet_ntoa(ia); printf("Certificate[0] altSubjectName IPAddr=%s\r\n",server_ip); } free(ipAddress[i]); } /* ipAddress points to a static - don't free */ } if (dNSName = tls_get_SAN_objs(ssl,GEN_EMAIL)) { int i = 0; for (i = 0; dNSName[i]; i++) { printf("Certificate[0] altSubjectName Email=%s\r\n",dNSName[i]); free(dNSName[i]); } } if (dNSName = tls_get_SAN_objs(ssl,GEN_URI)) { int i = 0; for (i = 0; dNSName[i]; i++) { printf("Certificate[0] altSubjectName URI=%s\r\n",dNSName[i]); free(dNSName[i]); } } if (dNSName = tls_get_SAN_objs(ssl,GEN_OTHERNAME)) { int i = 0; for (i = 0; dNSName[i]; i++) { printf("Certificate[0] altSubjectName Other=%s\r\n",dNSName[i]); free(dNSName[i]); } } } /* first we check if `hostname' is in fact an ip address */ if (inet_aton(hostname, &ia)) { ipAddress = tls_get_SAN_objs(ssl,GEN_IPADD); if (ipAddress) { int i = 0; char *server_ip = "UNKNOWN"; for (i = 0; ipAddress[i]; i++) if (*(unsigned long *)ipAddress[i] == ia.s_addr) return 0; if (ipAddress[i - 1]) { ia.s_addr = *(unsigned long *)ipAddress[i - 1]; server_ip = inet_ntoa(ia); } rv = show_hostname_warning(hostname, server_ip) ? 0 : -1; for (i = 0; ipAddress[i]; i++) free(ipAddress[i]); } else { rv = show_hostname_warning(hostname, "NO IP IN CERT") ? 0 : -1; } return(rv); } /* look for dNSName(s) in subjectAltName in the server's certificate */ dNSName = tls_get_SAN_objs(ssl,GEN_DNS); if (dNSName) { int i = 0; for (i = 0; dNSName[i]; i++) { if (!dNSName_cmp(hostname,(char *)dNSName[i])) return 0; } rv = show_hostname_warning(hostname, (char *)((dNSName[i - 1] == NULL) ? (char *)"UNKNOWN" : (char *)dNSName[i - 1])) ? 0 : -1; for (i = 0; dNSName[i]; i++) free(dNSName[i]); return rv; } else if ((commonName = ssl_get_commonName(ssl))) { /* so the server didn't have any dNSName's, check the commonName */ if (!dNSName_cmp(hostname, commonName)) return 0; else return (show_hostname_warning(hostname, commonName) ? 0 : -1); } return -1; } /* Is 'user' authorized to access the system without a login */ int tls_is_user_valid(SSL * ssl, const char *user) { X509 *client_cert; int r = 0; if ( !ssl || !user || !user[0] ) return(0); if (!(client_cert = SSL_get_peer_certificate(ssl))) return 0; /* Use user supplied function */ r = X509_userok(client_cert,user); X509_free(client_cert); return r; } int tls_is_anon(int x) { char buf[128]; SSL_CIPHER * cipher; SSL * ssl = NULL; switch ( x ) { #ifndef NOFTP #ifndef SYSFTP case 1: /* ftp command */ if ( ssl_ftp_active_flag ) ssl = ssl_ftp_con; else return(0); break; case 2: /* ftp data */ if ( ssl_ftp_data_active_flag ) ssl = ssl_ftp_data_con; else return(0); break; #endif /* SYSFTP */ #endif /* NOFTP */ default: if (tls_active_flag) ssl = tls_con; else if (ssl_active_flag) ssl = ssl_con; else return(0); } cipher = SSL_get_current_cipher(ssl); if (SSL_CIPHER_description(cipher,buf,sizeof(buf))) { if (ckindex("Au=None",buf,0,0,0) != 0) return(1); /* anonymous */ return(0); /* known */ } else { /* could not get cipher description. Assume anonymous */ return(1); } } int tls_is_krb5(int x) { char buf[128]; SSL_CIPHER * cipher; SSL * ssl = NULL; switch ( x ) { #ifndef NOFTP #ifndef SYSFTP case 1: /* ftp command */ if ( ssl_ftp_active_flag ) ssl = ssl_ftp_con; else return(0); break; case 2: /* ftp data */ if ( ssl_ftp_data_active_flag ) ssl = ssl_ftp_data_con; else return(0); break; #endif /* SYSFTP */ #endif /* NOFTP */ #ifndef NOHTTP case 3: if ( tls_http_active_flag ) ssl = tls_http_con; break; #endif /* NOHTTP */ default: if (tls_active_flag) ssl = tls_con; else if (ssl_active_flag) ssl = ssl_con; else return(0); } cipher = SSL_get_current_cipher(ssl); if (cipher && SSL_CIPHER_description(cipher,buf,sizeof(buf))) { if (ckindex("Au=KRB5",buf,0,0,0) != 0) return(1); /* krb5 */ } return(0); /* not */ } int ssl_get_client_finished(char *buf, int count) { #ifdef NO_GET_FINISHED return(0); #else if (sstelnet || tcp_incoming) { return(SSL_get_peer_finished(ssl_active_flag?ssl_con:tls_con, buf,count)); } else { return(SSL_get_finished(ssl_active_flag?ssl_con:tls_con, buf,count)); } #endif /* NO_GET_FINISHED */ } int ssl_get_server_finished(char *buf, int count) { #ifdef NO_GET_FINISHED return(0); #else if (sstelnet || tcp_incoming) { return(SSL_get_finished(ssl_active_flag?ssl_con:tls_con, buf,count)); } else { return(SSL_get_peer_finished(ssl_active_flag?ssl_con:tls_con, buf,count)); } #endif /* NO_GET_FINISHED */ } #ifdef CK_AUTHENTICATION int #ifdef CK_ANSIC ssl_reply(int how, unsigned char *data, int cnt) #else ssl_reply(how,data,cnt) int how; unsigned char *data; int cnt; #endif { char * str=NULL; setverbosity(); data += 4; /* Point to status byte */ cnt -= 4; if(cnt-- < 1) { auth_finished(AUTH_REJECT); return AUTH_FAILURE; } switch(*data++) { case SSL_ACCEPT: if (tn_deb || debses) tn_debug("[SSL - handshake starting]"); else if ( verbosity ) printf("[SSL - handshake starting]\r\n"); debug(F110,"ssl_reply","[SSL - handshake starting]",0); /* right ... now we drop into the SSL library */ if (!ssl_only_flag) { if (ssl_dummy_flag) { if (tn_deb || debses) tn_debug("[SSL - Dummy Connected]"); else if ( verbosity ) { printf("[SSL - Dummy Connected]\r\n"); } debug(F110,"ssl_reply","[SSL - Dummy Connected]",0); auth_finished(AUTH_UNKNOWN); accept_complete = 1; return AUTH_SUCCESS; } if (SSL_connect(ssl_con) <= 0) { int len; if (tn_deb || debses) { tn_debug("[SSL - FAILED]"); ERR_print_errors(bio_err); len = BIO_read(bio_err,ssl_err,SSL_ERR_BFSZ); ssl_err[len < SSL_ERR_BFSZ ? len : SSL_ERR_BFSZ] = '\0'; printf(ssl_err); } else if ( verbosity ) { printf("[SSL - FAILED]\r\n"); ERR_print_errors(bio_err); len = BIO_read(bio_err,ssl_err,SSL_ERR_BFSZ); ssl_err[len < SSL_ERR_BFSZ ? len : SSL_ERR_BFSZ] = '\0'; printf(ssl_err); } debug(F110,"ssl_reply","[SSL - FAILED]",0); auth_finished(AUTH_REJECT); ttclos(0); return AUTH_FAILURE; } else { if (tn_deb || debses) tn_debug("[SSL - OK]"); else if ( verbosity ) { printf("[SSL - OK]\r\n"); } debug(F110,"ssl_reply","[SSL - OK]",0); ssl_active_flag = 1; ssl_display_connect_details(ssl_con,0,verbosity); } } auth_finished(AUTH_UNKNOWN); accept_complete = 1; break; case SSL_REJECT: if (tn_deb || debses) { tn_debug( "[SSL - failed to switch on SSL - trying plaintext login]"); } else if ( verbosity ) { printf("[SSL - failed to switch on SSL]\r\n"); printf("Trying plaintext login:\r\n"); } debug(F110,"ssl_reply","[SSL - failed to switch on SSL]",0); auth_finished(AUTH_REJECT); return AUTH_FAILURE; default: return AUTH_FAILURE; } return AUTH_SUCCESS; } int #ifdef CK_ANSIC ssl_is(unsigned char *data, int cnt) #else ssl_is(data,cnt) unsigned char *data; int cnt; #endif { if ((cnt -= 4) < 1) return AUTH_FAILURE; setverbosity(); data += 4; switch(*data++) { case SSL_START: /* server starts the SSL stuff now ... */ if (!ssl_only_flag) { if ( !tls_load_certs(ssl_ctx,ssl_con,1) ) { auth_finished(AUTH_REJECT); return AUTH_FAILURE; } if (tn_deb || debses) tn_debug("[SSL - handshake starting]"); else if ( verbosity ) printf("[SSL - handshake starting]\r\n"); debug(F110,"ssl_is","[SSL - handshake starting]",0); SendSSLAuthSB(SSL_ACCEPT, (void *)0, 0); auth_ssl_valid = 1; if (ssl_dummy_flag) { if (tn_deb || debses) tn_debug("[SSL - Dummy Connected]"); else if ( verbosity ) { printf("[SSL - Dummy Connected]\r\n"); } debug(F110,"ssl_is","[SSL - Dummy Connected]",0); accept_complete = 1; auth_finished(AUTH_UNKNOWN); return AUTH_SUCCESS; } if (SSL_accept(ssl_con) <= 0) { char errbuf[1024]; sprintf(errbuf,"[SSL - SSL_accept error: %s", ERR_error_string(ERR_get_error(),NULL)); if (tn_deb || debses) tn_debug(errbuf); else if ( ssl_debug_flag ) printf("%s\r\n",errbuf); else if ( verbosity ) printf("[SSL - SSL_accept error]\r\n"); debug(F110,"ssl_is",errbuf,0); auth_finished(AUTH_REJECT); ttclos(0); return AUTH_FAILURE; } if (tn_deb || debses) tn_debug("[SSL - OK]"); else if ( verbosity ) { printf("[SSL - OK]\r\n"); } debug(F110,"ssl_is","[SSL - OK]",0); ssl_active_flag = 1; ssl_display_connect_details(ssl_con,1,verbosity); /* now check to see that we got exactly what we * wanted from the caller ... if a certificate is * required then we make 100% sure that we were * given one during the handshake (as it is an optional * part of SSL) */ #ifdef SSL_KRB5 if ( tls_is_krb5(0) ) { if (ssl_con->kssl_ctx->client_princ) debug(F110,"ssl_is KRB5",ssl_con->kssl_ctx->client_princ,0); } else #endif /* SSL_KRB5 */ if (ssl_verify_flag & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) { X509 * peer = SSL_get_peer_certificate(ssl_con); if (peer == NULL) { if (tn_deb || debses) tn_debug("[SSL - peer check failed]"); else if (ssl_debug_flag) printf("[SSL - peer check failed]\r\n"); debug(F110,"ssl_is","[SSL - peer check failed]",0); /* LOGGING REQUIRED HERE! */ auth_finished(AUTH_REJECT); return AUTH_FAILURE; } } auth_finished(AUTH_UNKNOWN); accept_complete = 1; } break; default: SendSSLAuthSB(SSL_REJECT, (void *) "Unknown option received", -1); if (tn_deb || debses) tn_debug("[SSL - Unknown option received]"); else printf("Unknown SSL option %d\r\n", data[-1]); debug(F111,"ssl_is","[SSL - Unknown option received]",data[-1]); auth_ssl_valid = 0; auth_finished(AUTH_REJECT); return(AUTH_FAILURE); } return AUTH_SUCCESS; } #endif /* CK_AUTHENTICATION */ int ck_tn_tls_negotiate(VOID) { X509 * peer = NULL; char str[256], *uid=NULL; extern int sstelnet; if ( !ck_ssleay_is_installed() ) return(-1); setverbosity(); if (sstelnet) { /* server starts the TLS stuff now ... */ if (!tls_only_flag) { if ( !tls_load_certs(tls_ctx,tls_con,1) ) { auth_finished(AUTH_REJECT); return -1; } if (tn_deb || debses) tn_debug("[TLS - handshake starting]"); else if ( verbosity ) printf("[TLS - handshake starting]\r\n"); debug(F110,"ck_tn_tls_negotiate","[TLS - handshake starting]",0); if (ssl_dummy_flag) { if (tn_deb || debses) tn_debug("[TLS - Dummy Connected]"); else if ( verbosity ) { printf("[TLS - Dummy Connected]\r\n"); } debug(F110,"ck_tn_tls_negotiate","[TLS - Dummy Connected]",0); accept_complete = 1; auth_finished(AUTH_REJECT); return 0; } if (SSL_accept(tls_con) <= 0) { char errbuf[1024]; sprintf(errbuf,"[TLS - SSL_accept error: %s", ERR_error_string(ERR_get_error(),NULL)); if (tn_deb || debses) tn_debug(errbuf); else if ( ssl_debug_flag ) printf("%s\r\n",errbuf); else if ( verbosity ) printf("[TLS - SSL_accept error]\r\n"); debug(F110,"ck_tn_tls_negotiate",errbuf,0); auth_finished(AUTH_REJECT); return -1; } if (tn_deb || debses) tn_debug("[TLS - OK]"); else if ( verbosity ) { printf("[TLS - OK]\r\n"); } debug(F110,"ck_tn_tls_negotiate","[TLS - OK]",0); tls_active_flag = 1; ssl_display_connect_details(tls_con,1,verbosity); #ifdef SSL_KRB5 if ( tls_is_krb5(0) ) { if (tls_con->kssl_ctx->client_princ) { char *p; ckstrncpy(szUserNameAuthenticated, tls_con->kssl_ctx->client_princ, UIDBUFLEN); ckstrncpy(szUserNameRequested, tls_con->kssl_ctx->client_princ, UIDBUFLEN); for ( p = szUserNameRequested; *p ; p++ ) { if ( *p == '@' || *p == '/' ) { *p = '\0'; break; } } } else { szUserNameRequested[0] = '\0'; szUserNameAuthenticated[0] = '\0'; } #ifdef CK_LOGIN if (zvuser(szUserNameRequested)) auth_finished(AUTH_VALID); else #endif /* CK_LOGIN */ auth_finished(AUTH_USER); } else #endif /* SSL_KRB5 */ { /* now check to see that we got exactly what we * wanted from the caller ... if a certificate is * required then we make 100% sure that we were * given one during the handshake (as it is an optional * part of TLS) */ peer=SSL_get_peer_certificate(tls_con); if (peer == NULL) { debug(F100,"SSL_get_peer_certificate() == NULL","",0); auth_finished(AUTH_REJECT); if (ssl_verify_flag & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) { if (tn_deb || debses) tn_debug("[TLS - peer check failed]"); else if (ssl_debug_flag) { printf("[TLS - peer check failed]\r\n"); } debug(F110, "ck_tn_tls_negotiate", "[TLS - peer check failed]", 0 ); /* LOGGING REQUIRED HERE! */ return -1; } } else { debug(F100,"SSL_get_peer_certificate() != NULL","",0); X509_NAME_get_text_by_NID(X509_get_subject_name(peer), NID_commonName,str, 256 ); if ( verbosity ) printf("[TLS - commonName=%s]\r\n",str); X509_NAME_get_text_by_NID(X509_get_subject_name(peer), #ifndef NID_x500UniqueIdentifier NID_uniqueIdentifier, #else NID_x500UniqueIdentifier, #endif str, 256 ); if ( verbosity ) printf("[TLS - uniqueIdentifier=%s]\r\n",str); /* Try to determine user name */ uid = tls_userid_from_client_cert(tls_con); if ( uid ) { /* This code is very questionable. * How should it behave? * The client has presented a certificate that * contains a username. We have validated the * certificate but we do not automatically * log the user in unless there is a .tlslogin * file. */ ckstrncpy(szUserNameRequested,uid,UIDBUFLEN); #ifdef CK_LOGIN if (zvuser(uid)) auth_finished(AUTH_VALID); else #endif /* CK_LOGIN */ auth_finished(AUTH_USER); } else { szUserNameRequested[0] = '\0'; auth_finished(AUTH_REJECT); } } } } } else { char * str=NULL; if (tn_deb || debses) tn_debug("[TLS - handshake starting]"); else if ( verbosity ) printf("[TLS - handshake starting]\r\n"); debug(F110,"ck_tn_tls_negotiate","[TLS - handshake starting]",0); /* right ... now we drop into the SSL library */ if (!tls_only_flag) { char *subject=NULL, *issuer=NULL, *commonName=NULL, *dNSName=NULL; if (ssl_dummy_flag) { if (tn_deb || debses) tn_debug("[TLS - Dummy Connected]"); else if ( verbosity ) { printf("[TLS - Dummy Connected]\r\n"); } debug(F110,"ck_tn_tls_negotiate","[TLS - Dummy Connected]",0); auth_finished(AUTH_REJECT); accept_complete = 1; return 0; } #ifndef USE_CERT_CB if (!tls_load_certs(tls_ctx,tls_con,0)) return(-1); #endif /* USE_CERT_CB */ if (SSL_connect(tls_con) <= 0) { int len; if (tn_deb || debses) { tn_debug("[TLS - FAILED]"); ERR_print_errors(bio_err); len = BIO_read(bio_err,ssl_err,SSL_ERR_BFSZ); ssl_err[len < SSL_ERR_BFSZ ? len : SSL_ERR_BFSZ] = '\0'; printf(ssl_err); } else if ( verbosity ) { printf("[TLS - FAILED]\r\n"); ERR_print_errors(bio_err); len = BIO_read(bio_err,ssl_err,SSL_ERR_BFSZ); ssl_err[len < SSL_ERR_BFSZ ? len : SSL_ERR_BFSZ] = '\0'; printf(ssl_err); } debug(F110,"ck_tn_tls_negotiate","[TLS - FAILED]",0); auth_finished(AUTH_REJECT); return -1; } tls_active_flag = 1; if ( !ssl_certsok_flag && (ssl_verify_flag & SSL_VERIFY_PEER) && !tls_is_krb5(0)) { char prmpt[1024]; subject = ssl_get_subject_name(tls_con); if (!subject) { if (ssl_verify_flag & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) { if (tn_deb || debses) tn_debug("[TLS - FAILED]"); else if ( verbosity ) printf("[TLS - FAILED]\r\n"); debug(F110,"ck_tn_tls_negotiate","[TLS - FAILED]",0); auth_finished(AUTH_REJECT); return -1; } else { int ok; ok = uq_ok("Warning: Server didn't provide a certificate", "Continue? (Y/N)", 3, NULL, 0); if (!ok) { if (tn_deb || debses) tn_debug("[TLS - FAILED]"); else if ( verbosity ) printf("[TLS - FAILED]\r\n"); debug(F110, "ck_tn_tls_negotiate","[TLS - FAILED]",0); auth_finished(AUTH_REJECT); return -1; } } } else if (ssl_check_server_name(tls_con, szHostName)) { if (tn_deb || debses) tn_debug("[TLS - FAILED]"); else if ( verbosity ) printf("[TLS - FAILED]\r\n"); debug(F110, "ck_tn_tls_negotiate","[TLS - FAILED]",0); auth_finished(AUTH_REJECT); return -1; } } if ( ssl_debug_flag && ssl_finished_messages) { char msg[32]; int i, len=32; extern char tn_msg[], hexbuf[]; tn_msg[0] = '\0'; len = ssl_get_client_finished(msg,len); if ( len > 0 ) { for ( i=0;i 0 ) { for ( i=0;i 0) return 0; /* END EXAMPLE */ #else /* X509_UID_TO_USER */ #ifdef X509_SUBJECT_ALT_NAME_TO_USER /* BEGIN EXAMPLE */ int i; X509_EXTENSION *ext = NULL; STACK_OF(GENERAL_NAME) *ialt = NULL; GENERAL_NAME *gen = NULL; char email[256]; if (!(peer_cert && userid) || len <= 0) return -1; userid[0] = '\0'; email[0] = '\0'; debug(F110,"X509_to_user() subject", X509_NAME_oneline(X509_get_subject_name(peer_cert),NULL,0),0); if ((i = X509_get_ext_by_NID(peer_cert, NID_subject_alt_name, -1))<0) return -1; if (!(ext = X509_get_ext(peer_cert, i))) return -1; X509V3_add_standard_extensions(); if (!(ialt = X509V3_EXT_d2i(ext))) return -1; for (i = 0; i < sk_GENERAL_NAME_num(ialt); i++) { gen = sk_GENERAL_NAME_value(ialt, i); if (gen->type == GEN_DNS) { if (!gen->d.ia5 || !gen->d.ia5->length) break; if (strlen(gen->d.ia5->data) != gen->d.ia5->length) { /* Ignoring IA5String containing null character */ continue; } if ( gen->d.ia5->length + 1 > sizeof(email) ) { goto cleanup; } memcpy(email, gen->d.ia5->data, gen->d.ia5->length); email[gen->d.ia5->length] = 0; break; } } cleanup: X509V3_EXT_cleanup(); if (ialt) sk_GENERAL_NAME_free(ialt); debug(F110,"X509_to_user() email",email,0); if ( email[0] ) { char * domain = NULL; /* Find domain */ for ( i=0 ; email[i] ; i++ ) { if ( email[i] == '@' ) { email[i] = '\0'; domain = &email[i+1]; break; } } if ( domain ) { /* XXX - Put code to Verify domain here */ if ( /* domain is okay */ 1 ) ckstrncpy(userid,email,len); } } return(userid[0] ? 0 : -1); /* END EXAMPLE */ #endif /* X509_SUBJECT_ALT_NAME_TO_USER */ #endif /* X509_UID_TO_USER */ return -1; } /* The following function should be replaced by institution specific */ /* code that will determine whether or not the combination of the */ /* provided X509 certificate and username is valid for automatic */ /* login. Whereas X509_to_user() is used to provide authentication */ /* of the user, the X509_userok() function is used to provide */ /* authorization. The certificate passed into X509_userok() does */ /* need to map to a userid; nor would the userid it would map to */ /* need to match the userid provided to the function. There are */ /* numerous circumstances in which it is beneficial to have the ability */ /* for multiple users to gain access to a common account such as */ /* 'root' on Unix; or a class account on a web server. In Unix we */ /* implement this capability with the ~userid/.tlslogin file which */ /* a list of X509 certificates which may be used to access the */ /* account 'userid'. */ /* X509_to_user() returns 0 if access is denied; 1 is access is permitted */ int X509_userok(X509 * peer_cert, const char * userid) { #ifndef VMS /* check if clients cert is in "user"'s ~/.tlslogin file */ char buf[512]; int r = 0; FILE *fp; struct passwd *pwd; X509 *file_cert; if ( peer_cert == NULL ) return(0); if (!(pwd = getpwnam(userid))) return 0; if (strlen(pwd->pw_dir) > 500) return(0); sprintf(buf, "%s/.tlslogin", pwd->pw_dir); if (!(fp = fopen(buf, "r"))) return 0; while (!r && (file_cert = PEM_read_X509(fp, NULL, NULL, NULL))) { if (!ASN1_STRING_cmp(peer_cert->signature, file_cert->signature)) r = 1; X509_free(file_cert); } fclose(fp); return(r); #else /* VMS */ /* Need to implement an appropriate function for VMS */ return(0); #endif /* VMS */ } #endif /* OS2 */ #endif /* CK_SSL */ ck_ssl.h0000644000015300001460000001071211575444442011323 0ustar fdckermit/* C K _ S S L . H -- OpenSSL Interface Header for C-Kermit Copyright (C) 1985, 2005, Trustees of Columbia University in the City of New York. All rights reserved. See the C-Kermit COPYING.TXT file or the copyright text in the ckcmai.c module for disclaimer and permissions. Author: Jeffrey E Altman (jaltman@secure-endpoints.com) Secure Endpoints Inc., New York City */ #ifdef CK_SSL #ifndef CK_ANSIC #define NOPROTO #endif /* CK_ANSIC */ #ifdef COMMENT /* Not for C-Kermit 7.1 */ #ifdef KRB5 #ifndef NOSSLK5 #ifndef SSL_KRB5 #define SSL_KRB5 #endif /* SSL_KRB5 */ #endif /* NOSSLK5 */ #endif /* KRB5 */ #endif /* COMMENT */ #ifdef OS2 #ifndef ZLIB #define ZLIB #endif /* ZLIB */ #endif /* OS2 */ #ifdef ZLIB #include #endif /* ZLIB */ /* We place the following to avoid loading openssl/mdc2.h since it * relies on the OpenSSL des.h. Since we do not need the MDC2 * definitions there is no reason to have it included by openssl/evp.h */ #define OPENSSL_NO_MDC2 #ifdef OPENSSL_100 #define OPENSSL_098 #endif /* OPENSSL_100 */ #ifdef OPENSSL_098 #define OPENSSL_097 #endif /* OPENSSL_098 */ #ifdef CK_DES #include #endif /* CK_DES */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef SSL_KRB5 #include #endif /* SSL_KRB5 */ extern BIO *bio_err; extern SSL *ssl_con; extern SSL_CTX *ssl_ctx; extern int ssl_debug_flag; extern int ssl_only_flag; extern int ssl_active_flag; extern int ssl_verify_flag; extern int ssl_verbose_flag; extern int ssl_certsok_flag; extern int ssl_dummy_flag; extern int ssl_verify_depth; extern char *ssl_rsa_cert_file; extern char *ssl_rsa_cert_chain_file; extern char *ssl_rsa_key_file; extern char *ssl_dsa_cert_file; extern char *ssl_dsa_cert_chain_file; extern char *ssl_dh_key_file; extern char *ssl_cipher_list; extern char *ssl_crl_file; extern char *ssl_crl_dir; extern char *ssl_verify_file; extern char *ssl_verify_dir; extern char *ssl_dh_param_file; extern char *ssl_rnd_file; extern SSL_CTX *tls_ctx; extern SSL *tls_con; extern int tls_only_flag; extern int tls_active_flag; extern int x509_cert_valid; extern X509_STORE *crl_store; extern int ssl_raw_flag; extern int tls_raw_flag; #ifndef NOHTTP extern SSL_CTX *tls_http_ctx; extern SSL *tls_http_con; extern int tls_http_active_flag; #endif /* NOHTTP */ extern int ssl_initialized; _PROTOTYP(VOID ssl_once_init,(void)); _PROTOTYP(int ssl_tn_init,(int)); _PROTOTYP(int ssl_http_init,(char *)); _PROTOTYP(int ck_ssl_http_client,(int,char *)); _PROTOTYP(int ssl_display_connect_details,(SSL *,int,int)); _PROTOTYP(int ssl_server_verify_callback,(int, X509_STORE_CTX *)); _PROTOTYP(int ssl_client_verify_callback,(int, X509_STORE_CTX *)); _PROTOTYP(int ssl_reply,(int, unsigned char *, int)); _PROTOTYP(int ssl_is,(unsigned char *, int)); _PROTOTYP(int ck_ssl_incoming,(int)); _PROTOTYP(int ck_ssl_outgoing,(int)); _PROTOTYP(int tls_is_user_valid,(SSL *, const char *)); _PROTOTYP(char * ssl_get_dnsName,(SSL *)); _PROTOTYP(char * ssl_get_commonName,(SSL *)); _PROTOTYP(char * ssl_get_issuer_name,(SSL *)); _PROTOTYP(char * ssl_get_subject_name,(SSL *)); _PROTOTYP(int ssl_get_client_finished,(char *, int)); _PROTOTYP(int ssl_get_server_finished,(char *, int)); _PROTOTYP(int ssl_passwd_callback,(char *, int, int, VOID *)); _PROTOTYP(VOID ssl_client_info_callback,(const SSL *,int, int)); _PROTOTYP(int ssl_anonymous_cipher,(SSL * ssl)); _PROTOTYP(int tls_load_certs,(SSL_CTX * ctx, SSL * con, int server)); _PROTOTYP(int ssl_verify_crl,(int, X509_STORE_CTX *)); _PROTOTYP(int tls_is_krb5,(int)); _PROTOTYP(int X509_userok,(X509 *,const char *)); _PROTOTYP(int ck_X509_save_cert_to_user_store,(X509 *)); /* SMS 2007/02/15 */ _PROTOTYP(int ssl_check_server_name,(SSL * ssl, char * hostname)); #ifdef OS2 #include "ckosslc.h" #include "ckossl.h" #endif /* OS2 */ #define SSL_CLIENT 0 #define SSL_SERVER 1 #define SSL_HTTP 2 #define SSL_ERR_BFSZ 4096 #ifdef SSL_KRB5 #define DEFAULT_CIPHER_LIST "HIGH:MEDIUM:LOW:+KRB5:+ADH:+EXP" #else #define DEFAULT_CIPHER_LIST "HIGH:MEDIUM:LOW:+ADH:+EXP" #endif /* SSL_KRB5 */ #endif /* CK_SSL */ ckcasc.h0000644000015300001460000000533411266111232011261 0ustar fdckermit/* File CKCASC.H Mnemonics for ASCII control characters (and Space) for use with C-Kermit. */ /* Author: Frank da Cruz (fdc@columbia.edu). Columbia University Academic Information Systems, New York City. Copyright (C) 1985, 2009, Trustees of Columbia University in the City of New York. All rights reserved. See the C-Kermit COPYING.TXT file or the copyright text in the ckcmai.c module for disclaimer and permissions. */ #ifndef CKCASC_H #define CKCASC_H #define NUL '\0' /* Null Ctrl-@*/ #define SOH 1 /* Start of header Ctrl-A */ #define STX 2 /* Ctrl-B */ #define ETX 3 /* Ctrl-C */ #define EOT 4 /* Ctrl-D */ #define ENQ 5 /* ENQ Ctrl-E */ #define ACK 6 /* Ctrl-F */ #define BEL 7 /* Bell (Beep) Ctrl-G */ #define BS 8 /* Backspace Ctrl-H */ #define HT 9 /* Horizontal Tab Ctrl-I */ #define LF 10 /* Linefeed Ctrl-J */ #define VT 11 /* Vertical Tab Ctrl-K */ #define NL '\n' /* Newline */ #define FF 12 /* Formfeed Ctrl-L */ #define CR 13 /* Carriage Return Ctrl-M */ #define SO 14 /* Shift Out Ctrl-N */ #define SI 15 /* Shift In Ctrl-O */ #define DLE 16 /* Datalink Escape Ctrl-P */ #define XON 17 /* XON Ctrl-Q */ #define DC1 17 #define DC2 18 /* Ctrl-R */ #define XOFF 19 /* XOFF Ctrl-S */ #define DC3 19 #define DC4 20 /* Ctrl-T */ #define NAK 21 /* Ctrl-U */ #define SYN 22 /* SYN, Ctrl-V */ #define ETB 23 /* Ctrl-W */ #define CAN 24 /* CAN, Ctrl-X */ #define XEM 25 /* Ctrl-Y (was EM but conflicts with OpenSSL) */ #define SUB 26 /* SUB Ctrl-Z */ #define ESC 27 /* Escape Ctrl-[ */ #define XFS 28 /* Field Separator, Ctrl-Backslash */ #define XGS 29 /* Group Separator, Ctrl-Rightbracket */ #define XRS 30 /* Record Separator, Ctrl-Circumflex */ #define US 31 /* Unit Separator, Ctrl-Underscore */ #define SP 32 /* Space */ #define DEL 127 /* Delete (Rubout) */ #define RUB 127 /* Delete (Rubout) */ #ifdef OS2 /* These are needed in OS/2, so let's not cause any unnecessary conflicts. */ #define _CSI 0233 /* 8-bit Control Sequence Introducer */ #define _SS2 0216 /* 8-bit Single Shift 2 */ #define _SS3 0217 /* 8-bit Single Shift 3 */ #define _DCS 0220 /* 8-bit Device Control String Introducer */ #define _ST8 0234 /* 8-bit String Terminator */ #define _OSC 0235 /* 8-bit Operating System Command */ #define _PM8 0236 /* 8-bit Privacy Message */ #define _APC 0237 /* 8-bit Application Program Command */ #endif /* OS2 */ #endif /* CKCASC_H */ ckcdeb.h0000644000015300001460000042577311434474020011266 0ustar fdckermit/* C K C D E B . H */ /* Mon Aug 23 09:22:05 2010 NOTE TO CONTRIBUTORS: This file, and all the other C-Kermit files, must be compatible with C preprocessors that support only #ifdef, #else, #endif, #define, and #undef. Please do not use #if, logical operators, or other later-model preprocessor features in any of the portable C-Kermit modules. You can, of course, use these constructions in platform-specific modules when you know they are supported. */ /* This file is included by all C-Kermit modules, including the modules that aren't specific to Kermit (like the command parser and the ck?tio and ck?fio modules). It should be included BEFORE any other C-Kermit header files. It specifies format codes for debug(), tlog(), and similar functions, and includes any necessary definitions to be used by all C-Kermit modules, and also includes some feature selection compile-time switches, and also system- or compiler-dependent definitions, plus #includes and prototypes required by all C-Kermit modules. */ /* Author: Frank da Cruz , Columbia University Academic Information Systems, New York City. Copyright (C) 1985, 2010, Trustees of Columbia University in the City of New York. All rights reserved. See the C-Kermit COPYING.TXT file or the copyright text in the ckcmai.c module for disclaimer and permissions. */ /* Etymology: The name of this file means "C-Kermit Common-C-Language Debugging Header", because originally it contained only the formats (F000-F111) for the debug() and tlog() functions. Since then it has grown to include all material required by all other C-Kermit modules, including the non-Kermit specific ones. In other words, this is the one header file that is guaranteed to be included by all C-Kermit source modules. */ #ifndef CKCDEB_H /* Don't include me more than once. */ #define CKCDEB_H #ifdef OS2 #include "ckoker.h" #else /* OS2 */ /* Unsigned numbers */ #ifndef USHORT #define USHORT unsigned short #endif /* USHORT */ #ifndef UINT #define UINT unsigned int #endif /* UINT */ #ifndef ULONG #define ULONG unsigned long #endif /* ULONG */ #endif /* OS2 */ #ifdef MACOSX10 /* Mac OS X 1.0 */ #ifndef MACOSX /* implies Mac OS X */ #define MACOSX #endif /* MACOSX */ #endif /* MACOSX10 */ #ifdef MACOSX /* Mac OS X */ #ifndef BSD44 /* implies 4.4 BSD */ #define BSD44 #endif /* BSD44 */ #endif /* MACOSX */ #ifdef SCO_OSR505 /* SCO 3.2v5.0.5 */ #ifndef SCO_OSR504 /* implies SCO 3.2v5.0.4 */ #define SCO_OSR504 #endif /* SCO_OSR504 */ #endif /* SCO_OSR505 */ #ifdef SCO_OSR504 /* SCO 3.2v5.0.4 */ #ifndef CK_SCOV5 /* implies SCO 3.2v5.0 */ #define CK_SCOV5 #endif /* CK_SCOV5 */ #include /* To sidestep header-file mess */ #endif /* SCO_OSR504 */ #ifdef CK_SCOV5 #ifndef ANYSCO #define ANYSCO #endif /* ANYSCO */ #endif /* CK_SCOV5 */ #ifdef UNIXWARE #ifndef ANYSCO #define ANYSCO #endif /* ANYSCO */ #endif /* UNIXWARE */ #ifndef MINIX /* Minix versions */ #ifdef MINIX315 #define MINIX #else #ifdef MINIX3 #define MINIX #else #ifdef MINIX2 #define MINIX #endif /* MINIX2 */ #endif /* MINIX3 */ #endif /* MINIX315 */ #endif /* MINIX */ #ifdef CK_SCO32V4 /* SCO 3.2v4 */ #ifndef ANYSCO #define ANYSCO #endif /* ANYSCO */ #ifndef XENIX #define XENIX #endif /* XENIX */ #ifndef SVR3 #define SVR3 #endif /* SVR3 */ #ifndef DIRENT #define DIRENT #endif /* DIRENT */ #ifndef RENAME #define RENAME #endif /* RENAME */ #ifndef SVR3JC #define SVR3JC #endif /* SVR3JC */ #ifndef CK_RTSCTS #define CK_RTSCTS #endif /* CK_RTSCTS */ #ifndef PID_T #define PID_T pid_t #endif /* PID_T */ #ifndef PWID_T #define PWID_T int #endif /* PWID_T */ #endif /* CK_SCO32V4 */ #ifdef NOICP /* If no command parser */ #ifndef NOSPL /* Then no script language either */ #define NOSPL #endif /* NOSPL */ #ifndef NOCSETS /* Or characer sets */ #define NOCSETS #endif /* NOCSETS */ #ifndef NOFTP /* Or FTP client */ #define NOFTP #endif /* NOFTP */ #endif /* NOICP */ /* Built-in makefile entries */ #ifdef SOLARIS11 /* Solaris 11 implies 10 */ #ifndef SOLARIS10 #define SOLARIS10 #endif /* SOLARIS10 */ #endif /* SOLARIS11 */ #ifdef SOLARIS10 /* Solaris 10 implies 9 */ #ifndef SOLARIS9 #define SOLARIS9 #endif /* SOLARIS9 */ #endif /* SOLARIS10 */ #ifdef SOLARIS9 /* Solaris 9 implies 8 */ #ifndef SOLARIS8 #define SOLARIS8 #endif /* SOLARIS8 */ #endif /* SOLARIS9 */ #ifdef SOLARIS8 /* Solaris 8 implies 7 */ #ifndef SOLARIS7 #define SOLARIS7 #endif /* SOLARIS7 */ #endif /* SOLARIS8 */ #ifdef SOLARIS7 /* Solaris 7 implies 2.6 */ #ifndef SOLARIS26 #define SOLARIS26 #endif /* SOLARIS26 */ #endif /* SOLARIS7 */ #ifdef SOLARIS26 /* Solaris 2.6 implies 2.5 */ #ifndef SOLARIS25 #define SOLARIS25 #endif /* SOLARIS25 */ #endif /* SOLARIS26 */ #ifdef SOLARIS25 /* Solaris 2.5 implies Solaris */ #ifndef SOLARIS #define SOLARIS #endif /* SOLARIS */ #ifndef POSIX /* And POSIX */ #define POSIX #endif /* POSIX */ #ifndef CK_WREFRESH /* And this (curses) */ #define CK_WREFRESH #endif /* CK_WREFRESH */ #endif /* SOLARIS25 */ #ifdef SOLARIS24 /* Solaris 2.4 implies Solaris */ #ifndef SOLARIS #define SOLARIS #endif /* SOLARIS */ #endif /* SOLARIS24 */ #ifdef SOLARIS /* Solaris gets "POSIX" RTS/CTS API */ #ifdef POSIX #ifndef POSIX_CRTSCTS #define POSIX_CRTSCTS #endif /* POSIX_CRTSCTS */ #endif /* POSIX */ #ifndef SVR4 #define SVR4 #endif /* SVR4 */ #ifndef STERMIOX #define STERMIOX #endif /* STERMIOX */ #ifndef SELECT #define SELECT #endif /* SELECT */ #ifndef FNFLOAT #define FNFLOAT #endif /* FNFLOAT */ #ifndef DIRENT #define DIRENT #endif /* DIRENT */ #ifndef BIGBUFOK #define BIGBUFOK #endif /* BIGBUFOK */ #ifndef CK_NEWTERM #define CK_NEWTERM #endif /* CK_NEWTERM */ #endif /* SOLARIS */ #ifdef SUN4S5 /* Sun-4 System V environment */ #ifndef SVR3 /* implies System V R3 or later */ #define SVR3 #endif /* SVR3 */ #endif /* SUN4S5 */ #ifdef SUNOS41 /* SUNOS41 implies SUNOS4 */ #ifndef SUNOS4 #define SUNOS4 #endif /* SUNOS4 */ #endif /* SUNOS41 */ #ifdef SUN4S5 /* Sun-4 System V environment */ #ifndef SVR3 /* implies System V R3 or later */ #define SVR3 #endif /* SVR3 */ #endif /* SUN4S5 */ #ifdef SUNOS41 /* SUNOS41 implies SUNOS4 */ #ifndef SUNOS4 #define SUNOS4 #endif /* SUNOS4 */ #endif /* SUNOS41 */ #ifdef SUNOS4 /* Built-in SUNOS4 makefile entry */ #ifndef UNIX #define UNIX #endif /* UNIX */ #ifndef BSD4 #define BSD4 #endif /* BSD4 */ #ifndef NOSETBUF #define NOSETBUF #endif /* NOSETBUF */ #ifndef DIRENT #define DIRENT #endif /* DIRENT */ #ifndef NONET #ifndef TCPSOCKET #define TCPSOCKET #endif /* TCPSOCKET */ #endif /* NONET */ #ifndef SAVEDUID #define SAVEDUID #endif /* SAVEDUID */ #ifndef DYNAMIC #define DYNAMIC #endif /* DYNAMIC */ #endif /* SUNOS4 */ #ifdef SOLARIS /* Built in makefile entry */ #ifndef NOSETBUF /* for Solaris 2.x */ #define NOSETBUF #endif /* NOSETBUF */ #ifndef NOCURSES #ifndef CK_CURSES #define CK_CURSES #endif /* CK_CURSES */ #endif /* NOCURSES */ #ifndef CK_NEWTERM #define CK_NEWTERM #endif /* CK_NEWTERM */ #ifndef DIRENT #define DIRENT #endif /* DIRENT */ #ifndef NONET #ifndef TCPSOCKET #define TCPSOCKET #endif /* TCPSOCKET */ #endif /* NONET */ #ifndef UNIX #define UNIX #endif /* UNIX */ #ifndef SVR4 #define SVR4 #endif /* SVR4 */ #ifndef HADDRLIST #define HADDRLIST #endif /* HADDRLIST */ #ifndef STERMIOX #define STERMIOX #endif /* STERMIOX */ #ifndef SELECT #define SELECT #endif /* SELECT */ #ifndef DYNAMIC #define DYNAMIC #endif /* DYNAMIC */ #ifndef NOUUCP #ifndef HDBUUCP #define HDBUUCP #endif /* HDBUUCP */ #endif /* NOUUCP */ #endif /* SOLARIS */ /* Features that can be eliminated from a no-file-transfer version */ #ifdef NOXFER #ifndef NOFTP #define NOFTP #endif /* NOFTP */ #ifndef OS2 #ifndef NOCURSES /* Fullscreen file-transfer display */ #define NOCURSES #endif /* NOCURSES */ #endif /* OS2 */ #ifndef NOCKXYZ /* XYZMODEM support */ #define NOCKXYZ #endif /* NOCKXYZ */ #ifndef NOCKSPEED /* Ctrl-char unprefixing */ #define NOCKSPEED #endif /* NOCKSPEED */ #ifndef NOSERVER /* Server mode */ #define NOSERVER #endif /* NOSERVER */ #ifndef NOCKTIMERS /* Dynamic packet timers */ #define NOCKTIMERS #endif /* NOCKTIMERS */ #ifndef NOPATTERNS /* File-type patterns */ #define NOPATTERNS #endif /* NOPATTERNS */ #ifndef NOSTREAMING /* Streaming */ #define NOSTREAMING #endif /* NOSTREAMING */ #ifndef NOIKSD /* Internet Kermit Service */ #define NOIKSD #endif /* NOIKSD */ #ifndef NOPIPESEND /* Sending from pipes */ #define NOPIPESEND #endif /* NOPIPESEND */ #ifndef NOAUTODL /* Autodownload */ #define NOAUTODL #endif /* NOAUTODL */ #ifndef NOMSEND /* MSEND */ #define NOMSEND #endif /* NOMSEND */ #ifndef NOTLOG /* Transaction logging */ #define NOTLOG #endif /* NOTLOG */ #ifndef NOCKXXCHAR /* Packet character doubling */ #define NOCKXXCHAR #endif /* NOCKXXCHAR */ #endif /* NOXFER */ #ifdef NOICP /* No Interactive Command Parser */ #ifndef NODIAL /* Implies No DIAL command */ #define NODIAL #endif /* NODIAL */ #ifndef NOCKXYZ /* and no external protocols */ #define NOCKXYZ #endif /* NOCKXYZ */ #endif /* NOICP */ #ifndef NOIKSD #ifdef IKSDONLY #ifndef IKSD #define IKSD #endif /* IKSD */ #ifndef NOLOCAL #define NOLOCAL #endif /* NOLOCAL */ #ifndef NOPUSH #define NOPUSH #endif /* NOPUSH */ #ifndef TNCODE #define TNCODE #endif /* TNCODE */ #ifndef TCPSOCKET #define TCPSOCKET #endif /* TCPSOCKET */ #ifndef NETCONN #define NETCONN #endif /* NETCONN */ #ifdef SUNX25 #undef SUNX25 #endif /* SUNX25 */ #ifdef IBMX25 #undef IBMX25 #endif /* IBMX25 */ #ifdef STRATUSX25 #undef STRATUSX25 #endif /* STRATUSX25 */ #ifdef CK_NETBIOS #undef CK_NETBIOS #endif /* CK_NETBIOS */ #ifdef SUPERLAT #undef SUPERLAT #endif /* SUPERLAT */ #ifdef NPIPE #undef NPIPE #endif /* NPIPE */ #ifdef NETFILE #undef NETFILE #endif /* NETFILE */ #ifdef NETCMD #undef NETCMD #endif /* NETCMD */ #ifdef NETPTY #undef NETPTY #endif /* NETPTY */ #ifdef RLOGCODE #undef RLOGCODE #endif /* RLOGCODE */ #ifdef NETDLL #undef NETDLL #endif /* NETDLL */ #ifndef NOSSH #undef NOSSH #endif /* NOSSH */ #ifndef NOFORWARDX #define NOFORWARDX #endif /* NOFORWARDX */ #ifndef NOBROWSER #define NOBROWSER #endif /* NOBROWSER */ #ifndef NOHTTP #define NOHTTP #endif /* NOHTTP */ #ifndef NOFTP #define NOFTP #endif /* NOFTP */ #ifndef NO_COMPORT #define NO_COMPORT #endif /* NO_COMPORT */ #endif /* IKSDONLY */ #endif /* NOIKSD */ /* Features that can be eliminated from a remote-only version */ #ifdef NOLOCAL #ifndef NOFTP #define NOFTP #endif /* NOFTP */ #ifndef NOHTTP #define NOHTTP #endif /* NOHTTP */ #ifndef NOSSH #define NOSSH #endif /* NOSSH */ #ifndef NOTERM #define NOTERM #endif /* NOTERM */ #ifndef NOCURSES /* Fullscreen file-transfer display */ #define NOCURSES #endif /* NOCURSES */ #ifndef NODIAL #define NODIAL #endif /* NODIAL */ #ifndef NOSCRIPT #define NOSCRIPT #endif /* NOSCRIPT */ #ifndef NOSETKEY #define NOSETKEY #endif /* NOSETKEY */ #ifndef NOKVERBS #define NOKVERBS #endif /* NOKVERBS */ #ifndef NOXMIT #define NOXMIT #endif /* NOXMIT */ #ifdef CK_CURSES #undef CK_CURSES #endif /* CK_CURSES */ #ifndef IKSDONLY #ifndef NOAPC #define NOAPC #endif /* NOAPC */ #ifndef NONET #define NONET #endif /* NONET */ #endif /* IKSDONLY */ #endif /* NOLOCAL */ #ifdef NONET #ifdef NETCONN #undef NETCONN #endif /* NETCONN */ #ifdef TCPSOCKET #undef TCPSOCKET #endif /* TCPSOCKET */ #ifndef NOTCPOPTS #define NOTCPOPTS #endif /* NOTCPOPTS */ #ifdef SUNX25 #undef SUNX25 #endif /* SUNX25 */ #ifdef IBMX25 #undef IBMX25 #endif /* IBMX25 */ #ifdef STRATUSX25 #undef STRATUSX25 #endif /* STRATUSX25 */ #ifdef CK_NETBIOS #undef CK_NETBIOS #endif /* CK_NETBIOS */ #ifdef SUPERLAT #undef SUPERLAT #endif /* SUPERLAT */ #ifdef NPIPE #undef NPIPE #endif /* NPIPE */ #ifdef NETFILE #undef NETFILE #endif /* NETFILE */ #ifdef NETCMD #undef NETCMD #endif /* NETCMD */ #ifdef NETPTY #undef NETPTY #endif /* NETPTY */ #ifdef RLOGCODE #undef RLOGCODE #endif /* RLOGCODE */ #ifdef NETDLL #undef NETDLL #endif /* NETDLL */ #ifndef NOSSH #define NOSSH #endif /* NOSSH */ #ifndef NOFTP #define NOFTP #endif /* NOFTP */ #ifndef NOHTTP #define NOHTTP #endif /* NOHTTP */ #ifndef NOBROWSER #define NOBROWSER #endif /* NOBROWSER */ #ifndef NOFORWARDX #define NOFORWARDX #endif /* NOFORWARDX */ #endif /* NONET */ #ifdef IKSDONLY #ifdef SUNX25 #undef SUNX25 #endif /* SUNX25 */ #ifdef IBMX25 #undef IBMX25 #endif /* IBMX25 */ #ifdef STRATUSX25 #undef STRATUSX25 #endif /* STRATUSX25 */ #ifdef CK_NETBIOS #undef CK_NETBIOS #endif /* CK_NETBIOS */ #ifdef SUPERLAT #undef SUPERLAT #endif /* SUPERLAT */ #ifdef NPIPE #undef NPIPE #endif /* NPIPE */ #ifdef NETFILE #undef NETFILE #endif /* NETFILE */ #ifdef NETCMD #undef NETCMD #endif /* NETCMD */ #ifdef NETPTY #undef NETPTY #endif /* NETPTY */ #ifdef RLOGCODE #undef RLOGCODE #endif /* RLOGCODE */ #ifdef NETDLL #undef NETDLL #endif /* NETDLL */ #ifndef NOSSH #define NOSSH #endif /* NOSSH */ #ifndef NOHTTP #define NOHTTP #endif /* NOHTTP */ #ifndef NOBROWSER #define NOBROWSER #endif /* NOBROWSER */ #endif /* IKSDONLY */ /* Note that none of the above precludes TNCODE, which can be defined in the absence of TCPSOCKET, etc, to enable server-side Telnet negotation. */ #ifndef TNCODE /* This is for the benefit of */ #ifdef TCPSOCKET /* modules that might need TNCODE */ #define TNCODE /* not all of ckcnet.h... */ #endif /* TCPSOCKET */ #endif /* TNCODE */ #ifndef NETCONN #ifdef TCPSOCKET #define NETCONN #endif /* TCPSOCKET */ #endif /* NETCONN */ #ifndef DEFPAR /* Default parity */ #define DEFPAR 0 /* Must be here because it is used */ #endif /* DEFPAR */ /* by all classes of modules */ #ifdef NT #ifndef OS2ORWIN32 #define OS2ORWIN32 #endif /* OS2ORWIN32 */ #ifndef OS2 #define WIN32ONLY #endif /* OS2 */ #endif /* NT */ #ifdef OS2 /* For OS/2 debugging */ #ifndef OS2ORWIN32 #define OS2ORWIN32 #endif /* OS2ORWIN32 */ #ifdef NT #define NOCRYPT #include #define NTSIG #else /* NT */ #define OS2ONLY #include #endif /* NT */ #ifndef OS2ORUNIX #define OS2ORUNIX #endif /* OS2ORUNIX */ #ifndef OS2ORVMS #define OS2ORVMS #endif /* OS2ORVMS */ #endif /* OS2 */ #include /* Begin by including this. */ #include /* and this. */ #ifdef VMS #include /* Ensure off_t. */ #include "ckvrms.h" /* Get NAMDEF NAMX_C_MAXRSS. */ #endif /* def VMS */ /* System-type compilation switches */ #ifdef FT21 /* Fortune For:Pro 2.1 implies 1.8 */ #ifndef FT18 #define FT18 #endif /* FT18 */ #endif /* FT21 */ #ifdef __bsdi__ #ifndef BSDI #define BSDI #endif /* BSDI */ #endif /* __bsdi__ */ #ifdef AIXPS2 /* AIXPS2 implies AIX370 */ #ifndef AIX370 #define AIX370 #endif /* AIX370 */ #endif /* AIXPS2 */ #ifdef AIX370 /* AIX PS/2 or 370 implies BSD4 */ #ifndef BSD4 #define BSD4 #endif /* BSD4 */ #endif /* AIX370 */ #ifdef AIXESA /* AIX/ESA implies BSD4.4 */ #ifndef BSD44 #define BSD44 #endif /* BSD44 */ #endif /* AIXESA */ #ifdef AIX53 /* AIX53 implies AIX52 */ #ifndef AIX52 #define AIX52 #endif /* AIX52 */ #endif /* AIX53 */ #ifdef AIX52 /* AIX52 implies AIX51 */ #ifndef AIX51 #define AIX51 #endif /* AIX51 */ #endif /* AIX52 */ #ifdef AIX51 /* AIX51 implies AIX50 */ #ifndef AIX50 #define AIX50 #endif /* AIX50 */ #endif /* AIX51 */ #ifdef AIX50 /* AIX50 implies AIX45 */ #ifndef AIX45 #define AIX45 #endif /* AIX45 */ #endif /* AIX50 */ #ifdef AIX45 /* AIX45 implies AIX44 */ #ifndef AIX44 #define AIX44 #endif /* AIX44 */ #endif /* AIX45 */ #ifdef AIX44 /* AIX44 implies AIX43 */ #ifndef AIX43 #define AIX43 #endif /* AIX43 */ #endif /* AIX44 */ #ifdef AIX43 /* AIX43 implies AIX42 */ #ifndef AIX42 #define AIX42 #endif /* AIX42 */ #endif /* AIX43 */ #ifdef AIX42 /* AIX42 implies AIX41 */ #ifndef AIX41 #define AIX41 #endif /* AIX41 */ #endif /* AIX42 */ #ifdef SV68R3V6 /* System V/68 R32V6 implies SVR3 */ #ifndef SVR3 #define SVR3 #endif /* SVR3 */ #endif /* SV68R3V6 */ #ifdef SV88R32 /* System V/88 R32 implies SVR3 */ #ifndef SVR3 #define SVR3 #endif /* SVR3 */ #endif /* SV88R32 */ #ifdef DGUX540 /* DG UX 5.40 implies Sys V R 4 */ #ifndef SVR4 #define SVR4 #endif /* SVR4 */ #endif /* DGUX540 */ #ifndef DGUX #ifdef DGUX540 /* DG/UX 5.40 implies DGUX */ #define DGUX #else #ifdef DGUX430 /* So does DG/UX 4.30 */ #define DGUX #endif /* DGUX430 */ #endif /* DGUX540 */ #endif /* DGUX */ #ifdef IRIX65 /* IRIX 6.5 implies IRIX 6.4 */ #ifndef IRIX64 #define IRIX64 #endif /* IRIX64 */ #endif /* IRIX65 */ #ifdef IRIX64 /* IRIX 6.4 implies IRIX 6.2 */ #ifndef BSD44ORPOSIX #define BSD44ORPOSIX /* for ckutio's benefit */ #endif /* BSD44ORPOSIX */ #ifndef IRIX62 #define IRIX62 #endif /* IRIX62 */ #endif /* IRIX64 */ #ifdef IRIX62 /* IRIX 6.2 implies IRIX 6.0 */ #ifndef IRIX60 #define IRIX60 #endif /* IRIX60 */ #endif /* IRIX62 */ #ifdef IRIX60 /* IRIX 6.0 implies IRIX 5.1 */ #ifndef IRIX51 #define IRIX51 #endif /* IRIX51 */ #ifndef IRIX52 /* And IRIX 5.2 (for hwfc) */ #define IRIX52 #endif /* IRIX52 */ #endif /* IRIX60 */ #ifndef IRIX /* IRIX 4.0 or greater implies IRIX */ #ifdef IRIX64 #define IRIX #else #ifdef IRIX62 #define IRIX #else #ifdef IRIX60 #define IRIX #else #ifdef IRIX51 #define IRIX #else #ifdef IRIX40 #define IRIX #endif /* IRIX40 */ #endif /* IRIX51 */ #endif /* IRIX60 */ #endif /* IRIX62 */ #endif /* IRIX64 */ #endif /* IRIX */ #ifdef MIPS /* MIPS System V environment */ #ifndef SVR3 /* implies System V R3 or later */ #define SVR3 #endif /* SVR3 */ #endif /* MIPS */ #ifdef HPUX9 /* HP-UX 9.x */ #ifndef SVR3 #define SVR3 #endif /* SVR3 */ #ifndef HPUX #define HPUX #endif /* HPUX */ #ifndef HPUX9PLUS #define HPUX9PLUS #endif /* HPUX9PLUS */ #endif /* HPUX9 */ #ifdef HPUX10 /* HP-UX 10.x */ #ifndef HPUX1010 /* If anything higher is defined */ #ifdef HPUX1020 /* define HPUX1010 too. */ #define HPUX1010 #endif /* HPUX1020 */ #ifdef HPUX1030 #define HPUX1010 #endif /* HPUX1030 */ #endif /* HPUX1010 */ #ifdef HPUX1100 /* HP-UX 11.00 implies 10.10 */ #ifndef HPUX1010 #define HPUX1010 #endif /* HPUX1010 */ #endif /* HPUX1100 */ #ifndef SVR4 #define SVR4 #endif /* SVR4 */ #ifndef HPUX #define HPUX #endif /* HPUX */ #ifndef HPUX9PLUS #define HPUX9PLUS #endif /* HPUX9PLUS */ #endif /* HPUX10 */ #ifdef QNX /* QNX Software Systems Inc */ #ifndef POSIX /* QNX 4.0 or later is POSIX */ #define POSIX #endif /* POSIX */ #ifndef __386__ /* Comes in 16-bit and 32-bit */ #define __16BIT__ #define CK_QNX16 #else #define __32BIT__ #define CK_QNX32 #endif /* __386__ */ #endif /* QNX */ /* 4.4BSD is a mixture of System V R4, POSIX, and 4.3BSD. */ #ifdef BSD44 /* 4.4 BSD */ #ifndef SVR4 /* BSD44 implies SVR4 */ #define SVR4 #endif /* SVR4 */ #ifndef NOSETBUF /* NOSETBUF is safe */ #define NOSETBUF #endif /* NOSETBUF */ #ifndef DIRENT /* Uses */ #define DIRENT #endif /* DIRENT */ #endif /* BSD44 */ #ifdef OPENBSD /* OpenBSD might or might not */ #ifndef __OpenBSD__ /* have this defined... */ #define __OpenBSD__ #endif /* __OpenBSD__ */ #endif /* OPENBSD */ #ifdef SVR3 /* SVR3 implies ATTSV */ #ifndef ATTSV #define ATTSV #endif /* ATTSV */ #endif /* SVR3 */ #ifdef SVR4 /* SVR4 implies ATTSV */ #ifndef ATTSV #define ATTSV #endif /* ATTSV */ #ifndef SVR3 /* ...as well as SVR3 */ #define SVR3 #endif /* SVR3 */ #endif /* SVR4 */ #ifdef OXOS #ifndef ATTSV #define ATTSV /* OXOS implies ATTSV */ #endif /* ! ATTSV */ #define SW_ACC_ID /* access() wants privs on */ #define kill priv_kill /* kill() wants privs on */ #ifndef NOSETBUF #define NOSETBUF /* NOSETBUF is safe */ #endif /* ! NOSETBUF */ #endif /* OXOS */ #ifdef UTSV /* UTSV implies ATTSV */ #ifndef ATTSV #define ATTSV #endif /* ATTSV */ #endif /* UTSV */ #ifdef XENIX /* XENIX implies ATTSV */ #ifndef ATTSV #define ATTSV #endif /* ATTSV */ #endif /* XENIX */ #ifdef AUX /* AUX implies ATTSV */ #ifndef ATTSV #define ATTSV #endif /* ATTSV */ #endif /* AUX */ #ifdef ATT7300 /* ATT7300 implies ATTSV */ #ifndef ATTSV #define ATTSV #endif /* ATTSV */ #endif /* ATT7300 */ #ifdef ATT6300 /* ATT6300 implies ATTSV */ #ifndef ATTSV #define ATTSV #endif /* ATTSV */ #endif /* ATT6300 */ #ifdef HPUX /* HPUX implies ATTSV */ #ifndef ATTSV #define ATTSV #endif /* ATTSV */ #endif /* HPUX */ #ifdef ISIII /* ISIII implies ATTSV */ #ifndef ATTSV #define ATTSV #endif /* ATTSV */ #endif /* ISIII */ #ifdef NEXT33 /* NEXT33 implies NEXT */ #ifndef NEXT #define NEXT #endif /* NEXT */ #endif /* NEXT33 */ #ifdef NEXT /* NEXT implies BSD4 */ #ifndef BSD4 #define BSD4 #endif /* BSD4 */ #endif /* NEXT */ #ifdef BSD41 /* BSD41 implies BSD4 */ #ifndef BSD4 #define BSD4 #endif /* BSD4 */ #endif /* BSD41 */ #ifdef BSD43 /* BSD43 implies BSD4 */ #ifndef BSD4 #define BSD4 #endif /* BSD4 */ #endif /* BSD43 */ #ifdef BSD4 /* BSD4 implies ANYBSD */ #ifndef ANYBSD #define ANYBSD #endif /* ANYBSD */ #endif /* BSD4 */ #ifdef BSD29 /* BSD29 implies ANYBSD */ #ifndef ANYBSD #define ANYBSD #endif /* ANYBSD */ #endif /* BSD29 */ #ifdef ATTSV /* ATTSV implies UNIX */ #ifndef UNIX #define UNIX #endif /* UNIX */ #endif /* ATTSV */ #ifdef ANYBSD /* ANYBSD implies UNIX */ #ifndef UNIX #define UNIX #endif /* UNIX */ #endif /* ANYBSD */ #ifdef POSIX /* POSIX implies UNIX */ #ifndef UNIX #define UNIX #endif /* UNIX */ #ifndef DIRENT /* and DIRENT, i.e. */ #ifndef SDIRENT #define DIRENT #endif /* SDIRENT */ #endif /* DIRENT */ #ifndef NOFILEH /* POSIX doesn't use */ #define NOFILEH #endif /* NOFILEH */ #endif /* POSIX */ #ifdef V7 #ifndef UNIX #define UNIX #endif /* UNIX */ #endif /* V7 */ #ifdef COHERENT #ifndef UNIX #define UNIX #endif /* UNIX */ #ifdef COMMENT #ifndef NOCURSES #define NOCURSES #endif /* NOCURSES */ #endif /* COMMENT */ #endif /* COHERENT */ #ifdef MINIX #ifndef UNIX #define UNIX #endif /* UNIX */ #endif /* MINIX */ /* The symbol SVORPOSIX is defined for both AT&T and POSIX compilations to make it easier to select items that System V and POSIX have in common, but which BSD, V7, etc, do not have. */ #ifdef ATTSV #ifndef SVORPOSIX #define SVORPOSIX #endif /* SVORPOSIX */ #endif /* ATTSV */ #ifdef POSIX #ifndef SVORPOSIX #define SVORPOSIX #endif /* SVORPOSIX */ #endif /* POSIX */ /* The symbol SVR4ORPOSIX is defined for both AT&T System V R4 and POSIX compilations to make it easier to select items that System V R4 and POSIX have in common, but which BSD, V7, and System V R3 and earlier, etc, do not have. */ #ifdef POSIX #ifndef SVR4ORPOSIX #define SVR4ORPOSIX #endif /* SVR4ORPOSIX */ #endif /* POSIX */ #ifdef SVR4 #ifndef SVR4ORPOSIX #define SVR4ORPOSIX #endif /* SVR4ORPOSIX */ #endif /* SVR4 */ /* The symbol BSD44ORPOSIX is defined for both 4.4BSD and POSIX compilations to make it easier to select items that 4.4BSD and POSIX have in common, but which System V, BSD, V7, etc, do not have. */ #ifdef BSD44 #ifndef BSD44ORPOSIX #define BSD44ORPOSIX #endif /* BSD44ORPOSIX */ #endif /* BSD44 */ #ifdef POSIX #ifndef BSD44ORPOSIX #define BSD44ORPOSIX #endif /* BSD44ORPOSIX */ #endif /* POSIX */ #ifdef UNIX /* For items common to OS/2 and UNIX */ #ifndef OS2ORUNIX #define OS2ORUNIX #endif /* OS2ORUNIX */ #endif /* UNIX */ #ifdef UNIX /* For items common to VMS and UNIX */ #define VMSORUNIX #else #ifdef VMS #define VMSORUNIX #ifndef OS2ORVMS #define OS2ORVMS #endif /* OS2ORVMS */ #endif /* VMS */ #endif /* UNIX */ #ifndef UNIXOROSK /* UNIX or OS-9 (or OS-9000) */ #ifdef UNIX #define UNIXOROSK #else #ifdef OSK #define UNIXOROSK #endif /* OSK */ #endif /* UNIX */ #endif /* UNIXOROSK */ #ifndef OSKORUNIX #ifdef UNIXOROSK #define OSKORUNIX #endif /* UNIXOROSK */ #endif /* OSKORUNIX */ #ifdef OS2 #define CK_ANSIC /* OS/2 supports ANSIC and more extensions */ #endif /* OS2 */ #ifdef OSF50 /* Newer OSF/1 versions imply older ones */ #ifndef OSF40 #define OSF40 #endif /* OSF40 */ #endif /* OSF50 */ #ifdef OSF40 #ifndef OSF32 #define OSF32 #endif /* OSF32 */ #endif /* OSF40 */ #ifdef OSF32 #ifndef OSF30 #define OSF30 #endif /* OSF30 */ #endif /* OSF32 */ #ifdef OSF30 #ifndef OSF20 #define OSF20 #endif /* OSF20 */ #endif /* OSF30 */ #ifdef OSF20 #ifndef OSF10 #define OSF10 #endif /* OSF10 */ #endif /* OSF20 */ #ifdef __DECC /* For DEC Alpha VMS or OSF/1 */ #ifndef CK_ANSIC #define CK_ANSIC /* Even with /stand=vaxc, need ansi */ #endif /* CKANSIC */ #ifndef SIG_V #define SIG_V /* and signal type is VOID */ #endif /* SIG_V */ #ifndef CK_ANSILIBS #define CK_ANSILIBS /* (Martin Zinser, Feb 1995) */ #endif /* CK_ANSILIBS */ #ifndef _POSIX_C_SOURCE #define _POSIX_C_SOURCE 1 #endif /* _POSIX_C_SOURCE */ #endif /* __DECC */ #ifdef VMS #ifdef __ia64 /* VMS on Itanium */ #ifndef VMSI64 #define VMSI64 #endif /* VMSI64 */ #endif /* __ia64 */ #ifndef VMS64BIT /* 64-bit VMS on Itanium or Alpha */ #ifdef __ia64 #define VMS64BIT #else #ifdef __ALPHA #define VMS64BIT #endif /* __ia64 */ #endif /* __ALPHA */ #endif /* VMS64BIT */ #endif /* VMS */ #ifdef apollo /* May be ANSI-C, check further */ #ifdef __STDCPP__ #define CK_ANSIC /* Yes, this is real ANSI-C */ #define SIG_V #else #define NOANSI /* Nope, not ANSI */ #undef __STDC__ /* Even though it say it is! */ #define SIG_I #endif /* __STDCPP__ */ #endif /* apollo */ #ifdef POSIX /* -DPOSIX on cc command line */ #ifndef _POSIX_SOURCE /* Implies _POSIX_SOURCE */ #define _POSIX_SOURCE #endif /* _POSIX_SOURCE */ #endif /* POSIX */ /* ANSI C? That is, do we have function prototypes, new-style function declarations, and parameter type checking and coercion? */ #ifdef MAC /* MPW C is ANSI */ #ifndef NOANSI #ifndef CK_ANSIC #define CK_ANSIC #endif /* CK_ANSIC */ #endif /* NOANSI */ #endif /* MAC */ #ifdef STRATUS /* Stratus VOS */ #ifndef CK_ANSIC #define CK_ANSIC #endif /* CK_ANSIC */ #ifndef NOSTAT #define NOSTAT #endif /* NOSTAT */ #endif /* STRATUS */ #ifndef NOANSI #ifdef __STDC__ /* __STDC__ means ANSI C */ #ifndef CK_ANSIC #define CK_ANSIC #endif /* CK_ANSIC */ #endif /* __STDC__ */ #endif /* NOANSI */ /* _PROTOTYP() is used for forward declarations of functions so we can have parameter and return value type checking if the compiler offers it. __STDC__ should be defined by the compiler only if function prototypes are allowed. Otherwise, we get old-style forward declarations. Our own private CK_ANSIC symbol tells whether we use ANSI C prototypes. To force use of ANSI prototypes, include -DCK_ANSIC on the cc command line. To disable the use of ANSI prototypes, include -DNOANSI. */ #ifdef CK_ANSIC #define _PROTOTYP( func, parms ) func parms #else /* Not ANSI C */ #define _PROTOTYP( func, parms ) func() #endif /* CK_ANSIC */ #ifndef OS2 #ifdef NOLOGIN /* NOLOGIN implies NOIKSD */ #ifndef NOIKSD #define NOIKSD #endif /* NOIKSD */ #endif /* NOLOGIN */ #endif /* OS2 */ #ifdef NOIKSD /* Internet Kermit Service Daemon */ #ifndef OS2 #ifndef NOPRINTFSUBST #define NOPRINTFSUBST #endif /* NOPRINTFSUBST */ #endif /* OS2 */ #ifndef NOLOGIN #define NOLOGIN #endif /* NOLOGIN */ #ifndef NOSYSLOG #define NOSYSLOG #endif /* NOSYSLOG */ #ifndef NOWTMP #define NOWTMP #endif /* NOWTMP */ #else #ifndef IKSD #ifdef OS2ORUNIX /* Platforms where IKSD is supported */ #define IKSD #endif /* OS2ORUNIX */ #endif /* IKSD */ #endif /* NOIKSD */ #ifdef IKSD /* IKSD options... */ #ifndef IKSDCONF /* IKSD configuration file */ #ifdef UNIX #define IKSDCONF "/etc/iksd.conf" #else #ifdef OS2 #define IKSDCONF "iksd.ksc" #endif /* OS2 */ #endif /* UNIX */ #endif /* IKSDCONF */ #ifndef NOIKSDB #ifndef IKSDB /* IKSD database */ #ifdef UNIX #define IKSDB #define IK_LCKTRIES 16 /* How many times to try to get lock */ #define IK_LCKSLEEP 1 /* How long to sleep between tries */ #define IK_LOCKFILE "iksd.lck" /* Database lockfilename */ #define IK_DBASEDIR "/var/log/" /* Database directory */ #define IK_DBASEFIL "iksd.db" /* Database filename */ #else /* UNIX */ #ifdef OS2 #define IKSDB #ifndef NOFTRUNCATE /* ftruncate() not available */ #define NOFTRUNCATE #endif /* NOFTRUNCATE */ #define IK_LCKTRIES 16 /* How many times to try to get lock */ #define IK_LCKSLEEP 1 /* How long to sleep between tries */ #define IK_LOCKFILE "iksd.lck" /* DB lockfilename (in systemroot) */ #define IK_DBASEFIL "iksd.db" /* Database filename */ #endif /* OS2 */ #endif /* UNIX */ #endif /* IKSDB */ #endif /* NOIKSDB */ #endif /* IKSD */ /* Substitutes for printf() and friends used in IKS to compensate for lack of a terminal driver, mainly to supply CR after LF. */ #ifndef NOPRINTFSUBST #ifdef MAC /* * The MAC doesn't use standard stdio routines. */ #undef getchar #define getchar() mac_getchar() #undef putchar #define putchar(c) mac_putchar(c) #define printf mac_printf #define perror mac_perror #define puts mac_puts extern int mac_putchar (int c); extern int mac_puts (const char *string); extern int mac_printf(const char *, ...); extern int mac_getchar (void); #endif /* MAC */ #ifdef OS2 #define printf Vscrnprintf #define fprintf Vscrnfprintf extern int Vscrnprintf(const char *, ...); extern int Vscrnprintw(const char *, ...); extern int Vscrnfprintf(FILE *, const char *, ...); #ifdef putchar #undef putchar #endif /* putchar */ #define putchar(x) Vscrnprintf("%c",x) #define perror(x) Vscrnperror(x) #endif /* OS2 */ #ifndef CKWART_C #ifdef UNIX #ifndef pdp11 #ifndef CKXPRINTF #define CKXPRINTF #endif /* CKXPRINTF */ #endif /* pdp11 */ #endif /* UNIX */ #endif /* CKWART_C */ #endif /* NOPRINTFSUBST */ #ifdef CKXPRINTF #define printf ckxprintf #define fprintf ckxfprintf #ifdef CK_ANSIC _PROTOTYP(int ckxprintf,(const char *, ...)); #ifdef NEXT _PROTOTYP(void ckxperror,(const char *)); #else #ifdef CK_SCOV5 _PROTOTYP(void ckxperror,(const char *)); #else _PROTOTYP(int ckxperror,(const char *)); #endif /* CK_SCOV5 */ #endif /* NEXT */ _PROTOTYP(int ckxfprintf,(FILE *, const char *, ...)); #endif /* CK_ANSIC */ #ifdef putchar #undef putchar #endif /* putchar */ #define putchar(x) ckxprintf("%c",x) #ifdef putc #undef putc #endif /* putc */ #define putc(a,b) ckxfprintf(b,"%c",a) #define perror(x) ckxperror(x) #endif /* CKXPRINTF */ /* Altos-specific items: 486, 586, 986 models... */ #ifdef A986 #define M_VOID #define void int #define CHAR char #define SIG_I #endif /* A986 */ /* Signal handling */ #ifdef QNX #ifndef CK_POSIX_SIG #define CK_POSIX_SIG #endif /* CK_POSIX_SIG */ #endif /* QNX */ /* void type, normally available only in ANSI compilers. The HP-UX exception (for its "bundled" non-ANSI C compiler) is known to be valid back to HP-UX 6.5. Adjustments might be needed for earlier HP-UX versions. */ #ifndef VOID /* Used throughout all C-Kermit */ #ifdef CK_ANSIC /* modules... */ #define VOID void #else #ifdef HPUX #define VOID void #else #define VOID int #endif /* HPUX */ #endif /* CK_ANSIC */ #endif /* VOID */ /* Exactly the same as VOID but for use in contexts where the VOID symbol conflicts some header-file definition. This is needed for the section of ckuusx.c that provides C-Kermit's curses interface, roughly the second half of ckuusx.c. */ #ifndef CKVOID #ifdef CK_ANSIC #define CKVOID void #else #ifdef HPUX #define CKVOID void #else #define CKVOID int #endif /* HPUX */ #endif /* CK_ANSIC */ #endif /* CKVOID */ /* Const type */ #ifndef CONST #ifdef OSK #ifdef _UCC #define CONST const #else #define CONST #endif /* _UCC */ #else /* !OSK */ #ifdef CK_SCO32V4 #define CONST #else #ifdef CK_ANSIC #define CONST const #else #define CONST #endif /* CK_ANSIC */ #endif /* CK_SCO32V4 */ #endif /* OSK */ #endif /* CONST */ /* Signal type */ #ifndef SIG_V /* signal() type, if not def'd yet */ #ifndef SIG_I #ifdef OS2 #define SIG_V #else #ifdef POSIX #define SIG_V #else #ifdef SVR3 /* System V R3 and later */ #define SIG_V #else #ifdef SUNOS4 /* SUNOS V 4.0 and later */ #ifndef sun386 #define SIG_V #else #define SIG_I #endif /* sun386 */ #else #ifdef NEXT /* NeXT */ #define SIG_V #else #ifdef AIX370 #include #define SIG_V #define SIGTYP __SIGVOID /* AIX370 */ #else #ifdef STRATUS /* Stratus VOS */ #define SIG_V #else #ifdef MAC #define SIGTYP long #define SIG_I #ifndef MPW33 #define SIG_IGN 0 #endif /* MPW33 */ #define SIGALRM 1 #ifndef MPW33 #define SIGINT 2 #endif /* MPW33 */ #else /* Everything else */ #define SIG_I #endif /* MAC */ #endif /* STRATUS */ #endif /* AIX370 */ #endif /* NEXT */ #endif /* SUNOS4 */ #endif /* SVR3 */ #endif /* POSIX */ #endif /* OS2 */ #endif /* SIG_I */ #endif /* SIG_V */ #ifdef SIG_I #define SIGRETURN return(0) #ifndef SIGTYP #define SIGTYP int #endif /* SIGTYP */ #endif /* SIG_I */ #ifdef SIG_V #define SIGRETURN return #ifndef SIGTYP #define SIGTYP void #endif /* SIGTYP */ #endif /* SIG_V */ #ifdef NT #ifndef SIGTYP #define SIGTYP void #endif /* SIGTYP */ #endif /* NT */ #ifndef SIGTYP #define SIGTYP int #endif /* SIGTYP */ #ifndef SIGRETURN #define SIGRETURN return(0) #endif /* SIGRETURN */ #ifdef CKNTSIG /* This does not work, so don't use it. */ #define signal ckntsignal SIGTYP (*ckntsignal(int type, SIGTYP (*)(int)))(int); #endif /* CKNTSIG */ /* We want all characters to be unsigned if the compiler supports it */ #ifdef KUI #ifdef CHAR #undef CHAR #endif /* CHAR */ #define CHAR unsigned char #else #ifdef PROVX1 typedef char CHAR; /* typedef long LONG; */ typedef int void; #else #ifdef MINIX typedef unsigned char CHAR; #else #ifdef V7 typedef char CHAR; #else #ifdef C70 typedef char CHAR; /* typedef long LONG; */ #else #ifdef BSD29 typedef char CHAR; /* typedef long LONG; */ #else #ifdef datageneral #define CHAR unsigned char /* 3.22 compiler */ #else #ifdef HPUX #define CHAR unsigned char #else #ifdef OS2 #ifdef NT #define CHAR unsigned char #else /* NT */ #ifdef CHAR #undef CHAR #endif /* CHAR */ typedef unsigned char CHAR; #endif /* NT */ #else /* OS2 */ #ifdef VMS typedef unsigned char CHAR; #else #ifdef CHAR #undef CHAR #endif /* CHAR */ typedef unsigned char CHAR; #endif /* VMS */ #endif /* OS2 */ #endif /* HPUX */ #endif /* datageneral */ #endif /* BSD29 */ #endif /* C70 */ #endif /* V7 */ #endif /* MINIX */ #endif /* PROVX1 */ #endif /* KUI */ union ck_short { /* Mainly for Unicode */ USHORT x_short; CHAR x_char[2]; }; #ifdef MAC /* Macintosh file routines */ #ifndef CKWART_C /* But not in "wart"... */ #ifdef feof #undef feof #endif /* feof */ #define feof mac_feof #define rewind mac_rewind #define fgets mac_fgets #define fopen mac_fopen #define fclose mac_fclose int mac_feof(); void mac_rewind(); char *mac_fgets(); FILE *mac_fopen(); int mac_fclose(); #endif /* CKCPRO_W */ #endif /* MAC */ /* Systems whose mainline modules have access to the communication-line file descriptor, ttyfd. */ #ifndef CK_TTYFD #ifdef UNIX #define CK_TTYFD #else #ifdef OS2 #define CK_TTYFD #else #ifdef VMS #define CK_TTYFD #endif /* VMS */ #endif /* OS2 */ #endif /* UNIX */ #endif /* CK_TTYFD */ /* Systems where we can get our own process ID */ #ifndef CK_PID #ifdef UNIX #define CK_PID #endif /* UNIX */ #ifdef OS2 #define CK_PID #endif /* OS2 */ #ifdef VMS #define CK_PID #endif /* VMS */ #endif /* CK_PID */ /* Systems that support the Microsoft Telephony API (TAPI) */ #ifndef NODIAL #ifndef CK_TAPI #ifdef NT #define CK_TAPI #endif /* NT */ #endif /* CK_TAPI */ #endif /* NODIAL */ #ifndef NONZXPAND #ifndef NZXPAND #ifdef OS2ORUNIX #define NZXPAND #else #ifdef VMS #define NZXPAND #else #ifdef datageneral #define NZXPAND #else #ifdef OSK #define NZXPAND #endif /* OSK */ #endif /* datageneral */ #endif /* VMS */ #endif /* OS2ORUNIX */ #endif /* NZXPAND */ #else #ifdef NZXPAND #undef NZXPAND #endif /* NZXPAND */ #endif /* NONZXPAND */ /* nzxpand() option flags */ #define ZX_FILONLY 1 /* Match only regular files */ #define ZX_DIRONLY 2 /* Match only directories */ #define ZX_RECURSE 4 /* Descend through directory tree */ #define ZX_MATCHDOT 8 /* Match "dot files" */ #define ZX_NOBACKUP 16 /* Don't match "backup files" */ #define ZX_NOLINKS 32 /* Don't follow symlinks */ #ifndef NZXPAND #define nzxpand(a,b) zxpand(a) #endif /* NZXPAND */ #ifndef NOZXREWIND #ifndef ZXREWIND /* Platforms that have zxrewind() */ #ifdef OS2ORUNIX #define ZXREWIND #else #ifdef VMS #define ZXREWIND #else #ifdef datageneral #define ZXREWIND #else #ifdef OSK #define ZXREWIND #else #ifdef STRATUS #define ZXREWIND #endif /* STRATUS */ #endif /* OSK */ #endif /* datageneral */ #endif /* VMS */ #endif /* OS2ORUNIX */ #endif /* ZXREWIND */ #else #ifdef ZXREWIND #undef ZXREWIND #endif /* ZXREWIND */ #endif /* NOZXREWIND */ /* Temporary-directory-for-RECEIVE feature ... */ /* This says whether we have the isdir() function defined. */ #ifdef UNIX /* UNIX has it */ #ifndef CK_TMPDIR #ifndef pdp11 #define CK_TMPDIR #define TMPDIRLEN 256 #endif /* pdp11 */ #endif /* CK_TMPDIR */ #endif /* UNIX */ #ifdef VMS /* VMS too */ #ifndef CK_TMPDIR #define CK_TMPDIR #define TMPDIRLEN 256 #endif /* CK_TMPDIR */ #endif /* VMS */ #ifdef OS2 /* OS two too */ #ifndef CK_TMPDIR #define CK_TMPDIR #define TMPDIRLEN 129 #endif /* CK_TMPDIR */ #endif /* OS2 */ #ifdef STRATUS /* Stratus VOS too. */ #ifndef CK_TMPDIR #define CK_TMPDIR #define TMPDIRLEN 256 #endif /* CK_TMPDIR */ #endif /* STRATUS */ #ifdef OSK /* OS-9 too */ #ifndef CK_TMPDIR #define CK_TMPDIR #define TMPDIRLEN 256 #endif /* CK_TMPDIR */ #endif /* OSK */ #ifdef datageneral /* AOS/VS too */ #ifndef CK_TMPDIR #define CK_TMPDIR #define TMPDIRLEN 256 #endif /* CK_TMPDIR */ #endif /* datageneral */ #ifdef CK_TMPDIR /* Needs command parser */ #ifdef NOICP #undef CK_TMPDIR #endif /* NOICP */ #endif /* CK_TMPDIR */ /* Whether to include */ #ifndef NOTIMEH /* */ #ifndef TIMEH #define TIMEH #endif /* TIMEH */ #endif /* NOTIMEH */ #ifndef NOSYSTIMEH /* */ #ifndef SYSTIMEH #ifdef UNIX /* UNIX */ #ifdef SVORPOSIX /* System V or POSIX... */ #ifdef M_UNIX #define SYSTIMEH #else #ifdef SCO_32V4 #define SYSTIMEH #else #ifdef OXOS #define SYSTIMEH #else #ifdef BSD44 #define SYSTIMEH #else #ifdef __linux__ #define SYSTIMEH #else #ifdef AIXRS #ifndef AIX41 #define SYSTIMEH #endif /* AIX41 */ #else #ifdef IRIX60 #define SYSTIMEH #else #ifdef I386IX #define SYSTIMEH #else #ifdef SV68R3V6 #define SYSTIMEH #endif /* SV68R3V6 */ #endif /* I386IX */ #endif /* IRIX60 */ #endif /* AIXRS */ #endif /* __linux__ */ #endif /* BSD44 */ #endif /* OXOS */ #endif /* SCO_32V4 */ #endif /* M_UNIX */ #else /* Not SVORPOSIX */ #ifndef BELLV10 /* All but these... */ #ifndef PROVX1 #ifndef V7 #ifndef BSD41 #ifndef COHERENT #define SYSTIMEH #endif /* COHERENT */ #endif /* BSD41 */ #endif /* V7 */ #endif /* PROVX1 */ #endif /* BELLV10 */ #endif /* SVORPOSIX */ #endif /* UNIX */ #endif /* SYSTIMEH */ #endif /* NOSYSTIMEH */ #ifndef NOSYSTIMEBH /* */ #ifndef SYSTIMEBH #ifdef OSF #define SYSTIMEBH #else #ifdef COHERENT #define SYSTIMEBH #else #ifdef BSD41 #define SYSTIMEBH #else #ifdef BSD29 #define SYSTIMEBH #else #ifdef TOWER1 #define SYSTIMEBH #else #ifdef FT21 #define SYSTIMEBH #else #ifdef BELLV10 #define SYSTIMEBH #endif /* BELLV10 */ #endif /* FT21 */ #endif /* TOWER1 */ #endif /* BSD29 */ #endif /* BSD41 */ #endif /* COHERENT */ #endif /* OSF */ #endif /* SYSTIMEBH */ #endif /* NOSYSTIMEBH */ /* Debug and transaction logging is included automatically unless you define NODEBUG or NOTLOG. Do this if you want to save the space and overhead. (Note, in version 4F these definitions changed from "{}" to the null string to avoid problems with semicolons after braces, as in: "if (x) tlog(this); else tlog(that);" */ #ifndef NODEBUG #ifndef DEBUG #define DEBUG #endif /* DEBUG */ #else #ifdef DEBUG #undef DEBUG #endif /* DEBUG */ #endif /* NODEBUG */ #ifdef NOTLOG #ifdef TLOG #undef TLOG #endif /* TLOG */ #else /* NOTLOG */ #ifndef TLOG #define TLOG #endif /* TLOG */ #endif /* NOTLOG */ /* debug() macro style selection. */ #ifdef VMS #ifndef IFDEBUG #define IFDEBUG #endif /* IFDEBUG */ #endif /* VMS */ #ifdef MAC #ifndef IFDEBUG #define IFDEBUG #endif /* IFDEBUG */ #endif /* MAC */ #ifdef OS2 #ifndef IFDEBUG #define IFDEBUG #endif /* IFDEBUG */ #endif /* OS2 */ #ifdef OXOS /* tst is faster than jsr */ #ifndef IFDEBUG #define IFDEBUG #endif /* IFDEBUG */ #endif /* OXOS */ #ifndef CKCMAI extern int deblog; extern int debok; extern int debxlen; extern int matchdot; extern int tt_bell; #endif /* CKCMAI */ #ifdef OS2 _PROTOTYP( void bleep, (short) ); #else /* OS2 */ #define bleep(x) if(tt_bell)putchar('\07') #endif /* OS2 */ #ifndef BEOSORBEBOX #ifdef BEBOX /* This was used only for DR7 */ #define BEOSORBEBOX #else #ifdef BEOS /* This is used for BeOS 4.x */ #define BEOSORBEBOX #endif /* BEOS */ #endif /* BEBOX */ #endif /* BEOSORBEBOX */ #ifdef NOICP #ifdef TLOG #undef TLOG #endif /* TLOG */ #endif /* NOICP */ /* Formats for debug() and tlog() */ #define F000 0 #define F001 1 #define F010 2 #define F011 3 #define F100 4 #define F101 5 #define F110 6 #define F111 7 #ifdef __linux__ #ifndef LINUX #define LINUX #endif /* LINUX */ #endif /* __linux__ */ /* Platforms where small size is needed */ #ifdef pdp11 #define CK_SMALL #endif /* pdp11 */ /* Can we use realpath()? */ #ifndef NOREALPATH #ifdef pdp11 #define NOREALPATH #endif /* pdp11 */ #endif /* NOREALPATH */ #ifndef NOREALPATH #ifdef UNIX #ifdef HPUX5 #define NOREALPATH #else #ifdef HPUX6 #define NOREALPATH #else #ifdef HPUX7 #define NOREALPATH #else #ifdef HPUX8 #define NOREALPATH #else #ifdef SV68R3V6 #define NOREALPATH #else #ifdef XENIX #define NOREALPATH #else #ifdef CK_SCO32V4 #define NOREALPATH #else #ifdef CK_SCOV5 #define NOREALPATH #else #ifdef OSF32 #define NOREALPATH #else #ifdef OSF30 #define NOREALPATH #else #ifdef ultrix #define NOREALPATH #else #ifdef COHERENT #define NOREALPATH #endif /* COHERENT */ #endif /* ultrix */ #endif /* OSF30 */ #endif /* OSF32 */ #endif /* CK_SCOV5 */ #endif /* CK_SCO32V4 */ #endif /* XENIX */ #endif /* SV68R3V6 */ #endif /* HPUX8 */ #endif /* HPUX7 */ #endif /* HPUX6 */ #endif /* HPUX5 */ #endif /* NOREALPATH */ #ifndef NOREALPATH #ifndef CKREALPATH #define CKREALPATH #endif /* NOREALPATH */ #endif /* CKREALPATH */ #endif /* UNIX */ #ifdef CKREALPATH #ifdef OS2ORUNIX #ifndef CKROOT #define CKROOT #endif /* CKROOT */ #endif /* OS2ORUNIX */ #endif /* CKREALPATH */ /* CKSYMLINK should be set only if we can use readlink() */ #ifdef UNIX #ifndef NOSYMLINK #ifndef CKSYMLINK #define CKSYMLINK #endif /* NOSYMLINK */ #endif /* CKSYMLINK */ #endif /* UNIX */ /* Platforms where we can use lstat() instead of stat() (for symlinks) */ /* This should be set only if both lstat() and readlink() are available */ #ifndef NOLSTAT #ifndef NOSYMLINK #ifndef USE_LSTAT #ifdef UNIX #ifdef CKSYMLINK #ifdef SVR4 /* SVR4 has lstat() */ #define USE_LSTAT #else #ifdef BSD42 /* 4.2BSD and 4.3BSD have it */ #define USE_LSTAT /* This should include old HPUXs */ #else #ifdef BSD44 /* 4.4BSD has it */ #define USE_LSTAT #else #ifdef LINUX /* LINUX has it */ #define USE_LSTAT #else #ifdef SUNOS4 /* SunOS has it */ #define USE_LSTAT #endif /* SUNOS4 */ #endif /* LINUX */ #endif /* BSD44 */ #endif /* BSD42 */ #endif /* SVR4 */ #endif /* CKSYMLINK */ #endif /* UNIX */ #endif /* USE_LSTAT */ #endif /* NOSYMLINK */ #endif /* NOLSTAT */ #ifdef NOLSTAT #ifdef USE_LSTAT #undef USE_LSTAT #endif /* USE_LSTAT */ #endif /* NOLSTAT */ #ifndef NOTTYLOCK /* UNIX systems that have ttylock() */ #ifndef USETTYLOCK #ifdef AIXRS /* AIX 3.1 and later */ #define USETTYLOCK #else #ifdef USE_UU_LOCK /* FreeBSD or other with uu_lock() */ #define USETTYLOCK #else /* Prior to 8.0.299 Alpha.08 this was HAVE_BAUDBOY which was added for Red Hat 7.2 in May 2003 but which is no longer supported in Debian and OpenSuse (at least). */ #ifdef HAVE_LOCKDEV #define USETTYLOCK #endif /* HAVE_LOCKDEV */ #endif /* USE_UU_LOCK */ #endif /* AIXRS */ #endif /* USETTYLOCK */ #endif /* NOTTYLOCK */ #ifndef NO_OPENPTY /* Can use openpty() */ #ifndef HAVE_OPENPTY #ifdef __linux__ #define HAVE_OPENPTY #else #ifdef __FreeBSD__ #define HAVE_OPENPTY #else #ifdef __OpenBSD__ #define HAVE_OPENPTY #else #ifdef __NetBSD__ #define HAVE_OPENPTY #else #ifdef MACOSX10 #define HAVE_OPENPTY #endif /* MACOSX10 */ #endif /* __NetBSD__ */ #endif /* __OpenBSD__ */ #endif /* __FreeBSD__ */ #endif /* __linux__ */ #endif /* HAVE_OPENPTY */ #endif /* NO_OPENPTY */ /* Kermit feature selection */ #ifndef NOSPL #ifndef NOCHANNELIO /* Channel-based file i/o package */ #ifndef CKCHANNELIO #ifdef UNIX #define CKCHANNELIO #else #ifdef OS2 #define CKCHANNELIO #else #ifdef VMS #define CKCHANNELIO #else #ifdef STRATUS #define CKCHANNELIO #endif /* STRATUS */ #endif /* VMS */ #endif /* OS2 */ #endif /* UNIX */ #endif /* CKCHANNELIO */ #endif /* NOCHANNELIO */ #endif /* NOSPL */ #ifndef NOCKEXEC /* EXEC command */ #ifndef NOPUSH #ifndef CKEXEC #ifdef UNIX /* UNIX can do it */ #define CKEXEC #endif /* UNIX */ #endif /* CKEXEC */ #endif /* NOPUSH */ #endif /* NOCKEXEC */ #ifndef NOFAST /* Fast Kermit protocol by default */ #ifndef CK_FAST #ifdef UNIX #define CK_FAST #else #ifdef VMS #define CK_FAST #else #ifdef OS2 #define CK_FAST #endif /* OS2 */ #endif /* VMS */ #endif /* UNIX */ #endif /* CK_FAST */ #endif /* NOFAST */ #ifdef UNIX /* Transparent print */ #ifndef NOXPRINT #ifndef XPRINT #define XPRINT #endif /* XPRINT */ #endif /* NOXPRINT */ #endif /* UNIX */ #ifndef NOHWPARITY /* Hardware parity */ #ifndef HWPARITY #ifdef SVORPOSIX /* System V or POSIX can have it */ #define HWPARITY #else #ifdef SUNOS41 /* SunOS 4.1 can have it */ #define HWPARITY #else #ifdef OS2 /* K95 can have it */ #define HWPARITY #endif /* OS2 */ #endif /* SUNOS41 */ #endif /* SVORPOSIX */ #endif /* HWPARITY */ #endif /* NOHWPARITY */ #ifndef NOSTOPBITS /* Stop-bit selection */ #ifndef STOPBITS #ifdef OS2ORUNIX /* In Unix really this should only be if CSTOPB is defined. */ /* But we don't know that yet. */ #define STOPBITS #else #ifdef TN_COMPORT #define STOPBITS #endif /* TN_COMPORT */ #endif /* OS2ORUNIX */ #endif /* STOPBITS */ #endif /* NOSTOPBITS */ #ifdef UNIX #ifndef NETCMD /* Can SET NETWORK TYPE COMMAND */ #define NETCMD #endif /* NETCMD */ #endif /* UNIX */ /* Pty support, nonportable, available on a case-by-case basis */ #ifndef NOPTY #ifdef NEXT /* NeXTSTEP (tested on 3.1)*/ #define NETPTY #else #ifdef CK_SCOV5 /* SCO OSR5 (tested on 5.0.5)*/ #define NETPTY #else #ifdef QNX /* QNX (tested on 4.25) */ #define NETPTY #else #ifdef SINIX /* Sinix (tested on 5.42) */ #define NETPTY #else #ifdef DGUX540 /* DG/UX 5.4++ (tested on 5.4R4.11) */ #define NETPTY #else #ifdef OSF32 /* Digital Unix 3.2 */ #define NETPTY #else #ifdef OSF40 /* Digital Unix 4.0 / Tru64 */ #define NETPTY #else #ifdef IRIX60 /* IRIX 6.0 (not earlier) */ #define NETPTY #else #ifdef HPUX10 /* HPUX 10.00 or later */ #define NETPTY #ifndef HAVE_PTYTRAP #define HAVE_PTYTRAP #endif /* HAVE_PTYTRAP */ #else #ifdef HPUX9 /* HPUX 9.00 (not earlier) */ #define NETPTY #ifndef HAVE_PTYTRAP #define HAVE_PTYTRAP #endif /* HAVE_PTYTRAP */ #else #ifdef BSD44 /* BSD44, {Net,Free,Open}BSD */ #define NETPTY #else #ifdef BSDI /* BSDI/OS (tested in 4) */ #define NETPTY #else #ifdef SOLARIS /* Solaris (tested in 2.5) */ #define NETPTY #else #ifdef UW7 /* Unixware 7 */ #define NETPTY #else #ifdef SUNOS41 /* SunOS (tested in 4.1.3) */ #define NETPTY #else #ifdef AIX41 /* AIX 4.1 and later */ #define NETPTY #else #ifdef LINUX /* Linux */ #define NETPTY #endif /* LINUX */ #endif /* AIX41 */ #endif /* SUNOS41 */ #endif /* UW7 */ #endif /* SOLARIS */ #endif /* BSDI */ #endif /* BSD44 */ #endif /* HPUX9 */ #endif /* HPUX10 */ #endif /* IRIX60 */ #endif /* OSF40 */ #endif /* OSF32 */ #endif /* DGUX540 */ #endif /* SINIX */ #endif /* QNX */ #endif /* CK_SCOV5 */ #endif /* NEXT */ #else /* NOPTY */ #ifdef NETPTY #undef NETPTY #endif /* NETPTY */ #endif /* NOPTY */ #ifdef NETPTY /* NETCMD required for NETPTY */ #ifndef NETCMD #define NETCMD #endif /* NETCMD */ #endif /* NETPTY */ #ifndef CK_UTSNAME /* Can we call uname()? */ #ifdef VMS #define CK_UTSNAME #else #ifdef OS2 #define CK_UTSNAME #else #ifdef POSIX /* It's in POSIX.1 */ #define CK_UTSNAME #else #ifdef SUNOS41 /* It's in SunOS 4.1 */ #define CK_UTSNAME #else #ifdef AIXRS /* It's in AIX */ #define CK_UTSNAME #else #ifdef SVR4 /* It's in SVR4 (but not SVR3) */ #define CK_UTSNAME #else #ifdef HPUX /* It's in HP-UX 5.00 and later */ #define CK_UTSNAME #else #ifdef OSF /* It's in OSF/1 / Digital UNIX */ #define CK_UTSNAME #else #ifdef CK_SCOV5 #define CK_UTSNAME #endif /* CK_SCOV5 */ #endif /* OSF */ #endif /* HPUX */ #endif /* SVR4 */ #endif /* AIXRS */ #endif /* SUNOS41 */ #endif /* POSIX */ #endif /* OS2 */ #endif /* VMS */ #endif /* CK_UTSNAME */ /* This section for anything that might use floating-point */ /* If the following causes trouble use -DFLOAT=float on the command line */ #ifdef NOSPL #ifdef FNFLOAT #undef FNFLOAT #endif /* FNFLOAT */ #ifdef CKFLOAT #undef CKFLOAT #endif /* CKFLOAT */ #endif /* NOSPL */ #ifndef NOFLOAT #ifndef CKFLOAT #ifdef __alpha /* Don't use double on 64-bit platforms -- bad things happen */ #define CKFLOAT float #define CKFLOAT_S "float" #else #define CKFLOAT double #define CKFLOAT_S "double" #endif /* __alpha */ #endif /* CKFLOAT */ #ifndef NOGFTIMER /* Floating-point timers */ #ifndef GFTIMER #ifdef UNIX /* For UNIX */ #define GFTIMER #endif /* UNIX */ #ifdef VMS /* VMS */ #ifndef OLD_VMS /* 5.0 and later */ #define GFTIMER #endif /* OLD_VMS */ #endif /* VMS */ #ifdef OS2 /* And K95 */ #define GFTIMER #endif /* OS2 */ #ifdef STRATUS /* And Stratus VOS */ #define GFTIMER #endif /* STRATUS */ #endif /* GFTIMER */ #endif /* NOGFTIMER */ #ifndef NOSPL #ifndef FNFLOAT /* Floating-point math functions */ #ifdef VMS /* defined by default in VMS */ #define FNFLOAT #else #ifdef OS2 /* and K95 */ #define FNFLOAT #endif /* OS2 */ #endif /* VMS */ #endif /* FNFLOAT */ #endif /* NOSPL */ #else /* NOFLOAT is defined */ #ifdef CKFLOAT #undef CKFLOAT #endif /* CKFLOAT */ #ifdef GFTIMER #undef GFTIMER #endif /* GFTIMER */ #ifdef FNFLOAT #undef FNFLOAT #endif /* FNFLOAT */ #endif /* NOFLOAT */ #ifdef GFTIMER /* Fraction of second to use when */ #ifndef GFMINTIME /* elapsed time is <= 0 */ #define GFMINTIME 0.005 #endif /* GFMINTIME */ #endif /* GFTIMER */ #ifndef CKCMAI extern long ztmsec, ztusec; /* Fraction of sec of current time */ #endif /* CKCMAI */ #ifndef NOUNPREFIXZERO /* Allow unprefixing of NUL (0) */ #ifndef UNPREFIXZERO /* in file-transfer packets */ #define UNPREFIXZERO #endif /* UNPREFIXZERO */ #endif /* NOUNPREFIXZERO */ #ifdef CK_SMALL #define NOCAL /* Calibrate */ #endif /* CK_SMALL */ #ifndef NOPATTERNS /* Filetype matching patterns */ #ifndef PATTERNS #ifndef VMS #ifndef CK_SMALL #define PATTERNS #endif /* CK_SMALL */ #endif /* VMS */ #endif /* PATTERNS */ #endif /* NOPATTERNS */ #ifndef NOCAL #ifndef CALIBRATE #define CALIBRATE #endif /* CALIBRATE */ #else #ifdef CALIBRATE #undef CALIBRATE #endif /* CALIBRATE */ #endif /* NOCAL */ #ifndef NORECURSE /* Recursive directory traversal */ #ifndef RECURSIVE #ifdef VMS #define RECURSIVE #else #ifdef OS2ORUNIX #ifndef CK_SMALL #define RECURSIVE #endif /* CK_SMALL */ #else #ifdef STRATUS #define RECURSIVE #else #ifdef OSK #define RECURSIVE #endif /* OSK */ #endif /* STRATUS */ #endif /* OS2ORUNIX */ #endif /* VMS */ #endif /* RECURSIVE */ #endif /* NORECURSE */ #ifndef CK_SMALL /* Enable file-transfer tuning code */ #ifndef CKTUNING /* in which more code is added */ #ifndef NOTUNING /* to avoid function calls, etc */ #define CKTUNING #endif /* NOTUNING */ #endif /* CKTUNING */ #endif /* CK_SMALL */ #ifndef NOURL /* Parse URLs in SET HOST, etc */ #define CK_URL #define NO_FTP_AUTH /* No auth "ftp" / "anonymous" */ #endif /* NOURL */ #ifndef NOTRIGGER #ifndef CK_TRIGGER /* Trigger string to exit CONNECT */ #ifdef OS2ORUNIX /* OK for UNIX and K95 */ #define CK_TRIGGER #else #ifdef VMS /* and VMS */ #define CK_TRIGGER #else #ifdef datageneral /* and AOS/VS */ #define CK_TRIGGER #endif /* datageneral */ #endif /* OS2ORUNIX */ #endif /* VMS */ #endif /* CK_TRIGGER */ #endif /* NOTRIGGER */ #ifdef CK_TRIGGER #define TRIGGERS 8 /* How many triggers allowed */ #endif /* CK_TRIGGER */ #ifndef XLIMITS /* CONNECT limits */ #ifdef OS2 #define XLIMITS #endif /* OS2 */ #endif /* XLIMITS */ #ifdef NOFRILLS #ifndef NOBROWSER #define NOBROWSER #endif /* NOBROWSER */ #ifndef NOFTP #define NOFTP #endif /* NOFTP */ #endif /* NOFRILLS */ #ifndef NOHTTP /* HTTP features need... */ #ifdef NOICP /* an interactive command parser */ #define NOHTTP #endif /* NOICP */ #ifndef VMS #ifndef OS2ORUNIX /* K95 or UNIX (because of */ #define NOHTTP /* time functions, time_t, etc) */ #endif /* OS2ORUNIX */ #endif /* VMS */ #endif /* NOHTTP */ #ifndef NONET #ifdef TCPSOCKET /* The HTTP code is not very portable, so it must be asked for with -DCKHTTP */ #ifndef NOHTTP #ifndef CKHTTP #ifdef SUNOS4 /* We can use it in SunOS */ #define CKHTTP #endif /* SUNOS4 */ #ifdef SOLARIS /* And in Solaris */ #define CKHTTP #endif /* SOLARIS */ #ifdef LINUX /* And Linux */ #define CKHTTP #endif /* LINUX */ #ifdef HPUX10 /* And HP-UX 10 and above */ #define CKHTTP #endif /* HPUX10 */ #ifdef OS2 /* And in K-95 */ #define CKHTTP #endif /* OS2 */ #ifdef AIX41 /* In AIX 4.1 and higher */ #define CKHTTP #endif /* AIX41 */ #ifdef UNIXWARE /* In Unixware 2.1 and higher */ #define CKHTTP /* and probably also in 1.x and 2.0 */ #endif /* UNIXWARE */ #ifdef CK_SCOV5 #define CKHTTP #endif /* CK_SCOV5 */ #ifdef OSF /* And in OSF Digital UNIX/True 64 */ #define CKHTTP #endif /* OSF */ #ifdef ultrix /* And in Ultrix Mips */ #ifdef mips #define CKHTTP #endif /* mips */ #endif /* ultrix */ #ifdef __NetBSD__ /* NetBSD */ #define CKHTTP #endif /* __NetBSD__ */ #ifdef __FreeBSD__ #define CKHTTP #endif /* __FreeBSD__ */ #ifdef __OpenBSD__ #define CKHTTP #endif /* __OpenBSD__ */ /* Add more here... */ #endif /* CKHTTP */ #ifndef CKHTTP /* If CKHTTP not defined yet */ #define NOHTTP /* then define NOHTTP */ #endif /* CKHTTP */ #endif /* NOHTTP */ #ifdef NETCONN /* Special "network" types... */ #ifndef NOLOCAL #ifdef OS2 #ifndef NETFILE #define NETFILE #endif /* NETFILE */ #ifndef NOPUSH #ifndef NETCMD #define NETCMD #endif /* NETCMD */ #endif /* NOPUSH */ #ifdef NT #ifndef NETDLL #define NETDLL #endif /* NETDLL */ #endif /* NT */ #endif /* OS2 */ #endif /* NOLOCAL */ #endif /* NETCONN */ #ifndef NOFTP #ifndef SYSFTP #ifndef NEWFTP #ifdef OS2ORUNIX #define NEWFTP #endif /* OS2ORUNIX */ #endif /* NEWFTP */ #endif /* SYSFTP */ #endif /* NOFTP */ #ifndef NOFTP #ifdef NEWFTP #ifdef SYSFTP #undef SYSFTP #endif /* SYSFTP */ #else /* NEWFTP */ #ifndef SYSFTP #define SYSFTP #endif /* SYSFTP */ #endif /* NEWFTP */ #else /* NOFTP */ #ifdef NEWFTP #undef NEWFTP #endif /* NEWFTP */ #ifdef SYSFTP #undef SYSFTP #endif /* SYSFTP */ #endif /* NOFTP */ #ifndef NOBROWSER #ifdef UNIX #ifndef BROWSER #ifndef NOPUSH #define BROWSER #endif /* NOPUSH */ #endif /* BROWSER */ #endif /* UNIX */ #ifdef OS2 #ifndef BROWSER #ifndef NOPUSH #define BROWSER #endif /* NOPUSH */ #endif /* BROWSER */ #endif /* OS2 */ #else #ifdef BROWSER #undef BROWSER #endif /* BROWSER */ #endif /* NOBROWSER */ #else /* TCPSOCKET */ #ifndef NOHTTP /* HTTP requires TCPSOCKET */ #define NOHTTP #endif /* NOHTTP */ #endif /* TCPSOCKET */ #endif /* NONET */ #ifdef TCPSOCKET #ifndef NOCKGETFQHOST #ifdef __ia64__ #define NOCKGETFQHOST #else /* __ia64__ */ #ifdef SV68 #define NOCKGETFQHOST #else #ifdef HPUXPRE65 #define NOCKGETFQHOST #endif /* HPUXPRE65 */ #endif /* SV68 */ #endif /* __ia64 */ #endif /* NOCKGETFQHOST */ /* Regarding System V/68 (SV68) (from Gerry Belanger, Oct 2002): 1) The gethostbyname() appears to return the actual host IP address in the hostent struct, instead of the expected pointer to the address. Hence the bogus address in the bcopy/memcopy. This is despite the header agreeing with our expectations. 2) the expected argument swap between bcopy and memcopy did not happen. What grief this might cause, I know not. */ #endif /* TCPSOCKET */ #ifdef TCPSOCKET #ifdef OS2ONLY #ifndef NOSOCKS #define NOSOCKS #endif /* NOSOCKS */ #endif /* OS2ONLY */ #ifdef NOSOCKS #ifdef CK_SOCKS #undef CK_SOCKS #endif /* CK_SOCKS */ #ifdef CK_SOCKS5 #undef CK_SOCKS5 #endif /* CK_SOCKS5 */ #else /* NOSOCKS */ #ifdef NT #ifndef CK_SOCKS #define CK_SOCKS #endif /* CK_SOCKS */ #endif /* NT */ #ifdef CK_SOCKS5 /* CK_SOCKS5 implies CK_SOCKS */ #ifndef CK_SOCKS #define CK_SOCKS #endif /* CK_SOCKS */ #endif /* CK_SOCKS5 */ #endif /* NOSOCKS */ #endif /* TCPSOCKET */ #ifdef TNCODE #ifndef CK_AUTHENTICATION #ifdef OS2 #ifdef _M_PPC #define NO_KERBEROS #define NO_SRP #else /* _M_PPC */ #ifndef NO_SSL #define CK_SSL #define SSLDLL #endif /* NO_SSL */ #endif /* _M_PPC */ #ifndef NO_KERBEROS #define CK_KERBEROS #define KRB4 #define KRB5 #define KRB524 #define KRB524_CONV #ifdef NT #ifndef _M_PPC #ifndef _M_ALPHA #ifndef NO_SSL_KRB5 #define SSL_KRB5 #endif /* NO_SSL_KRB5 */ #endif /* _M_ALPHA */ #endif /* _M_PPC */ #endif /* NT */ #endif /* NO_KERBEROS */ #ifndef NO_SRP #define CK_SRP #endif /* NO_SRP */ #define CK_AUTHENTICATION #endif /* OS2 */ #endif /* CK_AUTHENTICATION */ #ifdef CK_AUTHENTICATION /* Encryption must have Auth */ #ifndef CK_ENCRYPTION #ifndef NO_ENCRYPTION #ifdef OS2 #define CK_ENCRYPTION #define CK_DES #define CK_CAST #endif /* OS2 */ #endif /* NO_ENCRYPTION */ #endif /* CK_ENCRYPTION */ #endif /* CK_AUTHENTICATION */ #ifdef NO_AUTHENTICATION /* Allow authentication to be */ #ifdef CK_AUTHENTICATION /* disabled in NT and OS/2 */ #undef CK_AUTHENTICATION #endif /* CK_AUTHENTICATION */ #ifdef CK_KERBEROS #undef CK_KERBEROS #endif /* CK_KERBEROS */ #ifdef CK_SRP #undef CK_SRP #endif /* CK_SRP */ #ifdef CK_ENCRYPTION #undef CK_ENCRYPTION #endif /* CK_ENCRYPTION */ #endif /* NO_AUTHENTICATION */ #ifdef NO_ENCRYPTION /* Allow encryption to be */ #ifdef CK_ENCRYPTION /* disabled in NT and OS/2 */ #undef CK_ENCRYPTION #endif /* CK_ENCRYPTION */ #endif /* NO_ENCRYPTION */ #ifdef CK_KERBEROS /* Disable funcs not yet supported with Heimdal */ #ifdef KRB5 #ifndef HEIMDAL #define KRB5_U2U #endif /* HEIMDAL */ #endif /* KRB5 */ #endif /* CK_KERBEROS */ /* SSH section. NOSSH disables any form of SSH support. If NOSSH is not defined (or implied by NONET, NOLOCAL, etc) then SSHBUILTIN is defined for K95 and SSHCMD is defined for UNIX. Then, if either SSHBUILTIN or SSHCMD is defined, ANYSSH is also defined. */ #ifndef NOSSH #ifndef NO_SSL #ifdef OS2ONLY #define NOSSH #endif /* OS2ONLY */ #ifdef NT #ifndef CK_SSL #define NOSSH #endif /* CK_SSL */ #endif /* NT */ #else /* NO_SSL */ #define NOSSH #endif /* NO_SSL */ #endif /* NOSSH */ #ifdef NOSSH /* NOSSH */ #ifdef SSHBUILTIN /* undefines any SSH selctors */ #undef SSHBUILTIN #endif /* SSHBUILTIN */ #ifdef SFTP_BUILTIN #undef SFTP_BUILTIN #endif /* SFTP_BUILTIN */ #ifdef SSHCMD #undef SSHCMD #endif /* SSHCMD */ #ifdef ANYSSH #undef ANYSSH #endif /* ANYSSH */ #else /* Not NOSSH */ #ifndef NOLOCAL #ifdef OS2 #ifndef SSHBUILTIN #define SSHBUILTIN #endif /* SSHBUILTIN */ #else /* Not OS2 */ #ifdef UNIX #ifndef SSHCMD #ifdef NETPTY #ifndef NOPUSH #define SSHCMD #endif /* NOPUSH */ #endif /* NETPTY */ #endif /* SSHCMD */ #endif /* UNIX */ #endif /* OS2 */ #ifndef ANYSSH #ifdef SSHBUILTIN #define ANYSSH #ifdef SSHCMD #undef SSHCMD #endif /* SSHCMD */ #else /* SSHBUILTIN */ #ifdef SSHCMD #define ANYSSH #endif /* SSHCMD */ #endif /* SSHBUILTIN */ #endif /* ANYSSH */ #endif /* NOLOCAL */ #endif /* NOSSH */ /* This is in case #ifdef SSH is used anywhere in the K95 modules */ #ifdef OS2 #ifdef SSHBUILTIN #ifndef SSH #define SSH #endif /* SSH */ #endif /* SSHBUILTIN */ #endif /* OS2 */ #ifdef CK_AUTHENTICATION #define CK_SECURITY #else #ifdef CK_SSL #define CK_AUTHENTICATION #define CK_SECURITY #endif /* CK_SSL */ #endif /* CK_AUTHENTICATION */ /* Environment stuff */ #ifndef OS2ORUNIX #ifndef NOPUTENV #define NOPUTENV #endif /* NOPUTENV */ #endif /* OS2ORUNIX */ #ifndef CK_ENVIRONMENT #ifdef OS2 #define CK_ENVIRONMENT #else #ifdef UNIX #define CK_ENVIRONMENT #else #ifdef STRATUS #define CK_ENVIRONMENT #else #ifdef VMS #define CK_ENVIRONMENT #endif /* VMS */ #endif /* STRATUS */ #endif /* UNIX */ #endif /* OS2 */ #endif /* CK_ENVIRONMENT */ #ifndef NOSNDLOC /* RFC 779 SEND LOCATION */ #ifndef CK_SNDLOC #define CK_SNDLOC #endif /* CK_SNDLOC */ #endif /* NOSNDLOC */ #ifndef NOXDISPLOC /* RFC 1096 XDISPLOC */ #ifndef CK_XDISPLOC #define CK_XDISPLOC #endif /* CK_XDISPLOC */ #endif /* NOXDISPLOC */ #ifndef NOFORWARDX #ifndef NOPUTENV #ifndef NOSELECT #ifndef CK_FORWARD_X #ifdef CK_AUTHENTICATION #ifndef OS2ONLY #define CK_FORWARD_X #endif /* OS2ONLY */ #endif /* CK_AUTHENTICATION */ #endif /* CK_FORWARD_X */ #endif /* NOSELECT */ #endif /* NOPUTENV */ #endif /* NOFORWARDX */ #ifndef NO_COMPORT #ifdef TCPSOCKET #ifndef TN_COMPORT #define TN_COMPORT #endif /* TN_COMPORT */ #endif /* TCPSOCKET */ #endif /* NO_COMPORT */ #endif /* TNCODE */ #ifndef NOXFER #ifndef NOCTRLZ /* Allow SET FILE EOF CTRL-Z */ #ifndef CK_CTRLZ #ifdef OS2ORUNIX #define CK_CTRLZ #endif /* OS2ORUNIX */ #endif /* CK_CTRLZ */ #endif /* NOCTRLZ */ #endif /* NOXFER */ #ifndef NOPERMS /* File permissions in A packets */ #ifndef CK_PERMS #ifdef UNIX #define CK_PERMS #else #ifdef VMS #define CK_PERMS #endif /* VMS */ #endif /* UNIX */ #endif /* CK_PERMS */ #endif /* NOPERMS */ #ifdef CK_PERMS #define CK_PERMLEN 24 /* Max length of sys-dependent perms */ #endif /* CK_PERMS */ #ifdef UNIX /* NOSETBUF for everybody */ #ifndef NOSETBUF #ifndef USE_SETBUF /* This is the escape clause */ #define NOSETBUF #endif /* USE_SETBUF */ #endif /* NOSETBUF */ #endif /* UNIX */ #ifndef USE_STRERROR /* Whether to use strerror() */ #ifdef pdp11 #define USE_STRERROR #endif /* pdp11 */ #endif /* USE_STRERROR */ #ifdef VMS /* Features for all VMS builds */ #ifndef NOJC #define NOJC #endif /* NOJC */ #ifndef NOSETBUF #define NOSETBUF #endif /* NOSETBUF */ #ifndef DYNAMIC #define DYNAMIC #endif /* DYNAMIC */ #ifndef NOCURSES #ifndef CK_CURSES #define CK_CURSES #endif /* CK_CURSES */ #endif /* NOCURSES */ #endif /* VMS */ #ifndef NOCKTIMERS /* Dynamic timeouts */ #ifndef CK_TIMERS #define CK_TIMERS #endif /* CK_TIMERS */ #endif /* NOCKTIMERS */ #define CK_SPEED /* Control-prefix removal */ #ifdef NOCKSPEED #undef CK_SPEED #endif /* NOCKSPEED */ #ifndef NOCKXXCHAR #ifndef CKXXCHAR #ifdef UNIX #define CKXXCHAR #else #ifdef OS2 #define CKXXCHAR #endif /* OS2 */ #endif /* UNIX */ #endif /* CKXXCHAR */ #endif /* NOCKXXCHAR */ #ifdef MAC /* For Macintosh, no escape */ #define NOPUSH /* to operating system */ #endif /* MAC */ /* Systems where we can call zmkdir() to create directories. */ #ifndef CK_MKDIR #ifndef NOMKDIR #ifdef UNIX #ifndef pdp11 #define CK_MKDIR #endif /* pdp11 */ #endif /* UNIX */ #ifdef OS2 #define CK_MKDIR #endif /* OS2 */ #ifdef VMS #define CK_MKDIR #endif /* VMS */ #ifdef STRATUS #define CK_MKDIR #endif /* STRATUS */ #ifdef OSK #define CK_MKDIR #endif /* OSK */ #ifdef datageneral #define CK_MKDIR #endif /* datageneral */ #endif /* CK_MKDIR */ #endif /* NOMKDIR */ #ifdef NOMKDIR /* Allow for command-line override */ #ifdef CK_MKDIR #undef CK_MKDIR #endif /* CK_MKDIR */ #endif /* NOMKDIR */ /* Systems for which we can enable the REDIRECT command automatically */ /* As of 6.0.193, it should work for all UNIX... */ #ifndef NOREDIRECT #ifndef CK_REDIR #ifdef UNIX #define CK_REDIR #endif /* UNIX */ #ifdef OS2 /* As well as OS/2 and friends... */ #define CK_REDIR #endif /* OS2 */ #endif /* CK_REDIR */ #endif /* NOREDIRECT */ #ifdef NOPUSH /* But... REDIRECT command is not */ #ifdef CK_REDIR /* allowed if NOPUSH is defined. */ #undef CK_REDIR #endif /* CK_REDIR */ #ifdef NETCMD /* Nor is SET NET COMMAND */ #undef NETCMD #endif /* NETCMD */ #ifdef NETPTY #undef NETPTY #endif /* NETPTY */ #endif /* NOPUSH */ #ifndef PEXITSTAT /* \v(pexitstat) variable defined */ #ifdef OS2ORUNIX #define PEXITSTAT #else #ifdef VMS #define PEXITSTAT #endif /* VMS */ #endif /* OS2ORUNIX */ #endif /* PEXITSTAT */ /* The following allows automatic enabling of REDIRECT to be overridden... */ #ifdef NOREDIRECT #ifdef NETCMD #undef NETCMD #endif /* NETCMD */ #ifdef NETPTY #undef NETPTY #endif /* NETPTY */ #ifdef CK_REDIR #undef CK_REDIR #endif /* CK_REDIR */ #endif /* NOREDIRECT */ #ifdef NONETCMD #ifdef NETCMD #undef NETCMD #endif /* NETCMD */ #ifdef NETPTY #undef NETPTY #endif /* NETPTY */ #endif /* NONETCMD */ #ifdef CK_REDIR _PROTOTYP( int ttruncmd, (char *) ); #endif /* CK_REDIR */ /* Use built-in DIRECTORY command */ #ifndef NOMYDIR #ifndef DOMYDIR #ifdef UNIXOROSK #define DOMYDIR #else #ifdef OS2 #define DOMYDIR #else #ifdef VMS #define DOMYDIR #endif /* VMS */ #endif /* OS2 */ #endif /* UNIXOROSK */ #endif /* DOMYDIR */ #endif /* NOMYDIR */ /* Sending from and receiving to commands/pipes */ #ifndef PIPESEND #ifdef UNIX #define PIPESEND #endif /* UNIX */ #ifdef OS2 #define PIPESEND #endif /* OS2 */ #endif /* PIPESEND */ #ifdef PIPESEND #ifdef NOPIPESEND #undef PIPESEND #endif /* NOPIPESEND */ #ifdef NOPUSH #undef PIPESEND #endif /* NOPUSH */ #endif /* PIPESEND */ #ifdef NOPUSH #ifdef BROWSER #undef BROWSER #endif /* BROWSER */ #endif /* NOPUSH */ /* Versions where we support the RESEND command */ #ifndef NOXFER #ifndef NORESEND #ifndef CK_RESEND #ifdef UNIX #ifndef pdp11 #define CK_RESEND #endif /* pdp11 */ #endif /* UNIX */ #ifdef VMS #define CK_RESEND #endif /* VMS */ #ifdef OS2 #define CK_RESEND #endif /* OS2 */ #ifdef AMIGA #define CK_RESEND #endif /* AMIGA */ #ifdef datageneral #define CK_RESEND #endif /* datageneral */ #ifdef STRATUS #define CK_RESEND #endif /* STRATUS */ #ifdef OSK #define CK_RESEND #endif /* OSK */ #endif /* CK_RESEND */ #endif /* NORESEND */ #endif /* NOXFER */ /* Systems implementing "Doomsday Kermit" protocol ... */ #ifndef DOOMSDAY #ifdef UNIX #define DOOMSDAY #else #ifdef VMS #define DOOMSDAY #else #ifdef OS2 #define DOOMSDAY #else #ifdef STRATUS #define DOOMSDAY #endif /* STRATUS */ #endif /* OS2 */ #endif /* VMS */ #endif /* UNIX */ #endif /* DOOMSDAY */ /* Systems where we want the Thermometer to be used for fullscreen */ #ifdef OS2 #ifndef CK_PCT_BAR #define CK_PCT_BAR #endif /* CK_PCT_BAR */ #endif /* OS2 */ /* Systems where we have a REXX command */ #ifdef OS2 #ifdef __32BIT__ #ifndef NOREXX #define CK_REXX #endif /* NOREXX */ #endif /* __32BIT__ */ #endif /* OS2 */ /* Platforms that have a ZCHKPID function */ #ifdef OS2ORUNIX #define ZCHKPID #endif /* OS2ORUNIX */ #ifndef ZCHKPID /* If we can't check pids then we have treat all pids as active & valid. */ #define zchkpid(x) 1 #endif /* ZCHKPID */ /* Systems that have a ZRENAME function */ #define ZRENAME /* They all do */ /* Systems that have a ZCOPY function */ #ifndef ZCOPY #ifdef VMS #define ZCOPY #else #ifdef OS2 #define ZCOPY #else #ifdef UNIX #define ZCOPY #else #ifdef STRATUS #define ZCOPY #endif /* STRATUS */ #endif /* UNIX */ #endif /* OS2 */ #endif /* VMS */ #endif /* ZCOPY */ /* Systems that have ttgwsiz() (they all should but they don't) */ #ifndef NOTTGWSIZ #ifndef CK_TTGWSIZ #ifdef UNIX #define CK_TTGWSIZ #else #ifdef VMS #define CK_TTGWSIZ #else #ifdef OS2 #define CK_TTGWSIZ #else #ifdef OSK #define CK_TTGWSIZ #endif /* OSK */ #endif /* OS2 */ #endif /* VMS */ #endif /* UNIX */ #endif /* CK_TTGWSIZ */ #endif /* NOTTGWSIZ */ #ifdef NOTTGWSIZ #ifdef CK_TTGWSIZ #undef CK_TTGWSIZ #endif /* CK_TTGWSIZ */ #endif /* NOTTGWSIZ */ #ifdef OS2 /* OS/2 C-Kermit features not available in 16-bit version... */ #ifdef OS2ONLY #ifndef __32BIT__ #ifndef NOLOCAL #ifdef PCFONTS /* PC Font support */ #undef PCFONTS #endif /* PCFONTS */ #ifdef NPIPE /* Named Pipes communication */ #undef NPIPE #endif /* NPIPE */ #ifdef CK_NETBIOS /* NETBIOS communication */ #undef CK_NETBIOS #endif /* CK_NETBIOS */ #ifdef OS2MOUSE /* Mouse */ #undef OS2MOUSE #endif /* OS2MOUSE */ #ifdef OS2PM /* Presentation Manager */ #undef OS2PM #endif /* OS2PM */ #endif /* NOLOCAL */ #ifdef CK_REXX /* Rexx */ #undef CK_REXX #endif /* CK_REXX */ #endif /* __32BIT__ */ #endif /* OS2ONLY */ /* OS/2 C-Kermit features not available in Windows NT version... */ #ifdef NT #ifdef PCFONTS /* PC Font support */ #undef PCFONTS #endif /* PCFONTS */ #ifdef OS2PM /* Presentation Manager */ #undef OS2PM #endif /* OS2PM */ #ifdef CK_REXX /* Rexx */ #undef CK_REXX #endif /* CK_REXX */ #endif /* NT */ #endif /* OS2 */ /* Systems that have select(). This is used for both msleep() and for read-buffer checking in in_chk(). */ #define CK_SLEEPINT 250 /* milliseconds - set this to something that divides evenly into 1000 */ #ifndef SELECT #ifndef NOSELECT #ifdef __linux__ #define SELECT #else #ifdef SUNOS4 #define SELECT #else #ifdef NEXT #define SELECT #else #ifdef RTAIX #define SELECT #else #ifdef HPUX /* Not really. I think it's only in HP-UX 7.0 and later, except it's also in earlier versions that have TCP/IP installed. Override this default in particular HP-UX makefile entries by adding -DNOSELECT, as in (e.g.) the HP-UX 6.5 ones. */ #define SELECT #else #ifdef AIXRS #define SELECT #else #ifdef BSD44 #define SELECT #else #ifdef BSD4 #define SELECT #else #ifdef OXOS #define SELECT #else #ifdef OS2 #define SELECT #else #ifdef BEBOX #define SELECT #endif /* BEBOX */ #endif /* OS2 */ #endif /* OXOS */ #endif /* BSD4 */ #endif /* BSD44 */ #endif /* AIXRS */ #endif /* HPUX */ #endif /* RTAIX */ #endif /* NEXT */ #endif /* __linux__ */ #endif /* SUNOS4 */ #endif /* NOSELECT */ #endif /* SELECT */ /* The following section moved here from ckcnet.h in 6.1 because select() is now used for non-networking purposes. */ /* On HP-9000/500 HP-UX 5.21 this stuff is not defined in any header file */ #ifdef hp9000s500 #ifndef NEEDSELECTDEFS #define NEEDSELECTDEFS #endif /* NEEDSELECTDEFS */ #endif /* hp9000s500 */ #ifdef NEEDSELECTDEFS typedef long fd_mask; #ifndef NBBY #define NBBY 8 #endif /* NBBY */ #ifndef FD_SETSIZE #define FD_SETSIZE 32 #endif /* FD_SETSIZE */ #ifndef NFDBITS #define NFDBITS (sizeof(fd_mask) * NBBY) #endif /* NFDBITS */ #ifndef howmany #define howmany(x,y) (((x)+((y)-1))/(y)) #endif /* howmany */ typedef struct fd_set { fd_mask fds_bits[howmany(FD_SETSIZE, NFDBITS)]; } fd_set; #ifndef FD_SET #define FD_SET(n,p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS))) #endif /* FD_SET */ #ifndef FD_CLR #define FD_CLR(n,p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS))) #endif /* FD_CLR */ #ifndef FD_ISSET #define FD_ISSET(n,p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS))) #endif /* FD_ISSET */ #ifndef FD_COPY #define FD_COPY(f,t) (bcopy(f,t,sizeof(*(f))) #endif /* FD_COPY */ #ifndef FD_ZERO #define FD_ZERO(p) bzero((char *)(p),sizeof(*(p))) #endif /* FD_ZERO */ #endif /* NEEDSELECTDEFS */ /* CK_NEED_SIG is defined if the system cannot check the console to to see if characters are waiting. This is used during local-mode file transfer to interrupt the transfer, refresh the screen display, etc. If CK_NEED_SIG is defined, then file-transfer interruption characters have to be preceded a special character, e.g. the SIGQUIT character. CK_NEED_SIG should be defined if the conchk() function is not operational. */ #ifdef NOPOLL /* For overriding CK_POLL definition */ #ifdef CK_POLL #undef CK_POLL #endif /* CK_POLL */ #endif /* NOPOLL */ #ifndef CK_POLL /* If we don't have poll() */ #ifndef RDCHK /* And we don't have rdchk() */ #ifndef SELECT /* And we don't have select() */ #ifdef ATTSV #ifndef aegis #ifndef datageneral #ifndef OXOS #define CK_NEED_SIG #endif /* OXOS */ #endif /* datageneral */ #endif /* aegis */ #endif /* ATTSV */ #ifdef POSIX #ifndef CK_NEED_SIG #define CK_NEED_SIG #endif /* CK_NEED_SIG */ #endif /* POSIX */ #endif /* SELECT */ #endif /* RDCHK */ #endif /* CK_POLL */ #ifdef HPUX /* HP-UX has select() */ #ifdef CK_NEED_SIG #undef CK_NEED_SIG #endif /* CK_NEED_SIG */ #endif /* HPUX */ #ifdef AIXRS /* AIX has select() */ #ifdef CK_NEED_SIG #undef CK_NEED_SIG #endif /* CK_NEED_SIG */ #endif /* AIXRS */ #ifdef BSD44 /* 4.4BSD has FIONREAD */ #ifdef CK_NEED_SIG #undef CK_NEED_SIG #endif /* CK_NEED_SIG */ #endif /* BSD44 */ #ifdef QNX /* QNX has FIONREAD and select() */ #ifdef CK_NEED_SIG #undef CK_NEED_SIG #endif /* CK_NEED_SIG */ #endif /* QNX */ #ifdef COHERENT #ifndef NOTIMEZONE #define NOTIMEZONE #endif /* NOTIMEZONE */ #endif /* COHERENT */ #ifdef UNIX #ifndef HAVE_TZ /* Can we use struct timezone? */ #ifndef NOTIMEZONE #ifdef PTX #define NOTIMEZONE #else #ifndef SELECT #ifdef COHERENT #define NOTIMEZONE #else #ifdef BELLV10 #define NOTIMEZONE #endif /* BELLV10 */ #endif /* COHERENT */ #endif /* SELECT */ #endif /* PTX */ #endif /* NOTIMEZONE */ #endif /* HAVE_TZ */ #ifndef NOTIMEVAL /* Can we use struct timeval? */ #ifndef HAVE_TV #define HAVE_TV #endif /* HAVE_TV */ #endif /* NOTIMEVAL */ #ifndef NOTIMEZONE #ifndef HAVE_TZ #define HAVE_TZ #endif /* HAVE_TZ */ #endif /* NOTIMEZONE */ #endif /* UNIX */ #ifdef SCO32 #ifdef HAVE_TV #undef HAVE_TV #endif /* HAVE_TV */ #ifdef HAVE_TZ #undef HAVE_TZ #endif /* HAVE_TZ */ #ifndef NOTIMEVAL #define NOTIMEVAL #endif /* NOTIMEVAL */ #ifndef NOTIMEZONE #define NOTIMEZONE #endif /* NOTIMEZONE */ #endif /* SCO32 */ #ifdef ATT7300 #ifdef HAVE_TV #undef HAVE_TV #endif /* HAVE_TV */ #ifdef HAVE_TZ #undef HAVE_TZ #endif /* HAVE_TZ */ #ifndef NOTIMEVAL #define NOTIMEVAL #endif /* NOTIMEVAL */ #ifndef NOTIMEZONE #define NOTIMEZONE #endif /* NOTIMEZONE */ #endif /* ATT7300 */ /* Automatic parity detection. This actually implies a lot more now: length-driven packet reading, "Doomsday Kermit" IBM Mainframe file transfer through 3270 data streams, etc. */ #ifdef UNIX /* For Unix */ #ifndef NOPARSEN #define PARSENSE #endif /* NOPARSEN */ #endif /* UNIX */ #ifdef VMS /* ... and VMS */ #ifndef NOPARSEN #define PARSENSE #endif /* NOPARSEN */ #ifdef __GNUC__ #define VMSGCC #endif /* __GNUC__ */ #endif /* VMS */ #ifdef MAC /* and Macintosh */ #ifndef NOPARSEN #define PARSENSE #endif /* NOPARSEN */ #endif /* MAC */ #ifdef STRATUS /* and Stratus VOS */ #ifndef NOPARSEN #define PARSENSE #endif /* NOPARSEN */ #endif /* STRATUS */ #ifdef OS2 /* and OS/2, finally */ #ifndef NOPARSEN #define PARSENSE #endif /* NOPARSEN */ #endif /* OS2 */ #ifndef NODYNAMIC /* DYNAMIC is default for UNIX */ #ifndef DYNAMIC /* as of C-Kermit 7.0 */ #ifdef UNIX #define DYNAMIC #endif /* UNIX */ #endif /* DYNAMIC */ #endif /* NODYNAMIC */ #ifdef DYNAMIC /* If DYNAMIC is defined */ #define DCMDBUF /* then also define this. */ #endif /* DYNAMIC */ #ifndef CK_LBRK /* Can send Long BREAK */ #ifdef UNIX /* (everybody but OS-9) */ #define CK_LBRK #endif /* UNIX */ #ifdef VMS #define CK_LBRK #endif /* VMS */ #ifdef datageneral #define CK_LBRK #endif /* datageneral */ #ifdef GEMDOS #define CK_LBRK #endif /* GEMDOS */ #ifdef OS2 #define CK_LBRK #endif /* OS2 */ #ifdef AMIGA #define CK_LBRK #endif /* AMIGA */ #ifdef STRATUS #define CK_LBRK #endif /* STRATUS */ #endif /* CK_LBRK */ /* Carrier treatment */ /* These are defined here because they are shared by the system dependent */ /* and the system independent modules. */ #define CAR_OFF 0 /* Off: ignore carrier always. */ #define CAR_ON 1 /* On: heed carrier always, except during DIAL. */ #define CAR_AUT 2 /* Auto: heed carrier, but only if line is declared */ /* to be a modem line, and only during CONNECT. */ /* And more generically (for use with any ON/OFF/AUTO feature) */ #define CK_OFF 0 #define CK_ON 1 #define CK_AUTO 2 #ifndef NOLOCAL /* Serial interface speeds available. As of C-Kermit 6.1 there is a new method to get the supported speeds, which obviates the need for all the craziness below. At runtime, just call the new ttspdlist() routine to get a list of supported speeds. Then the user interface module can build a keyword table or menu from it. */ #ifndef TTSPDLIST #ifdef UNIX /* For now, only for UNIX */ #ifndef OLINUXHISPEED /* But not systems with hacks for */ #ifndef MINIX /* high speeds, like 110 = 115200 */ #define TTSPDLIST #endif /* MINIX */ #endif /* OLINUXHISPEED */ #else #ifdef VMS #define TTSPDLIST /* VMS gets it too */ #endif /* VMS */ #endif /* UNIX */ #endif /* TTSPDLIST */ #ifndef NODIAL /* Hangup by modem command */ #ifndef NOMDMHUP #ifndef MDMHUP #define MDMHUP #endif /* MDMHUP */ #endif /* NOMDMHUP */ #endif /* NODIAL */ #ifdef NOSPL #ifndef NOLOGDIAL /* Connection log needs mjd(), etc. */ #define NOLOGDIAL #endif /* NOLOGDIAL */ #endif /* NOSPL */ #ifdef pdp11 #define NOLOGDIAL #endif /* pdp11 */ #ifndef NOLOGDIAL /* Connection log */ #ifndef CXLOGFILE #define CXLOGFILE "CX.LOG" /* Default connection log file name */ #endif /* CXLOGFILE */ #ifndef CKLOGDIAL #ifndef CK_SMALL #define CKLOGDIAL #define CXLOGBUFL 1024 /* Connection log record buffer size */ #endif /* CK_SMALL */ #endif /* NOLOGDIAL */ #endif /* CKLOGDIAL */ #endif /* NOLOCAL */ #ifdef NOTTSPDLIST /* Except if NOTTSPDLIST is defined */ #ifdef TTSPDLIST #undef TTSPDLIST #endif /* TTSPDLIST */ #endif /* NOTTSPDLIST */ #ifdef TTSPDLIST _PROTOTYP( long * ttspdlist, (void) ); #else /* TTSPDLIST not defined */ /* We must use a long and convoluted series of #ifdefs that have to be kept in sync with the code in the ck?tio.c module. We assume that everybody supports: 0, 110, 300, 600, 1200, 2400, 4800, and 9600 bps. Symbols for other speeds are defined here. You can also add definitions on the CC command lines. These definitions affect the SET SPEED keyword table, and are not necessarily usable in the system-dependent speed-setting code in the ck?tio.c modules, which depends on system-specific symbols like (in UNIX) B19200. In other words, just defining it doesn't mean it'll work -- you also have to supply the supporting code in ttsspd() and ttgspd() in ck?tio.c. The symbols have the form BPS_xxxx, where xxxx is the speed in bits per second, or (for bps values larger than 9999) thousands of bps followed by K. The total symbol length should be 8 characters or less. Some values are enabled automatically below. You can disable a particular value by defining NOB_xxxx on the CC command line. */ #ifndef NOB_50 #define BPS_50 /* 50 bps */ #endif #ifndef NOB_75 #define BPS_75 /* 75 bps */ #endif #ifndef NOB7512 #ifdef ANYBSD #define BPS_7512 /* 75/1200 Split Speed */ #endif /* ANYBSD */ #endif /* NOB7512 */ #ifndef NOB134 #ifdef SOLARIS25 #define BPS_134 #else #undef BPS_134 /* 134.5 bps (IBM 2741) */ #endif /* BPS_134 */ #endif /* NOB134 */ #ifndef NOB_150 #define BPS_150 /* 150 bps */ #endif #ifndef NOB_200 #define BPS_200 /* 200 bps */ #endif #ifndef NOB_1800 #ifdef MAC #define BPS_1800 /* 1800 bps */ #else #ifdef SOLARIS25 #define BPS_1800 #endif #endif #endif #ifndef NOB_3600 #ifndef SOLARIS25 #define BPS_3600 /* 3600 bps */ #endif #endif #ifndef NOB_7200 #ifndef SOLARIS25 #define BPS_7200 /* 7200 bps */ #endif /* SOLARIS25 */ #endif #ifndef NOB_14K #ifdef BSD44 #define BPS_14K /* 14400 bps */ #else #ifdef OS2 #define BPS_14K #else #ifdef NEXT #define BPS_14K #else #ifdef MAC #define BPS_14K #else #ifdef AMIGA #define BPS_14K #endif /* AMIGA */ #endif /* MAC */ #endif /* NEXT */ #endif /* OS2 */ #endif /* BSD44 */ #endif /* NOB_14K */ #ifndef NOB_19K #define BPS_19K /* 19200 bps */ #endif #ifndef NOB_28K #ifdef BSD44 #define BPS_28K #else #ifdef OS2 #define BPS_28K #else #ifdef NEXT #define BPS_28K /* 28800 bps */ #else #ifdef MAC #define BPS_28K /* 28800 bps */ #endif /* MAC */ #endif /* NEXT */ #endif /* OS2 */ #endif /* BSD44 */ #endif /* NOB_28K */ #ifndef NOB_38K #define BPS_38K /* 38400 bps */ #endif #ifndef NOB_57K #ifdef Plan9 #define BPS_57K #else #ifdef SOLARIS25 #define BPS_57K #else #ifdef VMS #define BPS_57K /* 57600 bps */ #else #ifdef OS2 #define BPS_57K #else #ifdef __linux__ #define BPS_57K #else #ifdef HPUX #define BPS_57K #else #ifdef NEXT #define BPS_57K #else #ifdef __386BSD__ #define BPS_57K #else #ifdef __FreeBSD__ #define BPS_57K #else #ifdef __NetBSD__ #define BPS_57K #else #ifdef MAC #define BPS_57K #else #ifdef QNX #define BPS_57K #else #ifdef BEOSORBEBOX #define BPS_57K #else #ifdef IRIX62 #define BPS_57K #else #ifdef SCO_OSR504 #define BPS_57K #else #ifdef BSDI2 #define BPS_57K #endif /* BSDI2 */ #endif /* SCO_OSR504 */ #endif /* IRIX62 */ #endif /* BEOSORBEBOX */ #endif /* QNX */ #endif /* MAC */ #endif /* __NetBSD__ */ #endif /* __FreeBSD__ */ #endif /* __386BSD__ */ #endif /* NEXT */ #endif /* HPUX */ #endif /* __linux__ */ #endif /* OS2 */ #endif /* VMS */ #endif /* SOLARIS25 */ #endif /* Plan9 */ #endif /* NOB_57K */ #ifndef NOB_76K #ifdef BSDI2 #define BPS_76K #endif /* BSDI2 */ #ifdef Plan9 #define BPS_76K #endif /* Plan9 */ #ifdef SOLARIS25 #define BPS_76K #endif /* SOLARIS25 */ #ifdef VMS #define BPS_76K /* 76800 bps */ #endif /* VMS */ #ifdef OS2 #ifdef __32BIT__ #define BPS_76K #endif /* __32BIT__ */ #endif /* OS2 */ #ifdef QNX #define BPS_76K #endif /* QNX */ #ifdef IRIX62 #define BPS_76K #endif /* IRIX62 */ #ifdef SCO_OSR504 #define BPS_76K #endif /* SCO_OSR504 */ #endif /* NOB_76K */ #ifndef NOB_115K #ifdef BSDI2 #define BPS_115K #endif /* BSDI2 */ #ifdef Plan9 #define BPS_115K #endif /* Plan9 */ #ifdef SOLARIS25 #define BPS_115K #endif /* SOLARIS25 */ #ifdef VMS #define BPS_115K /* 115200 bps */ #else #ifdef QNX #define BPS_115K #else #ifdef HPUX #define BPS_115K #else #ifdef __linux__ #define BPS_115K #else #ifdef __386BSD__ #define BPS_115K #else #ifdef __FreeBSD__ #define BPS_115K #else #ifdef __NetBSD__ #define BPS_115K #else #ifdef OS2 #ifdef __32BIT__ #define BPS_115K #endif /* __32BIT__ */ #else #ifdef BEOSORBEBOX #define BPS_115K #else #ifdef IRIX62 #define BPS_115K #else #ifdef SCO_OSR504 #define BPS_115K #endif /* SCO_OSR504 */ #endif /* IRIX62 */ #endif /* BEOSORBEBOX */ #endif /* OS2 */ #endif /* __NetBSD__ */ #endif /* __FreeBSD__ */ #endif /* __386BSD__ */ #endif /* __linux__ */ #endif /* HPUX */ #endif /* QNX */ #endif /* VMS */ #endif /* NOB_115K */ #ifndef NOB_230K /* 230400 bps */ #ifdef BSDI2 #define BPS_230K #else #ifdef SCO_OSR504 #define BPS_230K #else #ifdef __linux__ #define BPS_230K #else #ifdef SOLARIS25 #define BPS_230K #else #ifdef OS2 #ifdef __32BIT__ #define BPS_230K #endif /* __32BIT__ */ #else #undef BPS_230K #endif /* OS2 */ #endif /* SOLARIS25 */ #endif /* __linux__ */ #endif /* SCO_OSR504 */ #endif /* BSDI2 */ #endif /* NOB_230K */ #ifndef NOB_460K /* 460800 bps */ #ifdef SCO_OSR504 #define BPS_460K #else #ifdef __linux__ #define BPS_460K #else #ifdef OS2 #ifdef __32BIT__ #define BPS_460K #endif /* __32BIT__ */ #else #undef BPS_460K #endif /* __linux__ */ #endif /* SCO_OSR504 */ #endif /* OS2 */ #endif /* NOB_460K */ #ifndef NOB_921K /* 921600 bps */ #ifdef SCO_OSR504 #define BPS_921K #endif /* SCO_OSR504 */ #endif /* NOB_921K */ #ifdef BPS_921K /* Maximum speed defined */ #define MAX_SPD 921600L #else #ifdef BPS_460K #define MAX_SPD 460800L #else #ifdef BPS_230K #define MAX_SPD 230400L #else #ifdef BPS_115K #define MAX_SPD 115200L #else #ifdef BPS_76K #define MAX_SPD 76800L #else #ifdef BPS_57K #define MAX_SPD 57600L #else #ifdef BPS_38K #define MAX_SPD 38400L #else #ifdef BPS_28K #define MAX_SPD 28800L #else #ifdef BPS_19K #define MAX_SPD 19200L #else #ifdef BPS_14K #define MAX_SPD 14400L #else #define MAX_SPD 9600L #endif #endif #endif #endif #endif #endif #endif #endif #endif #endif #endif /* TTSPDLIST */ #ifndef CONGSPD /* Systems that can call congspd() */ #ifdef UNIX #define CONGSPD #endif /* UNIX */ #ifdef VMS #define CONGSPD #endif /* VMS */ #ifdef STRATUS #define CONGSPD #endif /* STRATUS */ #endif /* CONGSPD */ /* Types of flow control available */ #define CK_XONXOFF /* Everybody can do this, right? */ #ifdef AMIGA /* Commodore Amiga */ #define CK_RTSCTS /* has RTS/CTS */ #endif /* AMIGA */ #ifdef SUN4S5 /* SunOS in System V environment */ #define CK_RTSCTS #else /* SunOS 4.0/4.1 in BSD environment */ #ifdef SUNOS4 /* SunOS 4.0+later supports RTS/CTS */ #ifdef SUNOS41 /* Easy in 4.1 and later */ #define CK_RTSCTS #else /* Harder in 4.0 */ #ifndef __GNUC__ /* (see tthflow() in ckutio.c) */ #ifndef GNUC #define CK_RTSCTS /* Only if not using GNU gcc */ #endif /* __GNUC__ */ #endif /* GNUC */ #endif /* SUNOS41 */ #endif /* SUNOS4 */ #endif /* SUN4S5 */ #ifdef BSD44 /* And in 4.4 BSD, including BSDI */ #define CK_RTSCTS #endif /* BSD44 */ #ifdef TERMIOX /* Sys V R4 */ #ifndef CK_RTSCTS #define CK_RTSCTS #endif /* CK_RTSCTS */ #ifndef CK_DTRCD #define CK_DTRCD #endif /* CK_DTRCD */ #else #ifdef STERMIOX /* Sys V R4 */ #ifndef CK_RTSCTS #define CK_RTSCTS #endif /* CK_RTSCTS */ #ifndef CK_DTRCD #define CK_DTRCD #endif /* CK_DTRCD */ #endif /* STERMIOX */ #endif /* TERMIOX */ #ifdef OXOS /* Olivetti X/OS R2 struct termios */ #define CK_RTSCTS /* Ditto. */ #define CK_DTRCD #endif /* OXOS */ #ifdef AIXRS /* RS/6000 with AIX 3.x */ #define CK_RTSCTS /* Has its own peculiar method... */ #endif /* AIXRS */ #ifdef __linux__ /* Linux */ #define CK_RTSCTS #endif /* __linux__ */ /* Hardware flow control is not defined in POSIX.1. Nevertheless, a certain style API for hardware flow control, using tcsetattr() and the CRTSCTS bit(s), seems to be gaining currency on POSIX-based UNIX systems. The following code defines the symbol POSIX_CRTSCTS for such systems. */ #ifdef CK_RTSCTS #ifdef __bsdi__ /* BSDI, a.k.a. BSD/386 */ #define POSIX_CRTSCTS #endif /* __bsdi__ */ #ifdef __linux__ /* Linux */ #define POSIX_CRTSCTS #endif /* __linux__ */ #ifdef __NetBSD__ /* NetBSD */ #define POSIX_CRTSCTS #endif /* __NetBSD__ */ #ifdef __OpenBSD__ #define POSIX_CRTSCTS #endif /* __OpenBSD__ */ #ifdef BEOSORBEBOX /* BeBOX */ #define POSIX_CRTSCTS /* BEBOX defines CRTSFL as (CTSFLOW & RTSFLOW) */ #define CRTSCTS CRTSFL #endif /* BEOSORBEBOX */ #ifdef IRIX52 /* IRIX 5.2 and later */ #define POSIX_CRTSCTS #define CRTSCTS CNEW_RTSCTS /* See */ #endif /* IRIX52 */ #endif /* CK_RTSCTS */ /* Implementations that have implemented the ttsetflow() function. */ #ifndef CK_TTSETFLOW #ifdef UNIX #define CK_TTSETFLOW #endif /* UNIX */ #ifdef OS2 #define CK_TTSETFLOW #endif /* OS2 */ #endif /* CK_TTSETFLOW */ #ifdef CK_TTSETFLOW _PROTOTYP( int ttsetflow, (int) ); #endif /* CK_TTSETFLOW */ /* Systems where we can expand tilde at the beginning of file or directory names */ #ifdef POSIX #ifndef DTILDE #define DTILDE #endif /* DTILDE */ #endif /* POSIX */ #ifdef BSD4 #ifndef DTILDE #define DTILDE #endif /* DTILDE */ #endif /* BSD4 */ #ifdef ATTSV #ifndef DTILDE #define DTILDE #endif /* DTILDE */ #endif /* ATTSV */ #ifdef OSK #ifndef DTILDE #define DTILDE #endif /* DTILDE */ #endif /* OSK */ #ifdef HPUX /* I don't know why this is */ #ifndef DTILDE /* necessary, since -DHPUX */ #define DTILDE /* automatically defines ATTSV */ #endif /* DTILDE */ /* (see above) ... */ #endif /* HPUX */ /* This is mainly for the benefit of ckufio.c (UNIX and OS/2 file support). Systems that have an atomic rename() function, so we don't have to use link() and unlink(). */ #ifdef POSIX #ifndef RENAME #define RENAME #endif /* RENAME */ #endif /* POSIX */ #ifdef OS2 #ifndef RENAME #define RENAME #endif /* RENAME */ #endif /* OS2 */ #ifdef SUNOS41 #ifndef RENAME #define RENAME #endif /* RENAME */ #endif /* SUNOS41 */ #ifdef SVR4 #ifndef RENAME #define RENAME #endif /* RENAME */ #endif /* SVR4 */ #ifdef AIXRS #ifndef RENAME #define RENAME #endif /* RENAME */ #endif /* AIXRS */ #ifdef BSD44 #ifndef RENAME #define RENAME #endif /* RENAME */ #endif /* BSD44 */ #ifdef NORENAME /* Allow for compile-time override */ #ifdef RENAME #undef RENAME #endif /* RENAME */ #endif /* NORENAME */ #ifdef STRATUS /* Stratus VOS */ #ifndef RENAME #define RENAME #endif /* RENAME */ #endif /* STRATUS */ /* Line delimiter for text files */ /* If the system uses a single character for text file line delimitation, define NLCHAR to the value of that character. For text files, that character will be converted to CRLF upon output, and CRLF will be converted to that character on input during text-mode (default) packet operations. */ #ifdef MAC /* Macintosh */ #define NLCHAR 015 #else #ifdef OSK /* OS-9/68K */ #define NLCHAR 015 #else /* All Unix-like systems */ #define NLCHAR 012 #endif /* OSK */ #endif /* MAC */ /* At this point, if there's a system that uses ordinary CRLF line delimitation AND the C compiler actually returns both the CR and the LF when doing input from a file, then #undef NLCHAR. */ #ifdef OS2 /* OS/2 */ #undef NLCHAR #endif /* OS2 */ #ifdef GEMDOS /* Atari ST */ #undef NLCHAR #endif /* GEMDOS */ /* VMS file formats are so complicated we need to do all the conversion work in the CKVFIO module, so we tell the rest of C-Kermit not to fiddle with the bytes. */ #ifdef vms #undef NLCHAR #endif /* vms */ /* The device name of a job's controlling terminal */ /* Special for VMS, same for all Unixes (?), not used by Macintosh */ #ifdef BEOS #define CTTNAM dftty #else #ifdef vms #define CTTNAM "SYS$INPUT:" /* (4 Jan 2002) Was TT: */ #else #ifdef datageneral #define CTTNAM "@output" #else #ifdef OSK extern char myttystr[]; #define CTTNAM myttystr #else #ifdef OS2 #define CTTNAM "con" #else #ifdef UNIX #define CTTNAM "/dev/tty" #else #ifdef GEMDOS #define CTTNAM "aux:" #else #ifdef STRATUS extern char myttystr[]; #define CTTNAM myttystr #else /* Anyone else... */ #define CTTNAM "stdout" /* This is a kludge used by Mac */ #endif /* STRATUS */ #endif /* GEMDOS */ #endif /* UNIX */ #endif /* OS2 */ #endif /* OSK */ #endif /* datageneral */ #endif /* vms */ #endif /* BEOS */ #ifndef HAVECTTNAM #ifdef UNIX #define HAVECTTNAM #else #ifdef VMS #define HAVECTTNAM #endif /* VMS */ #endif /* UNIX */ #endif /* HAVECTTNAM */ #ifndef ZFCDAT /* zfcdat() function available? */ #ifdef UNIX #define ZFCDAT #else #ifdef STRATUS #define ZFCDAT #else #ifdef GEMDOS #define ZFCDAT #else #ifdef AMIGA #define ZFCDAT #else #ifdef OS2 #define ZFCDAT #else #ifdef datageneral #define ZFCDAT #else #ifdef VMS #define ZFCDAT #endif /* VMS */ #endif /* datageneral */ #endif /* OS2 */ #endif /* AMIGA */ #endif /* GEMDOS */ #endif /* STRATUS */ #endif /* UNIX */ #endif /* ZFCDAT */ #ifdef SUNS4S5 #define tolower _tolower #define toupper _toupper #endif /* SUNS4S5 */ /* Error number */ #ifdef _CRAY #ifdef _CRAYCOM /* Cray Computer Corp. */ extern int errno; #else /* _CRAYCOM */ #include /* Cray Research UNICOS defines */ /* errno as a function. */ #endif /* _CRAYCOM */ /* OK for UNICOS 6.1 and 7.0. */ #else /* _CRAY */ #ifdef STRATUS /* Stratus VOS */ #include #else /* not STRATUS */ #ifndef VMS #ifndef OS2 #ifdef __GLIBC__ /* "glibc uses threads, kermit uses glibc; errno access is in Thread Local Storage (TLS) from glibc-3.2.2. ...a thread specific errno is being run in thread local storage relative to the %gs segment register, so some means to revector gets/puts needs to be done." - Jeff Johnson, Red Hat, Feb 2003. */ #include #else /* The following declaration would cause problems for VMS and OS/2, in which errno is an "extern volatile int noshare"... NOTE: by now (2007) the following is an anachronism and should be the execption rather than the rule. */ extern int errno; #endif /* __GLIBC__ */ #endif /* OS2 */ #endif /* VMS */ #endif /* STRATUS */ #endif /* _CRAY */ #ifdef UNIX /* Catch-all so we can have */ #ifndef ESRCH /* access to error mnemonics */ #include /* in all modules - 2007/08/25 */ #endif /* ESRCH */ #endif /* UNIX */ #ifdef pdp11 /* Try to make some space on PDP-11 */ #ifndef NODIAL #define NODIAL #endif /* NODIAL */ #ifndef NOCURSES #define NOCURSES #endif /* NOCURSES */ #ifndef NOBIGBUF #define NOBIGBUF #endif /* NOBIGBUF */ #endif /* pdp11 */ #ifndef NOBIGBUF #ifndef BIGBUFOK /* Platforms with lots of memory */ #ifdef QNX /* QNX */ #ifndef QNX16 /* But not 16-bit versions */ #define BIGBUFOK #endif /* QNX16 */ #endif /* QNX */ #ifdef BSD44 #define BIGBUFOK #endif /* BSD44 */ #ifdef STRATUS /* Stratus VOS */ #define BIGBUFOK #endif /* STRATUS */ #ifdef sparc /* SPARC processors */ #define BIGBUFOK #endif /* sparc */ #ifdef mips /* MIPS processors */ #define BIGBUFOK #endif /* mips */ #ifdef HPUX9 /* HP-UX 9.x */ #define BIGBUFOK #endif /* HPUX9 */ #ifdef HPUX10 /* HP-UX 10.0 PA-RISC */ #define BIGBUFOK #endif /* HPUX10 */ #ifdef NEXT /* NeXTSTEP */ #ifdef mc68000 /* on NEXT platforms... */ #define BIGBUFOK #endif /* mc68000 */ #endif /* NEXT */ #ifdef LINUX /* Linux in 1998 should be OK */ #ifndef BIGBUFOK #define BIGBUFOK #endif /* BIGBUFOK */ #endif /* LINUX */ #ifdef OS2 /* 32-bit OS/2 2.x and above */ #ifdef __32BIT__ #define BIGBUFOK #endif /* __32BIT__ */ #ifdef NT #define BIGBUFOK #endif /* NT */ #endif /* OS2 */ #ifdef Plan9 /* Plan 9 is OK */ #define BIGBUFOK #endif /* Plan9 */ #ifdef VMS /* Any VMS is OK */ #ifndef BIGBUFOK #define BIGBUFOK #endif /* BIGBUFOK */ #endif /* VMS */ #ifdef __alpha /* DEC 64-bit Alpha, e.g. OSF/1 */ #ifndef BIGBUFOK /* Might already be defined for VMS */ #define BIGBUFOK #endif /* BIGBUFOK */ #endif /* __alpha */ #ifdef sgi /* SGI with IRIX 4.0 or later */ #ifndef BIGBUFOK #define BIGBUFOK #endif /* BIGBUFOK */ #endif /* sgi */ #ifdef AIXRS /* AIX on RISC */ #define BIGBUFOK #endif /* AIXRS */ #ifdef CK_SCOV5 /* SCO OSR5 */ #ifndef BIGBUFOK #define BIGBUFOK #endif /* BIGBUFOK */ #endif /* CK_SCOV5 */ #ifdef SOLARIS /* Solaris x86 */ #ifndef BIGBUFOK #define BIGBUFOK #endif /* BIGBUFOK */ #endif /* SOLARIS */ #endif /* BIGBUFOK */ #endif /* NOBIGBUF */ #ifdef CK_SMALL #ifdef BIGBUFOK #undef BIGBUFOK #endif /* BIGBUFOK */ #endif /* CK_SMALL */ /* If "memory is no problem" then this improves performance */ #ifdef DEBUG #ifdef BIGBUFOK #ifndef IFDEBUG #define IFDEBUG #endif /* IFDEBUG */ #endif /* BIGBUFOK */ #endif /* DEBUG */ /* File System Defaults */ #ifndef UIDBUFLEN /* Length of User ID */ #ifdef OS2 #define UIDBUFLEN 256 #else /* OS2 */ #ifdef BIGBUFOK #define UIDBUFLEN 256 #else #define UIDBUFLEN 64 #endif /* BIGBUFOK */ #endif /* OS2 */ #endif /* UIDBUFLEN */ #ifdef UNIX #ifdef PROVX1 #define MAXWLD 50 #else #ifdef pdp11 #define MAXWLD 50 #else #ifdef BIGBUFOK #define MAXWLD 102400 #else #define MAXWLD 1024 #endif /* BIGBUFOK */ #endif /* pdp11 */ #endif /* PROVX1 */ #else #ifdef VMS #define MAXWLD 102400 /* Maximum wildcard filenames */ #else #ifdef datageneral #define MAXWLD 500 #else #ifdef STRATUS #define MAXWLD 5000 #endif /* STRATUS */ #endif /* datageneral */ #endif /* VMS */ #endif /* UNIX */ #ifdef VMS #define DBLKSIZ 512 #define DLRECL 512 #else #define DBLKSIZ 0 #define DLRECL 0 #endif /* VMS */ /* Communication device / network host name length */ #ifdef BIGBUFOK #define TTNAMLEN 512 #else #ifdef MAC #define TTNAMLEN 256 #else #ifndef CK_SMALL #define TTNAMLEN 128 #else #define TTNAMLEN 80 #endif /* CK_SMALL */ #endif /* MAC */ #endif /* BIGBUFOK */ /* Program return codes for DECUS C and UNIX (VMS uses UNIX codes) */ #ifdef decus #define GOOD_EXIT IO_NORMAL #define BAD_EXIT IO_ERROR #else #define GOOD_EXIT 0 #define BAD_EXIT 1 #endif /* decus */ /* Special hack for Fortune, which doesn't have ... */ #ifdef FT18 #define FREAD 0x01 #define FWRITE 0x10 #endif /* FT18 */ /* Special hack for OS-9/68k */ #ifdef OSK #ifndef _UCC #define SIGALRM 30 /* May always cancel I/O */ #endif /* _UCC */ #define SIGARB 1234 /* Arbitrary for I/O */ SIGTYP (*signal())(); #endif /* OSK */ #ifdef MINIX #ifdef putchar #undef putchar #endif /* putchar */ #define putchar(c) (putc(c,stdout)!=EOF)&&fflush(stdout) #endif /* MINIX */ #ifdef datageneral /* Data General AOS/VS */ #ifdef putchar #undef putchar #endif /* putchar */ #define putchar(c) conoc(c) #endif /* datageneral */ /* Escape/quote character used by the command parser */ #define CMDQ '\\' /* Symbols for RS-232 modem signals */ #define KM_FG 1 /* Frame ground */ #define KM_TXD 2 /* Transmit */ #define KM_RXD 3 /* Receive */ #define KM_RTS 4 /* Request to Send */ #define KM_CTS 5 /* Clear to Send */ #define KM_DSR 6 /* Data Set Ready */ #define KM_SG 7 /* Signal ground */ #define KM_DCD 8 /* Carrier Detect */ #define KM_DTR 20 /* Data Terminal Ready */ #define KM_RI 22 /* Ring Indication */ /* Bit mask values for modem signals */ #define BM_CTS 0001 /* Clear to send (From DCE) */ #define BM_DSR 0002 /* Dataset ready (From DCE) */ #define BM_DCD 0004 /* Carrier (From DCE) */ #define BM_RNG 0010 /* Ring Indicator (From DCE) */ #define BM_DTR 0020 /* Data Terminal Ready (From DTE) */ #define BM_RTS 0040 /* Request to Send (From DTE) */ /* Codes for full duplex flow control */ #define FLO_NONE 0 /* None */ #define FLO_XONX 1 /* Xon/Xoff (soft) */ #define FLO_RTSC 2 /* RTS/CTS (hard) */ #define FLO_DTRC 3 /* DTR/CD (hard) */ #define FLO_ETXA 4 /* ETX/ACK (soft) */ #define FLO_STRG 5 /* String-based (soft) */ #define FLO_DIAL 6 /* DIALing kludge */ #define FLO_DIAX 7 /* Cancel dialing kludge */ #define FLO_DTRT 8 /* DTR/CTS (hard) */ #define FLO_KEEP 9 /* Keep, i.e. don't touch or change */ #define FLO_AUTO 10 /* Figure out automatically */ /* Types of connections */ #define CXT_REMOTE 0 /* Remote mode - no connection */ #define CXT_DIRECT 1 /* Direct serial connection */ #define CXT_MODEM 2 /* Modem dialout */ #define CXT_TCPIP 3 /* TCP/IP - Telnet, Rlogin, etc */ #define CXT_X25 4 /* X.25 peer-to-peer */ #define CXT_DECNET 5 /* DECnet (CTERM, etc) */ #define CXT_LAT 6 /* LAT */ #define CXT_NETBIOS 7 /* NETBIOS */ #define CXT_NPIPE 8 /* Named Pipe */ #define CXT_PIPE 9 /* Pipe, Command, PTY, DLL, etc */ #define CXT_SSH 10 /* SSH */ #define CXT_MAX 10 /* Highest connection type */ /* Autodownload Detection Options */ #define ADL_PACK 0 /* Auto-Download detect packet */ #define ADL_STR 1 /* Auto-Download detect string */ /* And finally... */ #ifdef COMMENT /* Make sure this is NOT defined! */ #undef COMMENT #endif /* COMMENT */ /* zstr zattr filinfo were here (moved to top for DECC 5 Jun 2000) */ #ifndef ZFNQFP /* Versions that have zfnqfp() */ #ifdef UNIX #define ZFNQFP #else #ifdef VMS #define ZFNQFP #else #ifdef OS2 #define ZFNQFP #else #ifdef datageneral #define ZFNQFP #else #ifdef STRATUS #define ZFNQFP #endif /* STRATUS */ #endif /* datageneral */ #endif /* OS2 */ #endif /* VMS */ #endif /* UNIX */ struct zfnfp { int len; /* Length of full pathname */ char * fpath; /* Pointer to full pathname */ char * fname; /* Pointer to name part */ }; #endif /* ZFNQFP */ /* Systems that support FILE TYPE LABELED */ #ifdef VMS #define CK_LABELED #else #ifdef OS2 #ifdef __32BIT__ #ifndef NT #define CK_LABELED #endif /* NT */ #endif /* __32BIT__ */ #endif /* OS2 */ #endif /* VMS */ /* LABELED FILE options bitmask */ #ifdef VMS /* For VMS */ #define LBL_NAM 1 /* Ignore incoming name if set */ #define LBL_PTH 2 /* Use complete path if set */ #define LBL_ACL 4 /* Preserve ACLs if set */ #define LBL_BCK 8 /* Preserve backup date if set */ #define LBL_OWN 16 /* Preserve ownership if set */ #else #ifdef OS2 /* Ditto for OS/2 */ #define LBL_NOR 0x0000 /* Normal file */ #define LBL_ARC 0x0020 /* Archive */ #define LBL_DIR 0x0010 /* Directory */ #define LBL_HID 0x0002 /* Hidden file */ #define LBL_RO 0x0001 /* Read only file */ #define LBL_SYS 0x0004 /* System file */ #define LBL_EXT 0x0040 /* Extended */ #endif /* OS2 */ #endif /* VMS */ /* Data types. First the header file for data types so we can pick up the types used for pids, uids, and gids. Override this section by putting -DCKTYP_H=xxx on the command line to specify the header file where your system defines these types. */ #ifndef STRATUS #ifdef __ALPHA #ifdef MULTINET #define CK_TGV_AXP #endif /* MULTINET */ #endif /* __ALPHA */ #ifdef CK_TGV_AXP /* Alpha, VMS, MultiNet */ /* Starting in DECC 5.0, no longer includes . But before that an elaborate workaround is required, which results in including sometimes but not others, evidently depending on whether protects itself against multiple inclusion, which in turn probably differentiates between DECC and TGV . Unfortunately I don't remember the details. (fdc, 25 Oct 96) */ #ifdef COMMENT /* Previously the test here was for DEC version prior to 4.0, but since the test involved an "#if" statement, it was not portable and broke some non-VMS builds. In any case, condition was never satisfied, so the result of commenting this section out is the same as the previous "#if" condition. */ #ifndef __TYPES_LOADED #define __TYPES_LOADED /* Work around bug in .h files */ #endif /* __TYPES_LOADED */ #endif /* COMMENT */ #include #ifdef IF_DOT_H #ifndef MULTINET #include /* Needed to put up u_int typedef */ #endif /* MULTINET */ #else /* IF_DOT_H */ #ifdef NEEDUINT typedef unsigned int u_int; #endif /* NEEDUINT */ #endif /* IF_DOT_H */ #else /* !CK_TGV_AXP */ #ifdef OSK /* OS-9 */ #include #else /* General case, not OS-9 */ #ifndef CKTYP_H #ifndef VMS #ifndef MAC #ifndef AMIGA #define CKTYP_H #endif /* AMIGA */ #endif /* MAC */ #endif /* VMS */ #endif /* CKTYP_H */ #ifdef GEMDOS #undef CKTYP_H #include #endif /* GEMDOS */ #ifdef OS2 #undef CKTYP_H #include #endif /* OS2 */ #ifdef CKTYP_H /* Include it. */ #ifdef COHERENT /* Except for COHERENT */ #include #include #else #ifdef datageneral /* AOS/VS */ #include #else /* All others */ #ifdef __bsdi__ /* BSDI */ #ifdef POSIX #undef _POSIX_SOURCE #endif /* POSIX */ #endif /* __bsdi__ */ #include CKTYP_H #ifdef __bsdi__ #ifdef POSIX #define _POSIX_SOURCE #endif /* POSIX */ #endif /* __bsdi__ */ #endif /* datageneral */ #endif /* COHERENT */ #endif /* CKTYP_H */ #endif /* OSK */ #endif /* CK_TGV_AXP */ #endif /* STRATUS */ /* End of types.h section */ /* File lengths and offsets. This section is expected to grow as we support long files on 32-bit platforms. We want this data type to be signed because so many functions return either a file size or a negative value to indicate an error. */ #ifndef CK_OFF_T #ifdef OS2 #ifdef NT #define CK_OFF_T __int64 #else #define CK_OFF_T long #endif /* NT */ #endif /* OS2 */ #endif /* CK_OFF_T */ /* FreeBSD and OpenBSD set off_t to the appropriate size unconditionally */ #ifndef CK_OFF_T #ifdef __FreeBSD__ #define CK_OFF_T off_t #else #ifdef __OpenBSD__ #define CK_OFF_T off_t #endif /* __OpenBSD__ */ #endif /* __FreeBSD__ */ #endif /* CK_OFF_T */ /* 32-bit platforms that support long files thru "transitional interface" */ /* These include Linux, Solaris, NetBSD... */ #ifdef AIXRS #ifdef _LARGE_FILES #ifndef CK_OFF_T #define CK_OFF_T off_t #endif /* CK_OFF_T */ #endif /* _LARGE_FILES */ #endif /* AIXRS */ #ifdef _LARGEFILE_SOURCE #ifndef CK_OFF_T #define CK_OFF_T off_t #endif /* CK_OFF_T */ #ifdef IRIX #define CKFSEEK(a,b,c) fseek64(a,b,c) #define CKFTELL(a) ftell64(a) #else /* IRIX */ #define CKFSEEK(a,b,c) fseeko(a,b,c) #define CKFTELL(a) ftello(a) #endif /* IRIX */ #else /* Not _LARGEFILE_SOURCE */ #define CKFSEEK(a,b,c) fseek(a,b,c) #define CKFTELL(a) ftell(a) /* See below the next section for the catch-all case */ #endif /* _LARGEFILE_SOURCE */ /* 32-bit or 64-bit platforms */ /* CK_64BIT is a compile-time symbol indicating a true 64-bit build */ /* meaning that longs and pointers are 64 bits */ #ifndef VMS /* VMS Alpha and IA64 are 32-bit! */ #ifndef CK_64BIT #ifdef _LP64 /* Solaris */ #define CK_64BIT #else #ifdef __LP64__ /* MacOS X 10.4 (or _LP64,__ppc64__) */ #define CK_64BIT #else #ifdef __arch64__ /* gcc alpha, sparc */ #define CK_64BIT #else #ifdef __alpha /* Alpha decc (or __ALPHA) */ #define CK_64BIT #else #ifdef __amd64 /* AMD x86_64 (or __x86_64) */ #define CK_64BIT #else #ifdef __ia64 /* Intel IA64 */ #ifndef HPUX #define CK_64BIT #endif /* HPUX */ #endif /* __ia64 */ #endif /* __amd64 */ #endif /* __alpha */ #endif /* __arch64__ */ #endif /* __LP64__ */ #endif /* _LP64 */ #endif /* CK_64BIT */ #endif /* VMS */ #ifndef CK_OFF_T #ifdef CK_64BIT #define CK_OFF_T off_t /* This has to be signed */ #else /* CK_64BIT */ #define CK_OFF_T long /* Signed */ #endif /* CK_64BIT */ #endif /* CK_OFF_T */ #ifndef TLOG #define tlog(a,b,c,d) #else #ifndef CKCMAI /* Debugging included. Declare debug log flag in main program only. */ extern int tralog, tlogfmt; #endif /* CKCMAI */ _PROTOTYP(VOID dotlog,(int, char *, char *, CK_OFF_T)); #define tlog(a,b,c,d) if (tralog && tlogfmt) dotlog(a,b,c,(CK_OFF_T)d) _PROTOTYP(VOID doxlog,(int, char *, CK_OFF_T, int, int, char *)); #endif /* TLOG */ #ifndef DEBUG /* Compile all the debug() statements away. Saves a lot of space and time. */ #define debug(a,b,c,d) #define ckhexdump(a,b,c) /* Now define the debug() macro. */ #else /* DEBUG */ _PROTOTYP(int dodebug,(int,char *,char *,CK_OFF_T)); _PROTOTYP(int dohexdump,(CHAR *,CHAR *,int)); #ifdef IFDEBUG /* Use this form to avoid function calls: */ #ifdef COMMENT #define debug(a,b,c,d) if (deblog) dodebug(a,b,(char *)(c),(CK_OFF_T)(d)) #define ckhexdump(a,b,c) if (deblog) dohexdump((CHAR *)(a),(CHAR *)(b),c) #else #ifdef CK_ANSIC #define debug(a,b,c,d) \ ((void)(deblog?dodebug(a,b,(char *)(c),(CK_OFF_T)(d)):0)) #define ckhexdump(a,b,c) \ ((void)(deblog?dohexdump((CHAR *)(a),(CHAR *)(b),c):0)) #else #define debug(a,b,c,d) (deblog?dodebug(a,b,(char *)(c),(CK_OFF_T)(d)):0) #define ckhexdump(a,b,c) (deblog?dohexdump((CHAR *)(a),(CHAR *)(b),c):0) #endif /* CK_ANSIC */ #endif /* COMMENT */ #else /* IFDEBUG */ /* Use this form to save space: */ #define debug(a,b,c,d) dodebug(a,b,(char *)(c),(CK_OFF_T)(d)) #define ckhexdump(a,b,c) dohexdump((CHAR *)(a),(CHAR *)(b),c) #endif /* IFDEBUG */ #endif /* DEBUG */ /* Structure definitions for Kermit file attributes */ /* All strings come as pointer and length combinations */ /* Empty string (or for numeric variables, -1) = unused attribute. */ struct zstr { /* string format */ int len; /* length */ char *val; /* value */ }; struct zattr { /* Kermit File Attribute structure */ CK_OFF_T lengthk; /* (!) file length in K */ struct zstr type; /* (") file type (text or binary) */ struct zstr date; /* (#) file creation date yyyymmdd[ hh:mm[:ss]] */ struct zstr creator; /* ($) file creator id */ struct zstr account; /* (%) file account */ struct zstr area; /* (&) area (e.g. directory) for file */ struct zstr password; /* (') password for area */ long blksize; /* (() file blocksize */ struct zstr xaccess; /* ()) file access: new, supersede, append, warn */ struct zstr encoding; /* (*) encoding (transfer syntax) */ struct zstr disp; /* (+) disposition (mail, message, print, etc) */ struct zstr lprotect; /* (,) protection (local syntax) */ struct zstr gprotect; /* (-) protection (generic syntax) */ struct zstr systemid; /* (.) ID for system of origin */ struct zstr recfm; /* (/) record format */ struct zstr sysparam; /* (0) system-dependent parameter string */ CK_OFF_T length; /* (1) exact length on system of origin */ struct zstr charset; /* (2) transfer syntax character set */ #ifdef OS2 struct zstr longname; /* OS/2 longname if applicable */ #endif /* OS2 */ struct zstr reply; /* This goes last, used for attribute reply */ }; /* Kermit file information structure */ struct filinfo { int bs; /* Blocksize */ int cs; /* Character set */ long rl; /* Record length */ int org; /* Organization */ int fmt; /* Record format */ int cc; /* Carriage control */ int typ; /* Type (text/binary) */ int dsp; /* Disposition */ char *os_specific; /* OS-specific attributes */ #ifdef OS2 unsigned long int lblopts; /* LABELED FILE options bitmask */ #else int lblopts; #endif /* OS2 */ }; /* Data type for pids. If your system uses a different type, put something like -DPID_T=pid_t on command line, or override here. */ #ifndef PID_T #define PID_T int #endif /* PID_T */ /* Data types for uids and gids. Same deal as for pids. Wouldn't be nice if there was a preprocessor test to find out if a typedef existed? */ #ifdef VMS /* Not used in VMS so who cares */ #define UID_T int #define GID_T int #endif /* VMS */ #ifdef POSIX /* Or would it be better (or worse?) to use _POSIX_SOURCE here? */ #ifndef UID_T #define UID_T uid_t #endif /* UID_T */ #ifndef GID_T #define GID_T gid_t #endif /* GID_T */ #else /* Not POSIX */ #ifdef SVR4 /* SVR4 and later have uid_t and gid_t. */ /* SVR3 and earlier use int, or unsigned short, or.... */ #ifndef UID_T #define UID_T uid_t #endif /* UID_T */ #ifndef GID_T #define GID_T gid_t #endif /* GID_T */ #else /* Not SVR4 */ #ifdef BSD43 #ifndef UID_T #define UID_T uid_t #endif /* UID_T */ #ifndef GID_T #define GID_T gid_t #endif /* GID_T */ #else /* Not BSD43 */ /* Default these to int for older UNIX versions */ #ifndef UID_T #define UID_T int #endif /* UID_T */ #ifndef GID_T #define GID_T int #endif /* GID_T */ #endif /* BSD43 */ #endif /* SVR4 */ #endif /* POSIX */ /* getpwuid() arg type, which is not necessarily the same as UID_T, e.g. in SCO UNIX SVR3, it's int. */ #ifndef PWID_T #define PWID_T UID_T #endif /* PWID_T */ #ifdef CK_REDIR #ifdef NEXT #define MACHWAIT #else #ifdef MACH #define MACHWAIT #endif /* MACH */ #endif /* NEXT */ #ifdef MACHWAIT /* WAIT_T argument for wait() */ #include #define CK_WAIT_H typedef union wait WAIT_T; #else #ifdef POSIX #ifdef OSF /* OSF wait.h defines BSD wait if _BSD is defined so hide _BSD from wait.h */ #ifdef _BSD #define CK_OSF_BSD #undef _BSD #endif /* _BSD */ #endif /* OSF */ #include #define CK_WAIT_H #ifndef WAIT_T typedef int WAIT_T; #endif /* WAIT_T */ #ifdef CK_OSF_BSD /* OSF/1: Restore _BSD definition */ #define _BSD #undef CK_OSF_BSD #endif /* CK_OSF_BSD */ #else /* !POSIX */ typedef int WAIT_T; #endif /* POSIX */ #endif /* MACHWAIT */ #else typedef int WAIT_T; #endif /* CK_REDIR */ /* Assorted other blah_t's handled here... */ #ifndef SIZE_T #define SIZE_T size_t #endif /* SIZE_T */ /* Forward declarations of system-dependent functions callable from all */ /* C-Kermit modules. */ /* File-related functions from system-dependent file i/o module */ #ifndef CKVFIO_C /* For some reason, this does not agree with DEC C */ _PROTOTYP( int zkself, (void) ); #endif /* CKVFIO_C */ _PROTOTYP( int zopeni, (int, char *) ); _PROTOTYP( int zopeno, (int, char *, struct zattr *, struct filinfo *) ); _PROTOTYP( int zclose, (int) ); #ifndef MAC _PROTOTYP( int zchin, (int, int *) ); #endif /* MAC */ _PROTOTYP( int zxin, (int, char *, int) ); _PROTOTYP( int zsinl, (int, char *, int) ); _PROTOTYP( int zinfill, (void) ); _PROTOTYP( int zsout, (int, char*) ); _PROTOTYP( int zsoutl, (int, char*) ); _PROTOTYP( int zsoutx, (int, char*, int) ); _PROTOTYP( int zchout, (int, char) ); _PROTOTYP( int zoutdump, (void) ); _PROTOTYP( int zsyscmd, (char *) ); _PROTOTYP( int zshcmd, (char *) ); #ifdef UNIX _PROTOTYP( int zsetfil, (int, int) ); _PROTOTYP( int zchkpid, (unsigned long) ); #endif /* UNIX */ #ifdef CKEXEC _PROTOTYP( VOID z_exec, (char *, char **, int) ); #endif /* CKEXEC */ _PROTOTYP( int chkfn, (int) ); _PROTOTYP( CK_OFF_T zchki, (char *) ); #ifdef VMSORUNIX _PROTOTYP( CK_OFF_T zgetfs, (char *) ); #else #ifdef OS2 _PROTOTYP( CK_OFF_T zgetfs, (char *) ); #else #define zgetfs(a) zchki(a) #endif /* OS2 */ #endif /* VMSORUNIX */ _PROTOTYP( int iswild, (char *) ); _PROTOTYP( int isdir, (char *) ); _PROTOTYP( int zchko, (char *) ); _PROTOTYP( int zdelet, (char *) ); _PROTOTYP( VOID zrtol, (char *,char *) ); _PROTOTYP( VOID zltor, (char *,char *) ); _PROTOTYP( VOID zstrip, (char *,char **) ); #ifdef VMS _PROTOTYP( char * zrelname, (char *, char *) ); #endif /* VMS */ _PROTOTYP( int zchdir, (char *) ); _PROTOTYP( char * zhome, (void) ); _PROTOTYP( char * zgtdir, (void) ); _PROTOTYP( int zxcmd, (int, char *) ); #ifndef MAC _PROTOTYP( int zclosf, (int) ); #endif /* MAC */ #ifdef NZXPAND _PROTOTYP( int nzxpand, (char *, int) ); #else /* NZXPAND */ _PROTOTYP( int zxpand, (char *) ); #endif /* NZXPAND */ _PROTOTYP( int znext, (char *) ); #ifdef ZXREWIND _PROTOTYP( int zxrewind, (void) ); #endif /* ZXREWIND */ _PROTOTYP( int zchkspa, (char *, CK_OFF_T) ); _PROTOTYP( VOID znewn, (char *, char **) ); _PROTOTYP( int zrename, (char *, char *) ); _PROTOTYP( int zcopy, (char *, char *) ); _PROTOTYP( int zsattr, (struct zattr *) ); _PROTOTYP( int zfree, (char *) ); _PROTOTYP( char * zfcdat, (char *) ); _PROTOTYP( int zstime, (char *, struct zattr *, int) ); #ifdef CK_PERMS _PROTOTYP( char * zgperm, (char *) ); _PROTOTYP( char * ziperm, (char *) ); #endif /* CK_PERMS */ _PROTOTYP( int zmail, (char *, char *) ); _PROTOTYP( int zprint, (char *, char *) ); _PROTOTYP( char * tilde_expand, (char *) ); _PROTOTYP( int zmkdir, (char *) ) ; _PROTOTYP( int zfseek, (CK_OFF_T) ) ; #ifdef ZFNQFP _PROTOTYP( struct zfnfp * zfnqfp, (char *, int, char * ) ) ; #else #define zfnqfp(a,b,c) ckstrncpy(c,a,b) #endif /* ZFNQFP */ _PROTOTYP( int zvuser, (char *) ) ; _PROTOTYP( int zvpass, (char *) ) ; _PROTOTYP( VOID zvlogout, (void) ) ; #ifdef OS2 _PROTOTYP( int os2setlongname, ( char * fn, char * ln ) ) ; _PROTOTYP( int os2getlongname, ( char * fn, char ** ln ) ) ; _PROTOTYP( int os2rexx, ( char *, char *, int ) ) ; _PROTOTYP( int os2rexxfile, ( char *, char *, char *, int) ) ; _PROTOTYP( int os2geteas, (char *) ) ; _PROTOTYP( int os2seteas, (char *) ) ; _PROTOTYP( char * get_os2_vers, (void) ) ; _PROTOTYP( int do_label_send, (char *) ) ; _PROTOTYP( int do_label_recv, (void) ) ; #ifdef OS2MOUSE _PROTOTYP( unsigned long os2_mouseon, (void) ); _PROTOTYP( unsigned long os2_mousehide, (void) ); _PROTOTYP( unsigned long os2_mouseshow, (void) ); _PROTOTYP( unsigned long os2_mouseoff, (void) ); _PROTOTYP( void os2_mouseevt, (void *) ); _PROTOTYP( int mousebuttoncount, (void)); #endif /* OS2MOUSE */ #endif /* OS2 */ /* Functions from system-dependent terminal i/o module */ _PROTOTYP( int ttopen, (char *, int *, int, int) ); /* tty functions */ #ifndef MAC _PROTOTYP( int ttclos, (int) ); #endif /* MAC */ _PROTOTYP( int tthang, (void) ); _PROTOTYP( int ttres, (void) ); _PROTOTYP( int ttpkt, (long, int, int) ); #ifndef MAC _PROTOTYP( int ttvt, (long, int) ); #endif /* MAC */ _PROTOTYP( int ttsspd, (int) ); _PROTOTYP( long ttgspd, (void) ); _PROTOTYP( int ttflui, (void) ); _PROTOTYP( int ttfluo, (void) ); _PROTOTYP( int ttpushback, (CHAR *, int) ); _PROTOTYP( int ttpeek, (void) ); _PROTOTYP( int ttgwsiz, (void) ); _PROTOTYP( int ttchk, (void) ); _PROTOTYP( int ttxin, (int, CHAR *) ); _PROTOTYP( int ttxout, (CHAR *, int) ); _PROTOTYP( int ttol, (CHAR *, int) ); _PROTOTYP( int ttoc, (char) ); _PROTOTYP( int ttinc, (int) ); _PROTOTYP( int ttscarr, (int) ); _PROTOTYP( int ttgmdm, (void) ); _PROTOTYP( int ttsndb, (void) ); _PROTOTYP( int ttsndlb, (void) ); #ifdef UNIX _PROTOTYP( char * ttglckdir, (void) ); #endif /* UNIX */ #ifdef PARSENSE #ifdef UNIX _PROTOTYP( int ttinl, (CHAR *, int, int, CHAR, CHAR, int) ); #else #ifdef VMS _PROTOTYP( int ttinl, (CHAR *, int, int, CHAR, CHAR, int) ); #else #ifdef STRATUS _PROTOTYP( int ttinl, (CHAR *, int, int, CHAR, CHAR, int) ); #else #ifdef OS2 _PROTOTYP( int ttinl, (CHAR *, int, int, CHAR, CHAR, int) ); #else #ifdef OSK _PROTOTYP( int ttinl, (CHAR *, int, int, CHAR, CHAR, int) ); #else _PROTOTYP( int ttinl, (CHAR *, int, int, CHAR, CHAR) ); #endif /* OSK */ #endif /* OS2 */ #endif /* STRATUS */ #endif /* VMS */ #endif /* UNIX */ #else /* ! PARSENSE */ _PROTOTYP( int ttinl, (CHAR *, int, int, CHAR) ); #endif /* PARSENSE */ /* XYZMODEM support */ /* CK_XYZ enables the various commands and data structures. XYZ_INTERNAL means these protocols are built-in; if not defined, then they are external. XYZ_DLL is used to indicate a separate loadable library containing the XYZmodem protocol code. */ #ifdef pdp11 /* No room for this in PDP-11 */ #define NOCKXYZ #endif /* pdp11 */ #ifndef NOCKXYZ /* Alternative protocols */ #ifndef CK_XYZ #ifdef UNIX #define CK_XYZ #else #ifdef OS2 #define CK_XYZ #ifndef NOXYZDLL #define XYZ_INTERNAL /* Internal and DLL */ #define XYZ_DLL #endif /* NOXYZDLL */ #endif /* OS2 */ #endif /* UNIX */ #endif /* CK_XYZ */ #endif /* NOCKXYZ */ #ifdef XYZ_INTERNAL /* This ensures that XYZ_INTERNAL */ #ifndef CK_XYZ /* is defined only if CK_XYZ is too */ #undef XYZ_INTERNAL #endif /* CK_XYZ */ #endif /* XYZ_INTERNAL */ #ifdef XYZ_DLL /* This ensures XYZ_DLL is defined */ #ifndef XYZ_INTERNAL /* only if XYZ_INTERNAL is too */ #undef XYZ_DLL #endif /* XYZ_INTERNAL */ #endif /* XYZ_DLL */ /* Console functions */ _PROTOTYP( int congm, (void) ); #ifdef COMMENT _PROTOTYP( VOID conint, (SIGTYP (*)(int, int), SIGTYP (*)(int, int)) ); #else _PROTOTYP( VOID conint, (SIGTYP (*)(int), SIGTYP (*)(int)) ); #endif /* COMMENT */ _PROTOTYP( VOID connoi, (void) ); _PROTOTYP( int concb, (char) ); #ifdef CONGSPD _PROTOTYP( long congspd, (void) ); #endif /* CONGSPD */ _PROTOTYP( int conbin, (char) ); _PROTOTYP( int conres, (void) ); _PROTOTYP( int conoc, (char) ); _PROTOTYP( int conxo, (int, char *) ); _PROTOTYP( int conol, (char *) ); _PROTOTYP( int conola, (char *[]) ); _PROTOTYP( int conoll, (char *) ); _PROTOTYP( int conchk, (void) ); _PROTOTYP( int coninc, (int) ); _PROTOTYP( char * conkbg, (void) ); _PROTOTYP( int psuspend, (int) ); _PROTOTYP( int priv_ini, (void) ); _PROTOTYP( int priv_on, (void) ); _PROTOTYP( int priv_off, (void) ); _PROTOTYP( int priv_can, (void) ); _PROTOTYP( int priv_chk, (void) ); _PROTOTYP( int priv_opn, (char *, int) ); _PROTOTYP( int sysinit, (void) ); /* Misc Kermit functions */ _PROTOTYP( int syscleanup, (void) ); _PROTOTYP( int msleep, (int) ); _PROTOTYP( VOID rtimer, (void) ); _PROTOTYP( int gtimer, (void) ); #ifdef GFTIMER _PROTOTYP( VOID rftimer, (void) ); _PROTOTYP( CKFLOAT gftimer, (void) ); #endif /* GFTIMER */ _PROTOTYP( VOID ttimoff, (void) ); _PROTOTYP( VOID ztime, (char **) ); _PROTOTYP( int parchk, (CHAR *, CHAR, int) ); _PROTOTYP( VOID doexit, (int, int) ); _PROTOTYP( int askmore, (void) ); _PROTOTYP( VOID fatal, (char *) ); _PROTOTYP( VOID fatal2, (char *, char *) ); #ifdef VMS _PROTOTYP( int ck_cancio, (void) ); #endif /* VMS */ /* Key mapping support */ #ifdef NOICP #ifndef NOSETKEY #define NOSETKEY #endif /* NOSETKEY */ #endif /* NOICP */ #ifdef MAC #ifndef NOSETKEY #define NOSETKEY #endif /* NOSETKEY */ #endif /* MAC */ _PROTOTYP( int congks, (int) ); #ifdef OS2 /* OS2 requires these definitions even if SET KEY is not being supported */ #define KMSIZE 8916 typedef ULONG KEY; typedef CHAR *MACRO; extern int wideresult; #else /* Not OS2 */ #ifndef NOSETKEY /* Catch-all for systems where we don't know how to read keyboard scan codes > 255. */ #define KMSIZE 256 /* Note: CHAR (i.e. unsigned char) is very important here. */ typedef CHAR KEY; typedef CHAR * MACRO; #define congks coninc #endif /* NOSETKEY */ #endif /* OS2 */ #ifndef OS2 #ifndef NOKVERBS /* No \Kverbs unless... */ #define NOKVERBS #endif /* NOKVERBS */ #endif /* OS2 */ #ifndef NOKVERBS #ifdef OS2 /* Note: this value chosen to be bigger than PC BIOS key modifier bits, but still fit in 16 bits without affecting sign. As of K95 1.1.5, this no longer fits in 16 bits, good thing we are 32 bit. */ #define F_MACRO 0x2000 /* Bit indicating a macro indice */ #define IS_MACRO(x) (x & F_MACRO) #define F_KVERB 0x4000 /* Bit indicating a keyboard verb */ #define IS_KVERB(x) (x & F_KVERB) /* Test this bit */ #endif /* OS2 */ #endif /* NOKVERBS */ #define F_ESC 0x8000 /* Bit indicating ESC char combination */ #define IS_ESC(x) (x & F_ESC) #define F_CSI 0x10000 /* Bit indicating CSI char combination */ #define IS_CSI(x) (x & F_CSI) #ifdef NOSPL /* This might be overkill.. */ #ifndef NOKVERBS /* Not all \Kverbs require */ #define NOKVERBS /* the script programming language. */ #endif /* NOKVERBS */ #ifndef NOTAKEARGS #define NOTAKEARGS #endif /* NOTAKEARGS */ #endif /* NOSPL */ /* Function prototypes for system and library functions. */ #ifdef _POSIX_SOURCE #ifndef VMS #ifndef MAC #define CK_ANSILIBS #endif /* MAC */ #endif /* VMS */ #endif /* _POSIX_SOURCE */ #ifdef NEXT #define CK_ANSILIBS #endif /* NEXT */ #ifdef SVR4 #define CK_ANSILIBS #endif /* SVR4 */ #ifdef STRATUS /* Stratus VOS uses ANSI libraries */ #define CK_ANSILIBS #endif /* STRATUS */ #ifdef OS2 #define CK_ANSILIBS #ifndef NOCURSES #define MYCURSES #endif /* NOCURSES */ #define CK_RTSCTS #ifdef __IBMC__ #define S_IFMT 0xF000 #define timezone _timezone #endif /* __IBMC__ */ #include #include #ifdef __EMX__ #ifndef __32BIT__ #define __32BIT__ #endif /* __32BIT__ */ #include #else /* __EMX__ */ #include #undef SIGALRM #ifndef SIGUSR1 #define SIGUSR1 7 #endif /* SIGUSR1 */ #define SIGALRM SIGUSR1 _PROTOTYP( unsigned alarm, (unsigned) ); _PROTOTYP( unsigned sleep, (unsigned) ); #endif /* __EMX__ */ _PROTOTYP( unsigned long zdskspace, (int) ); _PROTOTYP( int zchdsk, (int) ); _PROTOTYP( int conincraw, (int) ); _PROTOTYP( int ttiscom, (int f) ); _PROTOTYP( int IsFileNameValid, (char *) ); _PROTOTYP( void ChangeNameForFAT, (char *) ); _PROTOTYP( char *GetLoadPath, (void) ); #endif /* OS2 */ /* Fullscreen file transfer display items... */ #ifndef NOCURSES #ifdef CK_NCURSES /* CK_NCURSES implies CK_CURSES */ #ifndef CK_CURSES #define CK_CURSES #endif /* CK_CURSES */ #endif /* CK_NCURSES */ #ifdef MYCURSES /* MYCURSES implies CK_CURSES */ #ifndef CK_CURSES #define CK_CURSES #endif /* CK_CURSES */ #endif /* MYCURSES */ #endif /* NOCURSES */ #ifdef NOCURSES #ifdef CK_CURSES #undef CK_CURSES #endif /* CK_CURSES */ #ifndef NODISPLAY #define NODISPLAY #endif /* NODISPLAY */ #endif /* NOCURSES */ #ifdef CK_CURSES /* The CK_WREFRESH symbol is defined if the curses library provides clearok() and wrefresh() functions, which are used in repainting the screen. */ #ifdef NOWREFRESH /* Override CK_WREFRESH */ #ifdef CK_WREFRESH /* If this is defined, */ #undef CK_WREFRESH /* undefine it. */ #endif /* CK_WREFRESH */ #else /* !NOWREFRESH */ /* No override... */ #ifndef CK_WREFRESH /* If CK_WREFRESH not defined */ /* Automatically define it for systems known to have it ... */ #ifdef VMS /* DEC (Open)VMS has it */ #define CK_WREFRESH #else #ifdef ultrix /* DEC ULTRIX has it */ #else #ifdef SVR3 /* System V has it */ #define CK_WREFRESH #else #ifdef BSD44 /* 4.4 BSD has it */ #define CK_WREFRESH #else #ifdef NEXT /* Define it for NeXTSTEP */ #define CK_WREFRESH #else #ifdef SUNOS4 /* SunOS 4.x... */ #define CK_WREFRESH #else #ifdef SOLARIS25 /* Solaris 2.5 and later */ #define CK_WREFRESH #else #ifdef AIXRS /* RS/6000 AIX ... */ #define CK_WREFRESH #else #ifdef RTAIX /* RT PC AIX ... */ #define CK_WREFRESH #else #ifdef OSF /* DEC OSF/1 ... */ #define CK_WREFRESH /* Add more here, or just define CK_WREFRESH on the CC command line... */ #endif /* OSF */ #endif /* RTAIX */ #endif /* AIXRS */ #endif /* SOLARIS25 */ #endif /* SUNOS4 */ #endif /* NEXT */ #endif /* BSD44 */ #endif /* SVR3 */ #endif /* ultrix */ #endif /* VMS */ #else /* CK_WREFRESH is defined */ /* This is within an ifdef CK_CURSES block. The following is not needed */ #ifndef CK_CURSES /* CK_WREFRESH implies CK_CURSES */ #define CK_CURSES #endif /* CK_CURSES */ #endif /* CK_WREFRESH */ #endif /* NOWREFRESH */ #ifndef TRMBUFL #ifdef BIGBUFOK #define TRMBUFL 16384 #else #ifdef DYNAMIC #define TRMBUFL 8192 #else #define TRMBUFL 1024 #endif /* BIGBUFOK */ #endif /* DYNAMIC */ #endif /* TRMBUFL */ #endif /* CK_CURSES */ /* Whether to use ckmatch() in all its glory for C-Shell-like patterns. If CKREGEX is NOT defined, all but * and ? matching are removed from ckmatch(). NOTE: Defining CKREGEX does not necessarily mean that ckmatch() regexes are used for filename matching. That depends on whether zxpand() in ck?fio.c calls ckmatch(). NOTE 2: REGEX is a misnomer -- these are not regular expressions in the computer-science sense (in which, e.g. "a*b" matches 0 or more 'a' characters followed by 'b') but patterns (in which "a*b" matches 'a' followed by 0 or more non-b characters, followed by b). */ #ifndef NOCKREGEX #ifndef CKREGEX #define CKREGEX #endif /* CKREGEX */ #endif /* NOCKREGEX */ /* Learned-script feature */ #ifndef NOLEARN #ifdef NOSPL #define NOLEARN #else #ifdef NOLOCAL #define NOLEARN #endif /* NOLOCAL */ #endif /* NOSPL */ #endif /* NOLEARN */ #ifdef NOLEARN #ifdef CKLEARN #undef CKLEARN #endif /* CKLEARN */ #else /* !NOLEARN */ #ifndef CKLEARN #ifdef OS2ORUNIX /* In UNIX this can work only with ckucns.c builds */ #define CKLEARN #else #ifdef VMS #define CKLEARN #endif /* VMS */ #endif /* OS2ORUNIX */ #endif /* CKLEARN */ #endif /* NOLEARN */ #ifdef CKLEARN #ifndef LEARNBUFSIZ #define LEARNBUFSIZ 128 #endif /* LEARNBUFSIZ */ #endif /* CKLEARN */ #ifndef IKSDONLY #ifndef CKTIDLE /* Pseudo-keepalive in CONNECT */ #ifdef OS2 /* In K95 */ #define CKTIDLE #else #ifdef UNIX /* In UNIX but only ckucns versions */ #ifndef NOLEARN #ifndef NOSELECT #define CKTIDLE #endif /* NOSELECT */ #endif /* NOLEARN */ #endif /* UNIX */ #endif /* OS2 */ #endif /* CKTIDLE */ #endif /* IKSDONLY */ #ifdef CK_ANSILIBS /* String library functions. For ANSI C, get prototypes from . Otherwise, skip the prototypes. */ #include /* Prototypes for other commonly used library functions, such as malloc, free, getenv, atol, atoi, and exit. Otherwise, no prototypes. */ #include #ifdef DIAB /* DIAB DS90 */ /* #include */ #include #define CK_WAIT_H #ifdef COMMENT extern void exit(int status); extern void _exit(int status); extern int uname(struct utsname *name); #endif /* COMMENT */ extern int chmod(char *path, int mode); extern int ioctl(int fildes, int request, ...); extern int rdchk(int ttyfd); extern int nap(int m); #ifdef COMMENT extern int getppid(void); #endif /* COMMENT */ extern int _filbuf(FILE *stream); extern int _flsbuf(char c,FILE *stream); #endif /* DIAB */ /* Prototypes for UNIX functions like access, alarm, chdir, sleep, fork, and pause. Otherwise, no prototypes. */ #ifdef VMS #include /* SMS: sleep() for old (V4.0-000) DEC C. */ #include #include /* SMS: getpid() for old (V4.0-000) DEC C. */ #endif /* VMS */ #ifdef NEXT #ifndef NEXT33 #include #endif /* NEXT33 */ #else /* NoT NeXT */ #ifndef AMIGA #ifndef OS2 #ifdef STRATUS #include #else /* !STRATUS */ #ifndef OSKXXC #include #endif /* OSKXXC */ #ifdef HAVE_CRYPT_H #include #endif /* HAVE_CRYPT_H */ #endif /* STRATUS */ #endif /* OS2 */ #endif /* AMIGA */ #endif /* NEXT */ #else /* Not ANSI libs... */ #ifdef MAC #include #include #endif /* MAC */ #ifdef HPUX #ifndef HPUXPRE65 #include #endif /* HPUXPRE65 */ #endif /* HPUX */ #ifdef SUNOS41 #include #include #else #ifndef MAC /* It is essential that these are declared correctly! Which is not always easy. Take malloc() for instance ... */ #ifdef PYRAMID #ifdef SVR4 #ifdef __STDC__ #define SIZE_T_MALLOC #endif /* __STDC__ */ #endif /* SVR4 */ #endif /* PYRAMID */ /* Maybe some other environments need the same treatment for malloc. If so, define SIZE_T_MALLOC for them here or in compiler CFLAGS. */ #ifdef SIZE_T_MALLOC _PROTOTYP( void * malloc, (size_t) ); #else _PROTOTYP( char * malloc, (unsigned int) ); #endif /* SIZE_T_MALLOC */ _PROTOTYP( char * getenv, (char *) ); _PROTOTYP( long atol, (char *) ); #endif /* !MAC */ #endif /* SUNOS41 */ #endif /* CK_ANSILIBS */ /* generally picks up NULL, MAXPATHLEN, and MAXNAMLEN and seems to present on all Unixes going back at least to SCO Xenix with the exception(s) noted. */ #ifndef NO_PARAM_H /* 2001-11-03 */ #ifndef UNIX /* Non-Unixes don't have it */ #define NO_PARAM_H #else #ifdef TRS16 /* Tandy Xenix doesn't have it */ #define NO_PARAM_H #endif /* TRS16 */ #endif /* UNIX */ #endif /* NO_PARAM_H */ #ifndef NO_PARAM_H #ifndef INCL_PARAM_H #define INCL_PARAM_H #endif /* INCL_PARAM_H */ #include #endif /* NO_PARAM_H */ #ifndef NULL /* In case NULL is still not defined */ #define NULL 0L /* or #define NULL 0 */ /* or #define NULL ((char *) 0) */ /* or #define NULL ((void *) 0) */ #endif /* NULL */ /* Macro to differentiate "" from NULL (to avoid comparisons with literals) */ #ifndef isemptystring #define isemptystring(s) ((s?(*s?0:1):0)) #endif /* isemptystring */ /* Maximum length for a fully qualified filename, not counting \0 at end. */ /* This is a rough cut, and errs on the side of being too big. We don't want to pull in hundreds of header files looking for many and varied symbols, for fear of introducing unnecessary conflicts. */ #ifndef CKMAXPATH #ifdef VMS /* VMS may have bad (small, ODS2) */ #define CKMAXPATH NAMX_C_MAXRSS /* PATH_MAX, so use NAMX_C_MAXRSS. */ #else /* def VMS */ #ifdef MAXPATHLEN /* (it probably isn't) */ #define CKMAXPATH MAXPATHLEN #else #ifdef PATH_MAX /* POSIX */ #define CKMAXPATH PATH_MAX #else /* def PATH_MAX */ #ifdef MAC #define CKMAXPATH 63 #else /* def MAC */ #ifdef pdp11 #define CKMAXPATH 255 #else /* def pdp11 */ #ifdef UNIX /* Even though some are way less... */ #define CKMAXPATH 1024 #else /* def UNIX */ #ifdef STRATUS #define CKMAXPATH 256 /* == $MXPL from PARU.H */ #else /* def STRATUS */ #ifdef datageneral #define CKMAXPATH 256 /* == $MXPL from PARU.H */ #else /* def datageneral */ #define CKMAXPATH 255 #endif /* def STRATUS [else] */ #endif /* def datageneral [else] */ #endif /* def UNIX [else] */ #endif /* def pdp11 [else] */ #endif /* def MAC [else] */ #endif /* def PATH_MAX [else] */ #endif /* def MAXPATHLEN [else] */ #endif /* def VMS [else] */ #endif /* ndef CKMAXPATH */ /* Maximum length for the name of a tty device */ #ifndef DEVNAMLEN #define DEVNAMLEN CKMAXPATH #endif /* DEVNAMLEN */ /* Directory (path segment) separator */ /* Not fully general - Tricky for VMS, Amiga, ... */ #ifndef DIRSEP #ifdef UNIX #define DIRSEP '/' #define ISDIRSEP(c) ((c)=='/') #else #ifdef OS2 #define DIRSEP '/' #define ISDIRSEP(c) ((c)=='/'||(c)=='\\') #else #ifdef datageneral #define DIRSEP ':' #define ISDIRSEP(c) (((c)==':')||((c)=='^')||((c)=='=')) #else #ifdef STRATUS #define DIRSEP '>' #define ISDIRSEP(c) ((c)=='>') #else #ifdef VMS #define DIRSEP ']' /* (not really) */ #define ISDIRSEP(c) ((c)==']'||(c)==':') #else #ifdef MAC #define DIRSEP ':' #define ISDIRSEP(c) ((c)==':') #else #ifdef AMIGA #define DIRSEP '/' #define ISDIRSEP(c) ((c)=='/'||(c)==':') #else #ifdef GEMDOS #define DIRSEP '\\' #define ISDIRSEP(c) ((c)=='\\'||(c)==':') #else #define DIRSEP '/' #define ISDIRSEP(c) ((c)=='/') #endif /* GEMDOS */ #endif /* AMIGA */ #endif /* MAC */ #endif /* VMS */ #endif /* STRATUS */ #endif /* datageneral */ #endif /* OS2 */ #endif /* UNIX */ #endif /* DIRSEP */ /* FILE package parameters */ #ifdef pdp11 #define NOCHANNELIO #else #ifndef CKMAXOPEN #ifdef QNX #define CKMAXOPEN 390 #else #ifdef VMS #define CKMAXOPEN 64 #else #ifdef OPEN_MAX #define CKMAXOPEN OPEN_MAX #else #ifdef FOPEN_MAX #define CKMAXOPEN FOPEN_MAX #else #define CKMAXOPEN 64 #endif /* FOPEN_MAX */ #endif /* OPEN_MAX */ #endif /* VMS */ #endif /* QNX */ #endif /* CKMAXOPEN */ /* Maximum channels for FOPEN = CKMAXOPEN minus logs, stdio, etc */ #ifndef Z_MINCHAN #define Z_MINCHAN 16 #endif /* Z_MINCHAN */ #ifndef Z_MAXCHAN #define Z_MAXCHAN (CKMAXOPEN-ZNFILS-5) #endif /* Z_MAXCHAN */ #endif /* pdp11 */ /* New-format nzltor() and nzrtol() functions that handle pathnames */ #ifndef NZLTOR #ifdef UNIX #define NZLTOR #else #ifdef VMS #define NZLTOR #else #ifdef OS2 #define NZLTOR #else #ifdef STRATUS #define NZLTOR #endif /* STRATUS */ #endif /* OS2 */ #endif /* VMS */ #endif /* UNIX */ #endif /* NZLTOR */ #ifdef NZLTOR _PROTOTYP( VOID nzltor, (char *, char *, int, int, int) ); _PROTOTYP( VOID nzrtol, (char *, char *, int, int, int) ); #endif /* NZLTOR */ /* Implementations with a zrmdir() function */ #ifndef ZRMDIR #ifdef OS2 #define ZRMDIR #else /* OS2 */ #ifdef UNIX #define ZRMDIR #else #ifdef VMS #define ZRMDIR #else /* VMS */ #ifdef STRATUS #define ZRMDIR #endif /* STRATUS */ #endif /* VMS */ #endif /* UNIX */ #endif /* OS2 */ #endif /* ZRMDIR */ #ifdef ZRMDIR _PROTOTYP( int zrmdir, (char *) ); #endif /* ZRMDIR */ #ifndef FILECASE #ifdef UNIXOROSK #define FILECASE 1 #else #define FILECASE 0 #endif /* UNIXOROSK */ #ifndef CKCMAI extern int filecase; #endif /* CKCMAI */ #endif /* FILECASE */ /* Funny names for library functions department... */ #ifdef ZILOG #define setjmp setret #define longjmp longret #define jmp_buf ret_buf #define getcwd curdir #endif /* ZILOG */ #ifdef STRATUS /* The C-runtime conflicts with things we do in Stratus VOS ckltio.c ... */ #define printf vosprtf _PROTOTYP( int vosprtf, (char *fmt, ...) ); #define perror(txt) printf("%s\n", txt) /* char_varying is a string type from PL/I that VOS uses extensively */ #define CV char_varying #endif /* STRATUS */ #ifdef NT extern int OSVer; #define isWin95() (OSVer==VER_PLATFORM_WIN32_WINDOWS) #else #define isWin95() (0) #endif /* NT */ #ifndef BPRINT #ifdef OS2 #define BPRINT #endif /* OS2 */ #endif /* BPRINT */ #ifndef SESLIMIT #ifdef OS2 #define SESLIMIT #endif /* OS2 */ #endif /* SESLIMIT */ #ifndef NOTERM #ifndef PCTERM #ifdef NT #define PCTERM #endif /* NT */ #endif /* PCTERM */ #endif /* NOTERM */ #ifdef BEOSORBEBOX #define query ckquery #endif /* BEOSORBEBOX */ #ifndef PTYORPIPE /* NETCMD and/or NETPTY defined */ #ifdef NETCMD #define PTYORPIPE #else #ifdef NETPTY #define PTYORPIPE #endif /* NETPTY */ #endif /* NETCMD */ #endif /* PTYORPIPE */ /* mktemp() and mkstemp() */ #ifndef NOMKTEMP #ifndef MKTEMP #ifdef OS2ORUNIX #define MKTEMP #endif /* OS2ORUNIX */ #endif /* MKTEMP */ #ifdef MKTEMP #ifndef NOMKSTEMP #ifndef MKSTEMP #ifdef BSD44 #define MKSTEMP #else #ifdef __linux__ #define MKSTEMP #endif /* __linux__ */ #endif /* BSD44 */ #endif /* MKSTEMP */ #endif /* NOMKSTEMP */ #endif /* MKTEMP */ #endif /* NOMKTEMP */ /* Platforms that have memcpy() -- only after all headers included */ #ifndef USE_MEMCPY #ifdef VMS #define USE_MEMCPY #else #ifdef NEXT #define USE_MEMCPY #else #ifdef OS2 #define USE_MEMCPY #else #ifdef __linux__ #define USE_MEMCPY #else #ifdef SOLARIS #define USE_MEMCPY #else #ifdef SUNOS4 #define USE_MEMCPY #else #ifdef AIXRS #define USE_MEMCPY #else #ifdef HPUX #define USE_MEMCPY #else #ifdef POSIX #define USE_MEMCPY #else #ifdef SVR4 #define USE_MEMCPY #else #ifdef OSF #define USE_MEMCPY #else #ifdef datageneral #define USE_MEMCPY #else #ifdef STRATUS #define USE_MEMCPY #endif /* STRATUS */ #endif /* datageneral */ #endif /* OSF */ #endif /* SVR4 */ #endif /* POSIX */ #endif /* HPUX */ #endif /* AIXRS */ #endif /* SUNOS4 */ #endif /* SOLARIS */ #endif /* __linux__ */ #endif /* OS2 */ #endif /* NEXT */ #endif /* VMS */ #endif /* USE_MEMCPY */ #ifndef USE_MEMCPY #define memcpy(a,b,c) ckmemcpy((a),(b),(c)) #else #ifdef CK_SCO32V4 /* Because the prototype isn't picked up in the normal header files */ _PROTOTYP( void *memcpy, (void *, const void *, size_t)); #endif /* CK_SCO32V4 */ #endif /* USE_MEMCPY */ /* User authentication for IKS -- So far K95 and UNIX only */ #ifdef NOICP #ifndef NOLOGIN #define NOLOGIN #endif /* NOLOGIN */ #endif /* NOICP */ #ifndef NOLOGIN #ifdef OS2ORUNIX #ifndef CK_LOGIN #define CK_LOGIN #ifndef NOSHADOW #ifdef CK_SCOV5 #define CK_SHADOW #endif /* CK_SCOV5 */ #endif /* NOSHADOW */ #endif /* CK_LOGIN */ #ifdef NT #define NTCREATETOKEN #endif /* NT */ #endif /* OS2ORUNIX */ #else /* NOLOGIN */ #ifdef CK_LOGIN #undef CK_LOGIN #endif /* CK_LOGIN */ #endif /* NOLOGIN */ #ifdef OS2 #define CKSPINNER #endif /* OS2 */ #ifdef CK_LOGIN /* Telnet protocol required */ #ifndef TNCODE /* for login to IKSD. */ #define TNCODE #endif /* TNCODE */ #endif /* CK_LOGIN */ #ifdef CK_AUTHENTICATION #ifdef NOSENDUID #undef NOSENDUID #endif /* NOSENDUID */ #endif /* CK_AUTHENTICATION */ #ifdef TNCODE /* Should TELNET send user ID? */ #ifndef NOSENDUID #ifndef CKSENDUID #define CKSENDUID #endif /* CKSENDUID */ #endif /* NOSENDUID */ #endif /* TNCODE */ /* UNIX platforms that don't have getusershell() */ #ifdef UNIX #ifndef NOGETUSERSHELL #ifdef IRIX #define NOGETUSERSHELL #else #ifdef PTX #define NOGETUSERSHELL #else #ifdef AIXRS #define NOGETUSERSHELL #else #ifdef SINIX #define NOGETUSERSHELL #else #ifdef UNIXWARE #define NOGETUSERSHELL #else #ifdef COHERENT #define NOGETUSERSHELL #endif /* COHERENT */ #endif /* UNIXWARE */ #endif /* SINIX */ #endif /* AIXRS */ #endif /* PTX */ #endif /* IRIX */ #endif /* NOGETUSERSHELL */ #endif /* UNIX */ #ifdef CK_LOGIN #ifdef NT #ifndef NOSYSLOG #ifndef CKSYSLOG #define CKSYSLOG #endif /* CKSYSLOG */ #endif /* NOSYSLOG */ #endif /* NT */ #ifdef UNIX #ifndef NOSYSLOG #ifndef CKSYSLOG #define CKSYSLOG #endif /* CKSYSLOG */ #endif /* NOSYSLOG */ #ifndef NOWTMP #ifndef CKWTMP #define CKWTMP #endif /* CKWTMP */ #endif /* NOWTMP */ #ifndef NOGETUSERSHELL #ifndef GETUSERSHELL #define GETUSERSHELL #endif /* GETUSERSHELL */ #endif /* NOGETUSERSHELL */ #endif /* UNIX */ _PROTOTYP( int ckxlogin, (CHAR *, CHAR *, CHAR *, int)); _PROTOTYP( int ckxlogout, (VOID)); #endif /* CK_LOGIN */ #ifndef NOZLOCALTIME /* zlocaltime() available. */ #ifdef OS2ORUNIX #define ZLOCALTIME _PROTOTYP( char * zlocaltime, (char *) ); #endif /* OS2ORUNIX */ #endif /* NOZLOCALTIME */ #ifdef CKSYSLOG /* Syslogging levels */ #define SYSLG_NO 0 /* No logging */ #define SYSLG_LI 1 /* Login/out */ #define SYSLG_DI 2 /* Dialing out */ #define SYSLG_AC 3 /* Making any kind of connection */ #define SYSLG_PR 4 /* Protocol Operations */ #define SYSLG_FC 5 /* File creation */ #define SYSLG_FA 6 /* File reading */ #define SYSLG_CM 7 /* Top-level commands */ #define SYSLG_CX 8 /* All commands */ #define SYSLG_DB 9 /* Debug */ #define SYSLGMAX 9 /* Highest level */ #define SYSLG_DF SYSLG_FA /* Default level */ /* Logging function */ _PROTOTYP(VOID cksyslog,(int, int, char *, char *, char *)); #endif /* CKSYSLOG */ #ifndef CKCMAI extern int ckxlogging, ckxsyslog, ikdbopen; #endif /* CKCMAI */ #ifndef CK_KEYTAB #define CK_KEYTAB /* Keyword Table Template */ /* Note: formerly defined in ckucmd.h but now more widely used */ struct keytab { /* Keyword table */ char *kwd; /* Pointer to keyword string */ int kwval; /* Associated value */ int flgs; /* Flags (as defined above) */ }; #endif /* CK_KEYTAB */ #ifdef UNIX _PROTOTYP( int isalink, (char *)); #endif /* UNIX */ #ifdef NETPTY _PROTOTYP( int do_pty, (int *, char *, int)); _PROTOTYP( VOID end_pty, (void)); #endif /* NETPTY */ #ifdef CKROOT _PROTOTYP( int zsetroot, (char *) ); _PROTOTYP( char * zgetroot, (void) ); _PROTOTYP( int zinroot, (char *) ); #endif /* CKROOT */ /* Local Echo Buffer prototypes */ _PROTOTYP( VOID le_init, (void) ); _PROTOTYP( VOID le_clean, (void)); _PROTOTYP( int le_inbuf, (void)); _PROTOTYP( int le_putstr, (CHAR *)); _PROTOTYP( int le_puts, (CHAR *, int)); _PROTOTYP( int le_putchar, (CHAR)); _PROTOTYP( int le_getchar, (CHAR *)); /* #ifndef NOHTTP */ #ifndef NOCMDATE2TM #ifndef CMDATE2TM #ifdef OS2ORUNIX #define CMDATE2TM #endif /* OS2ORUNIX */ #ifdef VMS #define CMDATE2TM #endif /* VMS */ #endif /* CMDATE2TM */ #endif /* NOCMDATE2TM */ #ifdef CMDATE2TM _PROTOTYP( struct tm * cmdate2tm, (char *,int)); #endif /* CMDATE2TM */ /* #endif */ /* NOHTTP */ #ifndef NOSETTIME /* This would be set in CFLAGS */ #ifdef SVR4ORPOSIX /* Defined in IEEE 1003.1-1996 */ #ifndef UTIMEH /* and in SVID for SVR4 */ #define UTIMEH #endif /* UTIMEH */ #else /* SVR4ORPOSIX */ #ifdef OSF /* Verified by Lucas Hart */ #ifndef UTIMEH #define UTIMEH #endif /* UTIMEH */ #else /* OSF */ #ifdef SUNOS41 /* Verified by Lucas Hart */ #ifndef UTIMEH #define UTIMEH #endif /* UTIMEH */ #else /* SUNOS41 */ #ifdef OS2 #ifndef SYSUTIMEH #define SYSUTIMEH #endif /* SYSUTIMEH */ #else /* OS2 */ #ifdef VMS #ifndef UTIMEH #define UTIMEH #endif /* UTIMEH */ #endif /* VMS */ #endif /* OS2 */ #endif /* SUNOS41 */ #endif /* OSF */ #endif /* SVR4ORPOSIX */ #endif /* NOSETTIME */ #ifdef NEWFTP _PROTOTYP( int ftpisconnected, (void)); _PROTOTYP( int ftpisloggedin, (void)); _PROTOTYP( int ftpissecure, (void)); #endif /* NEWFTP */ _PROTOTYP( int readpass, (char *, char *, int)); _PROTOTYP( int readtext, (char *, char *, int)); #ifdef OS2 _PROTOTYP(int ck_auth_loaddll, (VOID)); _PROTOTYP(int ck_auth_unloaddll, (VOID)); #endif /* OS2 */ #ifdef NT _PROTOTYP(DWORD ckGetLongPathname,(LPCSTR lpFileName, LPSTR lpBuffer, DWORD cchBuffer)); #endif /* NT */ #include "ckclib.h" /* End of ckcdeb.h */ #endif /* CKCDEB_H */ ckcfn2.c0000644000015300001460000030344011573174422011205 0ustar fdckermit/* C K C F N 2 -- System-independent Kermit protocol support functions... */ /* ...Part 2 (continued from ckcfns.c) */ /* Author: Frank da Cruz , Columbia University Academic Information Systems, New York City. Copyright (C) 1985, 2011, Trustees of Columbia University in the City of New York. All rights reserved. See the C-Kermit COPYING.TXT file or the copyright text in the ckcmai.c module for disclaimer and permissions. */ /* Note -- if you change this file, please amend the version number and date at the top of ckcfns.c accordingly. */ #include "ckcsym.h" /* Compilation options */ #include "ckcdeb.h" /* Debugging and other symbols */ #include "ckcasc.h" /* ASCII symbols */ #include "ckcker.h" /* Kermit symbols */ #include "ckcxla.h" /* Translation */ #include "ckcnet.h" /* IKS and VMS #define TCPSOCKET */ #ifdef TCPSOCKET /* For TELNET business in spack() */ extern int tn_nlm, ttnproto, tn_b_nlm; #endif /* TCPSOCKET */ extern int parity, network, local, interrupted, fatalio, wasclosed; int kstartactive = 0; /* Flag for kstart() in a packet */ static CHAR p_tbl[] = { /* Even parity table for dopar(). */ (CHAR) '\000', /* ANSI C casts '\ooo' constants */ (CHAR) '\201', /* to signed char, so we have to */ (CHAR) '\202', /* cast back to unsigned char... */ (CHAR) '\003', (CHAR) '\204', (CHAR) '\005', (CHAR) '\006', (CHAR) '\207', (CHAR) '\210', (CHAR) '\011', (CHAR) '\012', (CHAR) '\213', (CHAR) '\014', (CHAR) '\215', (CHAR) '\216', (CHAR) '\017', (CHAR) '\220', (CHAR) '\021', (CHAR) '\022', (CHAR) '\223', (CHAR) '\024', (CHAR) '\225', (CHAR) '\226', (CHAR) '\027', (CHAR) '\030', (CHAR) '\231', (CHAR) '\232', (CHAR) '\033', (CHAR) '\234', (CHAR) '\035', (CHAR) '\036', (CHAR) '\237', (CHAR) '\240', (CHAR) '\041', (CHAR) '\042', (CHAR) '\243', (CHAR) '\044', (CHAR) '\245', (CHAR) '\246', (CHAR) '\047', (CHAR) '\050', (CHAR) '\251', (CHAR) '\252', (CHAR) '\053', (CHAR) '\254', (CHAR) '\055', (CHAR) '\056', (CHAR) '\257', (CHAR) '\060', (CHAR) '\261', (CHAR) '\262', (CHAR) '\063', (CHAR) '\264', (CHAR) '\065', (CHAR) '\066', (CHAR) '\267', (CHAR) '\270', (CHAR) '\071', (CHAR) '\072', (CHAR) '\273', (CHAR) '\074', (CHAR) '\275', (CHAR) '\276', (CHAR) '\077', (CHAR) '\300', (CHAR) '\101', (CHAR) '\102', (CHAR) '\303', (CHAR) '\104', (CHAR) '\305', (CHAR) '\306', (CHAR) '\107', (CHAR) '\110', (CHAR) '\311', (CHAR) '\312', (CHAR) '\113', (CHAR) '\314', (CHAR) '\115', (CHAR) '\116', (CHAR) '\317', (CHAR) '\120', (CHAR) '\321', (CHAR) '\322', (CHAR) '\123', (CHAR) '\324', (CHAR) '\125', (CHAR) '\126', (CHAR) '\327', (CHAR) '\330', (CHAR) '\131', (CHAR) '\132', (CHAR) '\333', (CHAR) '\134', (CHAR) '\335', (CHAR) '\336', (CHAR) '\137', (CHAR) '\140', (CHAR) '\341', (CHAR) '\342', (CHAR) '\143', (CHAR) '\344', (CHAR) '\145', (CHAR) '\146', (CHAR) '\347', (CHAR) '\350', (CHAR) '\151', (CHAR) '\152', (CHAR) '\353', (CHAR) '\154', (CHAR) '\355', (CHAR) '\356', (CHAR) '\157', (CHAR) '\360', (CHAR) '\161', (CHAR) '\162', (CHAR) '\363', (CHAR) '\164', (CHAR) '\365', (CHAR) '\366', (CHAR) '\167', (CHAR) '\170', (CHAR) '\371', (CHAR) '\372', (CHAR) '\173', (CHAR) '\374', (CHAR) '\175', (CHAR) '\176', (CHAR) '\377' }; /* D O P A R -- Add an appropriate parity bit to a character */ CHAR #ifdef CK_ANSIC dopar(register CHAR ch) #else dopar(ch) register CHAR ch; #endif /* CK_ANSIC */ { register unsigned int a; if (!parity #ifdef TCPSOCKET || (network && (ttnproto == NP_TELNET) && (TELOPT_ME(TELOPT_BINARY))) #ifndef NOXFER || (!local && sstelnet) /* TELNET BINARY MODE */ #endif /* NOXFER */ #endif /* TCPSOCKET */ ) return((CHAR) (ch & 255)); else a = ch & 127; switch (parity) { case 'e': return(p_tbl[a]); /* Even */ case 'm': return((CHAR) (a | 128)); /* Mark */ case 'o': return((CHAR) (p_tbl[a] ^ 128)); /* Odd */ case 's': return((CHAR) a); /* Space */ default: return((CHAR) a); /* Something illegal */ } } #ifndef NOXFER /* Rest of this file... */ #define NEWDPL /* New dynamic packet length method */ #ifdef VMS extern int batch; #else extern int backgrd; #endif /* VMS */ #ifdef DYNAMIC extern struct pktinfo *s_pkt; /* array of pktinfo structures */ extern struct pktinfo *r_pkt; /* array of pktinfo structures */ #else extern struct pktinfo s_pkt[]; /* array of pktinfo structures */ extern struct pktinfo r_pkt[]; /* array of pktinfo structures */ #endif /* DYNAMIC */ extern int sseqtbl[], rseqtbl[], sbufuse[], sacktbl[], wslots, winlo, wslotn, sbufnum, rbufnum, pktpaus, reliable; #ifdef STREAMING static int dontsend = 0; extern int streaming; #endif /* STREAMING */ extern int ttprty; /* from ck*tio.c */ extern int autopar; extern int spsiz, spmax, rpsiz, timint, timef, npad, bestlen, maxsend; extern int rpt, rptq, rptflg, capas, spsizf, en_fin, tsecs, flow; extern int pktnum, sndtyp, rcvtyp, bctr, bctu, bctf, bctl, rsn, rln, maxtry; extern int size, osize, maxsize, spktl, rpktl, nfils, stdouf, fsecs; extern int turn, turnch, displa, pktlog, seslog, xflg, mypadn; extern int hcflg, server, cxseen, czseen, discard, slostart; extern int nakstate, quiet, success, xitsta, what, filestatus; extern int spackets, rpackets, timeouts, retrans, crunched, urpsiz; extern int carrier, fdispla, srvidl; #ifdef GFTIMER extern CKFLOAT fptsecs, fpfsecs, fpxfsecs; #endif /* GFTIMER */ extern long filcnt, filrej, speed, filcps, tfcps; extern CK_OFF_T ffc, flci, flco, tlci, tlco, tfc; extern char *cmarg, filnam[]; extern CHAR padch, mypadc, eol, seol, ctlq, sstate; extern CHAR *recpkt, *data, myinit[]; extern CHAR *srvptr, stchr, mystch, *rdatap; extern CHAR padbuf[]; extern CHAR * epktmsg; extern int epktrcvd, epktsent; #ifdef OS2 /* AUTODOWNLOAD parameters */ extern int adl_kmode, adl_zmode; /* Match Packet to signal download */ extern char * adl_kstr; /* KERMIT Download String */ extern char * adl_zstr; /* ZMODEM Download String */ #endif /* OS2 */ #ifdef CK_AUTODL CHAR ksbuf[96] = { NUL, NUL }; /* Autodownload "Kermit Start" buf */ #endif /* CK_AUTODL */ int numerrs = 0; /* Number of packet errors so far */ int rcvtimo = 0; /* Timeout for receiving a packet */ int idletmo = 0; /* Flag for idle timeout */ long filcps = 0L; /* CPS most recent file transferred */ long tfcps = 0L; /* CPS most recent transaction */ long xfsecs = 0L; /* Elapsed time for most recent file */ #ifdef GFTIMER CKFLOAT fpxfsecs = 0.0; /* Ditto, but floating point */ #endif /* GFTIMER */ #ifdef CK_TIMERS int rrttbl[64], srttbl[64]; /* Packet timestamp tables */ extern int rttflg; #define RTT_SCALE 1000 long rttsamples, /* Round trip time samples */ rttdelay, /* RTT delay */ pktintvl, /* Interpacket arrival time */ rttvariance, /* RTT variance */ rttstddev; /* RTT standard deviation */ #endif /* CK_TIMERS */ /* CRC generation tables */ long crcta[16] = { 0L, 010201L, 020402L, 030603L, 041004L, 051205L, 061406L, 071607L, 0102010L, 0112211L, 0122412L, 0132613L, 0143014L, 0153215L, 0163416L, 0173617L }; long crctb[16] = { 0L, 010611L, 021422L, 031233L, 043044L, 053655L, 062466L, 072277L, 0106110L, 0116701L, 0127532L, 0137323L, 0145154L, 0155745L, 0164576L, 0174367L }; #ifdef CK_TIMERS /* Round-trip timer calculations adapted from Tim Kientzle's article, "Improving Kermit Performance", Dr Dobb's Journal, February 1996. */ /* R T T I N I T -- Initialize timers at start of transaction */ VOID rttinit() { /* Initialize round-trip timing */ int i; if (timint == 0) return; rttsamples = 0L; /* Samples (packets) */ rttvariance = 0L; /* Variance in delay */ rttdelay = (long) timint * RTT_SCALE; /* Delay */ pktintvl = (long) timint * RTT_SCALE; /* Delay */ rttstddev = (long) timint * RTT_SCALE; /* Standard deviation of delay */ /* Tables of timestamps indexed by packet sequence number */ for (i = 0; i < 64; i++) { rrttbl[i] = -1; /* Time each packet was received */ srttbl[i] = -1; /* Time each packet was sent */ } rcvtimo = timint; /* Initial timeout is what user said */ } /* G E T R T T -- Get packet round trip time */ /* Call with nakstate == 0 if file sender, nonzero if receiver, and n == packet sequence number of the packet we just received. Returns: -1 on failure with rcvtimo set to timint (what the user said), or: 0 on success with rcvtimo set to dynamically calculated value: 1 <= rcvtimo <= timint * 3. */ int getrtt(nakstate, n) int nakstate, n; { extern int mintime, maxtime; static int prevz = 0, prevr = 0; int x, y, yy, z = 0, zz = 0; /* How long did it take to get here? */ rcvtimo = timint; /* Default timeout is what user said */ if (timint == 0) /* We're not timing out. */ return(0); if (!rttflg) /* Not supposed to be doing this? */ return(-1); /* So don't */ if (!RTT_SCALE) /* Paranoia... */ return(-1); /* rtimer() (reset timer) is not called until 1st data packet */ #ifdef GFTIMER /* rftimer(); */ #endif /* GFTIMER */ /* S (F [ A ] D* Z)* B */ /* NOTE: we calculate both the round-trip time AND the packet */ /* arrival rate. We don't use the RTT for anything, we just display it. */ /* Timeouts are based on the packet arrival rate. */ if (spackets > 3) { /* Don't start till 4th packet */ if (nakstate) { /* File receiver */ x = rrttbl[n]; /* Time when I got packet n */ y = rrttbl[n > 0 ? n - 1 : 63]; /* Time when I got packet n-1 */ yy = srttbl[n > 0 ? n - 1 : 63]; /* Time when I sent ACK(n-1) */ if (x > -1 && y > -1) { /* Be careful */ z = x - y; /* Packet rate */ zz = x - yy; /* Round trip time */ z++; /* So sender & receiver differ */ debug(F101,"RTT RECV","",z); } else { /* This shouldn't happen */ debug(F101,"RTT RECV ERROR spackets","",spackets); debug(F101,"RTT RECV ERROR sequence","",n); return(-1); } } else { /* File sender */ x = rrttbl[n]; /* Time when I got ACK(n) */ y = rrttbl[n > 0 ? n - 1 : 63]; /* Time when I got packet n-1 */ yy = srttbl[n]; /* Time when I sent n */ if (x > -1 && y > -1) { z = x - y; /* Packet rate */ zz = x - yy; /* Round trip time */ debug(F101,"RTT SEND","",z); } else { debug(F100,"RTT SEND ERROR","",0); return(-1); } } if (z < 1) /* For fast connections */ z = RTT_SCALE / 2; /* Convert to scale... */ else z *= RTT_SCALE; debug(F101,"RTT z scaled","",z); if (zz < 1) /* For fast connections */ zz = RTT_SCALE / 2; /* Convert to scale... */ else zz *= RTT_SCALE; rttdelay = zz; /* Round trip time of this packet */ #ifdef COMMENT /* This was used in C-Kermit 7.0 (and 6.0?) but not only is it overkill, it also can produce ridiculously long timeouts under certain conditions. Replaced in 8.0 by a far simpler and more aggressive strategy. */ if (rttsamples++ == 0L) { /* First sample */ pktintvl = z; } else { /* Subsequent samples */ long oldavg = pktintvl; long rttdiffsq; if (rttsamples > 30) /* Use real average for first 30 */ rttsamples = 30; /* then decaying average. */ /* Average delay, difference squared, variance, std deviation */ pktintvl += (z - pktintvl) / rttsamples; rttdiffsq = (z - oldavg) * (z - oldavg); rttvariance += (rttdiffsq - rttvariance) / rttsamples; debug(F101,"RTT stddev1","",rttstddev); if (rttstddev < 1L) /* It can be zero, in which case */ rttstddev = RTT_SCALE / 3; /* set it to something small... */ rttstddev = (rttstddev + rttvariance / rttstddev) / 2; } debug(F101,"RTT stddev2","",rttstddev); debug(F101,"RTT delay ","",pktintvl); rcvtimo = (pktintvl + (3L * rttstddev)) / RTT_SCALE + 1; if (rpackets < 32) /* Allow for slow start */ rcvtimo += rcvtimo + 2; else if (rpackets < 64) rcvtimo += rcvtimo / 2 + 1; /* On a reliable link, don't try too hard to time out. */ /* Especially on fast local network connections. */ if (server && what == W_NOTHING) /* Server command wait */ rcvtimo = rcvtimo; /* == srvtim */ else if (reliable == SET_ON && rcvtimo > 0) /* Reliable */ rcvtimo = rcvtimo +15; /* and not server command wait */ else /* Not reliable or server cmd wait */ rcvtimo = rcvtimo; if (rcvtimo < mintime) /* Lower bound */ rcvtimo = mintime; if (maxtime > 0) { /* User specified an upper bound */ if (rcvtimo > maxtime) rcvtimo = maxtime; } else if (maxtime == 0) { /* User didn't specify */ if (rcvtimo > timint * 6) rcvtimo = timint * 6; } #else /* COMMENT */ #ifdef CKFLOAT { CKFLOAT x; x = (CKFLOAT)(prevz + z + z) / 3.0; rcvtimo = (int)((((CKFLOAT)x * 2.66) / RTT_SCALE) + 0.5); debug(F101,"RTT rcvtimo (float)","",rcvtimo); } #else rcvtimo = (prevz + z + z) / RTT_SCALE; debug(F101,"RTT rcvtimo (int)","",rcvtimo); #endif /* CKFLOAT */ #endif /* COMMENT */ zz = (rttdelay + 500) / 1000; if (rcvtimo > (zz * 3)) rcvtimo = zz * 3; if (rcvtimo < 1) rcvtimo = 1; if (mintime > 0) { if (rcvtimo < mintime) /* Lower bound */ rcvtimo = mintime; } if (maxtime > 0) { /* Upper bound */ if (rcvtimo > maxtime) rcvtimo = maxtime; } if (rcvtimo == (prevr - 1)) rcvtimo++; debug(F101,"RTT final rcvtimo","",rcvtimo); } prevz = z; prevr = rcvtimo; return(0); } #endif /* CK_TIMERS */ /* I N P U T -- Attempt to read packet number 'pktnum'. */ /* This is the function that feeds input to Kermit's finite state machine, in the form of a character in the range 32-126, normally a packet type (uppercase letter) or pseudo-packet-type (lowercase letter). If a special start state is in effect, that state is returned as if it were the type of an incoming packet. */ int input() { int type = 0, acktype; /* Received packet type */ int x, y, k; /* Workers */ int z, pi, nf; /* Worker, packet index, NAK flag */ int nak2ack = 0; debug(F000,"input sstate","",sstate); debug(F101,"input nakstate","",nakstate); debug(F000,"input sndtyp","",sndtyp); debug(F101,"input xitsta","",xitsta); debug(F101,"input what","",what); while (1) { /* Big loop... */ /* It is ttchk()'s responsibility to tell us if the connection is broken, and to do so instantly and nondestructively -- no blocking, etc, that would slow down file transfer. */ if (ttchk() < 0) { debug(F100,"input CONNECTION BROKEN","",0); fatalio = 1; return('q'); } if (sstate != 0) { /* If a start state is in effect, */ type = sstate; /* return it like a packet type, */ sstate = 0; /* and then nullify it. */ numerrs = 0; /* (PWP) no errors so far */ return(type); } if (nakstate) { /* This section for file receiver. */ if (wslots > 1) { /* If we're doing windows, */ x = rseqtbl[winlo]; /* see if desired packet already in. */ debug(F101,"input winlo","",winlo); debug(F101,"input rseqtbl[winlo]","",rseqtbl[winlo]); if (x > -1) { /* Already there? */ if (r_pkt[x].pk_seq == winlo) { /* (double check) */ rsn = winlo; /* Yes, return its info */ debug(F101,"input return pre-stashed packet","",rsn); dumprbuf(); rdatap = r_pkt[x].pk_adr; /* like rpack would do. */ rln = (int)strlen((char *) rdatap); type = r_pkt[x].pk_typ; break; } } } type = rpack(); /* Try to read a packet. */ debug(F101,"input rpack","",type); while (type == 'e') { /* Handle echoes */ debug(F101,"input echo discarded","",type); type = rpack(); } #ifdef DEBUG if (deblog) { if (type == 'D') debug(F011,"input type D=",(char *)rdatap,39); else debug(F000,"input type",(char *)rdatap,type); } #endif /* DEBUG */ #ifndef OLDCHKINT if (type == 'z') { epktrcvd = 1; errpkt((CHAR *)"User cancelled."); type = 'E'; break; } #endif /* OLDCHKINT */ if (type < -1) { char * s; s = (type == -2) ? "FAILED - Interrupted" : "FAILED - Connection lost"; xxscreen(SCR_PT,'q',0L,s); dologend(); return('q'); /* Ctrl-C or connection lost */ } if (type < 0) { /* Receive window full */ /* Another thing to do here would be to delete */ /* the highest packet and NAK winlo. But that */ /* shouldn't be necessary since the other Kermit */ /* should not have sent a packet outside the window. */ #ifdef COMMENT char foo[256]; ckmakxmsg(foo,256,"Receive window full (rpack): wslots=", ckitoa(wslots)," winlo=",ckitoa(winlo)," pktnum=", ckitoa(pktnum), NULL,NULL,NULL,NULL,NULL,NULL); errpkt((CHAR *)foo); debug(F100,foo,"",0); #else errpkt((CHAR *)"Receive window full"); debug(F101,"rpack receive window full","",0); debug(F101," wslots","",wslots); debug(F101," winlo","",winlo); debug(F101," pktnum","",pktnum); #endif dumprbuf(); type = 'E'; break; } dumprbuf(); #ifdef OLDCHKINT if (chkint() < 0) { /* Check for console interrupts. */ errpkt((CHAR *)"User cancelled."); /* (old way) */ type = 'E'; break; } #endif /* OLDCHKINT */ #ifdef STREAMING if (streaming) { /* Streaming */ if (type == 'Q' || type == 'T') { /* Errors are fatal. */ crunched++; /* For statistics */ errpkt((CHAR *)"Transmission error on reliable link."); type = 'E'; } } #endif /* STREAMING */ if (type == 'E') { debug(F101,"input got E, nakstate","",nakstate); break; /* Error packet */ } if (type == 'Q') { /* Crunched packet. */ crunched++; numerrs++; /* Packet arrived damaged. It was most likely the packet we were expecting next, so we send a NAK for that packet. Prior to 5A(189), we always NAK'd winlo here, but that was bad because if two (or more) different packets were damaged, we would keep NAKing the first one and never NAK the other ones, which could result in a lengthy series of timeouts. Now we NAK the oldest as-yet-unNAK'd missing packet. */ #ifdef CK_TIMERS rcvtimo++; /* Stretch the timeout a little */ #endif /* CK_TIMERS */ z = (winlo + wslots) % 64; /* Search from winlo to z */ debug(F101,"ZZZ crunched z","",z); nf = 0; /* NAK flag not set yet */ for (x = winlo; x != z; x = (x + 1) % 64) { debug(F101,"ZZZ x","",x); if (rseqtbl[x] > -1) /* Have I received packet x? */ continue; /* Yes, go on. */ debug(F101,"ZZZ x not recd yet","",x); pi = sseqtbl[x]; /* No, have I NAK'd it yet? */ if (pi < 0 || s_pkt[pi].pk_rtr == 0) { debug(F101,"ZZZ x not NAK'd yet","",x); nack(x); /* No, NAK it now. */ nf = 1; /* Flag that I did. */ break; } } if (!nf) { /* If we didn't NAK anything above, */ debug(F101,"ZZZ NAKing winlo","",winlo); if (nack(winlo) < 0) { /* we have to NAK winlo (again) */ errpkt((CHAR *)"Too many retries."); /* Too many */ type = 'E'; break; } } continue; } if (type == 'T') { /* Timeout */ #ifndef OS2 /* K95 does this its own way */ if (server && srvidl) { idletmo = 1; debug(F101,"SERVER IDLE TIMEOUT","",srvidl); return('q'); } #endif /* OS2 */ #ifdef CK_TIMERS rcvtimo++; /* Stretch the timeout a little */ #endif /* CK_TIMERS */ timeouts++; debug(F101,"input receive-state timeout, winlo","",winlo); /* NAK only the packet at window-low */ debug(F101,"input sending NAK for winlo","",winlo); x = ttchk(); if (x > 0) /* Don't give up if there is still */ continue; /* something to read. */ else if (x < 0) { dologend(); fatalio = 1; return('q'); /* Connection Lost */ } if (nack(winlo) < 0) { debug(F101,"input sent too many naks","",winlo); errpkt((CHAR *)"Too many retries."); type = 'E'; break; } else continue; } if (rsn == winlo) { /* Got the packet we want, done. */ #ifdef CK_TIMERS if (rttflg && timint) /* Dynamic round trip timers? */ getrtt(nakstate, rsn); /* yes, do it. */ else /* JHD 20100208 */ rcvtimo = timint; /* JHD 20100208 */ #endif /* CK_TIMERS */ debug(F101,"input rsn=winlo","",rsn); break; } /* Got a packet out of order. */ debug(F101,"input out of sequence, rsn","",rsn); k = rseqtbl[rsn]; /* Get window slot of this packet. */ debug(F101,"input rseqtbl[rsn]","",k); if (k < 0) { debug(F101,"input recv can't find index for rcvd pkt","",rsn); /* Was "Internal error 21" */ /* This should not happen */ errpkt((CHAR *)"Sliding windows protocol error."); type = 'E'; break; } y = chkwin(rsn,winlo,wslots); /* See what window it's in. */ debug(F101,"input recv chkwin","",y); if (y == 1) { /* From previous window. */ #ifdef STREAMING if (!streaming) /* NO RESEND IF STREAMING! */ #endif /* STREAMING */ resend(rsn); /* Resend the ACK (might have data) */ freerpkt(rsn); /* Get rid of received packet */ continue; /* Back to wait for another packet */ } else { /* In this window or out of range */ if (y < 0) /* If out of range entirely, */ freerpkt(rsn); /* release its buffer */ #ifdef STREAMING if (streaming) { /* Streaming (this shouldn't happen) */ errpkt((CHAR *)"Sequence error on reliable link."); type = 'E'; break; } #endif /* STREAMING */ /* If our receive window is full, NAK window-low */ if (rbufnum < 1) { /* Receive window full? */ if (nack(winlo) < 0) { /* No choice, must NAK winlo. */ errpkt((CHAR *)"Too many retries."); /* Too many */ type = 'E'; break; } else continue; } /* Receive window not full. This is a packet in the current window but it is not the desired packet at winlo. So therefore there are gaps before this packet. So we find the "lowest" unNAK'd missing packet, if any, between winlo and this one, and NAK it. If there are no as-yet-unNAK'd missing packets in the window, then we send nothing and go wait for another packet. In theory, this could result in a timeout, but in practice it is likely that the already-NAK'd missing packets are already on their way. Note, we do not NAK ahead of ourselves, as that only creates unnecessary retransmissions. */ for (x = winlo; x != rsn; x = (x + 1) % 64) { if (rseqtbl[x] > -1) /* Have I received packet x? */ continue; /* Yes, check next sequence number. */ pi = sseqtbl[x]; /* No, have I NAK'd it yet? */ if (pi < 0 || s_pkt[pi].pk_rtr == 0) { nack(x); /* No, NAK it now. */ break; } } } /*!!!*/ } else { /* Otherwise file sender... */ #ifdef STREAMING if (streaming && sndtyp == 'D') { debug(F101,"STREAMING input streaming","",streaming); debug(F000,"STREAMING input sndtyp","",sndtyp); rsn = winlo; type = 'Y'; /* Pretend we got an ACK */ } #endif /* STREAMING */ if (!nak2ack) { /* NAK(n+1) = ACK(n) */ if (wslots > 1) { /* Packet at winlo already ACK'd? */ if (sacktbl[winlo]) { /* If so, */ sacktbl[winlo] = 0; /* Turn off the ACK'd flag */ winlo = (winlo + 1) % 64; /* Rotate the window */ type = 'Y'; /* And return ACK */ debug(F101, "input send returning pre-stashed ACK","", winlo-1); break; } } #ifdef STREAMING if (!(streaming && sndtyp == 'D')) { /* Not streaming | data */ type = rpack(); /* Try to read an acknowledgement */ } else { /* Streaming and in Data phase */ type = 'Y'; /* Assume all is normal */ if (chkint() < 0) /* Check for console interrupts. */ type = 'z'; else if (ttchk() > 4 + bctu) /* Check for return traffic */ type = rpack(); debug(F000,"input streaming type","",type); } #endif /* STREAMING */ debug(F111,"input send",(char *) rdatap,(int) type); while (type == 'e') { /* Handle echoes */ debug(F000,"echo discarded","",type); type = rpack(); } #ifndef OLDCHKINT if (type == 'z') { epktrcvd = 1; errpkt((CHAR *)"User cancelled."); type = 'E'; break; } #endif /* OLDCHKINT */ if (type < -1) { xxscreen(SCR_PT,'q',0L, ((char *)((type == -2) ? "Interrupted" : "Connection lost")) ); if (type != -2) dologend(); return('q'); /* Ctrl-C or connection lost */ } if (type == -1) { #ifdef COMMENT char foo[256]; ckmakxmsg(foo,256, "Receive window full (error 18): wslots=", ckitoa(wslots), " winlo=",ckitoa(winlo)," pktnum=", ckitoa(pktnum), NULL,NULL,NULL,NULL,NULL,NULL); errpkt((CHAR *)foo); debug(F100,foo,"",0); #else errpkt((CHAR *)"Receive window full"); /* was "internal */ debug(F101," wslots","",wslots); /* error 18" */ debug(F101," winlo","",winlo); debug(F101," pktnum","",pktnum); #endif /* COMMENT */ dumprbuf(); type = 'E'; break; } dumprbuf(); /* Debugging */ #ifdef OLDCHKINT if (chkint() < 0) { /* Check for console interrupts. */ errpkt((CHAR *)"User cancelled."); return(type = 'E'); } #endif /* OLDCHKINT */ /* Got a packet */ #ifdef STREAMING if (streaming) { /* Streaming */ if (type == 'Q' || type == 'T') { /* Errors are fatal. */ crunched++; /* For statistics */ errpkt((CHAR *)"Transmission error on reliable link."); type = 'E'; } } #endif /* STREAMING */ if (type == 'E') { debug(F101,"input send got E, nakstate","",nakstate); break; /* Error packet */ } if (type == 'Q') { /* Crunched packet */ crunched++; /* For statistics */ numerrs++; /* For packet resizing */ x = resend(winlo); /* Resend window-low */ if (x < 0) { type = 'E'; errpkt((CHAR *)"Too many retries"); break; } continue; } if (type == 'T') { /* Timeout waiting for ACKs. */ timeouts++; /* Count it */ numerrs++; /* Count an error too */ debug(F101,"input send state timeout, winlo","",winlo); /* Retransmit the oldest un-ACK'd packet. */ debug(F101,"input send resending winlo","",winlo); if (resend(winlo) < 0) { /* Check retries */ debug(F101,"input send too many resends","",maxtry); errpkt((CHAR *)"Too many retries"); return(type = 'E'); } #ifdef NEWDPL /* Reduce prevailing packet length */ x = sseqtbl[winlo]; /* Get length of packet we want ACKd */ if (x > -1) { /* Only if we have a valid index */ if (s_pkt[x].pk_typ == 'D') { /* only for D packets */ spsiz = (s_pkt[x].pk_len + 8) >> 1; /* halve it */ if (spsiz < 20) spsiz = 20; /* within reason */ debug(F101,"input T cut packet length","",spsiz); } } #endif /* NEWDPL */ continue; } } /* Got an actual normal packet */ nak2ack = 0; /* Unset this flag. */ y = chkwin(rsn,winlo,wslots); /* Is it in the window? */ debug(F101,"input send rsn","",rsn); debug(F101,"input send winlo","",winlo); debug(F101,"input send chkwin","",y); if (type == 'Y') { /* Got an ACK */ if (y == 0) { /* In current window */ if (spackets < 4) /* Error counter doesn't count */ numerrs = 0; /* until data phase. */ sacktbl[rsn]++; /* Mark the packet as ACK'd */ x = sseqtbl[rsn]; /* Get ACK'd packet's buffer index */ debug(F101,"bestlen ack x","",x); #ifdef NEWDPL if (x > -1) { acktype = s_pkt[x].pk_typ; /* Get type */ debug(F000,"bestlen ack type","",acktype); if (acktype == 'D') { /* Adjust data packet length */ if (spsiz > bestlen) { bestlen = spsiz; debug(F101,"bestlen B","",bestlen); } #ifdef DEBUG if (deblog) { debug(F101,"bestlen retry","",s_pkt[x].pk_rtr); debug(F101,"bestlen len","",s_pkt[x].pk_len); debug(F101,"bestlen spackets","",spackets); } #endif /* DEBUG */ /* Set new best length */ if (s_pkt[x].pk_rtr == 0 && s_pkt[x].pk_len + 8 > bestlen) { bestlen = s_pkt[x].pk_len + 8; if (bestlen > spmax) bestlen = spmax; debug(F101,"bestlen A","",bestlen); } #ifdef DEBUG if (deblog) { debug(F101,"bestlen wslots","",wslots); debug(F101,"bestlen maxsend","",maxsend); } #endif /* DEBUG */ /* Slow start */ if (slostart && (maxsend <= spmax) && (rpackets < 11) && (numerrs == 0)) { spsiz = spsiz << 1; debug(F101,"bestlen spsiz A","",spsiz); /* Creep up to best length */ } else if ((spackets > 5) && (spsiz < bestlen - 8)) { spsiz += (bestlen - spsiz) / 3; debug(F101,"bestlen spsiz B","",spsiz); /* Push the envelope */ } else if ((spackets % (wslots + 1) == 0) && (spackets > 6) && (bestlen < spmax - 8) && (spsiz < spmax)) { spsiz += (spmax - bestlen) / 3; debug(F101,"bestlen spsiz C","",spsiz); } /* But not too far */ if (spsiz > spmax) { spsiz = spmax; debug(F101,"bestlen spsiz D","",spsiz); } } } #endif /* NEWDPL */ #ifdef CK_TIMERS if (rttflg && timint) /* If doing dynamic timers */ getrtt(nakstate, rsn); /* call routine to set it. */ else /* JHD 20100208 */ rcvtimo = timint; /* JHD 20100208 */ #endif /* CK_TIMERS */ /* NOTE: The following statement frees the buffer of the ACK we just got. But the upper layers still need the data, like if it's the ACK to an I, S, F, D, Z, or just about any kind of packet. So for now, freerbuf() deallocates the buffer, but does not erase the data or destroy the pointer to it. There's no other single place where these receive buffers can be correctly freed (?) ... */ freerpkt(rsn); /* Free the ACK's buffer */ freesbuf(rsn); /* *** Free the sent packet's buffer */ if (rsn == winlo) { /* Got the one we want */ sacktbl[winlo] = 0; winlo = (winlo + 1) % 64; debug(F101,"input send rotated send window","",winlo); break; /* Return the ACK */ } else { debug(F101,"input send mark pkt","",rsn); continue; /* Otherwise go read another packet */ } } else if (y == 1 && wslots < 2) { /* (190) ACK for previous */ numerrs++; /* == NAK for current, count error */ debug(F101,"input send ACK for previous","",rsn); freerpkt(rsn); /* Free NAK's buffer */ x = resend(winlo); /* Resend current packet */ if (x < 0) { type = 'E'; errpkt((CHAR *)"Too many retries"); break; } else continue; /* Resend ok, go read another packet */ } else { /* Other cases, just ignore */ debug(F101,"input send ACK out of window","",rsn); freerpkt(rsn); continue; } } if (type == 'N') { /* NAK */ numerrs++; /* Count an error */ #ifdef STREAMING if (streaming) { /* Streaming */ errpkt((CHAR *)"NAK received on reliable link."); type = 'E'; break; } #endif /* STREAMING */ debug(F101,"input send NAK","",rsn); #ifdef NEWDPL /* Reduce prevailing packet length */ x = sseqtbl[rsn]; /* Length of packet that was NAK'd */ if (x > -1) { /* If it's a Data packet we've sent */ if (s_pkt[x].pk_typ == 'D') { spsiz = (s_pkt[x].pk_len + 8) >> 1; /* Halve length */ #ifdef COMMENT /* This might be a good idea -- haven't tried it ... */ if (bestlen > 0 && spsiz > bestlen) spsiz = bestlen; #endif /* COMMENT */ if (spsiz < 20) spsiz = 20; debug(F101,"input N cut packet length","",spsiz); } } #endif /* NEWDPL */ freerpkt(rsn); /* Free buffer where NAK lies. */ if (y == 0) { /* In current window */ debug(F100," in window","",0); k = sseqtbl[rsn]; /* Get pointer to NAK'd packet. */ if (k < 0 || (k > -1 && s_pkt[k].pk_typ == ' ')) { x = resend(winlo); /* Packet we haven't sent yet. */ } else { x = resend(rsn); /* Resend requested packet. */ } if (x < 0) { /* Resend error is fatal. */ type = 'E'; errpkt((CHAR *)"Too many retries"); break; } else continue; /* Resend ok, go read another packet */ } else if ((rsn == (pktnum + 1) % 64)) { /* NAK for next pkt */ if (wslots > 1) { debug( F101,"NAK for next packet, windowing","",rsn); x = resend(winlo); /* Resend window-low */ if (x < 0) { type = 'E'; errpkt((CHAR *)"Too many retries"); break; } continue; /* Go back and read another pkt */ } debug(F101,"NAK for next packet, no windowing","",rsn); x = (rsn == 0) ? 63 : rsn - 1; if (x == 0 && (sndtyp == 'S' || sndtyp == 'I')) { resend(0); /* ACK for S or I packet missing */ continue; /* so resend the S or I */ } rsn = x; /* Else, treat NAK(n+1) as ACK(n) */ nak2ack = 1; /* Go back and process the ACK */ continue; } else if (y > 0) { /* NAK for pkt we can't resend */ debug(F101," NAK out of window","",rsn); /* bad... */ type = 'E'; errpkt((CHAR *)"NAK out of window"); break; } else continue; /* Ignore other NAKs */ } /* End of file-sender NAK handler */ if (rsn == winlo) { /* Not ACK, NAK, timeout, etc. */ debug(F000,"input send unexpected type","",type); break; } } /* End of file-sender section */ } /* End of input() while() loop */ /* When the window size is 1 and we have the packet we want, there can not possibly be anything waiting for us on the connection that is useful to us. However, there might be redundant copies of a packet we already got, which would cause needless cycles of repeated packets. Therefore we flush the communications input buffer now to try to get rid of undesired and unneeded packets that we have not read yet. Actually, the first sentence above is not entirely true: there could be an Error packet waiting to be read. Flushing an E packet is bad because it will not be resent, and we'll go into a cycle of timing out and retransmitting up to the retry limit. - fdc 2007/03/02 */ if (wslotn == 1 /* (not wslots!) */ #ifdef STREAMING && !streaming /* But not when streaming */ #endif /* STREAMING */ ) { debug(F100,"input about to flush","",0); ttflui(); /* Got what we want, clear input buffer. */ } #ifndef NEWDPL if (!nakstate) /* When sending */ rcalcpsz(); /* recalculate size every packet */ #endif /* NEWDPL */ if (type == 'E') xitsta |= (what ? what : 1); /* Remember what failed. */ debug(F101,"input winlo","",winlo); debug(F101,"input rsn","",rsn); debug(F000,"input returning type","",type); return(rcvtyp = type); /* Success, return packet type. */ } #ifdef PARSENSE /* P A R C H K -- Check if Kermit packet has parity */ /* Call with s = pointer to packet, start = packet start character, n = length. Returns 0 if packet has no parity, -1 on error, or, if packet has parity: 'e' for even, 'o' for odd, 'm' for mark. Space parity cannot be sensed. So a return value of 0 really means either space or none. Returns -2 if parity has already been checked during this protocol operation. */ int #ifdef CK_ANSIC parchk(CHAR *s, CHAR start, int n) #else parchk(s,start,n) CHAR *s, start; int n; #endif /* CK_ANSIC */ /* parchk */ { CHAR s0, s1, s2, s3; debug(F101,"parchk n","",n); debug(F101,"parchk start","",start); s0 = s[0] & 0x7f; /* Mark field (usually Ctrl-A) */ if (s0 != start || n < 5) return(-1); /* Not a valid packet */ /* Look at packet control fields, which never have 8th bit set */ /* First check for no parity, most common case. */ if (((s[0] | s[1] | s[2] | s[3]) & 0x80) == 0) return(0); /* No parity or space parity */ /* Check for mark parity */ if (((s[0] & s[1] & s[2] & s[3]) & 0x80) == 0x80) return('m'); /* Mark parity */ /* Packet has some kind of parity */ /* Make 7-bit copies of control fields */ s1 = s[1] & 0x7f; /* LEN */ s2 = s[2] & 0x7f; /* SEQ */ s3 = s[3] & 0x7f; /* TYPE */ /* Check for even parity */ if ((s[0] == p_tbl[s0]) && (s[1] == p_tbl[s1]) && (s[2] == p_tbl[s2]) && (s[3] == p_tbl[s3])) return('e'); /* Check for odd parity */ if ((s[0] != p_tbl[s0]) && (s[1] != p_tbl[s1]) && (s[2] != p_tbl[s2]) && (s[3] != p_tbl[s3])) return('o'); /* Otherwise it's probably line noise. Let checksum calculation catch it. */ return(-1); } #endif /* PARSENSE */ /* Check to make sure timeout intervals are long enough to allow maximum length packets to get through before the timer goes off. If not, the timeout interval is adjusted upwards. This routine is called at the beginning of a transaction, before we know anything about the delay characteristics of the line. It works only for serial communication devices; it trusts the speed reported by the operating system. Call with a timout interval. Returns it, adjusted if necessary. */ int chktimo(timo,flag) int timo, flag; { long cps, z; int x, y; #ifdef STREAMING debug(F101,"chktimo streaming","",streaming); if (streaming) return(0); #endif /* STREAMING */ debug(F101,"chktimo timo","",timo); /* Timeout before adjustment */ debug(F101,"chktimo flag","",flag); if (flag) /* Don't change timeout if user */ return(timo); /* gave SET SEND TIMEOUT command. */ debug(F101,"chktimo spmax","",spmax); debug(F101,"chktimo urpsiz","",urpsiz); if (!network) { /* On serial connections... */ speed = ttgspd(); /* Get current speed. */ if (speed > 0L) { cps = speed / 10L; /* Convert to chars per second */ if (cps > 0L) { long plen; /* Maximum of send and rcv pkt size */ z = cps * (long) timo; /* Chars per timeout interval */ z -= z / 10L; /* Less 10 percent */ plen = spmax; if (urpsiz > spmax) plen = urpsiz; debug(F101,"chktimo plen","",plen); if (z < plen) { /* Compare with packet size */ x = (int) ((long) plen / cps); /* Adjust if necessary */ y = x / 10; /* Add 10 percent for safety */ if (y < 2) y = 2; /* Or 2 seconds, whichever is more */ x += y; if (x > timo) /* If this is greater than current */ timo = x; /* timeout, change the timeout */ debug(F101,"chktimo new timo","",timo); } } } } return(timo); } /* S P A C K -- Construct and send a packet */ /* spack() sends a packet of the given type, sequence number n, with len data characters pointed to by d, in either a regular or extended- length packet, depending on len. Returns the number of bytes actually sent, or else -1 upon failure. Uses global npad, padch, mystch, bctu, data. Leaves packet fully built and null-terminated for later retransmission by resend(). Updates global sndpktl (send-packet length). NOTE: The global pointer "data" is assumed to point into the 7th position of a character array (presumably in packet buffer for the current packet). It was used by getpkt() to build the packet data field. spack() fills in the header to the left of the data pointer (the data pointer is defined in getsbuf() in ckcfn3.c). If the address "d" is the same as "data", then the packet's data field has been built "in place" and need not be copied. */ int #ifdef CK_ANSIC spack(char pkttyp, int n, int len, CHAR *d) #else spack(pkttyp,n,len,d) char pkttyp; int n, len; CHAR *d; #endif /* CK_ANSIC */ /* spack */ { register int i; int ix, j, k, x, lp, longpkt, copy, loglen; #ifdef GFTIMER CKFLOAT t1 = 0.0, t2 = 0.0; #endif /* GFTIMER */ register CHAR *cp, *mydata; unsigned crc; copy = (d != data); /* Flag whether data must be copied */ #ifdef DEBUG if (deblog) { /* Save lots of function calls! */ debug(F101,"spack n","",n); #ifdef COMMENT if (pkttyp != 'D') { /* Data packets would be too long */ debug(F111,"spack data",data,data); debug(F111,"spack d",d,d); } #endif /* COMMENT */ debug(F101,"spack len","",len); debug(F101,"spack copy","",copy); } #endif /* DEBUG */ longpkt = (len + bctl + 2) > 94; /* Decide whether it's a long packet */ mydata = data - 7 + (longpkt ? 0 : 3); /* Starting position of header */ k = sseqtbl[n]; /* Packet structure info for pkt n */ #ifdef COMMENT #ifdef DEBUG if (deblog) { /* Save 2 more function calls... */ debug(F101,"spack mydata","",mydata); debug(F101,"spack sseqtbl[n]","",k); if (k < 0) { #ifdef STREAMING if (!streaming) #endif /* STREAMING */ debug(F101,"spack sending packet out of window","",n); } } #endif /* DEBUG */ #endif /* COMMENT */ if (k > -1) { s_pkt[k].pk_adr = mydata; /* Remember address of packet. */ s_pkt[k].pk_seq = n; /* Record sequence number */ s_pkt[k].pk_typ = pkttyp; /* Record packet type */ } spktl = 0; /* Initialize length of this packet */ i = 0; /* and position in packet. */ /* Now fill the packet */ mydata[i++] = mystch; /* MARK */ lp = i++; /* Position of LEN, fill in later */ mydata[i++] = tochar(n); /* SEQ field */ mydata[i++] = pkttyp; /* TYPE field */ j = len + bctl; /* Length of data + block check */ if (longpkt) { /* Long packet? */ int x; /* Yes, work around SCO Xenix/286 */ #ifdef CKTUNING unsigned int chk; #endif /* CKTUNING */ x = j / 95; /* compiler bug... */ mydata[lp] = tochar(0); /* Set LEN to zero */ mydata[i++] = tochar(x); /* Extended length, high byte */ mydata[i++] = tochar(j % 95); /* Extended length, low byte */ #ifdef CKTUNING /* Header checksum - skip the function calls and loops */ chk = (unsigned) mydata[lp] + (unsigned) mydata[lp+1] + (unsigned) mydata[lp+2] + (unsigned) mydata[lp+3] + (unsigned) mydata[lp+4] ; mydata[i++] = tochar((CHAR) ((((chk & 0300) >> 6) + chk) & 077)); #else mydata[i] = '\0'; /* Terminate for header checksum */ mydata[i++] = tochar(chk1(mydata+lp,5)); #endif /* CKTUNING */ } else mydata[lp] = tochar(j+2); /* Normal LEN */ /* When sending a file, the data is already in the right place. If it weren't, it might make sense to optimize this section by using memcpy or bcopy (neither of which are portable), but only if our packets were rather long. When receiving, we're only sending ACKs so it doesn't matter. So count the following loop as a sleeping dog. */ if (copy) /* Data field built in place? */ for ( ; len--; i++) mydata[i] = *d++; /* No, must copy. */ else /* Otherwise, */ i += len; /* Just skip past data field. */ mydata[i] = '\0'; /* Null-terminate for checksum calc. */ switch (bctu) { /* Block check */ case 1: /* 1 = 6-bit chksum */ ix = i - lp; /* Avoid "order of operation" error */ mydata[i++] = tochar(chk1(mydata+lp,ix)); break; case 2: /* 2 = 12-bit chksum */ j = chk2(mydata+lp,i-lp); mydata[i++] = (unsigned)tochar((j >> 6) & 077); mydata[i++] = (unsigned)tochar(j & 077); break; case 3: /* 3 = 16-bit CRC */ crc = chk3(mydata+lp,i-lp); mydata[i++] = (unsigned)tochar(((crc & 0170000)) >> 12); mydata[i++] = (unsigned)tochar((crc >> 6) & 077); mydata[i++] = (unsigned)tochar(crc & 077); break; case 4: /* 2 = 12-bit chksum, blank-free */ j = chk2(mydata+lp,i-lp); mydata[i++] = (unsigned)(tochar((unsigned)(((j >> 6) & 077) + 1))); mydata[i++] = (unsigned)(tochar((unsigned)((j & 077) + 1))); break; } loglen = i; mydata[i++] = seol; /* End of line (packet terminator) */ #ifdef TCPSOCKET /* If TELNET connection and packet terminator is carriage return, we must stuff either LF or NUL, according to SET TELNET NEWLINE-MODE (tn_nlm), to meet the TELNET NVT specification, unless user said RAW. If NEWLINE-MODE is set to LF instead of CR, we still send CR-NUL on a NVT connection and CR on a binary connection. */ if ( #ifdef STREAMING !dontsend && #endif /* STREAMING */ ((network && ttnproto == NP_TELNET) || (!local && sstelnet)) && seol == CR) { switch (TELOPT_ME(TELOPT_BINARY) ? tn_b_nlm : tn_nlm) { case TNL_CR: /* NVT or BINARY */ break; case TNL_CRNUL: mydata[i++] = NUL; break; case TNL_CRLF: mydata[i++] = LF; break; } } #endif /* TCPSOCKET */ mydata[i] = '\0'; /* Terminate string */ if ( #ifdef STREAMING !dontsend && #endif /* STREAMING */ pktlog ) /* Save a function call! */ logpkt('s',n,mydata,loglen); /* Log the packet */ /* (PWP) add the parity quickly at the end */ if (parity) { switch (parity) { case 'e': /* Even */ for (cp = &mydata[i-1]; cp >= mydata; cp--) *cp = p_tbl[*cp]; break; case 'm': /* Mark */ for (cp = &mydata[i-1]; cp >= mydata; cp--) *cp |= 128; break; case 'o': /* Odd */ for (cp = &mydata[i-1]; cp >= mydata; cp--) *cp = p_tbl[*cp] ^ 128; break; case 's': /* Space */ for (cp = &mydata[i-1]; cp >= mydata; cp--) *cp &= 127; break; } } if (pktpaus) msleep(pktpaus); /* Pause if requested */ x = 0; if (npad) { #ifdef STREAMING if (dontsend) x = 0; else #endif /* STREAMING */ x = ttol(padbuf,npad); /* Send any padding */ } if (x > -1) { #ifdef CK_TIMERS if (timint > 0) { if (pkttyp == 'N') srttbl[n > 0 ? n-1 : 63] = gtimer(); else srttbl[n] = gtimer(); } #endif /* CK_TIMERS */ spktl = i; /* Remember packet length */ if (k > -1) s_pkt[k].pk_len = spktl; /* also in packet info structure */ #ifdef DEBUG #ifdef GFTIMER /* This code shows (in the debug log) how long it takes write() to execute. Sometimes on a congested TCP connection, it can surprise you -- 90 seconds or more... */ if ( #ifdef STREAMING !dontsend && #endif /* STREAMING */ deblog ) t1 = gftimer(); #endif /* GFTIMER */ #endif /* DEBUG */ #ifdef STREAMING if (dontsend) { debug(F000,"STREAMING spack skipping","",pkttyp); x = 0; } else #endif /* STREAMING */ x = ttol(mydata,spktl); /* Send the packet */ } #ifdef STREAMING if (!dontsend) { #endif /* STREAMING */ debug(F101,"spack spktl","",spktl); debug(F101,"spack ttol returns","",x); if (x < 0) { /* Failed. */ if (local && x < -1) { xxscreen(SCR_ST,ST_ERR,0L,"FAILED: Connection lost"); /* We can't send an E packet because the connection is lost. */ epktsent = 1; /* So pretend we sent one. */ fatalio = 1; /* Remember we got a fatal i/o error */ dologend(); ckstrncpy((char *)epktmsg,"Connection lost",PKTMSGLEN); } return(x); } if (spktl > maxsend) /* Keep track of longest packet sent */ maxsend = spktl; #ifdef DEBUG #ifdef GFTIMER if (deblog) { /* Log elapsed time for write() */ t2 = gftimer(); debug(F101,"spack ttol msec","",(long)((t2-t1)*1000.0)); } #endif /* GFTIMER */ #endif /* DEBUG */ #ifdef STREAMING } #endif /* STREAMING */ sndtyp = pkttyp; /* Remember packet type for echos */ #ifdef STREAMING if (!dontsend) { /* If really sent, */ spackets++; /* count it. */ flco += spktl; /* Count the characters */ tlco += spktl; /* for statistics... */ #ifdef DEBUG if (deblog) { /* Save two function calls! */ dumpsbuf(); /* Dump send buffers to debug log */ debug(F111,"spack calling screen, mydata=",mydata,n); } #endif /* DEBUG */ } #endif /* STREAMING */ if (local) { int x = 0; if (fdispla != XYFD_N) x = 1; if ((fdispla == XYFD_B) && (pkttyp == 'D' || pkttyp == 'Y')) x = 0; if (x) xxscreen(SCR_PT,pkttyp,(long)n,(char *)mydata); /* Update screen */ } return(spktl); /* Return length */ } /* C H K 1 -- Compute a type-1 Kermit 6-bit checksum. */ int chk1(pkt,len) register CHAR *pkt; register int len; { register unsigned int chk; #ifdef CKTUNING #ifdef COMMENT register unsigned int m; /* Avoid function call */ m = (parity) ? 0177 : 0377; for (chk = 0; len-- > 0; pkt++) chk += *pkt & m; #else chk = 0; while (len-- > 0) chk += (unsigned) *pkt++; #endif /* COMMENT */ #else chk = chk2(pkt,len); #endif /* CKTUNING */ chk = (((chk & 0300) >> 6) + chk) & 077; debug(F101,"chk1","",chk); return((int) chk); } /* C H K 2 -- Compute the numeric sum of all the bytes in the packet. */ unsigned int chk2(pkt,len) register CHAR *pkt; register int len; { register long chk; #ifdef COMMENT register unsigned int m; m = (parity) ? 0177 : 0377; for (chk = 0; len-- > 0; pkt++) chk += *pkt & m; #else /* Parity has already been stripped */ chk = 0L; while (len-- > 0) chk += (unsigned) *pkt++; #endif /* COMMENT */ debug(F101,"chk2","",(unsigned int) (chk & 07777)); return((unsigned int) (chk & 07777)); } /* C H K 3 -- Compute a type-3 Kermit block check. */ /* Calculate the 16-bit CRC-CCITT of a null-terminated string using a lookup table. Assumes the argument string contains no embedded nulls. */ #ifdef COMMENT unsigned int chk3(pkt,parity,len) register CHAR *pkt; int parity; register int len; { register long c, crc; register unsigned int m; m = (parity) ? 0177 : 0377; for (crc = 0; len-- > 0; pkt++) { c = crc ^ (long)(*pkt & m); crc = (crc >> 8) ^ (crcta[(c & 0xF0) >> 4] ^ crctb[c & 0x0F]); } return((unsigned int) (crc & 0xFFFF)); } #else unsigned int chk3(pkt,len) register CHAR *pkt; register int len; { register long c, crc; for (crc = 0; len-- > 0; pkt++) { c = crc ^ (long)(*pkt); crc = (crc >> 8) ^ (crcta[(c & 0xF0) >> 4] ^ crctb[c & 0x0F]); } debug(F101,"chk3","",(unsigned int) (crc & 0xFFFF)); return((unsigned int) (crc & 0xFFFF)); } #endif /* COMMENT */ /* N X T P K T -- Next Packet */ /* Get packet number of next packet to send and allocate a buffer for it. Returns: 0 on success, with global pktnum set to the packet number; -1 on failure to allocate buffer (fatal); -2 if resulting packet number is outside the current window. */ int nxtpkt() { /* Called by file sender */ int j, n, x; debug(F101,"nxtpkt pktnum","",pktnum); debug(F101,"nxtpkt winlo ","",winlo); n = (pktnum + 1) % 64; /* Increment packet number mod 64 */ debug(F101,"nxtpkt n","",n); #ifdef STREAMING if (!streaming) { x = chkwin(n,winlo,wslots); /* Don't exceed window boundary */ debug(F101,"nxtpkt chkwin","",x); if (x) return(-2); j = getsbuf(n); /* Get a buffer for packet n */ if (j < 0) { debug(F101,"nxtpkt getsbuf failure","",j); return(-1); } } #endif /* STREAMING */ pktnum = n; return(0); } /* Functions for sending ACKs and NAKs */ /* Note, we should only ACK the packet at window-low (winlo) */ /* However, if an old packet arrives again (e.g. because the ACK we sent */ /* earlier was lost), we ACK it again. */ int ack() { /* Acknowledge the current packet. */ return(ackns(winlo,(CHAR *)"")); } #ifdef STREAMING int fastack() { /* Acknowledge packet n */ int j, k, n, x; n = winlo; k = rseqtbl[n]; /* First find received packet n. */ debug(F101,"STREAMING fastack k","",k); freesbuf(n); /* Free current send-buffer, if any */ if ((j = getsbuf(n)) < 0) { /* This can happen if we have to re-ACK an old packet that has */ /* already left the window. It does no harm. */ debug(F101,"STREAMING fastack can't getsbuf","",n); } dontsend = 1; x = spack('Y',n,0,(CHAR *)""); /* Now send it (but not really) */ dontsend = 0; if (x < 0) return(x); debug(F101,"STREAMING fastack x","",x); if (k > -1) freerbuf(k); /* don't need it any more */ if (j > -1) freesbuf(j); /* and don't need to keep ACK either */ winlo = (winlo + 1) % 64; return(0); } #endif /* STREAMING */ int ackns(n,s) int n; CHAR *s; { /* Acknowledge packet n */ int j, k, x; debug(F111,"ackns",s,n); k = rseqtbl[n]; /* First find received packet n. */ debug(F101,"ackns k","",k); freesbuf(n); /* Free current send-buffer, if any */ if ((j = getsbuf(n)) < 0) { /* This can happen if we have to re-ACK an old packet that has */ /* already left the window. It does no harm. */ debug(F101,"ackns can't getsbuf","",n); } x = spack('Y',n,(int)strlen((char *)s),s); /* Now send it. */ if (x < 0) return(x); debug(F101,"ackns winlo","",winlo); debug(F101,"ackns n","",n); if (n == winlo) { /* If we're acking winlo */ if (k > -1) freerbuf(k); /* don't need it any more */ if (j > -1) freesbuf(j); /* and don't need to keep ACK either */ winlo = (winlo + 1) % 64; } return(0); } int ackn(n) int n; { /* Send ACK for packet number n */ return(ackns(n,(CHAR *)"")); } int ack1(s) CHAR *s; { /* Send an ACK with data. */ if (!s) s = (CHAR *)""; debug(F110,"ack1",(char *)s,0); return(ackns(winlo,s)); } /* N A C K -- Send a Negative ACKnowledgment. */ /* Call with the packet number, n, to be NAK'd. Returns -1 if that packet has been NAK'd too many times, otherwise 0. Btw, it is not right to return 0 under error conditions. This is done because the -1 code is used for cancelling the file transfer. More work is needed here. */ int nack(n) int n; { int i, x; if (n < 0 || n > 63) { debug(F101,"nack bad pkt num","",n); return(0); } else debug(F101,"nack","",n); if ((i = sseqtbl[n]) < 0) { /* If necessary */ if (getsbuf(n) < 0) { /* get a buffer for this NAK */ debug(F101,"nack can't getsbuf","",n); return(0); } else i = sseqtbl[n]; /* New slot number */ } if (maxtry > 0 && s_pkt[i].pk_rtr++ > maxtry) /* How many? */ return(-1); /* Too many... */ /* Note, don't free this buffer. Eventually an ACK will come, and that */ /* will set it free. If not, well, it's back to ground zero anyway... */ x = spack('N',n,0,(CHAR *) ""); /* NAKs never have data. */ return(x); } #ifndef NEWDPL /* This routine no longer used */ /* * (PWP) recalculate the optimal packet length in the face of errors. * This is a modified version of the algorithm by John Chandler in Kermit/370, * see "Dynamic Packet Size Control", Kermit News, V2 #1, June 1988. * * This implementation minimizes the total overhead equation, which is * * Total chars = file_chars + (header_len * num_packs) * + (errors * (header_len + packet_len)) * * Differentiate with respect to number of chars, solve for packet_len, get: * * packet_len = sqrt (file_chars * header_len / errors) */ /* (FDC) New super-simple algorithm. If there was an error in the most recent packet exchange, cut the send-packet size in half, down to a minimum of 20. If there was no error, increase the size by 5/4, up to the maximum negotiated length. Seems to be much more responsive than previous algorithm, which took forever to recover the original packet length, and it also went crazy under certain conditions. Here's another idea for packet length resizing that keeps a history of the last n packets. Push a 1 into the left end of an n-bit shift register if the current packet is good, otherwise push a zero. The current n-bit value, w, of this register is a weighted sum of the noise hits for the last n packets, with the most recent weighing the most. The current packet length is some function of w and the negotiated packet length, like: (2^n - w) / (2^n) * (negotiated length) If the present resizing method causes problems, think about this one a little more. */ VOID rcalcpsz() { #ifdef COMMENT /* Old way */ register long x, q; if (numerrs == 0) return; /* bounds check just in case */ /* overhead on a data packet is npad+5+bctr, plus 3 if extended packet */ /* an ACK is 5+bctr */ /* first set x = per packet overhead */ if (wslots > 1) /* Sliding windows */ x = (long) (npad+5+bctr); /* packet only, don't count ack */ else /* Stop-n-wait */ x = (long) (npad+5+3+bctr+5+bctr); /* count packet and ack. */ /* then set x = packet length ** 2 */ x = x * ( ffc / (CK_OFF_T) numerrs); /* careful of overflow */ /* calculate the long integer sqrt(x) quickly */ q = 500; q = (q + x/q) >> 1; q = (q + x/q) >> 1; q = (q + x/q) >> 1; q = (q + x/q) >> 1; /* should converge in about 4 steps */ if ((q > 94) && (q < 130)) /* break-even point for long packets */ q = 94; if (q > spmax) q = spmax; /* maximum bounds */ if (q < 10) q = 10; /* minimum bounds */ spsiz = q; /* set new send packet size */ debug(F101,"rcalcpsiz","",q); #else /* New way */ debug(F101,"rcalcpsiz numerrs","",numerrs); debug(F101,"rcalcpsiz spsiz","",spsiz); if (spackets < 3) { numerrs = 0; return; } if (numerrs) spsiz = spsiz / 2; else spsiz = (spsiz / 4) * 5; if (spsiz < 20) spsiz = 20; if (spsiz > spmax) spsiz = spmax; debug(F101,"rcalcpsiz new spsiz","",spsiz); numerrs = 0; #endif /* COMMENT */ } #endif /* NEWDPL */ /* R E S E N D -- Retransmit packet n. */ /* Returns 0 or positive on success (the number of retries for packet n). On failure, returns a negative number, and an error message is placed in recpkt. */ int resend(n) int n; { /* Send packet n again. */ int j, k, x; #ifdef GFTIMER CKFLOAT t1 = 0.0, t2 = 0.0; #endif /* GFTIMER */ debug(F101,"resend seq","",n); k = chkwin(n,winlo,wslots); /* See if packet in current window */ j = -1; /* Assume it's lost */ if (k == 0) j = sseqtbl[n]; /* See if we still have a copy of it */ if (k != 0 || j < 0) { /* If not.... */ if (nakstate && k == 1) { /* Packet n is in the previous window and we are the file receiver. We already sent the ACK and deallocated its buffer so we can't just retransmit the ACK. Rather than give up, we try some tricks... */ if (n == 0 && spackets < 63 && myinit[0]) { /* ACK to Send-Init */ /* If the packet number is 0, and we're at the beginning of a protocol operation (spackets < 63), then we have to resend the ACK to an I or S packet, complete with parameters in the data field. So we take a chance and send a copy of the parameters in an ACK packet with block check type 1. (Or 3 if SET BLOCK 5.) */ if (bctf) { /* Force Type 3 on all packets? */ x = spack('Y',0,(int)strlen((char *)myinit),(CHAR *)myinit); if (x < 0) return(x); logpkt('#',n,(CHAR *)"",0); /* Log it */ } else { /* Regular Kermit protocol */ int bctlsav; /* Temporary storage */ int bctusav; bctlsav = bctl; /* Save current block check length */ bctusav = bctu; /* and type */ bctu = bctl = 1; /* Set block check to 1 */ x = spack('Y',0,(int)strlen((char *)myinit),(CHAR *)myinit); if (x < 0) return(x); logpkt('#',n,(CHAR *)"",0); /* Log it */ bctu = bctusav; /* Restore block check type */ bctl = bctlsav; /* and length */ } } else { /* Not the first packet */ /* It's not the first packet of the protocol operation. It's some other packet that we have already ACK'd and forgotten about. So we take a chance and send an empty ACK using the current block-check type. Usually this will work out OK (like when acking Data packets), and no great harm will be done if it was some other kind of packet (F, etc). If we are requesting an interruption of the file transfer, the flags are still set, so we'll catch up on the next packet. */ x = spack('Y',n,0,(CHAR *) ""); if (x < 0) return(x); } retrans++; xxscreen(SCR_PT,'%',(long)pktnum,"Retransmission"); return(0); } else { /* Packet number is not in current or previous window. We seem to hit this code occasionally at the beginning of a transaction, for apparently no good reason. Let's just log it for debugging, send nothing, and try to proceed with the protocol rather than killing it. */ debug(F101,"resend PKT NOT IN WINDOW","",n); debug(F101,"resend k","",k); return(0); } } /* OK, it's in the window and it's not lost. */ debug(F101,"resend pktinfo index","",k); if (maxtry > 0 && s_pkt[j].pk_rtr++ > maxtry) { /* Over retry limit */ xitsta |= what; return(-1); } debug(F101,"resend retry","",s_pkt[j].pk_rtr); /* OK so far */ dumpsbuf(); /* (debugging) */ if (s_pkt[j].pk_typ == ' ') { /* Incompletely formed packet */ if (nakstate) { /* (This shouldn't happen any more) */ nack(n); retrans++; xxscreen(SCR_PT,'%',(long)pktnum,"(resend)"); return(s_pkt[j].pk_rtr); } else { /* No packet to resend! */ #ifdef COMMENT /* This happened (once) while sending a file with 2 window slots and typing X to the sender to cancel the file. But since we're cancelling anyway, there's no need to give a scary message. */ sprintf((char *)epktmsg, "resend logic error: NPS, n=%d, j=%d.",n,j); return(-2); #else /* Just ignore it. */ return(0); #endif /* COMMENT */ } } #ifdef DEBUG #ifdef GFTIMER if (deblog) t1 = gftimer(); #endif /* GFTIMER */ #endif /* DEBUG */ /* Everything ok, send the packet */ #ifdef CK_TIMERS if (timint > 0) srttbl[n] = gtimer(); /* Update the timer */ #endif /* CK_TIMERS */ x = ttol(s_pkt[j].pk_adr,s_pkt[j].pk_len); #ifdef DEBUG #ifdef GFTIMER if (deblog) { t2 = gftimer(); debug(F101,"resend ttol msec","",(long)((t2-t1)*1000.0)); } #endif /* GFTIMER */ #endif /* DEBUG */ debug(F101,"resend ttol returns","",x); retrans++; /* Count a retransmission */ xxscreen(SCR_PT,'%',(long)pktnum,"(resend)"); /* Tell user about resend */ logpkt('S',n,s_pkt[j].pk_adr, s_pkt[j].pk_len); /* Log the resent packet */ return(s_pkt[j].pk_rtr); /* Return the number of retries. */ } /* E R R P K T -- Send an Error Packet */ int errpkt(reason) CHAR *reason; { /* ...containing the reason given */ extern int rtimo, state, justone; int x, y; czseen = 1; /* Also cancels batch */ state = 0; /* Reset protocol state */ debug(F110,"errpkt",reason,0); tlog(F110,"Protocol Error:",(char *)reason,0L); xxscreen(SCR_EM,0,0L,reason); encstr(reason); x = spack('E',pktnum,size,data); ckstrncpy((char *)epktmsg,(char *)reason,PKTMSGLEN); y = quiet; quiet = 1; epktsent = 1; /* Close files silently. */ clsif(); clsof(1); quiet = y; /* I just sent an E-packet. I'm in local mode, I was receiving a file, I'm not a server, and sliding windows are in use. Therefore, there are likely to be a bunch of packets already "in the pipe" on their way to me by the time the remote sender gets the E-packet. So the next time I CONNECT or try to start another protocol operation, I am likely to become terribly confused by torrents of incoming material. To prevent this, the following code soaks up packets from the connection until there is an error or timeout, without wasting too much time waiting. Exactly the same problem occurs when I am in remote mode or if I am in server mode with the justone flag set. In remote mode not only does the packet data potentially get echo'd back to the sender which is confusing to the user in CONNECT mode, but it also may result in the host performing bizarre actions such as suspending the process if ^Z is unprefixed, etc. Furthermore, thousands of packets bytes in the data stream prevent the client from being able to process Telnet Kermit Option negotiations properly. */ #ifdef STREAMING /* Because streaming sets the timeout to 0... */ if (streaming) { timint = rcvtimo = rtimo; streaming = 0; } #endif /* STREAMING */ if (what & W_RECV && (!server || (server && justone)) && (wslots > 1 #ifdef STREAMING || streaming #endif /* STREAMING */ )) { #ifdef GFTIMER CKFLOAT oldsec, sec = (CKFLOAT) 0.0; #else int oldsec, sec = 0; #endif /* GFTIMER */ debug(F101,"errpkt draining","",wslots); xxscreen(SCR_ST,ST_MSG,0l,"Draining incoming packets, wait..."); while (x > -1) { /* Don't bother if no connection */ oldsec = sec; #ifdef GFTIMER sec = gftimer(); if (oldsec != (CKFLOAT) 0.0) timint = rcvtimo = (int) (sec - oldsec + 0.5); #else sec = gtimer(); if (oldsec != 0) timint = rcvtimo = sec - oldsec + 1; #endif /* GFTIMER */ if (timint < 1) timint = rcvtimo = 1; msleep(50); /* Allow a bit of slop */ x = rpack(); /* Read a packet */ if (x == 'T' || x == 'z') /* Timed out means we're done */ break; xxscreen(SCR_PT,x,rsn,""); /* Let user know */ } xxscreen(SCR_ST,ST_MSG,0l,"Drain complete."); } if ((x = (what & W_KERMIT))) xitsta |= x; /* Remember what failed. */ success = 0; return(y); } /* scmd() -- Send a packet of the given type */ int #ifdef CK_ANSIC scmd(char t, CHAR *dat) #else scmd(t,dat) char t; CHAR *dat; #endif /* CK_ANSIC */ /* scmd */ { int x; extern char * srimsg; debug(F000,"scmd",dat,t); if (encstr(dat) < 0) { /* Encode the command string */ srimsg = "String too long"; return(-1); } x = spack(t,pktnum,size,data); debug(F101,"scmd spack","",x); return(x); } /* Compose and Send GET packet */ struct opktparm { /* O-Packet item list */ CHAR * opktitem; struct opktparm * opktnext; }; struct opktparm * opkthead = NULL; /* Linked list of O-packet fields */ int opktcnt = 0; /* O-Packet counter */ char * srimsg = NULL; /* GET-Packet error message */ /* S O P K T -- Send O-Packet */ /* Sends one O-Packet each time called, using first-fit method of filling the packet from linked list of parameters pointed to by opkthead. To be called repeatedly until list is empty or there is an error. Returns: -1 on failure. 0 on success and no more fields left to send. 1 on success but with more fields left to be sent. */ int sopkt() { int n = 0; /* Field number in this packet */ int rc = 0; /* Return code */ int len = 0; /* Data field length */ char c = NUL; struct opktparm * o = NULL; struct opktparm * t = NULL; struct opktparm * prev = NULL; CHAR * dsave = data; int x, ssave = spsiz; srimsg = NULL; /* Error message */ o = opkthead; /* Point to head of list */ if (!o) { /* Oops, no list... */ srimsg = "GET Packet Internal Error 1"; debug(F100,"sopkt NULL list","",0); return(-1); } while (o) { /* Go thru linked list... */ c = *(o->opktitem); /* Parameter code */ debug(F000,"sopkt",o->opktitem,c); x = encstr((CHAR *)o->opktitem); debug(F111,"sopkt encstr",dsave,x); if (x < 0) { /* Encode this item */ if (n == 0) { /* Failure, first field in packet */ debug(F100,"sopkt overflow","",0); spsiz = ssave; /* Restore these */ data = dsave; o = opkthead; /* Free linked list */ while (o) { if (o->opktitem) free(o->opktitem); t = o->opktnext; free((char *)o); o = t; } opkthead = NULL; srimsg = "GET Packet Too Long for Server"; return(-1); /* Fail */ } else { /* Not first field in packet */ debug(F110,"sopkt leftover",o->opktitem,0); prev = o; /* Make this one the new previous */ o = o->opktnext; /* Get next */ c = NUL; /* So we know we're not done */ *data = NUL; /* Erase any partial encoding */ continue; /* We can try this one again later */ } } n++; /* Encoding was successful */ debug(F111,"sopkt field",data,x); len += x; /* Total data field length */ data += x; /* Set up for next field... */ spsiz -= x; free(o->opktitem); /* Free item just encoded */ if (o == opkthead) { /* If head */ opkthead = o->opktnext; /* Move head to next */ free((char *)o); /* Free this list node */ o = opkthead; } else { /* If not head */ o = o->opktnext; /* Get next */ prev->opktnext = o; /* Link previous to next */ } if (c == '@') /* Loop exit */ break; if (!o && !opkthead) { /* Set up End Of Parameters Field */ o = (struct opktparm *)malloc(sizeof(struct opktparm)); if (o) { opkthead = o; if (!(o->opktitem = (CHAR *)malloc(3))) { free((char *)o); srimsg = "GET Packet Internal Error 8"; return(-1); } ckstrncpy((char *)(o->opktitem), "@ ", 3); debug(F111,"sopkt o->opktitem",o->opktitem, strlen((char *)(o->opktitem))); o->opktnext = NULL; } } } data = dsave; /* Restore globals */ spsiz = ssave; debug(F110,"sopkt data",data,0); debug(F101,"sopkt opktcnt","",opktcnt); if (opktcnt++ > 0) { if (nxtpkt() < 0) { /* Get next packet number and buffer */ srimsg = "GET Packet Internal Error 9"; return(-1); } } debug(F101,"sopkt pktnum","",pktnum); rc = spack((char)'O',pktnum,len,data); /* Send O-Packet */ debug(F101,"sopkt spack","",rc); if (rc < 0) /* Failed */ srimsg = "Send Packet Failure"; /* Set message */ else /* Succeeded */ rc = (c == '@') ? 0 : 1; /* 1 = come back for more, 0 = done */ debug(F101,"sopkt rc","",rc); return(rc); } /* S R I N I T -- Send GET packet */ /* Sends the appropriate GET-Class packet. Returns: -1 on error 0 if packet sent successfully and we can move on to the next state 1 if an O-packet was sent OK but more O packets still need to be sent. */ int srinit(reget, retrieve, opkt) int reget, retrieve, opkt; { int x = 0, left = 0; extern int oopts, omode; CHAR * p = NULL; #ifdef RECURSIVE extern int recursive; debug(F101,"srinit recursive","",recursive); #endif /* RECURSIVE */ debug(F101,"srinit reget","",reget); debug(F101,"srinit retrieve","",retrieve); debug(F101,"srinit opkt","",opkt); debug(F101,"srinit oopts","",oopts); debug(F101,"srinit omode","",omode); debug(F110,"srinit cmarg",cmarg,0); srimsg = NULL; opktcnt = 0; if (!cmarg) cmarg = ""; if (!*cmarg) { srimsg = "GET with no filename"; debug(F100,"srinit null cmarg","",0); return(-1); } if (opkt) { /* Extended GET is totally different */ char buf[16]; struct opktparm * o = NULL; struct opktparm * prev = NULL; buf[0] = NUL; /* Build O-Packet fields and send (perhaps first) O-Packet */ if (oopts > -1) { /* Write Option flags */ o = (struct opktparm *)malloc(sizeof(struct opktparm)); if (!o) { srimsg = "GET Packet Internal Error 2"; debug(F100,"srinit malloc fail O1","",0); return(-1); } sprintf(buf,"Ox%d",oopts); /* safe */ x = (int) strlen(buf+2); buf[1] = tochar(x); o->opktitem = (CHAR *)malloc(x + 3); if (!o->opktitem) { srimsg = "GET Packet Internal Error 3"; debug(F100,"srinit malloc fail O2","",0); return(-1); } ckstrncpy((char *)(o->opktitem),buf,x+3); o->opktnext = NULL; if (!opkthead) opkthead = o; prev = o; } if (omode > -1) { /* If Xfer Mode specified, write it */ o = (struct opktparm *)malloc(sizeof(struct opktparm)); if (!o) { srimsg = "GET Packet Internal Error 4"; debug(F100,"srinit malloc fail M1","",0); return(-1); } sprintf(buf,"Mx%d",omode); /* safe */ x = (int) strlen(buf+2); buf[1] = tochar(x); o->opktitem = (CHAR *)malloc(x + 3); if (!o->opktitem) { srimsg = "GET Packet Internal Error 5"; debug(F100,"srinit malloc fail O2","",0); return(-1); } ckstrncpy((char *)(o->opktitem),buf,x+3); o->opktnext = NULL; if (!opkthead) opkthead = o; else prev->opktnext = o; prev = o; } /* Same deal for oname and opath eventually but not needed now... */ x = strlen(cmarg); /* Now do filename */ if (x > spsiz - 4) { srimsg = "GET Packet Too Long for Server"; return(-1); } o = (struct opktparm *)malloc(sizeof(struct opktparm)); if (!o) { srimsg = "GET Packet Internal Error 6"; debug(F100,"srinit malloc fail F1","",0); return(-1); } left = x + 6; o->opktitem = (CHAR *)malloc(left + 1); if (!o->opktitem) { srimsg = "GET Packet Internal Error 7"; debug(F100,"srinit malloc fail F2","",0); return(-1); } p = o->opktitem; *p++ = 'F'; left--; if (x > 94) { /* Too long for normal length */ *p++ = SYN; /* Escape length with Ctrl-V */ *p++ = tochar(x / 95); *p++ = tochar(x % 95); left -= 3; } else { /* Normal encoding for 94 or less */ *p++ = tochar(x); left--; } ckstrncpy((char *)p,cmarg,left); /* Copy the filename */ o->opktnext = NULL; if (!opkthead) opkthead = o; else prev->opktnext = o; prev = o; /* End of Parameters */ prev->opktnext = NULL; /* End of list. */ return(sopkt()); } /* Not Extended GET */ if (encstr((CHAR *)cmarg) < 0) { /* Encode the filename. */ srimsg = "GET Packet Too Long for Server"; return(-1); } if (retrieve) { /* Send the packet. */ #ifdef RECURSIVE if (recursive) x = spack((char)'W',pktnum,size,data); /* GET /DELETE /RECURSIVE */ else #endif /* RECURSIVE */ x = spack((char)'H',pktnum,size,data); /* GET /DELETE */ } #ifdef RECURSIVE else if (recursive) x = spack((char)'V',pktnum,size,data); /* GET /RECURSIVE */ #endif /* RECURSIVE */ else x = spack((char)(reget ? 'J' : 'R'),pktnum,size,data); /* GET */ if (x < 0) srimsg = "Send Packet Failure"; return(x < 0 ? x : 0); } /* K S T A R T -- Checks for a Kermit packet while in terminal mode. */ /* (or command mode...) */ #ifdef CK_AUTODL int #ifdef CK_ANSIC kstart(CHAR ch) #else kstart(ch) CHAR ch; #endif /* CK_ANSIC */ /* kstart */ { static CHAR * p = NULL; #ifdef OS2 static CHAR * pk = NULL; #endif /* OS2 */ ch &= 0177; /* Strip 8th bit */ /* Because we're in cooked mode at the command prompt... */ if (ch == LF) { debug(F110,"kstart","ch == LF",0); if ((what == W_COMMAND || what == W_INIT || what == W_NOTHING)) { if (eol == CR) { ch = eol; debug(F110,"kstart","ch = CR",0); } } } #ifdef OS2 if (adl_kmode == ADL_STR) { if (!ch) return(0); if (!pk) pk = adl_kstr; if (ch == *pk) { pk++; if (*pk == '\0') { pk = adl_kstr; debug(F100, "kstart Kermit Start String","",0); return(PROTO_K + 1); } } else pk = adl_kstr; } #endif /* OS2 */ if (ch == stchr) { /* Start of packet */ kstartactive = 1; p = ksbuf; *p = ch; debug(F101,"kstart SOP","",ch); } else if (ch == eol) { /* End of packet */ kstartactive = 0; if (p) { debug(F101,"kstart EOL","",ch); p++; if (p - ksbuf < 94 ) { int rc = 0; *p++ = ch; *p = NUL; rc = chkspkt((char *)ksbuf); debug(F111,"kstart EOP chkspkt", ksbuf, rc); p = NULL; if (!rc) return(0); if (rc == 2) rc = -1; debug(F111,"kstart ksbuf",ksbuf,rc); return(rc); } else { debug(F110,"kstart","p - ksbuf >= 94",0); p = NULL; } } } else if (p) { if (ch < SP) kstartactive = 0; p++; if (p - ksbuf < 94) { *p = ch; } else { p = NULL; debug(F110,"kstart","p - ksbuf >= 94",0); } } return(0); } #ifdef CK_XYZ /* Z S T A R T -- Checks for a ZMODEM packet while in terminal mode. */ int #ifdef CK_ANSIC zstart(CHAR ch) #else zstart(ch) CHAR ch; #endif /* CK_ANSIC */ /* zstart */ { static CHAR * matchstr = (CHAR *) "\030B00"; /* "rz\r**\030B00000000000000\r\033J\021"; */ static CHAR * p = NULL; extern int inserver; if (inserver) return(0); if (!ch) return(0); if (!p) { #ifdef OS2 p = adl_zmode == ADL_PACK ? matchstr : adl_zstr; #else p = matchstr; #endif /* OS2 */ } if (ch == *p) { p++; if (*p == '\0') { #ifdef OS2 if (adl_zmode == ADL_PACK) { p = matchstr; debug(F100, "zstart Zmodem SOP","",0); } else { p = adl_zstr; debug(F100, "zstart Zmodem Start String","",0); } #else p = matchstr; debug(F100, "zstart Zmodem SOP","",0); #endif /* OS2 */ return(PROTO_Z + 1); } } else { #ifdef OS2 p = adl_zmode == ADL_PACK ? matchstr : adl_zstr; #else p = matchstr; #endif /* OS2 */ } return(0); } #endif /* CK_XYZ */ #ifndef NOICP #ifdef CK_APC /* A U T O D O W N */ #ifdef CK_ANSIC VOID autodown(int ch) #else VOID autodown(ch) int ch; #endif /* CK_ANSIC */ /* autodown */ { /* The Kermit and Zmodem Auto-download calls go here */ extern int justone; /* From protocol module */ extern int debses, protocol, apcactive, autodl, inautodl; #ifdef DCMDBUF extern char *apcbuf; #else extern char apcbuf[]; #endif /* DCMDBUF */ #ifdef OS2 extern int apclength, term_io; #endif /* OS2 */ int k = 0; if ((autodl || inautodl #ifdef IKS_OPTION || TELOPT_SB(TELOPT_KERMIT).kermit.me_start #endif /* IKS_OPTION */ ) && !debses) { #ifdef CK_XYZ #ifdef XYZ_INTERNAL extern int p_avail; #else int p_avail = 1; #endif /* XYZ_INTERNAL */ if (p_avail && zstart((CHAR) ch)) { debug(F100, "Zmodem download","",0); #ifdef OS2 #ifndef NOTERM apc_command(APC_LOCAL,"receive /protocol:zmodem"); #endif /* NOTERM */ #else /* OS2 */ ckstrncpy(apcbuf,"receive /protocol:zmodem",APCBUFLEN); apcactive = APC_LOCAL; #endif /* OS2 */ return; } #endif /* CK_XYZ */ /* First try... */ k = kstart((CHAR) ch); if ( #ifdef NOSERVER k > 0 #else /* NOSERVER */ k #endif /* NOSERVER */ ) { /* We saw a valid S or I packet */ if (k < 0) { /* Stuff RECEIVE into APC buffer */ justone = 1; switch (protocol) { #ifdef CK_XYZ case PROTO_G: ckstrncpy(apcbuf, "set proto kermit, server, set protocol g", APCBUFLEN ); break; case PROTO_X: ckstrncpy(apcbuf, "set proto kermit,server,set proto xmodem", APCBUFLEN ); break; case PROTO_XC: ckstrncpy(apcbuf, "set proto kermit,server,set proto xmodem-crc", APCBUFLEN ); break; case PROTO_Y: ckstrncpy(apcbuf, "set proto kermit,server, set protocol y", APCBUFLEN ); break; case PROTO_Z: ckstrncpy(apcbuf, "set proto kermit,server,set proto zmodem", APCBUFLEN ); break; #endif /* CK_XYZ */ case PROTO_K: ckstrncpy(apcbuf,"server",APCBUFLEN); break; } } else { justone = 0; ckstrncpy(apcbuf,"receive /protocol:kermit",APCBUFLEN); } #ifdef OS2 #ifndef NOTERM apc_command(APC_LOCAL,apcbuf); #endif /* NOTERM */ #else /* OS2 */ ckstrncpy(apcbuf,"receive /protocol:zmodem",APCBUFLEN); apcactive = APC_LOCAL; #endif /* OS2 */ return; } } } #endif /* CK_APC */ #endif /* NOICP */ /* C H K S P K T -- Check if buf contains a valid S or I packet */ int chkspkt(packet) char *packet; { int i; int buflen; int len = -1; CHAR chk; char type = 0; char *s = NULL; char *buf = NULL; char tmpbuf[100]; /* Longest S/I packet is about 30 */ if (!packet) return(0); buflen = ckstrncpy(tmpbuf,packet,100); /* Make a pokeable copy */ if (buflen < 5) return(0); /* Too short */ if (buflen > 100) return(0); /* Too long to be an S or I packet */ s = buf = tmpbuf; /* Point to beginning of copy */ if (*s++ != stchr) return(0); /* SOH */ len = xunchar(*s++); /* Length */ if (len < 0) return(0); if (*s++ != SP) return(0); /* Sequence number */ type = *s++; /* Type */ if (type != 'S' && type != 'I') return(0); if (buflen < len + 2) return(0); s += (len - 3); /* Position of checksum */ chk = (CHAR) (*s); /* Checksum */ *s = NUL; /* Temporarily null-terminate data field */ if (xunchar(chk) != chk1((CHAR *)(buf+1),buflen-2)) { /* Check it */ /* In C-Kermit 9.0 and later, an S or I packet can have a Type 3 Block check ("help set block-check" for details). */ unsigned crc; /* Failed... Try Type 3 block check */ *s = chk; /* Replace last byte */ s -= 2; /* Back up two bytes */ crc = (xunchar(s[0]) << 12) /* Convert 3 bytes to numeric CRC */ | (xunchar(s[1]) << 6) | (xunchar(s[2])); chk = (CHAR)(*s); /* Copy 1st byte of 3-byte CRC */ *s = NUL; /* Null-terminate data field */ if (crc != chk3((CHAR *)(buf+1),strlen(buf+1))) return(0); } return(type == 'S' ? 1 : 2); } #endif /* CK_AUTODL */ /* R P A C K -- Read a Packet */ /* rpack reads a packet and returns the packet type, or else Q if the packet was invalid, or T if a timeout occurred. Upon successful return, sets the values of global rsn (received sequence number), rln (received data length), and rdatap (pointer to null-terminated data field), and returns the packet type. NOTE: This is an inner-loop function so must be efficient. Protect function calls by if-tests where possible, e.g. "if (pktlog) logpkt(...);". */ int rpack() { register int i, j, x, lp; /* Local variables */ #ifdef CKTUNING unsigned int chk; #endif /* CKTUNING */ int k, type, chklen; unsigned crc; CHAR pbc[5]; /* Packet block check */ CHAR *sohp; /* Pointer to SOH */ CHAR e; /* Packet end character */ #ifdef GFTIMER CKFLOAT t1 = 0.0, t2 = 0.0; #endif /* GFTIMER */ debug(F101,"rpack pktnum","",pktnum); #ifndef OLDCHKINT if (chkint() < 0) /* Check for console interrupts. */ return('z'); #endif /* OLDCHKINT */ k = getrbuf(); /* Get a new packet input buffer. */ debug(F101,"rpack getrbuf","",k); if (k < 0) { /* Return like this if none free. */ return(-1); } recpkt = r_pkt[k].bf_adr; *recpkt = '\0'; /* Clear receive buffer. */ sohp = recpkt; /* Initialize pointers to it. */ rdatap = recpkt; rsn = rln = -1; /* In case of failure. */ e = (turn) ? turnch : eol; /* Use any handshake char for eol */ /* Try to get a "line". */ #ifdef CK_AUTODL debug(F110,"rpack ksbuf",ksbuf,0); if (ksbuf[0]) { /* Kermit packet already */ int x; /* collected for us in CONNECT mode */ CHAR *s1 = recpkt, *s2 = ksbuf; j = 0; while (*s2) { /* Copy and get length */ *s1++ = *s2++; /* No point optimizing this since */ j++; /* it's never more than ~20 chars */ } *s1 = NUL; #ifdef PARSENSE x = parchk(recpkt, stchr, j); /* Check parity */ debug(F000,"autodownload parity","",parity); debug(F000,"autodownload parchk","",x); if (x > 0 && parity != x) { autopar = 1; parity = x; } #endif /* PARSENSE */ ksbuf[0] = NUL; /* Don't do this next time! */ } else { /* Normally go read a packet */ #endif /* CK_AUTODL */ #ifdef DEBUG if (deblog) { debug(F101,"rpack timint","",timint); debug(F101,"rpack rcvtimo","",rcvtimo); #ifdef STREAMING debug(F101,"rpack streaming","",streaming); #endif /* STREAMING */ #ifdef GFTIMER /* Measure how long it takes to read a packet */ t1 = gftimer(); #endif /* GFTIMER */ } #endif /* DEBUG */ /* JUST IN CASE (otherwise this could clobber streaming) */ if ((timint == 0 #ifdef STREAMING || streaming #endif /* STREAMING */ ) && (rcvtimo != 0)) { debug(F101,"rpack timint 0 || streaming but rcvtimo","",rcvtimo); rcvtimo = 0; } #ifdef PARSENSE #ifdef UNIX /* So far the final turn argument is only for ck[uvdl]tio.c. Should be added to the others too. (turn == handshake character.) */ j = ttinl(recpkt,r_pkt[k].bf_len - 1,rcvtimo,e,stchr,turn); #else #ifdef VMS j = ttinl(recpkt,r_pkt[k].bf_len - 1,rcvtimo,e,stchr,turn); #else #ifdef datageneral j = ttinl(recpkt,r_pkt[k].bf_len - 1,rcvtimo,e,stchr,turn); #else #ifdef STRATUS j = ttinl(recpkt,r_pkt[k].bf_len - 1,rcvtimo,e,stchr,turn); #else #ifdef OS2 j = ttinl(recpkt,r_pkt[k].bf_len - 1,rcvtimo,e,stchr,turn); #else #ifdef OSK j = ttinl(recpkt,r_pkt[k].bf_len - 1,rcvtimo,e,stchr,turn); #else j = ttinl(recpkt,r_pkt[k].bf_len - 1,rcvtimo,e,stchr); #endif /* OSK */ #endif /* OS2 */ #endif /* STRATUS */ #endif /* datageneral */ #endif /* VMS */ #endif /* UNIX */ if (parity != 0 && parity != 's' && ttprty != 0) { if (parity != ttprty) autopar = 1; parity = ttprty; } #else /* !PARSENSE */ j = ttinl(recpkt,r_pkt[k].bf_len - 1,rcvtimo,e); #endif /* PARSENSE */ #ifdef DEBUG if (deblog) { debug(F101,"rpack ttinl len","",j); #ifdef GFTIMER t2 = gftimer(); debug(F101,"rpack ttinl msec","",(long)((t2-t1)*1000.0)); #endif /* GFTIMER */ } #endif /* DEBUG */ #ifdef STREAMING if (streaming && sndtyp == 'D' && j == 0) return('Y'); #endif /* STREAMING */ if (j < 0) { /* -1 == timeout, -2 == ^C, -3 == connection lost or fatal i/o */ debug(F101,"rpack: ttinl fails","",j); /* Otherwise, */ freerbuf(k); /* Free this buffer */ if (j < -1) { /* Bail out if ^C^C typed. */ if (j == -2) { interrupted = 1; debug(F101,"rpack ^C server","",server); debug(F101,"rpack ^C en_fin","",en_fin); } else if (j == -3) { fatalio = 1; debug(F101,"rpack fatalio","",en_fin); } return(j); } if (nakstate) /* j == -1 is a read timeout */ xxscreen(SCR_PT,'T',(long)winlo,""); else xxscreen(SCR_PT,'T',(long)pktnum,""); logpkt('r',-1,(CHAR *)"",0); if (flow == 1) ttoc(XON); /* In case of Xoff blockage. */ return('T'); } #ifdef CK_AUTODL } #endif /* CK_AUTODL */ rpktl = j; tlci += j; /* All OK, Count the characters. */ flci += j; /* Find start of packet */ #ifndef PARSENSE for (i = 0; (recpkt[i] != stchr) && (i < j); i++) sohp++; /* Find mark */ if (i++ >= j) { /* Didn't find it. */ logpkt('r',-1,"",0); freerbuf(k); return('T'); } #else i = 1; /* ttinl does this for us */ #endif /* PARSENSE */ rpackets++; /* Count received packet. */ lp = i; /* Remember LEN position. */ if ((j = xunchar(recpkt[i++])) == 0) { /* Get packet length. */ if ((j = lp+5) > MAXRP) { /* Long packet */ return('Q'); /* Too long */ } #ifdef CKTUNING /* Save some function-call and loop overhead... */ #ifdef COMMENT /* ttinl() already removed parity */ if (parity) #endif /* COMMENT */ chk = (unsigned) ((unsigned) recpkt[i-1] + (unsigned) recpkt[i] + (unsigned) recpkt[i+1] + (unsigned) recpkt[i+2] + (unsigned) recpkt[i+3] ); #ifdef COMMENT else chk = (unsigned) ((unsigned) (recpkt[i-1] & 077) + (unsigned) (recpkt[i] & 077) + (unsigned) (recpkt[i+1] & 077) + (unsigned) (recpkt[i+2] & 077) + (unsigned) (recpkt[i+3] & 077) ); #endif /* COMMENT */ if (xunchar(recpkt[j]) != ((((chk & 0300) >> 6) + chk) & 077)) #else x = recpkt[j]; /* Header checksum. */ recpkt[j] = '\0'; /* Calculate & compare. */ if (xunchar(x) != chk1(recpkt+lp,5)) #endif /* CKTUNING */ { freerbuf(k); logpkt('r',-1,(CHAR *)"",0); xxscreen(SCR_PT,'%',(long)pktnum,"Bad packet header"); return('Q'); } #ifndef CKTUNING recpkt[j] = x; /* Checksum ok, put it back. */ #endif /* CKTUNING */ rln = xunchar(recpkt[j-2]) * 95 + xunchar(recpkt[j-1]) - bctl; j = 3; /* Data offset. */ } else if (j < 3) { debug(F101,"rpack packet length less than 3","",j); freerbuf(k); logpkt('r',-1,(CHAR *)"",0); xxscreen(SCR_PT,'%',(long)pktnum,"Bad packet length"); return('Q'); } else { rln = j - bctl - 2; /* Regular packet */ j = 0; /* No extended header */ } rsn = xunchar(recpkt[i++]); /* Sequence number */ if (pktlog) /* Save a function call! */ logpkt('r',rsn,sohp,rln+bctl+j+4); if (rsn < 0 || rsn > 63) { debug(F101,"rpack bad sequence number","",rsn); freerbuf(k); if (pktlog) logpkt('r',rsn,(CHAR *)"",0); xxscreen(SCR_PT,'%',(long)pktnum,"Bad sequence number"); return('Q'); } /* If this packet has the same type as the packet just sent, assume it is an echo and ignore it. Don't even bother with the block check calculation: even if the packet is corrupted, we don't want to NAK an echoed packet. Nor must we NAK an ACK or NAK. */ type = recpkt[i++]; /* Get packet's TYPE field */ if (type == sndtyp || (nakstate && (type == 'N' /* || type == 'Y' */ ))) { debug(F000,"rpack echo","",type); /* If it's an echo */ freerbuf(k); /* Free this buffer */ logpkt('#',rsn,(CHAR *)"",0); return('e'); /* Return special (lowercase) code */ } /* Separate the data from the block check, accounting for the case where a packet was retransmitted after the block check switched. The "Type 3 Forced" business is new to C-Kermit 9.0. */ if (bctf) { /* Type 3 forced on all packets */ bctl = chklen = 3; } else if ((type == 'I' || type == 'S')) { /* Otherwise... */ if (recpkt[11] == '5') { /* Sender is forcing Type 3 */ bctf = 1; /* So we will too */ bctl = chklen = 3; debug(F100,"RECOGNIZE BLOCK CHECK TYPE 5","",0); } else { /* Normal case */ /* I & S packets always have type 1 */ chklen = 1; rln = rln + bctl - 1; } } else if (type == 'N') { /* A NAK packet never has data */ chklen = xunchar(recpkt[lp]) - 2; if (chklen < 1 || chklen > 3) { /* JHD 13 Apr 2010 */ debug(F101,"rpack bad nak chklen","",chklen); freerbuf(k); logpkt('r',-1,(CHAR *)"",0); xxscreen(SCR_PT,'%',(long)pktnum,"(bad nak)"); return('Q'); } rln = rln + bctl - chklen; } else chklen = bctl; #ifdef DEBUG if (deblog) { /* Save 2 function calls */ debug(F101,"rpack bctl","",bctl); debug(F101,"rpack chklen","",chklen); } #endif /* DEBUG */ i += j; /* Buffer index of DATA field */ rdatap = recpkt+i; /* Pointer to DATA field */ if ((j = rln + i) > r_pkt[k].bf_len) { /* Make sure it fits */ debug(F101,"packet too long","",j); freerbuf(k); logpkt('r',rsn,(CHAR *)"",0); return('Q'); } for (x = 0; x < chklen; x++) /* Copy the block check */ pbc[x] = recpkt[j+x]; /* 3 bytes at most. */ pbc[x] = '\0'; /* Null-terminate block check string */ recpkt[j] = '\0'; /* and the packet Data field. */ if (chklen == 2 && bctu == 4) { /* Adjust for Blank-Free-2 */ chklen = 4; /* (chklen is now a misnomer...) */ debug(F100,"rpack block check B","",0); } switch (chklen) { /* Check the block check */ case 1: /* Type 1, 6-bit checksum */ if (xunchar(*pbc) != chk1(recpkt+lp,j-lp)) { #ifdef DEBUG if (deblog) { debug(F110,"checked chars",recpkt+lp,0); debug(F101,"block check (1)","",(int) xunchar(*pbc)); debug(F101,"should be (1)","",chk1(recpkt+lp,j-lp)); } #endif /* DEBUG */ freerbuf(k); logpkt('r',-1,(CHAR *)"",0); xxscreen(SCR_PT,'%',(long)pktnum,"Checksum error"); return('Q'); } break; case 2: /* Type 2, 12-bit checksum */ x = xunchar(*pbc) << 6 | xunchar(pbc[1]); if (x != chk2(recpkt+lp,j-lp)) { /* No match */ if (type == 'E') { /* Allow E packets to have type 1 */ recpkt[j++] = pbc[0]; recpkt[j] = '\0'; if (xunchar(pbc[1]) == chk1(recpkt+lp,j-lp)) break; else recpkt[--j] = '\0'; } #ifdef DEBUG if (deblog) { debug(F110,"checked chars",recpkt+lp,0); debug(F101,"block check (2)","", x); debug(F101,"should be (2)","", (int) chk2(recpkt+lp,j-lp)); } #endif /* DEBUG */ freerbuf(k); logpkt('r',-1,(CHAR *)"",0); xxscreen(SCR_PT,'%',(long)pktnum,"Checksum error"); return('Q'); } break; case 3: /* Type 3, 16-bit CRC */ crc = (xunchar(pbc[0]) << 12) | (xunchar(pbc[1]) << 6) | (xunchar(pbc[2])); if (crc != chk3(recpkt+lp,j-lp)) { if (type == 'E') { /* Allow E packets to have type 1 */ recpkt[j++] = pbc[0]; recpkt[j++] = pbc[1]; recpkt[j] = '\0'; if (xunchar(pbc[2]) == chk1(recpkt+lp,j-lp)) break; else { j -=2; recpkt[j] = '\0'; } } #ifdef DEBUG if (deblog) { debug(F110,"checked chars",recpkt+lp,0); debug(F101,"block check (3)","",crc); debug(F101,"should be (3)","",(int) chk3(recpkt+lp,j-lp)); } #endif /* DEBUG */ freerbuf(k); logpkt('r',-1,(CHAR *)"",0); xxscreen(SCR_PT,'%',(long)pktnum,"CRC error"); return('Q'); } break; case 4: /* Type 4 = Type 2, no blanks. */ x = (unsigned)((xunchar(*pbc) - 1) << 6) | (unsigned)(xunchar(pbc[1]) - 1); if (x != chk2(recpkt+lp,j-lp)) { if (type == 'E') { /* Allow E packets to have type 1 */ recpkt[j++] = pbc[0]; recpkt[j] = '\0'; if (xunchar(pbc[1]) == chk1(recpkt+lp,j-lp)) break; else recpkt[--j] = '\0'; } debug(F101,"bad type B block check","",x); freerbuf(k); logpkt('r',-1,(CHAR *)"",0); xxscreen(SCR_PT,'%',(long)pktnum,"Checksum error"); return('Q'); } break; default: /* Shouldn't happen... */ freerbuf(k); logpkt('r',-1,(CHAR *)"",0); xxscreen(SCR_PT,'%',(long)pktnum,"(crunched)"); return('Q'); } debug(F101,"rpack block check OK","",rsn); /* Now we can believe the sequence number, and other fields. */ /* Here we violate strict principles of layering, etc, and look at the */ /* packet sequence number. If there's already a packet with the same */ /* number in the window, we remove this one so that the window will not */ /* fill up. */ if ((x = rseqtbl[rsn]) != -1) { /* Already a packet with this number */ retrans++; /* Count it for statistics */ debug(F101,"rpack got dup","",rsn); logpkt('r',rsn,(CHAR *)"",0); freerbuf(x); /* Free old buffer, keep new packet. */ r_pkt[k].pk_rtr++; /* Count this as a retransmission. */ } /* New packet, not seen before, enter it into the receive window. */ #ifdef CK_TIMERS if (timint > 0) rrttbl[rsn] = gtimer(); /* Timestamp */ #endif /* CK_TIMERS */ rseqtbl[rsn] = k; /* Make back pointer */ r_pkt[k].pk_seq = rsn; /* Record in packet info structure */ r_pkt[k].pk_typ = type; /* Sequence, type,... */ r_pkt[k].pk_adr = rdatap; /* pointer to data buffer */ if (local) { /* Save a function call! */ int x = 0; if (fdispla != XYFD_N) x = 1; if (fdispla == XYFD_B && (type == 'D' || sndtyp == 'D')) x = 0; if (x) /* Update screen */ xxscreen(SCR_PT,(char)type,(long)rsn,(char *)sohp); } return(type); /* Return packet type */ } /* L O G P K T -- Log packet number n, pointed to by s. */ /* c = 's' (send) or 'r' (receive) */ VOID #ifdef CK_ANSIC logpkt(char c,int n, CHAR *s, int len) #else logpkt(c,n,s,len) char c; int n; CHAR *s; int len; #endif /* CK_ANSIC */ /* logpkt */ { char plog[20]; if (!s) s = (CHAR *)""; if (pktlog) if (chkfn(ZPFILE) > 0) { if (n < 0) /* Construct entry header */ sprintf(plog,"%c-xx-%02d-",c,(gtimer()%60)); /* safe */ else sprintf(plog,"%c-%02d-%02d-",c,n,(gtimer()%60)); /* safe */ if (zsoutx(ZPFILE,plog,(int)strlen(plog)) < 0) { pktlog = 0; return; } else { if (len == 0) len = strlen((char *)s); if (len > 0) { char * p; /* Make SOP printable */ int x; /* so we can look at logs without */ p = dbchr(*s); /* triggering autodownload. */ x = strlen(dbchr(*s)); if (*s < 32 || (*s > 127 && *s < 160)) { if (zsoutx(ZPFILE,p,x) < 0) { pktlog = 0; return; } else { len--; s++; } } } if (zsoutx(ZPFILE,(char *)s,len) < 0) { pktlog = 0; return; } else if (zsoutx(ZPFILE, #ifdef UNIX "\n", 1 #else #ifdef datageneral "\n", 1 #else #ifdef OSK "\r", 1 #else #ifdef MAC "\r", 1 #else "\015\012", 2 #endif /* MAC */ #endif /* OSK */ #endif /* datageneral */ #endif /* UNIX */ ) < 0) { pktlog = 0; } } } } /* T S T A T S -- Record statistics in transaction log */ VOID tstats() { char *tp = NULL; #ifdef GFTIMER CKFLOAT xx; /* Elapsed time divisor */ #endif /* GFTIMER */ debug(F101,"tstats xfsecs","",xfsecs); debug(F101,"tstats filcnt","",filcnt); if (filcnt == 1) { /* Get timing for statistics */ tsecs = xfsecs; /* Single file, we already have it */ #ifdef GFTIMER debug(F101,"tstats fpxfsecs","",(int)fpxfsecs); fptsecs = fpxfsecs; #endif /* GFTIMER */ } else { /* Multiple files */ tsecs = gtimer(); /* Get current time */ #ifdef GFTIMER fptsecs = gftimer(); #endif /* GFTIMER */ } #ifdef GFTIMER if (fptsecs <= GFMINTIME) /* Calculate CPS */ fptsecs = (CKFLOAT) GFMINTIME; debug(F101,"tstats fptsecs","",(int)fptsecs); xx = (CKFLOAT) tfc / fptsecs; if (sizeof(long) <= 4) { /* doesn't account for 16-bit longs */ if (xx > 2147483647.0) tfcps = 2147483647L; /* 31 bits */ else tfcps = (long) xx; } else tfcps = (long) xx; #else if (tsecs < 2L) tsecs = 1L; debug(F101,"tstats tsecs","",tsecs); tfcps = tfc / tsecs; #endif /* GFTIMER */ ztime(&tp); /* Get time stamp */ tlog(F100,"","",0L); /* Leave a blank line */ tlog(F110,"Transaction complete",tp,0L); /* Record it */ if (filcnt < 1) return; /* If no files, done. */ /* If multiple files, record character totals for all files */ if (filcnt > 1) { tlog(F101," files transferred ","",filcnt - filrej); tlog(F101," total file characters ","",tfc); tlog(F101," communication line in ","",tlci); tlog(F101," communication line out ","",tlco); } /* Record timing info for one or more files */ #ifdef GFTIMER if (filcnt - filrej == 1) { tlog(F101," elapsed time (seconds) ","",(long) fpxfsecs); tlog(F101," effective data rate ","",filcps); } else { tlog(F101," elapsed time (seconds) ","",(long) fptsecs); tlog(F101," effective data rate ","",(long) xx); } #else tlog(F101," elapsed time (seconds) ","",tsecs); if (tsecs > 0) tlog(F101," effective data rate ","",(tfc / tsecs)); #endif /* GFTIMER */ tlog(F100,"","",0L); /* Leave a blank line */ } /* F S T A T S -- Record file statistics in transaction log */ VOID fcps() { #ifdef GFTIMER double xx; fpxfsecs = gftimer() - fpfsecs; if (fpxfsecs <= GFMINTIME) fpxfsecs = (CKFLOAT) GFMINTIME; xx = (CKFLOAT) ffc / fpxfsecs; if (sizeof(long) <= 4) { if (xx > 2147483647.0) tfcps = 2147483647L; /* 31 bits */ else filcps = (long) xx; } else filcps = (long) xx; if (sizeof(int) >= 4) xfsecs = (int) fpxfsecs; else if (fpxfsecs < 32768.0) xfsecs = (int) fpxfsecs; else xfsecs = 32767; #else /* GFTIMER */ xfsecs = gtimer() - fsecs; if (xfsecs < 1L) xfsecs = 1L; filcps = ffc / xfsecs; #endif /* GFTIMER */ } VOID fstats() { tfc += ffc; #ifdef DEBUG if (deblog) { debug(F101,"fstats tfc","",tfc); debug(F101,"fstats what","",what); debug(F110,"fstats epktmsg",epktmsg,0); } #endif /* DEBUG */ #ifdef TLOG if (!discard && !cxseen && !czseen && what != W_NOTHING && !*epktmsg) tlog(F101," complete, size","",ffc); #endif /* TLOG */ } #endif /* NOXFER */ ckcfn3.c0000644000015300001460000022575211334325754011220 0ustar fdckermit/* C K C F N 3 -- Packet buffer management for C-Kermit */ /* (plus assorted functions tacked on at the end) */ /* Author: Frank da Cruz , Columbia University Academic Information Systems, New York City. Copyright (C) 1985, 2010, Trustees of Columbia University in the City of New York. All rights reserved. See the C-Kermit COPYING.TXT file or the copyright text in the ckcmai.c module for disclaimer and permissions. */ /* Note -- if you change this file, please amend the version number and date at the top of ckcfns.c accordingly. */ #include "ckcsym.h" #include "ckcdeb.h" #include "ckcasc.h" #include "ckcker.h" #include "ckcxla.h" /* C K M K D I R -- Create a directory */ /* Call with: int fc = 0 to create, nonzero to remove, a directory. char * s = pointer to name of directory to create or remove. char ** r = address of pointer to return name or message. int m = 1 to print error messages, 0 to be silent. int cvt = 1 means convert s from standard format to local format; 0 means use s as is. Returns: 0 on success (directory was created or removed). -1 when attempt to create the directory failed. -2 on internal error (e.g. no code for creating directories). On success, the name is pointed to by p. On failure, the reason is pointed to by p. */ #ifdef CK_MKDIR static char ckmkdbuf[CKMAXPATH+1]; #else #ifdef datageneral static char ckmkdbuf[CKMAXPATH+1]; #endif /* datageneral */ #endif /* CK_MKDIR */ #ifdef CK_MKDIR int ckmkdir(fc,s,r,m,cvt) int fc; char * s; char ** r; int m; int cvt; { int x, rc = -2; char tmpbuf[CKMAXPATH+1]; char buf2[CKMAXPATH+1]; if (!s) s = ""; debug(F110,"ckmkdir 1 fc",s,fc); if (!*s) { ckmakmsg(ckmkdbuf, CKMAXPATH+1, (fc == 0) ? "mkdir" : "rmdir", ": no name given", NULL, NULL ); *r = ckmkdbuf; return(-2); } #ifdef datageneral /* Come back and make this nicer later if anybody notices */ if (fc == 0) { /* mkdir */ rc = createdir(s,0); } else { /* rmdir */ /* AOS/VS rmdir() is a no-op. */ ckmakmsg(tmpbuf,CKMAXPATH+1,"delete ",s,NULL,NULL); debug(F110,"ckmkdir 2",tmpbuf,0); rc = system(tmpbuf); } *r = NULL; #else /* not datageneral */ /* First make sure the name has an acceptable directory-name format */ #ifdef VMS { char *p = s; int lb = 0, rb = 0, sl = 0; while (*p) { if (*p == '[' || *p == '<') lb++; /* Count brackets */ else if (*p == ']' || *p == '>') rb++; else if (*p == '/') sl++; /* and slashes */ p++; } if (lb != 1 && rb != 1 && sl == 0 && p > s && *(p-1) != ':') { /* Probably just a word - convert to VMS format */ ckmakmsg(buf2, CKMAXPATH+1, "[", (*s == '.') ? "" : ".", s, "]" ); s = buf2; } else if (lb == 0 && rb == 0 && sl != 0 && p > s && *(p-1) != ':') { int flag = 0; /* Seems to be in UNIX format */ x = strlen(s); if (x > 0 && s[x-1] != '/') flag = 1; ckmakmsg(buf2,CKMAXPATH+1,s,flag ? "/" : "",NULL,NULL); s = buf2; } if (s == buf2) { ckstrncpy(tmpbuf,s,CKMAXPATH+1); s = tmpbuf; } debug(F110,"ckmkdir 2+VMS",s,0); } #else #ifdef UNIXOROSK #ifdef DTILDE s = tilde_expand(s); #endif /* DTILDE */ ckstrncpy(tmpbuf,s,CKMAXPATH+1); s = tmpbuf; x = strlen(s); if (x > 0 && s[x-1] != '/') { /* Must end in "/" for zmkdir() */ s[x] = '/'; s[x+1] = NUL; debug(F110,"ckmkdir 2+UNIXOROSK",s,0); } #else /* UNIXOROSK */ #ifdef OS2 ckstrncpy(tmpbuf,s,CKMAXPATH+1); s = tmpbuf; x = strlen(s); if (fc == 0 && x > 0 && s[x-1] != '/') { /* Must end in "/" for zmkdir() */ s[x] = '/'; s[x+1] = NUL; debug(F110,"ckmkdir 2+OS2",s,0); } #endif /* OS2 */ #endif /* UNIXOROSK */ #endif /* VMS */ #ifdef NZLTOR /* Server is calling us, so convert to local format if necessary */ if (cvt) { nzrtol(s,(char *)buf2,1,PATH_ABS,CKMAXPATH); s = buf2; debug(F110,"ckmkdir 3",s,0); } #endif /* NZLTOR */ debug(F110,"ckmkdir 4",s,0); if (fc == 0) { /* Making */ #ifdef CK_MKDIR rc = zmkdir(s); #else #ifdef NT rc = _mkdir(s); #else rc = mkdir(s,0777); #endif /* NT */ #endif /* CK_MKDIR */ } else { /* Removing */ #ifdef ZRMDIR rc = zrmdir(s); #else #ifdef NT rc = _rmdir(s); #else #ifdef OSK rc = -2; #else rc = rmdir(s); #endif /* OSK */ #endif /* NT */ #endif /* ZRMDIR */ } #endif /* datageneral */ debug(F101,"ckmkdir rc","",rc); if (rc == -2) { ckmakmsg(ckmkdbuf, CKMAXPATH, "Directory ", (fc == 0) ? "creation" : "removal", "not implemented in this version of C-Kermit", NULL ); *r = ckmkdbuf; if (m) printf("%s\n",*r); } else if (rc < 0) { if (m) perror(s); ckmakmsg(ckmkdbuf,CKMAXPATH,s,": ",ck_errstr(),NULL); *r = ckmkdbuf; } else if (fc == 0 && zfnqfp(s,CKMAXPATH,ckmkdbuf)) { *r = ckmkdbuf; } else if (fc != 0) { ckmakmsg(ckmkdbuf,CKMAXPATH,s,": removed",NULL,NULL); *r = ckmkdbuf; } return(rc); } #endif /* CK_MKDIR */ #ifndef NOXFER /* Rest of this file... */ #ifndef NODISPO #ifdef pdp11 #define NODISPO #endif /* pdpd11 */ #endif /* NODISPO */ extern int pipesend; #ifdef PIPESEND extern char ** sndfilter; #endif /* PIPESEND */ extern int unkcs, wmax, wcur, discard, bctu, bctl, local, fdispla, what, sendmode, opnerr, dest, epktrcvd, epktsent, filestatus, eofmethod, dispos, fncnv, fnrpath; extern char * ofn2; extern char * rfspec, * sfspec, * prfspec, * psfspec, * rrfspec, * prrfspec; extern char ofn1[]; extern int ofn1x; extern char * ofperms; #ifdef VMS extern int batch; #else extern int backgrd; #endif /* VMS */ extern int xflg, remfile, remappd; extern CHAR *data; extern char filnam[]; #ifndef NOFRILLS extern int rprintf, rmailf; /* REMOTE MAIL, PRINT */ char optbuf[OPTBUFLEN]; /* Options for MAIL or REMOTE PRINT */ #endif /* NOFRILLS */ extern int wslots; extern int fblksiz, frecl, forg, frecfm, fncact, fncsav, fcctrl, lf_opts; extern CHAR * srvcmd; extern int srvcmdlen; extern int binary, spsiz; extern int pktnum, cxseen, czseen, nfils, stdinf; extern int memstr, stdouf, keep, sndsrc, hcflg; extern int server, en_cwd, en_mai, en_pri; /* Attributes in/out enabled flags */ extern int atenci, atenco, atdati, atdato, atleni, atleno, atblki, atblko, attypi, attypo, atsidi, atsido, atsysi, atsyso, atdisi, atdiso; #ifdef CK_PERMS extern int atlpri, atlpro, atgpri, atgpro; #endif /* CK_PERMS */ #ifdef STRATUS extern int atfrmi, atfrmo, atcrei, atcreo, atacti, atacto; #endif /* STRATUS */ #ifdef datageneral extern int quiet; #endif /* datageneral */ extern long filcnt; extern CK_OFF_T fsize, ffc, tfc, sendstart, calibrate; CK_OFF_T rs_len; #ifndef NOCSETS _PROTOTYP (VOID setxlate, (void)); extern int tcharset, fcharset; extern int ntcsets, xlatype, xfrxla; extern struct csinfo tcsinfo[], fcsinfo[]; #endif /* NOCSETS */ /* Variables global to Kermit that are defined in this module */ #ifdef CKXXCHAR /* DOUBLE / IGNORE char table */ int dblflag = 0; int ignflag = 0; short dblt[256] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; #endif /* CKXXCHAR */ int winlo; /* packet number at low window edge */ int sbufnum; /* number of free buffers */ int dum001 = 1234; /* protection... */ int sbufuse[MAXWS]; /* buffer in-use flag */ int dum003 = 1111; int rbufnum; /* number of free buffers */ int dum002 = 4321; /* more protection */ int rbufuse[MAXWS]; /* buffer in-use flag */ int sseqtbl[64]; /* sequence # to buffer # table */ int rseqtbl[64]; /* sequence # to buffer # table */ int sacktbl[64]; /* sequence # ack table */ int o_isopen = 0, i_isopen = 0; /* Input & output files are open */ #ifdef DYNAMIC struct pktinfo *s_pkt = NULL; /* array of pktinfo structures */ struct pktinfo *r_pkt = NULL; /* array of pktinfo structures */ #else struct pktinfo s_pkt[MAXWS]; /* array of pktinfo structures */ struct pktinfo r_pkt[MAXWS]; /* array of pktinfo structures */ #endif /* DYNAMIC */ #ifdef DEBUG char xbuf[200]; /* For debug logging */ #endif /* DEBUG */ #ifdef DYNAMIC CHAR *bigsbuf = NULL, *bigrbuf = NULL; #else char bigsbt[8]; /* Protection (shouldn't need this). */ /* BUT DON'T REMOVE IT! */ CHAR bigsbuf[SBSIZ + 5]; /* Send-packet buffer area */ char bigrbt[8]; /* Safety padding */ CHAR bigrbuf[RBSIZ + 5]; /* Receive-packet area */ #endif int bigsbsiz = SBSIZ; /* Sizes of big send & rcv buffers. */ int bigrbsiz = RBSIZ; #ifdef VMS int zchkpath(char *s); #endif /* VMS */ /* FUNCTIONS */ VOID dofast() { long maxbufsiz = RBSIZ; /* Configuration parameters */ int maxpktsiz = MAXSP; extern int spsizf, /* For bug in IRIX Telnet server */ rpsiz, urpsiz, spsizr, spmax, wslotr; extern struct ck_p ptab[]; if (maxpktsiz < 40) /* Long packet length */ maxpktsiz = 40; else if (maxpktsiz > 4000) maxpktsiz = 4000; wslotr = maxbufsiz / maxpktsiz; if (wslotr > MAXWS) /* Window slots */ wslotr = MAXWS; if (wslotr > 30) wslotr = 30; else if (wslotr < 1) wslotr = 1; urpsiz = adjpkl(maxpktsiz,wslotr,maxbufsiz); ptab[PROTO_K].rpktlen = urpsiz; rpsiz = (urpsiz > 94) ? 94 : urpsiz; /* Max non-long packet length */ debug(F111,"dofast","uprsiz",urpsiz); #ifdef IRIX #ifndef IRIX65 /* IRIX Telnet server chops off writes longer than 4K */ spsiz = spmax = spsizr = urpsiz; debug(F101,"doarg Q IRIX spsiz","",spsiz); spsizf = 1; #endif /* IRIX65 */ #endif /* IRIX */ #ifdef CK_SPEED setprefix(PX_CAU); /* Cautious unprefixing */ #endif /* CK_SPEED */ } /* For sanity, use "i" for buffer slots, "n" for packet numbers. */ /* I N I B U F S */ /* Allocates the big send and receive buffers. Call with size for big send buffer (s) and receive buffer (r). These sizes can be different. Attempts to allocate buffers of the requested size, but if it can't, it will allocate smaller ones. Sets global variables bigsbsiz and bigrbsiz to the actual sizes, and bigsbuf and bigrbuf pointing to the actual buffers. Designed to be called more than once. Returns 0 on success, -1 on failure. */ CHAR *bigbufp = NULL; int inibufs(s,r) int s, r; { #ifdef DYNAMIC unsigned int size; #ifdef OS2 unsigned /* Don't you wish everybody had unsigned long... */ #endif /* OS2 */ long z; int x; debug(F101,"inibufs s","",s); debug(F101,"inibufs r","",r); if (s < 80 || r < 80) return(-1); /* Validate arguments. */ if (!s_pkt) { /* Allocate packet info structures */ if (!(s_pkt = (struct pktinfo *) malloc(sizeof(struct pktinfo)*MAXWS))) fatal("ini_pkts: no memory for s_pkt"); } for (x = 0; x < MAXWS; x++) s_pkt[x].pk_adr = NULL; /* Initialize addresses */ if (!r_pkt) { if (!(r_pkt = (struct pktinfo *) malloc(sizeof(struct pktinfo)*MAXWS))) fatal("ini_pkts: no memory for s_pkt"); } for (x = 0; x < MAXWS; x++) r_pkt[x].pk_adr = NULL; /* Initialize addresses */ if (!srvcmd) { /* Allocate srvcmd buffer */ srvcmd = (CHAR *) malloc(r + 100); if (!srvcmd) return(-1); srvcmdlen = r + 99; *srvcmd = NUL; } if (bigbufp) { /* Free previous buffers, if any. */ free(bigbufp); bigbufp = NULL; } size = s + r + 40; /* Combined requested size + padding */ z = (unsigned) s + (unsigned) r + 40; debug(F101,"inibufs size 1","",size); debug(F101,"inibufs size z","",z); if ((long) size != z) { debug(F100,"inibufs overflow","",0); size = 65535; } /* Try to get the space. If malloc fails, try to get a little less. */ /* (Obviously, this algorithm can be refined.) */ while (!(bigbufp = (CHAR *) malloc(size))) { debug(F101,"inibufs bigbuf malloc failed","",size); size = (size * 2) / 3; /* Failed, cut size by 1/3. */ if (size < 200) /* Try again until too small. */ return(-1); } debug(F101,"inibufs size 2","",size); /* OK, we got some space. */ /* Now divide the allocated space between the send and receive buffers in the requested proportion. The natural formula would be (s / (s + r)) * size (for the send buffer), but that doesn't work with integer arithmetic and we can't use floating point because some machines don't have it. This can be rearranged as (s * size) / (s + r). But (s * size) can be VERY large, too large for 32 bits. So let's do it this way. This arithmetic works for buffer sizes up to about 5,000,000. */ #define FACTOR 20L z = ( (long) s * FACTOR ) / ( (long) s + (long) r ); x = ( z * ( (long) size / FACTOR ) ); if (x < 0) return(-1); /* Catch overflow */ bigsbsiz = x - 5; /* Size of send buffer */ bigsbuf = bigbufp; /* Address of send buffer */ debug(F101,"inibufs bigsbsiz","",bigsbsiz); bigrbsiz = size - x - 5; /* Size of receive buffer */ bigrbuf = bigbufp + x; /* Addresss of receive buffer */ debug(F101,"inibufs bigrbsiz","",bigrbsiz); return(0); /* Success */ #else /* No dynamic allocation */ bigsbsiz = SBSIZ; /* Just use the symbols */ bigrbsiz = RBSIZ; /* ... */ return(0); /* Success. */ #endif /* DYNAMIC */ } /* M A K E B U F -- Makes and clears a new buffers. */ /* Call with: */ /* slots: number of buffer slots to make, 1 to 32 */ /* bufsiz: size of the big buffer */ /* buf: address of the big buffer */ /* xx: pointer to array of pktinfo structures for these buffers */ /* Subdivides the big buffer into "slots" buffers. */ /* Returns: */ /* -1 if too many or too few slots requested, */ /* -2 if slots would be too small. */ /* n (positive) on success = size of one buffer. */ /* with pktinfo structure initialized for this set of buffers. */ int makebuf(slots,bufsiz,buf,xx) /* makebuf */ int slots, bufsiz; CHAR buf[]; struct pktinfo *xx; { CHAR *a; int i, size; debug(F101,"makebuf","",slots); debug(F101,"makebuf bufsiz","",bufsiz); debug(F101,"makebuf MAXWS","",MAXWS); if (slots > MAXWS || slots < 1) return(-1); if (bufsiz < slots * 10 ) return(-2); size = bufsiz / slots; /* Divide up the big buffer. */ a = buf; /* Address of first piece. */ for (i = 0; i < slots; i++) { struct pktinfo *x = &xx[i]; x->bf_adr = a; /* Address of this buffer */ x->bf_len = size; /* Length of this buffer */ x->pk_len = 0; /* Length of data field */ x->pk_typ = ' '; /* packet type */ x->pk_seq = -1; /* packet sequence number */ x->pk_rtr = 0; /* retransmissions */ *a = '\0'; /* Clear the buffer */ a += size; /* Position to next buffer slot */ } return(size); } /* M A K S B U F -- Makes the send-packet buffer */ int mksbuf(slots) int slots; { int i, x; sbufnum = 0; if ((x = makebuf(slots,bigsbsiz,bigsbuf,s_pkt)) < 0) { debug(F101,"mksbuf makebuf return","",x); return(x); } debug(F101,"mksbuf makebuf return","",x); for (i = 0; i < 64; i++) { /* Initialize sequence-number- */ sseqtbl[i] = -1; /* to-buffer-number table. */ sacktbl[i] = 0; } for (i = 0; i < MAXWS; i++) sbufuse[i] = 0; /* Mark each buffer as free */ sbufnum = slots; wcur = 0; return(x); } /* M A K R B U F -- Makes the receive-packet buffer */ int mkrbuf(slots) int slots; { int i, x; rbufnum = 0; if ((x = makebuf(slots,bigrbsiz,bigrbuf,r_pkt)) < 0) { debug(F101,"mkrbuf makebuf return","",x); return(x); } debug(F101,"mkrbuf makebuf return","",x); for (i = 0; i < 64; i++) { /* Initialize sequence-number- */ rseqtbl[i] = -1; /* to-buffer-number table. */ } for (i = 0; i < MAXWS; i++) rbufuse[i] = 0; /* Mark each buffer as free */ rbufnum = slots; wcur = 0; return(x); } /* W I N D O W -- Resize the window to n */ int window(n) int n; { debug(F101,"window","",n); if (n < 1 || n > MAXWS) return(-1); if (mksbuf(n) < 0) return(-1); if (mkrbuf(n) < 0) return(-1); wslots = n; #ifdef DEBUG if (deblog) dumpsbuf(); if (deblog) dumprbuf(); #endif /* DEBUG */ return(0); } /* G E T S B U F -- Allocate a send-buffer. */ /* Call with packet sequence number to allocate buffer for. */ /* Returns: */ /* -4 if argument is invalid (negative, or greater than 63) */ /* -3 if buffers were thought to be available but really weren't (bug!) */ /* -2 if the number of free buffers is negative (bug!) */ /* -1 if no free buffers. */ /* 0 or positive, packet sequence number, with buffer allocated for it. */ int getsbuf(n) int n; { /* Allocate a send-buffer */ int i; CHAR * p = NULL; if (n < 0 || n > 63) { debug(F101,"getsbuf bad arg","",n); return(-4); /* Bad argument */ } debug(F101,"getsbuf packet","",n); /* debug(F101,"getsbuf, sbufnum","",sbufnum); */ if (sbufnum == 0) return(-1); /* No free buffers. */ if (sbufnum < 0) return(-2); /* Shouldn't happen. */ for (i = 0; i < wslots; i++) /* Find the first one not in use. */ if (sbufuse[i] == 0) { /* Got one? */ sbufuse[i] = 1; /* Mark it as in use. */ sbufnum--; /* One less free buffer. */ *s_pkt[i].bf_adr = '\0'; /* Zero the buffer data field */ s_pkt[i].pk_seq = n; /* Put in the sequence number */ sseqtbl[n] = i; /* Back pointer from sequence number */ sacktbl[n] = 0; /* ACK flag */ s_pkt[i].pk_len = 0; /* Data field length now zero. */ s_pkt[i].pk_typ = ' '; /* Blank the packet type too. */ s_pkt[i].pk_rtr = 0; /* Zero the retransmission count */ p = s_pkt[i].bf_adr + 7; /* Set global "data" address. */ debug(F101,"getsbuf p","",0); data = p; if (!data) { debug(F100,"getsbuf data == NULL","",0); return(-3); } if ((what & (W_SEND|W_REMO)) && (++wcur > wmax)) wmax = wcur; /* For statistics. */ /* debug(F101,"getsbuf wcur","",wcur); */ return(n); /* Return its index. */ } sbufnum = 0; /* Didn't find one. */ return(-3); /* Shouldn't happen! */ } int getrbuf() { /* Allocate a receive buffer */ int i; #ifdef COMMENT /* This code is pretty stable by now... */ /* Looks like we might need this after all */ debug(F101,"getrbuf rbufnum","",rbufnum); debug(F101,"getrbuf wslots","",wslots); debug(F101,"getrbuf dum002","",dum002); debug(F101,"getrbuf dum003","",dum003); #endif /* COMMENT */ if (rbufnum == 0) return(-1); /* No free buffers. */ if (rbufnum < 0) return(-2); /* Shouldn't happen. */ for (i = 0; i < wslots; i++) /* Find the first one not in use. */ if (rbufuse[i] == 0) { /* Got one? */ rbufuse[i] = 1; /* Mark it as in use. */ *r_pkt[i].bf_adr = '\0'; /* Zero the buffer data field */ rbufnum--; /* One less free buffer. */ debug(F101,"getrbuf new rbufnum","",rbufnum); if ((what & W_RECV) && (++wcur > wmax)) wmax = wcur; /* For statistics. */ /* debug(F101,"getrbuf wcur","",wcur); */ return(i); /* Return its index. */ } /* debug(F101,"getrbuf foulup","",i); */ rbufnum = 0; /* Didn't find one. */ return(-3); /* Shouldn't happen! */ } /* F R E E S B U F -- Free send-buffer for given packet sequence number */ /* Returns: */ /* 1 upon success */ /* -1 if specified buffer does not exist */ int freesbuf(n) int n; { /* Release send-buffer for packet n. */ int i; debug(F101,"freesbuf","",n); if (n < 0 || n > 63) /* No such packet. */ return(-1); i = sseqtbl[n]; /* Get the window slot number. */ if (i > -1 && i <= wslots) { sseqtbl[n] = -1; /* If valid, remove from seqtbl */ sbufnum++; /* and count one more free buffer */ sbufuse[i] = 0; /* and mark it as free, */ if (what & (W_SEND|W_REMO)) /* decrement active slots */ wcur--; /* for statistics and display. */ } else { debug(F101," sseqtbl[n]","",sseqtbl[n]); return(-1); } /* The following is done only so dumped buffers will look right. */ if (1) { *s_pkt[i].bf_adr = '\0'; /* Zero the buffer data field */ s_pkt[i].pk_seq = -1; /* Invalidate the sequence number */ s_pkt[i].pk_len = 0; /* Data field length now zero. */ s_pkt[i].pk_typ = ' '; /* Blank the packet type too. */ s_pkt[i].pk_rtr = 0; /* And the retries field. */ } return(1); } int freerbuf(i) int i; { /* Release receive-buffer slot "i". */ int n; /* NOTE !! Currently, this function frees the indicated buffer, but */ /* does NOT erase the data. The program counts on this. Will find a */ /* better way later.... */ /* debug(F101,"freerbuf, slot","",i); */ if (i < 0 || i >= wslots) { /* No such slot. */ debug(F101,"freerbuf no such slot","",i); return(-1); } n = r_pkt[i].pk_seq; /* Get the packet sequence number */ debug(F101,"freerbuf packet","",n); if (n > -1 && n < 64) /* If valid, remove from seqtbl */ rseqtbl[n] = -1; if (rbufuse[i] != 0) { /* If really allocated, */ rbufuse[i] = 0; /* mark it as free, */ rbufnum++; /* and count one more free buffer. */ if (what & W_RECV) /* Keep track of current slots */ wcur--; /* for statistics and display */ debug(F101,"freerbuf rbufnum","",rbufnum); } /* The following is done only so dumped buffers will look right. */ if (1) { /* *r_pkt[i].bf_adr = '\0'; */ /* Zero the buffer data field */ r_pkt[i].pk_seq = -1; /* And from packet list */ r_pkt[i].pk_len = 0; /* Data field length now zero. */ r_pkt[i].pk_typ = ' '; /* Blank the packet type too. */ r_pkt[i].pk_rtr = 0; /* And the retries field. */ } return(1); } /* This is like freerbuf, except it's called with a packet sequence number */ /* rather than a packet buffer index. */ VOID freerpkt(seq) int seq; { int k; debug(F101,"freerpkt seq","",seq); k = rseqtbl[seq]; /* debug(F101,"freerpkt k","",k); */ if (k > -1) { k = freerbuf(k); /* debug(F101,"freerpkt freerbuf","",k); */ } } /* C H K W I N -- Check if packet n is in window. */ /* Returns: */ /* 0 if it is in the current window, */ /* +1 if it would have been in previous window (e.g. if ack was lost), */ /* -1 if it is outside any window (protocol error), */ /* -2 if either of the argument packet numbers is out of range. */ /* Call with packet number to check (n), lowest packet number in window */ /* (bottom), and number of slots in window (slots). */ int chkwin(n,bottom,slots) int n, bottom, slots; { int top, prev; debug(F101,"chkwin packet","",n); debug(F101,"chkwin winlo","",bottom); debug(F101,"chkwin slots","",slots); /* First do the easy and common cases, where the windows are not split. */ if (n < 0 || n > 63 || bottom < 0 || bottom > 63) return(-2); if (n == bottom) return(0); /* In a perfect world... */ top = bottom + slots; /* Calculate window top. */ if (top < 64 && n < top && n >= bottom) return(0); /* In current window. */ prev = bottom - slots; /* Bottom of previous window. */ if (prev > -1 && n < bottom && n > prev) return(1); /* In previous. */ /* Now consider the case where the current window is split. */ if (top > 63) { /* Wraparound... */ top -= 64; /* Get modulo-64 sequence number */ if (n < top || n >= bottom) { return(0); /* In current window. */ } else { /* Not in current window. */ if (n < bottom && n >= prev) /* Previous window can't be split. */ return(1); /* In previous window. */ else return(-1); /* Not in previous window. */ } } /* Now the case where current window not split, but previous window is. */ if (prev < 0) { /* Is previous window split? */ prev += 64; /* Yes. */ if (n < bottom || n >= prev) return(1); /* In previous window. */ } else { /* Previous window not split. */ if (n < bottom && n >= prev) return(1); /* In previous window. */ } /* It's not in the current window, and not in the previous window... */ return(-1); /* So it's not in any window. */ } int dumpsbuf() { /* Dump send-buffers */ #ifdef DEBUG int j, x, z; /* to debug log. */ if (! deblog) return(0); x = zsoutl(ZDFILE,"SEND BUFFERS:"); if (x < 0) { deblog = 0; return(0); } x=zsoutl(ZDFILE,"buffer inuse address length data type seq flag retries"); if (x < 0) { deblog = 0; return(0); } for (j = 0; j < wslots; j++) { if (!sbufuse[j]) continue; z = ((unsigned long)(s_pkt[j].bf_adr)) & 0xffff; sprintf(xbuf, /* safe (200) */ "%4d%6d%10d%5d%6d%4c%5d%6d\n", j, sbufuse[j], /* Avoid warnings when addresses are bigger than ints */ z, s_pkt[j].bf_len, s_pkt[j].pk_len, s_pkt[j].pk_typ, s_pkt[j].pk_seq, s_pkt[j].pk_rtr ); if (zsout(ZDFILE,xbuf) < 0) { deblog = 0; return(0); } if (s_pkt[j].pk_adr) { x = (int)strlen((char *) s_pkt[j].pk_adr); if (x) sprintf(xbuf, /* safe (checked) */ "[%.72s%s]\n",s_pkt[j].pk_adr, x > 72 ? "..." : ""); else sprintf(xbuf,"[(empty string)]\n"); /* safe (200) */ } else { sprintf(xbuf,"[(null pointer)]\n"); /* safe (200) */ } if (zsout(ZDFILE,xbuf) < 0) { deblog = 0; return(0); } } sprintf(xbuf,"free: %d, winlo: %d\n", sbufnum, winlo); /* safe (200) */ if (zsout(ZDFILE,xbuf) < 0) { deblog = 0; return(0); } #endif /* DEBUG */ return(0); } int dumprbuf() { /* Dump receive-buffers */ #ifdef DEBUG int j, x, z; if (! deblog) return(0); if (zsoutl(ZDFILE,"RECEIVE BUFFERS:") < 0) { deblog = 0; return(0); } x=zsoutl(ZDFILE,"buffer inuse address length data type seq flag retries"); if (x < 0) { deblog = 0; return(0); } for ( j = 0; j < wslots; j++ ) { if (!rbufuse[j]) continue; z = ((unsigned long)(r_pkt[j].bf_adr)) & 0xffff; sprintf(xbuf, /* 200, safe */ "%4d%6d%10d%5d%6d%4c%5d%6d\n", j, rbufuse[j], /* Avoid warnings when addresses are bigger than ints */ z, r_pkt[j].bf_len, r_pkt[j].pk_len, r_pkt[j].pk_typ, r_pkt[j].pk_seq, r_pkt[j].pk_rtr ); if (zsout(ZDFILE,xbuf) < 0) { deblog = 0; return(0); } x = (int)strlen((char *)r_pkt[j].bf_adr); sprintf(xbuf, /* safe (checked) */ "[%.72s%s]\n",r_pkt[j].bf_adr, x > 72 ? "..." : ""); if (zsout(ZDFILE,xbuf) < 0) { deblog = 0; return(0); } } sprintf(xbuf,"free: %d, winlo: %d\n", rbufnum, winlo); /* safe (200) */ if (zsout(ZDFILE,xbuf) < 0) { deblog = 0; return(0); } #endif /* DEBUG */ return(0); } /* S A T T R -- Send an Attribute Packet */ /* Sends attribute packet(s) for the current file. If the info will not fit into one packet, it can be called repeatedly until all the fields that will fit are sent. Call with: xp == 0 if we're sending a real file (F packet), or: xp != 0 for screen data (X packet). And: flag == 1 for first A packet flag == 0 for subsequent A packets. Returns: 1 or greater if an A packet was sent, or: 0 if an S-packet was not sent because there was no data to send or there was no data left that was short enough to send, or: -1 on any kind of error. */ /* (don't) #define TSOFORMAT */ /* which was only for making C-Kermit send TSO-Kermit-like A packets */ /* to try to track down a problem somebody reported... */ int sattr(xp, flag) int xp, flag; { /* Send Attributes */ static int max; /* Maximum length for Attributes */ static short done[95]; /* Field-complete array */ static struct zattr x; /* File attribute struct */ static char xdate[24]; extern char * cksysid; /* Some extra flags are used because the "done" array is sparse */ int i, j, rc, aln, left = 0, numset = 0, xbin = 0; /* Workers */ int notafile = 0; char *tp, c; notafile = sndarray || pipesend || #ifdef PIPESEND sndfilter || #endif /* PIPESEND */ calibrate; debug(F101,"sattr flag","",flag); if (!flag) /* No more attributes to send */ if (done[xunchar('@')]) return(0); /* Initialize Attribute mechanism */ if (flag) { /* First time here for this file? */ initattr(&x); /* Blank out all the fields. */ for (j = 0; j < 95; j++) /* Init array of completed fields */ done[j] = 0; max = maxdata(); /* Get maximum data field length */ if (notafile || xp == 1) { /* Is it not a real file? */ extern char * zzndate(); char * p; int i; #ifdef CALIBRATE if (calibrate) { /* Calibration run... */ x.lengthk = calibrate / 1024L; /* We know the length */ x.length = calibrate; } #endif /* CALIBRATE */ x.systemid.val = cksysid; /* System ID */ x.systemid.len = (int)strlen(cksysid); ckstrncpy(xdate,zzndate(),24); xdate[8] = SP; ztime(&p); for (i = 11; i < 19; i++) /* copy hh:mm:ss */ xdate[i - 2] = p[i]; /* to xdate */ xdate[17] = NUL; /* terminate */ x.date.val = xdate; x.date.len = 17; debug(F111,"sattr notafile date",x.date.val,x.date.len); } else { /* Real file */ rc = zsattr(&x); /* Get attributes for this file */ debug(F101,"sattr zsattr","",rc); if (rc < 0) /* Can't get 'em so don't send 'em */ return(0); debug(F101,"sattr init max","",max); } } if (nxtpkt() < 0) /* Got 'em, get next packet number */ return(-1); /* Bad news if we can't */ i = 0; /* Init data field character number */ /* Do each attribute using first-fit method, marking as we go */ /* This is rather long and repititious - could be done more cleverly */ if (atsido && !done[xunchar(c = '.')]) { /* System type */ if (max - i >= x.systemid.len + 2) { /* Enough space ? */ data[i++] = c; /* Yes, add parameter */ data[i++] = tochar(x.systemid.len); /* Add length */ for (j = 0; j < x.systemid.len; j++) /* Add data */ data[i++] = x.systemid.val[j]; numset++; /* Count that we did at least one */ done[xunchar(c)] = 1; /* Mark this attribute as done */ } else /* No */ left++; /* so mark this one left to do */ } #ifdef STRATUS if (atcreo && !done[xunchar(c = '$')]) { /* Creator */ if (max - i >= x.creator.len + 2) { /* Enough space ? */ data[i++] = c; data[i++] = tochar(x.creator.len); for (j = 0; j < x.creator.len; j++) data[i++] = x.creator.val[j]; numset++; done[xunchar(c)] = 1; } else left++; } if (atacto && !done[xunchar(c = '%')]) { /* File account */ if (max - i >= x.account.len + 2) { data[i++] = c; data[i++] = tochar(x.account.len); for (j = 0; j < x.account.len; j++) data[i++] = x.account.val[j]; numset++; done[xunchar(c)] = 1; } else left++; } if (atfrmo && !done[xunchar(c = '/')]) { /* Packet data format */ if (max - i >= x.recfm.len + 2) { data[i++] = c; data[i++] = tochar(x.recfm.len); /* Copy from attr structure */ for (j = 0; j < x.recfm.len; j++) data[i++] = x.recfm.val[j]; numset++; done[xunchar(c)] = 1; } else left++; } #endif /* STRATUS */ xbin = /* Is the transfer in binary mode? */ #ifdef VMS binary == XYFT_I || binary == XYFT_L || /* IMAGE or LABELED */ !strncmp(x.recfm.val,"F",1) /* or RECFM=Fxxxxxx */ #else binary /* User said SET FILE TYPE BINARY */ #endif /* VMS */ ; if (attypo && !done[xunchar(c = '"')]) { /* File type */ if (max - i >= 5) { /* Max length for this field */ data[i++] = c; if (xbin) { /* Binary */ data[i++] = tochar(2); /* Two characters */ data[i++] = 'B'; /* B for Binary */ data[i++] = '8'; /* 8-bit bytes (note assumption...) */ #ifdef CK_LABELED if (binary != XYFT_L #ifdef VMS && binary != XYFT_I #endif /* VMS */ ) binary = XYFT_B; #endif /* CK_LABELED */ } else { /* Text */ #ifdef TSOFORMAT data[i++] = tochar(1); /* One character */ data[i++] = 'A'; /* A = (extended) ASCII with CRLFs */ #else data[i++] = tochar(3); /* Three characters */ data[i++] = 'A'; /* A = (extended) ASCII with CRLFs */ data[i++] = 'M'; /* M for carriage return */ data[i++] = 'J'; /* J for linefeed */ #endif /* TSOFORMAT */ #ifdef VMS binary = XYFT_T; /* We automatically detected text */ #endif /* VMS */ } numset++; done[xunchar(c)] = 1; } else left++; } #ifdef TSOFORMAT if (attypo && !xbin && !done[xunchar(c = '/')]) { /* Record format */ if (max - i >= 5) { data[i++] = c; data[i++] = tochar(3); /* Three characters */ data[i++] = 'A'; /* A = variable with CRLFs */ data[i++] = 'M'; /* M for carriage return */ data[i++] = 'J'; /* J for linefeed */ } } #endif /* TSOFORMAT */ if (attypo && !xbin && !done[xunchar(c = '*')]) { /* Text encoding */ #ifdef NOCSETS if (max - i >= 3) { data[i++] = c; data[i++] = tochar(1); /* Length of value is 1 */ data[i++] = 'A'; /* A for ASCII */ numset++; done[xunchar(c)] = 1; } else left++; #else if (tcharset == TC_TRANSP || !xfrxla) { /* Transfer character set */ if (max - i >= 3) { data[i++] = c; /* Encoding */ data[i++] = tochar(1); /* Length of value is 1 */ data[i++] = 'A'; /* A for ASCII (i.e. text) */ numset++; done[xunchar(c)] = 1; } else left++; } else { tp = tcsinfo[tcharset].designator; if (!tp) tp = ""; aln = strlen(tp); if (aln > 0) { if (max - i >= aln + 2) { data[i++] = c; /* Encoding */ data[i++] = tochar(aln+1); /* Length of designator. */ data[i++] = 'C'; /* Text in specified charset. */ for (j = 0; j < aln; j++) /* Copy designator */ data[i++] = *tp++; /* Example: *&I6/100 */ numset++; done[xunchar(c)] = 1; } else left++; } else done[xunchar(c)] = 1; } #endif /* NOCSETS */ } if (atdato && !done[xunchar(c = '#')] && /* Creation date, if any */ (aln = x.date.len) > 0) { if (max - i >= aln + 2) { data[i++] = c; data[i++] = tochar(aln); for (j = 0; j < aln; j++) data[i++] = x.date.val[j]; numset++; done[xunchar(c)] = 1; } else left++; } /* File length in K */ if (atleno && !done[xunchar(c = '!')] && x.lengthk > (CK_OFF_T)-1) { #ifdef COMMENT sprintf((char *) &data[i+2],"%ld",x.lengthk); /* safe */ #else ckstrncpy((char *)&data[i+2],ckfstoa(x.lengthk),32); #endif /* COMMENT */ aln = (int)strlen((char *)(data+i+2)); if (max - i >= aln + 2) { data[i] = c; data[i+1] = tochar(aln); i += aln + 2; numset++; done[xunchar(c)] = 1; } else { data[i] = NUL; left++; } } /* File length in bytes */ if (atleno && !done[xunchar(c = '1')] && x.length > (CK_OFF_T)-1) { #ifdef COMMENT sprintf((char *) &data[i+2],"%ld",x.length); /* safe */ #else ckstrncpy((char *)&data[i+2],ckfstoa(x.length),32); #endif /* COMMENT */ aln = (int)strlen((char *)(data+i+2)); if (max - i >= aln + 2) { data[i] = c; data[i+1] = tochar(aln); i += aln + 2; numset++; done[xunchar(c)] = 1; } else { data[i] = NUL; left++; } } #ifdef CK_PERMS if (atlpro && !done[xunchar(c = ',')] && /* Local protection */ (aln = x.lprotect.len) > 0 && !notafile && xp == 0) { if (max - i >= aln + 2) { data[i++] = c; data[i++] = tochar(aln); for (j = 0; j < aln; j++) data[i++] = x.lprotect.val[j]; numset++; done[xunchar(c)] = 1; } else left++; } if (atgpro && !done[xunchar(c = '-')] && /* Generic protection */ (aln = x.gprotect.len) > 0 && !notafile && xp == 0) { if (max - i >= aln + 2) { data[i++] = c; data[i++] = tochar(aln); for (j = 0; j < aln; j++) data[i++] = x.gprotect.val[j]; numset++; done[xunchar(c)] = 1; } else left++; } #endif /* CK_PERMS */ if (atblko && fblksiz && !done[xunchar(c = '(')] && !notafile && xp == 0) { /* Blocksize */ sprintf((char *) &data[i+2],"%d",fblksiz); /* safe */ aln = (int)strlen((char *)(data+i+2)); if (max - i >= aln + 2) { data[i] = c; data[i+1] = tochar(aln); i += aln + 2; numset++; done[xunchar(c)] = 1; } else { data[i] = NUL; left++; } } #ifndef NOFRILLS if ((rprintf || rmailf) && atdiso && /* MAIL, or REMOTE PRINT? */ !done[xunchar(c = '+')]) { aln = (int) strlen(optbuf) + 1; /* Options, if any */ if (max - i >= aln + 2) { data[i++] = c; /* Disposition */ data[i++] = tochar(aln); /* Options, if any */ if (rprintf) data[i++] = 'P'; /* P for Print */ else data[i++] = 'M'; /* M for Mail */ for (j = 0; optbuf[j]; j++) /* Copy any options */ data[i++] = optbuf[j]; numset++; done[xunchar(c)] = 1; } else { data[i] = NUL; left++; } } #endif /* NOFRILLS */ #ifdef CK_RESEND if (sendmode == SM_RESEND && !done[xunchar(c = '+')]) { if (max - i >= 3) { data[i++] = c; /* Disposition */ data[i++] = tochar(1); data[i++] = 'R'; /* is RESEND */ numset++; done[xunchar(c)] = 1; } else left++; } #endif /* CK_RESEND */ /* End of Attributes -- to be sent only after sending all others */ debug(F111,"sattr","@",i); debug(F101,"sattr numset","",numset); debug(F101,"sattr left","",left); if ((left == 0 || numset == 0) && !done[xunchar(c = '@')]) { if (max - i >= 3) { data[i++] = c; /* End of Attributes */ data[i++] = SP; /* Length 0 */ data[i] = NUL; /* Make sure it's null-terminated */ numset++; done[xunchar(c)] = 1; } } /* Finished - send the packet off if we have anything in it */ if (numset) { data[i] = NUL; /* Terminate last good field */ debug(F111,"sattr sending",data,left); aln = (int)strlen((char *)data); /* Get overall length of attributes */ return(spack('A',pktnum,aln,data)); /* Send it */ } else return(0); } static char *refused = ""; static char *reason[] = { "size", "type", "date", "creator", "account", "area", "password", "blocksize", "access", "encoding", "disposition", "protection", "protection", "origin", "format", "sys-dependent", /* 0 */ "size", /* 1 */ "2", /* 2 */ "3", /* 3 */ "4", /* 4 */ "5", /* 5 */ "6", /* 6 */ "7", /* 7 */ "8", /* 8 */ "9", /* 9 */ ":", /* : */ ";", /* ; */ "<", /* < */ "=", /* = */ ">", /* > */ "name", /* ? */ "@" }; static int nreason = sizeof(reason) / sizeof(char *); int rejection = -1; char * getreason(s) char *s; { /* Decode attribute refusal reason */ char c, *p; if (rejection == 1) /* Kludge for SET FIL COLL DISCARD */ return("name"); /* when other Kermit doesn't... */ p = s; if (*p++ != 'N') return(""); /* Should start with N */ else if ((c = *p) > SP) { /* get reason, */ rejection = c; /* remember it, */ c -= '!'; /* get offset */ p = ((unsigned int) ((CHAR) c) <= (unsigned int) nreason) ? reason[c] : "unknown"; } return(p); } int rsattr(s) CHAR *s; { /* Read response to attribute packet */ debug(F111,"rsattr",s,*s); if (*s == 'N') { /* If it's 'N' followed by anything, */ refused = getreason((char *)s); /* they are refusing, get reason. */ debug(F110,"rsattr refused",refused,0); tlog(F110," refused:",refused,0L); return(-1); } #ifdef CK_RESEND if (sendmode == SM_RESEND && *s == '1') { /* RESEND length */ int n; CK_OFF_T z; CHAR *p; p = s + 1; n = xunchar(*p++); debug(F101,"rsattr RESEND n","",n); z = (CK_OFF_T)0; while (n-- > 0) /* We assume the format is good. */ z = (CK_OFF_T)10 * z + (CK_OFF_T)(*p++ - '0'); debug(F101,"rsattr RESEND z","",z); if (z > (CK_OFF_T)0) sendstart = z; debug(F101,"rsattr RESEND sendstart","",sendstart); if (sendstart > (CK_OFF_T)0) if (zfseek(sendstart) < 0) /* Input file is already open. */ return(0); #ifdef CK_CURSES if (fdispla == XYFD_C) xxscreen(SCR_FS,0,fsize,""); /* Refresh file transfer display */ #endif /* CK_CURSES */ } #endif /* CK_RESEND */ refused = ""; return(0); } /* Get attributes from incoming A packet. Returns: 0 on success, file is to be accepted -1 on failure, file is to be refused */ int gattr(s, yy) CHAR *s; struct zattr *yy; { /* Read incoming attribute packet */ char c, d; char *ff; int aln, i, overflow = 0; #ifndef NOCSETS extern int r_cset, axcset[]; #endif /* NOCSETS */ #define ABUFL 40 /* Temporary buffer for conversions */ char abuf[ABUFL+1]; #define RFBUFL 10 /* Record-format buffer */ static char rfbuf[RFBUFL+1]; #define FTBUFL 10 /* File type buffer */ static char ftbuf[FTBUFL+1]; #define DTBUFL 40 /* File creation date */ static char dtbuf[DTBUFL+1]; #define TSBUFL 10 /* Transfer syntax */ static char tsbuf[TSBUFL+1]; #define IDBUFL 10 /* System ID */ static char idbuf[IDBUFL+1]; #ifndef DYNAMIC #define DSBUFL 100 /* Disposition */ static char dsbuf[DSBUFL+1]; #define SPBUFL 512 /* System-dependent parameters */ static char spbuf[SPBUFL+1]; #else #define DSBUFL 100 /* Disposition */ static char *dsbuf = NULL; #define SPBUFL 512 /* System-dependent parameters */ static char *spbuf = NULL; #endif /* DYNAMIC */ #define RPBUFL 20 /* Attribute reply */ static char rpbuf[RPBUFL+1]; #ifdef CK_PERMS static char lprmbuf[CK_PERMLEN+1]; static char gprmbuf[2]; #endif /* CK_PERMS */ char *rp; /* Pointer to reply buffer */ int retcode; /* Return code */ d = SP; /* Initialize disposition */ ff = filnam; /* Filename returned by rcvfil */ if (fncact == XYFX_R && ofn1x && ofn1[0]) /* But watch out for FC=RENAME */ ff = ofn1; /* because we haven't renamed it yet */ /* Fill in the attributes we have received */ rp = rpbuf; /* Initialize reply buffer */ *rp++ = 'N'; /* for negative reply. */ *rp = NUL; retcode = 0; /* Initialize return code. */ if (dest == DEST_P) { /* SET DESTINATION PRINTER */ #ifdef DYNAMIC if (!dsbuf) if ((dsbuf = malloc(DSBUFL+1)) == NULL) fatal("gtattr: no memory for dsbuf"); #endif /* DYNAMIC */ dsbuf[0] = 'P'; dsbuf[1] = '\0'; yy->disp.val = dsbuf; yy->disp.len = 1; } while (c = *s++) { /* Get attribute tag */ aln = xunchar(*s++); /* Length of attribute string */ switch (c) { #ifdef COMMENT /* This case combined with '1' below */ case '!': /* File length in K */ for (i = 0; (i < aln) && (i < ABUFL); i++) /* Copy it */ abuf[i] = *s++; abuf[i] = '\0'; /* Terminate with null */ if (i < aln) s += (aln - i); /* If field was too long for buffer */ yy->lengthk = ckatofs(abuf); /* Convert to number */ break; #endif /* COMMENT */ case '/': /* Record format */ rfbuf[1] = NUL; rfbuf[2] = NUL; for (i = 0; (i < aln) && (i < RFBUFL); i++) /* Copy it */ rfbuf[i] = *s++; rfbuf[i] = NUL; /* Terminate with null */ yy->recfm.val = rfbuf; /* Pointer to string */ yy->recfm.len = i; /* Length of string */ if ((rfbuf[0] != 'A') || (rfbuf[1] && rfbuf[1] != 'M') || (rfbuf[2] && rfbuf[2] != 'J')) { debug(F110,"gattr bad recfm",rfbuf,0); *rp++ = c; retcode = -1; } break; 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); /* TYPE attribute is enabled? */ if (attypi) { yy->type.val = ftbuf; /* Pointer to string */ yy->type.len = i; /* Length of string */ debug(F111,"gattr file type", ftbuf, i); debug(F101,"gattr binary 1","",binary); /* Unknown type? */ if ((*ftbuf != 'A' && *ftbuf != 'B' && *ftbuf != 'I') #ifdef CK_LABELED /* ... Or our FILE TYPE is LABELED and the incoming file is text... */ || (binary == XYFT_L && *ftbuf == 'A' && !xflg) #endif /* CK_LABELED */ ) { retcode = -1; /* Reject the file */ *rp++ = c; if (!opnerr) tlog(F100," refused: type","",0); break; } /* The following code moved here from opena() so we set binary mode as soon as requested by the attribute packet. That way when the first data packet comes, the mode of transfer can be displayed correctly before opena() is called. */ if (yy->type.val[0] == 'A') { /* Check received attributes. */ #ifdef VMS if (binary != XYFT_I) /* VMS IMAGE overrides this */ #endif /* VMS */ binary = XYFT_T; /* Set current type to Text. */ debug(F101,"gattr binary 2","",binary); } else if (yy->type.val[0] == 'B') { #ifdef CK_LABELED if (binary != XYFT_L #ifdef VMS && binary != XYFT_U /* VMS special case */ #endif /* VMS */ ) #endif /* CK_LABELED */ #ifdef MAC if (binary != XYFT_M) /* If not MacBinary... */ #endif /* MAC */ binary = XYFT_B; debug(F101,"gattr binary 3","",binary); } } break; case '#': /* File creation date */ for (i = 0; (i < aln) && (i < DTBUFL); i++) dtbuf[i] = *s++; /* Copy it into a static string */ if (i < aln) s += (aln - i); dtbuf[i] = '\0'; if (atdati && !xflg) { /* Real file and dates enabled */ yy->date.val = dtbuf; /* Pointer to string */ yy->date.len = i; /* Length of string */ if (fncact == XYFX_U) { /* Receiving in update mode? */ if (zstime(ff,yy,1) > 0) { /* Compare dates */ *rp++ = c; /* Discard if older, reason = date. */ if (!opnerr) tlog(F100," refused: date","",0); retcode = -1; /* Rejection notice. */ } } } break; case '(': /* File Block Size */ for (i = 0; (i < aln) && (i < ABUFL); i++) /* Copy it */ abuf[i] = *s++; abuf[i] = '\0'; /* Terminate with null */ if (i < aln) s += (aln - i); if (atblki) yy->blksize = atol(abuf); /* Convert to number */ break; case '*': /* Encoding (transfer syntax) */ for (i = 0; (i < aln) && (i < TSBUFL); i++) tsbuf[i] = *s++; /* Copy it into a static string */ if (i < aln) s += (aln - i); tsbuf[i] = '\0'; #ifndef NOCSETS xlatype = XLA_NONE; /* Assume no translation */ #endif /* NOCSETS */ if (atenci) { char * ss; yy->encoding.val = tsbuf; /* Pointer to string */ yy->encoding.len = i; /* Length of string */ debug(F101,"gattr encoding",tsbuf,i); ss = tsbuf+1; switch (*tsbuf) { #ifndef NOCSETS case 'A': /* Normal, nothing special */ tcharset = TC_TRANSP; /* Transparent chars untranslated */ debug(F110,"gattr sets tcharset TC_TRANSP","A",0); break; case 'C': /* Specified character set */ if (!xfrxla) { /* But translation disabled */ tcharset = TC_TRANSP; debug(F110,"gattr sets tcharset TC_TRANSP","C",0); break; } #ifdef UNICODE if (!strcmp("I196",ss)) /* Treat I196 (UTF-8 no level) */ ss = "I190"; /* as I190 (UTF-8 Level 1) */ #endif /* UNICODE */ if (!strcmp("I6/204",ss)) /* Treat "Latin-1 + Euro" */ ss = "I6/100"; /* as I6/100 (regular Latin-1) */ for (i = 0; i < ntcsets; i++) { if (!strcmp(tcsinfo[i].designator,ss)) break; } debug(F101,"gattr xfer charset lookup","",i); if (i == ntcsets) { /* If unknown character set, */ debug(F110,"gattr: xfer charset unknown",ss,0); if (!unkcs) { /* and SET UNKNOWN DISCARD, */ retcode = -1; /* reject the file. */ *rp++ = c; if (!opnerr) tlog(F100," refused: character set","",0); } } else { tcharset = tcsinfo[i].code; /* it's known, use it */ debug(F101,"gattr switch tcharset","",tcharset); debug(F101,"gattr fcharset","",fcharset); if (r_cset == XMODE_A) { /* Automatic switching? */ if (tcharset > -1 && tcharset <= MAXTCSETS) { int x; x = axcset[tcharset]; if (x > 0 && x <= MAXFCSETS) { fcharset = x; debug(F101,"gattr switch fcharset","",x); } } } /* Set up translation type and function */ setxlatype(tcharset,fcharset); } break; #endif /* NOCSETS */ default: /* Something else. */ debug(F110,"gattr unk encoding attribute",tsbuf,0); if (!unkcs) { /* If SET UNK DISC */ retcode = -1; *rp++ = c; if (!opnerr) tlog(F100," refused: encoding","",0); } break; } } break; case '+': /* Disposition */ #ifdef DYNAMIC if (!dsbuf) if ((dsbuf = malloc(DSBUFL+1)) == NULL) fatal("gtattr: no memory for dsbuf"); #endif /* DYNAMIC */ for (i = 0; (i < aln) && (i < DSBUFL); i++) dsbuf[i] = *s++; /* Copy it into a separate string */ dsbuf[i] = '\0'; if (i < aln) s += (aln - i); rs_len = (CK_OFF_T)0; if (atdisi) { /* We are doing this attribute */ /* Copy it into the attribute structure */ yy->disp.val = dsbuf; /* Pointer to string */ yy->disp.len = i; /* Length of string */ d = *dsbuf; #ifndef NODISPO /* Define NODISPO to disable receipt of mail or print files and of RESEND. */ if ( #ifndef datageneral /* MAIL supported only for */ #ifndef OS2 /* UNIX, VMS, and OS-9 */ #ifndef MAC #ifndef GEMDOS #ifndef AMIGA d != 'M' && /* MAIL */ #endif /* AMIGA */ #endif /* GEMDOS */ #endif /* MAC */ #endif /* OS/2 */ #endif /* datageneral */ #ifdef CK_RESEND d != 'R' && /* RESEND */ #endif /* CK_RESEND */ d != 'P') { /* PRINT */ retcode = -1; /* Unknown/unsupported disposition */ *rp++ = c; if (!opnerr) tlog(F101," refused: bad disposition","",d); } dispos = d; debug(F000,"gattr dispos","",dispos); switch (d) { #ifndef NOFRILLS case 'M': if (!en_mai) { retcode = -1; *rp++ = c; if (!opnerr) tlog(F100," refused: mail disabled","",0); dispos = 0; } break; #endif /* NOFRILLS */ case 'P': if (!en_pri) { retcode = -1; *rp++ = c; if (!opnerr) tlog(F100," refused: print disabled","",0); dispos = 0; } break; case 'R': dispos = 0; #ifdef CK_RESEND rs_len = zgetfs(ff); /* Get length of file */ debug(F111,"gattr RESEND",ff,rs_len); #ifdef VMS rs_len &= (long) -512; /* Ensure block boundary if VMS */ rs_len -= 512; /* In case last block not complete */ debug(F111,"gattr rs_len",ff,rs_len); #endif /* VMS */ #ifdef COMMENT if (rs_len < 0L) /* Local file doesn't exist */ rs_len = 0L; #endif /* COMMENT */ /* Another possibility here (or later, really) would be to check if the two file lengths are the same, and if so, keep the prevailing collision action as is (note: rs_len == length of existing file; yy->length == fsize == length of incoming file). This could be complicated, though, since (a) we might not have received the length attribute yet, and in fact it might even be in a subsequent A-packet, yet (b) we have to accept or reject the Recover attribute now. So better to leave as-is. Anyway, it's probably more useful this way. */ if (rs_len > (CK_OFF_T)0) { fncsav = fncact; /* Save collision action */ fncact = XYFX_A; /* Switch to APPEND */ } #else retcode = -1; /* This shouldn't happen */ *rp++ = c; /* 'cause it wasn't negotiated. */ if (!opnerr) tlog(F100," refused: resend","",0); #endif /* CK_RESEND */ } #else /* NODISPO */ retcode = -1; *rp++ = c; if (!opnerr) tlog(F100," refused: NODISPO","",0); #endif /* NODISPO */ } break; case '.': /* Sender's system ID */ for (i = 0; (i < aln) && (i < IDBUFL); i++) idbuf[i] = *s++; /* Copy it into a static string */ idbuf[i] = '\0'; if (i < aln) s += (aln - i); if (atsidi) { yy->systemid.val = idbuf; /* Pointer to string */ yy->systemid.len = i; /* Length of string */ } break; case '0': /* System-dependent parameters */ #ifdef DYNAMIC if (!spbuf && !(spbuf = malloc(SPBUFL))) fatal("gattr: no memory for spbuf"); #endif /* DYNAMIC */ for (i = 0; (i < aln) && (i < SPBUFL); i++) spbuf[i] = *s++; /* Copy it into a static string */ spbuf[i] = '\0'; if (i < aln) s += (aln - i); if (atsysi) { yy->sysparam.val = spbuf; /* Pointer to string */ yy->sysparam.len = i; /* Length of string */ } break; case '!': /* File length in K */ case '1': { /* File length in bytes */ char * l2; CK_OFF_T xlen; for (i = 0; (i < aln) && (i < ABUFL); i++) /* Copy it */ abuf[i] = *s++; abuf[i] = '\0'; /* Terminate with null */ if (i < aln) s += (aln - i); if (rdigits(abuf)) { /* Make sure string is all digits */ xlen = ckatofs(abuf); /* Convert to number */ l2 = ckfstoa(xlen); /* Convert number back to string */ if (c == '1') debug(F111,"gattr length",abuf,xlen); else debug(F111,"gattr lengthk",abuf,xlen); if (ckstrcmp(abuf,l2,-1,1)) { /* This is how we check... */ xlen = (CK_OFF_T)-2; /* -2 = unk, possibly too long */ overflow++; debug(F111,"gattr overflow", (c == '1') ? "length" : "lengthk", xlen); } if (c == '1') { yy->length = xlen; debug(F101,"gattr length","",xlen); } else { yy->lengthk = xlen; debug(F101,"gattr lengthk","",xlen); } } /* If the length field is not numeric accept the file */ /* anyway but with an unknown length */ break; } #ifdef CK_PERMS case ',': /* System-dependent protection code */ for (i = 0; (i < aln) && (i < CK_PERMLEN); i++) lprmbuf[i] = *s++; /* Just copy it - decode later */ lprmbuf[i] = '\0'; /* Terminate with null */ if (i < aln) s += (aln - i); if (atlpri) { yy->lprotect.val = (char *)lprmbuf; yy->lprotect.len = i; } else lprmbuf[0] = NUL; break; case '-': /* Generic "world" protection code */ gprmbuf[0] = NUL; /* Just 1 byte by definition */ for (i = 0; i < aln; i++) /* But allow for more... */ if (i == 0) gprmbuf[0] = *s++; gprmbuf[1] = NUL; if (atgpri) { yy->gprotect.val = (char *)gprmbuf; yy->gprotect.len = gprmbuf[0] ? 1 : 0; } else gprmbuf[0] = NUL; break; #endif /* CK_PERMS */ default: /* Unknown attribute */ s += aln; /* Just skip past it */ break; } } /* Check space now, because we also need to know the file type */ /* in case zchkspa() differentiates text and binary (VMS version does) */ if (atleni && !calibrate) { /* Length attribute enabled? */ if (yy->length > (CK_OFF_T)-1) { /* Length-in-bytes attribute rec'd? */ if (!zchkspa(ff,(yy->length))) { /* Check space */ retcode = -1; /* Not enuf */ *rp++ = '1'; if (!opnerr) tlog(F100," refused: length bytes","",0); } } else if (yy->lengthk > (CK_OFF_T)-1) { /* Length in K received? */ long xlen; xlen = yy->lengthk * 1024; if (!zchkspa(ff,xlen)) { retcode = -1; /* Check space */ *rp++ = '!'; if (!opnerr) tlog(F100," refused: length K","",0); } } } if (retcode > -1L) { /* Remember the file size */ if (yy->length > (CK_OFF_T)-1) { fsize = yy->length; } else if (yy->lengthk > (CK_OFF_T)-1 && !overflow) { fsize = yy->lengthk * 1024L; } else fsize = yy->length; /* (e.g. -2L) */ } #ifdef DEBUG if (deblog) { #ifdef COMMENT sprintf(abuf,"%ld",fsize); /* safe */ #else ckstrncpy(abuf,ckfstoa(fsize),ABUFL); #endif /* COMMENT */ debug(F110,"gattr fsize",abuf,0); } #endif /* DEBUG */ if (retcode == 0) rp = rpbuf; /* Null reply string if accepted */ *rp = '\0'; /* End of reply string */ #ifdef CK_RESEND if (d == 'R') { /* Receiving a RESEND? */ debug(F101,"gattr RESEND","",retcode); /* We ignore retcodes because this overrides */ if (binary != XYFT_B) { /* Reject if not binary */ retcode = -1; /* in case type field came */ ckstrncpy(rpbuf,"N+",RPBUFL); /* after the disposition field */ debug(F111,"gattr RESEND not binary",rpbuf,binary); } else { /* Binary mode */ retcode = 0; /* Accept the file */ discard = 0; /* If SET FILE COLLISION DISCARD */ #ifdef COMMENT sprintf(rpbuf+2,"%ld",rs_len); /* Reply with length of file */ #else ckstrncpy(rpbuf+2,ckfstoa(rs_len),RPBUFL-2); #endif /* COMMENT */ rpbuf[0] = '1'; /* '1' means Length in Bytes */ rpbuf[1] = tochar((int)strlen(rpbuf+2)); /* Length of length */ debug(F111,"gattr RESEND OK",rpbuf,retcode); } } #endif /* CK_RESEND */ if (retcode == 0 && discard != 0) { /* Do we still have a discard flag? */ ckstrncpy(rpbuf,"N?",RPBUFL); /* Yes, must be filename collision */ retcode = -1; /* "?" = name (reply-only code) */ } yy->reply.val = rpbuf; /* Add it to attribute structure */ yy->reply.len = (int)strlen(rpbuf); if (retcode < 0) { /* If we are rejecting */ discard = 1; /* remember to discard the file */ rejection = rpbuf[1]; /* and use the first reason given. */ if (fncsav != -1) { fncact = fncsav; fncsav = -1; } } debug(F111,"gattr return",rpbuf,retcode); return(retcode); } /* I N I T A T T R -- Initialize file attribute structure */ int initattr(yy) struct zattr *yy; { yy->lengthk = yy->length = (CK_OFF_T)-1; yy->type.val = ""; yy->type.len = 0; yy->date.val = ""; yy->date.len = 0; yy->encoding.val = ""; yy->encoding.len = 0; yy->disp.val = ""; yy->disp.len = 0; yy->systemid.val = ""; yy->systemid.len = 0; yy->sysparam.val = ""; yy->sysparam.len = 0; yy->creator.val = ""; yy->creator.len = 0; yy->account.val = ""; yy->account.len = 0; yy->area.val = ""; yy->area.len = 0; yy->password.val = ""; yy->password.len = 0; yy->blksize = -1L; yy->xaccess.val = ""; yy->xaccess.len = 0; #ifdef CK_PERMS if (!ofperms) ofperms = ""; debug(F110,"initattr ofperms",ofperms,0); yy->lprotect.val = ofperms; yy->lprotect.len = 0 - strlen(ofperms); /* <-- NOTE! */ /* A negative length indicates that we have a permissions string but it has been inherited from a previously existing file rather than picked up from an incoming A-packet. */ #else yy->lprotect.val = ""; yy->lprotect.len = 0; #endif /* CK_PERMS */ yy->gprotect.val = ""; yy->gprotect.len = 0; yy->recfm.val = ""; yy->recfm.len = 0; yy->reply.val = ""; yy->reply.len = 0; #ifdef OS2 yy->longname.len = 0 ; yy->longname.val = "" ; #endif /* OS2 */ return(0); } /* A D E B U -- Write attribute packet info to debug log */ int adebu(f,zz) char *f; struct zattr *zz; { #ifdef DEBUG if (deblog == 0) return(0); debug(F110,"Attributes for incoming file ",f,0); debug(F101," length in K","",(int) zz->lengthk); debug(F111," file type",zz->type.val,zz->type.len); debug(F111," creation date",zz->date.val,zz->date.len); debug(F111," creator",zz->creator.val,zz->creator.len); debug(F111," account",zz->account.val,zz->account.len); debug(F111," area",zz->area.val,zz->area.len); debug(F111," password",zz->password.val,zz->password.len); debug(F101," blksize","",(int) zz->blksize); debug(F111," access",zz->xaccess.val,zz->xaccess.len); debug(F111," encoding",zz->encoding.val,zz->encoding.len); debug(F111," disposition",zz->disp.val,zz->disp.len); debug(F111," lprotection",zz->lprotect.val,zz->lprotect.len); debug(F111," gprotection",zz->gprotect.val,zz->gprotect.len); debug(F111," systemid",zz->systemid.val,zz->systemid.len); debug(F111," recfm",zz->recfm.val,zz->recfm.len); debug(F111," sysparam",zz->sysparam.val,zz->sysparam.len); debug(F101," length","",(int) zz->length); debug(F110," reply",zz->reply.val,0); #endif /* DEBUG */ return(0); } /* O P E N A -- Open a file, with attributes. */ /* This function tries to open a new file to put the arriving data in. The filename is the one in the srvcmd buffer. File collision actions are: OVERWRITE (the existing file is overwritten), RENAME (the new file is renamed), BACKUP (the existing file is renamed), DISCARD (the new file is refused), UPDATE (the incoming file replaces the existing file only if the incoming file has a newer creation date). Returns 0 on failure, nonzero on success. */ extern char *rf_err; int opena(f,zz) char *f; struct zattr *zz; { int x, dispos = 0; static struct filinfo fcb; /* Must be static! */ debug(F110,"opena f",f,0); debug(F101,"opena discard","",discard); adebu(f,zz); /* Write attributes to debug log */ ffc = (CK_OFF_T)0; /* Init file-character counter */ #ifdef PIPESEND if (pipesend) /* Receiving to a pipe - easy. */ return(openo(f,zz,&fcb)); /* Just open the pipe. */ #endif /* PIPESEND */ /* Receiving to a file - set up file control structure */ fcb.bs = fblksiz; /* Blocksize */ #ifndef NOCSETS fcb.cs = fcharset; /* Character set */ #else fcb.cs = 0; /* Character set */ #endif /* NOCSETS */ fcb.rl = frecl; /* Record Length */ fcb.fmt = frecfm; /* Record Format */ fcb.org = forg; /* Organization */ fcb.cc = fcctrl; /* Carriage control */ fcb.typ = binary; /* Type */ debug(F101,"opena xflg","",xflg); debug(F101,"opena remfile","",remfile); debug(F101,"opena remappd","",remappd); if (xflg && remfile && remappd) /* REMOTE output redirected with >> */ fcb.dsp = XYFZ_A; else fcb.dsp = (fncact == XYFX_A) ? XYFZ_A : XYFZ_N; /* Disposition */ debug(F101,"opena disp","",fcb.dsp); fcb.os_specific = ""; /* OS-specific info */ #ifdef CK_LABELED fcb.lblopts = lf_opts; /* Labeled file options */ #else fcb.lblopts = 0; #endif /* CK_LABELED */ if (zz->disp.len > 0) { /* Incoming file has a disposition? */ debug(F111,"open disposition",zz->disp.val,zz->disp.len); dispos = (int) (*(zz->disp.val)); } if (!dispos && xflg && remfile && remappd) /* REMOTE redirect append ? */ dispos = fcb.dsp; debug(F101,"opena dispos","",dispos); if (!dispos) { /* No special disposition? */ if (fncact == XYFX_B && ofn1x && ofn2) { /* File collision = BACKUP? */ if (zrename(ofn1,ofn2) < 0) { /* Rename existing file. */ debug(F110,"opena rename fails",ofn1,0); rf_err = "Can't create backup file"; return(0); } else debug(F110,"opena rename ok",ofn2,0); } } else if (dispos == 'R') { /* Receiving a RESEND */ debug(F101,"opena remote len","",zz->length); debug(F101,"opena local len","",rs_len); #ifdef COMMENT if (fncact == XYFX_R) /* and file collision = RENAME */ if (ofn1x) #endif /* COMMENT */ if (ofn1[0]) f = ofn1; /* use original name. */ if (fncact == XYFX_R) /* if file collision is RENAME */ ckstrncpy(filnam,ofn1,CKMAXPATH+1); /* restore the real name */ xxscreen(SCR_AN,0,0L,f); /* update name on screen */ if (zz->length == rs_len) /* Local and remote lengths equal? */ return(-17); /* Secret code */ } debug(F111,"opena [file]=mode: ",f,fcb.dsp); if (x = openo(f,zz,&fcb)) { /* Try to open the file. */ #ifdef pdp11 tlog(F110," local name:",f,0L); /* OK, open, record local name. */ makestr(&prfspec,f); /* New preliminary name */ #else #ifndef ZFNQFP tlog(F110," local name:",f,0L); makestr(&prfspec,f); #else { /* Log full local pathname */ char *p = NULL, *q = f; if ((p = malloc(CKMAXPATH+1))) if (zfnqfp(filnam, CKMAXPATH, p)) q = p; tlog(F110," local name:",q,0L); makestr(&prfspec,q); if (p) free(p); } #endif /* ZFNQFP */ #endif /* pdp11 */ if (binary) { /* Log file mode in transaction log */ tlog(F101," mode: binary","",(long) binary); } else { /* If text mode, check character set */ tlog(F100," mode: text","",0L); #ifndef NOCSETS if (xfrxla) { if (fcharset > -1 && fcharset <= MAXFCSETS) tlog(F110," file character-set:",fcsinfo[fcharset].name,0L); if (tcharset > -1 && tcharset <= MAXTCSETS) tlog(F110," xfer character-set:",tcsinfo[tcharset].name,0L); } else { tlog(F110," character-set:","transparent",0L); } #endif /* NOCSETS */ debug(F111,"opena charset",zz->encoding.val,zz->encoding.len); } debug(F101,"opena binary","",binary); #ifdef COMMENT if (fsize >= 0) #endif /* COMMENT */ xxscreen(SCR_FS,0,fsize,""); #ifdef datageneral /* Need to turn on multi-tasking console interrupt task here, since multiple files may be received (huh?) ... */ if ((local) && (!quiet)) /* Only do this if local & not quiet */ consta_mt(); /* Start the async read task */ #endif /* datageneral */ } else { /* Did not open file OK. */ rf_err = ck_errstr(); /* Get system error message */ if (*rf_err) xxscreen(SCR_EM,0,0l,rf_err); else xxscreen(SCR_EM,0,0l,"Can't open output file"); tlog(F110,"Failure to open",f,0L); tlog(F110,"Error:",rf_err,0L); debug(F110,"opena error",rf_err,0); } return(x); /* Pass on return code from openo */ } /* O P E N C -- Open a command (in place of a file) for output */ int openc(n,s) int n; char * s; { int x; #ifndef NOPUSH x = zxcmd(n,s); #else x = 0; #endif /* NOPUSH */ debug(F111,"openc zxcmd",s,x); o_isopen = (x > 0) ? 1 : 0; return(x); } /* C A N N E D -- Check if current file transfer cancelled */ int canned(buf) CHAR *buf; { extern int interrupted; if (*buf == 'X') cxseen = 1; if (*buf == 'Z') czseen = 1; if (czseen || cxseen) interrupted = 1; debug(F101,"canned: cxseen","",cxseen); debug(F101," czseen","",czseen); return((czseen || cxseen) ? 1 : 0); } /* O P E N I -- Open an existing file for input */ int openi(name) char *name; { #ifndef NOSERVER extern int fromgetpath; #endif /* NOSERVER */ int x, filno; char *name2; extern CHAR *epktmsg; epktmsg[0] = NUL; /* Initialize error message */ if (memstr || sndarray) { /* Just return if "file" is memory. */ i_isopen = 1; return(1); } debug(F110,"openi name",name,0); debug(F101,"openi sndsrc","",sndsrc); filno = (sndsrc == 0) ? ZSTDIO : ZIFILE; /* ... */ debug(F101,"openi file number","",filno); #ifndef NOSERVER /* If I'm a server and CWD is disabled and name is not from GET-PATH... */ if (server && !en_cwd && !fromgetpath) { zstrip(name,&name2); if ( /* ... check if pathname included. */ #ifdef VMS zchkpath(name) #else strcmp(name,name2) #endif /* VMS */ ) { tlog(F110,name,"access denied",0L); debug(F110,"openi CD disabled",name,0); ckstrncpy((char *)epktmsg,"Access denied",PKTMSGLEN); return(0); } else name = name2; } #endif /* NOSERVER */ #ifdef PIPESEND debug(F101,"openi pipesend","",pipesend); if (pipesend) { int x; #ifndef NOPUSH x = zxcmd(ZIFILE,name); #else x = 0; #endif /* NOPUSH */ i_isopen = (x > 0) ? 1 : 0; if (!i_isopen) ckstrncpy((char *)epktmsg,"Command or pipe failure",PKTMSGLEN); debug(F111,"openi pipesend zxcmd",name,x); return(i_isopen); } #endif /* PIPESEND */ #ifdef CALIBRATE if (calibrate) { i_isopen = 1; return(1); } #endif /* CALIBRATE */ x = zopeni(filno,name); /* Otherwise, try to open it. */ debug(F111,"openi zopeni 1",name,x); if (x) { i_isopen = 1; return(1); } else { /* If not found, */ char xname[CKMAXPATH]; /* convert the name */ #ifdef NZLTOR nzrtol(name,xname,fncnv,fnrpath,CKMAXPATH); #else zrtol(name,xname); /* to local form and then */ #endif /* NZLTOR */ x = zopeni(filno,xname); /* try opening it again. */ debug(F111,"openi zopeni 2",xname,x); if (x) { i_isopen = 1; return(1); /* It worked. */ } else { char * s; s = ck_errstr(); if (s) if (!s) s = NULL; if (!s) s = "Can't open file"; ckstrncpy((char *)epktmsg,s,PKTMSGLEN); tlog(F110,xname,s,0L); debug(F110,"openi failed",xname,0); debug(F110,"openi message",s,0); i_isopen = 0; return(0); } } } /* O P E N O -- Open a new file for output. */ int openo(name,zz,fcb) char *name; struct zattr *zz; struct filinfo *fcb; { char *name2; #ifdef DTILDE char *dirp; #endif /* DTILDE */ int channel, x; if (stdouf) { /* Receiving to stdout? */ x = zopeno(ZSTDIO,"",zz,NULL); o_isopen = (x > 0); debug(F101,"openo stdouf zopeno","",x); return(x); } debug(F110,"openo: name",name,0); if (cxseen || czseen || discard) { /* If interrupted, get out before */ debug(F100," open cancelled","",0); /* destroying existing file. */ return(1); /* Pretend to succeed. */ } channel = ZOFILE; /* SET DESTINATION DISK or PRINTER */ #ifdef PIPESEND debug(F101,"openo pipesend","",pipesend); if (pipesend) { int x; #ifndef NOPUSH x = zxcmd(ZOFILE,(char *)srvcmd); #else x = 0; #endif /* NOPUSH */ o_isopen = x > 0; debug(F101,"openo zxcmd","",x); return(x); } #endif /* PIPESEND */ if (dest == DEST_S) { /* SET DEST SCREEN... */ channel = ZCTERM; fcb = NULL; } #ifdef DTILDE if (*name == '~') { dirp = tilde_expand(name); if (*dirp) ckstrncpy(name,dirp,CKMAXPATH+1); } #endif /* DTILDE */ if (server && !en_cwd) { /* If running as server */ zstrip(name,&name2); /* and CWD is disabled, */ if (strcmp(name,name2)) { /* check if pathname was included. */ tlog(F110,name,"authorization failure",0L); debug(F110,"openo CD disabled",name,0); return(0); } else name = name2; } if (zopeno(channel,name,zz,fcb) <= 0) { /* Try to open the file */ o_isopen = 0; debug(F110,"openo failed",name,0); /* tlog(F110,"Failure to open",name,0L); */ return(0); } else { o_isopen = 1; debug(F110,"openo ok, name",name,0); return(1); } } /* O P E N T -- Open the terminal for output, in place of a file */ int opent(zz) struct zattr *zz; { int x; ffc = tfc = (CK_OFF_T)0; x = zopeno(ZCTERM,"",zz,NULL); debug(F101,"opent zopeno","",x); if (x >= 0) { o_isopen = 1; binary = XYFT_T; } else return(0); return(x); } /* O P E N X -- Open nothing (incoming file to be accepted but ignored) */ int ckopenx(zz) struct zattr *zz; { ffc = tfc = (CK_OFF_T)0; /* Reset counters */ o_isopen = 1; debug(F101,"ckopenx fsize","",fsize); xxscreen(SCR_FS,0,fsize,""); /* Let screen display know the size */ return(1); } /* C L S I F -- Close the current input file. */ int clsif() { extern int xferstat, success; int x = 0; fcps(); /* Calculate CPS quickly */ #ifdef datageneral if ((local) && (!quiet)) /* Only do this if local & not quiet */ if (nfils < 1) /* More files to send ... leave it on! */ connoi_mt(); #endif /* datageneral */ debug(F101,"clsif i_isopen","",i_isopen); if (i_isopen) { /* If input file is open... */ if (memstr) { /* If input was memory string, */ memstr = 0; /* indicate no more. */ } else { x = zclose(ZIFILE); /* else close input file. */ } #ifdef DEBUG if (deblog) { debug(F101,"clsif zclose","",x); debug(F101,"clsif success","",success); debug(F101,"clsif xferstat","",xferstat); debug(F101,"clsif fsize","",fsize); debug(F101,"clsif ffc","",ffc); debug(F101,"clsif cxseen","",cxseen); debug(F101,"clsif czseen","",czseen); debug(F101,"clsif discard","",czseen); } #endif /* DEBUG */ if ((cxseen || czseen) && !epktsent) { /* If interrupted */ xxscreen(SCR_ST,ST_INT,0l,""); /* say so */ #ifdef TLOG if (tralog && !tlogfmt) doxlog(what,psfspec,fsize,binary,1,"Interrupted"); #endif /* TLOG */ } else if (discard && !epktsent) { /* If I'm refusing */ xxscreen(SCR_ST,ST_REFU,0l,refused); /* say why */ #ifdef TLOG if (tralog && !tlogfmt) { char buf[128]; ckmakmsg(buf,128,"Refused: ",refused,NULL,NULL); doxlog(what,psfspec,fsize,binary,1,buf); } #endif /* TLOG */ } else if (!epktrcvd && !epktsent && !cxseen && !czseen) { CK_OFF_T zz; zz = ffc; #ifdef CK_RESEND if (sendmode == SM_RESEND || sendmode == SM_PSEND) zz += sendstart; #endif /* CK_RESEND */ debug(F101,"clsif fstats","",zz); fstats(); /* Update statistics */ if ( /* Was the whole file sent? */ #ifdef VMS 0 /* Not a reliable check in VMS */ #else #ifdef STRATUS 0 /* Probably not for VOS either */ #else zz < fsize #ifdef CK_CTRLZ && ((eofmethod != XYEOF_Z && !binary) || binary) #endif /* CK_CTRLZ */ #endif /* STRATUS */ #endif /* VMS */ ) { xxscreen(SCR_ST,ST_INT,0l,""); #ifdef TLOG if (tralog && !tlogfmt) doxlog(what,psfspec,fsize,binary,1,"Incomplete"); #endif /* TLOG */ } else { #ifdef COMMENT /* Not yet -- we don't have confirmation from the receiver */ xxscreen(SCR_ST,ST_OK,0l,""); #endif /* COMMENT */ #ifdef TLOG if (tralog && !tlogfmt) doxlog(what,psfspec,fsize,binary,0,""); #endif /* TLOG */ } } } i_isopen = 0; hcflg = 0; /* Reset flags */ sendstart = (CK_OFF_T)0; /* Don't do this again! */ #ifdef COMMENT /* This prevents a subsequent call to clsof() from deleting the file when given the discard flag. */ *filnam = '\0'; /* and current file name */ #endif /* COMMENT */ return(x); } /* C L S O F -- Close an output file. */ /* Call with disp != 0 if file is to be discarded. */ /* Returns -1 upon failure to close, 0 or greater on success. */ int clsof(disp) int disp; { int x = 0; extern int success; fcps(); /* Calculate CPS quickly */ debug(F101,"clsof disp","",disp); debug(F101,"clsof cxseen","",cxseen); debug(F101,"clsof success","",success); debug(F101,"clsof o_isopen","",o_isopen); if (fncsav != -1) { /* Saved file collision action... */ fncact = fncsav; /* Restore it. */ fncsav = -1; /* Unsave it. */ } #ifdef datageneral if ((local) && (!quiet)) /* Only do this if local & not quiet */ connoi_mt(); #endif /* datageneral */ if (o_isopen && !calibrate) { if ((x = zclose(ZOFILE)) < 0) { /* Try to close the file */ tlog(F100,"Failure to close",filnam,0L); xxscreen(SCR_ST,ST_ERR,0l,"Can't close file"); #ifdef TLOG if (tralog && !tlogfmt) doxlog(what,prfspec,fsize,binary,1,"Can't close file"); #endif /* TLOG */ } else if (disp) { /* Interrupted or refused */ if (keep == 0 || /* If not keeping incomplete files */ (keep == SET_AUTO && binary == XYFT_T) ) { if (*filnam && (what & W_RECV)) /* AND we're receiving */ zdelet(filnam); /* ONLY THEN, delete it */ if (what & W_KERMIT) { debug(F100,"clsof incomplete discarded","",0); tlog(F100," incomplete: discarded","",0L); if (!epktrcvd && !epktsent) { xxscreen(SCR_ST,ST_DISC,0l,""); #ifdef TLOG if (tralog && !tlogfmt) doxlog(what,prfspec,fsize,binary,1,"Discarded"); #endif /* TLOG */ } } } else { /* Keep incomplete copy */ debug(F100,"clsof fstats 1","",0); fstats(); if (!discard) { /* Unless discarding for other reason... */ if (what & W_KERMIT) { debug(F100,"closf incomplete kept","",0); tlog(F100," incomplete: kept","",0L); } } if (what & W_KERMIT) { if (!epktrcvd && !epktsent) { xxscreen(SCR_ST,ST_INC,0l,""); #ifdef TLOG if (tralog && !tlogfmt) doxlog(what,prfspec,fsize,binary,1,"Incomplete"); #endif /* TLOG */ } } } } } if (o_isopen && x > -1 && !disp) { debug(F110,"clsof OK",rfspec,0); makestr(&rfspec,prfspec); makestr(&rrfspec,prrfspec); fstats(); if (!epktrcvd && !epktsent && !cxseen && !czseen) { xxscreen(SCR_ST,ST_OK,0L,""); #ifdef TLOG if (tralog && !tlogfmt) doxlog(what,rfspec,fsize,binary,0,""); #endif /* TLOG */ } } rs_len = (CK_OFF_T)0; o_isopen = 0; /* The file is not open any more. */ cxseen = 0; /* Reset per-file interruption flag */ return(x); /* Send back zclose() return code. */ } #ifdef SUNOS4S5 tolower(c) char c; { return((c)-'A'+'a'); } toupper(c) char c; { return((c)-'a'+'A'); } #endif /* SUNOS4S5 */ #endif /* NOXFER */ ckcfns.c0000644000015300001460000063776311573431377011335 0ustar fdckermitchar *fnsv = "C-Kermit functions, 9.0.233, 3 Jun 2011"; char *nm[] = { "Disabled", "Local only", "Remote only", "Enabled" }; /* C K C F N S -- System-independent Kermit protocol support functions. */ /* ...Part 1 (others moved to ckcfn2,3 to make this module smaller) */ /* Author: Frank da Cruz , Columbia University Academic Information Systems, New York City. Copyright (C) 1985, 2011, Trustees of Columbia University in the City of New York. All rights reserved. See the C-Kermit COPYING.TXT file or the copyright text in the ckcmai.c module for disclaimer and permissions. */ /* System-dependent primitives defined in: ck?tio.c -- terminal (communications) i/o cx?fio.c -- file i/o, directory structure */ #include "ckcsym.h" /* Needed for Stratus VOS */ #include "ckcasc.h" /* ASCII symbols */ #include "ckcdeb.h" /* Debug formats, typedefs, etc. */ #include "ckcker.h" /* Symbol definitions for Kermit */ #include "ckcxla.h" /* Character set symbols */ #include "ckcnet.h" /* VMS definition of TCPSOCKET */ #ifdef OS2 #ifdef OS2ONLY #include #endif /* OS2ONLY */ #include "ckocon.h" #endif /* OS2 */ int docrc = 0; /* Accumulate CRC for \v(crc16) */ long crc16 = 0L; /* File CRC = \v(crc16) */ int gnferror = 0; /* gnfile() failure reason */ extern CHAR feol; extern int byteorder, xflg, what, fmask, cxseen, czseen, nscanfile, sysindex; extern int xcmdsrc, dispos, matchfifo; extern int inserver; extern int nolinks; #ifdef VMSORUNIX extern int zgfs_dir; #ifdef CKSYMLINK extern int zgfs_link; #endif /* CKSYMLINK */ #endif /* VMSORUNIX */ #ifndef NOXFER #ifndef NOICP #ifndef NOSPL extern char * clcmds; extern int haveurl; #ifdef CK_APC extern int apcactive, adl_ask; #endif /* CK_APC */ #endif /* NOSPL */ #endif /* NOICP */ extern int remfile; /* (move these prototypes to the appropriate .h files...) */ #ifdef COMMENT /* Not used */ #ifdef VMS _PROTOTYP( int getvnum, (char *) ); #endif /* VMS */ #endif /* COMMENT */ _PROTOTYP( static int bgetpkt, (int) ); #ifndef NOCSETS _PROTOTYP( int lookup, (struct keytab[], char *, int, int *) ); #endif /* NOCSETS */ #ifndef NOSPL _PROTOTYP( int zzstring, (char *, char **, int *) ); #endif /* NOSPL */ #ifdef OS2 #include #ifdef OS2ONLY #include #endif /* OS2ONLY */ #endif /* OS2 */ #ifdef VMS #include #endif /* VMS */ /* Externals from ckcmai.c */ extern int srvcdmsg, srvidl, idletmo; extern char * cdmsgfile[]; extern int spsiz, spmax, rpsiz, timint, srvtim, rtimo, npad, ebq, ebqflg, rpt, rptq, rptflg, capas, keep, fncact, pkttim, autopar, spsizr, xitsta; extern int pktnum, bctr, bctu, bctf, bctl, clfils, sbufnum, protocol, size, osize, spktl, nfils, ckwarn, timef, spsizf, sndtyp, rcvtyp, success; extern int parity, turn, network, whatru, fsecs, justone, slostart, ckdelay, displa, mypadn, moving, recursive, nettype; extern long filcnt; extern CK_OFF_T tfc, fsize, sendstart, rs_len, flci, flco, tlci, tlco, calibrate; extern long filrej, oldcps, cps, peakcps, ccu, ccp, filestatus; extern int fblksiz, frecl, frecfm, forg, fcctrl, fdispla, skipbup; extern int spackets, rpackets, timeouts, retrans, crunched, wmax, wcur; extern int hcflg, binary, fncnv, b_save, f_save, server; extern int nakstate, discard, rejection, local, xfermode, interrupted; extern int rq, rqf, sq, wslots, wslotn, wslotr, winlo, urpsiz, rln; extern int fnspath, fnrpath, eofmethod, diractive, whatru2, wearealike; extern int atcapr, atcapb, atcapu; extern int lpcapr, lpcapb, lpcapu; extern int swcapr, swcapb, swcapu; extern int lscapr, lscapb, lscapu; extern int rscapr, rscapb, rscapu; extern int rptena, rptmin; extern int sseqtbl[]; extern int numerrs, nzxopts; extern long rptn; extern int maxtry; extern int stdouf; extern int sendmode; extern int carrier, ttprty; extern int g_fnrpath; #ifdef TCPSOCKET extern int ttnproto; #endif /* TCPSOCKET */ #ifndef NOSPL extern int sndxin, sndxhi, sndxlo; #endif /* NOSPL */ extern int g_binary, g_fncnv; #ifdef GFTIMER extern CKFLOAT fpfsecs; #endif /* GFTIMER */ #ifdef OS2 extern struct zattr iattr; #endif /* OS2 */ #ifdef PIPESEND extern int usepipes; #endif /* PIPESEND */ extern int pipesend; #ifdef STREAMING extern int streamrq, streaming, streamed, streamok; #endif /* STREAMING */ extern int reliable, clearrq, cleared, urclear; extern int atenci, atenco, atdati, atdato, atleni, atleno, atblki, atblko, attypi, attypo, atsidi, atsido, atsysi, atsyso, atdisi, atdiso; extern int bigsbsiz, bigrbsiz; extern char *versio; extern char *filefile; extern char whoareu[], * cksysid; #ifndef NOSERVER extern int ngetpath; extern char * getpath[]; extern int fromgetpath; #endif /* NOSERVER */ #ifdef CK_LOGIN extern int isguest; #endif /* CK_LOGIN */ extern int srvcmdlen; extern CHAR *srvcmd, * epktmsg; extern CHAR padch, mypadc, eol, seol, ctlq, myctlq, sstate, myrptq; extern CHAR *data, padbuf[], stchr, mystch; extern CHAR *srvptr; extern CHAR *rdatap; extern char *cmarg, *cmarg2, **cmlist, filnam[], ofilnam[]; extern char *rfspec, *prfspec, *rrfspec, *prrfspec, *sfspec, *psfspec, *rfspec; extern char fspec[]; extern int fspeclen; #ifndef NOMSEND extern struct filelist * filehead, * filenext; extern int addlist; #endif /* NOMSEND */ _PROTOTYP( int lslook, (unsigned int b) ); /* Locking Shift Lookahead */ _PROTOTYP( int szeof, (CHAR *s) ); _PROTOTYP( VOID fnlist, (void) ); #endif /* NOXFER */ extern CK_OFF_T ffc; /* Character set Translation */ #ifndef NOCSETS extern int tcharset, fcharset, dcset7, dcset8; extern int fcs_save, tcs_save; extern int ntcsets, xlatype, cseqtab[]; extern struct csinfo tcsinfo[], fcsinfo[]; extern int r_cset, s_cset, afcset[]; #ifdef UNICODE extern int ucsorder, fileorder; #endif /* UNICODE */ _PROTOTYP( CHAR ident, (CHAR) ); /* Identity translation function */ /* Arrays of and pointers to character translation functions */ #ifdef CK_ANSIC extern CHAR (*rx)(CHAR); /* Pointer to input character translation function */ extern CHAR (*sx)(CHAR); /* Pointer to output character translation function */ extern CHAR (*xls[MAXTCSETS+1][MAXFCSETS+1])(CHAR); /* Byte-to-Byte Send */ extern CHAR (*xlr[MAXTCSETS+1][MAXFCSETS+1])(CHAR); /* Byte-to-Byte Recv */ #ifdef UNICODE extern int (*xut)(USHORT); /* Translation function UCS to TCS */ extern int (*xuf)(USHORT); /* Translation function UCS to FCS */ extern USHORT (*xtu)(CHAR); /* Translation function TCS to UCS */ extern USHORT (*xfu)(CHAR); /* Translation function FCS to UCS */ #endif /* UNICODE */ #else /* The same declarations again for non-ANSI comilers... */ extern CHAR (*rx)(); extern CHAR (*sx)(); extern CHAR (*xls[MAXTCSETS+1][MAXFCSETS+1])(); extern CHAR (*xlr[MAXTCSETS+1][MAXFCSETS+1])(); #ifdef UNICODE extern int (*xut)(); extern int (*xuf)(); extern USHORT (*xtu)(); extern USHORT (*xfu)(); #endif /* UNICODE */ #endif /* CK_ANSIC */ #endif /* NOCSETS */ /* (PWP) external def. of things used in buffered file input and output */ #ifdef DYNAMIC extern char *zinbuffer, *zoutbuffer; #else extern char zinbuffer[], zoutbuffer[]; #endif /* DYNAMIC */ extern char *zinptr, *zoutptr; extern int zincnt, zoutcnt, zobufsize, xfrxla; extern long crcta[], crctb[]; /* CRC-16 generation tables */ extern int rseqtbl[]; /* Rec'd-packet sequence # table */ #ifndef NOXFER /* Criteria used by gnfile()... */ char sndafter[19] = { NUL, NUL }; char sndbefore[19] = { NUL, NUL }; char sndnafter[19] = { NUL, NUL }; char sndnbefore[19] = { NUL, NUL }; char *sndexcept[NSNDEXCEPT] = { NULL, NULL }; char *rcvexcept[NSNDEXCEPT] = { NULL, NULL }; CK_OFF_T sndsmaller = (CK_OFF_T)-1; CK_OFF_T sndlarger = (CK_OFF_T)-1; /* Variables defined in this module but shared by other modules. */ int xfrbel = 1; char * ofperms = ""; /* Output file permissions */ int autopath = 0; /* SET RECEIVE PATHNAMES AUTO flag */ #ifdef CALIBRATE #define CAL_O 3 #define CAL_M 253 int cal_j = 0; CHAR cal_a[] = { 16, 45, 98, 3, 52, 41, 14, 7, 76,165,122, 11,104, 77,166, 15, 160, 93, 18, 19,112, 85, 54, 23,232,213, 90, 27, 12, 81,126, 31, 4,205, 34, 35,144, 73,110, 39, 28,133,218, 43,156, 65,102, 47, 84, 61, 50, 51,208,117, 86, 55, 8,245, 74, 59, 44,125,222, 63, 80, 1,162, 67,116,105,206, 71,120, 9,250, 75, 88, 97, 6, 79, 100,221, 82, 83, 36, 89, 94, 87, 40, 21,106, 91,236,145,150, 95, 228, 33,130, 99,148,137,198,103,108,169, 42,107,184,129, 78,111, 0, 49,114,115, 32,121,254,119,172, 57,138,123,152,177, 22,127, 240,193, 2,131,176, 5, 38,135,204,229, 10,139,200,161,174,143, 128, 17,146,147, 68,153, 30,151, 72,217,170,155, 24,209, 62,159, 64,225,194,163,244,201, 70,167,216,197,234,171,188,109,230,175, 212,113,178,179,132,185,190,183,136,249,202,187, 92,241,118,191, 48,237, 66,195, 96,233,142,199,248, 37, 58,203, 60, 13,134,207, 20, 29,210,211,164,149,182,215,220, 25, 26,219,124,157,246,223, 180,141,226,227,192,101,238,231, 56, 69,154,235,252,173, 46,239, 224,253,242,243,196, 53,214,247,168,181,186,251,140,189,158,255 }; #endif /* CALIBRATE */ char * rf_err = "Error receiving file"; /* rcvfil() error message */ #ifdef CK_SPEED short ctlp[256] = { /* Control-Prefix table */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* C0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* G0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, /* DEL */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* C1 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* G1 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 /* 255 */ }; #endif /* CK_SPEED */ int sndsrc; /* Flag for where to get names of files to send: */ /* -1: znext() function */ /* 0: stdin */ /* >0: list in cmlist or other list */ /* -9: calibrate */ int memstr; /* Flag for input from memory string */ int funcstr; /* Flag for input from function */ int bestlen = 0; int maxsend = 0; int gnf_binary = 0; /* Prevailing xfer mode for gnfile */ #ifdef pdp11 #define MYINITLEN 32 #else #define MYINITLEN 100 #endif /* pdp11 */ CHAR myinit[MYINITLEN]; /* Copy of my Send-Init data */ /* Variables local to this module */ #ifdef TLOG #ifndef XYZ_INTERNAL static #endif /* XYZ_INTERNAL */ char *fncnam[] = { "rename", "replace", "backup", "append", "discard", "ask", "update", "dates-differ", "" }; #endif /* TLOG */ static char *memptr; /* Pointer for memory strings */ #ifdef VMS extern int batch; #else extern int backgrd; #endif /* VMS */ #ifdef CK_CTRLZ static int lastchar = 0; #endif /* CK_CTRLZ */ #ifdef CK_ANSIC static int (*funcptr)(void); /* Pointer for function strings */ #else static int (*funcptr)(); #endif /* CK_ANSIC */ #ifdef pdp11 #define CMDSTRL 50 static char cmdstr[50]; /* System command string. */ #else #ifdef BIGBUFOK #define CMDSTRL 1024 #else #define CMDSTRL 256 #endif /* BIGBUFOK */ static char cmdstr[CMDSTRL+1]; #endif /* pdp11 */ static int drain; /* For draining stacked-up ACKs. */ static int first; /* Flag for first char from input */ static CHAR t; /* Current character */ #ifdef COMMENT static CHAR next; /* Next character */ #endif /* COMMENT */ static int ebqsent = 0; /* 8th-bit prefix bid that I sent */ static int lsstate = 0; /* Locking shift state */ static int lsquote = 0; /* Locking shift quote */ extern int quiet; /* E N C S T R -- Encode a string from memory. */ /* Call this instead of getpkt() if source is a string, rather than a file. Note: Character set translation is never done in this case. */ #ifdef COMMENT #define ENCBUFL 200 #ifndef pdp11 CHAR encbuf[ENCBUFL]; #else /* This is gross, but the pdp11 root segment is out of space */ /* Will allocate it in ckuusr.c. */ extern CHAR encbuf[]; #endif /* pdp11 */ #endif /* COMMENT */ /* Encode packet data from a string in memory rather than from a file. Returns the length of the encoded string on success, -1 if the string could not be completely encoded into the currently negotiated data field length. */ int #ifdef CK_ANSIC encstr(CHAR *s) #else /* CK_ANSIC */ encstr(s) CHAR* s; #endif /* CK_ANSIC */ { /* Recoded 30 Jul 94 to use the regular data buffer and the negotiated maximum packet size. Previously we were limited to the length of encbuf[]. Also, to return a failure code if the entire encoded string would not fit. Modified 14 Jul 98 to return length of encoded string. */ int m, rc, slen; char *p; if (!data) { /* Watch out for null pointers. */ debug(F100,"SERIOUS ERROR: encstr data == NULL","",0); return(-1); } if (!s) s = (CHAR *)""; /* Ditto. */ slen = strlen((char *)s); /* Length of source string. */ debug(F111,"encstr",s,slen); rc = 0; /* Return code. */ m = memstr; p = memptr; /* Save these. */ memptr = (char *)s; /* Point to the string. */ /* debug(F101,"encstr memptr 1","",memptr); */ memstr = 1; /* Flag memory string as source. */ first = 1; /* Initialize character lookahead. */ *data = NUL; /* In case s is empty */ debug(F101,"encstr spsiz","",spsiz); rc = getpkt(spsiz,0); /* Fill a packet from the string. */ debug(F101,"encstr getpkt rc","",rc); if (rc > -1 && memptr < (char *)(s + slen)) { /* Means we didn't encode */ rc = -1; /* the whole string. */ debug(F101,"encstr string too big","",size); } debug(F101,"encstr getpkt rc","",rc); memstr = m; /* Restore memory string flag */ memptr = p; /* and pointer */ first = 1; /* Put this back as we found it. */ return(rc); } /* Output functions passed to 'decode': */ int /* Put character in server command buffer */ #ifdef CK_ANSIC putsrv(char c) #else putsrv(c) register char c; #endif /* CK_ANSIC */ /* putsrv */ { *srvptr++ = c; *srvptr = '\0'; /* Make sure buffer is null-terminated */ return(0); } int /* Output character to console. */ #ifdef CK_ANSIC puttrm(char c) #else puttrm(c) register char c; #endif /* CK_ANSIC */ /* puttrm */ { extern int rcdactive; #ifndef NOSPL extern char * qbufp; /* If REMOTE QUERY active, */ extern int query, qbufn; /* also store response in */ if (query && qbufn++ < 1024) { /* query buffer. */ *qbufp++ = c; *qbufp = NUL; } if (!query || !xcmdsrc) #endif /* NOSPL */ /* This routine is used (among other things) for printing the server's answer to a REMOTE command. But REMOTE CD is special because it isn't really asking for an answer from the server. Thus some people want to suppress the confirmation message (e.g. when the server sends back the actual path of the directory CD'd to), and expect to be able to do this with SET QUIET ON. But they would not want SET QUIET ON to suppress the other server replies, which are pointless without their answers. Thus the "rcdactive" flag (REMOTE CD command is active). Thu Oct 10 16:38:21 2002 */ if (!(quiet && rcdactive)) /* gross, yuk */ conoc(c); return(0); } #endif /* NOXFER */ int /* Output char to file. */ #ifdef CK_ANSIC putmfil(char c) /* Just like putfil but to ZMFILE */ #else /* rather than ZOFILE... */ putmfil(c) register char c; #endif /* CK_ANSIC */ /* putmfil */ { debug(F000,"putfil","",c); if (zchout(ZMFILE, (char) (c & fmask)) < 0) { czseen = 1; debug(F101,"putfil zchout write error, setting czseen","",1); return(-1); } return(0); } int /* Output char to nowhere. */ #ifdef CK_ANSIC putnowhere(char c) #else putnowhere(c) register char c; #endif /* CK_ANSIC */ /* putnowhere */ { return(0); } int /* Output char to file. */ #ifdef CK_ANSIC putfil(char c) #else putfil(c) register char c; #endif /* CK_ANSIC */ /* putfil */ { debug(F000,"putfil","",c); if (zchout(ZOFILE, (char) (c & fmask)) < 0) { czseen = 1; /* If write error... */ debug(F101,"putfil zchout write error, setting czseen","",1); return(-1); } return(0); } /* The following function is a wrapper for putfil(). The only reason for its existence is to be passed as a function pointer to decode(), which treats putfil() itself specially -- bypassing it and using an internal macro instead to speed things up. Use zputfil() instead of putfil() in cases where we do not want this to happen, e.g. when we need to send output to the file with a mixture of zchout() and zsout()/zsoutl() calls (as is the case with incoming short-form REMOTE command replies redirected to a file), which would otherwise result in data written to the file out of order. */ int #ifdef CK_ANSIC zputfil(char c) #else zputfil(c) register char c; #endif /* CK_ANSIC */ /* zputfil */ { return(putfil(c)); } #ifndef NOXFER /* D E C O D E -- Kermit packet decoding procedure */ /* Call with string to be decoded and an output function. Returns 0 on success, -1 on failure (e.g. disk full). This is the "inner loop" when receiving files, and must be coded as efficiently as possible. Note some potential problems: if a packet is badly formed, having a prefixed sequence ending prematurely, this function, as coded, could read past the end of the packet. This has never happened, thus the additional (time-consuming) tests have not been added. */ static CHAR *xdbuf; /* Global version of decode()'s buffer pointer */ /* for use by translation functions. */ /* Function for pushing a character onto decode()'s input stream. */ VOID #ifdef CK_ANSIC zdstuff(CHAR c) #else zdstuff(c) CHAR c; #endif /* CK_ANSIC */ /* zdstuff */ { xdbuf--; /* Back up the pointer. */ *xdbuf = c; /* Stuff the character. */ } #ifdef CKTUNING /* Trimmed-down packet decoder for binary-mode no-parity transfers. decode() is the full version. */ int #ifdef CK_ANSIC bdecode(CHAR *buf, int (*fn)(char)) #else bdecode(buf,fn) register CHAR *buf; register int (*fn)(); #endif /* CK_ANSIC */ /* bdecode */ { register unsigned int a, a7; /* Various copies of current char */ int ccpflg; /* For Ctrl-unprefixing stats */ int t; /* Int version of character */ int len; long z; /* For CRC calculation */ CHAR c; /* Current character */ if (!binary || parity || fn != putfil) /* JUST IN CASE */ return(decode(buf,fn,1)); debug(F100,"BDECODE","",0); xdbuf = buf; /* Global copy of source pointer. */ len = rln; /* Number of bytes in data field */ while (len > 0) { a = *xdbuf++ & 0xff; /* Get next character */ len--; rpt = 0; /* Initialize repeat count. */ if (a == rptq && rptflg) { /* Got a repeat prefix? */ rpt = xunchar(*xdbuf++ & 0xFF); /* Yes, get the repeat count, */ rptn += rpt; a = *xdbuf++ & 0xFF; /* and get the prefixed character. */ len -= 2; } ccpflg = 0; /* Control prefix flag. */ if (a == ctlq) { /* If control prefix, */ a = *xdbuf++ & 0xFF; /* get its operand */ len--; a7 = a & 0x7F; /* and its low 7 bits. */ if ((a7 >= 0100 && a7 <= 0137) || a7 == '?') { /* Controllify */ a = ctl(a); /* if in control range. */ a7 = a & 0x7F; ccpflg = 1; /* Note that we did this */ ccp++; /* Count for stats */ } } else a7 = a & 0x7f; /* Not control quote */ if (a7 < 32 || a7 == 127) /* A bare control character? */ if (!ccpflg) ccu++; /* Count it */ if (!rpt) rpt = 1; for (; rpt > 0; rpt--) { /* Output the char RPT times */ #ifdef CALIBRATE if (calibrate) { ffc++; continue; } #endif /* CALIBRATE */ #ifdef OS2 if (xflg && !remfile) { /* Write to virtual screen */ char _a; _a = a & fmask; t = conoc(_a); if (t < 1) t = -1; } else #endif /* OS2 */ t = zmchout(a & fmask); /* zmchout is a macro */ if (t < 0) { debug(F101,"bdecode write error - errno","",errno); return(-1); } ffc++; /* Count the character */ if (docrc && !remfile) { /* Update file CRC */ c = a; /* Force conversion to unsigned char */ z = crc16 ^ (long)c; crc16 = (crc16 >> 8) ^ (crcta[(z & 0xF0) >> 4] ^ crctb[z & 0x0F]); } } #ifdef CK_CTRLZ lastchar = a; #endif /* CK_CTRLZ */ } return(0); } #endif /* CKTUNING */ #endif /* NOXFER */ /* P N B Y T E -- Output next byte to file or other destination */ static CK_OFF_T offc = 0L; static int #ifdef CK_ANSIC pnbyte(CHAR c, int (*fn)(char)) #else pnbyte(c,fn) CHAR c; int (*fn)(); #endif /* CK_ANSIC */ /* pnbyte */ { int rc; long z; #ifdef OS2 #ifndef NOXFER if (xflg && !remfile) { /* Write to virtual screen */ char _a; _a = c & fmask; rc = conoc(_a); if (rc < 1) return(-1); } else #endif /* NOXFER */ #endif /* OS2 */ { if (fn == putfil) { /* Execute output function */ rc = zmchout(c); /* to-file macro (fast) */ } else if (!fn) { rc = putchar(c); /* to-screen macro (fast) */ } else { rc = (*fn)(c); /* function call (not as fast) */ } if (rc < 0) return(rc); } /* Both xgnbyte() and xpnbyte() increment ffc (the file byte counter). During file transfer, only one of these functions is called. However, the TRANSLATE command is likely to call them both. offc, therefore, contains the output byte count, necessary for handling the UCS-2 BOM. NOTE: It might be safe to just test for W_SEND, FTP or not. */ if ((what & (W_FTP|W_SEND)) != (W_FTP|W_SEND)) { offc++; /* Count the byte */ ffc++; /* Count the byte */ } #ifndef NOXFER if (docrc && !xflg && !remfile) { /* Update file CRC */ z = crc16 ^ (long)c; crc16 = (crc16 >> 8) ^ (crcta[(z & 0xF0) >> 4] ^ crctb[z & 0x0F]); } #endif /* NOXFER */ return(1); } /* X P N B Y T E -- Translate and put next byte to file. Only for Unicode. Call with next untranslated byte from incoming byte stream, which can be in any Transfer Character Set: UCS-2, UTF-8, Latin-1, Latin-Hebrew, etc. Translates to the file character set and writes bytes to the output file. Call with character to translate as an int, plus the transfer character set (to translate from) and the file character set (to translate to), or -1,0,0 to reset the UCS-2 byte number (which should be done at the beginning of a file). If the transfer (source) character-set is UCS-2, bytes MUST arrive in Big-Endian order. Returns: -1: On error 0: Nothing to write (mid-sequence) >0: Number of bytes written. */ #ifdef KANJI static int jstate = 0, jx = 0; /* For outputting JIS-7 */ static char jbuf[16] = { NUL, NUL }; #endif /* KANJI */ int #ifdef CK_ANSIC xpnbyte(int a, int tcs, int fcs, int (*fn)(char)) #else xpnbyte(a,tcs,fcs,fn) int a, tcs, fcs; int (*fn)(); #endif /* CK_ANSIC */ /* xpnbyte */ { #ifdef UNICODE extern int ucsbom; /* Byte order */ #endif /* UNICODE */ /* CHAR c; */ /* Unsigned char worker */ static union ck_short uc, eu, sj; /* UCS-2, EUC, and Shift-JIS workers */ USHORT ch; /* ditto... */ USHORT * us = NULL; /* ditto... */ int c7, rc, haveuc = 0; /* Return code and UCS-2 flag */ int utferror = 0; /* UTF-8 error */ static int bn = 0; /* UCS-2 byte number */ int swapping = 0; /* Swapping UCS bytes to output? */ /* swapping must be 0 or 1 */ if (a == -1 && (tcs | fcs) == 0) { /* Reset in case previous run */ bn = 0; /* left bn at 1... */ offc = (CK_OFF_T)0; debug(F101,"xpnbyte RESET","",bn); return(0); } debug(F001,"xpnbyte a","",a); #ifdef UNICODE /* byteorder = hardware byte order of this machine. ucsorder = override by SET FILE UCS BYTE-ORDER command. fileorder = byte order of UCS-2 input file detected from BOM. swapping applies only when output charset is UCS-2. */ if (ucsorder != 1 && ucsorder != 0) /* Also just in case... */ ucsorder = byteorder; if ((byteorder && !ucsorder) || (!byteorder && fileorder)) swapping = 1; /* Swapping bytes to output */ #ifdef COMMENT debug(F101,"xpnbyte ucsorder","",ucsorder); debug(F101,"xpnbyte swapping","",swapping); #endif /* COMMENT */ if (tcs == TC_UTF8) { /* 'a' is from a UTF-8 stream */ ch = a; if (fcs == TC_UTF8) /* Output is UTF-8 too */ return(pnbyte(ch,fn)); /* so just copy. */ rc = utf8_to_ucs2(ch,&us); /* Otherwise convert to UCS-2 */ if (rc == 0) { /* Done with this sequence */ uc.x_short = *us; /* We have a Unicode */ haveuc = 1; } else if (rc < 0) { /* Error */ debug(F101,"xpnbyte UTF-8 conversion error","",rc); haveuc = 1; /* Replace by U+FFFD */ uc.x_short = *us; utferror = 1; } else /* Sequence incomplete */ return(0); } else if (tcs == TC_UCS2) { /* 'a' is UCS-2 */ /* Here we have incoming UCS-2 in guaranteed Big Endian order */ /* so we must exchange bytes if local machine is Little Endian. */ switch (bn) { /* Which byte? */ case 0: /* High... */ uc.x_char[byteorder] = (unsigned)a & 0xff; bn++; return(0); /* Wait for next */ case 1: /* Low... */ uc.x_char[1-byteorder] = (unsigned)a & 0xff; bn = 0; /* Done with sequence */ haveuc = 1; /* Have a Unicode */ } } else #endif /* UNICODE */ #ifdef KANJI /* Whether UNICODE is defined or not */ if (tcs == TC_JEUC) { /* Incoming Japanese EUC */ int bad = 0; static int kanji = 0; /* Flags set in case 0 for case 1 */ static int kana = 0; switch (bn) { /* Byte number */ case 0: /* Byte 0 */ eu.x_short = 0; if ((a & 0x80) == 0) { sj.x_short = (unsigned)a & 0xff; /* Single byte */ kanji = kana = 0; } else { /* Double byte */ c7 = a & 0x7f; if (c7 > 0x20 && c7 < 0x7f) { /* Kanji */ eu.x_char[byteorder] = (CHAR) a; /* Store first byte */ bn++; /* Set up for second byte */ kanji = 1; kana = 0; return(0); } else if (a == 0x8e) { /* SS2 -- Katakana prefix */ eu.x_char[byteorder] = (CHAR) a; /* Save it */ bn++; kana = 1; kanji = 0; return(0); } else { bad++; } } break; case 1: /* Byte 1 */ bn = 0; if (kanji) { eu.x_char[1-byteorder] = (CHAR) a; sj.x_short = eu_to_sj(eu.x_short); break; } else if (kana) { sj.x_short = (CHAR) (a | 0x80); break; } else { /* (shouldn't happen) */ bad++; } } /* Come here with one Shift-JIS character */ #ifdef UNICODE if (bad) { uc.x_short = 0xfffd; } else { uc.x_short = sj_to_un(sj.x_short); /* Convert to Unicode */ } haveuc = 1; #endif /* UNICODE */ } else #endif /* KANJI */ #ifdef UNICODE uc.x_short = (unsigned)a & 0xff; /* Latin-1 or whatever... */ /* Come here with uc = the character to be translated. */ /* If (haveuc) it's UCS-2 in native order, otherwise it's a byte. */ debug(F101,"xpnbyte haveuc","",haveuc); if (haveuc) { /* If we have a Unicode... */ debug(F001,"xpnbyte uc.x_short","[A]",uc.x_short); debug(F101,"xpnbyte feol","",feol); if (what & W_XFER) { /* If transferring a file */ if (feol && uc.x_short == CR) { /* handle eol conversion. */ return(0); } else if (feol && uc.x_short == LF) { uc.x_short = feol; } } debug(F001,"xpnbyte uc.x_short","[B]",uc.x_short); if (fcs == FC_UCS2) { /* And FCS is UCS-2 */ /* Write out the bytes in the appropriate byte order */ int count = 0; #ifndef IKSDONLY #ifdef OS2 extern int k95stdout,wherex[],wherey[]; extern unsigned char colorcmd; union { USHORT ucs2; UCHAR bytes[2]; } output; #endif /* OS2 */ #endif /* IKSDONLY */ if (!offc && ucsbom) { /* Beginning of file? */ #ifndef IKSDONLY #ifdef OS2 if (fn == NULL && !k95stdout && !inserver) { offc++; #ifdef COMMENT /* Don't print the BOM to the display */ output.bytes[0] = (!ucsorder ? 0xff : 0xfe); output.bytes[1] = (!ucsorder ? 0xfe : 0xff); VscrnWrtUCS2StrAtt(VCMD, &output.ucs2, 1, wherey[VCMD], wherex[VCMD], &colorcmd ); #endif /* COMMENT */ } else #endif /* OS2 */ #endif /* IKSDONLY */ { if ((rc = pnbyte((ucsorder ? 0xff : 0xfe),fn)) < 0) return(rc); /* BOM */ if ((rc = pnbyte((ucsorder ? 0xfe : 0xff),fn)) < 0) return(rc); } count += 2; } if (utferror) { #ifndef IKSDONLY #ifdef OS2 if (fn == NULL && !k95stdout && !inserver) { offc++; output.bytes[0] = (!ucsorder ? 0xfd : 0xff); output.bytes[1] = (!ucsorder ? 0xff : 0xfd); VscrnWrtUCS2StrAtt(VCMD, &output.ucs2, 1, wherey[VCMD], wherex[VCMD], &colorcmd ); } else #endif /* OS2 */ #endif /* IKSDONLY */ { if ((rc = pnbyte((ucsorder ? 0xfd : 0xff),fn)) < 0) return(rc); if ((rc = pnbyte((ucsorder ? 0xff : 0xfd),fn)) < 0) return(rc); } count += 2; } #ifndef IKSDONLY #ifdef OS2 if (fn == NULL && !k95stdout && !inserver) { offc++; output.bytes[0] = uc.x_char[swapping]; output.bytes[1] = uc.x_char[1-swapping]; VscrnWrtUCS2StrAtt(VCMD, &output.ucs2, 1, wherey[VCMD], wherex[VCMD], &colorcmd ); } else #endif /* OS2 */ #endif /* IKSDONLY */ { if ((rc = pnbyte(uc.x_char[swapping],fn)) < 0) return(rc); if ((rc = pnbyte(uc.x_char[1-swapping],fn)) < 0) return(rc); } count += 2; return(count); } else if (fcs == FC_UTF8) { /* Convert to UTF-8 */ CHAR * buf = NULL; int i, count; if (utferror) { if ((rc = pnbyte((ucsorder ? 0xbd : 0xff),fn)) < 0) return(rc); if ((rc = pnbyte((ucsorder ? 0xff : 0xbd),fn)) < 0) return(rc); } if ((count = ucs2_to_utf8(uc.x_short,&buf)) < 1) return(-1); debug(F011,"xpnbyte buf",buf,count); for (i = 0; i < count; i++) if ((rc = pnbyte(buf[i],fn)) < 0) return(rc); if (utferror) count += 2; return(count); } else { /* Translate UCS-2 to byte */ if (uc.x_short == 0x2028 || uc.x_short == 0x2029) { if (utferror) pnbyte(UNK,fn); if (feol) return(pnbyte((CHAR)feol,fn)); if ((rc = pnbyte((CHAR)CR,fn)) < 0) return(rc); if ((rc = pnbyte((CHAR)LF,fn)) < 0) return(rc); else return(utferror ? 3 : 2); } else if (xuf) { /* UCS-to-FCS function */ int x = 0; if (utferror) pnbyte(UNK,fn); if ((rc = (*xuf)(uc.x_short)) < 0) /* These can fail... */ ch = UNK; else ch = (unsigned)((unsigned)rc & 0xffff); x = pnbyte(ch,fn); if (x < 0) return(x); else if (utferror) x++; return(x); #ifdef KANJI /* Also see the non-Unicode Kanji section further down in this function. */ } else if (fcsinfo[fcs].alphabet == AL_JAPAN) { /* Translate UCS-2 to Japanese set */ debug(F001,"xpnbyte uc","",uc.x_short); sj.x_short = un_to_sj(uc.x_short); /* First to Shift-JIS */ debug(F001,"xpnbyte sj","",sj.x_short); switch (fcs) { /* File character set */ case FC_SHJIS: /* Shift-JIS -- just output it */ if (sj.x_char[byteorder]) /* But not high byte if zero */ if ((rc = pnbyte((CHAR)sj.x_char[byteorder],fn)) < 0) return(rc); if ((rc = pnbyte((CHAR)sj.x_char[1-byteorder],fn)) < 0) return(rc); return(2); case FC_JEUC: /* EUC-JP */ eu.x_short = sj_to_eu(sj.x_short); /* Shift-JIS to EUC */ debug(F001,"xpnbyte eu","",eu.x_short); if (eu.x_short == 0xffff) { /* Bad */ if ((rc = pnbyte(UNK,fn)) < 0) return(rc); return(1); } else { /* Good */ int count = 0; /* Write high byte if not zero */ if (eu.x_char[byteorder]) { if ((rc=pnbyte((CHAR)eu.x_char[byteorder],fn)) < 0) return(rc); count++; } /* Always write low byte */ if ((rc = pnbyte((CHAR)eu.x_char[1-byteorder],fn)) < 0) return(rc); count++; return(count); } break; case FC_JIS7: /* JIS-7 */ case FC_JDEC: /* DEC Kanji */ eu.x_short = sj_to_eu(sj.x_short); /* Shift-JIS to EUC */ if (eu.x_short == 0xffff) { /* Bad */ debug(F001,"xpnbyte bad eu","",eu.x_short); if ((rc = pnbyte(UNK,fn)) < 0) return(rc); return(1); } else { /* Good */ int i; /* Use another name - 'a' hides parameter */ /* It's OK as is but causes compiler warnings */ char a = eu.x_char[1-byteorder]; /* Low byte */ debug(F001,"xpnbyte eu","",eu.x_short); if (eu.x_char[byteorder] == 0) { /* Roman */ switch (jstate) { case 1: /* Current state is Katakana */ jbuf[0] = 0x0f; /* SI */ jbuf[1] = a; jx = 2; break; case 2: /* Current state is Kanji */ jbuf[0] = 0x1b; /* ESC */ jbuf[1] = 0x28; /* ( */ jbuf[2] = 0x4a; /* J */ jbuf[3] = a; jx = 4; break; default: /* Current state is Roman */ jbuf[0] = a; jx = 1; break; } jstate = 0; /* New state is Roman */ } else if (eu.x_char[byteorder] == 0x8e) { /* Kana */ jx = 0; switch (jstate) { case 2: /* from Kanji */ jbuf[jx++] = 0x1b; /* ESC */ jbuf[jx++] = 0x28; /* ( */ jbuf[jx++] = 0x4a; /* J */ case 0: /* from Roman */ jbuf[jx++] = 0x0e; /* SO */ default: /* State is already Kana*/ jbuf[jx++] = (a & 0x7f); /* and the char */ break; } jstate = 1; /* New state is Katakana */ } else { /* Kanji */ jx = 0; switch (jstate) { case 1: /* Current state is Katakana */ jbuf[jx++] = 0x0f; /* SI */ case 0: /* Current state is Roman */ jbuf[jx++] = 0x1b; /* ESC */ jbuf[jx++] = 0x24; /* $ */ jbuf[jx++] = 0x42; /* B */ default: /* Current state is already Kanji */ jbuf[jx++] = eu.x_char[byteorder] & 0x7f; jbuf[jx++] = eu.x_char[1-byteorder] & 0x7f; break; } jstate = 2; /* Set state to Kanji */ } for (i = 0; i < jx; i++) /* Output the result */ if ((rc = pnbyte(jbuf[i],fn)) < 0) return(rc); return(jx); /* Return its length */ } } #endif /* KANJI */ } else { /* No translation function */ int count = 0; if (utferror) { if ((rc = pnbyte((ucsorder ? 0xfd : 0xff),fn)) < 0) return(rc); if ((rc = pnbyte((ucsorder ? 0xff : 0xfd),fn)) < 0) return(rc); count += 2; } if ((rc = pnbyte(uc.x_char[swapping],fn)) < 0) return(rc); if ((rc = pnbyte(uc.x_char[1-swapping],fn)) < 0) return(rc); count += 2; return(count); } } } else { /* Byte to Unicode */ if (xtu) { /* TCS-to-UCS function */ if (((tcsinfo[tcs].size > 128) && (uc.x_short & 0x80)) || tcsinfo[tcs].size <= 128) uc.x_short = (*xtu)(uc.x_short); } if (fcs == FC_UCS2) { /* And FCS is UCS-2 */ /* Write out the bytes in the appropriate byte order */ if (!offc && ucsbom) { /* Beginning of file? */ if ((rc = pnbyte((ucsorder ? 0xff : 0xfe),fn)) < 0) /* BOM */ return(rc); if ((rc = pnbyte((ucsorder ? 0xfe : 0xff),fn)) < 0) return(rc); } if ((rc = pnbyte(uc.x_char[swapping],fn)) < 0) return(rc); if ((rc = pnbyte(uc.x_char[1-swapping],fn)) < 0) return(rc); return(2); } else if (fcs == FC_UTF8) { /* Convert to UTF-8 */ CHAR * buf = NULL; int i, count; if ((count = ucs2_to_utf8(uc.x_short,&buf)) < 1) return(-1); for (i = 0; i < count; i++) if ((rc = pnbyte(buf[i],fn)) < 0) return(rc); return(count); } else { debug(F100,"xpnbyte impossible combination","",0); return(-1); } } #else #ifdef KANJI /* This almost, but not quite, duplicates the Kanji section above. There is no doubt a way to combine the sections more elegantly, but probably only at the expense of additional execution overhead. As matters stand, be careful to reflect any changes in this section to the other Kanji section above. */ if (tcs == TC_JEUC) { /* Incoming Japanese EUC */ int count = 0; switch (fcs) { /* File character set */ case FC_SHJIS: /* Shift-JIS -- just output it */ if (sj.x_char[byteorder]) /* But not high byte if zero */ if ((rc = pnbyte((CHAR)sj.x_char[byteorder],fn)) < 0) return(rc); count++; if ((rc = pnbyte((CHAR)sj.x_char[1-byteorder],fn)) < 0) return(rc); count++; return(count); case FC_JEUC: /* EUC-JP */ eu.x_short = sj_to_eu(sj.x_short); /* Shift-JIS to EUC */ debug(F001,"xpnbyte FC_JEUC eu","",eu.x_short); if (eu.x_short == 0xffff) { /* Bad */ if ((rc = pnbyte(UNK,fn)) < 0) return(rc); return(1); } else { /* Good */ int count = 0; /* Write high byte if not zero */ if (eu.x_char[byteorder]) { if ((rc = pnbyte((CHAR)eu.x_char[byteorder],fn)) < 0) return(rc); count++; } /* Always write low byte */ if ((rc = pnbyte((CHAR)eu.x_char[1-byteorder],fn)) < 0) return(rc); count++; return(count); } break; case FC_JIS7: /* JIS-7 */ case FC_JDEC: /* DEC Kanji */ eu.x_short = sj_to_eu(sj.x_short); /* Shift-JIS to EUC */ if (eu.x_short == 0xffff) { /* Bad */ debug(F001,"xpnbyte FC_JIS7 bad eu","",eu.x_short); if ((rc = pnbyte(UNK,fn)) < 0) return(rc); return(1); } else { /* Good */ int i; char a = eu.x_char[1-byteorder]; /* Low byte */ debug(F001,"xpnbyte FC_JIS7 eu","",eu.x_short); if (eu.x_char[byteorder] == 0) { /* Roman */ switch (jstate) { case 1: /* Current state is Katakana */ jbuf[0] = 0x0f; /* SI */ jbuf[1] = a; jx = 2; break; case 2: /* Current state is Kanji */ jbuf[0] = 0x1b; /* ESC */ jbuf[1] = 0x28; /* ( */ jbuf[2] = 0x4a; /* J */ jbuf[3] = a; jx = 4; break; default: /* Current state is Roman */ jbuf[0] = a; jx = 1; break; } jstate = 0; /* New state is Roman */ } else if (eu.x_char[byteorder] == 0x8e) { /* Kana */ jx = 0; switch (jstate) { case 2: /* from Kanji */ jbuf[jx++] = 0x1b; /* ESC */ jbuf[jx++] = 0x28; /* ( */ jbuf[jx++] = 0x4a; /* J */ case 0: /* from Roman */ jbuf[jx++] = 0x0e; /* SO */ default: /* State is already Kana*/ jbuf[jx++] = (a & 0x7f); /* and the char */ break; } jstate = 1; /* New state is Katakana */ } else { /* Kanji */ jx = 0; switch (jstate) { case 1: /* Current state is Katakana */ jbuf[jx++] = 0x0f; /* SI */ case 0: /* Current state is Roman */ jbuf[jx++] = 0x1b; /* ESC */ jbuf[jx++] = 0x24; /* $ */ jbuf[jx++] = 0x42; /* B */ default: /* Current state is already Kanji */ jbuf[jx++] = eu.x_char[byteorder] & 0x7f; jbuf[jx++] = eu.x_char[1-byteorder] & 0x7f; break; } jstate = 2; /* Set state to Kanji */ } for (i = 0; i < jx; i++) /* Output the result */ if ((rc = pnbyte(jbuf[i],fn)) < 0) return(rc); return(jx); /* Return its length */ } default: if (sj.x_short < 0x80) return(sj.x_short); else return('?'); } } #endif /* KANJI */ #endif /* UNICODE */ debug(F100,"xpnbyte BAD FALLTHRU","",0); return(-1); } #ifndef NOXFER /* D E C O D E -- Kermit Data-packet decoder */ int #ifdef CK_ANSIC decode(CHAR *buf, int (*fn)(char), int xlate) #else decode(buf,fn,xlate) register CHAR *buf; register int (*fn)(); int xlate; #endif /* CK_ANSIC */ /* decode */ { register unsigned int a, a7, a8, b8; /* Various copies of current char */ int t; /* Int version of character */ int ssflg; /* Character was single-shifted */ int ccpflg; /* For Ctrl-unprefixing stats */ int len; long z; CHAR c; /* Catch the case in which we are asked to decode into a file that is not open, for example, if the user interrupted the transfer, but the other Kermit keeps sending. */ if ((cxseen || czseen || discard) && (fn == putfil)) return(0); #ifdef COMMENT #ifdef CKTUNING if (binary && !parity) return(bdecode(buf,fn)); #endif /* CKTUNING */ #endif /* COMMENT */ debug(F100,"DECODE","",0); xdbuf = buf; /* Make global copy of pointer. */ rpt = 0; /* Initialize repeat count. */ len = rln; /* Number of bytes in data field */ while (len > 0) { /* Loop for each byte */ a = *xdbuf++ & 0xff; /* Get next character */ len--; if (a == rptq && rptflg) { /* Got a repeat prefix? */ rpt = xunchar(*xdbuf++ & 0xFF); /* Yes, get the repeat count, */ rptn += rpt; a = *xdbuf++ & 0xFF; /* and get the prefixed character. */ len -= 2; } b8 = lsstate ? 0200 : 0; /* 8th-bit value from SHIFT-STATE */ if (ebqflg && a == ebq) { /* Have 8th-bit prefix? */ b8 ^= 0200; /* Yes, invert the 8th bit's value, */ ssflg = 1; /* remember we did this, */ a = *xdbuf++ & 0xFF; /* and get the prefixed character. */ len--; } else ssflg = 0; ccpflg = 0; if (a == ctlq) { /* If control prefix, */ a = *xdbuf++ & 0xFF; /* get its operand */ len--; a7 = a & 0x7F; /* and its low 7 bits. */ if ((a7 >= 0100 && a7 <= 0137) || a7 == '?') { /* Controllify */ a = ctl(a); /* if in control range. */ a7 = a & 0x7F; ccpflg = 1; /* Note that we did this */ ccp++; /* Count for stats */ } } else a7 = a & 0x7f; /* Not control quote */ if (a7 < 32 || a7 == 127) { /* Control character? */ if (!ccpflg) ccu++; /* A bare one, count it */ if (lscapu) { /* If doing locking shifts... */ if (lsstate) /* If SHIFTED */ a8 = (a & ~b8) & 0xFF; /* Invert meaning of 8th bit */ else /* otherwise */ a8 = a | b8; /* OR in 8th bit */ /* If we're not in a quoted sequence */ if (!lsquote && (!lsstate || !ssflg)) { if (a8 == DLE) { /* Check for DLE quote */ lsquote = 1; /* prefixed by single shift! */ continue; } else if (a8 == SO) { /* Check for Shift-Out */ lsstate = 1; /* SHIFT-STATE = SHIFTED */ continue; } else if (a8 == SI) { /* or Shift-In */ lsstate = 0; /* SHIFT-STATE = UNSHIFTED */ continue; } } else lsquote = 0; } } a |= b8; /* OR in the 8th bit */ if (rpt == 0) rpt = 1; /* If no repeats, then one */ #ifndef NOCSETS if (!binary) { /* If in text mode, */ if (tcharset != TC_UCS2) { if (feol && a == CR) /* Convert CRLF to newline char */ continue; if (feol && a == LF) a = feol; } if (xlatype == XLA_BYTE) /* Byte-for-byte - do it now */ if (xlate && rx) a = (*rx)((CHAR) a); } #endif /* NOCSETS */ /* (PWP) Decoding speedup via buffered output and a macro... */ if (fn == putfil) { for (; rpt > 0; rpt--) { /* Output the char RPT times */ #ifdef CALIBRATE if (calibrate) { ffc++; continue; } #endif /* CALIBRATE */ /* Note: The Unicode and Kanji sections can probably be combined now; */ /* the Unicode method (xpnbyte()) covers Kanji too. */ #ifdef UNICODE if (!binary && xlatype == XLA_UNICODE) t = xpnbyte((unsigned)((unsigned)a & 0xff), tcharset, fcharset, fn ); else #endif /* UNICODE */ #ifdef KANJI if (!binary && tcharset == TC_JEUC && fcharset != FC_JEUC) { /* Translating from J-EUC */ if (!ffc) xkanjf(); if (xkanji(a,fn) < 0) /* to something else? */ return(-1); else t = 1; } else #endif /* KANJI */ { #ifdef OS2 if (xflg && !remfile) { /* Write to virtual screen */ char _a; _a = a & fmask; t = conoc(_a); if (t < 1) t = -1; } else #endif /* OS2 */ t = zmchout(a & fmask); /* zmchout is a macro */ } if (t < 0) { debug(F101,"decode write errno","",errno); return(-1); } #ifdef UNICODE if (xlatype != XLA_UNICODE || binary) { ffc++; /* Count the character */ if (docrc && !xflg && !remfile) { /* Update file CRC */ c = a; /* Force conversion to unsigned char */ z = crc16 ^ (long)c; crc16 = (crc16 >> 8) ^ (crcta[(z & 0xF0) >> 4] ^ crctb[z & 0x0F]); } } #endif /* UNICODE */ } } else { /* Output to something else. */ a &= fmask; /* Apply file mask */ for (; rpt > 0; rpt--) { /* Output the char RPT times */ #ifdef CALIBRATE if (calibrate) { ffc++; continue; } #endif /* CALIBRATE */ if ((*fn)((char) a) < 0) return(-1); /* Send to output func. */ } } #ifdef CK_CTRLZ lastchar = a; #endif /* CK_CTRLZ */ } return(0); } /* G E T P K T -- Fill a packet data field */ /* Gets characters from the current source -- file or memory string. Encodes the data into the packet, filling the packet optimally. Set first = 1 when calling for the first time on a given input stream (string or file). Call with: bufmax -- current send-packet size xlate -- flag: 0 to skip character-set translation, 1 to translate Uses global variables: t -- current character. first -- flag: 1 to start up, 0 for input in progress, -1 for EOF. next -- next character (not used any more). data -- pointer to the packet data buffer. size -- number of characters in the data buffer. memstr - flag that input is coming from a memory string instead of a file. memptr - pointer to string in memory. (*sx)() character set translation function Returns: The size as value of the function, and also sets global "size", and fills (and null-terminates) the global data array. Returns: 0 on EOF. -1 on fatal (internal) error. -3 on timeout (e.g. when reading data from a pipe). Rewritten by Paul W. Placeway (PWP) of Ohio State University, March 1989. Incorporates old getchx() and encode() inline to reduce function calls, uses buffered input for much-improved efficiency, and clears up some confusion with line termination (CRLF vs LF vs CR). Rewritten again by Frank da Cruz to incorporate locking shift mechanism, May 1991. And again in 1998 for efficiency, etc, with a separate bgetpkt() split out for binary-mode no-parity transfers. */ /* Note: Separate Kanji support dates from circa 1991 and now (1999) can most likely be combined with the the Unicode support: the xgnbyte()/xpnbyte() mechanism works for both Unicode and Kanji. */ #ifdef KANJI int kgetf( #ifdef CK_ANSIC VOID #endif /* CK_ANSIC */ ) { if (funcstr) return((*funcptr)()); else return(zminchar()); } int kgetm( #ifdef CK_ANSIC VOID #endif /* CK_ANSIC */ ) { int x; if ((x = *memptr++)) return(x); else return(-1); } #endif /* KANJI */ /* Lookahead function to decide whether locking shift is worth it. Looks at the next four input characters to see if all of their 8th bits match the argument. Call with 0 or 0200. Returns 1 on match, 0 if they don't match. If we don't happen to have at least 4 more characters waiting in the input buffer, returns 1. Note that zinptr points two characters ahead of the current character because of repeat-count lookahead. */ int lslook(b) unsigned int b; { /* Locking Shift Lookahead */ int i; if (zincnt < 3) /* If not enough chars in buffer, */ return(1); /* force shift-state switch. */ b &= 0200; /* Force argument to proper form. */ for (i = -1; i < 3; i++) /* Look at next 5 characters to */ if (((*(zinptr+i)) & 0200) != b) /* see if all their 8th bits match. */ return(0); /* They don't. */ return(1); /* They do. */ } /* Routine to compute maximum data length for packet to be filled */ int maxdata() { /* Get maximum data length */ int n, len; debug(F101,"maxdata spsiz 1","",spsiz); if (spsiz < 0) /* How could this happen? */ spsiz = DSPSIZ; debug(F101,"maxdata spsiz 2","",spsiz); n = spsiz - 5; /* Space for Data and Checksum */ if (n > 92 && n < 96) n = 92; /* "Short" Long packets don't pay */ if (n > 92 && lpcapu == 0) /* If long packets needed, */ n = 92; /* make sure they've been negotiated */ len = n - bctl; /* Space for data */ if (n > 92) len -= 3; /* Long packet needs header chksum */ debug(F101,"maxdata len 1","",len); if (len < 0) len = 10; debug(F101,"maxdata len 2","",len); return(len); } static CHAR leftover[9] = { '\0','\0','\0','\0','\0','\0','\0','\0','\0' }; static int nleft = 0; #ifdef CKTUNING /* When CKTUNING is defined we use this special trimmed-down version of getpkt to speed up binary-mode no-parity transfers. When CKTUNING is not defined, or for text-mode or parity transfers, we use the regular getpkt() function. Call just like getpkt() but test first for transfer mode and parity. NOTE: This routine is only to be called when sending a real file -- not for filenames, server responses, etc, because it only reads from the input file. See getpkt() for more detailed commentary. */ static int bgetpkt(bufmax) int bufmax; { register CHAR rt = t, rnext; register CHAR *dp, *odp, *p1, *p2; register int x = 0, a7; CHAR xxrc, xxcq; /* Pieces of prefixed sequence */ long z; /* A long worker (for CRC) */ if (!binary || parity || memstr) /* JUST IN CASE caller didn't test */ return(getpkt(bufmax,!binary)); if (!data) { debug(F100,"SERIOUS ERROR: bgetpkt data == NULL","",0); return(-1); } dp = data; /* Point to packet data buffer */ size = 0; /* And initialize its size */ bufmax = maxdata(); /* Get maximum data length */ #ifdef DEBUG if (deblog) debug(F101,"bgetpkt bufmax","",bufmax); #endif /* DEBUG */ if (first == 1) { /* If first character of this file.. */ ffc = (CK_OFF_T)0; /* reset file character counter */ #ifdef COMMENT /* Moved to below */ first = 0; /* Next character won't be first */ #endif /* COMMENT */ *leftover = '\0'; /* Discard any interrupted leftovers */ nleft = 0; /* Get first character of file into rt, watching out for null file */ #ifdef CALIBRATE if (calibrate) { #ifdef NORANDOM rt = 17; #else rt = cal_a[rand() & 0xff]; #endif /* NORANDOM */ first = 0; } else #endif /* CALIBRATE */ if ((x = zminchar()) < 0) { /* EOF or error */ if (x == -3) { /* Timeout. */ size = (dp - data); debug(F101,"bgetpkt timeout size","",size); return((size == 0) ? x : size); } first = -1; size = 0; if (x == -2) { /* Error */ debug(F100,"bgetpkt: input error","",0); cxseen = 1; /* Interrupt the file transfer */ } else { debug(F100,"bgetpkt empty file","",0); } return(0); } first = 0; /* Next char will not be the first */ ffc++; /* Count a file character */ rt = (CHAR) x; /* Convert int to char */ if (docrc && (what & W_SEND)) { /* Accumulate file crc */ z = crc16 ^ (long)rt; crc16 = (crc16 >> 8) ^ (crcta[(z & 0xF0) >> 4] ^ crctb[z & 0x0F]); } rt &= fmask; /* Apply SET FILE BYTESIZE mask */ } else if (first == -1 && nleft == 0) { /* EOF from last time */ return(size = 0); } /* Here we handle characters that were encoded for the last packet but did not fit, and so were saved in the "leftover" array. */ if (nleft) { for (p1 = leftover; nleft > 0; nleft--) /* Copy leftovers */ *dp++ = *p1++; *leftover = '\0'; /* Delete leftovers */ nleft = 0; } if (first == -1) /* Handle EOF */ return(size = (dp - data)); /* Now fill up the rest of the packet. */ rpt = 0; /* Initialize character repeat count */ while (first > -1) { /* Until EOF... */ #ifdef CALIBRATE if (calibrate) { /* We generate our own "file" */ if (ffc >= calibrate) { /* EOF */ first = -1; ffc--; } else { /* Generate next character */ if (cal_j > CAL_M * ffc) cal_j = cal_a[ffc & 0xff]; x = (unsigned)cal_a[(cal_j & 0xff)]; if (x == rt) x ^= 2; } ffc++; cal_j += (unsigned int)(ffc + CAL_O); } else #endif /* CALIBRATE */ if ((x = zminchar()) < 0) { /* Check for EOF */ if (x == -3) { /* Timeout. */ t = rt; size = (dp-data); debug(F101,"bgetpkt timeout size","",size); return((size == 0) ? x : size); } first = -1; /* Flag eof for next time. */ if (x == -2) cxseen = 1; /* If error, cancel this file. */ } else { ffc++; /* Count the character */ if (docrc && (what & W_SEND)) { /* Accumulate file crc */ z = crc16 ^ (long)((CHAR)x & 0xff); crc16 = (crc16 >> 8) ^ (crcta[(z & 0xF0) >> 4] ^ crctb[z & 0x0F]); } } rnext = (CHAR) (x & fmask); /* Apply file mask */ /* At this point, the character we just read is in rnext, and the character we are about to encode into the packet is in rt. */ odp = dp; /* Remember where we started. */ xxrc = xxcq = NUL; /* Clear these. */ /* Now encode the character according to the options that are in effect: ctlp[]: whether this control character needs prefixing. rptflg: repeat counts enabled. Other options don't apply in this routine. */ if (rptflg && (rt == rnext) && (first == 0)) { /* Got a run... */ if (++rpt < 94) { /* Below max, just count */ continue; /* go back and get another */ } else if (rpt == 94) { /* Reached max, must dump */ xxrc = (CHAR) tochar(rpt); /* Put the repeat count here */ rptn += rpt; /* Accumulate it for statistics */ rpt = 0; /* And reset it */ } } else if (rpt > 0) { /* End of run */ xxrc = (CHAR)tochar(++rpt); /* The count */ rptn += rpt; /* For stats */ rpt = 0; /* Reset repeat count */ } a7 = rt & 0177; /* Get low 7 bits of character */ if ( #ifdef CK_SPEED ctlp[(unsigned)(rt & 0xff)] /* Lop off any "sign" extension */ #else (a7 < SP) || (a7 == DEL) #endif /* CK_SPEED */ ) { /* Do control prefixing if necessary */ xxcq = myctlq; /* The prefix */ ccp++; /* Count it */ rt = (CHAR) ctl(rt); /* Uncontrollify the character */ } #ifdef CK_SPEED else if ((a7 < SP) || (a7 == DEL)) /* Count an unprefixed one */ ccu++; #endif /* CK_SPEED */ if (a7 == myctlq) /* Always prefix the control prefix */ xxcq = myctlq; if ((rptflg) && (a7 == rptq)) /* If it's the repeat prefix, */ xxcq = myctlq; /* prefix it if doing repeat counts */ /* Now construct the prefixed sequence */ if (xxrc) { /* Repeat count */ #ifdef COMMENT if (xxrc == (CHAR) '"' && !xxcq) { /* 2 in a row & not prefixed */ *dp++ = rt; /* So just do this */ } else { /* More than two or prefixed */ *dp++ = (CHAR) rptq; *dp++ = xxrc; /* Emit repeat sequence */ } #else /* CHECK THIS */ if (xxrc == (CHAR) '"' && !xxcq) { /* 2 in a row & not prefixed */ if (dp == data) { *dp++ = rt; /* So just do this */ } else if (*(dp-1) == rt) { *dp++ = (CHAR) rptq; *dp++ = xxrc; /* Emit repeat sequence */ } else { *dp++ = rt; /* So just do this */ } } else { /* More than two or prefixed */ *dp++ = (CHAR) rptq; *dp++ = xxrc; /* Emit repeat sequence */ } #endif /* COMMENT */ } if (xxcq) { *dp++ = myctlq; } /* Control prefix */ *dp++ = rt; /* Finally, the character itself */ rt = rnext; /* Next character is now current. */ /* Done encoding the character. Now take care of packet buffer overflow. */ size = dp - data; /* How many bytes we put in buffer. */ if (size >= bufmax) { /* If too big, save some for next. */ *dp = '\0'; /* Mark the end. */ if (size > bufmax) { /* if packet is overfull */ /* Copy the part that doesn't fit into the leftover buffer, */ /* taking care not to split a prefixed sequence. */ int i; nleft = dp - odp; p1 = leftover; p2 = odp; for (i = 0; i < nleft; i++) *p1++ = *p2++; size = odp - data; /* Return truncated packet. */ *odp = '\0'; /* Mark the new end */ } t = rt; /* Save for next time */ return(size); } } /* Otherwise, keep filling. */ size = dp - data; /* End of file */ *dp = '\0'; /* Mark the end of the data. */ return(size); /* Return partially filled last packet. */ } #endif /* CKTUNING */ VOID dofilcrc(c) int c; { /* Accumulate file crc */ long z; z = crc16 ^ (long)c; crc16 = (crc16 >> 8) ^ (crcta[(z & 0xF0) >> 4] ^ crctb[z & 0x0F]); } /* For SENDing from an array... */ int agnbyte() { /* Get next byte from array */ #ifndef NOSPL char c; static int save = 0; /* For CRLF */ static char ** ap = NULL; /* Array pointer */ static char * p = NULL; /* Character pointer */ static int i = 0, n = 0; /* Array index and limit */ extern int a_dim[]; /* Array dimension */ if (!ap) { /* First time thru */ ap = sndarray; /* Set up array pointers */ if (!ap || (i = sndxlo) > a_dim[sndxin]) { sndarray = NULL; ap = NULL; return(-1); } p = ap[i]; /* Point to first element in range */ n = sndxhi; /* Index of last element in range */ if (sndxhi > a_dim[sndxin]) /* Adjust if necessary */ n = a_dim[sndxin]; } if (save) { /* If anything saved */ c = save; /* unsave it */ save = 0; /* and return it */ return(c & 0xff); } if (i > n) { /* No more elements */ sndarray = NULL; ap = NULL; return(-1); } if (!p) /* Source pointer is NULL */ c = NUL; /* this means an empty line */ else /* Source pointer not NULL */ c = *p++; /* Next char */ if (!c) { /* Char is empty? */ if (!binary) { /* Text: end of line. */ if (feol) { /* Supply NL */ c = feol; } else { /* or CRLF */ save = LF; c = CR; } p = ap[++i]; return(c & 0xff); } while (i++ < n) { /* Binary - get next element */ p = ap[i]; if (!p) /* Empty line? */ continue; /* Ignore it and get another */ c = *p++; /* Get next char */ if (!c) /* Emtpy char? */ continue; /* Ignore it and get another */ return(c & 0xff); /* Not empty - return it */ } sndarray = NULL; ap = NULL; return(-1); /* Done */ } return(c & 0xff); /* Char is not empty */ #else sndarray = NULL; return(-1); #endif /* NOSPL */ } #endif /* NOXFER */ #ifndef NOCSETS static CHAR xlabuf[32] = { 0, 0, 0, 0, 0, 0, 0, 0 }; static int xlacount = 0; static int xlaptr = 0; /* static USHORT lastucs2 = 0; */ /* X G N B Y T E -- Get next translated byte from the input file. Returns the next byte that is to be put into the packet, already translated. This isolates getpkt() from having to know anything about translation, single- vs multibyte character sets, one-to-many vs many-to-one, etc, but it has rather high overhead, so don't call it unless you know translation is needed to or from Unicode, Japanese, or other multibyte character set. Call with: fcs: File character set (source, file we are reading from) tcs: Target character set (use an FC_xxx code, not a TC_xxx code) Returns: >= 0: A translated byte suitable for writing. < 0: Fatal error (such as EOF on input source). As of Sat Sep 7 18:37:41 2002: When the output character-set is UCS-2, bytes are ALWAYS returned in big-endian order (previously they could also be returned in LE order under certain conditions, which was just way too confusing). */ int #ifdef CK_ANSIC xgnbyte(int tcs, int fcs, int (*fn)(void)) #else /* CK_ANSIC */ xgnbyte(tcs,fcs,fn) int tcs, fcs, (*fn)(); #endif /* CK_ANSIC */ /* xgnbyte */ { _PROTOTYP( int (*xx), (USHORT) ) = NULL; int haveuc = 0; /* Flag for have Unicode character */ #ifdef KANJI int havesj = 0; /* Have Shift-JIS character */ int haveeu = 0; /* Have EUC-JP character */ #endif /* KANJI */ int rc = -1, x = 0, flag = 0; int utferror = 0; int eolflag = 0; unsigned int xc, thischar; static int swapping = 0; CHAR rt; /* USHORT ch; */ #ifdef UNICODE union ck_short uc; #endif /* UNICODE */ #ifdef KANJI union ck_short sj, eu; /* Shift-JIS character */ #endif /* KANJI */ #ifdef KANJI sj.x_short = 0; #endif /* KANJI */ #ifdef DEBUG if (deblog && !ffc) { debug(F101,"xgnbyte initial swap","",swapping); } #endif /* DEBUG */ if (xlacount-- > 0) { /* We already have some */ x = xlabuf[xlaptr++]; debug(F001,"xgnbyte from buf","",x); return(x); } if (xlatype != XLA_NONE) { /* Not not translating... */ haveuc = 0; #ifdef UNICODE if (fcs == FC_UCS2) { /* UCS-2: Read two bytes */ if (!ffc) /* Beginning of file? */ swapping = 0; /* Reset byte-swapping flag */ uc.x_short = 0; bomskip: x = fn ? (*fn)() : zminchar(); /* Get first byte */ debug(F001,"zminchar swapping","",swapping); debug(F001,"zminchar C0","",x); flag = 1; /* Remember we called zminchar() */ if (x > -1) { /* Didn't fail */ ffc++; /* Count a file byte */ uc.x_char[swapping] = x & 0xff; #ifndef NOXFER if (docrc && (what & W_SEND)) dofilcrc(x); #endif /* NOXFER */ x = fn ? (*fn)() : zminchar(); /* Get second byte */ if (x > -1) { /* If didn't fail */ debug(F001,"zminchar C1","",x); ffc++; /* count another file byte */ uc.x_char[1-swapping] = x & 0xff; haveuc = 1; /* And remember we have Unicode */ #ifndef NOXFER if (docrc && (what & W_SEND)) dofilcrc(x); #endif /* NOXFER */ if (ffc == (CK_OFF_T)2) { /* Second char of file */ debug(F001,"xgnbyte 1st UCS2","",uc.x_short); debug(F111,"xgnbyte fileorder","A",fileorder); if (fileorder < 0) /* Byte order of this file */ fileorder = ucsorder; /* Default is ucsorder */ if (fileorder > 1) fileorder = 1; debug(F111,"xgnbyte fileorder","B",fileorder); if (uc.x_short == (USHORT)0xfeff) { swapping = 0; debug(F101, "xgnbyte UCS2 goodbom swap","",swapping); fileorder = byteorder; /* Note: NOT 0 */ goto bomskip; } else if (uc.x_short == (USHORT)0xfffe) { swapping = 1; debug(F101, "xgnbyte UCS2 badbom swap","",swapping); fileorder = (1 - byteorder); /* Note: NOT 1 */ goto bomskip; } else if ((byteorder && !fileorder) || /* No BOM */ (!byteorder && fileorder > 0)) { /* fileorder might have been set by scanfile() */ CHAR c; c = uc.x_char[0]; uc.x_char[0] = uc.x_char[1]; uc.x_char[1] = c; swapping = 1; debug(F111,"xgnbyte UCS2 noBOM swap","A",swapping); } else { swapping = 0; debug(F111,"xgnbyte UCS2 noBOM swap","B",swapping); } debug(F111,"xgnbyte fileorder","C",fileorder); } } else return(x); } else return(x); debug(F001,"xgnbyte UCS2","",uc.x_short); } else if (fcs == FC_UTF8) { /* File is UTF-8 */ CHAR ch = 0; /* Data types needed for API... */ USHORT * us = NULL; uc.x_short = 0; flag = 1; /* We (will) have called zminchar() */ /* Read source bytes */ while ((x = fn ? (*fn)() : zminchar()) > -1) { ffc++; /* Got a byte - count it */ #ifndef NOXFER if (docrc && (what & W_SEND)) dofilcrc(x); #endif /* NOXFER */ ch = x; rc = utf8_to_ucs2(ch,&us); /* Convert to UCS-2 */ if (rc == 0) { /* Done */ uc.x_short = *us; haveuc = 1; break; } else if (rc < 0) { /* Error */ utferror = 1; debug(F101,"xgnbyte UTF-8 input error","",rc); haveuc = 1; uc.x_short = *us; break; } } if (x < 0) return(x); debug(F001,"xgnbyte UTF8->UCS2","",uc.x_short); } #endif /* UNICODE */ #ifdef KANJI #ifdef UNICODE else #endif /* UNICODE */ if (fcsinfo[fcs].alphabet == AL_JAPAN) { /* Japanese source file */ int c7, x, y; if (fcs == FC_JIS7) { /* If file charset is JIS-7 */ if (!ffc) /* If first byte of file */ j7init(); /* Initialize JIS-7 parser */ x = getj7(); /* Get a JIS-7 byte */ } else /* Otherwise */ x = fn ? (*fn)() : zminchar(); /* Just get byte */ if (x < 0) { /* Propogate EOF or error */ debug(F100,"XGNBYTE EOF","",0); return(x); } debug(F001,"XGNBYTE x","",x); ffc++; /* Count */ #ifndef NOXFER if (docrc && (what & W_SEND)) dofilcrc(x); /* Do CRC */ #endif /* NOXFER */ switch (fcs) { /* What next depends on charset */ case FC_SHJIS: /* Shift-JIS */ if ((x <= 0x80) || /* Any 7-bit char... */ (x >= 0xa0 && x <= 0xdf)) { /* or halfwidth Katakana */ sj.x_short = (USHORT) x; /* we read one byte. */ } else { /* Anything else */ if ((y = fn ? (*fn)() : zminchar()) < 0) /* get another */ return(y); #ifndef NOXFER if (docrc && (what & W_SEND)) dofilcrc(y); #endif /* NOXFER */ ffc++; sj.x_char[byteorder] = (CHAR) x; sj.x_char[1-byteorder] = (CHAR) y; } break; case FC_JIS7: /* JIS-7 */ case FC_JDEC: /* DEC Kanji */ case FC_JEUC: /* EUC-JP */ if ((x & 0x80) == 0) { /* Convert to Shift-JIS */ sj.x_short = (USHORT) x; /* C0 or G0: one byte */ eu.x_short = (USHORT) x; haveeu = 1; } else { c7 = x & 0x7f; if (c7 > 0x20 && c7 < 0x7f) { /* Kanji: two bytes */ if ((y = (fcs == FC_JEUC) ? (fn ? (*fn)() : zminchar()) : getj7() /* ^^^ */ ) < 0) return(y); ffc++; #ifndef NOXFER if (docrc && (what & W_SEND)) dofilcrc(y); #endif /* NOXFER */ eu.x_char[byteorder] = (CHAR) x; eu.x_char[1-byteorder] = (CHAR) y; sj.x_short = eu_to_sj(eu.x_short); haveeu = 1; } else if (x == 0x8e) { /* SS2 Katakana prefix: 2 bytes */ if ((y = (fcs == FC_JIS7) ? getj7() : /* ^^^ */ (fn ? (*fn)() : zminchar()) ) < 0) return(y); ffc++; #ifndef NOXFER if (docrc && (what & W_SEND)) dofilcrc(y); #endif /* NOXFER */ sj.x_short = y | 0x80; debug(F001,"XGNBYTE KANA SJ","",sj.x_short); } else { /* Something that translates to U+FFFD */ sj.x_short = UNKSJIS; } } break; } havesj = 1; /* Have Shift-JIS */ #ifdef UNICODE uc.x_short = sj_to_un(sj.x_short); /* Translate to UCS-2 */ haveuc = 1; /* Have Unicode */ #endif /* UNICODE */ flag = 1; /* Have a char */ } #endif /* KANJI */ } if (!flag) { /* If no character was read yet... */ if ((x = (fn ? (*fn)() : zminchar())) > -1) /* read one now */ ffc++; debug(F101,"xgnbyte zminchar 1","",x); if (x < 0) return(x); haveuc = 0; } #ifdef UNICODE if (haveuc) { thischar = uc.x_short; /* lastucs2 = uc.x_short; */ } else #endif /* UNICODE */ thischar = x; debug(F001,"xgnbyte thischar",haveuc ? "[UNICODE]" : "[other]",thischar); #ifdef CK_CTRLZ /* SET EOF CTRLZ */ if (eofmethod == XYEOF_Z && !binary && thischar == 26) { debug(F100,"xgnbyte EOF on Ctrl-Z 1","",0); return(-1); } #endif /* CK_CTRLZ */ #ifdef UNICODE if (!haveuc) /* If not Unicode... */ #endif /* UNICODE */ x &= fmask; /* Apply SET FILE BYTESIZE mask */ switch (xlatype) { /* Translation type... */ #ifdef UNICODE case XLA_UNICODE: { /* Unicode is involved */ xc = 0; /* Here we must choose the appropriate translation function. If we are being called by getpkt() (i.e. when transferring a file), we are translating from Unicode to the Transfer Character Set and therefore must use the function pointed to by xut. Otherwise, e.g. during TRANSLATE, CONNECT, TRANSMIT, etc, we are translating from Unicode to the File Character Set and so must call the function pointed to by xuf. There might be a cleaner way to set this up but I don't think so. For example, setxlatype() might have been called too soon and so might not have known whether it was a file transfer or a local operation. */ /* (Many years later...) In testing this code I noticed that TRANSLATE'ing Russian text from UTF-8 to ISO Latin/Cyrillic produced all question marks. Rereading the previous paragraph it seems to me we are (I am) overloading this function with responsibilites, satisfying the needs of file transfer (local file charset -> transfer charset for outbound packet) and local file conversion. In the case of TRANSLATE, we call (xgnbyte(), xpnbyte()) in a loop, expecting the xgnbyte() will feed UCS2 to xpnbyte(). But the following code does what xpnbyte() is going to do, returning (in this case) an ISO Latin/Cyrillic byte stream, which xpnbyte() believes to be UCS2, and comes up with nonsense. Not wanting to rip the whole thing apart and start over, I made the following change that should do no harm, upon observing that if the input character set is UTF-8 or UCS-2, then when we get here it has already been converted to UCS2, so if we are not transferring a file, we don't need to do anything else except put the bytes in the right place to be returned, which is done further along. */ #ifdef COMMENT /* Previous code */ xx = (what & W_SEND) ? xut : xuf; #else /* New code 2011-06-03 */ if (what & W_SEND) { xx = xut; } else { if (fcs == FC_UCS2 || fcs == FC_UTF8) xx = NULL; else xx = xuf; } #endif /* COMMENT */ eolflag = 0; if (haveuc) { /* File is Unicode */ /* See Unicode TR13, "Converting to Other Character Sets" */ if (uc.x_short == 0x2028 || /* Line Separator? */ uc.x_short == 0x2029 || /* Paragraph Separator */ (feol && (uc.x_short == (USHORT)feol)) ) { debug(F001,"xgnbyte uc eol","",uc.x_short); rc = 0; eolflag = 1; /* Don't translate and handle later */ } if (xx && !eolflag) { /* UCS-to-TCS function (UCS->byte) */ rc = (*xx)(uc.x_short); /* These can fail... */ debug(F101,"xgnbyte xx rc","",rc); if (rc < 0) /* If it can't be translated */ uc.x_short = UNK; /* Put unknown-character symbol */ else uc.x_short = (unsigned)((unsigned)rc & 0xffff); debug(F101,"xgnbyte xx uc","",uc.x_short); } #ifdef KANJI if (tcs == FC_JEUC) { /* Translating to EUC-JP */ USHORT sj = 0; union ck_short eu; debug(F001,"xgnbyte UCS->EUC UCS","",uc.x_short); if (!havesj) /* If we don't already have it */ sj = un_to_sj(uc.x_short); /* convert to Shift-JIS */ eu.x_short = sj_to_eu(sj); debug(F001,"xgnbyte UCS->EUC EUC","",eu.x_short); xlaptr = 0; xlacount = 0; if (eolflag) { if (what & W_SEND) { xlabuf[xlacount++] = LF; return(CR); } else { return(feol); } } if (eu.x_char[byteorder]) { /* Two bytes */ rc = eu.x_char[byteorder]; xlabuf[xlacount++] = eu.x_char[1-byteorder]; debug(F001,"xgnbyte UCS->EUC xlabuf[0]","",xlabuf[0]); } else { /* One byte */ rc = eu.x_char[1-byteorder]; } debug(F101,"xgnbyte UCS->EUC xlacount","",xlacount); debug(F001,"xgnbyte UCS->EUC rc","",rc); return(rc); } else #endif /* KANJI */ if (tcs != FC_UCS2 && tcs != FC_UTF8) { if (uc.x_short & 0xff00) { /* Decoding error */ debug(F001,"xgnbyte decoding error","",uc.x_short); return(-2); } else return((unsigned int)(uc.x_short & 0xff)); } xc = uc.x_short; } else { /* File is not Unicode */ USHORT ch; /* Translate from single FCS byte to UCS-2 */ /* This is a bit nonobvious... The blah_u() (Blah-to-Unicode) routines are called only with pieces of character sets, in the ISO 2022 sense. So, for example, if ch is a Latin-1 character, we call the translation routine only if it is in the right half; if it's in the left half, it isn't translated, and in fact will give the wrong result if sent to the translation function. That's because those functions were designed for use with the ISO 2022 G0..G3 sets, not for file transfer. On the other hand, if it's a 7-bit character set, we *do* call the translation function. (To put it another way, the left half of any 8-bit character set is ASCII and therefore doesn't need to be translated but 7-bit sets such as ISO 646 German do need translation). */ ch = (unsigned)(thischar & 0xff); if (((fcsinfo[fcs].size > 128) && (ch & 0x80)) || fcsinfo[fcs].size <= 128) { if (xfu) { /* FCS-to-UCS function */ ch = (*xfu)(ch); } } xc = ch; } /* At this point we have a UCS-2 character in native format */ /* (Big Endian or Little Endian) in xc, which is an unsigned int. */ debug(F001,"xgnbyte xc","",xc); if (tcs == FC_UTF8) { /* Now convert to UTF-8 */ USHORT c; /* NOTE: this is FC_UTF8 on purpose! */ CHAR * buf = NULL; int i, k = 0, x; xlaptr = 0; if (utferror) { xlabuf[k++] = 0xff; xlabuf[k++] = 0xbd; } if (eolflag) { /* We detected EOL in source file */ if (what & W_SEND) { /* Convert to CRLF */ xlabuf[k++] = LF; xlacount = k; return((unsigned int)CR); #ifdef COMMENT } else { /* Or to local line-end */ xlacount = k; return((unsigned int)feol); #endif /* COMMENT */ } } c = xc; if ((x = ucs2_to_utf8(c,&buf)) < 1) { debug(F101,"xgnbyte ucs2_to_utf8 error","",c); return(-2); } debug(F101,"xgnbyte UTF8 buf[0]","",buf[0]); for (i = 1; i < x; i++) { xlabuf[k+i-1] = buf[i]; debug(F111,"xgnbyte UTF8 xlabuf",ckitoa(i-1),buf[i]); } xlaptr = 0; xlacount = x - 1; debug(F101,"xgnbyte UTF8 xlacount","",xlacount); return((unsigned int)buf[0]); } else { /* Or keep it as UCS-2 */ int k = 0; CHAR c; xlaptr = 0; if (utferror) { xlabuf[k++] = 0xff; xlabuf[k++] = 0xfd; debug(F101,"xgnbyte error","",k); } if (eolflag) { /* We detected EOL in source file */ if (what & W_SEND) { /* Convert to CRLF */ xlabuf[k++] = CR; xlabuf[k++] = NUL; xlabuf[k++] = LF; xlacount = k; debug(F101,"xgnbyte send CRLF","",k); return(0); /* Return NUL */ } else { /* Or to local line-end */ #ifdef COMMENT /* This bypasses byte swapping that we might need */ xlabuf[k++] = (CHAR)feol; xlacount = k; debug(F101,"xgnbyte send feol","",k); return(0); /* Return NUL */ #else xc = (CHAR)feol; #endif /* COMMENT */ } } /* In which order should we return the bytes? */ #ifdef COMMENT if ( (what & W_SEND) || (what & W_FTP) || (fileorder == 0)) { #endif /* COMMENT */ /* ALWAYS RETURN IN BIG ENDIAN ORDER... 7 Sep 2002 */ /* xgnbyte() is almost always used to feed xpnbyte() */ /* which requires bytes in BE order. In cases where */ /* xgnbyte is used in isolation, the caller can swap */ /* bytes itself afterwards. */ xlabuf[k++] = (xc >> 8) & 0xff; /* Big Endian */ xlabuf[k++] = xc & 0xff; debug(F001,"xgnbyte->UCS2BE", ckitox((int)xlabuf[0]),xlabuf[1]); #ifdef COMMENT } else { /* Little Endian */ xlabuf[k++] = xc & 0xff; xlabuf[k++] = (xc >> 8) & 0xff; debug(F001,"xgnbyte->UCS2LE", ckitox((int)xlabuf[0]),xlabuf[1]); } #endif /* COMMENT */ c = xlabuf[0]; xlaptr = 1; xlacount = k-1; debug(F101,"xgnbyte c","",c); debug(F101,"xgnbyte xlaptr","",xlaptr); debug(F011,"xgnbyte xlabuf",xlabuf,xlacount); return((unsigned int)c); } } #endif /* UNICODE */ case XLA_NONE: return((fn ? (*fn)() : zminchar())); case XLA_BYTE: /* Byte-for-Byte translation */ rt = x; if (sx) rt = (*sx)(rt); #ifdef UNICODE if (utferror) { xlaptr = 0; xlacount = 1; xlabuf[0] = rt; return(UNK); } else #endif /* UNICODE */ return((unsigned int)rt); #ifdef KANJI case XLA_JAPAN: /* Come here with Shift-JIS */ if (tcs == FC_JEUC) { /* It better be... */ xlaptr = 0; xlacount = 0; if (!havesj) { printf("BAD BAD\n"); return(-2); } if (!haveeu) /* We might already have EUC too */ eu.x_short = sj_to_eu(sj.x_short); if (eu.x_char[byteorder]) { xlabuf[xlacount++] = eu.x_char[1-byteorder]; return(eu.x_char[byteorder]); } else { return(eu.x_char[1-byteorder]); } break; } #endif /* KANJI */ default: debug(F101,"xgnbyte bad xlatype","",xlatype); return(-2); } #ifdef COMMENT /* If there is a return() statement here, some compilers complain about "statement not reached". If there is no return() statement, other compilers complain that "Non-void function should return a value". There is no path through this function that falls through to here. */ debug(F100,"xgnbyte switch failure","",0); return(-2); #endif /* COMMENT */ } #endif /* NOCSETS */ #ifndef NOXFER /* G E T P K T -- Fill a packet data field from the indicated source. */ /* Parameters: bufmax: Maximum length of entire packet. xlate: Flag for whether to translate charsets when in text mode. Returns: Number of characters written to packet data field, 0 or more, Or -1 on failure (internal error), or -3 on timeout (e.g. when reading from a pipe). This is the full version allowing for parity and text-mode conversions; i.e. it works in all cases. Also see bgetpkt(), a special lean/mean/fast packet encoder that works only for binary-mode no-parity transfers. */ static int uflag = 0; int getpkt(bufmax,xlate) int bufmax, xlate; { /* Fill one packet buffer */ register CHAR rt = t, rnext = NUL; /* Register shadows of the globals */ register CHAR *dp, *odp, *odp2, *p1, *p2; /* pointers... */ register int x; /* Loop index. */ register int a7; /* Low 7 bits of character */ CHAR xxls, xxdl, xxrc, xxss, xxcq; /* Pieces of prefixed sequence */ if (binary) xlate = 0; /* We don't translate if binary */ if (!data) { debug(F100,"SERIOUS ERROR: getpkt data == NULL","",0); return(-1); } dp = data; /* Point to packet data buffer */ size = 0; /* And initialize its size */ /* Assume bufmax is the receiver's total receive-packet buffer length. Our whole packet has to fit into it, so we adjust the data field length. We also decide optimally whether it is better to use a short-format or long-format packet when we're near the borderline. */ bufmax = maxdata(); /* Get maximum data length */ if (first == 1) { /* If first character of this file.. */ #ifdef UNICODE /* Special end-of-line handling for Unicode */ if (tcharset == TC_UCS2 || tcharset == TC_UTF8) uflag = 1; #endif /* UNICODE */ debug(F101,"getpkt first uflag","",uflag); debug(F101,"getpkt first rt","",rt); if (!memstr && !funcstr) /* and real file... */ ffc = (CK_OFF_T)0; /* reset file character counter */ #ifdef COMMENT /* Moved to below... */ first = 0; /* Next character won't be first */ #endif /* COMMENT */ *leftover = '\0'; /* Discard any interrupted leftovers */ nleft = 0; #ifndef NOCSETS setxlatype(tcharset,fcharset); /* Set up charset translations */ #endif /* NOCSETS */ /* Get first character of file into rt, watching out for null file */ #ifdef CALIBRATE if (calibrate && !memstr) { #ifdef NORANDOM x = rt = 53; #else x = rt = cal_a[rand() & 0xff]; #endif /* NORANDOM */ first = 0; ffc++; } else #endif /* CALIBRATE */ #ifdef KANJI if (xlate && tcharset == TC_JEUC) { /* Kanji text */ x = zkanjf(); if ((x = zkanji(memstr ? kgetm : kgetf)) < 0) { first = -1; size = 0; if (x == -2) { debug(F100,"getpkt zkanji: input error","",0); cxseen = 1; } else debug(F100,"getpkt zkanji: empty string/file","",0); return(0); } rt = x; first = 0; if (!memstr) { ffc++; if (docrc && (what & W_SEND)) /* Accumulate file crc */ dofilcrc((int)rt); } } else { /* Not Kanji text */ #endif /* KANJI */ if (memstr) { /* Reading data from memory string */ /* This will not be Unicode */ if ((rt = *memptr++) == '\0') { /* end of string ==> EOF */ first = -1; size = 0; debug(F100,"getpkt: empty string","",0); return(0); } first = 0; } else if (funcstr) { /* Reading data from a function */ /* This will not be Unicode */ if ((x = (*funcptr)()) < 0) { /* End of input */ first = -1; size = 0; /* Empty */ return(0); } ffc++; /* Count a file character */ rt = (CHAR) x; /* Convert int to char */ first = 0; debug(F000,"getpkt funcstr","",rt); } else { /* Reading data from a file */ #ifndef NOCSETS if (xlate && !binary) { /* Could be Unicode */ if (xlatype == XLA_UNICODE) { /* Get next translated byte */ x = xgnbyte(cseqtab[tcharset],fcharset,NULL); debug(F101,"getpkt xgnbyte","",x); } else { /* Not Unicode */ x = zminchar(); /* Get next byte, translate below */ debug(F101,"getpkt zminchar A","",x); } } else { /* Just get next byte */ #endif /* NOCSETS */ x = zminchar(); debug(F101,"getpkt zminchar B","",x); #ifndef NOCSETS } #endif /* NOCSETS */ if (x < 0) { /* End of file or input error */ if (x == -3) { /* Timeout. */ size = (dp-data); debug(F101,"getpkt timeout size","",size); return((size == 0) ? x : size); } first = -1; size = 0; if (x == -2) { /* Error */ debug(F100,"getpkt: input error","",0); cxseen = 1; /* Interrupt the file transfer */ } else { debug(F100,"getpkt empty file","",0); } return(0); } first = 0; /* Next character won't be first */ rt = (CHAR) x; /* Convert int to char */ #ifndef NOCSETS if (xlatype != XLA_UNICODE || binary) { ffc++; if (sx) rt = (*sx)(rt); if (docrc && (what & W_SEND)) dofilcrc(x); } #endif /* NOCSETS */ #ifdef DEBUG if (deblog) debug(F101,"getpkt 1st char","",rt); #endif /* DEBUG */ if (/* !haveuc && */ docrc && (what & W_SEND)) /* File CRC */ dofilcrc(x); } #ifdef KANJI } #endif /* KANJI */ /* PWP: handling of feol is done later (in the while loop)... */ } else if ((first == -1) && (nleft == 0)) { /* EOF from last time */ #ifdef DEBUG if (deblog) { debug(F101,"getpkt eof crc16","",crc16); debug(F101,"getpkt eof ffc","",ffc); } #endif /* DEBUG */ return(size = 0); } /* Here we handle characters that were encoded for the last packet but did not fit, and so were saved in the "leftover" array. */ debug(F101,"getpkt nleft","",nleft); if (nleft) { for (p1 = leftover; nleft > 0; nleft--) /* Copy leftovers */ *dp++ = *p1++; *leftover = '\0'; /* Delete leftovers */ nleft = 0; } if (first == -1) /* Handle EOF */ return(size = (dp - data)); /* Now fill up the rest of the packet. */ rpt = 0; /* Initialize character repeat count */ while (first > -1) { /* Until EOF... */ #ifdef CALIBRATE if (calibrate && !memstr) { /* We generate our own "file" */ if (ffc >= calibrate) { /* EOF */ first = -1; ffc--; } else { /* Generate next character */ if (cal_j > CAL_M * ffc) cal_j = cal_a[ffc & 0xff]; x = (unsigned)cal_a[(cal_j & 0xff)]; if (x == rt) x ^= 2; } cal_j += (unsigned int)(ffc + CAL_O); ffc++; } else #endif /* CALIBRATE */ #ifdef KANJI if (xlate && tcharset == TC_JEUC) { if ((x = zkanji(memstr ? kgetm : kgetf)) < 0) { first = -1; if (x == -2) cxseen = 1; } else if (!memstr) ffc++; rnext = (CHAR) (x & fmask); } else { #endif /* KANJI */ if (memstr) { /* Get next char from memory string */ if ((x = *memptr++) == '\0') /* End of string means EOF */ first = -1; /* Flag EOF for next time. */ rnext = (CHAR) (x & fmask); /* Apply file mask */ } else if (funcstr) { /* Get next char from function */ if ((x = (*funcptr)()) < 0) /* End of string means EOF */ first = -1; /* Flag EOF for next time. */ rnext = (CHAR) (x & fmask); /* Apply file mask */ } else { /* From file... */ #ifndef NOCSETS if (xlate && !binary) { /* Could be Unicode */ if (xlatype == XLA_UNICODE) { /* Get next translated byte */ x = xgnbyte(cseqtab[tcharset],fcharset,NULL); } else { /* Not Unicode */ x = zminchar(); /* Get next byte, translate below */ /* debug(F101,"xgnbyte B zminchar","",x); */ } } else { /* Just get next byte */ #endif /* NOCSETS */ x = zminchar(); /* debug(F101,"xgnbyte C zminchar","",x); */ #ifndef NOCSETS } #endif /* NOCSETS */ if (x < 0) { /* Check for EOF */ if (x == -3) { /* Timeout reading from pipe */ t = rt; size = (dp-data); debug(F101,"getpkt timeout size","",size); return((size == 0) ? x : size); } first = -1; /* Flag eof for next time. */ if (x == -2) cxseen = 1; /* If error, cancel this file. */ } rnext = (CHAR) (x & fmask); /* Apply file mask */ #ifndef NOCSETS if (xlatype != XLA_UNICODE) { #endif /* NOCSETS */ ffc++; #ifndef NOCSETS if (sx) rt = (*sx)(rt); #endif /* NOCSETS */ if (docrc && (what & W_SEND)) dofilcrc(x); #ifndef NOCSETS } #endif /* NOCSETS */ } #ifdef KANJI } #endif /* KANJI */ /* At this point, the character we just read is in rnext, and the character we are about to encode into the packet is in rt. */ odp = dp; /* Remember where we started. */ xxls = xxdl = xxrc = xxss = xxcq = NUL; /* Clear these. */ /* Now encode the character according to the options that are in effect: ctlp[]: whether this control character needs prefixing. binary: text or binary mode. rptflg: repeat counts enabled. ebqflg: 8th-bit prefixing enabled. lscapu: locking shifts enabled. */ if (rptflg) { /* Repeat processing is on? */ if (!uflag && /* * If the next char is really CRLF, then we cannot * be doing a repeat (unless CR,CR,LF which becomes * "~ CR CR LF", which is OK but not most efficient). * I just plain don't worry about this case. The actual * conversion from NL to CRLF is done after the rptflg if... */ (!feol || binary || (feol && (rnext != feol))) && (rt == rnext) && (first == 0)) { /* Got a run... */ if (++rpt < 94) { /* Below max, just count */ continue; /* go back and get another */ } else if (rpt == 94) { /* Reached max, must dump */ xxrc = (CHAR) tochar(rpt); /* Put the repeat count here */ rptn += rpt; /* Accumulate it for statistics */ rpt = 0; /* And reset it */ } } else if (rpt > 1) { /* More than two */ xxrc = (CHAR) tochar(++rpt); /* and count. */ rptn += rpt; rpt = 0; /* Reset repeat counter. */ } /* If (rpt == 1) we must encode exactly two characters. This is done later, after the first character is encoded. */ } /* If it's the newline character... */ if (!uflag && !binary && feol && (rt == feol)) { if (lscapu && lsstate) { /* If SHIFT-STATE is SHIFTED */ if (ebqflg) { /* If single shifts enabled, */ *dp++ = (CHAR) ebq; /* insert a single shift. */ } else { /* Otherwise must shift in. */ *dp++ = myctlq; /* Insert shift-out code */ *dp++ = 'O'; lsstate = 0; /* Change shift state */ } } #ifdef CK_SPEED if (ctlp[CR]) { *dp++ = myctlq; /* Insert carriage return directly */ *dp++ = 'M'; ccp++; } else { *dp++ = CR; /* Perhaps literally */ ccu++; } #else /* !CK_SPEED */ *dp++ = myctlq; /* Insert carriage return directly */ *dp++ = 'M'; ccp++; #endif /* CK_SPEED */ rt = LF; /* Now make next char be linefeed. */ } /* Now handle the 8th bit of the file character. If we have an 8-bit connection, we preserve the 8th bit. If we have a 7-bit connection, we employ either single or locking shifts (if they are enabled). */ a7 = rt & 0177; /* Get low 7 bits of character */ if (rt & 0200) { /* 8-bit character? */ if (lscapu) { /* Locking shifts enabled? */ if (!lsstate) { /* Not currently shifted? */ x = lslook(0200); /* Look ahead */ if (x != 0 || ebqflg == 0) { /* Locking shift decision */ xxls = 'N'; /* Need locking shift-out */ lsstate = 1; /* and change to shifted state */ } else if (ebqflg) { /* Not worth it */ xxss = (CHAR) ebq; /* Use single shift */ } } rt = (CHAR) a7; /* Replace character by 7-bit value */ } else if (ebqflg) { /* 8th bit prefixing is on? */ xxss = (CHAR) ebq; /* Insert single shift */ rt = (CHAR) a7; /* Replace character by 7-bit value */ } /* In case we have a 7-bit connection and this is an 8-bit character, AND neither locking shifts nor single shifts are enabled, then the character's 8th bit will be destroyed in transmission, and a block check error will occur. */ } else if (lscapu) { /* 7-bit character */ if (lsstate) { /* Comes while shifted out? */ x = lslook(0); /* Yes, look ahead */ if (x || ebqflg == 0) { /* Time to shift in. */ xxls = 'O'; /* Set shift-in code */ lsstate = 0; /* Exit shifted state */ } else if (ebqflg) { /* Not worth it, stay shifted out */ xxss = (CHAR) ebq; /* Insert single shift */ } } } /* If data character is significant to locking shift protocol... */ if (lscapu && (a7 == SO || a7 == SI || a7 == DLE)) xxdl = 'P'; /* Insert datalink escape */ if ( #ifdef CK_SPEED /* Thwart YET ANOTHER unwanted, unneeded, and unloved sign extension. This one was particularly nasty because it prevented 255 (Telnet IAC) from being prefixed on some platforms -- e.g. VMS with VAX C -- but not others, thus causing file transfers to fail on Telnet connections by sending bare IACs. Not to mention the stray memory reference. Signed chars are a BAD idea. */ ctlp[(unsigned)(rt & 0xff)] /* Lop off any "sign" extension */ #else (a7 < SP) || (a7 == DEL) #endif /* CK_SPEED */ ) { /* Do control prefixing if necessary */ xxcq = myctlq; /* The prefix */ ccp++; /* Count it */ rt = (CHAR) ctl(rt); /* Uncontrollify the character */ } #ifdef CK_SPEED else if ((a7 < SP) || (a7 == DEL)) /* Count an unprefixed one */ ccu++; #endif /* CK_SPEED */ if (a7 == myctlq) /* Always prefix the control prefix */ xxcq = myctlq; if ((rptflg) && (a7 == rptq)) /* If it's the repeat prefix, */ xxcq = myctlq; /* prefix it if doing repeat counts */ if ((ebqflg) && (a7 == ebq)) /* Prefix the 8th-bit prefix */ xxcq = myctlq; /* if doing 8th-bit prefixes */ /* Now construct the entire sequence */ if (xxls) { *dp++ = myctlq; *dp++ = xxls; } /* Locking shift */ odp2 = dp; /* (Save this place) */ if (xxdl) { *dp++ = myctlq; *dp++ = xxdl; } /* Datalink escape */ if (xxrc) { *dp++ = (CHAR) rptq; *dp++ = xxrc; } /* Repeat count */ if (xxss) { *dp++ = (CHAR) ebq; } /* Single shift */ if (xxcq) { *dp++ = myctlq; } /* Control prefix */ *dp++ = rt; /* Finally, the character itself */ if (rpt == 1) { /* Exactly two copies? */ rpt = 0; p2 = dp; /* Save place temporarily */ for (p1 = odp2; p1 < p2; p1++) /* Copy the old chars over again */ *dp++ = *p1; if ((p2-data) <= bufmax) odp = p2; /* Check packet bounds */ if ((p2-data) < bufmax) odp = p2; /* Check packet bounds */ } rt = rnext; /* Next character is now current. */ /* Done encoding the character. Now take care of packet buffer overflow. */ if ((dp-data) >= bufmax) { /* If too big, save some for next. */ debug(F000,"getpkt EOP","",rt); size = (dp-data); /* Calculate the size. */ *dp = '\0'; /* Mark the end. */ if (memstr) { /* No leftovers for memory strings */ if (rt) /* Char we didn't encode yet */ memptr--; /* (for encstr()) */ return(size); } if ((dp-data) > bufmax) { /* if packet is overfull */ /* copy the part that doesn't fit into the leftover buffer, */ /* taking care not to split a prefixed sequence. */ int i; nleft = dp - odp; for (i = 0, p1 = leftover, p2 = odp; i < nleft; i++) { *p1++ = *p2++; if (memstr) memptr--; /* (for encstr) */ } debug(F111,"getpkt leftover",leftover,size); debug(F101,"getpkt osize","",(odp-data)); size = (odp-data); /* Return truncated packet. */ *odp = '\0'; /* Mark the new end */ } t = rt; /* Save for next time */ return(size); } } /* Otherwise, keep filling. */ size = (dp-data); /* End of file */ *dp = '\0'; /* Mark the end of the data. */ debug(F111,"getpkt eof/eot",data,size); /* Fell thru before packet full, */ return(size); /* return partially filled last packet. */ } /* T I N I T -- Initialize a transaction */ int epktrcvd = 0, epktsent = 0; /* Call with 1 to reset everything before S/I/Y negotiation, or 0 to reset only the things that are not set in the S/I/Y negotiation. Returns -1 on failure (e.g. to create packet buffers), 0 on success. */ int tinit(flag) int flag; { int x; #ifdef CK_TIMERS extern int rttflg; #endif /* CK_TIMERS */ extern int rcvtimo; extern int fatalio; debug(F101,"tinit flag","",flag); *epktmsg = NUL; epktrcvd = 0; epktsent = 0; ofperms = ""; diractive = 0; /* DIR / REMOTE DIR not active */ interrupted = 0; /* Not interrupted */ fatalio = 0; /* No fatal i/o error */ if (server) { moving = 0; pipesend = 0; /* This takes care of multiple GETs sent to a server. */ } bestlen = 0; /* For packet length optimization */ maxsend = 0; /* Biggest data field we can send */ #ifdef STREAMING streamok = 0; /* Streaming negotiated */ streaming = 0; /* Streaming being done now */ #endif /* STREAMING */ binary = b_save; /* ... */ gnf_binary = binary; /* Per-file transfer mode */ retrans = 0; /* Packet retransmission count */ sndtyp = 0; /* No previous packet */ xflg = 0; /* Reset x-packet flag */ memstr = 0; /* Reset memory-string flag */ memptr = NULL; /* and buffer pointer */ funcstr = 0; /* Reset "read from function" flag */ funcptr = NULL; /* and function pointer */ autopar = 0; /* Automatic parity detection flag */ /* This stuff is only for BEFORE S/I/Y negotiation, not after */ if (flag) { if (bctf) { /* Force Block Check 3 on all packets */ bctu = bctl = 3; /* Set block check type to 3 */ } else { bctu = bctl = 1; /* Reset block check type to 1 */ } myinit[0] = '\0'; /* Haven't sent init string yet */ rqf = -1; /* Reset 8th-bit-quote request flag */ ebq = MYEBQ; /* Reset 8th-bit quoting stuff */ ebqflg = 0; /* 8th bit quoting not enabled */ ebqsent = 0; /* No 8th-bit prefix bid sent yet */ sq = 'Y'; /* 8th-bit prefix bid I usually send */ spsiz = spsizr; /* Initial send-packet size */ debug(F101,"tinit spsiz","",spsiz); wslots = 1; /* One window slot */ wslotn = 1; /* No window negotiated yet */ justone = 0; /* (should this be zero'd here?) */ whoareu[0] = NUL; /* Partner's system type */ sysindex = -1; wearealike = 0; what = W_INIT; /* Doing nothing so far... */ } fncnv = f_save; /* Back to what user last said */ pktnum = 0; /* Initial packet number to send */ cxseen = czseen = discard = 0; /* Reset interrupt flags */ *filnam = '\0'; /* Clear file name */ spktl = 0; /* And its length */ nakstate = 0; /* Assume we're not in a NAK state */ numerrs = 0; /* Transmission error counter */ idletmo = 0; /* No idle timeout yet */ if (server) { /* If acting as server, */ if (srvidl > 0) /* If an idle timeout is given */ timint = srvidl; else timint = srvtim; /* use server timeout interval. */ } else { /* Otherwise */ timint = chktimo(rtimo,timef); /* and use local timeout value */ } debug(F101,"tinit timint","",timint); #ifdef CK_TIMERS if (rttflg && timint > 0) /* Using round-trip timers? */ rttinit(); else #endif /* CK_TIMERS */ rcvtimo = timint; winlo = 0; /* Packet 0 is at window-low */ debug(F101,"tinit winlo","",winlo); x = mksbuf(1); /* Make a 1-slot send-packet buffer */ if (x < 0) return(x); x = getsbuf(0); /* Allocate first send-buffer. */ debug(F101,"tinit getsbuf","",x); if (x < 0) return(x); dumpsbuf(); x = mkrbuf(wslots); /* & a 1-slot receive-packet buffer. */ if (x < 0) return(x); lsstate = 0; /* Initialize locking shift state */ if (autopath) { /* SET RECEIVE PATHNAMES AUTO fixup */ fnrpath = PATH_AUTO; autopath = 0; } return(0); } VOID pktinit() { /* Initialize packet sequence */ pktnum = 0; /* number & window low. */ winlo = 0; debug(F101,"pktinit winlo","",winlo); } /* R I N I T -- Respond to S or I packet */ VOID rinit(d) CHAR *d; { char *tp = NULL; ztime(&tp); tlog(F110,"Transaction begins",tp,0L); /* Make transaction log entry */ tlog(F110,"Global file mode:", binary ? "binary" : "text", 0L); tlog(F110,"Collision action:", fncnam[fncact],0); tlog(F100,"","",0); debug(F101,"rinit fncact","",fncact); filcnt = filrej = 0; /* Init file counters */ spar(d); ack1(rpar()); #ifdef datageneral if ((local) && (!quiet)) /* Only do this if local & not quiet */ consta_mt(); /* Start the asynch read task */ #endif /* datageneral */ } /* R E S E T C -- Reset per-transaction character counters */ VOID resetc() { rptn = 0; /* Repeat counts */ fsecs = flci = flco = (CK_OFF_T)0; /* File chars in and out */ #ifdef GFTIMER fpfsecs = 0.0; #endif /* GFTIMER */ tfc = tlci = tlco = (CK_OFF_T)0; /* Total file, line chars in & out */ ccu = ccp = 0L; /* Control-char statistics */ #ifdef COMMENT fsize = (CK_OFF_T)-1; /* File size */ #else if (!(what & W_SEND)) fsize = (CK_OFF_T)-1; debug(F101,"resetc fsize","",fsize); #endif /* COMMENT */ timeouts = retrans = 0; /* Timeouts, retransmissions */ spackets = rpackets = 0; /* Packet counts out & in */ crunched = 0; /* Crunched packets */ wcur = 0; /* Current window size */ wmax = 0; /* Maximum window size used */ peakcps = 0; /* Peak chars per second */ } /* S I N I T -- Get & verify first file name, then send Send-Init packet */ /* Returns: 1 if send operation begins successfully 0 if send operation fails */ #ifdef DYNAMIC char *cmargbuf = NULL; #else char cmargbuf[CKMAXPATH+1]; #endif /* DYNAMIC */ char *cmargp[2]; VOID fnlist() { if (!calibrate) sndsrc = (nfils < 0) ? -1 : nfils; /* Source for filenames */ #ifdef DYNAMIC if (!cmargbuf && !(cmargbuf = malloc(CKMAXPATH+1))) fatal("fnlist: no memory for cmargbuf"); #endif /* DYNAMIC */ cmargbuf[0] = NUL; /* Initialize name buffer */ debug(F101,"fnlist nfils","",nfils); debug(F110,"fnlist cmarg",cmarg,0); debug(F110,"fnlist cmarg2",cmarg2,0); if (!cmarg2) cmarg2 = ""; if (nfils == 0) { /* Sending from stdin or memory. */ if ((cmarg2 != NULL) && (*cmarg2)) { cmarg = cmarg2; /* If F packet, "as-name" is used */ cmarg2 = ""; /* if provided */ } else cmarg = "stdin"; /* otherwise just use "stdin" */ ckstrncpy(cmargbuf,cmarg,CKMAXPATH+1); cmargp[0] = cmargbuf; cmargp[1] = ""; cmlist = cmargp; nfils = 1; } } int sinit() { int x; /* Worker int */ char *tp, *xp, *m; /* Worker string pointers */ filcnt = filrej = 0; /* Initialize file counters */ fnlist(); xp = ""; if (nfils < 0) { #ifdef PIPESEND if (usepipes && protocol == PROTO_K && *cmarg == '!') { pipesend = 1; cmarg++; } #endif /* PIPESEND */ xp = cmarg; } else { #ifndef NOMSEND if (addlist) xp = filehead->fl_name; else #endif /* NOMSEND */ if (filefile) xp = filefile; else if (calibrate) xp = "Calibration"; else xp = *cmlist; } debug(F110,"sinit xp",xp,0); x = gnfile(); /* Get first filename. */ debug(F111,"sinit gnfile",ckitoa(gnferror),x); if (x == 0) x = gnferror; /* If none, get error reason */ m = NULL; /* Error message pointer */ debug(F101,"sinit gnfil","",x); switch (x) { case -6: m = "No files meet selection criteria"; break; case -5: m = "Too many files match wildcard"; break; case -4: m = "Cancelled"; break; case -3: m = "Read access denied"; break; case -2: m = "File is not readable"; break; #ifdef COMMENT case -1: m = iswild(filnam) ? "No files match" : "File not found"; break; case 0: m = "No filespec given!"; break; #else case 0: case -1: m = iswild(filnam) ? "No files match" : "File not found"; break; #endif /* COMMENT */ default: break; } debug(F101,"sinit nfils","",nfils); debug(F110,"sinit filnam",filnam,0); if (x < 1) { /* Didn't get a file. */ debug(F111,"sinit msg",m,x); if (server) { /* Doing GET command */ errpkt((CHAR *)m); /* so send Error packet. */ } else if (!local) { /* Doing SEND command */ interrupted = 1; /* (To suppress hint) */ printf("?%s\r\n",m); } else { xxscreen(SCR_EM,0,0l,m); /* so print message. */ } tlog(F110,xp,m,0L); /* Make transaction log entry. */ freerbuf(rseqtbl[0]); /* Free the buffer the GET came in. */ return(0); /* Return failure code */ } if (!local && !server && ckdelay > 0) /* OS-9 sleep(0) == infinite */ sleep(ckdelay); /* Delay if requested */ #ifdef datageneral if ((local) && (!quiet)) /* Only do this if local & not quiet */ consta_mt(); /* Start the async read task */ #endif /* datageneral */ freerbuf(rseqtbl[0]); /* Free the buffer the GET came in. */ sipkt('S'); /* Send the Send-Init packet. */ ztime(&tp); /* Get current date/time */ tlog(F110,"Transaction begins",tp,0L); /* Make transaction log entry */ tlog(F110,"Global file mode:", binary ? "binary" : "text", 0L); tlog(F100,"","",0); debug(F111,"sinit ok",filnam,0); return(1); } int #ifdef CK_ANSIC sipkt(char c) /* Send S or I packet. */ #else sipkt(c) char c; #endif /* sipkt */ { CHAR *rp; int k, x; extern int sendipkts; debug(F101,"sipkt pktnum","",pktnum); /* (better be 0...) */ ttflui(); /* Flush pending input. */ /* If this is an I packet and SET SEND I-PACKETS is OFF, don't send it; set sstate to 'Y' which makes the next input() call return 'Y' as if we had received an ACK to the I packet we didn't send. This is to work around buggy Kermit servers that can't handle I packets. */ if ((sendipkts == 0) && (c == 'I')) { /* I packet but don't send I pkts? */ sstate = 'Y'; /* Yikes! */ return(0); /* (see input()..)*/ } k = sseqtbl[pktnum]; /* Find slot for this packet */ if (k < 0) { /* No slot? */ k = getsbuf(winlo = pktnum); /* Make one. */ debug(F101,"sipkt getsbuf","",k); } rp = rpar(); /* Get protocol parameters. */ if (!rp) rp = (CHAR *)""; x = spack(c,pktnum,(int)strlen((char *)rp),rp); /* Send them. */ return(x); } /* X S I N I T -- Retransmit S-packet */ /* For use in the GET-SEND sequence, when we start to send, but receive another copy of the GET command because the receiver didn't get our S packet. This retransmits the S packet and frees the receive buffer for the ACK. This special case is necessary because packet number zero is being re-used. */ VOID xsinit() { int k; k = rseqtbl[0]; debug(F101,"xsinit k","",k); if (k > -1) freerbuf(k); resend(0); } /* R C V F I L -- Receive a file */ /* Incoming filename is in data field of F packet. This function decodes it into the srvcmd buffer, substituting an alternate "as-name", if one was given. Then it does any requested transformations (like converting to lowercase), and finally if a file of the same name already exists, takes the desired collision action. Returns: 1 on success. 0 on failure. */ char ofn1[CKMAXPATH+4]; /* Buffer for output file name */ char * ofn2; /* Pointer to backup file name */ int ofn1x; /* Flag output file already exists */ CK_OFF_T ofn1len = (CK_OFF_T)0; int opnerr; /* Flag for open error */ int /* Returns success ? 1 : 0 */ rcvfil(n) char *n; { extern int en_cwd; int i, skipthis; char * n2; char * dispo; #ifdef OS2ONLY char *zs, *longname, *newlongname, *pn; /* OS/2 long name items */ #endif /* OS2ONLY */ #ifdef DTILDE char *dirp; #endif /* DTILDE */ int dirflg, x, y; #ifdef PIPESEND extern char * rcvfilter; #endif /* PIPESEND */ #ifdef CALIBRATE extern int dest; CK_OFF_T csave; csave = calibrate; /* So we can decode filename */ calibrate = (CK_OFF_T)0; #endif /* CALIBRATE */ ofperms = ""; /* Reset old-file permissions */ opnerr = 0; /* No open error (yet) */ ofn2 = NULL; /* No new name (yet) */ lsstate = 0; /* Cancel locking-shift state */ srvptr = srvcmd; /* Decode file name from packet. */ #ifdef UNICODE xpnbyte(-1,0,0,NULL); /* Reset UCS-2 byte counter. */ #endif /* UNICODE */ debug(F110,"rcvfil rdatap",rdatap,0); decode(rdatap,putsrv,0); /* Don't xlate charsets. */ #ifdef CALIBRATE calibrate = csave; if (dest == DEST_N) { calibrate = 1; cmarg2 = "CALIBRATE"; } #endif /* CALIBRATE */ if (*srvcmd == '\0') /* Watch out for null F packet. */ ckstrncpy((char *)srvcmd,"NONAME",srvcmdlen); makestr(&prrfspec,(char *)srvcmd); /* New preliminary filename */ #ifdef DTILDE if (*srvcmd == '~') { dirp = tilde_expand((char *)srvcmd); /* Expand tilde, if any. */ if (*dirp != '\0') ckstrncpy((char *)srvcmd,dirp,srvcmdlen); } #else #ifdef OS2 if (isalpha(*srvcmd) && srvcmd[1] == ':' && srvcmd[2] == '\0') ckstrncat((char *)srvcmd,"NONAME",srvcmdlen); #endif /* OS2 */ #endif /* DTILDE */ #ifndef NOICP #ifndef NOSPL /* File dialog when downloading... */ if ( #ifdef CK_APC (apcactive == APC_LOCAL && adl_ask) || /* Autodownload with ASK */ #endif /* CK_APC */ (clcmds && haveurl) /* Or "kermit:" or "iksd:" URL */ ) { int x; char fnbuf[CKMAXPATH+1]; /* Result buffer */ char * preface; if (clcmds && haveurl) preface = "\r\nIncoming file from Kermit server...\r\n\ Please confirm output file specification or supply an alternative:"; else preface = "\r\nIncoming file from remote Kermit...\r\n\ Please confirm output file specification or supply an alternative:"; x = uq_file(preface, /* Preface */ NULL, /* Prompt (let uq_file() built it) */ 3, /* Output file */ NULL, /* Help text */ (char *)srvcmd, /* Default */ fnbuf, /* Result buffer */ CKMAXPATH+1 /* Size of result buffer */ ); if (x < 1) { /* Refused */ rf_err = "Refused by user"; return(0); } ckstrncpy((char *)srvcmd,fnbuf,CKMAXPATH+1); if (isabsolute((char *)srvcmd)) { /* User gave an absolute path */ g_fnrpath = fnrpath; /* Save current RECEIVE PATHNAMES */ fnrpath = PATH_ABS; /* switch to ABSOLUTE */ } } #endif /* NOSPL */ #endif /* NOICP */ if (!ENABLED(en_cwd)) { /* CD is disabled */ zstrip((char *)(srvcmd+2),&n2); /* and they included a pathname, */ if (strcmp((char *)(srvcmd+2),n2)) { /* so refuse. */ rf_err = "Access denied"; return(0); } } #ifdef COMMENT /* Wrong place for this -- handle cmarg2 first -- see below... */ if (zchko((char *)srvcmd) < 0) { /* Precheck for write access */ debug(F110,"rcvfil access denied",srvcmd,0); rf_err = "Write access denied"; discard = opnerr = 1; return(0); } xxscreen(SCR_FN,0,0l,(char *)srvcmd); /* Put it on screen if local */ debug(F110,"rcvfil srvcmd 1",srvcmd,0); tlog(F110,"Receiving",(char *)srvcmd,0L); /* Transaction log entry */ #endif /* COMMENT */ skipthis = 0; /* This file in our exception list? */ for (i = 0; i < NSNDEXCEPT; i++) { if (!rcvexcept[i]) { break; } if (ckmatch(rcvexcept[i],(char *)srvcmd,filecase,1)) { skipthis = 1; break; } } #ifdef DEBUG if (deblog && skipthis) { debug(F111,"rcvfil rcvexcept",rcvexcept[i],i); debug(F110,"rcvfil skipping",srvcmd,0); } #endif /* DEBUG */ if (skipthis) { /* Skipping this file */ discard = 1; rejection = 1; rf_err = "Exception list"; debug(F101,"rcvfil discard","",discard); tlog(F100," refused: exception list","",0); return(1); } /* File is not in exception list */ if (!cmarg2) /* No core dumps please */ cmarg2 = ""; debug(F110,"rcvfil cmarg2",cmarg2,0); if (*cmarg2) { /* Check for alternate name */ #ifndef NOSPL int y; char *s; /* Pass it thru the evaluator */ extern int cmd_quoting; if (cmd_quoting) { y = MAXRP; ckstrncpy(ofn1,(char *)srvcmd,CKMAXPATH+1); /* for \v(filename) */ s = (char *)srvcmd; zzstring(cmarg2,&s,&y); } else *srvcmd = NUL; if (!*srvcmd) /* If we got something */ #endif /* NOSPL */ ckstrncpy((char *)srvcmd,cmarg2,srvcmdlen); } debug(F110,"rcvfil srvcmd 2",srvcmd,0); #ifdef PIPESEND /* If it starts with "bang", it's a pipe, not a file. */ if (usepipes && protocol == PROTO_K && *srvcmd == '!' && !rcvfilter) { CHAR *s; s = srvcmd+1; /* srvcmd[] is not a pointer. */ while (*s) { /* So we have to slide the contents */ *(s-1) = *s; /* over 1 space to the left. */ s++; } *(s-1) = NUL; pipesend = 1; } #endif /* PIPESEND */ #ifdef COMMENT /* This is commented out because we need to know whether the name we are using was specified by the local user as an override, or came from the incoming packet. In the former case, we don't do stuff to it (like strip the pathname) that we might do to it in the latter. */ cmarg2 = ""; /* Done with alternate name */ #endif /* COMMENT */ if ((int)strlen((char *)srvcmd) > CKMAXPATH) /* Watch out for overflow */ *(srvcmd + CKMAXPATH - 1) = NUL; /* At this point, srvcmd[] contains the incoming filename or as-name. */ /* So NOW we check for write access. */ if (zchko((char *)srvcmd) < 0) { /* Precheck for write access */ debug(F110,"rcvfil access denied",srvcmd,0); rf_err = "Write access denied"; discard = opnerr = 1; return(0); } xxscreen(SCR_FN,0,0l,(char *)srvcmd); /* Put it on screen if local */ debug(F110,"rcvfil srvcmd 1",srvcmd,0); tlog(F110,"Receiving",(char *)srvcmd,0L); /* Transaction log entry */ #ifdef CK_LABELED #ifdef VMS /* If we have an as-name, this overrides the internal name if we are doing a labeled-mode transfer. */ if (*cmarg2) { extern int lf_opts; lf_opts &= ~LBL_NAM; } #endif /* VMS */ #endif /* CK_LABELED */ debug(F111,"rcvfil pipesend",srvcmd,pipesend); #ifdef PIPESEND /* Skip all the filename manipulation and collision actions */ if (pipesend) { dirflg = 0; ofn1[0] = '!'; ckstrncpy(&ofn1[1],(char *)srvcmd,CKMAXPATH+1); ckstrncpy(n,ofn1,CKMAXPATH+1); ckstrncpy(fspec,ofn1,CKMAXPATH+1); makestr(&prfspec,fspec); /* New preliminary filename */ debug(F110,"rcvfil pipesend",ofn1,0); goto rcvfilx; } #endif /* PIPESEND */ /* This is to avoid passing email subjects through nzrtol(). We haven't yet received the A packet so we don't yet know it's e-mail, so in fact we go ahead and convert it anyway, but later we get the original back from ofilnam[]. */ dispos = 0; ckstrncpy(ofilnam,(char *)srvcmd,CKMAXPATH+1); #ifdef NZLTOR if (*cmarg2) ckstrncpy((char *)ofn1,(char *)srvcmd,CKMAXPATH+1); else nzrtol((char *)srvcmd, /* Filename from packet */ (char *)ofn1, /* Where to put result */ fncnv, /* Filename conversion */ fnrpath, /* Pathname handling */ CKMAXPATH /* Size of result buffer */ ); #else debug(F101,"rcvfil fnrpath","",fnrpath); /* Handle pathnames */ if (fnrpath == PATH_OFF && !*cmarg2) { /* RECEIVE PATHNAMES OFF? */ char *t; /* Yes. */ zstrip((char *)srvcmd,&t); /* If there is a pathname, strip it */ debug(F110,"rcvfil PATH_OFF zstrip",t,0); if (!t) /* Be sure we didn't strip too much */ sprintf(ofn1,"FILE%02ld",filcnt); else if (*t == '\0') sprintf(ofn1,"FILE%02ld",filcnt); else ckstrncpy(ofn1,t,CKMAXPATH+1); ckstrncpy((char *)srvcmd,ofn1,srvcmdlen); /* Now copy it back. */ } /* SET RECEIVE PATHNAMES RELATIVE... The following doesn't belong here but doing it right would require defining and implementing a new file routine for all ck?fio.c modules. So for now... */ #ifdef UNIXOROSK else if (fnrpath == PATH_REL && !*cmarg2) { if (isabsolute((char *)srvcmd)) { ofn1[0] = '.'; ckstrncpy(&of1n[1],(char *)srvcmd,CKMAXPATH+1); ckstrncpy((char *)srvcmd,ofn1,srvcmdlen); debug(F110,"rcvfil PATH_REL",ofn1,0); } } #else #ifdef OS2 else if (fnrpath == PATH_REL && !*cmarg2) { if (isabsolute((char *)srvcmd)) { char *p = (char *)srvcmd; if (isalpha(*p) && *(p+1) == ':') p += 2; if (*p == '\\' || *p == '/') p++; ckstrncpy(ofn1,p,CKMAXPATH+1); ckstrncpy((char *)srvcmd,ofn1,srvcmdlen); debug(F110,"rcvfil OS2 PATH_REL",ofn1,0); } } #endif /* OS2 */ #endif /* UNIXOROSK */ /* Now srvcmd contains incoming filename with path possibly stripped */ if (fncnv) /* FILE NAMES CONVERTED? */ zrtol((char *)srvcmd,(char *)ofn1); /* Yes, convert to local form */ else ckstrncpy(ofn1,(char *)srvcmd,CKMAXPATH+1); /* No, copy literally. */ #endif /* NZLTOR */ #ifdef PIPESEND if (rcvfilter) { char * p = NULL, * q; int nn = MAXRP; pipesend = 1; debug(F110,"rcvfil rcvfilter ",rcvfilter,0); #ifndef NOSPL if ((p = (char *) malloc(nn + 1))) { q = p; #ifdef COMMENT /* We have already processed srvcmd and placed it into ofn1 */ ckstrncpy(ofn1,(char *)srvcmd,CKMAXPATH+1); /* For \v(filename) */ #endif /* COMMENT */ debug(F110,"rcvfile pipesend filter",rcvfilter,0); zzstring(rcvfilter,&p,&nn); debug(F111,"rcvfil pipename",q,nn); if (nn <= 0) { printf( "?Sorry, receive filter + filename too long, %d max.\n", CKMAXPATH ); rf_err = "Name too long"; free(q); return(0); } ckstrncpy((char *)srvcmd,q,MAXRP); free(q); } #endif /* NOSPL */ } #endif /* PIPESEND */ /* Now the incoming filename, possibly converted, is in ofn1[]. */ #ifdef OS2 /* Don't refuse the file just because the name is illegal. */ if (!IsFileNameValid(ofn1)) { /* Name is OK for OS/2? */ #ifdef OS2ONLY char *zs = NULL; zstrip(ofn1, &zs); /* Not valid, strip unconditionally */ if (zs) { if (iattr.longname.len && /* Free previous longname, if any */ iattr.longname.val) free(iattr.longname.val); iattr.longname.len = strlen(zs); /* Store in attribute structure */ iattr.longname.val = (char *) malloc(iattr.longname.len + 1); if (iattr.longname.val) /* Remember this (illegal) name */ strcpy(iattr.longname.val, zs); /* safe */ } #endif /* OS2ONLY */ debug(F110,"rcvfil: invalid file name",ofn1,0); ChangeNameForFAT(ofn1); /* Change to an acceptable name */ debug(F110,"rcvfil: FAT file name",ofn1,0); } else { /* Name is OK. */ debug(F110,"rcvfil: valid file name",ofn1,0); #ifdef OS2ONLY if (iattr.longname.len && iattr.longname.val) /* Free previous longname, if any */ free(iattr.longname.val); iattr.longname.len = 0; iattr.longname.val = NULL; /* This file doesn't need a longname */ #endif /* OS2ONLY */ } #endif /* OS2 */ debug(F110,"rcvfil as",ofn1,0); /* Filename collision action section. */ dirflg = /* Is it a directory name? */ #ifdef CK_TMPDIR isdir(ofn1) #else 0 #endif /* CK_TMPDIR */ ; debug(F101,"rcvfil dirflg","",dirflg); ofn1len = zchki(ofn1); /* File already exists? */ debug(F111,"rcvfil ofn1len",ofn1,ofn1len); ofn1x = (ofn1len != (CK_OFF_T)-1); if ( ( #ifdef UNIX strcmp(ofn1,"/dev/null") && /* It's not the null device? */ #else #ifdef OSK strcmp(ofn1,"/nil") && #endif /* OSK */ #endif /* UNIX */ !stdouf ) && /* Not copying to standard output? */ ofn1x || /* File of same name exists? */ dirflg ) { /* Or file is a directory? */ debug(F111,"rcvfil exists",ofn1,fncact); #ifdef CK_PERMS ofperms = zgperm((char *)ofn1); /* Get old file's permissions */ debug(F110,"rcvfil perms",ofperms,0); #endif /* CK_PERMS */ debug(F101,"rcvfil fncact","",fncact); switch (fncact) { /* Yes, do what user said. */ case XYFX_A: /* Append */ ofperms = ""; debug(F100,"rcvfil append","",0); if (dirflg) { rf_err = "Can't append to a directory"; tlog(F100," error - can't append to directory","",0); discard = opnerr = 1; return(0); } tlog(F110," appending to",ofn1,0); break; #ifdef COMMENT case XYFX_Q: /* Query (Ask) */ break; /* not implemented */ #endif /* COMMENT */ case XYFX_B: /* Backup (rename old file) */ if (dirflg) { rf_err = "Can't rename existing directory"; tlog(F100," error - can't rename directory","",0); discard = opnerr = 1; return(0); } znewn(ofn1,&ofn2); /* Get new unique name */ tlog(F110," backup:",ofn2,0); debug(F110,"rcvfil backup ofn1",ofn1,0); debug(F110,"rcvfil backup ofn2",ofn2,0); #ifdef CK_LABELED #ifdef OS2ONLY /* In case this is a FAT file system, we can't change only the FAT name, we also have to change the longname from the extended attributes block. Otherwise, we'll have many files with the same longname and if we copy them to an HPFS volume, only one will survive. */ if (os2getlongname(ofn1, &longname) > -1) { if (strlen(longname)) { char tmp[10]; extern int ck_znewn; sprintf(tmp,".~%d~",ck_znewn); newlongname = (char *) malloc(strlen(longname) + strlen(tmp) + 1); if (newlongname) { strcpy(newlongname, longname); /* safe (prechecked) */ strcat(newlongname, tmp); /* safe (prechecked) */ os2setlongname(ofn1, newlongname); free(newlongname); newlongname = NULL; } } } else debug(F100,"rcvfil os2getlongname failed","",0); #endif /* OS2ONLY */ #endif /* CK_LABELED */ #ifdef COMMENT /* Do this later, in opena()... */ if (zrename(ofn1,ofn2) < 0) { rf_err = "Can't transform filename"; debug(F110,"rcvfil rename fails",ofn1,0); discard = opnerr = 1; return(0); } #endif /* COMMENT */ break; case XYFX_D: /* Discard (refuse new file) */ ofperms = ""; discard = 1; rejection = 1; /* Horrible hack: reason = name */ debug(F101,"rcvfil discard","",discard); tlog(F100," refused: name","",0); break; case XYFX_R: /* Rename incoming file */ znewn(ofn1,&ofn2); /* Make new name for it */ #ifdef OS2ONLY if (iattr.longname.len) { char tmp[10]; extern int ck_znewn; sprintf(tmp,".~%d~",ck_znewn); newlongname = (char *) malloc(iattr.longname.len + strlen(tmp) + 1); if (newlongname) { strcpy(newlongname, iattr.longname.val); /* safe */ strcat(newlongname, tmp); /* safe */ debug(F110, "Rename Incoming: newlongname",newlongname,0); if (iattr.longname.len && iattr.longname.val) free(iattr.longname.val); iattr.longname.len = strlen(newlongname); iattr.longname.val = newlongname; /* free(newlongname) here ? */ } } #endif /* OS2ONLY */ break; case XYFX_X: /* Replace old file */ debug(F100,"rcvfil overwrite","",0); if (dirflg) { rf_err = "Can't overwrite existing directory"; tlog(F100," error - can't overwrite directory","",0); discard = opnerr = 1; #ifdef COMMENT return(0); #else break; #endif /* COMMENT */ } tlog(F110,"overwriting",ofn1,0); break; case XYFX_U: /* Refuse if older */ debug(F110,"rcvfil update",ofn1,0); if (dirflg) { rf_err = "File has same name as existing directory"; tlog(F110," error - directory exists:",ofn1,0); discard = opnerr = 1; #ifdef COMMENT /* Don't send an error packet, just refuse the file */ return(0); #endif /* COMMENT */ } break; /* Not here, we don't have */ /* the attribute packet yet. */ default: ofperms = ""; debug(F101,"rcvfil bad collision action","",fncact); break; } } debug(F110,"rcvfil ofn1",ofn1,0); debug(F110,"rcvfil ofn2",ofn2,0); debug(F110,"rcvfil ofperms",ofperms,0); if (fncact == XYFX_R && ofn1x && ofn2) { /* Renaming incoming file? */ xxscreen(SCR_AN,0,0l,ofn2); /* Display renamed name */ ckstrncpy(n, ofn2, CKMAXPATH+1); /* Return it */ } else { /* No */ xxscreen(SCR_AN,0,0l,ofn1); /* Display regular name */ ckstrncpy(n, ofn1, CKMAXPATH+1); /* and return it. */ } #ifdef CK_MKDIR /* Create directory(s) if necessary. */ if (!discard && fnrpath != PATH_OFF) { /* RECEIVE PATHAMES ON? */ int x; debug(F110,"rcvfil calling zmkdir",ofn1,0); /* Yes */ if ((x = zmkdir(ofn1)) < 0) { debug(F100,"zmkdir fails","",0); tlog(F110," error - directory creation failure:",ofn1,0); rf_err = "Directory creation failure."; discard = 1; return(0); } #ifdef TLOG else if (x > 0) tlog(F110," path created:",ofn1,0); #endif /* TLOG */ } #else debug(F110,"rcvfil CK_MKDIR not defined",ofn1,0); #endif /* CK_MKDIR */ if (calibrate) ckstrncpy(fspec,ofn1,CKMAXPATH+1); else zfnqfp(ofn1,fspeclen,fspec); debug(F110,"rcvfil fspec",fspec,0); #ifdef COMMENT /* See comments with VMS zfnqfp()... */ #ifdef VMS /* zfnqfp() does not return the version number */ if (!calibrate) { int x, v; x = strlen(ofn1); if (x > 0) { if (ofn1[x-1] == ';') { v = getvnum(ofn1); ckstrncpy(&ofn1[x],ckitoa(v),CKMAXPATH-x); } } } #endif /* VMS */ #endif /* COMMENT */ fspec[fspeclen] = NUL; makestr(&prfspec,fspec); /* New preliminary filename */ #ifdef PIPESEND rcvfilx: #endif /* PIPESEND */ debug(F110,"rcvfilx: n",n,0); debug(F110,"rcvfilx: ofn1",ofn1,0); ffc = (CK_OFF_T)0; /* Init per-file counters */ cps = oldcps = 0L; rs_len = (CK_OFF_T)0; rejection = -1; fsecs = gtimer(); /* Time this file started */ #ifdef GFTIMER fpfsecs = gftimer(); debug(F101,"rcvfil fpfsecs","",fpfsecs); #endif /* GFTIMER */ filcnt++; intmsg(filcnt); return(1); /* Successful return */ } /* R E O F -- Receive End Of File packet for incoming file */ /* Closes the received file. Returns: 0 on success. -1 if file could not be closed. 2 if disposition was mail, mail was sent, but temp file not deleted. 3 if disposition was print, file was printed, but not deleted. -2 if disposition was mail and mail could not be sent -3 if disposition was print and file could not be printed -4 if MOVE-TO: failed -5 if RENAME-TO: failed */ int reof(f,yy) char *f; struct zattr *yy; { extern char * rcv_move, * rcv_rename; extern int o_isopen; int rc = 0; /* Return code */ char *p; char c; debug(F111,"reof fncact",f,fncact); debug(F101,"reof discard","",discard); success = 1; /* Assume status is OK */ lsstate = 0; /* Cancel locking-shift state */ if (discard) { /* Handle attribute refusals, etc. */ debug(F101,"reof discarding","",0); success = 0; /* Status = failed. */ if (rejection == '#' || /* Unless rejection reason is */ rejection == 1 || /* date or name (SET FILE COLLISION */ rejection == '?') /* UPDATE or DISCARD) */ success = 1; debug(F101,"reof success","",success); filrej++; /* Count this rejection. */ discard = 0; /* We never opened the file, */ return(0); /* so we don't close it. */ } #ifdef DEBUG if (deblog) { debug(F101,"reof cxseen","",cxseen); debug(F101,"reof czseen","",czseen); debug(F110,"reof rdatap",rdatap,0); } #endif /* DEBUG */ if (cxseen == 0) /* Got cancel directive? */ cxseen = (*rdatap == 'D'); if (cxseen || czseen) /* (for hints) */ interrupted = 1; success = (cxseen || czseen) ? 0 : 1; /* Set SUCCESS flag appropriately */ if (!success) /* "Uncount" this file */ filrej++; debug(F101,"reof o_isopen","",o_isopen); if (o_isopen) { /* If an output file was open... */ #ifdef CK_CTRLZ if (success) { debug(F101,"reof lastchar","",lastchar); if (!binary && eofmethod == XYEOF_Z && lastchar != 26 && (!xflg || (xflg && remfile))) pnbyte((char)26,putfil); } #endif /* CK_CTRLZ */ rc = clsof(cxseen || czseen); /* Close the file (resets cxseen) */ debug(F101,"reof closf","",rc); if (rc < 0) { /* If failure to close, FAIL */ if (success) filrej++; success = 0; } if (!calibrate) { /* Set file modification date and/or permissions */ if (success) zstime(f,yy,0); #ifdef OS2ONLY #ifdef CK_LABELED if (success && yy->longname.len) os2setlongname(f, yy->longname.val); #endif /* CK_LABELED */ #endif /* OS2ONLY */ } if (success == 0) { /* And program return code */ xitsta |= W_RECV; } else if (success == 1) { /* File rec'd successfully */ makestr(&rrfspec,prrfspec); /* Record it for wheremsg */ makestr(&rfspec,prfspec); } /* Handle dispositions from attribute packet... */ c = NUL; #ifndef NOFRILLS if (!calibrate && yy->disp.len != 0) { p = yy->disp.val; c = *p++; #ifndef UNIX /* See ckcpro.w. In UNIX we don't use temp files any more -- we pipe the stuff right into mail or lpr. */ if (c == 'M') { /* Mail to user. */ rc = zmail(p,filnam); /* Do the system's mail command */ if (rc < 0) success = 0; /* Remember status */ tlog(F110,"mailed",filnam,0L); tlog(F110," to",p,0L); zdelet(filnam); /* Delete the file */ } else if (c == 'P') { /* Print the file. */ rc = zprint(p,filnam); /* Do the system's print command */ if (rc < 0) success = 0; /* Remember status */ tlog(F110,"printed",filnam,0L); tlog(F110," with options",p,0L); #ifndef VMS #ifndef STRATUS /* spooler deletes file after print complete in VOS & VMS */ if (zdelet(filnam) && rc == 0) rc = 3; /* Delete the file */ #endif /* STRATUS */ #endif /* VMS */ } #endif /* UNIX */ } #endif /* NOFRILLS */ if (success && !pipesend && !calibrate && c != 'M' && c != 'P') { if (rcv_move) { /* If /MOVE-TO was given... */ char * p = rcv_move; #ifdef COMMENT /* No need for this - it's a directory name */ char tmpbuf[CKMAXPATH+1]; extern int cmd_quoting; /* for \v(filename) */ if (cmd_quoting) { /* But only if cmd_quoting is on */ int n; n = CKMAXPATH; p = tmpbuf; debug(F111,"reof /move-to",rcv_move,0); zzstring(rcv_move,&p,&n); p = tmpbuf; } #endif /* COMMENT */ /* Here we could create the directory if it didn't exist (and it was relative) but there would have to be a user-settable option to say whether to do this. */ rc = zrename(filnam,p); debug(F111,"reof MOVE zrename",rcv_move,rc); if (rc > -1) { tlog(F110," moving received file to",rcv_move,0); } else { rc = -4; tlog(F110," FAILED to move received file to",rcv_move,0); } } else if (rcv_rename) { /* Or /RENAME-TO: */ char *s = rcv_rename; /* This is the renaming string */ #ifndef NOSPL char tmpnam[CKMAXPATH+16]; extern int cmd_quoting; /* for \v(filename) */ if (cmd_quoting) { /* But only if cmd_quoting is on */ int n; /* Pass it thru the evaluator */ n = CKMAXPATH; s = (char *)tmpnam; zzstring(rcv_rename,&s,&n); s = (char *)tmpnam; } #endif /* NOSPL */ if (s) if (*s) { rc = zrename(filnam,s); debug(F111,"reof RENAME zrename",s,rc); if (rc > -1) { tlog(F110," renaming received file to",s,0); } else { rc = -5; tlog(F110," FAILED to rename received file to",s,0); } } } } } debug(F101,"reof success","",success); debug(F101,"reof returns","",rc); filnam[0] = NUL; /* Erase the filename */ return(rc); } /* R E O T -- Receive End Of Transaction */ VOID reot() { cxseen = czseen = discard = 0; /* Reset interruption flags */ tstats(); /* Finalize transfer statistics */ } /* S F I L E -- Send File header or teXt header packet */ /* Call with x nonzero for X packet, zero for F packet. If X == 0, filename to send is in filnam[], and if cmarg2 is not null or empty, the file should be sent under this name rather than filnam[]. If sndfilter not NULL, it is the name of a send filter. Returns 1 on success, 0 on failure. */ int sfile(x) int x; { #ifdef pdp11 #define PKTNL 64 #else #define PKTNL 256 #endif /* pdp11 */ char pktnam[PKTNL+1]; /* Local copy of name */ char *s; int rc; int notafile = 0; extern int filepeek; #ifdef PIPESEND extern char * sndfilter; if (sndfilter) { pipesend = 1; debug(F110,"sfile send filter ",sndfilter,0); } #endif /* PIPESEND */ notafile = calibrate || sndarray || pipesend || x; debug(F101,"sfile x","",x); debug(F101,"sfile notafile","",notafile); #ifndef NOCSETS if (tcs_save > -1) { /* Character sets */ tcharset = tcs_save; tcs_save = -1; debug(F101,"sfile restored tcharset","",tcharset); } if (fcs_save > -1) { fcharset = fcs_save; fcs_save = -1; debug(F101,"sfile restored fcharset","",fcharset); } setxlatype(tcharset,fcharset); /* Translation type */ #endif /* NOCSETS */ /* cmarg2 or filnam (with that precedence) have the file's name */ lsstate = 0; /* Cancel locking-shift state */ #ifdef COMMENT /* Do this after making sure we can open the file */ if (nxtpkt() < 0) return(0); /* Bump packet number, get buffer */ #endif /* COMMENT */ pktnam[0] = NUL; /* Buffer for name we will send */ if (x == 0) { /* F-Packet setup */ if (!cmarg2) cmarg2 = ""; #ifdef DEBUG if (deblog) { /* debug(F111,"sfile cmarg2",cmarg2,cmarg2); */ debug(F101,"sfile binary 1","",binary); debug(F101,"sfile wearealike","",wearealike); debug(F101,"sfile xfermode","",xfermode); debug(F101,"sfile filepeek","",filepeek); #ifndef NOCSETS debug(F101,"sfile s_cset","",s_cset); debug(F101,"sfile tcharset","",tcharset); debug(F101,"sfile xfrxla","",xfrxla); #endif /* NOCSETS */ } #endif /* DEBUG */ if (xfermode == XMODE_A /* TRANSFER MODE AUTOMATIC */ #ifndef NOMSEND && !addlist /* And not working from a SEND-LIST */ #endif /* NOMSEND */ ) { /* Other Kermit is on a like system and no charset translation */ if (wearealike #ifndef NOCSETS && (tcharset == TC_TRANSP || xfrxla == 0) #endif /* NOCSETS */ ) { #ifdef VMS if (binary != XYFT_I) #endif /* VMS */ #ifdef CK_LABELED if (binary != XYFT_L) #endif /* CK_LABELED */ binary = XYFT_B; /* Send all files in binary mode */ } /* Otherwise select transfer mode based on file info */ else if (!notafile /* but not if sending from pipe */ #ifdef CK_LABELED && binary != XYFT_L /* and not if FILE TYPE LABELED */ #endif /* CK_LABELED */ #ifdef VMS && binary != XYFT_I /* or FILE TYPE IMAGE */ #endif /* VMS */ ) { #ifdef UNICODE fileorder = -1; /* File byte order */ #endif /* UNICODE */ if (filepeek && !notafile) { /* Real file, FILE SCAN is ON */ int k, x; k = scanfile(filnam,&x,nscanfile); /* Scan the file */ debug(F101,"sfile scanfile","",k); switch (k) { case FT_TEXT: /* Unspecified text */ debug(F100,"sfile scanfile text","",0); binary = XYFT_T; /* SET FILE TYPE TEXT */ break; #ifndef NOCSETS case FT_7BIT: /* 7-bit text */ binary = XYFT_T; /* SET FILE TYPE TEXT */ /* If SEND CHARSET-SELECTION AUTO */ /* and SET TRANSFER TRANSLATION is ON */ debug(F100,"sfile scanfile text7","",0); if (s_cset == XMODE_A && xfrxla) { if (fcsinfo[fcharset].size != 128) { fcs_save = fcharset; /* Current FCS not 7bit */ fcharset = dcset7; /* Use default 7bit set */ debug(F101,"sfile scanfile 7 fcharset", "",fcharset); } /* And also switch to appropriate TCS */ if (afcset[fcharset] > -1 && afcset[fcharset] <= MAXTCSETS) { tcs_save = tcharset; tcharset = afcset[fcharset]; debug(F101,"sfile scanfile 7 tcharset","", tcharset); } setxlatype(tcharset,fcharset); } break; case FT_8BIT: /* 8-bit text */ binary = XYFT_T; /* SET FILE TYPE TEXT */ /* If SEND CHARSET-SELEC AUTO */ /* and SET TRANSFER TRANSLATION is ON */ debug(F100,"sfile scanfile text8","",0); if (s_cset == XMODE_A && xfrxla) { if (fcsinfo[fcharset].size != 256) { fcs_save = fcharset; /* Current FCS not 8bit */ fcharset = dcset8; /* Use default 8bit set */ debug(F101,"sfile scanfile 8 fcharset", "",fcharset); } /* Switch to corresponding transfer charset */ if (afcset[fcharset] > -1 && afcset[fcharset] <= MAXTCSETS) { tcs_save = tcharset; tcharset = afcset[fcharset]; debug(F101,"sfile scanfile 8 tcharset","", tcharset); } setxlatype(tcharset,fcharset); } break; #ifdef UNICODE case FT_UTF8: /* UTF-8 text */ case FT_UCS2: /* UCS-2 text */ debug(F101,"sfile scanfile Unicode","",k); binary = XYFT_T; /* If SEND CHARSET-SELEC AUTO */ /* and SET TRANSFER TRANSLATION is ON */ if (s_cset == XMODE_A && xfrxla) { fcs_save = fcharset; tcs_save = tcharset; fcharset = (k == FT_UCS2) ? FC_UCS2 : FC_UTF8; if (k == FT_UCS2 && x > -1) fileorder = x; } /* Switch to associated transfer charset if any */ if (afcset[fcharset] > -1 && afcset[fcharset] <= MAXTCSETS) tcharset = afcset[fcharset]; if (tcharset == TC_TRANSP) /* If none */ tcharset = TC_UTF8; /* use UTF-8 */ setxlatype(tcharset,fcharset); debug(F101,"sfile Unicode tcharset","",tcharset); break; #endif /* UNICODE */ #endif /* NOCSETS */ case FT_BIN: debug(F101,"sfile scanfile binary","",k); binary = XYFT_B; break; /* Default: Don't change anything */ } } } debug(F101,"sfile binary 2","",binary); debug(F101,"sfile sendmode","",sendmode); } if (*cmarg2) { /* If we have a send-as name... */ int y; char *s; #ifndef NOSPL /* and a script programming language */ extern int cmd_quoting; if (cmd_quoting) { /* and it's not turned off */ y = PKTNL; /* pass as-name thru the evaluator */ s = pktnam; zzstring(cmarg2,&s,&y); #ifdef COMMENT /* This ruins macros like BSEND */ if (!pktnam[0]) /* and make sure result is not empty */ sprintf(pktnam,"FILE%02ld",filcnt); #endif /* COMMENT */ } else #endif /* NOSPL */ ckstrncpy(pktnam,cmarg2,PKTNL); /* copy it literally, */ debug(F110,"sfile pktnam",pktnam,0); #ifdef COMMENT /* We don't do this any more because now we have filename templates */ cmarg2 = ""; /* and blank it out for next time. */ #endif /* COMMENT */ } if (!*pktnam) { /* No as-name... */ #ifdef NZLTOR int xfncnv, xpath; debug(F101,"sfile fnspath","",fnspath); debug(F101,"sfile fncnv","",fncnv); xfncnv = fncnv; xpath = fnspath; if (notafile) { /* If not an actual file */ xfncnv = 0; /* Don't convert name */ xpath = PATH_OFF; /* Leave path off */ } else if (xfncnv && (!strcmp(whoareu,"U1") || !strcmp(whoareu,"UN")) ) { /* Less-strict conversion if partner is UNIX or Win32 */ xfncnv = -1; } debug(F101,"sfile xpath","",xpath); debug(F101,"sfile xfncnv","",xfncnv); nzltor(filnam,pktnam,xfncnv,xpath,PKTNL); #else /* Not NZLTOR */ debug(F101,"sfile fnspath","",fnspath); if (fnspath == PATH_OFF /* Stripping path names? */ && (!notafile) /* (of actual files) */ ) { char *t; /* Yes. */ zstrip(filnam,&t); /* Strip off the path. */ debug(F110,"sfile zstrip",t,0); if (!t) t = "UNKNOWN"; /* Be cautious... */ else if (*t == '\0') t = "UNKNOWN"; ckstrncpy(pktnam,t,PKTNL); /* Copy stripped name literally. */ } else if (fnspath == PATH_ABS && !notafile) { /* Converting to absolute form */ zfnqfp(filnam,PKTNL,pktnam); } else ckstrncpy(pktnam,filnam,PKTNL); /* pktnam[] has the packet name, filnam[] has the original name. */ /* But we still need to convert pktnam if FILE NAMES CONVERTED. */ debug(F101,"sfile fncnv","",fncnv); if (fncnv && !notafile) { /* If converting names of files */ zltor(pktnam,(char *)srvcmd); /* convert it to common form, */ ckstrncpy(pktnam,(char *)srvcmd,PKTNL); *srvcmd = NUL; } #endif /* NZLTOR */ } if (!*pktnam) /* Failsafe... */ sprintf(pktnam,"FILE%02ld",filcnt); debug(F110,"sfile filnam 1",filnam,0); debug(F110,"sfile pktnam 1",pktnam,0); #ifdef PIPESEND /* If we have a send filter, substitute the current filename into it */ if (sndfilter) { char * p = NULL, * q; int n = CKMAXPATH; #ifndef NOSPL if ((p = (char *) malloc(n + 1))) { q = p; debug(F110,"sfile pipesend filter",sndfilter,0); zzstring(sndfilter,&p,&n); debug(F111,"sfile pipename",q,n); if (n <= 0) { printf( "?Sorry, send filter + filename too long, %d max.\n", CKMAXPATH ); free(q); return(0); } ckstrncpy(filnam,q,CKMAXPATH+1); free(q); } #endif /* NOSPL */ } #endif /* PIPESEND */ debug(F110,"sfile filnam 2",filnam,0); /* Log debugging info */ debug(F110,"sfile pktnam 2",pktnam,0); if (openi(filnam) == 0) /* Try to open the input file */ return(0); #ifdef CK_RESEND /* The following check is done after openi() is called, since openi() itself can change the transfer mode (as in VMS). */ if ((binary == XYFT_T #ifdef VMS || binary == XYFT_L #endif /* VMS */ ) && sendmode == SM_RESEND) { /* Trying to RESEND/REGET a file first sent in TEXT mode. */ debug(F111,"sfile error - Recover vs Text",filnam,binary); /* Set appropriate error messages and make log entries here */ #ifdef VMS if (binary == XYFT_L) ckmakmsg((char *)epktmsg, PKTMSGLEN, "Recovery attempted in LABELED mode: ", filnam, NULL, NULL ); else #endif /* VMS */ ckmakmsg((char *)epktmsg, PKTMSGLEN, "Recovery attempted in TEXT mode: ", filnam, NULL, NULL ); return(0); } if (sendmode == SM_PSEND) /* PSENDing? */ if (sendstart > (CK_OFF_T)0) /* Starting position */ if (zfseek(sendstart) < 0) /* seek to it... */ return(0); #endif /* CK_RESEND */ s = pktnam; /* Name for packet data field */ #ifdef OS2 /* Never send a disk letter. */ if (isalpha(*s) && (*(s+1) == ':')) s += 2; #endif /* OS2 */ } else { /* X-packet setup, not F-packet. */ binary = XYFT_T; /* Text always */ debug(F110,"sfile X packet",cmdstr,0); /* Log debugging info */ s = cmdstr; /* Name for data field */ } /* Now s points to the string that goes in the packet data field. */ debug(F101,"sfile binary","",binary); /* Log debugging info */ encstr((CHAR *)s); /* Encode the name. */ /* Send the F or X packet */ /* If the encoded string did not fit into the packet, it was truncated. */ if (nxtpkt() < 0) return(0); /* Bump packet number, get buffer */ rc = spack((char) (x ? 'X' : 'F'), pktnum, size, data); if (rc < 0) return(rc); #ifndef NOCSETS setxlatype(tcharset,fcharset); /* Set up charset translations */ #endif /* NOCSETS */ if (x == 0) { /* Display for F packet */ if (displa) { /* Screen */ xxscreen(SCR_FN,'F',(long)pktnum,filnam); xxscreen(SCR_AN,0,0L,pktnam); xxscreen(SCR_FS,0,calibrate ? calibrate : fsize,""); } #ifdef pdp11 tlog(F110,"Sending",filnam,0L); /* Transaction log entry */ makestr(&psfspec,filnam); /* New filename */ #else #ifndef ZFNQFP tlog(F110,"Sending",filnam,0L); makestr(&psfspec,filnam); /* New filename */ #else if (notafile) { /* If not a file log simple name */ tlog(F110,"Sending", filnam, 0L); } else { /* Log fully qualified filename */ #ifdef COMMENT /* This section generates bad code in SCO 3.2v5.0.5's cc */ char *p = NULL, *q = filnam; debug(F101,"sfile CKMAXPATH","",CKMAXPATH); if ((p = malloc(CKMAXPATH+1))) { debug(F111,"sfile calling zfnqfp",filnam,strlen(filnam)); if (zfnqfp(filnam, CKMAXPATH, p)) { debug(F111,"sfile zfnqfp ok",p,strlen(p)); q = p; } } #else char tmpbuf[CKMAXPATH+1]; char *p = tmpbuf, *q = filnam; if (zfnqfp(filnam, CKMAXPATH, p)) q = p; #endif /* COMMENT */ debug(F111,"sfile q",q,strlen(q)); tlog(F110,"Sending",q,0L); makestr(&psfspec,q); /* New preliminary filename */ #ifdef COMMENT if (p) free(p); #endif /* COMMENT */ } #endif /* ZFNQFP */ #endif /* pdp11 */ tlog(F110," as",pktnam,0L); if (binary) { /* Log file mode in transaction log */ tlog(F101," mode: binary","",(long) binary); } else { /* If text mode, check character set */ tlog(F100," mode: text","",0L); #ifndef NOCSETS if (tcharset == TC_TRANSP || xfrxla == 0) { tlog(F110," character set","transparent",0L); } else { tlog(F110," xfer character set",tcsinfo[tcharset].name,0L); tlog(F110," file character set",fcsinfo[fcharset].name,0L); } #endif /* NOCSETS */ } } else { /* Display for X-packet */ xxscreen(SCR_XD,'X',(long)pktnum,cmdstr); /* Screen */ tlog(F110,"Sending from:",cmdstr,0L); /* Transaction log */ } intmsg(++filcnt); /* Count file, give interrupt msg */ first = 1; /* Init file character lookahead. */ ffc = (CK_OFF_T)0; /* Init file character counter. */ cps = oldcps = 0L; /* Init cps statistics */ rejection = -1; fsecs = gtimer(); /* Time this file started */ #ifdef GFTIMER fpfsecs = gftimer(); debug(F101,"SFILE fpfsecs","",fpfsecs); #endif /* GFTIMER */ debug(F101,"SFILE fsecs","",fsecs); return(1); } /* S D A T A -- Send a data packet */ /* Returns -1 if no data to send (end of file), -2 if connection is broken. If there is data, a data packet is sent, and sdata() returns 1. In the streaming case, the window is regarded as infinite and we keep sending data packets until EOF or there appears to be a Kermit packet on the reverse channel. When not streaming and the window size is greater than 1, we keep sending data packets until window is full or characters start to appear from the other Kermit. In the windowing or streaming case, when there is no more data left to send (or when sending has been interrupted), sdata() does nothing and returns 0 each time it is called until the acknowledgement sequence number catches up to the last data packet that was sent. */ int sdata() { int i, x, len; char * s; debug(F101,"sdata entry, first","",first); debug(F101,"sdata drain","",drain); /* The "drain" flag is used with window size > 1. It means we have sent our last data packet. If called and drain is not zero, then we return 0 as if we had sent an empty data packet, until all data packets have been ACK'd, then then we can finally return -1 indicating EOF, so that the protocol can switch to seof state. This is a kludge, but at least it's localized... */ if (first == 1) drain = 0; /* Start of file, init drain flag. */ if (drain) { /* If draining... */ debug(F101,"sdata draining, winlo","",winlo); if (winlo == pktnum) /* If all data packets are ACK'd */ return(-1); /* return EOF indication */ else /* otherwise */ return(0); /* pretend we sent a data packet. */ } debug(F101,"sdata sbufnum","",sbufnum); for (i = sbufnum; i > 0 #ifdef STREAMING || streaming #endif /* STREAMING */ ; i--) { if (i < 0) break; debug(F101,"sdata countdown","",i); #ifdef STREAMING if (streaming) { pktnum = (pktnum + 1) % 64; winlo = pktnum; debug(F101,"sdata streaming pktnum","",pktnum); } else { #endif /* STREAMING */ x = nxtpkt(); /* Get next pkt number and buffer */ debug(F101,"sdata nxtpkt pktnum","",pktnum); if (x < 0) return(0); #ifdef STREAMING } #endif /* STREAMING */ debug(F101,"sdata packet","",pktnum); if (chkint() < 0) /* Especially important if streaming */ return(-9); if (cxseen || czseen) { /* If interrupted, done. */ if (wslots > 1) { drain = 1; debug(F100,"sdata cx/zseen windowing","",0); return(0); } else { debug(F100,"sdata cx/zseen nonwindowing","",0); return(-1); } } #ifdef DEBUG if (deblog) { debug(F101,"sdata spsiz","",spsiz); debug(F101,"sdata binary","",binary); debug(F101,"sdata parity","",parity); } #endif /* DEBUG */ #ifdef CKTUNING if (binary && !parity && !memstr && !funcstr) len = bgetpkt(spsiz); else len = getpkt(spsiz,1); #else len = getpkt(spsiz,1); #endif /* CKTUNING */ s = (char *)data; if (len == -3) { /* Timed out (e.g.reading from pipe) */ s = ""; /* Send an empty data packet. */ len = 0; } else if (len == 0) { /* Done if no data. */ if (pktnum == winlo) return(-1); drain = 1; /* But can't return -1 until all */ debug(F101,"sdata eof, drain","",drain); return(0); /* ACKs are drained. */ } debug(F101,"sdata pktnum","",pktnum); debug(F101,"sdata len","",len); debug(F011,"sdata data",data,52); x = spack('D',pktnum,len,(CHAR *)s); /* Send the data packet. */ debug(F101,"sdata spack","",x); if (x < 0) { /* Error */ ttchk(); /* See if connection is still there */ return(-2); } #ifdef STREAMING if (streaming) /* What an ACK would do. */ winlo = pktnum; #endif /* STREAMING */ x = ttchk(); /* Peek at input buffer. */ debug(F101,"sdata ttchk","",x); /* ACKs waiting, maybe? */ if (x < 0) /* Or connection broken? */ return(-2); /* Here we check to see if any ACKs or NAKs have arrived, in which case we break out of the D-packet-sending loop and return to the state switcher to process them. This is what makes our windows slide instead of lurch. */ if ( #ifdef GEMDOS /* In the Atari ST version, ttchk() can only return 0 or 1. But note: x will probably always be > 0, since the as-yet-unread packet terminator from the last packet is probably still in the buffer, so sliding windows will probably never happen when the Atari ST is the file sender. The alternative is to say "if (0)", in which case the ST will always send a window full of packets before reading any ACKs or NAKs. */ x > 0 #else /* !GEMDOS */ /* In most other versions, ttchk() returns the actual count. It can't be a Kermit packet if it's less than five bytes long. */ x > 4 + bctu #endif /* GEMDOS */ ) return(1); /* Yes, stop sending data packets */ } /* and go try to read the ACKs. */ return(1); } /* S E O F -- Send an End-Of-File packet */ /* Call with a string pointer to character to put in the data field, */ /* or else a null pointer or "" for no data. */ /* There are two "send-eof" functions. seof() is used to send the normal eof packet at the end of a file's data (even if the file has no data), or when a file transfer is interrupted. sxeof() is used to send an EOF packet that occurs because of attribute refusal or interruption prior to entering data state. The difference is purely a matter of buffer allocation and packet sequence number management. Both functions act as "front ends" to the common send-eof function, szeof(). */ /* Code common to both seof() and sxeof() */ int szeof(s) CHAR *s; { int x; lsstate = 0; /* Cancel locking-shift state */ if (!s) s = (CHAR *)""; debug(F111,"szeof",s,pktnum); if (*s) { x = spack('Z',pktnum,1,s); xitsta |= W_SEND; #ifdef COMMENT tlog(F100," *** interrupted, sending discard request","",0L); #endif /* COMMENT */ filrej++; } else { x = spack('Z',pktnum,0,(CHAR *)""); } if (x < 0) return(x); #ifdef COMMENT /* No, too soon */ discard = 0; /* Turn off per-file discard flag */ #endif /* COMMENT */ /* If we were sending from a pipe, we're not any more... */ pipesend = 0; return(0); } int seof(x) int x; { char * s; /* ckcpro.w, before calling seof(), sets window size back to 1 and then calls window(), which clears out the old buffers. This is OK because the final data packet for the file has been ACK'd. However, sdata() has already called nxtpkt(), which set the new value of pktnum which seof() will use. So all we need to do here is is allocate a new send-buffer. */ s = x ? "D" : ""; debug(F111,"seof",s,pktnum); if (getsbuf(pktnum) < 0) { /* Get a buffer for packet n */ debug(F101,"seof can't get s-buffer","",pktnum); return(-1); } return(szeof((CHAR *)s)); } /* Version of seof() to be called when sdata() has not been called before. The difference is that this version calls nxtpkt() to allocate a send-buffer and get the next packet number. */ int sxeof(x) int x; { char * s; s = x ? "D" : ""; if (nxtpkt() < 0) /* Get next pkt number and buffer */ debug(F101,"sxeof nxtpkt fails","",pktnum); else debug(F101,"sxeof packet","",pktnum); return(szeof((CHAR *)s)); } /* S E O T -- Send an End-Of-Transaction packet */ int seot() { int x; x = nxtpkt(); debug(F101,"seot nxtpkt","",x); if (x < 0) return(-1); /* Bump packet number, get buffer */ x = spack('B',pktnum,0,(CHAR *)""); /* Send the EOT packet */ if (x < 0) return(x); cxseen = czseen = discard = 0; /* Reset interruption flags */ tstats(); /* Log timing info */ return(0); } /* R P A R -- Fill the data array with my send-init parameters */ int q8flag = 0; CHAR dada[32]; /* Use this instead of data[]. */ /* To avoid some kind of wierd */ /* addressing foulup in spack()... */ /* (which might be fixed now...) */ CHAR * rpar() { char *p; int i, x, max; extern int sprmlen; max = maxdata(); /* Biggest data field I can send */ debug(F101, "rpar max 1","",max); debug(F101, "rpar sprmlen","",sprmlen); if (sprmlen > 1 && sprmlen < max) /* User override */ max = sprmlen; debug(F101, "rpar max 2","",max); if (rpsiz > MAXPACK) /* Biggest normal packet I want. */ dada[0] = (char) tochar(MAXPACK); /* If > 94, use 94, but specify */ else /* extended packet length below... */ dada[0] = (char) tochar(rpsiz); /* else use what the user said. */ dada[1] = (char) tochar(chktimo(pkttim,0)); /* When to time me out */ dada[2] = (char) tochar(mypadn); /* How much padding I need (none) */ dada[3] = (char) ctl(mypadc); /* Padding character I want */ dada[4] = (char) tochar(eol); /* End-Of-Line character I want */ dada[5] = myctlq; /* Control-Quote character I send */ if (max < 6) { dada[6] = NUL; rqf = 0; ebq = sq = NUL; return(dada); } switch (rqf) { /* 8th-bit prefix (single-shift) */ case -1: /* I'm opening the bids */ case 1: /* Other Kermit already bid 'Y' */ if (parity) ebq = sq = MYEBQ; /* So I reply with '&' if parity */ break; /* otherwise with 'Y'. */ case 2: /* Other Kermit sent a valid prefix */ if (q8flag) sq = ebq; /* Fall through on purpose */ case 0: /* Other Kermit bid nothing */ break; /* So I reply with 'Y'. */ } debug(F000,"rpar 8bq sq","",sq); debug(F000,"rpar 8bq ebq","",ebq); if (lscapu == 2) /* LOCKING-SHIFT FORCED */ dada[6] = 'N'; /* requires no single-shift */ else /* otherwise send prefix or 'Y' */ dada[6] = (char) sq; ebqsent = dada[6]; /* And remember what I really sent */ if (max < 7) { dada[7] = NUL; bctr = 1; return(dada); } dada[7] = (char) (bctr == 4) ? 'B' : bctr + '0'; /* Block check type */ if (max < 8) { dada[8] = NUL; rptflg = 0; return(dada); } if (rptena) { if (rptflg) /* Run length encoding */ dada[8] = (char) rptq; /* If receiving, agree */ else /* by replying with same character. */ dada[8] = (char) (rptq = myrptq); /* When sending use this. */ } else dada[8] = SP; /* Not enabled, put a space here. */ /* CAPAS mask */ if (max < 9) { dada[9] = NUL; atcapr = 0; lpcapr = 0; swcapr = 0; rscapr = 0; return(dada); } dada[9] = (char) tochar((lscapr ? lscapb : 0) | /* Locking shifts */ (atcapr ? atcapb : 0) | /* Attribute packets */ (lpcapr ? lpcapb : 0) | /* Long packets */ (swcapr ? swcapb : 0) | /* Sliding windows */ (rscapr ? rscapb : 0)); /* RESEND */ if (max < 10) { wslotr = 1; return(dada); } dada[10] = (char) tochar(swcapr ? wslotr : 1); /* CAPAS+1 = Window size */ if (max < 12) { rpsiz = 80; return(dada); } if (urpsiz > 94) rpsiz = urpsiz - 1; /* Long packets ... */ dada[11] = (char) tochar(rpsiz / 95); /* Long packet size, big part */ dada[12] = (char) tochar(rpsiz % 95); /* Long packet size, little part */ if (max < 16) return(dada); dada[13] = '0'; /* CAPAS+4 = WONT CHKPNT */ dada[14] = '_'; /* CAPAS+5 = CHKINT (reserved) */ dada[15] = '_'; /* CAPAS+6 = CHKINT (reserved) */ dada[16] = '_'; /* CAPAS+7 = CHKINT (reserved) */ if (max < 17) return(dada); #ifndef WHATAMI dada[17] = ' '; #else x = 0; if (server) x |= WMI_SERVE; /* Whether I am a server */ if (binary) x |= WMI_FMODE; /* My file transfer mode is ... */ if (fncnv) x |= WMI_FNAME; /* My filename conversion is ... */ #ifdef STREAMING if (streamrq == SET_ON) x |= WMI_STREAM; else if (streamrq == SET_AUTO && reliable == SET_ON) x |= WMI_STREAM; /* Always offer to stream when in remote mode and STREAMING is AUTO and RELIABLE is not OFF (i.e. is ON or AUTO). */ else if (!local && streamrq == SET_AUTO && reliable != SET_OFF) x |= WMI_STREAM; #endif /* STREAMING */ #ifdef TCPSOCKET if (clearrq == SET_ON) x |= WMI_CLEAR; else if (clearrq == SET_AUTO && /* SET CLEAR-CHANNEL AUTO */ ((network && nettype == NET_TCPB /* TCP/IP */ #ifdef RLOGCODE && ttnproto != NP_RLOGIN/* Rlogin is not clear */ && !(ttnproto >= NP_K4LOGIN && ttnproto <= NP_EK5LOGIN) #endif /* RLOGCODE */ ) #ifdef SSHBUILTIN || (network && nettype == NET_SSH) #endif /* SSHBUILTIN */ #ifdef IKSD || inserver /* We are IKSD */ #endif /* IKSD */ )) x |= WMI_CLEAR; #endif /* TCPSOCKET */ x |= WMI_FLAG; dada[17] = (char) tochar(x); #endif /* WHATAMI */ i = 18; /* Position of next field */ p = cksysid; /* WHOAMI (my system ID) */ x = strlen(p); if (max - i < x + 1) return(dada); if (x > 0) { dada[i++] = (char) tochar(x); while (*p) dada[i++] = *p++; } if (max < i+1) return(dada); #ifndef WHATAMI /* WHATAMI2 */ dada[i++] = ' '; #else debug(F101,"rpar xfermode","",xfermode); x = WMI2_FLAG; /* Is-Valid flag */ if (xfermode != XMODE_A) /* If TRANSFER MODE is MANUAL */ x |= WMI2_XMODE; /* set the XFERMODE bit */ if (recursive > 0) /* If this is a recursive transfer */ x |= WMI2_RECU; /* set the RECURSIVE bit */ dada[i++] = tochar(x); debug(F101,"rpar whatami2","",x); #endif /* WHATAMI */ dada[i] = '\0'; /* Terminate the init string */ #ifdef DEBUG if (deblog) { debug(F110,"rpar",dada,0); rdebu(dada,(int)strlen((char *)dada)); } #endif /* DEBUG */ ckstrncpy((char *)myinit,(char *)dada,MYINITLEN); return(dada); /* Return pointer to string. */ } int spar(s) CHAR *s; { /* Set parameters */ int x, y, lpsiz, biggest; extern int rprmlen, lastspmax; extern struct sysdata sysidlist[]; whatru = 0; whoareu[0] = NUL; #ifdef STREAMING streamok = 0; streaming = 0; #endif /* STREAMING */ biggest = rln; debug(F101, "spar biggest 1","",biggest); debug(F101, "spar rprmlen","",rprmlen); if (rprmlen > 1 && rprmlen < biggest) biggest = rprmlen; debug(F101, "rpar biggest 2","",biggest); debug(F110,"spar packet",s,0); s--; /* Line up with field numbers. */ /* Limit on size of outbound packets */ x = (biggest >= 1) ? xunchar(s[1]) : 80; lpsiz = spsizr; /* Remember what they SET. */ if (spsizf) { /* SET-command override? */ if (x < spsizr) spsiz = x; /* Ignore LEN unless smaller */ } else { /* otherwise */ spsiz = (x < 10) ? 80 : x; /* believe them if reasonable */ } spmax = spsiz; /* Remember maximum size */ /* Timeout on inbound packets */ if (timef) { timint = rtimo; /* SET SEND TIMEOUT value overrides */ } else { /* Otherwise use requested value, */ x = (biggest >= 2) ? xunchar(s[2]) : rtimo; /* if it is legal. */ timint = (x < 0) ? rtimo : x; } timint = chktimo(timint,timef); /* Adjust if necessary */ /* Outbound Padding */ npad = 0; padch = '\0'; if (biggest >= 3) { npad = xunchar(s[3]); if (biggest >= 4) padch = (CHAR) ctl(s[4]); else padch = 0; } if (npad) { int i; for (i = 0; i < npad; i++) padbuf[i] = dopar(padch); } /* Outbound Packet Terminator */ seol = (CHAR) (biggest >= 5) ? xunchar(s[5]) : CR; if ((seol < 1) || (seol > 31)) seol = CR; /* Control prefix that the other Kermit is sending */ x = (biggest >= 6) ? s[6] : '#'; ctlq = (CHAR) (((x > 32 && x < 63) || (x > 95 && x < 127)) ? x : '#'); /* 8th-bit prefix */ /* NOTE: Maybe this could be simplified using rcvtyp. If rcvtyp == 'Y' then we're reading the ACK, otherwise we're reading the other Kermit's initial bid. But his horrendous code has been working OK for years, so... */ rq = (biggest >= 7) ? s[7] : 0; if (rq == 'Y') rqf = 1; else if ((rq > 32 && rq < 63) || (rq > 95 && rq < 127)) rqf = 2; else rqf = 0; debug(F000,"spar 8bq rq","",rq); debug(F000,"spar 8bq sq","",sq); debug(F000,"spar 8bq ebq","",ebq); debug(F101,"spar 8bq rqf","",rqf); switch (rqf) { case 0: /* Field is missing from packet. */ ebqflg = 0; /* So no 8th-bit prefixing. */ break; case 1: /* Other Kermit sent 'Y' = Will Do. */ /* When I am the file receiver, ebqsent is 0 because I didn't send a negotiation yet. If my parity is set to anything other than NONE, either because my user SET PARITY or because I detected parity bits on this packet, I reply with '&', otherwise 'Y'. When I am the file sender, ebqsent is what I just sent in rpar(), which can be 'Y', 'N', or '&'. If I sent '&', then this 'Y' means the other Kermit agrees to do 8th-bit prefixing. If I sent 'Y' or 'N', but then detected parity on the ACK packet that came back, then it's too late: there is no longer any way for me to tell the other Kermit that I want to do 8th-bit prefixing, so I must not do it, and in that case, if there is any 8-bit data in the file to be transferred, the transfer will fail because of block check errors. The following clause covers all of these situations: */ if (parity && (ebqsent == 0 || ebqsent == '&')) { ebqflg = 1; ebq = MYEBQ; } break; case 2: /* Other Kermit sent a valid prefix */ ebqflg = (ebq == sq || sq == 'Y'); if (ebqflg) { ebq = rq; debug(F101,"spar setting parity to space","",ebq); if (!parity) parity = ttprty = 's'; } } if (lscapu == 2) { /* But no single-shifts if LOCKING-SHIFT FORCED */ ebqflg = 0; ebq = 'N'; } /* Block check */ x = 1; if (biggest >= 8) { if (s[8] == 'B') x = 4; else x = s[8] - '0'; if ((x < 1) || (x > 5)) x = 1; /* "5" 20110605 */ } bctr = x; /* Repeat prefix */ rptflg = 0; /* Assume no repeat-counts */ if (biggest >= 9) { /* Is there a repeat-count field? */ char t; /* Yes. */ t = s[9]; /* Get its contents. */ /* If I'm sending files, then I'm reading these parameters from an ACK, and so this character must agree with what I sent. */ if (rptena) { /* If enabled ... */ if ((char) rcvtyp == 'Y') { /* Sending files, reading ACK. */ if (t == myrptq) rptflg = 1; } else { /* I'm receiving files */ if ((t > 32 && t < 63) || (t > 95 && t < 127)) { rptflg = 1; rptq = t; } } } else rptflg = 0; } /* Capabilities */ atcapu = lpcapu = swcapu = rscapu = 0; /* Assume none of these. */ if (lscapu != 2) lscapu = 0; /* Assume no LS unless forced. */ y = 11; /* Position of next field, if any */ if (biggest >= 10) { x = xunchar(s[10]); debug(F101,"spar capas","",x); atcapu = (x & atcapb) && atcapr; /* Attributes */ lpcapu = (x & lpcapb) && lpcapr; /* Long packets */ swcapu = (x & swcapb) && swcapr; /* Sliding windows */ rscapu = (x & rscapb) && rscapr; /* RESEND */ debug(F101,"spar lscapu","",lscapu); debug(F101,"spar lscapr","",lscapr); debug(F101,"spar ebqflg","",ebqflg); if (lscapu != 2) lscapu = ((x & lscapb) && lscapr && ebqflg) ? 1 : 0; debug(F101,"spar swcapr","",swcapr); debug(F101,"spar swcapu","",swcapu); debug(F101,"spar lscapu","",lscapu); for (y = 10; (xunchar(s[y]) & 1) && (biggest >= y); y++); debug(F101,"spar y","",y); } /* Long Packets */ debug(F101,"spar lpcapu","",lpcapu); if (lpcapu) { if (biggest > y+1) { x = xunchar(s[y+2]) * 95 + xunchar(s[y+3]); debug(F101,"spar lp len","",x); if (spsizf) { /* If overriding negotiations */ spsiz = (x < lpsiz) ? x : lpsiz; /* do this, */ } else { /* otherwise */ spsiz = (x > MAXSP) ? MAXSP : x; /* do this. */ } if (spsiz < 10) spsiz = 80; /* Be defensive... */ } } /* (PWP) save current send packet size for optimal packet size calcs */ spmax = spsiz; /* Maximum negotiated length */ lastspmax = spsiz; /* For stats */ if (slostart && spsiz > 499) /* Slow start length */ spsiz = 244; debug(F101,"spar slow-start spsiz","",spsiz); debug(F101,"spar lp spmax","",spmax); timint = chktimo(timint,timef); /* Recalculate the packet timeout */ /* Sliding Windows... */ if (swcapr) { /* Only if requested... */ if (biggest > y) { /* See what other Kermit says */ x = xunchar(s[y+1]); debug(F101,"spar window","",x); wslotn = (x > MAXWS) ? MAXWS : x; /* wslotn = negotiated size (from other Kermit's S or I packet). wslotr = requested window size (from this Kermit's SET WINDOW command). */ if (wslotn > wslotr) /* Use the smaller of the two */ wslotn = wslotr; if (wslotn < 1) /* Watch out for bad negotiation */ wslotn = 1; if (wslotn > 1) { swcapu = 1; /* We do windows... */ if (wslotn > maxtry) /* Retry limit must be greater */ maxtry = wslotn + 1; /* than window size. */ } debug(F101,"spar window after adjustment","",x); } else { /* No window size specified. */ wslotn = 1; /* We don't do windows... */ debug(F101,"spar window","",x); swcapu = 0; debug(F101,"spar no windows","",wslotn); } } /* Now recalculate packet length based on number of windows. */ /* The nogotiated number of window slots will be allocated, */ /* and the maximum packet length will be reduced if necessary, */ /* so that a windowful of packets can fit in the big buffer. */ if (wslotn > 1) { /* Shrink to fit... */ x = adjpkl(spmax,wslotn,bigsbsiz); if (x < spmax) { spmax = x; lastspmax = spsiz; if (slostart && spsiz > 499) spsiz = 244; /* Slow start again */ debug(F101,"spar sending, redefine spmax","",spmax); } } #ifdef WHATAMI debug(F101,"spar biggest","",biggest); if (biggest > y+7) { /* Get WHATAMI info if any */ whatru = xunchar(s[y+8]); debug(F101,"spar whatru","",whatru); } if (whatru & WMI_FLAG) { /* Only valid if this bit is set */ #ifdef STREAMING if (whatru & WMI_STREAM) { if (streamrq == SET_ON || (streamrq == SET_AUTO && (reliable == SET_ON || (reliable == SET_AUTO && !local) #ifdef TN_COMPORT && !istncomport() #endif /* TN_COMPORT */ #ifdef IKSD || inserver #endif /* IKSD */ ))) { streamok = 1; /* Streaming negotiated */ slostart = 0; /* Undo slow-start machinations */ spsiz = lastspmax; } } streamed = streamok; debug(F101,"spar streamok","",streamok); debug(F101,"spar clearrq","",clearrq); if (clearrq == SET_ON || (clearrq == SET_AUTO && ((network && nettype == NET_TCPB #ifdef RLOGCODE && ttnproto != NP_RLOGIN/* Rlogin is not clear */ && !(ttnproto >= NP_K4LOGIN && ttnproto <= NP_EK5LOGIN) #endif /* RLOGCODE */ #ifdef TN_COMPORT && !istncomport() #endif /* TN_COMPORT */ ) #ifdef SSHBUILTIN || (network && nettype == NET_SSH) #endif /* SSHBUILTIN */ #ifdef IKSD || inserver #endif /* IKSD */ ))) urclear = (whatru & WMI_CLEAR); debug(F101,"spar urclear","",urclear); #ifdef CK_SPEED if (urclear) setprefix(PX_NON); #endif /* CK_SPEED */ cleared = urclear; #endif /* STREAMING */ } #endif /* WHATAMI */ if (biggest > y+8) { /* Get WHOAREYOU info if any */ int x, z; x = xunchar(s[y+9]); /* Length of it */ z = y; y += (9 + x); debug(F101,"spar sysindex x","",x); debug(F101,"spar sysindex y","",y); debug(F101,"spar sysindex biggest","",biggest); if (x > 0 && x < 16 && biggest >= y) { strncpy(whoareu,(char *)s+z+10,x); /* Other Kermit's system ID */ debug(F111,"spar whoareyou",whoareu,whoareu[0]); if (whoareu[0]) { /* Got one? */ sysindex = getsysix((char *)whoareu); debug(F101,"spar sysindex",whoareu,sysindex); } } } else goto xspar; #ifdef WHATAMI y++; /* Advance pointer */ if (biggest >= y) { whatru2 = xunchar(s[y]); /* Next field is WHATAMI2 */ debug(F101,"spar whatru2","",whatru2); if (whatru2 & WMI2_FLAG) { /* Valid only if this bit is set */ if (server) { /* Server obeys client's xfer mode */ xfermode = (whatru2 & WMI2_XMODE) ? XMODE_M : XMODE_A; debug(F101,"spar whatru2 xfermode","",xfermode); } if (whatru2 & WMI2_RECU) { /* RECURSIVE transfer */ if (fnrpath == PATH_AUTO) { /* If REC PATH AUTO */ fnrpath = PATH_REL; /* Set it to RELATIVE */ autopath = 1; /* and remember we did this */ } } } } #endif /* WHATAMI */ xspar: if (sysindex > -1) { char * p; p = sysidlist[sysindex].sid_name; tlog(F110,"Remote system type: ",p,0L); if (sysindex > 0) { /* If partnet's system type known */ whoarewe(); /* see if we are a match. */ #ifdef CK_SPEED /* Never unprefix XON and XOFF when sending to VMS */ debug(F111,"proto whoareu",whoareu,sysindex); if (!strcmp((char *)whoareu,"D7")) { debug(F111,"proto special VMS prefixing","",0); ctlp[XON] = ctlp[XOFF] = 1; ctlp[XON+128] = ctlp[XOFF+128] = 1; ctlp[3] = 1; /* Ctrl-C might be dangerous too */ ctlp[14] = ctlp[15] = 1; /* And SO/SI */ ctlp[24] = ctlp[25] = 1; /* And ^X/^Y */ ctlp[141] = 1; /* And CR+128 */ } #endif /* CK_SPEED */ } } /* Record parameters in debug log */ #ifdef DEBUG if (deblog) sdebu(biggest); #endif /* DEBUG */ numerrs = 0; /* Start counting errors here. */ return(0); } /* G N F I L E -- Get name of next file to send */ /* Expects global sndsrc to be: -9: if we are generating a file internally for calibration. -1: next filename to be obtained by calling znext(). 0: no next file name 1: (or greater) next filename to be obtained from **cmlist, or if addlist != 0, from the "filehead" linked list, or if filefile pointer not null from that file (which is already open). Returns: 1, with name of next file in filnam. 0, no more files, with filnam set to empty string. -1, file not found -2, file is not readable (but then we just skip to the next one if any) -3, read access denied -4, cancelled -5, too many files match wildcard -6, no files selected NOTE: If gnfile() returns 0, then the global variable gnferror should be checked to find out the most recent gnfile() error, and use that instead of the return code (for reasons to hard to explain). */ int gnfile() { int i = 0, x = 0; CK_OFF_T filesize = 0; int dodirstoo = 0; int retcode = 0; char fullname[CKMAXPATH+1]; dodirstoo = ((what & (W_FTP|W_SEND)) == (W_FTP|W_SEND)) && recursive; debug(F101,"gnfile sndsrc","",sndsrc); debug(F101,"gnfile filcnt","",filcnt); debug(F101,"gnfile what","",what); debug(F101,"gnfile recursive","",recursive); debug(F101,"gnfile dodirstoo","",dodirstoo); gnferror = 0; fsize = (CK_OFF_T)-1; /* Initialize file size */ fullname[0] = NUL; #ifdef VMS /* In VMS, zopeni() sets binary 0/1 automatically from the file attributes. Don't undo it here. */ debug(F101,"gnfile VMS binary","",binary); #else /* VMS */ if (!(what & W_REMO) && (xfermode == XMODE_A) #ifndef NOMSEND && !addlist #endif /* NOMSEND */ ) { extern int stdinf; if (!stdinf) /* Not if sending from stdin */ #ifdef WHATAMI /* We don't do this in server mode because it undoes WHATAMI */ if (!server || (server && ((whatru & WMI_FLAG) == 0))) #endif /* WHATAMI */ binary = gnf_binary; /* Restore prevailing transfer mode */ debug(F101,"gnfile binary = gnf_binary","",gnf_binary); } #endif /* VMS */ #ifdef PIPESEND debug(F101,"gnfile pipesend","",pipesend); if (pipesend) { /* First one */ if (filcnt == 0) { ckstrncpy(filnam,cmarg,CKMAXPATH+1); return(1); } else { /* There's only one... */ *filnam = NUL; pipesend = 0; return(0); } } #endif /* PIPESEND */ #ifdef CALIBRATE if (sndsrc == -9) { debug(F100,"gnfile calibrate","",0); ckstrncpy(filnam,"CALIBRATION",CKMAXPATH); nfils = 0; cal_j = 0; fsize = calibrate; sndsrc = 0; /* For next time */ nfils = 0; return(1); } #endif /* CALIBRATE */ #ifndef NOSPL if (sndarray) { /* Sending from an array */ extern char sndxnam[]; /* Pseudo filename */ debug(F100,"gnfile array","",0); nfils = 0; fsize = (CK_OFF_T)-1; /* Size unknown */ sndsrc = 0; ckstrncpy(filnam,sndxnam,CKMAXPATH); return(1); } #endif /* NOSPL */ if (sndsrc == 0) { /* It's not really a file */ if (nfils > 0) { /* It's a pipe, or stdin */ ckstrncpy(filnam,*cmlist,CKMAXPATH+1); /* Copy its "name" */ nfils = 0; /* There is no next file */ return(1); /* OK this time */ } else return(0); /* but not next time */ } /* If file group interruption (C-Z) occurred, fail. */ if (czseen) { tlog(F100,"Transaction cancelled","",0L); debug(F100,"gnfile czseen","",0); return(-4); } /* Loop through file list till we find a readable, sendable file */ filesize = (CK_OFF_T)-1; /* Loop exit (file size) variable */ while (filesize < 0) { /* Keep trying till we get one... */ retcode = 0; if (sndsrc > 0) { /* File list in cmlist or file */ if (filefile) { /* Reading list from file... */ if (zsinl(ZMFILE,filnam,CKMAXPATH) < 0) { /* Read a line */ zclose(ZMFILE); /* Failed */ debug(F110,"gnfile filefile EOF",filefile,0); makestr(&filefile,NULL); return(0); } debug(F110,"gnfile filefile filnam",filnam,0); } debug(F101,"gnfile nfils","",nfils); if (nfils-- > 0 || filefile) { /* Still some left? */ #ifndef NOMSEND if (addlist) { if (filenext && filenext->fl_name) { ckstrncpy(filnam,filenext->fl_name,CKMAXPATH+1); cmarg2 = filenext->fl_alias ? filenext->fl_alias : ""; binary = filenext->fl_mode; } else { printf("?Internal error expanding ADD list\n"); return(-5); } filenext = filenext->fl_next; debug(F111,"gnfile addlist filnam",filnam,nfils); } else if (sndsrc > 0 && !filefile) { #endif /* NOMSEND */ ckstrncpy(filnam,*cmlist++,CKMAXPATH+1); debug(F111,"gnfile cmlist filnam",filnam,nfils); #ifndef NOMSEND } #endif /* NOMSEND */ i = 0; #ifndef NOSERVER debug(F101,"gnfile ngetpath","",ngetpath); #endif /* NOSERVER */ nextinpath: #ifndef NOSERVER fromgetpath = 0; if (server && !isabsolute(filnam) && (ngetpath > i)) { ckstrncpy(fullname,getpath[i],CKMAXPATH+1); ckstrncat(fullname,filnam,CKMAXPATH); debug(F111,"gnfile getpath",fullname,i); fromgetpath = 1; i++; } else { i = ngetpath + 1; #else i = 1; /* ? */ #endif /* NOSERVER */ ckstrncpy(fullname,filnam,CKMAXPATH+1); debug(F110,"gnfile absolute",fullname,0); #ifndef NOSERVER } #endif /* NOSERVER */ if (iswild(fullname) #ifdef RECURSIVE || recursive > 0 || !strcmp(fullname,".") #endif /* RECURSIVE */ ) { /* It looks wild... */ /* First check if a file with this name exists */ debug(F110,"gnfile wild",fullname,0); if (zchki(fullname) >= 0) { /* Here we have a file whose name actually contains wildcard characters. */ goto gotnam; } #ifdef COMMENT nzxopts = ZX_FILONLY; /* (was 0: 25 Jul 2001 fdc) */ #else nzxopts = recursive ? 0 : ZX_FILONLY; /* 30 Jul 2001 */ #endif /* COMMENT */ if (nolinks) nzxopts |= ZX_NOLINKS; /* (26 Jul 2001 fdc) */ #ifdef UNIXOROSK if (matchdot) nzxopts |= ZX_MATCHDOT; #endif /* UNIXOROSK */ if (recursive) nzxopts |= ZX_RECURSE; x = nzxpand(fullname,nzxopts); /* Expand wildcards */ debug(F101,"gnfile nzxpand","",x); if (x == 1) { int xx; xx = znext(fullname); debug(F111,"gnfile znext A",fullname,xx); goto gotnam; } if (x == 0) { /* None match */ #ifndef NOSERVER if (server && ngetpath > i) goto nextinpath; #endif /* NOSERVER */ retcode = -1; debug(F101,"gnfile gnferror A","",gnferror); gnferror = -1; continue; } if (x < 0) { /* Too many to expand */ debug(F101,"gnfile gnferror B","",gnferror); gnferror = -5; return(-5); } sndsrc = -1; /* Change send-source to znext() */ } } else { /* We're out of files. */ debug(F111,"gnfile done",ckitoa(gnferror),nfils); *filnam = '\0'; return(0); } } /* Otherwise, step to next element of internal wildcard expansion list. */ if (sndsrc == -1) { int xx = 0; while (1) { debug(F111,"gnfile znext X",filnam,xx); xx = znext(filnam); debug(F111,"gnfile znext B",filnam,xx); if (!filnam[0]) break; if (dodirstoo) { debug(F111,"gnfile FTP MPUT /RECURSIVE",filnam,xx); break; } if (!isdir(filnam)) break; } debug(F111,"gnfile znext C",filnam,x); if (!filnam[0]) { /* If no more, */ sndsrc = 1; /* go back to previous list */ debug(F101,"gnfile setting sndsrc back","",sndsrc); continue; } else ckstrncpy(fullname,filnam,CKMAXPATH+1); } /* Get here with a filename. */ gotnam: debug(F110,"gnfile fullname",fullname,0); if (fullname[0]) { #ifdef DTILDE char * dirp = ""; if (fullname[0] == '~') { dirp = tilde_expand((char *)fullname); if (*dirp) ckstrncpy(fullname,dirp,CKMAXPATH+1); } #endif /* DTILDE */ filesize = zchki(fullname); /* Check if file readable */ debug(F111,"gnfile zchki",fullname,filesize); retcode = filesize; /* Possible return code */ if (filesize == (CK_OFF_T)-2 && dodirstoo) { filesize = 0; } if (filesize < 0) { gnferror = (int)filesize; debug(F101,"gnfile gnferror C","",gnferror); } if (filesize == (CK_OFF_T)-1) { /* If not found */ debug(F100,"gnfile -1","",0); #ifndef NOSERVER if (server && ngetpath > i) goto nextinpath; #endif /* NOSERVER */ debug(F110,"gnfile skipping:",fullname,0); tlog(F110,fullname,": open failure - skipped",0); xxscreen(SCR_FN,0,0l,fullname); xxscreen(SCR_ST,ST_SKIP,SKP_ACC,fullname); #ifdef TLOG if (tralog && !tlogfmt) doxlog(what,fullname,fsize,binary,1,"Skipped"); #endif /* TLOG */ continue; } else if (filesize < 0) { if (filesize == (CK_OFF_T)-3) { /* Exists but not readable */ debug(F100,"gnfile -3","",0); filrej++; /* Count this one as not sent */ tlog(F110,"Read access denied",fullname,0); /* Log this */ xxscreen(SCR_FN,0,0l,fullname); xxscreen(SCR_ST,ST_SKIP,SKP_ACC,fullname); /* Display it */ #ifdef TLOG if (tralog && !tlogfmt) doxlog(what,fullname,fsize,binary,1,"Skipped"); #endif /* TLOG */ } continue; } else { int xx; fsize = filesize; /* +++ */ debug(F111,"gnfile sndsmaller",ckfstoa(sndsmaller),sndsmaller); debug(F111,"gnfile sndlarger",ckfstoa(sndlarger),sndlarger); debug(F111,"gnfile (CK_OFF_T)-1",ckfstoa((CK_OFF_T)-1),(CK_OFF_T)-1); xx = fileselect(fullname, sndafter, sndbefore, sndnafter,sndnbefore, sndsmaller,sndlarger, skipbup, NSNDEXCEPT,sndexcept); debug(F111,"gnfile fileselect",fullname,xx); if (!xx) { filesize = (CK_OFF_T)-1; gnferror = -6; debug(F101,"gnfile gnferror D","",gnferror); continue; } ckstrncpy(filnam,fullname,CKMAXPATH+1); return(1); } #ifdef COMMENT /* This can't be right! */ } else { /* sndsrc is 0... */ if (!fileselect(fullname, sndafter, sndbefore, sndnafter,sndnbefore, sndsmaller,sndlarger, skipbup, NSNDEXCEPT,sndexcept)) { gnferror = -6; debug(F111,"gnfile fileselect",fullname,gnferror); filesize = (CK_OFF_T)-1; continue; } ckstrncpy(filnam,fullname,CKMAXPATH+1); return(1); #endif /* COMMENT */ } } debug(F101,"gnfile result","",retcode); *filnam = '\0'; return(0); } /* The following bunch of routines feed internally generated data to the server to send to the client in response to REMOTE commands like DIRECTORY, DELETE, and so on. We have to write these lines in the format appropriate to our platform, so they can be converted to generic (CRLF) text format by the packetizer. */ #ifdef UNIX char * endline = "\12"; #else #ifdef datageneral char * endline = "\12"; #else #ifdef MAC char * endline = "\15"; #else #ifdef OSK char * endline = "\15"; #else char * endline = "\15\12"; #endif /* OSK */ #endif /* MAC */ #endif /* datageneral */ #endif /* UNIX */ #ifdef MAC #define FNCBUFL 256 #else #ifdef CKSYMLINK #define FNCBUFL (CKMAXPATH + CKMAXPATH + 64) #else #define FNCBUFL (CKMAXPATH + 64) #endif /* CKSYMLINK */ #endif /* MAC */ /* NB: The minimum FNCBUFL is 255 */ static CHAR funcbuf[FNCBUFL]; static int funcnxt = 0; static int funclen = 0; static int nxpnd = -1; static long ndirs = 0; static long nfiles = 0; static CK_OFF_T nbytes = 0; int sndstring(p) char * p; { #ifndef NOSERVER nfils = 0; /* No files, no lists. */ xflg = 1; /* Flag we must send X packet. */ ckstrncpy(cmdstr,versio,CMDSTRL); /* Data for X packet. */ first = 1; /* Init getchx lookahead */ memstr = 1; /* Just set the flag. */ memptr = p; /* And the pointer. */ binary = XYFT_T; /* Text mode for this. */ return(sinit()); #else return(0); #endif /* NOSERVER */ } /* S N D H L P -- Routine to send builtin help */ static int srvhlpnum = 0; #ifdef IKSD static char *nmx[] = { "Disabled", "Disabled", "Enabled", "Enabled" }; #endif /* IKSD */ static char * xnm(x) int x; { #ifdef IKSD if (inserver) return(nmx[x]); else #endif /* IKSD */ return(nm[x]); } static int nxthlp( #ifdef CK_ANSIC void #endif /* CK_ANSIC */ ) { int x = 0; extern int en_cpy, en_cwd, en_del, en_dir, en_fin, en_get, en_bye, en_mai, en_pri, en_hos, en_ren, en_sen, en_spa, en_set, en_typ, en_who, /* en_ret, */ en_mkd, en_rmd, en_asg, en_que, en_xit, x_login, x_logged, xfinish; extern char * ckxsys; if (funcnxt < funclen) return (funcbuf[funcnxt++]); switch (srvhlpnum++) { case 0: x = ckstrncpy((char *)funcbuf, "Client Command Status Description\n", FNCBUFL ); if (x_login && !x_logged) { x += ckstrncat((char *)funcbuf, " REMOTE LOGIN required\n", FNCBUFL ); } if (FNCBUFL - x > 74) sprintf((char *)(funcbuf+x)," GET %-14s%s\n", xnm(en_get), "Transfer file(s) from server to client." ); break; /* NOTE: The minimum funcbuf[] size is 255; all of the following are safe. */ case 1: sprintf((char *)funcbuf," SEND %-14s%s\n", xnm(en_sen), "Transfer file(s) from client to server." ); break; case 2: sprintf((char *)funcbuf," MAIL %-14s%s\n", xnm(inserver ? 0 : en_mai), "Send file(s) as e-mail." ); break; case 3: #ifndef NOSPL sprintf((char *)funcbuf," REMOTE ASSIGN %-14s%s\n", xnm(en_asg), "Assign value to server variable or macro." ); #else sprintf((char *)funcbuf," REMOTE ASSIGN not configured\n"); #endif /* NOSPL */ break; case 4: sprintf((char *)funcbuf," REMOTE CD %-14s%s\n", xnm(en_cwd), "Change server's directory." ); break; case 5: #ifdef ZCOPY sprintf((char *)funcbuf," REMOTE COPY %-14s%s\n", xnm(en_cpy), "Copy a file on the server." ); #else sprintf((char *)funcbuf," REMOTE COPY not configured\n"); #endif /* ZCOPY */ break; case 6: sprintf((char *)funcbuf," REMOTE DELETE %-14s%s\n", xnm(en_del), "Delete a file on the server." ); break; case 7: sprintf((char *)funcbuf," REMOTE DIRECTORY %-14s%s\n", xnm(en_dir), "List files on the server." ); break; case 8: sprintf((char *)funcbuf," REMOTE EXIT %-14s%s\n", xnm(en_xit), "Exit from Kermit server program." ); break; case 9: sprintf((char *)funcbuf," REMOTE HOST %-14s%s\n", xnm(inserver ? 0 : en_hos), #ifdef datageneral "Execute a CLI command on the server." #else #ifdef VMS "Execute a DCL command on the server." #else "Execute a shell command on the server." #endif /* VMS */ #endif /* datageneral */ ); break; case 10: sprintf((char *)funcbuf," REMOTE PRINT %-14s%s\n", xnm(inserver ? 0 : en_pri), "Send a file to the server for printing." ); break; case 11: #ifndef NOSPL sprintf((char *)funcbuf," REMOTE QUERY %-14s%s\n", xnm(en_que), "Get value of server variable or macro." ); #else sprintf((char *)funcbuf," REMOTE QUERY not configured\n"); #endif /* NOSPL */ break; case 12: sprintf((char *)funcbuf," REMOTE MKDIR %-14s%s\n", xnm(en_mkd), "Create a directory on the server." ); break; case 13: sprintf((char *)funcbuf," REMOTE RMDIR %-14s%s\n", xnm(en_rmd), "Remove a directory on the server." ); break; case 14: sprintf((char *)funcbuf," REMOTE RENAME %-14s%s\n", xnm(en_ren), "Rename a file on the server." ); break; case 15: sprintf((char *)funcbuf," REMOTE SET %-14s%s\n", xnm(en_set), "Set a parameter on the server" ); break; case 16: sprintf((char *)funcbuf," REMOTE SPACE %-14s%s\n", xnm(en_spa), "Inquire about disk space on the server." ); break; case 17: sprintf((char *)funcbuf," REMOTE TYPE %-14s%s\n", xnm(en_typ), "Display a server file on your screen." ); break; case 18: sprintf((char *)funcbuf," REMOTE WHO %-14s%s\n", xnm(inserver ? 0 : en_who), "List who is logged in to the server." ); break; case 19: sprintf((char *)funcbuf," FINISH %-14s%s\n", xnm(en_fin), xfinish ? "Exit from Kermit server program." : "Return the server to its command prompt." ); break; case 20: sprintf((char *)funcbuf," BYE %-14s%s\n\n", xnm(en_bye), "Log the server out and disconnect." ); break; default: return(-1); } funcnxt = 0; funclen = strlen((char *)funcbuf); return(funcbuf[funcnxt++]); } int sndhlp() { #ifndef NOSERVER extern char * ckxsys; first = 1; /* Init getchx lookahead */ nfils = 0; /* No files, no lists. */ xflg = 1; /* Flag we must send X packet. */ ckstrncpy(cmdstr,"REMOTE HELP",CMDSTRL); /* Data for X packet. */ sprintf((char *)funcbuf, "C-Kermit %s,%s\n\n", versio, ckxsys); funclen = strlen((char *)funcbuf); #ifdef IKSD if (inserver) { sprintf((char *)(funcbuf+funclen), "Internet Kermit Service (EXPERIMENTAL)\n\n"); funclen = strlen((char *)funcbuf); } #endif /* IKSD */ funcnxt = 0; funcptr = nxthlp; funcstr = 1; srvhlpnum = 0; binary = XYFT_T; /* Text mode for this. */ return(sinit()); #else return(0); #endif /* NOSERVER */ } /* Returns the next available character, -1 if no more data. */ static int nxttype( #ifdef CK_ANSIC void #endif /* CK_ANSIC */ ) { int c; if (zchin(ZIFILE,&c) < 0) { zclose(ZIFILE); return(-1); } else { return((unsigned)c); } } /* S N D T Y P -- TYPE a file to remote client */ int sndtype(file) char * file; { #ifndef NOSERVER char name[CKMAXPATH+1]; #ifdef OS2 char * p = NULL; if (*file) { ckstrncpy(name, file, CKMAXPATH+1); /* change / to \. */ p = name; while (*p) { /* Change them back to \ */ if (*p == '/') *p = '\\'; p++; } } else return(0); #else ckstrncpy(name, file, CKMAXPATH+1); #endif /* OS2 */ funcnxt = 0; funclen = strlen((char *)funcbuf); if (zchki(name) == -2) { /* Found a directory */ return(0); } if (!zopeni(ZIFILE,name)) return(0); nfils = 0; /* No files, no lists. */ xflg = 1; /* Flag we must send X packet. */ ckstrncpy(cmdstr,"type",CMDSTRL); /* Data for X packet. */ first = 1; /* Init getchx lookahead */ funcstr = 1; /* Just set the flag. */ funcptr = nxttype; /* And the pointer. */ binary = XYFT_T; /* Text mode for this */ return(sinit()); #else return(0); #endif /* NOSERVER */ } /* N X T D I R -- Provide data for senddir() Returns the next available character or -1 if no more data. */ #ifndef NOICP /* Directory listing parameters set by the user interface, if any. */ extern int dir_head, dir_dots, dir_back; #endif /* NOICP */ static int sd_hdg, sd_bkp, sd_dot; /* Local listing parameters */ static int nxtdir( #ifdef CK_ANSIC void #endif /* CK_ANSIC */ ) { char name[CKMAXPATH+1], dbuf[24], *p = NULL; char *dstr = NULL, * lnk = ""; CHAR c, * linebuf = funcbuf; #ifdef OSK /* Work around bugs in OSK compiler */ char *dirtag = "directories"; char *filetag = "files"; char *bytetag = "bytes"; #endif /* OSK */ CK_OFF_T len = 0; int x, itsadir = 0, gotone = 0; #ifdef DEBUG if (deblog) { debug(F101,"nxtdir funcnxt","",funcnxt); debug(F101,"nxtdir funclen","",funclen); debug(F110,"nxtdir funcbuf",funcbuf+funcnxt,0); } #endif /* DEBUG */ if (funcnxt < funclen) { /* Return next character from buffer */ c = funcbuf[funcnxt++]; debug(F000,"nxtdir return 1","",(unsigned)(c & 0xff)); return((unsigned)(c & 0xff)); } while (nxpnd > 0) { /* Buffer needs refill */ nxpnd--; znext(name); /* Get next filename */ if (!name[0]) { /* None left - done */ nxpnd = 0; return(nxtdir()); } if (sd_bkp) { /* Showing backup files? */ gotone = 1; /* Yes, no need to check. */ break; } x = ckmatch( /* No - see if this is one */ #ifdef CKREGEX "*.~[0-9]*~" /* Not perfect but close enough. */ #else "*.~*~" /* Less close. */ #endif /* CKREGEX */ ,name,filecase,1); debug(F111,"nxtdir ckmatch",name,x); if (x) { continue; /* It's a backup file - skip it */ } else { gotone = 1; /* It's not, break from loop. */ break; } } if (gotone) { len = zgetfs(name); /* Get file size */ debug(F111,"nxtdir zgetfs",name,len); #ifdef VMSORUNIX itsadir = zgfs_dir; /* See if it's a directory */ #else itsadir = (len == -2 || isdir(name)); #endif /* VMSORUNIX */ dstr = zfcdat(name); debug(F111,"nxtdir zcfdat",dstr,0); if (!dstr) dstr = "0000-00-00 00:00:00"; if (!*dstr) { dstr = "0000-00-00 00:00:00"; } else { dbuf[0] = dstr[0]; dbuf[1] = dstr[1]; dbuf[2] = dstr[2]; dbuf[3] = dstr[3]; dbuf[4] = '-'; dbuf[5] = dstr[4]; dbuf[6] = dstr[5]; dbuf[7] = '-'; dbuf[8] = dstr[6]; dbuf[9] = dstr[7]; strcpy(dbuf+10,dstr+8); dstr = dbuf; } #ifdef CK_PERMS #ifdef VMSORUNIX p = ziperm(name); /* Get permissions */ #else p = zgperm(name); #endif /* VMSORUNIX */ #else p = NULL; #endif /* CK_PERMS */ debug(F110,"domydir perms",p,0); #ifdef VMS /* Make name relative */ ckstrncpy(name,zrelname(name,zgtdir()),CKMAXPATH+1); #endif /* VMS */ if (itsadir) { ndirs++; } else { nfiles++; nbytes += len; } lnk = ""; #ifdef UNIX #ifdef CKSYMLINK if (zgfs_link) { extern char linkname[]; lnk = linkname; } debug(F111,"nxtdir linkname",lnk,zgfs_link); #endif /* CKSYMLINK */ #endif /* UNIX */ /* The following sprintf's are safe; linebuf is a pointer to funcbuf, which is 64 bytes larger than CKMAXPATH (or double CKMAXPATH when symlinks are possible). 64 allows for the fixed-field portions of the file listing line: permissions, size, and date. CKMAXPATH allows for the longest possible pathname. */ if (itsadir && len < 0) { /* Directory */ #ifdef VMS sprintf((char *)linebuf, "%-22s%-10s %s %s\n",p,"",dstr,name); #else if (p) sprintf((char *)linebuf, "%10s%-10s %s %s\n",p,"",dstr,name); else sprintf((char *)linebuf, "%-10s %s %s\n", "", dstr, name); #endif /* VMS */ } else { /* Regular file */ #ifdef VMS sprintf((char *)linebuf, "%-22s%10s %s %s\n", p, ckfstoa(len), dstr, name); #else if (p) sprintf((char *)linebuf, "%10s%10s %s %s%s%s\n", p, ckfstoa(len), dstr, name, *lnk ? " -> " : "", lnk ); else sprintf((char *)linebuf, "%10s %s %s%s%s\n", ckfstoa(len), dstr, name, *lnk ? " -> " : "", lnk ); #endif /* VMS */ } funcnxt = 0; funclen = strlen((char *)funcbuf); } else if (sd_hdg && nxpnd == 0) { /* Done, send summary */ char *blankline = ""; /* At beginning of summary */ /* The idea is to prevent (a) unnecessary multiple blanklines, and (b) prompt-stomping. Preventing (b) is practically impossible, because it depends on the client so for now always include that final CRLF. */ if (!ndirs || !nbytes || !nfiles) blankline = endline; #ifdef OSK /* Workaround bugs in OS-9 compiler... */ if (ndirs == 1) dirtag = "directory"; if (nfiles == 1) filetag = "file"; if (nbytes == (CK_OFF_T)1) bytetag = "byte"; sprintf((char *)funcbuf, "%sSummary: %ld %s, %ld %s, %s %s%s", blankline, ndirs, dirtag, nfiles, filetag, ckfstoa(nbytes), bytetag, endline); #else sprintf((char *)funcbuf, "%sSummary: %ld director%s, %ld file%s, %s byte%s%s", blankline, ndirs, (ndirs == 1) ? "y" : "ies", nfiles, (nfiles == 1) ? "" : "s", ckfstoa(nbytes), (nbytes == (CK_OFF_T)1) ? "" : "s", endline ); #endif /* OSK */ nxpnd--; funcnxt = 0; funclen = strlen((char *)funcbuf); } else { funcbuf[0] = '\0'; funcnxt = 0; funclen = 0; } debug(F101,"nxtdir funclen","",funclen); if (funcnxt < funclen) { /* If we have data to send... */ c = funcbuf[funcnxt++]; debug(F000,"nxtdir return 2","",(unsigned)(c & 0xff)); return((unsigned)(c & 0xff)); } else return(-1); /* Nothing left, done. */ } /* S N D D I R -- send directory listing */ int snddir(spec) char * spec; { #ifndef NOSERVER char * p = NULL, name[CKMAXPATH+1]; int t = 0, rc = 0; char fnbuf[CKMAXPATH+1]; debug(F111,"snddir matchdot",spec,matchdot); #ifndef NOICP debug(F111,"snddir dir_dots",spec,dir_dots); sd_hdg = dir_head > 0; /* Import listing parameters if any */ sd_bkp = dir_back > 0; if (dir_dots > -1) sd_dot = dir_dots; else sd_dot = matchdot; #else sd_hdg = 1; /* Or use hardwired defaults */ sd_bkp = 1; sd_dot = matchdot; #endif /* NOICP */ if (!spec) spec = ""; debug(F111,"snddir sd_dot",spec,sd_dot); if (*spec) { #ifdef COMMENT zfnqfp(spec,CKMAXPATH,name); debug(F110,"snddir zfnqfp",name,0); #else ckstrncpy(name,spec,CKMAXPATH+1); debug(F110,"snddir name",name,0); #endif /* COMMENT */ } else { #ifdef OS2 strcpy(name, "*"); #else #ifdef UNIXOROSK strcpy(name, "./*"); #else #ifdef VMS strcpy(name, "*.*"); #else #ifdef datageneral strcpy(name, "+"); #else debug(F101,"snddir quit (no filespec)","",0); return(0); #endif /* datageneral */ #endif /* VMS */ #endif /* UNIX */ #endif /* OS2 */ } debug(F110,"snddir name 1",name,0); ndirs = 0L; nfiles = 0L; nbytes = (CK_OFF_T)0; if (zfnqfp(name,CKMAXPATH,fnbuf)) debug(F110,"snddir name 2",name,0); p = name + strlen(name); /* Move it to end of list */ /* sprintf safe because funcbuf size >= max path len + 64 */ if (sd_hdg) { sprintf((char *)funcbuf,"Listing files: %s%s%s",fnbuf,endline,endline); funcnxt = 0; funclen = strlen((char *)funcbuf); } diractive = 1; #ifdef OS2 if (zchki(name) == -2) { /* Found a directory */ p--; if (*p == '\\' || *p == '/') ckstrncat(name, "*", CKMAXPATH); else if (*p == ':') ckstrncat(name, ".", CKMAXPATH); else ckstrncat(name, "\\*", CKMAXPATH); debug(F110,"snddir directory",name,0); } #else if (!iswild(name) && isdir(name)) { char * s = name; p--; #ifdef UNIXOROSK if (*p == '/') /* So append wildcard to it */ ckstrncat(s, "*", CKMAXPATH); else ckstrncat(s, "/*", CKMAXPATH); #else #ifdef VMS if (*p == ']' || *p == '>' || *p == ':') ckstrncat(s, "*.*", CKMAXPATH); #else #ifdef datageneral if (*p == ':') ckstrncat(s, "+", CKMAXPATH); else ckstrncat(s, ":+", CKMAXPATH); #else #ifdef VOS if (*p == '>') ckstrncat(s, "*", CKMAXPATH); else ckstrncat(s, ">*", CKMAXPATH); #endif /* VOS */ #endif /* datageneral */ #endif /* VMS */ #endif /* UNIXOROSK */ debug(F110,"snddir directory",name,0); } #endif /* OS2 */ nzxopts = 0; #ifdef UNIX { extern char ** mtchs; debug(F111,"snddir sd_dot",spec,sd_dot); if (sd_dot > 0) nzxopts |= ZX_MATCHDOT; if (recursive) nzxopts |= ZX_RECURSE; debug(F111,"snddir nzxopts",spec,nzxopts); nxpnd = nzxpand(name,nzxopts); /* Get the array of names */ sh_sort(mtchs,NULL,nxpnd,0,0,1); /* Sort the array */ } #else if (recursive) nzxopts |= ZX_RECURSE; nxpnd = nzxpand(name,nzxopts); #endif /* UNIX */ debug(F101,"snddir nzxpand nxpnd","",nxpnd); if (nxpnd < 1) return(-1); nfils = 0; /* No files, no lists. */ xflg = 1; /* Flag we must send X packet. */ if ((int)strlen(name) < CMDSTRL - 11) /* Data for X packet. */ sprintf(cmdstr,"DIRECTORY %s",name); /* safe */ else ckstrncpy(cmdstr,"DIRECTORY",CMDSTRL); first = 1; /* Init getchx lookahead */ funcstr = 1; /* Just set the flag. */ funcptr = nxtdir; /* And the pointer. */ binary = XYFT_T; /* Text mode for this */ rc = sinit(); /* 26 Aug 2005 */ debug(F111,"snddir","sinit()",rc); return(rc); #else return(0); #endif /* NOSERVER */ } /* N X T D E L -- provide data for delete */ /* Returns the next available character or -1 if no more data */ static int nxtdel( #ifdef CK_ANSIC void #endif /* CK_ANSIC */ ) { char name[257], *p = NULL; int len = 0; if (funcnxt < funclen) return ((unsigned)funcbuf[funcnxt++]); if (nxpnd > 0) { nxpnd--; znext(name); if (!name[0]) { nxpnd = 0; return(nxtdel()); } len = zchki(name); /* Find just the name of the file */ for (p = name + strlen(name); p != name && *p != '/' ; p--) ; if (*p == '/') p++; /* sprintf's safe because size of funcbuf >= 64 + maxpathlen */ if (len > -1L) { if (zdelet(name)) { sprintf((char *)funcbuf," %10s: %s%s","skipping",p,endline); } else { nfiles++; nbytes += len; sprintf((char *)funcbuf," %10s: %s%s","deleted",p,endline); } } else sprintf((char *)funcbuf," directory: %s%s", p, endline); funcnxt = 0; funclen = strlen((char *)funcbuf); } else /* If done processing the expanded entries send a summary statement */ if (nxpnd == 0) { sprintf((char *)funcbuf, "%s%ld file%s deleted, %s byte%s freed%s", endline, nfiles, (nfiles == 1) ? "" : "s", ckfstoa(nbytes), (nbytes == (CK_OFF_T)1) ? "" : "s", endline ); nxpnd--; funcnxt = 0; funclen = strlen((char *)funcbuf); } else { funcbuf[0] = '\0'; funcnxt = 0; funclen = 0; } /* If we have data to send */ if (funcnxt < funclen) return ((unsigned)funcbuf[funcnxt++]); /* Return a character */ else return(-1); /* No more input */ } /* S N D D E L -- Send delete message */ int snddel(spec) char * spec; { #ifndef NOSERVER char name[CKMAXPATH+1]; #ifdef OS2 char * p = NULL; #endif /* #ifdef OS2 */ if (!*spec) return(0); ckstrncpy(name, spec, CKMAXPATH+1); #ifdef OS2 /* change / to \. */ p = name; while (*p) { /* Change them back to \ */ if (*p == '/') *p = '\\'; p++; } #endif /* OS2 */ nfiles = 0L; nbytes = (CK_OFF_T)0; sprintf((char *)funcbuf,"Deleting \"%s\"%s",name,endline); funcnxt = 0; funclen = strlen((char *)funcbuf); nzxopts = ZX_FILONLY; /* Files only */ #ifdef UNIXOROSK if (matchdot) nzxopts |= ZX_MATCHDOT; #endif /* UNIXOROSK */ #ifdef COMMENT /* Recursive deleting not supported yet */ if (recursive) nzxopts |= ZX_RECURSE; #endif /* COMMENT */ nxpnd = nzxpand(name,nzxopts); if (nxpnd < 1) return(-1); nfils = 0; /* No files, no lists. */ xflg = 1; /* Flag we must send X packet. */ ckstrncpy(cmdstr,"REMOTE DELETE",CMDSTRL); /* Data for X packet. */ first = 1; /* Init getchx lookahead */ funcstr = 1; /* Just set the flag. */ funcptr = nxtdel; /* And the pointer. */ binary = XYFT_T; /* Use text mode for this, */ return(sinit()); #else return(0); #endif /* NOSERVER */ } #ifdef OS2 /* S N D S P A C E -- send disk space message */ int sndspace(drive) int drive; { #ifndef NOSERVER static char spctext[64]; unsigned long space; if (drive) { space = zdskspace(drive - 'A' + 1); if (space > 0 && space < 1024) sprintf(spctext, " Drive %c: unknown%s", drive, endline ); else sprintf(spctext, " Drive %c: %ldK free%s", drive, space / 1024L, endline ); } else { space = zdskspace(0); if (space > 0 && space < 1024) sprintf(spctext, " Free space: unknown%s", endline); else sprintf(spctext, " Free space: %ldK%s", space / 1024L, endline); } nfils = 0; /* No files, no lists. */ xflg = 1; /* Flag we must send X packet. */ ckstrncpy(cmdstr,"free space",CMDSTRL); /* Data for X packet. */ first = 1; /* Init getchx lookahead */ memstr = 1; /* Just set the flag. */ memptr = spctext; /* And the pointer. */ binary = XYFT_T; /* Text mode for this. */ return(sinit()); #else return(0); #endif /* NOSERVER */ } /* S N D W H O -- send who message */ int sndwho(who) char * who; { #ifndef NOSERVER nfils = 0; /* No files, no lists. */ xflg = 1; /* Flag we must send X packet. */ ckstrncpy(cmdstr,"who",CMDSTRL); /* Data for X packet. */ first = 1; /* Init getchx lookahead */ memstr = 1; /* Just set the flag. */ #ifdef NT memptr = "\15\12K95 SERVER\15\12"; /* And the pointer. */ #else memptr = "\15\12K/2 SERVER\15\12"; #endif /* NT */ binary = XYFT_T; /* Use text mode */ return(sinit()); #else return(0); #endif /* NOSERVER */ } #endif /* OS2 */ /* C W D -- Change server's working directory */ /* String passed has first byte as length of directory name, rest of string is name. Returns: 0 on failure. 1 on success after sending short-form response (ACK with name). 2 on success if a CD Message file is to be sent. */ int cwd(vdir) char *vdir; { char *cdd, *dirp; vdir[xunchar(*vdir) + 1] = '\0'; /* Terminate string with a null */ dirp = vdir+1; tlog(F110,"Directory requested: ",dirp,0L); if (zchdir(dirp)) { /* Try to change */ cdd = zgtdir(); /* Get new working directory. */ debug(F110,"cwd",cdd,0); if (srvcdmsg) { /* Send orientation file? */ int i; for (i = 0; i < 8; i++) { if (zchki(cdmsgfile[i]) > -1) { xxscreen(SCR_CD,0,0l,cdd); tlog(F110,"Changed directory to",cdd,0L); return(2); } } } encstr((CHAR *)cdd); /* Send short-form reply */ ack1(data); /* containing directory name. */ xxscreen(SCR_CD,0,0l,cdd); tlog(F110,"Changed directory to",cdd,0L); return(1); } else { debug(F110,"cwd failed",dirp,0); tlog(F110,"Failed to change directory to",dirp,0L); return(0); } } /* S Y S C M D -- Do a system command */ /* Command string is formed by concatenating the two arguments. */ int syscmd(prefix,suffix) char *prefix, *suffix; { extern int i_isopen; #ifndef NOPUSH char *cp; i_isopen = 0; if (!prefix) return(0); if (!*prefix) return(0); for (cp = cmdstr; *prefix != '\0'; (*cp++ = *prefix++)); while ((*cp++ = *suffix++)) #ifdef OS2 /* This takes away more than we gain in convenience if (*(cp-1) == '/') *(cp-1) = '\\' */ #endif /* OS2 */ ; /* Copy suffix */ debug(F110,"syscmd",cmdstr,0); if (zxcmd(ZIFILE,cmdstr) > 0) { debug(F110,"syscmd zxcmd ok",cmdstr,0); nfils = sndsrc = 0; /* Flag that input is from stdin */ xflg = hcflg = 1; /* And special flags for pipe */ binary = XYFT_T; /* Go to text mode */ i_isopen = 1; return (sinit()); /* Send S packet */ } else { debug(F100,"syscmd zxcmd failed",cmdstr,0); i_isopen = 0; return(0); } #else debug(F100,"syscmd zxcmd NOPUSH",cmdstr,0); i_isopen = 0; return(0); #endif /* NOPUSH */ } /* R E M S E T -- Remote Set */ /* Called by server to set variables as commanded in REMOTE SET packets. */ /* Returns 1 on success, 0 on failure. */ int remset(s) char *s; { extern int c_save, en_del; int len, i, x, y; char *p; len = xunchar(*s++); /* Length of first field */ p = s + len; /* Pointer to second length field */ *p++ = '\0'; /* Zero out second length field */ x = atoi(s); /* Value of first field */ debug(F111,"remset",s,x); debug(F110,"remset",p,0); switch (x) { /* Do the right thing */ case 132: /* Attributes (all, in) */ atcapr = atoi(p); return(1); case 133: /* File length attributes */ case 233: /* IN/OUT combined */ case 148: /* Both kinds of lengths */ case 248: atleni = atleno = atoi(p); return(1); case 134: /* File Type (text/binary) */ case 234: attypi = attypo = atoi(p); return(1); case 135: /* File creation date */ case 235: atdati = atdato = atoi(p); return(1); case 139: /* File Blocksize */ case 239: atblki = atblko = atoi(p); return(1); case 141: /* Encoding / Character Set */ case 241: atenci = atenco = atoi(p); return(1); case 142: /* Disposition */ case 242: atdisi = atdiso = atoi(p); return(1); case 145: /* System ID */ case 245: atsidi = atsido = atoi(p); return(1); case 147: /* System-Dependent Info */ case 247: atsysi = atsyso = atoi(p); return(1); case 232: /* Attributes (all, out) */ atcapr = atoi(p); return(1); case 300: /* File type (text, binary) */ binary = atoi(p); b_save = binary; #ifndef NOICP g_binary = -1; #endif /* NOICP */ return(1); case 301: /* File name conversion */ fncnv = 1 - atoi(p); /* (oops) */ f_save = fncnv; #ifndef NOICP g_fncnv = -1; #endif /* NOICP */ return(1); case 302: /* File name collision */ #ifdef IKSD #ifdef CK_LOGIN if (inserver && isguest) /* May not be changed by guest */ return(0); #endif /* CK_LOGIN */ #endif /* IKSD */ x = atoi(p); if (!ENABLED(en_del) && (x == XYFX_X || x == XYFX_U)) return(0); if (x == XYFX_R) ckwarn = 1; /* Rename */ if (x == XYFX_X) ckwarn = 0; /* Replace */ fncact = x; return(1); case 310: /* Incomplete File Disposition */ keep = atoi(p); /* Keep, Discard, Auto */ return(1); case 311: /* Blocksize */ fblksiz = atoi(p); return(1); case 312: /* Record Length */ frecl = atoi(p); return(1); case 313: /* Record format */ frecfm = atoi(p); return(1); case 314: /* File organization */ forg = atoi(p); return(1); case 315: /* File carriage control */ fcctrl = atoi(p); return(1); case 330: /* Match dotfiles */ #ifndef NOICP dir_dots = -1; /* This undoes DIR /DOT option */ #endif /* NOICP */ matchdot = atoi(p); return(1); case 331: /* Match FIFOs */ matchfifo = atoi(p); return(1); case 400: /* Block check */ y = atoi(p); if (y < 5 && y > 0) { bctr = y; c_save = -1; return(1); } else if (*p == 'B') { bctr = 4; c_save = -1; return(1); } else if (*p == '5') { bctr = 3; c_save = -1; return(1); } return(0); case 401: /* Receive packet-length */ rpsiz = urpsiz = atoi(p); if (urpsiz > MAXRP) urpsiz = MAXRP; /* Max long-packet length */ if (rpsiz > 94) rpsiz = 94; /* Max short-packet length */ urpsiz = adjpkl(urpsiz,wslots,bigrbsiz); return(1); case 402: /* Receive timeout */ y = atoi(p); /* Client is telling us */ if (y > -1 && y < 999) { /* the timeout that it wants */ pkttim = chktimo(y,timef); /* us to tell it to use. */ return(1); } else return(0); case 403: /* Retry limit */ y = atoi(p); if (y > -1 && y < 95) { maxtry = y; return(1); } else return(0); case 404: /* Server timeout */ y = atoi(p); if (y < 0) return(0); srvtim = y; return(1); #ifndef NOCSETS case 405: { /* Transfer character set */ extern int s_cset, axcset[]; int i; for (i = 0; i < ntcsets; i++) { if (!strcmp(tcsinfo[i].designator,p)) break; } debug(F101,"remset tcharset lookup","",i); if (i == ntcsets) return(0); tcharset = tcsinfo[i].code; /* If known, use it */ debug(F101,"remset tcharset","",tcharset); if (s_cset == XMODE_A) if (axcset[tcharset] > -1 && axcset[tcharset] > MAXFCSETS) fcharset = axcset[tcharset]; /* Auto-pick file charset */ debug(F101,"remset tcharset fcharset","",fcharset); setxlatype(tcharset,fcharset); /* Set up charset translations */ debug(F101,"remset xlatype","",xlatype); debug(F101,"remset tcharset after setxlatype","",tcharset); tcs_save = -1; return(1); } case 320: { /* File character set */ extern struct keytab fcstab[]; extern int nfilc, s_cset, r_cset; x = lookup(fcstab,p,nfilc,&y); debug(F111,"RSET FILE CHAR name",p,x); if (x < 0) return(0); s_cset = XMODE_M; /* No automatic charset switching */ r_cset = XMODE_M; fcharset = x; /* Set file charset */ setxlatype(tcharset,fcharset); /* and translation type */ fcs_save = -1; return(1); } #endif /* NOCSETS */ case 406: /* Window slots */ y = atoi(p); if (y == 0) y = 1; if (y < 1 || y > MAXWS) return(0); wslotr = y; swcapr = 1; urpsiz = adjpkl(urpsiz,wslotr,bigrbsiz); return(1); case 410: /* Transfer mode */ y = atoi(p); /* 0 = automatic, nonzero = manual */ if (y != 0) y = 1; xfermode = y; debug(F101,"REMOTE SET xfermode","",xfermode); return(1); case 420: /* SERVER CD-MESSAGE { ON, OFF } */ y = atoi(p); /* 0 = automatic, nonzero = manual */ srvcdmsg = y; return(1); default: /* Anything else... */ return(0); } } /* Adjust packet length based on number of window slots and buffer size */ int adjpkl(pktlen,slots,bufsiz) int pktlen, slots, bufsiz; { if (protocol != PROTO_K) return(pktlen); debug(F101,"adjpkl len","",pktlen); debug(F101,"adjpkl slots","",slots); debug(F101,"adjpkl bufsiz","",bufsiz); if (((pktlen + 6) * slots) > bufsiz) pktlen = (bufsiz / slots) - 6; debug(F101,"adjpkl new len","",pktlen); return(pktlen); } /* Set transfer mode and file naming based on comparison of system types */ VOID whoarewe() { #ifndef NOICP extern int g_xfermode; #endif /* NOICP */ wearealike = 0; debug(F101,"whoarewe xfermode","",xfermode); #ifndef NOICP debug(F101,"whoarewe g_xfermode","",g_xfermode); #endif /* NOICP */ if (whoareu[0]) { /* If we know partner's system type */ char * p = (char *)whoareu; debug(F110,"whoarewe remote sysid",whoareu,0); if (!strcmp(p,cksysid)) /* Other system same as us */ wearealike = 1; #ifdef UNIX else if (!strcmp(p,"L3")) /* UNIX is sort of like AmigaDOS */ wearealike = 1; /* (same directory separator) */ else if (!strcmp(p,"N3")) /* UNIX like Aegis */ wearealike = 1; #else #ifdef AMIGA /* Like UNIX, but case distinctions are ignored and can begin with device:. */ else if (!strcmp(p,"U1")) /* Amiga is sort of like UNIX */ wearealike = 1; else if (!strcmp(p,"N3")) /* Amiga is sort of like Aegis */ wearealike = 1; #else #ifdef OS2 /* (Includes Windows 95/NT) */ /* DOS, GEMDOS, Windows 3.x, Windows 95, Windows NT */ /* All "the same" for FAT partitions but all bets off otherwise */ /* so this part needs some refinement ... */ else if (!strcmp(p,"U8")) /* MS-DOS */ wearealike = 1; else if (!strcmp(p,"UO")) /* OS/2 */ wearealike = 1; else if (!strcmp(p,"UN")) /* Windows NT or 95 */ wearealike = 1; else if (!strcmp(p,"K2")) /* GEMDOS */ wearealike = 1; #else #ifdef GEMDOS else if (!strcmp(p,"U8")) wearealike = 1; else if (!strcmp(p,"UO")) wearealike = 1; else if (!strcmp(p,"UN")) wearealike = 1; else if (!strcmp(p,"K2")) wearealike = 1; #endif /* GEMDOS */ #endif /* OS2 */ #endif /* AMIGA */ #endif /* UNIX */ /* Get here with wearealike == 1 if system types match */ debug(F101,"whoarewe wearealike","",wearealike); if (!wearealike) /* Not alike */ return; fncnv = XYFN_L; /* Alike, so literal filenames */ debug(F101,"whoarewe setting fncnv","",fncnv); if (xfermode == XMODE_A) { /* Current xfer mode is auto */ #ifdef VMS binary = XYFT_L; /* For VMS-to-VMS, use labeled */ #else #ifdef OS2 /* OS/2 but not Windows */ if (!strcmp(cksysid,"UO") && !strcmp((char *)whoareu,"UO")) binary = XYFT_L; /* For OS/2-to-OS/2, use labeled */ #else binary = XYFT_B; /* For all others use binary */ #endif /* OS2 */ #endif /* VMS */ gnf_binary = binary; /* Prevailing type for gnfile() */ debug(F101,"whoarewe setting binary","",binary); } } } #endif /* NOXFER */ ckcftp.c0000644000015300001460000212443011607613232011306 0ustar fdckermit#define FTP_TIMEOUT /* C K C F T P -- FTP Client for C-Kermit */ char *ckftpv = "FTP Client, 9.0.260, 14 Jul 2011"; /* Authors: Jeffrey E Altman Secure Endpoints Inc., New York City Frank da Cruz , The Kermit Project, Columbia University. Copyright (C) 2000, 2011, Trustees of Columbia University in the City of New York. All rights reserved. See the C-Kermit COPYING.TXT file or the copyright text in the ckcmai.c module for disclaimer and permissions. Portions of conditionally included code Copyright Regents of the University of California and The Stanford SRP Authentication Project; see notices below. */ /* Pending... . Implement recursive NLST downloads by trying to CD to each filename. If it works, it's a directory; if not, it's a file -- GET it. But that won't work with servers like wu-ftpd that don't send directory names. Recursion with MLSD is done. . Make syslog entries for session? Files? . Messages are printed to stdout and stderr in random fashion. We should either print everything to stdout, or else be systematic about when to use stderr. . Implement mail (MAIL, MLFL, MSOM, etc) if any servers support it. . Adapt to VMS. Big job because of its record-oriented file system. RMS programmer required. There are probably also some VMS TCP/IP product-specific wrinkles, e.g. attribute preservation in VMS-to-VMS transfers using special options for Multinet or other FTP servers (find out about STRU VMS). */ /* Quick FTP command reference: RFC765 (1980) and earlier: MODE S(tream), B(lock), C(ompressed) STRU F(ILE), R(ECORD), P(AGE) TYPE A(SCII) , E(BCDIC) , I(MAGE), L(OCAL) PORT - Port PASV - Passive mode USER - User PASS - Password ACCT - Account CWD - Change Working Directory REIN - Logout but not disconnect QUIT - Bye RETR - Retreive STOR - Store APPE - Append ALLO - Allocate REST - Restart RNFR - Rename from RNTO - Rename to ABOR - Cancel DELE - Delete LIST - Directory NLST - Name List SITE - Site parameters or commands STAT - Status HELP - Help NOOP - Noop RFC959 (1985): CDUP - Change to Parent Directory SMNT - Structure Mount STOU - Store Unique RMD - Remove Directory MKD - Make Directory PWD - Print Directory SYST - System RFC2389 (1998): FEAT - List Features (done) OPTS - Send options (done) RFC2640 (1999): LANG - Specify language for messages (not done) Pending (Internet Drafts): SIZE - File size (done) MDTM - File modification date-time (done) MLST - File name and attribute list (single file) (not done) MLSD - File list with attributes (multiple files) (done) MAIL, MLFL, MSOM - mail delivery (not done) Alphabetical syntax list: ABOR ACCT ALLO [ R ] APPE CDUP CWD DELE FEAT HELP [ ] LANG [ ] LIST [ ] MKD MLSD [ ] MLST [ ] MODE NLST [ ] NOOP OPTS [ ] PASS PASV PORT PWD QUIT REIN REST RETR RMD RNFR RNTO SITE SIZE SMNT STAT [ ] STOR STOU STRU SYST TYPE USER */ #include "ckcsym.h" /* Standard includes */ #include "ckcdeb.h" #ifndef NOFTP /* NOFTP = no FTP */ #ifndef SYSFTP /* SYSFTP = use external ftp client */ #ifdef TCPSOCKET /* Build only if TCP/IP included */ #define CKCFTP_C /* Note: much of the following duplicates what was done in ckcdeb.h */ /* but let's not mess with it unless it causes trouble. */ #ifdef CK_ANSIC #include #else /* CK_ANSIC */ #include #endif /* CK_ANSIC */ #include #ifdef OS2 #ifdef OS2ONLY #include #endif /* OS2ONLY */ #include "ckowin.h" #include "ckocon.h" #endif /* OS2 */ #ifndef ZILOG #ifdef NT #include #ifdef NTSIG extern int TlsIndex; #endif /* NTSIG */ #else /* NT */ #include #endif /* NT */ #else #include #endif /* ZILOG */ #include "ckcsig.h" #ifdef VMS /* 2010-03-09 SMS. VAX C needs help to find "sys". It's easier not to try. */ #include #else /* def VMS */ #include #endif /* def VMS [else] */ #include #ifndef HPUXPRE65 #include /* Error number symbols */ #else #ifndef ERRNO_INCLUDED #include /* Error number symbols */ #endif /* ERRNO_INCLUDED */ #endif /* HPUXPRE65 */ #ifndef NOTIMEH #include #endif /* NOTIMEH */ #ifndef EPIPE #define EPIPE 32 /* Broken pipe error */ #endif /* EPIPE */ /* Kermit includes */ #include "ckcasc.h" #include "ckcker.h" #include "ckucmd.h" #include "ckuusr.h" #include "ckcnet.h" /* Includes ckctel.h */ #include "ckctel.h" /* (then why include it again?) */ #include "ckcxla.h" #ifdef CK_SSL #include "ckuath.h" /* SMS 2007/02/15 */ #endif /* def CK_SSL */ /* How to get the struct timeval definition so we can call select(). The xxTIMEH symbols are defined in ckcdeb.h, overridden in various makefile targets. The problem is: maybe we have already included some header file that defined struct timeval, and maybe we didn't. If we did, we don't want to include another header file that defines it again or the compilation will fail. If we didn't, we have to include the header file where it's defined. But in some cases even that won't work because of strict POSIX constraints or somesuch, or because this introduces other conflicts (e.g. struct tm multiply defined), in which case we have to define it ourselves, but this can work only if we didn't already encounter a definition. */ #ifndef DCLTIMEVAL #ifdef SV68R3V6 #define DCLTIMEVAL #else #ifdef SCO234 #define DCLTIMEVAL #endif /* SCO234 */ #endif /* SV68R3V6 */ #endif /* DCLTIMEVAL */ #ifdef DCLTIMEVAL /* Also maybe in some places the elements must be unsigned... */ struct timeval { long tv_sec; long tv_usec; }; #ifdef COMMENT /* Currently we don't use this... */ struct timezone { int tz_minuteswest; int tz_dsttime; }; #endif /* COMMENT */ #else /* !DCLTIMEVAL */ #ifndef NOSYSTIMEH #ifdef SYSTIMEH #include #endif /* SYSTIMEH */ #endif /* NOSYSTIMEH */ #ifndef NOSYSTIMEBH #ifdef SYSTIMEBH #include #endif /* SYSTIMEBH */ #endif /* NOSYSTIMEBH */ #endif /* DCLTIMEVAL */ /* 2010-03-09 SMS. VAX C needs help to find "sys". It's easier not to try. */ #ifdef VMS #include #else /* def VMS */ #include #endif /* def VMS [else] */ #include #include #ifdef HAVE_STDLIB_H #include #endif /* HAVE_STDLIB_H */ #ifndef NOSETTIME #ifdef COMMENT /* This section moved to ckcdeb.h */ #ifdef POSIX #define UTIMEH #else #ifdef HPUX9 #define UTIMEH #else #ifdef OS2 #define SYSUTIMEH #endif /* OS2 */ #endif /* HPUX9 */ #endif /* POSIX */ #endif /* COMMENT */ #ifdef VMS /* SMS 2007/02/15 */ #include "ckvrtl.h" /* for utime() */ #else /* def VMS */ #ifdef SYSUTIMEH #include #else #ifdef UTIMEH #include #define SYSUTIMEH #endif /* UTIMEH */ #endif /* SYSUTIMEH */ #endif /* def VMS */ #endif /* NOSETTIME */ #ifndef SCO_OSR504 #ifdef SELECT_H #include #endif /* SELECT_H */ #endif /* SCO_OSR504 */ #ifndef INADDR_NONE /* 2010-03-29 */ #define INADDR_NONE -1 #endif /* INADDR_NONE */ /* select() dialects... */ #ifdef UNIX #define BSDSELECT /* BSD select() syntax/semantics */ #ifndef FD_SETSIZE #define FD_SETSIZE 128 #endif /* FD_SETSIZE */ #ifdef HPUX6 /* For HP-UX 6.5 circa 1989 */ typedef long fd_mask; #define NFDBITS (sizeof(fd_mask) * NBBY) /* bits per mask */ #ifndef howmany #define howmany(x, y) (((x)+((y)-1))/(y)) #endif /* howmany */ #define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS))) #define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS))) #define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS))) #define FD_COPY(f, t) bcopy(f, t, sizeof(*(f))) #define FD_ZERO(p) bzero(p, sizeof(*(p))) #endif /* HPUX6 */ #else #ifdef OS2 /* OS/2 or Win32 */ #ifdef NT #define BSDSELECT #else /* NT */ #define IBMSELECT #endif /* NT */ #endif /* OS2 */ #endif /* UNIX */ #ifdef VMS #define BSDSELECT /* SMS 2007/02/15 */ #endif /* def VMS */ /* Other select() peculiarities */ #ifdef HPUX #ifndef HPUX10 /* HP-UX 9.xx and earlier */ #ifndef HPUX1100 /* The three interior args to select() are (int *) rather than (fd_set *) */ #ifndef INTSELECT #define INTSELECT #endif /* INTSELECT */ #endif /* HPUX1100 */ #endif /* HPUX10 */ #endif /* HPUX */ #ifdef CK_SOCKS /* SOCKS Internet relay package */ #ifdef CK_SOCKS5 /* SOCKS 5 */ #define accept SOCKSaccept #define bind SOCKSbind #define connect SOCKSconnect #define getsockname SOCKSgetsockname #define listen SOCKSlisten #else /* Not SOCKS 5 */ #define accept Raccept #define bind Rbind #define connect Rconnect #define getsockname Rgetsockname #define listen Rlisten #endif /* CK_SOCKS5 */ #endif /* CK_SOCKS */ #ifndef NOHTTP extern char * tcp_http_proxy; /* Name[:port] of http proxy server */ extern int tcp_http_proxy_errno; extern char * tcp_http_proxy_user; extern char * tcp_http_proxy_pwd; extern char * tcp_http_proxy_agent; #define HTTPCPYL 1024 static char proxyhost[HTTPCPYL]; #endif /* NOHTTP */ int ssl_ftp_proxy = 0; /* FTP over SSL/TLS Proxy Server */ /* Feature selection */ #ifndef USE_SHUTDOWN /* We don't use shutdown() because (a) we always call it just before close() so it's redundant and unnecessary, and (b) it introduces a long pause on some platforms like SV/68 R3. */ /* #define USE_SHUTDOWN */ #endif /* USE_SHUTDOWN */ #ifndef NORESEND #ifndef NORESTART /* Restart / recover */ #ifndef FTP_RESTART #define FTP_RESTART #endif /* FTP_RESTART */ #endif /* NORESTART */ #endif /* NORESEND */ #ifndef NOUPDATE /* Update mode */ #ifndef DOUPDATE #define DOUPDATE #endif /* DOUPDATE */ #endif /* NOUPDATE */ #ifndef UNICODE /* Unicode required */ #ifndef NOCSETS /* for charset translation */ #define NOCSETS #endif /* NOCSETS */ #endif /* UNICODE */ #ifndef OS2 #ifndef HAVE_MSECS /* Millisecond timer */ #ifdef UNIX #ifdef GFTIMER #define HAVE_MSECS #endif /* GFTIMER */ #endif /* UNIX */ #endif /* HAVE_MSECS */ #endif /* OS2 */ #ifdef PIPESEND /* PUT from pipe */ #ifndef PUTPIPE #define PUTPIPE #endif /* PUTPIPE */ #endif /* PIPESEND */ #ifndef NOSPL /* PUT from array */ #ifndef PUTARRAY #define PUTARRAY #endif /* PUTARRAY */ #endif /* NOSPL */ /* Security... */ #ifdef CK_SRP #define FTP_SRP #endif /* CK_SRP */ #ifdef CK_KERBEROS #ifdef KRB4 /* There is a conflict between the Key Schedule formats used internally within the standalone MIT KRB4 library and that used by Eric Young in OpenSSL and his standalone DES library. Therefore, KRB4 FTP AUTH cannot be supported when either of those two packages are used. */ #ifdef KRB524 #define FTP_KRB4 #else /* KRB524 */ #ifndef CK_SSL #ifndef LIBDES #define FTP_KRB4 #endif /* LIBDES */ #endif /* CK_SSL */ #endif /* KRB524 */ #endif /* KRB4 */ #ifdef KRB5 #ifndef HEIMDAL #ifndef NOFTP_GSSAPI /* 299 */ #define FTP_GSSAPI #endif /* NOFTP_GSSAPI */ #endif /* HEIMDAL */ #endif /* KRB5 */ #endif /* CK_KERBEROS */ /* FTP_SECURITY is defined if any of the above is selected */ #ifndef FTP_SECURITY #ifdef FTP_GSSAPI #define FTP_SECURITY #else #ifdef FTP_KRB4 #define FTP_SECURITY #else #ifdef FTP_SRP #define FTP_SECURITY #else #ifdef CK_SSL #define FTP_SECURITY #endif /* CK_SSL */ #endif /* FTP_SRP */ #endif /* FTP_KRB4 */ #endif /* FTP_GSSAPI */ #endif /* FTP_SECURITY */ #ifdef CK_DES #ifdef CK_SSL #ifndef LIBDES #define LIBDES #endif /* LIBDES */ #endif /* CK_SSL */ #endif /* CK_DES */ #ifdef CRYPT_DLL #ifndef LIBDES #define LIBDES #endif /* LIBDES */ #endif /* CRYPT_DLL */ #ifdef FTP_KRB4 #define des_cblock Block #define des_key_schedule Schedule #ifdef KRB524 #ifdef NT #define _WINDOWS #endif /* NT */ #include "kerberosIV/krb.h" #else /* KRB524 */ #ifdef SOLARIS #ifndef sun /* For some reason lost in history the Makefile Solaris targets have -Usun */ #define sun #endif /* sun */ #endif /* SOLARIS */ #include "krb.h" #define krb_get_err_text_entry krb_get_err_text #endif /* KRB524 */ #endif /* FTP_KRB4 */ #ifdef CK_SSL #ifdef FTP_KRB4 #ifndef HEADER_DES_H #define HEADER_DES_H #endif /* HEADER_DES_H */ #endif /* FTP_KRB4 */ #include "ck_ssl.h" #endif /* CK_SSL */ #ifdef FTP_SRP #ifdef HAVE_PWD_H #include "pwd.h" #endif /* HAVE_PWD_H */ #include "t_pwd.h" #include "t_client.h" #include "krypto.h" #endif /* FTP_SRP */ #ifdef FTP_GSSAPI #include /* Need to include the krb5 file, because we're doing manual fallback from the v2 mech to the v1 mech. Once there's real negotiation, we can be generic again. */ #include #include static gss_ctx_id_t gcontext; #ifdef MACOSX /** exported constants defined in gssapi_krb5{,_nx}.h **/ /* these are bogus, but will compile */ /* * The OID of the draft krb5 mechanism, assigned by IETF, is: * iso(1) org(3) dod(5) internet(1) security(5) * kerberosv5(2) = 1.3.5.1.5.2 * The OID of the krb5_name type is: * iso(1) member-body(2) US(840) mit(113554) infosys(1) gssapi(2) * krb5(2) krb5_name(1) = 1.2.840.113554.1.2.2.1 * The OID of the krb5_principal type is: * iso(1) member-body(2) US(840) mit(113554) infosys(1) gssapi(2) * krb5(2) krb5_principal(2) = 1.2.840.113554.1.2.2.2 * The OID of the proposed standard krb5 mechanism is: * iso(1) member-body(2) US(840) mit(113554) infosys(1) gssapi(2) * krb5(2) = 1.2.840.113554.1.2.2 * The OID of the proposed standard krb5 v2 mechanism is: * iso(1) member-body(2) US(840) mit(113554) infosys(1) gssapi(2) * krb5v2(3) = 1.2.840.113554.1.2.3 * */ /* * Encoding rules: The first two values are encoded in one byte as 40 * * value1 + value2. Subsequent values are encoded base 128, most * significant digit first, with the high bit (\200) set on all octets * except the last in each value's encoding. */ static CONST gss_OID_desc ck_krb5_gss_oid_array[] = { /* this is the official, rfc-specified OID */ {9, "\052\206\110\206\367\022\001\002\002"}, /* this is the unofficial, wrong OID */ {5, "\053\005\001\005\002"}, /* this is the v2 assigned OID */ {9, "\052\206\110\206\367\022\001\002\003"}, /* these two are name type OID's */ {10, "\052\206\110\206\367\022\001\002\002\001"}, {10, "\052\206\110\206\367\022\001\002\002\002"}, { 0, 0 } }; static CONST gss_OID_desc * CONST gss_mech_krb5_v2 = ck_krb5_gss_oid_array+2; #ifdef MACOSX103 static CONST gss_OID_desc * CONST gss_mech_krb5 = ck_krb5_gss_oid_array+0; #endif /* MACOSX103 */ #ifndef MACOSX static CONST gss_OID_desc * CONST gss_mech_krb5 = ck_krb5_gss_oid_array+0; static CONST gss_OID_desc * CONST gss_mech_krb5_old = ck_krb5_gss_oid_array+1; static CONST gss_OID_desc * CONST gss_nt_krb5_name = ck_krb5_gss_oid_array+3; static CONST gss_OID_desc * CONST gss_nt_krb5_principal = ck_krb5_gss_oid_array+4; #endif /* MACOSX */ /* * See krb5/gssapi_krb5.c for a description of the algorithm for * encoding an object identifier. */ /* * The OID of user_name is: * iso(1) member-body(2) US(840) mit(113554) infosys(1) gssapi(2) * generic(1) user_name(1) = 1.2.840.113554.1.2.1.1 * machine_uid_name: * iso(1) member-body(2) US(840) mit(113554) infosys(1) gssapi(2) * generic(1) machine_uid_name(2) = 1.2.840.113554.1.2.1.2 * string_uid_name: * iso(1) member-body(2) US(840) mit(113554) infosys(1) gssapi(2) * generic(1) string_uid_name(3) = 1.2.840.113554.1.2.1.3 * service_name: * iso(1) member-body(2) US(840) mit(113554) infosys(1) gssapi(2) * generic(1) service_name(4) = 1.2.840.113554.1.2.1.4 * exported_name: * 1(iso), 3(org), 6(dod), 1(internet), 5(security), 6(nametypes), * 4(gss-api-exported-name) * host_based_service_name (v2): * iso (1) org (3), dod (6), internet (1), security (5), nametypes(6), * gss-host-based-services(2) */ static gss_OID_desc ck_oids[] = { {10, "\052\206\110\206\367\022\001\002\001\001"}, {10, "\052\206\110\206\367\022\001\002\001\002"}, {10, "\052\206\110\206\367\022\001\002\001\003"}, {10, "\052\206\110\206\367\022\001\002\001\004"}, { 6, "\053\006\001\005\006\004"}, { 6, "\053\006\001\005\006\002"}, }; static gss_OID ck_gss_nt_user_name = ck_oids+0; static gss_OID ck_gss_nt_machine_uid_name = ck_oids+1; static gss_OID ck_gss_nt_string_uid_name = ck_oids+2; static gss_OID ck_gss_nt_service_name = ck_oids+3; static gss_OID ck_gss_nt_exported_name = ck_oids+4; static gss_OID ck_gss_nt_service_name_v2 = ck_oids+5; #endif /* MACOSX */ #endif /* FTP_GSSAPI */ #ifdef OS2 #ifdef FTP_SRP #define MAP_KRYPTO #ifdef SRPDLL #define MAP_SRP #endif /* SRPDLL */ #endif /* FTP_SRP */ #ifdef FTP_KRB4 #define MAP_KRB4 #ifdef CK_ENCRYPTION #define MAP_DES #endif /* CK_ENCRYPTION */ #endif /* FTP_KRB4 */ #ifdef FTP_GSSAPI #define MAP_GSSAPI #define GSS_OIDS #endif /* FTP_GSSAPI */ #include "ckoath.h" extern int k95stdout, wherex[], wherey[]; extern unsigned char colorcmd; #endif /* OS2 */ #ifdef FTP_KRB4 static char ftp_realm[REALM_SZ + 1]; static KTEXT_ST ftp_tkt; #ifdef OS2 static LEASH_CREDENTIALS ftp_cred; #else /* OS2 */ static CREDENTIALS ftp_cred; #endif /* OS2 */ static MSG_DAT ftp_msg_data; static des_key_schedule ftp_sched; static int foo[4] = {99,99,99,99}; #endif /* FTP_KRB4 */ /* getreply() function codes */ #define GRF_AUTH 1 /* Reply to AUTH command */ #define GRF_FEAT 2 /* Reply to FEAT command */ /* Operational definitions */ #define DEF_VBM 0 /* Default verbose mode */ /* #define SETVBM */ /* (see getreply) */ #define URL_ONEFILE /* GET, not MGET, for FTP URL */ #define FTP_BUFSIZ 10240 /* Max size for FTP cmds & replies */ #define SRVNAMLEN 32 /* Max length for server type name */ #define PWDSIZ 256 #define PASSBUFSIZ 256 #define PROMPTSIZ 256 #ifndef MGETMAX /* Max operands for MGET command */ #define MGETMAX 1000 #endif /* MGETMAX */ #ifdef FTP_SRP #define FUDGE_FACTOR 100 #endif /* FTP_SRP */ /* Amount of growth from cleartext to ciphertext. krb_mk_priv adds this number bytes. Must be defined for each auth type. GSSAPI appears to add 52 bytes, but I'm not sure it is a constant--hartmans 3DES requires 56 bytes. Lets use 96 just to be sure. */ #ifdef FTP_GSSAPI #ifndef FUDGE_FACTOR #define FUDGE_FACTOR 96 #endif /* FUDGE_FACTOR */ #endif /* FTP_GSSAPI */ #ifdef FTP_KRB4 #ifndef FUDGE_FACTOR #define FUDGE_FACTOR 32 #endif /* FUDGE_FACTOR */ #endif /* FTP_KRB4 */ #ifndef FUDGE_FACTOR /* In case no auth types define it */ #define FUDGE_FACTOR 0 #endif /* FUDGE_FACTOR */ #ifndef MAXHOSTNAMELEN #define MAXHOSTNAMELEN 64 #endif /* MAXHOSTNAMELEN */ #define MAX_DNS_NAMELEN (15*(MAXHOSTNAMELEN + 1)+1) /* Fascist compiler toadying */ #ifndef SENDARG2TYPE #ifdef COMMENT /* Might be needed here and there */ #define SENDARG2TYPE const char * #else #define SENDARG2TYPE char * #endif /* COMMENT */ #endif /* SENDARG2TYPE */ /* Common text messages */ static char *nocx = "?No FTP control connection\n"; static char *fncnam[] = { "rename", "overwrite", "backup", "append", "discard", "ask", "update", "dates-differ", "" }; /* Macro definitions */ /* Used to speed up text-mode PUTs */ #define zzout(fd,c) \ ((fd<0)?(-1):((nout>=ucbufsiz)?(zzsend(fd,c)):(ucbuf[nout++]=c))) #define CHECKCONN() if(!connected){printf(nocx);return(-9);} /* Externals */ #ifdef CK_URL extern struct urldata g_url; #endif /* CK_URL */ #ifdef DYNAMIC extern char *zinbuffer, *zoutbuffer; /* Regular Kermit file i/o */ #else extern char zinbuffer[], zoutbuffer[]; #endif /* DYNAMIC */ extern char *zinptr, *zoutptr; extern int zincnt, zoutcnt, zobufsize, fncact; #ifdef CK_TMPDIR extern int f_tmpdir; /* Directory changed temporarily */ extern char savdir[]; /* For saving current directory */ extern char * dldir; #endif /* CK_TMPDIR */ extern char * rfspec, * sfspec, * srfspec, * rrfspec; /* For WHERE command */ extern xx_strp xxstring; extern struct keytab onoff[], txtbin[], rpathtab[]; extern int nrpathtab, xfiletype, patterns, gnferror, moving, what, pktnum; extern int success, nfils, sndsrc, quiet, nopush, recursive, inserver, binary; extern int filepeek, nscanfile, fsecs, xferstat, xfermode, lastxfer, tsecs; extern int backgrd, spackets, rpackets, spktl, rpktl, xaskmore, cmd_rows; extern int nolinks, msgflg, keep; extern CK_OFF_T fsize, ffc, tfc, sendstart, sndsmaller, sndlarger, rs_len; extern long filcnt, xfsecs, tfcps, cps, oldcps; #ifdef FTP_TIMEOUT int ftp_timed_out = 0; long ftp_timeout = 0; #endif /* FTP_TIMEOUT */ #ifdef GFTIMER extern CKFLOAT fptsecs, fpfsecs, fpxfsecs; #else extern long xfsecs; #endif /* GFTIMER */ extern char filnam[], * filefile, myhost[]; extern char * snd_move, * rcv_move, * snd_rename, * rcv_rename; extern int g_skipbup, skipbup, sendmode; extern int g_displa, fdispla, displa; #ifdef LOCUS extern int locus, autolocus; #endif /* LOCUS */ #ifndef NOCSETS extern int nfilc, dcset7, dcset8, fileorder; extern struct csinfo fcsinfo[]; extern struct keytab fcstab[]; extern int fcharset; #endif /* NOCSETS */ extern char sndbefore[], sndafter[], *sndexcept[]; /* Selection criteria */ extern char sndnbefore[], sndnafter[], *rcvexcept[]; extern CHAR feol; extern char * remdest; extern int remfile, remappd, rempipe; #ifndef NOSPL extern int cmd_quoting; #ifdef PUTARRAY extern int sndxlo, sndxhi, sndxin; extern char sndxnam[]; extern char **a_ptr[]; /* Array pointers */ extern int a_dim[]; /* Array dimensions */ #endif /* PUTARRAY */ #endif /* NOSPL */ #ifndef NOMSEND /* MPUT and ADD SEND-LIST lists */ extern char *msfiles[]; extern int filesinlist; extern struct filelist * filehead; extern struct filelist * filetail; extern struct filelist * filenext; extern int addlist; extern char fspec[]; /* Most recent filespec */ extern int fspeclen; /* Length of fspec[] buffer */ #endif /* NOMSEND */ extern int pipesend; #ifdef PIPESEND extern char * sndfilter, * rcvfilter; #endif /* PIPESEND */ #ifdef CKROOT extern int ckrooterr; #endif /* CKROOT */ #ifdef KRB4 extern int krb4_autoget; _PROTOTYP(char * ck_krb4_realmofhost,(char *)); #endif /* KRB4 */ #ifdef KRB5 extern int krb5_autoget; extern int krb5_d_no_addresses; _PROTOTYP(char * ck_krb5_realmofhost,(char *)); #endif /* KRB5 */ #ifdef DCMDBUF extern char *atmbuf; /* Atom buffer (malloc'd) */ extern char *cmdbuf; /* Command buffer (malloc'd) */ extern char *line; /* Big string buffer #1 */ extern char *tmpbuf; /* Big string buffer #2 */ #else extern char atmbuf[]; /* The same, but static */ extern char cmdbuf[]; extern char line[]; extern char tmpbuf[]; #endif /* DCMDBUF */ extern char * cmarg, * cmarg2, ** cmlist; /* For setting up file lists */ /* Public variables declared here */ #ifdef NOXFER int ftpget = 1; /* GET/PUT/REMOTE orientation FTP */ #else int ftpget = 2; /* GET/PUT/REMOTE orientation AUTO */ #endif /* NOXFER */ int ftpcode = -1; /* Last FTP response code */ int ftp_cmdlin = 0; /* FTP invoked from command line */ int ftp_fai = 0; /* FTP failure count */ int ftp_deb = 0; /* FTP debugging */ int ftp_dis = -1; /* FTP display style */ int ftp_log = 1; /* FTP Auto-login */ int sav_log = -1; int ftp_action = 0; /* FTP action from command line */ int ftp_dates = 1; /* Set file dates from server */ int ftp_xfermode = XMODE_A; /* FTP-specific transfer mode */ char ftp_reply_str[FTP_BUFSIZ] = ""; /* Last line of previous reply */ char ftp_srvtyp[SRVNAMLEN] = { NUL, NUL }; /* Server's system type */ char ftp_user_host[MAX_DNS_NAMELEN]= ""; /* FTP hostname specified by user */ char * ftp_host = NULL; /* FTP hostname */ char * ftp_logname = NULL; /* FTP username */ char * ftp_rdir = NULL; /* Remote directory from cmdline */ char * ftp_apw = NULL; /* Anonymous password */ /* Definitions and typedefs needed for prototypes */ #define sig_t my_sig_t #define sigtype SIGTYP typedef sigtype (*sig_t)(); /* Static global variables */ static char ftpsndbuf[FTP_BUFSIZ+64]; static char * fts_sto = NULL; static int ftpsndret = 0; static struct _ftpsnd { sig_t oldintr, oldintp; int reply; int incs, outcs; char * cmd, * local, * remote; int bytes; int restart; int xlate; char * lmode; } ftpsnd; /* This is just a first stab -- these strings should match how the corresponding FTP servers identify themselves. */ #ifdef UNIX static char * myostype = "UNIX"; #else #ifdef VMS /* not yet... */ static char * myostype = "VMS"; #else #ifdef OS2 #ifdef NT static char * myostype = "WIN32"; #else static char * myostype = "OS/2"; #endif /* NT */ #else static char * myostype = "UNSUPPORTED"; #endif /* OS2 */ #endif /* VMS */ #endif /* UNIX */ static int noinit = 0; /* Don't send REST, STRU, MODE */ static int alike = 0; /* Client/server like platforms */ static int local = 1; /* Shadows Kermit global 'local' */ static int dout = -1; /* Data connection file descriptor */ static int dpyactive = 0; /* Data transfer is active */ static int globaldin = -1; /* Data connection f.d. */ static int out2screen = 0; /* GET output is to screen */ static int forcetype = 0; /* Force text or binary mode */ static int cancelfile = 0; /* File canceled */ static int cancelgroup = 0; /* Group canceled */ static int anonymous = 0; /* Logging in as anonymous */ static int loggedin = 0; /* Logged in (or not) */ static int puterror = 0; /* What to do on PUT error */ static int geterror = 0; /* What to do on GET error */ static int rfrc = 0; /* remote_files() return code */ static int okrestart = 0; /* Server understands REST */ static int printlines = 0; /* getreply()should print data lines */ static int haveurl = 0; /* Invoked by command-line FTP URL */ static int mdtmok = 1; /* Server supports MDTM */ static int sizeok = 1; static int featok = 1; static int mlstok = 1; static int stouarg = 1; static int typesent = 0; static int havesigint = 0; static long havetype = 0; static CK_OFF_T havesize = (CK_OFF_T)-1; static char * havemdtm = NULL; static int mgetmethod = 0; /* NLST or MLSD */ static int mgetforced = 0; static int i, /* j, k, */ x, y, z; /* Volatile temporaries */ static int c0, c1; /* Temp variables for characters */ static char putpath[CKMAXPATH+1] = { NUL, NUL }; static char asnambuf[CKMAXPATH+1] = { NUL, NUL }; #define RFNBUFSIZ 4096 /* Remote filename buffer size */ static unsigned int maxbuf = 0, actualbuf = 0; static CHAR *ucbuf = NULL; static int ucbufsiz = 0; static unsigned int nout = 0; /* Number of chars in ucbuf */ static jmp_buf recvcancel; static jmp_buf sendcancel; static jmp_buf ptcancel; static jmp_buf jcancel; static int ptabflg = 0; /* Protection level symbols */ #define FPL_CLR 1 /* Clear */ #define FPL_SAF 2 /* Safe */ #define FPL_PRV 3 /* Private */ #define FPL_CON 4 /* Confidential */ /* Symbols for file types returned by MLST/MLSD */ #define FTYP_FILE 1 /* Regular file */ #define FTYP_DIR 2 /* Directory */ #define FTYP_CDIR 3 /* Current directory */ #define FTYP_PDIR 4 /* Parent directory */ /* File type symbols keyed to the file-type symbols from ckcker.h */ #define FTT_ASC XYFT_T /* ASCII (text) */ #define FTT_BIN XYFT_B /* Binary (image) */ #define FTT_TEN XYFT_X /* TENEX (TOPS-20) */ /* Server feature table - sfttab[0] > 0 means server supports FEAT and OPTS */ static int sfttab[16] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; #define SFT_AUTH 1 /* FTP server feature codes */ #define SFT_LANG 2 #define SFT_MDTM 3 #define SFT_MLST 4 #define SFT_PBSZ 5 #define SFT_PROT 6 #define SFT_REST 7 #define SFT_SIZE 8 #define SFT_TVFS 9 #define SFT_UTF8 10 #define CNV_AUTO 2 /* FTP filename conversion */ #define CNV_CNV 1 #define CNV_LIT 0 /* SET FTP values */ static int /* SET FTP values... */ ftp_aut = 1, /* Auto-authentication */ #ifdef FTP_SECURITY ftp_cry = 1, /* Auto-encryption */ ftp_cfw = 0, /* Credential forwarding */ #endif /* FTP_SECURITY */ ftp_cpl = FPL_CLR, /* Command protection level */ ftp_dpl = FPL_CLR, /* Data protection level */ #ifdef FTP_PROXY ftp_prx = 0, /* Use proxy */ #endif /* FTP_PROXY */ sav_psv = -1, /* For saving passive mode */ ftp_psv = 1, /* Passive mode */ ftp_spc = 1, /* Send port commands */ ftp_typ = FTT_ASC, /* Type */ get_auto = 1, /* Automatic type switching for GET */ tenex = 0, /* Type is Tenex */ ftp_usn = 0, /* Unique server names */ ftp_prm = 0, /* Permissions */ ftp_cnv = CNV_AUTO, /* Filename conversion (2 = auto) */ ftp_vbm = DEF_VBM, /* Verbose mode */ ftp_vbx = DEF_VBM, /* Sticky version of same */ ftp_err = 0, /* Error action */ ftp_fnc = -1; /* Filename collision action */ #ifdef CK_SSL static int ftp_bug_use_ssl_v2 = 0; /* use SSLv2 for AUTH SSL */ #endif /* CK_SSL */ static int #ifdef NOCSETS ftp_csr = -1, /* Remote (server) character set */ #else ftp_csr = FC_UTF8, #endif /* NOCSETS */ ftp_xla = 0; /* Character-set translation on/off */ int ftp_csx = -1, /* Remote charset currently in use */ ftp_csl = -1; /* Local charset currently in use */ static int g_ftp_typ = FTT_ASC; /* For saving and restoring ftp_typ */ char * ftp_nml = NULL; /* /NAMELIST */ char * ftp_tmp = NULL; /* Temporary string */ static char * ftp_acc = NULL; /* Account string */ static char * auth_type = NULL; /* Authentication type */ static char * srv_renam = NULL; /* Server-rename string */ FILE * fp_nml = NULL; /* Namelist file pointer */ static int csocket = -1; /* Control socket */ static int connected = 0; /* Connected to FTP server */ /* static unsigned short ftp_port = 0; */ /* FTP port */ /* static int ftp_port = 0; */ /* SMS 2007/02/15 */ static int ftp_port = 0; /* fdc 2007/08/30 */ #ifdef FTPHOST static int hostcmd = 0; /* Has HOST command been sent */ #endif /* FTPHOST */ static int form, mode, stru, bytesize, curtype = FTT_ASC; static char bytename[8]; /* For parsing replies to FTP server command */ static char *reply_parse, reply_buf[FTP_BUFSIZ], *reply_ptr; #ifdef FTP_PROXY static int proxy, unix_proxy #endif /* FTP_PROXY */ static char pasv[64]; /* Passive-mode port */ static int passivemode = 0; static int sendport = 0; static int servertype = 0; /* FTP server's OS type */ static int testing = 0; static char ftpcmdbuf[FTP_BUFSIZ]; /* Macro definitions */ #define UC(b) ckitoa(((int)b)&0xff) #define nz(x) ((x) == 0 ? 1 : (x)) /* Command tables and definitions */ #define FTP_ACC 1 /* FTP command keyword codes */ #define FTP_APP 2 #define FTP_CWD 3 #define FTP_CHM 4 #define FTP_CLS 5 #define FTP_DEL 6 #define FTP_DIR 7 #define FTP_GET 8 #define FTP_IDL 9 #define FTP_MDE 10 #define FTP_MDI 11 #define FTP_MGE 12 #define FTP_MKD 13 #define FTP_MOD 14 #define FTP_MPU 15 #define FTP_OPN 16 #define FTP_PUT 17 #define FTP_PWD 18 #define FTP_RGE 19 #define FTP_REN 20 #define FTP_RES 21 #define FTP_HLP 22 #define FTP_RMD 23 #define FTP_STA 24 #define FTP_SIT 25 #define FTP_SIZ 26 #define FTP_SYS 27 #define FTP_UMA 28 #define FTP_GUP 29 #define FTP_USR 30 #define FTP_QUO 31 #define FTP_TYP 32 #define FTP_FEA 33 #define FTP_OPT 34 #define FTP_CHK 35 #define FTP_VDI 36 #define FTP_ENA 37 #define FTP_DIS 38 #define FTP_REP 39 struct keytab gprtab[] = { /* GET-PUT-REMOTE keywords */ { "auto", 2, 0 }, { "ftp", 1, 0 }, { "kermit", 0, 0 } }; static struct keytab qorp[] = { /* QUIT or PROCEED keywords */ { "proceed", 0, 0 }, /* 0 = proceed */ { "quit", 1, 0 } /* 1 = quit */ }; static struct keytab ftpcmdtab[] = { /* FTP command table */ { "account", FTP_ACC, 0 }, { "append", FTP_APP, 0 }, { "bye", FTP_CLS, 0 }, { "cd", FTP_CWD, 0 }, { "cdup", FTP_GUP, 0 }, { "check", FTP_CHK, 0 }, { "chmod", FTP_CHM, 0 }, { "close", FTP_CLS, 0 }, { "cwd", FTP_CWD, CM_INV }, { "delete", FTP_MDE, 0 }, { "directory", FTP_DIR, 0 }, { "disable", FTP_DIS, 0 }, { "enable", FTP_ENA, 0 }, { "features", FTP_FEA, 0 }, { "get", FTP_GET, 0 }, { "help", FTP_HLP, 0 }, { "idle", FTP_IDL, 0 }, { "login", FTP_USR, CM_INV }, { "mdelete", FTP_MDE, CM_INV }, { "mget", FTP_MGE, 0 }, { "mkdir", FTP_MKD, 0 }, { "modtime", FTP_MOD, 0 }, { "mput", FTP_MPU, 0 }, { "open", FTP_OPN, 0 }, { "opt", FTP_OPT, CM_INV|CM_ABR }, { "opts", FTP_OPT, CM_INV }, { "options", FTP_OPT, 0 }, { "put", FTP_PUT, 0 }, { "pwd", FTP_PWD, 0 }, { "quit", FTP_CLS, CM_INV }, { "quote", FTP_QUO, 0 }, { "reget", FTP_RGE, 0 }, { "rename", FTP_REN, 0 }, { "reput", FTP_REP, 0 }, { "resend", FTP_REP, CM_INV }, { "reset", FTP_RES, 0 }, { "rmdir", FTP_RMD, 0 }, { "send", FTP_PUT, CM_INV }, { "site", FTP_SIT, 0 }, { "size", FTP_SIZ, 0 }, { "status", FTP_STA, 0 }, { "system", FTP_SYS, 0 }, { "type", FTP_TYP, 0 }, { "umask", FTP_UMA, 0 }, { "up", FTP_GUP, CM_INV }, { "user", FTP_USR, 0 }, { "vdirectory",FTP_VDI, 0 }, { "", 0, 0 } }; static int nftpcmd = (sizeof(ftpcmdtab) / sizeof(struct keytab)) - 1; #define OPN_ANO 1 /* FTP OPEN switch codes */ #define OPN_PSW 2 #define OPN_USR 3 #define OPN_ACC 4 #define OPN_ACT 5 #define OPN_PSV 6 #define OPN_TLS 7 #define OPN_NIN 8 #define OPN_NOL 9 #ifdef FTP_SECURITY #ifdef CK_SSL #define USETLSTAB static struct keytab tlstab[] = { /* FTP SSL/TLS switches */ { "/ssl", OPN_TLS, 0 }, { "/tls", OPN_TLS, 0 }, { "", 0, 0 } }; static int ntlstab = (sizeof(tlstab) / sizeof(struct keytab)) - 1; #endif /* CK_SSL */ #endif /* FTP_SECURITY */ static struct keytab ftpswitab[] = { /* FTP command switches */ { "/account", OPN_ACC, CM_ARG }, { "/active", OPN_ACT, 0 }, { "/anonymous", OPN_ANO, 0 }, { "/noinit", OPN_NIN, 0 }, { "/nologin", OPN_NOL, 0 }, { "/passive", OPN_PSV, 0 }, { "/password", OPN_PSW, CM_ARG }, { "/user", OPN_USR, CM_ARG }, { "", 0, 0 } }; static int nftpswi = (sizeof(ftpswitab) / sizeof(struct keytab)) - 1; /* FTP { ENABLE, DISABLE } items */ #define ENA_FEAT 1 #define ENA_MDTM 2 #define ENA_MLST 3 #define ENA_SIZE 4 #define ENA_AUTH 5 static struct keytab ftpenatab[] = { { "AUTH", ENA_AUTH, 0 }, { "FEAT", ENA_FEAT, 0 }, { "MDTM", ENA_MDTM, 0 }, { "ML", ENA_MLST, CM_INV|CM_ABR }, { "MLS", ENA_MLST, CM_INV|CM_ABR }, { "MLSD", ENA_MLST, CM_INV }, { "MLST", ENA_MLST, 0 }, { "SIZE", ENA_SIZE, 0 }, { "", 0, 0 } }; static int nftpena = (sizeof(ftpenatab) / sizeof(struct keytab)) - 1; /* SET FTP command keyword indices */ #define FTS_AUT 1 /* Autoauthentication */ #define FTS_CRY 2 /* Encryption */ #define FTS_LOG 3 /* Autologin */ #define FTS_CPL 4 /* Command protection level */ #define FTS_CFW 5 /* Credentials forwarding */ #define FTS_DPL 6 /* Data protection level */ #define FTS_DBG 7 /* Debugging */ #define FTS_PSV 8 /* Passive mode */ #define FTS_SPC 9 /* Send port commands */ #define FTS_TYP 10 /* (file) Type */ #define FTS_USN 11 /* Unique server names (for files) */ #define FTS_VBM 12 /* Verbose mode */ #define FTS_ATP 13 /* Authentication type */ #define FTS_CNV 14 /* Filename conversion */ #define FTS_TST 15 /* Test (progress) messages */ #define FTS_PRM 16 /* (file) Permissions */ #define FTS_XLA 17 /* Charset translation */ #define FTS_CSR 18 /* Server charset */ #define FTS_ERR 19 /* Error action */ #define FTS_FNC 20 /* Collision */ #define FTS_SRP 21 /* SRP options */ #define FTS_GFT 22 /* GET automatic file-type switching */ #define FTS_DAT 23 /* Set file dates */ #define FTS_STO 24 /* Server time offset */ #define FTS_APW 25 /* Anonymous password */ #define FTS_DIS 26 /* File-transfer display style */ #define FTS_BUG 27 /* Bug(s) */ #define FTS_TMO 28 /* Timeout */ /* FTP BUGS */ #define FTB_SV2 1 /* use SSLv2 */ static struct keytab ftpbugtab[] = { { "use-ssl-v2", FTB_SV2, 0 } }; static int nftpbug = (sizeof(ftpbugtab) / sizeof(struct keytab)); /* FTP PUT options (mutually exclusive, not a bitmask) */ #define PUT_UPD 1 /* Update */ #define PUT_RES 2 /* Restart */ #define PUT_SIM 4 /* Simulation */ #define PUT_DIF 8 /* Dates Differ */ static struct keytab ftpcolxtab[] = { /* SET FTP COLLISION options */ #ifndef MAC { "append", XYFX_A, 0 }, /* append to old file */ #endif /* MAC */ #ifdef COMMENT { "ask", XYFX_Q, 0 }, /* ask what to do (not implemented) */ #endif { "backup", XYFX_B, 0 }, /* rename old file */ #ifndef MAC { "dates-differ", XYFX_M, 0 }, /* accept if dates differ */ { "discard", XYFX_D, 0 }, /* don't accept new file */ { "no-supersede", XYFX_D, CM_INV }, /* ditto (MSK compatibility) */ #endif /* MAC */ { "overwrite", XYFX_X, 0 }, /* overwrite the old file */ { "rename", XYFX_R, 0 }, /* rename the incoming file */ #ifndef MAC /* This crashes Mac Kermit. */ { "update", XYFX_U, 0 }, /* replace if newer */ #endif /* MAC */ { "", 0, 0 } }; static int nftpcolx = (sizeof(ftpcolxtab) / sizeof(struct keytab)) - 1; #ifdef FTP_SECURITY /* FTP authentication options */ #define FTA_AUTO 0 /* Auto */ #define FTA_SRP 1 /* SRP */ #define FTA_GK5 2 /* Kerberos 5 */ #define FTA_K4 3 /* Kerberos 4 */ #define FTA_SSL 4 /* SSL */ #define FTA_TLS 5 /* TLS */ /* FTP authentication types */ #define FTPATYPS 8 static int ftp_auth_type[FTPATYPS] = { #ifdef FTP_GSSAPI FTA_GK5, /* GSSAPI Kerberos 5 */ #endif /* FTP_GK5 */ #ifdef FTP_SRP FTA_SRP, /* SRP */ #endif /* FTP_SRP */ #ifdef FTP_KRB4 FTA_K4, /* Kerberos 4 */ #endif /* FTP_KRB4 */ #ifdef CK_SSL FTA_TLS, /* TLS */ FTA_SSL, /* SSL */ #endif /* CK_SSL */ 0 }; static struct keytab ftpauth[] = { /* SET FTP AUTHTYPE cmd table */ { "automatic", FTA_AUTO, CM_INV }, #ifdef FTP_GSSAPI { "gssapi-krb5", FTA_GK5, 0 }, #endif /* FTP_GSSAPI */ #ifdef FTP_KRB4 { "k4", FTA_K4, CM_INV }, #endif /* FTP_KRB4 */ #ifdef FTP_GSSAPI { "k5", FTA_GK5, CM_INV }, #endif /* FTP_GSSAPI */ #ifdef FTP_KRB4 { "kerberos4", FTA_K4, 0 }, #endif /* FTP_KRB4 */ #ifdef FTP_GSSAPI { "kerberos5", FTA_GK5, CM_INV }, #endif /* FTP_GSSAPI */ #ifdef FTP_KRB4 { "kerberos_iv",FTA_K4, CM_INV }, #endif /* FTP_KRB4 */ #ifdef FTP_GSSAPI { "kerberos_v", FTA_GK5, CM_INV }, #endif /* FTP_GSSAPI */ #ifdef FTP_KRB4 { "krb4", FTA_K4, CM_INV }, #endif /* FTP_KRB4 */ #ifdef FTP_GSSAPI { "krb5", FTA_GK5, CM_INV }, #endif /* FTP_GSSAPI */ #ifdef FTP_SRP { "srp", FTA_SRP, 0 }, #endif /* FTP_SRP */ #ifdef CK_SSL { "ssl", FTA_SSL, 0 }, { "tls", FTA_TLS, 0 }, #endif /* CK_SSL */ { "", 0, 0 } }; static int nftpauth = (sizeof(ftpauth) / sizeof(struct keytab)) - 1; #ifdef FTP_SRP #define SRP_CIPHER 1 #define SRP_HASH 2 static struct keytab ftpsrp[] = { /* SET FTP SRP command table */ { "cipher", SRP_CIPHER, 0 }, { "hash", SRP_HASH, 0 }, { "", 0, 0 } }; static int nftpsrp = (sizeof(ftpsrp) / sizeof(struct keytab)) - 1; #endif /* FTP_SRP */ #endif /* FTP_SECURITY */ static struct keytab ftpset[] = { /* SET FTP commmand table */ { "anonymous-password", FTS_APW, 0 }, #ifdef FTP_SECURITY { "authtype", FTS_ATP, 0 }, { "autoauthentication", FTS_AUT, 0 }, { "autoencryption", FTS_CRY, 0 }, #endif /* FTP_SECURITY */ { "autologin", FTS_LOG, 0 }, { "bug", FTS_BUG, 0 }, #ifndef NOCSETS { "character-set-translation",FTS_XLA, 0 }, #endif /* NOCSETS */ { "collision", FTS_FNC, 0 }, #ifdef FTP_SECURITY { "command-protection-level", FTS_CPL, 0 }, { "cpl", FTS_CPL, CM_INV }, { "credential-forwarding", FTS_CFW, 0 }, { "da", FTS_DAT, CM_INV|CM_ABR }, { "data-protection-level", FTS_DPL, 0 }, #endif /* FTP_SECURITY */ { "dates", FTS_DAT, 0 }, { "debug", FTS_DBG, 0 }, { "display", FTS_DIS, 0 }, #ifdef FTP_SECURITY { "dpl", FTS_DPL, CM_INV }, #endif /* FTP_SECURITY */ { "error-action", FTS_ERR, 0 }, { "filenames", FTS_CNV, 0 }, { "get-filetype-switching", FTS_GFT, 0 }, { "passive-mode", FTS_PSV, 0 }, { "pasv", FTS_PSV, CM_INV }, { "permissions", FTS_PRM, 0 }, { "progress-messages", FTS_TST, 0 }, { "send-port-commands", FTS_SPC, 0 }, #ifndef NOCSETS { "server-character-set", FTS_CSR, 0 }, #endif /* NOCSETS */ { "server-time-offset", FTS_STO, 0 }, #ifdef FTP_SRP { "srp", FTS_SRP, 0 }, #else { "srp", FTS_SRP, CM_INV }, #endif /* FTP_SRP */ #ifdef FTP_TIMEOUT { "timeout", FTS_TMO, 0 }, #endif /* FTP_TIMEOUT */ { "type", FTS_TYP, 0 }, { "unique-server-names", FTS_USN, 0 }, { "verbose-mode", FTS_VBM, 0 }, { "", 0, 0 } }; static int nftpset = (sizeof(ftpset) / sizeof(struct keytab)) - 1; /* GET and PUT switches are approximately the same as Kermit GET and SEND, and use the same SND_xxx definitions, but hijack a couple for FTP use. Don't just make up new ones, since the number of SND_xxx options must be known in advance for the switch-parsing arrays. */ #define SND_USN SND_PRO /* /UNIQUE instead of /PROTOCOL */ #define SND_PRM SND_PIP /* /PERMISSIONS instead of /PIPES */ #define SND_TEN SND_CAL /* /TENEX instead of /CALIBRATE */ static struct keytab putswi[] = { /* FTP PUT switch table */ { "/after", SND_AFT, CM_ARG }, #ifdef PUTARRAY { "/array", SND_ARR, CM_ARG }, #endif /* PUTARRAY */ { "/as", SND_ASN, CM_ARG|CM_INV|CM_ABR }, { "/as-name", SND_ASN, CM_ARG }, { "/ascii", SND_TXT, CM_INV }, { "/b", SND_BIN, CM_INV|CM_ABR }, { "/before", SND_BEF, CM_ARG }, { "/binary", SND_BIN, 0 }, #ifdef PUTPIPE { "/command", SND_CMD, CM_PSH }, #endif /* PUTPIPE */ #ifdef COMMENT /* This works but it's dangerous */ #ifdef DOUPDATE { "/dates-differ", SND_DIF, CM_INV }, #endif /* DOUPDATE */ #endif /* COMMENT */ { "/delete", SND_DEL, 0 }, #ifdef UNIXOROSK { "/dotfiles", SND_DOT, 0 }, #endif /* UNIXOROSK */ { "/error-action", SND_ERR, CM_ARG }, { "/except", SND_EXC, CM_ARG }, { "/filenames", SND_NAM, CM_ARG }, #ifdef PIPESEND #ifndef NOSPL { "/filter", SND_FLT, CM_ARG|CM_PSH }, #endif /* NOSPL */ #endif /* PIPESEND */ #ifdef CKSYMLINK { "/followlinks", SND_LNK, 0 }, #endif /* CKSYMLINK */ #ifdef VMS { "/image", SND_IMG, 0 }, #else { "/image", SND_BIN, CM_INV }, #endif /* VMS */ { "/larger-than", SND_LAR, CM_ARG }, { "/listfile", SND_FIL, CM_ARG }, #ifndef NOCSETS { "/local-character-set", SND_CSL, CM_ARG }, #endif /* NOCSETS */ #ifdef CK_TMPDIR { "/move-to", SND_MOV, CM_ARG }, #endif /* CK_TMPDIR */ { "/nobackupfiles", SND_NOB, 0 }, #ifdef UNIXOROSK { "/nodotfiles", SND_NOD, 0 }, #endif /* UNIXOROSK */ #ifdef CKSYMLINK { "/nofollowlinks", SND_NLK, 0 }, #endif /* CKSYMLINK */ { "/not-after", SND_NAF, CM_ARG }, { "/not-before", SND_NBE, CM_ARG }, #ifdef UNIX { "/permissions", SND_PRM, CM_ARG }, #else { "/permissions", SND_PRM, CM_ARG|CM_INV }, #endif /* UNIX */ { "/quiet", SND_SHH, 0 }, #ifdef FTP_RESTART { "/recover", SND_RES, 0 }, #endif /* FTP_RESTART */ #ifdef RECURSIVE { "/recursive", SND_REC, 0 }, #endif /* RECURSIVE */ { "/rename-to", SND_REN, CM_ARG }, #ifdef FTP_RESTART { "/restart", SND_RES, CM_INV }, #endif /* FTP_RESTART */ #ifndef NOCSETS { "/server-character-set", SND_CSR, CM_ARG }, #endif /* NOCSETS */ { "/server-rename-to", SND_SRN, CM_ARG }, { "/simulate", SND_SIM, 0 }, { "/since", SND_AFT, CM_INV|CM_ARG }, { "/smaller-than", SND_SMA, CM_ARG }, #ifdef COMMENT { "/starting-at", SND_STA, CM_ARG }, #endif /* COMMENT */ #ifdef RECURSIVE { "/subdirectories", SND_REC, CM_INV }, #endif /* RECURSIVE */ { "/tenex", SND_TEN, 0 }, { "/text", SND_TXT, 0 }, #ifndef NOCSETS { "/transparent", SND_XPA, 0 }, #endif /* NOCSETS */ { "/type", SND_TYP, CM_ARG }, #ifdef DOUPDATE { "/update", SND_UPD, 0 }, #endif /* DOUPDATE */ { "/unique-server-names", SND_USN, 0 }, { "", 0, 0 } }; static int nputswi = (sizeof(putswi) / sizeof(struct keytab)) - 1; static struct keytab getswi[] = { /* FTP [M]GET switch table */ { "/after", SND_AFT, CM_INV }, { "/as", SND_ASN, CM_ARG|CM_INV|CM_ABR }, { "/as-name", SND_ASN, CM_ARG }, { "/ascii", SND_TXT, CM_INV }, { "/before", SND_BEF, CM_INV }, { "/binary", SND_BIN, 0 }, { "/collision", SND_COL, CM_ARG }, #ifdef PUTPIPE { "/command", SND_CMD, CM_PSH }, #endif /* PUTPIPE */ { "/delete", SND_DEL, 0 }, { "/error-action", SND_ERR, CM_ARG }, { "/except", SND_EXC, CM_ARG }, { "/filenames", SND_NAM, CM_ARG }, #ifdef PIPESEND #ifndef NOSPL { "/filter", SND_FLT, CM_ARG|CM_PSH }, #endif /* NOSPL */ #endif /* PIPESEND */ #ifdef VMS { "/image", SND_IMG, 0 }, #else { "/image", SND_BIN, CM_INV }, #endif /* VMS */ { "/larger-than", SND_LAR, CM_ARG }, { "/listfile", SND_FIL, CM_ARG }, #ifndef NOCSETS { "/local-character-set", SND_CSL, CM_ARG }, #endif /* NOCSETS */ { "/match", SND_PAT, CM_ARG }, { "/ml", SND_MLS, CM_INV|CM_ABR }, { "/mls", SND_MLS, CM_INV|CM_ABR }, { "/mlsd", SND_MLS, 0 }, { "/mlst", SND_MLS, CM_INV }, #ifdef CK_TMPDIR { "/move-to", SND_MOV, CM_ARG }, #endif /* CK_TMPDIR */ { "/namelist", SND_NML, CM_ARG }, { "/nlst", SND_NLS, 0 }, { "/nobackupfiles", SND_NOB, 0 }, { "/nodotfiles", SND_NOD, 0 }, #ifdef DOUPDATE { "/dates-differ", SND_DIF, CM_INV }, #endif /* DOUPDATE */ { "/not-after", SND_NAF, CM_INV }, { "/not-before", SND_NBE, CM_INV }, { "/permissions", SND_PRM, CM_INV }, { "/quiet", SND_SHH, 0 }, #ifdef FTP_RESTART { "/recover", SND_RES, 0 }, #endif /* FTP_RESTART */ #ifdef RECURSIVE { "/recursive", SND_REC, 0 }, #endif /* RECURSIVE */ { "/rename-to", SND_REN, CM_ARG }, #ifdef FTP_RESTART { "/restart", SND_RES, CM_INV }, #endif /* FTP_RESTART */ #ifndef NOCSETS { "/server-character-set", SND_CSR, CM_ARG }, #endif /* NOCSETS */ { "/server-rename-to", SND_SRN, CM_ARG }, { "/smaller-than", SND_SMA, CM_ARG }, #ifdef RECURSIVE { "/subdirectories", SND_REC, CM_INV }, #endif /* RECURSIVE */ { "/text", SND_TXT, 0 }, { "/tenex", SND_TEN, 0 }, #ifndef NOCSETS { "/transparent", SND_XPA, 0 }, #endif /* NOCSETS */ { "/to-screen", SND_MAI, 0 }, #ifdef DOUPDATE { "/update", SND_UPD, CM_INV }, #endif /* DOUPDATE */ { "", 0, 0 } }; static int ngetswi = (sizeof(getswi) / sizeof(struct keytab)) - 1; static struct keytab delswi[] = { /* FTP [M]DELETE switch table */ { "/error-action", SND_ERR, CM_ARG }, { "/except", SND_EXC, CM_ARG }, { "/filenames", SND_NAM, CM_ARG }, { "/larger-than", SND_LAR, CM_ARG }, { "/nobackupfiles", SND_NOB, 0 }, #ifdef UNIXOROSK { "/nodotfiles", SND_NOD, 0 }, #endif /* UNIXOROSK */ { "/quiet", SND_SHH, 0 }, #ifdef RECURSIVE { "/recursive", SND_REC, 0 }, #endif /* RECURSIVE */ { "/smaller-than", SND_SMA, CM_ARG }, #ifdef RECURSIVE { "/subdirectories", SND_REC, CM_INV }, #endif /* RECURSIVE */ { "", 0, 0 } }; static int ndelswi = (sizeof(delswi) / sizeof(struct keytab)) - 1; static struct keytab fntab[] = { /* Filename conversion keyword table */ { "automatic", 2, CNV_AUTO }, { "converted", 1, CNV_CNV }, { "literal", 0, CNV_LIT } }; static int nfntab = (sizeof(fntab) / sizeof(struct keytab)); static struct keytab ftptyp[] = { /* SET FTP TYPE table */ { "ascii", FTT_ASC, 0 }, { "binary", FTT_BIN, 0 }, { "tenex", FTT_TEN, 0 }, { "text", FTT_ASC, CM_INV }, { "", 0, 0 } }; static int nftptyp = (sizeof(ftptyp) / sizeof(struct keytab)) - 1; #ifdef FTP_SECURITY static struct keytab ftppro[] = { /* SET FTP PROTECTION-LEVEL table */ { "clear", FPL_CLR, 0 }, { "confidential", FPL_CON, 0 }, { "private", FPL_PRV, 0 }, { "safe", FPL_SAF, 0 }, { "", 0, 0 } }; static int nftppro = (sizeof(ftppro) / sizeof(struct keytab)) - 1; #endif /* FTP_SECURITY */ /* Definitions for FTP from RFC765. */ /* Reply codes */ #define REPLY_PRELIM 1 /* Positive preliminary */ #define REPLY_COMPLETE 2 /* Positive completion */ #define REPLY_CONTINUE 3 /* Positive intermediate */ #define REPLY_TRANSIENT 4 /* Transient negative completion */ #define REPLY_ERROR 5 /* Permanent negative completion */ #define REPLY_SECURE 6 /* Security encoded message */ /* Form codes and names */ #define FORM_N 1 /* Non-print */ #define FORM_T 2 /* Telnet format effectors */ #define FORM_C 3 /* Carriage control (ASA) */ /* Structure codes and names */ #define STRU_F 1 /* File (no record structure) */ #define STRU_R 2 /* Record structure */ #define STRU_P 3 /* Page structure */ /* Mode types and names */ #define MODE_S 1 /* Stream */ #define MODE_B 2 /* Block */ #define MODE_C 3 /* Compressed */ /* Protection levels and names */ #define PROT_C 1 /* Clear */ #define PROT_S 2 /* Safe */ #define PROT_P 3 /* Private */ #define PROT_E 4 /* Confidential */ #ifdef COMMENT /* Not used */ #ifdef FTP_NAMES char *strunames[] = {"0", "File", "Record", "Page" }; char *formnames[] = {"0", "Nonprint", "Telnet", "Carriage-control" }; char *modenames[] = {"0", "Stream", "Block", "Compressed" }; char *levelnames[] = {"0", "Clear", "Safe", "Private", "Confidential" }; #endif /* FTP_NAMES */ /* Record Tokens */ #define REC_ESC '\377' /* Record-mode Escape */ #define REC_EOR '\001' /* Record-mode End-of-Record */ #define REC_EOF '\002' /* Record-mode End-of-File */ /* Block Header */ #define BLK_EOR 0x80 /* Block is End-of-Record */ #define BLK_EOF 0x40 /* Block is End-of-File */ #define BLK_REPLY_ERRORS 0x20 /* Block might have errors */ #define BLK_RESTART 0x10 /* Block is Restart Marker */ #define BLK_BYTECOUNT 2 /* Bytes in this block */ #endif /* COMMENT */ #define RADIX_ENCODE 0 /* radix_encode() function codes */ #define RADIX_DECODE 1 /* The default setpbsz() value in the Unix FTP client is 1<<20 (1MB). This results in a serious performance degradation due to the increased number of page faults and the inability to overlap encrypt/decrypt, file i/o, and network i/o. So instead we set the value to 1<<13 (8K), about half the size of the typical TCP window. Maybe we should add a command to allow the value to be changed. */ #define DEFAULT_PBSZ 1<<13 /* Prototypes */ _PROTOTYP(int remtxt, (char **) ); _PROTOTYP(char * gskreason, (int) ); _PROTOTYP(static int ftpclose,(void)); _PROTOTYP(static int zzsend, (int, CHAR)); _PROTOTYP(static int getreply,(int,int,int,int,int)); _PROTOTYP(static int radix_encode,(CHAR[], CHAR[], int, int *, int)); _PROTOTYP(static int setpbsz,(unsigned int)); _PROTOTYP(static int recvrequest,(char *,char *,char *,char *, int,int,char *,int,int,int)); _PROTOTYP(static int ftpcmd,(char *,char *,int,int,int)); _PROTOTYP(static int fts_cpl,(int)); _PROTOTYP(static int fts_dpl,(int)); #ifdef FTP_SECURITY _PROTOTYP(static int ftp_auth, (void)); #endif /* FTP_SECURITY */ _PROTOTYP(static int ftp_user, (char *, char *, char *)); _PROTOTYP(static int ftp_login, (char *)); _PROTOTYP(static int ftp_reset, (void)); _PROTOTYP(static int ftp_rename, (char *, char *)); _PROTOTYP(static int ftp_umask, (char *)); _PROTOTYP(static int secure_flush, (int)); #ifdef COMMENT _PROTOTYP(static int secure_putc, (char, int)); #endif /* COMMENT */ _PROTOTYP(static int secure_write, (int, CHAR *, unsigned int)); _PROTOTYP(static int scommand, (char *)); _PROTOTYP(static int secure_putbuf, (int, CHAR *, unsigned int)); _PROTOTYP(static int secure_getc, (int, int)); _PROTOTYP(static int secure_getbyte, (int, int)); _PROTOTYP(static int secure_read, (int, char *, int)); _PROTOTYP(static int initconn, (void)); _PROTOTYP(static int dataconn, (char *)); _PROTOTYP(static int setprotbuf,(unsigned int)); _PROTOTYP(static int sendrequest, (char *, char *, char *, int,int,int,int)); _PROTOTYP(static char * radix_error,(int)); _PROTOTYP(static char * ftp_hookup,(char *, int, int)); _PROTOTYP(static CHAR * remote_files, (int, CHAR *, CHAR *, int)); _PROTOTYP(static VOID mlsreset, (void)); _PROTOTYP(static VOID secure_error, (char *fmt, ...)); _PROTOTYP(static VOID lostpeer, (void)); _PROTOTYP(static VOID cancel_remote, (int)); _PROTOTYP(static VOID changetype, (int, int)); _PROTOTYP(static sigtype cmdcancel, (int)); #ifdef FTP_SRP _PROTOTYP(static int srp_reset, ()); _PROTOTYP(static int srp_ftp_auth, (char *,char *,char *)); _PROTOTYP(static int srp_put, (CHAR *, CHAR **, int, int *)); _PROTOTYP(static int srp_get, (CHAR **, CHAR **, int *, int *)); _PROTOTYP(static int srp_encode, (int, CHAR *, CHAR *, unsigned int)); _PROTOTYP(static int srp_decode, (int, CHAR *, CHAR *, unsigned int)); _PROTOTYP(static int srp_selcipher, (char *)); _PROTOTYP(static int srp_selhash, (char *)); #endif /* FTP_SRP */ #ifdef FTP_GSSAPI _PROTOTYP(static void user_gss_error,(OM_uint32, OM_uint32,char *)); #endif /* FTP_GSSAPI */ /* D O F T P A R G -- Do an FTP command-line argument. */ #ifdef FTP_SECURITY #ifndef NOICP #define FT_NOGSS 1 #define FT_NOK4 2 #define FT_NOSRP 3 #define FT_NOSSL 4 #define FT_NOTLS 5 #define FT_CERTFI 6 #define FT_OKCERT 7 #define FT_DEBUG 8 #define FT_KEY 9 #define FT_SECURE 10 #define FT_VERIFY 11 static struct keytab ftpztab[] = { { "!gss", FT_NOGSS, 0 }, { "!krb4", FT_NOK4, 0 }, { "!srp", FT_NOSRP, 0 }, { "!ssl", FT_NOSSL, 0 }, { "!tls", FT_NOTLS, 0 }, { "cert", FT_CERTFI, CM_ARG }, { "certsok", FT_OKCERT, 0 }, { "debug", FT_DEBUG, 0 }, { "key", FT_KEY, CM_ARG }, { "nogss", FT_NOGSS, 0 }, { "nokrb4", FT_NOK4, 0 }, { "nosrp", FT_NOSRP, 0 }, { "nossl", FT_NOSSL, 0 }, { "notls", FT_NOTLS, 0 }, #ifdef COMMENT { "secure", FT_SECURE, 0 }, #endif /* COMMENT */ { "verify", FT_VERIFY, CM_ARG }, { "", 0, 0 } }; static int nftpztab = sizeof(ftpztab) / sizeof(struct keytab) - 1; /* The following cipher and hash tables should be replaced with dynamicly created versions based upon the linked library. */ #define SRP_BLOWFISH_ECB 1 #define SRP_BLOWFISH_CBC 2 #define SRP_BLOWFISH_CFB64 3 #define SRP_BLOWFISH_OFB64 4 #define SRP_CAST5_ECB 5 #define SRP_CAST5_CBC 6 #define SRP_CAST5_CFB64 7 #define SRP_CAST5_OFB64 8 #define SRP_DES_ECB 9 #define SRP_DES_CBC 10 #define SRP_DES_CFB64 11 #define SRP_DES_OFB64 12 #define SRP_DES3_ECB 13 #define SRP_DES3_CBC 14 #define SRP_DES3_CFB64 15 #define SRP_DES3_OFB64 16 static struct keytab ciphertab[] = { { "blowfish_ecb", SRP_BLOWFISH_ECB, 0 }, { "blowfish_cbc", SRP_BLOWFISH_CBC, 0 }, { "blowfish_cfb64", SRP_BLOWFISH_CFB64, 0 }, { "blowfish_ofb64", SRP_BLOWFISH_OFB64, 0 }, { "cast5_ecb", SRP_CAST5_ECB, 0 }, { "cast5_cbc", SRP_CAST5_CBC, 0 }, { "cast5_cfb64", SRP_CAST5_CFB64, 0 }, { "cast5_ofb64", SRP_CAST5_OFB64, 0 }, { "des_ecb", SRP_DES_ECB, 0 }, { "des_cbc", SRP_DES_CBC, 0 }, { "des_cfb64", SRP_DES_CFB64, 0 }, { "des_ofb64", SRP_DES_OFB64, 0 }, { "des3_ecb", SRP_DES3_ECB, 0 }, { "des3_cbc", SRP_DES3_CBC, 0 }, { "des3_cfb64", SRP_DES3_CFB64, 0 }, { "des3_ofb64", SRP_DES3_OFB64, 0 }, { "none", 0, 0 }, { "", 0, 0 } }; static int nciphertab = sizeof(ciphertab) / sizeof(struct keytab) - 1; #define SRP_MD5 1 #define SRP_SHA 2 static struct keytab hashtab[] = { { "md5", SRP_MD5, 0 }, { "none", 0, 0 }, { "sha", SRP_SHA, 0 }, { "", 0, 0 } }; static int nhashtab = sizeof(hashtab) / sizeof(struct keytab) - 1; #endif /* NOICP */ #endif /* FTP_SECURITY */ static char * strval(s1,s2) char * s1, * s2; { if (!s1) s1 = ""; if (!s2) s2 = ""; return(*s1 ? s1 : (*s2 ? s2 : "(none)")); } #ifndef NOCSETS static char * rfnptr = NULL; static int rfnlen = 0; static char rfnbuf[RFNBUFSIZ]; /* Remote filename translate buffer */ static char * xgnbp = NULL; static int strgetc() { /* Helper function for xgnbyte() */ int c; if (!xgnbp) return(-1); if (!*xgnbp) return(-1); c = (unsigned) *xgnbp++; return(((unsigned) c) & 0xff); } static int /* Helper function for xpnbyte() */ #ifdef CK_ANSIC strputc(char c) #else strputc(c) char c; #endif /* CK_ANSIC */ { rfnlen = rfnptr - rfnbuf; if (rfnlen >= (RFNBUFSIZ - 1)) return(-1); *rfnptr++ = c; *rfnptr = NUL; return(0); } static int #ifdef CK_ANSIC xprintc(char c) #else xprintc(c) char c; #endif /* CK_ANSIC */ { printf("%c",c); return(0); } static VOID bytswap(c0,c1) int * c0, * c1; { int t; t = *c0; *c0 = *c1; *c1 = t; } #endif /* NOCSETS */ #ifdef CKLOGDIAL char ftplogbuf[CXLOGBUFL] = { NUL, NUL }; /* Connection Log */ int ftplogactive = 0; long ftplogprev = 0L; VOID ftplogend() { extern int dialog; extern char diafil[]; long d1, d2, t1, t2; char buf[32], * p; debug(F111,"ftp cx log active",ckitoa(dialog),ftplogactive); debug(F110,"ftp cx log buf",ftplogbuf,0); if (!ftplogactive || !ftplogbuf[0]) /* No active record */ return; ftplogactive = 0; /* Record is not active */ d1 = mjd((char *)ftplogbuf); /* Get start date of this session */ ckstrncpy(buf,ckdate(),31); /* Get current date */ d2 = mjd(buf); /* Convert them to mjds */ p = ftplogbuf; /* Get start time */ p[11] = NUL; p[14] = NUL; /* Convert to seconds */ t1 = atol(p+9) * 3600L + atol(p+12) * 60L + atol(p+15); p[11] = ':'; p[14] = ':'; p = buf; /* Get end time */ p[11] = NUL; p[14] = NUL; t2 = atol(p+9) * 3600L + atol(p+12) * 60L + atol(p+15); t2 = ((d2 - d1) * 86400L) + (t2 - t1); /* Compute elapsed time */ if (t2 > -1L) { ftplogprev = t2; p = hhmmss(t2); ckstrncat(ftplogbuf,"E=",CXLOGBUFL); /* Append to log record */ ckstrncat(ftplogbuf,p,CXLOGBUFL); } else ftplogprev = 0L; debug(F101,"ftp cx log dialog","",dialog); if (dialog) { /* If logging */ int x; x = diaopn(diafil,1,1); /* Open log in append mode */ if (x > 0) { debug(F101,"ftp cx log open","",x); x = zsoutl(ZDIFIL,ftplogbuf); /* Write the record */ debug(F101,"ftp cx log write","",x); x = zclose(ZDIFIL); /* Close the log */ debug(F101,"ftp cx log close","",x); } } } VOID dologftp() { ftplogend(); /* Previous session not closed out? */ ftplogprev = 0L; ftplogactive = 1; /* Record is active */ ckmakxmsg(ftplogbuf,CXLOGBUFL, ckdate()," ",strval(ftp_logname,NULL)," ",ckgetpid(), " T=FTP N=", strval(ftp_host,NULL)," H=",myhost, " P=", ckitoa(ftp_port)," "); /* SMS 2007/02/15 */ debug(F110,"ftp cx log begin",ftplogbuf,0); } #endif /* CKLOGDIAL */ static char * dummy[2] = { NULL, NULL }; static struct keytab modetab[] = { { "active", 0, 0 }, { "passive", 1, 0 } }; #ifndef NOCMDL int /* Called from ckuusy.c */ #ifdef CK_ANSIC doftparg(char c) #else doftparg(c) char c; #endif /* CK_ANSIC */ /* doftparg */ { int x, z; char *xp; extern char **xargv, *xarg0; extern int xargc, stayflg, haveftpuid; extern char uidbuf[]; xp = *xargv+1; /* Pointer for bundled args */ while (c) { if (ckstrchr("MuDPkcHzm",c)) { /* Options that take arguments */ if (*(xp+1)) { fatal("?Invalid argument bundling"); } xargv++, xargc--; if ((xargc < 1) || (**xargv == '-')) { fatal("?Required argument missing"); } } switch (c) { /* Big switch on arg */ case 'h': /* help */ printf("C-Kermit's FTP client command-line personality. Usage:\n"); printf(" %s [ options ] host [ port ] [-pg files ]\n\n",xarg0); printf("Options:\n"); printf(" -h = help (this message)\n"); printf(" -m mode = \"passive\" (default) or \"active\"\n"); printf(" -u name = username for autologin (or -M)\n"); printf(" -P password = password for autologin (RISKY)\n"); printf(" -A = autologin anonymously\n"); printf(" -D directory = cd after autologin\n"); printf(" -b = force binary mode\n"); printf(" -a = force text (\"ascii\") mode (or -T)\n"); printf(" -d = debug (double to add timestamps)\n"); printf(" -n = no autologin\n"); printf(" -v = verbose (default)\n"); printf(" -q = quiet\n"); printf(" -S = Stay (issue command prompt when done)\n"); printf(" -Y = do not execute Kermit init file\n"); printf(" -p files = files to put after autologin (or -s)\n"); printf(" -g files = files to get after autologin\n"); printf(" -R = recursive (for use with -p)\n"); #ifdef FTP_SECURITY printf("\nSecurity options:\n"); printf(" -k realm = Kerberos 4 realm\n"); printf(" -f = Kerboros 5 credentials forwarding\n"); printf(" -x = autoencryption mode\n"); printf(" -c cipher = SRP cipher type\n"); printf(" -H hash = SRP encryption hash\n"); printf(" -z option = Security options\n"); #endif /* FTP_SECURITY */ printf("\n-p or -g, if given, should be last. Example:\n"); printf(" ftp -A kermit.columbia.edu -D kermit -ag TESTFILE\n"); doexit(GOOD_EXIT,-1); break; case 'R': /* Recursive */ recursive = 1; break; case 'd': /* Debug */ #ifdef DEBUG if (deblog) { extern int debtim; debtim = 1; } else { deblog = debopn("debug.log",0); debok = 1; } #endif /* DEBUG */ /* fall thru on purpose */ case 't': /* Trace */ ftp_deb++; break; case 'n': /* No autologin */ ftp_log = 0; break; case 'i': /* No prompt */ case 'v': /* Verbose */ break; /* (ignored) */ case 'q': /* Quiet */ quiet = 1; break; case 'S': /* Stay */ stayflg = 1; break; case 'M': case 'u': /* My User Name */ if ((int)strlen(*xargv) > 63) { fatal("username too long"); } ckstrncpy(uidbuf,*xargv,UIDBUFLEN); haveftpuid = 1; break; case 'A': ckstrncpy(uidbuf,"anonymous",UIDBUFLEN); haveftpuid = 1; break; case 'T': /* Text */ case 'a': /* "ascii" */ case 'b': /* Binary */ binary = (c == 'b') ? FTT_BIN : FTT_ASC; ftp_xfermode = XMODE_M; filepeek = 0; patterns = 0; break; case 'g': /* Get */ case 'p': /* Put */ case 's': { /* Send (= Put) */ int havefiles, rc; if (ftp_action) { fatal("Only one FTP action at a time please"); } if (*(xp+1)) { fatal("invalid argument bundling after -s"); } nfils = 0; /* Initialize file counter */ havefiles = 0; /* Assume nothing to send */ cmlist = xargv + 1; /* Remember this pointer */ while (++xargv, --xargc > 0) { /* Traverse the list */ if (c == 'g') { havefiles++; nfils++; continue; } #ifdef RECURSIVE if (!strcmp(*xargv,".")) { havefiles = 1; nfils++; recursive = 1; } else #endif /* RECURSIVE */ if ((rc = zchki(*xargv)) > -1 || (rc == -2)) { if (rc != -2) havefiles = 1; nfils++; } else if (iswild(*xargv) && nzxpand(*xargv,0) > 0) { havefiles = 1; nfils++; } } xargc++, xargv--; /* Adjust argv/argc */ if (!havefiles) { if (c == 'g') { fatal("No files to put"); } else { fatal("No files to get"); } } ftp_action = c; break; } case 'D': /* Directory */ makestr(&ftp_rdir,*xargv); break; case 'm': /* Mode (Active/Passive */ ftp_psv = lookup(modetab,*xargv,2,NULL); if (ftp_psv < 0) fatal("Invalid mode"); break; case 'P': makestr(&ftp_tmp,*xargv); /* You-Know-What */ break; case 'Y': /* No initialization file */ break; /* (already done in prescan) */ #ifdef CK_URL case 'U': { /* URL */ /* These are set by urlparse() - any not set are NULL */ if (g_url.hos) { /* Kermit has accepted host:port notation since many years before URLs were invented. Unfortunately, URLs conflict with this notation. Thus "ftp host:449" looks like a URL and results in service = host and host = 449. Here we try to catch this situation transparently to the user. */ if (ckstrcmp(g_url.svc,"ftp",-1,0) #ifdef CK_SSL && ckstrcmp(g_url.svc,"ftps",-1,0) #endif /* CK_SSL */ ) { if (!g_url.usr && !g_url.psw && !g_url.por && !g_url.pth) { g_url.por = g_url.hos; g_url.hos = g_url.svc; g_url.svc = "ftp"; } else { ckmakmsg(tmpbuf,TMPBUFSIZ,"Non-FTP URL: service=", g_url.svc," host=",g_url.hos); fatal(tmpbuf); } } makestr(&ftp_host,g_url.hos); if (g_url.usr) { haveftpuid = 1; ckstrncpy(uidbuf,g_url.usr,UIDBUFLEN); makestr(&ftp_logname,uidbuf); } if (g_url.psw) { makestr(&ftp_tmp,g_url.psw); } if (g_url.pth) { if (!g_url.usr) { haveftpuid = 1; ckstrncpy(uidbuf,"anonymous",UIDBUFLEN); makestr(&ftp_logname,uidbuf); } if (ftp_action) { fatal("Only one FTP action at a time please"); } if (!stayflg) quiet = 1; nfils = 1; dummy[0] = g_url.pth; cmlist = dummy; ftp_action = 'g'; } xp = NULL; haveurl = 1; } break; } #endif /* CK_URL */ #ifdef FTP_SECURITY case 'k': { /* K4 Realm */ #ifdef FTP_KRB4 ckstrncpy(ftp_realm,*xargv, REALM_SZ); #endif /* FTP_KRB4 */ if (ftp_deb) printf("K4 Realm = [%s]\n",*xargv); break; } case 'f': { #ifdef FTP_GSSAPI ftp_cfw = 1; if (ftp_deb) printf("K5 Credentials Forwarding\n"); #else /* FTP_GSSAPI */ printf("K5 Credentials Forwarding not supported\n"); #endif /* FTP_GSSAPI */ break; } case 'x': { ftp_cry = 1; if (ftp_deb) printf("Autoencryption\n"); break; } case 'c': { /* Cipher */ #ifdef FTP_SRP if (!srp_selcipher(*xargv)) { if (ftp_deb) printf("SRP cipher type: \"%s\"\n",*xargv); } else printf("?Invalid SRP cipher type: \"%s\"\n",*xargv); #else /* FTP_SRP */ printf("?SRP not supported\n"); #endif /* FTP_SRP */ break; } case 'H': { #ifdef FTP_SRP if (!srp_selhash(*xargv)) { if (ftp_deb) printf("SRP hash type: \"%s\"\n",*xargv); } else printf("?Invalid SRP hash type: \"%s\"\n",*xargv); #else /* FTP_SRP */ printf("?SRP not supported\n"); #endif /* FTP_SRP */ break; } case 'z': { /* *xargv contains a value of the form tag=value */ /* we need to lookup the tag and save the value */ char * p = NULL, * q = NULL; makestr(&p,*xargv); y = ckindex("=",p,0,0,1); if (y > 0) p[y-1] = '\0'; x = lookup(ftpztab,p,nftpztab,&z); if (x < 0) { printf("?Invalid security option: \"%s\"\n",p); } else { if (ftp_deb) printf("Security option: \"%s",p); if (ftpztab[z].flgs & CM_ARG) { if (y <= 0) fatal("?Missing required value"); q = &p[y]; if (!*q) fatal("?Missing required value"); if (ftp_deb) printf("=%s\"",q); } switch (ftpztab[z].kwval) { /* -z options w/args */ case FT_NOGSS: #ifdef FTP_GSSAPI for (z = 0; z < FTPATYPS && ftp_auth_type[z]; z++) { if (ftp_auth_type[z] == FTA_GK5) { for (y = z; y < (FTPATYPS-1) && ftp_auth_type[y]; y++ ) ftp_auth_type[y] = ftp_auth_type[y+1]; ftp_auth_type[FTPATYPS-1] = 0; break; } } #endif /* FTP_GSSAPI */ break; case FT_NOK4: #ifdef FTP_KRB4 for (z = 0; z < FTPATYPS && ftp_auth_type[z]; z++) { if (ftp_auth_type[z] == FTA_K4) { for (y = z; y < (FTPATYPS-1) && ftp_auth_type[y]; y++ ) ftp_auth_type[y] = ftp_auth_type[y+1]; ftp_auth_type[FTPATYPS-1] = 0; break; } } #endif /* FTP_KRB4 */ break; case FT_NOSRP: #ifdef FTP_SRP for (z = 0; z < FTPATYPS && ftp_auth_type[z]; z++) { if (ftp_auth_type[z] == FTA_SRP) { for (y = z; y < (FTPATYPS-1) && ftp_auth_type[y]; y++ ) ftp_auth_type[y] = ftp_auth_type[y+1]; ftp_auth_type[FTPATYPS-1] = 0; break; } } #endif /* FTP_SRP */ break; case FT_NOSSL: #ifdef CK_SSL for (z = 0; z < FTPATYPS && ftp_auth_type[z]; z++) { if (ftp_auth_type[z] == FTA_SSL) { for (y = z; y < (FTPATYPS-1) && ftp_auth_type[y]; y++ ) ftp_auth_type[y] = ftp_auth_type[y+1]; ftp_auth_type[FTPATYPS-1] = 0; break; } } #endif /* CK_SSL */ break; case FT_NOTLS: #ifdef CK_SSL for (z = 0; z < FTPATYPS && ftp_auth_type[z]; z++) { if (ftp_auth_type[z] == FTA_TLS) { for (y = z; y < (FTPATYPS-1) && ftp_auth_type[y]; y++ ) ftp_auth_type[y] = ftp_auth_type[y+1]; ftp_auth_type[FTPATYPS-1] = 0; break; } } #endif /* CK_SSL */ break; case FT_CERTFI: #ifdef CK_SSL makestr(&ssl_rsa_cert_file,q); #endif /* CK_SSL */ break; case FT_OKCERT: #ifdef CK_SSL ssl_certsok_flag = 1; #endif /* CK_SSL */ break; case FT_DEBUG: #ifdef DEBUG if (deblog) { extern int debtim; debtim = 1; } else { deblog = debopn("debug.log",0); } #endif /* DEBUG */ break; case FT_KEY: #ifdef CK_SSL makestr(&ssl_rsa_key_file,q); #endif /* CK_SSL */ break; case FT_SECURE: /* no equivalent */ break; case FT_VERIFY: #ifdef CK_SSL if (!rdigits(q)) printf("?Bad number: %s\n",q); ssl_verify_flag = atoi(q); #endif /* CK_SSL */ break; } } if (ftp_deb) printf("\"\n"); free(p); break; } #endif /* FTP_SECURITY */ default: fatal2(*xargv, "unknown command-line option, type \"ftp -h\" for help" ); } if (!xp) break; c = *++xp; /* See if options are bundled */ } return(0); } #endif /* NOCMDL */ int ftpisconnected() { return(connected); } int ftpisloggedin() { return(connected ? loggedin : 0); } int ftpissecure() { return((ftp_dpl == FPL_CLR && !ssl_ftp_proxy) ? 0 : 1); } static VOID ftscreen(n, c, z, s) int n; char c; CK_OFF_T z; char * s; { if (displa && fdispla && !backgrd && !quiet && !out2screen) { if (!dpyactive) { ckscreen(SCR_PT,'S',(CK_OFF_T)0,""); dpyactive = 1; } ckscreen(n,c,z,s); } } #ifndef OS2 /* g m s t i m e r -- Millisecond timer */ long gmstimer() { #ifdef HAVE_MSECS /* For those versions of ztime() that also set global ztmsec. */ char *p = NULL; long z; ztime(&p); if (!p) return(0L); if (!*p) return(0L); z = atol(p+11) * 3600L + atol(p+14) * 60L + atol(p+17); return(z * 1000 + ztmsec); #else return((long)time(NULL) * 1000L); #endif /* HAVE_MSECS */ } #endif /* OS2 */ /* d o s e t f t p -- The SET FTP command */ int dosetftp() { int cx; if ((cx = cmkey(ftpset,nftpset,"","",xxstring)) < 0) /* Set what? */ return(cx); switch (cx) { case FTS_FNC: /* Filename collision action */ if ((x = cmkey(ftpcolxtab,nftpcolx,"","",xxstring)) < 0) return(x); if ((y = cmcfm()) < 0) return(y); ftp_fnc = x; return(1); case FTS_CNV: /* Filename conversion */ if ((x = cmkey(fntab,nfntab,"","automatic",xxstring)) < 0) return(x); if ((y = cmcfm()) < 0) return(y); ftp_cnv = x; return(1); case FTS_DBG: /* Debug messages */ return(seton(&ftp_deb)); case FTS_LOG: /* Auto-login */ return(seton(&ftp_log)); case FTS_PSV: /* Passive mode */ return(dosetftppsv()); case FTS_SPC: /* Send port commands */ x = seton(&ftp_spc); if (x > 0) sendport = ftp_spc; return(x); case FTS_TYP: /* Type */ if ((x = cmkey(ftptyp,nftptyp,"","",xxstring)) < 0) return(x); if ((y = cmcfm()) < 0) return(y); ftp_typ = x; g_ftp_typ = x; tenex = (ftp_typ == FTT_TEN); return(1); case FTS_USN: /* Unique server names */ return(seton(&ftp_usn)); case FTS_VBM: /* Verbose mode */ if ((x = seton(&ftp_vbm)) < 0) /* Per-command copy */ return(x); ftp_vbx = ftp_vbm; /* Global sticky copy */ return(x); case FTS_TST: /* "if (testing)" messages */ return(seton(&testing)); case FTS_PRM: /* Send permissions */ return(setonaut(&ftp_prm)); case FTS_AUT: /* Auto-authentication */ return(seton(&ftp_aut)); case FTS_ERR: /* Error action */ if ((x = cmkey(qorp,2,"","",xxstring)) < 0) return(x); if ((y = cmcfm()) < 0) return(y); ftp_err = x; return(success = 1); #ifndef NOCSETS case FTS_XLA: /* Translation */ return(seton(&ftp_xla)); case FTS_CSR: /* Server charset */ if ((x = cmkey(fcstab,nfilc,"character-set","utf8",xxstring)) < 0) return(x); if ((y = cmcfm()) < 0) return(y); ftp_csr = x; ftp_xla = 1; /* Also enable translation */ return(success = 1); #endif /* NOCSETS */ case FTS_GFT: return(seton(&get_auto)); /* GET-filetype-switching */ case FTS_DAT: return(seton(&ftp_dates)); /* Set file dates */ #ifdef FTP_TIMEOUT case FTS_TMO: /* Timeout */ if ((x = cmnum("Number of seconds","0",10,&z,xxstring)) < 0) return(x); if ((y = cmcfm()) < 0) return(y); ftp_timeout = z; return(success = 1); #endif /* FTP_TIMEOUT */ case FTS_STO: { /* Server time offset */ char * s, * p = NULL; long k; if ((x = cmfld("[+-]hh[:mm[:ss]]","+0",&s,xxstring)) < 0) return(x); if (!strcmp(s,"+0")) { s = NULL; } else if ((x = delta2sec(s,&k)) < 0) { /* Check format */ printf("?Invalid time offset\n"); return(-9); } makestr(&p,s); /* Make a safe copy the string */ if ((x = cmcfm()) < 0) { /* Get confirmation */ if (p) makestr(&p,NULL); return(x); } fts_sto = p; /* Confirmed - set the string. */ return(success = 1); } case FTS_APW: { char * s; if ((x = cmtxt("Text", "", &s, xxstring)) < 0) return(x); makestr(&ftp_apw, *s ? s : NULL); return(success = 1); } case FTS_BUG: { if ((x = cmkey(ftpbugtab,nftpbug,"","",xxstring)) < 0) return(x); switch (x) { #ifdef CK_SSL case FTB_SV2: return seton(&ftp_bug_use_ssl_v2); #endif /* CK_SSL */ default: return(-2); } } #ifdef FTP_SECURITY case FTS_CRY: /* Auto-encryption */ return(seton(&ftp_cry)); case FTS_CFW: /* Credential-forwarding */ return(seton(&ftp_cfw)); case FTS_CPL: /* Command protection level */ if ((x = cmkey(ftppro,nftppro,"","",xxstring)) < 0) return(x); if ((y = cmcfm()) < 0) return(y); success = fts_cpl(x); return(success); case FTS_DPL: /* Data protection level */ if ((x = cmkey(ftppro,nftppro,"","",xxstring)) < 0) return(x); if ((y = cmcfm()) < 0) return(y); success = fts_dpl(x); return(success); case FTS_ATP: { /* FTP Auth Type */ int i, j, atypes[8]; for (i = 0; i < 8; i++) { if ((y = cmkey(ftpauth,nftpauth,"", (i == 0) ? "automatic" : "", xxstring)) < 0) { if (y == -3) break; return(y); } if (i > 0 && (y == FTA_AUTO)) { printf("?Choice may only be used in first position.\r\n"); return(-9); } for (j = 0; j < i; j++) { if (atypes[j] == y) { printf("\r\n?Choice has already been used.\r\n"); return(-9); } } atypes[i] = y; if (y == FTA_AUTO) { i++; break; } } if (i < 8) atypes[i] = 0; if ((z = cmcfm()) < 0) return(z); if (atypes[0] == FTA_AUTO) { i = 0; #ifdef FTP_GSSAPI ftp_auth_type[i++] = FTA_GK5; #endif /* FTP_GSSAPI */ #ifdef FTP_SRP ftp_auth_type[i++] = FTA_SRP; #endif /* FTP_SRP */ #ifdef FTP_KRB4 ftp_auth_type[i++] = FTA_K4; #endif /* FTP_KRB4 */ #ifdef CK_SSL ftp_auth_type[i++] = FTA_TLS; ftp_auth_type[i++] = FTA_SSL; #endif /* CK_SSL */ ftp_auth_type[i] = 0; } else { for (i = 0; i < 8; i++) ftp_auth_type[i] = atypes[i]; } return(success = 1); } case FTS_SRP: #ifdef FTP_SRP if ((x = cmkey(ftpsrp,nftpsrp,"","",xxstring)) < 0) return(x); switch (x) { case SRP_CIPHER: if ((x = cmkey(ciphertab,nciphertab,"","",xxstring)) < 0) return(x); if ((z = cmcfm()) < 0) return(z); success = !srp_selcipher(ciphertab[x].kwd); return(success); case SRP_HASH: if ((x = cmkey(hashtab,nhashtab,"","",xxstring)) < 0) return(x); if ((z = cmcfm()) < 0) return(z); success = !srp_selhash(hashtab[x].kwd); return(success = 1); default: if ((z = cmcfm()) < 0) return(z); return(-2); } #else /* FTP_SRP */ if ((z = cmcfm()) < 0) return(z); return(-2); #endif /* FTP_SRP */ #endif /* FTP_SECURITY */ case FTS_DIS: doxdis(2); /* 2 == ftp */ return(success = 1); default: return(-2); } } int ftpbye() { int x; if (!connected) return(1); if (testing) printf(" ftp closing %s...\n",ftp_host); x = ftpclose(); return((x > -1) ? 1 : 0); } /* o p e n f t p -- Parse FTP hostname & port and open */ static int openftp(s,opn_tls) char * s; int opn_tls; { char c, * p, * hostname = NULL, *hostsave = NULL, * service = NULL; int i, n, havehost = 0, getval = 0, rc = -9, opn_psv = -1, nologin = 0; int haveuser = 0; struct FDB sw, fl, cm; extern int nnetdir; /* Network services directory */ extern int nhcount; /* Lookup result */ extern char *nh_p[]; /* Network directory entry pointers */ extern char *nh_p2[]; /* Network directory entry nettype */ if (!s) return(-2); if (!*s) return(-2); makestr(&hostname,s); hostsave = hostname; makestr(&ftp_logname,NULL); anonymous = 0; noinit = 0; debug(F110,"ftp open",hostname,0); if (sav_psv > -1) { /* Restore prevailing active/passive */ ftp_psv = sav_psv; /* selection in case it was */ sav_psv = -1; /* temporarily overriden by a switch */ } if (sav_log > -1) { /* Ditto for autologin */ ftp_log = sav_log; sav_log = -1; } cmfdbi(&sw, /* Switches */ _CMKEY, "Service name or port;\n or switch", "", /* default */ "", /* addtl string data */ nftpswi, /* addtl numeric data 1: tbl size */ 4, /* addtl numeric data 2: none */ xxstring, /* Processing function */ ftpswitab, /* Keyword table */ &fl /* Pointer to next FDB */ ); cmfdbi(&fl, /* A host name or address */ _CMFLD, /* fcode */ "", /* help */ "xYzBoo", /* default */ "", /* addtl string data */ 0, /* addtl numeric data 1 */ 0, /* addtl numeric data 2 */ xxstring, NULL, &cm ); cmfdbi(&cm, /* Command confirmation */ _CMCFM, "", "", "", 0, 0, NULL, NULL, NULL ); for (n = 0;; n++) { rc = cmfdb(&sw); /* Parse a service name or a switch */ if (rc < 0) goto xopenftp; if (cmresult.fcode == _CMCFM) { /* Done? */ break; } else if (cmresult.fcode == _CMFLD) { /* Port */ if (ckstrcmp("xYzBoo",cmresult.sresult,-1,1)) makestr(&service,cmresult.sresult); else makestr(&service,opn_tls?"ftps":"ftp"); } else if (cmresult.fcode == _CMKEY) { /* Have a switch */ c = cmgbrk(); /* get break character */ getval = (c == ':' || c == '='); rc = -9; if (getval && !(cmresult.kflags & CM_ARG)) { printf("?This switch does not take arguments\n"); goto xopenftp; } if (!getval && (cmresult.kflags & CM_ARG)) { printf("?This switch requires an argument\n"); goto xopenftp; } switch (cmresult.nresult) { /* Switch */ case OPN_ANO: /* /ANONYMOUS */ anonymous++; nologin = 0; break; case OPN_NIN: /* /NOINIT */ noinit++; break; case OPN_NOL: /* /NOLOGIN */ nologin++; anonymous = 0; makestr(&ftp_logname,NULL); break; case OPN_PSW: /* /PASSWORD */ if (!anonymous) /* Don't log real passwords */ debok = 0; rc = cmfld("Password for FTP server","",&p,xxstring); if (rc == -3) { makestr(&ftp_tmp,NULL); } else if (rc < 0) { goto xopenftp; } else { makestr(&ftp_tmp,brstrip(p)); nologin = 0; } break; case OPN_USR: /* /USER */ rc = cmfld("Username for FTP server","",&p,xxstring); if (rc == -3) { makestr(&ftp_logname,NULL); } else if (rc < 0) { goto xopenftp; } else { nologin = 0; anonymous = 0; haveuser = 1; makestr(&ftp_logname,brstrip(p)); } break; case OPN_ACC: rc = cmfld("Account for FTP server","",&p,xxstring); if (rc == -3) { makestr(&ftp_acc,NULL); } else if (rc < 0) { goto xopenftp; } else { makestr(&ftp_acc,brstrip(p)); } break; case OPN_ACT: opn_psv = 0; break; case OPN_PSV: opn_psv = 1; break; case OPN_TLS: opn_tls = 1; break; default: break; } } if (n == 0) { /* After first time through */ cmfdbi(&sw, /* accept only switches */ _CMKEY, "\nCarriage return to confirm to command, or switch", "", "", nftpswi, 4, xxstring, ftpswitab, &cm ); } } #ifdef COMMENT debug(F100,"ftp openftp while exit","",0); rc = cmcfm(); debug(F101,"ftp openftp cmcfm rc","",rc); if (rc < 0) goto xopenftp; #endif /* COMMENT */ if (opn_psv > -1) { /* /PASSIVE or /ACTIVE switch given */ sav_psv = ftp_psv; ftp_psv = opn_psv; } if (nologin || haveuser) { /* /NOLOGIN or /USER switch given */ sav_log = ftp_log; ftp_log = haveuser ? 1 : 0; } if (*hostname == '=') { /* Bypass directory lookup */ hostname++; /* if hostname starts with '=' */ havehost++; } else if (isdigit(*hostname)) { /* or if it starts with a digit */ havehost++; } if (!service) makestr(&service,opn_tls?"ftps":"ftp"); #ifndef NODIAL if (!havehost && nnetdir > 0) { /* If there is a networks directory */ lunet(hostname); /* Look up the name */ debug(F111,"ftp openftp lunet",hostname,nhcount); if (nhcount == 0) { if (testing) printf(" ftp open trying \"%s %s\"...\n",hostname,service); success = ftpopen(hostname,service,opn_tls); debug(F101,"ftp openftp A ftpopen success","",success); rc = success; } else { int found = 0; for (i = 0; i < nhcount; i++) { if (nh_p2[i]) /* If network type specified */ if (ckstrcmp(nh_p2[i],"tcp/ip",strlen(nh_p2[i]),0)) continue; found++; makestr(&hostname,nh_p[i]); debug(F111,"ftpopen lunet substitution",hostname,i); if (testing) printf(" ftp open trying \"%s %s\"...\n",hostname,service); success = ftpopen(hostname,service,opn_tls); debug(F101,"ftp openftp B ftpopen success","",success); rc = success; if (success) break; } if (!found) { /* E.g. if no network types match */ if (testing) printf(" ftp open trying \"%s %s\"...\n",hostname,service); success = ftpopen(hostname,service,opn_tls); debug(F101,"ftp openftp C ftpopen success","",success); rc = success; } } } else { #endif /* NODIAL */ if (testing) printf(" ftp open trying \"%s %s\"...\n",hostname,service); success = ftpopen(hostname,service,opn_tls); debug(F111,"ftp openftp D ftpopen success",hostname,success); debug(F111,"ftp openftp D ftpopen connected",hostname,connected); rc = success; #ifndef NODIAL } #endif /* NODIAL */ xopenftp: debug(F101,"ftp openftp xopenftp rc","",rc); if (hostsave) free(hostsave); if (service) free(service); if (rc < 0 && ftp_logname) { free(ftp_logname); ftp_logname = NULL; } if (ftp_tmp) { free(ftp_tmp); ftp_tmp = NULL; } return(rc); } VOID /* 12 Aug 2007 */ doftpglobaltype(x) int x; { ftp_xfermode = XMODE_M; /* Set manual FTP transfer mode */ ftp_typ = x; /* Used by top-level BINARY and */ g_ftp_typ = x; /* ASCII commands. */ get_auto = 0; forcetype = 1; } int doftpacct() { int x; char * s; if ((x = cmtxt("Remote account", "", &s, xxstring)) < 0) return(x); CHECKCONN(); makestr(&ftp_acc,brstrip(s)); if (testing) printf(" ftp account: \"%s\"\n",ftp_acc); success = (ftpcmd("ACCT",ftp_acc,-1,-1,ftp_vbm) == REPLY_COMPLETE); return(success); } int doftpusr() { /* Log in as USER */ extern char uidbuf[]; extern char pwbuf[]; extern int pwflg, pwcrypt; int x; char *s, * acct = ""; debok = 0; /* Don't log */ if ((x = cmfld("Remote username or ID",uidbuf,&s,xxstring)) < 0) return(x); ckstrncpy(line,brstrip(s),LINBUFSIZ); /* brstrip: 15 Jan 2003 */ if ((x = cmfld("Remote password","",&s,xxstring)) < 0) { if (x == -3) { /* no input */ if ( pwbuf[0] && pwflg ) { ckstrncpy(tmpbuf,(char *)pwbuf,TMPBUFSIZ); #ifdef OS2 if ( pwcrypt ) ck_encrypt((char *)tmpbuf); #endif /* OS2 */ } } else { return(x); } } else { ckstrncpy(tmpbuf,brstrip(s),TMPBUFSIZ); } if ((x = cmtxt("Remote account\n or Enter or CR to confirm the command", "", &s, xxstring)) < 0) return(x); CHECKCONN(); if (*s) { x = strlen(tmpbuf); if (x > 0) { acct = &tmpbuf[x+2]; ckstrncpy(acct,brstrip(s),TMPBUFSIZ - x - 2); } } if (testing) printf(" ftp user \"%s\" password \"%s\"...\n",line,tmpbuf); success = ftp_user(line,tmpbuf,acct); #ifdef CKLOGDIAL dologftp(); #endif /* CKLOGDIAL */ return(success); } /* DO (various FTP commands)... */ int doftptyp(type) int type; { /* TYPE */ CHECKCONN(); ftp_typ = type; changetype(ftp_typ,ftp_vbm); debug(F101,"doftptyp changed type","",type); return(1); } static int doftpxmkd(s,vbm) char * s; int vbm; { /* MKDIR action */ int lcs = -1, rcs = -1; #ifndef NOCSETS if (ftp_xla) { lcs = ftp_csl; if (lcs < 0) lcs = fcharset; rcs = ftp_csx; if (rcs < 0) rcs = ftp_csr; } #endif /* NOCSETS */ debug(F110,"ftp doftpmkd",s,0); if (ftpcmd("MKD",s,lcs,rcs,vbm) == REPLY_COMPLETE) return(success = 1); if (ftpcode == 500 || ftpcode == 502) { if (!quiet) printf("MKD command not recognized, trying XMKD\n"); if (ftpcmd("XMKD",s,lcs,rcs,vbm) == REPLY_COMPLETE) return(success = 1); } return(success = 0); } static int doftpmkd() { /* MKDIR parse */ int x; char * s; if ((x = cmtxt("Remote directory name", "", &s, xxstring)) < 0) return(x); CHECKCONN(); ckstrncpy(line,s,LINBUFSIZ); if (testing) printf(" ftp mkdir \"%s\"...\n",line); return(success = doftpxmkd(line,-1)); } static int doftprmd() { /* RMDIR */ int x, lcs = -1, rcs = -1; char * s; if ((x = cmtxt("Remote directory", "", &s, xxstring)) < 0) return(x); CHECKCONN(); ckstrncpy(line,s,LINBUFSIZ); if (testing) printf(" ftp rmdir \"%s\"...\n",line); #ifndef NOCSETS if (ftp_xla) { lcs = ftp_csl; if (lcs < 0) lcs = fcharset; rcs = ftp_csx; if (rcs < 0) rcs = ftp_csr; } #endif /* NOCSETS */ if (ftpcmd("RMD",line,lcs,rcs,ftp_vbm) == REPLY_COMPLETE) return(success = 1); if (ftpcode == 500 || ftpcode == 502) { if (!quiet) printf("RMD command not recognized, trying XMKD\n"); success = (ftpcmd("XRMD",line,lcs,rcs,ftp_vbm) == REPLY_COMPLETE); } else success = 0; return(success); } static int doftpren() { /* RENAME */ int x; char * s; if ((x = cmfld("Remote filename","",&s,xxstring)) < 0) return(x); ckstrncpy(line,s,LINBUFSIZ); if ((x = cmfld("New name for remote file","",&s,xxstring)) < 0) return(x); ckstrncpy(tmpbuf,s,TMPBUFSIZ); if ((x = cmcfm()) < 0) return(x); CHECKCONN(); if (testing) printf(" ftp rename \"%s\" (to) \"%s\"...\n",line,tmpbuf); success = ftp_rename(line,tmpbuf); return(success); } int doftpres() { /* RESET (log out without close) */ int x; if ((x = cmcfm()) < 0) return(x); CHECKCONN(); if (testing) printf(" ftp reset...\n"); return(success = ftp_reset()); } static int doftpxhlp() { /* HELP */ int x; char * s; if ((x = cmtxt("Command name", "", &s, xxstring)) < 0) return(x); CHECKCONN(); ckstrncpy(line,s,LINBUFSIZ); if (testing) printf(" ftp help \"%s\"...\n",line); /* No need to translate -- all FTP commands are ASCII */ return(success = (ftpcmd("HELP",line,0,0,1) == REPLY_COMPLETE)); } static int doftpdir(cx) int cx; { /* [V]DIRECTORY */ int x, lcs = 0, rcs = 0, xlate = 0; char * p, * s, * m = ""; if (cx == FTP_VDI) { switch (servertype) { case SYS_VMS: case SYS_DOS: case SYS_TOPS10: case SYS_TOPS20: m = "*.*"; break; default: m = "*"; } } if ((x = cmtxt("Remote filespec",m,&s,xxstring)) < 0) return(x); if ((x = remtxt(&s)) < 0) return(x); #ifdef NOCSETS xlate = 0; #else xlate = ftp_xla; #endif /* NOCSETS */ line[0] = NUL; ckstrncpy(line,s,LINBUFSIZ); s = line; CHECKCONN(); #ifndef NOCSETS if (xlate) { /* SET FTP CHARACTER-SET-TRANSLATION */ lcs = ftp_csl; /* Local charset */ if (lcs < 0) lcs = fcharset; if (lcs < 0) xlate = 0; } if (xlate) { /* Still ON? */ rcs = ftp_csx; /* Remote (Server) charset */ if (rcs < 0) rcs = ftp_csr; if (rcs < 0) xlate = 0; } #endif /* NOCSETS */ if (testing) { p = s; if (!p) p = ""; if (*p) printf("Directory of files %s at %s:\n", line, ftp_host); else printf("Directory of files at %s:\n", ftp_host); } debug(F111,"doftpdir",s,cx); if (cx == FTP_DIR) { /* Translation of line[] is done inside recvrequest() */ /* when it calls ftpcmd(). */ return(success = (recvrequest("LIST","-",s,"wb",0,0,NULL,xlate,lcs,rcs) == 0)); } success = 1; /* VDIR - one file at a time... */ p = (char *)remote_files(1,(CHAR *)s,NULL,0); /* Get the file list */ cancelgroup = 0; if (!ftp_vbm && !quiet) printlines = 1; while (p && !cancelfile && !cancelgroup) { /* STAT one file */ if (ftpcmd("STAT",p,lcs,rcs,ftp_vbm) < 0) { success = 0; break; } p = (char *)remote_files(0,NULL,NULL,0); /* Get next file */ debug(F110,"ftp vdir file",s,0); } return(success); } static int doftppwd() { /* PWD */ int x, lcs = -1, rcs = -1; #ifndef NOCSETS if (ftp_xla) { lcs = ftp_csl; if (lcs < 0) lcs = fcharset; rcs = ftp_csx; if (rcs < 0) rcs = ftp_csr; } #endif /* NOCSETS */ if ((x = cmcfm()) < 0) return(x); CHECKCONN(); if (ftpcmd("PWD",NULL,lcs,rcs,1) == REPLY_COMPLETE) { success = 1; } else if (ftpcode == 500 || ftpcode == 502) { if (ftp_deb) printf("PWD command not recognized, trying XPWD\n"); success = (ftpcmd("XPWD",NULL,lcs,rcs,1) == REPLY_COMPLETE); } return(success); } static int doftpcwd(s,vbm) char * s; int vbm; { /* CD (CWD) */ int lcs = -1, rcs = -1; #ifndef NOCSETS if (ftp_xla) { lcs = ftp_csl; if (lcs < 0) lcs = fcharset; rcs = ftp_csx; if (rcs < 0) rcs = ftp_csr; } #endif /* NOCSETS */ debug(F110,"ftp doftpcwd",s,0); if (ftpcmd("CWD",s,lcs,rcs,vbm) == REPLY_COMPLETE) return(success = 1); if (ftpcode == 500 || ftpcode == 502) { if (!quiet) printf("CWD command not recognized, trying XCWD\n"); if (ftpcmd("XCWD",s,lcs,rcs,vbm) == REPLY_COMPLETE) return(success = 1); } return(success = 0); } static int doftpcdup() { /* CDUP */ debug(F100,"ftp doftpcdup","",0); if (ftpcmd("CDUP",NULL,0,0,1) == REPLY_COMPLETE) return(success = 1); if (ftpcode == 500 || ftpcode == 502) { if (!quiet) printf("CDUP command not recognized, trying XCUP\n"); if (ftpcmd("XCUP",NULL,0,0,1) == REPLY_COMPLETE) return(success = 1); } return(success = 0); } /* s y n c d i r -- Synchronizes client & server directories */ /* Call with: local = pointer to pathname of local file to be sent. sim = 1 for simulation, 0 for real uploading. Returns 0 on failure, 1 on success. The 'local' argument is relative to the initial directory of the MPUT, i.e. the root of the tree being uploaded. If the directory of the argument file is different from the directory of the previous file (which is stored in global putpath[]), this routine does the appropriate CWDs, CDUPs, and/or MKDIRs to position the FTP server in the same place. */ static int cdlevel = 0, cdsimlvl = 0; /* Tree-level trackers */ static int syncdir(local,sim) char * local; int sim; { char buf[CKMAXPATH+1]; char tmp[CKMAXPATH+1]; char msgbuf[CKMAXPATH+64]; char c, * p = local, * s = buf, * q = buf, * psep, * ssep; int i, k = 0, done = 0, itsadir = 0, saveq; debug(F110,"ftp syncdir local (new)",local,0); debug(F110,"ftp syncdir putpath (old)",putpath,0); itsadir = isdir(local); /* Is the local file a directory? */ saveq = quiet; while ((*s = *p)) { /* Copy the argument filename */ if (++k == CKMAXPATH) /* so we can poke it. */ return(-1); if (*s == '/') /* Pointer to rightmost dirsep */ q = s; s++; p++; } if (!itsadir) /* If it's a regular file */ *q = NUL; /* keep just the path part */ debug(F110,"ftp syncdir buf",buf,0); if (!strcmp(buf,putpath)) { /* Same path as previous file? */ if (itsadir) { /* This file is a directory? */ if (doftpcwd(local,0)) { /* Try to CD to it */ doftpcdup(); /* Worked - CD back up */ } else if (sim) { /* Simulating... */ if (fdispla == XYFD_B) { printf("WOULD CREATE DIRECTORY %s\n",local); } else if (fdispla) { ckmakmsg(msgbuf,CKMAXPATH, "WOULD CREATE DIRECTORY",local,NULL,NULL); ftscreen(SCR_ST,ST_MSG,(CK_OFF_T)0,msgbuf); } /* See note above */ return(0); } else if (!doftpxmkd(local,0)) { /* Can't CD - try to create */ return(0); } else { /* Remote directory created OK */ if (fdispla == XYFD_B) { printf("CREATED DIRECTORY %s\n",local); } else if (fdispla) { ckmakmsg(msgbuf,CKMAXPATH+64, "CREATED DIRECTORY ",local,NULL,NULL); ftscreen(SCR_ST,ST_MSG,(CK_OFF_T)0,msgbuf); } } } debug(F110,"ftp syncdir no change",buf,0); return(1); /* Yes, done. */ } ckstrncpy(tmp,buf,CKMAXPATH+1); /* Make a safe (pre-poked) copy */ debug(F110,"ftp syncdir new path",buf,0); /* for later (see end) */ p = buf; /* New */ s = putpath; /* Old */ debug(F110,"ftp syncdir A (old) s",s,0); /* Previous */ debug(F110,"ftp syncdir A (new) p",p,0); /* New */ psep = buf; ssep = putpath; while (*p != NUL && *s != NUL && *p == *s) { if (*p == '/') { psep = p+1; ssep = s+1; } p++,s++; } /* psep and ssep point to the first path segment that differs. We have to do as many CDUPs as there are path segments in ssep. then we have to do as many MKDs and CWDs as there are segments in psep. */ s = ssep; p = psep; debug(F110,"ftp syncdir B (old) s",s,0); /* Previous */ debug(F110,"ftp syncdir B (new) p",p,0); /* New */ /* p and s now point to the leftmost spot where the paths differ */ if (*s) { /* We have to back up */ k = 1; /* How many levels counting this one */ while ((c = *s++)) { /* Count dirseps remaining in prev */ if (c == '/' && *s) k++; } debug(F101,"ftp syncdir levels up","",k); for (i = 1; i <= k; i++) { /* Do that many CDUPs */ debug(F111,"ftp syncdir CDUP A",p,i); if (fdispla == XYFD_B) printf(" CDUP\n"); if (sim && cdsimlvl) { cdsimlvl--; } else { if (!doftpcdup()) { quiet = saveq; return(0); } } cdlevel--; } if (!*p) /* If we don't have to go down */ goto xcwd; /* we're done. */ } #ifdef COMMENT while (p > buf && *p && *p != '/') /* If in middle of segment */ p--; /* back up to beginning */ if (*p == '/') /* and terminate there */ p++; #endif /* COMMENT */ debug(F110,"ftp syncdir NEW PATH",p,0); s = p; /* Point to start of new down path. */ while (1) { /* Loop through characters. */ if (*s == '/' || !*s) { /* Have a segment. */ if (!*s) /* If end of string, */ done++; /* after this segment we're done. */ else *s = NUL; /* NUL out the separator. */ if (*p) { /* If segment is not empty */ debug(F110,"ftp syncdir down segment",p,0); if (!doftpcwd(p,0)) { /* Try to CD to it */ if (sim) { if (fdispla == XYFD_B) { printf(" WOULD CREATE DIRECTORY %s\n",local); } else if (fdispla) { ckmakmsg(msgbuf,CKMAXPATH, "WOULD CREATE DIRECTORY", local,NULL,NULL); ftscreen(SCR_ST,ST_MSG,(CK_OFF_T)0,msgbuf); } cdsimlvl++; } else { if (!doftpxmkd(p,0)) { /* Can't CD - try to create */ debug(F110,"ftp syncdir mkdir failed",p,0); /* Suppose we are executing SEND /RECURSIVE. Locally we have a directory FOO but the remote has a regular file with the same name. We can't CD to it, can't MKDIR it either. There's no way out but to fail and let the user handle the problem. */ quiet = saveq; return(0); } debug(F110,"ftp syncdir mkdir OK",p,0); if (fdispla == XYFD_B) { printf(" CREATED DIRECTORY %s\n",p); } else if (fdispla) { ckmakmsg(msgbuf,CKMAXPATH, "CREATED DIRECTORY ",p,NULL,NULL); ftscreen(SCR_ST,ST_MSG,(CK_OFF_T)0,msgbuf); } if (!doftpcwd(p,0)) { /* Try again to CD */ debug(F110,"ftp syncdir CD failed",p,0); quiet = saveq; return(0); } if (fdispla == XYFD_B) printf(" CWD %s\n",p); debug(F110,"ftp syncdir CD OK",p,0); } } cdlevel++; } if (done) /* Quit if no next segment */ break; p = s+1; /* Point to next segment */ } s++; /* Point to next source char */ } xcwd: ckstrncpy(putpath,tmp,CKMAXPATH+1); /* All OK - make this the new path */ quiet = saveq; return(1); } #ifdef DOUPDATE #ifdef DEBUG static VOID dbtime(s,xx) char * s; struct tm * xx; { /* Write struct tm to debug log */ if (deblog) { debug(F111,"ftp year ",s,xx->tm_year); debug(F111,"ftp month",s,xx->tm_mon); debug(F111,"ftp day ",s,xx->tm_mday); debug(F111,"ftp hour ",s,xx->tm_hour); debug(F111,"ftp min ",s,xx->tm_min); debug(F111,"ftp sec ",s,xx->tm_sec); } } #endif /* DEBUG */ /* t m c o m p a r e -- Compare two struct tm's */ /* Like strcmp() but for struct tm's */ /* Returns -1 if xx < yy, 0 if they are equal, 1 if xx > yy */ static int tmcompare(xx,yy) struct tm * xx, * yy; { if (xx->tm_year < yy->tm_year) /* First year less than second */ return(-1); if (xx->tm_year > yy->tm_year) /* First year greater than second */ return(1); /* Years are equal so compare months */ if (xx->tm_mon < yy->tm_mon) /* And so on... */ return(-1); if (xx->tm_mon > yy->tm_mon) return(1); if (xx->tm_mday < yy->tm_mday) return(-1); if (xx->tm_mday > yy->tm_mday) return(1); if (xx->tm_hour < yy->tm_hour) return(-1); if (xx->tm_hour > yy->tm_hour) return(1); if (xx->tm_min < yy->tm_min) return(-1); if (xx->tm_min > yy->tm_min) return(1); if (xx->tm_sec < yy->tm_sec) return(-1); if (xx->tm_sec > yy->tm_sec) return(1); return(0); } #endif /* DOUPDATE */ #ifndef HAVE_TIMEGM /* For platforms that do not have timegm() */ static CONST int MONTHDAYS[] = { /* Number of days in each month. */ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; /* Macro for whether a given year is a leap year. */ #define ISLEAP(year) \ (((year) % 4) == 0 && (((year) % 100) != 0 || ((year) % 400) == 0)) #endif /* HAVE_TIMEGM */ /* m k u t i m e -- Like mktime() but argument is already UTC */ static time_t #ifdef CK_ANSIC mkutime(struct tm * tm) #else mkutime(tm) struct tm * tm; #endif /* CK_ANSIC */ /* mkutime */ { #ifdef HAVE_TIMEGM return(timegm(tm)); /* Have system service, use it. */ #else /* Contributed by Russ Allbery (rra@stanford.edu), used by permission. Given a struct tm representing a calendar time in UTC, convert it to seconds since epoch. Returns (time_t) -1 if the time is not convertable. Note that this function does not canonicalize the provided struct tm, nor does it allow out-of-range values or years before 1970. Result should be identical with timegm(). */ time_t result = 0; int i; /* We do allow some ill-formed dates, but we don't do anything special with them and our callers really shouldn't pass them to us. Do explicitly disallow the ones that would cause invalid array accesses or other algorithm problems. */ #ifdef DEBUG if (deblog) { debug(F101,"mkutime tm_mon","",tm->tm_mon); debug(F101,"mkutime tm_year","",tm->tm_year); } #endif /* DEBUG */ if (tm->tm_mon < 0 || tm->tm_mon > 11 || tm->tm_year < 70) return((time_t) -1); /* Convert to time_t. */ for (i = 1970; i < tm->tm_year + 1900; i++) result += 365 + ISLEAP(i); for (i = 0; i < tm->tm_mon; i++) result += MONTHDAYS[i]; if (tm->tm_mon > 1 && ISLEAP(tm->tm_year + 1900)) result++; result = 24 * (result + tm->tm_mday - 1) + tm->tm_hour; result = 60 * result + tm->tm_min; result = 60 * result + tm->tm_sec; debug(F101,"mkutime result","",result); return(result); #endif /* HAVE_TIMEGM */ } /* s e t m o d t i m e -- Set file modification time. f = char * filename; t = time_t date/time to set (Secs since 19700101 0:00:00 UTC, NOT local) UNIX-specific; isolates mainline code from hideous #ifdefs. Returns: 0 on success, -1 on error. */ static int #ifdef CK_ANSIC setmodtime(char * f, time_t t) #else setmodtime(f,t) char * f; time_t t; #endif /* CK_ANSIC */ /* setmodtime */ { #ifdef NT struct _stat sb; #else /* NT */ struct stat sb; #endif /* NT */ int x, rc = 0; #ifdef BSD44 struct timeval tp[2]; #else /* def BSD44 */ #ifdef V7 struct utimbuf { time_t timep[2]; } tp; #else /* def V7 */ #ifdef SYSUTIMEH #ifdef NT struct _utimbuf tp; #else /* NT */ struct utimbuf tp; #endif /* NT */ #else /* def SYSUTIMEH */ #ifdef VMS struct utimbuf tp; #define SYSUTIMEH /* Our utimbuf matches this one. */ #else /* def VMS */ struct utimbuf { time_t atime; time_t mtime; } tp; #endif /* def VMS [else] */ #endif /* def SYSUTIMEH [else] */ #endif /* def V7 [else] */ #endif /* def BSD44 [else] */ if (stat(f,&sb) < 0) { debug(F111,"setmodtime stat failure",f,errno); return(-1); } #ifdef BSD44 tp[0].tv_sec = sb.st_atime; /* Access time first */ tp[1].tv_sec = t; /* Update time second */ debug(F111,"setmodtime BSD44",f,t); #else #ifdef V7 tp.timep[0] = t; /* Set modif. time to creation date */ tp.timep[1] = sb.st_atime; /* Don't change the access time */ debug(F111,"setmodtime V7",f,t); #else #ifdef SYSUTIMEH tp.modtime = t; /* Set modif. time to creation date */ tp.actime = sb.st_atime; /* Don't change the access time */ debug(F111,"setmodtime SYSUTIMEH",f,t); #else tp.mtime = t; /* Set modif. time to creation date */ tp.atime = sb.st_atime; /* Don't change the access time */ debug(F111,"setmodtime (other)",f,t); #endif /* SYSUTIMEH */ #endif /* V7 */ #endif /* BSD44 */ /* Try to set the file date */ #ifdef BSD44 x = utimes(f,tp); debug(F111,"setmodtime utimes()","BSD44",x); #else #ifdef IRIX65 { /* The following produces the nonsensical warning: Argument of type "const struct utimbuf *" is incompatible with parameter of type "const struct utimbuf *". If you can make it go away, be my guest. */ const struct utimbuf * t2 = &tp; x = utime(f,t2); } #else x = utime(f,&tp); debug(F111,"setmodtime utime()","other",x); #endif /* IRIX65 */ #endif /* BSD44 */ if (x) rc = -1; debug(F101,"setmodtime result","",rc); return(rc); } /* c h k m o d t i m e -- Check/Set file modification time. fc = function code: 0 = Check; returns: -1 on error, 0 if local older than remote, 1 if modtimes are equal, 2 if local newer than remote. 1 = Set (local file's modtime from remote's); returns: -1 on error, 0 on success. */ static int chkmodtime(local,remote,fc) char * local, * remote; int fc; { #ifdef NT struct _stat statbuf; #else /* NT */ struct stat statbuf; #endif /* NT */ struct tm * tmlocal = NULL; struct tm tmremote; int rc = 0, havedate = 0, lcs = -1, rcs = -1, flag = 0; char * s, timebuf[64]; debug(F111,"chkmodtime",local,mdtmok); if (!mdtmok) /* Server supports MDTM? */ return(-1); /* No don't bother. */ #ifndef NOCSETS if (ftp_xla) { lcs = ftp_csl; if (lcs < 0) lcs = fcharset; rcs = ftp_csx; if (rcs < 0) rcs = ftp_csr; } #endif /* NOCSETS */ if (fc == 0) { rc = stat(local,&statbuf); if (rc == 0) { /* Get local file's mod time */ /* Convert to struct tm */ tmlocal = gmtime((time_t *)&statbuf.st_mtime); #ifdef DEBUG if (tmlocal) { dbtime(local,tmlocal); } #endif /* DEBUG */ } } /* Get remote file's mod time as yyyymmddhhmmss */ if (havemdtm) { /* Already got it from MLSD? */ s = havemdtm; flag++; } else if (ftpcmd("MDTM",remote,lcs,rcs,0) == REPLY_COMPLETE) { char c; bzero((char *)&tmremote, sizeof(struct tm)); s = ftp_reply_str; while ((c = *s++)) { /* Skip past response code */ if (c == SP) { flag++; break; } } } if (flag) { debug(F111,"ftp chkmodtime string",s,flag); if (fts_sto) { /* User gave server time offset? */ char * p; debug(F110,"ftp chkmodtime offset",fts_sto,0); ckmakmsg(timebuf,64,s," ",fts_sto,NULL); /* Build delta time */ if ((p = cmcvtdate(timebuf,1))) { /* Apply delta time */ ckstrncpy(timebuf,p,64); /* Convert to MDTM format */ timebuf[8] = timebuf[9]; /* h */ timebuf[9] = timebuf[10]; /* h */ timebuf[10] = timebuf[12]; /* m */ timebuf[11] = timebuf[13]; /* m */ timebuf[12] = timebuf[12]; /* s */ timebuf[13] = timebuf[13]; /* s */ timebuf[14] = NUL; s = timebuf; debug(F110,"ftp chkmodtime adjust",s,0); } } if (flag) { /* Convert to struct tm */ char * pat; int y2kbug = 0; /* Seen in Kerberos 4 FTP servers */ if (!ckstrcmp(s,"191",3,0)) { pat = "%05d%02d%02d%02d%02d%02d"; y2kbug++; debug(F110,"ftp chkmodtime Y2K BUG detected",s,0); } else { pat = "%04d%02d%02d%02d%02d%02d"; } if (sscanf(s, /* Parse into struct tm */ pat, &(tmremote.tm_year), &(tmremote.tm_mon), &(tmremote.tm_mday), &(tmremote.tm_hour), &(tmremote.tm_min), &(tmremote.tm_sec) ) == 6) { tmremote.tm_year -= (y2kbug ? 19000 : 1900); debug(F101,"ftp chkmodtime year","",tmremote.tm_year); tmremote.tm_mon--; #ifdef DEBUG debug(F100,"SERVER TIME FOLLOWS:","",0); dbtime(remote,&tmremote); #endif /* DEBUG */ if (havedate > -1) havedate = 1; } } } else { /* Failed */ debug(F101,"ftp chkmodtime ftpcode","",ftpcode); if (ftpcode == 500 || /* Command unrecognized */ ftpcode == 502 || /* Command not implemented */ ftpcode == 202) /* Command superfluous */ mdtmok = 0; /* Don't ask this server again */ return(-1); } if (fc == 0) { /* Compare */ if (havedate == 1) { /* Only if we have both file dates */ /* Compare with local file's time. We don't use clock time (time_t) here in case of signed/unsigned confusion, etc. */ int xx; #ifdef COMMENT #ifdef DEBUG if (deblog) { dbtime("LOCAL",tmlocal); dbtime("REMOT",&tmremote); } #endif /* DEBUG */ #endif /* COMMENT */ xx = tmcompare(tmlocal,&tmremote); debug(F101,"chkmodtime tmcompare","",xx); return(xx + 1); } } else if (ftp_dates) { /* Set */ /* Here we must convert struct tm to time_t without applying timezone conversion, for which there is no portable API. The method is hidden in mkutime(), defined above. */ time_t utc; utc = mkutime(&tmremote); debug(F111,"ftp chkmodtime mkutime",remote,utc); if (utc != (time_t)-1) return(setmodtime(local,utc)); } return(-1); } /* getfile() returns: -1 on error, 0 if file received, 1 if file skipped */ static int getfile(remote,local,recover,append,pipename,xlate,fcs,rcs) char * local, * remote, * pipename; int recover, append, xlate, fcs, rcs; /* getfile */ { int rc = -1; ULONG t0, t1; #ifdef GFTIMER CKFLOAT sec; #else int sec = 0; #endif /* GFTIMER */ char fullname[CKMAXPATH+1]; debug(F110,"ftp getfile remote A",remote,0); debug(F110,"ftp getfile local A",local,0); debug(F110,"ftp getfile pipename",pipename,0); if (!remote) remote = ""; #ifdef PATTERNS /* Automatic type switching? */ if (ftp_xfermode == XMODE_A && patterns && get_auto && !forcetype) { int x; x = matchname(remote,0,servertype); debug(F111,"ftp getfile matchname",remote,x); switch (x) { case 0: ftp_typ = FTT_ASC; break; case 1: ftp_typ = tenex ? FTT_TEN : FTT_BIN; break; default: if (g_ftp_typ > -1) ftp_typ = g_ftp_typ; } changetype(ftp_typ,ftp_vbm); binary = ftp_typ; /* For file-transfer display */ } #endif /* PATTERNS */ #ifndef NOCSETS ftp_csx = -1; /* For file-transfer display */ ftp_csl = -1; /* ... */ if (rcs > -1) /* -1 means no translation */ if (ftp_typ == FTT_ASC) /* File type is "ascii"? */ if (fcs < 0) /* File charset not forced? */ fcs = fcharset; /* use prevailing FILE CHARACTER-SET */ if (fcs > -1 && rcs > -1) { /* Set up translation functions */ debug(F110,"ftp getfile","initxlate",0); initxlate(rcs,fcs); /* NB: opposite order of PUT */ ftp_csx = rcs; ftp_csl = fcs; } else xlate = 0; #endif /* NOCSETS */ if (!local) local = ""; if (!pipename && !*local) local = remote; out2screen = !strcmp(local,"-"); fullname[0] = NUL; if (pipename) { ckstrncpy(fullname,pipename,CKMAXPATH+1); } else { zfnqfp(local,CKMAXPATH,fullname); if (!fullname[0]) ckstrncpy(fullname,local,CKMAXPATH+1); } if (!out2screen && displa && fdispla) { /* Screen */ ftscreen(SCR_FN,'F',(CK_OFF_T)pktnum,remote); ftscreen(SCR_AN,0,(CK_OFF_T)0,fullname); ftscreen(SCR_FS,0,fsize,""); } tlog(F110,ftp_typ ? "ftp get BINARY:" : "ftp get TEXT:", remote, 0); tlog(F110," as",fullname,0); debug(F111,"ftp getfile size",remote,fsize); debug(F111,"ftp getfile local",local,out2screen); ckstrncpy(filnam, pipename ? remote : local, CKMAXPATH); t0 = gmstimer(); /* Start time */ debug(F111,"ftp getfile t0",remote,t0); /* ^^^ */ rc = recvrequest("RETR", local, remote, append ? "ab" : "wb", 0, recover, pipename, xlate, fcs, rcs ); t1 = gmstimer(); /* End time */ debug(F111,"ftp getfile t1",remote,t1); debug(F111,"ftp getfile sec",remote,(t1-t0)/1000); #ifdef GFTIMER sec = (CKFLOAT)((CKFLOAT)(t1 - t0) / 1000.0); /* Stats */ fpxfsecs = sec; /* (for doxlog()) */ #else sec = (t1 - t0) / 1000; xfsecs = (int)sec; #endif /* GFTIMER */ #ifdef FTP_TIMEOUT if (ftp_timed_out) rc = -4; #endif /* FTP_TIMEOUT */ debug(F111,"ftp recvrequest rc",remote,rc); if (cancelfile || cancelgroup) { debug(F111,"ftp get canceled",ckitoa(cancelfile),cancelgroup); ftscreen(SCR_ST,ST_INT,(CK_OFF_T)0,""); } else if (rc > 0) { debug(F111,"ftp get skipped",ckitoa(cancelfile),cancelgroup); ftscreen(SCR_ST,ST_SKIP,(CK_OFF_T)0,cmarg); } else if (rc < 0) { switch (ftpcode) { case -4: /* Network error */ case -2: /* File error */ ftscreen(SCR_ST,ST_MSG,(CK_OFF_T)0,ck_errstr()); break; case -3: ftscreen(SCR_ST,ST_MSG,(CK_OFF_T)0, "Failure to make data connection"); break; case -1: /* (should be covered above) */ ftscreen(SCR_ST,ST_INT,(CK_OFF_T)0,""); break; default: ftscreen(SCR_ST,ST_MSG,(CK_OFF_T)0,&ftp_reply_str[4]); } } else { /* Tudo bem */ ftscreen(SCR_PT,'Z',(CK_OFF_T)0,""); if (rc == 0) { ftscreen(SCR_ST,ST_OK,(CK_OFF_T)0,""); /* For screen */ makestr(&rrfspec,remote); /* For WHERE command */ makestr(&rfspec,fullname); } } if (rc > -1) { if (ftp_dates) /* If FTP DATES ON... */ if (!pipename && !out2screen) /* and it's a real file */ if (rc < 1 && rc != -3) /* and it wasn't skipped */ if (connected) /* and we still have a connection */ if (zchki(local) > -1) { /* and the file wasn't discarded */ chkmodtime(local,remote,1); /* set local file date */ debug(F110,"ftp get set date",local,0); } filcnt++; /* Used by \v(filenum) */ } #ifdef TLOG if (tralog) { if (rc > 0) { tlog(F100," recovery skipped","",0); } else if (rc == 0) { tlog(F101," complete, size", "", fsize); } else if (cancelfile) { tlog(F100," canceled by user","",0); #ifdef FTP_TIMEOUT } else if (ftp_timed_out) { tlog(F100," timed out","",0); #endif /* FTP_TIMEOUT */ } else { tlog(F110," failed:",ftp_reply_str,0); } if (!tlogfmt) doxlog(what,local,fsize,ftp_typ,rc,""); } #endif /* TLOG */ return(rc); } /* putfile() returns: -1 on error, >0 if file not selected, 0 on success. */ /* Positive return value is Skip Reason, SKP_xxx, from ckcker.h. */ static int putfile(cx, local,remote,force,moving,mvto,rnto,srvrn,x_cnv,x_usn,xft,prm,fcs,rcs,flg) char * local, * remote, * mvto, *rnto, *srvrn; int cx, force, moving, x_cnv, x_usn, xft, fcs, rcs, flg, prm; /* putfile */ { char asname[CKMAXPATH+1]; char fullname[CKMAXPATH+1]; int k = -1, x = 0, y = 0, o = -1, rc = 0, nc = 0; int xlate = 0, restart = 0, mt = -1; char * s = NULL, * cmd = NULL; ULONG t0 = 0, t1 = 0; /* Times for stats */ int ofcs = 0, orcs = 0; #ifdef GFTIMER CKFLOAT sec = 0.0; #else int sec = 0; #endif /* GFTIMER */ debug(F111,"ftp putfile flg",local,flg); debug(F110,"ftp putfile srv_renam",srvrn,0); debug(F101,"ftp putfile fcs","",fcs); debug(F101,"ftp putfile rcs","",rcs); ofcs = fcs; /* Save charset args */ orcs = rcs; sendstart = (CK_OFF_T)0; restart = flg & PUT_RES; if (!remote) remote = ""; /* FTP protocol command to send to server */ cmd = (cx == FTP_APP) ? "APPE" : (x_usn ? "STOU" : "STOR"); if (x_cnv == SET_AUTO) { /* Name conversion is auto */ if (alike) { /* If server & client are alike */ nc = 0; /* no conversion */ } else { /* If they are different */ if (servertype == SYS_UNIX || servertype == SYS_WIN32) nc = -1; /* only minimal conversions needed */ else /* otherwise */ nc = 1; /* full conversion */ } } else /* Not auto - do what user said */ nc = x_cnv; /* If Transfer Mode is Automatic, determine file type */ if (ftp_xfermode == XMODE_A && filepeek && !pipesend) { if (isdir(local)) { /* If it's a directory */ k = FT_BIN; /* skip the file scan */ } else { debug(F110,"FTP PUT calling scanfile",local,0); k = scanfile(local,&o,nscanfile); /* Scan the file */ } debug(F111,"FTP PUT scanfile",local,k); if (k > -1 && !forcetype) { ftp_typ = (k == FT_BIN) ? 1 : 0; if (xft > -1 && ftp_typ != xft) { if (flg & PUT_SIM) tlog(F110,"ftp put SKIP (Type):", local, 0); return(SKP_TYP); } if (ftp_typ == 1 && tenex) /* User said TENEX? */ ftp_typ = FTT_TEN; } } #ifndef NOCSETS ftp_csx = -1; /* For file-transfer display */ ftp_csl = -1; /* ... */ if (rcs > -1) { /* -1 means no translation */ if (ftp_typ == 0) { /* File type is "ascii"? */ if (fcs < 0) { /* File charset not forced? */ if (k < 0) { /* If we didn't scan */ fcs = fcharset; /* use prevailing FILE CHARACTER-SET */ } else { /* If we did scan, use scan result */ switch (k) { case FT_TEXT: /* Unknown text */ fcs = fcharset; break; case FT_7BIT: /* 7-bit text */ fcs = dcset7; break; case FT_8BIT: /* 8-bit text */ fcs = dcset8; break; case FT_UTF8: /* UTF-8 */ fcs = FC_UTF8; break; case FT_UCS2: /* UCS-2 */ fcs = FC_UCS2; if (o > -1) /* Input file byte order */ fileorder = o; break; default: rcs = -1; } } } } } if (fcs > -1 && rcs > -1) { /* Set up translation functions */ debug(F110,"ftp putfile","initxlate",0); initxlate(fcs,rcs); debug(F111,"ftp putfile rcs",fcsinfo[rcs].keyword,rcs); xlate = 1; ftp_csx = rcs; ftp_csl = fcs; } #endif /* NOCSETS */ binary = ftp_typ; /* For file-transfer display */ asname[0] = NUL; if (recursive) { /* If sending recursively, */ if (!syncdir(local,flg & PUT_SIM)) /* synchronize directories. */ return(-1); /* Don't PUT if it fails. */ else if (isdir(local)) /* It's a directory */ return(0); /* Don't send it! */ } if (*remote) { /* If an as-name template was given */ #ifndef NOSPL if (cmd_quoting) { /* and COMMAND QUOTING is ON */ y = CKMAXPATH; /* evaluate it for this file */ s = asname; zzstring(remote,&s,&y); } else #endif /* NOSPL */ ckstrncpy(asname,remote,CKMAXPATH); /* (or take it literally) */ } else { /* No as-name */ nzltor(local,asname,nc,0,CKMAXPATH); /* use local name strip path */ debug(F110,"FTP PUT nzltor",asname,0); } /* Preliminary messages and log entries */ fullname[0] = NUL; zfnqfp(local,CKMAXPATH,fullname); if (!fullname[0]) ckstrncpy(fullname,local,CKMAXPATH+1); fullname[CKMAXPATH] = NUL; if (displa && fdispla) { /* Screen */ ftscreen(SCR_FN,'F',(CK_OFF_T)pktnum,local); ftscreen(SCR_AN,0,(CK_OFF_T)0,asname); ftscreen(SCR_FS,0,fsize,""); } #ifdef DOUPDATE if (flg & (PUT_UPD|PUT_DIF)) { /* Date-checking modes... */ mt = chkmodtime(fullname,asname,0); debug(F111,"ftp putfile chkmodtime",asname,mt); if (mt == 0 && ((flg & PUT_DIF) == 0)) { /* Local is older */ tlog(F110,"ftp put /update SKIP (Older modtime): ",fullname,0); /* Skip this one */ ftscreen(SCR_ST,ST_SKIP,(CK_OFF_T)SKP_DAT,fullname); filcnt++; return(SKP_DAT); } else if (mt == 1) { /* Times are equal */ tlog(F110,"ftp put /update SKIP (Equal modtime): ",fullname,0); ftscreen(SCR_ST,ST_SKIP,(CK_OFF_T)SKP_EQU,fullname); /* Skip it */ filcnt++; return(SKP_DAT); } /* Local file is newer */ tlog(F110,ftp_typ ? "ftp put /update BINARY:" : "ftp put /update TEXT:", fullname, 0); } else if (flg & PUT_RES) { tlog(F110,ftp_typ ? "ftp put /recover BINARY:" : "ftp put /recover TEXT:", fullname, 0); } else { tlog(F110,ftp_typ ? "ftp put BINARY:" : "ftp put TEXT:", fullname, 0); } #else tlog(F110,ftp_typ ? "ftp put BINARY:" : "ftp put TEXT:", fullname, 0); #endif /* DOUPDATE */ tlog(F110," as",asname,0); #ifndef NOCSETS if (xlate) { debug(F111,"ftp putfile fcs",fcsinfo[fcs].keyword,fcs); tlog(F110," file character set:",fcsinfo[fcs].keyword,0); tlog(F110," server character set:",fcsinfo[rcs].keyword,0); } else if (!ftp_typ) { tlog(F110," character sets:","no conversion",0); fcs = ofcs; /* Binary file but we still must */ rcs = orcs; /* translate its name */ } #endif /* NOCSETS */ /* PUT THE FILE */ t0 = gmstimer(); /* Start time */ if (flg & PUT_SIM) { /* rc > 0 is a skip reason code */ if (flg & (PUT_UPD|PUT_DIF)) { /* (see SKP_xxx in ckcker.h) */ rc = (mt < 0) ? /* Update mode... */ SKP_XNX : /* Remote file doesn't exist */ SKP_XUP; /* Remote file is older */ } else { rc = SKP_SIM; /* "Would be sent", period. */ } } else { rc = sendrequest(cmd,local,asname,xlate,fcs,rcs,restart); } t1 = gmstimer(); /* End time */ filcnt++; /* File number */ #ifdef GFTIMER sec = (CKFLOAT)((CKFLOAT)(t1 - t0) / 1000.0); /* Stats */ fpxfsecs = sec; /* (for doxlog()) */ #else sec = (t1 - t0) / 1000; xfsecs = (int)sec; #endif /* GFTIMER */ debug(F111,"ftp sendrequest rc",local,rc); if (cancelfile || cancelgroup) { debug(F111,"ftp put canceled",ckitoa(cancelfile),cancelgroup); ftscreen(SCR_ST,ST_INT,(CK_OFF_T)0,""); } else if (rc > 0) { debug(F101,"ftp put skipped",local,rc); ftscreen(SCR_ST,ST_SKIP,(CK_OFF_T)rc,fullname); } else if (rc < 0) { debug(F111,"ftp put error",local,ftpcode); ftscreen(SCR_ST,ST_MSG,(CK_OFF_T)0,&ftp_reply_str[4]); } else { debug(F111,"ftp put not canceled",ckitoa(displa),fdispla); ftscreen(SCR_PT,'Z',(CK_OFF_T)0,""); debug(F111,"ftp put ST_OK",local,rc); ftscreen(SCR_ST,ST_OK,(CK_OFF_T)0,""); debug(F110,"ftp put old sfspec",sfspec,0); makestr(&sfspec,fullname); /* For WHERE command */ debug(F110,"ftp put new sfspec",sfspec,0); debug(F110,"ftp put old srfspec",srfspec,0); makestr(&srfspec,asname); debug(F110,"ftp put new srfspec",srfspec,0); } /* Final log entries */ #ifdef TLOG if (tralog) { if (rc > 0) { if (rc == SKP_XNX) tlog(F100," /simulate: WOULD BE SENT:","no remote file",0); else if (rc == SKP_XUP) tlog(F100," /simulate: WOULD BE SENT:","remote file older",0); else if (rc == SKP_SIM) tlog(F100," /simulate: WOULD BE SENT","",0); else tlog(F110," skipped:",gskreason(rc),0); } else if (rc == 0) { tlog(F101," complete, size", "", fsize); } else if (cancelfile) { tlog(F100," canceled by user","",0); } else { tlog(F110," failed:",ftp_reply_str,0); } if (!tlogfmt) doxlog(what,local,fsize,ftp_typ,rc,""); } #endif /* TLOG */ if (rc < 0) /* PUT did not succeed */ return(-1); /* so done. */ if (flg & PUT_SIM) /* Simulating, skip the rest. */ return(SKP_SIM); #ifdef UNIX /* Set permissions too? */ if (prm) { /* Change permissions? */ s = zgperm(local); /* Get perms of local file */ if (!s) s = ""; x = strlen(s); if (x > 3) s += (x - 3); if (rdigits(s)) { ckmakmsg(ftpcmdbuf,FTP_BUFSIZ,s," ",asname,NULL); x = ftpcmd("SITE CHMOD",ftpcmdbuf,fcs,rcs,ftp_vbm) == REPLY_COMPLETE; tlog(F110, x ? " chmod" : " chmod failed", s, 0 ); if (!x) return(-1); } } #endif /* UNIX */ /* Disposition of source file */ if (moving) { x = zdelet(local); tlog(F110, (x > -1) ? " deleted" : " failed to delete", local, 0 ); if (x < 0) return(-1); } else if (mvto) { x = zrename(local,mvto); tlog(F110, (x > -1) ? " moved source to" : " failed to move source to", mvto, 0 ); if (x < 0) return(-1); /* ftscreen(SCR_ST,ST_MSG,(CK_OFF_T)0,mvto); */ } else if (rnto) { char * s = rnto; #ifndef NOSPL int y; /* Pass it thru the evaluator */ extern int cmd_quoting; /* for \v(filename) */ if (cmd_quoting) { /* But only if cmd_quoting is on */ y = CKMAXPATH; s = (char *)asname; zzstring(rnto,&s,&y); s = (char *)asname; } #endif /* NOSPL */ if (s) if (*s) { int x; x = zrename(local,s); tlog(F110, (x > -1) ? " renamed source file to" : " failed to rename source file to", s, 0 ); if (x < 0) return(-1); /* ftscreen(SCR_ST,ST_MSG,(CK_OFF_T)0,s); */ } } /* Disposition of destination file */ if (srvrn) { /* /SERVER-RENAME: */ char * s = srvrn; #ifndef NOSPL int y; /* Pass it thru the evaluator */ extern int cmd_quoting; /* for \v(filename) */ debug(F111,"ftp putfile srvrn",s,1); if (cmd_quoting) { /* But only if cmd_quoting is on */ y = CKMAXPATH; s = (char *)fullname; /* We can recycle this buffer now */ zzstring(srvrn,&s,&y); s = (char *)fullname; } #endif /* NOSPL */ debug(F111,"ftp putfile srvrn",s,2); if (s) if (*s) { int x; x = ftp_rename(asname,s); debug(F111,"ftp putfile ftp_rename",asname,x); tlog(F110, (x > 0) ? " renamed destination file to" : " failed to rename destination file to", s, 0 ); if (x < 1) return(-1); } } return(0); } /* xxout must only be used for ASCII transfers */ static int #ifdef CK_ANSIC xxout(char c) #else xxout(c) char c; #endif /* CK_ANSIC */ { #ifndef OS2 #ifndef VMS #ifndef MAC #ifndef OSK /* For Unix, DG, Stratus, Amiga, Gemdos, other */ if (c == '\012') { if (zzout(dout,(CHAR)'\015') < 0) return(-1); ftpsnd.bytes++; } #else /* OSK */ if (c == '\015') { c = '\012'; if (zzout(dout,(CHAR)'\015') < 0) return(-1); ftpsnd.bytes++; } #endif /* OSK */ #else /* MAC */ if (c == '\015') { c = '\012'; if (zzout(dout,(CHAR)'\015') < 0) return(-1); ftpsnd.bytes++; } #endif /* MAC */ #endif /* VMS */ #endif /* OS2 */ if (zzout(dout,(CHAR)c) < 0) return(-1); ftpsnd.bytes++; return(0); } static int #ifdef CK_ANSIC scrnout(char c) #else scrnout(c) char c; #endif /* CK_ANSIC */ { return(putchar(c)); } static int #ifdef CK_ANSIC pipeout(char c) #else pipeout(c) char c; #endif /* CK_ANSIC */ { return(zmchout(c)); } static int ispathsep(c) int c; { switch (servertype) { case SYS_VMS: case SYS_TOPS10: case SYS_TOPS20: return(((c == ']') || (c == '>') || (c == ':')) ? 1 : 0); case SYS_OS2: case SYS_WIN32: case SYS_DOS: return(((c == '\\') || (c == '/') || (c == ':')) ? 1 : 0); case SYS_VOS: return((c == '>') ? 1 : 0); default: return((c == '/') ? 1 : 0); } } static int iscanceled() { #ifdef CK_CURSES extern int ck_repaint(); #endif /* CK_CURSES */ int x, rc = 0; char c = 0; if (cancelfile) return(1); x = conchk(); /* Any chars waiting at console? */ if (x-- > 0) { /* Yes... */ c = coninc(5); /* Get one */ switch (c) { case 032: /* Ctrl-X or X */ case 'z': case 'Z': cancelgroup++; /* fall thru on purpose */ case 030: /* Ctrl-Z or Z */ case 'x': case 'X': cancelfile++; rc++; break; #ifdef CK_CURSES case 'L': case 'l': case 014: /* Ctrl-L or L or Ctrl-W */ case 027: ck_repaint(); /* Refresh screen */ #endif /* CK_CURSES */ } } while (x-- > 0) /* Soak up any rest */ c = coninc(1); return(rc); } #ifdef FTP_TIMEOUT /* fc = 0 for read; 1 for write */ static int check_data_connection(fd,fc) int fd, fc; { int x; struct timeval tv; fd_set in, out, err; if (ftp_timeout < 1L) return(0); FD_ZERO(&in); FD_ZERO(&out); FD_ZERO(&err); FD_SET(fd,fc ? &out : &in); tv.tv_sec = ftp_timeout; /* Time limit */ tv.tv_usec = 0L; #ifdef INTSELECT x = select(FD_SETSIZE,(int *)&in,(int *)&out,(int *)&err,&tv); #else x = select(FD_SETSIZE,&in,&out,&err,&tv); #endif /* INTSELECT */ if (x == 0) { #ifdef EWOULDBLOCK errno = EWOULDBLOCK; #else #ifdef EAGAIN errno = EAGAIN; #else errno = 11; #endif /* EAGAIN */ #endif /* EWOULDBLOCK */ debug(F100,"ftp check_data_connection TIMOUT","",0); return(-3); } return(0); } #endif /* FTP_TIMEOUT */ /* zzsend - used by buffered output macros. */ static int #ifdef CK_ANSIC zzsend(int fd, CHAR c) #else zzsend(fd,c) int fd; CHAR c; #endif /* CK_ANSIC */ { int rc; debug(F101,"zzsend ucbufsiz","",ucbufsiz); debug(F101,"zzsend nout","",nout); debug(F111,"zzsend","secure?",ftpissecure()); if (iscanceled()) /* Check for cancellation */ return(-9); #ifdef FTP_TIMEOUT ftp_timed_out = 0; if (check_data_connection(fd,1) < 0) { ftp_timed_out = 1; return(-3); } #endif /* FTP_TIMEOUT */ rc = (!ftpissecure()) ? send(fd, (SENDARG2TYPE)ucbuf, nout, 0) : secure_putbuf(fd, ucbuf, nout); ucbuf[nout] = NUL; nout = 0; ucbuf[nout++] = c; spackets++; pktnum++; if (rc > -1 && fdispla != XYFD_B) { spktl = nout; ftscreen(SCR_PT,'D',(CK_OFF_T)spackets,NULL); } return(rc); } /* c m d l i n p u t -- Command-line PUT */ int cmdlinput(stay) int stay; { int x, rc = 0, done = 0, good = 0, status = 0; ULONG t0, t1; /* Times for stats */ #ifdef GFTIMER CKFLOAT sec; #else int sec = 0; #endif /* GFTIMER */ if (quiet) { /* -q really means quiet */ displa = 0; fdispla = 0; } else { displa = 1; fdispla = XYFD_B; } testing = 0; out2screen = 0; dpyactive = 0; what = W_FTP|W_SEND; #ifndef NOSPL cmd_quoting = 0; #endif /* NOSPL */ sndsrc = nfils; t0 = gmstimer(); /* Record starting time */ while (!done && !cancelgroup) { /* Loop for all files */ cancelfile = 0; x = gnfile(); /* Get next file from list(s) */ if (x == 0) /* (see gnfile() comments...) */ x = gnferror; switch (x) { case 1: /* File to send */ rc = putfile(FTP_PUT, /* Function (PUT, APPEND) */ filnam, /* Local file to send */ filnam, /* Remote name for file */ forcetype, /* Text/binary mode forced */ 0, /* Not moving */ NULL, /* No move-to */ NULL, /* No rename-to */ NULL, /* No server-rename */ ftp_cnv, /* Filename conversion */ 0, /* Unique-server-names */ -1, /* All file types */ 0, /* No permissions */ -1, /* No character sets */ -1, /* No character sets */ 0 /* No update or restart */ ); if (rc > -1) { good++; status = 1; } if (cancelfile) { continue; /* Or break? */ } if (rc < 0) { ftp_fai++; } continue; /* Or break? */ case 0: /* No more files, done */ done++; continue; case -2: case -1: printf("?%s: file not found - \"%s\"\n", puterror ? "Fatal" : "Warning", filnam ); continue; /* or break? */ case -3: printf("?Warning access denied - \"%s\"\n", filnam); continue; /* or break? */ case -5: printf("?Too many files match\n"); done++; break; case -6: if (good < 1) printf("?No files selected\n"); done++; break; default: printf("?getnextfile() - unknown failure\n"); done++; } } if (status > 0) { if (cancelgroup) status = 0; else if (cancelfile && good < 1) status = 0; } success = status; x = success; if (x > -1) { lastxfer = W_FTP|W_SEND; xferstat = success; } t1 = gmstimer(); /* End time */ #ifdef GFTIMER sec = (CKFLOAT)((CKFLOAT)(t1 - t0) / 1000.0); /* Stats */ if (!sec) sec = 0.001; fptsecs = sec; #else sec = (t1 - t0) / 1000; if (!sec) sec = 1; #endif /* GFTIMER */ tfcps = (long) (tfc / sec); tsecs = (int)sec; lastxfer = W_FTP|W_SEND; xferstat = success; if (dpyactive) ftscreen(status > 0 ? SCR_TC : SCR_CW, 0, (CK_OFF_T)0, ""); if (!stay) doexit(success ? GOOD_EXIT : BAD_EXIT, -1); return(success); } /* d o f t p p u t -- Parse and execute PUT, MPUT, and APPEND */ int #ifdef CK_ANSIC doftpput(int cx, int who) /* who == 1 for ftp, 0 for kermit */ #else doftpput(cx,who) int cx, who; #endif /* CK_ANSIC */ { struct FDB sf, fl, sw, cm; int n, rc, confirmed = 0, wild = 0, getval = 0, mput = 0, done = 0; int x_cnv = 0, x_usn = 0, x_prm = 0, putflags = 0, status = 0, good = 0; char * s, * s2; int x_csl, x_csr = -1; /* Local and remote charsets */ int x_xla = 0; int x_recurse = 0; char c, * p; /* Workers */ #ifdef PUTARRAY int range[2]; /* Array range */ char ** ap = NULL; /* Array pointer */ int arrayx = -1; /* Array index */ #endif /* PUTARRAY */ ULONG t0 = 0L, t1 = 0L; /* Times for stats */ #ifdef GFTIMER CKFLOAT sec; #else int sec = 0; #endif /* GFTIMER */ struct stringint pv[SND_MAX+1]; /* Temporary array for switch values */ success = 0; /* Assume failure */ forcetype = 0; /* No /TEXT or /BINARY given yet */ out2screen = 0; /* Not outputting file to screen */ putflags = 0; /* PUT options */ x_cnv = ftp_cnv; /* Filename conversion */ x_usn = ftp_usn; /* Unique server names */ x_prm = ftp_prm; /* Permissions */ if (x_prm == SET_AUTO) /* Permissions AUTO */ x_prm = alike; #ifndef NOCSETS x_csr = ftp_csr; /* Inherit global server charset */ x_csl = ftp_csl; if (x_csl < 0) x_csl = fcharset; x_xla = ftp_xla; #endif /* NOCSETS */ makestr(&filefile,NULL); /* No filename list file yet. */ makestr(&srv_renam,NULL); /* Clear /SERVER-RENAME: */ makestr(&snd_rename,NULL); /* PUT /RENAME */ makestr(&snd_move,NULL); /* PUT /MOVE */ putpath[0] = NUL; /* Initialize for syncdir(). */ puterror = ftp_err; /* Inherit global error action. */ what = W_SEND|W_FTP; /* What we're doing (sending w/FTP) */ asnambuf[0] = NUL; /* Clear as-name buffer */ if (g_ftp_typ > -1) { /* Restore TYPE if saved */ ftp_typ = g_ftp_typ; /* g_ftp_typ = -1; */ } for (i = 0; i <= SND_MAX; i++) { /* Initialize switch values */ pv[i].sval = NULL; /* to null pointers */ pv[i].ival = -1; /* and -1 int values */ pv[i].wval = (CK_OFF_T)-1; /* and -1 wide values */ } if (who == 0) { /* Called with unprefixed command */ switch (cx) { case XXRSEN: pv[SND_RES].ival = 1; break; case XXCSEN: pv[SND_CMD].ival = 1; break; case XXMOVE: pv[SND_DEL].ival = 1; break; case XXMMOVE: pv[SND_DEL].ival = 1; /* fall thru */ case XXMSE: mput++; break; } } else { if (cx == FTP_REP) pv[SND_RES].ival = 1; if (cx == FTP_MPU) mput++; } cmfdbi(&sw, /* First FDB - command switches */ _CMKEY, /* fcode */ "Filename, or switch", /* hlpmsg */ "", /* default */ "", /* addtl string data */ nputswi, /* addtl numeric data 1: tbl size */ 4, /* addtl numeric data 2: 4 = cmswi */ xxstring, /* Processing function */ putswi, /* Keyword table */ &sf /* Pointer to next FDB */ ); cmfdbi(&fl, /* 3rd FDB - local filespec */ _CMFLD, /* fcode */ "", /* hlpmsg */ "", /* default */ "", /* addtl string data */ 0, /* addtl numeric data 1 */ 0, /* addtl numeric data 2 */ xxstring, NULL, &cm ); cmfdbi(&cm, /* 4th FDB - Confirmation */ _CMCFM, /* fcode */ "", /* hlpmsg */ "", /* default */ "", /* addtl string data */ 0, /* addtl numeric data 1 */ 0, /* addtl numeric data 2 */ NULL, NULL, NULL ); again: cmfdbi(&sf, /* 2nd FDB - file to send */ _CMIFI, /* fcode */ "", /* hlpmsg */ "", /* default */ "", /* addtl string data */ /* 0 = parse files, 1 = parse files or dirs, 2 = skip symlinks */ nolinks | x_recurse, /* addtl numeric data 1 */ 0, /* dirflg 0 means "not dirs only" */ xxstring, NULL, #ifdef COMMENT mput ? &cm : &fl #else &fl #endif /* COMMENT */ ); while (1) { /* Parse zero or more switches */ x = cmfdb(&sw); /* Parse something */ debug(F101,"ftp put cmfdb A","",x); debug(F101,"ftp put fcode A","",cmresult.fcode); if (x < 0) /* Error */ goto xputx; /* or reparse needed */ if (cmresult.fcode != _CMKEY) /* Break out of loop if not a switch */ break; c = cmgbrk(); /* Get break character */ getval = (c == ':' || c == '='); /* to see how they ended the switch */ if (getval && !(cmresult.kflags & CM_ARG)) { printf("?This switch does not take arguments\n"); x = -9; goto xputx; } if (!getval && (cmgkwflgs() & CM_ARG)) { printf("?This switch requires an argument\n"); x = -9; goto xputx; } n = cmresult.nresult; /* Numeric result = switch value */ debug(F101,"ftp put switch","",n); switch (n) { /* Process the switch */ case SND_AFT: /* Send /AFTER:date-time */ case SND_BEF: /* Send /BEFORE:date-time */ case SND_NAF: /* Send /NOT-AFTER:date-time */ case SND_NBE: /* Send /NOT-BEFORE:date-time */ if (!getval) break; if ((x = cmdate("File date-time","",&s,0,xxstring)) < 0) { if (x == -3) { printf("?Date-time required\n"); x = -9; } goto xputx; } pv[n].ival = 1; makestr(&(pv[n].sval),s); break; case SND_ASN: /* /AS-NAME: */ debug(F101,"ftp put /as-name getval","",getval); if (!getval) break; if ((x = cmfld("Name to send under","",&s,NULL)) < 0) { if (x == -3) { printf("?name required\n"); x = -9; } goto xputx; } makestr(&(pv[n].sval),brstrip(s)); debug(F110,"ftp put /as-name 1",pv[n].sval,0); if (pv[n].sval) pv[n].ival = 1; break; #ifdef PUTARRAY case SND_ARR: /* /ARRAY */ if (!getval) break; ap = NULL; if ((x = cmfld("Array name (a single letter will do)", "", &s, NULL )) < 0) { if (x == -3) break; else return(x); } if ((x = arraybounds(s,&(range[0]),&(range[1]))) < 0) { printf("?Bad array: %s\n",s); return(-9); } if (!(ap = a_ptr[x])) { printf("?No such array: %s\n",s); return(-9); } pv[n].ival = 1; pv[SND_CMD].ival = 0; /* Undo any conflicting ones... */ pv[SND_RES].ival = 0; pv[SND_FIL].ival = 0; arrayx = x; break; #endif /* PUTARRAY */ case SND_BIN: /* /BINARY */ case SND_TXT: /* /TEXT or /ASCII */ case SND_TEN: /* /TENEX */ pv[SND_BIN].ival = 0; pv[SND_TXT].ival = 0; pv[SND_TEN].ival = 0; pv[n].ival = 1; break; #ifdef PUTPIPE case SND_CMD: /* These take no args */ if (nopush) { printf("?Sorry, system command access is disabled\n"); x = -9; goto xputx; } #ifdef PIPESEND else if (sndfilter) { printf("?Sorry, no PUT /COMMAND when SEND FILTER selected\n"); x = -9; goto xputx; } #endif /* PIPESEND */ sw.hlpmsg = "Command, or switch"; /* Change help message */ pv[n].ival = 1; /* Just set the flag */ pv[SND_ARR].ival = 0; break; #endif /* PUTPIPE */ #ifdef CKSYMLINK case SND_LNK: nolinks = 0; goto again; /* Because CMIFI params changed... */ case SND_NLK: nolinks = 2; goto again; #endif /* CKSYMLINK */ #ifdef FTP_RESTART case SND_RES: /* /RECOVER (resend) */ pv[SND_ARR].ival = 0; /* fall thru on purpose... */ #endif /* FTP_RESTART */ case SND_NOB: case SND_DEL: /* /DELETE */ case SND_SHH: /* /QUIET */ case SND_UPD: /* /UPDATE */ case SND_SIM: /* /UPDATE */ case SND_USN: /* /UNIQUE */ pv[n].ival = 1; /* Just set the flag */ break; case SND_REC: /* /RECURSIVE */ recursive = 2; /* Must be set before cmifi() */ x_recurse = 1; goto again; /* Because CMIFI params changed... */ break; #ifdef UNIXOROSK case SND_DOT: /* /DOTFILES */ matchdot = 1; break; case SND_NOD: /* /NODOTFILES */ matchdot = 0; break; #endif /* UNIXOROSK */ case SND_ERR: /* /ERROR-ACTION */ if ((x = cmkey(qorp,2,"","",xxstring)) < 0) goto xputx; pv[n].ival = x; break; case SND_EXC: /* Excludes */ if (!getval) break; if ((x = cmfld("Pattern","",&s,xxstring)) < 0) { if (x == -3) { printf("?Pattern required\n"); x = -9; } goto xputx; } if (s) if (!*s) s = NULL; makestr(&(pv[n].sval),s); if (pv[n].sval) pv[n].ival = 1; break; case SND_PRM: /* /PERMISSIONS */ if (!getval) x = 1; else if ((x = cmkey(onoff,2,"","on",xxstring)) < 0) goto xputx; pv[SND_PRM].ival = x; break; #ifdef PIPESEND case SND_FLT: /* /FILTER */ debug(F101,"ftp put /filter getval","",getval); if (!getval) break; if ((x = cmfld("Filter program to send through","",&s,NULL)) < 0) { if (x == -3) s = ""; else goto xputx; } if (*s) s = brstrip(s); y = strlen(s); for (x = 0; x < y; x++) { /* Make sure they included "\v(...)" */ if (s[x] != '\\') continue; if (s[x+1] == 'v') break; } if (x == y) { printf( "?Filter must contain a replacement variable for filename.\n" ); x = -9; goto xputx; } if (s) if (!*s) s = NULL; makestr(&(pv[n].sval),s); if (pv[n].sval) pv[n].ival = 1; break; #endif /* PIPESEND */ case SND_NAM: /* /FILENAMES */ if (!getval) break; if ((x = cmkey(fntab,nfntab,"","automatic",xxstring)) < 0) goto xputx; debug(F101,"ftp put /filenames","",x); pv[n].ival = x; break; case SND_SMA: /* Smaller / larger than */ case SND_LAR: { CK_OFF_T y; if (!getval) break; if ((x = cmnumw("Size in bytes","0",10,&y,xxstring)) < 0) goto xputx; pv[n].wval = y; break; } case SND_FIL: /* Name of file containing filenames */ if (!getval) break; if ((x = cmifi("Name of file containing list of filenames", "",&s,&y,xxstring)) < 0) { if (x == -3) { printf("?Filename required\n"); x = -9; } goto xputx; } else if (y && iswild(s)) { printf("?Wildcards not allowed\n"); x = -9; goto xputx; } if (s) if (!*s) s = NULL; makestr(&(pv[n].sval),s); if (pv[n].sval) { pv[n].ival = 1; pv[SND_ARR].ival = 0; } else { pv[n].ival = 0; } mput = 0; break; case SND_MOV: /* MOVE after */ case SND_REN: /* RENAME after */ case SND_SRN: { /* SERVER-RENAME after */ char * m = ""; switch (n) { case SND_MOV: m = "device and/or directory for source file after sending"; break; case SND_REN: m = "new name for source file after sending"; break; case SND_SRN: m = "new name for destination file after sending"; break; } if (!getval) break; if ((x = cmfld(m, "", &s, n == SND_MOV ? xxstring : NULL)) < 0) { if (x == -3) { printf("%s\n", n == SND_MOV ? "?Destination required" : "?New name required" ); x = -9; } goto xputx; } if (s) if (!*s) s = NULL; makestr(&(pv[n].sval),s ? brstrip(s) : NULL); pv[n].ival = (pv[n].sval) ? 1 : 0; break; } case SND_STA: /* Starting position (= PSEND) */ if (!getval) break; if ((x = cmnum("0-based position","0",10,&y,xxstring)) < 0) goto xputx; pv[n].ival = y; break; case SND_TYP: /* /TYPE */ if (!getval) break; if ((x = cmkey(txtbin,3,"","all",xxstring)) < 0) goto xputx; pv[n].ival = (x == 2) ? -1 : x; break; #ifndef NOCSETS case SND_CSL: /* Local character set */ case SND_CSR: /* Remote (server) charset */ if ((x = cmkey(fcstab,nfilc,"","",xxstring)) < 0) { return((x == -3) ? -2 : x); } if (n == SND_CSL) x_csl = x; else x_csr = x; x_xla = 1; /* Overrides global OFF setting */ break; case SND_XPA: /* Transparent */ x_xla = 0; x_csr = -1; x_csl = -1; break; #endif /* NOCSETS */ } } #ifdef PIPESEND if (pv[SND_RES].ival > 0) { /* /RECOVER */ if (sndfilter || pv[SND_FLT].ival > 0) { printf("?Sorry, no /RECOVER or /START if SEND FILTER selected\n"); x = -9; goto xputx; } if (sfttab[0] > 0 && sfttab[SFT_REST] == 0) printf("WARNING: Server says it doesn't support REST.\n"); } #endif /* PIPESEND */ cmarg = ""; cmarg2 = asnambuf; line[0] = NUL; s = line; wild = 0; switch (cmresult.fcode) { /* How did we get out of switch loop */ case _CMIFI: /* Input filename */ if (pv[SND_FIL].ival > 0) { printf("?You may not give a PUT filespec and a /LISTFILE\n"); x = -9; goto xputx; } ckstrncpy(line,cmresult.sresult,LINBUFSIZ); /* Name */ if (pv[SND_ARR].ival > 0) ckstrncpy(asnambuf,line,CKMAXPATH); else wild = cmresult.nresult; /* Wild flag */ debug(F111,"ftp put wild",line,wild); if (!wild && !recursive && !mput) nolinks = 0; break; case _CMFLD: /* Field */ /* Only allowed with /COMMAND and /ARRAY */ if (pv[SND_FIL].ival > 0) { printf("?You may not give a PUT filespec and a /LISTFILE\n"); x = -9; goto xputx; } /* For MPUT it's OK to have filespecs that don't match any files */ if (mput) break; if (pv[SND_CMD].ival < 1 && pv[SND_ARR].ival < 1) { #ifdef CKROOT if (ckrooterr) printf("?Off limits: %s\n",cmresult.sresult); else #endif /* CKROOT */ printf("?%s - \"%s\"\n", iswild(cmresult.sresult) ? "No files match" : "File not found", cmresult.sresult ); x = -9; goto xputx; } ckstrncpy(line,cmresult.sresult,LINBUFSIZ); if (pv[SND_ARR].ival > 0) ckstrncpy(asnambuf,line,CKMAXPATH); break; case _CMCFM: /* Confirmation */ confirmed = 1; break; default: printf("?Unexpected function code: %d\n",cmresult.fcode); x = -9; goto xputx; } debug(F110,"ftp put string",s,0); debug(F101,"ftp put confirmed","",confirmed); /* Save and change protocol and transfer mode */ /* Global values are restored in main parse loop */ g_displa = fdispla; if (ftp_dis > -1) fdispla = ftp_dis; g_skipbup = skipbup; if (pv[SND_NOB].ival > -1) { /* /NOBACKUP (skip backup file) */ g_skipbup = skipbup; skipbup = 1; } if (pv[SND_TYP].ival > -1) { /* /TYPE */ xfiletype = pv[SND_TYP].ival; if (xfiletype == 2) xfiletype = -1; } if (pv[SND_BIN].ival > 0) { /* /BINARY really means binary... */ forcetype = 1; /* So skip file scan */ ftp_typ = FTT_BIN; /* Set binary */ } else if (pv[SND_TXT].ival > 0) { /* Similarly for /TEXT... */ forcetype = 1; ftp_typ = FTT_ASC; } else if (pv[SND_TEN].ival > 0) { /* and /TENEX*/ forcetype = 1; ftp_typ = FTT_TEN; } else if (ftp_cmdlin && ftp_xfermode == XMODE_M) { forcetype = 1; ftp_typ = binary; g_ftp_typ = binary; } #ifdef PIPESEND if (pv[SND_CMD].ival > 0) { /* /COMMAND - strip any braces */ debug(F110,"PUT /COMMAND before stripping",s,0); s = brstrip(s); debug(F110,"PUT /COMMAND after stripping",s,0); if (!*s) { printf("?Sorry, a command to send from is required\n"); x = -9; goto xputx; } cmarg = s; } #endif /* PIPESEND */ /* Set up /MOVE and /RENAME */ if (pv[SND_DEL].ival > 0 && (pv[SND_MOV].ival > 0 || pv[SND_REN].ival > 0)) { printf("?Sorry, /DELETE conflicts with /MOVE or /RENAME\n"); x = -9; goto xputx; } #ifdef CK_TMPDIR if (pv[SND_MOV].ival > 0) { int len; char * p = pv[SND_MOV].sval; len = strlen(p); if (!isdir(p)) { /* Check directory */ #ifdef CK_MKDIR char * s = NULL; s = (char *)malloc(len + 4); if (s) { strcpy(s,p); /* safe */ #ifdef datageneral if (s[len-1] != ':') { s[len++] = ':'; s[len] = NUL; } #else if (s[len-1] != '/') { s[len++] = '/'; s[len] = NUL; } #endif /* datageneral */ s[len++] = 'X'; s[len] = NUL; #ifdef NOMKDIR x = -1; #else x = zmkdir(s); #endif /* NOMKDIR */ free(s); if (x < 0) { printf("?Can't create \"%s\"\n",p); x = -9; goto xputx; } } #else printf("?Directory \"%s\" not found\n",p); x = -9; goto xputx; #endif /* CK_MKDIR */ } makestr(&snd_move,p); } #endif /* CK_TMPDIR */ if (pv[SND_REN].ival > 0) { /* /RENAME */ char * p = pv[SND_REN].sval; if (!p) p = ""; if (!*p) { printf("?New name required for /RENAME\n"); x = -9; goto xputx; } p = brstrip(p); #ifndef NOSPL /* If name given is wild, rename string must contain variables */ if (wild) { char * s = tmpbuf; x = TMPBUFSIZ; zzstring(p,&s,&x); if (!strcmp(tmpbuf,p)) { printf( "?/RENAME for file group must contain variables such as \\v(filename)\n" ); x = -9; goto xputx; } } #endif /* NOSPL */ makestr(&snd_rename,p); debug(F110,"FTP snd_rename",snd_rename,0); } if (pv[SND_SRN].ival > 0) { /* /SERVER-RENAME */ char * p = pv[SND_SRN].sval; if (!p) p = ""; if (!*p) { printf("?New name required for /SERVER-RENAME\n"); x = -9; goto xputx; } p = brstrip(p); #ifndef NOSPL if (wild) { char * s = tmpbuf; x = TMPBUFSIZ; zzstring(p,&s,&x); if (!strcmp(tmpbuf,p)) { printf( "?/SERVER-RENAME for file group must contain variables such as \\v(filename)\n" ); x = -9; goto xputx; } } #endif /* NOSPL */ makestr(&srv_renam,p); debug(F110,"ftp put srv_renam",srv_renam,0); } if (!confirmed) { /* CR not typed yet, get more fields */ char * lp; if (mput) { /* MPUT or MMOVE */ nfils = 0; /* We already have the first one */ #ifndef NOMSEND if (cmresult.fcode == _CMIFI) { /* First filespec is valid */ msfiles[nfils++] = line; /* Store pointer */ lp = line + (int)strlen(line) + 1; /* Point past it */ debug(F111,"ftp put mput",msfiles[nfils-1],nfils-1); } else { /* First filespec matches no files */ debug(F110,"ftp put mput skipping first filespec", cmresult.sresult, 0 ); lp = line; } /* Parse a filespec, a "field", or confirmation */ cmfdbi(&sf, /* 1st FDB - file to send */ _CMIFI, /* fcode */ "", /* hlpmsg */ "", /* default */ "", /* addtl string data */ nolinks | x_recurse, /* addtl numeric data 1 */ 0, /* dirflg 0 means "not dirs only" */ xxstring, NULL, &fl ); cmfdbi(&fl, /* 2nd FDB - local filespec */ _CMFLD, /* fcode */ "", /* hlpmsg */ "", /* default */ "", /* addtl string data */ 0, /* addtl numeric data 1 */ 0, /* addtl numeric data 2 */ xxstring, NULL, &cm ); cmfdbi(&cm, /* 3rd FDB - Confirmation */ _CMCFM, /* fcode */ "", "", "", 0, 0, NULL, NULL, NULL ); while (!confirmed) { /* Get more filenames */ x = cmfdb(&sf); /* Parse something */ debug(F101,"ftp put cmfdb B","",x); debug(F101,"ftp put fcode B","",cmresult.fcode); if (x < 0) /* Error */ goto xputx; /* or reparse needed */ switch (cmresult.fcode) { case _CMCFM: /* End of command */ confirmed++; if (nfils < 1) { debug(F100,"ftp put mput no files match","",0); printf("?No files match MPUT list\n"); x = -9; goto xputx; } break; case _CMFLD: /* No match */ debug(F110,"ftp put mput skipping",cmresult.sresult,0); continue; case _CMIFI: /* Good match */ s = cmresult.sresult; msfiles[nfils++] = lp; /* Got one, count, point to it, */ p = lp; /* remember pointer, */ while ((*lp++ = *s++)) /* and copy it into buffer */ if (lp > (line + LINBUFSIZ)) { /* Avoid memory leak */ printf("?MPUT list too long\n"); line[0] = NUL; x = -9; goto xputx; } debug(F111,"ftp put mput adding",msfiles[nfils-1],nfils-1); if (nfils == 1) /* Take care of \v(filespec) */ fspec[0] = NUL; #ifdef ZFNQFP zfnqfp(p,TMPBUFSIZ,tmpbuf); p = tmpbuf; #endif /* ZFNQFP */ if (((int)strlen(fspec) + (int)strlen(p) + 1) < fspeclen) { strcat(fspec,p); /* safe */ strcat(fspec," "); /* safe */ } else { #ifdef COMMENT printf("WARNING - \\v(filespec) buffer overflow\n"); #else debug(F101,"doxput filespec buffer overflow","",0); #endif /* COMMENT */ } } } #endif /* NOMSEND */ } else { /* Regular PUT */ nfils = -1; if ((x = cmtxt(wild ? "\nOptional as-name template containing replacement variables \ like \\v(filename)" : "Optional name to send it with", "",&p,NULL)) < 0) goto xputx; if (p) if (!*p) p = NULL; p = brstrip(p); if (p && *p) { makestr(&(pv[SND_ASN].sval),p); if (pv[SND_ASN].sval) pv[SND_ASN].ival = 1; debug(F110,"ftp put /as-name 2",pv[SND_ASN].sval,0); } } } /* Set cmarg2 from as-name, however we got it. */ CHECKCONN(); if (pv[SND_ASN].ival > 0 && pv[SND_ASN].sval && !asnambuf[0]) { char * p; p = brstrip(pv[SND_ASN].sval); ckstrncpy(asnambuf,p,CKMAXPATH+1); } debug(F110,"ftp put asnambuf",asnambuf,0); if (pv[SND_FIL].ival > 0) { if (confirmed) { if (zopeni(ZMFILE,pv[SND_FIL].sval) < 1) { debug(F110,"ftp put can't open",pv[SND_FIL].sval,0); printf("?Failure to open %s\n",pv[SND_FIL].sval); x = -9; goto xputx; } makestr(&filefile,pv[SND_FIL].sval); /* Open, remember name */ debug(F110,"ftp PUT /LISTFILE opened",filefile,0); wild = 1; } } if (confirmed && !line[0] && !filefile) { #ifndef NOMSEND if (filehead) { /* OK if we have a SEND-LIST */ nfils = filesinlist; sndsrc = nfils; /* Like MSEND */ addlist = 1; /* But using a different list... */ filenext = filehead; goto doput; } #endif /* NOMSEND */ printf("?Filename required but not given\n"); x = -9; goto xputx; } #ifndef NOMSEND addlist = 0; /* Don't use SEND-LIST. */ #endif /* NOMSEND */ if (mput) { /* MPUT (rather than PUT) */ #ifndef NOMSEND cmlist = msfiles; /* List of filespecs */ sndsrc = nfils; /* rather filespec and as-name */ #endif /* NOMSEND */ pipesend = 0; } else if (filefile) { /* File contains list of filenames */ s = ""; cmarg = ""; line[0] = NUL; nfils = 1; sndsrc = 1; } else if (pv[SND_ARR].ival < 1 && pv[SND_CMD].ival < 1) { /* Not MSEND, MMOVE, /LIST, or /ARRAY */ nfils = sndsrc = -1; if (!wild) { if (zchki(s) < 0) { printf("?Read access denied - \"%s\"\n", s); x = -9; goto xputx; } } if (s != line) /* We might already have done this. */ ckstrncpy(line,s,LINBUFSIZ); /* Copy of string just parsed. */ #ifdef DEBUG else debug(F110,"doxput line=s",line,0); #endif /* DEBUG */ cmarg = line; /* File to send */ } #ifndef NOMSEND zfnqfp(cmarg,fspeclen,fspec); /* Get full name */ #endif /* NOMSEND */ if (!mput) { /* For all but MPUT... */ #ifdef PIPESEND if (pv[SND_CMD].ival > 0) /* /COMMAND sets pipesend flag */ pipesend = 1; debug(F101,"ftp put /COMMAND pipesend","",pipesend); if (pipesend && filefile) { printf("?Invalid switch combination\n"); x = -9; goto xputx; } #endif /* PIPESEND */ #ifndef NOSPL /* If as-name given and filespec is wild, as-name must contain variables */ if ((wild || mput) && asnambuf[0]) { char * s = tmpbuf; x = TMPBUFSIZ; zzstring(asnambuf,&s,&x); if (!strcmp(tmpbuf,asnambuf)) { printf( "?As-name for file group must contain variables such as \\v(filename)\n" ); x = -9; goto xputx; } } #endif /* NOSPL */ } doput: if (pv[SND_SHH].ival > 0) { /* SEND /QUIET... */ fdispla = 0; debug(F101,"ftp put display","",fdispla); } else { displa = 1; if (ftp_deb) fdispla = XYFD_B; } #ifdef PUTARRAY /* SEND /ARRAY... */ if (pv[SND_ARR].ival > 0) { if (!ap) { x = -2; goto xputx; } /* (shouldn't happen) */ if (range[0] == -1) /* If low end of range not specified */ range[0] = 1; /* default to 1 */ if (range[1] == -1) /* If high not specified */ range[1] = a_dim[arrayx]; /* default to size of array */ if ((range[0] < 0) || /* Check range */ (range[0] > a_dim[arrayx]) || (range[1] < range[0]) || (range[1] > a_dim[arrayx])) { printf("?Bad array range - [%d:%d]\n",range[0],range[1]); x = -9; goto xputx; } sndarray = ap; /* Array pointer */ sndxin = arrayx; /* Array index */ sndxlo = range[0]; /* Array range */ sndxhi = range[1]; sndxnam[7] = (char)((sndxin == 1) ? 64 : sndxin + ARRAYBASE); if (!asnambuf[0]) ckstrncpy(asnambuf,sndxnam,CKMAXPATH); cmarg = ""; } #endif /* PUTARRAY */ moving = 0; if (pv[SND_ARR].ival < 1) { /* File selection & disposition... */ if (pv[SND_DEL].ival > 0) /* /DELETE was specified */ moving = 1; if (pv[SND_AFT].ival > 0) /* Copy SEND criteria */ ckstrncpy(sndafter,pv[SND_AFT].sval,19); if (pv[SND_BEF].ival > 0) ckstrncpy(sndbefore,pv[SND_BEF].sval,19); if (pv[SND_NAF].ival > 0) ckstrncpy(sndnafter,pv[SND_NAF].sval,19); if (pv[SND_NBE].ival > 0) ckstrncpy(sndnbefore,pv[SND_NBE].sval,19); if (pv[SND_EXC].ival > 0) makelist(pv[SND_EXC].sval,sndexcept,NSNDEXCEPT); if (pv[SND_SMA].ival > -1) sndsmaller = pv[SND_SMA].wval; if (pv[SND_LAR].ival > -1) sndlarger = pv[SND_LAR].wval; if (pv[SND_NAM].ival > -1) x_cnv = pv[SND_NAM].ival; if (pv[SND_USN].ival > -1) x_usn = pv[SND_USN].ival; if (pv[SND_ERR].ival > -1) puterror = pv[SND_ERR].ival; #ifdef DOUPDATE if (pv[SND_UPD].ival > 0) { if (x_usn) { printf("?Conflicting switches: /UPDATE /UNIQUE\n"); x = -9; goto xputx; } putflags |= PUT_UPD; ftp_dates |= 2; } #ifdef COMMENT /* This works but it's useless, maybe dangerous */ if (pv[SND_DIF].ival > 0) { if (x_usn) { printf("?Conflicting switches: /DATES-DIFFER /UNIQUE\n"); x = -9; goto xputx; } putflags |= PUT_DIF; ftp_dates |= 2; } #endif /* COMMENT */ #endif /* DOUPDATE */ if (pv[SND_SIM].ival > 0) putflags |= PUT_SIM; if (pv[SND_PRM].ival > -1) { #ifdef UNIX if (x_usn) { printf("?Conflicting switches: /PERMISSIONS /UNIQUE\n"); x = -9; goto xputx; } x_prm = pv[SND_PRM].ival; #else /* UNIX */ printf("?/PERMISSIONS switch is not supported\n"); #endif /* UNIX */ } #ifdef FTP_RESTART if (pv[SND_RES].ival > 0) { if (!sizeok) { printf("?PUT /RESTART can't be used because SIZE disabled.\n"); x = -9; goto xputx; } if (x_usn || putflags) { printf("?Conflicting switches: /RECOVER %s\n", x_usn && putflags ? "/UNIQUE /UPDATE" : (x_usn ? "/UNIQUE" : "/UPDATE") ); x = -9; goto xputx; } #ifndef NOCSETS if (x_xla && (x_csl == FC_UCS2 || x_csl == FC_UTF8 || x_csr == FC_UCS2 || x_csr == FC_UTF8)) { printf("?/RECOVER can not be used with Unicode translation\n"); x = -9; goto xputx; } #endif /* NOCSETS */ putflags = PUT_RES; } #endif /* FTP_RESTART */ } debug(F101,"ftp PUT restart","",putflags & PUT_RES); debug(F101,"ftp PUT update","",putflags & PUT_UPD); #ifdef PIPESEND if (pv[SND_FLT].ival > 0) { /* Have SEND FILTER? */ if (!pv[SND_FLT].sval) { sndfilter = NULL; } else { sndfilter = (char *) malloc((int) strlen(pv[SND_FLT].sval) + 1); if (sndfilter) strcpy(sndfilter,pv[SND_FLT].sval); /* safe */ } debug(F110,"ftp put /FILTER", sndfilter, 0); } if (sndfilter || pipesend) /* No /UPDATE or /RESTART */ if (putflags) /* with pipes or filters */ putflags = 0; #endif /* PIPESEND */ tfc = (CK_OFF_T)0; /* Initialize stats and counters */ filcnt = 0; pktnum = 0; spackets = 0L; if (wild) /* (is this necessary?) */ cx = FTP_MPU; t0 = gmstimer(); /* Record starting time */ done = 0; /* Loop control */ cancelgroup = 0; cdlevel = 0; cdsimlvl = 0; while (!done && !cancelgroup) { /* Loop for all files */ /* or until canceled. */ #ifdef FTP_PROXY /* If we are using a proxy, we don't use the local file list; instead we use the list on the remote machine which we want sent to someone else, and we use remglob() to get the names. But in that case we shouldn't even be executing this routine; see ftp_mput(). */ #endif /* FTP_PROXY */ cancelfile = 0; x = gnfile(); /* Get next file from list(s) */ if (x == 0) /* (see gnfile() comments...) */ x = gnferror; debug(F111,"FTP PUT gnfile",filnam,x); debug(F111,"FTP PUT binary",filnam,binary); switch (x) { case 1: /* File to send */ s2 = asnambuf; #ifndef NOSPL if (asnambuf[0]) { /* As-name */ int n; char *p; /* to be evaluated... */ n = TMPBUFSIZ; p = tmpbuf; zzstring(asnambuf,&p,&n); s2 = tmpbuf; debug(F110,"ftp put asname",s2,0); } #endif /* NOSPL */ rc = putfile(cx, /* Function (PUT, APPEND) */ filnam, s2, /* Name to send, as-name */ forcetype, moving, /* Parameters from switches... */ snd_move, snd_rename, srv_renam, x_cnv, x_usn, xfiletype, x_prm, #ifndef NOCSETS x_csl, (!x_xla ? -1 : x_csr), #else -1, -1, #endif /* NOCSETS */ putflags ); debug(F111,"ftp put putfile rc",filnam,rc); debug(F111,"ftp put putfile cancelfile",filnam,cancelfile); debug(F111,"ftp put putfile cancelgroup",filnam,cancelgroup); if (rc > -1) { good++; status = 1; } if (cancelfile) continue; if (rc < 0) { ftp_fai++; if (puterror) { status = 0; printf("?Fatal upload error: %s\n",filnam); done++; } } continue; case 0: /* No more files, done */ done++; continue; case -1: printf("?%s: file not found - \"%s\"\n", puterror ? "Fatal" : "Warning", filnam ); if (puterror) { status = 0; done++; break; } continue; case -2: if (puterror) { printf("?Fatal: file not found - \"%s\"\n", filnam); status = 0; done++; break; } continue; /* Not readable, keep going */ case -3: if (puterror) { printf("?Fatal: Read access denied - \"%s\"\n", filnam); status = 0; done++; break; } printf("?Warning access denied - \"%s\"\n", filnam); continue; #ifdef COMMENT case -4: /* Canceled */ done++; break; #endif /* COMMENT */ case -5: printf("?Too many files match\n"); done++; break; case -6: if (good < 1) printf("?No files selected\n"); done++; break; default: printf("?getnextfile() - unknown failure\n"); done++; } } if (cdlevel > 0) { while (cdlevel--) { if (cdsimlvl) { cdsimlvl--; } else if (!doftpcdup()) break; } } if (status > 0) { if (cancelgroup) status = 0; else if (cancelfile && good < 1) status = 0; } success = status; x = success; xputx: if (x > -1) { #ifdef GFTIMER t1 = gmstimer(); /* End time */ sec = (CKFLOAT)((CKFLOAT)(t1 - t0) / 1000.0); /* Stats */ if (!sec) sec = 0.001; fptsecs = sec; #else sec = (t1 - t0) / 1000; if (!sec) sec = 1; #endif /* GFTIMER */ tfcps = (long) (tfc / sec); tsecs = (int)sec; lastxfer = W_FTP|W_SEND; xferstat = success; if (dpyactive) ftscreen(status > 0 ? SCR_TC : SCR_CW, 0, (CK_OFF_T)0, ""); } for (i = 0; i <= SND_MAX; i++) { /* Free malloc'd memory */ if (pv[i].sval) free(pv[i].sval); } ftreset(); /* Undo switch effects */ dpyactive = 0; return(x); } static char ** mgetlist = NULL; /* For MGET */ static int mgetn = 0, mgetx = 0; static char xtmpbuf[4096]; /* c m d l i n g e t Get files specified by -g command-line option. File list is set up in cmlist[] by ckuusy.c; nfils is length of list. */ int cmdlinget(stay) int stay; { int i, x, rc = 0, done = 0, good = 0, status = 0, append = 0; int lcs = -1, rcs = -1, xlate = 0; int first = 1; int mget = 1; int nc; char * s, * s2, * s3; ULONG t0, t1; /* Times for stats */ #ifdef GFTIMER CKFLOAT sec; #else int sec = 0; #endif /* GFTIMER */ if (quiet) { /* -q really means quiet */ displa = 0; fdispla = 0; } else { displa = 1; fdispla = XYFD_B; } testing = 0; dpyactive = 0; out2screen = 0; what = W_FTP|W_RECV; mgetmethod = 0; mgetforced = 0; havetype = 0; havesize = (CK_OFF_T)-1; makestr(&havemdtm,NULL); if (ftp_fnc < 0) ftp_fnc = fncact; #ifndef NOSPL cmd_quoting = 0; #endif /* NOSPL */ debug(F101,"ftp cmdlinget nfils","",nfils); if (ftp_cnv == CNV_AUTO) { /* Name conversion is auto */ if (alike) { /* If server & client are alike */ nc = 0; /* no conversion */ } else { /* If they are different */ if (servertype == SYS_UNIX || servertype == SYS_WIN32) nc = -1; /* only minimal conversions needed */ else /* otherwise */ nc = 1; /* full conversion */ } } else /* Not auto - do what user said */ nc = ftp_cnv; if (nfils < 1) doexit(BAD_EXIT,-1); t0 = gmstimer(); /* Starting time for this batch */ #ifndef NOCSETS if (xlate) { /* SET FTP CHARACTER-SET-TRANSLATION */ lcs = ftp_csl; /* Local charset */ if (lcs < 0) lcs = fcharset; if (lcs < 0) xlate = 0; } if (xlate) { /* Still ON? */ rcs = ftp_csx; /* Remote (Server) charset */ if (rcs < 0) rcs = ftp_csr; if (rcs < 0) xlate = 0; } #endif /* NOCSETS */ /* If we have only one file and it is a directory, then we ask for a listing of its contents, rather than retrieving the directory file itself. This is what (e.g.) Netscape does. */ if (nfils == 1) { if (doftpcwd((char *)cmlist[mgetx],-1)) { /* If we can CD to it, it must be a directory */ if (recursive) { cmlist[mgetx] = "*"; } else { status = (recvrequest("LIST","-","","wb",0,0,NULL,xlate,lcs,rcs)==0); done = 1; } } } /* The following is to work around UNIX servers which, when given a command like "NLST path/blah" (not wild) returns the basename without the path. */ if (!done && servertype == SYS_UNIX && nfils == 1) { mget = iswild(cmlist[mgetx]); } if (!mget && !done) { /* Invoked by command-line FTP URL */ if (ftp_deb) printf("DOING GET...\n"); done++; cancelfile = 0; /* This file not canceled yet */ s = cmlist[mgetx]; rc = 0; /* Initial return code */ fsize = (CK_OFF_T)-1; if (sizeok) { x = ftpcmd("SIZE",s,lcs,rcs,ftp_vbm); /* Get remote file's size */ if (x == REPLY_COMPLETE) fsize = ckatofs(&ftp_reply_str[4]); } ckstrncpy(filnam,s,CKMAXPATH); /* For \v(filename) */ debug(F111,"ftp cmdlinget filnam",filnam,fsize); nzrtol(s,tmpbuf,nc,0,CKMAXPATH); /* Strip path and maybe convert */ s2 = tmpbuf; /* If local file already exists, take collision action */ if (zchki(s2) > -1) { switch (ftp_fnc) { case XYFX_A: /* Append */ append = 1; break; case XYFX_R: /* Rename */ case XYFX_B: { /* Backup */ char * p = NULL; int x = -1; znewn(s2,&p); /* Make unique name */ debug(F110,"ftp cmdlinget znewn",p,0); if (ftp_fnc == XYFX_B) { /* Backup existing file */ x = zrename(s2,p); debug(F111,"ftp cmdlinget backup zrename",p,x); } else { /* Rename incoming file */ x = ckstrncpy(tmpbuf,p,CKMAXPATH+1); s2 = tmpbuf; debug(F111,"ftp cmdlinget rename incoming",p,x); } if (x < 0) { printf("?Backup/Rename failed\n"); return(success = 0); } break; } case XYFX_D: /* Discard */ ftscreen(SCR_FN,'F',(CK_OFF_T)0,s); ftscreen(SCR_ST,ST_SKIP,(CK_OFF_T)SKP_NAM,s); tlog(F100," refused: name","",0); debug(F110,"ftp cmdlinget skip name",s2,0); goto xclget; case XYFX_X: /* Overwrite */ case XYFX_U: /* Update (already handled above) */ case XYFX_M: /* ditto */ break; } } rc = getfile(s, /* Remote name */ s2, /* Local name */ 0, /* Recover/Restart */ append, /* Append */ NULL, /* Pipename */ 0, /* Translate charsets */ -1, /* File charset (none) */ -1 /* Server charset (none) */ ); debug(F111,"ftp cmdlinget rc",s,rc); debug(F111,"ftp cmdlinget cancelfile",s,cancelfile); debug(F111,"ftp cmdlinget cancelgroup",s,cancelgroup); if (rc < 0 && haveurl && s[0] == '/') /* URL failed - try again */ rc = getfile(&s[1], /* Remote name without leading '/' */ s2, /* Local name */ 0, /* Recover/Restart */ append, /* Append */ NULL, /* Pipename */ 0, /* Translate charsets */ -1, /* File charset (none) */ -1 /* Server charset (none) */ ); if (rc > -1) { good++; status = 1; } if (cancelfile) goto xclget; if (rc < 0) { ftp_fai++; #ifdef FTP_TIMEOUT if (ftp_timed_out) status = 0; #endif /* FTP_TIMEOUT */ if (geterror) { status = 0; done++; } } } if (ftp_deb && !done) printf("DOING MGET...\n"); while (!done && !cancelgroup) { cancelfile = 0; /* This file not canceled yet */ s = (char *)remote_files(first,(CHAR *)cmlist[mgetx],NULL,0); if (!s) s = ""; if (!*s) { first = 1; mgetx++; if (mgetx < nfils) s = (char *)remote_files(first,(CHAR *)cmlist[mgetx],NULL,0); else s = NULL; debug(F111,"ftp cmdlinget remote_files B",s,0); if (!s) { done = 1; break; } } /* The semantics of NLST are ill-defined. Suppose we have just sent NLST /path/[a-z]*. Most servers send back names like /path/foo, /path/bar, etc. But some send back only foo and bar, and subsequent RETR commands based on the pathless names are not going to work. */ if (servertype == SYS_UNIX && !ckstrchr(s,'/')) { if ((s3 = ckstrrchr(cmlist[mgetx],'/'))) { int len, left = 4096; char * tmp = xtmpbuf; len = s3 - cmlist[mgetx] + 1; ckstrncpy(tmp,cmlist[mgetx],left); tmp += len; left -= len; ckstrncpy(tmp,s,left); s = xtmpbuf; debug(F111,"ftp cmdlinget remote_files X",s,0); } } first = 0; /* Not first any more */ debug(F111,"ftp cmdlinget havetype",s,havetype); if (havetype > 0 && havetype != FTYP_FILE) { /* Server says not file */ debug(F110,"ftp cmdlinget not-a-file",s,0); continue; } rc = 0; /* Initial return code */ if (havesize > (CK_OFF_T)-1) { /* Already have file size? */ fsize = havesize; } else { /* No - must ask server */ /* Prior to sending the NLST command we necessarily put the server into ASCII mode. We must now put it back into the the requested mode so the upcoming SIZE command returns right kind of size; this is especially important for GET /RECOVER; otherwise the server returns the "ASCII" size of the file, rather than its true size. */ changetype(ftp_typ,0); /* Change to requested type */ fsize = (CK_OFF_T)-1; if (sizeok) { x = ftpcmd("SIZE",s,lcs,rcs,ftp_vbm); if (x == REPLY_COMPLETE) fsize = ckatofs(&ftp_reply_str[4]); } } ckstrncpy(filnam,s,CKMAXPATH); /* For \v(filename) */ debug(F111,"ftp cmdlinget filnam",filnam,fsize); nzrtol(s,tmpbuf,nc,0,CKMAXPATH); /* Strip path and maybe convert */ s2 = tmpbuf; /* If local file already exists, take collision action */ if (zchki(s2) > -1) { switch (ftp_fnc) { case XYFX_A: /* Append */ append = 1; break; case XYFX_R: /* Rename */ case XYFX_B: { /* Backup */ char * p = NULL; int x = -1; znewn(s2,&p); /* Make unique name */ debug(F110,"ftp cmdlinget znewn",p,0); if (ftp_fnc == XYFX_B) { /* Backup existing file */ x = zrename(s2,p); debug(F111,"ftp cmdlinget backup zrename",p,x); } else { /* Rename incoming file */ x = ckstrncpy(tmpbuf,p,CKMAXPATH+1); s2 = tmpbuf; debug(F111,"ftp cmdlinget rename incoming",p,x); } if (x < 0) { printf("?Backup/Rename failed\n"); return(success = 0); } break; } case XYFX_D: /* Discard */ ftscreen(SCR_FN,'F',(CK_OFF_T)0,s); ftscreen(SCR_ST,ST_SKIP,(CK_OFF_T)SKP_NAM,s); tlog(F100," refused: name","",0); debug(F110,"ftp cmdlinget skip name",s2,0); continue; case XYFX_X: /* Overwrite */ case XYFX_U: /* Update (already handled above) */ case XYFX_M: /* ditto */ break; } } /* ^^^ ADD CHARSET STUFF HERE ^^^ */ rc = getfile(s, /* Remote name */ s2, /* Local name */ 0, /* Recover/Restart */ append, /* Append */ NULL, /* Pipename */ 0, /* Translate charsets */ -1, /* File charset (none) */ -1 /* Server charset (none) */ ); debug(F111,"ftp cmdlinget rc",s,rc); debug(F111,"ftp cmdlinget cancelfile",s,cancelfile); debug(F111,"ftp cmdlinget cancelgroup",s,cancelgroup); if (rc > -1) { good++; status = 1; } if (cancelfile) continue; if (rc < 0) { ftp_fai++; #ifdef FTP_TIMEOUT if (ftp_timed_out) status = 0; #endif /* FTP_TIMEOUT */ if (geterror) { status = 0; done++; } } } xclget: if (cancelgroup) mlsreset(); if (status > 0) { if (cancelgroup) status = 0; else if (cancelfile && good < 1) status = 0; } success = status; #ifdef GFTIMER t1 = gmstimer(); /* End time */ sec = (CKFLOAT)((CKFLOAT)(t1 - t0) / 1000.0); /* Stats */ if (!sec) sec = 0.001; fptsecs = sec; #else sec = (t1 - t0) / 1000; if (!sec) sec = 1; #endif /* GFTIMER */ tfcps = (long) (tfc / sec); tsecs = (int)sec; lastxfer = W_FTP|W_RECV; xferstat = success; if (dpyactive) ftscreen(status > 0 ? SCR_TC : SCR_CW, 0, (CK_OFF_T)0, ""); if (!stay) doexit(success ? GOOD_EXIT : BAD_EXIT, -1); return(success); } /* d o f t p g e t -- Parse and execute GET, MGET, MDELETE, ... */ /* Note: if we wanted to implement /AFTER:, /BEFORE:, etc, we could use zstrdat() to convert to UTC-based time_t. But it doesn't make sense from the user-interface perspective, since the server's directory listings show its own local times and since we don't know what timezone it's in, there's no way to reconcile our local times with the server's. */ int doftpget(cx,who) int cx, who; { /* who == 1 for ftp, 0 for kermit */ struct FDB fl, sw, cm; int i, n, rc, getval = 0, mget = 0, done = 0, pipesave = 0; int x_cnv = 0, x_prm = 0, restart = 0, status = 0, good = 0; int x_fnc = 0, first = 0, skipthis = 0, append = 0, selected = 0; int renaming = 0, mdel = 0, listfile = 0, updating = 0, getone = 0; int moving = 0, deleting = 0, toscreen = 0, haspath = 0; int gotsize = 0; int matchdot = 0; CK_OFF_T getlarger = (CK_OFF_T)-1; CK_OFF_T getsmaller = (CK_OFF_T)-1; char * msg, * s, * s2, * nam, * pipename = NULL, * pn = NULL; char * src = "", * local = ""; char * pat = ""; int x_csl = -1, x_csr = -1; /* Local and remote charsets */ int x_xla = 0; char c; /* Worker char */ ULONG t0 = 0L, t1; /* Times for stats */ #ifdef GFTIMER CKFLOAT sec; #else int sec = 0; #endif /* GFTIMER */ struct stringint pv[SND_MAX+1]; /* Temporary array for switch values */ success = 0; /* Assume failure */ forcetype = 0; /* No /TEXT or /BINARY given yet */ restart = 0; /* No restart yet */ out2screen = 0; /* No TO-SCREEN switch given yet */ mgetmethod = 0; /* No NLST or MLSD switch yet */ mgetforced = 0; g_displa = fdispla; if (ftp_dis > -1) fdispla = ftp_dis; x_cnv = ftp_cnv; /* Filename conversion */ if (x_cnv == CNV_AUTO) { /* Name conversion is auto */ if (alike) { /* If server & client are alike */ x_cnv = 0; /* no conversion */ } else { /* If they are different */ if (servertype == SYS_UNIX || servertype == SYS_WIN32) x_cnv = -1; /* only minimal conversions needed */ else /* otherwise */ x_cnv = 1; /* full conversion */ } } else /* Not auto - do what user said */ x_cnv = ftp_cnv; x_prm = ftp_prm; /* Permissions */ if (x_prm == SET_AUTO) /* Permissions AUTO */ x_prm = alike; #ifndef NOCSETS x_csr = ftp_csr; /* Inherit global server charset */ x_csl = ftp_csl; /* Inherit global local charset */ if (x_csl < 0) /* If none, use current */ x_csl = fcharset; /* file character-set. */ x_xla = ftp_xla; /* Translation On/Off */ #endif /* NOCSETS */ geterror = ftp_err; /* Inherit global error action. */ asnambuf[0] = NUL; /* No as-name yet. */ pipesave = pipesend; pipesend = 0; havetype = 0; havesize = (CK_OFF_T)-1; makestr(&havemdtm,NULL); if (g_ftp_typ > -1) { /* Restore TYPE if saved */ ftp_typ = g_ftp_typ; /* g_ftp_typ = -1; */ } for (i = 0; i <= SND_MAX; i++) { /* Initialize switch values */ pv[i].sval = NULL; /* to null pointers */ pv[i].ival = -1; /* and -1 int values */ pv[i].wval = (CK_OFF_T)-1; /* and -1 wide values */ } zclose(ZMFILE); /* In case it was left open */ x_fnc = ftp_fnc > -1 ? ftp_fnc : fncact; /* Filename collision action */ if (fp_nml) { /* Reset /NAMELIST */ if (fp_nml != stdout) fclose(fp_nml); fp_nml = NULL; } makestr(&ftp_nml,NULL); /* Initialize list of remote filespecs */ if (!mgetlist) { mgetlist = (char **)malloc(MGETMAX * sizeof(char *)); if (!mgetlist) { printf("?Memory allocation failure - MGET list\n"); return(-9); } for (i = 0; i < MGETMAX; i++) mgetlist[i] = NULL; } mgetn = 0; /* Number of mget arguments */ mgetx = 0; /* Current arg */ if (who == 0) { /* Called with unprefixed command */ if (cx == XXGET || cx == XXREGET || cx == XXRETR) getone++; switch (cx) { case XXREGET: pv[SND_RES].ival = 1; break; case XXRETR: pv[SND_DEL].ival = 1; break; case XXGET: case XXMGET: mget++; break; } } else { /* FTP command */ if (cx == FTP_GET || cx == FTP_RGE) getone++; switch (cx) { case FTP_DEL: /* (fall thru on purpose) */ case FTP_MDE: mdel++; /* (ditto) */ case FTP_GET: /* (ditto) */ case FTP_MGE: mget++; break; case FTP_RGE: pv[SND_RES].ival = 1; break; } } cmfdbi(&sw, /* First FDB - command switches */ _CMKEY, /* fcode */ "Remote filename;\n or switch", /* hlpmsg */ "", /* default */ "", /* addtl string data */ mdel ? ndelswi : ngetswi, /* addtl numeric data 1: tbl size */ 4, /* addtl numeric data 2: 4 = cmswi */ xxstring, /* Processing function */ mdel ? delswi : getswi, /* Keyword table */ &fl /* Pointer to next FDB */ ); cmfdbi(&fl, /* 2nd FDB - remote filename */ _CMFLD, /* fcode */ "", /* hlpmsg */ "", /* default */ "", /* addtl string data */ 0, /* addtl numeric data 1 */ 0, /* addtl numeric data 2 */ xxstring, NULL, &cm ); cmfdbi(&cm, /* 3rd FDB - Confirmation */ _CMCFM, /* fcode */ "", /* hlpmsg */ "", /* default */ "", /* addtl string data */ 0, /* addtl numeric data 1 */ 0, /* addtl numeric data 2 */ NULL, NULL, NULL ); while (1) { /* Parse 0 or more switches */ x = cmfdb(&sw); /* Parse something */ debug(F101,"ftp get cmfdb","",x); if (x < 0) /* Error */ goto xgetx; /* or reparse needed */ if (cmresult.fcode != _CMKEY) /* Break out of loop if not a switch */ break; c = cmgbrk(); /* Get break character */ getval = (c == ':' || c == '='); /* to see how they ended the switch */ if (getval && !(cmresult.kflags & CM_ARG)) { printf("?This switch does not take arguments\n"); x = -9; goto xgetx; } n = cmresult.nresult; /* Numeric result = switch value */ debug(F101,"ftp get switch","",n); if (!getval && (cmgkwflgs() & CM_ARG)) { printf("?This switch requires an argument\n"); x = -9; goto xgetx; } switch (n) { /* Process the switch */ case SND_ASN: /* /AS-NAME: */ debug(F101,"ftp get /as-name getval","",getval); if (!getval) break; if ((x = cmfld("Name to store it under","",&s,NULL)) < 0) { if (x == -3) { printf("?name required\n"); x = -9; } goto xgetx; } s = brstrip(s); if (!*s) s = NULL; makestr(&(pv[n].sval),s); pv[n].ival = 1; break; case SND_BIN: /* /BINARY */ case SND_TXT: /* /TEXT or /ASCII */ case SND_TEN: /* /TENEX */ pv[SND_BIN].ival = 0; pv[SND_TXT].ival = 0; pv[SND_TEN].ival = 0; pv[n].ival = 1; break; #ifdef PUTPIPE case SND_CMD: /* These take no args */ if (nopush) { printf("?Sorry, system command access is disabled\n"); x = -9; goto xgetx; } #ifdef PIPESEND else if (rcvfilter) { printf("?Sorry, no PUT /COMMAND when SEND FILTER selected\n"); x = -9; goto xgetx; } #endif /* PIPESEND */ sw.hlpmsg = "Command, or switch"; /* Change help message */ pv[n].ival = 1; /* Just set the flag */ pv[SND_ARR].ival = 0; break; #endif /* PUTPIPE */ case SND_SHH: /* /QUIET */ case SND_RES: /* /RECOVER (reget) */ case SND_NOB: /* /NOBACKUPFILES */ case SND_DEL: /* /DELETE */ case SND_UPD: /* /UPDATE */ case SND_USN: /* /UNIQUE */ case SND_NOD: /* /NODOTFILES */ case SND_REC: /* /RECOVER */ case SND_MAI: /* /TO-SCREEN */ pv[n].ival = 1; /* Just set the flag */ break; case SND_DIF: /* /DATES-DIFFER */ pv[SND_COL].ival = XYFX_M; /* Now it's a collision option */ pv[n].ival = 1; break; case SND_COL: /* /COLLISION: */ if ((x = cmkey(ftpcolxtab,nftpcolx,"","",xxstring)) < 0) goto xgetx; if (x == XYFX_M) pv[SND_DIF].ival = 1; /* (phase this out) */ pv[n].ival = x; /* this should be sufficient */ break; case SND_ERR: /* /ERROR-ACTION */ if ((x = cmkey(qorp,2,"","",xxstring)) < 0) goto xgetx; pv[n].ival = x; break; case SND_EXC: /* Exception list */ if (!getval) break; if ((x = cmfld("Pattern","",&s,xxstring)) < 0) { if (x == -3) { printf("?Pattern required\n"); x = -9; } goto xgetx; } if (s) if (!*s) s = NULL; makestr(&(pv[n].sval),s); if (pv[n].sval) pv[n].ival = 1; break; #ifdef PIPESEND case SND_FLT: debug(F101,"ftp get /filter getval","",getval); if (!getval) break; if ((x = cmfld("Filter program to send through","",&s,NULL)) < 0) { if (x == -3) s = ""; else goto xgetx; } s = brstrip(s); if (pv[SND_MAI].ival < 1) { y = strlen(s); /* Make sure they included "\v(...)" */ for (x = 0; x < y; x++) { if (s[x] != '\\') continue; if (s[x+1] == 'v') break; } if (x == y) { printf( "?Filter must contain a replacement variable for filename.\n" ); x = -9; goto xgetx; } } if (*s) { pv[n].ival = 1; makestr(&(pv[n].sval),s); } else { pv[n].ival = 0; makestr(&(pv[n].sval),NULL); } break; #endif /* PIPESEND */ case SND_NAM: if (!getval) break; if ((x = cmkey(fntab,nfntab,"","automatic",xxstring)) < 0) goto xgetx; debug(F101,"ftp get /filenames","",x); pv[n].ival = x; break; case SND_SMA: /* Smaller / larger than */ case SND_LAR: { CK_OFF_T y; if (!getval) break; if ((x = cmnumw("Size in bytes","0",10,&y,xxstring)) < 0) goto xgetx; pv[n].wval = y; break; } case SND_FIL: /* Name of file containing filnames */ if (!getval) break; if ((x = cmifi("Name of file containing list of filenames", "",&s,&y,xxstring)) < 0) { if (x == -3) { printf("?Filename required\n"); x = -9; } goto xgetx; } else if (y && iswild(s)) { printf("?Wildcards not allowed BBB\n"); x = -9; goto xgetx; } if (s) if (!*s) s = NULL; makestr(&(pv[n].sval),s); if (pv[n].sval) pv[n].ival = 1; break; case SND_MOV: /* MOVE after */ case SND_REN: /* RENAME after */ case SND_SRN: { /* SERVER-RENAME */ char * m = ""; switch (n) { case SND_MOV: m = "Device and/or directory for incoming file after reception"; break; case SND_REN: m = "New name for incoming file after reception"; break; case SND_SRN: m = "New name for source file on server after reception"; break; } if (!getval) break; if ((x = cmfld(m, "", &s, n == SND_MOV ? xxstring : NULL)) < 0) { if (x == -3) { printf("%s\n", n == SND_MOV ? "?Destination required" : "?New name required" ); x = -9; } goto xgetx; } makestr(&(pv[n].sval),*s ? brstrip(s) : NULL); pv[n].ival = (pv[n].sval) ? 1 : 0; break; } #ifndef NOCSETS case SND_CSL: /* Local character set */ case SND_CSR: /* Remote (server) charset */ if ((x = cmkey(fcstab,nfilc,"","",xxstring)) < 0) return((x == -3) ? -2 : x); if (n == SND_CSL) x_csl = x; else x_csr = x; x_xla = 1; /* Overrides global OFF setting */ break; case SND_XPA: /* Transparent */ x_xla = 0; x_csr = -1; x_csl = -1; break; #endif /* NOCSETS */ case SND_NML: if ((x = cmofi("Local filename","-",&s,xxstring)) < 0) goto xgetx; makestr(&ftp_nml,s); break; case SND_PAT: /* /PATTERN: */ if (!getval) break; if ((x = cmfld("Pattern","*", &s, xxstring)) < 0) goto xgetx; makestr(&(pv[n].sval),*s ? brstrip(s) : NULL); pv[n].ival = (pv[n].sval) ? 1 : 0; break; case SND_NLS: /* /NLST */ pv[n].ival = 1; /* Use NLST */ pv[SND_MLS].ival = 0; /* Don't use MLSD */ break; case SND_MLS: /* /MLSD */ pv[n].ival = 1; /* Use MLSD */ pv[SND_NLS].ival = 0; /* Don't use NLST */ break; default: /* /AFTER, /PERMISSIONS, etc... */ printf("?Sorry, \"%s\" works only with [M]PUT\n",atmbuf); x = -9; goto xgetx; } } line[0] = NUL; cmarg = line; cmarg2 = asnambuf; s = line; /* For GET, we want to parse an optional as-name, like with PUT. For MGET, we must parse a list of names, and then send NLST or MLSD commands for each name separately. */ switch (cmresult.fcode) { /* How did we get out of switch loop */ case _CMFLD: /* Field */ if (!getone) { s = brstrip(cmresult.sresult); makestr(&(mgetlist[mgetn++]),s); while ((x = cmfld("Remote filename","",&s,xxstring)) != -3) { if (x < 0) goto xgetx; makestr(&(mgetlist[mgetn++]),brstrip(s)); if (mgetn >= MGETMAX) { printf("?Too many items in MGET list\n"); goto xgetx; } } if ((x = cmcfm()) < 0) goto xgetx; } else { s = brstrip(cmresult.sresult); ckstrncpy(line,s,LINBUFSIZ); if ((x = cmfld("Name to store it under","",&s,xxstring)) < 0) if (x != -3) goto xgetx; s = brstrip(s); ckstrncpy(asnambuf,s,CKMAXPATH+1); if ((x = cmcfm()) < 0) goto xgetx; } break; case _CMCFM: /* Confirmation */ break; default: printf("?Unexpected function code: %d\n",cmresult.fcode); x = -9; goto xgetx; } if (pv[SND_REC].ival > 0) /* /RECURSIVE */ recursive = 2; if (pv[SND_BIN].ival > 0) { /* /BINARY really means binary... */ forcetype = 1; /* So skip the name-pattern match */ ftp_typ = XYFT_B; /* Set binary */ } else if (pv[SND_TXT].ival > 0) { /* Similarly for /TEXT... */ forcetype = 1; ftp_typ = XYFT_T; } else if (pv[SND_TEN].ival > 0) { /* and /TENEX*/ forcetype = 1; ftp_typ = FTT_TEN; } else if (ftp_cmdlin && ftp_xfermode == XMODE_M) { forcetype = 1; ftp_typ = binary; g_ftp_typ = binary; } if (pv[SND_ASN].ival > 0 && pv[SND_ASN].sval && !asnambuf[0]) { char * p; p = brstrip(pv[SND_ASN].sval); /* As-name */ ckstrncpy(asnambuf,p,CKMAXPATH+1); } debug(F110,"ftp get asnambuf",asnambuf,0); #ifdef PIPESEND if (pv[SND_CMD].ival > 0) { /* /COMMAND - strip any braces */ char * p; p = asnambuf; debug(F110,"GET /COMMAND before stripping",p,0); p = brstrip(p); debug(F110,"GET /COMMAND after stripping",p,0); if (!*p) { printf("?Sorry, a command to write to is required\n"); x = -9; goto xgetx; } pipename = p; pipesend = 1; } #endif /* PIPESEND */ /* Set up /MOVE and /RENAME */ #ifdef COMMENT /* Conflict exists only for PUT - removed 13 Mar 2006 - fdc */ if (pv[SND_DEL].ival > 0 && (pv[SND_MOV].ival > 0 || pv[SND_REN].ival > 0)) { printf("?Sorry, /DELETE conflicts with /MOVE or /RENAME\n"); x = -9; goto xgetx; } #endif /* COMMENT */ #ifdef CK_TMPDIR if (pv[SND_MOV].ival > 0 && pv[SND_MOV].sval) { int len; char * p = pv[SND_MOV].sval; len = strlen(p); if (!isdir(p)) { /* Check directory */ #ifdef CK_MKDIR char * s = NULL; s = (char *)malloc(len + 4); if (s) { strcpy(s,p); /* safe */ #ifdef datageneral if (s[len-1] != ':') { s[len++] = ':'; s[len] = NUL; } #else if (s[len-1] != '/') { s[len++] = '/'; s[len] = NUL; } #endif /* datageneral */ s[len++] = 'X'; s[len] = NUL; #ifdef NOMKDIR x = -1; #else x = zmkdir(s); #endif /* NOMKDIR */ free(s); if (x < 0) { printf("?Can't create \"%s\"\n",p); x = -9; goto xgetx; } } #else printf("?Directory \"%s\" not found\n",p); x = -9; goto xgetx; #endif /* CK_MKDIR */ } makestr(&rcv_move,p); moving = 1; } #endif /* CK_TMPDIR */ if (pv[SND_REN].ival > 0) { /* /RENAME */ char * p = pv[SND_REN].sval; if (!p) p = ""; if (!*p) { printf("?New name required for /RENAME\n"); x = -9; goto xgetx; } p = brstrip(p); #ifndef NOSPL /* If name given is wild, rename string must contain variables */ if (mget && !getone) { char * s = tmpbuf; x = TMPBUFSIZ; zzstring(p,&s,&x); if (!strcmp(tmpbuf,p)) { printf( "?/RENAME for file group must contain variables such as \\v(filename)\n" ); x = -9; goto xgetx; } } #endif /* NOSPL */ renaming = 1; makestr(&rcv_rename,p); debug(F110,"FTP rcv_rename",rcv_rename,0); } if (!cmarg[0] && mgetn == 0 && getone && pv[SND_FIL].ival < 1) { printf("?Filename required but not given\n"); x = -9; goto xgetx; } else if ((cmarg[0] || mgetn > 0) && pv[SND_FIL].ival > 0) { printf("?You can't give both /LISTFILE and a remote filename\n"); x = -9; goto xgetx; } CHECKCONN(); /* Check connection */ if (pv[SND_COL].ival > -1) x_fnc = pv[SND_COL].ival; #ifndef NOSPL /* If as-name given for MGET, as-name must contain variables */ if (mget && !getone && asnambuf[0] && x_fnc != XYFX_A) { char * s = tmpbuf; x = TMPBUFSIZ; zzstring(asnambuf,&s,&x); if (!strcmp(tmpbuf,asnambuf)) { printf( "?As-name for MGET must contain variables such as \\v(filename)\n" ); x = -9; goto xgetx; } } #endif /* NOSPL */ /* doget: */ if (pv[SND_SHH].ival > 0 || ftp_nml) { /* GET /QUIET... */ fdispla = 0; } else { displa = 1; if (mdel || ftp_deb) fdispla = XYFD_B; } deleting = 0; if (pv[SND_DEL].ival > 0) /* /DELETE was specified */ deleting = 1; if (pv[SND_EXC].ival > 0) makelist(pv[SND_EXC].sval,rcvexcept,NSNDEXCEPT); if (pv[SND_SMA].wval > -1) getsmaller = pv[SND_SMA].wval; if (pv[SND_LAR].wval > -1) getlarger = pv[SND_LAR].wval; if (pv[SND_NAM].ival > -1) x_cnv = pv[SND_NAM].ival; if (pv[SND_ERR].ival > -1) geterror = pv[SND_ERR].ival; if (pv[SND_MAI].ival > -1) toscreen = 1; if (pv[SND_NLS].ival > 0) { /* Force NLST or MLSD? */ mgetmethod = SND_NLS; mgetforced = 1; } else if (pv[SND_MLS].ival > 0) { mgetmethod = SND_MLS; mgetforced = 1; } #ifdef FTP_RESTART if (pv[SND_RES].ival > 0) { if (!ftp_typ) { printf("?Sorry, GET /RECOVER requires binary mode\n"); x = -9; goto xgetx; #ifdef COMMENT /* Not true - the fact that the initial REST fails does not mean */ /* it will fail here. */ } else if (!okrestart) { printf("WARNING: Server might not support restart...\n"); #endif /* COMMENT */ } restart = 1; } #endif /* FTP_RESTART */ #ifdef PIPESEND if (pv[SND_FLT].ival > 0) { /* Have SEND FILTER? */ if (pipesend) { printf("?Switch conflict: /FILTER and /COMMAND\n"); x = -9; goto xgetx; } makestr(&rcvfilter,pv[SND_FLT].sval); debug(F110,"ftp get /FILTER", rcvfilter, 0); } if (rcvfilter || pipesend) { /* /RESTART */ #ifdef FTP_RESTART if (restart) { /* with pipes or filters */ printf("?Switch conflict: /FILTER or /COMMAND and /RECOVER\n"); x = -9; goto xgetx; } #endif /* FTP_RESTART */ if (pv[SND_UPD].ival > 0 || x_fnc == XYFX_M || x_fnc == XYFX_U) { printf( "?Switch conflict: /FILTER or /COMMAND and Date Checking\n"); x = -9; goto xgetx; } } #endif /* PIPESEND */ tfc = (CK_OFF_T)0; /* Initialize stats and counters */ filcnt = 0; pktnum = 0; rpackets = 0L; if (pv[SND_FIL].ival > 0) { if (zopeni(ZMFILE,pv[SND_FIL].sval) < 1) { debug(F111,"ftp get can't open listfile",pv[SND_FIL].sval,errno); printf("?Failure to open listfile - \"%s\"\n",pv[SND_FIL].sval); x = -9; goto xgetx; } if (zsinl(ZMFILE,tmpbuf,CKMAXPATH) < 0) { /* Read a line */ zclose(ZMFILE); /* Failed */ debug(F110,"ftp get listfile EOF",pv[SND_FIL].sval,0); printf("?Empty listfile - \"%s\"\n",pv[SND_FIL].sval); x = -9; goto xgetx; } listfile = 1; debug(F110,"ftp get listfile first",tmpbuf,0); makestr(&(mgetlist[0]),tmpbuf); } t0 = gmstimer(); /* Record starting time */ updating = 0; /* Checking dates? */ if (pv[SND_UPD].ival > 0 || (!mdel && x_fnc == XYFX_U)) updating = 1; if (pv[SND_DIF].ival > 0 || x_fnc == XYFX_M) updating = 2; if (updating) /* These switches force FTP DATES ON */ ftp_dates |= 2; what = mdel ? W_FTP|W_FT_DELE : W_RECV|W_FTP; /* What we're doing */ cancelgroup = 0; /* Group not canceled yet */ if (!(ftp_xfermode == XMODE_A && patterns && get_auto && !forcetype)) changetype(ftp_typ,0); /* Change to requested type */ binary = ftp_typ; /* For file-transfer display */ first = 1; /* For MGET list */ done = 0; /* Loop control */ #ifdef CK_TMPDIR if (dldir && !f_tmpdir) { /* If they have a download directory */ if ((s = zgtdir())) { /* Get current directory */ if (zchdir(dldir)) { /* Change to download directory */ ckstrncpy(savdir,s,TMPDIRLEN); f_tmpdir = 1; /* Remember that we did this */ } } } #endif /* CK_TMPDIR */ if (ftp_nml) { /* /NAMELIST */ debug(F110,"ftp GET ftp_nml",ftp_nml,0); if (ftp_nml[0] == '-' && ftp_nml[1] == 0) fp_nml = stdout; else fp_nml = fopen(ftp_nml, "wb"); if (!fp_nml) { printf("?%s: %s\n",ftp_nml,ck_errstr()); goto xgetx; } } while (!done && !cancelgroup) { /* Loop for all files */ /* or until canceled. */ #ifdef FTP_PROXY /* do something here if proxy */ #endif /* FTP_PROXY */ rs_len = (CK_OFF_T)0; /* REGET position */ cancelfile = 0; /* This file not canceled yet */ haspath = 0; /* Recalculate this each time thru */ if (getone) { /* GET */ char * p; s = line; src = line; /* Server name */ done = 1; debug(F111,"ftp get file",s,0); } else if (mget) { /* MGET */ src = mgetlist[mgetx]; debug(F111,"ftp mget remote_files A",src,first); s = (char *)remote_files(first, (CHAR *)mgetlist[mgetx], (CHAR *)pv[SND_PAT].sval, 0 ); debug(F110,"ftp mget remote_files B",s,0); if (!s) s = ""; if (!*s) { first = 1; if (listfile) { /* Names from listfile */ again: tmpbuf[0] = NUL; while (!tmpbuf[0]) { if (zsinl(ZMFILE,tmpbuf,CKMAXPATH) < 0) { zclose(ZMFILE); debug(F110,"ftp get listfile EOF", pv[SND_FIL].sval,0); makestr(&(mgetlist[0]),NULL); s = NULL; done = 1; break; } } if (done) continue; makestr(&(mgetlist[0]),tmpbuf); debug(F110,"ftp get listfile next",tmpbuf,0); s = (char *)remote_files(first, (CHAR *)mgetlist[0], (CHAR *)pv[SND_PAT].sval, 0 ); debug(F110,"ftp mget remote_files C",s,0); if (!s) { ftscreen(SCR_FN,'F',(CK_OFF_T)0,s); ftscreen(SCR_ST,ST_MSG,(CK_OFF_T)0,"File not found"); tlog(F110,"ftp get file not found:",s,0); goto again; } } else { /* Names from command line */ mgetx++; if (mgetx < mgetn) s = (char *)remote_files(first, (CHAR *)mgetlist[mgetx], (CHAR *)pv[SND_PAT].sval, 0 ); else s = NULL; if (!s) mgetx++; debug(F111,"ftp mget remote_files D",s,mgetx); } if (!s) { if (!first || mgetx >= mgetn) { done = 1; break; } else if (geterror) { status = 0; done = 1; break; } else { continue; } } } } debug(F111,"ftp mget remote_files E",s,0); /* The semantics of NLST are ill-defined. Suppose we have just sent NLST /path/[a-z]*. Most servers send back names like /path/foo, /path/bar, etc. But some send back only foo and bar, and subsequent RETR commands based on the pathless names are not going to work. */ if (servertype == SYS_UNIX && !ckstrchr(s,'/')) { char * s3; if ((s3 = ckstrrchr(mgetlist[mgetx],'/'))) { int len, left = 4096; char * tmp = xtmpbuf; len = s3 - mgetlist[mgetx] + 1; ckstrncpy(tmp,mgetlist[mgetx],left); tmp += len; left -= len; ckstrncpy(tmp,s,left); s = xtmpbuf; debug(F111,"ftp mget remote_files F",s,0); } } first = 0; skipthis = 0; /* File selection... */ msg = ""; nam = s; /* Filename (without path) */ rc = 0; /* Initial return code */ s2 = ""; if (!getone && !skipthis) { /* For MGET and MDELETE... */ char c, * p = s; int srvpath = 0; int usrpath = 0; int i, k = 0; debug(F111,"ftp mget havetype",s,havetype); if (havetype > 0 && havetype != FTYP_FILE) { /* Server says it's not file... */ debug(F110,"ftp mget not-a-file",s,0); continue; } /* Explanation: Some ftp servers (such as wu-ftpd) return a recursive list. But if the client did not ask for a recursive list, we have to ignore any server files that include a pathname that extends beyond any path that was included in the user's request. User's filespec is blah or path/blah (or other non-UNIX syntax). We need to get the user's path segment. Then, for each incoming file, if it begins with the same path segment, we must strip it (point past it). */ src = mgetlist[mgetx]; /* In case it moved! */ if (src) { for (i = 0; src[i]; i++) { /* Find rightmost path separator */ if (ispathsep(src[i])) /* in user's pathname */ k = i + 1; } } else { src = ""; } usrpath = k; /* User path segment length */ debug(F111,"ftp get usrpath",src,usrpath); p = s; /* Server filename */ while ((c = *p++)) { /* Look for path in server filename */ if (ispathsep(c)) { /* haspath++; */ nam = p; /* Pathless name (for ckmatch) */ srvpath = p - s; /* Server path segment length */ } } debug(F111,"ftp get srvpath",s,srvpath); if (usrpath == 0) { /* Here we handle the case where the user said "mget foo" where foo is a directory name, and the server is sending back names like "foo/file1", "foo/file2", etc. This is a nasty trick but it's necessary because the user can't compensate by typing "mget foo/" because then the server is likely to send back "foo//file1, foo//file2" etc, and we still won't get a match... */ int srclen = 0, srvlen = 0; if (src) srclen = strlen(src); if (s) srvlen = strlen(s); if (src && (srvlen > srclen)) { if (!strncmp(src,s,srclen) && ispathsep(s[srclen])) { char * tmpsrc = NULL; tmpsrc = (char *)malloc(srclen + 2); strncpy(tmpsrc,src,srclen); tmpsrc[srclen] = s[srclen]; tmpsrc[srclen+1] = NUL; free(mgetlist[mgetx]); mgetlist[mgetx] = tmpsrc; tmpsrc = NULL; src = mgetlist[mgetx]; usrpath = srclen+1; } } } /* If as-name not given and server filename includes path that matches the pathname from the user's file specification, we must trim the common path prefix from the server's name when constructing the local name. */ if (src && /* Wed Sep 25 17:27:48 2002 */ !asnambuf[0] && !recursive && /* Thu Sep 19 16:11:59 2002 */ (srvpath > 0) && !strncmp(src,s,usrpath)) { s2 = s + usrpath; /* Local name skips past remote path */ } #ifdef COMMENT /* This doesn't work if the path prefix contains wildcards! */ haspath = (srvpath > usrpath); #else { /* Count path segments instead */ int x1 = 0, x2 = 0; char *p; for (p = s; *p; p++) if (ispathsep(*p)) x1++; for (p = src; *p; p++) { if (ispathsep(*p)) x2++; } haspath = recursive ? x1 || x2 : x1 > x2; debug(F111,"ftp get server path segments",s,x1); debug(F111,"ftp get user path segments",src,x2); } #endif /* COMMENT */ debug(F111,"ftp get haspath",s+usrpath,haspath); if (haspath) { /* Server file has path segments? */ if (!recursive) { /* [M]GET /RECURSIVE? */ /* We did not ask for a recursive listing, but the server is sending us one anyway (as wu-ftpd is wont to do). We get here if the current filename includes a path segment beyond any path segment we asked for in our non-recursive [M]GET command. We MUST skip this file. */ debug(F111,"ftp get skipping because of path",s,0); continue; } } } else if (getone && !skipthis) { /* GET (not MGET) */ char * p = nam; while ((c = *p++)) { /* Handle path in local name */ if (ispathsep(c)) { if (recursive) { /* If recursive, keep it */ haspath = 1; break; } else { /* Otherwise lose it. */ nam = p; } } } s2 = nam; } if (!*nam) /* Name without path */ nam = s; if (!skipthis && pv[SND_NOD].ival > 0) { /* /NODOTFILES */ if (nam[0] == '.') continue; } if (!skipthis && rcvexcept[0]) { /* /EXCEPT: list */ int xx; for (i = 0; i < NSNDEXCEPT; i++) { if (!rcvexcept[i]) { break; } xx = ckmatch(rcvexcept[i], nam, servertype == SYS_UNIX, 1); debug(F111,"ftp mget /except match",rcvexcept[i],xx); if (xx) { tlog(F100," refused: exception list","",0); msg = "Refused: Exception List"; skipthis++; break; } } } if (!skipthis && pv[SND_NOB].ival > 0) { /* /NOBACKUPFILES */ if (ckmatch( #ifdef CKREGEX "*.~[0-9]*~" #else "*.~*~" #endif /* CKREGEX */ ,nam,0,1) > 0) continue; } if (!x_xla) { /* If translation is off */ x_csl = -2; /* unset the charsets */ x_csr = -2; } ckstrncpy(filnam,s,CKMAXPATH); /* For \v(filename) */ if (!*s2) /* Local name */ s2 = asnambuf; /* As-name */ if (!*s2) /* Sat Nov 16 19:19:39 2002 */ s2 = recursive ? s : nam; /* Fri Jan 10 13:15:19 2003 */ debug(F110,"ftp get filnam ",s,0); debug(F110,"ftp get asname A",s2,0); /* Receiving to real file */ if (!pipesend && #ifdef PIPESEND !rcvfilter && #endif /* PIPESEND */ !toscreen) { #ifndef NOSPL /* Do this here so we can decide whether to skip */ if (cmd_quoting && !skipthis && asnambuf[0]) { int n; char *p; n = TMPBUFSIZ; p = tmpbuf; zzstring(asnambuf,&p,&n); s2 = tmpbuf; debug(F111,"ftp get asname B",s2,updating); } #endif /* NOSPL */ local = *s2 ? s2 : s; if (!skipthis && x_fnc == XYFX_D) { /* File Collision = Discard */ CK_OFF_T x; x = zchki(local); debug(F111,"ftp get DISCARD zchki",local,x); if (x > -1) { skipthis++; debug(F110,"ftp get skip name",local,0); tlog(F100," refused: name","",0); msg = "Refused: Name"; } } #ifdef DOUPDATE if (!skipthis && updating) { /* If updating and not yet skipping */ if (zchki(local) > -1) { x = chkmodtime(local,s,0); #ifdef DEBUG if (deblog) { if (updating == 2) debug(F111,"ftp get /dates-diff chkmodtime",local,x); else debug(F111,"ftp get /update chkmodtime",local,x); } #endif /* DEBUG */ if ((updating == 1 && x > 0) || /* /UPDATE */ (updating == 2 && x == 1)) { /* /DATES-DIFFER */ skipthis++; tlog(F100," refused: date","",0); msg = "Refused: Date"; debug(F110,"ftp get skip date",local,0); } } } #endif /* DOUPDATE */ } /* Initialize file size to -1 in case server doesn't understand */ /* SIZE command, so xxscreen() will know we don't know the size */ fsize = (CK_OFF_T)-1; /* Ask for size now only if we need it for selection */ /* because if you're going thru a list 100,000 files to select */ /* a small subset, 100,000 SIZE commands can take hours... */ gotsize = 0; if (!mdel && !skipthis && /* Don't need size for DELE... */ (getsmaller >= (CK_OFF_T)0 || getlarger >= (CK_OFF_T)0)) { if (havesize >= (CK_OFF_T)0) { /* Already have file size? */ fsize = havesize; gotsize = 1; } else { /* No - must ask server */ /* Prior to sending the NLST command we necessarily put the server into ASCII mode. We must now put it back into the the requested mode so the upcoming SIZE command returns right kind of size; this is especially important for GET /RECOVER; otherwise the server returns the "ASCII" size of the file, rather than its true size. */ changetype(ftp_typ,0); /* Change to requested type */ fsize = (CK_OFF_T)-1; if (sizeok) { x = ftpcmd("SIZE",s,x_csl,x_csr,ftp_vbm); if (x == REPLY_COMPLETE) { fsize = ckatofs(&ftp_reply_str[4]); gotsize = 1; } } } if (gotsize) { if (getsmaller >= (CK_OFF_T)0 && fsize >= getsmaller) skipthis++; if (getlarger >= (CK_OFF_T)0 && fsize <= getlarger) skipthis++; if (skipthis) { debug(F111,"ftp get skip size",s,fsize); tlog(F100," refused: size","",0); msg = "Refused: Size"; } #ifdef COMMENT } else if (getone) { /* SIZE can fail for many reasons. Does the file exist? */ x = ftpcmd("NLST",s,x_csl,x_csr,ftp_vbm); if (x != REPLY_COMPLETE) { printf(">>> FILE NOT FOUND: %s\n",s); break; } #endif /* COMMENT */ } } if (skipthis) { /* Skipping this file? */ ftscreen(SCR_FN,'F',(CK_OFF_T)0,s); if (msg) ftscreen(SCR_ST,ST_ERR,(CK_OFF_T)0,msg); else ftscreen(SCR_ST,ST_SKIP,(CK_OFF_T)0,s); continue; } if (fp_nml) { /* /NAMELIST only - no transfer */ fprintf(fp_nml,"%s\n",s); continue; } if (recursive && haspath && !pipesend #ifdef PIPESEND && !rcvfilter #endif /* PIPESEND */ ) { int x; #ifdef NOMKDIR x = -1; #else x = zmkdir(s); /* Try to make the directory */ #endif /* NOMKDIR */ if (x < 0) { rc = -1; /* Failure is fatal */ if (geterror) { status = 0; ftscreen(SCR_EM,0,(CK_OFF_T)0, "Directory creation failure"); break; } } } /* Not skipping */ selected++; /* Count this file as selected */ pn = NULL; if (!gotsize && !mdel) { /* Didn't get size yet */ if (havesize > (CK_OFF_T)-1) { /* Already have file size? */ fsize = havesize; gotsize = 1; } else { /* No - must ask server */ fsize = (CK_OFF_T)-1; if (sizeok) { x = ftpcmd("SIZE",s,x_csl,x_csr,ftp_vbm); if (x == REPLY_COMPLETE) { fsize = ckatofs(&ftp_reply_str[4]); gotsize = 1; } } } } if (mdel) { /* [M]DELETE */ if (displa && !ftp_vbm) printf(" %s...",s); rc = (ftpcmd("DELE",s,x_csl,x_csr,ftp_vbm) == REPLY_COMPLETE) ? 1 : -1; if (rc > -1) { tlog(F110,"ftp mdelete",s,0); if (displa && !ftp_vbm) printf("OK\n"); } else { tlog(F110,"ftp mdelete failed:",s,0); if (displa) printf("Failed\n"); } #ifndef NOSPL #ifdef PIPESEND } else if (rcvfilter) { /* [M]GET with filter */ int n; char * p; n = CKMAXPATH; p = tmpbuf; /* Safe - no asname with filter */ zzstring(rcvfilter,&p,&n); if (n > -1) pn = tmpbuf; debug(F111,"ftp get rcvfilter",pn,n); #endif /* PIPESEND */ #endif /* NOSPL */ if (toscreen) s2 = "-"; } else if (pipesend) { /* [M]GET /COMMAND */ int n; char * p; n = CKMAXPATH; p = tmpbuf; /* Safe - no asname with filter */ zzstring(pipename,&p,&n); if (n > -1) pn = tmpbuf; debug(F111,"ftp get pipename",pipename,n); if (toscreen) s2 = "-"; } else { /* [M]GET with no pipes or filters */ debug(F111,"ftp get s2 A",s2,x_cnv); if (toscreen) { s2 = "-"; /* (hokey convention for stdout) */ } else if (!*s2) { /* No asname? */ if (x_cnv) { /* If converting */ nzrtol(s,tmpbuf,x_cnv,1,CKMAXPATH); /* convert */ s2 = tmpbuf; debug(F110,"ftp get nzrtol",s2,0); } else /* otherwise */ s2 = s; /* use incoming file's name */ } debug(F110,"ftp get s2 B",s2,0); /* If local file already exists, take collision action */ if (!pipesend && #ifdef PIPESEND !rcvfilter && #endif /* PIPESEND */ !toscreen) { CK_OFF_T x; x = zchki(s2); debug(F111,"ftp get zchki",s2,x); debug(F111,"ftp get x_fnc",s2,x_fnc); if (x > (CK_OFF_T)-1 && !restart) { int x = -1; char * newname = NULL; switch (x_fnc) { case XYFX_A: /* Append */ append = 1; break; case XYFX_R: /* Rename */ case XYFX_B: /* Backup */ znewn(s2,&newname); /* Make unique name */ debug(F110,"ftp get znewn",newname,0); if (x_fnc == XYFX_B) { /* Backup existing file */ x = zrename(s2,newname); debug(F111,"ftp get backup zrename",newname,x); } else { /* Rename incoming file */ x = ckstrncpy(tmpbuf,newname,CKMAXPATH+1); s2 = tmpbuf; debug(F111,"ftp get rename incoming",newname,x); } if (x < 0) { ftscreen(SCR_EM,0,(CK_OFF_T)0, "Backup/Rename failed"); x = 0; goto xgetx; } break; case XYFX_D: /* Discard (already handled above) */ case XYFX_U: /* Update (ditto) */ case XYFX_M: /* Update (ditto) */ case XYFX_X: /* Overwrite */ break; } } } } if (!mdel) { #ifdef PIPESEND debug(F111,"ftp get pn",pn,rcvfilter ? 1 : 0); #endif /* PIPESEND */ if (pipesend && !toscreen) s2 = NULL; #ifdef DEBUG if (deblog) { debug(F101,"ftp get x_xla","",x_xla); debug(F101,"ftp get x_csl","",x_csl); debug(F101,"ftp get x_csr","",x_csr); debug(F101,"ftp get append","",append); } #endif /* DEBUG */ rc = getfile(s,s2,restart,append,pn,x_xla,x_csl,x_csr); #ifdef DEBUG if (deblog) { debug(F111,"ftp get rc",s,rc); debug(F111,"ftp get ftp_timed_out",s,ftp_timed_out); debug(F111,"ftp get cancelfile",s,cancelfile); debug(F111,"ftp get cancelgroup",s,cancelgroup); debug(F111,"ftp get renaming",s,renaming); debug(F111,"ftp get moving",s,moving); } #endif /* DEBUG */ } if (rc > -1) { good++; status = 1; if (!cancelfile) { if (deleting) { /* GET /DELETE (source file) */ rc = (ftpcmd("DELE",s,x_csl,x_csr,ftp_vbm) == REPLY_COMPLETE) ? 1 : -1; tlog(F110, (rc > -1) ? " deleted" : " failed to delete", s, 0); } if (renaming && rcv_rename && !toscreen) { char *p; /* Rename downloaded file */ #ifndef NOSPL char tmpbuf[CKMAXPATH+1]; int n; n = CKMAXPATH; p = tmpbuf; debug(F111,"ftp get /rename",rcv_rename,0); zzstring(rcv_rename,&p,&n); debug(F111,"ftp get /rename",rcv_rename,0); p = tmpbuf; #else p = rcv_rename; #endif /* NOSPL */ rc = (zrename(s2,p) < 0) ? -1 : 1; debug(F111,"doftpget /RENAME zrename",p,rc); tlog(F110, (rc > -1) ? " renamed to" : " failed to rename to", p, 0 ); } else if (moving && rcv_move && !toscreen) { char *p; /* Move downloaded file */ #ifndef NOSPL char tmpbuf[CKMAXPATH+1]; int n; n = TMPBUFSIZ; p = tmpbuf; debug(F111,"ftp get /move-to",rcv_move,0); zzstring(rcv_move,&p,&n); p = tmpbuf; #else p = rcv_move; #endif /* NOSPL */ debug(F111,"ftp get /move-to",p,0); rc = (zrename(s2,p) < 0) ? -1 : 1; debug(F111,"doftpget /MOVE zrename",p,rc); tlog(F110, (rc > -1) ? " moved to" : " failed to move to", p, 0); } if (pv[SND_SRN].ival > 0 && pv[SND_SRN].sval) { char * s = pv[SND_SRN].sval; char * srvrn = pv[SND_SRN].sval; char tmpbuf[CKMAXPATH+1]; #ifndef NOSPL int y; /* Pass it thru the evaluator */ extern int cmd_quoting; /* for \v(filename) */ debug(F111,"ftp get srv_renam",s,1); if (cmd_quoting) { y = CKMAXPATH; s = (char *)tmpbuf; zzstring(srvrn,&s,&y); s = (char *)tmpbuf; } #endif /* NOSPL */ debug(F111,"ftp get srv_renam",s,1); if (s) if (*s) { int x; x = ftp_rename(s2,s); debug(F111,"ftp get ftp_rename",s2,x); tlog(F110, (x > 0) ? " renamed source file to" : " failed to rename source file to", s, 0 ); if (x < 1) return(-1); } } } } if (cancelfile) continue; if (rc < 0) { ftp_fai++; #ifdef FTP_TIMEOUT debug(F101,"ftp get ftp_timed_out","",ftp_timed_out); if (ftp_timed_out) { status = 0; ftscreen(SCR_EM,0,(CK_OFF_T)0,"GET timed out"); } #endif /* FTP_TIMEOUT */ if (geterror) { status = 0; ftscreen(SCR_EM,0,(CK_OFF_T)0,"Fatal download error"); done++; } } } #ifdef DEBUG if (deblog) { debug(F101,"ftp get status","",status); debug(F101,"ftp get cancelgroup","",cancelgroup); debug(F101,"ftp get cancelfile","",cancelfile); debug(F101,"ftp get selected","",selected); debug(F101,"ftp get good","",good); } #endif /* DEBUG */ if (selected == 0) { /* No files met selection criteria */ status = 1; /* which is a kind of success. */ } else if (status > 0) { /* Some files were selected */ if (cancelgroup) /* but MGET was canceled */ status = 0; /* so MGET failed */ else if (cancelfile && good < 1) /* If file was canceled */ status = 0; /* MGET failed if it got no files */ } success = status; x = success; debug(F101,"ftp get success","",success); xgetx: pipesend = pipesave; /* Restore global pipe selection */ if (fp_nml) { /* Close /NAMELIST */ if (fp_nml != stdout) fclose(fp_nml); fp_nml = NULL; } if ( #ifdef COMMENT x > -1 #else success #endif /* COMMENT */ ) { /* Download successful */ #ifdef GFTIMER t1 = gmstimer(); /* End time */ sec = (CKFLOAT)((CKFLOAT)(t1 - t0) / 1000.0); /* Stats */ if (!sec) sec = 0.001; fptsecs = sec; #else sec = (t1 - t0) / 1000; if (!sec) sec = 1; #endif /* GFTIMER */ tfcps = (long) (tfc / sec); tsecs = (int)sec; lastxfer = W_FTP|W_RECV; xferstat = success; } if (dpyactive) ftscreen(success > 0 ? SCR_TC : SCR_CW, 0, (CK_OFF_T)0, ""); #ifdef CK_TMPDIR if (f_tmpdir) { /* If we changed to download dir */ zchdir((char *) savdir); /* Go back where we came from */ f_tmpdir = 0; } #endif /* CK_TMPDIR */ for (i = 0; i <= SND_MAX; i++) { /* Free malloc'd memory */ if (pv[i].sval) free(pv[i].sval); } for (i = 0; i < mgetn; i++) /* MGET list too */ makestr(&(mgetlist[i]),NULL); if (cancelgroup) /* Clear temp-file stack */ mlsreset(); ftreset(); /* Undo switch effects */ dpyactive = 0; return(x); } static struct keytab ftprmt[] = { { "cd", XZCWD, 0 }, { "cdup", XZCDU, 0 }, { "cwd", XZCWD, CM_INV }, { "delete", XZDEL, 0 }, { "directory", XZDIR, 0 }, { "exit", XZXIT, 0 }, { "help", XZHLP, 0 }, { "login", XZLGI, 0 }, { "logout", XZLGO, 0 }, { "mkdir", XZMKD, 0 }, { "pwd", XZPWD, 0 }, { "rename", XZREN, 0 }, { "rmdir", XZRMD, 0 }, { "type", XZTYP, 0 }, { "", 0, 0 } }; static int nftprmt = (sizeof(ftprmt) / sizeof(struct keytab)) - 1; int doftpsite() { /* Send a SITE command */ int reply; char * s; int lcs = -1, rcs = -1; int save_vbm = ftp_vbm; #ifndef NOCSETS if (ftp_xla) { lcs = ftp_csl; if (lcs < 0) lcs = fcharset; rcs = ftp_csx; if (rcs < 0) rcs = ftp_csr; } #endif /* NOCSETS */ if ((x = cmtxt("Command", "", &s, xxstring)) < 0) return(x); CHECKCONN(); ckstrncpy(line,s,LINBUFSIZ); if (testing) printf(" ftp site \"%s\"...\n",line); if (!ftp_vbm) ftp_vbm = !ckstrcmp("HELP",line,4,0); if ((reply = ftpcmd("SITE",line,lcs,rcs,ftp_vbm)) == REPLY_PRELIM) { do { reply = getreply(0,lcs,rcs,ftp_vbm,0); } while (reply == REPLY_PRELIM); } ftp_vbm = save_vbm; return(success = (reply == REPLY_COMPLETE)); } int dosetftppsv() { /* Passive mode */ x = seton(&ftp_psv); if (x > 0) passivemode = ftp_psv; return(x); } /* d o f t p r m t -- Parse and execute REMOTE commands */ int doftprmt(cx,who) int cx, who; { /* who == 1 for ftp, 0 for kermit */ /* cx == 0 means REMOTE */ /* cx != 0 is a XZxxx value */ char * s; if (who != 0) return(0); if (cx == 0) { if ((x = cmkey(ftprmt,nftprmt,"","",xxstring)) < 0) return(x); cx = x; } switch (cx) { case XZCDU: /* CDUP */ if ((x = cmcfm()) < 0) return(x); return(doftpcdup()); case XZCWD: /* RCD */ if ((x = cmtxt("Remote directory", "", &s, xxstring)) < 0) return(x); ckstrncpy(line,s,LINBUFSIZ); s = brstrip(line); return(doftpcwd(s,1)); case XZPWD: /* RPWD */ return(doftppwd()); case XZDEL: /* RDEL */ return(doftpget(FTP_MDE,1)); case XZDIR: /* RDIR */ return(doftpdir(FTP_DIR)); case XZHLP: /* RHELP */ return(doftpxhlp()); case XZMKD: /* RMKDIR */ return(doftpmkd()); case XZREN: /* RRENAME */ return(doftpren()); case XZRMD: /* RRMDIR */ return(doftprmd()); case XZLGO: /* LOGOUT */ return(doftpres()); case XZXIT: /* EXIT */ return(ftpbye()); } printf("?Not usable with FTP - \"%s\"\n", atmbuf); return(-9); } int doxftp() { /* Command parser for built-in FTP */ int cx, n; struct FDB kw, fl; char * s; int usetls = 0; int lcs = -1, rcs = -1; #ifndef NOCSETS if (ftp_xla) { lcs = ftp_csl; if (lcs < 0) lcs = fcharset; rcs = ftp_csx; if (rcs < 0) rcs = ftp_csr; } #endif /* NOCSETS */ if (inserver) /* FTP not allowed in IKSD. */ return(-2); if (g_ftp_typ > -1) { /* Restore TYPE if saved */ ftp_typ = g_ftp_typ; /* g_ftp_typ = -1; */ } #ifdef COMMENT /* We'll set the collision action locally in doftpget() based on whether ftp_fnc was ever set to a value. if not, we'll use the fncact value. */ if (ftp_fnc < 0) /* Inherit global collision action */ ftp_fnc = fncact; /* if none specified for FTP */ #endif /* COMMENT */ /* Restore global verbose mode */ if (ftp_deb) ftp_vbm = 1; else if (quiet) ftp_vbm = 0; else ftp_vbm = ftp_vbx; ftp_dates &= 1; /* Undo any previous /UPDATE switch */ dpyactive = 0; /* Reset global transfer-active flag */ printlines = 0; /* Reset printlines */ if (fp_nml) { /* Reset /NAMELIST */ if (fp_nml != stdout) fclose(fp_nml); fp_nml = NULL; } makestr(&ftp_nml,NULL); cmfdbi(&kw, /* First FDB - commands */ _CMKEY, /* fcode */ "Hostname; or FTP command", /* help */ "", /* default */ "", /* addtl string data */ nftpcmd, /* addtl numeric data 1: tbl size */ 0, /* addtl numeric data 2: none */ xxstring, /* Processing function */ ftpcmdtab, /* Keyword table */ &fl /* Pointer to next FDB */ ); cmfdbi(&fl, /* A host name or address */ _CMFLD, /* fcode */ "Hostname or address", /* help */ "", /* default */ "", /* addtl string data */ 0, /* addtl numeric data 1 */ 0, /* addtl numeric data 2 */ xxstring, NULL, NULL ); x = cmfdb(&kw); /* Parse a hostname or a keyword */ if (x == -3) { printf("?ftp what? \"help ftp\" for hints\n"); return(-9); } if (x < 0) return(x); if (cmresult.fcode == _CMFLD) { /* If hostname */ return(openftp(cmresult.sresult,0)); /* go open the connection */ } else { cx = cmresult.nresult; } switch (cx) { case FTP_ACC: /* ACCOUNT */ if ((x = cmtxt("Remote account", "", &s, xxstring)) < 0) return(x); CHECKCONN(); makestr(&ftp_acc,s); if (testing) printf(" ftp account: \"%s\"\n",ftp_acc); success = (ftpcmd("ACCT",ftp_acc,-1,-1,ftp_vbm) == REPLY_COMPLETE); return(success); case FTP_GUP: /* Go UP */ if ((x = cmcfm()) < 0) return(x); CHECKCONN(); if (testing) printf(" ftp cd: \"(up)\"\n"); return(success = doftpcdup()); case FTP_CWD: /* CD */ if ((x = cmtxt("Remote directory", "", &s, xxstring)) < 0) return(x); CHECKCONN(); ckstrncpy(line,s,LINBUFSIZ); if (testing) printf(" ftp cd: \"%s\"\n", line); return(success = doftpcwd(line,1)); case FTP_CHM: /* CHMOD */ if ((x = cmfld("Permissions or protection code","",&s,xxstring)) < 0) return(x); ckstrncpy(tmpbuf,s,TMPBUFSIZ); if ((x = cmtxt("Remote filename", "", &s, xxstring)) < 0) return(x); CHECKCONN(); ckmakmsg(ftpcmdbuf,FTP_BUFSIZ,tmpbuf," ",s,NULL); if (testing) printf(" ftp chmod: %s\n",ftpcmdbuf); success = (ftpcmd("SITE CHMOD",ftpcmdbuf,lcs,rcs,ftp_vbm) == REPLY_COMPLETE); return(success); case FTP_CLS: /* CLOSE FTP connection */ if ((y = cmcfm()) < 0) return(y); CHECKCONN(); if (testing) printf(" ftp closing...\n"); ftpclose(); return(success = 1); case FTP_DIR: /* DIRECTORY of remote files */ case FTP_VDI: return(doftpdir(cx)); case FTP_GET: /* GET a remote file */ case FTP_RGE: /* REGET */ case FTP_MGE: /* MGET */ case FTP_MDE: /* MDELETE */ return(doftpget(cx,1)); case FTP_IDL: /* IDLE */ if ((x = cmnum("Number of seconds","-1",10,&z,xxstring)) < 0) return(x); if ((y = cmcfm()) < 0) return(y); CHECKCONN(); if (z < 0) { /* Display idle timeout */ if (testing) printf(" ftp query idle timeout...\n"); success = (ftpcmd("SITE IDLE",NULL,0,0,1) == REPLY_COMPLETE); } else { /* Set idle timeout */ if (testing) printf(" ftp idle timeout set: %d...\n",z); success = (ftpcmd("SITE IDLE",ckitoa(z),0,0,1) == REPLY_COMPLETE); } return(success); case FTP_MKD: /* MKDIR */ return(doftpmkd()); case FTP_MOD: /* MODTIME */ if ((x = cmtxt("Remote filename", "", &s, xxstring)) < 0) return(x); CHECKCONN(); ckstrncpy(line,s,LINBUFSIZ); if (testing) printf(" ftp modtime \"%s\"...\n",line); success = 0; if (ftpcmd("MDTM",line,lcs,rcs,ftp_vbm) == REPLY_COMPLETE) { success = 1; mdtmok = 1; if (!quiet) { int flag = 0; char c, * s; struct tm tmremote; bzero((char *)&tmremote, sizeof(struct tm)); s = ftp_reply_str; while ((c = *s++)) { if (c == SP) { flag++; break; } } if (flag) { if (sscanf(s, "%04d%02d%02d%02d%02d%02d", &tmremote.tm_year, &tmremote.tm_mon, &tmremote.tm_mday, &tmremote.tm_hour, &tmremote.tm_min, &tmremote.tm_sec ) == 6) { printf(" %s %04d-%02d-%02d %02d:%02d:%02d GMT\n", line, tmremote.tm_year, tmremote.tm_mon, tmremote.tm_mday, tmremote.tm_hour, tmremote.tm_min, tmremote.tm_sec ); } else { success = 0; } } } } return(success); case FTP_OPN: /* OPEN connection */ #ifdef COMMENT x = cmfld("IP hostname or address","",&s,xxstring); if (x < 0) { success = 0; return(x); } ckstrncpy(line,s,LINBUFSIZ); s = line; return(openftp(s,0)); #else { /* OPEN connection */ char name[TTNAMLEN+1], *p; extern int network; extern char ttname[]; if (network) /* If we have a current connection */ ckstrncpy(name,ttname,LINBUFSIZ); /* get the host name */ else *name = '\0'; /* as default host */ for (p = name; *p; p++) /* Remove ":service" from end. */ if (*p == ':') { *p = '\0'; break; } #ifndef USETLSTAB x = cmfld("IP hostname or address",name,&s,xxstring); #else cmfdbi(&kw, /* First FDB - commands */ _CMKEY, /* fcode */ "Hostname or switch", /* help */ "", /* default */ "", /* addtl string data */ ntlstab, /* addtl numeric data 1: tbl size */ 0, /* addtl numeric data 2: none */ xxstring, /* Processing function */ tlstab, /* Keyword table */ &fl /* Pointer to next FDB */ ); cmfdbi(&fl, /* A host name or address */ _CMFLD, /* fcode */ "Hostname or address", /* help */ "", /* default */ "", /* addtl string data */ 0, /* addtl numeric data 1 */ 0, /* addtl numeric data 2 */ xxstring, NULL, NULL ); for (n = 0;; n++) { x = cmfdb(&kw); /* Parse a hostname or a keyword */ if (x == -3) { printf("?ftp open what? \"help ftp\" for hints\n"); return(-9); } if (x < 0) break; if (cmresult.fcode == _CMFLD) { /* Hostname */ s = cmresult.sresult; break; } else if (cmresult.nresult == OPN_TLS) { usetls = 1; } } #endif /* USETLSTAB */ if (x < 0) { success = 0; return(x); } ckstrncpy(line,s,LINBUFSIZ); s = line; return(openftp(s,usetls)); } #endif /* COMMENT */ case FTP_PUT: /* PUT */ case FTP_MPU: /* MPUT */ case FTP_APP: /* APPEND */ case FTP_REP: /* REPUT */ return(doftpput(cx,1)); case FTP_PWD: /* PWD */ x = doftppwd(); if (x > -1) success = x; return(x); case FTP_REN: /* RENAME */ return(doftpren()); case FTP_RES: /* RESET */ return(doftpres()); case FTP_HLP: /* (remote) HELP */ return(doftpxhlp()); case FTP_RMD: /* RMDIR */ return(doftprmd()); case FTP_STA: /* STATUS */ if ((x = cmtxt("Command", "", &s, xxstring)) < 0) return(x); CHECKCONN(); ckstrncpy(line,s,LINBUFSIZ); if (testing) printf(" ftp status \"%s\"...\n",line); success = (ftpcmd("STAT",line,lcs,rcs,1) == REPLY_COMPLETE); return(success); case FTP_SIT: { /* SITE */ return(doftpsite()); } case FTP_SIZ: /* (ask for) SIZE */ if ((x = cmtxt("Remote filename", "", &s, xxstring)) < 0) return(x); CHECKCONN(); ckstrncpy(line,s,LINBUFSIZ); if (testing) printf(" ftp size \"%s\"...\n",line); success = (ftpcmd("SIZE",line,lcs,rcs,1) == REPLY_COMPLETE); if (success) sizeok = 1; return(success); case FTP_SYS: /* Ask for server's SYSTEM type */ if ((x = cmcfm()) < 0) return(x); CHECKCONN(); if (testing) printf(" ftp system...\n"); success = (ftpcmd("SYST",NULL,0,0,1) == REPLY_COMPLETE); return(success); case FTP_UMA: /* Set/query UMASK */ if ((x = cmfld("Umask to set or nothing to query","",&s,xxstring)) < 0) if (x != -3) return(x); ckstrncpy(tmpbuf,s,TMPBUFSIZ); if ((x = cmcfm()) < 0) return(x); CHECKCONN(); if (testing) { if (tmpbuf[0]) printf(" ftp umask \"%s\"...\n",tmpbuf); else printf(" ftp query umask...\n"); } success = ftp_umask(tmpbuf); return(success); case FTP_USR: return(doftpusr()); case FTP_QUO: if ((x = cmtxt("FTP protocol command", "", &s, xxstring)) < 0) return(x); CHECKCONN(); success = (ftpcmd(s,NULL,0,0,ftp_vbm) == REPLY_COMPLETE); return(success); case FTP_TYP: /* Type */ if ((x = cmkey(ftptyp,nftptyp,"","",xxstring)) < 0) return(x); if ((y = cmcfm()) < 0) return(y); CHECKCONN(); ftp_typ = x; g_ftp_typ = x; tenex = (ftp_typ == FTT_TEN); changetype(ftp_typ,ftp_vbm); return(1); case FTP_CHK: /* Check if remote file(s) exist(s) */ if ((x = cmtxt("remote filename", "", &s, xxstring)) < 0) return(x); CHECKCONN(); success = remote_files(1,(CHAR *)s,(CHAR *)s,0) ? 1 : 0; return(success); case FTP_FEA: /* RFC2389 */ if ((y = cmcfm()) < 0) return(y); CHECKCONN(); success = (ftpcmd("FEAT",NULL,0,0,1) == REPLY_COMPLETE); if (success) { if (sfttab[0] > 0) { ftp_aut = sfttab[SFT_AUTH]; sizeok = sfttab[SFT_SIZE]; mdtmok = sfttab[SFT_MDTM]; mlstok = sfttab[SFT_MLST]; } } return(success); case FTP_OPT: /* RFC2389 */ /* Perhaps this should be a keyword list... */ if ((x = cmfld("FTP command","",&s,xxstring)) < 0) return(x); CHECKCONN(); ckstrncpy(line,s,LINBUFSIZ); if ((x = cmtxt("Options for this command", "", &s, xxstring)) < 0) return(x); success = (ftpcmd("OPTS",line,lcs,rcs,ftp_vbm) == REPLY_COMPLETE); return(success); case FTP_ENA: /* FTP ENABLE */ case FTP_DIS: /* FTP DISABLE */ if ((x = cmkey(ftpenatab,nftpena,"","",xxstring)) < 0) return(x); if ((y = cmcfm()) < 0) return(y); switch (x) { case ENA_AUTH: /* OK to use autoauthentication */ ftp_aut = (cx == FTP_ENA) ? 1 : 0; sfttab[SFT_AUTH] = ftp_aut; break; case ENA_FEAT: /* OK to send FEAT command */ featok = (cx == FTP_ENA) ? 1 : 0; break; case ENA_MLST: /* OK to use MLST/MLSD */ mlstok = (cx == FTP_ENA) ? 1 : 0; sfttab[SFT_MLST] = mlstok; break; case ENA_MDTM: /* OK to use MDTM */ mdtmok = (cx == FTP_ENA) ? 1 : 0; sfttab[SFT_MDTM] = mdtmok; break; case ENA_SIZE: /* OK to use SIZE */ sizeok = (cx == FTP_ENA) ? 1 : 0; sfttab[SFT_SIZE] = sizeok; break; } return(success = 1); } return(-2); } #ifndef NOSHOW static char * shopl(x) int x; { switch (x) { case FPL_CLR: return("clear"); case FPL_PRV: return("private"); case FPL_SAF: return("safe"); case 0: return("(not set)"); default: return("(unknown)"); } } int shoftp(brief) int brief; { char * s = "?"; int n, x; if (g_ftp_typ > -1) { /* Restore TYPE if saved */ ftp_typ = g_ftp_typ; /* g_ftp_typ = -1; */ } printf("\n"); printf("FTP connection: %s\n",connected ? ftp_host : "(none)" ); n = 2; if (connected) { n++; printf("FTP server type: %s\n", ftp_srvtyp[0] ? ftp_srvtyp : "(unknown)"); } if (loggedin) printf("Logged in as: %s\n", strval(ftp_logname,"(unknown)")); else printf("Not logged in\n"); n++; if (brief) return(0); printf("\nSET FTP values:\n\n"); n += 3; printf(" ftp anonymous-password: %s\n", ftp_apw ? ftp_apw : "(default)" ); printf(" ftp auto-login: %s\n",showoff(ftp_log)); printf(" ftp auto-authentication: %s\n",showoff(ftp_aut)); switch (ftp_typ) { case FTT_ASC: s = "text"; break; case FTT_BIN: s = "binary"; break; case FTT_TEN: s = "tenex"; break; } #ifdef FTP_TIMEOUT printf(" ftp timeout: %ld\n",ftp_timeout); #endif /* FTP_TIMEOUT */ printf(" ftp type: %s\n",s); printf(" ftp get-filetype-switching: %s\n",showoff(get_auto)); printf(" ftp dates: %s\n",showoff(ftp_dates)); printf(" ftp error-action: %s\n",ftp_err ? "quit":"proceed"); printf(" ftp filenames: %s\n", ftp_cnv == CNV_AUTO ? "auto" : (ftp_cnv ? "converted" : "literal") ); printf(" ftp debug %s\n",showoff(ftp_deb)); printf(" ftp passive-mode: %s\n",showoff(ftp_psv)); printf(" ftp permissions: %s\n",showooa(ftp_prm)); printf(" ftp verbose-mode: %s\n",showoff(ftp_vbx)); printf(" ftp send-port-commands: %s\n",showoff(ftp_psv)); printf(" ftp unique-server-names: %s\n",showoff(ftp_usn)); #ifdef COMMENT /* See note in doxftp() */ if (ftp_fnc < 0) ftp_fnc = fncact; #endif /* COMMENT */ printf(" ftp collision: %s\n", fncnam[ftp_fnc > -1 ? ftp_fnc : fncact]); printf(" ftp server-time-offset: %s\n", fts_sto ? fts_sto : "(none)"); n += 15; #ifndef NOCSETS printf(" ftp character-set-translation: %s\n",showoff(ftp_xla)); if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; } printf(" ftp server-character-set: %s\n",fcsinfo[ftp_csr].keyword); if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; } printf(" file character-set: %s\n",fcsinfo[fcharset].keyword); if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; } #endif /* NOCSETS */ x = ftp_dis; if (x < 0) x = fdispla; switch (x) { case XYFD_N: s = "none"; break; case XYFD_R: s = "serial"; break; case XYFD_C: s = "fullscreen"; break; case XYFD_S: s = "crt"; break; case XYFD_B: s = "brief"; break; } printf(" ftp display: %s\n",s); if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; } if (mlstok || featok || mdtmok || sizeok || ftp_aut) { printf(" enabled: "); if (ftp_aut) printf(" AUTH"); if (featok) printf(" FEAT"); if (mdtmok) printf(" MDTM"); if (mlstok) printf(" MLST"); if (sizeok) printf(" SIZE"); printf("\n"); if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; } } if (!mlstok || !featok || !mdtmok || !sizeok || !ftp_aut) { printf(" disabled: "); if (!ftp_aut) printf(" AUTH"); if (!featok) printf(" FEAT"); if (!mdtmok) printf(" MDTM"); if (!mlstok) printf(" MLST"); if (!sizeok) printf(" SIZE"); printf("\n"); if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; } } switch (ftpget) { case 0: s = "kermit"; break; case 1: s = "ftp"; break; case 2: s = "auto"; break; default: s = "?"; } printf(" get-put-remote: %s\n",s); if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; } printf("\n"); if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; } #ifdef FTP_SECURITY printf("Available security methods: "); #ifdef FTP_GSSAPI printf("GSSAPI "); #endif /* FTP_GSSAPI */ #ifdef FTP_KRB4 printf("Kerberos4 "); #endif /* FTP_KRB4 */ #ifdef FTP_SRP printf("SRP "); #endif /* FTP_SRP */ #ifdef FTP_SSL printf("SSL "); #endif /* FTP_SSL */ n++; printf("\n\n"); if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; } printf(" ftp authtype: %s\n",strval(auth_type,NULL)); if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; } printf(" ftp auto-encryption: %s\n",showoff(ftp_cry)); if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; } printf(" ftp credential-forwarding: %s\n",showoff(ftp_cfw)); if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; } printf(" ftp command-protection-level: %s\n",shopl(ftp_cpl)); if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; } printf(" ftp data-protection-level: %s\n",shopl(ftp_dpl)); if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; } printf(" ftp secure proxy: %s\n",shopl(ssl_ftp_proxy)); if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; } #else printf("Available security methods: (none)\n"); if (++ n > cmd_rows-3) { if (!askmore()) return 0; else n = 0; } #endif /* FTP_SECURITY */ if (n <= cmd_rows - 3) printf("\n"); return(0); } #endif /* NOSHOW */ #ifndef NOHELP /* FTP HELP text strings */ static char * fhs_ftp[] = { "Syntax: FTP subcommand [ operands ]", " Makes an FTP connection, or sends a command to the FTP server.", " To see a list of available FTP subcommands, type \"ftp ?\".", " and then use HELP FTP xxx to get help about subcommand xxx.", " Also see HELP SET FTP, HELP SET GET-PUT-REMOTE, and HELP FIREWALL.", "" }; static char * fhs_acc[] = { /* ACCOUNT */ "Syntax: FTP ACCOUNT text", " Sends an account designator to an FTP server that needs one.", " Most FTP servers do not use accounts; some use them for other", " other purposes, such as disk-access passwords.", "" }; static char * fhs_app[] = { /* APPEND */ "Syntax: FTP APPEND filname", " Equivalent to [ FTP ] PUT /APPEND. See HELP FTP PUT.", "" }; static char * fhs_cls[] = { /* BYE, CLOSE */ "Syntax: [ FTP ] BYE", " Logs out from the FTP server and closes the FTP connection.", " Also see HELP SET GET-PUT-REMOTE. Synonym: [ FTP ] CLOSE.", "" }; static char * fhs_cwd[] = { /* CD, CWD */ "Syntax: [ FTP ] CD directory", " Asks the FTP server to change to the given directory.", " Also see HELP SET GET-PUT-REMOTE. Synonyms: [ FTP ] CWD, RCD, RCWD.", "" }; static char * fhs_gup[] = { /* CDUP, UP */ "Syntax: FTP CDUP", " Asks the FTP server to change to the parent directory of its current", " directory. Also see HELP SET GET-PUT-REMOTE. Synonym: FTP UP.", "" }; static char * fhs_chm[] = { /* CHMOD */ "Syntax: FTP CHMOD filename permissions", " Asks the FTP server to change the permissions, protection, or mode of", " the given file. The given permissions must be in the syntax of the", " the server's file system, e.g. an octal number for UNIX. Also see", " FTP PUT /PERMISSIONS", "" }; static char * fhs_mde[] = { /* DELETE */ "Syntax: FTP DELETE [ switches ] filespec", " Asks the FTP server to delete the given file or files.", " Synonym: MDELETE (Kermit makes no distinction between single and", " multiple file deletion). Optional switches:", " ", " /ERROR-ACTION:{PROCEED,QUIT}", " /EXCEPT:pattern", " /FILENAMES:{AUTO,CONVERTED,LITERAL}", " /LARGER-THAN:number", #ifdef UNIXOROSK " /NODOTFILES", #endif /* UNIXOROSK */ " /QUIET", #ifdef RECURSIVE " /RECURSIVE (depends on server)", " /SUBDIRECTORIES", #endif /* RECURSIVE */ " /SMALLER-THAN:number", "" }; static char * fhs_dir[] = { /* DIRECTORY */ "Syntax: FTP DIRECTORY [ filespec ]", " Asks the server to send a directory listing of the files that match", " the given filespec, or if none is given, all the files in its current", " directory. The filespec, including any wildcards, must be in the", " syntax of the server's file system. Also see HELP SET GET-PUT-REMOTE.", " Synonym: RDIRECTORY.", "" }; static char * fhs_vdi[] = { /* VDIRECTORY */ "Syntax: FTP VDIRECTORY [ filespec ]", " Asks the server to send a directory listing of the files that match", " the given filespec, or if none is given, all the files in its current", " directory. VDIRECTORY is needed for getting verbose directory", " listings from certain FTP servers, such as on TOPS-20. Try it if", " FTP DIRECTORY lists only filenames without details.", "" }; static char * fhs_fea[] = { /* FEATURES */ "Syntax: FTP FEATURES", " Asks the FTP server to list its special features. Most FTP servers", " do not recognize this command.", "" }; static char * fhs_mge[] = { /* MGET */ "Syntax: [ FTP ] MGET [ options ] filespec [ filespec [ filespec ... ] ]", " Download a single file or multiple files. Asks the FTP server to send", " the given file or files. Also see FTP GET. Optional switches:", " ", " /AS-NAME:text", " Name under which to store incoming file.", " Pattern required for for multiple files.", " /BINARY", /* /IMAGE */ " Force binary mode. Synonym: /IMAGE.", " /COLLISION:{BACKUP,RENAME,UPDATE,DISCARD,APPEND,OVERWRITE}", " What to do if an incoming file has the same name as an existing file.", #ifdef PUTPIPE " /COMMAND", " Specifies that the as-name is a command to which the incoming file", " is to be piped as standard input.", #endif /* PUTPIPE */ #ifdef DOUPDATE " /DATES-DIFFER", " Download only those files whose modification date-times differ from", " those of the corresponding local files, or that do not already", " exist on the local computer.", #endif /* DOUPDATE */ " /DELETE", " Specifies that each file is to be deleted from the server after,", " and only if, it is successfully downloaded.", " /ERROR-ACTION:{PROCEED,QUIT}", " When downloading a group of files, what to do upon failure to", " transfer a file: quit or proceed to the next one.", " /EXCEPT:pattern", " Exception list: don't download any files that match this pattern.", " See HELP WILDCARD for pattern syntax.", " /FILENAMES:{AUTOMATIC,CONVERTED,LITERAL}", " Whether to convert incoming filenames to local syntax.", #ifdef PIPESEND #ifndef NOSPL " /FILTER:command", " Pass incoming files through the given command.", #endif /* NOSPL */ #endif /* PIPESEND */ " /LARGER-THAN:number", " Only download files that are larger than the given number of bytes.", " /LISTFILE:filename", " Obtain the list of files to download from the given file.", #ifndef NOCSETS " /LOCAL-CHARACTER-SET:name", " When downloading in text mode and character-set conversion is", " desired, this specifies the target set.", #endif /* NOCSETS */ " /MATCH:pattern", " Specifies a pattern to be used to select filenames locally from the", " server's list.", " /MLSD", " Forces sending of MLSD (rather than NLST) to get the file list.", #ifdef CK_TMPDIR " /MOVE-TO:directory", " Each file that is downloaded is to be moved to the given local", " directory immediately after, and only if, it has been received", " successfully.", #endif /* CK_TMPDIR */ " /NAMELIST:filename", " Instead of downloading the files, stores the list of files that", " would be downloaded in the given local file, one filename per line.", " /NLST", " Forces sending of NLST (rather than MLSD) to get the file list.", " /NOBACKUPFILES", " Don't download any files whose names end with .~~.", " /NODOTFILES", " Don't download any files whose names begin with period (.).", " /QUIET", " Suppress the file-transfer display.", #ifdef FTP_RESTART " /RECOVER", /* /RESTART */ " Resume a download that was previously interrupted from the point of", " failure. Works only in binary mode. Not supported by all servers.", " Synonym: /RESTART.", #endif /* FTP_RESTART */ #ifdef RECURSIVE " /RECURSIVE", /* /SUBDIRECTORIES */ " Create subdirectories automatically if the server sends files", " recursively and includes pathnames (most don't).", #endif /* RECURSIVE */ " /RENAME-TO:text", " Each file that is downloaded is to be renamed as indicated just,", " after, and only if, it has arrived successfully.", #ifndef NOCSETS " /SERVER-CHARACTER-SET:name", " When downloading in text mode and character-set conversion is desired" , " this specifies the original file's character set on the server.", #endif /* NOCSETS */ " /SERVER-RENAME:text", " Each server source file is to be renamed on the server as indicated", " immediately after, but only if, it has arrived successfully.", " /SMALLER-THAN:number", " Download only those files smaller than the given number of bytes.", " /TEXT", /* /ASCII */ " Force text mode. Synonym: /ASCII.", " /TENEX", " Force TENEX (TOPS-20) mode (see HELP SET FTP TYPE).", #ifndef NOCSETS " /TRANSPARENT", " When downloading in text mode, do not convert chracter-sets.", #endif /* NOCSETS */ " /TO-SCREEN", " The downloaded file is to be displayed on the screen.", #ifdef DOUPDATE " /UPDATE", " Equivalent to /COLLISION:UPDATE. Download only those files that are", " newer than than their local counterparts, or that do not exist on", " the local computer.", #endif /* DOUPDATE */ "" }; static char * fhs_hlp[] = { /* HELP */ "Syntax: FTP HELP [ command [ subcommand... ] ]", " Asks the FTP server for help about the given command. First use", " FTP HELP by itself to get a list of commands, then use HELP FTP xxx", " to get help for command \"xxx\". Synonyms: REMOTE HELP, RHELP.", "" }; static char * fhs_idl[] = { /* IDLE */ "Syntax: FTP IDLE [ number ]", " If given without a number, this asks the FTP server to tell its", " current idle-time limit. If given with a number, it asks the server", " to change its idle-time limit to the given number of seconds.", "" }; static char * fhs_usr[] = { /* USER, LOGIN */ "Syntax: FTP USER username [ password [ account ] ]", " Log in to the FTP server. To be used when connected but not yet", " logged in, e.g. when SET FTP AUTOLOGIN is OFF or autologin failed.", " If you omit the password, and one is required by the server, you are", " prompted for it. If you omit the account, no account is sent.", " Synonym: FTP LOGIN.", "" }; static char * fhs_get[] = { /* GET */ "Syntax: [ FTP ] GET [ options ] filename [ as-name ]", " Download a single file. Asks the FTP server to send the given file.", " The optional as-name is the name to store it under when it arrives;", " if omitted, the file is stored with the name it arrived with, as", " modified according to the FTP FILENAMES setting or /FILENAMES: switch", " value. Aside from the file list and as-name, syntax and options are", " the same as for FTP MGET, which is used for downloading multiple files." , "" }; static char * fhs_mkd[] = { /* MKDIR */ "Syntax: FTP MKDIR directory", " Asks the FTP server to create a directory with the given name,", " which must be in the syntax of the server's file system. Synonyms:", " REMOTE MKDIR, RMKDIR.", "" }; static char * fhs_mod[] = { /* MODTIME */ "Syntax: FTP MODTIME filename", " Asks the FTP server to send the modification time of the given file,", " to be displayed on the screen. The date-time format is all numeric:", " yyyymmddhhmmssxxx... (where xxx... is 0 or more digits indicating", " fractions of seconds).", "" }; static char * fhs_mpu[] = { /* MPUT */ "Syntax: [ FTP ] MPUT [ switches ] filespec [ filespec [ filespec ... ] ]", " Uploads files. Sends the given file or files to the FTP server.", " Also see FTP PUT. Optional switches are:", " ", " /AFTER:date-time", " Uploads only those files newer than the given date-time.", " HELP DATE for info about date-time formats. Synonym: /SINCE.", #ifdef PUTARRAY " /ARRAY:array-designator", " Tells Kermit to upload the contents of the given array, rather than", " a file.", #endif /* PUTARRAY */ " /AS-NAME:text", " Name under which to send files.", " Pattern required for for multiple files.", " /BEFORE:date-time", " Upload only those files older than the given date-time.", " /BINARY", " Force binary mode. Synonym: /IMAGE.", #ifdef PUTPIPE " /COMMAND", " Specifies that the filespec is a command whose standard output is", " to be sent.", #endif /* PUTPIPE */ #ifdef COMMENT #ifdef DOUPDATE " /DATES-DIFFER", " Upload only those files whose modification date-times differ from", " those on the server, or that don't exist on the server at all.", #endif /* DOUPDATE */ #endif /* COMMENT */ " /DELETE", " Specifies that each source file is to be deleted after, and only if,", " it is successfully uploaded.", " /DOTFILES", " Include files whose names begin with period (.).", " /ERROR-ACTION:{PROCEED,QUIT}", " When uploading a group of files, what to do upon failure to", " transfer a file: quit or proceed to the next one.", " /EXCEPT:pattern", " Exception list: don't upload any files that match this pattern.", " See HELP WILDCARD for pattern syntax.", " /FILENAMES:{AUTOMATIC,CONVERTED,LITERAL}", " Whether to convert outbound filenames to common syntax.", #ifdef PIPESEND #ifndef NOSPL " /FILTER:command", " Pass outbound files through the given command.", #endif /* NOSPL */ #endif /* PIPESEND */ #ifdef CKSYMLINK " /FOLLOWINKS", " Send files that are pointed to by symbolic links.", " /NOFOLLOWINKS", " Skip over symbolic links (default).", #endif /* CKSYMLINK */ " /LARGER-THAN:number", " Only upload files that are larger than the given number of bytes.", " /LISTFILE:filename", " Obtain the list of files to upload from the given file.", #ifndef NOCSETS " /LOCAL-CHARACTER-SET:name", " When uploading in text mode and character-set conversion is", " desired, this specifies the source-file character set.", #endif /* NOCSETS */ #ifdef CK_TMPDIR " /MOVE-TO:directory", " Each source file that is uploaded is to be moved to the given local", " directory when, and only if, the transfer is successful.", #endif /* CK_TMPDIR */ " /NOBACKUPFILES", " Don't upload any files whose names end with .~~.", #ifdef UNIXOROSK " /NODOTFILES", " Don't upload any files whose names begin with period (.).", #endif /* UNIXOROSK */ " /NOT-AFTER:date-time", " Upload only files that are not newer than the given date-time", " /NOT-BEFORE:date-time", " Upload only files that are not older than the given date-time", #ifdef UNIX " /PERMISSIONS", " Ask the server to set the permissions of each file it receives", " according to the source file's permissions.", #endif /* UNIX */ " /QUIET", " Suppress the file-transfer display.", #ifdef FTP_RESTART " /RECOVER", " Resume an upload that was previously interrupted from the point of", " failure. Synonym: /RESTART.", #endif /* FTP_RESTART */ #ifdef RECURSIVE " /RECURSIVE", " Send files from the given directory and all the directories beneath", " it. Synonym: /SUBDIRECTORIES.", #endif /* RECURSIVE */ " /RENAME-TO:text", " Each source file that is uploaded is to be renamed on the local", " local computer as indicated when and only if, the transfer completes", " successfully.", #ifndef NOCSETS " /SERVER-CHARACTER-SET:name", " When uploading in text mode and character-set conversion is desired,", " this specifies the character set to which the file should be", " converted for storage on the server.", #endif /* NOCSETS */ " /SERVER-RENAME:text", " Each file that is uploaded is to be renamed as indicated on the", " server after, and only if, if arrives successfully.", " /SIMULATE", " Show which files would be sent without actually sending them.", " /SMALLER-THAN:number", " Upload only those files smaller than the given number of bytes.", " /TEXT", " Force text mode. Synonym: /ASCII.", " /TENEX", " Force TENEX (TOPS-20) mode (see HELP SET FTP TYPE).", #ifndef NOCSETS " /TRANSPARENT", " When uploading in text mode, do not convert chracter-sets.", #endif /* NOCSETS */ " /TYPE:{TEXT,BINARY}", " Upload only files of the given type.", #ifdef DOUPDATE " /UPDATE", " If a file of the same name exists on the server, upload only if", " the local file is newer.", #endif /* DOUPDATE */ " /UNIQUE-SERVER-NAMES", " Ask the server to compute new names for any incoming file that has", " the same name as an existing file.", "" }; static char * fhs_opn[] = { /* OPEN */ #ifdef CK_SSL "Syntax: FTP [ OPEN ] [ { /SSL, /TLS } ] hostname [ port ] [ switches ]", " Opens a connection to the FTP server on the given host. The default", " TCP port is 21 (990 if SSL/TLS is used), but a different port number", " can be supplied if necessary. Optional switches are:", #else /* CK_SSL */ "Syntax: FTP [ OPEN ] hostname [ port ] [ switches ]", " Opens a connection to the FTP server on the given host. The default", " TCP port is 21, but a different port number can be supplied if", " necessary. Optional switches are:", #endif /* CK_SSL */ " ", " /ANONYMOUS", " Logs you in anonymously.", " /USER:text", " Supplies the given text as your username.", " /PASSWORD:text", " Supplies the given text as your password. If you include a username", " but omit this switch and the server requires a password, you are", " prompted for it.", " /ACCOUNT:text", " Supplies the given text as your account, if required by the server.", " /ACTIVE", " Forces an active (rather than passive) connection.", " /PASSIVE", " Forces a passive (rather than active) connection.", " /NOINIT", " Inhibits sending initial REST, STRU, and MODE commands, which are", " well-known standard commands, but to which some servers react badly.", " /NOLOGIN", " Inhibits autologin for this connection only.", "" }; static char * fhs_opt[] = { /* OPTS, OPTIONS */ "Syntax: FTP OPTIONS", " Asks the FTP server to list its current options. Advanced, new,", " not supported by most FTP servers.", "" }; static char * fhs_put[] = { /* PUT, SEND */ "Syntax: [ FTP ] PUT [ switches ] filespec [ as-name ]", " Like FTP MPUT, but only one filespec is allowed, and if it is followed", " by an additional field, this is interpreted as the name under which", " to send the file or files. See HELP FTP MPUT.", "" }; static char * fhs_reput[] = { /* REPUT, RESEND */ "Syntax: [ FTP ] REPUT [ switches ] filespec [ as-name ]", " Synonym for FTP PUT /RECOVER. Recovers an interrupted binary-mode", " upload from the point of failure if the FTP server supports recovery.", " Synonym: [ FTP ] RESEND. For details see HELP FTP MPUT.", "" }; static char * fhs_pwd[] = { /* PWD */ "Syntax: FTP PWD", " Asks the FTP server to reveal its current working directory.", " Synonyms: REMOTE PWD, RPWD.", "" }; static char * fhs_quo[] = { /* QUOTE */ "Syntax: FTP QUOTE text", " Sends an FTP protocol command to the FTP server. Use this command", " for sending commands that Kermit might not support.", "" }; static char * fhs_rge[] = { /* REGET */ "Syntax: FTP REGET", " Synonym for FTP GET /RECOVER.", "" }; static char * fhs_ren[] = { /* RENAME */ "Syntax: FTP RENAME name1 name1", " Asks the FTP server to change the name of the file whose name is name1", " and which resides in the FTP server's file system, to name2. Works", " only for single files; wildcards are not accepted.", "" }; static char * fhs_res[] = { /* RESET */ "Syntax: FTP RESET", " Asks the server to log out your session, terminating your access", " rights, without closing the connection.", "" }; static char * fhs_rmd[] = { /* RMDIR */ "Syntax: FTP RMDIR directory", " Asks the FTP server to remove the directory whose name is given.", " This usually requires the directory to be empty. Synonyms: REMOTE", " RMDIR, RRMDIR.", "" }; static char * fhs_sit[] = { /* SITE */ "Syntax: FTP SITE text", " Sends a site-specific command to the FTP server.", "" }; static char * fhs_siz[] = { /* SIZE */ "Syntax: FTP SIZE filename", " Asks the FTP server to send a numeric string representing the size", " of the given file.", "" }; static char * fhs_sta[] = { /* STATUS */ "Syntax: FTP STATUS [ filename ]", " Asks the FTP server to report its status. If a filename is given,", " the FTP server should report details about the file.", "" }; static char * fhs_sys[] = { /* SYSTEM */ "Syntax: FTP SYSTEM", " Asks the FTP server to report its operating system type.", "" }; static char * fhs_typ[] = { /* TYPE */ "Syntax: FTP TYPE { TEXT, BINARY, TENEX }", " Puts the client and server in the indicated transfer mode.", " ASCII is a synonym for TEXT. TENEX is used only for uploading 8-bit", " binary files to a 36-bit platforms such as TENEX or TOPS-20 and/or", " downloading files from TENEX or TOPS-20 that have been uploaded in", " TENEX mode.", "" }; static char * fhs_uma[] = { /* UMASK */ "Syntax: FTP UMASK number", " Asks the FTP server to set its file creation mode mask. Applies", " only (or mainly) to UNIX-based FTP servers.", "" }; static char * fhs_chk[] = { /* CHECK */ "Syntax: FTP CHECK remote-filespec", " Asks the FTP server if the given file or files exist. If the", " remote-filespec contains wildcards, this command fails if no server", " files match, and succeeds if at least one file matches. If the", " remote-filespec does not contain wildcards, this command succeeds if", " the given file exists and fails if it does not.", "" }; static char * fhs_ena[] = { /* ENABLE */ "Syntax: FTP ENABLE { AUTH, FEAT, MDTM, MLST, SIZE }", " Enables the use of the given FTP protocol command in case it has been", " disabled (but this is no guarantee that the FTP server understands it)." , " Use SHOW FTP to see which of these commands is enabled and disabled.", " Also see FTP DISABLE.", "" }; static char * fhs_dis[] = { /* DISABLE */ "Syntax: FTP DISABLE { AUTH, FEAT, MDTM, MLST, SIZE }", " Disables the use of the given FTP protocol command.", " Also see FTP ENABLE.", "" }; #endif /* NOHELP */ int doftphlp() { int cx; if ((cx = cmkey(ftpcmdtab,nftpcmd,"","",xxstring)) < 0) if (cx != -3) return(cx); if ((x = cmcfm()) < 0) return(x); #ifdef NOHELP printf("Sorry, no help available\n"); #else switch (cx) { case -3: return(hmsga(fhs_ftp)); case FTP_ACC: /* ACCOUNT */ return(hmsga(fhs_acc)); case FTP_APP: /* APPEND */ return(hmsga(fhs_app)); case FTP_CLS: /* BYE, CLOSE */ return(hmsga(fhs_cls)); case FTP_CWD: /* CD, CWD */ return(hmsga(fhs_cwd)); case FTP_GUP: /* CDUP, UP */ return(hmsga(fhs_gup)); case FTP_CHM: /* CHMOD */ return(hmsga(fhs_chm)); case FTP_MDE: /* DELETE, MDELETE */ return(hmsga(fhs_mde)); case FTP_DIR: /* DIRECTORY */ return(hmsga(fhs_dir)); case FTP_VDI: /* VDIRECTORY */ return(hmsga(fhs_vdi)); case FTP_FEA: /* FEATURES */ return(hmsga(fhs_fea)); case FTP_GET: /* GET */ return(hmsga(fhs_get)); case FTP_HLP: /* HELP */ return(hmsga(fhs_hlp)); case FTP_IDL: /* IDLE */ return(hmsga(fhs_idl)); case FTP_USR: /* USER, LOGIN */ return(hmsga(fhs_usr)); case FTP_MGE: /* MGET */ return(hmsga(fhs_mge)); case FTP_MKD: /* MKDIR */ return(hmsga(fhs_mkd)); case FTP_MOD: /* MODTIME */ return(hmsga(fhs_mod)); case FTP_MPU: /* MPUT */ return(hmsga(fhs_mpu)); case FTP_OPN: /* OPEN */ return(hmsga(fhs_opn)); case FTP_OPT: /* OPTS, OPTIONS */ return(hmsga(fhs_opt)); case FTP_PUT: /* PUT, SEND */ return(hmsga(fhs_put)); case FTP_REP: /* REPUT, RESEND */ return(hmsga(fhs_reput)); case FTP_PWD: /* PWD */ return(hmsga(fhs_pwd)); case FTP_QUO: /* QUOTE */ return(hmsga(fhs_quo)); case FTP_RGE: /* REGET */ return(hmsga(fhs_rge)); case FTP_REN: /* RENAME */ return(hmsga(fhs_ren)); case FTP_RES: /* RESET */ return(hmsga(fhs_res)); case FTP_RMD: /* RMDIR */ return(hmsga(fhs_rmd)); case FTP_SIT: /* SITE */ return(hmsga(fhs_sit)); case FTP_SIZ: /* SIZE */ return(hmsga(fhs_siz)); case FTP_STA: /* STATUS */ return(hmsga(fhs_sta)); case FTP_SYS: /* SYSTEM */ return(hmsga(fhs_sys)); case FTP_TYP: /* TYPE */ return(hmsga(fhs_typ)); case FTP_UMA: /* UMASK */ return(hmsga(fhs_uma)); case FTP_CHK: /* CHECK */ return(hmsga(fhs_chk)); case FTP_ENA: return(hmsga(fhs_ena)); case FTP_DIS: return(hmsga(fhs_dis)); default: printf("Sorry, help available for this command.\n"); break; } #endif /* NOHELP */ return(success = 0); } int dosetftphlp() { int cx; if ((cx = cmkey(ftpset,nftpset,"","",xxstring)) < 0) if (cx != -3) return(cx); if (cx != -3) ckstrncpy(tmpbuf,atmbuf,TMPBUFSIZ); if ((x = cmcfm()) < 0) return(x); #ifdef NOHELP printf("Sorry, no help available\n"); #else switch (cx) { case -3: printf("\nSyntax: SET FTP parameter value\n"); printf(" Type \"help set ftp ?\" for a list of parameters.\n"); printf(" Type \"help set ftp xxx\" for information about setting\n"); printf(" parameter xxx. Type \"show ftp\" for current values.\n\n"); return(0); case FTS_BUG: printf("\nSyntax: SET FTP BUG {ON, OFF}\n"); printf( " Activates a workaround for the named bug in the FTP server.\n"); printf(" Type SET FTP BUG ? for a list of names.\n"); printf(" For each bug, the default is OFF\n\n"); return(0); #ifdef FTP_SECURITY case FTS_ATP: /* "authtype" */ printf("\nSyntax: SET FTP AUTHTYPE list\n"); printf(" Specifies an ordered list of authentication methods to be\n" ); printf(" when FTP AUTOAUTHENTICATION is ON. The default list is:\n"); printf(" GSSAPI-KRB5, SRP, KERBEROS_V4, TLS, SSL.\n\n"); return(0); case FTS_AUT: /* "autoauthentication" */ printf("\nSyntax:SET FTP AUTOAUTHENTICATION { ON, OFF }\n"); printf(" Tells whether authentication should be negotiated by the\n"); printf(" FTP OPEN command. Default is ON.\n\n"); break; case FTS_CRY: /* "autoencryption" */ printf("\nSET FTP AUTOENCRYPTION { ON, OFF }\n"); printf(" Tells whether encryption (privacy) should be negotiated\n"); printf(" by the FTP OPEN command. Default is ON.\n\n"); break; #endif /* FTP_SECURITY */ case FTS_LOG: /* "autologin" */ printf("\nSET FTP AUTOLOGIN { ON, OFF }\n"); printf(" Tells Kermit whether to try to log you in automatically\n"); printf(" as part of the connection process.\n\n"); break; case FTS_DIS: printf("\nSET FTP DISPLAY { BRIEF, FULLSCREEN, CRT, ... }\n"); printf(" Chooses the file-transfer display style for FTP.\n"); printf(" Like SET TRANSFER DISPLAY but applies only to FTP.\n\n"); break; #ifndef NOCSETS case FTS_XLA: /* "character-set-translation" */ printf("\nSET FTP CHARACTER-SET-TRANSLATION { ON, OFF }\n"); printf(" Whether to translate character sets when transferring\n"); printf(" text files with FTP. OFF by default.\n\n"); break; #endif /* NOCSETS */ case FTS_FNC: /* "collision" */ printf("\n"); printf( "Syntax: SET FTP COLLISION { BACKUP,RENAME,UPDATE,DISCARD,APPEND,OVERWRITE }\n" ); printf(" Tells what do when an incoming file has the same name as\n"); printf(" an existing file when downloading with FTP.\n\n"); break; #ifdef FTP_SECURITY case FTS_CPL: /* "command-protection-level" */ printf("\n"); printf( "Syntax: SET FTP COMMAND-PROTECTION-LEVEL { CLEAR,CONFIDENTIAL,PRIVATE,SAFE }" ); printf("\n"); printf( " Tells what level of protection is applied to the FTP command channel.\n\n"); break; case FTS_CFW: /* "credential-forwarding" */ printf("\nSyntax: SET FTP CREDENTIAL-FORWARDING { ON, OFF }\n"); printf(" Tells whether end-user credentials are to be forwarded\n"); printf(" to the server if supported by the authentication method\n"); printf(" (GSSAPI-KRB5 only).\n\n"); break; case FTS_DPL: /* "data-protection-level" */ printf("\n"); printf( "Syntax: SET FTP DATA-PROTECTION-LEVEL { CLEAR,CONFIDENTIAL,PRIVATE,SAFE }" ); printf("\n"); printf( " Tells what level of protection is applied to the FTP data channel.\n\n"); break; #endif /* FTP_SECURITY */ case FTS_DBG: /* "debug" */ printf("\nSyntax: SET FTP DEBUG { ON, OFF }\n"); printf(" Whether to print FTP protocol messages.\n\n"); return(0); case FTS_ERR: /* "error-action" */ printf("\nSyntax: SET FTP ERROR-ACTION { QUIT, PROCEED }\n"); printf(" What to do when an error occurs when transferring a group\n") ; printf(" of files: quit and fail, or proceed to the next file.\n\n"); return(0); case FTS_CNV: /* "filenames" */ printf("\nSyntax: SET FTP FILENAMES { AUTO, CONVERTED, LITERAL }\n"); printf(" What to do with filenames: convert them, take and use them\n" ); printf(" literally; or choose what to do automatically based on the\n" ); printf(" OS type of the server. The default is AUTO.\n\n"); return(0); case FTS_PSV: /* "passive-mode" */ printf("\nSyntax: SET FTP PASSIVE-MODE { ON, OFF }\n"); printf(" Whether to use passive mode, which helps to get through\n"); printf(" firewalls. ON by default.\n\n"); return(0); case FTS_PRM: /* "permissions" */ printf("\nSyntax: SET FTP PERMISSIONS { AUTO, ON, OFF }\n"); printf(" Whether to try to send file permissions when uploading.\n"); printf(" OFF by default. AUTO means only if client and server\n"); printf(" have the same OS type.\n\n"); return(0); case FTS_TST: /* "progress-messages" */ printf("\nSyntax: SET FTP PROGRESS-MESSAGES { ON, OFF }\n"); printf(" Whether Kermit should print locally-generated feedback\n"); printf(" messages for each non-file-transfer command."); printf(" ON by default.\n\n"); return(0); case FTS_SPC: /* "send-port-commands" */ printf("\nSyntax: SET FTP SEND-PORT-COMMANDS { ON, OFF }\n"); printf(" Whether Kermit should send a new PORT command for each"); printf(" task.\n\n"); return(0); #ifndef NOCSETS case FTS_CSR: /* "server-character-set" */ printf("\nSyntax: SET FTP SERVER-CHARACTER-SET name\n"); printf(" The name of the character set used for text files on the\n"); printf(" server. Enter a name of '?' for a menu.\n\n"); return(0); #endif /* NOCSETS */ case FTS_STO: /* "server-time-offset */ printf( "\nSyntax: SET FTP SERVER-TIME-OFFSET +hh[:mm[:ss]] or -hh[:mm[:ss]]\n"); printf( " Specifies an offset to apply to the server's file timestamps.\n"); printf( " Use this to correct for misconfigured server time or timezone.\n"); printf( " Format: must begin with + or - sign. Hours must be given; minutes\n"); printf( " and seconds are optional: +4 = +4:00 = +4:00:00 (add 4 hours).\n\n"); return(0); case FTS_TYP: /* "type" */ printf("\nSyntax: SET FTP TYPE { TEXT, BINARY, TENEX }\n"); printf(" Establishes the default transfer mode.\n"); printf(" TENEX is used for uploading 8-bit binary files to 36-bit\n"); printf(" platforms such as TENEX and TOPS-20 and for downloading\n"); printf(" them again. ASCII is a synonym for TEXT. Normally each\n"); printf(" file's type is determined automatically from its contents\n" ); printf(" or its name; SET FTP TYPE does not prevent that, it only\n"); printf(" tells which mode to use when the type can't be determined\n" ); printf(" automatically. To completely disable automatic transfer-\n" ); printf(" mode switching and force either text or binary mode, give\n" ); printf(" the top-level command ASCII or BINARY, as in traditional\n"); printf(" FTP clients.\n\n"); return(0); #ifdef FTP_TIMEOUT case FTS_TMO: printf("\nSyntax: SET FTP TIMEOUT number-of-seconds\n"); printf(" Establishes a timeout for FTP transfers.\n"); printf(" The timeout applies per network read or write on the data\n"); printf(" connection, not to the whole transfer. By default the\n"); printf(" timeout value is 0, meaning no timeout. Use a positive\n"); printf(" number to escape gracefully from hung data connections or\n"); printf(" directory listings.\n\n"); return(0); #endif /* FTP_TIMEOUT */ #ifdef PATTERNS case FTS_GFT: printf("\nSyntax: SET FTP GET-FILETYPE-SWITCHING { ON, OFF }\n"); printf(" Tells whether GET and MGET should automatically switch\n"); printf(" the appropriate file type, TEXT, BINARY, or TENEX, by\n"); printf(" matching the name of each incoming file with its list of\n"); printf(" FILE TEXT-PATTERNS and FILE BINARY-PATTERNS. ON by\n"); printf(" default. SHOW PATTERNS displays the current pattern\n"); printf(" list. HELP SET FILE to see how to change it.\n"); break; #endif /* PATTERNS */ case FTS_USN: /* "unique-server-names" */ printf("\nSyntax: SET FTP UNIQUE-SERVER-NAMES { ON, OFF }\n"); printf(" Tells whether to ask the server to create unique names\n"); printf(" for any uploaded file that has the same name as an\n"); printf(" existing file. Default is OFF.\n\n"); return(0); case FTS_VBM: /* "verbose-mode" */ printf("\nSyntax: SET FTP VERBOSE-MODE { ON, OFF }\n"); printf(" Whether to display all responses from the FTP server.\n"); printf(" OFF by default.\n\n"); return(0); case FTS_DAT: printf("\nSyntax: SET FTP DATES { ON, OFF }\n"); printf(" Whether to set date of incoming files from the file date\n"); printf(" on the server. ON by default. Note: there is no way to\n") ; printf(" set the date on files uploaded to the server. Also note\n"); printf(" that not all servers support this feature.\n\n"); return(0); case FTS_APW: printf("\nSyntax: SET FTP ANONYMOUS-PASSWORD [ text ]\n"); printf(" Password to supply automatically on anonymous FTP\n"); printf(" connections instead of the default user@host.\n"); printf(" Omit optional text to restore default.\n\n"); return(0); default: printf("Sorry, help not available for \"set ftp %s\"\n",tmpbuf); } #endif /* NOHELP */ return(0); } #ifndef L_SET #define L_SET 0 #endif /* L_SET */ #ifndef L_INCR #define L_INCR 1 #endif /* L_INCR */ #ifdef FTP_SRP char srp_user[BUFSIZ]; /* where is BUFSIZ defined? */ char *srp_pass; char *srp_acct; #endif /* FTP_SRP */ static int kerror; /* Needed for all auth types */ static struct sockaddr_in hisctladdr; static struct sockaddr_in hisdataaddr; static struct sockaddr_in data_addr; static int data = -1; static int ptflag = 0; static struct sockaddr_in myctladdr; #ifdef COMMENT #ifndef OS2 UID_T getuid(); #endif /* OS2 */ #endif /* COMMENT */ static int cpend = 0; /* No pending replies */ #ifdef CK_SSL extern SSL *ssl_ftp_con; extern SSL_CTX *ssl_ftp_ctx; extern SSL *ssl_ftp_data_con; extern int ssl_ftp_active_flag; extern int ssl_ftp_data_active_flag; #endif /* CK_SSL */ /* f t p c m d -- Send a command to the FTP server */ /* Call with: char * cmd: The command to send. char * arg: The argument (e.g. a filename). int lcs: The local character set index. int rcs: The remote (server) character set index. int vbm: Verbose mode: 0 = force verbosity off >0 = force verbosity on If arg is given (not NULL or empty) and lcs != rcs and both are > -1, and neither lcs or rcs is UCS-2, the arg is translated from the local character set to the remote one before sending the result to the server. Returns: 0 on failure with ftpcode = -1 >= 0 on success (getreply() result) with ftpcode = 0. */ static char xcmdbuf[RFNBUFSIZ]; static int ftpcmd(cmd,arg,lcs,rcs,vbm) char * cmd, * arg; int lcs, rcs, vbm; { char * s = NULL; int r = 0, x = 0, fc = 0, len = 0, cmdlen = 0, q = -1; sig_t oldintr; if (ftp_deb) /* DEBUG */ vbm = 1; else if (quiet || dpyactive) /* QUIET or File Transfer Active */ vbm = 0; else if (vbm < 0) /* VERBOSE */ vbm = ftp_vbm; cancelfile = 0; if (!cmd) cmd = ""; if (!arg) arg = ""; cmdlen = (int)strlen(cmd); len = cmdlen + (int)strlen(arg) + 1; if (ftp_deb /* && !dpyactive */ ) { #ifdef FTP_PROXY if (ftp_prx) printf("%s ", ftp_host); #endif /* FTP_PROXY */ printf("---> "); if (!anonymous && strcmp("PASS",cmd) == 0) printf("PASS XXXX"); else printf("%s %s",cmd,arg); printf("\n"); } /* bzero(xcmdbuf,RFNBUFSIZ); */ ckmakmsg(xcmdbuf,RFNBUFSIZ, cmd, *arg ? " " : "", arg, NULL); #ifdef DEBUG if (deblog) { debug(F110,"ftpcmd cmd",cmd,0); debug(F110,"ftpcmd arg",arg,0); debug(F101,"ftpcmd lcs","",lcs); debug(F101,"ftpcmd rcs","",rcs); } #endif /* DEBUG */ if (csocket == -1) { perror("No control connection for command"); ftpcode = -1; return(0); } havesigint = 0; oldintr = signal(SIGINT, cmdcancel); #ifndef NOCSETS if (*arg && /* If an arg was given */ lcs > -1 && /* and a local charset */ rcs > -1 && /* and a remote charset */ lcs != rcs && /* and the two are not the same */ lcs != FC_UCS2 && /* and neither one is UCS-2 */ rcs != FC_UCS2 /* ... */ ) { initxlate(lcs,rcs); /* Translate arg from lcs to rcs */ xgnbp = arg; /* Global pointer to input string */ rfnptr = rfnbuf; /* Global pointer to output buffer */ while (1) { if ((c0 = xgnbyte(FC_UCS2,lcs,strgetc)) < 0) break; if (xpnbyte(c0,TC_UCS2,rcs,strputc) < 0) break; } /* We have to copy here instead of translating directly into xcmdbuf[] so strputc() can check length. Alternatively we could write yet another xpnbyte() output function. */ if ((int)strlen(rfnbuf) > (RFNBUFSIZ - (cmdlen+1))) { printf("?FTP command too long: %s + arg\n",cmd); ftpcode = -1; return(0); } x = ckstrncpy(&xcmdbuf[cmdlen+1], rfnbuf, RFNBUFSIZ - (cmdlen+1)); } #endif /* NOCSETS */ s = xcmdbuf; /* Command to send to server */ #ifdef DEBUG if (deblog) { /* Log it */ if (!anonymous && !ckstrcmp(s,"PASS ",5,0)) { /* But don't log passwords */ debug(F110,"FTP SENT ","PASS XXXX",0); } else { debug(F110,"FTP SENT ",s,0); } } #endif /* DEBUG */ #ifdef CK_ENCRYPTION again: #endif /* CK_ENCRYPTION */ if (scommand(s) == 0) { /* Send it. */ signal(SIGINT, oldintr); return(0); } cpend = 1; x = !strcmp(cmd,"QUIT"); /* Is it the QUIT command? */ if (x) /* In case we're interrupted */ connected = 0; /* while waiting for the reply... */ fc = 0; /* Function code for getreply() */ if (!strncmp(cmd,"AUTH ",5) /* Must parse AUTH reply */ #ifdef FTPHOST && strncmp(cmd, "HOST ",5) #endif /* FTPHOST */ ) { fc = GRF_AUTH; } else if (!ckstrcmp(cmd,"FEAT",-1,0)) { /* Must parse FEAT reply */ fc = GRF_FEAT; /* But FEAT not widely understood */ if (!ftp_deb) /* So suppress error messages */ vbm = 9; } r = getreply(x, /* Expect connection to close */ lcs,rcs, /* Charsets */ vbm, /* Verbosity */ fc /* Function code */ ); if (q > -1) quiet = q; #ifdef CK_ENCRYPTION if (ftpcode == 533 && ftp_cpl == FPL_PRV) { fprintf(stderr, "ENC command not supported at server; retrying under MIC...\n"); ftp_cpl = FPL_SAF; goto again; } #endif /* CK_ENCRYPTION */ #ifdef COMMENT if (cancelfile && oldintr != SIG_IGN) (*oldintr)(SIGINT); #endif /* COMMENT */ signal(SIGINT, oldintr); return(r); } static VOID lostpeer() { debug(F100,"lostpeer","",0); if (connected) { if (csocket != -1) { #ifdef CK_SSL if (ssl_ftp_active_flag) { SSL_shutdown(ssl_ftp_con); SSL_free(ssl_ftp_con); ssl_ftp_proxy = 0; ssl_ftp_active_flag = 0; ssl_ftp_con = NULL; } #endif /* CK_SSL */ #ifdef TCPIPLIB socket_close(csocket); #else /* TCPIPLIB */ #ifdef USE_SHUTDOWN shutdown(csocket, 1+1); #endif /* USE_SHUTDOWN */ close(csocket); #endif /* TCPIPLIB */ csocket = -1; } if (data != -1) { #ifdef CK_SSL if (ssl_ftp_data_active_flag) { SSL_shutdown(ssl_ftp_data_con); SSL_free(ssl_ftp_data_con); ssl_ftp_data_active_flag = 0; ssl_ftp_data_con = NULL; } #endif /* CK_SSL */ #ifdef TCPIPLIB socket_close(data); #else /* TCPIPLIB */ #ifdef USE_SHUTDOWN shutdown(data, 1+1); #endif /* USE_SHUTDOWN */ close(data); #endif /* TCPIPLIB */ data = -1; globaldin = -1; } connected = 0; anonymous = 0; loggedin = 0; auth_type = NULL; ftp_cpl = ftp_dpl = FPL_CLR; #ifdef CKLOGDIAL ftplogend(); #endif /* CKLOGDIAL */ #ifdef LOCUS if (autolocus) /* Auotomatic locus switching... */ setlocus(1,1); /* Switch locus to local. */ #endif /* LOCUS */ #ifdef OS2 DialerSend(OPT_KERMIT_HANGUP, 0); #endif /* OS2 */ } #ifdef FTP_PROXY pswitch(1); if (connected) { if (csocket != -1) { #ifdef TCPIPLIB socket_close(csocket); #else /* TCPIPLIB */ #ifdef USE_SHUTDOWN shutdown(csocket, 1+1); #endif /* USE_SHUTDOWN */ close(csocket); #endif /* TCPIPLIB */ csocket = -1; } connected = 0; anonymous = 0; loggedin = 0; auth_type = NULL; ftp_cpl = ftp_dpl = FPL_CLR; } proxflag = 0; pswitch(0); #endif /* FTP_PROXY */ } int ftpisopen() { return(connected); } static int ftpclose() { extern int quitting; if (!connected) return(0); ftp_xfermode = xfermode; if (!ftp_vbm && !quiet) printlines = 1; ftpcmd("QUIT",NULL,0,0,ftp_vbm); if (csocket) { #ifdef CK_SSL if (ssl_ftp_active_flag) { SSL_shutdown(ssl_ftp_con); SSL_free(ssl_ftp_con); ssl_ftp_proxy = 0; ssl_ftp_active_flag = 0; ssl_ftp_con = NULL; } #endif /* CK_SSL */ #ifdef TCPIPLIB socket_close(csocket); #else /* TCPIPLIB */ #ifdef USE_SHUTDOWN shutdown(csocket, 1+1); #endif /* USE_SHUTDOWN */ close(csocket); #endif /* TCPIPLIB */ } csocket = -1; connected = 0; anonymous = 0; loggedin = 0; mdtmok = 1; sizeok = 1; featok = 1; stouarg = 1; typesent = 0; data = -1; globaldin = -1; #ifdef FTP_PROXY if (!proxy) macnum = 0; #endif /* FTP_PROXY */ auth_type = NULL; ftp_dpl = FPL_CLR; #ifdef CKLOGDIAL ftplogend(); #endif /* CKLOGDIAL */ #ifdef LOCUS /* Unprefixed file management commands are executed locally */ if (autolocus && !ftp_cmdlin && !quitting) { setlocus(1,1); } #endif /* LOCUS */ #ifdef OS2 DialerSend(OPT_KERMIT_HANGUP, 0); #endif /* OS2 */ return(0); } int ftpopen(remote, service, use_tls) char * remote, * service; int use_tls; { char * host; if (connected) { printf("?Already connected to %s, use FTP CLOSE first.\n", ftp_host); ftpcode = -1; return(0); } #ifdef FTPHOST hostcmd = 0; #endif /* FTPHOST */ alike = 0; ftp_srvtyp[0] = NUL; if (!service) service = ""; if (!*service) service = use_tls ? "ftps" : "ftp"; if (!isdigit(service[0])) { struct servent *destsp; destsp = getservbyname(service, "tcp"); if (!destsp) { if (!ckstrcmp(service,"ftp",-1,0)) { ftp_port = 21; } else if (!ckstrcmp(service,"ftps",-1,0)) { ftp_port = 990; } else { printf("?Bad port name - \"%s\"\n", service); ftpcode = -1; return(0); } } else { ftp_port = destsp->s_port; ftp_port = ntohs((unsigned short)ftp_port); /* SMS 2007/02/15 */ } } else ftp_port = atoi(service); if (ftp_port <= 0) { printf("?Bad port name - \"%s\"\n", service); ftpcode = -1; return(0); } host = ftp_hookup(remote, ftp_port, use_tls); if (host) { ckstrncpy(ftp_user_host,remote,MAX_DNS_NAMELEN); connected = 1; /* Set FTP defaults */ ftp_cpl = ftp_dpl = FPL_CLR; curtype = FTT_ASC; /* Server uses ASCII mode */ form = FORM_N; mode = MODE_S; stru = STRU_F; strcpy(bytename, "8"); bytesize = 8; #ifdef FTP_SECURITY if (ftp_aut) { if (ftp_auth()) { if (ftp_cry #ifdef OS2 && ck_crypt_is_installed() #endif /* OS2 */ ) { if (!quiet) printf("FTP Command channel is Private (encrypted)\n"); ftp_cpl = FPL_PRV; if (setpbsz(DEFAULT_PBSZ) < 0) { /* a failure here is most likely caused by a mixup */ /* in the session key used by client and server */ printf("?Protection buffer size negotiation failed\n"); return(0); } if (ftpcmd("PROT P",NULL,0,0,ftp_vbm) == REPLY_COMPLETE) { if (!quiet) printf("FTP Data channel is Private (encrypted)\n"); ftp_dpl = FPL_PRV; } else printf("?Unable to enable encryption on data channel\n"); } else { ftp_cpl = FPL_SAF; } } if (!connected) goto fail; } #endif /* FTP_SECURITY */ if (ftp_log) /* ^^^ */ ftp_login(remote); if (!connected) goto fail; ftp_xfermode = xfermode; #ifdef CKLOGDIAL dologftp(); #endif /* CKLOGDIAL */ #ifdef OS2 DialerSend(OPT_KERMIT_CONNECT, 0); #endif /* OS2 */ passivemode = ftp_psv; sendport = ftp_spc; mdtmok = 1; sizeok = 1; stouarg = 1; typesent = 0; if (ucbuf == NULL) { actualbuf = DEFAULT_PBSZ; while (actualbuf && (ucbuf = (CHAR *)malloc(actualbuf)) == NULL) actualbuf >>= 2; } if (!maxbuf) ucbufsiz = actualbuf - FUDGE_FACTOR; debug(F101,"ftpopen ucbufsiz","",ucbufsiz); return(1); } fail: printf("?Can't FTP connect to %s:%s\n",remote,service); ftpcode = -1; return(0); } #ifdef CK_SSL int ssl_auth() { int i; char* p; if (ssl_debug_flag) { fprintf(stderr,"SSL DEBUG ACTIVE\n"); fflush(stderr); /* for the moment I want the output on screen */ } if (ssl_ftp_data_con != NULL) { SSL_free(ssl_ftp_data_con); ssl_ftp_data_con = NULL; } if (ssl_ftp_con != NULL) { SSL_free(ssl_ftp_con); ssl_ftp_con=NULL; } if (ssl_ftp_ctx != NULL) { SSL_CTX_free(ssl_ftp_ctx); ssl_ftp_ctx = NULL; } /* The SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS * was added to OpenSSL 0.9.6e and 0.9.7. It does not exist in previous * versions */ #ifndef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS #define SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS 0L #endif if (auth_type && !strcmp(auth_type,"TLS")) { ssl_ftp_ctx=SSL_CTX_new(SSLv3_client_method()); if (!ssl_ftp_ctx) return(0); SSL_CTX_set_options(ssl_ftp_ctx, SSL_OP_SINGLE_DH_USE|SSL_OP_EPHEMERAL_RSA ); } else { ssl_ftp_ctx = SSL_CTX_new(ftp_bug_use_ssl_v2 ? SSLv23_client_method() : SSLv3_client_method()); if (!ssl_ftp_ctx) return(0); SSL_CTX_set_options(ssl_ftp_ctx, (ftp_bug_use_ssl_v2 ? 0 : SSL_OP_NO_SSLv2)| SSL_OP_SINGLE_DH_USE|SSL_OP_EPHEMERAL_RSA ); } SSL_CTX_set_default_passwd_cb(ssl_ftp_ctx, (pem_password_cb *)ssl_passwd_callback); SSL_CTX_set_info_callback(ssl_ftp_ctx,ssl_client_info_callback); SSL_CTX_set_session_cache_mode(ssl_ftp_ctx,SSL_SESS_CACHE_CLIENT); #ifdef OS2 #ifdef NT /* The defaults in the SSL crypto library are not appropriate for OS/2 */ { char path[CKMAXPATH]; extern char exedir[]; ckmakmsg(path,CKMAXPATH,exedir,"certs",NULL,NULL); if (SSL_CTX_load_verify_locations(ssl_ftp_ctx,NULL,path) == 0) { debug(F110,"ftp ssl_auth unable to load path",path,0); if (ssl_debug_flag) printf("?Unable to load verify-dir: %s\r\n",path); } ckmakmsg(path,CKMAXPATH, (char *)GetAppData(1),"kermit 95/certs",NULL,NULL); if (SSL_CTX_load_verify_locations(ssl_ftp_ctx,NULL,path) == 0) { debug(F110,"ftp ssl_auth unable to load path",path,0); if (ssl_debug_flag) printf("?Unable to load verify-dir: %s\r\n",path); } ckmakmsg(path,CKMAXPATH, (char *)GetAppData(0),"kermit 95/certs",NULL,NULL); if (SSL_CTX_load_verify_locations(ssl_ftp_ctx,NULL,path) == 0) { debug(F110,"ftp ssl_auth unable to load path",path,0); if (ssl_debug_flag) printf("?Unable to load verify-dir: %s\r\n",path); } ckmakmsg(path,CKMAXPATH,exedir,"ca_certs.pem",NULL,NULL); if (SSL_CTX_load_verify_locations(ssl_ftp_ctx,path,NULL) == 0) { debug(F110,"ftp ssl_auth unable to load path",path,0); if (ssl_debug_flag) printf("?Unable to load verify-file: %s\r\n",path); } ckmakmsg(path,CKMAXPATH,(char *)GetAppData(1), "kermit 95/ca_certs.pem",NULL,NULL); if (SSL_CTX_load_verify_locations(ssl_ftp_ctx,path,NULL) == 0) { debug(F110,"ftp ssl_auth unable to load path",path,0); if (ssl_debug_flag) printf("?Unable to load verify-file: %s\r\n",path); } ckmakmsg(path,CKMAXPATH,(char *)GetAppData(0), "kermit 95/ca_certs.pem",NULL,NULL); if (SSL_CTX_load_verify_locations(ssl_ftp_ctx,path,NULL) == 0) { debug(F110,"ftp ssl_auth unable to load path",path,0); if (ssl_debug_flag) printf("?Unable to load verify-file: %s\r\n",path); } } #else /* NT */ /* The defaults in the SSL crypto library are not appropriate for OS/2 */ { char path[CKMAXPATH]; extern char exedir[]; ckmakmsg(path,CKMAXPATH,exedir,"certs",NULL,NULL); if (SSL_CTX_load_verify_locations(ssl_ftp_ctx,NULL,path) == 0) { debug(F110,"ftp ssl_auth unable to load path",path,0); if (ssl_debug_flag) printf("?Unable to load verify-dir: %s\r\n",path); } ckmakmsg(path,CKMAXPATH,exedir,"ca_certs.pem",NULL,NULL); if (SSL_CTX_load_verify_locations(ssl_ftp_ctx,path,NULL) == 0) { debug(F110,"ftp ssl_auth unable to load path",path,0); if (ssl_debug_flag) printf("?Unable to load verify-file: %s\r\n",path); } } #endif /* NT */ #else /* OS2 */ SSL_CTX_set_default_verify_paths(ssl_ftp_ctx); #endif /* OS2 */ if (ssl_verify_file && SSL_CTX_load_verify_locations(ssl_ftp_ctx,ssl_verify_file,NULL) == 0) { debug(F110, "ftp ssl auth unable to load ssl_verify_file", ssl_verify_file, 0 ); if (ssl_debug_flag) printf("?Unable to load verify-file: %s\r\n",ssl_verify_file); } if (ssl_verify_dir && SSL_CTX_load_verify_locations(ssl_ftp_ctx,NULL,ssl_verify_dir) == 0) { debug(F110, "ftp ssl auth unable to load ssl_verify_dir", ssl_verify_dir, 0 ); if (ssl_debug_flag) printf("?Unable to load verify-dir: %s\r\n",ssl_verify_dir); } /* set up the new CRL Store */ crl_store = (X509_STORE *)X509_STORE_new(); if (crl_store) { #ifdef OS2 char path[CKMAXPATH]; extern char exedir[]; ckmakmsg(path,CKMAXPATH,exedir,"crls",NULL,NULL); if (X509_STORE_load_locations(crl_store,NULL,path) == 0) { debug(F110,"ftp ssl auth unable to load dir",path,0); if (ssl_debug_flag) printf("?Unable to load crl-dir: %s\r\n",path); } #ifdef NT ckmakmsg(path,CKMAXPATH, (char *)GetAppData(1),"kermit 95/crls",NULL,NULL); if (X509_STORE_load_locations(crl_store,NULL,path) == 0) { debug(F110,"ftp ssl auth unable to load dir",path,0); if (ssl_debug_flag) printf("?Unable to load crl-dir: %s\r\n",path); } ckmakmsg(path,CKMAXPATH, (char *)GetAppData(0),"kermit 95/crls",NULL,NULL); if (X509_STORE_load_locations(crl_store,NULL,path) == 0) { debug(F110,"ftp ssl auth unable to load dir",path,0); if (ssl_debug_flag) printf("?Unable to load crl-dir: %s\r\n",path); } #endif /* NT */ ckmakmsg(path,CKMAXPATH,exedir,"ca_crls.pem",NULL,NULL); if (X509_STORE_load_locations(crl_store,path,NULL) == 0) { debug(F110,"ftp ssl auth unable to load file",path,0); if (ssl_debug_flag) printf("?Unable to load crl-file: %s\r\n",path); } #ifdef NT ckmakmsg(path,CKMAXPATH,(char *)GetAppData(1), "kermit 95/ca_crls.pem",NULL,NULL); if (X509_STORE_load_locations(crl_store,path,NULL) == 0) { debug(F110,"ftp ssl auth unable to load file",path,0); if (ssl_debug_flag) printf("?Unable to load crl-file: %s\r\n",path); } ckmakmsg(path,CKMAXPATH,(char *)GetAppData(0), "kermit 95/ca_crls.pem",NULL,NULL); if (X509_STORE_load_locations(crl_store,path,NULL) == 0) { debug(F110,"ftp ssl auth unable to load file",path,0); if (ssl_debug_flag) printf("?Unable to load crl-file: %s\r\n",path); } #endif /* NT */ #endif /* OS2 */ if (ssl_crl_file || ssl_crl_dir) { if (ssl_crl_file && X509_STORE_load_locations(crl_store,ssl_crl_file,NULL) == 0) { debug(F110, "ftp ssl auth unable to load ssl_crl_file", ssl_crl_file, 0 ); if (ssl_debug_flag) printf("?Unable to load crl-file: %s\r\n",ssl_crl_file); } if (ssl_crl_dir && X509_STORE_load_locations(crl_store,NULL,ssl_crl_dir) == 0) { debug(F110, "ftp ssl auth unable to load ssl_crl_dir", ssl_crl_dir, 0 ); if (ssl_debug_flag) printf("?Unable to load crl-dir: %s\r\n",ssl_crl_dir); } } else { X509_STORE_set_default_paths(crl_store); } } SSL_CTX_set_verify(ssl_ftp_ctx,ssl_verify_flag, ssl_client_verify_callback); ssl_verify_depth = -1; ssl_ftp_con=(SSL *)SSL_new(ssl_ftp_ctx); tls_load_certs(ssl_ftp_ctx,ssl_ftp_con,0); SSL_set_fd(ssl_ftp_con,csocket); SSL_set_verify(ssl_ftp_con,ssl_verify_flag,NULL); if (ssl_cipher_list) { SSL_set_cipher_list(ssl_ftp_con,ssl_cipher_list); } else { char * p; if (p = getenv("SSL_CIPHER")) { SSL_set_cipher_list(ssl_ftp_con,p); } else { SSL_set_cipher_list(ssl_ftp_con,DEFAULT_CIPHER_LIST); } } if (ssl_debug_flag) { fprintf(stderr,"=>START SSL/TLS connect on COMMAND\n"); fflush(stderr); } if (SSL_connect(ssl_ftp_con) <= 0) { static char errbuf[1024]; ckmakmsg(errbuf,1024,"ftp: SSL/TLS connect COMMAND error: ", ERR_error_string(ERR_get_error(),NULL),NULL,NULL); fprintf(stderr,"%s\n", errbuf); fflush(stderr); ssl_ftp_active_flag=0; SSL_free(ssl_ftp_con); ssl_ftp_con = NULL; } else { ssl_ftp_active_flag = 1; if (!ssl_certsok_flag && !tls_is_krb5(1)) { char *subject = ssl_get_subject_name(ssl_ftp_con); if (!subject) { if (ssl_verify_flag & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) { debug(F110,"ssl_auth","[SSL - FAILED]",0); return(ssl_ftp_active_flag = 0); } else { if (uq_ok("Warning: Server didn't provide a certificate\n", "Continue? (Y/N)",3,NULL,0) <= 0) { debug(F110, "ssl_auth","[SSL - FAILED]",0); return(ssl_ftp_active_flag = 0); } } } else if (ssl_check_server_name(ssl_ftp_con, ftp_user_host)) { debug(F110,"ssl_auth","[SSL - FAILED]",0); return(ssl_ftp_active_flag = 0); } } debug(F110,"ssl_auth","[SSL - OK]",0); ssl_display_connect_details(ssl_ftp_con,0,ssl_verbose_flag); } if (ssl_debug_flag) { fprintf(stderr,"=>DONE SSL/TLS connect on COMMAND\n"); fflush(stderr); } return(ssl_ftp_active_flag); } #endif /* CK_SSL */ static sigtype cmdcancel(sig) int sig; { #ifdef OS2 /* In Unix we "chain" to trap(), which prints this */ printf("^C...\n"); #endif /* OS2 */ debug(F100,"ftp cmdcancel caught SIGINT ","",0); fflush(stdout); secure_getc(0,1); /* Initialize net input buffers */ cancelfile++; cancelgroup++; mlsreset(); #ifndef OS2 #ifdef FTP_PROXY if (ptflag) /* proxy... */ longjmp(ptcancel,1); #endif /* FTP_PROXY */ debug(F100,"ftp cmdcancel chain to trap()...","",0); trap(SIGINT); /* NOTREACHED */ debug(F100,"ftp cmdcancel return from trap()...","",0); #else debug(F100,"ftp cmdcancel PostCtrlCSem()...","",0); PostCtrlCSem(); #endif /* OS2 */ } static int #ifdef CK_ANSIC scommand(char * s) /* Was secure_command() */ #else scommand(s) char * s; #endif /* CK_ANSIC */ { int length = 0, len2; char in[FTP_BUFSIZ], out[FTP_BUFSIZ]; #ifdef CK_SSL if (ssl_ftp_active_flag) { int error, rc; length = strlen(s) + 2; length = ckmakmsg(out,FTP_BUFSIZ,s,"\r\n",NULL,NULL); rc = SSL_write(ssl_ftp_con,out,length); error = SSL_get_error(ssl_ftp_con,rc); switch (error) { case SSL_ERROR_NONE: return(1); case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_READ: case SSL_ERROR_SYSCALL: #ifdef NT { int gle = GetLastError(); } #endif /* NT */ case SSL_ERROR_WANT_X509_LOOKUP: case SSL_ERROR_SSL: case SSL_ERROR_ZERO_RETURN: default: lostpeer(); } return(0); } #endif /* CK_SSL */ if (auth_type && ftp_cpl != FPL_CLR) { #ifdef FTP_SRP if (ck_srp_is_installed() && (strcmp(auth_type,"SRP") == 0)) if ((length = srp_encode(ftp_cpl == FPL_PRV, (CHAR *)s, (CHAR *)out, strlen(s))) < 0) { fprintf(stderr, "SRP failed to encode message\n"); return(0); } #endif /* FTP_SRP */ #ifdef FTP_KRB4 if (ck_krb4_is_installed() && (strcmp(auth_type, "KERBEROS_V4") == 0)) { if (ftp_cpl == FPL_PRV) { length = krb_mk_priv((CHAR *)s, (CHAR *)out, strlen(s), ftp_sched, #ifdef KRB524 ftp_cred.session, #else /* KRB524 */ &ftp_cred.session, #endif /* KRB524 */ &myctladdr, &hisctladdr); } else { length = krb_mk_safe((CHAR *)s, (CHAR *)out, strlen(s), #ifdef KRB524 ftp_cred.session, #else /* KRB524 */ &ftp_cred.session, #endif /* KRB524 */ &myctladdr, &hisctladdr); } if (length == -1) { fprintf(stderr, "krb_mk_%s failed for KERBEROS_V4\n", ftp_cpl == FPL_PRV ? "priv" : "safe"); return(0); } } #endif /* FTP_KRB4 */ #ifdef FTP_GSSAPI /* Scommand (based on level) */ if (ck_gssapi_is_installed() && (strcmp(auth_type, "GSSAPI") == 0)) { gss_buffer_desc in_buf, out_buf; OM_uint32 maj_stat, min_stat; int conf_state; in_buf.value = s; in_buf.length = strlen(s) + 1; maj_stat = gss_seal(&min_stat, gcontext, (ftp_cpl==FPL_PRV), /* private */ GSS_C_QOP_DEFAULT, &in_buf, &conf_state, &out_buf); if (maj_stat != GSS_S_COMPLETE) { /* Generally need to deal */ user_gss_error(maj_stat, min_stat, (ftp_cpl==FPL_PRV)? "gss_seal ENC didn't complete": "gss_seal MIC didn't complete"); } else if ((ftp_cpl == FPL_PRV) && !conf_state) { fprintf(stderr, "GSSAPI didn't encrypt message"); } else { if (ftp_deb) fprintf(stderr, "sealed (%s) %d bytes\n", ftp_cpl==FPL_PRV?"ENC":"MIC", out_buf.length); memcpy(out, out_buf.value, length=out_buf.length); gss_release_buffer(&min_stat, &out_buf); } } #endif /* FTP_GSSAPI */ /* Other auth types go here ... */ len2 = FTP_BUFSIZ; if ((kerror = radix_encode((CHAR *)out, (CHAR *)in, length, &len2, RADIX_ENCODE)) ) { fprintf(stderr,"Couldn't base 64 encode command (%s)\n", radix_error(kerror)); return(0); } if (ftp_deb) fprintf(stderr, "scommand(%s)\nencoding %d bytes\n", s, length); len2 = ckmakmsg(out, FTP_BUFSIZ, ftp_cpl == FPL_PRV ? "ENC " : "MIC ", in, "\r\n", NULL ); send(csocket,(SENDARG2TYPE)out,len2,0); } else { char out[FTP_BUFSIZ]; int len = ckmakmsg(out,FTP_BUFSIZ,s,"\r\n",NULL,NULL); send(csocket,(SENDARG2TYPE)out,len,0); } return(1); } static int mygetc() { static char inbuf[4096]; static int bp = 0, ep = 0; int rc; if (bp == ep) { bp = ep = 0; #ifdef CK_SSL if (ssl_ftp_active_flag) { int error; rc = SSL_read(ssl_ftp_con,inbuf,4096); error = SSL_get_error(ssl_ftp_con,rc); switch (error) { case SSL_ERROR_NONE: break; case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_READ: return(0); case SSL_ERROR_SYSCALL: if (rc == 0) { /* EOF */ break; } else { #ifdef NT int gle = GetLastError(); #endif /* NT */ break; } case SSL_ERROR_WANT_X509_LOOKUP: case SSL_ERROR_SSL: case SSL_ERROR_ZERO_RETURN: default: break; } } else #endif /* CK_SSL */ rc = recv(csocket,(char *)inbuf,4096,0); if (rc <= 0) return(EOF); ep = rc; } return(inbuf[bp++]); } /* x l a t e c -- Translate a character */ /* Call with: fc = Function code: 0 = translate, 1 = initialize. c = Character (as int). incs = Index of charset to translate from. outcs = Index of charset to translate to. Returns: 0: OK -1: Error */ static int xlatec(fc,c,incs,outcs) int fc, c, incs, outcs; { #ifdef NOCSETS return(c); #else static char buf[128]; static int cx; int c0, c1; if (fc == 1) { /* Initialize */ cx = 0; /* Catch-up buffer write index */ xgnbp = buf; /* Catch-up buffer read pointer */ buf[0] = NUL; /* Buffer is empty */ return(0); } if (cx >= 127) { /* Catch-up buffer full */ debug(F100,"xlatec overflow","",0); /* (shouldn't happen) */ printf("?Translation buffer overflow\n"); return(-1); } /* Add char to buffer. */ /* The buffer won't grow unless incs is a multibyte set, e.g. UTF-8. */ debug(F000,"xlatec buf",ckitoa(cx),c); buf[cx++] = c; buf[cx] = NUL; while ((c0 = xgnbyte(FC_UCS2,incs,strgetc)) > -1) { if (xpnbyte(c0,TC_UCS2,outcs,NULL) < 0) /* (NULL was xprintc) */ return(-1); } /* If we're caught up, reinitialize the buffer */ return((cx == (xgnbp - buf)) ? xlatec(1,0,0,0) : 0); #endif /* NOCSETS */ } /* p a r s e f e a t */ /* Note: for convenience we align keyword values with table indices */ /* If you need to insert a new keyword, adjust the SFT_xxx definitions */ static struct keytab feattab[] = { { "$$$$", 0, 0 }, /* Dummy for sfttab[0] */ { "AUTH", SFT_AUTH, 0 }, { "LANG", SFT_LANG, 0 }, { "MDTM", SFT_MDTM, 0 }, { "MLST", SFT_MLST, 0 }, { "PBSZ", SFT_PBSZ, 0 }, { "PROT", SFT_PROT, 0 }, { "REST", SFT_REST, 0 }, { "SIZE", SFT_SIZE, 0 }, { "TVFS", SFT_TVFS, 0 }, { "UTF8", SFT_UTF8, 0 } }; static int nfeattab = (sizeof(feattab) / sizeof(struct keytab)); #define FACT_CSET 1 #define FACT_CREA 2 #define FACT_LANG 3 #define FACT_MTYP 4 #define FACT_MDTM 5 #define FACT_PERM 6 #define FACT_SIZE 7 #define FACT_TYPE 8 #define FACT_UNIQ 9 static struct keytab facttab[] = { { "CHARSET", FACT_CSET, 0 }, { "CREATE", FACT_CREA, 0 }, { "LANG", FACT_LANG, 0 }, { "MEDIA-TYPE", FACT_MTYP, 0 }, { "MODIFY", FACT_MDTM, 0 }, { "PERM", FACT_PERM, 0 }, { "SIZE", FACT_SIZE, 0 }, { "TYPE", FACT_TYPE, 0 }, { "UNIQUE", FACT_UNIQ, 0 } }; static int nfacttab = (sizeof(facttab) / sizeof(struct keytab)); static struct keytab ftyptab[] = { { "CDIR", FTYP_CDIR, 0 }, { "DIR", FTYP_DIR, 0 }, { "FILE", FTYP_FILE, 0 }, { "PDIR", FTYP_PDIR, 0 } }; static int nftyptab = (sizeof(ftyptab) / sizeof(struct keytab)); static VOID parsefeat(s) char * s; { /* Parse a FEATURE response */ char kwbuf[8]; int i, x; if (!s) return; if (!*s) return; while (*s < '!') s++; for (i = 0; i < 4; i++) { if (s[i] < '!') break; kwbuf[i] = s[i]; } if (s[i] && s[i] != SP && s[i] != CR && s[i] != LF) return; kwbuf[i] = NUL; /* xlookup requires a full (but case independent) match */ i = xlookup(feattab,kwbuf,nfeattab,&x); debug(F111,"ftp parsefeat",s,i); if (i < 0 || i > 15) return; switch (i) { case SFT_MDTM: /* Controlled by ENABLE/DISABLE */ sfttab[i] = mdtmok; if (mdtmok) sfttab[0]++; break; case SFT_MLST: /* ditto */ sfttab[i] = mlstok; if (mlstok) sfttab[0]++; break; case SFT_SIZE: /* ditto */ sfttab[i] = sizeok; if (sizeok) sfttab[0]++; break; case SFT_AUTH: /* ditto */ sfttab[i] = ftp_aut; if (ftp_aut) sfttab[0]++; break; default: /* Others */ sfttab[0]++; sfttab[i]++; } } static char * parsefacts(s) char * s; { /* Parse MLS[DT] File Facts */ char * p; int i, j, x; if (!s) return(NULL); if (!*s) return(NULL); /* Maybe we should make a copy of s so we can poke it... */ while ((p = ckstrchr(s,'='))) { *p = NUL; /* s points to fact */ i = xlookup(facttab,s,nfacttab,&x); debug(F111,"ftp parsefact fact",s,i); *p = '='; s = p+1; /* Now s points to arg */ p = ckstrchr(s,';'); if (!p) p = ckstrchr(s,SP); if (!p) { debug(F110,"ftp parsefact end-of-val search fail",s,0); break; } *p = NUL; debug(F110,"ftp parsefact valu",s,0); switch (i) { case FACT_CSET: /* Ignore these for now */ case FACT_CREA: case FACT_LANG: case FACT_PERM: case FACT_MTYP: case FACT_UNIQ: break; case FACT_MDTM: /* Modtime */ makestr(&havemdtm,s); debug(F110,"ftp parsefact mdtm",havemdtm,0); break; case FACT_SIZE: /* Size */ havesize = ckatofs(s); debug(F101,"ftp parsefact size","",havesize); break; case FACT_TYPE: /* Type */ j = xlookup(ftyptab,s,nftyptab,NULL); debug(F111,"ftp parsefact type",s,j); havetype = (j < 1) ? 0 : j; break; } *p = ';'; s = p+1; /* s points next fact or name */ } while (*s == SP) /* Skip past spaces. */ s++; if (!*s) /* Make sure we still have a name */ s = NULL; debug(F110,"ftp parsefact name",s,0); return(s); } /* g e t r e p l y -- (to an FTP command sent to server) */ /* vbm = 1 (verbose); 0 (quiet except for error messages); 9 (super quiet) */ static int getreply(expecteof,lcs,rcs,vbm,fc) int expecteof, lcs, rcs, vbm, fc; { /* lcs, rcs, vbm parameters as in ftpcmd() */ register int i, c, n; register int dig; register char *cp; int xlate = 0; int count = 0; int auth = 0; int originalcode = 0, continuation = 0; sig_t oldintr; int pflag = 0; char *pt = pasv; char ibuf[FTP_BUFSIZ], obuf[FTP_BUFSIZ]; /* (these are pretty big...) */ int safe = 0; int xquiet = 0; auth = (fc == GRF_AUTH); #ifndef NOCSETS debug(F101,"ftp getreply lcs","",lcs); debug(F101,"ftp getreply rcs","",rcs); if (lcs > -1 && rcs > -1 && lcs != rcs) { xlate = 1; initxlate(rcs,lcs); xlatec(1,0,rcs,lcs); } #endif /* NOCSETS */ debug(F101,"ftp getreply fc","",fc); if (quiet) xquiet = 1; if (vbm == 9) { xquiet = 1; vbm = 0; } if (ftp_deb) /* DEBUG */ vbm = 1; else if (quiet || dpyactive) /* QUIET or File Transfer Active */ vbm = 0; else if (vbm < 0) /* VERBOSE */ vbm = ftp_vbm; ibuf[0] = '\0'; if (reply_parse) reply_ptr = reply_buf; havesigint = 0; oldintr = signal(SIGINT, cmdcancel); for (count = 0;; count++) { obuf[0] = '\0'; dig = n = ftpcode = i = 0; cp = ftp_reply_str; while ((c = ibuf[0] ? ibuf[i++] : mygetc()) != '\n') { if (c == IAC) { /* Handle telnet commands */ switch (c = mygetc()) { case WILL: case WONT: c = mygetc(); obuf[0] = IAC; obuf[1] = DONT; obuf[2] = c; obuf[3] = NUL; #ifdef CK_SSL if (ssl_ftp_active_flag) { int error, rc; rc = SSL_write(ssl_ftp_con,obuf,3); error = SSL_get_error(ssl_ftp_con,rc); switch (error) { case SSL_ERROR_NONE: break; case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_READ: return(0); case SSL_ERROR_SYSCALL: if (rc == 0) { /* EOF */ break; } else { #ifdef NT int gle = GetLastError(); #endif /* NT */ break; } case SSL_ERROR_WANT_X509_LOOKUP: case SSL_ERROR_SSL: case SSL_ERROR_ZERO_RETURN: default: break; } } else #endif /* CK_SSL */ send(csocket,(SENDARG2TYPE)obuf,3,0); break; case DO: case DONT: c = mygetc(); obuf[0] = IAC; obuf[1] = WONT; obuf[2] = c; obuf[3] = NUL; #ifdef CK_SSL if (ssl_ftp_active_flag) { int error, rc; rc = SSL_write(ssl_ftp_con,obuf,3); error = SSL_get_error(ssl_ftp_con,rc); switch (error) { case SSL_ERROR_NONE: break; case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_READ: signal(SIGINT,oldintr); return(0); case SSL_ERROR_SYSCALL: if (rc == 0) { /* EOF */ break; } else { #ifdef NT int gle = GetLastError(); #endif /* NT */ break; } case SSL_ERROR_WANT_X509_LOOKUP: case SSL_ERROR_SSL: case SSL_ERROR_ZERO_RETURN: default: break; } } else #endif /* CK_SSL */ send(csocket,(SENDARG2TYPE)obuf,3,0); break; default: break; } continue; } dig++; if (c == EOF) { if (expecteof) { signal(SIGINT,oldintr); ftpcode = 221; debug(F101,"ftp getreply EOF","",ftpcode); return(0); } lostpeer(); if (!xquiet) { if (ftp_deb) printf("421 "); printf( "Service not available, connection closed by server\n"); fflush(stdout); } signal(SIGINT,oldintr); ftpcode = 421; debug(F101,"ftp getreply EOF","",ftpcode); return(4); } if (n == 0) { /* First digit */ n = c; /* Save it */ } if (auth_type && #ifdef CK_SSL !ssl_ftp_active_flag && #endif /* CK_SSL */ !ibuf[0] && (n == '6' || continuation)) { if (c != '\r' && dig > 4) obuf[i++] = c; } else { if (auth_type && #ifdef CK_SSL !ssl_ftp_active_flag && #endif /* CK_SSL */ !ibuf[0] && dig == 1 && vbm) printf("Unauthenticated reply received from server:\n"); if (reply_parse) { *reply_ptr++ = c; *reply_ptr = NUL; } if ((!dpyactive || ftp_deb) && /* Don't mess up xfer display */ ftp_cmdlin < 2) { if ((c != '\r') && (ftp_deb || ((vbm || (!auth && n == '5')) && (dig > 4 || ( dig <= 4 && !isdigit(c) && ftpcode == 0 ))))) { #ifdef FTP_PROXY if (ftp_prx && (dig == 1 || (dig == 5 && vbm == 0))) printf("%s:",ftp_host); #endif /* FTP_PROXY */ if (!xquiet) { #ifdef NOCSETS printf("%c",c); #else if (xlate) { xlatec(0,c,rcs,lcs); } else { printf("%c",c); } #endif /* NOCSETS */ } } } } if (auth_type && #ifdef CK_SSL !ssl_ftp_active_flag && #endif /* CK_SSL */ !ibuf[0] && n != '6') continue; if (dig < 4 && isdigit(c)) ftpcode = ftpcode * 10 + (c - '0'); if (!pflag && ftpcode == 227) pflag = 1; if (dig > 4 && pflag == 1 && isdigit(c)) pflag = 2; if (pflag == 2) { if (c != '\r' && c != ')') *pt++ = c; else { *pt = '\0'; pflag = 3; } } if (dig == 4 && c == '-' && n != '6') { if (continuation) ftpcode = 0; continuation++; } if (cp < &ftp_reply_str[FTP_BUFSIZ - 1]) { *cp++ = c; *cp = NUL; } } if (deblog || #ifdef COMMENT /* Sometimes we need to print the server reply. printlines is nonzero for any command where the results are sent back on the control connection rather than the data connection, e.g. STAT. In the TOPS-20 case, each file line has ftpcode 213. But if you do this with a UNIX server, it sends "213-Start STAT", , "213-End" or somesuch. So when printlines is nonzero, we want the 213 lines from TOPS-20 and we DON'T want the 213 lines from UNIX. Further experimentation needed with other servers. Of course RFC959 is mute as to the format of the server reply. 'printlines' is also true for PWD and BYE. */ (printlines && ((ftpcode == 0) || (servertype == SYS_TOPS20))) #else /* No, we can't be that clever -- it breaks other things like RPWD... */ (printlines && (ftpcode != 631 && ftpcode != 632 && ftpcode != 633)) #endif /* COMMENT */ ) { char * q = cp; char *r = ftp_reply_str; *q-- = NUL; /* NUL-terminate */ while (*q < '!' && q > r) /* Strip CR, etc */ *q-- = NUL; if (!ftp_deb && printlines) { /* If printing */ if (ftpcode != 0) /* strip ftpcode if any */ r += 4; #ifdef NOCSETS printf("%s\n",r); /* and print */ #else if (!xlate) { printf("%s\n",r); } else { /* Translating */ xgnbp = r; /* Set up strgetc() */ while ((c0 = xgnbyte(FC_UCS2,rcs,strgetc)) > -1) { if (xpnbyte(c0,TC_UCS2,lcs,NULL) < 0) { /* (xprintc) */ signal(SIGINT,oldintr); return(-1); } } printf("\n"); } #endif /* NOCSETS */ } } debug(F110,"FTP RCVD ",ftp_reply_str,0); if (fc == GRF_FEAT) { /* Parsing FEAT command response? */ if (count == 0 && n == '2') { int i; /* (Re)-init server FEATure table */ debug(F100,"ftp getreply clearing feature table","",0); for (i = 0; i < 16; i++) sfttab[i] = 0; } else { parsefeat((char *)ftp_reply_str); } } if (auth_type && #ifdef CK_SSL !ssl_ftp_active_flag && #endif /* CK_SSL */ !ibuf[0] && n != '6') { signal(SIGINT,oldintr); return(getreply(expecteof,lcs,rcs,vbm,auth)); } ibuf[0] = obuf[i] = '\0'; if (ftpcode && n == '6') if (ftpcode != 631 && ftpcode != 632 && ftpcode != 633) { printf("Unknown reply: %d %s\n", ftpcode, obuf); n = '5'; } else safe = (ftpcode == 631); if (obuf[0] /* if there is a string to decode */ #ifdef CK_SSL && !ssl_ftp_active_flag /* and not SSL/TLS */ #endif /* CK_SSL */ ) { if (!auth_type) { printf("Cannot decode reply:\n%d %s\n", ftpcode, obuf); n = '5'; } #ifndef CK_ENCRYPTION else if (ftpcode == 632) { printf("Cannot decrypt %d reply: %s\n", ftpcode, obuf); n = '5'; } #endif /* CK_ENCRYPTION */ #ifdef NOCONFIDENTIAL else if (ftpcode == 633) { printf("Cannot decrypt %d reply: %s\n", ftpcode, obuf); n = '5'; } #endif /* NOCONFIDENTIAL */ else { int len = FTP_BUFSIZ; if ((kerror = radix_encode((CHAR *)obuf, (CHAR *)ibuf, 0, &len, RADIX_DECODE)) ) { printf("Can't decode base 64 reply %d (%s)\n\"%s\"\n", ftpcode, radix_error(kerror), obuf); n = '5'; } #ifdef FTP_SRP else if (strcmp(auth_type, "SRP") == 0) { int outlen; outlen = srp_decode(!safe, (CHAR *)ibuf, (CHAR *) ibuf, len); if (outlen < 0) { printf("Warning: %d reply %s!\n", ftpcode, safe ? "modified" : "garbled"); n = '5'; } else { ckstrncpy(&ibuf[outlen], "\r\n",FTP_BUFSIZ-outlen); if (ftp_deb) printf("%c:", safe ? 'S' : 'P'); continue; } } #endif /* FTP_SRP */ #ifdef FTP_KRB4 else if (strcmp(auth_type, "KERBEROS_V4") == 0) { if (safe) { kerror = krb_rd_safe((CHAR *)ibuf, len, #ifdef KRB524 ftp_cred.session, #else /* KRB524 */ &ftp_cred.session, #endif /* KRB524 */ &hisctladdr, &myctladdr, &ftp_msg_data ); } else { kerror = krb_rd_priv((CHAR *)ibuf, len, ftp_sched, #ifdef KRB524 ftp_cred.session, #else /* KRB524 */ &ftp_cred.session, #endif /* KRB524 */ &hisctladdr, &myctladdr, &ftp_msg_data ); } if (kerror != KSUCCESS) { printf("%d reply %s! (krb_rd_%s: %s)\n", ftpcode, safe ? "modified" : "garbled", safe ? "safe" : "priv", krb_get_err_text(kerror)); n = '5'; } else if (ftp_msg_data.app_length >= FTP_BUFSIZ - 3) { kerror = KFAILURE; n = '5'; printf("reply data too large for buffer\n"); } else { if (ftp_deb) printf("%c:", safe ? 'S' : 'P'); memcpy(ibuf,ftp_msg_data.app_data, ftp_msg_data.app_length); ckstrncpy(&ibuf[ftp_msg_data.app_length], "\r\n", FTP_BUFSIZ - ftp_msg_data.app_length); continue; } } #endif /* FTP_KRB4 */ #ifdef FTP_GSSAPI else if (strcmp(auth_type, "GSSAPI") == 0) { gss_buffer_desc xmit_buf, msg_buf; OM_uint32 maj_stat, min_stat; int conf_state; xmit_buf.value = ibuf; xmit_buf.length = len; /* decrypt/verify the message */ conf_state = safe; maj_stat = gss_unseal(&min_stat, gcontext, &xmit_buf, &msg_buf, &conf_state, NULL); if (maj_stat != GSS_S_COMPLETE) { user_gss_error(maj_stat, min_stat, "failed unsealing reply"); n = '5'; } else { memcpy(ibuf, msg_buf.value, msg_buf.length); ckstrncpy(&ibuf[msg_buf.length], "\r\n", FTP_BUFSIZ-msg_buf.length); gss_release_buffer(&min_stat,&msg_buf); if (ftp_deb) printf("%c:", safe ? 'S' : 'P'); continue; } } #endif /* FTP_GSSAPI */ /* Other auth types go here... */ } } else if ((!dpyactive || ftp_deb) && ftp_cmdlin < 2 && !xquiet && (vbm || (!auth && (n == '4' || n == '5')))) { #ifdef NOCSETS printf("%c",c); #else if (xlate) { xlatec(0,c,rcs,lcs); } else { printf("%c",c); } #endif /* NOCSETS */ fflush (stdout); } if (continuation && ftpcode != originalcode) { if (originalcode == 0) originalcode = ftpcode; continue; } *cp = '\0'; if (n != '1') cpend = 0; signal(SIGINT,oldintr); if (ftpcode == 421 || originalcode == 421) { lostpeer(); if (!xquiet && !ftp_deb) printf("%s\n",reply_buf); } if ((cancelfile != 0) && #ifndef ULTRIX3 /* Ultrix 3.0 cc objects violently to this clause */ (oldintr != cmdcancel) && #endif /* ULTRIX3 */ (oldintr != SIG_IGN)) { if (oldintr) (*oldintr)(SIGINT); } if (reply_parse) { *reply_ptr = '\0'; if ((reply_ptr = ckstrstr(reply_buf, reply_parse))) { reply_parse = reply_ptr + strlen(reply_parse); if ((reply_ptr = ckstrpbrk(reply_parse, " \r"))) *reply_ptr = '\0'; } else reply_parse = reply_ptr; } while (*cp < '!' && cp > ftp_reply_str) /* Remove trailing junk */ *cp-- = NUL; debug(F111,"ftp getreply",ftp_reply_str,n - '0'); return(n - '0'); } /* for (;;) */ } #ifdef BSDSELECT static int #ifdef CK_ANSIC empty(fd_set * mask, int sec) #else empty(mask, sec) fd_set * mask; int sec; #endif /* CK_ANSIC */ { struct timeval t; t.tv_sec = (long) sec; t.tv_usec = 0L; debug(F100,"ftp empty calling select...","",0); #ifdef INTSELECT x = select(32, (int *)mask, NULL, NULL, &t); #else x = select(32, mask, (fd_set *) 0, (fd_set *) 0, &t); #endif /* INTSELECT */ debug(F101,"ftp empty select","",x); return(x); } #else /* BSDSELECT */ #ifdef IBMSELECT static int empty(mask, cnt, sec) int * mask, sec; int cnt; { return(select(mask,cnt,0,0,sec*1000)); } #endif /* IBMSELECT */ #endif /* BSDSELECT */ static sigtype cancelsend(sig) int sig; { havesigint++; cancelgroup++; cancelfile = 0; printf(" Canceled...\n"); secure_getc(0,1); /* Initialize net input buffers */ debug(F100,"ftp cancelsend caught SIGINT ","",0); fflush(stdout); #ifndef OS2 longjmp(sendcancel, 1); #else PostCtrlCSem(); #endif /* OS2 */ } static VOID #ifdef CK_ANSIC secure_error(char *fmt, ...) #else /* VARARGS1 */ secure_error(fmt, p1, p2, p3, p4, p5) char *fmt; int p1, p2, p3, p4, p5; #endif /* CK_ANSIC */ { #ifdef CK_ANSIC va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); #else fprintf(stderr, fmt, p1, p2, p3, p4, p5); #endif fprintf(stderr, "\n"); } /* * Internal form of settype; changes current type in use with server * without changing our notion of the type for data transfers. * Used to change to and from ascii for listings. */ static VOID changetype(newtype, show) int newtype, show; { int rc; char * s; if ((newtype == curtype) && typesent++) return; switch (newtype) { case FTT_ASC: s = "A"; break; case FTT_BIN: s = "I"; break; case FTT_TEN: s = "L 8"; break; default: s = "I"; break; } rc = ftpcmd("TYPE",s,-1,-1,show); if (rc == REPLY_COMPLETE) curtype = newtype; } /* PUT a file. Returns -1 on error, 0 on success, 1 if file skipped */ static VOID #ifdef CK_ANSIC doftpsend(void * threadinfo) #else doftpsend(threadinfo) VOID * threadinfo; #endif { #ifdef NTSIG if (threadinfo) { /* Thread local storage... */ TlsSetValue(TlsIndex,threadinfo); debug(F100, "doftpsend called with threadinfo block","", 0); } else debug(F100, "doftpsend - threadinfo is NULL", "", 0); #endif /* NTSIG */ #ifdef CK_LOGIN #ifdef IKSD #ifdef NT if (inserver) setntcreds(); #endif /* NT */ #endif /* IKSD */ #endif /* CK_LOGIN */ if (initconn()) { #ifndef NOHTTP int y = -1; /* debug(F101,"doftpsend","tcp_http_proxy",tcp_http_proxy); */ /* If the connection failed and we are using an HTTP Proxy * and the reason for the failure was an authentication * error, then we need to give the user to ability to * enter a username and password, just like a browser. * * I tried to do all of this within the netopen() call * but it is much too much work. */ while (y != 0 && tcp_http_proxy != NULL ) { if (tcp_http_proxy_errno == 401 || tcp_http_proxy_errno == 407 ) { char uid[UIDBUFLEN]; char pwd[PWDSIZ]; struct txtbox tb[2]; int ok; tb[0].t_buf = uid; tb[0].t_len = UIDBUFLEN; tb[0].t_lbl = "Proxy Userid: "; tb[0].t_dflt = NULL; tb[0].t_echo = 1; tb[1].t_buf = pwd; tb[1].t_len = 256; tb[1].t_lbl = "Proxy Passphrase: "; tb[1].t_dflt = NULL; tb[1].t_echo = 2; ok = uq_mtxt("Proxy Server Authentication Required\n", NULL, 2, tb); if (ok && uid[0]) { char * proxy_user, * proxy_pwd; proxy_user = tcp_http_proxy_user; proxy_pwd = tcp_http_proxy_pwd; tcp_http_proxy_user = uid; tcp_http_proxy_pwd = pwd; y = initconn(); debug(F101,"doftpsend","initconn",y); memset(pwd,0,PWDSIZ); tcp_http_proxy_user = proxy_user; tcp_http_proxy_pwd = proxy_pwd; } else break; } else break; } if ( y != 0 ) { #endif /* NOHTTP */ signal(SIGINT, ftpsnd.oldintr); #ifdef SIGPIPE if (ftpsnd.oldintp) signal(SIGPIPE, ftpsnd.oldintp); #endif /* SIGPIPE */ ftpcode = -1; zclose(ZIFILE); ftpsndret = -1; #ifdef NTSIG ckThreadEnd(threadinfo); #endif /* NTSIG */ return; #ifndef NOHTTP } #endif /* NOHTTP */ } ftpsndret = 0; #ifdef NTSIG ckThreadEnd(threadinfo); #endif /* NTSIG */ } static VOID #ifdef CK_ANSIC failftpsend(void * threadinfo) #else failftpsend(threadinfo) VOID * threadinfo; #endif /* CK_ANSIC */ { #ifdef NTSIG if (threadinfo) { /* Thread local storage... */ TlsSetValue(TlsIndex,threadinfo); debug(F100, "docmdfile called with threadinfo block","", 0); } else debug(F100, "docmdfile - threadinfo is NULL", "", 0); #endif /* NTSIG */ #ifdef CK_LOGIN #ifdef IKSD #ifdef NT if (inserver) setntcreds(); #endif /* NT */ #endif /* IKSD */ #endif /* CK_LOGIN */ while (cpend) { ftpsnd.reply = getreply(0,ftpsnd.incs,ftpsnd.outcs,ftp_vbm,0); debug(F111,"ftp sendrequest getreply","null command",ftpsnd.reply); } if (data >= 0) { #ifdef CK_SSL if (ssl_ftp_data_active_flag) { SSL_shutdown(ssl_ftp_data_con); SSL_free(ssl_ftp_data_con); ssl_ftp_data_active_flag = 0; ssl_ftp_data_con = NULL; } #endif /* CK_SSL */ #ifdef TCPIPLIB socket_close(data); #else /* TCPIPLIB */ #ifdef USE_SHUTDOWN shutdown(data, 1+1); #endif /* USE_SHUTDOWN */ close(data); #endif /* TCPIPLIB */ data = -1; globaldin = -1; } if (ftpsnd.oldintr) signal(SIGINT,ftpsnd.oldintr); #ifdef SIGPIPE if (ftpsnd.oldintp) signal(SIGPIPE,ftpsnd.oldintp); #endif /* SIGPIPE */ ftpcode = -1; #ifndef OS2 /* TEST ME IN K95 */ if (havesigint) { havesigint = 0; debug(F100,"ftp failftpsend chain to trap()...","",0); if (ftpsnd.oldintr != SIG_IGN) (*ftpsnd.oldintr)(SIGINT); /* NOTREACHED (I hope!) */ debug(F100,"ftp failftpsend return from trap()...","",0); } #endif /* OS2 */ } static VOID #ifdef CK_ANSIC failftpsend2(void * threadinfo) #else failftpsend2(threadinfo) VOID * threadinfo; #endif /* CK_ANSIC */ { #ifdef NTSIG if (threadinfo) { /* Thread local storage... */ TlsSetValue(TlsIndex,threadinfo); debug(F100, "docmdfile called with threadinfo block","", 0); } else debug(F100, "docmdfile - threadinfo is NULL", "", 0); #endif /* NTSIG */ #ifdef CK_LOGIN #ifdef IKSD #ifdef NT if (inserver) setntcreds(); #endif /* NT */ #endif /* IKSD */ #endif /* CK_LOGIN */ debug(F101,"ftp sendrequest canceled","",ftpsnd.bytes); tfc += ffc; #ifdef GFTIMER fpfsecs = gftimer(); #endif /* GFTIMER */ zclose(ZIFILE); #ifdef PIPESEND if (sndfilter) pipesend = 0; #endif /* PIPESEND */ signal(SIGINT, ftpsnd.oldintr); #ifdef SIGPIPE if (ftpsnd.oldintp) signal(SIGPIPE, ftpsnd.oldintp); #endif /* SIGPIPE */ if (!cpend) { ftpcode = -1; ftpsndret = -1; #ifdef NTSIG ckThreadEnd(threadinfo); #endif /* NTSIG */ return; } if (data >= 0) { #ifdef CK_SSL if (ssl_ftp_data_active_flag) { SSL_shutdown(ssl_ftp_data_con); SSL_free(ssl_ftp_data_con); ssl_ftp_data_active_flag = 0; ssl_ftp_data_con = NULL; } #endif /* CK_SSL */ #ifdef TCPIPLIB socket_close(data); #else /* TCPIPLIB */ #ifdef USE_SHUTDOWN shutdown(data, 1+1); #endif /* USE_SHUTDOWN */ close(data); #endif /* TCPIPLIB */ data = -1; globaldin = -1; } if (dout) { #ifdef TCPIPLIB socket_close(dout); #else /* TCPIPLIB */ #ifdef USE_SHUTDOWN shutdown(dout, 1+1); #endif /* USE_SHUTDOWN */ close(dout); #endif /* TCPIPLIB */ } ftpsnd.reply = getreply(0,ftpsnd.incs,ftpsnd.outcs,ftp_vbm,0); ftpcode = -1; ftpsndret = -1; #ifndef OS2 /* TEST ME IN K95 */ if (havesigint) { havesigint = 0; debug(F100,"ftp failftpsend2 chain to trap()...","",0); if (ftpsnd.oldintr != SIG_IGN) (*ftpsnd.oldintr)(SIGINT); /* NOTREACHED (I hope!) */ debug(F100,"ftp failftpsend2 return from trap()...","",0); } #endif /* OS2 */ } static VOID #ifdef CK_ANSIC doftpsend2(void * threadinfo) #else doftpsend2(threadinfo) VOID * threadinfo; #endif { register int c, d = 0; int n, t, x, notafile, unique = 0; char *buf, *bufp; #ifdef NTSIG if (threadinfo) { /* Thread local storage... */ TlsSetValue(TlsIndex,threadinfo); debug(F100, "doftpsend2 called with threadinfo block","", 0); } else debug(F100, "doftpsend2 - threadinfo is NULL", "", 0); #endif /* NTSIG */ #ifdef CK_LOGIN #ifdef IKSD #ifdef NT if (inserver) setntcreds(); #endif /* NT */ #endif /* IKSD */ #endif /* CK_LOGIN */ buf = ftpsndbuf; /* (not on stack) */ unique = strcmp(ftpsnd.cmd,"STOU") ? 0 : 1; notafile = sndarray || pipesend; #ifdef FTP_RESTART if (ftpsnd.restart && ((curtype == FTT_BIN) || (alike > 0))) { char * p; changetype(FTT_BIN,0); /* Change to binary */ /* Ask for remote file's size */ x = ftpcmd("SIZE",ftpsnd.remote,ftpsnd.incs,ftpsnd.outcs,ftp_vbm); if (x == REPLY_COMPLETE) { /* Have ftpsnd.reply */ p = &ftp_reply_str[4]; /* Parse it */ while (isdigit(*p)) { sendstart = sendstart * 10 + (int)(*p - '0'); p++; } if (*p && *p != CR) { /* Bad number */ debug(F110,"doftpsend2 bad size",ftp_reply_str,0); sendstart = (CK_OFF_T)0; } else if (sendstart > fsize) { /* Remote file bigger than local */ debug(F110,"doftpsend2 big size",ckfstoa(fsize),sendstart); sendstart = (CK_OFF_T)0; } /* Local is newer */ debug(F111,"doftpsend2 size",ftpsnd.remote,sendstart); if (chkmodtime(ftpsnd.local,ftpsnd.remote,0) == 2) { debug(F110,"doftpsend2 date mismatch",ftp_reply_str,0); sendstart = (CK_OFF_T)0; /* Send the whole file */ } } changetype(ftp_typ,0); /* Change back to appropriate type */ if (sendstart > (CK_OFF_T)0) { /* Still restarting? */ if (sendstart == fsize) { /* Same size - no need to send */ debug(F111,"doftpsend2 /restart SKIP", ckfstoa(fsize),sendstart); zclose(ZIFILE); ftpsndret = SKP_RES; #ifdef NTSIG ckThreadEnd(threadinfo); #endif /* NTSIG */ return; } errno = 0; /* Restart needed, seek to the spot */ if (zfseek((long)sendstart) < 0) { debug(F111,"doftpsend2 zfseek fails", ftpsnd.local,sendstart); fprintf(stderr, "FSEEK: %s: %s\n", ftpsnd.local, ck_errstr()); sendstart = 0; zclose(ZIFILE); ftpsndret = -1; #ifdef NTSIG ckThreadEnd(threadinfo); #endif /* NTSIG */ return; } #ifdef COMMENT debug(F111,"doftpsend2 zfseek ok",ftpsnd.local,sendstart); x = ftpcmd("REST",ckltoa(sendstart),-1,-1,ftp_vbm); if (x != REPLY_CONTINUE) { sendstart = 0; zclose(ZIFILE); ftpsndret = -1; #ifdef NTSIG ckThreadEnd(threadinfo); #endif /* NTSIG */ return; } else { ftpsnd.cmd = "STOR"; } #else sendmode = SM_RESEND; ftpsnd.cmd = "APPE"; #endif /* COMMENT */ /* sendstart = (CK_OFF_T)0; */ } } #endif /* FTP_RESTART */ if (unique && !stouarg) /* If we know STOU accepts no arg */ ftpsnd.remote = NULL; /* don't include one. */ x = ftpcmd(ftpsnd.cmd, ftpsnd.remote, ftpsnd.incs, ftpsnd.outcs, ftp_vbm); debug(F111,"doftpsend2 ftpcode",ftpsnd.cmd,ftpcode); debug(F101,"doftpsend2 ftpcmd","",x); if (x != REPLY_PRELIM && unique) { /* RFC959 says STOU does not take an argument. But every FTP server I've encountered but one accepts the arg and constructs the unique name from it, which is better than making up a totally random name for the file, which is what RFC959 calls for. Especially because there is no way for the client to find out the name chosen by the server. So we try STOU with the argument first, which works with most servers, and if it fails we retry it without the arg, for the benefit of the one picky server that is not "liberal in what it accepts" UNLESS the first STOU got a 502 code ("not implemented") which means STOU is not accepted, period. */ if ((x == 5) && stouarg && (ftpcode != 502)) { x = ftpcmd(ftpsnd.cmd,NULL,ftpsnd.incs,ftpsnd.outcs,ftp_vbm); if (x == REPLY_PRELIM) /* If accepted */ stouarg = 0; /* flag no STOU arg for this server */ } } if (x != REPLY_PRELIM) { signal(SIGINT, ftpsnd.oldintr); #ifdef SIGPIPE if (ftpsnd.oldintp) signal(SIGPIPE, ftpsnd.oldintp); #endif /* SIGPIPE */ debug(F101,"doftpsend2 not REPLY_PRELIM","",x); zclose(ZIFILE); #ifdef PIPESEND if (sndfilter) pipesend = 0; #endif /* PIPESEND */ ftpsndret = -1; #ifdef NTSIG ckThreadEnd(threadinfo); #endif /* NTSIG */ return; } debug(F100,"doftpsend2 getting data connection...","",0); dout = dataconn(ftpsnd.lmode); /* Get data connection */ debug(F101,"doftpsend2 dataconn","",dout); if (dout == -1) { failftpsend2(threadinfo); #ifdef NTSIG ckThreadEnd(threadinfo); #endif /* NTSIG */ return; } /* Initialize per-file stats */ ffc = (CK_OFF_T)0; /* Character counter */ cps = oldcps = 0L; /* Thruput */ n = 0; #ifdef GFTIMER rftimer(); /* reset f.p. timer */ #endif /* GFTIMER */ #ifdef SIGPIPE ftpsnd.oldintp = signal(SIGPIPE, SIG_IGN); #endif /* SIGPIPE */ debug(F101,"doftpsend2 curtype","",curtype); switch (curtype) { case FTT_BIN: /* Binary mode */ case FTT_TEN: errno = d = 0; #ifdef VMS /* This is because VMS zxin() is C-Library fread() but the file was opened with zopeni(), which is RMS. */ while (((c = zminchar()) > -1) && !cancelfile) { ffc++; if (zzout(dout,c) < 0) break; } #else /* VMS */ while ((n = zxin(ZIFILE,buf,FTP_BUFSIZ - 1)) > 0 && !cancelfile) { ftpsnd.bytes += n; ffc += n; debug(F111,"doftpsend2 zxin",ckltoa(n),ffc); ckhexdump("doftpsend2 zxin",buf,16); #ifdef CK_SSL if (ssl_ftp_data_active_flag) { for (bufp = buf; n > 0; n -= d, bufp += d) { if ((d = SSL_write(ssl_ftp_data_con, bufp, n)) <= 0) break; spackets++; pktnum++; if (fdispla != XYFD_B) { spktl = d; ftscreen(SCR_PT,'D',(CK_OFF_T)spackets,NULL); } } } else { #endif /* CK_SSL */ for (bufp = buf; n > 0; n -= d, bufp += d) { if (((d = secure_write(dout, (CHAR *)bufp, n)) <= 0) || iscanceled()) break; spackets++; pktnum++; if (fdispla != XYFD_B) { spktl = d; ftscreen(SCR_PT,'D',(CK_OFF_T)spackets,NULL); } } #ifdef CK_SSL } #endif /* CK_SSL */ if (d <= 0) break; } #endif /* VMS */ debug(F111,"doftpsend2 XX zxin",ckltoa(n),ffc); if (n < 0) fprintf(stderr, "local: %s: %s\n", ftpsnd.local, ck_errstr()); if (d < 0 || (d = secure_flush(dout)) < 0) { if (d == -1 && errno && errno != EPIPE) perror("netout"); ftpsnd.bytes = -1; } break; case FTT_ASC: /* Text mode */ #ifndef NOCSETS if (ftpsnd.xlate) { /* With translation */ initxlate(ftpsnd.incs,ftpsnd.outcs); while (!cancelfile) { if ((c0 = xgnbyte(FC_UCS2,ftpsnd.incs,NULL)) < 0) break; if ((x = xpnbyte(c0,TC_UCS2,ftpsnd.outcs,xxout)) < 0) break; } } else { #endif /* NOCSETS */ /* Text mode, no translation */ while (((c = zminchar()) > -1) && !cancelfile) { ffc++; if (xxout(c) < 0) break; } d = 0; #ifndef NOCSETS } #endif /* NOCSETS */ if (dout == -1 || (d = secure_flush(dout)) < 0) { if (d == -1 && errno && errno != EPIPE) perror("netout"); ftpsnd.bytes = -1; } break; } tfc += ffc; /* Total file chars */ #ifdef GFTIMER fpfsecs = gftimer(); #endif /* GFTIMER */ zclose(ZIFILE); /* Close input file */ #ifdef PIPESEND if (sndfilter) /* Undo this (it's per file) */ pipesend = 0; #endif /* PIPESEND */ #ifdef CK_SSL if (ssl_ftp_data_active_flag) { SSL_shutdown(ssl_ftp_data_con); SSL_free(ssl_ftp_data_con); ssl_ftp_data_active_flag = 0; ssl_ftp_data_con = NULL; } #endif /* CK_SSL */ #ifdef TCPIPLIB socket_close(dout); /* Close data connection */ #else /* TCPIPLIB */ #ifdef USE_SHUTDOWN shutdown(dout, 1+1); #endif /* USE_SHUTDOWN */ close(dout); #endif /* TCPIPLIB */ ftpsnd.reply = getreply(0,ftpsnd.incs,ftpsnd.outcs,ftp_vbm,0); signal(SIGINT, ftpsnd.oldintr); /* Put back interrupts */ #ifdef SIGPIPE if (ftpsnd.oldintp) signal(SIGPIPE, ftpsnd.oldintp); #endif /* SIGPIPE */ if (ftpsnd.reply == REPLY_TRANSIENT || ftpsnd.reply == REPLY_ERROR) { debug(F101,"doftpsend2 ftpsnd.reply","",ftpsnd.reply); ftpsndret = -1; #ifdef NTSIG ckThreadEnd(threadinfo); #endif /* NTSIG */ return; } else if (cancelfile) { debug(F101,"doftpsend2 canceled","",ftpsnd.bytes); ftpsndret = -1; #ifdef NTSIG ckThreadEnd(threadinfo); #endif /* NTSIG */ return; } debug(F101,"doftpsend2 ok","",ftpsnd.bytes); ftpsndret = 0; #ifdef NTSIG ckThreadEnd(threadinfo); #endif /* NTSIG */ } static int sendrequest(cmd, local, remote, xlate, incs, outcs, restart) char *cmd, *local, *remote; int xlate, incs, outcs, restart; { if (!remote) remote = ""; /* Check args */ if (!*remote) remote = local; if (!local) local = ""; if (!*local) return(-1); if (!cmd) cmd = ""; if (!*cmd) cmd = "STOR"; debug(F111,"ftp sendrequest restart",local,restart); nout = 0; /* Init output buffer count */ ftpsnd.bytes = 0; /* File input byte count */ dout = -1; #ifdef FTP_PROXY if (proxy) { proxtrans(cmd, local, remote, !strcmp(cmd,"STOU")); return(0); } #endif /* FTP_PROXY */ changetype(ftp_typ,0); /* Change type for this file */ ftpsnd.oldintr = NULL; /* Set up interrupt handler */ ftpsnd.oldintp = NULL; ftpsnd.restart = restart; ftpsnd.xlate = xlate; ftpsnd.lmode = "wb"; #ifdef PIPESEND /* Use Kermit API for file i/o... */ if (sndfilter) { char * p = NULL, * q; #ifndef NOSPL int n = CKMAXPATH; if (cmd_quoting && (p = (char *) malloc(n + 1))) { q = p; debug(F110,"sendrequest pipesend filter",sndfilter,0); zzstring(sndfilter,&p,&n); debug(F111,"sendrequest pipename",q,n); if (n <= 0) { printf("?Sorry, send filter + filename too long, %d max.\n", CKMAXPATH ); free(q); return(-1); } ckstrncpy(filnam,q,CKMAXPATH+1); free(q); local = filnam; } #endif /* NOSPL */ } if (sndfilter) /* If sending thru a filter */ pipesend = 1; /* set this for open and i/o */ #endif /* PIPESEND */ #ifdef VMS debug(F101,"XXX before openi binary","",binary); debug(F101,"XXX before openi ftp_typ","",ftp_typ); #endif /* VMS */ if (openi(local) == 0) /* Try to open the input file */ return(-1); #ifdef VMS debug(F101,"XXX after openi binary","",binary); debug(F101,"XXX after openi ftp_typ","",ftp_typ); if (!forcetype) { if (binary != ftp_typ) { /* VMS zopeni() sets binary */ debug(F101,"XXX changing type","",binary); doftptyp(binary); debug(F101,"XXX after doftptyp","",ftp_typ); /* **** */ if (displa && fdispla) { /* Update file type display */ ftscreen(SCR_FN,'F',(CK_OFF_T)0,local); } } } #endif /* VMS */ ftpsndret = 0; ftpsnd.incs = incs; ftpsnd.outcs = outcs; ftpsnd.cmd = cmd; ftpsnd.local = local; ftpsnd.remote = remote; ftpsnd.oldintr = signal(SIGINT, cancelsend); havesigint = 0; if (cc_execute(ckjaddr(sendcancel), doftpsend, failftpsend) < 0) return(-1); if (ftpsndret < 0) return(-1); if (cc_execute(ckjaddr(sendcancel), doftpsend2, failftpsend2) < 0) return(-1); return(ftpsndret); } static sigtype cancelrecv(sig) int sig; { havesigint++; cancelfile = 0; cancelgroup++; secure_getc(0,1); /* Initialize net input buffers */ printf(" Canceling...\n"); debug(F100,"ftp cancelrecv caught SIGINT","",0); fflush(stdout); if (fp_nml) { if (fp_nml != stdout) fclose(fp_nml); fp_nml = NULL; } #ifndef OS2 longjmp(recvcancel, 1); #else PostCtrlCSem(); #endif /* OS2 */ } /* Argumentless front-end for secure_getc() */ static int netgetc() { return(secure_getc(globaldin,0)); } /* Returns -1 on failure, 0 on success, 1 if file skipped */ /* Sets ftpcode < 0 on failure if failure reason is not server reply code: -1: interrupted by user. -2: error opening or writing output file (reason in errno). -3: failure to make data connection. -4: network read error (reason in errno). */ struct xx_ftprecv { int reply; int fcs; int rcs; int recover; int xlate; int din; int is_retr; sig_t oldintr, oldintp; char * cmd; char * local; char * remote; char * lmode; char * pipename; int tcrflag; CK_OFF_T localsize; }; static struct xx_ftprecv ftprecv; static int ftprecvret = 0; static VOID #ifdef CK_ANSIC failftprecv(VOID * threadinfo) #else failftprecv(threadinfo) VOID * threadinfo; #endif /* CK_ANSIC */ { #ifdef NTSIG if (threadinfo) { /* Thread local storage... */ TlsSetValue(TlsIndex,threadinfo); debug(F100, "docmdfile called with threadinfo block","", 0); } else debug(F100, "docmdfile - threadinfo is NULL", "", 0); #endif /* NTSIG */ #ifdef CK_LOGIN #ifdef IKSD #ifdef NT if (inserver) setntcreds(); #endif /* NT */ #endif /* IKSD */ #endif /* CK_LOGIN */ while (cpend) { ftprecv.reply = getreply(0,ftprecv.fcs,ftprecv.rcs,ftp_vbm,0); } if (data >= 0) { #ifdef CK_SSL if (ssl_ftp_data_active_flag) { SSL_shutdown(ssl_ftp_data_con); SSL_free(ssl_ftp_data_con); ssl_ftp_data_active_flag = 0; ssl_ftp_data_con = NULL; } #endif /* CK_SSL */ #ifdef TCPIPLIB socket_close(data); #else /* TCPIPLIB */ #ifdef USE_SHUTDOWN shutdown(data, 1+1); #endif /* USE_SHUTDOWN */ close(data); #endif /* TCPIPLIB */ data = -1; globaldin = -1; } if (ftprecv.oldintr) signal(SIGINT, ftprecv.oldintr); ftpcode = -1; ftprecvret = -1; #ifndef OS2 /* TEST ME IN K95 */ if (havesigint) { havesigint = 0; debug(F100,"ftp failftprecv chain to trap()...","",0); if (ftprecv.oldintr != SIG_IGN) (*ftprecv.oldintr)(SIGINT); /* NOTREACHED (I hope!) */ debug(F100,"ftp failftprecv return from trap()...","",0); } #endif /* OS2 */ return; } static VOID #ifdef CK_ANSIC doftprecv(VOID * threadinfo) #else doftprecv(threadinfo) VOID * threadinfo; #endif /* CK_ANSIC */ { #ifdef NTSIG if (threadinfo) { /* Thread local storage... */ TlsSetValue(TlsIndex,threadinfo); debug(F100, "docmdfile called with threadinfo block","", 0); } else debug(F100, "docmdfile - threadinfo is NULL", "", 0); #endif /* NTSIG */ #ifdef CK_LOGIN #ifdef IKSD #ifdef NT if (inserver) setntcreds(); #endif /* NT */ #endif /* IKSD */ #endif /* CK_LOGIN */ #ifndef COMMENT if (!out2screen && !ftprecv.pipename) { int x; char * local; local = ftprecv.local; x = zchko(local); if (x < 0) { if ((!dpyactive || ftp_deb)) fprintf(stderr, "Temporary file %s: %s\n", ftprecv.local, ck_errstr()); signal(SIGINT, ftprecv.oldintr); ftpcode = -2; ftprecvret = -1; #ifdef NTSIG ckThreadEnd(threadinfo); #endif /* NTSIG */ return; } } #endif /* COMMENT */ changetype((!ftprecv.is_retr) ? FTT_ASC : ftp_typ, 0); if (initconn()) { /* Initialize the data connection */ signal(SIGINT, ftprecv.oldintr); ftpcode = -1; ftprecvret = -3; #ifdef NTSIG ckThreadEnd(threadinfo); #endif /* NTSIG */ return; } secure_getc(0,1); /* Initialize net input buffers */ ftprecvret = 0; #ifdef NTSIG ckThreadEnd(threadinfo); #endif /* NTSIG */ } static VOID #ifdef CK_ANSIC failftprecv2(VOID * threadinfo) #else failftprecv2(threadinfo) VOID * threadinfo; #endif /* CK_ANSIC */ { #ifdef NTSIG if (threadinfo) { /* Thread local storage... */ TlsSetValue(TlsIndex,threadinfo); debug(F100, "docmdfile called with threadinfo block","", 0); } else debug(F100, "docmdfile - threadinfo is NULL", "", 0); #endif /* NTSIG */ #ifdef CK_LOGIN #ifdef IKSD #ifdef NT if (inserver) setntcreds(); #endif /* NT */ #endif /* IKSD */ #endif /* CK_LOGIN */ /* Cancel using RFC959 recommended IP,SYNC sequence */ debug(F100,"ftp recvrequest CANCEL","",0); #ifdef GFTIMER fpfsecs = gftimer(); #endif /* GFTIMER */ #ifdef SIGPIPE if (ftprecv.oldintp) signal(SIGPIPE, ftprecv.oldintr); #endif /* SIGPIPE */ signal(SIGINT, SIG_IGN); if (!cpend) { ftpcode = -1; signal(SIGINT, ftprecv.oldintr); ftprecvret = -1; #ifdef NTSIG ckThreadEnd(threadinfo); #endif /* NTSIG */ return; } cancel_remote(ftprecv.din); #ifdef FTP_TIMEOUT if (ftp_timed_out && out2screen && !quiet) printf("\n?Timed out.\n"); #endif /* FTP_TIMEOUT */ if (ftpcode > -1) ftpcode = -1; if (data >= 0) { #ifdef CK_SSL if (ssl_ftp_data_active_flag) { SSL_shutdown(ssl_ftp_data_con); SSL_free(ssl_ftp_data_con); ssl_ftp_data_active_flag = 0; ssl_ftp_data_con = NULL; } #endif /* CK_SSL */ #ifdef TCPIPLIB socket_close(data); #else /* TCPIPLIB */ #ifdef USE_SHUTDOWN shutdown(data, 1+1); #endif /* USE_SHUTDOWN */ close(data); #endif /* TCPIPLIB */ data = -1; globaldin = -1; } if (!out2screen) { int x = 0; debug(F111,"ftp failrecv2 zclose",ftprecv.local,keep); zclose(ZOFILE); switch (keep) { /* which is... */ case SET_AUTO: /* AUTO */ if (curtype == FTT_ASC) /* Delete file if TYPE A. */ x = 1; break; case SET_OFF: /* DISCARD */ x = 1; /* Delete file, period. */ break; default: /* KEEP */ break; } if (x) { x = zdelet(ftprecv.local); debug(F111,"ftp failrecv2 delete incomplete",ftprecv.local,x); } } if (ftprecv.din) { #ifdef TCPIPLIB socket_close(ftprecv.din); #else /* TCPIPLIB */ #ifdef USE_SHUTDOWN shutdown(ftprecv.din, 1+1); #endif /* USE_SHUTDOWN */ close(ftprecv.din); #endif /* TCPIPLIB */ } signal(SIGINT, ftprecv.oldintr); ftprecvret = -1; if (havesigint) { havesigint = 0; debug(F100,"FTP failftprecv2 chain to trap()...","",0); #ifdef OS2 debug(F100,"FTP failftprecv2 PostCtrlCSem()...","",0); PostCtrlCSem(); #else /* OS2 */ if (ftprecv.oldintr != SIG_IGN) (*ftprecv.oldintr)(SIGINT); /* NOTREACHED (I hope!) */ debug(F100,"ftp failftprecv2 return from trap()...","",0); #endif /* OS2 */ } } static VOID #ifdef CK_ANSIC doftprecv2(VOID * threadinfo) #else doftprecv2(threadinfo) VOID * threadinfo; #endif /* CK_ANSIC */ { register int c, d; CK_OFF_T bytes = (CK_OFF_T)0; int bare_lfs = 0; int blksize = 0; ULONG start = 0L, stop; char * p; static char * rcvbuf = NULL; static int rcvbufsiz = 0; #ifdef CK_URL char newname[CKMAXPATH+1]; /* For file dialog */ #endif /* CK_URL */ extern int adl_ask; #ifdef FTP_TIMEOUT ftp_timed_out = 0; #endif /* FTP_TIMEOUT */ ftprecv.din = -1; #ifdef NTSIG if (threadinfo) { /* Thread local storage... */ TlsSetValue(TlsIndex,threadinfo); debug(F100, "docmdfile called with threadinfo block","", 0); } else debug(F100, "docmdfile - threadinfo is NULL", "", 0); #endif /* NTSIG */ #ifdef CK_LOGIN #ifdef IKSD #ifdef NT if (inserver) setntcreds(); #endif /* NT */ #endif /* IKSD */ #endif /* CK_LOGIN */ if (ftprecv.recover) { /* Initiate recovery */ x = ftpcmd("REST",ckfstoa(ftprecv.localsize),-1,-1,ftp_vbm); debug(F111,"ftp reply","REST",x); if (x == REPLY_CONTINUE) { ftprecv.lmode = "ab"; rs_len = ftprecv.localsize; } else { ftprecv.recover = 0; } } /* IMPORTANT: No FTP commands can come between REST and RETR! */ debug(F111,"ftp recvrequest recover E",ftprecv.remote,ftprecv.recover); /* Send the command and get reply */ debug(F110,"ftp recvrequest cmd",ftprecv.cmd,0); debug(F110,"ftp recvrequest remote",ftprecv.remote,0); if (ftpcmd(ftprecv.cmd,ftprecv.remote,ftprecv.fcs,ftprecv.rcs,ftp_vbm) != REPLY_PRELIM) { signal(SIGINT, ftprecv.oldintr); /* Bad reply, fail. */ ftprecvret = -1; /* ftpcode is set by ftpcmd() */ #ifdef NTSIG ckThreadEnd(threadinfo); #endif /* NTSIG */ return; } ftprecv.din = dataconn("r"); /* Good reply, open data connection */ globaldin = ftprecv.din; /* Global copy of file descriptor */ if (ftprecv.din == -1) { /* Check for failure */ ftpcode = -3; /* Code for no data connection */ ftprecvret = -1; #ifdef NTSIG ckThreadEnd(threadinfo); #endif /* NTSIG */ return; } #ifdef CK_URL /* In K95 GUI put up a file box */ if (haveurl && g_url.pth && adl_ask ) { /* Downloading from a URL */ int x; char * preface = "\r\nIncoming file from FTP server...\r\n\ Please confirm output file specification or supply an alternative:"; x = uq_file(preface, /* K95 GUI: Put up file box. */ NULL, 4, NULL, ftprecv.local ? ftprecv.local : ftprecv.remote, newname, CKMAXPATH+1 ); if (x > 0) { ftprecv.local = newname; /* Substitute user's file name */ if (x == 2) /* And append if user said to */ ftprecv.lmode = "ab"; } } #endif /* CK_URL */ x = 1; /* Output file open OK? */ if (ftprecv.pipename) { /* Command */ x = zxcmd(ZOFILE,ftprecv.pipename); debug(F111,"ftp recvrequest zxcmd",ftprecv.pipename,x); } else if (!out2screen) { /* File */ struct filinfo xx; xx.bs = 0; xx.cs = 0; xx.rl = 0; xx.org = 0; xx.cc = 0; xx.typ = 0; xx.os_specific = NUL; xx.lblopts = 0; /* Append or New */ xx.dsp = !strcmp(ftprecv.lmode,"ab") ? XYFZ_A : XYFZ_N; x = zopeno(ZOFILE,ftprecv.local,NULL,&xx); debug(F111,"ftp recvrequest zopeno",ftprecv.local,x); } if (x < 1) { /* Failure to open output file */ if ((!dpyactive || ftp_deb)) fprintf(stderr, "local(2): %s: %s\n", ftprecv.local, ck_errstr()); ftprecvret = -1; #ifdef NTSIG ckThreadEnd(threadinfo); #endif /* NTSIG */ return; } blksize = FTP_BUFSIZ; /* Allocate input buffer */ debug(F101,"ftp recvrequest blksize","",blksize); debug(F101,"ftp recvrequest rcvbufsiz","",rcvbufsiz); if (rcvbufsiz < blksize) { /* if necessary */ if (rcvbuf) { free(rcvbuf); rcvbuf = NULL; } rcvbuf = (char *)malloc((unsigned)blksize); if (!rcvbuf) { debug(F100,"ftp get rcvbuf malloc failed","",0); ftpcode = -2; #ifdef ENOMEM errno = ENOMEM; #endif /* ENOMEM */ if ((!dpyactive || ftp_deb)) perror("malloc"); rcvbufsiz = 0; ftprecvret = -1; #ifdef NTSIG ckThreadEnd(threadinfo); #endif /* NTSIG */ return; } debug(F101,"ftp get rcvbuf malloc ok","",blksize); rcvbufsiz = blksize; } debug(F111,"ftp get rcvbufsiz",ftprecv.local,rcvbufsiz); ffc = (CK_OFF_T)0; /* Character counter */ cps = oldcps = 0L; /* Thruput */ start = gmstimer(); /* Start time (msecs) */ #ifdef GFTIMER rftimer(); /* Start time (float) */ #endif /* GFTIMER */ debug(F111,"ftp get type",ftprecv.local,curtype); debug(F101,"ftp recvrequest ftp_dpl","",ftp_dpl); switch (curtype) { case FTT_BIN: /* Binary mode */ case FTT_TEN: /* TENEX mode */ d = 0; while (1) { errno = 0; c = secure_read(ftprecv.din, rcvbuf, rcvbufsiz); if (cancelfile) { failftprecv2(threadinfo); #ifdef NTSIG ckThreadEnd(threadinfo); #endif /* NTSIG */ return; } if (c < 1) break; #ifdef printf /* (What if it isn't?) */ if (out2screen && !ftprecv.pipename) { int i; for (i = 0; i < c; i++) printf("%c",rcvbuf[i]); } else #endif /* printf */ { register int i; i = 0; errno = 0; while (i < c) { if (zmchout(rcvbuf[i++]) < 0) { d = i; break; } } } bytes += c; ffc += c; } #ifdef FTP_TIMEOUT if (c == -3) { debug(F100,"ftp recvrequest timeout","",0); bytes = (CK_OFF_T)-1; ftp_timed_out = 1; ftpcode = -3; } else #endif /* FTP_TIMEOUT */ if (c < 0) { debug(F111,"ftp recvrequest errno",ckitoa(c),errno); if (c == -1 && errno != EPIPE) if ((!dpyactive || ftp_deb)) perror("netin"); bytes = (CK_OFF_T)-1; ftpcode = -4; } if (d < c) { ftpcode = -2; if ((!dpyactive || ftp_deb)) { char * p; p = ftprecv.local ? ftprecv.local : ftprecv.pipename; if (d < 0) fprintf(stderr, "local(3): %s: %s\n", ftprecv.local, ck_errstr()); else fprintf(stderr, "%s: short write\n", ftprecv.local); } } break; case FTT_ASC: /* Text mode */ debug(F101,"ftp recvrequest TYPE A xlate","",ftprecv.xlate); #ifndef NOCSETS if (ftprecv.xlate) { int t; #ifdef CK_ANSIC int (*fn)(char); #else int (*fn)(); #endif /* CK_ANSIC */ debug(F110,"ftp recvrequest (data)","initxlate",0); initxlate(ftprecv.rcs,ftprecv.fcs); /* (From,To) */ if (ftprecv.pipename) { fn = pipeout; debug(F110,"ftp recvrequest ASCII","pipeout",0); } else { fn = out2screen ? scrnout : putfil; debug(F110,"ftp recvrequest ASCII", out2screen ? "scrnout" : "putfil",0); } while (1) { /* Get byte from net */ c0 = xgnbyte(FC_UCS2,ftprecv.rcs,netgetc); if (cancelfile) { failftprecv2(threadinfo); #ifdef NTSIG ckThreadEnd(threadinfo); #endif /* NTSIG */ return; } if (c0 < 0) break; /* Second byte from net */ c1 = xgnbyte(FC_UCS2,ftprecv.rcs,netgetc); if (cancelfile) { failftprecv2(threadinfo); #ifdef NTSIG ckThreadEnd(threadinfo); #endif /* NTSIG */ return; } if (c1 < 0) break; #ifdef COMMENT /* K95: Check whether we need this */ if (fileorder > 0) /* Little Endian */ bytswap(&c0,&c1); /* swap bytes*/ #endif /* COMMENT */ #ifdef OS2 if ( out2screen && /* we're translating to UCS-2 */ !k95stdout && !inserver) /* for the real screen... */ { union { USHORT ucs2; UCHAR bytes[2]; } output; output.bytes[0] = c1; output.bytes[1] = c0; VscrnWrtUCS2StrAtt(VCMD, &output.ucs2, 1, wherey[VCMD], wherex[VCMD], &colorcmd ); } else #endif /* OS2 */ { if ((x = xpnbyte(c0,TC_UCS2,ftprecv.fcs,fn)) < 0) break; if ((x = xpnbyte(c1,TC_UCS2,ftprecv.fcs,fn)) < 0) break; } } } else { #endif /* NOCSETS */ while (1) { c = secure_getc(ftprecv.din,0); if (cancelfile #ifdef FTP_TIMEOUT || ftp_timed_out #endif /* FTP_TIMEOUT */ ) { failftprecv2(threadinfo); #ifdef NTSIG ckThreadEnd(threadinfo); #endif /* NTSIG */ return; } if (c < 0 || c == EOF) break; #ifdef UNIX /* Record format conversion for Unix */ /* SKIP THIS FOR WINDOWS! */ if (c == '\n') bare_lfs++; while (c == '\r') { bytes++; if ((c = secure_getc(ftprecv.din,0)) != '\n' || ftprecv.tcrflag) { if (cancelfile) { failftprecv2(threadinfo); #ifdef NTSIG ckThreadEnd(threadinfo); #endif /* NTSIG */ return; } if (c < 0 || c == EOF) goto break2; if (c == '\0') { bytes++; goto contin2; } } } if (c < 0) break; #endif /* UNX */ if (out2screen && !ftprecv.pipename) #ifdef printf printf("%c",(char)c); #else putchar((char)c); #endif /* printf */ else if ((d = zmchout(c)) < 0) break; bytes++; ffc++; contin2: ; } break2: if (bare_lfs && (!dpyactive || ftp_deb)) { printf("WARNING! %d bare linefeeds received in ASCII mode\n", bare_lfs); printf("File might not have transferred correctly.\n"); } if (ftprecv.din == -1) { bytes = (CK_OFF_T)-1; } if (c == -2) bytes = (CK_OFF_T)-1; break; #ifndef NOCSETS } #endif /* NOCSETS */ } if (ftprecv.pipename || !out2screen) { zclose(ZOFILE); /* Close the file */ debug(F111,"doftprecv2 zclose ftpcode",ftprecv.local,ftpcode); if (ftpcode < 0) { /* If download failed */ int x = 0; switch (keep) { /* which is... */ case SET_AUTO: /* AUTO */ if (curtype == FTT_ASC) /* Delete file if TYPE A. */ x = 1; break; case SET_OFF: /* DISCARD */ x = 1; /* Delete file, period. */ break; default: /* KEEP */ break; } if (x) { x = zdelet(ftprecv.local); debug(F111,"ftp get delete incomplete",ftprecv.local,x); } } } signal(SIGINT, ftprecv.oldintr); #ifdef SIGPIPE if (ftprecv.oldintp) signal(SIGPIPE, ftprecv.oldintp); #endif /* SIGPIPE */ stop = gmstimer(); #ifdef GFTIMER fpfsecs = gftimer(); #endif /* GFTIMER */ tfc += ffc; #ifdef TCPIPLIB socket_close(ftprecv.din); #else /* TCPIPLIB */ #ifdef USE_SHUTDOWN shutdown(ftprecv.din, 1+1); #endif /* USE_SHUTDOWN */ close(ftprecv.din); #endif /* TCPIPLIB */ ftprecv.reply = getreply(0,ftprecv.fcs,ftprecv.rcs,ftp_vbm,0); ftprecvret = ((ftpcode < 0 || ftprecv.reply == REPLY_TRANSIENT || ftprecv.reply == REPLY_ERROR) ? -1 : 0); #ifdef NTSIG ckThreadEnd(threadinfo); #endif /* NTSIG */ } static int recvrequest(cmd, local, remote, lmode, printnames, recover, pipename, xlate, fcs, rcs) char *cmd, *local, *remote, *lmode, *pipename; int printnames, recover, xlate, fcs, rcs; { #ifdef NT struct _stat stbuf; #else /* NT */ struct stat stbuf; #endif /* NT */ #ifdef DEBUG if (deblog) { debug(F111,"ftp recvrequest cmd",cmd,recover); debug(F110,"ftp recvrequest local ",local,0); debug(F111,"ftp recvrequest remote",remote,ftp_typ); debug(F110,"ftp recvrequest pipename ",pipename,0); debug(F101,"ftp recvrequest xlate","",xlate); debug(F101,"ftp recvrequest fcs","",fcs); debug(F101,"ftp recvrequest rcs","",rcs); } #endif /* DEBUG */ ftprecv.localsize = (CK_OFF_T)0; if (remfile) { /* See remcfm(), remtxt() */ if (rempipe) { pipename = remdest; } else { local = remdest; if (remappd) lmode = "ab"; } } out2screen = 0; if (!cmd) cmd = ""; /* Core dump prevention */ if (!remote) remote = ""; if (!lmode) lmode = ""; if (pipename) { /* No recovery for pipes. */ recover = 0; if (!local) local = pipename; } else { if (!local) /* Output to screen? */ local = "-"; out2screen = !strcmp(local,"-"); } debug(F101,"ftp recvrequest out2screen","",out2screen); #ifdef OS2 if ( ftp_xla && out2screen && !k95stdout && !inserver ) fcs = FC_UCS2; #endif /* OS2 */ if (out2screen) /* No recovery to screen */ recover = 0; if (!ftp_typ) /* No recovery in text mode */ recover = 0; ftprecv.is_retr = (strcmp(cmd, "RETR") == 0); if (!ftprecv.is_retr) /* No recovery except for RETRieve */ recover = 0; #ifdef COMMENT if (!out2screen && !pipename && ftprecv.is_retr) { /* To real file */ if (recursive && ckstrchr(local,'/')) { } } #endif /* COMMENT */ ftprecv.localsize = (CK_OFF_T)0; /* Local file size */ rs_len = (CK_OFF_T)0; /* Recovery point */ debug(F101,"ftp recvrequest recover","",recover); if (recover) { /* Recovering... */ if (stat(local, &stbuf) < 0) { /* Can't stat local file */ debug(F101,"ftp recvrequest recover stat failed","",errno); recover = 0; /* So cancel recovery */ } else { /* Have local file info */ ftprecv.localsize = stbuf.st_size; /* Get size */ /* Remote file smaller than local */ if (fsize < ftprecv.localsize) { debug(F101,"ftp recvrequest recover remote smaller","",fsize); recover = 0; /* Recovery can't work */ } else if (fsize == ftprecv.localsize) { /* Sizes are equal */ debug(F111,"ftp recvrequest recover equal size", remote,ftprecv.localsize); return(1); } #ifdef COMMENT /* The problem here is that the original partial file never got its date set, either because FTP DATES was OFF, or because the partial file was downloaded by some other program that doesn't set local file dates, or because Kermit only sets the file's date when the download was complete and successful. In all these cases, the local file has a later time than the remote. */ if (recover) { /* Remote is bigger */ x = chkmodtime(local,remote,0); /* Check file dates */ debug(F111,"ftp recvrequest chkmodtime",remote,x); if (x != 1) /* Dates must be equal! */ recover = 0; /* If not, get whole file */ } #endif /* COMMENT */ } debug(F111,"ftp recvrequest recover",remote,recover); } #ifdef FTP_PROXY if (proxy && ftprecv.is_retr) return(proxtrans(cmd, local ? local : remote, remote)); #endif /* FTP_PROXY */ ftprecv.tcrflag = (feol != CR) && ftprecv.is_retr; ftprecv.reply = 0; ftprecv.fcs = fcs; ftprecv.rcs = rcs; ftprecv.recover = recover; ftprecv.xlate = xlate; ftprecv.cmd = cmd; ftprecv.local = local; ftprecv.remote = remote; ftprecv.lmode = lmode; ftprecv.pipename = pipename; ftprecv.oldintp = NULL; ftpcode = 0; havesigint = 0; ftprecv.oldintr = signal(SIGINT, cancelrecv); if (cc_execute(ckjaddr(recvcancel), doftprecv, failftprecv) < 0) return -1; #ifdef FTP_TIMEOUT debug(F111,"ftp recvrequest ftprecvret",remote,ftprecvret); debug(F111,"ftp recvrequest ftp_timed_out",remote,ftp_timed_out); if (ftp_timed_out) ftprecvret = -1; #endif /* FTP_TIMEOUT */ if (ftprecvret < 0) return -1; if (cc_execute(ckjaddr(recvcancel), doftprecv2, failftprecv2) < 0) return -1; return ftprecvret; } /* * Need to start a listen on the data channel before we send the command, * otherwise the server's connect may fail. */ static int initconn() { register char *p, *a; int result, tmpno = 0; int on = 1; GSOCKNAME_T len; #ifndef NO_PASSIVE_MODE int a1,a2,a3,a4,p1,p2; if (passivemode) { data = socket(AF_INET, SOCK_STREAM, 0); globaldin = data; if (data < 0) { perror("ftp: socket"); return(-1); } if (ftpcmd("PASV",NULL,0,0,ftp_vbm) != REPLY_COMPLETE) { printf("Passive mode refused\n"); passivemode = 0; return(initconn()); } /* Now we have a string of comma-separated one-byte unsigned integer values, The first four are the an IP address. The fifth is the MSB of the port number, the sixth is the LSB. From that we can make a sockaddr_in. */ if (sscanf(pasv,"%d,%d,%d,%d,%d,%d",&a1,&a2,&a3,&a4,&p1,&p2) != 6) { printf("Passive mode address scan failure\n"); return(-1); }; #ifndef NOHTTP if (tcp_http_proxy) { #ifdef OS2 char * agent = "Kermit 95"; /* Default user agent */ #else char * agent = "C-Kermit"; #endif /* OS2 */ register struct hostent *hp = 0; struct servent *destsp; char host[512], *p, *q; #ifdef IP_TOS #ifdef IPTOS_THROUGHPUT int tos; #endif /* IPTOS_THROUGHPUT */ #endif /* IP_TOS */ int s; #ifdef DEBUG extern int debtim; int xdebtim; xdebtim = debtim; debtim = 1; #endif /* DEBUG */ ckmakxmsg(proxyhost,HTTPCPYL,ckuitoa(a1),".",ckuitoa(a2), ".",ckuitoa(a3),".",ckuitoa(a4),":",ckuitoa((p1<<8)|p2), NULL,NULL,NULL ); memset((char *)&hisctladdr, 0, sizeof (hisctladdr)); for (p = tcp_http_proxy, q=host; *p != '\0' && *p != ':'; p++, q++) *q = *p; *q = '\0'; hisctladdr.sin_addr.s_addr = inet_addr(host); if (hisctladdr.sin_addr.s_addr != INADDR_NONE) /* 2010-03-29 */ { debug(F110,"initconn A",host,0); hisctladdr.sin_family = AF_INET; } else { debug(F110,"initconn B",host,0); hp = gethostbyname(host); #ifdef HADDRLIST hp = ck_copyhostent(hp); /* make safe copy that won't change */ #endif /* HADDRLIST */ if (hp == NULL) { fprintf(stderr, "ftp: %s: Unknown host\n", host); ftpcode = -1; #ifdef DEBUG debtim = xdebtim; #endif /* DEBUG */ return(0); } hisctladdr.sin_family = hp->h_addrtype; #ifdef HADDRLIST memcpy((char *)&hisctladdr.sin_addr, hp->h_addr_list[0], sizeof(hisctladdr.sin_addr)); #else /* HADDRLIST */ memcpy((char *)&hisctladdr.sin_addr, hp->h_addr, sizeof(hisctladdr.sin_addr)); #endif /* HADDRLIST */ } data = socket(hisctladdr.sin_family, SOCK_STREAM, 0); debug(F101,"initconn socket","",data); if (data < 0) { perror("ftp: socket"); ftpcode = -1; #ifdef DEBUG debtim = xdebtim; #endif /* DEBUG */ return(0); } if (*p == ':') p++; else p = "http"; destsp = getservbyname(p,"tcp"); if (destsp) hisctladdr.sin_port = destsp->s_port; else if (p) hisctladdr.sin_port = htons(atoi(p)); else hisctladdr.sin_port = htons(80); errno = 0; #ifdef HADDRLIST debug(F100,"initconn HADDRLIST","",0); while #else debug(F100,"initconn no HADDRLIST","",0); if #endif /* HADDRLIST */ (connect(data, (struct sockaddr *)&hisctladdr, sizeof (hisctladdr)) < 0) { debug(F101,"initconn connect failed","",errno); #ifdef HADDRLIST if (hp && hp->h_addr_list[1]) { int oerrno = errno; fprintf(stderr, "ftp: connect to address %s: ", inet_ntoa(hisctladdr.sin_addr) ); errno = oerrno; perror((char *)0); hp->h_addr_list++; memcpy((char *)&hisctladdr.sin_addr, hp->h_addr_list[0], sizeof(hisctladdr.sin_addr)); fprintf(stdout, "Trying %s...\n", inet_ntoa(hisctladdr.sin_addr)); #ifdef TCPIPLIB socket_close(data); #else /* TCPIPLIB */ close(data); #endif /* TCPIPLIB */ data = socket(hisctladdr.sin_family, SOCK_STREAM, 0); if (data < 0) { perror("ftp: socket"); ftpcode = -1; #ifdef DEBUG debtim = xdebtim; #endif /* DEBUG */ return(0); } continue; } #endif /* HADDRLIST */ perror("ftp: connect"); ftpcode = -1; goto bad; } if (http_connect(data, tcp_http_proxy_agent ? tcp_http_proxy_agent : agent, NULL, tcp_http_proxy_user, tcp_http_proxy_pwd, 0, proxyhost ) < 0) { #ifdef TCPIPLIB socket_close(data); #else /* TCPIPLIB */ close(data); #endif /* TCPIPLIB */ perror("ftp: connect"); ftpcode = -1; goto bad; } } else #endif /* NOHTTP */ { data_addr.sin_family = AF_INET; data_addr.sin_addr.s_addr = htonl((a1<<24)|(a2<<16)|(a3<<8)|a4); data_addr.sin_port = htons((p1<<8)|p2); if (connect(data, (struct sockaddr *)&data_addr, sizeof(data_addr)) < 0 ) { perror("ftp: connect"); return(-1); } } debug(F100,"initconn connect ok","",0); #ifdef IP_TOS #ifdef IPTOS_THROUGHPUT on = IPTOS_THROUGHPUT; if (setsockopt(data, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0) perror("ftp: setsockopt TOS (ignored)"); #endif /* IPTOS_THROUGHPUT */ #endif /* IP_TOS */ memcpy(&hisdataaddr,&data_addr,sizeof(struct sockaddr_in)); return(0); } #endif /* NO_PASSIVE_MODE */ noport: memcpy(&data_addr,&myctladdr,sizeof(struct sockaddr_in)); if (sendport) data_addr.sin_port = 0; /* let system pick one */ if (data != -1) { #ifdef TCPIPLIB socket_close(data); #else /* TCPIPLIB */ #ifdef USE_SHUTDOWN shutdown(data, 1+1); #endif /* USE_SHUTDOWN */ close(data); #endif /* TCPIPLIB */ } data = socket(AF_INET, SOCK_STREAM, 0); globaldin = data; if (data < 0) { perror("ftp: socket"); if (tmpno) sendport = 1; return(-1); } if (!sendport) { if (setsockopt(data, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof (on) ) < 0 ) { perror("ftp: setsockopt (reuse address)"); goto bad; } } if (bind(data, (struct sockaddr *)&data_addr, sizeof (data_addr)) < 0) { perror("ftp: bind"); goto bad; } len = sizeof (data_addr); if (getsockname(data, (struct sockaddr *)&data_addr, &len) < 0) { perror("ftp: getsockname"); goto bad; } if (listen(data, 1) < 0) { perror("ftp: listen"); goto bad; } if (sendport) { a = (char *)&data_addr.sin_addr; p = (char *)&data_addr.sin_port; ckmakxmsg(ftpcmdbuf,FTP_BUFSIZ,"PORT ", UC(a[0]),",",UC(a[1]),",", UC(a[2]),",", UC(a[3]),",", UC(p[0]),",", UC(p[1])); result = ftpcmd(ftpcmdbuf,NULL,0,0,ftp_vbm); if (result == REPLY_ERROR && sendport) { sendport = 0; tmpno = 1; goto noport; } return(result != REPLY_COMPLETE); } if (tmpno) sendport = 1; #ifdef IP_TOS #ifdef IPTOS_THROUGHPUT on = IPTOS_THROUGHPUT; if (setsockopt(data, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0) perror("ftp: setsockopt TOS (ignored)"); #endif #endif return(0); bad: #ifdef TCPIPLIB socket_close(data); #else /* TCPIPLIB */ #ifdef USE_SHUTDOWN shutdown(data, 1+1); #endif /* USE_SHUTDOWN */ close(data); #endif /* TCPIPLIB */ data = -1; globaldin = data; if (tmpno) sendport = 1; return(-1); } #ifdef CK_SSL static int ssl_dataconn() { if (ssl_ftp_data_con!=NULL) { /* Do SSL */ SSL_free(ssl_ftp_data_con); ssl_ftp_data_con=NULL; } ssl_ftp_data_con=(SSL *)SSL_new(ssl_ftp_ctx); SSL_set_fd(ssl_ftp_data_con,data); SSL_set_verify(ssl_ftp_data_con,ssl_verify_flag,NULL); SSL_copy_session_id(ssl_ftp_data_con,ssl_ftp_con); if (ssl_debug_flag) { fprintf(stderr,"=>START SSL connect on DATA\n"); fflush(stderr); } if (SSL_connect(ssl_ftp_data_con) <= 0) { static char errbuf[1024]; ckmakmsg(errbuf,1024,"ftp: SSL_connect DATA error: ", ERR_error_string(ERR_get_error(),NULL),NULL,NULL); fprintf(stderr,"%s\n", errbuf); fflush(stderr); #ifdef TCPIPLIB socket_close(data); #else /* TCPIPLIB */ #ifdef USE_SHUTDOWN shutdown(data, 1+1); #endif /* USE_SHUTDOWN */ close(data); #endif /* TCPIPLIB */ data = -1; globaldin = data; return(-1); } else { ssl_ftp_data_active_flag=1; if (!ssl_certsok_flag && !tls_is_krb5(2)) { char *subject = ssl_get_subject_name(ssl_ftp_data_con); if (!subject) { if (ssl_verify_flag & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) { debug(F110,"dataconn","[SSL _- FAILED]",0); ssl_ftp_data_active_flag = 0; #ifdef TCPIPLIB socket_close(data); #else /* TCPIPLIB */ #ifdef USE_SHUTDOWN shutdown(data, 1+1); #endif /* USE_SHUTDOWN */ close(data); #endif /* TCPIPLIB */ data = -1; globaldin = data; return(-1); } else { if (!out2screen && displa && fdispla) { ftscreen(SCR_TC,0,(CK_OFF_T)0,"Display canceled"); /* fdispla = XYFD_B; */ } if (uq_ok( "Warning: Server didn't provide a certificate on data connection\n", "Continue with file transfer? (Y/N)", 3,NULL,0) <= 0) { debug(F110, "dataconn","[SSL - FAILED]",0); ssl_ftp_data_active_flag = 0; #ifdef TCPIPLIB socket_close(data); #else /* TCPIPLIB */ #ifdef USE_SHUTDOWN shutdown(data, 1+1); #endif /* USE_SHUTDOWN */ close(data); #endif /* TCPIPLIB */ data = -1; globaldin = data; return(-1); } } } else { if (!out2screen && displa && fdispla == XYFD_C) { ftscreen(SCR_TC,0,(CK_OFF_T)0,"Display canceled"); /* fdispla = XYFD_B; */ } if (ssl_check_server_name(ssl_ftp_data_con,ftp_user_host)) { debug(F110,"dataconn","[SSL - FAILED]",0); ssl_ftp_data_active_flag = 0; #ifdef TCPIPLIB socket_close(data); #else /* TCPIPLIB */ #ifdef USE_SHUTDOWN shutdown(data, 1+1); #endif /* USE_SHUTDOWN */ close(data); #endif /* TCPIPLIB */ data = -1; globaldin = data; return(-1); } } } debug(F110,"dataconn","[SSL - OK]",0); #ifdef COMMENT /* This messes up the full screen file transfer display */ ssl_display_connect_details(ssl_ftp_con,0,ssl_verbose_flag); #endif /* COMMENT */ } if (ssl_debug_flag) { fprintf(stderr,"=>DONE SSL connect on DATA\n"); fflush(stderr); } return(data); } #endif /* CK_SSL */ static int dataconn(lmode) char *lmode; { int s; #ifdef IP_TOS int tos; #endif /* IP_TOS */ #ifdef UCX50 static u_int fromlen; #else static SOCKOPT_T fromlen; #endif /* UCX50 */ fromlen = sizeof(hisdataaddr); #ifndef NO_PASSIVE_MODE if (passivemode) { #ifdef CK_SSL ssl_ftp_data_active_flag=0; if (ssl_ftp_active_flag && (ssl_ftp_proxy || ftp_dpl == FPL_PRV)) return(ssl_dataconn()); #endif /* CK_SSL */ return(data); } #endif /* NO_PASSIVE_MODE */ s = accept(data, (struct sockaddr *) &hisdataaddr, &fromlen); if (s < 0) { perror("ftp: accept"); #ifdef TCPIPLIB socket_close(data); #else /* TCPIPLIB */ #ifdef USE_SHUTDOWN shutdown(data, 1+1); #endif /* USE_SHUTDOWN */ close(data); #endif /* TCPIPLIB */ data = -1; globaldin = data; return(-1); } #ifdef TCPIPLIB socket_close(data); #else /* TCPIPLIB */ #ifdef USE_SHUTDOWN shutdown(data, 1+1); #endif /* USE_SHUTDOWN */ close(data); #endif /* TCPIPLIB */ data = s; globaldin = data; #ifdef IP_TOS #ifdef IPTOS_THROUGHPUT tos = IPTOS_THROUGHPUT; if (setsockopt(data, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0) perror("ftp: setsockopt TOS (ignored)"); #endif /* IPTOS_THROUGHPUT */ #endif /* IP_TOS */ #ifdef CK_SSL ssl_ftp_data_active_flag=0; if (ssl_ftp_active_flag && (ssl_ftp_proxy || ftp_dpl == FPL_PRV)) return(ssl_dataconn()); #endif /* CK_SSL */ return(data); } #ifdef FTP_PROXY static sigtype pscancel(sig) int sig; { cancelfile++; } static VOID pswitch(flag) int flag; { extern int proxy; sig_t oldintr; static struct comvars { int connect; char name[MAXHOSTNAMELEN]; struct sockaddr_in mctl; struct sockaddr_in hctl; FILE *in; FILE *out; int tpe; int curtpe; int cpnd; int sunqe; int runqe; int mcse; int ntflg; char nti[17]; char nto[17]; int mapflg; char mi[CKMAXPATH]; char mo[CKMAXPATH]; char *authtype; int clvl; int dlvl; #ifdef FTP_KRB4 des_cblock session; des_key_schedule ftp_sched; #endif /* FTP_KRB4 */ #ifdef FTP_GSSAPI gss_ctx_id_t gcontext; #endif /* GSSAPI */ } proxstruct, tmpstruct; struct comvars *ip, *op; cancelfile = 0; oldintr = signal(SIGINT, pscancel); if (flag) { if (proxy) return; ip = &tmpstruct; op = &proxstruct; proxy++; } else { if (!proxy) return; ip = &proxstruct; op = &tmpstruct; proxy = 0; } ip->connect = connected; connected = op->connect; if (ftp_host) { strncpy(ip->name, ftp_host, MAXHOSTNAMELEN - 1); ip->name[MAXHOSTNAMELEN - 1] = '\0'; ip->name[strlen(ip->name)] = '\0'; } else ip->name[0] = 0; ftp_host = op->name; ip->hctl = hisctladdr; hisctladdr = op->hctl; ip->mctl = myctladdr; myctladdr = op->mctl; ip->in = csocket; csocket = op->in; ip->out = csocket; csocket = op->out; ip->tpe = ftp_typ; ftp_typ = op->tpe; ip->curtpe = curtype; curtype = op->curtpe; ip->cpnd = cpend; cpend = op->cpnd; ip->sunqe = ftp_usn; ftp_usn = op->sunqe; ip->mcse = mcase; mcase = op->mcse; ip->ntflg = ntflag; ntflag = op->ntflg; strncpy(ip->nti, ntin, 16); (ip->nti)[strlen(ip->nti)] = '\0'; strcpy(ntin, op->nti); strncpy(ip->nto, ntout, 16); (ip->nto)[strlen(ip->nto)] = '\0'; strcpy(ntout, op->nto); ip->mapflg = mapflag; mapflag = op->mapflg; strncpy(ip->mi, mapin, CKMAXPATH - 1); (ip->mi)[strlen(ip->mi)] = '\0'; strcpy(mapin, op->mi); strncpy(ip->mo, mapout, CKMAXPATH - 1); (ip->mo)[strlen(ip->mo)] = '\0'; strcpy(mapout, op->mo); ip->authtype = auth_type; auth_type = op->authtype; ip->clvl = ftp_cpl; ftp_cpl = op->clvl; ip->dlvl = ftp_dpl; ftp_dpl = op->dlvl; if (!ftp_cpl) ftp_cpl = FPL_CLR; if (!ftp_dpl) ftp_dpl = FPL_CLR; #ifdef FTP_KRB4 memcpy(ip->session, ftp_cred.session, sizeof(ftp_cred.session)); memcpy(ftp_cred.session, op->session, sizeof(ftp_cred.session)); memcpy(ip->schedule, ftp_sched, sizeof(ftp_sched)); memcpy(ftp_sched, op->schedule, sizeof(ftp_sched)); #endif /* FTP_KRB4 */ #ifdef FTP_GSSAPI ip->gcontext = gcontext; gcontext = op->gcontext; #endif /* GSSAPI */ signal(SIGINT, oldintr); if (cancelfile) { cancelfile = 0; debug(F101,"pswitch cancelfile B","",cancelfile); (*oldintr)(SIGINT); } } static sigtype cancelpt(sig) int sig; { printf("\n"); fflush(stdout); ptabflg++; cancelfile = 0; #ifndef OS2 longjmp(ptcancel, 1); #else PostCtrlCSem(); #endif /* OS2 */ } void proxtrans(cmd, local, remote, unique) char *cmd, *local, *remote; int unique; { sig_t oldintr; int secndflag = 0, prox_type, nfnd; char *cmd2; #ifdef BSDSELECT fd_set mask; #endif /* BSDSELECT */ sigtype cancelpt(); if (strcmp(cmd, "RETR")) cmd2 = "RETR"; else cmd2 = unique ? "STOU" : "STOR"; if ((prox_type = type) == 0) { if (servertype == SYS_UNIX && unix_proxy) prox_type = FTT_BIN; else prox_type = FTT_ASC; } if (curtype != prox_type) changetype(prox_type, 1); if (ftpcmd("PASV",NULL,0,0,ftp_vbm) != REPLY_COMPLETE) { printf("Proxy server does not support third party transfers.\n"); return; } pswitch(0); if (!connected) { printf("No primary connection\n"); pswitch(1); ftpcode = -1; return; } if (curtype != prox_type) changetype(prox_type, 1); if (ftpcmd("PORT",pasv,-1,-1,ftp_vbm) != REPLY_COMPLETE) { pswitch(1); return; } /* Replace with calls to cc_execute() */ if (setjmp(ptcancel)) goto cancel; oldintr = signal(SIGINT, cancelpt); if (ftpcmd(cmd,remote,-1,-1,ftp_vbm) != PRELIM) { signal(SIGINT, oldintr); pswitch(1); return; } sleep(2000); pswitch(1); secndflag++; if (ftpcmd(cmd2,local,-1,-1,ftp_vbm) != PRELIM) goto cancel; ptflag++; getreply(0,-1,-1,ftp_vbm,0); pswitch(0); getreply(0,-1,-1,ftp_vbm,0); signal(SIGINT, oldintr); pswitch(1); ptflag = 0; return; cancel: signal(SIGINT, SIG_IGN); ptflag = 0; if (strcmp(cmd, "RETR") && !proxy) pswitch(1); else if (!strcmp(cmd, "RETR") && proxy) pswitch(0); if (!cpend && !secndflag) { /* only here if cmd = "STOR" (proxy=1) */ if (ftpcmd(cmd2,local,-1,-1,ftp_vbm) != PRELIM) { pswitch(0); if (cpend) cancel_remote(0); } pswitch(1); if (ptabflg) ftpcode = -1; signal(SIGINT, oldintr); return; } if (cpend) cancel_remote(0); pswitch(!proxy); if (!cpend && !secndflag) { /* only if cmd = "RETR" (proxy=1) */ if (ftpcmd(cmd2,local,-1,-1,ftp_vbm) != PRELIM) { pswitch(0); if (cpend) cancel_remote(0); pswitch(1); if (ptabflg) ftpcode = -1; signal(SIGINT, oldintr); return; } } if (cpend) cancel_remote(0); pswitch(!proxy); if (cpend) { #ifdef BSDSELECT FD_ZERO(&mask); FD_SET(csocket, &mask); if ((nfnd = empty(&mask, 10)) <= 0) { if (nfnd < 0) { perror("cancel"); } if (ptabflg) ftpcode = -1; lostpeer(); } #else /* BSDSELECT */ #ifdef IBMSELECT if ((nfnd = empty(&csocket, 1, 10)) <= 0) { if (nfnd < 0) { perror("cancel"); } if (ptabflg) ftpcode = -1; lostpeer(); } #endif /* IBMSELECT */ #endif /* BSDSELECT */ getreply(0,-1,-1,ftp_vbm,0); getreply(0,-1,-1,ftp_vbm,0); } if (proxy) pswitch(0); pswitch(1); if (ptabflg) ftpcode = -1; signal(SIGINT, oldintr); } #endif /* FTP_PROXY */ #ifdef FTP_SECURITY #ifdef FTP_GSSAPI #ifdef COMMENT /* ck_gss_mech_krb5 is not declared anywhere */ struct { CONST gss_OID_desc * CONST * mech_type; char *service_name; } gss_trials[] = { { &ck_gss_mech_krb5, "ftp" }, { &ck_gss_mech_krb5, "host" }, }; #else /* This matches what is declared above */ struct { CONST gss_OID_desc * CONST * mech_type; char *service_name; } gss_trials[] = { { &gss_mech_krb5, "ftp" }, { &gss_mech_krb5, "host" }, }; #endif /* COMMENT */ int n_gss_trials = sizeof(gss_trials)/sizeof(gss_trials[0]); #endif /* FTP_GSSAPI */ static int ftp_auth() { extern int setsafe(); int j = 0, n; #ifdef FTP_KRB4 char *service, inst[INST_SZ]; ULONG cksum; ULONG checksum = (ULONG) getpid(); CHAR out_buf[FTP_BUFSIZ]; int i; #else /* FTP_KRB4 */ #ifdef FTP_GSSAPI CHAR out_buf[FTP_BUFSIZ]; int i; #endif /* FTP_GSSAPI */ #endif /* FTP_KRB4 */ if (ssl_ftp_proxy) /* Do not allow AUTH over SSL proxy */ return(0); if (auth_type) return(1); /* auth already succeeded */ /* Try each auth type as specified by the end user */ for (j = 0; j < 8 && ftp_auth_type[j] != 0; j++) { #ifdef FTP_GSSAPI if (ftp_auth_type[j] == FTA_GK5 && ck_gssapi_is_installed()) { n = ftpcmd("AUTH GSSAPI",NULL,0,0,ftp_vbm); if (n == REPLY_CONTINUE) { OM_uint32 maj_stat, min_stat; gss_name_t target_name; gss_buffer_desc send_tok, recv_tok, *token_ptr; char stbuf[FTP_BUFSIZ]; int comcode, trial; struct gss_channel_bindings_struct chan; char * realm = NULL; char tgt[256]; chan.initiator_addrtype = GSS_C_AF_INET; /* OM_uint32 */ chan.initiator_address.length = 4; chan.initiator_address.value = &myctladdr.sin_addr.s_addr; chan.acceptor_addrtype = GSS_C_AF_INET; /* OM_uint32 */ chan.acceptor_address.length = 4; chan.acceptor_address.value = &hisctladdr.sin_addr.s_addr; chan.application_data.length = 0; chan.application_data.value = 0; if (!quiet) printf("GSSAPI accepted as authentication type\n"); realm = ck_krb5_realmofhost(ftp_user_host); if (realm) { ckmakmsg(tgt,sizeof(tgt),"krbtgt/",realm,"@",realm); debug(F110,"ftp_auth(GSSAPI) TGT",tgt,0); if ( krb5_autoget && !((ck_krb5_tkt_isvalid(NULL,tgt) > 0) || (ck_krb5_is_tgt_valid() > 0)) ) ck_krb5_autoget_TGT(realm); } /* Blob from gss-client */ for (trial = 0; trial < n_gss_trials; trial++) { /* ftp@hostname first, the host@hostname */ /* the V5 GSSAPI binding canonicalizes this for us... */ ckmakmsg(stbuf,FTP_BUFSIZ, gss_trials[trial].service_name, "@", ftp_user_host, NULL ); if (ftp_deb) fprintf(stderr, "Authenticating to <%s>...\n", stbuf); send_tok.value = stbuf; send_tok.length = strlen(stbuf); maj_stat = gss_import_name(&min_stat, &send_tok, gss_nt_service_name, &target_name ); if (maj_stat != GSS_S_COMPLETE) { user_gss_error(maj_stat, min_stat, "parsing name"); secure_error("name parsed <%s>\n", stbuf); continue; } token_ptr = GSS_C_NO_BUFFER; gcontext = GSS_C_NO_CONTEXT; /* structure copy */ do { if (ftp_deb) fprintf(stderr, "calling gss_init_sec_context\n"); maj_stat = gss_init_sec_context(&min_stat, GSS_C_NO_CREDENTIAL, &gcontext, target_name, (gss_OID) * gss_trials[trial].mech_type, GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | (ftp_cfw ? GSS_C_DELEG_FLAG : 0), 0, /* channel bindings */ (krb5_d_no_addresses ? GSS_C_NO_CHANNEL_BINDINGS : &chan), token_ptr, NULL, /* ignore mech type */ &send_tok, NULL, /* ignore ret_flags */ NULL ); /* ignore time_rec */ if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED) { if (trial == n_gss_trials-1) user_gss_error(maj_stat, min_stat, "initializing context" ); gss_release_name(&min_stat, &target_name); /* maybe we missed on the service name */ goto outer_loop; } if (send_tok.length != 0) { int len; reply_parse = "ADAT="; /* for ftpcmd() later */ len = FTP_BUFSIZ; kerror = radix_encode(send_tok.value, out_buf, send_tok.length, &len, RADIX_ENCODE ); if (kerror) { fprintf(stderr, "Base 64 encoding failed: %s\n", radix_error(kerror) ); goto gss_complete_loop; } comcode = ftpcmd("ADAT",out_buf,-1,-1,0); if (comcode != REPLY_COMPLETE && comcode != REPLY_CONTINUE /* (335) */ ) { if (trial == n_gss_trials-1) { fprintf(stderr, "GSSAPI ADAT failed\n"); /* force out of loop */ maj_stat = GSS_S_FAILURE; } /* Backoff to the v1 gssapi is still possible. Send a new AUTH command. If that fails, terminate the loop. */ if (ftpcmd("AUTH GSSAPI",NULL,0,0,ftp_vbm) != REPLY_CONTINUE) { fprintf(stderr, "GSSAPI ADAT failed, AUTH restart failed\n"); /* force out of loop */ maj_stat = GSS_S_FAILURE; } goto outer_loop; } if (!reply_parse) { fprintf(stderr, "No authentication data received from server\n"); if (maj_stat == GSS_S_COMPLETE) { fprintf(stderr, "...but no more was needed\n"); goto gss_complete_loop; } else { user_gss_error(maj_stat, min_stat, "no reply, huh?" ); goto gss_complete_loop; } } len = FTP_BUFSIZ; kerror = radix_encode(reply_parse,out_buf,i,&len, RADIX_DECODE); if (kerror) { fprintf(stderr, "Base 64 decoding failed: %s\n", radix_error(kerror)); goto gss_complete_loop; } /* everything worked */ token_ptr = &recv_tok; recv_tok.value = out_buf; recv_tok.length = len; continue; /* get out of loop clean */ gss_complete_loop: trial = n_gss_trials-1; gss_release_buffer(&min_stat, &send_tok); gss_release_name(&min_stat, &target_name); goto outer_loop; } } while (maj_stat == GSS_S_CONTINUE_NEEDED); outer_loop: if (maj_stat == GSS_S_COMPLETE) break; } if (maj_stat == GSS_S_COMPLETE) { printf("GSSAPI authentication succeeded\n"); reply_parse = NULL; auth_type = "GSSAPI"; return(1); } else { fprintf(stderr, "GSSAPI authentication failed\n"); reply_parse = NULL; } } else { if (ftp_deb) fprintf(stderr, "GSSAPI rejected as an authentication type\n"); if (ftpcode == 500 || ftpcode == 502) return(0); } } #endif /* FTP_GSSAPI */ #ifdef FTP_SRP if (ftp_auth_type[j] == FTA_SRP && ck_srp_is_installed()) { if (srp_ftp_auth(ftp_user_host,NULL,NULL)) return(1); else if (ftpcode == 500 || ftpcode == 502) return(0); } #endif /* FTP_SRP */ #ifdef FTP_KRB4 if (ftp_auth_type[j] == FTA_K4 && ck_krb4_is_installed()) { n = ftpcmd("AUTH KERBEROS_V4",NULL,0,0,ftp_vbm); if (n == REPLY_CONTINUE) { char tgt[4*REALM_SZ+1]; int rc; if (!quiet) printf("KERBEROS_V4 accepted as authentication type\n"); ckstrncpy(inst, (char *) krb_get_phost(ftp_user_host),INST_SZ); ckstrncpy(ftp_realm, (char *)ck_krb4_realmofhost(ftp_user_host), REALM_SZ ); ckmakmsg(tgt,sizeof(tgt),"krbtgt.",ftp_realm,"@",ftp_realm); rc = ck_krb4_tkt_isvalid(tgt); if (rc <= 0 && krb4_autoget) ck_krb4_autoget_TGT(ftp_realm); service = "ftp"; kerror = krb_mk_req(&ftp_tkt,service,inst,ftp_realm,checksum); if (kerror == KDC_PR_UNKNOWN) { service = "rcmd"; kerror = krb_mk_req(&ftp_tkt, service, inst, ftp_realm, checksum ); } if (kerror) fprintf(stderr, "Kerberos V4 krb_mk_req failed: %s\n", krb_get_err_text(kerror)); if (!kerror) { kerror = krb_get_cred(service, inst, ftp_realm,&ftp_cred); if (kerror) fprintf(stderr, "Kerberos V4 krb_get_cred failed: %s\n", krb_get_err_text(kerror)); } if (!kerror) { int rc; rc = des_key_sched(ftp_cred.session, ftp_sched); if (rc == -1) { printf("?Invalid DES key specified in credentials\r\n"); debug(F110,"ftp_auth", "invalid DES Key specified in credentials",0); } else if ( rc == -2 ) { printf("?Weak DES key specified in credentials\r\n"); debug(F110,"ftp_auth", "weak DES Key specified in credentials",0); } else if ( rc != 0 ) { printf("?DES Key Schedule not set by credentials\r\n"); debug(F110,"ftp_auth", "DES Key Schedule not set by credentials",0); } reply_parse = "ADAT="; i = FTP_BUFSIZ; kerror = radix_encode(ftp_tkt.dat, out_buf, ftp_tkt.length, &i, RADIX_ENCODE); if (kerror) { fprintf(stderr, "Base 64 encoding failed: %s\n", radix_error(kerror)); goto krb4_err; } if (i > FTP_BUFSIZ - 6) printf("?ADAT data too long\n"); if (ftpcmd("ADAT",out_buf,-1,-1,0) != REPLY_COMPLETE) { fprintf(stderr, "Kerberos V4 authentication failed\n"); goto krb4_err; } if (!reply_parse) { fprintf(stderr, "No authentication data received from server\n"); goto krb4_err; } i = sizeof(out_buf); kerror = radix_encode(reply_parse, out_buf, 0, &i, RADIX_DECODE); if (kerror) { fprintf(stderr, "Base 64 decoding failed: %s\n", radix_error(kerror)); goto krb4_err; } kerror = krb_rd_safe(out_buf, i, #ifdef KRB524 ftp_cred.session, #else /* KRB524 */ &ftp_cred.session, #endif /* KRB524 */ &hisctladdr, &myctladdr, &ftp_msg_data ); if (kerror) { fprintf(stderr, "Kerberos V4 krb_rd_safe failed: %s\n", krb_get_err_text(kerror)); goto krb4_err; } /* fetch the (modified) checksum */ memcpy(&cksum, ftp_msg_data.app_data, sizeof(cksum)); if (ntohl(cksum) == checksum + 1) { if (ftp_vbm) printf("Kerberos V4 authentication succeeded\n"); reply_parse = NULL; auth_type = "KERBEROS_V4"; return(1); } else fprintf(stderr, "Kerberos V4 mutual authentication failed\n"); krb4_err: reply_parse = NULL; } } else { if (ftp_deb) fprintf(stderr, "KERBEROS_V4 rejected as an authentication type\n"); if (ftpcode == 500 || ftpcode == 502) return(0); } } #endif /* FTP_KRB4 */ #ifdef CK_SSL if (ftp_auth_type[j] == FTA_TLS && ck_ssleay_is_installed()) { #ifdef FTPHOST if (!hostcmd) { ftpcmd("HOST",ftp_user_host,0,0,0); hostcmd = 1; } #endif /* FTPHOST */ n = ftpcmd("AUTH TLS",NULL,0,0,ftp_vbm); if (n != REPLY_COMPLETE) n = ftpcmd("AUTH TLS-P",NULL,0,0,ftp_vbm); if (n == REPLY_COMPLETE) { if (!quiet) printf("TLS accepted as authentication type\n"); auth_type = "TLS"; ssl_auth(); if (ssl_ftp_active_flag ) { ftp_dpl = FPL_CLR; ftp_cpl = FPL_PRV; return(1); } else { fprintf(stderr,"TLS authentication failed\n"); auth_type = NULL; #ifdef TCPIPLIB socket_close(csocket); #else /* TCPIPLIB */ #ifdef USE_SHUTDOWN shutdown(csocket, 1+1); #endif /* USE_SHUTDOWN */ close(csocket); #endif /* TCPIPLIB */ csocket = -1; if (ftp_hookup(ftp_user_host,ftp_port,0) == NULL) return(0); } } else { if (ftp_deb) fprintf(stderr,"TLS rejected as an authentication type\n"); if (ftpcode == 500 || ftpcode == 502) return(0); } } if (ftp_auth_type[j] == FTA_SSL && ck_ssleay_is_installed()) { #ifdef FTPHOST if (!hostcmd) { ftpcmd("HOST",ftp_user_host,0,0,0); hostcmd = 1; } #endif /* FTPHOST */ n = ftpcmd("AUTH SSL",NULL,0,0,ftp_vbm); if (n == REPLY_CONTINUE || n == REPLY_COMPLETE) { if (!quiet) printf("SSL accepted as authentication type\n"); auth_type = "SSL"; ssl_auth(); if (ssl_ftp_active_flag) { ftp_dpl = FPL_PRV; ftp_cpl = FPL_PRV; setprotbuf(1<<20); return(1); } else { fprintf(stderr,"SSL authentication failed\n"); auth_type = NULL; #ifdef TCPIPLIB socket_close(csocket); #else /* TCPIPLIB */ #ifdef USE_SHUTDOWN shutdown(csocket, 1+1); #endif /* USE_SHUTDOWN */ close(csocket); #endif /* TCPIPLIB */ csocket = -1; if (ftp_hookup(ftp_user_host,ftp_port,0) == NULL) return(0); } } else { if (ftp_deb) fprintf(stderr, "SSL rejected as an authentication type\n"); if (ftpcode == 500 || ftpcode == 502) return(0); } } #endif /* CK_SSL */ /* Other auth types go here ... */ } /* for (j;;) */ return(0); } #endif /* FTP_SECURITY */ static int #ifdef CK_ANSIC setprotbuf(unsigned int size) #else setprotbuf(size) unsigned int size; #endif /* CK_ANSIC */ /* setprotbuf */ { if (ucbuf) free(ucbuf); ucbuf = NULL; ucbufsiz = 0; actualbuf = size; while ((ucbuf = (CHAR *)malloc(actualbuf)) == NULL) { if (actualbuf) actualbuf /= 2; else return(0); } ucbufsiz = actualbuf - FUDGE_FACTOR; debug(F101,"setprotbuf ucbufsiz","",ucbufsiz); if (ucbufsiz < 128) { printf("WARNING: tiny ucbufsiz: %d\n",ucbufsiz); } else if (ucbufsiz < 0) { printf("ERROR: ucbuf allocation failure\n"); return(-1); } maxbuf = actualbuf; return(1); } static int #ifdef CK_ANSIC setpbsz(unsigned int size) #else setpbsz(size) unsigned int size; #endif /* CK_ANSIC */ /* setpbsz */ { if (!setprotbuf(size)) { perror("?Error while trying to malloc PROT buffer:"); #ifdef FTP_SRP srp_reset(); #endif /* FTP_SRP */ ftpclose(); return(-1); } reply_parse = "PBSZ="; ckmakmsg(ftpcmdbuf,FTP_BUFSIZ,"PBSZ ", #ifdef CK_SSL ssl_ftp_active_flag ? "0" : #endif /* CK_SSL */ ckuitoa(actualbuf),NULL,NULL); if (ftpcmd(ftpcmdbuf,NULL,0,0,0) != REPLY_COMPLETE) { if (connected) { printf("?Unable to negotiate PROT buffer size with FTP server\n"); ftpclose(); } return(-1); } if (reply_parse) { if ((maxbuf = (unsigned int) atol(reply_parse)) > actualbuf) maxbuf = actualbuf; } else maxbuf = actualbuf; ucbufsiz = maxbuf - FUDGE_FACTOR; debug(F101,"setpbsz ucbufsiz","",ucbufsiz); reply_parse = NULL; return(0); } static VOID cancel_remote(din) int din; { CHAR buf[FTP_BUFSIZ]; int x, nfnd; #ifdef BSDSELECT fd_set mask; #endif /* BSDSELECT */ #ifdef IBMSELECT int fds[2], fdcnt = 0; #endif /* IBMSELECT */ #ifdef DEBUG extern int debtim; int xdebtim; xdebtim = debtim; debtim = 1; #endif /* DEBUG */ debug(F100,"ftp cancel_remote entry","",0); #ifdef CK_SSL if (ssl_ftp_active_flag) { /* * Send Telnet IP, Telnet DM but do so inline and within the * TLS channel */ int count, error; buf[0] = IAC; buf[1] = TN_IP; buf[2] = IAC; buf[3] = TN_DM; buf[4] = NUL; count = SSL_write(ssl_ftp_con, buf, 4); debug(F111,"ftp cancel_remote","SSL_write(IAC IP IAC DM)",count); error = SSL_get_error(ssl_ftp_con,count); debug(F111,"ftp cancel_remote","SSL_get_error()",error); switch (error) { case SSL_ERROR_NONE: break; case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_READ: case SSL_ERROR_SYSCALL: #ifdef NT { int gle = GetLastError(); } #endif /* NT */ case SSL_ERROR_WANT_X509_LOOKUP: case SSL_ERROR_SSL: case SSL_ERROR_ZERO_RETURN: default: lostpeer(); return; } } else #endif /* CK_SSL */ { /* * send IAC in urgent mode instead of DM because 4.3BSD places oob mark * after urgent byte rather than before as is protocol now. */ buf[0] = IAC; buf[1] = TN_IP; buf[2] = IAC; buf[3] = NUL; if ((x = send(csocket, (SENDARG2TYPE)buf, 3, MSG_OOB)) != 3) perror("cancel"); debug(F101,"ftp cancel_remote send 1","",x); buf[0] = TN_DM; x = send(csocket,(SENDARG2TYPE)buf,1,0); debug(F101,"ftp cancel_remote send 2","",x); } x = scommand("ABOR"); debug(F101,"ftp cancel_remote scommand","",x); #ifdef BSDSELECT FD_ZERO(&mask); FD_SET(csocket, &mask); if (din) { FD_SET(din, &mask); } nfnd = empty(&mask, 10); debug(F101,"ftp cancel_remote empty","",nfnd); if ((nfnd) <= 0) { if (nfnd < 0) { perror("cancel"); } #ifdef FTP_PROXY if (ptabflg) ftpcode = -1; #endif /* FTP_PROXY */ lostpeer(); } debug(F110,"ftp cancel_remote","D",0); if (din && FD_ISSET(din, &mask)) { /* Security: No threat associated with this read. */ /* But you can't simply read the TLS data stream */ #ifdef CK_SSL if (ssl_ftp_data_active_flag) { int count, error; while ((count = SSL_read(ssl_ftp_data_con, buf, FTP_BUFSIZ)) > 0) /* LOOP */ ; } else #endif /* CK_SSL */ { while (recv(din, (SENDARG2TYPE)buf, FTP_BUFSIZ,0) > 0) /* LOOP */ ; } } debug(F110,"ftp cancel_remote","E",0); #else /* BSDSELECT */ #ifdef IBMSELECT fds[0] = csocket; fdcnt++; if (din) { fds[1] = din; fdcnt++; } nfnd = empty(fds, fdcnt, 10); debug(F101,"ftp cancel_remote empty","",nfnd); if ((nfnd) <= 0) { if (nfnd < 0) { perror("cancel"); } #ifdef FTP_PROXY if (ptabflg) ftpcode = -1; #endif /* FTP_PROXY */ lostpeer(); } debug(F110,"ftp cancel_remote","D",0); if (din && select(&din, 1,0,0,1) ) { #ifdef CK_SSL if (ssl_ftp_data_active_flag) { int count, error; while ((count = SSL_read(ssl_ftp_data_con, buf, FTP_BUFSIZ)) > 0) /* LOOP */ ; } else #endif /* CK_SSL */ { while (recv(din, (SENDARG2TYPE)buf, FTP_BUFSIZ,0) > 0) /* LOOP */ ; } } debug(F110,"ftp cancel_remote","E",0); #else /* IBMSELECT */ Some form of select is required. #endif /* IBMSELECT */ #endif /* BSDSELECT */ if (getreply(0,-1,-1,ftp_vbm,0) == REPLY_ERROR && ftpcode == 552) { debug(F110,"ftp cancel_remote","F",0); /* 552 needed for NIC style cancel */ getreply(0,-1,-1,ftp_vbm,0); debug(F110,"ftp cancel_remote","G",0); } debug(F110,"ftp cancel_remote","H",0); getreply(0,-1,-1,ftp_vbm,0); debug(F110,"ftp cancel_remote","I",0); #ifdef DEBUG debtim = xdebtim; #endif /* DEBUG */ } static int fts_dpl(x) int x; { if (!auth_type #ifdef OS2 || !ck_crypt_is_installed() #endif /* OS2 */ ) { switch ( x ) { case FPL_PRV: printf("?Cannot set protection level to PRIVATE\n"); return(0); case FPL_SAF: printf("?Cannot set protection level to SAFE\n"); return(0); } ftp_dpl = x; return(1); } #ifdef CK_SSL if (x == FPL_SAF && (!strcmp(auth_type,"SSL") || !strcmp(auth_type,"TLS"))) { printf("Cannot set protection level to safe\n"); return(0); } #endif /* CK_SSL */ /* Start with a PBSZ of 1 meg */ if (x != FPL_CLR) { if (setpbsz(DEFAULT_PBSZ) < 0) return(0); } y = ftpcmd(x == FPL_CLR ? "PROT C" : (x == FPL_SAF ? "PROT S" : "PROT P"), NULL, 0, 0,ftp_vbm); if (y == REPLY_COMPLETE) { ftp_dpl = x; return(1); } return(0); } static int fts_cpl(x) int x; { if (!auth_type #ifdef OS2 || !ck_crypt_is_installed() #endif /* OS2 */ ) { switch ( x ) { case FPL_PRV: printf("?Cannot set protection level to PRIVATE\n"); return(0); case FPL_SAF: printf("?Cannot set protection level to SAFE\n"); return(0); } ftp_cpl = x; return(1); } if (x == FPL_CLR) { y = ftpcmd("CCC",NULL,0,0,ftp_vbm); if (y == REPLY_COMPLETE) { ftp_cpl = x; return(1); } return(0); } ftp_cpl = x; return(1); } #ifdef FTP_GSSAPI static VOID user_gss_error(maj_stat, min_stat, s) OM_uint32 maj_stat, min_stat; char *s; { /* a lot of work just to report the error */ OM_uint32 gmaj_stat, gmin_stat, msg_ctx; gss_buffer_desc msg; msg_ctx = 0; while (!msg_ctx) { gmaj_stat = gss_display_status(&gmin_stat, maj_stat, GSS_C_GSS_CODE, GSS_C_NULL_OID, &msg_ctx, &msg ); if ((gmaj_stat == GSS_S_COMPLETE)|| (gmaj_stat == GSS_S_CONTINUE_NEEDED)) { fprintf(stderr, "GSSAPI error major: %s\n", (char*)msg.value); gss_release_buffer(&gmin_stat, &msg); } if (gmaj_stat != GSS_S_CONTINUE_NEEDED) break; } msg_ctx = 0; while (!msg_ctx) { gmaj_stat = gss_display_status(&gmin_stat, min_stat, GSS_C_MECH_CODE, GSS_C_NULL_OID, &msg_ctx, &msg ); if ((gmaj_stat == GSS_S_COMPLETE)|| (gmaj_stat == GSS_S_CONTINUE_NEEDED)) { fprintf(stderr, "GSSAPI error minor: %s\n", (char*)msg.value); gss_release_buffer(&gmin_stat, &msg); } if (gmaj_stat != GSS_S_CONTINUE_NEEDED) break; } fprintf(stderr, "GSSAPI error: %s\n", s); } #endif /* FTP_GSSAPI */ #ifndef NOMHHOST #ifdef datageneral #define NOMHHOST #else #ifdef HPUX5WINTCP #define NOMHHOST #endif /* HPUX5WINTCP */ #endif /* datageneral */ #endif /* NOMHHOST */ #ifdef INADDRX static struct in_addr inaddrx; #endif /* INADDRX */ static char * ftp_hookup(host, port, tls) char * host; int port; int tls; { register struct hostent *hp = 0; #ifdef IP_TOS #ifdef IPTOS_THROUGHPUT int tos; #endif /* IPTOS_THROUGHPUT */ #endif /* IP_TOS */ int s; GSOCKNAME_T len; static char hostnamebuf[MAXHOSTNAMELEN]; char hostname[MAXHOSTNAMELEN] /* , *p, *q */ ; int cport; #ifdef DEBUG extern int debtim; int xdebtim; xdebtim = debtim; debtim = 1; #endif /* DEBUG */ debug(F111,"ftp_hookup",host,port); #ifndef NOHTTP if (tcp_http_proxy) { struct servent *destsp; char *p, *q; ckmakmsg(proxyhost,HTTPCPYL,host,":",ckuitoa(port),NULL); for (p = tcp_http_proxy, q = hostname; *p != '\0' && *p != ':'; p++, q++ ) *q = *p; *q = '\0'; if (*p == ':') p++; else p = "http"; destsp = getservbyname(p,"tcp"); if (destsp) cport = ntohs(destsp->s_port); else if (p) { cport = atoi(p); } else cport = 80; } else #endif /* NOHTTP */ { ckstrncpy(hostname,host,MAXHOSTNAMELEN); cport = port; } memset((char *)&hisctladdr, 0, sizeof (hisctladdr)); hisctladdr.sin_addr.s_addr = inet_addr(host); if (hisctladdr.sin_addr.s_addr != INADDR_NONE) /* 2010-03-29 */ { debug(F110,"ftp hookup A",hostname,0); hisctladdr.sin_family = AF_INET; ckstrncpy(hostnamebuf, hostname, MAXHOSTNAMELEN); } else { debug(F110,"ftp hookup B",hostname,0); hp = gethostbyname(hostname); #ifdef HADDRLIST hp = ck_copyhostent(hp); /* make safe copy that won't change */ #endif /* HADDRLIST */ if (hp == NULL) { fprintf(stderr, "ftp: %s: Unknown host\n", host); ftpcode = -1; #ifdef DEBUG debtim = xdebtim; #endif /* DEBUG */ return((char *) 0); } hisctladdr.sin_family = hp->h_addrtype; #ifdef HADDRLIST memcpy((char *)&hisctladdr.sin_addr, hp->h_addr_list[0], sizeof(hisctladdr.sin_addr)); #else /* HADDRLIST */ memcpy((char *)&hisctladdr.sin_addr, hp->h_addr, sizeof(hisctladdr.sin_addr)); #endif /* HADDRLIST */ ckstrncpy(hostnamebuf, hp->h_name, MAXHOSTNAMELEN); } debug(F110,"ftp hookup C",hostnamebuf,0); ftp_host = hostnamebuf; s = socket(hisctladdr.sin_family, SOCK_STREAM, 0); debug(F101,"ftp hookup socket","",s); if (s < 0) { perror("ftp: socket"); ftpcode = -1; #ifdef DEBUG debtim = xdebtim; #endif /* DEBUG */ return(0); } hisctladdr.sin_port = htons(cport); errno = 0; #ifdef COMMENT printf("hisctladdr=%d\n",sizeof(hisctladdr)); printf("hisctladdr.sin_addr=%d\n",sizeof(hisctladdr.sin_addr)); printf("sockaddr_in=%d\n",sizeof(struct sockaddr_in)); printf("hisctladdr.sin_addr.s_addr=%d\n",sizeof(hisctladdr.sin_addr.s_addr)); #endif /* COMMENT */ #ifdef HADDRLIST debug(F100,"ftp hookup HADDRLIST","",0); while #else debug(F100,"ftp hookup no HADDRLIST","",0); if #endif /* HADDRLIST */ (connect(s, (struct sockaddr *)&hisctladdr, sizeof (hisctladdr)) < 0) { debug(F101,"ftp hookup connect failed","",errno); #ifdef HADDRLIST if (hp && hp->h_addr_list[1]) { int oerrno = errno; fprintf(stderr, "ftp: connect to address %s: ", inet_ntoa(hisctladdr.sin_addr)); errno = oerrno; perror((char *) 0); hp->h_addr_list++; memcpy((char *)&hisctladdr.sin_addr, hp->h_addr_list[0], sizeof(hisctladdr.sin_addr)); fprintf(stdout, "Trying %s...\n", inet_ntoa(hisctladdr.sin_addr)); #ifdef TCPIPLIB socket_close(s); #else /* TCPIPLIB */ close(s); #endif /* TCPIPLIB */ s = socket(hisctladdr.sin_family, SOCK_STREAM, 0); if (s < 0) { perror("ftp: socket"); ftpcode = -1; #ifdef DEBUG debtim = xdebtim; #endif /* DEBUG */ return(0); } continue; } #endif /* HADDRLIST */ perror("ftp: connect"); ftpcode = -1; goto bad; } debug(F100,"ftp hookup connect ok","",0); len = sizeof (myctladdr); errno = 0; if (getsockname(s, (struct sockaddr *)&myctladdr, &len) < 0) { debug(F101,"ftp hookup getsockname failed","",errno); perror("ftp: getsockname"); ftpcode = -1; goto bad; } debug(F100,"ftp hookup getsockname ok","",0); #ifndef NOHTTP if (tcp_http_proxy) { #ifdef OS2 char * agent = "Kermit 95"; /* Default user agent */ #else char * agent = "C-Kermit"; #endif /* OS2 */ if (http_connect(s,agent,NULL, tcp_http_proxy_user, tcp_http_proxy_pwd, 0, proxyhost ) < 0) { char * foo = NULL; #ifdef TCPIPLIB socket_close(s); #else /* TCPIPLIB */ close(s); #endif /* TCPIPLIB */ while (foo == NULL && tcp_http_proxy != NULL ) { if (tcp_http_proxy_errno == 401 || tcp_http_proxy_errno == 407 ) { char uid[UIDBUFLEN]; char pwd[PWDSIZ]; struct txtbox tb[2]; int ok; tb[0].t_buf = uid; tb[0].t_len = UIDBUFLEN; tb[0].t_lbl = "Proxy Userid: "; tb[0].t_dflt = NULL; tb[0].t_echo = 1; tb[1].t_buf = pwd; tb[1].t_len = 256; tb[1].t_lbl = "Proxy Passphrase: "; tb[1].t_dflt = NULL; tb[1].t_echo = 2; ok = uq_mtxt("Proxy Server Authentication Required\n", NULL, 2, tb); if (ok && uid[0]) { char * proxy_user, * proxy_pwd; proxy_user = tcp_http_proxy_user; proxy_pwd = tcp_http_proxy_pwd; tcp_http_proxy_user = uid; tcp_http_proxy_pwd = pwd; foo = ftp_hookup(host, port, 0); debug(F110,"ftp_hookup()",foo,0); memset(pwd,0,PWDSIZ); tcp_http_proxy_user = proxy_user; tcp_http_proxy_pwd = proxy_pwd; } else break; } else break; } if (foo != NULL) return(foo); perror("ftp: connect"); ftpcode = -1; goto bad; } ckstrncpy(hostnamebuf, proxyhost, MAXHOSTNAMELEN); } #endif /* NOHTTP */ csocket = s; #ifdef CK_SSL if (tls) { /* FTP over SSL * If the connection is over an SSL proxy then the * auth_type will be NULL. However, I'm not sure * whether we should protect the data channel in * that case or not. */ debug(F100,"ftp hookup use_tls","",0); if (!ssl_auth()) { debug(F100,"ftp hookup ssl_auth failed","",0); auth_type = NULL; ftpcode = -1; csocket = -1; goto bad; } ssl_ftp_proxy = 1; } #endif /* CK_SSL */ #ifdef IP_TOS #ifdef IPTOS_LOWDELAY tos = IPTOS_LOWDELAY; if (setsockopt(csocket, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0) perror("ftp: setsockopt TOS (ignored)"); #endif #endif if (!quiet) printf("Connected to %s.\n", host); /* Read greeting from server */ if (getreply(0,ftp_csl,ftp_csr,ftp_vbm,0) > 2) { debug(F100,"ftp hookup bad reply","",0); #ifdef TCPIPLIB socket_close(csocket); #else /* TCPIPLIB */ close(csocket); #endif /* TCPIPLIB */ ftpcode = -1; goto bad; } #ifdef SO_OOBINLINE { int on = 1; errno = 0; if (setsockopt(s, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0) { perror("ftp: setsockopt"); debug(F101,"ftp hookup setsockopt failed","",errno); } #ifdef DEBUG else debug(F100,"ftp hookup setsockopt ok","",0); #endif /* DEBUG */ } #endif /* SO_OOBINLINE */ #ifdef DEBUG debtim = xdebtim; #endif /* DEBUG */ return(ftp_host); bad: debug(F100,"ftp hookup bad","",0); #ifdef TCPIPLIB socket_close(s); #else /* TCPIPLIB */ close(s); #endif /* TCPIPLIB */ #ifdef DEBUG debtim = xdebtim; #endif /* DEBUG */ csocket = -1; return((char *)0); } static VOID ftp_init() { int i, n; /* The purpose of the initial REST 0 is not clear, but other FTP */ /* clients do it. In any case, failure of this command is not a */ /* reliable indication that the server does not support Restart. */ okrestart = 0; if (!noinit) { n = ftpcmd("REST 0",NULL,0,0,0); if (n == REPLY_COMPLETE) okrestart = 1; #ifdef COMMENT else if (ftp_deb) printf("WARNING: Unable to restore file pointer.\n"); #endif /* COMMENT */ } n = ftpcmd("SYST",NULL,0,0,0); /* Get server system type */ if (n == REPLY_COMPLETE) { register char *cp, c = NUL; cp = ckstrchr(ftp_reply_str+4,' '); /* Get first word of reply */ if (cp == NULL) cp = ckstrchr(ftp_reply_str+4,'\r'); if (cp) { if (cp[-1] == '.') cp--; c = *cp; /* Save this char */ *cp = '\0'; /* Replace it with NUL */ } if (!quiet) printf("Remote system type is %s.\n",ftp_reply_str+4); ckstrncpy(ftp_srvtyp,ftp_reply_str+4,SRVNAMLEN); if (cp) /* Put back saved char */ *cp = c; } alike = !ckstrcmp(ftp_srvtyp,myostype,-1,0); if (!ckstrcmp(ftp_srvtyp,"UNIX",-1,0)) servertype = SYS_UNIX; else if (!ckstrcmp(ftp_srvtyp,"WIN32",-1,0)) servertype = SYS_WIN32; else if (!ckstrcmp(ftp_srvtyp,"OS/2",-1,0)) servertype = SYS_WIN32; else if (!ckstrcmp(ftp_srvtyp,"VMS",-1,0)) servertype = SYS_VMS; else if (!ckstrcmp(ftp_srvtyp,"DOS",-1,0)) servertype = SYS_DOS; else if (!ckstrcmp(ftp_srvtyp,"TOPS20",-1,0)) servertype = SYS_TOPS20; else if (!ckstrcmp(ftp_srvtyp,"TOPS10",-1,0)) servertype = SYS_TOPS10; #ifdef FTP_PROXY unix_proxy = 0; if (servertype == SYS_UNIX && proxy) unix_proxy = 1; #endif /* FTP_PROXY */ if (ftp_cmdlin && ftp_xfermode == XMODE_M) ftp_typ = binary; /* Type given on command line */ else /* Otherwise set it automatically */ ftp_typ = alike ? FTT_BIN : FTT_ASC; changetype(ftp_typ,0); /* Change to this type */ g_ftp_typ = ftp_typ; /* Make it the global type */ if (!quiet) printf("Default transfer mode is %s\n", ftp_typ ? "BINARY" : "TEXT (\"ASCII\")" ); for (i = 0; i < 16; i++) /* Init server FEATure table */ sfttab[i] = 0; if (!noinit) { n = ftpcmd("MODE S",NULL,0,0,0); /* We always send in Stream mode */ #ifdef COMMENT if (n != REPLY_COMPLETE) printf("WARNING: Server does not accept MODE S(TREAM)\n"); #endif /* COMMENT */ n = ftpcmd("STRU F",NULL,0,0,0); /* STRU File (not Record or Page) */ #ifdef COMMENT if (n != REPLY_COMPLETE) printf("WARNING: Server does not accept STRU F(ILE)\n"); #endif /* COMMENT */ if (featok) { n = ftpcmd("FEAT",NULL,0,0,0); /* Ask server about features */ if (n == REPLY_COMPLETE) { debug(F101,"ftp_init FEAT","",sfttab[0]); if (deblog || ftp_deb) { int i; for (i = 1; i < 16 && i < nfeattab; i++) { debug(F111,"ftp_init FEAT",feattab[i].kwd,sfttab[i]); if (ftp_deb) printf(" Server %s %s\n", sfttab[i] ? "supports" : "does not support", feattab[i].kwd ); } /* Deal with disabled MLST opts here if necessary */ /* But why would it be? */ } } } } } static int ftp_login(host) char * host; { /* (also called from ckuusy.c) */ static char ftppass[PASSBUFSIZ]=""; char tmp[PASSBUFSIZ]; char *user = NULL, *pass = NULL, *acct = NULL; int n, aflag = 0; extern char uidbuf[]; extern char pwbuf[]; extern int pwflg, pwcrypt; debug(F111,"ftp_login",ftp_logname,ftp_log); if (!ckstrcmp(ftp_logname,"anonymous",-1,0)) anonymous = 1; if (!ckstrcmp(ftp_logname,"ftp",-1,0)) anonymous = 1; #ifdef FTP_SRP if (auth_type && !strcmp(auth_type, "SRP")) { user = srp_user; pass = srp_pass; acct = srp_acct; } else #endif /* FTP_SRP */ if (anonymous) { user = "anonymous"; if (ftp_tmp) { /* They gave a password */ pass = ftp_tmp; } else if (ftp_apw) { /* SET FTP ANONYMOUS-PASSWORD */ pass = ftp_apw; } else { /* Supply user@host */ ckmakmsg(tmp,PASSBUFSIZ,whoami(),"@",myhost,NULL); pass = tmp; } debug(F110,"ftp anonymous",pass,0); } else { #ifdef USE_RUSERPASS if (ruserpass(host, &user, &pass, &acct) < 0) { ftpcode = -1; return(0); } #endif /* USE_RUSERPASS */ if (ftp_logname) { user = ftp_logname; pass = ftp_tmp; } else if (uidbuf[0] && (ftp_tmp || pwbuf[0] && pwflg)) { user = uidbuf; if (ftp_tmp) { pass = ftp_tmp; } else if (pwbuf[0] && pwflg) { ckstrncpy(ftppass,pwbuf,PASSBUFSIZ); #ifdef OS2 if ( pwcrypt ) ck_encrypt((char *)ftppass); #endif /* OS2 */ pass = ftppass; } } acct = ftp_acc; while (user == NULL) { char *myname, prompt[PROMPTSIZ]; int ok; myname = whoami(); if (myname) ckmakxmsg(prompt,PROMPTSIZ," Name (",host,":",myname,"): ", NULL,NULL,NULL,NULL,NULL,NULL,NULL); else ckmakmsg(prompt,PROMPTSIZ," Name (",host,"): ",NULL); tmp[0] = '\0'; ok = uq_txt(NULL,prompt,1,NULL,tmp,PASSBUFSIZ,NULL, DEFAULT_UQ_TIMEOUT); if (!ok || *tmp == '\0') user = myname; else user = brstrip(tmp); } } n = ftpcmd("USER",user,-1,-1,ftp_vbm); if (n == REPLY_COMPLETE) { /* determine if we need to send a dummy password */ if (ftpcmd("PWD",NULL,0,0,0) != REPLY_COMPLETE) ftpcmd("PASS dummy",NULL,0,0,1); } else if (n == REPLY_CONTINUE) { #ifdef CK_ENCRYPTION int oldftp_cpl; #endif /* CK_ENCRYPTION */ if (pass == NULL) { int ok; setint(); ok = uq_txt(NULL," Password: ",2,NULL,ftppass,PASSBUFSIZ,NULL, DEFAULT_UQ_TIMEOUT); if (ok) pass = brstrip(ftppass); } #ifdef CK_ENCRYPTION oldftp_cpl = ftp_cpl; ftp_cpl = FPL_PRV; #endif /* CK_ENCRYPTION */ n = ftpcmd("PASS",pass,-1,-1,1); if (!anonymous && pass) { char * p = pass; while (*p++) *(p-1) = NUL; makestr(&ftp_tmp,NULL); } #ifdef CK_ENCRYPTION /* level may have changed */ if (ftp_cpl == FPL_PRV) ftp_cpl = oldftp_cpl; #endif /* CK_ENCRYPTION */ } if (n == REPLY_CONTINUE) { aflag++; if (acct == NULL) { static char ftpacct[80]; int ok; setint(); ok = uq_txt(NULL," Account: ",2,NULL,ftpacct,80,NULL, DEFAULT_UQ_TIMEOUT); if (ok) acct = brstrip(ftpacct); } n = ftpcmd("ACCT",acct,-1,-1,ftp_vbm); } if (n != REPLY_COMPLETE) { fprintf(stderr, "FTP login failed.\n"); if (haveurl) doexit(BAD_EXIT,-1); return(0); } if (!aflag && acct != NULL) { ftpcmd("ACCT",acct,-1,-1,ftp_vbm); } makestr(&ftp_logname,user); loggedin = 1; #ifdef LOCUS /* Unprefixed file management commands go to server */ if (autolocus && !ftp_cmdlin) { setlocus(0,1); } #endif /* LOCUS */ ftp_init(); if (anonymous && !quiet) { printf(" Logged in as anonymous (%s)\n",pass); memset(pass, 0, strlen(pass)); } if (ftp_rdir) { if (doftpcwd(ftp_rdir,-1) < 1) doexit(BAD_EXIT,-1); } #ifdef FTP_PROXY if (proxy) return(1); #endif /* FTP_PROXY */ return(1); } static int ftp_reset() { int rc; #ifdef BSDSELECT int nfnd = 1; fd_set mask; FD_ZERO(&mask); while (nfnd > 0) { FD_SET(csocket, &mask); if ((nfnd = empty(&mask,0)) < 0) { perror("reset"); ftpcode = -1; lostpeer(); return(0); } else if (nfnd) { getreply(0,-1,-1,ftp_vbm,0); } } #else /* BSDSELECT */ #ifdef IBMSELECT int nfnd = 1; while (nfnd > 0) { if ((nfnd = empty(&csocket,1,0)) < 0) { perror("reset"); ftpcode = -1; lostpeer(); return(0); } else if (nfnd) { getreply(0,-1,-1,ftp_vbm,0); } } #endif /* IBMSELECT */ #endif /* BSDSELECT */ rc = (ftpcmd("REIN",NULL,0,0,ftp_vbm) == REPLY_COMPLETE); if (rc > 0) loggedin = 0; return(rc); } static int ftp_rename(from, to) char * from, * to; { int lcs = -1, rcs = -1; #ifndef NOCSETS if (ftp_xla) { lcs = ftp_csl; if (lcs < 0) lcs = fcharset; rcs = ftp_csx; if (rcs < 0) rcs = ftp_csr; } #endif /* NOCSETS */ if (ftpcmd("RNFR",from,lcs,rcs,ftp_vbm) == REPLY_CONTINUE) { return(ftpcmd("RNTO",to,lcs,rcs,ftp_vbm) == REPLY_COMPLETE); } return(0); /* Failure */ } static int ftp_umask(mask) char * mask; { int rc; rc = (ftpcmd("SITE UMASK",mask,-1,-1,1) == REPLY_COMPLETE); return(rc); } static int ftp_user(user,pass,acct) char * user, * pass, * acct; { int n = 0, aflag = 0; char pwd[PWDSIZ]; if (!auth_type && ftp_aut) { #ifdef FTP_SRP if (ck_srp_is_installed()) { if (srp_ftp_auth( NULL, user, pass)) { makestr(&pass,srp_pass); } } #endif /* FTP_SRP */ } n = ftpcmd("USER",user,-1,-1,ftp_vbm); if (n == REPLY_COMPLETE) n = ftpcmd("PASS dummy",NULL,0,0,1); else if (n == REPLY_CONTINUE) { #ifdef CK_ENCRYPTION int oldftp_cpl; #endif /* CK_ENCRYPTION */ if (pass == NULL || !pass[0]) { int ok; pwd[0] = '\0'; setint(); ok = uq_txt(NULL," Password: ",2,NULL,pwd,PWDSIZ,NULL, DEFAULT_UQ_TIMEOUT); if (ok) pass = brstrip(pwd); } #ifdef CK_ENCRYPTION if ((oldftp_cpl = ftp_cpl) == PROT_S) ftp_cpl = PROT_P; #endif /* CK_ENCRYPTION */ n = ftpcmd("PASS",pass,-1,-1,1); memset(pass, 0, strlen(pass)); #ifdef CK_ENCRYPTION /* level may have changed */ if (ftp_cpl == PROT_P) ftp_cpl = oldftp_cpl; #endif /* CK_ENCRYPTION */ } if (n == REPLY_CONTINUE) { if (acct == NULL || !acct[0]) { int ok; pwd[0] = '\0'; setint(); ok = uq_txt(NULL," Account: ",2,NULL,pwd,PWDSIZ,NULL, DEFAULT_UQ_TIMEOUT); if (ok) acct = pwd; } n = ftpcmd("ACCT",acct,-1,-1,ftp_vbm); aflag++; } if (n != REPLY_COMPLETE) { printf("Login failed.\n"); return(0); } if (!aflag && acct != NULL && acct[0]) { n = ftpcmd("ACCT",acct,-1,-1,ftp_vbm); } if (n == REPLY_COMPLETE) { makestr(&ftp_logname,user); loggedin = 1; ftp_init(); return(1); } return(0); } char * ftp_authtype() { if (!connected) return("NULL"); return(auth_type ? auth_type : "NULL"); } char * ftp_cpl_mode() { switch (ftp_cpl) { case FPL_CLR: return("clear"); case FPL_SAF: return("safe"); case FPL_PRV: return("private"); case FPL_CON: return("confidential"); default: return("(error)"); } } char * ftp_dpl_mode() { switch (ftp_dpl) { case FPL_CLR: return("clear"); case FPL_SAF: return("safe"); case FPL_PRV: return("private"); case FPL_CON: return("confidential"); default: return("(error)"); } } /* remote_files() */ /* Returns next remote filename on success; NULL on error or no more files with global rfrc set to: -1: Bad argument -2: Server error response to NLST, e.g. file not found -3: No more files -9: Internal error */ #define FTPNAMBUFLEN CKMAXPATH+1024 /* Check: ckmaxfiles CKMAXOPEN */ #define MLSDEPTH 128 /* Stack of open temp files */ static int mlsdepth = 0; /* Temp file stack depth */ static FILE * tmpfilptr[MLSDEPTH+1] = { NULL, NULL }; /* Temp file pointers */ static char * tmpfilnam[MLSDEPTH+1] = { NULL, NULL }; /* Temp file names */ static VOID mlsreset() { /* Reset MGET temp-file stack */ int i; for (i = 0; i <= mlsdepth; i++) { if (tmpfilptr[i]) { fclose(tmpfilptr[i]); tmpfilptr[i] = NULL; if (tmpfilnam[i]) { #ifdef OS2 unlink(tmpfilnam[i]); #endif /* OS2 */ free(tmpfilnam[i]); } } } mlsdepth = 0; } static CHAR * #ifdef CK_ANSIC remote_files(int new_query, CHAR * arg, CHAR * pattern, int proxy_switch) #else /* CK_ANSIC */ remote_files(new_query, arg, pattern, proxy_switch) int new_query; CHAR * arg; /* That we send to the server */ CHAR * pattern; /* That we use locally */ int proxy_switch; #endif /* CK_ANSIC */ /* remote_files */ { static CHAR buf[FTPNAMBUFLEN]; CHAR *cp, *whicharg; char * cdto = NULL; char * p; int i, x, forced = 0; int lcs = 0, rcs = 0, xlate = 0; debug(F101,"ftp remote_files new_query","",new_query); debug(F110,"ftp remote_files arg",arg,0); debug(F110,"ftp remote_files pattern",pattern,0); rfrc = -1; if (pattern) /* Treat empty pattern same as NULL */ if (!*pattern) pattern = NULL; if (arg) /* Ditto for arg */ if (!*arg) arg = NULL; again: if (new_query) { if (tmpfilptr[mlsdepth]) { fclose(tmpfilptr[mlsdepth]); tmpfilptr[mlsdepth] = NULL; #ifdef OS2 if (!ftp_deb && !deblog) unlink(tmpfilnam[mlsdepth]); #endif /* OS2 */ } } if (tmpfilptr[mlsdepth] == NULL) { extern char * tempdir; char * p; debug(F110,"ftp remote_files tempdir",tempdir,0); if (tempdir) { p = tempdir; } else { #ifdef OS2 #ifdef NT p = getenv("K95TMP"); #else p = getenv("K2TMP"); #endif /* NT */ if (!p) #endif /* OS2 */ p = getenv("CK_TMP"); if (!p) p = getenv("TMPDIR"); if (!p) p = getenv("TEMP"); if (!p) p = getenv("TMP"); #ifdef OS2ORUNIX if (p) { int len = strlen(p); if (p[len-1] != '/' #ifdef OS2 && p[len-1] != '\\' #endif /* OS2 */ ) { static char foo[CKMAXPATH]; ckstrncpy(foo,p,CKMAXPATH); ckstrncat(foo,"/",CKMAXPATH); p = foo; } } else #else /* OS2ORUNIX */ if (!p) #endif /* OS2ORUNIX */ #ifdef UNIX /* Systems that have a standard */ p = "/tmp/"; /* temporary directory... */ #else #ifdef datageneral p = ":TMP:"; #else p = ""; #endif /* datageneral */ #endif /* UNIX */ } debug(F110,"ftp remote_files p",p,0); /* Get temp file */ if ((tmpfilnam[mlsdepth] = (char *)malloc(CKMAXPATH+1))) { ckmakmsg((char *)tmpfilnam[mlsdepth], CKMAXPATH+1,p,"ckXXXXXX",NULL,NULL); } else { printf("?Malloc failure: remote_files()\n"); return(NULL); } #ifdef NT { char * tmpfil = mktemp((char *)tmpfilnam[mlsdepth]); if ( tmpfil ) ckstrncpy(tmpfilnam[mlsdepth],tmpfil,CKMAXPATH+1); } #else /* NT */ #ifdef MKTEMP #ifdef MKSTEMP x = mkstemp((char *)tmpfilnam[mlsdepth]); if (x > -1) close(x); /* We just want the name. */ #else mktemp((char *)tmpfilnam[mlsdepth]); #endif /* MKSTEMP */ /* if no mktmpnam() the name will just be "ckXXXXXX"... */ #endif /* MKTEMP */ #endif /* NT */ debug(F111,"ftp remote_files tmpfilnam[mlsdepth]", tmpfilnam[mlsdepth],mlsdepth); #ifdef FTP_PROXY if (proxy_switch) { pswitch(!proxy); } #endif /* FTP_PROXY */ debug(F101,"ftp remote_files ftp_xla","",ftp_xla); debug(F101,"ftp remote_files ftp_csl","",ftp_csl); debug(F101,"ftp remote_files ftp_csr","",ftp_csr); #ifndef NOCSETS xlate = ftp_xla; /* SET FTP CHARACTER-SET-TRANSLATION */ if (xlate) { /* ON? */ lcs = ftp_csl; /* Local charset */ if (lcs < 0) lcs = fcharset; if (lcs < 0) xlate = 0; } if (xlate) { /* Still ON? */ rcs = ftp_csx; /* Remote (Server) charset */ if (rcs < 0) rcs = ftp_csr; if (rcs < 0) xlate = 0; } #endif /* NOCSETS */ forced = mgetforced; /* MGET method forced? */ if (!forced || !mgetmethod) /* Not forced... */ mgetmethod = (sfttab[0] && sfttab[SFT_MLST]) ? /* so pick one */ SND_MLS : SND_NLS; /* User's Command: Result: mget /nlst NLST (NULL) mget /nlst foo NLST foo mget /nlst *.txt NLST *.txt mget /nlst /match:*.txt NLST (NULL) mget /nlst /match:*.txt foo NLST foo mget /mlsd MLSD (NULL) mget /mlsd foo MLSD foo mget /mlsd *.txt MLSD (NULL) mget /mlsd /match:*.txt MLSD (NULL) mget /mlsd /match:*.txt foo MLSD foo */ x = -1; while (x < 0) { if (pattern) { /* Don't simplify this! */ whicharg = arg; } else if (mgetmethod == SND_MLS) { if (arg) whicharg = iswild((char *)arg) ? NULL : arg; else whicharg = NULL; } else { whicharg = arg; } debug(F110,"ftp remote_files mgetmethod", mgetmethod == SND_MLS ? "MLSD" : "NLST", 0); debug(F110,"ftp remote_files whicharg",whicharg,0); x = recvrequest((mgetmethod == SND_MLS) ? "MLSD" : "NLST", (char *)tmpfilnam[mlsdepth], (char *)whicharg, "wb", 0, 0, NULL, xlate, lcs, rcs ); if (x < 0) { /* Chosen method wasn't accepted */ if (forced) { if (ftpcode > 500 && ftpcode < 505 && !quiet) printf("?%s: Not supported by server\n", mgetmethod == SND_MLS ? "MLSD" : "NLST" ); rfrc = -2; /* Fail */ return(NULL); } /* Not forced - if MLSD failed, try NLST */ if (mgetmethod == SND_MLS) { /* Server lied about MLST */ sfttab[SFT_MLST] = 0; /* So disable it */ mlstok = 0; /* and */ mgetmethod = SND_NLS; /* try NLST */ continue; } rfrc = -2; return(NULL); } } #ifdef FTP_PROXY if (proxy_switch) { pswitch(!proxy); } #endif /* FTP_PROXY */ tmpfilptr[mlsdepth] = fopen((char *)tmpfilnam[mlsdepth], "r"); #ifndef OS2 if (tmpfilptr[mlsdepth]) { if (!ftp_deb && !deblog) unlink(tmpfilnam[mlsdepth]); } #endif /* OS2 */ notemp: if (!tmpfilptr[mlsdepth]) { debug(F110,"ftp remote_files open fail",tmpfilnam[mlsdepth],0); if ((!dpyactive || ftp_deb)) printf("?Can't find list of remote files, oops\n"); rfrc = -9; return(NULL); } if (ftp_deb) printf("LISTFILE: %s\n",tmpfilnam[mlsdepth]); } buf[0] = NUL; buf[FTPNAMBUFLEN-1] = NUL; buf[FTPNAMBUFLEN-2] = NUL; /* We have to redo all this because the first time was only for */ /* for getting the file list, now it's for getting each file */ if (arg && mgetmethod == SND_MLS) { /* MLSD */ if (!pattern && iswild((char *)arg)) { pattern = arg; /* Wild arg is really a pattern */ if (pattern) if (!*pattern) pattern = NULL; arg = NULL; /* and not an arg */ } if (new_query) { /* Initial query? */ cdto = (char *)arg; /* (nonwild) arg given? */ if (cdto) if (!*cdto) cdto = NULL; if (cdto) /* If so, then CD to it */ doftpcwd(cdto,0); } } new_query = 0; if (fgets((char *)buf, FTPNAMBUFLEN, tmpfilptr[mlsdepth]) == NULL) { fclose(tmpfilptr[mlsdepth]); tmpfilptr[mlsdepth] = NULL; #ifdef OS2 if (!ftp_deb && !deblog) unlink(tmpfilnam[mlsdepth]); #endif /* OS2 */ if (ftp_deb && !deblog) { printf("(Temporary file %s NOT deleted)\n", (char *)tmpfilnam[mlsdepth]); } if (mlsdepth <= 0) { /* EOF at depth 0 */ rfrc = -3; /* means we're done */ return(NULL); } printf("POPPING(%d)...\n",mlsdepth-1); if (tmpfilnam[mlsdepth]) free(tmpfilnam[mlsdepth]); mlsdepth--; doftpcdup(); zchdir(".."); /* <-- Not portable */ goto again; } if (buf[FTPNAMBUFLEN-1]) { printf("?BUFFER OVERFLOW -- FTP NLST or MLSD string longer than %d\n", FTPNAMBUFLEN ); debug(F101,"remote_files buffer overrun","",FTPNAMBUFLEN); return(NULL); } /* debug(F110,"ftp remote_files buf 1",buf,0); */ if ((cp = (CHAR *)ckstrchr((char *)buf,'\n')) != NULL) *cp = '\0'; if ((cp = (CHAR *)ckstrchr((char *)buf,'\r')) != NULL) *cp = '\0'; debug(F110,"ftp remote_files buf",buf,0); rfrc = 0; if (ftp_deb) printf("[%s]\n",(char *)buf); havesize = (CK_OFF_T)-1; /* Initialize file facts... */ havetype = 0; makestr(&havemdtm,NULL); p = (char *)buf; if (mgetmethod == SND_NLS) { /* NLST... */ if (pattern) { if (!ckmatch((char *)pattern,p,(servertype == SYS_UNIX),1)) goto again; } } else { /* MLSD... */ p = parsefacts((char *)buf); switch (havetype) { case FTYP_FILE: /* File: Get it if it matches */ if (pattern) { if (!ckmatch((char *)pattern,p,(servertype == SYS_UNIX),1)) goto again; } break; case FTYP_CDIR: /* Current directory */ case FTYP_PDIR: /* Parent directory */ goto again; /* Skip */ case FTYP_DIR: /* (Sub)Directory */ if (!recursive) /* If not /RECURSIVE */ goto again; /* Skip */ if (mlsdepth < MLSDEPTH) { char * p2 = NULL; mlsdepth++; printf("RECURSING [%s](%d)...\n",p,mlsdepth); if (doftpcwd(p,0) > 0) { int x; if (!ckstrchr(p,'/')) { /* zmkdir() needs dirsep */ if ((p2 = (char *)malloc((int)strlen(p) + 2))) { strcpy(p2,p); /* SAFE */ strcat(p2,"/"); /* SAFE */ p = p2; } } #ifdef NOMKDIR x = -1; #else x = zmkdir(p); #endif /* NOMKDIR */ if (x > -1) { zchdir(p); p = (char *)remote_files(1,arg,pattern,0); if (p2) free(p2); } else { printf("?mkdir failed: [%s] Depth=%d\n", p, mlsdepth ); mlsreset(); if (p2) free(p2); return(NULL); } } else { printf("?CWD failed: [%s] Depth=%d\n",p,mlsdepth); mlsreset(); return(NULL); } } else { printf("MAX DIRECTORY STACK DEPTH EXCEEDED: %d\n", mlsdepth ); mlsreset(); return(NULL); } } } #ifdef DEBUG if (deblog) { debug(F101,"remote_files havesize","",havesize); debug(F101,"remote_files havetype","",havetype); debug(F110,"remote_files havemdtm",havemdtm,0); debug(F110,"remote_files name",p,0); } #endif /* DEBUG */ return((CHAR *)p); } /* N O T P O R T A B L E !!! */ #if (SIZEOF_SHORT == 4) typedef unsigned short ftp_uint32; typedef short ftp_int32; #else #if (SIZEOF_INT == 4) typedef unsigned int ftp_uint32; typedef int ftp_int32; #else #if (SIZEOF_LONG == 4) typedef ULONG ftp_uint32; typedef long ftp_int32; #endif #endif #endif /* Perhaps use these in general, certainly use them for GSSAPI */ #ifndef looping_write #define ftp_int32 int #define ftp_uint32 unsigned int static int looping_write(fd, buf, len) int fd; register CONST char *buf; int len; { int cc; register int wrlen = len; do { cc = send(fd, (SENDARG2TYPE)buf, wrlen, 0); if (cc < 0) { if (errno == EINTR) continue; return(cc); } else { buf += cc; wrlen -= cc; } } while (wrlen > 0); return(len); } #endif #ifndef looping_read static int looping_read(fd, buf, len) int fd; register char *buf; register int len; { int cc, len2 = 0; do { cc = recv(fd, (char *)buf, len,0); if (cc < 0) { if (errno == EINTR) continue; return(cc); /* errno is already set */ } else if (cc == 0) { return(len2); } else { buf += cc; len2 += cc; len -= cc; } } while (len > 0); return(len2); } #endif /* looping_read */ #define ERR -2 #ifdef COMMENT static secure_putbyte(fd, c) int fd; CHAR c; { int ret; ucbuf[nout++] = c; if (nout == (maxbuf ? maxbuf : actualbuf) - FUDGE_FACTOR) { nout = 0; if (!ftpissecure()) ret = send(fd, (SENDARG2TYPE)ucbuf, (maxbuf ? maxbuf : actualbuf) - FUDGE_FACTOR, 0); else ret = secure_putbuf(fd, ucbuf, (maxbuf ? maxbuf : actualbuf) - FUDGE_FACTOR ); return(ret?ret:c); } return(c); } #endif /* COMMENT */ /* returns: * 0 on success * -1 on error (errno set) * -2 on security error */ static int secure_flush(fd) int fd; { int rc = 0; int len = 0; if (nout > 0) { len = nout; if (!ftpissecure()) { rc = send(fd, (SENDARG2TYPE)ucbuf, nout, 0); nout = 0; goto xflush; } else { rc = secure_putbuf(fd, ucbuf, nout); if (rc) goto xflush; } } rc = (!ftpissecure()) ? 0 : secure_putbuf(fd, (CHAR *)"", nout = 0); xflush: if (rc > -1 && len > 0 && fdispla != XYFD_B) { spackets++; spktl = len; ftscreen(SCR_PT,'D',(CK_OFF_T)spackets,NULL); } return(rc); } #ifdef COMMENT /* (not used) */ /* returns: * c>=0 on success * -1 on error * -2 on security error */ static int #ifdef CK_ANSIC secure_putc(char c, int fd) #else secure_putc(c, fd) char c; int fd; #endif /* CK_ANSIC */ /* secure_putc */ { return(secure_putbyte(fd, (CHAR) c)); } #endif /* COMMENT */ /* returns: * nbyte on success * -1 on error (errno set) * -2 on security error */ static int #ifdef CK_ANSIC secure_write(int fd, CHAR * buf, unsigned int nbyte) #else secure_write(fd, buf, nbyte) int fd; CHAR * buf; unsigned int nbyte; #endif /* CK_ANSIC */ { int ret; #ifdef FTP_TIMEOUT ftp_timed_out = 0; if (check_data_connection(fd,1) < 0) { ftp_timed_out = 1; return(-3); } #endif /* FTP_TIMEOUT */ if (!ftpissecure()) { if (nout > 0) { if ((ret = send(fd, (SENDARG2TYPE)ucbuf, nout, 0)) < 0) return(ret); nout = 0; } return(send(fd,(SENDARG2TYPE)buf,nbyte,0)); } else { int ucbuflen = (maxbuf ? maxbuf : actualbuf) - FUDGE_FACTOR; int bsent = 0; while (bsent < nbyte) { int b2cp = ((nbyte - bsent) > (ucbuflen - nout) ? (ucbuflen - nout) : (nbyte - bsent)); #ifdef DEBUG if (deblog) { debug(F101,"secure_write ucbuflen","",ucbuflen); debug(F101,"secure_write ucbufsiz","",ucbufsiz); debug(F101,"secure_write bsent","",bsent); debug(F101,"secure_write b2cp","",b2cp); } #endif /* DEBUG */ memcpy(&ucbuf[nout],&buf[bsent],b2cp); nout += b2cp; bsent += b2cp; if (nout == ucbuflen) { nout = 0; ret = secure_putbuf(fd, ucbuf, ucbuflen); if (ret < 0) return(ret); } } return(bsent); } } /* returns: * 0 on success * -1 on error (errno set) * -2 on security error */ static int #ifdef CK_ANSIC secure_putbuf(int fd, CHAR * buf, unsigned int nbyte) #else secure_putbuf(fd, buf, nbyte) int fd; CHAR * buf; unsigned int nbyte; #endif /* CK_ANSIC */ { static char *outbuf = NULL; /* output ciphertext */ #ifdef FTP_SECURITY static unsigned int bufsize = 0; /* size of outbuf */ #endif /* FTP_SECURITY */ ftp_int32 length = 0; ftp_uint32 net_len = 0; /* Other auth types go here ... */ #ifdef CK_SSL if (ssl_ftp_data_active_flag) { int count, error; /* there is no need to send an empty buffer when using SSL/TLS */ if ( nbyte == 0 ) return(0); count = SSL_write(ssl_ftp_data_con, buf, nbyte); error = SSL_get_error(ssl_ftp_data_con,count); switch (error) { case SSL_ERROR_NONE: return(0); case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_READ: case SSL_ERROR_SYSCALL: #ifdef NT { int gle = GetLastError(); if (gle == 0) return(0); debug(F111,"secure_putbuf","SSL_ERROR_SYSCALL",gle); } #endif /* NT */ case SSL_ERROR_WANT_X509_LOOKUP: case SSL_ERROR_SSL: case SSL_ERROR_ZERO_RETURN: default: SSL_shutdown(ssl_ftp_data_con); SSL_free(ssl_ftp_data_con); ssl_ftp_data_active_flag = 0; ssl_ftp_data_con = NULL; #ifdef TCPIPLIB socket_close(data); #else /* TCPIPLIB */ #ifdef USE_SHUTDOWN shutdown(data, 1+1); #endif /* USE_SHUTDOWN */ close(data); #endif /* TCPIPLIB */ data = -1; globaldin = data; return(-1); } return(-1); } #endif /* CK_SSL */ #ifdef FTP_SRP if (ck_srp_is_installed() && (strcmp(auth_type, "SRP") == 0)) { if (bufsize < nbyte + FUDGE_FACTOR) { if (outbuf? (outbuf = realloc(outbuf, (unsigned) (nbyte + FUDGE_FACTOR))): (outbuf = malloc((unsigned) (nbyte + FUDGE_FACTOR)))) { bufsize = nbyte + FUDGE_FACTOR; } else { bufsize = 0; secure_error("%s (in malloc of PROT buffer)", ck_errstr()); return(ERR); } } if ((length = srp_encode(ftp_dpl == FPL_PRV, (CHAR *) buf, (CHAR *) outbuf, nbyte ) ) < 0) { secure_error ("srp_encode failed"); return ERR; } } #endif /* FTP_SRP */ #ifdef FTP_KRB4 if (ck_krb4_is_installed() && (strcmp(auth_type, "KERBEROS_V4") == 0)) { struct sockaddr_in myaddr, hisaddr; GSOCKNAME_T len; len = sizeof(myaddr); if (getsockname(fd, (struct sockaddr*)&myaddr, &len) < 0) { secure_error("secure_putbuf: getsockname failed"); return(ERR); } len = sizeof(hisaddr); if (getpeername(fd, (struct sockaddr*)&hisaddr, &len) < 0) { secure_error("secure_putbuf: getpeername failed"); return(ERR); } if (bufsize < nbyte + FUDGE_FACTOR) { if (outbuf ? (outbuf = realloc(outbuf, (unsigned) (nbyte + FUDGE_FACTOR))): (outbuf = malloc((unsigned) (nbyte + FUDGE_FACTOR)))) { bufsize = nbyte + FUDGE_FACTOR; } else { bufsize = 0; secure_error("%s (in malloc of PROT buffer)", ck_errstr()); return(ERR); } } if (ftp_dpl == FPL_PRV) { length = krb_mk_priv(buf, (CHAR *) outbuf, nbyte, ftp_sched, #ifdef KRB524 ftp_cred.session, #else /* KRB524 */ &ftp_cred.session, #endif /* KRB524 */ &myaddr, &hisaddr ); } else { length = krb_mk_safe(buf, (CHAR *) outbuf, nbyte, #ifdef KRB524 ftp_cred.session, #else /* KRB524 */ &ftp_cred.session, #endif /* KRB524 */ &myaddr, &hisaddr ); } if (length == -1) { secure_error("krb_mk_%s failed for KERBEROS_V4", ftp_dpl == FPL_PRV ? "priv" : "safe"); return(ERR); } } #endif /* FTP_KRB4 */ #ifdef FTP_GSSAPI if (ck_gssapi_is_installed() && (strcmp(auth_type, "GSSAPI") == 0)) { gss_buffer_desc in_buf, out_buf; OM_uint32 maj_stat, min_stat; int conf_state; in_buf.value = buf; in_buf.length = nbyte; maj_stat = gss_seal(&min_stat, gcontext, (ftp_dpl == FPL_PRV), /* confidential */ GSS_C_QOP_DEFAULT, &in_buf, &conf_state, &out_buf ); if (maj_stat != GSS_S_COMPLETE) { /* generally need to deal */ /* ie. should loop, but for now just fail */ user_gss_error(maj_stat, min_stat, ftp_dpl == FPL_PRV? "GSSAPI seal failed": "GSSAPI sign failed"); return(ERR); } if (bufsize < out_buf.length) { if (outbuf ? (outbuf = realloc(outbuf, (unsigned) out_buf.length)): (outbuf = malloc((unsigned) out_buf.length))) { bufsize = out_buf.length; } else { bufsize = 0; secure_error("%s (in malloc of PROT buffer)", ck_errstr()); return(ERR); } } memcpy(outbuf, out_buf.value, length=out_buf.length); gss_release_buffer(&min_stat, &out_buf); } #endif /* FTP_GSSAPI */ net_len = htonl((ULONG) length); if (looping_write(fd, (char *)&net_len, 4) == -1) return(-1); if (looping_write(fd, outbuf, length) != length) return(-1); return(0); } /* fc = 0 means to get a byte; nonzero means to initialize buffer pointers */ static int secure_getbyte(fd,fc) int fd,fc; { /* number of chars in ucbuf, pointer into ucbuf */ static unsigned int nin = 0, bufp = 0; int kerror; ftp_uint32 length; if (fc) { nin = bufp = 0; ucbuf[0] = NUL; return(0); } if (nin == 0) { if (iscanceled()) return(-9); #ifdef FTP_TIMEOUT if (check_data_connection(fd,0) < 0) return(-3); #endif /* FTP_TIMEOUT */ #ifdef CK_SSL if (ssl_ftp_data_active_flag) { int count, error; count = SSL_read(ssl_ftp_data_con, ucbuf, ucbufsiz); error = SSL_get_error(ssl_ftp_data_con,count); #ifdef DEBUG if (error != SSL_ERROR_NONE) debug(F101,"ftp secure_getbyte error","",error); if (count == 0) debug(F101,"ftp secure_getbyte count","",count); #endif /* DEBUG */ switch (error) { case SSL_ERROR_NONE: if (count > 0) { nin = bufp = count; rpackets++; pktnum++; if (fdispla != XYFD_B) { rpktl = count; ftscreen(SCR_PT,'D',(CK_OFF_T)rpackets,NULL); } break; } case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_READ: case SSL_ERROR_SYSCALL: #ifdef NT { int gle = GetLastError(); } #endif /* NT */ case SSL_ERROR_WANT_X509_LOOKUP: case SSL_ERROR_SSL: case SSL_ERROR_ZERO_RETURN: default: nin = bufp = count = 0; SSL_shutdown(ssl_ftp_data_con); SSL_free(ssl_ftp_data_con); ssl_ftp_data_active_flag = 0; ssl_ftp_data_con = NULL; #ifdef TCPIPLIB socket_close(data); #else /* TCPIPLIB */ #ifdef USE_SHUTDOWN shutdown(data, 1+1); #endif /* USE_SHUTDOWN */ close(data); #endif /* TCPIPLIB */ data = -1; globaldin = data; break; } } else #endif /* CK_SSL */ { kerror = looping_read(fd, (char *)&length, sizeof(length)); if (kerror != sizeof(length)) { secure_error("Couldn't read PROT buffer length: %d/%s", kerror, kerror == -1 ? ck_errstr() : "premature EOF" ); return(ERR); } debug(F101,"secure_getbyte length","",length); debug(F101,"secure_getbyte ntohl(length)","",ntohl(length)); length = (ULONG) ntohl(length); if (length > maxbuf) { secure_error("Length (%d) of PROT buffer > PBSZ=%u", length, maxbuf ); return(ERR); } if ((kerror = looping_read(fd, ucbuf, length)) != length) { secure_error("Couldn't read %u byte PROT buffer: %s", length, kerror == -1 ? ck_errstr() : "premature EOF" ); return(ERR); } /* Other auth types go here ... */ #ifdef FTP_SRP if (strcmp(auth_type, "SRP") == 0) { if ((nin = bufp = srp_decode (ftp_dpl == FPL_PRV, (CHAR *) ucbuf, ucbuf, length ) ) == -1) { secure_error ("srp_encode failed" ); return ERR; } } #endif /* FTP_SRP */ #ifdef FTP_KRB4 if (strcmp(auth_type, "KERBEROS_V4") == 0) { struct sockaddr_in myaddr, hisaddr; GSOCKNAME_T len; len = sizeof(myaddr); if (getsockname(fd, (struct sockaddr*)&myaddr, &len) < 0) { secure_error("secure_putbuf: getsockname failed"); return(ERR); } len = sizeof(hisaddr); if (getpeername(fd, (struct sockaddr*)&hisaddr, &len) < 0) { secure_error("secure_putbuf: getpeername failed"); return(ERR); } if (ftp_dpl) { kerror = krb_rd_priv(ucbuf, length, ftp_sched, #ifdef KRB524 ftp_cred.session, #else /* KRB524 */ &ftp_cred.session, #endif /* KRB524 */ &hisaddr, &myaddr, &ftp_msg_data); } else { kerror = krb_rd_safe(ucbuf, length, #ifdef KRB524 ftp_cred.session, #else /* KRB524 */ &ftp_cred.session, #endif /* KRB524 */ &hisaddr, &myaddr, &ftp_msg_data); } if (kerror) { secure_error("krb_rd_%s failed for KERBEROS_V4 (%s)", ftp_dpl == FPL_PRV ? "priv" : "safe", krb_get_err_text(kerror)); return(ERR); } memcpy(ucbuf,ftp_msg_data.app_data,ftp_msg_data.app_length); nin = bufp = ftp_msg_data.app_length; } #endif /* FTP_KRB4 */ #ifdef FTP_GSSAPI if (strcmp(auth_type, "GSSAPI") == 0) { gss_buffer_desc xmit_buf, msg_buf; OM_uint32 maj_stat, min_stat; int conf_state; xmit_buf.value = ucbuf; xmit_buf.length = length; conf_state = (ftp_dpl == FPL_PRV); /* decrypt/verify the message */ maj_stat = gss_unseal(&min_stat, gcontext, &xmit_buf, &msg_buf, &conf_state, NULL); if (maj_stat != GSS_S_COMPLETE) { user_gss_error(maj_stat, min_stat, (ftp_dpl == FPL_PRV)? "failed unsealing ENC message": "failed unsealing MIC message"); return ERR; } memcpy(ucbuf, msg_buf.value, nin = bufp = msg_buf.length); gss_release_buffer(&min_stat, &msg_buf); } #endif /* FTP_GSSAPI */ /* Other auth types go here ... */ /* Update file transfer display */ rpackets++; pktnum++; if (fdispla != XYFD_B) { rpktl = nin; ftscreen(SCR_PT,'D',(CK_OFF_T)rpackets,NULL); } } } if (nin == 0) return(EOF); else return(ucbuf[bufp - nin--]); } /* secure_getc(fd,fc) * Call with: * fd = file descriptor for connection. * fc = 0 to get a character, fc != 0 to initialize buffer pointers. * Returns: * c>=0 on success (character value) * -1 on EOF * -2 on security error * -3 on timeout (if built with FTP_TIMEOUT defined) */ static int secure_getc(fd,fc) int fd,fc; { /* file descriptor, function code */ if (!ftpissecure()) { static unsigned int nin = 0, bufp = 0; if (fc) { nin = bufp = 0; ucbuf[0] = NUL; return(0); } if (nin == 0) { if (iscanceled()) return(-9); #ifdef FTP_TIMEOUT if (check_data_connection(fd,0) < 0) { debug(F100,"secure_getc TIMEOUT","",0); nin = bufp = 0; ftp_timed_out = 1; return(-3); } #endif /* FTP_TIMEOUT */ nin = bufp = recv(fd,(char *)ucbuf,actualbuf,0); if ((nin == 0) || (nin == (unsigned int)-1)) { debug(F111,"secure_getc recv errno",ckitoa(nin),errno); debug(F101,"secure_getc returns EOF","",EOF); nin = bufp = 0; return(EOF); } debug(F101,"ftp secure_getc recv","",nin); ckhexdump("ftp secure_getc recv",ucbuf,16); rpackets++; pktnum++; if (fdispla != XYFD_B) { rpktl = nin; ftscreen(SCR_PT,'D',(CK_OFF_T)rpackets,NULL); } } return(ucbuf[bufp - nin--]); } else return(secure_getbyte(fd,fc)); } /* returns: * n>0 on success (n == # of bytes read) * 0 on EOF * -1 on error (errno set), only for FPL_CLR * -2 on security error */ static int secure_read(fd, buf, nbyte) int fd; char *buf; int nbyte; { static int c = 0; int i; debug(F101,"secure_read bytes requested","",nbyte); if (c == EOF) return(c = 0); for (i = 0; nbyte > 0; nbyte--) { c = secure_getc(fd,0); switch (c) { case -9: /* Canceled from keyboard */ debug(F101,"ftp secure_read interrupted","",c); return(0); case ERR: debug(F101,"ftp secure_read error","",c); return(c); case EOF: debug(F101,"ftp secure_read EOF","",c); if (!i) c = 0; return(i); #ifdef FTP_TIMEOUT case -3: debug(F101,"ftp secure_read timeout","",c); return(c); #endif /* FTP_TIMEOUT */ default: buf[i++] = c; } } return(i); } #ifdef USE_RUSERPASS /* BEGIN_RUSERPASS * * Copyright (c) 1985 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static char sccsid[] = "@(#)ruserpass.c 5.3 (Berkeley) 3/1/91"; #endif /* not lint */ #ifndef MAXHOSTNAMELEN #define MAXHOSTNAMELEN 64 #endif char * renvlook(); static FILE * cfile; #define DEFAULT 1 #define LOGIN 2 #define PASSWD 3 #define ACCOUNT 4 #define MACDEF 5 #define ID 10 #define MACH 11 static char tokval[100]; static struct toktab { char *tokstr; int tval; } toktab[]= { "default", DEFAULT, "login", LOGIN, "password", PASSWD, "passwd", PASSWD, "account", ACCOUNT, "machine", MACH, "macdef", MACDEF, 0, 0 }; static int token() { char *cp; int c; struct toktab *t; if (feof(cfile)) return(0); while ((c = getc(cfile)) != EOF && (c == '\n' || c == '\t' || c == ' ' || c == ',')) continue; if (c == EOF) return(0); cp = tokval; if (c == '"') { while ((c = getc(cfile)) != EOF && c != '"') { if (c == '\\') c = getc(cfile); *cp++ = c; } } else { *cp++ = c; while ((c = getc(cfile)) != EOF && c != '\n' && c != '\t' && c != ' ' && c != ',') { if (c == '\\') c = getc(cfile); *cp++ = c; } } *cp = 0; if (tokval[0] == 0) return(0); for (t = toktab; t->tokstr; t++) if (!strcmp(t->tokstr, tokval)) return(t->tval); return(ID); } ruserpass(host, aname, apass, aacct) char *host, **aname, **apass, **aacct; { char *hdir, buf[FTP_BUFSIZ], *tmp; char myname[MAXHOSTNAMELEN], *mydomain; int t, i, c, usedefault = 0; #ifdef NT struct _stat stb; #else /* NT */ struct stat stb; #endif /* NT */ hdir = getenv("HOME"); if (hdir == NULL) hdir = "."; ckmakmsg(buf,FTP_BUFSIZ,hdir,"/.netrc",NULL,NULL); cfile = fopen(buf, "r"); if (cfile == NULL) { if (errno != ENOENT) perror(buf); return(0); } if (gethostname(myname, MAXHOSTNAMELEN) < 0) myname[0] = '\0'; if ((mydomain = ckstrchr(myname, '.')) == NULL) mydomain = ""; next: while ((t = token())) switch(t) { case DEFAULT: usedefault = 1; /* FALL THROUGH */ case MACH: if (!usedefault) { if (token() != ID) continue; /* * Allow match either for user's input host name * or official hostname. Also allow match of * incompletely-specified host in local domain. */ if (ckstrcmp(host, tokval,-1,1) == 0) goto match; if (ckstrcmp(ftp_host, tokval,-1,0) == 0) goto match; if ((tmp = ckstrchr(ftp_host, '.')) != NULL && ckstrcmp(tmp, mydomain,-1,1) == 0 && ckstrcmp(ftp_host, tokval, tmp-ftp_host,0) == 0 && tokval[tmp - ftp_host] == '\0') goto match; if ((tmp = ckstrchr(host, '.')) != NULL && ckstrcmp(tmp, mydomain,-1,1) == 0 && ckstrcmp(host, tokval, tmp - host, 0) == 0 && tokval[tmp - host] == '\0') goto match; continue; } match: while ((t = token()) && t != MACH && t != DEFAULT) switch(t) { case LOGIN: if (token()) if (*aname == 0) { *aname = malloc((unsigned) strlen(tokval) + 1); strcpy(*aname, tokval); /* safe */ } else { if (strcmp(*aname, tokval)) goto next; } break; case PASSWD: if (strcmp(*aname, "anonymous") && fstat(fileno(cfile), &stb) >= 0 && (stb.st_mode & 077) != 0) { fprintf(stderr, "Error - .netrc file not correct mode.\n"); fprintf(stderr, "Remove password or correct mode.\n"); goto bad; } if (token() && *apass == 0) { *apass = malloc((unsigned) strlen(tokval) + 1); strcpy(*apass, tokval); /* safe */ } break; case ACCOUNT: if (fstat(fileno(cfile), &stb) >= 0 && (stb.st_mode & 077) != 0) { fprintf(stderr, "Error - .netrc file not correct mode.\n"); fprintf(stderr, "Remove account or correct mode.\n"); goto bad; } if (token() && *aacct == 0) { *aacct = malloc((unsigned) strlen(tokval) + 1); strcpy(*aacct, tokval); /* safe */ } break; default: fprintf(stderr, "Unknown .netrc keyword %s\n", tokval); break; } goto done; } done: fclose(cfile); return(0); bad: fclose(cfile); return(-1); } #endif /* USE_RUSERPASS */ static char *radixN = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; static char pad = '='; static int radix_encode(inbuf, outbuf, inlen, outlen, decode) CHAR inbuf[], outbuf[]; int inlen, *outlen, decode; { int i, j, D = 0; char *p; CHAR c = NUL; if (decode) { for (i = 0, j = 0; inbuf[i] && inbuf[i] != pad; i++) { if ((p = ckstrchr(radixN, inbuf[i])) == NULL) return(1); D = p - radixN; switch (i&3) { case 0: outbuf[j] = D<<2; break; case 1: outbuf[j++] |= D>>4; outbuf[j] = (D&15)<<4; break; case 2: outbuf[j++] |= D>>2; outbuf[j] = (D&3)<<6; break; case 3: outbuf[j++] |= D; } if (j == *outlen) return(4); } switch (i&3) { case 1: return(3); case 2: if (D&15) return(3); if (strcmp((char *)&inbuf[i], "==")) return(2); break; case 3: if (D&3) return(3); if (strcmp((char *)&inbuf[i], "=")) return(2); } *outlen = j; } else { for (i = 0, j = 0; i < inlen; i++) { switch (i%3) { case 0: outbuf[j++] = radixN[inbuf[i]>>2]; c = (inbuf[i]&3)<<4; break; case 1: outbuf[j++] = radixN[c|inbuf[i]>>4]; c = (inbuf[i]&15)<<2; break; case 2: outbuf[j++] = radixN[c|inbuf[i]>>6]; outbuf[j++] = radixN[inbuf[i]&63]; c = 0; } if (j == *outlen) return(4); } if (i%3) outbuf[j++] = radixN[c]; switch (i%3) { case 1: outbuf[j++] = pad; case 2: outbuf[j++] = pad; } outbuf[*outlen = j] = '\0'; } return(0); } static char * radix_error(e) int e; { switch (e) { case 0: return("Success"); case 1: return("Bad character in encoding"); case 2: return("Encoding not properly padded"); case 3: return("Decoded # of bits not a multiple of 8"); case 4: return("Output buffer too small"); default: return("Unknown error"); } } /* END_RUSERPASS */ #ifdef FTP_SRP /*---------------------------------------------------------------------------+ | | | Package: srpftp | | Author: Eugene Jhong | | | +---------------------------------------------------------------------------*/ /* * Copyright (c) 1997-1999 The Stanford SRP Authentication Project * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. * * IN NO EVENT SHALL STANFORD BE LIABLE FOR ANY SPECIAL, INCIDENTAL, * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * In addition, the following conditions apply: * * 1. Any software that incorporates the SRP authentication technology * must display the following acknowlegment: * "This product uses the 'Secure Remote Password' cryptographic * authentication system developed by Tom Wu (tjw@CS.Stanford.EDU)." * * 2. Any software that incorporates all or part of the SRP distribution * itself must also display the following acknowledgment: * "This product includes software developed by Tom Wu and Eugene * Jhong for the SRP Distribution (http://srp.stanford.edu/srp/)." * * 3. Redistributions in source or binary form must retain an intact copy * of this copyright notice and list of conditions. */ #define SRP_PROT_VERSION 1 #ifdef CK_ENCRYPTION #define SRP_DEFAULT_CIPHER CIPHER_ID_CAST5_CBC #else #define SRP_DEFAULT_CIPHER CIPHER_ID_NONE #endif /* CK_ENCRYPTION */ #define SRP_DEFAULT_HASH HASH_ID_SHA CHAR srp_pref_cipher = CIPHER_ID_DES3_ECB; CHAR srp_pref_hash = HASH_ID_SHA; static struct t_client *tc = NULL; static CHAR *skey = NULL; static krypto_context *incrypt = NULL; static krypto_context *outcrypt = NULL; typedef unsigned int srp_uint32; /*--------------------------------------------------------------+ | srp_selcipher: select cipher | +--------------------------------------------------------------*/ static int srp_selcipher (cname) char *cname; { cipher_desc *cd; if (!(cd = cipher_getdescbyname (cname))) { int i; CHAR *list = cipher_getlist (); fprintf (stderr, "ftp: supported ciphers:\n\n"); for (i = 0; i < strlen (list); i++) fprintf (stderr, " %s\n", (cipher_getdescbyid(list[i]))->name); fprintf (stderr, "\n"); return -1; } srp_pref_cipher = cd->id; return 0; } /*--------------------------------------------------------------+ | srp_selhash: select hash | +--------------------------------------------------------------*/ static int srp_selhash (hname) char *hname; { hash_desc *hd; if (!(hd = hash_getdescbyname (hname))) { int i; CHAR *list = hash_getlist (); fprintf (stderr, "ftp: supported hash functions:\n\n"); for (i = 0; i < strlen (list); i++) fprintf (stderr, " %s\n", (hash_getdescbyid(list[i]))->name); fprintf (stderr, "\n"); return -1; } srp_pref_hash = hd->id; return 0; } /*--------------------------------------------------------------+ | srp_userpass: get username and password | +--------------------------------------------------------------*/ static int srp_userpass (host) char *host; { char tmp[BUFSIZ], prompt[PROMPTSIZ]; char *user; user = NULL; #ifdef USE_RUSERPASS ruserpass (host, &user, &srp_pass, &srp_acct); #endif /* USE_RUSERPASS */ while (user == NULL) { char *myname; int ok; myname = whoami(); if (!myname) myname = ""; if (myname[0]) ckmakxmsg(prompt,PROMPTSIZ," Name (",host,":",myname,"): ", NULL,NULL,NULL,NULL,NULL,NULL,NULL); else ckmakmsg(prompt,PROMPTSIZ," Name (",host,"): ",NULL); tmp[0] = '\0'; ok = uq_txt(NULL,prompt,1,NULL,tmp,BUFSIZ,NULL, DEFAULT_UQ_TIMEOUT); if (!ok || *tmp == '\0') user = myname; else user = brstrip(tmp); } ckstrncpy (srp_user, user,BUFSIZ); return(0); } /*--------------------------------------------------------------+ | srp_reset: reset srp information | +--------------------------------------------------------------*/ static int srp_reset () { if (tc) { t_clientclose (tc); tc = NULL; } if (incrypt) { krypto_delete (incrypt); incrypt = NULL; } if (outcrypt) { krypto_delete (outcrypt); outcrypt = NULL; } return(0); } /*--------------------------------------------------------------+ | srp_ftp_auth: perform srp authentication | +--------------------------------------------------------------*/ static int srp_ftp_auth(host, user, pass) char *host; char *user; char *pass; { struct t_num *wp; struct t_num N; struct t_num g; struct t_num s; struct t_num yp; CHAR buf[FTP_BUFSIZ]; CHAR tmp[FTP_BUFSIZ]; CHAR *bp, *cp; int n, e, clen, blen, len, i; CHAR cid = 0; CHAR hid = 0; srp_pass = srp_acct = 0; n = ftpcmd("AUTH SRP",NULL,0,0,ftp_vbm); if (n != REPLY_CONTINUE) { if (ftp_deb) fprintf(stderr, "SRP rejected as an authentication type\n"); return(0); } else { /* Send protocol version */ CHAR vers[4]; memset (vers, 0, 4); vers[3] = SRP_PROT_VERSION; if (!quiet) printf ("SRP accepted as authentication type.\n"); bp = tmp; blen = 0; srp_put (vers, &bp, 4, &blen); len = FTP_BUFSIZ; if (e = radix_encode (tmp, buf, blen, &len, RADIX_ENCODE)) goto encode_error; reply_parse = "ADAT="; n = ftpcmd("ADAT",buf,-1,-1,0); } if (n == REPLY_CONTINUE) { /* Get protocol version */ bp = buf; if (!reply_parse) goto data_error; blen = FTP_BUFSIZ; if (e = radix_encode(reply_parse, bp, 0, &blen, RADIX_DECODE)) goto decode_error; if (srp_get (&bp, &cp, &blen, &clen) != 4) goto data_error; if (host) { /* Get username/password if needed */ srp_userpass (host); } else { ckstrncpy (srp_user, user, BUFSIZ); srp_pass = pass; } bp = tmp; blen = 0; /* Send username */ srp_put (srp_user, &bp, strlen (srp_user), &blen); len = FTP_BUFSIZ; if (e = radix_encode (tmp, buf, blen, &len, RADIX_ENCODE)) goto encode_error; reply_parse = "ADAT="; n = ftpcmd("ADAT",buf,-1,-1,0); } if (n == REPLY_CONTINUE) { /* Get N, g and s */ bp = buf; if (!reply_parse) goto data_error; blen = FTP_BUFSIZ; if (e = radix_encode (reply_parse, bp, 0, &blen, RADIX_DECODE)) goto decode_error; if (srp_get (&bp, &(N.data), &blen, &(N.len)) < 0) goto data_error; if (srp_get (&bp, &(g.data), &blen, &(g.len)) < 0) goto data_error; if (srp_get (&bp, &(s.data), &blen, &(s.len)) < 0) goto data_error; if ((tc = t_clientopen (srp_user, &N, &g, &s)) == NULL) { fprintf (stderr, "Unable to open SRP client structure.\n"); goto bad; } wp = t_clientgenexp (tc); /* Send wp */ bp = tmp; blen = 0; srp_put (wp->data, &bp, wp->len, &blen); len = FTP_BUFSIZ; if (e = radix_encode (tmp, buf, blen, &len, RADIX_ENCODE)) goto encode_error; reply_parse = "ADAT="; n = ftpcmd("ADAT",buf,-1,-1,0); } if (n == REPLY_CONTINUE) { /* Get yp */ bp = buf; if (!reply_parse) goto data_error; blen = FTP_BUFSIZ; if (e = radix_encode (reply_parse, bp, 0, &blen, RADIX_DECODE)) goto decode_error; if (srp_get (&bp, &(yp.data), &blen, &(yp.len)) < 0) goto data_error; if (!srp_pass) { static char ftppass[PASSBUFSIZ]; int ok; setint(); ok = uq_txt(NULL," SRP Password: ",2,NULL,ftppass,PASSBUFSIZ,NULL, DEFAULT_UQ_TIMEOUT); if (ok) srp_pass = brstrip(ftppass); } t_clientpasswd (tc, srp_pass); memset (srp_pass, 0, strlen (srp_pass)); skey = t_clientgetkey (tc, &yp); /* Send response */ bp = tmp; blen = 0; srp_put (t_clientresponse (tc), &bp, 20, &blen); len = FTP_BUFSIZ; if (e = radix_encode (tmp, buf, blen, &len, RADIX_ENCODE)) goto encode_error; reply_parse = "ADAT="; n = ftpcmd("ADAT",buf,-1,-1,0); } if (n == REPLY_CONTINUE) { /* Get response */ bp = buf; if (!reply_parse) goto data_error; blen = FTP_BUFSIZ; if (e = radix_encode (reply_parse, bp, 0, &blen, RADIX_DECODE)) goto encode_error; if (srp_get (&bp, &cp, &blen, &clen) != 20) goto data_error; if (t_clientverify (tc, cp)) { fprintf (stderr, "WARNING: bad response to client challenge.\n"); goto bad; } bp = tmp; blen = 0; /* Send nothing */ srp_put ("\0", &bp, 1, &blen); len = FTP_BUFSIZ; if (e = radix_encode (tmp, buf, blen, &len, RADIX_ENCODE)) goto encode_error; reply_parse = "ADAT="; n = ftpcmd("ADAT",buf,-1,-1,0); } if (n == REPLY_CONTINUE) { /* Get cipher & hash lists, seqnum */ CHAR seqnum[4]; CHAR *clist; CHAR *hlist; CHAR *p1; int clist_len, hlist_len; bp = buf; if (!reply_parse) goto data_error; blen = FTP_BUFSIZ; if (e = radix_encode (reply_parse, bp, 0, &blen, RADIX_DECODE)) goto encode_error; if (srp_get (&bp, &clist, &blen, &clist_len) < 0) goto data_error; if (srp_get (&bp, &hlist, &blen, &hlist_len) < 0) goto data_error; if (srp_get (&bp, &cp, &blen, &clen) != 4) goto data_error; memcpy (seqnum, cp, 4); if (cipher_supported (clist, srp_pref_cipher)) /* Choose cipher */ cid = srp_pref_cipher; if (!cid && cipher_supported (clist, SRP_DEFAULT_CIPHER)) cid = SRP_DEFAULT_CIPHER; if (!cid) { CHAR *loclist = cipher_getlist (); for (i = 0; i < strlen (loclist); i++) if (cipher_supported (clist, loclist[i])) { cid = loclist[i]; break; } } if (!cid) { fprintf (stderr, "Unable to agree on cipher.\n"); goto bad; } /* Choose hash */ if (srp_pref_hash && hash_supported (hlist, srp_pref_hash)) hid = srp_pref_hash; if (!hid && hash_supported (hlist, SRP_DEFAULT_HASH)) hid = SRP_DEFAULT_HASH; if (!hid) { CHAR *loclist = hash_getlist (); for (i = 0; i < strlen (loclist); i++) if (hash_supported (hlist, loclist[i])) { hid = loclist[i]; break; } } if (!hid) { fprintf (stderr, "Unable to agree on hash.\n"); goto bad; } /* Set incrypt */ if (!(incrypt = krypto_new (cid, hid, skey, 20, NULL, 0, seqnum, KRYPTO_DECODE))) goto bad; /* Generate random number for outkey and outseqnum */ t_random (seqnum, 4); /* Send cid, hid, outkey, outseqnum */ bp = tmp; blen = 0; srp_put (&cid, &bp, 1, &blen); srp_put (&hid, &bp, 1, &blen); srp_put (seqnum, &bp, 4, &blen); len = FTP_BUFSIZ; if (e = radix_encode (tmp, buf, blen, &len, RADIX_ENCODE)) goto encode_error; reply_parse = "ADAT="; n = ftpcmd("ADAT",buf,-1,-1,0); /* Set outcrypt */ if (!(outcrypt = krypto_new (cid, hid, skey+20, 20, NULL, 0, seqnum, KRYPTO_ENCODE))) goto bad; t_clientclose (tc); tc = NULL; } if (n != REPLY_COMPLETE) goto bad; if (ftp_vbm) { if (ftp_deb) printf("\n"); printf ("SRP authentication succeeded.\n"); printf ("Using cipher %s and hash function %s.\n", (cipher_getdescbyid(cid))->name, (hash_getdescbyid(hid))->name ); } reply_parse = NULL; auth_type = "SRP"; return(1); encode_error: fprintf (stderr, "Base 64 encoding failed: %s.\n", radix_error (e)); goto bad; decode_error: fprintf (stderr, "Base 64 decoding failed: %s.\n", radix_error (e)); goto bad; data_error: fprintf (stderr, "Unable to unmarshal authentication data.\n"); goto bad; bad: fprintf (stderr, "SRP authentication failed, trying regular login.\n"); reply_parse = NULL; return(0); } /*--------------------------------------------------------------+ | srp_put: put item to send buffer | +--------------------------------------------------------------*/ static int srp_put (in, out, inlen, outlen) CHAR *in; CHAR **out; int inlen; int *outlen; { srp_uint32 net_len; net_len = htonl (inlen); memcpy (*out, &net_len, 4); *out += 4; *outlen += 4; memcpy (*out, in, inlen); *out += inlen; *outlen += inlen; return(0); } /*--------------------------------------------------------------+ | srp_get: get item from receive buffer | +--------------------------------------------------------------*/ static int srp_get (in, out, inlen, outlen) CHAR **in; CHAR **out; int *inlen; int *outlen; { srp_uint32 net_len; if (*inlen < 4) return -1; memcpy (&net_len, *in, 4); *inlen -= 4; *in += 4; *outlen = ntohl (net_len); if (*inlen < *outlen) return -1; *out = *in; *inlen -= *outlen; *in += *outlen; return *outlen; } /*--------------------------------------------------------------+ | srp_encode: encode control message | +--------------------------------------------------------------*/ static int srp_encode (private, in, out, len) int private; CHAR *in; CHAR *out; unsigned len; { if (private) return krypto_msg_priv (outcrypt, in, out, len); else return krypto_msg_safe (outcrypt, in, out, len); } /*--------------------------------------------------------------+ | srp_decode: decode control message | +--------------------------------------------------------------*/ static int srp_decode (private, in, out, len) int private; CHAR *in; CHAR *out; unsigned len; { if (private) return krypto_msg_priv (incrypt, in, out, len); else return krypto_msg_safe (incrypt, in, out, len); } #endif /* FTP_SRP */ #ifdef NOT_USED /* The following code is from the Unix FTP client. Be sure to make sure that the functionality is not lost. Especially the Proxy stuff even though we have not yet implemented it. */ /* Send multiple files */ static int ftp_mput(argc, argv) int argc; char **argv; { register int i; sig_t oldintr; int ointer; char *tp; sigtype mcancel(); if (argc < 2 && !another(&argc, &argv, "local-files")) { printf("usage: %s local-files\n", argv[0]); ftpcode = -1; return; } mname = argv[0]; mflag = 1; oldintr = signal(SIGINT, mcancel); /* Replace with calls to cc_execute() */ setjmp(jcancel); #ifdef FTP_PROXY if (proxy) { char *cp, *tp2, tmpbuf[CKMAXPATH]; while ((cp = remglob(argv,0)) != NULL) { if (*cp == 0) { mflag = 0; continue; } if (mflag && confirm(argv[0], cp)) { tp = cp; if (mcase) { while (*tp && !islower(*tp)) { tp++; } if (!*tp) { tp = cp; tp2 = tmpbuf; while ((*tp2 = *tp) != 0) { if (isupper(*tp2)) { *tp2 = 'a' + *tp2 - 'A'; } tp++; tp2++; } } tp = tmpbuf; } if (ntflag) { tp = dotrans(tp); } if (mapflag) { tp = domap(tp); } sendrequest((sunique) ? "STOU" : "STOR", cp, tp, 0, -1, -1, 0); if (!mflag && fromatty) { ointer = interactive; interactive = 1; if (confirm("Continue with","mput")) { mflag++; } interactive = ointer; } } } signal(SIGINT, oldintr); mflag = 0; return; } #endif /* FTP_PROXY */ for (i = 1; i < argc; i++) { register char **cpp, **gargs; if (mflag && confirm(argv[0], argv[i])) { tp = argv[i]; sendrequest((ftp_usn) ? "STOU" : "STOR", argv[i], tp, 0,-1,-1, 0); if (!mflag && fromatty) { ointer = interactive; interactive = 1; if (confirm("Continue with","mput")) { mflag++; } interactive = ointer; } } continue; gargs = ftpglob(argv[i]); if (globerr != NULL) { printf("%s\n", globerr); if (gargs) { blkfree(gargs); free((char *)gargs); } continue; } for (cpp = gargs; cpp && *cpp != NULL; cpp++) { if (mflag && confirm(argv[0], *cpp)) { tp = *cpp; sendrequest((sunique) ? "STOU":"STOR", *cpp, tp, 0, -1, -1, 0); if (!mflag && fromatty) { ointer = interactive; interactive = 1; if (confirm("Continue with","mput")) { mflag++; } interactive = ointer; } } } if (gargs != NULL) { blkfree(gargs); free((char *)gargs); } } signal(SIGINT, oldintr); mflag = 0; } /* Get multiple files */ static int ftp_mget(argc, argv) int argc; char **argv; { int rc = -1; sig_t oldintr; int ointer; char *cp, *tp, *tp2, tmpbuf[CKMAXPATH]; sigtype mcancel(); if (argc < 2 && !another(&argc, &argv, "remote-files")) { printf("usage: %s remote-files\n", argv[0]); ftpcode = -1; return(-1); } mname = argv[0]; mflag = 1; oldintr = signal(SIGINT,mcancel); /* Replace with calls to cc_execute() */ setjmp(jcancel); while ((cp = remglob(argv,proxy)) != NULL) { if (*cp == '\0') { mflag = 0; continue; } if (mflag && confirm(argv[0], cp)) { tp = cp; if (mcase) { while (*tp && !islower(*tp)) { tp++; } if (!*tp) { tp = cp; tp2 = tmpbuf; while ((*tp2 = *tp) != 0) { if (isupper(*tp2)) { *tp2 = 'a' + *tp2 - 'A'; } tp++; tp2++; } } tp = tmpbuf; } rc = (recvrequest("RETR", tp, cp, "wb", tp != cp || !interactive) == 0,0,NULL,0,0,0); if (!mflag && fromatty) { ointer = interactive; interactive = 1; if (confirm("Continue with","mget")) { mflag++; } interactive = ointer; } } } signal(SIGINT,oldintr); mflag = 0; return(rc); } /* Delete multiple files */ static int mdelete(argc, argv) int argc; char **argv; { sig_t oldintr; int ointer; char *cp; sigtype mcancel(); if (argc < 2 && !another(&argc, &argv, "remote-files")) { printf("usage: %s remote-files\n", argv[0]); ftpcode = -1; return(-1); } mname = argv[0]; mflag = 1; oldintr = signal(SIGINT, mcancel); /* Replace with calls to cc_execute() */ setjmp(jcancel); while ((cp = remglob(argv,0)) != NULL) { if (*cp == '\0') { mflag = 0; continue; } if (mflag && confirm(argv[0], cp)) { rc = (ftpcmd("DELE",cp,-1,-1,ftp_vbm) == REPLY_COMPLETE); if (!mflag && fromatty) { ointer = interactive; interactive = 1; if (confirm("Continue with", "mdelete")) { mflag++; } interactive = ointer; } } } signal(SIGINT, oldintr); mflag = 0; return(rc); } /* Get a directory listing of multiple remote files */ static int mls(argc, argv) int argc; char **argv; { sig_t oldintr; int ointer, i; char *cmd, mode[1], *dest; sigtype mcancel(); int rc = -1; if (argc < 2 && !another(&argc, &argv, "remote-files")) goto usage; if (argc < 3 && !another(&argc, &argv, "local-file")) { usage: printf("usage: %s remote-files local-file\n", argv[0]); ftpcode = -1; return(-1); } dest = argv[argc - 1]; argv[argc - 1] = NULL; if (strcmp(dest, "-") && *dest != '|') if (!globulize(&dest) || !confirm("output to local-file:", dest)) { ftpcode = -1; return(-1); } cmd = argv[0][1] == 'l' ? "NLST" : "LIST"; mname = argv[0]; mflag = 1; oldintr = signal(SIGINT, mcancel); /* Replace with calls to cc_execute() */ setjmp(jcancel); for (i = 1; mflag && i < argc-1; ++i) { *mode = (i == 1) ? 'w' : 'a'; rc = recvrequest(cmd, dest, argv[i], mode, 0,0,NULL,0,0,0); if (!mflag && fromatty) { ointer = interactive; interactive = 1; if (confirm("Continue with", argv[0])) { mflag ++; } interactive = ointer; } } signal(SIGINT, oldintr); mflag = 0; return(rc); } static char * remglob(argv,doswitch) char *argv[]; int doswitch; { char temp[16]; static char buf[CKMAXPATH]; static FILE *ftemp = NULL; static char **args; int oldhash; char *cp, *mode; if (!mflag) { if (!doglob) { args = NULL; } else { if (ftemp) { (void) fclose(ftemp); ftemp = NULL; } } return(NULL); } if (!doglob) { if (args == NULL) args = argv; if ((cp = *++args) == NULL) args = NULL; return(cp); } if (ftemp == NULL) { (void) strcpy(temp, _PATH_TMP); #ifdef MKTEMP #ifndef MKSTEMP (void) mktemp(temp); #endif /* MKSTEMP */ #endif /* MKTEMP */ verbose = 0; oldhash = hash, hash = 0; #ifdef FTP_PROXY if (doswitch) { pswitch(!proxy); } #endif /* FTP_PROXY */ for (mode = "wb"; *++argv != NULL; mode = "ab") recvrequest ("NLST", temp, *argv, mode, 0); #ifdef FTP_PROXY if (doswitch) { pswitch(!proxy); } #endif /* FTP_PROXY */ hash = oldhash; ftemp = fopen(temp, "r"); unlink(temp); if (ftemp == NULL && (!dpyactive || ftp_deb)) { printf("Can't find list of remote files, oops\n"); return(NULL); } } if (fgets(buf, CKMAXPATH, ftemp) == NULL) { fclose(ftemp), ftemp = NULL; return(NULL); } if ((cp = ckstrchr(buf,'\n')) != NULL) *cp = '\0'; return(buf); } #endif /* NOT_USED */ #endif /* TCPSOCKET (top of file) */ #endif /* SYSFTP (top of file) */ #endif /* NOFTP (top of file) */ ckcker.h0000644000015300001460000013242411355422700011301 0ustar fdckermit/* ckcker.h -- Symbol and macro definitions for C-Kermit */ /* Author: Frank da Cruz , Columbia University Academic Information Systems, New York City. Copyright (C) 1985, 2009, Trustees of Columbia University in the City of New York. All rights reserved. See the C-Kermit COPYING.TXT file or the copyright text in the ckcmai.c module for disclaimer and permissions. */ #ifndef CKCKER_H #define CKCKER_H #define I_AM_KERMIT 0 /* Personalities */ #define I_AM_TELNET 1 #define I_AM_RLOGIN 2 #define I_AM_IKSD 3 #define I_AM_FTP 4 #define I_AM_HTTP 5 #define I_AM_SSHSUB 6 #define I_AM_SSH 7 #ifndef NOSTREAMING #ifndef STREAMING #define STREAMING #endif /* STREAMING */ #endif /* NOSTREAMING */ /* If NEWDEFAULTS is defined then: - RECEIVE PACKET-LENGTH is 4095 rather than 90 - WINDOW is 30 rather than 1 - BLOCK-CHECK is 3 rather than 1 - FILE TYPE is BINARY rather than TEXT */ #ifdef BIGBUFOK /* (was OS2) */ #ifndef NEWDEFAULTS #define NEWDEFAULTS #endif /* NEWDEFAULTS */ #endif /* BIGBUFOK */ #ifdef NOICP /* No Interactive Command Parser */ #ifndef NOSPL /* implies... */ #define NOSPL /* No Script Programming Language */ #endif /* NOSPL */ #ifndef NOCSETS /* No character-set translation */ #define NOCSETS /* because the only way to set it up */ #endif /* NOCSETS */ /* is with interactive commands */ #endif /* NOICP */ #ifdef pdp11 /* There is a maximum number of */ #ifndef NOCKSPEED /* of -D's allowed on the CC */ #define NOCKSPEED /* command line, so some of them */ #endif /* NOCKSPEED */ /* have to go here... */ #ifndef NOREDIRECT #define NOREDIRECT #endif /* NOREDIRECT */ #ifdef WHATAMI #undef WHATAMI #endif /* WHATAMI */ #endif /* pdp11 */ #ifdef UIDBUFLEN #define LOGINLEN UIDBUFLEN #else #define LOGINLEN 32 /* Length of server login field */ #endif /* UIDBUFLEN */ /* Bell values */ #define XYB_NONE 0 /* No bell */ #define XYB_AUD 1 /* Audible bell */ #define XYB_VIS 2 /* Visible bell */ #define XYB_BEEP 0 /* Audible Beep */ #define XYB_SYS 4 /* Audible System Sounds */ /* File status bits */ #define FS_OK 1 /* File transferred OK */ #define FS_REFU 2 /* File was refused */ #define FS_DISC 4 /* File was discarded */ #define FS_INTR 8 /* Transfer was interrupted by user */ #define FS_ERR 16 /* Fatal error during transfer */ /* Control-character (un)prefixing options */ #define PX_ALL 0 /* Prefix all control chars */ #define PX_CAU 1 /* Unprefix cautiously */ #define PX_WIL 2 /* Unprefix with wild abandon */ #define PX_NON 3 /* Unprefix all (= prefix none) */ /* Destination codes */ #define DEST_D 0 /* DISK */ #define DEST_S 1 /* SCREEN */ #define DEST_P 2 /* PRINTER */ #define DEST_N 3 /* NOWHERE (calibration run) */ /* File transfer protocols */ #define PROTO_K 0 /* Kermit */ #ifdef CK_XYZ #define PROTO_X 1 /* XMODEM */ #define PROTO_XC 2 /* XMODEM-CRC */ #define PROTO_Y 3 /* YMODEM */ #define PROTO_G 4 /* YMODEM-g */ #define PROTO_Z 5 /* ZMODEM */ #define PROTO_O 6 /* OTHER */ #define NPROTOS 7 /* How many */ #else #define NPROTOS 1 /* How many */ #endif /* CK_XYZ */ struct ck_p { /* C-Kermit Protocol info structure */ char * p_name; /* Protocol name */ int rpktlen; /* Packet length - receive */ int spktlen; /* Packet length - send */ int spktflg; /* ... */ int winsize; /* Window size */ int prefix; /* Control-char prefixing options */ int fnca; /* Filename collision action */ int fncn; /* Filename conversion */ int fnsp; /* Send filename path stripping */ int fnrp; /* Receive filename path stripping */ char * h_b_init; /* Host receive initiation string - text */ char * h_t_init; /* Host receive initiation string - binary */ char * h_x_init; /* Host server string */ char * p_b_scmd; /* SEND cmd for external protocol - text */ char * p_t_scmd; /* SEND cmd for external protocol - binary */ char * p_b_rcmd; /* RECV cmd for external protocol - text */ char * p_t_rcmd; /* RECV cmd for external protocol - binary */ }; struct filelist { /* Send-file list element */ char * fl_name; /* Filename */ int fl_mode; /* Transfer mode */ char * fl_alias; /* Name to send the file under */ struct filelist * fl_next; /* Pointer to next element */ }; /* Kermit system IDs and associated properties... */ struct sysdata { char *sid_code; /* Kermit system ID code */ char *sid_name; /* Descriptive name */ short sid_unixlike; /* Tree-structured directory with separators */ char sid_dirsep; /* Directory separator character if unixlike */ short sid_dev; /* Can start with dev: */ short sid_case; /* Bit mapped: 1 = case matters, 2 = case preserved */ short sid_recfm; /* Text record separator */ /* 0 = unknown or nonstream 1 = cr 2 = lf 3 = crlf */ }; struct ssh_pf { /* SSH port forwarding */ int p1; /* port to be forwarded */ char * host; /* host */ int p2; /* port */ }; #define SET_ON 1 /* General values for settings that can be ON */ #define SET_OFF 0 /* OFF, */ #define SET_AUTO 2 /* or AUTO */ #define PATH_OFF 0 /* Pathnames off (to be stripped) */ #define PATH_REL 1 /* Pathnames on, left relative if possible */ #define PATH_ABS 2 /* Pathnames absolute always */ #define PATH_AUTO 4 /* Pathnames handled automatically */ /* GET Options */ #define GOPT_DEL 1 /* Delete source file */ #define GOPT_REC 2 /* Recursive */ #define GOPT_RES 4 /* Recover (Resend) */ #define GOPT_CMD 8 /* Filename is a Command */ /* GET Transfer Modes */ #define GMOD_TXT 0 /* Text */ #define GMOD_BIN 1 /* Binary */ #define GMOD_AUT 2 /* Auto */ #define GMOD_LBL 3 /* Labeled */ /* GET Filename Options */ #define GNAM_LIT 0 /* Literal */ #define GNAM_CNV 1 /* Converted */ /* GET Pathname Options */ #define GPTH_OFF 0 /* Pathnames Off */ #define GPTH_REL 1 /* Pathnames Relative */ #define GPTH_ABX 2 /* Pathnames Absolute */ #ifndef NOSPL /* The IF REMOTE-ONLY command is available only in versions that actually can be used in remote mode, and only if we have an interactive command parser. */ #define CK_IFRO #ifdef MAC #undef CK_IFRO #else #ifdef GEMDOS #undef CK_IFRO #endif /* GEMDOS */ #endif /* MAC */ #endif /* NOSPL */ /* Systems whose CONNECT modules can execute Application Program Commands */ #ifdef NOSPL /* Script programming language */ #ifdef CK_APC /* is required for APC. */ #undef CK_APC #endif /* CK_APC */ #ifndef NOAPC #define NOAPC #endif /* NOAPC */ #ifndef NOAUTODL #define NOAUTODL #endif /* NOAUTODL */ #endif /* NOSPL */ #ifndef NOAPC /* Unless they said NO APC */ #ifndef CK_APC /* And they didn't already define it */ #ifdef OS2 /* OS/2 gets it */ #define CK_APC #endif /* OS2 */ #ifdef UNIX /* UNIX gets it */ #define CK_APC #endif /* UNIX */ #ifdef VMS /* VMS too */ #define CK_APC #endif /* VMS */ #endif /* CK_APC */ #endif /* NOAPC */ #ifdef CK_APC /* APC buffer length */ #ifndef APCBUFLEN /* Should be no bigger than */ #ifdef NOSPL /* command buffer length */ #define APCBUFLEN 608 /* (see ckucmd.h) but we can't */ #else /* reference ckucmd.h symbols here */ #define APCBUFLEN 4096 #endif /* NOSPL */ #endif /* APCBUFLEN */ #define APC_OFF 0 /* APC OFF (disabled) */ #define APC_ON 1 /* APC ON (enabled for non-dangerous commands) */ #define APC_UNCH 2 /* APC UNCHECKED (enabled for ALL commands) bitmask */ #define APC_NOINP 4 /* APC (enabled with no input allowed - bitmask) */ #define APC_INACTIVE 0 /* APC not in use */ #define APC_REMOTE 1 /* APC in use from Remote */ #define APC_LOCAL 2 /* APC being used from within Kermit */ #ifndef NOAUTODL #ifndef CK_AUTODL /* Autodownload */ #ifdef OS2 #define CK_AUTODL #else #ifdef UNIX #define CK_AUTODL #else #ifdef VMS #define CK_AUTODL #else #ifdef CK_AUTODL #undef CK_AUTODL #endif /* CK_AUTODL */ #endif /* NOAUTODL */ #endif /* VMS */ #endif /* UNIX */ #endif /* OS2 */ #endif /* CK_AUTODL */ #else /* CK_APC not defined */ #ifdef NOICP #ifdef UNIX #ifndef CK_AUTODL #define CK_AUTODL #endif /* CK_AUTODL */ #endif /* UNIX */ #else /* Not NOICP... */ #ifdef CK_AUTODL #undef CK_AUTODL #endif /* CK_AUTODL */ #endif /* NOICP */ #endif /* CK_APC */ #ifdef NOAUTODL #ifdef CK_AUTODL #undef CK_AUTODL #endif /* CK_AUTODL */ #endif /* NOAUTODL */ /* Codes for what we are doing now - bit mask values */ #define W_NOTHING 0 /* Nothing */ #define W_INIT 1 /* Initializing protocol */ #define W_SEND 2 /* SENDing or MAILing */ #define W_RECV 4 /* RECEIVEing or GETting */ #define W_REMO 8 /* Doing a REMOTE command */ #define W_CONNECT 16 /* CONNECT mode */ #define W_COMMAND 32 /* Command mode */ #define W_DIALING 64 /* Dialing a modem */ #define W_FTP 128 /* FTP */ #define W_FT_DELE 64 /* FTP MDELETE */ #define W_KERMIT (W_INIT|W_SEND|W_RECV|W_REMO) /* Kermit protocol */ #define W_XFER (W_INIT|W_SEND|W_RECV|W_REMO|W_FTP) /* File xfer any protocol */ #ifndef NOWHATAMI #ifndef WHATAMI #define WHATAMI #endif /* WHATAMI */ #endif /* NOWHATAMI */ #ifdef WHATAMI /* Bit mask positions for WHATAMI */ #define WMI_SERVE 1 /* Server mode */ #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 */ /* WHATAMI2 bits... */ #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 */ #endif /* WHATAMI */ /* Terminal types */ #define VT100 0 /* Also for VT52 mode */ #define TEKTRONIX 1 /* Normal packet and window size */ #define MAXPACK 94 /* Maximum unextended packet size */ /* Can't be more than 94. */ #ifdef pdp11 /* Maximum sliding window slots */ #define MAXWS 8 #else #define MAXWS 32 /* Can't be more than 32. */ #endif /* pdp11 */ /* Maximum long packet size for sending packets */ /* Override these from cc command line via -DMAXSP=nnn */ #ifdef IRIX /* Irix 6.4 and earlier has */ #ifndef MAXSP /* Telnet server bug */ #ifdef IRIX65 #define MAXSP 9024 #else #define MAXSP 4000 #endif /* IRIX65 */ #endif /* MAXSP */ #endif /* IRIX */ #ifdef DYNAMIC #ifndef MAXSP #define MAXSP 9024 #endif /* MAXSP */ #else /* not DYNAMIC */ #ifndef MAXSP #ifdef pdp11 #define MAXSP 1024 #else #define MAXSP 2048 #endif /* pdp11 */ #endif /* MAXSP */ #endif /* DYNAMIC */ /* Maximum long packet size for receiving packets */ /* Override these from cc command line via -DMAXRP=nnn */ #ifdef DYNAMIC #ifndef MAXRP #define MAXRP 9024 #endif /* MAXRP */ #else /* not DYNAMIC */ #ifndef MAXRP #ifdef pdp11 #define MAXRP 1024 #else #define MAXRP 2048 #endif /* pdp11 */ #endif /* MAXRP */ #endif /* DYNAMIC */ /* Default sizes for windowed packet buffers. Override these from cc command line via -DSBSIZ=nnn, -DRBSIZ=nnn. Or just -DBIGBUFOK. */ #ifndef MAXGETPATH /* Maximum number of directories */ #ifdef BIGBUFOK /* for GET path... */ #define MAXGETPATH 128 #else #define MAXGETPATH 16 #endif /* BIGBUFOK */ #endif /* MAXGETPATH */ #ifndef NOSPL /* Query buffer length */ #ifdef OS2 #define QBUFL 4095 #else #ifdef BIGBUFOK #define QBUFL 4095 #else #define QBUFL 1023 #endif /* BIGBUFOK */ #endif /* OS2 */ #endif /* NOSPL */ #ifdef DYNAMIC #ifndef SBSIZ #ifdef BIGBUFOK /* If big buffers are safe... */ #define SBSIZ 290000 /* Allow for 10 x 9024 or 20 x 4096 */ #else /* Otherwise... */ #ifdef pdp11 #define SBSIZ 3020 #else #define SBSIZ 9050 /* Allow for 3 x 3000, etc. */ #endif /* pdp11 */ #endif /* BIGBUFOK */ #endif /* SBSIZ */ #ifndef RBSIZ #ifdef BIGBUFOK #define RBSIZ 290000 #else #ifdef pdp11 #define RBSIZ 3020 #else #define RBSIZ 9050 #endif /* pdp11 */ #endif /* BIGBUFOK */ #endif /* RBSIZ */ #else /* not DYNAMIC */ #ifdef pdp11 #define SBSIZ 3020 #define RBSIZ 3020 #else #ifndef SBSIZ #define SBSIZ (MAXSP * (MAXWS + 1)) #endif /* SBSIZ */ #ifndef RBSIZ #define RBSIZ (MAXRP * (MAXWS + 1)) #endif /* RBSIZ */ #endif /* pdp11 */ #endif /* DYNAMIC */ #ifdef BIGBUFOK #define PKTMSGLEN 1023 #else #define PKTMSGLEN 80 #endif /* BIGBUFOK */ /* Kermit parameters and defaults */ #define CTLQ '#' /* Control char prefix I will use */ #define MYEBQ '&' /* 8th-Bit prefix char I will use */ #define MYRPTQ '~' /* Repeat count prefix I will use */ #define MAXTRY 10 /* Times to retry a packet */ #define MYPADN 0 /* How many padding chars I need */ #define MYPADC '\0' /* Which padding character I need */ #define DMYTIM 8 /* Initial timeout interval to use. */ #define URTIME 15 /* Timeout interval to use on me. */ #define DSRVTIM 0 /* Default server cmd wait timeout. */ #define DEFTRN 0 /* Default line turnaround handshake */ #define MYEOL CR /* Incoming packet terminator. */ #ifdef NEWDEFAULTS #define DRPSIZ 4095 /* Default incoming packet size. */ #define DFWSIZ 30 /* Default window size */ #define DFBCT 3 /* Default block-check type */ #else #define DRPSIZ 90 /* Default incoming packet size. */ #define DFWSIZ 1 /* Default window size */ #define DFBCT 3 /* Default block-check type */ #endif /* NEWDEFAULTS */ /* The HP-UX 5 and 6 Telnet servers can only swallow 513 bytes at once */ #ifdef HPUX5 #ifdef DRPSIZ #undef DRPSIZ #endif /* DRPSIZ */ #define DRPSIZ 500 #else #ifdef HPUX6 #ifdef DRPSIZ #undef DRPSIZ #endif /* DRPSIZ */ #define DRPSIZ 500 #endif /* HPUX6 */ #endif /* HPUX5 */ #define DSPSIZ 90 /* Default outbound packet size. */ #define DDELAY 1 /* Default delay. */ #define DSPEED 9600 /* Default line speed. */ #ifdef OS2 /* Default CONNECT-mode */ #define DFESC 29 /* escape character */ #else #ifdef NEXT /* Ctrl-] for PC and NeXT */ #define DFESC 29 #else #ifdef GEMDOS /* And Atari ST */ #define DFESC 29 #else #define DFESC 28 /* Ctrl-backslash for others */ #endif /* GEMDOS */ #endif /* NEXT */ #endif /* OS2 */ #ifdef NOPUSH /* NOPUSH implies NOJC */ #ifndef NOJC /* (no job control) */ #define NOJC #endif /* NOJC */ #endif /* NOPUSH */ #ifdef UNIX /* Default for SET SUSPEND */ #ifdef NOJC /* UNIX but job control disabled */ #define DFSUSP 0 #else /* UNIX, job control enabled. */ #define DFSUSP 1 #endif /* NOJC */ #else #define DFSUSP 0 #endif /* UNIX */ #ifndef DFCDMSG #ifdef UNIXOROSK #define DFCDMSG "{{./.readme}{README.TXT}{READ.ME}}" #else #define DFCDMSG "{{README.TXT}{READ.ME}}" #endif /* UNIXOROSK */ #endif /* DFCDMSG */ #define NSNDEXCEPT 64 /* Max patterns for /EXCEPT: list */ /* Files */ #define ZCTERM 0 /* Console terminal */ #define ZSTDIO 1 /* Standard input/output */ #define ZIFILE 2 /* Current input file (SEND, etc) (in) */ #define ZOFILE 3 /* Current output file (RECEIVE, GET) (out) */ #define ZDFILE 4 /* Current debugging log file (out) */ #define ZTFILE 5 /* Current transaction log file (out) */ #define ZPFILE 6 /* Current packet log file (out) */ #define ZSFILE 7 /* Current session log file (out) */ #define ZSYSFN 8 /* Input/Output from a system function */ #define ZRFILE 9 /* Local file for READ (in) */ #define ZWFILE 10 /* Local file for WRITE (out) */ #define ZMFILE 11 /* Miscellaneous file, e.g. for XLATE */ #define ZDIFIL 12 /* DIAL log */ #define ZNFILS 13 /* How many defined file numbers */ #ifdef CKCHANNELIO /* File modes */ #define FM_REA 1 /* Read */ #define FM_WRI 2 /* Write */ #define FM_APP 4 /* Append */ #define FM_RWA 7 /* Read/Write/Append mask */ #define FM_BIN 8 /* Binary */ #define FM_RWB 15 /* Read/Write/Append/Binary mask */ #define FM_CMD 16 /* Command */ #define FM_EOF 64 /* (status) At EOF */ /* File errors */ #define FX_NER 0 /* No error */ #define FX_SYS -1 /* System error */ #define FX_EOF -2 /* End of file */ #define FX_NOP -3 /* Channel not open */ #define FX_CHN -4 /* Channel out of range */ #define FX_RNG -5 /* Argument range error */ #define FX_FNF -6 /* File not found */ #define FX_BFN -7 /* Bad or missing filename */ #define FX_NMF -8 /* No more files */ #define FX_FOP -9 /* Forbidden operation */ #define FX_ACC -10 /* Access denied */ #define FX_BOM -11 /* Bad combination of open modes */ #define FX_OFL -12 /* Buffer overflow */ #define FX_LNU -13 /* Current line number unknown */ #define FX_ROO -14 /* Set Root violation */ #define FX_NYI -99 /* Feature not implemented yet */ #define FX_UNK -999 /* Unknown error */ _PROTOTYP( int z_open, (char *, int) ); _PROTOTYP( int z_close, (int) ); _PROTOTYP( int z_out, (int, char *, int, int) ); _PROTOTYP( int z_in, (int, char *, int, int, int) ); _PROTOTYP( int z_flush, (int) ); _PROTOTYP( int z_seek, (int, CK_OFF_T) ); _PROTOTYP( int z_line, (int, CK_OFF_T) ); _PROTOTYP( int z_getmode, (int) ); _PROTOTYP( int z_getfnum, (int) ); _PROTOTYP( CK_OFF_T z_getpos, (int) ); _PROTOTYP( CK_OFF_T z_getline, (int) ); _PROTOTYP( CK_OFF_T z_count, (int, int) ); _PROTOTYP( char * z_getname, (int) ); _PROTOTYP( char * ckferror, (int) ); #endif /* CKCHANNELIO */ _PROTOTYP( int scanfile, (char *, int *, int) ); _PROTOTYP( int scanstring, (char *) ); /* Buffered file i/o ... */ #ifdef OS2 /* K-95 */ #define INBUFSIZE 32768 #define OBUFSIZE 32768 #else #ifdef pdp11 #define INBUFSIZE 512 #define OBUFSIZE 512 #else /* In VMS, allow for longest possible RMS record */ #ifdef VMS #define INBUFSIZE 32768 /* File input buffer size */ #define OBUFSIZE 32768 /* File output buffer size */ #else /* Not VMS */ #ifdef STRATUS #ifdef DYNAMIC #define INBUFSIZE 32767 /* File input buffer size */ #define OBUFSIZE 32767 /* File output buffer size */ #else /* STRATUS, not DYNAMIC */ #define INBUFSIZE 4096 /* File input buffer size */ #define OBUFSIZE 4096 /* File output buffer size */ #endif /* DYNAMIC */ #else /* not STRATUS */ #ifdef BIGBUFOK /* Systems where memory is */ #define INBUFSIZE 32768 /* not a problem... */ #define OBUFSIZE 32768 #else /* Not BIGBUFOK */ #define INBUFSIZE 1024 #define OBUFSIZE 1024 #endif /* BIGBUFOK */ #endif /* STRATUS */ #endif /* VMS */ #endif /* pdp11 */ #endif /* OS2 */ /* File-transfer character in/out macros for buffered i/o */ /* Get the next file byte */ #ifndef CKCMAI #ifndef NOXFER extern char ** sndarray; #endif /* NOXFER */ #endif /* CKCMAI */ #ifdef NOSPL #define zminchar() (((--zincnt)>=0) ? ((int)(*zinptr++) & 0377) : zinfill()) #else #ifdef NOXFER #define zminchar() (((--zincnt)>=0) ? ((int)(*zinptr++) & 0377) : zinfill()) #else #define zminchar() \ (sndarray?agnbyte():(((--zincnt)>=0) ? ((int)(*zinptr++) & 0377) : zinfill())) #endif /* NOXFER */ #endif /* NOSPL */ /* Stuff a character into the input buffer */ #define zmstuff(c) zinptr--, *zinptr = c, zincnt++ /* Put a character to a file */ #define zmchout(c) \ ((*zoutptr++=(char)(c)),(((++zoutcnt)>=zobufsize)?zoutdump():0)) /* Screen functions */ #define XYFD_N 0 /* File transfer display: None, Off */ #define XYFD_R 1 /* Regular, Dots */ #define XYFD_C 2 /* Cursor-positioning (e.g. curses) */ #define XYFD_S 3 /* CRT Screen */ #define XYFD_B 4 /* Brief */ #define XYFD_G 5 /* GUI */ #ifdef NODISPLAY #define xxscreen(a,b,c,d) #define ckscreen(a,b,c,d) #else _PROTOTYP( VOID ckscreen, (int, char, CK_OFF_T, char *) ); #ifdef VMS #define xxscreen(a,b,c,d) \ if (local && fdispla != XYFD_N) \ ckscreen((int)a,(char)b,(CK_OFF_T)c,(char *)d) #else #define xxscreen(a,b,c,d) \ if (local && !backgrd && fdispla != XYFD_N) \ ckscreen((int)a,(char)b,(CK_OFF_T)c,(char *)d) #endif /* VMS */ #endif /* NODISPLAY */ #define SCR_FN 1 /* filename */ #define SCR_AN 2 /* as-name */ #define SCR_FS 3 /* file-size */ #define SCR_XD 4 /* x-packet data */ #define SCR_ST 5 /* File status: */ #define ST_OK 0 /* Transferred OK */ #define ST_DISC 1 /* Discarded */ #define ST_INT 2 /* Interrupted */ #define ST_SKIP 3 /* Skipped */ #define ST_ERR 4 /* Fatal Error */ #define ST_REFU 5 /* Refused (use Attribute codes for reason) */ #define ST_INC 6 /* Incompletely received */ #define ST_MSG 7 /* Informational message */ #define ST_SIM 8 /* Transfer simulated (e.g. would be sent) */ #define SCR_PN 6 /* packet number */ #define SCR_PT 7 /* packet type or pseudotype */ #define SCR_TC 8 /* transaction complete */ #define SCR_EM 9 /* error message */ #define SCR_WM 10 /* warning message */ #define SCR_TU 11 /* arbitrary undelimited text */ #define SCR_TN 12 /* arbitrary new text, delimited at beginning */ #define SCR_TZ 13 /* arbitrary text, delimited at end */ #define SCR_QE 14 /* quantity equals (e.g. "foo: 7") */ #define SCR_CW 15 /* close screen window */ #define SCR_CD 16 /* display current directory */ #define SCR_MS 17 /* message from client */ /* Skip reasons */ #define SKP_DAT 1 /* Date-Time (Older) */ #define SKP_EQU 2 /* Date-Time (Equal) */ #define SKP_TYP 3 /* Type */ #define SKP_SIZ 4 /* Size */ #define SKP_NAM 5 /* Name collision */ #define SKP_EXL 6 /* Exception list */ #define SKP_DOT 7 /* Dot file */ #define SKP_BKU 8 /* Backup file */ #define SKP_RES 9 /* Recovery not needed */ #define SKP_ACC 10 /* Access denied */ #define SKP_NRF 11 /* Not a regular file */ #define SKP_SIM 12 /* Simulation (WOULD BE SENT) */ #define SKP_XUP 13 /* Simulation: Would be sent because remote file older */ #define SKP_XNX 14 /* Simulation: ditto, because remote file does not exist */ /* Macros */ #ifndef CKCMAI extern int tcp_incoming; /* Used by ENABLE macro */ #endif /* CKCMAI */ #ifndef TCPSOCKET /* ENABLED tells whether a server-side service is enabled. 0 = disabled, 1 = local, 2 = remote. A "set host *" connection is technically local but logically remote */ #define ENABLED(x) ((local && (x & 1)) || (!local && (x & 2))) #else #define ENABLED(x) (((local && !tcp_incoming) && (x & 1)) || \ ((!local || tcp_incoming) && (x&2))) #endif /* TCPSOCKET */ /* These are from the book */ #define tochar(ch) (((ch) + SP ) & 0xFF ) /* Number to character */ #define xunchar(ch) (((ch) - SP ) & 0xFF ) /* Character to number */ #define ctl(ch) (((ch) ^ 64 ) & 0xFF ) /* Control/Uncontrol toggle */ #define unpar(ch) (((ch) & 127) & 0xFF ) /* Clear parity bit */ #ifndef NOLOCAL /* CONNECT return status codes */ /* Users will see the numbers so they can't be changed */ /* Numbers >= 100 indicate connection loss */ #define CSX_NONE 0 /* No CONNECT yet so no status */ #define CSX_ESCAPE 1 /* User Escaped back */ #define CSX_TRIGGER 2 /* Trigger was encountered */ #define CSX_IKSD 3 /* IKSD autosynchronization */ #define CSX_APC 4 /* Application Program Command */ #define CSX_IDLE 5 /* Idle limit exceeded */ #define CSX_TN_ERR 6 /* Telnet Error */ #define CSX_MACRO 7 /* Macro bound to keystroke */ #define CSX_TIME 8 /* Time Limit exceeded */ #define CSX_INTERNAL 100 /* Internal error */ #define CSX_CARRIER 101 /* Carrier required but not detected */ #define CSX_IOERROR 102 /* I/O error on connection */ #define CSX_HOSTDISC 103 /* Disconnected by host */ #define CSX_USERDISC 104 /* Disconnected by user */ #define CSX_SESSION 105 /* Session Limit exceeded */ #define CSX_TN_POL 106 /* Rejected due to Telnet Policy */ #define CSX_KILL_SIG 107 /* Received Kill Signal */ /* SET TERMINAL IDLE-ACTION values */ #define IDLE_RET 0 /* Return to prompt */ #define IDLE_EXIT 1 /* Exit from Kermit */ #define IDLE_HANG 2 /* Hangup the connection */ #define IDLE_OUT 3 /* OUTPUT a string */ #define IDLE_TNOP 4 /* TELNET NOP */ #define IDLE_TAYT 5 /* TELNET AYT */ #endif /* NOLOCAL */ /* Modem and dialing definitions */ #ifndef NODIAL /* Modem capabilities (bit values) */ #define CKD_AT 1 /* Hayes AT commands and responses */ #define CKD_V25 2 /* V.25bis commands and responses */ #define CKD_SB 4 /* Speed buffering */ #define CKD_EC 8 /* Error correction */ #define CKD_DC 16 /* Data compression */ #define CKD_HW 32 /* Hardware flow control */ #define CKD_SW 64 /* (Local) software flow control */ #define CKD_KS 128 /* Kermit spoofing */ #define CKD_TB 256 /* Made by Telebit */ #define CKD_ID 512 /* Has Caller ID */ /* DIAL command result codes */ #define DIA_UNK -1 /* No DIAL command given yet */ #define DIA_OK 0 /* DIAL succeeded */ #define DIA_NOMO 1 /* Modem type not specified */ #define DIA_NOLI 2 /* Communication line not spec'd */ #define DIA_OPEN 3 /* Line can't be opened */ #define DIA_NOSP 4 /* Speed not specified */ #define DIA_HANG 5 /* Hangup failure */ #define DIA_IE 6 /* Internal error (malloc, etc) */ #define DIA_IO 7 /* I/O error */ #define DIA_TIMO 8 /* Dial timeout expired */ #define DIA_INTR 9 /* Dialing interrupted by user */ #define DIA_NRDY 10 /* Modem not ready */ #define DIA_PART 11 /* Partial dial command OK */ #define DIA_DIR 12 /* Dialing directory error */ #define DIA_HUP 13 /* Modem was hung up OK */ #define DIA_NRSP 19 /* No response from modem */ #define DIA_ERR 20 /* Modem command error */ #define DIA_NOIN 21 /* Failure to initialize modem */ #define DIA_BUSY 22 /* Phone busy */ #define DIA_NOCA 23 /* No carrier */ #define DIA_NODT 24 /* No dialtone */ #define DIA_RING 25 /* Ring, incoming call */ #define DIA_NOAN 26 /* No answer */ #define DIA_DISC 27 /* Disconnected */ #define DIA_VOIC 28 /* Answered by voice */ #define DIA_NOAC 29 /* Access denied, forbidden call */ #define DIA_BLCK 30 /* Blacklisted */ #define DIA_DELA 31 /* Delayed */ #define DIA_FAX 32 /* Fax */ #define DIA_DIGI 33 /* Digital Line */ #define DIA_TAPI 34 /* TAPI dialing failure */ #define DIA_UERR 98 /* Unknown error */ #define DIA_UNSP 99 /* Unspecified failure detected by modem */ #define MDMINF struct mdminf MDMINF { /* Structure for modem-specific information */ char * name; /* Descriptive name */ char * pulse; /* Command to force pulse dialing */ char * tone; /* Command to force tone dialing */ int dial_time; /* Time modem allows for dialing (secs) */ char * pause_chars; /* Character(s) to tell modem to pause */ int pause_time; /* Time associated with pause chars (secs) */ char * wake_str; /* String to wakeup modem & put in cmd mode */ int wake_rate; /* Delay between wake_str characters (msecs) */ char * wake_prompt; /* String prompt after wake_str */ char * dmode_str; /* String to put modem in dialing mode */ char * dmode_prompt; /* String prompt for dialing mode */ char * dial_str; /* Dialing string, with "%s" for number */ int dial_rate; /* Interchar delay to modem (msec) */ int esc_time; /* Escape sequence guard time (msec) */ int esc_char; /* Escape character */ char * hup_str; /* Hangup string */ char * hwfc_str; /* Hardware flow control string */ char * swfc_str; /* Software flow control string */ char * nofc_str; /* No flow control string */ char * ec_on_str; /* Error correction on string */ char * ec_off_str; /* Error correction off string */ char * dc_on_str; /* Data compression on string */ char * dc_off_str; /* Data compression off string */ char * aa_on_str; /* Autoanswer on string */ char * aa_off_str; /* Autoanswer off string */ char * sb_on_str; /* Speed buffering on string */ char * sb_off_str; /* Speed buffering off string */ char * sp_on_str; /* Speaker on string */ char * sp_off_str; /* Speaker off string */ char * vol1_str; /* Volume low string */ char * vol2_str; /* Volume med string */ char * vol3_str; /* Volume high string */ char * ignoredt; /* Ignore dialtone string */ char * ini2; /* Last-minute init string */ long max_speed; /* Maximum interface speed */ long capas; /* Capability bits */ /* function to read modem's response string to a non-dialing command */ _PROTOTYP( int (*ok_fn), (int,int) ); }; #endif /* NODIAL */ /* Symbols for File Attributes */ #define AT_XALL 0 /* All of them */ #define AT_ALLY 1 /* All of them on (Yes) */ #define AT_ALLN 2 /* All of them off (no) */ #define AT_LENK 3 /* Length in K */ #define AT_FTYP 4 /* File Type */ #define AT_DATE 5 /* Creation date */ #define AT_CREA 6 /* Creator */ #define AT_ACCT 7 /* Account */ #define AT_AREA 8 /* Area */ #define AT_PSWD 9 /* Password for area */ #define AT_BLKS 10 /* Blocksize */ #define AT_ACCE 11 /* Access */ #define AT_ENCO 12 /* Encoding */ #define AT_DISP 13 /* Disposition */ #define AT_LPRO 14 /* Local Protection */ #define AT_GPRO 15 /* Generic Protection */ #define AT_SYSI 16 /* System ID */ #define AT_RECF 17 /* Record Format */ #define AT_SYSP 18 /* System-Dependent Parameters */ #define AT_LENB 19 /* Length in Bytes */ #define AT_EOA 20 /* End of Attributes */ /* Kermit packet information structure */ struct pktinfo { /* Packet information structure */ CHAR *bf_adr; /* buffer address */ int bf_len; /* buffer length */ CHAR *pk_adr; /* Packet address within buffer */ int pk_len; /* length of data within buffer */ int pk_typ; /* packet type */ int pk_seq; /* packet sequence number */ int pk_rtr; /* retransmission count */ }; /* Send Modes (indicating which type of SEND command was used) */ #define SM_SEND 0 #define SM_MSEND 1 #define SM_RESEND 2 #define SM_PSEND 3 #define SM_MAIL 4 #define SM_PRINT 5 #define OPTBUFLEN 256 /* File-related symbols and structures */ /* Used by SET FILE command but also by protocol and i/o modules */ #define XMODE_A 0 /* Transfer mode Automatic */ #define XMODE_M 1 /* Transfer mode Manual */ #define XYFILN 0 /* Naming */ #define XYFN_L 0 /* Literal */ #define XYFN_C 1 /* Converted */ #define XYFILT 1 /* Type */ #define XYFT_T 0 /* Text */ #define XYFT_B 1 /* Binary */ #define XYFT_I 2 /* Image or Block (VMS) */ #define XYFT_L 3 /* Labeled (tagged binary) (VMS or OS/2) */ #define XYFT_U 4 /* Binary Undefined (VMS) */ #define XYFT_M 5 /* MacBinary (Macintosh) */ #define XYFT_X 6 /* TENEX (FTP TYPE L 8) */ #define XYFT_D 99 /* Debug (for session logs) */ #define XYFILW 2 /* Warning */ #define XYFILD 3 /* Display */ #define XYFILC 4 /* Character set */ #define XYFILF 5 /* Record Format */ #define XYFF_S 0 /* Stream */ #define XYFF_V 1 /* Variable */ #define XYFF_VB 2 /* Variable with RCW's */ #define XYFF_F 3 /* Fixed length */ #define XYFF_U 4 /* Undefined */ #define XYFILR 6 /* Record length */ #define XYFILO 7 /* Organization */ #define XYFO_S 0 /* Sequential */ #define XYFO_I 1 /* Indexed */ #define XYFO_R 2 /* Relative */ #define XYFILP 8 /* Printer carriage control */ #define XYFP_N 0 /* Newline (imbedded control characters) */ #define XYFP_F 1 /* FORTRAN (space, 1, +, etc, in column 1 */ #define XYFP_P 2 /* Special printer carriage controls */ #define XYFP_X 4 /* None */ #define XYFILX 9 /* Collision Action */ #define XYFX_A 3 /* Append */ #define XYFX_Q 5 /* Ask */ #define XYFX_B 2 /* Backup */ #define XYFX_D 4 /* Discard */ #define XYFX_R 0 /* Rename */ #define XYFX_X 1 /* Replace */ #define XYFX_U 6 /* Update */ #define XYFX_M 7 /* Modtimes differ */ #define XYFILB 10 /* Blocksize */ #define XYFILZ 11 /* Disposition */ #define XYFZ_N 0 /* New, Create */ #define XYFZ_A 1 /* New, append if file exists, else create */ #define XYFZ_O 2 /* Old, file must exist */ #define XYFILS 12 /* File Byte Size */ #define XYFILL 13 /* File Label (VMS) */ #define XYFILI 14 /* File Incomplete */ #define XYFILQ 15 /* File path action (strip or not) */ #define XYFILG 16 /* File download directory */ #define XYFILA 17 /* Line terminator for local text files */ #define XYFA_L 012 /* LF (as in UNIX) */ #define XYFA_C 015 /* CR (as in OS-9 or Mac OS) */ #define XYFA_2 000 /* CRLF -- Note: this must be defined as 0 */ #define XYFILY 18 /* Destination */ #define XYFILV 19 /* EOF Detection Method */ #define XYEOF_L 0 /* File length */ #define XYEOF_Z 1 /* Ctrl-Z in file */ #define XYFILH 20 /* OUTPUT parameters - buffered, blocking, etc */ #define XYFIBP 21 /* BINARY-PATTERN */ #define XYFITP 22 /* TEXT-PATTERN */ #define XYFIPA 23 /* PATTERNS ON/OFF */ #define XYFILU 24 /* UCS ... */ #define XYF_PRM 25 /* PERMISSIONS, PROTECTION */ #define XYF_INSP 26 /* INSPECTION (SCAN) */ #define XYF_DFLT 27 /* DEFAULT (character sets) */ #define XYF_SSPA 28 /* STRINGSPACE */ #define XYF_LSIZ 29 /* LISTSIZE */ /* File Type (return code) definitions and corresponding name strings */ #define FT_7BIT 0 /* 7-bit text */ #define FT_8BIT 1 /* 8-bit text */ #define FT_UTF8 2 /* UTF8 */ #define FT_UCS2 3 /* UCS2 */ #define FT_TEXT 4 /* Unknown text */ #define FT_BIN 5 /* Binary */ #define SCANFILEBUF 49152 /* Size of file scan (48K) */ /* Connection closed reasons */ #define WC_REMO 0 /* Closed by remote */ #define WC_CLOS 1 /* Closed from our end */ #define WC_TELOPT 2 /* Telnet negotiation failure */ #ifdef BIGBUFOK #define FTPATTERNS 256 #else #define FTPATTERNS 64 #endif /* BIGBUFOK */ #define SYS_UNK 0 /* Selected server system types */ #define SYS_UNIX 1 #define SYS_WIN32 2 #define SYS_VMS 3 #define SYS_OS2 4 #define SYS_DOS 5 #define SYS_TOPS10 6 #define SYS_TOPS20 7 #define SYS_VOS 8 #define SYS_DG 9 #define SYS_OSK 10 #define SYS_MAX 11 #ifdef CK_SMALL #define PWBUFL 63 #else #define PWBUFL 255 #endif /* CK_SMALL */ #ifdef OS2 struct tt_info_rec { /* Terminal emulation info */ char *x_name; char *x_aliases[4]; char *x_id; }; #endif /* OS2 */ /* BEEP TYPES */ #define BP_BEL 0 /* Terminal bell */ #define BP_NOTE 1 /* Info */ #define BP_WARN 2 /* Warning */ #define BP_FAIL 3 /* Error */ #ifndef NOIKSD #ifdef IKSDB /* IKSD Database definitions */ /* Field values */ #define DBF_INUSE 1 /* Flag bits... In use */ #define DBF_USER 2 /* Real user (versus anonymous) */ #define DBF_LOGGED 4 /* Logged in (versus not) */ /* Data Definitions... */ /* Numeric fields, hex, right justified, 0-filled on left */ #define db_FLAGS 0 /* Field 0: Flags */ #define DB_FLAGS 0 /* Offset: 0 */ #define dB_FLAGS 4 /* Length: 4 (hex digits) */ #define db_ATYPE 1 /* Field 1: Authentication type */ #define DB_ATYPE 4 /* 4 hex digits */ #define dB_ATYPE 4 #define db_AMODE 2 /* Field 2: Authentication mode */ #define DB_AMODE 8 /* 4 hex digits */ #define dB_AMODE 4 #define db_STATE 3 /* Field 3: State - 4 hex digits*/ #define DB_STATE 12 /* 4 hex digits */ #define dB_STATE 4 #define db_MYPID 4 /* Field 4: My PID */ #define DB_MYPID 16 /* 16 hex digits left padded with 0 */ #define dB_MYPID 16 #define db_SADDR 5 /* Field 5: Server (my) IP address */ #define DB_SADDR 32 /* 16 hex digits left padded with 0 */ #define dB_SADDR 16 #define db_CADDR 6 /* Field 6: Client IP address */ #define DB_CADDR 48 /* 16 hex digits left padded with 0 */ #define dB_CADDR 16 /* Date-time fields (17 right-adjusted in 18 for Y10K readiness) */ #define db_START 7 /* Field 7: Session start date-time */ #define DB_START 65 /* 64 is leading space for Y10K */ #define dB_START 17 #define db_LASTU 8 /* Field 8: Last lastu date-time */ #define DB_LASTU 83 /* 82 is leading space for Y10K */ #define dB_LASTU 17 #define db_ULEN 9 /* Field 9: Length of Username */ #define DB_ULEN 100 /* 4 hex digits */ #define dB_ULEN 4 #define db_DLEN 10 /* Field 10: Length of Directory */ #define DB_DLEN 104 /* 4 hex digits */ #define dB_DLEN 4 #define db_ILEN 11 /* Field 11: Length of Info */ #define DB_ILEN 108 /* 4 hex digits */ #define dB_ILEN 4 #define db_PAD1 12 /* Field 12: (Reserved) */ #define DB_PAD1 112 /* filled with spaces */ #define dB_PAD1 912 /* String fields, all right-padded with blanks */ #define db_USER 13 /* Field 13: Username */ #define DB_USER 1024 /* right-padded with spaces */ #define dB_USER 1024 #define db_DIR 14 /* Field 14: Current directory */ #define DB_DIR 2048 /* right-padded with spaces */ #define dB_DIR 1024 #define db_INFO 15 /* Field 15: State-specific info */ #define DB_INFO 3072 /* right-padded with spaces */ #define dB_INFO 1024 #define DB_RECL 4096 /* Database record length */ /* Offset, length, and type of each field thru its db_XXX symbol */ #define DBT_HEX 1 /* Hexadecimal number */ #define DBT_STR 2 /* String */ #define DBT_DAT 3 /* Date-Time yyyymmdd hh:mm:ss */ #define DBT_UND 9 /* Undefined and blank */ struct iksdbfld { int off; /* Position (offset) */ int len; /* Length (bytes) */ int typ; /* Data type */ }; _PROTOTYP(int dbinit, (void)); _PROTOTYP(int initslot, (int)); _PROTOTYP(int getslot, (void)); _PROTOTYP(int freeslot, (int)); _PROTOTYP(int updslot, (int)); _PROTOTYP(int slotstate, (int, char *, char *, char *)); _PROTOTYP(int slotdir, (char *, char *)); #endif /* IKSDB */ #endif /* NOIKSD */ /* ANSI forward declarations for protocol-related functions. */ _PROTOTYP( int input, (void) ); _PROTOTYP( int inibufs, (int, int) ); _PROTOTYP( int makebuf, (int, int, CHAR [], struct pktinfo *) ); _PROTOTYP( int mksbuf, (int) ); _PROTOTYP( int mkrbuf, (int) ); _PROTOTYP( int spack, (char, int, int, CHAR *) ); _PROTOTYP( VOID proto, (void) ); _PROTOTYP( int rpack, (void) ); _PROTOTYP( int ack, (void) ); _PROTOTYP( int nack, (int) ); _PROTOTYP( int ackn, (int) ); _PROTOTYP( int ack1, (CHAR *) ); _PROTOTYP( int ackns, (int, CHAR *) ); #ifdef STREAMING _PROTOTYP( int fastack, (void) ); #endif /* STREAMING */ _PROTOTYP( int resend, (int) ); _PROTOTYP( int errpkt, (CHAR *) ); _PROTOTYP( VOID logpkt, (char, int, CHAR *, int) ); _PROTOTYP( CHAR dopar, (CHAR) ); _PROTOTYP( int chk1, (CHAR *, int) ); _PROTOTYP( unsigned int chk2, (CHAR *, int) ); _PROTOTYP( unsigned int chk3, (CHAR *, int) ); _PROTOTYP( int sipkt, (char) ); _PROTOTYP( int sopkt, (void) ); _PROTOTYP( int sinit, (void) ); _PROTOTYP( VOID rinit, (CHAR *) ); _PROTOTYP( int spar, (CHAR *) ); _PROTOTYP( int rcvfil, (char *) ); _PROTOTYP( CHAR * rpar, (void) ); _PROTOTYP( int gnfile, (void) ); _PROTOTYP( int getsbuf, (int) ); _PROTOTYP( int getrbuf, (void) ); _PROTOTYP( int freesbuf, (int) ); _PROTOTYP( int freerbuf, (int) ); _PROTOTYP( int dumpsbuf, (void) ); _PROTOTYP( int dumprbuf, (void) ); _PROTOTYP( VOID freerpkt, (int) ); _PROTOTYP( int chkwin, (int, int, int) ); _PROTOTYP( int rsattr, (CHAR *) ); _PROTOTYP( char *getreason, (char *) ); _PROTOTYP( int scmd, (char, CHAR *) ); _PROTOTYP( int encstr, (CHAR *) ); _PROTOTYP( int decode, (CHAR *, int (*)(char), int) ); _PROTOTYP( int bdecode, (CHAR *, int (*)(char)) ); _PROTOTYP( int fnparse, (char *) ); _PROTOTYP( int syscmd, (char *, char *) ); _PROTOTYP( int cwd, (char *) ); _PROTOTYP( int remset, (char *) ); _PROTOTYP( int initattr, (struct zattr *) ); _PROTOTYP( int gattr, (CHAR *, struct zattr *) ); _PROTOTYP( int adebu, (char *, struct zattr *) ); _PROTOTYP( int canned, (CHAR *) ); _PROTOTYP( int opent, (struct zattr *) ); _PROTOTYP( int ckopenx, (struct zattr *) ); _PROTOTYP( int opena, (char *, struct zattr *) ); _PROTOTYP( int openi, (char *) ); _PROTOTYP( int openo, (char *, struct zattr *, struct filinfo *) ); _PROTOTYP( int openc, (int, char *) ); _PROTOTYP( int reof, (char *, struct zattr *) ); _PROTOTYP( VOID reot, (void) ); _PROTOTYP( int sfile, (int) ); _PROTOTYP( int sattr, (int, int) ); _PROTOTYP( int sdata, (void) ); _PROTOTYP( int seof, (int) ); _PROTOTYP( int sxeof, (int) ); _PROTOTYP( int seot, (void) ); _PROTOTYP( int window, (int) ); _PROTOTYP( int clsif, (void) ); _PROTOTYP( int clsof, (int) ); _PROTOTYP( CHAR setgen, (char, char *, char *, char *) ); _PROTOTYP( int getpkt, (int, int) ); _PROTOTYP( int maxdata, (void) ); _PROTOTYP( int putsrv, (char) ); _PROTOTYP( int puttrm, (char) ); _PROTOTYP( int putque, (char) ); _PROTOTYP( int putfil, (char) ); _PROTOTYP( int putmfil, (char) ); _PROTOTYP( int zputfil, (char) ); _PROTOTYP( VOID zdstuff, (CHAR) ); _PROTOTYP( int tinit, (int) ); _PROTOTYP( VOID pktinit, (void) ); _PROTOTYP( VOID resetc, (void) ); _PROTOTYP( VOID xsinit, (void) ); _PROTOTYP( int adjpkl, (int,int,int) ); _PROTOTYP( int chktimo, (int,int) ); _PROTOTYP( int nxtpkt, (void) ); _PROTOTYP( VOID rcalcpsz, (void) ); _PROTOTYP( int srinit, (int, int, int) ); _PROTOTYP( VOID tstats, (void) ); _PROTOTYP( VOID fstats, (void) ); _PROTOTYP( VOID intmsg, (long) ); _PROTOTYP( VOID ermsg, (char *) ); _PROTOTYP( int chkint, (void) ); _PROTOTYP( VOID sdebu, (int) ); _PROTOTYP( VOID rdebu, (CHAR *, int) ); _PROTOTYP( char * dbchr, ( int ) ); #ifdef COMMENT _PROTOTYP( SIGTYP stptrap, (int, int) ); _PROTOTYP( SIGTYP trap, (int, int) ); #else _PROTOTYP( SIGTYP stptrap, (int) ); _PROTOTYP( SIGTYP trap, (int) ); #endif /* COMMENT */ _PROTOTYP( char * ck_errstr, (void) ); #ifndef NOXFER _PROTOTYP( int agnbyte, (void) ); #endif /* NOXFER */ _PROTOTYP( int xgnbyte, (int, int, int (*)(void)) ); _PROTOTYP( int xpnbyte, (int, int, int, int (*)(char)) ); /* User interface functions needed by main program, etc. */ _PROTOTYP( int doconect, (int,int) ); _PROTOTYP( VOID setflow, (void) ); _PROTOTYP( VOID prescan, (int) ); _PROTOTYP( VOID setint, (void) ); _PROTOTYP( VOID doinit, (void) ); _PROTOTYP( VOID dofast, (void) ); _PROTOTYP( VOID cmdini, (void) ); _PROTOTYP( int dotake, (char *) ); _PROTOTYP( int cmdlin, (void) ); #ifdef OS2 _PROTOTYP( int conect, (int) ); #else /* OS2 */ _PROTOTYP( int conect, (void) ); #endif /* OS2 */ _PROTOTYP( int ckcgetc, (int) ); _PROTOTYP( int ckcputc, (int) ); _PROTOTYP (int mdmhup, (void) ); _PROTOTYP( VOID herald, (void) ); _PROTOTYP( VOID fixcmd, (void) ); _PROTOTYP( int doarg, (char) ); _PROTOTYP( int doxarg, (char **, int) ); _PROTOTYP( VOID usage, (void) ); _PROTOTYP( VOID doclean, (int) ); _PROTOTYP( int sndhlp, (void) ); _PROTOTYP( int sndstring, (char *) ); _PROTOTYP( VOID ckhost, (char *, int) ); _PROTOTYP( int gettcs, (int, int) ); _PROTOTYP( VOID getdialenv, (void) ); _PROTOTYP( VOID setprefix, (int) ); _PROTOTYP(VOID initproto,(int,char *,char *,char *,char *,char *,char*,char*)); _PROTOTYP( VOID initpat, (void) ); _PROTOTYP( VOID initcsets, (void) ); _PROTOTYP( char * getsysid, (char *) ); _PROTOTYP( int getsysix, (char *) ); #ifdef CK_TIMERS _PROTOTYP( VOID rttinit, (void) ); _PROTOTYP( int getrtt, (int, int) ); #endif /* CK_TIMERS */ _PROTOTYP( int is_a_tty, (int) ); _PROTOTYP( int snddir, (char *) ); _PROTOTYP( int snddel, (char *) ); _PROTOTYP( int sndtype, (char *) ); _PROTOTYP( int dooutput, (char *, int) ); _PROTOTYP( int isabsolute, (char *) ); _PROTOTYP( VOID whoarewe, (void) ); _PROTOTYP( int ckmkdir, (int, char *, char **, int, int) ); _PROTOTYP( int autoexitchk, (CHAR) ); _PROTOTYP( VOID fcps, (void) ); #ifdef OS2 _PROTOTYP( VOID logchar, (unsigned short) ); #else /* OS2 */ _PROTOTYP( VOID logchar, (char) ); #endif /* OS2 */ _PROTOTYP( VOID logstr, (char *, int) ); _PROTOTYP( VOID dologend, (void) ); #ifdef NOLOCAL #define dologshow() #else _PROTOTYP( long dologshow, (int) ); #endif /* NOLOCAL */ #ifdef NODISPLAY #define fxdinit(a) #else _PROTOTYP( VOID fxdinit, (int) ); #endif /* NODISPLAY */ _PROTOTYP( int fileselect, (char *, char *, char *, char *, char *, CK_OFF_T, CK_OFF_T, int, int, char **) ); _PROTOTYP( char * whoami, (void) ); _PROTOTYP( int shoesc, (int) ); #ifdef CK_APC _PROTOTYP( int chkspkt, (char *) ); _PROTOTYP( int kstart, (CHAR) ); _PROTOTYP( VOID autodown, (int)); #ifdef CK_XYZ _PROTOTYP( int zstart, (CHAR) ); #endif /* CK_XYZ */ #ifdef OS2 _PROTOTYP(void apc_command, (int, char*)); #endif /* OS2 */ #endif /* CK_APC */ /* User Query data structures and functions */ struct txtbox { char * t_buf; /* Destination buffer address */ int t_len; /* Destination buffer length */ char * t_lbl; /* Label for this field */ char * t_dflt; /* Default response for this field */ int t_echo; /* 0 = no, 1 = yes, 2 = asterisks */ }; #define DEFAULT_UQ_TIMEOUT 0 _PROTOTYP(int uq_ok, (char *,char *,int,char **,int) ); _PROTOTYP(int uq_txt, (char *,char *,int,char **,char *,int,char *,int)); _PROTOTYP(int uq_mtxt, (char *,char **,int,struct txtbox[]) ); _PROTOTYP(int uq_file, (char *,char *,int,char **,char *,char *,int)); #ifdef CK_URL struct urlopt { char * nam; char * val; }; #define MAX_URL_OPTS 16 struct urldata { char * sav; /* The URL itself */ char * svc; /* Service */ char * usr; /* User */ char * psw; /* Password */ char * hos; /* Host */ char * por; /* Port */ char * pth; /* Path */ int nopts; /* number of options */ struct urlopt opt[MAX_URL_OPTS]; /* options */ }; _PROTOTYP(int urlparse, (char *, struct urldata *)); #endif /* CK_URL */ #endif /* CKCKER_H */ /* End of ckcker.h */ ckclib.c0000644000015300001460000026366511602625175011303 0ustar fdckermitchar * cklibv = "C-Kermit library, 9.0.052, 29 Jun 2011"; #define CKCLIB_C /* C K C L I B . C -- C-Kermit Library routines. */ /* Author: Frank da Cruz , Columbia University Academic Information Systems, New York City. Copyright (C) 1999, 2011, Trustees of Columbia University in the City of New York. All rights reserved. See the C-Kermit COPYING.TXT file or the copyright text in the ckcmai.c module for disclaimer and permissions. */ /* General-purpose, system/platform/compiler-independent routines for use by all modules. Many are replacements for commonly used C library functions that are not found on every platform, and/or that lack needed functionality (e.g. caseless string search/compare) or safety features. ckstrncpy() - Similar to strncpy() but different (see comments). ckstrncat() - Similar to strncat() but different (see comments). chartostr() - Converts a char to a string (self or ctrl char name). ckstrchr() - Portable strchr(). ckstrpbrk() - Portable strpbrk(). cklower() - Lowercase a string (in place). ckupper() - Uppercase a string (in place). ckindex() - Left or right index. ckstrstr() - Portable strstr(). ckitoa() - Converts int to string. ckuitoa() - Converts unsigned int to string. ckltoa() - Converts long to string. ckultoa() - Converts unsigned long to string. ckfstoa() - Converts off_t-type integer (long or long long) to string. ckatofs() - Converts a numeric string to an off_t-type integer. ckctoa() - Converts char to string. ckmakmsg() - Constructs a message from 4 source strings. ckmakxmsg() - Constructs a message from 12 source strings. ckmatch() - Pattern matching. ckmemcpy() - Portable memcpy(). ckrchar() - Rightmost character of a string. ckstrcmp() - Possibly caseless string comparison. ckstrpre() - Caseless string prefix comparison. sh_sort() - Sorts an array of strings, many options. brstrip() - Strips enclosing braces (and doublequotes). makelist() - Splits "{{item}{item}...}" into an array. makestr() - Careful malloc() front end. xmakestr() - ditto (see comments). ckradix() - Convert number radix (2-36). b8tob64() - Convert data to base 64. b64tob8() - Convert base 64 to data. chknum() - Checks if string is a (possibly signed) integer. rdigits() - Checks if string is composed only of decimal digits. isfloat() - Checks if string is a valid floating-point number. ckround() - Rounds a floating-point number to desired precision. parnam() - Returns parity name string. hhmmss() - Converts seconds to hh:mm:ss string. lset() - Write fixed-length field left-adjusted into a record. rset() - Write fixed-length field right-adjusted into a record. ulongtohex() - Converts an unsigned long to a hex string. hextoulong() - Converts a hex string to an unsigned long. cksplit() - Splits a string into an array of words. ispattern() - Tells if argument string is a pattern. Prototypes are in ckclib.h. Note: This module should not contain any extern declarations. */ #include "ckcsym.h" #include "ckcdeb.h" #include "ckcasc.h" /* Public variables */ int dblquo = 1; /* Nonzero if doublequotes can be used for grouping */ char * ccntab[] = { /* Names of ASCII (C0) control characters 0-31 */ "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL", "BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI", "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB", "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US" }; char * c1tab[] = { /* Names of ISO 6429 (C1) control characters 0-32 */ "XXX", "XXX", "BPH", "NBH", "IND", "NEL", "SSA", "ESA", "HTS", "HTJ", "VTS", "PLD", "PLU", "RI", "SS2", "SS3", "DCS", "PU1", "PU2", "STS", "CCH", "MW", "SPA", "EPA", "SOS", "XXX", "SCI", "CSI", "ST", "OSC", "PM", "APC", "NBS" }; #define RXRESULT 127 static char rxdigits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; static char rxresult[RXRESULT+1]; /* C K S T R N C P Y */ /* Copies a NUL-terminated string into a buffer whose total length is given, ensuring that the result is NUL-terminated even if it has to be truncated. Call with: dest = pointer to destination buffer src = pointer to source string len = length of destination buffer (the actual length, not one less). Returns: int, The number of bytes copied, 0 or more. NOTE: This is NOT a replacement for strncpy(): . strncpy() does not require its source string to be NUL-terminated. . strncpy() does not necessarily NUL-terminate its result. . strncpy() right-pads dest with NULs if it is longer than src. . strncpy() treats the length argument as the number of bytes to copy. . ckstrncpy() treats the length argument as the size of the dest buffer. . ckstrncpy() doesn't dump core if given NULL string pointers. . ckstrncpy() returns a number. Use ckstrncpy() when you want to: . Copy an entire string into a buffer without overrun. . Get the length of the string back. Use strncpy() when you want to: . Copy a piece of a string. */ int #ifdef CK_ANSIC ckstrncpy(char * dest, const char * src, int len) #else ckstrncpy(dest,src,len) char * dest, * src; int len; #endif /* CK_ANSIC */ { int i; if (len < 1 || !src || !dest) { /* Nothing or nowhere to copy */ if (dest) *dest = NUL; return(0); } #ifndef NOCKSTRNCPY for (i = 0; src[i] && (i < len-1); i++) /* Args OK, copy */ dest[i] = src[i]; dest[i] = NUL; #else i = strlen(src); if (i > len) i = len; strncpy(dest,src,i); dest[len] = NUL; #endif /* NOCKSTRNCPY */ return(i); } /* C K S T R N C A T */ /* Appends a NUL-terminated string to a buffer whose total length is given, ensuring that the result is NUL-terminated even if it had to be truncated. Call with: dest = pointer to destination buffer containing a null-terminated string src = pointer to null-terminated source string len = length of destination buffer (the actual length, not one less). Returns: int, The number of bytes copied, 0 or more. */ int #ifdef CK_ANSIC ckstrncat(char * dest, const char * src, int len) #else ckstrncat(dest,src,len) char * dest, * src; int len; #endif /* CK_ANSIC */ { register int i, j; #ifdef NOCKSTRNCPY register char * s1, * s2; #endif /* NOCKSTRNCPY */ if (len < 1 || !src || !dest) { /* Nothing or nowhere to copy */ if (dest) *dest = NUL; return(0); } #ifndef NOCKSTRNCPY /* Args OK, copy */ for (i = 0, j = strlen(dest); src[i] && (i < len-j-1); i++) dest[i+j] = src[i]; dest[i+j] = NUL; #else j = 0; s1 = dest; while (*s1++) j++; /* j = strlen(dest); */ s1--; /* (back up over NUL) */ i = 0; s2 = (char *)src; while (*s2++) i++; /* i = strlen(src); */ if (i > (len-j)) i = len - j; if (i <= 0) return(0); #ifdef COMMENT strncpy(&dest[j],src,i); #else j = i; /* This should be a bit faster... */ s2 = (char *)src; /* depends on strcpy implementation; */ while ((*s1++ = *s2++) && j--) /* at least it shouldn't be slower. */ ; dest[len-1] = NUL; /* In case of early exit. */ #endif /* COMMENT */ #endif /* NOCKSTRNCPY */ return(i); } /* C K M A K M S G */ /* Constructs a message from up to 4 pieces with length checking. Result is always NUL terminated. Call with: buf: Pointer to buffer for constructing message. len: Length of buffer. s1-s4: String pointers (can be NULL). Returns: 0: Nothing was copied. n: (positive number) n bytes copied, all args copied successfully. -n: n bytes were copied, destination buffer not big enough for all. Also see: ckmakxmsg() -- accepts 12 string args. ckitoa(), ckltoa(), ckctoa(), ckitox(), etc. Use ckmak[x]msg() plus ck?to?() as a safe replacement for sprintf(). */ int #ifdef CK_ANSIC ckmakmsg(char * buf, int len, char *s1, char *s2, char *s3, char *s4) #else /* CK_ANSIC */ ckmakmsg(buf,len,s1,s2,s3,s4) char *buf, *s1, *s2, *s3, *s4; int len; #endif /* CK_ANSIC */ { int i, n = 0, m = 0; char *s; char *p, *a[4]; if (!buf) return(n); /* No destination */ if (len < 1) return(n); /* No size */ s = buf; /* Point to destination */ a[0] = s1; a[1] = s2; a[2] = s3; a[3] = s4; /* Array of source strings */ for (i = 0; i < 4; i++) { /* Loop thru array */ p = a[i]; /* Point to this element */ if (p) { /* If pointer not null */ n = ckstrncpy(s,p,len); /* Copy safely */ m += n; /* Accumulate total */ if (p[n]) /* Didn't get whole thing? */ return(-m); /* return indicating buffer full */ len -= n; /* Deduct from space left */ s += n; /* Otherwise advance dest pointer */ } } return(m); /* Return total bytes copied */ } /* C K M A K X M S G */ /* Exactly like ckmakmsg(), but accepts 12 string arguments. */ int #ifdef CK_ANSIC ckmakxmsg(char * buf, int len, char *s1, char *s2, char *s3, char *s4, char *s5, char *s6, char *s7, char *s8, char *s9, char *s10, char *s11, char *s12) #else /* CK_ANSIC */ ckmakxmsg(buf,len,s1,s2,s3,s4,s5,s6,s7,s8,s9,s10,s11,s12) char *buf, *s1, *s2, *s3, *s4, *s5, *s6, *s7, *s8, *s9, *s10, *s11, *s12; int len; #endif /* CK_ANSIC */ { int i, n = 0, m = 0; char *s; char *p, *a[12]; if (!buf) return(n); /* No destination */ if (len < 1) return(n); /* No size */ s = buf; /* Point to destination */ a[0] = s1; a[1] = s2; a[2] = s3; a[3] = s4; /* Source-string array */ a[4] = s5; a[5] = s6; a[6] = s7; a[7] = s8; a[8] = s9; a[9] = s10; a[10] = s11; a[11] = s12; for (i = 0; i < 12; i++) { /* Loop thru array */ p = a[i]; /* Point to this element */ if (p) { /* If pointer not null */ n = ckstrncpy(s,p,len); /* Copy safely */ m += n; /* Accumulate total */ if (p[n]) /* Didn't get whole thing? */ return(-m); /* return indicating buffer full */ len -= n; /* Deduct from space left */ s += n; /* Otherwise advance dest pointer */ } } return(m); /* Return total bytes copied */ } /* C H A R T O S T R */ /* Converts a character to a string, interpreting controls. */ char * chartostr(x) int x; { /* Call with char x */ static char buf[2]; /* Returns string pointer. */ if (x < 32) return(ccntab[x]); if (x == 127) return("DEL"); if (x > 127 && x < 161) return(c1tab[x - 128]); if (x == 0xAD) return("SHY"); buf[1] = NUL; buf[0] = (unsigned)(x & 0xff); return((char *)buf); } /* C K R C H A R */ /* Returns the rightmost character of the given null-terminated string */ int ckrchar(s) char * s; { register CHAR c = '\0', *p; p = (CHAR *)s; if (!p) p = (CHAR *)""; /* Null pointer == empty string */ if (!*p) return(0); while (*p) /* Crawl to end of string */ c = *p++; return((unsigned)(c & 0xff)); /* Return final character */ } /* C K S T R C H R */ /* Replacement for strchr(), which is not universal. */ /* Call with: s = pointer to string to look in. c = character to look for. Returns: NULL if c not found in s or upon any kind of error, or: pointer to first occurrence of c in s, searching from left to right. */ char * #ifdef CK_ANSIC ckstrchr(char * s, char c) #else ckstrchr(s,c) char *s, c; #endif /* CK_ANSIC */ /* ckstrchr */ { if (!s) return(NULL); while (*s && *s != c) s++; return((*s == c) ? s : NULL); } /* C K S T R R C H R */ /* Replacement for strrchr(), which is not universal. */ /* Call with: s = pointer to string to look in. c = character to look for. Returns: NULL if c not found in s or upon any kind of error, or: pointer to first occurrence of c in s, searching from right to left. */ char * #ifdef CK_ANSIC ckstrrchr(char * s, char c) #else ckstrrchr(s,c) char *s, c; #endif /* CK_ANSIC */ /* ckstrchr */ { char * s2 = NULL; if (!s) return(NULL); while (*s) { if (*s == c) s2 = s; s++; } return(s2); } /* C K S T R P B R K -- Portable replacement for strpbrk() */ /* Returns pointer to first char in s1 that is also in s2, or NULL */ char * ckstrpbrk(s1, s2) char * s1, * s2; { char c1, c2, * s3; if (!s1 || !s2) return(NULL); if (!*s1 || !*s2) return(NULL); while ((c1 = *s1++)) { s3 = s2; while ((c2 = *s3++)) { if (c2 == c1) return(s1-1); } } return(NULL); } /* C K L O W E R -- Lowercase a string IN PLACE */ /* Returns the length of the string */ int cklower(s) char *s; { int n = 0; if (!s) return(0); while (*s) { if (isupper(*s)) *s = (char) tolower(*s); s++, n++; } return(n); } /* C K U P P E R -- Uppercase a string IN PLACE */ /* Returns the length of the string */ int ckupper(s) char *s; { int n = 0; if (!s) return(0); while (*s) { if (islower(*s)) *s = (char) toupper(*s); s++, n++; } return(n); } /* C K L T O A -- Long to string -- FOR DISCIPLINED USE ONLY */ #define NUMBUF 1024 static char numbuf[NUMBUF+32] = { NUL, NUL }; static int numbp = 0; /* ckltoa() and ckitoa() are like atol() and atoi() in the reverse direction, returning a pointer to the string representation of the given number without the caller having to worry about allocating or defining a buffer first. They manage their own internal buffer, so successive calls return different pointers. However, to keep memory consumption from growing without bound, the buffer recycles itself. So after several hundred calls (depending on the size of the numbers), some of the earlier pointers might well find themselves referencing something different. Moral: You can't win in C. Therefore, these routines are intended mainly for generating numeric strings for short-term use, e.g. for passing numbers in string form as parameters to functions. For long-term use, the result must be copied to a safe place. */ char * #ifdef CK_ANSIC ckltoa(long n) #else ckltoa(n) long n; #endif /* CK_ANSIC */ /* ckltoa */ { char buf[32]; /* Internal working buffer */ char * p, * s, * q; int k, x, len = 0, sign = 0; if (n < 0L) { /* Sign */ n = 0L - n; sign = 1; } buf[31] = NUL; for (k = 30; k > 0; k--) { /* Convert number to string */ x = n % 10L; buf[k] = x + '0'; n = n / 10L; if (!n) break; } if (sign) buf[--k] = '-'; /* Add sign if necessary */ len = 31 - k; if (len + numbp > NUMBUF) numbp = 0; p = numbuf + numbp; q = p; s = buf + k; while ((*p++ = *s++)) ; /* Copy */ *p++ = NUL; numbp += len+1; return(q); /* Return pointer */ } /* C K U L T O A -- Unsigned long to string */ char * #ifdef CK_ANSIC ckultoa(unsigned long n) #else ckultoa(n) unsigned long n; #endif /* CK_ANSIC */ /* ckultoa */ { char buf[32]; /* Internal working buffer */ char * p, * s, * q; int k, x, len = 0; buf[31] = NUL; for (k = 30; k > 0; k--) { /* Convert number to string */ x = n % 10L; buf[k] = x + '0'; n = n / 10L; if (!n) break; } len = 31 - k; if (len + numbp > NUMBUF) numbp = 0; p = numbuf + numbp; q = p; s = buf + k; while ((*p++ = *s++)) ; /* Copy */ numbp += len+1; return(q); /* Return pointer */ } char * #ifdef CK_ANSIC ckltox(long n) /* Long int to "0x.." hex string */ #else ckltox(n) long n; #endif /* CK_ANSIC */ /* ckltox */ { char buf[32]; /* Internal working buffer */ char *p, *q, *s, *bp = buf + 2; int k; buf[0] = '0'; buf[1] = 'x'; sprintf(bp, "%lx", n); k = strlen(bp); if (k&1) { sprintf(bp, "0%lx", n); k++; } k += 2; /* "0x" */ if (numbp + k >= NUMBUF) numbp = 0; p = numbuf + numbp; q = p; s = buf; while ((*p++ = *s++)) ; /* Copy */ *p++ = NUL; numbp += k+1; return(q); /* Return pointer */ } /* C K F S T O A -- File Size (or offset) to string */ /* This is just like ckltoa() except for the data type of the argument. */ /* It's mainly for printing file sizes without having to know their data */ /* type, so we don't have to hardware "%ld" or "%lld" into printf()s. */ /* Works for 32 or 64 bits, according to CK_OFF_T definition. */ char * #ifdef CK_ANSIC ckfstoa(CK_OFF_T n) #else ckfstoa(n) CK_OFF_T n; #endif /* CK_ANSIC */ /* ckfstoa */ { char buf[32]; /* Internal working buffer */ char * p, * s, * q; int k, x, len = 0, sign = 0; if (n < (CK_OFF_T)0) { /* Sign */ n = (CK_OFF_T)0 - n; sign = 1; } buf[31] = NUL; /* 2^63-1 is about 20 decimal digits */ for (k = 30; k > 0; k--) { /* Convert number to string */ x = n % (CK_OFF_T)10; if (x < 0) { /* x += 10; */ ckstrncpy(&buf[23],"OVERFLOW",32); sign = 0; k = 23; break; } buf[k] = x + '0'; n = n / (CK_OFF_T)10; if (!n) break; } if (sign) buf[--k] = '-'; /* Add sign if necessary */ len = 31 - k; if (len + numbp > NUMBUF) numbp = 0; p = numbuf + numbp; q = p; s = buf + k; while ((*p++ = *s++)) ; /* Copy */ *p++ = NUL; numbp += len+1; return(q); /* Return pointer */ } /* C K A T O F S -- String to File Size (or offset) */ /* This is the inverse of ckfstoa(), a replacement for atol() that works */ /* for either 32-bit or 64-bit arguments, according to CK_OFF_T definition. */ /* Like atol(), there is no error indication. */ CK_OFF_T #ifdef CK_ANSIC ckatofs(char * s) #else ckatofs(s) char * s; #endif /* CK_ANSIC */ /* ckatofs */ { CK_OFF_T result = (CK_OFF_T)0; int minus = 0; while (*s && (*s == SP || *s == HT)) s++; if (*s == '+') s++; if (*s == '-') { minus = 1; s++; } while (isdigit(*s)) { result = (result * (CK_OFF_T)10) + (CK_OFF_T)(*s - '0'); s++; } return(minus ? -result : result); } /* C K I T O A -- Int to string -- FOR DISCIPLINED USE ONLY */ char * ckitoa(n) int n; { /* See comments with ckltoa(). */ long nn; nn = n; return(ckltoa(nn)); } char * /* Unsigned int to string */ ckuitoa(n) unsigned int n; { unsigned long nn; nn = n; return(ckultoa(nn)); } char * ckitox(n) int n; { /* Int to hex */ long nn; nn = n; return(ckltox(nn)); } char * #ifdef CK_ANSIC ckctoa(char c) /* Char to string */ #else ckctoa(c) char c; #endif /* ckctoa */ { static char buf[32]; static int current = 0; if (current >= 30) current = 0; buf[current++] = c; buf[current++] = '\0'; return((char *)(buf + current - 2)); } char * #ifdef CK_ANSIC ckctox(CHAR c, int flag) /* Unsigned char to hex */ #else ckctox(c, flag) CHAR c; int flag; #endif /* ckctox */ { static char buf[48]; static int current = 0; int x; char h; if (current > 45) current = 0; x = (c >> 4) & 0x0f; h = rxdigits[x]; if (!flag && isupper(rxdigits[x])) h = tolower(rxdigits[x]); buf[current++] = h; x = c & 0x0f; h = rxdigits[x]; if (!flag && isupper(rxdigits[x])) h = tolower(rxdigits[x]); buf[current++] = h; buf[current++] = '\0'; return((char *)(buf + current - 3)); } /* C K I N D E X -- C-Kermit's index function */ /* We can't depend on C libraries to have one, so here is our own. Call with: s1 - String to look for. s2 - String to look in. t - Offset from right or left of s2, 0 based; -1 for rightmost char in s2. r - 0 for left-to-right search, non-0 for right-to-left. icase 0 for case independence, non-0 if alphabetic case matters. Returns 0 if string not found, otherwise a 1-based result. Also returns 0 on any kind of error, e.g. junk parameters. */ int ckindex(s1,s2,t,r,icase) char *s1, *s2; int t, r, icase; { int len1 = 0, len2 = 0, i, j, x, ot = t; /* ot = original t */ char * s; if (!s1 || !s2) return(0); s = s1; while (*s++) len1++; /* length of string to look for */ s = s2; while (*s++) len2++; /* length of string to look in */ s = s2; if (t < 0) t = len2 - 1; j = len2 - len1; /* length difference */ if (j < 0 || (r == 0 && t > j)) /* search string is longer */ return(0); if (r == 0) { /* Index */ s = s2 + t; /* Point to beginning of target */ for (i = 0; i <= (j - t); i++) { /* Now compare */ x = ckstrcmp(s1,s++,len1,icase); if (!x) return(i+1+t); } } else { /* Reverse Index */ i = len2 - len1; /* Where to start looking */ if (ot > 0) /* Figure in offset if any */ i -= t; for (j = i; j > -1; j--) { if (!ckstrcmp(s1,&s2[j],len1,icase)) return(j+1); } } return(0); } /* C K S T R S T R -- Portable replacement for strstr() */ /* Returns pointer to first occurrence of s1 in s2, or NULL */ char * ckstrstr(s1, s2) char * s1, * s2; { int k; k = ckindex(s2,s1,0,0,1); return((k < 1) ? NULL : &s1[k-1]); } /* B R S T R I P -- Strip enclosing braces from arg string, in place. */ /* Call with: Pointer to string that can be poked. Returns: Pointer to string without enclosing braces. If original string was not braced, this is the arg pointer; otherwise it is 1 + the arg pointer, with the matching closing brace zero'd out. If the string starts with a brace but does not end with a matching brace, the original pointer to the original string is returned. If the arg pointer is NULL, a pointer to an empty string is returned. */ #ifdef COMMENT /* This is the original version, handling only braces */ char * brstrip(p) char *p; { if (!p) return(""); if (*p == '{') { int x; x = (int)strlen(p) - 1; if (p[x] == '}') { p[x] = NUL; p++; } } return(p); } #else /* New version handles braces and doublequotes */ /* WARNING: this function writes into its argument, it always has. */ char * brstrip(p) char *p; { if (!p) return(""); if (*p == '{' || (*p == '"' && dblquo)) { int x; x = (int)strlen(p) - 1; if (x > 0) { if ((*p == '{' && p[x] == '}') || (*p == '"' && p[x] == '"')) { if (x > 0 && p[x-1] != CMDQ) { p[x] = NUL; p++; } } } } return(p); } #endif /* COMMENT */ #ifdef COMMENT /* Even newer experimental version -- breaks many things */ char * fnstrip(p) char *p; { int i, j, k, n, len; extern int cmd_quoting; /* Bad - no externs allowed! */ if (!p) return(""); if (*p == '{') { len = strlen(p); n = 0; for (j = 0; j < len; j++ ) { if (p[j] == '{' && (!cmd_quoting || j == 0 || p[j-1] != CMDQ)) { for (n = 1, i = j+1; i < len; i++ ) { if (p[i] == '{' && (!cmd_quoting || p[i-1] != CMDQ)) n++; else if (p[i] == '}' && (!cmd_quoting || p[i-1] != CMDQ)) { if (--n == 0) { for (k = j; k < i - 1; k++) p[k] = p[k+1]; for (; i < len; i++ ) p[i-1] = p[i+1]; len -= 2; j = i - 1; } } } } } if (n == 1) { /* Implied right brace at end of field */ for (k = j; k < len; k++) p[k] = p[k+1]; len -= 1; } } else if (*p == '"') { len = strlen(p); n = 0; for (j = 0; j < len; j++) { if (p[j] == '"' && (!cmd_quoting || j == 0 || p[j-1] != CMDQ)) { n++; for (i = j + 1; i < len; i++) { if (p[i] == '"' && (!cmd_quoting || p[i-1] != CMDQ)) { n--; for (k = j; k < i - 1; k++) p[k] = p[k+1]; for (; i < len; i++) p[i-1] = p[i+1]; len -= 2; j = i - 1; } } } } if (n == 1) { /* Implied double quote at end of field */ for (k = j; k < len; k++ ) p[k] = p[k+1]; len -= 1; } } return(p); } #endif /* COMMENT */ #ifdef COMMENT /* Not used -- Note: these not only write into their arg, but write past past the end. */ char * brace(fn) char *fn; { int spaces = 0; char * p, ch, ch2; for (p = fn; *p; p++) { if (*p == SP) { spaces = 1; break; } } if (spaces) { p = fn; ch = *p; *p = '{'; p++; while (*p) { ch2 = *p; *p = ch; ch = ch2; p++; } *p = ch; p++; *p = '}'; p++; *p = '\0'; } return(fn); } #endif /* COMMENT */ /* d q u o t e -- Puts doublequotes around arg in place. */ /* Call with: Pointer to buffer and its total length and flag = 0 to use doublequotes, 1 to use braces. Returns: Number: length of result. */ int dquote(fn, len, flag) char *fn; int len; int flag; { int spaces = 0, k = 0; char * p, ch, ch2; if (!fn) return(0); k = strlen(fn); for (p = fn; *p; p++) { if (*p == SP) { spaces = 1; break; } } if (spaces) { if (k + 2 >= len) return(k); p = fn; ch = *p; *p = flag ? '{' : '"'; p++; while (*p) { ch2 = *p; *p = ch; ch = ch2; p++; } *p = ch; p++; *p = flag ? '}' : '"'; p++; *p = '\0'; } return(k+2); } /* U N T A B I F Y --- Untabify s1 into s2, assuming tabs every 8 space */ int untabify(s1,s2,max) char * s1, * s2; int max; { int i, j, k, x, z; x = strlen(s1); for (i = 0, k = 0; k < x; k++) { if (s1[k] != '\t') { if (i >= max-1) { s2[max-1] = '\0'; return(-1); } s2[i++] = s1[k]; continue; } z = 8 - i%8; if (z == 0) z = 8; for (j = 0; j < z && i < max; j++) s2[i++] = ' '; } s2[i] = '\0'; return(0); } /* M A K E L I S T --- Breaks {{s1}{s2}..{sn}} into an array of strings */ /* Call with: s = pointer to string to break up. list = array of string pointers. len = number of elements in array. NOTE: The array must be preinitialized to all NULL pointers. If any array element is not NULL, it is assumed to have been malloc'd and is therefore freed. Do NOT call this function with an uninitialized array, or with an array that has had any static elements assigned to it. */ VOID makelist(s,list,len) char * s; char *list[]; int len; { int i, n, q, bc = 0; char *p = NULL, *s2 = NULL; debug(F110,"makelist s",s,0); if (!s) { /* Check for null or empty string */ list[0] = NULL; return; } n = strlen(s); if (n == 0) { list[0] = NULL; return; } if ((s2 = (char *)malloc(n+1))) { /* Safe copy for poking */ strcpy(s2,s); /* (no need for ckstrncpy here) */ s = s2; } s = brstrip(s); /* Strip braces */ n = strlen(s); /* Get length */ if (*s != '{') { /* Outer braces only */ if ((p = (char *)malloc(n+1))) { /* So just one pattern */ strcpy(p,s); /* (no need for ckstrncpy here) */ if (list[0]) free(list[0]); list[0] = p; } if (s2) free(s2); return; } q = 0; /* Inner ones too */ i = 0; /* so a list of patterns. */ n = 0; while (*s && i < len) { if (*s == CMDQ) { /* Quote... */ q = 1; s++; n++; continue; } if (*s == '{' && !q) { /* Opening brace */ if (bc++ == 0) { /* Beginning of a group */ p = ++s; n = 0; } else { /* It's a brace inside the group */ n++; s++; } continue; } else if (*s == '}' && !q) { /* Closing brace */ if (--bc == 0) { /* End of a group */ *s++ = NUL; debug(F111,"makelist element",p,i); if (list[i]) free(list[i]); if ((list[i] = (char *)malloc(n+1))) { ckstrncpy(list[i],p,n+1); /* Note: n+1 */ i++; } while (*s == SP) s++; p = s; n = 0; continue; } else { /* Within a group */ n++; s++; } } else { /* Regular character */ q = 0; s++; n++; } } if (*p && i < len) { /* Last one */ if (list[i]) free(list[i]); if ((list[i] = (char *)malloc(n+1))) { ckstrncpy(list[i],p,n+1); debug(F111,"makelist last element",p,i); } } i++; /* Clear out the rest of the list */ for ( ; i < len; i++) { if (list[i]) free (list[i]); list[i] = NULL; } if (s2) free(s2); } /* M A K E S T R -- Creates a dynamically allocated string. Makes a new copy of string s and sets pointer p to its address. Handles degenerate cases, like when buffers overlap or are the same, one or both arguments are NULL, etc. The source string is assumed to be NUL-terminated. Therefore it can not be a UCS-2 string or arbitrary binary data. The target pointer must be either NULL or else a pointer to a previously malloc'ed buffer. If not, expect a core dump or segmentation fault. Note: The caller can tell whether this routine failed as follows: malloc(&p,q); if (q & !p) { makestr() failed }; Really this routine should have returned a length, but since it doesn't we set the global variable makestrlen to the length of the result string. */ int makestrlen = 0; VOID #ifdef CK_ANSIC makestr(char **p, const char *s) #else makestr(p,s) char **p, *s; #endif /* makestr */ { int x = 0; char *q = NULL; #ifdef CK_ANSIC register const char * s2; #else register char * s2; #endif /* CK_ANSIC */ register char * q2; if (*p == s) /* The two pointers are the same. */ return; /* Don't do anything. */ if (!s) { /* New definition is null? */ if (*p) /* Free old storage. */ free(*p); *p = NULL; /* Return null pointer. */ makestrlen = 0; return; } s2 = s; /* Maybe new string will fit */ #ifdef COMMENT /* This is a fairly big win, allowing us to skip the malloc() and free if the destination string already exists and is not shorter than the source string. But it doesn't allow for possible overlap of source and destination. */ if (*p) { /* into old storage... */ char * p2 = *p; char c; while (c = *p2) { if (!(*p2++ = *s2++)) break; x++; } makestrlen = x; if (c) return; } #endif /* COMMENT */ /* Didn't fit */ x = 0; while (*s2++) x++; /* Get (rest of) length of s. */ if (x >= 0) { /* Get length, even of empty string. */ q = malloc(x+1); /* Get and point to temp storage. */ if (q) { makestrlen = x; /* Remember length for stats */ s2 = s; /* Point back to beginning of source */ q2 = q; /* Copy dest pointer to increment... */ while ((*q2++ = *s2++)) ; /* Instead of calling strcpy(). */ /* Note: HP flexelint says that the above loop can result in creation (++) and access (*) of out-of-bounds pointers. I really don't see it. */ } #ifdef DEBUG else { /* This would be a really bad error */ char tmp[24]; /* So get a good record of it. */ if (x > 23) { ckstrncpy(tmp,s,20); strcpy(tmp+20,"..."); tmp[23] = NUL; } else { strcpy(tmp,s); /* We already checked the length */ } debug(F110,"MAKESTR MALLOC FAILURE ",tmp,0); } #endif /* DEBUG */ } else q = NULL; /* Length of string is zero */ if (*p) /* Now free the original storage. */ free(*p); *p = q; } /* X M A K E S T R -- Non-destructive makestr() if s is NULL. */ VOID #ifdef CK_ANSIC xmakestr(char **p, const char *s) #else xmakestr(p,s) char **p, *s; #endif /* xmakestr */ { if (s) makestr(p,s); } #ifndef USE_MEMCPY /* C K M E M C P Y -- Portable (but slow) memcpy() */ /* Copies n bytes from s to p, allowing for overlap. */ /* For use when real memcpy() not available. */ VOID ckmemcpy(p,s,n) char *p, *s; int n; { char * q = NULL; register int i; int x; if (!s || !p || n <= 0 || p == s) /* Verify args */ return; x = p - s; /* Check for overlap */ if (x < 0) x = 0 - x; if (x < n) { /* They overlap */ q = p; if (!(p = (char *)malloc(n))) /* So use a temporary buffer */ return; } for (i = 0; i < n; i++) /* Copy n bytes */ p[i] = s[i]; if (q) { /* If we used a temporary buffer */ for (i = 0; i < n; i++) /* copy from it to destination */ q[i] = p[i]; if (p) free(p); /* and free the temporary buffer */ } } #endif /* USE_MEMCPY */ /* C K S T R C M P -- String comparison with case-matters selection */ /* Call with pointers to the two strings, s1 and s2, a length, n, and c == 0 for caseless comparison, nonzero for case matters. Call with n == -1 to compare without a length limit. Compares up to n characters of the two strings and returns: 1 if s1 > s2 0 if s1 = s2 -1 if s1 < s2 Note: case handling is only as good as isupper() and tolower(). */ int ckstrcmp(s1,s2,n,c) char *s1, *s2; register int n, c; { register CHAR t1, t2; if (n == 0) return(0); if (!s1) s1 = ""; /* Watch out for null pointers. */ if (!s2) s2 = ""; if (!*s1) return(*s2 ? -1 : 0); if (!*s2) return(1); while (n--) { t1 = (CHAR) *s1++; /* Get next character from each. */ t2 = (CHAR) *s2++; if (!t1) return(t2 ? -1 : 0); if (!t2) return(1); if (!c) { /* If case doesn't matter */ if (isupper(t1)) t1 = tolower(t1); /* Convert case. */ if (isupper(t2)) t2 = tolower(t2); } if (t1 < t2) return(-1); /* s1 < s2 */ if (t1 > t2) return(1); /* s1 > s2 */ } return(0); /* They're equal */ } /* C K S T R P R E -- Caseless string prefix comparison */ /* Returns position of the first char in the 2 strings that doesn't match */ int ckstrpre(s1,s2) char *s1, *s2; { CHAR t1, t2; int n = 0; if (!s1) s1 = ""; if (!s2) s2 = ""; while (1) { t1 = (CHAR) *s1++; t2 = (CHAR) *s2++; if (!t1 || !t2) return(n); if (isupper(t1)) t1 = tolower(t1); if (isupper(t2)) t2 = tolower(t2); if (t1 != t2) return(n); n++; } } #define GLOBBING /* C K M A T C H -- Match a string against a pattern */ /* Call with: pattern to be matched. string to look for the pattern in. icase is 1 if case-sensitive, 0 otherwise. opts is a bitmask: Bit 0 (=1): 1 = Match strings starting with '.' 0 = Don't match them (used with UNIX filenames). Bit 1 (=2): 1 = File globbing (dirseps are fences); 0 = Dirseps are not fences. Bit 2 (=4): 1 = Allow ^ and $ anchors at beginning and end of pattern. 0 = Don't allow them (normal case for filename matching). Bit 3 (and beyond): Undefined. Works only with NUL-terminated strings. Pattern may contain any number of ? and/or *. If CKREGEX is defined, also [abc], [a-z], and/or {string,string,...}. (Note: REGEX is a misnomer, see below.) Returns: 0 if string does not match pattern, >= 1, the 1-based position in the string where the match was found. To be done: Find a way to identify the piece of the string that matched the pattern, as in Snobol "LINE (PAT . RESULT)". This is now partially done by setting matchpos and matchend (except matchend needs some tuning). But these are useless unless a copy of the string is kept, or a copy of the matching part is made. But that would be too costly in performance -- this routine has to be fast because it's used for wildcard expansion. Note: Patterns are not the same as regular expressions, in which '*' means 0 or more repetitions of the preceding item. For example "a*b" as a pattern matches any string that starts with 'a' and ends with 'b'; as a regular expression it matches any string of zero or more a's followed by one b. Regular expressions are especially useful in matching strings of (say) digits, or letters, e.g. "[0-9]*" matches any string of digits. So far, Kermit doesn't do this. */ static char * mypat = NULL; /* For rewriting pattern */ static int matchpos = 0; int matchend = 0; static int matchdepth = 0; static int stringpos = 0; static char * ostring = NULL; #define MATCHRETURN(x,y) { rc=y; where=x; goto xckmatch; } static char * lastpat = NULL; static int xxflag = 0; /* Global bailout flag for ckmatch() */ int ispattern(s) char * s; { int quote = 0, sbflag = 0, sb = 0, cbflag = 0, cb = 0; char c = 0; if (*s == '^') return(1); while ((c = *s++)) { if (quote) { quote = 0; continue; } if (c == '\\') { quote = 1; continue; } if (c == '*') return(1); if (c == '?') return(1); /* Unquoted brackets or braces must match */ if (c == '[') { sbflag++; sb++; continue; } if (c == ']') { sb--; continue; } if (c == '{') { cbflag++; cb++; continue; } if (c == '}') { cb--; continue; } if (!*s && c == '$') return(1); } return(sbflag || cbflag); } int ckmatch(pattern, string, icase, opts) char *pattern,*string; int icase, opts; { int q = 0, i = 0, k = -1, x, flag = 0; int rc = 0; /* Return code */ int havestar = 0; int where = -1; CHAR cp; /* Current character from pattern */ CHAR cs; /* Current character from string */ char * patstart; /* Start of pattern */ int plen, dot, globbing, xstar = 0; int bronly = 0; /* Whole pattern is {a,b,c,...} */ debug(F111,"CKMATCH ENTRY pat opt",pattern,opts); debug(F111,"CKMATCH ENTRY str dep",string,matchdepth); /* debug(F101,"CKMATCH ENTRY icase","",icase); */ globbing = opts & 2; if (!string) string = ""; if (!pattern) pattern = ""; if (!*pattern) { /* Empty pattern matches anything */ matchdepth++; /* (it wasn't incremented yet) */ MATCHRETURN(0,1); } else if (!*string) { MATCHRETURN(0,0); } patstart = pattern; /* Remember beginning of pattern */ if (matchdepth == 0) { /* Top-level call? */ xxflag = 0; stringpos = 0; /* Reset indices etc. */ matchpos = 0; matchend = 0; ostring = string; lastpat = pattern; if (*pattern == '{') /* Entire pattern is {a,b.c} */ bronly = 1; /* Maybe */ dot = (opts & 1) || /* Match leading dot (if file) */ ((opts & 2) == 0) || /* always if not file */ (pattern[0] == '.'); /* or if pattern starts with '.' */ plen = strlen(pattern); /* Length of pattern */ /* This would be used in calculating length of matching segment */ if (plen > 0) /* User's pattern ends with '*' */ if (pattern[plen - 1] == '*') xstar = 1; if (pattern[0] == '*') { /* User's pattern starts with '*' */ matchpos = 1; debug(F111,"CKMATCH 1",string, matchpos); } if (opts & 4) { /* ^..$ allowed (top level only) */ /* Rewrite pattern to account for ^..$ anchoring... */ if (mypat) free(mypat); /* Get space for "*pattern*" */ mypat = (char *)malloc(plen + 4); if (mypat) { /* Got space? */ char * s = pattern, * p = mypat; /* Set working pointers */ if (*s == '^') { /* First source char is ^ */ s++; /* so skip past it */ } else if (*s != '*') { /* otherwise */ *p++ = '*'; /* prepend '*' to pattern */ } while (*s) { /* Copy rest of pattern */ if (!*(s+1)) { /* Final pattern character? */ if (*s != '$') { /* If it's not '$' */ *p++ = *s; /* Copy it into the pattern */ if (*s++ != '*') /* And if it's also not '*' */ *p++ = '*'; /* append '*'. */ } break; /* Done */ } else /* Not final character */ *p++ = *s++; /* Just copy it */ } *p = NUL; /* Terminate the new pattern */ pattern = mypat; /* Make the switch */ } debug(F110,"CKMATCH INIT pat",pattern,0); } } matchdepth++; /* Now increment call depth */ #ifdef UNIX if (!dot) { /* For UNIX file globbing */ if (*string == '.' && *pattern != '.' && !matchdot) { if ( #ifdef CKREGEX *pattern != '{' && *pattern != '[' #else 1 #endif /* CKREGEX */ ) { debug(F110,"ckmatch skip",string,0); MATCHRETURN(1,0); } } } #endif /* UNIX */ while (1) { k++; cp = *pattern; /* Character from pattern */ cs = *string; /* Character from string */ #ifdef COMMENT debug(F000,"CKMATCH pat cp",pattern,cp); debug(F000,"CKMATCH str cs",string,cs); #endif /* COMMENT */ if (!cs) { /* End of string - done. */ x = (!cp || (cp == '*' && !*(pattern+1))) ? 1 : 0; if (x) { if (!matchpos) { matchpos = stringpos; debug(F111,"CKMATCH A",string, matchpos); } matchend = stringpos; MATCHRETURN(2,matchpos); } debug(F111,"CKMATCH ZERO d",string, matchpos); matchpos = 0; MATCHRETURN(16,matchpos); } if (!icase) { /* If ignoring case */ if (isupper(cp)) /* convert both to lowercase. */ cp = tolower(cp); if (isupper(cs)) cs = tolower(cs); } if (q) { /* This character was quoted */ debug(F000,"CKMATCH QUOTED",pattern,cp); q = 0; /* Turn off quote flag */ if (cs == cp) { /* Compare directly */ if (!matchpos) { /* Matches */ matchpos = stringpos; debug(F111,"CKMATCH \\ new match",string, matchpos); } pattern++; } else { /* Doesn't match */ pattern = lastpat; /* Back up the pattern */ matchpos = 0; debug(F111,"CKMATCH \\ no match",pattern, matchpos); } string++; stringpos++; continue; } if (cp == CMDQ && !q) { /* Quote in pattern */ debug(F000,"CKMATCH QUOTE",pattern,cp); q = 1; /* Set flag */ pattern++; /* Advance to next pattern character */ continue; /* and continue. */ } if (cs && cp == '?') { /* '?' matches any char */ if (!matchpos) { matchpos = stringpos; debug(F111,"CKMATCH D",string, matchpos); } debug(F110,"CKMATCH ? pat",pattern,0); debug(F110,"CKMATCH ? str",string,0); pattern++; string++; stringpos++; continue; #ifdef CKREGEX } else if (cp == '[') { /* Have bracket */ int q = 0; /* My own private q */ char * psave = NULL; /* and backup pointer */ CHAR clist[256]; /* Character list from brackets */ CHAR c, c1, c2; for (i = 0; i < 256; i++) /* memset() etc not portable */ clist[i] = NUL; psave = ++pattern; /* Where pattern starts */ debug(F111,"CKMATCH [] ",pattern-1, matchpos); for (flag = 0; !flag; pattern++) { /* Loop thru pattern */ c = (CHAR)*pattern; /* Current char */ debug(F000,">>> pattern char","",c); if (q) { /* Quote within brackets */ q = 0; clist[c] = 1; continue; } if (!icase) /* Case conversion */ if (isupper(c)) c = tolower(c); switch (c) { /* Handle unquoted character */ case NUL: /* End of string */ MATCHRETURN(4,0); /* No matching ']' so fail */ case CMDQ: /* Next char is quoted */ q = 1; /* Set flag */ continue; /* and continue. */ case '-': /* A range is specified */ c1 = (pattern > psave) ? (CHAR)*(pattern-1) : NUL; c2 = (CHAR)*(pattern+1); /* IGNORE OUT-OF-BOUNDS WARNING */ if (c2 == ']') c2 = NUL; /* (it can't happen) */ if (c1 == NUL) c1 = c2; for (c = c1; c <= c2; c++) { clist[c] = 1; if (!icase) { if (islower(c)) { clist[toupper(c)] = 1; } else if (isupper(c)) { clist[tolower(c)] = 1; } } } continue; case ']': /* End of bracketed sequence */ flag = 1; /* Done with FOR loop */ break; /* Compare what we have */ default: /* Just a char */ clist[c] = 1; /* Record it */ if (!icase) { if (islower(c)) { clist[toupper(c)] = 1; } else if (isupper(c)) { clist[tolower(c)] = 1; } } continue; } } debug(F000,">>> cs","",cs); debug(F101,">>> clist[cs]","",clist[cs]); debug(F000,">>> string",string,*string); if (!clist[(unsigned)cs]) { /* No match? */ if (!*string) { /* This clause 16 Jun 2005 */ MATCHRETURN(5,0); /* Nope, done. */ } /* We need to fail here if the [clist] is not allowed to float. The [clist] is not allowed to float if it is not preceded by an asterisk, right? 30 Dec 2005. */ if (!havestar) { MATCHRETURN(500,0); } string++; /* From here to end added 2005/6/15 */ stringpos++; pattern = lastpat; /* Back up pattern */ k = ckmatch(pattern,string,icase,opts); if (xxflag) MATCHRETURN(0,0); if (!matchpos && k > 0) matchpos = stringpos; MATCHRETURN(5, (*string) ? matchpos : 0); } if (!matchpos) { matchpos = stringpos; debug(F111,"CKMATCH [] match",string, matchpos); } string++; /* Yes, advance string pointer */ stringpos++; continue; /* and go on. */ } else if (cp == '{') { /* Braces enclosing list of strings */ char * p, * s, * s2, * buf = NULL; int n, bc = 0; int len = 0; debug(F111,"CKMATCH {} ",string, matchpos); for (p = pattern++; *p; p++) { if (*p == '{') bc++; if (*p == '}') bc--; if (bc < 1) break; } if (bc != 0) { /* Braces don't match */ MATCHRETURN(6,0); /* Fail */ } else { /* Braces do match */ int q = 0, done = 0; len = *p ? strlen(p+1) : 0; /* Length of rest of pattern */ if (len) bronly = 0; if (bronly && (matchdepth != 1)) bronly = 0; n = p - pattern; /* Size of list in braces */ if ((buf = (char *)malloc(n+1))) { /* Copy so we can poke it */ char * tp = NULL; int k, sofar; ckstrncpy(buf,pattern,n+1); sofar = string - ostring - matchpos + 1; if (sofar < 0) sofar = 0; debug(F111,"CKMATCH .. string",string,sofar); debug(F111,"CKMATCH .. ostring",ostring,sofar); n = 0; for (s = s2 = buf; 1; s++) { /* Loop through segments */ n++; if (q) { /* This char is quoted */ q = 0; if (!*s) done = 1; continue; } if (*s == CMDQ && !q) { /* Quote next char */ q = 1; continue; } if (!*s || *s == ',') { /* End of this segment */ int tplen = 0; if (!*s) /* If end of buffer */ done = 1; /* then end of last segment */ *s = NUL; /* Overwrite comma with NUL */ debug(F111,"CKMATCH {} segment",s2,done); tplen = n + len + sofar + 2; if (!*s2) { /* Empty segment, no advancement */ k = 0; } else if ((tp = (char *)malloc(tplen))) { int savpos, opts2; char * pp; pp = matchpos > 0 ? &ostring[matchpos-1] : ostring; if (bronly) { if (matchpos > 0) ckstrncpy(tp,pp,sofar+1); else ckstrncpy(tp,pp,sofar); } else { tp[0] = '*'; tp[1] = NUL; if (matchpos > 0) ckstrncpy(&tp[1],pp,sofar+1); else ckstrncpy(&tp[1],pp,sofar); } ckstrncat(tp,s2,tplen); /* Current segment */ ckstrncat(tp,p+1,tplen); /* rest of pattern */ debug(F101,"CKMATCH {} matchpos","",matchpos); savpos = matchpos; matchpos = 0; #ifdef DEBUG if (deblog) { debug(F111,"CKMATCH {} tp",tp,matchpos); debug(F111,"CKMATCH {} string", string,matchpos); debug(F111,"CKMATCH {} ostring", ostring,savpos); } #endif /* DEBUG */ /* If segment starts with dot */ /* then set matchdot option.. */ opts2 = opts; if (*s2 == '.') opts2 |= 1; debug(F111,"CKMATCH {} recursing",s2,opts2); k = ckmatch(tp, (string > ostring) ? &ostring[savpos-1] : string, icase,opts2); #ifdef DEBUG if (deblog) { debug(F101,"CKMATCH {} k","",k); debug(F101,"CKMATCH {} savpos","",savpos); } #endif /* DEBUG */ free(tp); tp = NULL; if (xxflag) MATCHRETURN(0,0); if (k == 0) { matchpos = savpos; } if (k > 0) { /* If it matched we're done */ MATCHRETURN(7,k); } } else { /* Malloc failure */ MATCHRETURN(14,0); } if (k) { /* Successful comparison */ if (!matchpos) { matchpos = stringpos; debug(F111,"CKMATCH {} match", string, matchpos); } string += n-1; /* Advance pointers */ pattern = p+1; break; } if (done) /* If no more segments */ break; /* break out of segment loop. */ s2 = s+1; /* Otherwise, on to next segment */ n = 0; } } free(buf); } } #endif /* CKREGEX */ } else if (cp == '*') { /* Pattern char is asterisk */ char * psave; char * p, * s = NULL; /* meaning match anything */ int k, n, q = 0; havestar++; /* The rest can float */ while (*pattern == '*') /* Collapse successive asterisks */ pattern++; psave = pattern; /* First non-asterisk after asterisk */ lastpat = pattern - 1; /* Ditto, global */ debug(F111,"CKMATCH * ",string,matchpos); for (n = 0, p = psave; *p; p++,n++) { /* Find next meta char */ if (!q) { if (*p == '?' || *p == '*' || *p == CMDQ #ifdef CKREGEX || *p == '[' || *p == '{' #endif /* CKREGEX */ ) break; #ifdef GLOBBING if (globbing #ifdef UNIXOROSK && *p == '/' #else #ifdef VMS && (*p == '.' || *p == ']' || *p == '<' || *p == '>' || *p == ':' || *p == ';') #else #ifdef datageneral && *p == ':' #else #ifdef STRATUS && *p == '>' #endif /* STRATUS */ #endif /* datageneral */ #endif /* VMS */ #endif /* UNIXOROSK */ ) break; #endif /* GLOBBING */ } } debug(F111,"CKMATCH * n string",string,n); debug(F111,"CKMATCH * n pattrn",pattern,n); debug(F111,"CKMATCH * n p",p,n); if (n > 0) { /* Literal string to match */ s = (char *)malloc(n+1); if (s) { ckstrncpy(s,psave,n+1); /* Copy cuz no poking original */ if (*p) { k = ckindex(s,string,0,0,icase); /* 1-based index() */ debug(F110,"CKMATCH * Index() string",string,0); debug(F110,"CKMATCH * Index() pattrn",s,0); debug(F101,"CKMATCH * Index() result","",k); } else { /* String is right-anchored */ k = ckindex(s,string,-1,1,icase); /* rindex() */ debug(F111,"CKMATCH * Rindex()",string,k); debug(F110,"CKMATCH * Rindex() pattrn",s,0); debug(F101,"CKMATCH * Rindex() result","",k); } free(s); if (k < 1) { MATCHRETURN(8,0); } debug(F111,"CKMATCH * stringpos matchpos", ckitoa(stringpos), matchpos); if (!matchpos) { matchpos = string - ostring + k; debug(F111,"CKMATCH * new match ", string, matchpos); } string += k + n - 1; stringpos += k + n - 1; pattern += n; debug(F111,"CKMATCH * new string", string, stringpos); debug(F110,"CKMATCH * new pattrn", pattern, 0); continue; } } else if (!*p) { /* Asterisk at end matches the rest */ if (!globbing) { /* (if not filename globbing) */ if (!matchpos) { matchpos = stringpos; debug(F111,"CKMATCH *$ ",string, matchpos); } matchend = stringpos; MATCHRETURN(9,matchpos); } #ifdef GLOBBING while (*string) { if (globbing /* Filespec so don't cross fields */ #ifdef OS2 && *string == '/' || *string == '\\' || *string == ':' #else #ifdef UNIXOROSK && *string == '/' #else #ifdef VMS && (*string == '.' || *string == ']' || *string == '<' || *string == '>' || *string == ':' || *string == ';') #else #ifdef datageneral && *string == ':' #else #ifdef STRATUS && *string == '>' #else && *string == '/' /* (catch-all) */ #endif /* STRATUS */ #endif /* datageneral */ #endif /* VMS */ #endif /* UNIXOROSK */ #endif /* OS2 */ ) { matchend = stringpos; MATCHRETURN(10,0); } if (!matchpos) { matchpos = stringpos; debug(F111,"CKMATCH *$ match",string, matchpos); } string++; stringpos++; } #endif /* GLOBBING */ if (!matchpos) { matchpos = stringpos; debug(F111,"CKMATCH ** match",string, matchpos); } matchend = stringpos; MATCHRETURN(11,matchpos); } else { /* A meta char follows asterisk */ if (!*string) MATCHRETURN(17, matchpos = 0); #ifdef COMMENT /* This is more elegant but it doesn't work. */ p--; string++; stringpos++; k = ckmatch(p,string,icase,opts); #else while (*string && ((k = ckmatch(p,string,icase,opts)) < 1)) { if (xxflag) MATCHRETURN(0,0); string++; stringpos++; } if (!*string && k < 1) { /* Definitely no match so we set a global flag to inibit further backing up and retrying by previous incarnations, since they don't see that the string and/or pattern, which are on the stack, have been exhausted at this level. */ xxflag++; debug(F111,"CKMATCH DEFINITELY NO MATCH",p,k); MATCHRETURN(91,0); } #endif /* COMMENT */ debug(F111,"CKMATCH * k",string, k); if (!matchpos && k > 0) { matchpos = stringpos; debug(F111,"CKMATCH * matchpos",string, matchpos); } MATCHRETURN(12, (*string) ? matchpos : 0); } } else if (cs == cp) { pattern++; string++; stringpos++; if (!matchpos) { matchpos = stringpos; debug(F111,"CKMATCH cs=cp",string, matchpos); } continue; } else { MATCHRETURN(13,0); } } xckmatch: { #ifdef DEBUG char msgbuf[256]; #endif /* DEBUG */ if (matchdepth > 0) matchdepth--; matchpos = rc; #ifdef DEBUG ckmakxmsg(msgbuf,256, "CKMATCH RETURN[", ckitoa(where), "] matchpos=", ckitoa(matchpos), " matchdepth=", ckitoa(matchdepth), " pat=",pattern, " string=",string,NULL,NULL ); debug(F110,msgbuf,string,0); #endif /* DEBUG */ return(rc); } } #ifdef CKFLOAT /* I S F L O A T -- Verify that arg represents a floating-point number */ /* Portable replacement for atof(), strtod(), scanf(), etc. Call with: s = pointer to string flag == 0 means entire string must be a (floating-pointing) number. flag != 0 means to terminate scan on first character that is not legal. Returns: 1 if result is a legal number; 2 if result has a fractional part; 0 if not or if string empty. Side effect: Sets global floatval to floating-point value if successful. Number need not contain a decimal point -- integer is subcase of float. Scientific notation not supported. */ CKFLOAT floatval = 0.0; /* For returning value */ int isfloat(s,flag) char *s; int flag; { int state = 0; int sign = 0; char c; CKFLOAT d = 0.0, f = 0.0; if (!s) return(0); if (!*s) return(0); while (isspace(*s)) s++; if (*s == '-') { /* Handle optional sign */ sign = 1; s++; } else if (*s == '+') s++; while ((c = *s++)) { /* Handle numeric part */ switch (state) { case 0: /* Mantissa... */ if (isdigit(c)) { f = f * 10.0 + (CKFLOAT)(c - '0'); continue; } else if (c == '.') { state = 1; d = 1.0; continue; } if (flag) /* Not digit or period */ goto done; /* break if flag != 0 */ return(0); /* otherwise fail. */ case 1: /* Fraction... */ if (isdigit(c)) { d *= 10.0; f += (CKFLOAT)(c - '0') / d; continue; } default: if (flag) /* Illegal character */ goto done; /* Break */ return(0); /* or fail, depending on flag */ } } done: if (sign) f = 0.0 - f; /* Apply sign to result */ floatval = f; /* Set result */ return(d ? 2 : 1); /* Succeed */ } /* c k r o u n d -- Rounds a floating point number or an integer. fpnum: Floating-point number to round. places: Positive...To how many decimal places. Zero.......Round to integer. Negative...-1 = nearest ten, -2 = nearest 100, -3 = nearest thousand, etc. obuf Output buffer for string result if desired. obuflen Length of output buffer. Returns: Result as CKFLOAT (which is not going to be as exact as the string result) And the exact result in the string output buffer, if one was specified. */ CKFLOAT #ifdef CK_ANSIC ckround(CKFLOAT fpnum, int places, char *obuf, int obuflen) #else ckround(fpnum,places,obuf,obuflen) CKFLOAT fpnum; int places, obuflen; char *obuf; #endif /* CK_ANSIC */ /* ckround */ { char *s, *s2, *d; int i, p, len, x, n, digits; int carry = 0; int minus = 0; char buf[200]; char * number; CKFLOAT value; extern int fp_digits; sprintf(buf,"%200.100f",fpnum); /* Make string version to work with */ number = (char *) buf; /* Make pointer to it */ p = places; /* Precision */ d = (char *)0; /* Pointer to decimal or string end */ s = number; /* Fix number... */ while (*s == ' ' || *s == '\011') s++; /* Strip leading whitespace */ if (*s == '+') s++; /* Skip leading plus sign*/ number = s; /* Start of number */ if (*s == '-') { minus++; number++; s++; } /* Remember if negative */ s = number; /* Don't allow false precision */ n = 0; while (*s && *s != '.') s++, n++; /* Find decimal */ if (p + n > fp_digits) /* Too many digits */ p = fp_digits - n; /* Don't ask for bogus precision */ if (p < 0) p = 0; /* But don't ask for less than zero */ if (n > fp_digits) /* Integer part has too many digits */ *s = 0; /* but we can't truncate it */ else /* Magnitude is OK */ number[fp_digits+1] = 0; /* Truncate fractional part. */ len = (int)strlen(number); /* Length of non-bogus number */ d = s; /* Pointer to decimal point */ if (p > 0) { /* Rounding the fractional part */ if (n + p < len) { /* If it's not already shorter */ if (*s == '.') s++; /* Skip past decimal */ s += p; /* Go to desired spot */ if (*s > '4' && *s <= '9') /* Check value of digit */ carry = 1; *s = 0; /* And end the string */ s--; /* Point to last digit */ } } else if (p == 0) { /* Rounding to integer */ if (*s == '.') { *s = 0; /* erase the decimal point */ if (*(s+1)) { /* and there is a factional part */ if (*(s+1) > '4' && *(s+1) <= '9') /* Check for carry */ carry = 1; } s--; /* Point to last digit */ } } else { /* Rounding the integer part */ if (s + p > number) { /* as in "the nearest hundred" */ s += p; /* Go left to desired digit */ *d = 0; /* Discard fraction */ carry = 0; if (*s > '4') /* Check first digit of fraction */ carry = 1; /* and set carry flag */ s2 = s; while (s2 < d) /* Fill in the rest with zeros */ *s2++ = '0'; s--; /* Point to last digit */ } } if (carry) { /* Handle carry, if any */ while (s >= number) { if (*s == '.') { /* Skip backwards over decimal */ s--; continue; } *s += 1; /* Add 1 to current digit */ carry = 0; if (*s <= '9') /* If result is 9 or less */ break; /* we're done */ *s = '0'; /* Otherwise put 0 */ carry = 1; /* carry the 1 */ s--; /* and back up to next digit */ } } #ifdef __alpha sscanf(number,"%f",&value); /* Convert back to floating point */ #else sscanf(number,"%lf",&value); /* Convert back to floating point */ #endif if (obuf) strncpy(obuf,number,obuflen); /* Set string result */ return(value); /* Return floating-point result */ } #endif /* CKFLOAT */ /* Sorting routines... */ /* S H _ S O R T -- Shell sort -- sorts string array s in place. */ /* Highly defensive and relatively quick. Uses shell sort algorithm. Args: s = pointer to array of strings. p = pointer to a second array to sort in parallel s, or NULL for none. n = number of elements in s. k = position of key. r = ascending lexical order if zero, reverse lexical order if nonzero. c = 0 for case independence, 1 for case matters, 2 for numeric. If k is past the right end of a string, the string is considered empty for comparison purposes. Hint: To sort a piece of an array, call with s pointing to the first element and n the number of elements to sort. Return value: None. Always succeeds, unless any of s[0]..s[n-1] are bad pointers, in which case memory violations are possible, but C offers no defense against this, so no way to gracefully return an error code. */ VOID sh_sort(s,p,n,k,r,c) char **s, **p; int n, k, r, c; { int m, i, j, x; char *t, *t1, *t2, *u = NULL; #ifdef CKFLOAT CKFLOAT f1, f2; #else long n1, n2; #endif /* CKFLOAT */ if (!s) return; /* Nothing to sort? */ if (n < 2) return; /* Not enough elements to sort? */ if (k < 0) k = 0; /* Key */ m = n; /* Initial group size is whole array */ while (1) { m = m / 2; /* Divide group size in half */ if (m < 1) /* Small as can be, so done */ break; for (j = 0; j < n-m; j++) { /* Sort each group */ t = t2 = s[j+m]; /* Compare this one... */ if (!t) /* But if it's NULL */ t2 = ""; /* make it the empty string */ if (p) /* Handle parallel array, if any */ u = p[j+m]; if (k > 0 && *t2) { if ((int)strlen(t2) < k) /* If key too big */ t2 = ""; /* make key the empty string */ else /* Key is in string */ t2 = t + k; /* so point to key position */ } for (i = j; i >= 0; i -= m) { /* Loop thru comparands s[i..]*/ t1 = s[i]; if (!t1) /* Same deal */ t1 = ""; if (k > 0 && *t1) { if ((int)strlen(t1) < k) t1 = ""; else t1 = s[i]+k; } if (c == 2) { /* Numeric comparison */ x = 0; #ifdef CKFLOAT f2 = 0.0; f1 = 0.0; if (isfloat(t1,1)) { f1 = floatval; if (isfloat(t2,1)) f2 = floatval; else f1 = 0.0; } if (f2 < f1) x = 1; else x = -1; #else n2 = 0L; n1 = 0L; if (rdigits(t1)) { n1 = atol(t1); if (rdigits(t2)) n2 = atol(t2); else n1 = 0L; } if (n2 < n1) x = 1; else x = -1; #endif /* CKFLOAT */ } else { x = ckstrcmp(t1,t2,-1,c); /* Compare */ } if (r == 0 && x < 0) break; if (r != 0 && x > 0) break; s[i+m] = s[i]; if (p) p[i+m] = p[i]; } s[i+m] = t; if (p) p[i+m] = u; } } } /* C K R A D I X -- Radix converter */ /* Call with: s: a number in string format. in: int, specifying the radix of s, 2-36. out: int, specifying the radix to convert to, 2-36. Returns: NULL on error (illegal radix, illegal number, etc.). "-1" on overflow (number too big for unsigned long). Otherwise: Pointer to result. */ char * ckradix(s,in,out) char * s; int in, out; { char c, *r = rxresult; int d, minus = 0; #ifdef COMMENT unsigned long zz = 0L; long z = 0L; #else /* To get 64 bits on 32-bit hardware we use off_t, but there is no unsigned version of off_t, so we lose the ability to detect overflow. */ CK_OFF_T zz = (CK_OFF_T)0; CK_OFF_T z = (CK_OFF_T)0; #endif /* COMMENT */ if (in < 2 || in > 36) /* Verify legal input radix */ return(NULL); if (out < 2 || out > 36) /* and output radix. */ return(NULL); if (*s == '+') { /* Get sign if any */ s++; } else if (*s == '-') { minus++; s++; } while (*s == SP || *s == '0') /* Trim leading blanks or 0's */ s++; /* For detecting overflow, we use a signed copy of the unsigned long accumulator. If it goes negative, we know we'll overflow NEXT time through the loop. */ for (; *s; s++) { /* Convert from input radix to */ c = *s; /* unsigned long */ if (islower(c)) c = toupper(c); if (c >= '0' && c <= '9') d = c - '0'; else if (c >= 'A' && c <= 'Z') d = c - 'A' + 10; else return(NULL); if (d >= in) /* Check for illegal digit */ return(NULL); zz = zz * in + d; if (z < 0L) /* Clever(?) overflow detector */ return("-1"); z = zz; } if (!zz) return("0"); r = &rxresult[RXRESULT]; /* Convert from unsigned long */ *r-- = NUL; /* to output radix. */ while (zz > 0 && r > rxresult) { d = zz % (unsigned)out; *r-- = rxdigits[d]; zz = zz / (unsigned)out; } if (minus) *r-- = '-'; /* Replace original sign */ return((char *)(r+1)); } #ifndef NOB64 /* Base-64 conversion routines */ static char b64[] = { /* Encoding vector */ #ifdef pdp11 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=" #else 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S', 'T','U','V','W','X','Y','Z','a','b','c','d','e','f','g','h','i','j','k','l', 'm','n','o','p','q','r','s','t','u','v','w','x','y','z','0','1','2','3','4', '5','6','7','8','9','+','/','=','\0' #endif /* pdp11 */ }; static int b64tbl[] = { /* Decoding vector */ -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; /* B 8 T O B 6 4 -- Converts 8-bit data to Base64 encoding. Call with: s = Pointer to 8-bit data; n = Number of source bytes to encode (SEE NOTE). If it's a null-terminated string, you can use -1 here. out = Address of output buffer. len = Length of output buffer (should > 4/3 longer than input). Returns: >= 0 if OK, number of bytes placed in output buffer, with the subsequent byte set to NUL if space permits. -1 on error (output buffer space exhausted). NOTE: If this function is to be called repeatedly, e.g. to encode a data stream a chunk at a time, the source length must be a multiple of 3 in all calls but the final one to avoid the generation of extraneous pad characters that would throw the decoder out of sync. When encoding only a single string, this is not a consideration. No internal state is kept, so there is no reset function. */ int b8tob64(s,n,out,len) char * s,* out; int n, len; { int b3, b4, i, x = 0; unsigned int t; if (n < 0) n = strlen(s); for (i = 0; i < n; i += 3,x += 4) { /* Loop through source bytes */ b3 = b4 = 0; t = (unsigned)((unsigned)((unsigned int)s[i] & 0xff) << 8); if (n - 1 > i) { /* Do we have another after this? */ t |= (unsigned)(s[i+1] & 0xff); /* Yes, OR it in */ b3 = 1; /* And remember */ } t <<= 8; /* Move over */ if (n - 2 > i) { /* Another one after that? */ t |= (unsigned)(s[i+2] & 0xff); /* Yes, OR it in */ b4 = 1; /* and remember */ } if (x + 4 > len) /* Check output space */ return(-1); out[x+3] = b64[b4 ? (t & 0x3f) : 64]; /* 64 = code for '=' */ t >>= 6; out[x+2] = b64[b3 ? (t & 0x3f) : 64]; t >>= 6; out[x+1] = b64[t & 0x3f]; t >>= 6; out[x] = b64[t & 0x3f]; } if (x < len) out[x] = NUL; /* Null-terminate the string */ return(x); } /* B 6 4 T O B 8 -- Converts Base64 string to 8-bit data. Call with: s = pointer to Base64 string (whitespace ignored). n = length of string, or -1 if null terminated, or 0 to reset. out = address of output buffer. len = length of output buffer. Returns: >= 0 if OK, number of bytes placed in output buffer, with the subsequent byte set to NUL if space permits. < 0 on error: -1 = output buffer too small for input. -2 = input contains illegal characters. -3 = internal coding error. NOTE: Can be called repeatedly to decode a Base64 stream, one chunk at a time. However, if it is to be called for multiple streams in succession, its internal state must be reset at the beginning of the new stream. */ int b64tob8(s,n,out,len) char * s,* out; int n, len; { /* Decode */ static int bits = 0; static unsigned int r = 0; int i, k = 0, x, t; unsigned char c; if (n == 0) { /* Reset state */ bits = 0; r = 0; return(0); } x = (n < 0) ? strlen(s) : n; /* Source length */ n = ((x + 3) / 4) * 3; /* Compute destination length */ if (x > 0 && s[x-1] == '=') n--; /* Account for padding */ if (x > 1 && s[x-2] == '=') n--; if (n > len) /* Destination not big enough */ return(-1); /* Fail */ for (i = 0; i < x; i++) { /* Loop thru source */ c = (CHAR)s[i]; /* Next char */ t = b64tbl[c]; /* Code for this char */ if (t == -2) { /* Whitespace or Ctrl */ n--; /* Ignore */ continue; } else if (t == -1) { /* Illegal code */ return(-2); /* Fail. */ } else if (t > 63 || t < 0) /* Illegal value */ return(-3); /* fail. */ bits += 6; /* Count bits */ r <<= 6; /* Make space */ r |= (unsigned) t; /* OR in new code */ if (bits >= 8) { /* Have a byte yet? */ bits -= 8; /* Output it */ c = (unsigned) ((r >> bits) & 0xff); out[k++] = c; } } if (k < len) out[k] = NUL; /* Null-terminate in case it's */ return(k); /* a text string */ } #endif /* NOB64 */ /* C H K N U M -- See if argument string is an integer */ /* Returns 1 if OK, zero if not OK */ /* If OK, string should be acceptable to atoi() or atol() or ckatofs() */ /* Allows leading space, sign */ int chknum(s) char *s; { /* Check Numeric String */ int x = 0; /* Flag for past leading space */ int y = 0; /* Flag for digit seen */ char c; debug(F110,"chknum",s,0); if (!s) return(0); if (!*s) return(0); while ((c = *s++)) { /* For each character in the string */ switch (c) { case SP: /* Allow leading spaces */ case HT: if (x == 0) continue; else return(0); case '+': /* Allow leading sign */ case '-': if (x == 0) x = 1; else return(0); break; default: /* After that, only decimal digits */ if (c >= '0' && c <= '9') { x = y = 1; continue; } else return(0); } } return(y); } /* R D I G I T S -- Verify that all characters in arg ARE DIGITS */ /* Returns 1 if so, 0 if not or if string is empty */ int rdigits(s) char *s; { if (!s) return(0); do { if (!isdigit(*s)) return(0); s++; } while (*s); return(1); } /* P A R N A M -- Return parity name */ char * #ifdef CK_ANSIC parnam(char c) #else parnam(c) char c; #endif /* CK_ANSIC */ /* parnam */ { switch (c) { case 'e': return("even"); case 'o': return("odd"); case 'm': return("mark"); case 's': return("space"); case 0: return("none"); default: return("invalid"); } } char * /* Convert seconds to hh:mm:ss */ #ifdef CK_ANSIC hhmmss(long x) #else hhmmss(x) long x; #endif /* CK_ANSIC */ /* hhmmss(x) */ { static char buf[10]; long s, h, m; h = x / 3600L; /* Hours */ x = x % 3600L; m = x / 60L; /* Minutes */ s = x % 60L; /* Seconds */ if (x > -1L) sprintf(buf,"%02ld:%02ld:%02ld",h,m,s); else buf[0] = NUL; return((char *)buf); } /* L S E T -- Set s into p, right padding to length n with char c; */ /* s is a NUL-terminated string. If length(s) > n, only n bytes are moved. The result is NOT NUL terminated unless c == NUL and length(s) < n. The intended of this routine is for filling in fixed-length record fields. */ VOID lset(p,s,n,c) char *s; char *p; int n; int c; { int x; #ifndef USE_MEMCPY int i; #endif /* USE_MEMCPY */ if (!s) s = ""; x = strlen(s); if (x > n) x = n; #ifdef USE_MEMCPY memcpy(p,s,x); if (n > x) memset(p+x,c,n-x); #else for (i = 0; i < x; i++) *p++ = *s++; for (; i < n; i++) *p++ = c; #endif /* USE_MEMCPY */ } /* R S E T -- Right-adjust s in p, left padding to length n with char c */ VOID rset(p,s,n,c) char *s; char *p; int n; int c; { int x; #ifndef USE_MEMCPY int i; #endif /* USE_MEMCPY */ if (!s) s = ""; x = strlen(s); if (x > n) x = n; #ifdef USE_MEMCPY memset(p,c,n-x); memcpy(p+n-x,s,x); #else for (i = 0; i < (n - x); i++) *p++ = c; for (; i < n; i++) *p++ = *s++; #endif /* USE_MEMCPY */ } /* U L O N G T O H E X -- Unsigned long to hex */ /* Converts unsigned long arg to hex and returns string pointer to rightmost n hex digits left padded with 0's. Allows for longs up to 64 bits. Returns pointer to result. */ char * #ifdef CK_ANSIC ulongtohex( unsigned long z, int n ) #else ulongtohex(z,n) unsigned long z; int n; #endif /* CK_ANSIC */ /* ulongtohex */ { static char hexbuf[17]; int i = 16, x, k = 0; hexbuf[16] = '\0'; if (n > 16) n = 16; k = 2 * (sizeof(long)); for (i = 0; i < n; i++) { if (i > k || z == 0) { hexbuf[15-i] = '0'; } else { x = z & 0x0f; z = z >> 4; hexbuf[15-i] = x + ((x < 10) ? '0' : 0x37); } } return((char *)(&hexbuf[16-i])); } /* H E X T O U L O N G -- Hex string to unsigned long */ /* Converts n chars from s from hex to unsigned long. Returns: 0L or positive, good result (0L is returned if arg is NULL or empty). -1L on error: non-hex arg or overflow. */ long hextoulong(s,n) char *s; int n; { char buf[64]; unsigned long result = 0L; int d, count = 0; int flag = 0; if (!s) s = ""; if (!*s) { return(0L); } if (n < 1) return(0L); if (n > 63) n = 63; strncpy(buf,s,n); buf[n] = '\0'; s = buf; while (*s) { d = *s++; if ((d == '0' || d == ' ')) { if (!flag) continue; } else { flag = 1; } if (islower(d)) d = toupper(d); if (d >= '0' && d <= '9') { d -= 0x30; } else if (d >= 'A' && d <= 'F') { d -= 0x37; } else { return(-1L); } if (++count > (sizeof(long) * 2)) return(-1L); result = (result << 4) | (d & 0x0f); } return(result); } /* c k s p l i t -- Splits a string into words, or: extracts a given word from a string. Allows for grouping. Operates on a copy of the string; does not alter the original string. All strings NUL-terminated. Call with: fc = function code: 1 = split, 0 = word. n1 = desired word number if fc == 0. s1 = source string. s2 = break string (NULL to accept default = all non-alphanum). s3 = include string (NULL to accept default = all alphanum). n2 = grouping mask (OR desired ones together): 1 = doublequotes, 2 = braces, 4 = apostrophes, 8 = parens, 16 = brackets, 32 = angle brackets, -1 = 63 = all of these. n3 = group quote character, ASCII value, used and tested only for LISP quote, e.g. (a 'b c '(d e f)). n4 = 0 to collapse adjacent separators; nonzero not to collapse them. Returns: Pointer to struct stringarray, with size: -1 = memory allocation error. -2 = too many words in string. n = number of words (0 or more). With: wordarray = array of pointers to n words (n == 1 if fc == 0), 1-based. Each pointer is to a malloc'd string. This array is recycled upon each call; if you need to keep the strings, make copies of them. This routine must have control of allocation and deallocation. If a given character is included in the include list, it is not treated as a separator or as a grouping character. Groups may be nested only if they are formed with braces, parens, or brackets, but not with quotes or apostrophes since ASCII quotes have no intrinsic handedness. Group-start and end characters are treated as separators even in the absence of other separators, so a string such as "a{b}c" results in three words, not one. Sample call to split a string into an array: struct stringarray * q; q = cksplit(1,0,s1,s2,s3,-1,0); q->a_size = size (>=0) or failure code (<0) q->a_head = pointer to array (elements 0 thru q->asize - 1). Sample call to extract word n from a string: struct stringarray * q; q = cksplit(0,n,s1,s2,s3,-1,0); q->a_size = size (1) or failure code (<0) q->a_head = pointer to array (element 1 is the desired word). */ /* States */ #define ST_BW 0 /* Between Words */ #define ST_IW 1 /* In Word */ #define ST_IG 2 /* Start Group */ /* Character Classes (bitmap) */ #define CL_SEP 1 /* Separator */ #define CL_OPN 2 /* Group Open */ #define CL_CLS 4 /* Group Close */ #define CL_DAT 8 /* Data */ #define CL_QUO 16 /* Group quote */ #ifdef BIGBUFOK #ifndef MAXWORDS #define MAXWORDS 4096 /* Max number of words */ #endif /* MAXWORDS */ #ifndef NESTMAX #define NESTMAX 64 /* Maximum nesting level */ #endif /* NESTMAX */ #else #ifndef MAXWORDS #define MAXWORDS 128 /* Max number of words */ #endif /* MAXWORDS */ #ifndef NESTMAX #define NESTMAX 16 /* Maximum nesting level */ #endif /* NESTMAX */ #endif /* BIGBUFOK */ /* static */ char ** wordarray = NULL; /* Result array of word pointers */ static struct stringarray ck_sval = { /* Return value structure */ NULL, /* Pointer to array */ 0 /* Size */ }; static int * wordsize = NULL; static VOID setword(n,s,len) int n, len; char * s; { register char * p; register int i, k; if (!s) s = ""; if (!wordarray) { /* Allocate result array (only once) */ if (!(wordarray = (char **)malloc((MAXWORDS+1) * sizeof(char *)))) return; if (!(wordsize = (int *)malloc((MAXWORDS+1) * sizeof(int)))) return; for (i = 0; i <= MAXWORDS; i++) { /* Initialize result array */ wordarray[i] = NULL; wordsize[i] = 0; } } if (wordsize[n] < len /* || !wordarray[n] */ ) { k = (len < 16) ? 16 : len + (len / 4); wordarray[n] = (char *) malloc(k+1); wordsize[n] = (wordarray[n]) ? k : 0; if (wordarray[n]) { p = wordarray[n]; while ((*p++ = *s++) && k-- > 0) ; } } else if (len > 0) { k = wordsize[n]; /* (In case len arg is a lie) */ p = wordarray[n]; while ((*p++ = *s++) && k-- > 0) { #ifdef COMMENT if ((*(s-1) == CMDQ) && *s != CMDQ) { k++; p--; } #endif /* COMMENT */ } } else if (wordarray[n]) { wordarray[n][0] = NUL; } } static char * splitbuf = NULL; static int nsplitbuf = 0; /* n4 = 1 to NOT collapse adjacent separators */ struct stringarray * cksplit(fc,n1,s1,s2,s3,n2,n3,n4) int fc,n1,n2,n3,n4; char *s1, *s2, *s3; { int splitting = 0; /* What I was asked to do */ int i, k, ko = 0, n, x, max = MAXWORDS; /* Workers */ char * s = NULL, * ss, * p; /* Workers */ char * sep = ""; /* Default break set */ char * notsep = ""; /* Default include set */ int grouping = 0; /* Grouping option */ char * gr_opn = "\"{'([<"; /* Group open brackets */ char * gr_cls = "\"}')]>"; /* Group close brackets */ int gr_stk[NESTMAX]; /* Nesting bracket stack */ int gr_lvl = 0; /* Nesting level */ int wordnum = 0; /* Current word number */ CHAR c = 'A'; /* Current char (dummy start value) */ int class = 0; /* Current character class */ int state = ST_BW; /* Current FSA state */ int len = 0; /* Length of current word */ int slen = 0; /* Length of s1 */ int gquote = 0; /* Quoted group */ int cquote = 0; /* Quoted character */ int collapse = 1; /* Collapse adjacent separators */ int all = 0; /* s3 == ALL */ int csv = 0; /* s3 == CSV */ int tsv = 0; /* s3 == TSV */ int prevstate = -1; unsigned int hex80 = 128; unsigned int hexff = 255; CHAR notsepbuf[256]; notsepbuf[0] = NUL; /* Keep set for "ALL" */ if (n4) collapse = 0; /* Don't collapse */ for (i = 0; i < NESTMAX; i++) /* Initialize nesting stack */ gr_stk[i] = 0; setword(1,NULL,0); ck_sval.a_head = wordarray; /* Initialize return value struct */ ck_sval.a_size = 0; if (!s1) s1 = ""; /* s1 = source string */ if (!*s1) { /* If none, nothing to do */ return(&ck_sval); } splitting = fc; /* Our job */ if (splitting) { /* If splitting n = word count */ n = 0; /* Initialize it */ } else { /* Otherwise */ #ifdef COMMENT if (n1 < 1) { /* If 0 (or less) */ ck_sval.a_size = 0; /* nothing to do. */ return(&ck_sval); } #else if (n1 == 0) { /* If 0 */ ck_sval.a_size = 0; /* nothing to do. */ return(&ck_sval); } #endif /* COMMENT */ n = n1; /* n = desired word number. */ } slen = 0; /* Get length of s1 */ debug(F111,"cksplit",s1,n); p = s1; #ifdef COMMENT while (*p++) slen++; /* Make pokeable copy of s1 */ if (!splitbuf || slen > nsplitbuf) { /* Allocate buffer if needed */ int xx; if (splitbuf) free(splitbuf); xx = (slen < 255) ? 255 : xx + (xx / 4); debug(F101,"cksplit splitbuf","",xx); splitbuf = (char *)malloc(xx+1); if (!splitbuf) { /* Memory allocation failure... */ ck_sval.a_size = -1; return(&ck_sval); } nsplitbuf = xx; /* Remember size of buffer */ } #else /* The idea is to just copy the string into splitbuf (if it exists). This gives us both the copy and the length so we only need to grovel through the string once in most cases. Only when splitbuf doesn't exist or is too short do we re-malloc(), which should be very infrequent so who cares if we have to go through the string again in that case. */ p = s1; s = splitbuf; if (splitbuf) { /* Make pokeable copy of s1 */ while ((*s++ = *p++) && (slen++ < nsplitbuf)) /* Try to copy */ ; } if (!splitbuf || slen >= nsplitbuf) { /* Need to do more... */ int xx; if (splitbuf) /* Free previous buf if any */ free(splitbuf); while (*p++) slen++; /* Get (rest of) length */ xx = (slen < 255) ? 255 : slen + (slen / 4); /* Size of new buffer */ splitbuf = (char *)malloc(xx+1); /* Allocate it */ if (!splitbuf) { /* Memory allocation failure... */ ck_sval.a_size = -1; return(&ck_sval); } nsplitbuf = xx; /* Remember (new) buffer size */ s = splitbuf; p = s1; while ((*s++ = *p++)) ; } #endif /* COMMENT */ s = splitbuf; sep = s2; /* s2 = break set */ if (!sep) sep = ""; notsep = s3; /* s3 = include set */ if (!notsep) { notsep = ""; } else if ((all = !ckstrcmp(notsep,"ALL",3,1)) || (csv = !ckstrcmp(notsep,"CSV",3,1)) || (tsv = !ckstrcmp(notsep,"TSV",3,1))) { int i, flag; CHAR c; int n = 0; char * ss; if (!all && (csv || tsv)) { all = 1; collapse = 0; } if (csv || tsv) { all = 1; collapse = 0; } for (i = 1; i < 256; i++) { flag = 0; ss = sep; while (c = *ss++ && !flag) { if (c == i) flag++; } if (!flag) notsepbuf[n++] = c; } notsepbuf[n] = NUL; notsep = (char *)notsepbuf; debug(F110,"CKMATCH NOTSEPBUF ALL",notsep,0); } if (*s && csv) { /* For CSV skip leading whitespace */ while (*s == SP || *s == HT) s++; c = *s; } if (n2 == 0 && csv) n2 = 1; /* CSV implies doublequote grouping */ if (n2 < 0) n2 = 63; /* n2 = grouping mask */ grouping = n2; p = ""; /* Pointer to current word */ while (c) { /* Loop through string */ c = *s; class = 0; if (!csv && !tsv) { /* fdc 2010-12-30 */ /* In CSV and TSV splitting, backslash is not special */ if (!cquote && c == CMDQ) { /* If CMDQ */ cquote++; /* next one is quoted */ goto nextc; /* go get it */ } } if (cquote && c == CMDQ) { /* Quoted CMDQ is special */ if (state != ST_BW) { /* because it can still separate */ char * s2 = s-1; while (s2 > p) { *s2 = *(s2-1); s2--; } p++; } cquote = 0; } if (cquote) { /* Other quoted character */ if (state != ST_BW) { /* can't separate or group */ char * s2 = s-1; while (s2 > p) { *s2 = *(s2-1); s2--; } p++; } class = CL_DAT; /* so treat it as data */ cquote = 0; x = 1; } else { /* Character is not quoted */ if (!all && c < SP) { /* Get its class */ x = 0; /* x == 0 means "is separator" */ } else if (*sep) { /* Break set given */ ss = sep; while (*ss && *ss != c) ss++; /* (instead of ckstrchr()) */ x = (*ss != c); } else { /* Default break set is */ x = ((c >= 'a' && c <= 'z') || /* all but alphanumerics */ (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || ((unsigned int)c >= hex80 && (unsigned int)c <= hexff) ); } if (x == 0 && *notsep && c) { /* Include set if given */ ss = notsep; while (*ss && *ss != c) ss++; /* (instead of ckstrchr()) */ x = (*ss == c); } if (c == n3 && grouping && state == ST_BW) { /* Group quote? */ class = CL_QUO; } else { class = x ? CL_DAT : CL_SEP; /* Class = data or separator */ } if (grouping) { /* Grouping? */ int j; /* Look for group start */ for (k = 0; k < 6; k++) { /* Group-start char? */ j = 1 << k; /* Get its mask bit value */ if (grouping & j) { if (c == gr_opn[k]) { /* Selected group opener? */ ko = k; class |= CL_OPN; if (c == '"' || c == '\'') { /* These can also */ class |= CL_CLS; /* be closers */ break; } } else if (c == gr_cls[k]) { /* Group closer? */ class |= CL_CLS; break; } } } } } debug(F000,"cksplit char",s,c); debug(F101,"cksplit state","",state); debug(F101,"cksplit class","",class); switch (state) { /* State switcher... */ case ST_BW: /* BETWEENWORDS */ if (class & CL_OPN) { /* Group opener */ if (gr_lvl == 0 && !gquote) { /* If not in group */ p = s; /* point to beginning of word */ } gr_lvl++; /* Push closer on nesting stack */ if (gr_lvl >= NESTMAX) goto xxsplit; gr_stk[gr_lvl] = gr_cls[ko]; prevstate = state; state = ST_IG; /* Switch to INGROUP state */ } else if (class & CL_DAT) { /* Data character */ gr_lvl = 0; /* Clear group nesting stack */ gr_stk[gr_lvl] = 0; len = 0; p = s; /* Point to beginning of word */ if (gquote) { /* Adjust for quote */ len++; p--; gquote = 0; } prevstate = state; state = ST_IW; /* Switch to INWORD state */ } else if (class & CL_QUO) { /* Group quote */ gquote = gr_lvl+1; /* Remember quoted level */ p = s - 1; len = 1; } if (collapse) break; case ST_IW: /* INWORD (but not in a group) */ if (class & CL_SEP) { /* Ends on any kind of separator */ *s = NUL; /* Terminate this word */ if (csv) { /* If comma-separated list */ char * s2 = s; /* discard surrounding spaces */ while (s2 > splitbuf) { /* first backwards... */ s2--; if (*s2 == SP || *s2 == HT) *s2 = NUL; else break; } s2 = s+1; /* Then forwards... */ while (*s2 && (*s2 == SP || *s2 == HT)) *s2++; s = s2-1; } if (!csv || prevstate != ST_IG) { wordnum++; /* Count it */ if (splitting || n < 0) { /* Dispose of it appropriately */ if (wordnum > max) { /* Add to array if splitting */ ck_sval.a_size = -2; return(&ck_sval); } /* This inelegant bit corrects an edge condition */ if (csv && !*p && (!c || !*(s+1))) { wordnum--; } else { setword(wordnum,p,len); } } else if (wordnum == n) { /* Searching for word n */ setword(1,p,len); ck_sval.a_size = 1; return(&ck_sval); } } prevstate = state; state = ST_BW; /* Switch to BETWEENWORDS state */ len = 0; } break; case ST_IG: /* INGROUP */ if (class & CL_CLS) { /* Have group closer? */ if (csv) { if (*(s+1) == c) { char *s2 = s; while ((*s2 = *(s2+1))) s2++; s++; c = *s; } } if (c == gr_stk[gr_lvl]) { /* Does it match current opener? */ gr_lvl--; /* Yes, pop stack */ if (gr_lvl < 0) /* Don't pop it too much */ gr_lvl = 0; if (gr_lvl == 0) { /* If at top of stack */ if (gquote) s++; c = *s; *s = NUL; /* we have word. */ wordnum++; /* Count and dispose of it. */ len--; if (splitting || n < 0) { if (wordnum > max) { ck_sval.a_size = -2; return(&ck_sval); } setword(wordnum,p+1,len); } else if (wordnum == n) { setword(1,p+1,len); ck_sval.a_size = 1; return(&ck_sval); } prevstate = state; state = ST_BW; /* Switch to BETWEENWORDS state */ len = 0; } if (gr_lvl < gquote) gquote = 0; } } else if (class & CL_OPN) { /* Have group opener */ gr_lvl++; /* Push on nesting stack */ if (gr_lvl >= NESTMAX) goto xxsplit; gr_stk[gr_lvl] = gr_cls[ko]; } } /* switch */ nextc: s++; /* Next char */ if (state) len++; } /* while (c) */ if (gr_lvl > 0) { /* In case of an unclosed group */ if (splitting || n < 0) { /* make it the last word. */ if (++wordnum > max) { ck_sval.a_size = -2; return(&ck_sval); } setword(wordnum,p+1,len); } else if (wordnum == n) { /* Counting from left */ setword(1,p+1,len); ck_sval.a_size = 1; return(&ck_sval); } else if (n < 0 && (wordnum + n > -1)) { /* Counting from right */ char * s = wordarray[wordnum + n + 1]; if (!s) s = ""; setword(1,s,strlen(s)); ck_sval.a_size = 1; return(&ck_sval); } } if (!splitting) { /* Fword... */ if (n < 0 && (wordnum + n > -1)) { /* Counting from right */ char * s = wordarray[wordnum + n + 1]; if (!s) s = ""; setword(1,s,strlen(s)); ck_sval.a_size = 1; return(&ck_sval); } setword(1,NULL,0); /* From left... */ ck_sval.a_size = 0; /* but there weren't n words */ return(&ck_sval); } else { /* Succeed otherwise */ ck_sval.a_size = wordnum; /* Always put a null element at the end of the array. It does no harm in the normal case, and it's required if we're making an argv[] array to pass to execvp(). This element is not included in the count. */ if (wordnum < MAXWORDS) setword(wordnum+1,NULL,0); } #ifdef DEBUG if (deblog) { for (i = 1; i <= wordnum; i++) debug(F111,"cksplit result",wordarray[i],i); } #endif /* DEBUG */ return(&ck_sval); xxsplit: /* Error return */ ck_sval.a_size = -2; return(&ck_sval); } /* ckhexbytetoint() expects a string of two hex characters, returns the int equivalent or -1 on error. */ int #ifdef CK_ANSIC ckhexbytetoint( char * s ) #else ckhexbytetoint(s) char * s; #endif /* CK_ANSIC */ { int i, c[2]; if (!s) return(-1); if ((int)strlen(s) != 2) return(-1); for (i = 0; i < 2; i++) { c[i] = *s++; if (!c[i]) return(-1); if (islower(c[i])) c[i] = toupper(c[i]); if (c[i] >= '0' && c[i] <= '9') { c[i] -= 0x30; } else if (c[i] >= 'A' && c[i] <= 'F') { c[i] -= 0x37; } else { return(-1); } } return(c[0] * 16 + c[1]); } /* End of ckclib.c */ ckclib.h0000644000015300001460000000715611354737052011301 0ustar fdckermit/* ckclib.h -- C-Kermit library routine prototypes */ /* Author: Frank da Cruz , Columbia University Academic Information Systems, New York City. Copyright (C) 2002, 2009, Trustees of Columbia University in the City of New York. All rights reserved. See the C-Kermit COPYING.TXT file or the copyright text in the ckcmai.c module for disclaimer and permissions. */ #ifndef CKCLIB_H #define CKCLIB_H struct stringarray { char ** a_head; int a_size; }; #ifdef CK_ANSIC _PROTOTYP( int ckstrncpy, (char *, const char *, int) ); _PROTOTYP( int ckstrncat, (char *, const char *, int) ); #else _PROTOTYP( int ckstrncpy, (char *, char *, int) ); _PROTOTYP( int ckstrncat, (char *, char *, int) ); #endif /* CK_ANSIC */ _PROTOTYP( int ckmakmsg, (char *, int, char *, char *, char *, char *) ); _PROTOTYP( int ckmakxmsg, (char *, int, char *, char *, char *, char *, char *, char *, char *, char *, char *, char *, char *, char *) ); _PROTOTYP( char * ckstrpbrk, (char *, char *) ); _PROTOTYP( char * ckstrstr, (char *, char *) ); _PROTOTYP( char * chartostr, (int) ); _PROTOTYP( int cklower, (char *) ); _PROTOTYP( int ckupper, (char *) ); _PROTOTYP( int ckindex, (char *, char *, int, int, int) ); _PROTOTYP( char * ckctoa, (char) ); _PROTOTYP( char * ckctox, (CHAR, int) ); _PROTOTYP( char * ckitoa, (int) ); _PROTOTYP( char * ckuitoa, (unsigned int) ); _PROTOTYP( char * ckltoa, (long) ); _PROTOTYP( char * ckultoa, (unsigned long) ); _PROTOTYP( char * ckfstoa, (CK_OFF_T) ); _PROTOTYP( CK_OFF_T ckatofs, (char *) ); _PROTOTYP( char * ckitox, (int) ); _PROTOTYP( char * ckltox, (long) ); _PROTOTYP( int ispattern, (char *) ); _PROTOTYP( int ckmatch, (char *, char *, int, int ) ); _PROTOTYP( VOID ckmemcpy, (char *, char *, int) ); _PROTOTYP( char * ckstrchr, (char *, char) ); _PROTOTYP( char * ckstrrchr, (char *, char) ); _PROTOTYP( int ckrchar, (char *) ); _PROTOTYP( int ckstrcmp, (char *, char *, int, int) ); #define xxstrcmp(a,b,c) ckstrcmp(a,b,c,0) _PROTOTYP( int ckstrpre, (char *, char *) ); _PROTOTYP( VOID sh_sort, (char **, char **, int, int, int, int) ); _PROTOTYP( char * brstrip, (char *) ); _PROTOTYP( char * fnstrip, (char *) ); #ifdef COMMENT _PROTOTYP( char * brace, (char *) ); #endif /* COMMENT */ _PROTOTYP( int dquote, (char *, int, int) ); _PROTOTYP( int untabify, (char *, char *, int) ); _PROTOTYP( VOID makelist, (char *, char *[], int) ); #ifndef CK_ANSIC _PROTOTYP( VOID makestr, (char **, char *) ); _PROTOTYP( VOID xmakestr, (char **, char *) ); #else /* CK_ANSIC */ _PROTOTYP( VOID makestr, (char **, const char *) ); _PROTOTYP( VOID xmakestr, (char **, const char *) ); #endif /* CK_ANSIC */ _PROTOTYP( int chknum, (char *) ); _PROTOTYP( int rdigits, (char *) ); _PROTOTYP( char * ckradix, (char *,int,int) ); /* Base-64 conversion needed for script programming and HTTP */ #ifndef NOB64 _PROTOTYP( int b8tob64, (char *,int,char *,int)); _PROTOTYP( int b64tob8, (char *,int,char *,int)); #endif /* NOB64 */ #ifdef CKFLOAT _PROTOTYP( int isfloat, (char *,int) ); #ifndef CKCLIB_C #ifndef CKWART_C extern CKFLOAT floatval; #endif /* CKWART_C */ #endif /* CKCLIB_C */ #endif /* CKFLOAT */ _PROTOTYP( char * parnam, (char) ); _PROTOTYP( char *hhmmss, (long) ); _PROTOTYP( VOID lset, (char *, char *, int, int) ); _PROTOTYP( VOID rset, (char *, char *, int, int) ); _PROTOTYP( char * ulongtohex, (unsigned long, int) ); _PROTOTYP( long hextoulong, (char *, int) ); _PROTOTYP( struct stringarray * cksplit, (int,int, char *,char *,char *,int,int,int) ); _PROTOTYP( int ckhexbytetoint, (char *) ); #endif /* CKCLIB_H */ ckcmai.c0000644000015300001460000036126411624022452011266 0ustar fdckermit#define EDITDATE "20 Aug 2011" /* Last edit date dd mmm yyyy */ #define EDITNDATE "20110820" /* Keep them in sync */ /* Sat Aug 20 17:20:17 2011 */ /* ckcmai.c - Main program for C-Kermit plus some miscellaneous functions */ /* ckcsym.h is used for for defining symbols that normally would be defined using -D or -d on the cc command line, for use with compilers that don't support this feature. Must come before any tests for preprocessor symbols. */ #include "ckcsym.h" /* Consolidated program C-Kermit version information for all platforms (but for UNIX also see ckuver.h). See makever() below for how they are used. */ #ifdef COMMENT /* Uncomment this for real K95 version */ #ifndef OS2 /* OS2 actually means Kermit 95. */ #ifndef BETATEST /* It's because Kermit 95 started */ #define BETATEST /* out as C-Kermit for OS/2. */ #endif /* BETATEST */ #endif /* OS2 */ #endif /* COMMENT */ #ifdef BETATEST #ifdef OS2 #ifdef __DATE__ #define BETADATE #endif /* __DATE__ */ #endif /* OS2 */ #endif /* BETATEST */ char * ck_cryear = "2011"; /* C-Kermit copyright year */ #ifndef MAC /* MAC = Kermit for MAC OS 6, 7, ... */ /* Note: initialize ck_s_test to "" if this is not a test version. Use (*ck_s_test != '\0') to decide whether to print test-related messages. */ #ifndef BETATEST #ifndef OS2 /* UNIX, VMS, etc... (i.e. C-Kermit) */ char *ck_s_test = ""; /* "Dev","Alpha","Beta","RC", or "" */ char *ck_s_tver = ""; /* Test version number or "" */ #else /* OS2 */ char *ck_s_test = ""; /* (i.e. K95) */ char *ck_s_tver = ""; #endif /* OS2 */ #else char *ck_s_test = ""; /* Development */ char *ck_s_tver = ""; #endif /* BETATEST */ #else /* MAC */ char *ck_s_test = "Pre-Alpha"; /* Mac Kermit is always a test... */ char *ck_s_tver = ""; /* (pre Mac OS X 10, that is!) */ #endif /* MAC */ #ifdef BETADATE /* Date of this version or edit */ char *ck_s_date = __DATE__; /* Compilation date */ #else char *ck_s_date = EDITDATE; /* See top */ #endif /* BETADATE */ char *buildid = EDITNDATE; /* See top */ #ifdef UNIX static char sccsid[] = "@(#)C-Kermit 9.0.302"; #endif /* UNIX */ /* The C-Kermit Version number is major.minor.edit (integers). Major version always goes up. Minor version is historical, hasn't been used since C-Kermit 7.1. Edit is sequential, always goes up, but there can be gaps. For example there might be many edits between releases. If the major goes to 10, some version-number-based feature tests could fail. It might be better to use the minor version field for future releases. */ char *ck_s_ver = "9.0.302"; /* C-Kermit version string */ long ck_l_ver = 900302L; /* C-Kermit version number */ #ifdef OS2 char *ck_s_xver = "3.0.0"; /* Product-specific version string */ long ck_l_xver = 3000L; /* Product-specific version number */ #else #ifdef MAC char *ck_s_xver = "0.995"; /* Product-specific version string */ long ck_l_xver = 995L; /* Product-specific version number */ #else char *ck_s_xver = ""; /* Don't touch these... */ long ck_l_xver = 0L; /* they are computed at runtime */ #endif /* MAC */ #endif /* OS2 */ #ifdef OS2 #ifdef IKSDONLY #ifdef NT char *ck_s_name = "IKS-NT"; #else /* NT */ char *ck_s_name = "IKS-OS/2"; #endif /* NT */ #else /* IKSDONLY */ char *ck_s_name = "Kermit 95"; /* Program name */ #endif /* IKSDONLY */ #else #ifdef MAC char *ck_s_name = "Mac Kermit"; #else char *ck_s_name = "C-Kermit"; #endif /* MAC */ #endif /* OS2 */ char *ck_s_who = ""; /* Where customized, "" = not. */ char *ck_patch = ""; /* Patch info, if any. */ #define CKVERLEN 128 char versiox[CKVERLEN]; /* Version string buffer */ char *versio = versiox; /* These are filled in at */ long vernum, xvernum; /* runtime from above. */ #define CKCMAI #include "ckcasc.h" /* ASCII character symbols */ #include "ckcdeb.h" /* Debug & other symbols */ char * myname = NULL; /* The name I am called by */ #ifndef OS2 char * exedir = NULL; /* Directory I was executed from */ #endif /* OS2 */ char * myhome = NULL; /* Home directory override */ /* C K C M A I -- C-Kermit Main program */ /* Author: Frank da Cruz (fdc@columbia.edu), Columbia University in the city of New York, Computer Center / Center for Computing Activities / Information Technology. I am no longer at Columbia U as of 1 July 2011, but the email address should still work. The Kermit website http://kermit.columbia.edu should still be available and under my control, as well as the Kermit FTP site, ftp://kermit.columbia.edu/kermit/. COPYRIGHT NOTICE: */ char *copyright[] = { #ifdef pdp11 "Copyright (C) 1985, %s, Trustees of Columbia University, NYC.", "All rights reserved.", " ", #else "Copyright (C) 1985, %s,", " The Trustees of Columbia University in the City of New York.", " All rights reserved.", " ", "Redistribution and use in source and binary forms, with or without", "modification, are permitted provided that the following conditions", "are met:", " ", " + Redistributions of source code must retain the above copyright", " notice, this list of conditions and the following disclaimer.", " ", " + Redistributions in binary form must reproduce the above copyright", " notice, this list of conditions and the following disclaimer in", " the documentation and/or other materials provided with the", " distribution.", " ", " + Neither the name of Columbia University nor the names of its", " contributors may be used to endorse or promote products derived", " from this software without specific prior written permission.", " ", "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS", "\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT", "LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR", "A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT", "HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,", "SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT", "LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,", "DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY", "THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT", "(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE", "OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.", #endif /* pdp11 */ #ifdef OS2 "Portions Copyright (C) 2002-2005, Secure Endpoints Inc, New York NY USA.", "Portions Copyright (C) 1995, Oy Online Solutions Ltd., Jyvaskyla, Finland.", #endif /* OS2 */ #ifdef CK_AUTHENTICATION "Portions Copyright (C) 1990, Massachusetts Institute of Technology.", #ifdef CK_ENCRYPTION "Portions Copyright (C) 1991, 1993 Regents of the University of California.", "Portions Copyright (C) 1991, 1992, 1993, 1994, 1995 by AT&T.", "Portions Copyright (C) 1995, 1997, Eric Young .", #endif /* CK_ENCRYPTION */ #ifdef CK_SRP "Portions Copyright (C) 1997, Stanford University.", #endif /* CK_SRP */ #endif /* CK_AUTHENTICATION */ #ifndef pdp11 " ", "For further information, visit the Kermit Project website:", "http://www.columbia.edu/kermit/ .", #endif /* pdp11 */ ""}; /* Windows IKSD copyright used to be separate */ char *wiksdcpr = (char *) copyright; /* DOCUMENTATION: "Using C-Kermit" by Frank da Cruz and Christine M. Gianone, Digital Press / Butterworth-Heinemann, Woburn MA, USA. Second edition (1997), ISBN 1-55558-164-1. Order from Digital Press: +1 (800) 366-2665 Or from Columbia University: +1 (212) 854-3703 For Kermit 95, also: "Kermit 95" by Christine M. Gianone and Frank da Cruz, Manning Publications, Greenwich CT, USA (1998) - Online. ACKNOWLEDGMENTS: The Kermit file transfer protocol was developed at the Columbia University Center for Computing Activities (CUCCA), which was since renamed to Columbia University Academic Information Systems (AcIS) and after that Columbia University Information Technology (CUIT). Kermit is named after Kermit the Frog, star of the television series THE MUPPET SHOW; the name is used by permission of Henson Associates, Inc. Thanks to at least the following people for their contributions to this program over the years, and apologies to anyone who was inadvertantly omitted: Chris Adie, Edinburgh U, Scotland (OS/2) Robert Adsett, University of Waterloo, Canada Larry Afrin, Clemson U Russ Allbery, Stanford U Jeffrey Altman, Columbia University Greg Andrews, Telebit Corp Barry Archer, U of Missouri Robert Andersson, International Systems A/S, Oslo, Norway Chris Armstrong, Brookhaven National Lab (OS/2) William Bader, Software Consulting Services, Nazareth, PA Fuat Baran, Columbia U Stan Barber, Rice U Jim Barbour, U of Colorado Donn Baumgartner, Dell Ian Beckwith, Debian Project Nelson Beebe, U of Utah Gerry Belanger, Cognitronics Karl Berry, UMB Mark Berryman, SAIC Dean W Bettinger, SUNY Gary Bilkus Peter Binderup, Denmark David Bolen, Advanced Networks and Services, Inc. Joop Bonen Marc Boucher, U of Montreal Charles Brooks, EDN Bob Brown Mike Brown, Purdue U Rob Brown Jack Bryans, California State U at Long Beach Mark Buda, DEC (VMS) Fernando Cabral, Padrao iX, Brasilia Bjorn Carlsson, Stockholm University Computer Centre QZ, Sweden Bill Catchings, (formerly of) Columbia U Bob Cattani, Columbia U CS Dept Davide Cervone, Rochester U Seth Chaiklin, Denmark John Chandler, Harvard U / Smithsonian Astronomical Observatory Bernard Chen, UCLA Andrew A Chernov, RELCOM Team, Moscow John L Chmielewski, AT&T, Lisle, IL Howard Chu, U of Michigan Bill Coalson, McDonnell Douglas Bertie Coopersmith, London Christian Corti Chet Creider, U of Western Ontario Alan Crosswell, Columbia U Jeff Damens, (formerly of) Columbia U Mark Davies, Bath U, UK Sin-itirou Dezawa, Fujifilm, Japan Joe R. Doupnik, Utah State U Frank Dreano, Honeywell John Dunlap, U of Washington Alex Dupuy, SMART.COM David Dyck, John Fluke Mfg Co. Stefaan A. Eeckels, Eurokom, Luxembourg Nick Efthymiou Paul Eggert, Twin Sun, Inc., El Segundo, CA Bernie Eiben, DEC Peter Eichhorn, Assyst International Kristoffer Eriksson, Peridot Konsult AB, Oerebro, Sweden John R. Evans, IRS, Kansas City Glenn Everhart, RCA Labs Charlie Finan, Cray Research Herm Fischer, Encino, CA (extensive contributions to version 4.0) Carl Fongheiser, CWRU Mike Freeman, Bonneville Power Authority Carl Friedberg Marcello Frutig, Catholic University, Sao Paulo, Brazil (X.25 support) Hirofumi Fujii, Japan Nat'l Lab for High Energy Physics, Tokyo (Kanji) Chuck Fuller, Westinghouse Corporate Computer Services Andy Fyfe, Caltech Christine M. Gianone, Columbia U John Gilmore, UC Berkeley Madhusudan Giyyarpuram, HP Rainer Glaschick, Siemens AG, Paderborn William H. Glass German Goldszmidt, IBM Chuck Goodhart, NASA Alistair Gorman, New Zealand Richard Gration, ADFA, Australia Chris Green, Essex U, UK Alan Grieg, Dundee Tech, Scotland Yekta Gursel, MIT Jim Guyton, Rand Corp Michael Haertel Bruno Haible Bob Hain, UMN Marion Hakanson, ORST Richard Hamilton John Hamilston, Iowa State U Simon Hania, Netherlands Stan Hanks, Rice U. Ken Harrenstein, SRI Eugenia Harris, Data General (AOS/VS) David Harrison, Kingston Warren Corp Lucas Hart, Oregon State University James Harvey, Indiana/Purdue U (VMS) Rob Healey Chuck Hedrick, Rutgers U Ron Heiby, Technical Systems Division, Motorola Computer Group Steve Hemminger, Tektronix Christian Hemsing, RWTH Aachen, Germany (OS-9) Randolph Herber, US DOE, Andrew Herbert, Monash Univ, Australia Marcus Herbert, Germany Mike Hickey, ITI Dan Hildebrand, QNX Software Systems Inc, Kanata, ON (QNX) R E Hill Stephan Hoffman-Emden Sven Holmstrom, ABB Utilities AB, Sweden Bill Homer, Cray Research Ray Hunter, The Wollongong Group Randy Huntziger, National Library of Medicine Larry Jacobs, Transarc Steve Jenkins, Lancaster University, UK Dave Johnson, Gradient Technologies Mark B Johnson, Apple Computer Jyke Jokinen, Tampere University of Technology, Finland (QNX) Eric F Jones, AT&T Luke Jones, AT&T Peter Jones, U of Quebec Montreal Phil Julian, SAS Institute Peter Kabal, U of Quebec Mic Kaczmarczik, U of Texas at Austin Sergey Kartashoff, Inst. of Precise Mechanics & Computer Equipment, Moscow Howie Kaye, Columbia U Rob Kedoin, Linotype Co, Hauppauge, NY (OS/2) Phil Keegstra Mark Kennedy, IBM Terry Kennedy, St Peter's College, Jersey City, NJ (VMS and more) "Carlo Kid", Technical University of Delft, Netherlands Tim Kientzle Paul Kimoto, Cornell U Douglas Kingston, morgan.com Lawrence Kirby, Wiltshire, UK Tom Kloos, Sequent Computer Systems Guenter Knauf Jim Knutson, U of Texas at Austin John T. Kohl (BSDI) Scott Kramer, SRI International, Menlo Park, CA John Kraynack, US Postal Service David Kricker, Encore Computer Thomas Krueger, UWM Bo Kullmar, ABC Klubben, Stockholm, and Central Bank of Sweden, Kista R. Brad Kummer, AT&T Bell Labs, Atlanta, GA John Kunze, UC Berkeley David Lane, BSSI / BellSouth (Stratus VOS, X.25) Bob Larson, USC (OS-9) Bert Laverman, Groningen U, Netherlands Steve Layton David Lawyer, UC Irvine Jason Lehr David LeVine, National Semiconductor Corporation Daniel S. Lewart, UIUC S.O. Lidie, Lehigh U Tor Lillqvist, Helsinki U, Finland David-Michael Lincke, U of St Gallen, Switzerland Robert Lipe (for SCO makefile entries & advice) Dean Long Mike Long, Analog Devices, Norwood MA Kevin Lowey, U of Saskatchewan (OS/2) Andy Lowry, Columbia U James Lummel, Caprica Telecomputing Resources (QNX) Lewis McCarthy David MacKenzie, Environmental Defense Fund, U of Maryland John Mackin, University of Sidney, Australia Martin Maclaren, Bath U, UK Chris Maio, Columbia U CS Dept Montserrat Mane, HP, Grenoble, France Fulvio Marino, Olivetti, Ivrea, Italy Arthur Marsh, dircsa.org.au Peter Mauzey, Lucent Technologies Tye McQueen, Utah State U Ted Medin Hellmuth Michaelis, Hanseatischer Computerservice GmbH, Hamburg, Germany Leslie Mikesell, American Farm Bureau Todd Miller, Courtesan Consulting Gary Mills Martin Minow, DEC (VMS) Pawan Misra, Bellcore Ken Mizialko, IBM, Manassas, VA Wolfgang Moeller, DECUS Germany Ray Moody, Purdue U Bruce J Moore, Allen-Bradley Co, Highland Heights, OH (Atari ST) Steve Morley, Convex Peter Mossel, Columbia U Tony Movshon, NYU Lou Muccioli, Swanson Analysis Systems Dan Murphy Neal P. Murphy, Harsof Systems, Wonder Lake IL Gary Mussar John Nall, FSU Jack Nelson, U of Pittsburgh Jim Noble, Planning Research Corporation (Macintosh) Ian O'Brien, Bath U, UK Melissa O'Neill, SFU John Owens Thomas Pinkl, Health Business Systems Inc. Michael Pins, Iowa Computer Aided Engineering Network Andre' Pirard, University of Liege, Belgium Paul Placeway, Ohio State U Piet W. Plomp, ICCE, Groningen University, Netherlands Ken Poulton, HP Labs Manfred Prange, Oakland U Christopher Pratt, APV Baker, UK Frank Prindle, NADC Tony Querubin, U of Hawaii Jean-Pierre Radley Anton Rang Mike Rechtman Scott Ribe Alan Robiette, Oxford University, UK Michel Robitaille, U of Montreal (Mac) Huw Rogers, Schweizerische Kreditanstalt, Zuerich Nigel Roles, Cambridge, England Kai Uwe Rommel, Technische Universitaet Muenchen (OS/2) Larry Rosenman (Amiga) Jay Rouman, U of Michigan Jack Rouse, SAS Institute (Data General and/or Apollo) Stew Rubenstein, Harvard U (VMS) Gerhard Rueckle, FH Darmstadt, Fb. E/Automatisierungstechnik John Santos, EG&H Bill Schilit, Columbia U Ulli Schlueter, RWTH Aachen, Germany (OS-9, etc) Michael Schmidt, U of Paderborn, Germany Eric Schnoebelen, Convex Benn Schreiber, DEC Dan Schullman, DEC (modems, DIAL command, etc) John Schultz, 3M Steven Schultz, Contel (PDP-11) Steven Schweda APPP Scorer, Leeds Polytechnic, UK Gordon Scott, Micro Focus, Newbury UK Gisbert W. Selke, WIdO, Bonn, Germany Kijal Shah David Singer, IBM Almaden Research Labs David Sizeland, U of London Medical School Fridrik Skulason, Iceland Rick Sladkey (Linux) Dave Slate Bradley Smith, UCLA Fred Smith, Merk / Computrition Richard S Smith, Cal State Ryan Stanisfer, UNT Bertil Stenstroem, Stockholm University Computer Centre (QZ), Sweden James Sturdevant, CAP GEMENI AMERICA, Minneapolis Peter Svanberg, Royal Techn. HS, Sweden James R. Swenson, Accu-Weather, Inc. Ted T'so, MIT (Linux) Andy Tanenbaum, Vrije U, Amsterdam, Netherlands Seth Theriault, Columbia U Glen Thobe Markku Toijala, Helsinki U of Technology Teemu Torma, Helsinki U of Technology Linus Torvalds, Helsinki Rick Troxel, NIH Warren Tucker, Tridom Corp, Mountain Park, GA Dave Tweten, AMES-NAS G Uddeborg, Sweden Walter Underwood, Ford Aerospace Pieter Van Der Linden, Centre Mondial, Paris Ge van Geldorp, Netherlands Fred van Kempen, MINIX User Group, Voorhout, Netherlands Wayne Van Pelt, GE/CRD Mark Vasoll, Oklahoma State U (V7 UNIX) Konstantin Vinogradov, ICSTI, Moscow Paul Vixie, DEC Bernie Volz, Process Software Eduard Vopicka, Prague University of Economics, Czech Republic Martin Vorlaender Dimitri Vulis, CUNY Roger Wallace, Raytheon Stephen Walton, Calif State U, Northridge (Amiga) Jamie Watson, Adasoft, Switzerland (AIX) Rick Watson, U of Texas (Macintosh) Eric Weaver, Columbia U Scott Weikart (Association for Progressive Communications) Robert Weiner, Programming Plus, New York City Lauren Weinstein, Vortex Technlogy David Wexelblat, AT&T Clark Wierda, Illuminati Online Joachim Wiesel, U of Karlsruhe Lon Willett, U of Utah Michael Williams, UCLA Nate Williams, U of Montana David Wilson Joellen Windsor, U of Arizona Patrick Wolfe, Kuck & Associates, Inc. Gregg Wonderly, Oklahoma State U (V7 UNIX) Farrell Woods, Concurrent (formerly Masscomp) Dave Woolley, CAP Communication Systems, London Jack Woolley, SCT Corp Frank Wortner Ken Yap, formerly of U of Rochester John Zeeff, Ann Arbor, MI */ #include "ckcker.h" /* Kermit symbols */ #include "ckcnet.h" /* Network symbols */ #ifdef CK_SSL #include "ck_ssl.h" #endif /* CK_SSL */ #ifndef NOSPL #include "ckuusr.h" #endif /* NOSPL */ #ifdef OS2ONLY #define INCL_VIO /* Needed for ckocon.h */ #include #undef COMMENT #endif /* OS2ONLY */ #ifdef NT #include #include #include "ckntap.h" #endif /* NT */ #ifndef NOSERVER /* Text message definitions.. each should be 256 chars long, or less. */ #ifdef MINIX char *srvtxt = "\r\n\ Entering server mode.\r\n\0"; #else #ifdef OLDMSG /* It seems there was a large installation that was using C-Kermit 5A(165) or thereabouts, which had deployed thousands of MS-DOS Kermit scripts in scattered locations that looked for strings in the old server message, which changed in 5A(183), August 1992. */ char *srvtxt = "\r\n\ C-Kermit server starting. Return to your local machine by typing\r\n\ its escape sequence for closing the connection, and issue further\r\n\ commands from there. To shut down the C-Kermit server, issue the\r\n\ FINISH or BYE command and then reconnect.\n\ \r\n\0"; #else #ifdef OSK char *srvtxt = "\r\012\ Entering server mode. If your local Kermit software is menu driven, use\r\012\ the menus to send commands to the server. Otherwise, enter the escape\r\012\ sequence to return to your local Kermit prompt and issue commands from\r\012\ there. Use SEND and GET for file transfer. Use REMOTE HELP for a list of\r\012\ other available services. Use BYE or FINISH to end server mode.\r\012\0"; #else /* UNIX, VMS, AOS/VS, and all others */ char *srvtxt = "\r\n\ Entering server mode. If your local Kermit software is menu driven, use\r\n\ the menus to send commands to the server. Otherwise, enter the escape\r\n\ sequence to return to your local Kermit prompt and issue commands from\r\n\ there. Use SEND and GET for file transfer. Use REMOTE HELP for a list of\r\n\ other available services. Use BYE or FINISH to end server mode.\r\n\0"; #endif /* OSK */ #endif /* OLDMSG */ #endif /* MINIX */ #else /* server mode disabled */ char *srvtxt = ""; #endif /* NOSERVER */ int initflg = 0; /* sysinit() has executed... */ int howcalled = I_AM_KERMIT; /* How I was called */ int hmtopline = 0; int quitting = 0; /* I'm in the act of quitting */ #ifdef IKSDCONF char * iksdconf = IKSDCONF; /* IKSD configuration file */ int iksdcf = 0; /* Has IKSD c.f. been processed? */ #endif /* IKSDCONF */ int srvcdmsg = 0; /* [Server] CD message */ char * cdmsgfile[8] = { NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL }; char * cdmsgstr = NULL; char * ckcdpath = NULL; #ifdef NLCHAR /* Text-file line terminator */ CHAR feol = NLCHAR; #else CHAR feol = 0; #endif /* NLCHAR */ int fblksiz = DBLKSIZ; /* File blocksize */ int frecl = DLRECL; /* File record length */ int frecfm = XYFF_S; /* File record format (default = stream) */ int forg = XYFO_S; /* File organization (sequential) */ int fcctrl = XYFP_N; /* File carriage control (ctrl chars) */ int filecase = FILECASE; /* Case matters in filenames */ int stathack = 1; /* Fast directory lookups by default */ char uidbuf[UIDBUFLEN] = { NUL, NUL }; /* User ID buffer */ int cfilef = 0; /* Application ("kerbang") file flag */ char cmdfil[CKMAXPATH + 1] = { NUL, NUL }; /* Application file name */ int haveurl = 0; /* URL given on command line */ #ifndef NOXFER /* Multi-protocol support */ struct ck_p ptab[NPROTOS] = { /* Initialize the Kermit part ... */ { "Kermit", DRPSIZ, /* Receive packet size */ DSPSIZ, /* Send packet size */ 0, /* Send-packet-size-set flag */ DFWSIZ, /* Window size */ #ifdef NEWDEFAULTS PX_CAU, /* Control char unprefixing... */ #else PX_ALL, #endif /* NEWDEFAULTS */ #ifdef VMS /* Default filename collision action */ XYFX_X, /* REPLACE for VAX/VMS */ #else XYFX_B, /* BACKUP for everybody else */ #endif /* VMS */ #ifdef OS2 /* Flag for file name conversion */ XYFN_L, /* Literal for OS2 */ #else XYFN_C, /* Converted for others */ #endif /* OS2 */ PATH_OFF, /* Send pathnames OFF */ PATH_AUTO, /* Receive pathnames AUTO */ NULL, /* Host receive initiation string (binary) */ NULL, /* Host receive initiation string (text) */ NULL, /* Host server string */ NULL, /* External protocol send command (binary) */ NULL, /* External protocol send command (text) */ NULL, /* External protocol receive command (bin) */ NULL } /* External protocol receive command (txt) */ #ifdef CK_XYZ , {"XMODEM", 128,128,-1,-1, 1,-1,-1,0,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"XMODEM-CRC",128,128,-1,-1, -1,-1,-1,0,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"YMODEM", -1, -1,-1,-1, -1,-1,-1,0,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"YMODEM-g", -1, -1,-1,-1, -1,-1,-1,0,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"ZMODEM", -1, -1,-1,-1,PX_WIL,-1,-1,0,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL}, {"Other", -1, -1,-1,-1, -1,-1,-1,0,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL} #endif /* CK_XYZ */ }; /* Declarations for Send-Init Parameters */ int spsiz = DSPSIZ, /* Current packet size to send */ spmax = DSPSIZ, /* Biggest packet size we can send */ lastspmax = DSPSIZ, /* Send-packet size last used */ spsizr = DSPSIZ, /* Send-packet size requested */ spsizf = 0, /* Flag to override size negotiation */ rpsiz = DRPSIZ, /* Biggest we want to receive */ urpsiz = DRPSIZ, /* User-requested receive pkt size */ maxrps = MAXRP, /* Maximum incoming long packet size */ maxsps = MAXSP, /* Maximum outbound l.p. size */ maxtry = MAXTRY, /* Maximum retries per packet */ wslots = 1, /* Window size currently in use */ wslotr = DFWSIZ, /* Window size from SET WINDOW */ wslotn = 1, /* Window size negotiated in S-pkt */ timeouts = 0, /* For statistics reporting */ spackets = 0, /* ... */ rpackets = 0, /* ... */ retrans = 0, /* ... */ crunched = 0, /* ... */ wmax = 0, /* ... */ wcur = 0, /* ... */ srvidl = 0, /* Server idle timeout */ srvdis = 1, /* Server file xfer display */ srvtim = DSRVTIM, /* Server command wait timeout */ srvping = 1, /* Server keepalive */ /* timint is the timeout interval I use when waiting for a packet. pkttim is the SET RECEIVE TIMEOUT value, sent to the other Kermit. rtimo is the SET SEND TIMEOUT value. rtimo is the initial value of timint. timint is changed by the value in the incoming negotiation packet unless a SET SEND TIMEOUT command was given. */ timint = DMYTIM, /* Timeout interval I use */ pkttim = URTIME, /* Timeout I want you to use */ rtimo = DMYTIM, /* Normal packet wait timeout */ timef = 0, /* Flag to override what you ask */ #ifdef CK_TIMERS rttflg = 1, /* Use dynamic round-trip timers */ #else rttflg = 0, /* Use fixed timer */ #endif /* CK_TIMERS */ mintime = 1, /* Minimum timeout */ maxtime = 0, /* Maximum timeout */ npad = MYPADN, /* How much padding to send */ mypadn = MYPADN, /* How much padding to ask for */ bctr = DFBCT, /* Block check type requested */ bctu = 1, /* Block check type used */ bctl = 1, /* Block check length */ bctf = 0, /* Block check type 3 forced on all */ c_save = -1, /* Block check saving and restoring */ ss_save = -1, /* Slow-start saving and restoring */ ebq = MYEBQ, /* 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 = MYRPTQ, /* Repeat prefix */ rptflg = 0, /* Repeat processing flag */ rptena = 1, /* Repeat processing enabled */ xfrcan = 1, /* Transfer cancellation enabled */ xfrint = 1, /* Transfer interruption enabled */ xfrchr = 3, /* Transfer cancel char = Ctrl-C */ xfrnum = 3, /* Need three of them by default */ g_xfrxla = -1; char * xfrmsg = NULL; /* Message for f.t. display screen */ #endif /* NOXFER */ #ifdef NOCSETS int xfrxla = 0; /* Character-set translation */ #else int xfrxla = 1; /* enabled or disabled */ #endif /* NOCSETS */ int havelfs = 0; /* Large file support available */ #ifndef NOXFER int epktflg = 0; /* E-PACKET command active */ int capas = 9, /* Position of Capabilities */ lpcapb = 2, /* Long Packet capability */ lpcapr = 1, /* requested */ lpcapu = 0, /* used */ swcapb = 4, /* Sliding Window capability */ swcapr = 1, /* requested (allowed) */ swcapu = 0, /* used */ atcapb = 8, /* Attribute capability */ atcapr = 1, /* requested */ atcapu = 0, /* used */ rscapb = 16, /* RESEND capability */ rscapr = 1, /* requested by default */ rscapu = 0, /* used */ lscapb = 32, /* Locking Shift capability */ lscapr = 1, /* requested by default */ lscapu = 0; /* used */ /* Flags for whether to use particular attributes */ int atenci = 1, /* Encoding in */ atenco = 1, /* Encoding out */ atdati = 1, /* Date in */ atdato = 1, /* Date out */ atdisi = 1, /* Disposition in/out */ atdiso = 1, atleni = 1, /* Length in/out (both kinds) */ atleno = 1, atblki = 1, /* Blocksize in/out */ atblko = 1, attypi = 1, /* File type in/out */ attypo = 1, atsidi = 1, /* System ID in/out */ atsido = 1, atsysi = 1, /* System-dependent parameters in/out */ atsyso = 1; int dispos = 0; /* Disposition */ #ifdef CK_PERMS int atlpri = 1, atlpro = 1, atgpri = 1, atgpro = 1; #endif /* CK_PERMS */ int atfrmi = 1, /* Record Format in/out */ atfrmo = 1; #ifdef STRATUS int atcrei = 1, /* Creator ID in/out */ atcreo = 1, atacti = 1, /* Account in/out */ atacto = 1; #endif /* STRATUS */ int sprmlen = -1; /* Send/Receive protocol parameter */ int rprmlen = -1; /* string length limits */ int sendipkts = 1; /* Send I packets */ CHAR padch = MYPADC, /* Padding character to send */ mypadc = MYPADC, /* Padding character to ask for */ seol = MYEOL, /* End-Of-Line character to send */ eol = MYEOL, /* End-Of-Line character to look for */ ctlq = CTLQ, /* Control prefix in incoming data */ myctlq = CTLQ, /* Outbound control character prefix */ myrptq = MYRPTQ; /* Repeat prefix I want to use */ int rptmin = 3; /* Repeat-count minimum */ int usepipes = 0, /* Used for xfer to/from pipes */ g_usepipes = -1; char * filefile = NULL; /* File containing list of filenames */ /* CD message filename list */ char whoareu[16] = { NUL, NUL }; /* System ID of other Kermit */ int sysindex = -1; /* and index to its system ID struct */ int myindex = -1; int wearealike = 0; /* 2 Kermits have compatible sysids */ char * cksysid = /* My system ID */ #ifdef UNIX "U1" #else #ifdef VMS "D7" #else #ifdef OSK "UD" #else #ifdef AMIGA "L3" #else #ifdef MAC "A3" #else #ifdef OS2 #ifdef NT "UN" #else /* NT */ "UO" #endif /* NT */ #else /* OS2 */ #ifdef datageneral "F3" #else #ifdef GEMDOS "K2" #else #ifdef STRATUS "MV" #else "" #endif /* STRATUS */ #endif /* GEMDOS */ #endif /* datageneral */ #endif /* OS2 */ #endif /* MAC */ #endif /* AMIGA */ #endif /* OSK */ #endif /* VMS */ #endif /* UNIX */ ; int oopts = -1; /* O-Packet Options */ int omode = -1; /* O-Packet Transfer Mode */ int oname = -1; /* O-Packet Filename Options */ int opath = -1; /* O-Packet Pathname Options */ struct zattr iattr; /* Incoming file attributes */ #ifdef VMS /* VMS labeled file default options - name only. */ int lf_opts = LBL_NAM; #else #ifdef OS2 /* OS/2 labeled file default options, all attributes but archived. */ unsigned long int lf_opts = LBL_EXT|LBL_HID|LBL_RO|LBL_SYS; #else int lf_opts = 0; #endif /* OS2 */ #endif /* VMS */ /* Packet-related variables */ int pktnum = 0, /* Current packet number */ sndtyp = 0, /* Type of packet just sent */ rcvtyp = 0, /* Type of packet just received */ rsn, /* Received packet sequence number */ rln, /* Received packet length */ size, /* Current size of output pkt data */ osize, /* Previous output packet data size */ maxsize, /* Max size for building data field */ spktl = 0, /* Length packet being sent */ rpktl = 0, /* Length of packet just received */ pktpaus = 0, /* Interpacket pause interval, msec */ rprintf, /* REMOTE PRINT flag */ rmailf, /* MAIL flag */ xferstat = -1, /* Status of last transaction */ filestatus = 0; /* Status of last file transfer */ CHAR pktmsgbuf[PKTMSGLEN+1]; CHAR *epktmsg = pktmsgbuf; #ifdef pdp11 int srvcmdlen = MAXRP; /* srvcmd buffer length */ #else #ifdef DYNAMIC int srvcmdlen = MAXRP; #else int srvcmdlen = 0; #endif /* DYNAMIC */ #endif /* pdp11 */ CHAR #ifdef pdp11 srvcmdbuf[MAXRP+4], *srvcmd = srvcmdbuf, #else #ifdef DYNAMIC *srvcmd = (CHAR *)0, /* Where to decode server command */ #else srvcmdbuf[MAXRP+4], *srvcmd = srvcmdbuf, #endif /* DYNAMIC */ #endif /* pdp11 */ padbuf[96], /* Buffer for send-padding */ *recpkt, *rdatap, /* Pointer to received packet data */ *data = (CHAR *)0, /* Pointer to send-packet data */ *srvptr, /* Pointer to srvcmd */ mystch = SOH, /* Outbound packet-start character */ stchr = SOH; /* Incoming packet-start character */ /* File-related variables */ #ifndef NOMSEND /* Multiple SEND */ struct filelist * filehead = NULL; /* SEND list */ struct filelist * filetail = NULL; struct filelist * filenext = NULL; int addlist = 0; #endif /* NOMSEND */ char filnam[CKMAXPATH + 1]; /* Name of current file. */ char ofilnam[CKMAXPATH + 1]; /* Original name. */ int pipesend = 0; /* Nonzero if sending from pipe */ #ifdef PIPESEND char * sndfilter = NULL; /* Send and receive filters */ char * rcvfilter = NULL; #endif /* PIPESEND */ char ** sndarray = NULL; /* SEND /ARRAY pointer and range */ #ifndef NOSPL int sndxlo = -1, sndxhi = -1, sndxin = -1; #endif /* NOSPL */ #endif /* NOXFER */ #ifndef NOSERVER int ngetpath = 0; /* GET search path */ int fromgetpath = 0; char * getpath[MAXGETPATH]; char * x_user = NULL; /* Server login information */ char * x_passwd = NULL; char * x_acct = NULL; #endif /* NOSERVER */ int x_login = 0; /* Login required */ int x_logged = 0; /* User is logged in */ extern int timelimit; #ifdef CK_LOGIN int logintimo = 300; /* Login timeout */ char * userfile = NULL; /* Forbidden user file */ #endif /* CK_LOGIN */ #ifdef IKSD char * anonfile = NULL; /* Anonymous login init file */ char * anonroot = NULL; /* Anonymous file-system root */ int iks_timo = 300; /* 5 minutes idle timo */ int iks_retry = 3; /* 3 attempts at login */ #endif /* IKSD */ #ifdef CKSYSLOG extern VOID zsyslog(); extern int ckxlogging, ckxsyslog; #endif /* CKSYSLOG */ CK_OFF_T fsize = (CK_OFF_T)0, /* Size of current file */ sendstart = (CK_OFF_T)0, /* SEND start position */ calibrate = (CK_OFF_T)0; /* Nonzero if calibration run */ int nzxopts = 0; /* Options for nzxpand() */ int nfils = 0; /* Number of files in file group */ int wildena = 1; /* Wildcard expansion enabled */ #ifdef UNIX int wildxpand = 0; /* Who expands wildcards, 0=Kermit.. */ #else /* UNIX */ #ifdef STRATUS int wildxpand = 1; /* 1=Shell. */ #endif /* STRATUS */ #endif /* UNIX */ #ifdef UNIXOROSK int matchdot = 0; /* Whether to match dot files */ #else int matchdot = 1; #endif /* UNIXOROSK */ int matchfifo = 0; /* Whether to match FIFO "files" */ int clfils = 0; /* Flag for command-line files */ int stayflg = 0; /* Flag for "stay", i.e. "-S" */ int xfinish = 0; /* Flag for FINISH = EXIT */ long ztusec = -1L; /* Used with ztime() */ long ztmsec = -1L; /* Ditto */ /* Communication device / connection variables */ char ttname[TTNAMLEN+1]; /* Name of communication device */ #ifdef MAC int connected = 0; /* True if connected */ int startconnected; /* initial state of connected */ #endif /* MAC */ long speed = -1L; /* Communication device speed */ int wasclosed = 0; /* Connection was just closed */ int whyclosed = WC_REMO; /* why it was closed */ int qnxportlock = 0; /* QNX port locking on/off */ #ifndef CLSONDISC #define CLSONDISC 0 #endif /* CLSONDISC */ int cxflow[CXT_MAX+1]; /* See initflow() */ #ifndef NOSHOW char * floname[] = { /* Flow control names */ "none", "xon/xoff", "rts/cts", "dtr/cd", "etx/ack", "string", "xxx1", "xxx2", "dtr/cts", "keep", "auto" }; int nfloname = (sizeof(floname) / sizeof(char *)); char * cxname[] = { /* Connection type names */ "remote", "direct-serial", "modem", "tcp/ip", "x.25", "decnet", "lat", "netbios", "named-pipe", "ssh", "pipe" }; int ncxname = (sizeof(cxname) / sizeof(char *)); #endif /* NOSHOW */ int parity = DEFPAR, /* Parity specified, 0,'e','o',etc */ hwparity = 0, /* Hardware parity for serial port */ stopbits = -1, /* Stop bits for serial port */ clsondisc = CLSONDISC, /* Serial port close on disconnect */ autopar = 0, /* Automatic parity change flag */ sosi = 0, /* Shift-In/Out flag */ flow = 0, /* Flow control (see initflow()) */ autoflow = 1, /* Automatic flow control */ turn = 0, /* Line turnaround handshake flag */ turnch = XON, /* Line turnaround character */ duplex = 0, /* Duplex, full by default */ escape = DFESC, /* Escape character for connect */ ckdelay = DDELAY, /* Initial delay before sending */ tnlm = 0; /* Terminal newline mode */ /* Networks for SET HOST */ #ifdef BIGBUFOK #define MYHOSTL 1024 #else #define MYHOSTL 100 #endif /* BIGBUFOK */ char myhost[MYHOSTL]; /* Local host name */ int network = 0; /* Network vs serial connection */ int inserver = 0; /* Running as an Internet server */ int isguest = 0; /* User is anonymous */ char * clienthost = NULL; /* Peer host name or address */ int tcp_incoming = 0; /* Incoming TCP connection? */ #ifdef NETCONN #ifdef TCPSOCKET int nettype = NET_TCPB; /* Default network type */ #else #ifdef SUNX25 int nettype = NET_SX25; #else #ifdef IBMX25 int nettype = NET_IX25; #else #ifdef HPX25 int nettype = NET_HX25; #else #ifdef STRATUSX25 int nettype = NET_VX25; #else #ifdef DECNET int nettype = NET_DEC; #else #ifdef SUPERLAT int nettype = NET_SLAT; #else int nettype = NET_NONE; #endif /* SUPERLAT */ #endif /* DECNET */ #endif /* STRATUSX25 */ #endif /* HPX25 */ #endif /* IBMX25 */ #endif /* SUNX25 */ #endif /* TCPSOCKET */ #else /* NETCONN */ int nettype = NET_NONE; #endif /* NETCONN */ #ifdef ANYX25 int revcall = 0; /* X.25 reverse call not selected */ int closgr = -1; /* X.25 closed user group */ int cudata = 0; /* X.25 call user data not specified */ char udata[MAXCUDATA]; /* X.25 call user data */ #ifdef IBMX25 /* I was unable to find any pre-defined MAX values for x25 addresses - the addresses that I've seen have been around 10-12 characters 32 is probably enough, 64 is hopefully safe for everyone. */ x25addr_t local_nua = {'\0'}; /* local x.25 address */ x25addr_t remote_nua = {'\0'}; /* remote x.25 address */ char x25name[32] = {'\0'}; /* x25 device name, sx25a0 or sx25a1 */ char x25dev[64] = "/dev/x25pkt"; /* x25 device in /dev */ int x25port = 0; /* port used for X.25 - AIX only */ #endif /* IBMX25 */ #ifndef IBMX25 /* This condition is unrelated to the above IBMX25 condition. IBM X.25 doesn't have PAD support. */ CHAR padparms[MAXPADPARMS+1]; /* X.3 parameters */ #endif /* IBMX25 */ #endif /* ANYX25 */ /* Other items */ int isinterrupted = 0; /* Used in exception handling */ int what = W_INIT; /* What I am doing */ int lastxfer = 0; /* Last transfer (send or receive) */ extern int mdmtyp; /* Modem (/network) type */ #ifdef NT extern int StartedFromDialer; #ifdef NTSIG extern int TlsIndex; #endif /* NTSIG */ #ifdef NTASM unsigned long ESPToRestore; /* Ditto */ #endif /* NTASM */ #endif /* NT */ #ifdef OS2PM int os2pm = 0; /* OS/2 Presentation Manager flag */ #endif /* OS2PM */ /* Terminal screen size, if known, -1 means unknown. */ #ifdef OS2 #include "ckocon.h" #ifdef KUI int tt_rows[VNUM] = {24,24,25,1}; /* Rows (height) */ int tt_cols[VNUM] = {80,80,80,80}; /* Columns (width) */ int cmd_rows = 24, cmd_cols = 80; /* Command/console screen dimensions */ #else /* KUI */ int tt_rows[VNUM] = {-1,24,25,1}; /* Rows (height) */ int tt_cols[VNUM] = {-1,80,80,80}; /* Columns (width) */ int cmd_rows = -1, cmd_cols = -1; /* Command/console screen dimensions */ #endif /* KUI */ int k95stdio = 0; /* Stdio threads */ int tt_bell = XYB_AUD | XYB_SYS; /* BELL AUDIBLE (system sounds) */ #else /* OS2 */ int tt_rows = -1; /* Rows (height) */ int tt_cols = -1; /* Columns (width) */ int cmd_rows = 24, cmd_cols = 80; /* Command/console screen dimensions */ int tt_bell = XYB_AUD; /* BELL ON */ #endif /* OS2 */ int tt_print = 0; /* Transparent print disabled */ int tt_escape = 1; /* Escaping back is enabled */ int tt_scroll = 1; /* Scrolling operations are enabled */ int tn_exit = 0; /* Exit on disconnect */ int exitonclose = 0; /* Exit on close */ int exithangup = 1; /* Hangup on exit */ int haveline = 0; /* SET LINE or SET HOST in effect */ int tlevel = -1; /* Take-file command level */ int hints = 1; /* Whether to give hints */ #ifdef NOLOCAL int remonly = 1; /* Remote-mode-only advisory (-R) */ int nolocal = 1; /* Remote-only strictly enforced */ #else int remonly = 0; int nolocal = 0; int cx_status = 0; /* CONNECT return status */ #endif /* NOLOCAL */ #ifndef NOSPL extern int cmdlvl; /* Command level */ extern int maclvl; /* Macro invocation level */ #endif /* NOSPL */ int protocol = PROTO_K; /* File transfer protocol = Kermit */ #ifdef NEWDEFAULTS int prefixing = PX_CAU; #else int prefixing = PX_ALL; #endif /* NEWDEFAULTS */ extern short ctlp[]; /* Control-prefix table */ int carrier = CAR_AUT; /* Pay attention to carrier signal */ int cdtimo = 0; /* Carrier wait timeout */ int xitsta = GOOD_EXIT; /* Program exit status */ #ifdef VMS /* Default filename collision action */ int fncact = XYFX_X; /* REPLACE for VMS */ #else int fncact = XYFX_B; /* BACKUP for everybody else */ #endif /* VMS */ int fncsav = -1; /* For saving & restoring the above */ int bgset = -1; /* BACKGROUND mode set explicitly */ int cmdint = 1; /* Interrupts are allowed */ #ifdef UNIX int xsuspend = DFSUSP; /* Whether SUSPEND command, etc, */ #else /* is to be allowed. */ int xsuspend = 0; #endif /* UNIX */ /* Statistics variables */ CK_OFF_T flci, /* Characters from line, current file */ flco, /* Chars to line, current file */ tlci, /* Chars from line in transaction */ tlco, /* Chars to line in transaction */ ffc, /* Chars to/from current file */ tfc; /* Chars to/from files in transaction */ long filcnt, /* Number of files in transaction */ filrej, /* Number of files rejected in transaction */ cps = 0L, /* Chars/sec last transfer */ peakcps = 0L, /* Peak chars/sec last transfer */ ccu, /* Control chars unprefixed in transaction */ ccp, /* Control chars prefixed in transaction */ rptn; /* Repeated characters compressed */ int tsecs = 0; /* Seconds for transaction */ int fsecs = 0; /* Per-file timer */ #ifdef GFTIMER CKFLOAT fpfsecs = 0.0, /* Floating point per-file timer */ fptsecs = 0.0; /* and per-transaction timer */ #endif /* GFTIMER */ /* Flags */ int deblog = 0, /* Debug log is open */ debok = 1, /* Debug log is not disabled */ debxlen = 54, /* Default length for debug strings */ debses = 0, /* Flag for DEBUG SESSION */ debtim = 0, /* Include timestamp in debug log */ debmsg = 0, /* Debug messages on/off */ pktlog = 0, /* Flag for packet logging */ seslog = 0, /* Session logging */ dialog = 0, /* DIAL logging */ tralog = 0, /* Transaction logging */ tlogfmt = 1, /* Transaction log format (verbose) */ tlogsep = (int)',', /* Transaction log field separator */ displa = 0, /* File transfer display on/off */ stdouf = 0, /* Flag for output to stdout */ stdinf = 0, /* Flag for input from stdin */ xflg = 0, /* Flag for X instead of F packet */ hcflg = 0, /* Doing Host command */ dest = DEST_D, /* Destination for packet data */ zchkod = 0, /* zchko() should work for dirs too? */ zchkid = 0, /* zchki() should work for dirs too? */ /* If you change this, also see struct ptab above... */ #ifdef OS2 /* Flag for file name conversion */ fncnv = XYFN_L, /* Default is Literal in OS/2, */ f_save = XYFN_L, /* (saved copy of same) */ #else fncnv = XYFN_C, /* elsewhere Convert them */ f_save = XYFN_C, /* (ditto) */ #endif /* OS2 */ fnspath = PATH_OFF, /* Send file path */ fnrpath = PATH_AUTO, /* Receive file path */ fackpath = 1, /* Send back path in ACK to F */ binary = XYFT_B, /* Default file transfer mode */ b_save = XYFT_B, /* Saved file mode */ eofmethod = 0, /* EOF detection method (length) */ #ifdef OS2 cursor_save = -1, /* Cursor state */ #endif /* OS2 */ xfermode = XMODE_A, /* Transfer mode, manual or auto */ xfiletype = -1, /* Transfer only text (or binary) */ recursive = 0, /* Recursive directory traversal */ nolinks = 2, /* Don't follow symbolic links */ skipbup = 0, /* Skip backup files when sending */ sendmode = SM_SEND, /* Which type of SEND operation */ slostart = 1, /* Slow start (grow packet lengths) */ cmask = 0377, /* CONNECT (terminal) byte mask */ fmask = 0377, /* File byte mask */ ckwarn = 0, /* Flag for file warning */ quiet = 0, /* Be quiet during file transfer */ local = 0, /* 1 = local mode, 0 = remote mode */ cxtype = CXT_REMOTE, /* Connection type */ server = 0, /* Flag for I Am Server */ query = 0, /* Flag for Query active */ justone = 0, /* Server should do Just One command */ urserver = 0, /* Flag for You Are Server */ bye_active = 0, /* Flag for BYE command active */ diractive = 0, /* Flag for DIRECTORY command active */ cdactive = 0, /* Flag for CD command active */ cflg = 0, /* Connect before transaction */ cnflg = 0, /* Connect after transaction */ cxseen = 0, /* Flag for cancelling a file */ czseen = 0, /* Flag for cancelling file group */ fatalio = 0, /* Flag for fatal i/o error */ discard = 0, /* Flag for file to be discarded */ keep = SET_AUTO, /* Keep incomplete files = AUTO */ unkcs = 1, /* Keep file w/unknown character set */ #ifdef VMS filepeek = 0, /* Inspection of files */ #else #ifdef datgeneral filepeek = 0, #else filepeek = 1, #endif /* datageneral */ #endif /* VMS */ nakstate = 0, /* In a state where we can send NAKs */ dblchar = -1, /* Character to double when sending */ moving = 0, /* MOVE = send, then delete */ reliable = SET_AUTO, /* Nonzero if transport is reliable */ xreliable = -1, setreliable = 0, urclear = 0, /* Nonzero for clear channel to you */ clearrq = SET_AUTO, /* SET CLEARCHANEL value */ cleared = 0, streaming = 0, /* Nonzero if streaming is active */ streamok = 0, /* Nonzero if streaming negotiated */ streamrq = SET_AUTO, /* SET STREAMING value */ streamed = -1; /* Whether we streamed last time */ char * snd_move = NULL; /* Move file after sending it */ char * snd_rename = NULL; /* Rename file after sending it */ char * rcv_move = NULL; /* Move file after receiving it */ char * rcv_rename = NULL; /* Rename file after receiving it */ char * g_snd_move = NULL; char * g_snd_rename = NULL; char * g_rcv_move = NULL; char * g_rcv_rename = NULL; #ifdef CK_TRIGGER char *tt_trigger[TRIGGERS] = { NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL }; CHAR *tt_trmatch[TRIGGERS] = { NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL }; char *triggerval = NULL; #endif /* CK_TRIGGER */ int ckxlogging = 0; /* Flag for syslogging active */ int ikdbopen = 0; /* Flag for IKSD database active */ int dbinited = 0; /* Flag for IKSDB record init'd */ #ifndef CKSYSLOG int ckxsyslog = 0; /* Logging level 0 */ #else #ifdef SYSLOGLEVEL int ckxsyslog = SYSLOGLEVEL; /* Logging level specified */ #else int ckxsyslog = SYSLG_DF; /* Default logging level */ #endif /* SYSLOGLEVEL */ #endif /* CKSYSLOG */ #ifndef NOHELP #ifndef NOCMDL _PROTOTYP( VOID iniopthlp, (void) ); /* Command-line help initializer */ #endif /* NOCMDL */ #endif /* NOHELP */ _PROTOTYP( VOID getexedir, (void) ); _PROTOTYP( int putnothing, (char) ); #ifdef IKSD _PROTOTYP( VOID doiksdinit, (void) ); _PROTOTYP( VOID iksdinit, (void) ); _PROTOTYP( VOID doiklog, (void) ); _PROTOTYP( int dbinit, (void) ); #endif /* IKSD */ /* Variables passed from command parser to protocol module */ #ifndef NOSPL #ifndef NOICP #ifdef CK_APC _PROTOTYP( VOID apconect, (void) ); #endif /* CK_APC */ #ifdef OS2 extern int initvik; #endif /* OS2 */ #endif /* NOICP */ #endif /* NOSPL */ char *clcmds = NULL; /* Pointer to command-line commands */ #ifndef NOSETKEY extern KEY *keymap; extern MACRO *macrotab; #endif /* NOSETKEY */ #ifndef NOPUSH int nopush = 0; /* PUSH enabled */ #else int nopush = 1; /* PUSH disabled */ #endif /* NOPUSH */ CHAR sstate = (CHAR) 0; /* Starting state for automaton */ CHAR zstate = (CHAR) 0; /* For remembering sstate */ char * printername = NULL; /* NULL if printer not redirected */ int printpipe = 0; /* For SET PRINTER */ int noprinter = 0; #ifndef NOXFER char *cmarg = ""; /* Pointer to command data */ char *cmarg2 = ""; /* Pointer to 2nd command data */ char **cmlist; /* Pointer to file list in argv */ #ifdef CK_AUTODL /* Autodownload */ int autodl = 1; /* Enabled by default */ #else int autodl = 0; /* (or if not implemented). */ #endif /* CK_AUTODL */ int adl_err = 1; /* 1 = stop on error */ #ifdef KUI int adl_ask = 1; /* 1 = file dialog on autodownload */ #else int adl_ask = 0; /* 0 = no file dialog */ #endif /* KUI */ #ifdef OS2 /* AUTODOWNLOAD parameters */ int adl_kmode = ADL_PACK, /* Match Packet to signal download */ adl_zmode = ADL_PACK; char * adl_kstr = NULL; /* KERMIT Download String */ char * adl_zstr = NULL; /* ZMODEM Download String */ #endif /* OS2 */ int remfile = 0, rempipe = 0, remappd = 0; /* REMOTE output redirection */ char * remdest = NULL; #ifndef NOSERVER /* Server services: 0 = disabled 1 = enabled in local mode 2 = enabled in remote mode 3 = enabled in both local and remote modes only as initial (default) values. */ int en_xit = 2; /* EXIT */ int en_cwd = 3; /* CD/CWD */ int en_cpy = 3; /* COPY */ int en_del = 2; /* DELETE */ int en_mkd = 3; /* MKDIR */ int en_rmd = 2; /* RMDIR */ int en_dir = 3; /* DIRECTORY */ int en_fin = 3; /* FINISH */ int en_get = 3; /* GET */ #ifndef NOPUSH int en_hos = 2; /* HOST enabled */ #else int en_hos = 0; /* HOST disabled */ #endif /* NOPUSH */ int en_ren = 3; /* RENAME */ int en_sen = 3; /* SEND */ int en_set = 3; /* SET */ int en_spa = 3; /* SPACE */ int en_typ = 3; /* TYPE */ int en_who = 3; /* WHO */ #ifdef datageneral /* Data General AOS/VS can't do this */ int en_bye = 0; /* BYE */ #else int en_bye = 2; /* PCs in local mode... */ #endif /* datageneral */ int en_asg = 3; /* ASSIGN */ int en_que = 3; /* QUERY */ int en_ret = 2; /* RETRIEVE */ int en_mai = 3; /* MAIL */ int en_pri = 3; /* PRINT */ int en_ena = 3; /* ENABLE */ #else int en_xit = 0, en_cwd = 0, en_cpy = 0, en_del = 0, en_mkd = 0, en_rmd = 0, en_dir = 0, en_fin = 0, en_get = 0, en_hos = 0, en_ren = 0, en_sen = 0, en_set = 0, en_spa = 0, en_typ = 0, en_who = 0, en_bye = 0, en_asg = 0, en_que = 0, en_ret = 0, en_mai = 0, en_pri = 0, en_ena = 0; #endif /* NOSERVER */ #endif /* NOXFER */ /* Miscellaneous */ char **xargv; /* Global copies of argv */ int xargc; /* and argc */ int xargs; /* an immutable copy of argc */ char *xarg0; /* and of argv[0] */ char *pipedata; /* Pointer to -P (pipe) data */ extern char *dftty; /* Default tty name from ck?tio.c */ extern int dfloc; /* Default location: remote/local */ extern int dfprty; /* Default parity */ extern int dfflow; /* Default flow control */ #ifdef TNCODE extern int tn_deb; #endif /* TNCODE */ /* Buffered file input and output buffers. See getpkt() in ckcfns.c and zoutdump() in the system-dependent file i/o module (usually ck?fio.c). */ #ifndef DYNAMIC /* Now we allocate them dynamically, see getiobs() below. */ char zinbuffer[INBUFSIZE], zoutbuffer[OBUFSIZE]; #endif /* DYNAMIC */ char *zinptr, *zoutptr; int zincnt, zoutcnt; int zobufsize = OBUFSIZE; int zofbuffer = 1; int zofblock = 1; #ifdef SESLIMIT int seslimit = 0; #endif /* SESLIMIT */ #ifdef CK_AUTHENTICATION #include "ckuath.h" #endif /* CK_AUTHENTICATION */ _PROTOTYP( int getiobs, (VOID) ); /* M A I N -- C-Kermit main program */ #include #ifndef NOCCTRAP #include #include "ckcsig.h" ckjmpbuf cmjbuf; #ifdef GEMDOS /* Special for Atari ST */ cc_clean(); /* This can't be right? */ #endif /* GEMDOS */ #endif /* NOCCTRAP */ #ifndef NOXFER /* Info associated with a system ID */ struct sysdata sysidlist[] = { /* Add others as needed... */ { "0", "anonymous", 0, NUL, 0, 0, 0 }, { "A1", "Apple II", 0, NUL, 0, 0, 3 }, /* fix this */ { "A3", "Macintosh", 1, ':', 0, 2, 1 }, { "D7", "VMS", 0, ']', 1, 0, 0 }, { "DA", "RSTS/E", 0, ']', 1, 0, 3 }, /* (i think...) */ { "DB", "RT11", 0, NUL, 1, 0, 3 }, /* (maybe...) */ { "F3", "AOS/VS", 1, ':', 0, 0, 2 }, { "I1", "VM/CMS", 0, NUL, 0, 0, 0 }, { "I2", "MVS/TSO", 0, NUL, 0, 0, 0 }, { "I4", "MUSIC", 0, NUL, 0, 0, 0 }, { "I7", "CICS", 0, NUL, 0, 0, 0 }, { "I9", "MVS/ROSCOE", 0, NUL, 0, 0, 0 }, { "K2", "Atari ST", 1, '\\', 1, 0, 3 }, { "L3", "Amiga", 1, '/', 1, 0, 2 }, { "MV", "Stratus VOS", 1, '>', 0, 1, 0 }, { "N3", "Apollo Aegis", 1, '/', 0, 3, 2 }, { "U1", "UNIX", 1, '/', 0, 3, 2 }, { "U8", "MS-DOS", 1, '\\', 1, 0, 3 }, { "UD", "OS-9", 1, '/', 0, 3, 2 }, { "UN", "Windows-32", 1, '\\', 1, 2, 3 }, { "UO", "OS/2", 1, '\\', 1, 2, 3 } }; static int nxxsysids = (sizeof(sysidlist) / sizeof(struct sysdata)); /* Given a Kermit system ID code, return the associated name string */ /* and some properties of the filenames... */ char * getsysid(s) char * s; { /* Get system-type name */ int i; if (!s) return(""); for (i = 0; i < nxxsysids; i++) if (!strcmp(sysidlist[i].sid_code,s)) return(sysidlist[i].sid_name); return(s); } int getsysix(s) char *s; { /* Get system-type index */ int i; if (!s) return(-1); for (i = 0; i < nxxsysids; i++) if (!strcmp(sysidlist[i].sid_code,s)) return(i); return(-1); } #endif /* NOXFER */ /* Tell if a pathname is absolute (versus relative) */ /* This should be parceled out to each of the ck*fio.c modules... */ /* VMS isabsolute() is now in ckvfio.c. */ #ifndef VMS int isabsolute(path) char * path; { int rc = 0; int x; if (!path) return(0); if (!*path) return(0); x = (int) strlen(path); debug(F111,"isabsolute",path,x); #ifdef UNIX if (*path == '/' #ifdef DTILDE || *path == '~' #endif /* DTILDE */ ) rc = 1; #else /* def UNIX */ #ifdef OS2 if (*path == '/' || *path == '\\') rc = 1; else if (isalpha(*path) && x > 2) if (*(path+1) == ':' && (*(path +2) == '/' || *(path+2) == '\\')) rc = 1; #else /* def OS2 */ #ifdef AMIGA if (*path == '/' #ifdef DTILDE || *path == '~' #endif /* DTILDE */ ) rc = 1; #else /* def AMIGA */ #ifdef OSK if (*path == '/' #ifdef DTILDE || *path == '~' #endif /* DTILDE */ ) rc = 1; #else /* def OSK */ #ifdef datageneral if (*path == ':') rc = 1; #else /* def datageneral */ #ifdef MAC rc = 0; /* Fill in later... */ #else /* def MAC */ #ifdef STRATUS rc = 0; /* Fill in later... */ #else /* def STRATUS */ #ifdef GEMDOS if (*path == '/' || *path == '\\') rc = 1; else if (isalpha(*path) && x > 1) if (*(path+1) == ':') rc = 1; #endif /* GEMDOS */ #endif /* STRATUS */ #endif /* MAC */ #endif /* datageneral */ #endif /* OSK */ #endif /* AMIGA */ #endif /* OS2 */ #endif /* UNIX */ debug(F101,"isabsolute rc","",rc); return(rc); } #endif /* ndef VMS */ /* See if I have direct access to the keyboard */ int is_a_tty(n) int n; { #ifdef UNIX extern int ttfdflg; if (ttfdflg > 0) return(1); #endif /* UNIX */ #ifdef KUI return 1; #else /* KUI */ #ifdef NT if (isWin95()) return(1); else return(_isatty(n)); #else #ifdef IKSD if (inserver) return(1); else #endif /* IKSD */ return(isatty(n)); #endif /* NT */ #endif /* KUI */ } #ifndef NOXFER VOID initxlist() { extern char * sndexcept[], * rcvexcept[]; int i; for (i = 0; i < NSNDEXCEPT; i++) { sndexcept[i] = NULL; rcvexcept[i] = NULL; } } #endif /* NOXFER */ /* Initialize flow control table */ VOID initflow() { /* Default values for flow control */ #ifdef VMS /* for each kind of connection. */ /* The VMS telnet terminal driver treats "none" as request to lose chars */ cxflow[CXT_REMOTE] = FLO_XONX; /* Remote mode... */ #else #ifdef HPUX /* Ditto for HP-UX */ cxflow[CXT_REMOTE] = FLO_XONX; /* Remote mode... */ #else /* The temptation is to make this one FLO_KEEP but don't!!! */ /* It totally wrecks binary-file transfer when coming in via Telnet. */ /* In UNIX at least... */ cxflow[CXT_REMOTE] = FLO_NONE; #endif /* HPUX */ #endif /* VMS */ #ifdef VMS cxflow[CXT_DIRECT] = FLO_XONX; /* Direct serial connections... */ #else cxflow[CXT_DIRECT] = FLO_NONE; #endif /* VMS */ #ifdef CK_RTSCTS cxflow[CXT_MODEM] = FLO_RTSC; /* Modem connections... */ #else #ifdef VMS cxflow[CXT_MODEM] = FLO_XONX; #else cxflow[CXT_MODEM] = FLO_NONE; #endif /* VMS */ #endif /* CK_RTSCTS */ #ifdef VMS cxflow[CXT_TCPIP] = FLO_XONX; /* TCP/IP connections... */ #else cxflow[CXT_TCPIP] = FLO_NONE; #endif /* VMS */ cxflow[CXT_SSH] = FLO_NONE; cxflow[CXT_X25] = FLO_NONE; /* Other kinds of networks... */ cxflow[CXT_DECNET] = FLO_XONX; cxflow[CXT_LAT] = FLO_XONX; cxflow[CXT_NETBIOS] = FLO_NONE; cxflow[CXT_NPIPE] = FLO_NONE; cxflow[CXT_PIPE] = FLO_NONE; flow = cxflow[cxtype]; /* Initial flow setting. */ debug(F101,"initflow","",flow); } #ifndef NOXFER /* Initialize file transfer protocols */ VOID initproto(y, upbstr, uptstr, srvstr, sndbstr, sndtstr, rcvbstr, rcvtstr) int y; char * upbstr, * uptstr, * srvstr, * sndbstr, * sndtstr, * rcvbstr, * rcvtstr; /* initproto */ { if (upbstr) /* Convert null strings */ if (!*upbstr) /* to null pointers */ upbstr = NULL; if (uptstr) /* Convert null strings */ if (!*uptstr) /* to null pointers */ uptstr = NULL; if (sndbstr) if (!*sndbstr) sndbstr = NULL; if (sndtstr) if (!*sndtstr) sndtstr = NULL; if (rcvbstr) if (!*rcvbstr) rcvbstr = NULL; if (rcvtstr) if (!*rcvtstr) rcvtstr = NULL; if (srvstr) if (!*srvstr) srvstr = NULL; protocol = y; /* Set protocol */ if (ptab[protocol].rpktlen > -1) urpsiz = ptab[protocol].rpktlen; if (ptab[protocol].spktflg > -1) spsizf = ptab[protocol].spktflg; if (ptab[protocol].spktlen > -1) { spsiz = ptab[protocol].spktlen; debug(F101,"initproto spsiz","",spsiz); if (spsizf) { spsizr = spmax = spsiz; debug(F101,"initproto spsizr","",spsizr); } } if (ptab[protocol].winsize > -1) wslotr = ptab[protocol].winsize; if (ptab[protocol].prefix > -1) prefixing = ptab[protocol].prefix; if (ptab[protocol].fnca > -1) fncact = ptab[protocol].fnca; if (ptab[protocol].fncn > -1) fncnv = ptab[protocol].fncn; if (ptab[protocol].fnsp > -1) fnspath = ptab[protocol].fnsp; if (ptab[protocol].fnrp > -1) fnrpath = ptab[protocol].fnrp; makestr(&(ptab[protocol].h_b_init),upbstr); makestr(&(ptab[protocol].h_t_init),uptstr); makestr(&(ptab[protocol].h_x_init),srvstr); makestr(&(ptab[protocol].p_b_scmd),sndbstr); makestr(&(ptab[protocol].p_t_scmd),sndtstr); makestr(&(ptab[protocol].p_b_rcmd),rcvbstr); makestr(&(ptab[protocol].p_t_rcmd),rcvtstr); } #endif /* NOXFER */ #ifndef NOCMDL VOID #ifdef CK_ANSIC docmdline(void * threadinfo) #else /* CK_ANSIC */ docmdline(threadinfo) VOID * threadinfo; #endif /* CK_ANSIC */ { #ifdef NTSIG setint(); if (threadinfo) { /* Thread local storage... */ TlsSetValue(TlsIndex,threadinfo); debug( F100, "docmdline called with threadinfo block", "", 0 ); } else debug( F100, "docmdline threadinfo is NULL","",0); #endif /* NTSIG */ #ifdef CK_LOGIN #ifdef NT #ifdef IKSD if (inserver) setntcreds(); #endif /* IKSD */ #endif /* NT */ #endif /* CK_LOGIN */ proto(); /* Take any requested action, then */ if (!quiet) /* put cursor back at left margin, */ conoll(""); #ifndef NOLOCAL if (cnflg) { /* Re-connect if requested */ cnflg = 0; doconect(0,0); if (ttchk() < 0) dologend(); } #endif /* NOLOCAL */ #ifdef NTSIG ckThreadEnd(threadinfo); #endif /* NTSIG */ return; } void ikslogin() { if (sstelnet #ifdef IKSD || inserver /* Internet server */ #endif /* IKSD */ ) { char *s; extern int fdispla; /* File-transfer display format */ extern char * ikprompt; /* IKSD prompt */ #ifdef IKSD #ifdef CK_LOGIN if (inserver) { x_login = 1; /* Login required */ x_logged = 0; /* Not logged in yet */ cmsetp(ikprompt); /* Set up IKSD's prompt */ #ifndef NOSERVER en_mai = 0; /* MAIL is disabled */ en_who = 0; /* REMOTE WHO is disabled */ en_hos = 0; /* REMOTE HOST is disabled */ en_pri = 0; /* PRINT is disabled */ #endif /* NOSERVER */ } else { x_login = 0; /* Login not required */ x_logged = 1; /* Already logged in */ } #endif /* CK_LOGIN */ #endif /* IKSD */ nolocal = 1; /* SET LINE/HOST not allowed */ fdispla = XYFD_N; /* No file-transfer display */ #ifdef NETCONN clienthost = ckgetpeer(); /* Get client's hostname */ debug(F110,"ikslogin clienthost",clienthost,0); #endif /* NETCONN */ ztime(&s); /* Get current date and time */ #ifdef CK_LOGIN #ifdef CK_AUTHENTICATION if (x_login) { x_logged = ck_tn_auth_valid(); /* Did Telnet Auth succeed? */ debug(F111,"ikslogin","x_logged",x_logged); #ifdef NT /* On Windows 9x, we do not have the ability in */ /* zvuser() at present to determine if the name */ /* approved in a Kerberos principal is really a */ /* an account in the Windows Access Control List */ if (isWin95() && x_logged == AUTH_VALID && (ck_tn_authenticated() != AUTHTYPE_NTLM) #ifdef CK_SRP && (ck_tn_authenticated() != AUTHTYPE_SRP) #endif /* CK_SRP */ ) { auth_finished(AUTH_USER); x_logged = AUTH_USER; printf("WARNING:\r\n"); printf( " The Telnet authentication method used cannot provide for automated\r\n"); printf( " login to Windows 95 or Windows 98. A password must be entered\r\n"); printf( " locally to validate your userid. Telnet authentication (and encryption)\r\n" ); printf( " can be used to validate the host (and protect the privacy of your password.)\ \r\n" ); } #endif /* NT */ if (x_logged == AUTH_VALID) { #ifdef CK_SSL if ((ssl_active_flag || tls_active_flag) && (!TELOPT_U(TELOPT_AUTHENTICATION) || ck_tn_authenticated() == AUTHTYPE_NULL || ck_tn_authenticated() == AUTHTYPE_AUTO) ) { #ifdef SSL_KRB5 if (tls_is_krb5(0)) { printf("Authenticated using Kerberos 5\r\n"); #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_LI && ckxlogging) { extern char szUserNameAuthenticated[]; cksyslog(SYSLG_LI, 1, "AUTH_VALID", "Kerberos 5", szUserNameAuthenticated ); } #endif /* CKSYSLOG */ } else #endif /* SSL_KRB5 */ { printf("Authenticated using X.509 certificate\r\n"); #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_LI && ckxlogging) { extern char szUserNameAuthenticated[]; cksyslog(SYSLG_LI, 1, "AUTH_VALID", "X.509 certificate", szUserNameAuthenticated ); } #endif /* CKSYSLOG */ } } else #endif /* CK_SSL */ { printf("Authenticated using %s\r\n", AUTHTYPE_NAME(ck_tn_authenticated())); #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_LI && ckxlogging) { extern char szUserNameAuthenticated[]; cksyslog(SYSLG_LI, 1, "AUTH_VALID", AUTHTYPE_NAME(ck_tn_authenticated()), szUserNameAuthenticated ); } #endif /* CKSYSLOG */ } zvuser(uidbuf); if (zvpass("") == 0) x_logged = 0; } else if (x_logged == AUTH_USER && !strcmp(uidbuf,"anonymous")) { extern char szUserNameAuthenticated[]; zvuser(uidbuf); debug(F110,"szUserNameAuthenticated", szUserNameAuthenticated,0); if (zvpass(szUserNameAuthenticated) == 0) { /* Anonymous login failed. Force a username prompt. */ x_logged = 0; uidbuf[0] = '\0'; } else { #ifdef CK_SSL if ((ssl_active_flag || tls_active_flag) && (!TELOPT_U(TELOPT_AUTHENTICATION) || ck_tn_authenticated() == AUTHTYPE_NULL || ck_tn_authenticated() == AUTHTYPE_AUTO)) { printf("Authenticated using X.509 certificate\r\n"); #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_LI && ckxlogging) { extern char szUserNameAuthenticated[]; cksyslog(SYSLG_LI, 1, "AUTH_USER", "X.509 certificate", szUserNameAuthenticated ); } #endif /* CKSYSLOG */ } else #endif /* CK_SSL */ { printf("Authenticated using %s\r\n", AUTHTYPE_NAME(ck_tn_authenticated()) ); #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_LI && ckxlogging) { cksyslog(SYSLG_LI, 1, "AUTH_USER", AUTHTYPE_NAME(ck_tn_authenticated()), szUserNameAuthenticated ); } #endif /* CKSYSLOG */ } } } else { #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_LI && ckxlogging && x_logged == AUTH_USER) { extern char szUserNameAuthenticated[]; cksyslog(SYSLG_LI, 1, "AUTH_USER", AUTHTYPE_NAME(ck_tn_authenticated()), szUserNameAuthenticated ); } #endif /* CKSYSLOG */ x_logged = 0; if (!strcmp("(unknown)",uidbuf) #ifdef NT || !stricmp("administrator",uidbuf) #ifdef UNIX || !strcmp("root",uidbuf) #else #ifdef Plan9 || !strcmp("root",uidbuf) #else #ifdef OSK || !strcmp("root",uidbuf) #endif /* OSK */ #endif /* Plan9 */ #endif /* UNIX */ #endif /* NT */ ) uidbuf[0] = '\0'; } } #endif /* CK_AUTHENTICATION */ #endif /* CK_LOGIN */ #ifdef IKSD if (inserver) printf("\r\nInternet Kermit Service ready at %s%s\r\n",s,versio); else #endif /* IKSD */ printf("\r\nC-Kermit ready at %s%s\r\n",s,versio); if (*myhost) printf("%s\r\n", myhost); printf("\r\n"); } #ifdef CK_LOGIN #ifdef IKSD if (inserver) { int i; extern int arg_x; /* Flag for '-x' on command line */ #ifndef NOSPL extern struct mtab *mactab; /* For ON_LOGIN macro. */ extern int nmac; #endif /* NOSPL */ debug(F110,"MAIN clienthost",clienthost,0); srvidl = timelimit = logintimo; /* For interactive login */ rtimer(); /* Reset timer */ for (i = 0; i < iks_retry && !x_logged; i++) { /* Count retries */ if (gtimer() > logintimo) break; #ifdef TNCODE tn_wait("login loop"); tn_push(); #endif /* TNCODE */ debug(F101,"MAIN LOGIN try","",i); what = W_NOTHING; /* Because proto() changes this */ #ifdef IKS_OPTION debug(F111,"MAIN LOGIN", "TELOPT_SB(TELOPT_KERMIT).kermit.me_start", TELOPT_SB(TELOPT_KERMIT).kermit.me_start ); /* Kermit server negotiated */ if (TELOPT_SB(TELOPT_KERMIT).kermit.me_start) { debug(F101,"IKSD starting in server mode","",0); arg_x = 1; /* Enter server mode */ sstate = 'x'; #ifdef IKSDPOPBACK justone = 1; /* Execute one command at a time. */ #endif /* IKSDPOPBACK */ proto(); /* Enter protocol if requested. */ #ifdef NTSIG ck_ih(); #endif /* NTSIG */ if (x_logged) /* Logged in */ break; } else { /* Not in client/server mode */ #endif /* IKS_OPTION */ debug(F101,"IKSD starting with Username prompt","",0); x_logged = ckxlogin((CHAR *)uidbuf,NULL,NULL,1); if (sstate) { /* Received a packet at prompt */ #ifdef IKSDPOPBACK justone = 1; /* Go handle it */ #endif /* IKSDPOPBACK */ proto(); } if (!x_logged) { /* In case we are at the prompt... */ printf("Access denied.\n"); uidbuf[0] = '\0'; /* Forget the name if we have one */ } #ifdef IKS_OPTION } #endif /* IKS_OPTION */ } srvidl = timelimit = iks_timo; /* Reset command timelimit */ debug(F101,"MAIN LOGIN","",x_logged); if (!x_logged) { /* Logins failed. */ if (TELOPT_SB(TELOPT_KERMIT).kermit.me_start) errpkt((CHAR *)"Login Timeout"); msleep(500); doexit(BAD_EXIT,0); } what = W_NOTHING; /* Stay in known state */ #ifndef NOSERVER if (isguest) { en_pri = 0; /* No printing for anonymous users */ en_mai = 0; /* No email for anonymous users */ en_mkd = 0; /* Or directory creation */ en_rmd = 0; /* Or directory removal */ en_ena = 0; /* Or ENABLing DISABLEd items */ } #endif /* NOSERVER */ #ifndef NOSPL /* If a macro named "on_login" is defined, execute it. Also remove it from the macro table so the user cannot see what it does. Execute it as part of the iksd.conf file. */ if (nmac) { /* Any macros defined? */ int k; /* Yes */ char * cmd = "on_login"; /* MSVC 2.x compiler error */ k = mlook(mactab,cmd,nmac); /* Look up "on_login" */ if (k >= 0) { /* If found, */ #ifdef IKSDCONF int saved = iksdcf; iksdcf = 0; #endif /* IKSDCONF */ if (dodo(k,"",0) > -1) /* set it up, */ parser(1); /* execute it */ #ifdef IKSDCONF iksdcf = saved; #endif /* IKSDCONF */ delmac(cmd,1); /* and delete it */ } } #endif /* NOSPL */ } /* if (inserver) */ #else /* CK_LOGIN */ if (inserver) srvidl = timelimit = iks_timo; /* Set idle limits for IKS */ #endif /* CK_LOGIN */ #endif /* IKSD */ } VOID #ifdef CK_ANSIC failcmdline(void * foo) #else /* CK_ANSIC */ failcmdline(foo) VOID * foo; #endif /* CK_ANSIC */ { #ifdef GEMDOS cc_clean(); #endif /* GEMDOS */ #ifndef NOLOCAL if (cnflg) doconect(0,0); /* connect again if requested. */ if (ttchk() < 0) dologend(); #endif /* NOLOCAL */ } #endif /* NOCMDL */ #ifndef NOICP VOID #ifdef CK_ANSIC dotakeini(void * threadinfo) /* Execute init file. */ #else /* CK_ANSIC */ dotakeini(threadinfo) VOID * threadinfo; /* Execute init file. */ #endif /* CK_ANSIC */ /* dotakeini */ { #ifdef NTSIG setint(); if (threadinfo) { /* Thread local storage... */ TlsSetValue(TlsIndex,threadinfo); debug(F100, "dotakeini called with threadinfo block","", 0); } else debug(F100, "dotakeini - threadinfo is NULL", "", 0); #endif /* NTSIG */ #ifdef CK_LOGIN #ifdef NT #ifdef IKSD if (inserver) setntcreds(); #endif /* IKSD */ #endif /* NT */ #endif /* CK_LOGIN */ cmdini(); /* Sets tlevel */ debug(F111,"dotakeini","inserver",inserver); debug(F111,"dotakeini","sstelnet",sstelnet); #ifdef COMMENT /* Wrong place for this... */ #ifndef NOXFER #ifdef CK_FAST dofast(); /* By now FAST defaults should be OK */ #endif /* CK_FAST */ #endif /* NOXFER */ #endif /* COMMENT */ doinit(); /* Now do the initialization file */ debug(F101,"main executing init file","",tlevel); while (tlevel > -1) { sstate = (CHAR) parser(1); /* Execute one command at a time. */ if (sstate) proto(); /* Enter protocol if requested. */ #ifdef NTSIG ck_ih(); #endif /* NTSIG */ } debug(F101,"main exits init file","",tlevel); #ifdef NTSIG ckThreadEnd(threadinfo); #endif /* NTSIG */ return; } VOID #ifdef CK_ANSIC failtakeini(void * threadinfo) #else /* CK_ANSIC */ failtakeini(threadinfo) VOID * threadinfo; #endif /* CK_ANSIC */ /* failtakeini */ { #ifdef GEMDOS cc_clean(); /* Atari: Clean up after ^C-trap. */ #endif /* GEMDOS */ fixcmd(); if (!cfilef) { conoll("Interrupted during initialization or \ command-line processing."); conoll("C-Kermit quitting..."); } doexit(BAD_EXIT,-1); /* Exit with bad status. */ } VOID #ifdef CK_ANSIC doicp(void * threadinfo) #else /* CK_ANSIC */ doicp(threadinfo) VOID * threadinfo; #endif /* CK_ANSIC */ /* doicp */ { #ifdef NTSIG setint(); if (threadinfo) { /* Thread local storage... */ if (!TlsSetValue(TlsIndex,threadinfo)) debug(F101,"doicp TlsSetValue failed","",GetLastError()); debug(F101, "doicp a threadinfo block - TlsIndex", "", TlsIndex); } else { debug(F100, "doicp received a null threadinfo", "", 0); } #endif /* NTSIG */ #ifdef CK_LOGIN #ifdef NT #ifdef IKSD if (inserver) setntcreds(); #endif /* IKSD */ #endif /* NT */ #endif /* CK_LOGIN */ #ifdef MAC while (1) { extern char *lfiles; /* Fake pointer cast */ if (connected) { debug(F100, "doicp: calling macparser", "", 0); sstate = newparser(1, 1, 0L); /* ignore null command state */ if (sstate == 'n') sstate = '\0'; if (sstate) proto(); } else { /* * process take files the finder gave us. */ if ((tlevel == -1) && lfiles) startlfile(); debug(F100, "doicp: calling parser", "", 0); sstate = (CHAR) parser(0); if (sstate == 'c') /* if MAC connect */ sstate = 0; if (sstate) proto(); } } #else /* Not MAC */ #ifndef NOSPL /* If interactive commands were given on the command line (using the -C "command, command, ..." option), assign them to a macro called "cl_commands", then execute the macro and leave it defined for subsequent re-execution if desired. */ if (clcmds) { /* Check for -C commands */ int x; x = addmac("cl_commands",clcmds); /* Put macro in table */ if (x > -1) { /* If successful, */ dodo(x,NULL,CF_CMDL); /* set up for macro execution */ while (maclvl > -1) { /* Loop getting macro commands. */ sstate = (CHAR) parser(1); if (sstate) proto(); /* Enter protocol if requested. */ #ifdef NTSIG ck_ih(); #endif /* NTSIG */ } } debug(F100,"doicp calling herald","",0); herald(); } #endif /* NOSPL */ while(1) { /* Loop getting commands. */ sstate = (CHAR) parser(0); if (sstate) proto(); /* Enter protocol if requested. */ #ifdef NTSIG ck_ih(); #endif /* NTSIG */ } #ifdef NTSIG ckThreadEnd(threadinfo); #endif /* NTSIG */ #endif /* MAC */ } VOID #ifdef CK_ANSIC failicp(void * threadinfo) #else /* CK_ANSIC */ failicp(threadinfo) VOID * threadinfo; #endif /* CK_ANSIC */ { #ifdef GEMDOS cc_clean(); #endif /* GEMDOS */ fixcmd(); /* Pop command stacks, etc. */ clcmds = NULL; debug(F100,"ckcmai got interrupt","",0); } #endif /* NOICP */ #ifndef NOICP VOID #ifdef CK_ANSIC docmdfile(void * threadinfo) /* Execute application file */ #else /* CK_ANSIC */ docmdfile(threadinfo) VOID * threadinfo; #endif /* CK_ANSIC */ /* docmdfile */ { #ifdef NTSIG concb((char)escape); setint(); if (threadinfo) { /* Thread local storage... */ TlsSetValue(TlsIndex,threadinfo); debug(F100, "docmdfile called with threadinfo block","", 0); } else debug(F100, "docmdfile - threadinfo is NULL", "", 0); #endif /* NTSIG */ #ifdef CK_LOGIN #ifdef IKSD #ifdef NT if (inserver) setntcreds(); #endif /* NT */ #endif /* IKSD */ #endif /* CK_LOGIN */ debug(F110,"main cmdfil",cmdfil,0); #ifndef NOSPL addmac("\\%0",cmdfil); #endif /* NOSPL */ dotake(cmdfil); /* Set up the command file */ if (tlevel > -1) /* Remember we did this */ cfilef = 1; while (tlevel > -1) { /* Execute it until it runs out. */ sstate = parser(1); /* Loop getting commands. */ if (sstate) proto(); /* Enter protocol if requested. */ #ifdef NTSIG ck_ih(); #endif /* NTSIG */ } #ifdef NTSIG ckThreadEnd(threadinfo); #endif /* NTSIG */ return; } VOID #ifdef CK_ANSIC failcmdfile(void * threadinfo) #else /* CK_ANSIC */ failcmdfile(threadinfo) VOID * threadinfo; #endif /* CK_ANSIC */ /* failcmdfile */ { #ifdef GEMDOS cc_clean(); /* Atari: Clean up after ^C-trap. */ #endif /* GEMDOS */ fixcmd(); if (!cfilef) { conoll("Interrupted during initialization or \ command-line processing."); conoll("C-Kermit quitting..."); } doexit(BAD_EXIT,-1); /* Exit with bad status. */ } #endif /* NOICP */ #ifndef NOXFER VOID setprefix(z) int z; { /* Initial control-char prefixing */ #ifdef CK_SPEED int i, val; prefixing = z; ptab[protocol].prefix = prefixing; debug(F101,"setprefix","",prefixing); switch (z) { case PX_ALL: /* All */ #ifdef COMMENT /* Don't let Clear-Channel be dependent on prefixing */ clearrq = 0; /* Turn off clearchannel, fall thru */ #endif /* COMMENT */ case PX_NON: /* None */ val = (z == PX_ALL) ? 1 : 0; for (i = #ifdef UNPREFIXZERO 0 #else 1 #endif /* UNPREFIXZERO */ ; i < 32; i++) ctlp[i] = val; for (i = 127; i < 160; i++) ctlp[i] = val; ctlp[(unsigned)255] = val; if (z == PX_NON) { /* These are never safe */ if (network) { /* Assume network = telnet or rlogin */ ctlp[CR] = 1; /* Prefix CR because of NVT rules */ ctlp[XON] = ctlp[XOFF] = 1; /* Because of Telnet server */ ctlp[127] = ctlp[255] = 1; /* Telnet IAC */ ctlp[mystch] = ctlp[mystch+128] = 1; /* Kermit packet start */ } else { ctlp[CR] = ctlp[255] = ctlp[mystch] = ctlp[mystch+128] = 1; if (flow == FLO_XONX) /* Xon/Xoff forces prefixing */ ctlp[XON] = ctlp[XOFF] = ctlp[XON+128] = ctlp[XOFF+128] = 1; } } break; case PX_CAU: /* Cautious or Minimal */ #ifdef COMMENT /* Don't let CLEAR-CHANNEL be dependent on Prefixing */ clearrq = 0; /* Turn off clearchannel */ #endif /* COMMENT */ case PX_WIL: /* Minimal ("wild") */ ctlp[0] = 1; /* Does not include 0 */ for (i = 1; i < 32; i++) ctlp[i] = 0; for (i = 127; i < 160; i++) ctlp[i] = 0; ctlp[mystch] = ctlp[mystch+128] = 1; /* Kermit start of packet */ if (seol != 13) ctlp[seol] = ctlp[seol+128] = 1; /* Kermit end */ ctlp[13] = ctlp[141] = 1; /* In case of TELNET (NVT rules) */ ctlp[(unsigned)255] = 1; /* Ditto */ /* ^D, ^J, ^M, or ^U followed by tilde trigger Rlogin escape */ ctlp[4] = ctlp[4+128] = 1; /* In case of RLOGIN */ ctlp[10] = ctlp[10+128] = 1; /* In case of RLOGIN */ ctlp[21] = ctlp[21+128] = 1; /* In case of RLOGIN */ if (flow == FLO_XONX || /* Xon/Xoff forces prefixing these */ prefixing == PX_CAU || /* So does CAUTIOUS */ network) /* Networks too... */ ctlp[XON] = ctlp[XOFF] = ctlp[XON+128] = ctlp[XOFF+128] = 1; if (prefixing == PX_CAU) { /* Cautious - add some more */ #ifdef UNPREFIXZERO ctlp[0] = 1; #endif /* UNPREFIXZERO */ ctlp[3] = ctlp[16] = 1; /* ^C, DLE */ ctlp[14] = ctlp[15] = 1; /* SO/SI */ ctlp[24] = ctlp[25] = 1; /* VMS might need these */ ctlp[26] = ctlp[26+128] = 1; /* UNIX suspend */ ctlp[28] = ctlp[29] = ctlp[30] = 1; /* Assorted esc chars */ ctlp[131] = ctlp[141] = ctlp[144] = 1; /* and 8-bit versions */ ctlp[(unsigned)255] = ctlp[156] = ctlp[157] = ctlp[158] = 1; } break; } #endif /* CK_SPEED */ } #endif /* NOXFER */ VOID makever() { /* Make version string from pieces */ int x, y; char * s; #ifndef OS2 #ifndef MAC ck_s_xver = ck_s_ver; /* Fill in C-Kermit version number */ ck_l_xver = ck_l_ver; /* for UNIX, VMS, etc. */ #endif /* MAC */ #endif /* OS2 */ x = strlen(ck_s_name); y = strlen(ck_s_xver); if (y + x + 1 < CKVERLEN) { ckmakmsg(versio,CKVERLEN,ck_s_name," ",ck_s_xver,NULL); } else { ckstrncpy(versio,"C-Kermit",CKVERLEN); return; } x += y + 1; s = " OPEN SOURCE:"; /* C-Kermit 9.0 and later */ y = strlen(s); if (CKVERLEN < x + y + 1) return; ckstrncat(versio,s,CKVERLEN); x += y + 1; if (*ck_s_who) { y = strlen(ck_s_who); if (CKVERLEN < x + y + 1) return; ckstrncat(versio,"-",CKVERLEN); ckstrncat(versio,ck_s_who,CKVERLEN); } x += y + 1; y = strlen(ck_s_test); if (y > 0 && y + x + 1 < CKVERLEN) { ckstrncat(versio," ",CKVERLEN); ckstrncat(versio,ck_s_test,CKVERLEN); x += y + 1; y = strlen(ck_s_tver); if (y > 0 && y + x + 1 < CKVERLEN) { ckstrncat(versio,".",CKVERLEN); ckstrncat(versio,ck_s_tver,CKVERLEN); x += y + 1; } } y = strlen(ck_s_date); if (y > 0 && y + x + 2 < CKVERLEN) { ckstrncat(versio,", ",CKVERLEN); ckstrncat(versio,ck_s_date,CKVERLEN); } vernum = ck_l_ver; xvernum = ck_l_xver; debug(F110,"Kermit version",versio,0); } union ck_short shortbytes; /* For determining byte order */ int byteorder = 0; /* 0 = Big Endian; 1 = Little Endian */ int bigendian = 1; /* NOTE: MUST BE 0 or 1 - nothing else */ #ifndef NOSPL #define SCRIPTLEN 10240 #endif /* NOSPL */ #ifdef NETCONN #ifndef NOCMDL #ifndef NOURL VOID dourl() { int rc = 0; char * port = NULL; extern int ttnproto; extern struct urldata g_url; #ifdef COMMENT /* NOTE: debug() doesn't work yet - must use printf's */ printf("URL: %s\n",g_url.sav ? g_url.sav : "(none)"); printf("Type: %s\n",g_url.svc ? g_url.svc : "(none)"); printf("User: %s\n",g_url.usr ? g_url.usr : "(none)"); printf("Pass: %s\n",g_url.psw ? g_url.psw : "(none)"); printf("Host: %s\n",g_url.hos ? g_url.hos : "(none)"); /* printf("Port: %s\n",g_url.por ? g_url.por : "(none)"); */ printf("Path: %s\n",g_url.pth ? g_url.pth : "(none)"); #endif /* COMMENT */ if (!ckstrcmp(g_url.svc,"iksd",-1,0) || !ckstrcmp(g_url.svc,"kermit",-1,0)) { extern char pwbuf[]; extern int pwflg; #ifdef OS2 extern int pwcrypt; #endif /* OS2 */ if (!g_url.hos) { printf("?Incomplete IKSD URL\n"); doexit(BAD_EXIT,1); } if (!g_url.usr) makestr(&g_url.usr,"anonymous"); if (!g_url.psw) { char * tmpbuf = NULL; if (!(tmpbuf = (char *)malloc(1024))) fatal("dourl: out of memory"); if (!ckstrcmp(g_url.usr,"anonymous",-1,0)) { ckmakmsg(tmpbuf,1024,uidbuf,"@",myhost,NULL); makestr(&g_url.psw,tmpbuf); } else { readpass(" Password:",tmpbuf,1024); makestr(&g_url.psw,tmpbuf); } free(tmpbuf); } port = "kermit"; ttnproto = NP_TELNET; nettype = NET_TCPB; mdmtyp = -nettype; local = -1; ckstrncpy(uidbuf,g_url.usr,UIDBUFLEN); if (g_url.psw) { ckstrncpy(pwbuf,g_url.psw,PWBUFL); pwflg = 1; #ifdef OS2 pwcrypt = 0; #endif /* OS2 */ } ckmakmsg(ttname, TTNAMLEN, g_url.hos, ":", g_url.por ? g_url.por : port, NULL ); rc = ttopen(ttname,&local,mdmtyp,0); if (rc > -1) { network = 1; exitonclose = 1; #ifdef CKLOGDIAL dolognet(); #endif /* CKLOGDIAL */ } else { printf("?Connection failed: %s\n",g_url.sav); doexit(BAD_EXIT,1); } /* Also need to check here for secure authentication already done */ #ifdef NOSPL cflg = 1; #else { char * script = NULL; if (!(script = (char *)malloc(SCRIPTLEN))) fatal("dourl: out of memory"); if (!g_url.pth) { /* Write the appropriate script */ cflg = 1; ckmakxmsg(script,SCRIPTLEN, "if not eq {\\v(authstate)} {user} ", "if not eq {\\v(authstate)} {valid} { ", "remote login ", /* No path */ g_url.usr, /* Just log in and CONNECT */ " ", g_url.psw, ", if fail exit 1 {IKSD login failed} }", ", connect", NULL,NULL,NULL,NULL); /* printf("CLCMDS 1: %s\n",script); */ } else { /* does the path specify a file or a directory? */ int len = strlen(g_url.pth); if (ISDIRSEP(g_url.pth[len-1])) { ckmakxmsg(script,SCRIPTLEN, /* Directory name given */ "if not eq {\\v(authstate)} {user} \ if not eq {\\v(authstate)} {valid} { remote login ", g_url.usr, " ", g_url.psw, ", if fail exit 1 {IKSD login failed} }", ", set macro error on", ", set xfer displ brief", ", set xfer bell off", ", remote cd ", g_url.pth, ", lineout directory", ", connect" ); /* printf("CLCMDS 2: %s\n",script); */ } else { ckmakxmsg(script,SCRIPTLEN, /* Path given, try to GET */ "if not eq {\\v(authstate)} {user} \ if not eq {\\v(authstate)} {valid} { remote login ", g_url.usr, " ", g_url.psw, ", if fail exit 1 {IKSD login failed} }", ", set xfer displ brief", ", set xfer bell off", ", get ", g_url.pth, ", .rc := \\v(status)", ", if open connection bye", ", exit \\m(rc)" ); /* printf("CLCMDS 2: %s\n",script); */ } } clcmds = script; /* Make this our -C cmdline macro */ /* printf("HAVEURL=%d\n",haveurl); */ } #endif /* NOSPL */ } else { if (ckstrcmp(g_url.svc,"telnet",-1,0) && #ifdef SSHBUILTIN ckstrcmp(g_url.svc,"ssh",-1,0) && #endif /* SSHBUILTIN */ ckstrcmp(g_url.svc,"ftp",-1,0)) { printf("?Sorry, %s URLs not supported\n", g_url.svc ? g_url.svc : ""); doexit(BAD_EXIT,1); } } } #endif /* NOCMDL */ #endif /* NETCONN */ #endif /* NOURL */ /* main()... If you get complaints about "main: return type is not blah", define MAINTYPE on the CC command line, e.g. "CFLAGS=-DMAINTYPE=blah" (where "blah" is int, long, or whatever). If the complaint is "Attempt to return a value from a function of type void" then add -DMAINISVOID. */ #ifndef MAINTYPE #ifndef MAINISVOID #define MAINTYPE int #endif /* MAINISVOID */ #endif /* MAINTYPE */ #ifdef MAINISVOID #ifndef MAINTYPE #define MAINTYPE void #endif /* MAINTYPE */ #endif /* MAINISVOID */ #ifdef aegis /* On the Apollo, intercept main to insert a cleanup handler */ int ckcmai(argc,argv) int argc; char **argv; #else #ifdef MAC /* Macintosh */ int main (void) #else #ifdef __GNUC__ /* GCC compiler */ int main(argc,argv) int argc; char **argv; #else #ifdef __DECC /* DEC Alpha with DEC C compiler */ #ifdef __ALPHA int main(argc,argv) int argc; char **argv; #else /* DEC C compiler, not Alpha */ #define MAINISVOID VOID main(argc,argv) int argc; char **argv; #endif /* __ALPHA */ #else #ifdef STRATUS /* Stratus VOS */ int main(argc,argv) int argc; char **argv; #else /* K-95 */ #ifdef OS2 #ifdef KUI #define MAINISVOID void Main( int argc, char ** argv ) #else /* KUI */ #define MAINISVOID VOID main(argc,argv) int argc; char **argv; #endif /* KUI */ #else /* Not K95 */ MAINTYPE /* All others... */ main(argc,argv) int argc; char **argv; #endif /* OS2 */ #endif /* STRATUS */ #endif /* __DECC */ #endif /* __GNUC__ */ #endif /* MAC */ #endif /* aegis */ /* main */ { char *p; #ifndef NOSETKEY int i; #endif /* NOSETKEY */ #ifdef datageneral short *pfha = 016000000036; /* Get around LANG_RT problem */ *pfha = (short) 0; /* No user protection fault handler */ #endif /* datageneral */ int unbuf = 0; /* nonzero for unbuffered stdout */ /* setbuf has to be called on the file descriptor before it is used */ #ifdef UNIX #ifdef NONOSETBUF /* Unbuffered console i/o */ unbuf++; /* as a compile-time option */ #endif /* NONOSETBUF */ if (!unbuf) { /* Or as a command-line selection */ int i, n; /* We have to pre-pre-scan for */ char * s; /* this one. */ for (i = 1; i < argc; i++) { s = argv[i]; if (!s) n = 0; else n = (int)strlen(s); if (n > 4) { if (!ckstrcmp("--unbuffered",s,n,0)) { unbuf++; break; } } } } if (unbuf) setbuf(stdout,NULL); #endif /* UNIX */ /* Do some initialization */ #ifdef VMS #ifdef __DECC /* Get some RMS default settings. */ get_rms_defaults(); #endif /* def __DECC */ #endif /* def VMS */ #ifndef MAC xargc = xargs = argc; /* Make global copies of argc */ xargv = argv; /* ...and argv. */ xarg0 = argv[0]; #ifdef NT setOSVer(); #endif /* NT */ zstrip(argv[0],&p); /* Get name we were invoked with */ makestr(&myname,p); if (!ckstrcmp(myname,"telnet",-1,0)) howcalled = I_AM_TELNET; #ifdef CK_KERBEROS else if (!ckstrcmp(myname,"ktelnet",-1,0)) howcalled = I_AM_TELNET; #endif /* CK_KERBEROS */ else if (!ckstrcmp(myname,"rlogin",-1,0)) howcalled = I_AM_RLOGIN; else if (!ckstrcmp(myname,"iksd",-1,0)) howcalled = I_AM_IKSD; #ifdef NEWFTP else if (!ckstrcmp(myname,"ftp",-1,0)) howcalled = I_AM_FTP; #endif /* NEWFTP */ #ifndef NOHTTP else if (!ckstrcmp(myname,"http",-1,0)) howcalled = I_AM_HTTP; #endif /* NOHTTP */ #ifdef OS2 else if (!ckstrcmp(myname,"telnet.exe",-1,0)) howcalled = I_AM_TELNET; #ifdef SSHBUILTIN else if (!ckstrcmp(myname,"ssh",-1,0)) howcalled = I_AM_SSH; else if (!ckstrcmp(myname,"ssh.exe",-1,0)) howcalled = I_AM_SSH; #endif /* SSHBUILTIN */ #ifdef CK_KERBEROS else if (!ckstrcmp(myname,"ktelnet.exe",-1,0)) howcalled = I_AM_TELNET; #endif /* CK_KERBEROS */ else if (!ckstrcmp(myname,"rlogin.exe",-1,0)) howcalled = I_AM_RLOGIN; #ifdef NT else if (!ckstrcmp(myname,"iksdnt",-1,0)) howcalled = I_AM_IKSD; else if (!ckstrcmp(myname,"iksdnt.exe",-1,0)) howcalled = I_AM_IKSD; #endif /* NT */ #ifdef NEWFTP else if (!ckstrcmp(myname,"ftp.exe",-1,0)) howcalled = I_AM_FTP; #endif /* NEWFTP */ #ifndef NOHTTP else if (!ckstrcmp(myname,"http.exe",-1,0)) howcalled = I_AM_HTTP; #endif /* NOHTTP */ #endif /* OS2 */ else if (!ckstrcmp(myname,"kermit-sshsub",-1,0)) howcalled = I_AM_SSHSUB; #ifndef NOICP cmdini(); /* Must come before prescan */ debug(F100,"main cmdini() done","",0); #endif /* NOICP */ prescan(0); /* Pre-Check for debugging, etc */ #endif /* MAC */ debug(F101,"MAIN feol","",feol); makever(); /* Put together version strings */ #ifndef NOSETKEY /* Allocate & initialize the keymap */ /* This code has been moved to before sysinit() for K95G */ if (!(keymap = (KEY *) malloc(sizeof(KEY)*KMSIZE))) fatal("main: no memory for keymap"); if (!(macrotab = (MACRO *) malloc(sizeof(MACRO)*KMSIZE))) fatal("main: no memory for macrotab"); for (i = 0; i < KMSIZE; i++) { keymap[i] = (KEY) i; macrotab[i] = NULL; } #endif /* NOSETKEY */ shortbytes.x_short = 0xABCD; /* Get Endianness */ if (shortbytes.x_char[0] == 0xCD) { /* 0 = Big Endian */ byteorder = 1; /* 1 = Little Endian */ bigendian = 0; /* (for clarity in programming) */ } else { byteorder = 0; /* Big Endian */ bigendian = 1; } if (sizeof(CK_OFF_T) == 8) /* Large files and ints? */ havelfs = 1; if (sysinit() < 0) /* System-dependent initialization. */ fatal("Can't initialize!"); else initflg = 1; /* Remember we did. */ debug(F111,"ckcmai myname",myname,howcalled); #ifdef UNIX getexedir(); /* Compute exedir variable */ #endif /* UNIX */ #ifdef CKSYSLOG #ifdef SYSLOGLEVEL /* If built with -DSYSLOGLEVEL on cc command line, this means we always do syslogging at the indicated level. */ zsyslog(); /* Open syslog */ #else /* SYSLOGLEVEL */ #ifdef IKSD if (inserver) zsyslog(); /* Open syslog */ #endif /* IKSD */ #endif /* SYSLOGLEVEL */ #endif /* CKSYSLOG */ #ifdef CK_KERBEROS ini_kerb(); /* Initialize Kerberos data */ #endif /* CK_KERBEROS */ #ifdef CK_SSL ssl_once_init(); #endif /* CK_SSL */ #ifdef TNCODE tn_set_modes(); /* Init Telnet Option tables */ #endif /* TNCODE */ #ifdef CK_TTGWSIZ /* Initialize screen dimensions */ #ifdef OS2 ttgcwsz(); #else /* OS2 */ if (ttgwsiz() > 0) { if (tt_rows > 0 && tt_cols > 0) { cmd_rows = tt_rows; cmd_cols = tt_cols; } } #endif /* OS2 */ #endif /* CK_TTGWSIZ */ #ifndef OS2 #ifdef TCPSOCKET #ifdef CK_SOCKS SOCKSinit(argv[0]); /* Internet relay package... */ #endif /* CK_SOCKS */ #endif /* TCPSOCKET */ #endif /* OS2 */ initflow(); /* Initialize flow-control table */ #ifndef NOICP #ifdef CKFLOAT initfloat(); /* Deduce floating-point precision */ #endif /* CKFLOAT */ #endif /* NOICP */ #ifndef NOXFER initxlist(); /* Init exception lists */ #ifdef CK_XYZ /* Initialize protocols... */ #ifdef XYZ_INTERNAL /* XYZMODEM are internal ... */ #ifdef COMMENT /* Can't do this for XMODEM because if filename contains a "C" etc... */ initproto(PROTO_X, "rx %s","rx %s", NULL, NULL, NULL, NULL, NULL); initproto(PROTO_XC,"rc %s","rc %s", NULL, NULL, NULL, NULL, NULL); #else /* COMMENT */ initproto(PROTO_X, NULL, NULL, NULL, NULL, NULL, NULL, NULL); initproto(PROTO_XC,NULL, NULL, NULL, NULL, NULL, NULL, NULL); #endif /* COMMENT */ initproto(PROTO_Y, "rb","rb", NULL, NULL, NULL, NULL, NULL); initproto(PROTO_G, "rb","rb", NULL, NULL, NULL, NULL, NULL); initproto(PROTO_Z, "rz","rz", NULL, NULL, NULL, NULL, NULL); initproto(PROTO_K,"kermit -ir","kermit -r","kermit -x",NULL,NULL,NULL,NULL); /* Kermit Must be last */ #else /* XYZMODEM are external protocols ... */ /* s1 s2 s3 s4 s5 s6 s7 */ initproto(PROTO_X, "rx %s","rx %s",NULL,"sx %s","sx -a %s","rx %s", "rx %s"); initproto(PROTO_XC,"rc %s","rc %s",NULL,"sx %s","sx -a %s","rc %s", "rc %s"); initproto(PROTO_Y, "rb", "rb", NULL,"sb %s","sb -a %s","rb", "rb" ); initproto(PROTO_G, "rb", "rb", NULL,"sb %s","sb -a %s","rb", "rb" ); initproto(PROTO_Z, "rz", "rz", NULL,"sz %s","sz -a %s","rz", "rz" ); initproto(PROTO_K, "kermit -ir","kermit -r","kermit -x",NULL,NULL,NULL,NULL); /* Kermit must be last */ #endif /* XYZ_INTERNAL */ #else /* No XYZMODEM support */ initproto(PROTO_K,"kermit -ir","kermit -r","kermit -x",NULL,NULL,NULL,NULL); #endif /* CK_XYZ */ #endif /* NOXFER */ connoi(); /* Console interrupts off */ #ifndef NOXFER #ifdef OS2 /* Initialize Kermit and Zmodem Auto-Download Strings */ adl_kstr = strdup("KERMIT READY TO SEND..."); adl_zstr = strdup("rz\r"); #endif /* OS2 */ #ifdef PATTERNS initpat(); /* Initialize filename patterns */ #endif /* PATTERNS */ #endif /* NOXFER */ #ifndef NOCSETS initcsets(); /* Initialize character sets */ #endif /* NOCSETS */ #ifndef NOICP #ifdef DFCDMSG makestr(&cdmsgstr,DFCDMSG); makelist(cdmsgstr,cdmsgfile,8); /* Initialize CD message filenames */ #endif /* DFCDMSG */ #endif /* NOICP */ sstate = 0; /* No default start state. */ #ifdef DYNAMIC if (getiobs() < 0) fatal("Can't allocate i/o buffers!"); #endif /* DYNAMIC */ #ifndef NOSPL #ifndef NORANDOM { char stackdata[256]; unsigned int c = 1234, n; /* try to make a random unsigned int to feed srand() */ #ifndef VMS /* time.h and MultiNet do not get along */ c = time(NULL); #endif /* VMS */ c *= getpid(); /* Referenced before set... DELIBERATELY */ for (n = 0; n < sizeof(stackdata); n++) /* IGNORE WARNING */ c += stackdata[n]; /* DELIBERATELY USED BEFORE SET */ srand((unsigned int)c); } #endif /* NORANDOM */ #endif /* NOSPL */ ckhost(myhost,MYHOSTL); /* Name of local host */ debug(F110,"main ckhost",myhost,0); #ifdef IKSD if (!inserver) { #endif /* IKSD */ ckstrncpy(ttname,dftty,TTNAMLEN); /* Set up default tty name. */ local = nolocal ? 0 : dfloc; /* And whether it's local or remote. */ parity = dfprty; /* Set initial parity, */ #ifndef NOXFER myindex = getsysix(cksysid); /* System index */ #endif /* NOXFER */ if (local) if (ttopen(ttname,&local,0,0) < 0) { #ifndef OS2 conol("Can't open device: "); conoll(ttname); #endif /* OS2 */ local = 0; ckstrncpy(ttname,CTTNAM,TTNAMLEN); } setflow(); /* Set appropriate flow control */ speed = ttgspd(); /* Get transmission speed. */ #ifdef IKSD } #endif /* IKSD */ #ifdef ANYX25 /* All X.25 implementations */ #ifndef IBMX25 /* except IBM have PAD support */ initpad(); /* Initialize X.25 PAD */ #endif /* IBMX25 */ #endif /* ANYX25 */ #ifndef NOXFER if (inibufs(SBSIZ,RBSIZ) < 0) /* Allocate packet buffers */ fatal("Can't allocate packet buffers!"); #ifndef NOCKSPEED setprefix(prefixing); /* Set up control char prefixing */ #endif /* NOCKSPEED */ #endif /* NOXFER */ #ifndef NOICP if (sstelnet #ifdef IKSD || inserver #endif /* IKSD */ ) { int on = 1, x = 0; extern int ckxech, ttnet, ttnproto, cmdmsk; #ifdef SO_SNDBUF extern int tcp_sendbuf; #endif #ifdef SO_RCVBUF extern int tcp_recvbuf; #endif #ifdef SO_KEEPALIVE extern int tcp_keepalive; #endif #ifdef SO_LINGER extern int tcp_linger, tcp_linger_tmo; #endif /* SO_LINGER */ #ifdef SO_DONTROUTE extern int tcp_dontroute; #endif /* SO_DONTROUTE */ #ifdef TCP_NODELAY extern int tcp_nodelay; #endif /* TCP_NODELAY */ #ifdef IKSD extern int iklogopen; #endif /* IKSD */ extern int ttmdm; #ifdef UNIX if (isatty(0)) fatal("Internet Kermit Service cannot be started at a terminal."); #endif /* UNIX */ reliable = xreliable = SET_ON; /* IKSD has reliable connection */ #ifndef VMS flow = 0; /* No flow control needed */ #endif /* VMS */ bgset = 0; /* Not in background */ nopush = 1; /* No external processes */ parity = 0; /* 8 bits ... */ cmdmsk = 0xff; /* all the way */ cmask = 0xff; #ifdef IKSD if (inserver) { /* If IKSD */ doiksdinit(); /* Execute IKSD configuration file */ while (tlevel > -1) parser(1); /* (Ignore any file-xfer commands) */ iksdcf = 1; /* IKSD c.f. has been processed */ } if (!iklogopen) (VOID) doiklog(); /* Open Kermit-specific log */ #endif /* IKSD */ #ifdef UNIX setbuf(stdout,NULL); /* Don't buffer the output */ ckstrncpy(ttname,"0",TTNAMLEN); /* not "/dev/tty"... */ #endif /* UNIX */ local = 0; /* We are in remote mode */ ckxech = 1; /* We will echo */ #ifdef OS2 nettype = NET_TCPB; /* So ttopen() treats the connection */ mdmtyp = -nettype; /* as a network */ #endif /* OS2 */ debug(F100,"main about to call ttopen() inserver","",0); if (ttopen(ttname,&local,mdmtyp,0) < 0) { /* Open comm channel */ fatal("can't initialize i/o"); } #ifdef OS2 local = 0; network = 1; /* Does use networking code */ #else /* OS2 */ network = 0; /* Does not use networking code */ #endif /* OS2 */ ttmdm = -1; /* Does not use a modem */ sstelnet = 1; /* Do server-side Telnet negotations */ debug(F111,"MAIN","sstelnet",sstelnet); ttnet = NET_TCPB; /* Network type is TCP sockets */ ttnproto = NP_TELNET; /* Netword protocol is Telnet */ #ifdef IKSDB dbinit(); /* Initialize database record */ #endif /* IKSDB */ #ifndef OS2 #ifdef CK_AUTHENTICATION /* Before initializating Telnet/Rlogin negotiations, init Kerberos */ ck_auth_init(ckgetpeer(),"","",0); #endif /* CK_AUTHENTICATION */ #ifdef NON_BLOCK_IO on = 1; x = socket_ioctl(0,FIONBIO,&on); debug(F101,"main FIONBIO","",x); #endif /* NON_BLOCK_IO */ #ifdef SO_OOBINLINE on = 1; x = setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)); debug(F101,"main SO_OOBINLINE","",x); #endif /* SO_OOBINLINE */ #ifndef NOTCPOPTS #ifndef datageneral #ifdef SOL_SOCKET #ifdef TCP_NODELAY no_delay(0,tcp_nodelay); #endif /* TCP_NODELAY */ #ifdef SO_KEEPALIVE keepalive(0,tcp_keepalive); #endif /* SO_KEEPALIVE */ #ifdef SO_LINGER ck_linger(0,tcp_linger, tcp_linger_tmo); #endif /* SO_LINGER */ #ifdef SO_DONTROUTE dontroute(0,tcp_dontroute); #endif /* SO_DONTROUTE */ #ifdef SO_SNDBUF sendbuf(0,tcp_sendbuf); #endif /* SO_SNDBUF */ #ifdef SO_RCVBUF recvbuf(0,tcp_recvbuf); #endif /* SO_RCVBUF */ #endif /* SOL_SOCKET */ #endif /* datageneral */ #endif /* NOTCPOPTS */ #ifdef CK_SSL if (ck_ssleay_is_installed()) { if (!ssl_tn_init(SSL_SERVER)) { if (bio_err != NULL) { BIO_printf(bio_err,"do_ssleay_init() failed\r\n"); ERR_print_errors(bio_err); } else { fflush(stderr); fprintf(stderr,"do_ssleay_init() failed\r\n"); ERR_print_errors_fp(stderr); } switch (ttnproto) { case NP_SSL: case NP_TLS: case NP_SSL_RAW: case NP_TLS_RAW: case NP_SSL_TELNET: case NP_TLS_TELNET: doexit(BAD_EXIT,1); } /* otherwise we will continue to accept the connection */ /* without SSL or TLS support unless required. */ if ( TELOPT_DEF_S_ME_MODE(TELOPT_START_TLS) != TN_NG_MU ) TELOPT_DEF_S_ME_MODE(TELOPT_START_TLS) = TN_NG_RF; if ( TELOPT_DEF_S_U_MODE(TELOPT_START_TLS) != TN_NG_MU ) TELOPT_DEF_S_U_MODE(TELOPT_START_TLS) = TN_NG_RF; if ( TELOPT_DEF_C_ME_MODE(TELOPT_START_TLS) != TN_NG_MU ) TELOPT_DEF_C_ME_MODE(TELOPT_START_TLS) = TN_NG_RF; if ( TELOPT_DEF_C_U_MODE(TELOPT_START_TLS) != TN_NG_MU ) TELOPT_DEF_C_U_MODE(TELOPT_START_TLS) = TN_NG_RF; } else { if ( ck_ssl_incoming(0) < 0 ) { doexit(BAD_EXIT,1); } } } #endif /* CK_SSL */ #ifdef TNCODE tn_ini(); /* Start Telnet negotiation now */ #endif /* TNCODE */ #endif /* OS2 */ } debug(F101,"main argc after prescan()","",argc); /* Now process any relevant environment variables */ #ifndef NODIAL getdialenv(); /* Dialing */ #ifdef NETCONN ndinit(); /* Initialize network directory info */ getnetenv(); /* Network directories */ #endif /* NETCONN */ #endif /* NODIAL */ #ifndef NOXFER #ifdef CK_FAST dofast(); /* By now FAST defaults should be OK */ #endif /* CK_FAST */ #endif /* NOXFER */ #ifndef NOCMDL ikslogin(); /* IKSD Login and other stuff */ #ifdef IKSD #ifdef NT if ( inserver ) setntcreds(); #endif /* NT */ #endif /* IKSD */ #endif /* NOCMDL */ if (howcalled == I_AM_SSHSUB) { reliable = 1; /* We say the connection is reliable */ xreliable = 1; /* And that we said it was */ setreliable = 1; /* And pretend the "user" did too */ xfinish = 1; /* For REMOTE HELP response */ mdmtyp = 0; /* For ttopen() */ ckstrncpy(ttname,"0",TTNAMLEN+1); /* Use file descriptor 0 */ local = 0; /* And force remote mode */ ttopen(ttname,&local,mdmtyp,0); /* Open the "connection" */ sstate = 'x'; /* Initial state is Server */ proto(); /* Enter protocol */ doexit(GOOD_EXIT,xitsta); /* Exit when done */ } debug(F111,"howcalled",myname,howcalled); #ifdef NOCCTRAP dotakeini(0); #else /* NOCCTRAP */ debug(F100,"main about to cc_execute","",0); setint(); cc_execute( ckjaddr(cmjbuf), dotakeini, failtakeini ); #endif /* NOCCTRAP */ debug(F111,"main 2 cfilef",cmdfil,cfilef); if (cmdfil[0]) { /* If we got one (see prescan())... */ #ifdef NOCCTRAP docmdfile(0); /* execute it. */ #else /* NOCCTRAP */ setint(); cc_execute( ckjaddr(cmjbuf), docmdfile, failcmdfile ); #endif /* NOCCTRAP */ } #ifndef OS2 /* Preserve name so we can delete it */ *cmdfil = '\0'; /* Done, nullify the file name */ #endif /* OS2 */ #endif /* NOICP */ #ifndef NOCMDL /* Look for a UNIX-style command line... */ what = W_NOTHING; debug(F101,"main argc","",argc); #ifndef NOHELP iniopthlp(); /* Initialize cmdline arg help */ #endif /* NOHELP */ if ( #ifdef COMMENT !cfilef && #endif /* COMMENT */ argc > 1) { /* Command line arguments? */ sstate = (CHAR) cmdlin(); /* Yes, parse. */ #ifdef NETCONN #ifndef NOURL if (haveurl) { /* Was a URL given? */ dourl(); /* if so, do it. */ } #endif /* NOURL */ #endif /* NETCONN */ #ifndef NOXFER zstate = sstate; /* Remember sstate around protocol */ debug(F101,"main zstate","",zstate); #endif /* NOXFER */ #ifndef NOLOCAL if (cflg) { /* Connect first if requested */ doconect(0,0); if (ttchk() < 0) dologend(); cflg = 0; } #endif /* NOLOCAL */ #ifndef NOXFER if (sstate) { #ifndef NOLOCAL if (displa) concb((char)escape); /* (for console "interrupts") */ #endif /* NOLOCAL */ #ifdef NOCCTRAP docmdline(1); #else /* NOCCTRAP */ setint(); cc_execute( ckjaddr(cmjbuf), docmdline, failcmdline ); #endif /* NOCCTRAP */ } #endif /* NOXFER */ #ifndef NOICP /* If a command-line action argument was given and -S ("stay") was not given, exit now. */ if ((cflg || cnflg || zstate) && !stayflg) #endif /* NOICP */ doexit(GOOD_EXIT,xitsta); /* Exit with good status */ #ifndef NOLOCAL #ifndef NOICP if (local) { #ifdef NETCONN if ((cflg || cnflg) && tn_exit && ttchk() < 0) doexit(GOOD_EXIT,xitsta); /* Exit with good status */ #endif /* NETCONN */ if (exitonclose && !network && (carrier != CAR_OFF && (ttgmdm() & BM_DCD) == 0)) doexit(GOOD_EXIT,xitsta); /* Exit with good status */ if (exitonclose && network && ttchk() < 0) doexit(GOOD_EXIT,xitsta); /* Exit with good status */ } #endif /* NOICP */ #endif /* NOLOCAL */ } #endif /* NOCMDL */ #ifdef NOICP /* No interactive command parser */ #ifndef NOCMDL else { /* Command-line-only version */ fatal("?No command-line options given - type 'kermit -h' for help"); } #else /* Neither one! */ sstate = 'x'; justone = 0; proto(); /* So go into server mode */ doexit(GOOD_EXIT,xitsta); /* exit with good status */ #endif /* NOCMDL */ #else /* not NOICP */ /* If no action requested on command line, or if -S ("stay") was included, enter the interactive command parser. */ if (!clcmds) herald(); /* Display program herald. */ #ifdef NOCCTRAP debug(F100,"main NOCCTRAP setting interrupt trap","",0); setint(); /* Set up command interrupt traps */ doicp(NULL); #else /* NOCCTRAP */ while (1) { debug(F100,"main setting interrupt trap","",0); setint(); /* Set up command interrupt traps */ if (!cc_execute(ckjaddr(cmjbuf), doicp, failicp)) break; } #endif /* NOCCTRAP */ #endif /* NOICP */ #ifndef MAINISVOID return(1); #endif /* MAINISVOID */ } #ifdef DYNAMIC /* Allocate file i/o buffers */ char *zinbuffer = NULL, *zoutbuffer = NULL; int getiobs() { zinbuffer = (char *)malloc(INBUFSIZE); if (!zinbuffer) return(-1); zoutbuffer = (char *)malloc(zobufsize); debug(F101,"zoutbuffer malloc","",zobufsize); if (!zoutbuffer) return(-1); debug(F100,"getiobs ok","",0); return(0); } #endif /* DYNAMIC */ ckcmdb.c0000644000015300001460000002074607423160260011261 0ustar fdckermit/* C K C M D B . C -- malloc debugger. */ /* Author: Howie Kaye, Columbia University Center for Computing Activities. Copyright (C) 1985, 1999, Trustees of Columbia University in the City of New York. All rights reserved. See the C-Kermit COPYING.TXT file or the copyright text in the ckcmai.c module for disclaimer and permissions. */ /* Use the real ones in this module! */ #ifdef malloc #undef malloc #endif /* malloc */ #ifdef calloc #undef calloc #endif /* calloc */ #ifdef realloc #undef realloc #endif /* realloc */ #ifdef free #undef free #endif /* free */ #include "ckcsym.h" #include #include "ckcdeb.h" #ifdef COHERENT _PROTOTYP ( FILE * fdopen, (int, char *) ); #endif /* COHERENT */ /* memdebug: variable to control memory debugging. if memdebug == 1, then action is always taken. if memdebug == 0, then no action is taken. if memdebug == -1, then the user is asked (works well with gdb). */ int memdebug = -1; int disabled = 0; int inited = 0; /* To use this package, compile your program with: -Dmalloc=dmalloc -Dfree=dfree =Dcalloc=dcalloc ... -DMDEBUG and then link it with ckcmdb.c. */ #ifdef MDEBUG #ifndef M_SIZE_T #ifdef NEXT #define M_SIZE_T size_t #else #ifdef SUNOS41 #define M_SIZE_T unsigned #else #define M_SIZE_T int #endif /* SUNOS41 */ #endif /* NEXT */ #endif /* M_SIZE_T */ #ifdef CK_ANSIC _PROTOTYP( void free, (void *) ); _PROTOTYP( void * malloc, (size_t) ); _PROTOTYP( void * realloc, (void *, size_t) ); #else _PROTOTYP( VOID free, (char *) ); _PROTOTYP( char * malloc, (M_SIZE_T) ); _PROTOTYP( char * realloc, (char *, M_SIZE_T) ); #endif /* NEXT */ _PROTOTYP( VOID m_insert, (char *) ); _PROTOTYP( int m_delete, (char *) ); _PROTOTYP( char * dmalloc, (int) ); _PROTOTYP( char * dcalloc, (int, int) ); _PROTOTYP( char * drealloc, (char *, int) ); _PROTOTYP( char *set_range_check, (char *, int) ); _PROTOTYP( char *check_range, (char *) ); _PROTOTYP( static char *maybe_check_range, (char *) ); _PROTOTYP( static VOID maybe_quit, (char *) ); _PROTOTYP( static int ask, (char *) ); #ifndef min #define min(x,y) ((x) < (y) ? (x) : (y)) #endif /* min */ #define RANGE "ABCDEFGHIJKLMNOP" #define INTSIZE sizeof(int) #define LONGSIZE sizeof(long) #define RSIZE sizeof(RANGE) #define RFRONT min((RSIZE/2),LONGSIZE) #define RBACK min((RSIZE-RFRONT),LONGSIZE) char * dmalloc(size) int size; { char *cp; cp = malloc(size + RSIZE + INTSIZE); if (cp) { cp = set_range_check(cp, size); m_insert(cp); } return(cp); } char * dcalloc(nelem, elsize) int nelem, elsize; { char *cp; cp = dmalloc(nelem * elsize); if (cp) memset(cp, 0, nelem * elsize); return(cp); } char * drealloc(bp,size) char *bp; int size; { char *cp; if (bp == NULL) { maybe_quit("Freeing NULL pointer"); } else { m_delete(bp); cp = check_range(bp); } cp = realloc(cp, size + RSIZE + INTSIZE); if (cp) { cp = set_range_check(cp, size); m_insert(cp); } return(cp); } VOID dfree(cp) char *cp; { if (cp == NULL) maybe_quit("Freeing NULL pointer"); else { switch(m_delete(cp)) { case 0: cp = maybe_check_range(cp); break; case 1: cp = check_range(cp); break; case 2: break; } } #ifndef CK_ANSIC return(free(cp)); #endif /* CK_ANSIC */ } char * set_range_check(cp,size) char *cp; int size; { register int i; int tmp = size; for(i = 0; i < INTSIZE; i++) { /* set the size in the string */ cp[i] = tmp & 0xff; tmp >>= 8; } cp += INTSIZE; /* skip the size */ for(i = 0; i < RFRONT; i++) /* set the front of the range check */ cp[i] = RANGE[i]; /* string */ cp += RFRONT; /* skip the front range check */ for(i = 0; i < RBACK; i++) /* set the back odf the range check */ cp[i+size] = RANGE[i+RFRONT]; return(cp); } /* Put calls to this routine in your code any place where you want to check whether you've copied too many characters into a malloc'd space. */ char * check_range(cp) char *cp; { register char *bp = cp - RFRONT - INTSIZE; char *xp = bp; register int i; int size = 0; for(i = 0 ; i < INTSIZE; i++) { /* get the size out of the string */ size <<= 8; size |= bp[INTSIZE-i-1] & 0xff; } bp += INTSIZE; for(i = 0; i < RFRONT; i++) /* check front range check */ if (bp[i] != RANGE[i]) { maybe_quit("leftside malloc buffer overrun"); break; } bp += RFRONT; /* skip front range check */ for(i = 0; i < RBACK; i++) /* check back range check */ if (bp[i+size] != RANGE[i+RFRONT]) { maybe_quit("rightside malloc buffer overrun"); break; } return(xp); } static char * maybe_check_range(cp) char *cp; { register char *bp = cp - RFRONT - INTSIZE; char *xp = bp; register int i; int size = 0; for(i = 0 ; i < INTSIZE; i++) { /* get the size out of the string */ size <<= 8; size |= bp[INTSIZE-i-1] & 0xff; } bp += INTSIZE; for(i = 0; i < RFRONT; i++) /* check front range check */ if (bp[i] != RANGE[i]) { return(cp); } bp += RFRONT; /* skip front range check */ for(i = 0; i < RBACK; i++) /* check back range check */ if (bp[i+size] != RANGE[i+RFRONT]) { fprintf(stderr,"rightside malloc buffer overrun\n"); abort(); break; } return(xp); } #define BUCKETS 10000 char *m_used[BUCKETS]; char *m_used2[BUCKETS]; VOID m_insert(cp) register char *cp; { register int i; if (disabled) return; for(i = 0; i < BUCKETS; i++) if (m_used[i] == 0) { m_used[i] = cp; return; } disabled ++; } static VOID m_insert2(cp) register char *cp; { register int i; if (disabled) return; for(i = 0; i < BUCKETS; i++) if (m_used2[i] == 0) { m_used2[i] = cp; return; } disabled ++; } int m_delete(cp) register char *cp; { register int i; for(i = 0; i < BUCKETS; i++) if (m_used[i] == cp) { m_used[i] = 0; return(1); } for(i = 0; i < BUCKETS; i++) if (m_used2[i] == cp) { m_used2[i] = 0; return(2); } if (disabled) return(0); maybe_quit("Freeing unmalloc'ed pointer"); return(0); } VOID m_init() { register int i; inited = 1; disabled = 0; #ifdef NEXT malloc_debug(2+4+8+16); #endif /* NEXT */ for(i = 0; i < BUCKETS; i++) m_used[i] = 0; } VOID m_done() { register int i,j=0; if (disabled) return; for(i = 0; i < BUCKETS; i++) if (m_used[i] != 0) { if (memdebug) { if (j == 0) fprintf(stderr,"unfree'ed buffers, indices: "); fprintf(stderr,"%d, ", i); j++; } } if (j) fprintf(stderr,"\n"); for(i = 0; i < BUCKETS; i++) if (m_used2[i] != 0) { if (memdebug) { if (j == 0) fprintf(stderr,"unfree'ed registered buffers, indices: "); fprintf(stderr,"%d, ", i); j++; } } if (j) fprintf(stderr,"\n"); if (j) maybe_quit("Unfree'ed malloc buffers"); } VOID m_checkranges() { int i; for ( i = 0; i < BUCKETS; i++) if (m_used[i]) check_range(m_used[i]); } static VOID maybe_quit(str) char *str; { debug(F100,"mdebug maybe_quit","",0); if (memdebug == 0) return; fprintf(stderr,"%s\n",str); if (memdebug == 1) abort(); if (memdebug == -1) if (ask("Quit? ")) abort(); } static int ask(str) char *str; { char buf[100]; FILE *in; int fd; fd = dup(fileno(stdin)); in = fdopen(fd, "r"); while(1) { fprintf(stderr,str); fflush(stderr); if (fgets(buf, 99, in) == NULL) /* EOF? */ return(0); if (buf[0] == 'n' || buf[0] == 'N') { fclose(in); return(0); } if (buf[0] == 'y' || buf[0] == 'Y') { fclose(in); return(1); } fprintf(stderr,"please answer y/n.\n"); } } #endif /* MDEBUG */ ckcnet.c0000644000015300001460000155551011607604671011320 0ustar fdckermitchar *cknetv = "Network support, 9.0.297, 14 Jul 2011"; /* C K C N E T -- Network support */ /* Copyright (C) 1985, 2011, Trustees of Columbia University in the City of New York. All rights reserved. See the C-Kermit COPYING.TXT file or the copyright text in the ckcmai.c module for disclaimer and permissions. */ /* REMINDER: Any changes made to this file that other modules depend must also be made to cklnet.c (for VOS) until such time as cklnet.c and this module are merged back together. NOTE TO CONTRIBUTORS: This file, and all the other shared (ckc and cku) C-Kermit source files, must be compatible with C preprocessors that support only #ifdef, #else, #endif, #define, and #undef. Please do not use #if, logical operators, or other preprocessor features in this module. Also, don't use any ANSI C constructs except within #ifdef CK_ANSIC..#endif. Authors: Frank da Cruz (fdc@columbia.edu), Columbia University Academic Information Systems, New York City. Jeffrey E Altman (jaltman@secure-endpoints.com) -- Primary maintainer/developer since about 1996. netopen() routine for TCP/IP originally by Ken Yap, Rochester University (ken@cs.rochester.edu) (no longer at that address). Missing pieces for Excelan sockets library from William Bader. Telnet protocol by Frank da Cruz and Jeffrey Altman. Rlogin protocol by Jeffrey E Altman. SSL support adapted by Jeffrey E Altman from work done by Tim Hudson +61 7 32781581 TLS support by Jeffrey E Altman. HTTP support by Jeffrey E Altman. TGV MultiNet code by Frank da Cruz. MultiNet code adapted to WIN/TCP by Ray Hunter of TWG. MultiNet code adapted to DEC TCP/IP by Lee Tibbert of DEC and Frank da Cruz. TCP/IP support adapted to IBM TCP/IP 1.2.1,2.0 for OS/2 by Kai Uwe Rommel. CMU-OpenVMS/IP modifications by Mike O'Malley, Digital (DEC). X.25 support by Marcello Frutig, Catholic University, Rio de Janeiro, Brazil (frutig@rnp.impa.br) with fixes from Stefaan Eeckels, Eurokom, Luxembourg. David Lane added support for Stratus VOS X.25 1996. Stephen Riehm added support for IBM AIX X.25 in April 1998. Other contributions as indicated in the code. */ #define CKCNET_C #include "ckcsym.h" #include "ckcdeb.h" #include "ckcker.h" #include "ckcasc.h" #ifdef I386IX /* Has to come before ckcnet.h in */ #include /* this version, but after in others */ #endif /* I386IX */ #include "ckcnet.h" /* which includes ckctel.h */ #ifdef CK_SSL #include "ck_ssl.h" #endif /* CK_SSL */ #ifdef CK_DNS_SRV #ifdef OS2 #ifdef NT #include #else /* NT */ /* !Error OS/2 does not support DNS Service Records. */ #endif /* NT */ #else /* OS2 */ #include #ifdef USE_NAMESER_COMPAT #include #endif /* USE_NAMESER_COMPAT */ #ifdef MINIX3 #include #include #else #include #include #endif /* MINIX 3 */ #ifndef PS2AIX10 #ifndef BSD4 #ifndef I386IX #ifndef RTAIX #include #endif /* RTAIX */ #endif /* I386IX */ #endif /* BSD4 */ #endif /* PS2AIX10 */ #endif /* OS2 */ #ifndef T_SRV #define T_SRV 33 #endif /* T_SRV */ #ifndef T_TXT #define T_TXT 16 #endif /* T_TXT */ /* for old Unixes and friends ... */ #ifndef MAXHOSTNAMELEN #define MAXHOSTNAMELEN 64 #endif /* MAXHOSTNAMELEN */ #define MAX_DNS_NAMELEN (15*(MAXHOSTNAMELEN + 1)+1) #endif /* CK_DNS_SRV */ #ifdef NONET #ifdef TCPIPLIB #undef TCPIPLIB #endif /* TCPIPLIB */ #endif /* NONET */ #ifndef NOMHHOST #ifdef datageneral #define NOMHHOST #else #ifdef HPUX5WINTCP #define NOMHHOST #endif /* HPUX5WINTCP */ #endif /* datageneral */ #endif /* NOMHHOST */ #ifdef INADDRX struct in_addr inaddrx; #endif /* INADDRX */ int ttnet = NET_NONE; /* Network type */ int ttnproto = NP_DEFAULT; /* Network virtual terminal protocol */ /* 0 = don't lowercase username for Rlogin/Telnet protocol */ /* nonzero = do lowercase it. Add a SET command if necessary... */ #ifdef VMS int ck_lcname = 1; #else int ck_lcname = 0; #endif /* VMS */ extern int /* External variables */ duplex, debses, seslog, sessft, wasclosed, ttyfd, quiet, msgflg, what, nettype, ttmdm; #ifdef IKSD extern int inserver; #endif /* IKSD */ char myipaddr[20] = { '\0' }; /* Global copy of my IP address */ char hostipaddr[64] = { '\0' }; /* Global copy of remote IP address */ #ifdef NETCONN /* Don't need any of this if there is no network support. */ #ifndef OS2 /* Current fd-swapping hack is not thread-safe */ #define HTTP_BUFFERING #endif /* OS2 */ #ifdef HTTP_BUFFERING #define HTTP_INBUFLEN 8192 static char http_inbuf[HTTP_INBUFLEN]; static int http_bufp = 0, http_count; #endif /* HTTP_BUFFERING */ /* NETLEBUF is (must be) defined for those platforms that call this module to do network i/o (e.g. netinc(), nettchk(), etc) rather than doing it themselves (ttinc(), ttchk(), etc). In this case the Telnet local-echo buffers and routines are defined and referenced here, rather than in the ck?tio.c module. */ #ifdef NETLEBUF #define LEBUFSIZ 4096 int ttpush = -1, le_data = 0; /* These are seen from outside */ static CHAR le_buf[LEBUFSIZ]; /* These are used internally */ static int le_start = 0, le_end = 0; int tt_push_inited = 0; #endif /* NETLEBUF */ #ifdef CK_SOCKS /* SOCKS Internet relay package */ #ifdef CK_SOCKS5 /* SOCKS 5 */ #define accept SOCKSaccept #define bind SOCKSbind #define connect SOCKSconnect #define getsockname SOCKSgetsockname #define listen SOCKSlisten #else /* Not SOCKS 5 */ #define accept Raccept #define bind Rbind #define connect Rconnect #define getsockname Rgetsockname #define listen Rlisten #endif /* CK_SOCKS5 */ #endif /* CK_SOCKS */ #ifdef DEC_TCPIP #include #include #endif /* DEC_TCPIP */ /* Also see ckcnet.h -- hmmm, why don't we always include inet.h? */ #ifdef HPUX #ifndef HPUX7 /* HPUX 7.00 doesn't have it */ #ifndef HPUX6 /* HPUX 6.00 doesn't have it */ #include /* For inet_ntoa() prototype */ #endif /* HPUX6 */ #endif /* HPUX7 */ #endif /* HPUX */ #ifdef CMU_TCPIP #include #endif /* CMU_TCPIP */ #ifndef NODCLTIMEVAL #ifdef DCLTIMEVAL /* UnixWare 7 */ struct timeval { /* And define these ourselves. */ long tv_sec; /* (see comments in ckutio.c) */ long tv_usec; }; struct timezone { int tz_minuteswest; int tz_dsttime; }; #endif /* DCLTIMEVAL */ #endif /* NODCLTIMEVAL */ #ifdef WINTCP #include #include #include /* The WIN/TCP code path is the same as that for MultiNet. Only the routine names have changed ... */ #define socket_read netread #define socket_ioctl ioctl #define socket_write netwrite #define socket_close netclose #ifdef OLD_TWG /* some routines have evolved */ extern int vmserrno, uerrno; #define socket_errno uerrno #define socket_perror perror /* which uses errno, not uerrno! */ #else #define socket_errno errno #define socket_perror win$perror #endif /* OLD_TWG */ #else /* Not WINTCP */ #ifdef OSF13 #ifdef CK_ANSIC #ifdef _NO_PROTO #undef _NO_PROTO #endif /* _NO_PROTO */ #endif /* CK_ANSIC */ #endif /* OSF13 */ #ifndef I386IX #ifndef HPUXPRE65 #include /* Error number symbols */ #else #ifndef ERRNO_INCLUDED #include /* Error number symbols */ #endif /* ERRNO_INCLUDED */ #endif /* HPUXPRE65 */ #endif /* I386IX */ #include /* Everybody needs this */ #ifdef ZILOG /* Zilog has different name for this */ #include #else #include #endif /* ZILOG */ #endif /* WINTCP */ #ifdef datageneral /* Data General AOS/VS */ #include <:usr:include:vs_tcp_errno.h> #include <:usr:include:sys:vs_tcp_types.h> #ifdef SELECT /* NOTE: This can be compiled and linked OK with SELECT defined but it doesn't work at all. Anybody who cares and knows how to fix it, feel free. */ #include <:usr:include:sys:vs_tcp_time.h> #endif /* SELECT */ #include <:usr:include:sys:socket.h> #include <:usr:include:netinet:in.h> #include <:usr:include:netdb.h> #endif /* datageneral */ #ifndef socket_errno #define socket_errno errno #endif /* socket_errno */ #ifdef TNCODE extern int tn_deb; #endif /* TNCODE */ int tcp_rdns = /* Reverse DNS lookup */ #ifdef DEC_TCPIP_OLD SET_OFF /* Doesn't seem to work in UCX */ #else SET_AUTO #endif /* DEC_TCPIP */ ; #ifdef CK_DNS_SRV int tcp_dns_srv = SET_OFF; #endif /* CK_DNS_SRV */ _PROTOTYP( char * cmcvtdate, (char *, int) ); #ifdef RLOGCODE _PROTOTYP( int rlog_ctrl, (CHAR *, int) ); _PROTOTYP( static int rlog_oob, (CHAR *, int) ); #ifndef TCPIPLIB _PROTOTYP( static SIGTYP rlogoobh, ( int ) ); #endif /* TCPIPLIB */ _PROTOTYP( static int rlog_ini, (CHAR *, int, struct sockaddr_in *, struct sockaddr_in *) ); int rlog_mode = RL_COOKED; int rlog_stopped = 0; int rlog_inband = 0; #endif /* RLOGCODE */ #ifndef NOICP extern int doconx; /* CONNECT-class command active */ #endif /* NOICP */ #ifdef IBMX25 /* This variable should probably be generalised for true client/server * support - ie: the fd of the listening server, accepted calls should * be forked or at least handled via a second fd (for IBM's X.25 - * ttyfd always holds the active fd - ie the server becomes inactive * as long as a client is connected, and becomes active again when the * connection is closed) */ int x25serverfd = 0; /* extern in ckcnet.h */ int x25seqno = 0; /* Connection sequence number */ int x25lastmsg = -1; /* A cheapskate's state table */ #define X25_CLOSED 0 /* Default state: no connection, no STREAM */ #define X25_SETUP 1 /* X.25 has been set up (no connection) */ #define X25_CONNECTED 2 /* X.25 connection has been established */ int x25_state = X25_CLOSED; /* Default state */ #endif /* IBMX25 */ #ifndef DEBUG #define deblog 0 #endif /* DEBUG */ #ifdef CK_NAWS /* Negotiate About Window Size */ #ifdef RLOGCODE _PROTOTYP( int rlog_naws, (void) ); #endif /* RLOGCODE */ #endif /* CK_NAWS */ #ifdef OS2 /* For terminal type name string */ #include "ckuusr.h" #ifndef NT #include #undef COMMENT #endif /* NT */ #include "ckocon.h" extern int tt_type, max_tt; extern struct tt_info_rec tt_info[]; extern char ttname[]; #else #ifdef CK_AUTHENTICATION #include "ckuusr.h" #endif /* CK_AUTHENTICATION */ #endif /* OS2 */ #ifdef NT extern int winsock_version; #endif /* NT */ #ifdef CK_AUTHENTICATION #include "ckuath.h" #endif /* CK_AUTHENTICATION */ #include "ckcsig.h" #ifndef OS2 /* For timeout longjumps */ static ckjmpbuf njbuf; #endif /* OS2 */ #define NAMECPYL 1024 /* Local copy of hostname */ char namecopy[NAMECPYL]; /* Referenced by ckctel.c */ char namecopy2[NAMECPYL]; /* Referenced by ckctel.c */ #ifndef NOHTTP char http_host_port[NAMECPYL]; /* orig host/port necessary for http */ char http_ip[20] = { '\0' }; /* ip address of host */ char http_port = 0; int http_ssl = 0; char * http_agent = 0; int httpfd = -1; /* socket for http connections */ int http_code = 0; #define HTTPBUFLEN 1024 char http_reply_str[HTTPBUFLEN] = ""; #endif /* NOHTTP */ char ipaddr[20] = { '\0' }; /* Global copy of IP address */ unsigned long myxipaddr = 0L; /* Ditto as a number */ #endif /* NETCONN */ char *tcp_address = NULL; /* Preferred IP Address */ extern char uidbuf[]; /* User ID buffer */ extern char pwbuf[]; /* Password buffer */ #ifndef NOHTTP char * tcp_http_proxy = NULL; /* Name[:port] of http proxy server */ int tcp_http_proxy_errno = 0; char * tcp_http_proxy_user = NULL; char * tcp_http_proxy_pwd = NULL; char * tcp_http_proxy_agent = NULL; #define HTTPCPYL 1024 static char proxycopy[HTTPCPYL]; #endif /* NOHTTP */ #ifdef OS2 extern int tt_rows[], tt_cols[]; extern int tt_status[VNUM]; #else /* OS2 */ extern int tt_rows, tt_cols; /* Everybody has this */ #endif /* OS2 */ extern int cmd_cols, cmd_rows; #ifdef STREAMING /* Use blocking writes for streaming */ extern int streaming; #endif /* STREAMING */ #ifdef NT extern int WSASafeToCancel; int win95selectbug = 0; /* For TCP/IP stacks whose select() */ /* always fails on write requests such as Cisco and Quarterdeck */ #define stricmp _stricmp #endif /* NT */ #ifndef NOTCPOPTS /* Skip all this if NOTCPOPTS specified. */ #ifdef SOL_SOCKET #ifdef TCP_NODELAY int tcp_nodelay = 0; /* Nagle algorithm TCP_NODELAY */ #endif /* TCP_NODELAY */ #ifdef SO_DONTROUTE int tcp_dontroute = 0; #endif /* SO_DONTROUTE */ #ifdef SO_LINGER int tcp_linger = 0; /* SO_LINGER */ int tcp_linger_tmo = 0; /* SO_LINGER timeout */ #endif /* SO_LINGER */ #ifdef HPUX /* But the data structures */ #ifndef HPUX8 /* needed for linger are not */ #ifndef HPUX9 /* defined in HP-UX versions */ #ifndef HPUX10 /* prior to 8.00. */ #ifdef SO_LINGER #undef SO_LINGER #endif /* SO_LINGER */ #endif /* HPUX10 */ #endif /* HPUX9 */ #endif /* HPUX8 */ #endif /* HPUX */ #ifndef SO_OOBINLINE /* Hopefully only HP-UX 7.0 */ #define SO_OOBINLINE 0x0100 #endif /* SO_OOBINLINE */ #ifndef TCPSNDBUFSIZ #ifdef VMS #ifdef __alpha #define TCPSNDBUFSIZ 16384 #endif /* __alpha */ #endif /* VMS */ #endif /* TCPSNDBUFSIZ */ #ifndef TCPSNDBUFSIZ #define TCPSNDBUFSIZ -1 #endif /* TCPSNDBUFSIZ */ #ifdef SO_SNDBUF int tcp_sendbuf = TCPSNDBUFSIZ; #endif /* SO_SNDBUF */ #ifdef SO_RCVBUF int tcp_recvbuf = -1; #endif /* SO_RCVBUF */ #ifdef SO_KEEPALIVE int tcp_keepalive = 1; #endif /* SO_KEEPALIVE */ #endif /* SOL_SOCKET */ #endif /* NOTCPOPTS */ #ifndef NETCONN /* Network support not defined. Dummy functions here in case #ifdef's forgotten elsewhere. */ int /* Open network connection */ netopen(name, lcl, nett) char *name; int *lcl, nett; { return(-1); } int /* Close network connection */ netclos() { return(-1); } int /* Check network input buffer */ nettchk() { return(-1); } int /* Flush network input buffer */ netflui() { return(-1); } int /* Send network BREAK */ netbreak() { return(-1); } int /* Input character from network */ netinc(timo) int timo; { return(-1); } int /* Output character to network */ #ifdef CK_ANSIC nettoc(CHAR c) #else nettoc(c) CHAR c; #endif /* CK_ANSIC */ /* nettoc */ { return(-1); } int nettol(s,n) CHAR *s; int n; { return(-1); } #else /* NETCONN is defined (much of this module...) */ #ifdef NETLEBUF VOID le_init() { /* LocalEchoInit() */ int i; for (i = 0; i < LEBUFSIZ; i++) le_buf[i] = '\0'; le_start = 0; le_end = 0; le_data = 0; tt_push_inited = 1; } VOID le_clean() { /* LocalEchoCleanup() */ le_init(); return; } int le_inbuf() { int rc = 0; if (le_start != le_end) { rc = (le_end - le_start + LEBUFSIZ) % LEBUFSIZ; } return(rc); } int #ifdef CK_ANSIC le_putchar(CHAR ch) #else le_putchar(ch) CHAR ch; #endif /* CK_ANSIC */ /* le_putchar */ { if ((le_start - le_end + LEBUFSIZ)%LEBUFSIZ == 1) { debug(F110,"le_putchar","buffer is full",0); return(-1); } le_buf[le_end++] = ch; if (le_end == LEBUFSIZ) le_end = 0; le_data = 1; return(0); } int #ifdef CK_ANSIC le_puts(CHAR * s, int n) #else le_puts(s,n) CHAR * s; int n; #endif /* CK_ANSIC */ /* le_puts */ { int rc = 0; int i = 0; CHAR * p = (CHAR *)"le_puts"; ckhexdump(p,s,n); for (i = 0; i < n; i++) rc = le_putchar((char)s[i]); debug(F101,"le_puts","",rc); return(rc); } int #ifdef CK_ANSIC le_putstr(CHAR * s) #else le_putstr(s) CHAR * s; #endif /* CK_ANSIC */ /* le_puts */ { CHAR * p; int rc = 0; p = (CHAR *)"le_putstr"; ckhexdump(p,s,(int)strlen((char *)s)); for (p = s; *p && !rc; p++) rc = le_putchar(*p); return(rc); } int #ifdef CK_ANSIC le_getchar(CHAR * pch) #else /* CK_ANSIC */ le_getchar(pch) CHAR * pch; #endif /* CK_ANSIC */ /* le_gatchar */ { int rc = 0; if (le_start != le_end) { *pch = le_buf[le_start]; le_buf[le_start] = 0; le_start++; if (le_start == LEBUFSIZ) le_start = 0; if (le_start == le_end) { le_data = 0; } rc++; } else { *pch = 0; } return(rc); } #endif /* NETLEBUF */ #ifdef VMS /* In edit 190, we moved tn_ini() to be called from within netopen(). But tn_ini() calls ttol(), and ttol() checks to see if it's a net connection, but the flag for that isn't set until after netopen() is finished. Since, in this module, we are always doing network output anyway, we just call nettol() directly, instead of going thru ttol(). Only needed for VMS, since UNIX, AOS/VS, and VOS can handle net connections just like regular connections in ttol(), and OS/2 has a special routine for this. */ #define ttol nettol #endif /* VMS */ int tcpsrfd = -1; #ifdef CK_KERBEROS char * krb5_d_principal = NULL; /* Default principal */ char * krb5_d_instance = NULL; /* Default instance */ char * krb5_d_realm = NULL; /* Default realm */ char * krb5_d_cc = NULL; /* Default credentials cache */ char * krb5_d_srv = NULL; /* Default Service */ int krb5_d_lifetime = 600; /* Default lifetime (10 hours) */ int krb5_d_forwardable = 0; /* creds not forwardable */ int krb5_d_proxiable = 0; /* creds not proxiable */ int krb5_d_renewable = 0; /* creds not renewable (0 min) */ int krb5_autoget = 1; /* Autoget TGTs */ int krb5_autodel = 0; /* Auto delete TGTs */ int krb5_d_getk4 = 0; /* K5 Kinit gets K4 TGTs */ int krb5_checkaddrs = 1; /* Check TGT Addrs */ int krb5_d_no_addresses = 0; /* Do not include IP Addresses */ char * krb5_d_addrs[KRB5_NUM_OF_ADDRS+1]={NULL,NULL}; /* Addrs to include */ int krb5_errno = 0; /* Last K5 errno */ char * krb5_errmsg = NULL; /* Last K5 errmsg */ char * k5_keytab = NULL; char * krb4_d_principal = NULL; /* Default principal */ char * krb4_d_realm = NULL; /* Default realm */ char * krb4_d_srv = NULL; /* Default Service */ int krb4_d_lifetime = 600; /* Default lifetime (10 hours) */ int krb4_d_preauth = 1; /* Use preauth requests */ char * krb4_d_instance = NULL; /* Default instance */ int krb4_autoget = 1; /* Autoget TGTs */ int krb4_autodel = 0; /* Auto delete TGTs */ int krb4_checkaddrs = 1; /* Check TGT Addrs */ char * k4_keytab = NULL; int krb4_errno = 0; /* Last K4 errno */ char * krb4_errmsg = NULL; /* Last K4 errmsg */ struct krb_op_data krb_op = { /* Operational data structure */ 0, NULL /* (version, cachefile) */ }; struct krb4_init_data krb4_init = { /* Kerberos 4 INIT data structure */ 0, NULL, NULL, NULL, NULL }; struct krb5_init_data krb5_init = { /* Kerberos 5 INIT data structure */ 0, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }, 0 }; struct krb5_list_cred_data krb5_lc = { /* List Credentials data structure */ 0, 0, 0 }; int krb_action = -1; /* Kerberos action to perform */ #ifndef CK_AUTHENTICATION char * ck_krb4_getrealm() { return(""); } char * ck_krb5_getrealm(cc) char * cc; { return(""); } char * ck_krb4_getprincipal() { return(""); } char * ck_krb5_getprincipal(cc) char * cc; { return(""); } #endif /* CK_AUTHENTICATION */ /* I N I _ K E R B -- Initialize Kerberos data */ VOID ini_kerb() { int i; krb_action = -1; /* No action specified */ krb_op.version = 0; /* Kerberos version (none) */ krb_op.cache = NULL; /* Cache file (none) */ /* Kerberos 5 */ krb5_init.forwardable = krb5_d_forwardable; /* Init switch values... */ krb5_init.proxiable = krb5_d_proxiable; krb5_init.lifetime = krb5_d_lifetime; krb5_init.renew = 0; krb5_init.renewable = krb5_d_renewable; krb5_init.validate = 0; krb5_init.no_addresses = krb5_d_no_addresses; krb5_init.getk4 = krb5_d_getk4; if (krb5_init.postdate) { free(krb5_init.postdate); krb5_init.postdate = NULL; } if (krb5_init.service) { free(krb5_init.service); krb5_init.service = NULL; } if (!krb5_d_cc || !krb5_d_cc[0]) { /* Set default cache */ char * p; p = ck_krb5_get_cc_name(); makestr(&krb5_d_cc,(p && p[0]) ? p : NULL); } if (!krb5_d_realm || !krb5_d_realm[0]) { /* Set default realm */ char * p; p = ck_krb5_getrealm(krb5_d_cc); makestr(&krb5_d_realm,(p && p[0]) ? p : NULL); } makestr(&krb5_init.instance,krb5_d_instance); makestr(&krb5_init.realm,krb5_d_realm); /* Set realm from default */ if (krb5_init.password) { memset(krb5_init.password,0xFF,strlen(krb5_init.password)); free(krb5_init.password); krb5_init.password = NULL; } if (!krb5_d_principal) { /* Default principal */ /* a Null principal indicates the user should be prompted */ char * p = ck_krb5_getprincipal(krb5_d_cc); if (!p || !(*p)) p = (char *)uidbuf; /* Principal = user */ makestr(&krb5_d_principal,(p && p[0]) ? p : NULL); } makestr(&krb5_init.principal,krb5_d_principal); for (i = 0; i <= KRB5_NUM_OF_ADDRS; i++) { if (krb5_init.addrs[i]) free(krb5_init.addrs[i]); krb5_init.addrs[i] = NULL; } for (i = 0; i <= KRB5_NUM_OF_ADDRS && krb5_d_addrs[i]; i++) { makestr(&krb5_init.addrs[i],krb5_d_addrs[i]); } /* Kerberos 4 */ krb4_init.lifetime = krb4_d_lifetime; krb4_init.preauth = krb4_d_preauth; makestr(&krb4_init.instance,krb4_d_instance); if (!krb4_d_realm || !krb4_d_realm[0]) {/* Set default realm */ char * p; p = ck_krb4_getrealm(); makestr(&krb4_d_realm,(p && p[0]) ? p : NULL); } makestr(&krb4_init.realm,krb4_d_realm); if (krb4_init.password) { memset(krb4_init.password,0xFF,strlen(krb4_init.password)); free(krb4_init.password); krb4_init.password = NULL; } if (!krb4_d_principal) { /* Default principal */ /* a Null principal indicates the user should be prompted */ char * p = ck_krb4_getprincipal(); if (!p || !(*p)) p = (char *)uidbuf; /* Principal = user */ makestr(&(krb4_d_principal),(p && p[0]) ? p : NULL); } makestr(&(krb4_init.principal),krb4_d_principal); } /* D O A U T H -- AUTHENTICATE action routine */ int doauth(cx) int cx; { /* AUTHENTICATE action routine */ int rc = 0; /* Return code */ #ifdef CK_AUTHENTICATION #ifdef OS2 if (!ck_security_loaddll()) /* Load various DLLs */ return(rc); #endif /* OS2 */ if (krb_op.version == 4) { /* Version = 4 */ #ifdef COMMENT sho_auth(AUTHTYPE_KERBEROS_V4); #endif /* COMMENT */ if (!ck_krb4_is_installed()) { printf("?Kerberos 4 is not installed\n"); return(0); } switch (krb_action) { /* Perform V4 functions */ case KRB_A_IN: /* INIT */ rc |= !(ck_krb4_initTGT(&krb_op,&krb4_init) < 0); break; case KRB_A_DE: /* DESTROY */ rc |= !(ck_krb4_destroy(&krb_op) < 0); break; case KRB_A_LC: /* LIST-CREDENTIALS */ rc |= !(ck_krb4_list_creds(&krb_op) < 0); break; } } if (krb_op.version == 5) { /* V5 functions */ #ifdef COMMENT sho_auth(AUTHTYPE_KERBEROS_V5); #endif /* COMMENT */ if (!ck_krb5_is_installed()) { printf("?Kerberos 5 is not installed\n"); return(0); } switch (krb_action) { case KRB_A_IN: /* INIT */ rc |= !(ck_krb5_initTGT(&krb_op,&krb5_init, krb5_init.getk4 ? &krb4_init : 0) < 0); break; case KRB_A_DE: /* DESTROY */ rc |= !(ck_krb5_destroy(&krb_op) < 0); break; case KRB_A_LC: /* LIST-CREDENTIALS */ if (krb_op.version == 0) printf("\n"); rc |= !(ck_krb5_list_creds(&krb_op,&krb5_lc) < 0); break; } } #else #ifndef NOICP #ifndef NOSHOW rc = sho_auth(0); /* Show all */ #endif /* NOSHOW */ #endif /* NOICP */ #endif /* CK_AUTHENTICATION */ return(rc); } #endif /* CK_KERBEROS */ #ifdef TCPSOCKET #ifndef OS2 #ifndef NOLISTEN /* For incoming connections */ #ifndef INADDR_ANY #define INADDR_ANY 0 #endif /* INADDR_ANY */ _PROTOTYP( int ttbufr, ( VOID ) ); _PROTOTYP( int tcpsrv_open, (char *, int *, int, int ) ); static unsigned short tcpsrv_port = 0; #endif /* NOLISTEN */ #endif /* OS2 */ static char svcbuf[80]; /* TCP service string */ static int svcnum = 0; /* TCP port number */ #endif /* TCPSOCKET */ /* TCPIPLIB means use separate socket calls for i/o, while on UNIX the normal file system calls are used for TCP/IP sockets too. Means "DEC_TCPIP or MULTINET or WINTCP or OS2 or BEBOX" (see ckcnet.h), */ #ifdef TCPIPLIB /* For buffered network reads... */ /* If the buffering code is written right, it shouldn't matter how long this buffer is. */ #ifdef OS2 #ifdef NT #define TTIBUFL 64240 /* 44 * 1460 (MSS) */ #else #define TTIBUFL 32120 /* 22 * 1460 (MSS) */ #endif /* NT */ #else /* OS2 */ #define TTIBUFL 8191 /* Let's use 8K. */ #endif /* OS2 */ CHAR ttibuf[TTIBUFL+1]; /* select() is used in preference to alarm()/signal(), but different systems use different forms of select()... */ #ifndef NOSELECT /* Option to override BSDSELECT */ #ifdef BELLV10 /* Note: Although BELLV10 does have TCP/IP support, and does use the unique form of select() that is evident in this module (and in ckutio.c), it does not have a sockets library and so we can't build Kermit TCP/IP support for it. For this, somebody would have to write TCP/IP streams code. */ #define BELLSELECT #ifndef FD_SETSIZE #define FD_SETSIZE 128 #endif /* FD_SETSIZE */ #else #ifdef WINTCP /* VMS with Wollongong WIN/TCP */ #ifndef OLD_TWG /* TWG 3.2 has only select(read) */ #define BSDSELECT #endif /* OLD_TWG */ #else #ifdef CMU_TCPIP /* LIBCMU can do select */ #define BSDSELECT #else #ifdef DEC_TCPIP #define BSDSELECT #else #ifdef OS2 /* OS/2 with TCP/IP */ #ifdef NT #define BSDSELECT #else /* NT */ #define IBMSELECT #endif /* NT */ #endif /* OS2 */ #endif /* DEC_TCPIP */ #endif /* CMU_TCPIP */ #endif /* WINTCP */ #endif /* BELLV10 */ #endif /* NOSELECT */ /* Others (TGV, TCPware, ...) use alarm()/signal(). The BSDSELECT case does not compile at all; the IBMSELECT case compiles and links but crashes at runtime. NOTE: If any of these can be converted to select(), they should be for two reasons: (1) It's a lot faster; (2) certain sockets libraries do not like their socket_read() calls to be interrupted; subsequent socket_read()'s tend to fail with EBUSY. This happened in the UCX case before it was converted to use select(). */ #ifndef OS2 #ifndef VMS static /* These are used in CKVTIO.C */ #endif /* VMS */ /* And in CKONET.C */ #endif /* OS2 */ int ttibp = 0, ttibn = 0; /* Read bytes from network into internal buffer ttibuf[]. To be called when input buffer is empty, i.e. when ttibn == 0. Other network reading routines, like ttinc, ttinl, ttxin, should check the internal buffer first, and call this routine for a refill if necessary. Returns -1 on error, 0 if nothing happens. When data is read successfully, returns number of bytes read, and sets global ttibn to that number and ttibp (the buffer pointer) to zero. */ _PROTOTYP( int ttbufr, ( VOID ) ); int ttbufr() { /* TT Buffer Read */ int count; if (ttnet != NET_TCPB) /* First make sure current net is */ return(-1); /* TCP/IP; if not, do nothing. */ #ifdef OS2 RequestTCPIPMutex(SEM_INDEFINITE_WAIT); #endif /* OS2 */ if (ttibn > 0) { /* Our internal buffer is not empty, */ #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ return(ttibn); /* so keep using it. */ } if (ttyfd == -1) { /* No connection, error */ #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ return(-2); } ttibp = 0; /* Else reset pointer to beginning */ #ifdef WINTCP count = 512; /* This works for WIN/TCP */ #else #ifdef DEC_TCPIP count = 512; /* UCX */ #else #ifdef OS2 count = TTIBUFL; #else /* Multinet, etc. */ count = ttchk(); /* Check network input buffer, */ if (ttibn > 0) { /* which can put a char there! */ debug(F111,"ttbufr","ttchk() returns",count); #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ return(ttibn); } if (count < 0) { /* Read error - connection closed */ #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ return(-2); } else if (count > TTIBUFL) /* Too many to read */ count = TTIBUFL; else if (count == 0) /* None, so force blocking read */ count = 1; #endif /* OS2 */ #endif /* DEC_TCPIP */ #endif /* WINTCP */ debug(F101,"ttbufr count 1","",count); #ifdef CK_SSL if (ssl_active_flag || tls_active_flag) { int error; ssl_read: if (ssl_active_flag) count = SSL_read(ssl_con, ttibuf, count); else count = SSL_read(tls_con, ttibuf, count); error = SSL_get_error(ssl_active_flag?ssl_con:tls_con,count); switch (error) { case SSL_ERROR_NONE: debug(F111,"ttbufr SSL_ERROR_NONE","count",count); if (count > 0) { ttibp = 0; /* Reset buffer pointer. */ ttibn = count; #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ return(ttibn); /* Return buffer count. */ } else if (count < 0) { #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ return(-1); } else { netclos(); #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ return(-2); } case SSL_ERROR_WANT_WRITE: debug(F100,"ttbufr SSL_ERROR_WANT_WRITE","",0); #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ return(-1); case SSL_ERROR_WANT_READ: debug(F100,"ttbufr SSL_ERROR_WANT_READ","",0); #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ return(-1); case SSL_ERROR_SYSCALL: if ( count == 0 ) { /* EOF */ netclos(); #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ return(-2); } else { int rc = -1; #ifdef NT int gle = GetLastError(); debug(F111,"ttbufr SSL_ERROR_SYSCALL", "GetLastError()",gle); rc = os2socketerror(gle); if (rc == -1) rc = -2; else if ( rc == -2 ) rc = -1; #endif /* NT */ #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ return(rc); } case SSL_ERROR_WANT_X509_LOOKUP: debug(F100,"ttbufr SSL_ERROR_WANT_X509_LOOKUP","",0); netclos(); #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ return(-2); case SSL_ERROR_SSL: if (bio_err!=NULL) { int len; extern char ssl_err[]; BIO_printf(bio_err,"ttbufr SSL_ERROR_SSL\n"); ERR_print_errors(bio_err); len = BIO_read(bio_err,ssl_err,SSL_ERR_BFSZ); ssl_err[len < SSL_ERR_BFSZ ? len : SSL_ERR_BFSZ] = '\0'; debug(F110,"ttbufr SSL_ERROR_SSL",ssl_err,0); if (ssl_debug_flag) printf(ssl_err); } else if (ssl_debug_flag) { debug(F100,"ttbufr SSL_ERROR_SSL","",0); fflush(stderr); fprintf(stderr,"ttbufr SSL_ERROR_SSL\n"); ERR_print_errors_fp(stderr); } #ifdef COMMENT netclos(); #endif /* COMMENT */ #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ return(-2); case SSL_ERROR_ZERO_RETURN: debug(F100,"ttbufr SSL_ERROR_ZERO_RETURN","",0); netclos(); #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ return(-2); default: debug(F100,"ttbufr SSL_ERROR_?????","",0); netclos(); #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ return(-2); } } #endif /* CK_SSL */ #ifdef COMMENT /* This is for nonblocking reads, which we don't do any more. This code didn't work anyway, in the sense that a broken connection was never sensed. */ if ((count = socket_read(ttyfd,&ttibuf[ttibp+ttibn],count)) < 1) { if (count == -1 && socket_errno == EWOULDBLOCK) { debug(F100,"ttbufr finds nothing","",0); #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ return(0); } else { debug(F101,"ttbufr socket_read error","",socket_errno); #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ return(-1); } } else if (count == 0) { debug(F100,"ttbufr socket eof","",0); #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ return(-1); } #else /* COMMENT */ /* This is for blocking reads */ #ifndef VMS #ifdef SO_OOBINLINE { int outofband = 0; #ifdef BELLSELECT if (select(128, NULL, NULL, efds, 0) > 0 && FD_ISSET(ttyfd, efds)) outofband = 1; #else #ifdef BSDSELECT fd_set efds; struct timeval tv; FD_ZERO(&efds); FD_SET(ttyfd, &efds); tv.tv_sec = tv.tv_usec = 0L; debug(F100,"Out-of-Band BSDSELECT","",0); #ifdef NT WSASafeToCancel = 1; #endif /* NT */ if (select(FD_SETSIZE, NULL, NULL, &efds, &tv) > 0 && FD_ISSET(ttyfd, &efds)) outofband = 1; #ifdef NT WSASafeToCancel = 0; #endif /* NT */ #else /* !BSDSELECT */ #ifdef IBMSELECT /* Is used by OS/2 ... */ /* ... and it came in handy! For our TCP/IP layer, it avoids all the fd_set */ /* and timeval stuff since this is the only place where it is used. */ int socket = ttyfd; debug(F100,"Out-of-Band IBMSELECT","",0); if ((select(&socket, 0, 0, 1, 0L) == 1) && (socket == ttyfd)) outofband = 1; #else /* !IBMSELECT */ /* If we can't use select(), then we use the regular alarm()/signal() timeout mechanism. */ debug(F101,"Out-of-Band data not supported","",0); outofband = 0; #endif /* IBMSELECT */ #endif /* BSDSELECT */ #endif /* BELLSELECT */ if (outofband) { /* Get the Urgent Data */ /* if OOBINLINE is disabled this should be only a single byte */ /* MS Winsock has a bug in Windows 95. Extra bytes are delivered */ /* That were never sent. */ #ifdef OS2 RequestTCPIPMutex(SEM_INDEFINITE_WAIT); #endif /* OS2 */ count = socket_recv(ttyfd,&ttibuf[ttibp+ttibn],count,MSG_OOB); #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ if (count <= 0) { int s_errno = socket_errno; debug(F101, "ttbufr socket_recv MSG_OOB","",count); debug(F101, "ttbufr socket_errno","",s_errno); #ifdef OS2ONLY if (count < 0 && (s_errno == 0 || s_errno == 23)) { /* These appear in OS/2 - don't know why */ /* ignore it and read as normal data */ /* and break, then we will attempt to read */ /* the port using normal read() techniques */ debug(F100,"ttbufr handing as in-band data","",0); count = 1; } else { netclos(); /* *** *** */ #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ return(-2); } #else /* OS2ONLY */ netclos(); /* *** *** */ #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ return(-2); #endif /* OS2ONLY */ } else { /* we got out-of-band data */ ckhexdump("ttbufr out-of-band chars",&ttibuf[ttibp+ttibn],count); #ifdef BETADEBUG bleep(BP_NOTE); #endif /* BETADEBUG */ #ifdef RLOGCODE /* blah */ if (ttnproto == NP_RLOGIN || ttnproto == NP_K4LOGIN || ttnproto == NP_EK4LOGIN || ((ttnproto == NP_K5LOGIN || ttnproto == NP_EK5LOGIN) && !rlog_inband) ) { /* When urgent data is read with MSG_OOB and not OOBINLINE then urgent data and normal data are not mixed. So treat the entire buffer as urgent data. */ rlog_oob(&ttibuf[ttibp+ttibn], count); #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ return ttbufr(); } else #endif /* RLOGCODE */ /* blah */ #ifdef COMMENT /* I haven't written this yet, nor do I know what it should do */ if (ttnproto == NP_TELNET) { tn_oob(); #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ return 0; } else #endif /* COMMENT */ { /* For any protocols we don't have a special out-of-band */ /* handler for, just put the bytes in the normal buffer */ /* and return */ ttibp += 0; /* Reset buffer pointer. */ ttibn += count; #ifdef DEBUG /* Got some bytes. */ debug(F101,"ttbufr count 2","",count); if (count > 0) ttibuf[ttibp+ttibn] = '\0'; debug(F111,"ttbufr ttibuf",ttibuf,ttibp); #endif /* DEBUG */ #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ return(ttibn); /* Return buffer count. */ } } } } #endif /* SO_OOBINLINE */ #endif /* VMS */ count = socket_read(ttyfd,&ttibuf[ttibp+ttibn],count); if (count <= 0) { int s_errno = socket_errno; debug(F101,"ttbufr socket_read","",count); debug(F101,"ttbufr socket_errno","",s_errno); #ifdef OS2 if (count == 0 || os2socketerror(s_errno) < 0) { netclos(); ReleaseTCPIPMutex(); return(-2); } ReleaseTCPIPMutex(); return(-1); #else /* OS2 */ netclos(); /* *** *** */ return(-2); #endif /* OS2 */ } #endif /* COMMENT */ /* (blocking vs nonblock reads...) */ else { ttibp = 0; /* Reset buffer pointer. */ ttibn += count; #ifdef DEBUG debug(F101,"ttbufr count 2","",count); /* Got some bytes. */ if (count > 0) ttibuf[ttibp+ttibn] = '\0'; debug(F111,"ttbufr ttibuf",&ttibuf[ttibp],ttibn); #endif /* DEBUG */ #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ return(ttibn); /* Return buffer count. */ } } #endif /* TCPIPLIB */ #ifndef IBMSELECT #ifndef BELLSELECT #ifndef BSDSELECT /* Non-TCPIPLIB case */ #ifdef SELECT #define BSDSELECT #endif /* SELECT */ #endif /* BSDSELECT */ #endif /* BELLSELECT */ #endif /* IBMSELECT */ #define TELNET_PORT 23 /* Should do lookup, but it won't change */ #define RLOGIN_PORT 513 #define KERMIT_PORT 1649 #define KLOGIN_PORT 543 #define EKLOGIN_PORT 2105 #ifndef NONET /* C-Kermit network open/close functions for BSD-sockets. Much of this code shared by SunLink X.25, which also uses the socket library. */ /* N E T O P N -- Open a network connection. */ /* Call with: name of host (or host:service), lcl - local-mode flag to be set if this function succeeds, network type - value defined in ckunet.h. */ #ifdef TCPSOCKET struct hostent * #ifdef CK_ANSIC ck_copyhostent(struct hostent * h) #else /* CK_ANSIC */ ck_copyhostent(h) struct hostent * h; #endif /* CK_ANSIC */ { /* * The hostent structure is dynamic in nature. * struct hostent { * char * h_name; * char * * h_aliases; * short h_addrtype; * short h_length; * char * * h_addr_list; * #define h_addr h_addr_list[0] */ #define HOSTENTCNT 5 static struct hostent hosts[HOSTENTCNT] = {{NULL,NULL,0,0,NULL}, {NULL,NULL,0,0,NULL}, {NULL,NULL,0,0,NULL}, {NULL,NULL,0,0,NULL}, {NULL,NULL,0,0,NULL}}; static int next = 0; int i,cnt; char ** pp; if ( h == NULL ) return(NULL); if (next == HOSTENTCNT) next = 0; if ( hosts[next].h_name ) { free(hosts[next].h_name); hosts[next].h_name = NULL; } if ( hosts[next].h_aliases ) { pp = hosts[next].h_aliases; while ( *pp ) { free(*pp); pp++; } free(hosts[next].h_aliases); } #ifdef HADDRLIST if ( hosts[next].h_addr_list ) { pp = hosts[next].h_addr_list; while ( *pp ) { free(*pp); pp++; } free(hosts[next].h_addr_list); } #endif /* HADDRLIST */ makestr(&hosts[next].h_name,h->h_name); if (h->h_aliases) { for ( cnt=0,pp=h->h_aliases; pp && *pp; pp++,cnt++) ; /* The following can give warnings in non-ANSI builds */ hosts[next].h_aliases = (char **) malloc(sizeof(char *) * (cnt+1)); for ( i=0; ih_aliases[i]); } hosts[next].h_aliases[i] = NULL; } else hosts[next].h_aliases = NULL; hosts[next].h_addrtype = h->h_addrtype; hosts[next].h_length = h->h_length; #ifdef HADDRLIST #ifdef h_addr if (h->h_addr_list) { for ( cnt=0,pp=h->h_addr_list; pp && *pp; pp++,cnt++) ; /* The following can give warnings non-ANSI builds */ hosts[next].h_addr_list = (char **) malloc(sizeof(char *) * (cnt+1)); for ( i=0; ih_length); bcopy(h->h_addr_list[i],hosts[next].h_addr_list[i],h->h_length); } hosts[next].h_addr_list[i] = NULL; } else hosts[next].h_addr_list = NULL; #else bcopy(h->h_addr, &hosts[next].h_addr, h->h_length); #endif /* h_addr */ #else /* HADDRLIST */ bcopy(h->h_addr, &hosts[next].h_addr, h->h_length); #endif /* HADDRLIST */ return(&hosts[next++]); } #ifdef EXCELAN /* Most other BSD sockets implementations define these in header files and libraries. */ struct servent { unsigned short s_port; }; struct hostent { short h_addrtype; struct in_addr h_addr; int h_length; }; struct servent * getservbyname(service, connection) char *service,*connection; { static struct servent servrec; int port; port = 0; if (strcmp(service, "telnet") == 0) port = 23; else if (strcmp(service, "smtp") == 0) port = 25; else port = atoi(service); debug(F101,"getservbyname return port ","",port); if (port > 0) { servrec.s_port = htons(port); return(&servrec); } return((struct servent *) NULL); } struct hostent * gethostbyname(hostname) char *hostname; { return((struct hostent *) NULL); } unsigned long inet_addr(name) char *name; { unsigned long addr; addr = rhost(&name); debug(F111,"inet_addr ",name,(int)addr); return(addr); } char * inet_ntoa(in) struct in_addr in; { static char name[80]; ckmakxmsg(name, ckuitoa(in.s_net),".",ckuitoa(in.s_host),".", ckuitoa(in.s_lh),".", ckuitoa(in.s_impno)); return(name); } #else #ifdef DEC_TCPIP /* UCX */ int ucx_port_bug = 0; /* Explained below */ #ifdef OLDIP /* Very old VAXC or GCC */ /* Note that my oldest VAX C (V3.1-051) does not need (or want) OLDIP, hence the "Very old" in the comment - SMS, 2010/03/15. */ #define getservbyname my_getservbyname #ifdef CK_ANSIC globalref int (*C$$GA_UCX_GETSERVBYNAME)(); extern void C$$TRANSLATE(); extern void C$$SOCK_TRANSLATE(); #else globalref int (*C$$GA_UCX_GETSERVBYNAME)(); extern VOID C$$TRANSLATE(); extern VOID C$$SOCK_TRANSLATE(); #endif /* CK_ANSIC */ struct servent * my_getservbyname (service, proto) char *service, *proto; { static struct servent sent; struct iosb { union { unsigned long status; unsigned short st[2]; } sb; unsigned long spare; } s; struct { struct iosb *s; char *serv; char *prot; } par; unsigned long e; char sbuf[30], pbuf[30]; char *p; debug(F111,"UCX getservbyname",service,(int)C$$GA_UCX_GETSERVBYNAME); p = sbuf; ckstrncpy(p, service, 29); while (*p = toupper(*p), *p++) {} p = pbuf; ckstrncpy(p, proto, 29); while (*p = toupper(*p), *p++) {} par.s = &s; par.serv = ""; par.prot = ""; /* reset file pointer or something like that!?!? */ e = (*C$$GA_UCX_GETSERVBYNAME)(&par, &sent, par.s); par.serv = sbuf; par.prot = pbuf; /* that is don't care */ e = (*C$$GA_UCX_GETSERVBYNAME)(&par, &sent, par.s); if ((long)e == -1L) return NULL; if ((e & 1) == 0L) { C$$TRANSLATE(e); return NULL; } if ((s.sb.st[0] & 1) == 0) { C$$SOCK_TRANSLATE(&s.sb.st[0]); return NULL; } /* sent.s_port is supposed to be returned by UCX in network byte order. However, UCX 2.0 through 2.0C did not do this; 2.0D and later do it. But there is no way of knowing which UCX version, so we have a user-settable runtime variable. Note: UCX 2.0 was only for the VAX. */ debug(F101,"UCX getservbyname port","",sent.s_port); debug(F101,"UCX getservbyname ntohs(port)","",ntohs(sent.s_port)); if (ucx_port_bug) { sent.s_port = htons(sent.s_port); debug(F100,"UCX-PORT-BUG ON: swapping bytes","",0); debug(F101,"UCX swapped port","",sent.s_port); debug(F101,"UCX swapped ntohs(port)","",ntohs(sent.s_port)); } return &sent; } #endif /* def OLDIP */ #endif /* DEC_TCPIP */ #endif /* EXCELAN */ int gettcpport() { return(svcnum); } #endif /* TCPSOCKET */ #ifndef NOTCPOPTS #ifndef datageneral int ck_linger(sock, onoff, timo) int sock; int onoff; int timo; { /* The following, from William Bader, turns off the socket linger parameter, which makes a close() block until all data is sent. "I don't think that disabling linger can ever cause kermit to lose data, but you telnet to a flaky server (or to our modem server when the modem is in use), disabling linger prevents kermit from hanging on the close if you try to exit." Modified by Jeff Altman to be generally useful. */ #ifdef SOL_SOCKET #ifdef SO_LINGER struct linger set_linger_opt; struct linger get_linger_opt; SOCKOPT_T x; #ifdef IKSD if (!inserver) #endif /* IKSD */ if (sock == -1 || nettype != NET_TCPA && nettype != NET_TCPB && nettype != NET_SSH || ttmdm >= 0) { tcp_linger = onoff; tcp_linger_tmo = timo; return(1); } x = sizeof(get_linger_opt); if (getsockopt(sock, SOL_SOCKET, SO_LINGER, (char *)&get_linger_opt, &x)) { debug(F111,"TCP ck_linger can't get SO_LINGER",ck_errstr(),errno); } else if (x != sizeof(get_linger_opt)) { #ifdef OS2 struct _linger16 { short s_linger; short s_onoff; } get_linger_opt16, set_linger_opt16; if ( x == sizeof(get_linger_opt16) ) { debug(F111,"TCP setlinger warning: SO_LINGER","len is 16-bit",x); if (getsockopt(sock, SOL_SOCKET, SO_LINGER, (char *)&get_linger_opt16, &x) ) { debug(F111, "TCP ck_linger can't get SO_LINGER",ck_errstr(),errno); } else if (get_linger_opt16.s_onoff != onoff || get_linger_opt16.s_linger != timo) { set_linger_opt16.s_onoff = onoff; set_linger_opt16.s_linger = timo; if (setsockopt(sock, SOL_SOCKET, SO_LINGER, (char *)&set_linger_opt16, sizeof(set_linger_opt16)) ) { debug(F111, "TCP ck_linger can't set SO_LINGER", ck_errstr(), errno ); tcp_linger = get_linger_opt16.s_onoff; tcp_linger_tmo = get_linger_opt16.s_linger; } else { debug(F101, "TCP ck_linger new SO_LINGER","", set_linger_opt16.s_onoff); tcp_linger = set_linger_opt16.s_onoff; tcp_linger_tmo = set_linger_opt16.s_linger; return 1; } } else { debug(F101,"TCP ck_linger SO_LINGER unchanged","", get_linger_opt16.s_onoff); tcp_linger = get_linger_opt16.s_onoff; tcp_linger_tmo = get_linger_opt16.s_linger; return 1; } return(0); } #endif /* OS2 */ debug(F111,"TCP ck_linger error: SO_LINGER","len",x); debug(F111,"TCP ck_linger SO_LINGER", "expected len",sizeof(get_linger_opt)); debug(F111,"TCP ck_linger SO_LINGER","linger_opt.l_onoff", get_linger_opt.l_onoff); debug(F111,"TCP linger SO_LINGER","linger_opt.l_linger", get_linger_opt.l_linger); } else if (get_linger_opt.l_onoff != onoff || get_linger_opt.l_linger != timo) { set_linger_opt.l_onoff = onoff; set_linger_opt.l_linger = timo; if (setsockopt(sock, SOL_SOCKET, SO_LINGER, (char *)&set_linger_opt, sizeof(set_linger_opt))) { debug(F111,"TCP ck_linger can't set SO_LINGER",ck_errstr(),errno); tcp_linger = get_linger_opt.l_onoff; tcp_linger_tmo = get_linger_opt.l_linger; } else { debug(F101, "TCP ck_linger new SO_LINGER", "", set_linger_opt.l_onoff ); tcp_linger = set_linger_opt.l_onoff; tcp_linger_tmo = set_linger_opt.l_linger; return 1; } } else { debug(F101,"TCP ck_linger SO_LINGER unchanged","", get_linger_opt.l_onoff); tcp_linger = get_linger_opt.l_onoff; tcp_linger_tmo = get_linger_opt.l_linger; return 1; } #else debug(F100,"TCP ck_linger SO_LINGER not defined","",0); #endif /* SO_LINGER */ #else debug(F100,"TCP ck_linger SO_SOCKET not defined","",0); #endif /* SOL_SOCKET */ return(0); } int sendbuf(sock,size) int sock; int size; { /* The following, from William Bader, allows changing of socket buffer sizes, in case that might affect performance. Modified by Jeff Altman to be generally useful. */ #ifdef SOL_SOCKET #ifdef SO_SNDBUF int i, j; SOCKOPT_T x; #ifdef IKSD if (!inserver) #endif /* IKSD */ if (sock == -1 || nettype != NET_TCPA && nettype != NET_TCPB && nettype != NET_SSH || ttmdm >= 0) { tcp_sendbuf = size; return 1; } x = sizeof(i); if (getsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char *)&i, &x)) { debug(F111,"TCP sendbuf can't get SO_SNDBUF",ck_errstr(),errno); } else if (x != sizeof(i)) { #ifdef OS2 short i16,j16; if (x == sizeof(i16)) { debug(F111,"TCP sendbuf warning: SO_SNDBUF","len is 16-bit",x); if (getsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char *)&i16, &x) ) { debug(F111,"TCP sendbuf can't get SO_SNDBUF", ck_errstr(),errno); } else if (size <= 0) { tcp_sendbuf = i16; debug(F101,"TCP sendbuf SO_SNDBUF retrieved","",i16); return 1; } else if (i16 != size) { j16 = size; if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char *)&j16, sizeof(j16)) ) { debug(F111,"TCP sendbuf can't set SO_SNDBUF", ck_errstr(),errno); } else { debug(F101,"TCP sendbuf old SO_SNDBUF","",i16); debug(F101,"TCP sendbuf new SO_SNDBUF","",j16); tcp_sendbuf = size; return 1; } } else { debug(F101,"TCP sendbuf SO_SNDBUF unchanged","",i16); tcp_sendbuf = size; return 1; } return(0); } #endif /* OS2 */ debug(F111,"TCP sendbuf error: SO_SNDBUF","len",x); debug(F111,"TCP sendbuf SO_SNDBUF","expected len",sizeof(i)); debug(F111,"TCP sendbuf SO_SNDBUF","i",i); } else if (size <= 0) { tcp_sendbuf = i; debug(F101,"TCP sendbuf SO_SNDBUF retrieved","",i); return 1; } else if (i != size) { j = size; if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char *)&j, sizeof(j))) { debug(F111,"TCP sendbuf can't set SO_SNDBUF",ck_errstr(),errno); tcp_sendbuf = i; } else { debug(F101,"TCP sendbuf old SO_SNDBUF","",i); debug(F101,"TCP sendbuf new SO_SNDBUF","",j); tcp_sendbuf = size; return 1; } } else { debug(F101,"TCP sendbuf SO_SNDBUF unchanged","",i); tcp_sendbuf = size; return 1; } #else debug(F100,"TCP sendbuf SO_SNDBUF not defined","",0); #endif /* SO_SNDBUF */ #else debug(F100,"TCP sendbuf SO_SOCKET not defined","",0); #endif /* SOL_SOCKET */ return(0); } int recvbuf(sock,size) int sock; int size; { /* The following, from William Bader, allows changing of socket buffer sizes, in case that might affect performance. Modified by Jeff Altman to be generally useful. */ #ifdef SOL_SOCKET #ifdef SO_RCVBUF int i, j; SOCKOPT_T x; #ifdef IKSD if (!inserver) #endif /* IKSD */ if (sock == -1 || nettype != NET_TCPA && nettype != NET_TCPB && nettype != NET_SSH || ttmdm >= 0) { tcp_recvbuf = size; return(1); } x = sizeof(i); if (getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char *)&i, &x)) { debug(F111,"TCP recvbuf can't get SO_RCVBUF",ck_errstr(),errno); } else if (x != sizeof(i)) { #ifdef OS2 short i16,j16; if ( x == sizeof(i16) ) { debug(F111,"TCP recvbuf warning: SO_RCVBUF","len is 16-bit",x); if (getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char *)&i16, &x) ) { debug(F111,"TCP recvbuf can't get SO_RCVBUF", ck_errstr(),errno); } else if (size <= 0) { tcp_recvbuf = i16; debug(F101,"TCP recvbuf SO_RCVBUF retrieved","",i16); return 1; } else if (i16 != size) { j16 = size; if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char *)&j16, sizeof(j16))) { debug(F111,"TCP recvbuf can' set SO_RCVBUF", ck_errstr(),errno); } else { debug(F101,"TCP recvbuf old SO_RCVBUF","",i16); debug(F101,"TCP recvbuf new SO_RCVBUF","",j16); tcp_recvbuf = size; return 1; } } else { debug(F101,"TCP recvbuf SO_RCVBUF unchanged","",i16); tcp_recvbuf = size; return 1; } return(0); } #endif /* OS2 */ debug(F111,"TCP recvbuf error: SO_RCVBUF","len",x); debug(F111,"TCP recvbuf SO_RCVBUF","expected len",sizeof(i)); debug(F111,"TCP recvbuf SO_RCVBUF","i",i); } else if (size <= 0) { tcp_recvbuf = i; debug(F101,"TCP recvbuf SO_RCVBUF retrieved","",i); return 1; } else if (i != size) { j = size; if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char *)&j, sizeof(j))) { debug(F111,"TCP recvbuf can't set SO_RCVBUF",ck_errstr(),errno); tcp_recvbuf = i; } else { debug(F101,"TCP recvbuf old SO_RCVBUF","",i); debug(F101,"TCP recvbuf new SO_RCVBUF","",j); tcp_recvbuf = size; return 1; } } else { debug(F101,"TCP recvbuf SO_RCVBUF unchanged","",i); tcp_recvbuf = size; return 1; } #else debug(F100,"TCP recvbuf SO_RCVBUF not defined","",0); #endif /* SO_RCVBUF */ #else debug(F100,"TCP recvbuf SO_SOCKET not defined","",0); #endif /* SOL_SOCKET */ return 0; } int keepalive(sock,onoff) int sock; int onoff; { #ifdef SOL_SOCKET #ifdef SO_KEEPALIVE int get_keepalive_opt; int set_keepalive_opt; SOCKOPT_T x; debug(F111,"TCP keepalive","sock",sock); debug(F111,"TCP keepalive","nettype",nettype); debug(F111,"TCP keepalive","ttmdm",ttmdm); #ifdef IKSD if (!inserver) #endif /* IKSD */ if (sock == -1 || nettype != NET_TCPA && nettype != NET_TCPB && nettype != NET_SSH || ttmdm >= 0) { tcp_keepalive = onoff; return 1; } x = sizeof(get_keepalive_opt); if (getsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (char *)&get_keepalive_opt, &x)) { debug(F111,"TCP keepalive can't get SO_KEEPALIVE",ck_errstr(),errno); } else if (x != sizeof(get_keepalive_opt)) { #ifdef OS2 short get_keepalive_opt16; short set_keepalive_opt16; if (x == sizeof(get_keepalive_opt16)) { debug(F111,"TCP keepalive warning: SO_KEEPALIVE", "len is 16-bit",x); if (getsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (char *)&get_keepalive_opt16, &x) ) { debug(F111, "TCP keepalive can't get SO_KEEPALIVE", ck_errstr(), errno ); } else if (get_keepalive_opt16 != onoff) { set_keepalive_opt16 = onoff; if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (char *)&set_keepalive_opt16, sizeof(set_keepalive_opt16)) ) { debug(F111, "TCP keepalive can't clear SO_KEEPALIVE", ck_errstr(), errno ); tcp_keepalive = get_keepalive_opt16; } else { debug(F101, "TCP keepalive new SO_KEEPALIVE","", set_keepalive_opt16); tcp_keepalive = set_keepalive_opt16; return 1; } } else { debug(F101,"TCP keepalive SO_KEEPALIVE unchanged","", get_keepalive_opt16); tcp_keepalive = onoff; return 1; } return(0); } #endif /* OS2 */ debug(F111,"TCP keepalive error: SO_KEEPALIVE","len",x); debug(F111, "TCP keepalive SO_KEEPALIVE", "expected len", sizeof(get_keepalive_opt) ); debug(F111, "TCP keepalive SO_KEEPALIVE", "keepalive_opt", get_keepalive_opt ); } else if (get_keepalive_opt != onoff) { set_keepalive_opt = onoff; if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (char *)&set_keepalive_opt, sizeof(set_keepalive_opt)) ) { debug(F111, "TCP keepalive can't clear SO_KEEPALIVE", ck_errstr(), errno ); tcp_keepalive = get_keepalive_opt; } else { debug(F101, "TCP keepalive new SO_KEEPALIVE", "", set_keepalive_opt ); tcp_keepalive = onoff; return 1; } } else { debug(F101,"TCP keepalive SO_KEEPALIVE unchanged", "", get_keepalive_opt ); tcp_keepalive = onoff; return 1; } #else debug(F100,"TCP keepalive SO_KEEPALIVE not defined","",0); #endif /* SO_KEEPALIVE */ #else debug(F100,"TCP keepalive SO_SOCKET not defined","",0); #endif /* SOL_SOCKET */ return(0); } int dontroute(sock,onoff) int sock; int onoff; { #ifdef SOL_SOCKET #ifdef SO_DONTROUTE int get_dontroute_opt; int set_dontroute_opt; SOCKOPT_T x; #ifdef IKSD if (!inserver) #endif /* IKSD */ if (sock == -1 || nettype != NET_TCPA && nettype != NET_TCPB && nettype != NET_SSH || ttmdm >= 0) { tcp_dontroute = onoff; return 1; } x = sizeof(get_dontroute_opt); if (getsockopt(sock, SOL_SOCKET, SO_DONTROUTE, (char *)&get_dontroute_opt, &x)) { debug(F111,"TCP dontroute can't get SO_DONTROUTE",ck_errstr(),errno); } else if (x != sizeof(get_dontroute_opt)) { #ifdef OS2 short get_dontroute_opt16; short set_dontroute_opt16; if (x == sizeof(get_dontroute_opt16)) { debug(F111,"TCP dontroute warning: SO_DONTROUTE", "len is 16-bit",x); if (getsockopt(sock, SOL_SOCKET, SO_DONTROUTE, (char *)&get_dontroute_opt16, &x) ) { debug(F111, "TCP dontroute can't get SO_DONTROUTE", ck_errstr(), errno ); } else if (get_dontroute_opt16 != onoff) { set_dontroute_opt16 = onoff; if (setsockopt(sock, SOL_SOCKET, SO_DONTROUTE, (char *)&set_dontroute_opt16, sizeof(set_dontroute_opt16)) ) { debug(F111, "TCP dontroute can't clear SO_DONTROUTE", ck_errstr(), errno ); tcp_dontroute = get_dontroute_opt16; } else { debug(F101, "TCP dontroute new SO_DONTROUTE","", set_dontroute_opt16); tcp_dontroute = set_dontroute_opt16; return 1; } } else { debug(F101,"TCP dontroute SO_DONTROUTE unchanged","", get_dontroute_opt16); tcp_dontroute = onoff; return 1; } return(0); } #endif /* OS2 */ debug(F111,"TCP dontroute error: SO_DONTROUTE","len",x); debug(F111, "TCP dontroute SO_DONTROUTE", "expected len", sizeof(get_dontroute_opt) ); debug(F111, "TCP dontroute SO_DONTROUTE", "dontroute_opt", get_dontroute_opt ); } else if (get_dontroute_opt != onoff) { set_dontroute_opt = onoff; if (setsockopt(sock, SOL_SOCKET, SO_DONTROUTE, (char *)&set_dontroute_opt, sizeof(set_dontroute_opt)) ) { debug(F111, "TCP dontroute can't clear SO_DONTROUTE", ck_errstr(), errno ); tcp_dontroute = get_dontroute_opt; } else { debug(F101, "TCP dontroute new SO_DONTROUTE", "", set_dontroute_opt ); tcp_dontroute = onoff; return 1; } } else { debug(F101,"TCP dontroute SO_DONTROUTE unchanged", "", get_dontroute_opt ); tcp_dontroute = onoff; return 1; } #else debug(F100,"TCP dontroute SO_DONTROUTE not defined","",0); #endif /* SO_DONTROUTE */ #else debug(F100,"TCP dontroute SO_SOCKET not defined","",0); #endif /* SOL_SOCKET */ return(0); } int no_delay(sock,onoff) int sock; int onoff; { #ifdef SOL_SOCKET #ifdef TCP_NODELAY int get_nodelay_opt; int set_nodelay_opt; SOCKOPT_T x; #ifdef IKSD if (!inserver) #endif /* IKSD */ if (sock == -1 || nettype != NET_TCPA && nettype != NET_TCPB && nettype != NET_SSH || ttmdm >= 0) { tcp_nodelay = onoff; return(1); } x = sizeof(get_nodelay_opt); if (getsockopt(sock,IPPROTO_TCP,TCP_NODELAY, (char *)&get_nodelay_opt,&x)) { debug(F111, "TCP no_delay can't get TCP_NODELAY", ck_errstr(), errno); } else if (x != sizeof(get_nodelay_opt)) { #ifdef OS2 short get_nodelay_opt16; short set_nodelay_opt16; if (x == sizeof(get_nodelay_opt16)) { debug(F111,"TCP no_delay warning: TCP_NODELAY","len is 16-bit",x); if (getsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *)&get_nodelay_opt16, &x) ) { debug(F111, "TCP no_delay can't get TCP_NODELAY", ck_errstr(), errno); } else if (get_nodelay_opt16 != onoff) { set_nodelay_opt16 = onoff; if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *)&set_nodelay_opt16, sizeof(set_nodelay_opt16)) ) { debug(F111, "TCP no_delay can't clear TCP_NODELAY", ck_errstr(), errno); tcp_nodelay = get_nodelay_opt16; } else { debug(F101, "TCP no_delay new TCP_NODELAY", "", set_nodelay_opt16); tcp_nodelay = onoff; return 1; } } else { debug(F101,"TCP no_delay TCP_NODELAY unchanged","", get_nodelay_opt16); tcp_nodelay = onoff; return 1; } return(0); } #endif /* OS2 */ debug(F111,"TCP no_delay error: TCP_NODELAY","len",x); debug(F111,"TCP no_delay TCP_NODELAY","expected len", sizeof(get_nodelay_opt)); debug(F111,"TCP no_delay TCP_NODELAY","nodelay_opt",get_nodelay_opt); } else if (get_nodelay_opt != onoff) { set_nodelay_opt = onoff; if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *)&set_nodelay_opt, sizeof(set_nodelay_opt))) { debug(F111, "TCP no_delay can't clear TCP_NODELAY", ck_errstr(), errno ); tcp_nodelay = get_nodelay_opt; } else { debug(F101,"TCP no_delay new TCP_NODELAY","",set_nodelay_opt); tcp_nodelay = onoff; return 1; } } else { debug(F101,"TCP no_delay TCP_NODELAY unchanged","",get_nodelay_opt); tcp_nodelay = onoff; return(1); } #else debug(F100,"TCP no_delay TCP_NODELAY not defined","",0); #endif /* TCP_NODELAY */ #else debug(F100,"TCP no_delay SO_SOCKET not defined","",0); #endif /* SOL_SOCKET */ return 0; } #endif /* datageneral */ #endif /* NOTCPOPTS */ #ifdef SUNX25 #ifndef X25_WR_FACILITY /* For Solaris 2.3 / SunLink 8.x - see comments in ckcnet.h */ void bzero(s,n) char *s; int n; { memset(s,0,n); } #endif /* X25_WR_FACILITY */ #endif /* SUNX25 */ #ifdef TCPSOCKET #ifndef OS2 #ifndef NOLISTEN #ifdef BSDSELECT #ifndef VMS #ifndef BELLV10 #ifndef datageneral #ifdef hp9000s500 /* HP-9000/500 HP-U 5.21 */ #include #else /****** THIS SECTION ADDED BY STEVE RANCE - OS9 NETWORK SERVER * ------------------------------------------------------ * * Due to OS9's Lack of a select() call, the following seems to be * enough to fool the rest of the code into compiling. The only * effect that I can see is using control L to refresh the status * display gets qued up until some network packets arrive. * * This solution is by no means elegant but works enough to be * a (the) solution. * * Also with the defines I had specified in my makefile I had to * have an #endif right at the end of the file when compiling. * I did not bother speding time to find out why. * * COPTS = -to=osk -d=OSK -d=TCPSOCKET -d=SELECT -d=VOID=void -d=SIG_V \ * -d=DYNAMIC -d=PARSENSE -d=KANJI -d=MYCURSES -d=ZFCDAT \ * -d=CK_APC -d=CK_REDIR -d=RENAME -d=CK_TTYFD -d=NOOLDMODEMS \ * -d=CK_ANSIC -d=CK_XYZ -tp=68040d -l=netdb.l -l=socklib.l \ * -l=termlib.l -l=math.l -l=sys_clib.l * * stever@ozemail.com.au */ #ifdef OSK #define BSDSELECT /* switch on BSD select code */ #define FD_SETSIZE 32 /* Max # of paths in OS9 */ #define FD_ZERO(p) ((*p)=0) #define FD_SET(n,b) ((*b)|=(1<<(n))) #define FD_ISSET(n,b) 1 /* always say data is ready */ #define select(a,b,c,d,e) 1 /* always say 1 path has data */ typedef int fd_set; /* keep BSD Code Happy */ struct timeval {int tv_sec,tv_usec;}; /* keep BSD Code Happy */ /****** END OF OS9 MODS FROM STEVE RANCE **************************/ #endif /* OSK */ #include #endif /* hp9000s500 */ #endif /* datageneral */ #endif /* BELLV10 */ #endif /* VMS */ #ifdef SELECT_H #include #endif /* SELECT_H */ #endif /* BSDSELECT */ #ifdef SELECT #ifdef CK_SCOV5 #include #endif /* CK_SCOV5 */ #endif /* SELECT */ #ifdef NOTUSED /* T C P S O C K E T _ O P E N -- Open a preexisting socket number */ int tcpsocket_open(name,lcl,nett,timo) char * name; int * lcl; int nett; int timo { int on = 1; static struct servent *service, servrec; static struct hostent *host; static struct sockaddr_in saddr; static #ifdef UCX50 unsigned #endif /* UCX50 */ int saddrlen; #ifdef BSDSELECT fd_set rfds; struct timeval tv; #else #ifdef BELLSELECT fd_set rfds; #else fd_set rfds; fd_set rfds; struct timeval { long tv_sec; long tv_usec; } tv; #endif /* BELLSELECT */ #endif /* BSDSELECT */ debug(F101,"tcpsocket_open nett","",nett); *ipaddr = '\0'; if (nett != NET_TCPB) return(-1); /* BSD socket support */ netclos(); /* Close any previous connection. */ ckstrncpy(namecopy, name, NAMECPYL); /* Copy the hostname. */ #ifdef COMMENT /* Jeff's version from 30 Dec 2005 doesn't inhibit Telnet */ if (ttnproto != NP_TCPRAW && ttnproto != NP_SSL_RAW && ttnproto != NP_TLS_RAW) ttnproto = NP_NONE; /* No protocol selected yet. */ #else /* fdc's version from 4 Dec 2005 works ok */ if (ttnproto != NP_TCPRAW) ttnproto = NP_NONE; /* No protocol selected yet. */ #endif /* COMMENT */ debug(F110,"tcpsocket_open namecopy",namecopy,0); /* Assign the socket number to ttyfd and then fill in tcp structures */ ttyfd = atoi(&name[1]); debug(F111,"tcpsocket_open","ttyfd",ttyfd); #ifndef NOTCPOPTS #ifdef SOL_SOCKET setsockopt(ttyfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on); #ifndef datageneral #ifdef TCP_NODELAY no_delay(ttyfd,tcp_nodelay); #endif /* TCP_NODELAY */ #ifdef SO_KEEPALIVE keepalive(ttyfd,tcp_keepalive); #endif /* SO_KEEPALIVE */ #ifdef SO_LINGER ck_linger(ttyfd,tcp_linger, tcp_linger_tmo); #endif /* SO_LINGER */ #ifdef SO_SNDBUF sendbuf(ttyfd,tcp_sendbuf); #endif /* SO_SNDBUF */ #ifdef SO_RCVBUF recvbuf(ttyfd,tcp_recvbuf); #endif /* SO_RCVBUF */ #endif /* datageneral */ #endif /* SOL_SOCKET */ #endif /* NOTCPOPTS */ #ifdef NT_TCP_OVERLAPPED OverlappedWriteInit(); OverlappedReadInit(); #endif /* NT_TCP_OVERLAPPED */ /* Get the name of the host we are connected to */ saddrlen = sizeof(saddr); getpeername(ttyfd,(struct sockaddr *)&saddr,&saddrlen); ckstrncpy(ipaddr,(char *)inet_ntoa(saddr.sin_addr),20); if (tcp_rdns == SET_ON #ifdef CK_KERBEROS || tcp_rdns == SET_AUTO && (ck_krb5_is_installed() || ck_krb4_is_installed()) #endif /* CK_KERBEROS */ #ifndef NOHTTP && (tcp_http_proxy == NULL) #endif /* NOHTTP */ #ifdef CK_SSL && !(ssl_only_flag || tls_only_flag) #endif /* CK_SSL */ ) { /* Reverse DNS */ if (!quiet) { printf(" Reverse DNS Lookup... "); fflush(stdout); } host = gethostbyaddr((char *)&saddr.sin_addr,4,PF_INET); debug(F110,"tcpsocket_open gethostbyaddr",host ? "OK" : "FAILED",0); if (host) { host = ck_copyhostent(host); debug(F100,"tcpsocket_open gethostbyaddr != NULL","",0); if (!quiet) { printf("(OK)\n"); fflush(stdout); } ckstrncpy(name, host->h_name, 80); ckstrncat(name, ":", 80); ckstrncat(name,ckuitoa(ntohs(saddr.sin_port)), 80); if (!quiet #ifndef NOICP && !doconx #endif /* NOICP */ ) printf("%s connected on port %d\n", host->h_name, ntohs(saddr.sin_port) ); } else if (!quiet) printf("Failed\n"); } else if (!quiet) printf("(OK)\n"); if (tcp_rdns != SET_ON || !host) { ckstrncpy(name,ipaddr,80); ckstrncat(name,":",80); ckstrncat(name,ckuitoa(ntohs(saddr.sin_port)),80); if (!quiet #ifdef NOICP && !doconx #endif /* NOICP */ ) printf("%s connected on port %d\n",ipaddr,ntohs(saddr.sin_port)); } if (!quiet) fflush(stdout); ttnet = nett; /* TCP/IP (sockets) network */ #ifdef RLOGCODE if (ntohs(saddr.sin_port) == 513) ttnproto = NP_LOGIN; else #endif /* RLOGCODE */ /* Assume the service is TELNET. */ #ifdef COMMENT /* Jeff's code from 2005/12/30 */ if (ttnproto != NP_TCP_RAW && ttnproto != NP_SSL_RAW && ttnproto != NP_TLS_RAW) #else /* fdc's code from 2005/12/04 */ if (ttnproto != NP_TCPRAW) #endif /* COMMENT */ ttnproto = NP_TELNET; /* Yes, set global flag. */ #ifdef CK_SECURITY /* Before Initialization Telnet/Rlogin Negotiations Init Kerberos */ ck_auth_init((tcp_rdns && host && host->h_name && host->h_name[0]) ? host->h_name : ipaddr, ipaddr, uidbuf, ttyfd ); #endif /* CK_SECURITY */ if (tn_ini() < 0) /* Start/Reset TELNET negotiations */ if (ttchk() < 0) /* Did it fail due to connect loss? */ return(-1); if (*lcl < 0) *lcl = 1; /* Local mode. */ return(0); /* Done. */ } #endif /* NOTUSED */ /* T C P S R V _ O P E N -- Open a TCP/IP Server connection */ /* Calling conventions same as ttopen(), except third argument is network type rather than modem type. */ int tcpsrv_open(name,lcl,nett,timo) char * name; int * lcl; int nett; int timo; { char *p; int i, x; SOCKOPT_T on = 1; int ready_to_accept = 0; static struct servent *service, *service2, servrec; static struct hostent *host; static struct sockaddr_in saddr; struct sockaddr_in l_addr; GSOCKNAME_T l_slen; #ifdef UCX50 static u_int saddrlen; #else static SOCKOPT_T saddrlen; #endif /* UCX50 */ #ifdef BSDSELECT fd_set rfds; struct timeval tv; #else #ifdef BELLSELCT fd_set rfds; #else fd_set rfds; struct timeval { long tv_sec; long tv_usec; } tv; #endif /* BELLSELECT */ #endif /* BSDSELECT */ #ifdef CK_SSL int ssl_failed = 0; #endif /* CK_SSL */ debug(F101,"tcpsrv_open nett","",nett); *ipaddr = '\0'; if (nett != NET_TCPB) return(-1); /* BSD socket support */ netclos(); /* Close any previous connection. */ ckstrncpy(namecopy, name, NAMECPYL); /* Copy the hostname. */ /* Don't do this. */ #ifdef COMMENT /* fdc */ if (ttnproto != NP_TCPRAW) ttnproto = NP_NONE; /* No protocol selected yet. */ #endif /* COMMENT */ #ifdef COMMENT /* Jeff */ if (ttnproto != NP_TCP_RAW && ttnproto != NP_SSL_RAW && ttnproto != NP_TLS_RAW) ttnproto = NP_NONE; /* No protocol selected yet. */ #endif /* COMMENT */ debug(F110,"tcpsrv_open namecopy",namecopy,0); p = namecopy; /* Was a service requested? */ while (*p != '\0' && *p != ':') p++; /* Look for colon */ if (*p == ':') { /* Have a colon */ *p++ = '\0'; /* Get service name or number */ } else { /* Otherwise use kermit */ p = "kermit"; } debug(F110,"tcpsrv_open service requested",p,0); if (isdigit(*p)) { /* Use socket number without lookup */ service = &servrec; service->s_port = htons((unsigned short)atoi(p)); } else { /* Otherwise lookup the service name */ service = getservbyname(p, "tcp"); } if (!service && !strcmp("kermit",p)) { /* Use Kermit service port */ service = &servrec; service->s_port = htons(1649); } #ifdef RLOGCODE if (service && !strcmp("login",p) && service->s_port != htons(513)) { fprintf(stderr, " Warning: login service on port %d instead of port 513\n", ntohs(service->s_port)); fprintf(stderr, " Edit SERVICES file if RLOGIN fails to connect.\n"); debug(F101,"tcpsrv_open login on port","",ntohs(service->s_port)); } #endif /* RLOGCODE */ if (!service) { fprintf(stderr, "Cannot find port for service: %s\n", p); debug(F111,"tcpsrv_open can't get service",p,errno); errno = 0; /* rather than mislead */ return(-1); } /* If we currently have a listen active but port has changed then close */ debug(F101,"tcpsrv_open checking previous connection","",tcpsrfd); debug(F101,"tcpsrv_open previous tcpsrv_port","",tcpsrv_port); if (tcpsrfd != -1 && tcpsrv_port != ntohs((unsigned short)service->s_port)) { debug(F100,"tcpsrv_open closing previous connection","",0); #ifdef TCPIPLIB socket_close(tcpsrfd); #else close(tcpsrfd); #endif /* TCPIPLIB */ tcpsrfd = -1; } debug(F100,"tcpsrv_open tcpsrfd","",tcpsrfd); if (tcpsrfd == -1) { /* Set up socket structure and get host address */ bzero((char *)&saddr, sizeof(saddr)); debug(F100,"tcpsrv_open bzero ok","",0); saddr.sin_family = AF_INET; if (tcp_address) { #ifdef INADDRX inaddrx = inet_addr(tcp_address); saddr.sin_addr.s_addr = *(unsigned long *)&inaddrx; #else saddr.sin_addr.s_addr = inet_addr(tcp_address); #endif /* INADDRX */ } else saddr.sin_addr.s_addr = INADDR_ANY; /* Get a file descriptor for the connection. */ saddr.sin_port = service->s_port; ipaddr[0] = '\0'; debug(F100,"tcpsrv_open calling socket","",0); if ((tcpsrfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("TCP socket error"); debug(F101,"tcpsrv_open socket error","",errno); return (-1); } errno = 0; /* Specify the Port may be reused */ debug(F100,"tcpsrv_open calling setsockopt","",0); x = setsockopt(tcpsrfd, SOL_SOCKET,SO_REUSEADDR,(char *)&on,sizeof on); debug(F101,"tcpsrv_open setsockopt","",x); /* Now bind to the socket */ printf("\nBinding socket to port %d ...\n", ntohs((unsigned short)service->s_port)); if (bind(tcpsrfd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) { i = errno; /* Save error code */ #ifdef TCPIPLIB socket_close(tcpsrfd); #else /* TCPIPLIB */ close(tcpsrfd); #endif /* TCPIPLIB */ tcpsrfd = -1; tcpsrv_port = 0; ttyfd = -1; wasclosed = 1; errno = i; /* and report this error */ debug(F101,"tcpsrv_open bind errno","",errno); printf("?Unable to bind to socket (errno = %d)\n",errno); return(-1); } debug(F100,"tcpsrv_open bind OK","",0); printf("Listening ...\n"); if (listen(tcpsrfd, 15) < 0) { i = errno; /* Save error code */ #ifdef TCPIPLIB socket_close(tcpsrfd); #else /* TCPIPLIB */ close(tcpsrfd); #endif /* TCPIPLIB */ tcpsrfd = -1; tcpsrv_port = 0; ttyfd = -1; wasclosed = 1; errno = i; /* And report this error */ debug(F101,"tcpsrv_open listen errno","",errno); return(-1); } debug(F100,"tcpsrv_open listen OK","",0); tcpsrv_port = ntohs((unsigned short)service->s_port); } #ifdef CK_SSL if (ck_ssleay_is_installed()) { if (!ssl_tn_init(SSL_SERVER)) { ssl_failed = 1; if (bio_err!=NULL) { BIO_printf(bio_err,"do_ssleay_init() failed\n"); ERR_print_errors(bio_err); } else { fflush(stderr); fprintf(stderr,"do_ssleay_init() failed\n"); ERR_print_errors_fp(stderr); } if (tls_only_flag || ssl_only_flag) { #ifdef TCPIPLIB socket_close(ttyfd); socket_close(tcpsrfd); #else /* TCPIPLIB */ close(ttyfd); close(tcpsrfd); #endif /* TCPIPLIB */ ttyfd = -1; wasclosed = 1; tcpsrfd = -1; tcpsrv_port = 0; return(-1); } /* we will continue to accept the connection */ /* without SSL or TLS support unless required. */ if ( TELOPT_DEF_S_ME_MODE(TELOPT_START_TLS) != TN_NG_MU ) TELOPT_DEF_S_ME_MODE(TELOPT_START_TLS) = TN_NG_RF; if ( TELOPT_DEF_S_U_MODE(TELOPT_START_TLS) != TN_NG_MU ) TELOPT_DEF_S_U_MODE(TELOPT_START_TLS) = TN_NG_RF; if ( TELOPT_DEF_C_ME_MODE(TELOPT_START_TLS) != TN_NG_MU ) TELOPT_DEF_C_ME_MODE(TELOPT_START_TLS) = TN_NG_RF; if ( TELOPT_DEF_C_U_MODE(TELOPT_START_TLS) != TN_NG_MU ) TELOPT_DEF_C_U_MODE(TELOPT_START_TLS) = TN_NG_RF; } } #endif /* CK_SSL */ printf("\nWaiting to Accept a TCP/IP connection on port %d ...\n", ntohs((unsigned short)service->s_port)); saddrlen = sizeof(saddr); #ifdef BSDSELECT tv.tv_sec = tv.tv_usec = 0L; if (timo < 0) tv.tv_usec = (long) -timo * 10000L; else tv.tv_sec = timo; debug(F101,"tcpsrv_open BSDSELECT","",timo); #else debug(F101,"tcpsrv_open not BSDSELECT","",timo); #endif /* BSDSELECT */ if (timo) { while (!ready_to_accept) { #ifdef BSDSELECT FD_ZERO(&rfds); FD_SET(tcpsrfd, &rfds); ready_to_accept = ((select(FD_SETSIZE, #ifdef HPUX #ifdef HPUX1010 (fd_set *) #else (int *) #endif /* HPUX1010 */ #else #ifdef __DECC #ifdef INTSELECT (int *) #else /* def INTSELECT */ (fd_set *) #endif /* def INTSELECT [else] */ #endif /* __DECC */ #endif /* HPUX */ &rfds, NULL, NULL, &tv) > 0) && FD_ISSET(tcpsrfd, &rfds)); #else /* BSDSELECT */ #ifdef IBMSELECT #define ck_sleepint 250 ready_to_accept = (select(&tcpsrfd, 1, 0, 0, timo < 0 ? -timo : (timo > 0 ? timo * 1000L : ck_sleepint)) == 1 ); #else #ifdef BELLSELECT FD_ZERO(rfds); FD_SET(tcpsrfd, rfds); ready_to_accept = ((select(128, rfds, NULL, NULL, timo < 0 ? -timo : (timo > 0 ? timo * 1000L)) > 0) && FD_ISSET(tcpsrfd, rfds)); #else /* Try this - what's the worst that can happen... */ FD_ZERO(&rfds); FD_SET(tcpsrfd, &rfds); ready_to_accept = ((select(FD_SETSIZE, (fd_set *) &rfds, NULL, NULL, &tv) > 0) && FD_ISSET(tcpsrfd, &rfds)); #endif /* BELLSELECT */ #endif /* IBMSELECT */ #endif /* BSDSELECT */ } } if (ready_to_accept || timo == 0) { if ((ttyfd = accept(tcpsrfd, (struct sockaddr *)&saddr,&saddrlen)) < 0) { i = errno; /* save error code */ #ifdef TCPIPLIB socket_close(tcpsrfd); #else /* TCPIPLIB */ close(tcpsrfd); #endif /* TCPIPLIB */ ttyfd = -1; wasclosed = 1; tcpsrfd = -1; tcpsrv_port = 0; errno = i; /* and report this error */ debug(F101,"tcpsrv_open accept errno","",errno); return(-1); } setsockopt(ttyfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on); #ifndef NOTCPOPTS #ifndef datageneral #ifdef SOL_SOCKET #ifdef TCP_NODELAY no_delay(ttyfd,tcp_nodelay); debug(F101,"tcpsrv_open no_delay","",tcp_nodelay); #endif /* TCP_NODELAY */ #ifdef SO_KEEPALIVE keepalive(ttyfd,tcp_keepalive); debug(F101,"tcpsrv_open keepalive","",tcp_keepalive); #endif /* SO_KEEPALIVE */ #ifdef SO_LINGER ck_linger(ttyfd,tcp_linger, tcp_linger_tmo); debug(F101,"tcpsrv_open linger","",tcp_linger_tmo); #endif /* SO_LINGER */ #ifdef SO_SNDBUF sendbuf(ttyfd,tcp_sendbuf); #endif /* SO_SNDBUF */ #ifdef SO_RCVBUF recvbuf(ttyfd,tcp_recvbuf); #endif /* SO_RCVBUF */ #endif /* SOL_SOCKET */ #endif /* datageneral */ #endif /* NOTCPOPTS */ ttnet = nett; /* TCP/IP (sockets) network */ tcp_incoming = 1; /* This is an incoming connection */ sstelnet = 1; /* Do server-side Telnet protocol */ /* See if the service is TELNET. */ x = (unsigned short)service->s_port; service2 = getservbyname("telnet", "tcp"); if (service2 && x == service2->s_port) { #ifdef COMMENT /* Jeff 2005/12/30 */ if (ttnproto != NP_TCPRAW && /* Yes... */ ttnproto != NP_SSL_RAW && ttnproto != NP_TLS_RAW) /* and if raw port not requested */ #else /* fdc 2005/12/04 */ if (ttnproto != NP_TCPRAW) /* Yes and if raw port not requested */ #endif /* */ ttnproto = NP_TELNET; /* set protocol to TELNET. */ } ckstrncpy(ipaddr,(char *)inet_ntoa(saddr.sin_addr),20); if (tcp_rdns) { if (!quiet) { printf(" Reverse DNS Lookup... "); fflush(stdout); } if (host = gethostbyaddr((char *)&saddr.sin_addr,4,PF_INET)) { host = ck_copyhostent(host); debug(F100,"tcpsrv_open gethostbyaddr != NULL","",0); if (!quiet) { printf("(OK)\n"); fflush(stdout); } name[0] = '*'; ckstrncpy(&name[1],host->h_name,78); ckstrncat(name,":",80-strlen(name)); ckstrncat(name,p,80-strlen(name)); if (!quiet #ifndef NOICP && !doconx #endif /* NOICP */ ) printf("%s connected on port %s\n",host->h_name,p); } else { if (!quiet) printf("Failed.\n"); } } else if (!quiet) printf("(OK)\n"); if (!tcp_rdns || !host) { ckstrncpy(name,ipaddr,80); ckstrncat(name,":",80); ckstrncat(name,ckuitoa(ntohs(saddr.sin_port)),80); if (!quiet #ifndef NOICP && !doconx #endif /* NOICP */ ) printf("%s connected on port %d\n",ipaddr,ntohs(saddr.sin_port)); } if (!quiet) fflush(stdout); #ifdef CK_SECURITY /* Before Initialization Telnet/Rlogin Negotiations Init Kerberos */ ck_auth_init((tcp_rdns && host && host->h_name && host->h_name[0]) ? (char *)host->h_name : ipaddr, ipaddr, uidbuf, ttyfd ); #endif /* CK_SECURITY */ #ifdef CK_SSL if (ck_ssleay_is_installed() && !ssl_failed) { if (ck_ssl_incoming(ttyfd) < 0) { #ifdef TCPIPLIB socket_close(ttyfd); socket_close(tcpsrfd); #else /* TCPIPLIB */ close(ttyfd); close(tcpsrfd); #endif /* TCPIPLIB */ ttyfd = -1; wasclosed = 1; tcpsrfd = -1; tcpsrv_port = 0; return(-1); } } #endif /* CK_SSL */ #ifndef datageneral /* Find out our own IP address. */ l_slen = sizeof(l_addr); bzero((char *)&l_addr, l_slen); #ifndef EXCELAN if (!getsockname(ttyfd, (struct sockaddr *)&l_addr, &l_slen)) { char * s = (char *)inet_ntoa(l_addr.sin_addr); ckstrncpy(myipaddr, s,20); debug(F110,"getsockname",myipaddr,0); } #endif /* EXCELAN */ #endif /* datageneral */ if (tn_ini() < 0) /* Start TELNET negotiations. */ if (ttchk() < 0) { /* Disconnected? */ i = errno; /* save error code */ #ifdef TCPIPLIB socket_close(tcpsrfd); #else /* TCPIPLIB */ close(tcpsrfd); #endif /* TCPIPLIB */ ttyfd = -1; wasclosed = 1; tcpsrfd = -1; tcpsrv_port = 0; errno = i; /* and report this error */ debug(F101,"tcpsrv_open accept errno","",errno); return(-1); } debug(F101,"tcpsrv_open service","",x); if (*lcl < 0) /* Set local mode. */ *lcl = 1; #ifdef CK_KERBEROS #ifdef KRB5_U2U if ( ttnproto == NP_K5U2U ) { if (k5_user_to_user_server_auth() != 0) { i = errno; /* save error code */ #ifdef TCPIPLIB socket_close(tcpsrfd); #else /* TCPIPLIB */ close(tcpsrfd); #endif /* TCPIPLIB */ ttyfd = -1; wasclosed = 1; tcpsrfd = -1; tcpsrv_port = 0; errno = i; /* and report this error */ debug(F101,"tcpsrv_open accept errno","",errno); return(-1); } } #endif /* KRB5_U2U */ #endif /* CK_KERBEROS */ return(0); /* Done. */ } else { i = errno; /* save error code */ #ifdef TCPIPLIB socket_close(tcpsrfd); #else /* TCPIPLIB */ close(tcpsrfd); #endif /* TCPIPLIB */ ttyfd = -1; wasclosed = 1; tcpsrfd = -1; tcpsrv_port = 0; errno = i; /* and report this error */ debug(F101,"tcpsrv_open accept errno","",errno); return(-1); } } #endif /* NOLISTEN */ #endif /* OS2 */ #endif /* TCPSOCKET */ #endif /* NONET */ #ifdef TCPSOCKET char * ckname2addr(name) char * name; { #ifdef HPUX5 return(""); #else struct hostent *host; if (name == NULL || *name == '\0') return(""); host = gethostbyname(name); if ( host ) { host = ck_copyhostent(host); return(inet_ntoa(*((struct in_addr *) host->h_addr))); } return(""); #endif /* HPUX5 */ } char * ckaddr2name(addr) char * addr; { #ifdef HPUX5 return(""); #else struct hostent *host; struct in_addr sin_addr; if (addr == NULL || *addr == '\0') return(""); sin_addr.s_addr = inet_addr(addr); host = gethostbyaddr((char *)&sin_addr,4,AF_INET); if (host) { host = ck_copyhostent(host); return((char *)host->h_name); } return(""); #endif /* HPUX5 */ } #endif /* TCPSOCKET */ unsigned long peerxipaddr = 0L; char * ckgetpeer() { #ifdef TCPSOCKET static char namebuf[256]; static struct hostent *host; static struct sockaddr_in saddr; #ifdef GPEERNAME_T static GPEERNAME_T saddrlen; #else #ifdef PTX static size_t saddrlen; #else #ifdef AIX42 /* It's size_t in 4.2 but int in 4.1 and earlier. */ /* Note: the 4.2 man page lies; believe socket.h. */ static size_t saddrlen; #else #ifdef UNIXWARE static size_t saddrlen; #else /* UNIXWARE */ #ifdef DEC_TCPIP /* 2010-03-08 SMS. * Coincidentally, the condition for integer arguments in select(), * which is actually "defined( _DECC_V4_SOURCE)", works for an integer * argument in getpeername(). Sadly, due to a lack of foresight, * "defined( _DECC_V4_SOURCE)" doesn't work with DEC C V4.0, so the * user-specified INTSELECT is used instead. Most likely, "size_t" * should be used instead of "unsigned int", but I'm a coward. */ #ifdef INTSELECT static int saddrlen; #else /* def INTSELECT */ static unsigned int saddrlen; #endif /* def INTSELECT [else] */ #else #ifdef MACOSX10 static unsigned int saddrlen; #else #ifdef CK_64BIT static socklen_t saddrlen; #else static int saddrlen; #endif /* CK_64BIT */ #endif /* MACOSX10 */ #endif /* DEC_TCPIP */ #endif /* UNIXWARE */ #endif /* AIX42 */ #endif /* PTX */ #endif /* GPEERNAME_T */ saddrlen = sizeof(saddr); if (getpeername(ttyfd,(struct sockaddr *)&saddr,&saddrlen) < 0) { debug(F111,"ckgetpeer failure",ckitoa(ttyfd),errno); return(NULL); } host = gethostbyaddr((char *)&saddr.sin_addr,4,AF_INET); if (host) { host = ck_copyhostent(host); ckstrncpy(namebuf,(char *)host->h_name,80); } else { ckstrncpy(namebuf,(char *)inet_ntoa(saddr.sin_addr),80); } peerxipaddr = ntohl(saddr.sin_addr.s_addr); debug(F111,"ckgetpeer",namebuf,peerxipaddr); return(namebuf); #else return(NULL); #endif /* TCPSOCKET */ } /* Get fully qualified IP hostname */ #ifndef NONET char * #ifdef CK_ANSIC ckgetfqhostname(char * name) #else ckgetfqhostname(name) char * name; #endif /* CK_ANSIC */ { #ifdef NOCKGETFQHOST return(name); #else /* If the following code dumps core, define NOCKGETFQHOST and rebuild. */ static char namebuf[256]; struct hostent *host=NULL; struct sockaddr_in r_addr; int i; debug(F110,"ckgetfqhn()",name,0); ckstrncpy(namebuf,name,256); namebuf[255] = '\0'; i = ckindex(":",namebuf,0,0,0); if (i) namebuf[i-1] = '\0'; bzero((char *)&r_addr, sizeof(r_addr)); host = gethostbyname(namebuf); if (host) { host = ck_copyhostent(host); debug(F100,"ckgetfqhn() gethostbyname != NULL","",0); r_addr.sin_family = host->h_addrtype; #ifdef HADDRLIST #ifdef h_addr /* This is for trying multiple IP addresses - see */ if (!(host->h_addr_list)) goto exit_func; bcopy(host->h_addr_list[0], (caddr_t)&r_addr.sin_addr, host->h_length ); #else bcopy(host->h_addr, (caddr_t)&r_addr.sin_addr, host->h_length); #endif /* h_addr */ #else /* HADDRLIST */ bcopy(host->h_addr, (caddr_t)&r_addr.sin_addr, host->h_length); #endif /* HADDRLIST */ #ifdef COMMENT #ifndef EXCELAN debug(F111,"BCOPY","host->h_addr",host->h_addr); #endif /* EXCELAN */ debug(F111,"BCOPY"," (caddr_t)&r_addr.sin_addr", (caddr_t)&r_addr.sin_addr); #endif /* COMMENT */ debug(F111,"BCOPY","host->h_length",host->h_length); #ifdef NT /* Windows 95/98 requires a 1 second wait between calls to Microsoft */ /* provided DNS functions. Otherwise, the TTL of the DNS response */ /* is ignored. */ if (isWin95()) sleep(1); #endif /* NT */ host = gethostbyaddr((char *)&r_addr.sin_addr,4,PF_INET); if (host) { host = ck_copyhostent(host); debug(F100,"ckgetfqhn() gethostbyaddr != NULL","",0); ckstrncpy(namebuf, host->h_name, 256); } } #ifdef HADDRLIST #ifdef h_addr exit_func: #endif /* h_addr */ #endif /* HADDRLIST */ if (i > 0) ckstrncat(namebuf,&name[i-1],256-strlen(namebuf)-strlen(&name[i-1])); debug(F110,"ckgetfqhn()",namebuf,0); return(namebuf); #endif /* NOCKGETFQHOST */ } VOID #ifdef CK_ANSIC setnproto(char * p) #else setnproto(p) char * p; #endif /* CK_ANSIC */ { if (!isdigit(*p)) { if (!strcmp("kermit",p)) ttnproto = NP_KERMIT; else if (!strcmp("telnet",p)) ttnproto = NP_TELNET; else if (!strcmp("http",p)) ttnproto = NP_TCPRAW; #ifdef RLOGCODE else if (!strcmp("login",p)) ttnproto = NP_RLOGIN; #endif /* RLOGCODE */ #ifdef CK_SSL /* Commonly used SSL ports (might not be in services file) */ else if (!strcmp("https",p)) { ttnproto = NP_SSL_RAW; ssl_only_flag = 1; } else if (!strcmp("ssl-telnet",p)) { ttnproto = NP_TELNET; ssl_only_flag = 1; } else if (!strcmp("telnets",p)) { ttnproto = NP_TELNET; ssl_only_flag = 1; } #endif /* CK_SSL */ #ifdef CK_KERBEROS #ifdef RLOGCODE else if (!strcmp("klogin",p)) { if (ck_krb5_is_installed()) ttnproto = NP_K5LOGIN; else if (ck_krb4_is_installed()) ttnproto = NP_K4LOGIN; else ttnproto = NP_RLOGIN; } else if (!strcmp("eklogin",p)) { if (ck_krb5_is_installed()) ttnproto = NP_EK5LOGIN; else if (ck_krb4_is_installed()) ttnproto = NP_EK4LOGIN; else ttnproto = NP_RLOGIN; } #endif /* RLOGCODE */ #endif /* CK_KERBEROS */ else ttnproto = NP_NONE; } else { switch (atoi(p)) { case 23: /* Telnet */ ttnproto = NP_TELNET; break; case 513: ttnproto = NP_RLOGIN; break; case 1649: ttnproto = NP_KERMIT; break; #ifdef CK_SSL case 443: #ifdef COMMENT /* Jeff 2005/12/30 */ ttnproto = NP_SSL_RAW; #else /* fdc 2005/12/04 */ ttnproto = NP_SSL; #endif /* COMMENT */ ssl_only_flag = 1; break; case 151: case 992: ttnproto = NP_TELNET; ssl_only_flag = 1; break; #endif /* CK_SSL */ #ifdef CK_KERBEROS case 543: if (ck_krb5_is_installed()) ttnproto = NP_K5LOGIN; else if (ck_krb4_is_installed()) ttnproto = NP_K4LOGIN; else ttnproto = NP_RLOGIN; break; case 2105: if (ck_krb5_is_installed()) ttnproto = NP_EK5LOGIN; else if (ck_krb4_is_installed()) ttnproto = NP_EK4LOGIN; else ttnproto = NP_RLOGIN; break; #endif /* CK_KERBEROS */ case 80: /* HTTP */ ttnproto = NP_TCPRAW; break; default: ttnproto = NP_NONE; break; } } } /* ckgetservice() is used to determine the port number for a given */ /* service taking into account the use of DNS SRV records. */ static struct servent servrec; static struct servent * ckgetservice(hostname, servicename, ip, iplen) char *hostname; char * servicename; char * ip; int iplen; { struct servent * service = NULL; #ifdef CK_DNS_SRV struct sockaddr * dns_addrs = NULL; int dns_naddrs = 0; #endif /* CK_DNS_SRV */ if (isdigit(*servicename)) { /* Use socket number without lookup */ service = &servrec; service->s_port = htons((unsigned short)atoi(servicename)); } else { /* Otherwise lookup the service name */ #ifdef CK_DNS_SRV if (tcp_dns_srv && !quiet) { printf(" DNS SRV Lookup... "); fflush(stdout); } if (tcp_dns_srv && locate_srv_dns(hostname, servicename, "tcp", &dns_addrs, &dns_naddrs ) ) { /* Use the first one. Eventually we should cycle through all */ /* the returned IP addresses and port numbers. */ struct sockaddr_in *sin = NULL; #ifdef BETADEBUG int i; printf("\r\n"); for ( i=0;isin_addr), ntohs(sin->sin_port)); } #endif /* BETADEBUG */ sin = (struct sockaddr_in *) &dns_addrs[0]; if ( ip && iplen > 0 ) ckstrncpy(ip,(char *)inet_ntoa(sin->sin_addr),iplen); service = &servrec; service->s_port = sin->sin_port; free(dns_addrs); dns_addrs = NULL; dns_naddrs = 0; } else #endif /* CK_DNS_SRV */ service = getservbyname(servicename, "tcp"); } if (!service) { if (!ckstrcmp("kermit",servicename,-1,0)) { /* Kermit service port */ service = &servrec; service->s_port = htons(1649); } else if (!ckstrcmp("telnet",servicename,-1,0)) { /* Telnet port */ service = &servrec; service->s_port = htons(23); } else if (!ckstrcmp("http",servicename,-1,0)) { service = &servrec; service->s_port = htons(80); } #ifdef RLOGCODE else if (!ckstrcmp("login",servicename,-1,0)) { service = &servrec; service->s_port = htons(513); } #endif /* RLOGCODE */ #ifdef CK_SSL /* Commonly used SSL ports (might not be in services file) */ else if (!ckstrcmp("https",servicename,-1,0)) { service = &servrec; service->s_port = htons(443); } else if (!ckstrcmp("ssl-telnet",servicename,-1,0)) { service = &servrec; service->s_port = htons(151); } else if (!ckstrcmp("telnets",servicename,-1,0)) { service = &servrec; service->s_port = htons(992); } #endif /* CK_SSL */ #ifdef CK_KERBEROS #ifdef RLOGCODE else if (!ckstrcmp("klogin",servicename,-1,0)) { service = &servrec; service->s_port = htons(543); } else if (!ckstrcmp("eklogin",servicename,-1,0)) { service = &servrec; service->s_port = htons(2105); } #endif /* RLOGCODE */ #endif /* CK_KERBEROS */ } return(service); } /* N E T O P E N -- Open a network connection */ /* Calling conventions same as ttopen(), except third argument is network type rather than modem type. Designed to be called from within ttopen. */ #define XXNAMELEN 256 static char xxname[XXNAMELEN]; int netopen(name, lcl, nett) char *name; int *lcl, nett; { char *p; int i, x, rc_inet_addr = 0, dns = 0; #ifdef TCPSOCKET int isconnect = 0; #ifdef SO_OOBINLINE int on = 1; #endif /* SO_OOBINLINE */ struct servent *service=NULL; struct hostent *host=NULL; struct sockaddr_in r_addr; struct sockaddr_in sin; struct sockaddr_in l_addr; GSOCKNAME_T l_slen; #ifdef EXCELAN struct sockaddr_in send_socket; #endif /* EXCELAN */ #ifdef INADDRX /* inet_addr() is of type struct in_addr */ #ifdef datageneral extern struct in_addr inet_addr(); #else #ifdef HPUX5WINTCP extern struct in_addr inet_addr(); #endif /* HPUX5WINTCP */ #endif /* datageneral */ struct in_addr iax; #else #ifdef INADDR_NONE struct in_addr iax; #else /* INADDR_NONE */ long iax; #endif /* INADDR_NONE */ #endif /* INADDRX */ #endif /* TCPSOCKET */ #ifdef COMMENT /* This causes big trouble */ #ifndef INADDR_NONE #define INADDR_NONE 0xffffffff #endif /* INADDR_NONE */ #endif /* COMMENT */ #ifdef SUNX25 /* Code for SunLink X.25 support */ #define X29PID 1 /* X.29 Protocol ID */ _PROTOTYP(SIGTYP x25oobh, (int) ); CONN_DB x25host; #ifndef X25_WR_FACILITY FACILITY x25facil; #else FACILITY_DB x25facil; #endif /* X25_WR_FACILITY */ static int needh = 1; PID_T pid; extern int linkid, lcn, x25ver; #endif /* SUNX25 */ #ifdef ANYX25 extern int revcall, closgr, cudata; extern char udata[]; #endif /* ANYX25 */ #ifdef IBMX25 /* Variables for IBM X25 */ extern int x25port; /* Logical port to use */ extern x25addr_t local_nua; /* Local X.25 address */ extern x25addr_t remote_nua; /* Remote X.25 address */ extern char x25name[]; /* X25 device name (sx25a0) */ extern char x25dev[]; /* X25 device file /dev/x25pkt */ ulong bind_flags = 0; /* Flags for binding the X25 stream */ ulong token = 0; /* Temporary return code */ #endif /* IBMX25 */ debug(F101,"netopen nett","",nett); *ipaddr = '\0'; /* Initialize IP address string */ #ifdef SUNX25 if (nett == NET_SX25) { /* If network type is X.25 */ netclos(); /* Close any previous net connection */ ttnproto = NP_NONE; /* No protocol selected yet */ /* Set up host structure */ bzero((char *)&x25host,sizeof(x25host)); if ((x25host.hostlen = pkx121(name,x25host.host)) < 0) { fprintf (stderr,"Invalid X.121 host address %s\n",name); errno = 0; return (-1); } x25host.datalen = X29PIDLEN; x25host.data[0] = X29PID; /* Set call user data if specified */ if (cudata) { ckstrncpy((char *)x25host.data+X29PIDLEN,udata,(int)strlen(udata)); x25host.datalen += (int)strlen(udata); } /* Open SunLink X.25 socket */ if (!quiet && *name) { printf(" Trying %s... ", name); fflush(stdout); } if ((ttyfd = socket(AF_X25, SOCK_STREAM, 0)) < 0) { debug(F101,"netopen socket error","",errno); perror ("X.25 socket error"); return (-1); } /* Setting X.25 out-of-band data handler */ pid = getpid(); if (ioctl(ttyfd,SIOCSPGRP,&pid)) { perror("X.25 set process group id error"); return(-1); } (VOID) signal(SIGURG,x25oobh); /* Set reverse charge call and closed user group if requested */ bzero ((char *)&x25facil,sizeof(x25facil)); #ifndef X25_WR_FACILITY /* New SunLink (7.0 or 8.0, not sure which)... */ x25facil.type = T_REVERSE_CHARGE; /* Reverse Charge */ x25facil.f_reverse_charge = revcall ? 1 : 0; if (ioctl(ttyfd,X25_SET_FACILITY,&x25facil) < 0) { perror ("Setting X.25 reverse charge"); return (-1); } if (closgr > -1) { /* Closed User Group (Outgoing) */ bzero ((char *)&x25facil,sizeof(x25facil)); x25facil.type = T_CUG; x25facil.f_cug_req = CUG_REQ_ACS; x25facil.f_cug_index = closgr; if (ioctl(ttyfd,X25_SET_FACILITY,&x25facil) < 0) { perror ("Setting X.25 closed user group"); return (-1); } } #else /* Old SunLink 6.0 (or 7.0?)... */ if (revcall) x25facil.reverse_charge = revcall; if (closgr > -1) { x25facil.cug_req = 1; x25facil.cug_index = closgr; } if (ioctl(ttyfd,X25_WR_FACILITY,&x25facil) < 0) { perror ("Setting X.25 facilities"); return (-1); } #endif /* X25_WR_FACILITY */ /* Need X.25 header with bits Q and M */ if (ioctl (ttyfd,X25_HEADER,&needh) < 0) { perror ("Setting X.25 header"); return (-1); } /* Connects to remote host via SunLink X.25 */ if (connect(ttyfd,(struct sockaddr *)&x25host,sizeof(x25host)) < 0) { i = errno; debug(F101,"netopen connect errno","",i); if (i) { perror("netopen x25 connect"); x25diag(); } (VOID) netclos(); ttyfd = -1; wasclosed = 1; ttnproto = NP_NONE; errno = i; return (-1); } /* Get X.25 link identification used for the connection */ if (ioctl(ttyfd,X25_GET_LINK,&linkid) < 0) { perror ("Getting X.25 link id"); return (-1); } /* Get X.25 logical channel number used for the connection */ if (ioctl(ttyfd,X25_RD_LCGN,&lcn) < 0) { perror ("Getting X.25 lcn"); return (-1); } /* Get SunLink X.25 version */ if (ioctl(ttyfd,X25_VERSION,&x25ver) < 0) { perror ("Getting SunLink X.25 version"); return (-1); } ttnet = nett; /* Sunlink X.25 network */ ttnproto = NP_X3; /* PAD X.3, X.28, X.29 protocol */ if (lcl) if (*lcl < 0) *lcl = 1; /* Local mode */ return(0); } else /* Note that SUNX25 support can coexist with TCP/IP support. */ #endif /* SUNX25 */ #ifdef IBMX25 /* riehm */ if (nett == NET_IX25) { /* IBM AIX X.25 */ netclos(); /* Close any previous net connection */ ttnproto = NP_NONE; /* No protocol selected yet */ /* find out who we are - this is not so easy on AIX */ /* riehm: need to write the code that finds this out * automatically, or at least allow it to be configured * somehow */ if (!local_nua[0] && !x25local_nua(local_nua)) { return(-1); } /* Initialise the X25 API (once per process? once per connection?) */ debug(F110, "Opening ", x25dev, 0 ); /* set O_NDELAY to allow polling? */ if ((ttyfd = open(x25dev, O_RDWR)) < 0) { perror ("X.25 device open error"); debug(F101,"netopen: device open error","",errno); return (-1); } /* push the NPI onto the STREAM */ if (ioctl(ttyfd,I_PUSH,"npi") < 0 ) { close(ttyfd); ttyfd = -1; wasclosed = 1; perror( "kermit: netopen(): couldn't push npi on the X25 stream" ); debug(F101,"netopen: can't push npi on the X25 stream","",errno); return (-1); } /* set up server mode - bind the x25 port and wait for * incoming connections */ if (name[0] == '*') { /* Server */ /* set up a server - see the warning in x25bind() */ bind_flags |= TOKEN_REQUEST; /* bind kermit to the local X25 address */ token = x25bind(ttyfd, local_nua, udata, (int)strlen( udata ), 1, x25port, bind_flags ); if (token < 0) { debug(F100,"netopen: couldn't bind to local X25 address","",0); netclos(); return(-1); } /* Currently not connected to a remote host */ remote_nua[0] = '\0'; /* store the fd so that incoming calls can have their own fd * This is almost support for a true server (ie: a'la ftpd) * but we're not quite there yet. * used in netclos() */ x25serverfd = ttyfd; /* * wait for an incoming call * this should happen in the "server" command and not in * the "set host *" command. */ if ((ttyfd = x25getcall(ttyfd)) < 0) { netclos(); return(-1); } } else { /* Client */ /* Bind kermit to the local X25 address */ token = x25bind( ttyfd, local_nua, (char *)NULL, 0, 0, x25port, bind_flags ); if (token < 0) { debug(F100,"netopen: couldn't bind to local X25 address","",0); netclos(); return(-1); } /* riehm: this should be done via the CONNECT command, not HOST! */ { x25serverfd = 0; /* call the remote host */ /* name == address of remote host as char* */ if (x25call(ttyfd, name, udata) < 0 ) { debug(F100, "netopen: couldn't connect to remote X25 address", "", 0); netclos(); return(-1); } strcpy(remote_nua, name); } } ttnet = nett; /* AIX X.25 network */ if (lcl) if (*lcl < 0) *lcl = 1; /* Local mode */ return(0); } else /* Note that IBMX25 support can coexist with TCP/IP support. */ #endif /* IBMX25 */ /* Add support for other networks here. */ if (nett != NET_TCPB) return(-1); /* BSD socket support */ #ifdef TCPSOCKET netclos(); /* Close any previous connection. */ ckstrncpy(namecopy, name, NAMECPYL); /* Copy the hostname. */ debug(F110,"netopen namecopy",namecopy,0); #ifndef NOLISTEN if (name[0] == '*') return(tcpsrv_open(name, lcl, nett, 0)); #endif /* NOLISTEN */ p = namecopy; /* Was a service requested? */ while (*p != '\0' && *p != ':') p++; /* Look for colon */ if (*p == ':') { /* Have a colon */ debug(F110,"netopen name has colon",namecopy,0); *p++ = '\0'; /* Get service name or number */ #ifdef CK_URL /* Here we have to check for various popular syntaxes: host:port (our original syntax) URL such as telnet:host or telnet://host/ Or even telnet://user:password@host:port/path/ Or a malformed URL such as generated by Netscape 4.0 like: telnet:telnet or telnet::host. */ /* * REPLACE THIS CODE WITH urlparse() but not on the day of the * C-Kermit 8.0 RELEASE. */ if (*p == ':') /* a second colon */ *p++ = '\0'; /* get rid of that one too */ while (*p == '/') *p++ = '\0'; /* and slashes */ x = strlen(p); /* Length of remainder */ if (p[x-1] == '/') /* If there is a trailing slash */ p[x-1] = '\0'; /* remove it. */ debug(F110,"netopen namecopy after stripping",namecopy,0); debug(F110,"netopen p after stripping",p,0); service = getservbyname(namecopy,"tcp"); if (service || #ifdef RLOGCODE !ckstrcmp("rlogin",namecopy,NAMECPYL,0) || #endif /* RLOGCODE */ #ifdef CK_SSL !ckstrcmp("telnets",namecopy,NAMECPYL,0) || #endif /* CK_SSL */ !ckstrcmp("iksd",namecopy,NAMECPYL,0) ) { char temphost[256], tempservice[80], temppath[256]; char * q = p, *r = p, *w = p; int uidfound=0; extern char pwbuf[]; extern int pwflg, pwcrypt; if (ttnproto == NP_DEFAULT) setnproto(namecopy); /* Check for userid and possibly password */ while (*p != '\0' && *p != '@') p++; /* look for @ */ if (*p == '@') { /* found username and perhaps password */ debug(F110,"netopen namecopy found @","",0); *p = '\0'; p++; while (*w != '\0' && *w != ':') w++; if (*w == ':') *w++ = '\0'; /* r now points to username, save it and the password */ debug(F110,"netopen namecopy username",r,0); debug(F110,"netopen namecopy password",w,0); uidfound=1; if ( strcmp(uidbuf,r) || *w ) ckstrncpy(pwbuf,w,PWBUFL+1); ckstrncpy(uidbuf,r,UIDBUFLEN); pwflg = 1; pwcrypt = 0; q = p; /* Host after user and pwd */ } else { p = q; /* No username or password */ } /* Now we must look for the optional port. */ debug(F110,"netopen x p",p,0); debug(F110,"netopen x q",q,0); /* Look for the port/service or a file/directory path */ while (*p != '\0' && *p != ':' && *p != '/') p++; if (*p == ':') { debug(F110,"netopen found port",q,0); *p++ = '\0'; /* Found a port name or number */ r = p; /* Look for the end of port/service or a file/directory path */ while (*p != '\0' && *p != '/') p++; if (*p == '/') *p++ = '\0'; debug(F110,"netopen port",r,0); ckstrncpy(tempservice,r,80); ckstrncpy(temphost,q,256); ckstrncpy(temppath,p,256); ckstrncpy(namecopy,temphost,NAMECPYL); debug(F110,"netopen tempservice",tempservice,0); debug(F110,"netopen temphost",temphost,0); debug(F110,"netopen temppath",temppath,0); /* move port/service to a buffer that won't go away */ x = strlen(namecopy); p = namecopy + x + 1; ckstrncpy(p, tempservice, NAMECPYL - x); } else { /* Handle a path if we found one */ if (*p == '/') *p++ = '\0'; ckstrncpy(temppath,p,256); /* We didn't find another port, but if q is a service */ /* then assume that namecopy is actually a host. */ if (getservbyname(q,"tcp")) { p = q; } else { #ifdef RLOGCODE /* rlogin is not a valid service */ if (!ckstrcmp("rlogin",namecopy,6,0)) { ckstrncpy(namecopy,"login",NAMECPYL); } #endif /* RLOGCODE */ /* iksd is not a valid service */ if (!ckstrcmp("iksd",namecopy,6,0)) { ckstrncpy(namecopy,"kermit",NAMECPYL); } /* Reconstruct namecopy */ ckstrncpy(tempservice,namecopy,80); ckstrncpy(temphost,q,256); ckstrncpy(namecopy,temphost,NAMECPYL); debug(F110,"netopen tempservice",tempservice,0); debug(F110,"netopen temphost",temphost,0); debug(F110,"netopen temppath",temppath,0); /* move port/service to a buffer that won't go away */ x = strlen(namecopy); p = namecopy + x + 1; ckstrncpy(p, tempservice, NAMECPYL - x - 1); } } debug(F110,"netopen URL result: host",namecopy,0); debug(F110,"netopen URL result: service",p,0); debug(F110,"netopen URL result: path",temppath,0); #ifdef IKS_GET /* If we have set a path specified, we need to try to GET it */ /* But we have another problem, we have to login first. How */ /* do we specify that a login must be done before the GET? */ /* The user's name if specified is in 'userid' and the */ /* password if any is in 'pwbuf'. */ if ( temppath[0] ) { extern int action; extern char * cmarg; if ( !uidfound ) { /* If no userid was specified as part of the URL but * a path was specified, then we * set the user name to anonymous and the password * to the current userid. */ ckstrncpy(pwbuf,uidbuf,PWBUFL); ckstrncat(pwbuf,"@",PWBUFL); pwflg = 1; pwcrypt = 0; ckstrncpy(uidbuf,"anonymous",UIDBUFLEN); } /* * If a file path was specified we perform the GET * operation and then terminate the connection. * * If a directory was given instead of a file, then * we should REMOTE CD to the directory and list its * contents. But how do we tell the difference? */ makestr(&cmarg,temppath); action = 'r'; } #endif /* IKS_GET */ } #endif /* CK_URL */ } else { /* Otherwise use telnet */ p = "telnet"; } /* By the time we get here, namecopy[] should hold the null-terminated hostname or address, and p should point to the service name or number. */ debug(F110,"netopen host",namecopy,0); debug(F110,"netopen service requested",p,0); /* Use the service port to set the default protocol type if necessary */ if (ttnproto == NP_DEFAULT) setnproto(p); ckstrncpy(namecopy2,namecopy,NAMECPYL); service = ckgetservice(namecopy,p,namecopy,NAMECPYL); if (!service) { fprintf(stderr, "Can't find port for service %s\n", p); #ifdef TGVORWIN debug(F101,"netopen can't get service","",socket_errno); #else debug(F101,"netopen can't get service","",errno); #endif /* TGVORWIN */ errno = 0; /* (rather than mislead) */ return(-1); } else { if (!ckstrcmp(namecopy,namecopy2,-1,0)) namecopy2[0] = '\0'; ckstrncpy(svcbuf,ckuitoa(ntohs(service->s_port)),sizeof(svcbuf)); debug(F110,"netopen service ok",svcbuf,0); } #ifdef RLOGCODE if (service && !strcmp("login",p) && service->s_port != htons(513)) { fprintf(stderr, " Warning: login service on port %d instead of port 513\n", ntohs(service->s_port) ); fprintf(stderr, " Edit SERVICES file if RLOGIN fails to connect.\n"); debug(F101,"tcpsrv_open login on port","",ntohs(service->s_port)); } #endif /* RLOGCODE */ #ifndef NOHTTP /* For HTTP connections we must preserve the original hostname and */ /* service requested so we can include them in the Host header. */ ckmakmsg(http_host_port,sizeof(http_host_port),namecopy,":", ckitoa(ntohs(service->s_port)),NULL); /* 'namecopy' contains the name of the host to which we want to connect */ /* 'svcbuf' contains the service name */ /* 'service->s_port' contains the port number in network byte order */ /* If we are using an http proxy, we need to create a buffer containing */ /* hostname:port-number */ /* to pass to the http_connect() function. Then we need to replace */ /* 'namecopy' with the name of the proxy server and the service->s_port */ /* with the port number of the proxy (default port 80). */ if ( tcp_http_proxy ) { ckmakmsg(proxycopy,sizeof(proxycopy),namecopy,":", ckuitoa(ntohs(service->s_port)),NULL); ckstrncpy(namecopy,tcp_http_proxy,NAMECPYL); p = namecopy; /* Was a service requested? */ while (*p != '\0' && *p != ':') p++; /* Look for colon */ if (*p == ':') { /* Have a colon */ debug(F110,"netopen name has colon",namecopy,0); *p++ = '\0'; /* Get service name or number */ } else { strcpy(++p,"http"); } service = ckgetservice(namecopy,p,namecopy,NAMECPYL); if (!service) { fprintf(stderr, "Can't find port for service %s\n", p); #ifdef TGVORWIN debug(F101,"netopen can't get service for proxy","",socket_errno); #else debug(F101,"netopen can't get service for proxy","",errno); #endif /* TGVORWIN */ errno = 0; /* (rather than mislead) */ return(-1); } ckstrncpy(p,ckuitoa(ntohs(service->s_port)),NAMECPYL-(p-namecopy)); } #endif /* NOHTTP */ /* Set up socket structure and get host address */ bzero((char *)&r_addr, sizeof(r_addr)); debug(F100,"netopen bzero ok","",0); /* NOTE: Originally the inet_addr() check was #ifdef NT, but is enabled for all as of 20 Sep 97, to allow people to "set host" to a specific numeric IP address without going through the multihomed host sequence and winding up at a different place than the one requested. */ #ifdef INADDR_NONE debug(F101,"netopen INADDR_NONE defined","",INADDR_NONE); #else /* INADDR_NONE */ debug(F100,"netopen INADDR_NONE not defined","",0); #endif /* INADDR_NONE */ #ifdef INADDRX debug(F100,"netopen INADDRX defined","",0); #else /* INADDRX */ debug(F100,"netopen INADDRX not defined","",0); #endif /* INADDRX */ #ifndef NOMHHOST #ifdef INADDRX iax = inet_addr(namecopy); debug(F111,"netopen inet_addr",namecopy,iax.s_addr); #else /* INADDRX */ #ifdef INADDR_NONE iax.s_addr = inet_addr(namecopy); debug(F111,"netopen inet_addr",namecopy,iax.s_addr); #else /* INADDR_NONE */ #ifdef SOLARIS /* In Solaris inet_addr() is of type in_addr_t which is uint32_t */ /* (unsigned) yet it returns -1 (signed) on failure. */ /* It makes a difference in 64-bit builds. */ rc_inet_addr = inet_addr(namecopy); /* Assign return code to an int */ iax = (unsigned) rc_inet_addr; /* and from there to whatever.. */ #else #ifndef datageneral iax = (unsigned int) inet_addr(namecopy); #else iax = -1L; #endif /* datageneral */ #endif /* SOLARIS */ debug(F111,"netopen rc_inet_addr",namecopy,rc_inet_addr); debug(F111,"netopen inet_addr",namecopy,iax); #endif /* INADDR_NONE */ #endif /* INADDRX */ dns = 0; if ( /* This might give warnings on 64-bit platforms but they should be harmless */ /* because INADDR_NONE should be all 1's anyway, thus the OR part is */ /* probably superfluous -- not sure why it's even there, maybe it should be */ /* removed. */ #ifdef SOLARIS rc_inet_addr == -1 #else #ifdef INADDR_NONE iax.s_addr == INADDR_NONE /* || iax.s_addr == (unsigned long) -1L */ #else /* INADDR_NONE */ iax < 0 #endif /* INADDR_NONE */ #endif /* SOLARIS */ ) { if (!quiet) { printf(" DNS Lookup... "); fflush(stdout); } if ((host = gethostbyname(namecopy)) != NULL) { debug(F110,"netopen gethostbyname != NULL",namecopy,0); host = ck_copyhostent(host); dns = 1; /* Remember we performed dns lookup */ r_addr.sin_family = host->h_addrtype; if (tcp_rdns && host->h_name && host->h_name[0] #ifndef NOHTTP && (tcp_http_proxy == NULL) #endif /* NOHTTP */ ) { #ifdef COMMENT ckstrncpy(xxname,host->h_name,XXNAMELEN); debug(F110,"netopen xxname[1]",xxname,0); if ((XXNAMELEN - (int)strlen(name)) > ((int)strlen(svcbuf)+1)){ ckstrncat(xxname,":",XXNAMELEN - (int)strlen(xxname)); ckstrncat(xxname,svcbuf,XXNAMELEN - (int)strlen(xxname)); debug(F110,"netopen xxname[2]",xxname,0); } name = (char *)xxname; #else ckstrncpy(name,host->h_name,80); /* Bad Bad Bad */ if ( (80-strlen(name)) > (strlen(svcbuf)+1) ) { ckstrncat(name,":",80-strlen(name)); ckstrncat(name,svcbuf,80-strlen(name)); } #endif /* COMMENT */ } debug(F110,"netopen name after lookup",name,0); #ifdef HADDRLIST #ifdef h_addr /* This is for trying multiple IP addresses - see */ if (!(host->h_addr_list)) return(-1); bcopy(host->h_addr_list[0], (caddr_t)&r_addr.sin_addr, host->h_length ); #else bcopy(host->h_addr, (caddr_t)&r_addr.sin_addr, host->h_length); #endif /* h_addr */ #else /* HADDRLIST */ #ifdef HPUX6 r_addr.sin_addr.s_addr = (u_long)host->h_addr; #else /* HPUX6 */ bcopy(host->h_addr, (caddr_t)&r_addr.sin_addr, host->h_length); #endif /* HPUX6 */ #endif /* HADDRLIST */ #ifndef HPUX6 debug(F111,"BCOPY","host->h_length",host->h_length); #endif /* HPUX6 */ } } #endif /* NOMHHOST */ debug(F101,"netopen dns","",dns); if (!dns) { #ifdef INADDRX /* inet_addr() is of type struct in_addr */ struct in_addr ina; unsigned long uu; debug(F100,"netopen gethostbyname == NULL: INADDRX","",0); ina = inet_addr(namecopy); uu = *(unsigned int *)&ina; #else /* Not INADDRX */ /* inet_addr() is unsigned long */ unsigned long uu; debug(F100,"netopen gethostbyname == NULL: Not INADDRX","",0); uu = inet_addr(namecopy); #endif /* INADDRX */ debug(F101,"netopen uu","",uu); if ( #ifdef INADDR_NONE !(uu == INADDR_NONE || uu == (unsigned int) -1L) #else /* INADDR_NONE */ uu != ((unsigned long)-1) #endif /* INADDR_NONE */ ) { r_addr.sin_addr.s_addr = uu; r_addr.sin_family = AF_INET; } else { #ifdef VMS fprintf(stdout, "\r\n"); /* complete any previous message */ #endif /* VMS */ fprintf(stderr, "Can't get address for %s\n", namecopy); #ifdef TGVORWIN debug(F101,"netopen can't get address","",socket_errno); #else debug(F101,"netopen can't get address","",errno); #endif /* TGVORWIN */ errno = 0; /* Rather than mislead */ return(-1); } } /* Get a file descriptor for the connection. */ r_addr.sin_port = service->s_port; ckstrncpy(ipaddr,(char *)inet_ntoa(r_addr.sin_addr),20); debug(F110,"netopen trying",ipaddr,0); if (!quiet && *ipaddr) { printf(" Trying %s... ", ipaddr); fflush(stdout); } /* Loop to try additional IP addresses, if any. */ do { #ifdef EXCELAN send_socket.sin_family = AF_INET; send_socket.sin_addr.s_addr = 0; send_socket.sin_port = 0; if ((ttyfd = socket(SOCK_STREAM, (struct sockproto *)0, &send_socket, SO_REUSEADDR)) < 0) #else /* EXCELAN */ #ifdef NT #ifdef COMMENT_X /* Must make sure that all sockets are opened in Non-overlapped mode since we use the standard C RTL functions to read and write data. But it doesn't seem to work as planned. */ { int optionValue = SO_SYNCHRONOUS_NONALERT; if (setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, (char *) &optionValue, sizeof(optionValue)) != NO_ERROR) return(-1); } #endif /* COMMENT */ #endif /* NT */ if ((ttyfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) #endif /* EXCELAN */ { #ifdef EXCELAN experror("TCP socket error"); #else #ifdef VMS fprintf(stdout, "\r\n"); /* complete any previous stdout */ #endif /* VMS */ #ifdef TGVORWIN #ifdef OLD_TWG errno = socket_errno; #endif /* OLD_TWG */ socket_perror("TCP socket error"); debug(F101,"netopen socket error","",socket_errno); #else perror("TCP socket error"); debug(F101,"netopen socket error","",errno); #endif /* TGVORWIN */ #endif /* EXCELAN */ return (-1); } errno = 0; #ifdef RLOGCODE /* Not part of the RLOGIN RFC, but the BSD implementation */ /* requires that the client port be a priviliged port (<1024) */ /* on a Unix system this would require SuperUser permissions */ /* thereby saying that the root of the Unix system has given */ /* permission for this connection to be created */ if (service->s_port == htons((unsigned short)RLOGIN_PORT)) { static unsigned short lport = 1024; /* max reserved port */ #ifdef OS2 int s_errno; #endif /* OS2 */ lport--; /* Make sure we do not reuse a port */ if (lport == 512) lport = 1023; sin.sin_family = AF_INET; if (tcp_address) { #ifdef INADDRX inaddrx = inet_addr(tcp_address); sin.sin_addr.s_addr = *(unsigned long *)&inaddrx; #else sin.sin_addr.s_addr = inet_addr(tcp_address); #endif /* INADDRX */ } else sin.sin_addr.s_addr = INADDR_ANY; while (1) { sin.sin_port = htons(lport); if (bind(ttyfd, (struct sockaddr *)&sin, sizeof(sin)) >= 0) break; #ifdef OS2 s_errno = socket_errno; if (s_errno && /* OS2 bind fails with 0, if already in use */ #ifdef NT s_errno != WSAEADDRINUSE #else s_errno != SOCEADDRINUSE && s_errno != (SOCEADDRINUSE - SOCBASEERR) #endif /* NT */ ) #else /* OS2 */ #ifdef TGVORWIN if (socket_errno != EADDRINUSE) #else if (errno != EADDRINUSE) #endif /* TGVORWIN */ #endif /* OS2 */ { #ifdef COMMENT printf("\nBind failed with errno %d for port %d.\n", #ifdef OS2 s_errno #else #ifdef TGVORWIN socket_errno #else errno #endif /* TGVORWIN */ #endif /* OS2 */ , lport ); #ifdef OS2 debug(F101,"rlogin bind failed","",s_errno); #else #ifdef TGVORWIN debug(F101,"rlogin bind failed","",socket_errno); #ifdef OLD_TWG errno = socket_errno; #endif /* OLD_TWG */ socket_perror("rlogin bind"); #else debug(F101,"rlogin bind failed","",errno); perror("rlogin bind"); #endif /* TGVORWIN */ #endif /* OS2 */ #else /* COMMENT */ #ifdef OS2 debug(F101,"rlogin bind s_errno","",s_errno); perror("rlogin bind"); #else #ifdef VMS printf("\r\n"); /* complete any previous message */ #endif /* VMS */ #ifdef TGVORWIN debug(F101,"rlogin bind socket_errno","",socket_errno); #ifdef OLD_TWG errno = socket_errno; #endif /* OLD_TWG */ socket_perror("rlogin bind"); #else debug(F101,"rlogin bind errno","",errno); perror("rlogin bind"); #endif /* TGVORWIN */ #endif /* OS2 */ debug(F101,"rlogin local port","",lport); #endif /* COMMENT */ netclos(); return -1; } lport--; if (lport == 512 /* lowest reserved port to use */ ) { printf("\nNo reserved ports available.\n"); netclos(); return -1; } } debug(F101,"rlogin lport","",lport); ttnproto = NP_RLOGIN; } else #endif /* RLOGCODE */ /* If a specific TCP address on the local host is desired we */ /* must bind it to the socket. */ #ifndef datageneral if (tcp_address) { int s_errno; debug(F110,"netopen binding socket to",tcp_address,0); bzero((char *)&sin,sizeof(sin)); sin.sin_family = AF_INET; #ifdef INADDRX inaddrx = inet_addr(tcp_address); sin.sin_addr.s_addr = *(unsigned long *)&inaddrx; #else sin.sin_addr.s_addr = inet_addr(tcp_address); #endif /* INADDRX */ sin.sin_port = 0; if (bind(ttyfd, (struct sockaddr *)&sin, sizeof(sin)) < 0) { s_errno = socket_errno; /* Save error code */ #ifdef TCPIPLIB socket_close(ttyfd); #else /* TCPIPLIB */ close(ttyfd); #endif /* TCPIPLIB */ ttyfd = -1; wasclosed = 1; errno = s_errno; /* and report this error */ debug(F101,"netopen bind errno","",errno); return(-1); } } #endif /* datageneral */ /* Now connect to the socket on the other end. */ #ifdef EXCELAN if (connect(ttyfd, &r_addr) < 0) #else #ifdef NT WSASafeToCancel = 1; #endif /* NT */ if (connect(ttyfd, (struct sockaddr *)&r_addr, sizeof(r_addr)) < 0) #endif /* EXCELAN */ { #ifdef NT WSASafeToCancel = 0; #endif /* NT */ #ifdef OS2 i = socket_errno; #else /* OS2 */ #ifdef TGVORWIN i = socket_errno; #else i = errno; /* Save error code */ #endif /* TGVORWIN */ #endif /* OS2 */ #ifdef RLOGCODE if ( #ifdef OS2 i && /* OS2 bind fails with 0, if already in use */ #ifdef NT i == WSAEADDRINUSE #else (i == SOCEADDRINUSE || i == (SOCEADDRINUSE - SOCBASEERR)) #endif /* NT */ #else /* OS2 */ #ifdef TGVORWIN socket_errno == EADDRINUSE #else errno == EADDRINUSE #endif /* TGVORWIN */ #endif /* OS2 */ && ttnproto == NP_RLOGIN) { #ifdef TCPIPLIB socket_close(ttyfd); /* Close it. */ #else close(ttyfd); #endif /* TCPIPLIB */ continue; /* Try a different lport */ } #endif /* RLOGCODE */ #ifdef HADDRLIST #ifdef h_addr if (host && host->h_addr_list && host->h_addr_list[1]) { perror(""); host->h_addr_list++; bcopy(host->h_addr_list[0], (caddr_t)&r_addr.sin_addr, host->h_length); ckstrncpy(ipaddr,(char *)inet_ntoa(r_addr.sin_addr),20); debug(F110,"netopen h_addr_list",ipaddr,0); if (!quiet && *ipaddr) { printf(" Trying %s... ", ipaddr); fflush(stdout); } #ifdef TCPIPLIB socket_close(ttyfd); /* Close it. */ #else close(ttyfd); #endif /* TCPIPLIB */ continue; } #endif /* h_addr */ #endif /* HADDRLIST */ netclos(); ttyfd = -1; wasclosed = 1; ttnproto = NP_NONE; errno = i; /* And report this error */ #ifdef EXCELAN if (errno) experror("netopen connect"); #else #ifdef TGVORWIN debug(F101,"netopen connect error","",socket_errno); /* if (errno) socket_perror("netopen connect"); */ #ifdef OLD_TWG errno = socket_errno; #endif /* OLD_TWG */ if (!quiet) socket_perror("netopen connect"); #else /* TGVORWIN */ debug(F101,"netopen connect errno","",errno); #ifdef VMS if (!quiet) perror("\r\nFailed"); #else if (!quiet) perror("Failed"); #endif /* VMS */ #ifdef DEC_TCPIP if (!quiet) perror("netopen connect"); #endif /* DEC_TCPIP */ #ifdef CMU_TCPIP if (!quiet) perror("netopen connect"); #endif /* CMU_TCPIP */ #endif /* TGVORWIN */ #endif /* EXCELAN */ return(-1); } #ifdef NT WSASafeToCancel = 0; #endif /* NT */ isconnect = 1; } while (!isconnect); #ifdef NON_BLOCK_IO on = 1; x = socket_ioctl(ttyfd,FIONBIO,&on); debug(F101,"netopen FIONBIO","",x); #endif /* NON_BLOCK_IO */ #ifdef NT_TCP_OVERLAPPED OverlappedWriteInit(); OverlappedReadInit(); #endif /* NT_TCP_OVERLAPPED */ ttnet = nett; /* TCP/IP (sockets) network */ #ifndef NOHTTP /* We have succeeded in connecting to the HTTP PROXY. So now we */ /* need to attempt to connect through the proxy to the actual host */ /* If that is successful, we have to pretend that we made a direct */ /* connection to the actual host. */ if ( tcp_http_proxy ) { #ifdef OS2 char * agent = "Kermit 95"; /* Default user agent */ #else char * agent = "C-Kermit"; #endif /* OS2 */ if (http_connect(ttyfd, tcp_http_proxy_agent ? tcp_http_proxy_agent : agent, NULL, tcp_http_proxy_user, tcp_http_proxy_pwd, 0, proxycopy ) < 0) { netclos(); return(-1); } ckstrncpy(namecopy,proxycopy,NAMECPYL); p = namecopy; /* Was a service requested? */ while (*p != '\0' && *p != ':') p++; /* Look for colon */ *p = '\0'; } #endif /* NOHTTP */ /* Jeff - Does this next block of code that set's the protocol */ /* need to be here anymore? 5/10/2000 */ /* There are certain magic port numbers that when used require */ /* the use of specific protocols. Check this now before we */ /* set the SO_OOBINLINE state or we might get it wrong. */ x = ntohs((unsigned short)service->s_port); svcnum = x; /* See if the service is TELNET. */ if (x == TELNET_PORT) { /* Yes, so if raw port not requested */ #ifdef COMMENT /* Jeff 2005/12/30 */ if (ttnproto != NP_TCPRAW && ttnproto != NP_SSL_RAW && ttnproto != NP_TLS_RAW && ttnproto != NP_NONE) #else /* fdc 2005/12/04 */ if (ttnproto != NP_TCPRAW && ttnproto != NP_NONE) #endif /* COMMENT */ ttnproto = NP_TELNET; /* Select TELNET protocol. */ } #ifdef RLOGCODE else if (x == RLOGIN_PORT) { ttnproto = NP_RLOGIN; } #ifdef CK_KERBEROS /* There is no good way to do this. If the user didn't tell */ /* which one to use up front. We may guess wrong if the user */ /* has both Kerberos versions installed and valid TGTs for each */ else if (x == KLOGIN_PORT && ttnproto != NP_K4LOGIN && ttnproto != NP_K5LOGIN) { if (ck_krb5_is_installed() && ck_krb5_is_tgt_valid()) ttnproto = NP_K5LOGIN; else if (ck_krb4_is_installed() && ck_krb4_is_tgt_valid()) ttnproto = NP_K4LOGIN; else ttnproto = NP_K4LOGIN; } else if (x == EKLOGIN_PORT && ttnproto != NP_EK4LOGIN && ttnproto != NP_EK5LOGIN) { if (ck_krb5_is_installed() && ck_krb5_is_tgt_valid()) ttnproto = NP_EK5LOGIN; else if (ck_krb4_is_installed() && ck_krb4_is_tgt_valid()) ttnproto = NP_EK4LOGIN; else ttnproto = NP_EK4LOGIN; } #endif /* CK_KERBEROS */ #endif /* RLOGCODE */ #ifdef IKS_OPTION else if (x == KERMIT_PORT) { /* IKS uses Telnet protocol */ if (ttnproto == NP_NONE) ttnproto = NP_KERMIT; } #endif /* IKS_OPTION */ #ifdef SO_OOBINLINE /* The symbol SO_OOBINLINE is not known to Ultrix 2.0. It means "leave out of band data inline". The normal value is 0x0100, but don't try this on systems where the symbol is undefined. */ /* Note from Jeff Altman: 12/13/95 In implementing rlogin protocol I have come to the conclusion that it is a really bad idea to read out-of-band data inline. At least Windows and OS/2 does not handle this well. And if you need to know that data is out-of-band, then it becomes absolutely pointless. Therefore, at least on OS2 and Windows (NT) I have changed the value of on to 0, so that out-of-band data stays out-of-band. 12/18/95 Actually, OOB data should be read inline when possible. Especially with protocols that don't care about the Urgent flag. This is true with Telnet. With Rlogin, you need to be able to catch OOB data. However, the best way to do this is to set a signal handler on SIGURG. This isn't possible on OS/2 and Windows. But it is in UNIX. We will also need OOB data for FTP so better create a general mechanism. The reason for making OOB data be inline is that the standard ttinc/ttoc calls can be used for reading that data on UNIX systems. If we didn't have the OOBINLINE option set then we would have to use recv(,MSG_OOB) to read it. */ #ifdef RLOGCODE #ifdef TCPIPLIB if (ttnproto == NP_RLOGIN #ifdef CK_KERBEROS || ttnproto == NP_K4LOGIN || ttnproto == NP_EK4LOGIN || ttnproto == NP_K5LOGIN || ttnproto == NP_EK5LOGIN #endif /* CK_KERBEROS */ ) on = 0; #else /* TCPIPLIB */ if (ttnproto == NP_RLOGIN #ifdef CK_KERBEROS || ttnproto == NP_K4LOGIN || ttnproto == NP_EK4LOGIN || ttnproto == NP_K5LOGIN || ttnproto == NP_EK5LOGIN #endif /* CK_KERBEROS */ ) { debug(F100,"Installing rlogoobh on SIGURG","",0); signal(SIGURG, rlogoobh); on = 0; } else { debug(F100,"Ignoring SIGURG","",0); signal(SIGURG, SIG_DFL); } #endif /* TCPIPLIB */ #endif /* RLOGCODE */ #ifdef datageneral setsockopt(ttyfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on); #else #ifdef BSD43 setsockopt(ttyfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on); #else #ifdef OSF1 setsockopt(ttyfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on); #else #ifdef POSIX setsockopt(ttyfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on); #else #ifdef MOTSV88R4 setsockopt(ttyfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on); #else #ifdef SOLARIS /* Maybe this applies to all SVR4 versions, but the other (else) way has been compiling and working fine on all the others, so best not to change it. */ setsockopt(ttyfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on); #else #ifdef OSK setsockopt(ttyfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on); #else #ifdef OS2 { int rc; rc = setsockopt(ttyfd, SOL_SOCKET, SO_OOBINLINE, (char *) &on, sizeof on ); debug(F111,"setsockopt SO_OOBINLINE",on ? "on" : "off" ,rc); } #else #ifdef VMS /* or, at least, VMS with gcc */ setsockopt(ttyfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on); #else #ifdef CLIX setsockopt(ttyfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on); #else setsockopt(ttyfd, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on); #endif /* CLIX */ #endif /* VMS */ #endif /* OS2 */ #endif /* OSK */ #endif /* SOLARIS */ #endif /* MOTSV88R4 */ #endif /* POSIX */ #endif /* BSD43 */ #endif /* OSF1 */ #endif /* datageneral */ #endif /* SO_OOBINLINE */ #ifndef NOTCPOPTS #ifndef datageneral #ifdef SOL_SOCKET #ifdef TCP_NODELAY no_delay(ttyfd,tcp_nodelay); #endif /* TCP_NODELAY */ #ifdef SO_KEEPALIVE keepalive(ttyfd,tcp_keepalive); #endif /* SO_KEEPALIVE */ #ifdef SO_LINGER ck_linger(ttyfd,tcp_linger, tcp_linger_tmo); #endif /* SO_LINGER */ #ifdef SO_SNDBUF sendbuf(ttyfd,tcp_sendbuf); #endif /* SO_SNDBUF */ #ifdef SO_RCVBUF recvbuf(ttyfd,tcp_recvbuf); #endif /* SO_RCVBUF */ #endif /* SOL_SOCKET */ #endif /* datageneral */ #endif /* NOTCPOPTS */ #ifndef datageneral /* Find out our own IP address. */ /* We need the l_addr structure for [E]KLOGIN. */ l_slen = sizeof(l_addr); bzero((char *)&l_addr, l_slen); #ifndef EXCELAN if (!getsockname(ttyfd, (struct sockaddr *)&l_addr, &l_slen)) { char * s = (char *)inet_ntoa(l_addr.sin_addr); ckstrncpy(myipaddr, s, 20); debug(F110,"getsockname",myipaddr,0); } #endif /* EXCELAN */ #endif /* datageneral */ /* This is really only needed for Kerberos IV but is useful information in any case. If we connect to a name that is really a pool, we need to get the name of the machine we are actually connecting to for K4 to authenticate properly. This way we also update the names properly. However, it is a security hole when used with insecure DNS. Note: This does not work on Windows 95 or Windows NT 3.5x. This is because of the Microsoft implementation of gethostbyaddr() in both Winsock 1.1 and Winsock 2.0 on those platforms. Their algorithm is: 1. Check the HOSTENT cache. 2. Check the HOSTS file at %SystemRoot%\System32\DRIVERS\ETC. 3. Do a DNS query if the DNS server is configured for name resolution. 4. Do an additional NetBIOS remote adapter status to an IP address for its NetBIOS name table. This step is specific only to the Windows NT version 3.51 implementation. The problem is the use of the HOSTENT cache. It means that gethostbyaddr() can not be used to resolve the real name of machine if it was originally accessed by an alias used to represent a cluster. */ if ((tcp_rdns && dns || tcp_rdns == SET_ON #ifdef CK_KERBEROS || tcp_rdns == SET_AUTO && (ck_krb5_is_installed() || ck_krb4_is_installed()) #endif /* CK_KERBEROS */ ) #ifndef NOHTTP && (tcp_http_proxy == NULL) #endif /* NOHTTP */ #ifdef CK_SSL && !(ssl_only_flag || tls_only_flag) #endif /* CK_SSL */ ) { #ifdef NT if (isWin95()) sleep(1); #endif /* NT */ if (!quiet) { printf(" Reverse DNS Lookup... "); fflush(stdout); } if (host = gethostbyaddr((char *)&r_addr.sin_addr,4,PF_INET)) { char * s; host = ck_copyhostent(host); debug(F100,"netopen gethostbyname != NULL","",0); if (!quiet) { printf("(OK)\n"); fflush(stdout); } s = host->h_name; if (!s) { /* This can happen... */ debug(F100,"netopen host->h_name is NULL","",0); s = ""; } /* Something is wrong with inet_ntoa() on HPUX 10.xx */ /* The compiler says "Integral value implicitly converted to */ /* pointer in assignment." The prototype is right there */ /* in so what's the problem? */ /* Ditto in HP-UX 5.x, but not 8.x or 9.x... */ if (!*s) { /* No name so substitute the address */ debug(F100,"netopen host->h_name is empty","",0); s = inet_ntoa(r_addr.sin_addr); /* Convert address to string */ if (!s) /* Trust No 1 */ s = ""; if (*s) { /* If it worked, use this string */ ckstrncpy(ipaddr,s,20); } s = ipaddr; /* Otherwise stick with the IP */ if (!*s) /* or failing that */ s = namecopy; /* the name we were called with. */ } if (*s) { /* Copying into our argument? */ ckstrncpy(name,s,80); /* Bad Bad Bad */ if ( (80-strlen(name)) > (strlen(svcbuf)+1) ) { ckstrncat(name,":",80-strlen(name)); ckstrncat(name,svcbuf,80-strlen(name)); } } if (!quiet && *s #ifndef NOICP && !doconx #endif /* NOICP */ ) { printf(" %s connected on port %s\n",s,p); #ifdef BETADEBUG /* This is simply for testing the DNS entries */ if (host->h_aliases) { char ** a = host->h_aliases; while (*a) { printf(" alias => %s\n",*a); a++; } } #endif /* BETADEBUG */ } } else { if (!quiet) printf("Failed.\n"); } } else if (!quiet) printf("(OK)\n"); if (!quiet) fflush(stdout); /* This should already have been done but just in case */ ckstrncpy(ipaddr,(char *)inet_ntoa(r_addr.sin_addr),20); #ifdef CK_SECURITY /* Before Initialization Telnet/Rlogin Negotiations Init Kerberos */ #ifndef NOHTTP if (tcp_http_proxy) { for (i=strlen(proxycopy); i >= 0 ; i--) if ( proxycopy[i] == ':' ) proxycopy[i] = '\0'; } #endif /* NOHTTP */ ck_auth_init( #ifndef NOHTTP tcp_http_proxy ? proxycopy : #endif /* NOHTTP */ (tcp_rdns && host && host->h_name && host->h_name[0]) ? (char *)host->h_name : (namecopy2[0] ? namecopy2 : (namecopy[0] ? namecopy : ipaddr)), ipaddr, uidbuf, ttyfd ); #endif /* CK_SECURITY */ #ifdef CK_SSL if (ck_ssleay_is_installed()) { if (!ssl_tn_init(SSL_CLIENT)) { debug(F100,"netopen ssl_tn_init() failed","",0); if (bio_err!=NULL) { BIO_printf(bio_err,"ssl_tn_init() failed\n"); ERR_print_errors(bio_err); } else { fflush(stderr); fprintf(stderr,"ssl_tn_init() failed\n"); ERR_print_errors_fp(stderr); } if (tls_only_flag || ssl_only_flag) { debug(F100,"netopen ssl/tls required","",0); netclos(); return(-1); } /* we will continue to accept the connection */ /* without SSL or TLS support unless required. */ if ( TELOPT_DEF_S_ME_MODE(TELOPT_START_TLS) != TN_NG_MU ) TELOPT_DEF_S_ME_MODE(TELOPT_START_TLS) = TN_NG_RF; if ( TELOPT_DEF_S_U_MODE(TELOPT_START_TLS) != TN_NG_MU ) TELOPT_DEF_S_U_MODE(TELOPT_START_TLS) = TN_NG_RF; if ( TELOPT_DEF_C_ME_MODE(TELOPT_START_TLS) != TN_NG_MU ) TELOPT_DEF_C_ME_MODE(TELOPT_START_TLS) = TN_NG_RF; if ( TELOPT_DEF_C_U_MODE(TELOPT_START_TLS) != TN_NG_MU ) TELOPT_DEF_C_U_MODE(TELOPT_START_TLS) = TN_NG_RF; } else if ( ck_ssl_outgoing(ttyfd) < 0 ) { debug(F100,"ck_ssl_outgoing() failed","",0); netclos(); return(-1); } } #endif /* CK_SSL */ #ifdef RLOGCODE if (ttnproto == NP_RLOGIN #ifdef CK_KERBEROS || ttnproto == NP_K4LOGIN || ttnproto == NP_EK4LOGIN || ttnproto == NP_K5LOGIN || ttnproto == NP_EK5LOGIN #endif /* CK_KERBEROS */ ) { /* Similar deal for rlogin */ if (rlog_ini(((tcp_rdns && host && host->h_name && host->h_name[0]) ? (CHAR *)host->h_name : (CHAR *)ipaddr), service->s_port, &l_addr,&r_addr ) < 0) { debug(F100,"rlogin initialization failed","",0); netclos(); return(-1); } } else #endif /* RLOGCODE */ if (tn_ini() < 0) { /* Start Telnet negotiations. */ netclos(); return(-1); /* Gone, so open failed. */ } if (ttchk() < 0) { netclos(); return(-1); } #ifdef CK_KERBEROS #ifdef KRB5_U2U if ( ttnproto == NP_K5U2U ) { if (k5_user_to_user_client_auth()) { netclos(); return(-1); } } #endif /* KRB5_U2U */ #endif /* CK_KERBEROS */ debug(F101,"netopen service","",svcnum); debug(F110,"netopen name",name,0); debug(F110,"netopen ipaddr",ipaddr,0); ckstrncpy(hostipaddr,ipaddr,63); if (lcl) if (*lcl < 0) /* Local mode. */ *lcl = 1; #endif /* TCPSOCKET */ return(0); /* Done. */ } /* N E T C L O S -- Close current network connection. */ #ifndef NOLOCAL _PROTOTYP(VOID slrestor,(VOID)); #ifdef CK_SSL int tls_norestore = 0; #endif /* CK_SSL */ #endif /* NOLOCAL */ int netclos() { static int close_in_progress = 0; int x = 0, y, z; debug(F101,"netclos","",ttyfd); #ifdef NETLEBUF if (!tt_push_inited) le_init(); #endif /* NETLEBUF */ if (ttyfd == -1) /* Was open? */ return(0); /* Wasn't. */ if (close_in_progress) return(0); close_in_progress = 1; /* Remember */ #ifndef NOLOCAL /* This function call should not be here since this is a direct call */ /* from an I/O routine to a user interface level function. However, */ /* the reality is that we do not have pure interfaces. If we ever */ /* decide to clean this up the UI level should assign this function */ /* via a pointer assignment. - Jeff 9/10/1999 */ #ifdef CK_SSL if (!tls_norestore) #endif /* CK_SSL */ slrestor(); #endif /* NOLOCAL */ #ifdef OS2 RequestTCPIPMutex(SEM_INDEFINITE_WAIT); #else /* OS2 */ if (ttyfd > -1) /* Was. */ #endif /* OS2 */ { #ifdef VMS y = 1; /* Turn on nonblocking reads */ z = socket_ioctl(ttyfd,FIONBIO,&y); debug(F111,"netclos FIONBIO","on",z); #endif /* VMS */ #ifdef TNCODE if (ttnproto == NP_TELNET) { if (!TELOPT_ME(TELOPT_LOGOUT) #ifdef COMMENT /* Jeff 2005/12/30 */ #ifdef CK_SSL && !ssl_raw_flag && !tls_raw_flag #endif /* CK_SSL */ #endif /* COMMENT */ ) { /* Send LOGOUT option before close */ if (tn_sopt(DO,TELOPT_LOGOUT) >= 0) { TELOPT_UNANSWERED_DO(TELOPT_LOGOUT) = 1; /* It would be nice to call tn_wait but we can't */ } } tn_push(); /* Place any waiting data into input*/ } #endif /* TNCODE */ #ifdef CK_SSL if (ssl_active_flag) { if (ssl_debug_flag) BIO_printf(bio_err,"calling SSL_shutdown\n"); SSL_shutdown(ssl_con); ssl_active_flag = 0; } if (tls_active_flag) { if (ssl_debug_flag) BIO_printf(bio_err,"calling SSL_shutdown\n"); SSL_shutdown(tls_con); tls_active_flag = 0; } #endif /* CK_SSL */ #ifdef VMS ck_cancio(); /* Cancel any outstanding reads. */ #endif /* VMS */ #ifdef TCPIPLIB x = socket_close(ttyfd); /* Close it. */ #else #ifndef OS2 #ifdef IBMX25 if (ttnet == NET_IX25) { /* riehm: should send a disc_req - but only if link is still OK */ x = x25clear(); close(ttyfd); if (x25serverfd) { /* we were the passive client of a server, now we * go back to being the normal client. * I hope that kermit can cope with the logic that * there can still be a connection after netclos * has been called. */ ttyfd = x25serverfd; x25serverfd = 0; /* * need to close the server connection too - because * all file descriptors connected to the NPI have the * same status. * * The problem is that any waiting connections get * lost, the client doesn't realise, and hangs. */ netclos(); } x25_state = X25_CLOSED; /* riehm: dead code? */ } else #endif /* IBMX25 */ x = close(ttyfd); #endif /* OS2 */ #endif /* TCPIPLIB */ } ttyfd = -1; /* Mark it as closed. */ wasclosed = 1; #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ #ifdef TNCODE #ifdef CK_FORWARD_X fwdx_close_all(); /* Shut down any Forward X sockets */ #endif /* CK_FORWARD_X */ tn_reset(); /* The Reset Telnet Option table. */ debug(F100,"netclose setting tn_init = 0","",0); tn_init = 0; /* Remember about telnet protocol... */ sstelnet = 0; /* Client-side Telnet */ #endif /* TNCODE */ *ipaddr = '\0'; /* Zero the IP address string */ tcp_incoming = 0; /* No longer incoming */ /* Don't reset ttnproto so that we can remember which protocol is in use */ #ifdef TCPIPLIB /* Empty the internal buffers so they won't be used as invalid input on the next connect attempt (rlogin). */ ttibp = 0; ttibn = 0; #endif /* TCPIPLIB */ #ifdef CK_KERBEROS /* If we are automatically destroying Kerberos credentials on Close */ /* do it now. */ #ifdef KRB4 if (krb4_autodel == KRB_DEL_CL) { extern struct krb_op_data krb_op; krb_op.version = 4; krb_op.cache = NULL; ck_krb4_destroy(&krb_op); } #endif /* KRB4 */ #ifdef KRB5 if (krb5_autodel == KRB_DEL_CL) { extern struct krb_op_data krb_op; extern char * krb5_d_cc; krb_op.version = 5; krb_op.cache = krb5_d_cc; ck_krb5_destroy(&krb_op); } #endif /* KRB5 */ #endif /* CK_KERBEROS */ close_in_progress = 0; /* Remember we are done. */ return(x); } #ifdef OS2 int os2socketerror( int s_errno ) { #ifdef OS2ONLY if (s_errno > 0 && s_errno <= SOCBASEERR) { /* in OS/2, there is a problem with threading in that * the value of errno is not thread safe. It can be * set to a value from a previous library call and if * it was not cleared it will appear here. Only treat * valid socket error codes as errors in this function. */ debug(F100,"os2socketerror errno.h","",0); socket_errno = 0; return(0); } #endif /* OS2ONLY */ switch (s_errno) { case 0: /* NO ERROR */ debug(F100,"os2socketerror NOERROR","",0); return(0); #ifdef NT case WSAECONNRESET: #else /* NT */ case SOCECONNRESET: case SOCECONNRESET - SOCBASEERR: #endif /* NT */ debug(F100,"os2socketerror ECONRESET","",0); tn_debug("ECONRESET"); netclos(); /* *** *** */ return(-1); /* Connection is broken. */ #ifdef NT case WSAECONNABORTED: #else /* NT */ case SOCECONNABORTED: case SOCECONNABORTED - SOCBASEERR: #endif /* NT */ debug(F100,"os2socketerror ECONNABORTED","",0); tn_debug("ECONNABORTED"); netclos(); /* *** *** */ return(-1); /* Connection is broken. */ #ifdef NT case WSAENETRESET: #else /* NT */ case SOCENETRESET: case SOCENETRESET - SOCBASEERR: #endif /* NT */ debug(F100,"os2socketerror ENETRESET","",0); tn_debug("ENETRESET"); netclos(); /* *** *** */ return(-1); /* Connection is broken. */ #ifdef NT case WSAENOTCONN: #else /* NT */ case SOCENOTCONN: case SOCENOTCONN - SOCBASEERR: #endif /* NT */ debug(F100,"os2socketerror ENOTCONN","",0); tn_debug("ENOTCONN"); netclos(); /* *** *** */ return(-1); /* Connection is broken. */ #ifdef NT case WSAESHUTDOWN: debug(F100,"os2socketerror ESHUTDOWN","",0); tn_debug("ESHUTDOWN"); netclos(); /* *** *** */ return(-1); /* Connection is broken. */ #endif /* NT */ #ifdef NT case WSAEWOULDBLOCK: #else case SOCEWOULDBLOCK: case SOCEWOULDBLOCK - SOCBASEERR: #endif /* NT */ debug(F100,"os2socketerror EWOULDBLOCK","",0); return(0); #ifdef NT case ERROR_IO_INCOMPLETE: case ERROR_IO_PENDING: case ERROR_OPERATION_ABORTED: return(0); #endif /* NT */ default: return(-2); } return(0); } #endif /* OS2 */ /* N E T T C H K -- Check if network up, and how many bytes can be read */ /* Returns number of bytes waiting, or -1 if connection has been dropped. */ int /* Check how many bytes are ready */ nettchk() { /* for reading from network */ #ifdef TCPIPLIB long count = 0; int x = 0, z; long y; char c; int rc; #ifdef NT extern int ionoblock; /* For Overlapped I/O */ #endif /* NT */ debug(F101,"nettchk entry ttibn","",ttibn); debug(F101,"nettchk entry ttibp","",ttibp); #ifdef NETLEBUF { int n = 0; if (ttpush >= 0) n++; n += le_inbuf(); if (n > 0) return(n); } #endif /* NETLEBUF */ #ifndef OS2 #ifndef BEBOX socket_errno = 0; /* This is a function call in NT, and BeOS */ #endif /* BEBOX */ #endif /* OS2 */ if (ttyfd == -1) { debug(F100,"nettchk socket is closed","",0); return(-1); } /* Note: this socket_ioctl() call does NOT return an error if the connection has been broken. (At least not in MultiNet.) */ #ifdef COMMENT /* Another trick that can be tried here is something like this: */ if (ttnet == NET_TCPB) { char dummy; x = read(ttyfd,&dummy,0); /* Try to read nothing */ if (x < 0) { /* "Connection reset by peer" */ perror("TCP/IP"); /* or somesuch... */ ttclos(0); /* Close our end too. */ return(-1); } } #endif /* COMMENT */ #ifdef CK_SSL if (ssl_active_flag) { #ifndef IKSDONLY #ifdef OS2 if ( IsConnectMode() ) { debug(F101,"nettchk (ssl_active_flag) returns","",count); return(0); } #endif /* OS2 */ #endif /* IKSDONLY */ count = SSL_pending(ssl_con); if (count < 0) { debug(F111,"nettchk","SSL_pending error",count); netclos(); return(-1); } if ( count > 0 ) return(count); /* Don't perform a read */ } else if (tls_active_flag) { #ifndef IKSDONLY #ifdef OS2 if ( IsConnectMode() ) { debug(F101,"nettchk (tls_active_flag) returns","",count); return(0); } #endif /* OS2 */ #endif /* IKSDONLY */ count = SSL_pending(tls_con); if (count < 0) { debug(F111,"nettchk","TLS_pending error",count); netclos(); return(-1); } if ( count > 0 ) return(count); /* Don't perform a read */ } else #endif /* CK_SSL */ if (socket_ioctl(ttyfd,FIONREAD, #ifdef COMMENT /* Now we've changed the ioctl(..,..,x) prototype for DECC to (void *) */ #ifdef __DECC /* NOTE: "&count" might need to be "(char *)&count" in some settings. */ /* Cast needed for DECC 4.1 & later? */ /* Maybe, but __DECC_VER only exists in 5.0 and later */ (char *) #endif /* __DECC */ #endif /* COMMENT */ &count ) < 0) { debug(F101,"nettchk socket_ioctl error","",socket_errno); /* If the connection is gone, the connection is gone. */ netclos(); #ifdef NT_TCP_OVERLAPPED /* Is there anything in the overlapped I/O buffers? */ count += OverlappedDataWaiting(); #endif /* NT_TCP_OVERLAPPED */ count += ttibn; return(count>0?count:-1); } debug(F101,"nettchk count","",count); #ifdef NT_TCP_OVERLAPPED /* Is there anything in the overlapped I/O buffers? */ count += OverlappedDataWaiting(); debug(F101,"nettchk count w/overlapped","",count); #endif /* NT_TCP_OVERLAPPED */ #ifdef OS2 #ifndef IKSDONLY if ( IsConnectMode() ) { debug(F101,"nettchk (FIONREAD) returns","",count); return(count); } #endif /* IKSDONLY */ #endif /* OS2 */ /* For the sake of efficiency, if there is still data in the ttibuf */ /* do not go to the bother of checking to see of the connection is */ /* still valid. The handle is still good, so just return the count */ /* of the bytes that we already have left to process. */ #ifdef OS2 if ( count > 0 || ttibn > 0 ) { count+=ttibn; debug(F101,"nettchk (count+ttibn > 0) returns","",count); return(count); } else { RequestTCPIPMutex(SEM_INDEFINITE_WAIT); if ( ttibn == 0 ) ttibp = 0; /* reset for next read */ } #else /* OS2 */ if ( count > 0 || ttibn > 0 ) { debug(F101,"nettchk returns","",count+ttibn); return(count+ttibn); } ttibn = ttibp = 0; #endif /* OS2 */ /* The following code works well in most settings, but messes things up in others, including CMU/Tek TCP/IP and UCX 2.0, where it somehow manages to make it impossible to ever make a new connection to the same host again with CONNECT, once it has been logged out from the first time. Not even if you HANGUP first, or SET HOST, or SET LINE. Reportedly, however, it does work OK in later releases of UCX. But there is no way we can accommodate both old and new -- we might have static linking or dynamic linking, etc etc. If we have static, I only have access to 2.0, where this doesn't work, etc etc blah blah. In the following lines, we define a symbol NOCOUNT for builds where we want to omit this code. By default, it is omitted for CMU/Tek. You can force omission of it for other combinations by defining NOCOUNT in CFLAGS. You can force inclusion of this code, even for CMU/Tek, by including NONOCOUNT in CFLAGS. */ #ifdef NONOCOUNT #ifdef NOCOUNT #undef NOCOUNT #endif /* NOCOUNT */ #else #ifndef NOCOUNT #ifdef CMU_TCPIP #define NOCOUNT #endif /* CMU_TCPIP */ #endif /* NOCOUNT */ #endif /* NONOCOUNT */ /* From this point forward we have a possible race condition in K95 * due to its use of multiple threads. Therefore, we must ensure * that only one thread attempt to read/write from the socket at a * time. Otherwise, it is possible for a buffer to be overwritten. */ /* we know now that count >= 0 and that ttibn == 0 */ if (count == 0 #ifdef RLOGCODE #ifdef CK_KERBEROS && ttnproto != NP_EK4LOGIN && ttnproto != NP_EK5LOGIN #endif /* CK_KERBEROS */ #endif /* RLOGCODE */ ) { int s_errno = 0; #ifndef NOCOUNT /* Here we need to tell the difference between a 0 count on an active connection, and a 0 count because the remote end of the socket broke the connection. There is no mechanism in TGV MultiNet (or WIN/TCP?) to query the status of the connection, so we have to do a read. -1 means there was no data available (socket_errno == EWOULDBLOCK), 0 means the connection is down. But if, by chance, we actually get a character, we have to put it where it won't be lost. */ #ifndef NON_BLOCK_IO #ifdef OS2 #ifdef CK_SSL RequestSSLMutex(SEM_INDEFINITE_WAIT); #endif /* CK_SSL */ #endif /* OS2 */ y = 1; /* Turn on nonblocking reads */ z = socket_ioctl(ttyfd,FIONBIO,&y); debug(F111,"nettchk FIONBIO","on",z); #ifdef OS2 #ifdef CK_SSL ReleaseSSLMutex(); #endif /* CK_SSL */ #endif /* OS2 */ #endif /* NON_BLOCK_IO */ #ifdef NT_TCP_OVERLAPPED ionoblock = 1; /* For Overlapped I/O */ #endif /* NT_TCP_OVERLAPPED */ #ifdef CK_SSL if ( ssl_active_flag || tls_active_flag ) { #ifdef OS2 ssl_read: x = SSL_read( ssl_active_flag?ssl_con:tls_con, &ttibuf[ttibp+ttibn], TTIBUFL-ttibp-ttibn ); switch (SSL_get_error(ssl_active_flag?ssl_con:tls_con,x)) { case SSL_ERROR_NONE: debug(F111,"nettchk SSL_ERROR_NONE","x",x); break; case SSL_ERROR_WANT_WRITE: debug(F100,"nettchk SSL_ERROR_WANT_WRITE","",0); x = -1; break; case SSL_ERROR_WANT_READ: debug(F100,"nettchk SSL_ERROR_WANT_READ","",0); x = -1; break; case SSL_ERROR_SYSCALL: if ( x == 0 ) { /* EOF */ netclos(); rc = -1; goto nettchk_return; } else { #ifdef NT int gle = GetLastError(); #endif /* NT */ #ifndef NON_BLOCK_IO #ifdef OS2 #ifdef CK_SSL RequestSSLMutex(SEM_INDEFINITE_WAIT); #endif /* CK_SSL */ #endif /* OS2 */ y = 0; /* Turn off nonblocking reads */ z = socket_ioctl(ttyfd,FIONBIO,&y); debug(F111,"nettchk FIONBIO","off",z); #ifdef OS2 #ifdef CK_SSL ReleaseSSLMutex(); #endif /* CK_SSL */ #endif /* OS2 */ #endif /* NON_BLOCK_IO */ #ifdef NT_TCP_OVERLAPPED ionoblock = 0; /* For Overlapped I/O */ #endif /* NT_TCP_OVERLAPPED */ #ifdef NT debug(F111,"nettchk SSL_ERROR_SYSCALL", "GetLastError()",gle); rc = os2socketerror(gle); if (rc == -1) rc = -2; else if ( rc == -2 ) rc = -1; goto nettchk_return; #endif /* NT */ break; } case SSL_ERROR_WANT_X509_LOOKUP: debug(F100,"nettchk SSL_ERROR_WANT_X509_LOOKUP","",0); break; case SSL_ERROR_SSL: if (bio_err!=NULL) { int len; extern char ssl_err[]; BIO_printf(bio_err,"nettchk() SSL_ERROR_SSL\n"); ERR_print_errors(bio_err); len = BIO_read(bio_err,ssl_err,SSL_ERR_BFSZ); ssl_err[len < SSL_ERR_BFSZ ? len : SSL_ERR_BFSZ] = '\0'; debug(F110,"nettchk SSL_ERROR_SSL",ssl_err,0); if (ssl_debug_flag) printf(ssl_err); } else if (ssl_debug_flag) { debug(F100,"nettchk SSL_ERROR_SSL","",0); fflush(stderr); fprintf(stderr,"nettchk() SSL_ERROR_SSL\n"); ERR_print_errors_fp(stderr); } #ifdef COMMENT netclos(); rc = -1; goto nettchk_return; #else x = -1; break; #endif case SSL_ERROR_ZERO_RETURN: debug(F100,"nettchk SSL_ERROR_ZERO_RETURN","",0); netclos(); rc = -1; goto nettchk_return; default: debug(F100,"nettchk SSL_ERROR_?????","",0); netclos(); rc = -1; goto nettchk_return; } #else /* OS2 */ /* Do not block */ x = -1; #endif /* OS2 */ } else #endif /* CK_SSL */ { #ifdef OS2 x = socket_read(ttyfd,&ttibuf[ttibp+ttibn], TTIBUFL-ttibp-ttibn); /* Returns -1 if no data */ #else /* OS2 */ x = socket_read(ttyfd,&c,1); /* Returns -1 if no data */ #endif /* OS2 */ } s_errno = socket_errno; /* socket_errno may be a function */ debug(F101,"nettchk socket_read","",x); #ifndef NON_BLOCK_IO #ifdef OS2 #ifdef CK_SSL RequestSSLMutex(SEM_INDEFINITE_WAIT); #endif /* CK_SSL */ #endif /* OS2 */ y = 0; /* Turn off nonblocking reads */ z = socket_ioctl(ttyfd,FIONBIO,&y); debug(F111,"nettchk FIONBIO","off",z); #ifdef OS2 #ifdef CK_SSL ReleaseSSLMutex(); #endif /* CK_SSL */ #endif /* OS2 */ #endif /* NON_BLOCK_IO */ #ifdef NT_TCP_OVERLAPPED ionoblock = 0; /* For Overlapped I/O */ #endif /* NT_TCP_OVERLAPPED */ if (x == -1) { debug(F101,"nettchk socket_read errno","",s_errno); #ifdef OS2 if (os2socketerror(s_errno) < 0) { rc = -1; goto nettchk_return; } #endif /* OS2 */ } else if (x == 0) { debug(F100,"nettchk connection closed","",0); netclos(); /* *** *** */ rc = -1; goto nettchk_return; } if (x >= 1) { /* Oops, actually got a byte? */ #ifdef OS2 /* In OS/2 we read directly into ttibuf[] */ ckhexdump("nettchk got real data",&ttibuf[ttibp+ttibn],x); ttibn += x; #else /* OS2 */ #ifdef CK_SSL if ( ssl_active_flag || tls_active_flag ) { ckhexdump("nettchk got real data",&ttibuf[ttibp+ttibn],x); ttibn += x; } else #endif /* CK_SSL */ { debug(F101,"nettchk socket_read char","",c); debug(F101,"nettchk ttibp","",ttibp); debug(F101,"nettchk ttibn","",ttibn); /* In the case of Overlapped I/O the character would have come from the beginning of the buffer, so put it back. */ if (ttibp > 0) { ttibp--; ttibuf[ttibp] = c; ttibn++; } else { ttibuf[ttibp+ttibn] = c; ttibn++; } } #endif /* OS2 */ } #else /* NOCOUNT */ if (ttnet == NET_TCPB) { char dummy; x = read(ttyfd,&dummy,0); /* Try to read nothing */ if (x < 0) { /* "Connection reset by peer" */ perror("TCP/IP"); /* or somesuch... */ ttclos(0); /* Close our end too. */ rc = -1; goto nettchk_return; } } #endif /* NOCOUNT */ } #ifdef CK_KERBEROS #ifdef KRB4 #ifdef RLOGCODE if (ttnproto == NP_EK4LOGIN) count += krb4_des_avail(ttyfd); #endif /* RLOGCODE */ #endif /* KRB4 */ #ifdef KRB5 #ifdef RLOGCODE if (ttnproto == NP_EK5LOGIN) count += krb5_des_avail(ttyfd); #endif /* RLOGCODE */ #ifdef KRB5_U2U if (ttnproto == NP_K5U2U) count += krb5_u2u_avail(ttyfd); #endif /* KRB5_U2U */ #endif /* KRB5 */ #endif /* CK_KERBEROS */ debug(F101,"nettchk returns","",count+ttibn); rc = count + ttibn; nettchk_return: #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ return(rc); #else /* Not TCPIPLIB */ /* UNIX just uses ttchk(), in which the ioctl() calls on the file descriptor seem to work OK. */ return(ttchk()); #endif /* TCPIPLIB */ /* But what about X.25? */ } #ifndef OS2 VOID nettout(i) int i; { /* Catch the alarm interrupts */ debug(F100,"nettout caught timeout","",0); ttimoff(); cklongjmp(njbuf, -1); } #endif /* !OS2 */ #ifdef TCPIPLIB VOID #ifdef CK_ANSIC donetinc(void * threadinfo) #else /* CK_ANSIC */ donetinc(threadinfo) VOID * threadinfo; #endif /* CK_ANSIC */ /* donetinc */ { #ifdef NTSIG extern int TlsIndex; setint(); if (threadinfo) { /* Thread local storage... */ TlsSetValue(TlsIndex,threadinfo); } #endif /* NTSIG */ #ifdef CK_LOGIN #ifdef NT #ifdef IKSD if (inserver) setntcreds(); #endif /* IKSD */ #endif /* NT */ #endif /* CK_LOGIN */ while (1) { if (ttbufr() < 0) /* Keep trying to refill it. */ break; /* Till we get an error. */ if (ttibn > 0) /* Or we get a character. */ break; } } #endif /* TCPIPLIB */ VOID #ifdef CK_ANSIC failnetinc(void * threadinfo) #else /* CK_ANSIC */ failnetinc(threadinfo) VOID * threadinfo; #endif /* CK_ANSIC */ /* failnetinc */ { ; /* Nothing to do on an error */ } /* N E T X I N -- Input block of characters from network */ int netxin(n,buf) int n; CHAR * buf; { int len, i, j; #ifdef TCPIPLIB int rc; #endif /* TCPIPLIB */ if (ttyfd == -1) { debug(F100,"netxin socket is closed","",0); return(-2); } #ifdef CK_KERBEROS #ifdef KRB4 #ifdef RLOGCODE if (ttnproto == NP_EK4LOGIN) { if ((len = krb4_des_read(ttyfd,buf,n)) < 0) return(-1); else return(len); } #endif /* RLOGCODE */ #endif /* KRB4 */ #ifdef KRB5 #ifdef RLOGCODE if (ttnproto == NP_EK5LOGIN) { if ((len = krb5_des_read(ttyfd,(char *)buf,n,0)) < 0) return(-1); else return(len); } #endif /* RLOGCODE */ #ifdef KRB5_U2U if (ttnproto == NP_K5U2U) { if ((len = krb5_u2u_read(ttyfd,(char *)buf,n)) < 0) return(-1); else return(len); } #endif /* KRB5_U2U */ #endif /* KRB5 */ #endif /* CK_KERBEROS */ #ifdef TCPIPLIB #ifdef OS2 RequestTCPIPMutex(SEM_INDEFINITE_WAIT); #endif /* OS2 */ if (ttibn == 0) if ((rc = ttbufr()) <= 0) { #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ return(rc); } if (ttibn <= n) { len = ttibn; memcpy(buf,&ttibuf[ttibp],len); /* safe */ ttibp += len; ttibn = 0; } else { memcpy(buf,&ttibuf[ttibp],n); /* safe */ ttibp += n; ttibn -= n; len = n; } #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ #else /* TCPIPLIB */ for (i = 0; i < n; i++) { if ((j = netinc(0)) < 0) { if (j < -1) return(j); else break; } buf[i] = j; } len = i; #endif /* TCPIPLIB */ #ifdef COMMENT #ifdef CK_ENCRYPTION /* This would be great if it worked. But what if the buffer we read */ /* contains a telnet negotiation that changes the state of the */ /* encryption. If so, we would be either decrypting unencrypted text */ /* or not decrypting encrypted text. So we must move this call to */ /* all functions that call ttxin(). In OS2 that means os2_netxin() */ /* where the Telnet Negotiations are handled. */ if (u_encrypt) ck_tn_decrypt(buf,len); #endif /* CK_ENCRYPTION */ #endif /* COMMENT */ return(len); } /* N E T I N C -- Input character from network */ #ifdef NETLEBUF #define LEBUF #endif /* NETLEBUF */ #ifdef TTLEBUF #define LEBUF #endif /* TTLEBUF */ #ifndef LEBUF #ifdef OS2 #define LEBUF #endif /* OS2 */ #endif /* LEBUF */ int netinc(timo) int timo; { #ifdef TCPIPLIB int x; unsigned char c; /* The locals. */ #ifdef NETLEBUF if (ttpush >= 0) { debug(F111,"netinc","ttpush",ttpush); c = ttpush; ttpush = -1; return(c); } if (le_data) { if (le_getchar((CHAR *)&c) > 0) { debug(F111,"netinc le_getchar","c",c); return(c); } } #endif /* NETLEBUF */ if (ttyfd == -1) { debug(F100,"netinc socket is closed","",0); return(-2); } #ifdef CK_KERBEROS #ifdef KRB4 #ifdef RLOGCODE if (ttnproto == NP_EK4LOGIN) { if ((x = krb4_des_read(ttyfd,&c,1)) == 0) return(-1); else if (x < 0) return(-2); else return(c); } #endif /* RLOGCODE */ #endif /* KRB4 */ #ifdef KRB5 #ifdef RLOGCODE if (ttnproto == NP_EK5LOGIN) { if ((x = krb5_des_read(ttyfd,&c,1,0)) == 0) return(-1); else if (x < 0) return(-2); else return(c); } #endif /* RLOGCODE */ #ifdef KRB5_U2U if (ttnproto == NP_K5U2U) { if ((x = krb5_u2u_read(ttyfd,&c,1)) == 0) return(-1); else if (x < 0) return(-2); else return(c); } #endif /* KRB5_U2U */ #endif /* KRB5 */ #endif /* CK_KERBEROS */ #ifdef OS2 RequestTCPIPMutex(SEM_INDEFINITE_WAIT); #endif /* OS2 */ if (ttibn > 0) { /* Something in internal buffer? */ #ifdef COMMENT debug(F100,"netinc char in buf","",0); /* Yes. */ #endif /* COMMENT */ x = 0; /* Success. */ } else { /* Else must read from network. */ x = -1; /* Assume failure. */ #ifdef DEBUG debug(F101,"netinc goes to net, timo","",timo); #endif /* DEBUG */ #ifdef CK_SSL /* * In the case of OpenSSL, it is possible that there is still * data waiting in the SSL session buffers that has not yet * been read by Kermit. If this is the case we must process * it without calling select() because select() will not return * with an indication that there is data to be read from the * socket. If there is no data pending in the SSL session * buffers then fall through to the select() code and wait for * some data to arrive. */ if (ssl_active_flag) { x = SSL_pending(ssl_con); if (x < 0) { debug(F111,"netinc","SSL_pending error",x); netclos(); #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ return(-1); } else if ( x > 0 ) { if ( ttbufr() >= 0 ) { x = netinc(timo); #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ return(x); } } x = -1; } else if (tls_active_flag) { x = SSL_pending(tls_con); if (x < 0) { debug(F111,"netinc","TLS_pending error",x); netclos(); #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ return(-1); } else if ( x > 0 ) { if ( ttbufr() >= 0 ) { x = netinc(timo); #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ return(x); } } x = -1; } #endif /* CK_SSL */ #ifndef LEBUF if (timo == 0) { /* Untimed case. */ while (1) { /* Wait forever if necessary. */ if (ttbufr() < 0) /* Refill buffer. */ break; /* Error, fail. */ if (ttibn > 0) { /* Success. */ x = 0; break; } } } else /* Timed case... */ #endif /* LEBUF */ { #ifdef NT_TCP_OVERLAPPED /* This code is for use on NT when we are using */ /* Overlapped I/O to handle reads. In the case */ /* of outstanding reads select() doesn't work */ if (WaitForOverlappedReadData(timo)) { while (1) { if (ttbufr() < 0) /* Keep trying to refill it. */ break; /* Till we get an error. */ if (ttibn > 0) { /* Or we get a character. */ x = 0; break; } } } #else /* NT_TCP_OVERLAPPED */ #ifdef BSDSELECT fd_set rfds; struct timeval tv; int timeout = timo < 0 ? -timo : 1000 * timo; debug(F101,"netinc BSDSELECT","",timo); for ( ; timeout >= 0; timeout -= (timo ? 100 : 0)) { int rc; debug(F111,"netinc","timeout",timeout); /* Don't move select() initialization out of the loop. */ FD_ZERO(&rfds); FD_SET(ttyfd, &rfds); tv.tv_sec = tv.tv_usec = 0L; if (timo) tv.tv_usec = (long) 100000L; else tv.tv_sec = 30; #ifdef NT WSASafeToCancel = 1; #endif /* NT */ rc = select(FD_SETSIZE, #ifdef __DECC #ifdef INTSELECT (int *) #else /* def INTSELECT */ (fd_set *) #endif /* def INTSELECT [else] */ #else /* def __DECC */ (fd_set *) #endif /* def __DECC [else] */ &rfds, NULL, NULL, &tv); if (rc < 0) { int s_errno = socket_errno; debug(F111,"netinc","select",rc); debug(F111,"netinc","socket_errno",s_errno); if (s_errno) { #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ return(-1); } } debug(F111,"netinc","select",rc); #ifdef NT WSASafeToCancel = 0; #endif /* NT */ if (!FD_ISSET(ttyfd, &rfds)) { #ifdef LEBUF if (le_inbuf() > 0) { timeout = -1; break; } #endif /* LEBUF */ /* If waiting forever we have no way of knowing if the */ /* socket closed so try writing a 0-length TCP packet */ /* which should force an error if the socket is closed */ if (!timo) { if ((rc = socket_write(ttyfd,"",0)) < 0) { int s_errno = socket_errno; debug(F101,"netinc socket_write error","",s_errno); #ifdef OS2 if (os2socketerror(s_errno) < 0) { ReleaseTCPIPMutex(); return(-2); } ReleaseTCPIPMutex(); #endif /* OS2 */ return(-1); /* Call it an i/o error */ } } continue; } while (1) { if (ttbufr() < 0) { /* Keep trying to refill it. */ timeout = -1; break; /* Till we get an error. */ } if (ttibn > 0) { /* Or we get a character. */ x = 0; timeout = -1; break; } } } #ifdef NT WSASafeToCancel = 0; #endif /* NT */ #else /* !BSDSELECT */ #ifdef IBMSELECT /* Was used by OS/2, currently not used, but might come in handy some day... ... and it came in handy! For our TCP/IP layer, it avoids all the fd_set and timeval stuff since this is the only place where it is used. */ int socket = ttyfd; int timeout = timo < 0 ? -timo : 1000 * timo; debug(F101,"netinc IBMSELECT","",timo); for ( ; timeout >= 0; timeout -= (timo ? 100 : 0)) { if (select(&socket, 1, 0, 0, 100L) == 1) { while (1) { if (ttbufr() < 0) { /* Keep trying to refill it. */ timeout = -1; break; /* Till we get an error. */ } if (ttibn > 0) { /* Or we get a character. */ x = 0; timeout = -1; break; } } } #ifdef LEBUF else if (le_inbuf() > 0) { timeout = -1; break; } #endif /* LEBUF */ } #else /* !IBMSELECT */ #ifdef WINSOCK /* Actually, under WinSock we have a better mechanism than select() */ /* for setting timeouts (SO_RCVTIMEO, SO_SNDTIMEO) */ SOCKET socket = ttyfd; debug(F101,"netinc NTSELECT","",timo); if (setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&timo, sizeof(timo)) == NO_ERROR) while (1) { if (ttbufr() < 0) /* Keep trying to refill it. */ break; /* Till we get an error. */ if (ttibn > 0) { /* Or we get a character. */ x = 0; break; } } #else /* WINSOCK */ /* If we can't use select(), then we use the regular alarm()/signal() timeout mechanism. */ debug(F101,"netinc alarm","",timo); x = alrm_execute(ckjaddr(njbuf),timo,nettout,donetinc,failnetinc); ttimoff(); /* Timer off. */ #endif /* WINSOCK */ #endif /* IBMSELECT */ #endif /* BSDSELECT */ #endif /* NT_TCP_OVERLAPPED */ } } #ifdef LEBUF if (le_inbuf() > 0) { /* If data was inserted into the */ if (le_getchar((CHAR *)&c) > 0) {/* Local Echo buffer while the */ #ifdef OS2 /* was taking place do not mix */ ReleaseTCPIPMutex(); /* the le data with the net data */ #endif /* OS2 */ return(c); } } #endif /* LEBUF */ if (x < 0) { /* Return -1 if we failed. */ debug(F100,"netinc timed out","",0); #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ return(-1); } else { /* Otherwise */ c = ttibuf[ttibp]; /* Return the first char in ttibuf[] */ if (deblog) { #ifndef COMMENT debug(F101,"netinc returning","",c); #endif /* COMMENT */ if (c == 0) { debug(F101,"netinc 0 ttibn","",ttibn); debug(F101,"netinc 0 ttibp","",ttibp); #ifdef BETADEBUG { #ifdef OS2 extern int tt_type_mode; if ( !ISVTNT(tt_type_mode) ) #endif /* OS2 */ ckhexdump("netinc &ttbuf[ttibp]",&ttibuf[ttibp],ttibn); } #endif /* BETADEBUG */ } } ttibp++; ttibn--; #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ #ifdef CK_ENCRYPTION if (TELOPT_U(TELOPT_ENCRYPTION)) ck_tn_decrypt(&c,1); #endif /* CK_ENCRYPTION */ return(c); } #else /* Not using TCPIPLIB */ return(-1); #endif /* TCPIPLIB */ } /* N E T T O L -- Output a string of bytes to the network */ /* Call with s = pointer to string, n = length. Returns number of bytes actually written on success, or -1 on i/o error, -2 if called improperly. */ int nettol(s,n) CHAR *s; int n; { #ifdef TCPIPLIB int count = 0; int len = n; int try = 0; if (ttyfd == -1) { debug(F100,"nettol socket is closed","",0); return -1; } debug(F101,"nettol TCPIPLIB ttnet","",ttnet); #ifdef COMMENT ckhexdump("nettol",s,n); #endif /* COMMENT */ #ifdef CK_KERBEROS #ifdef KRB4 #ifdef RLOGCODE if (ttnproto == NP_EK4LOGIN) { return(krb4_des_write(ttyfd,s,n)); } #endif /* RLOGCODE */ #endif /* KRB4 */ #ifdef KRB5 #ifdef RLOGCODE if (ttnproto == NP_EK5LOGIN) { return(krb5_des_write(ttyfd,s,n,0)); } #endif /* RLOGCODE */ #ifdef KRB5_U2U if (ttnproto == NP_K5U2U) { return(krb5_u2u_write(ttyfd,s,n)); } #endif /* KRB5_U2U */ #endif /* KRB5 */ #endif /* CK_KERBEROS */ #ifdef CK_ENCRYPTION if (TELOPT_ME(TELOPT_ENCRYPTION)) ck_tn_encrypt(s,n); #endif /* CK_ENCRYPTION */ #ifdef CK_SSL if (ssl_active_flag || tls_active_flag) { int error, r; /* Write using SSL */ ssl_retry: if (ssl_active_flag) r = SSL_write(ssl_con, s, len /* >1024?1024:len */); else r = SSL_write(tls_con, s, len /* >1024?1024:len */); switch (SSL_get_error(ssl_active_flag?ssl_con:tls_con,r)) { case SSL_ERROR_NONE: debug(F111,"nettol","SSL_write",r); if ( r == len ) return(n); s += r; len -= r; goto ssl_retry; case SSL_ERROR_WANT_WRITE: debug(F100,"nettol SSL_ERROR_WANT_WRITE","",0); return(-1); case SSL_ERROR_WANT_READ: debug(F100,"nettol SSL_ERROR_WANT_READ","",0); return(-1); case SSL_ERROR_SYSCALL: if ( r == 0 ) { /* EOF */ netclos(); return(-2); } else { int rc = -1; #ifdef NT int gle = GetLastError(); debug(F111,"nettol SSL_ERROR_SYSCALL", "GetLastError()",gle); rc = os2socketerror(gle); if (rc == -1) rc = -2; else if ( rc == -2 ) rc = -1; #endif /* NT */ return(rc); } case SSL_ERROR_WANT_X509_LOOKUP: debug(F100,"nettol SSL_ERROR_WANT_X509_LOOKUP","",0); netclos(); return(-2); case SSL_ERROR_SSL: debug(F100,"nettol SSL_ERROR_SSL","",0); if (bio_err!=NULL) { int len; extern char ssl_err[]; BIO_printf(bio_err,"nettol() SSL_ERROR_SSL\n"); ERR_print_errors(bio_err); len = BIO_read(bio_err,ssl_err,SSL_ERR_BFSZ); ssl_err[len < SSL_ERR_BFSZ ? len : SSL_ERR_BFSZ] = '\0'; debug(F110,"nettol SSL_ERROR_SSL",ssl_err,0); if (ssl_debug_flag) printf(ssl_err); } else if (ssl_debug_flag) { debug(F100,"nettol SSL_ERROR_SSL","",0); fflush(stderr); fprintf(stderr,"nettol() SSL_ERROR_SSL\n"); ERR_print_errors_fp(stderr); } #ifdef COMMENT netclos(); return(-2); #else return(-1); #endif case SSL_ERROR_ZERO_RETURN: debug(F100,"nettol SSL_ERROR_ZERO_RETURN","",0); netclos(); return(-2); default: debug(F100,"nettol SSL_ERROR_?????","",0); netclos(); return(-2); } } #endif /* CK_SSL */ nettol_retry: try++; /* Increase the try counter */ if (ttnet == NET_TCPB) { #ifdef BSDSELECT fd_set wfds; struct timeval tv; debug(F101,"nettol BSDSELECT","",0); tv.tv_usec = 0L; tv.tv_sec=30; #ifdef NT WSASafeToCancel = 1; #endif /* NT */ #ifdef STREAMING do_select: #endif /* STREAMING */ FD_ZERO(&wfds); FD_SET(ttyfd, &wfds); if (select(FD_SETSIZE, NULL, #ifdef __DECC #ifndef __DECC_VER (int *) #endif /* __DECC_VER */ #endif /* __DECC */ &wfds, NULL, &tv) < 0) { int s_errno = socket_errno; debug(F101,"nettol select failed","",s_errno); #ifdef BETADEBUG printf("nettol select failed: %d\n", s_errno); #endif /* BETADEBUG */ #ifdef NT WSASafeToCancel = 0; if (!win95selectbug) #endif /* NT */ return(-1); } if (!FD_ISSET(ttyfd, &wfds)) { #ifdef STREAMING if (streaming) goto do_select; #endif /* STREAMING */ debug(F111,"nettol","!FD_ISSET",ttyfd); #ifdef NT WSASafeToCancel = 0; if (!win95selectbug) #endif /* NT */ return(-1); } #ifdef NT WSASafeToCancel = 0; #endif /* NT */ #else /* BSDSELECT */ #ifdef IBMSELECT { int tries = 0; debug(F101,"nettol IBMSELECT","",0); while (select(&ttyfd, 0, 1, 0, 1000) != 1) { int count; if (tries++ >= 60) { /* if after 60 seconds we can't get permission to write */ debug(F101,"nettol select failed","",socket_errno); return(-1); } if ((count = nettchk()) < 0) { debug(F111,"nettol","nettchk()",count); return(count); } } } #endif /* IBMSELECT */ #endif /* BSDSELECT */ if ((count = socket_write(ttyfd,s,n)) < 0) { int s_errno = socket_errno; /* maybe a function */ debug(F101,"nettol socket_write error","",s_errno); #ifdef OS2 if (os2socketerror(s_errno) < 0) return(-2); #endif /* OS2 */ return(-1); /* Call it an i/o error */ } if (count < n) { debug(F111,"nettol socket_write",s,count); if (try > 25) { /* don't try more than 25 times */ debug(F100,"nettol tried more than 25 times","",0); return(-1); } if (count > 0) { s += count; n -= count; } debug(F111,"nettol retry",s,n); goto nettol_retry; } else { debug(F111,"nettol socket_write",s,count); return(len); /* success - return total length */ } } else return(-2); #else debug(F100,"nettol TCPIPLIB not defined","",0); return(-2); #endif /* TCPIPLIB */ } /* N E T T O C -- Output character to network */ /* Call with character to be transmitted. Returns 0 if transmission was successful, or -1 upon i/o error, or -2 if called improperly. */ int #ifdef CK_ANSIC nettoc(CHAR c) #else nettoc(c) CHAR c; #endif /* CK_ANSIC */ /* nettoc */ { #ifdef UNIX return(ttoc(c)); #else #ifdef TCPIPLIB unsigned char cc; if (ttyfd == -1) { debug(F100,"nettoc socket is closed","",0); return -1; } cc = c; debug(F101,"nettoc cc","",cc); #ifdef CK_KERBEROS #ifdef KRB4 #ifdef RLOGCODE if (ttnproto == NP_EK4LOGIN) { return(krb4_des_write(ttyfd,&cc,1)==1?0:-1); } #endif /* RLOGCODE */ #endif /* KRB4 */ #ifdef KRB5 #ifdef RLOGCODE if (ttnproto == NP_EK5LOGIN) { return(krb5_des_write(ttyfd,&cc,1,0)==1?0:-1); } #endif /* RLOGCODE */ #ifdef KRB5_U2U if (ttnproto == NP_K5U2U) { return(krb5_u2u_write(ttyfd,&cc,1)==1?0:-1); } #endif /* KRB5_U2U */ #endif /* KRB5 */ #endif /* CK_KERBEROS */ #ifdef CK_ENCRYPTION if ( TELOPT_ME(TELOPT_ENCRYPTION) ) ck_tn_encrypt(&cc,1); #endif /* CK_ENCRYPTION */ #ifdef CK_SSL if (ssl_active_flag || tls_active_flag) { int len, error; /* Write using SSL */ ssl_retry: if (ssl_active_flag) len = SSL_write(ssl_con, &cc, 1); else len = SSL_write(tls_con, &cc, 1); switch (SSL_get_error(ssl_active_flag?ssl_con:tls_con,len)) { case SSL_ERROR_NONE: debug(F111,"nettoc","SSL_write",len); return(len == 1 ? 0 : -1); case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_READ: return(-1); case SSL_ERROR_SYSCALL: if ( len == 0 ) { /* EOF */ netclos(); return(-2); } else { int rc = -1; #ifdef NT int gle = GetLastError(); debug(F111,"nettoc SSL_ERROR_SYSCALL", "GetLastError()",gle); rc = os2socketerror(gle); if (rc == -1) rc = -2; else if ( rc == -2 ) rc = -1; #endif /* NT */ return(rc); } case SSL_ERROR_SSL: if (bio_err!=NULL) { int len; extern char ssl_err[]; BIO_printf(bio_err,"nettoc() SSL_ERROR_SSL\n"); ERR_print_errors(bio_err); len = BIO_read(bio_err,ssl_err,SSL_ERR_BFSZ); ssl_err[len < SSL_ERR_BFSZ ? len : SSL_ERR_BFSZ] = '\0'; debug(F110,"nettoc SSL_ERROR_SSL",ssl_err,0); if (ssl_debug_flag) printf(ssl_err); } else if (ssl_debug_flag) { debug(F100,"nettoc SSL_ERROR_SSL","",0); fflush(stderr); fprintf(stderr,"nettoc() SSL_ERROR_SSL\n"); ERR_print_errors_fp(stderr); } return(-1); break; case SSL_ERROR_WANT_X509_LOOKUP: case SSL_ERROR_ZERO_RETURN: default: netclos(); return(-2); } } #endif /* CK_SSL */ if (ttnet == NET_TCPB) { #ifdef BSDSELECT fd_set wfds; struct timeval tv; debug(F101,"nettoc BSDSELECT","",0); tv.tv_usec = 0L; tv.tv_sec = 30; #ifdef STREAMING do_select: #endif /* STREAMING */ FD_ZERO(&wfds); FD_SET(ttyfd, &wfds); if (select(FD_SETSIZE, NULL, #ifdef __DECC #ifndef __DECC_VER (int *) #endif /* __DECC_VER */ #endif /* __DECC */ &wfds, NULL, &tv) < 0) { int s_errno = socket_errno; debug(F101,"nettoc select failed","",s_errno); #ifdef BETADEBUG printf("nettoc select failed: %d\n", s_errno); #endif /* BETADEBUG */ #ifdef NT WSASafeToCancel = 0; if (!win95selectbug) #endif /* NT */ return(-1); } if (!FD_ISSET(ttyfd, &wfds)) { #ifdef STREAMING if (streaming) goto do_select; #endif /* STREAMING */ debug(F111,"nettoc","!FD_ISSET",ttyfd); #ifdef NT WSASafeToCancel = 0; if (!win95selectbug) #endif /* NT */ return(-1); } #ifdef NT WSASafeToCancel = 0; #endif /* NT */ #else /* BSDSELECT */ #ifdef IBMSELECT { int tries = 0; while (select(&ttyfd, 0, 1, 0, 1000) != 1) { int count; if (tries++ >= 60) { /* if after 60 seconds we can't get permission to write */ debug(F101,"nettoc select failed","",socket_errno); return(-1); } if ((count = nettchk()) < 0) { debug(F111,"nettoc","nettchk()",count); return(count); } } } #endif /* IBMSELECT */ #endif /* BSDSELECT */ if (socket_write(ttyfd,&cc,1) < 1) { int s_errno = socket_errno; /* maybe a function */ debug(F101,"nettoc socket_write error","",s_errno); #ifdef OS2 if (os2socketerror(s_errno) < 0) return(-2); #endif /* OS2 */ return(-1); } debug(F101,"nettoc socket_write","", cc); return(0); } else return(-2); #else return(-2); #endif /* TCPIPLIB */ #endif /* UNIX */ } /* N E T F L U I -- Flush network input buffer */ #ifdef TNCODE static int #ifdef CK_ANSIC netgetc(int timo) /* Input function to point to... */ #else /* CK_ANSIC */ netgetc(timo) int timo; #endif /* CK_ANSIC */ { /* ...in the tn_doop() call */ #ifdef TCPIPLIB return netinc(timo); #else /* TCPIPLIB */ return ttinc(timo); #endif /* TCPIPLIB */ } #endif /* TNCODE */ int netflui() { int n; int ch; #ifdef NETLEBUF ttpush = -1; /* Clear the peek-ahead char */ while (le_data && (le_inbuf() > 0)) { CHAR ch = '\0'; if (le_getchar(&ch) > 0) { debug(F101,"ttflui le_inbuf ch","",ch); } } #endif /* NETLEBUF */ #ifdef TCPIPLIB #ifdef OS2 RequestTCPIPMutex(SEM_INDEFINITE_WAIT); #endif /* OS2 */ #ifdef TNCODE if (ttnproto == NP_TELNET) { /* Netflui must process Telnet negotiations or get out of sync */ if ((n = nettchk()) <= 0) goto exit_flui; while (n-- > 0) { ch = netinc(1); if (ch == IAC) { extern int duplex; /* this really shouldn't be here but ... */ int tx = tn_doop((CHAR)(ch & 0xff),duplex,netgetc); if (tx == 1) duplex = 1; else if (tx == 2) duplex = 0; n = nettchk(); } } } else #endif /* TNCODE */ { ttibuf[ttibp+ttibn] = '\0'; debug(F111,"netflui 1",ttibuf,ttibn); #ifdef CK_ENCRYPTION if (TELOPT_U(TELOPT_ENCRYPTION)) { ck_tn_decrypt(&ttibuf[ttibp],ttibn); } #endif /* CK_ENCRYPTION */ ttibn = ttibp = 0; /* Flush internal buffer *FIRST* */ if (ttyfd < 1) goto exit_flui; if ((n = nettchk()) > 0) { /* Now see what's waiting on the net */ if (n > TTIBUFL) n = TTIBUFL; /* and sponge it up */ debug(F101,"netflui 2","",n); /* ... */ n = socket_read(ttyfd,ttibuf,n); /* into our buffer */ if (n >= 0) ttibuf[n] = '\0'; debug(F111,"netflui 3",ttibuf,n); #ifdef CK_ENCRYPTION if (TELOPT_U(TELOPT_ENCRYPTION)) { ck_tn_decrypt(&ttibuf[ttibp],n); } #endif /* CK_ENCRYPTION */ ttibuf[0] = '\0'; } } #else /* !TCPIPLIB */ if (ttyfd < 1) goto exit_flui; #ifdef TNCODE if (ttnproto == NP_TELNET) { if ((n = ttchk()) <= 0) goto exit_flui; while (n-- >= 0) { /* Netflui must process Telnet negotiations or get out of sync */ ch = ttinc(1); if (ch == IAC) { extern int duplex; /* this really shouldn't be here but ... */ int tx = tn_doop((CHAR)(ch & 0xff),duplex,netgetc); if (tx == 1) duplex = 1; else if (tx == 2) duplex = 0; n = ttchk(); } }; } else #endif /* TNCODE */ if ((n = ttchk()) > 0) { debug(F101,"netflui non-TCPIPLIB","",n); while ((n--) && ttinc(1) > -1) /* Don't worry, ttinc() is buffered */ ; /* and it handles the decryption... */ } #endif /* TCPIPLIB */ exit_flui: #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ return(0); } #ifdef CK_KERBEROS /* The following two functions are required for encrypted rlogin */ /* They are called with nettoc() or nettol() are transmitting */ /* encrypted data. They call a function to encrypt the data */ /* and that function needs to be able to write to/read from the */ /* network in an unimpeded manner. Hence, these two simple fns. */ int net_write(fd, buf, len) int fd; register const char *buf; int len; { int cc; register int wrlen = len; do { #ifdef TCPIPLIB cc = socket_write(fd, buf, wrlen); #else cc = write(fd,buf,wrlen); #endif /* TCPIPLIB */ if (cc < 0) { int s_errno = socket_errno; debug(F101,"net_write error","",s_errno); #ifdef OS2 if (os2socketerror(s_errno) < 0) return(-1); else continue; #else /* OS2 */ if (errno == EINTR) continue; return(-1); #endif /* OS2 */ } else { buf += cc; wrlen -= cc; } } while (wrlen > 0); return(len); } int net_read(fd, buf, len) int fd; register char *buf; register int len; { int cc, len2 = 0; do { #ifdef TCPIPLIB cc = socket_read(fd, buf, len); #else cc = read(fd,buf,len); #endif if (cc < 0) { int s_errno = socket_errno; debug(F101,"net_read error","",s_errno); #ifdef OS2 if (os2socketerror(s_errno) < 0) return(-1); #endif /* OS2 */ return(cc); /* errno is already set */ } else if (cc == 0) { netclos(); return(len2); } else { buf += cc; len2 += cc; len -= cc; } } while (len > 0); return(len2); } #endif /* CK_KERBEROS */ #endif /* NONET */ /* getlocalipaddr() attempts to resolve an IP Address for the local machine. * If the host is multi-homed it returns only one address. * * Two techniques are used. * (1) get the local host name and perform a DNS lookup, then take * the first entry; * (2) open a UDP socket, use it to connect to a fictitious host (it's OK, * no data is sent), then retrieve the local address from the socket. * Note: the second technique won't work on Microsoft systems. See * Article ID: Q129065 PRB: Getsockname() Returns IP Address 0.0.0.0 for UDP */ /* Technique number one cannot work reliably if the machine is a laptop * and the hostname is associated with a physical adapter which is not * installed and a PPP connection is being used instead. This is because * the hostname DNS lookup will succeed for the physical adapter even though * it would be impossible to use it. In NT4 SP4, the gethostbyname() * when given the result of gethostname() returns not the real DNS entries * for that name+domain. Instead it returns all of the static and dynamic * IP addresses assigned to any physical or virtual adapter defined in the * system regardless of whether or not it is installed. The order of the * addresses is fixed according to the binding order in the NT registry. */ /* * It appears that calling gethostbyname(NULL) is more reliable than * calling gethostbyname(gethostname()) on Windows. So on Windows we will * only call gethostbyname(NULL). */ int getlocalipaddr() { #ifndef datageneral struct sockaddr_in l_sa; struct sockaddr_in r_sa; GSOCKNAME_T slen = sizeof(struct sockaddr_in); int sock; int rc; struct in_addr laddr; /* if still not resolved, then try second strategy */ /* This second strategy does not work on Windows */ debug(F100,"getlocalipaddr","",0); memset(&l_sa,0,slen); memset(&r_sa,0,slen); /* get a UDP socket */ sock = socket(AF_INET, SOCK_DGRAM, 0); if (sock != -1) { /* connect to arbirary port and address (NOT loopback) */ r_sa.sin_family = AF_INET; r_sa.sin_port = htons(IPPORT_ECHO); /* The following is an "illegal conversion" in AOS/VS */ /* (and who knows where else) */ #ifdef INADDRX inaddrx = inet_addr("128.127.50.1"); r_sa.sin_addr.s_addr = *(unsigned long *)&inaddrx; #else r_sa.sin_addr.s_addr = inet_addr("128.127.50.1"); #endif /* INADDRX */ rc = connect(sock, (struct sockaddr *) &r_sa, sizeof(struct sockaddr)); if (!rc) { /* get local address */ getsockname(sock,(struct sockaddr *)&l_sa,&slen); #ifdef TCPIPLIB socket_close(sock); /* We're done with the socket */ #else close(sock); #endif /* TCPIPLIB */ if (l_sa.sin_addr.s_addr != INADDR_ANY) { myxipaddr = ntohl(l_sa.sin_addr.s_addr); ckstrncpy(myipaddr,(char *)inet_ntoa(l_sa.sin_addr),20); debug(F110,"getlocalipaddr setting buf to",myipaddr,0); return(0); } } } return getlocalipaddrs(myipaddr,sizeof(myipaddr),0); #else /* datageneral */ return(-1); #endif /* datageneral */ } int getlocalipaddrs(buf,bufsz,index) char * buf; int bufsz; int index; /* getlocalipaddrs */ { #ifndef datageneral char localhost[256]; struct hostent * host=NULL; struct sockaddr_in l_sa; struct sockaddr_in r_sa; GSOCKNAME_T slen = sizeof(struct sockaddr_in); int rc; #ifdef COMMENT int sock; char messageBuf[60]; struct in_addr laddr; #endif /* COMMENT */ debug(F100,"getlocalipaddrs","",0); memset(&l_sa,0,slen); memset(&r_sa,0,slen); /* init local address (to zero) */ l_sa.sin_addr.s_addr = INADDR_ANY; #ifdef CKGHNLHOST rc = gethostname(localhost, 256); debug(F110,"getlocalipaddrs localhost",localhost,0); #else /* This doesn't work on some platforms, e.g. Solaris */ rc = 0; localhost[0] = '\0'; #ifdef NT if ( winsock_version < 20 ) { rc = gethostname(localhost, 256); debug(F110,"getlocalipaddrs localhost",localhost,0); } #endif /* NT */ #endif /* CKGHNLHOST */ if (!rc) { /* resolve host name for local address */ debug(F110,"getlocalipaddrs","calling gethostbyname()",0); host = gethostbyname(localhost); /* debug(F111,"getlocalipaddrs","gethostbyname() returned",host); */ if (host) { #ifdef HADDRLIST host = ck_copyhostent(host); if ( index < 0 || index > 63 || !host->h_addr_list[index] ) { buf[0] = '\0'; return(-1); } l_sa.sin_addr.s_addr = *((unsigned long *) (host->h_addr_list[index])); ckstrncpy(buf,(char *)inet_ntoa(l_sa.sin_addr),20); debug(F110,"getlocalipaddrs setting buf to",buf,0); #ifdef COMMENT /* This is for reporting multiple IP Address */ while (host->h_addr_list && host->h_addr_list[0]) { l_sa.sin_addr.s_addr = *((unsigned long *) (host->h_addr_list[0])); ckstrncpy(messageBuf, (char *)inet_ntoa(l_sa.sin_addr),60); if (tcp_address) { if (!strcmp(messageBuf,tcp_address)) ckstrncpy(myipaddr,tcp_address,20); } debug(F110,"getlocalipaddrs ip address list", messageBuf, 0); host->h_addr_list++; } #endif /* COMMENT */ #else /* HADDRLIST */ if (index != 0) { buf[0] = '\0'; return(-1); } l_sa.sin_addr.s_addr = *((unsigned long *) (host->h_addr)); ckstrncpy(buf,(char *)inet_ntoa(l_sa.sin_addr),bufsz); debug(F110,"getlocalipaddrs setting buf to",buf,0); #endif /* HADDRLIST */ return(0); } else debug(F110, "getlocalipaddrs: gethostbyname() failed", localhost, 0 ); } #endif /* datageneral */ return(-1); } #ifdef RLOGCODE /* TCP/IP RLOGIN protocol support code */ int rlog_naws() { struct rlog_naws { unsigned char id[4]; unsigned short rows, cols, ypix, xpix; } nawsbuf; if (ttnet != NET_TCPB) return 0; if (ttnproto != NP_RLOGIN #ifdef CK_KERBEROS && ttnproto != NP_K4LOGIN && ttnproto != NP_EK4LOGIN && ttnproto != NP_K5LOGIN && ttnproto != NP_EK5LOGIN #endif /* CK_KERBEROS */ ) return 0; if (!TELOPT_ME(TELOPT_NAWS)) return 0; debug(F100,"rlogin Window Size sent","",0); nawsbuf.id[0] = nawsbuf.id[1] = 0377; nawsbuf.id[2] = nawsbuf.id[3] = 's'; #ifdef OS2 nawsbuf.rows = htons((unsigned short) (VscrnGetHeight(VTERM) -(tt_status[VTERM]?1:0))); nawsbuf.cols = htons((unsigned short) VscrnGetWidth(VTERM)); #else /* OS2 */ nawsbuf.rows = htons((unsigned short) tt_rows); nawsbuf.cols = htons((unsigned short) tt_cols); #endif /* OS2 */ nawsbuf.ypix = htons(0); /* y pixels */ nawsbuf.xpix = htons(0); /* x pixels */ if (ttol((CHAR *)(&nawsbuf), sizeof(nawsbuf)) < 0) return(-1); return(0); } #ifdef OS2ORUNIX #define RLOGOUTBUF #endif /* OS2 */ static int #ifdef CK_ANSIC rlog_ini(CHAR * hostname, int port, struct sockaddr_in * l_addr, struct sockaddr_in * r_addr) #else /* CK_ANSIC */ rlog_ini(hostname, port, l_addr, r_addr) CHAR * hostname; int port; struct sockaddr_in * l_addr; struct sockaddr_in * r_addr; #endif /* CK_ANSIC */ /* rlog_ini */ { #ifdef RLOGOUTBUF char outbuf[512]; int outbytes=0; #endif /* RLOGOUTBUF */ int flag = 0; #define TERMLEN 16 #define CONSPDLEN 16 CHAR localuser[UIDBUFLEN+1]; CHAR remoteuser[UIDBUFLEN+1]; int userlen = 0; CHAR term_speed[TERMLEN+CONSPDLEN+1]; #ifdef CONGSPD long conspd = -1L; #endif /* CONGSPD */ #ifdef OS2 extern int tt_type, max_tt; extern struct tt_info_rec tt_info[]; #endif /* OS2 */ int i, n; int rc = 0; tn_reset(); /* This call will reset all of the Telnet */ /* options and then quit. We need to do */ /* this since we use the Telnet options */ /* to hold various state information */ duplex = 0; /* Rlogin is always remote echo */ rlog_inband = 0; #ifdef CK_TTGWSIZ /* But compute the values anyway before the first read since the out- of-band NAWS request would arrive before the first data byte (NULL). */ #ifdef OS2 /* Console terminal screen rows and columns */ debug(F101,"rlog_ini tt_rows 1","",VscrnGetHeight(VTERM) -(tt_status[VTERM]?1:0)); debug(F101,"rlog_ini tt_cols 1","",VscrnGetWidth(VTERM)); /* Not known yet */ if (VscrnGetWidth(VTERM) < 0 || VscrnGetHeight(VTERM)-(tt_status[VTERM]?1:0) < 0) { ttgwsiz(); /* Try to get screen dimensions */ } debug(F101, "rlog_ini tt_rows 2", "", VscrnGetHeight(VTERM)-(tt_status[VTERM]?1:0) ); debug(F101,"rlog_ini tt_cols 2","",VscrnGetWidth(VTERM)); #else /* OS2 */ debug(F101,"rlog_ini tt_rows 1","",tt_rows); debug(F101,"rlog_ini tt_cols 1","",tt_cols); if (tt_rows < 0 || tt_cols < 0) { /* Not known yet */ ttgwsiz(); /* Try to find out */ } debug(F101,"rlog_ini tt_rows 2","",tt_rows); debug(F101,"rlog_ini tt_cols 2","",tt_cols); #endif /* OS2 */ #endif /* CK_TTGWSIZ */ ttflui(); /* Start by flushing the buffers */ rlog_mode = RL_COOKED; /* Determine the user's local username ... */ localuser[0] = '\0'; #ifdef NT { char localuid[UIDBUFLEN+1]; ckstrncpy((char *)localuser,(char *)GetLocalUser(),UIDBUFLEN); } if ( !localuser[0] ) #endif /* NT */ { char * user = getenv("USER"); if (!user) user = ""; userlen = strlen(user); debug(F111,"rlogin getenv(USER)",user,userlen); ckstrncpy((char *)localuser,user,UIDBUFLEN); debug(F110,"rlog_ini localuser 1",localuser,0); } if ( !localuser[0] ) strcpy((char *)localuser,"unknown"); else if (ck_lcname) { cklower((char *)localuser); debug(F110,"rlog_ini localuser 2",localuser,0); } /* And the username to login with */ if (uidbuf[0]) { ckstrncpy((char *)remoteuser,uidbuf,UIDBUFLEN); debug(F110,"rlog_ini remoteuser 1",remoteuser,0); } else if (localuser[0]) { ckstrncpy((char *)remoteuser,(char *)localuser,UIDBUFLEN); debug(F110,"rlog_ini remoteuser 2",remoteuser,0); } else { remoteuser[0] = '\0'; debug(F110,"rlog_ini remoteuser 3",remoteuser,0); } if (ck_lcname) cklower((char *)remoteuser); debug(F110,"rlog_ini remoteuser 4",remoteuser,0); /* The command to issue is the terminal type and speed */ term_speed[0] = '\0'; if (tn_term) { /* SET TELNET TERMINAL-TYPE value */ if (*tn_term) { /* (if any) takes precedence. */ ckstrncpy((char *)term_speed, tn_term, TERMLEN); flag = 1; } } else { /* Otherwise the local terminal type */ #ifdef OS2 /* In terminal-emulating versions, it's the SET TERM TYPE value */ ckstrncpy(term_speed, (tt_type >= 0 && tt_type <= max_tt) ? tt_info[tt_type].x_name : "network", TERMLEN); #else /* In the others, we just look at the TERM environment variable */ { char *p = getenv("TERM"); if (p) ckstrncpy((char *)term_speed,p,TERMLEN); else term_speed[0] = '\0'; #ifdef VMS for (p = (char *) term_speed; *p; p++) { if (*p == '-' && (!strcmp(p,"-80") || !strcmp(p,"-132"))) break; else if (isupper(*p)) *p = tolower(*p); } *p = '\0'; #endif /* VMS */ } #endif /* OS2 */ } n = strlen((char *)term_speed); if (n > 0) { /* We have a terminal type */ if (!flag) { /* If not user-specified */ for (i = 0; i < n; i++) /* then lowercase it. */ if (isupper(term_speed[i])) term_speed[i] = tolower(term_speed[i]); } debug(F110,"rlog_ini term_speed 1",term_speed,0); #ifdef CONGSPD /* conspd() is not yet defined in all ck*tio.c modules */ conspd = congspd(); if (conspd > 0L) { ckstrncat((char *)term_speed,"/",sizeof(term_speed)); ckstrncat((char *)term_speed,ckltoa(conspd),sizeof(term_speed)); } else #endif /* CONGSPD */ ckstrncat((char *)term_speed,"/19200",sizeof(term_speed)); debug(F110,"rlog_ini term_speed 2",term_speed,0); } else { term_speed[0] = '\0'; debug(F110,"rlog_ini term_speed 3",term_speed,0); } #ifdef CK_KERBEROS if (ttnproto == NP_K4LOGIN || ttnproto == NP_EK4LOGIN || ttnproto == NP_K5LOGIN || ttnproto == NP_EK5LOGIN) { int kver, encrypt, rc; switch (ttnproto) { case NP_K4LOGIN: kver = 4; encrypt = 0; break; case NP_EK4LOGIN: kver = 4; encrypt = 1; break; case NP_K5LOGIN: kver = 5; encrypt = 0; break; case NP_EK5LOGIN: kver = 5; encrypt = 1; break; default: kver = 0; encrypt = 0; } rc = ck_krb_rlogin(hostname, port, localuser, remoteuser, term_speed, l_addr, r_addr, kver, encrypt); if (!rc) { /* success */ TELOPT_ME(TELOPT_NAWS) = 1; rc = rlog_naws(); } return(rc); } else #endif /* CK_KERBEROS */ if (ttnproto == NP_RLOGIN) { #ifdef RLOGOUTBUF /* * The rcmds start the connection with a series of init data: * * a port number upon which client is listening for stderr data * the user's name on the client machine * the user's name on the server machine * the terminal_type/speed or command to execute */ outbuf[outbytes++] = 0; strcpy((char *)outbuf+outbytes,(char *)localuser); outbytes += strlen((char *)localuser) + 1; strcpy((char *)outbuf+outbytes,(char *)remoteuser); outbytes += strlen((char *)remoteuser) + 1; strcpy((char *)outbuf+outbytes,(char *)term_speed); outbytes += strlen((char *)term_speed) + 1; rc = ttol((CHAR *)outbuf,outbytes); #else /* RLOGOUTBUF */ ttoc(0); /* Send an initial NUL as wake-up */ /* Send each variable with the trailing NUL */ rc = ttol(localuser,strlen((char *)localuser)+1); if (rc > 0) rc = ttol(remoteuser,strlen((char *)remoteuser)+1); if (rc > 0) rc = ttol(term_speed,strlen((char *)term_speed)+1); #endif /* RLOGOUTBUF */ /* Now we are supposed to get back a single NUL as confirmation */ errno = 0; rc = ttinc(60); debug(F101,"rlogin first ttinc","",rc); if (rc > 0) { debug(F101,"rlogin ttinc 1","",rc); printf( "Rlogin protocol error - 0x%x received instead of 0x00\n", rc); return(-1); } else if (rc < 0) { debug(F101,"rlogin ttinc errno","",errno); /* printf("Network error: %d\n", errno); */ return(-1); } } return(0); } /* two control messages are defined: a double flag byte of 'o' indicates a one-byte message which is identical to what was once carried out of band. a double flag byte of 'q' indicates a zero-byte message. This message is interpreted as two \377 data bytes. This is just a quote rule so that binary data from the server does not confuse the client. */ int rlog_ctrl(cp, n) unsigned char *cp; int n; { if ((n >= 5) && (cp[2] == 'o') && (cp[3] == 'o')) { if (rlog_oob(&cp[4],1)) return(-5); return(5); } else if ((n >= 4) && (cp[2] == 'q') && (cp[3] == 'q')) { /* this is somewhat of a hack */ cp[2] = '\377'; cp[3] = '\377'; return(2); } return(0); } static int rlog_oob(oobdata, count) CHAR * oobdata; int count; { int i; int flush = 0; debug(F111,"rlogin out_of_band","count",count); for (i = 0; i MAXPADPARMS) x29err[i+2] = *ps; else padparms[*ps] = *(ps+1); ps += 2; } } } /* Read PAD parameters */ VOID readpad(s,n,r) CHAR *s; int n; CHAR *r; { int i; CHAR *ps = s; CHAR *pr = r; *pr++ = X29_PARAMETER_INDICATION; if (n > 0) { for (i = 0; i < n; i++, ps++) { if (*ps > MAXPADPARMS) { x29err[i+2] = *ps++; } else { *pr++ = *ps; *pr++ = padparms[*ps++]; } } } else { for (i = 1; i < MAXPADPARMS; i++) { *pr++ = i; *pr++ = padparms[i]; } } } int qbitpkt(s,n) CHAR *s; int n; { CHAR *ps = s; int x29cmd = *ps; CHAR *psa = s+1; CHAR x29resp[(MAXPADPARMS*2)+1]; switch (x29cmd) { case X29_SET_PARMS: setpad (ps+1,n/2); if ((int)strlen((char *)x29err) > 2) { ttol(x29err,(int)strlen((char *)x29err)); x29err[2] = '\0'; } return (-2); case X29_READ_PARMS: readpad (ps+1,n/2,x29resp); setqbit (); ttol(x29resp,(n>1)?(n+1):(2*MAXPADPARMS+1)); if ((int)strlen((char *)x29err) > 2) { ttol(x29err,(int)strlen((char *)x29err)); x29err[2] = '\0'; } resetqbit(); break; case X29_SET_AND_READ_PARMS: setpad (ps+1,n/2); readpad (ps+1,n/2,x29resp); setqbit(); ttol(x29resp,(n>1)?(n+1):(2*MAXPADPARMS+1)); if ((int)strlen((char *)x29err) > 2) { ttol (x29err,(int)strlen((char *)x29err)); x29err [2] = '\0'; } resetqbit(); return (-2); case X29_INVITATION_TO_CLEAR: (VOID) x25clear(); return (-1); case X29_INDICATION_OF_BREAK: break; } return (0); } /* PAD break action processor */ VOID breakact() { extern char x25obuf[MAXOX25]; extern int obufl; extern int active; extern unsigned char tosend; static CHAR indbrk[3] = { X29_INDICATION_OF_BREAK, PAD_SUPPRESSION_OF_DATA, 1 }; CHAR intudat, cause, diag; if (x25stat() < 0) return; /* Ignore if no virtual call established */ if (padparms[PAD_BREAK_ACTION] != 0) /* Forward condition */ if (ttol((CHAR *)x25obuf,obufl) < 0) { perror ("\r\nCan't send characters"); active = 0; } else { bzero (x25obuf,sizeof(x25obuf)); obufl = 0; tosend = 0; }; switch (padparms[PAD_BREAK_ACTION]) { case 0 : break; /* do nothing */ case 1 : /* send interrupt packet with interrupt user data field = 1 */ intudat = 1; x25intr (intudat); break; case 2 : /* send reset packet with cause and diag = 0 */ cause = diag = 0; x25reset (cause,diag); break; case 5 : /* send interrupt packet with interrupt user data field = 0 */ intudat = 0; x25intr (intudat); setqbit (); /* send indication of break without a parameter field */ ttoc(X29_INDICATION_OF_BREAK); resetqbit (); break; case 8 : active = 0; /* leave data transfer */ conol ("\r\n"); break; case 21: /* send interrupt packet with interrupt user data field = 0 */ intudat = 0; x25intr (intudat); setpad (indbrk+1,2); /* set pad to discard input */ setqbit (); /* send indication of break with parameter field */ ttol (indbrk,sizeof(indbrk)); resetqbit (); break; } } /* X.25 support functions */ X25_CAUSE_DIAG diag; /* Convert a null-terminated string representing an X.121 address to a packed BCD form. */ int pkx121(str,bcd) char *str; CHAR *bcd; { int i, j; u_char c; i = j = 0; while (str[i]) { if (i >= 15 || str [i] < '0' || str [i] > '9') return (-1); c = str [i] - '0'; if (i & 1) bcd [j++] |= c; else bcd [j] = c << 4; i++; } return (i); } /* Reads and prints X.25 diagnostic */ int x25diag () { int i; bzero ((char *)&diag,sizeof(diag)); if (ioctl(ttyfd,X25_RD_CAUSE_DIAG,&diag)) { perror ("Reading X.25 diagnostic"); return(-1); } if (diag.datalen > 0) { printf ("X.25 Diagnostic :"); for (i = 0; i < (int)diag.datalen; i++) printf(" %02h",diag.data[i])+ printf ("\r\n"); } return(0); } /* X.25 Out-of-Band Signal Handler */ SIGTYP x25oobh(foo) int foo; { int oobtype; u_char oobdata; int t; (VOID) signal(SIGURG,x25oobh); do { if (ioctl(ttyfd,X25_OOB_TYPE,&oobtype)) { perror ("Getting signal type"); return; } switch (oobtype) { case INT_DATA: if (recv(ttyfd,(char *)&oobdata,1,MSG_OOB) < 0) { perror ("Receiving X.25 interrupt data"); return; } t = oobdata; printf ("\r\nInterrupt received, data = %d\r\n", t); break; case VC_RESET: printf ("\r\nVirtual circuit reset\r\n"); x25diag (); break; case N_RESETS: printf ("\r\nReset timeout\r\n"); break; case N_CLEARS: printf ("\r\nClear timeout\r\n"); break; case MSG_TOO_LONG: printf ("\r\nMessage discarded, too long\r\n"); break; default: if (oobtype) printf("\r\nUnknown oob type %d\r\n",oobtype); break; } } while (oobtype); } /* Send a X.25 interrupt packet */ int #ifdef CK_ANSIC x25intr(char intr) #else x25intr(intr) char intr; #endif /* CK_ANSIC */ /* x25intr */ { if (send(ttyfd,&intr,1,MSG_OOB) < 0) return(-1); debug(F100,"X.25 intr","",0); return(0); } /* Reset X.25 virtual circuit */ int #ifdef CK_ANSIC x25reset(char cause, char diagn) #else x25reset(cause, diagn) char cause; char diagn; #endif /* CK_ANSIC */ /* x25reset */ { bzero ((char *)&diag,sizeof(diag)); diag.flags = 0; diag.datalen = 2; diag.data[0] = cause; diag.data[1] = diagn; if (ioctl(ttyfd,X25_WR_CAUSE_DIAG,&diag) < 0) return(-1); debug(F100,"X.25 reset","",0); return(0); } /* Clear X.25 virtual circuit */ int x25clear() { int i; debug(F100,"X.25 clear","",0); bzero ((char *)&diag,sizeof(diag)); diag.flags = (1 << DIAG_TYPE); diag.datalen = 2; diag.data[0] = 0; diag.data[1] = 0; ioctl (ttyfd,X25_WR_CAUSE_DIAG,&diag); /* Send Clear Request */ return(ttclos(0)); /* Close socket */ } /* X.25 status */ int x25stat() { if (ttyfd == -1) return (-1); return(0); } /* Set Q_BIT on */ VOID setqbit() { static int qbiton = 1 << Q_BIT; ioctl (ttyfd,X25_SEND_TYPE,&qbiton); } /* Set Q_BIT off */ VOID resetqbit() { static int qbitoff = 0; ioctl (ttyfd,X25_SEND_TYPE,&qbitoff); } /* Read n characters from X.25 circuit into buf */ int x25xin(n,buf) int n; CHAR *buf; { register int x, c; int qpkt; do { x = read(ttyfd,buf,n); if (buf[0] & (1 << Q_BIT)) { /* If Q_BIT packet, process it */ /* If return -1 : invitation to clear; -2 : PAD changes */ if ((c=qbitpkt(buf+1,x-2)) < 0) return(c); qpkt = 1; } else qpkt = 0; } while (qpkt); #ifdef COMMENT /* Disabled by Stephen Riehm 19.12.97 */ /* BUG! * if buf[] is full, then this null lands in nirvana! * I was unable to find any code which needs a trailing null in buf[] */ if (x > 0) buf[x] = '\0'; #endif /* COMMENT */ if (x < 1) x = -1; debug(F101,"x25xin x","",x); return(x); } #ifdef COMMENT /* NO LONGER NEEDED! */ /* X.25 read a line */ int #ifdef PARSENSE #ifdef CK_ANSIC x25inl(CHAR *dest, int max,int timo, CHAR eol, CHAR start) #else x25inl(dest,max,timo,eol,start) int max,timo; CHAR *dest, eol, start; #endif /* CK_ANSIC */ #else /* not PARSENSE */ #ifdef CK_ANSIC x25inl(CHAR *dest, int max,int timo, CHAR eol) #else x25inl(dest,max,timo,eol) int max,timo; CHAR *dest, eol; #endif /* __SDTC__ */ #endif /*PARSENSE */ /* x25inl */ { CHAR *pdest; int pktype, goteol, rest, n; int i, flag = 0; extern int ttprty, ttpflg; int ttpmsk; ttpmsk = (ttprty) ? 0177 : 0377; /* Set parity stripping mask */ debug(F101,"x25inl max","",max); debug(F101,"x25inl eol","",eol); pdest = dest; rest = max; goteol = 0; do { n = read(ttyfd,pdest,rest); n--; pktype = *pdest & 0x7f; switch (pktype) { case 1 << Q_BIT: if (qbitpkt(pdest+1,--n) < 0) return(-2); break; default: if (flag == 0) { /* if not in packet, search start */ for (i = 1; (i < n) && !(flag = ((dest[i] & 0x7f) == start)); i++); if (flag == 0) { /* not found, discard junk */ debug(F101,"x25inl skipping","",n); continue; } else { /* found, discard junk before start */ int k; n = n - i + 1; for (k = 1; k <= n; k++, i++) dest[k] = dest[i]; } } for (i = 0; (i < n) && /* search for eol */ !(goteol=(((*pdest = *(pdest+1)&ttpmsk)&0x7f)== eol)); i++,pdest++); *pdest = '\0'; rest -= n; } } while ((rest > 0) && (!goteol)); if (goteol) { n = max - rest; debug (F111,"x25inl X.25 got",(char *) dest,n); if (timo) ttimoff(); if (ttpflg++ == 0 && ttprty == 0) { if ((ttprty = parchk(dest,start,n)) > 0) { int j; debug(F101,"x25inl senses parity","",ttprty); debug(F110,"x25inl packet before",(char *)dest,0); ttpmsk = 0x7f; for (j = 0; j < n; j++) dest[j] &= 0x7f; /* Strip parity from packet */ debug(F110,"x25inl packet after ",dest,0); } else { debug(F101,"parchk","",ttprty); if (ttprty < 0) { ttprty = 0; n = -1; } } } ttimoff(); return(n); } ttimoff(); return(-1); } #endif /* COMMENT */ #endif /* SUNX25 */ #ifdef IBMX25 /* * IBM X25 support - using the NPI streams interface * written by Stephen Riehm, pc-plus, Munich Germany */ /* riehm: missing functions / TODO list */ /* x25intr() - Send an interrupt packet */ /* return an error message depending on packet type */ char * x25err(n) int n; { static char buf[30]; switch (n) { case NBADADDR: return "invalid address"; case NBADOPT: return "invalid options"; case NACCESS: return "no permission"; case NNOADDR: return "unable to allocate address"; case NOUTSTATE: return "invalid state"; case NBADSEQ: return "invalid sequence number"; case NSYSERR: return "system error"; case NBADDATA: return "invalid data size"; case NBADFLAG: return "invalid flag"; case NNOTSUPPORT: return "unsupported primitive"; case NBOUND: return "address in use"; case NBADQOSPARAM: return "bad QOS parameters"; case NBADQOSTYPE: return "bad QOS type"; case NBADTOKEN: return "bad token value"; case NNOPROTOID: return "protocol id could not be allocated"; case NODDCUD: return "odd length call user data"; default: ckmakmsg(buf,sizeof(buf),"Unknown NPI error ",ckitoa(n),NULL,NULL); return buf; } } /* turn a meaningless primitive number into a meaningful primitive name */ char * x25prim(n) int n; { static char buf[30]; switch(n) { case N_BIND_ACK: return "N_BIND_ACK"; case N_BIND_REQ: return "N_BIND_REQ"; case N_CONN_CON: return "N_CONN_CON"; case N_CONN_IND: return "N_CONN_IND"; case N_CONN_REQ: return "N_CONN_REQ"; case N_CONN_RES: return "N_CONN_RES"; case N_DATACK_IND: return "N_DATAACK_IND"; case N_DATACK_REQ: return "N_DATAACK_REQ"; case N_DATA_IND: return "N_DATA_IND"; case N_DATA_REQ: return "N_DATA_REQ"; case N_DISCON_IND: return "N_DISCON_IND"; case N_DISCON_REQ: return "N_DISCON_REQ"; case N_ERROR_ACK: return "N_ERROR_ACK"; case N_EXDATA_IND: return "N_EXDATA_IND"; case N_EXDATA_REQ: return "N_EXDATA_REQ"; case N_INFO_ACK: return "N_INFO_ACK"; case N_INFO_REQ: return "N_INFO_REQ"; case N_OK_ACK: return "N_OK_ACK"; case N_OPTMGMT_REQ: return "N_OPTMGMT_REQ"; case N_RESET_CON: return "N_RESET_CON"; case N_RESET_IND: return "N_RESET_IND"; case N_RESET_REQ: return "N_RESET_REQ"; case N_RESET_RES: return "N_RESET_RES"; case N_UDERROR_IND: return "N_UDERROR_IND"; case N_UNBIND_REQ: return "N_UNBIND_REQ"; case N_UNITDATA_REQ: return "N_UNITDATA_REQ"; case N_UNITDATA_IND: return "N_UNITDATA_IND"; default: ckmakmsg(buf,sizeof(buf),"UNKNOWN (",ckitoa(n),")",NULL); return buf; } } /***************************************************************************** * Function: x25getmsg() * Description: get a STREAMS message, and check it for errors * * Parameters: * fd - file descriptor to x25 device (opened) * control - control buffer (pre-allocated) * ctl_size - size of control buffer * data - data buffer (pre-allocated) * data_size - size of data buffer * flags - flags for getmsg() * expected - expected Primitive type * * Return Value: * >= 0 OK (size of data returned) * -1 error * */ int x25getmsg( fd, control, ctl_size, data, data_size, get_flags, expected ) int fd; /* X25 device (opened) */ N_npi_ctl_t *control; /* control buffer (pre-allocated) */ int ctl_size; /* size of control buffer */ N_npi_data_t *data; /* data buffer (pre-allocated) */ int data_size; /* size of data buffer */ int *get_flags; /* getmsg() flags */ int expected; /* expected primitive type */ /* x25getmsg */ { int rc = 0; /* return code */ struct strbuf *get_ctl=NULL; /* getmsg control */ struct strbuf *get_data=NULL; /* getmsg data */ int more = 0; /* flag for more data etc */ int file_status = -1; /* file async status */ N_npi_ctl_t * result; /* pointer to simplify switch() */ int packet_type = -1; /* unknown packet thus far */ #ifdef TRACE printf( "TRACE: entering x25getmsg\n" ); #endif /* TRACE */ debug( F110, "x25getmsg waiting for packet ", x25prim( expected ), 0); /* prepare the control structures for getmsg */ if (control) { if ((get_ctl = (struct strbuf*)malloc(sizeof(struct strbuf))) == NULL) { perror("kermit x25getmsg(): get_ctl malloc failed\n"); debug( F100, "x25getmsg malloc failed for get_ctl\n", "", 0); return(-1); } /* allow getmsg to return an unexpected packet type (which may be * larger than the expected one) */ get_ctl->maxlen = NPI_MAX_CTL; get_ctl->len = 0; get_ctl->buf = (char *)control; } else { printf( "kermit x25getmsg(): internal error. control buffer MUST be pre-allocated!\n" ); debug(F100,"x25getmsg internal error. no buffer pre-allocated","",0); return( -1 ); } if (data) { if ((get_data = (struct strbuf*)malloc(sizeof(struct strbuf))) == NULL) { perror("kermit x25getmsg(): get_data malloc failed\n"); debug( F100, "x25getmsg malloc failed for get_data\n", "", 0); return(-1); } get_data->maxlen = (NPI_MAX_DATA < data_size ) ? NPI_MAX_DATA : data_size; get_data->len = 0; get_data->buf = (char *)data; } /* get an X.25 packet - * it may be any kind of packet, so check for special cases * it may be split into multiple parts - so loop if necessary */ do { #ifdef DEBUG printf( "kermit: x25getmsg(): getting a message\n" ); #endif /* DEBUG */ errno = 0; if ((more = getmsg(fd, get_ctl, get_data, get_flags)) < 0) { #ifdef DEBUG printf( "kermit: x25getmsg(): getmsg returned an error\n" ); perror( "getmsg error was" ); #endif /* DEBUG */ debug(F101, "x25getmsg getmsg returned an error\n", "", errno); if ((errno == EAGAIN) && (get_data && (get_data->len > 0)) ) { /* was in non-blocking mode, nothing to get, but we're * already waiting for the rest of the packet - * switch to blocking mode for the next read. * file_status used to reset file status before returning */ if ((file_status = fcntl(fd, F_GETFL, 0)) < 0 || fcntl(fd, F_SETFL, file_status & ~O_NDELAY) < 0) { perror("x25getmsg(): couldn't change x25 blocking mode"); debug(F101, "x25getmsg fcntl returned an error\n", "", errno); /* netclos(); */ rc = -1; break; } else { /* loop again into a blocking getmsg() */ continue; } } else { /* no data to get in non-blocking mode - return empty handed */ perror( "x25getmsg(): getmsg failed" ); debug(F101,"x25getmsg getmsg returned an error\n", "", errno); rc = -1; break; } } else if (more & MORECTL) { /* panic - the control information was larger than the * maximum control buffer size! */ /* riehm: close connection? */ #ifdef DEBUG printf("x25getmsg(): received partial control packet - panic\n"); #endif /* DEBUG */ debug( F101, "x25getmsg getmsg bad control block\n", "", errno); rc = -1; break; } if (result = (N_npi_ctl_t *)control) { packet_type = result->bind_ack.PRIM_type; if (packet_type != N_OK_ACK) { x25lastmsg = packet_type; } } #ifdef DEBUG /* printf( "kermit: x25getmsg(): getting " ); */ if (get_ctl->len > 0) { x25dump_prim(result); } debug(F110, "x25getmsg got packet ", x25prim( result->bind_ack.PRIM_type ), 0 ); #endif /* DEBUG */ if (get_ctl->len >= (int)sizeof(result->bind_ack.PRIM_type)) { /* not as pretty as a switch(), but switch can't handle * runtime variable values :-( */ if (packet_type == expected ) { /* got what we wanted, special case for DATA_IND * packets though */ /* riehm: check Q-bit ? */ #ifdef DEBUG printf("x25getmsg(): got expected packet\nrc is %d\n", rc); #endif /* DEBUG */ if (packet_type == N_DATA_IND ) { /* data received. May be incomplete, even though * getmsg returned OK */ if (result->data_ind.DATA_xfer_flags & N_MORE_DATA_FLAG) more |= MOREDATA; if (result->data_ind.DATA_xfer_flags & N_RC_FLAG) printf( "x25getmsg(): data packet wants ack\n" ); } } else if( packet_type == N_DISCON_IND) { printf( "X25 diconnected\n" ); /* riehm: need to acknowledge a disconnection? */ x25clear(); /* x25unbind( ttyfd ); */ rc = -1; } else if( packet_type == N_ERROR_ACK) { errno = result->error_ack.UNIX_error; perror( "X25 error received" ); rc = -1; } else { printf("x25getmsg(): failed %s\n", x25err(packet_type)); rc = -1; } } #ifdef COMMENT else { /* Panic - no control data */ printf( "kermit: x25getmsg(): no control data with packet\n" ); rc = -1; } #endif /* COMMENT */ if (get_data && (get_data->len >= 0)) { get_data->buf += get_data->len; get_data->maxlen -= get_data->len; } } while ((rc == 0) && (get_data && (get_data->maxlen > 0)) && (more & MOREDATA) ); /* return the file status to its original value, unless its still * set to -1, or one of the fcntl's failed */ if ((file_status >= 0) && fcntl(fd, F_SETFL, file_status) < 0) rc = -1; /* * Verify that we received an expected primitive * there is apparantly an error case where the primitive is set * correctly, but there is not enough data in the control structure */ if ((packet_type != expected) && (get_ctl->len >= ctl_size) ) { fprintf(stderr, "x25getmsg(): %s NOT received. Primitive received was %s\n", x25prim( expected ), x25prim( packet_type )); debug(F110, "x25getmsg got an unexpected packet ", x25prim(packet_type), 0 ); rc = -1; } if (rc == 0) { if (get_data && ( get_data->len >= 0)) { rc = get_data->len; } } if (get_ctl) { free(get_ctl); get_ctl = NULL; } if (get_data) { free(get_data); get_data = NULL; } #ifdef COMMENT #ifdef DEBUG printf( "kermit x25getmsg(): returning %d\n", rc ); #endif /* DEBUG */ #endif /* COMMENT */ debug(F110, "x25getmsg returning packet ", x25prim( packet_type ), 0); #ifdef TRACE printf( "TRACE: leaving x25getmsg\n" ); #endif /* TRACE */ return(rc); } /***************************************************************************** * Function: x25putmsg() * * Description: * send a message to a X25 STREAM * * Parameters: * fd - file descriptor to x25 device (opened) * control - control buffer (pre-allocated) * data - data buffer (pre-allocated) * data_len - length of data to be transmitted * put_flags - flags for putmsg() * * Return Value: * >= 0 number of bytes transmitted * -1 error */ int x25putmsg(fd, control, data, data_len, put_flags) int fd; /* X25 device (opened) */ N_npi_ctl_t *control; /* control buffer (pre-allocated) */ N_npi_data_t *data; /* data buffer (pre-allocated) */ int data_len; /* length of data (not the size of the buffer) */ int *put_flags; /* putmsg() flags */ /* x25putmsg */ { int rc = 0; /* return code */ ulong type; /* primitive type */ struct strbuf *put_ctl = NULL; /* putmsg control */ struct strbuf *put_data = NULL; /* putmsg data */ #ifdef TRACE printf( "TRACE: entering x25putmsg\n" ); #endif /* TRACE */ #ifdef DEBUG printf( "kermit: x25putmsg(): putting " ); x25dump_prim( control ); printf( "\tdata:\t\t" ); x25dump_data( data, 0, data_len ); debug(F110,"x25putmsg: putting packet ",x25prim(control->PRIM_type),0); #endif /* DEBUG */ if (control) { put_ctl = (struct strbuf *)malloc( sizeof( struct strbuf ) ); if (put_ctl == NULL) { perror("kermit x25putmsg(): put_ctl malloc failed\n"); return(-1); } put_ctl->maxlen = 0; /* unused by putmsg */ put_ctl->len = NPI_MAX_CTL; put_ctl->buf = (char *)control; } if (data && ( data_len > 0)) { put_data = (struct strbuf *)malloc( sizeof( struct strbuf ) ); if( put_data == NULL) { perror("kermit x25putmsg(): put_data malloc failed\n"); return(-1); } put_data->maxlen = 0; /* unused by putmsg */ put_data->len = data_len; put_data->buf = (char *)data; } errno = 0; rc = putmsg (fd, put_ctl, put_data, 0); if (rc < 0) { printf("x25putmsg(): couldn't put %s\n",x25prim(control->PRIM_type)); perror("kermit: x25putmsg(): putmsg failed"); return(-1); } /* riehm: this should perhaps be discounted! */ x25lastmsg = control->PRIM_type; #ifdef COMMENT #ifdef DEBUG printf( "kermit debug: x25putmsg() returning %d\n", data_len ); #endif /* DEBUG */ #endif /* COMMENT */ debug( F101, "x25putmsg block size put ", "", data_len); #ifdef TRACE printf( "TRACE: leaving x25putmsg\n" ); #endif /* TRACE */ return( data_len ); } /***************************************************************************** * Function: x25bind * Description: The bind submitted to NPI provides the information required * by the packet layer for it to listen for suitable incoming * calls. * * WARNING: * * This routine needs to be called in a completely different manner for * the client and server side. When starting a client, the * num_waiting_calls and CUD information should all be set to 0! The * client's CUD must be inserted in the CONN_REQ data block. * When starting a server, the CUD must be set to a CUD pattern, and * the number of waiting calls should be set to a number other than 0. * (num waiting calls is the number of incomming calls which are to be * put on hold while the server is servicing another client.) * * Who invented this crap? * * Parameters: * fd - X25 device (opened) * addr - local address * cud - User Data (null terminated) * cud_len - User Data length * num_waiting_calls - number of outstanding calls allowed on this stream * line - logical port number (1) * flags - 0, DEFAULT_LISTENER or TOKEN_REQUEST * * Return Value: * if binding is successful, 0 is returned for a client, and a token is * returned for a server * * Return code: 0 if successful * -1 if unsuccessful *****************************************************************************/ ulong x25bind(fd, addr, cud, cud_len, num_waiting_calls, line, bind_flags) int fd; /* X25 device (opened) */ char * addr; /* local address */ char * cud; /* Call User Data (null terminated) */ int cud_len; /* User Data length */ int num_waiting_calls; /* Outstanding calls allowed */ int line; /* logical port number */ ulong bind_flags; /* 0, DEFAULT_LISTENER or TOKEN_REQUEST */ /* x25bind */ { ulong rc; /* return code */ int get_flags; /* priority flag passed to getmsg */ int put_flags = 0; /* output flags for putmsg, always 0 */ ulong type; /* primitive type */ N_bind_req_t *bind_req; /* pointer to N_BIND_REQ primitive */ N_bind_ack_t *bind_ack; /* pointer to N_BIND_ACK primitive */ char *addtl_info; /* pointer to info in addition to * the N_BIND_REQ primitive that is * passed in the control structure * to putmsg */ int addr_len = 0; /* length of address string */ ulong bind_req_t_size; /* for debugging only */ #ifdef TRACE printf("TRACE: entering x25bind\n" ); #endif /* TRACE */ #ifdef DEBUG printf("TRACE: x25bind( %d, %s, %s, %d, %d )\n", fd, addr, cud, line, bind_flags ); #endif /* DEBUG */ /* * Allocate and zero out space to hold the control portion of the * message passed to putmsg. This will contain the N_BIND_REQ * primitive and any additional info required for that. * * Note: allocated space is the size of the union typedef * N_npi_ctl_t to allow the use fo the generic x25putmsg routine. */ bind_req = (N_bind_req_t *) malloc(sizeof( N_npi_ctl_t)); if (bind_req == NULL) { perror("kermit: x25bind(): bind_req malloc failed"); debug(F100, "x25bind bind_req malloc failed", "", 0); return(-1); } bzero((char *)bind_req, sizeof(N_npi_ctl_t)); /* Build the Bind Request Primitive */ bind_req->PRIM_type = (ulong) N_BIND_REQ; /* Note that the address length is n+2 and NOT n. Two bytes MUST preceed * the actual address in an N_BIND_REQ. The first byte contains the * line number being used with this address, and the second byte is the * X.121 address prefix, which must be zero. */ addr_len = strlen(addr); bind_req->ADDR_length = (ulong) (addr_len + 2); bind_req->ADDR_offset = (ulong)(sizeof(N_bind_req_t)); bind_req->CONIND_number = (ulong)num_waiting_calls; /* server only */ bind_req->BIND_flags = (ulong) bind_flags; /* 0 in client */ bind_req->PROTOID_length = (ulong) cud_len; /* 0 in client */ if (cud_len == 0) { bind_req->PROTOID_offset = (ulong) 0; } else { /* need to remember the trailing NULL in the address - not * counted in the address length */ bind_req->PROTOID_offset = (ulong) (sizeof(N_bind_req_t) + bind_req->ADDR_length); } /* * Now fill in the additional information required with this primitive * (address and protocol information (Call User Data)) */ addtl_info = (char *) ((void *)bind_req + bind_req->ADDR_offset); /* * The bitwise "&" ensures that the line number is only one byte long */ *addtl_info++ = (char) line & 0xff; *addtl_info++ = (char) 0; /* X.121 format */ bcopy( addr, addtl_info, addr_len ); /* include trailing null */ addtl_info += addr_len; if (cud_len > 0) bcopy( cud, addtl_info, cud_len ); /* * Call putmsg() to put the bind request message on the stream */ if (x25putmsg(fd, (N_npi_ctl_t*)bind_req, (N_npi_data_t *)NULL, 0, &put_flags ) < 0) { printf( "kermit: x25bind(): x25putmsg failed\n" ); return(-1); } /* * Allocate and zero out space for the N_BIND_ACK primitive */ bind_ack = (N_bind_ack_t *) malloc(sizeof(N_npi_ctl_t)); if (bind_ack == NULL){ perror("kermit: x25bind(): bind_ack malloc failed"); return(-1); } bzero(bind_ack, sizeof(N_npi_ctl_t)); /* * Initialize the control structure and flag variable sent to getmsg */ get_flags=0; /* get the ACK for the bind */ #ifdef DEBUG printf( "kermit: x25bind() trying to get a BIND_ACK\n" ); #endif /* DEBUG */ rc = (ulong)x25getmsg( fd, (N_npi_ctl_t*)bind_ack, (int)sizeof( N_bind_ack_t ), (N_npi_data_t*)NULL, 0, &get_flags, N_BIND_ACK ); /* turn quantitive return code into a qualitative one */ if (rc > 0) rc = 0; /* if all went well, get the token from the acknowledgement packet */ if ((bind_flags & TOKEN_REQUEST ) && ( rc >= 0)) { rc = bind_ack->TOKEN_value; } /* free up the memory we allocated earlier */ free(bind_req); free(bind_ack); #ifdef TRACE printf( "TRACE: leaving x25bind\n" ); #endif /* TRACE */ return( rc ); } /***************************************************************************** * Function: x25call * Description: This routine builds and sends an N_CONN_REQ primitive, then * checks for an N_CONN_CON primitive in return. * * Parameters: * fd - file descriptor of stream * caddr - called address (remote address) * * Functions Referenced: * malloc() * bzero() * getmsg() * putmsg() * * Return code: * 0 - if successful * -1 if not successful *****************************************************************************/ int x25call(fd, remote_nua, cud) int fd; /* X25 device (opened) */ char * remote_nua; /* remote address to call */ char * cud; /* call user data */ /* x25call */ { int rc; /* return code */ int flags; /* Connection flags */ int get_flags; /* priority flags for getmsg */ ulong type; /* primitive type */ N_conn_req_t *connreq_ctl; /* pointer to N_CONN_REQ primitive */ N_npi_data_t *connreq_data; /* pointer to N_CONN_REQ data (CUD) */ int connreq_data_len; /* length of filled data buffer */ N_conn_con_t *conncon_ctl; /* pointer to N_CONN_CON primitive */ N_npi_data_t *conncon_data; /* pointer to any data associated with * the N_CONN_CON primitive */ char *addtl_info; /* pointer to additional info needed * for N_CONN_REQ primitive */ int addr_len; /* length of address */ #ifdef TRACE printf( "TRACE: entering x25call\n" ); #endif /* TRACE */ #ifdef DEBUG printf( "x25call( %d, %s )\n", fd, remote_nua ); printf( "connecting to %s on fd %d\n", remote_nua, fd ); #endif /* DEBUG */ /* * Allocate and zero out space for the N_CONN_REQ primitive * use the size of the generic NPI primitive control buffer */ connreq_ctl = (N_conn_req_t *) malloc(sizeof(N_npi_ctl_t)); if (connreq_ctl == NULL){ perror("kermit: x25call(): connreq_ctl malloc failed"); return(-1); } bzero(connreq_ctl,sizeof(N_npi_ctl_t)); /* * Build the Connection Request Primitive */ flags = 0; connreq_ctl->PRIM_type = (ulong) N_CONN_REQ; /* Note that the address length is nchai+1 and not n+2. The line number * is only passed with the address for the bind. The first byte of * the address for the N_CONN primitives contains the X.121 * address prefix, which must be zero. The remaining bytes are the * address itself. */ addr_len = strlen( remote_nua ); connreq_ctl->DEST_length = (ulong) (addr_len + 1); connreq_ctl->DEST_offset = (ulong) sizeof(N_conn_req_t); /* connreq_ctl->CONN_flags = (ulong)EX_DATA_OPT | REC_CONF_OPT; */ connreq_ctl->CONN_flags = (ulong) 0; connreq_ctl->QOS_length = (ulong) 0; /* unsupported in AIX 4.1 */ connreq_ctl->QOS_offset = (ulong) 0; /* unsupported in AIX 4.1 */ addtl_info = (char *) ((void*)connreq_ctl + connreq_ctl->DEST_offset); *addtl_info++ = (char) 0; /* X.121 format */ bcopy( remote_nua, addtl_info, addr_len ); /* * setup the data buffer for the connection request */ connreq_data = (N_npi_data_t *) malloc(sizeof(N_npi_data_t)); if (connreq_data == NULL){ perror("kermit: x25call(): connreq_data malloc failed"); return(-1); } bzero(connreq_data,sizeof(N_npi_data_t)); /* facility selection needs to be put in the front of connreq_data */ connreq_data_len = 0; connreq_data_len += x25facilities( (char *)connreq_data ); if (cud && *cud) { bcopy(cud, (char *)((char *)connreq_data + connreq_data_len), strlen(cud) ); connreq_data_len += strlen( cud ); } /* * Call putmsg() to put the connection request message on the stream */ rc = x25putmsg( fd, (N_npi_ctl_t*)connreq_ctl, connreq_data, connreq_data_len, &flags ); if (rc < 0) { return(-1); } /* * Allocate and zero out space for the N_CONN_CON primitive */ if ((conncon_ctl = (N_conn_con_t *) malloc(sizeof(N_npi_ctl_t))) == NULL) { perror("kermit: x25call(): conncon_ctl malloc failed"); return(-1); } bzero(conncon_ctl, sizeof(N_npi_ctl_t)); /* * Allocate and zero out space for any data associated with N_CONN_CON */ if ( (conncon_data = (N_npi_data_t *) malloc(NPI_MAX_DATA)) == NULL) { perror("kermit: x25call(): conncon_data malloc failed"); return(-1); } bzero(conncon_data, NPI_MAX_DATA); /* Initialize and build the structures for getmsg */ get_flags=0; rc = x25getmsg( fd, (N_npi_ctl_t*)conncon_ctl, (int)sizeof( N_conn_con_t ), conncon_data, NPI_MAX_DATA, &get_flags, N_CONN_CON ); /* turn quantitive return code into a qualitative one */ if (rc > 0) rc = 0; /* Free the space that we no longer need */ if (connreq_ctl) { free(connreq_ctl); connreq_ctl = NULL; } if (conncon_ctl) { free(conncon_ctl); conncon_ctl = NULL; } if (conncon_data) { free(conncon_data); conncon_data = NULL; } #ifdef TRACE printf( "TRACE: leaving x25call\n" ); #endif /* TRACE */ return(rc); } /***************************************************************************** * Function: x25getcall * * Description: This routine checks for an incomming call, verified * that it is a CONNIND (connection indication) message, and then * accepts the call and returns the file descriptor of the new stream * * Parameters: * fd - file descriptor of listening stream * * Return Codes: * callfd - file descriptor of connected incomming call. * - set to -1 if an error occured * *****************************************************************************/ int x25getcall(fd) int fd; { int x25callfd; /* fd of incomming call */ N_conn_ind_t *connind_ctl; /* connind controll buffer */ N_npi_data_t *connind_data; /* connind data buffer */ int get_flags; /* flags for getmsg */ ulong flags; /* connection flags */ int rc; /* return code */ extern x25addr_t remote_nua; /* remote X.25 addr global var */ #ifdef TRACE printf( "TRACE: entering x25getcall\n" ); #endif /* TRACE */ /* allocate space for connection indication buffers */ if ((connind_ctl = (N_conn_ind_t *)malloc(sizeof(N_npi_ctl_t))) == NULL) { perror("kermit: x25getcall(): connind_ctl malloc failed"); return (-1); } bzero(connind_ctl, sizeof(N_npi_ctl_t)); if ((connind_data = (N_npi_data_t *)malloc(NPI_MAX_DATA)) == NULL) { perror("kermit: x25getcall(): connind_data malloc failed"); return (-1); } bzero(connind_data, NPI_MAX_DATA); /* initialise control structures */ get_flags = 0; /* call getmsg to check for a connection indication */ if (x25getmsg(fd, (N_npi_ctl_t*)connind_ctl, (int)sizeof(N_conn_ind_t), connind_data, NPI_MAX_DATA, &get_flags, N_CONN_IND ) < 0) { #ifdef DEBUG printf( "x25getcall(): errno is: %d\n", errno ); #endif /* DEBUG */ perror ("x25getcall(): getmsg failed"); return(-1); } /* a connection indication was received * - pull it to bits and answer the call */ x25seqno = connind_ctl->SEQ_number; flags = connind_ctl->CONN_flags; #ifdef DEBUG printf( "setting remote_nua to a new value due to incomming call\n" ); #endif /* DEBUG */ /* * no guarantee that the address is null terminated, ensure that * after copying that it is (assumption: remote_nua is longer than * the address + 1) */ bzero(remote_nua, sizeof(remote_nua)); /* note: connind_ctl contains a x121 address, which has a null as * the FIRST character - strip it off! */ ckstrncpy(remote_nua, (char*)((char*)connind_ctl + connind_ctl->SRC_offset + 1), connind_ctl->SRC_length - 1 ); #ifdef DEBUG printf( "remote_nua set to new value of %s\n", remote_nua ); #endif /* DEBUG */ /* errors handled by callee */ x25callfd = x25accept(x25seqno, flags); /* free the malloc'd buffers */ if (connind_ctl) { free(connind_ctl); connind_ctl = NULL; } if (connind_data) { free(connind_data); connind_data = NULL; } #ifdef TRACE printf( "TRACE: leaving x25getcall\n" ); #endif /* TRACE */ /* return the file descriptor (or error if < 0) */ return( x25callfd ); } /***************************************************************************** * Function: x25accept * * Description: accept an incomming call * This essentially means opening a new STREAM and sending * an acknowledge back to the caller. * * Parameters: * seqno - sequence number for acknowledgement * flags - flags passed to us by the caller * * Return Codes: * fd - file descriptor of new STREAM * set to -1 if an error occured * *****************************************************************************/ int x25accept(seqno,flags) ulong seqno; /* connection sequence number */ ulong flags; /* connection flags */ /* x25accept */ { int x25callfd; /* fd for incomming call */ int get_flags; /* priority flags for getmsg */ int put_flags = 0; /* flags for putmsg, always 0 */ int addr_len; /* length of local address */ ulong token; /* connection token */ N_conn_res_t *conn_res; /* N_CONN_RES primitive */ N_ok_ack_t *ok_ack; /* N_OK_ACK primitive */ char *addtl_info; /* temp pointer */ int rc; /* temporary return code */ /* global variables from ckcmai.c */ extern int revcall, closgr, cudata; extern char udata[]; extern x25addr_t local_nua; /* local X.25 address */ extern char x25name[]; /* x25 device name (sx25a0) */ extern char x25dev[]; /* x25 device file /dev/x25pkt */ extern int x25port; /* logical port to use */ ulong bind_flags = 0; /* flags for binding the X25 stream */ #ifdef TRACE printf( "TRACE: entering x25accept\n" ); #endif /* TRACE */ /* open a new packet level stream */ if ((x25callfd = open(x25dev, O_RDWR)) < 0) { perror ("kermit: x25accept(): X.25 device open error"); debug(F101,"x25accept() device open error","",errno); return(-1); } /* push the NPI onto the STREAM */ if (ioctl(x25callfd,I_PUSH,"npi") < 0) { perror( "kermit: x25accept(): couldn't push npi on the X25 stream" ); debug(F101,"x25accept can't push npi on the X25 stream","",errno); return (-1); } /* bind kermit server to the local X25 address */ /* taken from /usr/samples/sx25/npi/npiserver.c (AIX 4) */ bind_flags |= TOKEN_REQUEST; token = x25bind(x25callfd,local_nua,(char *)NULL,0,0,x25port,bind_flags); if (token < 0) { printf( "kermit: x25accept(): couldn't bind to local X25 address\n" ); netclos(); return(-1); } /* allocate connection response primitive */ if ((conn_res = (N_conn_res_t *)malloc( NPI_MAX_CTL )) == NULL) { perror("kermit: x25accept(): conn_res malloc failed"); return (-1); } bzero((char *)conn_res, NPI_MAX_CTL); /* setup connection response primitive */ addr_len = strlen( local_nua ); conn_res->PRIM_type = (ulong)N_CONN_RES; conn_res->TOKEN_value = token; /* note address length is n+1 to accomodate the X.121 address prefix */ conn_res->RES_length = (ulong)(addr_len + 1); conn_res->RES_offset = (ulong)sizeof( N_conn_res_t ); conn_res->SEQ_number = seqno; conn_res->CONN_flags = 0; conn_res->QOS_length = 0; /* unsupported - must be 0 (!?) */ conn_res->QOS_offset = 0; addtl_info = (char *)((char *)conn_res + conn_res->RES_offset); *addtl_info++ = (char)0; /* X.121 address prefix */ bcopy( local_nua, addtl_info, addr_len ); /* * send off the connect response */ if (x25putmsg(x25callfd, (N_npi_ctl_t*)conn_res, (N_npi_data_t *)NULL, 0, &put_flags ) < 0 ) { perror("kermit: x25accept(): putmsg connect response failed"); return(-1); } /* * Allocate and zero out space for the OK_ACK primitive */ if ((ok_ack = (N_ok_ack_t *) malloc(sizeof(N_npi_ctl_t))) == NULL) { perror("kermit: x25call(): ok_ack malloc failed"); return(-1); } bzero(ok_ack, sizeof(N_npi_ctl_t)); /* Initialize and build the structures for getmsg */ get_flags=0; rc = (int)x25getmsg(x25callfd, (N_npi_ctl_t*)ok_ack, (int)sizeof(N_ok_ack_t), (N_npi_data_t*)NULL, 0, &get_flags, N_OK_ACK ); if (rc == 0) { /* sequence number is only for disconnecting when not connected !? */ x25seqno = 0; } /* free up malloc'ed buffer space */ if (conn_res) { free(conn_res); conn_res = NULL; } if (ok_ack) { free(ok_ack); ok_ack = NULL; } #ifdef TRACE printf( "TRACE: leaving x25accept\n" ); #endif /* TRACE */ return( ( rc >= 0 ) ? x25callfd : -1 ); } /***************************************************************************** * Function: x25unbind * * Description: This subroutine builds and sends an unbind request and gets * the acknowledgement for it. * * Parameters: * fd - File descriptor of the stream * * Functions Referenced: * getmsg() * putmsg() * malloc() * bzero() * * Return code: * 0 - if successful * -1 - if not successful *****************************************************************************/ int x25unbind(fd) int fd; { /* X25 device (opened) */ int rc; /* return code */ int flags; /* bind flags */ int get_flags; /* priority flag for getmsg */ ulong type; /* primitive type */ N_unbind_req_t *unbind_req; /* pointer to N_UNBIND_REQ */ N_ok_ack_t *ok_ack; /* pointer to N_OK_ACK */ #ifdef TRACE printf( "TRACE: entering x25unbind\n" ); #endif /* TRACE */ #ifdef DEBUG /* printf( "x25unbind( %d )\n", fd ); */ #endif /* DEBUG */ debug(F101,"x25unbind closing x25 connection #","",fd); /* Allocate and zero out space to hold the N_UNBIND_REQ primitive */ unbind_req = (N_unbind_req_t *) malloc(sizeof(N_npi_ctl_t)); if (unbind_req == NULL) { perror("kermit: x25unbind(): unbind_req malloc failed"); return(-1); } bzero(unbind_req, sizeof(N_npi_ctl_t)); /* * Build the Unbind Request Primitive */ flags = 0; unbind_req->PRIM_type = (ulong) N_UNBIND_REQ; /* * Call putmsg() to put the bind request message on the stream */ if (x25putmsg(fd, (N_npi_ctl_t*)unbind_req, (N_npi_data_t *)NULL, 0, &flags ) < 0) { perror ("kermit: x25unbind(): putmsg failed"); return(-1); } /* Allocate and Zero out space for the N_OK_ACK primitive */ ok_ack = (N_ok_ack_t *) malloc(sizeof(N_npi_ctl_t)); if (ok_ack == NULL) { perror("kermit x25unbind(): ok_ack malloc failed\n"); return(-1); } bzero(ok_ack, sizeof(N_npi_ctl_t)); /* Initialize and build the control structure for getmsg */ get_flags=0; /* Call getmsg() to check for an acknowledgement */ rc = x25getmsg(fd, (N_npi_ctl_t*)ok_ack, (int)sizeof(N_ok_ack_t), (N_npi_data_t*)NULL, 0, &get_flags, N_OK_ACK ); if (rc < 0) { perror ("kermit: x25unbind: getmsg failed"); return(-1); } /* Free up the space that we no longer need */ if (unbind_req) { free(unbind_req); unbind_req = NULL; } if (ok_ack) { free(ok_ack); ok_ack = NULL; } #ifdef TRACE printf( "TRACE: leaving x25unbind\n" ); #endif /* TRACE */ return(0); } /***************************************************************************** * Function: x25xin * * Description: * Read n characters from X.25 circuit into buf (AIX only) * * Parameters: * data_buf_len maximum size of data buffer * data_buf pointer to pre-allocated buffer space * * Return Value: * the number of characters actually read */ int x25xin(data_buf_len,data_buf) int data_buf_len; CHAR *data_buf; { struct strbuf getmsg_ctl; /* streams control structure */ struct strbuf getmsg_data; /* streams data structure */ int rc = 0; /* return code */ int getmsg_flags; /* packet priority flags */ char * ctl_buf; /* npi control buffer */ N_npi_ctl_t * result; /* pointer to simplify switch() */ #ifdef TRACE printf( "TRACE: entering x25xin\n" ); #endif /* TRACE */ /* ensure that no maximum's are overridden */ data_buf_len = (NPI_MAX_DATA < data_buf_len) ? NPI_MAX_DATA : data_buf_len; /* allocate space for packet control info */ if ((ctl_buf = (char *)malloc(NPI_MAX_CTL)) == NULL) { perror( "kermit: x25xin(): ctl_buf malloc" ); return(-1); } #ifdef COMMENT /* riehm: need zeroed buffer for getmsg? */ bzero( ctl_buf, NPI_MAX_CTL ); /* clear data buffer */ bzero( data_buf, data_buf_len ); #endif /* COMMENT */ getmsg_flags = 0; /* get the first packet available */ rc = x25getmsg(ttyfd, ctl_buf, NPI_MAX_CTL, data_buf, data_buf_len, &getmsg_flags, N_DATA_IND ); #ifdef COMMENT #ifdef DEBUG if (rc >= 0) { printf( "kermit: x25xin(): got " ); x25dump_data( data_buf, 0, rc ); } else { printf( "x25xin(): attempt to get data resulted in an error\n" ); } #endif /* DEBUG */ #endif /* COMMENT */ /* free buffers */ if (ctl_buf) { free(ctl_buf); ctl_buf = NULL; } #ifdef TRACE printf( "TRACE: leaving x25xi\n" ); #endif /* TRACE */ return(rc); } /***************************************************************************** * Function: x25write * * Description: * write a block of characters to the X25 STREAM (AIX) * * Parameters: * fd file descriptor to write to * databuf buffer containing data to write * databufsize size of the buffer to write * * Return Value: * size the number of bytes actually transmitted */ int x25write(fd, databuf, databufsize) int fd; /* X25 STREAMS file descriptor (ttyfd) */ char *databuf; /* buffer to write */ int databufsize; /* buffer size */ /* x25write */ { N_data_req_t *data_req_ctl; int rc; /* return code (size transmitted) */ int write_flags = 0; /* always 0 !? */ #ifdef TRACE printf( "TRACE: entering x25write\n" ); #endif /* TRACE */ if ((data_req_ctl = (N_data_req_t *)malloc(NPI_MAX_CTL) ) == NULL) { perror( "kermit: x25write(): data_req_ctl malloc" ); return(-1); } data_req_ctl->PRIM_type = N_DATA_REQ; data_req_ctl->DATA_xfer_flags = 0; /* riehm: possible extension * possibly need to think about splitting up the data buffer * into multiple parts if databufsize > NPI_MAX_DATA */ #ifdef COMMENT #ifdef DEBUG printf( "kermit: x25write(): writing data to x25 stream\n" ); printf( "\tdata:\t" ); x25dump_data(databuf, 0, databufsize); #endif /* DEBUG */ #endif /* COMMENT */ rc = x25putmsg(fd, (N_npi_ctl_t*)data_req_ctl, (N_npi_data_t*)databuf, databufsize, &write_flags ); if (data_req) { free(data_req_ctl); data_req = NULL; } #ifdef TRACE printf( "TRACE: leaving x25write\n" ); #endif /* TRACE */ return(rc); } /***************************************************************************** * Function: x25local_nua * * Description: * This routine is only interesting for IBM computers. In order * to set up a connection (see x25bind()) you need to know the * local NUA (x25 address). Unfortunately, you need all this code * to find that out, I just hope this works for everyone else! * * Parameters: * a pre-allocated character buffer, long enough to hold an X.25 address * and the tailing null. * * Return Value: * the length of the address string. * 0 = error */ int x25local_nua(char *buf) { struct CuAt *response; /* structure to fill with info from ODM */ CLASS_SYMBOL retClass; /* ODM class */ char query[64]; /* odm database query */ int rc = 0; /* return value (length of local NUA) */ extern char x25name[]; /* x25 device name (sx25a0) */ #ifdef TRACE printf( "TRACE: entering x25local_nua\n" ); #endif /* TRACE */ /* set up query string */ if (x25name[0] == '\0') { #ifdef DEBUG printf( "kermit: x25local_nua(): No x25 device set, trying sx25a0\n" ); #endif /* DEBUG */ strcpy( x25name, "sx25a0" ); } ckmakmsg(query, sizeof(query), "name like ",x25name, " and attribute like local_nua"); /* initialise ODM database */ odmerrno = 0; if (odm_initialize() == -1) { printf( "x25local_nua(): can't initialize ODM database"); switch (odmerrno) { case ODMI_INVALID_PATH: printf( "invalid path\n" ); break; case ODMI_MALLOC_ERR: printf( "malloc failed\n" ); break; default: printf( "unknown error %d\nPlease call IBM\n", odmerrno ); } return(rc); } /* open the CuAt class */ retClass = odm_open_class(CuAt_CLASS); if (((int)retClass) == -1) { printf( "kermit: x25local_nua(): can't open CuAt class in odm. " ); switch (odmerrno) { case ODMI_CLASS_DNE: printf( "CuAt class doesn't exist\n" ); break; case ODMI_CLASS_PERMS: printf( "permission to CuAt class file denied\n" ); break; case ODMI_MAGICNO_ERR: printf( "CuAt is an invalid ODM object class\n" ); break; case ODMI_OPEN_ERR: printf( "cannot open CuAt class - and don't know why!\n" ); break; case ODMI_INVALID_PATH: printf( "invalid path\n" ); break; case ODMI_TOOMANYCLASSES: printf( "too many object classes have been opened\n" ); break; default: printf( "unknown error %d\nPlease call IBM\n", odmerrno ); } return(rc); } #ifdef DEBUG printf("retClass= %d\n", retClass); #endif /* DEBUG */ response = (struct CuAt *)odm_get_first( retClass, query, NULL ); if (((int)response) == -1) { printf( "kermit: x25local_nua(): odm query failed " ); switch (odmerrno) { case ODMI_BAD_CRIT: /* Programming error */ printf( "bad search criteria\n" ); break; case ODMI_CLASS_DNE: printf( "CuAt class doesn't exist\n" ); break; case ODMI_CLASS_PERMS: printf( "permission to CuAt class file denied\n" ); break; case ODMI_INTERNAL_ERR: printf("odm internal error\nPlease contact your administrator\n" ); break; case ODMI_INVALID_CLXN: printf("CuAt is invalid or inconsistent odm class collection\n"); break; case ODMI_INVALID_PATH: printf( "invalid path\n" ); break; case ODMI_MAGICNO_ERR: printf( "CuAt is an invalid ODM object class\n" ); break; case ODMI_MALLOC_ERR: printf( "malloc failed\n" ); break; case ODMI_OPEN_ERR: printf( "cannot open CuAt class - and don't know why!\n" ); break; case ODMI_TOOMANYCLASSES: printf( "too many object classes have been opened\n" ); break; default: printf( "unknown error %d\nPlease call IBM\n", odmerrno ); } return(rc); } /* check for a meaningfull response */ if (response != NULL) { if (response->value != NULL) { strcpy(buf, response->value); rc = strlen( buf ); #ifdef DEBUG /* printf( "attribute name is: %s\n", (char *)response->attribute ); printf( "I think my address is %s\n", (char*)response->value ); */ #endif /* DEBUG */ } else { printf( "kermit: x25local_nua(): couldn't find the local NUA\n" ); } } else { switch (odmerrno) { case ODMI_BAD_CRIT: printf( "Error: ODMI_BAD_CRIT - bad criteria\n" ); break; case ODMI_CLASS_DNE: printf( "Error: ODMI_CLASS_DNE - class doesn't exist\n" ); break; case ODMI_CLASS_PERMS: printf( "Error: ODMI_CLASS_PERMS - class permissions\n" ); break; case ODMI_INTERNAL_ERR: printf( "Error: ODMI_INTERNAL_ERR - panic\n" ); break; case ODMI_INVALID_CLXN: printf( "Error: ODMI_INVALID_CLXN - invalid collection\n" ); break; case ODMI_INVALID_PATH: printf( "Error: ODMI_INVALID_PATH - invalid path - what path?\n" ); break; case ODMI_MAGICNO_ERR: printf( "Error: ODMI_MAGICNO_ERR - invalid object magic\n" ); break; case ODMI_MALLOC_ERR: printf( "Error: ODMI_MALLOC_ERR - malloc failed\n" ); break; case ODMI_OPEN_ERR: printf( "Error: ODMI_OPEN_ERR - cannot open class\n" ); break; case ODMI_TOOMANYCLASSES: printf( "Error: ODMI_TOOMANYCLASSES - too many classes\n" ); break; default: printf( "Unknown error!\n" ); } return(rc); } /* close the database again */ odm_close_class( retClass ); /* forget about ODM all together */ odm_terminate(); #ifdef TRACE printf( "TRACE: leaving x25local_nua\n" ); #endif /* TRACE */ debug(F110, "x25local_nua local address is ", buf, 0); return(rc); } /***************************************************************************** * Function: x25facilities * * Description: * build up the facilities data packet for a connection request * * Parameters: * a pre-allocated char buffer, normally NPI_MAX_DATA big. * * Return Value: * the number of characters inserted into the buffer */ int x25facilities(buffer) char *buffer; { extern int revcall; extern int closgr; char *p; /* temp pointer */ char *start; /* temp pointer */ #ifdef TRACE printf( "TRACE: entering x25facilities\n" ); #endif /* TRACE */ p = buffer + 1; start = p; #ifdef DEBUG printf( "kermit: x25facilities(): getting X25 facilities\n" ); #endif /* DEBUG */ if (revcall != 0) { #ifdef DEBUG printf("reverse charge: %d\n", revcall ); #endif /* DEBUG */ *++p = 0x01; *++p = revcall; } if (closgr > 0) { #ifdef DEBUG printf("closed user group: %d\n", closgr ); #endif /* DEBUG */ *++p = 0x03; *++p = closgr; } #ifdef DEBUG if (p == start) { printf( "no facilities\n" ); } #endif /* DEBUG */ /* set the size of the facilities buffer */ *buffer = (char)( p - start ) & 0xff; #ifdef DEBUG printf( "kermit: x25facilities(): returning %d\n", (int)(p - buffer) ); #endif /* DEBUG */ #ifdef TRACE printf( "TRACE: leaving x25facilities\n" ); #endif /* TRACE */ /* return the size of the facilities with size byte */ /* 1 == no facilities, 0 byte returned as facilities size */ return( (int)(p - buffer) ); } /* * reset the connection */ int x25reset(cause, diagn) char cause; char diagn; { /* not implemented */ #ifdef TRACE printf( "TRACE: entering x25reset\n" ); #endif /* TRACE */ #ifdef TRACE printf( "TRACE: leaving x25reset\n" ); #endif /* TRACE */ return(0); } /* * clear the x25 connection - ie: hang up */ int x25clear() { int get_flags = 0; /* priority flag for getmsg */ int put_flags = 0; /* send flags, always 0 */ ulong type; /* primitive type */ N_discon_req_t *discon_req; /* pointer to N_DISCON_REQ */ N_discon_ind_t *discon_ind; /* pointer to N_DISCON_IND */ N_npi_data_t *discon_data; /* pointer to N_DISCON_IND data */ int rc = 0; /* return code */ #ifdef TRACE printf( "TRACE: entering x25clear\n" ); #endif /* TRACE */ #ifdef DEBUG /* printf( "x25clear(): checking last msg: %s\n", x25prim(x25lastmsg)); */ #endif /* DEBUG */ /* * The following checks are used to ensure that we don't disconnect * or unbind twice - this seems to throw the NPI interface right out of * kilter. */ switch(x25lastmsg) { case N_BIND_ACK: case N_CONN_CON: case N_CONN_REQ: case N_DATA_REQ: case N_DATA_IND: { #ifdef DEBUG /* printf("x25clear(): actively disconnecting\n"); */ #endif /* DEBUG */ discon_req = (N_discon_req_t *)malloc(NPI_MAX_CTL); if (discon_req == NULL) { perror("kermit x25clear(): discon_req malloc failed\n"); /* fallthrough, try to unbind the NPI anyway */ } else { discon_req->PRIM_type = N_DISCON_REQ; discon_req->DISCON_reason = 0; /* not used by AIX */ discon_req->RES_length = 0; discon_req->RES_offset = (ulong)(sizeof(N_discon_req_t)); discon_req->SEQ_number = x25seqno; /* global */ if (x25putmsg(ttyfd, (N_npi_ctl_t*)discon_req, (N_npi_data_t*)NULL, 0, &put_flags ) < 0) { perror("x25putmsg failed in x25clear()"); } discon_ind = (N_discon_ind_t *)malloc(NPI_MAX_CTL); discon_data = (N_npi_data_t *)malloc(NPI_MAX_DATA); if((discon_ind == NULL) || (discon_data == NULL)) { perror("x25clear(): discon_ind malloc failed\n"); /* fallthrough, try to unbind the NPI anyway */ } else { if(x25getmsg(ttyfd, (N_npi_ctl_t*)discon_ind, NPI_MAX_CTL, (N_npi_data_t*)discon_data, NPI_MAX_DATA, &get_flags, N_OK_ACK ) < 0 ) { perror("x25getmsg failed in x25clear()"); /* fallthrough, try to unbind the NPI anyway */ } } } break; } } if (x25lastmsg != N_UNBIND_REQ) { rc = x25unbind(ttyfd); } #ifdef TRACE printf( "TRACE: leaving x25clear\n" ); #endif /* TRACE */ return(rc); } #ifdef DEBUG /* * only for debugging * * turn the internal representation of a datablock into something * half-way readable. Because the length is known, we can print * the string including null's etc (important, because the first(!) * byte of an X121 address is a null! (X121 addr == 0 + X25 addr) */ x25dump_data(char *addr, ulong offset, ulong length) { char *ptr = addr + offset; ulong i = length; /* allocate enough memory for all unprintable chars */ char *buf = (char *)malloc( length * 4 ); char *bptr = buf; /* pointer to current place in the print buffer */ while (i > 0) { if (isprint(*ptr)) { *bptr++ = *ptr; } else { *bptr++ = '['; strcpy(bptr,ckctox(*ptr,1)); bptr += 2; *bptr++ = ']'; } ptr++; i--; } if (length > 0) { *bptr = '\0'; printf( "%s", buf ); } printf( " (%d+%d)\n", offset, length ); if (buf) { free(buf); buf = NULL; } return; } /* * only for debugging * print as much useful information about a packet as possible */ x25dump_prim(primitive) N_npi_ctl_t *primitive; { printf("Primitive"); switch (primitive->PRIM_type) { case N_BIND_ACK: printf( "\tN_BIND_ACK\n\taddress:\t" ); x25dump_data( (char *)primitive, primitive->bind_ack.ADDR_offset, primitive->bind_ack.ADDR_length ); printf( "\tproto id:\t" ); x25dump_data( (char *)primitive, primitive->bind_ack.PROTOID_offset, primitive->bind_ack.PROTOID_length ); printf( "\tconnind:\t%d\n\ttoken:\t\t%d\n", primitive->bind_ack.CONIND_number, primitive->bind_ack.TOKEN_value ); break; case N_BIND_REQ: printf( "\tN_BIND_REQ\n\taddress:\t" ); x25dump_data( (char *)primitive, primitive->bind_req.ADDR_offset, primitive->bind_req.ADDR_length ); printf( "\tproto id:\t" ); x25dump_data( (char *)primitive, primitive->bind_req.PROTOID_offset, primitive->bind_req.PROTOID_length ); printf( "\tconnind:\t%d\n\tflags:\t\t%d\n", primitive->bind_req.CONIND_number, primitive->bind_req.BIND_flags ); break; case N_CONN_CON: printf( "\tN_CONN_CON\n" ); printf( "\tRES\t\t" ); x25dump_data( (char *)primitive, primitive->conn_con.RES_offset, primitive->conn_con.RES_length ); printf( "\tflags:\t%d\n", primitive->conn_con.CONN_flags ); break; case N_CONN_IND: printf( "\tN_CONN_IND\n" ); printf( "\tsource:\t\t" ); x25dump_data( (char *)primitive, primitive->conn_ind.SRC_offset, primitive->conn_ind.SRC_length ); printf( "\tdestination:\t" ); x25dump_data( (char *)primitive, primitive->conn_ind.DEST_offset, primitive->conn_ind.DEST_length ); printf( "\tSEQ_number:\t%d\n", primitive->conn_ind.SEQ_number ); printf( "\tflags:\t%d\n", primitive->conn_ind.CONN_flags ); break; case N_CONN_REQ: printf( "\tN_CONN_REQ\n\tdestination:\t" ); x25dump_data( (char *)primitive, primitive->conn_req.DEST_offset, primitive->conn_req.DEST_length ); printf( "\tflags:\t%d\n", primitive->conn_req.CONN_flags ); break; case N_CONN_RES: printf( "\tN_CONN_RES\n" ); printf( "\tTOKEN_value\t%d\n", primitive->conn_res.TOKEN_value ); printf( "\tSEQ_number\t%d\n", primitive->conn_res.SEQ_number ); printf( "\tCONN_flags\t%d\n", primitive->conn_res.CONN_flags ); printf( "\tRES\t\t" ); x25dump_data( (char *)primitive, primitive->conn_res.RES_offset, primitive->conn_res.RES_length ); break; case N_DATACK_IND: printf( "\tN_DATACK_IND\n" ); break; case N_DATACK_REQ: printf( "\tN_DATACK_REQ\n" ); printf( "\tflags:\t%d\n", primitive->data_req.DATA_xfer_flags ); break; case N_DATA_IND: printf( "\tN_DATA_IND\n" ); printf( "\tflags:\t%d\n", primitive->data_ind.DATA_xfer_flags ); break; case N_DATA_REQ: printf( "\tN_DATA_REQ\n" ); break; case N_DISCON_IND: printf( "\tN_DISCON_IND\n" ); printf( "\torigin:\t%d\n", primitive->discon_ind.DISCON_orig ); printf( "\treason:\t\t%d\n", primitive->discon_ind.DISCON_reason ); printf( "\tseq no:\t\t%d\n", primitive->discon_ind.SEQ_number ); printf( "\tRES:\t" ); x25dump_data( (char *)primitive, primitive->discon_ind.RES_offset, primitive->discon_ind.RES_length ); break; case N_DISCON_REQ: printf( "\tN_DISCON_REQ\n" ); printf( "\tDISCON_reason:\t%d\n", primitive->discon_req.DISCON_reason ); printf( "\tRES:\t" ); x25dump_data( (char *)primitive, primitive->discon_req.RES_offset, primitive->discon_req.RES_length ); printf( "\tSEQ_number:\t%d\n", primitive->discon_req.SEQ_number ); break; case N_ERROR_ACK: printf( "\tN_ERROR_ACK\n" ); printf( "\tCaused by:\t%s\n", x25prim( primitive->error_ack.ERROR_prim ) ); printf( "\tNPI error:\t%s\n", x25err( primitive->error_ack.NPI_error )); errno = primitive->error_ack.UNIX_error; perror( "\t" ); break; case N_EXDATA_IND: printf( "\tN_EXDATA_ACK\n" ); break; case N_EXDATA_REQ: printf( "\tN_EXDATA_REQ\n" ); break; case N_INFO_ACK: printf( "\tN_INFO_ACK\n" ); printf( "\tNSDU size:\t%d\n", primitive->info_ack.NSDU_size ); printf( "\tENSDU size:\t%d\n", primitive->info_ack.ENSDU_size ); printf( "\tCDATA size:\t%d\n", primitive->info_ack.CDATA_size ); printf( "\tDDATA size:\t%d\n", primitive->info_ack.DDATA_size ); printf( "\tADDR size:\t%d\n", primitive->info_ack.ADDR_size ); printf( "\tNIDU size:\t%d\n", primitive->info_ack.NIDU_size ); break; case N_INFO_REQ: printf( "\tN_INFO_REQ\n" ); break; case N_OK_ACK: printf( "\tN_OK_ACK\n" ); break; case N_OPTMGMT_REQ: printf( "\tN_OPTMGMT_REQ\n" ); break; case N_RESET_CON: printf( "\tN_RESET_CON\n" ); break; case N_RESET_IND: printf( "\tN_RESET_IND\n" ); printf( "\treason:\t\t%d\n", primitive->reset_ind.RESET_reason ); printf( "\torigin:\t\t%d\n", primitive->reset_ind.RESET_orig ); break; case N_RESET_REQ: printf( "\tN_RESET_REQ\n" ); printf( "\treason:\t\t%d\n", primitive->reset_req.RESET_reason ); break; case N_RESET_RES: printf( "\tN_RESET_RES\n" ); break; case N_UDERROR_IND: printf( "\tN_UDERROR_IND\n" ); break; case N_UNBIND_REQ: printf( "\tN_UNBIND_REQ\n" ); break; case N_UNITDATA_REQ: printf( "\tN_UNITDATA_REQ\n" ); break; case N_UNITDATA_IND: printf( "\tN_UNITDATA_IND\n" ); break; default: (void) printf( "Unknown NPI error %d", primitive->PRIM_type ); return 0; } } #endif /* DEBUG */ /* it looks like signal handling is not needed with streams! */ /* x25oobh() - handle SIGURG signals - take from isode ? */ #endif /* IBMX25 */ #ifndef NOHTTP /* Which time.h files to include... See ckcdeb.h for defaults. Note that 0, 1, 2, or all 3 of these can be included according to the symbol definitions. */ #ifndef NOTIMEH #ifdef TIMEH #include #endif /* TIMEH */ #endif /* NOTIMEH */ #ifndef NOSYSTIMEH #ifdef SYSTIMEH #include #endif /* SYSTIMEH */ #endif /* NOSYSTIMEH */ #ifndef NOSYSTIMEBH #ifdef SYSTIMEBH #include #endif /* SYSTIMEBH */ #endif /* NOSYSTIMEBH */ #ifndef TIMEH #ifndef SYSTIMEH #ifndef SYSTIMEBH #ifdef Plan9 #include #else #ifdef AIX41 #include #else #ifdef SUNOS4 #include #else #ifdef SYSTIMEH #include #else #ifdef POSIX #include #else #ifdef CLIX #include #else #ifdef OS2 #include #else #include /* #include */ #endif /* OS2 */ #endif /* CLIX */ #endif /* POSIX */ #endif /* SYSTIMEH */ #endif /* SUNOS4 */ #endif /* AIX41 */ #endif /* Plan9 */ #endif #endif #endif #ifdef OS2 #include #ifdef NT #define utimbuf _utimbuf #endif /* NT */ #define utime _utime #else #ifdef SYSUTIMEH /* if requested, */ #include /* for extra fields required by */ #else /* 88Open spec. */ #ifdef UTIMEH /* or if requested */ #include /* (SVR4, POSIX) */ #define SYSUTIMEH /* Use this for both cases. */ #endif /* UTIMEH */ #endif /* SYSUTIMEH */ #endif /* OS2 */ #ifdef VMS /* SMS 2007/02/15 */ #include "ckvrtl.h" #endif /* def VMS */ #ifndef HTTP_VERSION #define HTTP_VERSION "HTTP/1.1" #endif /* HTTP_VERSION */ #ifdef CMDATE2TM time_t #ifdef CK_ANSIC http_date(char * date) #else http_date(date) char * date; #endif /* CK_ANSIC */ /* http_date */ { /* HTTP dates are of the form: "Sun, 06 Oct 1997 20:11:47 GMT" */ /* There are two older formats which we are required to parse * that we currently do not: * * RFC 850: "Sunday, 06-Oct-97 20:11:47 GMT" * asctime(): "Sun Nov 6 20:11:47 1997" * * However, it is required that all dates be sent in the form we * do accept. The other two formats are for compatibility with * really old servers. */ extern char cmdatebuf[18]; struct tm t_tm; time_t t; char ldate[32]; int j; j = ckindex(",",date,0,0,0); ckstrncpy(ldate,&date[j+1],25); { /* cmcvtate() date changed to return a string pointer. fdc, 12 Aug 2001. */ char * dp; dp = (char *)cmcvtdate(ldate,0); /* Convert to normal form */ if (!dp) return(0); t_tm = *cmdate2tm(dp,1); } /* From Lucas Hart, 5 Dec 2001: "On the systems to which I have access (SunOS 4.1.1, Solaris 8, and Tru64), setting tm_isdst to -1 maintains the correct timezone offsets, i.e., writes the specified (GMT) time if the buffer size is 21, or the contemporaneous localtime if the buffer size is 25. Perhaps tm_isdst should be set in cmdate2tm(), rather than only in http_date." */ #ifndef NOTM_ISDST /* For platforms where */ t_tm.tm_isdst = -1; /* tm_isdst doesn't exist. */ #endif /* NOTM_ISDST */ t = mktime(&t_tm); /* NOT PORTABLE */ #ifdef XX_TIMEZONE t -= _timezone; #endif /* XX_TIMEZONE */ return(t); } #endif /* CMDATE2TM */ char * http_now() { static char nowstr[32]; #ifdef CMDATE2TM struct tm *gmt; time_t ltime; /* NOT PORTABLE */ time(<ime); gmt = gmtime(<ime); /* PROBABLY NOT PORTABLE */ strftime(nowstr,32,"%a, %d %b %Y %H:%M:%S GMT",gmt); /* NOT PORTABLE */ /* not only is it not portable but it's locale-dependent */ #else /* This is hopeless. First of all, it seems that HTTP wants Day and Month NAMES? In English? Whose idea was that? Even worse, the date/time must be expressed in Zulu (UTC (GMT)), and converting from local time to GMT is a nightmare. Every platform does it differently, if at all -- even if we restrict ourselves to UNIX. For example (quoting from recent C-Kermit edit history), "Fixed a longstanding bug in the BSDI version, in which incoming file dates were set in GMT rather than local time. It seems in 4.4BSD, localtime() does not return the local time, but rather Zero Meridian (Zulu) time (GMT), and must be adjusted by the tm_gmtoff value." Swell. For greater appreciation of the scope of the problem, just take a look at the time-related #ifdefs in ckutio.c. The only right way to do this is to add our own portable API for converting between local time and GMT/UTC/Zulu that shields us not only from UNIXisms like time_t and struct tm, but also the unbelievable amount of differences in time-related APIs -- e.g. is "timezone" an external variable or a function; which header file(s) do we include, etc etc etc. It's a major project. */ int x; x = cmcvtdate("",1); Evidently this code is not used -- if it is, it must be fixed to use new (aug 2001) cmcvtdate() calling conventions. if (x < 0) return(""); /* yyyymmdd hh:mm:ss */ /* 01234567890123456 */ nowstr[0] = 'X'; /* 1st letter of day */ nowstr[1] = 'x'; /* 2nd letter of day */ nowstr[2] = 'x'; /* 3rd letter of day */ nowstr[3] = ','; nowstr[4] = ' '; nowstr[5] = cmdate[6]; nowstr[6] = cmdate[7]; nowstr[7] = ' '; nowstr[8] = ' '; /* first letter of month */ nowstr[9] = ' '; /* second letter of month */ nowstr[10] = ' '; /* third letter of month */ nowstr[11] = ' '; nowstr[12] = cmdate[0]; nowstr[13] = cmdate[1]; nowstr[14] = cmdate[2]; nowstr[15] = cmdate[3]; nowstr[16] = ' '; nowstr[17] = cmdate[9]; nowstr[18] = cmdate[10]; nowstr[19] = cmdate[11]; nowstr[20] = cmdate[12]; nowstr[21] = cmdate[13]; nowstr[22] = cmdate[14]; nowstr[23] = cmdate[15]; nowstr[24] = cmdate[16]; nowstr[25] = ' '; nowstr[26] = 'G'; nowstr[27] = 'M'; nowstr[28] = 'T'; nowstr[29] = '\0'; #endif /* CMDATE2TM */ return(nowstr); } #ifndef OS2 #ifndef CK_AUTHENTICATION /* from ckuusr.h, which this module normally doesn't include */ _PROTOTYP( int dclarray, (char, int) ); #endif /* CK_AUTHENTICATION */ #endif /* OS2 */ /* Assign http response pairs to given array. For best results, response pairs should contain no spaces. Call with: resp = pointer to response list. n = size of response list. array = array letter. Returns: 0 on failure. >= 1, size of array, on success. */ static int #ifdef CK_ANSIC http_mkarray(char ** resp, int n, char array) #else http_mkarray(resp, n, array) char ** resp; int n; char array; #endif /* CK_ANSIC */ { #ifndef NOSPL int i, x; char ** ap; extern char ** a_ptr[]; extern int a_dim[]; if (!array || n <= 0) return(0); if ((x = dclarray(array,n)) < 0) { printf("?Array declaration failure\n"); return(-9); } /* Note: argument array is 0-based but Kermit array is 1-based */ ap = a_ptr[x]; ap[0] = NULL; /* 0th element is empty */ for (i = 1; i <= n; i++) { ap[i] = resp[i-1]; /* If resp elements were malloc'd */ resp[i-1] = NULL; } a_dim[x] = n; return(n); #else return(0); #endif /* NOSPL */ } #define HTTPHEADCNT 64 int http_get_chunk_len() { int len = 0; int i = 0, j = -1; char buf[24]; int ch; while ((ch = http_inc(0)) >= 0 && i < 24) { buf[i] = ch; if ( buf[i] == ';' ) /* Find chunk-extension (if any) */ j = i; if ( buf[i] == 10 ) { /* found end of line */ if (i > 0 && buf[i-1] == 13) i--; buf[i] = '\0'; break; } i++; } if ( i < 24 ) { /* buf now contains len in Hex */ len = hextoulong(buf, j == -1 ? i : j-1); } return(len); } int http_isconnected() { return(httpfd != -1); } char * http_host() { return(httpfd != -1 ? http_host_port : ""); } char * http_security() { if ( httpfd == -1 ) return("NULL"); #ifdef CK_SSL if (tls_http_active_flag) { SSL_CIPHER * cipher; const char *cipher_list; static char buf[128]; buf[0] = NUL; cipher = SSL_get_current_cipher(tls_http_con); cipher_list = SSL_CIPHER_get_name(cipher); SSL_CIPHER_description(cipher,buf,sizeof(buf)); return(buf); } #endif /* CK_SSL */ return("NULL"); } int http_reopen() { int rc = 0; char * s = NULL; /* strdup is not portable */ if ( tcp_http_proxy ) { char * p; makestr(&s,(char *)http_host_port); p = s; while (*p != '\0' && *p != ':') p++; /* Look for colon */ if (*p == ':') { /* Have a colon */ *p++ = '\0'; /* Get service name or number */ } else { p="http"; } rc = http_open(s,p,http_ssl,NULL,0,http_agent); } else { makestr(&s,(char *)http_ip); rc = http_open(s,ckuitoa(http_port),http_ssl,NULL,0,http_agent); } free(s); return(rc); } int #ifdef CK_ANSIC http_open(char * hostname, char * svcname, int use_ssl, char * rdns_name, int rdns_len, char * agent) #else /* CK_ANSIC */ http_open(hostname, svcname, use_ssl, rdns_name, rdns_len, agent) char * hostname; char * svcname; int use_ssl; char * rdns_name; int rdns_len; char * agent; #endif /* CK_ANSIC */ { char namecopy[NAMECPYL]; char *p; int i, x, dns = 0; #ifdef TCPSOCKET int isconnect = 0; #ifdef SO_OOBINLINE int on = 1; #endif /* SO_OOBINLINE */ struct servent *service=NULL; struct hostent *host=NULL; struct sockaddr_in r_addr; struct sockaddr_in sin; struct sockaddr_in l_addr; GSOCKNAME_T l_slen; #ifdef EXCELAN struct sockaddr_in send_socket; #endif /* EXCELAN */ #ifdef INADDRX /* inet_addr() is of type struct in_addr */ #ifdef datageneral extern struct in_addr inet_addr(); #else #ifdef HPUX5WINTCP extern struct in_addr inet_addr(); #endif /* HPUX5WINTCP */ #endif /* datageneral */ struct in_addr iax; #else #ifdef INADDR_NONE struct in_addr iax; #else /* INADDR_NONE */ long iax; #endif /* INADDR_NONE */ #endif /* INADDRX */ if ( rdns_name == NULL || rdns_len < 0 ) rdns_len = 0; *http_ip = '\0'; /* Initialize IP address string */ namecopy[0] = '\0'; #ifdef DEBUG if (deblog) { debug(F110,"http_open hostname",hostname,0); debug(F110,"http_open svcname",svcname,0); } #endif /* DEBUG */ if (!hostname) hostname = ""; if (!svcname) svcname = ""; if (!*hostname || !*svcname) return(-1); service = ckgetservice(hostname,svcname,http_ip,20); if (service == NULL) { if ( !quiet ) printf("?Invalid service: %s\r\n",svcname); return(-1); } /* For HTTP connections we must preserve the original hostname and */ /* service requested so we can include them in the Host header. */ ckmakmsg(http_host_port,sizeof(http_host_port),hostname,":", ckuitoa(ntohs(service->s_port)),NULL); http_port = ntohs(service->s_port); http_ssl = use_ssl; debug(F111,"http_open",http_host_port,http_port); /* 'http_ip' contains the IP address to which we want to connect */ /* 'svcnam' contains the service name */ /* 'service->s_port' contains the port number in network byte order */ /* If we are using an http proxy, we need to create a buffer containing */ /* hostname:port-number */ /* to pass to the http_connect() function. Then we need to replace */ /* 'namecopy' with the name of the proxy server and the service->s_port */ /* with the port number of the proxy (default port 80). */ if ( tcp_http_proxy ) { ckmakmsg(proxycopy,sizeof(proxycopy),hostname,":", ckuitoa(ntohs(service->s_port)),NULL); ckstrncpy(namecopy,tcp_http_proxy,NAMECPYL); p = namecopy; /* Was a service requested? */ while (*p != '\0' && *p != ':') p++; /* Look for colon */ if (*p == ':') { /* Have a colon */ debug(F110,"http_open name has colon",namecopy,0); *p++ = '\0'; /* Get service name or number */ } else { strcpy(++p,"http"); } service = ckgetservice(namecopy,p,http_ip,20); if (!service) { fprintf(stderr, "Can't find port for service %s\n", p); #ifdef TGVORWIN debug(F101,"http_open can't get service for proxy","",socket_errno); #else debug(F101,"http_open can't get service for proxy","",errno); #endif /* TGVORWIN */ errno = 0; /* (rather than mislead) */ return(-1); } /* copy the proxyname and remove the service if any so we can use * it as the hostname */ ckstrncpy(namecopy,tcp_http_proxy,NAMECPYL); p = namecopy; /* Was a service requested? */ while (*p != '\0' && *p != ':') p++; /* Look for colon */ if (*p == ':') { /* Have a colon */ *p = '\0'; /* terminate string */ } hostname = namecopy; /* use proxy as hostname */ } /* Set up socket structure and get host address */ bzero((char *)&r_addr, sizeof(r_addr)); debug(F100,"http_open bzero ok","",0); #ifdef INADDR_NONE debug(F101,"http_open INADDR_NONE defined","",INADDR_NONE); #else /* INADDR_NONE */ debug(F100,"http_open INADDR_NONE not defined","",0); #endif /* INADDR_NONE */ #ifdef INADDRX debug(F100,"http_open INADDRX defined","",0); #else /* INADDRX */ debug(F100,"http_open INADDRX not defined","",0); #endif /* INADDRX */ #ifndef NOMHHOST #ifdef INADDRX iax = inet_addr(http_ip[0]?http_ip:hostname); debug(F111,"http_open inet_addr",http_ip[0]?http_ip:hostname,iax.s_addr); #else /* INADDRX */ #ifdef INADDR_NONE iax.s_addr = inet_addr(http_ip[0]?http_ip:hostname); debug(F111,"http_open inet_addr",http_ip[0]?http_ip:hostname,iax.s_addr); #else /* INADDR_NONE */ #ifndef datageneral iax = (unsigned int) inet_addr(http_ip[0]?http_ip:hostname); #else iax = -1L; #endif /* datageneral */ debug(F111,"http_open inet_addr",http_ip[0]?http_ip:hostname,iax); #endif /* INADDR_NONE */ #endif /* INADDRX */ dns = 0; if ( #ifdef INADDR_NONE /* This might give warnings on 64-bit platforms but they should be harmless */ /* because INADDR_NONE should be all 1's anyway, thus the OR part is */ /* probably superfluous -- not sure why it's even there, maybe it should be */ /* removed. */ iax.s_addr == INADDR_NONE /* || iax.s_addr == (unsigned long) -1L */ #else /* INADDR_NONE */ iax == -1 #endif /* INADDR_NONE */ ) { if (!quiet) { printf(" DNS Lookup... "); fflush(stdout); } if ((host = gethostbyname(http_ip[0] ? http_ip : hostname)) != NULL) { debug(F100,"http_open gethostbyname != NULL","",0); host = ck_copyhostent(host); dns = 1; /* Remember we performed dns lookup */ r_addr.sin_family = host->h_addrtype; if (tcp_rdns && host->h_name && host->h_name[0] && (rdns_len > 0) && (tcp_http_proxy == NULL) ) ckmakmsg(rdns_name,rdns_len,host->h_name,":",svcname,NULL); #ifdef HADDRLIST #ifdef h_addr /* This is for trying multiple IP addresses - see */ if (!(host->h_addr_list)) return(-1); bcopy(host->h_addr_list[0], (caddr_t)&r_addr.sin_addr, host->h_length ); #else bcopy(host->h_addr, (caddr_t)&r_addr.sin_addr, host->h_length); #endif /* h_addr */ #else /* HADDRLIST */ bcopy(host->h_addr, (caddr_t)&r_addr.sin_addr, host->h_length); #endif /* HADDRLIST */ #ifdef COMMENT #ifndef EXCELAN debug(F111,"BCOPY","host->h_addr",host->h_addr); #endif /* EXCELAN */ debug(F111,"BCOPY"," (caddr_t)&r_addr.sin_addr", (caddr_t)&r_addr.sin_addr); debug(F111,"BCOPY"," r_addr.sin_addr.s_addr", r_addr.sin_addr.s_addr); #endif /* COMMENT */ debug(F111,"BCOPY","host->h_length",host->h_length); } } #endif /* NOMHHOST */ if (!dns) { #ifdef INADDRX /* inet_addr() is of type struct in_addr */ struct in_addr ina; unsigned long uu; debug(F100,"http_open gethostbyname == NULL: INADDRX","",0); ina = inet_addr(http_ip[0]?http_ip:hostname); uu = *(unsigned int *)&ina; #else /* Not INADDRX */ /* inet_addr() is unsigned long */ unsigned long uu; debug(F100,"http_open gethostbyname == NULL: Not INADDRX","",0); uu = inet_addr(http_ip[0]?http_ip:hostname); #endif /* INADDRX */ debug(F101,"http_open uu","",uu); if ( #ifdef INADDR_NONE !(uu == INADDR_NONE || uu == (unsigned int) -1L) #else /* INADDR_NONE */ uu != ((unsigned long)-1) #endif /* INADDR_NONE */ ) { r_addr.sin_addr.s_addr = uu; r_addr.sin_family = AF_INET; } else { #ifdef VMS fprintf(stdout, "\r\n"); /* complete any previous message */ #endif /* VMS */ fprintf(stderr, "Can't get address for %s\n", http_ip[0]?http_ip:hostname); #ifdef TGVORWIN debug(F101,"http_open can't get address","",socket_errno); #else debug(F101,"http_open can't get address","",errno); #endif /* TGVORWIN */ errno = 0; /* Rather than mislead */ return(-1); } } /* Get a file descriptor for the connection. */ r_addr.sin_port = service->s_port; ckstrncpy(http_ip,(char *)inet_ntoa(r_addr.sin_addr),20); debug(F110,"http_open trying",http_ip,0); if (!quiet && *http_ip) { printf(" Trying %s... ", http_ip); fflush(stdout); } /* Loop to try additional IP addresses, if any. */ do { #ifdef EXCELAN send_socket.sin_family = AF_INET; send_socket.sin_addr.s_addr = 0; send_socket.sin_port = 0; if ((httpfd = socket(SOCK_STREAM, (struct sockproto *)0, &send_socket, SO_REUSEADDR)) < 0) #else /* EXCELAN */ if ((httpfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) #endif /* EXCELAN */ { #ifdef EXCELAN experror("TCP socket error"); #else #ifdef TGVORWIN #ifdef OLD_TWG errno = socket_errno; #endif /* OLD_TWG */ socket_perror("TCP socket error"); debug(F101,"http_open socket error","",socket_errno); #else perror("TCP socket error"); debug(F101,"http_open socket error","",errno); #endif /* TGVORWIN */ #endif /* EXCELAN */ return (-1); } errno = 0; /* If a specific TCP address on the local host is desired we */ /* must bind it to the socket. */ #ifndef datageneral if (tcp_address) { int s_errno; debug(F110,"http_open binding socket to",tcp_address,0); bzero((char *)&sin,sizeof(sin)); sin.sin_family = AF_INET; #ifdef INADDRX inaddrx = inet_addr(tcp_address); sin.sin_addr.s_addr = *(unsigned long *)&inaddrx; #else sin.sin_addr.s_addr = inet_addr(tcp_address); #endif /* INADDRX */ sin.sin_port = 0; if (bind(httpfd, (struct sockaddr *)&sin, sizeof(sin)) < 0) { s_errno = socket_errno; /* Save error code */ #ifdef TCPIPLIB socket_close(httpfd); #else /* TCPIPLIB */ close(httpfd); #endif /* TCPIPLIB */ httpfd = -1; errno = s_errno; /* and report this error */ debug(F101,"http_open bind errno","",errno); return(-1); } } #endif /* datageneral */ /* Now connect to the socket on the other end. */ #ifdef EXCELAN if (connect(httpfd, &r_addr) < 0) #else #ifdef NT WSASafeToCancel = 1; #endif /* NT */ if (connect(httpfd, (struct sockaddr *)&r_addr, sizeof(r_addr)) < 0) #endif /* EXCELAN */ { #ifdef NT WSASafeToCancel = 0; #endif /* NT */ #ifdef OS2 i = socket_errno; #else /* OS2 */ #ifdef TGVORWIN i = socket_errno; #else i = errno; /* Save error code */ #endif /* TGVORWIN */ #endif /* OS2 */ #ifdef HADDRLIST #ifdef h_addr if (host && host->h_addr_list && host->h_addr_list[1]) { perror(""); host->h_addr_list++; bcopy(host->h_addr_list[0], (caddr_t)&r_addr.sin_addr, host->h_length); ckstrncpy(http_ip,(char *)inet_ntoa(r_addr.sin_addr),20); debug(F110,"http_open h_addr_list",http_ip,0); if (!quiet && *http_ip) { printf(" Trying %s... ", http_ip); fflush(stdout); } #ifdef TCPIPLIB socket_close(httpfd); /* Close it. */ #else close(httpfd); #endif /* TCPIPLIB */ continue; } #endif /* h_addr */ #endif /* HADDRLIST */ http_close(); httpfd = -1; errno = i; /* And report this error */ #ifdef EXCELAN if (errno) experror("http_open connect"); #else #ifdef TGVORWIN debug(F101,"http_open connect error","",socket_errno); /* if (errno) socket_perror("http_open connect"); */ #ifdef OLD_TWG errno = socket_errno; #endif /* OLD_TWG */ if (!quiet) socket_perror("http_open connect"); #else /* TGVORWIN */ debug(F101,"http_open connect errno","",errno); #ifdef VMS if (!quiet) perror("\r\nFailed"); #else if (!quiet) perror("Failed"); #endif /* VMS */ #ifdef DEC_TCPIP if (!quiet) perror("http_open connect"); #endif /* DEC_TCPIP */ #ifdef CMU_TCPIP if (!quiet) perror("http_open connect"); #endif /* CMU_TCPIP */ #endif /* TGVORWIN */ #endif /* EXCELAN */ return(-1); } #ifdef NT WSASafeToCancel = 0; #endif /* NT */ isconnect = 1; } while (!isconnect); #ifdef NON_BLOCK_IO on = 1; x = socket_ioctl(httpfd,FIONBIO,&on); debug(F101,"http_open FIONBIO","",x); #endif /* NON_BLOCK_IO */ /* We have succeeded in connecting to the HTTP PROXY. So now we */ /* need to attempt to connect through the proxy to the actual host */ /* If that is successful, we have to pretend that we made a direct */ /* connection to the actual host. */ if ( tcp_http_proxy ) { #ifdef OS2 if (!agent) agent = "Kermit 95"; /* Default user agent */ #else if (!agent) agent = "C-Kermit"; #endif /* OS2 */ if (http_connect(httpfd, tcp_http_proxy_agent ? tcp_http_proxy_agent : agent, NULL, tcp_http_proxy_user, tcp_http_proxy_pwd, 0, proxycopy ) < 0) { http_close(); return(-1); } } #ifdef SO_OOBINLINE /* See note on SO_OOBINLINE in netopen() */ #ifdef datageneral setsockopt(httpfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on); #else #ifdef BSD43 setsockopt(httpfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on); #else #ifdef OSF1 setsockopt(httpfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on); #else #ifdef POSIX setsockopt(httpfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on); #else #ifdef MOTSV88R4 setsockopt(httpfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on); #else #ifdef SOLARIS /* Maybe this applies to all SVR4 versions, but the other (else) way has been compiling and working fine on all the others, so best not to change it. */ setsockopt(httpfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on); #else #ifdef OSK setsockopt(httpfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on); #else #ifdef OS2 { int rc; rc = setsockopt(httpfd, SOL_SOCKET, SO_OOBINLINE, (char *) &on, sizeof on ); debug(F111,"setsockopt SO_OOBINLINE",on ? "on" : "off" ,rc); } #else #ifdef VMS /* or, at least, VMS with gcc */ setsockopt(httpfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on); #else #ifdef CLIX setsockopt(httpfd, SOL_SOCKET, SO_OOBINLINE,(char *) &on, sizeof on); #else setsockopt(httpfd, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on); #endif /* CLIX */ #endif /* VMS */ #endif /* OS2 */ #endif /* OSK */ #endif /* SOLARIS */ #endif /* MOTSV88R4 */ #endif /* POSIX */ #endif /* BSD43 */ #endif /* OSF1 */ #endif /* datageneral */ #endif /* SO_OOBINLINE */ #ifndef NOTCPOPTS #ifndef datageneral #ifdef SOL_SOCKET #ifdef TCP_NODELAY no_delay(ttyfd,tcp_nodelay); #endif /* TCP_NODELAY */ #ifdef SO_KEEPALIVE keepalive(ttyfd,tcp_keepalive); #endif /* SO_KEEPALIVE */ #ifdef SO_LINGER ck_linger(ttyfd,tcp_linger, tcp_linger_tmo); #endif /* SO_LINGER */ #ifdef SO_SNDBUF sendbuf(ttyfd,tcp_sendbuf); #endif /* SO_SNDBUF */ #ifdef SO_RCVBUF recvbuf(ttyfd,tcp_recvbuf); #endif /* SO_RCVBUF */ #endif /* SOL_SOCKET */ #endif /* datageneral */ #endif /* NOTCPOPTS */ #ifndef datageneral /* Find out our own IP address. */ /* We need the l_addr structure for [E]KLOGIN. */ l_slen = sizeof(l_addr); bzero((char *)&l_addr, l_slen); #ifndef EXCELAN if (!getsockname(httpfd, (struct sockaddr *)&l_addr, &l_slen)) { char * s = (char *)inet_ntoa(l_addr.sin_addr); ckstrncpy(myipaddr, s, 20); debug(F110,"getsockname",myipaddr,0); } #endif /* EXCELAN */ #endif /* datageneral */ /* See note in netopen() on Reverse DNS lookups */ if (tcp_rdns == SET_ON) { #ifdef NT if (isWin95()) sleep(1); #endif /* NT */ if (!quiet) { printf(" Reverse DNS Lookup... "); fflush(stdout); } if (host = gethostbyaddr((char *)&r_addr.sin_addr,4,PF_INET)) { char * s; host = ck_copyhostent(host); debug(F100,"http_open gethostbyname != NULL","",0); if (!quiet) { printf("(OK)\n"); fflush(stdout); } s = host->h_name; if (!s) { /* This can happen... */ debug(F100,"http_open host->h_name is NULL","",0); s = ""; } /* Something is wrong with inet_ntoa() on HPUX 10.xx */ /* The compiler says "Integral value implicitly converted to */ /* pointer in assignment." The prototype is right there */ /* in so what's the problem? */ /* Ditto in HP-UX 5.x, but not 8.x or 9.x... */ if (!*s) { /* No name so substitute the address */ debug(F100,"http_open host->h_name is empty","",0); s = inet_ntoa(r_addr.sin_addr); /* Convert address to string */ if (!s) /* Trust No 1 */ s = ""; if (*s) { /* If it worked, use this string */ ckstrncpy(http_ip,s,20); } s = http_ip; /* Otherwise stick with the IP */ if (!*s) /* or failing that */ s = http_host_port; /* the name we were called with. */ } if (*s) /* return the rdns name */ ckmakmsg(rdns_name,rdns_len,s,":",svcname,NULL); if (!quiet && *s #ifndef NOICP && !doconx #endif /* NOICP */ ) { printf(" %s connected on port %s\n",s, ckuitoa(ntohs(service->s_port))); #ifdef BETADEBUG /* This is simply for testing the DNS entries */ if (host->h_aliases) { char ** a = host->h_aliases; while (*a) { printf(" alias => %s\n",*a); a++; } } #endif /* BETADEBUG */ } } else { if (!quiet) printf("Failed.\n"); } } else if (!quiet) printf("(OK)\n"); if (!quiet) fflush(stdout); if ( tcp_http_proxy ) { /* Erase the IP address since we cannot reuse it */ http_ip[0] = '\0'; } else { /* This should already have been done but just in case */ ckstrncpy(http_ip,(char *)inet_ntoa(r_addr.sin_addr),20); } makestr(&http_agent,agent); #ifdef CK_SSL if (use_ssl && ck_ssleay_is_installed()) { if (!ssl_http_init(hostname)) { if (bio_err!=NULL) { BIO_printf(bio_err,"ssl_tn_init() failed\n"); ERR_print_errors(bio_err); } else { fflush(stderr); fprintf(stderr,"ssl_tn_init() failed\n"); ERR_print_errors_fp(stderr); } http_close(); return(-1); } else if ( ck_ssl_http_client(httpfd,hostname) < 0 ) { http_close(); return(-1); } } #endif /* CK_SSL */ #endif /* TCPSOCKET */ return(0); /* Done. */ } int #ifdef CK_ANSIC http_close(VOID) #else /* CK_ANSIC */ http_close() #endif /* CK_ANSIC */ { int x = 0; debug(F101,"http_close","",httpfd); #ifdef HTTP_BUFFERING http_count = 0; http_bufp = 0; #endif /* HTTP_BUFFERING */ if (httpfd == -1) /* Was open? */ return(0); /* Wasn't. */ #ifndef OS2 if (httpfd > -1) /* Was. */ #endif /* OS2 */ { #ifdef CK_SSL if (tls_http_active_flag) { if (ssl_debug_flag) BIO_printf(bio_err,"calling SSL_shutdown\n"); SSL_shutdown(tls_http_con); tls_http_active_flag = 0; } #endif /* CK_SSL */ #ifdef TCPIPLIB x = socket_close(httpfd); /* Close it. */ #else #ifndef OS2 x = close(httpfd); #endif /* OS2 */ #endif /* TCPIPLIB */ } httpfd = -1; /* Mark it as closed. */ /* do not erase http_host_port, http_ip, http_port so they */ /* can be used by http_reopen() */ return(x); } /* http_tol() * Call with s = pointer to string, n = length. * Returns number of bytes actually written on success, or * -1 on i/o error, -2 if called improperly. */ int http_tol(s,n) CHAR *s; int n; { int count = 0; int len = n; int try = 0; if (httpfd == -1) { debug(F100,"http_tol socket is closed","",0); return -1; } debug(F101,"http_tol TCPIPLIB ttnet","",ttnet); #ifdef COMMENT ckhexdump("http_tol",s,n); #endif /* COMMENT */ #ifdef CK_SSL if (tls_http_active_flag) { int error, r; /* Write using SSL */ ssl_retry: r = SSL_write(tls_http_con, s, len /* >1024?1024:len */); switch (SSL_get_error(tls_http_con,r)) { case SSL_ERROR_NONE: debug(F111,"http_tol","SSL_write",r); if ( r == len ) return(n); s += r; len -= r; goto ssl_retry; case SSL_ERROR_WANT_WRITE: debug(F100,"http_tol SSL_ERROR_WANT_WRITE","",0); return(-1); case SSL_ERROR_WANT_READ: debug(F100,"http_tol SSL_ERROR_WANT_READ","",0); return(-1); case SSL_ERROR_SYSCALL: if ( r == 0 ) { /* EOF */ http_close(); return(-2); } else { int rc = -1; #ifdef NT int gle = GetLastError(); debug(F111,"http_tol SSL_ERROR_SYSCALL", "GetLastError()",gle); rc = os2socketerror(gle); if (rc == -1) rc = -2; else if ( rc == -2 ) return -1; #endif /* NT */ return(rc); } case SSL_ERROR_WANT_X509_LOOKUP: debug(F100,"http_tol SSL_ERROR_WANT_X509_LOOKUP","",0); http_close(); return(-2); case SSL_ERROR_SSL: debug(F100,"http_tol SSL_ERROR_SSL","",0); http_close(); return(-2); case SSL_ERROR_ZERO_RETURN: debug(F100,"http_tol SSL_ERROR_ZERO_RETURN","",0); http_close(); return(-2); default: debug(F100,"http_tol SSL_ERROR_?????","",0); http_close(); return(-2); } } #endif /* CK_SSL */ http_tol_retry: try++; /* Increase the try counter */ { #ifdef BSDSELECT fd_set wfds; struct timeval tv; debug(F101,"http_tol BSDSELECT","",0); tv.tv_usec = 0L; tv.tv_sec=30; #ifdef NT WSASafeToCancel = 1; #endif /* NT */ #ifdef STREAMING do_select: #endif /* STREAMING */ FD_ZERO(&wfds); FD_SET(httpfd, &wfds); if (select(FD_SETSIZE, NULL, #ifdef __DECC #ifndef __DECC_VER (int *) #endif /* __DECC_VER */ #endif /* __DECC */ &wfds, NULL, &tv) < 0) { int s_errno = socket_errno; debug(F101,"http_tol select failed","",s_errno); #ifdef BETADEBUG printf("http_tol select failed: %d\n", s_errno); #endif /* BETADEBUG */ #ifdef NT WSASafeToCancel = 0; if (!win95selectbug) #endif /* NT */ return(-1); } if (!FD_ISSET(httpfd, &wfds)) { #ifdef STREAMING if (streaming) goto do_select; #endif /* STREAMING */ debug(F111,"http_tol","!FD_ISSET",ttyfd); #ifdef NT WSASafeToCancel = 0; if (!win95selectbug) #endif /* NT */ return(-1); } #ifdef NT WSASafeToCancel = 0; #endif /* NT */ #else /* BSDSELECT */ #ifdef IBMSELECT { int tries = 0; debug(F101,"http_tol IBMSELECT","",0); while (select(&httpfd, 0, 1, 0, 1000) != 1) { int count; if (tries++ >= 60) { /* if after 60 seconds we can't get permission to write */ debug(F101,"http_tol select failed","",socket_errno); return(-1); } #ifdef COMMENT if ((count = http_tchk()) < 0) { debug(F111,"http_tol","http_tchk()",count); return(count); } #endif /* COMMENT */ } } #endif /* IBMSELECT */ #endif /* BSDSELECT */ #ifdef TCPIPLIB if ((count = socket_write(httpfd,s,n)) < 0) { int s_errno = socket_errno; /* maybe a function */ debug(F101,"http_tol socket_write error","",s_errno); #ifdef OS2 if (os2socketerror(s_errno) < 0) return(-2); #endif /* OS2 */ return(-1); /* Call it an i/o error */ } #else /* TCPIPLIB */ if ((count = write(httpfd,s,n)) < 0) { debug(F101,"http_tol socket_write error","",errno); return(-1); /* Call it an i/o error */ } #endif /* TCPIPLIB */ if (count < n) { debug(F111,"http_tol socket_write",s,count); if (try > 25) { /* don't try more than 25 times */ debug(F100,"http_tol tried more than 25 times","",0); return(-1); } if (count > 0) { s += count; n -= count; } debug(F111,"http_tol retry",s,n); goto http_tol_retry; } else { debug(F111,"http_tol socket_write",s,count); return(len); /* success - return total length */ } } } int http_inc(timo) int timo; { int x=-1; unsigned char c; /* The locals. */ if (httpfd == -1) { #ifdef HTTP_BUFFERING http_count = 0; http_bufp = 0; #endif /* HTTP_BUFFERING */ debug(F100,"http_inc socket is closed","",0); return(-2); } #ifdef CK_SSL /* * In the case of OpenSSL, it is possible that there is still * data waiting in the SSL session buffers that has not yet * been read by Kermit. If this is the case we must process * it without calling select() because select() will not return * with an indication that there is data to be read from the * socket. If there is no data pending in the SSL session * buffers then fall through to the select() code and wait for * some data to arrive. */ if (tls_http_active_flag) { int error; x = SSL_pending(tls_http_con); if (x < 0) { debug(F111,"http_inc","SSL_pending error",x); http_close(); return(-1); } else if ( x > 0 ) { ssl_read: x = SSL_read(tls_http_con, &c, 1); error = SSL_get_error(tls_http_con,x); switch (error) { case SSL_ERROR_NONE: debug(F111,"http_inc SSL_ERROR_NONE","x",x); if (x > 0) { #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ return(c); /* Return character. */ } else if (x < 0) { #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ return(-1); } else { http_close(); #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ return(-2); } case SSL_ERROR_WANT_WRITE: debug(F100,"http_inc SSL_ERROR_WANT_WRITE","",0); #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ return(-1); case SSL_ERROR_WANT_READ: debug(F100,"http_inc SSL_ERROR_WANT_READ","",0); #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ return(-1); case SSL_ERROR_SYSCALL: if ( x == 0 ) { /* EOF */ http_close(); #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ return(-2); } else { int rc = -1; #ifdef NT int gle = GetLastError(); debug(F111,"http_inc SSL_ERROR_SYSCALL", "GetLastError()",gle); rc = os2socketerror(gle); if (rc == -1) rc = -2; else if ( rc == -2 ) rc = -1; #endif /* NT */ #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ return(rc); } case SSL_ERROR_WANT_X509_LOOKUP: debug(F100,"http_inc SSL_ERROR_WANT_X509_LOOKUP","",0); http_close(); #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ return(-2); case SSL_ERROR_SSL: debug(F100,"http_inc SSL_ERROR_SSL","",0); #ifdef COMMENT http_close(); #endif /* COMMENT */ #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ return(-2); case SSL_ERROR_ZERO_RETURN: debug(F100,"http_inc SSL_ERROR_ZERO_RETURN","",0); http_close(); #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ return(-2); default: debug(F100,"http_inc SSL_ERROR_?????","",0); http_close(); #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ return(-2); } } } #endif /* CK_SSL */ #ifdef HTTP_BUFFERING /* Skip all the select() stuff if we have bytes buffered locally */ if (http_count > 0) goto getfrombuffer; #endif /* HTTP_BUFFERING */ { #ifdef BSDSELECT fd_set rfds; struct timeval tv; int timeout = timo < 0 ? -timo : 1000 * timo; debug(F101,"http_inc BSDSELECT","",timo); for ( ; timeout >= 0; timeout -= (timo ? 100 : 0)) { int rc; debug(F111,"http_inc","timeout",timeout); /* Don't move select() initialization out of the loop. */ FD_ZERO(&rfds); FD_SET(httpfd, &rfds); tv.tv_sec = tv.tv_usec = 0L; if (timo) tv.tv_usec = (long) 100000L; else tv.tv_sec = 30; #ifdef NT WSASafeToCancel = 1; #endif /* NT */ rc = select(FD_SETSIZE, #ifndef __DECC (fd_set *) #endif /* __DECC */ &rfds, NULL, NULL, &tv); if (rc < 0) { int s_errno = socket_errno; debug(F111,"http_inc","select",rc); debug(F111,"http_inc","socket_errno",s_errno); #ifdef HTTP_BUFFERING http_count = 0; http_bufp = 0; #endif /* HTTP_BUFFERING */ if (s_errno) return(-1); } debug(F111,"http_inc","select",rc); #ifdef NT WSASafeToCancel = 0; #endif /* NT */ if (FD_ISSET(httpfd, &rfds)) { x = 0; break; } else { /* If waiting forever we have no way of knowing if the */ /* socket closed so try writing a 0-length TCP packet */ /* which should force an error if the socket is closed */ if (!timo) { #ifdef TCPIPLIB if ((rc = socket_write(httpfd,"",0)) < 0) { #ifdef HTTP_BUFFERING http_count = 0; http_bufp = 0; #endif /* HTTP_BUFFERING */ int s_errno = socket_errno; debug(F101,"http_inc socket_write error","",s_errno); #ifdef OS2 if (os2socketerror(s_errno) < 0) return(-2); #endif /* OS2 */ return(-1); /* Call it an i/o error */ } #else /* TCPIPLIB */ if ((rc = write(httpfd,"",0)) < 0) { #ifdef HTTP_BUFFERING http_count = 0; http_bufp = 0; #endif /* HTTP_BUFFERING */ debug(F101,"http_inc socket_write error","",errno); return(-1); /* Call it an i/o error */ } #endif /* TCPIPLIB */ } continue; } } #ifdef NT WSASafeToCancel = 0; #endif /* NT */ #else /* !BSDSELECT */ #ifdef IBMSELECT /* Was used by OS/2, currently not used, but might come in handy some day... ... and it came in handy! For our TCP/IP layer, it avoids all the fd_set and timeval stuff since this is the only place where it is used. */ int socket = httpfd; int timeout = timo < 0 ? -timo : 1000 * timo; debug(F101,"http_inc IBMSELECT","",timo); for ( ; timeout >= 0; timeout -= (timo ? 100 : 0)) { if (select(&socket, 1, 0, 0, 100L) == 1) { x = 0; break; } } #else /* !IBMSELECT */ SELECT is required for this code #endif /* IBMSELECT */ #endif /* BSDSELECT */ } if (timo && x < 0) { /* select() timed out */ #ifdef HTTP_BUFFERING http_count = 0; http_bufp = 0; #endif /* HTTP_BUFFERING */ debug(F100,"http_inc select() timed out","",0); return(-1); /* Call it an i/o error */ } #ifdef CK_SSL if ( tls_http_active_flag ) { int error; ssl_read2: x = SSL_read(tls_http_con, &c, 1); error = SSL_get_error(tls_http_con,x); switch (error) { case SSL_ERROR_NONE: debug(F111,"http_inc SSL_ERROR_NONE","x",x); if (x > 0) { #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ return(c); /* Return character. */ } else if (x < 0) { #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ return(-1); } else { http_close(); #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ return(-2); } case SSL_ERROR_WANT_WRITE: debug(F100,"http_inc SSL_ERROR_WANT_WRITE","",0); #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ return(-1); case SSL_ERROR_WANT_READ: debug(F100,"http_inc SSL_ERROR_WANT_READ","",0); #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ return(-1); case SSL_ERROR_SYSCALL: if ( x == 0 ) { /* EOF */ http_close(); #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ return(-2); } else { int rc = -1; #ifdef NT int gle = GetLastError(); debug(F111,"http_inc SSL_ERROR_SYSCALL", "GetLastError()",gle); rc = os2socketerror(gle); if (rc == -1) rc = -2; else if ( rc == -2 ) rc = -1; #endif /* NT */ #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ return(rc); } case SSL_ERROR_WANT_X509_LOOKUP: debug(F100,"http_inc SSL_ERROR_WANT_X509_LOOKUP","",0); http_close(); #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ return(-2); case SSL_ERROR_SSL: debug(F100,"http_inc SSL_ERROR_SSL","",0); #ifdef COMMENT http_close(); #endif /* COMMENT */ #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ return(-2); case SSL_ERROR_ZERO_RETURN: debug(F100,"http_inc SSL_ERROR_ZERO_RETURN","",0); http_close(); #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ return(-2); default: debug(F100,"http_inc SSL_ERROR_?????","",0); http_close(); #ifdef OS2 ReleaseTCPIPMutex(); #endif /* OS2 */ return(-2); } } #endif /* CK_SSL */ #ifdef HTTP_BUFFERING /* Buffering code added by fdc 15 Dec 2005 for non-SSL case only because HTTP GETs were orders of magnitude too slow due to the single-byte read()s. The file-descriptor swapping is pretty gross, but the more elegant solution (calling a nettchk() like routine with the fd as a parameter) doesn't work, because nettchk() relies on too many other routines that, like itself, are hardwired for ttyfd. */ getfrombuffer: if (--http_count >= 0) { c = http_inbuf[http_bufp++]; x = 1; } else { int savefd; savefd = ttyfd; ttyfd = httpfd; x = nettchk(); ttyfd = savefd; debug(F101,"http_inc nettchk","",x); if (x > HTTP_INBUFLEN) x = HTTP_INBUFLEN; #ifdef TCPIPLIB x = socket_read(httpfd,http_inbuf,x); #else /* Not TCPIPLIB */ x = read(httpfd,http_inbuf,x); #endif /* TCPIPLIB */ http_count = 0; http_bufp = 0; if (x > 0) { c = http_inbuf[http_bufp++]; http_count = x - 1; } } #else /* Not HTTP_BUFFERING */ #ifdef TCPIPLIB x = socket_read(httpfd,&c,1); #else /* Not TCPIPLIB */ x = read(httpfd,&c,1); #endif /* TCPIPLIB */ #endif /* HTTP_BUFFERING */ if (x <= 0) { int s_errno = socket_errno; debug(F101,"ttbufr socket_read","",x); debug(F101,"ttbufr socket_errno","",s_errno); #ifdef OS2 if (x == 0 || os2socketerror(s_errno) < 0) { http_close(); ReleaseTCPIPMutex(); return(-2); } ReleaseTCPIPMutex(); return(-1); #else /* OS2 */ http_close(); /* *** *** */ return(-2); #endif /* OS2 */ } return(c); } void #ifdef CK_ANSIC http_set_code_reply(char * msg) #else http_set_code_reply(msg) char * msg; #endif /* CK_ANSIC */ { char * p = msg; char buf[16]; int i=0; while ( *p != SP && *p != NUL ) { buf[i] = *p; p++; i++; } http_code = atoi(buf); while ( *p == SP ) p++; ckstrncpy(http_reply_str,p,HTTPBUFLEN); } int #ifdef CK_ANSIC http_get(char * agent, char ** hdrlist, char * user, char * pwd, char array, char * local, char * remote, int stdio) #else http_get(agent, hdrlist, user, pwd, array, local, remote, stdio) char * agent; char ** hdrlist; char * user; char * pwd; char array; char * local; char * remote; int stdio; #endif /* CK_ANSIC */ { char * request = NULL; int i, j, len = 0, hdcnt = 0, rc = 0; int ch; int http_fnd = 0; char buf[HTTPBUFLEN], *p; int nullline; #ifdef OS2 struct utimbuf u_t; #else /* OS2 */ #ifdef SYSUTIMEH struct utimbuf u_t; #else struct utimbuf { time_t atime; time_t mtime; } u_t; #endif /* SYSUTIMH */ #endif /* OS2 */ time_t mod_t = 0; time_t srv_t = 0; time_t local_t = 0; char passwd[64]; char b64in[128]; char b64out[256]; char * headers[HTTPHEADCNT]; int closecon = 0; int chunked = 0; int zfile = 0; int first = 1; #ifdef DEBUG if (deblog) { debug(F101,"http_get httpfd","",httpfd); debug(F110,"http_agent",agent,0); debug(F110,"http_user",user,0); debug(F110,"http_local",local,0); debug(F110,"http_remote",remote,0); } #endif /* DEBUG */ if (!remote) remote = ""; if (httpfd == -1) return(-1); if (array) { for (i = 0; i < HTTPHEADCNT; i++) headers[i] = NULL; } len = 8; /* GET */ len += strlen(HTTP_VERSION); len += strlen(remote); len += 16; if (hdrlist) { for (i = 0; hdrlist[i]; i++) len += strlen(hdrlist[i]) + 2; } len += (int) strlen(http_host_port) + 8; if (agent) len += 13 + strlen(agent); if (user) { if (!pwd) { readpass("Password: ",passwd,64); pwd = passwd; } ckmakmsg(b64in,sizeof(b64in),user,":",pwd,NULL); j = b8tob64(b64in,strlen(b64in),b64out,256); memset(pwd,0,strlen(pwd)); /* NOT PORTABLE */ if (j < 0) return(-1); b64out[j] = '\0'; len += j + 24; } #ifdef HTTP_CLOSE len += 19; /* Connection: close */ #endif len += 3; /* blank line + null */ request = malloc(len); if (!request) return(-1); sprintf(request,"GET %s %s\r\n",remote,HTTP_VERSION); /* safe */ ckstrncat(request,"Host: ", len); ckstrncat(request,http_host_port, len); ckstrncat(request,"\r\n",len); if (agent) { ckstrncat(request,"User-agent: ",len); ckstrncat(request,agent,len); ckstrncat(request,"\r\n",len); } if (user) { ckstrncat(request,"Authorization: Basic ",len); ckstrncat(request,b64out,len); ckstrncat(request,"\r\n",len); } if ( hdrlist ) { for (i = 0; hdrlist[i]; i++) { ckstrncat(request,hdrlist[i],len); ckstrncat(request,"\r\n",len); } } #ifdef HTTP_CLOSE ckstrncat(request,"Connection: close\r\n",len); #endif ckstrncat(request,"\r\n",len); getreq: if (http_tol((CHAR *)request,strlen(request)) < 0) { http_close(); if ( first ) { first--; http_reopen(); goto getreq; } rc = -1; goto getexit; } /* Process the headers */ local_t = time(NULL); nullline = 0; i = 0; len = -1; while (!nullline && (ch = http_inc(0)) >= 0 && i < HTTPBUFLEN) { buf[i] = ch; if ( buf[i] == 10 ) { /* found end of line */ if (i > 0 && buf[i-1] == 13) i--; if (i < 1) nullline = 1; buf[i] = '\0'; if (array && !nullline && hdcnt < HTTPHEADCNT) makestr(&headers[hdcnt++],buf); if (!ckstrcmp(buf,"HTTP",4,0)) { http_fnd = 1; j = ckindex(" ",buf,0,0,0); p = &buf[j]; while ( isspace(*p) ) p++; switch ( p[0] ) { case '1': /* Informational message */ break; case '2': /* Success */ break; case '3': /* Redirection */ case '4': /* Client failure */ case '5': /* Server failure */ default: /* Unknown */ if (!quiet) printf("Failure: Server reports %s\n",p); rc = -1; local = NULL; } http_set_code_reply(p); #ifdef CMDATE2TM } else if (!ckstrcmp(buf,"Last-Modified",13,0)) { mod_t = http_date(&buf[15]); } else if (!ckstrcmp(buf,"Date",4,0)) { srv_t = http_date(&buf[4]); #endif /* CMDATE2TM */ } else if (!ckstrcmp(buf,"Connection:",11,0)) { if ( ckindex("close",buf,11,0,0) != 0 ) closecon = 1; } else if (!ckstrcmp(buf,"Content-Length:",15,0)) { len = atoi(&buf[16]); } else if (!ckstrcmp(buf,"Transfer-Encoding:",18,0)) { if ( ckindex("chunked",buf,18,0,0) != 0 ) chunked = 1; debug(F101,"http_get chunked","",chunked); } i = 0; } else { i++; } } if (ch < 0 && first) { first--; http_close(); http_reopen(); goto getreq; } if (http_fnd == 0) { rc = -1; closecon = 1; goto getexit; } /* Now we have the contents of the file */ if ( local && local[0] ) { if (zopeno(ZOFILE,local,NULL,NULL)) zfile = 1; else rc = -1; } if ( chunked ) { while ((len = http_get_chunk_len()) > 0) { while (len && (ch = http_inc(0)) >= 0) { len--; if ( zfile ) zchout(ZOFILE,(CHAR)ch); if ( stdio ) conoc((CHAR)ch); } if ((ch = http_inc(0)) != CR) break; if ((ch = http_inc(0)) != LF) break; } } else { while (len && (ch = http_inc(0)) >= 0) { len--; if ( zfile ) zchout(ZOFILE,(CHAR)ch); if ( stdio ) conoc((CHAR)ch); } } if ( zfile ) zclose(ZOFILE); if ( chunked ) { /* Parse Trailing Headers */ nullline = 0; while (!nullline && (ch = http_inc(0)) >= 0 && i < HTTPBUFLEN) { buf[i] = ch; if ( buf[i] == 10 ) { /* found end of line */ if (i > 0 && buf[i-1] == 13) i--; if (i < 1) nullline = 1; buf[i] = '\0'; if (array && !nullline && hdcnt < HTTPHEADCNT) makestr(&headers[hdcnt++],buf); #ifdef CMDATE2TM if (!ckstrcmp(buf,"Last-Modified",13,0)) { mod_t = http_date(&buf[15]); } else if (!ckstrcmp(buf,"Date",4,0)) { srv_t = http_date(&buf[4]); } #endif /* CMDATE2TM */ else if (!ckstrcmp(buf,"Connection:",11,0)) { if ( ckindex("close",buf,11,0,0) != 0 ) closecon = 1; } i = 0; } else { i++; } } } if ( zfile ) { /* Set timestamp */ #ifndef NOSETTIME #ifdef OS2 u_t.actime = srv_t ? srv_t : local_t; u_t.modtime = mod_t ? mod_t : local_t; #else /* OS2 */ #ifdef SYSUTIMEH u_t.actime = srv_t ? srv_t : local_t; u_t.modtime = mod_t ? mod_t : local_t; #else #ifdef BSD44 u_t[0].tv_sec = srv_t ? srv_t : local_t; u_t[1].tv_sec = mod_t ? mod_t : local_t; #else u_t.mtime = srv_t ? srv_t : local_t; u_t.atime = mod_t ? mod_t : local_t; #endif /* BSD44 */ #endif /* SYSUTIMEH */ #endif /* OS2 */ utime(local,&u_t); #endif /* NOSETTIME */ } getexit: if (array) http_mkarray(headers,hdcnt,array); if ( closecon ) http_close(); free(request); for (i = 0; i < hdcnt; i++) { if (headers[i]) free(headers[i]); } return(rc); } int #ifdef CK_ANSIC http_head(char * agent, char ** hdrlist, char * user, char * pwd, char array, char * local, char * remote, int stdio) #else http_head(agent, hdrlist, user, pwd, array, local, remote, stdio) char * agent; char ** hdrlist; char * user; char * pwd; char array; char * local; char * remote; int stdio; #endif /* CK_ANSIC */ { char * request = NULL; int i, j, len = 0, hdcnt = 0, rc = 0; int ch; int http_fnd = 0; char buf[HTTPBUFLEN], *p; int nullline; time_t mod_t; time_t srv_t; time_t local_t; char passwd[64]; char b64in[128]; char b64out[256]; char * headers[HTTPHEADCNT]; int closecon = 0; int first = 1; if (httpfd == -1) return(-1); if (array) { for (i = 0; i < HTTPHEADCNT; i++) headers[i] = NULL; } len = 9; /* HEAD */ len += strlen(HTTP_VERSION); len += strlen(remote); len += 16; if ( hdrlist ) { for (i = 0; hdrlist[i]; i++) len += strlen(hdrlist[i]) + 2; } len += strlen(http_host_port) + 8; if (agent) len += 13 + strlen(agent); if (user) { if (!pwd) { readpass("Password: ",passwd,64); pwd = passwd; } ckmakmsg(b64in,sizeof(b64in),user,":",pwd,NULL); j = b8tob64(b64in,strlen(b64in),b64out,256); memset(pwd,0,strlen(pwd)); /* NOT PORTABLE */ if (j < 0) return(-1); b64out[j] = '\0'; len += j + 24; } #ifdef HTTP_CLOSE len += 19; /* Connection: close */ #endif len += 3; /* blank line + null */ request = (char *)malloc(len); if (!request) return(-1); sprintf(request,"HEAD %s %s\r\n",remote,HTTP_VERSION); ckstrncat(request,"Host: ", len); ckstrncat(request,http_host_port, len); ckstrncat(request,"\r\n",len); if (agent) { ckstrncat(request,"User-agent: ",len); ckstrncat(request,agent,len); ckstrncat(request,"\r\n",len); } if (user) { ckstrncat(request,"Authorization: Basic ",len); ckstrncat(request,b64out,len); ckstrncat(request,"\r\n",len); } if ( hdrlist ) { for (i = 0; hdrlist[i]; i++) { ckstrncat(request,hdrlist[i],len); ckstrncat(request,"\r\n",len); } } #ifdef HTTP_CLOSE ckstrncat(request,"Connection: close\r\n",len); #endif ckstrncat(request,"\r\n",len); if ( local && local[0] ) { if (!zopeno(ZOFILE,local,NULL,NULL)) { free(request); return(-1); } } headreq: if (http_tol((CHAR *)request,strlen(request)) < 0) { http_close(); if ( first ) { first--; http_reopen(); goto headreq; } rc = -1; goto headexit; } /* Process the headers */ local_t = time(NULL); nullline = 0; i = 0; while (!nullline && (ch = http_inc(0)) >= 0 && i < HTTPBUFLEN) { buf[i] = ch; if (buf[i] == 10) { /* found end of line */ if (i > 0 && buf[i-1] == 13) i--; if (i < 1) nullline = 1; buf[i] = '\0'; if (array && !nullline && hdcnt < HTTPHEADCNT) makestr(&headers[hdcnt++],buf); if (!ckstrcmp(buf,"HTTP",4,0)) { http_fnd = 1; j = ckindex(" ",buf,0,0,0); p = &buf[j]; while (isspace(*p)) p++; switch (p[0]) { case '1': /* Informational message */ break; case '2': /* Success */ break; case '3': /* Redirection */ case '4': /* Client failure */ case '5': /* Server failure */ default: /* Unknown */ if (!quiet) printf("Failure: Server reports %s\n",p); rc = -1; } http_set_code_reply(p); } else { if (!ckstrcmp(buf,"Connection:",11,0)) { if ( ckindex("close",buf,11,0,0) != 0 ) closecon = 1; } if ( local && local[0] ) { zsout(ZOFILE,buf); zsout(ZOFILE,"\r\n"); } if (stdio) printf("%s\r\n",buf); } i = 0; } else { i++; } } if (ch < 0 && first) { first--; http_close(); http_reopen(); goto headreq; } if ( http_fnd == 0 ) rc = -1; if (array) http_mkarray(headers,hdcnt,array); headexit: if ( local && local[0] ) zclose(ZOFILE); if (closecon) http_close(); free(request); for (i = 0; i < hdcnt; i++) { if (headers[i]) free(headers[i]); } return(rc); } int #ifdef CK_ANSIC http_index(char * agent, char ** hdrlist, char * user, char * pwd, char array, char * local, char * remote, int stdio) #else http_index(agent, hdrlist, user, pwd, array, local, remote, stdio) char * agent; char ** hdrlist; char * user; char * pwd; char array; char * local; char * remote; int stdio; #endif /* CK_ANSIC */ { char * request = NULL; int i, j, len = 0, hdcnt = 0, rc = 0; int ch; int http_fnd = 0; char buf[HTTPBUFLEN], *p; int nullline; time_t mod_t; time_t srv_t; time_t local_t; char passwd[64]; char b64in[128]; char b64out[256]; char * headers[HTTPHEADCNT]; int closecon = 0; int chunked = 0; int zfile = 0; int first = 1; if (httpfd == -1) return(-1); if (array) { for (i = 0; i < HTTPHEADCNT; i++) headers[i] = NULL; } len = 10; /* INDEX */ len += strlen(HTTP_VERSION); len += strlen(remote); len += 16; if ( hdrlist ) { for (i = 0; hdrlist[i]; i++) len += strlen(hdrlist[i]) + 2; } len += strlen(http_host_port) + 8; if (agent) len += 13 + strlen(agent); if (user) { if (!pwd) { readpass("Password: ",passwd,64); pwd = passwd; } ckmakmsg(b64in,sizeof(b64in),user,":",pwd,NULL); j = b8tob64(b64in,strlen(b64in),b64out,256); memset(pwd,0,strlen(pwd)); if (j < 0) return(-1); b64out[j] = '\0'; len += j + 24; } #ifdef HTTP_CLOSE len += 19; /* Connection: close */ #endif len += 3; /* blank line + null */ request = malloc(len); if (!request) return(-1); sprintf(request,"INDEX %s\r\n",HTTP_VERSION); ckstrncat(request,"Host: ", len); ckstrncat(request,http_host_port, len); ckstrncat(request,"\r\n",len); if (agent) { ckstrncat(request,"User-agent: ",len); ckstrncat(request,agent,len); ckstrncat(request,"\r\n",len); } if (user) { ckstrncat(request,"Authorization: Basic ",len); ckstrncat(request,b64out,len); ckstrncat(request,"\r\n",len); } if ( hdrlist ) { for (i = 0; hdrlist[i]; i++) { ckstrncat(request,hdrlist[i],len); ckstrncat(request,"\r\n",len); } } #ifdef HTTP_CLOSE ckstrncat(request,"Connection: close\r\n",len); #endif ckstrncat(request,"\r\n",len); indexreq: if (http_tol((CHAR *)request,strlen(request)) < 0) { http_close(); if ( first ) { first--; http_reopen(); goto indexreq; } rc = -1; goto indexexit; } /* Process the headers */ local_t = time(NULL); nullline = 0; i = 0; len = -1; while (!nullline && (ch = http_inc(0)) >= 0 && i < HTTPBUFLEN) { buf[i] = ch; if (buf[i] == 10) { /* found end of line */ if (i > 0 && buf[i-1] == 13) i--; if (i < 1) nullline = 1; buf[i] = '\0'; if (array && !nullline && hdcnt < HTTPHEADCNT) makestr(&headers[hdcnt++],buf); if (!ckstrcmp(buf,"HTTP",4,0)) { http_fnd = 1; j = ckindex(" ",buf,0,0,0); p = &buf[j]; while (isspace(*p)) p++; switch ( p[0] ) { case '1': /* Informational message */ break; case '2': /* Success */ break; case '3': /* Redirection */ case '4': /* Client failure */ case '5': /* Server failure */ default: /* Unknown */ if (!quiet) printf("Failure: Server reports %s\n",p); rc = -1; } http_set_code_reply(p); } else if ( !nullline ) { if (!ckstrcmp(buf,"Connection:",11,0)) { if ( ckindex("close",buf,11,0,0) != 0 ) closecon = 1; } else if (!ckstrcmp(buf,"Content-Length:",15,0)) { len = atoi(&buf[16]); } else if (!ckstrcmp(buf,"Transfer-Encoding:",18,0)) { if ( ckindex("chunked",buf,18,0,0) != 0 ) chunked = 1; debug(F101,"http_index chunked","",chunked); } printf("%s\n",buf); } i = 0; } else { i++; } } if (ch < 0 && first) { first--; http_close(); http_reopen(); goto indexreq; } if ( http_fnd == 0 ) { rc = -1; closecon = 1; goto indexexit; } /* Now we have the contents of the file */ if ( local && local[0] ) { if (zopeno(ZOFILE,local,NULL,NULL)) zfile = 1; else rc = -1; } if ( chunked ) { while ((len = http_get_chunk_len()) > 0) { while (len && (ch = http_inc(0)) >= 0) { len--; if ( zfile ) zchout(ZOFILE,(CHAR)ch); if ( stdio ) conoc((CHAR)ch); } if ((ch = http_inc(0)) != CR) break; if ((ch = http_inc(0)) != LF) break; } } else { while (len && (ch = http_inc(0)) >= 0) { len--; if ( zfile ) zchout(ZOFILE,(CHAR)ch); if ( stdio ) conoc((CHAR)ch); } } if ( zfile ) zclose(ZOFILE); if ( chunked ) { /* Parse Trailing Headers */ nullline = 0; while (!nullline && (ch = http_inc(0)) >= 0 && i < HTTPBUFLEN) { buf[i] = ch; if ( buf[i] == 10 ) { /* found end of line */ if (i > 0 && buf[i-1] == 13) i--; if (i < 1) nullline = 1; buf[i] = '\0'; if (array && !nullline && hdcnt < HTTPHEADCNT) makestr(&headers[hdcnt++],buf); if (!ckstrcmp(buf,"Connection:",11,0)) { if ( ckindex("close",buf,11,0,0) != 0 ) closecon = 1; } i = 0; } else { i++; } } } rc = 0; indexexit: if (array) http_mkarray(headers,hdcnt,array); if (closecon) http_close(); free(request); for (i = 0; i < hdcnt; i++) { if (headers[i]) free(headers[i]); } return(rc); } int #ifdef CK_ANSIC http_put(char * agent, char ** hdrlist, char * mime, char * user, char * pwd, char array, char * local, char * remote, char * dest, int stdio) #else http_put(agent, hdrlist, mime, user, pwd, array, local, remote, dest, stdio) char * agent; char ** hdrlist; char * mime; char * user; char * pwd; char array; char * local; char * remote; char * dest; int stdio; #endif /* CK_ANSIC */ { char * request=NULL; int i, j, len = 0, hdcnt = 0, rc = 0; int ch; int http_fnd = 0; char buf[HTTPBUFLEN], *p; int nullline; time_t mod_t; time_t srv_t; time_t local_t; char passwd[64]; char b64in[128]; char b64out[256]; int filelen; char * headers[HTTPHEADCNT]; int closecon = 0; int chunked = 0; int first = 1; int zfile = 0; if (httpfd == -1) return(-1); if (!mime) mime = ""; if (!remote) remote = ""; if (!local) local = ""; if (!*local) return(-1); if (array) { for (i = 0; i < HTTPHEADCNT; i++) headers[i] = NULL; } filelen = zchki(local); if (filelen < 0) return(-1); /* Compute length of request header */ len = 8; /* PUT */ len += strlen(HTTP_VERSION); len += strlen(remote); len += 16; if ( hdrlist ) { for (i = 0; hdrlist[i]; i++) len += strlen(hdrlist[i]) + 2; } len += strlen(http_host_port) + 8; if (agent) len += 13 + strlen(agent); if (user) { if (!pwd) { readpass("Password: ",passwd,64); pwd = passwd; } ckmakmsg(b64in,sizeof(b64in),user,":",pwd,NULL); j = b8tob64(b64in,strlen(b64in),b64out,256); memset(pwd,0,strlen(pwd)); if (j < 0) return(-1); b64out[j] = '\0'; len += j + 24; } len += 16 + strlen(mime); /* Content-type: */ len += 32; /* Content-length: */ len += 32; /* Date: */ #ifdef HTTP_CLOSE len += 19; /* Connection: close */ #endif len += 3; /* blank line + null */ request = malloc(len); if (!request) return(-1); sprintf(request,"PUT %s %s\r\n",remote,HTTP_VERSION); ckstrncat(request,"Date: ",len); #ifdef CMDATE2TM ckstrncat(request,http_now(),len); #else ckstrncat(request,...,len); #endif /* CMDATE2TM */ ckstrncat(request,"\r\n",len); ckstrncat(request,"Host: ", len); ckstrncat(request,http_host_port, len); ckstrncat(request,"\r\n",len); if (agent) { ckstrncat(request,"User-agent: ",len); ckstrncat(request,agent,len); ckstrncat(request,"\r\n",len); } if (user) { ckstrncat(request,"Authorization: Basic ",len); ckstrncat(request,b64out,len); ckstrncat(request,"\r\n",len); } if ( hdrlist ) { for (i = 0; hdrlist[i]; i++) { ckstrncat(request,hdrlist[i],len); ckstrncat(request,"\r\n",len); } } ckstrncat(request,"Content-type: ",len); ckstrncat(request,mime,len); ckstrncat(request,"\r\n",len); sprintf(buf,"Content-length: %d\r\n",filelen); /* safe */ ckstrncat(request,buf,len); #ifdef HTTP_CLOSE ckstrncat(request,"Connection: close\r\n",len); #endif ckstrncat(request,"\r\n",len); /* Now we have the contents of the file */ if (zopeni(ZIFILE,local)) { putreq: /* Send request */ if (http_tol((CHAR *)request,strlen(request)) <= 0) { http_close(); if ( first ) { first--; http_reopen(); goto putreq; } zclose(ZIFILE); rc = -1; goto putexit; } /* Request headers have been sent */ i = 0; while (zchin(ZIFILE,&ch) == 0) { buf[i++] = ch; if (i == HTTPBUFLEN) { if (http_tol((CHAR *)buf,HTTPBUFLEN) <= 0) { http_close(); if ( first ) { first--; http_reopen(); goto putreq; } } i = 0; } } if (i > 0) { if (http_tol((CHAR *)buf,i) < 0) { http_close(); if ( first ) { first--; http_reopen(); goto putreq; } } } zclose(ZIFILE); /* Process the response headers */ local_t = time(NULL); nullline = 0; i = 0; len = -1; while (!nullline && (ch = http_inc(0)) >= 0 && i < HTTPBUFLEN) { buf[i] = ch; if (buf[i] == 10) { /* found end of line */ if (i > 0 && buf[i-1] == 13) i--; if (i < 1) nullline = 1; buf[i] = '\0'; if (array && !nullline && hdcnt < HTTPHEADCNT) makestr(&headers[hdcnt++],buf); if (!ckstrcmp(buf,"HTTP",4,0)) { http_fnd = 1; j = ckindex(" ",buf,0,0,0); p = &buf[j]; while (isspace(*p)) p++; switch (p[0]) { case '1': /* Informational message */ break; case '2': /* Success */ break; case '3': /* Redirection */ case '4': /* Client failure */ case '5': /* Server failure */ default: /* Unknown */ if (!quiet) printf("Failure: Server reports %s\n",p); rc = -1; } http_set_code_reply(p); } else { if (!ckstrcmp(buf,"Connection:",11,0)) { if ( ckindex("close",buf,11,0,0) != 0 ) closecon = 1; } else if (!ckstrcmp(buf,"Content-Length:",15,0)) { len = atoi(&buf[16]); } else if (!ckstrcmp(buf,"Transfer-Encoding:",18,0)) { if ( ckindex("chunked",buf,18,0,0) != 0 ) chunked = 1; debug(F101,"http_put chunked","",chunked); } if ( stdio ) printf("%s\n",buf); } i = 0; } else { i++; } } if (ch < 0 && first) { first--; http_close(); http_reopen(); goto putreq; } if ( http_fnd == 0 ) { closecon = 1; rc = -1; goto putexit; } /* Any response data? */ if ( dest && dest[0] ) { if (zopeno(ZOFILE,dest,NULL,NULL)) zfile = 1; else rc = -1; } if ( chunked ) { while ((len = http_get_chunk_len()) > 0) { while (len && (ch = http_inc(0)) >= 0) { len--; if ( zfile ) zchout(ZOFILE,(CHAR)ch); if ( stdio ) conoc((CHAR)ch); } if ((ch = http_inc(0)) != CR) break; if ((ch = http_inc(0)) != LF) break; } } else { while (len && (ch = http_inc(0)) >= 0) { len--; if ( zfile ) zchout(ZOFILE,(CHAR)ch); if ( stdio ) conoc((CHAR)ch); } } if ( zfile ) zclose(ZOFILE); if ( chunked ) { /* Parse Trailing Headers */ nullline = 0; while (!nullline && (ch = http_inc(0)) >= 0 && i < HTTPBUFLEN) { buf[i] = ch; if ( buf[i] == 10 ) { /* found end of line */ if (i > 0 && buf[i-1] == 13) i--; if (i < 1) nullline = 1; buf[i] = '\0'; if (array && !nullline && hdcnt < HTTPHEADCNT) makestr(&headers[hdcnt++],buf); if (!ckstrcmp(buf,"Connection:",11,0)) { if ( ckindex("close",buf,11,0,0) != 0 ) closecon = 1; } i = 0; } else { i++; } } } } else { rc = -1; } putexit: if ( array ) http_mkarray(headers,hdcnt,array); if (closecon) http_close(); free(request); for (i = 0; i < hdcnt; i++) { if (headers[i]) free(headers[i]); } return(rc); } int #ifdef CK_ANSIC http_delete(char * agent, char ** hdrlist, char * user, char * pwd, char array, char * remote) #else http_delete(agent, hdrlist, user, pwd, array, remote) char * agent; char ** hdrlist; char * user; char * pwd; char array; char * remote; #endif /* CK_ANSIC */ { char * request=NULL; int i, j, len = 0, hdcnt = 0, rc = 0; int ch; int http_fnd = 0; char buf[HTTPBUFLEN], *p; int nullline; time_t mod_t; time_t srv_t; time_t local_t; char passwd[64]; char b64in[128]; char b64out[256]; char * headers[HTTPHEADCNT]; int closecon = 0; int chunked = 0; int first = 1; if (httpfd == -1) return(-1); if (array) { for (i = 0; i < HTTPHEADCNT; i++) headers[i] = NULL; } /* Compute length of request header */ len = 11; /* DELETE */ len += strlen(HTTP_VERSION); len += strlen(remote); len += 16; if ( hdrlist ) { for (i = 0; hdrlist[i]; i++) len += strlen(hdrlist[i]) + 2; } len += strlen(http_host_port) + 8; if (agent) len += 13 + strlen(agent); if (user) { if (!pwd) { readpass("Password: ",passwd,64); pwd = passwd; } ckmakmsg(b64in,sizeof(b64in),user,":",pwd,NULL); j = b8tob64(b64in,strlen(b64in),b64out,256); memset(pwd,0,strlen(pwd)); if (j < 0) return(-1); b64out[j] = '\0'; len += j + 24; } len += 32; /* Date: */ #ifdef HTTP_CLOSE len += 19; /* Connection: close */ #endif len += 3; /* blank line + null */ request = malloc(len); if (!request) return(-1); sprintf(request,"DELETE %s %s\r\n",remote,HTTP_VERSION); ckstrncat(request,"Date: ",len); #ifdef CMDATE2TM ckstrncat(request,http_now(),len); #else ckstrncat(request,...,len); #endif /* CMDATE2TM */ ckstrncat(request,"\r\n",len); ckstrncat(request,"Host: ", len); ckstrncat(request,http_host_port, len); ckstrncat(request,"\r\n",len); if (agent) { ckstrncat(request,"User-agent: ",len); ckstrncat(request,agent,len); ckstrncat(request,"\r\n",len); } if (user) { ckstrncat(request,"Authorization: Basic ",len); ckstrncat(request,b64out,len); ckstrncat(request,"\r\n",len); } if ( hdrlist ) { for (i = 0; hdrlist[i]; i++) { ckstrncat(request,hdrlist[i],len); ckstrncat(request,"\r\n",len); } } #ifdef HTTP_CLOSE ckstrncat(request,"Connection: close\r\n",len); #endif ckstrncat(request,"\r\n",len); delreq: if (http_tol((CHAR *)request,strlen(request)) < 0) { http_close(); if ( first ) { first--; http_reopen(); goto delreq; } rc = -1; goto delexit; } /* Process the response headers */ local_t = time(NULL); nullline = 0; i = 0; len = -1; while (!nullline && (ch = http_inc(0)) >= 0 && i < HTTPBUFLEN) { buf[i] = ch; if (buf[i] == 10) { /* found end of line */ if (i > 0 && buf[i-1] == 13) i--; if (i < 1) nullline = 1; buf[i] = '\0'; if (array && !nullline && hdcnt < HTTPHEADCNT) makestr(&headers[hdcnt++],buf); if (!ckstrcmp(buf,"HTTP",4,0)) { http_fnd = 1; j = ckindex(" ",buf,0,0,0); p = &buf[j]; while (isspace(*p)) p++; switch (p[0]) { case '1': /* Informational message */ break; case '2': /* Success */ break; case '3': /* Redirection */ case '4': /* Client failure */ case '5': /* Server failure */ default: /* Unknown */ if (!quiet) printf("Failure: Server reports %s\n",p); rc = -1; } http_set_code_reply(p); } else { if (!ckstrcmp(buf,"Connection:",11,0)) { if ( ckindex("close",buf,11,0,0) != 0 ) closecon = 1; } else if (!ckstrcmp(buf,"Content-Length:",15,0)) { len = atoi(&buf[16]); } else if (!ckstrcmp(buf,"Transfer-Encoding:",18,0)) { if ( ckindex("chunked",buf,18,0,0) != 0 ) chunked = 1; debug(F101,"http_delete chunked","",chunked); } printf("%s\n",buf); } i = 0; } else { i++; } } if (ch < 0 && first) { first--; http_close(); http_reopen(); goto delreq; } if ( http_fnd == 0 ) { rc = -1; closecon = 1; goto delexit; } /* Any response data? */ if ( chunked ) { while ((len = http_get_chunk_len()) > 0) { while (len && (ch = http_inc(0)) >= 0) { len--; conoc((CHAR)ch); } if ((ch = http_inc(0)) != CR) break; if ((ch = http_inc(0)) != LF) break; } } else { while (len && (ch = http_inc(0)) >= 0) { len--; conoc((CHAR)ch); } } if ( chunked ) { /* Parse Trailing Headers */ nullline = 0; while (!nullline && (ch = http_inc(0)) >= 0 && i < HTTPBUFLEN) { buf[i] = ch; if ( buf[i] == 10 ) { /* found end of line */ if (i > 0 && buf[i-1] == 13) i--; if (i < 1) nullline = 1; buf[i] = '\0'; if (array && !nullline && hdcnt < HTTPHEADCNT) makestr(&headers[hdcnt++],buf); if (!ckstrcmp(buf,"Connection:",11,0)) { if ( ckindex("close",buf,11,0,0) != 0 ) closecon = 1; } i = 0; } else { i++; } } } delexit: if (array) http_mkarray(headers,hdcnt,array); if (closecon) http_close(); free(request); for (i = 0; i < hdcnt; i++) { if (headers[i]) free(headers[i]); } return(rc); } int #ifdef CK_ANSIC http_post(char * agent, char ** hdrlist, char * mime, char * user, char * pwd, char array, char * local, char * remote, char * dest, int stdio) #else http_post(agent, hdrlist, mime, user, pwd, array, local, remote, dest, stdio) char * agent; char ** hdrlist; char * mime; char * user; char * pwd; char array; char * local; char * remote; char * dest; int stdio; #endif /* CK_ANSIC */ { char * request=NULL; int i, j, len = 0, hdcnt = 0, rc = 0; int ch; int http_fnd = 0; char buf[HTTPBUFLEN], *p; int nullline; time_t mod_t; time_t srv_t; time_t local_t; char passwd[64]; char b64in[128]; char b64out[256]; int filelen; char * headers[HTTPHEADCNT]; int closecon = 0; int chunked = 0; int zfile = 0; int first = 1; if (httpfd == -1) return(-1); if (array) { for (i = 0; i < HTTPHEADCNT; i++) headers[i] = NULL; } filelen = zchki(local); if (filelen < 0) return(-1); /* Compute length of request header */ len = 9; /* POST */ len += strlen(HTTP_VERSION); len += strlen(remote); len += 16; if ( hdrlist ) { for (i = 0; hdrlist[i]; i++) len += strlen(hdrlist[i]) + 2; } len += strlen(http_host_port) + 8; if (agent) len += 13 + strlen(agent); if (user) { if (!pwd) { readpass("Password: ",passwd,64); pwd = passwd; } ckmakmsg(b64in,sizeof(b64in),user,":",pwd,NULL); j = b8tob64(b64in,strlen(b64in),b64out,256); memset(pwd,0,strlen(pwd)); if (j < 0) return(-1); b64out[j] = '\0'; len += j + 24; } len += 16 + strlen(mime); /* Content-type: */ len += 32; /* Content-length: */ len += 32; /* Date: */ #ifdef HTTP_CLOSE len += 19; /* Connection: close */ #endif len += 3; /* blank line + null */ request = malloc(len); if (!request) return(-1); sprintf(request,"POST %s %s\r\n",remote,HTTP_VERSION); ckstrncat(request,"Date: ",len); ckstrncat(request,http_now(),len); ckstrncat(request,"\r\n",len); ckstrncat(request,"Host: ", len); ckstrncat(request,http_host_port, len); ckstrncat(request,"\r\n",len); if (agent) { ckstrncat(request,"User-agent: ",len); ckstrncat(request,agent,len); ckstrncat(request,"\r\n",len); } if (user) { ckstrncat(request,"Authorization: Basic ",len); ckstrncat(request,b64out,len); ckstrncat(request,"\r\n",len); } if ( hdrlist ) { for (i = 0; hdrlist[i]; i++) { ckstrncat(request,hdrlist[i],len); ckstrncat(request,"\r\n",len); } } ckstrncat(request,"Content-type: ",len); ckstrncat(request,mime,len); ckstrncat(request,"\r\n",len); #ifdef HTTP_CLOSE ckstrncat(request,"Connection: close\r\n",len); #endif sprintf(buf,"Content-length: %d\r\n",filelen); /* safe */ ckstrncat(request,buf,len); ckstrncat(request,"\r\n",len); #ifdef COMMENT /* This is apparently a mistake - the previous ckstrncat() already */ /* appended a blank line to the request. There should only be one. */ /* Servers are not required by RFC 2616 to ignore extraneous empty */ /* lines. -fdc, 28 Aug 2005. */ ckstrncat(request,"\r\n",len); #endif /* COMMENT */ /* Now we have the contents of the file */ postopen: if (zopeni(ZIFILE,local)) { postreq: if (http_tol((CHAR *)request,strlen(request)) < 0) { http_close(); if ( first ) { first--; http_reopen(); goto postreq; } rc = -1; zclose(ZIFILE); goto postexit; } i = 0; while (zchin(ZIFILE,&ch) == 0) { buf[i++] = ch; if (i == HTTPBUFLEN) { http_tol((CHAR *)buf,HTTPBUFLEN); i = 0; } } if (i > 0) http_tol((CHAR *)buf,HTTPBUFLEN); zclose(ZIFILE); /* Process the response headers */ local_t = time(NULL); nullline = 0; i = 0; len = -1; while (!nullline && (ch = http_inc(0)) >= 0 && i < HTTPBUFLEN) { buf[i] = ch; if (buf[i] == 10) { /* found end of line */ if (i > 0 && buf[i-1] == 13) i--; if (i < 1) nullline = 1; buf[i] = '\0'; if (array && !nullline && hdcnt < HTTPHEADCNT) makestr(&headers[hdcnt++],buf); if (!ckstrcmp(buf,"HTTP",4,0)) { http_fnd = 1; j = ckindex(" ",buf,0,0,0); p = &buf[j]; while (isspace(*p)) p++; switch (p[0]) { case '1': /* Informational message */ break; case '2': /* Success */ break; case '3': /* Redirection */ case '4': /* Client failure */ case '5': /* Server failure */ default: /* Unknown */ if (!quiet) printf("Failure: Server reports %s\n",p); rc = -1; } http_set_code_reply(p); } else { if (!ckstrcmp(buf,"Connection:",11,0)) { if ( ckindex("close",buf,11,0,0) != 0 ) closecon = 1; } else if (!ckstrcmp(buf,"Content-Length:",15,0)) { len = atoi(&buf[16]); } else if (!ckstrcmp(buf,"Transfer-Encoding:",18,0)) { if ( ckindex("chunked",buf,18,0,0) != 0 ) chunked = 1; debug(F101,"http_post chunked","",chunked); } if (stdio) printf("%s\n",buf); } i = 0; } else { i++; } } if (ch < 0 && first) { first--; http_close(); http_reopen(); goto postopen; } if (http_fnd == 0) { rc = -1; closecon = 1; goto postexit; } /* Any response data? */ if ( dest && dest[0] ) { if (zopeno(ZOFILE,dest,NULL,NULL)) zfile = 1; else rc = -1; } if ( chunked ) { while ((len = http_get_chunk_len()) > 0) { while (len && (ch = http_inc(0)) >= 0) { len--; if ( zfile ) zchout(ZOFILE,(CHAR)ch); if ( stdio ) conoc((CHAR)ch); } if ((ch = http_inc(0)) != CR) break; if ((ch = http_inc(0)) != LF) break; } } else { while (len && (ch = http_inc(0)) >= 0) { len--; if ( zfile ) zchout(ZOFILE,(CHAR)ch); if ( stdio ) conoc((CHAR)ch); } } if ( zfile ) zclose(ZOFILE); if ( chunked ) { /* Parse Trailing Headers */ nullline = 0; while (!nullline && (ch = http_inc(0)) >= 0 && i < HTTPBUFLEN) { buf[i] = ch; if ( buf[i] == 10 ) { /* found end of line */ if (i > 0 && buf[i-1] == 13) i--; if (i < 1) nullline = 1; buf[i] = '\0'; if (array && !nullline && hdcnt < HTTPHEADCNT) makestr(&headers[hdcnt++],buf); if (!ckstrcmp(buf,"Connection:",11,0)) { if ( ckindex("close",buf,11,0,0) != 0 ) closecon = 1; } i = 0; } else { i++; } } } } else { rc = -1; } postexit: if (array) http_mkarray(headers,hdcnt,array); if (closecon) http_close(); free(request); for (i = 0; i < hdcnt; i++) { if (headers[i]) free(headers[i]); } return(rc); } int #ifdef CK_ANSIC http_connect(int socket, char * agent, char ** hdrlist, char * user, char * pwd, char array, char * host_port) #else http_connect(socket, agent, hdrlist, user, pwd, array, host_port) int socket; char * agent; char ** hdrlist; char * user; char * pwd; char array; char * host_port; #endif /* CK_ANSIC */ { char * request=NULL; int i, j, len = 0, hdcnt = 0, rc = 0; int http_fnd = 0; char buf[HTTPBUFLEN], *p, ch; int nullline; time_t mod_t; time_t srv_t; time_t local_t; char passwd[64]; char b64in[128]; char b64out[256]; char * headers[HTTPHEADCNT]; int connected = 0; tcp_http_proxy_errno = 0; if (socket == -1) return(-1); if (array) { for (i = 0; i < HTTPHEADCNT; i++) headers[i] = NULL; } /* Compute length of request header */ len = 12; /* CONNECT */ len += strlen(HTTP_VERSION); len += strlen(host_port); len += (int) strlen(http_host_port) + 8; len += 16; len += strlen("Proxy-Connection: Keep-Alive\r\n"); if ( hdrlist ) { for (i = 0; hdrlist[i]; i++) len += strlen(hdrlist[i]) + 2; } if (agent && agent[0]) len += 13 + strlen(agent); if (user && user[0]) { if (!pwd) { readpass("Password: ",passwd,64); pwd = passwd; } ckmakmsg(b64in,sizeof(b64in),user,":",pwd,NULL); j = b8tob64(b64in,strlen(b64in),b64out,256); memset(pwd,0,strlen(pwd)); if (j < 0) return(-1); b64out[j] = '\0'; len += j + 72; } len += 32; /* Date: */ len += 3; /* blank line + null */ request = malloc(len); if (!request) return(-1); sprintf(request,"CONNECT %s %s\r\n",host_port,HTTP_VERSION); ckstrncat(request,"Date: ",len); #ifdef CMDATE2TM ckstrncat(request,http_now(),len); #else strcat(request,...); #endif /* CMDATE2TM */ ckstrncat(request,"\r\n",len); ckstrncat(request,"Host: ", len); ckstrncat(request,http_host_port, len); ckstrncat(request,"\r\n",len); if (agent && agent[0]) { ckstrncat(request,"User-agent: ",len); ckstrncat(request,agent,len); ckstrncat(request,"\r\n",len); } if (user && user[0]) { ckstrncat(request,"Proxy-authorization: Basic ",len); ckstrncat(request,b64out,len); ckstrncat(request,"\r\n",len); ckstrncat(request,"Extension: Security/Remote-Passphrase\r\n",len); } ckstrncat(request,"Proxy-Connection: Keep-Alive\r\n",len); if ( hdrlist ) { for (i = 0; hdrlist[i]; i++) { ckstrncat(request,hdrlist[i],len); ckstrncat(request,"\r\n",len); } } ckstrncat(request,"\r\n",len); len = strlen(request); #ifdef TCPIPLIB /* Send request */ if (socket_write(socket,(CHAR *)request,strlen(request)) < 0) { rc = -1; goto connexit; } #else if (write(socket,(CHAR *)request,strlen(request)) < 0) { /* Send request */ rc = -1; goto connexit; } #endif /* TCPIPLIB */ /* Process the response headers */ local_t = time(NULL); nullline = 0; i = 0; while (!nullline && #ifdef TCPIPLIB (socket_read(socket,&ch,1) == 1) && #else (read(socket,&ch,1) == 1) && #endif /* TCPIPLIB */ i < HTTPBUFLEN) { buf[i] = ch; if (buf[i] == 10) { /* found end of line */ if (i > 0 && buf[i-1] == 13) i--; if (i < 1) nullline = 1; buf[i] = '\0'; if (array && !nullline && hdcnt < HTTPHEADCNT) makestr(&headers[hdcnt++],buf); if (!ckstrcmp(buf,"HTTP",4,0)) { http_fnd = 1; j = ckindex(" ",buf,0,0,0); p = &buf[j]; while (isspace(*p)) p++; tcp_http_proxy_errno = atoi(p); switch (p[0]) { case '1': /* Informational message */ break; case '2': /* Success */ connected = 1; break; case '3': /* Redirection */ case '4': /* Client failure */ case '5': /* Server failure */ default: /* Unknown */ if (!quiet) printf("Failure: Server reports %s\n",p); rc = -1; } http_set_code_reply(p); } else { printf("%s\n",buf); } i = 0; } else { i++; } } if ( http_fnd == 0 ) rc = -1; if (array) http_mkarray(headers,hdcnt,array); connexit: if ( !connected ) { if ( socket == ttyfd ) { ttclos(0); } else if ( socket == httpfd ) { http_close(); } } free(request); for (i = 0; i < hdcnt; i++) { if (headers[i]) free(headers[i]); } return(rc); } #endif /* NOHTTP */ #ifdef CK_DNS_SRV #define INCR_CHECK(x,y) x += y; if (x > size + answer.bytes) goto dnsout #define CHECK(x,y) if (x + y > size + answer.bytes) goto dnsout #define NTOHSP(x,y) x[0] << 8 | x[1]; x += y #ifndef CKQUERYTYPE #ifdef UNIXWARE #ifndef UW7 #define CKQUERYTYPE CHAR #endif /* UW7 */ #endif /* UNIXWARE */ #endif /* CKQUERYTYPE */ #ifndef CKQUERYTYPE #define CKQUERYTYPE char #endif /* CKQUERYTYPE */ /* 1 is success, 0 is failure */ int locate_srv_dns(host, service, protocol, addr_pp, naddrs) char *host; char *service; char *protocol; struct sockaddr **addr_pp; int *naddrs; { int nout, j, count; union { unsigned char bytes[2048]; HEADER hdr; } answer; unsigned char *p=NULL; CKQUERYTYPE query[MAX_DNS_NAMELEN]; #ifdef CK_ANSIC const char * h; #else char * h; #endif /* CK_ANSIC */ struct sockaddr *addr = NULL; struct sockaddr_in *sin = NULL; struct hostent *hp = NULL; int type, class; int priority, weight, size, len, numanswers, numqueries, rdlen; unsigned short port; #ifdef CK_ANSIC const #endif /* CK_ANSIC */ int hdrsize = sizeof(HEADER); struct srv_dns_entry { struct srv_dns_entry *next; int priority; int weight; unsigned short port; char *host; }; struct srv_dns_entry *head = NULL; struct srv_dns_entry *srv = NULL, *entry = NULL; char * s = NULL; nout = 0; addr = (struct sockaddr *) malloc(sizeof(struct sockaddr)); if (addr == NULL) return 0; count = 1; /* * First build a query of the form: * * service.protocol.host * * which will most likely be something like: * * _telnet._tcp.host * */ if (((int)strlen(service) + strlen(protocol) + strlen(host) + 5) > MAX_DNS_NAMELEN ) goto dnsout; /* Realm names don't (normally) end with ".", but if the query doesn't end with "." and doesn't get an answer as is, the resolv code will try appending the local domain. Since the realm names are absolutes, let's stop that. But only if a name has been specified. If we are performing a search on the prefix alone then the intention is to allow the local domain or domain search lists to be expanded. */ h = host + strlen (host); ckmakxmsg(query, sizeof(query), "_",service,"._",protocol,".", host, ((h > host) && (h[-1] != '.')?".":NULL), NULL,NULL,NULL,NULL,NULL); size = res_search(query, C_IN, T_SRV, answer.bytes, sizeof(answer.bytes)); if (size < hdrsize) goto dnsout; /* We got a reply - See how many answers it contains. */ p = answer.bytes; numqueries = ntohs(answer.hdr.qdcount); numanswers = ntohs(answer.hdr.ancount); p += sizeof(HEADER); /* * We need to skip over all of the questions, so we have to iterate * over every query record. dn_expand() is able to tell us the size * of compressed DNS names, so we use it. */ while (numqueries--) { len = dn_expand(answer.bytes,answer.bytes+size,p,query,sizeof(query)); if (len < 0) goto dnsout; INCR_CHECK(p, len + 4); } /* * We're now pointing at the answer records. Only process them if * they're actually T_SRV records (they might be CNAME records, * for instance). * * But in a DNS reply, if you get a CNAME you always get the associated * "real" RR for that CNAME. RFC 1034, 3.6.2: * * CNAME RRs cause special action in DNS software. When a name server * fails to find a desired RR in the resource set associated with the * domain name, it checks to see if the resource set consists of a CNAME * record with a matching class. If so, the name server includes the CNAME * record in the response and restarts the query at the domain name * specified in the data field of the CNAME record. The one exception to * this rule is that queries which match the CNAME type are not restarted. * * In other words, CNAMEs do not need to be expanded by the client. */ while (numanswers--) { /* First is the name; use dn_expand() to get the compressed size. */ len = dn_expand(answer.bytes,answer.bytes+size,p,query,sizeof(query)); if (len < 0) goto dnsout; INCR_CHECK(p, len); CHECK(p,2); /* Query type */ type = NTOHSP(p,2); CHECK(p, 6); /* Query class */ class = NTOHSP(p,6); /* Also skip over 4-byte TTL */ CHECK(p,2); /* Record data length */ rdlen = NTOHSP(p,2); /* * If this is an SRV record, process it. Record format is: * * Priority * Weight * Port * Server name */ if (class == C_IN && type == T_SRV) { CHECK(p,2); priority = NTOHSP(p,2); CHECK(p, 2); weight = NTOHSP(p,2); CHECK(p, 2); port = NTOHSP(p,2); len = dn_expand(answer. bytes, answer.bytes + size, p, query, sizeof(query) ); if (len < 0) goto dnsout; INCR_CHECK(p, len); /* * We got everything. Insert it into our list, but make sure * it's in the right order. Right now we don't do anything * with the weight field */ srv = (struct srv_dns_entry *)malloc(sizeof(struct srv_dns_entry)); if (srv == NULL) goto dnsout; srv->priority = priority; srv->weight = weight; srv->port = port; makestr(&s,(char *)query); /* strdup() is not portable */ srv->host = s; if (head == NULL || head->priority > srv->priority) { srv->next = head; head = srv; } else /* * Confusing. Insert an entry into this spot only if: * . The next person has a higher priority (lower * priorities are preferred), or: * . There is no next entry (we're at the end) */ for (entry = head; entry != NULL; entry = entry->next) if ((entry->next && entry->next->priority > srv->priority) || entry->next == NULL) { srv->next = entry->next; entry->next = srv; break; } } else INCR_CHECK(p, rdlen); } /* * Now we've got a linked list of entries sorted by priority. * Start looking up A records and returning addresses. */ if (head == NULL) goto dnsout; for (entry = head; entry != NULL; entry = entry->next) { hp = gethostbyname(entry->host); if (hp != 0) { /* Watch out - memset() and memcpy() are not portable... */ switch (hp->h_addrtype) { case AF_INET: for (j = 0; hp->h_addr_list[j]; j++) { sin = (struct sockaddr_in *) &addr[nout++]; memset ((char *) sin, 0, sizeof (struct sockaddr)); sin->sin_family = hp->h_addrtype; sin->sin_port = htons(entry->port); memcpy((char *) &sin->sin_addr, (char *) hp->h_addr_list[j], sizeof(struct in_addr)); /* safe */ if (nout + 1 >= count) { count += 5; addr = (struct sockaddr *) realloc((char *) addr, sizeof(struct sockaddr) * count); if (!addr) goto dnsout; } } break; default: break; } } } for (entry = head; entry != NULL;) { free(entry->host); entry->host = NULL; srv = entry; entry = entry->next; free(srv); srv = NULL; } dnsout: if (srv) free(srv); if (nout == 0) { /* No good servers */ if (addr) free(addr); return 0; } *addr_pp = addr; *naddrs = nout; return 1; } #undef INCR_CHECK #undef CHECK #undef NTOHSP #define INCR_CHECK(x, y) x += y; if (x > size + answer.bytes) \ return 0 #define CHECK(x, y) if (x + y > size + answer.bytes) \ return 0 #define NTOHSP(x, y) x[0] << 8 | x[1]; x += y int locate_txt_rr(prefix, name, retstr) char *prefix, *name; char **retstr; { union { unsigned char bytes[2048]; HEADER hdr; } answer; unsigned char *p; char host[MAX_DNS_NAMELEN], *h; int size; int type, class, numanswers, numqueries, rdlen, len; /* * Form our query, and send it via DNS */ if (name == NULL || name[0] == '\0') { strcpy(host,prefix); } else { if ( strlen(prefix) + strlen(name) + 3 > MAX_DNS_NAMELEN ) return 0; /* Realm names don't (normally) end with ".", but if the query doesn't end with "." and doesn't get an answer as is, the resolv code will try appending the local domain. Since the realm names are absolutes, let's stop that. But only if a name has been specified. If we are performing a search on the prefix alone then the intention is to allow the local domain or domain search lists to be expanded. */ h = host + strlen (host); ckmakmsg(host,sizeof(host),prefix, ".", name, ((h > host) && (h[-1] != '.'))?".":NULL); } size = res_search(host, C_IN, T_TXT, answer.bytes, sizeof(answer.bytes)); if (size < 0) return 0; p = answer.bytes; numqueries = ntohs(answer.hdr.qdcount); numanswers = ntohs(answer.hdr.ancount); p += sizeof(HEADER); /* * We need to skip over the questions before we can get to the answers, * which means we have to iterate over every query record. We use * dn_expand to tell us how long each compressed name is. */ while (numqueries--) { len = dn_expand(answer.bytes, answer.bytes + size, p, host, sizeof(host)); if (len < 0) return 0; INCR_CHECK(p, len + 4); /* Name plus type plus class */ } /* * We're now pointing at the answer records. Process the first * TXT record we find. */ while (numanswers--) { /* First the name; use dn_expand to get the compressed size */ len = dn_expand(answer.bytes, answer.bytes + size, p, host, sizeof(host)); if (len < 0) return 0; INCR_CHECK(p, len); /* Next is the query type */ CHECK(p, 2); type = NTOHSP(p,2); /* Next is the query class; also skip over 4 byte TTL */ CHECK(p,6); class = NTOHSP(p,6); /* Record data length - make sure we aren't truncated */ CHECK(p,2); rdlen = NTOHSP(p,2); if (p + rdlen > answer.bytes + size) return 0; /* * If this is a TXT record, return the string. Note that the * string has a 1-byte length in the front */ /* XXX What about flagging multiple TXT records as an error? */ if (class == C_IN && type == T_TXT) { len = *p++; if (p + len > answer.bytes + size) return 0; *retstr = malloc(len + 1); if (*retstr == NULL) return ENOMEM; strncpy(*retstr, (char *) p, len); (*retstr)[len] = '\0'; /* Avoid a common error. */ if ( (*retstr)[len-1] == '.' ) (*retstr)[len-1] = '\0'; return 1; } } return 0; } #undef INCR_CHECK #undef CHECK #undef NTOHSP #endif /* CK_DNS_SRV */ #ifdef TNCODE #ifdef CK_FORWARD_X #ifdef UNIX #include #define FWDX_UNIX_SOCK #ifndef AF_LOCAL #define AF_LOCAL AF_UNIX #endif #ifndef PF_LOCAL #define PF_LOCAL PF_UNIX #endif #ifndef SUN_LEN /* Evaluate to actual length of the `sockaddr_un' structure. */ #define SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 0)->sun_path) \ + strlen ((ptr)->sun_path)) #endif #endif /* UNIX */ int fwdx_create_listen_socket(screen) int screen; { #ifdef NOPUTENV return(-1); #else /* NOPUTENV */ struct sockaddr_in saddr; int display, port, sock=-1, i; static char env[512]; /* * X Windows Servers support multiple displays by listening on * one socket per display. Display 0 is port 6000; Display 1 is * port 6001; etc. * * We start by trying to open port 6001 so that display 0 is * reserved for the local X Windows Server. */ for ( display=1; display < 1000 ; display++ ) { if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { debug(F111,"fwdx_create_listen_socket()","socket() < 0",sock); return(-1); } port = 6000 + display; bzero((char *)&saddr, sizeof(saddr)); saddr.sin_family = AF_INET; saddr.sin_addr.s_addr = inet_addr(myipaddr); saddr.sin_port = htons(port); if (bind(sock, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) { i = errno; /* Save error code */ #ifdef TCPIPLIB socket_close(sock); #else /* TCPIPLIB */ close(sock); #endif /* TCPIPLIB */ sock = -1; debug(F110,"fwdx_create_listen_socket()","bind() < 0",0); continue; } debug(F100,"fdwx_create_listen_socket() bind OK","",0); break; } if ( display > 1000 ) { debug(F100,"fwdx_create_listen_socket() Out of Displays","",0); return(-1); } if (listen(sock, 5) < 0) { i = errno; /* Save error code */ #ifdef TCPIPLIB socket_close(sock); #else /* TCPIPLIB */ close(sock); #endif /* TCPIPLIB */ debug(F101,"fdwx_create_listen_socket() listen() errno","",errno); return(-1); } debug(F100,"fwdx_create_listen_socket() listen OK","",0); TELOPT_SB(TELOPT_FORWARD_X).forward_x.listen_socket = sock; if (!myipaddr[0]) getlocalipaddr(); if ( myipaddr[0] ) ckmakxmsg(env,sizeof(env),"DISPLAY=",myipaddr,":", ckuitoa(display),":",ckuitoa(screen), NULL,NULL,NULL,NULL,NULL,NULL); else ckmakmsg(env,sizeof(env),"DISPLAY=",ckuitoa(display),":", ckuitoa(screen)); putenv(env); return(0); #endif /* NOPUTENV */ } int fwdx_open_client_channel(channel) int channel; { char * env; struct sockaddr_in saddr; #ifdef FWDX_UNIX_SOCK struct sockaddr_un saddr_un = { AF_LOCAL }; #endif /* FWDX_UNIX_SOCK */ int colon, dot, display, port, sock, i, screen; int family; char buf[256], * host=NULL, * rest=NULL; #ifdef TCP_NODELAY int on=1; #endif /* TCP_NODELAY */ debug(F111,"fwdx_create_client_channel()","channel",channel); for ( i=0; i */ if (!(host->h_addr_list)) return(-1); bcopy(host->h_addr_list[0], (caddr_t)&saddr.sin_addr, host->h_length ); #else bcopy(host->h_addr, (caddr_t)&saddr.sin_addr, host->h_length); #endif /* h_addr */ #else /* HADDRLIST */ bcopy(host->h_addr, (caddr_t)&saddr.sin_addr, host->h_length); #endif /* HADDRLIST */ } if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { debug(F111,"fwdx_create_client_channel()","socket() < 0",sock); return(-1); } if ( connect(sock, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) { debug(F110,"fwdx_create_client_channel()","connect() failed",0); #ifdef TCPIPLIB socket_close(sock); #else /* TCPIPLIB */ close(sock); #endif /* TCPIPLIB */ return(-1); } #ifdef TCP_NODELAY setsockopt(sock,IPPROTO_TCP,TCP_NODELAY,(char *)&on,sizeof(on)); #endif /* TCP_NODELAY */ } for (i = 0; i < MAXFWDX; i++) { if (TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[i].id == -1) { TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[i].fd = sock; TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[i].id = channel; TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[i].need_to_send_xauth = 1; debug(F111,"fwdx_create_client_channel()","socket",sock); return(0); } } return(-1); } int fwdx_server_avail() { char * env; struct sockaddr_in saddr; #ifdef FWDX_UNIX_SOCK struct sockaddr_un saddr_un = { AF_LOCAL }; #endif /* FWDX_UNIX_SOCK */ int colon, dot, display, port, sock, i, screen; char buf[256], *host=NULL, *rest=NULL; #ifdef TCP_NODELAY int on=1; #endif /* TCP_NODELAY */ int family; env = getenv("DISPLAY"); if ( !env ) env = (char *)tn_get_display(); if ( env ) ckstrncpy(buf,env,256); else ckstrncpy(buf,"127.0.0.1:0.0",256); bzero((char *)&saddr,sizeof(saddr)); saddr.sin_family = AF_INET; if (!fwdx_parse_displayname(buf,&family,&host,&display,&screen,&rest)) { if ( host ) free(host); if ( rest ) free(rest); return(0); } if (rest) free(rest); #ifndef FWDX_UNIX_SOCK /* if $DISPLAY indicates use of unix domain sockets, but we don't support it, * we change things to use inet sockets on the ip loopback interface instead, * and hope that it works. */ if (family == FamilyLocal) { family = FamilyInternet; if (host) free(host); if (host = malloc(strlen("localhost") + 1)) strcpy(host, "localhost"); else { return(-1); } } #else /* FWDX_UNIX_SOCK */ if (family == FamilyLocal) { debug(F100,"fwdx_server_avail() FamilyLocal","",0); if (host) free(host); sock = socket(PF_LOCAL, SOCK_STREAM, 0); if (sock < 0) return(0); ckmakmsg(buf,sizeof(buf),"/tmp/.X11-unix/X",ckitoa(display),NULL,NULL); strncpy(saddr_un.sun_path, buf, sizeof(saddr_un.sun_path)); if (connect(sock,(struct sockaddr *)&saddr_un,SUN_LEN(&saddr_un)) < 0) return(0); close(sock); return(1); } #endif /* FWDX_UNIX_SOCK */ /* Otherwise, we are assuming FamilyInternet */ if (host) { ckstrncpy(buf,host,sizeof(buf)); free(host); } else ckstrncpy(buf,myipaddr,sizeof(buf)); debug(F111,"fwdx_server_avail()","display",display); port = 6000 + display; saddr.sin_port = htons(port); debug(F110,"fwdx_server_avail() ip-address",buf,0); saddr.sin_addr.s_addr = inet_addr(buf); if ( saddr.sin_addr.s_addr == (unsigned long) -1 #ifdef INADDR_NONE || saddr.sin_addr.s_addr == INADDR_NONE #endif /* INADDR_NONE */ ) { struct hostent *host; host = gethostbyname(buf); if ( host == NULL ) { debug(F110,"fwdx_server_avail() gethostbyname() failed", myipaddr,0); return(-1); } host = ck_copyhostent(host); #ifdef HADDRLIST #ifdef h_addr /* This is for trying multiple IP addresses - see */ if (!(host->h_addr_list)) return(-1); bcopy(host->h_addr_list[0], (caddr_t)&saddr.sin_addr, host->h_length ); #else bcopy(host->h_addr, (caddr_t)&saddr.sin_addr, host->h_length); #endif /* h_addr */ #else /* HADDRLIST */ bcopy(host->h_addr, (caddr_t)&saddr.sin_addr, host->h_length); #endif /* HADDRLIST */ } if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { debug(F111,"fwdx_server_avail()","socket() < 0",sock); return(0); } if ( connect(sock, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) { debug(F110,"fwdx_server_avail()","connect() failed",0); #ifdef TCPIPLIB socket_close(sock); #else /* TCPIPLIB */ close(sock); #endif /* TCPIPLIB */ return(0); } #ifdef TCPIPLIB socket_close(sock); #else /* TCPIPLIB */ close(sock); #endif /* TCPIPLIB */ return(1); } int fwdx_open_server_channel() { int sock, ready_to_accept, sock2,channel,i; #ifdef TCP_NODELAY int on=1; #endif /* TCP_NODELAY */ #ifdef UCX50 static u_int saddrlen; #else static SOCKOPT_T saddrlen; #endif /* UCX50 */ struct sockaddr_in saddr; char sb[8]; extern char tn_msg[]; #ifdef BSDSELECT fd_set rfds; struct timeval tv; #else #ifdef BELLSELCT fd_set rfds; #else fd_set rfds; struct timeval { long tv_sec; long tv_usec; } tv; #endif /* BELLSELECT */ #endif /* BSDSELECT */ unsigned short nchannel; unsigned char * p; sock = TELOPT_SB(TELOPT_FORWARD_X).forward_x.listen_socket; try_again: #ifdef BSDSELECT tv.tv_sec = tv.tv_usec = 0L; tv.tv_usec = 50; FD_ZERO(&rfds); FD_SET(sock, &rfds); ready_to_accept = ((select(FD_SETSIZE, #ifdef HPUX #ifdef HPUX1010 (fd_set *) #else (int *) #endif /* HPUX1010 */ #else #ifdef __DECC (fd_set *) #endif /* __DECC */ #endif /* HPUX */ &rfds, NULL, NULL, &tv) > 0) && FD_ISSET(sock, &rfds)); #else /* BSDSELECT */ #ifdef IBMSELECT ready_to_accept = (select(&sock, 1, 0, 0, 50) == 1); #else #ifdef BELLSELECT FD_ZERO(rfds); FD_SET(sock, rfds); ready_to_accept = ((select(128, rfds, NULL, NULL, 50) > 0) && FD_ISSET(sock, rfds)); #else /* Try this - what's the worst that can happen... */ tv.tv_sec = tv.tv_usec = 0L; tv.tv_usec = 50; FD_ZERO(&rfds); FD_SET(sock, &rfds); ready_to_accept = ((select(FD_SETSIZE, (fd_set *) &rfds, NULL, NULL, &tv) > 0) && FD_ISSET(sock, &rfds)); #endif /* BELLSELECT */ #endif /* IBMSELECT */ #endif /* BSDSELECT */ if ( !ready_to_accept ) return(0); if ((sock2 = accept(sock,(struct sockaddr *)&saddr,&saddrlen)) < 0) { int i = errno; /* save error code */ debug(F101,"tcpsrv_open accept errno","",i); return(-1); } /* * Now we have the open socket. We must now find a channel to store * it in, and then notify the client. */ for ( channel=0;channel 0) { data += count; len -= count; } debug(F111,"fwdx_write_data_to_channel retry",data,len); if ( len > 0 ) goto fwdx_write_data_to_channel_retry; } debug(F111,"fwdx_write_data_to_channel complete",data,length); return(length); /* success - return total length */ } VOID fwdx_check_sockets(fd_set *ibits) { int x, sock, channel, bytes; static char buffer[32000]; debug(F100,"fwdx_check_sockets()","",0); if ( sstelnet && !TELOPT_ME(TELOPT_FORWARD_X) || !sstelnet && !TELOPT_U(TELOPT_FORWARD_X)) { debug(F110,"fwdx_check_sockets()","TELOPT_FORWARD_X not negotiated",0); return; } for (x = 0; x < MAXFWDX; x++) { if ( TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[x].fd == -1 || TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[x].suspend ) continue; sock = TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[x].fd; if (FD_ISSET(sock, ibits)) { channel = TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[x].id; debug(F111,"fwdx_check_sockets()","channel set",channel); bytes = socket_read(sock, buffer, sizeof(buffer)); if (bytes > 0) fwdx_send_data_from_channel(channel, buffer, bytes); else if (bytes == 0) { fwdx_close_channel(channel); fwdx_send_close(channel); } } } } int fwdx_init_fd_set(fd_set *ibits) { int x,set=0,cnt=0; if ( sstelnet && !TELOPT_ME(TELOPT_FORWARD_X) || !sstelnet && !TELOPT_U(TELOPT_FORWARD_X)) { debug(F110,"fwdx_init_fd_set()","TELOPT_FORWARD_X not negotiated",0); return(0); } if (TELOPT_SB(TELOPT_FORWARD_X).forward_x.listen_socket != -1) { set++; FD_SET(TELOPT_SB(TELOPT_FORWARD_X).forward_x.listen_socket, ibits); } for (x = 0; x < MAXFWDX; x++) { if (TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[x].fd != -1) { cnt++; if (TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[x].suspend) continue; set++; FD_SET(TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[x].fd, ibits); } } if (set + cnt == 0) { return(-1); } else { return(set); } } #ifdef NT VOID fwdx_thread( VOID * dummy ) { fd_set ifds; struct timeval tv; extern int priority; int n; setint(); SetThreadPrty(priority,isWin95() ? 3 : 11); while ( !sstelnet && TELOPT_U(TELOPT_FORWARD_X) || sstelnet && TELOPT_ME(TELOPT_FORWARD_X)) { FD_ZERO(&ifds); n = fwdx_init_fd_set(&ifds); if (n > 0) { tv.tv_sec = 0; tv.tv_usec = 2500; if ( select(FD_SETSIZE, &ifds, NULL, NULL, &tv) > 0 ) fwdx_check_sockets(&ifds); } else if (n < 0) { TELOPT_SB(TELOPT_FORWARD_X).forward_x.thread_started = 0; ckThreadEnd(NULL); } else { sleep(1); } } } #endif /* NT */ #endif /* CK_FORWARD_X */ #endif /* TNCODE */ #endif /* NETCONN */ ckcnet.h0000644000015300001460000012300111605056051011275 0ustar fdckermit/* ckcnet.h -- Symbol and macro definitions for C-Kermit network support */ /* Author: Frank da Cruz Columbia University Academic Information Systems, New York City. Copyright (C) 1985, 2009, Trustees of Columbia University in the City of New York. All rights reserved. See the C-Kermit COPYING.TXT file or the copyright text in the ckcmai.c module for disclaimer and permissions. */ #ifndef CKCNET_H #define CKCNET_H /* Network types */ #define NET_NONE 0 /* None */ #define NET_TCPB 1 /* TCP/IP Berkeley (socket) */ #define NET_TCPA 2 /* TCP/IP AT&T (streams) */ #define NET_SX25 3 /* SUNOS SunLink X.25 */ #define NET_DEC 4 /* DECnet */ #define NET_VPSI 5 /* VAX PSI */ #define NET_PIPE 6 /* LAN Manager Named Pipe */ #define NET_VX25 7 /* Stratus VOS X.25 */ #define NET_BIOS 8 /* IBM NetBios */ #define NET_SLAT 9 /* Meridian Technologies' SuperLAT */ #define NET_FILE 10 /* Read from a file */ #define NET_CMD 11 /* Read from a sub-process */ #define NET_DLL 12 /* Load a DLL for use as comm channel*/ #define NET_IX25 13 /* IBM AIX 4.1 X.25 */ #define NET_HX25 14 /* HP-UX 10 X.25 */ #define NET_PTY 15 /* Pseudoterminal */ #define NET_SSH 16 /* SSH */ #ifdef OS2 /* In OS/2, only the 32-bit */ #ifndef __32BIT__ /* version gets NETBIOS */ #ifdef CK_NETBIOS #undef CK_NETBIOS #endif /* CK_NETBIOS */ #endif /* __32BIT__ */ #endif /* OS2 */ #ifdef _M_PPC #ifdef SUPERLAT #undef SUPERLAT #endif /* SUPERLAT */ #endif /* _M_PPC */ #ifdef NPIPE /* For items in common to */ #define NPIPEORBIOS /* Named Pipes and NETBIOS */ #endif /* NPIPE */ #ifdef CK_NETBIOS #ifndef NPIPEORBIOS #define NPIPEORBIOS #endif /* NPIPEORBIOS */ #endif /* CK_NETBIOS */ /* Network virtual terminal protocols (for SET HOST connections) */ /* FTP, HTTP and SSH have their own stacks */ #define NP_DEFAULT 255 #define NP_NONE 0 /* None (async) */ #define NP_TELNET 1 /* TCP/IP telnet */ #define NP_VTP 2 /* ISO Virtual Terminal Protocol */ #define NP_X3 3 /* CCITT X.3 */ #define NP_X28 4 /* CCITT X.28 */ #define NP_X29 5 /* CCITT X.29 */ #define NP_RLOGIN 6 /* TCP/IP Remote login */ #define NP_KERMIT 7 /* TCP/IP Kermit */ #define NP_TCPRAW 8 /* TCP/IP Raw socket */ #define NP_TCPUNK 9 /* TCP/IP Unknown */ #define NP_SSL 10 /* TCP/IP SSLv23 */ #define NP_TLS 11 /* TCP/IP TLSv1 */ #define NP_SSL_TELNET 12 /* TCP/IP Telnet over SSLv23 */ #define NP_TLS_TELNET 13 /* TCP/IP Telnet over TLSv1 */ #define NP_K4LOGIN 14 /* TCP/IP Kerberized remote login */ #define NP_EK4LOGIN 15 /* TCP/IP Encrypted Kerberized ... */ #define NP_K5LOGIN 16 /* TCP/IP Kerberized remote login */ #define NP_EK5LOGIN 17 /* TCP/IP Encrypted Kerberized ... */ #define NP_K5U2U 18 /* TCP/IP Kerberos 5 User to User */ #define NP_CTERM 19 /* DEC CTERM */ #define NP_LAT 20 /* DEC LAT */ #define NP_SSL_RAW 21 /* SSL with no Telnet permitted */ #define NP_TLS_RAW 22 /* TLS with no Telnet permitted */ /* others here... */ #ifdef CK_SSL #define IS_TELNET() (nettype == NET_TCPB && (ttnproto == NP_TELNET \ || ttnproto == NP_SSL_TELNET \ || ttnproto == NP_TLS_TELNET \ || ttnproto == NP_KERMIT)) #else /* CK_SSL */ #define IS_TELNET() (nettype == NET_TCPB && (ttnproto == NP_TELNET \ || ttnproto == NP_KERMIT)) #endif /* CK_SSL */ #ifdef CK_KERBEROS #ifdef KRB5 #ifdef KRB4 #define IS_RLOGIN() (nettype == NET_TCPB && (ttnproto == NP_RLOGIN \ || ttnproto == NP_K5LOGIN \ || ttnproto == NP_EK5LOGIN \ || ttnproto == NP_K4LOGIN \ || ttnproto == NP_EK4LOGIN \ )) #else /* KRB4 */ #define IS_RLOGIN() (nettype == NET_TCPB && (ttnproto == NP_RLOGIN \ || ttnproto == NP_K5LOGIN \ || ttnproto == NP_EK5LOGIN \ )) #endif /* KRB4 */ #else /* KRB5 */ #ifdef KRB4 #define IS_RLOGIN() (nettype == NET_TCPB && (ttnproto == NP_RLOGIN \ || ttnproto == NP_K4LOGIN \ || ttnproto == NP_EK4LOGIN \ )) #else /* KRB4 */ KERBEROS defined without either KRB4 or KRB5 #endif /* KRB4 */ #endif /* KRB5 */ #else /* CK_KERBEROS */ #define IS_RLOGIN() (nettype == NET_TCPB && (ttnproto == NP_RLOGIN)) #endif /* CK_KERBEROS */ #define IS_SSH() (nettype == NET_SSH) /* RLOGIN Modes */ #define RL_RAW 0 /* Do Not Process XON/XOFF */ #define RL_COOKED 1 /* Do Process XON/XOFF */ /* Encryption types */ #define CX_NONE 999 #ifdef ENCTYPE_ANY #define CX_AUTO ENCTYPE_ANY #else #define CX_AUTO 0 #endif /* ENCTYPE_ANY */ #ifdef ENCTYPE_DES_CFB64 #define CX_DESC64 ENCTYPE_DES_CFB64 #else #define CX_DESC64 1 #endif /* ENCTYPE_DES_CFB64 */ #ifdef ENCTYPE_DES_OFB64 #define CX_DESO64 ENCTYPE_DES_OFB64 #else #define CX_DESO64 2 #endif /* ENCTYPE_DES_OFB64 */ #ifdef ENCTYPE_DES3_CFB64 #define CX_DES3C64 ENCTYPE_DES3_CFB64 #else #define CX_DES3C64 3 #endif /* ENCTYPE_DES_CFB64 */ #ifdef ENCTYPE_DES3_OFB64 #define CX_DESO64 ENCTYPE_DES3_OFB64 #else #define CX_DES3O64 4 #endif /* ENCTYPE_DES_OFB64 */ #ifdef ENCTYPE_CAST5_40_CFB64 #define CX_C540C64 ENCTYPE_CAST5_40_CFB64 #else #define CX_C540C64 8 #endif /* ENCTYPE_CAST5_40_CFB64 */ #ifdef ENCTYPE_CAST5_40_OFB64 #define CX_C540O64 ENCTYPE_CAST5_40_OFB64 #else #define CX_C540O64 9 #endif /* ENCTYPE_CAST5_40_OFB64 */ #ifdef ENCTYPE_CAST128_CFB64 #define CX_C128C64 ENCTYPE_CAST128_CFB64 #else #define CX_C128C64 10 #endif /* ENCTYPE_CAST128_CFB64 */ #ifdef ENCTYPE_CAST128_OFB64 #define CX_C128O64 ENCTYPE_CAST128_OFB64 #else #define CX_C128O64 11 #endif /* ENCTYPE_CAST128_OFB64 */ /* Basic network function prototypes, common to all. */ _PROTOTYP( int netopen, (char *, int *, int) ); _PROTOTYP( int netclos, (void) ); _PROTOTYP( int netflui, (void) ); _PROTOTYP( int nettchk, (void) ); _PROTOTYP( int netxchk, (int) ); _PROTOTYP( int netbreak, (void) ); _PROTOTYP( int netinc, (int) ); _PROTOTYP( int netxin, (int, CHAR *) ); _PROTOTYP( int nettol, (CHAR *, int) ); _PROTOTYP( int nettoc, (CHAR) ); #ifdef TCPSOCKET _PROTOTYP( int gettcpport, (void) ); _PROTOTYP( int gettcpport, (void) ); #endif /* TCPSOCKET */ /* SunLink X.25 support by Marcello Frutig, Catholic University, Rio de Janeiro, Brazil, 1990. Maybe this can be adapted to VAX PSI and other X.25 products too. */ #ifndef SUNOS4 /* Only valid for SUNOS4 */ #ifndef SOLARIS #ifdef SUNX25 #undef SUNX25 #endif /* SUNX25 */ #endif /* SOLARIS */ #endif /* SUNOS4 */ #ifdef STRATUSX25 #define ANYX25 #define MAX_USER_DATA 128 /* SUN defines this in a header file, I believe. */ #endif /* STRATUSX25 */ #ifdef SUNX25 #define ANYX25 #endif /* SUNX25 */ #ifdef IBMX25 /* AIX 4.1 X.25 */ #ifndef AIX41 #undef IBMX25 #else /* AIX41 */ #define ANYX25 #define MAX_USER_DATA NPI_MAX_DATA /* used for buffer sizes */ #endif /* AIX41 */ #endif /* IBMX25 */ #ifdef HPX25 /* HP-UX 10.* X.25 */ #ifndef HPUX10 #undef HPX25 #else /* HPUX10 */ #define ANYX25 #endif /* HPUX10 */ #endif /* HPX25 */ #ifdef ANYX25 #ifndef NETCONN /* ANYX25 implies NETCONN */ #define NETCONN #endif /* NETCONN */ #define MAXPADPARMS 22 /* Number of PAD parameters */ #define MAXCUDATA 12 /* Max length of X.25 call user data */ #define X29PID 1 /* X.29 protocol ID */ #define X29PIDLEN 4 /* X.29 protocol ID length */ #define X29_SET_PARMS 2 #define X29_READ_PARMS 4 #define X29_SET_AND_READ_PARMS 6 #define X29_INVITATION_TO_CLEAR 1 #define X29_PARAMETER_INDICATION 0 #define X29_INDICATION_OF_BREAK 3 #define X29_ERROR 5 #define INVALID_PAD_PARM 1 #define PAD_BREAK_CHARACTER 0 #define PAD_ESCAPE 1 #define PAD_ECHO 2 #define PAD_DATA_FORWARD_CHAR 3 #define PAD_DATA_FORWARD_TIMEOUT 4 #define PAD_FLOW_CONTROL_BY_PAD 5 #define PAD_SUPPRESSION_OF_SIGNALS 6 #define PAD_BREAK_ACTION 7 #define PAD_SUPPRESSION_OF_DATA 8 #define PAD_PADDING_AFTER_CR 9 #define PAD_LINE_FOLDING 10 #define PAD_LINE_SPEED 11 #define PAD_FLOW_CONTROL_BY_USER 12 #define PAD_LF_AFTER_CR 13 #define PAD_PADDING_AFTER_LF 14 #define PAD_EDITING 15 #define PAD_CHAR_DELETE_CHAR 16 #define PAD_BUFFER_DELETE_CHAR 17 #define PAD_BUFFER_DISPLAY_CHAR 18 #define MAXIX25 MAX_USER_DATA*7 #define MAXOX25 MAX_USER_DATA #endif /* ANYX25 */ #ifdef SUNX25 #ifdef SOLARIS25 /* and presumably SunLink 9.xx */ #include #include #include #include #include #include #include #include #include #include #else #include /* X.25 includes, Sun only */ #include #ifndef SOLARIS #include #endif /* SOLARIS */ #include #include #ifdef SOLARIS #include #else #include #endif /* SOLARIS */ #include #include #include #include #include #include #endif /* SOLARIS25 */ #endif /* SUNX25 */ #ifdef ANYX25 #ifdef IBMX25 /* X.25 includes, AIX only */ #include #include #include #include #include #define NPI_20 /* required to include the whole NPI */ #include #include #include #include /* required for access to the ODM */ #include /* database, needed to find out the */ /* local NUA. see x25local_nua() */ /* IBM X25 NPI generic primitive type */ typedef union N_npi_ctl_t { ulong PRIM_type; /* generic primitive type */ char buffer[NPI_MAX_CTL]; /* maximum primitive size */ N_bind_ack_t bind_ack; N_bind_req_t bind_req; N_conn_con_t conn_con; N_conn_ind_t conn_ind; N_conn_req_t conn_req; N_conn_res_t conn_res; N_data_req_t data_req; N_data_ind_t data_ind; N_discon_ind_t discon_ind; N_discon_req_t discon_req; N_error_ack_t error_ack; N_exdata_ind_t exdata_ind; N_info_ack_t info_ack; N_ok_ack_t ok_ack; N_reset_con_t reset_con; N_reset_req_t reset_req; N_reset_ind_t reset_ind; } N_npi_ctl_t; /* some extra definitions to help out */ typedef char x25addr_t[45]; /* max 40 defined by CCITT */ typedef char N_npi_data_t[NPI_MAX_DATA]; /* fd or server waiting for connections, used by netclos and netopen */ extern int x25serverfd; #endif /* IBMX25 */ #ifdef HPX25 /* X.25 includes, HP-UX only */ #include #include #include #include #include #include #include #include #endif /* HPX25 */ /* C-Kermit X.3 / X.25 / X.29 / X.121 support functions */ /* (riehm: this list of functions isn't quite right for AIX) */ _PROTOTYP( int shopad, (int) ); _PROTOTYP( int shox25, (int) ); _PROTOTYP( VOID initpad, (void) ); _PROTOTYP( VOID setpad, (CHAR *, int) ); _PROTOTYP( VOID readpad, (CHAR *, int, CHAR *) ); _PROTOTYP( int qbitpkt, (CHAR *, int) ); _PROTOTYP( VOID setqbit, (void) ); _PROTOTYP( VOID resetqbit, (void) ); _PROTOTYP( VOID breakact, (void) ); _PROTOTYP( int pkx121, (char *, CHAR *) ); _PROTOTYP( SIGTYP x25oobh, (int) ); _PROTOTYP( int x25diag, (void) ); _PROTOTYP( int x25intr, (char) ); _PROTOTYP( int x25reset, (char, char) ); _PROTOTYP( int x25clear, (void) ); _PROTOTYP( int x25stat, (void) ); _PROTOTYP( int x25in, (int, CHAR *) ); _PROTOTYP( int setpadp, (void) ); _PROTOTYP( int setx25, (void) ); _PROTOTYP( int x25xin, (int, CHAR *) ); _PROTOTYP( int x25inl, (CHAR *, int, int, CHAR) ); #ifdef IBMX25 /* setup x25 */ _PROTOTYP( ulong x25bind, (int, char *, char *, int, int, int, ulong) ); _PROTOTYP( int x25call, (int, char *, char *) ); /* connect to remote */ _PROTOTYP( int x25unbind, (int) ); /* disconnect */ _PROTOTYP( char *x25prim, (int) ); /* display primitives */ _PROTOTYP( int x25local_nua, (char *) ); /* find local NUA */ #endif /* IBMX25 */ #endif /* ANYX25 */ /* CMU-OpenVMS/IP */ #ifdef CMU_TCPIP /* CMU_TCPIP implies TCPSOCKET */ #ifndef TCPSOCKET #define TCPSOCKET #endif /* TCPSOCKET */ #ifndef TCPIPLIB #define TCPIPLIB #endif /* TCPIPLIB */ #endif /* CMU_TCPIP */ /* DEC TCP/IP for (Open)VMS, previously known as UCX */ #ifdef DEC_TCPIP /* DEC_TCPIP implies TCPSOCKET */ #ifndef TCPSOCKET #define TCPSOCKET #endif /* TCPSOCKET */ #ifndef TCPIPLIB #define TCPIPLIB #endif /* TCPIPLIB */ #endif /* DEC_TCPIP */ /* SRI/TGV/Cisco/Process MultiNet, TCP/IP for VAX/VMS */ #ifdef MULTINET /* MULTINET implies TCPSOCKET */ #ifndef TCPSOCKET #define TCPSOCKET #endif /* TCPSOCKET */ #ifndef TCPIPLIB #define TCPIPLIB #endif /* TCPIPLIB */ #ifndef TGVORWIN /* MULTINET and WINTCP */ #define TGVORWIN /* share a lot of code... */ #endif /* TGVORWIN */ #endif /* MULTINET */ /* Wollongong TCP/IP for VAX/VMS */ #ifdef WINTCP /* WINTCP implies TCPSOCKET */ #ifndef TCPSOCKET #define TCPSOCKET #endif /* TCPSOCKET */ #ifndef TCPIPLIB #define TCPIPLIB #endif /* TCPIPLIB */ #ifndef TGVORWIN /* WINTCP and MULTINET */ #define TGVORWIN /* share a lot of code... */ #endif /* TGVORWIN */ #endif /* WINTCP */ /* Wollongong TCP/IP for AT&T Sys V */ #ifdef WOLLONGONG /* WOLLONGONG implies TCPSOCKET */ #ifndef TCPSOCKET /* Don't confuse WOLLONGONG */ #define TCPSOCKET /* (which is for UNIX) with */ #endif /* TCPSOCKET */ /* WINTCP, which is for VMS! */ #endif /* WOLLONGONG */ #ifdef EXCELAN /* EXCELAN implies TCPSOCKET */ #ifndef TCPSOCKET #define TCPSOCKET #endif /* TCPSOCKET */ #endif /* EXCELAN */ #ifdef INTERLAN /* INTERLAN implies TCPSOCKET */ #ifndef TCPSOCKET #define TCPSOCKET #endif /* TCPSOCKET */ #endif /* INTERLAN */ #ifdef BEBOX #ifndef TCPSOCKET #define TCPSOCKET #endif /* TCPSOCKET */ #ifndef TCPIPLIB #define TCPIPLIB #endif /* TCPIPLIB */ #define socket_errno h_errno #define socket_read(x,y,z) recv(x,y,sizeof(char),z) #define socket_write(x,y,z) send(x,y,sizeof(char),z) #define socket_ioctl ioctl #define socket_close(x) closesocket(x) #ifndef FIONBIO #define FIONBIO 2 #endif /* FIONBIO */ #ifndef FIONREAD #define FIONREAD 1 #endif /* FIONREAD */ #ifndef SIOCATMARK #define SIOCATMARK 3 #endif /* SIOCATMARK */ #endif /* BEBOX */ #ifdef COMMENT /* no longer used but might come in handy again later... */ /* CK_READ0 can (and should) be defined if and only if: (a) read(fd,&x,0) can be used harmlessly on a TCP/IP socket connection. (b) read(fd,&x,0) returns 0 if the connection is up, -1 if it is down. */ #ifndef CK_READ0 #ifdef TCPSOCKET #ifdef SUNOS41 /* It works in SunOS 4.1 */ #define CK_READ0 #else #ifdef NEXT /* and NeXTSTEP */ #define CK_READ0 #endif /* NEXT */ #endif /* SUNOS41 */ #endif /* TCPSOCKET */ #endif /* CK_READ0 */ #endif /* COMMENT */ /* Telnet protocol */ #ifdef TCPSOCKET /* TCPSOCKET implies TNCODE */ #ifndef TNCODE /* Which means... */ #define TNCODE /* Compile in telnet code */ #endif /* TNCODE */ /* Platforms where we must call gethostname(buf,len) and then gethostbyname(buf) to get local IP address, rather than calling gethostbyname(""). */ #ifndef CKGHNLHOST #ifdef datageneral #define CKGHNLHOST #else #ifdef SOLARIS #define CKGHNLHOST #else #ifdef SUNOS4 #define CKGHNLHOST #else #ifdef UNIXWARE #define CKGHNLHOST #else #ifdef SINIX #define CKGHNLHOST #endif /* SINIX */ #endif /* UNIXWARE */ #endif /* SUNOS4 */ #endif /* SOLARIS */ #endif /* datageneral */ #endif /* CKGHNLHOST */ #ifndef RLOGCODE /* What about Rlogin? */ #ifndef NORLOGIN /* Rlogin can be enabled only for UNIX versions that have both SIGURG (SCO doesn't) and CK_TTGWSIZ (OSF/1 doesn't), so we don't assume that any others have these without verifying first. Not that it really makes much difference since you can only use Rlogin if you are root... */ #ifdef SUNOS41 #define RLOGCODE #else #ifdef SOLARIS #define RLOGCODE #else #ifdef HPUX9 #define RLOGCODE #else #ifdef HPUX10 #define RLOGCODE #else #ifdef OSF40 #define RLOGCODE #else #ifdef NEXT #define RLOGCODE #else #ifdef AIX41 #define RLOGCODE #else #ifdef UNIXWARE #define RLOGCODE #else #ifdef IRIX51 #define RLOGCODE #else #ifdef IRIX60 #define RLOGCODE #else #ifdef QNX #define RLOGCODE #else #ifdef __linux__ #define RLOGCODE #else #ifdef BSD44 #define RLOGCODE #endif /* BSD44 */ #endif /* __linux__ */ #endif /* QNX */ #endif /* IRIX60 */ #endif /* IRIX51 */ #endif /* UNIXWARE */ #endif /* AIX41 */ #endif /* NEXT */ #endif /* OSF40 */ #endif /* HPUX10 */ #endif /* HPUX9 */ #endif /* SOLARIS */ #endif /* SUNOS41 */ #endif /* NORLOGIN */ #ifdef VMS /* VMS */ #define RLOGCODE #endif /* VMS */ #endif /* RLOGCODE */ #endif /* TCPSOCKET */ #ifdef TNCODE /* Telnet local-echo buffer, used for saving up user data that can't be properly displayed and/or evaluated until pending Telnet negotiations are complete. TTLEBUF is defined for platforms (like UNIX) where net i/o is done by the same routines that do serial i/o (in which case the relevant code goes into the ck?tio.c module, in the ttinc(), ttchk(), etc, routines); NETLETBUF is defined for platforms (like VMS) that use different APIs for network and serial i/o, and enables the copies of the same routines that are in ckcnet.c. */ #ifndef TTLEBUF #ifdef UNIX #define TTLEBUF #else #ifdef datageneral #define TTLEBUF #endif /* datageneral */ #endif /* UNIX */ #endif /* TTLEBUF */ #ifndef NETLEBUF #ifdef VMS #define NETLEBUF #endif /* VMS */ #endif /* NETLEBUF */ #endif /* TNCODE */ #ifdef SUNX25 /* SUNX25 implies TCPSOCKET */ #ifndef TCPSOCKET /* But doesn't imply TNCODE */ #define TCPSOCKET #endif /* TCPSOCKET */ #endif /* SUNX25 */ #ifndef TCPSOCKET #ifndef NO_DNS_SRV #define NO_DNS_SRV #endif /* NO_DNS_SRV */ #endif /* TCPSOCKET */ /* This is another TCPSOCKET section... */ #ifdef TCPSOCKET #ifndef NETCONN /* TCPSOCKET implies NETCONN */ #define NETCONN #endif /* NETCONN */ #ifndef NO_DNS_SRV #ifdef NOLOCAL #define NO_DNS_SRV #endif /* NOLOCAL */ #ifdef OS2ONLY #define NO_DNS_SRV #endif /* OS2ONLY */ #ifdef NT #ifdef _M_PPC #define NO_DNS_SRV #endif /* _M_DNS */ #endif /* NO_DNS_SRV */ #ifdef VMS #define NO_DNS_SRV #endif /* VMS */ #ifdef STRATUS #define NO_DNS_SRV #endif /* STRATUS */ #ifdef datageneral #define NO_DNS_SRV #endif /* datageneral */ #ifdef ultrix #define NO_DNS_SRV #endif /* ultrix */ #ifdef NEXT #define NO_DNS_SRV #endif /* NEXT */ #endif /* NO_DNS_SRV */ #ifndef CK_DNS_SRV /* Use DNS SRV records to determine */ #ifndef NO_DNS_SRV /* host and ports */ #define CK_DNS_SRV #endif /* NO_DNS_SRV */ #endif /* CK_DNS_SRV */ #ifndef NOLISTEN /* select() is required to support */ #ifndef SELECT /* incoming connections. */ #ifndef VMS #ifndef OS2 #define NOLISTEN #endif /* OS2 */ #endif /* VMS */ #endif /* SELECT */ #endif /* NOLISTEN */ /* BSD sockets library header files */ #ifdef VMS /* Because bzero() and bcopy() are not portable among VMS versions, or compilers, or TCP/IP products, etc. */ #ifndef bzero #define bzero(s,n) memset(s,0,n) #endif /* bzero */ #ifndef bcopy #define bcopy(h,a,l) memcpy(a,h,l) #endif /* bcopy */ #endif /* VMS */ #ifdef HPUX6 /* These are missing in HP-UX 6.xx */ #ifndef bzero #define bzero(s,n) memset(s,0,n) #endif /* bzero */ #ifndef bcopy #define bcopy(h,a,l) memcpy(a,h,l) #endif /* bcopy */ #endif /* HPUX6 */ #ifdef UNIX /* UNIX section */ #ifdef SVR4 /* These suggested by Rob Healey, rhealey@kas.helios.mn.org, to avoid bugs in Berkeley compatibility library on Sys V R4 systems, but untested by me (fdc). Remove this bit if it gives you trouble. (Later corrected by Marc Boucher because bzero/bcopy are not argument-compatible with memset/memcpy|memmove.) */ #ifndef bzero #define bzero(s,n) memset(s,0,n) #endif #ifdef SOLARIS #ifdef SUNX25 #undef bzero /* WOULD YOU BELIEVE... That the Solaris X.25 /opt/SUNWcomm/lib/libsockx25 library references bzero, even though the use of bzero is forbidden in Solaris? Look for the function definition in ckcnet.c. */ _PROTOTYP( void bzero, (char *, int) ); #endif /* SUNX25 */ #ifndef bcopy #define bcopy(h,a,l) memcpy(a,h,l) #endif #else #ifndef bcopy #define bcopy(h,a,l) memmove(a,h,l) #endif #endif /* SOLARIS */ #else /* !SVR4 */ #ifdef PTX /* Sequent DYNIX PTX 1.3 */ #ifndef bzero #define bzero(s,n) memset(s,0,n) #endif #ifndef bcopy #define bcopy(h,a,l) memcpy(a,h,l) #endif #endif /* PTX */ #endif /* SVR4 */ #ifdef INTERLAN /* Racal-Interlan TCP/IP */ #include #include #include #include #include #include /* Why twice ? ? ? */ #else /* Not Interlan */ #ifdef BEBOX #include #else /* Not BEBOX */ /* Normal BSD TCP/IP library */ #ifdef COMMENT #ifndef HPUX #include #endif /* HPUX */ #endif /* COMMENT */ #ifdef SCO234 #include #include #endif /* SCO234 */ #include #ifdef WOLLONGONG #include #else #include #ifndef SV68R3V6 /* (maybe this should be SVR3 in general) */ #include /* Added June 2001 */ #endif /* SV68R3V6 */ #endif /* WOLLONGONG */ #endif /* BEBOX */ #endif /* INTERLAN */ #ifndef EXCELAN #include #ifndef INTERLAN #ifdef WOLLONGONG #define minor /* Do not include */ #include #else #ifndef OXOS #ifndef HPUX #ifndef BEBOX #include #endif /* BEBOX */ #endif /* HPUX */ #else /* OXOS */ /* In too many releases of X/OS, declares inet_addr() as * ``struct in_addr''. This is definitively wrong, and could cause * core dumps. Instead of including that bad file, inet_addr() is * correctly declared here. Of course, all the declarations done there * has been copied here. */ unsigned long inet_addr(); char *inet_ntoa(); struct in_addr inet_makeaddr(); unsigned long inet_network(); #endif /* OXOS */ #endif /* WOLLONGONG */ #endif /* INTERLAN */ #endif /* EXCELAN */ #ifdef EXCELAN /* Excelan TCP/IP */ #ifndef bzero #define bzero(s,n) memset(s,0,n) #endif /* bzero */ #ifndef bcopy #define bcopy(h,a,l) memcpy(a,h,l) #endif /* bcopy */ #include #endif /* EXCELAN */ #ifdef I386IX /* Interactive Sys V R3 network. */ /* #define TELOPTS */ /* This might need defining. */ #define ORG_NLONG ENAMETOOLONG /* Resolve conflicting symbols */ #undef ENAMETOOLONG /* in and */ #define ORG_NEMPTY ENOTEMPTY #undef ENOTEMPTY #include #undef ENAMETOOLONG #define ENAMETOOLONG ORG_NLONG #undef ENOTEMPTY #define ENOTEMPTY ORG_NEMPTY #include /* for inet_addr() */ #endif /* I386IX */ /* Data type of the inet_addr() function... We define INADDRX if it is of type struct inaddr. If it is undefined, unsigned long is assumed. Look at to find out. The following known cases are handled here. Other systems that need it can be added here, or else -DINADDRX can be included in the CFLAGS on the cc command line. */ #ifndef NOINADDRX #ifdef DU2 /* DEC Ultrix 2.0 */ #define INADDRX #endif /* DU2 */ #endif /* NOINADDRX */ #else /* Not UNIX */ #ifdef VMS /* (Open)VMS section */ #ifdef MULTINET /* TGV MultiNet */ /* In C-Kermit 7.0 Beta.08 we started getting scads of compile time warnings in Multinet builds: "blah" is implicitly declared as a function, where blah is socket_read/write/close, ntohs, htons, getpeername, accept, select, etc. I have no idea why -- these routines are declared in the header files below, and the includes haven't changed. The executable still seems to work OK. Messing with the order of the following includes is disastrous. */ #ifdef MULTINET_NO_PROTOTYPES #undef MULTINET_NO_PROTOTYPES #endif /* MULTINET_NO_PROTOTYPES */ #ifdef __cplusplus #undef __cplusplus #endif /* __cplusplus */ #include "multinet_root:[multinet.include]errno.h" #include "multinet_root:[multinet.include.sys]types.h" #include "multinet_root:[multinet.include.sys]socket.h" #include "multinet_root:[multinet.include]netdb.h" #include "multinet_root:[multinet.include.netinet]in.h" #include "multinet_root:[multinet.include.arpa]inet.h" #include "multinet_root:[multinet.include.sys]ioctl.h" #ifdef COMMENT /* No longer needed because now bzero/bcopy are macros defined as memset/memmove in all VMS builds. */ /* We should be able to pick these up from but it's not portable between VAXC and DECC. And even with DECC 5.x we have a difference between VAX and Alpha. We get warnings here on the VAX with DECC 5.6-003 but they are not fatal. */ #ifndef __DECC_VER #ifndef bzero _PROTOTYP( void bzero, (char *, int) ); #endif /* bzero */ #ifndef bcopy _PROTOTYP( void bcopy, (char *, char *, int) ); #endif /* bcopy */ #endif /* __DECC_VER */ #endif /* COMMENT */ #ifdef __DECC /* If compiling under DEC C the socket calls must not be prefixed with DECC$. This is done by using the compiler switch /Prefix=Ansi_C89. However, this causes some calls that should be prefixed to not be (which I think is a bug in the compiler - I've been told these calls are present in ANSI compilers). At any rate, such calls are fixed here by explicitly prefixing them. */ #ifdef COMMENT /* But this causes errors with VMS 6.2 / DEC C 5.3-006 / MultiNet 4.0A on a VAX (but not on an Alpha). So now what happens if we skip doing this? */ #define close decc$close #define alarm decc$alarm #endif /* COMMENT */ #endif /* __DECC */ #else /* Not MULTINET */ #ifdef WINTCP /* WIN/TCP = PathWay for VMS */ #ifdef OLD_TWG #include "twg$tcp:[netdist.include.sys]errno.h" #include "twg$tcp:[netdist.include.sys]types2.h" /* avoid some duplicates */ #else #include "twg$tcp:[netdist.include]socket_aliases.h" #include #include "twg$tcp:[netdist.include.sys]types.h" #endif /* OLD_TWG */ #include "twg$tcp:[netdist.include.sys]socket.h" #include "twg$tcp:[netdist.include]netdb.h" #include "twg$tcp:[netdist.include.sys]domain.h" #include "twg$tcp:[netdist.include.sys]protosw.h" #include "twg$tcp:[netdist.include.netinet]in.h" #include "twg$tcp:[netdist.include.arpa]inet.h" #include "twg$tcp:[netdist.include.sys]ioctl.h" #else /* Not WINTCP */ #ifdef DEC_TCPIP #ifdef UCX50 #ifndef IF_DOT_H #define IF_DOT_H #endif /* IF_DOT_H */ #endif /* UCX50 */ #ifdef IF_DOT_H #include /* Needed to put up u_int typedef */ #else #ifdef NEEDUINT typedef unsigned int u_int; #endif /* NEEDUINT */ #endif /* IF_DOT_H */ #include #ifdef VMS #include /* (SMS 2007/02/15) */ #endif /* VMS */ #include #include #include "ckvioc.h" #define socket_errno errno #ifdef COMMENT /* No longer needed because now bzero/bcopy are macros defined as memset/memmove in all VMS builds. */ /* Translation: In , which exists only for DECC >= 5.2, bzero() and bcopy() are declared only for OpenVMS >= 7.0. This still might need adjustment for DECC 5.0 and higher. */ #ifdef __DECC_VER #ifdef VMSV70 /* Note: you can't use "#if (__VMS_VER>=70000000)" because that is not portable and kills non-VMS builds. */ #include #else #ifndef bzero #define bzero(s,n) memset(s,0,n) #endif #ifndef bcopy #define bcopy(h,a,l) memmove(a,h,l) #endif #endif /* VMSV70 */ #else #ifndef bzero #define bzero(s,n) memset(s,0,n) #endif #ifndef bcopy #define bcopy(h,a,l) memmove(a,h,l) #endif #endif /* __DECC_VER */ #endif /* COMMENT */ #define socket_read read #define socket_write write #define socket_ioctl ioctl #define socket_close close #ifdef __DECC int ioctl (int d, int request, void *argp); #else int ioctl (int d, int request, char *argp); #endif /* DECC */ /* UCX supports select(), but does not provide the needed symbol and structure definitions in any header file, so ... */ #include #ifndef NBBY /*- * Copyright (c) 1982, 1986, 1991, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)types.h 8.1 (Berkeley) 6/2/93 */ #define NBBY 8 /* number of bits in a byte */ /* * Select uses bit masks of file descriptors in longs. These macros * manipulate such bit fields (the filesystem macros use chars). * FD_SETSIZE may be defined by the user, but the default here should * be enough for most uses. */ #ifndef FD_SETSIZE #define FD_SETSIZE 256 #endif typedef long fd_mask; #define NFDBITS (sizeof(fd_mask) * NBBY) /* bits per mask */ #ifndef howmany #define howmany(x, y) (((x)+((y)-1))/(y)) #endif typedef struct fd_set { fd_mask fds_bits[howmany(FD_SETSIZE, NFDBITS)]; } fd_set; #define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS))) #define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS))) #define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS))) #define FD_COPY(f, t) bcopy(f, t, sizeof(*(f))) #define FD_ZERO(p) bzero(p, sizeof(*(p))) #endif /* !NBBY */ #else /* Not DEC_TCPIP */ #ifdef CMU_TCPIP #include #include #include #include #include #include #include "ckvioc.h" #define socket_errno errno /* * Routines supplied in LIBCMU.OLB */ #define socket_ioctl ioctl #define socket_read cmu_read #define socket_write cmu_write #define socket_close cmu_close #endif /* CMU_TCPIP */ #endif /* DEC_TCPIP */ #endif /* WINTCP */ #endif /* MULTINET */ #else /* Not VMS */ #ifdef OS2 #include "ckonet.h" #ifndef NT #include #endif #endif /* OS2 */ #ifdef STRATUS /* Stratus VOS using OS TCP/IP products S235, S236, S237 */ #include /* This gets used some places when TCPSOCKET is defined. */ /* OS TCP provides bzero(), but not bcopy()... go figure. */ #define bcopy(s,d,z) memcpy(d,s,z) #endif /* STRATUS */ #ifdef OSK #ifndef OSKXXC #include #include #include #else #include #include #include #endif /* OSKXXC */ #ifndef bzero #define bzero(s,n) memset(s,0,n) #endif #ifndef bcopy #define bcopy(h,a,l) memcpy(a,h,l) #endif typedef char * caddr_t; /* core address type */ #endif /* OSK */ #endif /* VMS */ #endif /* UNIX */ #endif /* TCPSOCKET */ #ifndef NOINADDRX /* 301 - Needed for Solaris 10 and 11 */ #ifdef SOLARIS #define NOINADDRX #ifdef INADDR_NONE #undef INADDR_NONE #endif /* INADDR_NONE */ #endif /* SOLARIS */ #endif /* NOINADDRX */ #ifdef NOINADDRX #ifdef INADDRX #undef INADDRX #endif /* INADDRX */ #endif /* NOINADDRX */ #ifdef TCPSOCKET #ifndef NOHADDRLIST #ifndef HADDRLIST #ifdef SUNOS41 #define HADDRLIST #endif /* SUNOS41 */ #ifdef SOLARIS #define HADDRLIST #endif /* SOLARIS */ #ifdef LINUX #define HADDRLIST #endif /* LINUX */ #ifdef AIXRS #define HADDRLIST #endif /* AIXRS */ #ifdef HPUX #define HADDRLIST #endif /* HPUX */ #ifdef IRIX #define HADDRLIST #endif /* IRIX */ #ifdef I386IX #define HADDRLIST #endif /* I386IX */ #endif /* HADDRLIST */ /* A system that defines h_addr as h_addr_list[0] should be HADDRLIST */ #ifndef HADDRLIST #ifdef h_addr #define HADDRLIST #endif /* h_addr */ #endif /* HADDRLIST */ #endif /* NOHADDRLIST */ #endif /* TCPSOCKET */ #ifdef TNCODE /* If we're compiling telnet code... */ #ifndef IKS_OPTION #ifndef STRATUS #define IKS_OPTION #endif /* STRATUS */ #endif /* IKS_OPTION */ #include "ckctel.h" #else extern int sstelnet; #ifdef IKSD #undef IKSD #endif /* IKSD */ #ifndef NOIKSD #define NOIKSD #endif /* NOIKSD */ #ifdef IKS_OPTION #undef IKS_OPTION #endif /* IKS_OPTION */ #endif /* TNCODE */ #ifndef NOTCPOPTS /* Automatically define NOTCPOPTS for configurations where they can't be used at runtime or cause too much trouble at compile time. */ #ifdef CMU_TCPIP /* CMU/Tek */ #define NOTCPOPTS #endif /* CMU_TCPIP */ #ifdef MULTINET /* Multinet on Alpha */ #ifdef __alpha #define NOTCPOPTS #endif /* __alpha */ #endif /* MULTINET */ #endif /* NOTCPOPTS */ #ifdef NOTCPOPTS #ifdef TCP_NODELAY #undef TCP_NODELAY #endif /* TCP_NODELAY */ #ifdef SO_LINGER #undef SO_LINGER #endif /* SO_LINGER */ #ifdef SO_KEEPALIVE #undef SO_KEEPALIVE #endif /* SO_KEEPALIVE */ #ifdef SO_SNDBUF #undef SO_SNDBUF #endif /* SO_SNDBUF */ #ifdef SO_RCVBUF #undef SO_RCVBUF #endif /* SO_RCVBUF */ #endif /* NOTCPOPTS */ /* This function is declared even when TCPSOCKET is not available */ _PROTOTYP( char * ckgetpeer, (VOID)); #ifdef TCPSOCKET #ifdef SOL_SOCKET #ifdef TCP_NODELAY _PROTOTYP( int no_delay, (int, int) ); #endif /* TCP_NODELAY */ #ifdef SO_KEEPALIVE _PROTOTYP( int keepalive, (int, int) ) ; #endif /* SO_KEEPALIVE */ #ifdef SO_LINGER _PROTOTYP( int ck_linger, (int, int, int) ) ; #endif /* SO_LINGER */ #ifdef SO_SNDBUF _PROTOTYP( int sendbuf,(int, int) ) ; #endif /* SO_SNDBUF */ #ifdef SO_RCVBUF _PROTOTYP( int recvbuf, (int, int) ) ; #endif /* SO_RCVBUF */ #ifdef SO_DONTROUTE _PROTOTYP(int dontroute, (int, int)); #endif /* SO_DONTROUTE */ #endif /* SOL_SOCKET */ _PROTOTYP( int getlocalipaddr, (VOID)); _PROTOTYP( int getlocalipaddrs, (char *,int,int)); _PROTOTYP( char * ckgetfqhostname,(char *)); _PROTOTYP( struct hostent * ck_copyhostent,(struct hostent *)); _PROTOTYP( char * ckname2addr, (char *)); _PROTOTYP( char * ckaddr2name, (char *)); /* AIX */ #ifdef AIXRS #ifndef TCP_NODELAY #define TCP_NODELAY 0x1 #endif /* TCP_NODELAY */ #ifndef TCP_MAXSEG #define TCP_MAXSEG 0x2 #endif /* TCP_MAXSEG */ #ifndef TCP_KEEPALIVE #define TCP_KEEPALIVE 0x8 #endif /* TCP_KEEPALIVE */ #endif /* AIXRS */ #endif /* TCPSOCKET */ #ifdef RLOGCODE #ifndef CK_TTGWSIZ SORRY_RLOGIN_REQUIRES_TTGWSIZ_see_ckcplm.doc #endif /* CK_TTGWSIZ */ #endif /* RLOGCODE */ #ifdef CK_NAWS #ifndef CK_TTGWSIZ SORRY_CK_NAWS_REQUIRES_TTGWSIZ_see_ckcplm.doc #endif /* CK_TTGWSIZ */ #endif /* CK_NAWS */ #ifndef PF_INET #ifdef AF_INET #define PF_INET AF_INET #endif /* AF_INET */ #endif /* PF_INET */ #ifndef IPPORT_ECHO #define IPPORT_ECHO 7 #endif /* IPPORT_ECHO */ #ifdef CK_KERBEROS #ifdef RLOGCODE _PROTOTYP(int ck_krb_rlogin,(CHAR *, int, CHAR *, CHAR *, CHAR *, struct sockaddr_in *, struct sockaddr_in *, int, int)); #endif /* RLOGCODE */ #endif /* CK_KERBEROS */ _PROTOTYP( VOID ini_kerb, ( void ) ); /* Kerberos initialization routine */ _PROTOTYP( int doauth, (int) ); /* AUTHENTICATE action routine */ #ifdef CK_DNS_SRV _PROTOTYP(int locate_srv_dns,(char *host, char *service, char *protocol, struct sockaddr **addr_pp, int *naddrs)); #endif /* CK_DNS_SRV */ #ifndef NOHTTP _PROTOTYP(int http_open, (char *, char *, int, char *, int, char *)); _PROTOTYP(int http_reopen, (VOID)); _PROTOTYP(int http_close, (VOID)); _PROTOTYP(int http_get, (char *,char **,char *,char *,char,char *,char *, int)); _PROTOTYP(int http_head, (char *,char **,char *,char *,char,char *,char *, int)); _PROTOTYP(int http_put, (char *,char **,char *,char *,char *,char,char *, char *, char *, int)); _PROTOTYP(int http_delete, (char *,char **,char *,char *,char,char *)); _PROTOTYP(int http_connect, (int, char *,char **,char *,char *,char,char *)); _PROTOTYP(int http_post, (char *,char **,char *,char *,char *,char,char *, char *,char *, int)); _PROTOTYP(int http_index, (char *,char **,char *,char *,char,char *,char *, int)); _PROTOTYP(int http_inc, (int)); _PROTOTYP(int http_isconnected, (void)); extern char * tcp_http_proxy; /* Name[:port] of http proxy server */ extern int tcp_http_proxy_errno; /* Return value from server */ extern char * tcp_http_proxy_user; /* Name of user for authentication */ extern char * tcp_http_proxy_pwd; /* Password of user */ #endif /* NOHTTP */ #ifdef TCPSOCKET /* Type needed as 5th argument (length) to get/setsockopt() */ #ifdef TRU64 /* They say it themselves - this does not conform to standards */ #define socklen_t int #else #ifdef HPUX #define socklen_t int #endif /* HPUX */ #endif /* TRU64 */ #ifndef SOCKOPT_T #ifdef CK_64BIT #define SOCKOPT_T socklen_t #endif /* CK_64BIT */ #endif /* SOCKOPT_T */ #ifndef SOCKOPT_T #define SOCKOPT_T int #ifdef MACOSX10 #undef SOCKOPT_T #define SOCKOPT_T unsigned int #else #ifdef AIX42 #undef SOCKOPT_T #define SOCKOPT_T unsigned long #else #ifdef PTX #undef SOCKOPT_T #define SOCKOPT_T size_t #else #ifdef NT #undef SOCKOPT_T #define SOCKOPT_T int #else /* NT */ #ifdef UNIXWARE #undef SOCKOPT_T #define SOCKOPT_T size_t #else /* UNIXWARE */ #ifdef VMS #ifdef DEC_TCPIP #ifdef __DECC_VER #undef SOCKOPT_T #define SOCKOPT_T size_t #endif /* __DECC_VER */ #endif /* DEC_TCPIP */ #endif /* VMS */ #endif /* UNIXWARE */ #endif /* NT */ #endif /* PTX */ #endif /* AIX42 */ #endif /* MACOSX10 */ #endif /* SOCKOPT_T */ /* Ditto for getsockname() */ #ifndef GSOCKNAME_T #ifdef CK_64BIT #define GSOCKNAME_T socklen_t #endif /* CK_64BIT */ #endif /* GSOCKNAME_T */ #ifndef GSOCKNAME_T #define GSOCKNAME_T int #ifdef MACOSX10 #undef GSOCKNAME_T #define GSOCKNAME_T unsigned int #else #ifdef PTX #undef GSOCKNAME_T #define GSOCKNAME_T size_t #else #ifdef AIX42 /* size_t in 4.2++, int in 4.1-- */ #undef GSOCKNAME_T #define GSOCKNAME_T size_t #else #ifdef UNIXWARE #undef GSOCKNAME_T #define GSOCKNAME_T size_t #else #ifdef VMS #ifdef DEC_TCPIP #ifdef __DECC_VER #undef GSOCKNAME_T #define GSOCKNAME_T size_t #endif /* __DECC_VER */ #endif /* DEC_TCPIP */ #endif /* VMS */ #endif /* UNIXWARE */ #endif /* AIX41 */ #endif /* PTX */ #endif /* MACOSX10 */ #endif /* GSOCKNAME_T */ #endif /* TCPSOCKET */ #ifdef MACOSX10 #ifdef bcopy #undef bcopy #endif #ifdef bzero #undef bzero #endif #endif /* MACOSX10 */ #endif /* CKCNET_H */ ckcpro.c0000644000015300001460000033355711257166413011334 0ustar fdckermit /* WARNING -- This C source program generated by Wart preprocessor. */ /* Do not edit this file; edit the Wart-format source file instead, */ /* and then run it through Wart to produce a new C source file. */ /* Wart Version Info: */ char *wartv = "Wart Version 2.14, 10 Nov 1999"; char *protv = /* -*-C-*- */ "C-Kermit Protocol Module 8.0.160, 12 Aug 2007"; int kactive = 0; /* Kermit protocol is active */ #define PKTZEROHACK /* C K C P R O -- C-Kermit Protocol Module, in Wart preprocessor notation. */ /* Author: Frank da Cruz , Columbia University Academic Information Systems, New York City. Copyright (C) 1985, 2007, Trustees of Columbia University in the City of New York. All rights reserved. See the C-Kermit COPYING.TXT file or the copyright text in the ckcmai.c module for disclaimer and permissions. */ #ifndef NOXFER #include "ckcsym.h" #include "ckcdeb.h" #include "ckcasc.h" #include "ckcker.h" #ifdef OS2 #ifndef NT #define INCL_NOPM #define INCL_VIO /* Needed for ckocon.h */ #include #undef COMMENT #endif /* NT */ #include "ckocon.h" #endif /* OS2 */ /* Note -- This file may also be preprocessed by the UNIX Lex program, but you must indent the above #include statements before using Lex, and then restore them to the left margin in the resulting C program before compilation. Also, the invocation of the "wart()" function below must be replaced by an invocation of the "yylex()" function. It might also be necessary to remove comments in the (%)(%)...(%)(%) section. */ /* State definitions for Wart (or Lex) */ #define ipkt 1 #define rfile 2 #define rattr 3 #define rdpkt 4 #define ssinit 5 #define ssfile 6 #define ssattr 7 #define ssdata 8 #define sseof 9 #define sseot 10 #define serve 11 #define generic 12 #define get 13 #define rgen 14 #define ssopkt 15 #define ropkt 16 _PROTOTYP(static VOID xxproto,(void)); _PROTOTYP(static VOID wheremsg,(void)); _PROTOTYP(int wart,(void)); _PROTOTYP(static int sgetinit,(int,int)); _PROTOTYP(int sndspace,(int)); /* External C-Kermit variable declarations */ extern char *versio, *srvtxt, *cmarg, *cmarg2, **cmlist, *rf_err; extern char * rfspec, * sfspec, * srfspec, * rrfspec; extern char * prfspec, * psfspec, * psrfspec, * prrfspec; extern char *cdmsgfile[]; extern char * snd_move, * snd_rename, * srimsg; extern char filnam[], ofilnam[], fspec[], ttname[], ofn1[]; extern CHAR sstate, *srvptr, *data; extern int timint, rtimo, nfils, hcflg, xflg, flow, mdmtyp, network; extern int oopts, omode, oname, opath, nopush, isguest, xcmdsrc, rcdactive; extern int rejection, moving, fncact, bye_active, urserver, fatalio; extern int protocol, prefixing, filcnt, carrier, fnspath, interrupted; extern int recursive, inserver, nzxopts, idletmo, srvidl, xfrint; extern struct ck_p ptab[]; extern int remfile, rempipe, xferstat, filestatus, wearealike, fackpath; extern int patterns, filepeek, gnferror; extern char * remdest; #ifdef PKTZEROHACK #define PKTZEROLEN 32 static char ipktack[PKTZEROLEN]; static int ipktlen = 0; #endif /* PKTZEROHACK */ static int s_timint = -1; /* For saving timeout value */ static int myjob = 0; static int havefs = 0; #ifdef CK_LOGIN static int logtries = 0; #endif /* CK_LOGIN */ static int cancel = 0; int fackbug = 0; #ifdef STREAMING extern int streaming, streamok; static VOID streamon() { if (streamok) { debug(F100,"streamon","",0); streaming = 1; timint = 0; /* No timeouts while streaming. */ } } #ifdef COMMENT /* (not used) */ static VOID streamoff() { if (streaming) { debug(F100,"streamoff","",0); streaming = 0; timint = s_timint; /* Restore timeout */ } } #endif /* COMMENT */ #else /* STREAMING */ #define streamon() #define streamoff() #endif /* STREAMING */ #ifndef NOSPL _PROTOTYP( int addmac, (char *, char *) ); _PROTOTYP( int zzstring, (char *, char **, int *) ); #endif /* NOSPL */ #ifndef NOICP _PROTOTYP( int cmdsrc, (void) ); #endif /* NOICP */ #ifndef NOSERVER extern char * x_user, * x_passwd, * x_acct; extern int x_login, x_logged; #endif /* NOSERVER */ #include "ckcnet.h" #ifdef TNCODE extern int ttnproto; /* Network protocol */ #endif /* TNCODE */ #ifdef CK_SPEED extern short ctlp[]; /* Control-character prefix table */ #endif /* CK_SPEED */ #ifdef TNCODE extern int tn_b_nlm, tn_b_xfer, tn_nlm; #ifdef CK_ENCRYPTION extern int tn_no_encrypt_xfer; #endif /* CK_ENCRYPTION */ #endif /* TNCODE */ #ifdef TCPSOCKET #ifndef NOLISTEN extern int tcpsrfd; #endif /* NOLISTEN */ #endif /* TCPSOCKET */ extern int cxseen, czseen, server, srvdis, local, displa, bctu, bctr, bctl; extern int quiet, tsecs, parity, backgrd, nakstate, atcapu, wslotn, winlo; extern int wslots, success, xitsta, rprintf, discard, cdtimo, keep, fdispla; extern int timef, stdinf, rscapu, sendmode, epktflg, epktrcvd, epktsent; extern int binary, fncnv; extern long speed, ffc, crc16, calibrate, dest; #ifdef COMMENT extern char *TYPCMD, *DIRCMD, *DIRCM2; #endif /* COMMENT */ #ifndef OS2 extern char *SPACMD, *SPACM2, *WHOCMD; #endif /* OS2 */ extern CHAR *rdatap; extern struct zattr iattr; #ifdef VMS extern int batch; #endif /* VMS */ #ifdef GFTIMER extern CKFLOAT fptsecs; #endif /* GFTIMER */ extern CHAR *srvcmd; extern CHAR *epktmsg; #ifdef CK_TMPDIR extern int f_tmpdir; /* Directory changed temporarily */ extern char savdir[]; /* For saving current directory */ extern char * dldir; #endif /* CK_TMPDIR */ extern int query; /* Query-active flag */ #ifndef NOSPL extern int cmdlvl; char querybuf[QBUFL+1] = { NUL, NUL }; /* QUERY response buffer */ char *qbufp = querybuf; /* Pointer to it */ int qbufn = 0; /* Length of data in it */ #else extern int tlevel; #endif /* NOSPL */ #ifndef NOICP extern int escape; #endif /* NOICP */ /* If the following flag is nonzero when the protocol module is entered, then server mode persists for exactly one transaction, rather than looping until BYE or FINISH is received. */ extern int justone; static int r_save = -1; static int p_save = -1; /* Function to let remote-mode user know where their file(s) went */ int whereflg = 1; /* Unset with SET XFER REPORT */ static VOID wheremsg() { extern int quiet, filrej; int n; n = filcnt - filrej; debug(F101,"wheremsg n","",n); debug(F110,"wheremsg prfspec",prfspec,0); debug(F110,"wheremsg rfspec",rfspec,0); debug(F110,"wheremsg psfspec",psfspec,0); debug(F110,"wheremsg sfspec",sfspec,0); debug(F110,"wheremsg prrfspec",prrfspec,0); debug(F110,"wheremsg rrfspec",rrfspec,0); debug(F110,"wheremsg psrfspec",psrfspec,0); debug(F110,"wheremsg srfspec",srfspec,0); if (!quiet && !local) { if (n == 1) { switch (myjob) { case 's': if (sfspec) { printf(" SENT: [%s]",sfspec); if (srfspec) printf(" To: [%s]",srfspec); printf(" (%s)\r\n", success ? "OK" : "FAILED"); } break; case 'r': case 'v': if (rrfspec) { printf(" RCVD: [%s]",rrfspec); if (rfspec) printf(" To: [%s]",rfspec); printf(" (%s)\r\n", success ? "OK" : "FAILED"); } } } else if (n > 1) { switch (myjob) { case 's': if (sfspec) { printf(" SENT: (%d files)",n); if (srfspec) printf(" Last: [%s]",srfspec); printf(" (%s)\r\n", success ? "OK" : "FAILED"); } break; case 'r': case 'v': if (rrfspec) { printf(" RCVD: (%d files)",n); if (rfspec) printf(" Last: [%s]",rfspec); printf(" (%s)\r\n", success ? "OK" : "FAILED"); } } } else if (n == 0) { if (myjob == 's') printf(" SENT: (0 files) \r\n"); else if (myjob == 'r' || myjob == 'v') printf(" RCVD: (0 files) \r\n"); } } } static VOID rdebug() { if (server) debug(F111,"RESUME","server=1",justone); else debug(F111,"RESUME","server=0",justone); } /* Flags for the ENABLE and DISABLE commands */ extern int en_cpy, en_cwd, en_del, en_dir, en_fin, en_get, en_bye, en_mai, en_pri, en_hos, en_ren, en_sen, en_spa, en_set, en_typ, en_who, en_ret, en_xit, en_mkd, en_rmd; #ifndef NOSPL extern int en_asg, en_que; #endif /* NOSPL */ extern int what, lastxfer; /* Global variables declared here */ int whatru = 0; /* What are you. */ int whatru2 = 0; /* What are you, cont'd. */ /* Local variables */ static char vstate = 0; /* Saved State */ static char vcmd = 0; /* Saved Command */ static int reget = 0; /* Flag for executing REGET */ static int retrieve = 0; /* Flag for executing RETRIEVE */ static int opkt = 0; /* Send Extended GET packet */ static int x; /* General-purpose integer */ static char *s; /* General-purpose string pointer */ /* Macros - Note, BEGIN is predefined by Wart (and Lex) as "state = ", */ /* BEGIN is NOT a GOTO! */ #define TINIT if (tinit(1) < 0) return(-9) #define SERVE { TINIT; resetc(); nakstate=1; what=W_NOTHING; cmarg2=""; \ sendmode=SM_SEND; havefs=0; recursive=r_save; fnspath=p_save; BEGIN serve; } #define RESUME { rdebug(); if (!server) { wheremsg(); return(0); } else \ if (justone) { justone=0; wheremsg(); return(0); } else { SERVE; } } #ifdef GFTIMER #define QUIT x=quiet; quiet=1; clsif(); clsof(1); tsecs=gtimer(); \ fptsecs=gftimer(); quiet=x; return(success) #else #define QUIT x=quiet; quiet=1; clsif(); clsof(1); tsecs=gtimer(); quiet=x; \ return(success) #endif /* GFTIMER */ /* By late 1999, the big switch() statement generated from the following state table began choking even gcc, so here we extract the code from the larger states into static routines to reduce the size of the cases and the switch() overall. The routines follow the state table; the prototypes are here. Each of these routines simply contains the text from the corresponding case, but with return(-1) added in appropriate places; see instructions after the state table switcher. */ static int rc; /* Return code for these routines */ static int rcv_s_pkt(); /* Received an S packet */ static int rcv_firstdata(); /* Received first Data packet */ static int rcv_shortreply(); /* Short reply to a REMOTE command */ static int srv_query(); /* Server answers an query */ static int srv_copy(); /* Server executes REMOTE COPY */ static int srv_rename(); /* Server executes REMOTE RENAME */ static int srv_login(); /* Server executes REMOTE LOGIN */ static int srv_timeout(); /* Server times out */ #define BEGIN state = int state = 0; int wart() { int c,actno; extern char tbl[]; while (1) { c = input() - 32; debug(F000,"PROTO input",ckitoa(state),c+32); if (c < 0 || c > 95) c = 0; if ((actno = tbl[c + state*96]) != -1) switch(actno) { case 1: { TINIT; /* Send file(s) */ if (sinit() > 0) BEGIN ssinit; else RESUME; } break; case 2: { TINIT; nakstate = 1; BEGIN get; } break; case 3: { /* Client sends a GET command */ TINIT; vstate = get; reget = 0; retrieve = 0; opkt = 0; vcmd = 0; #ifdef PKTZEROHACK ipktack[0] = NUL; #endif /* PKTZEROHACK */ if (sipkt('I') >= 0) BEGIN ipkt; else RESUME; } break; case 4: { /* Client sends a RETRIEVE command */ TINIT; vstate = get; reget = 0; retrieve = 1; opkt = 0; vcmd = 0; if (sipkt('I') >= 0) BEGIN ipkt; else RESUME; } break; case 5: { /* Client sends a REGET command */ TINIT; vstate = get; reget = 1; retrieve = 0; opkt = 0; vcmd = 0; if (sipkt('I') >= 0) BEGIN ipkt; else RESUME; } break; case 6: { /* Client sends Extended GET Packet */ TINIT; vstate = get; reget = oopts & GOPT_RES; retrieve = oopts & GOPT_DEL; opkt = 1; vcmd = 0; if (sipkt('I') >= 0) BEGIN ipkt; else RESUME; } break; case 7: { /* Client sends a Host command */ TINIT; vstate = rgen; vcmd = 'C'; if (sipkt('I') >= 0) BEGIN ipkt; else RESUME; } break; case 8: { TINIT; /* Client sends a Kermit command */ vstate = rgen; vcmd = 'K'; if (sipkt('I') >= 0) BEGIN ipkt; else RESUME; } break; case 9: { /* Client sends a REMOTE command */ TINIT; vstate = rgen; vcmd = 'G'; if (sipkt('I') >= 0) BEGIN ipkt; else RESUME; } break; case 10: { /* Enter server mode */ int x; x = justone; if (!ENABLED(en_del)) { /* If DELETE is disabled */ if (fncact == XYFX_B || /* undo any file collision action */ fncact == XYFX_U || /* that could result in deletion or */ fncact == XYFX_A || /* modification of existing files. */ fncact == XYFX_X) { #ifndef NOICP extern int g_fncact; g_fncact = fncact; /* Save current setting */ #endif /* NOICP */ fncact = XYFX_R; /* Change to RENAME */ debug(F101,"server DELETE disabled so fncact RENAME","",fncact); } } SERVE; /* tinit() clears justone... */ justone = x; #ifdef IKSDB if (ikdbopen) slotstate(what, "SERVER", "", ""); #endif /* IKSDB */ } break; case 11: { int b1 = 0, b2 = 0; if (!data) TINIT; /* "ABEND" -- Tell other side. */ #ifndef pdp11 if (epktflg) { /* If because of E-PACKET command */ b1 = bctl; b2 = bctu; /* Save block check type */ bctl = bctu = 1; /* set it to 1 */ } #endif /* pdp11 */ errpkt((CHAR *)"User cancelled"); /* Send the packet */ #ifndef pdp11 if (epktflg) { /* Restore the block check */ epktflg = 0; bctl = b1; bctu = b2; } #endif /* pdp11 */ success = 0; return(0); /* Return from protocol. */ } break; case 12: { /* Receive Send-Init packet. */ rc = rcv_s_pkt(); cancel = 0; /* Reset cancellation counter */ debug(F101,"rcv_s_pkt","",rc); if (rc > -1) return(rc); /* (see below) */ } break; case 13: { /* Get ack for I-packet */ int x = 0; #ifdef PKTZEROHACK ckstrncpy(ipktack,(char *)rdatap,PKTZEROLEN); /* Save a copy of the ACK */ ipktlen = strlen(ipktack); #endif /* PKTZEROHACK */ spar(rdatap); /* Set parameters */ cancel = 0; winlo = 0; /* Set window-low back to zero */ debug(F101,"Y winlo","",winlo); urserver = 1; /* So I know I'm talking to a server */ if (vcmd) { /* If sending a generic command */ if (tinit(0) < 0) return(-9); /* Initialize many things */ x = scmd(vcmd,(CHAR *)cmarg); /* Do that */ if (x >= 0) x = 0; /* (because of O-Packet) */ debug(F101,"proto G packet scmd","",x); vcmd = 0; /* and then un-remember it. */ } else if (vstate == get) { debug(F101,"REGET sstate","",sstate); x = srinit(reget, retrieve, opkt); /* GET or REGET, etc */ } if (x < 0) { /* If command was too long */ if (!srimsg) srimsg = "Error sending string"; errpkt((CHAR *)srimsg); /* cancel both sides. */ success = 0; RESUME; } else if (x > 0) { /* Need to send more O-Packets */ BEGIN ssopkt; } else { rtimer(); /* Reset the elapsed seconds timer. */ #ifdef GFTIMER rftimer(); #endif /* GFTIMER */ winlo = 0; /* Window back to 0, again. */ debug(F101,"Y vstate","",vstate); nakstate = 1; /* Can send NAKs from here. */ BEGIN vstate; /* Switch to desired state */ } } break; case 14: { /* Got ACK to O-Packet */ debug(F100,"CPCPRO Y","",0); x = sopkt(); debug(F101,"CPCPRO Y x","",x); if (x < 0) { /* If error */ errpkt((CHAR *)srimsg); /* cancel both sides. */ success = 0; RESUME; } else if (x == 0) { /* This was the last O-Packet */ rtimer(); /* Reset the elapsed seconds timer. */ #ifdef GFTIMER rftimer(); #endif /* GFTIMER */ winlo = 0; /* Window back to 0, again. */ debug(F101,"Y winlo","",winlo); nakstate = 1; /* Can send NAKs from here. */ BEGIN vstate; /* Switch to desired state */ } debug(F101,"CPCPRO Y not changing state","",x); } break; case 15: { /* Ignore Error reply to I packet */ int x = 0; winlo = 0; /* Set window-low back to zero */ debug(F101,"E winlo","",winlo); if (vcmd) { /* In case other Kermit doesn't */ if (tinit(0) < 0) return(-9); x = scmd(vcmd,(CHAR *)cmarg); /* understand I-packets. */ if (x >= 0) x = 0; /* (because of O-Packet) */ vcmd = 0; /* Otherwise act as above... */ } else if (vstate == get) x = srinit(reget, retrieve, opkt); if (x < 0) { /* If command was too long */ errpkt((CHAR *)srimsg); /* cancel both sides. */ success = 0; RESUME; } else if (x > 0) { /* Need to send more O-Packets */ BEGIN ssopkt; } else { freerpkt(winlo); /* Discard the Error packet. */ debug(F101,"E winlo","",winlo); winlo = 0; /* Back to packet 0 again. */ nakstate = 1; /* Can send NAKs from here. */ BEGIN vstate; } } break; case 16: { /* Resend of previous I-pkt ACK, same seq number */ freerpkt(0); /* Free the ACK's receive buffer */ resend(0); /* Send the GET packet again. */ } break; case 17: { /* Get I-packet */ #ifndef NOSERVER spar(rdatap); /* Set parameters from it */ ack1(rpar()); /* Respond with our own parameters */ #ifdef COMMENT pktinit(); /* Reinitialize packet numbers */ #else #ifdef COMMENT /* This can't be right - it undoes the stuff we just negotiated */ x = justone; tinit(1); /* Reinitialize EVERYTHING */ justone = x; /* But this... */ #else tinit(0); /* Initialize most things */ #endif /* COMMENT */ #endif /* COMMENT */ #endif /* NOSERVER */ cancel = 0; /* Reset cancellation counter */ } break; case 18: { /* GET */ #ifndef NOSERVER if (x_login && !x_logged) { errpkt((CHAR *)"Login required"); SERVE; } else if (sgetinit(0,0) < 0) { RESUME; } else { #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "GET", (char *)srvcmd); #endif /* CKSYSLOG */ BEGIN ssinit; } #endif /* NOSERVER */ } break; case 19: { /* GET /DELETE (RETRIEVE) */ #ifndef NOSERVER if (x_login && !x_logged) { errpkt((CHAR *)"Login required"); RESUME; } else if (!ENABLED(en_del)) { errpkt((CHAR *)"Deleting files is disabled"); RESUME; } else if (sgetinit(0,0) < 0) { RESUME; } else { moving = 1; #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "GET /DELETE", (char *)srvcmd); #endif /* CKSYSLOG */ BEGIN ssinit; } #endif /* NOSERVER */ } break; case 20: { /* GET /RECURSIVE */ #ifndef NOSERVER recursive = 1; /* Set these before sgetinit() */ if (fnspath == PATH_OFF) fnspath = PATH_REL; /* Don't worry, they will be */ if (x_login && !x_logged) { /* reset next time through. */ errpkt((CHAR *)"Login required"); RESUME; } else if (sgetinit(0,0) < 0) { RESUME; } else { #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "GET /RECURSIVE", (char *)srvcmd); #endif /* CKSYSLOG */ BEGIN ssinit; } #endif /* NOSERVER */ } break; case 21: { /* GET /RECURSIVE /DELETE */ #ifndef NOSERVER recursive = 1; /* Set these before sgetinit() */ if (fnspath == PATH_OFF) fnspath = PATH_REL; /* Don't worry, they will be */ moving = 1; /* reset next time through. */ if (x_login && !x_logged) { errpkt((CHAR *)"Login required"); RESUME; } else if (!ENABLED(en_del)) { errpkt((CHAR *)"Deleting files is disabled"); RESUME; } else if (sgetinit(0,0) < 0) { RESUME; } else { #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR,1,"server", "GET /RECURSIVE /DELETE",(char *)srvcmd); #endif /* CKSYSLOG */ BEGIN ssinit; } #endif /* NOSERVER */ } break; case 22: { /* GET /RECOVER (REGET) */ #ifndef NOSERVER if (x_login && !x_logged) { errpkt((CHAR *)"Login required"); SERVE; } else if (sgetinit(1,0) < 0) { RESUME; } else { #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "GET /RECOVER", (char *)srvcmd); #endif /* CKSYSLOG */ BEGIN ssinit; } #endif /* NOSERVER */ } break; case 23: { /* Extended GET */ #ifndef NOSERVER if (x_login && !x_logged) { /* (any combination of options) */ errpkt((CHAR *)"Login required"); SERVE; } else if ((x = sgetinit(0,1)) < 0) { debug(F101,"CKCPRO O sgetinit fail","",x); RESUME; } else if (x == 0) { debug(F101,"CKCPRO O sgetinit done","",x); #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "EXTENDED GET", (char *)srvcmd); #endif /* CKSYSLOG */ BEGIN ssinit; } else { /* Otherwise stay in this state */ debug(F101,"CKCPRO O sgetinit TBC","",x); ack(); BEGIN ropkt; } #endif /* NOSERVER */ } break; case 24: { #ifndef NOSERVER if (x_login && !x_logged) { /* (any combination of options) */ errpkt((CHAR *)"Login required"); SERVE; } else if ((x = sgetinit(0,1)) < 0) { debug(F101,"CKCPRO O sgetinit fail","",x); RESUME; } else if (x == 0) { debug(F101,"CKCPRO O sgetinit done","",x); #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "EXTENDED GET", (char *)srvcmd); #endif /* CKSYSLOG */ BEGIN ssinit; } else { /* Otherwise stay in this state */ debug(F101,"CKCPRO O sgetinit TBC","",x); ack(); } #endif /* NOSERVER */ } break; case 25: { /* Generic server command */ #ifndef NOSERVER srvptr = srvcmd; /* Point to command buffer */ decode(rdatap,putsrv,0); /* Decode packet data into it */ putsrv(NUL); /* Insert a couple nulls */ putsrv(NUL); /* for termination */ if (srvcmd[0]) { sstate = srvcmd[0]; /* Set requested start state */ if (x_login && !x_logged && /* Login required? */ /* Login, Logout, and Help are allowed when not logged in */ sstate != 'I' && sstate != 'L' && sstate != 'H') { errpkt((CHAR *)"Login required"); SERVE; } else { nakstate = 0; /* Now I'm the sender. */ what = W_REMO; /* Doing a REMOTE command. */ #ifdef STREAMING if (!streaming) #endif /* STREAMING */ if (timint < 1) timint = chktimo(rtimo,timef); /* Switch to per-packet timer */ binary = XYFT_T; /* Switch to text mode */ BEGIN generic; /* Switch to generic command state */ } } else { errpkt((CHAR *)"Badly formed server command"); /* report error */ RESUME; /* & go back to server command wait */ } #endif /* NOSERVER */ } break; case 26: { /* Receive Host command */ #ifndef NOSERVER if (x_login && !x_logged) { errpkt((CHAR *)"Login required"); SERVE; } else if (!ENABLED(en_hos)) { errpkt((CHAR *)"REMOTE HOST disabled"); RESUME; } else if (nopush) { errpkt((CHAR *)"HOST commands not available"); RESUME; } else { srvptr = srvcmd; /* Point to command buffer */ decode(rdatap,putsrv,0); /* Decode command packet into it */ putsrv(NUL); /* Null-terminate */ nakstate = 0; /* Now sending, not receiving */ binary = XYFT_T; /* Switch to text mode */ if (syscmd((char *)srvcmd,"")) { /* Try to execute the command */ what = W_REMO; /* Doing a REMOTE command. */ #ifdef STREAMING if (!streaming) #endif /* STREAMING */ if (timint < 1) timint = chktimo(rtimo,timef); /* Switch to per-packet timer */ #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "REMOTE HOST", (char *)srvcmd); #endif /* CKSYSLOG */ BEGIN ssinit; /* If OK, send back its output */ } else { /* Otherwise */ errpkt((CHAR *)"Can't do system command"); /* report error */ RESUME; /* & go back to server command wait */ } } #endif /* NOSERVER */ } break; case 27: { /* Interrupted or connection lost */ rc = srv_timeout(); debug(F101,"srv_timeout","",rc); if (rc > -1) return(rc); /* (see below) */ } break; case 28: { /* Server got a NAK in command-wait */ #ifndef NOSERVER errpkt((CHAR *)"Did you say RECEIVE instead of GET?"); RESUME; #endif /* NOSERVER */ } break; case 29: { /* Any other command in this state */ #ifndef NOSERVER if (c != ('E' - SP) && c != ('Y' - SP)) /* except E and Y packets. */ errpkt((CHAR *)"Unimplemented server function"); /* If we answer an E with an E, we get an infinite loop. */ /* A Y (ACK) can show up here if we sent back a short-form reply to */ /* a G packet and it was echoed. ACKs can be safely ignored here. */ RESUME; /* Go back to server command wait. */ #endif /* NOSERVER */ } break; case 30: { /* Login/Out */ rc = srv_login(); debug(F101,"I srv_login","",rc); if (rc > -1) return(rc); /* (see below) */ } break; case 31: { /* Got REMOTE CD command */ #ifndef NOSERVER #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "REMOTE CD", (char *)srvcmd); #endif /* CKSYSLOG */ if (!ENABLED(en_cwd)) { errpkt((CHAR *)"REMOTE CD disabled"); RESUME; } else { char * p = NULL; x = cwd((char *)(srvcmd+1)); /* Try to change directory */ #ifdef IKSDB if (ikdbopen) slotstate(what,"REMOTE CD", (char *)(srvcmd+2), ""); #endif /* IKSDB */ if (!x) { /* Failed */ errpkt((CHAR *)"Can't change directory"); RESUME; /* Back to server command wait */ } else if (x == 2) { /* User wants message */ if (!ENABLED(en_typ)) { /* Messages (REMOTE TYPE) disabled? */ errpkt((CHAR *)"REMOTE TYPE disabled"); RESUME; } else { /* TYPE is enabled */ int i; for (i = 0; i < 8; i++) { if (zchki(cdmsgfile[i]) > -1) { break; } } binary = XYFT_T; /* Use text mode for this. */ if (i < 8 && sndtype(cdmsgfile[i])) { /* Have readme file? */ BEGIN ssinit; /* OK */ } else { /* not OK */ p = zgtdir(); if (!p) p = ""; success = (*p) ? 1 : 0; ack1((CHAR *)p); /* ACK with new directory name */ success = 1; RESUME; /* wait for next server command */ } } } else { /* User doesn't want message */ p =zgtdir(); if (!p) p = ""; success = (*p) ? 1 : 0; ack1((CHAR *)p); success = 1; RESUME; /* Wait for next server command */ } } #endif /* NOSERVER */ } break; case 32: { /* Got REMOTE PWD command */ #ifndef NOSERVER #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "REMOTE PWD", NULL); #endif /* CKSYSLOG */ if (!ENABLED(en_cwd)) { errpkt((CHAR *)"REMOTE CD disabled"); RESUME; } else { if (encstr((CHAR *)zgtdir()) > -1) { /* Encode current directory */ ack1(data); /* If it fits, send it back in ACK */ success = 1; } else { /* Failed */ ack(); /* Send empty ACK */ success = 0; /* and indicate failure locally */ } RESUME; /* Back to server command wait */ } #endif /* NOSERVER */ } break; case 33: { /* REMOTE DIRECTORY command */ #ifndef NOSERVER char *n2; #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "REMOTE DIRECTORY", (char *)srvcmd); #endif /* CKSYSLOG */ if (!ENABLED(en_dir)) { /* If DIR is disabled, */ errpkt((CHAR *)"REMOTE DIRECTORY disabled"); /* refuse. */ RESUME; } else { /* DIR is enabled. */ #ifdef IKSDB if (ikdbopen) slotstate(what,"REMOTE DIR", (char *)(srvcmd+2), ""); #endif /* IKSDB */ if (!ENABLED(en_cwd)) { /* But CWD is disabled */ zstrip((char *)(srvcmd+2),&n2); /* and they included a pathname, */ if (strcmp((char *)(srvcmd+2),n2)) { /* so refuse. */ errpkt((CHAR *)"Access denied"); RESUME; /* Remember, this is not a goto! */ } } if (state == generic) { /* It's OK to go ahead. */ #ifdef COMMENT n2 = (*(srvcmd+2)) ? DIRCMD : DIRCM2; if (syscmd(n2,(char *)(srvcmd+2))) /* If it can be done */ #else int x; if ((x = snddir((char*)(srvcmd+2))) > 0) #endif /* COMMENT */ { BEGIN ssinit; /* send the results back; */ } else { /* otherwise */ if (x < 0) errpkt((CHAR *)"No files match"); else errpkt((CHAR *)"Can't list directory"); RESUME; /* return to server command wait */ } } } #endif /* NOSERVER */ } break; case 34: { /* REMOTE DELETE (Erase) */ #ifndef NOSERVER char *n2; #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "REMOTE DELETE", (char *)srvcmd); #endif /* CKSYSLOG */ if (!ENABLED(en_del)) { errpkt((CHAR *)"REMOTE DELETE disabled"); RESUME; } else { /* DELETE is enabled */ #ifdef IKSDB if (ikdbopen) slotstate(what,"REMOTE DELETE", (char *)(srvcmd+2), ""); #endif /* IKSDB */ if (!ENABLED(en_cwd)) { /* but CWD is disabled */ zstrip((char *)(srvcmd+2),&n2); /* and they included a pathname, */ if (strcmp((char *)(srvcmd+2),n2)) { /* so refuse. */ errpkt((CHAR *)"Access denied"); RESUME; /* Remember, this is not a goto! */ } } else if (isdir((char *)(srvcmd+2))) { /* A directory name? */ errpkt((CHAR *)"It's a directory"); RESUME; } if (state == generic) { /* It's OK to go ahead. */ int x; if ((x = snddel((char*)(srvcmd+2))) > 0) { BEGIN ssinit; /* If OK send results back */ } else { /* otherwise */ if (x < 0) errpkt((CHAR *)"File not found"); /* report failure */ else errpkt((CHAR *)"DELETE failed"); RESUME; /* & return to server command wait */ } } } #endif /* NOSERVER */ } break; case 35: { /* FINISH */ #ifndef NOSERVER #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "FINISH", NULL); #endif /* CKSYSLOG */ #ifdef IKSDB if (ikdbopen) slotstate(what,"SERVER FINISH", "", ""); #endif /* IKSDB */ if (!ENABLED(en_fin)) { errpkt((CHAR *)"FINISH disabled"); RESUME; } else { ack(); /* Acknowledge */ xxscreen(SCR_TC,0,0L,""); /* Display */ success = 1; return(0); /* Done */ } #endif /* NOSERVER */ } break; case 36: { /* EXIT */ #ifndef NOSERVER #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "REMOTE EXIT", NULL); #endif /* CKSYSLOG */ #ifdef IKSDB if (ikdbopen) slotstate(what,"REMOTE EXIT", "", ""); #endif /* IKSDB */ if (!ENABLED(en_xit)) { errpkt((CHAR *)"EXIT disabled"); RESUME; } else { ack(); /* Acknowledge */ xxscreen(SCR_TC,0,0L,""); /* Display */ doexit(GOOD_EXIT,xitsta); } #endif /* NOSERVER */ } break; case 37: { /* BYE (Logout) */ #ifndef NOSERVER #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "BYE", NULL); #endif /* CKSYSLOG */ #ifdef IKSDB if (ikdbopen) slotstate(what,"SERVER BYE", "", ""); #endif /* IKSDB */ if (!ENABLED(en_bye)) { errpkt((CHAR *)"BYE disabled"); RESUME; } else { ack(); /* Acknowledge */ success = 1; msleep(750); /* Give the ACK time to get out */ if (local) ttres(); /* Reset the terminal */ xxscreen(SCR_TC,0,0L,""); /* Display */ doclean(1); /* Clean up files, etc */ #ifdef DEBUG debug(F100,"C-Kermit BYE - Loggin out...","",0); zclose(ZDFILE); #endif /* DEBUG */ #ifdef IKSD #ifdef CK_LOGIN if (inserver) ckxlogout(); else #endif /* CK_LOGIN */ #endif /* IKSD */ #ifdef TCPSOCKET #ifndef NOLISTEN if (network && tcpsrfd > 0 && !inserver) doexit(GOOD_EXIT,xitsta); else #endif /* NOLISTEN */ #endif /* TCPSOCKET */ return(zkself()); /* Try to log self out */ } #endif /* NOSERVER */ } break; case 38: { /* REMOTE HELP */ #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "REMOTE HELP", NULL); #endif /* CKSYSLOG */ #ifdef IKSDB if (ikdbopen) slotstate(what,"REMOTE HELP", "", ""); #endif /* IKSDB */ #ifndef NOSERVER if (sndhlp()) { BEGIN ssinit; /* try to send it */ } else { /* If not ok, */ errpkt((CHAR *)"Can't send help"); /* send error message instead */ RESUME; /* and return to server command wait */ } #endif /* NOSERVER */ } break; case 39: { /* REMOTE RENAME */ rc = srv_rename(); debug(F101,"srv_rename","",rc); if (rc > -1) return(rc); /* (see below) */ } break; case 40: { /* REMOTE COPY */ rc = srv_copy(); debug(F101,"srv_copy","",rc); if (rc > -1) return(rc); /* (see below) */ } break; case 41: { /* REMOTE SET */ #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "REMOTE SET", (char *)srvcmd); #endif /* CKSYSLOG */ #ifndef NOSERVER #ifdef IKSDB if (ikdbopen) slotstate(what,"REMOTE SET", (char *)(srvcmd+1), ""); #endif /* IKSDB */ if (!ENABLED(en_set)) { errpkt((CHAR *)"REMOTE SET disabled"); RESUME; } else { if (remset((char *)(srvcmd+1))) { /* Try to do what they ask */ success = 1; ack(); /* If OK, then acknowledge */ } else /* Otherwise */ errpkt((CHAR *)"Unknown REMOTE SET parameter"); /* give error msg */ RESUME; /* Return to server command wait */ } #endif /* NOSERVER */ } break; case 42: { /* REMOTE TYPE */ #ifndef NOSERVER char *n2; #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "REMOTE TYPE", (char *)srvcmd); #endif /* CKSYSLOG */ if (!ENABLED(en_typ)) { errpkt((CHAR *)"REMOTE TYPE disabled"); RESUME; } else { #ifdef IKSDB if (ikdbopen) slotstate(what,"REMOTE TYPE", (char *)(srvcmd+2), ""); #endif /* IKSDB */ if (!ENABLED(en_cwd)) { /* If CWD disabled */ zstrip((char *)(srvcmd+2),&n2); /* and they included a pathname, */ if (strcmp((char *)(srvcmd+2),n2)) { /* refuse. */ errpkt((CHAR *)"Access denied"); RESUME; /* Remember, this is not a goto! */ } } if (state == generic) { /* It's OK to go ahead. */ binary = XYFT_T; /* Use text mode for this. */ if ( /* (RESUME didn't change state) */ #ifdef COMMENT syscmd(TYPCMD,(char *)(srvcmd+2)) /* Old way */ #else sndtype((char *)(srvcmd+2)) /* New way */ #endif /* COMMENT */ ) BEGIN ssinit; /* OK */ else { /* not OK */ errpkt((CHAR *)"Can't type file"); /* give error message */ RESUME; /* wait for next server command */ } } } #endif /* NOSERVER */ } break; case 43: { /* REMOTE MKDIR */ #ifndef NOSERVER #ifdef CK_MKDIR #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "REMOTE MKDIR", (char *)srvcmd); #endif /* CKSYSLOG */ #ifdef IKSDB if (ikdbopen) slotstate(what,"REMOTE MKDIR", (char *)(srvcmd+2), ""); #endif /* IKSDB */ if (!ENABLED(en_mkd)) { errpkt((CHAR *)"REMOTE MKDIR disabled"); RESUME; } else if (!ENABLED(en_cwd)) { /* If CWD disabled */ errpkt((CHAR *)"Directory access restricted"); RESUME; /* Remember, this is not a goto! */ } if (state == generic) { /* OK to go ahead. */ char *p = NULL; x = ckmkdir(0,(char *)(srvcmd+2),&p,0,1); /* Make the directory */ if (!p) p = ""; if (x > -1) { encstr((CHAR *)p); /* OK - encode the name */ ack1(data); /* Send short-form response */ success = 1; RESUME; } else { /* not OK */ if (!*p) p = "Directory creation failure"; errpkt((CHAR *)p); /* give error message */ RESUME; /* Wait for next server command */ } } #else errpkt((CHAR *)"REMOTE MKDIR not available"); RESUME; #endif /* CK_MKDIR */ #endif /* NOSERVER */ } break; case 44: { /* REMOTE RMDIR */ #ifndef NOSERVER #ifdef CK_MKDIR #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "REMOTE RMDIR", (char *)srvcmd); #endif /* CKSYSLOG */ #ifdef IKSDB if (ikdbopen) slotstate(what,"REMOTE RMDIR", (char *)(srvcmd+2), ""); #endif /* IKSDB */ if (!ENABLED(en_rmd)) { errpkt((CHAR *)"REMOTE RMDIR disabled"); RESUME; } else if (!ENABLED(en_cwd)) { /* If CWD disabled */ errpkt((CHAR *)"Directory access restricted"); RESUME; /* Remember, this is not a goto! */ } if (state == generic) { /* OK to go ahead. */ char *p = NULL; x = ckmkdir(1,(char *)(srvcmd+2),&p,0,1); if (!p) p = ""; if (x > -1) { encstr((CHAR *)p); /* OK - encode the name */ ack1(data); /* Send short-form response */ success = 1; RESUME; } else { /* not OK */ if (!*p) p = "Directory removal failure"; errpkt((CHAR *)p); /* give error message */ RESUME; /* Wait for next server command */ } } #else errpkt((CHAR *)"REMOTE RMDIR not available"); RESUME; #endif /* CK_MKDIR */ #endif /* NOSERVER */ } break; case 45: { /* REMOTE SPACE */ #ifndef NOSERVER #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "REMOTE SPACE", (char *)srvcmd); #endif /* CKSYSLOG */ if (!ENABLED(en_spa)) { errpkt((CHAR *)"REMOTE SPACE disabled"); RESUME; } else { x = srvcmd[1]; /* Get area to check */ x = ((x == NUL) || (x == SP) #ifdef OS2 || (x == '!') || (srvcmd[3] == ':') #endif /* OS2 */ ); #ifdef IKSDB if (ikdbopen) slotstate(what, "REMOTE SPACE", (x ? "" : (char *)srvcmd), "" ); #endif /* IKSDB */ if (!x && !ENABLED(en_cwd)) { /* CWD disabled */ errpkt((CHAR *)"Access denied"); /* and non-default area given, */ RESUME; /* refuse. */ } else { #ifdef OS2 _PROTOTYP(int sndspace,(int)); if (sndspace(x ? toupper(srvcmd[2]) : 0)) { BEGIN ssinit; /* send the report. */ } else { /* If not ok, */ errpkt((CHAR *)"Can't send space"); /* send error message */ RESUME; /* and return to server command wait */ } #else if (nopush) x = 0; else x = (x ? syscmd(SPACMD,"") : syscmd(SPACM2,(char *)(srvcmd+2))); if (x) { /* If we got the info */ BEGIN ssinit; /* send it */ } else { /* otherwise */ errpkt((CHAR *)"Can't check space"); /* send error message */ RESUME; /* and await next server command */ } #endif /* OS2 */ } } #endif /* NOSERVER */ } break; case 46: { /* REMOTE WHO */ #ifndef NOSERVER #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "REMOTE WHO", (char *)srvcmd); #endif /* CKSYSLOG */ #ifdef IKSDB if (ikdbopen) slotstate(what,"REMOTE WHO", (char *)(srvcmd+2), ""); #endif /* IKSDB */ if (!ENABLED(en_who)) { errpkt((CHAR *)"REMOTE WHO disabled"); RESUME; } else { #ifdef OS2 _PROTOTYP(int sndwho,(char *)); if (sndwho((char *)(srvcmd+2))) { BEGIN ssinit; /* try to send it */ } else { /* If not ok, */ errpkt((CHAR *)"Can't do who command"); /* send error msg */ RESUME; /* and return to server command wait */ } #else if (syscmd(WHOCMD,(char *)(srvcmd+2))) { BEGIN ssinit; } else { errpkt((CHAR *)"Can't do who command"); RESUME; } #endif /* OS2 */ } #endif /* NOSERVER */ } break; case 47: { /* Variable query or set */ rc = srv_query(); debug(F101,"srv_query","",rc); if (rc > -1) return(rc); } break; case 48: { /* REMOTE MESSAGE command */ #ifndef NOSERVER debug(F110,"RMSG",(char *)srvcmd+2,0); xxscreen(SCR_MS,0,0L,(char *)(srvcmd+2)); ack(); RESUME; #endif /* NOSERVER */ } break; case 49: { /* Interrupted or connection lost */ #ifndef NOSERVER if (fatalio) { /* Connection lost */ #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "Interrupted", NULL); #endif /* CKSYSLOG */ success = 0; xitsta |= (what & W_KERMIT); QUIT; } else if (interrupted) { if (!ENABLED(en_fin)) { /* Ctrl-C typed */ errpkt((CHAR *)"QUIT disabled"); RESUME; } else { #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "Interrupted", NULL); #endif /* CKSYSLOG */ success = 0; xitsta |= (what & W_KERMIT); QUIT; } } else { /* Shouldn't happen */ debug(F100,"SERVER (generic) GOT UNEXPECTED 'q'","",0); QUIT; } #endif /* NOSERVER */ } break; case 50: { /* Anything else in this state... */ #ifndef NOSERVER errpkt((CHAR *)"Unimplemented REMOTE command"); /* Complain */ RESUME; /* and return to server command wait */ #endif /* NOSERVER */ } break; case 51: { /* Sent BYE and connection broken */ if (bye_active && ttchk() < 0) { msleep(500); bye_active = 0; ttclos(0); /* Close our end of the connection */ clsof(0); return(success = 1); } else { /* Other generic command */ return(success = 0); /* or connection not broken */ } } break; case 52: { /* Short-Form reply */ rc = rcv_shortreply(); debug(F101,"Y rcv_shortreply","",rc); if (rc > -1) return(rc); } break; case 53: { /* File header */ /* char *n2; */ extern int rsn; debug(F101,"F winlo 1","",winlo); xflg = 0; /* Not screen data */ if (!czseen) cancel = 0; /* Reset cancellation counter */ #ifdef CALIBRATE if (dest == DEST_N) calibrate = 1; #endif /* CALIBRATE */ if (!rcvfil(filnam)) { /* Figure out local filename */ errpkt((CHAR *)rf_err); /* Trouble */ RESUME; } else { /* Real file, OK to receive */ char * fnp; debug(F111,"F winlo 2",fspec,winlo); if (filcnt == 1) /* rcvfil set this to 1 for 1st file */ crc16 = 0L; /* Clear file CRC */ fnp = fspec; /* This is the full path */ if (server && !ENABLED(en_cwd) || /* if DISABLE CD */ !fackpath /* or F-ACK-PATH OFF */ ) { zstrip(fspec,&fnp); /* don't send back full path */ } encstr((CHAR *)fnp); if (fackbug) ack(); else ack1(data); /* Send it back in ACK */ initattr(&iattr); /* Clear file attribute structure */ streamon(); if (window(wslotn) < 0) { /* Allocate negotiated window slots */ errpkt((CHAR *)"Can't open window"); RESUME; } #ifdef IKSDB if (ikdbopen) slotstate(what, server ? "SERVER" : "", "RECEIVE", fspec ); #endif /* IKSDB */ BEGIN rattr; /* Now expect Attribute packets */ } } break; case 54: { /* X-packet instead of file header */ xflg = 1; /* Screen data */ if (!czseen) cancel = 0; /* Reset cancellation counter */ ack(); /* Acknowledge the X-packet */ initattr(&iattr); /* Initialize attribute structure */ streamon(); if (window(wslotn) < 0) { /* allocate negotiated window slots */ errpkt((CHAR *)"Can't open window"); RESUME; } #ifndef NOSPL if (query) { /* If this is the response to */ qbufp = querybuf; /* a query that we sent, initialize */ qbufn = 0; /* the response buffer */ querybuf[0] = NUL; } #endif /* NOSPL */ what = W_REMO; /* we're doing a REMOTE command */ #ifdef IKSDB if (ikdbopen) slotstate(what, server ? "SERVER" : "", "RECEIVE", fspec ); #endif /* IKSDB */ BEGIN rattr; /* Expect Attribute packets */ } break; case 55: { /* Attribute packet */ if (gattr(rdatap,&iattr) == 0) { /* Read into attribute structure */ #ifdef CK_RESEND ack1((CHAR *)iattr.reply.val); /* Reply with data */ #else ack(); /* If OK, acknowledge */ #endif /* CK_RESEND */ } else { /* Otherwise */ extern long fsize; char *r; r = getreason(iattr.reply.val); ack1((CHAR *)iattr.reply.val); /* refuse to accept the file */ xxscreen(SCR_ST,ST_REFU,0L,r); /* reason */ #ifdef TLOG if (tralog && !tlogfmt) doxlog(what,filnam,fsize,binary,1,r); #endif /* TLOG */ } } break; case 56: { /* First data packet */ debug(F100," D firstdata","",0); rc = rcv_firstdata(); debug(F101,"rcv_firstdata rc","",rc); if (rc > -1) return(rc); /* (see below) */ } break; case 57: { /* EOT, no more files */ ack(); /* Acknowledge the B packet */ reot(); /* Do EOT things */ #ifdef CK_TMPDIR /* If we were cd'd temporarily to another device or directory ... */ if (f_tmpdir) { int x; x = zchdir((char *) savdir); /* ... restore previous directory */ f_tmpdir = 0; /* and remember we did it. */ debug(F111,"ckcpro.w B tmpdir restoring",savdir,x); } #endif /* CK_TMPDIR */ RESUME; /* and quit */ } break; case 58: { /* Got Data packet */ debug(F101,"D cxseen","",cxseen); debug(F101,"D czseen","",czseen); if (cxseen || czseen || discard) { /* If file or group interruption */ CHAR * msg; msg = czseen ? (CHAR *)"Z" : (CHAR *)"X"; #ifdef STREAMING if (streaming) { /* Need to cancel */ debug(F111,"D streaming cancel",msg,cancel); if (cancel++ == 0) { /* Only do this once */ ack1(msg); /* Put "X" or "Z" in ACK */ } else if (czseen) { errpkt((CHAR *)"User canceled"); RESUME; } else { fastack(); } } else #endif /* STREAMING */ ack1(msg); } else { /* No interruption */ int rc, qf; #ifndef NOSPL qf = query; #else qf = 0; #endif /* NOSPL */ #ifdef CKTUNING rc = (binary && !parity) ? bdecode(rdatap,putfil): decode(rdatap, qf ? puttrm : putfil, 1); #else rc = decode(rdatap, qf ? puttrm : putfil, 1); #endif /* CKTUNING */ if (rc < 0) { discard = (keep == 0 || (keep == SET_AUTO && binary != XYFT_T)); errpkt((CHAR *)"Error writing data"); /* If failure, */ RESUME; } else /* Data written OK, send ACK */ #ifdef STREAMING if (streaming) fastack(); else #endif /* STREAMING */ ack(); } } break; case 59: { /* EOF immediately after A-Packet. */ rf_err = "Can't create file"; timint = s_timint; if (discard) { /* Discarding a real file... */ x = 1; } else if (xflg) { /* If screen data */ if (remfile) { /* redirected to file */ if (rempipe) /* or pipe */ x = openc(ZOFILE,remdest); /* Pipe: start command */ else x = opena(remdest,&iattr); /* File: open with attributes */ } else { /* otherwise */ x = opent(&iattr); /* "open" the screen */ } #ifdef CALIBRATE } else if (calibrate) { /* If calibration run */ x = ckopenx(&iattr); /* do this */ #endif /* CALIBRATE */ } else { /* otherwise */ x = opena(filnam,&iattr); /* open the file, with attributes */ if (x == -17) { /* REGET skipped because same size */ discard = 1; rejection = 1; } } if (!x || reof(filnam, &iattr) < 0) { /* Close output file */ errpkt((CHAR *) rf_err); /* If problem, send error msg */ RESUME; /* and quit */ } else { /* otherwise */ if (x == -17) xxscreen(SCR_ST,ST_SKIP,SKP_RES,""); ack(); /* acknowledge the EOF packet */ BEGIN rfile; /* and await another file */ } } break; case 60: { /* Ctrl-C or connection loss. */ timint = s_timint; window(1); /* Set window size back to 1... */ cxseen = 1; x = clsof(1); /* Close file */ return(success = 0); /* Failed */ } break; case 61: { /* End Of File (EOF) Packet */ /* wslots = 1; */ /* (don't set) Window size back to 1 */ #ifndef COHERENT /* Coherent compiler blows up on this switch() statement. */ x = reof(filnam, &iattr); /* Handle the EOF packet */ switch (x) { /* reof() sets the success flag */ case -5: /* Handle problems */ errpkt((CHAR *)"RENAME failed"); /* Fatal */ RESUME; break; case -4: errpkt((CHAR *)"MOVE failed"); /* Fatal */ RESUME; break; case -3: /* If problem, send error msg */ errpkt((CHAR *)"Can't print file"); /* Fatal */ RESUME; break; case -2: errpkt((CHAR *)"Can't mail file"); /* Fatal */ RESUME; break; case 2: /* Not fatal */ case 3: xxscreen(SCR_EM,0,0L,"Receiver can't delete temp file"); RESUME; break; default: if (x < 0) { /* Fatal */ errpkt((CHAR *)"Can't close file"); RESUME; } else { /* Success */ #ifndef NOSPL if (query) /* Query reponses generally */ conoll(""); /* don't have line terminators */ #endif /* NOSPL */ if (czseen) { /* Batch canceled? */ if (cancel++ == 0) { /* If we haven't tried this yet */ ack1((CHAR *)"Z"); /* Try it once */ } else { /* Otherwise */ errpkt((CHAR *)"User canceled"); /* quite with Error */ RESUME; } } else ack(); /* Acknowledge the EOF packet */ BEGIN rfile; /* and await another file */ } } #else if (reof(filnam, &iattr) < 0) { /* Close the file */ errpkt((CHAR *)"Error at end of file"); RESUME; } else { /* reof() sets success flag */ ack(); BEGIN rfile; } #endif /* COHERENT */ } break; case 62: { /* ACK for Send-Init */ spar(rdatap); /* set parameters from it */ cancel = 0; bctu = bctr; /* switch to agreed-upon block check */ bctl = (bctu == 4) ? 2 : bctu; /* Set block-check length */ #ifdef CK_RESEND if ((sendmode == SM_RESEND) && (!atcapu || !rscapu)) { /* RESEND */ errpkt((CHAR *) "RESEND capabilities not negotiated"); RESUME; } else { #endif /* CK_RESEND */ what = W_SEND; /* Remember we're sending */ lastxfer = W_SEND; x = sfile(xflg); /* Send X or F header packet */ cancel = 0; /* Reset cancellation counter */ if (x) { /* If the packet was sent OK */ if (!xflg && filcnt == 1) /* and it's a real file */ crc16 = 0L; /* Clear the file CRC */ resetc(); /* reset per-transaction counters */ rtimer(); /* reset timers */ #ifdef GFTIMER rftimer(); #endif /* GFTIMER */ streamon(); /* turn on streaming */ #ifdef IKSDB if (ikdbopen) slotstate(what, (server ? "SERVER" : ""), "SEND", filnam ); #endif /* IKSDB */ BEGIN ssfile; /* and switch to receive-file state */ } else { /* otherwise send error msg & quit */ s = xflg ? "Can't execute command" : (char *)epktmsg; if (!*s) s = "Can't open file"; errpkt((CHAR *)s); RESUME; } #ifdef CK_RESEND } #endif /* CK_RESEND */ } break; case 63: { /* R packet was retransmitted. */ xsinit(); /* Resend packet 0 */ } break; case 64: { /* Same deal if G packet comes again */ xsinit(); } break; case 65: { /* Same deal if C packet comes again */ xsinit(); } break; case 66: { /* ACK for F or X packet */ srvptr = srvcmd; /* Point to string buffer */ decode(rdatap,putsrv,0); /* Decode data field, if any */ putsrv(NUL); /* Terminate with null */ ffc = 0L; /* Reset file byte counter */ debug(F101,"Y cxseen","",cxseen); if (*srvcmd) { /* If remote name was recorded */ if (sendmode != SM_RESEND) { if (fdispla == XYFD_C || fdispla == XYFD_S) xxscreen(SCR_AN,0,0L,(char *)srvcmd); tlog(F110," remote name:",(char *) srvcmd,0L); makestr(&psrfspec,(char *)srvcmd); } } if (cxseen||czseen) { /* Interrupted? */ debug(F101,"Y canceling","",0); x = clsif(); /* Close input file */ sxeof(1); /* Send EOF(D) */ BEGIN sseof; /* and switch to EOF state. */ } else if (atcapu) { /* If attributes are to be used */ if (sattr(xflg | stdinf, 1) < 0) { /* send them */ errpkt((CHAR *)"Can't send attributes"); /* if problem, say so */ RESUME; /* and quit */ } else BEGIN ssattr; /* if ok, switch to attribute state */ } else { /* Attributes not negotiated */ if (window(wslotn) < 0) { /* Open window */ errpkt((CHAR *)"Can't open window"); RESUME; } else if ((x = sdata()) == -2) { /* Send first data packet data */ window(1); /* Connection lost, reset window */ x = clsif(); /* Close input file */ return(success = 0); /* Return failure */ } else if (x == -9) { /* User interrupted */ errpkt((CHAR *)"User cancelled"); /* Send Error packet */ window(1); /* Set window size back to 1... */ timint = s_timint; /* Restore timeout */ return(success = 0); /* Failed */ } else if (x < 0) { /* EOF (empty file) or interrupted */ window(1); /* put window size back to 1, */ debug(F101,"Y cxseen","",cxseen); x = clsif(); /* If not ok, close input file, */ if (x < 0) /* treating failure as interruption */ cxseen = 1; /* Send EOF packet */ seof(cxseen||czseen); BEGIN sseof; /* and switch to EOF state. */ } else { /* First data sent OK */ BEGIN ssdata; /* All ok, switch to send-data state */ } } } break; case 67: { /* Got ACK to A packet */ ffc = 0L; /* Reset file byte counter */ debug(F101,"Y cxseen","",cxseen); if (cxseen||czseen) { /* Interrupted? */ debug(F101,"Y canceling","",0); x = clsif(); /* Close input file */ sxeof(1); /* Send EOF(D) */ BEGIN sseof; /* and switch to EOF state. */ } else if (rsattr(rdatap) < 0) { /* Was the file refused? */ discard = 1; /* Set the discard flag */ clsif(); /* Close the file */ sxeof(1); /* send EOF with "discard" code */ BEGIN sseof; /* switch to send-EOF state */ } else if ((x = sattr(xflg | stdinf, 0)) < 0) { /* Send more? */ errpkt((CHAR *)"Can't send attributes"); /* Trouble... */ RESUME; } else if (x == 0) { /* No more to send so now the data */ if (window(wslotn) < 0) { /* Allocate negotiated window slots */ errpkt((CHAR *)"Can't open window"); RESUME; } if ((x = sdata()) == -2) { /* File accepted, send first data */ window(1); /* Connection broken */ x = clsif(); /* Close file */ return(success = 0); /* Return failure */ } else if (x == -9) { /* User interrupted */ errpkt((CHAR *)"User cancelled"); /* Send Error packet */ window(1); /* Set window size back to 1... */ timint = s_timint; /* Restore timeout */ return(success = 0); /* Failed */ } else if (x < 0) { /* If data was not sent */ window(1); /* put window size back to 1, */ debug(F101,"Y cxseen","",cxseen); if (clsif() < 0) /* Close input file */ cxseen = 1; /* Send EOF packet */ seof(cxseen||czseen); BEGIN sseof; /* and switch to EOF state. */ } else { BEGIN ssdata; /* All ok, switch to send-data state */ } } } break; case 68: { /* Ctrl-C or connection loss. */ window(1); /* Set window size back to 1... */ cxseen = 1; /* To indicate interruption */ x = clsif(); /* Close file */ return(success = 0); /* Failed */ } break; case 69: { /* Got ACK to Data packet */ canned(rdatap); /* Check if file transfer cancelled */ debug(F111,"Y cxseen",rdatap,cxseen); debug(F111,"Y czseen",rdatap,czseen); if ((x = sdata()) == -2) { /* Try to send next data */ window(1); /* Connection lost, reset window */ x = clsif(); /* Close file */ return(success = 0); /* Failed */ } else if (x == -9) { /* User interrupted */ errpkt((CHAR *)"User cancelled"); /* Send Error packet */ window(1); /* Set window size back to 1... */ timint = s_timint; /* Restore original timeout */ return(success = 0); /* Failed */ } else if (x < 0) { /* EOF - finished sending data */ debug(F101,"Y cxseen","",cxseen); window(1); /* Set window size back to 1... */ if (clsif() < 0) /* Close input file */ cxseen = 1; /* Send EOF packet */ debug(F101,"Y CALLING SEOF()","",cxseen); seof(cxseen||czseen); BEGIN sseof; /* and enter send-eof state */ } /* NOTE: If x == 0 it means we're draining: see sdata()! */ } break; case 70: { /* Got ACK to EOF */ int g, xdiscard; canned(rdatap); /* Check if file transfer cancelled */ debug(F111,"Y cxseen",rdatap,cxseen); debug(F111,"Y czseen",rdatap,czseen); debug(F111,"Y discard",rdatap,discard); xdiscard = discard; discard = 0; success = (cxseen == 0 && czseen == 0); /* Transfer status... */ debug(F101,"Y success","",success); if (success && rejection > 0) /* If rejected, succeed if */ if (rejection != '#' && /* reason was date */ rejection != 1 && rejection != '?') /* or name; */ success = 0; /* fail otherwise. */ cxseen = 0; /* This goes back to zero. */ if (success) { /* Only if transfer succeeded... */ xxscreen(SCR_ST,ST_OK,0L,""); if (!xdiscard) { makestr(&sfspec,psfspec); /* Record filenames for WHERE */ makestr(&srfspec,psrfspec); } if (moving) { /* If MOVE'ing */ x = zdelet(filnam); /* Try to delete the source file */ #ifdef TLOG if (tralog) { if (x > -1) { tlog(F110," deleted",filnam,0); } else { tlog(F110," delete failed:",ck_errstr(),0); } } #endif /* TLOG */ } else if (snd_move) { /* Or move it */ int x; x = zrename(filnam,snd_move); #ifdef TLOG if (tralog) { if (x > -1) { tlog(F110," moved to ",snd_move,0); } else { tlog(F110," move failed:",ck_errstr(),0); } } #endif /* TLOG */ } else if (snd_rename) { /* Or rename it */ char *s = snd_rename; /* Renaming string */ #ifndef NOSPL int y; /* Pass it thru the evaluator */ extern int cmd_quoting; /* for \v(filename) */ if (cmd_quoting) { /* But only if cmd_quoting is on */ y = MAXRP; s = (char *)srvcmd; zzstring(snd_rename,&s,&y); s = (char *)srvcmd; } #endif /* NOSPL */ if (s) if (*s) { int x; x = zrename(filnam,s); #ifdef TLOG if (tralog) { if (x > -1) { tlog(F110," renamed to",s,0); } else { tlog(F110," rename failed:",ck_errstr(),0); } } #endif /* TLOG */ #ifdef COMMENT *s = NUL; #endif /* COMMENT */ } } } if (czseen) { /* Check group interruption flag */ g = 0; /* No more files if interrupted */ } else { /* Otherwise... */ #ifdef COMMENT /* This code makes any open error fatal to a file-group transfer. */ g = gnfile(); debug(F111,"Y gnfile",filnam,g); if (g > 0) { /* Any more files to send? */ if (sfile(xflg)) /* Yes, try to send next file header */ BEGIN ssfile; /* if ok, enter send-file state */ else { /* otherwise */ s = xflg ? "Can't execute command" : (char *)epktmsg; if (!*s) s = "Can't open file"; errpkt((CHAR *)s); /* send error message */ RESUME; /* and quit */ } } else { /* No next file */ tsecs = gtimer(); /* get statistics timers */ #ifdef GFTIMER fptsecs = gftimer(); #endif /* GFTIMER */ seot(); /* send EOT packet */ BEGIN sseot; /* enter send-eot state */ } #else /* COMMENT */ while (1) { /* Keep trying... */ g = gnfile(); /* Get next file */ debug(F111,"Y gnfile",filnam,g); if (g == 0 && gnferror == 0) /* No more, stop trying */ break; if (g > 0) { /* Have one */ if (sfile(xflg)) { /* Try to open and send F packet */ BEGIN ssfile; /* If OK, enter send-file state */ break; /* and break out of loop. */ } } /* Otherwise keep trying to get one we can send... */ } } if (g == 0) { debug(F101,"Y no more files","",czseen); tsecs = gtimer(); /* Get statistics timers */ #ifdef GFTIMER fptsecs = gftimer(); #endif /* GFTIMER */ seot(); /* Send EOT packet */ BEGIN sseot; /* Enter send-eot state */ } #endif /* COMMENT */ } break; case 71: { /* Got ACK to EOT */ debug(F101,"sseot justone","",justone); RESUME; /* All done, just quit */ } break; case 72: { /* Got Error packet, in any state */ char *s = ""; window(1); /* Close window */ timint = s_timint; /* Restore original timeout */ if (*epktmsg) /* Message from Error packet */ s = (char *)epktmsg; if (!*s) { /* If not there then maybe here */ s = (char *)rdatap; ckstrncpy((char *)epktmsg,(char *)rdatap,PKTMSGLEN); } if (!*s) /* Hopefully we'll never see this. */ s = "Unknown error"; success = 0; /* For IF SUCCESS/FAIL. */ debug(F101,"ckcpro.w justone at E pkt","",justone); success = 0; /* Transfer failed */ xferstat = success; /* Remember transfer status */ if (!epktsent) { x = quiet; quiet = 1; /* Close files silently, */ epktrcvd = 1; /* Prevent messages from clsof() */ clsif(); clsof(1); /* discarding any output file. */ ermsg(s); /* Issue the message (calls screen). */ quiet = x; /* Restore quiet state */ } tstats(); /* Get stats */ /* If we are executing commands from a command file or macro, let the command file or macro decide whether to exit, based on SET { TAKE, MACRO } ERROR. */ if ( #ifndef NOICP !xcmdsrc && #endif /* NOICP */ backgrd && !server) fatal("Protocol error"); xitsta |= (what & W_KERMIT); /* Save this for doexit(). */ #ifdef CK_TMPDIR /* If we were cd'd temporarily to another device or directory ... */ if (f_tmpdir) { int x; x = zchdir((char *) savdir); /* ... restore previous directory */ f_tmpdir = 0; /* and remember we did it. */ debug(F111,"ckcpro.w E tmpdir restored",savdir,x); } #endif /* CK_TMPDIR */ #ifdef IKSDB if (ikdbopen) slotstate(what,"ERROR", (char *)epktmsg, ""); #endif /* IKSDB */ RESUME; } break; case 73: { success = 0; QUIT; } break; case 74: { /* Anything not accounted for above */ errpkt((CHAR *)"Unexpected packet type"); /* Give error message */ window(1); xitsta |= (what & W_KERMIT); /* Save this for doexit(). */ RESUME; /* and quit */ } break; } } } char tbl[] = { -1, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 72, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 11, 74, 7, 74, 74, 74, 9, 4, 74, 5, 8, 74, 74, 74, 6, 74, 73, 3, 1, 74, 74, 2, 74, 10, 74, 74, 74, 74, 74, 74, 74, -1, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 15, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 13, 74, 74, 74, 74, 74, 74, 74, 11, 74, 7, 74, 74, 74, 9, 4, 74, 5, 8, 74, 74, 74, 6, 74, 73, 3, 1, 74, 74, 2, 74, 10, 74, 74, 74, 74, 74, 74, 74, -1, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 57, 74, 74, 72, 53, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 54, 74, 74, 74, 74, 74, 74, 74, 74, 11, 74, 7, 74, 74, 74, 9, 4, 74, 5, 8, 74, 74, 74, 6, 74, 73, 3, 1, 74, 74, 2, 74, 10, 74, 74, 74, 74, 74, 74, 74, -1, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 55, 74, 74, 56, 72, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 59, 74, 74, 74, 74, 74, 74, 11, 74, 7, 74, 74, 74, 9, 4, 74, 5, 8, 74, 74, 74, 6, 74, 73, 3, 1, 74, 74, 2, 74, 10, 74, 74, 74, 74, 74, 74, 74, -1, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 58, 72, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 61, 74, 74, 74, 74, 74, 74, 11, 74, 7, 74, 74, 74, 9, 4, 74, 5, 8, 74, 74, 74, 6, 74, 60, 3, 1, 74, 74, 2, 74, 10, 74, 74, 74, 74, 74, 74, 74, -1, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 65, 74, 72, 74, 64, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 63, 74, 74, 74, 74, 74, 74, 62, 74, 74, 74, 74, 74, 74, 74, 11, 74, 7, 74, 74, 74, 9, 4, 74, 5, 8, 74, 74, 74, 6, 74, 73, 3, 1, 74, 74, 2, 74, 10, 74, 74, 74, 74, 74, 74, 74, -1, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 72, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 66, 74, 74, 74, 74, 74, 74, 74, 11, 74, 7, 74, 74, 74, 9, 4, 74, 5, 8, 74, 74, 74, 6, 74, 73, 3, 1, 74, 74, 2, 74, 10, 74, 74, 74, 74, 74, 74, 74, -1, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 72, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 67, 74, 74, 74, 74, 74, 74, 74, 11, 74, 7, 74, 74, 74, 9, 4, 74, 5, 8, 74, 74, 74, 6, 74, 73, 3, 1, 74, 74, 2, 74, 10, 74, 74, 74, 74, 74, 74, 74, -1, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 72, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 69, 74, 74, 74, 74, 74, 74, 74, 11, 74, 7, 74, 74, 74, 9, 4, 74, 5, 8, 74, 74, 74, 6, 74, 68, 3, 1, 74, 74, 2, 74, 10, 74, 74, 74, 74, 74, 74, 74, -1, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 72, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 70, 74, 74, 74, 74, 74, 74, 74, 11, 74, 7, 74, 74, 74, 9, 4, 74, 5, 8, 74, 74, 74, 6, 74, 73, 3, 1, 74, 74, 2, 74, 10, 74, 74, 74, 74, 74, 74, 74, -1, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 72, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 71, 74, 74, 74, 74, 74, 74, 74, 11, 74, 7, 74, 74, 74, 9, 4, 74, 5, 8, 74, 74, 74, 6, 74, 73, 3, 1, 74, 74, 2, 74, 10, 74, 74, 74, 74, 74, 74, 74, -1, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 26, 29, 29, 29, 25, 19, 17, 22, 29, 29, 29, 28, 23, 29, 29, 18, 12, 29, 29, 20, 21, 29, 29, 29, 29, 29, 29, 29, 29, 29, 11, 29, 7, 29, 29, 29, 9, 4, 29, 5, 8, 29, 29, 29, 6, 29, 27, 3, 1, 29, 29, 2, 29, 10, 29, 29, 29, 29, 29, 29, 29, -1, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 32, 50, 31, 33, 34, 35, 50, 38, 30, 50, 40, 37, 48, 50, 50, 50, 50, 39, 41, 42, 45, 47, 46, 36, 50, 50, 50, 50, 50, 50, 50, 50, 11, 50, 7, 44, 50, 50, 9, 4, 50, 5, 8, 50, 43, 50, 6, 50, 49, 3, 1, 50, 50, 2, 50, 10, 50, 50, 50, 50, 50, 50, 50, -1, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 72, 74, 74, 74, 17, 74, 74, 74, 74, 74, 74, 74, 74, 74, 12, 74, 74, 74, 74, 74, 16, 74, 74, 74, 74, 74, 74, 74, 11, 74, 7, 74, 74, 74, 9, 4, 74, 5, 8, 74, 74, 74, 6, 74, 73, 3, 1, 74, 74, 2, 74, 10, 74, 74, 74, 74, 74, 74, 74, -1, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 72, 53, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 12, 74, 74, 74, 74, 54, 52, 74, 74, 74, 74, 74, 74, 74, 11, 74, 7, 74, 74, 74, 9, 4, 74, 5, 8, 74, 74, 74, 6, 74, 51, 3, 1, 74, 74, 2, 74, 10, 74, 74, 74, 74, 74, 74, 74, -1, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 72, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 14, 74, 74, 74, 74, 74, 74, 74, 11, 74, 7, 74, 74, 74, 9, 4, 74, 5, 8, 74, 74, 74, 6, 74, 73, 3, 1, 74, 74, 2, 74, 10, 74, 74, 74, 74, 74, 74, 74, 0, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 72, 74, 74, 74, 74, 74, 74, 74, 74, 74, 24, 74, 74, 74, 12, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 11, 74, 7, 74, 74, 74, 9, 4, 74, 5, 8, 74, 74, 74, 6, 74, 73, 3, 1, 74, 74, 2, 74, 10, 74, 74, 74, 74, 74, 74,74 }; /* From here down to proto() are routines that were moved out of the state table switcher because the resulting switch() had become too large. To move the contents of a state-table case to a routine: 1. Add a prototype to the list above the state table switcher. 2. Make a routine with an appropriate name, returning int. 3. Move the code into it. 4. Put a call to the new routine in the former spot: rc = name_of_routine(); if (rc > -1) return(rc); 5. Add "return(-1);" after every RESUME, SERVE, or BEGIN macro and at the end if the code is open-ended. */ static int rcv_firstdata() { extern int dispos; debug(F101,"rcv_firstdata","",dispos); if (discard) { /* if we're discarding the file */ ack1((CHAR *)"X"); /* just ack the data like this. */ cancel++; /* and count it */ BEGIN rdpkt; /* and wait for more data packets. */ return(-1); } else { /* Not discarding. */ rf_err = "Can't open file"; if (xflg) { /* If screen data */ if (remfile) { /* redirected to file */ if (rempipe) /* or pipe */ x = openc(ZOFILE,remdest); /* Pipe: start command */ else x = opena(remdest,&iattr); /* File: open with attributes */ } else { /* otherwise */ x = opent(&iattr); /* "open" the screen */ } } else { /* otherwise */ #ifdef CALIBRATE if (calibrate) { /* If calibration run */ x = ckopenx(&iattr); /* open nothing */ #ifdef STREAMING if (streaming) /* Streaming */ fastack(); /* ACK without ACKing. */ else #endif /* STREAMING */ ack(); /* Send real ACK */ BEGIN rdpkt; /* Proceed to next state */ return(-1); } else #endif /* CALIBRATE */ #ifdef UNIX /* In UNIX we can pipe the file data into the mail program, which is to be preferred to writing it out to a temp file and then mailing it afterwards. This depends rather heavily on all UNIXes having a mail command that accepts '-s "subject"' on the command line. MAILCMD (e.g. mail, Mail, mailx) is defined in ckufio.c. */ if (dispos == 'M') { /* Mail... */ char *s; char * tmp = NULL; int n = 0; extern char *MAILCMD; s = iattr.disp.val + 1; n = (int)strlen(MAILCMD) + /* Mail command */ (int)strlen(s) + /* address */ (int)strlen(ofilnam) + 32; /* subject */ if (tmp = (char *)malloc(n)) { ckmakxmsg(tmp,n, MAILCMD," -s \"",ofilnam,"\" ",s, NULL,NULL,NULL,NULL,NULL,NULL,NULL); debug(F111,"rcv_firsdata mail",tmp,(int)strlen(tmp)); x = openc(ZOFILE,(char *)tmp); free(tmp); } else x = 0; } else if (dispos == 'P') { /* Ditto for print */ char * tmp = NULL; int n; extern char *PRINTCMD; n = (int)strlen(PRINTCMD) + (int)strlen(iattr.disp.val+1) + 4; if (tmp = (char *)malloc(n)) { sprintf(tmp, /* safe (prechecked) */ "%s %s", PRINTCMD, iattr.disp.val + 1); x = openc(ZOFILE,(char *)tmp); free(tmp); } else x = 0; } else #endif /* UNIX */ x = opena(filnam,&iattr); /* open the file, with attributes */ } if (x) { /* If file was opened ok */ int rc, qf; #ifndef NOSPL qf = query; #else qf = 0; #endif /* NOSPL */ #ifdef CKTUNING rc = (binary && !parity) ? bdecode(rdatap,putfil): decode(rdatap, qf ? puttrm : putfil, 1); #else rc = decode(rdatap, qf ? puttrm : putfil, 1); #endif /* CKTUNING */ if (rc < 0) { errpkt((CHAR *)"Error writing data"); RESUME; return(-1); } #ifdef STREAMING if (streaming) /* Streaming was negotiated */ fastack(); /* ACK without ACKing. */ else #endif /* STREAMING */ ack(); /* acknowledge it */ BEGIN rdpkt; /* and switch to receive-data state */ return(-1); } else { /* otherwise */ errpkt((CHAR *) rf_err); /* send error packet */ RESUME; /* and quit. */ return(-1); } } } static int rcv_shortreply() { #ifdef PKTZEROHACK success = 0; debug(F111,"rcv_shortreply",rdatap,ipktlen); if (ipktack[0] && !strncmp(ipktack,(char *)rdatap,ipktlen)) { /* No it's the ACK to the I packet again */ x = scmd(vcmd,(CHAR *)cmarg); /* So send the REMOTE command again */ /* Maybe this should be resend() */ debug(F110,"IPKTZEROHACK",ipktack,x); if (x < 0) { errpkt((CHAR *)srimsg); RESUME; return(-1); } } else { ipktack[0] = NUL; #endif /* PKTZEROHACK */ urserver = 1; #ifndef NOSERVER #ifndef NOSPL if (query) { /* If to query, */ qbufp = querybuf; /* initialize query response buffer */ qbufn = 0; querybuf[0] = NUL; } #endif /* NOSPL */ x = 1; if (remfile) { /* Response redirected to file */ rf_err = "Can't open file"; if (rempipe) /* or pipe */ x = #ifndef NOPUSH zxcmd(ZOFILE,remdest) /* Pipe: Start command */ #else 0 #endif /* NOPUSH */ ; else x = opena(remdest,&iattr); /* File: Open with attributes */ debug(F111,"rcv_shortreply remfile",remdest,x); } else { x = opent(&iattr); /* "open" the screen */ } if (x) { /* If file was opened ok */ if (decode(rdatap, #ifndef NOSPL (query || !remfile) ? puttrm : #else !remfile ? puttrm : #endif /* NOSPL */ zputfil, 1) < 0) { /* Note: zputfil, not putfil. */ errpkt((CHAR *)"Error writing data"); RESUME; return(-1); } else { if (rdatap) /* If we had data */ if (*rdatap) /* add a line terminator */ if (remfile) { /* to file */ zsoutl(ZOFILE,""); } else { /* or to screen. */ #ifndef NOICP if (!query || !xcmdsrc) #endif /* NOICP */ if (!(quiet && rcdactive)) conoll(""); } if (bye_active && network) { /* I sent BYE or REMOTE LOGOUT */ msleep(500); /* command and got the ACK... */ bye_active = 0; ttclos(0); } clsof(0); if (!epktsent && !epktrcvd) /* If no error packet... */ success = 1; /* success. */ RESUME; return(-1); } } else { /* File not opened OK */ errpkt((CHAR *) rf_err); /* send error message */ RESUME; /* and quit. */ return(-1); } #endif /* NOSERVER */ #ifdef PKTZEROHACK } #endif /* PKTZEROHACK */ debug(F101,"rcv_shortreply fallthru","",success); return(-1); } static int srv_query() { #ifndef NOSERVER #ifndef NOSPL char c; #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "REMOTE QUERY", (char *)srvcmd); #endif /* CKSYSLOG */ #ifdef IKSDB if (ikdbopen) slotstate(what,"REMOTE QUERY", (char *)(srvcmd+2), ""); #endif /* IKSDB */ c = *(srvcmd+2); /* Q = Query, S = Set */ if (c == 'Q') { /* Query */ if (!ENABLED(en_que)) { /* Security */ errpkt((CHAR *)"REMOTE QUERY disabled"); RESUME; return(-1); } else { /* Query allowed */ int n; char *p, *q; qbufp = querybuf; /* Wipe out old stuff */ qbufn = 0; querybuf[0] = NUL; p = (char *) srvcmd + 3; /* Pointer for making wrapper */ n = strlen((char *)srvcmd); /* Position of end */ c = *(srvcmd+4); /* Which type of variable */ if (*(srvcmd+6) == CMDQ) { /* Starts with command quote? */ p = (char *) srvcmd + 6; /* Take it literally */ if (*p == CMDQ) p++; } else { /* They played by the rules */ if (c == 'K') { /* Kermit variable */ int k; k = (int) strlen(p); if (k > 0 && p[k-1] == ')') { p = (char *)(srvcmd + 4); *(srvcmd+4) = CMDQ; *(srvcmd+5) = 'f'; /* Function, so make it \f...() */ } else { *(srvcmd+3) = CMDQ; /* Stuff wrapping into buffer */ *(srvcmd+4) = 'v'; /* Variable, so make it \v(...) */ *(srvcmd+5) = '('; /* around variable name */ *(srvcmd+n) = ')'; *(srvcmd+n+1) = NUL; } } else { *(srvcmd+3) = CMDQ; /* Stuff wrapping into buffer */ *(srvcmd+4) = 'v'; /* Variable, so make it \v(...) */ *(srvcmd+5) = '('; /* around variable name */ *(srvcmd+n) = ')'; *(srvcmd+n+1) = NUL; if (c == 'S') { /* System variable */ *(srvcmd+4) = '$'; /* so it's \$(...) */ } else if (c == 'G') { /* Non-\ Global variable */ *(srvcmd+4) = 'm'; /* so wrap it in \m(...) */ } } } /* Now evaluate it */ n = QBUFL; /* Max length */ q = querybuf; /* Where to put it */ if (zzstring(p,&q,&n) < 0) { errpkt((n > 0) ? (CHAR *)"Can't get value" : (CHAR *)"Value too long" ); RESUME; return(-1); } else { if (encstr((CHAR *)querybuf) > -1) { /* Encode it */ ack1(data); /* If it fits, send it back in ACK */ success = 1; RESUME; return(-1); } else if (sndstring(querybuf)) { /* Long form response */ BEGIN ssinit; return(-1); } else { /* sndhlp() fails */ errpkt((CHAR *)"Can't send value"); RESUME; return(-1); } } } } else if (c == 'S') { /* Set (assign) */ if (!ENABLED(en_asg)) { /* Security */ errpkt((CHAR *)"REMOTE ASSIGN disabled"); RESUME; return(-1); } else { /* OK */ int n; n = xunchar(*(srvcmd+3)); /* Length of name */ n = 3 + n + 1; /* Position of length of value */ *(srvcmd+n) = NUL; /* Don't need it */ if (addmac((char *)(srvcmd+4),(char *)(srvcmd+n+1)) < 0) errpkt((CHAR *)"REMOTE ASSIGN failed"); else { ack(); success = 1; } RESUME; return(-1); } } else { errpkt((CHAR *)"Badly formed server command"); RESUME; return(-1); } #else errpkt((CHAR *)"Variable query/set not available"); RESUME; return(-1); #endif /* NOSPL */ #endif /* NOSERVER */ } static int srv_copy() { #ifndef NOSERVER #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "REMOTE COPY", (char *)srvcmd); #endif /* CKSYSLOG */ #ifdef ZCOPY if (!ENABLED(en_cpy)) { errpkt((CHAR *)"REMOTE COPY disabled"); RESUME; return(-1); } else { char *str1, *str2, f1[256], f2[256]; int len1, len2; len1 = xunchar(srvcmd[1]); /* Separate the parameters */ len2 = xunchar(srvcmd[2+len1]); strncpy(f1,(char *)(srvcmd+2),len1); f1[len1] = NUL; strncpy(f2,(char *)(srvcmd+3+len1),len2); f2[len2] = NUL; #ifdef IKSDB if (ikdbopen) slotstate(what,"REMOTE COPY", f1, f2); #endif /* IKSDB */ if (!ENABLED(en_cwd)) { /* If CWD is disabled */ zstrip(f1,&str1); /* and they included a pathname, */ zstrip(f2,&str2); if (strcmp(f1,str1) || strcmp(f2,str2)) { /* Refuse. */ errpkt((CHAR *)"Access denied"); RESUME; /* Remember, this is not a goto! */ return(-1); } } if (state == generic) { /* It's OK to go ahead. */ if (zcopy(f1,f2)) { /* Try */ errpkt((CHAR *)"Can't copy file"); /* give error message */ } else { success = 1; ack(); } RESUME; /* wait for next server command */ return(-1); } } return(-1); #else /* no ZCOPY */ errpkt((CHAR *)"REMOTE COPY not available"); /* give error message */ RESUME; /* wait for next server command */ return(-1); #endif /* ZCOPY */ #endif /* NOSERVER */ } static int srv_rename() { #ifndef NOSERVER #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "REMOTE RENAME", (char *)srvcmd); #endif /* CKSYSLOG */ #ifdef ZRENAME if (!ENABLED(en_ren)) { errpkt((CHAR *)"REMOTE RENAME disabled"); RESUME; return(-1); } else { /* RENAME is enabled */ char *str1, *str2, f1[256], f2[256]; int len1, len2; len1 = xunchar(srvcmd[1]); /* Separate the parameters */ len2 = xunchar(srvcmd[2+len1]); strncpy(f1,(char *)(srvcmd+2),len1); f1[len1] = NUL; strncpy(f2,(char *)(srvcmd+3+len1),len2); f2[len2] = NUL; len2 = xunchar(srvcmd[2+len1]); strncpy(f1,(char *)(srvcmd+2),len1); f1[len1] = NUL; strncpy(f2,(char *)(srvcmd+3+len1),len2); f2[len2] = NUL; #ifdef IKSDB if (ikdbopen) slotstate(what,"REMOTE RENAME", f1, f2); #endif /* IKSDB */ if (!ENABLED(en_cwd)) { /* If CWD is disabled */ zstrip(f1,&str1); /* and they included a pathname, */ zstrip(f2,&str2); if ( strcmp(f1,str1) || strcmp(f2,str2) ) { /* refuse. */ errpkt((CHAR *)"Access denied"); RESUME; /* Remember, this is not a goto! */ return(-1); } } if (state == generic) { /* It's OK to go ahead. */ if (zrename(f1,f2)) { /* Try */ errpkt((CHAR *)"Can't rename file"); /* Give error msg */ } else { success = 1; ack(); } RESUME; /* Wait for next server command */ return(-1); } } return(-1); #else /* no ZRENAME */ /* Give error message */ errpkt((CHAR *)"REMOTE RENAME not available"); RESUME; /* Wait for next server command */ return(-1); #endif /* ZRENAME */ #endif /* NOSERVER */ } static int srv_login() { #ifndef NOSERVER char f1[LOGINLEN+1], f2[LOGINLEN+1], f3[LOGINLEN+1]; CHAR *p; int len, i; debug(F101,"REMOTE LOGIN x_login","",x_login); debug(F101,"REMOTE LOGIN x_logged","",x_logged); f1[0] = NUL; f2[0] = NUL; f3[0] = NUL; len = 0; if (srvcmd[1]) /* First length field */ len = xunchar(srvcmd[1]); /* Separate the parameters */ if (x_login) { /* Login required */ if (x_logged) { /* And already logged in */ if (len > 0) { /* Logging in again */ errpkt((CHAR *)"Already logged in."); } else { /* Logging out */ debug(F101,"REMOTE LOGOUT","",x_logged); #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "REMOTE LOGOUT", NULL); #endif /* CKSYSLOG */ #ifdef IKSDB if (ikdbopen) slotstate(what,"REMOTE LOGOUT", "", ""); #endif /* IKSDB */ tlog(F110,"Logged out",x_user,0); ack1((CHAR *)"Logged out"); success = 1; msleep(500); #ifdef CK_LOGIN x_logged = 0; #ifdef IKSD if (inserver) ckxlogout(); #endif /* IKSD */ #endif /* CK_LOGIN */ } } else { /* Not logged in yet */ debug(F101,"REMOTE LOGIN len","",len); if (len > 0) { /* Have username */ #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "REMOTE LOGIN", NULL); #endif /* CKSYSLOG */ if (len > LOGINLEN) { errpkt((CHAR *)"Username too long"); } p = srvcmd + 2; /* Point to it */ for (i = 0; i < len; i++) /* Copy it */ f1[i] = p[i]; f1[len] = NUL; /* Terminate it */ p += len; /* Point to next length field */ if (*p) { /* If we have one */ len = xunchar(*p++); /* decode it */ if (len > 0 && len <= LOGINLEN) { for (i = 0; i < len; i++) /* Same deal for password */ f2[i] = p[i]; f2[len] = NUL; p += len; /* And account */ if (*p) { len = xunchar(*p++); if (len > 0 && len <= LOGINLEN) { for (i = 0; i < len; i++) f3[i] = p[i]; /* Set but never used */ f3[len] = NUL; /* (because account not used) */ } } } } debug(F101,"REMOTE LOGIN 1","",x_logged); #ifdef IKSD #ifdef CK_LOGIN if (inserver) { /* Log in to system for real */ x_logged = ckxlogin((CHAR *)f1,(CHAR *)f2,NULL,0); debug(F101,"REMOTE LOGIN 2","",x_logged); if (x_logged) { /* Count attempts */ logtries = 0; justone = 1; } else { logtries++; sleep(logtries); } } else #endif /* CK_LOGIN */ #endif /* IKSD */ if (x_user && x_passwd) { /* User and password must match */ if (!strcmp(x_user,f1)) /* SET SERVER LOGIN */ if (!strcmp(x_passwd,f2)) x_logged = 1; debug(F101,"REMOTE LOGIN 3","",x_logged); } else if (x_user) { /* Only username given, no password */ if (!strcmp(x_user,f1)) /* so only username must match */ x_logged = 1; debug(F101,"REMOTE LOGIN 4","",x_logged); } #ifdef CK_LOGIN else { x_logged = ckxlogin((CHAR *)f1,(CHAR *)f2,NULL,0); debug(F101,"REMOTE LOGIN 5","",x_logged); } #endif /* CK_LOGIN */ if (x_logged) { /* Logged in? */ tlog(F110,"Logged in", x_user, 0); if (isguest) ack1((CHAR *)"Logged in as guest - restrictions apply"); else ack1((CHAR *)"Logged in"); success = 1; } else { tlog(F110,"Login failed", f1, 0); errpkt((CHAR *)"Access denied."); #ifdef IKSD #ifdef CK_LOGIN if (inserver && logtries > 2) ckxlogout(); #endif /* CK_LOGIN */ #endif /* IKSD */ } } else { /* LOGOUT */ errpkt((CHAR *)"Logout ignored"); } } } else { /* Login not required */ if (len > 0) errpkt((CHAR *)"Login ignored."); else errpkt((CHAR *)"Logout ignored."); } #endif /* NOSERVER */ RESUME; return(-1); } static int srv_timeout() { /* K95 does this its own way */ if (idletmo) { #ifdef IKSD if (inserver) { printf("\r\nIKSD IDLE TIMEOUT: %d sec\r\n", srvidl); doexit(GOOD_EXIT,xitsta); } #endif /* IKSD */ idletmo = 0; printf("\r\nSERVER IDLE TIMEOUT: %d sec\r\n", srvidl); xitsta |= (what & W_KERMIT); QUIT; } #ifndef NOSERVER else if (fatalio) { /* Connection lost */ #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "Connection lost", NULL); #endif /* CKSYSLOG */ #ifdef IKSDB if (ikdbopen) slotstate(what,"SERVER DISCONNECT",(char *)srvcmd, ""); #endif /* IKSDB */ xitsta |= what; QUIT; } else if (interrupted) { /* Interrupted by hand */ if (!ENABLED(en_fin)) { errpkt((CHAR *)"QUIT disabled"); RESUME; return(-1); } else { if (what == W_SEND || what == W_RECV || what == W_REMO) { success = 0; #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "Interrupted", NULL); #endif /* CKSYSLOG */ } else if (what == W_NOTHING && filcnt == 0) { success = 1; } /* Otherwise leave success alone */ xitsta |= (what & W_KERMIT); QUIT; } } else { /* Shouldn't happen */ debug(F100,"SERVER (top) GOT UNEXPECTED 'q'","",0); QUIT; } #endif /* NOSERVER */ } static int rcv_s_pkt() { #ifndef NOSERVER if (state == rgen) urserver = 1; if (/* state == serve && */ x_login && !x_logged) { errpkt((CHAR *)"Login required"); SERVE; } else #endif /* NOSERVER */ if (state == serve && !ENABLED(en_sen)) { /* Not in server mode */ errpkt((CHAR *)"SEND disabled"); /* when SEND is disabled. */ RESUME; return(-1); } else { /* OK to go ahead. */ #ifdef CK_TMPDIR if (dldir && !f_tmpdir) { /* If they have a download directory */ debug(F110,"receive download dir",dldir,0); if (s = zgtdir()) { /* Get current directory */ debug(F110,"receive current dir",s,0); if (zchdir(dldir)) { /* Change to download directory */ debug(F100,"receive zchdir ok","",0); ckstrncpy(savdir,s,TMPDIRLEN); f_tmpdir = 1; /* Remember that we did this */ } else debug(F100,"receive zchdir failed","",0); } } #endif /* CK_TMPDIR */ nakstate = 1; /* Can send NAKs from here. */ rinit(rdatap); /* Set parameters */ bctu = bctr; /* Switch to agreed-upon block check */ bctl = (bctu == 4) ? 2 : bctu; /* Set block-check length */ what = W_RECV; /* Remember we're receiving */ lastxfer = W_RECV; resetc(); /* Reset counters */ rtimer(); /* Reset timer */ #ifdef GFTIMER rftimer(); #endif /* GFTIMER */ streamon(); BEGIN rfile; /* Go into receive-file state */ } return(-1); } /* END OF ROUTINES MOVED OUT OF STATE MACHINE */ /* P R O T O -- Protocol entry function */ static int is_tn = 0; /* It's a Telnet connection */ #ifdef CK_SPEED int f_ctlp = 0; /* Control-character prefix table */ #ifdef COMMENT short s_ctlp[256]; #endif /* COMMENT */ #endif /* CK_SPEED */ /* This is simply a wrapper for the real protocol function just below, that saves any items that might be changed automatically by protocol negotiations and then restores them upon exit from protocol mode. */ VOID proto() { extern int b_save, f_save, c_save, ss_save, slostart, reliable, urclear; #ifndef NOCSETS extern int fcharset, fcs_save, tcharset, tcs_save; #endif /* NOCSETS */ #ifdef PIPESEND extern int pipesend; #endif /* PIPESEND */ #ifndef NOLOCAL #ifdef OS2 extern int cursorena[], cursor_save, term_io; extern BYTE vmode; extern int display_demo; int term_io_save; #endif /* OS2 */ #endif /* NOLOCAL */ #ifdef TNCODE int _u_bin=0, _me_bin = 0; #ifdef IKS_OPTION int /* _u_start=0, */ _me_start = 0; #endif /* IKS_OPTION */ #endif /* TNCODE */ #ifdef PATTERNS int pa_save; int i; #endif /* PATTERNS */ int scan_save; #ifdef PATTERNS pa_save = patterns; #endif /* PATTERNS */ scan_save = filepeek; myjob = sstate; #ifdef CK_LOGIN if (isguest) { /* If user is anonymous */ en_pri = 0; /* disable printing */ en_mai = 0; /* and disable email */ en_del = 0; /* and file deletion */ } #endif /* CK_LOGIN */ #ifndef NOLOCAL #ifdef OS2 cursor_save = cursorena[vmode]; cursorena[vmode] = 0; term_io_save = term_io; term_io = 0; #endif /* OS2 */ #endif /* NOLOCAL */ b_save = binary; /* SET FILE TYPE */ f_save = fncnv; /* SET FILE NAMES */ c_save = bctr; p_save = fnspath; r_save = recursive; s_timint = timint; ss_save = slostart; #ifndef NOCSETS fcs_save = fcharset; tcs_save = tcharset; #endif /* NOCSETS */ #ifdef COMMENT /* Don't do this because then user can never find out what happened. */ #ifdef CK_SPEED for (i = 0; i < 256; i++) s_ctlp[i] = ctlp[i]; f_ctlp = 1; #endif /* CK_SPEED */ #endif /* COMMENT */ if (reliable == SET_ON) slostart = 0; is_tn = (!local && sstelnet) #ifdef TNCODE || (local && network && ttnproto == NP_TELNET) #endif /* TNCODE */ ; #ifdef TNCODE if (is_tn) { if (tn_b_xfer && !(sstelnet || inserver)) { /* Save the current state of Telnet Binary */ _u_bin = TELOPT_U(TELOPT_BINARY); _me_bin = TELOPT_ME(TELOPT_BINARY); /* If either direction is not Binary attempt to negotiate it */ if (!_u_bin && TELOPT_U_MODE(TELOPT_BINARY) != TN_NG_RF) { tn_sopt(DO,TELOPT_BINARY); TELOPT_UNANSWERED_DO(TELOPT_BINARY) = 1; } if (!_me_bin && TELOPT_ME_MODE(TELOPT_BINARY) != TN_NG_RF) { tn_sopt(WILL,TELOPT_BINARY); TELOPT_UNANSWERED_WILL(TELOPT_BINARY) = 1; } if (!(_me_bin && _u_bin)) tn_wait("proto set binary mode"); } #ifdef IKS_OPTION #ifdef CK_XYZ if (protocol != PROTO_K) { /* Non-Kermit protocol selected */ if (TELOPT_U(TELOPT_KERMIT) && TELOPT_SB(TELOPT_KERMIT).kermit.u_start) { iks_wait(KERMIT_REQ_STOP,0); /* Stop the other Server */ /* _u_start = 1; */ } if (TELOPT_ME(TELOPT_KERMIT) && TELOPT_SB(TELOPT_KERMIT).kermit.me_start) { tn_siks(KERMIT_STOP); /* I'm not servering */ TELOPT_SB(TELOPT_KERMIT).kermit.me_start = 0; _me_start = 1; } } else #endif /* CK_XYZ */ if (sstate == 'x' || sstate == 'v') { /* Responding to a request */ if (!inserver && TELOPT_U(TELOPT_KERMIT) && TELOPT_SB(TELOPT_KERMIT).kermit.u_start) { iks_wait(KERMIT_REQ_STOP,0); /* Stop the other Server */ /* _u_start = 1; */ } if (TELOPT_ME(TELOPT_KERMIT) && !TELOPT_SB(TELOPT_KERMIT).kermit.me_start) { tn_siks(KERMIT_START); /* Send Kermit-Server Start */ TELOPT_SB(TELOPT_KERMIT).kermit.me_start = 1; } } else { /* Initiating a request */ if (TELOPT_ME(TELOPT_KERMIT) && TELOPT_SB(TELOPT_KERMIT).kermit.me_start) { tn_siks(KERMIT_STOP); /* I'm not servering */ TELOPT_SB(TELOPT_KERMIT).kermit.me_start = 0; _me_start = 1; } if (TELOPT_U(TELOPT_KERMIT) && !TELOPT_SB(TELOPT_KERMIT).kermit.u_start) { /* Send Req-Server-Start */ if (!iks_wait(KERMIT_REQ_START,0)) { if (sstate != 's') { success = 0; /* Other Kermit refused to serve */ if (local) printf("A Kermit Server is not available\r\n"); debug(F110,"proto()", "A Kermit Server is not available",0); tlog(F110,"IKS client/server failure", "A Kermit Server is not available",0); goto xxprotox; } } } } #endif /* IKS_OPTION */ #ifdef CK_ENCRYPTION if (tn_no_encrypt_xfer && !(sstelnet || inserver)) { ck_tn_enc_stop(); } #endif /* CK_ENCRYPTION */ } #endif /* TNCODE */ if (!xfrint) connoi(); xxproto(); /* Call the real protocol function */ #ifdef IKS_OPTION xxprotox: #endif /* IKS_OPTION */ xferstat = success; /* Remember transfer status */ kactive = 0; #ifdef TNCODE #ifdef CK_ENCRYPTION if (tn_no_encrypt_xfer && !(sstelnet || inserver)) { ck_tn_enc_start(); } #endif /* CK_ENCRYPTION */ #ifdef IKS_OPTION if (TELOPT_ME(TELOPT_KERMIT) && TELOPT_SB(TELOPT_KERMIT).kermit.me_start && !_me_start) { tn_siks(KERMIT_STOP); /* Server is stopped */ TELOPT_SB(TELOPT_KERMIT).kermit.me_start = 0; } #endif /* IKS_OPTION */ if (is_tn && tn_b_xfer && !(sstelnet || inserver)) { /* if we negotiated Binary mode try to reset it */ if (!_u_bin) { /* Check to see if the state changed during the transfer */ if (TELOPT_U(TELOPT_BINARY)) { tn_sopt(DONT,TELOPT_BINARY); TELOPT_UNANSWERED_DONT(TELOPT_BINARY) = 1; } else _u_bin = 1; /* So we don't call tn_wait() */ } if (!_me_bin) { /* Check to see if the state changed during the transfer */ if (TELOPT_ME(TELOPT_BINARY)) { tn_sopt(WONT,TELOPT_BINARY); TELOPT_UNANSWERED_WONT(TELOPT_BINARY) = 1; } else _me_bin = 1; /* So we don't call tn_wait() */ } if (!(_me_bin && _u_bin)) tn_wait("proto reset binary mode"); } #endif /* TNCODE */ #ifdef PATTERNS patterns = pa_save; #endif /* PATTERNS */ filepeek = scan_save; #ifdef STREAMING streaming = 0; /* streamok = 0; */ #endif /* STREAMING */ #ifdef COMMENT #ifdef CK_SPEED for (i = 0; i < 256; i++) ctlp[i] = s_ctlp[i]; f_ctlp = 0; #endif /* CK_SPEED */ #endif /* COMMENT */ urclear = 0; if (!success) { xitsta |= (what & W_KERMIT); tlog(F110," failed:",(char *)epktmsg,0); } debug(F111,"proto xferstat",epktmsg,xferstat); slostart = ss_save; if (s_timint > -1) { /* Because of REMOTE SET */ timint = s_timint; s_timint = -1; } recursive = r_save; fnspath = p_save; if (c_save > -1) { /* Because of REMOTE SET */ bctr = c_save; c_save = -1; } fncnv = f_save; binary = b_save; #ifdef PIPESEND pipesend = 0; /* Next time might not be pipesend */ #endif /* PIPESEND */ #ifndef NOLOCAL #ifdef OS2 cursorena[vmode] = cursor_save; term_io = term_io_save; display_demo = 1; #endif /* OS2 */ #endif /* NOLOCAL */ } static VOID xxproto() { int x; long lx; #ifdef CK_XYZ #ifdef XYZ_INTERNAL _PROTOTYP( int pxyz, (int) ); #endif /* XYZ_INTERNAL */ #endif /* CK_XYZ */ char xss[2]; /* String representation of sstate */ xss[0] = sstate; xss[1] = NUL; s_timint = timint; debug(F101,"xxproto entry justone","",justone); success = 0; retrieve = 0; /* Reset these ... */ reget = 0; opkt = 0; if (local && ttchk() < 0) { /* Giving BYE or FIN */ if (bye_active) { /* but there is no connection */ ttclos(0); success = 1; return; } /* Ditto for any REMOTE command */ if (sstate == 'g' && cmarg ) { if (*cmarg == 'L' || *cmarg == 'F' || *cmarg == 'X') success = 1; else printf("?No connection\r\n"); return; } } /* Set up the communication line for file transfer. */ /* NOTE: All of the xxscreen() calls prior to the wart() invocation */ /* could just as easily be printf's or, for that matter, hints. */ if (local && (speed < 0L) && (network == 0)) { xxscreen(SCR_EM,0,0L,"Sorry, you must 'set speed' first"); return; } x = -1; if (ttopen(ttname,&x,mdmtyp,cdtimo) < 0) { debug(F111,"failed: proto ttopen local",ttname,local); xxscreen(SCR_EM,0,0L,"Can't open line"); return; } if (x > -1) local = x; debug(F111,"proto ttopen local",ttname,local); lx = (local && !network) ? speed : -1; #ifdef NETCONN #ifdef CK_SPEED if (is_tn) { ctlp[(unsigned)255] = ctlp[CR] = 1; if (parity == 'e' || parity == 'm') ctlp[127] = 1; if (flow == FLO_XONX) { /* Also watch out for Xon/Xoff */ ctlp[17] = ctlp[19] = 1; ctlp[17+128] = ctlp[19+128] = 1; } } #endif /* CK_SPEED */ #endif /* NETCONN */ if (ttpkt(lx,flow,parity) < 0) { /* Put line in packet mode, */ xxscreen(SCR_EM,0,0L,"Can't condition line"); return; } if (local && !network && carrier != CAR_OFF) { int x; /* Serial connection */ x = ttgmdm(); /* with carrier checking */ if (x > -1) { if (!(x & BM_DCD)) { debug(F101,"proto ttgmdm","",0); xxscreen(SCR_EM,0,0L,"Carrier required but not detected"); return; } } } /* Send remote side's "receive" or "server" startup string, if any */ if (local && ckindex((char *)xss,"srgcjhk",0,0,1)) { char *s = NULL; if ( #ifdef IKS_OPTION /* Don't send auto-blah string if we know other side is serving */ !TELOPT_U(TELOPT_KERMIT) || !TELOPT_SB(TELOPT_KERMIT).kermit.u_start #else 1 #endif /* IKS_OPTION */ ) { if (sstate == 's') { /* Sending file(s) */ s = binary ? ptab[protocol].h_b_init : ptab[protocol].h_t_init; } else if (protocol == PROTO_K) { /* Command for server */ s = ptab[protocol].h_x_init; } } #ifdef CK_SPEED #ifndef UNPREFIXZERO if (protocol == PROTO_K) /* Because of C-strings... */ ctlp[0] = 1; #endif /* UNPREFIXZERO */ #endif /* CK_SPEED */ if (s) if (*s) { /* If we have a command to send... */ char tmpbuf[356]; int tmpbufsiz = 356; int stuff = -1, stuff2 = -1, len = 0; extern int tnlm; if (sstate == 's') { /* Sending file(s) */ #ifdef CK_XYZ if (protocol == PROTO_X) { char * s2; s2 = cmarg2[0] ? cmarg2 : cmarg; if ((int)strlen(s) + (int)strlen(s2) + 4 < 356) sprintf(tmpbuf, s, s2); else tmpbuf[0] = NUL; } else { #endif /* CK_XYZ */ ckmakmsg(tmpbuf, 356, s, NULL, NULL, NULL); #ifdef CK_XYZ } #endif /* CK_XYZ */ } else { /* Command for server */ ckstrncpy(tmpbuf,s,356); } ckstrncat(tmpbuf, "\015",sizeof(tmpbuf)); if (tnlm) /* TERMINAL NEWLINE ON */ stuff = LF; /* Stuff LF */ #ifdef TNCODE /* TELNET NEWLINE MODE */ if (is_tn) { switch (TELOPT_ME(TELOPT_BINARY) ? tn_b_nlm : tn_nlm) { case TNL_CR: break; case TNL_CRNUL: break; case TNL_CRLF: stuff2 = stuff; stuff = LF; break; } } #endif /* TNCODE */ #ifdef NETCONN #ifdef TCPSOCKET #ifdef RLOGCODE if (network && ttnproto == NP_RLOGIN) { switch (tn_b_nlm) { /* Always BINARY */ case TNL_CR: break; case TNL_CRNUL: stuff2 = stuff; stuff = NUL; break; case TNL_CRLF: stuff2 = stuff; stuff = LF; break; } } #endif /* RLOGCODE */ #endif /* TCPSOCKET */ #endif /* NETCONN */ len = strlen(tmpbuf); if (stuff >= 0 && len < tmpbufsiz - 1) { tmpbuf[len++] = stuff; if (stuff2 >= 0 && len < tmpbufsiz - 1) tmpbuf[len++] = stuff2; tmpbuf[len] = NUL; } ttol((CHAR *)tmpbuf,len); if (protocol == PROTO_K) /* Give remote Kermit time to start */ msleep(400); } } #ifdef CK_XYZ if (protocol != PROTO_K) { /* Non-Kermit protocol selected */ char tmpbuf[356]; int tmpbufsiz = 356; char * s = ""; #ifdef CK_TMPDIR if (sstate == 'v') { /* If receiving and... */ if (dldir && !f_tmpdir) { /* if they have a download directory */ if (s = zgtdir()) { /* Get current directory */ if (zchdir(dldir)) { /* Change to download directory */ ckstrncpy(savdir,s,TMPDIRLEN); f_tmpdir = 1; /* Remember that we did this */ } } } } #endif /* CK_TMPDIR */ #ifdef XYZ_INTERNAL /* Internal */ success = !pxyz(sstate); #else #ifdef CK_REDIR /* External */ switch (sstate) { case 's': /* 'Tis better to SEND... */ s = binary ? ptab[protocol].p_b_scmd : ptab[protocol].p_t_scmd; break; case 'v': /* ... than RECEIVE */ s = binary ? ptab[protocol].p_b_rcmd : ptab[protocol].p_t_rcmd; break; } if (!s) s = ""; if (*s) { if (sstate == 's') { /* Sending */ extern int xfermode; int k = 0, x = 0, b = binary; /* If just one file we can scan it to set the xfer mode. Otherwise it's up to the external protocol program. */ if (patterns && xfermode == XMODE_A && !iswild(fspec)) { extern int nscanfile; k = scanfile(fspec,&x,nscanfile); if (k > -1) { b = (k == FT_BIN) ? XYFT_B : XYFT_T; s = b ? ptab[protocol].p_b_scmd : ptab[protocol].p_t_scmd; } } if ((int)strlen(s) + (int)strlen(fspec) < tmpbufsiz) { sprintf(tmpbuf,s,fspec); /* safe (prechecked) */ tlog(F110,"Sending",fspec,0L); } } else { /* Receiving */ if ((int)strlen(s) + (int)strlen(cmarg2) < tmpbufsiz) { sprintf(tmpbuf,s,cmarg2); /* safe (prechecked) */ tlog(F110,"Receiving",cmarg2,0L); } } tlog(F110," via external protocol:",tmpbuf,0); debug(F110,"ckcpro ttruncmd",tmpbuf,0); success = ttruncmd(tmpbuf); tlog(F110," status:",success ? "OK" : "FAILED", 0); } else { printf("?Sorry, no external protocol defined for %s\r\n", ptab[protocol].p_name ); } #else printf( "Sorry, only Kermit protocol is supported in this version of Kermit\n" ); #endif /* CK_REDIR */ #endif /* XYZ_INTERNAL */ return; } #endif /* CK_XYZ */ #ifdef NTSIGX conraw(); connoi(); #else if (!local) connoi(); /* No console interrupts if remote */ #endif /* NTSIG */ kactive = 1; if (sstate == 'x') { /* If entering server mode, */ extern int howcalled; server = 1; /* set flag, */ debug(F101,"server backgrd","",backgrd); debug(F101,"server quiet","",quiet); debug(F100,"SHOULD NOT SEE THIS IF IN BACKGROUND!","",0); if (howcalled == I_AM_SSHSUB) { /* and issue appropriate message. */ ttol((CHAR *)"KERMIT READY TO SERVE...\015\012",26); } else if (!local) { if (!quiet && !backgrd #ifdef IKS_OPTION && !TELOPT_ME(TELOPT_KERMIT) /* User was told by negotiation */ #endif /* IKS_OPTION */ ) { conoll(srvtxt); conoll("KERMIT READY TO SERVE..."); } } else { conol("Entering server mode on "); conoll(ttname); conoll("Type Ctrl-C to quit."); if (srvdis) intmsg(-1L); #ifdef TCPSOCKET #ifndef NOLISTEN if (network && tcpsrfd > 0) ttol((CHAR *)"KERMIT READY TO SERVE...\015\012",26); #endif /* NOLISTEN */ #endif /* TCPSOCKET */ } } else server = 0; #ifdef VMS if (!quiet && !backgrd) /* So message doesn't overwrite prompt */ conoll(""); if (local) conres(); /* So Ctrl-C will work */ #endif /* VMS */ /* If in remote mode, not shushed, not in background, and at top command level, issue a helpful message telling what to do... */ if (!local && !quiet && !backgrd) { if (sstate == 'v') { conoll("Return to your local Kermit and give a SEND command."); conoll(""); conoll("KERMIT READY TO RECEIVE..."); } else if (sstate == 's') { conoll("Return to your local Kermit and give a RECEIVE command."); conoll(""); conoll("KERMIT READY TO SEND..."); } else if ( sstate == 'g' || sstate == 'r' || sstate == 'h' || sstate == 'j' || sstate == 'c' ) { conoll("Return to your local Kermit and give a SERVER command."); conoll(""); conoll((sstate == 'r' || sstate == 'j' || sstate == 'h') ? "KERMIT READY TO GET..." : "KERMIT READY TO SEND SERVER COMMAND..."); } } #ifdef COMMENT if (!local) sleep(1); #endif /* COMMENT */ /* The 'wart()' function is generated by the wart program. It gets a character from the input() routine and then based on that character and the current state, selects the appropriate action, according to the state table above, which is transformed by the wart program into a big case statement. The function is active for one transaction. */ rtimer(); /* Reset elapsed-time timer */ #ifdef GFTIMER rftimer(); #endif /* GFTIMER */ resetc(); /* & other per-transaction counters. */ debug(F101,"proto calling wart, justone","",justone); wart(); /* Enter the state table switcher. */ /* Note: the following is necessary in case we have just done a remote-mode file transfer, in which case the controlling terminal modes have been changed by ttpkt(). In particular, special characters like Ctrl-C and Ctrl-\ might have been turned off (see ttpkt). So this call to ttres() is essential. IMPORTANT: restore interrupt handlers first, otherwise any terminal interrupts that occur before this is done in the normal place later will cause a crash. */ #ifdef OS2 ttres(); /* Reset the communication device */ #else if (!local) { setint(); /* Arm interrupt handlers FIRST */ msleep(500); ttres(); /* Then restore terminal. */ } #endif /* OS2 */ xxscreen(SCR_TC,0,0L,""); /* Transaction complete */ x = quiet; quiet=1; clsif(); /* Failsafe in case we missed */ clsof(1); /* a case in the state machine. */ quiet = x; if (server) { /* Back from packet protocol. */ if (!quiet && !backgrd #ifdef IKSD && !inserver #endif /* IKSD */ ) { /* Give appropriate message */ conoll(""); conoll("C-Kermit server done"); } server = 0; /* Not a server any more */ } } /* S G E T I N I T -- Handle incoming GET-Class packets */ /* Returns: -1: On error 0: GET packet processed OK - ready to Send. 1: Extended GET processed OK - wait for another. */ static int sgetinit(reget,xget) int reget, xget; { /* Server end of GET command */ char * fs = NULL; /* Pointer to filespec */ int i, n, done = 0; #ifdef PIPESEND extern int usepipes, pipesend; #endif /* PIPESEND */ extern int nolinks; if (!ENABLED(en_get)) { /* Only if not disabled! */ errpkt((CHAR *)"GET disabled"); return(-1); } /* OK to proceed */ nolinks = recursive; filcnt = 0; #ifdef WHATAMI /* If they are alike this was already done in whoarewe() */ debug(F101,"sgetinit whatru","",whatru); if (whatru & WMI_FLAG) { /* Did we get WHATAMI info? */ debug(F101,"sgetinit binary (1)","",binary); #ifdef VMS if (binary != XYFT_I && binary != XYFT_L) #else #ifdef OS2 if (binary != XYFT_L) #endif /* OS2 */ #endif /* VMS */ binary = (whatru & WMI_FMODE) ? /* Yes, set file type */ XYFT_B : XYFT_T; /* automatically */ debug(F101,"sgetinit binary (2)","",binary); if (!wearealike) fncnv = (whatru & WMI_FNAME) ? 1 : 0; /* And name conversion */ } #endif /* WHATAMI */ fs = (char *)srvcmd; srvptr = srvcmd; /* Point to server command buffer */ decode(rdatap,putsrv,0); /* Decode the GET command into it */ /* Accept multiple filespecs */ cmarg2 = ""; /* Don't use cmarg2 */ cmarg = ""; /* Don't use cmarg */ done = 1; /* Only 1 packet needed... */ if (xget) { /* Special decoding for Extended GET */ char L, next, c; /* PLV items */ int len, val; /* More PLV items */ char * p = (char *)srvcmd; /* String to decode */ done = 0; /* Maybe more packets needed */ fs = NULL; /* We don't know the filespec yet */ c = *p++; /* Get first parameter */ while (c) { /* For all parameters... */ debug(F000,"sgetinit c","",c); L = *p++; /* Get length */ if (L >= SP) /* Decode length */ len = xunchar(L); else if (c == '@') { /* Allow missing EOP length field */ len = 0; } else { len = (xunchar(*p++) * 95); len += xunchar(*p++); } debug(F101,"sgetinit len","",len); next = *(p+len); /* Get next parameter */ *(p+len) = NUL; /* Zero it out to terminal value */ debug(F110,"sgetinit p",p,0); switch (c) { /* Do the parameter */ case 'O': /* GET Options */ val = atoi(p); /* Convert to int */ debug(F101,"sgetinit O val","",val); if (val & GOPT_DEL) moving = 1; if (val & GOPT_RES) reget = 1; if (val & GOPT_REC) { recursive = 1; nolinks = 2; if (fnspath == PATH_OFF) fnspath = PATH_REL; } break; case 'M': /* Transfer Mode */ val = atoi(p); debug(F101,"sgetinit M val","",val); if (val < 1) break; patterns = 0; /* Takes precedence over patterns */ filepeek = 0; /* and FILE SCAN */ if (val == GMOD_TXT) binary = XYFT_T; /* Text */ if (val == GMOD_BIN) binary = XYFT_B; /* Binary */ if (val == GMOD_LBL) binary = XYFT_L; /* Labeled */ break; case 'F': /* Filename */ fs = p; debug(F110,"sgetinit filename",fs,0); break; case '@': /* End Of Parameters */ done = 1; debug(F100,"sgetinit EOP","",0); break; default: errpkt((CHAR *)"Unknown GET Parameter"); debug(F100,"sgetinit unknown parameter","",0); return(-1); } p += (len + 1); c = next; } } if (!fs) fs = ""; /* A filename is required */ if (*fs) { havefs = 1; n = 0; /* Check for quoted name */ if ((n = strlen(fs)) > 1) { /* Note: this does not allow for multiple quoted names */ if ((fs[0] == '{' && fs[n-1] == '}') || (fs[0] == '"' && fs[n-1] == '"')) { fs[n-1] = '\0'; fs++; debug(F111,"sgetinit unquoted filename",fs,n); } else n = 0; /* This means no quoting */ } #ifdef PIPESEND debug(F111,"sgetinit",fs,usepipes); if (usepipes && ENABLED(en_hos) && *fs == '!') { cmarg = fs + 1; /* Point past the bang */ *fs = NUL; nfils = -1; pipesend = 1; debug(F111,"sgetinit pipesend",cmarg,pipesend); } if (!pipesend) { /* If it's not a pipe */ #endif /* PIPESEND */ if (n == 0) { /* If the name was not quoted */ #ifndef NOMSEND nfils = fnparse(fs); /* Allow it to be a list of names */ debug(F111,"sgetinit A",fs,nfils); #ifdef COMMENT /* This doesn't work if a GET-PATH is set. */ if (nfils == 1 && !iswild(fs)) { /* Single file */ char * m; if ((x = zchki(fs)) < 0) { /* Check if it's sendable */ switch (x) { case -1: m = "File not found"; break; case -2: m = "Not a regular file"; break; case -3: m = "Read access denied"; break; } errpkt((CHAR *)m); return(-1); } } #endif /* COMMENT */ } else { /* If it was quoted */ #endif /* NOMSEND */ nzxopts = 0; #ifdef UNIXOROSK if (matchdot) nzxopts |= ZX_MATCHDOT; #endif /* UNIXOROSK */ if (recursive) nzxopts |= ZX_RECURSE; /* Treat as a single filespec */ nfils = 0 - nzxpand(fs,nzxopts); debug(F111,"sgetinit B",fs,nfils); cmarg = fs; } #ifdef PIPESEND } #endif /* PIPESEND */ } if (!done) { /* Need more O packets... */ debug(F100,"sgetinit O-Packet TBC","",0); /* To Be Continued */ return(1); } debug(F100,"sgetinit O-Packet done - havefs","",havefs); if (!havefs) { /* Done - make sure we have filename */ errpkt((CHAR *)"GET without filename"); return(-1); } freerpkt(winlo); winlo = 0; /* Back to packet 0 again. */ debug(F101,"sgetinit winlo","",winlo); nakstate = 0; /* Now I'm the sender! */ if (reget) sendmode = SM_RESEND; if (sinit() > 0) { /* Send Send-Init */ #ifdef STREAMING if (!streaming) #endif /* STREAMING */ timint = chktimo(rtimo,timef); /* Switch to per-packet timer */ return(0); /* If successful, switch state */ } else return(-1); /* Else back to server command wait */ } #else /* NOXFER */ #include "ckcdeb.h" VOID proto() { extern int success; success = 0; } #endif /* NOXFER */ ckcpro.w0000644000015300001460000031600111573470131011335 0ustar fdckermitchar *protv = /* -*-C-*- */ "C-Kermit Protocol Module 9.0.160, 16 Oct 2009"; int kactive = 0; /* Kermit protocol is active */ #define PKTZEROHACK /* C K C P R O -- C-Kermit Protocol Module, in Wart preprocessor notation. */ /* Author: Frank da Cruz , Columbia University Academic Information Systems, New York City. Copyright (C) 1985, 2009, Trustees of Columbia University in the City of New York. All rights reserved. See the C-Kermit COPYING.TXT file or the copyright text in the ckcmai.c module for disclaimer and permissions. */ #ifndef NOXFER #include "ckcsym.h" #include "ckcdeb.h" #include "ckcasc.h" #include "ckcker.h" #ifdef OS2 #ifndef NT #define INCL_NOPM #define INCL_VIO /* Needed for ckocon.h */ #include #undef COMMENT #endif /* NT */ #include "ckocon.h" #endif /* OS2 */ /* Note -- This file may also be preprocessed by the UNIX Lex program, but you must indent the above #include statements before using Lex, and then restore them to the left margin in the resulting C program before compilation. Also, the invocation of the "wart()" function below must be replaced by an invocation of the "yylex()" function. It might also be necessary to remove comments in the (%)(%)...(%)(%) section. */ /* State definitions for Wart (or Lex) */ %states ipkt rfile rattr rdpkt ssinit ssfile ssattr ssdata sseof sseot %states serve generic get rgen ssopkt ropkt _PROTOTYP(static VOID xxproto,(void)); _PROTOTYP(static VOID wheremsg,(void)); _PROTOTYP(int wart,(void)); _PROTOTYP(static int sgetinit,(int,int)); _PROTOTYP(int sndspace,(int)); /* External C-Kermit variable declarations */ extern char *versio, *srvtxt, *cmarg, *cmarg2, **cmlist, *rf_err; extern char * rfspec, * sfspec, * srfspec, * rrfspec; extern char * prfspec, * psfspec, * psrfspec, * prrfspec; extern char *cdmsgfile[]; extern char * snd_move, * snd_rename, * srimsg; extern char filnam[], ofilnam[], fspec[], ttname[], ofn1[]; extern CHAR sstate, *srvptr, *data; extern int timint, rtimo, nfils, hcflg, xflg, flow, mdmtyp, network; extern int oopts, omode, oname, opath, nopush, isguest, xcmdsrc, rcdactive; extern int rejection, moving, fncact, bye_active, urserver, fatalio; extern int protocol, prefixing, filcnt, carrier, fnspath, interrupted; extern int recursive, inserver, nzxopts, idletmo, srvidl, xfrint; extern struct ck_p ptab[]; extern int remfile, rempipe, xferstat, filestatus, wearealike, fackpath; extern int patterns, filepeek, gnferror; extern char * remdest; #ifdef PKTZEROHACK #define PKTZEROLEN 32 static char ipktack[PKTZEROLEN]; static int ipktlen = 0; #endif /* PKTZEROHACK */ static int s_timint = -1; /* For saving timeout value */ static int myjob = 0; static int havefs = 0; #ifdef CK_LOGIN static int logtries = 0; #endif /* CK_LOGIN */ static int cancel = 0; int fackbug = 0; #ifdef STREAMING extern int streaming, streamok; static VOID streamon() { if (streamok) { debug(F100,"streamon","",0); streaming = 1; timint = 0; /* No timeouts while streaming. */ } } #ifdef COMMENT /* (not used) */ static VOID streamoff() { if (streaming) { debug(F100,"streamoff","",0); streaming = 0; timint = s_timint; /* Restore timeout */ } } #endif /* COMMENT */ #else /* STREAMING */ #define streamon() #define streamoff() #endif /* STREAMING */ #ifndef NOSPL _PROTOTYP( int addmac, (char *, char *) ); _PROTOTYP( int zzstring, (char *, char **, int *) ); #endif /* NOSPL */ #ifndef NOICP _PROTOTYP( int cmdsrc, (void) ); #endif /* NOICP */ #ifndef NOSERVER extern char * x_user, * x_passwd, * x_acct; extern int x_login, x_logged; #endif /* NOSERVER */ #include "ckcnet.h" #ifdef TNCODE extern int ttnproto; /* Network protocol */ #endif /* TNCODE */ #ifdef CK_SPEED extern short ctlp[]; /* Control-character prefix table */ #endif /* CK_SPEED */ #ifdef TNCODE extern int tn_b_nlm, tn_b_xfer, tn_nlm; #ifdef CK_ENCRYPTION extern int tn_no_encrypt_xfer; #endif /* CK_ENCRYPTION */ #endif /* TNCODE */ #ifdef TCPSOCKET #ifndef NOLISTEN extern int tcpsrfd; #endif /* NOLISTEN */ #endif /* TCPSOCKET */ extern int cxseen, czseen, server, srvdis, local, displa, bctu, bctr, bctl; extern int bctf; extern int quiet, tsecs, parity, backgrd, nakstate, atcapu, wslotn, winlo; extern int wslots, success, xitsta, rprintf, discard, cdtimo, keep, fdispla; extern int timef, stdinf, rscapu, sendmode, epktflg, epktrcvd, epktsent; extern int binary, fncnv; extern long speed, ffc, crc16, calibrate, dest; #ifdef COMMENT extern char *TYPCMD, *DIRCMD, *DIRCM2; #endif /* COMMENT */ #ifndef OS2 extern char *SPACMD, *SPACM2, *WHOCMD; #endif /* OS2 */ extern CHAR *rdatap; extern struct zattr iattr; #ifdef VMS extern int batch; #endif /* VMS */ #ifdef GFTIMER extern CKFLOAT fptsecs; #endif /* GFTIMER */ extern CHAR *srvcmd; extern CHAR *epktmsg; #ifdef CK_TMPDIR extern int f_tmpdir; /* Directory changed temporarily */ extern char savdir[]; /* For saving current directory */ extern char * dldir; #endif /* CK_TMPDIR */ extern int query; /* Query-active flag */ #ifndef NOSPL extern int cmdlvl; char querybuf[QBUFL+1] = { NUL, NUL }; /* QUERY response buffer */ char *qbufp = querybuf; /* Pointer to it */ int qbufn = 0; /* Length of data in it */ #else extern int tlevel; #endif /* NOSPL */ #ifndef NOICP extern int escape; #endif /* NOICP */ /* If the following flag is nonzero when the protocol module is entered, then server mode persists for exactly one transaction, rather than looping until BYE or FINISH is received. */ extern int justone; static int r_save = -1; static int p_save = -1; /* Function to let remote-mode user know where their file(s) went */ int whereflg = 1; /* Unset with SET XFER REPORT */ static VOID wheremsg() { extern int quiet, filrej; int n; n = filcnt - filrej; debug(F101,"wheremsg n","",n); debug(F110,"wheremsg prfspec",prfspec,0); debug(F110,"wheremsg rfspec",rfspec,0); debug(F110,"wheremsg psfspec",psfspec,0); debug(F110,"wheremsg sfspec",sfspec,0); debug(F110,"wheremsg prrfspec",prrfspec,0); debug(F110,"wheremsg rrfspec",rrfspec,0); debug(F110,"wheremsg psrfspec",psrfspec,0); debug(F110,"wheremsg srfspec",srfspec,0); if (!quiet && !local) { if (n == 1) { switch (myjob) { case 's': if (sfspec) { printf(" SENT: [%s]",sfspec); if (srfspec) printf(" To: [%s]",srfspec); printf(" (%s)\r\n", success ? "OK" : "FAILED"); } break; case 'r': case 'v': if (rrfspec) { printf(" RCVD: [%s]",rrfspec); if (rfspec) printf(" To: [%s]",rfspec); printf(" (%s)\r\n", success ? "OK" : "FAILED"); } } } else if (n > 1) { switch (myjob) { case 's': if (sfspec) { printf(" SENT: (%d files)",n); if (srfspec) printf(" Last: [%s]",srfspec); printf(" (%s)\r\n", success ? "OK" : "FAILED"); } break; case 'r': case 'v': if (rrfspec) { printf(" RCVD: (%d files)",n); if (rfspec) printf(" Last: [%s]",rfspec); printf(" (%s)\r\n", success ? "OK" : "FAILED"); } } } else if (n == 0) { if (myjob == 's') printf(" SENT: (0 files) \r\n"); else if (myjob == 'r' || myjob == 'v') printf(" RCVD: (0 files) \r\n"); } } } static VOID rdebug() { if (server) debug(F111,"RESUME","server=1",justone); else debug(F111,"RESUME","server=0",justone); } /* Flags for the ENABLE and DISABLE commands */ extern int en_cpy, en_cwd, en_del, en_dir, en_fin, en_get, en_bye, en_mai, en_pri, en_hos, en_ren, en_sen, en_spa, en_set, en_typ, en_who, en_ret, en_xit, en_mkd, en_rmd; #ifndef NOSPL extern int en_asg, en_que; #endif /* NOSPL */ extern int what, lastxfer; /* Global variables declared here */ int whatru = 0; /* What are you. */ int whatru2 = 0; /* What are you, cont'd. */ /* Local variables */ static char vstate = 0; /* Saved State */ static char vcmd = 0; /* Saved Command */ static int reget = 0; /* Flag for executing REGET */ static int retrieve = 0; /* Flag for executing RETRIEVE */ static int opkt = 0; /* Send Extended GET packet */ static int x; /* General-purpose integer */ static char *s; /* General-purpose string pointer */ /* Macros - Note, BEGIN is predefined by Wart (and Lex) as "state = ", */ /* BEGIN is NOT a GOTO! */ #define TINIT if (tinit(1) < 0) return(-9) #define SERVE { TINIT; resetc(); nakstate=1; what=W_NOTHING; cmarg2=""; \ sendmode=SM_SEND; havefs=0; recursive=r_save; fnspath=p_save; BEGIN serve; } #define RESUME { rdebug(); if (!server) { wheremsg(); return(0); } else \ if (justone) { justone=0; wheremsg(); return(0); } else { SERVE; } } #ifdef GFTIMER #define QUIT x=quiet; quiet=1; clsif(); clsof(1); tsecs=gtimer(); \ fptsecs=gftimer(); quiet=x; return(success) #else #define QUIT x=quiet; quiet=1; clsif(); clsof(1); tsecs=gtimer(); quiet=x; \ return(success) #endif /* GFTIMER */ /* By late 1999, the big switch() statement generated from the following state table began choking even gcc, so here we extract the code from the larger states into static routines to reduce the size of the cases and the switch() overall. The routines follow the state table; the prototypes are here. Each of these routines simply contains the text from the corresponding case, but with return(-1) added in appropriate places; see instructions after the state table switcher. */ static int rc; /* Return code for these routines */ static int rcv_s_pkt(); /* Received an S packet */ static int rcv_firstdata(); /* Received first Data packet */ static int rcv_shortreply(); /* Short reply to a REMOTE command */ static int srv_query(); /* Server answers an query */ static int srv_copy(); /* Server executes REMOTE COPY */ static int srv_rename(); /* Server executes REMOTE RENAME */ static int srv_login(); /* Server executes REMOTE LOGIN */ static int srv_timeout(); /* Server times out */ %% /* Protocol entry points, one for each start state (sstate). The lowercase letters are internal "inputs" from the user interface. NOTE: The start state letters that appear on the left margin immediately below can NOT be used as packet types OR as G-packet subcodes. */ s { TINIT; /* Send file(s) */ if (sinit() > 0) BEGIN ssinit; else RESUME; } v { TINIT; nakstate = 1; BEGIN get; } /* Receive file(s) */ r { /* Client sends a GET command */ TINIT; vstate = get; reget = 0; retrieve = 0; opkt = 0; vcmd = 0; #ifdef PKTZEROHACK ipktack[0] = NUL; #endif /* PKTZEROHACK */ if (sipkt('I') >= 0) BEGIN ipkt; else RESUME; } h { /* Client sends a RETRIEVE command */ TINIT; vstate = get; reget = 0; retrieve = 1; opkt = 0; vcmd = 0; if (sipkt('I') >= 0) BEGIN ipkt; else RESUME; } j { /* Client sends a REGET command */ TINIT; vstate = get; reget = 1; retrieve = 0; opkt = 0; vcmd = 0; if (sipkt('I') >= 0) BEGIN ipkt; else RESUME; } o { /* Client sends Extended GET Packet */ TINIT; vstate = get; reget = oopts & GOPT_RES; retrieve = oopts & GOPT_DEL; opkt = 1; vcmd = 0; if (sipkt('I') >= 0) BEGIN ipkt; else RESUME; } c { /* Client sends a Host command */ TINIT; vstate = rgen; vcmd = 'C'; if (sipkt('I') >= 0) BEGIN ipkt; else RESUME; } k { TINIT; /* Client sends a Kermit command */ vstate = rgen; vcmd = 'K'; if (sipkt('I') >= 0) BEGIN ipkt; else RESUME; } g { /* Client sends a REMOTE command */ TINIT; vstate = rgen; vcmd = 'G'; if (sipkt('I') >= 0) BEGIN ipkt; else RESUME; } x { /* Enter server mode */ int x; x = justone; if (!ENABLED(en_del)) { /* If DELETE is disabled */ if (fncact == XYFX_B || /* undo any file collision action */ fncact == XYFX_U || /* that could result in deletion or */ fncact == XYFX_A || /* modification of existing files. */ fncact == XYFX_X) { #ifndef NOICP extern int g_fncact; g_fncact = fncact; /* Save current setting */ #endif /* NOICP */ fncact = XYFX_R; /* Change to RENAME */ debug(F101,"server DELETE disabled so fncact RENAME","",fncact); } } SERVE; /* tinit() clears justone... */ justone = x; #ifdef IKSDB if (ikdbopen) slotstate(what, "SERVER", "", ""); #endif /* IKSDB */ } a { int b1 = 0, b2 = 0; if (!data) TINIT; /* "ABEND" -- Tell other side. */ if (!bctf) { /* Block check 3 forced on all packets */ #ifndef pdp11 if (epktflg) { /* If because of E-PACKET command */ b1 = bctl; b2 = bctu; /* Save block check type */ bctl = bctu = 1; /* set it to 1 */ } #endif /* pdp11 */ } errpkt((CHAR *)"User cancelled"); /* Send the packet */ if (!bctf) { /* Block check 3 forced on all packets */ #ifndef pdp11 if (epktflg) { /* Restore the block check */ epktflg = 0; bctl = b1; bctu = b2; } } #endif /* pdp11 */ success = 0; return(0); /* Return from protocol. */ } /* Dynamic states: input-character { action } nakstate != 0 means we're in a receiving state, in which we send ACKs & NAKs. */ S { /* Receive Send-Init packet. */ rc = rcv_s_pkt(); cancel = 0; /* Reset cancellation counter */ debug(F101,"rcv_s_pkt","",rc); if (rc > -1) return(rc); /* (see below) */ } /* States in which we get replies back from commands sent to a server. */ /* Complicated because direction of protocol changes, packet number */ /* stays at zero through I-G-S sequence, and complicated even more by */ /* sliding windows buffer allocation. */ Y { /* Get ack for I-packet */ int x = 0; #ifdef PKTZEROHACK ckstrncpy(ipktack,(char *)rdatap,PKTZEROLEN); /* Save a copy of the ACK */ ipktlen = strlen(ipktack); #endif /* PKTZEROHACK */ spar(rdatap); /* Set parameters */ cancel = 0; winlo = 0; /* Set window-low back to zero */ debug(F101,"Y winlo","",winlo); urserver = 1; /* So I know I'm talking to a server */ if (vcmd) { /* If sending a generic command */ if (tinit(0) < 0) return(-9); /* Initialize many things */ x = scmd(vcmd,(CHAR *)cmarg); /* Do that */ if (x >= 0) x = 0; /* (because of O-Packet) */ debug(F101,"proto G packet scmd","",x); vcmd = 0; /* and then un-remember it. */ } else if (vstate == get) { debug(F101,"REGET sstate","",sstate); x = srinit(reget, retrieve, opkt); /* GET or REGET, etc */ } if (x < 0) { /* If command was too long */ if (!srimsg) srimsg = "Error sending string"; errpkt((CHAR *)srimsg); /* cancel both sides. */ success = 0; RESUME; } else if (x > 0) { /* Need to send more O-Packets */ BEGIN ssopkt; } else { rtimer(); /* Reset the elapsed seconds timer. */ #ifdef GFTIMER rftimer(); #endif /* GFTIMER */ winlo = 0; /* Window back to 0, again. */ debug(F101,"Y vstate","",vstate); nakstate = 1; /* Can send NAKs from here. */ BEGIN vstate; /* Switch to desired state */ } } Y { /* Got ACK to O-Packet */ debug(F100,"CPCPRO Y","",0); x = sopkt(); debug(F101,"CPCPRO Y x","",x); if (x < 0) { /* If error */ errpkt((CHAR *)srimsg); /* cancel both sides. */ success = 0; RESUME; } else if (x == 0) { /* This was the last O-Packet */ rtimer(); /* Reset the elapsed seconds timer. */ #ifdef GFTIMER rftimer(); #endif /* GFTIMER */ winlo = 0; /* Window back to 0, again. */ debug(F101,"Y winlo","",winlo); nakstate = 1; /* Can send NAKs from here. */ BEGIN vstate; /* Switch to desired state */ } debug(F101,"CPCPRO Y not changing state","",x); } E { /* Ignore Error reply to I packet */ int x = 0; winlo = 0; /* Set window-low back to zero */ debug(F101,"E winlo","",winlo); if (vcmd) { /* In case other Kermit doesn't */ if (tinit(0) < 0) return(-9); x = scmd(vcmd,(CHAR *)cmarg); /* understand I-packets. */ if (x >= 0) x = 0; /* (because of O-Packet) */ vcmd = 0; /* Otherwise act as above... */ } else if (vstate == get) x = srinit(reget, retrieve, opkt); if (x < 0) { /* If command was too long */ errpkt((CHAR *)srimsg); /* cancel both sides. */ success = 0; RESUME; } else if (x > 0) { /* Need to send more O-Packets */ BEGIN ssopkt; } else { freerpkt(winlo); /* Discard the Error packet. */ debug(F101,"E winlo","",winlo); winlo = 0; /* Back to packet 0 again. */ nakstate = 1; /* Can send NAKs from here. */ BEGIN vstate; } } Y { /* Resend of previous I-pkt ACK, same seq number */ freerpkt(0); /* Free the ACK's receive buffer */ resend(0); /* Send the GET packet again. */ } /* States in which we're being a server */ I { /* Get I-packet */ #ifndef NOSERVER spar(rdatap); /* Set parameters from it */ ack1(rpar()); /* Respond with our own parameters */ #ifdef COMMENT pktinit(); /* Reinitialize packet numbers */ #else #ifdef COMMENT /* This can't be right - it undoes the stuff we just negotiated */ x = justone; tinit(1); /* Reinitialize EVERYTHING */ justone = x; /* But this... */ #else tinit(0); /* Initialize most things */ #endif /* COMMENT */ #endif /* COMMENT */ #endif /* NOSERVER */ cancel = 0; /* Reset cancellation counter */ } R { /* GET */ #ifndef NOSERVER if (x_login && !x_logged) { errpkt((CHAR *)"Login required"); SERVE; } else if (sgetinit(0,0) < 0) { RESUME; } else { #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "GET", (char *)srvcmd); #endif /* CKSYSLOG */ BEGIN ssinit; } #endif /* NOSERVER */ } H { /* GET /DELETE (RETRIEVE) */ #ifndef NOSERVER if (x_login && !x_logged) { errpkt((CHAR *)"Login required"); RESUME; } else if (!ENABLED(en_del)) { errpkt((CHAR *)"Deleting files is disabled"); RESUME; } else if (sgetinit(0,0) < 0) { RESUME; } else { moving = 1; #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "GET /DELETE", (char *)srvcmd); #endif /* CKSYSLOG */ BEGIN ssinit; } #endif /* NOSERVER */ } V { /* GET /RECURSIVE */ #ifndef NOSERVER recursive = 1; /* Set these before sgetinit() */ if (fnspath == PATH_OFF) fnspath = PATH_REL; /* Don't worry, they will be */ if (x_login && !x_logged) { /* reset next time through. */ errpkt((CHAR *)"Login required"); RESUME; } else if (sgetinit(0,0) < 0) { RESUME; } else { #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "GET /RECURSIVE", (char *)srvcmd); #endif /* CKSYSLOG */ BEGIN ssinit; } #endif /* NOSERVER */ } W { /* GET /RECURSIVE /DELETE */ #ifndef NOSERVER recursive = 1; /* Set these before sgetinit() */ if (fnspath == PATH_OFF) fnspath = PATH_REL; /* Don't worry, they will be */ moving = 1; /* reset next time through. */ if (x_login && !x_logged) { errpkt((CHAR *)"Login required"); RESUME; } else if (!ENABLED(en_del)) { errpkt((CHAR *)"Deleting files is disabled"); RESUME; } else if (sgetinit(0,0) < 0) { RESUME; } else { #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR,1,"server", "GET /RECURSIVE /DELETE",(char *)srvcmd); #endif /* CKSYSLOG */ BEGIN ssinit; } #endif /* NOSERVER */ } J { /* GET /RECOVER (REGET) */ #ifndef NOSERVER if (x_login && !x_logged) { errpkt((CHAR *)"Login required"); SERVE; } else if (sgetinit(1,0) < 0) { RESUME; } else { #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "GET /RECOVER", (char *)srvcmd); #endif /* CKSYSLOG */ BEGIN ssinit; } #endif /* NOSERVER */ } O { /* Extended GET */ #ifndef NOSERVER if (x_login && !x_logged) { /* (any combination of options) */ errpkt((CHAR *)"Login required"); SERVE; } else if ((x = sgetinit(0,1)) < 0) { debug(F101,"CKCPRO O sgetinit fail","",x); RESUME; } else if (x == 0) { debug(F101,"CKCPRO O sgetinit done","",x); #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "EXTENDED GET", (char *)srvcmd); #endif /* CKSYSLOG */ BEGIN ssinit; } else { /* Otherwise stay in this state */ debug(F101,"CKCPRO O sgetinit TBC","",x); ack(); BEGIN ropkt; } #endif /* NOSERVER */ } O { #ifndef NOSERVER if (x_login && !x_logged) { /* (any combination of options) */ errpkt((CHAR *)"Login required"); SERVE; } else if ((x = sgetinit(0,1)) < 0) { debug(F101,"CKCPRO O sgetinit fail","",x); RESUME; } else if (x == 0) { debug(F101,"CKCPRO O sgetinit done","",x); #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "EXTENDED GET", (char *)srvcmd); #endif /* CKSYSLOG */ BEGIN ssinit; } else { /* Otherwise stay in this state */ debug(F101,"CKCPRO O sgetinit TBC","",x); ack(); } #endif /* NOSERVER */ } G { /* Generic server command */ #ifndef NOSERVER srvptr = srvcmd; /* Point to command buffer */ decode(rdatap,putsrv,0); /* Decode packet data into it */ putsrv(NUL); /* Insert a couple nulls */ putsrv(NUL); /* for termination */ if (srvcmd[0]) { sstate = srvcmd[0]; /* Set requested start state */ if (x_login && !x_logged && /* Login required? */ /* Login, Logout, and Help are allowed when not logged in */ sstate != 'I' && sstate != 'L' && sstate != 'H') { errpkt((CHAR *)"Login required"); SERVE; } else { nakstate = 0; /* Now I'm the sender. */ what = W_REMO; /* Doing a REMOTE command. */ #ifdef STREAMING if (!streaming) #endif /* STREAMING */ if (timint < 1) timint = chktimo(rtimo,timef); /* Switch to per-packet timer */ binary = XYFT_T; /* Switch to text mode */ BEGIN generic; /* Switch to generic command state */ } } else { errpkt((CHAR *)"Badly formed server command"); /* report error */ RESUME; /* & go back to server command wait */ } #endif /* NOSERVER */ } C { /* Receive Host command */ #ifndef NOSERVER if (x_login && !x_logged) { errpkt((CHAR *)"Login required"); SERVE; } else if (!ENABLED(en_hos)) { errpkt((CHAR *)"REMOTE HOST disabled"); RESUME; } else if (nopush) { errpkt((CHAR *)"HOST commands not available"); RESUME; } else { srvptr = srvcmd; /* Point to command buffer */ decode(rdatap,putsrv,0); /* Decode command packet into it */ putsrv(NUL); /* Null-terminate */ nakstate = 0; /* Now sending, not receiving */ binary = XYFT_T; /* Switch to text mode */ if (syscmd((char *)srvcmd,"")) { /* Try to execute the command */ what = W_REMO; /* Doing a REMOTE command. */ #ifdef STREAMING if (!streaming) #endif /* STREAMING */ if (timint < 1) timint = chktimo(rtimo,timef); /* Switch to per-packet timer */ #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "REMOTE HOST", (char *)srvcmd); #endif /* CKSYSLOG */ BEGIN ssinit; /* If OK, send back its output */ } else { /* Otherwise */ errpkt((CHAR *)"Can't do system command"); /* report error */ RESUME; /* & go back to server command wait */ } } #endif /* NOSERVER */ } q { /* Interrupted or connection lost */ rc = srv_timeout(); debug(F101,"srv_timeout","",rc); if (rc > -1) return(rc); /* (see below) */ } N { /* Server got a NAK in command-wait */ #ifndef NOSERVER errpkt((CHAR *)"Did you say RECEIVE instead of GET?"); RESUME; #endif /* NOSERVER */ } . { /* Any other command in this state */ #ifndef NOSERVER if (c != ('E' - SP) && c != ('Y' - SP)) /* except E and Y packets. */ errpkt((CHAR *)"Unimplemented server function"); /* If we answer an E with an E, we get an infinite loop. */ /* A Y (ACK) can show up here if we sent back a short-form reply to */ /* a G packet and it was echoed. ACKs can be safely ignored here. */ RESUME; /* Go back to server command wait. */ #endif /* NOSERVER */ } I { /* Login/Out */ rc = srv_login(); debug(F101,"I srv_login","",rc); if (rc > -1) return(rc); /* (see below) */ } C { /* Got REMOTE CD command */ #ifndef NOSERVER #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "REMOTE CD", (char *)srvcmd); #endif /* CKSYSLOG */ if (!ENABLED(en_cwd)) { errpkt((CHAR *)"REMOTE CD disabled"); RESUME; } else { char * p = NULL; x = cwd((char *)(srvcmd+1)); /* Try to change directory */ #ifdef IKSDB if (ikdbopen) slotstate(what,"REMOTE CD", (char *)(srvcmd+2), ""); #endif /* IKSDB */ if (!x) { /* Failed */ errpkt((CHAR *)"Can't change directory"); RESUME; /* Back to server command wait */ } else if (x == 2) { /* User wants message */ if (!ENABLED(en_typ)) { /* Messages (REMOTE TYPE) disabled? */ errpkt((CHAR *)"REMOTE TYPE disabled"); RESUME; } else { /* TYPE is enabled */ int i; for (i = 0; i < 8; i++) { if (zchki(cdmsgfile[i]) > -1) { break; } } binary = XYFT_T; /* Use text mode for this. */ if (i < 8 && sndtype(cdmsgfile[i])) { /* Have readme file? */ BEGIN ssinit; /* OK */ } else { /* not OK */ p = zgtdir(); if (!p) p = ""; success = (*p) ? 1 : 0; ack1((CHAR *)p); /* ACK with new directory name */ success = 1; RESUME; /* wait for next server command */ } } } else { /* User doesn't want message */ p = zgtdir(); if (!p) p = ""; success = (*p) ? 1 : 0; ack1((CHAR *)p); success = 1; RESUME; /* Wait for next server command */ } } #endif /* NOSERVER */ } A { /* Got REMOTE PWD command */ #ifndef NOSERVER #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "REMOTE PWD", NULL); #endif /* CKSYSLOG */ if (!ENABLED(en_cwd)) { errpkt((CHAR *)"REMOTE CD disabled"); RESUME; } else { if (encstr((CHAR *)zgtdir()) > -1) { /* Encode current directory */ ack1(data); /* If it fits, send it back in ACK */ success = 1; } else { /* Failed */ ack(); /* Send empty ACK */ success = 0; /* and indicate failure locally */ } RESUME; /* Back to server command wait */ } #endif /* NOSERVER */ } D { /* REMOTE DIRECTORY command */ #ifndef NOSERVER char *n2; #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "REMOTE DIRECTORY", (char *)srvcmd); #endif /* CKSYSLOG */ if (!ENABLED(en_dir)) { /* If DIR is disabled, */ errpkt((CHAR *)"REMOTE DIRECTORY disabled"); /* refuse. */ RESUME; } else { /* DIR is enabled. */ #ifdef IKSDB if (ikdbopen) slotstate(what,"REMOTE DIR", (char *)(srvcmd+2), ""); #endif /* IKSDB */ if (!ENABLED(en_cwd)) { /* But CWD is disabled */ zstrip((char *)(srvcmd+2),&n2); /* and they included a pathname, */ if (strcmp((char *)(srvcmd+2),n2)) { /* so refuse. */ errpkt((CHAR *)"Access denied"); RESUME; /* Remember, this is not a goto! */ } } if (state == generic) { /* It's OK to go ahead. */ #ifdef COMMENT n2 = (*(srvcmd+2)) ? DIRCMD : DIRCM2; if (syscmd(n2,(char *)(srvcmd+2))) /* If it can be done */ #else int x; if ((x = snddir((char*)(srvcmd+2))) > 0) #endif /* COMMENT */ { BEGIN ssinit; /* send the results back; */ } else { /* otherwise */ if (x < 0) errpkt((CHAR *)"No files match"); else errpkt((CHAR *)"Can't list directory"); RESUME; /* return to server command wait */ } } } #endif /* NOSERVER */ } E { /* REMOTE DELETE (Erase) */ #ifndef NOSERVER char *n2; #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "REMOTE DELETE", (char *)srvcmd); #endif /* CKSYSLOG */ if (!ENABLED(en_del)) { errpkt((CHAR *)"REMOTE DELETE disabled"); RESUME; } else { /* DELETE is enabled */ #ifdef IKSDB if (ikdbopen) slotstate(what,"REMOTE DELETE", (char *)(srvcmd+2), ""); #endif /* IKSDB */ if (!ENABLED(en_cwd)) { /* but CWD is disabled */ zstrip((char *)(srvcmd+2),&n2); /* and they included a pathname, */ if (strcmp((char *)(srvcmd+2),n2)) { /* so refuse. */ errpkt((CHAR *)"Access denied"); RESUME; /* Remember, this is not a goto! */ } } else if (isdir((char *)(srvcmd+2))) { /* A directory name? */ errpkt((CHAR *)"It's a directory"); RESUME; } if (state == generic) { /* It's OK to go ahead. */ int x; if ((x = snddel((char*)(srvcmd+2))) > 0) { BEGIN ssinit; /* If OK send results back */ } else { /* otherwise */ if (x < 0) errpkt((CHAR *)"File not found"); /* report failure */ else errpkt((CHAR *)"DELETE failed"); RESUME; /* & return to server command wait */ } } } #endif /* NOSERVER */ } F { /* FINISH */ #ifndef NOSERVER #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "FINISH", NULL); #endif /* CKSYSLOG */ #ifdef IKSDB if (ikdbopen) slotstate(what,"SERVER FINISH", "", ""); #endif /* IKSDB */ if (!ENABLED(en_fin)) { errpkt((CHAR *)"FINISH disabled"); RESUME; } else { ack(); /* Acknowledge */ xxscreen(SCR_TC,0,0L,""); /* Display */ success = 1; return(0); /* Done */ } #endif /* NOSERVER */ } X { /* EXIT */ #ifndef NOSERVER #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "REMOTE EXIT", NULL); #endif /* CKSYSLOG */ #ifdef IKSDB if (ikdbopen) slotstate(what,"REMOTE EXIT", "", ""); #endif /* IKSDB */ if (!ENABLED(en_xit)) { errpkt((CHAR *)"EXIT disabled"); RESUME; } else { ack(); /* Acknowledge */ xxscreen(SCR_TC,0,0L,""); /* Display */ doexit(GOOD_EXIT,xitsta); } #endif /* NOSERVER */ } L { /* BYE (Logout) */ #ifndef NOSERVER #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "BYE", NULL); #endif /* CKSYSLOG */ #ifdef IKSDB if (ikdbopen) slotstate(what,"SERVER BYE", "", ""); #endif /* IKSDB */ if (!ENABLED(en_bye)) { errpkt((CHAR *)"BYE disabled"); RESUME; } else { ack(); /* Acknowledge */ success = 1; msleep(750); /* Give the ACK time to get out */ if (local) ttres(); /* Reset the terminal */ xxscreen(SCR_TC,0,0L,""); /* Display */ doclean(1); /* Clean up files, etc */ #ifdef DEBUG debug(F100,"C-Kermit BYE - Logging out...","",0); zclose(ZDFILE); #endif /* DEBUG */ #ifdef IKSD #ifdef CK_LOGIN if (inserver) ckxlogout(); else #endif /* CK_LOGIN */ #endif /* IKSD */ #ifdef TCPSOCKET #ifndef NOLISTEN if (network && tcpsrfd > 0 && !inserver) doexit(GOOD_EXIT,xitsta); else #endif /* NOLISTEN */ #endif /* TCPSOCKET */ return(zkself()); /* Try to log self out */ } #endif /* NOSERVER */ } H { /* REMOTE HELP */ #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "REMOTE HELP", NULL); #endif /* CKSYSLOG */ #ifdef IKSDB if (ikdbopen) slotstate(what,"REMOTE HELP", "", ""); #endif /* IKSDB */ #ifndef NOSERVER if (sndhlp()) { BEGIN ssinit; /* try to send it */ } else { /* If not ok, */ errpkt((CHAR *)"Can't send help"); /* send error message instead */ RESUME; /* and return to server command wait */ } #endif /* NOSERVER */ } R { /* REMOTE RENAME */ rc = srv_rename(); debug(F101,"srv_rename","",rc); if (rc > -1) return(rc); /* (see below) */ } K { /* REMOTE COPY */ rc = srv_copy(); debug(F101,"srv_copy","",rc); if (rc > -1) return(rc); /* (see below) */ } S { /* REMOTE SET */ #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "REMOTE SET", (char *)srvcmd); #endif /* CKSYSLOG */ #ifndef NOSERVER #ifdef IKSDB if (ikdbopen) slotstate(what,"REMOTE SET", (char *)(srvcmd+1), ""); #endif /* IKSDB */ if (!ENABLED(en_set)) { errpkt((CHAR *)"REMOTE SET disabled"); RESUME; } else { if (remset((char *)(srvcmd+1))) { /* Try to do what they ask */ success = 1; ack(); /* If OK, then acknowledge */ } else /* Otherwise */ errpkt((CHAR *)"Unknown REMOTE SET parameter"); /* give error msg */ RESUME; /* Return to server command wait */ } #endif /* NOSERVER */ } T { /* REMOTE TYPE */ #ifndef NOSERVER char *n2; #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "REMOTE TYPE", (char *)srvcmd); #endif /* CKSYSLOG */ if (!ENABLED(en_typ)) { errpkt((CHAR *)"REMOTE TYPE disabled"); RESUME; } else { #ifdef IKSDB if (ikdbopen) slotstate(what,"REMOTE TYPE", (char *)(srvcmd+2), ""); #endif /* IKSDB */ if (!ENABLED(en_cwd)) { /* If CWD disabled */ zstrip((char *)(srvcmd+2),&n2); /* and they included a pathname, */ if (strcmp((char *)(srvcmd+2),n2)) { /* refuse. */ errpkt((CHAR *)"Access denied"); RESUME; /* Remember, this is not a goto! */ } } if (state == generic) { /* It's OK to go ahead. */ binary = XYFT_T; /* Use text mode for this. */ if ( /* (RESUME didn't change state) */ #ifdef COMMENT syscmd(TYPCMD,(char *)(srvcmd+2)) /* Old way */ #else sndtype((char *)(srvcmd+2)) /* New way */ #endif /* COMMENT */ ) BEGIN ssinit; /* OK */ else { /* not OK */ errpkt((CHAR *)"Can't type file"); /* give error message */ RESUME; /* wait for next server command */ } } } #endif /* NOSERVER */ } m { /* REMOTE MKDIR */ #ifndef NOSERVER #ifdef CK_MKDIR #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "REMOTE MKDIR", (char *)srvcmd); #endif /* CKSYSLOG */ #ifdef IKSDB if (ikdbopen) slotstate(what,"REMOTE MKDIR", (char *)(srvcmd+2), ""); #endif /* IKSDB */ if (!ENABLED(en_mkd)) { errpkt((CHAR *)"REMOTE MKDIR disabled"); RESUME; } else if (!ENABLED(en_cwd)) { /* If CWD disabled */ errpkt((CHAR *)"Directory access restricted"); RESUME; /* Remember, this is not a goto! */ } if (state == generic) { /* OK to go ahead. */ char *p = NULL; x = ckmkdir(0,(char *)(srvcmd+2),&p,0,1); /* Make the directory */ if (!p) p = ""; if (x > -1) { encstr((CHAR *)p); /* OK - encode the name */ ack1(data); /* Send short-form response */ success = 1; RESUME; } else { /* not OK */ if (!*p) p = "Directory creation failure"; errpkt((CHAR *)p); /* give error message */ RESUME; /* Wait for next server command */ } } #else errpkt((CHAR *)"REMOTE MKDIR not available"); RESUME; #endif /* CK_MKDIR */ #endif /* NOSERVER */ } d { /* REMOTE RMDIR */ #ifndef NOSERVER #ifdef CK_MKDIR #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "REMOTE RMDIR", (char *)srvcmd); #endif /* CKSYSLOG */ #ifdef IKSDB if (ikdbopen) slotstate(what,"REMOTE RMDIR", (char *)(srvcmd+2), ""); #endif /* IKSDB */ if (!ENABLED(en_rmd)) { errpkt((CHAR *)"REMOTE RMDIR disabled"); RESUME; } else if (!ENABLED(en_cwd)) { /* If CWD disabled */ errpkt((CHAR *)"Directory access restricted"); RESUME; /* Remember, this is not a goto! */ } if (state == generic) { /* OK to go ahead. */ char *p = NULL; x = ckmkdir(1,(char *)(srvcmd+2),&p,0,1); if (!p) p = ""; if (x > -1) { encstr((CHAR *)p); /* OK - encode the name */ ack1(data); /* Send short-form response */ success = 1; RESUME; } else { /* not OK */ if (!*p) p = "Directory removal failure"; errpkt((CHAR *)p); /* give error message */ RESUME; /* Wait for next server command */ } } #else errpkt((CHAR *)"REMOTE RMDIR not available"); RESUME; #endif /* CK_MKDIR */ #endif /* NOSERVER */ } U { /* REMOTE SPACE */ #ifndef NOSERVER #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "REMOTE SPACE", (char *)srvcmd); #endif /* CKSYSLOG */ if (!ENABLED(en_spa)) { errpkt((CHAR *)"REMOTE SPACE disabled"); RESUME; } else { x = srvcmd[1]; /* Get area to check */ x = ((x == NUL) || (x == SP) #ifdef OS2 || (x == '!') || (srvcmd[3] == ':') #endif /* OS2 */ ); #ifdef IKSDB if (ikdbopen) slotstate(what, "REMOTE SPACE", (x ? "" : (char *)srvcmd), "" ); #endif /* IKSDB */ if (!x && !ENABLED(en_cwd)) { /* CWD disabled */ errpkt((CHAR *)"Access denied"); /* and non-default area given, */ RESUME; /* refuse. */ } else { #ifdef OS2 _PROTOTYP(int sndspace,(int)); if (sndspace(x ? toupper(srvcmd[2]) : 0)) { BEGIN ssinit; /* send the report. */ } else { /* If not ok, */ errpkt((CHAR *)"Can't send space"); /* send error message */ RESUME; /* and return to server command wait */ } #else if (nopush) x = 0; else x = (x ? syscmd(SPACMD,"") : syscmd(SPACM2,(char *)(srvcmd+2))); if (x) { /* If we got the info */ BEGIN ssinit; /* send it */ } else { /* otherwise */ errpkt((CHAR *)"Can't check space"); /* send error message */ RESUME; /* and await next server command */ } #endif /* OS2 */ } } #endif /* NOSERVER */ } W { /* REMOTE WHO */ #ifndef NOSERVER #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "REMOTE WHO", (char *)srvcmd); #endif /* CKSYSLOG */ #ifdef IKSDB if (ikdbopen) slotstate(what,"REMOTE WHO", (char *)(srvcmd+2), ""); #endif /* IKSDB */ if (!ENABLED(en_who)) { errpkt((CHAR *)"REMOTE WHO disabled"); RESUME; } else { #ifdef OS2 _PROTOTYP(int sndwho,(char *)); if (sndwho((char *)(srvcmd+2))) { BEGIN ssinit; /* try to send it */ } else { /* If not ok, */ errpkt((CHAR *)"Can't do who command"); /* send error msg */ RESUME; /* and return to server command wait */ } #else if (syscmd(WHOCMD,(char *)(srvcmd+2))) { BEGIN ssinit; } else { errpkt((CHAR *)"Can't do who command"); RESUME; } #endif /* OS2 */ } #endif /* NOSERVER */ } V { /* Variable query or set */ rc = srv_query(); debug(F101,"srv_query","",rc); if (rc > -1) return(rc); } M { /* REMOTE MESSAGE command */ #ifndef NOSERVER debug(F110,"RMSG",(char *)srvcmd+2,0); xxscreen(SCR_MS,0,0L,(char *)(srvcmd+2)); ack(); RESUME; #endif /* NOSERVER */ } q { /* Interrupted or connection lost */ #ifndef NOSERVER if (fatalio) { /* Connection lost */ #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "Interrupted", NULL); #endif /* CKSYSLOG */ success = 0; xitsta |= (what & W_KERMIT); QUIT; } else if (interrupted) { if (!ENABLED(en_fin)) { /* Ctrl-C typed */ errpkt((CHAR *)"QUIT disabled"); RESUME; } else { #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "Interrupted", NULL); #endif /* CKSYSLOG */ success = 0; xitsta |= (what & W_KERMIT); QUIT; } } else { /* Shouldn't happen */ debug(F100,"SERVER (generic) GOT UNEXPECTED 'q'","",0); QUIT; } #endif /* NOSERVER */ } . { /* Anything else in this state... */ #ifndef NOSERVER errpkt((CHAR *)"Unimplemented REMOTE command"); /* Complain */ RESUME; /* and return to server command wait */ #endif /* NOSERVER */ } q { /* Sent BYE and connection broken */ if (bye_active && ttchk() < 0) { msleep(500); bye_active = 0; ttclos(0); /* Close our end of the connection */ clsof(0); return(success = 1); } else { /* Other generic command */ return(success = 0); /* or connection not broken */ } } Y { /* Short-Form reply */ rc = rcv_shortreply(); debug(F101,"Y rcv_shortreply","",rc); if (rc > -1) return(rc); } F { /* File header */ /* char *n2; */ extern int rsn; debug(F101,"F winlo 1","",winlo); xflg = 0; /* Not screen data */ if (!czseen) cancel = 0; /* Reset cancellation counter */ #ifdef CALIBRATE if (dest == DEST_N) calibrate = 1; #endif /* CALIBRATE */ if (!rcvfil(filnam)) { /* Figure out local filename */ errpkt((CHAR *)rf_err); /* Trouble */ RESUME; } else { /* Real file, OK to receive */ char * fnp; debug(F111,"F winlo 2",fspec,winlo); if (filcnt == 1) /* rcvfil set this to 1 for 1st file */ crc16 = 0L; /* Clear file CRC */ fnp = fspec; /* This is the full path */ if (server && !ENABLED(en_cwd) || /* if DISABLE CD */ !fackpath /* or F-ACK-PATH OFF */ ) { zstrip(fspec,&fnp); /* don't send back full path */ } encstr((CHAR *)fnp); if (fackbug) ack(); else ack1(data); /* Send it back in ACK */ initattr(&iattr); /* Clear file attribute structure */ streamon(); if (window(wslotn) < 0) { /* Allocate negotiated window slots */ errpkt((CHAR *)"Can't open window"); RESUME; } #ifdef IKSDB if (ikdbopen) slotstate(what, server ? "SERVER" : "", "RECEIVE", fspec ); #endif /* IKSDB */ BEGIN rattr; /* Now expect Attribute packets */ } } X { /* X-packet instead of file header */ xflg = 1; /* Screen data */ if (!czseen) cancel = 0; /* Reset cancellation counter */ ack(); /* Acknowledge the X-packet */ initattr(&iattr); /* Initialize attribute structure */ streamon(); if (window(wslotn) < 0) { /* allocate negotiated window slots */ errpkt((CHAR *)"Can't open window"); RESUME; } #ifndef NOSPL if (query) { /* If this is the response to */ qbufp = querybuf; /* a query that we sent, initialize */ qbufn = 0; /* the response buffer */ querybuf[0] = NUL; } #endif /* NOSPL */ what = W_REMO; /* we're doing a REMOTE command */ #ifdef IKSDB if (ikdbopen) slotstate(what, server ? "SERVER" : "", "RECEIVE", fspec ); #endif /* IKSDB */ BEGIN rattr; /* Expect Attribute packets */ } A { /* Attribute packet */ if (gattr(rdatap,&iattr) == 0) { /* Read into attribute structure */ #ifdef CK_RESEND ack1((CHAR *)iattr.reply.val); /* Reply with data */ #else ack(); /* If OK, acknowledge */ #endif /* CK_RESEND */ } else { /* Otherwise */ extern long fsize; char *r; r = getreason(iattr.reply.val); ack1((CHAR *)iattr.reply.val); /* refuse to accept the file */ xxscreen(SCR_ST,ST_REFU,0L,r); /* reason */ #ifdef TLOG if (tralog && !tlogfmt) doxlog(what,filnam,fsize,binary,1,r); #endif /* TLOG */ } } D { /* First data packet */ debug(F100," D firstdata","",0); rc = rcv_firstdata(); debug(F101,"rcv_firstdata rc","",rc); if (rc > -1) return(rc); /* (see below) */ } B { /* EOT, no more files */ ack(); /* Acknowledge the B packet */ reot(); /* Do EOT things */ #ifdef CK_TMPDIR /* If we were cd'd temporarily to another device or directory ... */ if (f_tmpdir) { int x; x = zchdir((char *) savdir); /* ... restore previous directory */ f_tmpdir = 0; /* and remember we did it. */ debug(F111,"ckcpro.w B tmpdir restoring",savdir,x); } #endif /* CK_TMPDIR */ RESUME; /* and quit */ } D { /* Got Data packet */ debug(F101,"D cxseen","",cxseen); debug(F101,"D czseen","",czseen); if (cxseen || czseen || discard) { /* If file or group interruption */ CHAR * msg; msg = czseen ? (CHAR *)"Z" : (CHAR *)"X"; #ifdef STREAMING if (streaming) { /* Need to cancel */ debug(F111,"D streaming cancel",msg,cancel); if (cancel++ == 0) { /* Only do this once */ ack1(msg); /* Put "X" or "Z" in ACK */ } else if (czseen) { errpkt((CHAR *)"User canceled"); RESUME; } else { fastack(); } } else #endif /* STREAMING */ ack1(msg); } else { /* No interruption */ int rc, qf; #ifndef NOSPL qf = query; #else qf = 0; #endif /* NOSPL */ #ifdef CKTUNING rc = (binary && !parity) ? bdecode(rdatap,putfil): decode(rdatap, qf ? puttrm : putfil, 1); #else rc = decode(rdatap, qf ? puttrm : putfil, 1); #endif /* CKTUNING */ if (rc < 0) { discard = (keep == 0 || (keep == SET_AUTO && binary != XYFT_T)); errpkt((CHAR *)"Error writing data"); /* If failure, */ RESUME; } else /* Data written OK, send ACK */ #ifdef STREAMING if (streaming) fastack(); else #endif /* STREAMING */ ack(); } } Z { /* EOF immediately after A-Packet. */ rf_err = "Can't create file"; timint = s_timint; if (discard) { /* Discarding a real file... */ x = 1; } else if (xflg) { /* If screen data */ if (remfile) { /* redirected to file */ if (rempipe) /* or pipe */ x = openc(ZOFILE,remdest); /* Pipe: start command */ else x = opena(remdest,&iattr); /* File: open with attributes */ } else { /* otherwise */ x = opent(&iattr); /* "open" the screen */ } #ifdef CALIBRATE } else if (calibrate) { /* If calibration run */ x = ckopenx(&iattr); /* do this */ #endif /* CALIBRATE */ } else { /* otherwise */ x = opena(filnam,&iattr); /* open the file, with attributes */ if (x == -17) { /* REGET skipped because same size */ discard = 1; rejection = 1; } } if (!x || reof(filnam, &iattr) < 0) { /* Close output file */ errpkt((CHAR *) rf_err); /* If problem, send error msg */ RESUME; /* and quit */ } else { /* otherwise */ if (x == -17) xxscreen(SCR_ST,ST_SKIP,SKP_RES,""); ack(); /* acknowledge the EOF packet */ BEGIN rfile; /* and await another file */ } } q { /* Ctrl-C or connection loss. */ timint = s_timint; window(1); /* Set window size back to 1... */ cxseen = 1; x = clsof(1); /* Close file */ return(success = 0); /* Failed */ } Z { /* End Of File (EOF) Packet */ /* wslots = 1; */ /* (don't set) Window size back to 1 */ #ifndef COHERENT /* Coherent compiler blows up on this switch() statement. */ x = reof(filnam, &iattr); /* Handle the EOF packet */ switch (x) { /* reof() sets the success flag */ case -5: /* Handle problems */ errpkt((CHAR *)"RENAME failed"); /* Fatal */ RESUME; break; case -4: errpkt((CHAR *)"MOVE failed"); /* Fatal */ RESUME; break; case -3: /* If problem, send error msg */ errpkt((CHAR *)"Can't print file"); /* Fatal */ RESUME; break; case -2: errpkt((CHAR *)"Can't mail file"); /* Fatal */ RESUME; break; case 2: /* Not fatal */ case 3: xxscreen(SCR_EM,0,0L,"Receiver can't delete temp file"); RESUME; break; default: if (x < 0) { /* Fatal */ errpkt((CHAR *)"Can't close file"); RESUME; } else { /* Success */ #ifndef NOSPL if (query) /* Query reponses generally */ conoll(""); /* don't have line terminators */ #endif /* NOSPL */ if (czseen) { /* Batch canceled? */ if (cancel++ == 0) { /* If we haven't tried this yet */ ack1((CHAR *)"Z"); /* Try it once */ } else { /* Otherwise */ errpkt((CHAR *)"User canceled"); /* quite with Error */ RESUME; } } else ack(); /* Acknowledge the EOF packet */ BEGIN rfile; /* and await another file */ } } #else if (reof(filnam, &iattr) < 0) { /* Close the file */ errpkt((CHAR *)"Error at end of file"); RESUME; } else { /* reof() sets success flag */ ack(); BEGIN rfile; } #endif /* COHERENT */ } Y { /* ACK for Send-Init */ spar(rdatap); /* set parameters from it */ cancel = 0; if (bctf) { bctu = 3; bctl = 3; } else { bctu = bctr; /* switch to agreed-upon block check */ bctl = (bctu == 4) ? 2 : bctu; /* Set block-check length */ } #ifdef CK_RESEND if ((sendmode == SM_RESEND) && (!atcapu || !rscapu)) { /* RESEND */ errpkt((CHAR *) "RESEND capabilities not negotiated"); RESUME; } else { #endif /* CK_RESEND */ what = W_SEND; /* Remember we're sending */ lastxfer = W_SEND; x = sfile(xflg); /* Send X or F header packet */ cancel = 0; /* Reset cancellation counter */ if (x) { /* If the packet was sent OK */ if (!xflg && filcnt == 1) /* and it's a real file */ crc16 = 0L; /* Clear the file CRC */ resetc(); /* reset per-transaction counters */ rtimer(); /* reset timers */ #ifdef GFTIMER rftimer(); #endif /* GFTIMER */ streamon(); /* turn on streaming */ #ifdef IKSDB if (ikdbopen) slotstate(what, (server ? "SERVER" : ""), "SEND", filnam ); #endif /* IKSDB */ BEGIN ssfile; /* and switch to receive-file state */ } else { /* otherwise send error msg & quit */ s = xflg ? "Can't execute command" : (char *)epktmsg; if (!*s) s = "Can't open file"; errpkt((CHAR *)s); RESUME; } #ifdef CK_RESEND } #endif /* CK_RESEND */ } /* These states are necessary to handle the case where we get a server command packet (R, G, or C) reply with an S packet, but the client retransmits the command packet. The input() function doesn't catch this because the packet number is still zero. */ R { /* R packet was retransmitted. */ xsinit(); /* Resend packet 0 */ } G { /* Same deal if G packet comes again */ xsinit(); } /* should probably add cases for O, W, V, H, J, ... */ C { /* Same deal if C packet comes again */ xsinit(); } Y { /* ACK for F or X packet */ srvptr = srvcmd; /* Point to string buffer */ decode(rdatap,putsrv,0); /* Decode data field, if any */ putsrv(NUL); /* Terminate with null */ ffc = 0L; /* Reset file byte counter */ debug(F101,"Y cxseen","",cxseen); if (*srvcmd) { /* If remote name was recorded */ if (sendmode != SM_RESEND) { if (fdispla == XYFD_C || fdispla == XYFD_S) xxscreen(SCR_AN,0,0L,(char *)srvcmd); tlog(F110," remote name:",(char *) srvcmd,0L); makestr(&psrfspec,(char *)srvcmd); } } if (cxseen||czseen) { /* Interrupted? */ debug(F101,"Y canceling","",0); x = clsif(); /* Close input file */ sxeof(1); /* Send EOF(D) */ BEGIN sseof; /* and switch to EOF state. */ } else if (atcapu) { /* If attributes are to be used */ if (sattr(xflg | stdinf, 1) < 0) { /* send them */ errpkt((CHAR *)"Can't send attributes"); /* if problem, say so */ RESUME; /* and quit */ } else BEGIN ssattr; /* if ok, switch to attribute state */ } else { /* Attributes not negotiated */ if (window(wslotn) < 0) { /* Open window */ errpkt((CHAR *)"Can't open window"); RESUME; } else if ((x = sdata()) == -2) { /* Send first data packet data */ window(1); /* Connection lost, reset window */ x = clsif(); /* Close input file */ return(success = 0); /* Return failure */ } else if (x == -9) { /* User interrupted */ errpkt((CHAR *)"User cancelled"); /* Send Error packet */ window(1); /* Set window size back to 1... */ timint = s_timint; /* Restore timeout */ return(success = 0); /* Failed */ } else if (x < 0) { /* EOF (empty file) or interrupted */ window(1); /* put window size back to 1, */ debug(F101,"Y cxseen","",cxseen); x = clsif(); /* If not ok, close input file, */ if (x < 0) /* treating failure as interruption */ cxseen = 1; /* Send EOF packet */ seof(cxseen||czseen); BEGIN sseof; /* and switch to EOF state. */ } else { /* First data sent OK */ BEGIN ssdata; /* All ok, switch to send-data state */ } } } Y { /* Got ACK to A packet */ ffc = 0L; /* Reset file byte counter */ debug(F101,"Y cxseen","",cxseen); if (cxseen||czseen) { /* Interrupted? */ debug(F101,"Y canceling","",0); x = clsif(); /* Close input file */ sxeof(1); /* Send EOF(D) */ BEGIN sseof; /* and switch to EOF state. */ } else if (rsattr(rdatap) < 0) { /* Was the file refused? */ discard = 1; /* Set the discard flag */ clsif(); /* Close the file */ sxeof(1); /* send EOF with "discard" code */ BEGIN sseof; /* switch to send-EOF state */ } else if ((x = sattr(xflg | stdinf, 0)) < 0) { /* Send more? */ errpkt((CHAR *)"Can't send attributes"); /* Trouble... */ RESUME; } else if (x == 0) { /* No more to send so now the data */ if (window(wslotn) < 0) { /* Allocate negotiated window slots */ errpkt((CHAR *)"Can't open window"); RESUME; } if ((x = sdata()) == -2) { /* File accepted, send first data */ window(1); /* Connection broken */ x = clsif(); /* Close file */ return(success = 0); /* Return failure */ } else if (x == -9) { /* User interrupted */ errpkt((CHAR *)"User cancelled"); /* Send Error packet */ window(1); /* Set window size back to 1... */ timint = s_timint; /* Restore timeout */ return(success = 0); /* Failed */ } else if (x < 0) { /* If data was not sent */ window(1); /* put window size back to 1, */ debug(F101,"Y cxseen","",cxseen); if (clsif() < 0) /* Close input file */ cxseen = 1; /* Send EOF packet */ seof(cxseen||czseen); BEGIN sseof; /* and switch to EOF state. */ } else { BEGIN ssdata; /* All ok, switch to send-data state */ } } } q { /* Ctrl-C or connection loss. */ window(1); /* Set window size back to 1... */ cxseen = 1; /* To indicate interruption */ x = clsif(); /* Close file */ return(success = 0); /* Failed */ } Y { /* Got ACK to Data packet */ canned(rdatap); /* Check if file transfer cancelled */ debug(F111,"Y cxseen",rdatap,cxseen); debug(F111,"Y czseen",rdatap,czseen); if ((x = sdata()) == -2) { /* Try to send next data */ window(1); /* Connection lost, reset window */ x = clsif(); /* Close file */ return(success = 0); /* Failed */ } else if (x == -9) { /* User interrupted */ errpkt((CHAR *)"User cancelled"); /* Send Error packet */ window(1); /* Set window size back to 1... */ timint = s_timint; /* Restore original timeout */ return(success = 0); /* Failed */ } else if (x < 0) { /* EOF - finished sending data */ debug(F101,"Y cxseen","",cxseen); window(1); /* Set window size back to 1... */ if (clsif() < 0) /* Close input file */ cxseen = 1; /* Send EOF packet */ debug(F101,"Y CALLING SEOF()","",cxseen); seof(cxseen||czseen); BEGIN sseof; /* and enter send-eof state */ } /* NOTE: If x == 0 it means we're draining: see sdata()! */ } Y { /* Got ACK to EOF */ int g, xdiscard; canned(rdatap); /* Check if file transfer cancelled */ debug(F111,"Y cxseen",rdatap,cxseen); debug(F111,"Y czseen",rdatap,czseen); debug(F111,"Y discard",rdatap,discard); xdiscard = discard; discard = 0; success = (cxseen == 0 && czseen == 0); /* Transfer status... */ debug(F101,"Y success","",success); if (success && rejection > 0) /* If rejected, succeed if */ if (rejection != '#' && /* reason was date */ rejection != 1 && rejection != '?') /* or name; */ success = 0; /* fail otherwise. */ cxseen = 0; /* This goes back to zero. */ if (success) { /* Only if transfer succeeded... */ xxscreen(SCR_ST,ST_OK,0L,""); if (!xdiscard) { makestr(&sfspec,psfspec); /* Record filenames for WHERE */ makestr(&srfspec,psrfspec); } if (moving) { /* If MOVE'ing */ x = zdelet(filnam); /* Try to delete the source file */ #ifdef TLOG if (tralog) { if (x > -1) { tlog(F110," deleted",filnam,0); } else { tlog(F110," delete failed:",ck_errstr(),0); } } #endif /* TLOG */ } else if (snd_move) { /* Or move it */ int x; x = zrename(filnam,snd_move); #ifdef TLOG if (tralog) { if (x > -1) { tlog(F110," moved to ",snd_move,0); } else { tlog(F110," move failed:",ck_errstr(),0); } } #endif /* TLOG */ } else if (snd_rename) { /* Or rename it */ char *s = snd_rename; /* Renaming string */ #ifndef NOSPL int y; /* Pass it thru the evaluator */ extern int cmd_quoting; /* for \v(filename) */ if (cmd_quoting) { /* But only if cmd_quoting is on */ y = MAXRP; s = (char *)srvcmd; zzstring(snd_rename,&s,&y); s = (char *)srvcmd; } #endif /* NOSPL */ if (s) if (*s) { int x; x = zrename(filnam,s); #ifdef TLOG if (tralog) { if (x > -1) { tlog(F110," renamed to",s,0); } else { tlog(F110," rename failed:",ck_errstr(),0); } } #endif /* TLOG */ #ifdef COMMENT *s = NUL; #endif /* COMMENT */ } } } if (czseen) { /* Check group interruption flag */ g = 0; /* No more files if interrupted */ } else { /* Otherwise... */ #ifdef COMMENT /* This code makes any open error fatal to a file-group transfer. */ g = gnfile(); debug(F111,"Y gnfile",filnam,g); if (g > 0) { /* Any more files to send? */ if (sfile(xflg)) /* Yes, try to send next file header */ BEGIN ssfile; /* if ok, enter send-file state */ else { /* otherwise */ s = xflg ? "Can't execute command" : (char *)epktmsg; if (!*s) s = "Can't open file"; errpkt((CHAR *)s); /* send error message */ RESUME; /* and quit */ } } else { /* No next file */ tsecs = gtimer(); /* get statistics timers */ #ifdef GFTIMER fptsecs = gftimer(); #endif /* GFTIMER */ seot(); /* send EOT packet */ BEGIN sseot; /* enter send-eot state */ } #else /* COMMENT */ while (1) { /* Keep trying... */ g = gnfile(); /* Get next file */ debug(F111,"Y gnfile",filnam,g); if (g == 0 && gnferror == 0) /* No more, stop trying */ break; if (g > 0) { /* Have one */ if (sfile(xflg)) { /* Try to open and send F packet */ BEGIN ssfile; /* If OK, enter send-file state */ break; /* and break out of loop. */ } } /* Otherwise keep trying to get one we can send... */ } } if (g == 0) { debug(F101,"Y no more files","",czseen); tsecs = gtimer(); /* Get statistics timers */ #ifdef GFTIMER fptsecs = gftimer(); #endif /* GFTIMER */ seot(); /* Send EOT packet */ BEGIN sseot; /* Enter send-eot state */ } #endif /* COMMENT */ } Y { /* Got ACK to EOT */ debug(F101,"sseot justone","",justone); RESUME; /* All done, just quit */ } E { /* Got Error packet, in any state */ char *s = ""; window(1); /* Close window */ timint = s_timint; /* Restore original timeout */ if (*epktmsg) /* Message from Error packet */ s = (char *)epktmsg; if (!*s) { /* If not there then maybe here */ s = (char *)rdatap; ckstrncpy((char *)epktmsg,(char *)rdatap,PKTMSGLEN); } if (!*s) /* Hopefully we'll never see this. */ s = "Unknown error"; success = 0; /* For IF SUCCESS/FAIL. */ debug(F101,"ckcpro.w justone at E pkt","",justone); success = 0; /* Transfer failed */ xferstat = success; /* Remember transfer status */ if (!epktsent) { x = quiet; quiet = 1; /* Close files silently, */ epktrcvd = 1; /* Prevent messages from clsof() */ clsif(); clsof(1); /* discarding any output file. */ ermsg(s); /* Issue the message (calls screen). */ quiet = x; /* Restore quiet state */ } tstats(); /* Get stats */ /* If we are executing commands from a command file or macro, let the command file or macro decide whether to exit, based on SET { TAKE, MACRO } ERROR. */ if ( #ifndef NOICP !xcmdsrc && #endif /* NOICP */ backgrd && !server) fatal("Protocol error"); xitsta |= (what & W_KERMIT); /* Save this for doexit(). */ #ifdef CK_TMPDIR /* If we were cd'd temporarily to another device or directory ... */ if (f_tmpdir) { int x; x = zchdir((char *) savdir); /* ... restore previous directory */ f_tmpdir = 0; /* and remember we did it. */ debug(F111,"ckcpro.w E tmpdir restored",savdir,x); } #endif /* CK_TMPDIR */ #ifdef IKSDB if (ikdbopen) slotstate(what,"ERROR", (char *)epktmsg, ""); #endif /* IKSDB */ RESUME; } q { success = 0; QUIT; } /* Ctrl-C or connection loss. */ . { /* Anything not accounted for above */ errpkt((CHAR *)"Unexpected packet type"); /* Give error message */ window(1); xitsta |= (what & W_KERMIT); /* Save this for doexit(). */ RESUME; /* and quit */ } %% /* From here down to proto() are routines that were moved out of the state table switcher because the resulting switch() had become too large. To move the contents of a state-table case to a routine: 1. Add a prototype to the list above the state table switcher. 2. Make a routine with an appropriate name, returning int. 3. Move the code into it. 4. Put a call to the new routine in the former spot: rc = name_of_routine(); if (rc > -1) return(rc); 5. Add "return(-1);" after every RESUME, SERVE, or BEGIN macro and at the end if the code is open-ended. */ static int rcv_firstdata() { extern int dispos; debug(F101,"rcv_firstdata","",dispos); if (discard) { /* if we're discarding the file */ ack1((CHAR *)"X"); /* just ack the data like this. */ cancel++; /* and count it */ BEGIN rdpkt; /* and wait for more data packets. */ return(-1); } else { /* Not discarding. */ rf_err = "Can't open file"; if (xflg) { /* If screen data */ if (remfile) { /* redirected to file */ if (rempipe) /* or pipe */ x = openc(ZOFILE,remdest); /* Pipe: start command */ else x = opena(remdest,&iattr); /* File: open with attributes */ } else { /* otherwise */ x = opent(&iattr); /* "open" the screen */ } } else { /* otherwise */ #ifdef CALIBRATE if (calibrate) { /* If calibration run */ x = ckopenx(&iattr); /* open nothing */ #ifdef STREAMING if (streaming) /* Streaming */ fastack(); /* ACK without ACKing. */ else #endif /* STREAMING */ ack(); /* Send real ACK */ BEGIN rdpkt; /* Proceed to next state */ return(-1); } else #endif /* CALIBRATE */ #ifdef UNIX /* In UNIX we can pipe the file data into the mail program, which is to be preferred to writing it out to a temp file and then mailing it afterwards. This depends rather heavily on all UNIXes having a mail command that accepts '-s "subject"' on the command line. MAILCMD (e.g. mail, Mail, mailx) is defined in ckufio.c. */ if (dispos == 'M') { /* Mail... */ char *s; char * tmp = NULL; int n = 0; extern char *MAILCMD; s = iattr.disp.val + 1; n = (int)strlen(MAILCMD) + /* Mail command */ (int)strlen(s) + /* address */ (int)strlen(ofilnam) + 32; /* subject */ if (tmp = (char *)malloc(n)) { ckmakxmsg(tmp,n, MAILCMD," -s \"",ofilnam,"\" ",s, NULL,NULL,NULL,NULL,NULL,NULL,NULL); debug(F111,"rcv_firsdata mail",tmp,(int)strlen(tmp)); x = openc(ZOFILE,(char *)tmp); free(tmp); } else x = 0; } else if (dispos == 'P') { /* Ditto for print */ char * tmp = NULL; int n; extern char *PRINTCMD; n = (int)strlen(PRINTCMD) + (int)strlen(iattr.disp.val+1) + 4; if (tmp = (char *)malloc(n)) { sprintf(tmp, /* safe (prechecked) */ "%s %s", PRINTCMD, iattr.disp.val + 1); x = openc(ZOFILE,(char *)tmp); free(tmp); } else x = 0; } else #endif /* UNIX */ x = opena(filnam,&iattr); /* open the file, with attributes */ } if (x) { /* If file was opened ok */ int rc, qf; #ifndef NOSPL qf = query; #else qf = 0; #endif /* NOSPL */ #ifdef CKTUNING rc = (binary && !parity) ? bdecode(rdatap,putfil): decode(rdatap, qf ? puttrm : putfil, 1); #else rc = decode(rdatap, qf ? puttrm : putfil, 1); #endif /* CKTUNING */ if (rc < 0) { errpkt((CHAR *)"Error writing data"); RESUME; return(-1); } #ifdef STREAMING if (streaming) /* Streaming was negotiated */ fastack(); /* ACK without ACKing. */ else #endif /* STREAMING */ ack(); /* acknowledge it */ BEGIN rdpkt; /* and switch to receive-data state */ return(-1); } else { /* otherwise */ errpkt((CHAR *) rf_err); /* send error packet */ RESUME; /* and quit. */ return(-1); } } } static int rcv_shortreply() { #ifdef PKTZEROHACK success = 0; debug(F111,"rcv_shortreply",rdatap,ipktlen); if (ipktack[0] && !strncmp(ipktack,(char *)rdatap,ipktlen)) { /* No it's the ACK to the I packet again */ x = scmd(vcmd,(CHAR *)cmarg); /* So send the REMOTE command again */ /* Maybe this should be resend() */ debug(F110,"IPKTZEROHACK",ipktack,x); if (x < 0) { errpkt((CHAR *)srimsg); RESUME; return(-1); } } else { ipktack[0] = NUL; #endif /* PKTZEROHACK */ urserver = 1; #ifndef NOSERVER #ifndef NOSPL if (query) { /* If to query, */ qbufp = querybuf; /* initialize query response buffer */ qbufn = 0; querybuf[0] = NUL; } #endif /* NOSPL */ x = 1; if (remfile) { /* Response redirected to file */ rf_err = "Can't open file"; if (rempipe) /* or pipe */ x = #ifndef NOPUSH zxcmd(ZOFILE,remdest) /* Pipe: Start command */ #else 0 #endif /* NOPUSH */ ; else x = opena(remdest,&iattr); /* File: Open with attributes */ debug(F111,"rcv_shortreply remfile",remdest,x); } else { x = opent(&iattr); /* "open" the screen */ } if (x) { /* If file was opened ok */ if (decode(rdatap, #ifndef NOSPL (query || !remfile) ? puttrm : #else !remfile ? puttrm : #endif /* NOSPL */ zputfil, 1) < 0) { /* Note: zputfil, not putfil. */ errpkt((CHAR *)"Error writing data"); RESUME; return(-1); } else { if (rdatap) /* If we had data */ if (*rdatap) /* add a line terminator */ if (remfile) { /* to file */ zsoutl(ZOFILE,""); } else { /* or to screen. */ #ifndef NOICP if (!query || !xcmdsrc) #endif /* NOICP */ if (!(quiet && rcdactive)) conoll(""); } if (bye_active && network) { /* I sent BYE or REMOTE LOGOUT */ msleep(500); /* command and got the ACK... */ bye_active = 0; ttclos(0); } clsof(0); if (!epktsent && !epktrcvd) /* If no error packet... */ success = 1; /* success. */ RESUME; return(-1); } } else { /* File not opened OK */ errpkt((CHAR *) rf_err); /* send error message */ RESUME; /* and quit. */ return(-1); } #endif /* NOSERVER */ #ifdef PKTZEROHACK } #endif /* PKTZEROHACK */ debug(F101,"rcv_shortreply fallthru","",success); return(-1); } static int srv_query() { #ifndef NOSERVER #ifndef NOSPL char c; #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "REMOTE QUERY", (char *)srvcmd); #endif /* CKSYSLOG */ #ifdef IKSDB if (ikdbopen) slotstate(what,"REMOTE QUERY", (char *)(srvcmd+2), ""); #endif /* IKSDB */ c = *(srvcmd+2); /* Q = Query, S = Set */ if (c == 'Q') { /* Query */ if (!ENABLED(en_que)) { /* Security */ errpkt((CHAR *)"REMOTE QUERY disabled"); RESUME; return(-1); } else { /* Query allowed */ int n; char *p, *q; qbufp = querybuf; /* Wipe out old stuff */ qbufn = 0; querybuf[0] = NUL; p = (char *) srvcmd + 3; /* Pointer for making wrapper */ n = strlen((char *)srvcmd); /* Position of end */ c = *(srvcmd+4); /* Which type of variable */ if (*(srvcmd+6) == CMDQ) { /* Starts with command quote? */ p = (char *) srvcmd + 6; /* Take it literally */ if (*p == CMDQ) p++; } else { /* They played by the rules */ if (c == 'K') { /* Kermit variable */ int k; k = (int) strlen(p); if (k > 0 && p[k-1] == ')') { p = (char *)(srvcmd + 4); *(srvcmd+4) = CMDQ; *(srvcmd+5) = 'f'; /* Function, so make it \f...() */ } else { *(srvcmd+3) = CMDQ; /* Stuff wrapping into buffer */ *(srvcmd+4) = 'v'; /* Variable, so make it \v(...) */ *(srvcmd+5) = '('; /* around variable name */ *(srvcmd+n) = ')'; *(srvcmd+n+1) = NUL; } } else { *(srvcmd+3) = CMDQ; /* Stuff wrapping into buffer */ *(srvcmd+4) = 'v'; /* Variable, so make it \v(...) */ *(srvcmd+5) = '('; /* around variable name */ *(srvcmd+n) = ')'; *(srvcmd+n+1) = NUL; if (c == 'S') { /* System variable */ *(srvcmd+4) = '$'; /* so it's \$(...) */ } else if (c == 'G') { /* Non-\ Global variable */ *(srvcmd+4) = 'm'; /* so wrap it in \m(...) */ } } } /* Now evaluate it */ n = QBUFL; /* Max length */ q = querybuf; /* Where to put it */ if (zzstring(p,&q,&n) < 0) { errpkt((n > 0) ? (CHAR *)"Can't get value" : (CHAR *)"Value too long" ); RESUME; return(-1); } else { if (encstr((CHAR *)querybuf) > -1) { /* Encode it */ ack1(data); /* If it fits, send it back in ACK */ success = 1; RESUME; return(-1); } else if (sndstring(querybuf)) { /* Long form response */ BEGIN ssinit; return(-1); } else { /* sndhlp() fails */ errpkt((CHAR *)"Can't send value"); RESUME; return(-1); } } } } else if (c == 'S') { /* Set (assign) */ if (!ENABLED(en_asg)) { /* Security */ errpkt((CHAR *)"REMOTE ASSIGN disabled"); RESUME; return(-1); } else { /* OK */ int n; n = xunchar(*(srvcmd+3)); /* Length of name */ n = 3 + n + 1; /* Position of length of value */ *(srvcmd+n) = NUL; /* Don't need it */ if (addmac((char *)(srvcmd+4),(char *)(srvcmd+n+1)) < 0) errpkt((CHAR *)"REMOTE ASSIGN failed"); else { ack(); success = 1; } RESUME; return(-1); } } else { errpkt((CHAR *)"Badly formed server command"); RESUME; return(-1); } #else errpkt((CHAR *)"Variable query/set not available"); RESUME; return(-1); #endif /* NOSPL */ #endif /* NOSERVER */ } static int srv_copy() { #ifndef NOSERVER #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "REMOTE COPY", (char *)srvcmd); #endif /* CKSYSLOG */ #ifdef ZCOPY if (!ENABLED(en_cpy)) { errpkt((CHAR *)"REMOTE COPY disabled"); RESUME; return(-1); } else { char *str1, *str2, f1[256], f2[256]; int len1, len2; len1 = xunchar(srvcmd[1]); /* Separate the parameters */ len2 = xunchar(srvcmd[2+len1]); strncpy(f1,(char *)(srvcmd+2),len1); f1[len1] = NUL; strncpy(f2,(char *)(srvcmd+3+len1),len2); f2[len2] = NUL; #ifdef IKSDB if (ikdbopen) slotstate(what,"REMOTE COPY", f1, f2); #endif /* IKSDB */ if (!ENABLED(en_cwd)) { /* If CWD is disabled */ zstrip(f1,&str1); /* and they included a pathname, */ zstrip(f2,&str2); if (strcmp(f1,str1) || strcmp(f2,str2)) { /* Refuse. */ errpkt((CHAR *)"Access denied"); RESUME; /* Remember, this is not a goto! */ return(-1); } } if (state == generic) { /* It's OK to go ahead. */ if (zcopy(f1,f2)) { /* Try */ errpkt((CHAR *)"Can't copy file"); /* give error message */ } else { success = 1; ack(); } RESUME; /* wait for next server command */ return(-1); } } return(-1); #else /* no ZCOPY */ errpkt((CHAR *)"REMOTE COPY not available"); /* give error message */ RESUME; /* wait for next server command */ return(-1); #endif /* ZCOPY */ #endif /* NOSERVER */ } static int srv_rename() { #ifndef NOSERVER #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "REMOTE RENAME", (char *)srvcmd); #endif /* CKSYSLOG */ #ifdef ZRENAME if (!ENABLED(en_ren)) { errpkt((CHAR *)"REMOTE RENAME disabled"); RESUME; return(-1); } else { /* RENAME is enabled */ char *str1, *str2, f1[256], f2[256]; int len1, len2; len1 = xunchar(srvcmd[1]); /* Separate the parameters */ len2 = xunchar(srvcmd[2+len1]); strncpy(f1,(char *)(srvcmd+2),len1); f1[len1] = NUL; strncpy(f2,(char *)(srvcmd+3+len1),len2); f2[len2] = NUL; len2 = xunchar(srvcmd[2+len1]); strncpy(f1,(char *)(srvcmd+2),len1); f1[len1] = NUL; strncpy(f2,(char *)(srvcmd+3+len1),len2); f2[len2] = NUL; #ifdef IKSDB if (ikdbopen) slotstate(what,"REMOTE RENAME", f1, f2); #endif /* IKSDB */ if (!ENABLED(en_cwd)) { /* If CWD is disabled */ zstrip(f1,&str1); /* and they included a pathname, */ zstrip(f2,&str2); if ( strcmp(f1,str1) || strcmp(f2,str2) ) { /* refuse. */ errpkt((CHAR *)"Access denied"); RESUME; /* Remember, this is not a goto! */ return(-1); } } if (state == generic) { /* It's OK to go ahead. */ if (zrename(f1,f2)) { /* Try */ errpkt((CHAR *)"Can't rename file"); /* Give error msg */ } else { success = 1; ack(); } RESUME; /* Wait for next server command */ return(-1); } } return(-1); #else /* no ZRENAME */ /* Give error message */ errpkt((CHAR *)"REMOTE RENAME not available"); RESUME; /* Wait for next server command */ return(-1); #endif /* ZRENAME */ #endif /* NOSERVER */ } static int srv_login() { #ifndef NOSERVER char f1[LOGINLEN+1], f2[LOGINLEN+1], f3[LOGINLEN+1]; CHAR *p; int len, i; debug(F101,"REMOTE LOGIN x_login","",x_login); debug(F101,"REMOTE LOGIN x_logged","",x_logged); f1[0] = NUL; f2[0] = NUL; f3[0] = NUL; len = 0; if (srvcmd[1]) /* First length field */ len = xunchar(srvcmd[1]); /* Separate the parameters */ if (x_login) { /* Login required */ if (x_logged) { /* And already logged in */ if (len > 0) { /* Logging in again */ errpkt((CHAR *)"Already logged in."); } else { /* Logging out */ debug(F101,"REMOTE LOGOUT","",x_logged); #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "REMOTE LOGOUT", NULL); #endif /* CKSYSLOG */ #ifdef IKSDB if (ikdbopen) slotstate(what,"REMOTE LOGOUT", "", ""); #endif /* IKSDB */ tlog(F110,"Logged out",x_user,0); ack1((CHAR *)"Logged out"); success = 1; msleep(500); #ifdef CK_LOGIN x_logged = 0; #ifdef IKSD if (inserver) ckxlogout(); #endif /* IKSD */ #endif /* CK_LOGIN */ } } else { /* Not logged in yet */ debug(F101,"REMOTE LOGIN len","",len); if (len > 0) { /* Have username */ #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "REMOTE LOGIN", NULL); #endif /* CKSYSLOG */ if (len > LOGINLEN) { errpkt((CHAR *)"Username too long"); } p = srvcmd + 2; /* Point to it */ for (i = 0; i < len; i++) /* Copy it */ f1[i] = p[i]; f1[len] = NUL; /* Terminate it */ p += len; /* Point to next length field */ if (*p) { /* If we have one */ len = xunchar(*p++); /* decode it */ if (len > 0 && len <= LOGINLEN) { for (i = 0; i < len; i++) /* Same deal for password */ f2[i] = p[i]; f2[len] = NUL; p += len; /* And account */ if (*p) { len = xunchar(*p++); if (len > 0 && len <= LOGINLEN) { for (i = 0; i < len; i++) f3[i] = p[i]; /* Set but never used */ f3[len] = NUL; /* (because account not used) */ } } } } debug(F101,"REMOTE LOGIN 1","",x_logged); #ifdef IKSD #ifdef CK_LOGIN if (inserver) { /* Log in to system for real */ x_logged = ckxlogin((CHAR *)f1,(CHAR *)f2,NULL,0); debug(F101,"REMOTE LOGIN 2","",x_logged); if (x_logged) { /* Count attempts */ logtries = 0; justone = 1; } else { logtries++; sleep(logtries); } } else #endif /* CK_LOGIN */ #endif /* IKSD */ if (x_user && x_passwd) { /* User and password must match */ if (!strcmp(x_user,f1)) /* SET SERVER LOGIN */ if (!strcmp(x_passwd,f2)) x_logged = 1; debug(F101,"REMOTE LOGIN 3","",x_logged); } else if (x_user) { /* Only username given, no password */ if (!strcmp(x_user,f1)) /* so only username must match */ x_logged = 1; debug(F101,"REMOTE LOGIN 4","",x_logged); } #ifdef CK_LOGIN else { x_logged = ckxlogin((CHAR *)f1,(CHAR *)f2,NULL,0); debug(F101,"REMOTE LOGIN 5","",x_logged); } #endif /* CK_LOGIN */ if (x_logged) { /* Logged in? */ tlog(F110,"Logged in", x_user, 0); if (isguest) ack1((CHAR *)"Logged in as guest - restrictions apply"); else ack1((CHAR *)"Logged in"); success = 1; } else { tlog(F110,"Login failed", f1, 0); errpkt((CHAR *)"Access denied."); #ifdef IKSD #ifdef CK_LOGIN if (inserver && logtries > 2) ckxlogout(); #endif /* CK_LOGIN */ #endif /* IKSD */ } } else { /* LOGOUT */ errpkt((CHAR *)"Logout ignored"); } } } else { /* Login not required */ if (len > 0) errpkt((CHAR *)"Login ignored."); else errpkt((CHAR *)"Logout ignored."); } #endif /* NOSERVER */ RESUME; return(-1); } static int srv_timeout() { /* K95 does this its own way */ if (idletmo) { #ifdef IKSD if (inserver) { printf("\r\nIKSD IDLE TIMEOUT: %d sec\r\n", srvidl); doexit(GOOD_EXIT,xitsta); } #endif /* IKSD */ idletmo = 0; printf("\r\nSERVER IDLE TIMEOUT: %d sec\r\n", srvidl); xitsta |= (what & W_KERMIT); QUIT; } #ifndef NOSERVER else if (fatalio) { /* Connection lost */ #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "Connection lost", NULL); #endif /* CKSYSLOG */ #ifdef IKSDB if (ikdbopen) slotstate(what,"SERVER DISCONNECT",(char *)srvcmd, ""); #endif /* IKSDB */ xitsta |= what; QUIT; } else if (interrupted) { /* Interrupted by hand */ if (!ENABLED(en_fin)) { errpkt((CHAR *)"QUIT disabled"); RESUME; return(-1); } else { if (what == W_SEND || what == W_RECV || what == W_REMO) { success = 0; #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_PR && ckxlogging) cksyslog(SYSLG_PR, 1, "server", "Interrupted", NULL); #endif /* CKSYSLOG */ } else if (what == W_NOTHING && filcnt == 0) { success = 1; } /* Otherwise leave success alone */ xitsta |= (what & W_KERMIT); QUIT; } } else { /* Shouldn't happen */ debug(F100,"SERVER (top) GOT UNEXPECTED 'q'","",0); QUIT; } #endif /* NOSERVER */ } static int rcv_s_pkt() { #ifndef NOSERVER if (state == rgen) urserver = 1; if (/* state == serve && */ x_login && !x_logged) { errpkt((CHAR *)"Login required"); SERVE; } else #endif /* NOSERVER */ if (state == serve && !ENABLED(en_sen)) { /* Not in server mode */ errpkt((CHAR *)"SEND disabled"); /* when SEND is disabled. */ RESUME; return(-1); } else { /* OK to go ahead. */ #ifdef CK_TMPDIR if (dldir && !f_tmpdir) { /* If they have a download directory */ debug(F110,"receive download dir",dldir,0); if (s = zgtdir()) { /* Get current directory */ debug(F110,"receive current dir",s,0); if (zchdir(dldir)) { /* Change to download directory */ debug(F100,"receive zchdir ok","",0); ckstrncpy(savdir,s,TMPDIRLEN); f_tmpdir = 1; /* Remember that we did this */ } else debug(F100,"receive zchdir failed","",0); } } #endif /* CK_TMPDIR */ nakstate = 1; /* Can send NAKs from here. */ rinit(rdatap); /* Set parameters */ if (bctf) { bctu = 3; bctl = 3; } else { bctu = bctr; /* switch to agreed-upon block check */ bctl = (bctu == 4) ? 2 : bctu; /* Set block-check length */ } what = W_RECV; /* Remember we're receiving */ lastxfer = W_RECV; resetc(); /* Reset counters */ rtimer(); /* Reset timer */ #ifdef GFTIMER rftimer(); #endif /* GFTIMER */ streamon(); BEGIN rfile; /* Go into receive-file state */ } return(-1); } /* END OF ROUTINES MOVED OUT OF STATE MACHINE */ /* P R O T O -- Protocol entry function */ static int is_tn = 0; /* It's a Telnet connection */ #ifdef CK_SPEED int f_ctlp = 0; /* Control-character prefix table */ #ifdef COMMENT short s_ctlp[256]; #endif /* COMMENT */ #endif /* CK_SPEED */ /* This is simply a wrapper for the real protocol function just below, that saves any items that might be changed automatically by protocol negotiations and then restores them upon exit from protocol mode. */ VOID proto() { extern int b_save, f_save, c_save, ss_save, slostart, reliable, urclear; #ifndef NOCSETS extern int fcharset, fcs_save, tcharset, tcs_save; #endif /* NOCSETS */ #ifdef PIPESEND extern int pipesend; #endif /* PIPESEND */ #ifndef NOLOCAL #ifdef OS2 extern int cursorena[], cursor_save, term_io; extern BYTE vmode; extern int display_demo; int term_io_save; #endif /* OS2 */ #endif /* NOLOCAL */ #ifdef TNCODE int _u_bin=0, _me_bin = 0; #ifdef IKS_OPTION int /* _u_start=0, */ _me_start = 0; #endif /* IKS_OPTION */ #endif /* TNCODE */ #ifdef PATTERNS int pa_save; int i; #endif /* PATTERNS */ int scan_save; #ifdef PATTERNS pa_save = patterns; #endif /* PATTERNS */ scan_save = filepeek; myjob = sstate; #ifdef CK_LOGIN if (isguest) { /* If user is anonymous */ en_pri = 0; /* disable printing */ en_mai = 0; /* and disable email */ en_del = 0; /* and file deletion */ } #endif /* CK_LOGIN */ #ifndef NOLOCAL #ifdef OS2 cursor_save = cursorena[vmode]; cursorena[vmode] = 0; term_io_save = term_io; term_io = 0; #endif /* OS2 */ #endif /* NOLOCAL */ b_save = binary; /* SET FILE TYPE */ f_save = fncnv; /* SET FILE NAMES */ c_save = bctr; p_save = fnspath; r_save = recursive; s_timint = timint; ss_save = slostart; #ifndef NOCSETS fcs_save = fcharset; tcs_save = tcharset; #endif /* NOCSETS */ #ifdef COMMENT /* Don't do this because then user can never find out what happened. */ #ifdef CK_SPEED for (i = 0; i < 256; i++) s_ctlp[i] = ctlp[i]; f_ctlp = 1; #endif /* CK_SPEED */ #endif /* COMMENT */ if (reliable == SET_ON) slostart = 0; is_tn = (!local && sstelnet) #ifdef TNCODE || (local && network && ttnproto == NP_TELNET) #endif /* TNCODE */ ; #ifdef TNCODE if (is_tn) { if (tn_b_xfer && !(sstelnet || inserver)) { /* Save the current state of Telnet Binary */ _u_bin = TELOPT_U(TELOPT_BINARY); _me_bin = TELOPT_ME(TELOPT_BINARY); /* If either direction is not Binary attempt to negotiate it */ if (!_u_bin && TELOPT_U_MODE(TELOPT_BINARY) != TN_NG_RF) { tn_sopt(DO,TELOPT_BINARY); TELOPT_UNANSWERED_DO(TELOPT_BINARY) = 1; } if (!_me_bin && TELOPT_ME_MODE(TELOPT_BINARY) != TN_NG_RF) { tn_sopt(WILL,TELOPT_BINARY); TELOPT_UNANSWERED_WILL(TELOPT_BINARY) = 1; } if (!(_me_bin && _u_bin)) tn_wait("proto set binary mode"); } #ifdef IKS_OPTION #ifdef CK_XYZ if (protocol != PROTO_K) { /* Non-Kermit protocol selected */ if (TELOPT_U(TELOPT_KERMIT) && TELOPT_SB(TELOPT_KERMIT).kermit.u_start) { iks_wait(KERMIT_REQ_STOP,0); /* Stop the other Server */ /* _u_start = 1; */ } if (TELOPT_ME(TELOPT_KERMIT) && TELOPT_SB(TELOPT_KERMIT).kermit.me_start) { tn_siks(KERMIT_STOP); /* I'm not servering */ TELOPT_SB(TELOPT_KERMIT).kermit.me_start = 0; _me_start = 1; } } else #endif /* CK_XYZ */ if (sstate == 'x' || sstate == 'v') { /* Responding to a request */ if (!inserver && TELOPT_U(TELOPT_KERMIT) && TELOPT_SB(TELOPT_KERMIT).kermit.u_start) { iks_wait(KERMIT_REQ_STOP,0); /* Stop the other Server */ /* _u_start = 1; */ } if (TELOPT_ME(TELOPT_KERMIT) && !TELOPT_SB(TELOPT_KERMIT).kermit.me_start) { tn_siks(KERMIT_START); /* Send Kermit-Server Start */ TELOPT_SB(TELOPT_KERMIT).kermit.me_start = 1; } } else { /* Initiating a request */ if (TELOPT_ME(TELOPT_KERMIT) && TELOPT_SB(TELOPT_KERMIT).kermit.me_start) { tn_siks(KERMIT_STOP); /* I'm not servering */ TELOPT_SB(TELOPT_KERMIT).kermit.me_start = 0; _me_start = 1; } if (TELOPT_U(TELOPT_KERMIT) && !TELOPT_SB(TELOPT_KERMIT).kermit.u_start) { /* Send Req-Server-Start */ if (!iks_wait(KERMIT_REQ_START,0)) { if (sstate != 's') { success = 0; /* Other Kermit refused to serve */ if (local) printf("A Kermit Server is not available\r\n"); debug(F110,"proto()", "A Kermit Server is not available",0); tlog(F110,"IKS client/server failure", "A Kermit Server is not available",0); goto xxprotox; } } } } #endif /* IKS_OPTION */ #ifdef CK_ENCRYPTION if (tn_no_encrypt_xfer && !(sstelnet || inserver)) { ck_tn_enc_stop(); } #endif /* CK_ENCRYPTION */ } #endif /* TNCODE */ if (!xfrint) connoi(); xxproto(); /* Call the real protocol function */ #ifdef IKS_OPTION xxprotox: #endif /* IKS_OPTION */ xferstat = success; /* Remember transfer status */ kactive = 0; #ifdef TNCODE #ifdef CK_ENCRYPTION if (tn_no_encrypt_xfer && !(sstelnet || inserver)) { ck_tn_enc_start(); } #endif /* CK_ENCRYPTION */ #ifdef IKS_OPTION if (TELOPT_ME(TELOPT_KERMIT) && TELOPT_SB(TELOPT_KERMIT).kermit.me_start && !_me_start) { tn_siks(KERMIT_STOP); /* Server is stopped */ TELOPT_SB(TELOPT_KERMIT).kermit.me_start = 0; } #endif /* IKS_OPTION */ if (is_tn && tn_b_xfer && !(sstelnet || inserver)) { /* if we negotiated Binary mode try to reset it */ if (!_u_bin) { /* Check to see if the state changed during the transfer */ if (TELOPT_U(TELOPT_BINARY)) { tn_sopt(DONT,TELOPT_BINARY); TELOPT_UNANSWERED_DONT(TELOPT_BINARY) = 1; } else _u_bin = 1; /* So we don't call tn_wait() */ } if (!_me_bin) { /* Check to see if the state changed during the transfer */ if (TELOPT_ME(TELOPT_BINARY)) { tn_sopt(WONT,TELOPT_BINARY); TELOPT_UNANSWERED_WONT(TELOPT_BINARY) = 1; } else _me_bin = 1; /* So we don't call tn_wait() */ } if (!(_me_bin && _u_bin)) tn_wait("proto reset binary mode"); } #endif /* TNCODE */ #ifdef PATTERNS patterns = pa_save; #endif /* PATTERNS */ filepeek = scan_save; #ifdef STREAMING streaming = 0; /* streamok = 0; */ #endif /* STREAMING */ #ifdef COMMENT #ifdef CK_SPEED for (i = 0; i < 256; i++) ctlp[i] = s_ctlp[i]; f_ctlp = 0; #endif /* CK_SPEED */ #endif /* COMMENT */ urclear = 0; if (!success) { xitsta |= (what & W_KERMIT); tlog(F110," failed:",(char *)epktmsg,0); } debug(F111,"proto xferstat",epktmsg,xferstat); slostart = ss_save; if (s_timint > -1) { /* Because of REMOTE SET */ timint = s_timint; s_timint = -1; } recursive = r_save; fnspath = p_save; if (c_save > -1) { /* Because of REMOTE SET */ bctr = c_save; c_save = -1; } fncnv = f_save; binary = b_save; #ifdef PIPESEND pipesend = 0; /* Next time might not be pipesend */ #endif /* PIPESEND */ #ifndef NOLOCAL #ifdef OS2 cursorena[vmode] = cursor_save; term_io = term_io_save; display_demo = 1; #endif /* OS2 */ #endif /* NOLOCAL */ } static VOID xxproto() { int x; long lx; #ifdef CK_XYZ #ifdef XYZ_INTERNAL _PROTOTYP( int pxyz, (int) ); #endif /* XYZ_INTERNAL */ #endif /* CK_XYZ */ char xss[2]; /* String representation of sstate */ xss[0] = sstate; xss[1] = NUL; s_timint = timint; debug(F101,"xxproto entry justone","",justone); success = 0; retrieve = 0; /* Reset these ... */ reget = 0; opkt = 0; if (local && ttchk() < 0) { /* Giving BYE or FIN */ if (bye_active) { /* but there is no connection */ ttclos(0); success = 1; return; } /* Ditto for any REMOTE command */ if (sstate == 'g' && cmarg ) { if (*cmarg == 'L' || *cmarg == 'F' || *cmarg == 'X') success = 1; else printf("?No connection\r\n"); return; } } /* Set up the communication line for file transfer. */ /* NOTE: All of the xxscreen() calls prior to the wart() invocation */ /* could just as easily be printf's or, for that matter, hints. */ if (local && (speed < 0L) && (network == 0)) { xxscreen(SCR_EM,0,0L,"Sorry, you must 'set speed' first"); return; } x = -1; if (ttopen(ttname,&x,mdmtyp,cdtimo) < 0) { debug(F111,"failed: proto ttopen local",ttname,local); xxscreen(SCR_EM,0,0L,"Can't open line"); return; } if (x > -1) local = x; debug(F111,"proto ttopen local",ttname,local); lx = (local && !network) ? speed : -1; #ifdef NETCONN #ifdef CK_SPEED if (is_tn) { ctlp[(unsigned)255] = ctlp[CR] = 1; if (parity == 'e' || parity == 'm') ctlp[127] = 1; if (flow == FLO_XONX) { /* Also watch out for Xon/Xoff */ ctlp[17] = ctlp[19] = 1; ctlp[17+128] = ctlp[19+128] = 1; } } #endif /* CK_SPEED */ #endif /* NETCONN */ if (ttpkt(lx,flow,parity) < 0) { /* Put line in packet mode, */ xxscreen(SCR_EM,0,0L,"Can't condition line"); return; } if (local && !network && carrier != CAR_OFF) { int x; /* Serial connection */ x = ttgmdm(); /* with carrier checking */ if (x > -1) { if (!(x & BM_DCD)) { debug(F101,"proto ttgmdm","",0); xxscreen(SCR_EM,0,0L,"Carrier required but not detected"); return; } } } /* Send remote side's "receive" or "server" startup string, if any */ if (local && ckindex((char *)xss,"srgcjhk",0,0,1)) { char *s = NULL; if ( #ifdef IKS_OPTION /* Don't send auto-blah string if we know other side is serving */ !TELOPT_U(TELOPT_KERMIT) || !TELOPT_SB(TELOPT_KERMIT).kermit.u_start #else 1 #endif /* IKS_OPTION */ ) { if (sstate == 's') { /* Sending file(s) */ s = binary ? ptab[protocol].h_b_init : ptab[protocol].h_t_init; } else if (protocol == PROTO_K) { /* Command for server */ s = ptab[protocol].h_x_init; } } #ifdef CK_SPEED #ifndef UNPREFIXZERO if (protocol == PROTO_K) /* Because of C-strings... */ ctlp[0] = 1; #endif /* UNPREFIXZERO */ #endif /* CK_SPEED */ if (s) if (*s) { /* If we have a command to send... */ char tmpbuf[356]; int tmpbufsiz = 356; int stuff = -1, stuff2 = -1, len = 0; extern int tnlm; if (sstate == 's') { /* Sending file(s) */ #ifdef CK_XYZ if (protocol == PROTO_X) { char * s2; s2 = cmarg2[0] ? cmarg2 : cmarg; if ((int)strlen(s) + (int)strlen(s2) + 4 < 356) sprintf(tmpbuf, s, s2); else tmpbuf[0] = NUL; } else { #endif /* CK_XYZ */ ckmakmsg(tmpbuf, 356, s, NULL, NULL, NULL); #ifdef CK_XYZ } #endif /* CK_XYZ */ } else { /* Command for server */ ckstrncpy(tmpbuf,s,356); } ckstrncat(tmpbuf, "\015",sizeof(tmpbuf)); if (tnlm) /* TERMINAL NEWLINE ON */ stuff = LF; /* Stuff LF */ #ifdef TNCODE /* TELNET NEWLINE MODE */ if (is_tn) { switch (TELOPT_ME(TELOPT_BINARY) ? tn_b_nlm : tn_nlm) { case TNL_CR: break; case TNL_CRNUL: break; case TNL_CRLF: stuff2 = stuff; stuff = LF; break; } } #endif /* TNCODE */ #ifdef NETCONN #ifdef TCPSOCKET #ifdef RLOGCODE if (network && ttnproto == NP_RLOGIN) { switch (tn_b_nlm) { /* Always BINARY */ case TNL_CR: break; case TNL_CRNUL: stuff2 = stuff; stuff = NUL; break; case TNL_CRLF: stuff2 = stuff; stuff = LF; break; } } #endif /* RLOGCODE */ #endif /* TCPSOCKET */ #endif /* NETCONN */ len = strlen(tmpbuf); if (stuff >= 0 && len < tmpbufsiz - 1) { tmpbuf[len++] = stuff; if (stuff2 >= 0 && len < tmpbufsiz - 1) tmpbuf[len++] = stuff2; tmpbuf[len] = NUL; } ttol((CHAR *)tmpbuf,len); if (protocol == PROTO_K) /* Give remote Kermit time to start */ msleep(400); } } #ifdef CK_XYZ if (protocol != PROTO_K) { /* Non-Kermit protocol selected */ char tmpbuf[356]; int tmpbufsiz = 356; char * s = ""; #ifdef CK_TMPDIR if (sstate == 'v') { /* If receiving and... */ if (dldir && !f_tmpdir) { /* if they have a download directory */ if (s = zgtdir()) { /* Get current directory */ if (zchdir(dldir)) { /* Change to download directory */ ckstrncpy(savdir,s,TMPDIRLEN); f_tmpdir = 1; /* Remember that we did this */ } } } } #endif /* CK_TMPDIR */ #ifdef XYZ_INTERNAL /* Internal */ success = !pxyz(sstate); #else #ifdef CK_REDIR /* External */ switch (sstate) { case 's': /* 'Tis better to SEND... */ s = binary ? ptab[protocol].p_b_scmd : ptab[protocol].p_t_scmd; break; case 'v': /* ... than RECEIVE */ s = binary ? ptab[protocol].p_b_rcmd : ptab[protocol].p_t_rcmd; break; } if (!s) s = ""; if (*s) { if (sstate == 's') { /* Sending */ extern int xfermode; int k = 0, x = 0, b = binary; /* If just one file we can scan it to set the xfer mode. Otherwise it's up to the external protocol program. */ if (patterns && xfermode == XMODE_A && !iswild(fspec)) { extern int nscanfile; k = scanfile(fspec,&x,nscanfile); if (k > -1) { b = (k == FT_BIN) ? XYFT_B : XYFT_T; s = b ? ptab[protocol].p_b_scmd : ptab[protocol].p_t_scmd; } } if ((int)strlen(s) + (int)strlen(fspec) < tmpbufsiz) { sprintf(tmpbuf,s,fspec); /* safe (prechecked) */ tlog(F110,"Sending",fspec,0L); } } else { /* Receiving */ if ((int)strlen(s) + (int)strlen(cmarg2) < tmpbufsiz) { sprintf(tmpbuf,s,cmarg2); /* safe (prechecked) */ tlog(F110,"Receiving",cmarg2,0L); } } tlog(F110," via external protocol:",tmpbuf,0); debug(F110,"ckcpro ttruncmd",tmpbuf,0); success = ttruncmd(tmpbuf); tlog(F110," status:",success ? "OK" : "FAILED", 0); } else { printf("?Sorry, no external protocol defined for %s\r\n", ptab[protocol].p_name ); } #else printf( "Sorry, only Kermit protocol is supported in this version of Kermit\n" ); #endif /* CK_REDIR */ #endif /* XYZ_INTERNAL */ return; } #endif /* CK_XYZ */ #ifdef NTSIGX conraw(); connoi(); #else if (!local) connoi(); /* No console interrupts if remote */ #endif /* NTSIG */ kactive = 1; if (sstate == 'x') { /* If entering server mode, */ extern int howcalled; server = 1; /* set flag, */ debug(F101,"server backgrd","",backgrd); debug(F101,"server quiet","",quiet); debug(F100,"SHOULD NOT SEE THIS IF IN BACKGROUND!","",0); if (howcalled == I_AM_SSHSUB) { /* and issue appropriate message. */ ttol((CHAR *)"KERMIT READY TO SERVE...\015\012",26); } else if (!local) { if (!quiet && !backgrd #ifdef IKS_OPTION && !TELOPT_ME(TELOPT_KERMIT) /* User was told by negotiation */ #endif /* IKS_OPTION */ ) { conoll(srvtxt); conoll("KERMIT READY TO SERVE..."); } } else { conol("Entering server mode on "); conoll(ttname); conoll("Type Ctrl-C to quit."); if (srvdis) intmsg(-1L); #ifdef TCPSOCKET #ifndef NOLISTEN if (network && tcpsrfd > 0) ttol((CHAR *)"KERMIT READY TO SERVE...\015\012",26); #endif /* NOLISTEN */ #endif /* TCPSOCKET */ } } else server = 0; #ifdef VMS if (!quiet && !backgrd) /* So message doesn't overwrite prompt */ conoll(""); if (local) conres(); /* So Ctrl-C will work */ #endif /* VMS */ /* If in remote mode, not shushed, not in background, and at top command level, issue a helpful message telling what to do... */ if (!local && !quiet && !backgrd) { if (sstate == 'v') { conoll("Return to your local Kermit and give a SEND command."); conoll(""); conoll("KERMIT READY TO RECEIVE..."); } else if (sstate == 's') { conoll("Return to your local Kermit and give a RECEIVE command."); conoll(""); conoll("KERMIT READY TO SEND..."); } else if ( sstate == 'g' || sstate == 'r' || sstate == 'h' || sstate == 'j' || sstate == 'c' ) { conoll("Return to your local Kermit and give a SERVER command."); conoll(""); conoll((sstate == 'r' || sstate == 'j' || sstate == 'h') ? "KERMIT READY TO GET..." : "KERMIT READY TO SEND SERVER COMMAND..."); } } #ifdef COMMENT if (!local) sleep(1); #endif /* COMMENT */ /* The 'wart()' function is generated by the wart program. It gets a character from the input() routine and then based on that character and the current state, selects the appropriate action, according to the state table above, which is transformed by the wart program into a big case statement. The function is active for one transaction. */ rtimer(); /* Reset elapsed-time timer */ #ifdef GFTIMER rftimer(); #endif /* GFTIMER */ resetc(); /* & other per-transaction counters. */ debug(F101,"proto calling wart, justone","",justone); wart(); /* Enter the state table switcher. */ /* Note: the following is necessary in case we have just done a remote-mode file transfer, in which case the controlling terminal modes have been changed by ttpkt(). In particular, special characters like Ctrl-C and Ctrl-\ might have been turned off (see ttpkt). So this call to ttres() is essential. IMPORTANT: restore interrupt handlers first, otherwise any terminal interrupts that occur before this is done in the normal place later will cause a crash. */ #ifdef OS2 ttres(); /* Reset the communication device */ #else if (!local) { setint(); /* Arm interrupt handlers FIRST */ msleep(500); ttres(); /* Then restore terminal. */ } #endif /* OS2 */ xxscreen(SCR_TC,0,0L,""); /* Transaction complete */ x = quiet; quiet=1; clsif(); /* Failsafe in case we missed */ clsof(1); /* a case in the state machine. */ quiet = x; if (server) { /* Back from packet protocol. */ if (!quiet && !backgrd #ifdef IKSD && !inserver #endif /* IKSD */ ) { /* Give appropriate message */ conoll(""); conoll("C-Kermit server done"); } server = 0; /* Not a server any more */ } } /* S G E T I N I T -- Handle incoming GET-Class packets */ /* Returns: -1: On error 0: GET packet processed OK - ready to Send. 1: Extended GET processed OK - wait for another. */ static int sgetinit(reget,xget) int reget, xget; { /* Server end of GET command */ char * fs = NULL; /* Pointer to filespec */ int i, n, done = 0; #ifdef PIPESEND extern int usepipes, pipesend; #endif /* PIPESEND */ extern int nolinks; if (!ENABLED(en_get)) { /* Only if not disabled! */ errpkt((CHAR *)"GET disabled"); return(-1); } /* OK to proceed */ nolinks = recursive; filcnt = 0; #ifdef WHATAMI /* If they are alike this was already done in whoarewe() */ debug(F101,"sgetinit whatru","",whatru); if (whatru & WMI_FLAG) { /* Did we get WHATAMI info? */ debug(F101,"sgetinit binary (1)","",binary); #ifdef VMS if (binary != XYFT_I && binary != XYFT_L) #else #ifdef OS2 if (binary != XYFT_L) #endif /* OS2 */ #endif /* VMS */ binary = (whatru & WMI_FMODE) ? /* Yes, set file type */ XYFT_B : XYFT_T; /* automatically */ debug(F101,"sgetinit binary (2)","",binary); if (!wearealike) fncnv = (whatru & WMI_FNAME) ? 1 : 0; /* And name conversion */ } #endif /* WHATAMI */ fs = (char *)srvcmd; srvptr = srvcmd; /* Point to server command buffer */ decode(rdatap,putsrv,0); /* Decode the GET command into it */ /* Accept multiple filespecs */ cmarg2 = ""; /* Don't use cmarg2 */ cmarg = ""; /* Don't use cmarg */ done = 1; /* Only 1 packet needed... */ if (xget) { /* Special decoding for Extended GET */ char L, next, c; /* PLV items */ int len, val; /* More PLV items */ char * p = (char *)srvcmd; /* String to decode */ done = 0; /* Maybe more packets needed */ fs = NULL; /* We don't know the filespec yet */ c = *p++; /* Get first parameter */ while (c) { /* For all parameters... */ debug(F000,"sgetinit c","",c); L = *p++; /* Get length */ if (L >= SP) /* Decode length */ len = xunchar(L); else if (c == '@') { /* Allow missing EOP length field */ len = 0; } else { len = (xunchar(*p++) * 95); len += xunchar(*p++); } debug(F101,"sgetinit len","",len); next = *(p+len); /* Get next parameter */ *(p+len) = NUL; /* Zero it out to terminal value */ debug(F110,"sgetinit p",p,0); switch (c) { /* Do the parameter */ case 'O': /* GET Options */ val = atoi(p); /* Convert to int */ debug(F101,"sgetinit O val","",val); if (val & GOPT_DEL) moving = 1; if (val & GOPT_RES) reget = 1; if (val & GOPT_REC) { recursive = 1; nolinks = 2; if (fnspath == PATH_OFF) fnspath = PATH_REL; } break; case 'M': /* Transfer Mode */ val = atoi(p); debug(F101,"sgetinit M val","",val); if (val < 1) break; patterns = 0; /* Takes precedence over patterns */ filepeek = 0; /* and FILE SCAN */ if (val == GMOD_TXT) binary = XYFT_T; /* Text */ if (val == GMOD_BIN) binary = XYFT_B; /* Binary */ if (val == GMOD_LBL) binary = XYFT_L; /* Labeled */ break; case 'F': /* Filename */ fs = p; debug(F110,"sgetinit filename",fs,0); break; case '@': /* End Of Parameters */ done = 1; debug(F100,"sgetinit EOP","",0); break; default: errpkt((CHAR *)"Unknown GET Parameter"); debug(F100,"sgetinit unknown parameter","",0); return(-1); } p += (len + 1); c = next; } } if (!fs) fs = ""; /* A filename is required */ if (*fs) { havefs = 1; n = 0; /* Check for quoted name */ if ((n = strlen(fs)) > 1) { /* Note: this does not allow for multiple quoted names */ if ((fs[0] == '{' && fs[n-1] == '}') || (fs[0] == '"' && fs[n-1] == '"')) { fs[n-1] = '\0'; fs++; debug(F111,"sgetinit unquoted filename",fs,n); } else n = 0; /* This means no quoting */ } #ifdef PIPESEND debug(F111,"sgetinit",fs,usepipes); if (usepipes && ENABLED(en_hos) && *fs == '!') { cmarg = fs + 1; /* Point past the bang */ *fs = NUL; nfils = -1; pipesend = 1; debug(F111,"sgetinit pipesend",cmarg,pipesend); } if (!pipesend) { /* If it's not a pipe */ #endif /* PIPESEND */ if (n == 0) { /* If the name was not quoted */ #ifndef NOMSEND nfils = fnparse(fs); /* Allow it to be a list of names */ debug(F111,"sgetinit A",fs,nfils); #ifdef COMMENT /* This doesn't work if a GET-PATH is set. */ if (nfils == 1 && !iswild(fs)) { /* Single file */ char * m; if ((x = zchki(fs)) < 0) { /* Check if it's sendable */ switch (x) { case -1: m = "File not found"; break; case -2: m = "Not a regular file"; break; case -3: m = "Read access denied"; break; } errpkt((CHAR *)m); return(-1); } } #endif /* COMMENT */ } else { /* If it was quoted */ #endif /* NOMSEND */ nzxopts = 0; #ifdef UNIXOROSK if (matchdot) nzxopts |= ZX_MATCHDOT; #endif /* UNIXOROSK */ if (recursive) nzxopts |= ZX_RECURSE; /* Treat as a single filespec */ nfils = 0 - nzxpand(fs,nzxopts); debug(F111,"sgetinit B",fs,nfils); cmarg = fs; } #ifdef PIPESEND } #endif /* PIPESEND */ } if (!done) { /* Need more O packets... */ debug(F100,"sgetinit O-Packet TBC","",0); /* To Be Continued */ return(1); } debug(F100,"sgetinit O-Packet done - havefs","",havefs); if (!havefs) { /* Done - make sure we have filename */ errpkt((CHAR *)"GET without filename"); return(-1); } freerpkt(winlo); winlo = 0; /* Back to packet 0 again. */ debug(F101,"sgetinit winlo","",winlo); nakstate = 0; /* Now I'm the sender! */ if (reget) sendmode = SM_RESEND; if (sinit() > 0) { /* Send Send-Init */ #ifdef STREAMING if (!streaming) #endif /* STREAMING */ timint = chktimo(rtimo,timef); /* Switch to per-packet timer */ return(0); /* If successful, switch state */ } else return(-1); /* Else back to server command wait */ } #else /* NOXFER */ #include "ckcdeb.h" VOID proto() { extern int success; success = 0; } #endif /* NOXFER */ ckcsig.h0000644000015300001460000001360211266111272011276 0ustar fdckermit/* C K C S I G . H */ /* Definitions and prototypes for signal handling */ /* Author: Jeffrey E Altman (jaltman@secure-endpoints.com), Secure Endpoints Inc., New York City. Copyright (C) 1985, 2009, Trustees of Columbia University in the City of New York. All rights reserved. See the C-Kermit COPYING.TXT file or the copyright text in the ckcmai.c module for disclaimer and permissions. */ #ifdef OS2 #ifndef NT #ifndef __HEV__ /* INCL_SEMAPHORE may also define HEV */ #define __HEV__ typedef ULONG HEV; /* hev */ typedef HEV *PHEV; #endif /* __HEV__ */ #endif /* NT */ struct _threadinfo { int inuse; int child; int sibling; #ifdef NT HANDLE id; HANDLE handle; HANDLE parent; HANDLE CompletionSem ; HANDLE DieSem ; #else /* NT */ TID id; TID parent; HEV CompletionSem; HEV DieSem; #endif /* NT */ }; #endif /* OS2 */ #ifdef CK_ANSIC typedef SIGTYP (*ck_sigfunc)(void *); typedef SIGTYP (*ck_sighand)(int); #else typedef SIGTYP (*ck_sigfunc)(); typedef SIGTYP (*ck_sighand)(); #endif /* CK_ANSIC */ /* Macros for POSIX vs old-style signal handling. */ #ifdef CK_POSIX_SIG typedef sigjmp_buf ckjmpbuf; #else #ifdef NT #define NOCRYPT #include #ifdef NTASM typedef struct { CONTEXT context; DWORD retcode; } ckjmpbuf; #else /* NTASM */ typedef jmp_buf ckjmpbuf; #endif /* NTASM */ #else typedef jmp_buf ckjmpbuf; #endif #endif /* CK_POSIX_SIG */ /* Suppose you want to pass the address of a jmp_buf bar to a function foo. Since jmp_buf is normally defined (typedef'd) as an array, you would do it like this: foo(bar), where foo = foo(jmp_buf bar). But suppose a jmp_buf is (say) a struct rather than an array. Then you must do foo(&bar) where foo is foo(jmp_buf * bar). This is controlled here in the traditional fashion, by ifdefs. By default, we assume that jmp_buf is an array. Define the symbol JBNOTARRAY if jmp_buf is not an array. */ #ifndef JBNOTARRAY #ifdef NT #define JBNOTARRAY #endif /* NT */ #endif /* JBNOTARRAY */ #ifdef JBNOTARRAY typedef ckjmpbuf * ckjptr; #define ckjaddr(x) & x #define ckjdref(x) * x #ifdef CK_POSIX_SIG #define cksetjmp(x) sigsetjmp(x,1) #define cklongjmp(x,y) siglongjmp(x,y) #else #ifdef NT __inline int ck_ih(void) { extern int TlsIndex; #ifdef NTSIG struct _threadinfo * threadinfo; threadinfo = (struct _threadinfo *) TlsGetValue(TlsIndex); if (threadinfo) { if (WaitAndResetSem(threadinfo->DieSem,0)) { ckThreadDie(threadinfo); return 1; /* This should never execute */ } } #ifdef COMMENT else debug( F100, "ck_ih() threadinfo is NULL","",0); #endif /* COMMENT */ #endif /* NTSIG */ return 0; } #ifdef NTSIG #define cksetjmp(x) setjmp(x) #define cklongjmp(x,y) longjmp(x,y) #else /* NTSIG */ #ifdef NTASM __inline DWORD cksetjmp( ckjptr jmp ) { extern int isinterrupted; jmp->retcode = 0; memset( &jmp->context, 0, sizeof(CONTEXT) ); jmp->context.ContextFlags = CONTEXT_FULL ; if ( !GetThreadContext( GetCurrentThread(), &jmp->context ) ) debug( F101, "cksetjmp GetThreadContext failed","",GetLastError()); debug(F101,"cksetjmp returns","",jmp->retcode); isinterrupted = 0; return (jmp->retcode); } __inline void cklongjmp( ckjptr jmp, int retval ) { extern HANDLE tidCommand; extern int ttyfd, mdmtyp ; extern DWORD CommandID; extern int isinterrupted; connoi(); isinterrupted = 1; jmp->retcode = ( retval ? retval : 1 ); debug(F101,"about to SetThreadContext for thread","", CommandID); debug(F101,"from Thread","",GetCurrentThreadId()); if ( mdmtyp >= 0 ) { PurgeComm( (HANDLE) ttyfd, PURGE_TXABORT | PURGE_RXABORT ); } if (SetThreadContext( tidCommand, &jmp->context )) debug(F100,"cklongjmp SetThreadContext success","",0); else debug(F101,"cklongjmp SetThreadContext failed","",GetLastError()); msleep(50); cmini(1); /* Reset command parser */ putkey(13); /* Stuff a carriage return */ /* PostEventAvailSem(); */ } #else /* NTASM */ void crash( void ) ; #define cksetjmp(x) setjmp(x) __inline void cklongjmp( ckjptr jmp, int retval ) { extern HANDLE tidCommand; extern int ttyfd, mdmtyp; extern DWORD CommandID; CONTEXT context; if ( mdmtyp >= 0 ) { PurgeComm( (HANDLE) ttyfd, PURGE_TXABORT | PURGE_RXABORT ) ; } memset( &context, 0, sizeof(CONTEXT) ); context.ContextFlags = CONTEXT_FULL; if ( !GetThreadContext( tidCommand, &context ) ) debug( F101, "cklongjmp GetThreadContext failed","",GetLastError()); /* Invalidate the instruction pointer */ context.Eip = (unsigned long) crash; debug(F101,"about to SetThreadContext for thread","", CommandID); debug(F101,"from Thread","",GetCurrentThreadId()); if (SetThreadContext( tidCommand, &context )) debug(F100,"cklongjmp SetThreadContext success","",0); else debug(F101,"cklongjmp SetThreadContext failed","",GetLastError()); } #endif /* NTASM */ #endif /* NTSIG */ #else /* NT */ #define cksetjmp(x) setjmp(x) #define cklongjmp(x,y) longjmp(x,y) #endif /* NT */ #endif /* CK_POSIX_SIG */ #else /* jmp_buf is an array */ typedef ckjmpbuf ckjptr; #define ckjaddr(x) x #define ckjdref(x) x #ifdef CK_POSIX_SIG #define cksetjmp(x) sigsetjmp(x,1) #define cklongjmp(x,y) siglongjmp(x,y) #else #define cksetjmp(x) setjmp(x) #define cklongjmp(x,y) longjmp(x,y) #endif /* CK_POSIX_SIG */ #endif /* JBNOTARRAY */ _PROTOTYP( int cc_execute, (ckjptr, ck_sigfunc, ck_sigfunc) ); _PROTOTYP( int alrm_execute, (ckjptr, int /* timo */, ck_sighand /* handler */, ck_sigfunc, ck_sigfunc) ); _PROTOTYP( int cc_alrm_execute, (ckjptr, int /* timo */, ck_sighand /* handler */, ck_sigfunc, ck_sigfunc) ); /* End of ckusig.h */ ckcssl.h0000644000015300001460000001276306771564224011342 0ustar fdckermit#ifdef CK_SSL #ifndef CK_ANSIC #define NOPROTO #endif /* CK_ANSIC */ #include "bio.h" #include "buffer.h" #include "x509.h" #include "pem.h" #include "ssl.h" extern BIO *bio_err; extern SSL *ssl_con; extern SSL_CTX *ssl_ctx; extern int ssl_debug_flag; extern int ssl_only_flag; extern int ssl_active_flag; extern int ssl_verify_flag; extern int ssl_secure_flag; extern int ssl_verbose_flag; extern int ssl_disabled_flag; extern int ssl_cert_required; extern int ssl_certsok_flag; extern int ssl_dummy_flag; extern char *ssl_log_file; extern char *ssl_rsa_cert_file; extern char *ssl_rsa_key_file; extern char *ssl_dsa_cert_file; extern char *ssl_dh_key_file; extern char *ssl_cipher_list; extern SSL_CTX *tls_ctx; extern SSL *tls_con; extern int tls_only_flag; extern int tls_active_flag; extern int tls_secure_flag; _PROTOTYP(int ssl_do_init,(int)); _PROTOTYP(int ssl_display_connect_details,(SSL *,int)); _PROTOTYP(int ssl_server_verify_callback,(int, X509_STORE_CTX *)); _PROTOTYP(int ssl_client_verify_callback,(int, X509_STORE_CTX *)); #ifdef OS2 #define SSL_get_error ck_SSL_get_error #define SSL_read ck_SSL_read #define SSL_peek ck_SSL_peek #define SSL_connect ck_SSL_connect #define SSL_set_fd ck_SSL_set_fd #define SSL_free ck_SSL_free #define SSL_shutdown ck_SSL_shutdown #define SSL_write ck_SSL_write #define SSL_pending ck_SSL_pending #define SSL_load_error_strings ck_SSL_load_error_strings #define SSL_get_peer_certificate ck_SSL_get_peer_certificate #define SSL_CIPHER_get_name ck_SSL_CIPHER_get_name #define SSL_get_current_cipher ck_SSL_get_current_cipher #define SSL_get_shared_ciphers ck_SSL_get_shared_ciphers #define SSL_get_ciphers ck_SSL_get_ciphers #define SSL_get_cipher_list ck_SSL_get_cipher_list #define SSL_CTX_set_default_verify_paths ck_SSL_CTX_set_default_verify_paths #define SSL_use_RSAPrivateKey_file ck_SSL_use_RSAPrivateKey_file #define SSL_use_DSAPrivateKey_file ck_SSL_use_DSAPrivateKey_file #define SSL_use_PrivateKey_file ck_SSL_use_PrivateKey_file #define SSL_use_certificate_file ck_SSL_use_certificate_file #define SSL_CTX_use_PrivateKey_file ck_SSL_CTX_use_PrivateKey_file #define SSL_CTX_use_certificate_file ck_SSL_CTX_use_certificate_file #define SSL_set_verify ck_SSL_set_verify #define SSL_new ck_SSL_new #define SSL_CTX_ctrl ck_SSL_CTX_ctrl #define SSL_CTX_new ck_SSL_CTX_new #define SSL_CTX_free ck_SSL_CTX_free #define SSL_CTX_set_default_passwd_cb ck_SSL_CTX_set_default_passwd_cb #define SSLv23_method ck_SSLv23_method #define SSLv3_method ck_SSLv3_method #define TLSv1_method ck_TLSv1_method #define SSLv23_client_method ck_SSLv23_client_method #define SSLv3_client_method ck_SSLv3_client_method #define TLSv1_client_method ck_TLSv1_client_method #define SSLv23_server_method ck_SSLv23_server_method #define SSLv3_server_method ck_SSLv3_server_method #define TLSv1_server_method ck_TLSv1_server_method #define SSL_library_init ck_SSL_library_init #define SSL_state_string ck_SSL_state_string #define SSL_state_string_long ck_SSL_state_string_long #define SSL_accept ck_SSL_accept #define SSL_set_cipher_list ck_SSL_set_cipher_list #define ERR_print_errors ck_ERR_print_errors #define ERR_print_errors_fp ck_ERR_print_errors_fp #define ERR_error_string ck_ERR_error_string #define ERR_get_error ck_ERR_get_error #define BIO_printf ck_BIO_printf #define BIO_ctrl ck_BIO_ctrl #define BIO_new ck_BIO_new #define BIO_s_file ck_BIO_s_file #define BIO_s_mem ck_BIO_s_mem #define BIO_s_null ck_BIO_s_null #define BIO_read ck_BIO_read #define BIO_new_file ck_BIO_new_file #define BIO_free ck_BIO_free #define X509_get_issuer_name ck_X509_get_issuer_name #define X509_verify_cert_error_string ck_X509_verify_cert_error_string #define X509_NAME_oneline ck_X509_NAME_oneline #define X509_get_subject_name ck_X509_get_subject_name #define X509_STORE_CTX_get_current_cert ck_X509_STORE_CTX_get_current_cert #define X509_get_default_cert_dir ck_X509_get_default_cert_dir #define X509_free ck_X509_free #define RSA_free ck_RSA_free #define RSA_generate_key ck_RSA_generate_key #define DH_new ck_DH_new #define DH_free ck_DH_free #define DH_generate_key ck_DH_generate_key #define DH_generate_parameters ck_DH_generate_parameters #define DSA_free ck_DSA_free #define DSA_generate_key ck_DSA_generate_key #define DSA_generate_parameters ck_DSA_generate_parameters #define PEM_read_bio_DHparams ck_PEM_read_bio_DHparams #define BN_bin2bn ck_BN_bin2bn #endif /* OS2 */ #endif /* CK_SSL */ ckcsym.h0000644000015300001460000000042506750601622011330 0ustar fdckermit/* This file is for use with compilers that don't have the capability to * #define symbols on the C compiler command line. This file must * be #include'd before all other ck*.h files so that the symbols #define'd * here can be used for any subsequent conditional code. */ ckctel.c0000644000015300001460000110126611347747310011310 0ustar fdckermitchar *cktelv = "Telnet support, 9.0.274, 16 Mar 2010"; #define CKCTEL_C int sstelnet = 0; /* Do server-side Telnet negotiation */ /* C K C T E L -- Telnet support */ /* Authors: Telnet protocol by Frank da Cruz and Jeffrey Altman. Telnet Forward X by Jeffrey Altman Telnet START_TLS support by Jeffrey Altman Telnet AUTH and ENCRYPT support by Jeffrey Altman Telnet COMPORT support by Jeffrey Altman Telnet NEW-ENVIRONMENT support by Jeffrey Altman Telnet NAWS support by Frank da Cruz and Jeffrey Altman Telnet TERMTYPE support by Jeffrey Altman Telnet KERMIT support by Jeffrey Altman Other contributions as indicated in the code. Copyright (C) 1985, 2010, Trustees of Columbia University in the City of New York. All rights reserved. See the C-Kermit COPYING.TXT file or the copyright text in the ckcmai.c module for disclaimer and permissions. */ /* NOTE TO CONTRIBUTORS: This file, and all the other shared (ckc and cku) C-Kermit source files, must be compatible with C preprocessors that support only #ifdef, #else, #endif, #define, and #undef. Please do not use #if, logical operators, or other preprocessor features in this module. Also, don't use any ANSI C constructs except within #ifdef CK_ANSIC..#endif. */ #include "ckcsym.h" #include "ckcdeb.h" #ifdef TNCODE #include "ckcker.h" #define TELCMDS /* to define name array */ #define TELOPTS /* to define name array */ #define SLC_NAMES /* to define name array */ #define ENCRYPT_NAMES #define AUTH_NAMES #define TELOPT_STATES #define TELOPT_MODES #define TNC_NAMES #include "ckcnet.h" #include "ckctel.h" #ifdef CK_AUTHENTICATION #include "ckuath.h" #endif /* CK_AUTHENTICATION */ #ifdef CK_SSL #include "ck_ssl.h" #endif /* CK_SSL */ #ifndef NOTERM #ifdef OS2 /* For terminal type name string */ #include "ckuusr.h" #ifndef NT #include #undef COMMENT #else #define isascii __isascii #endif /* NT */ #include "ckocon.h" extern int tt_type, max_tt; extern struct tt_info_rec tt_info[]; #endif /* OS2 */ #endif /* NOTERM */ #ifdef OS2 #include #ifdef NT #include #else /* NT */ #include #endif /* NT */ #include #include "ckcsig.h" #include "ckosyn.h" #endif /* OS2 */ #ifdef CK_NAWS /* Negotiate About Window Size */ #ifdef RLOGCODE _PROTOTYP( int rlog_naws, (void) ); #endif /* RLOGCODE */ #endif /* CK_NAWS */ int tn_init = 0; /* Telnet protocol initialized flag */ int tn_begun = 0; /* Telnet protocol started flag */ static int tn_first = 1; /* First time init flag */ extern int tn_exit; /* Exit on disconnect */ extern int nettype; extern int inserver; /* Running as IKSD */ char *tn_term = NULL; /* Terminal type override */ #ifdef CK_SNDLOC char *tn_loc = NULL; /* Location override */ #endif /* CK_SNDLOC */ int tn_nlm = TNL_CRLF; /* Telnet CR -> CR LF mode */ int tn_b_nlm = TNL_CR; /* Telnet Binary CR RAW mode */ int tn_b_meu = 0; /* Telnet Binary ME means U too */ int tn_b_ume = 0; /* Telnet Binary U means ME too */ int tn_wait_flg = 1; /* Telnet Wait for Negotiations */ int tn_infinite = 0; /* Telnet Bug Infinite-Loop-Check */ int tn_rem_echo = 1; /* We will echo if WILL ECHO */ int tn_b_xfer = 0; /* Telnet Binary for Xfers? */ int tn_sb_bug = 1; /* Telnet BUG - SB w/o WILL or DO */ int tn_auth_krb5_des_bug = 1; /* Telnet BUG - AUTH KRB5 DES */ /* truncates long keys */ int tn_no_encrypt_xfer = 0; /* Turn off Telnet Encrypt? */ int tn_delay_sb = 1; /* Delay SBs until safe */ int tn_auth_how = TN_AUTH_HOW_ANY; int tn_auth_enc = TN_AUTH_ENC_ANY; int tn_deb = 0; /* Telnet Debug mode */ int tn_sfu = 0; /* Microsoft SFU compatibility */ #ifdef CK_FORWARD_X char * tn_fwdx_xauthority = NULL; /* Xauthority File */ int fwdx_no_encrypt = 0; /* Forward-X requires encryption */ #endif /* CK_FORWARD_X */ #ifdef OS2 int ttnum = -1; /* Last Telnet Terminal Type sent */ int ttnumend = 0; /* Has end of list been found */ #endif /* OS2 */ char tn_msg[TN_MSG_LEN]; /* Telnet data can be rather long */ char hexbuf[TN_MSG_LEN]; char tn_msg_out[TN_MSG_LEN]; #ifdef CK_FORWARD_X CHAR fwdx_msg_out[TN_MSG_LEN]; #endif /* CK_FORWARD_X */ /* In order to prevent an infinite telnet negotiation loop we maintain a count of the number of times the same telnet negotiation message is sent. When this count hits MAXTNCNT, we do not send any more of the message. The count is stored in the tncnts[][] array. The tncnts[][] array is indexed by negotiation option (SUPPRESS GO AHEAD, TERMINAL TYPE, NAWS, etc. - see the tnopts[] array) and the four negotiation message types (WILL, WONT, DO, DONT). All telnet negotiations are kept track of in this way. The count for a message is zeroed when the "opposite" message is sent. WILL is the opposite of WONT, and DO is the opposite of DONT. For example sending "WILL SGA" increments tncnts[TELOPT_SGA][0] and zeroes tncnts[TELOPT_SGA][1]. The code that does this is in tn_sopt(). rogersh@fsj.co.jp, 18/3/1995 8/16/1998 - with the recent rewrite of the telnet state machine I don't think this code is necessary anymore. However, it can't do any harm so I am leaving it in. - Jeff 12/28/1998 - all references to tncnts[] must be done with TELOPT_INDEX(opt) because the Telnet option list is no longer contiguous. We also must allocate NTELOPTS + 1 because the TELOPT_INDEX() macro returns NTELOPTS for an invalid option number. */ #define MAXTNCNT 4 /* Permits 4 intermediate telnet firewalls/gateways */ char tncnts[NTELOPTS+1][4]; /* Counts */ char tnopps[4] = { 1,0,3,2 }; /* Opposites */ #ifdef CK_ENVIRONMENT #ifdef CK_FORWARD_X #define TSBUFSIZ 2056 #else /* CK_FORWARD_X */ #define TSBUFSIZ 1024 #endif /* CK_FORWARD_X */ char tn_env_acct[64]; char tn_env_disp[64]; char tn_env_job[64]; char tn_env_prnt[64]; char tn_env_sys[64]; char * tn_env_uservar[8][2]; int tn_env_flg = 1; #else /* CK_ENVIRONMENT */ #define TSBUFSIZ 41 int tn_env_flg = 0; #endif /* CK_ENVIRONMENT */ #ifdef COMMENT /* SIGWINCH handler moved to ckuusx.c */ #ifndef NOSIGWINCH #ifdef CK_NAWS /* Window size business */ #ifdef UNIX #include #endif /* UNIX */ #endif /* CK_NAWS */ #endif /* NOSIGWINCH */ #endif /* COMMENT */ CHAR sb[TSBUFSIZ]; /* Buffer - incoming subnegotiations */ CHAR sb_out[TSBUFSIZ]; /* Buffer - outgoing subnegotiations */ int tn_duplex = 1; /* Local echo */ extern char uidbuf[]; /* User ID buffer */ extern int quiet, ttnet, ttnproto, debses, what, duplex, oldplex, local; extern int seslog, sessft, whyclosed; #ifdef OS2 #ifndef NOTERM extern int tt_rows[], tt_cols[]; extern int tt_status[VNUM]; extern int scrninitialized[]; #endif /* NOTERM */ #else /* OS2 */ extern int tt_rows, tt_cols; /* Everybody has this */ #endif /* OS2 */ extern int cmd_cols, cmd_rows; extern char namecopy[]; extern char myipaddr[]; /* Global copy of my IP address */ #ifndef TELOPT_MACRO int telopt_index(opt) int opt; { if (opt >= 0 && opt <= TELOPT_STDERR) return(opt); else if (opt >= TELOPT_PRAGMA_LOGON && opt <= TELOPT_PRAGMA_HEARTBEAT) return(opt-88); else if (opt == TELOPT_IBM_SAK) return(opt-147); else return(NTELOPTS); } int telopt_ok(opt) int opt; { return((opt >= TELOPT_BINARY && opt <= TELOPT_STDERR) || (opt >= TELOPT_PRAGMA_LOGON && opt <= TELOPT_PRAGMA_HEARTBEAT) || (opt == TELOPT_IBM_SAK)); } CHAR * telopt(opt) int opt; { if (telopt_ok(opt)) return((CHAR *)telopts[telopt_index(opt)]); else return((CHAR *)"UNKNOWN"); } int telopt_mode_ok(opt) int opt; { return((unsigned int)(opt) <= TN_NG_MU); } CHAR * telopt_mode(opt) int opt; { if (telopt_mode_ok(opt)) return((CHAR *)telopt_modes[opt-TN_NG_RF]); else return((CHAR *)"UNKNOWN"); } #endif /* TELOPT_MACRO */ static int tn_outst(notquiet) int notquiet; { int outstanding = 0; int x = 0; #ifdef CK_ENCRYPTION int e = 0; int d = 0; #endif /* CK_ENCRYPTION */ if (tn_wait_flg) { for (x = TELOPT_FIRST; x <= TELOPT_LAST; x++) { if (TELOPT_OK(x)) { if (TELOPT_UNANSWERED_WILL(x)) { if ( notquiet ) printf("?Telnet waiting for response to WILL %s\r\n", TELOPT(x)); debug(F111,"tn_outst","unanswered WILL",x); outstanding = 1; if ( !notquiet ) break; } if (TELOPT_UNANSWERED_DO(x)) { if ( notquiet ) printf("?Telnet waiting for response to DO %s\r\n", TELOPT(x)); debug(F111,"tn_outst","unanswered DO",x); outstanding = 1; if ( !notquiet ) break; } if (TELOPT_UNANSWERED_WONT(x)) { if ( notquiet ) printf("?Telnet waiting for response to WONT %s\r\n", TELOPT(x)); debug(F111,"tn_outst","unanswered WONT",x); outstanding = 1; if ( !notquiet ) break; } if (TELOPT_UNANSWERED_DONT(x)) { if ( notquiet ) printf("?Telnet waiting for response to DONT %s\r\n", TELOPT(x)); debug(F111,"tn_outst","unanswered DONT",x); outstanding = 1; if ( !notquiet ) break; } if (TELOPT_UNANSWERED_SB(x)) { if ( notquiet ) printf("?Telnet waiting for response to SB %s\r\n", TELOPT(x)); debug(F111,"tn_outst","unanswered SB",x); outstanding = 1; if ( !notquiet ) break; } } } #ifdef CK_AUTHENTICATION if (ck_tn_auth_in_progress()) { if (TELOPT_ME(TELOPT_AUTHENTICATION)) { if (notquiet) printf("?Telnet waiting for WILL %s subnegotiation\r\n", TELOPT(TELOPT_AUTHENTICATION)); debug(F111, "tn_outst", "ME authentication in progress", TELOPT_AUTHENTICATION ); outstanding = 1; } else if (TELOPT_U(TELOPT_AUTHENTICATION)) { if (notquiet) printf("?Telnet waiting for DO %s subnegotiation\r\n", TELOPT(TELOPT_AUTHENTICATION)); debug(F111, "tn_outst", "U authentication in progress", TELOPT_AUTHENTICATION ); outstanding = 1; } } #endif /* CK_AUTHENTICATION */ #ifdef CK_ENCRYPTION if (!outstanding) { e = ck_tn_encrypting(); d = ck_tn_decrypting(); if (TELOPT_ME(TELOPT_ENCRYPTION)) { if (TELOPT_SB(TELOPT_ENCRYPTION).encrypt.stop && e || !TELOPT_SB(TELOPT_ENCRYPTION).encrypt.stop && !e ) { if ( notquiet ) printf("?Telnet waiting for WILL %s subnegotiation\r\n", TELOPT(TELOPT_ENCRYPTION)); debug(F111, "tn_outst", "encryption mode switch", TELOPT_ENCRYPTION ); outstanding = 1; } } if (TELOPT_U(TELOPT_ENCRYPTION)) { if (TELOPT_SB(TELOPT_ENCRYPTION).encrypt.stop && d || !TELOPT_SB(TELOPT_ENCRYPTION).encrypt.stop && !d ) { if ( notquiet ) printf("?Telnet waiting for DO %s subnegotiation\r\n", TELOPT(TELOPT_ENCRYPTION)); debug(F111, "tn_outst", "decryption mode switch", TELOPT_ENCRYPTION ); outstanding = 1; } } } #endif /* CK_ENCRYPTION */ } /* if (tn_wait_flg) */ #ifdef IKS_OPTION /* Even if we are not waiting for Telnet options we must wait for */ /* Kermit Telnet Subnegotiations if we have sent a request to the */ /* other guy. Otherwise we will get out of sync. */ if (!outstanding) { if (TELOPT_U(TELOPT_KERMIT) && (TELOPT_SB(TELOPT_KERMIT).kermit.me_req_start || TELOPT_SB(TELOPT_KERMIT).kermit.me_req_stop || !TELOPT_SB(TELOPT_KERMIT).kermit.sop) ) { if ( notquiet ) printf("?Telnet waiting for SB %s negotiation\r\n", TELOPT(TELOPT_KERMIT)); debug(F111,"tn_outst","U kermit in progress",TELOPT_KERMIT); outstanding = 1; } } #endif /* IKS_OPTION */ #ifdef TN_COMPORT if (!outstanding) { if (TELOPT_ME(TELOPT_COMPORT)) { if (TELOPT_SB(TELOPT_COMPORT).comport.wait_for_sb) { if (notquiet) printf("?Telnet waiting for SB %s negotiation\r\n", TELOPT(TELOPT_COMPORT)); debug(F111,"tn_outst","ComPort SB in progress",TELOPT_COMPORT); outstanding = 1; } if (TELOPT_SB(TELOPT_COMPORT).comport.wait_for_ms) { if (notquiet) printf("?Telnet waiting for SB %s MODEM_STATUS negotiation\r\n", TELOPT(TELOPT_COMPORT)); debug(F111,"tn_outst","ComPort SB MS in progress",TELOPT_COMPORT); outstanding = 1; } } } #endif /* TN_COMPORT */ return(outstanding); } int istncomport() { #ifdef TN_COMPORT if (!local) return(0); if (ttnet != NET_TCPB) return(0); if (ttnproto != NP_TELNET) return(0); if (TELOPT_ME(TELOPT_COMPORT)) return(1); else #endif /* TN_COMPORT */ return(0); } /* tn_wait() -- Wait for response to Telnet negotiation. */ /* Wait for up to seconds for the response to arrive. Place all non-telnet data into Telnet Wait Buffer. If response does arrive return 1, else return 0. */ #ifndef TN_WAIT_BUF_SZ #define TN_WAIT_BUF_SZ 4096 #endif /* TN_WAIT_BUF_SZ */ static char tn_wait_buf[TN_WAIT_BUF_SZ]; static int tn_wait_idx = 0; #ifndef TN_TIMEOUT #define TN_TIMEOUT 120 #endif /* TN_TIMEOUT */ static int tn_wait_tmo = TN_TIMEOUT; #ifdef CKSPINNER VOID prtwait(state) int state; { switch (state % 4) { case 0: printf("/"); break; case 1: printf("-"); break; case 2: printf("\\"); break; case 3: printf("|"); break; } } #endif /* CKSPINNER */ static int nflag = 0; int #ifdef CK_ANSIC tn_wait(char * where) #else tn_wait(where) char * where; #endif /* CK_ANSIC */ /* tn_wait */ { extern int ckxech, local; int ch = 0, count = 0; #ifndef NOHINTS int nohintgiven = 1; extern int hints; #endif /* NOHINTS */ int outstanding; #ifdef TN_COMPORT int savcarr; extern int ttcarr; #endif /* TN_COMPORT */ /* if (!IS_TELNET()) return(1); */ rtimer(); debug(F110,"tn_wait waiting for",where,0); tn_wait_tmo = TN_TIMEOUT; debug(F111,"tn_wait","timeout",tn_wait_tmo); outstanding = tn_outst(0); debug(F111,"tn_wait","outstanding",outstanding); debug(F111,"tn_wait","tn_wait_flg",tn_wait_flg); /* The following is meant to be !(||). We only want to return */ /* immediately if both the tn_wait_flg && tn_outst() are false */ if (!(outstanding || tn_wait_flg)) /* If no need to wait */ return(1); /* Don't. */ if (tn_deb || debses) tn_debug(""); #ifdef CKSPINNER if (!sstelnet && !quiet) prtwait(0); #endif /* CKSPINNER */ /* Wait up to TN_TIMEOUT sec for responses to outstanding telnet negs */ do { #ifdef NTSIG ck_ih(); #endif /* NTSIG */ ch = ttinc(1); if (ch == -1) { /* Timed out */ if (!sstelnet && !quiet) { /* Let user know... */ #ifdef CKSPINNER printf("\b"); prtwait(gtimer()); #else if (nflag == 0) { printf(" Negotiations."); nflag++; } if (nflag > 0) { printf("."); nflag++; fflush(stdout); } #endif /* CKSPINNER */ } } else if (ch < -1) { printf("\r\n?Connection closed by peer.\r\n"); if (tn_deb || debses) tn_debug(""); return(-1); } else switch (ch) { case IAC: #ifdef CKSPINNER if (!sstelnet && !quiet) printf("\b"); #endif /* CKSPINNER */ ch = tn_doop((CHAR)(ch & 0xff),inserver?ckxech:duplex,ttinc); #ifdef CKSPINNER if (!sstelnet && !quiet) { prtwait(gtimer()); } #endif /* CKSPINNER */ debug(F101,"tn_wait tn_doop","",ch); switch (ch) { case 1: duplex = 1; /* Turn on echoing */ if (inserver) ckxech = 1; break; case 2: duplex = 0; /* Turn off echoing */ if (inserver) ckxech = 0; break; case 3: tn_wait_buf[tn_wait_idx++] = IAC; break; case 4: /* IKS event */ case 6: /* Logout */ break; case -1: if (!quiet) printf("?Telnet Option negotiation error.\n"); if (tn_deb || debses) tn_debug(""); return(-1); case -2: printf("?Connection closed by peer.\n"); if (tn_deb || debses) tn_debug(""); ttclos(0); return(-2); default: if (ch < 0) { if (tn_deb || debses) tn_debug(""); return(ch); } } /* switch */ break; default: #ifdef CKSPINNER if (!sstelnet && !quiet) { printf("\b"); prtwait(gtimer()); } #endif /* CKSPINNER */ tn_wait_buf[tn_wait_idx++] = (CHAR)(ch & 0xff); } /* switch */ outstanding = tn_outst(0); if ( outstanding && ch != IAC ) { int timer = gtimer(); if ( timer > tn_wait_tmo ) { if (!sstelnet) { printf( "\r\n?Telnet Protocol Timeout - connection closed\r\n"); if (tn_deb || debses) tn_debug( ""); tn_outst(1); } /* if we do not close the connection, then we will block */ /* the next time we hit a wait. and if we don't we will */ /* do the wrong thing if the host sends 0xFF and does */ /* not intend it to be an IAC. */ ttclos(0); whyclosed = WC_TELOPT; return(-1); } #ifndef NOHINTS else if ( hints && timer > 30 && nohintgiven && !inserver ) { #ifdef CKSPINNER printf("\b"); #else /* CKSPINNER */ printf("\r\n"); #endif /* CKSPINNER */ printf("*************************\r\n"); printf("The Telnet %s is not sending required responses.\r\n\r\n", sstelnet?"client":"server"); tn_outst(1); printf("\nYou can continue to wait or you can cancel with Ctrl-C.\r\n"); printf("In case the Telnet server never responds as required,\r\n"); printf("you can try connecting to this host with TELNET /NOWAIT.\r\n"); printf("Use SET HINTS OFF to suppress further hints.\r\n"); printf("*************************\r\n"); nohintgiven = 0; } #endif /* NOHINTS */ } #ifdef TN_COMPORT /* Must disable carrier detect check if we are using Telnet Comport */ savcarr = ttcarr; ttscarr(CAR_OFF); count = ttchk(); ttscarr(savcarr); #else /* TN_COMPORT */ count = ttchk(); #endif /* TN_COMPORT */ } while ((tn_wait_idx < TN_WAIT_BUF_SZ) && (outstanding && count >= 0)); if (tn_wait_idx == TN_WAIT_BUF_SZ) { if (tn_deb || debses) tn_debug(""); return(0); } if (!sstelnet && !quiet) { #ifdef CKSPINNER printf("\b \b"); #else if (nflag > 0) { printf(" (OK)\n"); nflag = -1; } #endif /* CKSPINNER */ } if (tn_deb || debses) tn_debug(""); return(0); } /* Push data from the Telnet Wait Buffer into the I/O Queue */ /* Return 1 on success */ int tn_push() { #ifdef NETLEBUF extern int tt_push_inited; #endif /* NETLEBUF */ /* if (!IS_TELNET()) return(1); */ if (tn_wait_idx) { ckhexdump((CHAR *)"tn_push",tn_wait_buf,tn_wait_idx); #ifdef NETLEBUF if (!tt_push_inited) /* Local handling */ le_init(); le_puts((CHAR *)tn_wait_buf,tn_wait_idx); #else /* External handling... */ #ifdef OS2 /* K95 has its own way */ le_puts((CHAR *)tn_wait_buf,tn_wait_idx); #else #ifdef TTLEBUF /* UNIX, etc */ le_puts((CHAR *)tn_wait_buf,tn_wait_idx); #else /* If you see this message in AOS/VS, OS-9, VOS, etc, you need to copy the #ifdef TTLEBUF..#endif code from ckutio.c to the corresponding places in your ck?tio.c module. */ printf("tn_push called but not implemented - data lost.\n"); #endif /* TTLEBUF */ #endif /* OS2 */ #endif /* NETLEBUF */ tn_wait_idx = 0; } tn_wait_tmo = TN_TIMEOUT; /* Reset wait timer stats */ return(1); } /* T N _ S O P T */ /* Sends a telnet option, avoids loops. Returns 1 if command was sent, 0 if not, -1 on error. */ int tn_sopt(cmd,opt) int cmd, opt; { /* TELNET SEND OPTION */ CHAR buf[5]; char msg[128]; int rc; if (ttnet != NET_TCPB) return(-1); /* Must be TCP/IP */ if (ttnproto != NP_TELNET) return(-1); /* Must be telnet protocol */ if (!TELCMD_OK(cmd)) return(-1); if (TELOPT_OK(opt)) { if (cmd == DO && TELOPT_UNANSWERED_DO(opt)) return(0); if (cmd == WILL && TELOPT_UNANSWERED_WILL(opt)) return(0); if (cmd == DONT && TELOPT_UNANSWERED_DONT(opt)) return(0); if (cmd == WONT && TELOPT_UNANSWERED_WONT(opt)) return(0); } #ifdef CK_SSL if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) { return(0); } #endif /* CK_SSL */ if (cmd == DO && opt == TELOPT_AUTHENTICATION) buf[0] = 0; if (tn_infinite && TELOPT_OK(opt)) { /* See comment above about */ int index = TELOPT_INDEX(opt); /* preventing infinite loops */ int m = cmd - WILL; if (tncnts[index][m] > MAXTNCNT) { #ifdef DEBUG if (tn_deb || debses || deblog) { ckmakmsg(msg,sizeof(msg), "TELNET negotiation loop ", TELCMD(cmd), " ", TELOPT(opt)); debug(F101,msg,"",opt); if (tn_deb || debses) tn_debug(msg); } #endif /* DEBUG */ return(0); } tncnts[index][m]++; tncnts[index][tnopps[m]] = 0; } buf[0] = (CHAR) IAC; buf[1] = (CHAR) (cmd & 0xff); buf[2] = (CHAR) (opt & 0xff); buf[3] = (CHAR) 0; #ifdef DEBUG if ((tn_deb || debses || deblog) && cmd != SB) ckmakmsg(msg,sizeof(msg),"TELNET SENT ",TELCMD(cmd)," ", TELOPT(opt)); #endif /* DEBUG */ #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif debug(F101,msg,"",opt); if ((tn_deb || debses) && cmd != SB) tn_debug(msg); rc = (ttol(buf,3) < 3); #ifdef OS2 ReleaseTelnetMutex(); #endif if (rc) return(-1); if (TELOPT_OK(opt)) { if (cmd == DONT && TELOPT_UNANSWERED_DO(opt)) TELOPT_UNANSWERED_DO(opt) = 0; if (cmd == WONT && TELOPT_UNANSWERED_WILL(opt)) TELOPT_UNANSWERED_WILL(opt) = 0; if (cmd == DO && TELOPT_UNANSWERED_DONT(opt)) TELOPT_UNANSWERED_DONT(opt) = 0; if (cmd == WILL && TELOPT_UNANSWERED_WONT(opt)) TELOPT_UNANSWERED_WONT(opt) = 0; } return(1); } /* Send a telnet sub-option */ /* Returns 1 if command was sent, 0 if not, -1 on error */ int tn_ssbopt(opt,sub,data,len) int opt, sub; CHAR * data; int len; { CHAR buf[256]; int n,m,rc; if (ttnet != NET_TCPB) return(0); /* Must be TCP/IP */ if (ttnproto != NP_TELNET) return(0); /* Must be telnet protocol */ if (!TELOPT_OK(opt)) return(-1); if (len < 0 || len > 250) { debug(F111,"Unable to Send TELNET SB - data too long","len",len); return(-1); /* Data too long */ } #ifdef CK_SSL if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) { if (ttchk() < 0) return(-1); else return(1); } #endif /* CK_SSL */ if (!data) len = 0; buf[0] = (CHAR) IAC; buf[1] = (CHAR) (SB & 0xff); buf[2] = (CHAR) (opt & 0xff); buf[3] = (CHAR) (sub & 0xff); if (data && len > 0) { memcpy(&buf[4],data,len); } buf[4+len] = (CHAR) IAC; buf[5+len] = (CHAR) SE; #ifdef DEBUG if (tn_deb || debses || deblog) { if (opt == TELOPT_START_TLS && sub == 1) ckmakmsg(tn_msg_out,TN_MSG_LEN,"TELNET SENT SB ", TELOPT(opt)," FOLLOWS IAC SE",NULL); else if (opt == TELOPT_TTYPE && sub == 1) ckmakmsg(tn_msg_out,TN_MSG_LEN,"TELNET SENT SB ", TELOPT(opt), " SEND IAC SE",NULL); else if (opt == TELOPT_TTYPE && sub == 0) ckmakxmsg(tn_msg_out,TN_MSG_LEN,"TELNET SENT SB ",TELOPT(opt)," IS ", (char *)data," IAC SE",NULL,NULL,NULL,NULL,NULL,NULL,NULL); else if (opt == TELOPT_NEWENVIRON) { int i, quote; ckmakmsg(tn_msg_out,TN_MSG_LEN,"TELNET SENT SB ", TELOPT(TELOPT_NEWENVIRON)," ", sub == TELQUAL_SEND ? "SEND" : sub == TELQUAL_IS ? "IS" : sub == TELQUAL_INFO ?"INFO" : "UNKNOWN" ); for (i = 0, quote = 0; i < len; i++) { if (quote) { sprintf(hexbuf,"%02x",data[i]); /* safe but ugly */ ckstrncat(tn_msg_out,hexbuf,TN_MSG_LEN); quote = 0; } else { switch (data[i]) { case TEL_ENV_USERVAR: ckstrncat(tn_msg_out," USERVAR ",TN_MSG_LEN); break; case TEL_ENV_VAR: ckstrncat(tn_msg_out," VAR ",TN_MSG_LEN); break; case TEL_ENV_VALUE: ckstrncat(tn_msg_out," VALUE ",TN_MSG_LEN); break; case TEL_ENV_ESC: ckstrncat(tn_msg_out," ESC ",TN_MSG_LEN); quote = 1; break; case IAC: ckstrncat(tn_msg_out," IAC ",TN_MSG_LEN); break; default: sprintf(hexbuf,"%c",data[i]); /* safe but ugly */ ckstrncat(tn_msg_out,hexbuf,TN_MSG_LEN); } } } ckstrncat(tn_msg_out," IAC SE",TN_MSG_LEN); } else { sprintf(hexbuf,"%02x",sub); /* safe but ugly */ ckmakxmsg(tn_msg_out,TN_MSG_LEN, "TELNET SENT SB ",TELOPT(opt), " ", hexbuf, " IAC SE", NULL,NULL,NULL,NULL,NULL,NULL,NULL ); } } #endif /* DEBUG */ #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif /* OS2 */ #ifdef DEBUG debug(F101,tn_msg_out,"",opt); if (tn_deb || debses) tn_debug(tn_msg_out); #endif /* DEBUG */ rc = (ttol(buf,6+len) < 6+len); #ifdef OS2 ReleaseTelnetMutex(); #endif if (rc) return(-1); return(1); } /* tn_flui() -- Processes all waiting data for Telnet commands. All non-Telnet data is to be stored into the Telnet Wait Buffer. Returns 1 on success. */ int tn_flui() { extern int ckxech; int x = 0; /* if (!IS_TELNET()) return(0); */ /* Wait up to 5 sec for responses to outstanding telnet negotiations */ while (x >= 0 && ttchk() > 0 && tn_wait_idx < TN_WAIT_BUF_SZ) { x = ttinc(1); switch (x) { case IAC: x = tn_doop((CHAR)(x & 0xff),inserver?ckxech:duplex,ttinc); debug(F101,"tn_flui tn_doop","",x); switch (x) { case 1: /* Turn on echoing */ duplex = 1; if (inserver) ckxech = 1; break; case 2: /* Turn off echoing */ duplex = 0; if (inserver) ckxech = 0; break; case 3: tn_wait_buf[tn_wait_idx++] = IAC; break; case 4: /* IKS event */ case 6: /* Logout */ break; } break; default: if (x >= 0) tn_wait_buf[tn_wait_idx++] = x; } } return(1); } unsigned char * tn_get_display() { char * disp = NULL; static char tmploc[256]; /* Must compute the DISPLAY string we are going to send to the host */ /* If one is not assigned, do not send a string unless the user has */ /* explicitedly requested we try to send one via X-Display Location */ /* But do not send a string at all if FORWARD_X is in use. */ /* if (!IS_TELNET()) return(0); */ debug(F110,"tn_get_display() myipaddr",myipaddr,0); #ifdef CK_ENVIRONMENT debug(F110,"tn_get_display() tn_env_disp",tn_env_disp,0); if (tn_env_disp[0]) { int colon = ckindex(":",tn_env_disp,0,0,1); if ( !colon ) { ckmakmsg(tmploc,256,myipaddr,":",tn_env_disp,NULL); disp = tmploc; } else if ( ckindex("localhost:",tn_env_disp,0,0,0) || ckindex("unix:",tn_env_disp,0,0,0) || ckindex("127.0.0.1:",tn_env_disp,0,0,0) || !ckstrcmp("0:",tn_env_disp,2,1) || tn_env_disp[0] == ':' ) { ckmakmsg(tmploc,256,myipaddr,":",&tn_env_disp[colon],NULL); disp = tmploc; } else disp = tn_env_disp; } else #endif /* CK_ENVIRONMENT */ if (TELOPT_ME(TELOPT_XDISPLOC) || TELOPT_U(TELOPT_FORWARD_X)) { ckmakmsg(tmploc,256,myipaddr,":0.0",NULL,NULL); disp = tmploc; } debug(F110,"tn_get_display() returns",disp,0); return((CHAR *)disp); } #ifdef CK_FORWARD_X static Xauth fake_xauth = {0,0,NULL,0,NULL,0,NULL,0,NULL}; static Xauth *real_xauth=NULL; /* * Author: Jim Fulton, MIT X Consortium * * fwdx_parse_displayname - * display a display string up into its component parts */ #ifdef UNIX #define UNIX_CONNECTION "unix" #define UNIX_CONNECTION_LENGTH 4 #endif /* * private utility routines */ static int #ifdef CK_ANSIC XmuGetHostname (char *buf, int maxlen) #else XmuGetHostname (buf, maxlen) char *buf; int maxlen; #endif /* CK_ANSIC */ { int len; #ifdef NEED_UTSNAME /* * same host name crock as in server and xinit. */ struct utsname name; uname (&name); len = strlen (name.nodename); if (len >= maxlen) len = maxlen - 1; strncpy (buf, name.nodename, len); buf[len] = '\0'; #else buf[0] = '\0'; (void) gethostname (buf, maxlen); buf [maxlen - 1] = '\0'; len = strlen(buf); #endif /* hpux */ return len; } static char * #ifdef CK_ANSIC copystring (char *src, int len) #else copystring (src, len) char *src; int len; #endif /* CK_ANSIC */ { char *cp; if (!src && len != 0) return NULL; cp = malloc (len + 1); if (cp) { if (src) strncpy (cp, src, len); cp[len] = '\0'; } return cp; } static char * #ifdef CK_ANSIC get_local_hostname (char *buf, int maxlen) #else get_local_hostname (buf, maxlen) char *buf; int maxlen; #endif { buf[0] = '\0'; (void) XmuGetHostname (buf, maxlen); return (buf[0] ? buf : NULL); } #ifndef UNIX static char * copyhostname () { char buf[256]; return (get_local_hostname (buf, sizeof buf) ? copystring (buf, strlen (buf)) : NULL); } #endif int #ifdef CK_ANSIC fwdx_parse_displayname (char *displayname, int *familyp, char **hostp, int *dpynump, int *scrnump, char **restp) #else fwdx_parse_displayname (displayname, familyp, hostp, dpynump, scrnump, restp) char *displayname; int *familyp; /* return */ char **hostp; /* return */ int *dpynump, *scrnump; /* return */ char **restp; /* return */ #endif /* CK_ANSIC */ { char *ptr; /* work variables */ int len; /* work variable */ int family = -1; /* value to be returned */ char *host = NULL; /* must free if set and error return */ int dpynum = -1; /* value to be returned */ int scrnum = 0; /* value to be returned */ char *rest = NULL; /* must free if set and error return */ int dnet = 0; /* if 1 then using DECnet */ /* check the name */ if (!displayname || !displayname[0]) return 0; /* must have at least :number */ ptr = (char *)strchr(displayname, ':'); if (!ptr || !ptr[1]) return 0; if (ptr[1] == ':') { if (ptr[2] == '\0') return 0; dnet = 1; } /* * get the host string; if none is given, use the most effiecient path */ len = (ptr - displayname); /* length of host name */ if (len == 0) { /* choose most efficient path */ #ifdef UNIX host = copystring (UNIX_CONNECTION, UNIX_CONNECTION_LENGTH); family = FamilyLocal; #else if (dnet) { host = copystring ("0", 1); family = FamilyDECnet; } else { host = copyhostname (); family = FamilyInternet; } #endif } else { host = copystring (displayname, len); if (dnet) { family = dnet; } else { #ifdef UNIX if (host && strcmp (host, UNIX_CONNECTION) == 0) family = FamilyLocal; else #endif family = FamilyInternet; } } if (!host) return 0; /* * get the display number; we know that there is something after the * colon (or colons) from above. note that host is now set and must * be freed if there is an error. */ if (dnet) ptr++; /* skip the extra DECnet colon */ ptr++; /* move to start of display num */ { register char *cp; for (cp = ptr; *cp && isascii(*cp) && isdigit(*cp); cp++) ; len = (cp - ptr); /* check present and valid follow */ if (len == 0 || (*cp && *cp != '.')) { free (host); return 0; } dpynum = atoi (ptr); /* it will handle num. as well */ ptr = cp; } /* * now get screen number if given; ptr may point to nul at this point */ if (ptr[0] == '.') { register char *cp; ptr++; for (cp = ptr; *cp && isascii(*cp) && isdigit(*cp); cp++) ; len = (cp - ptr); if (len == 0 || (*cp && *cp != '.')) { /* all prop name */ free (host); return 0; } scrnum = atoi (ptr); /* it will handle num. as well */ ptr = cp; } /* * and finally, get any additional stuff that might be following the * the screen number; ptr must point to a period if there is anything */ if (ptr[0] == '.') { ptr++; len = strlen (ptr); if (len > 0) { rest = copystring (ptr, len); if (!rest) { free (host); return 1; } } } /* * and we are done! */ if ( familyp ) *familyp = family; if ( hostp ) *hostp = host; else free(host); if ( dpynump ) *dpynump = dpynum; if ( scrnump ) *scrnump = scrnum; if ( restp ) *restp = rest; else free(rest); return 1; } int #ifdef CK_ANSIC fwdx_tn_sb( unsigned char * sb, int n ) #else fwdx_tn_sb( sb, n ) unsigned char * sb; int n; #endif /* CK_ANSIC */ { unsigned short hchannel, nchannel; unsigned char * p; int i; int rc = -1; /* check to ensure we have negotiated Forward X */ if ( sstelnet && !TELOPT_ME(TELOPT_FORWARD_X) || !sstelnet && !TELOPT_U(TELOPT_FORWARD_X) ) { debug(F100,"fwdx_tn_sb() not negotiated","",0); return(0); } #ifdef CK_SSL if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) { return(0); } #endif /* CK_SSL */ switch (sb[0]) { case FWDX_SCREEN: if (sstelnet && n == 4) rc = fwdx_create_listen_socket(sb[1]); break; case FWDX_OPEN: if ( !sstelnet && n >= 5 ) { p = (unsigned char *) &nchannel; i = 1; /* IAC quoting has been stripped in tn_sb() */ p[0] = sb[i++]; p[1] = sb[i++]; hchannel = ntohs(nchannel); rc = fwdx_open_client_channel(hchannel); if ( rc < 0 ) { /* Failed; Send CLOSE channel */ fwdx_send_close(hchannel); rc = 0; /* Do not consider this to be a telnet error */ } #ifdef NT if ( !TELOPT_SB(TELOPT_FORWARD_X).forward_x.thread_started ) { ckThreadBegin( &fwdx_thread,32655, 0, FALSE, 0 ) ; TELOPT_SB(TELOPT_FORWARD_X).forward_x.thread_started = 1; } #endif /* NT */ } break; case FWDX_CLOSE: p = (unsigned char *) &nchannel; i = 1; /* IAC quoting has been stripped in tn_sb() */ p[0] = sb[i++]; p[1] = sb[i++]; hchannel = ntohs(nchannel); fwdx_close_channel(hchannel); rc = 0; /* no errors when closing */ break; case FWDX_DATA: p = (unsigned char *) &nchannel; i = 1; /* IAC quoting has been stripped in tn_sb() */ p[0] = sb[i++]; p[1] = sb[i++]; hchannel = ntohs(nchannel); rc = fwdx_send_xauth_to_xserver(hchannel,(CHAR *)&sb[3],n-5); if ( rc >= 0 && n-5-rc > 0) { rc = fwdx_write_data_to_channel(hchannel,(char *)&sb[3+rc],n-5-rc); if ( rc < 0 ) { /* Failed; Send CLOSE channel */ rc = fwdx_send_close(hchannel); } } break; case FWDX_OPTIONS: if ( sstelnet ) { #ifndef FWDX_SERVER rc = 0; #else rc = fwdx_server_accept_options((char*)&sb[2],n-3); #endif } else { rc = fwdx_client_reply_options((char *)&sb[2],n-3); if ( rc >= 0 ) { rc = tn_sndfwdx(); } } break; case FWDX_OPT_DATA: switch ( sb[1] ) { default: rc = 0; /* we don't recognize, not an error */ } break; case FWDX_XOFF: case FWDX_XON: if ( !sstelnet ) { p = (unsigned char *) &nchannel; i = 1; /* IAC quoting has been stripped in tn_sb() */ p[0] = sb[i++]; p[1] = sb[i++]; hchannel = ntohs(nchannel); TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[hchannel].suspend = (sb[0] == FWDX_XOFF); rc = 0; } break; } return(rc < 0 ? -1 : 0); } int #ifdef CK_ANSIC fwdx_send_xauth_to_xserver(int channel, unsigned char * data, int len) #else fwdx_send_xauth_to_xserver(channel, data, len) int channel; unsigned char * data; int len; #endif /* CK_ANSIC */ { int name_len, data_len, i; for (i = 0; i < MAXFWDX ; i++) { if (TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[i].id == channel) break; } if ( i == MAXFWDX ) goto auth_err; if (!TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[i].need_to_send_xauth) return(0); if (len < 12) goto auth_err; /* Parse the lengths of variable-length fields. */ if (data[0] == 0x42) { /* byte order MSB first. */ /* Xauth packets appear to always have this format */ if ( data[1] != 0x00 || data[2] != 0x00 || data[3] != 0x0B || data[4] != 0x00 || data[5] != 0x00 ) goto auth_err; name_len = (data[6] << 8) + data[7]; data_len = (data[8] << 8) + data[9]; } else if (data[0] == 0x6c) { /* Byte order LSB first. */ /* Xauth packets appear to always have this format */ if ( data[1] != 0x00 || data[2] != 0x0B || data[3] != 0x00 || data[4] != 0x00 || data[5] != 0x00 ) goto auth_err; name_len = data[6] + (data[7] << 8); data_len = data[8] + (data[9] << 8); } else { /* bad byte order byte */ goto auth_err; } /* Check if the whole packet is in buffer. */ if (len < 12 + ((name_len + 3) & ~3) + ((data_len + 3) & ~3)) goto auth_err; /* If the Telnet Server allows a real Xauth message to be sent */ /* Then let the message be processed by the Xserver. */ if (name_len + data_len > 0) { TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[i].need_to_send_xauth = 0; return(0); } else /* If an empty Xauth message was received. We are going to */ /* send our own Xauth message using the real Xauth data. And */ /* then send any other data in the buffer. */ { int c, err, dpynum, scrnum, family, sb_len; char *display, *host = NULL, *rest = NULL; unsigned char *sb, *p; /* parse the local DISPLAY env var */ display = getenv("DISPLAY"); if ( !display ) display = "127.0.0.1:0.0"; if (fwdx_parse_displayname(display, &family, &host, &dpynum, &scrnum, &rest)) { char * disp_no = ckitoa(dpynum); /* should be unsigned */ if (family == FamilyLocal) { /* call with address = "" */ char address[300] = "localhost"; gethostname(address, sizeof(address) - 1); real_xauth = XauGetAuthByAddr(family, strlen(address), address, strlen(disp_no), disp_no, 0, NULL); } else if (family == FamilyInternet) { /* call with address = 4 bytes numeric ip addr (MSB) */ struct hostent *hi; if (hi = gethostbyname(host)) real_xauth = XauGetAuthByAddr(family, 4, hi->h_addr, strlen(disp_no), disp_no, 0, NULL); } } if (host) free(host); if (rest) free(rest); if (!real_xauth) { TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[i].need_to_send_xauth = 0; return(0); } if (!strncmp(real_xauth->name, "MIT-MAGIC-COOKIE-1", real_xauth->name_length)) { char msg[64]; name_len = real_xauth->name_length; data_len = 16; if ( data[0] == 0x42 ) { msg[0] = 0x42; /* MSB order */ msg[1] = msg[2] = 0; msg[3] = 0x0B; msg[4] = msg[5] = 0; msg[6] = (name_len >> 8); msg[7] = (name_len & 0xFF); msg[8] = (data_len >> 8); msg[9] = (data_len & 0xFF); } else { msg[0] = 0x6c; /* LSB order */ msg[1] = 0; msg[2] = 0x0B; msg[3] = msg[4] = msg[5] = 0; msg[6] = (name_len & 0xFF); msg[7] = (name_len >> 8); msg[8] = (data_len & 0xFF); msg[9] = (data_len >> 8); } msg[10] = msg[11] = 0; memcpy(&msg[12],real_xauth->name,18); msg[30] = msg[31] = 0; memcpy(&msg[32],real_xauth->data,16); if (fwdx_write_data_to_channel(channel,(char *)msg,48) < 0) { TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[i].need_to_send_xauth = 0; return(-1); } else { TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[i].need_to_send_xauth = 0; return(12); } } else { TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[i].need_to_send_xauth = 0; return(0); /* we do not know how to handle this type yet */ } } auth_err: debug(F100,"fwdx_send_xauth_to_xserver error","",0); return(-1); } #ifdef COMMENT int #ifdef CK_ANSIC fwdx_authorize_channel(int channel, unsigned char * data, int len) #else fwdx_authorize_channel(channel, data, len) int channel; unsigned char * data; int len; #endif /* CK_ANSIC */ { /* XXX maybe we should have some retry handling if not the whole first * authorization packet arrives complete */ if ( !TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[channel].authorized ) { int name_len, data_len; if (len < 12) goto auth_err; /* Parse the lengths of variable-length fields. */ if (data[0] == 0x42) { /* byte order MSB first. */ /* Xauth packets appear to always have this format */ if ( data[1] != 0x00 || data[2] != 0x00 || data[3] != 0x0B || data[4] != 0x00 || data[5] != 0x00 ) goto auth_err; name_len = (data[6] << 8) + data[7]; data_len = (data[8] << 8) + data[9]; } else if (data[0] == 0x6c) { /* Byte order LSB first. */ /* Xauth packets appear to always have this format */ if ( data[1] != 0x00 || data[2] != 0x0B || data[3] != 0x00 || data[4] != 0x00 || data[5] != 0x00 ) goto auth_err; name_len = data[6] + (data[7] << 8); data_len = data[8] + (data[9] << 8); } else { /* bad byte order byte */ goto auth_err; } /* Check if authentication protocol matches. */ if (name_len != fake_xauth.name_length || memcmp(data + 12, fake_xauth.name, name_len) != 0) { /* connection uses different authentication protocol */ goto auth_err; } /* Check if authentication data matches our fake data. */ if (data_len != fake_xauth.data_length || memcmp(data + 12 + ((name_len + 3) & ~3), fake_xauth.data, fake_xauth.data_length) != 0) { /* auth data does not match fake data */ goto auth_err; } /* substitute the fake data with real data if we have any */ if (real_xauth && real_xauth->data) memcpy(data + 12 + ((name_len + 3) & ~3), real_xauth->data, data_len); TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[channel].authorized = 1; } return(0); auth_err: return(-1); } #endif /* COMMENT */ int #ifdef CK_ANSIC fwdx_send_close(int channel) #else fwdx_send_close(channel) int channel; #endif /* CK_ANSIC */ { unsigned short nchannel; int i,rc; CHAR * p; #ifdef CK_SSL if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) { return(0); } #endif /* CK_SSL */ nchannel = htons(channel); p = (unsigned char *) &nchannel; i = 0; sb_out[i++] = (CHAR) IAC; /* I Am a Command */ sb_out[i++] = (CHAR) SB; /* Subnegotiation */ sb_out[i++] = TELOPT_FORWARD_X; /* Forward X */ sb_out[i++] = FWDX_CLOSE; /* Open */ sb_out[i++] = p[0]; /* First Byte of Channel */ if ( p[0] == IAC ) sb_out[i++] = IAC; sb_out[i++] = p[1]; /* Second Byte of Channel */ if ( p[1] == IAC ) sb_out[i++] = IAC; sb_out[i++] = (CHAR) IAC; /* End of Subnegotiation */ sb_out[i++] = (CHAR) SE; /* marked by IAC SE */ #ifdef DEBUG if (deblog || tn_deb || debses) { ckmakxmsg((char *)fwdx_msg_out,TN_MSG_LEN,"TELNET SENT SB ", TELOPT(TELOPT_FORWARD_X), " CLOSE CHANNEL=",ckitoa(channel)," IAC SE", NULL,NULL,NULL,NULL,NULL,NULL,NULL ); } #endif /* DEBUG */ #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif #ifdef DEBUG debug(F100,(char *)fwdx_msg_out,"",0); if (tn_deb || debses) tn_debug((char *)fwdx_msg_out); #endif /* DEBUG */ rc = (ttol((CHAR *)sb_out,i) < 0); /* Send it. */ #ifdef OS2 ReleaseTelnetMutex(); #endif if (rc) return(-1); return(0); } int #ifdef CK_ANSIC fwdx_send_open(int channel) #else /* CK_ANSIC */ fwdx_send_open(channel) int channel; #endif /* CK_ANSIC */ { unsigned short nchannel; int i, rc; CHAR * p; #ifdef CK_SSL if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) { return(0); } #endif /* CK_SSL */ nchannel = htons(channel); p = (unsigned char *) &nchannel; i = 0; sb_out[i++] = (CHAR) IAC; /* I Am a Command */ sb_out[i++] = (CHAR) SB; /* Subnegotiation */ sb_out[i++] = TELOPT_FORWARD_X; /* Forward X */ sb_out[i++] = FWDX_OPEN; /* Open */ sb_out[i++] = p[0]; /* First Byte of Channel */ if ( p[0] == IAC ) sb_out[i++] = IAC; sb_out[i++] = p[1]; /* Second Byte of Channel */ if ( p[1] == IAC ) sb_out[i++] = IAC; sb_out[i++] = (CHAR) IAC; /* End of Subnegotiation */ sb_out[i++] = (CHAR) SE; /* marked by IAC SE */ #ifdef DEBUG if (deblog || tn_deb || debses) { ckmakxmsg((char *)fwdx_msg_out,TN_MSG_LEN,"TELNET SENT SB ", TELOPT(TELOPT_FORWARD_X), " OPEN CHANNEL=",ckitoa(channel)," IAC SE", NULL,NULL,NULL,NULL,NULL,NULL,NULL); } #endif /* DEBUG */ #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif #ifdef DEBUG debug(F100,(char *)fwdx_msg_out,"",0); if (tn_deb || debses) tn_debug((char *)fwdx_msg_out); #endif /* DEBUG */ rc = (ttol((CHAR *)sb_out,i) < 0); /* Send it. */ #ifdef OS2 ReleaseTelnetMutex(); #endif if (rc) return(-1); return(0); } int #ifdef CK_ANSIC fwdx_client_reply_options(char *opts, int n) #else fwdx_client_reply_options(opts, n) char *opts; int n; #endif /* CK_ANSIC */ { int i,j,rc; #ifdef CK_SSL if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) { return(0); } #endif /* CK_SSL */ i = 0; sb_out[i++] = (CHAR) IAC; /* I Am a Command */ sb_out[i++] = (CHAR) SB; /* Subnegotiation */ sb_out[i++] = TELOPT_FORWARD_X; /* Forward X */ sb_out[i++] = FWDX_OPTIONS; /* Options */ /* Look for the options we recognize and will support for this session */ /* and reply with their bytes set */ for (j=0; j= 2045 && (i < len-1) ) { sb_priv[j++] = (CHAR) IAC; /* End of Subnegotiation */ sb_priv[j++] = (CHAR) SE; /* marked by IAC SE */ #ifdef DEBUG if (deblog || tn_deb || debses) { ckmakxmsg( (char *)fwdx_msg_out,TN_MSG_LEN,"TELNET SENT SB ", TELOPT(TELOPT_FORWARD_X), " DATA CHANNEL=",ckitoa(channel)," ", NULL,NULL,NULL,NULL,NULL,NULL,NULL ); tn_hex((CHAR *)fwdx_msg_out, TN_MSG_LEN,&sb_priv[j_sav],j-(j_sav+2)); ckstrncat((char *)fwdx_msg_out," IAC SE",TN_MSG_LEN); } #endif /* DEBUG */ #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif #ifdef DEBUG debug(F100,(char *)fwdx_msg_out,"",0); if (tn_deb || debses) tn_debug((char *)fwdx_msg_out); #endif /* DEBUG */ rc = (ttol(sb_priv,j) < 0); /* Send it. */ #ifdef OS2 ReleaseTelnetMutex(); #endif if (rc) { debug(F110,"fwdx_send_data_from_channel()","ttol() failed",0); return(-1); } j = 0; sb_priv[j++] = (CHAR) IAC; /* I Am a Command */ sb_priv[j++] = (CHAR) SB; /* Subnegotiation */ sb_priv[j++] = TELOPT_FORWARD_X; /* Forward X */ sb_priv[j++] = FWDX_DATA; /* Data */ sb_priv[j++] = p[0]; /* First Byte of Channel */ if ( p[0] == IAC ) sb_priv[j++] = IAC; sb_priv[j++] = p[1]; /* Second Byte of Channel */ if ( p[1] == IAC ) sb_priv[j++] = IAC; } } sb_priv[j++] = (CHAR) IAC; /* End of Subnegotiation */ sb_priv[j++] = (CHAR) SE; /* marked by IAC SE */ #ifdef DEBUG if (deblog || tn_deb || debses) { ckmakxmsg( (char *)fwdx_msg_out,TN_MSG_LEN, "TELNET SENT SB ",TELOPT(TELOPT_FORWARD_X), " DATA ",ckctox(p[0],1)," ",ckctox(p[1],1)," ", NULL,NULL,NULL,NULL,NULL); tn_hex((CHAR *)fwdx_msg_out,TN_MSG_LEN,&sb_priv[6],j-8); ckstrncat((char *)fwdx_msg_out," IAC SE",TN_MSG_LEN); } #endif /* DEBUG */ #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif #ifdef DEBUG debug(F100,(char *)fwdx_msg_out,"",0); if (tn_deb || debses) tn_debug((char *)fwdx_msg_out); #endif /* DEBUG */ rc = (ttol(sb_priv,j) < 0); /* Send it. */ #ifdef OS2 ReleaseTelnetMutex(); #endif if ( rc ) { debug(F110,"fwdx_send_data_from_channel()","ttol() failed",0); return(-1); } return(0); } static unsigned char * #ifdef CK_ANSIC fwdx_add_quoted_twobyte(unsigned char *p, unsigned short twobyte) #else fwdx_add_quoted_twobyte(p, twobyte) unsigned char *p; unsigned short twobyte; #endif /* CK_ANSIC */ /* adds the IAC quoted (MSB) representation of 'channel' at buffer pointer 'p', * returning pointer to new buffer position. NO OVERFLOW CHECK! */ { *p++ = (unsigned char)((twobyte >> 8) & 0xFF); if (*(p - 1) == 0xFF) *p++ = 0xFF; *p++ = (unsigned char)(twobyte & 0xFF); if (*(p - 1) == 0xFF) *p++ = 0xFF; return p; } int #ifdef CK_ANSIC fwdx_create_fake_xauth(char *name, int name_len, int data_len) #else fwdx_create_fake_xauth(name, name_len, data_len) char *name; int name_len; int data_len; #endif /* CK_ANSIC */ { char stackdata[256]; unsigned int c, n; if (!name_len || !data_len) return 1; fake_xauth.name = malloc(name_len); fake_xauth.data = malloc(data_len); if (!fake_xauth.name || !fake_xauth.data) return 2; fake_xauth.name_length = name_len; memcpy(fake_xauth.name, name, name_len); fake_xauth.data_length = data_len; /* try to make a random unsigned int to feed srand() */ c = time(NULL); c *= getpid(); for (n = 0; n < sizeof(stackdata); n++) c += stackdata[n]; srand((unsigned int)c); for (c = 0; c < data_len; c++) fake_xauth.data[c] = (unsigned char)rand(); return 0; } #ifdef COMMENT /* No longer used */ int fwdx_send_xauth(void) { int c, err, dpynum, family, sb_len, rc; char *display, *host = NULL; unsigned char *sb_priv, *p; /* parse the local DISPLAY env var */ if (!(display = tn_get_display())) return (-1); if (fwdx_parse_displayname(display, &family, &host, &dpynum, NULL, NULL)) { char * disp_no = ckitoa(dpynum); if (family == FamilyLocal) { /* call with address = "" */ char address[300] = "localhost"; gethostname(address, sizeof(address) - 1); real_xauth = XauGetAuthByAddr(family, strlen(address), address, strlen(disp_no), disp_no, 0, NULL ); } else if (family == FamilyInternet) { /* call with address = 4 bytes numeric ip addr (MSB) */ struct hostent *hi; if (hi = gethostbyname(host)) real_xauth = XauGetAuthByAddr(family, 4, hi->h_addr, strlen(disp_no), disp_no, 0, NULL ); } } if (host) { free(host); host = NULL; } if (real_xauth) err = fwdx_create_fake_xauth(real_xauth->name, real_xauth->name_length, real_xauth->data_length ); else err = fwdx_create_fake_xauth("MIT-MAGIC-COOKIE-1", strlen("MIT-MAGIC-COOKIE-1"), 16); if (err) return(-1); /* allocate memory for the SB block, alloc for worst case */ /* the following sprintf() calls are safe due to length checking */ /* buffer is twice as big as the input just in case every byte was IAC */ sb_len = 5 + 2 + 2 + fake_xauth.name_length + fake_xauth.data_length + 2; if (!(sb_priv = malloc(2 * sb_len))) return(-1); p = sb_priv; sprintf(p, "%c%c%c%c%c", IAC, SB, TELOPT_FORWARD_X, FWDX_OPT_DATA, FWDX_OPT_XAUTH); p += 5; p = fwdx_add_quoted_twobyte(p, fake_xauth.name_length); p = fwdx_add_quoted_twobyte(p, fake_xauth.data_length); for (c = 0; c < fake_xauth.name_length; c++) { *p++ = fake_xauth.name[c]; if ((unsigned char)fake_xauth.name[c] == 0xFF) *p++ = 0xFF; } for (c = 0; c < fake_xauth.data_length; c++) { *p++ = fake_xauth.data[c]; if ((unsigned char)fake_xauth.data[c] == 0xFF) *p++ = 0xFF; } sprintf(p, "%c%c", IAC, SE); p += 2; #ifdef DEBUG if (deblog || tn_deb || debses) { sprintf((char *)fwdx_msg_out,"TELNET SENT SB %s OPTION_DATA XAUTH ", TELOPT(TELOPT_FORWARD_X)); tn_hex((char *)fwdx_msg_out,TN_MSG_LEN,&sb_priv[5],(p-sb_priv)-7); ckstrncat((char *)fwdx_msg_out," IAC SE",TN_MSG_LEN); } #endif /* DEBUG */ /* Add Telnet Debug info here */ #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif #ifdef DEBUG debug(F100,(char *)fwdx_msg_out,"",0); if (tn_deb || debses) tn_debug((char *)fwdx_msg_out); #endif /* DEBUG */ rc = ( ttol(sb_priv,p-sb_priv) < 0 ); /* Send it. */ #ifdef OS2 ReleaseTelnetMutex(); #endif if (rc) { debug(F110,"fwdx_send_xauth()","ttol() failed",0); return(-1); } free(sb_priv); return(0); } #endif /* COMMENT */ #ifdef FWDX_SERVER /* Only if we ever become a server - not yet ported to Kermit */ /* And even so most of this code does not belong in this module */ int fwdx_write_xauthfile(void) { int dpynum, scrnum, family; char myhost[300], *host, *rest = NULL; FILE *file; struct sockaddr_in saddr; struct hostent *hi; if (!fwdx_display && !fwdx_xauthfile) return 1; if (!parse_displayname(fwdx_display, &family, &host, &dpynum, &scrnum, &rest)) return 2; if (rest) free(rest); if (host) free(host); if (family != FamilyInternet) return 3; /* every thing but FamilyInternet is unexpected */ /* X connections to localhost:1 is actually treated as local unix sockets, * see the 'xauth' man page. */ xauth.family = FamilyLocal; if (gethostname(myhost, sizeof(myhost) - 1)) return 5; xauth.address_length = strlen(myhost); if (!(xauth.address = malloc(xauth.address_length))) return 5; memcpy(xauth.address, myhost, xauth.address_length); /* the display number is written as a string, not numeric */ if (!(xauth.number = malloc(6))) return 6; snprintf(xauth.number, 5, "%u", dpynum); xauth.number_length = strlen(xauth.number); if (!(file = fopen(fwdx_xauthfile, "wb"))) return 7; if (!XauWriteAuth(file, &xauth)) return 8; fclose(file); setenv("XAUTHORITY", fwdx_xauthfile, 1); return 0; } int fwdx_setup_xauth(unsigned char *sp, int len) /* called with 'len' xauth bytes, starting at 'sp' * the data format is: */ { int xauthfd; if (!fwdx_options[FWDX_OPT_XAUTH]) return 1; if (len < 4) return 2; /* setup the xauth struct */ xauth.name_length = (sp[0] << 8) + sp[1]; xauth.data_length = (sp[2] << 8) + sp[3]; if (len != 4 + xauth.name_length + xauth.data_length) return 3; xauth.name = malloc(xauth.name_length); xauth.data = malloc(xauth.data_length); if (!xauth.name || !xauth.data) return 4; memcpy(xauth.name, sp + 4, xauth.name_length); memcpy(xauth.data, sp + 4 + xauth.name_length, xauth.data_length); /* Setup to always have a local .Xauthority. */ fwdx_xauthfile = malloc(MAXPATHLEN+1); snprintf(fwdx_xauthfile, MAXPATHLEN, "/tmp/XauthXXXXXX"); if ((xauthfd = mkstemp(fwdx_xauthfile)) != -1) /* we change file ownership later, when we know who is to be owner! */ close(xauthfd); else { free(fwdx_xauthfile); fwdx_xauthfile = NULL; return 5; } /* Must have the subshell's new DISPLAY env var to write xauth to xauthfile */ if (fwdx_display) if (fwdx_write_xauthfile()) return 6; return 0; } void fwdx_set_xauthfile_owner(int uid) { struct passwd *pwd; if (!fwdx_xauthfile || !(pwd = getpwuid(uid))) return; chown(fwdx_xauthfile, pwd->pw_uid, pwd->pw_gid); } int fwdx_server_accept_options(unsigned char *sp, int len) /* called with 'len' option bytes, starting at 'sp' */ { int c; for (c = 0; c < len-2; c++) { if (c == 0) { if (sp[c] & FWDX_OPT_XAUTH) flag = 1; } } return(0); } #endif /* FWDX_SERVER */ #endif /* CK_FORWARD_X */ #ifdef IKS_OPTION /* iks_wait() -- Wait for an IKS subnegotiation response. sb - is either KERMIT_REQ_START or KERMIT_REQ_STOP depending on the desired state of the peer's Kermit server. flushok - specifies whether it is ok to throw away non-Telnet data if so, then we call ttflui() instead of tn_flui(). Returns: 1 if the desired state is achieved or if it is unknown. 0 if the desired state is not achieved. */ int #ifdef CK_ANSIC iks_wait(int sb, int flushok) #else /* CK_ANSIC */ iks_wait(sb,flushok) int sb; int flushok; #endif /* CK_ANSIC */ { int tn_wait_save = tn_wait_flg; int x; if (TELOPT_U(TELOPT_KERMIT)) { switch (sb) { case KERMIT_REQ_START: debug(F111, "iks_wait KERMIT_REQ_START", "u_start", TELOPT_SB(TELOPT_KERMIT).kermit.u_start ); tn_siks(KERMIT_REQ_START); tn_wait_flg = 1; /* Kermit Option MUST wait */ do { if (flushok) tn_wait_idx = 0; x = tn_wait("iks_wait() me_iks_req_start"); } while (x == 0 && flushok && tn_wait_idx == TN_WAIT_BUF_SZ); tn_wait_flg = tn_wait_save; if (flushok) tn_wait_idx = 0; if (tn_wait_idx == TN_WAIT_BUF_SZ) { /* * We are attempting to start a kermit server on the peer * the most likely reason is because we want to perform a * file transfer. But there is a huge amount of non telnet * negotiation data coming in and so we have not been able * to find the response. So we will lie and assume that * response is 'yes'. The worse that will happen is that * a RESP_STOP is received after we enter protocol mode. * And the protocol operation will be canceled. */ tn_push(); return(1); } else { tn_push(); return(TELOPT_SB(TELOPT_KERMIT).kermit.u_start); } case KERMIT_REQ_STOP: debug(F111, "iks_wait KERMIT_REQ_STOP", "u_start", TELOPT_SB(TELOPT_KERMIT).kermit.u_start ); tn_siks(KERMIT_REQ_STOP); tn_wait_flg = 1; /* Kermit Option MUST wait */ do { if (flushok) tn_wait_idx = 0; x = tn_wait("iks_wait() me_iks_req_stop"); } while (x == 0 && flushok && tn_wait_idx == TN_WAIT_BUF_SZ); tn_wait_flg = tn_wait_save; if (flushok) tn_wait_idx = 0; if (tn_wait_idx == TN_WAIT_BUF_SZ) { /* * We are attempting to stop a kermit server on the peer * the most likely reason being that we want to enter * CONNECT mode. But there is a huge amount of non telnet * negotiation data coming in and so we have not been able * to find the response. So we will lie and assume that * the answer is 'yes' and allow the CONNECT command to * succeed. The worst that happens is that CONNECT mode * swallows the incoming data displaying it to the user * and then it resumes Kermit client mode. */ tn_push(); return(1); } else { tn_push(); return(!TELOPT_SB(TELOPT_KERMIT).kermit.u_start); } } tn_push(); } return(1); } int #ifdef CK_ANSIC iks_tn_sb(CHAR * sb, int n) #else iks_tn_sb(sb, n) CHAR * sb; int n; #endif /* CK_ANSIC */ { extern int server; extern CHAR sstate; #ifdef NOICP extern int autodl; int inautodl = 0, cmdadl = 1; #else #ifdef CK_AUTODL extern int autodl, inautodl, cmdadl; #endif /* CK_AUTODL */ #endif /* NOICP */ switch (sb[0]) { case KERMIT_START: /* START */ TELOPT_SB(TELOPT_KERMIT).kermit.u_start = 1; return(4); case KERMIT_STOP: /* STOP */ TELOPT_SB(TELOPT_KERMIT).kermit.u_start = 0; return(4); case KERMIT_REQ_START: /* REQ-START */ #ifndef NOXFER if (inserver) { #ifdef CK_AUTODL cmdadl = 1; /* Turn on packet detection */ #endif /* CK_AUTODL */ TELOPT_SB(TELOPT_KERMIT).kermit.me_start = 1; tn_siks(KERMIT_RESP_START); } else if (TELOPT_SB(TELOPT_KERMIT).kermit.me_start) { tn_siks(KERMIT_RESP_START); } else { #ifndef IKSDONLY #ifdef CK_AUTODL #ifdef OS2 if (local && (IsConnectMode() && autodl) || (!IsConnectMode() && (inautodl || sstate == 'x' || sstate == 'v')) ) tn_siks(KERMIT_RESP_START); /* START */ else #else /* OS2 */ if ((local && what == W_CONNECT && autodl) || (local && what != W_CONNECT && (inautodl || sstate == 'x' || sstate == 'v') )) tn_siks(KERMIT_RESP_START); /* START */ else #endif /* OS2 */ #endif /* CK_AUTODL */ #endif /* IKSDONLY */ tn_siks(KERMIT_RESP_STOP); } #else /* NOXFER */ tn_siks(KERMIT_RESP_STOP); #endif /* NOXFER */ return(4); case KERMIT_REQ_STOP: /* REQ-STOP */ /* The protocol requires that the request be responded to */ /* either by changing states or by reporting the current */ /* state. */ /* We need to provide the user some way of dictating what */ /* the policies should be. For instance, if we are in */ /* CONNECT mode with autodownload ON and we get a REQ-STOP*/ /* what should the proper response be? */ #ifndef NOXFER if (inserver #ifdef CK_AUTODL || !local && cmdadl #endif /* CK_AUTODL */ ) { #ifdef CK_AUTODL cmdadl = 0; /* Turn off packet detection */ #endif /* CK_AUTODL */ tn_siks(KERMIT_RESP_STOP); } else if (server) { extern int en_fin; if (en_fin) { /* If the server is allowed to stop */ tn_siks(KERMIT_RESP_STOP); } else { /* We are not allowed to stop */ tn_siks(KERMIT_RESP_START); } } #ifndef IKSDONLY #ifdef CK_AUTODL #ifdef OS2 else if (local && (IsConnectMode() && autodl) || (!IsConnectMode() && inautodl) ) { /* If we are a pseudo-server and the other side requests */ /* that we stop, tell then that we have even though we */ /* have not. Otherwise, the other side might refuse to */ /* enter SERVER mode. */ tn_siks(KERMIT_RESP_STOP); /* STOP */ } #else /* OS2 */ else if ((local && what == W_CONNECT && autodl) || (local && what != W_CONNECT && inautodl) ) { /* If we are a pseudo-server and the other side requests */ /* that we stop, tell then that we have even though we */ /* have not. Otherwise, the other side might refuse to */ /* enter SERVER mode. */ tn_siks(KERMIT_RESP_STOP); /* STOP */ } #endif /* OS2 */ #endif /* CK_AUTODL */ #endif /* IKSDONLY */ else #endif /* NOXFER */ { /* If we are not currently in any mode that accepts */ /* Kermit packets then of course report that we are */ /* not being a Kermit server. */ tn_siks(KERMIT_RESP_STOP); /* STOP */ } return(4); case KERMIT_SOP: { /* SOP */ #ifndef NOXFER extern CHAR stchr; /* Incoming SOP character */ stchr = sb[1]; #endif /* NOXFER */ TELOPT_SB(TELOPT_KERMIT).kermit.sop = 1; return(4); } case KERMIT_RESP_START: /* START */ TELOPT_SB(TELOPT_KERMIT).kermit.u_start = 1; if (TELOPT_SB(TELOPT_KERMIT).kermit.me_req_start) { TELOPT_SB(TELOPT_KERMIT).kermit.me_req_start = 0; } else if (TELOPT_SB(TELOPT_KERMIT).kermit.me_req_stop) { /* If we have issued a request to stop a Kermit Server */ /* and the response is Start, then we must report this */ /* to the caller. */ TELOPT_SB(TELOPT_KERMIT).kermit.me_req_stop = 0; } return(4); case KERMIT_RESP_STOP: /* STOP */ TELOPT_SB(TELOPT_KERMIT).kermit.u_start = 0; if (TELOPT_SB(TELOPT_KERMIT).kermit.me_req_start) { TELOPT_SB(TELOPT_KERMIT).kermit.me_req_start = 0; /* If we have issued a request to start a Kermit Server */ /* and the response is Stop, then we must report this */ /* to the caller. */ } else if (TELOPT_SB(TELOPT_KERMIT).kermit.me_req_stop) { TELOPT_SB(TELOPT_KERMIT).kermit.me_req_stop = 0; } return(4); default: return(0); } /* switch (sb[0]) */ } #endif /* IKS_OPTION */ /* Initialize telnet settings - set default values for ME and U modes */ int tn_set_modes() { int opt,cmd; #ifdef CK_FORWARD_X int x; #endif /* CK_FORWARD_X */ #ifdef CK_ENVIRONMENT { int i,j; for (i = 0; i < 8; i++) { tn_env_uservar[i][0] = NULL; tn_env_uservar[i][1] = NULL; } } #endif /* CK_ENVIRONMENT */ /* initialize all options to refuse in both directions */ for (opt = 0; opt < NTELOPTS; opt++) { TELOPT_ME(opt) = 0; TELOPT_U(opt) = 0; TELOPT_UNANSWERED_WILL(opt) = 0; TELOPT_UNANSWERED_DO(opt) = 0; TELOPT_UNANSWERED_WONT(opt) = 0; TELOPT_UNANSWERED_DONT(opt) = 0; TELOPT_UNANSWERED_SB(opt) = 0; TELOPT_ME_MODE(opt) = TN_NG_RF; TELOPT_U_MODE(opt) = TN_NG_RF; TELOPT_DEF_S_ME_MODE(opt) = TN_NG_RF; TELOPT_DEF_S_U_MODE(opt) = TN_NG_RF; TELOPT_DEF_C_ME_MODE(opt) = TN_NG_RF; TELOPT_DEF_C_U_MODE(opt) = TN_NG_RF; for (cmd = 0; cmd < 4; cmd ++) tncnts[TELOPT_INDEX(opt)][cmd] = 0; } #ifdef IKS_OPTION TELOPT_SB(TELOPT_KERMIT).kermit.me_start = 0; TELOPT_SB(TELOPT_KERMIT).kermit.u_start = 0; TELOPT_SB(TELOPT_KERMIT).kermit.me_req_start = 0; TELOPT_SB(TELOPT_KERMIT).kermit.me_req_stop = 0; TELOPT_SB(TELOPT_KERMIT).kermit.sop = 0; #endif /* IKS_OPTION */ #ifdef CK_ENCRYPTION TELOPT_SB(TELOPT_ENCRYPTION).encrypt.stop = 0; #endif /* CK_ENCRYPTION */ #ifdef CK_NAWS TELOPT_SB(TELOPT_NAWS).naws.x = 0; TELOPT_SB(TELOPT_NAWS).naws.y = 0; #endif /* CK_NAWS */ #ifdef CK_SSL TELOPT_SB(TELOPT_START_TLS).start_tls.u_follows = 0; TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows = 0; TELOPT_SB(TELOPT_START_TLS).start_tls.auth_request = 0; #endif /* CK_SSL */ /* Now set the ones we want to accept to the proper values */ TELOPT_DEF_S_ME_MODE(TELOPT_SGA) = TN_NG_RQ; TELOPT_DEF_S_U_MODE(TELOPT_SGA) = TN_NG_RQ; TELOPT_DEF_C_ME_MODE(TELOPT_SGA) = TN_NG_AC; TELOPT_DEF_C_U_MODE(TELOPT_SGA) = TN_NG_AC; TELOPT_DEF_S_ME_MODE(TELOPT_BINARY) = TN_NG_AC; TELOPT_DEF_S_U_MODE(TELOPT_BINARY) = TN_NG_AC; TELOPT_DEF_C_ME_MODE(TELOPT_BINARY) = TN_NG_AC; TELOPT_DEF_C_U_MODE(TELOPT_BINARY) = TN_NG_AC; TELOPT_DEF_S_ME_MODE(TELOPT_LOGOUT) = TN_NG_AC; TELOPT_DEF_S_U_MODE(TELOPT_LOGOUT) = TN_NG_AC; TELOPT_DEF_C_ME_MODE(TELOPT_LOGOUT) = TN_NG_AC; TELOPT_DEF_C_U_MODE(TELOPT_LOGOUT) = TN_NG_AC; #ifdef IKS_OPTION TELOPT_DEF_S_ME_MODE(TELOPT_KERMIT) = TN_NG_RQ; TELOPT_DEF_S_U_MODE(TELOPT_KERMIT) = TN_NG_RQ; TELOPT_DEF_C_ME_MODE(TELOPT_KERMIT) = TN_NG_RQ; TELOPT_DEF_C_U_MODE(TELOPT_KERMIT) = TN_NG_RQ; #endif /* IKS_OPTION */ #ifdef CK_ENCRYPTION TELOPT_DEF_S_U_MODE(TELOPT_ENCRYPTION) = TN_NG_RQ; TELOPT_DEF_S_ME_MODE(TELOPT_ENCRYPTION) = TN_NG_RQ; TELOPT_DEF_C_U_MODE(TELOPT_ENCRYPTION) = TN_NG_RQ; TELOPT_DEF_C_ME_MODE(TELOPT_ENCRYPTION) = TN_NG_RQ; #endif /* CK_ENCRYPTION */ TELOPT_DEF_S_ME_MODE(TELOPT_ECHO) = TN_NG_RQ; #ifdef IKSD if ( !inserver ) #endif /* IKSD */ TELOPT_DEF_S_U_MODE(TELOPT_TTYPE) = TN_NG_RQ; #ifdef CK_ENVIRONMENT TELOPT_DEF_S_U_MODE(TELOPT_NEWENVIRON) = TN_NG_RQ; #endif /* CK_ENVIRONMENT */ #ifdef CK_AUTHENTICATION TELOPT_DEF_S_U_MODE(TELOPT_AUTHENTICATION) = TN_NG_RQ; #endif /* CK_AUTHENTICATION */ #ifdef CK_SSL if (ck_ssleay_is_installed()) { TELOPT_DEF_S_U_MODE(TELOPT_START_TLS) = TN_NG_RQ; TELOPT_DEF_C_ME_MODE(TELOPT_START_TLS) = TN_NG_AC; } #endif /* CK_SSL */ #ifdef CK_NAWS TELOPT_DEF_S_U_MODE(TELOPT_NAWS) = TN_NG_RQ; #endif /* CK_NAWS */ TELOPT_DEF_C_U_MODE(TELOPT_ECHO) = TN_NG_AC; TELOPT_DEF_C_ME_MODE(TELOPT_TTYPE) = TN_NG_RQ; #ifdef CK_ENVIRONMENT TELOPT_DEF_C_ME_MODE(TELOPT_NEWENVIRON) = TN_NG_RQ; #endif /* CK_ENVIRONMENT */ #ifdef CK_AUTHENTICATION TELOPT_DEF_C_ME_MODE(TELOPT_AUTHENTICATION) = TN_NG_RQ; #endif /* CK_AUTHENTICATION */ #ifdef CK_NAWS TELOPT_DEF_C_ME_MODE(TELOPT_NAWS) = TN_NG_RQ; #endif /* CK_NAWS */ #ifdef CK_SNDLOC TELOPT_DEF_C_ME_MODE(TELOPT_SNDLOC) = TN_NG_RQ; #endif /* CK_SNDLOC */ #ifdef CK_FORWARD_X TELOPT_DEF_C_U_MODE(TELOPT_FORWARD_X) = TN_NG_AC; TELOPT_SB(TELOPT_FORWARD_X).forward_x.listen_socket = -1; for (x = 0; x < MAXFWDX; x++) { TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[x].fd = -1; TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[x].id = -1; TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[x].need_to_send_xauth = 0; TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[x].suspend = 0; } #endif /* CK_FORWARD_X */ #ifdef TN_COMPORT TELOPT_DEF_C_ME_MODE(TELOPT_COMPORT) = TN_NG_RQ; #endif /* TN_COMPORT */ /* Set the initial values for currently known mode */ for (opt = TELOPT_FIRST; opt <= TELOPT_LAST; opt++) { if (TELOPT_OK(opt)) { TELOPT_ME_MODE(opt) = sstelnet ? TELOPT_DEF_S_ME_MODE(opt) : TELOPT_DEF_C_ME_MODE(opt); TELOPT_U_MODE(opt) = sstelnet ? TELOPT_DEF_S_U_MODE(opt) : TELOPT_DEF_C_U_MODE(opt); } } return(1); } /* Send Delayed Subnegotiations */ VOID tn_sdsb() { /* if (!IS_TELNET()) return; */ if (TELOPT_SB(TELOPT_TTYPE).term.need_to_send) { tn_sttyp(); TELOPT_SB(TELOPT_TTYPE).term.need_to_send = 0; } #ifdef CK_ENVIRONMENT if (TELOPT_SB(TELOPT_NEWENVIRON).env.need_to_send && TELOPT_SB(TELOPT_NEWENVIRON).env.str) { tn_snenv((CHAR *)TELOPT_SB(TELOPT_NEWENVIRON).env.str, TELOPT_SB(TELOPT_NEWENVIRON).env.len); free(TELOPT_SB(TELOPT_NEWENVIRON).env.str); TELOPT_SB(TELOPT_NEWENVIRON).env.str=NULL; TELOPT_SB(TELOPT_NEWENVIRON).env.len=0; TELOPT_SB(TELOPT_NEWENVIRON).env.need_to_send = 0; } #ifdef CK_XDISPLOC if (TELOPT_SB(TELOPT_XDISPLOC).xdisp.need_to_send) { tn_sxdisploc(); TELOPT_SB(TELOPT_XDISPLOC).xdisp.need_to_send = 0; } #endif /* CK_XDISPLOC */ #endif /* CK_ENVIRONMENT */ #ifdef CK_NAWS if (TELOPT_SB(TELOPT_NAWS).naws.need_to_send) { tn_snaws(); TELOPT_SB(TELOPT_NAWS).naws.need_to_send = 0; } #endif /* CK_NAWS */ #ifdef CK_SNDLOC if (TELOPT_SB(TELOPT_SNDLOC).sndloc.need_to_send) { tn_sndloc(); TELOPT_SB(TELOPT_SNDLOC).sndloc.need_to_send = 0; } #endif /* CK_SNDLOC */ #ifdef CK_FORWARD_X if (TELOPT_SB(TELOPT_FORWARD_X).forward_x.need_to_send) { if ( sstelnet ) fwdx_send_options(); TELOPT_SB(TELOPT_FORWARD_X).forward_x.need_to_send = 0; } #endif /* CK_FORWARD_X */ #ifdef TN_COMPORT if (TELOPT_SB(TELOPT_COMPORT).comport.need_to_send) { tn_sndcomport(); TELOPT_SB(TELOPT_COMPORT).comport.need_to_send = 0; } #endif /* TN_COMPORT */ } int tn_reset() { int x,opt,cmd; /* if (!IS_TELNET()) return(1); */ tn_wait_idx = 0; /* Clear the tn_push() buffer */ tn_wait_tmo = TN_TIMEOUT; /* Reset wait timer stats */ nflag = 0; /* Reset the TELNET OPTIONS counts */ for (opt = TELOPT_FIRST; opt <= TELOPT_LAST; opt++) { if (TELOPT_OK(opt)) { TELOPT_ME(opt) = 0; TELOPT_U(opt) = 0; TELOPT_UNANSWERED_WILL(opt) = 0; TELOPT_UNANSWERED_DO(opt) = 0; TELOPT_UNANSWERED_WONT(opt) = 0; TELOPT_UNANSWERED_DONT(opt) = 0; TELOPT_UNANSWERED_SB(opt) = 0; TELOPT_ME_MODE(opt) = sstelnet ? TELOPT_DEF_S_ME_MODE(opt) : TELOPT_DEF_C_ME_MODE(opt); TELOPT_U_MODE(opt) = sstelnet ? TELOPT_DEF_S_U_MODE(opt) : TELOPT_DEF_C_U_MODE(opt); #ifdef DEBUG if (deblog) { switch (TELOPT_ME_MODE(opt)) { case TN_NG_RF: debug(F110,"tn_ini ME REFUSE ",TELOPT(opt),0); break; case TN_NG_AC: debug(F110,"tn_ini ME ACCEPT ",TELOPT(opt),0); break; case TN_NG_RQ: debug(F110,"tn_ini ME REQUEST",TELOPT(opt),0); break; case TN_NG_MU: debug(F110,"tn_ini ME REQUIRE",TELOPT(opt),0); break; } switch (TELOPT_U_MODE(opt)) { case TN_NG_RF: debug(F110,"tn_ini U REFUSE ",TELOPT(opt),0); break; case TN_NG_AC: debug(F110,"tn_ini U ACCEPT ",TELOPT(opt),0); break; case TN_NG_RQ: debug(F110,"tn_ini U REQUEST",TELOPT(opt),0); break; case TN_NG_MU: debug(F110,"tn_ini U REQUIRE",TELOPT(opt),0); break; } } #endif /* DEBUG */ for (cmd = 0; cmd < 4; cmd ++) tncnts[TELOPT_INDEX(opt)][cmd] = 0; } } #ifdef CK_ENVIRONMENT if (!tn_env_flg) { TELOPT_ME_MODE(TELOPT_NEWENVIRON) = TN_NG_RF; TELOPT_U_MODE(TELOPT_NEWENVIRON) = TN_NG_RF; } #endif /* CK_ENVIRONMENT */ #ifdef CK_SNDLOC if (!tn_loc) TELOPT_DEF_C_ME_MODE(TELOPT_SNDLOC) = TN_NG_RF; #endif /* CK_SNDLOC */ #ifdef IKS_OPTION TELOPT_SB(TELOPT_KERMIT).kermit.me_start = 0; TELOPT_SB(TELOPT_KERMIT).kermit.u_start = 0; TELOPT_SB(TELOPT_KERMIT).kermit.me_req_start = 0; TELOPT_SB(TELOPT_KERMIT).kermit.me_req_stop = 0; TELOPT_SB(TELOPT_KERMIT).kermit.sop = 0; #endif /* IKS_OPTION */ #ifdef CK_ENCRYPTION TELOPT_SB(TELOPT_ENCRYPTION).encrypt.stop = 0; TELOPT_SB(TELOPT_ENCRYPTION).encrypt.need_to_send = 0; #endif /* CK_ENCRYPTION */ #ifdef CK_NAWS TELOPT_SB(TELOPT_NAWS).naws.need_to_send = 0; TELOPT_SB(TELOPT_NAWS).naws.x = 0; TELOPT_SB(TELOPT_NAWS).naws.y = 0; #endif /* CK_NAWS */ TELOPT_SB(TELOPT_TTYPE).term.need_to_send = 0; TELOPT_SB(TELOPT_TTYPE).term.type[0] = '\0'; #ifdef CK_ENVIRONMENT TELOPT_SB(TELOPT_NEWENVIRON).env.need_to_send = 0; if (tn_first) TELOPT_SB(TELOPT_NEWENVIRON).env.str=NULL; else if (TELOPT_SB(TELOPT_NEWENVIRON).env.str) { free(TELOPT_SB(TELOPT_NEWENVIRON).env.str); TELOPT_SB(TELOPT_NEWENVIRON).env.str=NULL; } TELOPT_SB(TELOPT_NEWENVIRON).env.len=0; #ifdef CK_XDISPLOC TELOPT_SB(TELOPT_XDISPLOC).xdisp.need_to_send = 0; #endif /* CK_XDISPLOC */ #endif /* CK_ENVIRONMENT */ #ifdef CK_SNDLOC TELOPT_SB(TELOPT_SNDLOC).sndloc.need_to_send = 0; #endif /* CK_SNDLOC */ #ifdef CK_FORWARD_X TELOPT_SB(TELOPT_FORWARD_X).forward_x.need_to_send = 0; TELOPT_SB(TELOPT_FORWARD_X).forward_x.listen_socket = -1; for (x = 0; x < MAXFWDX; x++) { TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[x].fd = -1; TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[x].id = -1; TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[x].need_to_send_xauth = 0; TELOPT_SB(TELOPT_FORWARD_X).forward_x.channel[x].suspend = 0; } /* Reset Xauth data */ if ( real_xauth ) { XauDisposeAuth(real_xauth); real_xauth = NULL; } if ( fake_xauth.name ) free(fake_xauth.name); if ( fake_xauth.data ) free(fake_xauth.data); if ( fake_xauth.address ) free(fake_xauth.address); if ( fake_xauth.number ) free(fake_xauth.number); memset(&fake_xauth,0,sizeof(fake_xauth)); #ifdef NT TELOPT_SB(TELOPT_FORWARD_X).forward_x.thread_started = 0; #endif /* NT */ #endif /* CK_FORWARD_X */ #ifdef CK_SSL if (tls_only_flag || ssl_only_flag) { TELOPT_ME_MODE(TELOPT_START_TLS) = TN_NG_RF; TELOPT_U_MODE(TELOPT_START_TLS) = TN_NG_RF; } TELOPT_SB(TELOPT_START_TLS).start_tls.u_follows = 0; TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows = 0; TELOPT_SB(TELOPT_START_TLS).start_tls.auth_request = 0; #endif /* CK_SSL */ #ifdef CK_ENCRYPTION if (!ck_crypt_is_installed() #ifdef CK_SSL || tls_only_flag || ssl_only_flag #endif /* CK_SSL */ ) { TELOPT_ME_MODE(TELOPT_ENCRYPTION) = TN_NG_RF; TELOPT_U_MODE(TELOPT_ENCRYPTION) = TN_NG_RF; } #endif /* CK_ENCRYPTION */ #ifdef TN_COMPORT TELOPT_SB(TELOPT_COMPORT).comport.need_to_send = 0; TELOPT_SB(TELOPT_COMPORT).comport.wait_for_sb = 0; TELOPT_SB(TELOPT_COMPORT).comport.wait_for_ms = 0; tnc_init(); #endif /* TN_COMPORT */ tn_first = 0; /* No longer the first time init */ #ifdef OS2 ttnum = -1; /* Reset TermType negotiation */ ttnumend = 0; #endif /* OS2 */ return(0); } int tn_start() { int wait, x, opt; /* if (!IS_TELNET()) return(0); */ if (tn_init && tn_begun) return(0); tn_begun = 1; debug(F111,"tn_start","sstelnet",sstelnet); wait = 0; if (tn_duplex) { oldplex = duplex; /* save old duplex value */ duplex = 1; /* and set to half duplex for telnet */ } #ifdef CK_SSL if (!TELOPT_ME(TELOPT_START_TLS) && TELOPT_ME_MODE(TELOPT_START_TLS) >= TN_NG_RQ) { if (tn_sopt(WILL, TELOPT_START_TLS) < 0) return(-1); TELOPT_UNANSWERED_WILL(TELOPT_START_TLS) = 1; wait = 1; } if (!TELOPT_U(TELOPT_START_TLS) && TELOPT_U_MODE(TELOPT_START_TLS) >= TN_NG_RQ) { if (tn_sopt(DO, TELOPT_START_TLS) < 0) return(-1); TELOPT_UNANSWERED_DO(TELOPT_START_TLS) = 1; wait = 1; } #endif /* CK_SSL */ #ifdef CK_AUTHENTICATION debug(F110,"tn_ini() CK_AUTHENTICATION","",0); if (tn_init) /* tn_ini() might be called recursively */ return(0); if (!TELOPT_ME(TELOPT_AUTHENTICATION) && TELOPT_ME_MODE(TELOPT_AUTHENTICATION) >= TN_NG_RQ) { if (tn_sopt(WILL, TELOPT_AUTHENTICATION) < 0) return(-1); TELOPT_UNANSWERED_WILL(TELOPT_AUTHENTICATION) = 1; wait = 1; } if (!TELOPT_U(TELOPT_AUTHENTICATION) && TELOPT_U_MODE(TELOPT_AUTHENTICATION) >= TN_NG_RQ) { if (tn_sopt(DO, TELOPT_AUTHENTICATION) < 0) return(-1); TELOPT_UNANSWERED_DO(TELOPT_AUTHENTICATION) = 1; wait = 1; } #ifdef CK_ENCRYPTION if (TELOPT_U_MODE(TELOPT_AUTHENTICATION) == TN_NG_RF && TELOPT_ME_MODE(TELOPT_AUTHENTICATION) == TN_NG_RF) { TELOPT_ME_MODE(TELOPT_ENCRYPTION) = TN_NG_RF; TELOPT_U_MODE(TELOPT_ENCRYPTION) = TN_NG_RF; } #endif /* CK_ENCRYPTION */ #endif /* CK_AUTHENTICATION */ #ifdef CK_NAWS #ifndef NOLOCAL debug(F110,"tn_ini() CK_NAWS !NOLOCAL","",0); if (!sstelnet) { /* Console terminal screen rows and columns */ #ifdef OS2 debug(F101, "tn_ini tt_rows 1", "", VscrnGetHeight(VTERM)-(tt_status[VTERM]?1:0) ); debug(F101,"tn_ini tt_cols 1","",VscrnGetWidth(VTERM)); /* Not known yet */ if (VscrnGetWidth(VTERM) < 0 || VscrnGetHeight(VTERM)-(tt_status[VTERM]?1:0) < 0) { ttgwsiz(); /* Try to find out */ } debug(F101, "tn_ini tt_rows 2", "", VscrnGetHeight(VTERM)-(tt_status[VTERM]?1:0) ); debug(F101,"tn_ini tt_cols 2","",VscrnGetWidth(VTERM)); /* Now do we know? */ if (VscrnGetWidth(VTERM) > 0 && VscrnGetHeight(VTERM)-(tt_status[VTERM]?1:0) > 0) { if (!TELOPT_ME(TELOPT_NAWS) && TELOPT_ME_MODE(TELOPT_NAWS) >= TN_NG_RQ) { if (tn_sopt(WILL, TELOPT_NAWS) < 0) return(-1); TELOPT_UNANSWERED_WILL(TELOPT_NAWS) = 1; wait = 1; } } #else /* OS2 */ debug(F101,"tn_ini tt_rows 1","",tt_rows); debug(F101,"tn_ini tt_cols 1","",tt_cols); if (tt_rows < 0 || tt_cols < 0) { /* Not known yet */ ttgwsiz(); /* Try to find out */ } debug(F101,"tn_ini tt_rows 2","",tt_rows); debug(F101,"tn_ini tt_cols 2","",tt_cols); if (tt_rows > 0 && tt_cols > 0) { /* Now do we know? */ if (!TELOPT_ME(TELOPT_NAWS) && TELOPT_ME_MODE(TELOPT_NAWS) >= TN_NG_RQ) { if (tn_sopt(WILL, TELOPT_NAWS) < 0) return(-1); TELOPT_UNANSWERED_WILL(TELOPT_NAWS) = 1; wait = 1; } } #endif /* OS2 */ } else #endif /* NOLOCAL */ { if (!TELOPT_U(TELOPT_NAWS) && TELOPT_U_MODE(TELOPT_NAWS) >= TN_NG_RQ) { if (tn_sopt(DO, TELOPT_NAWS) < 0) return(-1); TELOPT_UNANSWERED_DO(TELOPT_NAWS) = 1; wait = 1; } } #endif /* CK_NAWS */ if (!TELOPT_ME(TELOPT_SGA) && TELOPT_ME_MODE(TELOPT_SGA) >= TN_NG_RQ) { if (tn_sopt(WILL, TELOPT_SGA) < 0) return(-1); TELOPT_UNANSWERED_WILL(TELOPT_SGA) = 1; wait = 1; } if (!TELOPT_U(TELOPT_SGA) && TELOPT_U_MODE(TELOPT_SGA) >= TN_NG_RQ) { if (tn_sopt(DO, TELOPT_SGA) < 0) return(-1); TELOPT_UNANSWERED_DO(TELOPT_SGA) = 1; wait = 1; } if (!tn_duplex) { if (!TELOPT_U(TELOPT_ECHO) && TELOPT_U_MODE(TELOPT_ECHO) >= TN_NG_RQ) { if (tn_sopt(DO, TELOPT_ECHO) < 0) return(-1); TELOPT_UNANSWERED_DO(TELOPT_ECHO) = 1; wait = 1; } } if (!TELOPT_ME(TELOPT_ECHO) && TELOPT_ME_MODE(TELOPT_ECHO) >= TN_NG_RQ) { if (tn_sopt(WILL, TELOPT_ECHO) < 0) return(-1); TELOPT_UNANSWERED_WILL(TELOPT_ECHO) = 1; wait = 1; } debug(F100,"tn_ini about to send WILL TTYPE if requested","",0); /* Talking to TELNET port, so send WILL TERMINAL TYPE and DO SGA. Also send WILL NAWS if we know our screen dimensions. */ if (!TELOPT_ME(TELOPT_TTYPE) && TELOPT_ME_MODE(TELOPT_TTYPE) >= TN_NG_RQ) { if ((x = tn_sopt(WILL,TELOPT_TTYPE)) < 0) { debug(F101,"tn_ini tn_sopt WILL TTYPE failed","",x); return(-1); } TELOPT_UNANSWERED_WILL(TELOPT_TTYPE) = 1; wait = 1; debug(F100,"tn_ini sent WILL TTYPE ok","",0); } if (!TELOPT_U(TELOPT_TTYPE) && TELOPT_U_MODE(TELOPT_TTYPE) >= TN_NG_RQ) { if ((x = tn_sopt(DO,TELOPT_TTYPE)) < 0) { debug(F101,"tn_ini tn_sopt DO TTYPE failed","",x); return(-1); } TELOPT_UNANSWERED_DO(TELOPT_TTYPE) = 1; wait = 1; debug(F100,"tn_ini sent DO TTYPE ok","",0); } if (!TELOPT_ME(TELOPT_BINARY) && TELOPT_ME_MODE(TELOPT_BINARY) >= TN_NG_RQ) { if (tn_sopt(WILL, TELOPT_BINARY) < 0) return(-1); TELOPT_UNANSWERED_WILL(TELOPT_BINARY) = 1; wait = 1; } if (!TELOPT_U(TELOPT_BINARY) && TELOPT_U_MODE(TELOPT_BINARY) >= TN_NG_RQ) { if (tn_sopt(DO, TELOPT_BINARY) < 0) return(-1); TELOPT_UNANSWERED_DO(TELOPT_BINARY) = 1; wait = 1; } #ifdef CK_SNDLOC if (tn_loc) { if (!TELOPT_ME(TELOPT_SNDLOC) && TELOPT_ME_MODE(TELOPT_SNDLOC) >= TN_NG_RQ) { if (tn_sopt(WILL, TELOPT_SNDLOC) < 0) return(-1); TELOPT_UNANSWERED_WILL(TELOPT_SNDLOC) = 1; wait = 1; } } #endif /* CK_SNDLOC */ #ifdef CK_ENVIRONMENT #ifdef CK_FORWARD_X if (!TELOPT_U(TELOPT_FORWARD_X) && TELOPT_U_MODE(TELOPT_FORWARD_X) >= TN_NG_RQ) { if (tn_sopt(WILL, TELOPT_FORWARD_X) < 0) return(-1); TELOPT_UNANSWERED_WILL(TELOPT_FORWARD_X) = 1; wait = 1; } #endif /* FORWARD_X */ #ifdef CK_XDISPLOC if (!TELOPT_ME(TELOPT_XDISPLOC) && TELOPT_ME_MODE(TELOPT_XDISPLOC) >= TN_NG_RQ) { if (tn_sopt(WILL, TELOPT_XDISPLOC) < 0) return(-1); TELOPT_UNANSWERED_WILL(TELOPT_XDISPLOC) = 1; wait = 1; } #endif /* CK_XDISPLOC */ /* Will send terminal environment. */ if (!TELOPT_ME(TELOPT_NEWENVIRON) && TELOPT_ME_MODE(TELOPT_NEWENVIRON) >= TN_NG_RQ) { if (tn_sopt(WILL, TELOPT_NEWENVIRON) < 0) return(-1); TELOPT_UNANSWERED_WILL(TELOPT_NEWENVIRON) = 1; wait = 1; } if (!TELOPT_U(TELOPT_NEWENVIRON) && TELOPT_U_MODE(TELOPT_NEWENVIRON) >= TN_NG_RQ) { if (tn_sopt(DO, TELOPT_NEWENVIRON) < 0) return(-1); TELOPT_UNANSWERED_DO(TELOPT_NEWENVIRON) = 1; wait = 1; } #endif /* CK_ENVIRONMENT */ /* Take care of any other telnet options that require handling. */ for (opt = TELOPT_FIRST; opt <= TELOPT_LAST; opt++) { switch (opt) { case TELOPT_AUTHENTICATION: case TELOPT_ENCRYPTION: case TELOPT_TTYPE: case TELOPT_NAWS: case TELOPT_BINARY: case TELOPT_NEWENVIRON: case TELOPT_SNDLOC: case TELOPT_XDISPLOC: case TELOPT_SGA: case TELOPT_ECHO: case TELOPT_KERMIT: case TELOPT_START_TLS: case TELOPT_FORWARD_X: break; break; default: if (TELOPT_OK(opt)) { if (!TELOPT_ME(opt) && TELOPT_ME_MODE(opt) >= TN_NG_RQ) { if (tn_sopt(WILL, opt) < 0) return(-1); TELOPT_UNANSWERED_WILL(opt) = 1; wait = 1; } if (!TELOPT_U(opt) && TELOPT_U_MODE(opt) >= TN_NG_RQ) { if (tn_sopt(DO, opt) < 0) return(-1); TELOPT_UNANSWERED_DO(opt) = 1; wait = 1; } } } } if (wait) { if (tn_wait("pre-encrypt") < 0) { tn_push(); return(-1); } wait = 0; } #ifdef CK_ENCRYPTION if (tn_init) /* tn_ini() may be called recursively */ return(0); if (!TELOPT_ME(TELOPT_ENCRYPTION) && TELOPT_ME_MODE(TELOPT_ENCRYPTION) >= TN_NG_RQ) { if (tn_sopt(WILL, TELOPT_ENCRYPTION) < 0) return(-1); TELOPT_UNANSWERED_WILL(TELOPT_ENCRYPTION) = 1; wait = 1; } if (!TELOPT_U(TELOPT_ENCRYPTION) && TELOPT_U_MODE(TELOPT_ENCRYPTION) >= TN_NG_RQ) { if (tn_sopt(DO, TELOPT_ENCRYPTION) < 0) return(-1); TELOPT_UNANSWERED_DO(TELOPT_ENCRYPTION) = 1; wait = 1; } /* If we are going to encrypt, we want to do it before we send any more */ /* data, especially the terminal type and environment variables. */ if (wait) { if (tn_wait("post-encrypt") < 0) { tn_push(); return(-1); } wait = 0; } #endif /* CK_ENCRYPTION */ tn_sdsb(); if (tn_init) /* tn_ini() may be called recursively */ return(0); #ifdef IKS_OPTION /* Kermit Server negotiation must go last */ /* Send U before ME */ if (!TELOPT_U(TELOPT_KERMIT) && TELOPT_U_MODE(TELOPT_KERMIT) >= TN_NG_RQ) { if (tn_sopt(DO, TELOPT_KERMIT) < 0) return(-1); TELOPT_UNANSWERED_DO(TELOPT_KERMIT) = 1; wait = 1; } if (!TELOPT_ME(TELOPT_KERMIT) && TELOPT_ME_MODE(TELOPT_KERMIT) >= TN_NG_RQ) { if (tn_sopt(WILL, TELOPT_KERMIT) < 0) return(-1); TELOPT_UNANSWERED_WILL(TELOPT_KERMIT) = 1; wait = 1; } #endif /* IKS_OPTION */ if (wait) { if (tn_wait("end of telnet negotiations") < 0) { tn_push(); return(-1); } wait = 0; } tn_sdsb(); /* Send delayed subnegotiations */ tn_push(); return(0); } /* Start a telnet connection. */ /* Returns -1 on error, 0 if nothing happens, 1 if init msgs sent ok */ int tn_ini() { int x; debug(F101,"tn_ini ttnproto","",ttnproto); debug(F101,"tn_ini tn_init","",tn_init); if (ttnet != NET_TCPB) /* Make sure connection is TCP/IP */ return(0); /* if (!IS_TELNET()) return(0); */ if (tn_init) /* Have we done this already? */ return(0); /* Don't do it again. */ tn_reset(); /* Reset telnet parameters */ tn_begun = 0; /* Reset; will be set by tn_start() */ switch ( ttnproto ) { case NP_RLOGIN: case NP_K4LOGIN: case NP_EK4LOGIN: case NP_K5LOGIN: case NP_EK5LOGIN: case NP_K5U2U: tn_init = 1; debug(F100,"tn_ini telnet negotiations ignored","tn_init",tn_init); return(0); #ifdef COMMENT /* Jeff's code from 30 Dec 2005 - doesn't work with SSL POP server */ case NP_NONE: case NP_SSL: case NP_TLS: ttnproto = NP_TELNET; /* pretend it's telnet anyway, */ oldplex = duplex; /* save old duplex value */ duplex = 1; /* and set to half duplex for telnet */ if (inserver) debug(F100,"tn_ini skipping telnet negotiations","",0); else tn_wait("tn_ini - waiting to see if telnet negotiations were sent"); tn_push(); return(0); case NP_SSL_RAW: case NP_TLS_RAW: case NP_TCPRAW: /* Raw socket requested. */ debug(F100,"tn_ini telnet negotiations ignored","tn_init",tn_init); return(0); #else /* My code from 4 Dec 2005 - works with SSL POP server */ case NP_NONE: case NP_SSL: case NP_TLS: /* If not talking to a telnet port, */ case NP_SSL_RAW: /* SSL and TLS with Telnet */ case NP_TLS_RAW: /* negotiations disabled. */ ttnproto = NP_TELNET; /* pretend it's telnet anyway, */ oldplex = duplex; /* save old duplex value */ duplex = 1; /* and set to half duplex for telnet */ if (inserver) debug(F100,"tn_ini skipping telnet negotiations","",0); else tn_wait("tn_ini - waiting to see if telnet negotiations were sent"); tn_push(); return(0); case NP_TCPRAW: /* Raw socket requested. */ debug(F100,"tn_ini telnet negotiations ignored","tn_init",tn_init); return(0); #endif /* COMMENT */ case NP_KERMIT: /* switching to Telnet protocol */ case NP_SSL_TELNET: case NP_TLS_TELNET: debug(F101,"tn_ini switching from XXX to Telnet","",ttnproto); ttnproto = NP_TELNET; /* fall through */ default: /* We are already using a variation on Telnet protocol */ ; } x = tn_start(); tn_init = 1; /* Remember successful completion. */ /* Don't send anything else! */ debug(F101,"tn_ini duplex","",duplex); debug(F101,"tn_ini done, tn_init","",tn_init); return(x); } int #ifdef CK_ANSIC tn_hex(CHAR * buf, int buflen, CHAR * data, int datalen) #else /* CK_ANSIC */ tn_hex(buf, buflen, data, datalen) CHAR * buf; int buflen; CHAR * data; int datalen; #endif /* CK_ANSIC */ { int i = 0, j = 0, k = 0; CHAR tmp[16]; /* in case value is treated as negative */ #ifdef COMMENT int was_hex = 1; for (k=0; k < datalen; k++) { if (data[k] < 32 || data[k] >= 127) { sprintf(tmp,"%s%02X ",was_hex?"":"\" ",data[k]); was_hex = 1; } else { sprintf(tmp,"%s%c",was_hex?"\"":"",data[k]); was_hex = 0; } ckstrncat((char *)buf,tmp,buflen); } if (!was_hex) ckstrncat((char *)buf,"\" ",buflen); #else /* COMMENT */ if (datalen <= 0 || data == NULL || buf == NULL || buflen <= 0) return(0); for (i = 0; i < datalen; i++) { ckstrncat((char *)buf,"\r\n ",buflen); for (j = 0 ; (j < 16); j++) { if ((i + j) < datalen) sprintf((char *)tmp, "%s%02x ", (j == 8 ? "| " : ""), (unsigned int) data[i + j] ); else sprintf((char *)tmp, "%s ", (j == 8 ? "| " : "") ); ckstrncat((char *)buf,(char *)tmp,buflen); } ckstrncat((char *)buf," ",buflen); for (k = 0; (k < 16) && ((i + k) < datalen); k++) { sprintf((char *)tmp, "%s%c", (k == 8 ? " " : ""), isprint((char)(data[i+k])) ? data[i+k] : '.' ); ckstrncat((char *)buf,(char *)tmp,buflen); } i += j - 1; } /* end for */ ckstrncat((char *)buf,"\r\n ",buflen); #endif /* COMMENT */ return(strlen((char *)buf)); } VOID tn_debug(s) char *s; { #ifdef NOLOCAL return; #else /* NOLOCAL */ #ifdef OS2 void cwrite(unsigned short); char *p = s; _PROTOTYP (void os2bold, (void)); extern int tt_type_mode; #endif /* OS2 */ if (!(tn_deb || debses)) return; debug(F111,"tn_debug",s,what); #ifdef OS2 if (1) { extern unsigned char colorcmd; colorcmd ^= 0x8 ; printf("%s\r\n",s); colorcmd ^= 0x8 ; } if (!scrninitialized[VTERM]) { USHORT x,y; checkscreenmode(); GetCurPos(&y, &x); SaveCmdMode(x+1,y+1); scrninit(); RestoreCmdMode(); } if ( ISVTNT(tt_type_mode) && ttnum != -1 && !debses ) return; RequestVscrnMutex( VTERM, SEM_INDEFINITE_WAIT ) ; os2bold(); /* Toggle boldness */ while (*p) cwrite((CHAR) *p++); /* Go boldly ... */ os2bold(); /* Toggle boldness back */ if (debses) { debses = 0; cwrite((CHAR) '\015'); cwrite((CHAR) '\012'); debses = 1; } else { cwrite((CHAR) '\015'); cwrite((CHAR) '\012'); } ReleaseVscrnMutex(VTERM) ; #else if (what != W_CONNECT && what != W_DIALING && what != W_COMMAND && what != W_NOTHING) return; /* CONNECT/command must be active */ conoll(s); #endif /* OS2 */ #endif /* NOLOCAL */ } /* Process in-band Telnet negotiation characters from the remote host. Call with the telnet IAC character and the current duplex setting (0 = remote echo, 1 = local echo), and a pointer to a function to call to read more characters. Returns: 6 if DO LOGOUT was received and accepted 5 if the Kermit start of packet character has changed 4 if state of remote Internet Kermit Service has changed 3 if a quoted IAC was received 2 if local echo must be changed to remote 1 if remote echo must be changed to local 0 if nothing happens or no action necessary -1 on failure (= internal or i/o error) */ #ifdef IKS_OPTION int tn_siks(cmd) int cmd; { /* TELNET SEND IKS SUB */ CHAR buf[8]; #ifndef NOXFER extern CHAR mystch; /* Outgoing Start of Packet Char */ #else CHAR mystch = '\1'; #endif /* NOXFER */ int n,m,rc; if (ttnet != NET_TCPB) return(0); /* Must be TCP/IP */ if (ttnproto != NP_TELNET) return(0); /* Must be telnet protocol */ if (cmd < KERMIT_START || cmd > KERMIT_RESP_STOP) /* Illegal subcommand */ return(-1); #ifdef CK_SSL if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) { return(0); } #endif /* CK_SSL */ if (cmd == KERMIT_START || cmd == KERMIT_RESP_START) { TELOPT_SB(TELOPT_KERMIT).kermit.me_start = 1; } else if (cmd == KERMIT_STOP || cmd == KERMIT_RESP_STOP) { TELOPT_SB(TELOPT_KERMIT).kermit.me_start = 0; } else if (cmd == KERMIT_REQ_STOP) TELOPT_SB(TELOPT_KERMIT).kermit.me_req_stop = 1; else if (cmd == KERMIT_REQ_START) TELOPT_SB(TELOPT_KERMIT).kermit.me_req_start = 1; if (cmd == KERMIT_SOP) { buf[0] = (CHAR) IAC; buf[1] = (CHAR) SB; buf[2] = (CHAR) TELOPT_KERMIT; buf[3] = (CHAR) (cmd & 0xff); buf[4] = (CHAR) mystch; buf[5] = (CHAR) IAC; buf[6] = (CHAR) SE; buf[7] = (CHAR) 0; #ifdef DEBUG if (tn_deb || debses || deblog) ckmakmsg( tn_msg_out,TN_MSG_LEN,"TELNET SENT SB KERMIT SOP ", ckctox(mystch,1)," IAC SE",NULL); #endif /* DEBUG */ #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif #ifdef DEBUG debug(F101,tn_msg_out,"",cmd); if (tn_deb || debses) tn_debug(tn_msg_out); #endif /* DEBUG */ rc = ( ttol(buf,7) < 7 ); /* Send it. */ #ifdef OS2 ReleaseTelnetMutex(); #endif if (rc) return(-1); } else { buf[0] = (CHAR) IAC; buf[1] = (CHAR) SB; buf[2] = (CHAR) TELOPT_KERMIT; buf[3] = (CHAR) (cmd & 0xff); buf[4] = (CHAR) IAC; buf[5] = (CHAR) SE; buf[6] = (CHAR) 0; #ifdef DEBUG if (tn_deb || debses || deblog) { char * s = 0; switch (cmd) { case KERMIT_START: s = "START"; break; case KERMIT_STOP: s = "STOP"; break; case KERMIT_REQ_START: s = "REQ-START"; break; case KERMIT_REQ_STOP: s = "REQ-STOP"; break; case KERMIT_RESP_START: s = "RESP-START"; break; case KERMIT_RESP_STOP: s = "RESP-STOP"; break; } ckmakmsg( tn_msg_out,TN_MSG_LEN, "TELNET SENT SB kermit ",s," IAC SE",NULL); } #endif /* DEBUG */ #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif #ifdef DEBUG debug(F101,tn_msg_out,"",cmd); if (tn_deb || debses) tn_debug(tn_msg_out); #endif /* DEBUG */ rc = ( ttol(buf,6) < 6 ); /* Send it. */ #ifdef OS2 ReleaseTelnetMutex(); #endif if (rc) return(-1); } return(1); } #endif /* IKS_OPTION */ /* tn_sb() performs Telnet Subnegotiation Parsing and Debugging */ /* returns <= 0 on error, 1 on success */ /* the length returned includes the IAC SE bytes */ int #ifdef CK_ANSIC /* TELNET SB */ tn_sb( int opt, int * len, int (*fn)(int) ) #else tn_sb( opt, len, fn ) int opt; int * len; int (*fn)(); #endif /* CK_ANSIC */ /* tn_sb */ { int c, x, y, n, m, flag; /* if (!IS_TELNET()) return(1); */ debug(F100,"Entering tn_sb()","",0); *len = 0; /* Initialize Len to 0 */ n = flag = 0; /* Flag for when done reading SB */ while (n < TSBUFSIZ) { /* Loop looking for IAC SE */ if ((y = (*fn)(0)) < 0) /* Read a byte */ return(y); y &= 0xff; /* Make sure it's just 8 bits. */ sb[n++] = (char) y; /* Deposit in buffer. */ if (seslog && sessft == XYFT_D) { /* Take care of session log */ logchar((char) y); } if (y == IAC) { /* If this is an IAC */ if (flag) { /* If previous char was IAC */ n--; /* it's quoted, keep one IAC */ flag = 0; /* and turn off the flag. */ } else flag = 1; /* Otherwise set the flag. */ } else if (flag) { /* Something else following IAC */ if (y == SE) /* If not SE, it's a protocol error */ break; else if (y == DONT) { /* Used DONT instead of SE */ debug(F100, "TELNET Subnegotiation error - used DONT instead of SE!", "" ,0 ); if (tn_deb || debses) tn_debug( "TELNET Subnegotiation error - used DONT instead of SE!"); flag = 3; break; } else { /* Other protocol error */ flag = 0; break; } } #ifdef CK_FORWARD_X if ( opt == TELOPT_FORWARD_X && sb[0] == FWDX_DATA && n >= (TSBUFSIZ-4) && !flag ) { /* do not let the buffer over flow */ /* write the data to the channel and continue processing */ /* the incoming data until IAC SE is reached. */ sb[n++] = IAC; sb[n++] = SE; #ifdef DEBUG if ( deblog || tn_deb || debses ) { int i; ckmakmsg( tn_msg,TN_MSG_LEN, "TELNET RCVD SB ",TELOPT(opt), " DATA(buffer-full) ",NULL); tn_hex((CHAR *)tn_msg,TN_MSG_LEN,&sb[1],n-3); if (flag == 2) ckstrncat(tn_msg," SE",TN_MSG_LEN); else if (flag == 3) ckstrncat(tn_msg," IAC DONT",TN_MSG_LEN); else ckstrncat(tn_msg," IAC SE",TN_MSG_LEN); debug(F100,tn_msg,"",0); if (tn_deb || debses) tn_debug(tn_msg); } #endif /* DEBUG */ if ( fwdx_tn_sb(sb,n) < 0 ) { debug(F100,"fxdx_tn_sb() failed","",0); /* We can't return though because that would leave */ /* data to be forwarded in the queue to the be sent */ /* to the terminal emulator. */ } /* reset leave the msg type and channel number in place */ n = 3; } #endif /* CK_FORWARD_X */ } debug(F111,"tn_sb end of while loop","flag",flag); if (!flag) { /* Make sure we got a valid SB */ debug(F111, "TELNET Subnegotiation prematurely broken","opt",opt); if (tn_deb || debses) { ckmakmsg( tn_msg, TN_MSG_LEN, "TELNET ", TELOPT(opt), " Subnegotiation prematurely broken",NULL ); tn_debug(tn_msg); } /* Was -1 but that would be an I/O error, so absorb it and go on. */ return(0); } #ifdef DEBUG if (deblog || tn_deb || debses) { int i; char * s[16]; for (i = 0; i < 16; i++) s[i] = ""; if (opt == TELOPT_NAWS) { i = 0; } else { i = 1; s[0] = "UNKNOWN"; switch (sb[0]) { case 0: if (opt == TELOPT_FORWARD_X) s[0] = "SCREEN"; else if (opt == TELOPT_KERMIT) s[0] = "START"; else if (opt == TELOPT_LFLOW) s[0] = "OFF"; else if (opt == TELOPT_COMPORT) s[0] = "SIGNATURE"; else s[0] = "IS"; if (opt == TELOPT_ENCRYPTION) { i++; if (sb[1] < ENCTYPE_CNT) { s[1] = enctype_names[sb[1]]; i++; switch(sb[2]) { case 1: s[2] = "FB64_IV"; break; case 2: s[2] = "FB64_IV_OK"; break; case 3: s[2] = "FB64_IV_BAD"; break; case 4: s[2] = "FB64_CHALLENGE"; break; case 5: s[2] = "FB64_RESPONSE"; break; } } else { s[1] = "UNKNOWN"; } } if (opt == TELOPT_AUTHENTICATION) { i += 2; s[1] = AUTHTYPE_NAME(sb[1]); s[2] = AUTHMODE_NAME(sb[2]); if (sb[1]) { i++; switch (sb[3]) { case 0: switch (sb[1]) { case AUTHTYPE_NTLM: s[3] = "NTLM_AUTH"; break; default: s[3] = "AUTH"; } break; case 1: switch (sb[1]) { case AUTHTYPE_SSL: s[3] = "START"; break; case AUTHTYPE_NTLM: s[3] = "NTLM_CHALLENGE"; break; default: s[3] = "REJECT"; } break; case 2: switch (sb[1]) { case AUTHTYPE_NTLM: s[3] = "NTLM_RESPONSE"; break; default: s[3] = "ACCEPT"; } break; case 3: switch (sb[1]) { case AUTHTYPE_NTLM: s[3] = "NTLM_ACCEPT"; break; case 1: /* KERBEROS_v4 */ case 5: /* SRP */ s[3] = "CHALLENGE"; break; case 2: /* KERBEROS_v5 */ s[3] = "RESPONSE"; break; case AUTHTYPE_SSL: s[3] = "REJECT"; break; } break; case 4: switch (sb[1]) { case AUTHTYPE_NTLM: s[3] = "NTLM_REJECT"; break; case 1: /* KERBEROS_V4 */ case 5: /* SRP */ s[3] = "RESPONSE"; break; case 2: /* KERBEROS_V5 */ s[3] = "FORWARD"; break; } break; case 5: switch (sb[1]) { case 5: /* SRP */ s[3] = "FORWARD"; break; case 2: /* KERBEROS_V5 */ s[3] = "FORWARD_ACCEPT"; break; } break; case 6: switch (sb[1]) { case 5: /* SRP */ s[3] = "FORWARD_ACCEPT"; break; case 2: /* KERBEROS_V5 */ s[3] = "FORWARD_REJECT"; break; } break; case 7: switch (sb[1]) { case 5: /* SRP */ s[3] = "FORWARD_REJECT"; break; case 2: /* KERBEROS_V5 */ s[3] = "TLS_VERIFY"; break; } break; case 8: switch (sb[1]) { case 5: /* SRP */ s[3] = "EXP"; break; } break; case 9: switch (sb[1]) { case 5: /* SRP */ s[3] = "PARAMS"; break; } break; } } } break; case 1: switch (opt) { case TELOPT_FORWARD_X: s[0] = "OPEN"; break; case TELOPT_LFLOW: s[0] = "ON"; break; case TELOPT_KERMIT: s[0] = "STOP"; break; case TELOPT_COMPORT: s[0] = "SET-BAUDRATE"; break; case TELOPT_AUTHENTICATION: s[0] = "SEND"; hexbuf[0] = '\0'; for (; i < n-2; i += 2) { if ( AUTHTYPE_NAME_OK(sb[i]) && AUTHMODE_NAME_OK(sb[i])) ckmakmsg( tn_msg, TN_MSG_LEN, AUTHTYPE_NAME(sb[i])," ", AUTHMODE_NAME(sb[i+1])," " ); else ckmakxmsg(tn_msg, TN_MSG_LEN, AUTHTYPE_NAME(sb[i]), "=", ckitoa(sb[i]), " ", AUTHMODE_NAME(sb[i+1]), "=", ckitoa(sb[i+1]), " ", NULL,NULL,NULL,NULL ); ckstrncat(hexbuf,tn_msg,sizeof(hexbuf)); } s[1] = hexbuf; break; case TELOPT_ENCRYPTION: s[0] = "SUPPORT"; while (i < n-2) { s[i] = enctype_names[sb[i]]; i++; } break; case TELOPT_START_TLS: s[0] = "FOLLOWS"; break; default: s[0] = "SEND"; } break; case 2: switch (opt) { case TELOPT_FORWARD_X: s[0] = "CLOSE"; break; case TELOPT_LFLOW: s[0] = "RESTART-ANY"; break; case TELOPT_KERMIT: s[0] = "REQ-START"; break; case TELOPT_COMPORT: s[0] = "SET-DATASIZE"; break; case TELOPT_NEWENVIRON: s[0] = "INFO"; break; case TELOPT_AUTHENTICATION: s[0] = "REPLY"; i=4; s[1] = AUTHTYPE_NAME(sb[1]); s[2] = AUTHMODE_NAME(sb[2]); switch (sb[3]) { case 0: switch (sb[1]) { case AUTHTYPE_NTLM: s[3] = "NTLM_AUTH"; break; default: s[3] = "AUTH"; } break; case 1: switch (sb[1]) { case AUTHTYPE_NTLM: s[3] = "NTLM_CHALLENGE"; break; default: s[3] = "REJECT"; } break; case 2: switch (sb[1]) { case AUTHTYPE_NTLM: s[3] = "NTLM_RESPONSE"; break; default: s[3] = "ACCEPT"; } break; case 3: switch (sb[1]) { case AUTHTYPE_NTLM: s[3] = "NTLM_ACCEPT"; break; case AUTHTYPE_KERBEROS_V4: case AUTHTYPE_SRP: s[3] = "CHALLENGE"; break; case AUTHTYPE_KERBEROS_V5: s[3] = "RESPONSE"; break; } break; case 4: switch (sb[1]) { case AUTHTYPE_NTLM: s[3] = "NTLM_REJECT"; break; case AUTHTYPE_KERBEROS_V4: case AUTHTYPE_SRP: s[3] = "RESPONSE"; break; case AUTHTYPE_KERBEROS_V5: s[3] = "FORWARD"; break; } break; case 5: switch (sb[1]) { case AUTHTYPE_SRP: s[3] = "FORWARD"; break; case AUTHTYPE_KERBEROS_V5: s[3] = "FORWARD_ACCEPT"; break; } break; case 6: switch (sb[1]) { case AUTHTYPE_SRP: s[3] = "FORWARD_ACCEPT"; break; case AUTHTYPE_KERBEROS_V5: s[3] = "FORWARD_REJECT"; break; } break; case 7: switch (sb[1]) { case AUTHTYPE_SRP: s[3] = "FORWARD_REJECT"; break; case AUTHTYPE_KERBEROS_V5: s[3] = "TLS_VERIFY"; break; } break; case 8: switch (sb[1]) { case AUTHTYPE_SRP: s[3] = "EXP"; break; } break; case 9: switch (sb[1]) { case AUTHTYPE_SRP: s[3] = "PARAMS"; break; } break; } break; case TELOPT_ENCRYPTION: s[0] = "REPLY"; s[1] = enctype_names[sb[1]]; i++; switch (sb[2]) { case 1: i++; s[2] = "FB64_IV"; break; case 2: i++; s[2] = "FB64_IV_OK"; break; case 3: i++; s[2] = "FB64_IV_BAD"; break; case 4: i++; s[2] = "FB64_CHALLENGE"; break; case 5: i++; s[2] = "FB64_RESPONSE"; break; } break; } break; case 3: switch (opt) { case TELOPT_FORWARD_X: s[0] = "DATA"; break; case TELOPT_LFLOW: s[0] = "RESTART-XON"; break; case TELOPT_KERMIT: s[0] = "REQ-STOP"; break; case TELOPT_COMPORT: s[0] = "SET-PARITY"; break; case TELOPT_AUTHENTICATION: s[0] = "NAME"; break; case TELOPT_ENCRYPTION: s[0] = "START"; break; } break; case 4: switch (opt) { case TELOPT_FORWARD_X: s[0] = "OPTIONS"; break; case TELOPT_KERMIT: s[0] = "SOP"; break; case TELOPT_COMPORT: s[0] = "SET-STOPSIZE"; break; case TELOPT_ENCRYPTION: s[0] = "END"; break; } break; case 5: switch (opt) { case TELOPT_FORWARD_X: s[0] = "OPTION_DATA"; break; case TELOPT_ENCRYPTION: s[0] = "REQUEST-START"; break; case TELOPT_COMPORT: s[0] = "SET-CONTROL"; break; } break; case 6: switch (opt) { case TELOPT_FORWARD_X: s[0] = "XOFF"; break; case TELOPT_ENCRYPTION: s[0] = "REQUEST-END"; break; case TELOPT_COMPORT: s[0] = "NOTIFY-LINESTATE"; break; } break; case 7: switch (opt) { case TELOPT_FORWARD_X: s[0] = "XON"; break; case TELOPT_ENCRYPTION: s[0] = "ENC-KEYID"; break; case TELOPT_COMPORT: s[0] = "NOTIFY-MODEMSTATE"; break; } break; case 8: switch (opt) { case TELOPT_KERMIT: s[0] = "RESP-START"; break; case TELOPT_ENCRYPTION: s[0] = "DEC-KEYID"; break; case TELOPT_COMPORT: s[0] = "FLOWCONTROL-SUSPEND"; break; } break; case 9: switch (opt) { case TELOPT_KERMIT: s[0] = "RESP-STOP"; break; case TELOPT_COMPORT: s[0] = "FLOWCONTROL-RESUME"; break; } break; case 10: switch (opt) { case TELOPT_COMPORT: s[0] = "SET-LINESTATE-MASK"; break; } break; case 11: switch (opt) { case TELOPT_COMPORT: s[0] = "SET-MODEMSTATE-MASK"; break; } break; case 12: switch (opt) { case TELOPT_COMPORT: s[0] = "PURGE-DATA"; break; } break; case 100: switch (opt) { case TELOPT_COMPORT: s[0] = "S_SIGNATURE"; break; } break; case 101: switch (opt) { case TELOPT_COMPORT: s[0] = "S_SET-BAUDRATE"; break; } break; case 102: switch (opt) { case TELOPT_COMPORT: s[0] = "S_SET-DATASIZE"; break; } break; case 103: switch (opt) { case TELOPT_COMPORT: s[0] = "S_SET-PARITY"; break; } break; case 104: switch (opt) { case TELOPT_COMPORT: s[0] = "S_SET-STOPSIZE"; break; } break; case 105: switch (opt) { case TELOPT_COMPORT: s[0] = "S_SET-CONTROL"; break; } break; case 106: switch (opt) { case TELOPT_COMPORT: s[0] = "S_NOTIFY-LINESTATE"; break; } break; case 107: switch (opt) { case TELOPT_COMPORT: s[0] = "S_NOTIFY-MODEMSTATE"; break; } break; case 108: switch (opt) { case TELOPT_COMPORT: s[0] = "S_FLOWCONTROL-SUSPEND"; break; } break; case 109: switch (opt) { case TELOPT_COMPORT: s[0] = "S_FLOWCONTROL-RESUME"; break; } break; case 110: switch (opt) { case TELOPT_COMPORT: s[0] = "S_SET-LINESTATE-MASK"; break; } break; case 111: switch (opt) { case TELOPT_COMPORT: s[0] = "S_SET-MODEMSTATE-MASK"; break; } break; case 112: switch (opt) { case TELOPT_COMPORT: s[0] = "S_PURGE-DATA"; break; } break; } } #ifdef M_XENIX { int len, param, param_len; ckmakmsg( tn_msg, TN_MSG_LEN, "TELNET RCVD SB ", TELOPT(opt)," ",NULL); len = strlen(tn_msg); for (param = 0; param <= 15; param++) { param_len = strlen(s[param]); if (param_len > 0) { strcpy(&tn_msg[len], s[param]); len += param_len; tn_msg[len++] = ' '; } } tn_msg[len] = '\0'; } #else /* M_XENIX */ ckmakxmsg(tn_msg,TN_MSG_LEN,"TELNET RCVD SB ",TELOPT(opt)," ", NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL); { int i; for (i = 0; i < 16; i++) { if (s[i][0]) { ckstrncat(tn_msg,s[i],TN_MSG_LEN); ckstrncat(tn_msg," ",TN_MSG_LEN); } } } #endif /* M_XENIX */ tn_hex((CHAR *)tn_msg,TN_MSG_LEN,&sb[i],n-2-i); if (flag == 2) ckstrncat(tn_msg," SE",TN_MSG_LEN); else if (flag == 3) ckstrncat(tn_msg," IAC DONT",TN_MSG_LEN); else ckstrncat(tn_msg," IAC SE",TN_MSG_LEN); debug(F100,tn_msg,"",0); if (tn_deb || debses) tn_debug(tn_msg); } debug(F111,"tn_sb","len",n); #endif /* DEBUG */ *len = n; /* return length */ return(1); /* success */ } static char rows_buf[16] = { 0, 0 }; /* LINES Environment variable */ static char cols_buf[16] = { 0, 0 }; /* COLUMNS Enviornment variable */ static char term_buf[64] = { 0, 0 }; /* TERM Environment variable */ #ifdef CK_CURSES #ifndef VMS #ifndef COHERENT _PROTOTYP(int tgetent,(char *, char *)); #endif /* COHERENT */ #else #ifdef __DECC _PROTOTYP(int tgetent,(char *, char *)); #endif /* __DECC */ #endif /* VMS */ extern char * trmbuf; /* Real curses */ #endif /* CK_CURSES */ #ifdef CK_ENCRYPTION static int tn_no_encrypt() { /* Prevent Encryption from being negotiated */ TELOPT_ME_MODE(TELOPT_ENCRYPTION) = TN_NG_RF; TELOPT_U_MODE(TELOPT_ENCRYPTION) = TN_NG_RF; /* Cancel any negotiation that might have started */ ck_tn_enc_stop(); if (TELOPT_ME(TELOPT_ENCRYPTION) || TELOPT_UNANSWERED_WILL(TELOPT_ENCRYPTION)) { TELOPT_ME(TELOPT_ENCRYPTION) = 0; if (tn_sopt(WONT,TELOPT_ENCRYPTION) < 0) return(-1); TELOPT_UNANSWERED_WONT(TELOPT_ENCRYPTION) = 1; } if (TELOPT_U(TELOPT_ENCRYPTION) || TELOPT_UNANSWERED_DO(TELOPT_ENCRYPTION)) { TELOPT_U(TELOPT_ENCRYPTION) = 0; if (tn_sopt(DONT,TELOPT_ENCRYPTION) < 0) return(-1); TELOPT_UNANSWERED_DONT(TELOPT_ENCRYPTION) = 1; } return(0); } #endif /* CK_ENCRYPTION */ /* The following note came from the old SGA negotiation code. This should */ /* no longer be necessary with the New Telnet negotiation state machine. */ /* Note: The following is proper behavior, and required for talking to the Apertus interface to the NOTIS library system, e.g. at Iowa State U: scholar.iastate.edu. Without this reply, the server hangs forever. This code should not be loop-inducing, since C-Kermit never sends WILL SGA as an initial bid, so if DO SGA comes, it is never an ACK. */ /* Return values: -1 = Telnet Option negotiation error -2 = Connection closed by peer -3 = Connection closed by us 0 = Success 1 = Echoing on 2 = Echoing off 3 = Quoted IAC 4 = IKS Event 5 = (unassigned) 6 = Logout */ static int #ifdef CK_ANSIC /* TELNET DO OPTION */ tn_xdoop(CHAR z, int echo, int (*fn)(int)) #else tn_xdoop(z, echo, fn) CHAR z; int echo; int (*fn)(); #endif /* CK_ANSIC */ /* tn_xdoop */ { int c, x, y, n, m; #ifdef IKS_OPTION extern int server; #ifdef NOICP extern int autodl; int inautodl = 0, cmdadl = 1; #else #ifdef CK_AUTODL extern int autodl, inautodl, cmdadl; #endif /* CK_AUTODL */ #endif /* NOICP */ #endif /* IKS_OPTION */ /* if (!IS_TELNET()) return(7); */ /* Have IAC, read command character. */ while ((c = (*fn)(0)) == -1); /* Read command character */ if (c < 0) return(c); c &= 0xFF; /* Strip high bits */ if (!TELCMD_OK(c)) { #ifdef DEBUG if (tn_deb || debses || deblog) { ckmakmsg(tn_msg,TN_MSG_LEN,"TELNET RCVD UNKNOWN (", ckitoa(c),")",NULL); debug(F101,tn_msg,"",c); if (tn_deb || debses) tn_debug(tn_msg); } #endif /* DEBUG */ return(0); } if (ttnproto == NP_NONE) { debug(F100,"tn_doop discovered a Telnet command", "ttnproto = NP_TELNET",0); ttnproto = NP_TELNET; } if (seslog && sessft == XYFT_D) { /* Copy to session log, if any. */ logchar((char)z); logchar((char)c); } if (c == (CHAR) IAC) /* Quoted IAC */ return(3); if (c < SB) { /* Other command with no arguments. */ #ifdef DEBUG if (deblog || tn_deb || debses) { ckmakmsg(tn_msg,TN_MSG_LEN,"TELNET RCVD ",TELCMD(c),NULL,NULL); debug(F101,tn_msg,"",c); if (tn_deb || debses) tn_debug(tn_msg); } #endif /* DEBUG */ switch (c) { /* What we would like to do here */ case TN_GA: /* Is substitute ASCII characters */ break; /* for the Telnet Command so that */ case TN_EL: /* the command may be processed by */ break; /* either the internal emulator or */ case TN_EC: /* by the superior process or shell */ break; case TN_AYT: #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif ttol((CHAR *)"[Yes]\015\012",7); #ifdef OS2 ReleaseTelnetMutex(); #endif break; case TN_AO: #ifdef BETADEBUG bleep(BP_NOTE); #endif /* BETADEBUG */ break; case TN_IP: break; case BREAK: break; case TN_DM: break; case TN_NOP: break; case SE: break; case TN_EOR: break; case TN_ABORT: break; case TN_SUSP: break; case TN_EOF: break; case TN_SAK: break; } return(0); } /* SB, WILL, WONT, DO, or DONT need more bytes... */ if ((x = (*fn)(0)) < 0) /* Get the option. */ return(x); x &= 0xff; /* Trim to 8 bits. */ if (seslog && sessft == XYFT_D) { /* Session log */ logchar((char) x); } #ifdef DEBUG if ((deblog || tn_deb || debses) && c != SB) { ckmakmsg(tn_msg,TN_MSG_LEN,"TELNET RCVD ",TELCMD(c)," ",TELOPT(x)); debug(F101,tn_msg,"",x); if (tn_deb || debses) tn_debug(tn_msg); } #endif /* DEBUG */ /* Now handle the command */ switch (c) { case WILL: #ifdef CK_SSL if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) return(0); #endif /* CK_SSL */ #ifdef CK_FORWARD_X if (x == TELOPT_FORWARD_X) { if (!fwdx_server_avail() || !(fwdx_no_encrypt || #ifdef CK_SSL (ssl_active_flag || tls_active_flag) #else /* CK_SSL */ 0 #endif /* CK_SSL */ || #ifdef CK_ENCRYPTION (ck_tn_encrypting() && ck_tn_decrypting()) #else /* CK_ENCRYPTION */ 0 #endif /* CK_ENCRYPTION */ )) { TELOPT_U_MODE(TELOPT_FORWARD_X) = TN_NG_RF; TELOPT_ME_MODE(TELOPT_FORWARD_X) = TN_NG_RF; } } #endif /* CK_FORWARD_X */ if (!TELOPT_OK(x) || TELOPT_U_MODE(x) == TN_NG_RF) { if (tn_sopt(DONT,x) < 0) return(-1); if (TELOPT_UNANSWERED_DO(x)) TELOPT_UNANSWERED_DO(x) = 0; } else if (!TELOPT_U(x)) { if (!TELOPT_UNANSWERED_DO(x)) { if (tn_sopt(DO,x) < 0) return -1; } if (TELOPT_UNANSWERED_DO(x)) TELOPT_UNANSWERED_DO(x) = 0; TELOPT_U(x) = 1; switch (x) { #ifdef CK_SSL case TELOPT_START_TLS: /* If my proposal is accepted, at this point the Telnet protocol is turned off and a TLS negotiation takes place. Start by sending SB START_TLS FOLLOWS to signal we are ready. Wait for the peer to send the same and then start the TLS negotiation. If the TLS negotiation succeeds we call tn_ini() again to reset the telnet state machine and restart the negotiation process over the now secure link. If the TLS negotiation fails, we call ttclos() to terminate the connection. Only the server should receive a WILL START_TLS */ tn_ssbopt(TELOPT_START_TLS,1,NULL,0); TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows = 1; break; #endif /* CK_SSL */ #ifdef CK_AUTHENTICATION case TELOPT_AUTHENTICATION: { /* We now have to perform a SB SEND to identify the */ /* supported authentication types to the other side. */ extern int authentication_version; #ifdef CK_SSL /* if we have an outstanding DO START_TLS then we must * wait for the response before we determine what to do */ if (TELOPT_UNANSWERED_DO(TELOPT_START_TLS)) { TELOPT_SB(TELOPT_START_TLS).start_tls.auth_request = 1; break; } #endif /* CK_SSL */ authentication_version = AUTHTYPE_AUTO; ck_tn_auth_request(); break; } #endif /* CK_AUTHENTICATION */ #ifdef CK_ENCRYPTION case TELOPT_ENCRYPTION: if (!(TELOPT_ME(TELOPT_AUTHENTICATION) || TELOPT_U(TELOPT_AUTHENTICATION)) ) { if (tn_sopt(DONT,x) < 0) return(-1); TELOPT_U(x) = 0; } else { if (ck_tn_auth_in_progress()) { TELOPT_SB(TELOPT_ENCRYPTION).encrypt.need_to_send = 1; } else { /* Perform subnegotiation */ ck_encrypt_send_support(); } if (!(TELOPT_ME(x) || TELOPT_UNANSWERED_WILL(x)) && TELOPT_ME_MODE(x) != TN_NG_RF) { if (tn_sopt(WILL, x) < 0) return(-1); TELOPT_UNANSWERED_WILL(x) = 1; } } break; #endif /* CK_ENCRYPTION */ #ifdef IKS_OPTION case TELOPT_KERMIT: if (!TELOPT_ME(x)) { /* Tell the other side what Start of Packet Character */ tn_siks(KERMIT_SOP); /* SOP */ if (!TELOPT_UNANSWERED_WILL(x) && TELOPT_ME_MODE(x) != TN_NG_RF) { if (tn_sopt(WILL, x) < 0) return(-1); TELOPT_UNANSWERED_WILL(x) = 1; } } break; #endif /* IKS_OPTION */ case TELOPT_BINARY: if (!TELOPT_ME(x)) { if (!TELOPT_UNANSWERED_WILL(x) && TELOPT_ME_MODE(x) >= TN_NG_RQ) { if (tn_sopt(WILL, x) < 0) return(-1); TELOPT_UNANSWERED_WILL(x) = 1; } } break; case TELOPT_ECHO: if (echo) { if (TELOPT_UNANSWERED_DO(x)) TELOPT_UNANSWERED_DO(x) = 0; return(2); } break; case TELOPT_TTYPE: /* SB TTYPE SEND */ tn_ssbopt(TELOPT_TTYPE,TELQUAL_SEND,NULL,0); TELOPT_UNANSWERED_SB(TELOPT_TTYPE)=1; break; #ifdef CK_ENVIRONMENT case TELOPT_NEWENVIRON: /* SB NEW-ENVIRON SEND */ { char request[6]; /* request it */ sprintf(request,"%cUSER",TEL_ENV_VAR); /* safe */ tn_ssbopt(TELOPT_NEWENVIRON,TELQUAL_SEND,request, strlen(request)); TELOPT_UNANSWERED_SB(TELOPT_NEWENVIRON)=1; } break; #endif /* CK_ENVIRONMENT */ } /* switch */ } else { if (TELOPT_UNANSWERED_DO(x)) TELOPT_UNANSWERED_DO(x) = 0; } break; case WONT: #ifdef CK_SSL if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) return(0); #endif /* CK_SSL */ if (TELOPT_U(x) || TELOPT_UNANSWERED_DO(x)) { /* David Borman says we should not respond DONT when * the WONT is a response to a DO that we sent. * Nor should we send one if the state is already WONT * such as when we do not recognize the option since * options are initialized in the WONT/DONT state. */ if (!(TELOPT_UNANSWERED_DO(x) || TELOPT_UNANSWERED_DONT(x))) if (tn_sopt(DONT,x) < 0) return(-1); if (TELOPT_UNANSWERED_DONT(x)) TELOPT_UNANSWERED_DONT(x) = 0; if (TELOPT_UNANSWERED_DO(x)) TELOPT_UNANSWERED_DO(x) = 0; if (TELOPT_U(x)) { TELOPT_U(x) = 0; } switch(x) { #ifdef CK_SSL case TELOPT_START_TLS: if (sstelnet) { if (TELOPT_U_MODE(x) == TN_NG_MU) { printf("Telnet Start-TLS refused.\n"); ttclos(0); whyclosed = WC_TELOPT; return(-3); } if (TELOPT_SB(x).start_tls.auth_request) { extern int authentication_version; TELOPT_SB(x).start_tls.auth_request = 0; authentication_version = AUTHTYPE_AUTO; ck_tn_auth_request(); } } break; #endif /* CK_SSL */ #ifdef CK_AUTHENTICATION case TELOPT_AUTHENTICATION: if (sstelnet && TELOPT_U_MODE(x) == TN_NG_MU) { printf("Telnet authentication refused.\n"); ttclos(0); whyclosed = WC_TELOPT; return(-3); } else if (TELOPT_U_MODE(x) == TN_NG_RQ) { TELOPT_U_MODE(x) = TN_NG_AC; } if (ck_tn_auth_in_progress()) printf("Telnet authentication refused.\n"); #ifdef CK_ENCRYPTION if (sstelnet) { if (tn_no_encrypt()<0) return(-1); } #endif /* CK_ENCRYPTION */ break; #endif /* CK_AUTHENTICATION */ #ifdef CK_ENCRYPTION case TELOPT_ENCRYPTION: ck_tn_enc_stop(); break; #endif /* CK_ENCRYPTION */ #ifdef IKS_OPTION case TELOPT_KERMIT: TELOPT_SB(x).kermit.u_start = 0; TELOPT_SB(x).kermit.me_req_start = 0; TELOPT_SB(x).kermit.me_req_stop = 0; break; #endif /* IKS_OPTION */ case TELOPT_NAWS: { /* The client does not support NAWS. */ /* Assume a height of 24 and a width of 80 */ if (sstelnet #ifdef IKSD || inserver #endif /* IKSD */ ) { int w = 80, h = 24; #ifndef NOLOCAL if (tcp_incoming) { #ifdef OS2 tt_cols[VTERM] = w; tt_rows[VTERM] = h; VscrnSetWidth(VTERM, w); VscrnSetHeight(VTERM, h+(tt_status[VTERM]?1:0)); #else /* OS2 */ tt_cols = w; tt_rows = h; #endif /* OS2 */ } else { #ifdef OS2 tt_cols[VCMD] = w; tt_rows[VCMD] = h; VscrnSetWidth(VCMD, w); VscrnSetHeight(VCMD, h); #endif /* OS2 */ cmd_cols = w; cmd_rows = h; } #else /* NOLOCAL */ cmd_cols = w; cmd_rows = h; #endif /* NOLOCAL */ /* Add LINES and COLUMNS to the environment */ ckmakmsg((char *)rows_buf,16,"LINES=",ckitoa(h), NULL,NULL); ckmakmsg((char *)cols_buf,16,"COLUMNS=",ckitoa(w), NULL,NULL); #ifdef OS2ORUNIX #ifndef NOPUTENV putenv(rows_buf); putenv(cols_buf); #endif /* NOPUTENV */ #endif /* OS2ORUNIX */ } break; } case TELOPT_ECHO: if (!echo) { if (TELOPT_UNANSWERED_DO(x)) TELOPT_UNANSWERED_DO(x) = 0; return(1); } break; } } else { if (TELOPT_UNANSWERED_DONT(x)) TELOPT_UNANSWERED_DONT(x) = 0; if (TELOPT_UNANSWERED_DO(x)) TELOPT_UNANSWERED_DO(x) = 0; } if (TELOPT_U_MODE(x) == TN_NG_MU) { ckmakmsg( tn_msg,TN_MSG_LEN, "Peer refuses TELNET DO ",TELOPT(x), " negotiations - terminating connection",NULL ); debug(F100,tn_msg,"",0); if (tn_deb || debses) tn_debug(tn_msg); printf("%s\n",tn_msg); ttclos(0); whyclosed = WC_TELOPT; return(-3); } #ifdef COMMENT if (x == TELOPT_ECHO && !echo) /* Special handling for echo */ return(1); /* because we allow 'duplex' */ #endif /* COMMENT */ break; case DO: #ifdef CK_SSL if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) return(0); #endif /* CK_SSL */ if (!TELOPT_OK(x) || TELOPT_ME_MODE(x) == TN_NG_RF) { if (tn_sopt(WONT,x) < 0) return(-1); if (TELOPT_UNANSWERED_WILL(x)) TELOPT_UNANSWERED_WILL(x) = 0; } else if (!TELOPT_ME(x)) { if (!TELOPT_UNANSWERED_WILL(x)) { if (tn_sopt(WILL,x) < 0) return(-1); } if (TELOPT_UNANSWERED_WILL(x)) TELOPT_UNANSWERED_WILL(x) = 0; TELOPT_ME(x) = 1; switch (x) { #ifdef CK_SSL case TELOPT_START_TLS: /* If my proposal is accepted at this point the Telnet protocol is turned off and a TLS negotiation takes place. Start by sending SB START_TLS FOLLOWS to signal we are ready. Wait for the peer to send the same and then start the TLS negotiation. If the TLS negotiation succeeds we call tn_ini() again to reset the telnet state machine and restart the negotiation process over the now secure link. If the TLS negotiation fails, we call ttclos() to terminate the connection. Then we set the U_MODE and ME_MODE for TELOPT_START_TLS to REFUSE and then call ttopen() to create a new connection to the same host but this time do not attempt TLS security. Only the client should receive DO START_TLS. */ tn_ssbopt(TELOPT_START_TLS,1,NULL,0); TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows = 1; break; #endif /* CK_SSL */ #ifdef CK_AUTHENTICATION case TELOPT_AUTHENTICATION: { /* We don't know what authentication we are using yet */ /* but it is not NULL until a failure is detected so */ /* use AUTO in the meantime. */ extern int authentication_version; authentication_version = AUTHTYPE_AUTO; break; } #endif /* CK_AUTHENTICATION */ #ifdef CK_ENCRYPTION case TELOPT_ENCRYPTION: if (!(TELOPT_ME(TELOPT_AUTHENTICATION) || TELOPT_U(TELOPT_AUTHENTICATION)) ) { if (tn_sopt(WONT,x) < 0) return(-1); TELOPT_ME(x) = 0; } else { if (!(TELOPT_U(x) || TELOPT_UNANSWERED_DO(x)) && TELOPT_U_MODE(x) != TN_NG_RF) { if (tn_sopt(DO, x) < 0) return(-1); TELOPT_UNANSWERED_DO(x) = 1; } } break; #endif /* CK_ENCRYPTION */ #ifdef IKS_OPTION case TELOPT_KERMIT: /* If currently processing Kermit server packets, must tell the other side */ debug(F111,"tn_doop","what",what); debug(F111,"tn_doop","server",server); #ifdef CK_AUTODL debug(F111,"tn_doop","autodl",autodl); debug(F111,"tn_doop","inautodl",inautodl); debug(F111,"tn_doop","cmdadl",cmdadl); #endif /* CK_AUTODL */ if (server #ifdef CK_AUTODL || (local && ((what == W_CONNECT && autodl) || (what != W_CONNECT && inautodl))) || (!local && cmdadl) #endif /* CK_AUTODL */ ) { tn_siks(KERMIT_START); /* START */ } if (!TELOPT_U(x)) { /* Tell the other side what Start of Packet Character */ tn_siks(KERMIT_SOP); /* SOP */ if (!TELOPT_UNANSWERED_DO(x) && TELOPT_U_MODE(x) != TN_NG_RF) { if (tn_sopt(DO, x) < 0) return(-1); TELOPT_UNANSWERED_DO(x) = 1; } } break; #endif /* IKS_OPTION */ case TELOPT_BINARY: if (!TELOPT_U(x)) { if (!TELOPT_UNANSWERED_DO(x) && TELOPT_U_MODE(x) >= TN_NG_RQ) { if (tn_sopt(DO, x) < 0) return(-1); TELOPT_UNANSWERED_DO(x) = 1; } } break; case TELOPT_NAWS: #ifdef CK_NAWS if ( !tn_delay_sb || !tn_outst(0) || tn_init ) { if (tn_snaws() < 0) return(-1); } else { TELOPT_SB(TELOPT_NAWS).naws.need_to_send = 1; } #endif /* CK_NAWS */ break; case TELOPT_LOGOUT: ttclos(0); /* And then hangup */ whyclosed = WC_TELOPT; return(6); #ifdef CK_SNDLOC case TELOPT_SNDLOC: if ( !tn_delay_sb || !tn_outst(0) || tn_init ) { if (tn_sndloc() < 0) return(-1); } else { TELOPT_SB(TELOPT_SNDLOC).sndloc.need_to_send = 1; } break; #endif /* CK_SNDLOC */ #ifdef CK_FORWARD_X case TELOPT_FORWARD_X: if ( !tn_delay_sb || !tn_outst(0) || tn_init ) { if (fwdx_send_options() < 0) { if (tn_sopt(DONT,x) < 0) return(-1); TELOPT_UNANSWERED_DONT(x) = 1; } } else { TELOPT_SB(TELOPT_FORWARD_X).forward_x.need_to_send = 1; } break; #endif /* CK_FORWARD_X */ #ifdef TN_COMPORT case TELOPT_COMPORT: { extern int reliable; if (!tn_delay_sb || !tn_outst(0) || tn_init) { if (tn_sndcomport() < 0) return(-1); } else { TELOPT_SB(TELOPT_COMPORT).comport.need_to_send = 1; } /* Telnet -> Serial -> ??? is not a reliable connection. */ reliable = SET_OFF; break; } #endif /* TN_COMPORT */ } /* switch */ } else { if (TELOPT_UNANSWERED_WILL(x)) TELOPT_UNANSWERED_WILL(x) = 0; } break; case DONT: #ifdef CK_SSL if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) return(0); #endif /* CK_SSL */ if (TELOPT_ME(x) || TELOPT_UNANSWERED_WILL(x)) { /* David Borman says we should not respond WONT when * the DONT is a response to a WILL that we sent. * Nor should we send one if the state is already WONT * such as when we do not recognize the option since * options are initialized in the WONT/DONT state. */ if (!(TELOPT_UNANSWERED_WILL(x) || TELOPT_UNANSWERED_WONT(x))) if (tn_sopt(WONT,x) < 0) return(-1); if (TELOPT_UNANSWERED_WILL(x)) TELOPT_UNANSWERED_WILL(x) = 0; if (TELOPT_UNANSWERED_WONT(x)) TELOPT_UNANSWERED_WONT(x) = 0; if (TELOPT_ME(x)) TELOPT_ME(x) = 0; switch (x) { #ifdef CK_SSL case TELOPT_START_TLS: if (!sstelnet && TELOPT_ME_MODE(x) == TN_NG_MU) { printf("Telnet Start-TLS refused.\n"); ttclos(0); whyclosed = WC_TELOPT; return(-3); } break; #endif /* CK_SSL */ #ifdef CK_AUTHENTICATION case TELOPT_AUTHENTICATION: if (!sstelnet && TELOPT_ME_MODE(x) == TN_NG_MU) { #ifdef CK_SSL if (tls_active_flag) { TELOPT_ME_MODE(x) = TN_NG_AC; break; } else #endif /* CK_SSL */ { printf("Telnet authentication refused.\n"); ttclos(0); whyclosed = WC_TELOPT; return(-3); } } else if (TELOPT_ME_MODE(x) == TN_NG_RQ) { TELOPT_ME_MODE(x) = TN_NG_AC; } if (ck_tn_auth_in_progress()) printf("Telnet authentication refused.\n"); #ifdef CK_ENCRYPTION if (!sstelnet) { if (tn_no_encrypt()<0) return(-1); } #endif /* CK_ENCRYPTION */ break; #endif /* CK_AUTHENTICATION */ case TELOPT_ENCRYPTION: #ifdef CK_ENCRYPTION ck_tn_enc_stop(); #endif /* CK_ENCRYPTION */ break; case TELOPT_KERMIT: #ifdef IKS_OPTION TELOPT_SB(x).kermit.me_start = 0; #endif /* IKS_OPTION */ break; default: break; } /* switch */ } else { if (TELOPT_UNANSWERED_WILL(x)) TELOPT_UNANSWERED_WILL(x) = 0; if (TELOPT_UNANSWERED_WONT(x)) TELOPT_UNANSWERED_WONT(x) = 0; } if (TELOPT_ME_MODE(x) == TN_NG_MU) { ckmakmsg( tn_msg,TN_MSG_LEN, "Peer refuses TELNET WILL ",TELOPT(x), " negotiations - terminating connection", NULL ); debug(F100,tn_msg,"",0); if (tn_deb || debses) tn_debug(tn_msg); printf("%s\n",tn_msg); ttclos(0); whyclosed = WC_TELOPT; return(-3); } break; case SB: if ((y = tn_sb(x,&n,fn)) <= 0) return(y); #ifdef CK_SSL /* Do not process subnegotiations other than START_TLS after we */ /* have agreed to begin the TLS negotiation sequence. */ if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows && x != TELOPT_START_TLS) break; #endif /* CK_SSL */ if (!TELOPT_OK(x)) { ckhexdump("unknown telnet subnegotiation",sb,n); break; } else if ( !(TELOPT_ME(x) || TELOPT_U(x)) ) { ckhexdump("telnet option not negotiated",sb,n); if (!tn_sb_bug) break; if (TELOPT_UNANSWERED_WILL(x)) { TELOPT_UNANSWERED_WILL(x) = 0; TELOPT_U(x) = 1; ckmakmsg(tn_msg,TN_MSG_LEN, "TELNET DO ",TELOPT(x), "(implied by receipt of SB - protocol error ignored)", NULL ); debug(F100,tn_msg,"",0); if (tn_deb || debses) tn_debug(tn_msg); } if (TELOPT_UNANSWERED_DO(x)) { TELOPT_UNANSWERED_DO(x) = 0; TELOPT_ME(x) = 1; ckmakmsg(tn_msg,TN_MSG_LEN,"TELNET WILL ",TELOPT(x), " (implied by receipt of SB - protocol error ignored)", NULL); debug(F100,tn_msg,"",0); if (tn_deb || debses) tn_debug(tn_msg); } } TELOPT_UNANSWERED_SB(x)=0; switch (x) { #ifdef CK_FORWARD_X case TELOPT_FORWARD_X: return(fwdx_tn_sb(sb, n)); #endif /* CK_FORWARD_X */ #ifdef CK_SSL case TELOPT_START_TLS: { /* the other side is saying SB START_TLS FOLLOWS the incoming channel is now ready for starting the TLS negotiation. */ int def_tls_u_mode, def_tls_me_mode; int def_enc_u_mode, def_enc_me_mode; int rc = 0; if (sb[0] != 1) { break; } TELOPT_SB(TELOPT_START_TLS).start_tls.u_follows = 1; /* Preserve the default modes and make sure we will */ /* refuse START_TLS when we retry. */ if (sstelnet) { def_tls_u_mode = TELOPT_DEF_S_U_MODE(TELOPT_START_TLS); def_tls_me_mode = TELOPT_DEF_S_ME_MODE(TELOPT_START_TLS); TELOPT_DEF_S_U_MODE(TELOPT_START_TLS) = TN_NG_RF; TELOPT_DEF_S_ME_MODE(TELOPT_START_TLS)= TN_NG_RF; #ifdef CK_ENCRYPTION def_enc_u_mode = TELOPT_DEF_S_U_MODE(TELOPT_ENCRYPTION); def_enc_me_mode = TELOPT_DEF_S_ME_MODE(TELOPT_ENCRYPTION); TELOPT_DEF_S_U_MODE(TELOPT_ENCRYPTION) = TN_NG_RF; TELOPT_DEF_S_ME_MODE(TELOPT_ENCRYPTION)= TN_NG_RF; #endif /* CK_ENCRYPTION */ } else { def_tls_u_mode = TELOPT_DEF_C_U_MODE(TELOPT_START_TLS); def_tls_me_mode = TELOPT_DEF_C_ME_MODE(TELOPT_START_TLS); TELOPT_DEF_C_U_MODE(TELOPT_START_TLS) = TN_NG_RF; TELOPT_DEF_C_ME_MODE(TELOPT_START_TLS)= TN_NG_RF; #ifdef CK_ENCRYPTION def_enc_u_mode = TELOPT_DEF_C_U_MODE(TELOPT_ENCRYPTION); def_enc_me_mode = TELOPT_DEF_C_ME_MODE(TELOPT_ENCRYPTION); TELOPT_DEF_C_U_MODE(TELOPT_ENCRYPTION) = TN_NG_RF; TELOPT_DEF_C_ME_MODE(TELOPT_ENCRYPTION)= TN_NG_RF; #endif /* CK_ENCRYPTION */ } /* Negotiate TLS */ ttnproto = NP_TLS; tn_init = 0; tn_begun = 0; if (ck_tn_tls_negotiate()<0) { /* we failed. disconnect and if we are the client */ /* then reconnect and try without START_TLS. */ extern char * line; int x = -1; extern int mdmtyp; if (sstelnet) { printf("TLS failed: Disconnecting.\n"); TELOPT_DEF_S_U_MODE(TELOPT_START_TLS) = def_tls_u_mode; TELOPT_DEF_S_ME_MODE(TELOPT_START_TLS) = def_tls_me_mode; #ifdef CK_ENCRYPTION TELOPT_DEF_S_U_MODE(TELOPT_ENCRYPTION) = def_enc_u_mode; TELOPT_DEF_S_ME_MODE(TELOPT_ENCRYPTION) = def_enc_me_mode; #endif /* CK_ENCRYPTION */ ttclos(0); whyclosed = WC_TELOPT; ttnproto = NP_TELNET; rc = -3; } else { #ifndef NOLOCAL extern int tls_norestore; #endif /* NOLOCAL */ printf("TLS failed: Disconnecting...\n"); #ifdef CK_ENCRYPTION TELOPT_DEF_C_U_MODE(TELOPT_ENCRYPTION) = def_enc_u_mode; TELOPT_DEF_C_ME_MODE(TELOPT_ENCRYPTION) = def_enc_me_mode; #endif /* CK_ENCRYPTION */ /* if START_TLS is not REQUIRED, then retry without it */ if ( def_tls_me_mode != TN_NG_MU ) { extern char ttname[]; #ifndef NOLOCAL tls_norestore = 1; #endif /* NOLOCAL */ ttclos(0); whyclosed = WC_TELOPT; #ifndef NOLOCAL tls_norestore = 0; #endif /* NOLOCAL */ ttnproto = NP_TELNET; printf("Reconnecting without TLS.\n"); sleep(2); if (ttopen(ttname,&x,mdmtyp,0)<0) rc = -3; } else { TELOPT_DEF_C_U_MODE(TELOPT_START_TLS) = def_tls_u_mode; TELOPT_DEF_C_ME_MODE(TELOPT_START_TLS) = def_tls_me_mode; ttclos(0); whyclosed = WC_TELOPT; ttnproto = NP_TELNET; rc = -3; } } } else { #ifdef CK_AUTHENTICATION /* we succeeded. restart telnet negotiations from */ /* the beginning. However, if we have received a */ /* client certificate and we are a server, then do */ /* not offer TELOPT_AUTH. */ if ( ck_tn_auth_valid() == AUTH_VALID ) { TELOPT_DEF_S_U_MODE(TELOPT_AUTHENTICATION) = TN_NG_AC; TELOPT_DEF_S_ME_MODE(TELOPT_AUTHENTICATION)= TN_NG_AC; } #endif /* CK_AUTHENTICATION */ ttnproto = NP_TELNET; if (tn_ini() < 0) if (ttchk() < 0) rc = -1; } /* Restore the default modes */ if (sstelnet) { TELOPT_DEF_S_U_MODE(TELOPT_START_TLS) = def_tls_u_mode; TELOPT_DEF_S_ME_MODE(TELOPT_START_TLS) = def_tls_me_mode; #ifdef CK_ENCRYPTION TELOPT_DEF_S_U_MODE(TELOPT_ENCRYPTION) = def_enc_u_mode; TELOPT_DEF_S_ME_MODE(TELOPT_ENCRYPTION) = def_enc_me_mode; #endif /* CK_ENCRYPTION */ } else { TELOPT_DEF_C_U_MODE(TELOPT_START_TLS) = def_tls_u_mode; TELOPT_DEF_C_ME_MODE(TELOPT_START_TLS) = def_tls_me_mode; #ifdef CK_ENCRYPTION TELOPT_DEF_C_U_MODE(TELOPT_ENCRYPTION) = def_enc_u_mode; TELOPT_DEF_C_ME_MODE(TELOPT_ENCRYPTION) = def_enc_me_mode; #endif /* CK_ENCRYPTION */ } return(rc); } #endif /* CK_SSL */ #ifdef CK_AUTHENTICATION case TELOPT_AUTHENTICATION: if (ck_tn_sb_auth((char *)sb,n) < 0) { if (sstelnet && TELOPT_U_MODE(x) == TN_NG_MU) { ttclos(0); whyclosed = WC_TELOPT; return(-3); } else if (!sstelnet && TELOPT_ME_MODE(x) == TN_NG_MU) { ttclos(0); whyclosed = WC_TELOPT; return(-3); } else { if (TELOPT_ME_MODE(x) == TN_NG_RQ) TELOPT_ME_MODE(x) = TN_NG_AC; if (TELOPT_U_MODE(x) == TN_NG_RQ) TELOPT_U_MODE(x) = TN_NG_AC; } if (TELOPT_ME(x)) { TELOPT_ME(x) = 0; if (tn_sopt(WONT,x) < 0) return(-1); } if (TELOPT_U(x)) { TELOPT_U(x) = 0; if (tn_sopt(DONT,x) < 0) return(-1); } #ifdef CK_ENCRYPTION if (tn_no_encrypt()<0) return(-1); #endif /* CK_ENCRYPTION */ } else { #ifdef CK_ENCRYPTION if (!ck_tn_auth_in_progress()) { /* we are finished */ if (ck_tn_authenticated() == AUTHTYPE_SSL) { /* TLS was successful. Disable ENCRYPTION */ TELOPT_U_MODE(TELOPT_ENCRYPTION) = TN_NG_RF; TELOPT_ME_MODE(TELOPT_ENCRYPTION) = TN_NG_RF; } if (TELOPT_SB(TELOPT_ENCRYPTION).encrypt.need_to_send) { ck_encrypt_send_support(); TELOPT_SB(TELOPT_ENCRYPTION).encrypt.need_to_send = 0; } } #endif /* CK_ENCRYPTION */ } break; #endif /* CK_AUTHENTICATION */ #ifdef CK_ENCRYPTION case TELOPT_ENCRYPTION: if (ck_tn_sb_encrypt((char *)sb, n) < 0) { if (TELOPT_U_MODE(x) == TN_NG_MU || TELOPT_ME_MODE(x) == TN_NG_MU) { ttclos(0); whyclosed = WC_TELOPT; return(-3); } else { if (TELOPT_ME_MODE(x) == TN_NG_RQ) TELOPT_ME_MODE(x) = TN_NG_AC; if (TELOPT_U_MODE(x) == TN_NG_RQ) TELOPT_U_MODE(x) = TN_NG_AC; } if (TELOPT_ME(x)) { TELOPT_ME(x) = 0; if (tn_sopt(WONT,x) < 0) return(-1); } if (TELOPT_U(x)) { TELOPT_U(x) = 0; if (tn_sopt(DONT,x) < 0) return(-1); } } break; #endif /* CK_ENCRYPTION */ #ifdef IKS_OPTION case TELOPT_KERMIT: return(iks_tn_sb(sb, n-2)); #endif /* IKS_OPTION */ #ifdef TN_COMPORT case TELOPT_COMPORT: return(tnc_tn_sb(sb, n-2)); #endif /* TN_COMPORT */ case TELOPT_TTYPE: switch (sb[0]) { case TELQUAL_SEND: /* SEND terminal type? */ if ( !tn_delay_sb || !tn_outst(0) || tn_init ) { if (tn_sttyp() < 0) /* Yes, so send it. */ return(-1); } else { TELOPT_SB(TELOPT_TTYPE).term.need_to_send = 1; } break; case TELQUAL_IS: { /* IS terminal type? */ /* IS terminal type -- remote gave us its current type */ int i = 0; #ifndef OS2 CHAR oldterm[64], *p; #endif /* OS2 */ /* Isolate the specified terminal type string */ while (sb[i++] != IAC) { if (i == 40 || /* max len of term string - RFC */ sb[i] == IAC) { sb[i] = '\0'; break; } } #ifdef OS2 #ifndef NOTERM strupr(&(sb[1])); /* Upper case it */ for (i = 0; i <= max_tt; i++) { /* find it in our list */ if (!strcmp(&(sb[1]),tt_info[i].x_name) && i != TT_VTNT) /* can't support VTNT as server */ { /* Set terminal type to the one chosen */ if (i != tt_type) settermtype(i,0); break; } } if (i > max_tt && strcmp(&(sb[1]),TELOPT_SB(TELOPT_TTYPE).term.type)) { /* Couldn't find the specified term type */ sb[40] = '\0'; strcpy(TELOPT_SB(TELOPT_TTYPE).term.type,&(sb[1])); /* SB TTYPE SEND */ tn_ssbopt(TELOPT_TTYPE,TELQUAL_SEND,NULL,0); TELOPT_UNANSWERED_SB(TELOPT_TTYPE)=1; } #endif /* NOTERM */ #else /* OS2 */ p = (CHAR *) getenv("TERM"); if (p) ckstrncpy((char *)oldterm,(char *)p,63); else oldterm[0] = '\0'; cklower((char *)&(sb[1])); /* Lower case new term */ ckmakmsg(term_buf,64,"TERM=",(char *)&(sb[1]),NULL,NULL); #ifdef OS2ORUNIX #ifndef NOPUTENV putenv(term_buf); #endif /* NOPUTENV */ #endif /* OS2ORUNIX */ #ifdef CK_CURSES #ifndef MYCURSES #ifndef COHERENT if (trmbuf) { if (tgetent(trmbuf,(char *)&sb[1]) < 1) { /* Unsupported terminal. If new and old terminal */ /* types do not match, ask for another type. */ if (strcmp((char *)oldterm,(char *)&sb[1])) { /* SB TTYPE SEND */ tn_ssbopt(TELOPT_TTYPE,TELQUAL_SEND,NULL,0); TELOPT_UNANSWERED_SB(TELOPT_TTYPE)=1; } } } #endif /* COHERENT */ #endif /* MYCURSES */ #endif /* CK_CURSES */ #endif /* OS2 */ } } break; #ifdef CK_ENVIRONMENT #ifdef CK_XDISPLOC case TELOPT_XDISPLOC: /* Send X-Display Location */ if (sb[0] == TELQUAL_SEND) {/* SEND X-Display Loc? */ if ( !tn_delay_sb || !tn_outst(0) || tn_init ) { if (tn_sxdisploc() < 0) /* Yes, so send it. */ return(-1); } else { TELOPT_SB(TELOPT_XDISPLOC).xdisp.need_to_send = 1; } } /* IS -- X Display Location (not supported) */ else if (sb[0] == TELQUAL_IS) { int i = 0; /* Isolate the specified X-display string */ while (sb[i++] != IAC) { if (i >= TSBUFSIZ) return (-1); if (sb[i] == IAC) { sb[i] = '\0'; break; } } debug(F110,"TELNET SB XDISPLOC IS",&sb[1],0); } break; #endif /* CK_XDISPLOC */ #endif /* CK_ENVIRONMENT */ case TELOPT_NAWS: if (sstelnet #ifdef IKSD || inserver #endif /* IKSD */ ) { int w = 0, h = 0; int i = 0; /* At this point sb[] should contain width and height */ if (sb[i] == IAC) i++; w = (sb[i++] << 8); /* save upper height */ if (sb[i] == IAC) i++; w += sb[i++]; /* save the width */ if (sb[i] == IAC) i++; h = (sb[i++] << 8); /* save upper height */ if (sb[i] == IAC) i++; h += sb[i++]; debug(F111,"tn_doop NAWS SB","width",w); debug(F111,"tn_doop NAWS SB","height",h); if (w == 0) w = 80; if (h == 0) h = 24; #ifndef NOLOCAL if (tcp_incoming || inserver) { #ifdef OS2 tt_cols[VTERM] = w; tt_rows[VTERM] = h; VscrnSetWidth(VTERM, w); VscrnSetHeight(VTERM, h+(tt_status[VTERM]?1:0)); #ifdef IKSD if (inserver) { cmd_cols = tt_cols[VCMD] = w; cmd_rows = tt_rows[VCMD] = h; VscrnSetWidth(VCMD, w); VscrnSetHeight(VCMD, h); } #endif /* IKSD */ #else /* OS2 */ tt_cols = w; tt_rows = h; #endif /* OS2 */ } else { #ifdef OS2 tt_cols[VCMD] = w; tt_rows[VCMD] = h; VscrnSetWidth(VCMD, w); VscrnSetHeight(VCMD, h); #endif /* OS2 */ cmd_cols = w; cmd_rows = h; } #else /* NOLOCAL */ cmd_cols = w; cmd_rows = h; #endif /* NOLOCAL */ /* Add LINES and COLUMNS to the environment */ ckmakmsg((char *)rows_buf,16,"LINES=",ckitoa(h),NULL,NULL); ckmakmsg((char *)cols_buf,16,"COLUMNS=",ckitoa(w),NULL,NULL); #ifdef OS2ORUNIX #ifndef NOPUTENV putenv(rows_buf); putenv(cols_buf); #endif /* NOPUTENV */ #endif /* OS2ORUNIX */ } break; #ifdef CK_ENVIRONMENT case TELOPT_NEWENVIRON: switch (sb[0]) { case TELQUAL_IS: /* IS */ case TELQUAL_INFO: /* INFO */ if (sb[0] == TELQUAL_IS) debug(F101,"tn_doop NEW-ENV SB IS","",n-3); else debug(F101,"tn_doop NEW-ENV SB INFO","",n-3); if (sstelnet || inserver) { /* Yes, receive it. */ if (tn_rnenv((CHAR *)&sb[1],n-3) < 0) return(-1); } break; case TELQUAL_SEND: /* SEND */ if ( sstelnet || inserver ) /* ignore if server */ break; /* We need to take the sb[] and build a structure */ /* containing all of the variables and types that */ /* we are supposed to keep track of and send to */ /* the host, then call tn_snenv(). */ /* Or we can punt ... */ if ( !tn_delay_sb || !tn_outst(0) || tn_init ) { if (tn_snenv((CHAR *)&sb[1],n-3) < 0) /* Yes, send it. */ return(-1); } else { #ifndef VMS CHAR * xxx; xxx = (CHAR *) malloc(n-1); #else unsigned char * xxx; xxx = (unsigned char *) malloc(n-1); #endif /* VMS */ /* Postpone sending until end of tn_ini() */ TELOPT_SB(TELOPT_NEWENVIRON).env.str = xxx; if (TELOPT_SB(TELOPT_NEWENVIRON).env.str) { memcpy((char *)TELOPT_SB(TELOPT_NEWENVIRON).env.str, (char *)&sb[1],n-3); TELOPT_SB(TELOPT_NEWENVIRON).env.str[n-3] = IAC; TELOPT_SB(TELOPT_NEWENVIRON).env.str[n-2] = '\0'; TELOPT_SB(TELOPT_NEWENVIRON).env.len = n-3; TELOPT_SB(TELOPT_NEWENVIRON).env.need_to_send = 1; } } break; } break; #endif /* CK_ENVIRONMENT */ #ifdef CK_SNDLOC case TELOPT_SNDLOC: { if ( deblog ) { sb[n-2] = '\0'; debug(F110,"TELNET Send-Location",sb,0); } break; } #endif /* CK_SNDLOC */ } /* switch */ break; } return(0); } int #ifdef CK_ANSIC /* TELNET DO OPTION */ tn_doop(CHAR z, int echo, int (*fn)(int)) #else tn_doop(z, echo, fn) CHAR z; int echo; int (*fn)(); #endif /* CK_ANSIC */ /* tn_doop */ { int x=0, y=0; debug(F101,"tn_doop char","",z); debug(F101,"tn_doop ttnproto","",ttnproto); if (!IS_TELNET()) return(3); #ifdef CK_SSL debug(F101,"tn_doop ssl_raw_flag","",ssl_raw_flag); if (ssl_raw_flag || tls_raw_flag) return(7); #endif /* CK_SSL */ debug(F100,"tn_doop ttnproto proceeding...","",0); if (z != (CHAR) IAC) { debug(F101,"tn_doop bad call","",z); return(-1); } if (ttnet != NET_TCPB) /* Check network type */ return(0); if (ttnproto != NP_TELNET && ttnproto != NP_NONE) /* Check protocol */ return(0); x = tn_xdoop(z,echo,fn); if (x >= 0 && !tn_begun) { y = tn_start(); } return(y < 0 ? y : x); } #ifdef CK_ENVIRONMENT /* Telnet receive new environment */ /* Returns -1 on error, 0 if nothing happens, 1 on success */ /* In order for this code to work, sb[len] == IAC */ /* We currently only support the USER environment variable */ int #ifdef CK_ANSIC tn_rnenv(CHAR * sb, int len) #else tn_rnenv(sb, len) CHAR * sb; int len; #endif /* CK_ANSIC */ /* tn_rnenv */ { /* Receive new environment */ char varname[17]; char value[65]; char * reply = 0, * s = 0; int i,j,k,n; /* Worker. */ int type = 0; /* 0 for NONE, 1 for VAR, 2 for USERVAR, */ /* 3 for VALUE in progress */ if (ttnet != NET_TCPB) return(0); if (ttnproto != NP_TELNET) return(0); if (sb == NULL) return(-1); if (len == 0) return(1); /* Pairs of [VAR=0, VALUE=1, ESC=2, USERVAR=3] "unterminated" follow here until done... */ for (i = 0, j = 0, k = 0, type = 0, varname[0]= '\0'; i <= len; i++) { switch (sb[i]) { case TEL_ENV_VAR: /* VAR */ case TEL_ENV_USERVAR: /* USERVAR */ case IAC: /* End of the list */ switch (type) { case 0: /* Nothing in progress */ /* If we get IAC only, then that means there were */ /* no environment variables to send. we are done */ if (j == 0 && sb[i] == IAC) return(1); case 1: /* VAR in progress */ case 2: /* USERVAR in progress */ case 3: /* VALUE in progress */ value[k] = '\0'; varname[j] = '\0'; debug(F111,"tn_rnenv varname",varname,type); debug(F111,"tn_rnenv value",value,type); if (!strcmp(varname,"USER")) { #ifdef CK_AUTHENTICATION if (ck_tn_auth_valid() != AUTH_VALID) { extern char szUserNameRequested[]; debug(F100,"tn_rnenv != AUTH_VALID","",0); ckstrncpy(szUserNameRequested,value,UIDBUFLEN); ckstrncpy(uidbuf,value,UIDBUFLEN); #ifdef CK_SSL if (ssl_active_flag) { if ( tls_is_user_valid(ssl_con, uidbuf) ) { extern char szUserNameAuthenticated[]; ckstrncpy(szUserNameAuthenticated,uidbuf, UIDBUFLEN); auth_finished(AUTH_VALID); } } else if (tls_active_flag) { if ( tls_is_user_valid(tls_con, uidbuf) ) { extern char szUserNameAuthenticated[]; ckstrncpy(szUserNameAuthenticated,uidbuf, UIDBUFLEN); auth_finished(AUTH_VALID); } } #endif /* CK_SSL */ } else { /* AUTH_VALID */ int x = 0; debug(F110,"tn_rnenv AUTH_VALID uidbuf",uidbuf,0); #ifdef OS2 x = ckstrcmp(value,uidbuf,-1,0); /* case insensitive */ #ifdef NT /* NTLM authentication returns names of the form */ /* DOMAIN\user. We need to check to see of the */ /* USER VAR contains a domain name or not. If */ /* not, then we do not want to change state if */ /* the uidbuf matches the USER VAR when the */ /* DOMAIN is ignored. */ if ( x && ck_tn_authenticated() == AUTHTYPE_NTLM ) { char * s1=NULL, * s2=NULL; int len1, len2, i; len1 = strlen(value); for ( i=len1-1 ; i>=0 ; i--) { if ( value[i] == '\\' ) { s1 = &value[i+1]; /* DOMAIN found */ break; } } if ( s1 == NULL ) { len2 = strlen(uidbuf); for ( i=len2-1 ; i>=0 ; i--) { if ( uidbuf[i] == '\\' ) { s2 = &uidbuf[i+1]; /* DOMAIN found */ break; } } if ( s2 ) x = ckstrcmp(value,s2,-1,0); } } #endif /* NT */ #else /* OS2 */ x = ckstrcmp(value,uidbuf,-1,1); /* case sensitive */ #endif /* OS2 */ if ( x ) { extern char szUserNameRequested[]; ckstrncpy(uidbuf,value,UIDBUFLEN); ckstrncpy(szUserNameRequested,value,UIDBUFLEN); auth_finished(AUTH_USER); #ifdef CK_SSL if (ssl_active_flag || tls_active_flag) { if ( tls_is_user_valid(ssl_con, uidbuf) ) auth_finished(AUTH_VALID); } #endif /* CK_SSL */ } } #else /* CK_AUTHENTICATION */ ckstrncpy(uidbuf,value,UIDBUFLEN); #endif /* CK_AUTHENTICATION */ } break; } varname[0] = '\0'; value[0] = '\0'; j = 0; k = 0; type = (sb[i] == TEL_ENV_USERVAR ? 2 : /* USERVAR */ sb[i] == TEL_ENV_VAR ? 1 : /* VAR */ 0 ); break; case TEL_ENV_VALUE: /* VALUE */ if ( type == 1 || type == 2 ) type = 3; break; case TEL_ENV_ESC: /* ESC */ /* Take next character literally */ if ( ++i >= len ) break; /* otherwise, fallthrough so byte will be added to string. */ default: switch (type) { case 1: /* VAR in progress */ case 2: /* USERVAR in progress */ if ( j < 16 ) varname[j++] = sb[i]; break; case 3: if ( k < 64 ) value[k++] = sb[i]; break; } } } return(0); } /* These are for Microsoft SFU version 2 Telnet Server */ #define SFUTLNTVER "SFUTLNTVER" #define SFUTLNTMODE "SFUTLNTMODE" #define SFUTLNTVER_VALUE "2" #define SFUTLNTMODE_VALUE "console" /* The other value is "stream" */ /* Telnet send new environment */ /* Returns -1 on error, 0 if nothing happens, 1 on success */ /* In order for this code to work, sb[len] == IAC */ int #ifdef CK_ANSIC tn_snenv(CHAR * sb, int len) #else tn_snenv(sb, len) CHAR * sb; int len; #endif /* CK_ANSIC */ /* tn_snenv */ { /* Send new environment */ char varname[16]; char * reply = 0, * s = 0; int i,j,n; /* Worker. */ int type = 0; /* 0 for NONE, 1 for VAR, 2 for USERVAR in progress */ extern int ck_lcname; char localuidbuf[UIDBUFLEN]; /* (Initialized just below) */ char * uu = uidbuf; char * disp = NULL; localuidbuf[0] = '\0'; if (ttnet != NET_TCPB) return(0); if (ttnproto != NP_TELNET) return(0); if (!sb) return(-1); #ifdef CK_SSL if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) { return(0); } #endif /* CK_SSL */ #ifdef CK_FORWARD_X if (TELOPT_U(TELOPT_FORWARD_X)) { disp = NULL; } else #endif /* CK_FORWARD_X */ disp = (char *)tn_get_display(); if (ck_lcname) { ckstrncpy(localuidbuf,uidbuf,UIDBUFLEN); cklower(localuidbuf); uu = localuidbuf; } ckhexdump((CHAR *)"tn_snenv sb[]",sb,len); debug(F110,"tn_snenv uidbuf",uidbuf,0); debug(F110,"tn_snenv localuidbuf",localuidbuf,0); debug(F110,"tn_snenv tn_env_sys",tn_env_sys,0); debug(F110,"tn_snenv tn_env_disp",tn_env_disp,0); debug(F110,"tn_snenv disp",disp,0); /* First determine the size of the buffer we will need */ for (i = 0, j = 0, n = 0, type = 0, varname[0]= '\0'; i <= len; i++) { switch (sb[i]) { case TEL_ENV_VAR: /* VAR */ case TEL_ENV_USERVAR: /* USERVAR */ case IAC: /* End of the list */ switch (type) { case 0: /* Nothing in progress */ /* If we get IAC only, then that means send all */ /* VAR and USERVAR. */ if (!(j == 0 && sb[i] == IAC)) break; case 1: /* VAR in progress */ varname[j] = '\0' ; if (!varname[0]) { /* Send All */ if (uu[0]) n += strlen(uu) + 4 + 2; if (tn_env_job[0]) n += strlen(tn_env_job) + 3 + 2; if (tn_env_acct[0]) n += strlen(tn_env_acct) + 4 + 2; if (tn_env_prnt[0]) n += strlen(tn_env_prnt) + 7 + 2; if (tn_env_sys[0]) n += strlen(tn_env_sys) + 10 + 2; if (disp) n += strlen(disp) + 7 + 2; } else if (!strcmp(varname,"USER") && uu[0]) n += strlen(uu) + 4 + 2; else if (!strcmp(varname,"JOB") && tn_env_job[0]) n += strlen(tn_env_job) + 3 + 2; else if (!strcmp(varname,"ACCT") && tn_env_acct[0]) n += strlen(tn_env_acct) + 4 + 2; else if (!strcmp(varname,"PRINTER") && tn_env_prnt[0]) n += strlen(tn_env_prnt) + 7 + 2; else if (!strcmp(varname,"SYSTEMTYPE") && tn_env_sys[0]) n += strlen(tn_env_sys) + 10 + 2; else if (!strcmp(varname,"DISPLAY") && disp) n += strlen(disp) + 7 + 2; /* If we get IAC only, then that means send all */ /* VAR and USERVAR. */ if (!(j == 0 && sb[i] == IAC)) break; case 2: /* USERVAR in progress */ varname[j] = '\0' ; if (!varname[0]) { /* Send All */ int x; for ( x=0 ; x<8 ; x++ ) { if ( tn_env_uservar[x][0] && tn_env_uservar[x][1] ) n += strlen(tn_env_uservar[x][0]) + strlen(tn_env_uservar[x][1]) + 2; } if ( tn_sfu ) { /* For compatibility with Microsoft Telnet Server */ n += strlen(SFUTLNTVER) + strlen(SFUTLNTVER_VALUE) + 2; n += strlen(SFUTLNTMODE) + strlen(SFUTLNTMODE_VALUE) + 2; } #ifdef CK_SNDLOC if ( tn_loc && tn_loc[0] ) n += strlen("LOCATION") + strlen(tn_loc) + 2; #endif /* CK_SNDLOC */ } else if (tn_sfu && !strcmp(varname,SFUTLNTVER)) n += strlen(SFUTLNTVER) + strlen(SFUTLNTVER_VALUE) + 2; else if (tn_sfu && !strcmp(varname,SFUTLNTMODE)) n += strlen(SFUTLNTMODE) + strlen(SFUTLNTMODE_VALUE) + 2; #ifdef CK_SNDLOC else if ( tn_loc && tn_loc[0] && !strcmp(varname,"LOCATION")) n += strlen("LOCATION") + strlen(tn_loc) + 2; #endif /* CK_SNDLOC */ else { int x; for ( x=0 ; x<8 ; x++ ) { if ( tn_env_uservar[x][0] && tn_env_uservar[x][1] && !strcmp(varname,tn_env_uservar[x][0])) n += strlen(tn_env_uservar[x][0]) + strlen(tn_env_uservar[x][1]) + 2; } } break; } varname[0] = '\0'; j = 0; type = (sb[i] == TEL_ENV_USERVAR ? 2 : /* USERVAR */ sb[i] == TEL_ENV_VAR ? 1 : /* VAR */ 0 ); break; case TEL_ENV_VALUE: /* VALUE */ /* Protocol Error */ debug(F100, "TELNET Subnegotiation error - VALUE in SEND", "",0); if (tn_deb || debses) tn_debug("TELNET Subnegotiation error - VALUE in SEND"); return(0); case TEL_ENV_ESC: /* ESC */ if (++i >= len) break; default: if (j < 16 ) varname[j++] = sb[i]; } } reply = malloc(n + 16); /* Leave room for IAC stuff */ if (!reply) { debug(F100, "TELNET Subnegotiation error - malloc failed", "",0); if (tn_deb || debses) tn_debug("TELNET Subnegotiation error - malloc failed"); /* Send a return packet with no variables so that the host */ /* may continue with additional negotiations */ if (tn_ssbopt(TELOPT_NEWENVIRON,TELQUAL_IS,"",0) < 0) return(-1); return(0); } /* Now construct the real reply */ n = 0; /* Start at beginning of buffer */ /* Pairs of [VAR=0, VALUE=1, ESC=2, USERVAR=3] "unterminated" follow here until done... */ for (i = 0, j = 0, type = 0, varname[0]= '\0'; i <= len; i++) { switch (sb[i]) { case TEL_ENV_VAR: /* VAR */ case TEL_ENV_USERVAR: /* USERVAR */ case IAC: /* End of the list */ switch (type) { case 0: /* Nothing in progress */ /* If we get IAC only, then that means send all */ /* VAR and USERVAR. */ if (!(j == 0 && sb[i] == IAC)) break; case 1: /* VAR in progress */ varname[j] = '\0'; if (!varname[0]) { /* Send All */ if (uu[0]) { reply[n] = TEL_ENV_VAR; /* VAR */ strcpy(&reply[n+1],"USER"); reply[n+5] = TEL_ENV_VALUE; /* VALUE */ strcpy(&reply[n+6],uu); n += strlen(uu) + 4 + 2; } if (tn_env_job[0]) { reply[n] = TEL_ENV_VAR; /* VAR */ strcpy(&reply[n+1],"JOB"); reply[n+4] = TEL_ENV_VALUE; /* VALUE */ strcpy(&reply[n+5],tn_env_job); n += strlen(tn_env_job) + 3 + 2; } if (tn_env_acct[0]) { reply[n] = TEL_ENV_VAR; /* VAR */ strcpy(&reply[n+1],"ACCT"); reply[n+5] = TEL_ENV_VALUE; /* VALUE */ strcpy(&reply[n+6],tn_env_acct); n += strlen(tn_env_acct) + 4 + 2; } if (tn_env_prnt[0]) { reply[n] = TEL_ENV_VAR; /* VAR */ strcpy(&reply[n+1],"PRINTER"); reply[n+8] = TEL_ENV_VALUE; /* VALUE */ strcpy(&reply[n+9],tn_env_prnt); n += strlen(tn_env_prnt) + 7 + 2; } if (tn_env_sys[0]) { reply[n] = TEL_ENV_VAR; /* VAR */ strcpy(&reply[n+1],"SYSTEMTYPE"); reply[n+11] = TEL_ENV_VALUE; /* VALUE */ strcpy(&reply[n+12],tn_env_sys); n += strlen(tn_env_sys) + 10 + 2; } if (disp) { reply[n] = TEL_ENV_VAR; /* VAR */ strcpy(&reply[n+1],"DISPLAY"); reply[n+8] = TEL_ENV_VALUE; /* VALUE */ strcpy(&reply[n+9],disp); n += strlen(disp) + 7 + 2; } } else if (!strcmp(varname,"USER") && uu[0]) { reply[n] = TEL_ENV_VAR; /* VAR */ strcpy(&reply[n+1],"USER"); reply[n+5] = TEL_ENV_VALUE; /* VALUE */ strcpy(&reply[n+6],uu); n += strlen(uu) + 4 + 2; } else if (!strcmp(varname,"JOB") && tn_env_job[0]) { reply[n] = TEL_ENV_VAR; /* VAR */ strcpy(&reply[n+1],"JOB"); reply[n+4] = TEL_ENV_VALUE; /* VALUE */ strcpy(&reply[n+5],tn_env_job); n += strlen(tn_env_job) + 3 + 2; } else if (!strcmp(varname,"ACCT") && tn_env_acct[0]) { reply[n] = TEL_ENV_VAR; /* VAR */ strcpy(&reply[n+1],"ACCT"); reply[n+5] = TEL_ENV_VALUE; /* VALUE */ strcpy(&reply[n+6],tn_env_acct); n += strlen(tn_env_acct) + 4 + 2; } else if (!strcmp(varname,"PRINTER") && tn_env_prnt[0]) { reply[n] = TEL_ENV_VAR; /* VAR */ strcpy(&reply[n+1],"PRINTER"); reply[n+8] = TEL_ENV_VALUE; /* VALUE */ strcpy(&reply[n+9],tn_env_prnt); n += strlen(tn_env_prnt) + 7 + 2; } else if (!strcmp(varname,"SYSTEMTYPE") && tn_env_sys[0]) { reply[n] = TEL_ENV_VAR; /* VAR */ strcpy(&reply[n+1],"SYSTEMTYPE"); reply[n+11] = TEL_ENV_VALUE; /* VALUE */ strcpy(&reply[n+12],tn_env_sys); n += strlen(tn_env_sys) + 10 + 2; } else if (!strcmp(varname,"DISPLAY") && disp) { reply[n] = TEL_ENV_VAR; /* VAR */ strcpy(&reply[n+1],"DISPLAY"); reply[n+8] = TEL_ENV_VALUE; /* VALUE */ strcpy(&reply[n+9],disp); n += strlen(disp) + 7 + 2; } /* If we get IAC only, then that means send all */ /* VAR and USERVAR. */ if (!(j == 0 && sb[i] == IAC)) break; case 2: /* USERVAR in progress */ varname[j] = '\0'; if (!varname[0]) { /* Send All */ int x,y; for ( x=0 ; x<8 ; x++ ) { if ( tn_env_uservar[x][0] && tn_env_uservar[x][1] ) { reply[n] = TEL_ENV_USERVAR; /* VAR */ y = strlen(tn_env_uservar[x][0]); strcpy(&reply[n+1],tn_env_uservar[x][0]); reply[n+y+1] = TEL_ENV_VALUE; /* VALUE */ strcpy(&reply[n+y+2],tn_env_uservar[x][1]); n += y+strlen(tn_env_uservar[x][1])+2; } } if ( tn_sfu ) { /* Compatibility with Microsoft Telnet Server */ reply[n] = TEL_ENV_USERVAR; /* VAR */ strcpy(&reply[n+1],SFUTLNTVER); reply[n+11] = TEL_ENV_VALUE; /* VALUE */ strcpy(&reply[n+12],SFUTLNTVER_VALUE); n += strlen(SFUTLNTVER)+strlen(SFUTLNTVER_VALUE)+2; reply[n] = TEL_ENV_USERVAR; /* VAR */ strcpy(&reply[n+1],SFUTLNTMODE); reply[n+12] = TEL_ENV_VALUE; /* VALUE */ strcpy(&reply[n+13],SFUTLNTMODE_VALUE); n += strlen(SFUTLNTMODE)+strlen(SFUTLNTMODE_VALUE)+2; } if (tn_loc && tn_loc[0]) { reply[n] = TEL_ENV_USERVAR; /* VAR */ strcpy(&reply[n+1],"LOCATION"); reply[n+9] = TEL_ENV_VALUE; /* VALUE */ strcpy(&reply[n+10],tn_loc); n += strlen("LOCATION") + strlen(tn_loc) + 2; } } else if (tn_sfu && !strcmp(varname,SFUTLNTVER)) { reply[n] = TEL_ENV_USERVAR; /* VAR */ strcpy(&reply[n+1],SFUTLNTVER); reply[n+11] = TEL_ENV_VALUE; /* VALUE */ strcpy(&reply[n+12],SFUTLNTVER_VALUE); n += strlen(SFUTLNTVER) + strlen(SFUTLNTVER_VALUE) + 2; } else if (tn_sfu && !strcmp(varname,SFUTLNTMODE)) { reply[n] = TEL_ENV_USERVAR; /* VAR */ strcpy(&reply[n+1],SFUTLNTMODE); reply[n+12] = TEL_ENV_VALUE; /* VALUE */ strcpy(&reply[n+13],SFUTLNTMODE_VALUE); n += strlen(SFUTLNTMODE) + strlen(SFUTLNTMODE_VALUE) + 2; } #ifdef CK_SNDLOC else if (tn_loc && tn_loc[0] && !strcmp(varname,"LOCATION")){ reply[n] = TEL_ENV_USERVAR; /* VAR */ strcpy(&reply[n+1],"LOCATION"); reply[n+9] = TEL_ENV_VALUE; /* VALUE */ strcpy(&reply[n+10],tn_loc); n += strlen("LOCATION") + strlen(tn_loc) + 2; } #endif /* CK_SNDLOC */ else { int x,y; for ( x=0 ; x<8 ; x++ ) { if ( tn_env_uservar[x][0] && tn_env_uservar[x][1] && !strcmp(varname,tn_env_uservar[x][0])) { reply[n] = TEL_ENV_USERVAR; /* VAR */ y = strlen(tn_env_uservar[x][0]); strcpy(&reply[n+1],tn_env_uservar[x][0]); reply[n+y+1] = TEL_ENV_VALUE; /* VALUE */ strcpy(&reply[n+y+2],tn_env_uservar[x][1]); n += y+strlen(tn_env_uservar[x][1])+2; } } } break; } varname[0] = '\0'; j = 0; type = (sb[i] == TEL_ENV_USERVAR ? 2 : /* USERVAR */ sb[i] == TEL_ENV_VAR ? 1 : /* VAR */ 0 ); break; case TEL_ENV_VALUE: /* VALUE */ /* Protocol Error */ debug(F100, "TELNET Subnegotiation error - VALUE in SEND", "",0); if (tn_deb || debses) tn_debug("TELNET Subnegotiation error - VALUE in SEND"); return(0); /* Was -1 but that would be taken as */ /* an I/O error, so absorb it and go on. */ case TEL_ENV_ESC: /* ESC */ /* Not sure what this for. Quote next character? */ break; default: varname[j++] = sb[i]; } } if (tn_ssbopt(TELOPT_NEWENVIRON,TELQUAL_IS,reply,n) < 0) { free(reply); return(-1); } free(reply); return(1); } #endif /* CK_ENVIRONMENT */ /* Telnet send terminal type */ /* Returns -1 on error, 0 if nothing happens, 1 if type sent successfully */ int tn_sttyp() { /* Send telnet terminal type. */ char *ttn; /* Name of terminal type. */ #ifdef OS2 static int alias = -1; /* which alias are we using ? */ int settype = 0; #endif /* OS2 */ int i, rc; /* Worker. */ int tntermflg = 0; if (ttnet != NET_TCPB) return(0); if (ttnproto != NP_TELNET) return(0); if (!TELOPT_ME(TELOPT_TTYPE)) return(0); #ifdef CK_SSL if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) { return(0); } #endif /* CK_SSL */ ttn = NULL; #ifndef NOTERM #ifdef OS2 if (!tn_term) { if (ttnum == -1) { ttnum = tt_type; settype = 0; alias = -1; } else if (ttnumend) { ttnumend = 0; settype = 0; } else { if (tt_info[tt_type].x_aliases[++alias] == NULL) { if (--tt_type < 0) tt_type = max_tt; if (ttnum == tt_type) ttnumend = 1; settype = 1; alias = -1; } } if (tt_type >= 0 && tt_type <= max_tt) { if (alias == -1) ttn = tt_info[tt_type].x_name; else ttn = tt_info[tt_type].x_aliases[alias]; } else ttn = NULL; } else settype = 0; #endif /* OS2 */ #endif /* NOTERM */ if (tn_term) { /* Terminal type override? */ debug(F110,"tn_sttyp",tn_term,0); if (*tn_term) { ttn = tn_term; tntermflg = 1; } } else debug(F100,"tn_sttyp no term override","",0); #ifndef datageneral if (!ttn) { /* If no override, */ ttn = getenv("TERM"); /* get it from the environment. */ } #endif /* datageneral */ if ((ttn == ((char *)0)) || ((int)strlen(ttn) >= TSBUFSIZ)) ttn = "UNKNOWN"; sb_out[0] = (CHAR) IAC; /* I Am a Command */ sb_out[1] = (CHAR) SB; /* Subnegotiation */ sb_out[2] = TELOPT_TTYPE; /* Terminal Type */ sb_out[3] = (CHAR) 0; /* Is... */ for (i = 4; *ttn; ttn++,i++) { /* Copy and uppercase it */ #ifdef VMS if (!tntermflg && *ttn == '-' && (!strcmp(ttn,"-80") || !strcmp(ttn,"-132"))) break; else #endif /* VMS */ sb_out[i] = (char) ((!tntermflg && islower(*ttn)) ? toupper(*ttn) : *ttn); } ttn = (char *)sb_out; /* Point back to beginning */ #ifdef DEBUG if (deblog || tn_deb || debses) { sb_out[i] = '\0'; /* For debugging */ ckmakxmsg(tn_msg_out,TN_MSG_LEN,"TELNET SENT SB ", TELOPT(TELOPT_TTYPE)," IS ",(char *)sb_out+4," IAC SE", NULL,NULL,NULL,NULL,NULL,NULL,NULL); } #endif /* DEBUG */ sb_out[i++] = (CHAR) IAC; /* End of Subnegotiation */ sb_out[i++] = (CHAR) SE; /* marked by IAC SE */ #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif #ifdef DEBUG debug(F100,tn_msg_out,"",0); if (tn_deb || debses) tn_debug(tn_msg_out); #endif /* DEBUG */ rc = (ttol((CHAR *)sb_out,i) < 0); /* Send it. */ #ifdef OS2 ReleaseTelnetMutex(); #endif if (rc) return(-1); #ifndef NOTERM #ifdef OS2 if (settype) settermtype(tt_type,0); else { ipadl25(); VscrnIsDirty(VTERM); } #endif /* OS2 */ #endif /* NOTERM */ return(1); } #ifdef CK_ENVIRONMENT #ifdef CK_XDISPLOC /* Telnet send xdisplay location */ /* Returns -1 on error, 0 if nothing happens, 1 if type sent successfully */ int tn_sxdisploc() { /* Send telnet X display location. */ char * disp=NULL; int i,rc; if (ttnet != NET_TCPB) return(0); if (ttnproto != NP_TELNET) return(0); if (!TELOPT_ME(TELOPT_XDISPLOC)) return(0); #ifdef CK_SSL if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) { return(0); } #endif /* CK_SSL */ #ifdef CK_FORWARD_X if (TELOPT_U(TELOPT_FORWARD_X)) { disp = NULL; } else #endif /* CK_FORWARD_X */ disp = (char *)tn_get_display(); debug(F110,"tn_sxdisploc",disp,0); if (!disp) { /* Can't do both, send WONT */ if (tn_sopt(WONT,TELOPT_XDISPLOC) < 0) return(-1); TELOPT_UNANSWERED_WONT(TELOPT_XDISPLOC) = 1; return(0); } sb_out[0] = (CHAR) IAC; /* I Am a Command */ sb_out[1] = (CHAR) SB; /* Subnegotiation */ sb_out[2] = TELOPT_XDISPLOC; /* X-Display Location */ sb_out[3] = (CHAR) 0; /* Is... */ for (i = 4; *disp; disp++,i++) { /* Copy and uppercase it */ sb_out[i] = (char) *disp; } #ifdef DEBUG if (deblog || tn_deb || debses) { sb_out[i] = '\0'; /* For debugging */ ckmakxmsg( tn_msg_out,TN_MSG_LEN, "TELNET SENT SB ",TELOPT(TELOPT_XDISPLOC), " IS ",(char *)sb_out+4," IAC SE", NULL,NULL,NULL,NULL,NULL,NULL,NULL); } #endif /* DEBUG */ sb_out[i++] = (CHAR) IAC; /* End of Subnegotiation */ sb_out[i++] = (CHAR) SE; /* marked by IAC SE */ #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif #ifdef DEBUG debug(F100,tn_msg_out,"",0); if (tn_deb || debses) tn_debug(tn_msg_out); #endif /* DEBUG */ rc = (ttol((CHAR *)sb_out,i) < 0); /* Send it. */ #ifdef OS2 ReleaseTelnetMutex(); #endif if (rc) return(-1); return(1); } #endif /* CK_XDISPLOC */ #endif /* CK_ENVIRONMENT */ #ifdef CK_FORWARD_X int tn_sndfwdx() { /* Send Fwd X Screen number to host */ unsigned char screen = 0; char * disp; int i,rc; /* if (!IS_TELNET()) return(0); */ if (!TELOPT_U(TELOPT_FORWARD_X)) return(0); #ifdef CK_SSL if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) { return(0); } #endif /* CK_SSL */ /* * The format of the DISPLAY variable is [:][.] * where is an optional DNS name or ip address with a default of * the localhost; the screen defaults to 0 */ disp = (char *)tn_get_display(); if (disp) { int colon,dot; colon = ckindex(":",disp,0,0,1); dot = ckindex(".",&disp[colon],0,0,1); if ( dot ) { screen = atoi(&disp[colon+dot]); } } else { screen = 0; } i = 0; sb_out[i++] = (CHAR) IAC; /* I Am a Command */ sb_out[i++] = (CHAR) SB; /* Subnegotiation */ sb_out[i++] = TELOPT_FORWARD_X; /* Forward X */ sb_out[i++] = FWDX_SCREEN; /* Screen */ sb_out[i++] = screen; if ( screen == IAC ) sb_out[i++] = IAC; sb_out[i++] = (CHAR) IAC; /* End of Subnegotiation */ sb_out[i++] = (CHAR) SE; /* marked by IAC SE */ #ifdef DEBUG if (deblog || tn_deb || debses) { ckmakxmsg( tn_msg_out,TN_MSG_LEN, "TELNET SENT SB ",TELOPT(TELOPT_FORWARD_X), " SCREEN ",ckctox(screen,1)," IAC SE", NULL,NULL,NULL,NULL,NULL,NULL,NULL); } #endif /* DEBUG */ #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif #ifdef DEBUG debug(F100,tn_msg_out,"",0); if (tn_deb || debses) tn_debug(tn_msg_out); #endif /* DEBUG */ rc = (ttol((CHAR *)sb_out,i) < 0); /* Send it. */ #ifdef OS2 ReleaseTelnetMutex(); #endif if (rc) return(-1); return(0); } #endif /* CK_FORWARD_X */ #ifdef CK_SNDLOC int tn_sndloc() { /* Send location. */ int i,rc; /* Worker. */ char *ttloc; /* if (!IS_TELNET()) return(0); */ if (!TELOPT_ME(TELOPT_SNDLOC)) return(0); #ifdef CK_SSL if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) { return(0); } #endif /* CK_SSL */ ttloc = (tn_loc ? tn_loc : ""); /* In case we are being called even */ /* though there is no location. */ sb_out[0] = (CHAR) IAC; /* I Am a Command */ sb_out[1] = (CHAR) SB; /* Subnegotiation */ sb_out[2] = TELOPT_SNDLOC; /* Location */ for (i = 3; *ttloc && i < TSBUFSIZ; ttloc++,i++) /* Copy it */ sb_out[i] = (char) *ttloc; sb_out[i++] = (CHAR) IAC; /* End of Subnegotiation */ sb_out[i++] = (CHAR) SE; /* marked by IAC SE */ #ifdef DEBUG if (deblog || tn_deb || debses) { ckmakxmsg(tn_msg_out,TN_MSG_LEN, "TELNET SENT SB ",TELOPT(TELOPT_SNDLOC)," ",(char *)sb_out+3, " IAC SE", NULL,NULL,NULL,NULL,NULL,NULL,NULL); } #endif /* DEBUG */ #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif #ifdef DEBUG debug(F100,tn_msg_out,"",0); if (tn_deb || debses) tn_debug(tn_msg_out); #endif /* DEBUG */ rc = (ttol((CHAR *)sb_out,i) < 0); /* Send it. */ #ifdef OS2 ReleaseTelnetMutex(); #endif if (rc) return(-1); sb_out[i-2] = '\0'; /* For debugging */ return(0); } #endif /* CK_SNDLOC */ #ifdef CK_NAWS /* NAWS = Negotiate About Window Size */ int tn_snaws() { /* Send terminal width and height, RFC 1073 */ #ifndef NOLOCAL CHAR sb_out[24]; /* multiple threads */ int i = 0,rc; #ifdef OS2 int x = VscrnGetWidth(VTERM), y = VscrnGetHeight(VTERM) - (tt_status[VTERM] ? 1 : 0); #else /* OS2 */ int x = tt_cols, y = tt_rows; #endif /* OS2 */ if (ttnet != NET_TCPB) return(0); if (ttnproto != NP_TELNET) return(0); if (!TELOPT_ME(TELOPT_NAWS)) return(0); #ifdef CK_SSL if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) { return(0); } #endif /* CK_SSL */ if (x < 0) x = 0; if (y < 0) y = 0; if (x == TELOPT_SB(TELOPT_NAWS).naws.x && /* Only send if changed */ y == TELOPT_SB(TELOPT_NAWS).naws.y ) return(0); TELOPT_SB(TELOPT_NAWS).naws.x = x; /* Remember the size */ TELOPT_SB(TELOPT_NAWS).naws.y = y; sb_out[i++] = (CHAR) IAC; /* Send the subnegotiation */ sb_out[i++] = (CHAR) SB; sb_out[i++] = TELOPT_NAWS; sb_out[i++] = (CHAR) (x >> 8) & 0xff; if ((CHAR) sb_out[i-1] == (CHAR) IAC) /* IAC in data must be doubled */ sb_out[i++] = (CHAR) IAC; sb_out[i++] = (CHAR) (x & 0xff); if ((CHAR) sb_out[i-1] == (CHAR) IAC) sb_out[i++] = (CHAR) IAC; sb_out[i++] = (CHAR) (y >> 8) & 0xff; if ((CHAR) sb_out[i-1] == (CHAR) IAC) sb_out[i++] = (CHAR) IAC; sb_out[i++] = (CHAR) (y & 0xff); if ((CHAR) sb_out[i-1] == (CHAR) IAC) sb_out[i++] = (CHAR) IAC; sb_out[i++] = (CHAR) IAC; sb_out[i++] = (CHAR) SE; #ifdef DEBUG if (deblog || tn_deb || debses) { ckmakxmsg(tn_msg_out,TN_MSG_LEN,"TELNET SENT SB NAWS ", ckitoa(x)," ",ckitoa(y)," IAC SE", NULL,NULL,NULL,NULL,NULL,NULL,NULL); } #endif /* DEBUG */ #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif #ifdef DEBUG debug(F100,tn_msg_out,"",0); if (tn_deb || debses) tn_debug(tn_msg_out); #endif /* DEBUG */ rc = (ttol((CHAR *)sb_out,i) < 0); /* Send it. */ #ifdef OS2 ReleaseTelnetMutex(); #endif if (rc) return(-1); #endif /* NOLOCAL */ return (0); } #endif /* CK_NAWS */ #ifdef TN_COMPORT static char * tnc_signature = NULL; static int tnc_ls_mask = 0; static int tnc_ls = 0; static int tnc_ms_mask = 255; static int tnc_ms = 0; static int tnc_oflow = 0; static int tnc_iflow = 0; static int tnc_bps = 0; static int tnc_datasize = 0; static int tnc_parity = 0; static int tnc_stopbit = 0; static int tnc_break = 0; static int tnc_dtr = 0; static int tnc_rts = 0; static int tnc_suspend_xmit = 0; static int tnc_bps_index = -1; int #ifdef CK_ANSIC tnc_init(void) #else /* CK_ANSIC */ tnc_init() #endif /* CK_ANSIC */ /* tnc_init */ { debug(F100,"tnc_init","",0); /* if (!IS_TELNET()) return(0); */ if (tnc_signature) { free(tnc_signature); tnc_signature = NULL; } tnc_ls_mask = 0; tnc_ls = 0; tnc_ms_mask = 255; tnc_ms = 0; tnc_oflow = 0; tnc_iflow = 0; tnc_bps = 0; tnc_datasize = 0; tnc_parity = 0; tnc_stopbit = 0; tnc_break = 0; tnc_dtr = 0; tnc_rts = 0; tnc_suspend_xmit = 0; tnc_bps_index = -1; return(0); } int #ifdef CK_ANSIC tn_sndcomport(void) #else /* CK_ANSIC */ tn_sndcomport() #endif /* CK_ANSIC */ /* tn_sndcomport */ { int baud, datasize, parity, stopsize, oflow, iflow; CONST char * signature; /* if (!IS_TELNET()) return(0); */ debug(F100,"tnc_sndcomport","",0); signature = tnc_get_signature(); baud = tnc_get_baud(); datasize = tnc_get_datasize(); parity = tnc_get_parity(); stopsize = tnc_get_stopsize(); oflow = tnc_get_oflow(); iflow = tnc_get_iflow(); tnc_set_ls_mask(255); tnc_set_ms_mask(255); return(0); } int #ifdef CK_ANSIC tnc_wait(CHAR * msg, int ms) #else /* CK_ANSIC */ tnc_wait(msg, ms) CHAR * msg; int ms; #endif /* CK_ANSIC */ /* tnc_wait */ { int rc, tn_wait_save = tn_wait_flg; /* if (!IS_TELNET()) return(0); */ debug(F111,"tnc_wait","begin",ms); if ( ms ) TELOPT_SB(TELOPT_COMPORT).comport.wait_for_ms = 1; else TELOPT_SB(TELOPT_COMPORT).comport.wait_for_sb = 1; tn_wait_flg = 1; rc = tn_wait((char *)msg); tn_push(); debug(F110,"tnc_wait","end",0); tn_wait_flg = tn_wait_save; return(rc); } /* Returns -1 on error, 0 on success */ /* In order for this code to work, sb[len] == IAC */ int #ifdef CK_ANSIC tnc_tn_sb(CHAR * sb, int len) #else tnc_tn_sb(sb, len) CHAR * sb; int len; #endif /* CK_ANSIC */ /* tnc_tn_sb */ { if (ttnet != NET_TCPB) return(0); if (ttnproto != NP_TELNET) return(0); if (!TELOPT_ME(TELOPT_COMPORT)) return(0); if (!sb) return(-1); #ifdef CK_SSL if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) { return(0); } #endif /* CK_SSL */ debug(F111,"tnc_tn_sb","sb[0]",sb[0]); debug(F111,"tnc_tn_sb","len",len); switch (sb[0]) { case TNC_C2S_SIGNATURE: case TNC_S2C_SIGNATURE: debug(F111,"tnc_tn_sb","signature",len); if (len == 1) { tnc_send_signature("Kermit Telnet Com Port Option"); } else { TELOPT_SB(TELOPT_COMPORT).comport.wait_for_sb = 0; if (tnc_signature) free(tnc_signature); tnc_signature = malloc(len); if (tnc_signature) { memcpy(tnc_signature,&sb[1],len-1); tnc_signature[len-1] = '\0'; } } break; case TNC_C2S_SET_BAUDRATE: case TNC_S2C_SET_BAUDRATE: { long baudrate; char * br = (char *)&baudrate; TELOPT_SB(TELOPT_COMPORT).comport.wait_for_sb = 0; if (len == 2) { /* Actual behavior of the Access Server... */ debug(F111,"tnc_tn_sb","baudrate index",sb[1]); tnc_bps_index = 1; switch (sb[1]) { case TNC_BPS_300: tnc_bps = 300; break; case TNC_BPS_600: tnc_bps = 600; break; case TNC_BPS_1200: tnc_bps = 1200; break; case TNC_BPS_2400: tnc_bps = 2400; break; case TNC_BPS_4800: tnc_bps = 4800; break; case TNC_BPS_9600: tnc_bps = 9600; break; case TNC_BPS_14400: tnc_bps = 14400; break; case TNC_BPS_19200: tnc_bps = 19200; break; case TNC_BPS_28800: tnc_bps = 28800; break; case TNC_BPS_38400: tnc_bps = 38400; break; case TNC_BPS_57600: tnc_bps = 57600; break; case TNC_BPS_115200: tnc_bps = 115200; break; case TNC_BPS_230400: tnc_bps = 230400; break; case TNC_BPS_460800: tnc_bps = 460800; break; default: tnc_bps = -1; } } else if (len == 5) { /* This section attempts to follow RFC 2217 */ tnc_bps_index = 0; br[0] = sb[1]; br[1] = sb[2]; br[2] = sb[3]; br[3] = sb[4]; #ifdef datageneral /* AOS/VS doesn't have ntohl() but MV's are big-endian */ tnc_bps = baudrate; #else tnc_bps = ntohl(baudrate); #endif /* datageneral */ debug(F111,"tnc_tn_sb","baudrate rfc",tnc_bps); } else { debug(F111,"tnc_tn_sb","baudrate invalid len",len); return(-1); } break; } case TNC_C2S_SET_DATASIZE: case TNC_S2C_SET_DATASIZE: TELOPT_SB(TELOPT_COMPORT).comport.wait_for_sb = 0; if (len < 2) return(-1); tnc_datasize = sb[1]; debug(F111,"tnc_tn_sb","datasize",sb[1]); break; case TNC_C2S_SET_PARITY: case TNC_S2C_SET_PARITY: TELOPT_SB(TELOPT_COMPORT).comport.wait_for_sb = 0; if (len < 2) return(-1); tnc_parity = sb[1]; debug(F111,"tnc_tn_sb","parity",sb[1]); break; case TNC_C2S_SET_STOPSIZE: case TNC_S2C_SET_STOPSIZE: TELOPT_SB(TELOPT_COMPORT).comport.wait_for_sb = 0; if (len < 2) return(-1); tnc_stopbit = sb[1]; debug(F111,"tnc_tn_sb","stopsize",sb[1]); break; case TNC_C2S_SET_CONTROL: case TNC_S2C_SET_CONTROL: if (len < 2) { TELOPT_SB(TELOPT_COMPORT).comport.wait_for_sb = 0; return(-1); } #ifdef COMMENT /* This line should be removed when testing is complete. */ TELOPT_SB(TELOPT_COMPORT).comport.wait_for_sb = 0; #endif /* COMMENT */ switch ( sb[1] ) { case TNC_CTL_OFLOW_REQUEST: /* determine local outbound flow control and send to peer */ /* Cisco IOS returns 0 (TNC_CTL_OFLOW_REQUEST) when attempting */ /* to set the inbound flow control if it is not supported */ /* separately from outbound flow control. So must reset */ /* wait for sb flag. */ debug(F110,"tnc_tn_sb","oflow request",0); TELOPT_SB(TELOPT_COMPORT).comport.wait_for_sb = 0; break; case TNC_CTL_OFLOW_NONE: case TNC_CTL_OFLOW_XON_XOFF: case TNC_CTL_OFLOW_RTS_CTS: case TNC_CTL_OFLOW_DCD: case TNC_CTL_OFLOW_DSR: TELOPT_SB(TELOPT_COMPORT).comport.wait_for_sb = 0; tnc_oflow = sb[1]; debug(F111,"tnc_tn_sb","oflow",sb[1]); break; case TNC_CTL_BREAK_REQUEST: /* determine local break state and send to peer */ debug(F110,"tnc_tn_sb","break request",0); break; case TNC_CTL_BREAK_ON: TELOPT_SB(TELOPT_COMPORT).comport.wait_for_sb = 0; tnc_break = 1; debug(F110,"tnc_tn_sb","break on",0); break; case TNC_CTL_BREAK_OFF: TELOPT_SB(TELOPT_COMPORT).comport.wait_for_sb = 0; tnc_break = 0; debug(F110,"tnc_tn_sb","break off",0); break; case TNC_CTL_DTR_REQUEST: /* determine local dtr state and send to peer */ debug(F110,"tnc_tn_sb","dtr request",0); break; case TNC_CTL_DTR_ON: TELOPT_SB(TELOPT_COMPORT).comport.wait_for_sb = 0; tnc_dtr = 1; debug(F110,"tnc_tn_sb","dtr on",0); break; case TNC_CTL_DTR_OFF: TELOPT_SB(TELOPT_COMPORT).comport.wait_for_sb = 0; tnc_dtr = 0; debug(F110,"tnc_tn_sb","dtr off",0); break; case TNC_CTL_RTS_REQUEST: /* determine local rts state and send to peer */ debug(F110,"tnc_tn_sb","rts request",0); break; case TNC_CTL_RTS_ON: TELOPT_SB(TELOPT_COMPORT).comport.wait_for_sb = 0; tnc_rts = 1; debug(F110,"tnc_tn_sb","rts on",0); break; case TNC_CTL_RTS_OFF: TELOPT_SB(TELOPT_COMPORT).comport.wait_for_sb = 0; tnc_rts = 0; debug(F110,"tnc_tn_sb","rts off",0); break; case TNC_CTL_IFLOW_REQUEST: /* determine local inbound flow control and send to peer */ debug(F110,"tnc_tn_sb","iflow request",0); break; case TNC_CTL_IFLOW_NONE: case TNC_CTL_IFLOW_XON_XOFF: case TNC_CTL_IFLOW_RTS_CTS: case TNC_CTL_IFLOW_DTR: TELOPT_SB(TELOPT_COMPORT).comport.wait_for_sb = 0; tnc_iflow = sb[1]; debug(F111,"tnc_tn_sb","iflow",sb[1]); break; default: return(-1); } break; case TNC_C2S_NOTIFY_LINESTATE: case TNC_S2C_SEND_LS: if (len < 2) return(-1); tnc_ls = sb[1]; debug(F111,"tnc_tn_sb","linestate",sb[1]); if (tn_deb || debses) { if (tnc_ls & TNC_MS_DATA_READY ) tn_debug(" ComPort Linestate Data Ready"); if (tnc_ls & TNC_MS_OVERRUN_ERROR ) tn_debug(" ComPort Linestate Overrun Error"); if (tnc_ls & TNC_MS_PARITY_ERROR ) tn_debug(" ComPort Linestate Parity Error"); if (tnc_ls & TNC_MS_FRAME_ERROR ) tn_debug(" ComPort Linestate Framing Error"); if (tnc_ls & TNC_MS_BREAK_ERROR ) tn_debug(" ComPort Linestate Break Detect Error"); if (tnc_ls & TNC_MS_HR_EMPTY ) tn_debug(" ComPort Linestate Holding Register Empty"); if (tnc_ls & TNC_MS_SR_EMPTY ) tn_debug(" ComPort Linestate Shift Register Empty"); if (tnc_ls & TNC_MS_TIMEOUT_ERROR ) tn_debug(" ComPort Linestate Timeout Error"); } break; case TNC_C2S_NOTIFY_MODEMSTATE: case TNC_S2C_SEND_MS: TELOPT_SB(TELOPT_COMPORT).comport.wait_for_ms = 0; if (len < 2) return(-1); tnc_ms = sb[1]; debug(F111,"tnc_tn_sb","modemstate",sb[1]); if (tn_deb || debses) { if (tnc_ms & TNC_MS_CTS_DELTA ) tn_debug(" ComPort Modemstate CTS State Change"); if (tnc_ms & TNC_MS_DSR_DELTA ) tn_debug(" ComPort Modemstate DSR State Change"); if (tnc_ms & TNC_MS_EDGE_RING ) tn_debug(" ComPort Modemstate Trailing Edge Ring Detector On"); else tn_debug(" ComPort Modemstate Trailing Edge Ring Detector Off"); if (tnc_ms & TNC_MS_RLSD_DELTA ) tn_debug(" ComPort Modemstate RLSD State Change"); if (tnc_ms & TNC_MS_CTS_SIG ) tn_debug(" ComPort Modemstate CTS Signal On"); else tn_debug(" ComPort Modemstate CTS Signal Off"); if (tnc_ms & TNC_MS_DSR_SIG ) tn_debug(" ComPort Modemstate DSR Signal On"); else tn_debug(" ComPort Modemstate DSR Signal Off"); if (tnc_ms & TNC_MS_RI_SIG ) tn_debug(" ComPort Modemstate Ring Indicator On"); else tn_debug(" ComPort Modemstate Ring Indicator Off"); if (tnc_ms & TNC_MS_RLSD_SIG ) tn_debug(" ComPort Modemstate RLSD Signal On"); else tn_debug(" ComPort Modemstate RLSD Signal Off"); } break; case TNC_C2S_FLOW_SUSPEND: case TNC_S2C_FLOW_SUSPEND: debug(F110,"tnc_tn_sb","flow suspend",0); tnc_suspend_xmit = 1; break; case TNC_C2S_FLOW_RESUME: case TNC_S2C_FLOW_RESUME: debug(F110,"tnc_tn_sb","flow resume",0); tnc_suspend_xmit = 0; break; case TNC_C2S_SET_LS_MASK: case TNC_S2C_SET_LS_MASK: TELOPT_SB(TELOPT_COMPORT).comport.wait_for_sb = 0; if (len < 2) return(-1); debug(F111,"tnc_tn_sb","linestate mask",sb[1]); tnc_ls_mask = sb[1]; break; case TNC_C2S_SET_MS_MASK: case TNC_S2C_SET_MS_MASK: TELOPT_SB(TELOPT_COMPORT).comport.wait_for_sb = 0; if (len < 2) return(-1); debug(F111,"tnc_tn_sb","modemstate mask",sb[1]); tnc_ls_mask = sb[1]; break; case TNC_C2S_PURGE: case TNC_S2C_PURGE: if (len < 2) return(-1); debug(F111,"tnc_tn_sb","purge",sb[1]); switch ( sb[1] ) { case TNC_PURGE_RECEIVE: case TNC_PURGE_TRANSMIT: case TNC_PURGE_BOTH: /* purge local buffers */ break; default: return(-1); } break; default: return(-1); } return(0); } CONST char * #ifdef CK_ANSIC tnc_get_signature(void) #else /* CK_ANSIC */ tnc_get_signature() #endif /* CK_ANSIC */ /* tnc_get_signature */ { /* send IAC SB COM-PORT SIGNATURE IAC SE */ /* wait for response */ int i = 0, rc; if (ttnet != NET_TCPB) return(NULL); if (ttnproto != NP_TELNET) return(NULL); if (!TELOPT_ME(TELOPT_COMPORT)) return(NULL); #ifdef CK_SSL if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) { return(NULL); } #endif /* CK_SSL */ if ( tnc_signature ) return(tnc_signature); sb_out[i++] = (CHAR) IAC; /* I Am a Command */ sb_out[i++] = (CHAR) SB; /* Subnegotiation */ sb_out[i++] = TELOPT_COMPORT; /* ComPort */ sb_out[i++] = TNC_C2S_SIGNATURE; /* Signature */ sb_out[i++] = (CHAR) IAC; /* End of Subnegotiation */ sb_out[i++] = (CHAR) SE; /* marked by IAC SE */ #ifdef DEBUG if (deblog || tn_deb || debses) { ckmakmsg(tn_msg_out,TN_MSG_LEN, "TELNET SENT SB ",TELOPT(TELOPT_COMPORT), " SIGNATURE IAC SE", NULL); } #endif /* DEBUG */ #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif #ifdef DEBUG debug(F100,tn_msg_out,"",0); if (tn_deb || debses) tn_debug(tn_msg_out); #endif /* DEBUG */ rc = (ttol((CHAR *)sb_out,i) < 0); /* Send it. */ #ifdef OS2 ReleaseTelnetMutex(); #endif if (rc) return(NULL); if (tnc_wait((CHAR *)"comport signature request",0) < 0) { tn_push(); return(NULL); } debug(F110,"tnc_get_signature",tnc_signature,0); return(tnc_signature); } int #ifdef CK_ANSIC tnc_send_signature(char * signature) #else /* CK_ANSIC */ tnc_send_signature(signature) char * signature; #endif /* CK_ANSIC */ /* tnc_send_signature */ { /* send IAC SB COM-PORT SIGNATURE IAC SE */ int i = 0, j = 0, rc; debug(F110,"tnc_send_signature",signature,0); if (!signature || !signature[0]) return(0); if (ttnet != NET_TCPB) return(0); if (ttnproto != NP_TELNET) return(0); if (!TELOPT_ME(TELOPT_COMPORT)) return(0); #ifdef CK_SSL if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) { return(0); } #endif /* CK_SSL */ sb_out[i++] = (CHAR) IAC; /* I Am a Command */ sb_out[i++] = (CHAR) SB; /* Subnegotiation */ sb_out[i++] = TELOPT_COMPORT; /* ComPort */ sb_out[i++] = TNC_C2S_SIGNATURE; /* Signature */ for (; signature[j]; i++,j++) sb_out[i] = signature[j]; sb_out[i++] = (CHAR) IAC; /* End of Subnegotiation */ sb_out[i++] = (CHAR) SE; /* marked by IAC SE */ #ifdef DEBUG if (deblog || tn_deb || debses) { ckmakxmsg(tn_msg_out,TN_MSG_LEN, "TELNET SENT SB ",TELOPT(TELOPT_COMPORT), " SIGNATURE ", signature, " IAC SE", NULL, NULL,NULL,NULL,NULL,NULL,NULL); } #endif /* DEBUG */ #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif #ifdef DEBUG debug(F100,tn_msg_out,"",0); if (tn_deb || debses) tn_debug(tn_msg_out); #endif /* DEBUG */ rc = (ttol((CHAR *)sb_out,i) < 0); /* Send it. */ #ifdef OS2 ReleaseTelnetMutex(); #endif if (rc) return(-1); return(0); } int #ifdef CK_ANSIC tnc_set_baud( long baud ) #else /* CK_ANSIC */ tnc_set_baud(baud) long baud; #endif /* CK_ANSIC */ /* tnc_set_baud */ { /* send IAC SB COM-PORT SET-BAUD IAC SE */ /* wait for response */ /* 0 is used to request the current baud rate and */ /* may not be sent by this func */ /* return new host value */ /* * the above comes from the RFC. But that is not what I am seeing * instead I appear to be seeing to following: * * Value Baud * 1 ? * 2 ? * 3 300 * 4 600 * 5 1200 * 6 2400 * 7 4800 ? * 8 9600 * 9 ? * 10 19200 ? * 11 ? * 12 38400 * 13 57600 ? * 14 115200 * 15 230400 ? * 16 460800 ? */ int i = 0, rc; #ifdef datageneral /* AOS/VS doesn't have htonl() but MV's are big-endian */ long net_baud = baud; #else long net_baud = htonl(baud); #endif /* datageneral */ CHAR b; debug(F111,"tnc_set_baud","begin",baud); if (ttnet != NET_TCPB) return(0); if (ttnproto != NP_TELNET) return(0); if (!TELOPT_ME(TELOPT_COMPORT)) return(0); #ifdef CK_SSL if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) { return(0); } #endif /* CK_SSL */ if (baud <= 0) return(0); if ( net_baud != 0 && net_baud == tnc_bps) return(tnc_bps); sb_out[i++] = (CHAR) IAC; /* I Am a Command */ sb_out[i++] = (CHAR) SB; /* Subnegotiation */ sb_out[i++] = TELOPT_COMPORT; /* ComPort */ sb_out[i++] = TNC_C2S_SET_BAUDRATE; /* Set Baud Rate */ if (tnc_bps_index) { /* IOS Access Server */ if (baud <= 300) b = TNC_BPS_300; else if (baud <= 600) b = TNC_BPS_600; else if (baud <= 1200) b = TNC_BPS_1200; else if (baud <= 2400) b = TNC_BPS_2400; else if (baud <= 4800) b = TNC_BPS_4800; else if (baud <= 9600) b = TNC_BPS_9600; else if (baud <= 14400) b = TNC_BPS_14400; else if (baud <= 19200) b = TNC_BPS_19200; else if (baud <= 28800) b = TNC_BPS_28800; else if (baud <= 38400) b = TNC_BPS_38400; else if (baud <= 57600) b = TNC_BPS_57600; else if (baud <= 115200) b = TNC_BPS_115200; else if (baud <= 230400) b = TNC_BPS_230400; else b = TNC_BPS_460800; sb_out[i++] = b; } else { /* RFC 2217 */ sb_out[i++] = ((char *)&net_baud)[0]; sb_out[i++] = ((char *)&net_baud)[1]; sb_out[i++] = ((char *)&net_baud)[2]; sb_out[i++] = ((char *)&net_baud)[3]; } sb_out[i++] = (CHAR) IAC; /* End of Subnegotiation */ sb_out[i++] = (CHAR) SE; /* marked by IAC SE */ #ifdef DEBUG if (deblog || tn_deb || debses) { ckmakxmsg(tn_msg_out,TN_MSG_LEN, "TELNET SENT SB ",TELOPT(TELOPT_COMPORT), " SET-BAUD-RATE ", ckltoa(baud)," IAC SE", NULL, NULL,NULL,NULL,NULL,NULL,NULL); } #endif /* DEBUG */ #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif #ifdef DEBUG debug(F100,tn_msg_out,"",0); if (tn_deb || debses) tn_debug(tn_msg_out); #endif /* DEBUG */ rc = (ttol((CHAR *)sb_out,i) < 0); /* Send it. */ #ifdef OS2 ReleaseTelnetMutex(); #endif if (rc) return(-1); if (tnc_wait((CHAR *)"comport set baud rate",0) < 0) { tn_push(); return(-1); } debug(F111,"tnc_set_baud","end",tnc_bps); return(tnc_bps); } int #ifdef CK_ANSIC tnc_get_baud(void) #else /* CK_ANSIC */ tnc_get_baud() #endif /* CK_ANSIC */ /* tnc_get_baud */ { /* send IAC SB COM-PORT SET-BAUD IAC SE */ /* wait for response */ int i = 0, rc; debug(F110,"tnc_get_baud","begin",0); if (ttnet != NET_TCPB) return(0); if (ttnproto != NP_TELNET) return(0); if (!TELOPT_ME(TELOPT_COMPORT)) return(0); #ifdef CK_SSL if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) { return(0); } #endif /* CK_SSL */ sb_out[i++] = (CHAR) IAC; /* I Am a Command */ sb_out[i++] = (CHAR) SB; /* Subnegotiation */ sb_out[i++] = TELOPT_COMPORT; /* ComPort */ sb_out[i++] = TNC_C2S_SET_BAUDRATE; /* Set Baud Rate */ if (tnc_bps_index > 0) { /* Access Server */ sb_out[i++] = 0; } else { /* RFC 2217 */ sb_out[i++] = 0; sb_out[i++] = 0; sb_out[i++] = 0; sb_out[i++] = 0; } sb_out[i++] = (CHAR) IAC; /* End of Subnegotiation */ sb_out[i++] = (CHAR) SE; /* marked by IAC SE */ #ifdef DEBUG if (deblog || tn_deb || debses) { ckmakxmsg(tn_msg_out,TN_MSG_LEN, "TELNET SENT SB ",TELOPT(TELOPT_COMPORT), " SET-BAUD-RATE ", ckltoa(0)," IAC SE", NULL, NULL,NULL,NULL,NULL,NULL,NULL); } #endif /* DEBUG */ #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif #ifdef DEBUG debug(F100,tn_msg_out,"",0); if (tn_deb || debses) tn_debug(tn_msg_out); #endif /* DEBUG */ rc = (ttol((CHAR *)sb_out,i) < 0); /* Send it. */ #ifdef OS2 ReleaseTelnetMutex(); #endif if (rc) return(-1); if (tnc_wait((CHAR *)"comport get baud rate",0) < 0) { tn_push(); return(-1); } debug(F111,"tnc_get_baud","end",tnc_bps); return(tnc_bps); } int #ifdef CK_ANSIC tnc_set_datasize(int datasize) #else /* CK_ANSIC */ tnc_set_datasize(datasize) int datasize; #endif /* CK_ANSIC */ /* tnc_set_datasize */ { /* IAC SB COM-PORT SET_DATASIZE IAC SE */ /* Valid s are 5 through 8 */ /* Wait for response */ /* return new host value */ int i = 0, rc; debug(F111,"tnc_set_datasize","begin",datasize); if (ttnet != NET_TCPB) return(0); if (ttnproto != NP_TELNET) return(0); if (!TELOPT_ME(TELOPT_COMPORT)) return(0); #ifdef CK_SSL if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) { return(0); } #endif /* CK_SSL */ if ( !(datasize >= 5 && datasize <= 8) ) return(0); if ( datasize != 0 && datasize == tnc_datasize ) return(tnc_datasize); sb_out[i++] = (CHAR) IAC; /* I Am a Command */ sb_out[i++] = (CHAR) SB; /* Subnegotiation */ sb_out[i++] = TELOPT_COMPORT; /* ComPort */ sb_out[i++] = TNC_C2S_SET_DATASIZE; /* Set DataSize */ sb_out[i++] = (unsigned char)(datasize & 0xFF); sb_out[i++] = (CHAR) IAC; /* End of Subnegotiation */ sb_out[i++] = (CHAR) SE; /* marked by IAC SE */ #ifdef DEBUG if (deblog || tn_deb || debses) { ckmakxmsg(tn_msg_out,TN_MSG_LEN, "TELNET SENT SB ",TELOPT(TELOPT_COMPORT), " SET-DATASIZE ", ckitoa(datasize)," IAC SE", NULL, NULL,NULL,NULL,NULL,NULL,NULL); } #endif /* DEBUG */ #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif #ifdef DEBUG debug(F100,tn_msg_out,"",0); if (tn_deb || debses) tn_debug(tn_msg_out); #endif /* DEBUG */ rc = (ttol((CHAR *)sb_out,i) < 0); /* Send it. */ #ifdef OS2 ReleaseTelnetMutex(); #endif if (rc) return(-1); if (tnc_wait((CHAR *)"comport set datasize",0) < 0) { tn_push(); return(-1); } debug(F111,"tnc_set_datasize","end",tnc_datasize); return(tnc_datasize); } int #ifdef CK_ANSIC tnc_get_datasize(void) #else /* CK_ANSIC */ tnc_get_datasize() #endif /* CK_ANSIC */ /* tnc_get_datasize */ { /* IAC SB COM-PORT SET_DATASIZE IAC SE */ /* Wait for response */ int i = 0, rc; debug(F110,"tnc_get_datasize","begin",0); if (ttnet != NET_TCPB) return(0); if (ttnproto != NP_TELNET) return(0); if (!TELOPT_ME(TELOPT_COMPORT)) return(0); #ifdef CK_SSL if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) { return(0); } #endif /* CK_SSL */ sb_out[i++] = (CHAR) IAC; /* I Am a Command */ sb_out[i++] = (CHAR) SB; /* Subnegotiation */ sb_out[i++] = TELOPT_COMPORT; /* ComPort */ sb_out[i++] = TNC_C2S_SET_DATASIZE; /* Set DataSize */ sb_out[i++] = 0; sb_out[i++] = (CHAR) IAC; /* End of Subnegotiation */ sb_out[i++] = (CHAR) SE; /* marked by IAC SE */ #ifdef DEBUG if (deblog || tn_deb || debses) { ckmakxmsg(tn_msg_out,TN_MSG_LEN, "TELNET SENT SB ",TELOPT(TELOPT_COMPORT), " SET-DATASIZE ", ckltoa(0)," IAC SE", NULL, NULL,NULL,NULL,NULL,NULL,NULL); } #endif /* DEBUG */ #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif #ifdef DEBUG debug(F100,tn_msg_out,"",0); if (tn_deb || debses) tn_debug(tn_msg_out); #endif /* DEBUG */ rc = (ttol((CHAR *)sb_out,i) < 0); /* Send it. */ #ifdef OS2 ReleaseTelnetMutex(); #endif if (rc) return(-1); if (tnc_wait((CHAR *)"comport get datasize",0) < 0) { tn_push(); return(-1); } debug(F111,"tnc_get_datasize","end",tnc_datasize); return(tnc_datasize); } int #ifdef CK_ANSIC tnc_set_parity(int parity) #else /* CK_ANSIC */ tnc_set_parity(parity) int parity; #endif /* CK_ANSIC */ /* tnc_set_parity */ { /* IAC SB COM-PORT SET_PARITY IAC SE */ /* Value Parity * 1 None * 2 Odd * 3 Even * 4 Mark * 5 Space */ /* Wait for response. Return new host value. */ int i = 0, rc; debug(F110,"tnc_set_parity","begin",parity); if (ttnet != NET_TCPB) return(0); if (ttnproto != NP_TELNET) return(0); if (!TELOPT_ME(TELOPT_COMPORT)) return(0); #ifdef CK_SSL if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) { return(0); } #endif /* CK_SSL */ if ( !(parity >= 1 && parity <= 5) ) return(0); if ( parity != 0 && parity == tnc_parity ) return(tnc_parity); sb_out[i++] = (CHAR) IAC; /* I Am a Command */ sb_out[i++] = (CHAR) SB; /* Subnegotiation */ sb_out[i++] = TELOPT_COMPORT; /* ComPort */ sb_out[i++] = TNC_C2S_SET_PARITY; /* Set Parity */ sb_out[i++] = (unsigned char)(parity & 0xFF); sb_out[i++] = (CHAR) IAC; /* End of Subnegotiation */ sb_out[i++] = (CHAR) SE; /* marked by IAC SE */ #ifdef DEBUG if (deblog || tn_deb || debses) { ckmakxmsg(tn_msg_out,TN_MSG_LEN, "TELNET SENT SB ",TELOPT(TELOPT_COMPORT), " SET-PARITY ", ckitoa(parity)," IAC SE", NULL, NULL,NULL,NULL,NULL,NULL,NULL); } #endif /* DEBUG */ #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif #ifdef DEBUG debug(F100,tn_msg_out,"",0); if (tn_deb || debses) tn_debug(tn_msg_out); #endif /* DEBUG */ rc = (ttol((CHAR *)sb_out,i) < 0); /* Send it. */ #ifdef OS2 ReleaseTelnetMutex(); #endif if (rc) return(-1); if (tnc_wait((CHAR *)"comport set parity",0) < 0) { tn_push(); return(-1); } debug(F111,"tnc_set_parity","end",tnc_parity); return(tnc_parity); } int #ifdef CK_ANSIC tnc_get_parity(void) #else /* CK_ANSIC */ tnc_get_parity() #endif /* CK_ANSIC */ /* tnc_get_parity */ { /* IAC SB COM-PORT SET_PARITY IAC SE */ /* wait for response */ int i = 0, rc; debug(F110,"tnc_get_parity","begin",0); if (ttnet != NET_TCPB) return(0); if (ttnproto != NP_TELNET) return(0); if (!TELOPT_ME(TELOPT_COMPORT)) return(0); #ifdef CK_SSL if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) { return(0); } #endif /* CK_SSL */ sb_out[i++] = (CHAR) IAC; /* I Am a Command */ sb_out[i++] = (CHAR) SB; /* Subnegotiation */ sb_out[i++] = TELOPT_COMPORT; /* ComPort */ sb_out[i++] = TNC_C2S_SET_PARITY; /* Set Parity */ sb_out[i++] = 0; sb_out[i++] = (CHAR) IAC; /* End of Subnegotiation */ sb_out[i++] = (CHAR) SE; /* marked by IAC SE */ #ifdef DEBUG if (deblog || tn_deb || debses) { ckmakxmsg(tn_msg_out,TN_MSG_LEN, "TELNET SENT SB ",TELOPT(TELOPT_COMPORT), " SET-PARITY ", ckitoa(0)," IAC SE", NULL, NULL,NULL,NULL,NULL,NULL,NULL); } #endif /* DEBUG */ #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif #ifdef DEBUG debug(F100,tn_msg_out,"",0); if (tn_deb || debses) tn_debug(tn_msg_out); #endif /* DEBUG */ rc = (ttol((CHAR *)sb_out,i) < 0); /* Send it. */ #ifdef OS2 ReleaseTelnetMutex(); #endif if (rc) return(-1); if (tnc_wait((CHAR *)"comport get parity",0) < 0) { tn_push(); return(-1); } debug(F111,"tnc_get_parity","end",tnc_parity); return(tnc_parity); } int #ifdef CK_ANSIC tnc_set_stopsize(int stopsize) #else /* CK_ANSIC */ tnc_set_stopsize(stopsize) int stopsize; #endif /* CK_ANSIC */ /* tnc_set_stopsize */ { /* IAC SB COM-PORT SET_STOPSIZE IAC SE */ /* Value Stop Bit Size * 1 1 * 2 2 * 3 1.5 */ /* Wait for response. Return new host value. */ int i = 0, rc; debug(F111,"tnc_set_stopsize","begin",stopsize); if (ttnet != NET_TCPB) return(0); if (ttnproto != NP_TELNET) return(0); if (!TELOPT_ME(TELOPT_COMPORT)) return(0); #ifdef CK_SSL if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) { return(0); } #endif /* CK_SSL */ if (!(stopsize >= 1 && stopsize <= 3) ) return(0); if ( stopsize != 0 && stopsize == tnc_stopbit ) return(tnc_stopbit); sb_out[i++] = (CHAR) IAC; /* I Am a Command */ sb_out[i++] = (CHAR) SB; /* Subnegotiation */ sb_out[i++] = TELOPT_COMPORT; /* ComPort */ sb_out[i++] = TNC_C2S_SET_STOPSIZE; /* Set Stop Bits */ sb_out[i++] = (unsigned char)(stopsize & 0xFF); sb_out[i++] = (CHAR) IAC; /* End of Subnegotiation */ sb_out[i++] = (CHAR) SE; /* marked by IAC SE */ #ifdef DEBUG if (deblog || tn_deb || debses) { ckmakxmsg(tn_msg_out,TN_MSG_LEN, "TELNET SENT SB ",TELOPT(TELOPT_COMPORT), " SET-STOPSIZE ", ckitoa(stopsize)," IAC SE", NULL, NULL,NULL,NULL,NULL,NULL,NULL); } #endif /* DEBUG */ #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif #ifdef DEBUG debug(F100,tn_msg_out,"",0); if (tn_deb || debses) tn_debug(tn_msg_out); #endif /* DEBUG */ rc = (ttol((CHAR *)sb_out,i) < 0); /* Send it. */ #ifdef OS2 ReleaseTelnetMutex(); #endif if (rc) return(-1); if (tnc_wait((CHAR *)"comport set stopsize",0) < 0) { tn_push(); return(-1); } debug(F111,"tnc_set_stopsize","end",tnc_stopbit); return(tnc_stopbit); } int #ifdef CK_ANSIC tnc_get_stopsize(void) #else /* CK_ANSIC */ tnc_get_stopsize() #endif /* CK_ANSIC */ /* tnc_get_stopsize */ { /* IAC SB COM-PORT SET_STOPSIZE IAC SE */ /* Wait for response */ int i = 0, rc; debug(F110,"tnc_get_stopsize","begin",0); if (ttnet != NET_TCPB) return(0); if (ttnproto != NP_TELNET) return(0); if (!TELOPT_ME(TELOPT_COMPORT)) return(0); #ifdef CK_SSL if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) { return(0); } #endif /* CK_SSL */ sb_out[i++] = (CHAR) IAC; /* I Am a Command */ sb_out[i++] = (CHAR) SB; /* Subnegotiation */ sb_out[i++] = TELOPT_COMPORT; /* ComPort */ sb_out[i++] = TNC_C2S_SET_STOPSIZE; /* Set Stop Bits */ sb_out[i++] = 0; sb_out[i++] = (CHAR) IAC; /* End of Subnegotiation */ sb_out[i++] = (CHAR) SE; /* marked by IAC SE */ #ifdef DEBUG if (deblog || tn_deb || debses) { ckmakxmsg(tn_msg_out,TN_MSG_LEN, "TELNET SENT SB ",TELOPT(TELOPT_COMPORT), " SET-STOPSIZE ", ckitoa(0)," IAC SE", NULL, NULL,NULL,NULL,NULL,NULL,NULL); } #endif /* DEBUG */ #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif #ifdef DEBUG debug(F100,tn_msg_out,"",0); if (tn_deb || debses) tn_debug(tn_msg_out); #endif /* DEBUG */ rc = (ttol((CHAR *)sb_out,i) < 0); /* Send it. */ #ifdef OS2 ReleaseTelnetMutex(); #endif if (rc) return(-1); if (tnc_wait((CHAR *)"comport set stopsize",0) < 0) { tn_push(); return(-1); } debug(F111,"tnc_get_stopsize","end",tnc_stopbit); return(tnc_stopbit); } int #ifdef CK_ANSIC tnc_set_oflow(int control) #else /* CK_ANSIC */ tnc_set_oflow(control) int control; #endif /* CK_ANSIC */ /* tnc_set_oflow */ { /* IAC SB COM_PORT SET_CONTROL IAC SE */ /* Value Flow Control * 1 No Flow Control * 2 Xon/Xoff * 3 Rts/Cts * 17 DCD * 19 DSR */ /* wait for response, return new host value. */ int i = 0, rc; debug(F111,"tnc_set_oflow","begin",control); if (ttnet != NET_TCPB) return(0); if (ttnproto != NP_TELNET) return(0); if (!TELOPT_ME(TELOPT_COMPORT)) return(0); #ifdef CK_SSL if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) { return(0); } #endif /* CK_SSL */ if (control != 1 && control != 2 && control != 3 && control != 17 && control != 19) return(0); if ( control != 0 && control == tnc_oflow ) return(tnc_oflow); sb_out[i++] = (CHAR) IAC; /* I Am a Command */ sb_out[i++] = (CHAR) SB; /* Subnegotiation */ sb_out[i++] = TELOPT_COMPORT; /* ComPort */ sb_out[i++] = TNC_C2S_SET_CONTROL; /* Set Control */ sb_out[i++] = (unsigned char)(control & 0xFF); sb_out[i++] = (CHAR) IAC; /* End of Subnegotiation */ sb_out[i++] = (CHAR) SE; /* marked by IAC SE */ #ifdef DEBUG if (deblog || tn_deb || debses) { ckmakxmsg(tn_msg_out,TN_MSG_LEN, "TELNET SENT SB ",TELOPT(TELOPT_COMPORT), " SET-CONTROL ", ckitoa(control)," IAC SE", NULL, NULL,NULL,NULL,NULL,NULL,NULL); } #endif /* DEBUG */ #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif #ifdef DEBUG debug(F100,tn_msg_out,"",0); if (tn_deb || debses) tn_debug(tn_msg_out); #endif /* DEBUG */ rc = (ttol((CHAR *)sb_out,i) < 0); /* Send it. */ #ifdef OS2 ReleaseTelnetMutex(); #endif if (rc) return(-1); if (tnc_wait((CHAR *)"comport set outbound flow control",0) < 0) { tn_push(); return(-1); } debug(F111,"tnc_set_oflow","end",tnc_oflow); return(tnc_oflow); } int #ifdef CK_ANSIC tnc_get_oflow(void) #else /* CK_ANSIC */ tnc_get_oflow() #endif /* CK_ANSIC */ /* tnc_get_oflow */ { /* IAC SB COM_PORT SET_CONTROL IAC SE */ /* wait for response */ int i = 0, rc; debug(F110,"tnc_get_oflow","begin",0); if (ttnet != NET_TCPB) return(0); if (ttnproto != NP_TELNET) return(0); if (!TELOPT_ME(TELOPT_COMPORT)) return(0); #ifdef CK_SSL if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) { return(0); } #endif /* CK_SSL */ sb_out[i++] = (CHAR) IAC; /* I Am a Command */ sb_out[i++] = (CHAR) SB; /* Subnegotiation */ sb_out[i++] = TELOPT_COMPORT; /* ComPort */ sb_out[i++] = TNC_C2S_SET_CONTROL; /* Set Control */ sb_out[i++] = TNC_CTL_OFLOW_REQUEST; sb_out[i++] = (CHAR) IAC; /* End of Subnegotiation */ sb_out[i++] = (CHAR) SE; /* marked by IAC SE */ #ifdef DEBUG if (deblog || tn_deb || debses) { ckmakxmsg(tn_msg_out,TN_MSG_LEN, "TELNET SENT SB ",TELOPT(TELOPT_COMPORT), " SET-CONTROL ", ckitoa(TNC_CTL_OFLOW_REQUEST), " IAC SE", NULL, NULL,NULL,NULL,NULL,NULL,NULL); } #endif /* DEBUG */ #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif #ifdef DEBUG debug(F100,tn_msg_out,"",0); if (tn_deb || debses) tn_debug(tn_msg_out); #endif /* DEBUG */ rc = (ttol((CHAR *)sb_out,i) < 0); /* Send it. */ #ifdef OS2 ReleaseTelnetMutex(); #endif if (rc) return(-1); if (tnc_wait((CHAR *)"comport get outbound flow control",0) < 0) { tn_push(); return(-1); } debug(F111,"tnc_get_oflow","end",tnc_oflow); return(tnc_oflow); } int #ifdef CK_ANSIC tnc_set_iflow(int control) #else /* CK_ANSIC */ tnc_set_iflow(control) int control; #endif /* CK_ANSIC */ /* tnc_set_iflow */ { /* IAC SB COM_PORT SET_CONTROL IAC SE */ /* Value Flow Control * 14 No Flow Control * 15 Xon/Xoff * 16 Rts/Cts * 18 DTR */ /* wait for response, return new host value. */ int i = 0, rc; debug(F111,"tnc_set_iflow","begin",control); if (ttnet != NET_TCPB) return(0); if (ttnproto != NP_TELNET) return(0); if (!TELOPT_ME(TELOPT_COMPORT)) return(0); #ifdef CK_SSL if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) { return(0); } #endif /* CK_SSL */ if (control != 14 && control != 15 && control != 16 && control != 18) return(0); if ( control != 0 && control == tnc_iflow ) return(tnc_iflow); sb_out[i++] = (CHAR) IAC; /* I Am a Command */ sb_out[i++] = (CHAR) SB; /* Subnegotiation */ sb_out[i++] = TELOPT_COMPORT; /* ComPort */ sb_out[i++] = TNC_C2S_SET_CONTROL; /* Set Control */ sb_out[i++] = (unsigned char)(control & 0xFF); sb_out[i++] = (CHAR) IAC; /* End of Subnegotiation */ sb_out[i++] = (CHAR) SE; /* marked by IAC SE */ #ifdef DEBUG if (deblog || tn_deb || debses) { ckmakxmsg(tn_msg_out,TN_MSG_LEN, "TELNET SENT SB ",TELOPT(TELOPT_COMPORT), " SET-CONTROL ", ckitoa(control)," IAC SE", NULL, NULL,NULL,NULL,NULL,NULL,NULL); } #endif /* DEBUG */ #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif #ifdef DEBUG debug(F100,tn_msg_out,"",0); if (tn_deb || debses) tn_debug(tn_msg_out); #endif /* DEBUG */ rc = (ttol((CHAR *)sb_out,i) < 0); /* Send it. */ #ifdef OS2 ReleaseTelnetMutex(); #endif if (rc) return(-1); if (tnc_wait((CHAR *)"comport set inbound flow control",0) < 0) { tn_push(); return(-1); } debug(F111,"tnc_set_iflow","end",tnc_iflow); return(tnc_iflow); } int #ifdef CK_ANSIC tnc_get_iflow(void) #else /* CK_ANSIC */ tnc_get_iflow() #endif /* CK_ANSIC */ /* tnc_get_iflow */ { /* IAC SB COM_PORT SET_CONTROL IAC SE */ /* wait for response */ int i = 0, rc; debug(F110,"tnc_get_iflow","begin",0); if (ttnet != NET_TCPB) return(0); if (ttnproto != NP_TELNET) return(0); if (!TELOPT_ME(TELOPT_COMPORT)) return(0); #ifdef CK_SSL if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) { return(0); } #endif /* CK_SSL */ sb_out[i++] = (CHAR) IAC; /* I Am a Command */ sb_out[i++] = (CHAR) SB; /* Subnegotiation */ sb_out[i++] = TELOPT_COMPORT; /* ComPort */ sb_out[i++] = TNC_C2S_SET_CONTROL; /* Set Control */ sb_out[i++] = TNC_CTL_IFLOW_REQUEST; sb_out[i++] = (CHAR) IAC; /* End of Subnegotiation */ sb_out[i++] = (CHAR) SE; /* marked by IAC SE */ #ifdef DEBUG if (deblog || tn_deb || debses) { ckmakxmsg(tn_msg_out,TN_MSG_LEN, "TELNET SENT SB ",TELOPT(TELOPT_COMPORT), " SET-CONTROL ", ckitoa(TNC_CTL_IFLOW_REQUEST), " IAC SE", NULL, NULL,NULL,NULL,NULL,NULL,NULL); } #endif /* DEBUG */ #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif #ifdef DEBUG debug(F100,tn_msg_out,"",0); if (tn_deb || debses) tn_debug(tn_msg_out); #endif /* DEBUG */ rc = (ttol((CHAR *)sb_out,i) < 0); /* Send it. */ #ifdef OS2 ReleaseTelnetMutex(); #endif if (rc) return(-1); if (tnc_wait((CHAR *)"comport get inbound flow control",0) < 0) { tn_push(); return(-1); } debug(F111,"tnc_get_iflow","end",tnc_iflow); return(tnc_iflow); } int #ifdef CK_ANSIC tnc_set_break_state(int onoff) #else /* CK_ANSIC */ tnc_set_break_state(onoff) int onoff; #endif /* CK_ANSIC */ /* tnc_set_break_state */ { /* IAC SB COM_PORT SET_CONTROL IAC SE */ /* Value Break State * 5 On * 6 Off */ /* wait for response, return new host value. */ int i = 0, rc; debug(F111,"tnc_set_break_state","begin",onoff); if (ttnet != NET_TCPB) return(0); if (ttnproto != NP_TELNET) return(0); if (!TELOPT_ME(TELOPT_COMPORT)) return(0); #ifdef CK_SSL if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) { return(0); } #endif /* CK_SSL */ if ( onoff != 0 && onoff == tnc_break ) return(tnc_break); sb_out[i++] = (CHAR) IAC; /* I Am a Command */ sb_out[i++] = (CHAR) SB; /* Subnegotiation */ sb_out[i++] = TELOPT_COMPORT; /* ComPort */ sb_out[i++] = TNC_C2S_SET_CONTROL; /* Set Control */ sb_out[i++] = onoff ? TNC_CTL_BREAK_ON : TNC_CTL_BREAK_OFF; sb_out[i++] = (CHAR) IAC; /* End of Subnegotiation */ sb_out[i++] = (CHAR) SE; /* marked by IAC SE */ #ifdef DEBUG if (deblog || tn_deb || debses) { ckmakxmsg(tn_msg_out,TN_MSG_LEN, "TELNET SENT SB ",TELOPT(TELOPT_COMPORT), " SET-CONTROL ", onoff ? "BREAK-ON" : "BREAK-OFF", " IAC SE", NULL, NULL,NULL,NULL,NULL,NULL,NULL); } #endif /* DEBUG */ #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif #ifdef DEBUG debug(F100,tn_msg_out,"",0); if (tn_deb || debses) tn_debug(tn_msg_out); #endif /* DEBUG */ rc = (ttol((CHAR *)sb_out,i) < 0); /* Send it. */ #ifdef OS2 ReleaseTelnetMutex(); #endif if (rc) return(-1); if (tnc_wait((CHAR *)"comport set break state",0) < 0) { tn_push(); return(-1); } debug(F111,"tnc_set_break_state","end",tnc_break); return(tnc_break); } int #ifdef CK_ANSIC tnc_get_break_state(void) #else /* CK_ANSIC */ tnc_get_break_state() #endif /* CK_ANSIC */ /* tnc_get_break_state */ { /* IAC SB COM_PORT SET_CONTROL IAC SE */ /* wait for response */ int i = 0, rc; debug(F110,"tnc_get_break_state","begin",0); if (ttnet != NET_TCPB) return(0); if (ttnproto != NP_TELNET) return(0); if (!TELOPT_ME(TELOPT_COMPORT)) return(0); #ifdef CK_SSL if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) { return(0); } #endif /* CK_SSL */ sb_out[i++] = (CHAR) IAC; /* I Am a Command */ sb_out[i++] = (CHAR) SB; /* Subnegotiation */ sb_out[i++] = TELOPT_COMPORT; /* ComPort */ sb_out[i++] = TNC_C2S_SET_CONTROL; /* Set Control */ sb_out[i++] = TNC_CTL_BREAK_REQUEST; sb_out[i++] = (CHAR) IAC; /* End of Subnegotiation */ sb_out[i++] = (CHAR) SE; /* marked by IAC SE */ #ifdef DEBUG if (deblog || tn_deb || debses) { ckmakxmsg(tn_msg_out,TN_MSG_LEN, "TELNET SENT SB ",TELOPT(TELOPT_COMPORT), " SET-CONTROL ", "BREAK-REQUEST", " IAC SE", NULL, NULL,NULL,NULL,NULL,NULL,NULL); } #endif /* DEBUG */ #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif #ifdef DEBUG debug(F100,tn_msg_out,"",0); if (tn_deb || debses) tn_debug(tn_msg_out); #endif /* DEBUG */ rc = (ttol((CHAR *)sb_out,i) < 0); /* Send it. */ #ifdef OS2 ReleaseTelnetMutex(); #endif if (rc) return(-1); if (tnc_wait((CHAR *)"comport get break state",0) < 0) { tn_push(); return(-1); } debug(F111,"tnc_get_break_state","end",tnc_break); return(tnc_break); } int #ifdef CK_ANSIC tnc_set_dtr_state(int onoff) #else /* CK_ANSIC */ tnc_set_dtr_state(onoff) int onoff; #endif /* CK_ANSIC */ /* tnc_set_dtr_state */ { /* IAC SB COM_PORT SET_CONTROL IAC SE */ /* Value Dtr State * 8 On * 9 Off */ /* wait for response, return new host value. */ int i = 0, rc; debug(F111,"tnc_set_dtr_state","begin",onoff); if (ttnet != NET_TCPB) return(0); if (ttnproto != NP_TELNET) return(0); if (!TELOPT_ME(TELOPT_COMPORT)) return(0); #ifdef CK_SSL if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) { return(0); } #endif /* CK_SSL */ if ( onoff != 0 && onoff == tnc_dtr ) return(tnc_dtr); sb_out[i++] = (CHAR) IAC; /* I Am a Command */ sb_out[i++] = (CHAR) SB; /* Subnegotiation */ sb_out[i++] = TELOPT_COMPORT; /* ComPort */ sb_out[i++] = TNC_C2S_SET_CONTROL; /* Set Control */ sb_out[i++] = onoff ? TNC_CTL_DTR_ON : TNC_CTL_DTR_OFF; sb_out[i++] = (CHAR) IAC; /* End of Subnegotiation */ sb_out[i++] = (CHAR) SE; /* marked by IAC SE */ #ifdef DEBUG if (deblog || tn_deb || debses) { ckmakxmsg(tn_msg_out,TN_MSG_LEN, "TELNET SENT SB ",TELOPT(TELOPT_COMPORT), " SET-CONTROL ", onoff ? "DTR-ON" : "DTR-OFF", " IAC SE", NULL, NULL,NULL,NULL,NULL,NULL,NULL); } #endif /* DEBUG */ #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif #ifdef DEBUG debug(F100,tn_msg_out,"",0); if (tn_deb || debses) tn_debug(tn_msg_out); #endif /* DEBUG */ rc = (ttol((CHAR *)sb_out,i) < 0); /* Send it. */ #ifdef OS2 ReleaseTelnetMutex(); #endif if (rc) return(-1); if (tnc_wait((CHAR *)"comport set dtr state",0) < 0) { tn_push(); return(-1); } debug(F111,"tnc_set_dtr_state","end",tnc_dtr); return(tnc_dtr); } int #ifdef CK_ANSIC tnc_get_dtr_state(void) #else /* CK_ANSIC */ tnc_get_dtr_state() #endif /* CK_ANSIC */ /* tnc_get_dtr_state */ { /* IAC SB COM_PORT SET_CONTROL IAC SE */ /* wait for response */ int i = 0, rc; debug(F110,"tnc_get_dtr_state","begin",0); if (ttnet != NET_TCPB) return(0); if (ttnproto != NP_TELNET) return(0); if (!TELOPT_ME(TELOPT_COMPORT)) return(0); #ifdef CK_SSL if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) { return(0); } #endif /* CK_SSL */ sb_out[i++] = (CHAR) IAC; /* I Am a Command */ sb_out[i++] = (CHAR) SB; /* Subnegotiation */ sb_out[i++] = TELOPT_COMPORT; /* ComPort */ sb_out[i++] = TNC_C2S_SET_CONTROL; /* Set Control */ sb_out[i++] = TNC_CTL_DTR_REQUEST; sb_out[i++] = (CHAR) IAC; /* End of Subnegotiation */ sb_out[i++] = (CHAR) SE; /* marked by IAC SE */ #ifdef DEBUG if (deblog || tn_deb || debses) { ckmakxmsg(tn_msg_out,TN_MSG_LEN, "TELNET SENT SB ",TELOPT(TELOPT_COMPORT), " SET-CONTROL ", "DTR-REQUEST", " IAC SE", NULL, NULL,NULL,NULL,NULL,NULL,NULL); } #endif /* DEBUG */ #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif #ifdef DEBUG debug(F100,tn_msg_out,"",0); if (tn_deb || debses) tn_debug(tn_msg_out); #endif /* DEBUG */ rc = (ttol((CHAR *)sb_out,i) < 0); /* Send it. */ #ifdef OS2 ReleaseTelnetMutex(); #endif if (rc) return(-1); if (tnc_wait((CHAR *)"comport get dtr state",0) < 0) { tn_push(); return(-1); } debug(F111,"tnc_get_dtr_state","end",tnc_dtr); return(tnc_dtr); } int #ifdef CK_ANSIC tnc_set_rts_state(int onoff) #else /* CK_ANSIC */ tnc_set_rts_state(onoff) int onoff; #endif /* CK_ANSIC */ /* tnc_set_rts_state */ { /* IAC SB COM_PORT SET_CONTROL IAC SE */ /* Value Rts State * 5 On * 6 Off */ /* wait for response, return new host value. */ int i = 0, rc; debug(F111,"tnc_set_rts_state","begin",onoff); if (ttnet != NET_TCPB) return(0); if (ttnproto != NP_TELNET) return(0); if (!TELOPT_ME(TELOPT_COMPORT)) return(0); #ifdef CK_SSL if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) { return(0); } #endif /* CK_SSL */ if ( onoff != 0 && onoff == tnc_rts ) return(tnc_rts); sb_out[i++] = (CHAR) IAC; /* I Am a Command */ sb_out[i++] = (CHAR) SB; /* Subnegotiation */ sb_out[i++] = TELOPT_COMPORT; /* ComPort */ sb_out[i++] = TNC_C2S_SET_CONTROL; /* Set Control */ sb_out[i++] = onoff ? TNC_CTL_RTS_ON : TNC_CTL_RTS_OFF; sb_out[i++] = (CHAR) IAC; /* End of Subnegotiation */ sb_out[i++] = (CHAR) SE; /* marked by IAC SE */ #ifdef DEBUG if (deblog || tn_deb || debses) { ckmakxmsg(tn_msg_out,TN_MSG_LEN, "TELNET SENT SB ",TELOPT(TELOPT_COMPORT), " SET-CONTROL ", onoff ? "RTS-ON" : "RTS-OFF", " IAC SE", NULL, NULL,NULL,NULL,NULL,NULL,NULL); } #endif /* DEBUG */ #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif #ifdef DEBUG debug(F100,tn_msg_out,"",0); if (tn_deb || debses) tn_debug(tn_msg_out); #endif /* DEBUG */ rc = (ttol((CHAR *)sb_out,i) < 0); /* Send it. */ #ifdef OS2 ReleaseTelnetMutex(); #endif if (rc) return(-1); if (tnc_wait((CHAR *)"comport set rts state",0) < 0) { tn_push(); return(-1); } debug(F111,"tnc_set_rts_state","end",tnc_rts); return(tnc_rts); } int #ifdef CK_ANSIC tnc_get_rts_state(void) #else /* CK_ANSIC */ tnc_get_rts_state() #endif /* CK_ANSIC */ /* tnc_get_rts_state */ { /* IAC SB COM_PORT SET_CONTROL IAC SE */ /* wait for response */ int i = 0, rc; debug(F110,"tnc_get_rts_state","begin",0); if (ttnet != NET_TCPB) return(0); if (ttnproto != NP_TELNET) return(0); if (!TELOPT_ME(TELOPT_COMPORT)) return(0); #ifdef CK_SSL if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) { return(0); } #endif /* CK_SSL */ sb_out[i++] = (CHAR) IAC; /* I Am a Command */ sb_out[i++] = (CHAR) SB; /* Subnegotiation */ sb_out[i++] = TELOPT_COMPORT; /* ComPort */ sb_out[i++] = TNC_C2S_SET_CONTROL; /* Set Control */ sb_out[i++] = TNC_CTL_RTS_REQUEST; sb_out[i++] = (CHAR) IAC; /* End of Subnegotiation */ sb_out[i++] = (CHAR) SE; /* marked by IAC SE */ #ifdef DEBUG if (deblog || tn_deb || debses) { ckmakxmsg(tn_msg_out,TN_MSG_LEN, "TELNET SENT SB ",TELOPT(TELOPT_COMPORT), " SET-CONTROL ", "RTS-REQUEST", " IAC SE", NULL, NULL,NULL,NULL,NULL,NULL,NULL); } #endif /* DEBUG */ #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif #ifdef DEBUG debug(F100,tn_msg_out,"",0); if (tn_deb || debses) tn_debug(tn_msg_out); #endif /* DEBUG */ rc = (ttol((CHAR *)sb_out,i) < 0); /* Send it. */ #ifdef OS2 ReleaseTelnetMutex(); #endif if (rc) return(-1); if (tnc_wait((CHAR *)"comport get rts state",0) < 0) { tn_push(); return(-1); } debug(F111,"tnc_get_rts_state","end",tnc_rts); return(tnc_rts); } int #ifdef CK_ANSIC tnc_set_ls_mask(int mask) #else /* CK_ANSIC */ tnc_set_ls_mask(mask) int mask; #endif /* CK_ANSIC */ /* tnc_set_ls_mask */ { /* IAC SB COM_PORT SET_LINESTATE_MASK IAC SE */ /* Bit Meaning * 0 Data Ready * 1 Overrun Error * 2 Parity Error * 3 Framing Error * 4 Break Detect Error * 5 Transfer Holding Register Empty * 6 Transfer Shift Register Empty * 7 Timeout Error */ int i = 0, rc; debug(F111,"tnc_set_ls_mask","begin",mask); if (ttnet != NET_TCPB) return(0); if (ttnproto != NP_TELNET) return(0); if (!TELOPT_ME(TELOPT_COMPORT)) return(0); #ifdef CK_SSL if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) { return(0); } #endif /* CK_SSL */ if ( mask != 0 && mask == tnc_ls_mask ) return(tnc_ls_mask); sb_out[i++] = (CHAR) IAC; /* I Am a Command */ sb_out[i++] = (CHAR) SB; /* Subnegotiation */ sb_out[i++] = TELOPT_COMPORT; /* ComPort */ sb_out[i++] = TNC_C2S_SET_LS_MASK; sb_out[i++] = (unsigned char)(mask & 0xFF); if (sb_out[i-1] == IAC ) sb_out[i++] = IAC; sb_out[i++] = (CHAR) IAC; /* End of Subnegotiation */ sb_out[i++] = (CHAR) SE; /* marked by IAC SE */ #ifdef DEBUG if (deblog || tn_deb || debses) { ckmakxmsg(tn_msg_out,TN_MSG_LEN, "TELNET SENT SB ",TELOPT(TELOPT_COMPORT), " SET-LINESTATE-MASK ", ckitoa(mask & 0xFF), " IAC SE", NULL, NULL,NULL,NULL,NULL,NULL,NULL); } #endif /* DEBUG */ #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif #ifdef DEBUG debug(F100,tn_msg_out,"",0); if (tn_deb || debses) tn_debug(tn_msg_out); #endif /* DEBUG */ rc = (ttol((CHAR *)sb_out,i) < 0); /* Send it. */ #ifdef OS2 ReleaseTelnetMutex(); #endif if (rc) return(-1); tnc_ls_mask = mask; debug(F111,"tnc_set_ls_mask","end",tnc_ls_mask); return(0); } int #ifdef CK_ANSIC tnc_get_ls_mask(void) #else /* CK_ANSIC */ tnc_get_ls_mask() #endif /* CK_ANSIC */ /* tnc_get_ls_mask */ { debug(F101,"tnc_get_ls_mask","",tnc_ls_mask); return(tnc_ls_mask); } int #ifdef CK_ANSIC tnc_get_ls(void) #else /* CK_ANSIC */ tnc_get_ls() #endif /* CK_ANSIC */ /* tnc_get_ls */ { int ls = tnc_ls; debug(F101,"tnc_get_ls","",tnc_ls); return(ls); } int #ifdef CK_ANSIC tnc_set_ms_mask(int mask) #else /* CK_ANSIC */ tnc_set_ms_mask(mask) int mask; #endif /* CK_ANSIC */ /* tnc_set_ms_mask */ { /* IAC SB COM_PORT SET_MODEMSTATE_MASK IAC SE */ /* Bit Meaning * 0 Delta Clear To Send * 1 Delta Data Set Ready * 2 Trailing Edge Ring Detector * 3 Delta Receive Line Signal (Carrier) Detect * 4 Clear To Send Signal State * 5 Data-Set-Ready Signal State * 6 Ring Indicator * 7 Receive Line Signal (Carrier) Detect */ int i = 0, rc; debug(F111,"tnc_set_ms_mask","begin",mask); if (ttnet != NET_TCPB) return(0); if (ttnproto != NP_TELNET) return(0); if (!TELOPT_ME(TELOPT_COMPORT)) return(0); #ifdef CK_SSL if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) { return(0); } #endif /* CK_SSL */ if ( mask != 0 && mask == tnc_ms_mask ) return(tnc_ms_mask); sb_out[i++] = (CHAR) IAC; /* I Am a Command */ sb_out[i++] = (CHAR) SB; /* Subnegotiation */ sb_out[i++] = TELOPT_COMPORT; /* ComPort */ sb_out[i++] = TNC_C2S_SET_MS_MASK; sb_out[i++] = (unsigned char)(mask & 0xFF); if (sb_out[i-1] == IAC ) sb_out[i++] = IAC; sb_out[i++] = (CHAR) IAC; /* End of Subnegotiation */ sb_out[i++] = (CHAR) SE; /* marked by IAC SE */ #ifdef DEBUG if (deblog || tn_deb || debses) { ckmakxmsg(tn_msg_out,TN_MSG_LEN, "TELNET SENT SB ",TELOPT(TELOPT_COMPORT), " SET-MODEMSTATE-MASK ", ckitoa(mask & 0xFF), " IAC SE", NULL, NULL,NULL,NULL,NULL,NULL,NULL); } #endif /* DEBUG */ #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif #ifdef DEBUG debug(F100,tn_msg_out,"",0); if (tn_deb || debses) tn_debug(tn_msg_out); #endif /* DEBUG */ rc = (ttol((CHAR *)sb_out,i) < 0); /* Send it. */ #ifdef OS2 ReleaseTelnetMutex(); #endif if (rc) return(-1); tnc_ms_mask = mask; debug(F111,"tnc_set_ms_mask","end",tnc_ms_mask); return(0); } int #ifdef CK_ANSIC tnc_get_ms_mask(void) #else /* CK_ANSIC */ tnc_get_ms_mask() #endif /* CK_ANSIC */ /* tnc_get_ms_mask */ { debug(F101,"tnc_get_gs_mask","",tnc_ms_mask); return(tnc_ms_mask); } int #ifdef CK_ANSIC tnc_get_ms(void) #else /* CK_ANSIC */ tnc_get_ms() #endif /* CK_ANSIC */ /* tnc_get_ms */ { int ms = tnc_ms; debug(F101,"tnc_get_ms","",tnc_ms); return(ms); } int #ifdef CK_ANSIC tnc_send_purge_data(int mode) #else /* CK_ANSIC */ tnc_send_purge_data(mode) int mode; #endif /* CK_ANSIC */ /* tnc_send_purge_data */ { /* IAC SB COM_PORT PURGE_DATA IAC SE */ /* Value Meaning * 1 Purge access server receive data buffer * 2 Purge access server transmit data buffer * 3 Purge access server receive and transmit data buffers */ /* No response */ int i = 0, rc; debug(F111,"tnc_send_purge_data","begin",mode); if (ttnet != NET_TCPB) return(0); if (ttnproto != NP_TELNET) return(0); if (!TELOPT_ME(TELOPT_COMPORT)) return(0); #ifdef CK_SSL if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) { return(0); } #endif /* CK_SSL */ if ( !(mode >= 1 && mode <= 3) ) return(0); sb_out[i++] = (CHAR) IAC; /* I Am a Command */ sb_out[i++] = (CHAR) SB; /* Subnegotiation */ sb_out[i++] = TELOPT_COMPORT; /* ComPort */ sb_out[i++] = TNC_C2S_PURGE; sb_out[i++] = (unsigned char)(mode & 0xFF); sb_out[i++] = (CHAR) IAC; /* End of Subnegotiation */ sb_out[i++] = (CHAR) SE; /* marked by IAC SE */ #ifdef DEBUG if (deblog || tn_deb || debses) { ckmakxmsg(tn_msg_out,TN_MSG_LEN, "TELNET SENT SB ",TELOPT(TELOPT_COMPORT), " PURGE-DATA ", ckitoa(mode & 0xFF), " IAC SE", NULL, NULL,NULL,NULL,NULL,NULL,NULL); } #endif /* DEBUG */ #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif #ifdef DEBUG debug(F100,tn_msg_out,"",0); if (tn_deb || debses) tn_debug(tn_msg_out); #endif /* DEBUG */ rc = (ttol((CHAR *)sb_out,i) < 0); /* Send it. */ #ifdef OS2 ReleaseTelnetMutex(); #endif if (rc) return(-1); debug(F110,"tnc_send_purge_data","end",0); return(0); } int #ifdef CK_ANSIC tnc_flow_suspended(void) #else /* CK_ANSIC */ tnc_flow_suspended() #endif /* CK_ANSIC */ /* tnc_flow_suspended */ { debug(F111,"tnc_flow_suspended","",tnc_suspend_xmit); return(tnc_suspend_xmit); } int #ifdef CK_ANSIC tnc_suspend_flow(void) #else /* CK_ANSIC */ tnc_suspend_flow() #endif /* CK_ANSIC */ /* tnc_suspend_flow */ { /* IAC SB COM_PORT FLOWCONTROL_SUSPEND IAC SE */ int i = 0, rc; debug(F110,"tnc_suspend_flow","begin",0); if (ttnet != NET_TCPB) return(0); if (ttnproto != NP_TELNET) return(0); if (!TELOPT_ME(TELOPT_COMPORT)) return(0); #ifdef CK_SSL if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) { return(0); } #endif /* CK_SSL */ sb_out[i++] = (CHAR) IAC; /* I Am a Command */ sb_out[i++] = (CHAR) SB; /* Subnegotiation */ sb_out[i++] = TELOPT_COMPORT; /* ComPort */ sb_out[i++] = TNC_C2S_FLOW_SUSPEND; sb_out[i++] = (CHAR) IAC; /* End of Subnegotiation */ sb_out[i++] = (CHAR) SE; /* marked by IAC SE */ #ifdef DEBUG if (deblog || tn_deb || debses) { ckmakmsg(tn_msg_out,TN_MSG_LEN, "TELNET SENT SB ",TELOPT(TELOPT_COMPORT), " FLOWCONTROL-SUSPEND IAC SE", NULL); } #endif /* DEBUG */ #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif #ifdef DEBUG debug(F100,tn_msg_out,"",0); if (tn_deb || debses) tn_debug(tn_msg_out); #endif /* DEBUG */ rc = (ttol((CHAR *)sb_out,i) < 0); /* Send it. */ #ifdef OS2 ReleaseTelnetMutex(); #endif if (rc) return(-1); debug(F110,"tnc_suspend_flow","end",0); return(0); } int #ifdef CK_ANSIC tnc_resume_flow(void) #else /* CK_ANSIC */ tnc_resume_flow() #endif /* CK_ANSIC */ /* tnc_resume_flow */ { /* IAC SB COM_PORT FLOWCONTROL_RESUME IAC SE */ int i = 0, rc; debug(F110,"tnc_resume_flow","begin",0); if (ttnet != NET_TCPB) return(0); if (ttnproto != NP_TELNET) return(0); if (!TELOPT_ME(TELOPT_COMPORT)) return(0); #ifdef CK_SSL if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) { return(0); } #endif /* CK_SSL */ sb_out[i++] = (CHAR) IAC; /* I Am a Command */ sb_out[i++] = (CHAR) SB; /* Subnegotiation */ sb_out[i++] = TELOPT_COMPORT; /* ComPort */ sb_out[i++] = TNC_C2S_FLOW_RESUME; sb_out[i++] = (CHAR) IAC; /* End of Subnegotiation */ sb_out[i++] = (CHAR) SE; /* marked by IAC SE */ #ifdef DEBUG if (deblog || tn_deb || debses) { ckmakmsg(tn_msg_out,TN_MSG_LEN, "TELNET SENT SB ",TELOPT(TELOPT_COMPORT), " FLOWCONTROL-RESUME IAC SE", NULL); } #endif /* DEBUG */ #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif #ifdef DEBUG debug(F100,tn_msg_out,"",0); if (tn_deb || debses) tn_debug(tn_msg_out); #endif /* DEBUG */ rc = (ttol((CHAR *)sb_out,i) < 0); /* Send it. */ #ifdef OS2 ReleaseTelnetMutex(); #endif if (rc) return(-1); debug(F110,"tnc_resume_flow","end",0); return(0); } int #ifdef CK_ANSIC tnsetflow(int nflow) #else tnsetflow(nflow) int nflow; #endif /* CK_ANSIC */ /* tnsetflow */ { int rc = -1; debug(F111,"tnsetflow","begin",nflow); if (ttnet != NET_TCPB || ttnproto != NP_TELNET) return(-1); if (TELOPT_ME(TELOPT_COMPORT)) { switch(nflow) { case FLO_XONX: rc = tnc_set_oflow( TNC_CTL_OFLOW_XON_XOFF ); if (rc >= 0) rc = tnc_set_iflow( TNC_CTL_IFLOW_XON_XOFF ); break; case FLO_RTSC: rc = tnc_set_oflow( TNC_CTL_OFLOW_RTS_CTS ); if (rc >= 0) rc = tnc_set_iflow( TNC_CTL_IFLOW_RTS_CTS ); break; case FLO_KEEP: /* leave things exactly as they are */ rc = 0; break; case FLO_NONE: case FLO_DIAL: /* dialing hack */ case FLO_DIAX: /* cancel dialing hack */ rc = tnc_set_oflow( TNC_CTL_OFLOW_NONE ); if (rc >= 0) rc = tnc_set_iflow( TNC_CTL_IFLOW_NONE ); break; case FLO_DTRC: case FLO_ETXA: case FLO_STRG: case FLO_DTRT: default: /* not supported */ rc = -1; break; } } debug(F111,"tnsetflow","end",rc); return(rc >= 0 ? 0 : -1); } int #ifdef CK_ANSIC tnsettings(int par, int stop) #else tnsettings(par, stop) int par, stop; #endif /* CK_ANSIC */ /* tnsettings */ { int rc = -1; int datasize = 0; extern int hwparity; debug(F111,"tnsettings begin","par",par); debug(F111,"tnsettings begin","stop",stop); if (ttnet != NET_TCPB || ttnproto != NP_TELNET) return(-1); datasize = par ? TNC_DS_7 : TNC_DS_8; if (!par) par = hwparity; if (TELOPT_ME(TELOPT_COMPORT)) { switch (par) { case 'e': rc = tnc_set_parity(TNC_PAR_EVEN); if (rc >= 0) rc = tnc_set_datasize(datasize); break; case 'o': rc = tnc_set_parity(TNC_PAR_ODD); if (rc >= 0) rc = tnc_set_datasize(datasize); break; case 'm': rc = tnc_set_parity(TNC_PAR_MARK); if (rc >= 0) rc = tnc_set_datasize(datasize); break; case 's': rc = tnc_set_parity(TNC_PAR_SPACE); if (rc >= 0) rc = tnc_set_datasize(datasize); break; case 0: case 'n': rc = tnc_set_parity(TNC_PAR_NONE); if (rc >= 0) rc = tnc_set_datasize(datasize); break; default: /* no change */ rc = 0; } switch(stop) { case 2: if (rc >= 0) rc = tnc_set_stopsize(TNC_SB_2); break; case 1: if (rc >= 0) rc = tnc_set_stopsize(TNC_SB_1); break; default: /* no change */ if (rc >= 0) rc = 0; } } debug(F111,"tnsettings","end",rc); return((rc >= 0) ? 0 : -1); } /* T N G M D M -- Telnet Get modem signals */ /* Looks for the modem signals CTS, DSR, and CTS, and returns those that are on in as its return value, in a bit mask as described for ttwmdm. Returns: -3 Not implemented -2 if the line does not have modem control -1 on error. >= 0 on success, with a bit mask containing the modem signals that are on. */ int #ifdef CK_ANSIC tngmdm(void) #else tngmdm() #endif /* CK_ANSIC */ /* tngmdm */ { int rc = -1; debug(F110,"tngmdm","begin",0); if (ttnet != NET_TCPB || ttnproto != NP_TELNET) return(-1); if (TELOPT_ME(TELOPT_COMPORT)) { int modemstate = tnc_get_ms(); int modem = 0; if (modemstate & TNC_MS_CTS_SIG) modem |= BM_CTS; if (modemstate & TNC_MS_DSR_SIG) modem |= BM_DSR; if (modemstate & TNC_MS_RI_SIG) modem |= BM_RNG; if (modemstate & TNC_MS_RLSD_SIG) modem |= BM_DCD; debug(F111,"tngmdm","end",modem); return(modem); } else { debug(F111,"tngmdm","end",-2); return(-2); } } int #ifdef CK_ANSIC tnsndb(long wait) #else tnsndb(wait) long wait; #endif /* CK_ANSIC */ /* tnsndb */ { int rc = -1; debug(F111,"tnsndb","begin",wait); if (ttnet != NET_TCPB || ttnproto != NP_TELNET) return(-1); if (TELOPT_ME(TELOPT_COMPORT)) { rc = tnc_set_break_state(1); if (rc >= 0) { msleep(wait); /* ZZZzzz */ rc = tnc_set_break_state(0); } } debug(F111,"tnsndb","end",rc); return((rc >= 0) ? 0 : -1); } #endif /* TN_COMPORT */ #endif /* TNCODE */ ckctel.h0000644000015300001460000013020511257231507011303 0ustar fdckermit/* ckctel.h -- Symbol and macro definitions for C-Kermit telnet support */ /* Authors: Jeffrey E Altman , Secure Endpoints Inc., New York City Frank da Cruz Columbia University Academic Information Systems, New York City. Copyright (C) 1985, 2009, Trustees of Columbia University in the City of New York. All rights reserved. See the C-Kermit COPYING.TXT file or the copyright text in the ckcmai.c module for disclaimer and permissions. Notes: . This file should be used in place of "arpa/telnet.h" . Only one source file should include #defines for TELCMDS, TELOPTS, TELOPT_STATES, SLC_NAMES, and AUTH_NAMES. */ #ifndef CKCTEL_H #define CKCTEL_H #ifdef TNCODE /* Definitions for the TELNET protocol. can't rely on library header files for any of them. */ #ifdef COMMENT /* In some compilers these are prone to sign extension */ #ifndef IAC /* First the telnet commands */ #define IAC 255 #endif /* IAC */ #ifndef DONT #define DONT 254 #endif /* DONT */ #ifndef DO #define DO 253 #endif /* DO */ #ifndef WONT #define WONT 252 #endif /* WONT */ #ifndef WILL #define WILL 251 #endif /* WILL */ #ifndef SB #define SB 250 #endif /* SB */ #ifndef TN_GA #define TN_GA 249 #endif /* TN_GA */ #ifndef TN_EL #define TN_EL 248 #endif /* TN_EL */ #ifndef TN_EC #define TN_EC 247 #endif /* TN_EC */ #ifndef TN_AYT #define TN_AYT 246 #endif /* TN_AYT */ #ifndef TN_AO #define TN_AO 245 #endif /* TN_AO */ #ifndef TN_IP #define TN_IP 244 #endif /* TN_IP */ #ifndef BREAK #define BREAK 243 #endif /* BREAK */ #ifndef TN_DM #define TN_DM 242 #endif /* TN_DM */ #ifndef TN_NOP #define TN_NOP 241 #endif /* TN_NOP */ #ifndef SE #define SE 240 #endif /* SE */ #ifndef TN_EOR #define TN_EOR 239 #endif /* TN_EOR */ #ifndef TN_ABORT #define TN_ABORT 238 #endif /* TN_ABORT */ #ifndef TN_SUSP #define TN_SUSP 237 #endif /* TN_SUSP */ #ifndef TN_EOF #define TN_EOF 236 #endif /* TN_EOF */ #ifndef LAST_TN_CMD #define LAST_TN_CMD 236 #define TN_SAK 200 /* IBM Secure Attention Key */ #endif /* LAST_TN_CMD */ #define SYNCH 242 /* for telfunc calls */ #else /* Hex notation seems to suppress the sugn extension effect */ #ifndef IAC /* First the telnet commands */ #define IAC 0xff #endif /* IAC */ #ifndef DONT #define DONT 0xfe /* 254 */ #endif /* DONT */ #ifndef DO #define DO 0xfd /* 253 */ #endif /* DO */ #ifndef WONT #define WONT 0xfc /* 252 */ #endif /* WONT */ #ifndef WILL #define WILL 0xfb /* 251 */ #endif /* WILL */ #ifndef SB #define SB 0xfa /* 250 */ #endif /* SB */ #ifndef TN_GA #define TN_GA 0xf9 /* 249 */ #endif /* TN_GA */ #ifndef TN_EL #define TN_EL 0xf8 /* 248 */ #endif /* TN_EL */ #ifndef TN_EC #define TN_EC 0xf7 /* 247 */ #endif /* TN_EC */ #ifndef TN_AYT #define TN_AYT 0xf6 /* 246 */ #endif /* TN_AYT */ #ifndef TN_AO #define TN_AO 0xf5 /* 245 */ #endif /* TN_AO */ #ifndef TN_IP #define TN_IP 0xf4 /* 244 */ #endif /* TN_IP */ #ifndef BREAK #define BREAK 0xf3 /* 243 */ #endif /* BREAK */ #ifndef TN_DM #define TN_DM 0xf2 /* 242 */ #endif /* TN_DM */ #ifndef TN_NOP #define TN_NOP 0xf1 /* 241 */ #endif /* TN_NOP */ #ifndef SE #define SE 0xf0 /* 240 */ #endif /* SE */ #ifndef TN_EOR #define TN_EOR 0xef /* 239 */ #endif /* TN_EOR */ #ifndef TN_ABORT #define TN_ABORT 0xee /* 238 */ #endif /* TN_ABORT */ #ifndef TN_SUSP #define TN_SUSP 0xed /* 237 */ #endif /* TN_SUSP */ #ifndef TN_EOF #define TN_EOF 0xec /* 236 */ #endif /* TN_EOF */ #ifndef LAST_TN_CMD #define LAST_TN_CMD 0xec /* 236 */ #define TN_SAK 0xc8 /* 200 - IBM Secure Attention Key */ #endif /* LAST_TN_CMD */ #define SYNCH 0xf2 /* 242 - for telfunc calls */ #endif /* COMMENT */ #ifdef TELCMDS char *telcmds[] = { "EOF", "SUSP", "ABORT", "EOR", "SE", "NOP", "DMARK", "BRK", "IP", "AO", "AYT", "EC", "EL", "GA", "SB", "WILL", "WONT", "DO", "DONT", "IAC", 0 }; #else /* TELCMDS */ extern char *telcmds[]; #endif /* TELCMDS */ #define TELCMD_FIRST TN_EOF #define TELCMD_LAST IAC #define TELCMD_OK(x) ((unsigned int)(x) <= TELCMD_LAST && \ (unsigned int)(x) >= TELCMD_FIRST || \ (unsigned int)(x) == TN_SAK) #define TELCMD(x) (TELCMD_OK(x)? ((x) == TN_SAK?"SAK": \ telcmds[(x)-TELCMD_FIRST]):"UNKNOWN") /* Then the options */ /* NB: the following platforms have TELOPT_AUTHENTICATION defined as */ /* 45 instead of 37. */ #ifdef TELOPT_AUTHENTICATION #ifdef __osf__ #undef TELOPT_AUTHENTICATION #endif /* __osf__ */ #ifndef IRIX #undef TELOPT_AUTHENTICATION #endif /* IRIX */ #ifndef ultrix #undef TELOPT_AUTHENTICATION #endif /* ultrix */ #endif /* TELOPT_AUTHENTICATION */ /* telnet options */ #ifndef TELOPT_BINARY #define TELOPT_BINARY 0 /* 8-bit data path (RFC 856)*/ #endif #ifndef TELOPT_ECHO #define TELOPT_ECHO 1 /* echo (RFC 857)*/ #endif #ifndef TELOPT_RCP #define TELOPT_RCP 2 /* prepare to reconnect (NIC 50005)*/ #endif #ifndef TELOPT_SGA #define TELOPT_SGA 3 /* suppress go ahead (RFC 858) */ #endif #ifndef TELOPT_NAMS #define TELOPT_NAMS 4 /* approximate message size (ETHERNET) */ #endif #ifndef TELOPT_STATUS #define TELOPT_STATUS 5 /* give status (RFC 859) */ #endif #ifndef TELOPT_TM #define TELOPT_TM 6 /* timing mark (RFC 860) */ #endif #ifndef TELOPT_RCTE #define TELOPT_RCTE 7 /* remote controlled transmission and echo */ #endif /* (RFC 726) */ #ifndef TELOPT_NAOL #define TELOPT_NAOL 8 /* negotiate about output line width */ #endif /* (NIC 50005) */ #ifndef TELOPT_NAOP #define TELOPT_NAOP 9 /* negotiate about output page size */ #endif /* (NIC 50005) */ #ifndef TELOPT_NAOCRD #define TELOPT_NAOCRD 10 /* negotiate about CR disposition (RFC 652) */ #endif /* [Historic] */ #ifndef TELOPT_NAOHTS #define TELOPT_NAOHTS 11 /* negotiate about horizontal tabstops */ #endif /* (RFC 653) [Historic] */ #ifndef TELOPT_NAOHTD #define TELOPT_NAOHTD 12 /* negotiate about horiz tab disposition */ #endif /* (RFC 654) [Historic] */ #ifndef TELOPT_NAOFFD #define TELOPT_NAOFFD 13 /* negotiate about formfeed disposition */ #endif /* (RFC 655) [Historic] */ #ifndef TELOPT_NAOVTS #define TELOPT_NAOVTS 14 /* negotiate about vertical tab stops */ #endif /* (RFC 656) [Historic] */ #ifndef TELOPT_NAOVTD #define TELOPT_NAOVTD 15 /* negotiate about vertical tab disposition */ #endif /* (RFC 657) [Historic] */ #ifndef TELOPT_NAOLFD #define TELOPT_NAOLFD 16 /* negotiate about output LF disposition */ #endif /* (RFC 658) [Historic] */ #ifndef TELOPT_XASCII #define TELOPT_XASCII 17 /* extended ascii character set */ #endif /* (RFC 698) */ #ifndef TELOPT_LOGOUT #define TELOPT_LOGOUT 18 /* force logout (RFC 727) */ #endif #ifndef TELOPT_BM #define TELOPT_BM 19 /* byte macro (RFC 735) */ #endif #ifndef TELOPT_DET #define TELOPT_DET 20 /* data entry terminal (RFC 1043, 732) */ #endif #ifndef TELOPT_SUPDUP #define TELOPT_SUPDUP 21 /* supdup protocol (RFC 736, 734) */ #endif #ifndef TELOPT_SUPDUPOUTPUT #define TELOPT_SUPDUPOUTPUT 22 /* supdup output (RFC 749) */ #endif #ifndef TELOPT_SNDLOC #define TELOPT_SNDLOC 23 /* send location (RFC 779) */ #endif #ifndef TELOPT_TTYPE #define TELOPT_TTYPE 24 /* terminal type (RFC 1091) */ #endif #ifndef TELOPT_EOR #define TELOPT_EOR 25 /* end of record (RFC 885) */ #endif #ifndef TELOPT_TUID #define TELOPT_TUID 26 /* TACACS user identification (RFC 927) */ #endif #ifndef TELOPT_OUTMRK #define TELOPT_OUTMRK 27 /* output marking (RFC 933) */ #endif #ifndef TELOPT_TTYLOC #define TELOPT_TTYLOC 28 /* terminal location number (RFC 946) */ #endif #ifndef TELOPT_3270REGIME #define TELOPT_3270REGIME 29 /* 3270 regime (RFC 1041) */ #endif #ifndef TELOPT_X3PAD #define TELOPT_X3PAD 30 /* X.3 PAD (RFC 1053) */ #endif #ifndef TELOPT_NAWS #define TELOPT_NAWS 31 /* window size (RFC 1073) */ #endif #ifndef TELOPT_TSPEED #define TELOPT_TSPEED 32 /* terminal speed (RFC 1079) */ #endif #ifndef TELOPT_LFLOW #define TELOPT_LFLOW 33 /* remote flow control (RFC 1372) */ #endif #ifndef TELOPT_LINEMODE #define TELOPT_LINEMODE 34 /* Linemode option (RFC 1184) */ #endif #ifndef TELOPT_XDISPLOC #define TELOPT_XDISPLOC 35 /* X Display Location (RFC 1096) */ #endif #ifndef TELOPT_OLD_ENVIRON #define TELOPT_OLD_ENVIRON 36 /* Old - Environment variables (RFC 1408) */ #endif #ifndef TELOPT_AUTHENTICATION #define TELOPT_AUTHENTICATION 37/* Authenticate (RFC 2941) */ #endif #ifndef TELOPT_ENCRYPTION #define TELOPT_ENCRYPTION 38 /* Encryption option (RFC 2946) */ #endif #ifndef TELOPT_NEWENVIRON #define TELOPT_NEWENVIRON 39 /* New - Environment variables (RFC 1572) */ #endif #ifndef TELOPT_3270E #define TELOPT_3270E 40 /* 3270 Extended (RFC 1647) */ #endif #ifndef TELOPT_XAUTH #define TELOPT_XAUTH 41 /* ??? (Earhart) */ #endif #ifndef TELOPT_CHARSET #define TELOPT_CHARSET 42 /* Character-set (RFC 2066) */ #endif #ifndef TELOPT_RSP #define TELOPT_RSP 43 /* Remote Serial Port (Barnes) */ #endif #ifndef TELOPT_COMPORT #define TELOPT_COMPORT 44 /* Com Port Control (RFC 2217) */ #endif #ifndef TELOPT_SLE #define TELOPT_SLE 45 /* Suppress Local Echo (Atmar) - [rejected] */ #endif #ifndef TELOPT_START_TLS #define TELOPT_START_TLS 46 /* Telnet over Transport Layer Security */ #endif /* (Boe) */ #ifndef TELOPT_KERMIT #define TELOPT_KERMIT 47 /* Kermit (RFC 2840) */ #endif #ifndef TELOPT_SEND_URL #define TELOPT_SEND_URL 48 /* Send URL */ #endif #ifndef TELOPT_FORWARD_X #define TELOPT_FORWARD_X 49 /* X Windows Forwarding (Altman) */ #endif /* TELOPT_FORWARD_X */ #ifndef TELOPT_STDERR #define TELOPT_STDERR 50 /* Redirected Stderr (Altman) */ #endif /* TELOPT_STDERR */ #ifndef TELOPT_PRAGMA_LOGON #define TELOPT_PRAGMA_LOGON 138 /* Encrypted Logon option (PragmaSys) */ #endif #ifndef TELOPT_SSPI_LOGON #define TELOPT_SSPI_LOGON 139 /* MS SSPI Logon option (PragmaSys) */ #endif #ifndef TELOPT_PRAGMA_HEARTBEAT /* Server Send Heartbeat option (PragmaSys) */ #define TELOPT_PRAGMA_HEARTBEAT 140 #endif #define TELOPT_IBM_SAK 200 /* IBM Secure Attention Key */ /* IBM Secure Attention Key (SAK) Option In addition to terminal negotiation, the telnet command allows negotiation for the Secure Attention Key (SAK) option. This option, when supported, provides the local user with a secure communication path to the remote host for tasks such as changing user IDs or passwords. If the remote host supports the SAK function, a trusted shell is opened on the remote host when the telnet send sak subcommand is issued. The SAK function can also be assigned to a single key available in telnet input mode, using the set sak subcommand. TN_SAK Sends the TELNET SAK (Secure Attention Key) sequence, which causes the remote system to invoke the trusted shell. If the SAK is not supported, then an error message is displayed that reads: Remote side does not support SAK. */ #ifndef TELOPT_EXOPL #define TELOPT_EXOPL 255 /* Extended-options-list (RFC 861) */ #endif #ifdef NTELOPTS #undef NTELOPTS #endif /* NTELOPTS */ /* The Telnet Option space is no longer being allocated by ICANN as a */ /* continuous list. In other words it is becoming sparse. But we do */ /* not want to have to allocate memory for a long list of strings and */ /* structs which will never be used. Therefore, the NTELOPTS define */ /* can no longer be equal to TELOPT_LAST+1. In fact, the notion of */ /* TELOPT_FIRST and TELOPT_LAST no longer make sense. */ #define NTELOPTS 55 #define TELOPT_FIRST TELOPT_BINARY #define TELOPT_LAST TELOPT_IBM_SAK /* The following macros speed us up at runtime but are too complex for some preprocessors / compilers; if your compiler bombs on ckctel.c with "Instruction table overflow" or somesuch, rebuild with -DNOTOMACROS. */ #ifndef NOTOMACROS #ifndef TELOPT_MACRO #define TELOPT_MACRO #endif /* TELOPT_MACRO */ #endif /* NOTOMACROS */ #ifdef TELOPT_MACRO #define TELOPT_INDEX(x) (((x)>=0 && (x)<= TELOPT_STDERR)?(x):\ ((x)>=TELOPT_PRAGMA_LOGON && (x)<=TELOPT_PRAGMA_HEARTBEAT)?(x)-87: \ ((x) == TELOPT_IBM_SAK)?(x)-146: NTELOPTS) #define TELOPT_OK(x) (((x) >= TELOPT_BINARY && (x) <= TELOPT_STDERR) ||\ ((x) >= TELOPT_PRAGMA_LOGON && (x) <= TELOPT_PRAGMA_HEARTBEAT) ||\ ((x) == TELOPT_IBM_SAK)) #define TELOPT(x) (TELOPT_OK(x)?telopts[TELOPT_INDEX(x)]:"UNKNOWN") #else /* TELOPT_MACRO */ _PROTOTYP(int telopt_index,(int)); _PROTOTYP(int telopt_ok,(int)); _PROTOTYP(CHAR * telopt, (int)); #define TELOPT_INDEX(x) telopt_index(x) #define TELOPT_OK(x) telopt_ok(x) #define TELOPT(x) telopt(x) #endif /* TELOPT_MACRO */ #ifdef TELOPTS char *telopts[NTELOPTS+2] = { /* 0 */ "BINARY", "ECHO", "RCP", "SUPPRESS-GO-AHEAD", /* 4 */ "NAME", "STATUS", "TIMING-MARK", "RCTE", /* 8 */ "NAOL", "NAOP", "NAOCRD", "NAOHTS", /* 12 */ "NAOHTD", "NAOFFD", "NAOVTS", "NAOVTD", /* 16 */ "NAOLFD", "EXTEND-ASCII", "LOGOUT", "BYTE-MACRO", /* 20 */ "DATA-ENTRY-TERMINAL", "SUPDUP", "SUPDUP-OUTPUT", "SEND-LOCATION", /* 24 */ "TERMINAL-TYPE", "END-OF-RECORD", "TACACS-UID", "OUTPUT-MARKING", /* 28 */ "TTYLOC", "3270-REGIME", "X.3-PAD", "NAWS", /* 32 */ "TSPEED", "LFLOW", "LINEMODE", "XDISPLOC", /* 36 */ "OLD-ENVIRON", "AUTHENTICATION", "ENCRYPTION", "NEW-ENVIRONMENT", /* 40 */ "TN3270E","xauth","CHARSET", "remote-serial-port", /* 44 */ "COM-PORT-CONTROL","suppress-local-echo","START-TLS","KERMIT", /* 48 */ "send-url","FORWARD-X","stderr", /* 138 */ "pragma-logon", "sspi-logon", "pragma-heartbeat", /* 200 */ "ibm-sak", "unknown", 0 }; #else /*TELOPTS */ extern char * telopts[]; #endif /* TELOPTS */ /* TELNET Newline Mode */ #define TNL_CR 0 /* CR sends bare carriage return */ #define TNL_CRNUL 1 /* CR and NUL */ #define TNL_CRLF 2 /* CR and LF */ #define TNL_LF 3 /* LF instead of CR */ /* TELNET Negotiation Mode */ #define TN_NG_RF 0 /* Negotiation REFUSED */ #define TN_NG_AC 1 /* Negotiation ACCEPTED */ #define TN_NG_RQ 2 /* Negotiation REQUESTED */ #define TN_NG_MU 3 /* Negotiation REQUIRED (must) */ /* Systems where we know we can define TELNET NAWS automatically. */ #ifndef CK_NAWS /* In other words, if both */ #ifdef CK_TTGWSIZ /* TNCODE and TTGWSIZ are defined */ #define CK_NAWS /* then we can do NAWS. */ #endif /* CK_TTGWSIZ */ #endif /* CK_NAWS */ #ifdef CK_FORWARD_X #ifndef MAXFWDX #define MAXFWDX 64 /* Num of X windows to be fwd'd */ #endif /* MAXFWDX */ #endif /* CK_FORWARD_X */ /* Telnet State structures and definitions */ struct _telopt_state { unsigned char def_server_me_mode; /* Default Negotiation Mode */ unsigned char def_server_u_mode; /* Default Negotiation Mode */ unsigned char def_client_me_mode; /* Default Negotiation Mode */ unsigned char def_client_u_mode; /* Default Negotiation Mode */ unsigned char me_mode; /* Telnet Negotiation Mode */ unsigned char u_mode; /* Telnet Negotiation Mode */ unsigned char me; /* Am I ? */ unsigned char u; /* Are you? */ unsigned char unanswered_will; /* Sent Will, Waiting for DO/DONT */ unsigned char unanswered_do; /* Send DO, Waiting for WILL/WONT */ unsigned char unanswered_wont; /* Sent WONT, Waiting for DONT */ unsigned char unanswered_dont; /* Sent DONT, Waiting for WONT */ unsigned char unanswered_sb; /* Sent SB, Waiting for SB (server) */ union { #ifdef IKS_OPTION struct _telopt_kermit { /* Kermit Option States */ unsigned char me_start; /* I have a Server active */ unsigned char me_req_start; /* Sent Req-Start, Waiting for response */ unsigned char me_req_stop; /* Sent Req-Stop, Waiting for response */ unsigned char u_start; /* You have a Server active */ unsigned char sop; /* Have we received the SOP char? */ } kermit; #endif /* IKS_OPTION */ #ifdef CK_ENCRYPTION struct _telopt_encrypt { /* Encryption Option States */ unsigned char need_to_send; unsigned char stop; /* Is encryption stopped? */ } encrypt; #endif /* CK_ENCRYPTION */ #ifdef CK_NAWS struct _telopt_naws { /* NAWS Option Information */ unsigned char need_to_send; int x; /* Last Width */ int y; /* Last Height */ } naws; #endif /* CK_NAWS */ #ifdef CK_SSL struct _telopt_start_tls { /* Start TLS Option */ unsigned char u_follows; /* u ready for TLS negotiation */ unsigned char me_follows; /* me ready for TLS negotiation */ unsigned char auth_request; /* Rcvd WILL AUTH before WONT START_TLS */ } start_tls; #endif /* CK_SSL */ struct _telopt_term { /* Terminal Type */ unsigned char need_to_send; unsigned char type[41]; /* Last terminal type */ } term; #ifdef CK_ENVIRONMENT struct _telopt_new_env { unsigned char need_to_send; unsigned char * str; int len; } env; #ifdef CK_XDISPLOC struct _telopt_xdisp { unsigned char need_to_send; } xdisp; #endif /* CK_XDISPLOC */ #endif /* CK_ENVIRONMENT */ #ifdef CK_SNDLOC struct _telopt_sndloc { unsigned char need_to_send; } sndloc; #endif /* CK_SNDLOC */ #ifdef CK_FORWARD_X struct _telopt_fwd_x { unsigned char need_to_send; int listen_socket; struct _channel { int fd; int id; unsigned char need_to_send_xauth; unsigned char suspend; } channel[MAXFWDX]; #ifdef NT int thread_started; #endif /* NT */ } forward_x; #endif /* CK_FORWARD_X */ #ifdef TN_COMPORT struct _telopt_comport { unsigned char need_to_send; unsigned char wait_for_sb; unsigned char wait_for_ms; } comport; #endif /* TN_COMPORT */ /* additional options such as New Environment or Send Location */ } sb; }; typedef struct _telopt_state telopt_state, *p_telopt_state; /* telopt_states[] is the array of structs which the state of each telnet */ /* option is stored. We allocate one more than we need in case we are */ /* sent telnet options that we do not recognize. If by some chance the */ /* TELOPT_OK() check is skipped, TELOPT_INDEX() will force the option to */ /* use the extra cell. */ #ifdef TELOPT_STATES telopt_state telopt_states[NTELOPTS+1]; #else /* TELOPT_STATES */ extern telopt_state telopt_states[]; #endif /* TELOPT_STATES */ #define TELOPT_ME(x) (telopt_states[TELOPT_INDEX(x)].me) #define TELOPT_U(x) (telopt_states[TELOPT_INDEX(x)].u) #define TELOPT_ME_MODE(x) \ (telopt_states[TELOPT_INDEX(x)].me_mode) #define TELOPT_U_MODE(x) \ (telopt_states[TELOPT_INDEX(x)].u_mode) #define TELOPT_UNANSWERED_WILL(x) \ (telopt_states[TELOPT_INDEX(x)].unanswered_will) #define TELOPT_UNANSWERED_DO(x) \ (telopt_states[TELOPT_INDEX(x)].unanswered_do) #define TELOPT_UNANSWERED_WONT(x) \ (telopt_states[TELOPT_INDEX(x)].unanswered_wont) #define TELOPT_UNANSWERED_DONT(x) \ (telopt_states[TELOPT_INDEX(x)].unanswered_dont) #define TELOPT_UNANSWERED_SB(x) \ (telopt_states[TELOPT_INDEX(x)].unanswered_sb) #define TELOPT_SB(x) \ (telopt_states[TELOPT_INDEX(x)].sb) #define TELOPT_DEF_S_ME_MODE(x) \ (telopt_states[TELOPT_INDEX(x)].def_server_me_mode) #define TELOPT_DEF_S_U_MODE(x) \ (telopt_states[TELOPT_INDEX(x)].def_server_u_mode) #define TELOPT_DEF_C_ME_MODE(x) \ (telopt_states[TELOPT_INDEX(x)].def_client_me_mode) #define TELOPT_DEF_C_U_MODE(x) \ (telopt_states[TELOPT_INDEX(x)].def_client_u_mode) #ifdef TELOPT_MODES char * telopt_modes[4] = { "REFUSED", "ACCEPTED", "REQUESTED", "REQUIRED" }; #else /* TELOPT_MODES */ extern char * telopt_modes[]; #endif /* TELOPT_MODES */ #ifdef TELOPT_MACRO #define TELOPT_MODE_OK(x) ((unsigned int)(x) <= TN_NG_MU) #define TELOPT_MODE(x) (TELOPT_MODE_OK(x)?telopt_modes[(x)-TN_NG_RF]:"UNKNOWN") #else /* TELOPT_MACRO */ _PROTOTYP(int telopt_mode_ok,(int)); _PROTOTYP(CHAR * telopt_mode,(int)); #define TELOPT_MODE_OK(x) telopt_mode_ok(x) #define TELOPT_MODE(x) telopt_mode(x) #endif /* TELOPT_MACRO */ /* Sub-option qualifiers */ #define TELQUAL_IS 0 /* option is... */ #define TELQUAL_SEND 1 /* send option */ #define TELQUAL_INFO 2 /* ENVIRON: informational version of IS */ #define TELQUAL_REPLY 2 /* AUTHENTICATION: client version of IS */ #define TELQUAL_NAME 3 /* AUTHENTICATION: client version of IS */ #define TEL_ENV_VAR 0 #define TEL_ENV_VALUE 1 #define TEL_ENV_ESC 2 #define TEL_ENV_USERVAR 3 #define LFLOW_OFF 0 /* Disable remote flow control */ #define LFLOW_ON 1 /* Enable remote flow control */ #define LFLOW_RESTART_ANY 2 /* Restart output on any char */ #define LFLOW_RESTART_XON 3 /* Restart output only on XON */ /* * LINEMODE suboptions */ #define LM_MODE 1 #define LM_FORWARDMASK 2 #define LM_SLC 3 #define MODE_EDIT 0x01 #define MODE_TRAPSIG 0x02 #define MODE_ACK 0x04 #define MODE_SOFT_TAB 0x08 #define MODE_LIT_ECHO 0x10 #define MODE_MASK 0x1f /* Not part of protocol, but needed to simplify things... */ #define MODE_FLOW 0x0100 #define MODE_ECHO 0x0200 #define MODE_INBIN 0x0400 #define MODE_OUTBIN 0x0800 #define MODE_FORCE 0x1000 #define SLC_SYNCH 1 #define SLC_BRK 2 #define SLC_IP 3 #define SLC_AO 4 #define SLC_AYT 5 #define SLC_EOR 6 #define SLC_ABORT 7 #define SLC_EOF 8 #define SLC_SUSP 9 #define SLC_EC 10 #define SLC_EL 11 #define SLC_EW 12 #define SLC_RP 13 #define SLC_LNEXT 14 #define SLC_XON 15 #define SLC_XOFF 16 #define SLC_FORW1 17 #define SLC_FORW2 18 #define SLC_MCL 19 #define SLC_MCR 20 #define SLC_MCWL 21 #define SLC_MCWR 22 #define SLC_MCBOL 23 #define SLC_MCEOL 24 #define SLC_INSRT 25 #define SLC_OVER 26 #define SLC_ECR 27 #define SLC_EWR 28 #define SLC_EBOL 29 #define SLC_EEOL 30 #define NSLC 30 /* * For backwards compatability, we define SLC_NAMES to be the * list of names if SLC_NAMES is not defined. */ #define SLC_NAMELIST "0", "SYNCH", "BRK", "IP", "AO", "AYT", "EOR", \ "ABORT", "EOF", "SUSP", "EC", "EL", "EW", "RP", \ "LNEXT", "XON", "XOFF", "FORW1", "FORW2", \ "MCL", "MCR", "MCWL", "MCWR", "MCBOL", "MCEOL", \ "INSRT", "OVER", "ECR", "EWR", "EBOL", "EEOL", 0 #ifdef SLC_NAMES char *slc_names[] = { SLC_NAMELIST }; #else extern char *slc_names[]; #define SLC_NAMES SLC_NAMELIST #endif #define SLC_NAME_OK(x) ((unsigned int)(x) <= NSLC) #define SLC_NAME(x) (SLC_NAME_OK(x)?slc_names[x]:"UNKNOWN") #define SLC_NOSUPPORT 0 #define SLC_CANTCHANGE 1 #define SLC_VARIABLE 2 #define SLC_DEFAULT 3 #define SLC_LEVELBITS 0x03 #define SLC_FUNC 0 #define SLC_FLAGS 1 #define SLC_VALUE 2 #define SLC_ACK 0x80 #define SLC_FLUSHIN 0x40 #define SLC_FLUSHOUT 0x20 #define OLD_ENV_VAR 1 #define OLD_ENV_VALUE 0 #define NEW_ENV_VAR 0 #define NEW_ENV_VALUE 1 #define ENV_ESC 2 #define ENV_USERVAR 3 #define FWDX_SCREEN 0 #define FWDX_OPEN 1 #define FWDX_CLOSE 2 #define FWDX_DATA 3 #define FWDX_OPTIONS 4 #define FWDX_OPT_DATA 5 #define FWDX_XOFF 6 #define FWDX_XON 7 #define FWDX_OPT_NONE 0 #define FWDX_OPT_XAUTH 1 /* * AUTHENTICATION suboptions */ /* * Who is authenticating who ... */ #define AUTH_CLIENT_TO_SERVER 0 /* Client authenticating server */ #define AUTH_SERVER_TO_CLIENT 1 /* Server authenticating client */ #define AUTH_WHO_MASK 1 /* * amount of authentication done */ #define AUTH_HOW_ONE_WAY 0 #define AUTH_HOW_MUTUAL 2 #define AUTH_HOW_MASK 2 /* * should we be encrypting? */ #define AUTH_ENCRYPT_OFF 0 #define AUTH_ENCRYPT_USING_TELOPT 4 #define AUTH_ENCRYPT_AFTER_EXCHANGE 16 #define AUTH_ENCRYPT_START_TLS 20 #define AUTH_ENCRYPT_MASK 20 /* * will we be forwarding? * if we want to activate the use of this flag then * #define USE_INI_CRED_FWD */ #define INI_CRED_FWD_OFF 0 #define INI_CRED_FWD_ON 8 #define INI_CRED_FWD_MASK 8 #define USE_INI_CRED_FWD #define AUTHTYPE_NULL 0 #define AUTHTYPE_KERBEROS_V4 1 #define AUTHTYPE_KERBEROS_V5 2 #define AUTHTYPE_SPX 3 #define AUTHTYPE_MINK 4 #define AUTHTYPE_SRP 5 #define AUTHTYPE_RSA 6 #define AUTHTYPE_SSL 7 #define AUTHTYPE_LOKI 10 #define AUTHTYPE_SSA 11 #define AUTHTYPE_KEA_SJ 12 #define AUTHTYPE_KEA_INTEG 13 #define AUTHTYPE_DSS 14 #define AUTHTYPE_NTLM 15 #define AUTHTYPE_GSSAPI_KRB5 16 /* Not allocated by IANA */ #ifdef AUTHTYPE_CNT #undef AUTHTYPE_CNT #endif /* AUTHTYPE_CNT */ #define AUTHTYPE_CNT 17 /* * AUTHTYPEs Last updated 21 March 1999 * from http://www.isi.edu/in-notes/iana/assignments/telnet-options */ #define AUTHTYPE_AUTO 99 #ifdef AUTH_NAMES char *authtype_names[] = { "NULL", /* RFC 2941 */ "KERBEROS_V4", /* RFC 2941 / 1411 */ "KERBEROS_V5", /* RFC 2941 */ "SPX", /* RFC 2941 (not Internet Standard) */ "MINK", /* RFC 2941 (not Internet Standard) */ "SRP", /* RFC 2944 */ "RSA", /* RFC 2941 (not Internet Standard) */ "SSL", /* RFC 2941 (not Internet Standard) */ "IANA_8", /* not assigned by IANA */ "IANA_9", /* not assigned by IANA */ "LOKI", /* RFC 2941 (not Internet Standard) */ "SSA", /* Schoch */ "KEA_SJ", /* RFC 2951 */ "KEA_SJ_INTEG", /* RFC 2951 */ "DSS", /* RFC 2943 */ "NTLM", /* Kahn */ "GSSAPI-KRB5", /* experimental - altman */ 0 }; char * authmode_names[] = { "CLIENT_TO_SERVER|ONE_WAY", "SERVER_TO_CLIENT|ONE_WAY", "CLIENT_TO_SERVER|MUTUAL", "SERVER_TO_CLIENT|MUTUAL", "CLIENT_TO_SERVER|ONE_WAY|ENCRYPT_USING_TELOPT", "SERVER_TO_CLIENT|ONE_WAY|ENCRYPT_USING_TELOPT", "CLIENT_TO_SERVER|MUTUAL|ENCRYPT_USING_TELOPT", "SERVER_TO_CLIENT|MUTUAL|ENCRYPT_USING_TELOPT", "CLIENT_TO_SERVER|ONE_WAY|CRED_FWD", "SERVER_TO_CLIENT|ONE_WAY|CRED_FWD", "CLIENT_TO_SERVER|MUTUAL|CRED_FWD", "SERVER_TO_CLIENT|MUTUAL|CRED_FWD", "CLIENT_TO_SERVER|ONE_WAY|ENCRYPT_USING_TELOPT|CRED_FWD", "SERVER_TO_CLIENT|ONE_WAY|ENCRYPT_USING_TELOPT|CRED_FWD", "CLIENT_TO_SERVER|MUTUAL|ENCRYPT_USING_TELOPT|CRED_FWD", "SERVER_TO_CLIENT|MUTUAL|ENCRYPT_USING_TELOPT|CRED_FWD", "CLIENT_TO_SERVER|ONE_WAY|ENCRYPT_AFTER_EXCHANGE", "SERVER_TO_CLIENT|ONE_WAY|ENCRYPT_AFTER_EXCHANGE", "CLIENT_TO_SERVER|MUTUAL|ENCRYPT_AFTER_EXCHANGE", "SERVER_TO_CLIENT|MUTUAL|ENCRYPT_AFTER_EXCHANGE", "CLIENT_TO_SERVER|ONE_WAY|ENCRYPT_START_TLS", "SERVER_TO_CLIENT|ONE_WAY|ENCRYPT_START_TLS", "CLIENT_TO_SERVER|MUTUAL|ENCRYPT_START_TLS", "SERVER_TO_CLIENT|MUTUAL|ENCRYPT_START_TLS", "CLIENT_TO_SERVER|ONE_WAY|ENCRYPT_AFTER_EXCHANGE|CRED_FWD", "SERVER_TO_CLIENT|ONE_WAY|ENCRYPT_AFTER_EXCHANGE|CRED_FWD", "CLIENT_TO_SERVER|MUTUAL|ENCRYPT_AFTER_EXCHANGE|CRED_FWD", "SERVER_TO_CLIENT|MUTUAL|ENCRYPT_AFTER_EXCHANGE|CRED_FWD", "CLIENT_TO_SERVER|ONE_WAY|ENCRYPT_START_TLS|CRED_FWD", "SERVER_TO_CLIENT|ONE_WAY|ENCRYPT_START_TLS|CRED_FWD", "CLIENT_TO_SERVER|MUTUAL|ENCRYPT_START_TLS|CRED_FWD", "SERVER_TO_CLIENT|MUTUAL|ENCRYPT_START_TLS|CRED_FWD", 0 }; #else extern char *authtype_names[]; extern char *authmode_names[]; #endif #define AUTHMODE_CNT 32 #define AUTHTYPE_NAME_OK(x) ((unsigned int)(x) < AUTHTYPE_CNT) #define AUTHTYPE_NAME(x) (AUTHTYPE_NAME_OK(x)?authtype_names[x]:"UNKNOWN") #define AUTHMODE_NAME_OK(x) ((unsigned int)(x) < AUTHMODE_CNT) #define AUTHMODE_NAME(x) (AUTHMODE_NAME_OK(x)?authmode_names[x]:"UNKNOWN") /* Kerberos Authentication Message Identifiers */ #define KRB_AUTH 0 /* Authentication data follows */ #define KRB_REJECT 1 /* Rejected (reason might follow) */ #define KRB_ACCEPT 2 /* Accepted */ #define KRB4_CHALLENGE 3 #define KRB4_RESPONSE 4 #define KRB5_RESPONSE 3 /* Response for mutual auth. */ #define KRB5_FORWARD 4 /* Forwarded credentials follow */ #define KRB5_FORWARD_ACCEPT 5 /* Forwarded credentials accepted */ #define KRB5_FORWARD_REJECT 6 /* Forwarded credentials rejected */ #define KRB5_TLS_VERIFY 7 /* TLS Finished Msg verifier */ /* GSSAPI-KRB5 Authentication Message Identifiers */ #define GSS_AUTH_DATA 0 /* Authentication data follows */ #define GSS_REJECT 1 /* Rejected (reason might follow) */ #define GSS_ACCEPT 2 /* Accepted (username might follow) */ #define GSS_CONTINUE 3 /* Secure Remote Password Authentication Message Identifiers */ #define SRP_AUTH 0 /* Authentication data follows */ #define SRP_REJECT 1 /* Rejected (reason might follow) */ #define SRP_ACCEPT 2 /* Accepted */ #define SRP_CHALLENGE 3 #define SRP_RESPONSE 4 #define SRP_EXP 8 /* */ #define SRP_PARAMS 9 /* */ /* Telnet Auth using KEA and SKIPJACK */ #define KEA_CERTA_RA 1 #define KEA_CERTB_RB_IVB_NONCEB 2 #define KEA_IVA_RESPONSEB_NONCEA 3 #define KEA_RESPONSEA 4 /* Tim Hudson's SSL Authentication Message Identifiers */ #define SSL_START 1 #define SSL_ACCEPT 2 #define SSL_REJECT 3 /* Microsoft NTLM Authentication Message Identifiers */ #define NTLM_AUTH 0 #define NTLM_CHALLENGE 1 #define NTLM_RESPONSE 2 #define NTLM_ACCEPT 3 #define NTLM_REJECT 4 /* Generic Constants */ #define AUTH_SUCCESS 0 #define AUTH_FAILURE 255 /* * ENCRYPTion suboptions */ #define ENCRYPT_IS 0 /* I pick encryption type ... */ #define ENCRYPT_SUPPORT 1 /* I support encryption types ... */ #define ENCRYPT_REPLY 2 /* Initial setup response */ #define ENCRYPT_START 3 /* Am starting to send encrypted */ #define ENCRYPT_END 4 /* Am ending encrypted */ #define ENCRYPT_REQSTART 5 /* Request you start encrypting */ #define ENCRYPT_REQEND 6 /* Request you send encrypting */ #define ENCRYPT_ENC_KEYID 7 #define ENCRYPT_DEC_KEYID 8 #define ENCRYPT_CNT 9 #define ENCTYPE_ANY 0 #define ENCTYPE_DES_CFB64 1 #define ENCTYPE_DES_OFB64 2 #define ENCTYPE_DES3_CFB64 3 #define ENCTYPE_DES3_OFB64 4 #define ENCTYPE_CAST5_40_CFB64 8 #define ENCTYPE_CAST5_40_OFB64 9 #define ENCTYPE_CAST128_CFB64 10 #define ENCTYPE_CAST128_OFB64 11 #ifdef ENCTYPE_CNT #undef ENCTYPE_CNT #endif #define ENCTYPE_CNT 12 #ifdef ENCRYPT_NAMES char *encrypt_names[] = { "IS", "SUPPORT", "REPLY", "START", "END", "REQUEST-START", "REQUEST-END", "ENC-KEYID", "DEC-KEYID", 0 }; char *enctype_names[] = { "ANY", "DES_CFB64", /* RFC 2952 */ "DES_OFB64", /* RFC 2953 */ "DES3_CFB64", /* RFC 2947 */ "DES3_OFB64", /* RFC 2948 */ "UNKNOWN-5", "UNKNOWN-6", "UNKNOWN-7", "CAST5_40_CFB64", /* RFC 2950 */ "CAST5_40_OFB64", /* RFC 2949 */ "CAST128_CFB64", /* RFC 2950*/ "CAST128_OFB64", /* RFC 2949 */ 0 }; #else extern char *encrypt_names[]; extern char *enctype_names[]; #endif #define ENCRYPT_NAME_OK(x) ((unsigned int)(x) < ENCRYPT_CNT) #define ENCRYPT_NAME(x) (ENCRYPT_NAME_OK(x)?encrypt_names[x]:"UNKNOWN") #define ENCTYPE_NAME_OK(x) ((unsigned int)(x) < ENCTYPE_CNT) #define ENCTYPE_NAME(x) (ENCTYPE_NAME_OK(x)?enctype_names[x]:"UNKNOWN") /* For setting the state of validUser */ #define AUTH_REJECT 0 /* Rejected */ #define AUTH_UNKNOWN 1 /* We don't know who he is, but he's okay */ #define AUTH_OTHER 2 /* We know him, but not his name */ #define AUTH_USER 3 /* We know he name */ #define AUTH_VALID 4 /* We know him, and he needs no password */ /* Kermit Option Subnegotiations */ #define KERMIT_START 0 #define KERMIT_STOP 1 #define KERMIT_REQ_START 2 #define KERMIT_REQ_STOP 3 #define KERMIT_SOP 4 #define KERMIT_RESP_START 8 #define KERMIT_RESP_STOP 9 /* For SET TELNET AUTH HOW */ #define TN_AUTH_HOW_ANY 0 #define TN_AUTH_HOW_ONE_WAY 1 #define TN_AUTH_HOW_MUTUAL 2 /* For SET TELNET AUTH ENCRYPT */ #define TN_AUTH_ENC_ANY 0 #define TN_AUTH_ENC_NONE 1 #define TN_AUTH_ENC_TELOPT 2 #define TN_AUTH_ENC_EXCH 3 /* not used in Kermit */ #define TN_AUTH_ENC_TLS 4 /* Telnet protocol functions defined in C-Kermit */ _PROTOTYP( int tn_ini, (void) ); /* Telnet protocol support */ _PROTOTYP( int tn_reset, (void)); _PROTOTYP( int tn_set_modes, (void)); _PROTOTYP( int tn_sopt, (int, int) ); _PROTOTYP( int tn_doop, (CHAR, int, int (*)(int) ) ); _PROTOTYP( int tn_sttyp, (void) ); _PROTOTYP( int tn_snenv, (CHAR *, int) ) ; _PROTOTYP( int tn_rnenv, (CHAR *, int) ) ; _PROTOTYP( int tn_wait, (char *) ) ; _PROTOTYP( int tn_push, (void) ) ; _PROTOTYP( int tnsndbrk, (void) ); _PROTOTYP( VOID tn_debug, (char *)); _PROTOTYP( int tn_hex, (CHAR *, int, CHAR *, int)); _PROTOTYP( unsigned char * tn_get_display, (void)); #ifdef IKS_OPTION _PROTOTYP( int tn_siks, (int) ); _PROTOTYP( int iks_wait, (int, int) ); #endif /* IKS_OPTION */ #ifdef CK_NAWS _PROTOTYP( int tn_snaws, (void) ); #endif /* CK_NAWS */ #ifdef CK_XDISPLOC _PROTOTYP( int tn_sxdisploc, (void) ); #endif /* CK_XDISPLOC */ #ifdef CK_SNDLOC _PROTOTYP( int tn_sndloc, (void) ); #endif /* CK_SNDLOC */ #ifdef CK_FORWARD_X /* From Xauth.h */ typedef struct xauth { unsigned short family; unsigned short address_length; char *address; unsigned short number_length; char *number; unsigned short name_length; char *name; unsigned short data_length; char *data; } Xauth; #include /* from X.h */ #define FamilyInternet 0 #define FamilyDECnet 1 #define FamilyChaos 2 # define FamilyLocal (256) /* not part of X standard (i.e. X.h) */ # define FamilyWild (65535) # define FamilyNetname (254) /* not part of X standard */ # define FamilyKrb5Principal (253) /* Kerberos 5 principal name */ # define FamilyLocalHost (252) /* for local non-net authentication */ char *XauFileName(); Xauth *XauReadAuth( FILE* /* auth_file */ ); int XauWriteAuth( FILE* /* auth_file */, Xauth* /* auth */ ); Xauth *XauGetAuthByName( const char* /* display_name */ ); Xauth *XauGetAuthByAddr( unsigned int /* family */, unsigned int /* address_length */, const char* /* address */, unsigned int /* number_length */, const char* /* number */, unsigned int /* name_length */, const char* /* name */ ); void XauDisposeAuth( Xauth* /* auth */ ); _PROTOTYP( int fwdx_create_listen_socket,(int)); _PROTOTYP( int fwdx_open_client_channel,(int)); _PROTOTYP( int fwdx_open_server_channel,(VOID)); _PROTOTYP( int fwdx_close_channel,(int)); _PROTOTYP( int fwdx_write_data_to_channel,(int, char *,int)); _PROTOTYP( int fwdx_send_data_from_channel,(int, char *,int)); _PROTOTYP( int fwdx_close_all,(VOID)); _PROTOTYP( int fwdx_tn_sb,(unsigned char *, int)); _PROTOTYP( int tn_sndfwdx, (void)); _PROTOTYP( int fwdx_send_close,(int)); _PROTOTYP( int fwdx_send_open,(int)); _PROTOTYP( int fwdx_client_reply_options,(char *, int)); _PROTOTYP( int fwdx_send_options,(VOID)); _PROTOTYP( VOID fwdx_check_sockets,(fd_set *)); _PROTOTYP( int fwdx_init_fd_set,(fd_set *)); _PROTOTYP( int fwdx_authorize_channel, (int, unsigned char *, int)); _PROTOTYP( int fwdx_create_fake_xauth, (char *, int, int)); _PROTOTYP( int fwdx_send_xauth_to_xserver, (int, unsigned char *, int len)); _PROTOTYP( int fwdx_server_avail, (VOID)); _PROTOTYP( int fwdx_parse_displayname, (char *, int *, char **, int *, int *, char **)); #ifdef NT _PROTOTYP( VOID fwdx_thread,(VOID *)); #endif /* NT */ #endif /* CK_FORWARD_X */ #ifdef TN_COMPORT #define TNC_C2S_SIGNATURE 0 #define TNC_C2S_SET_BAUDRATE 1 #define TNC_C2S_SET_DATASIZE 2 #define TNC_C2S_SET_PARITY 3 #define TNC_C2S_SET_STOPSIZE 4 #define TNC_C2S_SET_CONTROL 5 #define TNC_C2S_NOTIFY_LINESTATE 6 #define TNC_C2S_NOTIFY_MODEMSTATE 7 #define TNC_C2S_FLOW_SUSPEND 8 #define TNC_C2S_FLOW_RESUME 9 #define TNC_C2S_SET_LS_MASK 10 #define TNC_C2S_SET_MS_MASK 11 #define TNC_C2S_PURGE 12 #define TNC_S2C_SIGNATURE 100 #define TNC_S2C_SET_BAUDRATE 101 #define TNC_S2C_SET_DATASIZE 102 #define TNC_S2C_SET_PARITY 103 #define TNC_S2C_SET_STOPSIZE 104 #define TNC_S2C_SET_CONTROL 105 #define TNC_S2C_SEND_LS 106 #define TNC_S2C_SEND_MS 107 #define TNC_S2C_FLOW_SUSPEND 108 #define TNC_S2C_FLOW_RESUME 109 #define TNC_S2C_SET_LS_MASK 110 #define TNC_S2C_SET_MS_MASK 111 #define TNC_S2C_PURGE 112 /* The COMPORT values are not defined in RFC 2217 */ #define TNC_BPS_REQUEST 0 #define TNC_BPS_300 3 #define TNC_BPS_600 4 #define TNC_BPS_1200 5 #define TNC_BPS_2400 6 #define TNC_BPS_4800 7 #define TNC_BPS_9600 8 #define TNC_BPS_14400 9 #define TNC_BPS_19200 10 #define TNC_BPS_28800 11 #define TNC_BPS_38400 12 #define TNC_BPS_57600 13 #define TNC_BPS_115200 14 #define TNC_BPS_230400 15 #define TNC_BPS_460800 16 #define TNC_DS_REQUEST 0 #define TNC_DS_5 5 #define TNC_DS_6 6 #define TNC_DS_7 7 #define TNC_DS_8 8 #define TNC_PAR_REQUEST 0 #define TNC_PAR_NONE 1 #define TNC_PAR_ODD 2 #define TNC_PAR_EVEN 3 #define TNC_PAR_MARK 4 #define TNC_PAR_SPACE 5 #define TNC_SB_REQUEST 0 #define TNC_SB_1 1 #define TNC_SB_1_5 3 #define TNC_SB_2 2 #define TNC_CTL_OFLOW_REQUEST 0 #define TNC_CTL_OFLOW_NONE 1 #define TNC_CTL_OFLOW_XON_XOFF 2 #define TNC_CTL_OFLOW_RTS_CTS 3 #define TNC_CTL_OFLOW_DCD 17 #define TNC_CTL_OFLOW_DSR 19 #define TNC_CTL_BREAK_REQUEST 4 #define TNC_CTL_BREAK_ON 5 #define TNC_CTL_BREAK_OFF 6 #define TNC_CTL_DTR_REQUEST 7 #define TNC_CTL_DTR_ON 8 #define TNC_CTL_DTR_OFF 9 #define TNC_CTL_RTS_REQUEST 10 #define TNC_CTL_RTS_ON 11 #define TNC_CTL_RTS_OFF 12 #define TNC_CTL_IFLOW_REQUEST 13 #define TNC_CTL_IFLOW_NONE 14 #define TNC_CTL_IFLOW_XON_XOFF 15 #define TNC_CTL_IFLOW_RTS_CTS 16 #define TNC_CTL_IFLOW_DTR 18 #define TNC_MS_DATA_READY 1 #define TNC_MS_OVERRUN_ERROR 2 #define TNC_MS_PARITY_ERROR 4 #define TNC_MS_FRAME_ERROR 8 #define TNC_MS_BREAK_ERROR 16 #define TNC_MS_HR_EMPTY 32 #define TNC_MS_SR_EMPTY 64 #define TNC_MS_TIMEOUT_ERROR 128 #define TNC_MS_CTS_DELTA 1 #define TNC_MS_DSR_DELTA 2 #define TNC_MS_EDGE_RING 4 #define TNC_MS_RLSD_DELTA 8 #define TNC_MS_CTS_SIG 16 #define TNC_MS_DSR_SIG 32 #define TNC_MS_RI_SIG 64 #define TNC_MS_RLSD_SIG 128 #define TNC_PURGE_RECEIVE 1 #define TNC_PURGE_TRANSMIT 2 #define TNC_PURGE_BOTH 3 #ifdef TNC_NAMES char *tnc_names[] = { "SIGNATURE", "SET-BAUDRATE", "SET-DATARATE", "SET-PARITY", "SET-STOPSIZE", "SET-CONTROL", "NOTIFY-LINESTATE", "NOTIFY-MODEMSTATE", "FLOWCONTROL-SUSPEND", "FLOWCONTROL-RESUME", "SET-LINESTATE-MASK", "SET-MODEMSTATE-MASK", "PURGE-DATA", 0 }; #else extern char *tnc_names[]; #endif #define TNC_NAME_OK(x) ((x) >= 0 && (x) <= 12 || (x) >= 100 && (x) <= 112) #define TNC_NAME(x) \ (TNC_NAME_OK(x)?tnc_names[(x)>=100?(x)-100:(x)]:"UNKNOWN") _PROTOTYP(int tnc_init,(void)); _PROTOTYP(int tnc_wait,(CHAR *, int)); _PROTOTYP(int tnc_tn_sb,(CHAR *,int)); _PROTOTYP(CONST char * tnc_get_signature, (void)); _PROTOTYP(int tnc_send_signature, (char *)); _PROTOTYP(int tnc_set_baud,(long)); _PROTOTYP(int tnc_get_baud,(void)); _PROTOTYP(int tnc_set_datasize,(int)); _PROTOTYP(int tnc_get_datasize,(void)); _PROTOTYP(int tnc_set_parity,(int)); _PROTOTYP(int tnc_get_parity,(void)); _PROTOTYP(int tnc_set_stopsize,(int)); _PROTOTYP(int tnc_get_stopsize,(void)); _PROTOTYP(int tnc_set_oflow,(int)); _PROTOTYP(int tnc_get_oflow,(void)); _PROTOTYP(int tnc_set_iflow,(int)); _PROTOTYP(int tnc_get_iflow,(void)); _PROTOTYP(int tnc_set_break_state,(int)); _PROTOTYP(int tnc_get_break_state,(void)); _PROTOTYP(int tnc_set_dtr_state,(int)); _PROTOTYP(int tnc_get_dtr_state,(void)); _PROTOTYP(int tnc_set_rts_state,(int)); _PROTOTYP(int tnc_get_rts_state,(void)); _PROTOTYP(int tnc_set_ls_mask,(int)); _PROTOTYP(int tnc_get_ls_mask,(void)); _PROTOTYP(int tnc_get_ls,(void)); _PROTOTYP(int tnc_set_ms_mask,(int)); _PROTOTYP(int tnc_get_ms_mask,(void)); _PROTOTYP(int tnc_get_ms,(void)); _PROTOTYP(int tnc_send_purge_data,(int)); _PROTOTYP(int tnc_flow_suspended,(void)); _PROTOTYP(int tnc_suspend_flow,(void)); _PROTOTYP(int tnc_resume_flow,(void)); /* The following methods are to be called by ck?tio.c routines */ _PROTOTYP(int tnsetflow,(int)); _PROTOTYP(int tnsettings,(int,int)); _PROTOTYP(int tngmdm,(void)); _PROTOTYP(int tnsndb,(long)); _PROTOTYP(int istncomport,(void)); _PROTOTYP(int tn_sndcomport,(void)); #endif /* TN_COMPORT */ #ifndef CKCTEL_C /* These are declared in ckctel.c */ extern int tn_init; /* Telnet protocol initialized flag */ extern char *tn_term; /* Terminal type override */ extern int sstelnet; /* Server side telnet? */ extern int tn_deb; /* Telnet option debugging flag */ extern int tn_auth_krb5_des_bug; /* Telnet BUG */ #endif /* CKCTEL_C */ #define TN_MSG_LEN 12292 #endif /* TNCODE */ #endif /* CKCTEL_H */ ckcuni.c0000644000015300001460000274630511266110211011311 0ustar fdckermitchar * ckcuni = "Unicode support 9.0.115, 16 Oct 2009"; #ifdef OS2 #define KERMITFONT #endif /* OS2 */ /* C K C U N I . C -- Unicode/Terminal character-set translations */ /* Copyright (C) 1999, 2009, Trustees of Columbia University in the City of New York. All rights reserved. See the C-Kermit COPYING.TXT file or the copyright text in the ckcmai.c module for disclaimer and permissions. Authors: Frank da Cruz The Kermit Project, Columbia University, New York City. Jeffrey E Altman Secure Endpoints Inc., New York City Functions u_blah() translate from blah to Unicode (incoming material). Functions tx_blah() translate from Unicode to blah (keystrokes). Function pointers are kept in parallel arrays indexed by TX_blah values defined in ckcuni.h. NOTE: tx_decspec and tx_dectech are undefined since these character sets are never typed, only displayed. The blah_u() routines accept an unsigned character value in character-set blah, and return the Unicode translation, or 0xfffd if the character is not defined in Unicode. The 8th bit of the argument should be ignored, a`la ISO 4873 and 2022. The tx_blah() routines accept a unicode value and return the value of the corresponding character in character-set blah, or else -1 if the character does not exist in blah. In this case, the full 8-bit value is returned, since ISO 2022 only works in the host-to-terminal direction. NOTE: KERMITFONT should be defined only if we are using the new (still hypothetical) Kermit font that has all the VT/Heath/Wyse/TVI graphic characters in it. IMPORTANT: all Kermitfont code points need updating to the values in the final proposal to the UTC. LATER NOTE: The needed characters were approved for Unicode 3.1, and therefore nothing special should be required to use them, except that it will take some time for them to show up in commercial fonts. TO DO: A lot of the functions can be tightened up -- use ranges when possible, sort switch statements in order of frequency, etc. */ #include "ckcsym.h" /* Includes... */ #include "ckcdeb.h" #include "ckcker.h" #include "ckucmd.h" #include "ckcxla.h" #include "ckuxla.h" #ifdef UNICODE #ifdef OS2 /* This material is needed for the integration of TextPS into Kermit 95. When printing a file we use the SET FILE CHARACTER-SET value as the input character-set and then convert it to the special Latin1 set called CourierLatin1 using the Unicode translation functions. */ struct _pair { int tx; int fc; } TXFC[] = { TX_ASCII, FC_USASCII, TX_BRITISH, FC_UKASCII, TX_CN_FRENCH, FC_FCASCII, TX_CP437, FC_CP437, TX_CP850, FC_CP850, TX_CP852, FC_CP852, TX_CP857, -2, TX_CP862, FC_CP862, TX_CP864, -2, TX_CP866, FC_CP866, TX_CP869, -2, TX_CUBAN, -2, TX_CZECH, -2, TX_DANISH, -2, TX_DECMCS, FC_DECMCS, TX_DECSPEC, -2, /* while defined, not in fcs tables */ TX_DECTECH, -2, /* while defined, not in fcs tables */ TX_DGI, FC_DGMCS, TX_DUTCH, FC_DUASCII, TX_FINNISH, FC_FIASCII, TX_FRENCH, FC_FRASCII, TX_GERMAN, FC_GEASCII, TX_HE7, FC_HE7, TX_HPR8, FC_HPR8, TX_HUNGARIAN, FC_HUASCII, TX_ITALIAN, FC_ITASCII, TX_J201R, -2, TX_J201K, -2, TX_KOI7, FC_KOI7, TX_KOI8, FC_KOI8, TX_KOI8R, FC_KOI8R, TX_KOI8U, FC_KOI8U, TX_8859_1, FC_1LATIN, TX_8859_2, FC_2LATIN, TX_8859_3, -2, TX_8859_4, -2, TX_8859_5, FC_CYRILL, TX_8859_6, -2, TX_8859_7, -2, TX_8859_8, FC_HEBREW, TX_8859_9, -2, TX_8859_10, -2, TX_8859_15, -2, TX_MACL1, FC_APPQD, TX_NEXT, FC_NEXT, TX_NORWEGIAN, FC_NOASCII, TX_PORTUGUESE, FC_POASCII, TX_SPANISH, FC_SPASCII, TX_SWEDISH, FC_SWASCII, TX_SWISS, FC_CHASCII, TX_ICELANDIC, -2, TX_JIS7, -2, TX_SHJIS, FC_SHJIS, TX_JEUC, FC_JEUC, TX_JDEC, FC_JDEC, TX_ELOT927, FC_ELOT, TX_DGPCGRPH, -2, TX_DGLDGRPH, -2, TX_DGWPGRPH, -2, TX_HPLINE, -2, TX_HPMATH, -2, TX_QNXGRPH, -2, TX_SNIBRACK, -2, TX_SNIEURO, -2, TX_SNIFACET, -2, TX_SNIIBM, -2, TX_SNIBLANK, -2, TX_CP1252, -2, TX_CP1250, FC_CP1250, TX_CP1251, FC_CP1251, TX_CP1253, -2, TX_CP1254, -2, TX_CP1257, -2, TX_CP856, -2, TX_CP855, FC_CP855, TX_CP819, FC_1LATIN, TX_CP912, FC_2LATIN, TX_CP913, -2, TX_CP914, -2, TX_CP915, FC_CYRILL, TX_CP1089, -2, TX_CP813, FC_GREEK, TX_CP916, FC_HEBREW, TX_CP920, -2, TX_CP1051, -2, TX_CP858, FC_CP858, TX_8859_15, FC_9LATIN, TX_CP923, FC_CP923, TX_ELOT928, -2, TX_CP10000, -2, TX_CP37, -2, TX_CP1255, -2, TX_CP1256, -2, TX_CP1258, -2, TX_MAZOVIA, FC_MAZOVIA, TX_APL1, -2, TX_APL2, -2, TX_APL3, -2, TX_APL4, -2, TX_APL5, -2, TX_TRANSP, FC_TRANSP }; int nTXFC = sizeof(TXFC) / sizeof(struct _pair); int #ifdef CK_ANSIC fc2tx(int fc) #else fc2tx(int c) int fc; #endif /* CK_ANSIC */ { int i; for (i = 0; i < nTXFC ; i++) if (TXFC[i].fc == fc && TXFC[i].tx >= 0) return(TXFC[i].tx); return(TX_ASCII); } int #ifdef CK_ANSIC tx2fc(int tx) #else tx2fc(int x) int tx; #endif /* CK_ANSIC */ { int i; for (i = 0; i < nTXFC ; i++) if (TXFC[i].tx == tx && TXFC[i].fc >= 0) return(TXFC[i].fc); return(FC_USASCII); } /* SET TERMINAL REMOTE CHARACTER-SET keyword table */ struct keytab txrtab[] = { "apl2-ibm", TX_APL4, 0, "apl-2741", TX_APL5, 0, "apl-dyadic", TX_APL2, 0, "apl-iso", TX_APL1, 0, "apl-plus-2000", TX_APL3, 0, /* = APL-2000 */ "arabic-iso", TX_8859_6, 0, "ascii", TX_ASCII, 0, "british", TX_BRITISH, 0, "canadian-french", TX_CN_FRENCH, 0, "bulgaria-pc", TX_CP856, 0, #ifdef COMMENT "cp037", TX_CP37, 0, /* U.S. EBCDIC */ #endif /* COMMENT */ "cp10000", TX_CP10000, 0, /* Apple Quickdraw */ "cp1051", TX_CP1051, 0, /* Same as HP Roman 8 */ "cp1089", TX_CP1089, 0, /* Same as ISO 8859-6 */ "cp1250", TX_CP1250, 0, /* Latin-2 Windows */ "cp1251", TX_CP1251, 0, /* Cyrillic Windows */ "cp1252", TX_CP1252, 0, /* Latin-1 Windows */ "cp1253", TX_CP1253, 0, /* Greek Windows */ "cp1254", TX_CP1254, 0, /* Turkey Windows */ "cp1255", TX_CP1255, 0, /* Hebrew Windows */ "cp1256", TX_CP1256, 0, /* Arabic Windows */ "cp1257", TX_CP1257, 0, /* Latin-4 Windows */ "cp1258", TX_CP1258, 0, /* Viet Nam Windows */ "cp437", TX_CP437, 0, "cp813", TX_CP813, 0, /* Same as ISO 8859-7 */ "cp819", TX_CP819, 0, /* Same as ISO 8859-1 */ "cp850", TX_CP850, 0, "cp852", TX_CP852, 0, "cp855", TX_CP855, 0, /* Cyrillic */ "cp856", TX_CP856, CM_INV, /* Bulgaria */ "cp857", TX_CP857, 0, /* Latin-5 */ "cp858", TX_CP858, 0, /* Euro modified cp850 */ "cp862-hebrew", TX_CP862, 0, /* Hebrew */ "cp864", TX_CP864, 0, /* Arabic */ "cp866", TX_CP866, 0, /* Cyrillic */ "cp869", TX_CP869, 0, /* Greek */ "cp912", TX_CP912, 0, /* Same as ISO 8859-2 */ "cp913", TX_CP913, 0, /* Same as ISO 8859-3 */ "cp914", TX_CP914, 0, /* Same as ISO 8859-4 */ "cp915", TX_CP915, 0, /* Same as ISO 8859-5 */ "cp916", TX_CP916, 0, /* Same as ISO 8859-8 */ "cp920", TX_CP920, 0, /* Same as ISO 8859-9 */ "cp923", TX_CP923, 0, /* Same as ISO 8859-15 */ #ifdef COMMENT /* Not implemented yet */ "cuban", TX_CUBAN, 0, #endif /* COMMENT */ "cyrillic-iso", TX_8859_5, 0, #ifdef COMMENT /* Not implemented yet */ "czech", TX_CZECH, 0, #endif /* COMMENT */ "danish", TX_DANISH, 0, "dec-m", TX_DECMCS, CM_ABR|CM_INV, "dec-mcs", TX_DECMCS, CM_INV, "dec-multinational",TX_DECMCS, 0, #ifdef COMMENT /* Not implemented yet */ "dec-kanji", TX_JDEC, 0, #endif /* COMMENT */ "dec-special", TX_DECSPEC, 0, "dec-technical", TX_DECTECH, 0, "dg-international", TX_DGI, 0, "dg-linedrawing", TX_DGLDGRPH, 0, "dg-specialgraphcs",TX_DGPCGRPH, 0, "dg-wordprocessing",TX_DGWPGRPH, 0, "dutch", TX_DUTCH, 0, "elot927-greek", TX_ELOT927, 0, "elot928-greek", TX_ELOT928, 0, "finnish", TX_FINNISH, 0, "french", TX_FRENCH, 0, "german", TX_GERMAN, 0, "greek-iso", TX_8859_7, 0, "hebrew-7", TX_HE7, 0, "hebrew-iso", TX_8859_8, 0, "hp-line-drawing", TX_HPLINE, 0, "hp-math/technical",TX_HPMATH, 0, "hp-roman8", TX_HPR8, 0, "hungarian", TX_HUNGARIAN, 0, "italian", TX_ITALIAN, 0, "japanese-roman", TX_J201R, 0, #ifdef COMMENT /* Not implemented yet */ "japanese-euc", TX_JEUC, 0, "jis7-kanji", TX_JIS7, 0, #endif /* COMMENT */ "katakana", TX_J201K, 0, "ko", TX_KOI8, CM_ABR|CM_INV, "koi", TX_KOI8, CM_ABR|CM_INV, "koi8", TX_KOI8, 0, "koi8-cyrillic", TX_KOI8, CM_INV, "koi8r", TX_KOI8R, 0, "koi8u", TX_KOI8U, 0, "l", TX_8859_1, CM_ABR|CM_INV, "la", TX_8859_1, CM_ABR|CM_INV, "lat", TX_8859_1, CM_ABR|CM_INV, "lati", TX_8859_1, CM_ABR|CM_INV, "latin", TX_8859_1, CM_ABR|CM_INV, "latin1-iso", TX_8859_1, 0, "latin2-iso", TX_8859_2, 0, "latin3-iso", TX_8859_3, 0, "latin4-iso", TX_8859_4, 0, "latin5-iso", TX_8859_9, 0, "latin6-iso", TX_8859_10, 0, "latin9-iso", TX_8859_15, 0, "macintosh-latin", TX_MACL1, 0, "mazovia-pc", TX_MAZOVIA, 0, "next-multinational",TX_NEXT, 0, "norwegian", TX_NORWEGIAN, 0, "portuguese", TX_PORTUGUESE, 0, "qnx-console", TX_QNXGRPH, 0, #ifdef COMMENT /* Not implemented yet */ "shift-jis", TX_SHJIS, 0, #endif /* COMMENT */ "short-koi", TX_KOI7, 0, "sni-blanks", TX_SNIBLANK, 0, "sni-brackets", TX_SNIBRACK, 0, "sni-euro", TX_SNIEURO, 0, "sni-facet", TX_SNIFACET, 0, "sni-ibm", TX_SNIIBM, 0, "spanish", TX_SPANISH, 0, "swedish", TX_SWEDISH, 0, "swiss", TX_SWISS, 0, "transparent", TX_TRANSP, 0, #ifdef COMMENT "utf7", TX_UTF7, 0, #endif /* COMMENT */ "utf8", TX_UTF8, 0, "", 0, 0 }; int ntxrtab = sizeof(txrtab)/sizeof(struct keytab) - 1; #endif /* OS2 */ #ifdef KANJI /* All Kanji/Unicode translations are based on the Unicode 3.0 Shift-JIS mapping. Other character sets, like EUC-JP, JIS-7, etc, are transformed algorithmically to/from Shift-JIS before/after conversion to/from Unicode. This is because Kanji/Unicode mapping tables add about 120K to the program, and we don't want to do this for each Kanji character set. */ static USHORT /* Shift-JIS to Unicode */ sju_8140[] = { /* 0x8140 thru 0x9ffc */ 0x3000, 0x3001, 0x3002, 0xff0c, 0xff0e, 0x30fb, 0xff1a, 0xff1b, 0xff1f, 0xff01, 0x309b, 0x309c, 0x00b4, 0xff40, 0x00a8, 0xff3e, 0xffe3, 0xff3f, 0x30fd, 0x30fe, 0x309d, 0x309e, 0x3003, 0x4edd, 0x3005, 0x3006, 0x3007, 0x30fc, 0x2015, 0x2010, 0xff0f, 0x005c, 0x301c, 0x2016, 0xff5c, 0x2026, 0x2025, 0x2018, 0x2019, 0x201c, 0x201d, 0xff08, 0xff09, 0x3014, 0x3015, 0xff3b, 0xff3d, 0xff5b, 0xff5d, 0x3008, 0x3009, 0x300a, 0x300b, 0x300c, 0x300d, 0x300e, 0x300f, 0x3010, 0x3011, 0xff0b, 0x2212, 0x00b1, 0x00d7, 0xfffd, 0x00f7, 0xff1d, 0x2260, 0xff1c, 0xff1e, 0x2266, 0x2267, 0x221e, 0x2234, 0x2642, 0x2640, 0x00b0, 0x2032, 0x2033, 0x2103, 0xffe5, 0xff04, 0x00a2, 0x00a3, 0xff05, 0xff03, 0xff06, 0xff0a, 0xff20, 0x00a7, 0x2606, 0x2605, 0x25cb, 0x25cf, 0x25ce, 0x25c7, 0x25c6, 0x25a1, 0x25a0, 0x25b3, 0x25b2, 0x25bd, 0x25bc, 0x203b, 0x3012, 0x2192, 0x2190, 0x2191, 0x2193, 0x3013, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x2208, 0x220b, 0x2286, 0x2287, 0x2282, 0x2283, 0x222a, 0x2229, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x2227, 0x2228, 0x00ac, 0x21d2, 0x21d4, 0x2200, 0x2203, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x2220, 0x22a5, 0x2312, 0x2202, 0x2207, 0x2261, 0x2252, 0x226a, 0x226b, 0x221a, 0x223d, 0x221d, 0x2235, 0x222b, 0x222c, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x212b, 0x2030, 0x266f, 0x266d, 0x266a, 0x2020, 0x2021, 0x00b6, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x25ef, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xff10, 0xff11, 0xff12, 0xff13, 0xff14, 0xff15, 0xff16, 0xff17, 0xff18, 0xff19, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xff21, 0xff22, 0xff23, 0xff24, 0xff25, 0xff26, 0xff27, 0xff28, 0xff29, 0xff2a, 0xff2b, 0xff2c, 0xff2d, 0xff2e, 0xff2f, 0xff30, 0xff31, 0xff32, 0xff33, 0xff34, 0xff35, 0xff36, 0xff37, 0xff38, 0xff39, 0xff3a, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xff41, 0xff42, 0xff43, 0xff44, 0xff45, 0xff46, 0xff47, 0xff48, 0xff49, 0xff4a, 0xff4b, 0xff4c, 0xff4d, 0xff4e, 0xff4f, 0xff50, 0xff51, 0xff52, 0xff53, 0xff54, 0xff55, 0xff56, 0xff57, 0xff58, 0xff59, 0xff5a, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x3041, 0x3042, 0x3043, 0x3044, 0x3045, 0x3046, 0x3047, 0x3048, 0x3049, 0x304a, 0x304b, 0x304c, 0x304d, 0x304e, 0x304f, 0x3050, 0x3051, 0x3052, 0x3053, 0x3054, 0x3055, 0x3056, 0x3057, 0x3058, 0x3059, 0x305a, 0x305b, 0x305c, 0x305d, 0x305e, 0x305f, 0x3060, 0x3061, 0x3062, 0x3063, 0x3064, 0x3065, 0x3066, 0x3067, 0x3068, 0x3069, 0x306a, 0x306b, 0x306c, 0x306d, 0x306e, 0x306f, 0x3070, 0x3071, 0x3072, 0x3073, 0x3074, 0x3075, 0x3076, 0x3077, 0x3078, 0x3079, 0x307a, 0x307b, 0x307c, 0x307d, 0x307e, 0x307f, 0x3080, 0x3081, 0x3082, 0x3083, 0x3084, 0x3085, 0x3086, 0x3087, 0x3088, 0x3089, 0x308a, 0x308b, 0x308c, 0x308d, 0x308e, 0x308f, 0x3090, 0x3091, 0x3092, 0x3093, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x30a1, 0x30a2, 0x30a3, 0x30a4, 0x30a5, 0x30a6, 0x30a7, 0x30a8, 0x30a9, 0x30aa, 0x30ab, 0x30ac, 0x30ad, 0x30ae, 0x30af, 0x30b0, 0x30b1, 0x30b2, 0x30b3, 0x30b4, 0x30b5, 0x30b6, 0x30b7, 0x30b8, 0x30b9, 0x30ba, 0x30bb, 0x30bc, 0x30bd, 0x30be, 0x30bf, 0x30c0, 0x30c1, 0x30c2, 0x30c3, 0x30c4, 0x30c5, 0x30c6, 0x30c7, 0x30c8, 0x30c9, 0x30ca, 0x30cb, 0x30cc, 0x30cd, 0x30ce, 0x30cf, 0x30d0, 0x30d1, 0x30d2, 0x30d3, 0x30d4, 0x30d5, 0x30d6, 0x30d7, 0x30d8, 0x30d9, 0x30da, 0x30db, 0x30dc, 0x30dd, 0x30de, 0x30df, 0xfffd, 0x30e0, 0x30e1, 0x30e2, 0x30e3, 0x30e4, 0x30e5, 0x30e6, 0x30e7, 0x30e8, 0x30e9, 0x30ea, 0x30eb, 0x30ec, 0x30ed, 0x30ee, 0x30ef, 0x30f0, 0x30f1, 0x30f2, 0x30f3, 0x30f4, 0x30f5, 0x30f6, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, 0x039e, 0x039f, 0x03a0, 0x03a1, 0x03a3, 0x03a4, 0x03a5, 0x03a6, 0x03a7, 0x03a8, 0x03a9, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x03b1, 0x03b2, 0x03b3, 0x03b4, 0x03b5, 0x03b6, 0x03b7, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf, 0x03c0, 0x03c1, 0x03c3, 0x03c4, 0x03c5, 0x03c6, 0x03c7, 0x03c8, 0x03c9, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0401, 0x0416, 0x0417, 0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, 0x041f, 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042a, 0x042b, 0x042c, 0x042d, 0x042e, 0x042f, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0451, 0x0436, 0x0437, 0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0xfffd, 0x043e, 0x043f, 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044a, 0x044b, 0x044c, 0x044d, 0x044e, 0x044f, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x2500, 0x2502, 0x250c, 0x2510, 0x2518, 0x2514, 0x251c, 0x252c, 0x2524, 0x2534, 0x253c, 0x2501, 0x2503, 0x250f, 0x2513, 0x251b, 0x2517, 0x2523, 0x2533, 0x252b, 0x253b, 0x254b, 0x2520, 0x252f, 0x2528, 0x2537, 0x253f, 0x251d, 0x2530, 0x2525, 0x2538, 0x2542, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x4e9c, 0x5516, 0x5a03, 0x963f, 0x54c0, 0x611b, 0x6328, 0x59f6, 0x9022, 0x8475, 0x831c, 0x7a50, 0x60aa, 0x63e1, 0x6e25, 0x65ed, 0x8466, 0x82a6, 0x9bf5, 0x6893, 0x5727, 0x65a1, 0x6271, 0x5b9b, 0x59d0, 0x867b, 0x98f4, 0x7d62, 0x7dbe, 0x9b8e, 0x6216, 0x7c9f, 0x88b7, 0x5b89, 0x5eb5, 0x6309, 0x6697, 0x6848, 0x95c7, 0x978d, 0x674f, 0x4ee5, 0x4f0a, 0x4f4d, 0x4f9d, 0x5049, 0x56f2, 0x5937, 0x59d4, 0x5a01, 0x5c09, 0x60df, 0x610f, 0x6170, 0x6613, 0x6905, 0x70ba, 0x754f, 0x7570, 0x79fb, 0x7dad, 0x7def, 0x80c3, 0x840e, 0x8863, 0x8b02, 0x9055, 0x907a, 0x533b, 0x4e95, 0x4ea5, 0x57df, 0x80b2, 0x90c1, 0x78ef, 0x4e00, 0x58f1, 0x6ea2, 0x9038, 0x7a32, 0x8328, 0x828b, 0x9c2f, 0x5141, 0x5370, 0x54bd, 0x54e1, 0x56e0, 0x59fb, 0x5f15, 0x98f2, 0x6deb, 0x80e4, 0x852d, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9662, 0x9670, 0x96a0, 0x97fb, 0x540b, 0x53f3, 0x5b87, 0x70cf, 0x7fbd, 0x8fc2, 0x96e8, 0x536f, 0x9d5c, 0x7aba, 0x4e11, 0x7893, 0x81fc, 0x6e26, 0x5618, 0x5504, 0x6b1d, 0x851a, 0x9c3b, 0x59e5, 0x53a9, 0x6d66, 0x74dc, 0x958f, 0x5642, 0x4e91, 0x904b, 0x96f2, 0x834f, 0x990c, 0x53e1, 0x55b6, 0x5b30, 0x5f71, 0x6620, 0x66f3, 0x6804, 0x6c38, 0x6cf3, 0x6d29, 0x745b, 0x76c8, 0x7a4e, 0x9834, 0x82f1, 0x885b, 0x8a60, 0x92ed, 0x6db2, 0x75ab, 0x76ca, 0x99c5, 0x60a6, 0x8b01, 0x8d8a, 0x95b2, 0x698e, 0x53ad, 0x5186, 0xfffd, 0x5712, 0x5830, 0x5944, 0x5bb4, 0x5ef6, 0x6028, 0x63a9, 0x63f4, 0x6cbf, 0x6f14, 0x708e, 0x7114, 0x7159, 0x71d5, 0x733f, 0x7e01, 0x8276, 0x82d1, 0x8597, 0x9060, 0x925b, 0x9d1b, 0x5869, 0x65bc, 0x6c5a, 0x7525, 0x51f9, 0x592e, 0x5965, 0x5f80, 0x5fdc, 0x62bc, 0x65fa, 0x6a2a, 0x6b27, 0x6bb4, 0x738b, 0x7fc1, 0x8956, 0x9d2c, 0x9d0e, 0x9ec4, 0x5ca1, 0x6c96, 0x837b, 0x5104, 0x5c4b, 0x61b6, 0x81c6, 0x6876, 0x7261, 0x4e59, 0x4ffa, 0x5378, 0x6069, 0x6e29, 0x7a4f, 0x97f3, 0x4e0b, 0x5316, 0x4eee, 0x4f55, 0x4f3d, 0x4fa1, 0x4f73, 0x52a0, 0x53ef, 0x5609, 0x590f, 0x5ac1, 0x5bb6, 0x5be1, 0x79d1, 0x6687, 0x679c, 0x67b6, 0x6b4c, 0x6cb3, 0x706b, 0x73c2, 0x798d, 0x79be, 0x7a3c, 0x7b87, 0x82b1, 0x82db, 0x8304, 0x8377, 0x83ef, 0x83d3, 0x8766, 0x8ab2, 0x5629, 0x8ca8, 0x8fe6, 0x904e, 0x971e, 0x868a, 0x4fc4, 0x5ce8, 0x6211, 0x7259, 0x753b, 0x81e5, 0x82bd, 0x86fe, 0x8cc0, 0x96c5, 0x9913, 0x99d5, 0x4ecb, 0x4f1a, 0x89e3, 0x56de, 0x584a, 0x58ca, 0x5efb, 0x5feb, 0x602a, 0x6094, 0x6062, 0x61d0, 0x6212, 0x62d0, 0x6539, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9b41, 0x6666, 0x68b0, 0x6d77, 0x7070, 0x754c, 0x7686, 0x7d75, 0x82a5, 0x87f9, 0x958b, 0x968e, 0x8c9d, 0x51f1, 0x52be, 0x5916, 0x54b3, 0x5bb3, 0x5d16, 0x6168, 0x6982, 0x6daf, 0x788d, 0x84cb, 0x8857, 0x8a72, 0x93a7, 0x9ab8, 0x6d6c, 0x99a8, 0x86d9, 0x57a3, 0x67ff, 0x86ce, 0x920e, 0x5283, 0x5687, 0x5404, 0x5ed3, 0x62e1, 0x64b9, 0x683c, 0x6838, 0x6bbb, 0x7372, 0x78ba, 0x7a6b, 0x899a, 0x89d2, 0x8d6b, 0x8f03, 0x90ed, 0x95a3, 0x9694, 0x9769, 0x5b66, 0x5cb3, 0x697d, 0x984d, 0x984e, 0x639b, 0x7b20, 0x6a2b, 0xfffd, 0x6a7f, 0x68b6, 0x9c0d, 0x6f5f, 0x5272, 0x559d, 0x6070, 0x62ec, 0x6d3b, 0x6e07, 0x6ed1, 0x845b, 0x8910, 0x8f44, 0x4e14, 0x9c39, 0x53f6, 0x691b, 0x6a3a, 0x9784, 0x682a, 0x515c, 0x7ac3, 0x84b2, 0x91dc, 0x938c, 0x565b, 0x9d28, 0x6822, 0x8305, 0x8431, 0x7ca5, 0x5208, 0x82c5, 0x74e6, 0x4e7e, 0x4f83, 0x51a0, 0x5bd2, 0x520a, 0x52d8, 0x52e7, 0x5dfb, 0x559a, 0x582a, 0x59e6, 0x5b8c, 0x5b98, 0x5bdb, 0x5e72, 0x5e79, 0x60a3, 0x611f, 0x6163, 0x61be, 0x63db, 0x6562, 0x67d1, 0x6853, 0x68fa, 0x6b3e, 0x6b53, 0x6c57, 0x6f22, 0x6f97, 0x6f45, 0x74b0, 0x7518, 0x76e3, 0x770b, 0x7aff, 0x7ba1, 0x7c21, 0x7de9, 0x7f36, 0x7ff0, 0x809d, 0x8266, 0x839e, 0x89b3, 0x8acc, 0x8cab, 0x9084, 0x9451, 0x9593, 0x9591, 0x95a2, 0x9665, 0x97d3, 0x9928, 0x8218, 0x4e38, 0x542b, 0x5cb8, 0x5dcc, 0x73a9, 0x764c, 0x773c, 0x5ca9, 0x7feb, 0x8d0b, 0x96c1, 0x9811, 0x9854, 0x9858, 0x4f01, 0x4f0e, 0x5371, 0x559c, 0x5668, 0x57fa, 0x5947, 0x5b09, 0x5bc4, 0x5c90, 0x5e0c, 0x5e7e, 0x5fcc, 0x63ee, 0x673a, 0x65d7, 0x65e2, 0x671f, 0x68cb, 0x68c4, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x6a5f, 0x5e30, 0x6bc5, 0x6c17, 0x6c7d, 0x757f, 0x7948, 0x5b63, 0x7a00, 0x7d00, 0x5fbd, 0x898f, 0x8a18, 0x8cb4, 0x8d77, 0x8ecc, 0x8f1d, 0x98e2, 0x9a0e, 0x9b3c, 0x4e80, 0x507d, 0x5100, 0x5993, 0x5b9c, 0x622f, 0x6280, 0x64ec, 0x6b3a, 0x72a0, 0x7591, 0x7947, 0x7fa9, 0x87fb, 0x8abc, 0x8b70, 0x63ac, 0x83ca, 0x97a0, 0x5409, 0x5403, 0x55ab, 0x6854, 0x6a58, 0x8a70, 0x7827, 0x6775, 0x9ecd, 0x5374, 0x5ba2, 0x811a, 0x8650, 0x9006, 0x4e18, 0x4e45, 0x4ec7, 0x4f11, 0x53ca, 0x5438, 0x5bae, 0x5f13, 0x6025, 0x6551, 0xfffd, 0x673d, 0x6c42, 0x6c72, 0x6ce3, 0x7078, 0x7403, 0x7a76, 0x7aae, 0x7b08, 0x7d1a, 0x7cfe, 0x7d66, 0x65e7, 0x725b, 0x53bb, 0x5c45, 0x5de8, 0x62d2, 0x62e0, 0x6319, 0x6e20, 0x865a, 0x8a31, 0x8ddd, 0x92f8, 0x6f01, 0x79a6, 0x9b5a, 0x4ea8, 0x4eab, 0x4eac, 0x4f9b, 0x4fa0, 0x50d1, 0x5147, 0x7af6, 0x5171, 0x51f6, 0x5354, 0x5321, 0x537f, 0x53eb, 0x55ac, 0x5883, 0x5ce1, 0x5f37, 0x5f4a, 0x602f, 0x6050, 0x606d, 0x631f, 0x6559, 0x6a4b, 0x6cc1, 0x72c2, 0x72ed, 0x77ef, 0x80f8, 0x8105, 0x8208, 0x854e, 0x90f7, 0x93e1, 0x97ff, 0x9957, 0x9a5a, 0x4ef0, 0x51dd, 0x5c2d, 0x6681, 0x696d, 0x5c40, 0x66f2, 0x6975, 0x7389, 0x6850, 0x7c81, 0x50c5, 0x52e4, 0x5747, 0x5dfe, 0x9326, 0x65a4, 0x6b23, 0x6b3d, 0x7434, 0x7981, 0x79bd, 0x7b4b, 0x7dca, 0x82b9, 0x83cc, 0x887f, 0x895f, 0x8b39, 0x8fd1, 0x91d1, 0x541f, 0x9280, 0x4e5d, 0x5036, 0x53e5, 0x533a, 0x72d7, 0x7396, 0x77e9, 0x82e6, 0x8eaf, 0x99c6, 0x99c8, 0x99d2, 0x5177, 0x611a, 0x865e, 0x55b0, 0x7a7a, 0x5076, 0x5bd3, 0x9047, 0x9685, 0x4e32, 0x6adb, 0x91e7, 0x5c51, 0x5c48, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x6398, 0x7a9f, 0x6c93, 0x9774, 0x8f61, 0x7aaa, 0x718a, 0x9688, 0x7c82, 0x6817, 0x7e70, 0x6851, 0x936c, 0x52f2, 0x541b, 0x85ab, 0x8a13, 0x7fa4, 0x8ecd, 0x90e1, 0x5366, 0x8888, 0x7941, 0x4fc2, 0x50be, 0x5211, 0x5144, 0x5553, 0x572d, 0x73ea, 0x578b, 0x5951, 0x5f62, 0x5f84, 0x6075, 0x6176, 0x6167, 0x61a9, 0x63b2, 0x643a, 0x656c, 0x666f, 0x6842, 0x6e13, 0x7566, 0x7a3d, 0x7cfb, 0x7d4c, 0x7d99, 0x7e4b, 0x7f6b, 0x830e, 0x834a, 0x86cd, 0x8a08, 0x8a63, 0x8b66, 0x8efd, 0x981a, 0x9d8f, 0x82b8, 0x8fce, 0x9be8, 0xfffd, 0x5287, 0x621f, 0x6483, 0x6fc0, 0x9699, 0x6841, 0x5091, 0x6b20, 0x6c7a, 0x6f54, 0x7a74, 0x7d50, 0x8840, 0x8a23, 0x6708, 0x4ef6, 0x5039, 0x5026, 0x5065, 0x517c, 0x5238, 0x5263, 0x55a7, 0x570f, 0x5805, 0x5acc, 0x5efa, 0x61b2, 0x61f8, 0x62f3, 0x6372, 0x691c, 0x6a29, 0x727d, 0x72ac, 0x732e, 0x7814, 0x786f, 0x7d79, 0x770c, 0x80a9, 0x898b, 0x8b19, 0x8ce2, 0x8ed2, 0x9063, 0x9375, 0x967a, 0x9855, 0x9a13, 0x9e78, 0x5143, 0x539f, 0x53b3, 0x5e7b, 0x5f26, 0x6e1b, 0x6e90, 0x7384, 0x73fe, 0x7d43, 0x8237, 0x8a00, 0x8afa, 0x9650, 0x4e4e, 0x500b, 0x53e4, 0x547c, 0x56fa, 0x59d1, 0x5b64, 0x5df1, 0x5eab, 0x5f27, 0x6238, 0x6545, 0x67af, 0x6e56, 0x72d0, 0x7cca, 0x88b4, 0x80a1, 0x80e1, 0x83f0, 0x864e, 0x8a87, 0x8de8, 0x9237, 0x96c7, 0x9867, 0x9f13, 0x4e94, 0x4e92, 0x4f0d, 0x5348, 0x5449, 0x543e, 0x5a2f, 0x5f8c, 0x5fa1, 0x609f, 0x68a7, 0x6a8e, 0x745a, 0x7881, 0x8a9e, 0x8aa4, 0x8b77, 0x9190, 0x4e5e, 0x9bc9, 0x4ea4, 0x4f7c, 0x4faf, 0x5019, 0x5016, 0x5149, 0x516c, 0x529f, 0x52b9, 0x52fe, 0x539a, 0x53e3, 0x5411, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x540e, 0x5589, 0x5751, 0x57a2, 0x597d, 0x5b54, 0x5b5d, 0x5b8f, 0x5de5, 0x5de7, 0x5df7, 0x5e78, 0x5e83, 0x5e9a, 0x5eb7, 0x5f18, 0x6052, 0x614c, 0x6297, 0x62d8, 0x63a7, 0x653b, 0x6602, 0x6643, 0x66f4, 0x676d, 0x6821, 0x6897, 0x69cb, 0x6c5f, 0x6d2a, 0x6d69, 0x6e2f, 0x6e9d, 0x7532, 0x7687, 0x786c, 0x7a3f, 0x7ce0, 0x7d05, 0x7d18, 0x7d5e, 0x7db1, 0x8015, 0x8003, 0x80af, 0x80b1, 0x8154, 0x818f, 0x822a, 0x8352, 0x884c, 0x8861, 0x8b1b, 0x8ca2, 0x8cfc, 0x90ca, 0x9175, 0x9271, 0x783f, 0x92fc, 0x95a4, 0x964d, 0xfffd, 0x9805, 0x9999, 0x9ad8, 0x9d3b, 0x525b, 0x52ab, 0x53f7, 0x5408, 0x58d5, 0x62f7, 0x6fe0, 0x8c6a, 0x8f5f, 0x9eb9, 0x514b, 0x523b, 0x544a, 0x56fd, 0x7a40, 0x9177, 0x9d60, 0x9ed2, 0x7344, 0x6f09, 0x8170, 0x7511, 0x5ffd, 0x60da, 0x9aa8, 0x72db, 0x8fbc, 0x6b64, 0x9803, 0x4eca, 0x56f0, 0x5764, 0x58be, 0x5a5a, 0x6068, 0x61c7, 0x660f, 0x6606, 0x6839, 0x68b1, 0x6df7, 0x75d5, 0x7d3a, 0x826e, 0x9b42, 0x4e9b, 0x4f50, 0x53c9, 0x5506, 0x5d6f, 0x5de6, 0x5dee, 0x67fb, 0x6c99, 0x7473, 0x7802, 0x8a50, 0x9396, 0x88df, 0x5750, 0x5ea7, 0x632b, 0x50b5, 0x50ac, 0x518d, 0x6700, 0x54c9, 0x585e, 0x59bb, 0x5bb0, 0x5f69, 0x624d, 0x63a1, 0x683d, 0x6b73, 0x6e08, 0x707d, 0x91c7, 0x7280, 0x7815, 0x7826, 0x796d, 0x658e, 0x7d30, 0x83dc, 0x88c1, 0x8f09, 0x969b, 0x5264, 0x5728, 0x6750, 0x7f6a, 0x8ca1, 0x51b4, 0x5742, 0x962a, 0x583a, 0x698a, 0x80b4, 0x54b2, 0x5d0e, 0x57fc, 0x7895, 0x9dfa, 0x4f5c, 0x524a, 0x548b, 0x643e, 0x6628, 0x6714, 0x67f5, 0x7a84, 0x7b56, 0x7d22, 0x932f, 0x685c, 0x9bad, 0x7b39, 0x5319, 0x518a, 0x5237, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x5bdf, 0x62f6, 0x64ae, 0x64e6, 0x672d, 0x6bba, 0x85a9, 0x96d1, 0x7690, 0x9bd6, 0x634c, 0x9306, 0x9bab, 0x76bf, 0x6652, 0x4e09, 0x5098, 0x53c2, 0x5c71, 0x60e8, 0x6492, 0x6563, 0x685f, 0x71e6, 0x73ca, 0x7523, 0x7b97, 0x7e82, 0x8695, 0x8b83, 0x8cdb, 0x9178, 0x9910, 0x65ac, 0x66ab, 0x6b8b, 0x4ed5, 0x4ed4, 0x4f3a, 0x4f7f, 0x523a, 0x53f8, 0x53f2, 0x55e3, 0x56db, 0x58eb, 0x59cb, 0x59c9, 0x59ff, 0x5b50, 0x5c4d, 0x5e02, 0x5e2b, 0x5fd7, 0x601d, 0x6307, 0x652f, 0x5b5c, 0x65af, 0x65bd, 0x65e8, 0x679d, 0x6b62, 0xfffd, 0x6b7b, 0x6c0f, 0x7345, 0x7949, 0x79c1, 0x7cf8, 0x7d19, 0x7d2b, 0x80a2, 0x8102, 0x81f3, 0x8996, 0x8a5e, 0x8a69, 0x8a66, 0x8a8c, 0x8aee, 0x8cc7, 0x8cdc, 0x96cc, 0x98fc, 0x6b6f, 0x4e8b, 0x4f3c, 0x4f8d, 0x5150, 0x5b57, 0x5bfa, 0x6148, 0x6301, 0x6642, 0x6b21, 0x6ecb, 0x6cbb, 0x723e, 0x74bd, 0x75d4, 0x78c1, 0x793a, 0x800c, 0x8033, 0x81ea, 0x8494, 0x8f9e, 0x6c50, 0x9e7f, 0x5f0f, 0x8b58, 0x9d2b, 0x7afa, 0x8ef8, 0x5b8d, 0x96eb, 0x4e03, 0x53f1, 0x57f7, 0x5931, 0x5ac9, 0x5ba4, 0x6089, 0x6e7f, 0x6f06, 0x75be, 0x8cea, 0x5b9f, 0x8500, 0x7be0, 0x5072, 0x67f4, 0x829d, 0x5c61, 0x854a, 0x7e1e, 0x820e, 0x5199, 0x5c04, 0x6368, 0x8d66, 0x659c, 0x716e, 0x793e, 0x7d17, 0x8005, 0x8b1d, 0x8eca, 0x906e, 0x86c7, 0x90aa, 0x501f, 0x52fa, 0x5c3a, 0x6753, 0x707c, 0x7235, 0x914c, 0x91c8, 0x932b, 0x82e5, 0x5bc2, 0x5f31, 0x60f9, 0x4e3b, 0x53d6, 0x5b88, 0x624b, 0x6731, 0x6b8a, 0x72e9, 0x73e0, 0x7a2e, 0x816b, 0x8da3, 0x9152, 0x9996, 0x5112, 0x53d7, 0x546a, 0x5bff, 0x6388, 0x6a39, 0x7dac, 0x9700, 0x56da, 0x53ce, 0x5468, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x5b97, 0x5c31, 0x5dde, 0x4fee, 0x6101, 0x62fe, 0x6d32, 0x79c0, 0x79cb, 0x7d42, 0x7e4d, 0x7fd2, 0x81ed, 0x821f, 0x8490, 0x8846, 0x8972, 0x8b90, 0x8e74, 0x8f2f, 0x9031, 0x914b, 0x916c, 0x96c6, 0x919c, 0x4ec0, 0x4f4f, 0x5145, 0x5341, 0x5f93, 0x620e, 0x67d4, 0x6c41, 0x6e0b, 0x7363, 0x7e26, 0x91cd, 0x9283, 0x53d4, 0x5919, 0x5bbf, 0x6dd1, 0x795d, 0x7e2e, 0x7c9b, 0x587e, 0x719f, 0x51fa, 0x8853, 0x8ff0, 0x4fca, 0x5cfb, 0x6625, 0x77ac, 0x7ae3, 0x821c, 0x99ff, 0x51c6, 0x5faa, 0x65ec, 0x696f, 0x6b89, 0x6df3, 0xfffd, 0x6e96, 0x6f64, 0x76fe, 0x7d14, 0x5de1, 0x9075, 0x9187, 0x9806, 0x51e6, 0x521d, 0x6240, 0x6691, 0x66d9, 0x6e1a, 0x5eb6, 0x7dd2, 0x7f72, 0x66f8, 0x85af, 0x85f7, 0x8af8, 0x52a9, 0x53d9, 0x5973, 0x5e8f, 0x5f90, 0x6055, 0x92e4, 0x9664, 0x50b7, 0x511f, 0x52dd, 0x5320, 0x5347, 0x53ec, 0x54e8, 0x5546, 0x5531, 0x5617, 0x5968, 0x59be, 0x5a3c, 0x5bb5, 0x5c06, 0x5c0f, 0x5c11, 0x5c1a, 0x5e84, 0x5e8a, 0x5ee0, 0x5f70, 0x627f, 0x6284, 0x62db, 0x638c, 0x6377, 0x6607, 0x660c, 0x662d, 0x6676, 0x677e, 0x68a2, 0x6a1f, 0x6a35, 0x6cbc, 0x6d88, 0x6e09, 0x6e58, 0x713c, 0x7126, 0x7167, 0x75c7, 0x7701, 0x785d, 0x7901, 0x7965, 0x79f0, 0x7ae0, 0x7b11, 0x7ca7, 0x7d39, 0x8096, 0x83d6, 0x848b, 0x8549, 0x885d, 0x88f3, 0x8a1f, 0x8a3c, 0x8a54, 0x8a73, 0x8c61, 0x8cde, 0x91a4, 0x9266, 0x937e, 0x9418, 0x969c, 0x9798, 0x4e0a, 0x4e08, 0x4e1e, 0x4e57, 0x5197, 0x5270, 0x57ce, 0x5834, 0x58cc, 0x5b22, 0x5e38, 0x60c5, 0x64fe, 0x6761, 0x6756, 0x6d44, 0x72b6, 0x7573, 0x7a63, 0x84b8, 0x8b72, 0x91b8, 0x9320, 0x5631, 0x57f4, 0x98fe, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x62ed, 0x690d, 0x6b96, 0x71ed, 0x7e54, 0x8077, 0x8272, 0x89e6, 0x98df, 0x8755, 0x8fb1, 0x5c3b, 0x4f38, 0x4fe1, 0x4fb5, 0x5507, 0x5a20, 0x5bdd, 0x5be9, 0x5fc3, 0x614e, 0x632f, 0x65b0, 0x664b, 0x68ee, 0x699b, 0x6d78, 0x6df1, 0x7533, 0x75b9, 0x771f, 0x795e, 0x79e6, 0x7d33, 0x81e3, 0x82af, 0x85aa, 0x89aa, 0x8a3a, 0x8eab, 0x8f9b, 0x9032, 0x91dd, 0x9707, 0x4eba, 0x4ec1, 0x5203, 0x5875, 0x58ec, 0x5c0b, 0x751a, 0x5c3d, 0x814e, 0x8a0a, 0x8fc5, 0x9663, 0x976d, 0x7b25, 0x8acf, 0x9808, 0x9162, 0x56f3, 0x53a8, 0xfffd, 0x9017, 0x5439, 0x5782, 0x5e25, 0x63a8, 0x6c34, 0x708a, 0x7761, 0x7c8b, 0x7fe0, 0x8870, 0x9042, 0x9154, 0x9310, 0x9318, 0x968f, 0x745e, 0x9ac4, 0x5d07, 0x5d69, 0x6570, 0x67a2, 0x8da8, 0x96db, 0x636e, 0x6749, 0x6919, 0x83c5, 0x9817, 0x96c0, 0x88fe, 0x6f84, 0x647a, 0x5bf8, 0x4e16, 0x702c, 0x755d, 0x662f, 0x51c4, 0x5236, 0x52e2, 0x59d3, 0x5f81, 0x6027, 0x6210, 0x653f, 0x6574, 0x661f, 0x6674, 0x68f2, 0x6816, 0x6b63, 0x6e05, 0x7272, 0x751f, 0x76db, 0x7cbe, 0x8056, 0x58f0, 0x88fd, 0x897f, 0x8aa0, 0x8a93, 0x8acb, 0x901d, 0x9192, 0x9752, 0x9759, 0x6589, 0x7a0e, 0x8106, 0x96bb, 0x5e2d, 0x60dc, 0x621a, 0x65a5, 0x6614, 0x6790, 0x77f3, 0x7a4d, 0x7c4d, 0x7e3e, 0x810a, 0x8cac, 0x8d64, 0x8de1, 0x8e5f, 0x78a9, 0x5207, 0x62d9, 0x63a5, 0x6442, 0x6298, 0x8a2d, 0x7a83, 0x7bc0, 0x8aac, 0x96ea, 0x7d76, 0x820c, 0x8749, 0x4ed9, 0x5148, 0x5343, 0x5360, 0x5ba3, 0x5c02, 0x5c16, 0x5ddd, 0x6226, 0x6247, 0x64b0, 0x6813, 0x6834, 0x6cc9, 0x6d45, 0x6d17, 0x67d3, 0x6f5c, 0x714e, 0x717d, 0x65cb, 0x7a7f, 0x7bad, 0x7dda, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x7e4a, 0x7fa8, 0x817a, 0x821b, 0x8239, 0x85a6, 0x8a6e, 0x8cce, 0x8df5, 0x9078, 0x9077, 0x92ad, 0x9291, 0x9583, 0x9bae, 0x524d, 0x5584, 0x6f38, 0x7136, 0x5168, 0x7985, 0x7e55, 0x81b3, 0x7cce, 0x564c, 0x5851, 0x5ca8, 0x63aa, 0x66fe, 0x66fd, 0x695a, 0x72d9, 0x758f, 0x758e, 0x790e, 0x7956, 0x79df, 0x7c97, 0x7d20, 0x7d44, 0x8607, 0x8a34, 0x963b, 0x9061, 0x9f20, 0x50e7, 0x5275, 0x53cc, 0x53e2, 0x5009, 0x55aa, 0x58ee, 0x594f, 0x723d, 0x5b8b, 0x5c64, 0x531d, 0x60e3, 0x60f3, 0x635c, 0x6383, 0x633f, 0x63bb, 0xfffd, 0x64cd, 0x65e9, 0x66f9, 0x5de3, 0x69cd, 0x69fd, 0x6f15, 0x71e5, 0x4e89, 0x75e9, 0x76f8, 0x7a93, 0x7cdf, 0x7dcf, 0x7d9c, 0x8061, 0x8349, 0x8358, 0x846c, 0x84bc, 0x85fb, 0x88c5, 0x8d70, 0x9001, 0x906d, 0x9397, 0x971c, 0x9a12, 0x50cf, 0x5897, 0x618e, 0x81d3, 0x8535, 0x8d08, 0x9020, 0x4fc3, 0x5074, 0x5247, 0x5373, 0x606f, 0x6349, 0x675f, 0x6e2c, 0x8db3, 0x901f, 0x4fd7, 0x5c5e, 0x8cca, 0x65cf, 0x7d9a, 0x5352, 0x8896, 0x5176, 0x63c3, 0x5b58, 0x5b6b, 0x5c0a, 0x640d, 0x6751, 0x905c, 0x4ed6, 0x591a, 0x592a, 0x6c70, 0x8a51, 0x553e, 0x5815, 0x59a5, 0x60f0, 0x6253, 0x67c1, 0x8235, 0x6955, 0x9640, 0x99c4, 0x9a28, 0x4f53, 0x5806, 0x5bfe, 0x8010, 0x5cb1, 0x5e2f, 0x5f85, 0x6020, 0x614b, 0x6234, 0x66ff, 0x6cf0, 0x6ede, 0x80ce, 0x817f, 0x82d4, 0x888b, 0x8cb8, 0x9000, 0x902e, 0x968a, 0x9edb, 0x9bdb, 0x4ee3, 0x53f0, 0x5927, 0x7b2c, 0x918d, 0x984c, 0x9df9, 0x6edd, 0x7027, 0x5353, 0x5544, 0x5b85, 0x6258, 0x629e, 0x62d3, 0x6ca2, 0x6fef, 0x7422, 0x8a17, 0x9438, 0x6fc1, 0x8afe, 0x8338, 0x51e7, 0x86f8, 0x53ea, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x53e9, 0x4f46, 0x9054, 0x8fb0, 0x596a, 0x8131, 0x5dfd, 0x7aea, 0x8fbf, 0x68da, 0x8c37, 0x72f8, 0x9c48, 0x6a3d, 0x8ab0, 0x4e39, 0x5358, 0x5606, 0x5766, 0x62c5, 0x63a2, 0x65e6, 0x6b4e, 0x6de1, 0x6e5b, 0x70ad, 0x77ed, 0x7aef, 0x7baa, 0x7dbb, 0x803d, 0x80c6, 0x86cb, 0x8a95, 0x935b, 0x56e3, 0x58c7, 0x5f3e, 0x65ad, 0x6696, 0x6a80, 0x6bb5, 0x7537, 0x8ac7, 0x5024, 0x77e5, 0x5730, 0x5f1b, 0x6065, 0x667a, 0x6c60, 0x75f4, 0x7a1a, 0x7f6e, 0x81f4, 0x8718, 0x9045, 0x99b3, 0x7bc9, 0x755c, 0x7af9, 0x7b51, 0x84c4, 0xfffd, 0x9010, 0x79e9, 0x7a92, 0x8336, 0x5ae1, 0x7740, 0x4e2d, 0x4ef2, 0x5b99, 0x5fe0, 0x62bd, 0x663c, 0x67f1, 0x6ce8, 0x866b, 0x8877, 0x8a3b, 0x914e, 0x92f3, 0x99d0, 0x6a17, 0x7026, 0x732a, 0x82e7, 0x8457, 0x8caf, 0x4e01, 0x5146, 0x51cb, 0x558b, 0x5bf5, 0x5e16, 0x5e33, 0x5e81, 0x5f14, 0x5f35, 0x5f6b, 0x5fb4, 0x61f2, 0x6311, 0x66a2, 0x671d, 0x6f6e, 0x7252, 0x753a, 0x773a, 0x8074, 0x8139, 0x8178, 0x8776, 0x8abf, 0x8adc, 0x8d85, 0x8df3, 0x929a, 0x9577, 0x9802, 0x9ce5, 0x52c5, 0x6357, 0x76f4, 0x6715, 0x6c88, 0x73cd, 0x8cc3, 0x93ae, 0x9673, 0x6d25, 0x589c, 0x690e, 0x69cc, 0x8ffd, 0x939a, 0x75db, 0x901a, 0x585a, 0x6802, 0x63b4, 0x69fb, 0x4f43, 0x6f2c, 0x67d8, 0x8fbb, 0x8526, 0x7db4, 0x9354, 0x693f, 0x6f70, 0x576a, 0x58f7, 0x5b2c, 0x7d2c, 0x722a, 0x540a, 0x91e3, 0x9db4, 0x4ead, 0x4f4e, 0x505c, 0x5075, 0x5243, 0x8c9e, 0x5448, 0x5824, 0x5b9a, 0x5e1d, 0x5e95, 0x5ead, 0x5ef7, 0x5f1f, 0x608c, 0x62b5, 0x633a, 0x63d0, 0x68af, 0x6c40, 0x7887, 0x798e, 0x7a0b, 0x7de0, 0x8247, 0x8a02, 0x8ae6, 0x8e44, 0x9013, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x90b8, 0x912d, 0x91d8, 0x9f0e, 0x6ce5, 0x6458, 0x64e2, 0x6575, 0x6ef4, 0x7684, 0x7b1b, 0x9069, 0x93d1, 0x6eba, 0x54f2, 0x5fb9, 0x64a4, 0x8f4d, 0x8fed, 0x9244, 0x5178, 0x586b, 0x5929, 0x5c55, 0x5e97, 0x6dfb, 0x7e8f, 0x751c, 0x8cbc, 0x8ee2, 0x985b, 0x70b9, 0x4f1d, 0x6bbf, 0x6fb1, 0x7530, 0x96fb, 0x514e, 0x5410, 0x5835, 0x5857, 0x59ac, 0x5c60, 0x5f92, 0x6597, 0x675c, 0x6e21, 0x767b, 0x83df, 0x8ced, 0x9014, 0x90fd, 0x934d, 0x7825, 0x783a, 0x52aa, 0x5ea6, 0x571f, 0x5974, 0x6012, 0x5012, 0x515a, 0x51ac, 0xfffd, 0x51cd, 0x5200, 0x5510, 0x5854, 0x5858, 0x5957, 0x5b95, 0x5cf6, 0x5d8b, 0x60bc, 0x6295, 0x642d, 0x6771, 0x6843, 0x68bc, 0x68df, 0x76d7, 0x6dd8, 0x6e6f, 0x6d9b, 0x706f, 0x71c8, 0x5f53, 0x75d8, 0x7977, 0x7b49, 0x7b54, 0x7b52, 0x7cd6, 0x7d71, 0x5230, 0x8463, 0x8569, 0x85e4, 0x8a0e, 0x8b04, 0x8c46, 0x8e0f, 0x9003, 0x900f, 0x9419, 0x9676, 0x982d, 0x9a30, 0x95d8, 0x50cd, 0x52d5, 0x540c, 0x5802, 0x5c0e, 0x61a7, 0x649e, 0x6d1e, 0x77b3, 0x7ae5, 0x80f4, 0x8404, 0x9053, 0x9285, 0x5ce0, 0x9d07, 0x533f, 0x5f97, 0x5fb3, 0x6d9c, 0x7279, 0x7763, 0x79bf, 0x7be4, 0x6bd2, 0x72ec, 0x8aad, 0x6803, 0x6a61, 0x51f8, 0x7a81, 0x6934, 0x5c4a, 0x9cf6, 0x82eb, 0x5bc5, 0x9149, 0x701e, 0x5678, 0x5c6f, 0x60c7, 0x6566, 0x6c8c, 0x8c5a, 0x9041, 0x9813, 0x5451, 0x66c7, 0x920d, 0x5948, 0x90a3, 0x5185, 0x4e4d, 0x51ea, 0x8599, 0x8b0e, 0x7058, 0x637a, 0x934b, 0x6962, 0x99b4, 0x7e04, 0x7577, 0x5357, 0x6960, 0x8edf, 0x96e3, 0x6c5d, 0x4e8c, 0x5c3c, 0x5f10, 0x8fe9, 0x5302, 0x8cd1, 0x8089, 0x8679, 0x5eff, 0x65e5, 0x4e73, 0x5165, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x5982, 0x5c3f, 0x97ee, 0x4efb, 0x598a, 0x5fcd, 0x8a8d, 0x6fe1, 0x79b0, 0x7962, 0x5be7, 0x8471, 0x732b, 0x71b1, 0x5e74, 0x5ff5, 0x637b, 0x649a, 0x71c3, 0x7c98, 0x4e43, 0x5efc, 0x4e4b, 0x57dc, 0x56a2, 0x60a9, 0x6fc3, 0x7d0d, 0x80fd, 0x8133, 0x81bf, 0x8fb2, 0x8997, 0x86a4, 0x5df4, 0x628a, 0x64ad, 0x8987, 0x6777, 0x6ce2, 0x6d3e, 0x7436, 0x7834, 0x5a46, 0x7f75, 0x82ad, 0x99ac, 0x4ff3, 0x5ec3, 0x62dd, 0x6392, 0x6557, 0x676f, 0x76c3, 0x724c, 0x80cc, 0x80ba, 0x8f29, 0x914d, 0x500d, 0x57f9, 0x5a92, 0x6885, 0xfffd, 0x6973, 0x7164, 0x72fd, 0x8cb7, 0x58f2, 0x8ce0, 0x966a, 0x9019, 0x877f, 0x79e4, 0x77e7, 0x8429, 0x4f2f, 0x5265, 0x535a, 0x62cd, 0x67cf, 0x6cca, 0x767d, 0x7b94, 0x7c95, 0x8236, 0x8584, 0x8feb, 0x66dd, 0x6f20, 0x7206, 0x7e1b, 0x83ab, 0x99c1, 0x9ea6, 0x51fd, 0x7bb1, 0x7872, 0x7bb8, 0x8087, 0x7b48, 0x6ae8, 0x5e61, 0x808c, 0x7551, 0x7560, 0x516b, 0x9262, 0x6e8c, 0x767a, 0x9197, 0x9aea, 0x4f10, 0x7f70, 0x629c, 0x7b4f, 0x95a5, 0x9ce9, 0x567a, 0x5859, 0x86e4, 0x96bc, 0x4f34, 0x5224, 0x534a, 0x53cd, 0x53db, 0x5e06, 0x642c, 0x6591, 0x677f, 0x6c3e, 0x6c4e, 0x7248, 0x72af, 0x73ed, 0x7554, 0x7e41, 0x822c, 0x85e9, 0x8ca9, 0x7bc4, 0x91c6, 0x7169, 0x9812, 0x98ef, 0x633d, 0x6669, 0x756a, 0x76e4, 0x78d0, 0x8543, 0x86ee, 0x532a, 0x5351, 0x5426, 0x5983, 0x5e87, 0x5f7c, 0x60b2, 0x6249, 0x6279, 0x62ab, 0x6590, 0x6bd4, 0x6ccc, 0x75b2, 0x76ae, 0x7891, 0x79d8, 0x7dcb, 0x7f77, 0x80a5, 0x88ab, 0x8ab9, 0x8cbb, 0x907f, 0x975e, 0x98db, 0x6a0b, 0x7c38, 0x5099, 0x5c3e, 0x5fae, 0x6787, 0x6bd8, 0x7435, 0x7709, 0x7f8e, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9f3b, 0x67ca, 0x7a17, 0x5339, 0x758b, 0x9aed, 0x5f66, 0x819d, 0x83f1, 0x8098, 0x5f3c, 0x5fc5, 0x7562, 0x7b46, 0x903c, 0x6867, 0x59eb, 0x5a9b, 0x7d10, 0x767e, 0x8b2c, 0x4ff5, 0x5f6a, 0x6a19, 0x6c37, 0x6f02, 0x74e2, 0x7968, 0x8868, 0x8a55, 0x8c79, 0x5edf, 0x63cf, 0x75c5, 0x79d2, 0x82d7, 0x9328, 0x92f2, 0x849c, 0x86ed, 0x9c2d, 0x54c1, 0x5f6c, 0x658c, 0x6d5c, 0x7015, 0x8ca7, 0x8cd3, 0x983b, 0x654f, 0x74f6, 0x4e0d, 0x4ed8, 0x57e0, 0x592b, 0x5a66, 0x5bcc, 0x51a8, 0x5e03, 0x5e9c, 0x6016, 0x6276, 0x6577, 0xfffd, 0x65a7, 0x666e, 0x6d6e, 0x7236, 0x7b26, 0x8150, 0x819a, 0x8299, 0x8b5c, 0x8ca0, 0x8ce6, 0x8d74, 0x961c, 0x9644, 0x4fae, 0x64ab, 0x6b66, 0x821e, 0x8461, 0x856a, 0x90e8, 0x5c01, 0x6953, 0x98a8, 0x847a, 0x8557, 0x4f0f, 0x526f, 0x5fa9, 0x5e45, 0x670d, 0x798f, 0x8179, 0x8907, 0x8986, 0x6df5, 0x5f17, 0x6255, 0x6cb8, 0x4ecf, 0x7269, 0x9b92, 0x5206, 0x543b, 0x5674, 0x58b3, 0x61a4, 0x626e, 0x711a, 0x596e, 0x7c89, 0x7cde, 0x7d1b, 0x96f0, 0x6587, 0x805e, 0x4e19, 0x4f75, 0x5175, 0x5840, 0x5e63, 0x5e73, 0x5f0a, 0x67c4, 0x4e26, 0x853d, 0x9589, 0x965b, 0x7c73, 0x9801, 0x50fb, 0x58c1, 0x7656, 0x78a7, 0x5225, 0x77a5, 0x8511, 0x7b86, 0x504f, 0x5909, 0x7247, 0x7bc7, 0x7de8, 0x8fba, 0x8fd4, 0x904d, 0x4fbf, 0x52c9, 0x5a29, 0x5f01, 0x97ad, 0x4fdd, 0x8217, 0x92ea, 0x5703, 0x6355, 0x6b69, 0x752b, 0x88dc, 0x8f14, 0x7a42, 0x52df, 0x5893, 0x6155, 0x620a, 0x66ae, 0x6bcd, 0x7c3f, 0x83e9, 0x5023, 0x4ff8, 0x5305, 0x5446, 0x5831, 0x5949, 0x5b9d, 0x5cf0, 0x5cef, 0x5d29, 0x5e96, 0x62b1, 0x6367, 0x653e, 0x65b9, 0x670b, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x6cd5, 0x6ce1, 0x70f9, 0x7832, 0x7e2b, 0x80de, 0x82b3, 0x840c, 0x84ec, 0x8702, 0x8912, 0x8a2a, 0x8c4a, 0x90a6, 0x92d2, 0x98fd, 0x9cf3, 0x9d6c, 0x4e4f, 0x4ea1, 0x508d, 0x5256, 0x574a, 0x59a8, 0x5e3d, 0x5fd8, 0x5fd9, 0x623f, 0x66b4, 0x671b, 0x67d0, 0x68d2, 0x5192, 0x7d21, 0x80aa, 0x81a8, 0x8b00, 0x8c8c, 0x8cbf, 0x927e, 0x9632, 0x5420, 0x982c, 0x5317, 0x50d5, 0x535c, 0x58a8, 0x64b2, 0x6734, 0x7267, 0x7766, 0x7a46, 0x91e6, 0x52c3, 0x6ca1, 0x6b86, 0x5800, 0x5e4c, 0x5954, 0x672c, 0x7ffb, 0x51e1, 0x76c6, 0xfffd, 0x6469, 0x78e8, 0x9b54, 0x9ebb, 0x57cb, 0x59b9, 0x6627, 0x679a, 0x6bce, 0x54e9, 0x69d9, 0x5e55, 0x819c, 0x6795, 0x9baa, 0x67fe, 0x9c52, 0x685d, 0x4ea6, 0x4fe3, 0x53c8, 0x62b9, 0x672b, 0x6cab, 0x8fc4, 0x4fad, 0x7e6d, 0x9ebf, 0x4e07, 0x6162, 0x6e80, 0x6f2b, 0x8513, 0x5473, 0x672a, 0x9b45, 0x5df3, 0x7b95, 0x5cac, 0x5bc6, 0x871c, 0x6e4a, 0x84d1, 0x7a14, 0x8108, 0x5999, 0x7c8d, 0x6c11, 0x7720, 0x52d9, 0x5922, 0x7121, 0x725f, 0x77db, 0x9727, 0x9d61, 0x690b, 0x5a7f, 0x5a18, 0x51a5, 0x540d, 0x547d, 0x660e, 0x76df, 0x8ff7, 0x9298, 0x9cf4, 0x59ea, 0x725d, 0x6ec5, 0x514d, 0x68c9, 0x7dbf, 0x7dec, 0x9762, 0x9eba, 0x6478, 0x6a21, 0x8302, 0x5984, 0x5b5f, 0x6bdb, 0x731b, 0x76f2, 0x7db2, 0x8017, 0x8499, 0x5132, 0x6728, 0x9ed9, 0x76ee, 0x6762, 0x52ff, 0x9905, 0x5c24, 0x623b, 0x7c7e, 0x8cb0, 0x554f, 0x60b6, 0x7d0b, 0x9580, 0x5301, 0x4e5f, 0x51b6, 0x591c, 0x723a, 0x8036, 0x91ce, 0x5f25, 0x77e2, 0x5384, 0x5f79, 0x7d04, 0x85ac, 0x8a33, 0x8e8d, 0x9756, 0x67f3, 0x85ae, 0x9453, 0x6109, 0x6108, 0x6cb9, 0x7652, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8aed, 0x8f38, 0x552f, 0x4f51, 0x512a, 0x52c7, 0x53cb, 0x5ba5, 0x5e7d, 0x60a0, 0x6182, 0x63d6, 0x6709, 0x67da, 0x6e67, 0x6d8c, 0x7336, 0x7337, 0x7531, 0x7950, 0x88d5, 0x8a98, 0x904a, 0x9091, 0x90f5, 0x96c4, 0x878d, 0x5915, 0x4e88, 0x4f59, 0x4e0e, 0x8a89, 0x8f3f, 0x9810, 0x50ad, 0x5e7c, 0x5996, 0x5bb9, 0x5eb8, 0x63da, 0x63fa, 0x64c1, 0x66dc, 0x694a, 0x69d8, 0x6d0b, 0x6eb6, 0x7194, 0x7528, 0x7aaf, 0x7f8a, 0x8000, 0x8449, 0x84c9, 0x8981, 0x8b21, 0x8e0a, 0x9065, 0x967d, 0x990a, 0x617e, 0x6291, 0x6b32, 0xfffd, 0x6c83, 0x6d74, 0x7fcc, 0x7ffc, 0x6dc0, 0x7f85, 0x87ba, 0x88f8, 0x6765, 0x83b1, 0x983c, 0x96f7, 0x6d1b, 0x7d61, 0x843d, 0x916a, 0x4e71, 0x5375, 0x5d50, 0x6b04, 0x6feb, 0x85cd, 0x862d, 0x89a7, 0x5229, 0x540f, 0x5c65, 0x674e, 0x68a8, 0x7406, 0x7483, 0x75e2, 0x88cf, 0x88e1, 0x91cc, 0x96e2, 0x9678, 0x5f8b, 0x7387, 0x7acb, 0x844e, 0x63a0, 0x7565, 0x5289, 0x6d41, 0x6e9c, 0x7409, 0x7559, 0x786b, 0x7c92, 0x9686, 0x7adc, 0x9f8d, 0x4fb6, 0x616e, 0x65c5, 0x865c, 0x4e86, 0x4eae, 0x50da, 0x4e21, 0x51cc, 0x5bee, 0x6599, 0x6881, 0x6dbc, 0x731f, 0x7642, 0x77ad, 0x7a1c, 0x7ce7, 0x826f, 0x8ad2, 0x907c, 0x91cf, 0x9675, 0x9818, 0x529b, 0x7dd1, 0x502b, 0x5398, 0x6797, 0x6dcb, 0x71d0, 0x7433, 0x81e8, 0x8f2a, 0x96a3, 0x9c57, 0x9e9f, 0x7460, 0x5841, 0x6d99, 0x7d2f, 0x985e, 0x4ee4, 0x4f36, 0x4f8b, 0x51b7, 0x52b1, 0x5dba, 0x601c, 0x73b2, 0x793c, 0x82d3, 0x9234, 0x96b7, 0x96f6, 0x970a, 0x9e97, 0x9f62, 0x66a6, 0x6b74, 0x5217, 0x52a3, 0x70c8, 0x88c2, 0x5ec9, 0x604b, 0x6190, 0x6f23, 0x7149, 0x7c3e, 0x7df4, 0x806f, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x84ee, 0x9023, 0x932c, 0x5442, 0x9b6f, 0x6ad3, 0x7089, 0x8cc2, 0x8def, 0x9732, 0x52b4, 0x5a41, 0x5eca, 0x5f04, 0x6717, 0x697c, 0x6994, 0x6d6a, 0x6f0f, 0x7262, 0x72fc, 0x7bed, 0x8001, 0x807e, 0x874b, 0x90ce, 0x516d, 0x9e93, 0x7984, 0x808b, 0x9332, 0x8ad6, 0x502d, 0x548c, 0x8a71, 0x6b6a, 0x8cc4, 0x8107, 0x60d1, 0x67a0, 0x9df2, 0x4e99, 0x4e98, 0x9c10, 0x8a6b, 0x85c1, 0x8568, 0x6900, 0x6e7e, 0x7897, 0x8155, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x5f0c, 0x4e10, 0x4e15, 0x4e2a, 0x4e31, 0x4e36, 0x4e3c, 0x4e3f, 0x4e42, 0x4e56, 0x4e58, 0x4e82, 0x4e85, 0x8c6b, 0x4e8a, 0x8212, 0x5f0d, 0x4e8e, 0x4e9e, 0x4e9f, 0x4ea0, 0x4ea2, 0x4eb0, 0x4eb3, 0x4eb6, 0x4ece, 0x4ecd, 0x4ec4, 0x4ec6, 0x4ec2, 0x4ed7, 0x4ede, 0x4eed, 0x4edf, 0x4ef7, 0x4f09, 0x4f5a, 0x4f30, 0x4f5b, 0x4f5d, 0x4f57, 0x4f47, 0x4f76, 0x4f88, 0x4f8f, 0x4f98, 0x4f7b, 0x4f69, 0x4f70, 0x4f91, 0x4f6f, 0x4f86, 0x4f96, 0x5118, 0x4fd4, 0x4fdf, 0x4fce, 0x4fd8, 0x4fdb, 0x4fd1, 0x4fda, 0x4fd0, 0x4fe4, 0x4fe5, 0x501a, 0x5028, 0x5014, 0x502a, 0x5025, 0x5005, 0x4f1c, 0x4ff6, 0x5021, 0x5029, 0x502c, 0x4ffe, 0x4fef, 0x5011, 0x5006, 0x5043, 0x5047, 0x6703, 0x5055, 0x5050, 0x5048, 0x505a, 0x5056, 0x506c, 0x5078, 0x5080, 0x509a, 0x5085, 0x50b4, 0x50b2, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x50c9, 0x50ca, 0x50b3, 0x50c2, 0x50d6, 0x50de, 0x50e5, 0x50ed, 0x50e3, 0x50ee, 0x50f9, 0x50f5, 0x5109, 0x5101, 0x5102, 0x5116, 0x5115, 0x5114, 0x511a, 0x5121, 0x513a, 0x5137, 0x513c, 0x513b, 0x513f, 0x5140, 0x5152, 0x514c, 0x5154, 0x5162, 0x7af8, 0x5169, 0x516a, 0x516e, 0x5180, 0x5182, 0x56d8, 0x518c, 0x5189, 0x518f, 0x5191, 0x5193, 0x5195, 0x5196, 0x51a4, 0x51a6, 0x51a2, 0x51a9, 0x51aa, 0x51ab, 0x51b3, 0x51b1, 0x51b2, 0x51b0, 0x51b5, 0x51bd, 0x51c5, 0x51c9, 0x51db, 0x51e0, 0x8655, 0x51e9, 0x51ed, 0xfffd, 0x51f0, 0x51f5, 0x51fe, 0x5204, 0x520b, 0x5214, 0x520e, 0x5227, 0x522a, 0x522e, 0x5233, 0x5239, 0x524f, 0x5244, 0x524b, 0x524c, 0x525e, 0x5254, 0x526a, 0x5274, 0x5269, 0x5273, 0x527f, 0x527d, 0x528d, 0x5294, 0x5292, 0x5271, 0x5288, 0x5291, 0x8fa8, 0x8fa7, 0x52ac, 0x52ad, 0x52bc, 0x52b5, 0x52c1, 0x52cd, 0x52d7, 0x52de, 0x52e3, 0x52e6, 0x98ed, 0x52e0, 0x52f3, 0x52f5, 0x52f8, 0x52f9, 0x5306, 0x5308, 0x7538, 0x530d, 0x5310, 0x530f, 0x5315, 0x531a, 0x5323, 0x532f, 0x5331, 0x5333, 0x5338, 0x5340, 0x5346, 0x5345, 0x4e17, 0x5349, 0x534d, 0x51d6, 0x535e, 0x5369, 0x536e, 0x5918, 0x537b, 0x5377, 0x5382, 0x5396, 0x53a0, 0x53a6, 0x53a5, 0x53ae, 0x53b0, 0x53b6, 0x53c3, 0x7c12, 0x96d9, 0x53df, 0x66fc, 0x71ee, 0x53ee, 0x53e8, 0x53ed, 0x53fa, 0x5401, 0x543d, 0x5440, 0x542c, 0x542d, 0x543c, 0x542e, 0x5436, 0x5429, 0x541d, 0x544e, 0x548f, 0x5475, 0x548e, 0x545f, 0x5471, 0x5477, 0x5470, 0x5492, 0x547b, 0x5480, 0x5476, 0x5484, 0x5490, 0x5486, 0x54c7, 0x54a2, 0x54b8, 0x54a5, 0x54ac, 0x54c4, 0x54c8, 0x54a8, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x54ab, 0x54c2, 0x54a4, 0x54be, 0x54bc, 0x54d8, 0x54e5, 0x54e6, 0x550f, 0x5514, 0x54fd, 0x54ee, 0x54ed, 0x54fa, 0x54e2, 0x5539, 0x5540, 0x5563, 0x554c, 0x552e, 0x555c, 0x5545, 0x5556, 0x5557, 0x5538, 0x5533, 0x555d, 0x5599, 0x5580, 0x54af, 0x558a, 0x559f, 0x557b, 0x557e, 0x5598, 0x559e, 0x55ae, 0x557c, 0x5583, 0x55a9, 0x5587, 0x55a8, 0x55da, 0x55c5, 0x55df, 0x55c4, 0x55dc, 0x55e4, 0x55d4, 0x5614, 0x55f7, 0x5616, 0x55fe, 0x55fd, 0x561b, 0x55f9, 0x564e, 0x5650, 0x71df, 0x5634, 0x5636, 0x5632, 0x5638, 0xfffd, 0x566b, 0x5664, 0x562f, 0x566c, 0x566a, 0x5686, 0x5680, 0x568a, 0x56a0, 0x5694, 0x568f, 0x56a5, 0x56ae, 0x56b6, 0x56b4, 0x56c2, 0x56bc, 0x56c1, 0x56c3, 0x56c0, 0x56c8, 0x56ce, 0x56d1, 0x56d3, 0x56d7, 0x56ee, 0x56f9, 0x5700, 0x56ff, 0x5704, 0x5709, 0x5708, 0x570b, 0x570d, 0x5713, 0x5718, 0x5716, 0x55c7, 0x571c, 0x5726, 0x5737, 0x5738, 0x574e, 0x573b, 0x5740, 0x574f, 0x5769, 0x57c0, 0x5788, 0x5761, 0x577f, 0x5789, 0x5793, 0x57a0, 0x57b3, 0x57a4, 0x57aa, 0x57b0, 0x57c3, 0x57c6, 0x57d4, 0x57d2, 0x57d3, 0x580a, 0x57d6, 0x57e3, 0x580b, 0x5819, 0x581d, 0x5872, 0x5821, 0x5862, 0x584b, 0x5870, 0x6bc0, 0x5852, 0x583d, 0x5879, 0x5885, 0x58b9, 0x589f, 0x58ab, 0x58ba, 0x58de, 0x58bb, 0x58b8, 0x58ae, 0x58c5, 0x58d3, 0x58d1, 0x58d7, 0x58d9, 0x58d8, 0x58e5, 0x58dc, 0x58e4, 0x58df, 0x58ef, 0x58fa, 0x58f9, 0x58fb, 0x58fc, 0x58fd, 0x5902, 0x590a, 0x5910, 0x591b, 0x68a6, 0x5925, 0x592c, 0x592d, 0x5932, 0x5938, 0x593e, 0x7ad2, 0x5955, 0x5950, 0x594e, 0x595a, 0x5958, 0x5962, 0x5960, 0x5967, 0x596c, 0x5969, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x5978, 0x5981, 0x599d, 0x4f5e, 0x4fab, 0x59a3, 0x59b2, 0x59c6, 0x59e8, 0x59dc, 0x598d, 0x59d9, 0x59da, 0x5a25, 0x5a1f, 0x5a11, 0x5a1c, 0x5a09, 0x5a1a, 0x5a40, 0x5a6c, 0x5a49, 0x5a35, 0x5a36, 0x5a62, 0x5a6a, 0x5a9a, 0x5abc, 0x5abe, 0x5acb, 0x5ac2, 0x5abd, 0x5ae3, 0x5ad7, 0x5ae6, 0x5ae9, 0x5ad6, 0x5afa, 0x5afb, 0x5b0c, 0x5b0b, 0x5b16, 0x5b32, 0x5ad0, 0x5b2a, 0x5b36, 0x5b3e, 0x5b43, 0x5b45, 0x5b40, 0x5b51, 0x5b55, 0x5b5a, 0x5b5b, 0x5b65, 0x5b69, 0x5b70, 0x5b73, 0x5b75, 0x5b78, 0x6588, 0x5b7a, 0x5b80, 0xfffd, 0x5b83, 0x5ba6, 0x5bb8, 0x5bc3, 0x5bc7, 0x5bc9, 0x5bd4, 0x5bd0, 0x5be4, 0x5be6, 0x5be2, 0x5bde, 0x5be5, 0x5beb, 0x5bf0, 0x5bf6, 0x5bf3, 0x5c05, 0x5c07, 0x5c08, 0x5c0d, 0x5c13, 0x5c20, 0x5c22, 0x5c28, 0x5c38, 0x5c39, 0x5c41, 0x5c46, 0x5c4e, 0x5c53, 0x5c50, 0x5c4f, 0x5b71, 0x5c6c, 0x5c6e, 0x4e62, 0x5c76, 0x5c79, 0x5c8c, 0x5c91, 0x5c94, 0x599b, 0x5cab, 0x5cbb, 0x5cb6, 0x5cbc, 0x5cb7, 0x5cc5, 0x5cbe, 0x5cc7, 0x5cd9, 0x5ce9, 0x5cfd, 0x5cfa, 0x5ced, 0x5d8c, 0x5cea, 0x5d0b, 0x5d15, 0x5d17, 0x5d5c, 0x5d1f, 0x5d1b, 0x5d11, 0x5d14, 0x5d22, 0x5d1a, 0x5d19, 0x5d18, 0x5d4c, 0x5d52, 0x5d4e, 0x5d4b, 0x5d6c, 0x5d73, 0x5d76, 0x5d87, 0x5d84, 0x5d82, 0x5da2, 0x5d9d, 0x5dac, 0x5dae, 0x5dbd, 0x5d90, 0x5db7, 0x5dbc, 0x5dc9, 0x5dcd, 0x5dd3, 0x5dd2, 0x5dd6, 0x5ddb, 0x5deb, 0x5df2, 0x5df5, 0x5e0b, 0x5e1a, 0x5e19, 0x5e11, 0x5e1b, 0x5e36, 0x5e37, 0x5e44, 0x5e43, 0x5e40, 0x5e4e, 0x5e57, 0x5e54, 0x5e5f, 0x5e62, 0x5e64, 0x5e47, 0x5e75, 0x5e76, 0x5e7a, 0x9ebc, 0x5e7f, 0x5ea0, 0x5ec1, 0x5ec2, 0x5ec8, 0x5ed0, 0x5ecf, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x5ed6, 0x5ee3, 0x5edd, 0x5eda, 0x5edb, 0x5ee2, 0x5ee1, 0x5ee8, 0x5ee9, 0x5eec, 0x5ef1, 0x5ef3, 0x5ef0, 0x5ef4, 0x5ef8, 0x5efe, 0x5f03, 0x5f09, 0x5f5d, 0x5f5c, 0x5f0b, 0x5f11, 0x5f16, 0x5f29, 0x5f2d, 0x5f38, 0x5f41, 0x5f48, 0x5f4c, 0x5f4e, 0x5f2f, 0x5f51, 0x5f56, 0x5f57, 0x5f59, 0x5f61, 0x5f6d, 0x5f73, 0x5f77, 0x5f83, 0x5f82, 0x5f7f, 0x5f8a, 0x5f88, 0x5f91, 0x5f87, 0x5f9e, 0x5f99, 0x5f98, 0x5fa0, 0x5fa8, 0x5fad, 0x5fbc, 0x5fd6, 0x5ffb, 0x5fe4, 0x5ff8, 0x5ff1, 0x5fdd, 0x60b3, 0x5fff, 0x6021, 0x6060, 0xfffd, 0x6019, 0x6010, 0x6029, 0x600e, 0x6031, 0x601b, 0x6015, 0x602b, 0x6026, 0x600f, 0x603a, 0x605a, 0x6041, 0x606a, 0x6077, 0x605f, 0x604a, 0x6046, 0x604d, 0x6063, 0x6043, 0x6064, 0x6042, 0x606c, 0x606b, 0x6059, 0x6081, 0x608d, 0x60e7, 0x6083, 0x609a, 0x6084, 0x609b, 0x6096, 0x6097, 0x6092, 0x60a7, 0x608b, 0x60e1, 0x60b8, 0x60e0, 0x60d3, 0x60b4, 0x5ff0, 0x60bd, 0x60c6, 0x60b5, 0x60d8, 0x614d, 0x6115, 0x6106, 0x60f6, 0x60f7, 0x6100, 0x60f4, 0x60fa, 0x6103, 0x6121, 0x60fb, 0x60f1, 0x610d, 0x610e, 0x6147, 0x613e, 0x6128, 0x6127, 0x614a, 0x613f, 0x613c, 0x612c, 0x6134, 0x613d, 0x6142, 0x6144, 0x6173, 0x6177, 0x6158, 0x6159, 0x615a, 0x616b, 0x6174, 0x616f, 0x6165, 0x6171, 0x615f, 0x615d, 0x6153, 0x6175, 0x6199, 0x6196, 0x6187, 0x61ac, 0x6194, 0x619a, 0x618a, 0x6191, 0x61ab, 0x61ae, 0x61cc, 0x61ca, 0x61c9, 0x61f7, 0x61c8, 0x61c3, 0x61c6, 0x61ba, 0x61cb, 0x7f79, 0x61cd, 0x61e6, 0x61e3, 0x61f6, 0x61fa, 0x61f4, 0x61ff, 0x61fd, 0x61fc, 0x61fe, 0x6200, 0x6208, 0x6209, 0x620d, 0x620c, 0x6214, 0x621b, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x621e, 0x6221, 0x622a, 0x622e, 0x6230, 0x6232, 0x6233, 0x6241, 0x624e, 0x625e, 0x6263, 0x625b, 0x6260, 0x6268, 0x627c, 0x6282, 0x6289, 0x627e, 0x6292, 0x6293, 0x6296, 0x62d4, 0x6283, 0x6294, 0x62d7, 0x62d1, 0x62bb, 0x62cf, 0x62ff, 0x62c6, 0x64d4, 0x62c8, 0x62dc, 0x62cc, 0x62ca, 0x62c2, 0x62c7, 0x629b, 0x62c9, 0x630c, 0x62ee, 0x62f1, 0x6327, 0x6302, 0x6308, 0x62ef, 0x62f5, 0x6350, 0x633e, 0x634d, 0x641c, 0x634f, 0x6396, 0x638e, 0x6380, 0x63ab, 0x6376, 0x63a3, 0x638f, 0x6389, 0x639f, 0x63b5, 0x636b, 0xfffd, 0x6369, 0x63be, 0x63e9, 0x63c0, 0x63c6, 0x63e3, 0x63c9, 0x63d2, 0x63f6, 0x63c4, 0x6416, 0x6434, 0x6406, 0x6413, 0x6426, 0x6436, 0x651d, 0x6417, 0x6428, 0x640f, 0x6467, 0x646f, 0x6476, 0x644e, 0x652a, 0x6495, 0x6493, 0x64a5, 0x64a9, 0x6488, 0x64bc, 0x64da, 0x64d2, 0x64c5, 0x64c7, 0x64bb, 0x64d8, 0x64c2, 0x64f1, 0x64e7, 0x8209, 0x64e0, 0x64e1, 0x62ac, 0x64e3, 0x64ef, 0x652c, 0x64f6, 0x64f4, 0x64f2, 0x64fa, 0x6500, 0x64fd, 0x6518, 0x651c, 0x6505, 0x6524, 0x6523, 0x652b, 0x6534, 0x6535, 0x6537, 0x6536, 0x6538, 0x754b, 0x6548, 0x6556, 0x6555, 0x654d, 0x6558, 0x655e, 0x655d, 0x6572, 0x6578, 0x6582, 0x6583, 0x8b8a, 0x659b, 0x659f, 0x65ab, 0x65b7, 0x65c3, 0x65c6, 0x65c1, 0x65c4, 0x65cc, 0x65d2, 0x65db, 0x65d9, 0x65e0, 0x65e1, 0x65f1, 0x6772, 0x660a, 0x6603, 0x65fb, 0x6773, 0x6635, 0x6636, 0x6634, 0x661c, 0x664f, 0x6644, 0x6649, 0x6641, 0x665e, 0x665d, 0x6664, 0x6667, 0x6668, 0x665f, 0x6662, 0x6670, 0x6683, 0x6688, 0x668e, 0x6689, 0x6684, 0x6698, 0x669d, 0x66c1, 0x66b9, 0x66c9, 0x66be, 0x66bc, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x66c4, 0x66b8, 0x66d6, 0x66da, 0x66e0, 0x663f, 0x66e6, 0x66e9, 0x66f0, 0x66f5, 0x66f7, 0x670f, 0x6716, 0x671e, 0x6726, 0x6727, 0x9738, 0x672e, 0x673f, 0x6736, 0x6741, 0x6738, 0x6737, 0x6746, 0x675e, 0x6760, 0x6759, 0x6763, 0x6764, 0x6789, 0x6770, 0x67a9, 0x677c, 0x676a, 0x678c, 0x678b, 0x67a6, 0x67a1, 0x6785, 0x67b7, 0x67ef, 0x67b4, 0x67ec, 0x67b3, 0x67e9, 0x67b8, 0x67e4, 0x67de, 0x67dd, 0x67e2, 0x67ee, 0x67b9, 0x67ce, 0x67c6, 0x67e7, 0x6a9c, 0x681e, 0x6846, 0x6829, 0x6840, 0x684d, 0x6832, 0x684e, 0xfffd, 0x68b3, 0x682b, 0x6859, 0x6863, 0x6877, 0x687f, 0x689f, 0x688f, 0x68ad, 0x6894, 0x689d, 0x689b, 0x6883, 0x6aae, 0x68b9, 0x6874, 0x68b5, 0x68a0, 0x68ba, 0x690f, 0x688d, 0x687e, 0x6901, 0x68ca, 0x6908, 0x68d8, 0x6922, 0x6926, 0x68e1, 0x690c, 0x68cd, 0x68d4, 0x68e7, 0x68d5, 0x6936, 0x6912, 0x6904, 0x68d7, 0x68e3, 0x6925, 0x68f9, 0x68e0, 0x68ef, 0x6928, 0x692a, 0x691a, 0x6923, 0x6921, 0x68c6, 0x6979, 0x6977, 0x695c, 0x6978, 0x696b, 0x6954, 0x697e, 0x696e, 0x6939, 0x6974, 0x693d, 0x6959, 0x6930, 0x6961, 0x695e, 0x695d, 0x6981, 0x696a, 0x69b2, 0x69ae, 0x69d0, 0x69bf, 0x69c1, 0x69d3, 0x69be, 0x69ce, 0x5be8, 0x69ca, 0x69dd, 0x69bb, 0x69c3, 0x69a7, 0x6a2e, 0x6991, 0x69a0, 0x699c, 0x6995, 0x69b4, 0x69de, 0x69e8, 0x6a02, 0x6a1b, 0x69ff, 0x6b0a, 0x69f9, 0x69f2, 0x69e7, 0x6a05, 0x69b1, 0x6a1e, 0x69ed, 0x6a14, 0x69eb, 0x6a0a, 0x6a12, 0x6ac1, 0x6a23, 0x6a13, 0x6a44, 0x6a0c, 0x6a72, 0x6a36, 0x6a78, 0x6a47, 0x6a62, 0x6a59, 0x6a66, 0x6a48, 0x6a38, 0x6a22, 0x6a90, 0x6a8d, 0x6aa0, 0x6a84, 0x6aa2, 0x6aa3, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x6a97, 0x8617, 0x6abb, 0x6ac3, 0x6ac2, 0x6ab8, 0x6ab3, 0x6aac, 0x6ade, 0x6ad1, 0x6adf, 0x6aaa, 0x6ada, 0x6aea, 0x6afb, 0x6b05, 0x8616, 0x6afa, 0x6b12, 0x6b16, 0x9b31, 0x6b1f, 0x6b38, 0x6b37, 0x76dc, 0x6b39, 0x98ee, 0x6b47, 0x6b43, 0x6b49, 0x6b50, 0x6b59, 0x6b54, 0x6b5b, 0x6b5f, 0x6b61, 0x6b78, 0x6b79, 0x6b7f, 0x6b80, 0x6b84, 0x6b83, 0x6b8d, 0x6b98, 0x6b95, 0x6b9e, 0x6ba4, 0x6baa, 0x6bab, 0x6baf, 0x6bb2, 0x6bb1, 0x6bb3, 0x6bb7, 0x6bbc, 0x6bc6, 0x6bcb, 0x6bd3, 0x6bdf, 0x6bec, 0x6beb, 0x6bf3, 0x6bef, 0xfffd, 0x9ebe, 0x6c08, 0x6c13, 0x6c14, 0x6c1b, 0x6c24, 0x6c23, 0x6c5e, 0x6c55, 0x6c62, 0x6c6a, 0x6c82, 0x6c8d, 0x6c9a, 0x6c81, 0x6c9b, 0x6c7e, 0x6c68, 0x6c73, 0x6c92, 0x6c90, 0x6cc4, 0x6cf1, 0x6cd3, 0x6cbd, 0x6cd7, 0x6cc5, 0x6cdd, 0x6cae, 0x6cb1, 0x6cbe, 0x6cba, 0x6cdb, 0x6cef, 0x6cd9, 0x6cea, 0x6d1f, 0x884d, 0x6d36, 0x6d2b, 0x6d3d, 0x6d38, 0x6d19, 0x6d35, 0x6d33, 0x6d12, 0x6d0c, 0x6d63, 0x6d93, 0x6d64, 0x6d5a, 0x6d79, 0x6d59, 0x6d8e, 0x6d95, 0x6fe4, 0x6d85, 0x6df9, 0x6e15, 0x6e0a, 0x6db5, 0x6dc7, 0x6de6, 0x6db8, 0x6dc6, 0x6dec, 0x6dde, 0x6dcc, 0x6de8, 0x6dd2, 0x6dc5, 0x6dfa, 0x6dd9, 0x6de4, 0x6dd5, 0x6dea, 0x6dee, 0x6e2d, 0x6e6e, 0x6e2e, 0x6e19, 0x6e72, 0x6e5f, 0x6e3e, 0x6e23, 0x6e6b, 0x6e2b, 0x6e76, 0x6e4d, 0x6e1f, 0x6e43, 0x6e3a, 0x6e4e, 0x6e24, 0x6eff, 0x6e1d, 0x6e38, 0x6e82, 0x6eaa, 0x6e98, 0x6ec9, 0x6eb7, 0x6ed3, 0x6ebd, 0x6eaf, 0x6ec4, 0x6eb2, 0x6ed4, 0x6ed5, 0x6e8f, 0x6ea5, 0x6ec2, 0x6e9f, 0x6f41, 0x6f11, 0x704c, 0x6eec, 0x6ef8, 0x6efe, 0x6f3f, 0x6ef2, 0x6f31, 0x6eef, 0x6f32, 0x6ecc }; static USHORT /* Shift-JIS to Unicode */ sju_e040[] = { /* 0xe040 thru 0xeaa4 */ 0x6f3e, 0x6f13, 0x6ef7, 0x6f86, 0x6f7a, 0x6f78, 0x6f81, 0x6f80, 0x6f6f, 0x6f5b, 0x6ff3, 0x6f6d, 0x6f82, 0x6f7c, 0x6f58, 0x6f8e, 0x6f91, 0x6fc2, 0x6f66, 0x6fb3, 0x6fa3, 0x6fa1, 0x6fa4, 0x6fb9, 0x6fc6, 0x6faa, 0x6fdf, 0x6fd5, 0x6fec, 0x6fd4, 0x6fd8, 0x6ff1, 0x6fee, 0x6fdb, 0x7009, 0x700b, 0x6ffa, 0x7011, 0x7001, 0x700f, 0x6ffe, 0x701b, 0x701a, 0x6f74, 0x701d, 0x7018, 0x701f, 0x7030, 0x703e, 0x7032, 0x7051, 0x7063, 0x7099, 0x7092, 0x70af, 0x70f1, 0x70ac, 0x70b8, 0x70b3, 0x70ae, 0x70df, 0x70cb, 0x70dd, 0xfffd, 0x70d9, 0x7109, 0x70fd, 0x711c, 0x7119, 0x7165, 0x7155, 0x7188, 0x7166, 0x7162, 0x714c, 0x7156, 0x716c, 0x718f, 0x71fb, 0x7184, 0x7195, 0x71a8, 0x71ac, 0x71d7, 0x71b9, 0x71be, 0x71d2, 0x71c9, 0x71d4, 0x71ce, 0x71e0, 0x71ec, 0x71e7, 0x71f5, 0x71fc, 0x71f9, 0x71ff, 0x720d, 0x7210, 0x721b, 0x7228, 0x722d, 0x722c, 0x7230, 0x7232, 0x723b, 0x723c, 0x723f, 0x7240, 0x7246, 0x724b, 0x7258, 0x7274, 0x727e, 0x7282, 0x7281, 0x7287, 0x7292, 0x7296, 0x72a2, 0x72a7, 0x72b9, 0x72b2, 0x72c3, 0x72c6, 0x72c4, 0x72ce, 0x72d2, 0x72e2, 0x72e0, 0x72e1, 0x72f9, 0x72f7, 0x500f, 0x7317, 0x730a, 0x731c, 0x7316, 0x731d, 0x7334, 0x732f, 0x7329, 0x7325, 0x733e, 0x734e, 0x734f, 0x9ed8, 0x7357, 0x736a, 0x7368, 0x7370, 0x7378, 0x7375, 0x737b, 0x737a, 0x73c8, 0x73b3, 0x73ce, 0x73bb, 0x73c0, 0x73e5, 0x73ee, 0x73de, 0x74a2, 0x7405, 0x746f, 0x7425, 0x73f8, 0x7432, 0x743a, 0x7455, 0x743f, 0x745f, 0x7459, 0x7441, 0x745c, 0x7469, 0x7470, 0x7463, 0x746a, 0x7476, 0x747e, 0x748b, 0x749e, 0x74a7, 0x74ca, 0x74cf, 0x74d4, 0x73f1, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x74e0, 0x74e3, 0x74e7, 0x74e9, 0x74ee, 0x74f2, 0x74f0, 0x74f1, 0x74f8, 0x74f7, 0x7504, 0x7503, 0x7505, 0x750c, 0x750e, 0x750d, 0x7515, 0x7513, 0x751e, 0x7526, 0x752c, 0x753c, 0x7544, 0x754d, 0x754a, 0x7549, 0x755b, 0x7546, 0x755a, 0x7569, 0x7564, 0x7567, 0x756b, 0x756d, 0x7578, 0x7576, 0x7586, 0x7587, 0x7574, 0x758a, 0x7589, 0x7582, 0x7594, 0x759a, 0x759d, 0x75a5, 0x75a3, 0x75c2, 0x75b3, 0x75c3, 0x75b5, 0x75bd, 0x75b8, 0x75bc, 0x75b1, 0x75cd, 0x75ca, 0x75d2, 0x75d9, 0x75e3, 0x75de, 0x75fe, 0x75ff, 0xfffd, 0x75fc, 0x7601, 0x75f0, 0x75fa, 0x75f2, 0x75f3, 0x760b, 0x760d, 0x7609, 0x761f, 0x7627, 0x7620, 0x7621, 0x7622, 0x7624, 0x7634, 0x7630, 0x763b, 0x7647, 0x7648, 0x7646, 0x765c, 0x7658, 0x7661, 0x7662, 0x7668, 0x7669, 0x766a, 0x7667, 0x766c, 0x7670, 0x7672, 0x7676, 0x7678, 0x767c, 0x7680, 0x7683, 0x7688, 0x768b, 0x768e, 0x7696, 0x7693, 0x7699, 0x769a, 0x76b0, 0x76b4, 0x76b8, 0x76b9, 0x76ba, 0x76c2, 0x76cd, 0x76d6, 0x76d2, 0x76de, 0x76e1, 0x76e5, 0x76e7, 0x76ea, 0x862f, 0x76fb, 0x7708, 0x7707, 0x7704, 0x7729, 0x7724, 0x771e, 0x7725, 0x7726, 0x771b, 0x7737, 0x7738, 0x7747, 0x775a, 0x7768, 0x776b, 0x775b, 0x7765, 0x777f, 0x777e, 0x7779, 0x778e, 0x778b, 0x7791, 0x77a0, 0x779e, 0x77b0, 0x77b6, 0x77b9, 0x77bf, 0x77bc, 0x77bd, 0x77bb, 0x77c7, 0x77cd, 0x77d7, 0x77da, 0x77dc, 0x77e3, 0x77ee, 0x77fc, 0x780c, 0x7812, 0x7926, 0x7820, 0x792a, 0x7845, 0x788e, 0x7874, 0x7886, 0x787c, 0x789a, 0x788c, 0x78a3, 0x78b5, 0x78aa, 0x78af, 0x78d1, 0x78c6, 0x78cb, 0x78d4, 0x78be, 0x78bc, 0x78c5, 0x78ca, 0x78ec, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x78e7, 0x78da, 0x78fd, 0x78f4, 0x7907, 0x7912, 0x7911, 0x7919, 0x792c, 0x792b, 0x7940, 0x7960, 0x7957, 0x795f, 0x795a, 0x7955, 0x7953, 0x797a, 0x797f, 0x798a, 0x799d, 0x79a7, 0x9f4b, 0x79aa, 0x79ae, 0x79b3, 0x79b9, 0x79ba, 0x79c9, 0x79d5, 0x79e7, 0x79ec, 0x79e1, 0x79e3, 0x7a08, 0x7a0d, 0x7a18, 0x7a19, 0x7a20, 0x7a1f, 0x7980, 0x7a31, 0x7a3b, 0x7a3e, 0x7a37, 0x7a43, 0x7a57, 0x7a49, 0x7a61, 0x7a62, 0x7a69, 0x9f9d, 0x7a70, 0x7a79, 0x7a7d, 0x7a88, 0x7a97, 0x7a95, 0x7a98, 0x7a96, 0x7aa9, 0x7ac8, 0x7ab0, 0xfffd, 0x7ab6, 0x7ac5, 0x7ac4, 0x7abf, 0x9083, 0x7ac7, 0x7aca, 0x7acd, 0x7acf, 0x7ad5, 0x7ad3, 0x7ad9, 0x7ada, 0x7add, 0x7ae1, 0x7ae2, 0x7ae6, 0x7aed, 0x7af0, 0x7b02, 0x7b0f, 0x7b0a, 0x7b06, 0x7b33, 0x7b18, 0x7b19, 0x7b1e, 0x7b35, 0x7b28, 0x7b36, 0x7b50, 0x7b7a, 0x7b04, 0x7b4d, 0x7b0b, 0x7b4c, 0x7b45, 0x7b75, 0x7b65, 0x7b74, 0x7b67, 0x7b70, 0x7b71, 0x7b6c, 0x7b6e, 0x7b9d, 0x7b98, 0x7b9f, 0x7b8d, 0x7b9c, 0x7b9a, 0x7b8b, 0x7b92, 0x7b8f, 0x7b5d, 0x7b99, 0x7bcb, 0x7bc1, 0x7bcc, 0x7bcf, 0x7bb4, 0x7bc6, 0x7bdd, 0x7be9, 0x7c11, 0x7c14, 0x7be6, 0x7be5, 0x7c60, 0x7c00, 0x7c07, 0x7c13, 0x7bf3, 0x7bf7, 0x7c17, 0x7c0d, 0x7bf6, 0x7c23, 0x7c27, 0x7c2a, 0x7c1f, 0x7c37, 0x7c2b, 0x7c3d, 0x7c4c, 0x7c43, 0x7c54, 0x7c4f, 0x7c40, 0x7c50, 0x7c58, 0x7c5f, 0x7c64, 0x7c56, 0x7c65, 0x7c6c, 0x7c75, 0x7c83, 0x7c90, 0x7ca4, 0x7cad, 0x7ca2, 0x7cab, 0x7ca1, 0x7ca8, 0x7cb3, 0x7cb2, 0x7cb1, 0x7cae, 0x7cb9, 0x7cbd, 0x7cc0, 0x7cc5, 0x7cc2, 0x7cd8, 0x7cd2, 0x7cdc, 0x7ce2, 0x9b3b, 0x7cef, 0x7cf2, 0x7cf4, 0x7cf6, 0x7cfa, 0x7d06, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x7d02, 0x7d1c, 0x7d15, 0x7d0a, 0x7d45, 0x7d4b, 0x7d2e, 0x7d32, 0x7d3f, 0x7d35, 0x7d46, 0x7d73, 0x7d56, 0x7d4e, 0x7d72, 0x7d68, 0x7d6e, 0x7d4f, 0x7d63, 0x7d93, 0x7d89, 0x7d5b, 0x7d8f, 0x7d7d, 0x7d9b, 0x7dba, 0x7dae, 0x7da3, 0x7db5, 0x7dc7, 0x7dbd, 0x7dab, 0x7e3d, 0x7da2, 0x7daf, 0x7ddc, 0x7db8, 0x7d9f, 0x7db0, 0x7dd8, 0x7ddd, 0x7de4, 0x7dde, 0x7dfb, 0x7df2, 0x7de1, 0x7e05, 0x7e0a, 0x7e23, 0x7e21, 0x7e12, 0x7e31, 0x7e1f, 0x7e09, 0x7e0b, 0x7e22, 0x7e46, 0x7e66, 0x7e3b, 0x7e35, 0x7e39, 0x7e43, 0x7e37, 0xfffd, 0x7e32, 0x7e3a, 0x7e67, 0x7e5d, 0x7e56, 0x7e5e, 0x7e59, 0x7e5a, 0x7e79, 0x7e6a, 0x7e69, 0x7e7c, 0x7e7b, 0x7e83, 0x7dd5, 0x7e7d, 0x8fae, 0x7e7f, 0x7e88, 0x7e89, 0x7e8c, 0x7e92, 0x7e90, 0x7e93, 0x7e94, 0x7e96, 0x7e8e, 0x7e9b, 0x7e9c, 0x7f38, 0x7f3a, 0x7f45, 0x7f4c, 0x7f4d, 0x7f4e, 0x7f50, 0x7f51, 0x7f55, 0x7f54, 0x7f58, 0x7f5f, 0x7f60, 0x7f68, 0x7f69, 0x7f67, 0x7f78, 0x7f82, 0x7f86, 0x7f83, 0x7f88, 0x7f87, 0x7f8c, 0x7f94, 0x7f9e, 0x7f9d, 0x7f9a, 0x7fa3, 0x7faf, 0x7fb2, 0x7fb9, 0x7fae, 0x7fb6, 0x7fb8, 0x8b71, 0x7fc5, 0x7fc6, 0x7fca, 0x7fd5, 0x7fd4, 0x7fe1, 0x7fe6, 0x7fe9, 0x7ff3, 0x7ff9, 0x98dc, 0x8006, 0x8004, 0x800b, 0x8012, 0x8018, 0x8019, 0x801c, 0x8021, 0x8028, 0x803f, 0x803b, 0x804a, 0x8046, 0x8052, 0x8058, 0x805a, 0x805f, 0x8062, 0x8068, 0x8073, 0x8072, 0x8070, 0x8076, 0x8079, 0x807d, 0x807f, 0x8084, 0x8086, 0x8085, 0x809b, 0x8093, 0x809a, 0x80ad, 0x5190, 0x80ac, 0x80db, 0x80e5, 0x80d9, 0x80dd, 0x80c4, 0x80da, 0x80d6, 0x8109, 0x80ef, 0x80f1, 0x811b, 0x8129, 0x8123, 0x812f, 0x814b, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x968b, 0x8146, 0x813e, 0x8153, 0x8151, 0x80fc, 0x8171, 0x816e, 0x8165, 0x8166, 0x8174, 0x8183, 0x8188, 0x818a, 0x8180, 0x8182, 0x81a0, 0x8195, 0x81a4, 0x81a3, 0x815f, 0x8193, 0x81a9, 0x81b0, 0x81b5, 0x81be, 0x81b8, 0x81bd, 0x81c0, 0x81c2, 0x81ba, 0x81c9, 0x81cd, 0x81d1, 0x81d9, 0x81d8, 0x81c8, 0x81da, 0x81df, 0x81e0, 0x81e7, 0x81fa, 0x81fb, 0x81fe, 0x8201, 0x8202, 0x8205, 0x8207, 0x820a, 0x820d, 0x8210, 0x8216, 0x8229, 0x822b, 0x8238, 0x8233, 0x8240, 0x8259, 0x8258, 0x825d, 0x825a, 0x825f, 0x8264, 0xfffd, 0x8262, 0x8268, 0x826a, 0x826b, 0x822e, 0x8271, 0x8277, 0x8278, 0x827e, 0x828d, 0x8292, 0x82ab, 0x829f, 0x82bb, 0x82ac, 0x82e1, 0x82e3, 0x82df, 0x82d2, 0x82f4, 0x82f3, 0x82fa, 0x8393, 0x8303, 0x82fb, 0x82f9, 0x82de, 0x8306, 0x82dc, 0x8309, 0x82d9, 0x8335, 0x8334, 0x8316, 0x8332, 0x8331, 0x8340, 0x8339, 0x8350, 0x8345, 0x832f, 0x832b, 0x8317, 0x8318, 0x8385, 0x839a, 0x83aa, 0x839f, 0x83a2, 0x8396, 0x8323, 0x838e, 0x8387, 0x838a, 0x837c, 0x83b5, 0x8373, 0x8375, 0x83a0, 0x8389, 0x83a8, 0x83f4, 0x8413, 0x83eb, 0x83ce, 0x83fd, 0x8403, 0x83d8, 0x840b, 0x83c1, 0x83f7, 0x8407, 0x83e0, 0x83f2, 0x840d, 0x8422, 0x8420, 0x83bd, 0x8438, 0x8506, 0x83fb, 0x846d, 0x842a, 0x843c, 0x855a, 0x8484, 0x8477, 0x846b, 0x84ad, 0x846e, 0x8482, 0x8469, 0x8446, 0x842c, 0x846f, 0x8479, 0x8435, 0x84ca, 0x8462, 0x84b9, 0x84bf, 0x849f, 0x84d9, 0x84cd, 0x84bb, 0x84da, 0x84d0, 0x84c1, 0x84c6, 0x84d6, 0x84a1, 0x8521, 0x84ff, 0x84f4, 0x8517, 0x8518, 0x852c, 0x851f, 0x8515, 0x8514, 0x84fc, 0x8540, 0x8563, 0x8558, 0x8548, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8541, 0x8602, 0x854b, 0x8555, 0x8580, 0x85a4, 0x8588, 0x8591, 0x858a, 0x85a8, 0x856d, 0x8594, 0x859b, 0x85ea, 0x8587, 0x859c, 0x8577, 0x857e, 0x8590, 0x85c9, 0x85ba, 0x85cf, 0x85b9, 0x85d0, 0x85d5, 0x85dd, 0x85e5, 0x85dc, 0x85f9, 0x860a, 0x8613, 0x860b, 0x85fe, 0x85fa, 0x8606, 0x8622, 0x861a, 0x8630, 0x863f, 0x864d, 0x4e55, 0x8654, 0x865f, 0x8667, 0x8671, 0x8693, 0x86a3, 0x86a9, 0x86aa, 0x868b, 0x868c, 0x86b6, 0x86af, 0x86c4, 0x86c6, 0x86b0, 0x86c9, 0x8823, 0x86ab, 0x86d4, 0x86de, 0x86e9, 0x86ec, 0xfffd, 0x86df, 0x86db, 0x86ef, 0x8712, 0x8706, 0x8708, 0x8700, 0x8703, 0x86fb, 0x8711, 0x8709, 0x870d, 0x86f9, 0x870a, 0x8734, 0x873f, 0x8737, 0x873b, 0x8725, 0x8729, 0x871a, 0x8760, 0x875f, 0x8778, 0x874c, 0x874e, 0x8774, 0x8757, 0x8768, 0x876e, 0x8759, 0x8753, 0x8763, 0x876a, 0x8805, 0x87a2, 0x879f, 0x8782, 0x87af, 0x87cb, 0x87bd, 0x87c0, 0x87d0, 0x96d6, 0x87ab, 0x87c4, 0x87b3, 0x87c7, 0x87c6, 0x87bb, 0x87ef, 0x87f2, 0x87e0, 0x880f, 0x880d, 0x87fe, 0x87f6, 0x87f7, 0x880e, 0x87d2, 0x8811, 0x8816, 0x8815, 0x8822, 0x8821, 0x8831, 0x8836, 0x8839, 0x8827, 0x883b, 0x8844, 0x8842, 0x8852, 0x8859, 0x885e, 0x8862, 0x886b, 0x8881, 0x887e, 0x889e, 0x8875, 0x887d, 0x88b5, 0x8872, 0x8882, 0x8897, 0x8892, 0x88ae, 0x8899, 0x88a2, 0x888d, 0x88a4, 0x88b0, 0x88bf, 0x88b1, 0x88c3, 0x88c4, 0x88d4, 0x88d8, 0x88d9, 0x88dd, 0x88f9, 0x8902, 0x88fc, 0x88f4, 0x88e8, 0x88f2, 0x8904, 0x890c, 0x890a, 0x8913, 0x8943, 0x891e, 0x8925, 0x892a, 0x892b, 0x8941, 0x8944, 0x893b, 0x8936, 0x8938, 0x894c, 0x891d, 0x8960, 0x895e, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8966, 0x8964, 0x896d, 0x896a, 0x896f, 0x8974, 0x8977, 0x897e, 0x8983, 0x8988, 0x898a, 0x8993, 0x8998, 0x89a1, 0x89a9, 0x89a6, 0x89ac, 0x89af, 0x89b2, 0x89ba, 0x89bd, 0x89bf, 0x89c0, 0x89da, 0x89dc, 0x89dd, 0x89e7, 0x89f4, 0x89f8, 0x8a03, 0x8a16, 0x8a10, 0x8a0c, 0x8a1b, 0x8a1d, 0x8a25, 0x8a36, 0x8a41, 0x8a5b, 0x8a52, 0x8a46, 0x8a48, 0x8a7c, 0x8a6d, 0x8a6c, 0x8a62, 0x8a85, 0x8a82, 0x8a84, 0x8aa8, 0x8aa1, 0x8a91, 0x8aa5, 0x8aa6, 0x8a9a, 0x8aa3, 0x8ac4, 0x8acd, 0x8ac2, 0x8ada, 0x8aeb, 0x8af3, 0x8ae7, 0xfffd, 0x8ae4, 0x8af1, 0x8b14, 0x8ae0, 0x8ae2, 0x8af7, 0x8ade, 0x8adb, 0x8b0c, 0x8b07, 0x8b1a, 0x8ae1, 0x8b16, 0x8b10, 0x8b17, 0x8b20, 0x8b33, 0x97ab, 0x8b26, 0x8b2b, 0x8b3e, 0x8b28, 0x8b41, 0x8b4c, 0x8b4f, 0x8b4e, 0x8b49, 0x8b56, 0x8b5b, 0x8b5a, 0x8b6b, 0x8b5f, 0x8b6c, 0x8b6f, 0x8b74, 0x8b7d, 0x8b80, 0x8b8c, 0x8b8e, 0x8b92, 0x8b93, 0x8b96, 0x8b99, 0x8b9a, 0x8c3a, 0x8c41, 0x8c3f, 0x8c48, 0x8c4c, 0x8c4e, 0x8c50, 0x8c55, 0x8c62, 0x8c6c, 0x8c78, 0x8c7a, 0x8c82, 0x8c89, 0x8c85, 0x8c8a, 0x8c8d, 0x8c8e, 0x8c94, 0x8c7c, 0x8c98, 0x621d, 0x8cad, 0x8caa, 0x8cbd, 0x8cb2, 0x8cb3, 0x8cae, 0x8cb6, 0x8cc8, 0x8cc1, 0x8ce4, 0x8ce3, 0x8cda, 0x8cfd, 0x8cfa, 0x8cfb, 0x8d04, 0x8d05, 0x8d0a, 0x8d07, 0x8d0f, 0x8d0d, 0x8d10, 0x9f4e, 0x8d13, 0x8ccd, 0x8d14, 0x8d16, 0x8d67, 0x8d6d, 0x8d71, 0x8d73, 0x8d81, 0x8d99, 0x8dc2, 0x8dbe, 0x8dba, 0x8dcf, 0x8dda, 0x8dd6, 0x8dcc, 0x8ddb, 0x8dcb, 0x8dea, 0x8deb, 0x8ddf, 0x8de3, 0x8dfc, 0x8e08, 0x8e09, 0x8dff, 0x8e1d, 0x8e1e, 0x8e10, 0x8e1f, 0x8e42, 0x8e35, 0x8e30, 0x8e34, 0x8e4a, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8e47, 0x8e49, 0x8e4c, 0x8e50, 0x8e48, 0x8e59, 0x8e64, 0x8e60, 0x8e2a, 0x8e63, 0x8e55, 0x8e76, 0x8e72, 0x8e7c, 0x8e81, 0x8e87, 0x8e85, 0x8e84, 0x8e8b, 0x8e8a, 0x8e93, 0x8e91, 0x8e94, 0x8e99, 0x8eaa, 0x8ea1, 0x8eac, 0x8eb0, 0x8ec6, 0x8eb1, 0x8ebe, 0x8ec5, 0x8ec8, 0x8ecb, 0x8edb, 0x8ee3, 0x8efc, 0x8efb, 0x8eeb, 0x8efe, 0x8f0a, 0x8f05, 0x8f15, 0x8f12, 0x8f19, 0x8f13, 0x8f1c, 0x8f1f, 0x8f1b, 0x8f0c, 0x8f26, 0x8f33, 0x8f3b, 0x8f39, 0x8f45, 0x8f42, 0x8f3e, 0x8f4c, 0x8f49, 0x8f46, 0x8f4e, 0x8f57, 0x8f5c, 0xfffd, 0x8f62, 0x8f63, 0x8f64, 0x8f9c, 0x8f9f, 0x8fa3, 0x8fad, 0x8faf, 0x8fb7, 0x8fda, 0x8fe5, 0x8fe2, 0x8fea, 0x8fef, 0x9087, 0x8ff4, 0x9005, 0x8ff9, 0x8ffa, 0x9011, 0x9015, 0x9021, 0x900d, 0x901e, 0x9016, 0x900b, 0x9027, 0x9036, 0x9035, 0x9039, 0x8ff8, 0x904f, 0x9050, 0x9051, 0x9052, 0x900e, 0x9049, 0x903e, 0x9056, 0x9058, 0x905e, 0x9068, 0x906f, 0x9076, 0x96a8, 0x9072, 0x9082, 0x907d, 0x9081, 0x9080, 0x908a, 0x9089, 0x908f, 0x90a8, 0x90af, 0x90b1, 0x90b5, 0x90e2, 0x90e4, 0x6248, 0x90db, 0x9102, 0x9112, 0x9119, 0x9132, 0x9130, 0x914a, 0x9156, 0x9158, 0x9163, 0x9165, 0x9169, 0x9173, 0x9172, 0x918b, 0x9189, 0x9182, 0x91a2, 0x91ab, 0x91af, 0x91aa, 0x91b5, 0x91b4, 0x91ba, 0x91c0, 0x91c1, 0x91c9, 0x91cb, 0x91d0, 0x91d6, 0x91df, 0x91e1, 0x91db, 0x91fc, 0x91f5, 0x91f6, 0x921e, 0x91ff, 0x9214, 0x922c, 0x9215, 0x9211, 0x925e, 0x9257, 0x9245, 0x9249, 0x9264, 0x9248, 0x9295, 0x923f, 0x924b, 0x9250, 0x929c, 0x9296, 0x9293, 0x929b, 0x925a, 0x92cf, 0x92b9, 0x92b7, 0x92e9, 0x930f, 0x92fa, 0x9344, 0x932e, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9319, 0x9322, 0x931a, 0x9323, 0x933a, 0x9335, 0x933b, 0x935c, 0x9360, 0x937c, 0x936e, 0x9356, 0x93b0, 0x93ac, 0x93ad, 0x9394, 0x93b9, 0x93d6, 0x93d7, 0x93e8, 0x93e5, 0x93d8, 0x93c3, 0x93dd, 0x93d0, 0x93c8, 0x93e4, 0x941a, 0x9414, 0x9413, 0x9403, 0x9407, 0x9410, 0x9436, 0x942b, 0x9435, 0x9421, 0x943a, 0x9441, 0x9452, 0x9444, 0x945b, 0x9460, 0x9462, 0x945e, 0x946a, 0x9229, 0x9470, 0x9475, 0x9477, 0x947d, 0x945a, 0x947c, 0x947e, 0x9481, 0x947f, 0x9582, 0x9587, 0x958a, 0x9594, 0x9596, 0x9598, 0x9599, 0xfffd, 0x95a0, 0x95a8, 0x95a7, 0x95ad, 0x95bc, 0x95bb, 0x95b9, 0x95be, 0x95ca, 0x6ff6, 0x95c3, 0x95cd, 0x95cc, 0x95d5, 0x95d4, 0x95d6, 0x95dc, 0x95e1, 0x95e5, 0x95e2, 0x9621, 0x9628, 0x962e, 0x962f, 0x9642, 0x964c, 0x964f, 0x964b, 0x9677, 0x965c, 0x965e, 0x965d, 0x965f, 0x9666, 0x9672, 0x966c, 0x968d, 0x9698, 0x9695, 0x9697, 0x96aa, 0x96a7, 0x96b1, 0x96b2, 0x96b0, 0x96b4, 0x96b6, 0x96b8, 0x96b9, 0x96ce, 0x96cb, 0x96c9, 0x96cd, 0x894d, 0x96dc, 0x970d, 0x96d5, 0x96f9, 0x9704, 0x9706, 0x9708, 0x9713, 0x970e, 0x9711, 0x970f, 0x9716, 0x9719, 0x9724, 0x972a, 0x9730, 0x9739, 0x973d, 0x973e, 0x9744, 0x9746, 0x9748, 0x9742, 0x9749, 0x975c, 0x9760, 0x9764, 0x9766, 0x9768, 0x52d2, 0x976b, 0x9771, 0x9779, 0x9785, 0x977c, 0x9781, 0x977a, 0x9786, 0x978b, 0x978f, 0x9790, 0x979c, 0x97a8, 0x97a6, 0x97a3, 0x97b3, 0x97b4, 0x97c3, 0x97c6, 0x97c8, 0x97cb, 0x97dc, 0x97ed, 0x9f4f, 0x97f2, 0x7adf, 0x97f6, 0x97f5, 0x980f, 0x980c, 0x9838, 0x9824, 0x9821, 0x9837, 0x983d, 0x9846, 0x984f, 0x984b, 0x986b, 0x986f, 0x9870, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9871, 0x9874, 0x9873, 0x98aa, 0x98af, 0x98b1, 0x98b6, 0x98c4, 0x98c3, 0x98c6, 0x98e9, 0x98eb, 0x9903, 0x9909, 0x9912, 0x9914, 0x9918, 0x9921, 0x991d, 0x991e, 0x9924, 0x9920, 0x992c, 0x992e, 0x993d, 0x993e, 0x9942, 0x9949, 0x9945, 0x9950, 0x994b, 0x9951, 0x9952, 0x994c, 0x9955, 0x9997, 0x9998, 0x99a5, 0x99ad, 0x99ae, 0x99bc, 0x99df, 0x99db, 0x99dd, 0x99d8, 0x99d1, 0x99ed, 0x99ee, 0x99f1, 0x99f2, 0x99fb, 0x99f8, 0x9a01, 0x9a0f, 0x9a05, 0x99e2, 0x9a19, 0x9a2b, 0x9a37, 0x9a45, 0x9a42, 0x9a40, 0x9a43, 0xfffd, 0x9a3e, 0x9a55, 0x9a4d, 0x9a5b, 0x9a57, 0x9a5f, 0x9a62, 0x9a65, 0x9a64, 0x9a69, 0x9a6b, 0x9a6a, 0x9aad, 0x9ab0, 0x9abc, 0x9ac0, 0x9acf, 0x9ad1, 0x9ad3, 0x9ad4, 0x9ade, 0x9adf, 0x9ae2, 0x9ae3, 0x9ae6, 0x9aef, 0x9aeb, 0x9aee, 0x9af4, 0x9af1, 0x9af7, 0x9afb, 0x9b06, 0x9b18, 0x9b1a, 0x9b1f, 0x9b22, 0x9b23, 0x9b25, 0x9b27, 0x9b28, 0x9b29, 0x9b2a, 0x9b2e, 0x9b2f, 0x9b32, 0x9b44, 0x9b43, 0x9b4f, 0x9b4d, 0x9b4e, 0x9b51, 0x9b58, 0x9b74, 0x9b93, 0x9b83, 0x9b91, 0x9b96, 0x9b97, 0x9b9f, 0x9ba0, 0x9ba8, 0x9bb4, 0x9bc0, 0x9bca, 0x9bb9, 0x9bc6, 0x9bcf, 0x9bd1, 0x9bd2, 0x9be3, 0x9be2, 0x9be4, 0x9bd4, 0x9be1, 0x9c3a, 0x9bf2, 0x9bf1, 0x9bf0, 0x9c15, 0x9c14, 0x9c09, 0x9c13, 0x9c0c, 0x9c06, 0x9c08, 0x9c12, 0x9c0a, 0x9c04, 0x9c2e, 0x9c1b, 0x9c25, 0x9c24, 0x9c21, 0x9c30, 0x9c47, 0x9c32, 0x9c46, 0x9c3e, 0x9c5a, 0x9c60, 0x9c67, 0x9c76, 0x9c78, 0x9ce7, 0x9cec, 0x9cf0, 0x9d09, 0x9d08, 0x9ceb, 0x9d03, 0x9d06, 0x9d2a, 0x9d26, 0x9daf, 0x9d23, 0x9d1f, 0x9d44, 0x9d15, 0x9d12, 0x9d41, 0x9d3f, 0x9d3e, 0x9d46, 0x9d48, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9d5d, 0x9d5e, 0x9d64, 0x9d51, 0x9d50, 0x9d59, 0x9d72, 0x9d89, 0x9d87, 0x9dab, 0x9d6f, 0x9d7a, 0x9d9a, 0x9da4, 0x9da9, 0x9db2, 0x9dc4, 0x9dc1, 0x9dbb, 0x9db8, 0x9dba, 0x9dc6, 0x9dcf, 0x9dc2, 0x9dd9, 0x9dd3, 0x9df8, 0x9de6, 0x9ded, 0x9def, 0x9dfd, 0x9e1a, 0x9e1b, 0x9e1e, 0x9e75, 0x9e79, 0x9e7d, 0x9e81, 0x9e88, 0x9e8b, 0x9e8c, 0x9e92, 0x9e95, 0x9e91, 0x9e9d, 0x9ea5, 0x9ea9, 0x9eb8, 0x9eaa, 0x9ead, 0x9761, 0x9ecc, 0x9ece, 0x9ecf, 0x9ed0, 0x9ed4, 0x9edc, 0x9ede, 0x9edd, 0x9ee0, 0x9ee5, 0x9ee8, 0x9eef, 0xfffd, 0x9ef4, 0x9ef6, 0x9ef7, 0x9ef9, 0x9efb, 0x9efc, 0x9efd, 0x9f07, 0x9f08, 0x76b7, 0x9f15, 0x9f21, 0x9f2c, 0x9f3e, 0x9f4a, 0x9f52, 0x9f54, 0x9f63, 0x9f5f, 0x9f60, 0x9f61, 0x9f66, 0x9f67, 0x9f6c, 0x9f6a, 0x9f77, 0x9f72, 0x9f76, 0x9f95, 0x9f9c, 0x9fa0, 0x582f, 0x69c7, 0x9059, 0x7464, 0x51dc, 0x7199 }; /* Unicode-to-Kanji tables... */ static USHORT /* Unicode to Shift-JIS */ usj_0391[] = { /* 0x0391 thru 0x039c */ 0x839f, 0x83a0, 0x83a1, 0x83a2, 0x83a3, 0x83a4, 0x83a5, 0x83a6, 0x83a7, 0x83a8, 0x83a9, 0x83aa, 0x83ab, 0x83ac, 0x83ad, 0x83ae, 0x83af, 0xfffd, 0x83b0, 0x83b1, 0x83b2, 0x83b3, 0x83b4, 0x83b5, 0x83b6, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x83bf, 0x83c0, 0x83c1, 0x83c2, 0x83c3, 0x83c4, 0x83c5, 0x83c6, 0x83c7, 0x83c8, 0x83c9, 0x83ca, 0x83cb, 0x83cc, 0x83cd, 0x83ce, 0x83cf, 0xfffd, 0x83d0, 0x83d1, 0x83d2, 0x83d3, 0x83d4, 0x83d5, 0x83d6 }; static USHORT /* Unicode to Shift-JIS */ usj_0401[] = { /* 0x0401 thru 0x0451 */ 0x8446, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8440, 0x8441, 0x8442, 0x8443, 0x8444, 0x8445, 0x8447, 0x8448, 0x8449, 0x844a, 0x844b, 0x844c, 0x844d, 0x844e, 0x844f, 0x8450, 0x8451, 0x8452, 0x8453, 0x8454, 0x8455, 0x8456, 0x8457, 0x8458, 0x8459, 0x845a, 0x845b, 0x845c, 0x845d, 0x845e, 0x845f, 0x8460, 0x8470, 0x8471, 0x8472, 0x8473, 0x8474, 0x8475, 0x8477, 0x8478, 0x8479, 0x847a, 0x847b, 0x847c, 0x847d, 0x847e, 0x8480, 0x8481, 0x8482, 0x8483, 0x8484, 0x8485, 0x8486, 0x8487, 0x8488, 0x8489, 0x848a, 0x848b, 0x848c, 0x848d, 0x848e, 0x848f, 0x8490, 0x8491, 0xfffd, 0x8476 }; static USHORT /* Unicode to Shift-JIS */ usj_3000[] = { /* 0x3000 thru 0x30ff */ 0x8140, 0x8141, 0x8142, 0x8156, 0xfffd, 0x8158, 0x8159, 0x815a, 0x8171, 0x8172, 0x8173, 0x8174, 0x8175, 0x8176, 0x8177, 0x8178, 0x8179, 0x817a, 0x81a7, 0x81ac, 0x816b, 0x816c, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8160, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x829f, 0x82a0, 0x82a1, 0x82a2, 0x82a3, 0x82a4, 0x82a5, 0x82a6, 0x82a7, 0x82a8, 0x82a9, 0x82aa, 0x82ab, 0x82ac, 0x82ad, 0x82ae, 0x82af, 0x82b0, 0x82b1, 0x82b2, 0x82b3, 0x82b4, 0x82b5, 0x82b6, 0x82b7, 0x82b8, 0x82b9, 0x82ba, 0x82bb, 0x82bc, 0x82bd, 0x82be, 0x82bf, 0x82c0, 0x82c1, 0x82c2, 0x82c3, 0x82c4, 0x82c5, 0x82c6, 0x82c7, 0x82c8, 0x82c9, 0x82ca, 0x82cb, 0x82cc, 0x82cd, 0x82ce, 0x82cf, 0x82d0, 0x82d1, 0x82d2, 0x82d3, 0x82d4, 0x82d5, 0x82d6, 0x82d7, 0x82d8, 0x82d9, 0x82da, 0x82db, 0x82dc, 0x82dd, 0x82de, 0x82df, 0x82e0, 0x82e1, 0x82e2, 0x82e3, 0x82e4, 0x82e5, 0x82e6, 0x82e7, 0x82e8, 0x82e9, 0x82ea, 0x82eb, 0x82ec, 0x82ed, 0x82ee, 0x82ef, 0x82f0, 0x82f1, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x814a, 0x814b, 0x8154, 0x8155, 0xfffd, 0xfffd, 0x8340, 0x8341, 0x8342, 0x8343, 0x8344, 0x8345, 0x8346, 0x8347, 0x8348, 0x8349, 0x834a, 0x834b, 0x834c, 0x834d, 0x834e, 0x834f, 0x8350, 0x8351, 0x8352, 0x8353, 0x8354, 0x8355, 0x8356, 0x8357, 0x8358, 0x8359, 0x835a, 0x835b, 0x835c, 0x835d, 0x835e, 0x835f, 0x8360, 0x8361, 0x8362, 0x8363, 0x8364, 0x8365, 0x8366, 0x8367, 0x8368, 0x8369, 0x836a, 0x836b, 0x836c, 0x836d, 0x836e, 0x836f, 0x8370, 0x8371, 0x8372, 0x8373, 0x8374, 0x8375, 0x8376, 0x8377, 0x8378, 0x8379, 0x837a, 0x837b, 0x837c, 0x837d, 0x837e, 0x8380, 0x8381, 0x8382, 0x8383, 0x8384, 0x8385, 0x8386, 0x8387, 0x8388, 0x8389, 0x838a, 0x838b, 0x838c, 0x838d, 0x838e, 0x838f, 0x8390, 0x8391, 0x8392, 0x8393, 0x8394, 0x8395, 0x8396, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8145, 0x815b, 0x8152, 0x8153, 0xfffd }; static USHORT /* Unicode to Shift-JIS */ usj_ff00[] = { /* 0xff00 thru 0x0ff9f */ 0xfffd, 0x8149, 0xfffd, 0x8194, 0x8190, 0x8193, 0x8195, 0xfffd, 0x8169, 0x816a, 0x8196, 0x817b, 0x8143, 0xfffd, 0x8144, 0x815e, 0x824f, 0x8250, 0x8251, 0x8252, 0x8253, 0x8254, 0x8255, 0x8256, 0x8257, 0x8258, 0x8146, 0x8147, 0x8183, 0x8181, 0x8184, 0x8148, 0x8197, 0x8260, 0x8261, 0x8262, 0x8263, 0x8264, 0x8265, 0x8266, 0x8267, 0x8268, 0x8269, 0x826a, 0x826b, 0x826c, 0x826d, 0x826e, 0x826f, 0x8270, 0x8271, 0x8272, 0x8273, 0x8274, 0x8275, 0x8276, 0x8277, 0x8278, 0x8279, 0x816d, 0xfffd, 0x816e, 0x814f, 0x8151, 0x814d, 0x8281, 0x8282, 0x8283, 0x8284, 0x8285, 0x8286, 0x8287, 0x8288, 0x8289, 0x828a, 0x828b, 0x828c, 0x828d, 0x828e, 0x828f, 0x8290, 0x8291, 0x8292, 0x8293, 0x8294, 0x8295, 0x8296, 0x8297, 0x8298, 0x8299, 0x829a, 0x816f, 0x8162, 0x8170, 0xfffd, 0xfffd, 0xfffd, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af, 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7, 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf, 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf, 0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7, 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df }; /* Now one humongous table for Kanji */ static USHORT /* Unicode to Shift-JIS */ usj_4e00[] = { /* 0x4e00 thru 0x9fa0 */ 0x88ea, 0x929a, 0xfffd, 0x8eb5, 0xfffd, 0xfffd, 0xfffd, 0x969c, 0x8fe4, 0x8e4f, 0x8fe3, 0x89ba, 0xfffd, 0x9573, 0x975e, 0xfffd, 0x98a0, 0x894e, 0xfffd, 0xfffd, 0x8a8e, 0x98a1, 0x90a2, 0x99c0, 0x8b75, 0x95b8, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8fe5, 0xfffd, 0xfffd, 0x97bc, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x95c0, 0xfffd, 0xfffd, 0xfffd, 0x98a2, 0xfffd, 0xfffd, 0x9286, 0xfffd, 0xfffd, 0xfffd, 0x98a3, 0x8bf8, 0xfffd, 0xfffd, 0xfffd, 0x98a4, 0xfffd, 0x8adb, 0x924f, 0xfffd, 0x8ee5, 0x98a5, 0xfffd, 0xfffd, 0x98a6, 0xfffd, 0xfffd, 0x98a7, 0x9454, 0xfffd, 0x8b76, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9456, 0xfffd, 0x93e1, 0x8cc1, 0x9652, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe568, 0x98a8, 0x8fe6, 0x98a9, 0x89b3, 0xfffd, 0xfffd, 0xfffd, 0x8be3, 0x8cee, 0x96e7, 0xfffd, 0xfffd, 0x9ba4, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9790, 0xfffd, 0x93fb, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8aa3, 0xfffd, 0x8b54, 0xfffd, 0x98aa, 0xfffd, 0xfffd, 0x98ab, 0x97b9, 0xfffd, 0x975c, 0x9188, 0x98ad, 0x8e96, 0x93f1, 0xfffd, 0x98b0, 0xfffd, 0xfffd, 0x895d, 0x8cdd, 0xfffd, 0x8cdc, 0x88e4, 0xfffd, 0xfffd, 0x986a, 0x9869, 0xfffd, 0x8db1, 0x889f, 0xfffd, 0x98b1, 0x98b2, 0x98b3, 0x9653, 0x98b4, 0xfffd, 0x8cf0, 0x88e5, 0x9692, 0xfffd, 0x8b9c, 0xfffd, 0xfffd, 0x8b9d, 0x8b9e, 0x92e0, 0x97ba, 0xfffd, 0x98b5, 0xfffd, 0xfffd, 0x98b6, 0xfffd, 0xfffd, 0x98b7, 0xfffd, 0xfffd, 0xfffd, 0x906c, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8f59, 0x906d, 0x98bc, 0xfffd, 0x98ba, 0xfffd, 0x98bb, 0x8b77, 0xfffd, 0xfffd, 0x8da1, 0x89ee, 0xfffd, 0x98b9, 0x98b8, 0x95a7, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8e65, 0x8e64, 0x91bc, 0x98bd, 0x9574, 0x90e5, 0xfffd, 0xfffd, 0xfffd, 0x8157, 0x98be, 0x98c0, 0xfffd, 0xfffd, 0xfffd, 0x91e3, 0x97df, 0x88c8, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x98bf, 0x89bc, 0xfffd, 0x8bc2, 0xfffd, 0x9287, 0xfffd, 0xfffd, 0xfffd, 0x8c8f, 0x98c1, 0xfffd, 0xfffd, 0xfffd, 0x9443, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8ae9, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x98c2, 0x88c9, 0xfffd, 0xfffd, 0x8cde, 0x8aea, 0x959a, 0x94b0, 0x8b78, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x89ef, 0xfffd, 0x98e5, 0x9360, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x948c, 0x98c4, 0xfffd, 0xfffd, 0xfffd, 0x94ba, 0xfffd, 0x97e0, 0xfffd, 0x904c, 0xfffd, 0x8e66, 0xfffd, 0x8e97, 0x89be, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x92cf, 0xfffd, 0xfffd, 0x9241, 0x98c8, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x88ca, 0x92e1, 0x8f5a, 0x8db2, 0x9743, 0xfffd, 0x91cc, 0xfffd, 0x89bd, 0xfffd, 0x98c7, 0xfffd, 0x975d, 0x98c3, 0x98c5, 0x8dec, 0x98c6, 0x9b43, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x98ce, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x98d1, 0x98cf, 0xfffd, 0xfffd, 0x89c0, 0xfffd, 0x95b9, 0x98c9, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x98cd, 0x8cf1, 0xfffd, 0xfffd, 0x8e67, 0xfffd, 0xfffd, 0xfffd, 0x8aa4, 0xfffd, 0xfffd, 0x98d2, 0xfffd, 0x98ca, 0xfffd, 0xfffd, 0x97e1, 0xfffd, 0x8e98, 0xfffd, 0x98cb, 0xfffd, 0x98d0, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x98d3, 0xfffd, 0x98cc, 0xfffd, 0xfffd, 0x8b9f, 0xfffd, 0x88cb, 0xfffd, 0xfffd, 0x8ba0, 0x89bf, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9b44, 0xfffd, 0x9699, 0x958e, 0x8cf2, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x904e, 0x97b5, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x95d6, 0xfffd, 0xfffd, 0x8c57, 0x91a3, 0x89e2, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8f72, 0xfffd, 0xfffd, 0xfffd, 0x98d7, 0xfffd, 0x98dc, 0x98da, 0xfffd, 0xfffd, 0x98d5, 0xfffd, 0xfffd, 0x91ad, 0x98d8, 0xfffd, 0x98db, 0x98d9, 0xfffd, 0x95db, 0xfffd, 0x98d6, 0xfffd, 0x904d, 0xfffd, 0x9693, 0x98dd, 0x98de, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8f43, 0x98eb, 0xfffd, 0xfffd, 0xfffd, 0x946f, 0xfffd, 0x9555, 0x98e6, 0xfffd, 0x95ee, 0xfffd, 0x89b4, 0xfffd, 0xfffd, 0xfffd, 0x98ea, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x98e4, 0x98ed, 0xfffd, 0xfffd, 0x9171, 0xfffd, 0x8cc2, 0xfffd, 0x947b, 0xfffd, 0xe0c5, 0xfffd, 0x98ec, 0x937c, 0xfffd, 0x98e1, 0xfffd, 0x8cf4, 0xfffd, 0xfffd, 0x8cf3, 0x98df, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8ed8, 0xfffd, 0x98e7, 0xfffd, 0x95ed, 0x926c, 0x98e3, 0x8c91, 0xfffd, 0x98e0, 0x98e8, 0x98e2, 0x97cf, 0x98e9, 0x9860, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8be4, 0xfffd, 0xfffd, 0x8c90, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x98ee, 0xfffd, 0xfffd, 0xfffd, 0x98ef, 0x98f3, 0x88cc, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x95ce, 0x98f2, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x98f1, 0x98f5, 0xfffd, 0xfffd, 0xfffd, 0x98f4, 0xfffd, 0x92e2, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8c92, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x98f6, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8ec3, 0xfffd, 0x91a4, 0x92e3, 0x8bf4, 0xfffd, 0x98f7, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8b55, 0xfffd, 0xfffd, 0x98f8, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x98fa, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9654, 0xfffd, 0xfffd, 0xfffd, 0x8c86, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8e50, 0x94f5, 0x98f9, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8dc3, 0x9762, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x98fc, 0x9942, 0x98fb, 0x8dc2, 0xfffd, 0x8f9d, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8c58, 0xfffd, 0xfffd, 0xfffd, 0x9943, 0xfffd, 0xfffd, 0x8bcd, 0xfffd, 0xfffd, 0xfffd, 0x9940, 0x9941, 0xfffd, 0xfffd, 0x93ad, 0xfffd, 0x919c, 0xfffd, 0x8ba1, 0xfffd, 0xfffd, 0xfffd, 0x966c, 0x9944, 0xfffd, 0xfffd, 0xfffd, 0x97bb, 0xfffd, 0xfffd, 0xfffd, 0x9945, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9948, 0xfffd, 0x9946, 0xfffd, 0x916d, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9947, 0x9949, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x994b, 0xfffd, 0xfffd, 0xfffd, 0x994a, 0xfffd, 0x95c6, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8b56, 0x994d, 0x994e, 0xfffd, 0x89ad, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x994c, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8ef2, 0xfffd, 0x9951, 0x9950, 0x994f, 0xfffd, 0x98d4, 0xfffd, 0x9952, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8f9e, 0xfffd, 0x9953, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9744, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x96d7, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9955, 0xfffd, 0xfffd, 0x9954, 0x9957, 0x9956, 0xfffd, 0xfffd, 0x9958, 0x9959, 0x88f2, 0xfffd, 0x8cb3, 0x8c5a, 0x8f5b, 0x929b, 0x8ba2, 0x90e6, 0x8cf5, 0xfffd, 0x8d8e, 0x995b, 0x96c6, 0x9365, 0xfffd, 0x8e99, 0xfffd, 0x995a, 0xfffd, 0x995c, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x937d, 0xfffd, 0x8a95, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x995d, 0xfffd, 0xfffd, 0x93fc, 0xfffd, 0xfffd, 0x9153, 0x995f, 0x9960, 0x94aa, 0x8cf6, 0x985a, 0x9961, 0xfffd, 0xfffd, 0x8ba4, 0xfffd, 0xfffd, 0xfffd, 0x95ba, 0x91b4, 0x8bef, 0x9354, 0xfffd, 0xfffd, 0xfffd, 0x8c93, 0xfffd, 0xfffd, 0xfffd, 0x9962, 0xfffd, 0x9963, 0xfffd, 0xfffd, 0x93e0, 0x897e, 0xfffd, 0xfffd, 0x9966, 0x8dfb, 0xfffd, 0x9965, 0x8dc4, 0xfffd, 0x9967, 0xe3ec, 0x9968, 0x9660, 0x9969, 0xfffd, 0x996a, 0x996b, 0x8fe7, 0xfffd, 0x8eca, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8aa5, 0xfffd, 0x996e, 0xfffd, 0x996c, 0x96bb, 0x996d, 0xfffd, 0x9579, 0x996f, 0x9970, 0x9971, 0x937e, 0xfffd, 0xfffd, 0xfffd, 0x9975, 0x9973, 0x9974, 0x9972, 0x8de1, 0x9976, 0x96e8, 0x97e2, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9977, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x90a6, 0x9978, 0x8f79, 0xfffd, 0xfffd, 0x9979, 0xfffd, 0x929c, 0x97bd, 0x9380, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x99c3, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x997a, 0xeaa3, 0x8bc3, 0xfffd, 0xfffd, 0x997b, 0x967d, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8f88, 0x91fa, 0xfffd, 0x997d, 0x93e2, 0xfffd, 0xfffd, 0x997e, 0xfffd, 0xfffd, 0x9980, 0x8a4d, 0xfffd, 0xfffd, 0xfffd, 0x9981, 0x8ba5, 0xfffd, 0x93ca, 0x899a, 0x8f6f, 0xfffd, 0xfffd, 0x949f, 0x9982, 0xfffd, 0x9381, 0xfffd, 0xfffd, 0x906e, 0x9983, 0xfffd, 0x95aa, 0x90d8, 0x8aa0, 0xfffd, 0x8aa7, 0x9984, 0xfffd, 0xfffd, 0x9986, 0xfffd, 0xfffd, 0x8c59, 0xfffd, 0xfffd, 0x9985, 0xfffd, 0xfffd, 0x97f1, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8f89, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x94bb, 0x95ca, 0xfffd, 0x9987, 0xfffd, 0x9798, 0x9988, 0xfffd, 0xfffd, 0xfffd, 0x9989, 0xfffd, 0x939e, 0xfffd, 0xfffd, 0x998a, 0xfffd, 0xfffd, 0x90a7, 0x8dfc, 0x8c94, 0x998b, 0x8e68, 0x8d8f, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x92e4, 0x998d, 0xfffd, 0xfffd, 0x91a5, 0xfffd, 0xfffd, 0x8ded, 0x998e, 0x998f, 0x914f, 0xfffd, 0x998c, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9991, 0xfffd, 0x9655, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8d84, 0xfffd, 0xfffd, 0x9990, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8c95, 0x8ddc, 0x948d, 0xfffd, 0xfffd, 0xfffd, 0x9994, 0x9992, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x959b, 0x8fe8, 0x999b, 0x8a84, 0x9995, 0x9993, 0x916e, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9997, 0xfffd, 0x9996, 0xfffd, 0xfffd, 0xfffd, 0x8a63, 0xfffd, 0xfffd, 0xfffd, 0x8c80, 0x999c, 0x97ab, 0xfffd, 0xfffd, 0xfffd, 0x9998, 0xfffd, 0xfffd, 0xfffd, 0x999d, 0x999a, 0xfffd, 0x9999, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x97cd, 0xfffd, 0xfffd, 0xfffd, 0x8cf7, 0x89c1, 0xfffd, 0xfffd, 0x97f2, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8f95, 0x9377, 0x8d85, 0x99a0, 0x99a1, 0xfffd, 0xfffd, 0xfffd, 0x97e3, 0xfffd, 0xfffd, 0x984a, 0x99a3, 0xfffd, 0xfffd, 0xfffd, 0x8cf8, 0xfffd, 0xfffd, 0x99a2, 0xfffd, 0x8a4e, 0xfffd, 0xfffd, 0x99a4, 0xfffd, 0x9675, 0xfffd, 0x92ba, 0xfffd, 0x9745, 0xfffd, 0x95d7, 0xfffd, 0xfffd, 0xfffd, 0x99a5, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe8d3, 0xfffd, 0xfffd, 0x93ae, 0xfffd, 0x99a6, 0x8aa8, 0x96b1, 0xfffd, 0xfffd, 0xfffd, 0x8f9f, 0x99a7, 0x95e5, 0x99ab, 0xfffd, 0x90a8, 0x99a8, 0x8bce, 0xfffd, 0x99a9, 0x8aa9, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8c4d, 0x99ac, 0xfffd, 0x99ad, 0xfffd, 0xfffd, 0x99ae, 0x99af, 0x8ed9, 0xfffd, 0xfffd, 0xfffd, 0x8cf9, 0x96dc, 0xfffd, 0x96e6, 0x93f5, 0xfffd, 0xfffd, 0x95ef, 0x99b0, 0xfffd, 0x99b1, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x99b3, 0xfffd, 0x99b5, 0x99b4, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x99b6, 0x89bb, 0x966b, 0xfffd, 0x8dfa, 0x99b7, 0xfffd, 0xfffd, 0x9178, 0xfffd, 0xfffd, 0x8fa0, 0x8ba7, 0xfffd, 0x99b8, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x94d9, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x99b9, 0xfffd, 0x99ba, 0xfffd, 0x99bb, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x99bc, 0x9543, 0x8be6, 0x88e3, 0xfffd, 0xfffd, 0xfffd, 0x93bd, 0x99bd, 0x8f5c, 0xfffd, 0x90e7, 0xfffd, 0x99bf, 0x99be, 0x8fa1, 0x8cdf, 0x99c1, 0x94bc, 0xfffd, 0xfffd, 0x99c2, 0xfffd, 0xfffd, 0xfffd, 0x94da, 0x91b2, 0x91ec, 0x8ba6, 0xfffd, 0xfffd, 0x93ec, 0x9250, 0xfffd, 0x948e, 0xfffd, 0x966d, 0xfffd, 0x99c4, 0xfffd, 0x90e8, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8c54, 0xfffd, 0xfffd, 0x99c5, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x99c6, 0x894b, 0x88f3, 0x8aeb, 0xfffd, 0x91a6, 0x8b70, 0x9791, 0xfffd, 0x99c9, 0x89b5, 0xfffd, 0xfffd, 0x99c8, 0xfffd, 0xfffd, 0xfffd, 0x8ba8, 0xfffd, 0xfffd, 0x99ca, 0xfffd, 0x96ef, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x99cb, 0xfffd, 0x97d0, 0xfffd, 0x8cfa, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8cb4, 0x99cc, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x99ce, 0x99cd, 0xfffd, 0x907e, 0x8958, 0xfffd, 0xfffd, 0xfffd, 0x897d, 0x99cf, 0xfffd, 0x99d0, 0xfffd, 0xfffd, 0x8cb5, 0xfffd, 0xfffd, 0x99d1, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8b8e, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8e51, 0x99d2, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9694, 0x8db3, 0x8b79, 0x9746, 0x916f, 0x94bd, 0x8efb, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8f66, 0xfffd, 0x8ee6, 0x8ef3, 0xfffd, 0x8f96, 0xfffd, 0x94be, 0xfffd, 0xfffd, 0xfffd, 0x99d5, 0xfffd, 0x8962, 0x9170, 0x8cfb, 0x8cc3, 0x8be5, 0xfffd, 0xfffd, 0x99d9, 0x9240, 0x91fc, 0x8ba9, 0x8fa2, 0x99da, 0x99d8, 0x89c2, 0x91e4, 0x8eb6, 0x8e6a, 0x8945, 0xfffd, 0xfffd, 0x8a90, 0x8d86, 0x8e69, 0xfffd, 0x99db, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x99dc, 0xfffd, 0x8b68, 0x8a65, 0xfffd, 0xfffd, 0xfffd, 0x8d87, 0x8b67, 0x92dd, 0x8944, 0x93af, 0x96bc, 0x8d40, 0x9799, 0x9366, 0x8cfc, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8c4e, 0xfffd, 0x99e5, 0xfffd, 0x8be1, 0x9669, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x94db, 0xfffd, 0xfffd, 0x99e4, 0xfffd, 0x8adc, 0x99df, 0x99e0, 0x99e2, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x99e3, 0xfffd, 0x8b7a, 0x9081, 0xfffd, 0x95ab, 0x99e1, 0x99dd, 0x8ce1, 0xfffd, 0x99de, 0xfffd, 0x9843, 0xfffd, 0xfffd, 0xfffd, 0x95f0, 0xfffd, 0x92e6, 0x8ce0, 0x8d90, 0xfffd, 0xfffd, 0xfffd, 0x99e6, 0xfffd, 0xfffd, 0x93db, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x99ea, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8efc, 0xfffd, 0x8ef4, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x99ed, 0x99eb, 0xfffd, 0x96a1, 0xfffd, 0x99e8, 0x99f1, 0x99ec, 0xfffd, 0xfffd, 0xfffd, 0x99ef, 0x8cc4, 0x96bd, 0xfffd, 0xfffd, 0x99f0, 0xfffd, 0xfffd, 0xfffd, 0x99f2, 0xfffd, 0x99f4, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8dee, 0x9861, 0xfffd, 0x99e9, 0x99e7, 0x99f3, 0xfffd, 0x99ee, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x99f6, 0xfffd, 0x9a42, 0x99f8, 0xfffd, 0xfffd, 0x99fc, 0xfffd, 0xfffd, 0x9a40, 0x99f9, 0xfffd, 0xfffd, 0x9a5d, 0xfffd, 0xfffd, 0x8de7, 0x8a50, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x99f7, 0xfffd, 0xfffd, 0xfffd, 0x9a44, 0x88f4, 0x9a43, 0xfffd, 0x88a3, 0x9569, 0x9a41, 0xfffd, 0x99fa, 0xfffd, 0xfffd, 0x99f5, 0x99fb, 0x8dc6, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9a45, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x88f5, 0x9a4e, 0xfffd, 0xfffd, 0x9a46, 0x9a47, 0xfffd, 0x8fa3, 0x9689, 0xfffd, 0xfffd, 0xfffd, 0x9a4c, 0x9a4b, 0xfffd, 0xfffd, 0xfffd, 0x934e, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9a4d, 0xfffd, 0xfffd, 0x9a4a, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8953, 0xfffd, 0x8db4, 0x904f, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9a48, 0x9382, 0xfffd, 0xfffd, 0xfffd, 0x9a49, 0xfffd, 0x88a0, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9a53, 0x9742, 0xfffd, 0x8fa5, 0xfffd, 0x9a59, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9a58, 0x9a4f, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x91c1, 0xfffd, 0x9a50, 0xfffd, 0xfffd, 0xfffd, 0x91ed, 0x9a55, 0x8fa4, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9a52, 0xfffd, 0xfffd, 0x96e2, 0xfffd, 0xfffd, 0xfffd, 0x8c5b, 0xfffd, 0xfffd, 0x9a56, 0x9a57, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9a54, 0x9a5a, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9a51, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9a60, 0x9a65, 0xfffd, 0x9a61, 0xfffd, 0x9a5c, 0xfffd, 0xfffd, 0x9a66, 0x9150, 0xfffd, 0xfffd, 0x9a68, 0xfffd, 0x8d41, 0x9a5e, 0x929d, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9a62, 0x9a5b, 0x8aab, 0xfffd, 0x8aec, 0x8a85, 0x9a63, 0x9a5f, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8c96, 0x9a69, 0x9a67, 0x9172, 0x8b69, 0x8baa, 0xfffd, 0x9a64, 0xfffd, 0x8bf2, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8963, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9a6d, 0x9a6b, 0xfffd, 0x9aa5, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9a70, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9a6a, 0xfffd, 0x9a6e, 0xfffd, 0xfffd, 0x9a6c, 0xfffd, 0xfffd, 0xfffd, 0x8e6b, 0x9a6f, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9a72, 0xfffd, 0x9a77, 0xfffd, 0xfffd, 0xfffd, 0x9a75, 0x9a74, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9251, 0xfffd, 0xfffd, 0x89c3, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9a71, 0xfffd, 0x9a73, 0x8fa6, 0x8952, 0xfffd, 0xfffd, 0x9a76, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x89dc, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9a82, 0xfffd, 0x8ffa, 0x9a7d, 0xfffd, 0x9a7b, 0xfffd, 0x9a7c, 0xfffd, 0x9a7e, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x895c, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9158, 0xfffd, 0x9a78, 0xfffd, 0x9a79, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8a9a, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9a81, 0xfffd, 0xfffd, 0xfffd, 0x8aed, 0xfffd, 0x9a84, 0x9a80, 0x9a83, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x95ac, 0xfffd, 0xfffd, 0xfffd, 0x93d3, 0xfffd, 0x94b6, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9a86, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9a85, 0x8a64, 0xfffd, 0xfffd, 0x9a87, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9a8a, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9a89, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9a88, 0xfffd, 0x9458, 0xfffd, 0xfffd, 0x9a8b, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9a8c, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9a8e, 0xfffd, 0x9a8d, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9a90, 0xfffd, 0xfffd, 0xfffd, 0x9a93, 0x9a91, 0x9a8f, 0x9a92, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9a94, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9a95, 0xfffd, 0xfffd, 0x9a96, 0xfffd, 0x9a97, 0xfffd, 0xfffd, 0xfffd, 0x9a98, 0x9964, 0xfffd, 0x8efa, 0x8e6c, 0xfffd, 0xfffd, 0x89f1, 0xfffd, 0x88f6, 0xfffd, 0xfffd, 0x9263, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9a99, 0xfffd, 0x8da2, 0xfffd, 0x88cd, 0x907d, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9a9a, 0x8cc5, 0xfffd, 0xfffd, 0x8d91, 0xfffd, 0x9a9c, 0x9a9b, 0xfffd, 0xfffd, 0x95de, 0x9a9d, 0xfffd, 0xfffd, 0xfffd, 0x9a9f, 0x9a9e, 0xfffd, 0x9aa0, 0xfffd, 0x9aa1, 0xfffd, 0x8c97, 0xfffd, 0xfffd, 0x8980, 0x9aa2, 0xfffd, 0xfffd, 0x9aa4, 0xfffd, 0x9aa3, 0xfffd, 0xfffd, 0xfffd, 0x9aa6, 0xfffd, 0xfffd, 0x9379, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9aa7, 0x88b3, 0x8ddd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8c5c, 0xfffd, 0xfffd, 0x926e, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9aa8, 0x9aa9, 0xfffd, 0xfffd, 0x9aab, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9aac, 0xfffd, 0x8de2, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8bcf, 0xfffd, 0xfffd, 0x9656, 0xfffd, 0xfffd, 0xfffd, 0x9aaa, 0x9aad, 0x8dbf, 0x8d42, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9ab1, 0xfffd, 0xfffd, 0x8da3, 0xfffd, 0x9252, 0xfffd, 0xfffd, 0x9aae, 0x92d8, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9ab2, 0xfffd, 0xfffd, 0x9082, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9ab0, 0x9ab3, 0xfffd, 0x8c5e, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9ab4, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9ab5, 0xfffd, 0x8d43, 0x8a5f, 0x9ab7, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9ab8, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9ab9, 0xfffd, 0xfffd, 0x9ab6, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9aaf, 0xfffd, 0xfffd, 0x9aba, 0xfffd, 0xfffd, 0x9abb, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9684, 0xfffd, 0xfffd, 0x8fe9, 0xfffd, 0xfffd, 0xfffd, 0x9abd, 0x9abe, 0x9abc, 0xfffd, 0x9ac0, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9457, 0xfffd, 0xfffd, 0x88e6, 0x9575, 0xfffd, 0xfffd, 0x9ac1, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8ffb, 0xfffd, 0xfffd, 0x8eb7, 0xfffd, 0x947c, 0x8aee, 0xfffd, 0x8de9, 0xfffd, 0xfffd, 0xfffd, 0x9678, 0xfffd, 0x93b0, 0xfffd, 0xfffd, 0x8c98, 0x91cd, 0xfffd, 0xfffd, 0xfffd, 0x9abf, 0x9ac2, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x91c2, 0xfffd, 0xfffd, 0xfffd, 0x9ac3, 0xfffd, 0xfffd, 0xfffd, 0x9ac4, 0xfffd, 0xfffd, 0xfffd, 0x9ac6, 0xfffd, 0xfffd, 0x92e7, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8aac, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xea9f, 0x8981, 0x95f1, 0xfffd, 0xfffd, 0x8fea, 0x9367, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8de4, 0xfffd, 0xfffd, 0x9acc, 0xfffd, 0xfffd, 0x95bb, 0x97db, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x89f2, 0x9ac8, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9159, 0x9acb, 0xfffd, 0x9383, 0xfffd, 0xfffd, 0x9368, 0x9384, 0x94b7, 0x92cb, 0xfffd, 0xfffd, 0xfffd, 0x8dc7, 0xfffd, 0xfffd, 0xfffd, 0x9ac7, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8996, 0xfffd, 0x9355, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9ac9, 0xfffd, 0x9ac5, 0xfffd, 0xfffd, 0x906f, 0xfffd, 0xfffd, 0xfffd, 0x9acd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8f6d, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8bab, 0xfffd, 0x9ace, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x95e6, 0xfffd, 0xfffd, 0xfffd, 0x919d, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x92c4, 0xfffd, 0xfffd, 0x9ad0, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x966e, 0xfffd, 0xfffd, 0x9ad1, 0xfffd, 0xfffd, 0x9ad6, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x95ad, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9ad5, 0x9acf, 0x9ad2, 0x9ad4, 0xfffd, 0xfffd, 0x8da4, 0xfffd, 0xfffd, 0x95c7, 0xfffd, 0xfffd, 0xfffd, 0x9ad7, 0xfffd, 0x9264, 0xfffd, 0xfffd, 0x89f3, 0xfffd, 0x8feb, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9ad9, 0xfffd, 0x9ad8, 0xfffd, 0x8d88, 0xfffd, 0x9ada, 0x9adc, 0x9adb, 0xfffd, 0xfffd, 0x9ade, 0xfffd, 0x9ad3, 0x9ae0, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9adf, 0x9add, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8e6d, 0x9070, 0xfffd, 0x9173, 0x9ae1, 0x90ba, 0x88eb, 0x9484, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x92d9, 0xfffd, 0x9ae3, 0x9ae2, 0x9ae4, 0x9ae5, 0x9ae6, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9ae7, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x95cf, 0x9ae8, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x89c4, 0x9ae9, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x975b, 0x8a4f, 0xfffd, 0x99c7, 0x8f67, 0x91bd, 0x9aea, 0x96e9, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x96b2, 0xfffd, 0xfffd, 0x9aec, 0xfffd, 0x91e5, 0xfffd, 0x9356, 0x91be, 0x9576, 0x9aed, 0x9aee, 0x899b, 0xfffd, 0xfffd, 0x8eb8, 0x9aef, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x88ce, 0x9af0, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9af1, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8982, 0xfffd, 0xfffd, 0x8aef, 0x93de, 0x95f2, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9af5, 0x9174, 0x9af4, 0x8c5f, 0xfffd, 0xfffd, 0x967a, 0x9af3, 0xfffd, 0x9385, 0x9af7, 0xfffd, 0x9af6, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9af9, 0xfffd, 0x9af8, 0xfffd, 0xfffd, 0x899c, 0xfffd, 0x9afa, 0x8fa7, 0x9afc, 0x9244, 0xfffd, 0x9afb, 0xfffd, 0x95b1, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8f97, 0x937a, 0xfffd, 0xfffd, 0xfffd, 0x9b40, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8d44, 0xfffd, 0xfffd, 0xfffd, 0x9b41, 0x9440, 0x94dc, 0x96cf, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9444, 0xfffd, 0xfffd, 0x9b4a, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8b57, 0xfffd, 0xfffd, 0x9764, 0xfffd, 0xfffd, 0x96ad, 0xfffd, 0x9baa, 0xfffd, 0x9b42, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9b45, 0xfffd, 0x91c3, 0xfffd, 0xfffd, 0x9657, 0xfffd, 0xfffd, 0xfffd, 0x9369, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9b46, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9685, 0xfffd, 0x8dc8, 0xfffd, 0xfffd, 0x8fa8, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9b47, 0xfffd, 0xfffd, 0x8e6f, 0xfffd, 0x8e6e, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x88b7, 0x8cc6, 0xfffd, 0x90a9, 0x88cf, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9b4b, 0x9b4c, 0xfffd, 0x9b49, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8957, 0x8aad, 0xfffd, 0x9b48, 0xfffd, 0x96c3, 0x9550, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x88a6, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x88f7, 0xfffd, 0xfffd, 0xfffd, 0x8e70, 0xfffd, 0x88d0, 0xfffd, 0x88a1, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9b51, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9b4f, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x96ba, 0xfffd, 0x9b52, 0xfffd, 0x9b50, 0xfffd, 0xfffd, 0x9b4e, 0x9050, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9b4d, 0xfffd, 0xfffd, 0xfffd, 0x95d8, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8ce2, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9b56, 0x9b57, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8fa9, 0xfffd, 0xfffd, 0xfffd, 0x9b53, 0x984b, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x946b, 0xfffd, 0xfffd, 0x9b55, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8da5, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9b58, 0xfffd, 0xfffd, 0xfffd, 0x9577, 0xfffd, 0xfffd, 0xfffd, 0x9b59, 0xfffd, 0x9b54, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x96b9, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x947d, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9b5a, 0x9551, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9b5b, 0x9b5f, 0x9b5c, 0xfffd, 0xfffd, 0x89c5, 0x9b5e, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8eb9, 0xfffd, 0x9b5d, 0x8c99, 0xfffd, 0xfffd, 0xfffd, 0x9b6b, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9b64, 0x9b61, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9284, 0xfffd, 0x9b60, 0xfffd, 0xfffd, 0x9b62, 0xfffd, 0xfffd, 0x9b63, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9b65, 0x9b66, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8af0, 0xfffd, 0x9b68, 0x9b67, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9b69, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8fec, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9b6c, 0xfffd, 0x92da, 0xfffd, 0xfffd, 0xfffd, 0x8964, 0xfffd, 0x9b6a, 0xfffd, 0xfffd, 0xfffd, 0x9b6d, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9b6e, 0xfffd, 0x9b71, 0xfffd, 0xfffd, 0x9b6f, 0xfffd, 0x9b70, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8e71, 0x9b72, 0xfffd, 0xfffd, 0x8d45, 0x9b73, 0xfffd, 0x8e9a, 0x91b6, 0xfffd, 0x9b74, 0x9b75, 0x8e79, 0x8d46, 0xfffd, 0x96d0, 0xfffd, 0xfffd, 0xfffd, 0x8b47, 0x8cc7, 0x9b76, 0x8a77, 0xfffd, 0xfffd, 0x9b77, 0xfffd, 0x91b7, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9b78, 0x9ba1, 0xfffd, 0x9b79, 0xfffd, 0x9b7a, 0xfffd, 0xfffd, 0x9b7b, 0xfffd, 0x9b7d, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9b7e, 0xfffd, 0xfffd, 0x9b80, 0xfffd, 0x91ee, 0xfffd, 0x8946, 0x8ee7, 0x88c0, 0xfffd, 0x9176, 0x8aae, 0x8eb3, 0xfffd, 0x8d47, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9386, 0xfffd, 0x8f40, 0x8aaf, 0x9288, 0x92e8, 0x88b6, 0x8b58, 0x95f3, 0xfffd, 0x8ec0, 0xfffd, 0xfffd, 0x8b71, 0x90e9, 0x8eba, 0x9747, 0x9b81, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8b7b, 0xfffd, 0x8dc9, 0xfffd, 0xfffd, 0x8a51, 0x8983, 0x8faa, 0x89c6, 0xfffd, 0x9b82, 0x9765, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8f68, 0xfffd, 0xfffd, 0x8ee2, 0x9b83, 0x8af1, 0x93d0, 0x96a7, 0x9b84, 0xfffd, 0x9b85, 0xfffd, 0xfffd, 0x9578, 0xfffd, 0xfffd, 0xfffd, 0x9b87, 0xfffd, 0x8aa6, 0x8bf5, 0x9b86, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8ab0, 0xfffd, 0x9051, 0x9b8b, 0x8e40, 0xfffd, 0x89c7, 0x9b8a, 0xfffd, 0x9b88, 0x9b8c, 0x9b89, 0x944a, 0x9ecb, 0x9052, 0xfffd, 0x9b8d, 0xfffd, 0xfffd, 0x97be, 0xfffd, 0x9b8e, 0xfffd, 0xfffd, 0x9b90, 0xfffd, 0x929e, 0x9b8f, 0xfffd, 0x90a1, 0xfffd, 0x8e9b, 0xfffd, 0xfffd, 0xfffd, 0x91ce, 0x8ef5, 0xfffd, 0x9595, 0x90ea, 0xfffd, 0x8ecb, 0x9b91, 0x8fab, 0x9b92, 0x9b93, 0x88d1, 0x91b8, 0x9071, 0xfffd, 0x9b94, 0x93b1, 0x8fac, 0xfffd, 0x8fad, 0xfffd, 0x9b95, 0xfffd, 0xfffd, 0x90eb, 0xfffd, 0xfffd, 0xfffd, 0x8fae, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9b96, 0xfffd, 0x9b97, 0xfffd, 0x96de, 0xfffd, 0xfffd, 0xfffd, 0x9b98, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8bc4, 0xfffd, 0xfffd, 0xfffd, 0x8f41, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9b99, 0x9b9a, 0x8eda, 0x904b, 0x93f2, 0x9073, 0x94f6, 0x9441, 0x8bc7, 0x9b9b, 0xfffd, 0xfffd, 0xfffd, 0x8b8f, 0x9b9c, 0xfffd, 0x8bfc, 0xfffd, 0x93cd, 0x89ae, 0xfffd, 0x8e72, 0x9b9d, 0x9ba0, 0x9b9f, 0x8bfb, 0xfffd, 0x9b9e, 0xfffd, 0x9357, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x91ae, 0xfffd, 0x936a, 0x8ec6, 0xfffd, 0xfffd, 0x9177, 0x979a, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9ba2, 0xfffd, 0x9ba3, 0x93d4, 0xfffd, 0x8e52, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9ba5, 0xfffd, 0xfffd, 0x9ba6, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9ba7, 0xfffd, 0xfffd, 0xfffd, 0x8af2, 0x9ba8, 0xfffd, 0xfffd, 0x9ba9, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x89aa, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x915a, 0x8ae2, 0xfffd, 0x9bab, 0x96a6, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x91d0, 0xfffd, 0x8a78, 0xfffd, 0xfffd, 0x9bad, 0x9baf, 0x8add, 0xfffd, 0xfffd, 0x9bac, 0x9bae, 0xfffd, 0x9bb1, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9bb0, 0xfffd, 0x9bb2, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9bb3, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x93bb, 0x8bac, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x89e3, 0x9bb4, 0x9bb9, 0xfffd, 0xfffd, 0x9bb7, 0xfffd, 0x95f5, 0x95f4, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9387, 0xfffd, 0xfffd, 0xfffd, 0x9bb6, 0x8f73, 0xfffd, 0x9bb5, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9092, 0xfffd, 0xfffd, 0xfffd, 0x9bba, 0xfffd, 0xfffd, 0x8de8, 0xfffd, 0xfffd, 0x9bc0, 0xfffd, 0xfffd, 0x9bc1, 0x9bbb, 0x8a52, 0x9bbc, 0x9bc5, 0x9bc4, 0x9bc3, 0x9bbf, 0xfffd, 0xfffd, 0xfffd, 0x9bbe, 0xfffd, 0xfffd, 0x9bc2, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x95f6, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9bc9, 0x9bc6, 0xfffd, 0x9bc8, 0xfffd, 0x9792, 0xfffd, 0x9bc7, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9bbd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9093, 0xfffd, 0xfffd, 0x9bca, 0xfffd, 0xfffd, 0x8db5, 0xfffd, 0xfffd, 0xfffd, 0x9bcb, 0xfffd, 0xfffd, 0x9bcc, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9bcf, 0xfffd, 0x9bce, 0xfffd, 0xfffd, 0x9bcd, 0xfffd, 0xfffd, 0xfffd, 0x9388, 0x9bb8, 0xfffd, 0xfffd, 0xfffd, 0x9bd5, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9bd1, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9bd0, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9bd2, 0xfffd, 0x9bd3, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9bd6, 0xfffd, 0xfffd, 0x97e4, 0xfffd, 0x9bd7, 0x9bd4, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9bd8, 0xfffd, 0xfffd, 0x8ade, 0x9bd9, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9bdb, 0x9bda, 0xfffd, 0xfffd, 0x9bdc, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9bdd, 0xfffd, 0x90ec, 0x8f42, 0xfffd, 0xfffd, 0x8f84, 0xfffd, 0x9183, 0xfffd, 0x8d48, 0x8db6, 0x8d49, 0x8b90, 0xfffd, 0xfffd, 0x9bde, 0xfffd, 0xfffd, 0x8db7, 0xfffd, 0xfffd, 0x8cc8, 0x9bdf, 0x96a4, 0x9462, 0x9be0, 0xfffd, 0x8d4a, 0xfffd, 0xfffd, 0xfffd, 0x8aaa, 0xfffd, 0x9246, 0x8bd0, 0xfffd, 0xfffd, 0xfffd, 0x8e73, 0x957a, 0xfffd, 0xfffd, 0x94bf, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9be1, 0x8af3, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9be4, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x929f, 0xfffd, 0xfffd, 0x9be3, 0x9be2, 0x9be5, 0xfffd, 0x92e9, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9083, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8e74, 0xfffd, 0x90c8, 0xfffd, 0x91d1, 0x8b41, 0xfffd, 0xfffd, 0x92a0, 0xfffd, 0xfffd, 0x9be6, 0x9be7, 0x8fed, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9658, 0xfffd, 0xfffd, 0x9bea, 0xfffd, 0xfffd, 0x9be9, 0x9be8, 0x959d, 0xfffd, 0x9bf1, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9679, 0xfffd, 0x9beb, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9bed, 0x968b, 0xfffd, 0x9bec, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9bee, 0xfffd, 0x94a6, 0x9bef, 0x95bc, 0x9bf0, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8ab1, 0x95bd, 0x944e, 0x9bf2, 0x9bf3, 0xfffd, 0x8d4b, 0x8ab2, 0x9bf4, 0x8cb6, 0x9763, 0x9748, 0x8af4, 0x9bf6, 0xfffd, 0x92a1, 0xfffd, 0x8d4c, 0x8faf, 0xfffd, 0xfffd, 0x94dd, 0xfffd, 0xfffd, 0x8fb0, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8f98, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x92ea, 0x95f7, 0x9358, 0xfffd, 0xfffd, 0x8d4d, 0xfffd, 0x957b, 0xfffd, 0xfffd, 0xfffd, 0x9bf7, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9378, 0x8dc0, 0xfffd, 0xfffd, 0xfffd, 0x8cc9, 0xfffd, 0x92eb, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x88c1, 0x8f8e, 0x8d4e, 0x9766, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9bf8, 0x9bf9, 0x9470, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9bfa, 0x97f5, 0x984c, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9bfc, 0x9bfb, 0xfffd, 0xfffd, 0x8a66, 0xfffd, 0xfffd, 0x9c40, 0xfffd, 0xfffd, 0xfffd, 0x9c43, 0x9c44, 0xfffd, 0x9c42, 0xfffd, 0x955f, 0x8fb1, 0x9c46, 0x9c45, 0x9c41, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9c47, 0x9c48, 0xfffd, 0xfffd, 0x9c49, 0xfffd, 0xfffd, 0xfffd, 0x9c4c, 0x9c4a, 0xfffd, 0x9c4b, 0x9c4d, 0xfffd, 0x8984, 0x92ec, 0x9c4e, 0xfffd, 0x8c9a, 0x89f4, 0x9455, 0xfffd, 0x9c4f, 0x93f9, 0xfffd, 0x95d9, 0xfffd, 0x9c50, 0x984d, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9c51, 0x95be, 0x9c54, 0x989f, 0x98af, 0xfffd, 0x8eae, 0x93f3, 0x9c55, 0xfffd, 0x8b7c, 0x92a2, 0x88f8, 0x9c56, 0x95a4, 0x8d4f, 0xfffd, 0xfffd, 0x926f, 0xfffd, 0xfffd, 0xfffd, 0x92ed, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x96ed, 0x8cb7, 0x8cca, 0xfffd, 0x9c57, 0xfffd, 0xfffd, 0xfffd, 0x9c58, 0xfffd, 0x9c5e, 0xfffd, 0x8ee3, 0xfffd, 0xfffd, 0xfffd, 0x92a3, 0xfffd, 0x8bad, 0x9c59, 0xfffd, 0xfffd, 0xfffd, 0x954a, 0xfffd, 0x9265, 0xfffd, 0xfffd, 0x9c5a, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9c5b, 0xfffd, 0x8bae, 0xfffd, 0x9c5c, 0xfffd, 0x9c5d, 0xfffd, 0xfffd, 0x9c5f, 0xfffd, 0x9396, 0xfffd, 0xfffd, 0x9c60, 0x9c61, 0xfffd, 0x9c62, 0xfffd, 0xfffd, 0x9c53, 0x9c52, 0xfffd, 0xfffd, 0xfffd, 0x9c63, 0x8c60, 0xfffd, 0xfffd, 0xfffd, 0x9546, 0xfffd, 0xfffd, 0x8dca, 0x9556, 0x92a4, 0x956a, 0x9c64, 0xfffd, 0xfffd, 0x8fb2, 0x8965, 0xfffd, 0x9c65, 0xfffd, 0xfffd, 0xfffd, 0x9c66, 0xfffd, 0x96f0, 0xfffd, 0xfffd, 0x94de, 0xfffd, 0xfffd, 0x9c69, 0x899d, 0x90aa, 0x9c68, 0x9c67, 0x8c61, 0x91d2, 0xfffd, 0x9c6d, 0x9c6b, 0xfffd, 0x9c6a, 0x97a5, 0x8ce3, 0xfffd, 0xfffd, 0xfffd, 0x8f99, 0x9c6c, 0x936b, 0x8f5d, 0xfffd, 0xfffd, 0xfffd, 0x93be, 0x9c70, 0x9c6f, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9c6e, 0xfffd, 0x9c71, 0x8ce4, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9c72, 0x959c, 0x8f7a, 0xfffd, 0xfffd, 0x9c73, 0x94f7, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x93bf, 0x92a5, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x934f, 0xfffd, 0xfffd, 0x9c74, 0x8b4a, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9053, 0xfffd, 0x954b, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8af5, 0x9445, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9c75, 0x8e75, 0x9659, 0x965a, 0xfffd, 0xfffd, 0x899e, 0x9c7a, 0xfffd, 0xfffd, 0x9289, 0xfffd, 0xfffd, 0xfffd, 0x9c77, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x89f5, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9cab, 0x9c79, 0xfffd, 0xfffd, 0xfffd, 0x944f, 0xfffd, 0xfffd, 0x9c78, 0xfffd, 0xfffd, 0x9c76, 0xfffd, 0x8d9a, 0xfffd, 0x9c7c, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9c83, 0x9c89, 0x9c81, 0xfffd, 0x937b, 0xfffd, 0xfffd, 0x9c86, 0x957c, 0xfffd, 0xfffd, 0x9c80, 0xfffd, 0x9c85, 0x97e5, 0x8e76, 0xfffd, 0xfffd, 0x91d3, 0x9c7d, 0xfffd, 0xfffd, 0xfffd, 0x8b7d, 0x9c88, 0x90ab, 0x8985, 0x9c82, 0x89f6, 0x9c87, 0xfffd, 0xfffd, 0xfffd, 0x8baf, 0xfffd, 0x9c84, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9c8a, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9c8c, 0x9c96, 0x9c94, 0xfffd, 0xfffd, 0x9c91, 0xfffd, 0xfffd, 0xfffd, 0x9c90, 0x97f6, 0xfffd, 0x9c92, 0xfffd, 0xfffd, 0x8bb0, 0xfffd, 0x8d50, 0xfffd, 0xfffd, 0x8f9a, 0xfffd, 0xfffd, 0xfffd, 0x9c99, 0x9c8b, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9c8f, 0x9c7e, 0xfffd, 0x89f8, 0x9c93, 0x9c95, 0x9270, 0xfffd, 0xfffd, 0x8da6, 0x89b6, 0x9c8d, 0x9c98, 0x9c97, 0x8bb1, 0xfffd, 0x91a7, 0x8a86, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8c62, 0xfffd, 0x9c8e, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9c9a, 0xfffd, 0x9c9d, 0x9c9f, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8ebb, 0xfffd, 0x9ca5, 0x92ee, 0x9c9b, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9ca3, 0xfffd, 0x89f7, 0xfffd, 0x9ca1, 0x9ca2, 0xfffd, 0xfffd, 0x9c9e, 0x9ca0, 0xfffd, 0xfffd, 0xfffd, 0x8ce5, 0x9749, 0xfffd, 0xfffd, 0x8ab3, 0xfffd, 0xfffd, 0x8978, 0x9ca4, 0xfffd, 0x9459, 0x88ab, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x94df, 0x9c7b, 0x9caa, 0x9cae, 0x96e3, 0xfffd, 0x9ca7, 0xfffd, 0xfffd, 0xfffd, 0x9389, 0x9cac, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8fee, 0x9cad, 0x93d5, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9866, 0xfffd, 0x9ca9, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9caf, 0xfffd, 0x8d9b, 0xfffd, 0x90c9, 0xfffd, 0xfffd, 0x88d2, 0x9ca8, 0x9ca6, 0xfffd, 0x9179, 0xfffd, 0xfffd, 0xfffd, 0x9c9c, 0x8e53, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x91c4, 0x9cbb, 0xfffd, 0x917a, 0x9cb6, 0xfffd, 0x9cb3, 0x9cb4, 0xfffd, 0x8ee4, 0x9cb7, 0x9cba, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9cb5, 0x8f44, 0xfffd, 0x9cb8, 0xfffd, 0xfffd, 0x9cb2, 0xfffd, 0x96fa, 0x96f9, 0xfffd, 0xfffd, 0xfffd, 0x9cbc, 0x9cbd, 0x88d3, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9cb1, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8bf0, 0x88a4, 0xfffd, 0xfffd, 0xfffd, 0x8ab4, 0xfffd, 0x9cb9, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9cc1, 0x9cc0, 0xfffd, 0xfffd, 0xfffd, 0x9cc5, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9cc6, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9cc4, 0x9cc7, 0x9cbf, 0x9cc3, 0xfffd, 0xfffd, 0x9cc8, 0xfffd, 0x9cc9, 0xfffd, 0xfffd, 0x9cbe, 0x8e9c, 0xfffd, 0x9cc2, 0x91d4, 0x8d51, 0x9cb0, 0x9054, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9cd6, 0xfffd, 0x95e7, 0xfffd, 0xfffd, 0x9ccc, 0x9ccd, 0x9cce, 0xfffd, 0xfffd, 0x9cd5, 0xfffd, 0x9cd4, 0xfffd, 0xfffd, 0x969d, 0x8ab5, 0xfffd, 0x9cd2, 0xfffd, 0x8c64, 0x8a53, 0xfffd, 0xfffd, 0x9ccf, 0xfffd, 0xfffd, 0x97b6, 0x9cd1, 0x88d4, 0x9cd3, 0xfffd, 0x9cca, 0x9cd0, 0x9cd7, 0x8c63, 0x9ccb, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x977c, 0xfffd, 0xfffd, 0xfffd, 0x974a, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9cda, 0xfffd, 0xfffd, 0x9cde, 0xfffd, 0xfffd, 0xfffd, 0x919e, 0xfffd, 0x97f7, 0x9cdf, 0xfffd, 0xfffd, 0x9cdc, 0xfffd, 0x9cd9, 0xfffd, 0xfffd, 0x9cd8, 0x9cdd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x95ae, 0xfffd, 0xfffd, 0x93b2, 0xfffd, 0x8c65, 0xfffd, 0x9ce0, 0x9cdb, 0xfffd, 0x9ce1, 0xfffd, 0xfffd, 0xfffd, 0x8c9b, 0xfffd, 0xfffd, 0xfffd, 0x89af, 0xfffd, 0xfffd, 0xfffd, 0x9ce9, 0xfffd, 0xfffd, 0xfffd, 0x8ab6, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9ce7, 0xfffd, 0xfffd, 0x9ce8, 0x8da7, 0x9ce6, 0x9ce4, 0x9ce3, 0x9cea, 0x9ce2, 0x9cec, 0xfffd, 0xfffd, 0x89f9, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9cee, 0xfffd, 0xfffd, 0x9ced, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x92a6, 0xfffd, 0x9cf1, 0xfffd, 0x9cef, 0x9ce5, 0x8c9c, 0xfffd, 0x9cf0, 0xfffd, 0x9cf4, 0x9cf3, 0x9cf5, 0x9cf2, 0x9cf6, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9cf7, 0x9cf8, 0x95e8, 0xfffd, 0x9cfa, 0x9cf9, 0x8f5e, 0xfffd, 0x90ac, 0x89e4, 0x89fa, 0xfffd, 0x9cfb, 0xfffd, 0x88bd, 0xfffd, 0xfffd, 0xfffd, 0x90ca, 0x9cfc, 0xfffd, 0xe6c1, 0x9d40, 0x8c81, 0xfffd, 0x9d41, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x90ed, 0xfffd, 0xfffd, 0xfffd, 0x9d42, 0xfffd, 0xfffd, 0xfffd, 0x9d43, 0x8b59, 0x9d44, 0xfffd, 0x9d45, 0x9d46, 0x91d5, 0xfffd, 0xfffd, 0xfffd, 0x8ccb, 0xfffd, 0xfffd, 0x96df, 0xfffd, 0xfffd, 0xfffd, 0x965b, 0x8f8a, 0x9d47, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x90ee, 0xe7bb, 0x94e0, 0xfffd, 0x8ee8, 0xfffd, 0x8dcb, 0x9d48, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x91c5, 0xfffd, 0x95a5, 0xfffd, 0xfffd, 0x91ef, 0xfffd, 0xfffd, 0x9d4b, 0xfffd, 0xfffd, 0x9d49, 0xfffd, 0x9d4c, 0xfffd, 0xfffd, 0x9d4a, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9d4d, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x95af, 0xfffd, 0xfffd, 0x88b5, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x957d, 0xfffd, 0xfffd, 0x94e1, 0xfffd, 0xfffd, 0x9d4e, 0xfffd, 0x9d51, 0x8fb3, 0x8b5a, 0xfffd, 0x9d4f, 0x9d56, 0x8fb4, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9d50, 0x9463, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x977d, 0x9d52, 0x9d53, 0x9d57, 0x938a, 0x9d54, 0x8d52, 0x90dc, 0xfffd, 0xfffd, 0x9d65, 0x94b2, 0xfffd, 0x91f0, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x94e2, 0x9dab, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x95f8, 0xfffd, 0xfffd, 0xfffd, 0x92ef, 0xfffd, 0xfffd, 0xfffd, 0x9695, 0xfffd, 0x9d5a, 0x899f, 0x928a, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9d63, 0xfffd, 0xfffd, 0x9253, 0x9d5d, 0x9d64, 0x9d5f, 0x9d66, 0x9d62, 0xfffd, 0x9d61, 0x948f, 0xfffd, 0x9d5b, 0x89fb, 0x9d59, 0x8b91, 0x91f1, 0x9d55, 0xfffd, 0xfffd, 0x9d58, 0x8d53, 0x90d9, 0xfffd, 0x8fb5, 0x9d60, 0x9471, 0xfffd, 0xfffd, 0x8b92, 0x8a67, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8a87, 0x9040, 0x9d68, 0x9d6d, 0xfffd, 0x9d69, 0xfffd, 0x8c9d, 0xfffd, 0x9d6e, 0x8e41, 0x8d89, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8f45, 0x9d5c, 0xfffd, 0x8e9d, 0x9d6b, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8e77, 0x9d6c, 0x88c2, 0xfffd, 0xfffd, 0x9d67, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x92a7, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8b93, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8bb2, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9d6a, 0x88a5, 0xfffd, 0xfffd, 0x8dc1, 0xfffd, 0xfffd, 0xfffd, 0x9055, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x92f0, 0xfffd, 0xfffd, 0x94d2, 0x9d70, 0x917d, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x91a8, 0xfffd, 0xfffd, 0x8e4a, 0x9d71, 0xfffd, 0x9d73, 0x9d6f, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x95df, 0xfffd, 0x92bb, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x917b, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x95f9, 0x8ecc, 0x9d80, 0xfffd, 0x9d7e, 0xfffd, 0xfffd, 0x9098, 0xfffd, 0xfffd, 0xfffd, 0x8c9e, 0xfffd, 0xfffd, 0xfffd, 0x9d78, 0x8fb7, 0xfffd, 0xfffd, 0x93e6, 0x9450, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9d76, 0xfffd, 0xfffd, 0x917c, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8ef6, 0x9d7b, 0xfffd, 0xfffd, 0x8fb6, 0xfffd, 0x9d75, 0x9d7a, 0xfffd, 0xfffd, 0x9472, 0xfffd, 0xfffd, 0xfffd, 0x9d74, 0xfffd, 0x8c40, 0xfffd, 0xfffd, 0x8a7c, 0xfffd, 0xfffd, 0xfffd, 0x9d7c, 0x97a9, 0x8dcc, 0x9254, 0x9d79, 0xfffd, 0x90da, 0xfffd, 0x8d54, 0x9084, 0x8986, 0x915b, 0x9d77, 0x8b64, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8c66, 0xfffd, 0x92cd, 0x9d7d, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x917e, 0xfffd, 0xfffd, 0x9d81, 0xfffd, 0x9d83, 0xfffd, 0xfffd, 0x91b5, 0x9d89, 0xfffd, 0x9d84, 0xfffd, 0xfffd, 0x9d86, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9560, 0x92f1, 0xfffd, 0x9d87, 0xfffd, 0xfffd, 0xfffd, 0x974b, 0xfffd, 0xfffd, 0xfffd, 0x9767, 0x8ab7, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x88ac, 0xfffd, 0x9d85, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9d82, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8af6, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8987, 0xfffd, 0x9d88, 0xfffd, 0xfffd, 0xfffd, 0x9768, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9d8c, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x91b9, 0xfffd, 0x9d93, 0xfffd, 0xfffd, 0xfffd, 0x9d8d, 0xfffd, 0xfffd, 0x9d8a, 0x9d91, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9d72, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9d8e, 0xfffd, 0x9d92, 0xfffd, 0xfffd, 0xfffd, 0x94c0, 0x938b, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9d8b, 0xfffd, 0x9d8f, 0xfffd, 0xfffd, 0xfffd, 0x8c67, 0xfffd, 0xfffd, 0xfffd, 0x8def, 0xfffd, 0xfffd, 0xfffd, 0x90db, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9d97, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9345, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9d94, 0xfffd, 0x9680, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9d95, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9d96, 0xfffd, 0x96cc, 0xfffd, 0x90a0, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8c82, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9d9d, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8e54, 0x9d9a, 0xfffd, 0x9d99, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9451, 0xfffd, 0xfffd, 0xfffd, 0x93b3, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9350, 0x9d9b, 0xfffd, 0xfffd, 0xfffd, 0x9d9c, 0xfffd, 0x958f, 0xfffd, 0x9464, 0x8e42, 0xfffd, 0x90ef, 0xfffd, 0x966f, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8a68, 0xfffd, 0x9da3, 0x9d9e, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9769, 0x9da5, 0xfffd, 0xfffd, 0x9da1, 0xfffd, 0x9da2, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9180, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9da0, 0xfffd, 0x9d5e, 0xfffd, 0xfffd, 0xfffd, 0x9da4, 0xfffd, 0x9d9f, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9da9, 0x9daa, 0x9346, 0x9dac, 0xfffd, 0xfffd, 0x8e43, 0x9da7, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8b5b, 0xfffd, 0xfffd, 0x9dad, 0xfffd, 0x9da6, 0x9db1, 0xfffd, 0x9db0, 0xfffd, 0x9daf, 0xfffd, 0xfffd, 0xfffd, 0x9db2, 0xfffd, 0xfffd, 0x9db4, 0x8fef, 0xfffd, 0x9db3, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9db7, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9db5, 0xfffd, 0xfffd, 0xfffd, 0x9db6, 0x9d90, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9db9, 0x9db8, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9d98, 0x9dba, 0x9dae, 0xfffd, 0xfffd, 0x8e78, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9dbb, 0x9dbc, 0x9dbe, 0x9dbd, 0x9dbf, 0x89fc, 0xfffd, 0x8d55, 0xfffd, 0xfffd, 0x95fa, 0x90ad, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8ccc, 0xfffd, 0xfffd, 0x9dc1, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9dc4, 0xfffd, 0x9571, 0xfffd, 0x8b7e, 0xfffd, 0xfffd, 0xfffd, 0x9dc3, 0x9dc2, 0x9473, 0x9dc5, 0x8bb3, 0xfffd, 0xfffd, 0xfffd, 0x9dc7, 0x9dc6, 0xfffd, 0xfffd, 0xfffd, 0x8ab8, 0x8e55, 0xfffd, 0xfffd, 0x93d6, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8c68, 0xfffd, 0xfffd, 0xfffd, 0x9094, 0xfffd, 0x9dc8, 0xfffd, 0x90ae, 0x9347, 0xfffd, 0x957e, 0x9dc9, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9dca, 0x9dcb, 0xfffd, 0xfffd, 0xfffd, 0x95b6, 0x9b7c, 0x90c4, 0xfffd, 0xfffd, 0x956b, 0xfffd, 0x8dd6, 0xfffd, 0x94e3, 0x94c1, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x936c, 0xfffd, 0x97bf, 0xfffd, 0x9dcd, 0x8ece, 0xfffd, 0xfffd, 0x9dce, 0xfffd, 0x88b4, 0xfffd, 0xfffd, 0x8bd2, 0x90cb, 0xfffd, 0x9580, 0xfffd, 0xfffd, 0xfffd, 0x9dcf, 0x8e61, 0x9266, 0xfffd, 0x8e7a, 0x9056, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9dd0, 0xfffd, 0x95fb, 0xfffd, 0xfffd, 0x8997, 0x8e7b, 0xfffd, 0xfffd, 0xfffd, 0x9dd3, 0xfffd, 0x9dd1, 0x9dd4, 0x97b7, 0x9dd2, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x90f9, 0x9dd5, 0xfffd, 0xfffd, 0x91b0, 0xfffd, 0xfffd, 0x9dd6, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8af8, 0xfffd, 0x9dd8, 0xfffd, 0x9dd7, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9dd9, 0x9dda, 0x8af9, 0xfffd, 0xfffd, 0x93fa, 0x9255, 0x8b8c, 0x8e7c, 0x9181, 0xfffd, 0xfffd, 0x8f7b, 0x88ae, 0xfffd, 0xfffd, 0xfffd, 0x9ddb, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x89a0, 0x9ddf, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8d56, 0x9dde, 0xfffd, 0xfffd, 0x8da9, 0x8fb8, 0xfffd, 0xfffd, 0x9ddd, 0xfffd, 0x8fb9, 0xfffd, 0x96be, 0x8da8, 0xfffd, 0xfffd, 0xfffd, 0x88d5, 0x90cc, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9de4, 0xfffd, 0xfffd, 0x90af, 0x8966, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8f74, 0xfffd, 0x9686, 0x8df0, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8fba, 0xfffd, 0x90a5, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9de3, 0x9de1, 0x9de2, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x928b, 0xfffd, 0xfffd, 0x9e45, 0xfffd, 0x9de8, 0x8e9e, 0x8d57, 0x9de6, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9de7, 0xfffd, 0x9057, 0xfffd, 0xfffd, 0xfffd, 0x9de5, 0xfffd, 0xfffd, 0x8e4e, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9dea, 0x9de9, 0x9dee, 0xfffd, 0xfffd, 0x9def, 0xfffd, 0x9deb, 0xfffd, 0x8a41, 0x9dec, 0x9ded, 0x94d3, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9581, 0x8c69, 0x9df0, 0xfffd, 0xfffd, 0xfffd, 0x90b0, 0xfffd, 0x8fbb, 0xfffd, 0xfffd, 0xfffd, 0x9271, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8bc5, 0xfffd, 0x9df1, 0x9df5, 0xfffd, 0xfffd, 0x89c9, 0x9df2, 0x9df4, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9df3, 0xfffd, 0xfffd, 0x8f8b, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9267, 0x88c3, 0x9df6, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9df7, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x92a8, 0xfffd, 0xfffd, 0xfffd, 0x97ef, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8e62, 0xfffd, 0xfffd, 0x95e9, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x965c, 0xfffd, 0xfffd, 0xfffd, 0x9e41, 0x9df9, 0xfffd, 0xfffd, 0x9dfc, 0xfffd, 0x9dfb, 0xfffd, 0xfffd, 0x9df8, 0xfffd, 0xfffd, 0x9e40, 0xfffd, 0xfffd, 0x93dc, 0xfffd, 0x9dfa, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9e42, 0xfffd, 0xfffd, 0x8f8c, 0x9e43, 0xfffd, 0x976a, 0x9498, 0xfffd, 0xfffd, 0x9e44, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9e46, 0xfffd, 0xfffd, 0x9e47, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9e48, 0xfffd, 0x8bc8, 0x8967, 0x8d58, 0x9e49, 0xfffd, 0x9e4a, 0x8f91, 0x9182, 0xfffd, 0xfffd, 0x99d6, 0x915d, 0x915c, 0x91d6, 0x8dc5, 0xfffd, 0xfffd, 0x98f0, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8c8e, 0x974c, 0xfffd, 0x95fc, 0xfffd, 0x959e, 0xfffd, 0x9e4b, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8df1, 0x92bd, 0x9e4c, 0x984e, 0xfffd, 0xfffd, 0xfffd, 0x965d, 0xfffd, 0x92a9, 0x9e4d, 0x8afa, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9e4e, 0x9e4f, 0x96d8, 0xfffd, 0x96a2, 0x9696, 0x967b, 0x8e44, 0x9e51, 0xfffd, 0xfffd, 0x8ee9, 0xfffd, 0xfffd, 0x9670, 0xfffd, 0x9e53, 0x9e56, 0x9e55, 0xfffd, 0x8af7, 0xfffd, 0xfffd, 0x8b80, 0xfffd, 0x9e52, 0xfffd, 0x9e54, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9e57, 0xfffd, 0xfffd, 0x9099, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x979b, 0x88c7, 0x8dde, 0x91ba, 0xfffd, 0x8edb, 0xfffd, 0xfffd, 0x8ff1, 0xfffd, 0xfffd, 0x9e5a, 0xfffd, 0xfffd, 0x936d, 0xfffd, 0x9e58, 0x91a9, 0x9e59, 0x8ff0, 0x96db, 0x9e5b, 0x9e5c, 0x9788, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9e61, 0xfffd, 0xfffd, 0x8d59, 0xfffd, 0x9474, 0x9e5e, 0x938c, 0x9ddc, 0x9de0, 0xfffd, 0x8b6e, 0xfffd, 0x9466, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9e60, 0xfffd, 0x8fbc, 0x94c2, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9e66, 0xfffd, 0x94f8, 0xfffd, 0x9e5d, 0xfffd, 0x9e63, 0x9e62, 0xfffd, 0xfffd, 0xfffd, 0x90cd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x968d, 0xfffd, 0x97d1, 0xfffd, 0xfffd, 0x9687, 0xfffd, 0x89ca, 0x8e7d, 0xfffd, 0xfffd, 0x9867, 0x9e65, 0x9095, 0xfffd, 0xfffd, 0xfffd, 0x9e64, 0xfffd, 0xfffd, 0x9e5f, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8ccd, 0xfffd, 0xfffd, 0xfffd, 0x9e6b, 0x9e69, 0xfffd, 0x89cb, 0x9e67, 0x9e6d, 0x9e73, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x91c6, 0xfffd, 0xfffd, 0x95bf, 0xfffd, 0x9e75, 0xfffd, 0xfffd, 0xfffd, 0x9541, 0xfffd, 0xfffd, 0xfffd, 0x9e74, 0x9490, 0x965e, 0x8ab9, 0xfffd, 0x90f5, 0x8f5f, 0xfffd, 0xfffd, 0xfffd, 0x92d1, 0xfffd, 0x974d, 0xfffd, 0xfffd, 0x9e70, 0x9e6f, 0xfffd, 0xfffd, 0xfffd, 0x9e71, 0xfffd, 0x9e6e, 0xfffd, 0xfffd, 0x9e76, 0xfffd, 0x9e6c, 0xfffd, 0xfffd, 0x9e6a, 0xfffd, 0x9e72, 0x9e68, 0xfffd, 0x928c, 0xfffd, 0x96f6, 0x8ec4, 0x8df2, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8db8, 0xfffd, 0xfffd, 0x968f, 0x8a60, 0xfffd, 0xfffd, 0x92cc, 0x93c8, 0x8968, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x90f0, 0xfffd, 0xfffd, 0x90b2, 0x8c49, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9e78, 0xfffd, 0xfffd, 0x8d5a, 0x8a9c, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9e7a, 0x8a94, 0x9e81, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9e7d, 0xfffd, 0x90f1, 0xfffd, 0xfffd, 0xfffd, 0x8a6a, 0x8daa, 0xfffd, 0xfffd, 0x8a69, 0x8dcd, 0xfffd, 0xfffd, 0x9e7b, 0x8c85, 0x8c6a, 0x938d, 0xfffd, 0xfffd, 0x9e79, 0xfffd, 0x88c4, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9e7c, 0x9e7e, 0xfffd, 0x8bcb, 0x8c4b, 0xfffd, 0x8aba, 0x8b6a, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9e82, 0xfffd, 0xfffd, 0x8df7, 0x9691, 0xfffd, 0x8e56, 0xfffd, 0xfffd, 0xfffd, 0x9e83, 0xfffd, 0xfffd, 0xfffd, 0x954f, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9e8f, 0xfffd, 0x89b1, 0x9e84, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9e95, 0x9e85, 0xfffd, 0x97c0, 0xfffd, 0x9e8c, 0xfffd, 0x947e, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9e94, 0xfffd, 0x9e87, 0xfffd, 0xfffd, 0xfffd, 0x88b2, 0x9e89, 0xfffd, 0xfffd, 0x8d5b, 0xfffd, 0xfffd, 0xfffd, 0x9e8b, 0xfffd, 0x9e8a, 0xfffd, 0x9e86, 0x9e91, 0xfffd, 0x8fbd, 0xfffd, 0xfffd, 0xfffd, 0x9aeb, 0x8ce6, 0x979c, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9e88, 0xfffd, 0x92f2, 0x8a42, 0x8dab, 0xfffd, 0x9e80, 0xfffd, 0x9e90, 0x8a81, 0xfffd, 0xfffd, 0x9e8e, 0x9e92, 0xfffd, 0x938e, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8afc, 0xfffd, 0x9eb0, 0xfffd, 0xfffd, 0x96c7, 0x9e97, 0x8afb, 0xfffd, 0x9e9e, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x965f, 0xfffd, 0x9e9f, 0x9ea1, 0xfffd, 0x9ea5, 0x9e99, 0xfffd, 0x9249, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x938f, 0x9ea9, 0x9e9c, 0xfffd, 0x9ea6, 0xfffd, 0xfffd, 0xfffd, 0x9ea0, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9058, 0x9eaa, 0xfffd, 0xfffd, 0x90b1, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9ea8, 0x8abb, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x986f, 0x9e96, 0xfffd, 0xfffd, 0x9ea4, 0x88d6, 0xfffd, 0xfffd, 0x9e98, 0xfffd, 0xfffd, 0x96b8, 0x9e9d, 0x9041, 0x92c5, 0x9e93, 0xfffd, 0xfffd, 0x9ea3, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x909a, 0x9ead, 0x8a91, 0x8c9f, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9eaf, 0x9e9a, 0x9eae, 0xfffd, 0x9ea7, 0x9e9b, 0xfffd, 0x9eab, 0xfffd, 0x9eac, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9ebd, 0xfffd, 0xfffd, 0xfffd, 0x93cc, 0xfffd, 0x9ea2, 0xfffd, 0xfffd, 0x9eb9, 0xfffd, 0xfffd, 0xfffd, 0x9ebb, 0xfffd, 0x92d6, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x976b, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9596, 0x9eb6, 0x91c8, 0xfffd, 0xfffd, 0xfffd, 0x9ebc, 0x915e, 0xfffd, 0x9eb3, 0x9ec0, 0x9ebf, 0xfffd, 0x93ed, 0x9ebe, 0x93e8, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9ec2, 0x9eb5, 0xfffd, 0x8bc6, 0x9eb8, 0x8f7c, 0xfffd, 0xfffd, 0xfffd, 0x9480, 0x9eba, 0x8bc9, 0xfffd, 0x9eb2, 0x9eb4, 0x9eb1, 0xfffd, 0xfffd, 0x984f, 0x8a79, 0x9eb7, 0xfffd, 0xfffd, 0x9ec1, 0x8a54, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8de5, 0xfffd, 0xfffd, 0xfffd, 0x897c, 0xfffd, 0xfffd, 0x9ed2, 0xfffd, 0xfffd, 0x9850, 0x9ed5, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9059, 0x9ed4, 0xfffd, 0xfffd, 0xfffd, 0x9ed3, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9ed0, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9ec4, 0xfffd, 0xfffd, 0x9ee1, 0x9ec3, 0xfffd, 0x9ed6, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9ece, 0xfffd, 0xfffd, 0x9ec9, 0x9ec6, 0xfffd, 0x9ec7, 0xfffd, 0x9ecf, 0xfffd, 0xfffd, 0xfffd, 0xeaa0, 0xfffd, 0xfffd, 0x9ecc, 0x8d5c, 0x92c6, 0x9184, 0x9eca, 0xfffd, 0x9ec5, 0xfffd, 0xfffd, 0x9ec8, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x976c, 0x968a, 0xfffd, 0xfffd, 0xfffd, 0x9ecd, 0x9ed7, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9edf, 0x9ed8, 0xfffd, 0xfffd, 0x9ee5, 0xfffd, 0x9ee3, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9ede, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9edd, 0xfffd, 0x92ce, 0xfffd, 0x9185, 0xfffd, 0x9edb, 0xfffd, 0xfffd, 0x9ed9, 0xfffd, 0xfffd, 0x9ee0, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9ee6, 0x94f3, 0x9eec, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9ee7, 0x9eea, 0x9ee4, 0xfffd, 0xfffd, 0x9294, 0xfffd, 0x9557, 0xfffd, 0x9eda, 0xfffd, 0xfffd, 0x9ee2, 0x8fbe, 0xfffd, 0x96cd, 0x9ef6, 0x9ee9, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8ca0, 0x89a1, 0x8a7e, 0xfffd, 0xfffd, 0x9ed1, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8fbf, 0x9eee, 0xfffd, 0x9ef5, 0x8ef7, 0x8a92, 0xfffd, 0xfffd, 0x924d, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9eeb, 0xfffd, 0xfffd, 0x9ef0, 0x9ef4, 0xfffd, 0xfffd, 0x8bb4, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8b6b, 0x9ef2, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8b40, 0xfffd, 0x93c9, 0x9ef1, 0xfffd, 0xfffd, 0xfffd, 0x9ef3, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9eed, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9eef, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8a80, 0x9268, 0xfffd, 0xfffd, 0xfffd, 0x9efa, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9ef8, 0x8ce7, 0xfffd, 0x9ef7, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9f40, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9e77, 0xfffd, 0xfffd, 0xfffd, 0x9ef9, 0xfffd, 0x9efb, 0x9efc, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9f4b, 0xfffd, 0x9f47, 0xfffd, 0x9e8d, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9f46, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9f45, 0xfffd, 0xfffd, 0x9f42, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9ee8, 0x9f44, 0x9f43, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9f49, 0xfffd, 0x9845, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9f4c, 0x8bf9, 0xfffd, 0xfffd, 0x9f48, 0x9f4a, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x94a5, 0xfffd, 0x9f4d, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9f51, 0x9f4e, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9793, 0x9f4f, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9edc, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9f52, 0xfffd, 0xfffd, 0xfffd, 0x9f53, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8954, 0xfffd, 0x9f55, 0x8c87, 0x8e9f, 0xfffd, 0x8bd3, 0xfffd, 0xfffd, 0xfffd, 0x89a2, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x977e, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9f57, 0x9f56, 0x9f59, 0x8b5c, 0xfffd, 0xfffd, 0x8bd4, 0x8abc, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9f5c, 0xfffd, 0xfffd, 0xfffd, 0x9f5b, 0xfffd, 0x9f5d, 0xfffd, 0xfffd, 0x89cc, 0xfffd, 0x9256, 0xfffd, 0x9f5e, 0xfffd, 0xfffd, 0x8abd, 0x9f60, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9f5f, 0xfffd, 0x9f61, 0xfffd, 0xfffd, 0xfffd, 0x9f62, 0xfffd, 0x9f63, 0x8e7e, 0x90b3, 0x8d9f, 0xfffd, 0x9590, 0xfffd, 0xfffd, 0x95e0, 0x9863, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8e95, 0xfffd, 0xfffd, 0xfffd, 0x8dce, 0x97f0, 0xfffd, 0xfffd, 0xfffd, 0x9f64, 0x9f65, 0xfffd, 0x8e80, 0xfffd, 0xfffd, 0xfffd, 0x9f66, 0x9f67, 0xfffd, 0xfffd, 0x9f69, 0x9f68, 0xfffd, 0x9677, 0xfffd, 0xfffd, 0x8f7d, 0x8eea, 0x8e63, 0xfffd, 0x9f6a, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9f6c, 0x9042, 0xfffd, 0x9f6b, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9f6d, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9f6e, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9f6f, 0x9f70, 0xfffd, 0xfffd, 0xfffd, 0x9f71, 0xfffd, 0x9f73, 0x9f72, 0x9f74, 0x89a3, 0x9269, 0xfffd, 0x9f75, 0xfffd, 0xfffd, 0x8e45, 0x8a6b, 0x9f76, 0xfffd, 0xfffd, 0x9361, 0x9aca, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8b42, 0x9f77, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9f78, 0xfffd, 0x95ea, 0x9688, 0xfffd, 0xfffd, 0xfffd, 0x93c5, 0x9f79, 0x94e4, 0xfffd, 0xfffd, 0xfffd, 0x94f9, 0xfffd, 0xfffd, 0x96d1, 0xfffd, 0xfffd, 0xfffd, 0x9f7a, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9f7c, 0x9f7b, 0xfffd, 0xfffd, 0x9f7e, 0xfffd, 0xfffd, 0xfffd, 0x9f7d, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9f81, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8e81, 0xfffd, 0x96af, 0xfffd, 0x9f82, 0x9f83, 0xfffd, 0xfffd, 0x8b43, 0xfffd, 0xfffd, 0xfffd, 0x9f84, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9f86, 0x9f85, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9085, 0xfffd, 0xfffd, 0x9558, 0x8969, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x94c3, 0xfffd, 0x92f3, 0x8f60, 0x8b81, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x94c4, 0xfffd, 0x8eac, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9f88, 0xfffd, 0x8abe, 0xfffd, 0xfffd, 0x8998, 0xfffd, 0xfffd, 0x93f0, 0x9f87, 0x8d5d, 0x9272, 0xfffd, 0x9f89, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9f91, 0xfffd, 0x9f8a, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x91bf, 0xfffd, 0x8b82, 0x9f92, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8c88, 0xfffd, 0xfffd, 0x8b44, 0x9f90, 0xfffd, 0xfffd, 0x9f8e, 0x9f8b, 0x9780, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x92be, 0xfffd, 0xfffd, 0xfffd, 0x93d7, 0x9f8c, 0xfffd, 0xfffd, 0x9f94, 0xfffd, 0x9f93, 0x8c42, 0xfffd, 0xfffd, 0x89ab, 0xfffd, 0xfffd, 0x8db9, 0x9f8d, 0x9f8f, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9676, 0x91f2, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9697, 0xfffd, 0xfffd, 0x9f9c, 0xfffd, 0xfffd, 0x9f9d, 0xfffd, 0x89cd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x95a6, 0x96fb, 0x9f9f, 0x8ea1, 0x8fc0, 0x9f98, 0x9f9e, 0x8988, 0xfffd, 0x8bb5, 0xfffd, 0xfffd, 0x9f95, 0x9f9a, 0xfffd, 0xfffd, 0xfffd, 0x90f2, 0x9491, 0xfffd, 0x94e5, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9f97, 0xfffd, 0x9640, 0xfffd, 0x9f99, 0xfffd, 0x9fa2, 0xfffd, 0x9fa0, 0xfffd, 0x9f9b, 0xfffd, 0xfffd, 0xfffd, 0x9641, 0x9467, 0x8b83, 0xfffd, 0x9344, 0xfffd, 0xfffd, 0x928d, 0xfffd, 0x9fa3, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9fa1, 0x91d7, 0x9f96, 0xfffd, 0x896a, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x976d, 0x9fae, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9fad, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x90f4, 0xfffd, 0x9faa, 0xfffd, 0x978c, 0xfffd, 0xfffd, 0x93b4, 0x9fa4, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x92c3, 0xfffd, 0xfffd, 0xfffd, 0x896b, 0x8d5e, 0x9fa7, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8f46, 0x9fac, 0xfffd, 0x9fab, 0x9fa6, 0xfffd, 0x9fa9, 0xfffd, 0xfffd, 0x8a88, 0xfffd, 0x9fa8, 0x9468, 0xfffd, 0xfffd, 0x97ac, 0xfffd, 0xfffd, 0x8ff2, 0x90f3, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9fb4, 0x9fb2, 0xfffd, 0x956c, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9faf, 0x9fb1, 0xfffd, 0x8959, 0xfffd, 0xfffd, 0x8d5f, 0x9851, 0xfffd, 0x8a5c, 0xfffd, 0x9582, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9781, 0xfffd, 0xfffd, 0x8a43, 0x905a, 0x9fb3, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9fb8, 0xfffd, 0xfffd, 0x8fc1, 0xfffd, 0xfffd, 0xfffd, 0x974f, 0xfffd, 0x9fb5, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9fb0, 0xfffd, 0x9fb6, 0xfffd, 0xfffd, 0xfffd, 0x97dc, 0xfffd, 0x9393, 0x93c0, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8a55, 0xfffd, 0xfffd, 0x8974, 0xfffd, 0xfffd, 0x9fbc, 0xfffd, 0xfffd, 0x9fbf, 0xfffd, 0xfffd, 0xfffd, 0x97c1, 0xfffd, 0xfffd, 0xfffd, 0x9784, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9fc6, 0x9fc0, 0x9fbd, 0xfffd, 0xfffd, 0xfffd, 0x97d2, 0x9fc3, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8f69, 0x9fc5, 0xfffd, 0xfffd, 0x9fca, 0xfffd, 0xfffd, 0x9391, 0x9fc8, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9fc2, 0xfffd, 0xfffd, 0x9257, 0xfffd, 0xfffd, 0x9fc9, 0xfffd, 0x9fbe, 0xfffd, 0x9fc4, 0xfffd, 0x9fcb, 0x88fa, 0x9fc1, 0xfffd, 0x9fcc, 0xfffd, 0xfffd, 0x905b, 0xfffd, 0x8f7e, 0xfffd, 0x95a3, 0xfffd, 0x8dac, 0xfffd, 0x9fb9, 0x9fc7, 0x9359, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x90b4, 0xfffd, 0x8a89, 0x8dcf, 0x8fc2, 0x9fbb, 0x8f61, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8c6b, 0xfffd, 0x9fba, 0xfffd, 0xfffd, 0xfffd, 0x9fd0, 0x8f8d, 0x8cb8, 0xfffd, 0x9fdf, 0xfffd, 0x9fd9, 0x8b94, 0x936e, 0xfffd, 0x9fd4, 0x9fdd, 0x88ad, 0x8951, 0xfffd, 0xfffd, 0x89b7, 0xfffd, 0x9fd6, 0x91aa, 0x9fcd, 0x9fcf, 0x8d60, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9fe0, 0xfffd, 0x9fdb, 0xfffd, 0xfffd, 0xfffd, 0x9fd3, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9fda, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x96a9, 0xfffd, 0xfffd, 0x9fd8, 0x9fdc, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8cce, 0xfffd, 0x8fc3, 0xfffd, 0xfffd, 0x9258, 0xfffd, 0xfffd, 0xfffd, 0x9fd2, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x974e, 0xfffd, 0xfffd, 0xfffd, 0x9fd5, 0xfffd, 0xfffd, 0x9fce, 0x9392, 0xfffd, 0xfffd, 0x9fd1, 0xfffd, 0xfffd, 0xfffd, 0x9fd7, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9870, 0x8ebc, 0x969e, 0xfffd, 0x9fe1, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x94ac, 0xfffd, 0xfffd, 0x9fed, 0x8cb9, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8f80, 0xfffd, 0x9fe3, 0xfffd, 0xfffd, 0xfffd, 0x97ad, 0x8d61, 0xfffd, 0x9ff0, 0xfffd, 0xfffd, 0x88ec, 0xfffd, 0xfffd, 0x9fee, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9fe2, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9fe8, 0xfffd, 0xfffd, 0x9fea, 0xfffd, 0xfffd, 0xfffd, 0x976e, 0x9fe5, 0xfffd, 0xfffd, 0x934d, 0xfffd, 0xfffd, 0x9fe7, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9fef, 0xfffd, 0x9fe9, 0x96c5, 0xfffd, 0xfffd, 0xfffd, 0x9fe4, 0xfffd, 0x8ea0, 0x9ffc, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8a8a, 0xfffd, 0x9fe6, 0x9feb, 0x9fec, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x91ea, 0x91d8, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9ff4, 0xfffd, 0xfffd, 0x9ffa, 0xfffd, 0xfffd, 0x9ff8, 0xfffd, 0x9348, 0xfffd, 0xfffd, 0xe042, 0x9ff5, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9ff6, 0x9fde, 0xfffd, 0x8b99, 0x9559, 0xfffd, 0xfffd, 0xfffd, 0x8ebd, 0xfffd, 0xfffd, 0x8d97, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9852, 0xfffd, 0x9ff2, 0xfffd, 0xe041, 0x8989, 0x9186, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9499, 0xfffd, 0x8abf, 0x97f8, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x969f, 0x92d0, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9ff9, 0x9ffb, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9151, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe040, 0x9ff7, 0xfffd, 0x9ff1, 0xfffd, 0xfffd, 0xfffd, 0x8ac1, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8c89, 0xfffd, 0xfffd, 0xfffd, 0xe04e, 0xfffd, 0xfffd, 0xe049, 0x90f6, 0xfffd, 0xfffd, 0x8a83, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8f81, 0xfffd, 0xe052, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe04b, 0x92aa, 0xe048, 0x92d7, 0xfffd, 0xfffd, 0xfffd, 0xe06b, 0xfffd, 0xfffd, 0xfffd, 0xe045, 0xfffd, 0xe044, 0xfffd, 0xe04d, 0xfffd, 0xfffd, 0xfffd, 0xe047, 0xe046, 0xe04c, 0xfffd, 0x909f, 0xfffd, 0xe043, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe04f, 0xfffd, 0xfffd, 0xe050, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8ac0, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe055, 0xfffd, 0xe054, 0xe056, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe059, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9362, 0xfffd, 0xe053, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe057, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8c83, 0x91f7, 0xe051, 0x945a, 0xfffd, 0xfffd, 0xe058, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe05d, 0xe05b, 0xfffd, 0xfffd, 0xe05e, 0xfffd, 0xfffd, 0xe061, 0xfffd, 0xfffd, 0xfffd, 0xe05a, 0x8d8a, 0x9447, 0xfffd, 0xfffd, 0x9fb7, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9794, 0xe05c, 0xfffd, 0xe060, 0x91f3, 0xfffd, 0xe05f, 0xfffd, 0xe04a, 0xfffd, 0xfffd, 0xe889, 0xfffd, 0xfffd, 0xfffd, 0xe064, 0xfffd, 0xfffd, 0xfffd, 0xe068, 0xfffd, 0xfffd, 0xe066, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe062, 0xfffd, 0xe063, 0xfffd, 0xfffd, 0xfffd, 0xe067, 0xfffd, 0xe065, 0xfffd, 0xfffd, 0xfffd, 0x956d, 0xfffd, 0xfffd, 0xe06d, 0xfffd, 0xe06a, 0xe069, 0xfffd, 0xe06c, 0x93d2, 0xe06e, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9295, 0x91eb, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x90a3, 0xfffd, 0xfffd, 0xfffd, 0xe06f, 0xfffd, 0xe071, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe070, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9ff3, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe072, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x93e5, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe073, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x89ce, 0xfffd, 0xfffd, 0xfffd, 0x9394, 0x8a44, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8b84, 0xfffd, 0xfffd, 0xfffd, 0x8edc, 0x8dd0, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9846, 0x9086, 0xfffd, 0xfffd, 0xfffd, 0x898a, 0xfffd, 0xfffd, 0xfffd, 0xe075, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe074, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe078, 0x9259, 0xe07b, 0xe076, 0xfffd, 0xfffd, 0xfffd, 0xe07a, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe079, 0x935f, 0x88d7, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x97f3, 0xfffd, 0xfffd, 0xe07d, 0xfffd, 0xfffd, 0xfffd, 0x8947, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe080, 0xfffd, 0xfffd, 0xfffd, 0xe07e, 0xfffd, 0xe07c, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe077, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9642, 0xfffd, 0xfffd, 0xfffd, 0xe082, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe081, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x898b, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe084, 0x95b0, 0xfffd, 0xe083, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x96b3, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8fc5, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9152, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8fc4, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x97f9, 0xfffd, 0xfffd, 0xe08a, 0xfffd, 0x90f7, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe086, 0xe08b, 0xfffd, 0xfffd, 0x898c, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe089, 0xfffd, 0x9481, 0xe085, 0xe088, 0x8fc6, 0xfffd, 0x94cf, 0xfffd, 0xfffd, 0xe08c, 0xfffd, 0x8ecf, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x90f8, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe08f, 0xfffd, 0xfffd, 0xfffd, 0xe087, 0xfffd, 0x8c46, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe08d, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x976f, 0xe090, 0xfffd, 0xfffd, 0xfffd, 0xeaa4, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8f6e, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe091, 0xfffd, 0xfffd, 0xfffd, 0xe092, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x944d, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe094, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe095, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9452, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9395, 0xe097, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe099, 0xfffd, 0x97d3, 0xfffd, 0xe096, 0xfffd, 0xe098, 0x898d, 0xfffd, 0xe093, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9a7a, 0xe09a, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9187, 0x8e57, 0xe09c, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe09b, 0x9043, 0x99d7, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe09d, 0xfffd, 0xfffd, 0xfffd, 0xe09f, 0xfffd, 0xe08e, 0xe09e, 0xfffd, 0xfffd, 0xe0a0, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x949a, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe0a1, 0xfffd, 0xfffd, 0xe0a2, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe0a3, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe0a4, 0xfffd, 0x92dc, 0xfffd, 0xe0a6, 0xe0a5, 0xfffd, 0xfffd, 0xe0a7, 0xfffd, 0xe0a8, 0xfffd, 0xfffd, 0x8edd, 0x9583, 0xfffd, 0xfffd, 0xfffd, 0x96ea, 0xe0a9, 0xe0aa, 0x9175, 0x8ea2, 0xe0ab, 0xe0ac, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe0ad, 0x95d0, 0x94c5, 0xfffd, 0xfffd, 0xe0ae, 0x9476, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x92ab, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe0af, 0x89e5, 0xfffd, 0x8b8d, 0xfffd, 0x96c4, 0xfffd, 0x96b4, 0xfffd, 0x89b2, 0x9853, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9671, 0xfffd, 0x95a8, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x90b5, 0xfffd, 0xe0b0, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x93c1, 0xfffd, 0xfffd, 0xfffd, 0x8ca1, 0xe0b1, 0xfffd, 0x8dd2, 0xe0b3, 0xe0b2, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe0b4, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe0b5, 0xfffd, 0xfffd, 0xfffd, 0xe0b6, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8b5d, 0xfffd, 0xe0b7, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe0b8, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8ca2, 0xfffd, 0xfffd, 0x94c6, 0xfffd, 0xfffd, 0xe0ba, 0xfffd, 0xfffd, 0xfffd, 0x8ff3, 0xfffd, 0xfffd, 0xe0b9, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8bb6, 0xe0bb, 0xe0bd, 0xfffd, 0xe0bc, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe0be, 0xfffd, 0x8ccf, 0xfffd, 0xe0bf, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8be7, 0xfffd, 0x915f, 0xfffd, 0x8d9d, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe0c1, 0xe0c2, 0xe0c0, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8eeb, 0xfffd, 0xfffd, 0x93c6, 0x8bb7, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe0c4, 0x924b, 0xe0c3, 0xfffd, 0xfffd, 0x9854, 0x9482, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe0c7, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe0c9, 0xe0c6, 0xfffd, 0xfffd, 0xfffd, 0x96d2, 0xe0c8, 0xe0ca, 0xfffd, 0x97c2, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe0ce, 0xfffd, 0xfffd, 0xfffd, 0xe0cd, 0x9296, 0x944c, 0xfffd, 0xfffd, 0x8ca3, 0xe0cc, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe0cb, 0xfffd, 0x9750, 0x9751, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe0cf, 0x898e, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8d96, 0x8e82, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe0d0, 0xe0d1, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe0d3, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8f62, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe0d5, 0xfffd, 0xe0d4, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe0d6, 0xfffd, 0x8a6c, 0xfffd, 0xfffd, 0xe0d8, 0xfffd, 0xfffd, 0xe0d7, 0xfffd, 0xe0da, 0xe0d9, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8cba, 0xfffd, 0xfffd, 0x97a6, 0xfffd, 0x8bca, 0xfffd, 0x89a4, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8be8, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8adf, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x97e6, 0xe0dc, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe0de, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe0df, 0xfffd, 0x89cf, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe0db, 0xfffd, 0x8e58, 0xfffd, 0xfffd, 0x92bf, 0xe0dd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe0e2, 0xfffd, 0x8eec, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe0e0, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8c5d, 0xfffd, 0xfffd, 0x94c7, 0xe0e1, 0xfffd, 0xfffd, 0xe0fc, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe0e7, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8cbb, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8b85, 0xfffd, 0xe0e4, 0x979d, 0xfffd, 0xfffd, 0x97ae, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x91f4, 0xfffd, 0xfffd, 0xe0e6, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe0e8, 0x97d4, 0x8bd5, 0x94fa, 0x9469, 0xfffd, 0xfffd, 0xfffd, 0xe0e9, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe0eb, 0xfffd, 0xe0ee, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe0ea, 0xfffd, 0xfffd, 0xfffd, 0xe0ed, 0x8ce8, 0x896c, 0xe0ef, 0xfffd, 0x9090, 0xe0ec, 0x97da, 0xfffd, 0xfffd, 0xe0f2, 0xeaa2, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe0f0, 0xe0f3, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe0e5, 0xe0f1, 0xfffd, 0xfffd, 0x8dba, 0xfffd, 0xfffd, 0xe0f4, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe0f5, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x979e, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe0f6, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe0f7, 0xfffd, 0xfffd, 0xfffd, 0xe0e3, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe0f8, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8ac2, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8ea3, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe0f9, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe0fa, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe0fb, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x895a, 0xfffd, 0xfffd, 0xfffd, 0xe140, 0xfffd, 0x955a, 0xe141, 0xfffd, 0xfffd, 0x8aa2, 0xe142, 0xfffd, 0xe143, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe144, 0xfffd, 0xe146, 0xe147, 0xe145, 0xfffd, 0xfffd, 0xfffd, 0x9572, 0xe149, 0xe148, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe14b, 0xe14a, 0xe14c, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe14d, 0xe14f, 0xe14e, 0xfffd, 0xfffd, 0x8d99, 0xfffd, 0xe151, 0xfffd, 0xe150, 0xfffd, 0xfffd, 0x8ac3, 0xfffd, 0x9072, 0xfffd, 0x935b, 0xfffd, 0xe152, 0x90b6, 0xfffd, 0xfffd, 0xfffd, 0x8e59, 0xfffd, 0x8999, 0xe153, 0xfffd, 0x9770, 0xfffd, 0xfffd, 0x95e1, 0xe154, 0xfffd, 0xfffd, 0xfffd, 0x9363, 0x9752, 0x8d62, 0x905c, 0xfffd, 0xfffd, 0xfffd, 0x926a, 0x99b2, 0xfffd, 0x92ac, 0x89e6, 0xe155, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe156, 0xfffd, 0xe15b, 0xfffd, 0xfffd, 0xe159, 0xe158, 0x9dc0, 0x8a45, 0xe157, 0xfffd, 0x88d8, 0xfffd, 0x94a8, 0xfffd, 0xfffd, 0x94c8, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x97af, 0xe15c, 0xe15a, 0x927b, 0x90a4, 0xfffd, 0xfffd, 0x94a9, 0xfffd, 0x954c, 0xfffd, 0xe15e, 0x97aa, 0x8c6c, 0xe15f, 0xfffd, 0xe15d, 0x94d4, 0xe160, 0xfffd, 0xe161, 0xfffd, 0xfffd, 0x88d9, 0xfffd, 0xfffd, 0x8ff4, 0xe166, 0xfffd, 0xe163, 0x93eb, 0xe162, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8b45, 0xfffd, 0xfffd, 0xe169, 0xfffd, 0xfffd, 0xfffd, 0xe164, 0xe165, 0xfffd, 0xe168, 0xe167, 0x9544, 0xfffd, 0xfffd, 0x9161, 0x9160, 0xfffd, 0x8b5e, 0xfffd, 0xfffd, 0xe16a, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe16b, 0xfffd, 0xfffd, 0xe16c, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe16e, 0xfffd, 0xe16d, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8975, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe176, 0x94e6, 0xe170, 0xfffd, 0xe172, 0xfffd, 0xfffd, 0xe174, 0x905d, 0xfffd, 0xfffd, 0xe175, 0xe173, 0x8ebe, 0xfffd, 0xfffd, 0xfffd, 0xe16f, 0xe171, 0xfffd, 0x9561, 0xfffd, 0x8fc7, 0xfffd, 0xfffd, 0xe178, 0xfffd, 0xfffd, 0xe177, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe179, 0xfffd, 0x8ea4, 0x8dad, 0xfffd, 0xfffd, 0x9397, 0xe17a, 0xfffd, 0x92c9, 0xfffd, 0xfffd, 0xe17c, 0xfffd, 0xfffd, 0xfffd, 0x979f, 0xe17b, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9189, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe182, 0xfffd, 0xe184, 0xe185, 0x9273, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe183, 0xfffd, 0xe180, 0xfffd, 0xe17d, 0xe17e, 0xfffd, 0xe181, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe188, 0xfffd, 0xe186, 0xfffd, 0xe187, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe189, 0xe18b, 0xe18c, 0xe18d, 0xfffd, 0xe18e, 0xfffd, 0xfffd, 0xe18a, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe190, 0xfffd, 0xfffd, 0xfffd, 0xe18f, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe191, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x97c3, 0xfffd, 0xfffd, 0xfffd, 0xe194, 0xe192, 0xe193, 0xfffd, 0xfffd, 0xfffd, 0x8ae0, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x96fc, 0xfffd, 0xfffd, 0xfffd, 0x95c8, 0xfffd, 0xe196, 0xfffd, 0xfffd, 0xfffd, 0xe195, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe197, 0xe198, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe19c, 0xe199, 0xe19a, 0xe19b, 0xfffd, 0xe19d, 0xfffd, 0xfffd, 0xfffd, 0xe19e, 0xfffd, 0xe19f, 0xfffd, 0xfffd, 0xfffd, 0xe1a0, 0xfffd, 0xe1a1, 0xfffd, 0x94ad, 0x936f, 0xe1a2, 0x9492, 0x9553, 0xfffd, 0xe1a3, 0xfffd, 0xfffd, 0xe1a4, 0x9349, 0xfffd, 0x8a46, 0x8d63, 0xe1a5, 0xfffd, 0xfffd, 0xe1a6, 0xfffd, 0xfffd, 0xe1a7, 0xfffd, 0x8e48, 0xfffd, 0xfffd, 0xe1a9, 0xfffd, 0xfffd, 0xe1a8, 0xfffd, 0xfffd, 0xe1aa, 0xe1ab, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x94e7, 0xfffd, 0xe1ac, 0xfffd, 0xfffd, 0xfffd, 0xe1ad, 0xfffd, 0xfffd, 0xea89, 0xe1ae, 0xe1af, 0xe1b0, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8e4d, 0xfffd, 0xfffd, 0xe1b1, 0x9475, 0xfffd, 0xfffd, 0x967e, 0xfffd, 0x896d, 0xfffd, 0x8976, 0xfffd, 0xfffd, 0xe1b2, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe1b4, 0xfffd, 0xfffd, 0xfffd, 0xe1b3, 0x9390, 0xfffd, 0xfffd, 0xfffd, 0x90b7, 0x9f58, 0xfffd, 0xe1b5, 0x96bf, 0xfffd, 0xe1b6, 0xfffd, 0x8ac4, 0x94d5, 0xe1b7, 0xfffd, 0xe1b8, 0xfffd, 0xfffd, 0xe1b9, 0xfffd, 0xfffd, 0xfffd, 0x96da, 0xfffd, 0xfffd, 0xfffd, 0x96d3, 0xfffd, 0x92bc, 0xfffd, 0xfffd, 0xfffd, 0x918a, 0xfffd, 0xfffd, 0xe1bb, 0xfffd, 0xfffd, 0x8f82, 0xfffd, 0xfffd, 0x8fc8, 0xfffd, 0xfffd, 0xe1be, 0xfffd, 0xfffd, 0xe1bd, 0xe1bc, 0x94fb, 0xfffd, 0x8ac5, 0x8ca7, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe1c4, 0xfffd, 0xfffd, 0xe1c1, 0x905e, 0x96b0, 0xfffd, 0xfffd, 0xfffd, 0xe1c0, 0xe1c2, 0xe1c3, 0xfffd, 0xfffd, 0xe1bf, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe1c5, 0xe1c6, 0xfffd, 0x92ad, 0xfffd, 0x8ae1, 0xfffd, 0xfffd, 0xfffd, 0x9285, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe1c7, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe1c8, 0xe1cb, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9087, 0xfffd, 0x93c2, 0xfffd, 0xe1cc, 0x9672, 0xfffd, 0xe1c9, 0xfffd, 0xfffd, 0xe1ca, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe1cf, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe1ce, 0xe1cd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe1d1, 0xfffd, 0xfffd, 0xe1d0, 0xfffd, 0xfffd, 0xe1d2, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe1d4, 0xfffd, 0xe1d3, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x95cb, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8f75, 0x97c4, 0xfffd, 0xfffd, 0xe1d5, 0xfffd, 0xfffd, 0x93b5, 0xfffd, 0xfffd, 0xe1d6, 0xfffd, 0xfffd, 0xe1d7, 0xfffd, 0xe1db, 0xe1d9, 0xe1da, 0xfffd, 0xe1d8, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe1dc, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe1dd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe1de, 0xfffd, 0xfffd, 0xe1df, 0x96b5, 0xe1e0, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x96ee, 0xe1e1, 0xfffd, 0x926d, 0xfffd, 0x948a, 0xfffd, 0x8be9, 0xfffd, 0xfffd, 0xfffd, 0x925a, 0xe1e2, 0x8bb8, 0xfffd, 0xfffd, 0xfffd, 0x90ce, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe1e3, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8dbb, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe1e4, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe1e5, 0xfffd, 0x8ca4, 0x8dd3, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe1e7, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9375, 0x8dd4, 0x8b6d, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9643, 0xfffd, 0x946a, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9376, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8d7b, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe1e9, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8fc9, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x97b0, 0x8d64, 0xfffd, 0xfffd, 0x8ca5, 0xfffd, 0xfffd, 0x94a1, 0xfffd, 0xe1eb, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe1ed, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8ce9, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe1ec, 0x92f4, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe1ef, 0x8a56, 0xe1ea, 0xfffd, 0xfffd, 0x94e8, 0xfffd, 0x894f, 0xfffd, 0x8dea, 0xfffd, 0x9871, 0xfffd, 0xfffd, 0xe1ee, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe1f0, 0xfffd, 0xfffd, 0xfffd, 0x95c9, 0xfffd, 0x90d7, 0xe1f2, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe1f3, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe1f1, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8a6d, 0xfffd, 0xe1f9, 0xfffd, 0xe1f8, 0xfffd, 0xfffd, 0x8ea5, 0xfffd, 0xfffd, 0xfffd, 0xe1fa, 0xe1f5, 0xfffd, 0xfffd, 0xfffd, 0xe1fb, 0xe1f6, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x94d6, 0xe1f4, 0xfffd, 0xfffd, 0xe1f7, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe241, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe240, 0x9681, 0xfffd, 0xfffd, 0xfffd, 0xe1fc, 0xfffd, 0xfffd, 0x88e9, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe243, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe242, 0xfffd, 0xfffd, 0xfffd, 0x8fca, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe244, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9162, 0xfffd, 0xfffd, 0xe246, 0xe245, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe247, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe1e6, 0xfffd, 0xfffd, 0xfffd, 0xe1e8, 0xe249, 0xe248, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8ea6, 0xfffd, 0x97e7, 0xfffd, 0x8ed0, 0xfffd, 0xe24a, 0x8c56, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8b5f, 0x8b46, 0x8e83, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9753, 0xfffd, 0xfffd, 0xe250, 0xfffd, 0xe24f, 0x9163, 0xe24c, 0xfffd, 0xfffd, 0xe24e, 0xfffd, 0xfffd, 0x8f6a, 0x905f, 0xe24d, 0xe24b, 0xfffd, 0x9449, 0xfffd, 0xfffd, 0x8fcb, 0xfffd, 0xfffd, 0x955b, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8dd5, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9398, 0xfffd, 0xfffd, 0xe251, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe252, 0xe268, 0x8bd6, 0xfffd, 0xfffd, 0x985c, 0x9154, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe253, 0xfffd, 0xfffd, 0x89d0, 0x92f5, 0x959f, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe254, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8b9a, 0xe255, 0xfffd, 0xfffd, 0xe257, 0xfffd, 0xfffd, 0xfffd, 0xe258, 0xfffd, 0x9448, 0xfffd, 0xfffd, 0xe259, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe25a, 0xe25b, 0xfffd, 0xfffd, 0x8bd7, 0x89d1, 0x93c3, 0x8f47, 0x8e84, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe25c, 0xfffd, 0x8f48, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x89c8, 0x9562, 0xfffd, 0xfffd, 0xe25d, 0xfffd, 0xfffd, 0x94e9, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9164, 0xfffd, 0xe260, 0xfffd, 0xe261, 0x9489, 0xfffd, 0x9060, 0xe25e, 0xfffd, 0x9281, 0xfffd, 0xfffd, 0xe25f, 0xfffd, 0xfffd, 0xfffd, 0x8fcc, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x88da, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8b48, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe262, 0xfffd, 0xfffd, 0x92f6, 0xfffd, 0xe263, 0x90c5, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x96ab, 0xfffd, 0xfffd, 0x9542, 0xe264, 0xe265, 0x9274, 0xfffd, 0x97c5, 0xfffd, 0xfffd, 0xe267, 0xe266, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8eed, 0xfffd, 0xfffd, 0xe269, 0x88ee, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe26c, 0xfffd, 0xfffd, 0xfffd, 0xe26a, 0x89d2, 0x8c6d, 0xe26b, 0x8d65, 0x8d92, 0xfffd, 0x95e4, 0xe26d, 0xfffd, 0xfffd, 0x9673, 0xfffd, 0xfffd, 0xe26f, 0xfffd, 0xfffd, 0xfffd, 0x90cf, 0x896e, 0x89b8, 0x88aa, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe26e, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe270, 0xe271, 0x8ff5, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe272, 0xfffd, 0x8a6e, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe274, 0xfffd, 0xfffd, 0xfffd, 0x8c8a, 0xfffd, 0x8b86, 0xfffd, 0xfffd, 0xe275, 0x8bf3, 0xfffd, 0xfffd, 0xe276, 0xfffd, 0x90fa, 0xfffd, 0x93cb, 0xfffd, 0x90de, 0x8df3, 0xfffd, 0xfffd, 0xfffd, 0xe277, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9282, 0x918b, 0xfffd, 0xe279, 0xe27b, 0xe278, 0xe27a, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8c41, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe27c, 0x8c45, 0xfffd, 0xfffd, 0xfffd, 0x8b87, 0x9771, 0xe27e, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe280, 0xfffd, 0xfffd, 0xfffd, 0x894d, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe283, 0xfffd, 0xfffd, 0xfffd, 0x8a96, 0xe282, 0xe281, 0xfffd, 0xe285, 0xe27d, 0xfffd, 0xe286, 0x97a7, 0xfffd, 0xe287, 0xfffd, 0xe288, 0xfffd, 0xfffd, 0x9af2, 0xe28a, 0xfffd, 0xe289, 0xfffd, 0xfffd, 0xfffd, 0xe28b, 0xe28c, 0xfffd, 0x97b3, 0xe28d, 0xfffd, 0xe8ed, 0x8fcd, 0xe28e, 0xe28f, 0x8f76, 0xfffd, 0x93b6, 0xe290, 0xfffd, 0xfffd, 0xfffd, 0x9247, 0xfffd, 0xfffd, 0xe291, 0xfffd, 0x925b, 0xe292, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8ba3, 0xfffd, 0x995e, 0x927c, 0x8eb1, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8ac6, 0xfffd, 0xfffd, 0xe293, 0xfffd, 0xe2a0, 0xfffd, 0xe296, 0xfffd, 0x8b88, 0xfffd, 0xe295, 0xe2a2, 0xfffd, 0xfffd, 0xfffd, 0xe294, 0xfffd, 0x8fce, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe298, 0xe299, 0xfffd, 0x934a, 0xfffd, 0xfffd, 0xe29a, 0xfffd, 0x8a7d, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9079, 0x9584, 0xfffd, 0xe29c, 0xfffd, 0xfffd, 0xfffd, 0x91e6, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe297, 0xfffd, 0xe29b, 0xe29d, 0xfffd, 0xfffd, 0x8df9, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe2a4, 0x954d, 0xfffd, 0x94a4, 0x9399, 0xfffd, 0x8bd8, 0xe2a3, 0xe2a1, 0xfffd, 0x94b3, 0xe29e, 0x927d, 0x939b, 0xfffd, 0x939a, 0xfffd, 0x8df4, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe2b6, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe2a6, 0xfffd, 0xe2a8, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe2ab, 0xfffd, 0xe2ac, 0xfffd, 0xe2a9, 0xe2aa, 0xfffd, 0xfffd, 0xe2a7, 0xe2a5, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe29f, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x95cd, 0x89d3, 0xfffd, 0xfffd, 0xfffd, 0xe2b3, 0xfffd, 0xe2b0, 0xfffd, 0xe2b5, 0xfffd, 0xfffd, 0xe2b4, 0xfffd, 0x9493, 0x96a5, 0xfffd, 0x8e5a, 0xe2ae, 0xe2b7, 0xe2b2, 0xfffd, 0xe2b1, 0xe2ad, 0xfffd, 0xe2af, 0xfffd, 0x8ac7, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x925c, 0xfffd, 0xfffd, 0x90fb, 0xfffd, 0xfffd, 0xfffd, 0x94a0, 0xfffd, 0xfffd, 0xe2bc, 0xfffd, 0xfffd, 0xfffd, 0x94a2, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x90df, 0xe2b9, 0xfffd, 0xfffd, 0x94cd, 0xfffd, 0xe2bd, 0x95d1, 0xfffd, 0x927a, 0xfffd, 0xe2b8, 0xe2ba, 0xfffd, 0xfffd, 0xe2bb, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe2be, 0xfffd, 0xfffd, 0x8ec2, 0xfffd, 0xfffd, 0xfffd, 0x93c4, 0xe2c3, 0xe2c2, 0xfffd, 0xfffd, 0xe2bf, 0xfffd, 0xfffd, 0xfffd, 0x9855, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe2c8, 0xfffd, 0xfffd, 0xe2cc, 0xe2c9, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe2c5, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe2c6, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe2cb, 0xfffd, 0xfffd, 0xfffd, 0xe2c0, 0x99d3, 0xe2c7, 0xe2c1, 0xfffd, 0xfffd, 0xe2ca, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe2d0, 0xfffd, 0x8ac8, 0xfffd, 0xe2cd, 0xfffd, 0xfffd, 0xfffd, 0xe2ce, 0xfffd, 0xfffd, 0xe2cf, 0xe2d2, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe2d1, 0x94f4, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe2d3, 0x97fa, 0x95eb, 0xe2d8, 0xfffd, 0xfffd, 0xe2d5, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe2d4, 0x90d0, 0xfffd, 0xe2d7, 0xe2d9, 0xfffd, 0xfffd, 0xfffd, 0xe2d6, 0xfffd, 0xe2dd, 0xfffd, 0xe2da, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe2db, 0xe2c4, 0xfffd, 0xfffd, 0xfffd, 0xe2dc, 0xe2de, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe2df, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x95c4, 0xfffd, 0xe2e0, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x96e0, 0xfffd, 0xfffd, 0x8bcc, 0x8c48, 0xe2e1, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x95b2, 0xfffd, 0x9088, 0xfffd, 0x96ae, 0xfffd, 0xfffd, 0xe2e2, 0xfffd, 0x97b1, 0xfffd, 0xfffd, 0x9494, 0xfffd, 0x9165, 0x9453, 0xfffd, 0xfffd, 0x8f6c, 0xfffd, 0xfffd, 0xfffd, 0x88be, 0xfffd, 0xe2e7, 0xe2e5, 0xfffd, 0xe2e3, 0x8a9f, 0xfffd, 0x8fcf, 0xe2e8, 0xfffd, 0xfffd, 0xe2e6, 0xfffd, 0xe2e4, 0xe2ec, 0xfffd, 0xfffd, 0xe2eb, 0xe2ea, 0xe2e9, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe2ed, 0xfffd, 0xfffd, 0xfffd, 0xe2ee, 0x90b8, 0xfffd, 0xe2ef, 0xfffd, 0xe2f1, 0xfffd, 0xfffd, 0xe2f0, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8cd0, 0xfffd, 0xfffd, 0xfffd, 0x9157, 0xfffd, 0xfffd, 0xfffd, 0xe2f3, 0xfffd, 0xfffd, 0xfffd, 0x939c, 0xfffd, 0xe2f2, 0xfffd, 0xfffd, 0xfffd, 0xe2f4, 0xfffd, 0x95b3, 0x918c, 0x8d66, 0xfffd, 0xe2f5, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x97c6, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe2f7, 0xfffd, 0xfffd, 0xe2f8, 0xfffd, 0xe2f9, 0xfffd, 0xe2fa, 0xfffd, 0x8e85, 0xfffd, 0xe2fb, 0x8c6e, 0xfffd, 0xfffd, 0x8b8a, 0xfffd, 0x8b49, 0xfffd, 0xe340, 0xfffd, 0x96f1, 0x8d67, 0xe2fc, 0xfffd, 0xfffd, 0xfffd, 0xe343, 0x96e4, 0xfffd, 0x945b, 0xfffd, 0xfffd, 0x9552, 0xfffd, 0xfffd, 0xfffd, 0x8f83, 0xe342, 0xfffd, 0x8ed1, 0x8d68, 0x8e86, 0x8b89, 0x95b4, 0xe341, 0xfffd, 0xfffd, 0xfffd, 0x9166, 0x9661, 0x8df5, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8e87, 0x92db, 0xfffd, 0xe346, 0x97dd, 0x8dd7, 0xfffd, 0xe347, 0x9061, 0xfffd, 0xe349, 0xfffd, 0xfffd, 0xfffd, 0x8fd0, 0x8dae, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe348, 0xfffd, 0xfffd, 0x8f49, 0x8cbc, 0x9167, 0xe344, 0xe34a, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe345, 0x8c6f, 0xfffd, 0xe34d, 0xe351, 0x8c8b, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe34c, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe355, 0xfffd, 0xfffd, 0x8d69, 0xfffd, 0xfffd, 0x978d, 0x88ba, 0xe352, 0xfffd, 0xfffd, 0x8b8b, 0xfffd, 0xe34f, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe350, 0xfffd, 0xfffd, 0x939d, 0xe34e, 0xe34b, 0xfffd, 0x8a47, 0x90e2, 0xfffd, 0xfffd, 0x8ca6, 0xfffd, 0xfffd, 0xfffd, 0xe357, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe354, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe356, 0xfffd, 0xfffd, 0xfffd, 0xe353, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8c70, 0x91b1, 0xe358, 0x918e, 0xfffd, 0xfffd, 0xe365, 0xfffd, 0xfffd, 0xe361, 0xe35b, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe35f, 0x8ef8, 0x88db, 0xe35a, 0xe362, 0xe366, 0x8d6a, 0x96d4, 0xfffd, 0x92d4, 0xe35c, 0xfffd, 0xfffd, 0xe364, 0xfffd, 0xe359, 0x925d, 0xfffd, 0xe35e, 0x88bb, 0x96c8, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe35d, 0xfffd, 0xfffd, 0x8bd9, 0x94ea, 0xfffd, 0xfffd, 0xfffd, 0x918d, 0xfffd, 0x97ce, 0x8f8f, 0xfffd, 0xfffd, 0xe38e, 0xfffd, 0xfffd, 0xe367, 0xfffd, 0x90fc, 0xfffd, 0xe363, 0xe368, 0xe36a, 0xfffd, 0x92f7, 0xe36d, 0xfffd, 0xfffd, 0xe369, 0xfffd, 0xfffd, 0xfffd, 0x95d2, 0x8ac9, 0xfffd, 0xfffd, 0x96c9, 0xfffd, 0xfffd, 0x88dc, 0xfffd, 0xfffd, 0xe36c, 0xfffd, 0x97fb, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe36b, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x898f, 0xfffd, 0xfffd, 0x93ea, 0xe36e, 0xfffd, 0xfffd, 0xfffd, 0xe375, 0xe36f, 0xe376, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe372, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x949b, 0xfffd, 0xfffd, 0x8ec8, 0xe374, 0xfffd, 0xe371, 0xe377, 0xe370, 0xfffd, 0xfffd, 0x8f63, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9644, 0xfffd, 0xfffd, 0x8f6b, 0xfffd, 0xfffd, 0xe373, 0xe380, 0xfffd, 0xfffd, 0xe37b, 0xfffd, 0xe37e, 0xfffd, 0xe37c, 0xe381, 0xe37a, 0xfffd, 0xe360, 0x90d1, 0xfffd, 0xfffd, 0x94c9, 0xfffd, 0xe37d, 0xfffd, 0xfffd, 0xe378, 0xfffd, 0xfffd, 0xfffd, 0x9140, 0x8c71, 0xfffd, 0x8f4a, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9044, 0x9155, 0xe384, 0xfffd, 0xfffd, 0xe386, 0xe387, 0xfffd, 0xfffd, 0xe383, 0xe385, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe379, 0xe382, 0xfffd, 0xe38a, 0xe389, 0xfffd, 0xfffd, 0x969a, 0xfffd, 0xfffd, 0x8c4a, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe388, 0xfffd, 0xe38c, 0xe38b, 0xe38f, 0xfffd, 0xe391, 0xfffd, 0xfffd, 0x8e5b, 0xe38d, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe392, 0xe393, 0xfffd, 0xfffd, 0xe394, 0xfffd, 0xe39a, 0x935a, 0xe396, 0xfffd, 0xe395, 0xe397, 0xe398, 0xfffd, 0xe399, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe39b, 0xe39c, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8aca, 0xfffd, 0xe39d, 0xfffd, 0xe39e, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe39f, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe3a0, 0xe3a1, 0xe3a2, 0xfffd, 0xe3a3, 0xe3a4, 0xfffd, 0xfffd, 0xe3a6, 0xe3a5, 0xfffd, 0xfffd, 0xe3a7, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe3a8, 0xe3a9, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe3ac, 0xe3aa, 0xe3ab, 0x8ddf, 0x8c72, 0xfffd, 0xfffd, 0x9275, 0xfffd, 0x94b1, 0xfffd, 0x8f90, 0xfffd, 0xfffd, 0x946c, 0xfffd, 0x94eb, 0xe3ad, 0x9ceb, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe3ae, 0xe3b0, 0xfffd, 0x9785, 0xe3af, 0xe3b2, 0xe3b1, 0xfffd, 0x9772, 0xfffd, 0xe3b3, 0xfffd, 0x94fc, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe3b4, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe3b7, 0xfffd, 0xfffd, 0xe3b6, 0xe3b5, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe3b8, 0x8c51, 0xfffd, 0xfffd, 0xfffd, 0x9141, 0x8b60, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe3bc, 0xe3b9, 0xfffd, 0xfffd, 0xe3ba, 0xfffd, 0xfffd, 0xfffd, 0xe3bd, 0xfffd, 0xe3be, 0xe3bb, 0xfffd, 0xfffd, 0xfffd, 0x8948, 0xfffd, 0xfffd, 0xfffd, 0x89a5, 0xfffd, 0xfffd, 0xfffd, 0xe3c0, 0xe3c1, 0xfffd, 0xfffd, 0xfffd, 0xe3c2, 0xfffd, 0x9782, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8f4b, 0xfffd, 0xe3c4, 0xe3c3, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9089, 0xe3c5, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe3c6, 0xfffd, 0xfffd, 0xe3c7, 0xfffd, 0x8ae3, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8acb, 0xfffd, 0xfffd, 0xe3c8, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe3c9, 0xfffd, 0x967c, 0x9783, 0xfffd, 0xfffd, 0xfffd, 0x9773, 0x9856, 0xfffd, 0x8d6c, 0xe3cc, 0x8ed2, 0xe3cb, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe3cd, 0x8ea7, 0xfffd, 0xfffd, 0xfffd, 0x91cf, 0xfffd, 0xe3ce, 0xfffd, 0xfffd, 0x8d6b, 0xfffd, 0x96d5, 0xe3cf, 0xe3d0, 0xfffd, 0xfffd, 0xe3d1, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe3d2, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe3d3, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8ea8, 0xfffd, 0xfffd, 0x96eb, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe3d5, 0xfffd, 0x925e, 0xfffd, 0xe3d4, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe3d7, 0xfffd, 0xfffd, 0xfffd, 0xe3d6, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe3d8, 0xfffd, 0xfffd, 0xfffd, 0x90b9, 0xfffd, 0xe3d9, 0xfffd, 0xe3da, 0xfffd, 0xfffd, 0xfffd, 0x95b7, 0xe3db, 0xfffd, 0x918f, 0xe3dc, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe3dd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x97fc, 0xe3e0, 0xfffd, 0xe3df, 0xe3de, 0x92ae, 0xfffd, 0xe3e1, 0x9045, 0xfffd, 0xe3e2, 0xfffd, 0xfffd, 0xfffd, 0xe3e3, 0x9857, 0xe3e4, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe3e5, 0xe3e7, 0xe3e6, 0x94a3, 0xfffd, 0x93f7, 0xfffd, 0x985d, 0x94a7, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe3e9, 0xfffd, 0xfffd, 0x8fd1, 0xfffd, 0x9549, 0xfffd, 0xe3ea, 0xe3e8, 0xfffd, 0x8acc, 0xfffd, 0xfffd, 0xfffd, 0x8cd2, 0x8e88, 0xfffd, 0xfffd, 0x94ec, 0xfffd, 0xfffd, 0xfffd, 0x8ca8, 0x9662, 0xfffd, 0xe3ed, 0xe3eb, 0xfffd, 0x8d6d, 0xfffd, 0x8d6e, 0x88e7, 0xfffd, 0x8de6, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9478, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x88dd, 0xe3f2, 0xfffd, 0x925f, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9477, 0xfffd, 0x91d9, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe3f4, 0xfffd, 0xfffd, 0xe3f0, 0xe3f3, 0xe3ee, 0xfffd, 0xe3f1, 0x9645, 0xfffd, 0xfffd, 0x8cd3, 0xfffd, 0xfffd, 0x88fb, 0xe3ef, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe3f6, 0xfffd, 0xe3f7, 0xfffd, 0xfffd, 0x93b7, 0xfffd, 0xfffd, 0xfffd, 0x8bb9, 0xfffd, 0xfffd, 0xfffd, 0xe445, 0x945c, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8e89, 0xfffd, 0xfffd, 0x8bba, 0x90c6, 0x9865, 0x96ac, 0xe3f5, 0x90d2, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8b72, 0xe3f8, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe3fa, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe3f9, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe3fb, 0xfffd, 0x9245, 0xfffd, 0x945d, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x92af, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe442, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe441, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe3fc, 0xfffd, 0xfffd, 0x9074, 0xfffd, 0x9585, 0xe444, 0xfffd, 0xe443, 0x8d6f, 0x9872, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe454, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe448, 0xe449, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8eee, 0xfffd, 0xfffd, 0xe447, 0xfffd, 0x8d98, 0xe446, 0xfffd, 0xfffd, 0xe44a, 0xfffd, 0xfffd, 0xfffd, 0x92b0, 0x95a0, 0x9142, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x91da, 0xe44e, 0xfffd, 0xe44f, 0xe44b, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe44c, 0xfffd, 0xe44d, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8d70, 0xfffd, 0xfffd, 0xfffd, 0xe455, 0xfffd, 0xe451, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9586, 0xfffd, 0x968c, 0x9547, 0xfffd, 0xfffd, 0xe450, 0xfffd, 0xfffd, 0xe453, 0xe452, 0xfffd, 0xfffd, 0xfffd, 0x9663, 0xe456, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe457, 0xfffd, 0xfffd, 0x9156, 0xfffd, 0xe458, 0xfffd, 0xfffd, 0xe45a, 0xfffd, 0xe45e, 0xfffd, 0xfffd, 0xe45b, 0xe459, 0x945e, 0xe45c, 0xfffd, 0xe45d, 0xfffd, 0xfffd, 0xfffd, 0x89b0, 0xfffd, 0xe464, 0xe45f, 0xfffd, 0xfffd, 0xfffd, 0xe460, 0xfffd, 0xfffd, 0xfffd, 0xe461, 0xfffd, 0x919f, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe463, 0xe462, 0xe465, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe466, 0xe467, 0xfffd, 0xfffd, 0x9062, 0xfffd, 0x89e7, 0xfffd, 0xe468, 0x97d5, 0xfffd, 0x8ea9, 0xfffd, 0xfffd, 0x8f4c, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8e8a, 0x9276, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe469, 0xe46a, 0x8950, 0xfffd, 0xe46b, 0xfffd, 0xfffd, 0xe46c, 0xe46d, 0xfffd, 0xfffd, 0xe46e, 0xfffd, 0xe46f, 0x8bbb, 0x9da8, 0xe470, 0xfffd, 0x90e3, 0xe471, 0x8ec9, 0xfffd, 0xe472, 0xfffd, 0x98ae, 0xfffd, 0xfffd, 0xfffd, 0xe473, 0x95dc, 0x8ada, 0xfffd, 0xfffd, 0x9143, 0x8f77, 0xfffd, 0x9591, 0x8f4d, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe474, 0x8d71, 0xe475, 0x94ca, 0xfffd, 0xe484, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe477, 0xfffd, 0x91c7, 0x9495, 0x8cbd, 0xe476, 0x9144, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe478, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x92f8, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe47a, 0xe479, 0xe47c, 0xfffd, 0xfffd, 0xe47b, 0xfffd, 0xe47d, 0xfffd, 0xfffd, 0xe480, 0xfffd, 0xe47e, 0xfffd, 0x8acd, 0xfffd, 0xe481, 0xfffd, 0xe482, 0xe483, 0xfffd, 0xfffd, 0x8daf, 0x97c7, 0xfffd, 0xe485, 0x9046, 0xfffd, 0xfffd, 0xfffd, 0x8990, 0xe486, 0xe487, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe488, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x88f0, 0xfffd, 0xe489, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe48a, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9587, 0xfffd, 0xfffd, 0xfffd, 0x8ec5, 0xfffd, 0xe48c, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8a48, 0x88b0, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe48b, 0xe48e, 0x946d, 0xfffd, 0x9063, 0xfffd, 0x89d4, 0xfffd, 0x9646, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8c7c, 0x8bda, 0xfffd, 0xe48d, 0xfffd, 0x89e8, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8aa1, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8991, 0xe492, 0x97e8, 0x91db, 0xfffd, 0xfffd, 0x9563, 0xfffd, 0xe49e, 0xfffd, 0x89d5, 0xe49c, 0xfffd, 0xe49a, 0xe491, 0xfffd, 0xe48f, 0xfffd, 0xe490, 0xfffd, 0x8ee1, 0x8bea, 0x9297, 0xfffd, 0xfffd, 0xfffd, 0x93cf, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8970, 0xfffd, 0xe494, 0xe493, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe499, 0xe495, 0xe498, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x96ce, 0xe497, 0x89d6, 0x8a9d, 0xe49b, 0xfffd, 0xfffd, 0xe49d, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8c73, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe4a1, 0xe4aa, 0xe4ab, 0xfffd, 0xfffd, 0xfffd, 0x88a9, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe4b2, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x88ef, 0xfffd, 0xfffd, 0xe4a9, 0xfffd, 0xfffd, 0xfffd, 0xe4a8, 0xfffd, 0xe4a3, 0xe4a2, 0xfffd, 0xe4a0, 0xe49f, 0x9283, 0xfffd, 0x91f9, 0xe4a5, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe4a4, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe4a7, 0xfffd, 0xfffd, 0xfffd, 0x9190, 0x8c74, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8960, 0xe4a6, 0xfffd, 0x8d72, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9191, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe4b8, 0xfffd, 0xe4b9, 0xfffd, 0x89d7, 0xfffd, 0xfffd, 0xfffd, 0x89ac, 0xe4b6, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe4ac, 0xfffd, 0xe4b4, 0xfffd, 0xe4bb, 0xe4b5, 0xfffd, 0xfffd, 0xfffd, 0xe4b3, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe496, 0xfffd, 0xfffd, 0xe4b1, 0xfffd, 0xfffd, 0xfffd, 0xe4ad, 0xfffd, 0xfffd, 0xfffd, 0x8ace, 0xe4af, 0xe4ba, 0xfffd, 0xe4b0, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe4bc, 0xfffd, 0xe4ae, 0x949c, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9789, 0xfffd, 0xfffd, 0xfffd, 0xe4b7, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe4cd, 0xfffd, 0xfffd, 0xfffd, 0xe4c5, 0xfffd, 0xfffd, 0xfffd, 0x909b, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8b65, 0xfffd, 0x8bdb, 0xfffd, 0xe4c0, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x89d9, 0xfffd, 0xfffd, 0x8fd2, 0xfffd, 0xe4c3, 0xfffd, 0xfffd, 0xfffd, 0x8dd8, 0xfffd, 0xfffd, 0x9370, 0xe4c8, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x95ec, 0xfffd, 0xe4bf, 0xfffd, 0xfffd, 0xfffd, 0x89d8, 0x8cd4, 0x9548, 0xe4c9, 0xfffd, 0xe4bd, 0xfffd, 0xfffd, 0xe4c6, 0xfffd, 0xfffd, 0xfffd, 0xe4d0, 0xfffd, 0xe4c1, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe4c2, 0x93b8, 0xfffd, 0xfffd, 0xe4c7, 0xfffd, 0xfffd, 0xfffd, 0xe4c4, 0x9647, 0xe4ca, 0x88de, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe4be, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe4cc, 0xfffd, 0xe4cb, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x948b, 0xe4d2, 0xfffd, 0xe4dd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8a9e, 0xfffd, 0xfffd, 0xfffd, 0xe4e0, 0xfffd, 0xfffd, 0xe4ce, 0xfffd, 0xfffd, 0xfffd, 0xe4d3, 0x978e, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe4dc, 0xfffd, 0xfffd, 0x9774, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x97a8, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9298, 0xfffd, 0xfffd, 0xfffd, 0x8a8b, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9592, 0xe4e2, 0x939f, 0xfffd, 0xfffd, 0x88af, 0xfffd, 0xfffd, 0xe4db, 0xfffd, 0xe4d7, 0x9192, 0xe4d1, 0xe4d9, 0xe4de, 0xfffd, 0x944b, 0xfffd, 0xfffd, 0xfffd, 0x88a8, 0xfffd, 0xe4d6, 0xfffd, 0xe4df, 0x9598, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe4da, 0xfffd, 0xe4d5, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8fd3, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8f4e, 0xfffd, 0xfffd, 0xfffd, 0x8eaa, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x96d6, 0xfffd, 0xfffd, 0x9566, 0xfffd, 0xfffd, 0xe4e5, 0xfffd, 0xe4ee, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe4d8, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8a97, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8ff6, 0xe4e3, 0xfffd, 0xe4e8, 0x9193, 0xfffd, 0xfffd, 0xe4e4, 0xfffd, 0xe4eb, 0xfffd, 0xfffd, 0x927e, 0xfffd, 0xe4ec, 0xfffd, 0xfffd, 0x9775, 0xe4e1, 0x8a57, 0xfffd, 0xe4e7, 0xfffd, 0xfffd, 0xe4ea, 0x96aa, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe4ed, 0xfffd, 0xfffd, 0xe4e6, 0xe4e9, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9648, 0xfffd, 0x9840, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe4f1, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe4f8, 0xfffd, 0xfffd, 0xe4f0, 0x8ec1, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe4cf, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x95cc, 0xfffd, 0x96a0, 0xe4f7, 0xe4f6, 0xfffd, 0xe4f2, 0xe4f3, 0xfffd, 0x8955, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe4f5, 0xfffd, 0xe4ef, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x92d3, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe4f4, 0x88fc, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x91a0, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x95c1, 0xfffd, 0xfffd, 0xe4f9, 0xe540, 0xfffd, 0x94d7, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe4fc, 0x8fd4, 0x8ec7, 0xe542, 0xfffd, 0xfffd, 0x8bbc, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe543, 0xfffd, 0x9599, 0xe4fb, 0xfffd, 0xe4d4, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe4fa, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x986e, 0x93a0, 0x9593, 0xfffd, 0xfffd, 0xe54a, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe550, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe551, 0xfffd, 0xe544, 0xfffd, 0xfffd, 0xfffd, 0x9496, 0xfffd, 0xfffd, 0xe54e, 0xe546, 0xfffd, 0xe548, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe552, 0xe547, 0xfffd, 0xfffd, 0xe54b, 0xfffd, 0xfffd, 0x8992, 0xfffd, 0x93e3, 0xfffd, 0xe54c, 0xe54f, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe545, 0xfffd, 0x9145, 0xfffd, 0xe549, 0x8e46, 0x9064, 0x8c4f, 0x96f2, 0xfffd, 0x96f7, 0x8f92, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe556, 0xe554, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x986d, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe553, 0xfffd, 0xfffd, 0xfffd, 0x9795, 0xfffd, 0xe555, 0xe557, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe558, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe55b, 0xe559, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x93a1, 0xe55a, 0xfffd, 0xfffd, 0xfffd, 0x94cb, 0xe54d, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8f93, 0xfffd, 0xe55c, 0xe561, 0x9194, 0xfffd, 0xfffd, 0xe560, 0xfffd, 0xfffd, 0xfffd, 0xe541, 0xfffd, 0xfffd, 0xfffd, 0xe562, 0x9168, 0xfffd, 0xfffd, 0xe55d, 0xe55f, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe55e, 0xfffd, 0xfffd, 0x9f50, 0x9f41, 0xfffd, 0xfffd, 0xe564, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe563, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9796, 0xfffd, 0xe1ba, 0xe565, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe566, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe567, 0x8cd5, 0xfffd, 0x8b73, 0xfffd, 0xfffd, 0xfffd, 0xe569, 0x997c, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8b95, 0xfffd, 0x97b8, 0xfffd, 0x8bf1, 0xe56a, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe56b, 0xfffd, 0xfffd, 0xfffd, 0x928e, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe56c, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x93f8, 0xfffd, 0x88b8, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x89e1, 0xe571, 0xe572, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe56d, 0xfffd, 0x8e5c, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe56e, 0x9461, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe56f, 0xe570, 0xe57a, 0xfffd, 0xfffd, 0xfffd, 0xe574, 0xe577, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe573, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe575, 0xfffd, 0xe576, 0x8ed6, 0xfffd, 0xe578, 0xfffd, 0x9260, 0xfffd, 0x8c75, 0x8a61, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe57b, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8a5e, 0xfffd, 0xe581, 0xfffd, 0xfffd, 0xe57c, 0xe580, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x94b8, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe57d, 0xfffd, 0xfffd, 0xe57e, 0x9567, 0x94d8, 0xe582, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x91fb, 0xe58c, 0xfffd, 0xe588, 0xfffd, 0xfffd, 0x89e9, 0xfffd, 0xe586, 0xfffd, 0x9649, 0xe587, 0xfffd, 0xfffd, 0xe584, 0xfffd, 0xe585, 0xe58a, 0xe58d, 0xfffd, 0xfffd, 0xe58b, 0xfffd, 0xfffd, 0xfffd, 0xe589, 0xe583, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9277, 0xfffd, 0xe594, 0xfffd, 0x96a8, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe592, 0xfffd, 0xfffd, 0xfffd, 0xe593, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe58e, 0xfffd, 0xfffd, 0xe590, 0xfffd, 0xfffd, 0xfffd, 0xe591, 0xfffd, 0xfffd, 0xfffd, 0xe58f, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x90e4, 0xfffd, 0x9858, 0xe598, 0xfffd, 0xe599, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe59f, 0xfffd, 0x9049, 0xfffd, 0xe59b, 0xfffd, 0xe59e, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe596, 0xe595, 0xfffd, 0xfffd, 0xe5a0, 0xfffd, 0xfffd, 0x89da, 0xfffd, 0xe59c, 0xfffd, 0xe5a1, 0xfffd, 0xfffd, 0xfffd, 0xe59d, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe59a, 0xfffd, 0x92b1, 0xfffd, 0xe597, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9488, 0xfffd, 0xfffd, 0xe5a5, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x975a, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe5a4, 0xfffd, 0xfffd, 0xe5a3, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe5ac, 0xfffd, 0xfffd, 0xfffd, 0xe5a6, 0xfffd, 0xfffd, 0xfffd, 0xe5ae, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9786, 0xe5b1, 0xfffd, 0xe5a8, 0xfffd, 0xfffd, 0xe5a9, 0xfffd, 0xfffd, 0xfffd, 0xe5ad, 0xfffd, 0xe5b0, 0xe5af, 0xfffd, 0xfffd, 0xfffd, 0xe5a7, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe5aa, 0xfffd, 0xe5bb, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe5b4, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe5b2, 0xfffd, 0xfffd, 0xe5b3, 0xfffd, 0xfffd, 0xfffd, 0xe5b8, 0xe5b9, 0xfffd, 0x8a49, 0xfffd, 0x8b61, 0xfffd, 0xfffd, 0xe5b7, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe5a2, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe5b6, 0xe5ba, 0xe5b5, 0xfffd, 0xe5bc, 0xfffd, 0xfffd, 0xfffd, 0xe5be, 0xe5bd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe5c0, 0xe5bf, 0xe579, 0xfffd, 0xfffd, 0xfffd, 0xe5c4, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe5c1, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe5c2, 0xfffd, 0xfffd, 0xe5c3, 0xfffd, 0xe5c5, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8c8c, 0xfffd, 0xe5c7, 0xfffd, 0xe5c6, 0xfffd, 0x8f4f, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8d73, 0x9fa5, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe5c8, 0x8f70, 0xfffd, 0xfffd, 0xfffd, 0x8a58, 0xfffd, 0xe5c9, 0xfffd, 0x8971, 0xfffd, 0x8fd5, 0xe5ca, 0xfffd, 0xfffd, 0x8d74, 0xe5cb, 0x88df, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x955c, 0xfffd, 0xfffd, 0xe5cc, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x908a, 0xfffd, 0xe5d3, 0xfffd, 0xfffd, 0xe5d0, 0xfffd, 0x928f, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe5d1, 0xe5ce, 0x8bdc, 0xfffd, 0xe5cd, 0xe5d4, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8c55, 0xfffd, 0xfffd, 0x91dc, 0xfffd, 0xe5da, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe5d6, 0xfffd, 0xfffd, 0xfffd, 0x91b3, 0xe5d5, 0xfffd, 0xe5d8, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe5cf, 0xfffd, 0xfffd, 0xfffd, 0xe5d9, 0xfffd, 0xe5db, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x94ed, 0xfffd, 0xfffd, 0xe5d7, 0xfffd, 0xe5dc, 0xe5de, 0xfffd, 0xfffd, 0x8cd1, 0xe5d2, 0xfffd, 0x88bf, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe5dd, 0xfffd, 0x8dd9, 0x97f4, 0xe5df, 0xe5e0, 0x9195, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x97a0, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe5e1, 0x9754, 0xfffd, 0xfffd, 0xe5e2, 0xe5e3, 0xfffd, 0xfffd, 0x95e2, 0xe5e4, 0xfffd, 0x8dbe, 0xfffd, 0x97a1, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe5e9, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe5ea, 0x8fd6, 0xe5e8, 0xfffd, 0xfffd, 0xfffd, 0x9787, 0xe5e5, 0xfffd, 0xfffd, 0xe5e7, 0x90bb, 0x909e, 0xfffd, 0xfffd, 0xfffd, 0xe5e6, 0xfffd, 0xe5eb, 0xfffd, 0xfffd, 0x95a1, 0xfffd, 0xfffd, 0xe5ed, 0xfffd, 0xe5ec, 0xfffd, 0xfffd, 0xfffd, 0x8a8c, 0xfffd, 0x964a, 0xe5ee, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe5fa, 0xe5f0, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe5f1, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe5f2, 0xe5f3, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe5f7, 0xfffd, 0xe5f8, 0xfffd, 0xfffd, 0xe5f6, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe5f4, 0xfffd, 0xe5ef, 0xe5f5, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe5f9, 0xe8b5, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x89a6, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe5fc, 0x8bdd, 0xe5fb, 0xfffd, 0xfffd, 0xfffd, 0xe641, 0xfffd, 0xe640, 0xfffd, 0xfffd, 0xfffd, 0xe643, 0xfffd, 0xfffd, 0xe642, 0xfffd, 0xe644, 0xfffd, 0xfffd, 0x8f50, 0xfffd, 0xe645, 0xfffd, 0xfffd, 0xe646, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe647, 0x90bc, 0xfffd, 0x9776, 0xfffd, 0xe648, 0xfffd, 0xfffd, 0x95a2, 0x9465, 0xe649, 0xfffd, 0xe64a, 0x8ca9, 0xfffd, 0xfffd, 0xfffd, 0x8b4b, 0xfffd, 0xfffd, 0xfffd, 0xe64b, 0xfffd, 0xfffd, 0x8e8b, 0x9460, 0xe64c, 0xfffd, 0x8a6f, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe64d, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe64f, 0x9797, 0xfffd, 0xe64e, 0x9065, 0xfffd, 0xe650, 0xfffd, 0xfffd, 0xe651, 0xfffd, 0xfffd, 0xe652, 0x8acf, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe653, 0xfffd, 0xfffd, 0xe654, 0xfffd, 0xe655, 0xe656, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8a70, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe657, 0xfffd, 0xe658, 0xe659, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x89f0, 0xfffd, 0xfffd, 0x9047, 0xe65a, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe65b, 0xfffd, 0xfffd, 0xfffd, 0xe65c, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8cbe, 0xfffd, 0x92f9, 0xe65d, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8c76, 0xfffd, 0x9075, 0xfffd, 0xe660, 0xfffd, 0x93a2, 0xfffd, 0xe65f, 0xfffd, 0xfffd, 0x8c50, 0xfffd, 0xfffd, 0xe65e, 0x91f5, 0x8b4c, 0xfffd, 0xfffd, 0xe661, 0xfffd, 0xe662, 0xfffd, 0x8fd7, 0xfffd, 0xfffd, 0xfffd, 0x8c8d, 0xfffd, 0xe663, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x964b, 0xfffd, 0xfffd, 0x90dd, 0xfffd, 0xfffd, 0xfffd, 0x8b96, 0xfffd, 0x96f3, 0x9169, 0xfffd, 0xe664, 0xfffd, 0xfffd, 0xfffd, 0x9066, 0x9290, 0x8fd8, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe665, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe668, 0xfffd, 0xe669, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8dbc, 0x91c0, 0xe667, 0xfffd, 0x8fd9, 0x955d, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe666, 0xfffd, 0xfffd, 0x8e8c, 0xfffd, 0x8972, 0xfffd, 0xe66d, 0x8c77, 0xfffd, 0xfffd, 0x8e8e, 0xfffd, 0xfffd, 0x8e8d, 0xfffd, 0x986c, 0xe66c, 0xe66b, 0x9146, 0xfffd, 0x8b6c, 0x9862, 0x8a59, 0x8fda, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe66a, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe66f, 0xfffd, 0xe670, 0xe66e, 0xfffd, 0x8cd6, 0xfffd, 0x975f, 0xfffd, 0xfffd, 0x8e8f, 0x9446, 0xfffd, 0xfffd, 0xfffd, 0xe673, 0xfffd, 0x90be, 0xfffd, 0x9261, 0xfffd, 0xfffd, 0x9755, 0xfffd, 0xe676, 0xfffd, 0xfffd, 0xfffd, 0x8cea, 0xfffd, 0x90bd, 0xe672, 0xfffd, 0xe677, 0x8ceb, 0xe674, 0xe675, 0xfffd, 0xe671, 0xfffd, 0xfffd, 0xfffd, 0x90e0, 0x93c7, 0xfffd, 0xfffd, 0x924e, 0xfffd, 0x89db, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x94ee, 0xfffd, 0xfffd, 0x8b62, 0xfffd, 0xfffd, 0x92b2, 0xfffd, 0xfffd, 0xe67a, 0xfffd, 0xe678, 0xfffd, 0xfffd, 0x926b, 0xfffd, 0xfffd, 0xfffd, 0x90bf, 0x8ad0, 0xe679, 0xfffd, 0x907a, 0xfffd, 0xfffd, 0x97c8, 0xfffd, 0xfffd, 0xfffd, 0x985f, 0xfffd, 0xfffd, 0xfffd, 0xe67b, 0xe687, 0x92b3, 0xfffd, 0xe686, 0xfffd, 0xe683, 0xe68b, 0xe684, 0xfffd, 0xe680, 0xfffd, 0x92fa, 0xe67e, 0xfffd, 0xfffd, 0xfffd, 0xe67c, 0xfffd, 0x9740, 0x8e90, 0xfffd, 0xfffd, 0xe681, 0xfffd, 0xe67d, 0xfffd, 0xfffd, 0xfffd, 0xe685, 0x8f94, 0xfffd, 0x8cbf, 0xfffd, 0xfffd, 0xfffd, 0x91f8, 0xfffd, 0x9664, 0x8979, 0x88e0, 0xfffd, 0x93a3, 0xfffd, 0xfffd, 0xe689, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe688, 0xfffd, 0x93e4, 0xfffd, 0xe68d, 0xfffd, 0xfffd, 0xfffd, 0xe682, 0xfffd, 0xe68c, 0xe68e, 0xfffd, 0x8caa, 0xe68a, 0x8d75, 0xfffd, 0x8ed3, 0xfffd, 0xfffd, 0xe68f, 0x9777, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe692, 0xfffd, 0xe695, 0xfffd, 0xfffd, 0xe693, 0x9554, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe690, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8bde, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe694, 0xfffd, 0xfffd, 0xe696, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe69a, 0xfffd, 0xfffd, 0xe697, 0xfffd, 0xe699, 0xe698, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe69b, 0xfffd, 0x8eaf, 0xfffd, 0xe69d, 0xe69c, 0x9588, 0xfffd, 0xfffd, 0xe69f, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8c78, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe69e, 0xe6a0, 0xfffd, 0xfffd, 0xe6a1, 0x8b63, 0xe3bf, 0x8ff7, 0xfffd, 0xe6a2, 0xfffd, 0xfffd, 0x8cec, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe6a3, 0xfffd, 0xfffd, 0xe6a4, 0xfffd, 0xfffd, 0x8e5d, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9dcc, 0xfffd, 0xe6a5, 0xfffd, 0xe6a6, 0xfffd, 0x8f51, 0xfffd, 0xe6a7, 0xe6a8, 0xfffd, 0xfffd, 0xe6a9, 0xfffd, 0xfffd, 0xe6aa, 0xe6ab, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x924a, 0xfffd, 0xfffd, 0xe6ac, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe6ae, 0xfffd, 0xe6ad, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x93a4, 0xfffd, 0xe6af, 0xfffd, 0x964c, 0xfffd, 0xe6b0, 0xfffd, 0xe6b1, 0xfffd, 0xe6b2, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe6b3, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x93d8, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8fdb, 0xe6b4, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8d8b, 0x98ac, 0xe6b5, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe6b6, 0x955e, 0xe6b7, 0xfffd, 0xe6bf, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe6b8, 0xfffd, 0xfffd, 0xe6ba, 0xfffd, 0xfffd, 0xfffd, 0xe6b9, 0xe6bb, 0xfffd, 0x9665, 0xe6bc, 0xe6bd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe6be, 0xfffd, 0xfffd, 0xfffd, 0xe6c0, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8a4c, 0x92e5, 0xfffd, 0x9589, 0x8de0, 0x8d76, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x956e, 0x89dd, 0x94cc, 0xe6c3, 0x8ad1, 0x90d3, 0xe6c2, 0xe6c7, 0x9299, 0x96e1, 0xfffd, 0xe6c5, 0xe6c6, 0x8b4d, 0xfffd, 0xe6c8, 0x9483, 0x91dd, 0xfffd, 0xfffd, 0x94ef, 0x935c, 0xe6c4, 0xfffd, 0x9666, 0x89ea, 0xe6ca, 0x9847, 0x92c0, 0x9864, 0xfffd, 0xfffd, 0x8e91, 0xe6c9, 0xfffd, 0x91af, 0xfffd, 0xfffd, 0xe6da, 0x9147, 0xfffd, 0xfffd, 0x93f6, 0xfffd, 0x956f, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe6cd, 0x8e5e, 0x8e92, 0xfffd, 0x8fdc, 0xfffd, 0x9485, 0xfffd, 0x8cab, 0xe6cc, 0xe6cb, 0xfffd, 0x958a, 0xfffd, 0xfffd, 0xfffd, 0x8ebf, 0xfffd, 0xfffd, 0x9371, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe6cf, 0xe6d0, 0x8d77, 0xe6ce, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe6d1, 0xe6d2, 0xfffd, 0xe6d4, 0x91a1, 0xfffd, 0xe6d3, 0x8ae4, 0xfffd, 0xe6d6, 0xfffd, 0xe6d5, 0xe6d7, 0xfffd, 0xfffd, 0xe6d9, 0xe6db, 0xfffd, 0xe6dc, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x90d4, 0xfffd, 0x8ecd, 0xe6dd, 0xfffd, 0xfffd, 0xfffd, 0x8a71, 0xfffd, 0xe6de, 0xfffd, 0xfffd, 0x9196, 0xe6df, 0xfffd, 0xe6e0, 0x958b, 0xfffd, 0xfffd, 0x8b4e, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe6e1, 0xfffd, 0xfffd, 0xfffd, 0x92b4, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x897a, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe6e2, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8eef, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9096, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x91ab, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe6e5, 0xfffd, 0xfffd, 0xfffd, 0xe6e4, 0xfffd, 0xfffd, 0xfffd, 0xe6e3, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe6eb, 0xe6e9, 0xfffd, 0xfffd, 0xe6e6, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe6e8, 0xfffd, 0xfffd, 0xfffd, 0xe6e7, 0xe6ea, 0xfffd, 0x8b97, 0xfffd, 0xe6ee, 0xfffd, 0x90d5, 0xfffd, 0xe6ef, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8cd7, 0xfffd, 0xe6ec, 0xe6ed, 0xfffd, 0xfffd, 0xfffd, 0x9848, 0xfffd, 0xfffd, 0xfffd, 0x92b5, 0xfffd, 0x9148, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe6f0, 0xfffd, 0xfffd, 0xe6f3, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe6f1, 0xe6f2, 0x9778, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x93a5, 0xe6f6, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe6f4, 0xe6f5, 0xe6f7, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe748, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe6fa, 0xfffd, 0xfffd, 0xfffd, 0xe6fb, 0xe6f9, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe6f8, 0xfffd, 0x92fb, 0xfffd, 0xfffd, 0xe740, 0xe744, 0xe741, 0xe6fc, 0xfffd, 0xe742, 0xfffd, 0xfffd, 0xfffd, 0xe743, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe74a, 0xfffd, 0xfffd, 0xfffd, 0xe745, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x90d6, 0xe747, 0xfffd, 0xfffd, 0xe749, 0xe746, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe74c, 0xfffd, 0x8f52, 0xfffd, 0xe74b, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe74d, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe74e, 0xfffd, 0xfffd, 0xe751, 0xe750, 0xfffd, 0xe74f, 0xfffd, 0xfffd, 0xe753, 0xe752, 0xfffd, 0x96f4, 0xfffd, 0xfffd, 0xfffd, 0xe755, 0xfffd, 0xe754, 0xe756, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe757, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe759, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe758, 0x9067, 0xe75a, 0xfffd, 0xfffd, 0x8beb, 0xe75b, 0xe75d, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe75e, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe75f, 0xe75c, 0xfffd, 0xe760, 0xfffd, 0x8ed4, 0xe761, 0x8b4f, 0x8c52, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8cac, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe762, 0xfffd, 0xfffd, 0xfffd, 0x93ee, 0xfffd, 0xfffd, 0x935d, 0xe763, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe766, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8eb2, 0xfffd, 0xfffd, 0xe765, 0xe764, 0x8c79, 0xe767, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8a72, 0xfffd, 0xe769, 0xfffd, 0xfffd, 0xfffd, 0x8dda, 0xe768, 0xfffd, 0xe771, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe76b, 0xe76d, 0x95e3, 0xe76a, 0xfffd, 0xfffd, 0xfffd, 0xe76c, 0xfffd, 0xe770, 0xe76e, 0x8b50, 0xfffd, 0xe76f, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe772, 0xfffd, 0xfffd, 0x9479, 0x97d6, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8f53, 0xfffd, 0xfffd, 0xfffd, 0xe773, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9741, 0xe775, 0xfffd, 0xe774, 0xfffd, 0xfffd, 0xe778, 0x9760, 0xfffd, 0xfffd, 0xe777, 0xfffd, 0x8a8d, 0xe776, 0xe77b, 0xfffd, 0xfffd, 0xe77a, 0xfffd, 0xfffd, 0xe779, 0x9351, 0xe77c, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe77d, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe77e, 0xfffd, 0xfffd, 0x8d8c, 0xfffd, 0x8c44, 0xe780, 0xe781, 0xe782, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9068, 0xe783, 0xfffd, 0x8eab, 0xe784, 0xfffd, 0xfffd, 0xfffd, 0xe785, 0xfffd, 0xfffd, 0xfffd, 0x999f, 0x999e, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe786, 0xe390, 0xe787, 0x9243, 0x904a, 0x945f, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe788, 0xfffd, 0xfffd, 0x95d3, 0x92d2, 0x8d9e, 0xfffd, 0xfffd, 0x9248, 0xfffd, 0xfffd, 0x8949, 0xfffd, 0x9698, 0x9076, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8c7d, 0xfffd, 0xfffd, 0x8bdf, 0xfffd, 0xfffd, 0x95d4, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe789, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe78b, 0xfffd, 0xfffd, 0xe78a, 0x89de, 0xfffd, 0xfffd, 0x93f4, 0xe78c, 0x9497, 0xfffd, 0x9352, 0xfffd, 0xe78d, 0x8f71, 0xfffd, 0xfffd, 0xfffd, 0xe78f, 0xfffd, 0xfffd, 0x96c0, 0xe79e, 0xe791, 0xe792, 0xfffd, 0xfffd, 0x92c7, 0xfffd, 0xfffd, 0x91de, 0x9197, 0xfffd, 0x93a6, 0xfffd, 0xe790, 0x8b74, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe799, 0xfffd, 0xe796, 0xe7a3, 0x93a7, 0x9280, 0xe793, 0xfffd, 0x92fc, 0x9372, 0xe794, 0xe798, 0x9080, 0xfffd, 0x9487, 0x92ca, 0xfffd, 0xfffd, 0x90c0, 0xe797, 0x91ac, 0x91a2, 0xe795, 0x88a7, 0x9841, 0xfffd, 0xfffd, 0xfffd, 0xe79a, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x91df, 0xfffd, 0xfffd, 0x8f54, 0x9069, 0xfffd, 0xfffd, 0xe79c, 0xe79b, 0xfffd, 0x88ed, 0xe79d, 0xfffd, 0xfffd, 0x954e, 0xfffd, 0xe7a5, 0xfffd, 0xfffd, 0x93d9, 0x908b, 0xfffd, 0xfffd, 0x9278, 0xfffd, 0x8bf6, 0xfffd, 0xe7a4, 0x9756, 0x895e, 0xfffd, 0x95d5, 0x89df, 0xe79f, 0xe7a0, 0xe7a1, 0xe7a2, 0x93b9, 0x9242, 0x88e1, 0xe7a6, 0xfffd, 0xe7a7, 0xeaa1, 0xfffd, 0xfffd, 0x91bb, 0xfffd, 0xe7a8, 0xfffd, 0x8993, 0x916b, 0xfffd, 0x8cad, 0xfffd, 0x9779, 0xfffd, 0xfffd, 0xe7a9, 0x934b, 0xfffd, 0xfffd, 0xfffd, 0x9198, 0x8ed5, 0xe7aa, 0xfffd, 0xfffd, 0xe7ad, 0xfffd, 0xfffd, 0x8f85, 0xe7ab, 0x914a, 0x9149, 0xfffd, 0x88e2, 0xfffd, 0x97c9, 0xe7af, 0xfffd, 0x94f0, 0xe7b1, 0xe7b0, 0xe7ae, 0xe284, 0x8ad2, 0xfffd, 0xfffd, 0xe78e, 0xfffd, 0xe7b3, 0xe7b2, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe7b4, 0xfffd, 0x9757, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x93df, 0xfffd, 0xfffd, 0x964d, 0xfffd, 0xe7b5, 0xfffd, 0x8ed7, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe7b6, 0xfffd, 0xe7b7, 0xfffd, 0xfffd, 0xfffd, 0xe7b8, 0xfffd, 0xfffd, 0x9340, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x88e8, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8d78, 0xfffd, 0xfffd, 0xfffd, 0x9859, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe7bc, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8c53, 0xe7b9, 0xfffd, 0xe7ba, 0xfffd, 0xfffd, 0xfffd, 0x9594, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8a73, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9758, 0xfffd, 0x8bbd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9373, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe7bd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe7be, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe7bf, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9341, 0xfffd, 0xfffd, 0xe7c1, 0xfffd, 0xe7c0, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x93d1, 0xe7c2, 0x8f55, 0x8ede, 0x947a, 0x9291, 0xfffd, 0xfffd, 0xfffd, 0x8ef0, 0xfffd, 0x908c, 0xfffd, 0xe7c3, 0xfffd, 0xe7c4, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x907c, 0xe7c5, 0xfffd, 0xe7c6, 0xfffd, 0xfffd, 0xfffd, 0xe7c7, 0x978f, 0xfffd, 0x8f56, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe7c9, 0xe7c8, 0xfffd, 0x8d79, 0xfffd, 0x8d93, 0x8e5f, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe7cc, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8f86, 0xfffd, 0xe7cb, 0xfffd, 0xe7ca, 0xfffd, 0x91e7, 0xfffd, 0xfffd, 0x8ced, 0xfffd, 0x90c1, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x94ae, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8f58, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe7cd, 0xfffd, 0x8fdd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe7d0, 0xe7ce, 0xfffd, 0xfffd, 0xfffd, 0xe7cf, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe7d2, 0xe7d1, 0xfffd, 0xfffd, 0x8ff8, 0xfffd, 0xe7d3, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe7d4, 0xe7d5, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x94ce, 0x8dd1, 0x8edf, 0xe7d6, 0xfffd, 0xe7d7, 0x97a2, 0x8f64, 0x96ec, 0x97ca, 0xe7d8, 0x8be0, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe7d9, 0xfffd, 0x9342, 0xfffd, 0xfffd, 0xe7dc, 0x8a98, 0x906a, 0xfffd, 0xe7da, 0xfffd, 0xe7db, 0xfffd, 0x92de, 0xfffd, 0xfffd, 0x9674, 0x8bfa, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe7de, 0xe7df, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe7dd, 0xfffd, 0xfffd, 0xe7e1, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x93dd, 0x8a62, 0xfffd, 0xfffd, 0xe7e5, 0xfffd, 0xfffd, 0xe7e2, 0xe7e4, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe7e0, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe86e, 0xfffd, 0xfffd, 0xe7e3, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x97e9, 0xfffd, 0xfffd, 0x8cd8, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe7ed, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9353, 0xe7e8, 0xfffd, 0xfffd, 0xe7eb, 0xe7e9, 0xfffd, 0xe7ee, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe7ef, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe7e7, 0xfffd, 0xfffd, 0xe7f4, 0x8994, 0xfffd, 0xfffd, 0xe7e6, 0xfffd, 0xfffd, 0xfffd, 0x94ab, 0xfffd, 0xe7ea, 0xfffd, 0x8fde, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8d7a, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9667, 0xfffd, 0x8be2, 0xfffd, 0xfffd, 0x8f65, 0xfffd, 0x93ba, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x914c, 0xfffd, 0xe7f2, 0xfffd, 0xe7ec, 0xe7f1, 0xfffd, 0x96c1, 0xfffd, 0x92b6, 0xe7f3, 0xe7f0, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x914b, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe7f7, 0xfffd, 0xe7f6, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe7f5, 0xfffd, 0xfffd, 0x964e, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8f9b, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe7f8, 0x95dd, 0xfffd, 0xfffd, 0x8973, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9565, 0x9292, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8b98, 0xfffd, 0xe7fa, 0xfffd, 0x8d7c, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8e4b, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe7f9, 0x908d, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x908e, 0xe840, 0xe842, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8ff9, 0xfffd, 0xe841, 0xe843, 0xfffd, 0xfffd, 0x8bd1, 0xfffd, 0x9564, 0xfffd, 0xfffd, 0x8ee0, 0x9842, 0xfffd, 0xe7fc, 0x8df6, 0xfffd, 0xfffd, 0x985e, 0xfffd, 0xfffd, 0xe845, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe844, 0xe846, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe7fb, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x93e7, 0xfffd, 0x9374, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x92d5, 0xfffd, 0xe84b, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9262, 0xe847, 0xfffd, 0xfffd, 0xfffd, 0xe848, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8c4c, 0xfffd, 0xe84a, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8cae, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe849, 0xfffd, 0x8fdf, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8a99, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe84f, 0xfffd, 0x8dbd, 0x9199, 0xfffd, 0xfffd, 0x92c8, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8a5a, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe84d, 0xe84e, 0x92c1, 0xfffd, 0xe84c, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe850, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe856, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe859, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe858, 0x934c, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe851, 0xe852, 0xe855, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe857, 0xfffd, 0xfffd, 0xfffd, 0x8bbe, 0xfffd, 0xfffd, 0xe85a, 0xe854, 0xfffd, 0xfffd, 0xe853, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe85e, 0xfffd, 0xfffd, 0xfffd, 0xe85f, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe860, 0xfffd, 0xfffd, 0xe85d, 0xe85c, 0xfffd, 0xfffd, 0xfffd, 0x8fe0, 0x93a8, 0xe85b, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe864, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe862, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe863, 0xe861, 0xfffd, 0x91f6, 0xfffd, 0xe865, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe866, 0xfffd, 0xfffd, 0xe868, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8ad3, 0xe867, 0x96f8, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe873, 0xe869, 0xfffd, 0xfffd, 0xe86c, 0xfffd, 0xe86a, 0xfffd, 0xe86b, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe86d, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe86f, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe870, 0xfffd, 0xe871, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe874, 0xe872, 0xe875, 0xe877, 0xfffd, 0xe876, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x92b7, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x96e5, 0xfffd, 0xe878, 0x914d, 0xfffd, 0xfffd, 0xfffd, 0xe879, 0xfffd, 0x95c2, 0xe87a, 0x8a4a, 0xfffd, 0xfffd, 0xfffd, 0x895b, 0xfffd, 0x8ad5, 0xfffd, 0x8ad4, 0xe87b, 0xfffd, 0xe87c, 0xfffd, 0xe87d, 0xe87e, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe880, 0xfffd, 0x8ad6, 0x8a74, 0x8d7d, 0x94b4, 0xfffd, 0xe882, 0xe881, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe883, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x897b, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe886, 0xfffd, 0xe885, 0xe884, 0xfffd, 0xe887, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe88a, 0xfffd, 0xfffd, 0xfffd, 0x88c5, 0xfffd, 0xfffd, 0xe888, 0xfffd, 0xe88c, 0xe88b, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe88e, 0xe88d, 0xe88f, 0xfffd, 0x93ac, 0xfffd, 0xfffd, 0xfffd, 0xe890, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe891, 0xe893, 0xfffd, 0xfffd, 0xe892, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x958c, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe894, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe895, 0xfffd, 0x8de3, 0xfffd, 0xfffd, 0xfffd, 0xe896, 0xe897, 0xfffd, 0xfffd, 0x9668, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x916a, 0xfffd, 0xfffd, 0xfffd, 0x88a2, 0x91c9, 0xfffd, 0xe898, 0xfffd, 0x958d, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe89b, 0xe899, 0x8d7e, 0xfffd, 0xe89a, 0x8cc0, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x95c3, 0xe89d, 0xe89f, 0xe89e, 0xe8a0, 0xfffd, 0xfffd, 0x8940, 0x9077, 0x8f9c, 0x8ad7, 0xe8a1, 0xfffd, 0xfffd, 0xfffd, 0x9486, 0xfffd, 0xe8a3, 0xfffd, 0xfffd, 0xfffd, 0x8941, 0xfffd, 0xe8a2, 0x92c2, 0xfffd, 0x97cb, 0x93a9, 0xe89c, 0x97a4, 0xfffd, 0x8caf, 0xfffd, 0xfffd, 0x977a, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8bf7, 0x97b2, 0xfffd, 0x8c47, 0xfffd, 0x91e0, 0xe440, 0xfffd, 0xe8a4, 0x8a4b, 0x908f, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8a75, 0xe8a6, 0xfffd, 0xe8a7, 0xe8a5, 0x8c84, 0xfffd, 0x8ddb, 0x8fe1, 0xfffd, 0xfffd, 0xfffd, 0x8942, 0xfffd, 0xfffd, 0x97d7, 0xfffd, 0xfffd, 0xfffd, 0xe8a9, 0xe7ac, 0xfffd, 0xe8a8, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe8ac, 0xe8aa, 0xe8ab, 0xfffd, 0xe8ad, 0xfffd, 0xe8ae, 0x97ea, 0xe8af, 0xe8b0, 0xfffd, 0x90c7, 0x94b9, 0xfffd, 0xfffd, 0xfffd, 0x909d, 0x8ae5, 0xfffd, 0xfffd, 0x9759, 0x89eb, 0x8f57, 0x8cd9, 0xfffd, 0xe8b3, 0xfffd, 0xe8b2, 0x8e93, 0xe8b4, 0xe8b1, 0xfffd, 0xfffd, 0x8e47, 0xfffd, 0xfffd, 0xfffd, 0xe8b8, 0xe5ab, 0xfffd, 0xfffd, 0x99d4, 0xfffd, 0x9097, 0xe8b6, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x97a3, 0x93ef, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x894a, 0xfffd, 0x90e1, 0x8eb4, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x95b5, 0xfffd, 0x895f, 0xfffd, 0xfffd, 0xfffd, 0x97eb, 0x978b, 0xfffd, 0xe8b9, 0xfffd, 0x9364, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8ef9, 0xfffd, 0xfffd, 0xfffd, 0xe8ba, 0xfffd, 0xe8bb, 0x906b, 0xe8bc, 0xfffd, 0x97ec, 0xfffd, 0xfffd, 0xe8b7, 0xe8be, 0xe8c0, 0xfffd, 0xe8bf, 0xfffd, 0xe8bd, 0xfffd, 0xfffd, 0xe8c1, 0xfffd, 0xfffd, 0xe8c2, 0xfffd, 0xfffd, 0x919a, 0xfffd, 0x89e0, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe8c3, 0xfffd, 0xfffd, 0x96b6, 0xfffd, 0xfffd, 0xe8c4, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe8c5, 0xfffd, 0x9849, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9e50, 0xe8c6, 0xfffd, 0xfffd, 0xfffd, 0xe8c7, 0xe8c8, 0xfffd, 0xfffd, 0xfffd, 0xe8cc, 0xfffd, 0xe8c9, 0xfffd, 0xe8ca, 0xfffd, 0xe8cb, 0xe8cd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x90c2, 0xfffd, 0xfffd, 0xfffd, 0x96f5, 0xfffd, 0xfffd, 0x90c3, 0xfffd, 0xfffd, 0xe8ce, 0xfffd, 0x94f1, 0xfffd, 0xe8cf, 0xea72, 0x96ca, 0xfffd, 0xe8d0, 0xfffd, 0xe8d1, 0xfffd, 0xe8d2, 0x8a76, 0xfffd, 0xe8d4, 0xfffd, 0x9078, 0xfffd, 0xfffd, 0xfffd, 0xe8d5, 0xfffd, 0xfffd, 0x8c43, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe8d6, 0xe8da, 0xfffd, 0xe8d8, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe8d9, 0xfffd, 0xfffd, 0x8a93, 0xe8d7, 0xe8db, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe8dc, 0xfffd, 0x88c6, 0xfffd, 0xe8dd, 0xe8de, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8fe2, 0xfffd, 0xfffd, 0xfffd, 0xe8df, 0xfffd, 0xfffd, 0xfffd, 0x8b66, 0xfffd, 0xfffd, 0xe8e2, 0xfffd, 0xfffd, 0xe8e1, 0xfffd, 0xe8e0, 0xfffd, 0xfffd, 0xe691, 0xfffd, 0x95da, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe8e3, 0xe8e4, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe8e5, 0xfffd, 0xfffd, 0xe8e6, 0xfffd, 0xe8e7, 0xfffd, 0xfffd, 0xe8e8, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8ad8, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe8e9, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe8ea, 0x9442, 0xfffd, 0xfffd, 0xfffd, 0xe8ec, 0x89b9, 0xfffd, 0xe8ef, 0xe8ee, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8943, 0xfffd, 0xfffd, 0xfffd, 0x8bbf, 0xfffd, 0x95c5, 0x92b8, 0x8da0, 0xfffd, 0x8d80, 0x8f87, 0xfffd, 0x907b, 0xfffd, 0xfffd, 0xfffd, 0xe8f1, 0xfffd, 0xfffd, 0xe8f0, 0x9761, 0x8ae6, 0x94d0, 0x93da, 0xfffd, 0xfffd, 0xfffd, 0x909c, 0x97cc, 0xfffd, 0x8c7a, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe8f4, 0xfffd, 0xfffd, 0xe8f3, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x966a, 0x93aa, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x896f, 0xfffd, 0xfffd, 0xe8f5, 0xe8f2, 0xfffd, 0xfffd, 0x9570, 0x978a, 0xe8f6, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe8f7, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe8f9, 0x91e8, 0x8a7a, 0x8a7b, 0xe8f8, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8ae7, 0x8cb0, 0xfffd, 0xfffd, 0x8ae8, 0xfffd, 0xfffd, 0x935e, 0xfffd, 0xfffd, 0x97de, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8cda, 0xfffd, 0xfffd, 0xfffd, 0xe8fa, 0xfffd, 0xfffd, 0xfffd, 0xe8fb, 0xe8fc, 0xe940, 0xfffd, 0xe942, 0xe941, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9597, 0xfffd, 0xe943, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe944, 0xfffd, 0xe945, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe946, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe948, 0xe947, 0xfffd, 0xe949, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x94f2, 0xe3ca, 0xfffd, 0xfffd, 0x9048, 0xfffd, 0xfffd, 0x8b51, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe94a, 0xfffd, 0xe94b, 0xfffd, 0x99aa, 0x9f5a, 0x94d1, 0xfffd, 0xfffd, 0x88f9, 0xfffd, 0x88b9, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8e94, 0x964f, 0x8ffc, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe94c, 0xfffd, 0x96dd, 0xfffd, 0xfffd, 0xfffd, 0xe94d, 0x977b, 0xfffd, 0x8961, 0xfffd, 0xfffd, 0xfffd, 0x8e60, 0xfffd, 0xe94e, 0x89ec, 0xe94f, 0xfffd, 0xfffd, 0xfffd, 0xe950, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe952, 0xe953, 0xfffd, 0xe955, 0xe951, 0xfffd, 0xfffd, 0xe954, 0xfffd, 0xfffd, 0xfffd, 0x8ad9, 0xfffd, 0xfffd, 0xfffd, 0xe956, 0xfffd, 0xe957, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe958, 0xe959, 0xfffd, 0xfffd, 0xfffd, 0xe95a, 0xfffd, 0xfffd, 0xe95c, 0xfffd, 0xfffd, 0xfffd, 0xe95b, 0xfffd, 0xe95e, 0xe961, 0xfffd, 0xfffd, 0xfffd, 0xe95d, 0xe95f, 0xe960, 0xfffd, 0xfffd, 0xe962, 0xfffd, 0x8bc0, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8ef1, 0xe963, 0xe964, 0x8d81, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe965, 0xfffd, 0xfffd, 0x8a5d, 0xfffd, 0xfffd, 0xfffd, 0x946e, 0xe966, 0xe967, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9279, 0x93e9, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe968, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x949d, 0xfffd, 0xfffd, 0x91ca, 0x8977, 0x8bec, 0xfffd, 0x8bed, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9293, 0xe96d, 0x8bee, 0xfffd, 0xfffd, 0x89ed, 0xfffd, 0xfffd, 0xe96c, 0xfffd, 0xfffd, 0xe96a, 0xfffd, 0xe96b, 0xfffd, 0xe969, 0xfffd, 0xfffd, 0xe977, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe96e, 0xe96f, 0xfffd, 0xfffd, 0xe970, 0xe971, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe973, 0xfffd, 0xfffd, 0xe972, 0xfffd, 0xfffd, 0xfffd, 0x8f78, 0xfffd, 0xe974, 0xfffd, 0xfffd, 0xfffd, 0xe976, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8b52, 0xe975, 0xfffd, 0xfffd, 0x919b, 0x8cb1, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe978, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x91cb, 0xfffd, 0xfffd, 0xe979, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x93ab, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe97a, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe980, 0xfffd, 0xe97d, 0xfffd, 0xe97c, 0xe97e, 0xfffd, 0xe97b, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe982, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe981, 0xfffd, 0xe984, 0xfffd, 0xfffd, 0x8bc1, 0xe983, 0xfffd, 0xfffd, 0xfffd, 0xe985, 0xfffd, 0xfffd, 0xe986, 0xfffd, 0xe988, 0xe987, 0xfffd, 0xfffd, 0xfffd, 0xe989, 0xe98b, 0xe98a, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8d9c, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe98c, 0xfffd, 0xfffd, 0xe98d, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8a5b, 0xfffd, 0xfffd, 0xfffd, 0xe98e, 0xfffd, 0xfffd, 0xfffd, 0xe98f, 0xfffd, 0xfffd, 0xfffd, 0x9091, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe990, 0xfffd, 0xe991, 0xfffd, 0xe992, 0xe993, 0xfffd, 0xfffd, 0xfffd, 0x8d82, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe994, 0xe995, 0xfffd, 0xfffd, 0xe996, 0xe997, 0xfffd, 0xfffd, 0xe998, 0xfffd, 0xfffd, 0xfffd, 0x94af, 0xe99a, 0xfffd, 0x9545, 0xe99b, 0xe999, 0xfffd, 0xe99d, 0xfffd, 0xfffd, 0xe99c, 0xfffd, 0xfffd, 0xe99e, 0xfffd, 0xfffd, 0xfffd, 0xe99f, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe9a0, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe9a1, 0xfffd, 0xe9a2, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe9a3, 0xfffd, 0xfffd, 0xe9a4, 0xe9a5, 0xfffd, 0xe9a6, 0xfffd, 0xe9a7, 0xe9a8, 0xe9a9, 0xe9aa, 0xfffd, 0xfffd, 0xfffd, 0xe9ab, 0xe9ac, 0xfffd, 0x9f54, 0xe9ad, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe2f6, 0x8b53, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8a40, 0x8db0, 0xe9af, 0xe9ae, 0x96a3, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe9b1, 0xe9b2, 0xe9b0, 0xfffd, 0xe9b3, 0xfffd, 0xfffd, 0x9682, 0xfffd, 0xfffd, 0xfffd, 0xe9b4, 0xfffd, 0x8b9b, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9844, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe9b5, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe9b7, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x88bc, 0xfffd, 0xfffd, 0xe9b8, 0x95a9, 0xe9b6, 0xfffd, 0xfffd, 0xe9b9, 0xe9ba, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe9bb, 0xe9bc, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe9bd, 0xfffd, 0x968e, 0x8e4c, 0xfffd, 0x8df8, 0x914e, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe9be, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe9c1, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe9bf, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe9c2, 0xfffd, 0xfffd, 0x8cef, 0xe9c0, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe9c3, 0xfffd, 0xe9c4, 0xe9c5, 0xfffd, 0xe9c9, 0xfffd, 0x8e49, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x91e2, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe9ca, 0xe9c7, 0xe9c6, 0xe9c8, 0xfffd, 0xfffd, 0xfffd, 0x8c7e, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe9ce, 0xe9cd, 0xe9cc, 0xfffd, 0xfffd, 0x88b1, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe9d8, 0xfffd, 0xe9d4, 0xfffd, 0xe9d5, 0xe9d1, 0xe9d7, 0xfffd, 0xe9d3, 0x8a82, 0xfffd, 0xfffd, 0x986b, 0xfffd, 0xe9d6, 0xe9d2, 0xe9d0, 0xe9cf, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe9da, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe9dd, 0xfffd, 0xfffd, 0xe9dc, 0xe9db, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9568, 0xe9d9, 0x88f1, 0xe9de, 0xfffd, 0xe9e0, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8a8f, 0xe9cb, 0x8956, 0xfffd, 0xfffd, 0xe9e2, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe9e1, 0xe9df, 0x924c, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9690, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x97d8, 0xfffd, 0xfffd, 0xe9e3, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe9e4, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe9e5, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe9e6, 0xfffd, 0xe9e7, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x92b9, 0xfffd, 0xe9e8, 0xfffd, 0x94b5, 0xfffd, 0xe9ed, 0xe9e9, 0xfffd, 0xfffd, 0xfffd, 0xe9ea, 0xfffd, 0xfffd, 0x9650, 0x96c2, 0xfffd, 0x93ce, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xe9ee, 0xfffd, 0xfffd, 0xe9ef, 0x93bc, 0xe9ec, 0xe9eb, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x89a8, 0xfffd, 0xfffd, 0xfffd, 0xe9f7, 0xfffd, 0xfffd, 0xe9f6, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8995, 0xfffd, 0xfffd, 0xfffd, 0xe9f4, 0xfffd, 0xfffd, 0xfffd, 0xe9f3, 0xfffd, 0xfffd, 0xe9f1, 0xfffd, 0x8a9b, 0xfffd, 0xe9f0, 0x8eb0, 0x89a7, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8d83, 0xfffd, 0xfffd, 0xe9fa, 0xe9f9, 0xfffd, 0xe9f8, 0xfffd, 0xfffd, 0xe9f5, 0xfffd, 0xe9fb, 0xfffd, 0xe9fc, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xea44, 0xea43, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xea45, 0xfffd, 0xfffd, 0x894c, 0xea40, 0xea41, 0xfffd, 0x8d94, 0x96b7, 0xfffd, 0xfffd, 0xea42, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9651, 0xfffd, 0xfffd, 0xea4a, 0xfffd, 0xfffd, 0xea46, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xea4b, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xea48, 0xfffd, 0xea47, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8c7b, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xea4c, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xea4d, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xea4e, 0xfffd, 0xea49, 0xfffd, 0xfffd, 0xfffd, 0xe9f2, 0xfffd, 0xfffd, 0xea4f, 0xfffd, 0x92df, 0xfffd, 0xfffd, 0xfffd, 0xea53, 0xfffd, 0xea54, 0xea52, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xea51, 0xea57, 0xfffd, 0xea50, 0xfffd, 0xea55, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xea56, 0xfffd, 0xfffd, 0xfffd, 0xea59, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xea58, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xea5b, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xea5c, 0xfffd, 0xea5d, 0xfffd, 0xfffd, 0x9868, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xea5a, 0x91e9, 0x8deb, 0xfffd, 0xfffd, 0xea5e, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xea5f, 0xea60, 0xfffd, 0xfffd, 0xea61, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xea62, 0xfffd, 0xfffd, 0x8cb2, 0xea63, 0xfffd, 0xfffd, 0xfffd, 0xea64, 0xfffd, 0x8ead, 0xfffd, 0xea65, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xea66, 0xfffd, 0xfffd, 0xea67, 0xea68, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xea6b, 0xea69, 0x985b, 0xfffd, 0xea6a, 0xfffd, 0x97ed, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xea6c, 0xfffd, 0x97d9, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xea6d, 0x949e, 0xfffd, 0xfffd, 0xea6e, 0xea70, 0xfffd, 0xfffd, 0xea71, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xea6f, 0x8d8d, 0x96cb, 0x9683, 0x9bf5, 0xfffd, 0x9f80, 0x969b, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x89a9, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xea73, 0x8b6f, 0xea74, 0xea75, 0xea76, 0xfffd, 0x8d95, 0xfffd, 0xea77, 0xfffd, 0xfffd, 0xfffd, 0xe0d2, 0x96d9, 0xfffd, 0x91e1, 0xea78, 0xea7a, 0xea79, 0xfffd, 0xea7b, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xea7c, 0xfffd, 0xfffd, 0xea7d, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xea7e, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xea80, 0xfffd, 0xea81, 0xea82, 0xfffd, 0xea83, 0xfffd, 0xea84, 0xea85, 0xea86, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xea87, 0xea88, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9343, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x8cdb, 0xfffd, 0xea8a, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x916c, 0xea8b, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xea8c, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x9540, 0xfffd, 0xfffd, 0xea8d, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xea8e, 0xe256, 0xfffd, 0xfffd, 0xe6d8, 0xe8eb, 0xfffd, 0xfffd, 0xea8f, 0xfffd, 0xea90, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xea92, 0xea93, 0xea94, 0x97ee, 0xea91, 0xfffd, 0xfffd, 0xea95, 0xea96, 0xfffd, 0xfffd, 0xea98, 0xfffd, 0xea97, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xea9a, 0xfffd, 0xfffd, 0xfffd, 0xea9b, 0xea99, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x97b4, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xea9c, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xea9d, 0xe273, 0xfffd, 0xfffd, 0xea9e, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }; #endif /* KANJI */ /* Blah-to-Unicode translation tables */ struct x_to_unicode u_transparent = { 256, X2U_CXG, 0, 0, "Transparent", "transparent", 0, "", 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF }; /* 7-bit character sets: ISO 646, DEC NRCs, Short KOI, and Hebrew-7 */ struct x_to_unicode u_ascii = { 94, 33, X2U_ISO|X2U_STD, AL_ROMAN, "US ASCII", "ascii", 6, "B", 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e }; struct x_to_unicode u_british = { 94, 33, X2U_ISO|X2U_STD, AL_ROMAN, "British ISO 646", "british", 1, "A", 0x0021, 0x0022, 0x00a3, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e }; struct x_to_unicode u_dutch = { 94, 33, X2U_DEC|X2U_STD, AL_ROMAN, "Dutch NRC", "dutch", 0, "4", 0x0021, 0x0022, 0x00a3, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x00be, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x00ff, 0x00bd, 0x007c, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x00a8, 0x00a4, 0x00bc, 0x0027 }; struct x_to_unicode u_finnish = { 94, 33, X2U_DEC|X2U_STD, AL_ROMAN, "Finnish NRC", "finnish", 0, "5C", 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x00c4, 0x00d6, 0x00c5, 0x00dc, 0x005f, 0x00e9, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x00e4, 0x00f6, 0x00e5, 0x00fc }; struct x_to_unicode u_french = { 94, 33, X2U_ISO|X2U_STD, AL_ROMAN, "French ISO 646", "french", 0, "fR", 0x0021, 0x0022, 0x00a3, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x00e0, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x00b0, 0x00e7, 0x00a7, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x00e9, 0x00f9, 0x00e8, 0x00a8 }; struct x_to_unicode u_fr_canadian = { 94,33,X2U_DEC|X2U_STD,AL_ROMAN,"French Canadian NRC","canadian-french",0,"9Q", 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x00e0, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x00e2, 0x00e7, 0x00ea, 0x00ee, 0x005f, 0x00f4, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x00e9, 0x00f9, 0x00e8, 0x00fb }; struct x_to_unicode u_german = { 94, 33, X2U_ISO|X2U_STD, AL_ROMAN, "German ISO 646", "german", 21, "K", 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x00a7, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x00c4, 0x00d6, 0x00dc, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x00e4, 0x00f6, 0x00fc, 0x00df }; struct x_to_unicode u_hungarian = { 94, 33, X2U_ISO|X2U_STD, AL_ROMAN, "Hungarian ISO 646","hungarian",86,"i", 0x0021, 0x0022, 0x0023, 0x00a4, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x00c1, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x00c9, 0x00d6, 0x00dc, 0x005e, 0x005f, 0x00e1, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x00e9, 0x00f6, 0x00fc, 0x0022, 0x02dd }; struct x_to_unicode u_italian = { 94, 33, X2U_ISO|X2U_STD, AL_ROMAN, "Italian ISO 646", "italian", 15, "Y", 0x0021, 0x0022, 0x00a3, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x00a7, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x00b0, 0x00e7, 0x00e9, 0x005e, 0x005f, 0x00f9, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x00e0, 0x00f2, 0x00e8, 0x00ec }; struct x_to_unicode u_icelandic = { 94, 33, X2U_DEC|X2U_STD, AL_ROMAN, "Icelandic NRC", "icelandic", 0, NULL, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x00de, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x00f0, 0x00d8, 0x00c6, 0x00d6, 0x005f, 0x00fe, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x00f0, 0x007c, 0x00e6, 0x00f6 }; struct x_to_unicode u_jis0201r = { 94, 33, X2U_ISO|X2U_STD,AL_ROMAN,"Japanese Roman","japanese-roman",14,"J", 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x00a5, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x203e }; struct x_to_unicode u_jis0201k = { 94, 33, X2U_ISO|X2U_STD,AL_KANA,"Japanese Katakana", "katakana", 13, "I", 0xff61, 0xff62, 0xff63, 0xff64, 0xff65, 0xff66, 0xff67, 0xff68, 0xff69, 0xff6a, 0xff6b, 0xff6c, 0xff6d, 0xff6e, 0xff6f, 0xff70, 0xff71, 0xff72, 0xff73, 0xff74, 0xff75, 0xff76, 0xff77, 0xff78, 0xff79, 0xff7a, 0xff7b, 0xff7c, 0xff7d, 0xff7e, 0xff7f, 0xff80, 0xff81, 0xff82, 0xff83, 0xff84, 0xff85, 0xff86, 0xff87, 0xff88, 0xff89, 0xff8a, 0xff8b, 0xff8c, 0xff8d, 0xff8e, 0xff8f, 0xff90, 0xff91, 0xff92, 0xff93, 0xff94, 0xff95, 0xff96, 0xff97, 0xff98, 0xff99, 0xff9a, 0xff9b, 0xff9c, 0xff9d, 0xff9e, 0xff9f, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd }; struct x_to_unicode u_norwegian = { /* Same as Danish */ 94,33,X2U_ISO|X2U_STD,AL_ROMAN,"Norwegian ISO 646", "norwegian", 60, "`E6", 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x00c6, 0x00d8, 0x00c5, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x00e6, 0x00f8, 0x00e5, 0x007e }; struct x_to_unicode u_danish = { /* Same as Norwegian */ 94, 33, X2U_ISO|X2U_STD, AL_ROMAN, "Danish ISO 646", "danish", 60, "`E6", 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x00c6, 0x00d8, 0x00c5, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x00e6, 0x00f8, 0x00e5, 0x007e }; struct x_to_unicode u_portuguese = { 94,33,X2U_ISO|X2U_STD,AL_ROMAN,"Portuguese ISO 646","portuguese",16,"L%6", 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x00c6, 0x00d8, 0x00c5, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x00e6, 0x00f8, 0x00e5, 0x007e }; struct x_to_unicode u_spanish = { 94, 33, X2U_ISO|X2U_STD, AL_ROMAN, "Spanish ISO 646", "spanish", 17, "Z", 0x0021, 0x0022, 0x00a3, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x00a7, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x00a1, 0x00d1, 0x00bf, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x00b0, 0x00f1, 0x00e7, 0x007e }; struct x_to_unicode u_swedish = { 94, 33, X2U_ISO|X2U_STD, AL_ROMAN, "Swedish ISO 646", "swedish", 11, "HG", 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x00c9, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x00c4, 0x00d6, 0x00c5, 0x00dc, 0x005f, 0x00e9, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x00e4, 0x00f6, 0x00e5, 0x00fc }; struct x_to_unicode u_swiss = { 94, 33, X2U_DEC|X2U_STD, AL_ROMAN, "Swiss NRC", "swiss", 0, "=", 0x0021, 0x0022, 0x00f9, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x00e0, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x00e9, 0x00e7, 0x00ea, 0x00ee, 0x00e8, 0x00f4, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x00e4, 0x00f6, 0x00fc, 0x00fb }; struct x_to_unicode u_koi7 = { 94, 33, X2U_STD, AL_CYRIL, "Short KOI", "short-koi", 0, NULL, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x042e, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413, 0x0425, 0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, 0x041f, 0x042f, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412, 0x042c, 0x042b, 0x0417, 0x0428, 0x042d, 0x0429, 0x0427 }; struct x_to_unicode u_elot927 = { 94, 33, X2U_STD, AL_GREEK, "ELOT 927", "elot927-greek", 0, NULL, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, 0x039e, 0x039f, 0x03a0, 0x03a1, 0x03a3, 0x03a4, 0x03a5, 0x03a6, 0x03a7, 0x03a8, 0x03a9, 0x0020, 0x0020, 0x007b, 0x007c, 0x007d, 0x007e }; struct x_to_unicode u_hebrew7 = { 94, 33, X2U_STD, AL_HEBREW, "Hebrew-7", "hebrew-7", 0, NULL, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x05d0, 0x05d1, 0x05d2, 0x05d3, 0x05d4, 0x05d5, 0x05d6, 0x05d7, 0x05d8, 0x05d9, 0x05da, 0x05db, 0x05dc, 0x05dd, 0x05de, 0x05df, 0x05e0, 0x05e1, 0x05e2, 0x05e3, 0x05e4, 0x05e5, 0x05e6, 0x05e7, 0x05e8, 0x05e9, 0x05ea, 0x007b, 0x007c, 0x007d, 0x007e }; struct x_to_unicode u_apl1 = { 94, 33, X2U_ISO|X2U_STD, AL_ROMAN, "APL ISO", "apl-iso", 68, "e", 0x00a8, 0x0029, 0x003c, 0x2264, 0x003d, 0x003e, 0x005d, 0x2228, 0x2227, 0x2260, 0x00f7, 0x002c, 0x002b, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x0028, 0x005b, 0x003b, 0x00d7, 0x003a, 0x005c, 0x00af, 0x237a, 0x22a5, 0x22c2, 0x230a, 0x220a, 0x005f, 0x2207, 0x2206, 0x2373, 0x2218, 0x0027, 0x25af, 0x007c, 0x22a4, 0x25cb, 0x22c6, 0x003f, 0x2374, 0x2308, 0x223c, 0x2193, 0x222a, 0x2375, 0x2283, 0x2191, 0x2282, 0x2190, 0x22a2, 0x2192, 0x2265, 0x002d, 0x22c4, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x007b, 0x22a3, 0x007d, 0x0024 }; /* ISO 8859 Latin Alphabets */ struct x_to_unicode u_8859_1 = { 96, 32, X2U_ISO|X2U_STD, AL_ROMAN, "ISO Latin-1", "latin1", 100, "A", 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF }; struct x_to_unicode u_8859_2 = { 96, 32, X2U_ISO|X2U_STD, AL_ROMAN, "ISO Latin-2", "latin2", 101, "B", 0x00A0, 0x0104, 0x02D8, 0x0141, 0x00A4, 0x013D, 0x015A, 0x00A7, 0x00A8, 0x0160, 0x015E, 0x0164, 0x0179, 0x00AD, 0x017D, 0x017B, 0x00B0, 0x0105, 0x02DB, 0x0142, 0x00B4, 0x013E, 0x015B, 0x02C7, 0x00B8, 0x0161, 0x015F, 0x0165, 0x017A, 0x02DD, 0x017E, 0x017C, 0x0154, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x0139, 0x0106, 0x00C7, 0x010C, 0x00C9, 0x0118, 0x00CB, 0x011A, 0x00CD, 0x00CE, 0x010E, 0x0110, 0x0143, 0x0147, 0x00D3, 0x00D4, 0x0150, 0x00D6, 0x00D7, 0x0158, 0x016E, 0x00DA, 0x0170, 0x00DC, 0x00DD, 0x0162, 0x00DF, 0x0155, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x013A, 0x0107, 0x00E7, 0x010D, 0x00E9, 0x0119, 0x00EB, 0x011B, 0x00ED, 0x00EE, 0x010F, 0x0111, 0x0144, 0x0148, 0x00F3, 0x00F4, 0x0151, 0x00F6, 0x00F7, 0x0159, 0x016F, 0x00FA, 0x0171, 0x00FC, 0x00FD, 0x0163, 0x02D9 }; struct x_to_unicode u_8859_3 = { 96, 32, X2U_ISO|X2U_STD, AL_ROMAN, "ISO Latin-3", "latin3", 109, "C", 0x00A0, 0x0126, 0x02D8, 0x00A3, 0x00A4, 0xfffd, 0x0124, 0x00A7, 0x00A8, 0x0130, 0x015E, 0x011E, 0x0134, 0x00AD, 0xfffd, 0x017B, 0x00B0, 0x0127, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x0125, 0x00B7, 0x00B8, 0x0131, 0x015F, 0x011F, 0x0135, 0x00BD, 0xfffd, 0x017C, 0x00C0, 0x00C1, 0x00C2, 0xfffd, 0x00C4, 0x010A, 0x0108, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, 0xfffd, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x0120, 0x00D6, 0x00D7, 0x011C, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x016C, 0x015C, 0x00DF, 0x00E0, 0x00E1, 0x00E2, 0xfffd, 0x00E4, 0x010B, 0x0109, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, 0xfffd, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x0121, 0x00F6, 0x00F7, 0x011D, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x016D, 0x015D, 0x02D9 }; struct x_to_unicode u_8859_4 = { 96, 32, X2U_ISO|X2U_STD, AL_ROMAN, "ISO Latin-4", "latin4", 110, "D", 0x00A0, 0x0104, 0x0138, 0x0156, 0x00A4, 0x0128, 0x013B, 0x00A7, 0x00A8, 0x0160, 0x0112, 0x0122, 0x0166, 0x00AD, 0x017D, 0x00AF, 0x00B0, 0x0105, 0x02DB, 0x0157, 0x00B4, 0x0129, 0x013C, 0x02C7, 0x00B8, 0x0161, 0x0113, 0x0123, 0x0167, 0x014A, 0x017E, 0x014B, 0x0100, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x012E, 0x010C, 0x00C9, 0x0118, 0x00CB, 0x0116, 0x00CD, 0x00CE, 0x012A, 0x0110, 0x0145, 0x014C, 0x0136, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00D8, 0x0172, 0x00DA, 0x00DB, 0x00DC, 0x0168, 0x016A, 0x00DF, 0x0101, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x012F, 0x010D, 0x00E9, 0x0119, 0x00EB, 0x0117, 0x00ED, 0x00EE, 0x012B, 0x0111, 0x0146, 0x014D, 0x0137, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x0173, 0x00FA, 0x00FB, 0x00FC, 0x0169, 0x016B, 0x02D9 }; struct x_to_unicode u_8859_5 = { 96,32,X2U_ISO|X2U_STD,AL_CYRIL,"ISO Latin/Cyrillic","cyrillic-iso",144,"L", 0x00A0, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407, 0x0408, 0x0409, 0x040A, 0x040B, 0x040C, 0x00AD, 0x040E, 0x040F, 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F, 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F, 0x2116, 0x0451, 0x0452, 0x0453, 0x0454, 0x0455, 0x0456, 0x0457, 0x0458, 0x0459, 0x045A, 0x045B, 0x045C, 0x00A7, 0x045E, 0x045F }; struct x_to_unicode u_8859_6 = { 96, 32, X2U_ISO|X2U_STD,AL_ARABIC,"ISO Latin/Arabic","arabic-iso",127,"G", 0x00A0, 0xfffd, 0xfffd, 0xfffd, 0x00A4, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x060C, 0x00AD, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x061B, 0xfffd, 0xfffd, 0xfffd, 0x061F, 0xfffd, 0x0621, 0x0622, 0x0623, 0x0624, 0x0625, 0x0626, 0x0627, 0x0628, 0x0629, 0x062A, 0x062B, 0x062C, 0x062D, 0x062E, 0x062F, 0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x0636, 0x0637, 0x0638, 0x0639, 0x063A, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x0640, 0x0641, 0x0642, 0x0643, 0x0644, 0x0645, 0x0646, 0x0647, 0x0648, 0x0649, 0x064A, 0x064B, 0x064C, 0x064D, 0x064E, 0x064F, 0x0650, 0x0651, 0x0652, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd }; struct x_to_unicode u_8859_7 = { 96, 32, X2U_ISO|X2U_STD,AL_GREEK,"ISO Latin/Greek", "greek-iso", 126, "F", 0x00A0, 0x2018, 0x2019, 0x00A3, 0xFFFD, 0xFFFD, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0xFFFD, 0x00AB, 0x00AC, 0x00AD, 0xFFFD, 0x2015, 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x0384, 0x0385, 0x0386, 0x00B7, 0x0388, 0x0389, 0x038A, 0x00BB, 0x038C, 0x00BD, 0x038E, 0x038F, 0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0, 0x03A1, 0xfffd, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0x03AA, 0x03AB, 0x03AC, 0x03AD, 0x03AE, 0x03AF, 0x03B0, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9, 0x03CA, 0x03CB, 0x03CC, 0x03CD, 0x03CE, 0xfffd }; struct x_to_unicode u_8859_8 = { 96, 32, X2U_ISO|X2U_STD,AL_HEBREW,"ISO Latin/Hebrew","hebrew-iso",121,"H", 0x00A0, 0xfffd, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00D7, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x203E, 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00F7, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x2017, 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, 0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF, 0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7, 0x05E8, 0x05E9, 0x05EA, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd }; struct x_to_unicode u_8859_9 = { 96, 32, X2U_ISO|X2U_STD, AL_ROMAN, "ISO Latin-5", "latin5", 148, "M", 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, 0x011E, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x0130, 0x015E, 0x00DF, 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, 0x011F, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x0131, 0x015F, 0x00FF }; struct x_to_unicode u_8859_10 = { 96, 32, X2U_ISO|X2U_STD, AL_ROMAN, "ISO Latin-6", "latin6", 157, "V", 0x00a0, 0x0104, 0x0112, 0x0122, 0x012a, 0x0128, 0x0136, 0x013b, 0x0143, 0x0156, 0x0160, 0x0166, 0x017d, 0x00ad, 0x0138, 0x014a, 0x0111, 0x0105, 0x0113, 0x0123, 0x012b, 0x0129, 0x0137, 0x013c, 0x0144, 0x0157, 0x0161, 0x0167, 0x017e, 0x00bd, 0x00be, 0x014b, 0x0100, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x012e, 0x010c, 0x00c9, 0x0118, 0x00cb, 0x0116, 0x00cd, 0x00ce, 0x00cf, 0x0110, 0x0145, 0x014c, 0x00de, 0x00d4, 0x00d5, 0x00d6, 0x0168, 0x00d8, 0x0172, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x016a, 0x0101, 0x00e1, 0x00e2, 0x00d3, 0x00e4, 0x00e5, 0x00e6, 0x012f, 0x010d, 0x00e9, 0x0119, 0x00eb, 0x0117, 0x00ed, 0x00ee, 0x00ef, 0x00f0, 0x00f1, 0x014d, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x0169, 0x00f8, 0x0173, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x016b }; /* Latin-9 (ISO 8859-15) is the same as Latin-1 with the following changes: * A4 is Euro Symbol 20AC * A6 is Capital S Caron 0160 * A8 is Small s caron 0161 * B4 is Capital Z caron 017D * B8 is Small z caron 017E * BC is Capital OE ligature 0152 * BD is Small oe ligature 0153 * BE is Capital Y diaeresis 0178 */ struct x_to_unicode u_8859_15 = { 96, 32, X2U_ISO|X2U_STD, AL_ROMAN, "ISO Latin-9", "latin9", 0, NULL, 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x20AC, 0x00A5, 0x0160, 0x00A7, 0x0161, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x017D, 0x00B5, 0x00B6, 0x00B7, 0x017E, 0x00B9, 0x00BA, 0x00BB, 0x0152, 0x0153, 0x0178, 0x00BF, 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF }; /* Dyadic Systems Dyalog/X APL, corresponds to APLTERMI.TTF. */ /* Unicode mappings according to ISO-IEC / JTC 1 / SC 22 N 3067, 1999-12-28. */ struct x_to_unicode u_apl2 = { /* Dyadic Systems APL + box drawings */ 96, 32, X2U_STD, AL_ROMAN, "Dyadic Systems APL", "apl-dyadic", 0, NULL, 0x00a0, 0x00d7, 0x2502, 0x2524, 0x00a2, 0x2510, 0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x2518, 0x250c, 0x2206, 0x00f7, 0x2260, 0x22c4, 0x2375, 0x2374, 0x237a, 0x220a, 0x2261, 0x2265, 0x2264, 0x22a5, 0x22a4, 0x2190, 0x2218, 0x235d, 0x233f, 0x2340, 0x234e, 0x2355, 0x234b, 0x2352, 0x2372, 0x2371, 0x2368, 0x235f, 0x25af, 0x235e, 0x2339, 0x236b, 0x236a, 0x2262, 0x230a, 0x2308, 0x2349, 0x2229, 0x222a, 0x236c, 0x00a3, 0x233d, 0x2296, 0x22a2, 0x22a3, 0x2337, 0x00af, 0x2373, 0x00a8, 0x25cb, 0x2192, 0x2228, 0x2282, 0x2283, 0x2359, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x2207, 0x2191, 0x2193 }; struct x_to_unicode u_apl3 = { /* APL-Plus = APL-2000 */ 128, 0, X2U_CXG, AL_ROMAN, "APL-2000", "apl-2000", 0, NULL, 0x20ac, 0x22a3, 0x22a4, 0x22a5, 0x2190, 0x2192, 0x2191, 0x2193, 0x2264, 0x2265, 0x2372, 0x2371, 0x25af, 0x235e, 0x2339, 0x2359, 0x236b, 0x2206, 0x2207, 0x234b, 0x2352, 0x2355, 0x234e, 0x2308, 0x230a, 0x2340, 0x233f, 0x2282, 0x2283, 0x2229, 0x222a, 0x2228, 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x22a2, 0x00a5, 0x00a6, 0x00a7, 0x00a8, 0x235d, 0x22c4, 0x00ab, 0x2260, 0x2261, 0x236a, 0x00af, 0x2218, 0x25cb, 0x233d, 0x2349, 0x2296, 0x235f, 0x00b6, 0x00b7, 0x237a, 0x220a, 0x2377, 0x00bb, 0x2373, 0x2374, 0x2375, 0x00bf, 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf, 0x236c, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7, 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x2337, 0x00df, 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, 0x2364, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, 0x2205, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x2368, 0x00ff }; struct x_to_unicode u_apl4 = { /* IBM APL2 */ 128, 0, X2U_CXG, AL_ROMAN, "IBM APL2", "apl2-ibm", 0, NULL, 0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7, 0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5, 0x25af, 0x235e, 0x2339, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9, 0x22a4, 0x00d6, 0x00dc, 0x00f8, 0x00a3, 0x22a5, 0x2190, 0x2336, 0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00ba, 0x00bf, 0x2308, 0x00ac, 0x2192, 0x222a, 0x00a1, 0x2355, 0x234e, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x235f, 0x2206, 0x2207, 0x2192, 0x2563, 0x2551, 0x2557, 0x255d, 0x2190, 0x230a, 0x2510, 0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x2191, 0x2193, 0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2261, 0x2378, 0x2377, 0x2235, 0x2337, 0x2342, 0x233b, 0x22a2, 0x22a3, 0x22c4, 0x2518, 0x250c, 0x2588, 0x2584, 0x00a6, 0x00cc, 0x2580, 0x237a, 0x00df, 0x2282, 0x2283, 0x235d, 0x2372, 0x2374, 0x2371, 0x233d, 0x2296, 0x25cb, 0x2228, 0x2373, 0x2349, 0x00c5, 0x2229, 0x233f, 0x2340, 0x2265, 0x2264, 0x2260, 0x00d7, 0x00f7, 0x2359, 0x2218, 0x2375, 0x236b, 0x234b, 0x2352, 0x00af, 0x00a8, 0x00a0 }; struct x_to_unicode u_apl5 = { /* APL-2741 */ 128, 0, X2U_CXG, AL_ROMAN, "APL-2741", "apl-2741", 0, NULL, 0x20ac, 0x22a3, 0x22a4, 0x22a5, 0x2190, 0x2192, 0x2191, 0x2193, 0x2264, 0x2265, 0x2372, 0x2371, 0x25af, 0x235e, 0x2339, 0x2359, 0x236b, 0x2206, 0x2207, 0x234b, 0x2352, 0x2355, 0x234e, 0x2308, 0x230a, 0x2340, 0x233f, 0x2282, 0x2283, 0x2229, 0x222a, 0x2228, 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x22a2, 0x2378, 0x2261, 0x2336, 0x00a8, 0x235d, 0x22c4, 0x236c, 0x2260, 0x2261, 0x236a, 0x00af, 0x2218, 0x25cb, 0x233d, 0x2349, 0x2296, 0x235f, 0x00b6, 0x00b7, 0x237a, 0x220a, 0x2377, 0x2262, 0x2373, 0x2374, 0x2375, 0x00bf, 0x2514, 0x2534, 0x252c, 0x251c, 0x00c4, 0x00c5, 0x00c6, 0x00c7, 0x00c8, 0x00c9, 0x2342, 0x233b, 0x2510, 0x00cd, 0x2500, 0x253c, 0x236c, 0x00d1, 0x2350, 0x2357, 0x2347, 0x2348, 0x00d6, 0x00d7, 0x00d8, 0x2518, 0x250c, 0x2502, 0x00dc, 0x2524, 0x2337, 0x00df, 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, 0x2364, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, 0x2205, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x2235, 0x2368, 0x2365 }; /* 8-bit GOST standard sets */ struct x_to_unicode u_koi8 = { 96, 32, X2U_STD, AL_CYRIL, "KOI-8", "koi8", 0, NULL, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x044E, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433, 0x0445, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, 0x044F, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432, 0x044C, 0x044B, 0x0437, 0x0448, 0x044D, 0x0449, 0x0447, 0x044A, 0x042E, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413, 0x0425, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F, 0x042F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412, 0x042C, 0x042B, 0x0417, 0x0428, 0x042D, 0x0429, 0x0427, 0xfffd }; /* Other KOI-8 based sets */ struct x_to_unicode u_koi8r = { /* (Russia) Table from RFC1489 */ 128, 0, X2U_CP, AL_CYRIL, "KOI8-R", "koi8r", 0, NULL, 0x2500, 0x2502, 0x250C, 0x2510, 0x2514, 0x2518, 0x251C, 0x2524, 0x252C, 0x2534, 0x253C, 0x2580, 0x2584, 0x2588, 0x258C, 0x2590, 0x2591, 0x2592, 0x2593, 0x2320, 0x25A0, 0x2219, 0x221A, 0x2248, 0x2264, 0x2265, 0x00A0, 0x2321, 0x00B0, 0x00B2, 0x00B7, 0x00F7, 0x2550, 0x2551, 0x2552, 0x0451, 0x2553, 0x2554, 0x2555, 0x2556, 0x2557, 0x2558, 0x2559, 0x255A, 0x255B, 0x255C, 0x255D, 0x255E, 0x255F, 0x2560, 0x2561, 0x0401, 0x2562, 0x2563, 0x2564, 0x2565, 0x2566, 0x2567, 0x2568, 0x2569, 0x256A, 0x256B, 0x256C, 0x00A9, 0x044E, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433, 0x0445, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, 0x044F, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432, 0x044C, 0x044B, 0x0437, 0x0448, 0x044D, 0x0449, 0x0447, 0x044A, 0x042E, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413, 0x0425, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F, 0x042F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412, 0x042C, 0x042B, 0x0417, 0x0428, 0x042D, 0x0429, 0x0427, 0x042A }; struct x_to_unicode u_koi8u = { /* (Ukraine) From RFC2319 */ 128, 0, X2U_CP, AL_CYRIL, "KOI8-U", "koi8u", 0, NULL, 0x2500, 0x2502, 0x250C, 0x2510, 0x2514, 0x2518, 0x251C, 0x2524, 0x252C, 0x2534, 0x253C, 0x2580, 0x2584, 0x2588, 0x258C, 0x2590, 0x2591, 0x2592, 0x2593, 0x2320, 0x25A0, 0x2219, 0x221A, 0x2248, 0x2264, 0x2265, 0x00A0, 0x2321, 0x00B0, 0x00B2, 0x00B7, 0x00F7, 0x2550, 0x2551, 0x2552, 0x0451, 0x0454, 0x2554, 0x0456, 0x0457, 0x2557, 0x2558, 0x2559, 0x255A, 0x255B, 0x0491, 0x255D, 0x255E, 0x255F, 0x2560, 0x2561, 0x0401, 0x0404, 0x2563, 0x0406, 0x0407, 0x2566, 0x2567, 0x2568, 0x2569, 0x256A, 0x0490, 0x256C, 0x00A9, 0x044E, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433, 0x0445, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, 0x044F, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432, 0x044C, 0x044B, 0x0437, 0x0448, 0x044D, 0x0449, 0x0447, 0x044A, 0x042E, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413, 0x0425, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F, 0x042F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412, 0x042C, 0x042B, 0x0417, 0x0428, 0x042D, 0x0429, 0x0427, 0x042A }; /* PC Code Pages */ struct x_to_unicode u_cp437 = { 128, 0, X2U_CP, AL_ROMAN,"PC Code Page 437","cp437", 0, NULL, 0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7, 0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5, 0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9, 0x00ff, 0x00d6, 0x00dc, 0x00a2, 0x00a3, 0x00a5, 0x20a7, 0x0192, 0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00ba, 0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510, 0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f, 0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567, 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256b, 0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258c, 0x2590, 0x2580, 0x03b1, 0x00df, 0x0393, 0x03c0, 0x03a3, 0x03c3, 0x00b5, 0x03c4, 0x03a6, 0x0398, 0x03a9, 0x03b4, 0x221e, 0x03c6, 0x03b5, 0x2229, 0x2261, 0x00b1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00f7, 0x2248, 0x00b0, 0x2219, 0x00b7, 0x221a, 0x207f, 0x00b2, /* 0x25ae */ 0x25a0, 0x00a0 }; struct x_to_unicode u_mazovia = { 128, 0, X2U_CP, AL_ROMAN,"Polish Mazovia PC Code Page","mazovia", 0, NULL, 0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x0105, 0x00e7, /* 80 */ 0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x0107, 0x00c4, 0x0104, /* 88 */ 0x0118, 0x0119, 0x0142, 0x00f4, 0x00f6, 0x0106, 0x00fb, 0x00f9, /* 90 */ 0x015a, 0x00d6, 0x00dc, 0x00a2, 0x0141, 0x00a5, 0x015b, 0x0192, /* 98 */ 0x0179, 0x017b, 0x00f3, 0x00d3, 0x0144, 0x0143, 0x017a, 0x017c, /* a0 */ 0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb, /* a8 */ 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, /* b0 */ 0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510, /* b8 */ 0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f, /* c0 */ 0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567, /* c8 */ 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256b, /* d0 */ 0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258c, 0x2590, 0x2580, /* d8 */ 0x03b1, 0x00df, 0x0393, 0x03c0, 0x03a3, 0x03c3, 0x00b5, 0x03c4, /* e0 */ 0x03a6, 0x0398, 0x03a9, 0x03b4, 0x221e, 0x03c6, 0x03b5, 0x2229, /* e8 */ 0x2261, 0x00b1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00f7, 0x2248, /* f0 */ 0x00b0, 0x2219, 0x00b7, 0x221a, 0x207f, 0x00b2, 0x25a0, 0x00a0 /* f8 */ }; struct x_to_unicode u_cp850 = { 128, 0, X2U_CP, AL_ROMAN,"PC Code Page 850","cp850", 0, NULL, 0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7, 0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5, 0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9, 0x00ff, 0x00d6, 0x00dc, 0x00f8, 0x00a3, 0x00d8, 0x00d7, 0x0192, 0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00ba, 0x00bf, 0x00ae, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00c1, 0x00c2, 0x00c0, 0x00a9, 0x2563, 0x2551, 0x2557, 0x255d, 0x00a2, 0x00a5, 0x2510, 0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x00e3, 0x00c3, 0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x00a4, 0x00f0, 0x00d0, 0x00ca, 0x00cb, 0x00c8, 0x0131, 0x00cd, 0x00ce, 0x00cf, 0x2518, 0x250c, 0x2588, 0x2584, 0x00a6, 0x00cc, 0x2580, 0x00d3, 0x00df, 0x00d4, 0x00d2, 0x00f5, 0x00d5, 0x00b5, 0x00fe, 0x00de, 0x00da, 0x00db, 0x00d9, 0x00fd, 0x00dd, 0x00af, 0x00b4, 0x00ad, 0x00b1, 0x2017, 0x00be, 0x00b6, 0x00a7, 0x00f7, 0x00b8, 0x00b0, 0x00a8, 0x00b7, 0x00b9, 0x00b3, 0x00b2, 0x25a0, 0x00a0 }; struct x_to_unicode u_cp852 = { 128, 0, X2U_CP, AL_ROMAN,"PC Code Page 852","cp852", 0, NULL, 0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x016f, 0x0107, 0x00e7, 0x0142, 0x00eb, 0x0150, 0x0151, 0x00ee, 0x0179, 0x00c4, 0x0106, 0x00c9, 0x0139, 0x013a, 0x00f4, 0x00f6, 0x013d, 0x013e, 0x015a, 0x015b, 0x00d6, 0x00dc, 0x0164, 0x0165, 0x0141, 0x00d7, 0x010d, 0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x0104, 0x0105, 0x017d, 0x017e, 0x0118, 0x0119, 0x00ac, 0x017a, 0x010c, 0x015f, 0x00ab, 0x00bb, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00c1, 0x00c2, 0x011a, 0x015e, 0x2563, 0x2551, 0x2557, 0x255d, 0x017b, 0x017c, 0x2510, 0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x0102, 0x0103, 0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x00a4, 0x0111, 0x0110, 0x010e, 0x00cb, 0x010f, 0x0147, 0x00cd, 0x00ce, 0x011b, 0x2518, 0x250c, 0x2588, 0x2584, 0x0162, 0x016e, 0x2580, 0x00d3, 0x00df, 0x00d4, 0x0143, 0x0144, 0x0148, 0x0160, 0x0161, 0x0154, 0x00da, 0x0155, 0x0170, 0x00fd, 0x00dd, 0x0163, 0x00b4, 0x00ad, 0x02dd, 0x02db, 0x02c7, 0x02d8, 0x00a7, 0x00f7, 0x00b8, 0x00b0, 0x00a8, 0x02d9, 0x0171, 0x0158, 0x0159, 0x25a0, 0x00a0 }; struct x_to_unicode u_cp855 = { /* CP855 Cyrillic to Unicode */ 128, 0, X2U_CP, AL_CYRIL,"PC Code Page 855","cp855", 0, NULL, 0x0452, 0x0402, 0x0453, 0x0403, 0x0451, 0x0401, 0x0454, 0x0404, 0x0455, 0x0405, 0x0456, 0x0406, 0x0457, 0x0407, 0x0458, 0x0408, 0x0459, 0x0409, 0x045a, 0x040a, 0x045b, 0x040b, 0x045c, 0x040c, 0x045e, 0x040e, 0x045f, 0x040f, 0x044e, 0x042e, 0x044a, 0x042a, 0x0430, 0x0410, 0x0431, 0x0411, 0x0446, 0x0426, 0x0434, 0x0414, 0x0435, 0x0415, 0x0444, 0x0424, 0x0433, 0x0413, 0x00ab, 0x00bb, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x0445, 0x0425, 0x0438, 0x0418, 0x2563, 0x2551, 0x2557, 0x255d, 0x0439, 0x0419, 0x2510, 0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x043a, 0x041a, 0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x00a4, 0x043b, 0x041b, 0x043c, 0x041c, 0x043d, 0x041d, 0x043e, 0x041e, 0x043f, 0x2518, 0x250c, 0x2588, 0x2584, 0x041f, 0x044f, 0x2580, 0x042f, 0x0440, 0x0420, 0x0441, 0x0421, 0x0442, 0x0422, 0x0443, 0x0423, 0x0436, 0x0416, 0x0432, 0x0412, 0x044c, 0x042c, 0x2116, 0x002d, 0x044b, 0x042b, 0x0437, 0x0417, 0x0448, 0x0428, 0x044d, 0x042d, 0x0449, 0x0429, 0x0447, 0x0427, 0x00a7, 0x25a0, 0x0020 }; struct x_to_unicode u_cp856 = { /* CP856 (Bulgaria) to Unicode */ 128, 0, X2U_CP, AL_CYRIL,"PC Code Page 856","cp856", 0, NULL, 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, 0x041f, 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042a, 0x042b, 0x042c, 0x042d, 0x042e, 0x042f, 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e, 0x043f, 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044a, 0x044b, 0x044c, 0x044d, 0x044e, 0x044f, 0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x2563, 0x2551, 0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2510, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2116, 0x00a7, 0x2557, 0x255d, 0x2518, 0x250c, 0x2588, 0x2584, 0x258c, 0x2590, 0x2580, 0x03b1, 0x00df, 0x0393, 0x03c0, 0x03a3, 0x03c3, 0x00b5, 0x03c4, 0x03a6, 0x0398, 0x03a9, 0x03b4, 0x221e, 0x03c6, 0x03b5, 0x2229, 0x2261, 0x00b1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00f7, 0x2248, 0x00b0, 0x2219, 0x00b7, 0x221a, 0x207f, 0x00b2, 0x25a0, 0x00a0 }; struct x_to_unicode u_cp857 = { 128, 0, X2U_CP, AL_ROMAN,"PC Code Page 857","cp857", 0, NULL, 0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7, /* 0x80 */ 0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x0131, 0x00c4, 0x00c5, /* 0x88 */ 0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9, /* 0x90 */ 0x0130, 0x00d6, 0x00dc, 0x00f8, 0x00a3, 0x00d8, 0x015e, 0x015f, /* 0x98 */ 0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x011e, 0x011f, /* 0xa0 */ 0x00bf, 0x00ae, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb, /* 0xa8 */ 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00c1, 0x00c2, 0x00c0, /* 0xb0 */ 0x00a9, 0x2563, 0x2551, 0x2557, 0x255d, 0x00a2, 0x00a5, 0x2510, /* 0xb8 */ 0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x00e3, 0x00c3, /* 0xc0 */ 0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x00a4, /* 0xc8 */ 0x00ba, 0x00aa, 0x00ca, 0x00cb, 0x00c8, 0x20ac, 0x00cd, 0x00ce, /* 0xd0 */ 0x00cf, 0x2518, 0x250c, 0x2588, 0x2584, 0x00a6, 0x00cc, 0x2580, /* 0xd8 */ 0x00d3, 0x00df, 0x00d4, 0x00d2, 0x00f5, 0x00d5, 0x00b5, 0xfffd, /* 0xe0 */ 0x00d7, 0x00da, 0x00db, 0x00d9, 0x00ec, 0x00ff, 0x00af, 0x00b4, /* 0xe8 */ 0x00ad, 0x00b1, 0xfffd, 0x00be, 0x00b6, 0x00a7, 0x00f7, 0x00b8, /* 0xf0 */ 0x00b0, 0x00a8, 0x00b7, 0x00b9, 0x00b3, 0x00b2, 0x25a0, 0x00a0 /* 0xf8 */ }; struct x_to_unicode u_cp858 = { 128, 0, X2U_CP, AL_ROMAN,"PC Code Page 858","cp858", 0, NULL, 0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7, 0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5, 0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9, 0x00ff, 0x00d6, 0x00dc, 0x00f8, 0x00a3, 0x00d8, 0x00d7, 0x0192, 0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00ba, 0x00bf, 0x00ae, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00c1, 0x00c2, 0x00c0, 0x00a9, 0x2563, 0x2551, 0x2557, 0x255d, 0x00a2, 0x00a5, 0x2510, 0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x00e3, 0x00c3, 0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x00a4, 0x00f0, 0x00d0, 0x00ca, 0x00cb, 0x00c8, 0x20ac, 0x00cd, 0x00ce, 0x00cf, 0x2518, 0x250c, 0x2588, 0x2584, 0x00a6, 0x00cc, 0x2580, 0x00d3, 0x00df, 0x00d4, 0x00d2, 0x00f5, 0x00d5, 0x00b5, 0x00fe, 0x00de, 0x00da, 0x00db, 0x00d9, 0x00fd, 0x00dd, 0x00af, 0x00b4, 0x00ad, 0x00b1, 0x2017, 0x00be, 0x00b6, 0x00a7, 0x00f7, 0x00b8, 0x00b0, 0x00a8, 0x00b7, 0x00b9, 0x00b3, 0x00b2, 0x25a0, 0x00a0 }; struct x_to_unicode u_cp862 = { 128, 0, X2U_CP, AL_HEBREW,"PC Code Page 862","cp862", 0, NULL, 0x05d0, 0x05d1, 0x05d2, 0x05d3, 0x05d4, 0x05d5, 0x05d6, 0x05d7, 0x05d8, 0x05d9, 0x05da, 0x05db, 0x05dc, 0x05dd, 0x05de, 0x05df, 0x05e0, 0x05e1, 0x05e2, 0x05e3, 0x05e4, 0x05e5, 0x05e6, 0x05e7, 0x05e8, 0x05e9, 0x05ea, 0x00a2, 0x00a3, 0x00a5, 0x20a7, 0x0192, 0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00ba, 0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510, 0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f, 0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567, 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256b, 0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258c, 0x2590, 0x2580, 0x03b1, 0x00df, 0x0393, 0x03c0, 0x03a3, 0x03c3, 0x00b5, 0x03c4, 0x03a6, 0x0398, 0x03a9, 0x03b4, 0x221e, 0x03c6, 0x03b5, 0x2229, 0x2261, 0x00b1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00f7, 0x2248, 0x00b0, 0x2219, 0x00b7, 0x221a, 0x207f, 0x00b2, 0x25a0, 0x00a0 }; struct x_to_unicode u_cp864 = { 128, 0, X2U_CP, AL_ARABIC,"PC Code Page 864","cp864", 0, NULL, 0x00b0, 0x00b7, 0x2219, 0x221a, 0x2592, 0x2500, 0x2502, 0x253c, 0x2524, 0x252c, 0x251c, 0x2534, 0x2510, 0x250c, 0x2514, 0x2518, 0x03b2, 0x221e, 0x03c6, 0x00b1, 0x00bd, 0x00bc, 0x2248, 0x00ab, 0x00bb, 0xfef7, 0xfef8, 0xfffd, 0xfffd, 0xfefb, 0xfefc, 0xfffd, 0x00a0, 0x00ad, 0xfe82, 0x00a3, 0x00a4, 0xfe84, 0xfffd, 0xfffd, 0xfe8e, 0xfe8f, 0xfe95, 0xfe99, 0x060c, 0xfe9d, 0xfea1, 0xfea5, 0x0660, 0x0661, 0x0662, 0x0663, 0x0664, 0x0665, 0x0666, 0x0667, 0x0668, 0x0669, 0xfed1, 0x061b, 0xfeb1, 0xfeb5, 0xfeb9, 0x061f, 0x00a2, 0xfe80, 0xfe81, 0xfe83, 0xfe85, 0xfeca, 0xfe8b, 0xfe8d, 0xfe91, 0xfe93, 0xfe97, 0xfe9b, 0xfe9f, 0xfea3, 0xfea7, 0xfea9, 0xfeab, 0xfead, 0xfeaf, 0xfeb3, 0xfeb7, 0xfebb, 0xfebf, 0xfec1, 0xfec5, 0xfecb, 0xfecf, 0x00a6, 0x00ac, 0x00f7, 0x00d7, 0xfec9, 0x0640, 0xfed3, 0xfed7, 0xfedb, 0xfedf, 0xfee3, 0xfee7, 0xfeeb, 0xfeed, 0xfeef, 0xfef3, 0xfebd, 0xfecc, 0xfece, 0xfecd, 0xfee1, 0xfe7d, 0x0651, 0xfee5, 0xfee9, 0xfeec, 0xfef0, 0xfef2, 0xfed0, 0xfed5, 0xfef5, 0xfef6, 0xfedd, 0xfed9, 0xfef1, 0x25a0, 0xfffd }; struct x_to_unicode u_cp866 = { 128, 0, X2U_CP, AL_CYRIL,"PC Code Page 866","cp866", 0, NULL, 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, 0x041f, 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042a, 0x042b, 0x042c, 0x042d, 0x042e, 0x042f, 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e, 0x043f, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510, 0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f, 0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567, 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256b, 0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258c, 0x2590, 0x2580, 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044a, 0x044b, 0x044c, 0x044d, 0x044e, 0x044f, 0x0401, 0x0451, 0x0404, 0x0454, 0x0407, 0x0457, 0x040e, 0x045e, 0x00b0, 0x2219, 0x00b7, 0x221a, 0x2116, 0x00a4, 0x25a0, 0x00a0 }; struct x_to_unicode u_cp869 = { 128, 0, X2U_CP, AL_GREEK,"PC Code Page 869","cp869", 0, NULL, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x0386, 0xfffd, 0x00b7, 0x00ac, 0x00a6, 0x2018, 0x2019, 0x0388, 0x2015, 0x0389, 0x038a, 0x03aa, 0x038c, 0xfffd, 0xfffd, 0x038e, 0x03ab, 0x00a9, 0x038f, 0x00b2, 0x00b3, 0x03ac, 0x00a3, 0x03ad, 0x03ae, 0x03af, 0x03ca, 0x0390, 0x03cc, 0x03cd, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x00bd, 0x0398, 0x0399, 0x00ab, 0x00bb, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x039a, 0x039b, 0x039c, 0x039d, 0x2563, 0x2551, 0x2557, 0x255d, 0x039e, 0x039f, 0x2510, 0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x03a0, 0x03a1, 0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x03a3, 0x03a4, 0x03a5, 0x03a6, 0x03a7, 0x03a8, 0x03a9, 0x03b1, 0x03b2, 0x03b3, 0x2518, 0x250c, 0x2588, 0x2584, 0x03b4, 0x03b5, 0x2580, 0x03b6, 0x03b7, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf, 0x03c0, 0x03c1, 0x03c3, 0x03c2, 0x03c4, 0x0384, 0x00ad, 0x00b1, 0x03c5, 0x03c6, 0x03c7, 0x00a7, 0x03c8, 0x0385, 0x00b0, 0x00a8, 0x03c9, 0x03cb, 0x03b0, 0x03ce, 0x25a0, 0x00a0 }; /* Windows code pages */ struct x_to_unicode u_cp1250 = { /* Windows Latin-2 */ 128, 0, X2U_CP, AL_ROMAN,"Windows Code Page 1250","cp1250", 0, NULL, 0x20ac, 0xfffd, 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021, /* 80 */ 0x005e, 0x2031, 0x0160, 0x003c, 0x015a, 0x0164, 0x017d, 0x0179, /* 88 */ 0xfffd, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2012, 0x2014, /* 90 */ 0xfffd, 0x2122, 0x0161, 0x003e, 0x015b, 0x0165, 0x017e, 0x017a, /* 98 */ 0x00A0, 0x02c7, 0x02d8, 0x0141, 0x00A4, 0x0104, 0x00A6, 0x00A7, /* A0 */ 0x00A8, 0x00a9, 0x015E, 0x00ab, 0x00ac, 0x002D, 0x00ae, 0x017B, /* A8 */ 0x00B0, 0x00b1, 0x02DB, 0x0142, 0x00B4, 0x00b5, 0x00b6, 0x00b7, /* B0 */ 0x00B8, 0x0105, 0x015F, 0x00bb, 0x013d, 0x02DD, 0x013E, 0x017C, /* B8 */ 0x0154, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x0139, 0x0106, 0x00C7, /* C0 */ 0x010C, 0x00C9, 0x0118, 0x00CB, 0x011A, 0x00CD, 0x00CE, 0x010E, 0x0110, 0x0143, 0x0147, 0x00D3, 0x00D4, 0x0150, 0x00D6, 0x00D7, 0x0158, 0x016E, 0x00DA, 0x0170, 0x00DC, 0x00DD, 0x0162, 0x00DF, 0x0155, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x013A, 0x0107, 0x00E7, 0x010D, 0x00E9, 0x0119, 0x00EB, 0x011B, 0x00ED, 0x00EE, 0x010F, 0x0111, 0x0144, 0x0148, 0x00F3, 0x00F4, 0x0151, 0x00F6, 0x00F7, 0x0159, 0x016F, 0x00FA, 0x0171, 0x00FC, 0x00FD, 0x0163, 0x02D9 }; struct x_to_unicode u_cp1251 = { /* Windows Cyrillic */ 128, 0, X2U_CP, AL_CYRIL,"Windows Code Page 1251","cp1251", 0, NULL, 0x0402, 0x0403, 0x201a, 0x0453, 0x201e, 0x2026, 0x2020, 0x2021, /* 80 */ 0x20ac, 0x2031, 0x0409, 0x003c, 0x040a, 0x040c, 0x040b, 0x040f, /* 88 */ 0x0452, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2012, 0x2014, /* 90 */ 0x007e, 0x2122, 0x0459, 0x003e, 0x045a, 0x045c, 0x045b, 0x045f, /* 98 */ 0x00A0, 0x040e, 0x045e, 0x0408, 0x00A4, 0x0490, 0x00A6, 0x00A7, /* a0 */ 0x0401, 0x00A9, 0x0404, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x0407, /* a8 */ 0x00b0, 0x00b1, 0x0406, 0x0456, 0x0491, 0x00B5, 0x00B6, 0x00B7, /* b0 */ 0x0451, 0x2116, 0x0454, 0x00BB, 0x0458, 0x0405, 0x0455, 0x0457, /* b8 */ 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, /* c0 */ 0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, 0x041f, /* c8 */ 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, /* d0 */ 0x0428, 0x0429, 0x042a, 0x042b, 0x042c, 0x042d, 0x042e, 0x042f, /* d8 */ 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, /* e0 */ 0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e, 0x043f, /* e8 */ 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, /* f0 */ 0x0448, 0x0449, 0x044a, 0x044b, 0x044c, 0x044d, 0x044e, 0x044f /* f8 */ }; struct x_to_unicode u_cp1252 = { /* Windows Latin-1 */ /* The following code points added September 1998: 0x80: Euro 0x8E: Latin Capital Letter Z with Caron 0x9E: Latin Small Letter Z with Caron Announced by Murray Sargent to Unicode consortium, email, 3 September 1998. The code page was changed in June 1998. The change is reflected in Windows 98 and "recent service packs" for Window 95 and Windows NT 4.0. */ 128, 0, X2U_CP, AL_ROMAN,"Windows Code Page 1252","cp1252", 0, NULL, 0x20ac, 0xfffd, 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021, 0x005e, 0x2031, 0x0160, 0x003c, 0x0152, 0xfffd, 0x017D, 0xfffd, 0xfffd, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2012, 0x2014, 0x007e, 0x2122, 0x0161, 0x003e, 0x0153, 0xfffd, 0x017E, 0x0178, 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF }; struct x_to_unicode u_cp1253 = { /* Windows Greece */ 128, 0, X2U_CP, AL_GREEK,"Windows Code Page 1253","cp1253", 0, NULL, 0x20ac, 0xfffd, 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021, /* 80 */ 0xfffd, 0x2031, 0xfffd, 0x003c, 0xfffd, 0xfffd, 0xfffd, 0xfffd, /* 88 */ 0xfffd, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2012, 0x2014, /* 90 */ 0xfffd, 0x2122, 0xfffd, 0x003e, 0xfffd, 0xfffd, 0xfffd, 0xfffd, /* 98 */ 0x00A0, 0x00b7, 0x0386, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, /* a0 */ 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, /* a8 */ 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B7, 0x00B5, 0x00B6, 0x00B7, /* b0 */ 0x0388, 0x0389, 0x038A, 0x00BB, 0x038C, 0x00BD, 0x038E, 0x038F, /* b8 */ 0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, /* c0 */ 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, /* c8 */ 0x03a0, 0x03a1, 0xfffd, 0x03a3, 0x03a4, 0x03a5, 0x03a6, 0x03a7, /* d0 */ 0x03a8, 0x03a9, 0x03aA, 0x03aB, 0x03aC, 0x03aD, 0x03aE, 0x03aF, /* d8 */ 0x03b0, 0x03b1, 0x03b2, 0x03b3, 0x03b4, 0x03b5, 0x03b6, 0x03b7, /* e0 */ 0x03b8, 0x03b9, 0x03bA, 0x03bB, 0x03bC, 0x03bD, 0x03bE, 0x03bF, /* e8 */ 0x03c0, 0x03c1, 0x03c2, 0x03c3, 0x03c4, 0x03c5, 0x03c6, 0x03c7, /* f0 */ 0x03c8, 0x03c9, 0x03cA, 0x03cB, 0x03cC, 0x03cD, 0x03cE, 0xfffd /* f8 */ }; struct x_to_unicode u_cp1254 = { /* Windows Turkey */ 128, 0, X2U_CP, AL_ROMAN,"Windows Code Page 1254","cp1254", 0, NULL, 0x20ac, 0xfffd, 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021, /* 80 */ 0x005e, 0x2031, 0x0160, 0x003c, 0x0152, 0xfffd, 0xfffd, 0xfffd, /* 88 */ 0xfffd, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2012, 0x2014, /* 90 */ 0x007e, 0x2122, 0x0161, 0x003e, 0x0153, 0xfffd, 0xfffd, 0x0178, /* 98 */ 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, 0x011e, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x0130, 0x015e, 0x00DF, 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, 0x011f, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x0131, 0x015f, 0x00FF }; struct x_to_unicode u_cp1255 = { /* Windows Hebrew */ 128, 0, X2U_CP, AL_ROMAN,"Windows Code Page 1255 (Hebrew)","cp1255", 0, NULL, 0x20AC, 0xFFFD, 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021, 0x02c6, 0x2030, 0xfffd, 0x2039, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014, 0x02dc, 0x2122, 0xfffd, 0x203a, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x20aa, 0x00a5, 0x00a6, 0x00a7, 0x00a8, 0x00a9, 0x00d7, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af, 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7, 0x00b8, 0x00b9, 0x00f7, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf, 0x05b0, 0x05b1, 0x05b2, 0x05b3, 0x05b4, 0x05b5, 0x05b6, 0x05b7, 0x05b8, 0x05b9, 0xfffd, 0x05bb, 0x05bc, 0x05bd, 0x05be, 0x05bf, 0x05c0, 0x05c1, 0x05c2, 0x05c3, 0x05f0, 0x05f1, 0x05f2, 0x05f3, 0x05f4, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x05d0, 0x05d1, 0x05d2, 0x05d3, 0x05d4, 0x05d5, 0x05d6, 0x05d7, 0x05d8, 0x05d9, 0x05da, 0x05db, 0x05dc, 0x05dd, 0x05de, 0x05df, 0x05e0, 0x05e1, 0x05e2, 0x05e3, 0x05e4, 0x05e5, 0x05e6, 0x05e7, 0x05e8, 0x05e9, 0x05ea, 0xfffd, 0xfffd, 0x200e, 0x200f, 0xfffd }; struct x_to_unicode u_cp1256 = { /* Windows Arabic */ 128, 0, X2U_CP, AL_ROMAN,"Windows Code Page 1256 (Arabic)","cp1256", 0, NULL, 0x20ac, 0x067e, 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021, 0x02c6, 0x2030, 0xfffd, 0x2039, 0x0152, 0x0686, 0x0698, 0xfffd, /* 88 */ 0x06af, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014, /* 90 */ 0xfffd, 0x2122, 0xfffd, 0x003a, 0x0153, 0x200c, 0x200d, 0xfffd, /* 98 */ 0x00A0, 0x060c, 0x00A2, 0x00A3, 0x00A4, 0x00a5, 0x00A6, 0x00A7, /* a0 */ 0x00a8, 0x00A9, 0xfffd, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00af, /* a8 */ 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00b4, 0x00B5, 0x00B6, 0x00B7, /* b0 */ 0x00b8, 0x00B9, 0x061b, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x061f, /* b8 */ 0xfffd, 0x0621, 0x0622, 0x0623, 0x0624, 0x0625, 0x0626, 0x0627, /* c0 */ 0x0628, 0x0629, 0x062a, 0x062b, 0x062c, 0x062d, 0x062e, 0x062f, /* c8 */ 0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x0636, 0x00D7, /* d0 */ 0x0637, 0x0638, 0x0639, 0x063a, 0x0640, 0x0641, 0x0642, 0x0643, /* d8 */ 0x00e0, 0x0644, 0x00e2, 0x0645, 0x0646, 0x0647, 0x0648, 0x00e7, /* e0 */ 0x00e8, 0x00E9, 0x00ea, 0x00eb, 0x0649, 0x064a, 0x00ee, 0x00ef, /* e8 */ 0x064b, 0x064c, 0x064d, 0x064e, 0x00f4, 0x064f, 0x0650, 0x00F7, /* f0 */ 0x0651, 0x00f9, 0x0652, 0x00fb, 0x00fc, 0x200e, 0x200f, 0xfffd /* f8 */ }; struct x_to_unicode u_cp1257 = { /* Windows Latin-4 */ 128, 0, X2U_CP, AL_ROMAN,"Windows Code Page 1257","cp1257", 0, NULL, 0x20ac, 0xfffd, 0x201a, 0xfffd, 0x201e, 0x2026, 0x2020, 0x2021, /* 80 */ 0xfffd, 0x2031, 0xfffd, 0x003c, 0xfffd, 0xfffd, 0xfffd, 0xfffd, /* 88 */ 0xfffd, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2012, 0x2014, /* 90 */ 0xfffd, 0x2122, 0xfffd, 0x003e, 0xfffd, 0xfffd, 0xfffd, 0xfffd, /* 98 */ 0x00A0, 0xfffd, 0x00A2, 0x00A3, 0x00A4, 0xfffd, 0x00A6, 0x00A7, /* a0 */ 0x00d8, 0x00A9, 0x0156, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00c6, /* a8 */ 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0xfffd, 0x00B5, 0x00B6, 0x00B7, /* b0 */ 0x00f8, 0x00B9, 0x0157, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00e6, /* b8 */ 0x0104, 0x012e, 0x0100, 0x0106, 0x00C4, 0x00C5, 0x0118, 0x0112, /* c0 */ 0x010C, 0x00C9, 0x0179, 0x0116, 0x0122, 0x0136, 0x012a, 0x00b7, /* c8 */ 0x0160, 0x0143, 0x0145, 0x00d3, 0x014c, 0x00D5, 0x00D6, 0x00D7, /* d0 */ 0x0172, 0x0141, 0x015A, 0x016a, 0x00DC, 0x017b, 0x017d, 0x00DF, /* d8 */ 0x0105, 0x012f, 0x0101, 0x0107, 0x00E4, 0x00E5, 0x0119, 0x0113, /* e0 */ 0x010D, 0x00E9, 0x017a, 0x0117, 0x0123, 0x0137, 0x012b, 0x013c, /* e8 */ 0x0161, 0x0144, 0x0146, 0x00f3, 0x014d, 0x00F5, 0x00F6, 0x00F7, /* f0 */ 0x0173, 0x0142, 0x015b, 0x016b, 0x00fc, 0x017c, 0x017e, 0xfffd /* f8 */ }; struct x_to_unicode u_cp1258 = { /* Windows Viet Nam */ 128, 0, X2U_CP, AL_ROMAN,"Windows Code Page 1258 (Viet Nam)","cp1258", 0, NULL, 0x20ac, 0xfffd, 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021, /* 80 */ 0x02c6, 0x2030, 0xfffd, 0x2039, 0x0152, 0xfffd, 0xfffd, 0xfffd, /* 88 */ 0xfffd, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014, /* 90 */ 0x02dc, 0x2122, 0xfffd, 0x203a, 0x0153, 0xfffd, 0xfffd, 0x0178, /* 98 */ 0x00A0, 0x00a1, 0x00A2, 0x00A3, 0x00A4, 0x00a5, 0x00A6, 0x00A7, /* a0 */ 0x00a8, 0x00A9, 0x00aa, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00af, /* a8 */ 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00b4, 0x00B5, 0x00B6, 0x00B7, /* b0 */ 0x00b8, 0x00B9, 0x00ba, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00bf, /* b8 */ 0x00c0, 0x00c1, 0x00c2, 0x0102, 0x00C4, 0x00C5, 0x00c6, 0x00c7, /* c0 */ 0x00c8, 0x00C9, 0x00ca, 0x00cb, 0x0300, 0x00cd, 0x00ce, 0x00cf, /* c8 */ 0x0110, 0x00d1, 0x0309, 0x00d3, 0x00d4, 0x01a0, 0x00D6, 0x00D7, /* d0 */ 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00DC, 0x01af, 0x0303, 0x00DF, /* d8 */ 0x00e0, 0x00e1, 0x00e2, 0x0103, 0x00E4, 0x00E5, 0x00e6, 0x00d7, /* e0 */ 0x00e8, 0x00E9, 0x00ea, 0x00eb, 0x0301, 0x00ed, 0x00ee, 0x00ef, /* e8 */ 0x0111, 0x00f1, 0x0323, 0x00f3, 0x00f4, 0x01a1, 0x00F6, 0x00F7, /* f0 */ 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x01b0, 0x20ab, 0x00ff /* f8 */ }; struct x_to_unicode u_cp37 = { /* EBCDIC U.S. */ 256, 0, X2U_CP, AL_ROMAN,"Code Page 037 EBCDIC (U.S.)","cp037", 0, NULL, 0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F, 0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, 0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087, 0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F, 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007, 0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004, 0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A, 0x0020, 0x00A0, 0x00E2, 0x00E4, 0x00E0, 0x00E1, 0x00E3, 0x00E5, 0x00E7, 0x00F1, 0x00A2, 0x002E, 0x003C, 0x0028, 0x002B, 0x007C, 0x0026, 0x00E9, 0x00EA, 0x00EB, 0x00E8, 0x00ED, 0x00EE, 0x00EF, 0x00EC, 0x00DF, 0x0021, 0x0024, 0x002A, 0x0029, 0x003B, 0x00AC, 0x002D, 0x002F, 0x00C2, 0x00C4, 0x00C0, 0x00C1, 0x00C3, 0x00C5, 0x00C7, 0x00D1, 0x00A6, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F, 0x00F8, 0x00C9, 0x00CA, 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF, 0x00CC, 0x0060, 0x003A, 0x0023, 0x0040, 0x0027, 0x003D, 0x0022, 0x00D8, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x00AB, 0x00BB, 0x00F0, 0x00FD, 0x00FE, 0x00B1, 0x00B0, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072, 0x00AA, 0x00BA, 0x00E6, 0x00B8, 0x00C6, 0x00A4, 0x00B5, 0x007E, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x00A1, 0x00BF, 0x00D0, 0x00DD, 0x00DE, 0x00AE, 0x005E, 0x00A3, 0x00A5, 0x00B7, 0x00A9, 0x00A7, 0x00B6, 0x00BC, 0x00BD, 0x00BE, 0x005B, 0x005D, 0x00AF, 0x00A8, 0x00B4, 0x00D7, 0x007B, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x00AD, 0x00F4, 0x00F6, 0x00F2, 0x00F3, 0x00F5, 0x007D, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052, 0x00B9, 0x00FB, 0x00FC, 0x00F9, 0x00FA, 0x00FF, 0x005C, 0x00F7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x00B2, 0x00D4, 0x00D6, 0x00D2, 0x00D3, 0x00D5, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x00B3, 0x00DB, 0x00DC, 0x00D9, 0x00DA, 0x009F }; /* Other proprietary 8-bit sets */ struct x_to_unicode u_decmcs = { 96, 32, X2U_DEC|X2U_STD, AL_ROMAN, "DEC Multinational", "dec-mcs", 0, "%5", 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0xfffd, 0x00A5, 0xfffd, 0x00A7, 0x00A4, 0x00A9, 0x00AA, 0x00AB, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0xfffd, 0x00B5, 0x00B6, 0x00B7, 0xfffd, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0xfffd, 0x00BF, 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, 0xfffd, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x0152, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0xfffd, 0x00DF, 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, 0xfffd, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x0153, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0xfffd, 0x00FF }; struct x_to_unicode u_hproman8 = { 96, 32, X2U_STD, AL_ROMAN, "Hewlett Packard Roman 8", "hp-roman8", 0, NULL, 0x00a0, 0x00c0, 0x00c2, 0x00c8, 0x00ca, 0x00cb, 0x00ce, 0x00cf, 0x00b4, 0x00a6, 0x00a9, 0x00a8, 0x00ac, 0x00d9, 0x00db, 0x20a4, 0x00af, 0x00dd, 0x00fd, 0x00b0, 0x00c7, 0x00e7, 0x00d1, 0x00f1, 0x00a1, 0x00bf, 0x00a4, 0x00a3, 0x00a5, 0x00a7, 0x0192, 0x00a2, 0x00e2, 0x00ea, 0x00f4, 0x00fb, 0x00e1, 0x00e9, 0x00f3, 0x00fa, 0x00e0, 0x00e8, 0x00f2, 0x00f9, 0x00e4, 0x00eb, 0x00f6, 0x00fc, 0x00c5, 0x00ee, 0x00d8, 0x00c6, 0x00e5, 0x00ed, 0x00f8, 0x00e6, 0x00c4, 0x00ec, 0x00d6, 0x00dc, 0x00c9, 0x00ef, 0x00df, 0x00d4, 0x00c1, 0x00c3, 0x00e3, 0x00d0, 0x00f0, 0x00cd, 0x00cc, 0x00d3, 0x00d2, 0x00d5, 0x00f5, 0x0160, 0x0161, 0x00da, 0x00b8, 0x00ff, 0x00de, 0x00fe, 0x00b7, 0x00b5, 0x00b6, 0x00be, 0x2015, 0x00bc, 0x00bd, 0x00aa, 0x00ba, 0x00ab, 0x2588, 0x00bb, 0x00b1, 0xfffd }; struct x_to_unicode u_dgi = { 96,32,X2U_STD,AL_ROMAN,"Data General International","dg-international",0,NULL, 0x00a0, 0x00ac, 0x00bd, 0x00b5, 0x00b2, 0x00b3, 0x00a4, 0x00a2, 0x00a3, 0x00aa, 0x00ba, 0x00a1, 0x00bf, 0x00a9, 0x00ae, 0x2021, 0x00bb, 0x00ab, 0x00b6, 0x2122, 0x0192, 0x00a5, 0x00b1, 0x2264, 0x2265, 0x00b7, 0x00b8, 0x00a7, 0x00b0, 0x00a8, 0x00b4, 0x2191, 0x00c1, 0x00c0, 0x00c2, 0x00c4, 0x00c3, 0x00c5, 0x00c6, 0x00c7, 0x00c9, 0x00c8, 0x00ca, 0x00cb, 0x00cd, 0x00cc, 0x00ce, 0x00cf, 0x00d1, 0x00d3, 0x00d2, 0x00d4, 0x00d6, 0x00d5, 0x00d8, 0x0276, 0x00da, 0x00d9, 0x00db, 0x00dc, 0xfffd, 0x0178, 0xfffd, 0xfffd, 0x00e1, 0x00e0, 0x00e2, 0x00e4, 0x00e3, 0x00e5, 0x00e6, 0x00e7, 0x00e9, 0x00e8, 0x00ea, 0x00eb, 0x00ed, 0x00ec, 0x00ee, 0x00ef, 0x00f1, 0x00f3, 0x00f2, 0x00f4, 0x00f6, 0x00f5, 0x00f8, 0x0153, 0x00fa, 0x00f9, 0x00fb, 0x00fc, 0x00df, 0x00ff, 0xfffd, 0x2588 }; struct x_to_unicode u_nextstep = { 128, 0, 0, AL_ROMAN,"NeXTSTEP Multinational","next-multinational",0,NULL, 0x00a0, 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c7, 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf, 0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00b5, 0x00d7, 0x00f7, 0x00a9, 0x00a1, 0x00a2, 0x00a3, 0x2044, 0x00a5, 0x0192, 0x00a7, 0x00a4, 0x2019, 0x201c, 0x00ab, 0x2039, 0x203a, 0xfb01, 0xfb02, 0x00ae, 0x2013, 0x2020, 0x2021, 0x00b7, 0x00a6, 0x00b6, 0x2022, 0x201a, 0x201e, 0x201d, 0x00bb, 0x2026, 0x2030, 0x00ac, 0x00bf, 0x00b9, 0x02cb, 0x00b4, 0x02c6, 0x02dc, 0x00af, 0x02d8, 0x02d9, 0x00a8, 0x00b2, 0x02da, 0x00b8, 0x00b3, 0x02dd, 0x02db, 0x02c7, 0x2014, 0x00b1, 0x00bc, 0x00bd, 0x00be, 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e7, 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00c6, 0x00ed, 0x00aa, 0x00ee, 0x00ef, 0x00f0, 0x00f1, 0x0141, 0x00d8, 0x0152, 0x00ba, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00e6, 0x00f9, 0x00fa, 0x00fb, 0x0131, 0x00fc, 0x00fd, 0x0142, 0x00f8, 0x0153, 0x00df, 0x00fe, 0x00ff, 0xfffd, 0xfffd }; struct x_to_unicode u_maclatin = { 128, 0, 0, AL_ROMAN,"Macintosh Latin","maclatin", 0, NULL, 0x00C4, 0x00C5, 0x00C7, 0x00C9, 0x00D1, 0x00D6, 0x00DC, 0x00E1, 0x00E0, 0x00E2, 0x00E4, 0x00E3, 0x00E5, 0x00E7, 0x00E9, 0x00E8, 0x00EA, 0x00EB, 0x00ED, 0x00EC, 0x00EE, 0x00EF, 0x00F1, 0x00F3, 0x00F2, 0x00F4, 0x00F6, 0x00F5, 0x00FA, 0x00F9, 0x00FB, 0x00FC, 0x00DD, 0x00B0, 0x00A2, 0x00A3, 0x00A7, 0x2022, 0x00B6, 0x00DF, 0x00AE, 0x00A9, 0x2122, 0x00B4, 0x00A8, 0x2260, 0x00C6, 0x00D8, 0x221E, 0x00B1, 0x2264, 0x2265, 0x00A5, 0x00B5, 0x2202, 0x2211, 0x220F, 0x03C0, 0x222B, 0x00AA, 0x00BA, 0x2126, 0x00E6, 0x00F8, 0x00BF, 0x00A1, 0x00AC, 0x221A, 0x0192, 0x2248, 0x2206, 0x00AB, 0x00BB, 0x2026, 0x00A0, 0x00C0, 0x00C3, 0x00D5, 0x0152, 0x0153, 0x2013, 0x2014, 0x201C, 0x201D, 0x2018, 0x2019, 0x00F7, 0x25CA, 0x00FF, 0x0178, 0x2044, 0x00A4, 0x00D0, 0x00F0, 0x00DE, 0x00FE, 0x00FD, 0x00B7, 0x201A, 0x201E, 0x2030, 0x00C2, 0x00CA, 0x00C1, 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF, 0x00CC, 0x00D3, 0x00D4, 0xF8FF, 0x00D2, 0x00DA, 0x00DB, 0x00D9, 0x0131, 0x02C6, 0x02DC, 0x00AF, 0x02D8, 0x02D9, 0x02DA, 0x00B8, 0x02DD, 0x02DB, 0x02C7 }; struct x_to_unicode u_quickdraw = { 128, 0, 0, AL_ROMAN,"QuickDraw","quickdraw", 0, NULL, 0x00C4, 0x00C5, 0x00C7, 0x00C9, 0x00D1, 0x00D6, 0x00DC, 0x00E1, 0x00E0, 0x00E2, 0x00E4, 0x00E3, 0x00E5, 0x00E7, 0x00E9, 0x00E8, 0x00EA, 0x00EB, 0x00ED, 0x00EC, 0x00EE, 0x00EF, 0x00F1, 0x00F3, 0x00F2, 0x00F4, 0x00F6, 0x00F5, 0x00FA, 0x00F9, 0x00FB, 0x00FC, 0x2020, 0x00B0, 0x00A2, 0x00A3, 0x00A7, 0x2022, 0x00B6, 0x00DF, 0x00AE, 0x00A9, 0x2122, 0x00B4, 0x00A8, 0x2260, 0x00C6, 0x00D8, 0x221E, 0x00B1, 0x2264, 0x2265, 0x00A5, 0x00B5, 0x2202, 0x2211, 0x220F, 0x03C0, 0x222B, 0x00AA, 0x00BA, 0x03A9, 0x00E6, 0x00F8, 0x00BF, 0x00A1, 0x00AC, 0x221A, 0x0192, 0x2248, 0x2206, 0x00AB, 0x00BB, 0x2026, 0x00A0, 0x00C0, 0x00C3, 0x00D5, 0x0152, 0x0153, 0x2013, 0x2014, 0x201C, 0x201D, 0x2018, 0x2019, 0x00F7, 0x25CA, 0x00FF, 0x0178, 0x2044, 0x00A4, 0x2039, 0x203A, 0xFB01, 0xFB02, 0x2021, 0x00B7, 0x201A, 0x201E, 0x2030, 0x00C2, 0x00CA, 0x00C1, 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF, 0x00CC, 0x00D3, 0x00D4, 0xF8FF, 0x00D2, 0x00DA, 0x00DB, 0x00D9, 0x0131, 0x02C6, 0x02DC, 0x00AF, 0x02D8, 0x02D9, 0x02DA, 0x00B8, 0x02DD, 0x02DB, 0x02C7 }; /* DEC special graphics / technical sets for VT emulation */ #ifdef KERMITFONT struct x_to_unicode u_dectech = { 94, 33, X2U_DEC|X2U_STD,AL_ROMAN,"DEC Technical", "dec-technical", 0, ">", 0xE400, 0x250c, 0x2500, 0x2320, 0x2321, 0x2502, 0xE204, 0xE203, 0xE209, 0xE208, 0xE202, 0xE201, 0xE207, 0xE206, 0xE200, 0xE205, 0xE20D, 0xE20C, 0x2572, 0x2571, 0xE20E, 0xE20F, 0x232a, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x2264, 0x2260, 0x2265, 0x222b, 0x2234, 0x221d, 0x221e, 0x00f7, 0x2206, 0x2207, 0x03a6, 0x0393, 0x223c, 0x2243, 0x0398, 0x00d7, 0x039b, 0x21d4, 0x21d2, 0x2261, 0x220f, 0x03a8, 0xE401, 0x03a3, 0xFFFD, 0xfffd, 0x221a, 0x03a9, 0x039e, 0x03d2, 0x2282, 0x2283, 0x2229, 0x222a, 0x2227, 0x2228, 0x00ac, 0x03b1, 0x03b2, 0x03c7, 0x03b4, 0x03b5, 0x03c6, 0x03b3, 0x03b7, 0x03b9, 0x03b8, 0x03ba, 0x03bb, 0xFFFD, 0x03bd, 0x2202, 0x03c0, 0x03c8, 0x03c1, 0x03c3, 0x03c4, 0xFFFD, 0x0192, 0x03c9, 0x03be, 0x03c5, 0x03b6, 0x2190, 0x2191, 0x2192, 0x2193 }; #else struct x_to_unicode u_dectech = { 94, 33, X2U_DEC|X2U_STD,AL_ROMAN,"DEC Technical", "dec-technical", 0, ">", 0x221a, 0x250c, 0x2500, 0x2320, 0x2321, 0x2502, 0x2308, /* 21-27 */ 0x230a, 0x2309, 0x230b, 0x256d, 0x2570, 0x256e, 0x256f, 0x2525, /* 28-2f */ 0x251d, 0x2211, 0x2211, 0x2572, 0x2571, 0x231d, 0x231f, 0x232a, /* 30-37 */ 0x005b, 0x2022, 0x005d, 0x00b1, 0x2264, 0x2260, 0x2265, 0x222b, /* 38-3f */ 0x2234, 0x221d, 0x221e, 0x00f7, 0x25b3, 0x25bd, 0x03a6, 0x0393, /* 40-47 */ 0x223c, 0x2243, 0x0398, 0x00d7, 0x039b, 0x21d4, 0x21d2, 0x2261, /* 48-4f */ 0x220f, 0x03a8, 0x2218, 0x2211, 0x00a7, 0x00b6, 0x221a, 0x03a9, /* 50-57 */ 0x039e, 0x03d2, 0x2282, 0x2283, 0x2229, 0x222a, 0x2227, 0x2228, /* 58-5f */ 0x00ac, 0x03b1, 0x03b2, 0x03c7, 0x03b4, 0x03b5, 0x03c6, 0x03b3, /* 60-67 */ 0x03b7, 0x03b9, 0x03b8, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x2202, /* 68-6f */ 0x03c0, 0x03c8, 0x03c1, 0x03c3, 0x03c4, 0x0020, 0x0192, 0x03c9, /* 70-77 */ 0x03be, 0x03c5, 0x03b6, 0x2190, 0x2191, 0x2192, 0x2193 /* 78-7e */ }; #endif /* KERMITFONT */ #ifdef KERMITFONT struct x_to_unicode u_decspec = { 94,33,X2U_DEC|X2U_STD,AL_ROMAN,"DEC Special Graphics","dec-special",0,"0", 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x2666, 0x2591, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0, 0x00b1, 0x2424, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c, 0x23BA, 0x23BB, 0x2500, 0x23BC, 0x23BD, 0x251c, 0x2524, 0x2534, 0x252c, 0x2502, 0x2264, 0x2265, 0x03c0, 0x2260, 0x00a3, 0x00B7 }; #else struct x_to_unicode u_decspec = { 94,33,X2U_DEC|X2U_STD,AL_ROMAN,"DEC Special Graphics","dec-special",0,"0", 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x2666, 0x2591, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0, 0x00b1, 0x2424, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c, 0x2594, 0x2500, 0x2500, 0x2500, 0x2500, 0x251c, 0x2524, 0x2534, 0x252c, 0x2502, 0x2264, 0x2265, 0x03a0, 0x2260, 0x00a3, 0x00B7 }; #endif /* KERMITFONT */ /* Hazeltine 1500/1520 graphic set. Includes several approximations: . (0,9) should be heavy black right arrow. Unicode has one of these at U+27A1 but... . (3,9) should be heavy black down arrow; Unicode doesn't have one. So we use the white versions of the heavy arrows instead. . (1,9) the letters "Pe" in one cell, doesn't exist in Unicode. Substitution is just "P". */ struct x_to_unicode u_hz1500 = { 94,33,X2U_STD,AL_ROMAN,"Hazeltime Graphics","hz1500-graphics",0,"0", /* 0 1 2 3 4 5 6 7 */ 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, /* 0 */ 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, /* 1 */ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, /* 2 */ 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, /* 3 */ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, /* 4 */ 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, /* 5 */ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, /* 6 */ 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, /* 7 */ 0x2500, 0x2502, 0x253c, 0x2534, 0x252c, 0x2514, 0x250c, 0x00b1, /* 8 */ 0x21e8, 0x0050, 0x00f7, 0x21e9, 0x2510, 0x2518, 0x251c, 0x2524, /* 9 */ 0x0070, 0x0071, 0x0072, 0x250c, 0x0074, 0x0075, 0x0076, 0x0077, /* a */ 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e /* b */ }; #ifdef KERMITFONT struct x_to_unicode u_heath19g = { 94,33,X2U_STD,AL_ROMAN,"Heath-19 Special Graphics","h19-special",0,NULL, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x25cf, 0xe30b, 0x2502, 0x2500, 0x253c, 0x2510, 0x2518, 0x2514, 0x250c, 0x00b1, 0x2192, 0x2592, 0x00f7, 0x2193, 0xe321, 0xe320, 0xe322, 0xe328, 0x2580, 0xe325, 0xe30a, 0x252c, 0x2524, 0x2534, 0x251c, 0x2573, 0x2571, 0x2572, 0xe311, 0xe319, 0xe300, 0xe309, 0x00b6 }; #else struct x_to_unicode u_heath19g = { 94,33,X2U_STD,AL_ROMAN,"Heath-19 Special Graphics","h19-special",0,NULL, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x25cf, 0x25e5, 0x2502, 0x2500, 0x253c, 0x2510, 0x2518, 0x2514, 0x250c, 0x00b1, 0x2192, 0x2592, 0x00f7, 0x2193, 0x2590, 0x258c, 0x258c, 0x2590, 0x2580, 0x2590, 0x25e4, 0x252c, 0x2524, 0x2534, 0x251c, 0x2573, 0x2571, 0x2572, 0x2500, 0x2500, 0x2502, 0x2502, 0x00b6 }; #endif /* KERMITFONT */ /* DG Graphic sets - "KERMITFONT" these later... */ /* Missing, backwards question mark eighth note "DT" control pic horizontal scan lines */ struct x_to_unicode u_dgspec = { /* Needs to be checked */ 94, 33, X2U_STD,AL_ROMAN,"DG Special Graphics","dg-specialgraphics",0,NULL, 0xfffd, 0xfffd, 0x2424, 0x2594, 0x2594, 0x2581, 0x2581, 0x25a1, 0x263a, 0x263b, 0x2665, 0x2663, 0x2660, 0x25cf, 0x25d8, 0x25cb, 0x25d9, 0x2642, 0x2640, 0x266a, 0x266c, 0x263c, 0x2195, 0x2583, 0x21a8, 0x231e, 0x2194, 0x2207, 0x00ff, 0x20a7, 0x00aa, 0x00ba, 0x231c, 0x231d, 0x2591, 0x2591, 0x2593, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x255e, 0x255f, 0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567, 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256b, 0x256a, 0x2588, 0x2584, 0x258c, 0x2590, 0x2580, 0x0393, 0x03c0, 0x03a3, 0x03a6, 0x0398, 0x03d5, 0x03b5, 0x03a0, 0x039e, 0x00b7, 0x03b7, 0x25a0, 0x0178, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd }; /* Missing: arrow-to-line, various orientations */ struct x_to_unicode u_dgline = { 94, 33, X2U_STD,AL_ROMAN,"DG Line Drawing","dg-linedrawing",0,NULL, 0x250c, 0x2510, 0x2514, 0x2518, 0x252c, 0x2524, 0x251c, 0x2534, 0x253c, 0x2502, 0x2500, 0x219f, 0x21e5, 0x21e4, 0x21a1, 0x2506, 0x250f, 0x2513, 0x2517, 0x251b, 0x2533, 0x252b, 0x2523, 0x253b, 0x254b, 0x2503, 0x2501, 0x2504, 0x00f7, 0x00a2, 0x2122, 0x00ae, 0x00a9, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd }; struct x_to_unicode u_dgword = { 94, 33, X2U_STD,AL_ROMAN,"DG Word Processing","dg-wordprocessing",0,NULL, 0x2308, 0x230a, 0x2309, 0x230b, 0x0192, 0x223c, 0x2202, 0xfffd, 0xfffd, 0x2320, 0x2321, 0x221a, 0xfffd, 0x221e, 0x221d, 0x2070, 0x00b9, 0x00b2, 0x00b3, 0x2074, 0x2075, 0x2076, 0x2077, 0x2078, 0x2079, 0x2260, 0xfffd, 0x21ef, 0xfffd, 0x21e5, 0x00b7, 0x203c, 0x03b1, 0x03b2, 0x03b3, 0x03b4, 0x03b5, 0x03b6, 0x03b7, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf, 0x03c0, 0x03c1, 0x03c3, 0x03c4, 0x03c5, 0x03c6, 0x03c7, 0x03c8, 0x03c9, 0x03a9, 0x0394, 0x00b6, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x22a6, 0x25c6, 0x25b6, 0x25b7, 0x25c0, 0x25b2, 0x25bc, 0x2327, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x25e3, 0x25e2, 0x2080, 0x2081, 0x2082, 0x2083, 0x2084, 0x2085, 0x2086, 0x2087, 0x2088, 0x2089, 0xfffd, 0x2191, 0x2192, 0x2190, 0x2193 }; /* HP Graphic sets - "KERMITFONT" these later... */ /* Many are missing from Unicode, Single-to-triple-line box-drawing characters. Double/double cross & some others. */ struct x_to_unicode u_hpline = { /* Needs to be checked */ 94, 33, X2U_STD,AL_ROMAN,"HP Line Drawing Graphics", "hp-line-drawing",0,NULL, 0x2520, 0x2528, 0x252f, 0x2537, 0x255f, 0x2562, 0x2564, 0x2567, 0x2551, 0x2542, 0x253f, 0x2550, 0x230a, 0x2502, 0x253c, 0x254b, 0x2523, 0x252b, 0x2533, 0x253b, 0x251c, 0x2524, 0x252c, 0x2534, 0x2550, 0x2503, 0x2501, 0x256b, 0x2308, 0x256a, 0x256c, 0x255e, 0x2517, 0x2549, 0x2588, 0x258c, 0x258e, 0x2514, 0x2518, 0x2510, 0x2555, 0x252c, 0x2556, 0x2556, 0x2547, 0x2548, 0x2555, 0x230b, 0x250f, 0x250c, 0x251b, 0x2510, 0x2524, 0x254a, 0x2513, 0x2584, 0x2309, 0x2582, 0x2561, 0x2504, 0x2559, 0x2576, 0x2565, 0x255e, 0x2517, 0x2549, 0x2588, 0x258c, 0x258e, 0x2514, 0x2518, 0x2510, 0x2555, 0x252c, 0x2556, 0x2556, 0x2547, 0x2548, 0x2555, 0x230b, 0x250f, 0x250c, 0x251b, 0x2510, 0x2524, 0x254a, 0x2513, 0x2584, 0x2309, 0x2582, 0x2561, 0x2504, 0x2559, 0x2576 }; struct x_to_unicode u_hpmath = { 94, 33, X2U_STD,AL_ROMAN,"HP Math/Technical","hp-math/technical",0,NULL, 0x221a, 0x2223, 0x00a7, 0x2207, 0x00b1, 0x03b1, 0x2320, 0x00f7, 0x2243, 0x03a0, 0x0393, 0x03c8, 0x2261, 0x03a6, 0x039e, 0x2070, 0x00b9, 0x00b2, 0x00b3, 0x2074, 0x2075, 0x2076, 0x2077, 0x2078, 0x2079, 0x03a9, 0x039b, 0x221e, 0x2321, 0x2020, 0x03a3, 0x00b6, 0x03b1, 0x03b2, 0x03c8, 0x03d5, 0x03b5, 0x2202, 0x03bb, 0x03b7, 0x03b9, 0x0398, 0x03ba, 0x03c9, 0x03bc, 0x03bd, 0x03c1, 0x03c0, 0x03b3, 0x03b8, 0x03c3, 0x03c4, 0x03be, 0x0394, 0x03b4, 0x00d7, 0x03c5, 0x03b6, 0x2191, 0x2192, 0x03d2, 0x2190, 0x2193, 0x00b6, 0x03b1, 0x03b2, 0x03c8, 0x03d5, 0x03b5, 0x2202, 0x03bb, 0x03b7, 0x03b9, 0x0398, 0x03ba, 0x03c9, 0x03bc, 0x03bd, 0x03c1, 0x03c0, 0x03b3, 0x03b8, 0x03c3, 0x03c4, 0x03be, 0x0394, 0x03b4, 0x00d7, 0x03c5, 0x03b6, 0x2191, 0x2192, 0x03d2, 0x2190 }; struct x_to_unicode u_tvig = { 15,65,0,0,"Televideo Special Graphics","tvi-special",0,NULL, 0x2570, 0x256D, 0x256E, 0x256F, 0x2514, 0x250C, 0x2510, 0x2518, 0x253C, 0x2502, 0x2500, 0x2524, 0x251C, 0x252C, 0x2534 }; struct x_to_unicode u_wyse_gn = { #ifdef COMMENT 16,16,0,0,"Wyse Normal-Mode Graphics","wy-graphics-normal",0,NULL, 0x252C, 0x2514, 0x250C, 0x2510, 0x251C, 0x2518, 0x2502, 0x2588, 0x253C, 0x2524, 0x2500, 0x2592, 0x2550, 0x2534, 0x2551, 0x2591 #else 80,48,0,0,"Wyse Normal-Mode Graphics","wy-graphics-normal",0,NULL, 0x252C, 0x2514, 0x250C, 0x2510, 0x251C, 0x2518, 0x2502, 0x2588, 0x253C, 0x2524, 0x2500, 0x2592, 0x2550, 0x2534, 0x2551, 0x2591, 0x252C, 0x2514, 0x250C, 0x2510, 0x251C, 0x2518, 0x2502, 0x2588, 0x253C, 0x2524, 0x2500, 0x2592, 0x2550, 0x2534, 0x2551, 0x2591, 0x252C, 0x2514, 0x250C, 0x2510, 0x251C, 0x2518, 0x2502, 0x2588, 0x253C, 0x2524, 0x2500, 0x2592, 0x2550, 0x2534, 0x2551, 0x2591, 0x252C, 0x2514, 0x250C, 0x2510, 0x251C, 0x2518, 0x2502, 0x2588, 0x253C, 0x2524, 0x2500, 0x2592, 0x2550, 0x2534, 0x2551, 0x2591, 0x252C, 0x2514, 0x250C, 0x2510, 0x251C, 0x2518, 0x2502, 0x2588, 0x253C, 0x2524, 0x2500, 0x2592, 0x2550, 0x2534, 0x2551, 0x2591 #endif /* COMMENT */ }; struct x_to_unicode u_wyse_g1 = { 79,48,0,0,"Wyse Graphics 1","wy-graphics-1",0,NULL, 0x2070, 0x00B9, 0x00B2, 0x00B3, 0x2074, 0x2075, 0x2075, 0x2077, 0x2078, 0x2079, 0xFFFD, 0xFFFD, 0x25BA, 0x25C4, 0x25B2, 0x25BC, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0x2080, 0x2081, 0x2082, 0x2083, 0x2084, 0x2085, 0x2086, 0x2087, 0x2088, 0x2089, 0x2518, 0x2510, 0x250C, 0x2514, 0x253C, 0x258C, 0x2590, 0x2500, 0x2584, 0x2580, 0x251C, 0x2524, 0x2534, 0x252C, 0x2502, 0xFFFD, 0x256E, 0x256F, 0x2570, 0x256D, 0x258C }; struct x_to_unicode u_wyse_g2 = { 41,64,0,0,"Wyse Graphics 2","wy-graphics-2",0,NULL, 0x250C, 0xFFFD, 0xFFFD, 0xFFFD, 0x2510, 0xFFFD, 0xFFFD, 0xFFFD, 0x2514, 0xFFFD, 0xFFFD, 0xFFFD, 0x2518, 0xFFFD, 0xFFFD, 0xFFFD, 0x252C, 0xFFFD, 0xFFFD, 0xFFFD, 0x2524, 0xFFFD, 0xFFFD, 0xFFFD, 0x251C, 0xFFFD, 0xFFFD, 0xFFFD, 0x2534, 0xFFFD, 0xFFFD, 0xFFFD, 0x2500, 0xFFFD, 0xFFFD, 0xFFFD, 0x2502, 0xFFFD, 0xFFFD, 0xFFFD, 0x253C }; #ifdef KERMITFONT struct x_to_unicode u_wyse_g3 = { 31,65,0,0,"Wyse Graphics 3","wy-graphics-3",0,NULL, 0x2570, 0x256D, 0x256E, 0x256F, 0x2514, 0x250C, 0x2510, 0x2518, 0x253C, 0x2502, 0x2500, 0x2524, 0x251C, 0x252C, 0x2534, 0x23BA, 0x23BD, 0x2666, 0xE328, 0xE321, 0xE320, 0xE322, 0x2590, 0x2584, 0x258C, 0x2580, 0xE323, 0xE326, 0xE327, 0xE329, 0x258C }; #else struct x_to_unicode u_wyse_g3 = { 31,65,0,0,"Wyse Graphics 3","wy-graphics-3",0,NULL, 0x2570, 0x256D, 0x256E, 0x256F, 0x2514, 0x250C, 0x2510, 0x2518, 0x253C, 0x2502, 0x2500, 0x2524, 0x251C, 0x252C, 0x2534, 0x2500, 0x2500, 0x2666, 0x2590, 0x2590, 0x258c, 0x258c, 0x2590, 0x2584, 0x258C, 0x2580, 0x2588, 0x2588, 0x2588, 0x2588, 0x258C }; #endif /* KERMITFONT */ /* QNX Console -- This is exactly the same as CP437 except for the code point at 0xEE (epsilon vs element-of), which I think was just a mistake in reading glyphs by the QNX people, but who knows. Also the glyph at 0xED might be a fi (as it is in CP437, and as I have it here) or it might be a null-set symbol. */ struct x_to_unicode u_qnxgrph = { 128,0,0,0,"QNX Console","qnx-console",0,NULL, 0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7, /* 128 */ 0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5, /* 136 */ 0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9, /* 144 */ 0x00ff, 0x00d6, 0x00dc, 0x00a2, 0x00a3, 0x00a5, 0x20a7, 0x0192, /* 152 */ 0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00ba, /* 160 */ 0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb, /* 168 */ 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, /* 176 */ 0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510, /* 184 */ 0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f, /* 192 */ 0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567, /* 200 */ 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256b, /* 208 */ 0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258c, 0x2590, 0x2580, /* 216 */ 0x221d, 0x00df, 0x0393, 0x03c0, 0x03a3, 0x03c3, 0x00b5, 0x03c4, /* 224 */ 0x03a6, 0x0398, 0x03a9, 0x03b4, 0x221e, 0x03c6, 0x2208, 0x2229, /* 232 */ 0x2261, 0x00b1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00f7, 0x2248, /* 240 */ 0x00b0, 0x2219, 0x00b7, 0x221a, 0x207f, 0x00b2, 0x25ae, 0x00a0 /* 248 */ }; struct x_to_unicode u_snibrack = { 94, 33, 0, 0,"Siemens Nixdorf 97801 Brackets","sni-brackets",0,"w", 0x2590, 0x258c, 0x2584, 0x2580, 0x2590, 0x258c, 0x2584, /* a0-7 */ 0x2580, 0x2329, 0x2327, 0x25af, 0x00b7, 0x25b9, 0x25c1, 0x003c, /* a8-f */ 0x253b, 0x2533, 0x2523, 0x252b, 0x2329, 0x232a, 0x2304, 0x2303, /* b0-7 */ 0x25e4, 0x25e5, 0x25e3, 0x25e2, 0x253f, 0x231b, 0x25cf, 0x25cb, /* b8-f */ 0x2502, 0x2500, 0x250c, 0x2510, 0x2514, 0x2518, 0x251c, 0x2524, /* c0-7 */ 0x252c, 0x2534, 0x253c, 0x2192, 0x2190, 0x2191, 0x2193, 0x2575, /* c8-f */ 0x2577, 0x25d4, 0x256d, 0x256e, 0x2570, 0x256f, 0x251c, 0x2524, /* d0-7 */ 0x252c, 0x2534, 0x253c, 0x253c, 0x2592, 0x2591, 0x2592, 0x2593, /* d8-f */ 0x2503, 0x2501, 0x250f, 0x2513, 0x2517, 0x251b, 0x2523, 0x252b, /* e0-7 */ 0x2533, 0x253b, 0x254b, 0x279e, 0x2190, 0x2191, 0x2193, 0x2579, /* e8-f */ 0x257b, 0x2261, 0x2554, 0x2557, 0x255a, 0x255d, 0x2523, 0x252b, /* f0-7 */ 0x2533, 0x253b, 0x254b, 0x254b, 0x0393, 0x03c3, 0x03c4 /* f8-f */ }; struct x_to_unicode u_sniblanks = { 94, 33, 0, 0,"Siemens Nixdorf 97801 Blanks","sni-blanks",0,"y", 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, /* a0-7 */ 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, /* a8-f */ 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, /* b0-7 */ 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, /* b8-f */ 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, /* c0-7 */ 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, /* c8-f */ 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, /* d0-7 */ 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, /* d8-f */ 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, /* e0-7 */ 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, /* e8-f */ 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, /* f0-7 */ 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020 /* f8-f */ }; struct x_to_unicode u_snifacet = { 94, 33, 0, 0,"Siemens Nixdorf 97801 Facet","sni-facet",0,"c", 0x0020, 0x2581, 0x2582, 0x2583, 0x2584, 0x2585, 0x2587, /* a1-a7 */ 0x005f, 0x2581, 0x2582, 0x2583, 0x2584, 0x2585, 0x2586, 0x2587, /* a8-af */ 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, /* b0-b7 */ 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, /* b8-bf */ 0x2503, 0x2501, 0x250f, 0x2513, 0x2517, 0x251b, 0x2523, 0x252b, /* c0-c7 */ 0x2533, 0x253b, 0x254b, 0x258f, 0x2595, 0xfffd, 0xfffd, 0xfffd, /* c8-cf */ 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, /* d0-d7 */ 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x2591, 0x2591, 0x2588, /* d8-df */ 0x2503, 0x2501, 0x250f, 0x2513, 0x2517, 0x251b, 0x2523, 0x252b, /* e0-e7 */ 0x2533, 0x253b, 0x254b, 0xfffd, 0xfffd, 0x2593, 0x2593, 0x0020, /* e8-ef */ 0x2585, 0x2586, 0x2587, 0x2588, 0xfffd, 0xfffd, 0xfffd, 0xfffd, /* f0-f7 */ 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x2594, 0x2594, 0xfffd /* f8-fe */ }; struct x_to_unicode u_sniibm = { 94, 33, 0, 0,"Siemens Nixdorf 97801 IBM","sni-ibm",0,"v", 0x263a, 0x263b, 0x2665, 0x2666, 0x2663, 0x2660, 0x25cf, 0x25d8, 0x25cb, 0x25d9, 0x2642, 0x2640, 0x266a, 0x266b, 0x263c, 0x25ba, 0x25c4, 0x2195, 0x203c, 0x00b6, 0x25c1, 0x2582, 0x21a8, 0x2191, 0x2193, 0x2192, 0x2190, 0x2319, 0x2194, 0x25b4, 0x25be, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, /* Hex */ 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, /* bytes */ 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x2070, 0x00b9, 0x00b2, 0x00b3, 0x2074, 0x2075, 0x2076, 0x2077, 0x2078, 0x2079, 0x207b, 0x207a, 0xfffd, 0xfffd, 0x2320, 0x2321, 0x2080, 0x2081, 0x2082, 0x2083, 0x2084, 0x2085, 0x2086, 0x2087, 0x2088, 0x2089, 0x208b, 0x208a, 0x221e, 0x03b1, 0x03a6 }; struct x_to_unicode u_snieuro = { 94, 33, 0, 0,"Siemens Nixdorf 97801 Euro","sni-euro",0,"u", 0x00e0, 0x00e1, 0x00e2, 0x00e4, 0x00e5, 0x0105, 0x00e3, 0x0103, 0x00e6, 0x00e7, 0x010d, 0x0107, 0x00f0, 0x0111, 0x010f, 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x011b, 0x0119, 0x011f, 0x0131, 0x00ee, 0x00ec, 0x01d0, 0x00ef, 0x0133, 0x013a, 0x0142, 0x013e, 0x0148, 0x0144, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f6, 0x00f5, 0x00f8, 0x0151, 0x0153, 0x00fe, 0x0159, 0x0155, 0x0161, 0x015b, 0x015f, 0x00df, 0x0163, 0x0165, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x016f, 0x0171, 0x00fd, 0x00ff, 0x017e, 0x017a, 0x017c, 0x00c9, 0x00c5, 0x00c6, 0x00d0, 0x0130, 0x0132, 0x0167, 0x00d8, 0x0152, 0x00de, 0x00c4, 0x00d6, 0x00dc, 0x00a7, 0x0024, 0x00a3, 0x00ae, 0x00a9, 0x03a9, 0x00b5, 0x00b0, 0x00c7, 0x20a7, 0x03c0, 0x02d8, 0x00b4, 0x02dd, 0x00d1, 0x2514, 0x2518, 0x007e, 0x02c7 }; struct x_to_unicode u_smiley = { 32,0,X2U_CXG,0,"PC C0 Graphics","smiley-faces",0,NULL, 0x00a0, 0x263a, 0x263b, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022, 0x25d8, 0x25ef, 0x25d9, 0x2642, 0x2640, 0x266a, 0x266c, 0x263c, 0x25ba, 0x25c4, 0x2195, 0x203c, 0x00b6, 0x00a7, 0x25ac, 0x21a8, 0x2191, 0x2193, 0x2192, 0x2190, 0x2319, 0x2194, 0x25b2, 0x25bc }; struct x_to_unicode u_c0pics = { 128,0,X2U_CXG,0,"C0/C1 Display Controls","display-controls",0,NULL, 0x2400, 0x2401, 0x2402, 0x2403, 0x2404, 0x2405, 0x2406, 0x2407, 0x2408, 0x2409, 0x240a, 0x240b, 0x240c, 0x240d, 0x240e, 0x240f, 0x2410, 0x2411, 0x2412, 0x2413, 0x2414, 0x2415, 0x2416, 0x2417, 0x2418, 0x2419, 0x241a, 0x241b, 0x241c, 0x241d, 0x241e, 0x241f, 0x2420, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0x2421 }; #ifdef KERMITFONT struct x_to_unicode u_c1pics = { 32,0,X2U_CXG,0,"C1 Display Controls","c1-display-controls",0,NULL, 0xe080, 0xe081, 0xe082, 0xe083, 0xe084, 0xe085, 0xe086, 0xe087, 0xe088, 0xe089, 0xe08a, 0xe08b, 0xe08c, 0xe08d, 0xe08e, 0xe08f, 0xe090, 0xe091, 0xe092, 0xe093, 0xe094, 0xe095, 0xe096, 0xe097, 0xe098, 0xe099, 0xe09a, 0xe09b, 0xe09c, 0xe09d, 0xe09e, 0xe09f }; #else struct x_to_unicode u_c1pics = { 32,0,X2U_CXG,0,"C1 Display Controls","c1-display-controls",0,NULL, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd }; #endif /* KERMITFONT */ /* Blah-to-Unicode functions */ USHORT #ifdef CK_ANSIC ascii_u(CHAR c) #else ascii_u(c) CHAR c; #endif /* CK_ANSIC */ { if (c & 0x80) return(UNK); /* NOTE: Strict ANSI compilers complain about "<" and similar comparisons between unsigned and signed quantities, as found in all the routines of the "blah_u()" class -- casts must be added to squelch the warnings. */ if (c < u_ascii.offset) return(c); else if (c >= u_ascii.offset + u_ascii.size) return(c); else return(u_ascii.map[c - u_ascii.offset]); } USHORT #ifdef CK_ANSIC british_u(CHAR c) #else british_u(c) CHAR c; #endif /* CK_ANSIC */ { if (c & 0x80) return(UNK); if (c < u_british.offset) return(c); else if (c >= u_british.offset + u_british.size) return(c); else return(u_british.map[c - u_british.offset]); } USHORT #ifdef CK_ANSIC dutch_u(CHAR c) #else dutch_u(c) CHAR c; #endif /* CK_ANSIC */ { if (c & 0x80) return(UNK); if (c < u_dutch.offset) return(c); else if (c >= u_dutch.offset + u_dutch.size) return(c); else return(u_dutch.map[c - u_dutch.offset]); } USHORT #ifdef CK_ANSIC finnish_u(CHAR c) #else finnish_u(c) CHAR c; #endif /* CK_ANSIC */ { if (c & 0x80) return(UNK); if (c < u_finnish.offset) return(c); else if (c >= u_finnish.offset + u_finnish.size) return(c); else return(u_finnish.map[c - u_finnish.offset]); } USHORT #ifdef CK_ANSIC french_u(CHAR c) #else french_u(c) CHAR c; #endif /* CK_ANSIC */ { if (c & 0x80) return(UNK); if (c < u_french.offset) return(c); else if (c >= u_french.offset + u_french.size) return(c); else return(u_french.map[c - u_french.offset]); } USHORT #ifdef CK_ANSIC fr_canadian_u(CHAR c) #else fr_canadian_u(c) CHAR c; #endif /* CK_ANSIC */ { if (c & 0x80) return(UNK); if (c < u_fr_canadian.offset) return(c); else if (c >= u_fr_canadian.offset + u_fr_canadian.size) return(c); else return(u_fr_canadian.map[c - u_fr_canadian.offset]); } USHORT #ifdef CK_ANSIC german_u(CHAR c) #else german_u(c) CHAR c; #endif /* CK_ANSIC */ { if (c & 0x80) return(UNK); if (c < u_german.offset) return(c); else if (c >= u_german.offset + u_german.size) return(c); else return(u_german.map[c - u_german.offset]); } USHORT #ifdef CK_ANSIC hungarian_u(CHAR c) #else hungarian_u(c) CHAR c; #endif /* CK_ANSIC */ { if (c & 0x80) return(UNK); if (c < u_hungarian.offset) return(c); else if (c >= u_hungarian.offset + u_hungarian.size) return(c); else return(u_hungarian.map[c - u_hungarian.offset]); } USHORT #ifdef CK_ANSIC italian_u(CHAR c) #else italian_u(c) CHAR c; #endif /* CK_ANSIC */ { if (c & 0x80) return(UNK); if (c < u_italian.offset) return(c); else if (c >= u_italian.offset + u_italian.size) return(c); else return(u_italian.map[c - u_italian.offset]); } USHORT #ifdef CK_ANSIC icelandic_u(CHAR c) #else icelandic_u(c) CHAR c; #endif /* CK_ANSIC */ { if (c & 0x80) return(UNK); if (c < u_icelandic.offset) return(c); else if (c >= u_icelandic.offset + u_icelandic.size) return(c); else return(u_icelandic.map[c - u_icelandic.offset]); } USHORT #ifdef CK_ANSIC jis0201r_u(CHAR c) #else jis0201r_u(c) CHAR c; #endif /* CK_ANSIC */ { if (c & 0x80) return(UNK); if (c < u_jis0201r.offset) return(c); else if (c >= u_jis0201r.offset + u_jis0201r.size) return(c); else return(u_jis0201r.map[c - u_jis0201r.offset]); } USHORT #ifdef CK_ANSIC jis0201k_u(CHAR c) #else jis0201k_u(c) CHAR c; #endif /* CK_ANSIC */ { if (c & 0x80) return(UNK); if (c < u_jis0201k.offset) return(c); else if (c >= u_jis0201k.offset + u_jis0201k.size) return(c); else return(u_jis0201k.map[c - u_jis0201k.offset]); } USHORT #ifdef CK_ANSIC norwegian_u(CHAR c) #else norwegian_u(c) CHAR c; #endif /* CK_ANSIC */ { if (c & 0x80) return(UNK); if (c < u_norwegian.offset) return(c); else if (c >= u_norwegian.offset + u_norwegian.size) return(c); else return(u_norwegian.map[c - u_norwegian.offset]); } USHORT #ifdef CK_ANSIC danish_u(CHAR c) #else danish_u(c) CHAR c; #endif /* CK_ANSIC */ { if (c & 0x80) return(UNK); if (c < u_danish.offset) return(c); else if (c >= u_danish.offset + u_danish.size) return(c); else return(u_danish.map[c - u_danish.offset]); } USHORT #ifdef CK_ANSIC portuguese_u(CHAR c) #else portuguese_u(c) CHAR c; #endif /* CK_ANSIC */ { if (c & 0x80) return(UNK); if (c < u_portuguese.offset) return(c); else if (c >= u_portuguese.offset + u_portuguese.size) return(c); else return(u_portuguese.map[c - u_portuguese.offset]); } USHORT #ifdef CK_ANSIC spanish_u(CHAR c) #else spanish_u(c) CHAR c; #endif /* CK_ANSIC */ { if (c & 0x80) return(UNK); if (c < u_spanish.offset) return(c); else if (c >= u_spanish.offset + u_spanish.size) return(c); else return(u_spanish.map[c - u_spanish.offset]); } USHORT #ifdef CK_ANSIC swedish_u(CHAR c) #else swedish_u(c) CHAR c; #endif /* CK_ANSIC */ { if (c & 0x80) return(UNK); if (c < u_swedish.offset) return(c); else if (c >= u_swedish.offset + u_swedish.size) return(c); else return(u_swedish.map[c - u_swedish.offset]); } USHORT #ifdef CK_ANSIC swiss_u(CHAR c) #else swiss_u(c) CHAR c; #endif /* CK_ANSIC */ { if (c & 0x80) return(UNK); if (c < u_swiss.offset) return(c); else if (c >= u_swiss.offset + u_swiss.size) return(c); else return(u_swiss.map[c - u_swiss.offset]); } USHORT #ifdef CK_ANSIC apl1_u(CHAR c) #else apl1_u(c) CHAR c; #endif /* CK_ANSIC */ { if (c & 0x80) return(UNK); if (c < u_apl1.offset) return(c); else if (c >= u_apl1.offset + u_apl1.size) return(c); else return(u_apl1.map[c - u_apl1.offset]); } USHORT #ifdef CK_ANSIC ident_u(CHAR c) #else /* CK_ANSIC */ ident_u(c) CHAR c; #endif /* CK_ANSIC */ { return((USHORT)c); } USHORT #ifdef CK_ANSIC iso_8859_1_u(CHAR c) #else iso_8859_1_u(c) CHAR c; #endif /* CK_ANSIC */ { if (c >= 0x80 && c < 0xa0) return(c); c &= 0x7f; if (c < u_8859_1.offset) return(c); else if (c >= u_8859_1.offset + u_8859_1.size) return(c); else return(u_8859_1.map[c - u_8859_1.offset]); } USHORT #ifdef CK_ANSIC iso_8859_2_u(CHAR c) #else iso_8859_2_u(c) CHAR c; #endif /* CK_ANSIC */ { if (c >= 0x80 && c < 0xa0) return(c); c &= 0x7f; if (c < u_8859_2.offset) return(c); else if (c >= u_8859_2.offset + u_8859_2.size) return(c); else return(u_8859_2.map[c - u_8859_2.offset]); } USHORT #ifdef CK_ANSIC iso_8859_3_u(CHAR c) #else iso_8859_3_u(c) CHAR c; #endif /* CK_ANSIC */ { if (c >= 0x80 && c < 0xa0) return(c); c &= 0x7f; if (c < u_8859_3.offset) return(c); else if (c >= u_8859_3.offset + u_8859_3.size) return(c); else return(u_8859_3.map[c - u_8859_3.offset]); } USHORT #ifdef CK_ANSIC iso_8859_4_u(CHAR c) #else iso_8859_4_u(c) CHAR c; #endif /* CK_ANSIC */ { if (c >= 0x80 && c < 0xa0) return(c); c &= 0x7f; if (c < u_8859_4.offset) return(c); else if (c >= u_8859_4.offset + u_8859_4.size) return(c); else return(u_8859_4.map[c - u_8859_4.offset]); } USHORT #ifdef CK_ANSIC iso_8859_5_u(CHAR c) #else iso_8859_5_u(c) CHAR c; #endif /* CK_ANSIC */ { if (c >= 0x80 && c < 0xa0) return(c); c &= 0x7f; if (c < u_8859_5.offset) return(c); else if (c >= u_8859_5.offset + u_8859_5.size) return(c); else return(u_8859_5.map[c - u_8859_5.offset]); } USHORT #ifdef CK_ANSIC koi8_u(CHAR c) #else koi8_u(c) CHAR c; #endif /* CK_ANSIC */ { if (c >= 0x80 && c < 0xa0) return(c); c &= 0x7f; if (c < u_koi8.offset) return(c); else if (c >= u_koi8.offset + u_koi8.size) return(c); else return(u_koi8.map[c - u_koi8.offset]); } USHORT #ifdef CK_ANSIC koi8r_u(CHAR c) /* KOI8-R to Unicode */ #else koi8r_u(c) CHAR c; #endif /* CK_ANSIC */ { c &= 0x7f; if (c < u_koi8r.offset) return(c); else if (c >= u_koi8r.offset + u_koi8r.size) return(c); else return(u_koi8r.map[c - u_koi8r.offset]); } USHORT #ifdef CK_ANSIC koi8u_u(CHAR c) #else koi8u_u(c) CHAR c; #endif /* CK_ANSIC */ { c &= 0x7f; if (c < u_koi8u.offset) return(c); else if (c >= u_koi8u.offset + u_koi8u.size) return(c); else return(u_koi8u.map[c - u_koi8u.offset]); } USHORT #ifdef CK_ANSIC iso_8859_6_u(CHAR c) #else iso_8859_6_u(c) CHAR c; #endif /* CK_ANSIC */ { if (c >= 0x80 && c < 0xa0) return(c); c &= 0x7f; if (c < u_8859_6.offset) return(c); else if (c >= u_8859_6.offset + u_8859_6.size) return(c); else return(u_8859_6.map[c - u_8859_6.offset]); } USHORT #ifdef CK_ANSIC iso_8859_7_u(CHAR c) #else iso_8859_7_u(c) CHAR c; #endif /* CK_ANSIC */ { if (c >= 0x80 && c < 0xa0) return(c); c &= 0x7f; if (c < u_8859_7.offset) return(c); else if (c >= u_8859_7.offset + u_8859_7.size) return(c); else return(u_8859_7.map[c - u_8859_7.offset]); } USHORT #ifdef CK_ANSIC iso_8859_8_u(CHAR c) #else iso_8859_8_u(c) CHAR c; #endif /* CK_ANSIC */ { if (c >= 0x80 && c < 0xa0) return(c); c &= 0x7f; if (c < u_8859_8.offset) return(c); else if (c >= u_8859_8.offset + u_8859_8.size) return(c); else return(u_8859_8.map[c - u_8859_8.offset]); } USHORT #ifdef CK_ANSIC hebrew7_u(CHAR c) #else hebrew7_u(c) CHAR c; #endif /* CK_ANSIC */ { c &= 0x7f; if (c < u_hebrew7.offset) return(c); else if (c >= u_hebrew7.offset + u_hebrew7.size) return(c); else return(u_hebrew7.map[c - u_hebrew7.offset]); } USHORT #ifdef CK_ANSIC elot927_u(CHAR c) #else elot927_u(c) CHAR c; #endif /* CK_ANSIC */ { c &= 0x7f; if (c < u_elot927.offset) return(c); else if (c >= u_elot927.offset + u_elot927.size) return(c); else return(u_elot927.map[c - u_elot927.offset]); } USHORT #ifdef CK_ANSIC iso_8859_9_u(CHAR c) #else iso_8859_9_u(c) CHAR c; #endif /* CK_ANSIC */ { if (c >= 0x80 && c < 0xa0) return(c); c &= 0x7f; if (c < u_8859_9.offset) return(c); else if (c >= u_8859_9.offset + u_8859_9.size) return(c); else return(u_8859_9.map[c - u_8859_9.offset]); } USHORT #ifdef CK_ANSIC iso_8859_10_u(CHAR c) #else iso_8859_10_u(c) CHAR c; #endif /* CK_ANSIC */ { if (c >= 0x80 && c < 0xa0) return(c); c &= 0x7f; if (c < u_8859_10.offset) return(c); else if (c >= u_8859_10.offset + u_8859_10.size) return(c); else return(u_8859_10.map[c - u_8859_10.offset]); } USHORT #ifdef CK_ANSIC iso_8859_15_u(CHAR c) #else iso_8859_15_u(c) CHAR c; #endif /* CK_ANSIC */ { if (c >= 0x80 && c < 0xa0) return(c); c &= 0x7f; if (c < u_8859_15.offset) return(c); else if (c >= u_8859_15.offset + u_8859_15.size) return(c); else return(u_8859_15.map[c - u_8859_15.offset]); } USHORT #ifdef CK_ANSIC apl2_u(CHAR c) #else apl2_u(c) CHAR c; #endif /* CK_ANSIC */ { if (c >= 0x80 && c < 0xa0) return(c); c &= 0x7f; if (c < u_apl2.offset) return(c); else if (c >= u_apl2.offset + u_apl2.size) return(c); else return(u_apl2.map[c - u_apl2.offset]); } USHORT #ifdef CK_ANSIC apl3_u(CHAR c) #else apl3_u(c) CHAR c; #endif /* CK_ANSIC */ { c &= 0x7f; if (c < u_apl3.offset) return(c); else if (c >= u_apl3.offset + u_apl3.size) return(c); else return(u_apl3.map[c - u_apl3.offset]); } USHORT #ifdef CK_ANSIC apl4_u(CHAR c) #else apl4_u(c) CHAR c; #endif /* CK_ANSIC */ { c &= 0x7f; if (c < u_apl4.offset) return(c); else if (c >= u_apl4.offset + u_apl4.size) return(c); else return(u_apl4.map[c - u_apl4.offset]); } USHORT #ifdef CK_ANSIC apl5_u(CHAR c) #else apl5_u(c) CHAR c; #endif /* CK_ANSIC */ { c &= 0x7f; if (c < u_apl5.offset) return(c); else if (c >= u_apl5.offset + u_apl5.size) return(c); else return(u_apl5.map[c - u_apl5.offset]); } USHORT #ifdef CK_ANSIC koi7_u(CHAR c) #else koi7_u(c) CHAR c; #endif /* CK_ANSIC */ { if (c & 0x80) return(UNK); if (c < u_koi7.offset) return(c); else if (c >= u_koi7.offset + u_koi7.size) return(c); else return(u_koi7.map[c - u_koi7.offset]); } USHORT #ifdef CK_ANSIC decmcs_u(CHAR c) #else decmcs_u(c) CHAR c; #endif /* CK_ANSIC */ { if (c >= 0x80 && c < 0xa0) return(c); c &= 0x7f; if (c < u_decmcs.offset) return(c); else if (c >= u_decmcs.offset + u_decmcs.size) return(c); else return(u_decmcs.map[c - u_decmcs.offset]); } USHORT #ifdef CK_ANSIC nextstep_u(CHAR c) #else nextstep_u(c) CHAR c; #endif /* CK_ANSIC */ { return(u_nextstep.map[(c & 0x7f) - u_nextstep.offset]); } USHORT #ifdef CK_ANSIC dgi_u(CHAR c) #else dgi_u(c) CHAR c; #endif /* CK_ANSIC */ { if (c >= 0x80 && c < 0xa0) return(c); return(u_dgi.map[(c & 0x7f) - u_dgi.offset]); } USHORT #ifdef CK_ANSIC hproman8_u(CHAR c) #else hproman8_u(c) CHAR c; #endif /* CK_ANSIC */ { if (c >= 0x80 && c < 0xa0) return(c); return(u_hproman8.map[(c & 0x7f) - u_hproman8.offset]); } USHORT #ifdef CK_ANSIC cp37_u(CHAR c) #else cp37_u(c) CHAR c; #endif /* CK_ANSIC */ { return(u_cp37.map[c & 0x7f]); } USHORT #ifdef CK_ANSIC cp437_u(CHAR c) #else cp437_u(c) CHAR c; #endif /* CK_ANSIC */ { return(u_cp437.map[c & 0x7f]); } USHORT #ifdef CK_ANSIC mazovia_u(CHAR c) /* Mazovia = CP437 with substitutions */ #else mazovia_u(c) CHAR c; #endif /* CK_ANSIC */ { return(u_mazovia.map[c & 0x7f]); } USHORT #ifdef CK_ANSIC cp850_u(CHAR c) #else cp850_u(c) CHAR c; #endif /* CK_ANSIC */ { return(u_cp850.map[c & 0x7f]); } USHORT #ifdef CK_ANSIC cp858_u(CHAR c) #else cp858_u(c) CHAR c; #endif /* CK_ANSIC */ { return(u_cp858.map[c & 0x7f]); } USHORT #ifdef CK_ANSIC cp1250_u(CHAR c) #else cp1250_u(c) CHAR c; #endif /* CK_ANSIC */ { return(u_cp1250.map[c & 0x7f]); } USHORT #ifdef CK_ANSIC cp1251_u(CHAR c) #else cp1251_u(c) CHAR c; #endif /* CK_ANSIC */ { return(u_cp1251.map[c & 0x7f]); } USHORT #ifdef CK_ANSIC cp1252_u(CHAR c) #else cp1252_u(c) CHAR c; #endif /* CK_ANSIC */ { return(u_cp1252.map[c & 0x7f]); } USHORT #ifdef CK_ANSIC cp1253_u(CHAR c) #else cp1253_u(c) CHAR c; #endif /* CK_ANSIC */ { return(u_cp1253.map[c & 0x7f]); } USHORT #ifdef CK_ANSIC cp1254_u(CHAR c) #else cp1254_u(c) CHAR c; #endif /* CK_ANSIC */ { return(u_cp1254.map[c & 0x7f]); } USHORT #ifdef CK_ANSIC cp1255_u(CHAR c) #else cp1255_u(c) CHAR c; #endif /* CK_ANSIC */ { return(u_cp1255.map[c & 0x7f]); } USHORT #ifdef CK_ANSIC cp1256_u(CHAR c) #else cp1256_u(c) CHAR c; #endif /* CK_ANSIC */ { return(u_cp1256.map[c & 0x7f]); } USHORT #ifdef CK_ANSIC cp1257_u(CHAR c) #else cp1257_u(c) CHAR c; #endif /* CK_ANSIC */ { return(u_cp1257.map[c & 0x7f]); } USHORT #ifdef CK_ANSIC cp1258_u(CHAR c) #else cp1258_u(c) CHAR c; #endif /* CK_ANSIC */ { return(u_cp1258.map[c & 0x7f]); } USHORT #ifdef CK_ANSIC cp852_u(CHAR c) #else cp852_u(c) CHAR c; #endif /* CK_ANSIC */ { return(u_cp852.map[c & 0x7f]); } USHORT /* Cyrillic */ #ifdef CK_ANSIC cp855_u(CHAR c) #else cp855_u(c) CHAR c; #endif /* CK_ANSIC */ { return(u_cp855.map[c & 0x7f]); } USHORT /* Bulgaria */ #ifdef CK_ANSIC cp856_u(CHAR c) #else cp856_u(c) CHAR c; #endif /* CK_ANSIC */ { return(u_cp856.map[c & 0x7f]); } USHORT #ifdef CK_ANSIC cp857_u(CHAR c) #else cp857_u(c) CHAR c; #endif /* CK_ANSIC */ { return(u_cp857.map[c & 0x7f]); } USHORT #ifdef CK_ANSIC cp862_u(CHAR c) #else cp862_u(c) CHAR c; #endif /* CK_ANSIC */ { return(u_cp862.map[c & 0x7f]); } USHORT #ifdef CK_ANSIC cp864_u(CHAR c) #else cp864_u(c) CHAR c; #endif /* CK_ANSIC */ { return(u_cp864.map[c & 0x7f]); } USHORT #ifdef CK_ANSIC cp866_u(CHAR c) #else cp866_u(c) CHAR c; #endif /* CK_ANSIC */ { return(u_cp866.map[c & 0x7f]); } USHORT #ifdef CK_ANSIC cp869_u(CHAR c) #else cp869_u(c) CHAR c; #endif /* CK_ANSIC */ { return(u_cp869.map[c & 0x7f]); } USHORT #ifdef CK_ANSIC maclatin_u(CHAR c) #else maclatin_u(c) CHAR c; #endif /* CK_ANSIC */ { return(u_maclatin.map[c & 0x7f]); } USHORT #ifdef CK_ANSIC quickdraw_u(CHAR c) #else quickdraw_u(c) CHAR c; #endif /* CK_ANSIC */ { return(u_quickdraw.map[c & 0x7f]); } USHORT #ifdef CK_ANSIC decspec_u(CHAR c) #else decspec_u(c) CHAR c; #endif /* CK_ANSIC */ { c &= 0x7f; if (c < u_decspec.offset) return(c); else if (c >= u_decspec.offset + u_decspec.size) return(c); else return (u_decspec.map[c - u_decspec.offset]); } USHORT #ifdef CK_ANSIC dectech_u(CHAR c) #else dectech_u(c) CHAR c; #endif /* CK_ANSIC */ { c &= 0x7f; if (c < u_dectech.offset) return(c); else if (c >= u_dectech.offset + u_dectech.size) return(c); else return(u_dectech.map[c - u_dectech.offset]); } USHORT #ifdef CK_ANSIC dgspec_u(CHAR c) #else dgspec_u(c) CHAR c; #endif /* CK_ANSIC */ { c &= 0x7f; if (c < u_dgspec.offset) return(c); else if (c >= u_dgspec.offset + u_dgspec.size) return(c); else return(u_dgspec.map[c - u_dgspec.offset]); } USHORT #ifdef CK_ANSIC dgline_u(CHAR c) #else dgline_u(c) CHAR c; #endif /* CK_ANSIC */ { c &= 0x7f; if (c < u_dgline.offset) return(c); else if (c >= u_dgline.offset + u_dgline.size) return(c); else return(u_dgline.map[c - u_dgline.offset]); } USHORT #ifdef CK_ANSIC dgword_u(CHAR c) #else dgword_u(c) CHAR c; #endif /* CK_ANSIC */ { c &= 0x7f; if (c < u_dgword.offset) return(c); else if (c >= u_dgword.offset + u_dgword.size) return(c); else return(u_dgword.map[c - u_dgword.offset]); } USHORT #ifdef CK_ANSIC hpline_u(CHAR c) #else hpline_u(c) CHAR c; #endif /* CK_ANSIC */ { c &= 0x7f; if (c < u_hpline.offset) return(c); else if (c >= u_hpline.offset + u_hpline.size) return(c); else return(u_hpline.map[c - u_hpline.offset]); } USHORT #ifdef CK_ANSIC hpmath_u(CHAR c) #else hpmath_u(c) CHAR c; #endif /* CK_ANSIC */ { c &= 0x7f; if (c < u_hpmath.offset) return(c); else if (c >= u_hpmath.offset + u_hpmath.size) return(c); else return(u_hpmath.map[c - u_hpmath.offset]); } USHORT #ifdef CK_ANSIC qnxgrph_u(CHAR c) #else qnxgrph_u(c) CHAR c; #endif /* CK_ANSIC */ { c &= 0x7f; if (c < u_qnxgrph.offset) return(c); else if (c >= u_qnxgrph.offset + u_qnxgrph.size) return(c); else return(u_qnxgrph.map[c - u_qnxgrph.offset]); } USHORT #ifdef CK_ANSIC hz1500_u(CHAR c) #else hz1500_u(c) CHAR c; #endif /* CK_ANSIC */ { c &= 0x7f; if (c < u_hz1500.offset) return(c); else if (c >= u_hz1500.offset + u_hz1500.size) return(c); else return(u_hz1500.map[c - u_hz1500.offset]); } USHORT #ifdef CK_ANSIC sniblanks_u(CHAR c) #else sniblanks_u(c) CHAR c; #endif /* CK_ANSIC */ { c &= 0x7f; if (c < u_sniblanks.offset) return(c); else if (c >= u_sniblanks.offset + u_sniblanks.size) return(c); else return(u_sniblanks.map[c - u_sniblanks.offset]); } USHORT #ifdef CK_ANSIC snibrack_u(CHAR c) #else snibrack_u(c) CHAR c; #endif /* CK_ANSIC */ { c &= 0x7f; if (c < u_snibrack.offset) return(c); else if (c >= u_snibrack.offset + u_snibrack.size) return(c); else return(u_snibrack.map[c - u_snibrack.offset]); } USHORT #ifdef CK_ANSIC snifacet_u(CHAR c) #else snifacet_u(c) CHAR c; #endif /* CK_ANSIC */ { c &= 0x7f; if (c < u_snifacet.offset) return(c); else if (c >= u_snifacet.offset + u_snifacet.size) return(c); else return(u_snifacet.map[c - u_snifacet.offset]); } USHORT #ifdef CK_ANSIC sniibm_u(CHAR c) #else sniibm_u(c) CHAR c; #endif /* CK_ANSIC */ { c &= 0x7f; if (c < u_sniibm.offset) return(c); else if (c >= u_sniibm.offset + u_sniibm.size) return(c); else return(u_sniibm.map[c - u_sniibm.offset]); } USHORT #ifdef CK_ANSIC snieuro_u(CHAR c) #else snieuro_u(c) CHAR c; #endif /* CK_ANSIC */ { c &= 0x7f; if (c < u_snieuro.offset) return(c); else if (c >= u_snieuro.offset + u_snieuro.size) return(c); else return(u_snieuro.map[c - u_snieuro.offset]); } USHORT #ifdef CK_ANSIC heath19g_u(CHAR c) #else heath19g_u(c) CHAR c; #endif /* CK_ANSIC */ { c &= 0x7f; if (c < u_heath19g.offset) return(c); else if (c >= u_heath19g.offset + u_heath19g.size) return(c); else return(u_heath19g.map[c - u_heath19g.offset]); } USHORT #ifdef CK_ANSIC tvig_u(CHAR c) #else tvig_u(c) CHAR c; #endif /* CK_ANSIC */ { c &= 0x7f; if (c < u_tvig.offset) return(c); else if (c >= u_tvig.offset + u_tvig.size) return(c); else return(u_tvig.map[c - u_tvig.offset]); } USHORT #ifdef CK_ANSIC wyse_gn_u(CHAR c) #else wyse_gn_u(c) CHAR c; #endif /* CK_ANSIC */ { c &= 0x7f; if (c < u_wyse_gn.offset) return(c); else if (c >= u_wyse_gn.offset + u_wyse_gn.size) return(c); else return(u_wyse_gn.map[c - u_wyse_gn.offset]); } USHORT #ifdef CK_ANSIC wyse_g1_u(CHAR c) #else wyse_g1_u(c) CHAR c; #endif /* CK_ANSIC */ { c &= 0x7f; if (c < u_wyse_g1.offset) return(c); else if (c >= u_wyse_g1.offset + u_wyse_g1.size) return(c); else return(u_wyse_g1.map[c - u_wyse_g1.offset]); } USHORT #ifdef CK_ANSIC wyse_g2_u(CHAR c) #else wyse_g2_u(c) CHAR c; #endif /* CK_ANSIC */ { c &= 0x7f; if (c < u_wyse_g2.offset) return(c); else if (c >= u_wyse_g2.offset + u_wyse_g2.size) return(c); else return(u_wyse_g2.map[c - u_wyse_g2.offset]); } USHORT #ifdef CK_ANSIC wyse_g3_u(CHAR c) #else wyse_g3_u(c) CHAR c; #endif /* CK_ANSIC */ { c &= 0x7f; if (c < u_wyse_g3.offset) return(c); else if (c >= u_wyse_g3.offset + u_wyse_g3.size) return(c); else return(u_wyse_g3.map[c - u_wyse_g3.offset]); } USHORT #ifdef CK_ANSIC smiley_u(CHAR c) #else smiley_u(c) CHAR c; #endif /* CK_ANSIC */ { c &= 0x7f; if (c < u_smiley.offset) return(c); else if (c >= u_smiley.offset + u_smiley.size) return(c); else return(u_smiley.map[c - u_smiley.offset]); } USHORT #ifdef CK_ANSIC c0pics_u(CHAR c) #else c0pics_u(c) CHAR c; #endif /* CK_ANSIC */ { c &= 0x7f; if (c < u_c0pics.offset) return(c); else if (c >= u_c0pics.offset + u_c0pics.size) return(c); else return(u_c0pics.map[c - u_c0pics.offset]); } USHORT #ifdef CK_ANSIC c1pics_u(CHAR c) #else c1pics_u(c) CHAR c; #endif /* CK_ANSIC */ { c &= 0x7f; if (c < u_c1pics.offset) return(c); else if (c >= u_c1pics.offset + u_c1pics.size) return(c); else return(u_c1pics.map[c - u_c1pics.offset]); } #ifdef KANJI /* Kanji/Unicode functions */ static long /* Statistics counters */ bad = 0, /* REMOVE THESE LATER... */ kanji = 0, kana = 0, greek = 0, cyrillic = 0, special = 0, roman = 0; USHORT #ifdef CK_ANSIC sj_to_un(USHORT sj) /* Shift-JIS to Unicode */ #else sj_to_un(sj) USHORT sj; #endif /* CK_ANSIC */ { /* Kanji blocks */ if (sj >= 0x8140) { /* All possible Kanjis */ kanji++; /* Optimistically count a Kanji */ if (sj <= 0x9ffc) { /* 7869-element table */ return(sju_8140[sj - 0x8140]); } else if (sj >= 0xe040 && sj <= 0xeaa4) { /* 2660-element table */ return(sju_e040[sj - 0xe040]); } else if (sj >= 0xf040) { /* User-defined areas */ if (sj <= 0xf0fc) { /* ten 189-char chunks */ return(0xe000 + (sj - 0xf040)); } else if (sj >= 0xf140 && sj <= 0xf1fc) { return(0xe0bc + (sj - 0xf140)); } else if (sj >= 0xf240 && sj <= 0xf2fc) { return(0xe178 + (sj - 0xf240)); } else if (sj >= 0xf340 && sj <= 0xf3fc) { return(0xe234 + (sj - 0xf340)); } else if (sj >= 0xf440 && sj <= 0xf4fc) { return(0xe2f0 + (sj - 0xf440)); } else if (sj >= 0xf540 && sj <= 0xf5fc) { return(0xe3ac + (sj - 0xf540)); } else if (sj >= 0xf640 && sj <= 0xf6fc) { return(0xe468 + (sj - 0xf640)); } else if (sj >= 0xf740 && sj <= 0xf7fc) { return(0xe524 + (sj - 0xf740)); } else if (sj >= 0xf840 && sj <= 0xf8fc) { return(0xe5e0 + (sj - 0xf840)); } else if (sj >= 0xf940 && sj <= 0xf9fc) { return(0xe69c + (sj - 0xf940)); } } kanji--; /* None of the above, uncount */ } /* C0 / Halfwidth-Roman / C1 block (0x00-0x9f, no holes) */ else if (sj < 0x00a0) { roman++; /* Count a Roman */ if (sj == 0x5c) { /* Yen sign */ return(0x00a5); } else if (sj == 0x7e) { /* Overline (macron) */ return(0x203e); } else { /* Control or Halfwidth Roman */ return(sj); } } /* Halfwidth Katakana block (0xa0-0xdf, no holes) */ else if (sj >= 0xa1 && sj <= 0xdf) { kana++; return(sj + 0xfec0); } /* Catch-all must be final */ bad++; return(0xfffd); } USHORT #ifdef CK_ANSIC un_to_sj(USHORT un) /* Unicode to Shift-JIS */ #else un_to_sj(un) USHORT un; #endif /* CK_ANSIC */ { if (un < 0x00a0) { switch (un) { case 0x005c: roman++; return(0x815f); /* Backslash */ case 0x007e: bad++; return(0xfffd); /* No tilde in Shift-JIS */ default: /* ASCII or C0/C1 control */ roman++; return(un); } } if (un >= 0x00a0 && un < 0x0391) { /* Latin-1 symbols */ roman++; switch(un) { case 0x00A2: return(0x8191); case 0x00A3: return(0x8192); case 0x00A5: return(0x005C); /* Yen */ case 0x00A7: return(0x8198); case 0x00A8: return(0x814E); case 0x00AC: return(0x81CA); case 0x00B0: return(0x818B); case 0x00B1: return(0x817D); case 0x00B4: return(0x814C); case 0x00B6: return(0x81F7); case 0x00D7: return(0x817E); case 0x00F7: return(0x8180); default: roman--; bad++; return(0xfffd); } } if (un >= 0x0391 && un < 0x0401) { /* Greek */ greek++; if (un <= 0x039c) return(usj_0391[un-0x0391]); greek--; bad++; return(0xfffd); } if (un >= 0x0401 && un < 0x2010) { /* Cyrillic */ cyrillic++; if (un <= 0x0451) return(usj_0401[un-0x0401]); cyrillic--; bad++; return(0xfffd); } if (un >= 0x2010 && un < 0x2500) { /* General punctuation */ special++; switch(un) { case 0x2010: return(0x815D); case 0x2015: return(0x815C); case 0x2016: return(0x8161); case 0x2018: return(0x8165); case 0x2019: return(0x8166); case 0x201C: return(0x8167); case 0x201D: return(0x8168); case 0x2020: return(0x81F5); case 0x2021: return(0x81F6); case 0x2025: return(0x8164); case 0x2026: return(0x8163); case 0x2030: return(0x81F1); case 0x2032: return(0x818C); case 0x2033: return(0x818D); case 0x203B: return(0x81A6); case 0x203E: return(0x007E); case 0x2103: return(0x818E); /* Letterlike symbols */ case 0x212B: return(0x81F0); case 0x2190: return(0x81A9); /* Arrows */ case 0x2191: return(0x81AA); case 0x2192: return(0x81A8); case 0x2193: return(0x81AB); case 0x21D2: return(0x81CB); case 0x21D4: return(0x81CC); case 0x2200: return(0x81CD); /* Math */ case 0x2202: return(0x81DD); case 0x2203: return(0x81CE); case 0x2207: return(0x81DE); case 0x2208: return(0x81B8); case 0x220B: return(0x81B9); case 0x2212: return(0x817C); case 0x221A: return(0x81E3); case 0x221D: return(0x81E5); case 0x221E: return(0x8187); case 0x2220: return(0x81DA); case 0x2227: return(0x81C8); case 0x2228: return(0x81C9); case 0x2229: return(0x81BF); case 0x222A: return(0x81BE); case 0x222B: return(0x81E7); case 0x222C: return(0x81E8); case 0x2234: return(0x8188); case 0x2235: return(0x81E6); case 0x223D: return(0x81E4); case 0x2252: return(0x81E0); case 0x2260: return(0x8182); case 0x2261: return(0x81DF); case 0x2266: return(0x8185); case 0x2267: return(0x8186); case 0x226A: return(0x81E1); case 0x226B: return(0x81E2); case 0x2282: return(0x81BC); case 0x2283: return(0x81BD); case 0x2286: return(0x81BA); case 0x2287: return(0x81BB); case 0x22A5: return(0x81DB); case 0x2312: return(0x81DC); /* Arc */ default: special--; bad++; return(0xfffd); } } if (un >= 0x2500 && un < 0x3000) { /* Box drawing */ special++; switch(un) { case 0x2500: return(0x849F); case 0x2501: return(0x84AA); case 0x2502: return(0x84A0); case 0x2503: return(0x84AB); case 0x250C: return(0x84A1); case 0x250F: return(0x84AC); case 0x2510: return(0x84A2); case 0x2513: return(0x84AD); case 0x2514: return(0x84A4); case 0x2517: return(0x84AF); case 0x2518: return(0x84A3); case 0x251B: return(0x84AE); case 0x251C: return(0x84A5); case 0x251D: return(0x84BA); case 0x2520: return(0x84B5); case 0x2523: return(0x84B0); case 0x2524: return(0x84A7); case 0x2525: return(0x84BC); case 0x2528: return(0x84B7); case 0x252B: return(0x84B2); case 0x252C: return(0x84A6); case 0x252F: return(0x84B6); case 0x2530: return(0x84BB); case 0x2533: return(0x84B1); case 0x2534: return(0x84A8); case 0x2537: return(0x84B8); case 0x2538: return(0x84BD); case 0x253B: return(0x84B3); case 0x253C: return(0x84A9); case 0x253F: return(0x84B9); case 0x2542: return(0x84BE); case 0x254B: return(0x84B4); case 0x25A0: return(0x81A1); /* Geometric shapes */ case 0x25A1: return(0x81A0); case 0x25B2: return(0x81A3); case 0x25B3: return(0x81A2); case 0x25BC: return(0x81A5); case 0x25BD: return(0x81A4); case 0x25C6: return(0x819F); case 0x25C7: return(0x819E); case 0x25CB: return(0x819B); case 0x25CE: return(0x819D); case 0x25CF: return(0x819C); case 0x25EF: return(0x81FC); case 0x2605: return(0x819A); /* Misc symbols */ case 0x2606: return(0x8199); case 0x2640: return(0x818A); case 0x2642: return(0x8189); case 0x266A: return(0x81F4); case 0x266D: return(0x81F3); case 0x266F: return(0x81F2); default: special--; bad++; return(0xfffd); } } if (un >= 0x3000 && un < 0x4e00) { /* CJK symbols & punc */ kanji++; if (un <= 0x30ff) return(usj_3000[un-0x3000]); kanji--; bad++; return(0xfffd); } if (un >= 0xff00 && un < 0xffff) { /* Half/full-width Roman & Katakana */ if (un <= 0xff9f) { if (un > 0xff60) kana++; return(usj_ff00[un-0xff00]); } bad++; return(0xfffd); } if (un >= 0x4e00 && un < 0xe000) { /* Kanji */ kanji++; if (un <= 0x9fa0) return(usj_4e00[un-0x4e00]); kanji--; bad++; return(0xfffd); } if (un >= 0xe000 && un < 0xff00) { /* User-defined (Gaiji) */ kanji++; if (un <= 0xe0bb) { /* ten 189-char chunks */ return(0xf040 + (un - 0xe000)); } else if (un >= 0xe0bc && un <= 0xe177) { return(0xf140 + (un - 0xe0bc)); } else if (un >= 0xe178 && un <= 0xe233) { return(0xf240 + (un - 0xe178)); } else if (un >= 0xe234 && un <= 0xe2ef) { return(0xf340 + (un - 0xe234)); } else if (un >= 0xe2f0 && un <= 0xe3ab) { return(0xf440 + (un - 0xe2f0)); } else if (un >= 0xe3ac && un <= 0xe467) { return(0xf540 + (un - 0xe3ac)); } else if (un >= 0xe468 && un <= 0xe523) { return(0xf640 + (un - 0xe468)); } else if (un >= 0xe524 && un <= 0xe5df) { return(0xf740 + (un - 0xe524)); } else if (un >= 0xe5e0 && un <= 0xe69b) { return(0xf840 + (un - 0xe5e0)); } else if (un >= 0xe69c && un <= 0xe757) { return(0xf940 + (un - 0xe69c)); } bad++; return(0xfffd); } /* NOTREACHED */ /* Some compilers (correctly) warn of "statement not reached" here. */ /* But others give up the ghost with "no return value". The former */ /* is the lesser of two evils. */ bad++; return(0xfffd); } #endif /* KANJI */ /* Unicode-to-blah functions, tx_blah(). */ static int #ifdef CK_ANSIC tx_punc(USHORT c) #else tx_punc(c) USHORT c; #endif /* CK_ANSIC */ { if (c >= 0x2000 && c <= 0x200a) /* Various-width spaces */ return((CHAR)(0x20)); else if (c >= 0x2010 && c <= 0x2015) /* Various-width dashes */ return((CHAR)'-'); else if (c >= 0x2018 && c <= 0x201b) /* Assorted single quotes */ return((CHAR)0x27); else if (c >= 0x201c && c <= 0x201f) /* Assorted double quotes */ return((CHAR)0x22); else if ((c >= 0x2022 && c <= 0x2024) || c == 0x2043) /* Bullets */ return((CHAR)0xb7); switch (c) { case 0x2039: /* Less-than sign */ return((CHAR)0x3c); case 0x203a: /* Greater-than sign */ return((CHAR)0x3e); case 0x2044: /* Solidus -> Slash */ return((CHAR)0x2f); default: return(-1); } } int /* For Latin-1 */ #ifdef CK_ANSIC tx_ident(USHORT c) #else tx_ident(c) USHORT c; #endif /* CK_ANSIC */ { if (c == 0x203e) /* Overline -> Macron */ return((CHAR)0xaf); else if (c < 0x100) /* Latin-1 range */ return((CHAR)(c & 0xff)); else /* Or maybe from punctuation block */ return(tx_punc(c)); } int #ifdef CK_ANSIC tx_usascii(USHORT c) #else tx_usascii(c) USHORT c; /* US ASCII */ #endif /* CK_ANSIC */ { if (c < 0x80) return((CHAR)(c & 0xff)); else if (c >= 0x2000 && c <= 0x200a) /* Various-width spaces */ return((CHAR)(0x20)); else if (c >= 0x2010 && c <= 0x2015) /* Various-width dashes */ return((CHAR)'-'); else if (c >= 0x2018 && c <= 0x201b) /* Assorted single quotes */ return((CHAR)0x27); else if (c >= 0x201c && c <= 0x201f) /* Assorted double quotes */ return((CHAR)0x22); else if ((c >= 0x2022 && c <= 0x2024) || c == 0x2043) /* Bullets */ return((CHAR)0xb7); switch (c) { case 0x2039: /* Less-than sign */ return((CHAR)0x3c); case 0x203a: /* Greater-than sign */ return((CHAR)0x3e); case 0x2044: /* Solidus -> Slash */ return((CHAR)0x2f); } /* Here we might also (a) map accented Roman letters to unaccented ones; (b) map Greek/Cyrillic A (etc) to Roman, and so on. */ return((c & 0xff80) ? -1 : (CHAR)(c & 0x7f)); } int #ifdef CK_ANSIC tx_british(USHORT c) #else tx_british(c) USHORT c; /* British */ #endif /* CK_ANSIC */ { if (c & 0xff00) return(-1); else if (c == (USHORT) 0x00a3) /* Pound sign */ return(0x2b); else return(tx_usascii(c)); } int #ifdef CK_ANSIC tx_apl1(USHORT c) #else tx_apl1(c) USHORT c; /* Apl1 */ #endif /* CK_ANSIC */ { if (c >= 0x0041 && c <= 0x005a) /* Letters */ return(c + 0x20); switch (c) { /* Others */ case 0x0024: return((CHAR)0x7e); case 0x0027: return((CHAR)0x4b); case 0x0028: return((CHAR)0x3a); case 0x0029: return((CHAR)0x22); case 0x002b: return((CHAR)0x2d); case 0x002c: return((CHAR)0x2c); case 0x002d: return((CHAR)0x5f); case 0x002e: return((CHAR)0x2e); case 0x002f: return((CHAR)0x2f); case 0x0030: return((CHAR)0x30); case 0x0031: return((CHAR)0x31); case 0x0032: return((CHAR)0x32); case 0x0033: return((CHAR)0x33); case 0x0034: return((CHAR)0x34); case 0x0035: return((CHAR)0x35); case 0x0036: return((CHAR)0x36); case 0x0037: return((CHAR)0x37); case 0x0038: return((CHAR)0x38); case 0x0039: return((CHAR)0x39); case 0x003a: return((CHAR)0x3e); case 0x003b: return((CHAR)0x3c); case 0x003c: return((CHAR)0x23); case 0x003d: return((CHAR)0x25); case 0x003e: return((CHAR)0x26); case 0x003f: return((CHAR)0x51); case 0x005b: return((CHAR)0x3b); case 0x005c: return((CHAR)0x3f); case 0x005d: return((CHAR)0x27); case 0x005f: return((CHAR)0x46); case 0x007b: return((CHAR)0x7b); case 0x007c: return((CHAR)0x4d); case 0x007d: return((CHAR)0x7d); case 0x00a8: return((CHAR)0x21); case 0x00af: return((CHAR)0x40); case 0x00d7: return((CHAR)0x3d); case 0x00f7: return((CHAR)0x2b); case 0x2190: return((CHAR)0x5b); case 0x2191: return((CHAR)0x59); case 0x2192: return((CHAR)0x5d); case 0x2193: return((CHAR)0x55); case 0x2206: return((CHAR)0x48); case 0x2207: return((CHAR)0x47); case 0x220a: return((CHAR)0x45); case 0x2218: return((CHAR)0x4a); case 0x2227: return((CHAR)0x29); case 0x2228: return((CHAR)0x28); case 0x222a: return((CHAR)0x56); case 0x223c: return((CHAR)0x54); case 0x2260: return((CHAR)0x2a); case 0x2264: return((CHAR)0x24); case 0x2265: return((CHAR)0x5e); case 0x2282: return((CHAR)0x5a); case 0x2283: return((CHAR)0x58); case 0x22a2: return((CHAR)0x5c); case 0x22a3: return((CHAR)0x7c); case 0x22a4: return((CHAR)0x4e); case 0x22a5: return((CHAR)0x42); case 0x22c2: return((CHAR)0x43); case 0x22c4: return((CHAR)0x60); case 0x22c6: return((CHAR)0x50); case 0x2308: return((CHAR)0x53); case 0x230a: return((CHAR)0x44); case 0x2373: return((CHAR)0x49); case 0x2374: return((CHAR)0x52); case 0x2375: return((CHAR)0x57); case 0x237a: return((CHAR)0x41); case 0x25af: return((CHAR)0x4c); case 0x25cb: return((CHAR)0x4f); default: return(tx_usascii(c)); } } int /* Canadian French */ #ifdef CK_ANSIC tx_fr_canadian(USHORT c) #else tx_fr_canadian(c) USHORT c; #endif /* CK_ANSIC */ { if (c & 0xff00) /* Out of range */ return(-1); switch(c) { case 0xe0: return(0x40); /* a grave */ case 0xe2: return(0x5b); /* a circumflex */ case 0xe7: return(0x5c); /* c cedilla */ case 0xe8: return(0x7d); /* e grave */ case 0xe9: return(0x7b); /* e acute */ case 0xea: return(0x5d); /* e circumflex */ case 0xee: return(0x5e); /* i circumflex */ case 0xf4: return(0x60); /* o circumflex */ case 0xf9: return(0x7c); /* u grave */ case 0xfb: return(0x6e); /* u circumflex */ default: return(tx_usascii(c)); } } int /* Danish/Norwegian */ #ifdef CK_ANSIC tx_danish(USHORT c) #else tx_danish(c) USHORT c; #endif /* CK_ANSIC */ { if (c & 0xff00) /* Out of range */ return(-1); switch(c) { case 0xc6: return(0x5b); /* AE */ case 0xd8: return(0x5c); /* O stroke */ case 0xe6: return(0x7b); /* ae */ case 0xf8: return(0x7c); /* o stroke */ case 0xe5: return(0x7d); /* a ring */ case 0xaf: return(0x7e); /* macron */ default: return(tx_usascii(c)); } } int /* Dutch */ #ifdef CK_ANSIC tx_dutch(USHORT c) #else tx_dutch(c) USHORT c; #endif /* CK_ANSIC */ { if (c & 0xfe00) /* Out of range */ return(-1); switch(c) { case 0x007c: return(0x5d); /* vertical bar */ case 0x00a3: return(0x23); /* pound sign */ case 0x00ab: return(0x7b); /* diaeresis */ case 0x00b4: return(0x7e); /* acute accent */ case 0x00bc: return(0x7d); /* 1/4 */ case 0x00be: return(0x40); /* 3/4 */ case 0x00bd: return(0x5c); /* 1/2 */ case 0x00ff: return(0x5b); /* y diaeresis (ij) */ case 0x0192: return(0x7c); /* Florin */ default: return((c & 0x80) ? -1 : (CHAR)(c & 0x7f)); } } int /* Finnish */ #ifdef CK_ANSIC tx_finnish(USHORT c) #else tx_finnish(c) USHORT c; #endif /* CK_ANSIC */ { if (c & 0xff00) /* Out of range */ return(-1); switch(c) { case 0xc4: return(0x5b); /* A diaeresis */ case 0xd6: return(0x5c); /* O diaeresis */ case 0xc5: return(0x5d); /* A ring */ case 0xdc: return(0x5e); /* U diaeresis */ case 0xe9: return(0x60); /* e acute */ case 0xe4: return(0x7b); /* a diaeresis */ case 0xf6: return(0x7c); /* o diaeresis */ case 0xe5: return(0x7d); /* a ring */ case 0xfc: return(0x7e); /* u diaeresis */ default: return(tx_usascii(c)); } } int /* French */ #ifdef CK_ANSIC tx_french(USHORT c) #else tx_french(c) USHORT c; #endif /* CK_ANSIC */ { if (c & 0xff00) /* Out of range */ return(-1); switch(c) { case 0xa3: return(0x23); /* pound sign */ case 0xa7: return(0x5d); /* section sign */ case 0xa8: return(0x7e); /* diaeresis */ case 0xb0: return(0x5b); /* ring */ case 0xb5: return(0x60); /* micron sign (mu) */ case 0xe0: return(0x40); /* a grave */ case 0xe7: return(0x5c); /* c cedilla */ case 0xe8: return(0x7d); /* e grave */ case 0xe9: return(0x7b); /* e acute */ case 0xf9: return(0x7c); /* u grave */ default: return(tx_usascii(c)); } } int /* German */ #ifdef CK_ANSIC tx_german(USHORT c) #else tx_german(c) USHORT c; #endif /* CK_ANSIC */ { if (c & 0xff00) /* Out of range */ return(-1); switch(c) { case 0xa7: return(0x40); /* section sign */ case 0xc4: return(0x5b); /* A umlaut */ case 0xd6: return(0x5c); /* O umlaut */ case 0xdc: return(0x5d); /* U umlaut */ case 0xdf: return(0x7e); /* ess-zet */ case 0xe4: return(0x7b); /* a umlaut */ case 0xf6: return(0x7c); /* o umlaut */ case 0xfc: return(0x7d); /* u umlaut*/ default: return(tx_usascii(c)); } } int /* Hebrew-7 */ #ifdef CK_ANSIC tx_hebrew7(USHORT c) #else tx_hebrew7(c) USHORT c; #endif /* CK_ANSIC */ { if (c < 0x60) /* ASCII */ return((CHAR)(c & 0x7f)); else if (c >= 123 && c < 128) /* ASCII */ return((CHAR)(c & 0x7f)); else if (c >= 0x05d0 && c <= 0x05ea) /* Hebrew 27 contiguous characters */ return((CHAR)((int)c - 0x5d0 + 96)); else return(-1); } int /* Greek ELOT 927 */ #ifdef CK_ANSIC tx_elot927(USHORT c) #else tx_elot927(c) USHORT c; #endif /* CK_ANSIC */ { if (c <= 0x80) { /* ASCII */ if (islower(c)) c = toupper(c); /* Send all letters in uppercase */ return((CHAR)(c & 0x7f)); } /* Greek -- map all Greek characters to unaccented uppercase */ if (c >= 0x0391 && c <= 0x03a1) /* Alpha thru Rho - uppercase */ return((CHAR)((int)c - 0x0391 + 97)); else if (c >= 0x03a3 && c <= 0x03a9) /* Sigma thru Omega - uppercase */ return((CHAR)((int)c - 0x0391 + 96)); else if (c >= 0x03b1 && c <= 0x03c1) /* Alpha thru Rho - lowercase */ return((CHAR)((int)c - 0x0391 + 97)); else if (c >= 0x03c3 && c <= 0x03c9) /* Sigma thru Omega - uppercase */ return((CHAR)((int)c - 0x0391 + 96)); switch (c) { case 0x03c2: return((CHAR)114); /* Terminal sigma */ case 0x0386: return((CHAR)97); /* Alpha Tonos */ case 0x03ac: return((CHAR)97); /* alpha Tonos */ case 0x0388: return((CHAR)101); /* Epsilon Tonos */ case 0x03ad: return((CHAR)101); /* epsilon Tonos */ case 0x0389: return((CHAR)103); /* Eta Tonos */ case 0x03ae: return((CHAR)103); /* eta Tonos */ case 0x038a: return((CHAR)105); /* Iota Tonos */ case 0x03af: return((CHAR)105); /* iota Tonos */ case 0x03ca: return((CHAR)105); /* iota Dialytika */ case 0x038c: return((CHAR)111); /* Omicron Tonos */ case 0x03cc: return((CHAR)111); /* omicron Tonos */ case 0x038e: return((CHAR)116); /* Upsilon Tonos */ case 0x03d3: return((CHAR)116); /* Upsilon Tonos */ case 0x03cd: return((CHAR)116); /* upsilon Tonos */ case 0x03cb: return((CHAR)116); /* upsilon Dialytika */ case 0x03b0: return((CHAR)116); /* upsilon Dialytika+Tonos */ case 0x038f: return((CHAR)120); /* Omega Tonos */ case 0x03ce: return((CHAR)120); /* omega Tonos */ case 0x0390: return((CHAR)105); /* iota Dialytika+Tonos */ case 0x03aa: return((CHAR)105); /* Iota Dialytika */ case 0x03ab: return((CHAR)116); /* Upsilon Dialytika */ case 0x03d4: return((CHAR)116); /* Upsilon Dialytika */ case 0x03d0: return((CHAR)98); /* Alternative beta */ case 0x03d1: return((CHAR)104); /* Open theta */ case 0x03d5: return((CHAR)117); /* Open phi */ case 0x03d6: return((CHAR)112); /* Alternative Pi */ default: return(-1); } } int /* Hungarian */ #ifdef CK_ANSIC tx_hungarian(USHORT c) #else tx_hungarian(c) USHORT c; #endif /* CK_ANSIC */ { if (c == 0x02dd || c == 0x2033) return(0x7e); /* double acute accent */ else if (c & 0xff00) /* Out of range */ return(-1); switch(c) { case 0xc1: return(0x40); /* A acute */ case 0xc9: return(0x5b); /* E acute */ case 0xd6: return(0x5c); /* O umlaut */ case 0xdc: return(0x5d); /* U umlaut */ case 0xe9: return(0x7b); /* e acute */ case 0xf6: return(0x7c); /* o umlaut */ case 0xfa: return(0x60); /* u acute */ case 0xfc: return(0x7d); /* u umlaut */ default: return(tx_usascii(c)); } } int /* Icelandic */ #ifdef CK_ANSIC tx_icelandic(USHORT c) #else tx_icelandic(c) USHORT c; #endif /* CK_ANSIC */ { if (c & 0xff00) /* Out of range */ return(-1); switch(c) { case 0xde: return(0x40); /* Thorn */ case 0xd0: return(0x5b); /* Eth */ case 0xc6: return(0x5d); /* AE */ case 0xd6: return(0x5e); /* O umlaut */ case 0xfe: return(0x60); /* thorn */ case 0xf0: return(0x7b); /* eth */ case 0xe6: return(0x7d); /* ae */ case 0xf6: return(0x7e); /* o umlaut */ default: return(tx_usascii(c)); } } int /* Italian */ #ifdef CK_ANSIC tx_italian(USHORT c) #else tx_italian(c) USHORT c; #endif /* CK_ANSIC */ { if (c & 0xff00) /* Out of range */ return(-1); switch(c) { case 0xa3: return(0x23); /* pound sign */ case 0xa7: return(0x40); /* section sign */ case 0xb0: return(0x5b); /* ring */ case 0xe7: return(0x5c); /* c cedilla */ case 0xe9: return(0x5d); /* e acute */ case 0xf9: return(0x60); /* u grave */ case 0xe0: return(0x7b); /* a grave */ case 0xf2: return(0x7c); /* o grave */ case 0xe8: return(0x7d); /* e grave */ case 0xec: return(0x7e); /* i grave */ default: return(tx_usascii(c)); } } int /* JIS 0201 Roman */ #ifdef CK_ANSIC tx_jis0201r(USHORT c) #else tx_jis0201r(c) USHORT c; #endif /* CK_ANSIC */ { if (c && 0xff80) /* 7 bits */ return(-1); switch (c) { /* Like ASCII with */ case 0x00a5: return(92); /* two exceptions */ case 0x00af: return(126); case 0x203e: return(126); default: return(tx_usascii(c)); } } int /* JIS 0201 Katakana */ #ifdef CK_ANSIC tx_jis0201k(USHORT c) #else tx_jis0201k(c) USHORT c; #endif /* CK_ANSIC */ { if (c < 0xff61 || c > 0xff9f) return(-1); /* Out of range */ else return((int)c - 0xfec0); /* 0xff61 - a0 = 0xfec0 */ } int /* Short KOI */ #ifdef CK_ANSIC tx_koi7(USHORT c) #else tx_koi7(c) USHORT c; #endif /* CK_ANSIC */ { if (c < 0x50) return((CHAR)(c & 0x7f)); else if (c > 0x7f) return(-1); /* Out of range */ switch(c) { case 0x0410: return((CHAR)97); case 0x0411: return((CHAR)98); case 0x0412: return((CHAR)119); case 0x0413: return((CHAR)103); case 0x0414: return((CHAR)100); case 0x0415: return((CHAR)101); case 0x0416: return((CHAR)118); case 0x0417: return((CHAR)122); case 0x0418: return((CHAR)105); case 0x0419: return((CHAR)106); case 0x041a: return((CHAR)107); case 0x041b: return((CHAR)108); case 0x041c: return((CHAR)109); case 0x041d: return((CHAR)110); case 0x041e: return((CHAR)111); case 0x041f: return((CHAR)112); case 0x0420: return((CHAR)114); case 0x0421: return((CHAR)115); case 0x0422: return((CHAR)116); case 0x0423: return((CHAR)117); case 0x0424: return((CHAR)102); case 0x0425: return((CHAR)104); case 0x0426: return((CHAR)99); case 0x0427: return((CHAR)126); case 0x0428: return((CHAR)123); case 0x0429: return((CHAR)125); case 0x042b: return((CHAR)121); case 0x042c: return((CHAR)120); case 0x042d: return((CHAR)124); case 0x042e: return((CHAR)96); case 0x042f: return((CHAR)113); default: return(-1); } } int /* Portuguese */ #ifdef CK_ANSIC tx_portuguese(USHORT c) #else tx_portuguese(c) USHORT c; #endif /* CK_ANSIC */ { if (c & 0xff00) /* Out of range */ return(-1); switch(c) { case 0xe0: return(0xa7); /* section sign */ case 0xb0: return(0xc3); /* A tilde */ case 0xe7: return(0xc7); /* C cedilla */ case 0xa7: return(0xd5); /* O tilde */ case 0xe9: return(0xe3); /* a tilde */ case 0xf9: return(0xe7); /* c cedilla */ case 0xe8: return(0xf5); /* o tilde */ case 0xa8: return(0xb0); /* ring */ default: return(tx_usascii(c)); } } int /* Spanish */ #ifdef CK_ANSIC tx_spanish(USHORT c) #else tx_spanish(c) USHORT c; #endif /* CK_ANSIC */ { if (c & 0xff00) /* Out of range */ return(-1); switch(c) { case 0xa3: return(0x23); /* pound sign */ case 0xa7: return(0x40); /* section */ case 0xa1: return(0x5b); /* inverted exclamation */ case 0xd1: return(0x5c); /* N tilde */ case 0xbf: return(0x5d); /* inverted question mark */ case 0xb0: return(0x7b); /* ring */ case 0xf1: return(0x7c); /* n tilde */ case 0xe7: return(0x7d); /* c cedilla */ default: return(tx_usascii(c)); } } int /* Swedish */ #ifdef CK_ANSIC tx_swedish(USHORT c) #else tx_swedish(c) USHORT c; #endif /* CK_ANSIC */ { if (c & 0xff00) /* Out of range */ return(-1); switch(c) { case 0xc9: return(0x40); /* E acute */ case 0xc4: return(0x5b); /* A umlaut*/ case 0xd6: return(0x5c); /* O umlaut */ case 0xc5: return(0x5d); /* A ring */ case 0xdc: return(0x5e); /* U umlaut */ case 0xe9: return(0x60); /* e acute */ case 0xe4: return(0x7b); /* a umlaut */ case 0xf6: return(0x7c); /* o umlaut */ case 0xe5: return(0x7d); /* a ring */ case 0xfc: return(0x7e); /* u umlaut */ default: return(tx_usascii(c)); } } int /* Swiss NRC */ #ifdef CK_ANSIC tx_swiss(USHORT c) #else tx_swiss(c) USHORT c; #endif /* CK_ANSIC */ { if (c & 0xff00) /* Out of range */ return(-1); switch(c) { case 0xf9: return(0x23); /* u grave */ case 0xe0: return(0x40); /* a grave */ case 0xe9: return(0x5b); /* e acute */ case 0xe7: return(0x5c); /* c cedilla */ case 0xea: return(0x5d); /* e circumflex */ case 0xee: return(0x5e); /* i circumflex */ case 0xe8: return(0x5f); /* e grave */ case 0xf4: return(0x60); /* o circumflex */ case 0xe4: return(0x7b); /* a umlaut */ case 0xf6: return(0x7c); /* o umlaut */ case 0xfc: return(0x7d); /* u umlaut */ case 0xfb: return(0x7e); /* u circumflex */ default: return(tx_usascii(c)); } } int /* Dyadic APL */ #ifdef CK_ANSIC tx_apl2(USHORT c) #else tx_apl2(c) USHORT c; #endif /* CK_ANSIC */ { if (c >= 0x0041 && c <= 0x005a) /* Letters */ return(c - 0xa2); switch (c) { case 0x00a0: return((CHAR)0xa0); case 0x00a2: return((CHAR)0xa4); case 0x00a3: return((CHAR)0xd4); case 0x00a8: return((CHAR)0xdc); case 0x00af: return((CHAR)0xda); case 0x00d7: return((CHAR)0xa1); case 0x00f7: return((CHAR)0xaf); case 0x2190: return((CHAR)0xbb); case 0x2191: return((CHAR)0xfe); case 0x2192: return((CHAR)0xde); case 0x2193: return((CHAR)0xff); case 0x2206: return((CHAR)0xae); case 0x2207: return((CHAR)0xfd); case 0x220a: return((CHAR)0xb5); case 0x2218: return((CHAR)0xbc); case 0x2228: return((CHAR)0xdf); case 0x2229: return((CHAR)0xd1); case 0x222a: return((CHAR)0xd2); case 0x2260: return((CHAR)0xb0); case 0x2261: return((CHAR)0xb6); case 0x2262: return((CHAR)0xcd); case 0x2264: return((CHAR)0xb8); case 0x2265: return((CHAR)0xb7); case 0x2282: return((CHAR)0xe0); case 0x2283: return((CHAR)0xe1); case 0x2296: return((CHAR)0xd6); case 0x22a2: return((CHAR)0xd7); case 0x22a3: return((CHAR)0xd8); case 0x22a4: return((CHAR)0xba); case 0x22a5: return((CHAR)0xb9); case 0x22c4: return((CHAR)0xb1); case 0x2308: return((CHAR)0xcf); case 0x230a: return((CHAR)0xce); case 0x2337: return((CHAR)0xd9); case 0x2339: return((CHAR)0xca); case 0x233d: return((CHAR)0xd5); case 0x233f: return((CHAR)0xbe); case 0x2340: return((CHAR)0xbf); case 0x2349: return((CHAR)0xd0); case 0x234b: return((CHAR)0xc2); case 0x234e: return((CHAR)0xc0); case 0x2352: return((CHAR)0xc3); case 0x2355: return((CHAR)0xc1); case 0x2359: return((CHAR)0xe2); case 0x235d: return((CHAR)0xbd); case 0x235e: return((CHAR)0xc9); case 0x235f: return((CHAR)0xc7); case 0x2368: return((CHAR)0xc6); case 0x236a: return((CHAR)0xcc); case 0x236b: return((CHAR)0xcb); case 0x236c: return((CHAR)0xd3); case 0x2371: return((CHAR)0xc5); case 0x2372: return((CHAR)0xc4); case 0x2373: return((CHAR)0xdb); case 0x2374: return((CHAR)0xb3); case 0x2375: return((CHAR)0xb2); case 0x237a: return((CHAR)0xb4); case 0x2500: return((CHAR)0xaa); case 0x2502: return((CHAR)0xa2); case 0x250c: return((CHAR)0xad); case 0x2510: return((CHAR)0xa5); case 0x2514: return((CHAR)0xa6); case 0x2518: return((CHAR)0xac); case 0x251c: return((CHAR)0xa9); case 0x2524: return((CHAR)0xa3); case 0x252c: return((CHAR)0xa8); case 0x2534: return((CHAR)0xa7); case 0x253c: return((CHAR)0xab); case 0x25af: return((CHAR)0xc8); case 0x25cb: return((CHAR)0xdd); default: if (c < 0xa0) return((CHAR)(c & 0xff)); return(tx_punc(c)); } } int /* APL-Plus */ #ifdef CK_ANSIC tx_apl3(USHORT c) #else tx_apl3(c) USHORT c; #endif /* CK_ANSIC */ { switch (c) { case 0x00a0: return((CHAR)0xa0); case 0x00a1: return((CHAR)0xa1); case 0x00a2: return((CHAR)0xa2); case 0x00a3: return((CHAR)0xa3); case 0x00a5: return((CHAR)0xa5); case 0x00a6: return((CHAR)0xa6); case 0x00a7: return((CHAR)0xa7); case 0x00a8: return((CHAR)0xa8); case 0x00ab: return((CHAR)0xab); case 0x00af: return((CHAR)0xaf); case 0x00b6: return((CHAR)0xb6); case 0x00b7: return((CHAR)0xb7); case 0x00bb: return((CHAR)0xbb); case 0x00bf: return((CHAR)0xbf); case 0x00c0: return((CHAR)0xc0); case 0x00c1: return((CHAR)0xc1); case 0x00c2: return((CHAR)0xc2); case 0x00c3: return((CHAR)0xc3); case 0x00c4: return((CHAR)0xc4); case 0x00c5: return((CHAR)0xc5); case 0x00c6: return((CHAR)0xc6); case 0x00c7: return((CHAR)0xc7); case 0x00c8: return((CHAR)0xc8); case 0x00c9: return((CHAR)0xc9); case 0x00ca: return((CHAR)0xca); case 0x00cb: return((CHAR)0xcb); case 0x00cc: return((CHAR)0xcc); case 0x00cd: return((CHAR)0xcd); case 0x00ce: return((CHAR)0xce); case 0x00cf: return((CHAR)0xcf); case 0x00d1: return((CHAR)0xd1); case 0x00d2: return((CHAR)0xd2); case 0x00d3: return((CHAR)0xd3); case 0x00d4: return((CHAR)0xd4); case 0x00d5: return((CHAR)0xd5); case 0x00d6: return((CHAR)0xd6); case 0x00d7: return((CHAR)0xd7); case 0x00d8: return((CHAR)0xd8); case 0x00d9: return((CHAR)0xd9); case 0x00da: return((CHAR)0xda); case 0x00db: return((CHAR)0xdb); case 0x00dc: return((CHAR)0xdc); case 0x00dd: return((CHAR)0xdd); case 0x00df: return((CHAR)0xdf); case 0x00e0: return((CHAR)0xe0); case 0x00e1: return((CHAR)0xe1); case 0x00e2: return((CHAR)0xe2); case 0x00e3: return((CHAR)0xe3); case 0x00e4: return((CHAR)0xe4); case 0x00e5: return((CHAR)0xe5); case 0x00e6: return((CHAR)0xe6); case 0x00e7: return((CHAR)0xe7); case 0x00e8: return((CHAR)0xe8); case 0x00e9: return((CHAR)0xe9); case 0x00ea: return((CHAR)0xea); case 0x00eb: return((CHAR)0xeb); case 0x00ec: return((CHAR)0xec); case 0x00ed: return((CHAR)0xed); case 0x00ee: return((CHAR)0xee); case 0x00ef: return((CHAR)0xef); case 0x00f1: return((CHAR)0xf1); case 0x00f2: return((CHAR)0xf2); case 0x00f3: return((CHAR)0xf3); case 0x00f4: return((CHAR)0xf4); case 0x00f5: return((CHAR)0xf5); case 0x00f6: return((CHAR)0xf6); case 0x00f7: return((CHAR)0xf7); case 0x00f9: return((CHAR)0xf9); case 0x00fa: return((CHAR)0xfa); case 0x00fb: return((CHAR)0xfb); case 0x00fc: return((CHAR)0xfc); case 0x00fd: return((CHAR)0xfd); case 0x00ff: return((CHAR)0xff); case 0x20ac: return((CHAR)0x80); case 0x2190: return((CHAR)0x84); case 0x2191: return((CHAR)0x86); case 0x2192: return((CHAR)0x85); case 0x2193: return((CHAR)0x87); case 0x2205: return((CHAR)0xf8); case 0x2206: return((CHAR)0x91); case 0x2207: return((CHAR)0x92); case 0x220a: return((CHAR)0xb9); case 0x2218: return((CHAR)0xb0); case 0x2228: return((CHAR)0x9f); case 0x2229: return((CHAR)0x9d); case 0x222a: return((CHAR)0x9e); case 0x2260: return((CHAR)0xac); case 0x2261: return((CHAR)0xad); case 0x2264: return((CHAR)0x88); case 0x2265: return((CHAR)0x89); case 0x2282: return((CHAR)0x9b); case 0x2283: return((CHAR)0x9c); case 0x2296: return((CHAR)0xb4); case 0x22a2: return((CHAR)0xa4); case 0x22a3: return((CHAR)0x81); case 0x22a4: return((CHAR)0x82); case 0x22a5: return((CHAR)0x83); case 0x22c4: return((CHAR)0xaa); case 0x2308: return((CHAR)0x97); case 0x230a: return((CHAR)0x98); case 0x2337: return((CHAR)0xde); case 0x2339: return((CHAR)0x8e); case 0x233d: return((CHAR)0xb2); case 0x233f: return((CHAR)0x9a); case 0x2340: return((CHAR)0x99); case 0x2349: return((CHAR)0xb3); case 0x234b: return((CHAR)0x93); case 0x234e: return((CHAR)0x96); case 0x2352: return((CHAR)0x94); case 0x2355: return((CHAR)0x95); case 0x2359: return((CHAR)0x8f); case 0x235d: return((CHAR)0xa9); case 0x235e: return((CHAR)0x8d); case 0x235f: return((CHAR)0xb5); case 0x2364: return((CHAR)0xf0); case 0x2368: return((CHAR)0xfe); case 0x236a: return((CHAR)0xae); case 0x236b: return((CHAR)0x90); case 0x236c: return((CHAR)0xd0); case 0x2371: return((CHAR)0x8b); case 0x2372: return((CHAR)0x8a); case 0x2373: return((CHAR)0xbc); case 0x2374: return((CHAR)0xbd); case 0x2375: return((CHAR)0xbe); case 0x2377: return((CHAR)0xba); case 0x237a: return((CHAR)0xb8); case 0x25af: return((CHAR)0x8c); case 0x25cb: return((CHAR)0xb1); default: return(tx_punc(c)); } } int /* IBM APL2 */ #ifdef CK_ANSIC tx_apl4(USHORT c) #else tx_apl4(c) USHORT c; #endif /* CK_ANSIC */ { switch (c) { case 0x00a0: return((CHAR)0xff); case 0x00a1: return((CHAR)0xad); case 0x00a3: return((CHAR)0x9c); case 0x00a6: return((CHAR)0xdd); case 0x00a8: return((CHAR)0xfe); case 0x00aa: return((CHAR)0xa6); case 0x00ac: return((CHAR)0xaa); case 0x00af: return((CHAR)0xfd); case 0x00ba: return((CHAR)0xa7); case 0x00bf: return((CHAR)0xa8); case 0x00c4: return((CHAR)0x8e); case 0x00c5: return((CHAR)0xee); /* and 0x8f */ case 0x00c7: return((CHAR)0x80); case 0x00cc: return((CHAR)0xde); case 0x00d1: return((CHAR)0xa5); case 0x00d6: return((CHAR)0x99); case 0x00d7: return((CHAR)0xf5); case 0x00dc: return((CHAR)0x9a); case 0x00df: return((CHAR)0xe1); case 0x00e0: return((CHAR)0x85); case 0x00e1: return((CHAR)0xa0); case 0x00e2: return((CHAR)0x83); case 0x00e4: return((CHAR)0x84); case 0x00e5: return((CHAR)0x86); case 0x00e7: return((CHAR)0x87); case 0x00e8: return((CHAR)0x8a); case 0x00e9: return((CHAR)0x82); case 0x00ea: return((CHAR)0x88); case 0x00eb: return((CHAR)0x89); case 0x00ec: return((CHAR)0x8d); case 0x00ed: return((CHAR)0xa1); case 0x00ee: return((CHAR)0x8c); case 0x00ef: return((CHAR)0x8b); case 0x00f1: return((CHAR)0xa4); case 0x00f2: return((CHAR)0x95); case 0x00f3: return((CHAR)0xa2); case 0x00f4: return((CHAR)0x93); case 0x00f6: return((CHAR)0x94); case 0x00f7: return((CHAR)0xf6); case 0x00f8: return((CHAR)0x9b); case 0x00f9: return((CHAR)0x97); case 0x00fa: return((CHAR)0xa3); case 0x00fb: return((CHAR)0x96); case 0x00fc: return((CHAR)0x81); case 0x2190: return((CHAR)0x9e); case 0x2191: return((CHAR)0xc6); case 0x2192: return((CHAR)0xab); case 0x2193: return((CHAR)0xc7); case 0x2206: return((CHAR)0xb6); case 0x2207: return((CHAR)0xb7); case 0x2218: return((CHAR)0xf8); case 0x2228: return((CHAR)0xeb); case 0x2229: return((CHAR)0xef); case 0x222a: return((CHAR)0xac); case 0x2235: return((CHAR)0xd2); case 0x2260: return((CHAR)0xf4); case 0x2261: return((CHAR)0xcf); case 0x2264: return((CHAR)0xf3); case 0x2265: return((CHAR)0xf2); case 0x2282: return((CHAR)0xe2); case 0x2283: return((CHAR)0xe3); case 0x2296: return((CHAR)0xe9); case 0x22a2: return((CHAR)0xd6); case 0x22a3: return((CHAR)0xd7); case 0x22a4: return((CHAR)0x98); case 0x22a5: return((CHAR)0x9d); case 0x22c4: return((CHAR)0xd8); case 0x2308: return((CHAR)0xa9); case 0x230a: return((CHAR)0xbe); case 0x2336: return((CHAR)0x9f); case 0x2337: return((CHAR)0xd3); case 0x2339: return((CHAR)0x92); case 0x233b: return((CHAR)0xd5); case 0x233d: return((CHAR)0xe8); case 0x233f: return((CHAR)0xf0); case 0x2340: return((CHAR)0xf1); case 0x2342: return((CHAR)0xd4); case 0x2349: return((CHAR)0xed); case 0x234b: return((CHAR)0xfb); case 0x234e: return((CHAR)0xaf); case 0x2352: return((CHAR)0xfc); case 0x2355: return((CHAR)0xae); case 0x2359: return((CHAR)0xf7); case 0x235d: return((CHAR)0xe4); case 0x235e: return((CHAR)0x91); case 0x235f: return((CHAR)0xb5); case 0x236b: return((CHAR)0xfa); case 0x2371: return((CHAR)0xe7); case 0x2372: return((CHAR)0xe5); case 0x2373: return((CHAR)0xec); case 0x2374: return((CHAR)0xe6); case 0x2375: return((CHAR)0xf9); case 0x2377: return((CHAR)0xd1); case 0x2378: return((CHAR)0xd0); case 0x237a: return((CHAR)0xe0); case 0x2500: return((CHAR)0xc4); case 0x2502: return((CHAR)0xb3); case 0x250c: return((CHAR)0xda); case 0x2510: return((CHAR)0xbf); case 0x2514: return((CHAR)0xc0); case 0x2518: return((CHAR)0xd9); case 0x251c: return((CHAR)0xc3); case 0x2524: return((CHAR)0xb4); case 0x252c: return((CHAR)0xc2); case 0x2534: return((CHAR)0xc1); case 0x253c: return((CHAR)0xc5); case 0x2550: return((CHAR)0xcd); case 0x2551: return((CHAR)0xba); case 0x2554: return((CHAR)0xc9); case 0x2557: return((CHAR)0xbb); case 0x255a: return((CHAR)0xc8); case 0x255d: return((CHAR)0xbc); case 0x2560: return((CHAR)0xcc); case 0x2563: return((CHAR)0xb9); case 0x2566: return((CHAR)0xcb); case 0x2569: return((CHAR)0xca); case 0x256c: return((CHAR)0xce); case 0x2580: return((CHAR)0xdf); case 0x2584: return((CHAR)0xdc); case 0x2588: return((CHAR)0xdb); case 0x2591: return((CHAR)0xb0); case 0x2592: return((CHAR)0xb1); case 0x2593: return((CHAR)0xb2); case 0x25af: return((CHAR)0x90); case 0x25cb: return((CHAR)0xea); default: return(tx_punc(c)); } } int /* APL-2741 */ #ifdef CK_ANSIC tx_apl5(USHORT c) #else tx_apl5(c) USHORT c; #endif /* CK_ANSIC */ { switch (c) { case 0x00a0: return((CHAR)0xa0); case 0x00a1: return((CHAR)0xa1); case 0x00a2: return((CHAR)0xa2); case 0x00a3: return((CHAR)0xa3); case 0x00a8: return((CHAR)0xa8); case 0x00af: return((CHAR)0xaf); case 0x00b6: return((CHAR)0xb6); case 0x00b7: return((CHAR)0xb7); case 0x00bf: return((CHAR)0xbf); case 0x00c4: return((CHAR)0xc4); case 0x00c5: return((CHAR)0xc5); case 0x00c6: return((CHAR)0xc6); case 0x00c7: return((CHAR)0xc7); case 0x00c8: return((CHAR)0xc8); case 0x00c9: return((CHAR)0xc9); case 0x00cd: return((CHAR)0xcd); case 0x00d1: return((CHAR)0xd1); case 0x00d6: return((CHAR)0xd6); case 0x00d7: return((CHAR)0xd7); case 0x00d8: return((CHAR)0xd8); case 0x00dc: return((CHAR)0xdc); case 0x00df: return((CHAR)0xdf); case 0x00e0: return((CHAR)0xe0); case 0x00e1: return((CHAR)0xe1); case 0x00e2: return((CHAR)0xe2); case 0x00e3: return((CHAR)0xe3); case 0x00e4: return((CHAR)0xe4); case 0x00e5: return((CHAR)0xe5); case 0x00e6: return((CHAR)0xe6); case 0x00e7: return((CHAR)0xe7); case 0x00e8: return((CHAR)0xe8); case 0x00e9: return((CHAR)0xe9); case 0x00ea: return((CHAR)0xea); case 0x00eb: return((CHAR)0xeb); case 0x00ec: return((CHAR)0xec); case 0x00ed: return((CHAR)0xed); case 0x00ee: return((CHAR)0xee); case 0x00ef: return((CHAR)0xef); case 0x00f1: return((CHAR)0xf1); case 0x00f2: return((CHAR)0xf2); case 0x00f3: return((CHAR)0xf3); case 0x00f4: return((CHAR)0xf4); case 0x00f5: return((CHAR)0xf5); case 0x00f6: return((CHAR)0xf6); case 0x00f7: return((CHAR)0xf7); case 0x00f9: return((CHAR)0xf9); case 0x00fa: return((CHAR)0xfa); case 0x00fb: return((CHAR)0xfb); case 0x00fc: return((CHAR)0xfc); case 0x20ac: return((CHAR)0x80); case 0x2190: return((CHAR)0x84); case 0x2191: return((CHAR)0x86); case 0x2192: return((CHAR)0x85); case 0x2193: return((CHAR)0x87); case 0x2205: return((CHAR)0xf8); case 0x2206: return((CHAR)0x91); case 0x2207: return((CHAR)0x92); case 0x220a: return((CHAR)0xb9); case 0x2218: return((CHAR)0xb0); case 0x2228: return((CHAR)0x9f); case 0x2229: return((CHAR)0x9d); case 0x222a: return((CHAR)0x9e); case 0x2235: return((CHAR)0xfd); case 0x2260: return((CHAR)0xac); case 0x2261: return((CHAR)0xa6); case 0x2262: return((CHAR)0xbb); case 0x2264: return((CHAR)0x88); case 0x2265: return((CHAR)0x89); case 0x2282: return((CHAR)0x9b); case 0x2283: return((CHAR)0x9c); case 0x2296: return((CHAR)0xb4); case 0x22a2: return((CHAR)0xa4); case 0x22a3: return((CHAR)0x81); case 0x22a4: return((CHAR)0x82); case 0x22a5: return((CHAR)0x83); case 0x22c4: return((CHAR)0xaa); case 0x2308: return((CHAR)0x97); case 0x230a: return((CHAR)0x98); case 0x2336: return((CHAR)0xa7); case 0x2337: return((CHAR)0xde); case 0x2339: return((CHAR)0x8e); case 0x233b: return((CHAR)0xcb); case 0x233d: return((CHAR)0xb2); case 0x233f: return((CHAR)0x9a); case 0x2340: return((CHAR)0x99); case 0x2342: return((CHAR)0xca); case 0x2347: return((CHAR)0xd4); case 0x2348: return((CHAR)0xd5); case 0x2349: return((CHAR)0xb3); case 0x234b: return((CHAR)0x93); case 0x234e: return((CHAR)0x96); case 0x2350: return((CHAR)0xd2); case 0x2352: return((CHAR)0x94); case 0x2355: return((CHAR)0x95); case 0x2357: return((CHAR)0xd3); case 0x2359: return((CHAR)0x8f); case 0x235d: return((CHAR)0xa9); case 0x235e: return((CHAR)0x8d); case 0x235f: return((CHAR)0xb5); case 0x2364: return((CHAR)0xf0); case 0x2365: return((CHAR)0xff); case 0x2368: return((CHAR)0xfe); case 0x236a: return((CHAR)0xae); case 0x236b: return((CHAR)0x90); case 0x236c: return((CHAR)0xab); case 0x2371: return((CHAR)0x8b); case 0x2372: return((CHAR)0x8a); case 0x2373: return((CHAR)0xbc); case 0x2374: return((CHAR)0xbd); case 0x2375: return((CHAR)0xbe); case 0x2377: return((CHAR)0xba); case 0x2378: return((CHAR)0xa5); case 0x237a: return((CHAR)0xb8); case 0x2500: return((CHAR)0xce); case 0x2502: return((CHAR)0xdb); case 0x250c: return((CHAR)0xda); case 0x2510: return((CHAR)0xcc); case 0x2514: return((CHAR)0xc0); case 0x2518: return((CHAR)0xd9); case 0x251c: return((CHAR)0xc3); case 0x2524: return((CHAR)0xdd); case 0x252c: return((CHAR)0xc2); case 0x2534: return((CHAR)0xc1); case 0x253c: return((CHAR)0xcf); case 0x25af: return((CHAR)0x8c); case 0x25cb: return((CHAR)0xb1); default: return(tx_punc(c)); } } /* For Latin-1, use tx_ident() */ int /* Latin-2 */ #ifdef CK_ANSIC tx_8859_2(USHORT c) #else tx_8859_2(c) USHORT c; #endif /* CK_ANSIC */ { if (c < 0xa0) return((CHAR)(c & 0xff)); switch(c) { case 0x00A0: return((CHAR)160); case 0x00A4: return((CHAR)164); case 0x00A7: return((CHAR)167); case 0x00A8: return((CHAR)168); case 0x00AD: return((CHAR)173); case 0x00B0: return((CHAR)176); case 0x00B4: return((CHAR)180); case 0x00B8: return((CHAR)184); case 0x00C1: return((CHAR)193); case 0x00C2: return((CHAR)194); case 0x00C4: return((CHAR)196); case 0x00C7: return((CHAR)199); case 0x00C9: return((CHAR)201); case 0x00CB: return((CHAR)203); case 0x00CD: return((CHAR)205); case 0x00CE: return((CHAR)206); case 0x00D3: return((CHAR)211); case 0x00D4: return((CHAR)212); case 0x00D6: return((CHAR)214); case 0x00D7: return((CHAR)215); case 0x00DA: return((CHAR)218); case 0x00DC: return((CHAR)220); case 0x00DD: return((CHAR)221); case 0x00DF: return((CHAR)223); case 0x00E1: return((CHAR)225); case 0x00E2: return((CHAR)226); case 0x00E4: return((CHAR)228); case 0x00E7: return((CHAR)231); case 0x00E9: return((CHAR)233); case 0x00EB: return((CHAR)235); case 0x00ED: return((CHAR)237); case 0x00EE: return((CHAR)238); case 0x00F3: return((CHAR)243); case 0x00F4: return((CHAR)244); case 0x00F6: return((CHAR)246); case 0x00F7: return((CHAR)247); case 0x00FA: return((CHAR)250); case 0x00FC: return((CHAR)252); case 0x00FD: return((CHAR)253); case 0x0102: return((CHAR)195); case 0x0103: return((CHAR)227); case 0x0104: return((CHAR)161); case 0x0105: return((CHAR)177); case 0x0106: return((CHAR)198); case 0x0107: return((CHAR)230); case 0x010C: return((CHAR)200); case 0x010D: return((CHAR)232); case 0x010E: return((CHAR)207); case 0x010F: return((CHAR)239); case 0x0110: return((CHAR)208); case 0x0111: return((CHAR)240); case 0x0118: return((CHAR)202); case 0x0119: return((CHAR)234); case 0x011A: return((CHAR)204); case 0x011B: return((CHAR)236); case 0x0139: return((CHAR)197); case 0x013A: return((CHAR)229); case 0x013D: return((CHAR)165); case 0x013E: return((CHAR)181); case 0x0141: return((CHAR)163); case 0x0142: return((CHAR)179); case 0x0143: return((CHAR)209); case 0x0144: return((CHAR)241); case 0x0147: return((CHAR)210); case 0x0148: return((CHAR)242); case 0x0150: return((CHAR)213); case 0x0151: return((CHAR)245); case 0x0154: return((CHAR)192); case 0x0155: return((CHAR)224); case 0x0158: return((CHAR)216); case 0x0159: return((CHAR)248); case 0x015A: return((CHAR)166); case 0x015B: return((CHAR)182); case 0x015E: return((CHAR)170); case 0x015F: return((CHAR)186); case 0x0160: return((CHAR)169); case 0x0161: return((CHAR)185); case 0x0162: return((CHAR)222); case 0x0163: return((CHAR)254); case 0x0164: return((CHAR)171); case 0x0165: return((CHAR)187); case 0x016E: return((CHAR)217); case 0x016F: return((CHAR)249); case 0x0170: return((CHAR)219); case 0x0171: return((CHAR)251); case 0x0179: return((CHAR)172); case 0x017A: return((CHAR)188); case 0x017B: return((CHAR)175); case 0x017C: return((CHAR)191); case 0x017D: return((CHAR)174); case 0x017E: return((CHAR)190); case 0x02C7: return((CHAR)183); case 0x02D8: return((CHAR)162); case 0x02D9: return((CHAR)255); case 0x02DB: return((CHAR)178); case 0x02DD: return((CHAR)189); default: return(tx_punc(c)); } } int /* Latin-3 */ #ifdef CK_ANSIC tx_8859_3(USHORT c) #else tx_8859_3(c) USHORT c; #endif /* CK_ANSIC */ { if (c < 0xa0) return((CHAR)(c & 0xff)); switch(c) { case 0x00A0: return((CHAR)160); case 0x00A3: return((CHAR)163); case 0x00A4: return((CHAR)164); case 0x00A7: return((CHAR)167); case 0x00A8: return((CHAR)168); case 0x00AD: return((CHAR)173); case 0x00B0: return((CHAR)176); case 0x00B2: return((CHAR)178); case 0x00B3: return((CHAR)179); case 0x00B4: return((CHAR)180); case 0x00B5: return((CHAR)181); case 0x00B7: return((CHAR)183); case 0x00B8: return((CHAR)184); case 0x00BD: return((CHAR)189); case 0x00C0: return((CHAR)192); case 0x00C1: return((CHAR)193); case 0x00C2: return((CHAR)194); case 0x00C4: return((CHAR)196); case 0x00C7: return((CHAR)199); case 0x00C8: return((CHAR)200); case 0x00C9: return((CHAR)201); case 0x00CA: return((CHAR)202); case 0x00CB: return((CHAR)203); case 0x00CC: return((CHAR)204); case 0x00CD: return((CHAR)205); case 0x00CE: return((CHAR)206); case 0x00CF: return((CHAR)207); case 0x00D1: return((CHAR)209); case 0x00D2: return((CHAR)210); case 0x00D3: return((CHAR)211); case 0x00D4: return((CHAR)212); case 0x00D6: return((CHAR)214); case 0x00D7: return((CHAR)215); case 0x00D9: return((CHAR)217); case 0x00DA: return((CHAR)218); case 0x00DB: return((CHAR)219); case 0x00DC: return((CHAR)220); case 0x00DF: return((CHAR)223); case 0x00E0: return((CHAR)224); case 0x00E1: return((CHAR)225); case 0x00E2: return((CHAR)226); case 0x00E4: return((CHAR)228); case 0x00E7: return((CHAR)231); case 0x00E8: return((CHAR)232); case 0x00E9: return((CHAR)233); case 0x00EA: return((CHAR)234); case 0x00EB: return((CHAR)235); case 0x00EC: return((CHAR)236); case 0x00ED: return((CHAR)237); case 0x00EE: return((CHAR)238); case 0x00EF: return((CHAR)239); case 0x00F1: return((CHAR)241); case 0x00F2: return((CHAR)242); case 0x00F3: return((CHAR)243); case 0x00F4: return((CHAR)244); case 0x00F6: return((CHAR)246); case 0x00F7: return((CHAR)247); case 0x00F9: return((CHAR)249); case 0x00FA: return((CHAR)250); case 0x00FB: return((CHAR)251); case 0x00FC: return((CHAR)252); case 0x0108: return((CHAR)198); case 0x0109: return((CHAR)230); case 0x010A: return((CHAR)197); case 0x010B: return((CHAR)229); case 0x011C: return((CHAR)216); case 0x011D: return((CHAR)248); case 0x011E: return((CHAR)171); case 0x011F: return((CHAR)187); case 0x0120: return((CHAR)213); case 0x0121: return((CHAR)245); case 0x0124: return((CHAR)166); case 0x0125: return((CHAR)182); case 0x0126: return((CHAR)161); case 0x0127: return((CHAR)177); case 0x0130: return((CHAR)169); case 0x0131: return((CHAR)185); case 0x0134: return((CHAR)172); case 0x0135: return((CHAR)188); case 0x015C: return((CHAR)222); case 0x015D: return((CHAR)254); case 0x015E: return((CHAR)170); case 0x015F: return((CHAR)186); case 0x016C: return((CHAR)221); case 0x016D: return((CHAR)253); case 0x017B: return((CHAR)175); case 0x017C: return((CHAR)191); case 0x02D8: return((CHAR)162); case 0x02D9: return((CHAR)255); default: return(tx_punc(c)); } } int /* Latin-4 */ #ifdef CK_ANSIC tx_8859_4(USHORT c) #else tx_8859_4(c) USHORT c; #endif /* CK_ANSIC */ { if (c < 0xa0) return((CHAR)(c & 0xff)); switch(c) { case 0x00A0: return((CHAR)160); case 0x00A4: return((CHAR)164); case 0x00A7: return((CHAR)167); case 0x00A8: return((CHAR)168); case 0x00AD: return((CHAR)173); case 0x00AF: return((CHAR)175); case 0x00B0: return((CHAR)176); case 0x00B4: return((CHAR)180); case 0x00B8: return((CHAR)184); case 0x00C1: return((CHAR)193); case 0x00C2: return((CHAR)194); case 0x00C3: return((CHAR)195); case 0x00C4: return((CHAR)196); case 0x00C5: return((CHAR)197); case 0x00C6: return((CHAR)198); case 0x00C9: return((CHAR)201); case 0x00CB: return((CHAR)203); case 0x00CD: return((CHAR)205); case 0x00CE: return((CHAR)206); case 0x00D4: return((CHAR)212); case 0x00D5: return((CHAR)213); case 0x00D6: return((CHAR)214); case 0x00D7: return((CHAR)215); case 0x00D8: return((CHAR)216); case 0x00DA: return((CHAR)218); case 0x00DB: return((CHAR)219); case 0x00DC: return((CHAR)220); case 0x00DF: return((CHAR)223); case 0x00E1: return((CHAR)225); case 0x00E2: return((CHAR)226); case 0x00E3: return((CHAR)227); case 0x00E4: return((CHAR)228); case 0x00E5: return((CHAR)229); case 0x00E6: return((CHAR)230); case 0x00E9: return((CHAR)233); case 0x00EB: return((CHAR)235); case 0x00ED: return((CHAR)237); case 0x00EE: return((CHAR)238); case 0x00F4: return((CHAR)244); case 0x00F5: return((CHAR)245); case 0x00F6: return((CHAR)246); case 0x00F7: return((CHAR)247); case 0x00F8: return((CHAR)248); case 0x00FA: return((CHAR)250); case 0x00FB: return((CHAR)251); case 0x00FC: return((CHAR)252); case 0x0100: return((CHAR)192); case 0x0101: return((CHAR)224); case 0x0104: return((CHAR)161); case 0x0105: return((CHAR)177); case 0x010C: return((CHAR)200); case 0x010D: return((CHAR)232); case 0x0110: return((CHAR)208); case 0x0111: return((CHAR)240); case 0x0112: return((CHAR)170); case 0x0113: return((CHAR)186); case 0x0116: return((CHAR)204); case 0x0117: return((CHAR)236); case 0x0118: return((CHAR)202); case 0x0119: return((CHAR)234); case 0x0122: return((CHAR)171); case 0x0123: return((CHAR)187); case 0x0128: return((CHAR)165); case 0x0129: return((CHAR)181); case 0x012A: return((CHAR)207); case 0x012B: return((CHAR)239); case 0x012E: return((CHAR)199); case 0x012F: return((CHAR)231); case 0x0136: return((CHAR)211); case 0x0137: return((CHAR)243); case 0x0138: return((CHAR)162); case 0x013B: return((CHAR)166); case 0x013C: return((CHAR)182); case 0x0145: return((CHAR)209); case 0x0146: return((CHAR)241); case 0x014A: return((CHAR)189); case 0x014B: return((CHAR)191); case 0x014C: return((CHAR)210); case 0x014D: return((CHAR)242); case 0x0156: return((CHAR)163); case 0x0157: return((CHAR)179); case 0x0160: return((CHAR)169); case 0x0161: return((CHAR)185); case 0x0166: return((CHAR)172); case 0x0167: return((CHAR)188); case 0x0168: return((CHAR)221); case 0x0169: return((CHAR)253); case 0x016A: return((CHAR)222); case 0x016B: return((CHAR)254); case 0x0172: return((CHAR)217); case 0x0173: return((CHAR)249); case 0x017D: return((CHAR)174); case 0x017E: return((CHAR)190); case 0x02C7: return((CHAR)183); case 0x02D9: return((CHAR)255); case 0x02DB: return((CHAR)178); default: return(tx_punc(c)); } } int /* ISO 8859-5 (Latin/Cyrillic) */ #ifdef CK_ANSIC tx_8859_5(USHORT c) #else tx_8859_5(c) USHORT c; #endif /* CK_ANSIC */ { if (c < 0xa0) /* (8859-5 is not Latin-5!) */ return((CHAR)(c & 0xff)); switch(c) { case 0x00A0: return((CHAR)160); case 0x00A7: return((CHAR)253); case 0x00AD: return((CHAR)173); case 0x0401: return((CHAR)161); case 0x0402: return((CHAR)162); case 0x0403: return((CHAR)163); case 0x0404: return((CHAR)164); case 0x0405: return((CHAR)165); case 0x0406: return((CHAR)166); case 0x0407: return((CHAR)167); case 0x0408: return((CHAR)168); case 0x0409: return((CHAR)169); case 0x040A: return((CHAR)170); case 0x040B: return((CHAR)171); case 0x040C: return((CHAR)172); case 0x040E: return((CHAR)174); case 0x040F: return((CHAR)175); case 0x0410: return((CHAR)176); case 0x0411: return((CHAR)177); case 0x0412: return((CHAR)178); case 0x0413: return((CHAR)179); case 0x0414: return((CHAR)180); case 0x0415: return((CHAR)181); case 0x0416: return((CHAR)182); case 0x0417: return((CHAR)183); case 0x0418: return((CHAR)184); case 0x0419: return((CHAR)185); case 0x041A: return((CHAR)186); case 0x041B: return((CHAR)187); case 0x041C: return((CHAR)188); case 0x041D: return((CHAR)189); case 0x041E: return((CHAR)190); case 0x041F: return((CHAR)191); case 0x0420: return((CHAR)192); case 0x0421: return((CHAR)193); case 0x0422: return((CHAR)194); case 0x0423: return((CHAR)195); case 0x0424: return((CHAR)196); case 0x0425: return((CHAR)197); case 0x0426: return((CHAR)198); case 0x0427: return((CHAR)199); case 0x0428: return((CHAR)200); case 0x0429: return((CHAR)201); case 0x042A: return((CHAR)202); case 0x042B: return((CHAR)203); case 0x042C: return((CHAR)204); case 0x042D: return((CHAR)205); case 0x042E: return((CHAR)206); case 0x042F: return((CHAR)207); case 0x0430: return((CHAR)208); case 0x0431: return((CHAR)209); case 0x0432: return((CHAR)210); case 0x0433: return((CHAR)211); case 0x0434: return((CHAR)212); case 0x0435: return((CHAR)213); case 0x0436: return((CHAR)214); case 0x0437: return((CHAR)215); case 0x0438: return((CHAR)216); case 0x0439: return((CHAR)217); case 0x043A: return((CHAR)218); case 0x043B: return((CHAR)219); case 0x043C: return((CHAR)220); case 0x043D: return((CHAR)221); case 0x043E: return((CHAR)222); case 0x043F: return((CHAR)223); case 0x0440: return((CHAR)224); case 0x0441: return((CHAR)225); case 0x0442: return((CHAR)226); case 0x0443: return((CHAR)227); case 0x0444: return((CHAR)228); case 0x0445: return((CHAR)229); case 0x0446: return((CHAR)230); case 0x0447: return((CHAR)231); case 0x0448: return((CHAR)232); case 0x0449: return((CHAR)233); case 0x044A: return((CHAR)234); case 0x044B: return((CHAR)235); case 0x044C: return((CHAR)236); case 0x044D: return((CHAR)237); case 0x044E: return((CHAR)238); case 0x044F: return((CHAR)239); case 0x0451: return((CHAR)241); case 0x0452: return((CHAR)242); case 0x0453: return((CHAR)243); case 0x0454: return((CHAR)244); case 0x0455: return((CHAR)245); case 0x0456: return((CHAR)246); case 0x0457: return((CHAR)247); case 0x0458: return((CHAR)248); case 0x0459: return((CHAR)249); case 0x045A: return((CHAR)250); case 0x045B: return((CHAR)251); case 0x045C: return((CHAR)252); case 0x045E: return((CHAR)254); case 0x045F: return((CHAR)255); case 0x2116: return((CHAR)240); default: return(tx_punc(c)); } } int /* ISO 8859-6 (Latin/Arabic) */ #ifdef CK_ANSIC tx_8859_6(USHORT c) #else tx_8859_6(c) USHORT c; #endif /* CK_ANSIC */ { if (c < 0xa0) /* (8859-6 != Latin-6) */ return((CHAR)(c & 0xff)); switch(c) { case 0x00A0: return((CHAR)160); case 0x00A4: return((CHAR)164); case 0x00AD: return((CHAR)173); case 0x060C: return((CHAR)172); case 0x061B: return((CHAR)187); case 0x061F: return((CHAR)191); case 0x0621: return((CHAR)193); case 0x0622: return((CHAR)194); case 0x0623: return((CHAR)195); case 0x0624: return((CHAR)196); case 0x0625: return((CHAR)197); case 0x0626: return((CHAR)198); case 0x0627: return((CHAR)199); case 0x0628: return((CHAR)200); case 0x0629: return((CHAR)201); case 0x062A: return((CHAR)202); case 0x062B: return((CHAR)203); case 0x062C: return((CHAR)204); case 0x062D: return((CHAR)205); case 0x062E: return((CHAR)206); case 0x062F: return((CHAR)207); case 0x0630: return((CHAR)208); case 0x0631: return((CHAR)209); case 0x0632: return((CHAR)210); case 0x0633: return((CHAR)211); case 0x0634: return((CHAR)212); case 0x0635: return((CHAR)213); case 0x0636: return((CHAR)214); case 0x0637: return((CHAR)215); case 0x0638: return((CHAR)216); case 0x0639: return((CHAR)217); case 0x063A: return((CHAR)218); case 0x0640: return((CHAR)224); case 0x0641: return((CHAR)225); case 0x0642: return((CHAR)226); case 0x0643: return((CHAR)227); case 0x0644: return((CHAR)228); case 0x0645: return((CHAR)229); case 0x0646: return((CHAR)230); case 0x0647: return((CHAR)231); case 0x0648: return((CHAR)232); case 0x0649: return((CHAR)233); case 0x064A: return((CHAR)234); case 0x064B: return((CHAR)235); case 0x064C: return((CHAR)236); case 0x064D: return((CHAR)237); case 0x064E: return((CHAR)238); case 0x064F: return((CHAR)239); case 0x0650: return((CHAR)240); case 0x0651: return((CHAR)241); case 0x0652: return((CHAR)242); default: return(tx_punc(c)); } } int /* ISO 8859-7 (Latin/Greek) */ #ifdef CK_ANSIC tx_8859_7(USHORT c) #else tx_8859_7(c) USHORT c; #endif /* CK_ANSIC */ { if (c < 0xa0) return((CHAR)(c & 0xff)); switch(c) { case 0x00a0: return((CHAR)160); case 0x00a3: return((CHAR)163); case 0x00a6: return((CHAR)166); case 0x00a7: return((CHAR)167); case 0x00a8: return((CHAR)168); case 0x00a9: return((CHAR)169); case 0x00ab: return((CHAR)171); case 0x00ac: return((CHAR)172); case 0x00ad: return((CHAR)173); case 0x00b0: return((CHAR)176); case 0x00b1: return((CHAR)177); case 0x00b2: return((CHAR)178); case 0x00b3: return((CHAR)179); case 0x00b7: return((CHAR)183); case 0x00bb: return((CHAR)187); case 0x00bd: return((CHAR)189); case 0x02bc: return((CHAR)162); case 0x02bd: return((CHAR)161); case 0x0384: return((CHAR)180); case 0x0385: return((CHAR)181); case 0x0386: return((CHAR)182); case 0x0388: return((CHAR)184); case 0x0389: return((CHAR)185); case 0x038a: return((CHAR)186); case 0x038c: return((CHAR)188); case 0x038e: return((CHAR)190); case 0x038f: return((CHAR)191); case 0x0390: return((CHAR)192); case 0x0391: return((CHAR)193); case 0x0392: return((CHAR)194); case 0x0393: return((CHAR)195); case 0x0394: return((CHAR)196); case 0x0395: return((CHAR)197); case 0x0396: return((CHAR)198); case 0x0397: return((CHAR)199); case 0x0398: return((CHAR)200); case 0x0399: return((CHAR)201); case 0x039a: return((CHAR)202); case 0x039b: return((CHAR)203); case 0x039c: return((CHAR)204); case 0x039d: return((CHAR)205); case 0x039e: return((CHAR)206); case 0x039f: return((CHAR)207); case 0x03a0: return((CHAR)208); case 0x03a1: return((CHAR)209); case 0x03a3: return((CHAR)211); case 0x03a4: return((CHAR)212); case 0x03a5: return((CHAR)213); case 0x03a6: return((CHAR)214); case 0x03a7: return((CHAR)215); case 0x03a8: return((CHAR)216); case 0x03a9: return((CHAR)217); case 0x03aa: return((CHAR)218); case 0x03ab: return((CHAR)219); case 0x03ac: return((CHAR)220); case 0x03ad: return((CHAR)221); case 0x03ae: return((CHAR)222); case 0x03af: return((CHAR)223); case 0x03b0: return((CHAR)224); case 0x03b1: return((CHAR)225); case 0x03b2: return((CHAR)226); case 0x03b3: return((CHAR)227); case 0x03b4: return((CHAR)228); case 0x03b5: return((CHAR)229); case 0x03b6: return((CHAR)230); case 0x03b7: return((CHAR)231); case 0x03b8: return((CHAR)232); case 0x03b9: return((CHAR)233); case 0x03ba: return((CHAR)234); case 0x03bb: return((CHAR)235); case 0x03bc: return((CHAR)236); case 0x03bd: return((CHAR)237); case 0x03be: return((CHAR)238); case 0x03bf: return((CHAR)239); case 0x03c0: return((CHAR)240); case 0x03c1: return((CHAR)241); case 0x03c2: return((CHAR)242); case 0x03c3: return((CHAR)243); case 0x03c4: return((CHAR)244); case 0x03c5: return((CHAR)245); case 0x03c6: return((CHAR)246); case 0x03c7: return((CHAR)247); case 0x03c8: return((CHAR)248); case 0x03c9: return((CHAR)249); case 0x03ca: return((CHAR)250); case 0x03cb: return((CHAR)251); case 0x03cc: return((CHAR)252); case 0x03cd: return((CHAR)253); case 0x03ce: return((CHAR)254); case 0x2015: return((CHAR)175); case 0x2018: return((CHAR)161); case 0x2019: return((CHAR)162); default: return(tx_punc(c)); } } int /* ISO 8859-8 (Latin/Hebrew) */ #ifdef CK_ANSIC tx_8859_8(USHORT c) #else tx_8859_8(c) USHORT c; #endif /* CK_ANSIC */ { if (c < 0xa0) return((CHAR)(c & 0xff)); switch(c) { case 0x00a0: return((CHAR)160); case 0x00a2: return((CHAR)162); case 0x00a3: return((CHAR)163); case 0x00a4: return((CHAR)164); case 0x00a5: return((CHAR)165); case 0x00a6: return((CHAR)166); case 0x00a7: return((CHAR)167); case 0x00a8: return((CHAR)168); case 0x00a9: return((CHAR)169); case 0x00d7: return((CHAR)170); case 0x00ab: return((CHAR)171); case 0x00ac: return((CHAR)172); case 0x00ad: return((CHAR)173); case 0x00ae: return((CHAR)174); case 0x203e: return((CHAR)175); case 0x00b0: return((CHAR)176); case 0x00b1: return((CHAR)177); case 0x00b2: return((CHAR)178); case 0x00b3: return((CHAR)179); case 0x00b4: return((CHAR)180); case 0x00b5: return((CHAR)181); case 0x00b6: return((CHAR)182); case 0x00b7: return((CHAR)183); case 0x00b8: return((CHAR)184); case 0x00b9: return((CHAR)185); case 0x00f7: return((CHAR)186); case 0x00bb: return((CHAR)187); case 0x00bc: return((CHAR)188); case 0x00bd: return((CHAR)189); case 0x00be: return((CHAR)190); case 0x2017: return((CHAR)223); case 0x05d0: return((CHAR)224); case 0x05d1: return((CHAR)225); case 0x05d2: return((CHAR)226); case 0x05d3: return((CHAR)227); case 0x05d4: return((CHAR)228); case 0x05d5: return((CHAR)229); case 0x05d6: return((CHAR)230); case 0x05d7: return((CHAR)231); case 0x05d8: return((CHAR)232); case 0x05d9: return((CHAR)233); case 0x05da: return((CHAR)234); case 0x05db: return((CHAR)235); case 0x05dc: return((CHAR)236); case 0x05dd: return((CHAR)237); case 0x05de: return((CHAR)238); case 0x05df: return((CHAR)239); case 0x05e0: return((CHAR)240); case 0x05e1: return((CHAR)241); case 0x05e2: return((CHAR)242); case 0x05e3: return((CHAR)243); case 0x05e4: return((CHAR)244); case 0x05e5: return((CHAR)245); case 0x05e6: return((CHAR)246); case 0x05e7: return((CHAR)247); case 0x05e8: return((CHAR)248); case 0x05e9: return((CHAR)249); case 0x05ea: return((CHAR)250); default: return(tx_punc(c)); } } int /* ISO 8859-9 (Latin-4) */ #ifdef CK_ANSIC tx_8859_9(USHORT c) #else tx_8859_9(c) USHORT c; #endif /* CK_ANSIC */ { if (c < 0xa0) return((CHAR)(c & 0xff)); switch(c) { case 0x011E: return((CHAR)208); /* Differs from Latin-1 in */ case 0x011F: return((CHAR)240); /* only six places */ case 0x0130: return((CHAR)221); case 0x0131: return((CHAR)253); case 0x015E: return((CHAR)222); case 0x015F: return((CHAR)254); default: return(tx_ident(c)); } } int /* Latin-6 */ #ifdef CK_ANSIC tx_8859_10(USHORT c) #else tx_8859_10(c) USHORT c; #endif /* CK_ANSIC */ { if (c < 0xa0) return((CHAR)(c & 0xff)); switch(c) { case 0x00a0: return((CHAR)160); case 0x00ad: return((CHAR)173); case 0x00bd: return((CHAR)189); case 0x00be: return((CHAR)190); case 0x00c1: return((CHAR)193); case 0x00c2: return((CHAR)194); case 0x00c3: return((CHAR)195); case 0x00c4: return((CHAR)196); case 0x00c5: return((CHAR)197); case 0x00c6: return((CHAR)198); case 0x00c9: return((CHAR)201); case 0x00cb: return((CHAR)203); case 0x00cd: return((CHAR)205); case 0x00ce: return((CHAR)206); case 0x00cf: return((CHAR)207); case 0x00d3: return((CHAR)211); case 0x00d4: return((CHAR)212); case 0x00d5: return((CHAR)213); case 0x00d6: return((CHAR)214); case 0x00d8: return((CHAR)216); case 0x00da: return((CHAR)218); case 0x00db: return((CHAR)219); case 0x00dc: return((CHAR)220); case 0x00dd: return((CHAR)221); case 0x00de: return((CHAR)222); case 0x00e1: return((CHAR)225); case 0x00e2: return((CHAR)226); case 0x00e3: return((CHAR)227); case 0x00e4: return((CHAR)228); case 0x00e5: return((CHAR)229); case 0x00e6: return((CHAR)230); case 0x00e9: return((CHAR)233); case 0x00eb: return((CHAR)235); case 0x00ed: return((CHAR)237); case 0x00ee: return((CHAR)238); case 0x00ef: return((CHAR)239); case 0x00f0: return((CHAR)240); case 0x00f1: return((CHAR)241); case 0x00f3: return((CHAR)243); case 0x00f4: return((CHAR)244); case 0x00f5: return((CHAR)245); case 0x00f6: return((CHAR)246); case 0x00f8: return((CHAR)248); case 0x00fa: return((CHAR)250); case 0x00fb: return((CHAR)251); case 0x00fc: return((CHAR)252); case 0x00fd: return((CHAR)253); case 0x00fe: return((CHAR)254); case 0x0100: return((CHAR)192); case 0x0101: return((CHAR)224); case 0x0104: return((CHAR)161); case 0x0105: return((CHAR)177); case 0x010c: return((CHAR)200); case 0x010d: return((CHAR)232); case 0x0110: return((CHAR)208); case 0x0111: return((CHAR)176); case 0x0112: return((CHAR)162); case 0x0113: return((CHAR)178); case 0x0116: return((CHAR)204); case 0x0117: return((CHAR)236); case 0x0118: return((CHAR)202); case 0x0119: return((CHAR)234); case 0x0122: return((CHAR)163); case 0x0123: return((CHAR)179); case 0x0128: return((CHAR)165); case 0x0129: return((CHAR)181); case 0x012a: return((CHAR)164); case 0x012b: return((CHAR)180); case 0x012e: return((CHAR)199); case 0x012f: return((CHAR)231); case 0x0136: return((CHAR)166); case 0x0137: return((CHAR)182); case 0x0138: return((CHAR)174); case 0x013b: return((CHAR)167); case 0x013c: return((CHAR)183); case 0x0143: return((CHAR)168); case 0x0144: return((CHAR)184); case 0x0145: return((CHAR)209); case 0x014a: return((CHAR)175); case 0x014b: return((CHAR)191); case 0x014c: return((CHAR)210); case 0x014d: return((CHAR)242); case 0x0156: return((CHAR)169); case 0x0157: return((CHAR)185); case 0x0160: return((CHAR)170); case 0x0161: return((CHAR)186); case 0x0166: return((CHAR)171); case 0x0167: return((CHAR)187); case 0x0168: return((CHAR)215); case 0x0169: return((CHAR)247); case 0x016a: return((CHAR)223); case 0x016b: return((CHAR)255); case 0x0172: return((CHAR)217); case 0x0173: return((CHAR)249); case 0x017d: return((CHAR)172); case 0x017e: return((CHAR)188); default: return(tx_ident(c)); } } int /* ISO 8859-15 Latin-9 */ #ifdef CK_ANSIC tx_8859_15(USHORT c) #else tx_8859_15(c) USHORT c; #endif /* CK_ANSIC */ { if (c < 0xa0) return((CHAR)(c & 0xff)); switch(c) { case 0x20AC: return((CHAR)0xA4); /* Differs from Latin-1 in */ case 0x0160: return((CHAR)0xAC); /* only eight places */ case 0x0161: return((CHAR)0xA8); case 0x017D: return((CHAR)0xB4); case 0x017E: return((CHAR)0xB8); case 0x0152: return((CHAR)0xBC); case 0x0153: return((CHAR)0xBD); case 0x0178: return((CHAR)0xBE); default: return(tx_ident(c)); } } int /* Old KOI-8 (ECMA 113 First Ed.) */ #ifdef CK_ANSIC tx_koi8(USHORT c) #else tx_koi8(c) USHORT c; #endif /* CK_ANSIC */ { if (c < 0xa0) return((CHAR)(c & 0xff)); switch(c) { case 0x0410: return((CHAR)(225 & 0xff)); case 0x0411: return((CHAR)(226 & 0xff)); case 0x0412: return((CHAR)(247 & 0xff)); case 0x0413: return((CHAR)(231 & 0xff)); case 0x0414: return((CHAR)(228 & 0xff)); case 0x0415: return((CHAR)(229 & 0xff)); case 0x0416: return((CHAR)(246 & 0xff)); case 0x0417: return((CHAR)(250 & 0xff)); case 0x0418: return((CHAR)(233 & 0xff)); case 0x0419: return((CHAR)(234 & 0xff)); case 0x041a: return((CHAR)(235 & 0xff)); case 0x041b: return((CHAR)(236 & 0xff)); case 0x041c: return((CHAR)(237 & 0xff)); case 0x041d: return((CHAR)(238 & 0xff)); case 0x041e: return((CHAR)(239 & 0xff)); case 0x041f: return((CHAR)(240 & 0xff)); case 0x0420: return((CHAR)(242 & 0xff)); case 0x0421: return((CHAR)(243 & 0xff)); case 0x0422: return((CHAR)(244 & 0xff)); case 0x0423: return((CHAR)(245 & 0xff)); case 0x0424: return((CHAR)(230 & 0xff)); case 0x0425: return((CHAR)(232 & 0xff)); case 0x0426: return((CHAR)(227 & 0xff)); case 0x0427: return((CHAR)(254 & 0xff)); case 0x0428: return((CHAR)(251 & 0xff)); case 0x0429: return((CHAR)(253 & 0xff)); case 0x042b: return((CHAR)(249 & 0xff)); case 0x042c: return((CHAR)(248 & 0xff)); case 0x042d: return((CHAR)(252 & 0xff)); case 0x042e: return((CHAR)(224 & 0xff)); case 0x042f: return((CHAR)(241 & 0xff)); case 0x0430: return((CHAR)(193 & 0xff)); case 0x0431: return((CHAR)(194 & 0xff)); case 0x0432: return((CHAR)(215 & 0xff)); case 0x0433: return((CHAR)(199 & 0xff)); case 0x0434: return((CHAR)(196 & 0xff)); case 0x0435: return((CHAR)(197 & 0xff)); case 0x0436: return((CHAR)(214 & 0xff)); case 0x0437: return((CHAR)(218 & 0xff)); case 0x0438: return((CHAR)(201 & 0xff)); case 0x0439: return((CHAR)(202 & 0xff)); case 0x043a: return((CHAR)(203 & 0xff)); case 0x043b: return((CHAR)(204 & 0xff)); case 0x043c: return((CHAR)(205 & 0xff)); case 0x043d: return((CHAR)(206 & 0xff)); case 0x043e: return((CHAR)(207 & 0xff)); case 0x043f: return((CHAR)(208 & 0xff)); case 0x0440: return((CHAR)(210 & 0xff)); case 0x0441: return((CHAR)(211 & 0xff)); case 0x0442: return((CHAR)(212 & 0xff)); case 0x0443: return((CHAR)(213 & 0xff)); case 0x0444: return((CHAR)(198 & 0xff)); case 0x0445: return((CHAR)(200 & 0xff)); case 0x0446: return((CHAR)(195 & 0xff)); case 0x0447: return((CHAR)(222 & 0xff)); case 0x0448: return((CHAR)(219 & 0xff)); case 0x0449: return((CHAR)(221 & 0xff)); case 0x044a: return((CHAR)(223 & 0xff)); case 0x044b: return((CHAR)(217 & 0xff)); case 0x044c: return((CHAR)(216 & 0xff)); case 0x044d: return((CHAR)(220 & 0xff)); case 0x044e: return((CHAR)(192 & 0xff)); case 0x044f: return((CHAR)(209 & 0xff)); default: return(tx_ident(c)); } } int /* UCS-2 to KOI8-R (Russia) */ #ifdef CK_ANSIC tx_koi8r(USHORT c) #else tx_koi8r(c) USHORT c; #endif /* CK_ANSIC */ { if (c < 0x00A0) return((CHAR)(c & 0xff)); switch(c) { case 0x00A0: return((CHAR)(154 & 0xff)); case 0x00A9: return((CHAR)(191 & 0xff)); case 0x00B0: return((CHAR)(156 & 0xff)); case 0x00B2: return((CHAR)(157 & 0xff)); case 0x00B7: return((CHAR)(158 & 0xff)); case 0x00F7: return((CHAR)(159 & 0xff)); case 0x0401: return((CHAR)(179 & 0xff)); case 0x0410: return((CHAR)(225 & 0xff)); case 0x0411: return((CHAR)(226 & 0xff)); case 0x0412: return((CHAR)(247 & 0xff)); case 0x0413: return((CHAR)(231 & 0xff)); case 0x0414: return((CHAR)(228 & 0xff)); case 0x0415: return((CHAR)(229 & 0xff)); case 0x0416: return((CHAR)(246 & 0xff)); case 0x0417: return((CHAR)(250 & 0xff)); case 0x0418: return((CHAR)(233 & 0xff)); case 0x0419: return((CHAR)(234 & 0xff)); case 0x041A: return((CHAR)(235 & 0xff)); case 0x041B: return((CHAR)(236 & 0xff)); case 0x041C: return((CHAR)(237 & 0xff)); case 0x041D: return((CHAR)(238 & 0xff)); case 0x041E: return((CHAR)(239 & 0xff)); case 0x041F: return((CHAR)(240 & 0xff)); case 0x0420: return((CHAR)(242 & 0xff)); case 0x0421: return((CHAR)(243 & 0xff)); case 0x0422: return((CHAR)(244 & 0xff)); case 0x0423: return((CHAR)(245 & 0xff)); case 0x0424: return((CHAR)(230 & 0xff)); case 0x0425: return((CHAR)(232 & 0xff)); case 0x0426: return((CHAR)(227 & 0xff)); case 0x0427: return((CHAR)(254 & 0xff)); case 0x0428: return((CHAR)(251 & 0xff)); case 0x0429: return((CHAR)(253 & 0xff)); case 0x042A: return((CHAR)(255 & 0xff)); case 0x042B: return((CHAR)(249 & 0xff)); case 0x042C: return((CHAR)(248 & 0xff)); case 0x042D: return((CHAR)(252 & 0xff)); case 0x042E: return((CHAR)(224 & 0xff)); case 0x042F: return((CHAR)(241 & 0xff)); case 0x0430: return((CHAR)(193 & 0xff)); case 0x0431: return((CHAR)(194 & 0xff)); case 0x0432: return((CHAR)(215 & 0xff)); case 0x0433: return((CHAR)(199 & 0xff)); case 0x0434: return((CHAR)(196 & 0xff)); case 0x0435: return((CHAR)(197 & 0xff)); case 0x0436: return((CHAR)(214 & 0xff)); case 0x0437: return((CHAR)(218 & 0xff)); case 0x0438: return((CHAR)(201 & 0xff)); case 0x0439: return((CHAR)(202 & 0xff)); case 0x043A: return((CHAR)(203 & 0xff)); case 0x043B: return((CHAR)(204 & 0xff)); case 0x043C: return((CHAR)(205 & 0xff)); case 0x043D: return((CHAR)(206 & 0xff)); case 0x043E: return((CHAR)(207 & 0xff)); case 0x043F: return((CHAR)(208 & 0xff)); case 0x0440: return((CHAR)(210 & 0xff)); case 0x0441: return((CHAR)(211 & 0xff)); case 0x0442: return((CHAR)(212 & 0xff)); case 0x0443: return((CHAR)(213 & 0xff)); case 0x0444: return((CHAR)(198 & 0xff)); case 0x0445: return((CHAR)(200 & 0xff)); case 0x0446: return((CHAR)(195 & 0xff)); case 0x0447: return((CHAR)(222 & 0xff)); case 0x0448: return((CHAR)(219 & 0xff)); case 0x0449: return((CHAR)(221 & 0xff)); case 0x044A: return((CHAR)(223 & 0xff)); case 0x044B: return((CHAR)(217 & 0xff)); case 0x044C: return((CHAR)(216 & 0xff)); case 0x044D: return((CHAR)(220 & 0xff)); case 0x044E: return((CHAR)(192 & 0xff)); case 0x044F: return((CHAR)(209 & 0xff)); case 0x0451: return((CHAR)(163 & 0xff)); case 0x2219: return((CHAR)(149 & 0xff)); case 0x221A: return((CHAR)(150 & 0xff)); case 0x2248: return((CHAR)(151 & 0xff)); case 0x2264: return((CHAR)(152 & 0xff)); case 0x2265: return((CHAR)(153 & 0xff)); case 0x2320: return((CHAR)(147 & 0xff)); case 0x2321: return((CHAR)(155 & 0xff)); case 0x2500: return((CHAR)(128 & 0xff)); case 0x2502: return((CHAR)(129 & 0xff)); case 0x250C: return((CHAR)(130 & 0xff)); case 0x2510: return((CHAR)(131 & 0xff)); case 0x2514: return((CHAR)(132 & 0xff)); case 0x2518: return((CHAR)(133 & 0xff)); case 0x251C: return((CHAR)(134 & 0xff)); case 0x2524: return((CHAR)(135 & 0xff)); case 0x252C: return((CHAR)(136 & 0xff)); case 0x2534: return((CHAR)(137 & 0xff)); case 0x253C: return((CHAR)(138 & 0xff)); case 0x2550: return((CHAR)(160 & 0xff)); case 0x2551: return((CHAR)(161 & 0xff)); case 0x2552: return((CHAR)(162 & 0xff)); case 0x2553: return((CHAR)(164 & 0xff)); case 0x2554: return((CHAR)(165 & 0xff)); case 0x2555: return((CHAR)(166 & 0xff)); case 0x2556: return((CHAR)(167 & 0xff)); case 0x2557: return((CHAR)(168 & 0xff)); case 0x2558: return((CHAR)(169 & 0xff)); case 0x2559: return((CHAR)(170 & 0xff)); case 0x255A: return((CHAR)(171 & 0xff)); case 0x255B: return((CHAR)(172 & 0xff)); case 0x255C: return((CHAR)(173 & 0xff)); case 0x255D: return((CHAR)(174 & 0xff)); case 0x255E: return((CHAR)(175 & 0xff)); case 0x255F: return((CHAR)(176 & 0xff)); case 0x2560: return((CHAR)(177 & 0xff)); case 0x2561: return((CHAR)(178 & 0xff)); case 0x2562: return((CHAR)(180 & 0xff)); case 0x2563: return((CHAR)(181 & 0xff)); case 0x2564: return((CHAR)(182 & 0xff)); case 0x2565: return((CHAR)(183 & 0xff)); case 0x2566: return((CHAR)(184 & 0xff)); case 0x2567: return((CHAR)(185 & 0xff)); case 0x2568: return((CHAR)(186 & 0xff)); case 0x2569: return((CHAR)(187 & 0xff)); case 0x256A: return((CHAR)(188 & 0xff)); case 0x256B: return((CHAR)(189 & 0xff)); case 0x256C: return((CHAR)(190 & 0xff)); case 0x2580: return((CHAR)(139 & 0xff)); case 0x2584: return((CHAR)(140 & 0xff)); case 0x2588: return((CHAR)(141 & 0xff)); case 0x258C: return((CHAR)(142 & 0xff)); case 0x2590: return((CHAR)(143 & 0xff)); case 0x2591: return((CHAR)(144 & 0xff)); case 0x2592: return((CHAR)(145 & 0xff)); case 0x2593: return((CHAR)(146 & 0xff)); case 0x25A0: return((CHAR)(148 & 0xff)); default: return(tx_ident(c)); } } int /* KOI8-U (Ukraine) */ #ifdef CK_ANSIC tx_koi8u(USHORT c) #else tx_koi8u(c) USHORT c; #endif /* CK_ANSIC */ { if (c < 0xa0) return((CHAR)(c & 0xff)); switch(c) { case 0x00A0: return((CHAR)(154 & 0xff)); case 0x00A9: return((CHAR)(191 & 0xff)); case 0x00B0: return((CHAR)(156 & 0xff)); case 0x00B2: return((CHAR)(157 & 0xff)); case 0x00B7: return((CHAR)(158 & 0xff)); case 0x00F7: return((CHAR)(159 & 0xff)); case 0x0401: return((CHAR)(179 & 0xff)); case 0x0404: return((CHAR)(180 & 0xff)); case 0x0406: return((CHAR)(182 & 0xff)); case 0x0407: return((CHAR)(183 & 0xff)); case 0x0410: return((CHAR)(225 & 0xff)); case 0x0411: return((CHAR)(226 & 0xff)); case 0x0412: return((CHAR)(247 & 0xff)); case 0x0413: return((CHAR)(231 & 0xff)); case 0x0414: return((CHAR)(228 & 0xff)); case 0x0415: return((CHAR)(229 & 0xff)); case 0x0416: return((CHAR)(246 & 0xff)); case 0x0417: return((CHAR)(250 & 0xff)); case 0x0418: return((CHAR)(233 & 0xff)); case 0x0419: return((CHAR)(234 & 0xff)); case 0x041A: return((CHAR)(235 & 0xff)); case 0x041B: return((CHAR)(236 & 0xff)); case 0x041C: return((CHAR)(237 & 0xff)); case 0x041D: return((CHAR)(238 & 0xff)); case 0x041E: return((CHAR)(239 & 0xff)); case 0x041F: return((CHAR)(240 & 0xff)); case 0x0420: return((CHAR)(242 & 0xff)); case 0x0421: return((CHAR)(243 & 0xff)); case 0x0422: return((CHAR)(244 & 0xff)); case 0x0423: return((CHAR)(245 & 0xff)); case 0x0424: return((CHAR)(230 & 0xff)); case 0x0425: return((CHAR)(232 & 0xff)); case 0x0426: return((CHAR)(227 & 0xff)); case 0x0427: return((CHAR)(254 & 0xff)); case 0x0428: return((CHAR)(251 & 0xff)); case 0x0429: return((CHAR)(253 & 0xff)); case 0x042A: return((CHAR)(255 & 0xff)); case 0x042B: return((CHAR)(249 & 0xff)); case 0x042C: return((CHAR)(248 & 0xff)); case 0x042D: return((CHAR)(252 & 0xff)); case 0x042E: return((CHAR)(224 & 0xff)); case 0x042F: return((CHAR)(241 & 0xff)); case 0x0430: return((CHAR)(193 & 0xff)); case 0x0431: return((CHAR)(194 & 0xff)); case 0x0432: return((CHAR)(215 & 0xff)); case 0x0433: return((CHAR)(199 & 0xff)); case 0x0434: return((CHAR)(196 & 0xff)); case 0x0435: return((CHAR)(197 & 0xff)); case 0x0436: return((CHAR)(214 & 0xff)); case 0x0437: return((CHAR)(218 & 0xff)); case 0x0438: return((CHAR)(201 & 0xff)); case 0x0439: return((CHAR)(202 & 0xff)); case 0x043A: return((CHAR)(203 & 0xff)); case 0x043B: return((CHAR)(204 & 0xff)); case 0x043C: return((CHAR)(205 & 0xff)); case 0x043D: return((CHAR)(206 & 0xff)); case 0x043E: return((CHAR)(207 & 0xff)); case 0x043F: return((CHAR)(208 & 0xff)); case 0x0440: return((CHAR)(210 & 0xff)); case 0x0441: return((CHAR)(211 & 0xff)); case 0x0442: return((CHAR)(212 & 0xff)); case 0x0443: return((CHAR)(213 & 0xff)); case 0x0444: return((CHAR)(198 & 0xff)); case 0x0445: return((CHAR)(200 & 0xff)); case 0x0446: return((CHAR)(195 & 0xff)); case 0x0447: return((CHAR)(222 & 0xff)); case 0x0448: return((CHAR)(219 & 0xff)); case 0x0449: return((CHAR)(221 & 0xff)); case 0x044A: return((CHAR)(223 & 0xff)); case 0x044B: return((CHAR)(217 & 0xff)); case 0x044C: return((CHAR)(216 & 0xff)); case 0x044D: return((CHAR)(220 & 0xff)); case 0x044E: return((CHAR)(192 & 0xff)); case 0x044F: return((CHAR)(209 & 0xff)); case 0x0451: return((CHAR)(163 & 0xff)); case 0x0454: return((CHAR)(164 & 0xff)); case 0x0456: return((CHAR)(166 & 0xff)); case 0x0457: return((CHAR)(167 & 0xff)); case 0x0490: return((CHAR)(189 & 0xff)); case 0x0491: return((CHAR)(173 & 0xff)); case 0x2219: return((CHAR)(149 & 0xff)); case 0x221A: return((CHAR)(150 & 0xff)); case 0x2248: return((CHAR)(151 & 0xff)); case 0x2264: return((CHAR)(152 & 0xff)); case 0x2265: return((CHAR)(153 & 0xff)); case 0x2320: return((CHAR)(147 & 0xff)); case 0x2321: return((CHAR)(155 & 0xff)); case 0x2500: return((CHAR)(128 & 0xff)); case 0x2502: return((CHAR)(129 & 0xff)); case 0x250C: return((CHAR)(130 & 0xff)); case 0x2510: return((CHAR)(131 & 0xff)); case 0x2514: return((CHAR)(132 & 0xff)); case 0x2518: return((CHAR)(133 & 0xff)); case 0x251C: return((CHAR)(134 & 0xff)); case 0x2524: return((CHAR)(135 & 0xff)); case 0x252C: return((CHAR)(136 & 0xff)); case 0x2534: return((CHAR)(137 & 0xff)); case 0x253C: return((CHAR)(138 & 0xff)); case 0x2550: return((CHAR)(160 & 0xff)); case 0x2551: return((CHAR)(161 & 0xff)); case 0x2552: return((CHAR)(162 & 0xff)); case 0x2554: return((CHAR)(165 & 0xff)); case 0x2557: return((CHAR)(168 & 0xff)); case 0x2558: return((CHAR)(169 & 0xff)); case 0x2559: return((CHAR)(170 & 0xff)); case 0x255A: return((CHAR)(171 & 0xff)); case 0x255B: return((CHAR)(172 & 0xff)); case 0x255D: return((CHAR)(174 & 0xff)); case 0x255E: return((CHAR)(175 & 0xff)); case 0x255F: return((CHAR)(176 & 0xff)); case 0x2560: return((CHAR)(177 & 0xff)); case 0x2561: return((CHAR)(178 & 0xff)); case 0x2563: return((CHAR)(181 & 0xff)); case 0x2566: return((CHAR)(184 & 0xff)); case 0x2567: return((CHAR)(185 & 0xff)); case 0x2568: return((CHAR)(186 & 0xff)); case 0x2569: return((CHAR)(187 & 0xff)); case 0x256A: return((CHAR)(188 & 0xff)); case 0x256C: return((CHAR)(190 & 0xff)); case 0x2580: return((CHAR)(139 & 0xff)); case 0x2584: return((CHAR)(140 & 0xff)); case 0x2588: return((CHAR)(141 & 0xff)); case 0x258C: return((CHAR)(142 & 0xff)); case 0x2590: return((CHAR)(143 & 0xff)); case 0x2591: return((CHAR)(144 & 0xff)); case 0x2592: return((CHAR)(145 & 0xff)); case 0x2593: return((CHAR)(146 & 0xff)); case 0x25A0: return((CHAR)(148 & 0xff)); default: return(tx_ident(c)); } } int /* DEC MCS */ #ifdef CK_ANSIC tx_decmcs(USHORT c) #else tx_decmcs(c) USHORT c; #endif /* CK_ANSIC */ { if (c < 0xa0) return((CHAR)(c & 0xff)); switch(c) { case 0x00a6: case 0x00a8: case 0x00ac: case 0x00ae: case 0x00af: case 0x00b4: case 0x00b8: case 0x00be: case 0x00d0: case 0x00de: case 0x00f0: case 0x00fe: case 0x00ff: return(-1); /* These are all undefined in DECMCS */ case 0x00a4: /* Currency sign */ return((CHAR)0xa8); case 0x0152: /* OE */ return((CHAR)0xd7); case 0x0153: /* oe */ return((CHAR)0xf7); default: return(tx_ident(c)); } } int /* NeXTSTEP */ #ifdef CK_ANSIC tx_nextstep(USHORT c) #else tx_nextstep(c) USHORT c; #endif /* CK_ANSIC */ { if (c < 0x80) /* Has C1 graphics */ return((CHAR)(c & 0xff)); switch (c) { case 0x00a0: return((CHAR)(128 & 0xff)); case 0x00a1: return((CHAR)(161 & 0xff)); case 0x00a2: return((CHAR)(162 & 0xff)); case 0x00a3: return((CHAR)(163 & 0xff)); case 0x00a4: return((CHAR)(168 & 0xff)); case 0x00a5: return((CHAR)(165 & 0xff)); case 0x00a6: return((CHAR)(181 & 0xff)); case 0x00a7: return((CHAR)(167 & 0xff)); case 0x00a8: return((CHAR)(200 & 0xff)); case 0x00a9: return((CHAR)(160 & 0xff)); case 0x00aa: return((CHAR)(227 & 0xff)); case 0x00ab: return((CHAR)(171 & 0xff)); case 0x00ac: return((CHAR)(190 & 0xff)); case 0x00ae: return((CHAR)(176 & 0xff)); case 0x00af: return((CHAR)(197 & 0xff)); case 0x00b1: return((CHAR)(209 & 0xff)); case 0x00b2: return((CHAR)(201 & 0xff)); case 0x00b3: return((CHAR)(204 & 0xff)); case 0x00b4: return((CHAR)(194 & 0xff)); case 0x00b5: return((CHAR)(157 & 0xff)); case 0x00b6: return((CHAR)(182 & 0xff)); case 0x00b7: return((CHAR)(180 & 0xff)); case 0x00b8: return((CHAR)(203 & 0xff)); case 0x00b9: return((CHAR)(192 & 0xff)); case 0x00ba: return((CHAR)(235 & 0xff)); case 0x00bb: return((CHAR)(187 & 0xff)); case 0x00bc: return((CHAR)(210 & 0xff)); case 0x00bd: return((CHAR)(211 & 0xff)); case 0x00be: return((CHAR)(212 & 0xff)); case 0x00bf: return((CHAR)(191 & 0xff)); case 0x00c0: return((CHAR)(129 & 0xff)); case 0x00c1: return((CHAR)(130 & 0xff)); case 0x00c2: return((CHAR)(131 & 0xff)); case 0x00c3: return((CHAR)(132 & 0xff)); case 0x00c4: return((CHAR)(133 & 0xff)); case 0x00c5: return((CHAR)(134 & 0xff)); case 0x00c6: return((CHAR)(225 & 0xff)); case 0x00c7: return((CHAR)(135 & 0xff)); case 0x00c8: return((CHAR)(136 & 0xff)); case 0x00c9: return((CHAR)(137 & 0xff)); case 0x00ca: return((CHAR)(138 & 0xff)); case 0x00cb: return((CHAR)(139 & 0xff)); case 0x00cc: return((CHAR)(140 & 0xff)); case 0x00cd: return((CHAR)(141 & 0xff)); case 0x00ce: return((CHAR)(142 & 0xff)); case 0x00cf: return((CHAR)(143 & 0xff)); case 0x00d0: return((CHAR)(144 & 0xff)); case 0x00d1: return((CHAR)(145 & 0xff)); case 0x00d2: return((CHAR)(146 & 0xff)); case 0x00d3: return((CHAR)(147 & 0xff)); case 0x00d4: return((CHAR)(148 & 0xff)); case 0x00d5: return((CHAR)(149 & 0xff)); case 0x00d6: return((CHAR)(150 & 0xff)); case 0x00d7: return((CHAR)(158 & 0xff)); case 0x00d8: return((CHAR)(233 & 0xff)); case 0x00d9: return((CHAR)(151 & 0xff)); case 0x00da: return((CHAR)(152 & 0xff)); case 0x00db: return((CHAR)(153 & 0xff)); case 0x00dc: return((CHAR)(154 & 0xff)); case 0x00dd: return((CHAR)(155 & 0xff)); case 0x00de: return((CHAR)(156 & 0xff)); case 0x00df: return((CHAR)(251 & 0xff)); case 0x00e0: return((CHAR)(213 & 0xff)); case 0x00e1: return((CHAR)(214 & 0xff)); case 0x00e2: return((CHAR)(215 & 0xff)); case 0x00e3: return((CHAR)(216 & 0xff)); case 0x00e4: return((CHAR)(217 & 0xff)); case 0x00e5: return((CHAR)(218 & 0xff)); case 0x00e6: return((CHAR)(241 & 0xff)); case 0x00e7: return((CHAR)(219 & 0xff)); case 0x00e8: return((CHAR)(220 & 0xff)); case 0x00e9: return((CHAR)(221 & 0xff)); case 0x00ea: return((CHAR)(222 & 0xff)); case 0x00eb: return((CHAR)(223 & 0xff)); case 0x00ec: return((CHAR)(224 & 0xff)); case 0x00ed: return((CHAR)(226 & 0xff)); case 0x00ee: return((CHAR)(228 & 0xff)); case 0x00ef: return((CHAR)(229 & 0xff)); case 0x00f0: return((CHAR)(230 & 0xff)); case 0x00f1: return((CHAR)(231 & 0xff)); case 0x00f2: return((CHAR)(236 & 0xff)); case 0x00f3: return((CHAR)(237 & 0xff)); case 0x00f4: return((CHAR)(238 & 0xff)); case 0x00f5: return((CHAR)(239 & 0xff)); case 0x00f6: return((CHAR)(240 & 0xff)); case 0x00f7: return((CHAR)(159 & 0xff)); case 0x00f8: return((CHAR)(249 & 0xff)); case 0x00f9: return((CHAR)(242 & 0xff)); case 0x00fa: return((CHAR)(243 & 0xff)); case 0x00fb: return((CHAR)(244 & 0xff)); case 0x00fc: return((CHAR)(246 & 0xff)); case 0x00fd: return((CHAR)(247 & 0xff)); case 0x00fe: return((CHAR)(252 & 0xff)); case 0x00ff: return((CHAR)(253 & 0xff)); case 0x0131: return((CHAR)(245 & 0xff)); case 0x0141: return((CHAR)(232 & 0xff)); case 0x0142: return((CHAR)(248 & 0xff)); case 0x0152: return((CHAR)(234 & 0xff)); case 0x0153: return((CHAR)(250 & 0xff)); case 0x0192: return((CHAR)(166 & 0xff)); case 0x02c6: return((CHAR)(195 & 0xff)); case 0x02c7: return((CHAR)(207 & 0xff)); case 0x02cb: return((CHAR)(193 & 0xff)); case 0x02d8: return((CHAR)(198 & 0xff)); case 0x02d9: return((CHAR)(199 & 0xff)); case 0x02da: return((CHAR)(202 & 0xff)); case 0x02db: return((CHAR)(206 & 0xff)); case 0x02dc: return((CHAR)(196 & 0xff)); case 0x02dd: return((CHAR)(205 & 0xff)); case 0x2013: return((CHAR)(177 & 0xff)); case 0x2014: return((CHAR)(208 & 0xff)); case 0x2019: return((CHAR)(169 & 0xff)); case 0x201a: return((CHAR)(184 & 0xff)); case 0x201c: return((CHAR)(170 & 0xff)); case 0x201d: return((CHAR)(186 & 0xff)); case 0x201e: return((CHAR)(185 & 0xff)); case 0x2020: return((CHAR)(178 & 0xff)); case 0x2021: return((CHAR)(179 & 0xff)); case 0x2022: return((CHAR)(183 & 0xff)); case 0x2026: return((CHAR)(188 & 0xff)); case 0x2030: return((CHAR)(189 & 0xff)); case 0x2039: return((CHAR)(172 & 0xff)); case 0x203a: return((CHAR)(173 & 0xff)); case 0x2044: return((CHAR)(164 & 0xff)); case 0xfb01: return((CHAR)(174 & 0xff)); case 0xfb02: return((CHAR)(175 & 0xff)); default: return(tx_punc(c)); } } int /* DG International */ #ifdef CK_ANSIC tx_dgi(USHORT c) #else tx_dgi(c) USHORT c; #endif /* CK_ANSIC */ { if (c < 0xa0) return((CHAR)(c & 0xff)); switch(c) { case 0x00a0: return((CHAR)(160 & 0xff)); case 0x00a1: return((CHAR)(171 & 0xff)); case 0x00a2: return((CHAR)(167 & 0xff)); case 0x00a3: return((CHAR)(168 & 0xff)); case 0x00a4: return((CHAR)(166 & 0xff)); case 0x00a5: return((CHAR)(181 & 0xff)); case 0x00a7: return((CHAR)(187 & 0xff)); case 0x00a8: return((CHAR)(189 & 0xff)); case 0x00a9: return((CHAR)(173 & 0xff)); case 0x00aa: return((CHAR)(169 & 0xff)); case 0x00ab: return((CHAR)(177 & 0xff)); case 0x00ac: return((CHAR)(161 & 0xff)); case 0x00ae: return((CHAR)(174 & 0xff)); case 0x00b0: return((CHAR)(188 & 0xff)); case 0x00b1: return((CHAR)(182 & 0xff)); case 0x00b2: return((CHAR)(164 & 0xff)); case 0x00b3: return((CHAR)(165 & 0xff)); case 0x00b4: return((CHAR)(190 & 0xff)); case 0x00b5: return((CHAR)(163 & 0xff)); case 0x00b6: return((CHAR)(178 & 0xff)); case 0x00b7: return((CHAR)(185 & 0xff)); case 0x00b8: return((CHAR)(186 & 0xff)); case 0x00ba: return((CHAR)(170 & 0xff)); case 0x00bb: return((CHAR)(176 & 0xff)); case 0x00bd: return((CHAR)(162 & 0xff)); case 0x00bf: return((CHAR)(172 & 0xff)); case 0x00c0: return((CHAR)(193 & 0xff)); case 0x00c1: return((CHAR)(192 & 0xff)); case 0x00c2: return((CHAR)(194 & 0xff)); case 0x00c3: return((CHAR)(196 & 0xff)); case 0x00c4: return((CHAR)(195 & 0xff)); case 0x00c5: return((CHAR)(197 & 0xff)); case 0x00c6: return((CHAR)(198 & 0xff)); case 0x00c7: return((CHAR)(199 & 0xff)); case 0x00c8: return((CHAR)(201 & 0xff)); case 0x00c9: return((CHAR)(200 & 0xff)); case 0x00ca: return((CHAR)(202 & 0xff)); case 0x00cb: return((CHAR)(203 & 0xff)); case 0x00cc: return((CHAR)(205 & 0xff)); case 0x00cd: return((CHAR)(204 & 0xff)); case 0x00ce: return((CHAR)(206 & 0xff)); case 0x00cf: return((CHAR)(207 & 0xff)); case 0x00d1: return((CHAR)(208 & 0xff)); case 0x00d2: return((CHAR)(210 & 0xff)); case 0x00d3: return((CHAR)(209 & 0xff)); case 0x00d4: return((CHAR)(211 & 0xff)); case 0x00d5: return((CHAR)(213 & 0xff)); case 0x00d6: return((CHAR)(212 & 0xff)); case 0x00d8: return((CHAR)(214 & 0xff)); case 0x00d9: return((CHAR)(217 & 0xff)); case 0x00da: return((CHAR)(216 & 0xff)); case 0x00db: return((CHAR)(218 & 0xff)); case 0x00dc: return((CHAR)(219 & 0xff)); case 0x00df: return((CHAR)(252 & 0xff)); case 0x00e0: return((CHAR)(225 & 0xff)); case 0x00e1: return((CHAR)(224 & 0xff)); case 0x00e2: return((CHAR)(226 & 0xff)); case 0x00e3: return((CHAR)(228 & 0xff)); case 0x00e4: return((CHAR)(227 & 0xff)); case 0x00e5: return((CHAR)(229 & 0xff)); case 0x00e6: return((CHAR)(230 & 0xff)); case 0x00e7: return((CHAR)(231 & 0xff)); case 0x00e8: return((CHAR)(233 & 0xff)); case 0x00e9: return((CHAR)(232 & 0xff)); case 0x00ea: return((CHAR)(234 & 0xff)); case 0x00eb: return((CHAR)(235 & 0xff)); case 0x00ec: return((CHAR)(237 & 0xff)); case 0x00ed: return((CHAR)(236 & 0xff)); case 0x00ee: return((CHAR)(238 & 0xff)); case 0x00ef: return((CHAR)(239 & 0xff)); case 0x00f1: return((CHAR)(240 & 0xff)); case 0x00f2: return((CHAR)(242 & 0xff)); case 0x00f3: return((CHAR)(241 & 0xff)); case 0x00f4: return((CHAR)(243 & 0xff)); case 0x00f5: return((CHAR)(245 & 0xff)); case 0x00f6: return((CHAR)(244 & 0xff)); case 0x00f8: return((CHAR)(246 & 0xff)); case 0x00f9: return((CHAR)(249 & 0xff)); case 0x00fa: return((CHAR)(248 & 0xff)); case 0x00fb: return((CHAR)(250 & 0xff)); case 0x00fc: return((CHAR)(251 & 0xff)); case 0x00ff: return((CHAR)(253 & 0xff)); case 0x0153: return((CHAR)(247 & 0xff)); case 0x0178: return((CHAR)(221 & 0xff)); case 0x0192: return((CHAR)(180 & 0xff)); case 0x0276: return((CHAR)(215 & 0xff)); case 0x2021: return((CHAR)(175 & 0xff)); case 0x2122: return((CHAR)(179 & 0xff)); case 0x2191: return((CHAR)(191 & 0xff)); case 0x2264: return((CHAR)(183 & 0xff)); case 0x2265: return((CHAR)(184 & 0xff)); case 0x2588: return((CHAR)(255 & 0xff)); default: return(tx_punc(c)); } } int /* Macintosh Latin */ #ifdef CK_ANSIC tx_maclatin(USHORT c) #else tx_maclatin(c) USHORT c; #endif /* CK_ANSIC */ { if (c < 0x80) /* Has C1 graphics */ return((CHAR)(c & 0xff)); switch (c) { case 0x00a0: return((CHAR)(202 & 0xff)); case 0x00a1: return((CHAR)(193 & 0xff)); case 0x00a2: return((CHAR)(162 & 0xff)); case 0x00a3: return((CHAR)(163 & 0xff)); case 0x00a4: return((CHAR)(219 & 0xff)); case 0x00a5: return((CHAR)(180 & 0xff)); case 0x00a7: return((CHAR)(164 & 0xff)); case 0x00a8: return((CHAR)(172 & 0xff)); case 0x00a9: return((CHAR)(169 & 0xff)); case 0x00aa: return((CHAR)(187 & 0xff)); case 0x00ab: return((CHAR)(199 & 0xff)); case 0x00ac: return((CHAR)(194 & 0xff)); case 0x00ae: return((CHAR)(168 & 0xff)); case 0x00af: return((CHAR)(248 & 0xff)); case 0x00b0: return((CHAR)(161 & 0xff)); case 0x00b1: return((CHAR)(177 & 0xff)); case 0x00b4: return((CHAR)(171 & 0xff)); case 0x00b5: return((CHAR)(181 & 0xff)); case 0x00b6: return((CHAR)(166 & 0xff)); case 0x00b7: return((CHAR)(225 & 0xff)); case 0x00b8: return((CHAR)(252 & 0xff)); case 0x00ba: return((CHAR)(188 & 0xff)); case 0x00bb: return((CHAR)(200 & 0xff)); case 0x00bf: return((CHAR)(192 & 0xff)); case 0x00c0: return((CHAR)(203 & 0xff)); case 0x00c1: return((CHAR)(231 & 0xff)); case 0x00c2: return((CHAR)(229 & 0xff)); case 0x00c3: return((CHAR)(204 & 0xff)); case 0x00c4: return((CHAR)(128 & 0xff)); case 0x00c5: return((CHAR)(129 & 0xff)); case 0x00c6: return((CHAR)(174 & 0xff)); case 0x00c7: return((CHAR)(130 & 0xff)); case 0x00c8: return((CHAR)(233 & 0xff)); case 0x00c9: return((CHAR)(131 & 0xff)); case 0x00ca: return((CHAR)(230 & 0xff)); case 0x00cb: return((CHAR)(232 & 0xff)); case 0x00cc: return((CHAR)(237 & 0xff)); case 0x00cd: return((CHAR)(234 & 0xff)); case 0x00ce: return((CHAR)(235 & 0xff)); case 0x00cf: return((CHAR)(236 & 0xff)); case 0x00d0: return((CHAR)(220 & 0xff)); case 0x00d1: return((CHAR)(132 & 0xff)); case 0x00d2: return((CHAR)(241 & 0xff)); case 0x00d3: return((CHAR)(238 & 0xff)); case 0x00d4: return((CHAR)(239 & 0xff)); case 0x00d5: return((CHAR)(205 & 0xff)); case 0x00d6: return((CHAR)(133 & 0xff)); case 0x00d8: return((CHAR)(175 & 0xff)); case 0x00d9: return((CHAR)(244 & 0xff)); case 0x00da: return((CHAR)(242 & 0xff)); case 0x00db: return((CHAR)(243 & 0xff)); case 0x00dc: return((CHAR)(134 & 0xff)); case 0x00dd: return((CHAR)(160 & 0xff)); case 0x00de: return((CHAR)(222 & 0xff)); case 0x00df: return((CHAR)(167 & 0xff)); case 0x00e0: return((CHAR)(136 & 0xff)); case 0x00e1: return((CHAR)(135 & 0xff)); case 0x00e2: return((CHAR)(137 & 0xff)); case 0x00e3: return((CHAR)(139 & 0xff)); case 0x00e4: return((CHAR)(138 & 0xff)); case 0x00e5: return((CHAR)(140 & 0xff)); case 0x00e6: return((CHAR)(190 & 0xff)); case 0x00e7: return((CHAR)(141 & 0xff)); case 0x00e8: return((CHAR)(143 & 0xff)); case 0x00e9: return((CHAR)(142 & 0xff)); case 0x00ea: return((CHAR)(144 & 0xff)); case 0x00eb: return((CHAR)(145 & 0xff)); case 0x00ec: return((CHAR)(147 & 0xff)); case 0x00ed: return((CHAR)(146 & 0xff)); case 0x00ee: return((CHAR)(148 & 0xff)); case 0x00ef: return((CHAR)(149 & 0xff)); case 0x00f0: return((CHAR)(221 & 0xff)); case 0x00f1: return((CHAR)(150 & 0xff)); case 0x00f2: return((CHAR)(152 & 0xff)); case 0x00f3: return((CHAR)(151 & 0xff)); case 0x00f4: return((CHAR)(153 & 0xff)); case 0x00f5: return((CHAR)(155 & 0xff)); case 0x00f6: return((CHAR)(154 & 0xff)); case 0x00f7: return((CHAR)(214 & 0xff)); case 0x00f8: return((CHAR)(191 & 0xff)); case 0x00f9: return((CHAR)(157 & 0xff)); case 0x00fa: return((CHAR)(156 & 0xff)); case 0x00fb: return((CHAR)(158 & 0xff)); case 0x00fc: return((CHAR)(159 & 0xff)); case 0x00fd: return((CHAR)(224 & 0xff)); case 0x00fe: return((CHAR)(223 & 0xff)); case 0x00ff: return((CHAR)(216 & 0xff)); case 0x0131: return((CHAR)(245 & 0xff)); case 0x0152: return((CHAR)(206 & 0xff)); case 0x0153: return((CHAR)(207 & 0xff)); case 0x0178: return((CHAR)(217 & 0xff)); case 0x0192: return((CHAR)(196 & 0xff)); case 0x02c6: return((CHAR)(246 & 0xff)); case 0x02c7: return((CHAR)(255 & 0xff)); case 0x02d8: return((CHAR)(249 & 0xff)); case 0x02d9: return((CHAR)(250 & 0xff)); case 0x02da: return((CHAR)(251 & 0xff)); case 0x02db: return((CHAR)(254 & 0xff)); case 0x02dc: return((CHAR)(247 & 0xff)); case 0x02dd: return((CHAR)(253 & 0xff)); case 0x03c0: return((CHAR)(185 & 0xff)); case 0x2013: return((CHAR)(208 & 0xff)); case 0x2014: return((CHAR)(209 & 0xff)); case 0x2018: return((CHAR)(212 & 0xff)); case 0x2019: return((CHAR)(213 & 0xff)); case 0x201a: return((CHAR)(226 & 0xff)); case 0x201c: return((CHAR)(210 & 0xff)); case 0x201d: return((CHAR)(211 & 0xff)); case 0x201e: return((CHAR)(227 & 0xff)); case 0x2022: return((CHAR)(165 & 0xff)); case 0x2026: return((CHAR)(201 & 0xff)); case 0x2030: return((CHAR)(228 & 0xff)); case 0x2044: return((CHAR)(218 & 0xff)); case 0x2122: return((CHAR)(170 & 0xff)); case 0x2126: return((CHAR)(189 & 0xff)); case 0x2202: return((CHAR)(182 & 0xff)); case 0x2206: return((CHAR)(198 & 0xff)); case 0x220f: return((CHAR)(184 & 0xff)); case 0x2211: return((CHAR)(183 & 0xff)); case 0x221a: return((CHAR)(195 & 0xff)); case 0x221e: return((CHAR)(176 & 0xff)); case 0x222b: return((CHAR)(186 & 0xff)); case 0x2248: return((CHAR)(197 & 0xff)); case 0x2260: return((CHAR)(173 & 0xff)); case 0x2264: return((CHAR)(178 & 0xff)); case 0x2265: return((CHAR)(179 & 0xff)); case 0x25ca: return((CHAR)(215 & 0xff)); case 0xf8ff: return((CHAR)(240 & 0xff)); default: return(tx_punc(c)); } } int /* Apple QuickDraw / CP10000 */ #ifdef CK_ANSIC tx_quickdraw(USHORT c) #else tx_quickdraw(c) USHORT c; #endif /* CK_ANSIC */ { if (c < 0x80) /* Has C1 graphics */ return((CHAR)(c & 0xff)); switch (c) { case 0x00a0: return((CHAR)(202 & 0xff)); case 0x00a1: return((CHAR)(193 & 0xff)); case 0x00a2: return((CHAR)(162 & 0xff)); case 0x00a3: return((CHAR)(163 & 0xff)); case 0x00a4: return((CHAR)(219 & 0xff)); case 0x00a5: return((CHAR)(180 & 0xff)); case 0x00a7: return((CHAR)(164 & 0xff)); case 0x00a8: return((CHAR)(172 & 0xff)); case 0x00a9: return((CHAR)(169 & 0xff)); case 0x00aa: return((CHAR)(187 & 0xff)); case 0x00ab: return((CHAR)(199 & 0xff)); case 0x00ac: return((CHAR)(194 & 0xff)); case 0x00ae: return((CHAR)(168 & 0xff)); case 0x00af: return((CHAR)(248 & 0xff)); case 0x00b0: return((CHAR)(161 & 0xff)); case 0x00b1: return((CHAR)(177 & 0xff)); case 0x00b4: return((CHAR)(171 & 0xff)); case 0x00b5: return((CHAR)(181 & 0xff)); case 0x00b6: return((CHAR)(166 & 0xff)); case 0x00b7: return((CHAR)(225 & 0xff)); case 0x00b8: return((CHAR)(252 & 0xff)); case 0x00ba: return((CHAR)(188 & 0xff)); case 0x00bb: return((CHAR)(200 & 0xff)); case 0x00bf: return((CHAR)(192 & 0xff)); case 0x00c0: return((CHAR)(203 & 0xff)); case 0x00c1: return((CHAR)(231 & 0xff)); case 0x00c2: return((CHAR)(229 & 0xff)); case 0x00c3: return((CHAR)(204 & 0xff)); case 0x00c4: return((CHAR)(128 & 0xff)); case 0x00c5: return((CHAR)(129 & 0xff)); case 0x00c6: return((CHAR)(174 & 0xff)); case 0x00c7: return((CHAR)(130 & 0xff)); case 0x00c8: return((CHAR)(233 & 0xff)); case 0x00c9: return((CHAR)(131 & 0xff)); case 0x00ca: return((CHAR)(230 & 0xff)); case 0x00cb: return((CHAR)(232 & 0xff)); case 0x00cc: return((CHAR)(237 & 0xff)); case 0x00cd: return((CHAR)(234 & 0xff)); case 0x00ce: return((CHAR)(235 & 0xff)); case 0x00cf: return((CHAR)(236 & 0xff)); case 0x2039: return((CHAR)(220 & 0xff)); case 0x00d1: return((CHAR)(132 & 0xff)); case 0x00d2: return((CHAR)(241 & 0xff)); case 0x00d3: return((CHAR)(238 & 0xff)); case 0x00d4: return((CHAR)(239 & 0xff)); case 0x00d5: return((CHAR)(205 & 0xff)); case 0x00d6: return((CHAR)(133 & 0xff)); case 0x00d8: return((CHAR)(175 & 0xff)); case 0x00d9: return((CHAR)(244 & 0xff)); case 0x00da: return((CHAR)(242 & 0xff)); case 0x00db: return((CHAR)(243 & 0xff)); case 0x00dc: return((CHAR)(134 & 0xff)); case 0x2020: return((CHAR)(160 & 0xff)); case 0xfb01: return((CHAR)(222 & 0xff)); case 0x00df: return((CHAR)(167 & 0xff)); case 0x00e0: return((CHAR)(136 & 0xff)); case 0x00e1: return((CHAR)(135 & 0xff)); case 0x00e2: return((CHAR)(137 & 0xff)); case 0x00e3: return((CHAR)(139 & 0xff)); case 0x00e4: return((CHAR)(138 & 0xff)); case 0x00e5: return((CHAR)(140 & 0xff)); case 0x00e6: return((CHAR)(190 & 0xff)); case 0x00e7: return((CHAR)(141 & 0xff)); case 0x00e8: return((CHAR)(143 & 0xff)); case 0x00e9: return((CHAR)(142 & 0xff)); case 0x00ea: return((CHAR)(144 & 0xff)); case 0x00eb: return((CHAR)(145 & 0xff)); case 0x00ec: return((CHAR)(147 & 0xff)); case 0x00ed: return((CHAR)(146 & 0xff)); case 0x00ee: return((CHAR)(148 & 0xff)); case 0x00ef: return((CHAR)(149 & 0xff)); case 0x203a: return((CHAR)(221 & 0xff)); case 0x00f1: return((CHAR)(150 & 0xff)); case 0x00f2: return((CHAR)(152 & 0xff)); case 0x00f3: return((CHAR)(151 & 0xff)); case 0x00f4: return((CHAR)(153 & 0xff)); case 0x00f5: return((CHAR)(155 & 0xff)); case 0x00f6: return((CHAR)(154 & 0xff)); case 0x00f7: return((CHAR)(214 & 0xff)); case 0x00f8: return((CHAR)(191 & 0xff)); case 0x00f9: return((CHAR)(157 & 0xff)); case 0x00fa: return((CHAR)(156 & 0xff)); case 0x00fb: return((CHAR)(158 & 0xff)); case 0x00fc: return((CHAR)(159 & 0xff)); case 0x2021: return((CHAR)(224 & 0xff)); case 0xfb02: return((CHAR)(223 & 0xff)); case 0x00ff: return((CHAR)(216 & 0xff)); case 0x0131: return((CHAR)(245 & 0xff)); case 0x0152: return((CHAR)(206 & 0xff)); case 0x0153: return((CHAR)(207 & 0xff)); case 0x0178: return((CHAR)(217 & 0xff)); case 0x0192: return((CHAR)(196 & 0xff)); case 0x02c6: return((CHAR)(246 & 0xff)); case 0x02c7: return((CHAR)(255 & 0xff)); case 0x02d8: return((CHAR)(249 & 0xff)); case 0x02d9: return((CHAR)(250 & 0xff)); case 0x02da: return((CHAR)(251 & 0xff)); case 0x02db: return((CHAR)(254 & 0xff)); case 0x02dc: return((CHAR)(247 & 0xff)); case 0x02dd: return((CHAR)(253 & 0xff)); case 0x03c0: return((CHAR)(185 & 0xff)); case 0x2013: return((CHAR)(208 & 0xff)); case 0x2014: return((CHAR)(209 & 0xff)); case 0x2018: return((CHAR)(212 & 0xff)); case 0x2019: return((CHAR)(213 & 0xff)); case 0x201a: return((CHAR)(226 & 0xff)); case 0x201c: return((CHAR)(210 & 0xff)); case 0x201d: return((CHAR)(211 & 0xff)); case 0x201e: return((CHAR)(227 & 0xff)); case 0x2022: return((CHAR)(165 & 0xff)); case 0x2026: return((CHAR)(201 & 0xff)); case 0x2030: return((CHAR)(228 & 0xff)); case 0x2044: return((CHAR)(218 & 0xff)); case 0x2122: return((CHAR)(170 & 0xff)); case 0x03a9: return((CHAR)(189 & 0xff)); case 0x2202: return((CHAR)(182 & 0xff)); case 0x2206: return((CHAR)(198 & 0xff)); case 0x220f: return((CHAR)(184 & 0xff)); case 0x2211: return((CHAR)(183 & 0xff)); case 0x221a: return((CHAR)(195 & 0xff)); case 0x221e: return((CHAR)(176 & 0xff)); case 0x222b: return((CHAR)(186 & 0xff)); case 0x2248: return((CHAR)(197 & 0xff)); case 0x2260: return((CHAR)(173 & 0xff)); case 0x2264: return((CHAR)(178 & 0xff)); case 0x2265: return((CHAR)(179 & 0xff)); case 0x25ca: return((CHAR)(215 & 0xff)); case 0xf8ff: return((CHAR)(240 & 0xff)); default: return(tx_punc(c)); } } int /* HP Roman-8 */ #ifdef CK_ANSIC tx_hproman8(USHORT c) #else tx_hproman8(c) USHORT c; #endif /* CK_ANSIC */ { if (c < 0xa0) return((CHAR)(c & 0xff)); switch(c) { case 0x00a0: return((CHAR)(160 & 0xff)); case 0x00a1: return((CHAR)(171 & 0xff)); case 0x00a2: return((CHAR)(167 & 0xff)); case 0x00a3: return((CHAR)(168 & 0xff)); case 0x00a4: return((CHAR)(166 & 0xff)); case 0x00a5: return((CHAR)(181 & 0xff)); case 0x00a7: return((CHAR)(187 & 0xff)); case 0x00a8: return((CHAR)(189 & 0xff)); case 0x00a9: return((CHAR)(173 & 0xff)); case 0x00aa: return((CHAR)(169 & 0xff)); case 0x00ab: return((CHAR)(177 & 0xff)); case 0x00ac: return((CHAR)(161 & 0xff)); case 0x00ae: return((CHAR)(174 & 0xff)); case 0x00b0: return((CHAR)(188 & 0xff)); case 0x00b1: return((CHAR)(182 & 0xff)); case 0x00b2: return((CHAR)(164 & 0xff)); case 0x00b3: return((CHAR)(165 & 0xff)); case 0x00b4: return((CHAR)(190 & 0xff)); case 0x00b5: return((CHAR)(163 & 0xff)); case 0x00b6: return((CHAR)(178 & 0xff)); case 0x00b7: return((CHAR)(185 & 0xff)); case 0x00b8: return((CHAR)(186 & 0xff)); case 0x00ba: return((CHAR)(170 & 0xff)); case 0x00bb: return((CHAR)(176 & 0xff)); case 0x00bd: return((CHAR)(162 & 0xff)); case 0x00bf: return((CHAR)(172 & 0xff)); case 0x00c0: return((CHAR)(193 & 0xff)); case 0x00c1: return((CHAR)(192 & 0xff)); case 0x00c2: return((CHAR)(194 & 0xff)); case 0x00c3: return((CHAR)(196 & 0xff)); case 0x00c4: return((CHAR)(195 & 0xff)); case 0x00c5: return((CHAR)(197 & 0xff)); case 0x00c6: return((CHAR)(198 & 0xff)); case 0x00c7: return((CHAR)(199 & 0xff)); case 0x00c8: return((CHAR)(201 & 0xff)); case 0x00c9: return((CHAR)(200 & 0xff)); case 0x00ca: return((CHAR)(202 & 0xff)); case 0x00cb: return((CHAR)(203 & 0xff)); case 0x00cc: return((CHAR)(205 & 0xff)); case 0x00cd: return((CHAR)(204 & 0xff)); case 0x00ce: return((CHAR)(206 & 0xff)); case 0x00cf: return((CHAR)(207 & 0xff)); case 0x00d1: return((CHAR)(208 & 0xff)); case 0x00d2: return((CHAR)(210 & 0xff)); case 0x00d3: return((CHAR)(209 & 0xff)); case 0x00d4: return((CHAR)(211 & 0xff)); case 0x00d5: return((CHAR)(213 & 0xff)); case 0x00d6: return((CHAR)(212 & 0xff)); case 0x00d8: return((CHAR)(214 & 0xff)); case 0x00d9: return((CHAR)(217 & 0xff)); case 0x00da: return((CHAR)(216 & 0xff)); case 0x00db: return((CHAR)(218 & 0xff)); case 0x00dc: return((CHAR)(219 & 0xff)); case 0x00df: return((CHAR)(252 & 0xff)); case 0x00e0: return((CHAR)(225 & 0xff)); case 0x00e1: return((CHAR)(224 & 0xff)); case 0x00e2: return((CHAR)(226 & 0xff)); case 0x00e3: return((CHAR)(228 & 0xff)); case 0x00e4: return((CHAR)(227 & 0xff)); case 0x00e5: return((CHAR)(229 & 0xff)); case 0x00e6: return((CHAR)(230 & 0xff)); case 0x00e7: return((CHAR)(231 & 0xff)); case 0x00e8: return((CHAR)(233 & 0xff)); case 0x00e9: return((CHAR)(232 & 0xff)); case 0x00ea: return((CHAR)(234 & 0xff)); case 0x00eb: return((CHAR)(235 & 0xff)); case 0x00ec: return((CHAR)(237 & 0xff)); case 0x00ed: return((CHAR)(236 & 0xff)); case 0x00ee: return((CHAR)(238 & 0xff)); case 0x00ef: return((CHAR)(239 & 0xff)); case 0x00f1: return((CHAR)(240 & 0xff)); case 0x00f2: return((CHAR)(242 & 0xff)); case 0x00f3: return((CHAR)(241 & 0xff)); case 0x00f4: return((CHAR)(243 & 0xff)); case 0x00f5: return((CHAR)(245 & 0xff)); case 0x00f6: return((CHAR)(244 & 0xff)); case 0x00f8: return((CHAR)(246 & 0xff)); case 0x00f9: return((CHAR)(249 & 0xff)); case 0x00fa: return((CHAR)(248 & 0xff)); case 0x00fb: return((CHAR)(250 & 0xff)); case 0x00fc: return((CHAR)(251 & 0xff)); case 0x00ff: return((CHAR)(253 & 0xff)); case 0x0153: return((CHAR)(247 & 0xff)); case 0x0178: return((CHAR)(221 & 0xff)); case 0x0192: return((CHAR)(180 & 0xff)); case 0x0276: return((CHAR)(215 & 0xff)); case 0x2021: return((CHAR)(175 & 0xff)); case 0x2122: return((CHAR)(179 & 0xff)); case 0x2191: return((CHAR)(191 & 0xff)); case 0x2264: return((CHAR)(183 & 0xff)); case 0x2265: return((CHAR)(184 & 0xff)); case 0x2588: return((CHAR)(255 & 0xff)); default: return(tx_punc(c)); } } int /* PC Code Page 437 */ #ifdef CK_ANSIC tx_cp437(USHORT c) #else tx_cp437(c) USHORT c; #endif /* CK_ANSIC */ { if (c < 0x80) /* Has C1 graphics */ return((CHAR)(c & 0xff)); switch (c) { case 0x00a0: return((CHAR)(255 & 0xff)); case 0x00a1: return((CHAR)(173 & 0xff)); case 0x00a2: return((CHAR)(155 & 0xff)); case 0x00a3: return((CHAR)(156 & 0xff)); case 0x00a4: return((CHAR)(15 & 0xff)); case 0x00a5: return((CHAR)(157 & 0xff)); case 0x00a6: return((CHAR)(124 & 0xff)); case 0x00a7: return((CHAR)(21 & 0xff)); case 0x00a8: return((CHAR)(34 & 0xff)); case 0x00a9: return((CHAR)(67 & 0xff)); case 0x00aa: return((CHAR)(166 & 0xff)); case 0x00ab: return((CHAR)(174 & 0xff)); case 0x00ac: return((CHAR)(170 & 0xff)); case 0x00ad: return((CHAR)(45 & 0xff)); case 0x00ae: return((CHAR)(84 & 0xff)); case 0x00af: return((CHAR)(22 & 0xff)); case 0x00b0: return((CHAR)(248 & 0xff)); case 0x00b1: return((CHAR)(241 & 0xff)); case 0x00b2: return((CHAR)(253 & 0xff)); case 0x00b3: return((CHAR)(51 & 0xff)); case 0x00b4: return((CHAR)(39 & 0xff)); case 0x00b5: return((CHAR)(230 & 0xff)); case 0x00b6: return((CHAR)(20 & 0xff)); case 0x00b7: return((CHAR)(250 & 0xff)); case 0x00b8: return((CHAR)(44 & 0xff)); case 0x00b9: return((CHAR)(49 & 0xff)); case 0x00ba: return((CHAR)(167 & 0xff)); case 0x00bb: return((CHAR)(175 & 0xff)); case 0x00bc: return((CHAR)(172 & 0xff)); case 0x00bd: return((CHAR)(171 & 0xff)); case 0x00be: return((CHAR)(19 & 0xff)); case 0x00bf: return((CHAR)(168 & 0xff)); case 0x00c0: return((CHAR)(65 & 0xff)); case 0x00c1: return((CHAR)(65 & 0xff)); case 0x00c2: return((CHAR)(65 & 0xff)); case 0x00c3: return((CHAR)(65 & 0xff)); case 0x00c4: return((CHAR)(142 & 0xff)); case 0x00c5: return((CHAR)(143 & 0xff)); case 0x00c6: return((CHAR)(146 & 0xff)); case 0x00c7: return((CHAR)(128 & 0xff)); case 0x00c8: return((CHAR)(69 & 0xff)); case 0x00c9: return((CHAR)(144 & 0xff)); case 0x00ca: return((CHAR)(69 & 0xff)); case 0x00cb: return((CHAR)(69 & 0xff)); case 0x00cc: return((CHAR)(73 & 0xff)); case 0x00cd: return((CHAR)(73 & 0xff)); case 0x00ce: return((CHAR)(73 & 0xff)); case 0x00cf: return((CHAR)(73 & 0xff)); case 0x00d0: return((CHAR)(19 & 0xff)); case 0x00d1: return((CHAR)(165 & 0xff)); case 0x00d2: return((CHAR)(79 & 0xff)); case 0x00d3: return((CHAR)(79 & 0xff)); case 0x00d4: return((CHAR)(79 & 0xff)); case 0x00d5: return((CHAR)(79 & 0xff)); case 0x00d6: return((CHAR)(153 & 0xff)); case 0x00d7: return((CHAR)(120 & 0xff)); case 0x00d8: return((CHAR)(79 & 0xff)); case 0x00d9: return((CHAR)(85 & 0xff)); case 0x00da: return((CHAR)(85 & 0xff)); case 0x00db: return((CHAR)(85 & 0xff)); case 0x00dc: return((CHAR)(154 & 0xff)); case 0x00dd: return((CHAR)(89 & 0xff)); case 0x00de: return((CHAR)(19 & 0xff)); case 0x00df: return((CHAR)(225 & 0xff)); case 0x00e0: return((CHAR)(133 & 0xff)); case 0x00e1: return((CHAR)(160 & 0xff)); case 0x00e2: return((CHAR)(131 & 0xff)); case 0x00e3: return((CHAR)(97 & 0xff)); /* a-tilde -> a (not 101 = e) */ case 0x00e4: return((CHAR)(132 & 0xff)); case 0x00e5: return((CHAR)(134 & 0xff)); case 0x00e6: return((CHAR)(145 & 0xff)); case 0x00e7: return((CHAR)(135 & 0xff)); case 0x00e8: return((CHAR)(138 & 0xff)); case 0x00e9: return((CHAR)(130 & 0xff)); case 0x00ea: return((CHAR)(136 & 0xff)); case 0x00eb: return((CHAR)(137 & 0xff)); case 0x00ec: return((CHAR)(141 & 0xff)); case 0x00ed: return((CHAR)(161 & 0xff)); case 0x00ee: return((CHAR)(140 & 0xff)); case 0x00ef: return((CHAR)(139 & 0xff)); case 0x00f0: return((CHAR)(19 & 0xff)); case 0x00f1: return((CHAR)(164 & 0xff)); case 0x00f2: return((CHAR)(149 & 0xff)); case 0x00f3: return((CHAR)(162 & 0xff)); case 0x00f4: return((CHAR)(147 & 0xff)); case 0x00f5: return((CHAR)(111 & 0xff)); case 0x00f6: return((CHAR)(148 & 0xff)); case 0x00f7: return((CHAR)(246 & 0xff)); case 0x00f8: return((CHAR)(111 & 0xff)); case 0x00f9: return((CHAR)(151 & 0xff)); case 0x00fa: return((CHAR)(163 & 0xff)); case 0x00fb: return((CHAR)(150 & 0xff)); case 0x00fc: return((CHAR)(129 & 0xff)); case 0x00fd: return((CHAR)(121 & 0xff)); case 0x00fe: return((CHAR)(19 & 0xff)); case 0x00ff: return((CHAR)(152 & 0xff)); case 0x0192: return((CHAR)(159 & 0xff)); case 0x0393: return((CHAR)(226 & 0xff)); case 0x0398: return((CHAR)(233 & 0xff)); case 0x03a3: return((CHAR)(228 & 0xff)); case 0x03a6: return((CHAR)(232 & 0xff)); case 0x03a9: return((CHAR)(234 & 0xff)); case 0x03b1: return((CHAR)(224 & 0xff)); case 0x03b4: return((CHAR)(235 & 0xff)); case 0x03b5: return((CHAR)(238 & 0xff)); case 0x03c0: return((CHAR)(227 & 0xff)); case 0x03c3: return((CHAR)(229 & 0xff)); case 0x03c4: return((CHAR)(231 & 0xff)); case 0x03c6: return((CHAR)(237 & 0xff)); case 0x207f: return((CHAR)(252 & 0xff)); case 0x20a7: return((CHAR)(158 & 0xff)); case 0x2219: return((CHAR)(249 & 0xff)); case 0x221a: return((CHAR)(251 & 0xff)); case 0x221e: return((CHAR)(236 & 0xff)); case 0x2229: return((CHAR)(239 & 0xff)); case 0x2248: return((CHAR)(247 & 0xff)); case 0x2261: return((CHAR)(240 & 0xff)); case 0x2264: return((CHAR)(243 & 0xff)); case 0x2265: return((CHAR)(242 & 0xff)); case 0x2310: return((CHAR)(169 & 0xff)); case 0x2320: return((CHAR)(244 & 0xff)); case 0x2321: return((CHAR)(245 & 0xff)); case 0x2500: return((CHAR)(196 & 0xff)); case 0x2502: return((CHAR)(179 & 0xff)); case 0x250c: return((CHAR)(218 & 0xff)); case 0x2510: return((CHAR)(191 & 0xff)); case 0x2514: return((CHAR)(192 & 0xff)); case 0x2518: return((CHAR)(217 & 0xff)); case 0x251c: return((CHAR)(195 & 0xff)); case 0x2524: return((CHAR)(180 & 0xff)); case 0x252c: return((CHAR)(194 & 0xff)); case 0x2534: return((CHAR)(193 & 0xff)); case 0x253c: return((CHAR)(197 & 0xff)); case 0x2550: return((CHAR)(205 & 0xff)); case 0x2551: return((CHAR)(186 & 0xff)); case 0x2552: return((CHAR)(213 & 0xff)); case 0x2553: return((CHAR)(214 & 0xff)); case 0x2554: return((CHAR)(201 & 0xff)); case 0x2555: return((CHAR)(184 & 0xff)); case 0x2556: return((CHAR)(183 & 0xff)); case 0x2557: return((CHAR)(187 & 0xff)); case 0x2558: return((CHAR)(212 & 0xff)); case 0x2559: return((CHAR)(211 & 0xff)); case 0x255a: return((CHAR)(200 & 0xff)); case 0x255b: return((CHAR)(190 & 0xff)); case 0x255c: return((CHAR)(189 & 0xff)); case 0x255d: return((CHAR)(188 & 0xff)); case 0x255e: return((CHAR)(198 & 0xff)); case 0x255f: return((CHAR)(199 & 0xff)); case 0x2560: return((CHAR)(204 & 0xff)); case 0x2561: return((CHAR)(181 & 0xff)); case 0x2562: return((CHAR)(182 & 0xff)); case 0x2563: return((CHAR)(185 & 0xff)); case 0x2564: return((CHAR)(209 & 0xff)); case 0x2565: return((CHAR)(210 & 0xff)); case 0x2566: return((CHAR)(203 & 0xff)); case 0x2567: return((CHAR)(207 & 0xff)); case 0x2568: return((CHAR)(208 & 0xff)); case 0x2569: return((CHAR)(202 & 0xff)); case 0x256a: return((CHAR)(216 & 0xff)); case 0x256b: return((CHAR)(215 & 0xff)); case 0x256c: return((CHAR)(206 & 0xff)); case 0x2580: return((CHAR)(223 & 0xff)); case 0x2584: return((CHAR)(220 & 0xff)); case 0x2588: return((CHAR)(219 & 0xff)); case 0x258c: return((CHAR)(221 & 0xff)); case 0x2590: return((CHAR)(222 & 0xff)); case 0x2591: return((CHAR)(176 & 0xff)); case 0x2592: return((CHAR)(177 & 0xff)); case 0x2593: return((CHAR)(178 & 0xff)); case 0x25a0: return((CHAR)(254 & 0xff)); /* Black square */ default: return(tx_cpsub(c)); /* For box characters etc */ } } int /* Mazovia */ #ifdef CK_ANSIC tx_mazovia(USHORT c) #else tx_mazovia(c) USHORT c; #endif /* CK_ANSIC */ { if (c < 0x80) /* Has C1 graphics */ return((CHAR)(c & 0xff)); switch (c) { case 0x00d3: return((CHAR)0xa3 & 0xff); /* O acute */ case 0x00f3: return((CHAR)0xa2 & 0xff); /* O acute */ case 0x0104: return((CHAR)0x8f & 0xff); /* A Ogonek */ case 0x0105: return((CHAR)0x86 & 0xff); /* a Ogonek */ case 0x0106: return((CHAR)0x95 & 0xff); /* C acute */ case 0x0107: return((CHAR)0x8d & 0xff); /* c acute */ case 0x0118: return((CHAR)0x90 & 0xff); /* E Ogonek */ case 0x0119: return((CHAR)0x91 & 0xff); /* E Ogonek */ case 0x0141: return((CHAR)0x9c & 0xff); /* L stroke */ case 0x0142: return((CHAR)0x92 & 0xff); /* L stroke */ case 0x0143: return((CHAR)0xa5 & 0xff); /* N acute */ case 0x0144: return((CHAR)0xa4 & 0xff); /* N acute */ case 0x015a: return((CHAR)0x98 & 0xff); /* S acute */ case 0x015b: return((CHAR)0x9e & 0xff); /* S acute */ case 0x0179: return((CHAR)0xa0 & 0xff); /* Z acute */ case 0x017a: return((CHAR)0xa6 & 0xff); /* Z acute */ case 0x017b: return((CHAR)0xa1 & 0xff); /* Z dot above */ case 0x017c: return((CHAR)0xa7 & 0xff); /* Z dot above */ default: return(tx_cp437(c)); } } int /* PC Code Page 850 */ #ifdef CK_ANSIC tx_cp850(USHORT c) #else tx_cp850(c) USHORT c; #endif /* CK_ANSIC */ { if (c < 0x80) /* Has C1 graphics */ return((CHAR)(c & 0xff)); switch (c) { case 0x00a0: return((CHAR)(255 & 0xff)); case 0x00a1: return((CHAR)(173 & 0xff)); case 0x00a2: return((CHAR)(189 & 0xff)); case 0x00a3: return((CHAR)(156 & 0xff)); case 0x00a4: return((CHAR)(207 & 0xff)); case 0x00a5: return((CHAR)(190 & 0xff)); case 0x00a6: return((CHAR)(221 & 0xff)); case 0x00a7: return((CHAR)(245 & 0xff)); case 0x00a8: return((CHAR)(249 & 0xff)); case 0x00a9: return((CHAR)(184 & 0xff)); case 0x00aa: return((CHAR)(166 & 0xff)); case 0x00ab: return((CHAR)(174 & 0xff)); case 0x00ac: return((CHAR)(170 & 0xff)); case 0x00ad: return((CHAR)(240 & 0xff)); case 0x00ae: return((CHAR)(169 & 0xff)); case 0x00af: return((CHAR)(238 & 0xff)); case 0x00b0: return((CHAR)(248 & 0xff)); case 0x00b1: return((CHAR)(241 & 0xff)); case 0x00b2: return((CHAR)(253 & 0xff)); case 0x00b3: return((CHAR)(252 & 0xff)); case 0x00b4: return((CHAR)(239 & 0xff)); case 0x00b5: return((CHAR)(230 & 0xff)); case 0x00b6: return((CHAR)(244 & 0xff)); case 0x00b7: return((CHAR)(250 & 0xff)); case 0x00b8: return((CHAR)(247 & 0xff)); case 0x00b9: return((CHAR)(251 & 0xff)); case 0x00ba: return((CHAR)(167 & 0xff)); case 0x00bb: return((CHAR)(175 & 0xff)); case 0x00bc: return((CHAR)(172 & 0xff)); case 0x00bd: return((CHAR)(171 & 0xff)); case 0x00be: return((CHAR)(243 & 0xff)); case 0x00bf: return((CHAR)(168 & 0xff)); case 0x00c0: return((CHAR)(183 & 0xff)); case 0x00c1: return((CHAR)(181 & 0xff)); case 0x00c2: return((CHAR)(182 & 0xff)); case 0x00c3: return((CHAR)(199 & 0xff)); case 0x00c4: return((CHAR)(142 & 0xff)); case 0x00c5: return((CHAR)(143 & 0xff)); case 0x00c6: return((CHAR)(146 & 0xff)); case 0x00c7: return((CHAR)(128 & 0xff)); case 0x00c8: return((CHAR)(212 & 0xff)); case 0x00c9: return((CHAR)(144 & 0xff)); case 0x00ca: return((CHAR)(210 & 0xff)); case 0x00cb: return((CHAR)(211 & 0xff)); case 0x00cc: return((CHAR)(222 & 0xff)); case 0x00cd: return((CHAR)(214 & 0xff)); case 0x00ce: return((CHAR)(215 & 0xff)); case 0x00cf: return((CHAR)(216 & 0xff)); case 0x00d0: return((CHAR)(209 & 0xff)); case 0x00d1: return((CHAR)(165 & 0xff)); case 0x00d2: return((CHAR)(227 & 0xff)); case 0x00d3: return((CHAR)(224 & 0xff)); case 0x00d4: return((CHAR)(226 & 0xff)); case 0x00d5: return((CHAR)(229 & 0xff)); case 0x00d6: return((CHAR)(153 & 0xff)); case 0x00d7: return((CHAR)(158 & 0xff)); case 0x00d8: return((CHAR)(157 & 0xff)); case 0x00d9: return((CHAR)(235 & 0xff)); case 0x00da: return((CHAR)(233 & 0xff)); case 0x00db: return((CHAR)(234 & 0xff)); case 0x00dc: return((CHAR)(154 & 0xff)); case 0x00dd: return((CHAR)(237 & 0xff)); case 0x00de: return((CHAR)(232 & 0xff)); case 0x00df: return((CHAR)(225 & 0xff)); case 0x00e0: return((CHAR)(133 & 0xff)); case 0x00e1: return((CHAR)(160 & 0xff)); case 0x00e2: return((CHAR)(131 & 0xff)); case 0x00e3: return((CHAR)(198 & 0xff)); case 0x00e4: return((CHAR)(132 & 0xff)); case 0x00e5: return((CHAR)(134 & 0xff)); case 0x00e6: return((CHAR)(145 & 0xff)); case 0x00e7: return((CHAR)(135 & 0xff)); case 0x00e8: return((CHAR)(138 & 0xff)); case 0x00e9: return((CHAR)(130 & 0xff)); case 0x00ea: return((CHAR)(136 & 0xff)); case 0x00eb: return((CHAR)(137 & 0xff)); case 0x00ec: return((CHAR)(141 & 0xff)); case 0x00ed: return((CHAR)(161 & 0xff)); case 0x00ee: return((CHAR)(140 & 0xff)); case 0x00ef: return((CHAR)(139 & 0xff)); case 0x00f0: return((CHAR)(208 & 0xff)); case 0x00f1: return((CHAR)(164 & 0xff)); case 0x00f2: return((CHAR)(149 & 0xff)); case 0x00f3: return((CHAR)(162 & 0xff)); case 0x00f4: return((CHAR)(147 & 0xff)); case 0x00f5: return((CHAR)(228 & 0xff)); case 0x00f6: return((CHAR)(148 & 0xff)); case 0x00f7: return((CHAR)(246 & 0xff)); case 0x00f8: return((CHAR)(155 & 0xff)); case 0x00f9: return((CHAR)(151 & 0xff)); case 0x00fa: return((CHAR)(163 & 0xff)); case 0x00fb: return((CHAR)(150 & 0xff)); case 0x00fc: return((CHAR)(129 & 0xff)); case 0x00fd: return((CHAR)(236 & 0xff)); case 0x00fe: return((CHAR)(231 & 0xff)); case 0x00ff: return((CHAR)(152 & 0xff)); case 0x0131: return((CHAR)(213 & 0xff)); case 0x0192: return((CHAR)(159 & 0xff)); case 0x2017: return((CHAR)(242 & 0xff)); case 0x2500: return((CHAR)(196 & 0xff)); case 0x2502: return((CHAR)(179 & 0xff)); case 0x250c: return((CHAR)(218 & 0xff)); case 0x2510: return((CHAR)(191 & 0xff)); case 0x2514: return((CHAR)(192 & 0xff)); case 0x2518: return((CHAR)(217 & 0xff)); case 0x251c: return((CHAR)(195 & 0xff)); case 0x2524: return((CHAR)(180 & 0xff)); case 0x252c: return((CHAR)(194 & 0xff)); case 0x2534: return((CHAR)(193 & 0xff)); case 0x253c: return((CHAR)(197 & 0xff)); case 0x2550: return((CHAR)(205 & 0xff)); case 0x2551: return((CHAR)(186 & 0xff)); case 0x2554: return((CHAR)(201 & 0xff)); case 0x2557: return((CHAR)(187 & 0xff)); case 0x255a: return((CHAR)(200 & 0xff)); case 0x255d: return((CHAR)(188 & 0xff)); case 0x2560: return((CHAR)(204 & 0xff)); case 0x2563: return((CHAR)(185 & 0xff)); case 0x2566: return((CHAR)(203 & 0xff)); case 0x2569: return((CHAR)(202 & 0xff)); case 0x256c: return((CHAR)(206 & 0xff)); case 0x2580: return((CHAR)(223 & 0xff)); case 0x2584: return((CHAR)(220 & 0xff)); case 0x2588: return((CHAR)(219 & 0xff)); case 0x2591: return((CHAR)(176 & 0xff)); case 0x2592: return((CHAR)(177 & 0xff)); case 0x2593: return((CHAR)(178 & 0xff)); case 0x25a0: return((CHAR)(254 & 0xff)); default: return(tx_cpsub(c)); /* For box characters etc */ } } int /* PC Code Page 858 */ #ifdef CK_ANSIC tx_cp858(USHORT c) #else tx_cp858(c) USHORT c; #endif /* CK_ANSIC */ { if (c < 0x80) /* Has C1 graphics */ return((CHAR)(c & 0xff)); switch (c) { case 0x00a0: return((CHAR)(255 & 0xff)); case 0x00a1: return((CHAR)(173 & 0xff)); case 0x00a2: return((CHAR)(189 & 0xff)); case 0x00a3: return((CHAR)(156 & 0xff)); case 0x00a4: return((CHAR)(207 & 0xff)); case 0x00a5: return((CHAR)(190 & 0xff)); case 0x00a6: return((CHAR)(221 & 0xff)); case 0x00a7: return((CHAR)(245 & 0xff)); case 0x00a8: return((CHAR)(249 & 0xff)); case 0x00a9: return((CHAR)(184 & 0xff)); case 0x00aa: return((CHAR)(166 & 0xff)); case 0x00ab: return((CHAR)(174 & 0xff)); case 0x00ac: return((CHAR)(170 & 0xff)); case 0x00ad: return((CHAR)(240 & 0xff)); case 0x00ae: return((CHAR)(169 & 0xff)); case 0x00af: return((CHAR)(238 & 0xff)); case 0x00b0: return((CHAR)(248 & 0xff)); case 0x00b1: return((CHAR)(241 & 0xff)); case 0x00b2: return((CHAR)(253 & 0xff)); case 0x00b3: return((CHAR)(252 & 0xff)); case 0x00b4: return((CHAR)(239 & 0xff)); case 0x00b5: return((CHAR)(230 & 0xff)); case 0x00b6: return((CHAR)(244 & 0xff)); case 0x00b7: return((CHAR)(250 & 0xff)); case 0x00b8: return((CHAR)(247 & 0xff)); case 0x00b9: return((CHAR)(251 & 0xff)); case 0x00ba: return((CHAR)(167 & 0xff)); case 0x00bb: return((CHAR)(175 & 0xff)); case 0x00bc: return((CHAR)(172 & 0xff)); case 0x00bd: return((CHAR)(171 & 0xff)); case 0x00be: return((CHAR)(243 & 0xff)); case 0x00bf: return((CHAR)(168 & 0xff)); case 0x00c0: return((CHAR)(183 & 0xff)); case 0x00c1: return((CHAR)(181 & 0xff)); case 0x00c2: return((CHAR)(182 & 0xff)); case 0x00c3: return((CHAR)(199 & 0xff)); case 0x00c4: return((CHAR)(142 & 0xff)); case 0x00c5: return((CHAR)(143 & 0xff)); case 0x00c6: return((CHAR)(146 & 0xff)); case 0x00c7: return((CHAR)(128 & 0xff)); case 0x00c8: return((CHAR)(212 & 0xff)); case 0x00c9: return((CHAR)(144 & 0xff)); case 0x00ca: return((CHAR)(210 & 0xff)); case 0x00cb: return((CHAR)(211 & 0xff)); case 0x00cc: return((CHAR)(222 & 0xff)); case 0x00cd: return((CHAR)(214 & 0xff)); case 0x00ce: return((CHAR)(215 & 0xff)); case 0x00cf: return((CHAR)(216 & 0xff)); case 0x00d0: return((CHAR)(209 & 0xff)); case 0x00d1: return((CHAR)(165 & 0xff)); case 0x00d2: return((CHAR)(227 & 0xff)); case 0x00d3: return((CHAR)(224 & 0xff)); case 0x00d4: return((CHAR)(226 & 0xff)); case 0x00d5: return((CHAR)(229 & 0xff)); case 0x00d6: return((CHAR)(153 & 0xff)); case 0x00d7: return((CHAR)(158 & 0xff)); case 0x00d8: return((CHAR)(157 & 0xff)); case 0x00d9: return((CHAR)(235 & 0xff)); case 0x00da: return((CHAR)(233 & 0xff)); case 0x00db: return((CHAR)(234 & 0xff)); case 0x00dc: return((CHAR)(154 & 0xff)); case 0x00dd: return((CHAR)(237 & 0xff)); case 0x00de: return((CHAR)(232 & 0xff)); case 0x00df: return((CHAR)(225 & 0xff)); case 0x00e0: return((CHAR)(133 & 0xff)); case 0x00e1: return((CHAR)(160 & 0xff)); case 0x00e2: return((CHAR)(131 & 0xff)); case 0x00e3: return((CHAR)(198 & 0xff)); case 0x00e4: return((CHAR)(132 & 0xff)); case 0x00e5: return((CHAR)(134 & 0xff)); case 0x00e6: return((CHAR)(145 & 0xff)); case 0x00e7: return((CHAR)(135 & 0xff)); case 0x00e8: return((CHAR)(138 & 0xff)); case 0x00e9: return((CHAR)(130 & 0xff)); case 0x00ea: return((CHAR)(136 & 0xff)); case 0x00eb: return((CHAR)(137 & 0xff)); case 0x00ec: return((CHAR)(141 & 0xff)); case 0x00ed: return((CHAR)(161 & 0xff)); case 0x00ee: return((CHAR)(140 & 0xff)); case 0x00ef: return((CHAR)(139 & 0xff)); case 0x00f0: return((CHAR)(208 & 0xff)); case 0x00f1: return((CHAR)(164 & 0xff)); case 0x00f2: return((CHAR)(149 & 0xff)); case 0x00f3: return((CHAR)(162 & 0xff)); case 0x00f4: return((CHAR)(147 & 0xff)); case 0x00f5: return((CHAR)(228 & 0xff)); case 0x00f6: return((CHAR)(148 & 0xff)); case 0x00f7: return((CHAR)(246 & 0xff)); case 0x00f8: return((CHAR)(155 & 0xff)); case 0x00f9: return((CHAR)(151 & 0xff)); case 0x00fa: return((CHAR)(163 & 0xff)); case 0x00fb: return((CHAR)(150 & 0xff)); case 0x00fc: return((CHAR)(129 & 0xff)); case 0x00fd: return((CHAR)(236 & 0xff)); case 0x00fe: return((CHAR)(231 & 0xff)); case 0x00ff: return((CHAR)(152 & 0xff)); case 0x20ac: return((CHAR)(213 & 0xff)); case 0x0192: return((CHAR)(159 & 0xff)); case 0x2017: return((CHAR)(242 & 0xff)); case 0x2500: return((CHAR)(196 & 0xff)); case 0x2502: return((CHAR)(179 & 0xff)); case 0x250c: return((CHAR)(218 & 0xff)); case 0x2510: return((CHAR)(191 & 0xff)); case 0x2514: return((CHAR)(192 & 0xff)); case 0x2518: return((CHAR)(217 & 0xff)); case 0x251c: return((CHAR)(195 & 0xff)); case 0x2524: return((CHAR)(180 & 0xff)); case 0x252c: return((CHAR)(194 & 0xff)); case 0x2534: return((CHAR)(193 & 0xff)); case 0x253c: return((CHAR)(197 & 0xff)); case 0x2550: return((CHAR)(205 & 0xff)); case 0x2551: return((CHAR)(186 & 0xff)); case 0x2554: return((CHAR)(201 & 0xff)); case 0x2557: return((CHAR)(187 & 0xff)); case 0x255a: return((CHAR)(200 & 0xff)); case 0x255d: return((CHAR)(188 & 0xff)); case 0x2560: return((CHAR)(204 & 0xff)); case 0x2563: return((CHAR)(185 & 0xff)); case 0x2566: return((CHAR)(203 & 0xff)); case 0x2569: return((CHAR)(202 & 0xff)); case 0x256c: return((CHAR)(206 & 0xff)); case 0x2580: return((CHAR)(223 & 0xff)); case 0x2584: return((CHAR)(220 & 0xff)); case 0x2588: return((CHAR)(219 & 0xff)); case 0x2591: return((CHAR)(176 & 0xff)); case 0x2592: return((CHAR)(177 & 0xff)); case 0x2593: return((CHAR)(178 & 0xff)); case 0x25a0: return((CHAR)(254 & 0xff)); default: return(tx_cpsub(c)); /* For box characters etc */ } } int /* Windows Code Page 1250 (Latin-2) */ #ifdef CK_ANSIC tx_cp1250(USHORT c) #else tx_cp1250(c) USHORT c; #endif /* CK_ANSIC */ { if (c < 0x80 || (c > 0xbf && c <= 0xff)) /* Has C1 graphics */ return((CHAR)(c & 0xff)); switch (c) { case 0x002D: return((CHAR)(0xad & 0xff)); case 0x00A0: return((CHAR)(0xa0 & 0xff)); case 0x00A4: return((CHAR)(0xa4 & 0xff)); case 0x00A6: return((CHAR)(0xa6 & 0xff)); case 0x00A7: return((CHAR)(0xa7 & 0xff)); case 0x00A8: return((CHAR)(0xa8 & 0xff)); case 0x00A9: return((CHAR)(0xa9 & 0xff)); case 0x00AB: return((CHAR)(0xab & 0xff)); case 0x00AC: return((CHAR)(0xac & 0xff)); case 0x00AE: return((CHAR)(0xae & 0xff)); case 0x00B0: return((CHAR)(0xb0 & 0xff)); case 0x00B1: return((CHAR)(0xb1 & 0xff)); case 0x00B4: return((CHAR)(0xb4 & 0xff)); case 0x00B5: return((CHAR)(0xb5 & 0xff)); case 0x00B6: return((CHAR)(0xb6 & 0xff)); case 0x00B7: return((CHAR)(0xb7 & 0xff)); case 0x00B8: return((CHAR)(0xb8 & 0xff)); case 0x00BB: return((CHAR)(0xbb & 0xff)); case 0x0104: return((CHAR)(0xa5 & 0xff)); case 0x0105: return((CHAR)(0xb9 & 0xff)); case 0x013D: return((CHAR)(0xbc & 0xff)); case 0x013E: return((CHAR)(0xbe & 0xff)); case 0x0141: return((CHAR)(0xa3 & 0xff)); case 0x0142: return((CHAR)(0xb3 & 0xff)); case 0x015A: return((CHAR)(0x8c & 0xff)); /* S acute */ case 0x015E: return((CHAR)(0xaa & 0xff)); case 0x015F: return((CHAR)(0xba & 0xff)); case 0x015b: return((CHAR)(0x9c & 0xff)); /* s acute */ case 0x0164: return((CHAR)(0x8d & 0xff)); /* T caron */ case 0x0165: return((CHAR)(0x9d & 0xff)); /* t caron */ case 0x0173: return((CHAR)(0x9e & 0xff)); /* z caron */ case 0x0179: return((CHAR)(0x8f & 0xff)); /* Z acute */ case 0x017A: return((CHAR)(0x9f & 0xff)); /* z acute */ case 0x017B: return((CHAR)(0xaf & 0xff)); case 0x017C: return((CHAR)(0xbf & 0xff)); case 0x017D: return((CHAR)(0x8e & 0xff)); /* Z caron */ case 0x02C7: return((CHAR)(0xa1 & 0xff)); case 0x02D8: return((CHAR)(0xa2 & 0xff)); case 0x02DB: return((CHAR)(0xb2 & 0xff)); case 0x02DD: return((CHAR)(0xbd & 0xff)); case 0x2010: case 0x2011: /* Hyphens */ return((CHAR)(0x2d & 0xff)); case 0x2012: case 0x2013: /* en-dashes */ return((CHAR)(0x96 & 0xff)); case 0x2014: case 0x2015: /* em-dashes */ return((CHAR)(0x97 & 0xff)); case 0x2018: /* Various quotation marks... */ return((CHAR)(0x91 & 0xff)); case 0x2019: return((CHAR)(0x92 & 0xff)); case 0x201c: return((CHAR)(0x93 & 0xff)); case 0x201d: return((CHAR)(0x94 & 0xff)); case 0x201e: return((CHAR)(0x84 & 0xff)); case 0x2020: /* Dagger */ return((CHAR)(0x86 & 0xff)); case 0x2021: /* Double Dagger */ return((CHAR)(0x87 & 0xff)); case 0x2022: /* Bullet */ return((CHAR)(0x95 & 0xff)); case 0x2026: /* Ellipsis */ return((CHAR)(0x85 & 0xff)); case 0x2030: /* Per mil */ return((CHAR)(0x89 & 0xff)); case 0x20AC: /* Euro */ return((CHAR)(0x80 & 0xff)); case 0x2122: /* Trade Mark */ return((CHAR)(0x99 & 0xff)); default: return(0x003f); } } int /* Windows Code Page 1251 (Cyrillic) */ #ifdef CK_ANSIC tx_cp1251(USHORT c) #else tx_cp1251(c) USHORT c; #endif /* CK_ANSIC */ { if (c < 0x80) /* Has C1 graphics */ return((CHAR)(c & 0xff)); /* This is simply the inverse of u_cp1251.map */ switch (c) { case 0x003c: return((CHAR)(0x8b & 0xff)); case 0x003e: return((CHAR)(0x9b & 0xff)); case 0x007e: return((CHAR)(0x98 & 0xff)); case 0x00A0: return((CHAR)(0xa0 & 0xff)); case 0x00A4: return((CHAR)(0xa4 & 0xff)); case 0x00A6: return((CHAR)(0xa6 & 0xff)); case 0x00A7: return((CHAR)(0xa7 & 0xff)); case 0x00A9: return((CHAR)(0xa9 & 0xff)); case 0x00AB: return((CHAR)(0xab & 0xff)); case 0x00AC: return((CHAR)(0xac & 0xff)); case 0x00AD: return((CHAR)(0xad & 0xff)); case 0x00AE: return((CHAR)(0xae & 0xff)); case 0x00b0: return((CHAR)(0xb0 & 0xff)); case 0x00b1: return((CHAR)(0xb1 & 0xff)); case 0x00B5: return((CHAR)(0xb5 & 0xff)); case 0x00B6: return((CHAR)(0xb6 & 0xff)); case 0x00B7: return((CHAR)(0xb7 & 0xff)); case 0x00BB: return((CHAR)(0xbb & 0xff)); case 0x0401: return((CHAR)(0xa8 & 0xff)); case 0x0402: return((CHAR)(0x80 & 0xff)); case 0x0403: return((CHAR)(0x81 & 0xff)); case 0x0404: return((CHAR)(0xaa & 0xff)); case 0x0405: return((CHAR)(0xbd & 0xff)); case 0x0406: return((CHAR)(0xb2 & 0xff)); case 0x0407: return((CHAR)(0xaf & 0xff)); case 0x0408: return((CHAR)(0xa3 & 0xff)); case 0x0409: return((CHAR)(0x8a & 0xff)); case 0x040a: return((CHAR)(0x8c & 0xff)); case 0x040b: return((CHAR)(0x8e & 0xff)); case 0x040c: return((CHAR)(0x8d & 0xff)); case 0x040e: return((CHAR)(0xa1 & 0xff)); case 0x040f: return((CHAR)(0x8f & 0xff)); case 0x0410: return((CHAR)(0xc0 & 0xff)); case 0x0411: return((CHAR)(0xc1 & 0xff)); case 0x0412: return((CHAR)(0xc2 & 0xff)); case 0x0413: return((CHAR)(0xc3 & 0xff)); case 0x0414: return((CHAR)(0xc4 & 0xff)); case 0x0415: return((CHAR)(0xc5 & 0xff)); case 0x0416: return((CHAR)(0xc6 & 0xff)); case 0x0417: return((CHAR)(0xc7 & 0xff)); case 0x0418: return((CHAR)(0xc8 & 0xff)); case 0x0419: return((CHAR)(0xc9 & 0xff)); case 0x041a: return((CHAR)(0xca & 0xff)); case 0x041b: return((CHAR)(0xcb & 0xff)); case 0x041c: return((CHAR)(0xcc & 0xff)); case 0x041d: return((CHAR)(0xcd & 0xff)); case 0x041e: return((CHAR)(0xce & 0xff)); case 0x041f: return((CHAR)(0xcf & 0xff)); case 0x0420: return((CHAR)(0xd0 & 0xff)); case 0x0421: return((CHAR)(0xd1 & 0xff)); case 0x0422: return((CHAR)(0xd2 & 0xff)); case 0x0423: return((CHAR)(0xd3 & 0xff)); case 0x0424: return((CHAR)(0xd4 & 0xff)); case 0x0425: return((CHAR)(0xd5 & 0xff)); case 0x0426: return((CHAR)(0xd6 & 0xff)); case 0x0427: return((CHAR)(0xd7 & 0xff)); case 0x0428: return((CHAR)(0xd8 & 0xff)); case 0x0429: return((CHAR)(0xd9 & 0xff)); case 0x042a: return((CHAR)(0xda & 0xff)); case 0x042b: return((CHAR)(0xdb & 0xff)); case 0x042c: return((CHAR)(0xdc & 0xff)); case 0x042d: return((CHAR)(0xdd & 0xff)); case 0x042e: return((CHAR)(0xde & 0xff)); case 0x042f: return((CHAR)(0xdf & 0xff)); case 0x0430: return((CHAR)(0xe0 & 0xff)); case 0x0431: return((CHAR)(0xe1 & 0xff)); case 0x0432: return((CHAR)(0xe2 & 0xff)); case 0x0433: return((CHAR)(0xe3 & 0xff)); case 0x0434: return((CHAR)(0xe4 & 0xff)); case 0x0435: return((CHAR)(0xe5 & 0xff)); case 0x0436: return((CHAR)(0xe6 & 0xff)); case 0x0437: return((CHAR)(0xe7 & 0xff)); case 0x0438: return((CHAR)(0xe8 & 0xff)); case 0x0439: return((CHAR)(0xe9 & 0xff)); case 0x043a: return((CHAR)(0xea & 0xff)); case 0x043b: return((CHAR)(0xeb & 0xff)); case 0x043c: return((CHAR)(0xec & 0xff)); case 0x043d: return((CHAR)(0xed & 0xff)); case 0x043e: return((CHAR)(0xee & 0xff)); case 0x043f: return((CHAR)(0xef & 0xff)); case 0x0440: return((CHAR)(0xf0 & 0xff)); case 0x0441: return((CHAR)(0xf1 & 0xff)); case 0x0442: return((CHAR)(0xf2 & 0xff)); case 0x0443: return((CHAR)(0xf3 & 0xff)); case 0x0444: return((CHAR)(0xf4 & 0xff)); case 0x0445: return((CHAR)(0xf5 & 0xff)); case 0x0446: return((CHAR)(0xf6 & 0xff)); case 0x0447: return((CHAR)(0xf7 & 0xff)); case 0x0448: return((CHAR)(0xf8 & 0xff)); case 0x0449: return((CHAR)(0xf9 & 0xff)); case 0x044a: return((CHAR)(0xfa & 0xff)); case 0x044b: return((CHAR)(0xfb & 0xff)); case 0x044c: return((CHAR)(0xfc & 0xff)); case 0x044d: return((CHAR)(0xfd & 0xff)); case 0x044e: return((CHAR)(0xfe & 0xff)); case 0x044f: return((CHAR)(0xff & 0xff)); case 0x0451: return((CHAR)(0xb8 & 0xff)); case 0x0452: return((CHAR)(0x90 & 0xff)); case 0x0453: return((CHAR)(0x83 & 0xff)); case 0x0454: return((CHAR)(0xba & 0xff)); case 0x0455: return((CHAR)(0xbe & 0xff)); case 0x0456: return((CHAR)(0xb3 & 0xff)); case 0x0457: return((CHAR)(0xbf & 0xff)); case 0x0458: return((CHAR)(0xbc & 0xff)); case 0x0459: return((CHAR)(0x9a & 0xff)); case 0x045a: return((CHAR)(0x9c & 0xff)); case 0x045b: return((CHAR)(0x9e & 0xff)); case 0x045c: return((CHAR)(0x9d & 0xff)); case 0x045e: return((CHAR)(0xa2 & 0xff)); case 0x045f: return((CHAR)(0x9f & 0xff)); case 0x0490: return((CHAR)(0xa5 & 0xff)); case 0x0491: return((CHAR)(0xb4 & 0xff)); case 0x2012: return((CHAR)(0x96 & 0xff)); case 0x2014: return((CHAR)(0x97 & 0xff)); case 0x2018: return((CHAR)(0x91 & 0xff)); case 0x2019: return((CHAR)(0x92 & 0xff)); case 0x201a: return((CHAR)(0x82 & 0xff)); case 0x201c: return((CHAR)(0x93 & 0xff)); case 0x201d: return((CHAR)(0x94 & 0xff)); case 0x201e: return((CHAR)(0x84 & 0xff)); case 0x2020: return((CHAR)(0x86 & 0xff)); case 0x2021: return((CHAR)(0x87 & 0xff)); case 0x2022: return((CHAR)(0x95 & 0xff)); case 0x2026: return((CHAR)(0x85 & 0xff)); case 0x2031: return((CHAR)(0x89 & 0xff)); case 0x20AC: /* Euro */ return((CHAR)(0x88 & 0xff)); case 0x2116: return((CHAR)(0xb9 & 0xff)); case 0x2122: return((CHAR)(0x99 & 0xff)); default: return(0x003f); } } int /* Unicode to Windows Code Page 1252 (Latin-1) */ #ifdef CK_ANSIC tx_cp1252(USHORT c) #else tx_cp1252(c) USHORT c; #endif /* CK_ANSIC */ { if (c < 0x80 || (c > 0x9f && c <= 0xff)) /* Has C1 graphics */ return((CHAR)(c & 0xff)); switch (c) { case 0x0152: /* OE */ return((CHAR)(0x8c & 0xff)); case 0x0153: /* oe */ return((CHAR)(0x9c & 0xff)); case 0x0160: /* S caron */ return((CHAR)(0x8a & 0xff)); case 0x0161: /* s caron */ return((CHAR)(0x9a & 0xff)); case 0x0178: /* Y diaeresis */ return((CHAR)(0x9f & 0xff)); case 0x017D: /* Z caron */ return((CHAR)(0x8e & 0xff)); case 0x017E: /* z caron */ return((CHAR)(0x9e & 0xff)); case 0x0192: /* Florin */ return((CHAR)(0x83 & 0xff)); case 0x2010: case 0x2011: /* Hyphens */ return((CHAR)(0x2d & 0xff)); case 0x2012: case 0x2013: /* en-dashes */ return((CHAR)(0x96 & 0xff)); case 0x2014: case 0x2015: /* em-dashes */ return((CHAR)(0x97 & 0xff)); case 0x2018: /* Various quotation marks... */ return((CHAR)(0x91 & 0xff)); case 0x2019: return((CHAR)(0x92 & 0xff)); case 0x201c: return((CHAR)(0x93 & 0xff)); case 0x201d: return((CHAR)(0x94 & 0xff)); case 0x201e: return((CHAR)(0x84 & 0xff)); case 0x2020: /* Dagger */ return((CHAR)(0x86 & 0xff)); case 0x2021: /* Double Dagger */ return((CHAR)(0x87 & 0xff)); case 0x2022: /* Bullet */ return((CHAR)(0x95 & 0xff)); case 0x2026: /* Ellipsis */ return((CHAR)(0x85 & 0xff)); case 0x2030: /* Per mil */ return((CHAR)(0x89 & 0xff)); case 0x20AC: /* Euro */ return((CHAR)(0x80 & 0xff)); case 0x2122: /* Trade Mark */ return((CHAR)(0x99 & 0xff)); default: return(0x003f); } } int /* PC Code Page 852 */ #ifdef CK_ANSIC tx_cp852(USHORT c) #else tx_cp852(c) USHORT c; #endif /* CK_ANSIC */ { if (c < 0x80) /* Has C1 graphics */ return((CHAR)(c & 0xff)); switch (c) { case 0x00a0: return((CHAR)(255 & 0xff)); case 0x00a4: return((CHAR)(207 & 0xff)); case 0x00a7: return((CHAR)(245 & 0xff)); case 0x00a8: return((CHAR)(249 & 0xff)); case 0x00ab: return((CHAR)(174 & 0xff)); case 0x00ac: return((CHAR)(170 & 0xff)); case 0x00ad: return((CHAR)(240 & 0xff)); case 0x00b0: return((CHAR)(248 & 0xff)); case 0x00b4: return((CHAR)(239 & 0xff)); case 0x00b8: return((CHAR)(247 & 0xff)); case 0x00bb: return((CHAR)(175 & 0xff)); case 0x00c1: return((CHAR)(181 & 0xff)); case 0x00c2: return((CHAR)(182 & 0xff)); case 0x00c4: return((CHAR)(142 & 0xff)); case 0x00c7: return((CHAR)(128 & 0xff)); case 0x00c9: return((CHAR)(144 & 0xff)); case 0x00cb: return((CHAR)(211 & 0xff)); case 0x00cd: return((CHAR)(214 & 0xff)); case 0x00ce: return((CHAR)(215 & 0xff)); case 0x00d3: return((CHAR)(224 & 0xff)); case 0x00d4: return((CHAR)(226 & 0xff)); case 0x00d6: return((CHAR)(153 & 0xff)); case 0x00d7: return((CHAR)(158 & 0xff)); case 0x00da: return((CHAR)(233 & 0xff)); case 0x00dc: return((CHAR)(154 & 0xff)); case 0x00dd: return((CHAR)(237 & 0xff)); case 0x00df: return((CHAR)(225 & 0xff)); case 0x00e1: return((CHAR)(160 & 0xff)); case 0x00e2: return((CHAR)(131 & 0xff)); case 0x00e4: return((CHAR)(132 & 0xff)); case 0x00e7: return((CHAR)(135 & 0xff)); case 0x00e9: return((CHAR)(130 & 0xff)); case 0x00eb: return((CHAR)(137 & 0xff)); case 0x00ed: return((CHAR)(161 & 0xff)); case 0x00ee: return((CHAR)(140 & 0xff)); case 0x00f3: return((CHAR)(162 & 0xff)); case 0x00f4: return((CHAR)(147 & 0xff)); case 0x00f6: return((CHAR)(148 & 0xff)); case 0x00f7: return((CHAR)(246 & 0xff)); case 0x00fa: return((CHAR)(163 & 0xff)); case 0x00fc: return((CHAR)(129 & 0xff)); case 0x00fd: return((CHAR)(236 & 0xff)); case 0x0102: return((CHAR)(198 & 0xff)); case 0x0103: return((CHAR)(199 & 0xff)); case 0x0104: return((CHAR)(164 & 0xff)); case 0x0105: return((CHAR)(165 & 0xff)); case 0x0106: return((CHAR)(143 & 0xff)); case 0x0107: return((CHAR)(134 & 0xff)); case 0x010c: return((CHAR)(172 & 0xff)); case 0x010d: return((CHAR)(159 & 0xff)); case 0x010e: return((CHAR)(210 & 0xff)); case 0x010f: return((CHAR)(212 & 0xff)); case 0x0110: return((CHAR)(209 & 0xff)); case 0x0111: return((CHAR)(208 & 0xff)); case 0x0118: return((CHAR)(168 & 0xff)); case 0x0119: return((CHAR)(169 & 0xff)); case 0x011a: return((CHAR)(183 & 0xff)); case 0x011b: return((CHAR)(216 & 0xff)); case 0x0139: return((CHAR)(145 & 0xff)); case 0x013a: return((CHAR)(146 & 0xff)); case 0x013d: return((CHAR)(149 & 0xff)); case 0x013e: return((CHAR)(150 & 0xff)); case 0x0141: return((CHAR)(157 & 0xff)); case 0x0142: return((CHAR)(136 & 0xff)); case 0x0143: return((CHAR)(227 & 0xff)); case 0x0144: return((CHAR)(228 & 0xff)); case 0x0147: return((CHAR)(213 & 0xff)); case 0x0148: return((CHAR)(229 & 0xff)); case 0x0150: return((CHAR)(138 & 0xff)); case 0x0151: return((CHAR)(139 & 0xff)); case 0x0154: return((CHAR)(232 & 0xff)); case 0x0155: return((CHAR)(234 & 0xff)); case 0x0158: return((CHAR)(252 & 0xff)); case 0x0159: return((CHAR)(253 & 0xff)); case 0x015a: return((CHAR)(151 & 0xff)); case 0x015b: return((CHAR)(152 & 0xff)); case 0x015e: return((CHAR)(184 & 0xff)); case 0x015f: return((CHAR)(173 & 0xff)); case 0x0160: return((CHAR)(230 & 0xff)); case 0x0161: return((CHAR)(231 & 0xff)); case 0x0162: return((CHAR)(221 & 0xff)); case 0x0163: return((CHAR)(238 & 0xff)); case 0x0164: return((CHAR)(155 & 0xff)); case 0x0165: return((CHAR)(156 & 0xff)); case 0x016e: return((CHAR)(222 & 0xff)); case 0x016f: return((CHAR)(133 & 0xff)); case 0x0170: return((CHAR)(235 & 0xff)); case 0x0171: return((CHAR)(251 & 0xff)); case 0x0179: return((CHAR)(141 & 0xff)); case 0x017a: return((CHAR)(171 & 0xff)); case 0x017b: return((CHAR)(189 & 0xff)); case 0x017c: return((CHAR)(190 & 0xff)); case 0x017d: return((CHAR)(166 & 0xff)); case 0x017e: return((CHAR)(167 & 0xff)); case 0x02c7: return((CHAR)(243 & 0xff)); case 0x02d8: return((CHAR)(244 & 0xff)); case 0x02d9: return((CHAR)(250 & 0xff)); case 0x02db: return((CHAR)(242 & 0xff)); case 0x02dd: return((CHAR)(241 & 0xff)); case 0x2500: return((CHAR)(196 & 0xff)); case 0x2502: return((CHAR)(179 & 0xff)); case 0x250c: return((CHAR)(218 & 0xff)); case 0x2510: return((CHAR)(191 & 0xff)); case 0x2514: return((CHAR)(192 & 0xff)); case 0x2518: return((CHAR)(217 & 0xff)); case 0x251c: return((CHAR)(195 & 0xff)); case 0x2524: return((CHAR)(180 & 0xff)); case 0x252c: return((CHAR)(194 & 0xff)); case 0x2534: return((CHAR)(193 & 0xff)); case 0x253c: return((CHAR)(197 & 0xff)); case 0x2550: return((CHAR)(205 & 0xff)); case 0x2551: return((CHAR)(186 & 0xff)); case 0x2554: return((CHAR)(201 & 0xff)); case 0x2557: return((CHAR)(187 & 0xff)); case 0x255a: return((CHAR)(200 & 0xff)); case 0x255d: return((CHAR)(188 & 0xff)); case 0x2560: return((CHAR)(204 & 0xff)); case 0x2563: return((CHAR)(185 & 0xff)); case 0x2566: return((CHAR)(203 & 0xff)); case 0x2569: return((CHAR)(202 & 0xff)); case 0x256c: return((CHAR)(206 & 0xff)); case 0x2580: return((CHAR)(223 & 0xff)); case 0x2584: return((CHAR)(220 & 0xff)); case 0x2588: return((CHAR)(219 & 0xff)); case 0x2591: return((CHAR)(176 & 0xff)); case 0x2592: return((CHAR)(177 & 0xff)); case 0x2593: return((CHAR)(178 & 0xff)); case 0x25a0: return((CHAR)(254 & 0xff)); default: return(tx_cpsub(c)); /* For box characters etc */ } } int /* Windows Code Page 1253 (Greek) */ #ifdef CK_ANSIC tx_cp1253(USHORT c) #else tx_cp1253(c) USHORT c; #endif /* CK_ANSIC */ { if (c < 0x80) /* Has C1 graphics */ return((CHAR)(c & 0xff)); switch (c) { case 0x003c: return((CHAR)(0x8b & 0xff)); case 0x003e: return((CHAR)(0x9b & 0xff)); case 0x00A0: return((CHAR)(0xa0 & 0xff)); case 0x00A3: return((CHAR)(0xa3 & 0xff)); case 0x00A4: return((CHAR)(0xa4 & 0xff)); case 0x00A5: return((CHAR)(0xa5 & 0xff)); case 0x00A6: return((CHAR)(0xa6 & 0xff)); case 0x00A7: return((CHAR)(0xa7 & 0xff)); case 0x00A8: return((CHAR)(0xa8 & 0xff)); case 0x00A9: return((CHAR)(0xa9 & 0xff)); case 0x00AA: return((CHAR)(0xaa & 0xff)); case 0x00AB: return((CHAR)(0xab & 0xff)); case 0x00AC: return((CHAR)(0xac & 0xff)); case 0x00AD: return((CHAR)(0xad & 0xff)); case 0x00AE: return((CHAR)(0xae & 0xff)); case 0x00AF: return((CHAR)(0xaf & 0xff)); case 0x00B0: return((CHAR)(0xb0 & 0xff)); case 0x00B1: return((CHAR)(0xb1 & 0xff)); case 0x00B2: return((CHAR)(0xb2 & 0xff)); case 0x00B3: return((CHAR)(0xb3 & 0xff)); case 0x00B5: return((CHAR)(0xb5 & 0xff)); case 0x00B6: return((CHAR)(0xb6 & 0xff)); case 0x00b7: return((CHAR)(0xa1 & 0xff)); case 0x00BB: return((CHAR)(0xbb & 0xff)); case 0x00BD: return((CHAR)(0xbd & 0xff)); case 0x0192: return((CHAR)(0x83 & 0xff)); case 0x0386: return((CHAR)(0xa2 & 0xff)); case 0x0388: return((CHAR)(0xb8 & 0xff)); case 0x0389: return((CHAR)(0xb9 & 0xff)); case 0x038A: return((CHAR)(0xba & 0xff)); case 0x038C: return((CHAR)(0xbc & 0xff)); case 0x038E: return((CHAR)(0xbe & 0xff)); case 0x038F: return((CHAR)(0xbf & 0xff)); case 0x0390: return((CHAR)(0xc0 & 0xff)); case 0x0391: return((CHAR)(0xc1 & 0xff)); case 0x0392: return((CHAR)(0xc2 & 0xff)); case 0x0393: return((CHAR)(0xc3 & 0xff)); case 0x0394: return((CHAR)(0xc4 & 0xff)); case 0x0395: return((CHAR)(0xc5 & 0xff)); case 0x0396: return((CHAR)(0xc6 & 0xff)); case 0x0397: return((CHAR)(0xc7 & 0xff)); case 0x0398: return((CHAR)(0xc8 & 0xff)); case 0x0399: return((CHAR)(0xc9 & 0xff)); case 0x039A: return((CHAR)(0xca & 0xff)); case 0x039B: return((CHAR)(0xcb & 0xff)); case 0x039C: return((CHAR)(0xcc & 0xff)); case 0x039D: return((CHAR)(0xcd & 0xff)); case 0x039E: return((CHAR)(0xce & 0xff)); case 0x039F: return((CHAR)(0xcf & 0xff)); case 0x03a0: return((CHAR)(0xd0 & 0xff)); case 0x03a1: return((CHAR)(0xd1 & 0xff)); case 0x03a3: return((CHAR)(0xd3 & 0xff)); case 0x03a4: return((CHAR)(0xd4 & 0xff)); case 0x03a5: return((CHAR)(0xd5 & 0xff)); case 0x03a6: return((CHAR)(0xd6 & 0xff)); case 0x03a7: return((CHAR)(0xd7 & 0xff)); case 0x03a8: return((CHAR)(0xd8 & 0xff)); case 0x03a9: return((CHAR)(0xd9 & 0xff)); case 0x03aA: return((CHAR)(0xda & 0xff)); case 0x03aB: return((CHAR)(0xdb & 0xff)); case 0x03aC: return((CHAR)(0xdc & 0xff)); case 0x03aD: return((CHAR)(0xdd & 0xff)); case 0x03aE: return((CHAR)(0xde & 0xff)); case 0x03aF: return((CHAR)(0xdf & 0xff)); case 0x03b0: return((CHAR)(0xe0 & 0xff)); case 0x03b1: return((CHAR)(0xe1 & 0xff)); case 0x03b2: return((CHAR)(0xe2 & 0xff)); case 0x03b3: return((CHAR)(0xe3 & 0xff)); case 0x03b4: return((CHAR)(0xe4 & 0xff)); case 0x03b5: return((CHAR)(0xe5 & 0xff)); case 0x03b6: return((CHAR)(0xe6 & 0xff)); case 0x03b7: return((CHAR)(0xe7 & 0xff)); case 0x03b8: return((CHAR)(0xe8 & 0xff)); case 0x03b9: return((CHAR)(0xe9 & 0xff)); case 0x03bA: return((CHAR)(0xea & 0xff)); case 0x03bB: return((CHAR)(0xeb & 0xff)); case 0x03bC: return((CHAR)(0xec & 0xff)); case 0x03bD: return((CHAR)(0xed & 0xff)); case 0x03bE: return((CHAR)(0xee & 0xff)); case 0x03bF: return((CHAR)(0xef & 0xff)); case 0x03c0: return((CHAR)(0xf0 & 0xff)); case 0x03c1: return((CHAR)(0xf1 & 0xff)); case 0x03c2: return((CHAR)(0xf2 & 0xff)); case 0x03c3: return((CHAR)(0xf3 & 0xff)); case 0x03c4: return((CHAR)(0xf4 & 0xff)); case 0x03c5: return((CHAR)(0xf5 & 0xff)); case 0x03c6: return((CHAR)(0xf6 & 0xff)); case 0x03c7: return((CHAR)(0xf7 & 0xff)); case 0x03c8: return((CHAR)(0xf8 & 0xff)); case 0x03c9: return((CHAR)(0xf9 & 0xff)); case 0x03cA: return((CHAR)(0xfa & 0xff)); case 0x03cB: return((CHAR)(0xfb & 0xff)); case 0x03cC: return((CHAR)(0xfc & 0xff)); case 0x03cD: return((CHAR)(0xfd & 0xff)); case 0x03cE: return((CHAR)(0xfe & 0xff)); case 0x2012: return((CHAR)(0x96 & 0xff)); case 0x2014: return((CHAR)(0x97 & 0xff)); case 0x2018: return((CHAR)(0x91 & 0xff)); case 0x2019: return((CHAR)(0x92 & 0xff)); case 0x201a: return((CHAR)(0x82 & 0xff)); case 0x201c: return((CHAR)(0x93 & 0xff)); case 0x201d: return((CHAR)(0x94 & 0xff)); case 0x201e: return((CHAR)(0x84 & 0xff)); case 0x2020: return((CHAR)(0x86 & 0xff)); case 0x2021: return((CHAR)(0x87 & 0xff)); case 0x2022: return((CHAR)(0x95 & 0xff)); case 0x2026: return((CHAR)(0x85 & 0xff)); case 0x2031: return((CHAR)(0x89 & 0xff)); case 0x20AC: /* Euro */ return((CHAR)(0x80 & 0xff)); case 0x2122: return((CHAR)(0x99 & 0xff)); default: return(0x003f); } } int /* Windows Code Page 1254 (Turkish) */ #ifdef CK_ANSIC tx_cp1254(USHORT c) #else tx_cp1254(c) USHORT c; #endif /* CK_ANSIC */ { if (c < 0x80) return((CHAR)(c & 0xff)); switch (c) { case 0x011e: return((CHAR)(0xd0 & 0xff)); /* G breve */ case 0x0130: return((CHAR)(0xdd & 0xff)); /* I with dot */ case 0x015e: return((CHAR)(0xde & 0xff)); /* S cedilla */ case 0x011f: return((CHAR)(0xf0 & 0xff)); /* g breve */ case 0x0131: return((CHAR)(0xfd & 0xff)); /* i dotless */ case 0x015f: return((CHAR)(0xfe & 0xff)); /* s cedilla */ default: return(tx_cp1252(c)); /* The rest is like Windows Latin-1 */ } } int #ifdef CK_ANSIC tx_cp1255(USHORT c) #else tx_cp1255(c) USHORT c; #endif /* CK_ANSIC */ { if (c < 0x080) return((CHAR)(c & 0xff)); switch (c) { case 0x20AC: return((CHAR)(0x80 & 0xff)); /* EURO SIGN */ case 0x201A: return((CHAR)(0x82 & 0xff)); /* SINGLE LOW-9 QUOTATION MARK */ case 0x0192: return((CHAR)(0x83 & 0xff)); /* LATIN SMALL LETTER F WITH HOOK */ case 0x201E: return((CHAR)(0x84 & 0xff)); /* DOUBLE LOW-9 QUOTATION MARK */ case 0x2026: return((CHAR)(0x85 & 0xff)); /* HORIZONTAL ELLIPSIS */ case 0x2020: return((CHAR)(0x86 & 0xff)); /* DAGGER */ case 0x2021: return((CHAR)(0x87 & 0xff)); /* DOUBLE DAGGER */ case 0x02C6: return((CHAR)(0x88 & 0xff)); /* MODIFIER LETTER CIRCUMFLEX ACCENT */ case 0x2030: return((CHAR)(0x89 & 0xff)); /* PER MILLE SIGN */ case 0x2039: return((CHAR)(0x8B & 0xff)); /* SINGLE LEFT-POINTING ANGLE QUOTE */ case 0x2018: return((CHAR)(0x91 & 0xff)); /* LEFT SINGLE QUOTATION MARK */ case 0x2019: return((CHAR)(0x92 & 0xff)); /* RIGHT SINGLE QUOTATION MARK */ case 0x201C: return((CHAR)(0x93 & 0xff)); /* LEFT DOUBLE QUOTATION MARK */ case 0x201D: return((CHAR)(0x94 & 0xff)); /* RIGHT DOUBLE QUOTATION MARK */ case 0x2022: return((CHAR)(0x95 & 0xff)); /* BULLET */ case 0x2013: return((CHAR)(0x96 & 0xff)); /* EN DASH */ case 0x2014: return((CHAR)(0x97 & 0xff)); /* EM DASH */ case 0x02DC: return((CHAR)(0x98 & 0xff)); /* SMALL TILDE */ case 0x2122: return((CHAR)(0x99 & 0xff)); /* TRADE MARK SIGN */ case 0x203A: return((CHAR)(0x9B & 0xff)); /* SINGLE RIGHT-POINTING ANGLE QUOTE */ case 0x00A0: return((CHAR)(0xA0 & 0xff)); /* NO-BREAK SPACE */ case 0x00A1: return((CHAR)(0xA1 & 0xff)); /* INVERTED EXCLAMATION MARK */ case 0x00A2: return((CHAR)(0xA2 & 0xff)); /* CENT SIGN */ case 0x00A3: return((CHAR)(0xA3 & 0xff)); /* POUND SIGN */ case 0x20AA: return((CHAR)(0xA4 & 0xff)); /* NEW SHEQEL SIGN */ case 0x00A5: return((CHAR)(0xA5 & 0xff)); /* YEN SIGN */ case 0x00A6: return((CHAR)(0xA6 & 0xff)); /* BROKEN BAR */ case 0x00A7: return((CHAR)(0xA7 & 0xff)); /* SECTION SIGN */ case 0x00A8: return((CHAR)(0xA8 & 0xff)); /* DIAERESIS */ case 0x00A9: return((CHAR)(0xA9 & 0xff)); /* COPYRIGHT SIGN */ case 0x00D7: return((CHAR)(0xAA & 0xff)); /* MULTIPLICATION SIGN */ case 0x00AB: return((CHAR)(0xAB & 0xff)); /* LEFT-POINTING DOUBLE ANGLE QUOTE */ case 0x00AC: return((CHAR)(0xAC & 0xff)); /* NOT SIGN */ case 0x00AD: return((CHAR)(0xAD & 0xff)); /* SOFT HYPHEN */ case 0x00AE: return((CHAR)(0xAE & 0xff)); /* REGISTERED SIGN */ case 0x00AF: return((CHAR)(0xAF & 0xff)); /* MACRON */ case 0x00B0: return((CHAR)(0xB0 & 0xff)); /* DEGREE SIGN */ case 0x00B1: return((CHAR)(0xB1 & 0xff)); /* PLUS-MINUS SIGN */ case 0x00B2: return((CHAR)(0xB2 & 0xff)); /* SUPERSCRIPT TWO */ case 0x00B3: return((CHAR)(0xB3 & 0xff)); /* SUPERSCRIPT THREE */ case 0x00B4: return((CHAR)(0xB4 & 0xff)); /* ACUTE ACCENT */ case 0x00B5: return((CHAR)(0xB5 & 0xff)); /* MICRO SIGN */ case 0x00B6: return((CHAR)(0xB6 & 0xff)); /* PILCROW SIGN */ case 0x00B7: return((CHAR)(0xB7 & 0xff)); /* MIDDLE DOT */ case 0x00B8: return((CHAR)(0xB8 & 0xff)); /* CEDILLA */ case 0x00B9: return((CHAR)(0xB9 & 0xff)); /* SUPERSCRIPT ONE */ case 0x00F7: return((CHAR)(0xBA & 0xff)); /* DIVISION SIGN */ case 0x00BB: return((CHAR)(0xBB & 0xff)); /* RIGHT-POINTING DOUBLE ANGLE QUOTE */ case 0x00BC: return((CHAR)(0xBC & 0xff)); /* VULGAR FRACTION ONE QUARTER */ case 0x00BD: return((CHAR)(0xBD & 0xff)); /* VULGAR FRACTION ONE HALF */ case 0x00BE: return((CHAR)(0xBE & 0xff)); /* VULGAR FRACTION THREE QUARTERS */ case 0x00BF: return((CHAR)(0xBF & 0xff)); /* INVERTED QUESTION MARK */ case 0x05B0: return((CHAR)(0xC0 & 0xff)); /* HEBREW POINT SHEVA */ case 0x05B1: return((CHAR)(0xC1 & 0xff)); /* HEBREW POINT HATAF SEGOL */ case 0x05B2: return((CHAR)(0xC2 & 0xff)); /* HEBREW POINT HATAF PATAH */ case 0x05B3: return((CHAR)(0xC3 & 0xff)); /* HEBREW POINT HATAF QAMATS */ case 0x05B4: return((CHAR)(0xC4 & 0xff)); /* HEBREW POINT HIRIQ */ case 0x05B5: return((CHAR)(0xC5 & 0xff)); /* HEBREW POINT TSERE */ case 0x05B6: return((CHAR)(0xC6 & 0xff)); /* HEBREW POINT SEGOL */ case 0x05B7: return((CHAR)(0xC7 & 0xff)); /* HEBREW POINT PATAH */ case 0x05B8: return((CHAR)(0xC8 & 0xff)); /* HEBREW POINT QAMATS */ case 0x05B9: return((CHAR)(0xC9 & 0xff)); /* HEBREW POINT HOLAM */ case 0x05BB: return((CHAR)(0xCB & 0xff)); /* HEBREW POINT QUBUTS */ case 0x05BC: return((CHAR)(0xCC & 0xff)); /* HEBREW POINT DAGESH OR MAPIQ */ case 0x05BD: return((CHAR)(0xCD & 0xff)); /* HEBREW POINT METEG */ case 0x05BE: return((CHAR)(0xCE & 0xff)); /* HEBREW PUNCTUATION MAQAF */ case 0x05BF: return((CHAR)(0xCF & 0xff)); /* HEBREW POINT RAFE */ case 0x05C0: return((CHAR)(0xD0 & 0xff)); /* HEBREW PUNCTUATION PASEQ */ case 0x05C1: return((CHAR)(0xD1 & 0xff)); /* HEBREW POINT SHIN DOT */ case 0x05C2: return((CHAR)(0xD2 & 0xff)); /* HEBREW POINT SIN DOT */ case 0x05C3: return((CHAR)(0xD3 & 0xff)); /* HEBREW PUNCTUATION SOF PASUQ */ case 0x05F0: return((CHAR)(0xD4 & 0xff)); /* HEBREW LIG. YIDDISH DOUBLE VAV */ case 0x05F1: return((CHAR)(0xD5 & 0xff)); /* HEBREW LIGATURE YIDDISH VAV YOD */ case 0x05F2: return((CHAR)(0xD6 & 0xff)); /* HEBREW LIG. YIDDISH DOUBLE YOD */ case 0x05F3: return((CHAR)(0xD7 & 0xff)); /* HEBREW PUNCTUATION GERESH */ case 0x05F4: return((CHAR)(0xD8 & 0xff)); /* HEBREW PUNCTUATION GERSHAYIM */ case 0x05D0: return((CHAR)(0xE0 & 0xff)); /* HEBREW LETTER ALEF */ case 0x05D1: return((CHAR)(0xE1 & 0xff)); /* HEBREW LETTER BET */ case 0x05D2: return((CHAR)(0xE2 & 0xff)); /* HEBREW LETTER GIMEL */ case 0x05D3: return((CHAR)(0xE3 & 0xff)); /* HEBREW LETTER DALET */ case 0x05D4: return((CHAR)(0xE4 & 0xff)); /* HEBREW LETTER HE */ case 0x05D5: return((CHAR)(0xE5 & 0xff)); /* HEBREW LETTER VAV */ case 0x05D6: return((CHAR)(0xE6 & 0xff)); /* HEBREW LETTER ZAYIN */ case 0x05D7: return((CHAR)(0xE7 & 0xff)); /* HEBREW LETTER HET */ case 0x05D8: return((CHAR)(0xE8 & 0xff)); /* HEBREW LETTER TET */ case 0x05D9: return((CHAR)(0xE9 & 0xff)); /* HEBREW LETTER YOD */ case 0x05DA: return((CHAR)(0xEA & 0xff)); /* HEBREW LETTER FINAL KAF */ case 0x05DB: return((CHAR)(0xEB & 0xff)); /* HEBREW LETTER KAF */ case 0x05DC: return((CHAR)(0xEC & 0xff)); /* HEBREW LETTER LAMED */ case 0x05DD: return((CHAR)(0xED & 0xff)); /* HEBREW LETTER FINAL MEM */ case 0x05DE: return((CHAR)(0xEE & 0xff)); /* HEBREW LETTER MEM */ case 0x05DF: return((CHAR)(0xEF & 0xff)); /* HEBREW LETTER FINAL NUN */ case 0x05E0: return((CHAR)(0xF0 & 0xff)); /* HEBREW LETTER NUN */ case 0x05E1: return((CHAR)(0xF1 & 0xff)); /* HEBREW LETTER SAMEKH */ case 0x05E2: return((CHAR)(0xF2 & 0xff)); /* HEBREW LETTER AYIN */ case 0x05E3: return((CHAR)(0xF3 & 0xff)); /* HEBREW LETTER FINAL PE */ case 0x05E4: return((CHAR)(0xF4 & 0xff)); /* HEBREW LETTER PE */ case 0x05E5: return((CHAR)(0xF5 & 0xff)); /* HEBREW LETTER FINAL TSADI */ case 0x05E6: return((CHAR)(0xF6 & 0xff)); /* HEBREW LETTER TSADI */ case 0x05E7: return((CHAR)(0xF7 & 0xff)); /* HEBREW LETTER QOF */ case 0x05E8: return((CHAR)(0xF8 & 0xff)); /* HEBREW LETTER RESH */ case 0x05E9: return((CHAR)(0xF9 & 0xff)); /* HEBREW LETTER SHIN */ case 0x05EA: return((CHAR)(0xFA & 0xff)); /* HEBREW LETTER TAV */ case 0x200E: return((CHAR)(0xFD & 0xff)); /* LEFT-TO-RIGHT MARK */ case 0x200F: return((CHAR)(0xFE & 0xff)); /* RIGHT-TO-LEFT MARK */ default: return(0x003f); } } int /* Windows Arabic */ #ifdef CK_ANSIC tx_cp1256(USHORT c) #else tx_cp1256(c) USHORT c; #endif /* CK_ANSIC */ { if (c < 0x80) return((CHAR)(c & 0xff)); switch (c) { case 0x20AC: return((CHAR)(0x80 & 0xff)); /* EURO SIGN */ case 0x067E: return((CHAR)(0x81 & 0xff)); /* ARABIC LETTER PEH */ case 0x201A: return((CHAR)(0x82 & 0xff)); /* SINGLE LOW-9 QUOTATION MARK */ case 0x0192: return((CHAR)(0x83 & 0xff)); /* LATIN SMALL LETTER F WITH HOOK */ case 0x201E: return((CHAR)(0x84 & 0xff)); /* DOUBLE LOW-9 QUOTATION MARK */ case 0x2026: return((CHAR)(0x85 & 0xff)); /* HORIZONTAL ELLIPSIS */ case 0x2020: return((CHAR)(0x86 & 0xff)); /* DAGGER */ case 0x2021: return((CHAR)(0x87 & 0xff)); /* DOUBLE DAGGER */ case 0x02C6: return((CHAR)(0x88 & 0xff)); /* MODIFIER LETTER CIRCUMFLEX ACCENT */ case 0x2030: return((CHAR)(0x89 & 0xff)); /* PER MILLE SIGN */ case 0x2039: return((CHAR)(0x8B & 0xff)); /* SINGLE LEFT-POINTING ANGLE QUOTE */ case 0x0152: return((CHAR)(0x8C & 0xff)); /* LATIN CAPITAL LIGATURE OE */ case 0x0686: return((CHAR)(0x8D & 0xff)); /* ARABIC LETTER TCHEH */ case 0x0698: return((CHAR)(0x8E & 0xff)); /* ARABIC LETTER JEH */ case 0x06AF: return((CHAR)(0x90 & 0xff)); /* ARABIC LETTER GAF */ case 0x2018: return((CHAR)(0x91 & 0xff)); /* LEFT SINGLE QUOTATION MARK */ case 0x2019: return((CHAR)(0x92 & 0xff)); /* RIGHT SINGLE QUOTATION MARK */ case 0x201C: return((CHAR)(0x93 & 0xff)); /* LEFT DOUBLE QUOTATION MARK */ case 0x201D: return((CHAR)(0x94 & 0xff)); /* RIGHT DOUBLE QUOTATION MARK */ case 0x2022: return((CHAR)(0x95 & 0xff)); /* BULLET */ case 0x2013: return((CHAR)(0x96 & 0xff)); /* EN DASH */ case 0x2014: return((CHAR)(0x97 & 0xff)); /* EM DASH */ case 0x2122: return((CHAR)(0x99 & 0xff)); /* TRADE MARK SIGN */ case 0x203A: return((CHAR)(0x9B & 0xff)); /* SINGLE RIGHT-POINTING ANGLE QUOTE */ case 0x0153: return((CHAR)(0x9C & 0xff)); /* LATIN SMALL LIGATURE OE */ case 0x200C: return((CHAR)(0x9D & 0xff)); /* ZERO WIDTH NON-JOINER */ case 0x200D: return((CHAR)(0x9E & 0xff)); /* ZERO WIDTH JOINER */ case 0x00A0: return((CHAR)(0xA0 & 0xff)); /* NO-BREAK SPACE */ case 0x060C: return((CHAR)(0xA1 & 0xff)); /* ARABIC COMMA */ case 0x00A2: return((CHAR)(0xA2 & 0xff)); /* CENT SIGN */ case 0x00A3: return((CHAR)(0xA3 & 0xff)); /* POUND SIGN */ case 0x00A4: return((CHAR)(0xA4 & 0xff)); /* CURRENCY SIGN */ case 0x00A5: return((CHAR)(0xA5 & 0xff)); /* YEN SIGN */ case 0x00A6: return((CHAR)(0xA6 & 0xff)); /* BROKEN BAR */ case 0x00A7: return((CHAR)(0xA7 & 0xff)); /* SECTION SIGN */ case 0x00A8: return((CHAR)(0xA8 & 0xff)); /* DIAERESIS */ case 0x00A9: return((CHAR)(0xA9 & 0xff)); /* COPYRIGHT SIGN */ case 0x00AB: return((CHAR)(0xAB & 0xff)); /* LEFT-POINTING DOUBLE ANGLE QUOTE */ case 0x00AC: return((CHAR)(0xAC & 0xff)); /* NOT SIGN */ case 0x00AD: return((CHAR)(0xAD & 0xff)); /* SOFT HYPHEN */ case 0x00AE: return((CHAR)(0xAE & 0xff)); /* REGISTERED SIGN */ case 0x00AF: return((CHAR)(0xAF & 0xff)); /* MACRON */ case 0x00B0: return((CHAR)(0xB0 & 0xff)); /* DEGREE SIGN */ case 0x00B1: return((CHAR)(0xB1 & 0xff)); /* PLUS-MINUS SIGN */ case 0x00B2: return((CHAR)(0xB2 & 0xff)); /* SUPERSCRIPT TWO */ case 0x00B3: return((CHAR)(0xB3 & 0xff)); /* SUPERSCRIPT THREE */ case 0x00B4: return((CHAR)(0xB4 & 0xff)); /* ACUTE ACCENT */ case 0x00B5: return((CHAR)(0xB5 & 0xff)); /* MICRO SIGN */ case 0x00B6: return((CHAR)(0xB6 & 0xff)); /* PILCROW SIGN */ case 0x00B7: return((CHAR)(0xB7 & 0xff)); /* MIDDLE DOT */ case 0x00B8: return((CHAR)(0xB8 & 0xff)); /* CEDILLA */ case 0x00B9: return((CHAR)(0xB9 & 0xff)); /* SUPERSCRIPT ONE */ case 0x061B: return((CHAR)(0xBA & 0xff)); /* ARABIC SEMICOLON */ case 0x00BB: return((CHAR)(0xBB & 0xff)); /* RIGHT-POINTING DOUBLE ANGLE QUOTE */ case 0x00BC: return((CHAR)(0xBC & 0xff)); /* VULGAR FRACTION ONE QUARTER */ case 0x00BD: return((CHAR)(0xBD & 0xff)); /* VULGAR FRACTION ONE HALF */ case 0x00BE: return((CHAR)(0xBE & 0xff)); /* VULGAR FRACTION THREE QUARTERS */ case 0x061F: return((CHAR)(0xBF & 0xff)); /* ARABIC QUESTION MARK */ case 0x0621: return((CHAR)(0xC1 & 0xff)); /* ARABIC LETTER HAMZA */ case 0x0622: return((CHAR)(0xC2 & 0xff)); /* ARABIC LTR. ALEF WITH MADDA ABOVE */ case 0x0623: return((CHAR)(0xC3 & 0xff)); /* ARABIC LTR. ALEF WITH HAMZA ABOVE */ case 0x0624: return((CHAR)(0xC4 & 0xff)); /* ARABIC LTR. WAW WITH HAMZA ABOVE */ case 0x0625: return((CHAR)(0xC5 & 0xff)); /* ARABIC LTR. ALEF WITH HAMZA BELOW */ case 0x0626: return((CHAR)(0xC6 & 0xff)); /* ARABIC LTR. YEH WITH HAMZA ABOVE */ case 0x0627: return((CHAR)(0xC7 & 0xff)); /* ARABIC LTR. ALEF */ case 0x0628: return((CHAR)(0xC8 & 0xff)); /* ARABIC LTR. BEH */ case 0x0629: return((CHAR)(0xC9 & 0xff)); /* ARABIC LETTER TEH MARBUTA */ case 0x062A: return((CHAR)(0xCA & 0xff)); /* ARABIC LETTER TEH */ case 0x062B: return((CHAR)(0xCB & 0xff)); /* ARABIC LETTER THEH */ case 0x062C: return((CHAR)(0xCC & 0xff)); /* ARABIC LETTER JEEM */ case 0x062D: return((CHAR)(0xCD & 0xff)); /* ARABIC LETTER HAH */ case 0x062E: return((CHAR)(0xCE & 0xff)); /* ARABIC LETTER KHAH */ case 0x062F: return((CHAR)(0xCF & 0xff)); /* ARABIC LETTER DAL */ case 0x0630: return((CHAR)(0xD0 & 0xff)); /* ARABIC LETTER THAL */ case 0x0631: return((CHAR)(0xD1 & 0xff)); /* ARABIC LETTER REH */ case 0x0632: return((CHAR)(0xD2 & 0xff)); /* ARABIC LETTER ZAIN */ case 0x0633: return((CHAR)(0xD3 & 0xff)); /* ARABIC LETTER SEEN */ case 0x0634: return((CHAR)(0xD4 & 0xff)); /* ARABIC LETTER SHEEN */ case 0x0635: return((CHAR)(0xD5 & 0xff)); /* ARABIC LETTER SAD */ case 0x0636: return((CHAR)(0xD6 & 0xff)); /* ARABIC LETTER DAD */ case 0x00D7: return((CHAR)(0xD7 & 0xff)); /* MULTIPLICATION SIGN */ case 0x0637: return((CHAR)(0xD8 & 0xff)); /* ARABIC LETTER TAH */ case 0x0638: return((CHAR)(0xD9 & 0xff)); /* ARABIC LETTER ZAH */ case 0x0639: return((CHAR)(0xDA & 0xff)); /* ARABIC LETTER AIN */ case 0x063A: return((CHAR)(0xDB & 0xff)); /* ARABIC LETTER GHAIN */ case 0x0640: return((CHAR)(0xDC & 0xff)); /* ARABIC TATWEEL */ case 0x0641: return((CHAR)(0xDD & 0xff)); /* ARABIC LETTER FEH */ case 0x0642: return((CHAR)(0xDE & 0xff)); /* ARABIC LETTER QAF */ case 0x0643: return((CHAR)(0xDF & 0xff)); /* ARABIC LETTER KAF */ case 0x00E0: return((CHAR)(0xE0 & 0xff)); /* LATIN SMALL LETTER A WITH GRAVE */ case 0x0644: return((CHAR)(0xE1 & 0xff)); /* ARABIC LETTER LAM */ case 0x00E2: return((CHAR)(0xE2 & 0xff)); /* SMALL LETTER A WITH CIRCUMFLEX */ case 0x0645: return((CHAR)(0xE3 & 0xff)); /* ARABIC LETTER MEEM */ case 0x0646: return((CHAR)(0xE4 & 0xff)); /* ARABIC LETTER NOON */ case 0x0647: return((CHAR)(0xE5 & 0xff)); /* ARABIC LETTER HEH */ case 0x0648: return((CHAR)(0xE6 & 0xff)); /* ARABIC LETTER WAW */ case 0x00E7: return((CHAR)(0xE7 & 0xff)); /* LATIN SMALL LETTER C WITH CEDILLA */ case 0x00E8: return((CHAR)(0xE8 & 0xff)); /* LATIN SMALL LETTER E WITH GRAVE */ case 0x00E9: return((CHAR)(0xE9 & 0xff)); /* LATIN SMALL LETTER E WITH ACUTE */ case 0x00EA: return((CHAR)(0xEA & 0xff)); /* SMALL LETTER E WITH CIRCUMFLEX */ case 0x00EB: return((CHAR)(0xEB & 0xff)); /* SMALL LETTER E WITH DIAERESIS */ case 0x0649: return((CHAR)(0xEC & 0xff)); /* ARABIC LETTER ALEF MAKSURA */ case 0x064A: return((CHAR)(0xED & 0xff)); /* ARABIC LETTER YEH */ case 0x00EE: return((CHAR)(0xEE & 0xff)); /* SMALL LETTER I WITH CIRCUMFLEX */ case 0x00EF: return((CHAR)(0xEF & 0xff)); /* SMALL LETTER I WITH DIAERESIS */ case 0x064B: return((CHAR)(0xF0 & 0xff)); /* ARABIC FATHATAN */ case 0x064C: return((CHAR)(0xF1 & 0xff)); /* ARABIC DAMMATAN */ case 0x064D: return((CHAR)(0xF2 & 0xff)); /* ARABIC KASRATAN */ case 0x064E: return((CHAR)(0xF3 & 0xff)); /* ARABIC FATHA */ case 0x00F4: return((CHAR)(0xF4 & 0xff)); /* SMALL LETTER O WITH CIRCUMFLEX */ case 0x064F: return((CHAR)(0xF5 & 0xff)); /* ARABIC DAMMA */ case 0x0650: return((CHAR)(0xF6 & 0xff)); /* ARABIC KASRA */ case 0x00F7: return((CHAR)(0xF7 & 0xff)); /* DIVISION SIGN */ case 0x0651: return((CHAR)(0xF8 & 0xff)); /* ARABIC SHADDA */ case 0x00F9: return((CHAR)(0xF9 & 0xff)); /* LATIN SMALL LETTER U WITH GRAVE */ case 0x0652: return((CHAR)(0xFA & 0xff)); /* ARABIC SUKUN */ case 0x00FB: return((CHAR)(0xFB & 0xff)); /* SMALL LETTER U WITH CIRCUMFLEX */ case 0x00FC: return((CHAR)(0xFC & 0xff)); /* SMALL LETTER U WITH DIAERESIS */ case 0x200E: return((CHAR)(0xFD & 0xff)); /* LEFT-TO-RIGHT MARK */ case 0x200F: return((CHAR)(0xFE & 0xff)); /* RIGHT-TO-LEFT MARK */ default: return(0x003f); } } int /* Windows Code Page 1257 (Latin-4) */ #ifdef CK_ANSIC tx_cp1257(USHORT c) #else tx_cp1257(c) USHORT c; #endif /* CK_ANSIC */ { if (c < 0x80) return((CHAR)(c & 0xff)); switch (c) { case 0x003c: return((CHAR)(0x8b & 0xff)); case 0x003e: return((CHAR)(0x9b & 0xff)); case 0x00A0: return((CHAR)(0xa0 & 0xff)); case 0x00A2: return((CHAR)(0xa2 & 0xff)); case 0x00A3: return((CHAR)(0xa3 & 0xff)); case 0x00A4: return((CHAR)(0xa4 & 0xff)); case 0x00A6: return((CHAR)(0xa6 & 0xff)); case 0x00A7: return((CHAR)(0xa7 & 0xff)); case 0x00A9: return((CHAR)(0xa9 & 0xff)); case 0x00AB: return((CHAR)(0xab & 0xff)); case 0x00AC: return((CHAR)(0xac & 0xff)); case 0x00AD: return((CHAR)(0xad & 0xff)); case 0x00AE: return((CHAR)(0xae & 0xff)); case 0x00B0: return((CHAR)(0xb0 & 0xff)); case 0x00B1: return((CHAR)(0xb1 & 0xff)); case 0x00B2: return((CHAR)(0xb2 & 0xff)); case 0x00B3: return((CHAR)(0xb3 & 0xff)); case 0x00B5: return((CHAR)(0xb5 & 0xff)); case 0x00B6: return((CHAR)(0xb6 & 0xff)); case 0x00B7: return((CHAR)(0xb7 & 0xff)); case 0x00B9: return((CHAR)(0xb9 & 0xff)); case 0x00BB: return((CHAR)(0xbb & 0xff)); case 0x00BC: return((CHAR)(0xbc & 0xff)); case 0x00BD: return((CHAR)(0xbd & 0xff)); case 0x00BE: return((CHAR)(0xbe & 0xff)); case 0x00C4: return((CHAR)(0xc4 & 0xff)); case 0x00C5: return((CHAR)(0xc5 & 0xff)); case 0x00c6: return((CHAR)(0xaf & 0xff)); case 0x00C9: return((CHAR)(0xc9 & 0xff)); case 0x00d3: return((CHAR)(0xd3 & 0xff)); case 0x00D5: return((CHAR)(0xd5 & 0xff)); case 0x00D6: return((CHAR)(0xd6 & 0xff)); case 0x00D7: return((CHAR)(0xd7 & 0xff)); case 0x00d8: return((CHAR)(0xa8 & 0xff)); case 0x00DC: return((CHAR)(0xdc & 0xff)); case 0x00DF: return((CHAR)(0xdf & 0xff)); case 0x00E4: return((CHAR)(0xe4 & 0xff)); case 0x00E5: return((CHAR)(0xe5 & 0xff)); case 0x00e6: return((CHAR)(0xbf & 0xff)); case 0x00E9: return((CHAR)(0xe9 & 0xff)); case 0x00f3: return((CHAR)(0xf3 & 0xff)); case 0x00F5: return((CHAR)(0xf5 & 0xff)); case 0x00F6: return((CHAR)(0xf6 & 0xff)); case 0x00F7: return((CHAR)(0xf7 & 0xff)); case 0x00f8: return((CHAR)(0xb8 & 0xff)); case 0x00fc: return((CHAR)(0xfc & 0xff)); case 0x0100: return((CHAR)(0xc2 & 0xff)); case 0x0101: return((CHAR)(0xe2 & 0xff)); case 0x0104: return((CHAR)(0xc0 & 0xff)); case 0x0105: return((CHAR)(0xe0 & 0xff)); case 0x0106: return((CHAR)(0xc3 & 0xff)); case 0x0107: return((CHAR)(0xe3 & 0xff)); case 0x010C: return((CHAR)(0xc8 & 0xff)); case 0x010D: return((CHAR)(0xe8 & 0xff)); case 0x0112: return((CHAR)(0xc7 & 0xff)); case 0x0113: return((CHAR)(0xe7 & 0xff)); case 0x0116: return((CHAR)(0xcb & 0xff)); case 0x0117: return((CHAR)(0xeb & 0xff)); case 0x0118: return((CHAR)(0xc6 & 0xff)); case 0x0119: return((CHAR)(0xe6 & 0xff)); case 0x0122: return((CHAR)(0xcc & 0xff)); case 0x0123: return((CHAR)(0xec & 0xff)); case 0x012a: return((CHAR)(0xce & 0xff)); case 0x012b: return((CHAR)(0xee & 0xff)); case 0x012e: return((CHAR)(0xc1 & 0xff)); case 0x012f: return((CHAR)(0xe1 & 0xff)); case 0x0136: return((CHAR)(0xcd & 0xff)); case 0x0137: return((CHAR)(0xed & 0xff)); case 0x013c: return((CHAR)(0xef & 0xff)); case 0x0141: return((CHAR)(0xd9 & 0xff)); case 0x0142: return((CHAR)(0xf9 & 0xff)); case 0x0143: return((CHAR)(0xd1 & 0xff)); case 0x0144: return((CHAR)(0xf1 & 0xff)); case 0x0145: return((CHAR)(0xd2 & 0xff)); case 0x0146: return((CHAR)(0xf2 & 0xff)); case 0x014c: return((CHAR)(0xd4 & 0xff)); case 0x014d: return((CHAR)(0xf4 & 0xff)); case 0x0156: return((CHAR)(0xaa & 0xff)); case 0x0157: return((CHAR)(0xba & 0xff)); case 0x015A: return((CHAR)(0xda & 0xff)); case 0x015b: return((CHAR)(0xfa & 0xff)); case 0x0160: return((CHAR)(0xd0 & 0xff)); case 0x0161: return((CHAR)(0xf0 & 0xff)); case 0x016a: return((CHAR)(0xdb & 0xff)); case 0x016b: return((CHAR)(0xfb & 0xff)); case 0x0172: return((CHAR)(0xd8 & 0xff)); case 0x0173: return((CHAR)(0xf8 & 0xff)); case 0x0179: return((CHAR)(0xca & 0xff)); case 0x017a: return((CHAR)(0xea & 0xff)); case 0x017b: return((CHAR)(0xdd & 0xff)); case 0x017c: return((CHAR)(0xfd & 0xff)); case 0x017d: return((CHAR)(0xde & 0xff)); case 0x017e: return((CHAR)(0xfe & 0xff)); case 0x2012: return((CHAR)(0x96 & 0xff)); case 0x2014: return((CHAR)(0x97 & 0xff)); case 0x2018: return((CHAR)(0x91 & 0xff)); case 0x2019: return((CHAR)(0x92 & 0xff)); case 0x201a: return((CHAR)(0x82 & 0xff)); case 0x201c: return((CHAR)(0x93 & 0xff)); case 0x201d: return((CHAR)(0x94 & 0xff)); case 0x201e: return((CHAR)(0x84 & 0xff)); case 0x2020: return((CHAR)(0x86 & 0xff)); case 0x2021: return((CHAR)(0x87 & 0xff)); case 0x2022: return((CHAR)(0x95 & 0xff)); case 0x2026: return((CHAR)(0x85 & 0xff)); case 0x2031: return((CHAR)(0x89 & 0xff)); case 0x20AC: /* Euro */ return((CHAR)(0x80 & 0xff)); case 0x2122: return((CHAR)(0x99 & 0xff)); default: return(0x003f); } } int /* Windows Code Page 1258 (Viet Nam) */ #ifdef CK_ANSIC tx_cp1258(USHORT c) #else tx_cp1258(c) USHORT c; #endif /* CK_ANSIC */ { if (c < 0x80) /* Has C1 graphics */ return((CHAR)(c & 0xff)); switch (c) { case 0x20AC: return((CHAR)(0x80 & 0xff)); /* EURO SIGN */ case 0x201A: return((CHAR)(0x82 & 0xff)); /* SINGLE LOW-9 QUOTATION MARK */ case 0x0192: return((CHAR)(0x83 & 0xff)); /* LATIN SMALL LETTER F WITH HOOK */ case 0x201E: return((CHAR)(0x84 & 0xff)); /* DOUBLE LOW-9 QUOTATION MARK */ case 0x2026: return((CHAR)(0x85 & 0xff)); /* HORIZONTAL ELLIPSIS */ case 0x2020: return((CHAR)(0x86 & 0xff)); /* DAGGER */ case 0x2021: return((CHAR)(0x87 & 0xff)); /* DOUBLE DAGGER */ case 0x02C6: return((CHAR)(0x88 & 0xff)); /* MODIFIER LETTER CIRCUMFLEX ACCENT */ case 0x2030: return((CHAR)(0x89 & 0xff)); /* PER MILLE SIGN */ case 0x2039: return((CHAR)(0x8B & 0xff)); /* SINGLE LEFT ANGLE QUOTATION MARK */ case 0x0152: return((CHAR)(0x8C & 0xff)); /* LATIN CAPITAL LIGATURE OE */ case 0x2018: return((CHAR)(0x91 & 0xff)); /* LEFT SINGLE QUOTATION MARK */ case 0x2019: return((CHAR)(0x92 & 0xff)); /* RIGHT SINGLE QUOTATION MARK */ case 0x201C: return((CHAR)(0x93 & 0xff)); /* LEFT DOUBLE QUOTATION MARK */ case 0x201D: return((CHAR)(0x94 & 0xff)); /* RIGHT DOUBLE QUOTATION MARK */ case 0x2022: return((CHAR)(0x95 & 0xff)); /* BULLET */ case 0x2013: return((CHAR)(0x96 & 0xff)); /* EN DASH */ case 0x2014: return((CHAR)(0x97 & 0xff)); /* EM DASH */ case 0x02DC: return((CHAR)(0x98 & 0xff)); /* SMALL TILDE */ case 0x2122: return((CHAR)(0x99 & 0xff)); /* TRADE MARK SIGN */ case 0x203A: /* SINGLE RIGHT-POINTING ANGLE QUOTATION MAR K*/ return((CHAR)(0x9B & 0xff)); case 0x0153: return((CHAR)(0x9C & 0xff)); /* LATIN SMALL LIGATURE OE */ case 0x0178: /* LATIN CAPITAL LETTER Y WITH DIAERESIS */ return((CHAR)(0x9F & 0xff)); case 0x00A0: return((CHAR)(0xA0 & 0xff)); /* NO-BREAK SPACE */ case 0x00A1: return((CHAR)(0xA1 & 0xff)); /* INVERTED EXCLAMATION MARK */ case 0x00A2: return((CHAR)(0xA2 & 0xff)); /* CENT SIGN */ case 0x00A3: return((CHAR)(0xA3 & 0xff)); /* POUND SIGN */ case 0x00A4: return((CHAR)(0xA4 & 0xff)); /* CURRENCY SIGN */ case 0x00A5: return((CHAR)(0xA5 & 0xff)); /* YEN SIGN */ case 0x00A6: return((CHAR)(0xA6 & 0xff)); /* BROKEN BAR */ case 0x00A7: return((CHAR)(0xA7 & 0xff)); /* SECTION SIGN */ case 0x00A8: return((CHAR)(0xA8 & 0xff)); /* DIAERESIS */ case 0x00A9: return((CHAR)(0xA9 & 0xff)); /* COPYRIGHT SIGN */ case 0x00AA: return((CHAR)(0xAA & 0xff)); /* FEMININE ORDINAL INDICATOR */ case 0x00AB: /* LEFT-POINTING DOUBLE ANGLE QUOTATION MARK */ return((CHAR)(0xAB & 0xff)); case 0x00AC: return((CHAR)(0xAC & 0xff)); /* NOT SIGN */ case 0x00AD: return((CHAR)(0xAD & 0xff)); /* SOFT HYPHEN */ case 0x00AE: return((CHAR)(0xAE & 0xff)); /* REGISTERED SIGN */ case 0x00AF: return((CHAR)(0xAF & 0xff)); /* MACRON */ case 0x00B0: return((CHAR)(0xB0 & 0xff)); /* DEGREE SIGN */ case 0x00B1: return((CHAR)(0xB1 & 0xff)); /* PLUS-MINUS SIGN */ case 0x00B2: return((CHAR)(0xB2 & 0xff)); /* SUPERSCRIPT TWO */ case 0x00B3: return((CHAR)(0xB3 & 0xff)); /* SUPERSCRIPT THREE */ case 0x00B4: return((CHAR)(0xB4 & 0xff)); /* ACUTE ACCENT */ case 0x00B5: return((CHAR)(0xB5 & 0xff)); /* MICRO SIGN */ case 0x00B6: return((CHAR)(0xB6 & 0xff)); /* PILCROW SIGN */ case 0x00B7: return((CHAR)(0xB7 & 0xff)); /* MIDDLE DOT */ case 0x00B8: return((CHAR)(0xB8 & 0xff)); /* CEDILLA */ case 0x00B9: return((CHAR)(0xB9 & 0xff)); /* SUPERSCRIPT ONE */ case 0x00BA: return((CHAR)(0xBA & 0xff)); /* MASCULINE ORDINAL INDICATOR */ case 0x00BB: /* RIGHT-POINTING DOUBLE ANGLE QUOTATION MAR K*/ return((CHAR)(0xBB & 0xff)); case 0x00BC: return((CHAR)(0xBC & 0xff)); /* VULGAR FRACTION ONE QUARTER */ case 0x00BD: return((CHAR)(0xBD & 0xff)); /* VULGAR FRACTION ONE HALF */ case 0x00BE: return((CHAR)(0xBE & 0xff)); /* VULGAR FRACTION THREE QUARTERS */ case 0x00BF: return((CHAR)(0xBF & 0xff)); /* INVERTED QUESTION MARK */ case 0x00C0: return((CHAR)(0xC0 & 0xff)); /* LATIN CAPITAL LETTER A WITH GRAVE */ case 0x00C1: return((CHAR)(0xC1 & 0xff)); /* LATIN CAPITAL LETTER A WITH ACUTE */ case 0x00C2: return((CHAR)(0xC2 & 0xff)); /* A CIRCUMFLEX */ case 0x0102: return((CHAR)(0xC3 & 0xff)); /* LATIN CAPITAL LETTER A WITH BREVE */ case 0x00C4: return((CHAR)(0xC4 & 0xff)); /* A DIAERESIS */ case 0x00C5: return((CHAR)(0xC5 & 0xff)); /* A RING */ case 0x00C6: return((CHAR)(0xC6 & 0xff)); /* LATIN CAPITAL LETTER AE */ case 0x00C7: return((CHAR)(0xC7 & 0xff)); /* C CEDILLA */ case 0x00C8: return((CHAR)(0xC8 & 0xff)); /* E GRAVE */ case 0x00C9: return((CHAR)(0xC9 & 0xff)); /* LATIN CAPITAL LETTER E WITH ACUTE */ case 0x00CA: return((CHAR)(0xCA & 0xff)); /* E WITH CIRCUMFLEX */ case 0x00CB: return((CHAR)(0xCB & 0xff)); /* E WITH DIAERESIS */ case 0x0300: return((CHAR)(0xCC & 0xff)); /* COMBINING GRAVE ACCENT */ case 0x00CD: return((CHAR)(0xCD & 0xff)); /* I WITH ACUTE */ case 0x00CE: return((CHAR)(0xCE & 0xff)); /* I WITH CIRCUMFLEX */ case 0x00CF: return((CHAR)(0xCF & 0xff)); /* I WITH DIAERESIS */ case 0x0110: return((CHAR)(0xD0 & 0xff)); /* D WITH STROKE */ case 0x00D1: return((CHAR)(0xD1 & 0xff)); /* LATIN CAPITAL LETTER N WITH TILDE */ case 0x0309: return((CHAR)(0xD2 & 0xff)); /* COMBINING HOOK ABOVE */ case 0x00D3: return((CHAR)(0xD3 & 0xff)); /* LATIN CAPITAL LETTER O WITH ACUTE */ case 0x00D4: return((CHAR)(0xD4 & 0xff)); /* O WITH CIRCUMFLEX */ case 0x01A0: return((CHAR)(0xD5 & 0xff)); /* LATIN CAPITAL LETTER O WITH HORN */ case 0x00D6: return((CHAR)(0xD6 & 0xff)); /* O WITH DIAERESIS */ case 0x00D7: return((CHAR)(0xD7 & 0xff)); /* MULTIPLICATION SIGN */ case 0x00D8: return((CHAR)(0xD8 & 0xff)); /* O WITH STROKE */ case 0x00D9: return((CHAR)(0xD9 & 0xff)); /* LATIN CAPITAL LETTER U WITH GRAVE */ case 0x00DA: return((CHAR)(0xDA & 0xff)); /* LATIN CAPITAL LETTER U WITH ACUTE */ case 0x00DB: return((CHAR)(0xDB & 0xff)); /* U WITH CIRCUMFLEX */ case 0x00DC: return((CHAR)(0xDC & 0xff)); /* U WITH DIAERESIS */ case 0x01AF: return((CHAR)(0xDD & 0xff)); /* LATIN CAPITAL LETTER U WITH HORN */ case 0x0303: return((CHAR)(0xDE & 0xff)); /* COMBINING TILDE */ case 0x00DF: return((CHAR)(0xDF & 0xff)); /* LATIN SMALL LETTER SHARP S */ case 0x00E0: return((CHAR)(0xE0 & 0xff)); /* LATIN SMALL LETTER A WITH GRAVE */ case 0x00E1: return((CHAR)(0xE1 & 0xff)); /* LATIN SMALL LETTER A WITH ACUTE */ case 0x00E2: return((CHAR)(0xE2 & 0xff)); /* SMALL A WITH CIRCUMFLEX */ case 0x0103: return((CHAR)(0xE3 & 0xff)); /* LATIN SMALL LETTER A WITH BREVE */ case 0x00E4: return((CHAR)(0xE4 & 0xff)); /* SMALL A WITH DIAERESIS */ case 0x00E5: return((CHAR)(0xE5 & 0xff)); /* SMALL A WITH RING ABOVE */ case 0x00E6: return((CHAR)(0xE6 & 0xff)); /* LATIN SMALL LETTER AE */ case 0x00E7: return((CHAR)(0xE7 & 0xff)); /* LATIN SMALL LETTER C WITH CEDILLA */ case 0x00E8: return((CHAR)(0xE8 & 0xff)); /* LATIN SMALL LETTER E WITH GRAVE */ case 0x00E9: return((CHAR)(0xE9 & 0xff)); /* LATIN SMALL LETTER E WITH ACUTE */ case 0x00EA: return((CHAR)(0xEA & 0xff)); /* SMALL E WITH CIRCUMFLEX */ case 0x00EB: return((CHAR)(0xEB & 0xff)); /* SMALL E WITH DIAERESIS */ case 0x0301: return((CHAR)(0xEC & 0xff)); /* COMBINING ACUTE ACCENT */ case 0x00ED: return((CHAR)(0xED & 0xff)); /* LATIN SMALL LETTER I WITH ACUTE */ case 0x00EE: return((CHAR)(0xEE & 0xff)); /* SMALL I WITH CIRCUMFLEX */ case 0x00EF: return((CHAR)(0xEF & 0xff)); /* SMALL I WITH DIAERESIS */ case 0x0111: return((CHAR)(0xF0 & 0xff)); /* LATIN SMALL LETTER D WITH STROKE */ case 0x00F1: return((CHAR)(0xF1 & 0xff)); /* LATIN SMALL LETTER N WITH TILDE */ case 0x0323: return((CHAR)(0xF2 & 0xff)); /* COMBINING DOT BELOW */ case 0x00F3: return((CHAR)(0xF3 & 0xff)); /* LATIN SMALL LETTER O WITH ACUTE */ case 0x00F4: return((CHAR)(0xF4 & 0xff)); /* SMALL O WITH CIRCUMFLEX */ case 0x01A1: return((CHAR)(0xF5 & 0xff)); /* LATIN SMALL LETTER O WITH HORN */ case 0x00F6: return((CHAR)(0xF6 & 0xff)); /* SMALL O WITH DIAERESIS */ case 0x00F7: return((CHAR)(0xF7 & 0xff)); /* DIVISION SIGN */ case 0x00F8: return((CHAR)(0xF8 & 0xff)); /* LATIN SMALL LETTER O WITH STROKE */ case 0x00F9: return((CHAR)(0xF9 & 0xff)); /* LATIN SMALL LETTER U WITH GRAVE */ case 0x00FA: return((CHAR)(0xFA & 0xff)); /* LATIN SMALL LETTER U WITH ACUTE */ case 0x00FB: return((CHAR)(0xFB & 0xff)); /* SMALL U WITH CIRCUMFLEX */ case 0x00FC: return((CHAR)(0xFC & 0xff)); /* SMALL U WITH DIAERESIS */ case 0x01B0: return((CHAR)(0xFD & 0xff)); /* LATIN SMALL LETTER U WITH HORN */ case 0x20AB: return((CHAR)(0xFE & 0xff)); /* DONG SIGN */ case 0x00FF: return((CHAR)(0xFF & 0xff)); /* SMALL Y WITH DIAERESIS */ default: return(0x003f); } } int /* Code Page 037 - EBCDIC (U.S.) */ #ifdef CK_ANSIC tx_cp37(USHORT c) #else tx_cp37(c) USHORT c; #endif /* CK_ANSIC */ { switch (c) { case 0x0000: return((CHAR)(0x00 & 0xff)); /* NULL */ case 0x0001: return((CHAR)(0x01 & 0xff)); /* START OF HEADING */ case 0x0002: return((CHAR)(0x02 & 0xff)); /* START OF TEXT */ case 0x0003: return((CHAR)(0x03 & 0xff)); /* END OF TEXT */ case 0x009C: return((CHAR)(0x04 & 0xff)); /* CONTROL */ case 0x0009: return((CHAR)(0x05 & 0xff)); /* HORIZONTAL TABULATION */ case 0x0086: return((CHAR)(0x06 & 0xff)); /* CONTROL */ case 0x007F: return((CHAR)(0x07 & 0xff)); /* DELETE */ case 0x0097: return((CHAR)(0x08 & 0xff)); /* CONTROL */ case 0x008D: return((CHAR)(0x09 & 0xff)); /* CONTROL */ case 0x008E: return((CHAR)(0x0A & 0xff)); /* CONTROL */ case 0x000B: return((CHAR)(0x0B & 0xff)); /* VERTICAL TABULATION */ case 0x000C: return((CHAR)(0x0C & 0xff)); /* FORM FEED */ case 0x000D: return((CHAR)(0x0D & 0xff)); /* CARRIAGE RETURN */ case 0x000E: return((CHAR)(0x0E & 0xff)); /* SHIFT OUT */ case 0x000F: return((CHAR)(0x0F & 0xff)); /* SHIFT IN */ case 0x0010: return((CHAR)(0x10 & 0xff)); /* DATA LINK ESCAPE */ case 0x0011: return((CHAR)(0x11 & 0xff)); /* DEVICE CONTROL ONE */ case 0x0012: return((CHAR)(0x12 & 0xff)); /* DEVICE CONTROL TWO */ case 0x0013: return((CHAR)(0x13 & 0xff)); /* DEVICE CONTROL THREE */ case 0x009D: return((CHAR)(0x14 & 0xff)); /* CONTROL */ case 0x0085: return((CHAR)(0x15 & 0xff)); /* CONTROL */ case 0x0008: return((CHAR)(0x16 & 0xff)); /* BACKSPACE */ case 0x0087: return((CHAR)(0x17 & 0xff)); /* CONTROL */ case 0x0018: return((CHAR)(0x18 & 0xff)); /* CANCEL */ case 0x0019: return((CHAR)(0x19 & 0xff)); /* END OF MEDIUM */ case 0x0092: return((CHAR)(0x1A & 0xff)); /* CONTROL */ case 0x008F: return((CHAR)(0x1B & 0xff)); /* CONTROL */ case 0x001C: return((CHAR)(0x1C & 0xff)); /* FILE SEPARATOR */ case 0x001D: return((CHAR)(0x1D & 0xff)); /* GROUP SEPARATOR */ case 0x001E: return((CHAR)(0x1E & 0xff)); /* RECORD SEPARATOR */ case 0x001F: return((CHAR)(0x1F & 0xff)); /* UNIT SEPARATOR */ case 0x0080: return((CHAR)(0x20 & 0xff)); /* CONTROL */ case 0x0081: return((CHAR)(0x21 & 0xff)); /* CONTROL */ case 0x0082: return((CHAR)(0x22 & 0xff)); /* CONTROL */ case 0x0083: return((CHAR)(0x23 & 0xff)); /* CONTROL */ case 0x0084: return((CHAR)(0x24 & 0xff)); /* CONTROL */ case 0x000A: return((CHAR)(0x25 & 0xff)); /* LINE FEED */ case 0x0017: return((CHAR)(0x26 & 0xff)); /* END OF TRANSMISSION BLOCK */ case 0x001B: return((CHAR)(0x27 & 0xff)); /* ESCAPE */ case 0x0088: return((CHAR)(0x28 & 0xff)); /* CONTROL */ case 0x0089: return((CHAR)(0x29 & 0xff)); /* CONTROL */ case 0x008A: return((CHAR)(0x2A & 0xff)); /* CONTROL */ case 0x008B: return((CHAR)(0x2B & 0xff)); /* CONTROL */ case 0x008C: return((CHAR)(0x2C & 0xff)); /* CONTROL */ case 0x0005: return((CHAR)(0x2D & 0xff)); /* ENQUIRY */ case 0x0006: return((CHAR)(0x2E & 0xff)); /* ACKNOWLEDGE */ case 0x0007: return((CHAR)(0x2F & 0xff)); /* BELL */ case 0x0090: return((CHAR)(0x30 & 0xff)); /* CONTROL */ case 0x0091: return((CHAR)(0x31 & 0xff)); /* CONTROL */ case 0x0016: return((CHAR)(0x32 & 0xff)); /* SYNCHRONOUS IDLE */ case 0x0093: return((CHAR)(0x33 & 0xff)); /* CONTROL */ case 0x0094: return((CHAR)(0x34 & 0xff)); /* CONTROL */ case 0x0095: return((CHAR)(0x35 & 0xff)); /* CONTROL */ case 0x0096: return((CHAR)(0x36 & 0xff)); /* CONTROL */ case 0x0004: return((CHAR)(0x37 & 0xff)); /* END OF TRANSMISSION */ case 0x0098: return((CHAR)(0x38 & 0xff)); /* CONTROL */ case 0x0099: return((CHAR)(0x39 & 0xff)); /* CONTROL */ case 0x009A: return((CHAR)(0x3A & 0xff)); /* CONTROL */ case 0x009B: return((CHAR)(0x3B & 0xff)); /* CONTROL */ case 0x0014: return((CHAR)(0x3C & 0xff)); /* DEVICE CONTROL FOUR */ case 0x0015: return((CHAR)(0x3D & 0xff)); /* NEGATIVE ACKNOWLEDGE */ case 0x009E: return((CHAR)(0x3E & 0xff)); /* CONTROL */ case 0x001A: return((CHAR)(0x3F & 0xff)); /* SUBSTITUTE */ case 0x0020: return((CHAR)(0x40 & 0xff)); /* SPACE */ case 0x00A0: return((CHAR)(0x41 & 0xff)); /* NO-BREAK SPACE */ case 0x00E2: return((CHAR)(0x42 & 0xff)); /* SMALL LETTER A WITH CIRCUMFLEX */ case 0x00E4: return((CHAR)(0x43 & 0xff)); /* SMALL LETTER A WITH DIAERESIS */ case 0x00E0: return((CHAR)(0x44 & 0xff)); /* LATIN SMALL LETTER A WITH GRAVE */ case 0x00E1: return((CHAR)(0x45 & 0xff)); /* LATIN SMALL LETTER A WITH ACUTE */ case 0x00E3: return((CHAR)(0x46 & 0xff)); /* LATIN SMALL LETTER A WITH TILDE */ case 0x00E5: return((CHAR)(0x47 & 0xff)); /* SMALL LETTER A WITH RING ABOVE */ case 0x00E7: return((CHAR)(0x48 & 0xff)); /* LATIN SMALL LETTER C WITH CEDILLA */ case 0x00F1: return((CHAR)(0x49 & 0xff)); /* LATIN SMALL LETTER N WITH TILDE */ case 0x00A2: return((CHAR)(0x4A & 0xff)); /* CENT SIGN */ case 0x002E: return((CHAR)(0x4B & 0xff)); /* FULL STOP */ case 0x003C: return((CHAR)(0x4C & 0xff)); /* LESS-THAN SIGN */ case 0x0028: return((CHAR)(0x4D & 0xff)); /* LEFT PARENTHESIS */ case 0x002B: return((CHAR)(0x4E & 0xff)); /* PLUS SIGN */ case 0x007C: return((CHAR)(0x4F & 0xff)); /* VERTICAL LINE */ case 0x0026: return((CHAR)(0x50 & 0xff)); /* AMPERSAND */ case 0x00E9: return((CHAR)(0x51 & 0xff)); /* SMALL LETTER E WITH ACUTE */ case 0x00EA: return((CHAR)(0x52 & 0xff)); /* SMALL LETTER E WITH CIRCUMFLEX */ case 0x00EB: return((CHAR)(0x53 & 0xff)); /* SMALL LETTER E WITH DIAERESIS */ case 0x00E8: return((CHAR)(0x54 & 0xff)); /* LATIN SMALL LETTER E WITH GRAVE */ case 0x00ED: return((CHAR)(0x55 & 0xff)); /* LATIN SMALL LETTER I WITH ACUTE */ case 0x00EE: return((CHAR)(0x56 & 0xff)); /* SMALL LETTER I WITH CIRCUMFLEX */ case 0x00EF: return((CHAR)(0x57 & 0xff)); /* SMALL LETTER I WITH DIAERESIS */ case 0x00EC: return((CHAR)(0x58 & 0xff)); /* LATIN SMALL LETTER I WITH GRAVE */ case 0x00DF: return((CHAR)(0x59 & 0xff)); /* SMALL LETTER SHARP S (GERMAN) */ case 0x0021: return((CHAR)(0x5A & 0xff)); /* EXCLAMATION MARK */ case 0x0024: return((CHAR)(0x5B & 0xff)); /* DOLLAR SIGN */ case 0x002A: return((CHAR)(0x5C & 0xff)); /* ASTERISK */ case 0x0029: return((CHAR)(0x5D & 0xff)); /* RIGHT PARENTHESIS */ case 0x003B: return((CHAR)(0x5E & 0xff)); /* SEMICOLON */ case 0x00AC: return((CHAR)(0x5F & 0xff)); /* NOT SIGN */ case 0x002D: return((CHAR)(0x60 & 0xff)); /* HYPHEN-MINUS */ case 0x002F: return((CHAR)(0x61 & 0xff)); /* SOLIDUS */ case 0x00C2: return((CHAR)(0x62 & 0xff)); /* CAPITAL LETTER A WITH CIRCUMFLEX */ case 0x00C4: return((CHAR)(0x63 & 0xff)); /* CAPITAL LETTER A WITH DIAERESIS */ case 0x00C0: return((CHAR)(0x64 & 0xff)); /* LATIN CAPITAL LETTER A WITH GRAVE */ case 0x00C1: return((CHAR)(0x65 & 0xff)); /* LATIN CAPITAL LETTER A WITH ACUTE */ case 0x00C3: return((CHAR)(0x66 & 0xff)); /* LATIN CAPITAL LETTER A WITH TILDE */ case 0x00C5: return((CHAR)(0x67 & 0xff)); /* CAPITAL LETTER A WITH RING ABOVE */ case 0x00C7: return((CHAR)(0x68 & 0xff)); /* CAPITAL LETTER C WITH CEDILLA */ case 0x00D1: return((CHAR)(0x69 & 0xff)); /* LATIN CAPITAL LETTER N WITH TILDE */ case 0x00A6: return((CHAR)(0x6A & 0xff)); /* BROKEN BAR */ case 0x002C: return((CHAR)(0x6B & 0xff)); /* COMMA */ case 0x0025: return((CHAR)(0x6C & 0xff)); /* PERCENT SIGN */ case 0x005F: return((CHAR)(0x6D & 0xff)); /* LOW LINE */ case 0x003E: return((CHAR)(0x6E & 0xff)); /* GREATER-THAN SIGN */ case 0x003F: return((CHAR)(0x6F & 0xff)); /* QUESTION MARK */ case 0x00F8: return((CHAR)(0x70 & 0xff)); /* LATIN SMALL LETTER O WITH STROKE */ case 0x00C9: return((CHAR)(0x71 & 0xff)); /* LATIN CAPITAL LETTER E WITH ACUTE */ case 0x00CA: /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX */ return((CHAR)(0x72 & 0xff)); case 0x00CB: /* LATIN CAPITAL LETTER E WITH DIAERESIS */ return((CHAR)(0x73 & 0xff)); case 0x00C8: return((CHAR)(0x74 & 0xff)); /* LATIN CAPITAL LETTER E WITH GRAVE */ case 0x00CD: return((CHAR)(0x75 & 0xff)); /* LATIN CAPITAL LETTER I WITH ACUTE */ case 0x00CE: /* LATIN CAPITAL LETTER I WITH CIRCUMFLEX */ return((CHAR)(0x76 & 0xff)); case 0x00CF: /* LATIN CAPITAL LETTER I WITH DIAERESIS */ return((CHAR)(0x77 & 0xff)); case 0x00CC: return((CHAR)(0x78 & 0xff)); /* LATIN CAPITAL LETTER I WITH GRAVE */ case 0x0060: return((CHAR)(0x79 & 0xff)); /* GRAVE ACCENT */ case 0x003A: return((CHAR)(0x7A & 0xff)); /* COLON */ case 0x0023: return((CHAR)(0x7B & 0xff)); /* NUMBER SIGN */ case 0x0040: return((CHAR)(0x7C & 0xff)); /* COMMERCIAL AT */ case 0x0027: return((CHAR)(0x7D & 0xff)); /* APOSTROPHE */ case 0x003D: return((CHAR)(0x7E & 0xff)); /* EQUALS SIGN */ case 0x0022: return((CHAR)(0x7F & 0xff)); /* QUOTATION MARK */ case 0x00D8: /* LATIN CAPITAL LETTER O WITH STROKE */ return((CHAR)(0x80 & 0xff)); case 0x0061: return((CHAR)(0x81 & 0xff)); /* LATIN SMALL LETTER A */ case 0x0062: return((CHAR)(0x82 & 0xff)); /* LATIN SMALL LETTER B */ case 0x0063: return((CHAR)(0x83 & 0xff)); /* LATIN SMALL LETTER C */ case 0x0064: return((CHAR)(0x84 & 0xff)); /* LATIN SMALL LETTER D */ case 0x0065: return((CHAR)(0x85 & 0xff)); /* LATIN SMALL LETTER E */ case 0x0066: return((CHAR)(0x86 & 0xff)); /* LATIN SMALL LETTER F */ case 0x0067: return((CHAR)(0x87 & 0xff)); /* LATIN SMALL LETTER G */ case 0x0068: return((CHAR)(0x88 & 0xff)); /* LATIN SMALL LETTER H */ case 0x0069: return((CHAR)(0x89 & 0xff)); /* LATIN SMALL LETTER I */ case 0x00AB: /* LEFT-POINTING DOUBLE ANGLE QUOTATION MARK */ return((CHAR)(0x8A & 0xff)); case 0x00BB: /* RIGHT-POINTING DOUBLE ANGLE QUOTATION MAR K*/ return((CHAR)(0x8B & 0xff)); case 0x00F0: /* LATIN SMALL LETTER ETH (ICELANDIC) */ return((CHAR)(0x8C & 0xff)); case 0x00FD: return((CHAR)(0x8D & 0xff)); /* LATIN SMALL LETTER Y WITH ACUTE */ case 0x00FE: /* LATIN SMALL LETTER THORN (ICELANDIC) */ return((CHAR)(0x8E & 0xff)); case 0x00B1: return((CHAR)(0x8F & 0xff)); /* PLUS-MINUS SIGN */ case 0x00B0: return((CHAR)(0x90 & 0xff)); /* DEGREE SIGN */ case 0x006A: return((CHAR)(0x91 & 0xff)); /* LATIN SMALL LETTER J */ case 0x006B: return((CHAR)(0x92 & 0xff)); /* LATIN SMALL LETTER K */ case 0x006C: return((CHAR)(0x93 & 0xff)); /* LATIN SMALL LETTER L */ case 0x006D: return((CHAR)(0x94 & 0xff)); /* LATIN SMALL LETTER M */ case 0x006E: return((CHAR)(0x95 & 0xff)); /* LATIN SMALL LETTER N */ case 0x006F: return((CHAR)(0x96 & 0xff)); /* LATIN SMALL LETTER O */ case 0x0070: return((CHAR)(0x97 & 0xff)); /* LATIN SMALL LETTER P */ case 0x0071: return((CHAR)(0x98 & 0xff)); /* LATIN SMALL LETTER Q */ case 0x0072: return((CHAR)(0x99 & 0xff)); /* LATIN SMALL LETTER R */ case 0x00AA: return((CHAR)(0x9A & 0xff)); /* FEMININE ORDINAL INDICATOR */ case 0x00BA: return((CHAR)(0x9B & 0xff)); /* MASCULINE ORDINAL INDICATOR */ case 0x00E6: return((CHAR)(0x9C & 0xff)); /* LATIN SMALL LIGATURE AE */ case 0x00B8: return((CHAR)(0x9D & 0xff)); /* CEDILLA */ case 0x00C6: return((CHAR)(0x9E & 0xff)); /* LATIN CAPITAL LIGATURE AE */ case 0x00A4: return((CHAR)(0x9F & 0xff)); /* CURRENCY SIGN */ case 0x00B5: return((CHAR)(0xA0 & 0xff)); /* MICRO SIGN */ case 0x007E: return((CHAR)(0xA1 & 0xff)); /* TILDE */ case 0x0073: return((CHAR)(0xA2 & 0xff)); /* LATIN SMALL LETTER S */ case 0x0074: return((CHAR)(0xA3 & 0xff)); /* LATIN SMALL LETTER T */ case 0x0075: return((CHAR)(0xA4 & 0xff)); /* LATIN SMALL LETTER U */ case 0x0076: return((CHAR)(0xA5 & 0xff)); /* LATIN SMALL LETTER V */ case 0x0077: return((CHAR)(0xA6 & 0xff)); /* LATIN SMALL LETTER W */ case 0x0078: return((CHAR)(0xA7 & 0xff)); /* LATIN SMALL LETTER X */ case 0x0079: return((CHAR)(0xA8 & 0xff)); /* LATIN SMALL LETTER Y */ case 0x007A: return((CHAR)(0xA9 & 0xff)); /* LATIN SMALL LETTER Z */ case 0x00A1: return((CHAR)(0xAA & 0xff)); /* INVERTED EXCLAMATION MARK */ case 0x00BF: return((CHAR)(0xAB & 0xff)); /* INVERTED QUESTION MARK */ case 0x00D0: /* LATIN CAPITAL LETTER ETH (ICELANDIC) */ return((CHAR)(0xAC & 0xff)); case 0x00DD: return((CHAR)(0xAD & 0xff)); /* LATIN CAPITAL LETTER Y WITH ACUTE */ case 0x00DE: /* LATIN CAPITAL LETTER THORN (ICELANDIC) */ return((CHAR)(0xAE & 0xff)); case 0x00AE: return((CHAR)(0xAF & 0xff)); /* REGISTERED SIGN */ case 0x005E: return((CHAR)(0xB0 & 0xff)); /* CIRCUMFLEX ACCENT */ case 0x00A3: return((CHAR)(0xB1 & 0xff)); /* POUND SIGN */ case 0x00A5: return((CHAR)(0xB2 & 0xff)); /* YEN SIGN */ case 0x00B7: return((CHAR)(0xB3 & 0xff)); /* MIDDLE DOT */ case 0x00A9: return((CHAR)(0xB4 & 0xff)); /* COPYRIGHT SIGN */ case 0x00A7: return((CHAR)(0xB5 & 0xff)); /* SECTION SIGN */ case 0x00B6: return((CHAR)(0xB6 & 0xff)); /* PILCROW SIGN */ case 0x00BC: return((CHAR)(0xB7 & 0xff)); /* VULGAR FRACTION ONE QUARTER */ case 0x00BD: return((CHAR)(0xB8 & 0xff)); /* VULGAR FRACTION ONE HALF */ case 0x00BE: return((CHAR)(0xB9 & 0xff)); /* VULGAR FRACTION THREE QUARTERS */ case 0x005B: return((CHAR)(0xBA & 0xff)); /* LEFT SQUARE BRACKET */ case 0x005D: return((CHAR)(0xBB & 0xff)); /* RIGHT SQUARE BRACKET */ case 0x00AF: return((CHAR)(0xBC & 0xff)); /* MACRON */ case 0x00A8: return((CHAR)(0xBD & 0xff)); /* DIAERESIS */ case 0x00B4: return((CHAR)(0xBE & 0xff)); /* ACUTE ACCENT */ case 0x00D7: return((CHAR)(0xBF & 0xff)); /* MULTIPLICATION SIGN */ case 0x007B: return((CHAR)(0xC0 & 0xff)); /* LEFT CURLY BRACKET */ case 0x0041: return((CHAR)(0xC1 & 0xff)); /* LATIN CAPITAL LETTER A */ case 0x0042: return((CHAR)(0xC2 & 0xff)); /* LATIN CAPITAL LETTER B */ case 0x0043: return((CHAR)(0xC3 & 0xff)); /* LATIN CAPITAL LETTER C */ case 0x0044: return((CHAR)(0xC4 & 0xff)); /* LATIN CAPITAL LETTER D */ case 0x0045: return((CHAR)(0xC5 & 0xff)); /* LATIN CAPITAL LETTER E */ case 0x0046: return((CHAR)(0xC6 & 0xff)); /* LATIN CAPITAL LETTER F */ case 0x0047: return((CHAR)(0xC7 & 0xff)); /* LATIN CAPITAL LETTER G */ case 0x0048: return((CHAR)(0xC8 & 0xff)); /* LATIN CAPITAL LETTER H */ case 0x0049: return((CHAR)(0xC9 & 0xff)); /* LATIN CAPITAL LETTER I */ case 0x00AD: return((CHAR)(0xCA & 0xff)); /* SOFT HYPHEN */ case 0x00F4: return((CHAR)(0xCB & 0xff)); /* SMALL LETTER O WITH CIRCUMFLEX */ case 0x00F6: return((CHAR)(0xCC & 0xff)); /* SMALL LETTER O WITH DIAERESIS */ case 0x00F2: return((CHAR)(0xCD & 0xff)); /* LATIN SMALL LETTER O WITH GRAVE */ case 0x00F3: return((CHAR)(0xCE & 0xff)); /* LATIN SMALL LETTER O WITH ACUTE */ case 0x00F5: return((CHAR)(0xCF & 0xff)); /* LATIN SMALL LETTER O WITH TILDE */ case 0x007D: return((CHAR)(0xD0 & 0xff)); /* RIGHT CURLY BRACKET */ case 0x004A: return((CHAR)(0xD1 & 0xff)); /* LATIN CAPITAL LETTER J */ case 0x004B: return((CHAR)(0xD2 & 0xff)); /* LATIN CAPITAL LETTER K */ case 0x004C: return((CHAR)(0xD3 & 0xff)); /* LATIN CAPITAL LETTER L */ case 0x004D: return((CHAR)(0xD4 & 0xff)); /* LATIN CAPITAL LETTER M */ case 0x004E: return((CHAR)(0xD5 & 0xff)); /* LATIN CAPITAL LETTER N */ case 0x004F: return((CHAR)(0xD6 & 0xff)); /* LATIN CAPITAL LETTER O */ case 0x0050: return((CHAR)(0xD7 & 0xff)); /* LATIN CAPITAL LETTER P */ case 0x0051: return((CHAR)(0xD8 & 0xff)); /* LATIN CAPITAL LETTER Q */ case 0x0052: return((CHAR)(0xD9 & 0xff)); /* LATIN CAPITAL LETTER R */ case 0x00B9: return((CHAR)(0xDA & 0xff)); /* SUPERSCRIPT ONE */ case 0x00FB: return((CHAR)(0xDB & 0xff)); /* SMALL LETTER U WITH CIRCUMFLEX */ case 0x00FC: return((CHAR)(0xDC & 0xff)); /* SMALL LETTER U WITH DIAERESIS */ case 0x00F9: return((CHAR)(0xDD & 0xff)); /* SMALL LETTER U WITH GRAVE */ case 0x00FA: return((CHAR)(0xDE & 0xff)); /* SMALL LETTER U WITH ACUTE */ case 0x00FF: return((CHAR)(0xDF & 0xff)); /* SMALL LETTER Y WITH DIAERESIS */ case 0x005C: return((CHAR)(0xE0 & 0xff)); /* REVERSE SOLIDUS */ case 0x00F7: return((CHAR)(0xE1 & 0xff)); /* DIVISION SIGN */ case 0x0053: return((CHAR)(0xE2 & 0xff)); /* LATIN CAPITAL LETTER S */ case 0x0054: return((CHAR)(0xE3 & 0xff)); /* LATIN CAPITAL LETTER T */ case 0x0055: return((CHAR)(0xE4 & 0xff)); /* LATIN CAPITAL LETTER U */ case 0x0056: return((CHAR)(0xE5 & 0xff)); /* LATIN CAPITAL LETTER V */ case 0x0057: return((CHAR)(0xE6 & 0xff)); /* LATIN CAPITAL LETTER W */ case 0x0058: return((CHAR)(0xE7 & 0xff)); /* LATIN CAPITAL LETTER X */ case 0x0059: return((CHAR)(0xE8 & 0xff)); /* LATIN CAPITAL LETTER Y */ case 0x005A: return((CHAR)(0xE9 & 0xff)); /* LATIN CAPITAL LETTER Z */ case 0x00B2: return((CHAR)(0xEA & 0xff)); /* SUPERSCRIPT TWO */ case 0x00D4: return((CHAR)(0xEB & 0xff)); /* CAPITAL LETTER O WITH CIRCUMFLEX */ case 0x00D6: return((CHAR)(0xEC & 0xff)); /* CAPITAL LETTER O WITH DIAERESIS */ case 0x00D2: return((CHAR)(0xED & 0xff)); /* CAPITAL LETTER O WITH GRAVE */ case 0x00D3: return((CHAR)(0xEE & 0xff)); /* CAPITAL LETTER O WITH ACUTE */ case 0x00D5: return((CHAR)(0xEF & 0xff)); /* CAPITAL LETTER O WITH TILDE */ case 0x0030: return((CHAR)(0xF0 & 0xff)); /* DIGIT ZERO */ case 0x0031: return((CHAR)(0xF1 & 0xff)); /* DIGIT ONE */ case 0x0032: return((CHAR)(0xF2 & 0xff)); /* DIGIT TWO */ case 0x0033: return((CHAR)(0xF3 & 0xff)); /* DIGIT THREE */ case 0x0034: return((CHAR)(0xF4 & 0xff)); /* DIGIT FOUR */ case 0x0035: return((CHAR)(0xF5 & 0xff)); /* DIGIT FIVE */ case 0x0036: return((CHAR)(0xF6 & 0xff)); /* DIGIT SIX */ case 0x0037: return((CHAR)(0xF7 & 0xff)); /* DIGIT SEVEN */ case 0x0038: return((CHAR)(0xF8 & 0xff)); /* DIGIT EIGHT */ case 0x0039: return((CHAR)(0xF9 & 0xff)); /* DIGIT NINE */ case 0x00B3: return((CHAR)(0xFA & 0xff)); /* SUPERSCRIPT THREE */ case 0x00DB: return((CHAR)(0xFB & 0xff)); /* CAPITAL LETTER U WITH CIRCUMFLEX */ case 0x00DC: return((CHAR)(0xFC & 0xff)); /* CAPITAL LETTER U WITH DIAERESIS */ case 0x00D9: return((CHAR)(0xFD & 0xff)); /* LATIN CAPITAL LETTER U WITH GRAVE */ case 0x00DA: return((CHAR)(0xFE & 0xff)); /* LATIN CAPITAL LETTER U WITH ACUTE */ case 0x009F: return((CHAR)(0xFF & 0xff)); /* CONTROL */ default: return(0x003f); } } int /* PC Code Page 855 */ #ifdef CK_ANSIC tx_cp855(USHORT c) #else tx_cp855(c) USHORT c; #endif /* CK_ANSIC */ { if (c < 0x80) /* Has C1 graphics */ return((CHAR)(c & 0xff)); switch (c) { case 0x00a4: return((CHAR)(207 & 0xff)); case 0x00a7: return((CHAR)(253 & 0xff)); case 0x00ab: return((CHAR)(174 & 0xff)); case 0x00bb: return((CHAR)(175 & 0xff)); case 0x0401: return((CHAR)(133 & 0xff)); case 0x0402: return((CHAR)(129 & 0xff)); case 0x0403: return((CHAR)(131 & 0xff)); case 0x0404: return((CHAR)(135 & 0xff)); case 0x0405: return((CHAR)(137 & 0xff)); case 0x0406: return((CHAR)(139 & 0xff)); case 0x0407: return((CHAR)(141 & 0xff)); case 0x0408: return((CHAR)(143 & 0xff)); case 0x0409: return((CHAR)(145 & 0xff)); case 0x040a: return((CHAR)(147 & 0xff)); case 0x040b: return((CHAR)(149 & 0xff)); case 0x040c: return((CHAR)(151 & 0xff)); case 0x040e: return((CHAR)(153 & 0xff)); case 0x040f: return((CHAR)(155 & 0xff)); case 0x0410: return((CHAR)(161 & 0xff)); case 0x0411: return((CHAR)(163 & 0xff)); case 0x0412: return((CHAR)(236 & 0xff)); case 0x0413: return((CHAR)(173 & 0xff)); case 0x0414: return((CHAR)(167 & 0xff)); case 0x0415: return((CHAR)(169 & 0xff)); case 0x0416: return((CHAR)(234 & 0xff)); case 0x0417: return((CHAR)(244 & 0xff)); case 0x0418: return((CHAR)(184 & 0xff)); case 0x0419: return((CHAR)(190 & 0xff)); case 0x041a: return((CHAR)(199 & 0xff)); case 0x041b: return((CHAR)(209 & 0xff)); case 0x041c: return((CHAR)(211 & 0xff)); case 0x041d: return((CHAR)(213 & 0xff)); case 0x041e: return((CHAR)(215 & 0xff)); case 0x041f: return((CHAR)(221 & 0xff)); case 0x0420: return((CHAR)(226 & 0xff)); case 0x0421: return((CHAR)(228 & 0xff)); case 0x0422: return((CHAR)(230 & 0xff)); case 0x0423: return((CHAR)(232 & 0xff)); case 0x0424: return((CHAR)(171 & 0xff)); case 0x0425: return((CHAR)(182 & 0xff)); case 0x0426: return((CHAR)(165 & 0xff)); case 0x0427: return((CHAR)(252 & 0xff)); case 0x0428: return((CHAR)(246 & 0xff)); case 0x0429: return((CHAR)(250 & 0xff)); case 0x042a: return((CHAR)(159 & 0xff)); case 0x042b: return((CHAR)(242 & 0xff)); case 0x042c: return((CHAR)(238 & 0xff)); case 0x042d: return((CHAR)(248 & 0xff)); case 0x042e: return((CHAR)(157 & 0xff)); case 0x042f: return((CHAR)(224 & 0xff)); case 0x0430: return((CHAR)(160 & 0xff)); case 0x0431: return((CHAR)(162 & 0xff)); case 0x0432: return((CHAR)(235 & 0xff)); case 0x0433: return((CHAR)(172 & 0xff)); case 0x0434: return((CHAR)(166 & 0xff)); case 0x0435: return((CHAR)(168 & 0xff)); case 0x0436: return((CHAR)(233 & 0xff)); case 0x0437: return((CHAR)(243 & 0xff)); case 0x0438: return((CHAR)(183 & 0xff)); case 0x0439: return((CHAR)(189 & 0xff)); case 0x043a: return((CHAR)(198 & 0xff)); case 0x043b: return((CHAR)(208 & 0xff)); case 0x043c: return((CHAR)(210 & 0xff)); case 0x043d: return((CHAR)(212 & 0xff)); case 0x043e: return((CHAR)(214 & 0xff)); case 0x043f: return((CHAR)(216 & 0xff)); case 0x0440: return((CHAR)(225 & 0xff)); case 0x0441: return((CHAR)(227 & 0xff)); case 0x0442: return((CHAR)(229 & 0xff)); case 0x0443: return((CHAR)(231 & 0xff)); case 0x0444: return((CHAR)(170 & 0xff)); case 0x0445: return((CHAR)(181 & 0xff)); case 0x0446: return((CHAR)(164 & 0xff)); case 0x0447: return((CHAR)(251 & 0xff)); case 0x0448: return((CHAR)(245 & 0xff)); case 0x0449: return((CHAR)(249 & 0xff)); case 0x044a: return((CHAR)(158 & 0xff)); case 0x044b: return((CHAR)(241 & 0xff)); case 0x044c: return((CHAR)(237 & 0xff)); case 0x044d: return((CHAR)(247 & 0xff)); case 0x044e: return((CHAR)(156 & 0xff)); case 0x044f: return((CHAR)(222 & 0xff)); case 0x0451: return((CHAR)(132 & 0xff)); case 0x0452: return((CHAR)(128 & 0xff)); case 0x0453: return((CHAR)(130 & 0xff)); case 0x0454: return((CHAR)(134 & 0xff)); case 0x0455: return((CHAR)(136 & 0xff)); case 0x0456: return((CHAR)(138 & 0xff)); case 0x0457: return((CHAR)(140 & 0xff)); case 0x0458: return((CHAR)(142 & 0xff)); case 0x0459: return((CHAR)(144 & 0xff)); case 0x045a: return((CHAR)(146 & 0xff)); case 0x045b: return((CHAR)(148 & 0xff)); case 0x045c: return((CHAR)(150 & 0xff)); case 0x045e: return((CHAR)(152 & 0xff)); case 0x045f: return((CHAR)(154 & 0xff)); case 0x2116: return((CHAR)(239 & 0xff)); case 0x2500: return((CHAR)(196 & 0xff)); case 0x2502: return((CHAR)(179 & 0xff)); case 0x250c: return((CHAR)(218 & 0xff)); case 0x2510: return((CHAR)(191 & 0xff)); case 0x2514: return((CHAR)(192 & 0xff)); case 0x2518: return((CHAR)(217 & 0xff)); case 0x251c: return((CHAR)(195 & 0xff)); case 0x2524: return((CHAR)(180 & 0xff)); case 0x252c: return((CHAR)(194 & 0xff)); case 0x2534: return((CHAR)(193 & 0xff)); case 0x253c: return((CHAR)(197 & 0xff)); case 0x2550: return((CHAR)(205 & 0xff)); case 0x2551: return((CHAR)(186 & 0xff)); case 0x2554: return((CHAR)(201 & 0xff)); case 0x2557: return((CHAR)(187 & 0xff)); case 0x255a: return((CHAR)(200 & 0xff)); case 0x255d: return((CHAR)(188 & 0xff)); case 0x2560: return((CHAR)(204 & 0xff)); case 0x2563: return((CHAR)(185 & 0xff)); case 0x2566: return((CHAR)(203 & 0xff)); case 0x2569: return((CHAR)(202 & 0xff)); case 0x256c: return((CHAR)(206 & 0xff)); case 0x2580: return((CHAR)(223 & 0xff)); case 0x2584: return((CHAR)(220 & 0xff)); case 0x2588: return((CHAR)(219 & 0xff)); case 0x2591: return((CHAR)(176 & 0xff)); case 0x2592: return((CHAR)(177 & 0xff)); case 0x2593: return((CHAR)(178 & 0xff)); case 0x25a0: return((CHAR)(254 & 0xff)); default: return(tx_cpsub(c)); /* For box characters etc */ } } int /* PC Code Page 856 */ #ifdef CK_ANSIC tx_cp856(USHORT c) #else tx_cp856(c) USHORT c; #endif /* CK_ANSIC */ { if (c < 0x80) /* Has C1 graphics */ return((CHAR)(c & 0xff)); switch (c) { case 0x00a0: return((CHAR)(0xff & 0xff)); case 0x00a7: return((CHAR)(0xd6 & 0xff)); case 0x00b0: return((CHAR)(0xf8 & 0xff)); case 0x00b1: return((CHAR)(0xf1 & 0xff)); case 0x00b2: return((CHAR)(0xfd & 0xff)); case 0x00b5: return((CHAR)(0xe6 & 0xff)); case 0x00b7: return((CHAR)(0xfa & 0xff)); case 0x00df: return((CHAR)(0xe1 & 0xff)); case 0x00f7: return((CHAR)(0xf6 & 0xff)); case 0x0393: return((CHAR)(0xe2 & 0xff)); case 0x0398: return((CHAR)(0xe9 & 0xff)); case 0x03a3: return((CHAR)(0xe4 & 0xff)); case 0x03a6: return((CHAR)(0xe8 & 0xff)); case 0x03a9: return((CHAR)(0xea & 0xff)); case 0x03b1: return((CHAR)(0xe0 & 0xff)); case 0x03b4: return((CHAR)(0xeb & 0xff)); case 0x03b5: return((CHAR)(0xee & 0xff)); case 0x03c0: return((CHAR)(0xe3 & 0xff)); case 0x03c3: return((CHAR)(0xe5 & 0xff)); case 0x03c4: return((CHAR)(0xe7 & 0xff)); case 0x03c6: return((CHAR)(0xed & 0xff)); case 0x0410: return((CHAR)(0x80 & 0xff)); case 0x0411: return((CHAR)(0x81 & 0xff)); case 0x0412: return((CHAR)(0x82 & 0xff)); case 0x0413: return((CHAR)(0x83 & 0xff)); case 0x0414: return((CHAR)(0x84 & 0xff)); case 0x0415: return((CHAR)(0x85 & 0xff)); case 0x0416: return((CHAR)(0x86 & 0xff)); case 0x0417: return((CHAR)(0x87 & 0xff)); case 0x0418: return((CHAR)(0x88 & 0xff)); case 0x0419: return((CHAR)(0x89 & 0xff)); case 0x041a: return((CHAR)(0x8a & 0xff)); case 0x041b: return((CHAR)(0x8b & 0xff)); case 0x041c: return((CHAR)(0x8c & 0xff)); case 0x041d: return((CHAR)(0x8d & 0xff)); case 0x041e: return((CHAR)(0x8e & 0xff)); case 0x041f: return((CHAR)(0x8f & 0xff)); case 0x0420: return((CHAR)(0x90 & 0xff)); case 0x0421: return((CHAR)(0x91 & 0xff)); case 0x0422: return((CHAR)(0x92 & 0xff)); case 0x0423: return((CHAR)(0x93 & 0xff)); case 0x0424: return((CHAR)(0x94 & 0xff)); case 0x0425: return((CHAR)(0x95 & 0xff)); case 0x0426: return((CHAR)(0x96 & 0xff)); case 0x0427: return((CHAR)(0x97 & 0xff)); case 0x0428: return((CHAR)(0x98 & 0xff)); case 0x0429: return((CHAR)(0x99 & 0xff)); case 0x042a: return((CHAR)(0x9a & 0xff)); case 0x042b: return((CHAR)(0x9b & 0xff)); case 0x042c: return((CHAR)(0x9c & 0xff)); case 0x042d: return((CHAR)(0x9d & 0xff)); case 0x042e: return((CHAR)(0x9e & 0xff)); case 0x042f: return((CHAR)(0x9f & 0xff)); case 0x0430: return((CHAR)(0xa0 & 0xff)); case 0x0431: return((CHAR)(0xa1 & 0xff)); case 0x0432: return((CHAR)(0xa2 & 0xff)); case 0x0433: return((CHAR)(0xa3 & 0xff)); case 0x0434: return((CHAR)(0xa4 & 0xff)); case 0x0435: return((CHAR)(0xa5 & 0xff)); case 0x0436: return((CHAR)(0xa6 & 0xff)); case 0x0437: return((CHAR)(0xa7 & 0xff)); case 0x0438: return((CHAR)(0xa8 & 0xff)); case 0x0439: return((CHAR)(0xa9 & 0xff)); case 0x043a: return((CHAR)(0xaa & 0xff)); case 0x043b: return((CHAR)(0xab & 0xff)); case 0x043c: return((CHAR)(0xac & 0xff)); case 0x043d: return((CHAR)(0xad & 0xff)); case 0x043e: return((CHAR)(0xae & 0xff)); case 0x043f: return((CHAR)(0xaf & 0xff)); case 0x0440: return((CHAR)(0xb0 & 0xff)); case 0x0441: return((CHAR)(0xb1 & 0xff)); case 0x0442: return((CHAR)(0xb2 & 0xff)); case 0x0443: return((CHAR)(0xb3 & 0xff)); case 0x0444: return((CHAR)(0xb4 & 0xff)); case 0x0445: return((CHAR)(0xb5 & 0xff)); case 0x0446: return((CHAR)(0xb6 & 0xff)); case 0x0447: return((CHAR)(0xb7 & 0xff)); case 0x0448: return((CHAR)(0xb8 & 0xff)); case 0x0449: return((CHAR)(0xb9 & 0xff)); case 0x044a: return((CHAR)(0xba & 0xff)); case 0x044b: return((CHAR)(0xbb & 0xff)); case 0x044c: return((CHAR)(0xbc & 0xff)); case 0x044d: return((CHAR)(0xbd & 0xff)); case 0x044e: return((CHAR)(0xbe & 0xff)); case 0x044f: return((CHAR)(0xbf & 0xff)); case 0x207f: return((CHAR)(0xfc & 0xff)); case 0x2116: return((CHAR)(0xd5 & 0xff)); case 0x2219: return((CHAR)(0xf9 & 0xff)); case 0x221a: return((CHAR)(0xfb & 0xff)); case 0x221e: return((CHAR)(0xec & 0xff)); case 0x2229: return((CHAR)(0xef & 0xff)); case 0x2248: return((CHAR)(0xf7 & 0xff)); case 0x2261: return((CHAR)(0xf0 & 0xff)); case 0x2264: return((CHAR)(0xf3 & 0xff)); case 0x2265: return((CHAR)(0xf2 & 0xff)); case 0x2320: return((CHAR)(0xf4 & 0xff)); case 0x2321: return((CHAR)(0xf5 & 0xff)); case 0x2500: return((CHAR)(0xc4 & 0xff)); case 0x2502: return((CHAR)(0xd3 & 0xff)); case 0x250c: return((CHAR)(0xda & 0xff)); case 0x2510: return((CHAR)(0xcf & 0xff)); case 0x2514: return((CHAR)(0xc0 & 0xff)); case 0x2518: return((CHAR)(0xd9 & 0xff)); case 0x251c: return((CHAR)(0xc3 & 0xff)); case 0x2524: return((CHAR)(0xd4 & 0xff)); case 0x252c: return((CHAR)(0xc2 & 0xff)); case 0x2534: return((CHAR)(0xc1 & 0xff)); case 0x253c: return((CHAR)(0xc5 & 0xff)); case 0x2550: return((CHAR)(0xcd & 0xff)); case 0x2551: return((CHAR)(0xc7 & 0xff)); case 0x2554: return((CHAR)(0xc9 & 0xff)); case 0x2557: return((CHAR)(0xd7 & 0xff)); case 0x255a: return((CHAR)(0xc8 & 0xff)); case 0x255d: return((CHAR)(0xd8 & 0xff)); case 0x2560: return((CHAR)(0xcc & 0xff)); case 0x2563: return((CHAR)(0xc6 & 0xff)); case 0x2566: return((CHAR)(0xcb & 0xff)); case 0x2569: return((CHAR)(0xca & 0xff)); case 0x256c: return((CHAR)(0xce & 0xff)); case 0x2580: return((CHAR)(0xdf & 0xff)); case 0x2584: return((CHAR)(0xdc & 0xff)); case 0x2588: return((CHAR)(0xdb & 0xff)); case 0x258c: return((CHAR)(0xdd & 0xff)); case 0x2590: return((CHAR)(0xde & 0xff)); case 0x2591: return((CHAR)(0xd0 & 0xff)); case 0x2592: return((CHAR)(0xd1 & 0xff)); case 0x2593: return((CHAR)(0xd2 & 0xff)); case 0x25a0: return((CHAR)(0xfe & 0xff)); default: return(tx_cpsub(c)); /* For box characters etc */ } } int /* PC Code Page 857 */ #ifdef CK_ANSIC tx_cp857(USHORT c) #else tx_cp857(c) USHORT c; #endif /* CK_ANSIC */ { if (c < 0x80) /* Has C1 graphics */ return((CHAR)(c & 0xff)); switch (c) { case 0x00c7: return((CHAR)(128 & 0xff)); case 0x00fc: return((CHAR)(129 & 0xff)); case 0x00e9: return((CHAR)(130 & 0xff)); case 0x00e2: return((CHAR)(131 & 0xff)); case 0x00e4: return((CHAR)(132 & 0xff)); case 0x00e0: return((CHAR)(133 & 0xff)); case 0x00e5: return((CHAR)(134 & 0xff)); case 0x00e7: return((CHAR)(135 & 0xff)); case 0x00ea: return((CHAR)(136 & 0xff)); case 0x00eb: return((CHAR)(137 & 0xff)); case 0x00e8: return((CHAR)(138 & 0xff)); case 0x00ef: return((CHAR)(139 & 0xff)); case 0x00ee: return((CHAR)(140 & 0xff)); case 0x0131: return((CHAR)(141 & 0xff)); case 0x00c4: return((CHAR)(142 & 0xff)); case 0x00c5: return((CHAR)(143 & 0xff)); case 0x00c9: return((CHAR)(144 & 0xff)); case 0x00e6: return((CHAR)(145 & 0xff)); case 0x00c6: return((CHAR)(146 & 0xff)); case 0x00f4: return((CHAR)(147 & 0xff)); case 0x00f6: return((CHAR)(148 & 0xff)); case 0x00f2: return((CHAR)(149 & 0xff)); case 0x00fb: return((CHAR)(150 & 0xff)); case 0x00f9: return((CHAR)(151 & 0xff)); case 0x0130: return((CHAR)(152 & 0xff)); case 0x00d6: return((CHAR)(153 & 0xff)); case 0x00dc: return((CHAR)(154 & 0xff)); case 0x00f8: return((CHAR)(155 & 0xff)); case 0x00a3: return((CHAR)(156 & 0xff)); case 0x00d8: return((CHAR)(157 & 0xff)); case 0x015e: return((CHAR)(158 & 0xff)); case 0x015f: return((CHAR)(159 & 0xff)); case 0x00e1: return((CHAR)(160 & 0xff)); case 0x00ed: return((CHAR)(161 & 0xff)); case 0x00f3: return((CHAR)(162 & 0xff)); case 0x00fa: return((CHAR)(163 & 0xff)); case 0x00f1: return((CHAR)(164 & 0xff)); case 0x00d1: return((CHAR)(165 & 0xff)); case 0x011e: return((CHAR)(166 & 0xff)); case 0x011f: return((CHAR)(167 & 0xff)); case 0x00bf: return((CHAR)(168 & 0xff)); case 0x00ae: return((CHAR)(169 & 0xff)); case 0x00ac: return((CHAR)(170 & 0xff)); case 0x00bd: return((CHAR)(171 & 0xff)); case 0x00bc: return((CHAR)(172 & 0xff)); case 0x00a1: return((CHAR)(173 & 0xff)); case 0x00ab: return((CHAR)(174 & 0xff)); case 0x00bb: return((CHAR)(175 & 0xff)); case 0x20ac: return((CHAR)(213 & 0xff)); /* Euro */ case 0x2591: return((CHAR)(176 & 0xff)); case 0x2592: return((CHAR)(177 & 0xff)); case 0x2593: return((CHAR)(178 & 0xff)); case 0x2502: return((CHAR)(179 & 0xff)); case 0x2524: return((CHAR)(180 & 0xff)); case 0x00c1: return((CHAR)(181 & 0xff)); case 0x00c2: return((CHAR)(182 & 0xff)); case 0x00c0: return((CHAR)(183 & 0xff)); case 0x00a9: return((CHAR)(184 & 0xff)); case 0x2563: return((CHAR)(185 & 0xff)); case 0x2551: return((CHAR)(186 & 0xff)); case 0x2557: return((CHAR)(187 & 0xff)); case 0x255d: return((CHAR)(188 & 0xff)); case 0x00a2: return((CHAR)(189 & 0xff)); case 0x00a5: return((CHAR)(190 & 0xff)); case 0x2510: return((CHAR)(191 & 0xff)); case 0x2514: return((CHAR)(192 & 0xff)); case 0x2534: return((CHAR)(193 & 0xff)); case 0x252c: return((CHAR)(194 & 0xff)); case 0x251c: return((CHAR)(195 & 0xff)); case 0x2500: return((CHAR)(196 & 0xff)); case 0x253c: return((CHAR)(197 & 0xff)); case 0x00e3: return((CHAR)(198 & 0xff)); case 0x00c3: return((CHAR)(199 & 0xff)); case 0x255a: return((CHAR)(200 & 0xff)); case 0x2554: return((CHAR)(201 & 0xff)); case 0x2569: return((CHAR)(202 & 0xff)); case 0x2566: return((CHAR)(203 & 0xff)); case 0x2560: return((CHAR)(204 & 0xff)); case 0x2550: return((CHAR)(205 & 0xff)); case 0x256c: return((CHAR)(206 & 0xff)); case 0x00a4: return((CHAR)(207 & 0xff)); case 0x00ba: return((CHAR)(208 & 0xff)); case 0x00aa: return((CHAR)(209 & 0xff)); case 0x00ca: return((CHAR)(210 & 0xff)); case 0x00cb: return((CHAR)(211 & 0xff)); case 0x00c8: return((CHAR)(212 & 0xff)); case 0x00cd: return((CHAR)(214 & 0xff)); case 0x00ce: return((CHAR)(215 & 0xff)); case 0x00cf: return((CHAR)(216 & 0xff)); case 0x2518: return((CHAR)(217 & 0xff)); case 0x250c: return((CHAR)(218 & 0xff)); case 0x2588: return((CHAR)(219 & 0xff)); case 0x2584: return((CHAR)(220 & 0xff)); case 0x00a6: return((CHAR)(221 & 0xff)); case 0x00cc: return((CHAR)(222 & 0xff)); case 0x2580: return((CHAR)(223 & 0xff)); case 0x00d3: return((CHAR)(224 & 0xff)); case 0x00df: return((CHAR)(225 & 0xff)); case 0x00d4: return((CHAR)(226 & 0xff)); case 0x00d2: return((CHAR)(227 & 0xff)); case 0x00f5: return((CHAR)(228 & 0xff)); case 0x00d5: return((CHAR)(229 & 0xff)); case 0x00b5: return((CHAR)(230 & 0xff)); case 0x00d7: return((CHAR)(232 & 0xff)); case 0x00da: return((CHAR)(233 & 0xff)); case 0x00db: return((CHAR)(234 & 0xff)); case 0x00d9: return((CHAR)(235 & 0xff)); case 0x00ec: return((CHAR)(236 & 0xff)); case 0x00ff: return((CHAR)(237 & 0xff)); case 0x00af: return((CHAR)(238 & 0xff)); case 0x00b4: return((CHAR)(239 & 0xff)); case 0x00ad: return((CHAR)(240 & 0xff)); case 0x00b1: return((CHAR)(241 & 0xff)); case 0x00be: return((CHAR)(243 & 0xff)); case 0x00b6: return((CHAR)(244 & 0xff)); case 0x00a7: return((CHAR)(245 & 0xff)); case 0x00f7: return((CHAR)(246 & 0xff)); case 0x00b8: return((CHAR)(247 & 0xff)); case 0x00b0: return((CHAR)(248 & 0xff)); case 0x00a8: return((CHAR)(249 & 0xff)); case 0x00b7: return((CHAR)(250 & 0xff)); case 0x00b9: return((CHAR)(251 & 0xff)); case 0x00b3: return((CHAR)(252 & 0xff)); case 0x00b2: return((CHAR)(253 & 0xff)); case 0x25a0: return((CHAR)(254 & 0xff)); case 0x00a0: return((CHAR)(255 & 0xff)); default: return(tx_cpsub(c)); /* For box characters etc */ } } int /* PC Code Page 862 */ #ifdef CK_ANSIC tx_cp862(USHORT c) #else tx_cp862(c) USHORT c; #endif /* CK_ANSIC */ { if (c < 0x80) /* Has C1 graphics */ return((CHAR)(c & 0xff)); switch (c) { case 0x05d0: return((CHAR)(128 & 0xff)); case 0x05d1: return((CHAR)(129 & 0xff)); case 0x05d2: return((CHAR)(130 & 0xff)); case 0x05d3: return((CHAR)(131 & 0xff)); case 0x05d4: return((CHAR)(132 & 0xff)); case 0x05d5: return((CHAR)(133 & 0xff)); case 0x05d6: return((CHAR)(134 & 0xff)); case 0x05d7: return((CHAR)(135 & 0xff)); case 0x05d8: return((CHAR)(136 & 0xff)); case 0x05d9: return((CHAR)(137 & 0xff)); case 0x05da: return((CHAR)(138 & 0xff)); case 0x05db: return((CHAR)(139 & 0xff)); case 0x05dc: return((CHAR)(140 & 0xff)); case 0x05dd: return((CHAR)(141 & 0xff)); case 0x05de: return((CHAR)(142 & 0xff)); case 0x05df: return((CHAR)(143 & 0xff)); case 0x05e0: return((CHAR)(144 & 0xff)); case 0x05e1: return((CHAR)(145 & 0xff)); case 0x05e2: return((CHAR)(146 & 0xff)); case 0x05e3: return((CHAR)(147 & 0xff)); case 0x05e4: return((CHAR)(148 & 0xff)); case 0x05e5: return((CHAR)(149 & 0xff)); case 0x05e6: return((CHAR)(150 & 0xff)); case 0x05e7: return((CHAR)(151 & 0xff)); case 0x05e8: return((CHAR)(152 & 0xff)); case 0x05e9: return((CHAR)(153 & 0xff)); case 0x05ea: return((CHAR)(154 & 0xff)); case 0x00a2: return((CHAR)(155 & 0xff)); case 0x00a3: return((CHAR)(156 & 0xff)); case 0x00a5: return((CHAR)(157 & 0xff)); case 0x20a7: return((CHAR)(158 & 0xff)); case 0x0192: return((CHAR)(159 & 0xff)); case 0x00e1: return((CHAR)(160 & 0xff)); case 0x00ed: return((CHAR)(161 & 0xff)); case 0x00f3: return((CHAR)(162 & 0xff)); case 0x00fa: return((CHAR)(163 & 0xff)); case 0x00f1: return((CHAR)(164 & 0xff)); case 0x00d1: return((CHAR)(165 & 0xff)); case 0x00aa: return((CHAR)(166 & 0xff)); case 0x00ba: return((CHAR)(167 & 0xff)); case 0x00bf: return((CHAR)(168 & 0xff)); case 0x2310: return((CHAR)(169 & 0xff)); case 0x00ac: return((CHAR)(170 & 0xff)); case 0x00bd: return((CHAR)(171 & 0xff)); case 0x00bc: return((CHAR)(172 & 0xff)); case 0x00a1: return((CHAR)(173 & 0xff)); case 0x00ab: return((CHAR)(174 & 0xff)); case 0x00bb: return((CHAR)(175 & 0xff)); case 0x2591: return((CHAR)(176 & 0xff)); case 0x2592: return((CHAR)(177 & 0xff)); case 0x2593: return((CHAR)(178 & 0xff)); case 0x2502: return((CHAR)(179 & 0xff)); case 0x2524: return((CHAR)(180 & 0xff)); case 0x2561: return((CHAR)(181 & 0xff)); case 0x2562: return((CHAR)(182 & 0xff)); case 0x2556: return((CHAR)(183 & 0xff)); case 0x2555: return((CHAR)(184 & 0xff)); case 0x2563: return((CHAR)(185 & 0xff)); case 0x2551: return((CHAR)(186 & 0xff)); case 0x2557: return((CHAR)(187 & 0xff)); case 0x255d: return((CHAR)(188 & 0xff)); case 0x255c: return((CHAR)(189 & 0xff)); case 0x255b: return((CHAR)(190 & 0xff)); case 0x2510: return((CHAR)(191 & 0xff)); case 0x2514: return((CHAR)(192 & 0xff)); case 0x2534: return((CHAR)(193 & 0xff)); case 0x252c: return((CHAR)(194 & 0xff)); case 0x251c: return((CHAR)(195 & 0xff)); case 0x2500: return((CHAR)(196 & 0xff)); case 0x253c: return((CHAR)(197 & 0xff)); case 0x255e: return((CHAR)(198 & 0xff)); case 0x255f: return((CHAR)(199 & 0xff)); case 0x255a: return((CHAR)(200 & 0xff)); case 0x2554: return((CHAR)(201 & 0xff)); case 0x2569: return((CHAR)(202 & 0xff)); case 0x2566: return((CHAR)(203 & 0xff)); case 0x2560: return((CHAR)(204 & 0xff)); case 0x2550: return((CHAR)(205 & 0xff)); case 0x256c: return((CHAR)(206 & 0xff)); case 0x2567: return((CHAR)(207 & 0xff)); case 0x2568: return((CHAR)(208 & 0xff)); case 0x2564: return((CHAR)(209 & 0xff)); case 0x2565: return((CHAR)(210 & 0xff)); case 0x2559: return((CHAR)(211 & 0xff)); case 0x2558: return((CHAR)(212 & 0xff)); case 0x2552: return((CHAR)(213 & 0xff)); case 0x2553: return((CHAR)(214 & 0xff)); case 0x256b: return((CHAR)(215 & 0xff)); case 0x256a: return((CHAR)(216 & 0xff)); case 0x2518: return((CHAR)(217 & 0xff)); case 0x250c: return((CHAR)(218 & 0xff)); case 0x2588: return((CHAR)(219 & 0xff)); case 0x2584: return((CHAR)(220 & 0xff)); case 0x258c: return((CHAR)(221 & 0xff)); case 0x2590: return((CHAR)(222 & 0xff)); case 0x2580: return((CHAR)(223 & 0xff)); case 0x03b1: return((CHAR)(224 & 0xff)); case 0x00df: return((CHAR)(225 & 0xff)); case 0x0393: return((CHAR)(226 & 0xff)); case 0x03c0: return((CHAR)(227 & 0xff)); case 0x03a3: return((CHAR)(228 & 0xff)); case 0x03c3: return((CHAR)(229 & 0xff)); case 0x00b5: return((CHAR)(230 & 0xff)); case 0x03c4: return((CHAR)(231 & 0xff)); case 0x03a6: return((CHAR)(232 & 0xff)); case 0x0398: return((CHAR)(233 & 0xff)); case 0x03a9: return((CHAR)(234 & 0xff)); case 0x03b4: return((CHAR)(235 & 0xff)); case 0x221e: return((CHAR)(236 & 0xff)); case 0x03c6: return((CHAR)(237 & 0xff)); case 0x03b5: return((CHAR)(238 & 0xff)); case 0x2229: return((CHAR)(239 & 0xff)); case 0x2261: return((CHAR)(240 & 0xff)); case 0x00b1: return((CHAR)(241 & 0xff)); case 0x2265: return((CHAR)(242 & 0xff)); case 0x2264: return((CHAR)(243 & 0xff)); case 0x2320: return((CHAR)(244 & 0xff)); case 0x2321: return((CHAR)(245 & 0xff)); case 0x00f7: return((CHAR)(246 & 0xff)); case 0x2248: return((CHAR)(247 & 0xff)); case 0x00b0: return((CHAR)(248 & 0xff)); case 0x2219: return((CHAR)(249 & 0xff)); case 0x00b7: return((CHAR)(250 & 0xff)); case 0x221a: return((CHAR)(251 & 0xff)); case 0x207f: return((CHAR)(252 & 0xff)); case 0x00b2: return((CHAR)(253 & 0xff)); case 0x25a0: return((CHAR)(254 & 0xff)); case 0x00a0: return((CHAR)(255 & 0xff)); default: return(tx_cpsub(c)); /* For box characters etc */ } } int /* PC Code Page 864 */ #ifdef CK_ANSIC tx_cp864(USHORT c) #else tx_cp864(c) USHORT c; #endif /* CK_ANSIC */ { if (c < 0x80) /* Has C1 graphics */ return((CHAR)(c & 0xff)); switch (c) { case 0x00b0: return((CHAR)0x80 & 0xff); case 0x00b7: return((CHAR)0x81 & 0xff); case 0x2219: return((CHAR)0x82 & 0xff); case 0x221a: return((CHAR)0x83 & 0xff); case 0x2592: return((CHAR)0x84 & 0xff); case 0x2500: return((CHAR)0x85 & 0xff); case 0x2502: return((CHAR)0x86 & 0xff); case 0x253c: return((CHAR)0x87 & 0xff); case 0x2524: return((CHAR)0x88 & 0xff); case 0x252c: return((CHAR)0x89 & 0xff); case 0x251c: return((CHAR)0x8a & 0xff); case 0x2534: return((CHAR)0x8b & 0xff); case 0x2510: return((CHAR)0x8c & 0xff); case 0x250c: return((CHAR)0x8d & 0xff); case 0x2514: return((CHAR)0x8e & 0xff); case 0x2518: return((CHAR)0x8f & 0xff); case 0x03b2: return((CHAR)0x90 & 0xff); case 0x221e: return((CHAR)0x91 & 0xff); case 0x03c6: return((CHAR)0x92 & 0xff); case 0x00b1: return((CHAR)0x93 & 0xff); case 0x00bd: return((CHAR)0x94 & 0xff); case 0x00bc: return((CHAR)0x95 & 0xff); case 0x2248: return((CHAR)0x96 & 0xff); case 0x00ab: return((CHAR)0x97 & 0xff); case 0x00bb: return((CHAR)0x98 & 0xff); case 0xfef7: return((CHAR)0x99 & 0xff); case 0xfef8: return((CHAR)0x9a & 0xff); case 0xfefb: return((CHAR)0x9d & 0xff); case 0xfefc: return((CHAR)0x9e & 0xff); case 0x00a0: return((CHAR)0xa0 & 0xff); case 0x00ad: return((CHAR)0xa1 & 0xff); case 0xfe82: return((CHAR)0xa2 & 0xff); case 0x00a3: return((CHAR)0xa3 & 0xff); case 0x00a4: return((CHAR)0xa4 & 0xff); case 0xfe84: return((CHAR)0xa5 & 0xff); case 0xfe8e: return((CHAR)0xa8 & 0xff); case 0xfe8f: return((CHAR)0xa9 & 0xff); case 0xfe95: return((CHAR)0xaa & 0xff); case 0xfe99: return((CHAR)0xab & 0xff); case 0x060c: return((CHAR)0xac & 0xff); case 0xfe9d: return((CHAR)0xad & 0xff); case 0xfea1: return((CHAR)0xae & 0xff); case 0xfea5: return((CHAR)0xaf & 0xff); case 0x0660: return((CHAR)0xb0 & 0xff); case 0x0661: return((CHAR)0xb1 & 0xff); case 0x0662: return((CHAR)0xb2 & 0xff); case 0x0663: return((CHAR)0xb3 & 0xff); case 0x0664: return((CHAR)0xb4 & 0xff); case 0x0665: return((CHAR)0xb5 & 0xff); case 0x0666: return((CHAR)0xb6 & 0xff); case 0x0667: return((CHAR)0xb7 & 0xff); case 0x0668: return((CHAR)0xb8 & 0xff); case 0x0669: return((CHAR)0xb9 & 0xff); case 0xfed1: return((CHAR)0xba & 0xff); case 0x061b: return((CHAR)0xbb & 0xff); case 0xfeb1: return((CHAR)0xbc & 0xff); case 0xfeb5: return((CHAR)0xbd & 0xff); case 0xfeb9: return((CHAR)0xbe & 0xff); case 0x061f: return((CHAR)0xbf & 0xff); case 0x00a2: return((CHAR)0xc0 & 0xff); case 0xfe80: return((CHAR)0xc1 & 0xff); case 0xfe81: return((CHAR)0xc2 & 0xff); case 0xfe83: return((CHAR)0xc3 & 0xff); case 0xfe85: return((CHAR)0xc4 & 0xff); case 0xfeca: return((CHAR)0xc5 & 0xff); case 0xfe8b: return((CHAR)0xc6 & 0xff); case 0xfe8d: return((CHAR)0xc7 & 0xff); case 0xfe91: return((CHAR)0xc8 & 0xff); case 0xfe93: return((CHAR)0xc9 & 0xff); case 0xfe97: return((CHAR)0xca & 0xff); case 0xfe9b: return((CHAR)0xcb & 0xff); case 0xfe9f: return((CHAR)0xcc & 0xff); case 0xfea3: return((CHAR)0xcd & 0xff); case 0xfea7: return((CHAR)0xce & 0xff); case 0xfea9: return((CHAR)0xcf & 0xff); case 0xfeab: return((CHAR)0xd0 & 0xff); case 0xfead: return((CHAR)0xd1 & 0xff); case 0xfeaf: return((CHAR)0xd2 & 0xff); case 0xfeb3: return((CHAR)0xd3 & 0xff); case 0xfeb7: return((CHAR)0xd4 & 0xff); case 0xfebb: return((CHAR)0xd5 & 0xff); case 0xfebf: return((CHAR)0xd6 & 0xff); case 0xfec1: return((CHAR)0xd7 & 0xff); case 0xfec5: return((CHAR)0xd8 & 0xff); case 0xfecb: return((CHAR)0xd9 & 0xff); case 0xfecf: return((CHAR)0xda & 0xff); case 0x00a6: return((CHAR)0xdb & 0xff); case 0x00ac: return((CHAR)0xdc & 0xff); case 0x00f7: return((CHAR)0xdd & 0xff); case 0x00d7: return((CHAR)0xde & 0xff); case 0xfec9: return((CHAR)0xdf & 0xff); case 0x0640: return((CHAR)0xe0 & 0xff); case 0xfed3: return((CHAR)0xe1 & 0xff); case 0xfed7: return((CHAR)0xe2 & 0xff); case 0xfedb: return((CHAR)0xe3 & 0xff); case 0xfedf: return((CHAR)0xe4 & 0xff); case 0xfee3: return((CHAR)0xe5 & 0xff); case 0xfee7: return((CHAR)0xe6 & 0xff); case 0xfeeb: return((CHAR)0xe7 & 0xff); case 0xfeed: return((CHAR)0xe8 & 0xff); case 0xfeef: return((CHAR)0xe9 & 0xff); case 0xfef3: return((CHAR)0xea & 0xff); case 0xfebd: return((CHAR)0xeb & 0xff); case 0xfecc: return((CHAR)0xec & 0xff); case 0xfece: return((CHAR)0xed & 0xff); case 0xfecd: return((CHAR)0xee & 0xff); case 0xfee1: return((CHAR)0xef & 0xff); case 0xfe7d: return((CHAR)0xf0 & 0xff); case 0x0651: return((CHAR)0xf1 & 0xff); case 0xfee5: return((CHAR)0xf2 & 0xff); case 0xfee9: return((CHAR)0xf3 & 0xff); case 0xfeec: return((CHAR)0xf4 & 0xff); case 0xfef0: return((CHAR)0xf5 & 0xff); case 0xfef2: return((CHAR)0xf6 & 0xff); case 0xfed0: return((CHAR)0xf7 & 0xff); case 0xfed5: return((CHAR)0xf8 & 0xff); case 0xfef5: return((CHAR)0xf9 & 0xff); case 0xfef6: return((CHAR)0xfa & 0xff); case 0xfedd: return((CHAR)0xfb & 0xff); case 0xfed9: return((CHAR)0xfc & 0xff); case 0xfef1: return((CHAR)0xfd & 0xff); case 0x25a0: return((CHAR)0xfe & 0xff); default: return(tx_cpsub(c)); /* For box characters etc */ } } int /* PC Code Page 866 */ #ifdef CK_ANSIC tx_cp866(USHORT c) #else tx_cp866(c) USHORT c; #endif /* CK_ANSIC */ { if (c < 0x80) /* Has C1 graphics */ return((CHAR)(c & 0xff)); switch (c) { case 0x00a0: return((CHAR)(255 & 0xff)); case 0x00a4: return((CHAR)(253 & 0xff)); case 0x00b0: return((CHAR)(248 & 0xff)); case 0x00b7: return((CHAR)(250 & 0xff)); case 0x0401: return((CHAR)(240 & 0xff)); case 0x0404: return((CHAR)(242 & 0xff)); case 0x0407: return((CHAR)(244 & 0xff)); case 0x040e: return((CHAR)(246 & 0xff)); case 0x0410: return((CHAR)(128 & 0xff)); case 0x0411: return((CHAR)(129 & 0xff)); case 0x0412: return((CHAR)(130 & 0xff)); case 0x0413: return((CHAR)(131 & 0xff)); case 0x0414: return((CHAR)(132 & 0xff)); case 0x0415: return((CHAR)(133 & 0xff)); case 0x0416: return((CHAR)(134 & 0xff)); case 0x0417: return((CHAR)(135 & 0xff)); case 0x0418: return((CHAR)(136 & 0xff)); case 0x0419: return((CHAR)(137 & 0xff)); case 0x041a: return((CHAR)(138 & 0xff)); case 0x041b: return((CHAR)(139 & 0xff)); case 0x041c: return((CHAR)(140 & 0xff)); case 0x041d: return((CHAR)(141 & 0xff)); case 0x041e: return((CHAR)(142 & 0xff)); case 0x041f: return((CHAR)(143 & 0xff)); case 0x0420: return((CHAR)(144 & 0xff)); case 0x0421: return((CHAR)(145 & 0xff)); case 0x0422: return((CHAR)(146 & 0xff)); case 0x0423: return((CHAR)(147 & 0xff)); case 0x0424: return((CHAR)(148 & 0xff)); case 0x0425: return((CHAR)(149 & 0xff)); case 0x0426: return((CHAR)(150 & 0xff)); case 0x0427: return((CHAR)(151 & 0xff)); case 0x0428: return((CHAR)(152 & 0xff)); case 0x0429: return((CHAR)(153 & 0xff)); case 0x042a: return((CHAR)(154 & 0xff)); case 0x042b: return((CHAR)(155 & 0xff)); case 0x042c: return((CHAR)(156 & 0xff)); case 0x042d: return((CHAR)(157 & 0xff)); case 0x042e: return((CHAR)(158 & 0xff)); case 0x042f: return((CHAR)(159 & 0xff)); case 0x0430: return((CHAR)(160 & 0xff)); case 0x0431: return((CHAR)(161 & 0xff)); case 0x0432: return((CHAR)(162 & 0xff)); case 0x0433: return((CHAR)(163 & 0xff)); case 0x0434: return((CHAR)(164 & 0xff)); case 0x0435: return((CHAR)(165 & 0xff)); case 0x0436: return((CHAR)(166 & 0xff)); case 0x0437: return((CHAR)(167 & 0xff)); case 0x0438: return((CHAR)(168 & 0xff)); case 0x0439: return((CHAR)(169 & 0xff)); case 0x043a: return((CHAR)(170 & 0xff)); case 0x043b: return((CHAR)(171 & 0xff)); case 0x043c: return((CHAR)(172 & 0xff)); case 0x043d: return((CHAR)(173 & 0xff)); case 0x043e: return((CHAR)(174 & 0xff)); case 0x043f: return((CHAR)(175 & 0xff)); case 0x0440: return((CHAR)(224 & 0xff)); case 0x0441: return((CHAR)(225 & 0xff)); case 0x0442: return((CHAR)(226 & 0xff)); case 0x0443: return((CHAR)(227 & 0xff)); case 0x0444: return((CHAR)(228 & 0xff)); case 0x0445: return((CHAR)(229 & 0xff)); case 0x0446: return((CHAR)(230 & 0xff)); case 0x0447: return((CHAR)(231 & 0xff)); case 0x0448: return((CHAR)(232 & 0xff)); case 0x0449: return((CHAR)(233 & 0xff)); case 0x044a: return((CHAR)(234 & 0xff)); case 0x044b: return((CHAR)(235 & 0xff)); case 0x044c: return((CHAR)(236 & 0xff)); case 0x044d: return((CHAR)(237 & 0xff)); case 0x044e: return((CHAR)(238 & 0xff)); case 0x044f: return((CHAR)(239 & 0xff)); case 0x0451: return((CHAR)(241 & 0xff)); case 0x0454: return((CHAR)(243 & 0xff)); case 0x0457: return((CHAR)(245 & 0xff)); case 0x045e: return((CHAR)(247 & 0xff)); case 0x2116: return((CHAR)(252 & 0xff)); case 0x2219: return((CHAR)(249 & 0xff)); case 0x221a: return((CHAR)(251 & 0xff)); case 0x2500: return((CHAR)(196 & 0xff)); case 0x2502: return((CHAR)(179 & 0xff)); case 0x250c: return((CHAR)(218 & 0xff)); case 0x2510: return((CHAR)(191 & 0xff)); case 0x2514: return((CHAR)(192 & 0xff)); case 0x2518: return((CHAR)(217 & 0xff)); case 0x251c: return((CHAR)(195 & 0xff)); case 0x2524: return((CHAR)(180 & 0xff)); case 0x252c: return((CHAR)(194 & 0xff)); case 0x2534: return((CHAR)(193 & 0xff)); case 0x253c: return((CHAR)(197 & 0xff)); case 0x2550: return((CHAR)(205 & 0xff)); case 0x2551: return((CHAR)(186 & 0xff)); case 0x2552: return((CHAR)(213 & 0xff)); case 0x2553: return((CHAR)(214 & 0xff)); case 0x2554: return((CHAR)(201 & 0xff)); case 0x2555: return((CHAR)(184 & 0xff)); case 0x2556: return((CHAR)(183 & 0xff)); case 0x2557: return((CHAR)(187 & 0xff)); case 0x2558: return((CHAR)(212 & 0xff)); case 0x2559: return((CHAR)(211 & 0xff)); case 0x255a: return((CHAR)(200 & 0xff)); case 0x255b: return((CHAR)(190 & 0xff)); case 0x255c: return((CHAR)(189 & 0xff)); case 0x255d: return((CHAR)(188 & 0xff)); case 0x255e: return((CHAR)(198 & 0xff)); case 0x255f: return((CHAR)(199 & 0xff)); case 0x2560: return((CHAR)(204 & 0xff)); case 0x2561: return((CHAR)(181 & 0xff)); case 0x2562: return((CHAR)(182 & 0xff)); case 0x2563: return((CHAR)(185 & 0xff)); case 0x2564: return((CHAR)(209 & 0xff)); case 0x2565: return((CHAR)(210 & 0xff)); case 0x2566: return((CHAR)(203 & 0xff)); case 0x2567: return((CHAR)(207 & 0xff)); case 0x2568: return((CHAR)(208 & 0xff)); case 0x2569: return((CHAR)(202 & 0xff)); case 0x256a: return((CHAR)(216 & 0xff)); case 0x256b: return((CHAR)(215 & 0xff)); case 0x256c: return((CHAR)(206 & 0xff)); case 0x2580: return((CHAR)(223 & 0xff)); case 0x2584: return((CHAR)(220 & 0xff)); case 0x2588: return((CHAR)(219 & 0xff)); case 0x258c: return((CHAR)(221 & 0xff)); case 0x2590: return((CHAR)(222 & 0xff)); case 0x2591: return((CHAR)(176 & 0xff)); case 0x2592: return((CHAR)(177 & 0xff)); case 0x2593: return((CHAR)(178 & 0xff)); case 0x25a0: return((CHAR)(254 & 0xff)); default: return(tx_cpsub(c)); /* For box characters etc */ } } int /* PC Code Page 869 */ #ifdef CK_ANSIC tx_cp869(USHORT c) #else tx_cp869(c) USHORT c; #endif /* CK_ANSIC */ { if (c < 0x80) /* Has C1 graphics */ return((CHAR)(c & 0xff)); switch (c) { case 0x00a0: return((CHAR)(255 & 0xff)); case 0x00a3: return((CHAR)(156 & 0xff)); case 0x00a6: return((CHAR)(138 & 0xff)); case 0x00a7: return((CHAR)(245 & 0xff)); case 0x00a8: return((CHAR)(249 & 0xff)); case 0x00a9: return((CHAR)(151 & 0xff)); case 0x00ab: return((CHAR)(174 & 0xff)); case 0x00ac: return((CHAR)(137 & 0xff)); case 0x00ad: return((CHAR)(240 & 0xff)); case 0x00b0: return((CHAR)(248 & 0xff)); case 0x00b1: return((CHAR)(241 & 0xff)); case 0x00b2: return((CHAR)(153 & 0xff)); case 0x00b3: return((CHAR)(154 & 0xff)); case 0x00b7: return((CHAR)(136 & 0xff)); case 0x00bb: return((CHAR)(175 & 0xff)); case 0x00bd: return((CHAR)(171 & 0xff)); case 0x0384: return((CHAR)(239 & 0xff)); case 0x0385: return((CHAR)(247 & 0xff)); case 0x0386: return((CHAR)(134 & 0xff)); case 0x0388: return((CHAR)(141 & 0xff)); case 0x0389: return((CHAR)(143 & 0xff)); case 0x038a: return((CHAR)(144 & 0xff)); case 0x038c: return((CHAR)(146 & 0xff)); case 0x038e: return((CHAR)(149 & 0xff)); case 0x038f: return((CHAR)(152 & 0xff)); case 0x0390: return((CHAR)(161 & 0xff)); case 0x0391: return((CHAR)(164 & 0xff)); case 0x0392: return((CHAR)(165 & 0xff)); case 0x0393: return((CHAR)(166 & 0xff)); case 0x0394: return((CHAR)(167 & 0xff)); case 0x0395: return((CHAR)(168 & 0xff)); case 0x0396: return((CHAR)(169 & 0xff)); case 0x0397: return((CHAR)(170 & 0xff)); case 0x0398: return((CHAR)(172 & 0xff)); case 0x0399: return((CHAR)(173 & 0xff)); case 0x039a: return((CHAR)(181 & 0xff)); case 0x039b: return((CHAR)(182 & 0xff)); case 0x039c: return((CHAR)(183 & 0xff)); case 0x039d: return((CHAR)(184 & 0xff)); case 0x039e: return((CHAR)(189 & 0xff)); case 0x039f: return((CHAR)(190 & 0xff)); case 0x03a0: return((CHAR)(198 & 0xff)); case 0x03a1: return((CHAR)(199 & 0xff)); case 0x03a3: return((CHAR)(207 & 0xff)); case 0x03a4: return((CHAR)(208 & 0xff)); case 0x03a5: return((CHAR)(209 & 0xff)); case 0x03a6: return((CHAR)(210 & 0xff)); case 0x03a7: return((CHAR)(211 & 0xff)); case 0x03a8: return((CHAR)(212 & 0xff)); case 0x03a9: return((CHAR)(213 & 0xff)); case 0x03aa: return((CHAR)(145 & 0xff)); case 0x03ab: return((CHAR)(150 & 0xff)); case 0x03ac: return((CHAR)(155 & 0xff)); case 0x03ad: return((CHAR)(157 & 0xff)); case 0x03ae: return((CHAR)(158 & 0xff)); case 0x03af: return((CHAR)(159 & 0xff)); case 0x03b0: return((CHAR)(252 & 0xff)); case 0x03b1: return((CHAR)(214 & 0xff)); case 0x03b2: return((CHAR)(215 & 0xff)); case 0x03b3: return((CHAR)(216 & 0xff)); case 0x03b4: return((CHAR)(221 & 0xff)); case 0x03b5: return((CHAR)(222 & 0xff)); case 0x03b6: return((CHAR)(224 & 0xff)); case 0x03b7: return((CHAR)(225 & 0xff)); case 0x03b8: return((CHAR)(226 & 0xff)); case 0x03b9: return((CHAR)(227 & 0xff)); case 0x03ba: return((CHAR)(228 & 0xff)); case 0x03bb: return((CHAR)(229 & 0xff)); case 0x03bc: return((CHAR)(230 & 0xff)); case 0x03bd: return((CHAR)(231 & 0xff)); case 0x03be: return((CHAR)(232 & 0xff)); case 0x03bf: return((CHAR)(233 & 0xff)); case 0x03c0: return((CHAR)(234 & 0xff)); case 0x03c1: return((CHAR)(235 & 0xff)); case 0x03c2: return((CHAR)(237 & 0xff)); case 0x03c3: return((CHAR)(236 & 0xff)); case 0x03c4: return((CHAR)(238 & 0xff)); case 0x03c5: return((CHAR)(242 & 0xff)); case 0x03c6: return((CHAR)(243 & 0xff)); case 0x03c7: return((CHAR)(244 & 0xff)); case 0x03c8: return((CHAR)(246 & 0xff)); case 0x03c9: return((CHAR)(250 & 0xff)); case 0x03ca: return((CHAR)(160 & 0xff)); case 0x03cb: return((CHAR)(251 & 0xff)); case 0x03cc: return((CHAR)(162 & 0xff)); case 0x03cd: return((CHAR)(163 & 0xff)); case 0x03ce: return((CHAR)(253 & 0xff)); case 0x2015: return((CHAR)(142 & 0xff)); case 0x2018: return((CHAR)(139 & 0xff)); case 0x2019: return((CHAR)(140 & 0xff)); case 0x2500: return((CHAR)(196 & 0xff)); case 0x2502: return((CHAR)(179 & 0xff)); case 0x250c: return((CHAR)(218 & 0xff)); case 0x2510: return((CHAR)(191 & 0xff)); case 0x2514: return((CHAR)(192 & 0xff)); case 0x2518: return((CHAR)(217 & 0xff)); case 0x251c: return((CHAR)(195 & 0xff)); case 0x2524: return((CHAR)(180 & 0xff)); case 0x252c: return((CHAR)(194 & 0xff)); case 0x2534: return((CHAR)(193 & 0xff)); case 0x253c: return((CHAR)(197 & 0xff)); case 0x2550: return((CHAR)(205 & 0xff)); case 0x2551: return((CHAR)(186 & 0xff)); case 0x2554: return((CHAR)(201 & 0xff)); case 0x2557: return((CHAR)(187 & 0xff)); case 0x255a: return((CHAR)(200 & 0xff)); case 0x255d: return((CHAR)(188 & 0xff)); case 0x2560: return((CHAR)(204 & 0xff)); case 0x2563: return((CHAR)(185 & 0xff)); case 0x2566: return((CHAR)(203 & 0xff)); case 0x2569: return((CHAR)(202 & 0xff)); case 0x256c: return((CHAR)(206 & 0xff)); case 0x2580: return((CHAR)(223 & 0xff)); case 0x2584: return((CHAR)(220 & 0xff)); case 0x2588: return((CHAR)(219 & 0xff)); case 0x2591: return((CHAR)(176 & 0xff)); case 0x2592: return((CHAR)(177 & 0xff)); case 0x2593: return((CHAR)(178 & 0xff)); case 0x25a0: return((CHAR)(254 & 0xff)); default: return(tx_cpsub(c)); /* For box characters etc */ } } int /* PC Code Page C0 graphics */ #ifdef CK_ANSIC tx_smiley(USHORT c) #else tx_smiley(c) USHORT c; #endif /* CK_ANSIC */ { if (c > 0x1f) return(-1); switch (c) { case 0x00a0: return((CHAR)0 & 0x7f); case 0x00a7: return((CHAR)21 & 0x7f); case 0x00b6: return((CHAR)20 & 0x7f); case 0x2022: return((CHAR)7 & 0x7f); case 0x203c: return((CHAR)19 & 0x7f); case 0x2190: return((CHAR)27 & 0x7f); case 0x2191: return((CHAR)24 & 0x7f); case 0x2192: return((CHAR)26 & 0x7f); case 0x2193: return((CHAR)25 & 0x7f); case 0x2194: return((CHAR)29 & 0x7f); case 0x2195: return((CHAR)18 & 0x7f); case 0x21a8: return((CHAR)23 & 0x7f); case 0x2319: return((CHAR)28 & 0x7f); case 0x25ac: return((CHAR)22 & 0x7f); case 0x25b2: return((CHAR)30 & 0x7f); case 0x25ba: return((CHAR)16 & 0x7f); case 0x25bc: return((CHAR)31 & 0x7f); case 0x25c4: return((CHAR)17 & 0x7f); case 0x25d8: return((CHAR)8 & 0x7f); case 0x25d9: return((CHAR)10 & 0x7f); case 0x25ef: return((CHAR)9 & 0x7f); case 0x263a: return((CHAR)1 & 0x7f); case 0x263b: return((CHAR)2 & 0x7f); case 0x263c: return((CHAR)15 & 0x7f); case 0x2640: return((CHAR)12 & 0x7f); case 0x2642: return((CHAR)11 & 0x7f); case 0x2660: return((CHAR)6 & 0x7f); case 0x2663: return((CHAR)5 & 0x7f); case 0x2665: return((CHAR)3 & 0x7f); case 0x2666: return((CHAR)4 & 0x7f); case 0x266a: return((CHAR)13 & 0x7f); case 0x266c: return((CHAR)14 & 0x7f); default: return(-1); } } USHORT /* Horizontal Scan Lines Unicode substitutions */ #ifdef CK_ANSIC tx_hslsub(USHORT c) #else tx_hslsub(c) USHORT c; #endif /* CK_ANSIC */ { if (c >= 0x23BA && c <= 0x23BD ) switch (c) { case 0x23BA: return(0x2500); /* H line - Scan 1 */ case 0x23BB: return(0x2500); /* H line - Scan 3 */ case 0x23BC: return(0x2500); /* H line - Scan 7 */ case 0x23BD: return(0x2500); /* H line - Scan 9 */ } return(c); } USHORT /* Kermit font 0xE??? Unicode substitutions */ #ifdef CK_ANSIC tx_usub(USHORT c) #else tx_usub(c) USHORT c; #endif /* CK_ANSIC */ { if (c < 0xE000 || c > 0xEFFF) return(c); switch (c) { case 0xE200: return(0x2524); /* Extensible Left Brace Middle */ case 0xE201: return(0x2570); /* Extensible Left Parenthesis bot */ case 0xE202: return(0x256d); /* Extensible left parenthesis top */ case 0xE203: return(0x2514); /* Extensible left SB bot */ case 0xE204: return(0x250c); /* Extensible left SB top */ case 0xE205: return(0x251c); /* Extensible right brace middle */ case 0xE206: return(0x256f); /* Extensible right parenthesis bot */ case 0xE207: return(0x256e); /* Extensible right parenthesis top */ case 0xE208: return(0x2518); /* Extensible right SB bot */ case 0xE209: return(0x2510); /* Extensible right SB top */ case 0xE20C: return(0x03a3); /* Summation symbol bot */ case 0xE20D: return(0x03a3); /* Summation symbol top */ case 0xE20E: return(0x2510); /* Right ceiling corner */ case 0xE20F: return(0x2518); /* Right floor corner */ case 0xE300: return(0x2502); /* V box line, extensible, left */ case 0xE309: return(0x2502); /* V box line, extensible, right */ case 0xE30A: return(0x258c); /* Diagonal fill, dark, UL */ case 0xE30B: return(0x2590); /* Diagonal fill, dark, UR */ case 0xE320: return(0x2583); /* Quadrant LL */ case 0xE321: return(0x2490); /* Quadrant LR */ case 0xE322: return(0x258c); /* Quadrant UL */ case 0xE323: return(0x2588); /* Quadrant UL and LL and LR */ case 0xE324: return(0x2588); /* Quadrant UL and LR */ case 0xE325: return(0x2588); /* Quadrant UL and LR */ case 0xE326: return(0x2588); /* Quadrant UL and UR and LL */ case 0xE327: return(0x2588); /* Quadrant UL and UR and LR */ case 0xE328: return(0x2590); /* Quadrant UR */ case 0xE329: return(0x2588); /* Quadrant UR and LL and LR */ case 0xE400: return(0x221a); /* Radical symbol, small */ case 0xE401: return(0x00bf); /* Reverse question mark */ default: return((unsigned)0xfffd); } } int /* Unicode to CP437 substitutions */ #ifdef CK_ANSIC tx_cpsub(USHORT c) #else tx_cpsub(c) USHORT c; #endif /* CK_ANSIC */ { int x; if (c < 0x0080) /* ASCII */ return((CHAR)(c & 0xff)); if (c >= 0x0080 && c <= 0x0100) { switch (c) { /* Latin-1 */ case 0x00A2: return(0x9b); /* Cent sign */ case 0x00A3: return(156); /* Pound sign */ case 0x00AC: return(170); /* Not symbol */ case 0x00B0: return(248); /* Degree symbol */ case 0x00B1: return(241); /* Plus or minus symbol */ case 0x00B2: return(253); /* Superscript 2 */ case 0x00B3: return(51); /* Superscript 3 */ case 0x00B6: return(14); /* Pilcrow symbol */ case 0x00B7: return(250); /* Center dot, small */ case 0x00B9: return(49); /* Superscript 1 */ case 0x00D0: return(68); /* Eth -> D */ case 0x00D7: return(120); /* Multiplication symbol */ case 0x00DE: return(84); /* Thorn -> T */ case 0x00F0: return(100); /* eth -> eth */ case 0x00F7: return(246); /* Division symbol */ case 0x00FE: return(116); /* thorn -> t */ default: return(0x13); } } else if (c >= 0x0100 && c <= 0x02ff) { /* Latin-2 etc */ switch (c) { case 0x0103: return(97); /* a breve */ case 0x0105: return(97); /* a ogonek */ case 0x0107: /* c acute */ case 0x010d: return(99); /* c caron */ case 0x010f: /* d caron */ case 0x0111: return(100); /* d with stroke */ case 0x0119: /* e ogonek */ case 0x011b: return(101); /* e caron */ case 0x011f: return(103); /* g breve */ case 0x0130: return(73); /* Capital I with Dot */ case 0x0131: return(105); /* Dotless i */ case 0x0132: return(89); /* IJ => Y */ case 0x0133: return(152); /* ij -> y diaeresis */ case 0x013a: /* l acute */ case 0x013e: /* l caron */ case 0x0142: return(108); /* l with stroke */ case 0x0144: /* n acute */ case 0x0148: return(110); /* n caron */ case 0x0151: return(111); /* o double acute */ case 0x0152: return(79); /* OE */ case 0x0153: return(111); /* oe */ case 0x0155: /* r acute */ case 0x0159: return(114); /* r caron */ case 0x015b: /* s acute */ case 0x015f: /* s ogonek */ case 0x0161: return(115); /* s caron */ case 0x0163: /* t ogonek */ case 0x0165: /* t caron */ case 0x0167: return(116); /* t with stroke */ case 0x016f: /* u ring */ case 0x0171: return(117); /* u double acute */ case 0x017a: /* z acute */ case 0x017c: /* z dot above */ case 0x017e: return(122); /* z caron */ case 0x0192: return(159); /* Function-of symbol, Script f */ case 0x01d0: return(105); /* i caron */ case 0x02c7: /* caron -> UNK */ case 0x02d8: return(0x13); /* breve -> UNK */ case 0x02dd: return(34); /* Double acute -> Doublequote */ default: return(0x13); } } else if (c >= 0x0300 && c <= 0x03ff) { /* Greek */ switch (c) { case 0x0393: return(226); /* Uppercase Greek Gamma */ case 0x0398: return(233); /* Uppercase Greek Theta */ case 0x039B: return(235); /* Uppercase Greek Lambda */ case 0x03A0: return(227); /* Uppercase Greek Pi */ case 0x03A3: return(228); /* Uppercase Greek Sigma */ case 0x03A4: return(0xEA); /* Omega */ case 0x03A6: return(232); /* Uppercase Greek Phi */ case 0x03A9: return(234); /* Uppercase Greek Omega */ case 0x03B1: return(0xE0); /* alpha */ case 0x03B2: return(0xE1); /* beta */ case 0x03B3: return(226); /* Lowercase Greek gamma */ case 0x03B4: return(0xEB); /* delta */ case 0x03B5: return(238); /* Lowercase Greek epsilon */ case 0x03B7: return(238); /* Lowercase Greek eta */ case 0x03B8: return(233); /* Lowercase Greek theta */ case 0x03B9: return(105); /* Lowercase Greek iota */ case 0x03BA: return(107); /* Lowercase Greek kappa */ case 0x03BB: return(235); /* Lowercase Greek lambda */ case 0x03BC: return(230); /* Lowercase Greek mu */ case 0x03C0: return(227); /* Lowercase Greek pi */ case 0x03C3: return(229); /* Lowercase Greek sigma */ case 0x03C4: return(231); /* Lowercase Greek tau */ case 0x03C6: return(237); /* Lowercase Greek phi */ case 0x03C7: return(120); /* Lowercase Greek chi */ case 0x03C9: return(234); /* Lowercase Greek omega */ default: return(0x13); } } else if (c >= 0x2000 && c <= 0x20ff) { /* Sub+Superscripts & Currency */ switch (c) { case 0x203C: return(0x13); /* !! */ case 0x2070: return(48); /* Superscript 0 */ case 0x2074: return(52); /* Superscript 4 */ case 0x2075: return(53); /* Superscript 5 */ case 0x2076: return(54); /* Superscript 6 */ case 0x2077: return(55); /* Superscript 7 */ case 0x2078: return(56); /* Superscript 8 */ case 0x2079: return(57); /* Superscript 9 */ case 0x207a: return(43); /* Superscript + */ case 0x207b: return(45); /* Superscript - */ case 0x207F: return(252); /* Superscript n */ case 0x2080: return(48); /* Subscript 0 */ case 0x2081: return(49); /* Subscript 1 */ case 0x2082: return(50); /* Subscript 2 */ case 0x2083: return(51); /* Subscript 3 */ case 0x2084: return(52); /* Subscript 4 */ case 0x2085: return(53); /* Subscript 5 */ case 0x2086: return(54); /* Subscript 6 */ case 0x2087: return(55); /* Subscript 7 */ case 0x2088: return(56); /* Subscript 8 */ case 0x2089: return(57); /* Subscript 9 */ case 0x208a: return(43); /* Subscript + */ case 0x208b: return(45); /* Subscript - */ case 0x20a7: return(0x93); /* Peseta */ default: x = tx_punc(c); /* Various spaces, dashes, etc */ return((x < 0) ? 0x13 : x); } } else if (c >= 0x2100 && c <= 0x21ff) { /* Arrows */ switch (c) { case 0x2190: return(27); /* Arrow, left-pointing */ case 0x2191: return(24); /* Arrow, up-pointing */ case 0x2192: return(26); /* Arrow, right-pointing */ case 0x2193: return(25); /* Arrow, down-pointing */ case 0x2194: return(0x1d); /* Arrow, left-right */ case 0x2195: return(0x12); /* Arrow, up-down */ case 0x219F: return(0x18); /* Arrow, up, doublehead */ case 0x21A1: return(0x19); /* Arrow, down, doublehead */ case 0x21A8: return(0x17); /* Arrow, up-down with base */ case 0x21D2: return(26); /* Implies symbol */ case 0x21D4: return(29); /* If and only if symbol */ case 0x21E4: return(0x1B); /* Arrow, left, to bar */ case 0x21E5: return(0x1A); /* Arrow, right, to bar */ case 0x21E8: return(0x10); /* Outline white right arrow */ case 0x21E9: return(0x0f); /* Outline white down arrow */ default: return(0x13); } } else if (c >= 0x2200 && c <= 0x22ff) { /* Math */ switch (c) { case 0x2202: return(235); /* Partial differential symbol */ case 0x2207: return(31); /* Nabla, Laplace operator */ case 0x2208: return(0x33); /* (because of QNX misunderstanding) */ case 0x221A: return(251); /* Radical symbol */ case 0x221D: return(236); /* Proportional-to */ case 0x221E: return(236); /* Infinity symbol */ case 0x2227: return(30); /* Logical AND */ case 0x2228: return(31); /* Logical OR */ case 0x2229: return(239); /* Intersection symbol */ case 0x222A: return(85); /* Union symbol */ case 0x222B: return(244); /* Integral symbol */ case 0x2234: return(254); /* Therefore symbol */ case 0x223C: return(126); /* Centered tilde operator */ case 0x2243: return(247); /* Asymptotically equals */ case 0x2248: return(247); /* Almost equal to symbol */ case 0x2260: return(88); /* Not equal symbol */ case 0x2261: return(240); /* Identity symbol */ case 0x2264: return(243); /* Less than or equal symbol */ case 0x2265: return(242); /* Greater than or equal symbol */ case 0x2282: return(40); /* Subset symbol */ case 0x2283: return(41); /* Superset symbol */ case 0x22A6: return(0xC3); /* Assertion symbol */ default: return(0x13); } } else if (c >= 0x23BA && c <= 0x23BD ) { switch (c) { case 0x23BA: return(0x2500); /* H line - Scan 1 */ case 0x23BB: return(0x2500); /* H line - Scan 3 */ case 0x23BC: return(0x2500); /* H line - Scan 7 */ case 0x23BD: return(0x2500); /* H line - Scan 9 */ } } else if (c >= 0x2300 && c <= 0x24ff) { /* Tech */ switch (c) { case 0x2308: return(0xDA); /* Left ceiling */ case 0x2309: return(0xBF); /* Right ceiling */ case 0x230A: return(0xC0); /* Left floor */ case 0x230B: return(0xD9); /* Right floor */ case 0x2319: return(0x1C); /* Turned Not sign */ case 0x2320: return(244); /* Integral symbol top */ case 0x2321: return(245); /* Integral symbol bot */ case 0x2329: return(60); /* BRA, large left angle bracket */ case 0x232A: return(62); /* KET, large right angle bracket */ case 0x2409: return(26); /* "HT" becomes right arrow */ case 0x240A: return(25); /* "LF" becomes down arrow */ case 0x240B: return(23); /* "VT" becomes up-down arrow */ case 0x240C: return(24); /* "FF" becomes up arrow */ case 0x240D: return(27); /* "CR" becomes left arrow */ case 0x2424: return(31); /* "NL" becomes down triangle */ default: return(0x13); } } else if (c >= 0x2500 && c <= 0x2552) { /* Box drawing */ switch (c) { case 0x2500: return(196); /* Center box bar horizontal */ case 0x2501: return(0xCD); /* Bold -> Double */ case 0x2502: return(179); /* Center box bar vertical */ case 0x2503: return(0xBA); /* Bold */ case 0x2504: return(45); /* Dashed line */ case 0x2506: return(124); /* Broken vertical bar */ case 0x250C: return(218); /* UL box corner */ case 0x250F: return(0xC9); /* Bold */ case 0x2510: return(191); /* UR Box Corner */ case 0x2513: return(0xBB); /* Bold */ case 0x2514: return(192); /* LL box corner */ case 0x2517: return(0xC8); /* Bold */ case 0x2518: return(217); /* LR box corner */ case 0x251B: return(0xBC); /* Bold */ case 0x251C: return(195); /* Left middle box side */ case 0x2520: return(0xC3); case 0x2523: return(0xCC); /* Bold */ case 0x2524: return(180); /* Right middle box side */ case 0x2528: return(180); case 0x252B: return(0xB9); /* Bold */ case 0x252C: return(194); /* Middle box top */ case 0x252F: return(194); case 0x2533: return(0xCB); /* Bold */ case 0x2534: return(193); /* Middle box bot */ case 0x2537: return(193); case 0x253B: return(0xCA); /* Bold */ case 0x253C: return(197); /* Box intersection */ case 0x253F: return(197); case 0x2542: return(197); case 0x2547: return(197); case 0x2548: return(197); case 0x2549: return(197); case 0x254A: return(197); case 0x254B: return(0xCE); /* Bold */ case 0x2550: return(205); /* Center box bar horizontal double */ case 0x2551: return(186); /* Center box bar vertical double */ case 0x2552: return(213); /* UL box corner single to double */ default: return(0x13); } } else if (c >= 0x2553 && c <= 0x2579) { /* More box drawing */ switch (c) { case 0x2553: return(214); /* UL box corner double to single */ case 0x2554: return(201); /* UL box corner double */ case 0x2555: return(184); /* UR box corner double to single */ case 0x2556: return(183); /* UR box corner single to double */ case 0x2557: return(187); /* UR box corner double */ case 0x2558: return(212); /* LL box corner single to double */ case 0x2559: return(211); /* LL box corner double to single */ case 0x255A: return(200); /* LL box corner double */ case 0x255B: return(190); /* LR box corner double to single */ case 0x255C: return(189); /* LR box corner single to double */ case 0x255D: return(188); /* LR box corner double */ case 0x255E: return(198); /* Left mid box side single to doubl */ case 0x255F: return(199); /* Left mid box side double to singl */ case 0x2560: return(204); /* Left middle box side double */ case 0x2561: return(181); /* Right box side double to single */ case 0x2562: return(182); /* Right box side single to double */ case 0x2563: return(185); /* Right middle box side double */ case 0x2564: return(209); /* Middle box top double to single */ case 0x2565: return(210); /* Middle box top single to double */ case 0x2566: return(203); /* Middle box top double */ case 0x2567: return(207); /* Middle box bot single to double */ case 0x2568: return(208); /* Middle box bot double to single */ case 0x2569: return(202); /* Middle box bot double */ case 0x256A: return(216); /* Box intersection double to single */ case 0x256B: return(215); /* Box intersection single to double */ case 0x256C: return(206); /* Box intersection double */ case 0x256D: return(218); /* UL arc */ case 0x256E: return(191); /* UR arc */ case 0x256F: return(217); /* LR arc */ case 0x2570: return(192); /* LL arc */ case 0x2571: return(179); /* Diagonal line LL to UR */ case 0x2572: return(196); /* Diagonal line UL to LR */ case 0x2573: return(88); /* Diagonal lines crossed */ case 0x2575: return(0xb3); /* High vertical line */ case 0x2576: return(45); /* Short horizontal line */ case 0x2577: return(0xb3); /* Low vertical line */ case 0x2579: return(0xb3); /* High vertical line bold */ default: return(0x13); } } else if (c >= 0x257a && c <= 0x25ff) { /* Still more box drawing */ switch (c) { case 0x257b: return(0xb3); /* Low vertical line bold */ case 0x2580: return(223); /* Quadrant UL and UR (top half) */ case 0x2581: return(0xc4); /* Scan line 9 */ case 0x2582: return(0xDC); /* Black blob lower half */ case 0x2584: return(220); /* Quadrant LL and LR (lower half) */ case 0x2588: return(219); /* Fill character dark */ case 0x258C: return(221); /* Quadrant UL and LL (left half) */ case 0x258E: return(0xDD); case 0x2590: return(222); /* Quadrant UR and LR (right half) */ case 0x2591: return(176); /* Fill character light */ case 0x2592: return(177); /* Fill character medium */ case 0x2593: return(178); /* Fill character heavy */ case 0x2594: return(0xc4); /* Scan line 1 */ case 0x25A0: return(254); /* Solid square, center */ case 0x25A6: return(177); /* Blotch */ case 0x25AC: /* Black rectangle */ case 0x25AF: return(0x16); /* White rectangle */ case 0x25B2: /* Triangle, up-pointing */ case 0x25B4: return(0x1e); /* Triangle, up-pointing */ case 0x25B6: /* Triangle, right-pointing, dark */ case 0x25B7: /* Triangle, right-pointing, light */ case 0x25B9: /* Triangle, right-pointing, light */ case 0x25BA: return(0x10); /* Triangle, right-pointing, narrow */ case 0x25BC: /* Triangle, down-pointing */ case 0x25BE: return(0x1f); /* Triangle, down-pointing */ case 0x25C0: /* Triangle, left-pointing, dark */ case 0x25C1: /* Triangle, left-pointing, dark */ case 0x25C4: return(0x11); /* Triangle, left-pointing, narrow */ case 0x25C6: return(4); /* Diamond, center, solid */ case 0x25CB: return(0x09); /* Circle */ case 0x25CF: return(249); /* Center dot, large */ case 0x25d8: return(0x08); /* Inverse bullet */ case 0x25d9: return(0x0a); /* Inverse white circle */ case 0x25E2: return(0xD9); /* Lower right triangle */ case 0x25E3: return(0xC0); /* Lower left triangle */ case 0x25E4: return(0xDA); /* Upper left triangle */ case 0x25E5: return(0xBf); /* Upper right triangle */ default: return(0x13); } } else if (c >= 0x2600) { /* All the rest */ switch (c) { case 0x263a: return(0x01); /* Smiley */ case 0x263b: return(0x02); /* Inverse Smiley */ case 0x263c: return(0x0f); /* White Sun with Rays */ case 0x2640: return(0x0c); /* Male sign */ case 0x2642: return(0x0b); /* Female sign */ case 0x2660: return(0x06); /* Spade */ case 0x2663: return(0x05); /* Club */ case 0x2665: return(0x03); /* Heart */ case 0x2666: return(0x04); /* Diamond, center, solid */ case 0x266a: return(0x0d); /* Quarter note */ case 0x266b: /* Beamed quarter notes */ case 0x266c: return(0x0e); /* Beamed 8th notes */ case 0x279e: return(0x1a); /* Bold right arrow */ case 0x27a1: return(0x1a); /* Heavy black right arrow. */ case 0xE200: return(180); /* Extensible left brace middle */ case 0xE201: return(192); /* Extensible Left parenthesis bot */ case 0xE202: return(218); /* Extensible left parenthesis top */ case 0xE203: return(192); /* Extensible left SB bot */ case 0xE204: return(218); /* Extensible left SB top */ case 0xE205: return(195); /* Extensible right brace middle */ case 0xE206: return(217); /* Extensible right parenthesis bot */ case 0xE207: return(191); /* Extensible right parenthesis top */ case 0xE208: return(217); /* Extensible right SB bot */ case 0xE209: return(191); /* Extensible right SB top */ case 0xE20C: return(228); /* Summation symbol bot */ case 0xE20D: return(228); /* Summation symbol top */ case 0xE20E: return(191); /* Right ceiling corner */ case 0xE20F: return(217); /* Right floor corner */ case 0xE300: return(179); /* V box line, extensible, left */ case 0xE309: return(179); /* V box line, extensible, right */ case 0xE30A: return(221); /* Diagonal fill, dark, UL */ case 0xE30B: return(222); /* Diagonal fill, dark, UR */ case 0xE320: return(221); /* Quadrant LL */ case 0xE321: return(222); /* Quadrant LR */ case 0xE322: return(221); /* Quadrant UL */ case 0xE323: return(219); /* Quadrant UL and LL and LR */ case 0xE324: return(219); /* Quadrant UL and LR */ case 0xE325: return(219); /* Quadrant UL and LR */ case 0xE326: return(219); /* Quadrant UL and UR and LL */ case 0xE327: return(219); /* Quadrant UL and UR and LR */ case 0xE328: return(222); /* Quadrant UR */ case 0xE329: return(219); /* Quadrant UR and LL and LR */ case 0xE400: return(251); /* Radical symbol, small */ case 0xE401: return(168); /* Reverse question mark */ case 0xFFFD: return(0x13); /* !! for unknown */ default: return(0x13); } } return(0x13); } #ifdef OS2 /* Lucida Console is a Unicode font, but it is very sparsely populated. Since it is the only monospace Unicode font most people have, we have to make reasonable substitutions or else 3/4 of the glyphs will show up as blobs on the screen. */ USHORT /* Unicode to Lucida Console */ #ifdef CK_ANSIC tx_lucidasub(USHORT c) #else tx_lucidasub(c) USHORT c; #endif /* CK_ANSIC */ { if (c < 0x0180) /* Latin-1 and Extended A */ return(c); /* For efficiency we try to arrange the sections by frequency of use. */ if (c >= 0x23BA && c <= 0x23BD) { switch(c) { case 0x23BA: /* H line - Scan 1 */ case 0x23BB: /* H line - Scan 3 */ case 0x23BC: /* H line - Scan 7 */ case 0x23BD: /* H line - Scan 9 */ return(0x2500); } } if (c >= 0x2500 && c <= 0x257f) { /* Box drawing */ if (c >= 0x2550 && c <= 0x256c) return(c); switch (c) { /* Themselves */ case 0x2500: case 0x2502: case 0x250c: case 0x2510: case 0x2514: case 0x2518: case 0x251c: case 0x2524: case 0x252c: case 0x2534: case 0x253c: return(c); /* Horizontal lines */ case 0x2501: /* Bold */ return(0x2550); /* Use double */ case 0x2504: case 0x2505: case 0x2508: case 0x2509: case 0x254c: case 0x254d: case 0x257c: case 0x257e: return(0x2500); /* Vertical lines */ case 0x2503: /* Bold */ return(0x2551); /* Use double */ case 0x2506: case 0x2507: case 0x250a: case 0x250b: /* Other */ case 0x254e: case 0x254f: case 0x257d: case 0x257f: case 0x2575: case 0x2577: case 0x2579: case 0x257b: return(0x2502); /* Upper left box corner */ case 0x250f: /* Bold */ return(0x2554); /* Use double */ case 0x250d: case 0x250e: /* Other */ return(0x250c); /* Upper right box corner */ case 0x2513: /* Bold */ return(0x2557); case 0x2511: case 0x2512: /* Other */ return(0x2510); /* Lower left box corner */ case 0x2517: /* Bold */ return(0x255a); case 0x2515: case 0x2516: /* Other */ return(0x2514); /* Lower right box corner */ case 0x251b: /* Bold */ return(0x255d); case 0x2519: case 0x251a: /* Other */ return(0x2518); /* Vertical and right */ case 0x2523: /* Bold */ return(0x2560); case 0x251d: case 0x251e: case 0x251f: case 0x2520: case 0x2521: case 0x2522: return(0x251c); /* Vertical and left */ case 0x252b: /* Bold */ return(0x2563); case 0x2525: case 0x2526: case 0x2527: case 0x2528: case 0x2529: case 0x252a: return(0x2524); /* Horizontal and down */ case 0x2533: /* Bold */ return(0x2566); case 0x252d: case 0x252e: case 0x252f: case 0x2530: case 0x2531: case 0x2532: return(0x252c); /* Horizontal and up */ case 0x253b: /* Bold */ return(0x2569); case 0x2535: case 0x2536: case 0x2537: case 0x2538: case 0x2539: case 0x253a: return(0x2534); /* Horizontal and Vertical */ case 0x254b: /* Bold */ return(0x256c); case 0x253d: case 0x253e: case 0x253f: case 0x2540: case 0x2541: case 0x2542: case 0x2543: case 0x2544: case 0x2545: case 0x2546: case 0x2547: case 0x2548: case 0x2549: case 0x254a: return(0x253c); /* Curved corners */ case 0x256d: /* UL */ return(0x250c); case 0x256e: /* UR */ return(0x2510); case 0x256f: /* LL */ return(0x2518); case 0x2570: /* LR */ return(0x2514); case 0x2571: /* Diagonal */ return(0x002f); case 0x2572: /* Other diagonal */ return(0x005c); case 0x2573: /* Diagonal cross */ return(0x00d7); /* Partial horizontal lines */ case 0x2574: case 0x2576: case 0x2578: case 0x257a: return(0x002d); default: return(0xfffd); } } if (c >= 0x2190 && c <= 0x21ff) { /* Arrows */ if (c >= 0x2190 && c <= 0x2195 || c == 0x21a8) return(c); switch (c) { /* Left-arrow forms */ case 0x219e: case 0x21a2: case 0x21a4: case 0x21a9: case 0x21bc: case 0x21d0: case 0x21da: case 0x21dc: case 0x21e0: case 0x21e4: case 0x21e6: case 0x21c7: return(0x2190); /* Right-arrow forms */ case 0x21a0: case 0x21a3: case 0x21a6: case 0x21aa: case 0x21c0: case 0x21c1: case 0x21c9: case 0x21d2: case 0x21db: case 0x21dd: case 0x21e2: case 0x21e5: case 0x21e8: return(0x2192); /* Up-arrow forms */ case 0x219f: case 0x21a5: case 0x21be: case 0x21bf: case 0x21c8: case 0x21d1: case 0x21de: case 0x21e1: case 0x21e7: case 0x21ea: return(0x2191); /* Down-arrow forms */ case 0x21a1: case 0x21a7: case 0x21af: case 0x21c2: case 0x21ce: case 0x21ca: case 0x21d3: case 0x21df: case 0x21e3: case 0x21e9: return(0x2193); /* Up-down-arrow forms */ case 0x21c5: case 0x21d5: return(0x2195); default: return(0xfffd); } } if (c >= 0x2580 && c <= 0x259f) { /* Block elements */ switch (c) { case 0x2580: case 0x2584: case 0x2588: case 0x258c: case 0x2590: case 0x2591: case 0x2592: case 0x2593: return(c); case 0x2581: case 0x2582: case 0x2583: case 0x2585: case 0x2586: case 0x2587: return(0x2584); case 0x2589: case 0x258a: case 0x258b: case 0x258d: case 0x258e: case 0x258f: return(0x258c); case 0x2595: return(0x2502); default: return(0xfffd); } } if (c >= 0x2200 && c <= 0x22ff) { /* Mathematical operators */ switch (c) { case 0x2202: case 0x2206: case 0x220f: case 0x2211: case 0x2212: case 0x2219: case 0x2220: case 0x221e: case 0x221f: case 0x2229: case 0x222b: case 0x2248: case 0x2260: case 0x2261: case 0x2264: case 0x2265: return(c); default: return(0xfffd); } } if (c >= 0x2300 && c <= 0x237f) { /* Miscellaneous Technical */ if (c == 0x2302 || c == 0x2310 || c == 0x2320 || c == 0x2321) return(c); switch (c) { case 0x2329: /* BRA */ return(0x003c); case 0x232a: /* KET */ return(0x003e); default: return(0xfffd); } } if (c >= 0x25a0 && c <= 0x25ff) { /* Geometric shapes */ switch (c) { /* Themselves */ case 0x25a0: case 0x25ac: case 0x25b2: case 0x25ba: case 0x25bc: case 0x25c4: case 0x25ca: case 0x25cb: case 0x25d8: case 0x25d9: return(c); /* Squares */ case 0x25a1: case 0x25a2: case 0x25a3: case 0x25a4: case 0x25a5: case 0x25a6: case 0x25a7: case 0x25a8: case 0x25a9: case 0x25aa: case 0x25ab: case 0x25e7: case 0x25e8: case 0x25e9: case 0x25ea: case 0x25eb: return(0x25a0); case 0x25ad: case 0x25ae: case 0x25af: /* Rectangles */ case 0x25b0: case 0x25b1: return(0x25ac); case 0x25b3: case 0x25b4: case 0x25b5: /* Upright triangles */ case 0x25ec: case 0x25ed: case 0x25ee: return(0x25b2); case 0x25b6: case 0x25b7: case 0x25b8: case 0x25b9: case 0x25bb: return(0x25ba); /* Right-pointing triangles */ case 0x25bd: case 0x25be: case 0x25bf: /* Down-pointing triangles */ return(0x25bc); case 0x25c0: case 0x25c1: case 0x25c2: case 0x25c3: case 0x25c5: return(0x25c4); /* Left-pointing triangles */ case 0x25c6: case 0x25c7: case 0x25c8: /* Diamonds */ return(0x2666); /* Circles */ case 0x25c9: case 0x25cc: case 0x25cd: case 0x25ce: case 0x25cf: case 0x25d0: case 0x25d1: case 0x25d2: case 0x25d3: case 0x25d4: case 0x25d5: case 0x25e6: case 0x25ef: return(0x25cb); /* Curves and corner triangles */ case 0x25dc: case 0x25e4: /* UL */ return(0x250c); case 0x25dd: case 0x25e5: /* UR */ return(0x2510); case 0x25df: case 0x25e3: /* LL */ return(0x2514); case 0x25de: case 0x25e2: /* LR */ return(0x2518); default: return(0xfffd); } } if (c >= 0x2600 && c <= 0x26ff) { /* Misc symbols */ switch (c) { /* Themselves */ case 0x263a: case 0x263b: case 0x263c: case 0x2640: case 0x2642: case 0x2660: case 0x2663: case 0x2665: case 0x2666: case 0x266a: case 0x266b: return(c); default: return(0xfffd); } } if (c >= 0x2794 && c <= 0x27be) /* Right-arrow Dingbats */ return(0x2192); if (c >= 0x2070 && c <= 0x209f) { /* Super & subscripts */ if (c == 0x207f) /* n */ return(c); else if (c == 0x2070) /* 0 */ return(0x0030); else if (c >= 0x2074 && c <= 0x2079) return(c - 0x2040); else if (c >= 0x2080 && c <= 0x2089) return(c - 0x2050); switch (c) { case 0x207a: case 0x208a: return(0x002b); case 0x207b: case 0x208b: return(0x002d); case 0x207c: case 0x208c: return(0x003d); case 0x207d: case 0x208d: return(0x0028); case 0x207e: case 0x208e: return(0x0029); default: return(0xfffd); } } if (c >= 0x0180 && c <= 0x024f) { /* Latin Extended B, Part I */ if (c == 0x0192 || c >= 0x01fa && c <= 0x01ff) return(c); /* Latin Extended B */ switch (c) { case 0x0180: case 0x0183: case 0x0184: case 0x0185: return(0x0062); /* Lowercase b variants */ case 0x0181: case 0x0182: return(0x0042); /* Uppercase B variants */ case 0x0186: case 0x0187: return(0x0043); /* Uppercase C variants */ case 0x0189: /* D with stroke */ return(0x00D0); /* Looks just like Eth */ case 0x018a: /* D with hook */ return(0x0044); case 0x018e: case 0x0190: /* E-like letters */ return(0x0045); case 0x018f: /* e-like letters */ return(0x0065); case 0x0191: /* F-like */ return(0x0046); case 0x0193: /* G-like */ return(0x0047); case 0x0194: /* Latin Capital Gamma */ return(0x0393); /* Use Greek */ case 0x0195: /* Gothic hv */ return(0x0068); /* Use h */ case 0x0196: /* Latin Capital Iota */ return(0x0399); /* Use Greek */ case 0x0197: /* I with bar */ return(0x0069); case 0x0198: /* K with hook */ return(0x004B); case 0x0199: /* k with hook */ return(0x006B); case 0x019A: /* l with bar */ return(0x006C); case 0x019B: /* lambda with bar */ return(0x03bb); /* Use Greek */ case 0x019C: /* Upside down m */ return(0x006d); case 0x019D: /* N with left hook */ return(0x004e); case 0x019E: /* n with long right leg */ return(0x006e); case 0x019F: case 0x01a0: case 0x01a2: /* O-like letters */ return(0x004f); case 0x01a1: case 0x01a3: /* o-like */ return(0x006f); case 0x01A4: /* P with hook */ return(0x0050); case 0x01A5: /* p with hook */ return(0x0070); case 0x01A6: /* Old Norse YR */ return(0x0052); case 0x01A7: /* Backwards S */ return(0x0053); case 0x01A8: case 0x01AA: /* s-like letters */ return(0x0073); case 0x01A9: /* Esh */ return(0x03a3); /* Looks just like Sigma */ case 0x01AB: case 0x01AD: /* t-like */ return(0x0074); case 0x01AC: case 0x01AE: /* T-like */ return(0x0054); case 0x01AF: case 0x01B1: /* U-like */ return(0x0055); case 0x01B0: /* u-like */ return(0x0075); case 0x01B2: /* V-like */ return(0x0056); case 0x01B3: /* Y-like */ return(0x0059); case 0x01B4: /* y-like */ return(0x0079); case 0x01B5: /* Z-like */ return(0x005a); case 0x01B6: /* z-like */ return(0x007a); case 0x01B7: /* Yogh */ return(0x0033); /* Use "3" */ case 0x01BF: /* Wynn */ return(0x0077); /* Use "w" */ case 0x01CD: /* A caron */ return(0x0041); case 0x01CE: /* a caron */ return(0x0061); case 0x01CF: /* I caron */ return(0x0049); case 0x01D0: /* i caron */ return(0x0069); case 0x01D1: /* O caron */ return(0x004f); case 0x01D2: /* o caron */ return(0x006f); case 0x01D3: /* U caron */ return(0x0055); case 0x01D4: /* u caron */ return(0x0075); /* U diaeresis + other things */ case 0x01D5: case 0x01D7: case 0x01D9: case 0x01DB: return(0x00dc); /* u diaeresis + other things */ case 0x01D6: case 0x01Da: case 0x01Dc: return(0x00fc); /* Fill in more here if anybody asks */ default: return(0xfffd); } } if (c >= 0x1e00 && c <= 0x1eff) { /* Latin Extended Additional */ if (c >= 0x1e80 && c <= 0x1e85) return(c); else return(0xfffd); } if (c >= 0x0400 && c <= 0x04ff) { /* Cyrillic */ if (c >= 0x0400 && c <= 0x045f || c == 0x0490 || c == 0x0491) return(c); else return(0xfffd); } if (c >= 0x0370 && c <= 0x03ff) { /* Greek */ if (c == 0x037e || c >= 0x0384 && c <= 0x03ce) return(c); switch (c) { case 0x0374: case 0x0375: return(0x0027); case 0x037a: return(0x002c); /* Fill in more here if needed */ default: return(0xfffd); } } if (c >= 0x1f00 && c <= 0x1fff) { /* Greek Extended */ /* Fill in if asked */ return(0xfffd); } if (c >= 0x20a0 && c <= 0x20cf) { /* Currency symbols */ if (c == 0x20a3 || c == 0x20a4 || c == 0x20a7 || c == 0x20ac) return(c); else return(0xfffd); } if (c >= 0x2100 && c <= 0x214f) { /* Letterlike symbols */ if (c == 0x2116 || c == 0x2122 || c == 0x2126) return(c); else return(0xfffd); } if (c >= 0x2150 && c <= 0x218f) { /* Number forms */ if (c >= 0x215b && c <= 0x215e) /* Roman numerals etc */ return(c); else return(0xfffd); } if (c >= 0x02b0 && c <= 0x02ff) { /* Spacing modifier letters */ if (c == 0x02c6 || c == 0x02c7 || c == 0x02c9 || c >= 0x02d8 && c <= 0x02dd) return(c); switch (c) { case 0x02b0: case 0x02b1: return(0x0068); case 0x02b2: return(0x006a); case 0x02b3: case 0x02b4: case 0x02b5: return(0x0072); case 0x02b7: return(0x0077); case 0x02b8: return(0x0079); case 0x02b9: return(0x00b4); case 0x02ba: return(0x0022); case 0x02bb: case 0x02bc: case 0x02bd: case 0x02be: case 0x02bf: return(0x0027); case 0x02c2: return(0x003c); case 0x02c3: return(0x003e); case 0x02da: return(0x00b0); case 0x02dc: return(0x007e); default: return(0xfffd); } } if (c >= 0x2000 && c <= 0x206f) { /* General Punctuation */ if (c >= 0x2013 && c <= 0x2015 || c >= 0x2017 && c <= 0x201a || c >= 0x201c && c <= 0x201e || c >= 0x2020 && c <= 0x2022 || c == 0x2026 || c == 0x2030 || c >= 0x2039 && c <= 0x203a || c == 0x203c || c == 0x203e || c == 0x2044) return(c); else if (c == 0x2016) return(0x2551); else if (c == 0x2017) return(0x2550); else if (c == 0x2044) return(0x002f); else return(0xfffd); } if (c == 0xfb01 || c == 0xfb02) /* Alphabetic Presentation Forms */ return(c); return(0xfffd); /* Catch-all */ } #endif /* OS2 */ /* Table of Blah-to-Unicode information structures. KEEP THIS IN SYNC WITH THE TX_blah DEFINITIONS in ckcuni.h. */ struct x_to_unicode * txrinfo[MAXTXSETS+1] = { &u_ascii, /* 0 US ISO 646 (ASCII) */ &u_british, /* 1 UK ISO 646 */ &u_fr_canadian, /* 2 Canadian French NRC */ NULL, /* 3 Cuba */ NULL, /* 4 Czecho */ &u_danish, /* 5 Danish/Norwegian ISO 646 */ &u_dutch, /* 6 Dutch NRC */ &u_finnish, /* 7 Finnish NRC */ &u_french, /* 8 French ISO 646 */ &u_german, /* 9 German ISO 646 */ &u_hebrew7, /* 10 Hebrew-7 (DEC) */ &u_hungarian, /* 11 Hungarian ISO 646 */ &u_icelandic, /* 12 Icelandic NRC */ &u_italian, /* 13 Italian ISO 646 */ &u_jis0201r, /* 14 Japanese Roman ISO 646 */ &u_jis0201k, /* 15 Japanese Katakana */ &u_koi7, /* 16 Short KOI */ &u_danish, /* 17 Norwegian/Danish ISO 646 */ &u_portuguese, /* 18 Portuguese ISO 646 */ &u_spanish, /* 19 spanish ISO 646 */ &u_swedish, /* 20 Swedish ISO 646 */ NULL, /* 21 Swedish ISO 646 for names */ &u_swiss, /* 22 Swiss NRC */ &u_8859_1, /* 23 ISO 8859-1 */ &u_8859_2, /* 24 ISO 8859-2 */ &u_8859_3, /* 25 ISO 8859-3 */ &u_8859_4, /* 26 ISO 8859-4 */ &u_8859_5, /* 27 ISO 8859-5 */ /* Cyrillic */ &u_8859_6, /* 28 ISO 8859-6 */ /* Arabic */ &u_8859_7, /* 29 ISO 8859-7 */ /* Greek */ &u_8859_8, /* 30 ISO 8859-8 */ /* Hebrew */ &u_8859_9, /* 31 ISO 8859-9 */ &u_8859_10, /* 32 ISO 8859-10 */ &u_koi8, /* 33 KOI-8 */ NULL, /* 34 JIS-7 */ NULL, /* 35 Shift JIS */ NULL, /* 36 Japanese EUC (JAE) */ NULL, /* 37 Japanese DEC Kanji */ &u_decmcs, /* 38 DEC MCS */ &u_nextstep, /* 39 NeXT */ &u_dgi, /* 40 DGI */ &u_maclatin, /* 41 Macintosh Latin */ &u_hproman8, /* 42 HP Roman 8 */ &u_cp437, /* 43 CP437 - Original */ &u_cp850, /* 44 CP850 - W Europe */ &u_cp852, /* 45 CP852 - E Europe */ &u_cp857, /* 46 CP857 - Turkish */ &u_cp862, /* 47 CP862 - Hebrew */ &u_cp864, /* 48 CP864 - Arabic */ &u_cp866, /* 49 CP866 - Cyrillic */ &u_cp869, /* 50 CP869 - Greek */ &u_decspec, /* 51 DEC Special Graphics */ &u_dectech, /* 52 DEC Technical */ &u_c0pics, /* 53 C0 Pictures */ &u_c1pics, /* 54 C1 Pictures */ &u_smiley, /* 55 IBM C0 Graphics */ &u_heath19g, /* 56 Heath 19 Graphics */ &u_tvig, /* 57 TVI Graphics */ &u_wyse_gn, /* 58 Wyse 60 Graphics Normal */ &u_wyse_g1, /* 59 Wyse 60 Graphics 1 */ &u_wyse_g2, /* 60 Wyse 60 Graphics 2 */ &u_wyse_g3, /* 61 Wyse 60 Graphics 3 */ &u_elot927, /* 62 Greek ELOT 927 */ &u_dgspec, /* 63 DG Special graphics */ &u_dgline, /* 64 DG Line drawing */ &u_dgword, /* 65 DG Word processing */ &u_hpline, /* 66 HP Line drawing */ &u_hpmath, /* 67 HP Math/Technical */ &u_qnxgrph, /* 68 QNX Graphics */ &u_snibrack, /* 69 SNI Brackets */ &u_snieuro, /* 70 SNI Euro */ &u_snifacet, /* 71 SNI Facet */ &u_sniibm, /* 72 SNI IBM */ &u_sniblanks, /* 73 SNI Blanks */ &u_cp1252, /* 74 Windows Latin-1 */ &u_cp1250, /* 75 Windows Latin-2 */ &u_cp1251, /* 76 Windows Cyrillic */ &u_cp1253, /* 77 Windows Greek */ &u_cp1254, /* 78 Windows Turkish */ &u_cp1257, /* 79 Windows Latin-4 */ &u_cp856, /* 80 Cyrillic PC Code Page 856 */ &u_cp855, /* 81 Cyrillic PC Code Page 855 */ &u_8859_1, /* 82 CP819 - Same as 8859-1 */ &u_8859_2, /* 83 CP912 - Same as 8859-2 */ &u_8859_3, /* 84 CP913 - Same as 8859-3 */ &u_8859_4, /* 85 CP914 - Same as 8859-4 */ &u_8859_5, /* 86 CP915 - Same as 8859-5 */ &u_8859_6, /* 87 CP1089 - Same as 8859-6 */ &u_8859_7, /* 88 CP813 - Same as 8859-7 */ &u_8859_8, /* 89 CP916 - Same as 8859-8 */ &u_8859_9, /* 90 CP920 - Same as 8859-9 */ &u_hproman8, /* 91 CP1051 - Same as HP Roman 8 */ &u_cp858, /* 92 CP858 - W Europe w/Euro */ &u_8859_15, /* 93 ISO 8859-15 Latin-15 */ &u_8859_15, /* 94 CP923 - Same as 8859-15 */ &u_8859_7, /* 95 ELOT928 - Same as 8859-7 */ &u_quickdraw, /* 96 CP10000 - Apple Quickdraw */ &u_cp37, /* 97 CP37 - U.S. EBCDIC */ &u_cp1255, /* 98 CP1255 - Windows Hebrew */ &u_cp1256, /* 99 CP1256 - Windows Arabic */ &u_cp1258, /* 100 CP1258 - Windows Viet Nam */ &u_mazovia, /* 101 Mazovia Polish code page */ &u_transparent, /* 102 Transparent */ &u_hz1500, /* 103 Hazeltine 1500/1520 graphics */ &u_koi8r, /* 104 KOI8-R */ &u_koi8u, /* 105 KOI8-U */ &u_apl1, /* 106 APL 1 (ISO) */ &u_apl2, /* 107 APL 2 (Dyadic) */ &u_apl3, /* 108 APL 3 (Plus) */ &u_apl4, /* 108 APL 4 (IBM) */ &u_apl5 /* 110 APL 5 (2741) */ }; /* Table of Blah-to-Unicode translation functions. KEEP THIS IN SYNC WITH THE TX_blah DEFINITITIONS in ckcuni.h. */ USHORT #ifdef CK_ANSIC (*xl_u[MAXTXSETS+1])(CHAR) #else (*xl_u[MAXTXSETS+1])() #endif /* CK_ANSIC */ = { ascii_u, /* 0 US ISO 646 (ASCII) */ british_u, /* 1 UK ISO 646 */ fr_canadian_u, /* 2 Canadian French NRC */ NULL, /* 3 Cuba */ NULL, /* 4 Czecho */ danish_u, /* 5 Danish/Norwegian ISO 646 */ dutch_u, /* 6 Dutch NRC */ finnish_u, /* 7 Finnish NRC */ french_u, /* 8 French ISO 646 */ german_u, /* 9 German ISO 646 */ hebrew7_u, /* 10 Hebrew-7 (DEC) */ hungarian_u, /* 11 Hungarian ISO 646 */ icelandic_u, /* 12 Icelandic */ italian_u, /* 13 Italian ISO 646 */ jis0201r_u, /* 14 Japanese Roman ISO 646 */ jis0201k_u, /* 15 Japanese Katakana */ koi7_u, /* 16 Short KOI */ danish_u, /* 17 Norwegian/Danish ISO 646 */ portuguese_u, /* 18 Portuguese ISO 646 */ spanish_u, /* 19 spanish ISO 646 */ swedish_u, /* 20 Swedish ISO 646 */ NULL, /* 21 Swedish ISO 646 for names */ swiss_u, /* 22 Swiss NRC */ iso_8859_1_u, /* 23 ISO 8859-1 */ iso_8859_2_u, /* 24 ISO 8859-2 */ iso_8859_3_u, /* 25 ISO 8859-3 */ iso_8859_4_u, /* 26 ISO 8859-4 */ iso_8859_5_u, /* 27 ISO 8859-5 */ /* Cyrillic */ iso_8859_6_u, /* 28 ISO 8859-6 */ /* Arabic */ iso_8859_7_u, /* 29 ISO 8859-7 */ /* Greek */ iso_8859_8_u, /* 30 ISO 8859-8 */ /* Hebrew */ iso_8859_9_u, /* 31 ISO 8859-9 */ /* Latin-5 */ iso_8859_10_u, /* 32 ISO 8859-10 */ koi8_u, /* 33 KOI-8 */ NULL, /* 34 JIS-7 */ NULL, /* 35 Shift JIS */ NULL, /* 36 Japanese EUC (JAE) */ NULL, /* 37 Japanese DEC Kanji */ decmcs_u, /* 38 DEC MCS */ nextstep_u, /* 39 NeXT */ dgi_u, /* 40 DGI */ maclatin_u, /* 41 Macintosh Latin */ hproman8_u, /* 42 HP Roman 8 */ cp437_u, /* 43 CP437 - Original */ cp850_u, /* 44 CP850 - W Europe */ cp852_u, /* 45 CP852 - E Europe */ cp857_u, /* 46 CP857 - Turkish */ cp862_u, /* 47 CP862 - Hebrew */ cp864_u, /* 48 CP864 - Arabic */ cp866_u, /* 49 CP866 - Cyrillic */ cp869_u, /* 50 CP869 - Greek */ decspec_u, /* 51 DEC Special Graphics */ dectech_u, /* 52 DEC Technical */ c0pics_u, /* 53 C0 Pictures */ c1pics_u, /* 54 C1 Pictures */ smiley_u, /* 55 IBM C0 Graphics */ heath19g_u, /* 56 Heath 19 graphics */ tvig_u, /* 57 TVI graphics */ wyse_gn_u, /* 58 Wyse 60 normal-mode graphics */ wyse_g1_u, /* 59 Wyse 60 graphics 1 */ wyse_g2_u, /* 60 Wyse 60 graphics 2 */ wyse_g3_u, /* 61 Wyse 60 graphics 3 */ elot927_u, /* 62 Greek ELOT 927 */ dgspec_u, /* 63 DG Special graphics */ dgline_u, /* 64 DG Line drawing */ dgword_u, /* 65 DG Word processing */ hpline_u, /* 66 HP Line drawing */ hpmath_u, /* 67 HP Math/Technical */ qnxgrph_u, /* 68 QNX Graphics */ snibrack_u, /* 69 SNI Brackets */ snieuro_u, /* 70 SNI Euro */ snifacet_u, /* 71 SNI Facet */ sniibm_u, /* 72 SNI IBM */ sniblanks_u, /* 73 SNI Blanks */ cp1252_u, /* 74 Windows Latin-1 */ cp1250_u, /* 75 Windows Latin-2 */ cp1251_u, /* 76 Windows Cyrillic */ cp1253_u, /* 77 Windows Greek */ cp1254_u, /* 78 Windows Turkish */ cp1257_u, /* 79 Windows Latin-4 */ cp856_u, /* 80 Cyrillic PC Code Page 856 */ cp855_u, /* 81 Cyrillic PC Code Page 856 */ iso_8859_1_u, /* 82 CP819 - Same as 8859-1 */ iso_8859_2_u, /* 83 CP912 - Same as 8859-2 */ iso_8859_3_u, /* 84 CP913 - Same as 8859-3 */ iso_8859_4_u, /* 85 CP914 - Same as 8859-4 */ iso_8859_5_u, /* 86 CP915 - Same as 8859-5 */ iso_8859_6_u, /* 87 CP1089 - Same as 8859-6 */ iso_8859_7_u, /* 88 CP813 - Same as 8859-7 */ iso_8859_8_u, /* 89 CP916 - Same as 8859-8 */ iso_8859_9_u, /* 90 CP920 - Same as 8859-9 */ hproman8_u, /* 91 CP1051 -Same as HP Roman 8 */ cp858_u, /* 92 CP858 - W Europe w/Euro */ iso_8859_15_u, /* 93 ISO 8859-15 Latin 15 */ iso_8859_15_u, /* 94 CP923 - Same as 8859-15 */ iso_8859_7_u, /* 95 ELOT928 - Same as 8859-7 */ quickdraw_u, /* 96 CP10000 - Apple Quickdraw */ cp37_u, /* 97 CP37 - U.S. EBCDIC */ cp1255_u, /* 98 CP1255 - Windows Hebrew */ cp1256_u, /* 99 CP1256 - Windows Arabic */ cp1258_u, /* 100 CP1258 - Windows Viet Nam */ mazovia_u, /* 101 Mazovia PC code page */ ident_u, /* 102 Transparent - no translation */ hz1500_u, /* 103 Hazeltine 1500/1520 graphics */ koi8r_u, /* 104 KOI8-R */ koi8u_u, /* 105 KOI8-U */ apl1_u, /* 106 APL 1 (ISO) */ apl2_u, /* 107 APL 2 (AIX) */ apl3_u, /* 108 APL 3 (Plus) */ apl4_u, /* 109 APL 4 (IBM) */ apl5_u /* 110 APL 5 (2741) */ }; /* Table of Unicode-to-Blah translation functions. KEEP THIS IN SYNC WITH THE TX_blah DEFINITITIONS in ckcuni.h, and also with the tables above. */ int #ifdef CK_ANSIC (*xl_tx[MAXTXSETS+1])(USHORT) #else (*xl_tx[MAXTXSETS+1])() #endif /* CK_ANSIC */ = { tx_usascii, /* 0 US ISO 646 (ASCII) */ tx_british, /* 1 UK ISO 646 */ tx_fr_canadian, /* 2 Canadian French NRC */ NULL, /* 3 Cuba */ NULL, /* 4 Czecho */ tx_danish, /* 5 Danish/Norwegian ISO 646 */ tx_dutch, /* 6 Dutch NRC */ tx_finnish, /* 7 Finnish NRC */ tx_french, /* 8 French ISO 646 */ tx_german, /* 9 German ISO 646 */ tx_hebrew7, /* 10 Hebrew-7 (DEC) */ tx_hungarian, /* 11 Hungarian ISO 646 */ tx_icelandic, /* 12 Icelandic */ tx_italian, /* 13 Italian ISO 646 */ tx_jis0201r, /* 14 Japanese Roman ISO 646 */ tx_jis0201k, /* 15 Japanese Katakana */ tx_koi7, /* 16 Short KOI */ tx_danish, /* 17 Norwegian/Danish ISO 646 */ tx_portuguese, /* 18 Portuguese ISO 646 */ tx_spanish, /* 19 spanish ISO 646 */ tx_swedish, /* 20 Swedish ISO 646 */ NULL, /* 21 Swedish ISO 646 for names */ tx_swiss, /* 22 Swiss NRC */ tx_ident, /* 23 ISO 8859-1 */ tx_8859_2, /* 24 ISO 8859-2 */ tx_8859_3, /* 25 ISO 8859-3 */ tx_8859_4, /* 26 ISO 8859-4 */ tx_8859_5, /* 27 ISO 8859-5 */ /* Cyrillic */ tx_8859_6, /* 28 ISO 8859-6 */ /* Arabic */ tx_8859_7, /* 29 ISO 8859-7 */ /* Greek */ tx_8859_8, /* 30 ISO 8859-8 */ /* Hebrew */ tx_8859_9, /* 31 ISO 8859-9 */ /* Latin-5 */ tx_8859_10, /* 32 ISO 8859-10 */ /* Latin-6 */ tx_koi8, /* 33 KOI-8 */ NULL, /* 34 JIS-7 */ NULL, /* 35 Shift JIS */ NULL, /* 36 Japanese EUC (JAE) */ NULL, /* 37 Japanese DEC Kanji */ tx_decmcs, /* 38 DEC MCS */ tx_nextstep, /* 39 NeXT */ tx_dgi, /* 40 DGI */ tx_maclatin, /* 41 Macintosh Latin */ tx_hproman8, /* 42 HP Roman 8 */ tx_cp437, /* 43 CP437 - Original */ tx_cp850, /* 44 CP850 - W Europe */ tx_cp852, /* 45 CP852 - E Europe */ tx_cp857, /* 46 CP857 - Turkish */ tx_cp862, /* 47 CP862 - Hebrew */ tx_cp866, /* 48 CP864 - Arabic */ tx_cp866, /* 49 CP866 - Cyrillic */ tx_cp869, /* 50 CP869 - Greek */ NULL, /* Display only */ /* 51 DEC Special Graphics */ NULL, /* Display only */ /* 52 DEC Technical */ NULL, /* Display only */ /* 53 C0 Pictures */ NULL, /* Display only */ /* 54 C1 Pictures */ NULL, /* Display only */ /* 55 IBM C0 Graphics */ NULL, /* Display only */ /* 56 Heath 19 graphics */ NULL, /* Display only */ /* 57 TVI graphics */ NULL, /* Display only */ /* 58 Wyse 60 normal-mode graphics */ NULL, /* Display only */ /* 59 Wyse 60 graphics 1 */ NULL, /* Display only */ /* 60 Wyse 60 graphics 2 */ NULL, /* Display only */ /* 61 Wyse 60 graphics 3 */ tx_elot927, /* 62 Greek ELOT 927 */ NULL, /* Display only */ /* 63 DG special graphics */ NULL, /* Display only */ /* 64 DG line-drawing */ NULL, /* Display only */ /* 65 DG word-processing */ NULL, /* Display only */ /* 66 HP line-drawing */ NULL, /* Display only */ /* 67 HP math/techical */ NULL, /* Display only */ /* 68 QNX Graphics */ NULL, /* Display only */ /* 69 SNI Brackets */ NULL, /* Display only */ /* 70 SNI Euro */ NULL, /* Display only */ /* 71 SNI Facet */ NULL, /* Display only */ /* 72 SNI IBM */ NULL, /* Display only */ /* 73 SNI Blanks */ tx_cp1252, /* 74 Windows Latin-1 */ tx_cp1250, /* 75 Windows Latin-2 */ tx_cp1251, /* 76 Windows Cyrillic */ tx_cp1253, /* 77 Windows Greek */ tx_cp1254, /* 78 Windows Turkish */ tx_cp1257, /* 79 Windows Latin-4 */ tx_cp856, /* 80 Cyrillic PC Code Page 856 */ tx_cp855, /* 81 Cyrillic PC Code Page 855 */ tx_ident, /* 82 CP819 - Same as 8859-1 */ tx_8859_2, /* 83 CP912 - Same as 8859-2 */ tx_8859_3, /* 84 CP913 - Same as 8859-3 */ tx_8859_4, /* 85 CP914 - Same as 8859-4 */ tx_8859_5, /* 86 CP915 - Same as 8859-5 */ tx_8859_6, /* 87 CP1089 - Same as 8859-6 */ tx_8859_7, /* 88 CP813 - Same as 8859-7 */ tx_8859_8, /* 89 CP916 - Same as 8859-8 */ tx_8859_9, /* 90 CP920 - Same as 8859-9 */ tx_hproman8, /* 91 CP1051 -Same as HP Roman 8 */ tx_cp858, /* 92 CP858 - W Europe w/Euro */ tx_8859_15, /* 93 ISO 8859-15 Latin 15 */ tx_8859_15, /* 94 CP923 - Same as Latin 15 */ tx_8859_7, /* 95 ELOT928 - Same as 8859-7 */ tx_quickdraw, /* 96 CP10000 - Apple Quickdraw */ tx_cp37, /* 97 CP37 - U.S. EBCDIC */ tx_cp1255, /* 98 CP1255 - Windows Hebrew */ tx_cp1256, /* 99 CP1256 - Windows Arabic */ tx_cp1258, /* 100 CP1258 - Windows Viet Nam */ tx_mazovia, /* 101 Mazovia PC code page */ tx_ident, /* 102 Transparent - no translation */ NULL, /* Display only */ /* 103 Hazeltine 1500/1520 graphics */ tx_koi8r, /* 104 KOI8-R */ tx_koi8u, /* 105 KOI8-U */ tx_apl1, /* 106 APL 1 (ISO) */ tx_apl2, /* 107 APL 2 (AIX) */ tx_apl3, /* 108 APL 3 (Plus) */ tx_apl4, /* 108 APL 4 (IBM) */ tx_apl5 /* 110 APL 5 (2741) */ }; /* Table of FCS-to-Unicode translation functions. KEEP THIS IN SYNC WITH THE FC_blah DEFINITITIONS in ckuxla.h. */ USHORT #ifdef CK_ANSIC (*xl_fcu[MAXFCSETS+1])(CHAR) #else (*xl_fcu[MAXFCSETS+1])() #endif /* CK_ANSIC */ = { ascii_u, /* 0 US ISO 646 (ASCII) */ british_u, /* 1 UK ISO 646 */ dutch_u, /* 2 Dutch NRC */ finnish_u, /* 3 Finnish NRC */ french_u, /* 4 French ISO 646 */ fr_canadian_u, /* 5 Canadian French NRC */ german_u, /* 6 German ISO 646 */ hungarian_u, /* 7 Hungarian ISO 646 */ italian_u, /* 8 Italian ISO 646 */ danish_u, /* 9 Danish/Norwegian ISO 646 */ portuguese_u, /* 10 Portuguese ISO 646 */ spanish_u, /* 11 spanish ISO 646 */ swedish_u, /* 12 Swedish ISO 646 */ swiss_u, /* 13 Swiss NRC */ iso_8859_1_u, /* 14 ISO 8859-1 Latin-1 */ iso_8859_2_u, /* 15 ISO 8859-2 Latin-2 */ decmcs_u, /* 16 DEC MCS */ nextstep_u, /* 17 NeXT */ cp437_u, /* 18 CP437 - Original */ cp850_u, /* 19 CP850 - W Europe */ cp852_u, /* 20 CP852 - E Europe */ quickdraw_u, /* 21 CP10000 - Apple Quickdraw */ dgi_u, /* 22 DGI */ hproman8_u, /* 23 HP Roman 8 */ iso_8859_5_u, /* 24 ISO 8859-5 Cyrillic */ cp866_u, /* 25 CP866 - Cyrillic */ koi7_u, /* 26 Short KOI */ koi8_u, /* 27 KOI-8 */ NULL, /* 28 JIS-7 */ NULL, /* 29 Shift-JIS */ NULL, /* 30 Japanese EUC */ NULL, /* 31 DEC Kanji */ hebrew7_u, /* 32 Hebrew-7 (DEC) */ iso_8859_8_u, /* 33 ISO 8859-8 Hebrew */ cp862_u, /* 34 CP862 Hebrew */ elot927_u, /* 35 Greek ELOT 927 */ iso_8859_7_u, /* 36 ISO 8859-7 Greek */ cp869_u, /* 37 CP869 Greek */ iso_8859_15_u, /* 38 ISO 8859-15 Latin-9 */ cp858_u, /* 39 CP858 - W Europe w/Euro */ cp855_u, /* 40 Cyrillic PC Code Page 856 */ cp1251_u, /* 41 Windows Cyrillic */ cp856_u, /* 42 Bulgarian PC Code Page 856 */ cp1250_u, /* 43 Windows Latin-2 */ mazovia_u, /* 44 Mazovia PC code page */ NULL, /* 45 UCS-2 */ NULL, /* 46 UTF-8 */ koi8r_u, /* 47 KOI8-R */ koi8u_u, /* 48 KOI8-U */ cp1252_u /* 49 CP1252 */ }; /* Table of Unicode-to-FCS translation functions. KEEP THIS IN SYNC WITH THE FC_blah DEFINITITIONS in ckuxla.h. */ int #ifdef CK_ANSIC (*xl_ufc[MAXFCSETS+1])(USHORT) #else (*xl_ufc[MAXFCSETS+1])() #endif /* CK_ANSIC */ = { tx_usascii, /* 0 US ISO 646 (ASCII) */ tx_british, /* 1 UK ISO 646 */ tx_dutch, /* 2 Dutch NRC */ tx_finnish, /* 3 Finnish NRC */ tx_french, /* 4 French ISO 646 */ tx_fr_canadian, /* 5 Canadian French NRC */ tx_german, /* 6 German ISO 646 */ tx_hungarian, /* 7 Hungarian ISO 646 */ tx_italian, /* 8 Italian ISO 646 */ tx_danish, /* 9 Danish/Norwegian ISO 646 */ tx_portuguese, /* 10 Portuguese ISO 646 */ tx_spanish, /* 11 spanish ISO 646 */ tx_swedish, /* 12 Swedish ISO 646 */ tx_swiss, /* 13 Swiss NRC */ tx_ident, /* 14 ISO 8859-1 Latin-1 */ tx_8859_2, /* 15 ISO 8859-2 Latin-2 */ tx_decmcs, /* 16 DEC MCS */ tx_nextstep, /* 17 NeXT */ tx_cp437, /* 18 CP437 - Original */ tx_cp850, /* 19 CP850 - W Europe */ tx_cp852, /* 20 CP852 - E Europe */ tx_quickdraw, /* 21 CP10000 - Apple Quickdraw */ tx_dgi, /* 22 DGI */ tx_hproman8, /* 23 HP Roman 8 */ tx_8859_5, /* 24 ISO 8859-5 Cyrillic */ tx_cp866, /* 25 CP866 - Cyrillic */ tx_koi7, /* 26 Short KOI */ tx_koi8, /* 27 KOI-8 */ NULL, /* 28 JIS-7 */ NULL, /* 29 Shift-JIS */ NULL, /* 30 Japanese EUC */ NULL, /* 31 DEC Kanji */ tx_hebrew7, /* 32 Hebrew-7 (DEC) */ tx_8859_8, /* 33 ISO 8859-8 Hebrew */ tx_cp862, /* 34 CP862 Hebrew */ tx_elot927, /* 35 Greek ELOT 927 */ tx_8859_7, /* 36 ISO 8859-7 Greek */ tx_cp869, /* 37 CP869 Greek */ tx_8859_15, /* 38 ISO 8859-15 Latin-9 */ tx_cp858, /* 39 CP858 - W Europe w/Euro */ tx_cp855, /* 40 Cyrillic PC Code Page 856 */ tx_cp1251, /* 41 Windows Cyrillic */ tx_cp856, /* 42 Bulgarian PC Code Page 856 */ tx_cp1250, /* 43 Windows Latin-2 */ tx_mazovia, /* 44 Mazovia PC code page */ NULL, /* 45 UCS-2 */ NULL, /* 46 UTF-8 */ tx_koi8r, /* 47 KOI8-R */ tx_koi8u, /* 48 KOI8-U */ tx_cp1252 /* 49 CP1252 */ }; /* Table of TCS-to-Unicode translation functions. KEEP THIS IN SYNC WITH THE TC_blah DEFINITIONS in ckuxla.h. */ USHORT #ifdef CK_ANSIC (*xl_tcu[MAXTCSETS+1])(CHAR) #else (*xl_tcu[MAXTCSETS+1])() #endif /* CK_ANSIC */ = { NULL, /* 0 = Transparent */ ascii_u, /* 1 = ASCII */ iso_8859_1_u, /* 2 ISO 8859-1 Latin-1 */ iso_8859_2_u, /* 3 ISO 8859-2 Latin-2 */ iso_8859_5_u, /* 4 ISO 8859-5 Cyrillic */ NULL, /* 5 Japanese EUC */ iso_8859_8_u, /* 6 ISO 8859-8 Hebrew */ iso_8859_7_u, /* 7 ISO 8859-7 Greek */ iso_8859_15_u, /* 8 ISO 8859-15 Latin-9 */ NULL, /* 9 UCS-2 */ NULL /* 10 UTF-8 */ }; /* Table of Unicode-to-TCS translation functions. KEEP THIS IN SYNC WITH THE TC_blah DEFINITIONS in ckuxla.h. */ int #ifdef CK_ANSIC (*xl_utc[MAXTCSETS+1])(USHORT) #else (*xl_utc[MAXTCSETS+1])() #endif /* CK_ANSIC */ = { NULL, /* 0 = Transparent */ tx_usascii, /* 1 = ASCII */ tx_ident, /* 2 ISO 8859-1 Latin-1 */ tx_8859_2, /* 3 ISO 8859-2 Latin-2 */ tx_8859_5, /* 4 ISO 8859-5 Cyrillic */ NULL, /* 5 Japanese EUC */ tx_8859_8, /* 6 ISO 8859-8 Hebrew */ tx_8859_7, /* 7 ISO 8859-7 Greek */ tx_8859_15, /* 8 ISO 8859-15 Latin-9 */ NULL, /* 9 UCS-2 */ NULL /* 10 UTF-8 */ }; #ifdef COMMENT /* The UTF8 conversions are based on the ConvertUTF functions written by Mark E. Davis, copyright 1994 Taligent, Inc. Tables for use in calculating UTF8 conversions. These contain support for ISO-10646 which supports a 31-bit char size. NOTE: 0xnnnUL is NOT portable! */ ULONG offsetsFromUTF8[7] = { #ifdef CK_ANSIC 0x00000000UL, /* Ignored */ 0x00000000UL, 0x00003080UL, 0x000E2080UL, 0x03C82080UL, 0xFA082080UL, 0x82082080UL #else 0x00000000L, /* Ignored */ 0x00000000L, 0x00003080L, 0x000E2080L, 0x03C82080L, (unsigned) 0xFA082080L, (unsigned) 0x82082080L #endif /* CK_ANSIC */ }; CHAR bytesInUTF8[256] = { 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, 4,4,4,4,4,4,4,4,5,5,5,5,6,6,6,6 }; #endif /* COMMENT */ CHAR firstByteMark[7] = {0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC}; /* utf8_to_ucs2() takes one UTF-8 byte at a time. It returns a pointer the UCS-2 character if and only if the entire UTF-8 string has been received. Return values: 0: Complete UTF-8 sequence received; ucs2 points to valid ucs2 character. >0: UTF-8 sequence incomplete, ignore ucs2 value. <0: UTF-8 error, 0xfffd should be inserted BEFORE the ucs2 value. NOTE: A negative value is returned only when two return values are indicated, e.g. when a UTF-8 sequence is interrupted by an ASCII char. In this case the incomplete UTF-8 sequence must be replaced by 0xfffd, and then the ASCII character is kept as-is. In other error cases, ucs2 is set to 0xfffd and the return value is 0. */ #define UTFBUFSIZ 16 int #ifdef CK_ANSIC utf8_to_ucs2(CHAR ch, USHORT ** ucs2) #else utf8_to_ucs2(ch, ucs2) CHAR ch; USHORT ** ucs2; #endif /* CK_ANSIC */ { static USHORT ucs2return = 0; #ifdef COMMENT /* Unicode Consortium sample code works only with well-formed UTF8 */ int i = 0; static int len = 0; static CHAR utf8[UTFBUFSIZ] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; ULONG ucs4 = 0; utf8[len++] = ch; /* Add char to string to process */ if (len < bytesInUTF8[utf8[0]]) /* Need more bytes */ return(bytesInUTF8[utf8[0]] - len); switch (len) { /* Have complete sequence... */ case 6: ucs4 += utf8[i++]; ucs4 <<= 6; /* (fall-thru is intentional) */ case 5: ucs4 += utf8[i++]; ucs4 <<= 6; case 4: ucs4 += utf8[i++]; ucs4 <<= 6; case 3: ucs4 += utf8[i++]; ucs4 <<= 6; case 2: ucs4 += utf8[i++]; ucs4 <<= 6; case 1: ucs4 += utf8[i++]; } ucs4 -= offsetsFromUTF8[len]; ucs2return = (USHORT)(ucs4 & 0xFFFF); #ifdef DEBUG /* This shows that our return value is in the prevailing byte order: */ /* e.g. LE on PC, BE on Sparc. */ if (deblog) { char buf[16]; union ck_short xx; xx.x_short = ucs2return; sprintf(buf,"%04X",ucs2return); debug(F111,"utf8_to_ucs2 short",buf,ucs2return); debug(F101,"utf8_to_ucs2 char[0]","",xx.x_char[0]); debug(F101,"utf8_to_ucs2 char[1]","",xx.x_char[1]); } #endif /* DEBUG */ *ucs2 = &ucs2return; len = 0; return(0); #else /* Robuster code adapted from Thomas Dickey, Xfree86, recommended by Markus Kuhn. */ static int utfcount = 0; /* Position in UTF sequence */ int utferror = 0; /* Flag for malformed UTF */ unsigned c = ch; /* Input byte */ int haveucs2 = 0; if (ch < 0x80) { /* ASCII char... */ if (utfcount > 0) /* Not legal in UTF-8 sequence */ utferror = 1; /* so flag */ haveucs2 = 1; ucs2return = ch; /* but also return it */ utfcount = 0; /* reset UTF-8 count */ } else if (ch < 0xc0) { /* 0x80 <= c < 0xc0... */ if (utfcount < 1) { /* Not valid in first position... */ utferror = 1; } else { /* Maybe valid */ if (ucs2return > 0x03ff) { /* Value would be > 0xffff */ utferror = 1; /* so not valid */ } else { /* OK... */ ucs2return <<= 6; /* Shift result */ ucs2return |= (ch & 0x3f); /* and OR in this byte */ } if (--utfcount == 0) haveucs2 = 1; } } else { /* c >= 0xc0... */ if (utfcount > 0) utferror = 1; if (c < 0xe0) { utfcount = 1; ucs2return = (c & 0x1f); haveucs2 = 1; } else if (c < 0xf0) { utfcount = 2; ucs2return = (c & 0x0f); haveucs2 = 1; } else if (c < 0xf8) { utfcount = 3; ucs2return = (c & 0x07); haveucs2 = 1; } else if (c < 0xfc) { utfcount = 4; ucs2return = (c & 0x03); haveucs2 = 1; } else if (c < 0xfe) { utfcount = 5; ucs2return = (c & 0x01); haveucs2 = 1; } else { utferror = 1; utfcount = 0; } } if (haveucs2 == 0 && utferror != 0) { haveucs2 = 1; ucs2return = 0xfffd; utferror = 0; } if (haveucs2) { *ucs2 = &ucs2return; if (utferror) utfcount = 0 - utfcount; return(utfcount); } else { if (utfcount == 0) utfcount++; return(utfcount); } #endif /* COMMENT */ } /* ucs2_to_utf8() takes one ucs2 character and returns the length and and a pointer to an array containing the equivalent utf8 string. This code can easily be altered to support UCS4 simply by changing the input from USHORT to ULONG. Returns: >0: Number of bytes in the utf8 string. 0: (or less) Error. */ int #ifdef CK_ANSIC ucs2_to_utf8(USHORT ucs2, CHAR ** utf8) #else ucs2_to_utf8(ucs2, utf8) USHORT ucs2; CHAR ** utf8; #endif /* CK_ANSIC */ { static CHAR utf8return[8]={0,0,0,0,0,0,0,0}; register CONST ULONG byteMask = 0xBF; register CONST ULONG byteMark = 0x80; int utf8len = 0; int i = 0; if (ucs2 < 0x80) { utf8len = 1; debug(F101,"ucs2_to_utf8 X1","",utf8len); } else if (ucs2 < 0x800) { utf8len = 2; debug(F101,"ucs2_to_utf8 X2","",utf8len); } else #ifdef DO_UCS4 /* This is always true for UCS-2 but would be needed for UCS-4*/ /* When ucs2 is USHORT this gives compiler warnings. */ if (ucs2 <= 0xffff) #endif /* DO_UCS4 */ { utf8len = 3; debug(F101,"ucs2_to_utf8 X3","",utf8len); } #ifdef DO_UCS4 /* The following would be for UCS-4 */ else if (ucs2 < 0x200000) { utf8len = 4; } else if (ucs2 < 0x4000000) { utf8len = 5; } else if (ucs2 <= #ifdef CK_ANSIC 0x7FFFFFFFUL /* (doesn't really need the "U") */ #else 0x7FFFFFFFL #endif /* CK_ANSIC */ ) { /* 31 bits = max for UCS4 */ utf8len = 6; } else { utf8len = 2; ucs2 = 0xFFFD; /* Replacement for invalid char */ } #endif /* DO_UCS4 */ i = utf8len; /* index into utf8return */ utf8return[i--] = 0; /* Null terminate the string */ switch (utf8len) { /* code falls through cases! */ case 6: utf8return[i--] = (ucs2 | byteMark) & byteMask; ucs2 >>= 6; case 5: utf8return[i--] = (ucs2 | byteMark) & byteMask; ucs2 >>= 6; case 4: utf8return[i--] = (ucs2 | byteMark) & byteMask; ucs2 >>= 6; case 3: utf8return[i--] = (ucs2 | byteMark) & byteMask; ucs2 >>= 6; case 2: utf8return[i--] = (ucs2 | byteMark) & byteMask; ucs2 >>= 6; case 1: utf8return[i--] = ucs2 | firstByteMark[utf8len]; } debug(F111,"ucs2_to_utf8",utf8return,utf8len); *utf8 = utf8return; return(utf8len); } /* UTF-8 functions... */ #ifdef CK_ANSIC extern int (*xuf)(USHORT); /* Translation function UCS to FCS */ extern USHORT (*xfu)(CHAR); /* Translation function FCS to UCS */ #else extern int (*xuf)(); extern USHORT (*xfu)(); #endif /* CK_ANSIC */ /* u _ t o _ b -- UTF-8 to Byte */ /* Converts from UTF-8 to the current terminal or file character set. Call with: c: a single byte, which is part of a UTF-8 stream. Returns: -9: Error, with second char to follow (call u_to_b2() to get it). -2: UCS line/paragraph end (LS or PS). -1: UTF-8 stream is incomplete and more input is required. >=0: Byte value of result, possibly the "error" byte (e.g. '?'). Requires: Global (*xuf)() to point to a function that translates from UCS-2 to the appropriate term/file character set. */ static int savedbyte = 0; int /* Call if u_to_b() returns -9 */ u_to_b2() { return((unsigned)(savedbyte & 0xff)); } int /* UTF-8 to byte */ #ifdef CK_ANSIC u_to_b(CHAR c) #else u_to_b(c) CHAR c; #endif /* CK_ANSIC */ { int x; USHORT * ucs2, uc; if (!xuf) /* If no conversion function */ return(c); /* don't convert (shouldn't happen). */ x = utf8_to_ucs2(c,&ucs2); /* Send for conversion to UCS-2 */ if (x > 0) /* Not done yet... */ return(-1); uc = (x < 0) ? 0xfffd : *ucs2; /* Done, check result */ if (uc == 0x2028 || uc == 0x2029) /* LS or PS */ return(-2); return((unsigned)(((*xuf)(uc)) & 0xff)); /* Convert UCS-2 to byte */ } /* b _ t o _ u -- Byte to UTF-8 */ /* Converts a byte from the current terminal or file character set to UTF-8. Call with: c........ The byte to be converted. buf...... Pointer to buffer in which to place the result. buflen... Length of the result buffer. setsize.. The size of the source character set (128 or 256). Requires: Global (*xfu)() to point to the function to convert the byte to UCS-2. Returns: -1 if the xfu is NULL; otherwise: >0 indicating the length (in bytes) of the UTF-8 string. If the translation fails, the Unicode "Replacement Character" is returned (0xFFFD translated to UTF-8 == 0xFFBD). */ int /* Byte to UTF-8 */ #ifdef CK_ANSIC b_to_u(CHAR c, CHAR * buf, int buflen, int setsize) #else b_to_u(c, buf, buflen, setsize) CHAR c, * buf; int buflen, setsize; #endif /* CK_ANSIC */ { CHAR * tmp = NULL; int i, count = 0; USHORT uc; if (!xfu) { debug(F100,"b_to_u no xfu","",0); return(-1); } uc = c; if (((setsize > 128) && (c & 0x80)) || setsize <= 128) { if (xfu) /* FCS-to-UCS function */ uc = (*xfu)(c); } count = ucs2_to_utf8(uc,&tmp); if (count < 0) { buf[0] = 0xef; /* == 0xFFFD in UTF-8 */ buf[1] = 0xbf; buf[2] = 0xbd; buf[3] = '\0'; return(2); } if (count >= buflen) { debug(F101,"WARNING: UTF8 buffer overflow","",count); count = buflen - 1; } for (i = 0; i < count; i++) /* Copy to result buffer */ buf[i] = tmp[i]; buf[i] = '\0'; return(count); } #ifndef OS2 int isunicode( /* Tells whether the host we are */ #ifdef CK_ANSIC /* running on supports Unicode */ void /* display */ #endif /* CK_ANSIC */ ) { #ifdef NT extern int tt_unicode; #ifdef KUI return(tt_unicode); #else /* KUI */ if (tt_unicode && !isWin95()) return(1); else return(0); #endif /* KUI */ #else /* NT */ return(0); #endif /* NT */ } #endif /* OS2 */ #endif /* UNICODE */ ckcuni.h0000644000015300001460000002531111266111344011307 0ustar fdckermit/* C K C U N I . H -- Unicode/Terminal character-set translations */ /* Copyright (C) 1999, 2009, Trustees of Columbia University in the City of New York. All rights reserved. See the C-Kermit COPYING.TXT file or the copyright text in the ckcmai.c module for disclaimer and permissions. Authors: Frank da Cruz The Kermit Project, Columbia University, New York City. Jeffrey E Altman Secure Endpoints Inc., New York City */ /* Terminal character sets */ #ifndef CKOUNI_H #define CKOUNI_H #ifdef OS2 #ifndef CKOUNI #define CKOUNI /* Use UNICODE for OS/2 functions */ #endif /* CKOUNI */ #ifdef KUI #define X_CKOUNI_IN /* Use Unicode Input */ #define CKOUNI_OUT #endif /* KUI */ #endif /* OS2 */ /* Terminal Character Sets */ #define TX_ASCII 0 /* US ASCII */ #define TX_BRITISH 1 /* British ISO 646 */ #define TX_CN_FRENCH 2 /* Canadian French NRC */ #define TX_CUBAN 3 /* Cuba */ #define TX_CZECH 4 /* Czech Republic */ #define TX_DANISH 5 /* Denmark / Norway ISO 646 */ #define TX_DUTCH 6 /* Dutch NRC */ #define TX_FINNISH 7 /* Finnish NRC */ #define TX_FRENCH 8 /* French ISO 646 */ #define TX_GERMAN 9 /* German ISO 646 */ #define TX_HE7 10 /* Hebrew 7 (DEC) */ #define TX_HUNGARIAN 11 /* Hungarian ISO 646 */ #define TX_ICELANDIC 12 /* Icelandic NRC */ #define TX_ITALIAN 13 /* Italian ISO 646 */ #define TX_J201R 14 /* JIS 0201 Japanese Roman */ #define TX_J201K 15 /* JIS 0201 Katakana */ #define TX_KOI7 16 /* Short KOI */ #define TX_NORWEGIAN 17 /* Denmark / Norway ISO 646 */ #define TX_PORTUGUESE 18 /* Portuguese ISO 646 */ #define TX_SPANISH 19 /* Spanish ISO 646 */ #define TX_SWEDISH 20 /* Swedish ISO 646 */ #define TX_SWE_2 21 /* Swedish for names ISO 646 */ #define TX_SWISS 22 /* Swiss NRC */ #define TX_8859_1 23 /* Latin-1 */ #define TX_8859_2 24 /* Latin-2 */ #define TX_8859_3 25 /* Latin-3 */ #define TX_8859_4 26 /* Latin-4 */ #define TX_8859_5 27 /* Latin/Cyrillic */ #define TX_8859_6 28 /* Latin/Arabic */ #define TX_8859_7 29 /* Latin/Greek */ #define TX_8859_8 30 /* Latin/Hebrew */ #define TX_8859_9 31 /* Latin-5 */ #define TX_8859_10 32 /* Latin-6 */ #define TX_KOI8 33 /* GOST 19768-74 KOI-8 */ #define TX_JIS7 34 /* JIS-7 */ #define TX_SHJIS 35 /* Shift JIS */ #define TX_JEUC 36 /* Japanese EUC */ #define TX_JDEC 37 /* Japanese DEC Kanji */ #define TX_DECMCS 38 /* DEC MCS */ #define TX_NEXT 39 /* NeXT */ #define TX_DGI 40 /* Data General International */ #define TX_MACL1 41 /* Macintosh Latin-1 */ #define TX_HPR8 42 /* HP Roman 8 */ /* Code pages */ #define TX_CP437 43 /* Original */ #define TX_CP850 44 /* Multinational (Western Europe) */ #define TX_CP852 45 /* Eastern Europe */ #define TX_CP857 46 /* Turkey */ #define TX_CP862 47 /* Hebrew */ #define TX_CP864 48 /* Arabic */ #define TX_CP866 49 /* Cyrillic */ #define TX_CP869 50 /* Greek */ #define TX_DECSPEC 51 /* DEC Special Graphics */ #define TX_DECTECH 52 /* DEC Technical */ #define TX_C0PICT 53 /* C0 Display Controls */ #define TX_C1PICT 54 /* C1 Display Controls */ #define TX_IBMC0GRPH 55 /* IBM C0 Graphics (smileys) */ #define TX_H19GRAPH 56 /* Heath/Zenith 19 Graphics */ #define TX_TVIGRAPH 57 /* Televideo Graphics */ #define TX_WYSE60G_N 58 /* Wyse 60 Native Mode Graphics */ #define TX_WYSE60G_1 59 /* Wyse 60 Graphics 1 */ #define TX_WYSE60G_2 60 /* Wyse 60 Graphics 2 */ #define TX_WYSE60G_3 61 /* Wyse 60 Graphics 3 */ /* New ones that came too late for the nice grouping... */ #define TX_ELOT927 62 /* Greek ELOT 927 */ #define TX_DGPCGRPH 63 /* DG PC Graphics */ #define TX_DGLDGRPH 64 /* DG Line Drawing Graphics */ #define TX_DGWPGRPH 65 /* DG Word Processing (etc) Graphics */ #define TX_HPLINE 66 /* HP Line Drawing */ #define TX_HPMATH 67 /* HP Math/Technical */ #define TX_QNXGRPH 68 /* QNX Graphics */ /* Siemens Nixdorf character sets */ #define TX_SNIBRACK 69 /* SNI 97801 Brackets */ #define TX_SNIEURO 70 /* SNI 97801 Euro */ #define TX_SNIFACET 71 /* SNI 97801 Facet */ #define TX_SNIIBM 72 /* SNI 97801 "IBM" */ #define TX_SNIBLANK 73 /* SNI 97801 Blanks */ /* Windows Code pages */ #define TX_CP1252 74 /* Latin-1 Windows */ #define TX_CP1250 75 /* Latin-2 Windows */ #define TX_CP1251 76 /* Cyrillic Windows */ #define TX_CP1253 77 /* Greece Windows */ #define TX_CP1254 78 /* Turkey Windows */ #define TX_CP1257 79 /* Latin-4 Windows */ #define TX_CP856 80 /* Bulgaria CP856 (DATECS Ltd) */ #define TX_CP855 81 #define TX_CP819 82 /* Same as ISO 8859-1 */ #define TX_CP912 83 /* Same as ISO 8859-2 */ #define TX_CP913 84 /* Same as ISO 8859-3 */ #define TX_CP914 85 /* Same as ISO 8859-4 */ #define TX_CP915 86 /* Same as ISO 8859-5 */ #define TX_CP1089 87 /* Same as ISO 8859-6 */ #define TX_CP813 88 /* Same as ISO 8859-7 */ #define TX_CP916 89 /* Same as ISO 8859-8 */ #define TX_CP920 90 /* Same as ISO 8859-9 */ #define TX_CP1051 91 /* Same as HP Roman 8 */ #define TX_CP858 92 /* Multinational (W. Europe) w/Euro */ #define TX_8859_15 93 /* Latin-9 */ #define TX_CP923 94 /* Same as ISO 8859-15 */ #define TX_ELOT928 95 /* Same as ISO 8859-7 */ #define TX_CP10000 96 /* Same as original Apple Quickdraw */ #define TX_CP37 97 /* EBCDIC */ #define TX_CP1255 98 /* Israel Windows */ #define TX_CP1256 99 /* Arabic Windows */ #define TX_CP1258 100 /* Viet Nam Windows */ #define TX_MAZOVIA 101 #define TX_TRANSP 102 /* Transparent - no translation */ #define TX_HZ1500 103 /* Hazeltine 1500 graphics set */ #define TX_KOI8R 104 /* KOI8R - Russian */ #define TX_KOI8U 105 /* KOI8U - Ukrainian */ #define TX_APL1 106 /* APL ISO IR 68 */ #define TX_APL2 107 /* Dyadic Systems Inc APL */ #define TX_APL3 108 /* APL-Plus (APL-2000) */ #define TX_APL4 109 /* IBM APL/2 */ #define TX_APL5 110 /* APL-2741 */ #define MAXTXSETS 111 /* Number of terminal character sets */ /* The following are not implemented yet */ /* UTF-8 is supported as a special mode in Kermit 95 (see utf8 flag) */ #define TX_UTF7 128 #define TX_UTF8 129 #define TX_HEXBYTES 242 /* Hex bytes */ #define TX_DEBUG 243 /* Debugging but not hex bytes */ /* These are actually used */ #define TX_UNDEF 255 /* Unknown character-set */ /* Flag bit values */ #define X2U_STD 1 /* Has standard ISO 4873 layout */ #define X2U_ISO 2 /* ISO standard character set */ #define X2U_JIS 4 /* Japan Industrial Standard */ #define X2U_CP 8 /* PC Code Page */ #define X2U_DEC 16 /* DEC Private character set */ #define X2U_CXG 32 /* Control codes used for graphics */ struct x_to_unicode { int size; /* 94, 96, 128, or other */ int offset; /* 0, 32, 33, 128, 160, ... */ int flags; int family; /* Language family, writing system */ char * keywd; /* Keyword name */ char * name; /* Descriptive name */ int code; /* ISO reg number if Standard */ /* CP number if Code-page, etc. */ char * final; /* Esc seq final char(s) (ISO, DEC) */ unsigned short map[256]; /* Mapping table */ }; extern struct keytab txrtab[]; extern int ntxrtab; #ifndef NULL #define NULL (char *)0 #endif /* NULL */ #ifndef USHORT #define USHORT unsigned short #endif /* USHORT */ #ifndef ULONG #define ULONG unsigned long #endif /* ULONG */ #ifndef CHAR #define CHAR unsigned char #endif /* CHAR */ #ifdef CK_ANSIC extern USHORT (*xl_u[MAXTXSETS+1])(CHAR); /* Blah-to-Unicode functions */ extern int (*xl_tx[MAXTXSETS+1])(USHORT); /* Unicode-to-Blah functions */ #else extern USHORT (*xl_u[MAXTXSETS+1])(); extern int (*xl_tx[MAXTXSETS+1])(); #endif /* CK_ANSIC */ extern struct x_to_unicode * txrinfo[MAXTXSETS+1]; _PROTOTYP(int isunicode, (void)); _PROTOTYP(int utf8_to_ucs2, (CHAR, USHORT **)); _PROTOTYP(int ucs2_to_utf8, (USHORT, CHAR **)); _PROTOTYP(int tx_cpsub, (USHORT)); _PROTOTYP(int u_to_b, (CHAR) ); _PROTOTYP(int u_to_b2, (void) ); _PROTOTYP(int b_to_u, (CHAR, CHAR *, int, int) ); #ifdef KANJI _PROTOTYP(USHORT sj_to_un, (USHORT) ); /* Shift-JIS to Unicode */ _PROTOTYP(USHORT un_to_sj, (USHORT) ); /* Unicode to Shift-JIS */ #endif /* KANJI */ #ifdef OS2 #ifdef NT _inline #else _Inline #endif /* NT */ int isunicode( #ifdef CK_ANSIC void #endif /* CK_ANSIC */ ) { extern int tt_unicode; #ifdef NT #ifdef KUI return(tt_unicode); #else /* KUI */ if (tt_unicode && !isWin95()) return(1); else return(0); #endif /* KUI */ #else /* NT */ return(0); #endif /* NT */ } #endif /* OS2 */ #endif /* CKOUNI_H */ ckcxla.h0000644000015300001460000002567411573434226011324 0ustar fdckermit/* File CKCXLA.H System-independent character-set translation header file for C-Kermit. */ /* Author: Frank da Cruz , The Kermit Project - Columbia University, New York City. Copyright (C) 1985, 2009, Trustees of Columbia University in the City of New York. All rights reserved. See the C-Kermit COPYING.TXT file or the copyright text in the ckcmai.c module for disclaimer and permissions. */ /* NOTE: ISO 204 is Latin-1 + Euro. ISO 205 is Latin-4 + Euro. ISO 206 is Latin-7 + Euro. */ #ifndef CKCXLA_H /* Guard against multiple inclusion */ #define CKCXLA_H #ifndef KANJI /* Systems supporting Kanji */ #ifdef OS2 #define KANJI #endif /* OS2 */ #endif /* KANJI */ #ifdef NOKANJI /* Except if NOKANJI is defined. */ #ifdef KANJI #undef KANJI #endif /* KANJI */ #endif /* NOKANJI */ #ifndef NOUNICODE #ifndef UNICODE /* Unicode support */ #ifdef OS2ORUNIX /* Only for K95, UNIX, VMS,... */ #define UNICODE #else #ifdef VMS #define UNICODE #endif /* VMS */ #endif /* OS2ORUNIX */ #endif /* UNICODE */ #endif /* NOUNICODE */ #define XLA_NONE 0 /* Translation types - none */ #define XLA_BYTE 1 /* Byte-for-byte */ #define XLA_JAPAN 2 /* Japanese */ #define XLA_UNICODE 3 /* Unicode */ #ifndef UNIORKANJI /* Unicode OR Kanji */ #ifdef UNICODE /* i.e. some support for */ #define UNIORKANJI /* multibyte character sets */ #endif /* UNICODE */ #ifdef KANJI #define UNIORKANJI #endif /* KANJI */ #endif /* UNIORKANJI */ /* Disable all support for all classes of character sets if NOCSETS is defined. */ #ifdef NOCSETS #ifdef CKOUNI #undef CKOUNI #endif /* CKOUNI */ #ifdef KANJI #undef KANJI #endif /* KANJI */ #ifdef CYRILLIC #undef CYRILLIC #endif /* CYRILLIC */ #ifdef LATIN2 #undef LATIN2 #endif /* LATIN2 */ #ifdef HEBREW #undef HEBREW #endif /* HEBREW */ #ifdef UNICODE #undef UNICODE #endif /* UNICODE */ #ifndef NOUNICODE #define NOUNICODE #endif /* NOUNICODE */ #else /* Not NOCSETS - Rest of this file... */ #ifdef NOUNICODE /* Unicode */ #ifdef UNICODE #undef UNICODE #endif /* UNICODE */ #endif /* NOUNICODE */ #ifdef UNICODE #ifdef OS2 #ifndef CKOUNI #define CKOUNI /* Special Unicode features for K95 */ #endif /* CKOUNI */ #endif /* OS2 */ #endif /* UNICODE */ #ifndef OS2 #ifdef CKOUNI #undef CKOUNI #endif /* CKOUNI */ #endif /* OS2 */ #ifndef NOLATIN2 /* If they didn't say "no Latin-2" */ #ifndef LATIN2 /* Then if LATIN2 isn't already */ #define LATIN2 /* defined, define it. */ #endif /* LATIN2 */ #endif /* NOLATIN2 */ #ifdef NOCYRILLIC /* (spelling variant...) */ #ifndef NOCYRIL #define NOCYRIL #endif /* NOCYRIL */ #endif /* NOCYRILLIC */ #ifndef NOCYRIL /* If they didn't say "no Cyrillic" */ #ifndef CYRILLIC /* Then if CYRILLIC isn't already */ #define CYRILLIC /* defined, define it. */ #endif /* CYRILLIC */ #endif /* NOCYRIL */ #ifndef NOHEBREW /* If they didn't say "no Hebrew" */ #ifndef HEBREW /* Then if HEBREW isn't already */ #define HEBREW /* defined, define it. */ #endif /* HEBREW */ #endif /* NOHEBREW */ #ifndef NOGREEK /* If not no Greek */ #ifndef GREEK /* then if GREEK isn't already */ #define GREEK /* defined, define it. */ #endif /* GREEK */ #endif /* NOGREEK */ #ifndef NOKANJI /* If not no Kanji */ #ifndef KANJI /* then if KANJI isn't already */ #define KANJI /* defined, define it. */ #endif /* KANJI */ #endif /* NOKANJI */ /* File ckcxla.h -- Character-set-related definitions, system independent */ /* Codes for Kermit Transfer Syntax Level (obsolete) */ #define TS_L0 0 /* Level 0 (Transparent) */ #define TS_L1 1 /* Level 1 (one standard character set) */ #define TS_L2 2 /* Level 2 (multiple character sets in same file) */ #define UNK 63 /* Symbol to use for unknown character (63 = ?) */ /* Codes for the base alphabet of a given character set. These are assigned in roughly ISO 8859 order. (Each is assumed to include ASCII/Roman.) */ #define AL_UNIV 0 /* Universal (like ISO 10646) */ #define AL_ROMAN 1 /* Roman (Latin) alphabet */ #define AL_CYRIL 2 /* Cyrillic alphabet */ #define AL_ARABIC 3 /* Arabic */ #define AL_GREEK 4 /* Greek */ #define AL_HEBREW 5 /* Hebrew */ #define AL_KANA 6 /* Japanese Katakana */ #define AL_JAPAN 7 /* Japanese Katakana+Kanji ideograms */ #define AL_HAN 8 /* Chinese/Japanese/Korean ideograms */ #define AL_INDIA 9 /* Indian scripts (ISCII) */ #define AL_VIET 10 /* Vietnamese (VISCII) */ /* Add more here... */ #define AL_UNK 999 /* Unknown (transparent) */ /* Codes for languages */ /* NOTE: It would perhaps be better to use ISO 639-1988 2-letter "Codes for Representation of Names of Languages" here, shown in the comments below. */ #define L_ASCII 0 /* EN ASCII, English */ #define L_USASCII 0 /* EN ASCII, English */ #define L_DUTCH 1 /* NL Dutch */ #define L_FINNISH 2 /* FI Finnish */ #define L_FRENCH 3 /* FR French */ #define L_GERMAN 4 /* DE German */ #define L_HUNGARIAN 5 /* HU Hungarian */ #define L_ITALIAN 6 /* IT Italian */ #define L_NORWEGIAN 7 /* NO Norwegian */ #define L_PORTUGUESE 8 /* PT Portuguese */ #define L_SPANISH 9 /* ES Spanish */ #define L_SWEDISH 10 /* SV Swedish */ #define L_SWISS 11 /* RM Swiss (Rhaeto-Romance) */ #define L_DANISH 12 /* DA Danish */ #define L_ICELANDIC 13 /* IS Icelandic */ #define L_RUSSIAN 14 /* RU Russian */ #define L_JAPANESE 15 /* JA Japanese */ #define L_HEBREW 16 /* IW Hebrew */ #define L_GREEK 17 /* Greek */ #define MAXLANG 17 /* Number of languages */ /* File character-sets are defined in the system-specific ck?xla.h file, except for the following ones, which must be available to all versions: */ #define FC_TRANSP 254 /* Transparent */ #define FC_UNDEF 255 /* Undefined */ /* Designators for Kermit's transfer character sets. These are all standard sets, or based on them. Symbols must be unique in the first 8 characters, because some C preprocessors have this limit. */ /* LIST1 */ #define TC_TRANSP 0 /* Transparent, no character translation */ #define TC_USASCII 1 /* ISO 646 IRV / US 7-bit ASCII */ #define TC_1LATIN 2 /* ISO 8859-1, Latin Alphabet 1 */ #define TC_2LATIN 3 /* ISO 8859-2, Latin Alphabet 2 */ #define TC_CYRILL 4 /* ISO 8859-5, Latin/Cyrillic */ #define TC_JEUC 5 /* Japanese EUC = JIS 0201+0202+0208 */ #define TC_HEBREW 6 /* ISO 8859-8, Latin/Hebrew */ #define TC_GREEK 7 /* ISO 8859-7, Latin/Greek */ #define TC_9LATIN 8 /* ISO 8859-15 Latin Alphabet 9 (with Euro) */ #define TC_UCS2 9 /* ISO 10646 / Unicode UCS-2 */ #define TC_UTF8 10 /* ISO 10646 / Unicode UTF-8 */ #define MAXTCSETS 10 /* Highest Transfer Character Set Number */ #ifdef COMMENT /* Not used and probably won't be due to ISO-10646 / Unicode. */ #define TC_3LATIN 11 /* ISO 8859-3, Latin-3 */ #define TC_4LATIN 12 /* ISO 8859-4, Latin-4 */ #define TC_5LATIN 13 /* ISO 8859-9, Latin-5 */ #define TC_ARABIC 14 /* ISO-8859-6, Latin/Arabic */ #define TC_JIS208 15 /* Japanese JIS X 0208 multibyte set */ #define TC_CHINES 16 /* Chinese Standard GB 2312-80 */ #define TC_KOREAN 17 /* Korean KS C 5601-1987 */ #define TC_ISCII 18 /* Indian standard code for ii... */ #define TC_VSCII 19 /* Vietnam standard code for ii... */ /* etc... */ #endif /* COMMENT */ /* Structure for character-set information */ struct csinfo { char *name; /* Descriptive name of character set */ int size; /* Size (e.g. 128, 256, 16384) */ int code; /* Like TC_1LATIN, etc. */ char *designator; /* Designator, like I2/100 = Latin-1 */ int alphabet; /* Base alphabet */ char *keyword; /* Keyword for this character-set */ }; /* Structure for language information */ struct langinfo { int id; /* Language ID code (L_whatever) */ int fc; /* File character set to use */ int tc; /* Transfer character set to use */ char *description; /* Description of language */ }; /* Now take in the system-specific definitions */ #ifdef UNIX #include "ckuxla.h" #endif /* UNIX */ #ifdef OSK /* OS-9 */ #include "ckuxla.h" #endif /* OS-9 */ #ifdef VMS /* VAX/VMS */ #include "ckuxla.h" #endif /* VMS */ #ifdef GEMDOS /* Atari ST */ #include "ckuxla.h" #endif /* GEMDOS */ #ifdef MAC /* Macintosh */ #include "ckmxla.h" #endif /* MAC */ #ifdef OS2 /* OS/2 */ #include "ckuxla.h" /* Uses big UNIX version */ #endif /* OS2 */ #ifdef AMIGA /* Commodore Amiga */ #include "ckuxla.h" #endif /* AMIGA */ #ifdef datageneral /* Data General MV AOS/VS */ #include "ckuxla.h" #endif /* datageneral */ #ifdef STRATUS /* Stratus Computer, Inc. VOS */ #include "ckuxla.h" #endif /* STRATUS */ #ifdef UNICODE #include "ckcuni.h" /* Unicode */ #endif /* UNICODE */ #ifdef KANJI #define UNKSJIS 0x817f _PROTOTYP(USHORT eu_to_sj, (USHORT) ); /* EUC-JP to Shift-JIS */ _PROTOTYP(USHORT sj_to_eu, (USHORT) ); /* Shift-JIS to EUC-JP */ _PROTOTYP( int xkanjf, (void) ); _PROTOTYP( int xkanji, (int, int (*)(char)) ); _PROTOTYP( int xkanjz, (int (*)(char) ) ); _PROTOTYP( int zkanjf, (void) ); _PROTOTYP( int zkanji, (int (*)(void)) ); /* Kanji function prototypes */ _PROTOTYP( int zkanjz, (void) ); _PROTOTYP(VOID j7init, ( void ) ); /* Initialize JIS-7 parser */ _PROTOTYP(int getj7, ( void ) ); /* Get next JIS-7 character */ #endif /* KANJI */ #ifndef MAC #ifndef NOLOCAL _PROTOTYP( int cs_size, (int) ); _PROTOTYP( int cs_is_std, (int) ); _PROTOTYP( int cs_is_nrc, (int) ); _PROTOTYP( VOID setremcharset, (int, int) ); _PROTOTYP( VOID setlclcharset, (int) ); #endif /* NOLOCAL */ #endif /* MAC */ _PROTOTYP(VOID setxlatype, (int, int)); #endif /* NOCSETS */ #endif /* CKCXLA_H */ /* End of ckcxla.h */ ckuat2.h0000644000015300001460000003011411266111372011222 0ustar fdckermit/* C K U A T 2 . H -- Kerberos headers for C-Kermit Copyright (C) 1985, 2009, Trustees of Columbia University in the City of New York. All rights reserved. See the C-Kermit COPYING.TXT file or the copyright text in the ckcmai.c module for disclaimer and permissions. Author: Kerberos IV and V intergration. Jeffrey E Altman (jaltman@secure-endpoints.com) Secure Endpoints Inc., New York City */ /* * Based on a concatenation of all necessary include files distributed with * the Kerberos 5 NT Alpha 2 Telnet package from MIT. */ #ifndef KRB5_TELNET_H #define KRB5_TELNET_H /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)encrypt.h 8.1 (Berkeley) 6/4/93 */ /* * Copyright (C) 1990 by the Massachusetts Institute of Technology * * Export of this software from the United States of America may * require a specific license from the United States Government. * It is the responsibility of any person or organization contemplating * export to obtain such a license before exporting. * * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and * distribute this software and its documentation for any purpose and * without fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright notice and * this permission notice appear in supporting documentation, and that * the name of M.I.T. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior * permission. M.I.T. makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. */ #ifdef CK_ENCRYPTION #ifndef __ENCRYPTION__ #define __ENCRYPTION__ #define DIR_DECRYPT 1 #define DIR_ENCRYPT 2 #ifndef CK_DES_C #ifndef NOBLOCKDEF typedef unsigned char Block[8]; #endif /* NOBLOCKDEF */ typedef unsigned char *BlockT; #ifndef KRB4 /* already defined in kerberosiv/des.h */ typedef struct des_ks_struct { Block _; } Schedule[16]; #else /* KRB4 */ #ifndef OS2 #ifndef NOBLOCKDEF /* already defined in kerberosiv/des.h */ typedef struct des_ks_struct { Block _; } Schedule[16]; #endif /* NOBLOCKDEF */ #endif /* OS2 */ #endif /* KRB4 */ #define VALIDKEY(key) (key[0]|key[1]|key[2]|key[3]|key[4]|key[5]|key[6]|key[7]) #define SAMEKEY(k1, k2) (!memcmp((void *)k1, (void *)k2, sizeof(Block))) #endif /* CK_DES_C */ typedef struct _session_key { short type; int length; unsigned char *data; } Session_Key; #ifdef __STDC__ typedef struct { char *name; int type; void (*output)(unsigned char *, int); int (*input)(int); void (*init)(int); int (*start)(int, int); int (*is)(unsigned char *, int); int (*reply)(unsigned char *, int); int (*session)(Session_Key *, int); int (*keyid)(int, unsigned char *, int *); void (*printsub)(unsigned char *, int, unsigned char *, int); } Encryptions; #if !defined(P) #define P(x) x #endif #else typedef struct { char *name; int type; void (*output)(); int (*input)(); void (*init)(); int (*start)(); int (*is)(); int (*reply)(); int (*session)(); int (*keyid)(); void (*printsub)(); } Encryptions; #if !defined(P) #define P(x) () #endif #endif int encrypt_parse(unsigned char *, int); #ifdef DEBUG int printsub(char, unsigned char *, size_t); #endif #define SK_GENERIC 0 /* Just a string of bits */ #define SK_DES 1 /* Matched Kerberos v5 ENCTYPE_DES */ void encrypt_init P((kstream,int)); Encryptions *findencryption P((int)); void encrypt_send_support P((void)); void encrypt_auto P((int)); void decrypt_auto P((int)); int encrypt_is P((unsigned char *, int)); int encrypt_reply P((unsigned char *, int)); void encrypt_start_input P((int)); int encrypt_session_key P((Session_Key *, int)); int encrypt_dont_support P((int)); void encrypt_end_input P((void)); void encrypt_start_output P((int)); void encrypt_end_output P((void)); void encrypt_send_request_start P((void)); void encrypt_send_request_end P((void)); void encrypt_send_end P((void)); void encrypt_wait P((void)); int encrypt_is_encrypting P((void)); void encrypt_send_support P((void)); int encrypt_send_keyid P((int, unsigned char *, int, int)); int encrypt_cmd P((int, char **)); void encrypt_display P((void)); #ifdef CK_KERBEROS void krbdes_encrypt P((unsigned char *, int)); int krbdes_decrypt P((int)); int krbdes_is P((unsigned char *, int)); int krbdes_reply P((unsigned char *, int)); void krbdes_init P((int)); int krbdes_start P((int, int)); void krbdes_session P((Session_Key *, int)); void krbdes_printsub P((unsigned char *, int, unsigned char *, int)); #endif /* CK_KERBEROS */ void cfb64_encrypt P((unsigned char *, int)); int cfb64_decrypt P((int)); void cfb64_init P((int)); int cfb64_start P((int, int)); int cfb64_is P((unsigned char *, int)); int cfb64_reply P((unsigned char *, int)); int cfb64_session P((Session_Key *, int)); int cfb64_keyid P((int, unsigned char *, int *)); void cfb64_printsub P((unsigned char *, int, unsigned char *, int)); void ofb64_encrypt P((unsigned char *, int)); int ofb64_decrypt P((int)); void ofb64_init P((int)); int ofb64_start P((int, int)); int ofb64_is P((unsigned char *, int)); int ofb64_reply P((unsigned char *, int)); int ofb64_session P((Session_Key *, int)); int ofb64_keyid P((int, unsigned char *, int *)); void ofb64_printsub P((unsigned char *, int, unsigned char *, int)); void des3_cfb64_encrypt P((unsigned char *, int)); int des3_cfb64_decrypt P((int)); void des3_cfb64_init P((int)); int des3_cfb64_start P((int, int)); int des3_cfb64_is P((unsigned char *, int)); int des3_cfb64_reply P((unsigned char *, int)); int des3_cfb64_session P((Session_Key *, int)); int des3_cfb64_keyid P((int, unsigned char *, int *)); void des3_cfb64_printsub P((unsigned char *, int, unsigned char *, int)); void des3_ofb64_encrypt P((unsigned char *, int)); int des3_ofb64_decrypt P((int)); void des3_ofb64_init P((int)); int des3_ofb64_start P((int, int)); int des3_ofb64_is P((unsigned char *, int)); int des3_ofb64_reply P((unsigned char *, int)); int des3_ofb64_session P((Session_Key *, int)); int des3_ofb64_keyid P((int, unsigned char *, int *)); void des3_ofb64_printsub P((unsigned char *, int, unsigned char *, int)); #ifdef CAST_ENCRYPTION void cast_cfb64_encrypt P((unsigned char *, int)); int cast_cfb64_decrypt P((int)); void cast_cfb64_init P((int)); int cast_cfb64_start P((int, int)); int cast_cfb64_is P((unsigned char *, int)); int cast_cfb64_reply P((unsigned char *, int)); int cast_cfb64_session P((Session_Key *, int)); int cast_cfb64_keyid P((int, unsigned char *, int *)); void cast_cfb64_printsub P((unsigned char *, int, unsigned char *, int)); void cast_ofb64_encrypt P((unsigned char *, int)); int cast_ofb64_decrypt P((int)); void cast_ofb64_init P((int)); int cast_ofb64_start P((int, int)); int cast_ofb64_is P((unsigned char *, int)); int cast_ofb64_reply P((unsigned char *, int)); int cast_ofb64_session P((Session_Key *, int)); int cast_ofb64_keyid P((int, unsigned char *, int *)); void cast_ofb64_printsub P((unsigned char *, int, unsigned char *, int)); void castexp_cfb64_encrypt P((unsigned char *, int)); int castexp_cfb64_decrypt P((int)); void castexp_cfb64_init P((int)); int castexp_cfb64_start P((int, int)); int castexp_cfb64_is P((unsigned char *, int)); int castexp_cfb64_reply P((unsigned char *, int)); int castexp_cfb64_session P((Session_Key *, int)); int castexp_cfb64_keyid P((int, unsigned char *, int *)); void castexp_cfb64_printsub P((unsigned char *, int, unsigned char *, int)); void castexp_ofb64_encrypt P((unsigned char *, int)); int castexp_ofb64_decrypt P((int)); void castexp_ofb64_init P((int)); int castexp_ofb64_start P((int, int)); int castexp_ofb64_is P((unsigned char *, int)); int castexp_ofb64_reply P((unsigned char *, int)); int castexp_ofb64_session P((Session_Key *, int)); int castexp_ofb64_keyid P((int, unsigned char *, int *)); void castexp_ofb64_printsub P((unsigned char *, int, unsigned char *, int)); #endif /* CAST_ENCRYPTION */ /* int des_string_to_key P((char *, Block)); */ #ifdef DEBUG extern int encrypt_debug_mode; #endif int decrypt_ks_hack(unsigned char *, int); #endif /* __ENCRYPTION__ */ #endif /* ENCRYPTION */ #ifdef CRYPT_DLL struct _crypt_dll_init { int version; /* Version 1 variables */ int (*p_ttol)(char *,int); int (*p_dodebug)(int,char *,char *,CK_OFF_T); int (*p_dohexdump)(char *,char *,int); void (*p_tn_debug)(char *); int (*p_vscrnprintf)(char *, ...); /* Version 2 variables */ void * p_k5_context; /* Version 3 variables */ void (*p_install_funcs)(char *,void *); /* Version 5 variables */ unsigned long (*p_reqtelmutex)(unsigned long); unsigned long (*p_reltelmutex)(void); }; #endif /* CRYPT_DLL */ /* per Kerberos v5 protocol spec */ #ifndef ENCTYPE_NULL #define ENCTYPE_NULL 0x0000 #endif #ifndef ENCTYPE_DES_CBC_CRC #define ENCTYPE_DES_CBC_CRC 0x0001 /* DES cbc mode with CRC-32 */ #endif #ifndef ENCTYPE_DES_CBC_MD4 #define ENCTYPE_DES_CBC_MD4 0x0002 /* DES cbc mode with RSA-MD4 */ #endif #ifndef ENCTYPE_DES_CBC_MD5 #define ENCTYPE_DES_CBC_MD5 0x0003 /* DES cbc mode with RSA-MD5 */ #endif #ifndef ENCTYPE_DES_CBC_RAW #define ENCTYPE_DES_CBC_RAW 0x0004 /* DES cbc mode raw */ #endif /* XXX deprecated? */ #ifndef ENCTYPE_DES3_CBC_SHA #define ENCTYPE_DES3_CBC_SHA 0x0005 /* DES-3 cbc mode with NIST-SHA */ #endif #ifndef ENCTYPE_DES3_CBC_RAW #define ENCTYPE_DES3_CBC_RAW 0x0006 /* DES-3 cbc mode raw */ #endif #ifndef ENCTYPE_DES_HMAC_SHA1 #define ENCTYPE_DES_HMAC_SHA1 0x0008 #endif #ifndef ENCTYPE_DES3_CBC_SHA1 #define ENCTYPE_DES3_CBC_SHA1 0x0010 #endif #ifndef ENCTYPE_AES128_CTS_HMAC_SHA1_96 #define ENCTYPE_AES128_CTS_HMAC_SHA1_96 0x0011 #endif #ifndef ENCTYPE_AES256_CTS_HMAC_SHA1_96 #define ENCTYPE_AES256_CTS_HMAC_SHA1_96 0x0012 #endif #ifndef ENCTYPE_ARCFOUR_HMAC #define ENCTYPE_ARCFOUR_HMAC 0x0017 #endif #ifndef ENCTYPE_ARCFOUR_HMAC_EXP #define ENCTYPE_ARCFOUR_HMAC_EXP 0x0018 #endif #ifndef ENCTYPE_LOCAL_RC4_MD4 #define ENCTYPE_LOCAL_RC4_MD4 0xFFFFFF80 #endif #ifndef ENCTYPE_UNKNOWN #define ENCTYPE_UNKNOWN 0x01ff #endif /* local crud */ /* marc's DES-3 with 32-bit length */ #ifndef ENCTYPE_LOCAL_DES3_HMAC_SHA1 #define ENCTYPE_LOCAL_DES3_HMAC_SHA1 0x7007 #endif #endif /* KRB5_TELNET_H */ ckuath.c0000644000015300001460000143711311575444136011330 0ustar fdckermitchar *ckathv = "Authentication, 9.0.235, 16 Mar 2010"; /* C K U A T H . C -- Authentication for C-Kermit Copyright (C) 1999, 2010, Trustees of Columbia University in the City of New York. All rights reserved. See the C-Kermit COPYING.TXT file or the copyright text in the ckcmai.c module for disclaimer and permissions. Author: Jeffrey E Altman (jaltman@secure-endpoints.com) Secure Endpoints Inc., New York City */ /* * Additional copyrights included with affected code. */ #ifdef HEIMDAL /* Turned off User to User support Turned off KDESTROY support Turned off KLIST support Turned off krb5_prompter() support Turned off ticket validation Turned off ticket renewal Turned off alternative cache support in k5_get_ccache() Remaining link problems: ckuath.o: In function `ck_krb5_initTGT': ckuath.o(.text+0x50c2): undefined reference to `krb5_string_to_deltat' ckuath.o(.text+0x516d): undefined reference to `krb5_string_to_deltat' ckuath.o(.text+0x51ef): undefined reference to `krb5_string_to_deltat' */ #endif /* HEIMDAL */ /* * Implements Kerberos 4/5, SRP, SSL, NTLM authentication and START_TLS */ #include "ckcsym.h" #include "ckcdeb.h" #ifdef CK_SECURITY #define CKUATH_C #include "ckcker.h" #include "ckuusr.h" #include "ckucmd.h" /* For struct keytab */ #include "ckcnet.h" #include "ckctel.h" char szUserNameRequested[UIDBUFLEN+1]; /* for incoming connections */ char szUserNameAuthenticated[UIDBUFLEN+1];/* for incoming connections */ char szHostName[UIDBUFLEN+1]; char szUserName[UIDBUFLEN+1]; static char szIP[16]; static int validUser = AUTH_REJECT; /* User starts out invalid */ int authentication_version = AUTHTYPE_NULL; int accept_complete = 0; #ifdef CK_AUTHENTICATION #ifdef CK_SSL #ifdef KRB5 #define TLS_VERIFY #endif /* KRB5 */ #endif /* CK_SSL */ #ifdef CK_DES #ifdef CK_SSL #ifndef LIBDES #define LIBDES #endif /* LIBDES */ #endif /* CK_SSL */ #endif /* CK_DES */ #ifdef CRYPT_DLL #ifndef LIBDES #define LIBDES #endif /* LIBDES */ #ifdef OS2 #ifdef NT #include #else /* NT */ #define INCL_DOSMODULEMGR #include #endif /* NT */ #endif /* OS2 */ #endif /* CRYPT_DLL */ #ifdef NT #define KRB5_AUTOCONF__ #define NTLM #endif /* NT */ #ifdef CK_KERBEROS #define KINIT #ifndef HEIMDAL #define KLIST #define KDESTROY #endif /* HEIMDAL */ #define CHECKADDRS #else /* CK_KERBEROS */ #ifdef KRB4 #undef KRB4 #endif /* KRB4 */ #ifdef KRB5 #undef KRB5 #endif /* KRB5 */ #ifdef KRB524 #undef KRB524 #endif /* KRB524 */ #endif /* CK_KERBEROS */ #include #include #include #include #include #include #ifndef malloc #ifndef VMS #ifndef FREEBSD4 #ifndef OpenBSD #ifdef MACOSX #include #else /* MACOSX */ #include #endif /* MACOSX */ #endif /* OpenBSD */ #endif /* FREEBSD4 */ #endif /* VMS */ #endif /* malloc */ #ifdef OS2 #include #endif /* OS2 */ #ifdef KRB5 #ifdef HEIMDAL #ifdef printf #define saveprintf printf #undef printf #endif /* printf */ #include "krb5.h" #include "com_err.h" #ifdef saveprintf #define printf saveprintf #endif /* saveprintf */ #else /* HEIMDAL */ #include "krb5.h" #include "profile.h" #include "com_err.h" #ifdef KRB5_GET_INIT_CREDS_OPT_TKT_LIFE #define KRB5_HAVE_GET_INIT_CREDS #else #define krb5_free_unparsed_name(con,val) krb5_xfree((char *)(val)) #endif #ifndef KRB5_HAVE_GET_INIT_CREDS #define krb5_free_data_contents(c,v) krb5_xfree((char *)(v)->data) #endif #endif /* HEIMDAL */ #ifdef HAVE_PWD_H #include #endif #endif /* KRB5 */ #ifdef KRB4 #define des_cblock Block #define const_des_cblock const Block #define des_key_schedule Schedule #ifdef KRB524 #ifdef NT #define _WINDOWS #endif /* NT */ #include "kerberosIV/krb.h" #ifndef OS2 #ifdef KRB524_CONV #include "krb524.h" #endif /* KRB524_CONV */ _PROTOTYP(const char * krb_get_err_text_entry, (int)); #endif /* OS2 */ #else /* KRB524 */ #ifdef SOLARIS #ifndef sun /* for some reason the Makefile entries for the Solaris systems have -Usun */ #define sun #endif /* sun */ #endif /* SOLARIS */ #include "krb.h" #define krb_get_err_text_entry krb_get_err_text #endif /* KRB524 */ #else /* KRB4 */ #ifdef CK_SSL #define des_cblock Block #ifdef COMMENT #define const_des_cblock const Block #endif /* COMMENT */ #define des_key_schedule Schedule #endif /* CK_SSL */ #endif /* KRB4 */ #include "ckuath.h" #ifdef CK_KERBEROS #ifndef KRB5 #define NOBLOCKDEF #else /* KRB5 */ #ifdef KRB524 #define NOBLOCKDEF #endif /* KRB524 */ #endif /* KRB5 */ #endif /* CK_KERBEROS */ #include "ckuat2.h" #ifdef CK_SSL #ifdef LIBDES #ifdef OPENSSL_097 #ifdef CK_DES #define OPENSSL_ENABLE_OLD_DES_SUPPORT #include #endif /* CK_DES */ #endif /* OPENSSL_097 */ #ifndef HEADER_DES_H #define HEADER_DES_H #endif /* HEADER_DES_H */ #endif /* LIBDES */ #include "ck_ssl.h" extern int ssl_finished_messages; #endif /* SSL */ #define PWD_SZ 128 #ifndef LIBDES #ifdef UNIX #define des_set_random_generator_seed(x) des_init_random_number_generator(x) #endif /* UNIX */ #else /* LIBDES */ #define des_fixup_key_parity des_set_odd_parity #endif /* LIBDES */ #ifdef OS2 #ifdef CK_ENCRYPTION #define MAP_DES #endif /* CK_ENCRYPTION */ #ifdef KRB4 #define MAP_KRB4 #endif /* KRB4 */ #ifdef SRPDLL #define MAP_SRP #endif /* SRPDLL */ #ifdef KRB5 #define MAP_KRB5 #endif /* KRB5 */ #ifdef CRYPT_DLL #define MAP_CRYPT #endif /* CRYPT_DLL */ #define MAP_NTLM #include "ckoath.h" #include "ckosyn.h" #endif /* OS2 */ /* * Globals */ int auth_type_user[AUTHTYPLSTSZ] = {AUTHTYPE_AUTO, AUTHTYPE_NULL}; int auth_how=0; int auth_crypt=0; int auth_fwd=0; /* These are state completion variables */ static int mutual_complete = 0; #ifdef KRB4 #ifdef OS2 static LEASH_CREDENTIALS cred; #else /* OS2 */ static CREDENTIALS cred; #endif /* OS2 */ static KTEXT_ST k4_auth; static char k4_name[ANAME_SZ]; static AUTH_DAT k4_adat = { 0 }; static MSG_DAT k4_msg_data; #ifdef CK_ENCRYPTION static Block k4_session_key = { 0 }; static Schedule k4_sched; static Block k4_challenge = { 0 }; #ifdef MIT_CURRENT static krb5_keyblock k4_krbkey; #endif /* MIT_CURRENT */ #endif /* ENCRYPTION */ #define KRB4_SERVICE_NAME "rcmd" _PROTOTYP(static int k4_auth_send,(VOID)); _PROTOTYP(static int k4_auth_reply,(unsigned char *, int)); _PROTOTYP(static int k4_auth_is,(unsigned char *, int)); #endif /* KRB4 */ #ifdef KRB5 static krb5_data k5_auth; static krb5_auth_context auth_context; static krb5_keyblock *k5_session_key = NULL; static krb5_ticket *k5_ticket = NULL; #ifndef KRB5_SERVICE_NAME #define KRB5_SERVICE_NAME "host" #ifdef MACOSX #define MIT_CURRENT 1 #define decode_krb5_ticket krb5_decode_ticket #define krb5_read_message ck_krb5_read_message #define krb5_write_message ck_krb5_write_message #endif /* MACOSX */ #endif _PROTOTYP(static int k5_auth_send,(int,int,int)); _PROTOTYP(static int k5_auth_reply,(int, unsigned char *, int)); _PROTOTYP(static int k5_auth_is,(int,unsigned char *, int)); _PROTOTYP(static int SendK5AuthSB,(int, void *, int)); #ifdef TLS_VERIFY static int krb5_tls_verified = 0; #endif /* TLS_VERIFY */ #endif /* KRB5 */ #ifdef GSSAPI_KRB5 #include #include #include static gss_ctx_id_t gcontext; #define GSS_BUFSIZ 4096 static gss_buffer_desc gss_send_tok, gss_recv_tok, *gss_token_ptr; static char gss_stbuf[GSS_BUFSIZ]; static gss_name_t gss_target_name; static struct gss_channel_bindings_struct gss_chan; _PROTOTYP(static int gssk5_auth_send,(int,int,int)); _PROTOTYP(static int gssk5_auth_reply,(int, unsigned char *, int)); _PROTOTYP(static int gssk5_auth_is,(int,unsigned char *, int)); _PROTOTYP(static int SendGSSK5AuthSB,(int, void *, int)); #endif /* GSSAPI_KRB5 */ #ifdef CK_SRP #ifdef PRE_SRP_1_7_3 _PROTOTYP(static int srp_reply,(int, unsigned char *, int)); _PROTOTYP(static int srp_is,(int, unsigned char *, int)); #else /* PRE_SRP_1_7_3 */ _PROTOTYP(static int new_srp_reply,(int, unsigned char *, int)); _PROTOTYP(static int new_srp_is,(int, unsigned char *, int)); #endif /* PRE_SRP_1_7_3 */ #endif /* SRP */ #ifdef CK_ENCRYPTION int encrypt_flag = 1; #endif #ifdef FORWARD int forward_flag = 0; /* forward tickets? */ int forwardable_flag = 1; /* get forwardable tickets to forward? */ int forwarded_tickets = 0; /* were tickets forwarded? */ #endif static unsigned char str_data[4096] = { IAC, SB, TELOPT_AUTHENTICATION, 0, AUTHTYPE_KERBEROS_V5, }; #define AUTHTMPBL 2048 static char strTmp[AUTHTMPBL+1]; static char szLocalHostName[UIDBUFLEN+1]; static kstream g_kstream=NULL; #ifdef KRB5 krb5_context k5_context=NULL; static krb5_creds * ret_cred=NULL; static krb5_context telnet_context=NULL; static char * telnet_krb5_realm = NULL; static krb5_principal fwd_server = NULL; #endif /* KRB5 */ #ifdef CK_SRP #ifdef PRE_SRP_1_4_4 #ifndef PRE_SRP_1_4_5 #define PRE_SRP_1_4_5 #endif /* PRE_SRP_1_4_5 */ #endif /* PRE_SRP_1_4_5 */ #ifdef PRE_SRP_1_4_5 #ifndef PRE_SRP_1_7_3 #define PRE_SRP_1_7_3 #endif /* PRE_SRP_1_7_3 */ #endif /* PRE_SRP_1_4_5 */ #include #include #include static struct t_server * ts = NULL; static struct t_client * tc = NULL; #ifdef PRE_SRP_1_4_4 static struct t_pw * tpw = NULL; static struct t_conf * tconf = NULL; #endif /* PRE_SRP_1_4_4 */ #ifndef PRE_SRP_1_7_3 #ifndef STDC_HEADERS #define STDC_HEADERS 1 #endif /* STDC_HEADERS */ #include static SRP * s_srp = NULL; static cstr * s_key = NULL; static SRP * c_srp = NULL; static cstr * c_key = NULL; #endif /* PRE_SRP_1_7_3 */ static int srp_waitresp = 0; /* Flag to indicate readiness for response */ static char srp_passwd[PWD_SZ]; #endif /* CK_SRP */ #ifdef CK_KERBEROS #ifdef RLOGCODE #define OPTS_FORWARD_CREDS 0x00000020 #define OPTS_FORWARDABLE_CREDS 0x00000010 #define KCMD_KEYUSAGE 1026 #define RLOG_BUFSIZ 5120 static int rlog_encrypt = 0; char des_inbuf[2*RLOG_BUFSIZ]; /* needs to be > largest read size */ char des_outpkt[2*RLOG_BUFSIZ+4]; /* needs to be > largest write size */ #ifdef KRB5 krb5_data desinbuf,desoutbuf; krb5_encrypt_block eblock; /* eblock for encrypt/decrypt */ static krb5_data encivec_i[2], encivec_o[2]; enum krb5_kcmd_proto { /* Old protocol: DES encryption only. No subkeys. No protection for cleartext length. No ivec supplied. OOB hacks used for rlogin. Checksum may be omitted at connection startup. */ KCMD_OLD_PROTOCOL = 1, /* New protocol: Any encryption scheme. Client-generated subkey required. Prepend cleartext-length to cleartext data (but don't include it in count). Starting ivec defined, chained. In-band signalling. Checksum required. */ KCMD_NEW_PROTOCOL, /* Hack: Get credentials, and use the old protocol iff the session key type is single-DES. */ KCMD_PROTOCOL_COMPAT_HACK, KCMD_UNKNOWN_PROTOCOL }; enum krb5_kcmd_proto krb5_rlog_ver = KCMD_PROTOCOL_COMPAT_HACK; #endif /* KRB5 */ #endif /* RLOGCODE */ static char storage[65536]; /* storage for the decryption */ static int nstored = 0; static char *store_ptr = storage; extern char * krb5_d_principal; /* Default principal */ extern char * krb5_d_instance; /* Default instance */ extern char * krb5_d_realm; /* Default realm */ extern char * krb5_d_cc; /* Default credentials cache */ extern char * krb5_d_srv; /* Default service name */ extern int krb5_d_lifetime; /* Default lifetime */ extern int krb5_d_forwardable; extern int krb5_d_proxiable; extern int krb5_d_renewable; extern int krb5_autoget; extern int krb5_checkaddrs; extern int krb5_d_getk4; extern int krb5_d_no_addresses; extern char * k5_keytab; extern int krb5_errno; extern char * krb5_errmsg; extern char * krb4_d_principal; /* Default principal */ extern char * krb4_d_realm; /* Default realm */ extern char * krb4_d_srv; /* Default service name */ extern int krb4_d_lifetime; /* Default lifetime */ extern int krb4_d_preauth; extern char * krb4_d_instance; extern int krb4_autoget; extern int krb4_checkaddrs; extern char * k4_keytab; extern int krb4_errno; extern char * krb4_errmsg; #endif /* CK_KERBEROS */ extern char tn_msg[], hexbuf[]; /* from ckcnet.c */ extern CHAR pwbuf[]; extern int pwflg, pwcrypt; extern int deblog, debses, tn_deb; extern int sstelnet, inserver; #ifdef CK_LOGIN extern int ckxanon; #endif /* CK_LOGIN */ extern int tn_auth_how; extern int tn_auth_enc; #ifdef CK_ENCRYPTION extern int cx_type; #endif /* CK_ENCRYPTION */ extern int quiet, ttyfd, ttnproto; int ck_gssapi_is_installed() { #ifdef KRB5 #ifdef OS2 return(hGSSAPI != NULL); #else /* OS2 */ return(1); #endif /* OS2 */ #else /* KRB5 */ return(0); #endif /* KRB5 */ } int ck_krb5_is_installed() { #ifdef KRB5 #ifdef OS2 return(hKRB5_32 != NULL); #else /* OS2 */ return(1); #endif /* OS2 */ #else /* KRB5 */ return(0); #endif /* KRB5 */ } int ck_krb5_is_installed_as_server() { #ifdef KRB5 #ifdef HEIMDAL krb5_error_code ret; krb5_keytab kt; krb5_kt_cursor cursor; ret = krb5_kt_default(k5_context, &kt); if ( ret ) { krb5_kt_close(k5_context, kt); return(0); } else { krb5_kt_end_seq_get(k5_context, kt, &cursor); krb5_kt_close(k5_context, kt); return(1); } #else /* HEIMDAL */ #ifndef COMMENT char ktname[CKMAXPATH]=""; if ( k5_keytab ) { ckstrncpy(ktname,k5_keytab,CKMAXPATH); } else { krb5_error_code code; if ( k5_context == NULL) if (krb5_init_context(&k5_context)) return(0); code = krb5_kt_default_name(k5_context,ktname,CKMAXPATH); debug(F101,"krb5_kt_default_name","",code); if ( code ) { /* We can't check the existence of the file since we can't */ /* determine the file name. So we return TRUE and let */ /* Krb5 be offered to the user even though it may fail later */ return(1); } } if ( !strncmp("FILE:",ktname,5) ) { if ( zchki(&ktname[5]) > 0 ) return(1); else return(0); } else { if (ktname[0]) return(1); else return(0); } #else /* COMMENT */ krb5_error_code krb5rc = KRB5KRB_ERR_GENERIC; krb5_context krb5context = NULL; krb5_ccache krb5ccdef = NULL; krb5_creds krb5creds, *krb5credsp = NULL; int rc = 0; if ( !ck_krb5_is_installed() ) return(0); memset((char *)&krb5creds, 0, sizeof(krb5creds)); if ((krb5rc = krb5_init_context(&krb5context)) != 0) goto err; if ((krb5rc = krb5_sname_to_principal(krb5context, szHostName, krb5_d_srv ? krb5_d_srv : KRB5_SERVICE_NAME, KRB5_NT_SRV_HST, &krb5creds.server)) != 0) goto err; if ((krb5rc = krb5_cc_default(krb5context, &krb5ccdef)) != 0) goto err; if ((krb5rc = krb5_cc_get_principal(krb5context, krb5ccdef, &krb5creds.client)) != 0) goto err; if ((krb5rc = krb5_get_credentials(krb5context, 0, krb5ccdef, &krb5creds, &krb5credsp)) != 0) goto err; rc = 1; err: if (krb5creds.client) krb5_free_principal(krb5context, krb5creds.client); if (krb5creds.server) krb5_free_principal(krb5context, krb5creds.server); if (krb5context) krb5_free_context(krb5context); return(rc); #endif /* COMMENT */ #endif /* HEIMDAL */ #else /* KRB5 */ return(0); #endif /* KRB5 */ } int ck_krb4_is_installed() { #ifdef KRB4 #ifdef OS2 return(hKRB4_32 != NULL); #else /* OS2 */ return(1); #endif /* OS2 */ #else /* KRB4 */ return(0); #endif /* KRB4 */ } int ck_krb4_is_installed_as_server() { if ( !ck_krb4_is_installed() ) return(0); #ifdef KRB4 if ( !k4_keytab ) { #ifdef NT char name[CKMAXPATH]=""; DWORD len = CKMAXPATH; len = GetWindowsDirectory(name,len); if ( len > 0 ) ckstrncat(name,"/srvtab",CKMAXPATH); if ( name[0] ) makestr(&k4_keytab,name); #else /* NT */ makestr(&k4_keytab,"/etc/srvtab"); #endif /* NT */ } if ( !k4_keytab ) return(0); if ( zchki(k4_keytab) > 0 ) return(1); #ifdef KRB524 else if (ck_krb5_is_installed_as_server()) return(1); #endif /* KRB524 */ else return(0); #endif /* KRB4 */ } int ck_srp_is_installed_as_server() { #ifdef CK_SRP #ifdef SRPDLL if ( hSRP == NULL ) return(0); #endif /* SRPDLL */ #ifdef COMMENT /* This is the new API as of 1.7.4. However, all it does is allocate a data structure. It can never fail. */ { SRP * s_srp = SRP_new(SRP_RFC2945_server_method()); if ( s_srp ) { SRP_free(s_srp); s_srp = NULL; return(1); } return(0); } #else /* COMMENT */ { struct t_pw * tpw = NULL; struct t_conf * tconf = NULL; if((tconf = t_openconf(NULL)) == NULL) return(0); if((tpw = t_openpw(NULL)) == NULL) { t_closeconf(tconf); return(0); } t_closeconf(tconf); t_closepw(tpw); return(1); } #endif /* COMMENT */ #else /* SRP */ return(0); #endif /* SRP */ } int ck_srp_is_installed() { #ifdef CK_SRP #ifdef SRPDLL if ( hSRP == NULL ) return(0); #endif /* SRPDLL */ return(1); #else /* CK_SRP */ return(0); #endif /* CK_SRP */ } int ck_krypto_is_installed() { #ifdef CK_SRP #ifdef OS2 if ( hLIBKRYPTO == NULL ) return(0); #endif /* OS2 */ return(1); #else /* CK_SRP */ return(0); #endif /* CK_SRP */ } int ck_crypt_is_installed() { #ifdef CK_ENCRYPTION #ifdef CRYPT_DLL return(hCRYPT != NULL); #else /* CRYPT_DLL */ return(1); #endif /* CRYPT_DLL */ #else /* ENCRYPTION */ return(0); #endif /* ENCRYPTION */ } int ck_ntlm_is_installed() { #ifdef NT return(hSSPI != NULL); #else /* NT */ return(0); #endif /* NT */ } int ck_tn_auth_valid() { return(validUser); } /* C K _ K R B _ A U T H _ I N _ P R O G R E S S * * Is an authentication negotiation still in progress? * */ int #ifdef CK_ANSIC ck_tn_auth_in_progress(void) #else ck_tn_auth_in_progress() #endif { switch (authentication_version) { case AUTHTYPE_AUTO: return(1); case AUTHTYPE_NULL: return(0); #ifdef KRB4 case AUTHTYPE_KERBEROS_V4: if (!accept_complete) { debug(F100,"ck_auth_in_progress() Kerberos 4 !accept_complete", "",0); return(1); } else if ((auth_how & AUTH_HOW_MASK) && !mutual_complete) { debug(F100,"ck_auth_in_progress() Kerberos 4 !mutual_complete", "",0); return(1); } else return(0); #endif /* KRB4 */ #ifdef KRB5 case AUTHTYPE_KERBEROS_V5: if (!accept_complete) { debug(F100,"ck_auth_in_progress() Kerberos 5 !accept_complete", "",0); return(1); } else if ((auth_how & AUTH_HOW_MASK) && !mutual_complete) { debug(F100,"ck_auth_in_progress() Kerberos 5 !mutual_complete", "",0); return(1); } else return(0); #ifdef GSSAPI_K5 case AUTHTYPE_GSSAPI_KRB5: if (!accept_complete) { debug(F100, "ck_auth_in_progress() GSSAPI Kerberos 5 !accept_complete", "", 0 ); return(1); } else if ((auth_how & AUTH_HOW_MASK) && !mutual_complete) { debug(F100, "ck_auth_in_progress() GSSAPI Kerberos 5 !mutual_complete", "", 0 ); return(1); } else return(0); break; #endif /* GSSAPI_K5 */ #endif /* KRB5 */ #ifdef CK_SRP case AUTHTYPE_SRP: if (!accept_complete || srp_waitresp) return(1); else return(0); #endif /* CK_SRP */ #ifdef NTLM case AUTHTYPE_NTLM: if (!accept_complete) { debug(F100,"ck_auth_in_progress() NTLM !accept_complete", "",0); return(1); } else return(0); #endif /* NTLM */ case AUTHTYPE_SSL: if (!accept_complete) { debug(F100,"ck_auth_in_progress() SSL !accept_complete", "",0); return(1); } else return(0); default: return(0); } return(0); } /* C K _ K R B _ T N _ A U T H _ R E Q U E S T * * Builds a Telnet Authentication Send Negotiation providing the * list of supported authentication methods. To be used only * when accepting incoming connections as only the server (DO) side of the * Telnet negotiation is allowed to send an AUTH SEND. * * Returns: 0 on success and -1 on failure */ static unsigned char str_request[64] = { IAC, SB, TELOPT_AUTHENTICATION, TELQUAL_SEND }; #ifdef GSSAPI_K5 static int ck_tn_auth_request_gsskrb5(int i) { if (ck_gssapi_is_installed() && ck_krb5_is_installed_as_server()) { if ( (tn_auth_how == TN_AUTH_HOW_ANY || tn_auth_how == TN_AUTH_HOW_MUTUAL) && (tn_auth_enc == TN_AUTH_ENC_ANY || tn_auth_enc == TN_AUTH_ENC_EXCH) ) { str_request[i++] = AUTHTYPE_KERBEROS_V5; str_request[i] = AUTH_CLIENT_TO_SERVER | AUTH_HOW_MUTUAL; str_request[i] |= AUTH_ENCRYPT_AFTER_EXCHANGE; if ( deblog || tn_deb || debses ) ckstrncat(tn_msg, "KERBEROS_V5 CLIENT_TO_SERVER|MUTUAL|ENCRYPT_AFTER_EXCHANGE ", TN_MSG_LEN); i++; } } } #endif /* GSSAPI_K5 */ #ifdef KRB5 static int ck_tn_auth_request_krb5(int i) { if (ck_krb5_is_installed_as_server()) { #ifdef CK_SSL if ( ck_ssleay_is_installed() && (tls_active_flag || ssl_active_flag) && ssl_finished_messages ) { #ifdef USE_INI_CRED_FWD if ( forward_flag && (tn_auth_how == TN_AUTH_HOW_ANY || tn_auth_how == TN_AUTH_HOW_MUTUAL) && (tn_auth_enc == TN_AUTH_ENC_ANY || tn_auth_enc == TN_AUTH_ENC_TELOPT) ) { str_request[i++] = AUTHTYPE_KERBEROS_V5; str_request[i] = AUTH_CLIENT_TO_SERVER | AUTH_HOW_MUTUAL; str_request[i] |= AUTH_ENCRYPT_START_TLS; str_request[i] |= INI_CRED_FWD_ON; if ( deblog || tn_deb || debses ) ckstrncat(tn_msg, "KERBEROS_V5 CLIENT_TO_SERVER|MUTUAL|ENCRYPT_START_TLS|INI_CRED_FWD_ON ", TN_MSG_LEN); i++; } #endif /* USE_INI_CRED_FWD */ if ( (tn_auth_how == TN_AUTH_HOW_ANY || tn_auth_how == TN_AUTH_HOW_MUTUAL) && (tn_auth_enc == TN_AUTH_ENC_ANY || tn_auth_enc == TN_AUTH_ENC_TELOPT) ) { str_request[i++] = AUTHTYPE_KERBEROS_V5; str_request[i] = AUTH_CLIENT_TO_SERVER | AUTH_HOW_MUTUAL; str_request[i] |= AUTH_ENCRYPT_START_TLS; if ( deblog || tn_deb || debses ) ckstrncat(tn_msg, "KERBEROS_V5 CLIENT_TO_SERVER|MUTUAL|ENCRYPT_START_TLS ", TN_MSG_LEN); i++; } if ( tn_auth_how == TN_AUTH_HOW_ANY || tn_auth_how == TN_AUTH_HOW_ONE_WAY ) { str_request[i++] = AUTHTYPE_KERBEROS_V5; str_request[i] = AUTH_CLIENT_TO_SERVER | AUTH_HOW_ONE_WAY; str_request[i] |= AUTH_ENCRYPT_START_TLS; if ( deblog || tn_deb || debses ) ckstrncat(tn_msg, "KERBEROS_V5 CLIENT_TO_SERVER|ONE_WAY|ENCRYPT_START_TLS ", TN_MSG_LEN); i++; } } #ifdef CK_ENCRYPTION else { #endif /* CK_ENCRYPTION */ #endif /* CK_SSL */ #ifdef CK_ENCRYPTION #ifdef USE_INI_CRED_FWD if ( forward_flag && TELOPT_ME_MODE(TELOPT_ENCRYPTION) != TN_NG_RF && TELOPT_U_MODE(TELOPT_ENCRYPTION) != TN_NG_RF && (tn_auth_how == TN_AUTH_HOW_ANY || tn_auth_how == TN_AUTH_HOW_MUTUAL) && (tn_auth_enc == TN_AUTH_ENC_ANY || tn_auth_enc == TN_AUTH_ENC_TELOPT) ) { str_request[i++] = AUTHTYPE_KERBEROS_V5; str_request[i] = AUTH_CLIENT_TO_SERVER | AUTH_HOW_MUTUAL; str_request[i] |= AUTH_ENCRYPT_USING_TELOPT; str_request[i] |= INI_CRED_FWD_ON; if ( deblog || tn_deb || debses ) ckstrncat(tn_msg, "KERBEROS_V5 CLIENT_TO_SERVER|MUTUAL|ENCRYPT_USING_TELOPT|INI_CRED_FWD_ON ", TN_MSG_LEN); i++; } #endif /* USE_INI_CRED_FWD */ if ( TELOPT_ME_MODE(TELOPT_ENCRYPTION) != TN_NG_RF && TELOPT_U_MODE(TELOPT_ENCRYPTION) != TN_NG_RF && (tn_auth_how == TN_AUTH_HOW_ANY || tn_auth_how == TN_AUTH_HOW_MUTUAL) && (tn_auth_enc == TN_AUTH_ENC_ANY || tn_auth_enc == TN_AUTH_ENC_TELOPT) ) { str_request[i++] = AUTHTYPE_KERBEROS_V5; str_request[i] = AUTH_CLIENT_TO_SERVER | AUTH_HOW_MUTUAL; str_request[i] |= AUTH_ENCRYPT_USING_TELOPT; if ( deblog || tn_deb || debses ) ckstrncat(tn_msg, "KERBEROS_V5 CLIENT_TO_SERVER|MUTUAL|ENCRYPT_USING_TELOPT ", TN_MSG_LEN); i++; } #ifdef CK_SSL } #endif /* CK_SSL */ #endif /* CK_ENCRYPTION */ if ( TELOPT_ME_MODE(TELOPT_ENCRYPTION) != TN_NG_MU && TELOPT_U_MODE(TELOPT_ENCRYPTION) != TN_NG_MU && (tn_auth_enc == TN_AUTH_ENC_ANY || tn_auth_enc == TN_AUTH_ENC_NONE) #ifdef CK_SSL && !(ck_ssleay_is_installed() && (tls_active_flag || ssl_active_flag) && tls_is_anon(0)) #endif /* CK_SSL */ ) { #ifdef CK_ENCRYPTION /* Can't perform mutual authentication without encryption */ if ( tn_auth_how == TN_AUTH_HOW_ANY || tn_auth_how == TN_AUTH_HOW_MUTUAL ) { str_request[i++] = AUTHTYPE_KERBEROS_V5; str_request[i] = AUTH_CLIENT_TO_SERVER | AUTH_HOW_MUTUAL; str_request[i] |= AUTH_ENCRYPT_OFF; if ( deblog || tn_deb || debses ) ckstrncat(tn_msg,"KERBEROS_V5 CLIENT_TO_SERVER|MUTUAL ", TN_MSG_LEN); i++; } #endif /* CK_ENCRYPTION */ if ( tn_auth_how == TN_AUTH_HOW_ANY || tn_auth_how == TN_AUTH_HOW_ONE_WAY ) { str_request[i++] = AUTHTYPE_KERBEROS_V5; str_request[i] = AUTH_CLIENT_TO_SERVER | AUTH_HOW_ONE_WAY; str_request[i] |= AUTH_ENCRYPT_OFF; if ( deblog || tn_deb || debses ) ckstrncat(tn_msg,"KERBEROS_V5 CLIENT_TO_SERVER|ONE_WAY ", TN_MSG_LEN); i++; } } } return(i); } #endif /* KRB5 */ #ifdef KRB4 static int ck_tn_auth_request_krb4(int i) { if (ck_krb4_is_installed_as_server()) { #ifdef CK_ENCRYPTION if (TELOPT_ME_MODE(TELOPT_ENCRYPTION) != TN_NG_RF && TELOPT_U_MODE(TELOPT_ENCRYPTION) != TN_NG_RF && (tn_auth_how == TN_AUTH_HOW_ANY || tn_auth_how == TN_AUTH_HOW_MUTUAL) && (tn_auth_enc == TN_AUTH_ENC_ANY || tn_auth_enc == TN_AUTH_ENC_TELOPT) ) { str_request[i++] = AUTHTYPE_KERBEROS_V4; str_request[i] = AUTH_CLIENT_TO_SERVER | AUTH_HOW_MUTUAL; str_request[i] |= AUTH_ENCRYPT_USING_TELOPT; if ( deblog || tn_deb || debses ) ckstrncat(tn_msg,"KERBEROS_V4 CLIENT_TO_SERVER|MUTUAL|ENCRYPT ", TN_MSG_LEN); i++; } #endif /* CK_ENCRYPTION */ if (TELOPT_ME_MODE(TELOPT_ENCRYPTION) != TN_NG_MU && TELOPT_U_MODE(TELOPT_ENCRYPTION) != TN_NG_MU && (tn_auth_enc == TN_AUTH_ENC_ANY || tn_auth_enc == TN_AUTH_ENC_NONE) ) { #ifdef CK_ENCRYPTION /* Can't perform mutual authentication without encryption */ if ( tn_auth_how == TN_AUTH_HOW_ANY || tn_auth_how == TN_AUTH_HOW_MUTUAL ) { str_request[i++] = AUTHTYPE_KERBEROS_V4; str_request[i] = AUTH_CLIENT_TO_SERVER | AUTH_HOW_MUTUAL; str_request[i] |= AUTH_ENCRYPT_OFF; if ( deblog || tn_deb || debses ) ckstrncat(tn_msg,"KERBEROS_V4 CLIENT_TO_SERVER|MUTUAL ", TN_MSG_LEN); i++; } #endif /* CK_ENCRYPTION */ if ( tn_auth_how == TN_AUTH_HOW_ANY || tn_auth_how == TN_AUTH_HOW_ONE_WAY ) { str_request[i++] = AUTHTYPE_KERBEROS_V4; str_request[i] = AUTH_CLIENT_TO_SERVER | AUTH_HOW_ONE_WAY; str_request[i] |= AUTH_ENCRYPT_OFF; if ( deblog || tn_deb || debses ) ckstrncat(tn_msg,"KERBEROS_V4 CLIENT_TO_SERVER|ONE_WAY ", TN_MSG_LEN); i++; } } } return(i); } #endif /* KRB4 */ #ifdef CK_SRP static int ck_tn_auth_request_srp(int i) { if (ck_srp_is_installed_as_server()) { #ifndef PRE_SRP_1_4_5 /* Dont' do this yet. SRP when it uses the ENCRYPT_USING_TELOPT */ /* flag it must perform a checksum of the auth-type-pair but there */ /* is no mechansim to do that yet. */ #ifdef CK_SSL if ( ck_ssleay_is_installed() && (tls_active_flag || ssl_active_flag) && ssl_finished_messages && (tn_auth_how == TN_AUTH_HOW_ANY || tn_auth_how == TN_AUTH_HOW_ONE_WAY) && (tn_auth_enc == TN_AUTH_ENC_ANY || tn_auth_enc == TN_AUTH_ENC_TELOPT)) { str_request[i++] = AUTHTYPE_SRP; str_request[i] = AUTH_CLIENT_TO_SERVER | AUTH_HOW_ONE_WAY; str_request[i] |= AUTH_ENCRYPT_START_TLS; if ( deblog || tn_deb || debses ) ckstrncat(tn_msg, "SRP CLIENT_TO_SERVER|ONE_WAY|ENCRYPT_START_TLS ", TN_MSG_LEN); i++; } #ifdef CK_ENCRYPTION else { #endif /* CK_ENCRYPTION */ #endif /* CK_SSL */ #ifdef CK_ENCRYPTION if (TELOPT_ME_MODE(TELOPT_ENCRYPTION) != TN_NG_RF && TELOPT_U_MODE(TELOPT_ENCRYPTION) != TN_NG_RF && (tn_auth_how == TN_AUTH_HOW_ANY || tn_auth_how == TN_AUTH_HOW_ONE_WAY) && (tn_auth_enc == TN_AUTH_ENC_ANY || tn_auth_enc == TN_AUTH_ENC_TELOPT) ) { str_request[i++] = AUTHTYPE_SRP; str_request[i] = AUTH_CLIENT_TO_SERVER | AUTH_HOW_ONE_WAY; str_request[i] |= AUTH_ENCRYPT_USING_TELOPT; if ( deblog || tn_deb || debses ) ckstrncat(tn_msg, "SRP CLIENT_TO_SERVER|ONE_WAY|ENCRYPT_USING_TELOPT ", TN_MSG_LEN); i++; } #ifdef CK_SSL } #endif /* CK_SSL */ #endif /* CK_ENCRYPTION */ #endif /* PRE_SRP_1_4_5 */ if (TELOPT_ME_MODE(TELOPT_ENCRYPTION) != TN_NG_MU && TELOPT_U_MODE(TELOPT_ENCRYPTION) != TN_NG_MU && (tn_auth_how == TN_AUTH_HOW_ANY || tn_auth_how == TN_AUTH_HOW_MUTUAL) && (tn_auth_enc == TN_AUTH_ENC_ANY || tn_auth_enc == TN_AUTH_ENC_NONE) #ifdef CK_SSL && !(ck_ssleay_is_installed() && (tls_active_flag || ssl_active_flag) && tls_is_anon(0)) #endif /* CK_SSL */ ) { str_request[i++] = AUTHTYPE_SRP; str_request[i] = AUTH_CLIENT_TO_SERVER | AUTH_HOW_ONE_WAY; str_request[i] |= AUTH_ENCRYPT_OFF; if ( deblog || tn_deb || debses ) ckstrncat(tn_msg,"SRP CLIENT_TO_SERVER|ONE_WAY ", TN_MSG_LEN); i++; } } return(i); } #endif /* CK_SRP */ #ifdef CK_SSL static int ck_tn_auth_request_ssl(int i) { if (ck_ssleay_is_installed() && !tls_active_flag && !ssl_active_flag && ssl_initialized ) { if (TELOPT_ME_MODE(TELOPT_ENCRYPTION) != TN_NG_MU && TELOPT_U_MODE(TELOPT_ENCRYPTION) != TN_NG_MU && (tn_auth_how == TN_AUTH_HOW_ANY || tn_auth_how == TN_AUTH_HOW_ONE_WAY) && (tn_auth_enc == TN_AUTH_ENC_ANY || tn_auth_enc == TN_AUTH_ENC_NONE) ) { str_request[i++] = AUTHTYPE_SSL; str_request[i] = AUTH_CLIENT_TO_SERVER | AUTH_HOW_ONE_WAY; str_request[i] |= AUTH_ENCRYPT_OFF; if ( deblog || tn_deb || debses ) ckstrncat(tn_msg,"SSL CLIENT_TO_SERVER|ONE_WAY ", TN_MSG_LEN); i++; } } return(i); } #endif /* CK_SSL */ #ifdef NTLM static int ck_tn_auth_request_ntlm(int i) { /* Microsoft's Telnet client won't perform authentication if */ /* NTLM is not first. */ if ( ck_ntlm_is_valid(1) ) { if (TELOPT_ME_MODE(TELOPT_ENCRYPTION) != TN_NG_MU && TELOPT_U_MODE(TELOPT_ENCRYPTION) != TN_NG_MU && (tn_auth_how == TN_AUTH_HOW_ANY || tn_auth_how == TN_AUTH_HOW_ONE_WAY) && (tn_auth_enc == TN_AUTH_ENC_ANY || tn_auth_enc == TN_AUTH_ENC_NONE) ) { str_request[i++] = AUTHTYPE_NTLM; str_request[i] = AUTH_CLIENT_TO_SERVER | AUTH_HOW_ONE_WAY; str_request[i] |= AUTH_ENCRYPT_OFF; if ( deblog || tn_deb || debses ) ckstrncat(tn_msg,"NTLM CLIENT_TO_SERVER|ONE_WAY ", TN_MSG_LEN); i++; } } return(i); } #endif /* NTLM */ int #ifdef CK_ANSIC ck_tn_auth_request(void) #else ck_tn_auth_request() #endif { int i = 4, rc = -1; #ifdef CK_SSL if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) { return(0); } #endif /* CK_SSL */ if ( deblog || tn_deb || debses ) strcpy(tn_msg,"TELNET SENT SB AUTHENTICATION SEND "); /* Create a list of acceptable Authentication types to send to */ /* the client and let it choose find one that we support */ /* For those authentication methods that support Encryption or */ /* Credentials Forwarding we must send all of the appropriate */ /* combinations based upon the state of */ /* TELOPT_x_MODE(TELOPT_ENCRYPTION) and forward_flag. */ if ( auth_type_user[0] == AUTHTYPE_AUTO ) { #ifdef GSSAPI_K5 i = ck_tn_auth_request_gsskrb5(i); #endif /* GSSAPI_K5 */ #ifdef KRB5 i = ck_tn_auth_request_krb5(i); #endif /* KRB5 */ #ifdef KRB4 i = ck_tn_auth_request_krb4(i); #endif /* KRB4 */ #ifdef CK_SRP i = ck_tn_auth_request_srp(i); #endif /* SRP */ #ifdef CK_SSL i = ck_tn_auth_request_ssl(i); #endif /* CK_SSL */ #ifdef NTLM i = ck_tn_auth_request_ntlm(i); #endif /* NTLM */ } else { int j; for ( j=0; jencrypt && encrypt_is_encrypting()) { debug(F111,"ck_tn_encrypting","encrypting", g_kstream->encrypt_type); return(g_kstream->encrypt_type); } #endif /* CK_ENCRYPTION */ debug(F110,"ck_tn_encrypting","not encrypting",0); return(0); } /* C K _ K R B _ D E C R Y P T I N G * Returns 1 if we are decrypting and 0 if we are not */ int #ifdef CK_ANSIC ck_tn_decrypting(VOID) #else ck_tn_decrypting() #endif /* CK_ANSIC */ { #ifdef CK_ENCRYPTION if ( g_kstream == NULL ) return(0); if ( g_kstream->decrypt && encrypt_is_decrypting()) { debug(F111,"ck_tn_decrypting","decrypting", g_kstream->decrypt_type); return(g_kstream->decrypt_type); } #endif /* CK_ENCRYPTION */ debug(F110,"ck_tn_decrypting","not decrypting",0); return(0); } /* C K _ K R B _ A U T H E N T I C A T E D * Returns the authentication type: AUTHTYPE_NULL, AUTHTYPE_KERBEROS4, * or AUTHTYPE_KERBEROS5, AUTHTYPE_SRP, ... (see ckctel.h) */ int #ifdef CK_ANSIC ck_tn_authenticated(VOID) #else ck_tn_authenticated() #endif { return(authentication_version); } /* C K _ K R B _ E N C R Y P T * encrypts n characters in s if we are encrypting */ VOID #ifdef CK_ANSIC ck_tn_encrypt( char * s, int n ) #else ck_tn_encrypt( s,n ) char * s; int n; #endif { #ifdef CK_ENCRYPTION struct kstream_data_block i; if (g_kstream->encrypt && encrypt_is_encrypting()) { #ifdef DEBUG ckhexdump("from plaintext", s, n); #endif i.ptr = s; i.length = n; g_kstream->encrypt(&i, NULL); #ifdef DEBUG ckhexdump("to cyphertext", s, n); #endif } else debug(F101,"ck_tn_encrypt not encrypting","",n); #endif /* ENCRYPTION */ } /* C K _ K R B _ D E C R Y P T * decrypts n characters in s if we are decrypting */ VOID #ifdef CK_ANSIC ck_tn_decrypt( char * s, int n ) #else ck_tn_decrypt( s,n ) char * s; int n; #endif { #ifdef CK_ENCRYPTION struct kstream_data_block i; if (g_kstream->decrypt && encrypt_is_decrypting()) { #ifdef DEBUG ckhexdump("from cyphertext", s, n); #endif i.ptr = s; i.length = n; g_kstream->decrypt(&i, NULL); #ifdef DEBUG ckhexdump("to plaintext", s, n); #endif } else debug(F101,"ck_tn_decrypt not decrypting","",n); #endif /* ENCRYPTION */ } /* S E N D K 5 A U T H S B * Send a Kerberos 5 Authentication Subnegotiation to host and * output appropriate Telnet Debug messages * * type - Sub Negotiation type * data - ptr to buffer containing data * len - len of buffer if not NUL terminated * * returns number of characters sent or error value */ static int #ifdef CK_ANSIC SendK5AuthSB(int type, void *data, int len) #else SendK5AuthSB(type,data,len) int type; void *data; int len; #endif { int rc; unsigned char *p = str_data + 3; unsigned char *cd = (unsigned char *)data; extern int sstelnet; #ifdef CK_SSL if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) { if (ttchk() < 0) return(0); else return(1); } #endif /* CK_SSL */ if ( type < 0 || type > 7 ) /* Check for invalid values */ return(0); if (!cd) { cd = (unsigned char *)""; len = 0; } if (len == -1) /* Use strlen() for len */ len = strlen((char *)cd); /* Construct Message */ *p++ = sstelnet ? TELQUAL_REPLY : TELQUAL_IS; *p++ = AUTHTYPE_KERBEROS_V5; *p = AUTH_CLIENT_TO_SERVER; *p |= auth_how; #ifdef CK_ENCRYPTION *p |= auth_crypt; #endif #ifdef USE_INI_CRED_FWD if (auth_fwd) *p |= INI_CRED_FWD_ON; #endif /* USE_INI_CRED_FWD */ p++; *p++ = type; while (len-- > 0) { if ((*p++ = *cd++) == IAC) *p++ = IAC; } *p++ = IAC; *p++ = SE; /* Handle Telnet Debugging Messages */ if (deblog || tn_deb || debses) { int i; int deblen=p-str_data-2; char *s=NULL; int mode = AUTH_CLIENT_TO_SERVER | (auth_how & AUTH_HOW_MASK) | auth_crypt #ifdef USE_INI_CRED_FWD | (auth_fwd?INI_CRED_FWD_ON:INI_CRED_FWD_OFF) #endif /* USE_INI_CRED_FWD */ ; switch (type) { case 0: s = "AUTH"; break; case 1: s = "REJECT"; break; case 2: s = "ACCEPT"; break; case 3: s = "RESPONSE"; break; case 4: s = "FORWARD"; break; case 5: s = "FORWARD_ACCEPT"; break; case 6: s = "FORWARD_REJECT"; break; case 7: s = "TLS_VERIFY"; break; } ckmakxmsg(tn_msg,TN_MSG_LEN, "TELNET SENT SB ", TELOPT(TELOPT_AUTHENTICATION)," ", str_data[3] == TELQUAL_IS ? "IS" : str_data[3] == TELQUAL_REPLY ? "REPLY" : "???"," ", AUTHTYPE_NAME(authentication_version)," ", AUTHMODE_NAME(mode)," ", s," ",NULL); tn_hex((CHAR *)tn_msg,TN_MSG_LEN,&str_data[7],deblen-7); ckstrncat(tn_msg,"IAC SE",TN_MSG_LEN); debug(F100,tn_msg,"",0); if (tn_deb || debses) tn_debug(tn_msg); } /* Send data */ #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif rc = ttol((CHAR *)str_data, p - str_data); #ifdef OS2 ReleaseTelnetMutex(); #endif debug(F111,"SendK5AuthSB","ttol()",rc); return(rc); } /* S E N D K 4 A U T H S B * Send a Kerberos 4 Authentication Subnegotiation to host and * output appropriate Telnet Debug messages * * type - Sub Negotiation type * data - ptr to buffer containing data * len - len of buffer if not NUL terminated * * returns number of characters sent or error value */ static int #ifdef CK_ANSIC SendK4AuthSB(int type, void *data, int len) #else SendK4AuthSB(type,data,len) int type; void *data; int len; #endif { int rc; unsigned char *p = str_data + 3; unsigned char *cd = (unsigned char *)data; extern int sstelnet; int mode = (auth_how & AUTH_HOW_MASK) | auth_crypt; if ( type < 0 || type > 4 ) /* Check for invalid values */ return(0); #ifdef CK_SSL if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) { if (ttchk() < 0) return(0); else return(1); } #endif /* CK_SSL */ if (!cd) { cd = (unsigned char *)""; len = 0; } if (len == -1) /* Use strlen() for len */ len = strlen((char *)cd); /* Construct Message */ *p++ = sstelnet ? TELQUAL_REPLY : TELQUAL_IS; *p++ = AUTHTYPE_KERBEROS_V4; *p = AUTH_CLIENT_TO_SERVER; *p |= mode; p++; *p++ = type; while (len-- > 0) { if ((*p++ = *cd++) == IAC) *p++ = IAC; } *p++ = IAC; *p++ = SE; /* Handle Telnet Debugging Messages */ if (deblog || tn_deb || debses) { int i; int deblen=p-str_data-2; char *s=NULL; switch (type) { case 0: s = "AUTH"; break; case 1: s = "REJECT"; break; case 2: s = "ACCEPT"; break; case 3: s = "CHALLENGE"; break; case 4: s = "RESPONSE"; break; } ckmakxmsg(tn_msg,TN_MSG_LEN,"TELNET SENT SB ", TELOPT(TELOPT_AUTHENTICATION)," ", str_data[3] == TELQUAL_IS ? "IS" : (str_data[3] == TELQUAL_REPLY ? "REPLY" : "???")," ", AUTHTYPE_NAME(authentication_version)," ", AUTHMODE_NAME(mode)," ", s," ",NULL); tn_hex((CHAR *)tn_msg,TN_MSG_LEN,&str_data[7],deblen-7); ckstrncat(tn_msg,"IAC SE",TN_MSG_LEN); debug(F100,tn_msg,"",0); if (tn_deb || debses) tn_debug(tn_msg); } /* Send data */ #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif rc = ttol((CHAR *)str_data, p - str_data); #ifdef OS2 ReleaseTelnetMutex(); #endif debug(F111,"SendK4AuthSB","ttol()",rc); return(rc); } /* S E N D S R P A U T H S B * Send a SRP Authentication Subnegotiation to host and * output appropriate Telnet Debug messages * * type - Sub Negotiation type * data - ptr to buffer containing data * len - len of buffer if not NUL terminated * * returns number of characters sent or error value */ static int #ifdef CK_ANSIC SendSRPAuthSB(int type, void *data, int len) #else SendSRPAuthSB(type,data,len) int type; void *data; int len; #endif { int rc; unsigned char *p = str_data + 3; unsigned char *cd = (unsigned char *)data; extern int sstelnet; /* Check for invalid values */ if ( type != SRP_EXP && type != SRP_RESPONSE && type != SRP_REJECT && type != SRP_ACCEPT && type != SRP_CHALLENGE && type != SRP_PARAMS && type != SRP_AUTH) return(0); if (len == -1) /* Use strlen() for len */ len = strlen((char *)cd); /* Construct Message */ *p++ = sstelnet ? TELQUAL_REPLY : TELQUAL_IS; *p++ = AUTHTYPE_SRP; *p = AUTH_CLIENT_TO_SERVER; *p |= auth_how; #ifdef CK_ENCRYPTION *p |= auth_crypt; #endif p++; *p++ = type; while (len-- > 0) { if ((*p++ = *cd++) == IAC) *p++ = IAC; } *p++ = IAC; *p++ = SE; /* Handle Telnet Debugging Messages */ if (deblog || tn_deb || debses) { int i; int deblen=p-str_data-2; char *s=NULL; int mode = AUTH_CLIENT_TO_SERVER | (auth_how & AUTH_HOW_MASK) | auth_crypt; switch (type) { case 0: s = "AUTH"; break; case 1: s = "REJECT"; break; case 2: s = "ACCEPT"; break; case 3: s = "CHALLENGE"; break; case 4: s = "RESPONSE"; break; case 5: s = "FORWARD"; break; case 6: s = "FORWARD_ACCEPT"; break; case 7: s = "FORWARD_REJECT"; break; case 8: s = "EXP"; break; case 9: s = "PARAMS"; break; } ckmakxmsg(tn_msg,TN_MSG_LEN, "TELNET SENT SB ", TELOPT(TELOPT_AUTHENTICATION)," ", str_data[3] == TELQUAL_REPLY ? "REPLY" : str_data[3] == TELQUAL_IS ? "IS" : "???"," ", AUTHTYPE_NAME(authentication_version)," ", AUTHMODE_NAME(mode)," ", s," ",NULL); tn_hex((CHAR *)tn_msg,TN_MSG_LEN,&str_data[7],deblen-7); ckstrncat(tn_msg,"IAC SE",TN_MSG_LEN); debug(F100,tn_msg,"",0); if (tn_deb || debses) tn_debug(tn_msg); } /* Send data */ #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif rc = ttol((CHAR *)str_data, p - str_data); #ifdef OS2 ReleaseTelnetMutex(); #endif return(rc); } #ifdef CK_ENCRYPTION /* * Function: Enable or disable the encryption process. * * Parameters: * enable - TRUE to enable, FALSE to disable. */ static VOID #ifdef CK_ANSIC auth_encrypt_enable(BOOL enable) #else auth_encrypt_enable(enable) BOOL enable; #endif { encrypt_flag = enable; } #endif /* * Function: Abort the authentication process * * Parameters: */ static VOID #ifdef CK_ANSIC auth_abort(char *errmsg, long r) #else auth_abort(errmsg,r) char *errmsg; long r; #endif { char buf[9]; extern int sstelnet; #ifdef CK_SSL if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) { return; } #endif /* CK_SSL */ debug(F111,"auth_abort",errmsg,r); /* Construct Telnet Debugging messages */ if (deblog || tn_deb || debses) { ckmakxmsg(tn_msg,TN_MSG_LEN, "TELNET SENT SB ",TELOPT(TELOPT_AUTHENTICATION), " IS ",AUTHTYPE_NAME(AUTHTYPE_NULL)," ", AUTHTYPE_NAME(AUTHTYPE_NULL)," IAC SE", NULL,NULL,NULL,NULL,NULL ); debug(F100,tn_msg,"",0); if (tn_deb || debses) tn_debug(tn_msg); } /* Construct the Abort message to send to the host */ /* Basicly we change the authentication type to NULL */ sprintf(buf, "%c%c%c%c%c%c%c%c", IAC, SB, TELOPT_AUTHENTICATION, sstelnet ? TELQUAL_REPLY : TELQUAL_IS, AUTHTYPE_NULL, AUTHTYPE_NULL, IAC, SE); /* safe */ #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif ttol((CHAR *)buf, 8); #ifdef OS2 ReleaseTelnetMutex(); #endif /* If there is an error message, and error number construct */ /* an explanation to display to the user */ if (errmsg != NULL) { ckstrncpy(strTmp, errmsg, AUTHTMPBL); } else strTmp[0] = '\0'; if (r != AUTH_SUCCESS) { ckstrncat(strTmp, "\r\n",AUTHTMPBL); #ifdef KRB4 if ( authentication_version == AUTHTYPE_KERBEROS_V4 ) { ckstrncat(strTmp, (char *)krb_get_err_text_entry(r), AUTHTMPBL); debug(F111,"auth_abort",(char *)krb_get_err_text_entry(r),r); } #endif #ifdef KRB5 if ( authentication_version == AUTHTYPE_KERBEROS_V5 ) { ckstrncat(strTmp, error_message(r),AUTHTMPBL); debug(F111,"auth_abort",error_message(r),r); } #endif } printf("Authentication failed: %s\r\n",strTmp); #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_LI && ckxlogging) { cksyslog(SYSLG_LI, 0, "Telnet authentication failure", (char *) szUserNameRequested, strTmp); } #endif /* CKSYSLOG */ authentication_version = AUTHTYPE_NULL; } /* * Function: Copy data to buffer, doubling IAC character if present. * */ int #ifdef CK_ANSIC copy_for_net(unsigned char *to, unsigned char *from, int c) #else copy_for_net(to,from,c) unsigned char *to; unsigned char *from; int c; #endif { int n; n = c; debug(F111,"copy_for_net","before",n); while (c-- > 0) { if ((*to++ = *from++) == IAC) { n++; *to++ = IAC; } } debug(F111,"copy_for_net","after",n); return n; } #ifdef CK_SSL /* S E N D S S L A U T H S B * Send a SSL Authentication Subnegotiation to host and * output appropriate Telnet Debug messages * * type - Sub Negotiation type * data - ptr to buffer containing data * len - len of buffer if not NUL terminated * * returns number of characters sent or error value */ int #ifdef CK_ANSIC SendSSLAuthSB(int type, void *data, int len) #else SendSSLAuthSB(type,data,len) int type; void *data; int len; #endif { int rc; unsigned char *p = str_data + 3; unsigned char *cd = (unsigned char *)data; extern int sstelnet; /* Check for invalid values */ if ( type != SSL_START && type != SSL_ACCEPT && type != SSL_REJECT) return(0); if (TELOPT_SB(TELOPT_START_TLS).start_tls.me_follows) { if (ttchk() < 0) return(0); else return(1); } if (len == -1) /* Use strlen() for len */ len = strlen((char *)cd); /* Construct Message */ *p++ = sstelnet ? TELQUAL_REPLY : TELQUAL_IS; *p++ = AUTHTYPE_SSL; *p = AUTH_CLIENT_TO_SERVER; *p |= auth_how; #ifdef CK_ENCRYPTION *p |= auth_crypt; #endif p++; *p++ = type; while (len-- > 0) { if ((*p++ = *cd++) == IAC) *p++ = IAC; } *p++ = IAC; *p++ = SE; /* Handle Telnet Debugging Messages */ if (deblog || tn_deb || debses) { int i; int deblen=p-str_data-2; char *s=NULL; int mode = AUTH_CLIENT_TO_SERVER | (auth_how & AUTH_HOW_MASK) | (auth_crypt?AUTH_ENCRYPT_USING_TELOPT:AUTH_ENCRYPT_OFF); switch (type) { case SSL_START: s = "START"; break; case SSL_ACCEPT: s = "ACCEPT"; break; case SSL_REJECT: s = "REJECT"; break; } ckmakxmsg(tn_msg,TN_MSG_LEN, "TELNET SENT SB ", TELOPT(TELOPT_AUTHENTICATION)," ", str_data[3] == TELQUAL_REPLY ? "REPLY" : str_data[3] == TELQUAL_IS ? "IS" : "???"," ", AUTHTYPE_NAME(authentication_version)," ", AUTHMODE_NAME(mode)," ", s," ",NULL); tn_hex((CHAR *)tn_msg,TN_MSG_LEN,&str_data[7],deblen-7); ckstrncat(tn_msg,"IAC SE",TN_MSG_LEN); debug(F100,tn_msg,"",0); if (tn_deb || debses) tn_debug(tn_msg); } /* Send data */ #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif rc = ttol((CHAR *)str_data, p - str_data); #ifdef OS2 ReleaseTelnetMutex(); #endif return(rc); } #endif /* CK_SSL */ int tn_how_ok(int how) { switch ( tn_auth_how ) { case TN_AUTH_HOW_ANY: return(1); case TN_AUTH_HOW_ONE_WAY: return((how & AUTH_HOW_MASK) == AUTH_HOW_ONE_WAY); case TN_AUTH_HOW_MUTUAL: return((how & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL); default: return(0); } } int tn_enc_ok(int enc) { switch ( tn_auth_enc ) { case TN_AUTH_ENC_ANY: if ((enc & AUTH_ENCRYPT_MASK) == AUTH_ENCRYPT_START_TLS && (!ck_ssleay_is_installed() #ifdef CK_SSL || !ssl_finished_messages || !(tls_active_flag || ssl_active_flag) #endif /* CK_SSL */ )) { #ifdef CK_SSL if (!ssl_finished_messages) debug(F100,"tn_enc_ok !ssl_finished_messages","",0); #endif /* CK_SSL */ return(0); } return(1); case TN_AUTH_ENC_NONE: return((enc & AUTH_ENCRYPT_MASK) == AUTH_ENCRYPT_OFF); case TN_AUTH_ENC_TELOPT: return((enc & AUTH_ENCRYPT_MASK) == AUTH_ENCRYPT_USING_TELOPT); case TN_AUTH_ENC_EXCH: return((enc & AUTH_ENCRYPT_MASK) == AUTH_ENCRYPT_AFTER_EXCHANGE); case TN_AUTH_ENC_TLS: return(((enc & AUTH_ENCRYPT_MASK) == AUTH_ENCRYPT_START_TLS) && ck_ssleay_is_installed() #ifdef CK_SSL && ssl_finished_messages && (tls_active_flag || ssl_active_flag) #endif /* CK_SSL */ ); default: return(0); } } static int atok(int at) { int i; if ( auth_type_user[0] == AUTHTYPE_AUTO ) return(1); if ( auth_type_user[0] == AUTHTYPE_NULL ) return(0); for ( i=0; i 512 ? 512 : end_sub; memcpy(send_list,parsedat,send_len); /* Search the list of acceptable Authentication types sent from */ /* the host and find one that we support */ /* For Kerberos authentications, try to determine if we have a */ /* valid TGT, if not skip over the authentication type because */ /* we wouldn't be able to successfully login anyway. Perhaps */ /* there is another supported authentication which we could use */ #ifdef NO_FTP_AUTH /* If the userid is "ftp" or "anonymous" refuse to perform AUTH */ /* for Kerberos or SRP. */ #endif /* NO_FTP_AUTH */ if ( auth_type_user[0] == AUTHTYPE_AUTO ) { for (i = 2; i+1 <= end_sub; i += 2) { #ifdef NTLM if (parsedat[i] == AUTHTYPE_NTLM && ck_ntlm_is_valid(1) && ntlm_auth_send() == 0) { if ((parsedat[i+1] & AUTH_WHO_MASK) == AUTH_CLIENT_TO_SERVER && tn_how_ok(parsedat[i+1]) && tn_enc_ok(parsedat[i+1])) { #ifdef CK_ENCRYPTION /* NTLM does not support Telnet Encryption */ if ((parsedat[i+1] & AUTH_ENCRYPT_MASK)) continue; auth_crypt = parsedat[i+1] & AUTH_ENCRYPT_MASK; #endif /* CK_ENCRYPTION */ TELOPT_ME_MODE(TELOPT_ENCRYPTION) = TN_NG_RF; TELOPT_U_MODE(TELOPT_ENCRYPTION) = TN_NG_RF; authentication_version = AUTHTYPE_NTLM; auth_how = parsedat[i+1] & AUTH_HOW_MASK; break; } } #endif /* NTLM */ #ifdef CK_SSL if ( parsedat[i] == AUTHTYPE_SSL && ssl_initialized && #ifdef SSLDLL ck_ssleay_is_installed() && #endif /* SSLDLL */ !tls_active_flag && !ssl_active_flag #ifndef USE_CERT_CB && tls_load_certs(ssl_ctx,ssl_con,0) #endif /* USE_CERT_CB */ ) { if ((parsedat[i+1] & AUTH_WHO_MASK) == AUTH_CLIENT_TO_SERVER && tn_how_ok(parsedat[i+1]) && tn_enc_ok(parsedat[i+1])) { #ifdef CK_ENCRYPTION /* SSL does not support Telnet Encryption */ if ((parsedat[i+1] & AUTH_ENCRYPT_MASK)) continue; auth_crypt = parsedat[i+1] & AUTH_ENCRYPT_MASK; #endif /* CK_ENCRYPTION */ TELOPT_ME_MODE(TELOPT_ENCRYPTION) = TN_NG_RF; TELOPT_U_MODE(TELOPT_ENCRYPTION) = TN_NG_RF; authentication_version = AUTHTYPE_SSL; auth_how = parsedat[i+1] & AUTH_HOW_MASK; break; } } #endif /* SSL */ #ifdef CK_SRP if ( parsedat[i] == AUTHTYPE_SRP #ifdef SRPDLL && hSRP #endif /* SRPDLL */ #ifdef NO_FTP_AUTH && strcmp("ftp",szUserName) && strcmp("anonymous",szUserName) #endif /* NO_FTP_AUTH */ ) { if ((parsedat[i+1] & AUTH_WHO_MASK) == AUTH_CLIENT_TO_SERVER && tn_how_ok(parsedat[i+1]) && tn_enc_ok(parsedat[i+1])) { #ifdef PRE_SRP_1_4_5 if (parsedat[i+1] & AUTH_ENCRYPT_MASK) /* Do not support ENCRYPT_USING_TELOPT yet. */ continue; #endif /* PRE_SRP_1_4_5 */ if (((parsedat[i+1] & AUTH_ENCRYPT_MASK) == AUTH_ENCRYPT_USING_TELOPT) && (TELOPT_ME_MODE(TELOPT_ENCRYPTION) == TN_NG_RF || TELOPT_U_MODE(TELOPT_ENCRYPTION) == TN_NG_RF)) continue; auth_crypt = parsedat[i+1] & AUTH_ENCRYPT_MASK; #ifdef CK_ENCRYPTION if ( auth_crypt == AUTH_ENCRYPT_USING_TELOPT ) { TELOPT_ME_MODE(TELOPT_ENCRYPTION) = TN_NG_MU; TELOPT_U_MODE(TELOPT_ENCRYPTION) = TN_NG_MU; } #endif /* CK_ENCRYPTION */ #ifdef CK_SSL if ( auth_crypt == AUTH_ENCRYPT_START_TLS && ck_ssleay_is_installed() && (tls_active_flag || ssl_active_flag) ) { TELOPT_ME_MODE(TELOPT_ENCRYPTION) = TN_NG_RF; TELOPT_U_MODE(TELOPT_ENCRYPTION) = TN_NG_RF; } #endif /* CK_SSL */ authentication_version = AUTHTYPE_SRP; auth_how = parsedat[i+1] & AUTH_HOW_MASK; break; } } #endif /* SRP */ #ifdef GSSAPI_KRB5 if (parsedat[i] == AUTHTYPE_GSSAPI_KRB5 && (parsedat[i+1] & AUTH_ENCRYPT_MASK) == AUTH_ENCRYPT_AFTER_EXCHANGE && #ifdef OS2 hGSSAPI && #endif /* OS2 */ #ifdef NO_FTP_AUTH strcmp("ftp",szUserName) && strcmp("anonymous",szUserName) && #endif /* NO_FTP_AUTH */ ck_gssapi_is_installed() && !gssk5_msg) { if ( !gssk5_auth_send(parsedat[i+1] & AUTH_HOW_MASK, parsedat[i+1] & AUTH_ENCRYPT_MASK, parsedat[i+1] & INI_CRED_FWD_MASK) ) { /* If we are auto-getting TGTs, try */ if ( !ck_krb5_is_tgt_valid() ) { printf("Kerberos 5: Ticket Getting Ticket not valid.\r\n"); } gssk5_msg = 1; } else if ((parsedat[i+1] & AUTH_WHO_MASK) == AUTH_CLIENT_TO_SERVER && tn_how_ok(parsedat[i+1]) && tn_enc_ok(parsedat[i+1])) { auth_crypt = parsedat[i+1] & AUTH_ENCRYPT_MASK; #ifdef CK_ENCRYPTION if ( auth_crypt == AUTH_ENCRYPT_AFTER_EXCHANGE ) { TELOPT_ME_MODE(TELOPT_ENCRYPTION) = TN_NG_RF; TELOPT_U_MODE(TELOPT_ENCRYPTION) = TN_NG_RF; } #endif /* CK_ENCRYPTION */ auth_fwd = parsedat[i+1] & INI_CRED_FWD_MASK; authentication_version = AUTHTYPE_GSSAPI_KRB5; auth_how = parsedat[i+1] & AUTH_HOW_MASK; break; } } #endif /* GSSAPI_KRB5 */ #ifdef KRB5 if (parsedat[i] == AUTHTYPE_KERBEROS_V5 && #ifdef OS2 hKRB5_32 && #endif /* OS2 */ #ifdef NO_FTP_AUTH strcmp("ftp",szUserName) && strcmp("anonymous",szUserName) && #endif /* NO_FTP_AUTH */ ck_krb5_is_installed() && !krb5_msg) { /* Without encryption we can't perform mutual authentication */ if ( (parsedat[i+1] & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL && !ck_crypt_is_installed()) continue; /* Skip over entries that request credential forwarding */ /* if we are not forwarding. */ if ((!forward_flag && (parsedat[i+1] & INI_CRED_FWD_MASK)) || (forward_flag && ((parsedat[i+1] & AUTH_HOW_MASK) == AUTH_HOW_ONE_WAY))) continue; if ( !k5_auth_send(parsedat[i+1] & AUTH_HOW_MASK, parsedat[i+1] & AUTH_ENCRYPT_MASK, parsedat[i+1] & INI_CRED_FWD_MASK) ) { /* If we are auto-getting TGTs, try */ if ( !ck_krb5_is_tgt_valid() ) { printf("Kerberos 5: Ticket Getting Ticket not valid.\r\n"); } krb5_msg = 1; } else if ((parsedat[i+1] & AUTH_WHO_MASK) == AUTH_CLIENT_TO_SERVER && tn_how_ok(parsedat[i+1]) && tn_enc_ok(parsedat[i+1])) { if (((parsedat[i+1] & AUTH_ENCRYPT_MASK) == AUTH_ENCRYPT_USING_TELOPT) && (TELOPT_ME_MODE(TELOPT_ENCRYPTION) == TN_NG_RF || TELOPT_U_MODE(TELOPT_ENCRYPTION) == TN_NG_RF)) continue; if (((parsedat[i+1] & AUTH_ENCRYPT_MASK) == AUTH_ENCRYPT_START_TLS) && (!ck_ssleay_is_installed() #ifdef CK_SSL || !(tls_active_flag || ssl_active_flag) #endif /* CK_SSL */ )) continue; auth_crypt = parsedat[i+1] & AUTH_ENCRYPT_MASK; #ifdef CK_ENCRYPTION if ( auth_crypt == AUTH_ENCRYPT_USING_TELOPT ) { TELOPT_ME_MODE(TELOPT_ENCRYPTION) = TN_NG_MU; TELOPT_U_MODE(TELOPT_ENCRYPTION) = TN_NG_MU; } #endif /* CK_ENCRYPTION */ #ifdef CK_SSL if ( auth_crypt == AUTH_ENCRYPT_START_TLS && ck_ssleay_is_installed() && (tls_active_flag || ssl_active_flag) ) { TELOPT_ME_MODE(TELOPT_ENCRYPTION) = TN_NG_RF; TELOPT_U_MODE(TELOPT_ENCRYPTION) = TN_NG_RF; } #endif /* CK_SSL */ auth_fwd = parsedat[i+1] & INI_CRED_FWD_MASK; authentication_version = AUTHTYPE_KERBEROS_V5; auth_how = parsedat[i+1] & AUTH_HOW_MASK; if ( auth_how == AUTH_HOW_ONE_WAY ) { TELOPT_ME_MODE(TELOPT_ENCRYPTION) = TN_NG_RF; TELOPT_U_MODE(TELOPT_ENCRYPTION) = TN_NG_RF; } break; } } #endif /* KRB5 */ #ifdef KRB4 if (parsedat[i] == AUTHTYPE_KERBEROS_V4 && #ifdef OS2 hKRB4_32 && #endif /* OS2 */ #ifdef NO_FTP_AUTH strcmp("ftp",szUserName) && strcmp("anonymous",szUserName) && #endif /* NO_FTP_AUTH */ ck_krb4_is_installed() && !krb4_msg) { int rc = 0; /* Without encryption we can't perform mutual authentication */ if ( (parsedat[i+1] & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL && !ck_crypt_is_installed() ) continue; if ( !k4_auth_send() ) { /* If we are auto-getting TGTs, try */ if ( !ck_krb4_is_tgt_valid() ) { printf("Kerberos 4: Ticket Getting Ticket not valid.\r\n"); } krb4_msg = 1; } else if ((parsedat[i+1] & AUTH_WHO_MASK) == AUTH_CLIENT_TO_SERVER && tn_how_ok(parsedat[i+1]) && tn_enc_ok(parsedat[i+1])) { #ifdef CK_ENCRYPTION if ((parsedat[i+1] & AUTH_ENCRYPT_MASK) && (TELOPT_ME_MODE(TELOPT_ENCRYPTION) == TN_NG_RF || TELOPT_U_MODE(TELOPT_ENCRYPTION) == TN_NG_RF)) continue; auth_crypt = parsedat[i+1] & AUTH_ENCRYPT_MASK; if ( auth_crypt == AUTH_ENCRYPT_USING_TELOPT ) { TELOPT_ME_MODE(TELOPT_ENCRYPTION) = TN_NG_MU; TELOPT_U_MODE(TELOPT_ENCRYPTION) = TN_NG_MU; } #endif /* CK_ENCRYPTION */ authentication_version = AUTHTYPE_KERBEROS_V4; auth_how = parsedat[i+1] & AUTH_HOW_MASK; if ( auth_how == AUTH_HOW_ONE_WAY ) { TELOPT_ME_MODE(TELOPT_ENCRYPTION) = TN_NG_RF; TELOPT_U_MODE(TELOPT_ENCRYPTION) = TN_NG_RF; } break; } } #endif /* KRB4 */ } } else { for (i = 2; i+1 <= end_sub; i += 2) { #ifdef CK_SSL if ( atok(AUTHTYPE_SSL) && parsedat[i] == AUTHTYPE_SSL && #ifdef SSLDLL ck_ssleay_is_installed() && #endif /* SSLDLL */ !tls_active_flag && !ssl_active_flag && ssl_initialized #ifndef USE_CERT_CB && tls_load_certs(ssl_ctx,ssl_con,0) #endif /* USE_CERT_CB */ ) { if ((parsedat[i+1] & AUTH_WHO_MASK) == AUTH_CLIENT_TO_SERVER && tn_how_ok(parsedat[i+1]) && tn_enc_ok(parsedat[i+1])) { #ifdef CK_ENCRYPTION /* SSL does not support Telnet Encryption */ if ((parsedat[i+1] & AUTH_ENCRYPT_MASK)) continue; auth_crypt = parsedat[i+1] & AUTH_ENCRYPT_MASK; #endif /* CK_ENCRYPTION */ TELOPT_ME_MODE(TELOPT_ENCRYPTION) = TN_NG_RF; TELOPT_U_MODE(TELOPT_ENCRYPTION) = TN_NG_RF; authentication_version = AUTHTYPE_SSL; auth_how = parsedat[i+1] & AUTH_HOW_MASK; break; } } #endif /* SSL */ #ifdef CK_SRP if ( atok(AUTHTYPE_SRP) && parsedat[i] == AUTHTYPE_SRP #ifdef SRPDLL && hSRP #endif /* SRPDLL */ #ifdef NO_FTP_AUTH && strcmp("ftp",szUserName) && strcmp("anonymous",szUserName) #endif /* NO_FTP_AUTH */ ) { if ((parsedat[i+1] & AUTH_WHO_MASK) == AUTH_CLIENT_TO_SERVER && tn_how_ok(parsedat[i+1]) && tn_enc_ok(parsedat[i+1])) { #ifdef PRE_SRP_1_4_5 if (parsedat[i+1] & AUTH_ENCRYPT_MASK) /* Do not support ENCRYPT_USING_TELOPT yet. */ continue; #endif /* PRE_SRP_1_4_5 */ if (((parsedat[i+1] & AUTH_ENCRYPT_MASK) == AUTH_ENCRYPT_USING_TELOPT) && (TELOPT_ME_MODE(TELOPT_ENCRYPTION) == TN_NG_RF || TELOPT_U_MODE(TELOPT_ENCRYPTION) == TN_NG_RF)) continue; if (((parsedat[i+1] & AUTH_ENCRYPT_MASK) == AUTH_ENCRYPT_START_TLS) && (!ck_ssleay_is_installed() #ifdef CK_SSL || !(tls_active_flag || ssl_active_flag) #endif /* CK_SSL */ )) continue; auth_crypt = parsedat[i+1] & AUTH_ENCRYPT_MASK; #ifdef CK_ENCRYPTION if ( auth_crypt == AUTH_ENCRYPT_USING_TELOPT ) { TELOPT_ME_MODE(TELOPT_ENCRYPTION) = TN_NG_MU; TELOPT_U_MODE(TELOPT_ENCRYPTION) = TN_NG_MU; } #endif /* CK_ENCRYPTION */ #ifdef CK_SSL if ( auth_crypt == AUTH_ENCRYPT_START_TLS && ck_ssleay_is_installed() && (tls_active_flag || ssl_active_flag) ) { TELOPT_ME_MODE(TELOPT_ENCRYPTION) = TN_NG_RF; TELOPT_U_MODE(TELOPT_ENCRYPTION) = TN_NG_RF; } #endif /* CK_SSL */ authentication_version = AUTHTYPE_SRP; auth_how = parsedat[i+1] & AUTH_HOW_MASK; break; } } #endif /* SRP */ #ifdef GSSAPI_KRB5 if (atok(AUTHTYPE_GSSAPI_KRB5) && parsedat[i] == AUTHTYPE_GSSAPI_KRB5 && (parsedat[i+1] & AUTH_ENCRYPT_MASK) == AUTH_ENCRYPT_AFTER_EXCHANGE && #ifdef OS2 hGSSAPI && #endif /* OS2 */ #ifdef NO_FTP_AUTH strcmp("ftp",szUserName) && strcmp("anonymous",szUserName) && #endif /* NO_FTP_AUTH */ ck_gssapi_is_installed() && !gssk5_msg) { if ( !gssk5_auth_send(parsedat[i+1] & AUTH_HOW_MASK, parsedat[i+1] & AUTH_ENCRYPT_MASK, parsedat[i+1] & INI_CRED_FWD_MASK) ) { /* If we are auto-getting TGTs, try */ if ( !ck_krb5_is_tgt_valid() ) { printf("Kerberos 5: Ticket Getting Ticket not valid.\r\n"); } gssk5_msg = 1; } else if ((parsedat[i+1] & AUTH_WHO_MASK) == AUTH_CLIENT_TO_SERVER && tn_how_ok(parsedat[i+1]) && tn_enc_ok(parsedat[i+1])) { auth_crypt = parsedat[i+1] & AUTH_ENCRYPT_MASK; #ifdef CK_ENCRYPTION if ( auth_crypt == AUTH_ENCRYPT_AFTER_EXCHANGE ) { TELOPT_ME_MODE(TELOPT_ENCRYPTION) = TN_NG_RF; TELOPT_U_MODE(TELOPT_ENCRYPTION) = TN_NG_RF; } #endif /* CK_ENCRYPTION */ auth_fwd = parsedat[i+1] & INI_CRED_FWD_MASK; authentication_version = AUTHTYPE_GSSAPI_KRB5; auth_how = parsedat[i+1] & AUTH_HOW_MASK; break; } } #endif /* GSSAPI_KRB5 */ #ifdef KRB5 if ( atok(AUTHTYPE_KERBEROS_V5) && parsedat[i] == AUTHTYPE_KERBEROS_V5 && #ifdef OS2 hKRB5_32 && #endif /* OS2 */ #ifdef NO_FTP_AUTH strcmp("ftp",szUserName) && strcmp("anonymous",szUserName) && #endif /* NO_FTP_AUTH */ ck_krb5_is_installed() && !krb5_msg) { /* Without encryption we can't perform mutual authentication */ if ( (parsedat[i+1] & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL && !ck_crypt_is_installed()) continue; /* Skip over entries that request credential forwarding */ /* if we are not forwarding. */ if ((!forward_flag && (parsedat[i+1] & INI_CRED_FWD_MASK)) || (forward_flag && ((parsedat[i+1] & AUTH_HOW_MASK) == AUTH_HOW_ONE_WAY))) continue; if ( !k5_auth_send(parsedat[i+1] & AUTH_HOW_MASK, parsedat[i+1] & AUTH_ENCRYPT_MASK, parsedat[i+1] & INI_CRED_FWD_MASK) ) { /* If we are auto-getting TGTs, try */ if ( !ck_krb5_is_tgt_valid() ) { printf( "Kerberos 5: Ticket Getting Ticket not valid.\r\n"); } krb5_msg = 1; } else if ((parsedat[i+1] & AUTH_WHO_MASK) == AUTH_CLIENT_TO_SERVER && tn_how_ok(parsedat[i+1]) && tn_enc_ok(parsedat[i+1])) { if (((parsedat[i+1] & AUTH_ENCRYPT_MASK) == AUTH_ENCRYPT_USING_TELOPT) && (TELOPT_ME_MODE(TELOPT_ENCRYPTION) == TN_NG_RF || TELOPT_U_MODE(TELOPT_ENCRYPTION) == TN_NG_RF)) continue; if (((parsedat[i+1] & AUTH_ENCRYPT_MASK) == AUTH_ENCRYPT_START_TLS) && (!ck_ssleay_is_installed() #ifdef CK_SSL || !(tls_active_flag || ssl_active_flag) #endif /* CK_SSL */ )) continue; auth_crypt = parsedat[i+1] & AUTH_ENCRYPT_MASK; #ifdef CK_ENCRYPTION if (auth_crypt) { TELOPT_ME_MODE(TELOPT_ENCRYPTION) = TN_NG_MU; TELOPT_U_MODE(TELOPT_ENCRYPTION) = TN_NG_MU; } #endif /* CK_ENCRYPTION */ #ifdef CK_SSL if ( auth_crypt == AUTH_ENCRYPT_START_TLS && ck_ssleay_is_installed() && (tls_active_flag || ssl_active_flag) ) { TELOPT_ME_MODE(TELOPT_ENCRYPTION) = TN_NG_RF; TELOPT_U_MODE(TELOPT_ENCRYPTION) = TN_NG_RF; } #endif /* CK_SSL */ authentication_version = AUTHTYPE_KERBEROS_V5; auth_how = parsedat[i+1] & AUTH_HOW_MASK; if ( auth_how == AUTH_HOW_ONE_WAY ) { TELOPT_ME_MODE(TELOPT_ENCRYPTION) = TN_NG_RF; TELOPT_U_MODE(TELOPT_ENCRYPTION) = TN_NG_RF; } break; } } #endif /* KRB5 */ #ifdef KRB4 if ( atok(AUTHTYPE_KERBEROS_V4) && parsedat[i] == AUTHTYPE_KERBEROS_V4 && #ifdef OS2 hKRB4_32 && #endif /* OS2 */ #ifdef NO_FTP_AUTH strcmp("ftp",szUserName) && strcmp("anonymous",szUserName) && #endif /* NO_FTP_AUTH */ ck_krb4_is_installed() && !krb4_msg) { int rc = 0; /* Without encryption we can't perform mutual authentication */ if ( (parsedat[i+1] & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL && !ck_crypt_is_installed()) continue; if ( !k4_auth_send() ) { /* If we are auto-getting TGTs, try */ if ( !ck_krb4_is_tgt_valid() ) { printf("Kerberos 4: Ticket Getting Ticket not valid.\r\n"); } krb4_msg = 1; } else if ((parsedat[i+1] & AUTH_WHO_MASK) == AUTH_CLIENT_TO_SERVER && tn_how_ok(parsedat[i+1]) && tn_enc_ok(parsedat[i+1])) { #ifdef CK_ENCRYPTION if ((parsedat[i+1] & AUTH_ENCRYPT_MASK) && (TELOPT_ME_MODE(TELOPT_ENCRYPTION) == TN_NG_RF || TELOPT_U_MODE(TELOPT_ENCRYPTION) == TN_NG_RF)) continue; auth_crypt = parsedat[i+1] & AUTH_ENCRYPT_MASK; if (auth_crypt) { TELOPT_ME_MODE(TELOPT_ENCRYPTION) = TN_NG_MU; TELOPT_U_MODE(TELOPT_ENCRYPTION) = TN_NG_MU; } #endif /* CK_ENCRYPTION */ authentication_version = AUTHTYPE_KERBEROS_V4; auth_how = parsedat[i+1] & AUTH_HOW_MASK; if ( auth_how == AUTH_HOW_ONE_WAY ) { TELOPT_ME_MODE(TELOPT_ENCRYPTION) = TN_NG_RF; TELOPT_U_MODE(TELOPT_ENCRYPTION) = TN_NG_RF; } break; } } #endif /* KRB4 */ #ifdef NTLM if ( atok(AUTHTYPE_NTLM) && parsedat[i] == AUTHTYPE_NTLM && ck_ntlm_is_valid(1) && ntlm_auth_send() == 0) { if ((parsedat[i+1] & AUTH_WHO_MASK) == AUTH_CLIENT_TO_SERVER && tn_how_ok(parsedat[i+1]) && tn_enc_ok(parsedat[i+1])) { #ifdef CK_ENCRYPTION /* NTLM does not support Telnet Encryption */ if ((parsedat[i+1] & AUTH_ENCRYPT_MASK)) continue; auth_crypt = parsedat[i+1] & AUTH_ENCRYPT_MASK; #endif /* CK_ENCRYPTION */ TELOPT_ME_MODE(TELOPT_ENCRYPTION) = TN_NG_RF; TELOPT_U_MODE(TELOPT_ENCRYPTION) = TN_NG_RF; authentication_version = AUTHTYPE_NTLM; auth_how = parsedat[i+1] & AUTH_HOW_MASK; break; } } #endif /* NTLM */ } } if (auth_how == -1) { /* Did we find one? */ switch ( auth_type_user[0] ) { /* If not, abort the negotiation */ case AUTHTYPE_NULL: auth_abort("User refused to accept any authentication method",0); break; case AUTHTYPE_AUTO: auth_abort("No authentication method available", 0); break; default: { char msg[80]; ckmakmsg(msg,80,AUTHTYPE_NAME(auth_type_user[0]), " could not be negotiated",NULL,NULL ); auth_abort(msg, 0); } } auth_finished(AUTH_REJECT); return AUTH_FAILURE; } printf("Authenticating with %s\r\n", AUTHTYPE_NAME(authentication_version)); /* Send Telnet Auth Name message (if necessary) */ switch ( authentication_version ) { case AUTHTYPE_SRP: case AUTHTYPE_KERBEROS_V4: case AUTHTYPE_KERBEROS_V5: case AUTHTYPE_GSSAPI_KRB5: /* if we do not have a name to login with get one now. */ while ( szUserName[0] == '\0' ) { extern char * tn_pr_uid; int ok = uq_txt(NULL, tn_pr_uid && tn_pr_uid[0] ? tn_pr_uid : "Host Userid: ", 1, NULL, szUserName, 63, NULL,DEFAULT_UQ_TIMEOUT); if ( !ok ) return AUTH_FAILURE; } plen = strlen(szUserName); pname = (unsigned char *) szUserName; /* Construct Telnet Debugging Message */ if (deblog || tn_deb || debses) { ckmakxmsg(tn_msg,TN_MSG_LEN, "TELNET SENT SB ",TELOPT(TELOPT_AUTHENTICATION), " NAME ",(char *)pname," IAC SE",NULL, NULL,NULL,NULL,NULL,NULL,NULL ); debug(F100,tn_msg,"",0); if (tn_deb || debses) tn_debug(tn_msg); } /* Construct and send Authentication Name subnegotiation */ if ( plen < sizeof(buf) - 6 ) { sprintf((char *)buf, "%c%c%c%c", IAC, SB, TELOPT_AUTHENTICATION, TELQUAL_NAME); memcpy(&buf[4], pname, plen); /* safe */ sprintf((char *)&buf[plen + 4], "%c%c", IAC, SE); /* safe */ #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif ttol((CHAR *)buf, plen+6); #ifdef OS2 ReleaseTelnetMutex(); #endif } else { sprintf((char *)buf, "%c%c%c%c%c%c", IAC, SB, TELOPT_AUTHENTICATION, TELQUAL_NAME, IAC, SE); /* safe */ #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif ttol((CHAR *)buf, 6); #ifdef OS2 ReleaseTelnetMutex(); #endif } } /* Construct Authentication Mode subnegotiation message (if necessary) */ switch ( authentication_version ) { case AUTHTYPE_SRP: case AUTHTYPE_KERBEROS_V4: case AUTHTYPE_KERBEROS_V5: case AUTHTYPE_GSSAPI_KRB5: case AUTHTYPE_NTLM: mode = AUTH_CLIENT_TO_SERVER | (auth_how & AUTH_HOW_MASK) | auth_crypt #ifdef USE_INI_CRED_FWD | (((authentication_version == AUTHTYPE_KERBEROS_V5) && auth_fwd)?INI_CRED_FWD_ON:INI_CRED_FWD_OFF) #endif /* USE_INI_CRED_FWD */ ; sprintf((char *)buf, "%c%c%c%c%c%c%c", IAC, SB, TELOPT_AUTHENTICATION, TELQUAL_IS, authentication_version, mode, KRB_AUTH); /* safe */ break; } /* Send initial authentication data */ switch ( authentication_version ) { #ifdef CK_SSL case AUTHTYPE_SSL: SendSSLAuthSB(SSL_START,NULL,0); break; #endif /* SSL */ #ifdef CK_SRP case AUTHTYPE_SRP: sprintf(&buf[7], "%c%c", IAC, SE); /* safe */ if (deblog || tn_deb || debses) { ckmakxmsg(tn_msg,TN_MSG_LEN, "TELNET SENT SB ",TELOPT(TELOPT_AUTHENTICATION), " IS ",AUTHTYPE_NAME(authentication_version), " AUTH ",AUTHMODE_NAME(mode)," IAC SE", NULL,NULL,NULL,NULL,NULL ); debug(F100,tn_msg,"",0); if (tn_deb || debses) tn_debug(tn_msg); } #ifdef OS2 RequestTelnetMutex( SEM_INDEFINITE_WAIT ); #endif ttol((CHAR *)buf, 9); #ifdef OS2 ReleaseTelnetMutex(); #endif break; #endif /* SRP */ #ifdef NTLM case AUTHTYPE_NTLM: { int length = 0; for ( i=0 ; i= 0; --i) { register int x; x = (unsigned int)k4_challenge[i] + 1; k4_challenge[i] = x; /* ignore overflow */ if (x < 256) /* if no overflow, all done */ break; } ckhexdump("auth_send k4_challenge+1",k4_challenge,8); #ifdef MIT_CURRENT data.data = k4_challenge; data.length = 8; encdata.ciphertext.data = k4_challenge; encdata.ciphertext.length = 8; encdata.enctype = ENCTYPE_UNKNOWN; if (code = krb5_c_encrypt(k5_context, &k4_krbkey, 0, 0, &data, &encdata)) { com_err("libtelnet", code, "while encrypting random key"); return(0); } #else /* MIT_CURRENT */ #ifdef NT des_ecb_encrypt(k4_challenge, k4_challenge, k4_sched, 1); #else /* NT */ des_ecb_encrypt(&k4_challenge, &k4_challenge, k4_sched, 1); #endif /* NT */ ckhexdump("auth_send des_ecb_encrypt(k4_session_key,k4_challenge,1)", k4_challenge,8); #endif /* MIT_CURRENT */ } #endif /* ENCRYPTION */ #endif /* REMOVE_FOR_EXPORT */ break; #endif /* KRB4 */ #ifdef GSSAPI_KRB5 case AUTHTYPE_GSSAPI_KRB5: for ( i=0 ; i 63 ? 63 : (end_sub-2); if ( len > 0 && (len + 1) < sizeof(szUserNameRequested)) { memcpy(szUserNameRequested,&parsedat[2],len); /* safe */ szUserNameRequested[len] = '\0'; } else szUserNameRequested[0] = '\0'; debug(F111,"auth_name szUserNameRequested",szUserNameRequested,len); return(AUTH_SUCCESS); } /* * Function: Parse the athorization sub-options and reply. * * Parameters: * parsedat - sub-option string to parse. * * end_sub - last charcter position in parsedat. */ int auth_parse(unsigned char *parsedat, int end_sub) { int rc = AUTH_FAILURE; switch (parsedat[1]) { case TELQUAL_SEND: rc = auth_send(parsedat, end_sub); break; case TELQUAL_REPLY: rc= auth_reply(parsedat, end_sub); break; case TELQUAL_IS: rc = auth_is(parsedat, end_sub); break; case TELQUAL_NAME: rc = auth_name(parsedat, end_sub); break; } debug(F111,"auth_parse","rc",rc); return(rc); } /* * Function: Initialization routine called kstream encryption system. * * Parameters: * data - user data. */ int #ifdef CK_ANSIC auth_init(kstream ks) #else auth_init(ks) kstream_ptr ks; #endif { #ifdef FORWARD forwarded_tickets = 0; /* were tickets forwarded? */ #endif /* FORWARD */ #ifdef CK_ENCRYPTION encrypt_init(ks,cx_type); #endif return 0; } /* * Function: Destroy routine called kstream encryption system. * * Parameters: * data - user data. */ VOID #ifdef CK_ANSIC auth_destroy(void) #else auth_destroy() #endif { } /* * Function: Callback to encrypt a block of characters * * Parameters: * out - return as pointer to converted buffer. * * in - the buffer to convert * * Returns: number of characters converted. */ int #ifdef CK_ANSIC auth_encrypt(struct kstream_data_block *out, struct kstream_data_block *in) #else auth_encrypt(out,in) struct kstream_data_block *out; struct kstream_data_block *in; #endif { out->ptr = in->ptr; out->length = in->length; return(out->length); } /* * Function: Callback to decrypt a block of characters * * Parameters: * out - return as pointer to converted buffer. * * in - the buffer to convert * * Returns: number of characters converted. */ int #ifdef CK_ANSIC auth_decrypt(struct kstream_data_block *out, struct kstream_data_block *in) #else auth_decrypt(out,in) struct kstream_data_block *out; struct kstream_data_block *in; #endif { out->ptr = in->ptr; out->length = in->length; return(out->length); } #ifdef KRB4 #ifdef NT void ck_krb4_debug(int x) { set_krb_debug(x); set_krb_ap_req_debug(x); } #endif /* NT */ int ck_krb4_autoget_TGT(char * realm) { extern struct krb_op_data krb_op; extern struct krb4_init_data krb4_init; char passwd[PWD_SZ]; char prompt[256]; char * saverealm=NULL; int rc = -1; extern char * k4prprompt; extern char * k4pwprompt; ini_kerb(); /* Place defaults in above structs */ passwd[0] = '\0'; if ( krb4_init.principal == NULL || krb4_init.principal[0] == '\0') { int ok = uq_txt(NULL, k4prprompt && k4prprompt[0] ? k4prprompt : "Kerberos 4 Principal: ", 2, NULL, passwd,PWD_SZ-1, NULL, DEFAULT_UQ_TIMEOUT); if ( ok && passwd[0] ) makestr(&krb4_init.principal,passwd); else return(0); } /* Save realm in init structure so it can be restored */ if ( realm ) { saverealm = krb4_init.realm; krb4_init.realm = realm; } if ( passwd[0] || !(pwbuf[0] && pwflg) ) { int ok; if ( k4pwprompt && k4pwprompt[0] && (strlen(k4pwprompt) + strlen(krb4_init.principal) + strlen(krb4_init.realm) - 4) < sizeof(prompt)) { sprintf(prompt,k4pwprompt,krb4_init.principal,krb4_init.realm); } else ckmakxmsg(prompt,sizeof(prompt), "Kerberos 4 Password for ",krb4_init.principal,"@", krb4_init.realm,": ", NULL,NULL,NULL,NULL,NULL,NULL,NULL); ok = uq_txt(NULL,prompt,2,NULL,passwd,PWD_SZ-1,NULL, DEFAULT_UQ_TIMEOUT); if ( !ok ) passwd[0] = '\0'; } else { ckstrncpy(passwd,pwbuf,sizeof(passwd)); #ifdef OS2 if ( pwcrypt ) ck_encrypt((char *)passwd); #endif /* OS2 */ } if ( passwd[0] ) { makestr(&krb4_init.password,passwd); rc = ck_krb4_initTGT(&krb_op, &krb4_init); free(krb4_init.password); krb4_init.password = NULL; } krb4_init.password = NULL; memset(passwd,0,PWD_SZ); /* restore realm to init structure if needed */ if ( saverealm ) krb4_init.realm = saverealm; return(rc == 0); } char * ck_krb4_realmofhost(char *host) { return (char *)krb_realmofhost(host); } /* * * K4_auth_send - gets authentication bits we need to send to KDC. * * Result is left in auth * * Returns: 0 on failure, 1 on success */ static int #ifdef CK_ANSIC k4_auth_send(void) #else k4_auth_send() #endif { int r=0; /* Return value */ char instance[INST_SZ+1]=""; char *realm=NULL; char tgt[4*REALM_SZ+1]; memset(instance, 0, sizeof(instance)); #ifdef COMMENT /* we only need to call krb_get_phost if the hostname */ /* is not fully qualified. But we have already done */ /* this in netopen() call. This will save a round of */ /* DNS queries. */ debug(F110,"k4_auth_send","krb_get_phost",0); if (realm = (char *)krb_get_phost(szHostName)) { ckstrncpy(instance, realm, INST_SZ); } #else /* COMMENT */ { char *p; ckstrncpy(instance, szHostName, INST_SZ); for ( p=instance; *p && *p != '.' ; p++ ); *p = '\0'; } #endif /* COMMENT */ debug(F110,"k4_auth_send","krb_get_realmofhost",0); realm = (char *)krb_realmofhost(szHostName); if (!realm) { strcpy(strTmp, "Can't find realm for host \""); ckstrncat(strTmp, szHostName,AUTHTMPBL); ckstrncat(strTmp, "\"",AUTHTMPBL); printf("?Kerberos 4 error: %s\r\n",strTmp); krb4_errno = r; makestr(&krb4_errmsg,strTmp); return(0); } ckmakmsg(tgt,sizeof(tgt),"krbtgt.",realm,"@",realm); r = ck_krb4_tkt_isvalid(tgt); if ( r <= 0 && krb4_autoget ) ck_krb4_autoget_TGT(realm); debug(F110,"k4_auth_send","krb_mk_req",0); r = krb_mk_req(&k4_auth, krb4_d_srv ? krb4_d_srv : KRB4_SERVICE_NAME, instance, realm, 0); if (r == 0) { debug(F110,"k4_auth_send","krb_get_cred",0); r = krb_get_cred(krb4_d_srv ? krb4_d_srv : KRB4_SERVICE_NAME, instance, realm, &cred); if (r) debug(F111,"k4_auth_send","krb_get_cred() failed",r); } else debug(F111,"k4_auth_send","krb_mk_req() failed",r); if (r) { strcpy(strTmp, "Can't get \""); ckstrncat(strTmp, krb4_d_srv ? krb4_d_srv : KRB4_SERVICE_NAME,AUTHTMPBL); if (instance[0] != 0) { ckstrncat(strTmp, ".",AUTHTMPBL); ckstrncat(strTmp, instance,AUTHTMPBL); } ckstrncat(strTmp, "@",AUTHTMPBL); ckstrncat(strTmp, realm,AUTHTMPBL); ckstrncat(strTmp, "\" ticket\r\n ",AUTHTMPBL); ckstrncat(strTmp, (char *)krb_get_err_text_entry(r),AUTHTMPBL); debug(F111,"k4_auth_send",(char *)krb_get_err_text_entry(r),r); printf("?Kerberos 4 error: %s\r\n",strTmp); krb4_errno = r; makestr(&krb4_errmsg,krb_get_err_text_entry(krb4_errno)); return(0); } #ifdef OS2 if ( !szUserName[0] || !stricmp(szUserName,cred.pname) ) { ckstrncpy(szUserName, cred.pname, UIDBUFLEN); } #endif /* OS2 */ krb4_errno = r; makestr(&krb4_errmsg,krb_get_err_text_entry(krb4_errno)); debug(F110,"k4_auth_send",krb4_errmsg,0); return(1); } /* * Function: K4 parse authentication reply command * * Parameters: * parsedat - the sub-command data. * * end_sub - index of the character in the 'parsedat' array which * is the last byte in a sub-negotiation * * Returns: Kerberos error code. */ static int #ifdef CK_ANSIC k4_auth_reply(unsigned char *parsedat, int end_sub) #else k4_auth_reply(parsedat,end_sub) unsigned char *parsedat; int end_sub; #endif { #ifdef CK_ENCRYPTION Session_Key skey; #ifdef MIT_CURRENT krb5_data kdata; krb5_enc_data encdata; krb5_error_code code; #endif /* MIT_CURRENT */ #endif time_t t; int x; int i; if (end_sub < 4 || parsedat[2] != AUTHTYPE_KERBEROS_V4) { auth_finished(AUTH_REJECT); return AUTH_FAILURE; } if (parsedat[4] == KRB_REJECT) { strTmp[0] = 0; for (i = 5; i <= end_sub; i++) { if (parsedat[i] == IAC) break; strTmp[i-5] = parsedat[i]; strTmp[i-4] = 0; } if (!strTmp[0]) strcpy(strTmp, "Authentication rejected by remote machine!"); printf("Kerberos V4 authentication failed!\r\n%s\r\n",strTmp); krb4_errno = -1; makestr(&krb4_errmsg,strTmp); auth_finished(AUTH_REJECT); return AUTH_FAILURE; } if (parsedat[4] == KRB_ACCEPT) { int net_len; if ((parsedat[3] & AUTH_HOW_MASK) == AUTH_HOW_ONE_WAY) { ckmakmsg(strTmp,sizeof(strTmp),"Kerberos V4 accepts you as ", szUserName,NULL,NULL); printf("%s\r\n",strTmp); accept_complete = 1; krb4_errno = 0; makestr(&krb4_errmsg,strTmp); auth_finished(AUTH_USER); return AUTH_SUCCESS; } if ((parsedat[3] & AUTH_HOW_MASK) != AUTH_HOW_MUTUAL) { printf("Kerberos V4 authentication failed!\r\n"); ckstrncpy(strTmp, "Kerberos V4 accepted you, but didn't provide mutual authentication", sizeof(strTmp)); printf("%s\r\n",strTmp); krb4_errno = -1; makestr(&krb4_errmsg,strTmp); auth_finished(AUTH_REJECT); return AUTH_FAILURE; } #ifndef REMOVE_FOR_EXPORT #ifdef CK_ENCRYPTION SendK4AuthSB(KRB4_CHALLENGE,k4_session_key,sizeof(k4_session_key)); /* We have sent the decrypted session key to the host as a challenge */ /* now encrypt it to restore it to its original valid DES key value */ #ifdef MIT_CURRENT kdata.data = k4_session_key; kdata.length = 8; encdata.ciphertext.data = k4_session_key; encdata.ciphertext.length = 8; encdata.enctype = ENCTYPE_UNKNOWN; if (code = krb5_c_encrypt(k5_context, &k4_krbkey, 0, 0, &kdata, &encdata)) { com_err("k4_auth_reply", code, "while encrypting session_key"); auth_finished(AUTH_REJECT); return AUTH_FAILURE; } #else /* MIT_CURRENT */ #ifdef NT des_ecb_encrypt(k4_session_key, k4_session_key, k4_sched, 1); #else /* NT */ des_ecb_encrypt(&k4_session_key, &k4_session_key, k4_sched, 1); #endif /* NT */ ckhexdump( "k4_auth_reply des_ecb_encrypt(k4_session_key,k4_session_key,1)", k4_session_key, 8 ); #endif /* MIT_CURRENT */ #ifdef CK_SSL if (!(ssl_active_flag || tls_active_flag)) #endif /* CK_SSL */ { /* And then use it to configure the encryption state machine. */ skey.type = SK_DES; skey.length = 8; skey.data = k4_session_key; encrypt_session_key(&skey, AUTH_CLIENT_TO_SERVER); } #endif /* ENCRYPTION */ #endif /* REMOVE_FOR_EXPORT */ accept_complete = 1; ckmakmsg(strTmp,sizeof(strTmp), "Kerberos V4 accepts you as ",szUserName,NULL,NULL); printf("%s\r\n",strTmp); krb4_errno = 0; makestr(&krb4_errmsg,strTmp); auth_finished(AUTH_USER); return AUTH_SUCCESS; } if (parsedat[4] == KRB4_RESPONSE) { if (end_sub < 12) { auth_finished(AUTH_REJECT); return AUTH_FAILURE; } ckhexdump("KRB4_RESPONSE &parsedat[5]",&parsedat[5],8); #ifdef CK_ENCRYPTION ckhexdump("KRB4_RESPONSE k4_challenge",k4_challenge,8); /* The datablock returned from the host should match the value */ /* we stored in k4_challenge. */ if (memcmp(&parsedat[5], k4_challenge, sizeof(k4_challenge)) != 0) { printf("Kerberos V4 authentication failed!\r\n%s\r\n", "Remote machine is being impersonated!"); krb4_errno = -1; makestr(&krb4_errmsg,"Remote machine is being impersonated!"); auth_finished(AUTH_REJECT); return AUTH_FAILURE; } #else /* ENCRYPTION */ makestr(&krb4_errmsg,"Kermit built without support for encryption."); return AUTH_FAILURE; #endif /* ENCRYPTION */ mutual_complete = 1; ckstrncpy(strTmp,"Remote machine has been mutually authenticated", sizeof(strTmp)); printf("%s\r\n",strTmp); krb4_errno = 0; makestr(&krb4_errmsg,strTmp); auth_finished(AUTH_USER); return AUTH_SUCCESS; } auth_finished(AUTH_REJECT); return AUTH_FAILURE; } /* * Function: K4 parse authentication IS command * * Parameters: * parsedat - the sub-command data. * * end_sub - index of the character in the 'parsedat' array which * is the last byte in a sub-negotiation * * Returns: Kerberos error code. */ static int #ifdef CK_ANSIC k4_auth_is(unsigned char *parsedat, int end_sub) #else k4_auth_is(parsedat,end_sub) unsigned char *parsedat; int end_sub; #endif { #ifdef CK_ENCRYPTION Session_Key skey; #ifdef MIT_CURRENT Block datablock, tmpkey; krb5_data kdata; krb5_enc_data encdata; krb5_error_code code; #else /* MIT_CURRENT */ Block datablock; #endif /* MIT_CURRENT */ #endif /* ENCRYPTION */ char realm[REALM_SZ+1]; char instance[INST_SZ]; int r = 0; char * data = &parsedat[5]; int cnt = end_sub - 5; extern char myipaddr[]; struct hostent *host; struct in_addr inaddr; int i; if (end_sub < 4 || parsedat[2] != AUTHTYPE_KERBEROS_V4) { debug(F110,"k4_auth_is","Not kerberos v4",0); auth_finished(AUTH_REJECT); return AUTH_FAILURE; } switch (parsedat[4]) { case KRB_AUTH: debug(F110,"k4_auth_is","KRB_AUTH",0); ckstrncpy(realm,ck_krb4_getrealm(),REALM_SZ+1); if (realm[0] == '\0') { SendK4AuthSB(KRB_REJECT, (void *)"No local V4 Realm.", -1); printf("\r\n? Kerberos 4 - No Local Realm\r\n"); debug(F110,"k4_auth_is","No local realm",0); krb4_errno = -1; makestr(&krb4_errmsg,"No local realm"); auth_finished(AUTH_REJECT); return AUTH_FAILURE; } debug(F110,"k4_auth_is",realm,0); if ( cnt < sizeof(k4_auth.dat) ) { k4_auth.length = cnt; memcpy((void *)k4_auth.dat, (void *)data, k4_auth.length); } else k4_auth.length = 0; ckhexdump("k4_auth.dat",k4_auth.dat, k4_auth.length); /* Get Instance */ inaddr.s_addr = inet_addr(myipaddr); host = gethostbyaddr((unsigned char *)&inaddr,4,PF_INET); if ( host ) { #ifdef HADDRLIST host = ck_copyhostent(host); #endif /* HADDRLIST */ ckstrncpy(instance,host->h_name,INST_SZ); for ( i=0;i= 0; r--) { register int t; t = (unsigned int)k4_challenge[r] + 1; k4_challenge[r] = t; /* ignore overflow */ if (t < 256) /* if no overflow, all done */ break; } ckhexdump("auth_is k4_challenge+1",k4_challenge,8); #ifdef MIT_CURRENT kdata.data = k4_challenge; kdata.length = 8; encdata.ciphertext.data = k4_challenge; encdata.ciphertext.length = 8; encdata.enctype = ENCTYPE_UNKNOWN; if (code = krb5_c_encrypt(k5_context, &k4_krbkey, 0, 0, &kdata, &encdata)) { com_err("k4_auth_is", code, "while decrypting challenge"); auth_finished(AUTH_REJECT); return AUTH_FAILURE; } #else /* MIT_CURRENT */ #ifdef NT des_ecb_encrypt(k4_challenge, k4_challenge, k4_sched, 1); #else /* NT */ des_ecb_encrypt(&k4_challenge, &k4_challenge, k4_sched, 1); #endif /* NT */ ckhexdump("auth_is des_ecb_encrypt(k4_challenge_key,k4_challenge,1)", k4_challenge,8); #endif /* MIT_CURRENT */ SendK4AuthSB(KRB4_RESPONSE,(void *)k4_challenge,sizeof(k4_challenge)); #endif /* ENCRYPTION */ mutual_complete = 1; break; default: if (1) printf("Unknown Kerberos option %d\r\n", data[-1]); SendK4AuthSB(KRB_REJECT, 0, 0); return(AUTH_FAILURE); } krb4_errno = r; makestr(&krb4_errmsg,krb_get_err_text_entry(krb4_errno)); return(AUTH_SUCCESS); } #endif /* KRB4 */ #ifdef KRB5 int ck_krb5_autoget_TGT(char * realm) { extern struct krb_op_data krb_op; extern struct krb5_init_data krb5_init; char passwd[PWD_SZ]; char prompt[64]; char * saverealm=NULL; int rc = -1; extern char * k5prprompt; extern char * k5pwprompt; ini_kerb(); /* Place defaults in above structs */ passwd[0] = '\0'; if ( krb5_init.principal == NULL || krb5_init.principal[0] == '\0') { int ok = uq_txt(NULL,k5prprompt && k5prprompt[0] ? k5prprompt : "Kerberos 5 Principal: ",2,NULL,passwd,PWD_SZ-1,NULL, DEFAULT_UQ_TIMEOUT); if ( ok && passwd[0] ) makestr(&krb5_init.principal,passwd); else return(0); } /* Save realm in init structure so it can be restored */ if ( realm ) { saverealm = krb5_init.realm; krb5_init.realm = realm; } if ( passwd[0] || !(pwbuf[0] && pwflg) ) { int ok; if ( k5pwprompt && k5pwprompt[0] && (strlen(k5pwprompt) + strlen(krb5_init.principal) + strlen(krb5_init.realm) - 4) < sizeof(prompt)) { sprintf(prompt,k5pwprompt,krb5_init.principal,krb5_init.realm); } else ckmakxmsg(prompt,sizeof(prompt), k5pwprompt && k5pwprompt[0] ? k5pwprompt : "Kerberos 5 Password for ", krb5_init.principal,"@",krb5_init.realm,": ", NULL,NULL,NULL,NULL,NULL,NULL,NULL ); ok = uq_txt(NULL,prompt,2,NULL,passwd,PWD_SZ-1,NULL, DEFAULT_UQ_TIMEOUT); if ( !ok ) passwd[0] = '\0'; } else { ckstrncpy(passwd,(char *)pwbuf,sizeof(passwd)); #ifdef OS2 if ( pwcrypt ) ck_encrypt((char *)passwd); #endif /* OS2 */ } if ( passwd[0] ) { extern struct krb4_init_data krb4_init; char * savek4realm=NULL; makestr(&krb5_init.password,passwd); if ( krb5_d_getk4 ) { krb5_init.getk4 = 1; makestr(&krb4_init.principal,krb5_init.principal); makestr(&krb4_init.password,passwd); if ( realm ) { savek4realm = krb4_init.realm; krb4_init.realm = realm; } rc = ck_krb5_initTGT(&krb_op, &krb5_init,&krb4_init); if ( savek4realm ) krb4_init.realm = savek4realm; free(krb4_init.password); krb4_init.password = NULL; } else { rc = ck_krb5_initTGT(&krb_op, &krb5_init,NULL); } free(krb5_init.password); krb5_init.password = NULL; memset(passwd,0,PWD_SZ); } /* restore realm to init structure if needed */ if ( saverealm ) krb5_init.realm = saverealm; return(rc == 0); } static krb5_error_code #ifdef CK_ANSIC k5_get_ccache( krb5_context k5_context, krb5_ccache * p_ccache, char * cc_name ) #else /* CK_ANSIC */ k5_get_ccache(k5_context, p_ccache, cc_name) krb5_context k5_context; krb5_ccache * p_ccache; char * cc_name; #endif /* CK_ANSIC */ { krb5_error_code r=0; char cc_tmp[CKMAXPATH+1]; const char * def_name = NULL; #ifndef HEIMDAL if ( cc_name ) { if ( strncmp("FILE:",cc_name,5) && strncmp("MEMORY:",cc_name,7) && strncmp("API:",cc_name,4) && strncmp("STDIO:",cc_name,6) && strncmp("MSLSA:",cc_name,6)) #ifdef NT ckmakmsg(cc_tmp,CKMAXPATH,"API:",cc_name,NULL,NULL); #else /* NT */ ckmakmsg(cc_tmp,CKMAXPATH,"FILE:",cc_name,NULL,NULL); #endif /* NT */ else { ckstrncpy(cc_tmp,cc_name,CKMAXPATH); } r = krb5_cc_resolve (k5_context, cc_tmp, p_ccache); if (r != 0) { com_err("k5_get_ccache resolving ccache",r, cc_tmp); } else { /* Make sure GSSAPI sees the same cache we are using */ char buf[128]; ckmakmsg((char *)buf,128,"KRB5CCNAME=",cc_tmp,NULL,NULL); putenv(buf); } } else if ( krb5_d_cc ) { if ( strncmp("FILE:",krb5_d_cc,5) && strncmp("MEMORY:",krb5_d_cc,7) && strncmp("API:",krb5_d_cc,4) && strncmp("STDIO:",krb5_d_cc,6) && strncmp("MSLSA:", krb5_d_cc,6)) #ifdef NT ckmakmsg(cc_tmp,CKMAXPATH,"API:",krb5_d_cc,NULL,NULL); #else /* NT */ ckmakmsg(cc_tmp,CKMAXPATH,"FILE:",krb5_d_cc,NULL,NULL); #endif /* NT */ else { ckstrncpy(cc_tmp,krb5_d_cc,CKMAXPATH); } r = krb5_cc_resolve (k5_context, cc_tmp, p_ccache); if (r != 0) { com_err("k5_get_ccache resolving ccache",r, krb5_d_cc); } else { /* Make sure GSSAPI sees the same cache we are using */ char buf[128]; ckmakmsg((char *)buf,128,"KRB5CCNAME=",cc_tmp,NULL,NULL); putenv(buf); } } else #endif /* HEIMDAL */ { if ((r = krb5_cc_default(k5_context, p_ccache))) { com_err("k5_get_ccache",r,"while getting default ccache"); } } /* do not set krb5_errno/krb5_errmsg here since the value returned */ /* is being passed internally within the krb5 functions. */ return(r); } char * ck_krb5_realmofhost(char *host) { char ** realmlist=NULL; krb5_context private_context=NULL; static char * realm = NULL; if ( !host ) return NULL; if ( realm ) { free(realm); realm = NULL; } /* create private_context */ if (krb5_init_context(&private_context)) { debug(F110,"ck_krb5_realmofhost()","unable to init_context",0); return(NULL); } krb5_get_host_realm(private_context,host,&realmlist); if (realmlist && realmlist[0]) { makestr(&realm,realmlist[0]); krb5_free_host_realm(private_context,realmlist); realmlist = NULL; } if ( private_context ) { krb5_free_context(private_context); private_context = NULL; } if (ckstrchr(realm,'.') == NULL) { int n = 0; char * p = host; while ( (p = ckstrchr(p,'.')) != NULL ) { n++; p++; } if (n == 1) { makestr(&realm,host); ckupper(realm); } else { free(realm); realm = NULL; } } return(realm); } /* * * K5_auth_send - gets authentication bits we need to send to KDC. * * Code lifted from telnet sample code in the appl directory. * * Result is left in k5_auth * * Returns: 0 on failure, 1 on success * */ static int #ifdef CK_ANSIC k5_auth_send(int how, int encrypt, int forward) #else k5_auth_send(how,encrypt,forward) int how; int encrypt; int forward; #endif { krb5_error_code r=0; krb5_ccache ccache=NULL; #ifndef HEIMDAL krb5_creds creds; #endif /* HEIMDAL */ krb5_creds * new_creds=NULL; #ifdef CK_ENCRYPTION krb5_keyblock *newkey = 0; #endif /* CK_ENCRYPTION */ krb5_flags ap_opts, auth_flags; char type_check[32]; krb5_data checksum; int len=0; char * realm = NULL; char tgt[256]; realm = ck_krb5_realmofhost(szHostName); if (!realm) { ckstrncpy(strTmp, "Can't find realm for host \"",AUTHTMPBL); ckstrncat(strTmp, szHostName,AUTHTMPBL); ckstrncat(strTmp, "\"",AUTHTMPBL); printf("?Kerberos 5 error: %s\r\n",strTmp); krb5_errno = KRB5_ERR_HOST_REALM_UNKNOWN; makestr(&krb5_errmsg,strTmp); return(0); } ckmakmsg(tgt,sizeof(tgt),"krbtgt/",realm,"@",realm); debug(F110,"k5_auth_send TGT",tgt,0); if ( krb5_autoget && !((ck_krb5_tkt_isvalid(NULL,tgt) > 0) || (ck_krb5_is_tgt_valid() > 0)) ) ck_krb5_autoget_TGT(realm); r = k5_get_ccache(k5_context,&ccache,NULL); if ( r ) { com_err(NULL, r, "while authorizing (0)."); krb5_errno = r; makestr(&krb5_errmsg,error_message(krb5_errno)); return(0); } #ifndef HEIMDAL memset((char *)&creds, 0, sizeof(creds)); if (r = krb5_sname_to_principal(k5_context, szHostName, krb5_d_srv ? krb5_d_srv : KRB5_SERVICE_NAME, KRB5_NT_SRV_HST, &creds.server)) { com_err(NULL, r, "while authorizing (1)."); krb5_errno = r; makestr(&krb5_errmsg,error_message(krb5_errno)); return(0); } if (forward_flag) { if (fwd_server) { krb5_free_principal(k5_context,fwd_server); fwd_server = NULL; } krb5_copy_principal(k5_context,creds.server,&fwd_server); } r = krb5_cc_get_principal(k5_context, ccache, &creds.client); if (r) { com_err(NULL, r, "while authorizing (2)."); krb5_free_cred_contents(k5_context, &creds); krb5_errno = r; makestr(&krb5_errmsg,error_message(krb5_errno)); return(0); } if (szUserName[0] == '\0') { /* Get user name now */ len = krb5_princ_component(k5_context, creds.client, 0)->length; if ( len < sizeof(szUserName) ) { memcpy(szUserName, krb5_princ_component(k5_context, creds.client, 0)->data, len); /* safe */ } else len = 0; szUserName[len] = '\0'; } else { char * name = NULL; len = krb5_princ_component(k5_context, creds.client, 0)->length; if ( len == strlen(szUserName) ) { name = krb5_princ_component(k5_context, creds.client, 0)->data; #ifdef OS2 if ( !strnicmp(szUserName,name,len) ) memcpy(szUserName,name,len); /* safe */ #endif /* OS2 */ } } if ( tn_auth_krb5_des_bug ) { /* !ALLOW_KRB_3DES_ENCRYPT */ /* Not sure if this is necessary anymore. What impact does it have * on Win2000 TGTs that use DES_CBC_MD5 or RC4_HMAC? * * This prevents using 3DES Service Tickets. */ creds.keyblock.enctype=ENCTYPE_DES_CBC_CRC; } if (r = krb5_get_credentials(k5_context, 0, ccache, &creds, &new_creds)) { com_err(NULL, r, "while authorizing (3)."); krb5_free_cred_contents(k5_context, &creds); krb5_errno = r; makestr(&krb5_errmsg,error_message(krb5_errno)); return(0); } #endif /* HEIMDAL */ if (auth_context) { krb5_auth_con_free(k5_context, auth_context); auth_context = 0; } if (r = krb5_auth_con_init(k5_context, &auth_context)) { com_err(NULL, r, "while initializing auth context"); krb5_errno = r; makestr(&krb5_errmsg,error_message(krb5_errno)); return(0); } /* UPDATE for START_TLS. AUTH_ENCRYPT_START_TLS and inclusion of */ /* client and then server finished messages. */ type_check[0] = AUTHTYPE_KERBEROS_V5; type_check[1] = AUTH_CLIENT_TO_SERVER | (how ? AUTH_HOW_MUTUAL : AUTH_HOW_ONE_WAY) | (encrypt) | (forward ? INI_CRED_FWD_ON : INI_CRED_FWD_OFF); #ifdef CK_SSL if (encrypt == AUTH_ENCRYPT_START_TLS) { ssl_get_client_finished(&type_check[2],12); ssl_get_server_finished(&type_check[14],12); } #endif /* CK_SSL */ #ifndef HEIMDAL checksum.magic = KV5M_DATA; #endif /* HEIMDAL */ checksum.length = #ifdef CK_SSL (encrypt == AUTH_ENCRYPT_START_TLS) ? 26 : #endif /* CK_SSL */ 2; checksum.data = (char *)&type_check; ap_opts = 0; if ((how & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) ap_opts |= AP_OPTS_MUTUAL_REQUIRED; #ifdef HEIMDAL r = krb5_auth_setkeytype(k5_context, auth_context, KEYTYPE_DES); if (r) com_err(NULL, r, "while setting auth keytype"); r = krb5_auth_con_setaddrs_from_fd(k5_context,auth_context, &ttyfd); if (r) com_err(NULL, r, "while setting auth addrs"); r = krb5_mk_req(k5_context, &auth_context, ap_opts, krb5_d_srv ? krb5_d_srv : KRB5_SERVICE_NAME, szHostName, &checksum, ccache, &k5_auth); if (r) com_err(NULL, r, "while making request"); #else /* HEIMDAL */ auth_flags = KRB5_AUTH_CONTEXT_RET_TIME; #ifdef CK_ENCRYPTION ap_opts |= AP_OPTS_USE_SUBKEY; #endif /* CK_ENCRYPTION */ #ifdef TLS_VERIFY if ((how & AUTH_ENCRYPT_MASK) == AUTH_ENCRYPT_START_TLS) { auth_flags |= KRB5_AUTH_CONTEXT_DO_SEQUENCE; if (!krb5_d_no_addresses) r = krb5_auth_con_genaddrs(k5_context, auth_context, ttyfd, KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR); } #endif /* CK_SSL */ krb5_auth_con_setflags(k5_context, auth_context, auth_flags); r = krb5_mk_req_extended(k5_context, &auth_context, ap_opts, &checksum, new_creds, &k5_auth); #endif /* HEIMDAL */ #ifdef CK_ENCRYPTION if (!r) { r = krb5_auth_con_getlocalsubkey(k5_context, auth_context, &newkey); if (r) r = krb5_auth_con_getkey(k5_context, auth_context, &newkey); if (k5_session_key) { krb5_free_keyblock(k5_context, k5_session_key); k5_session_key = 0; } } if (newkey) { /* * keep the key in our private storage, but don't use it * yet---see kerberos5_reply() below */ #ifdef HEIMDAL if ((newkey->keytype == ETYPE_DES_CBC_CRC) || (newkey->keytype == ETYPE_DES_CBC_MD5) || (newkey->keytype == ETYPE_DES_CBC_MD4)) { debug(F111,"k5_auth_send()","newkey->keytype",newkey->keytype); krb5_copy_keyblock(k5_context, newkey, &k5_session_key); } #else /* HEIMDAL */ /* look for all possible DES keys first - just for compatibility */ /* other key types are much less likely to be available */ if ((newkey->enctype == ENCTYPE_DES_CBC_CRC) || (newkey->enctype == ENCTYPE_DES_CBC_MD5) || (newkey->enctype == ENCTYPE_DES_CBC_MD4)) { debug(F111,"k5_auth_send()","newkey->enctype",newkey->enctype); krb5_copy_keyblock(k5_context, newkey, &k5_session_key); } else if ((new_creds->keyblock.enctype == ENCTYPE_DES_CBC_CRC) || (new_creds->keyblock.enctype == ENCTYPE_DES_CBC_MD5)) { /* use the session key in credentials instead */ debug(F111,"k5_auth_send()","new_creds->keyblock.enctype", new_creds->keyblock.enctype); krb5_copy_keyblock(k5_context, &new_creds->keyblock, &k5_session_key); } else if (newkey->enctype != 0) { debug(F111,"k5_auth_send()","newkey->enctype",newkey->enctype); krb5_copy_keyblock(k5_context, newkey, &k5_session_key); } else if (new_creds->keyblock.enctype != 0) { /* use the session key in credentials instead */ debug(F111,"k5_auth_send()","new_creds->keyblock.enctype", new_creds->keyblock.enctype); krb5_copy_keyblock(k5_context, &new_creds->keyblock, &k5_session_key); } else { debug(F110,"k5_auth_send()","NO KEY in newkey",0); } #endif /* HEIMDAL */ krb5_free_keyblock(k5_context, newkey); } #endif /* CK_ENCRYPTION */ #ifndef HEIMDAL krb5_free_cred_contents(k5_context, &creds); krb5_free_creds(k5_context, new_creds); #endif /* HEIMDAL */ krb5_cc_close(k5_context,ccache); if (r) { com_err(NULL, r, "while authorizing (4)."); krb5_errno = r; makestr(&krb5_errmsg,error_message(krb5_errno)); return(0); } krb5_errno = 0; makestr(&krb5_errmsg,"OK"); return(1); } /* * K5_auth_reply -- checks the reply for mutual authentication. */ static int #ifdef CK_ANSIC k5_auth_reply(int how, unsigned char *data, int cnt) #else k5_auth_reply(how,data,cnt) int how; unsigned char *data; int cnt; #endif { #ifdef CK_ENCRYPTION Session_Key skey; #endif /* CK_ENCRYPTION */ data += 4; /* Point to status byte */ cnt -=5; switch (*data++) { case KRB_REJECT: if (cnt > 0) { char *s; int len; ckstrncpy(strTmp,"Kerberos V5 refuses authentication because\r\n", sizeof(strTmp)); len = strlen(strTmp); if ( len + cnt < sizeof(strTmp) ) { s = strTmp + strlen(strTmp); memcpy(s, data, cnt); /* safe */ s[cnt] = 0; } } else ckstrncpy(strTmp,"Kerberos V5 refuses authentication", sizeof(strTmp)); krb5_errno = -1; makestr(&krb5_errmsg,strTmp); printf("Kerberos authentication failed!\r\n%s\r\n",strTmp); auth_finished(AUTH_REJECT); return AUTH_FAILURE; case KRB_ACCEPT: if (!mutual_complete) { if ((how & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL && !mutual_complete) { ckstrncpy(strTmp, "Kerberos V5 accepted you, but didn't provide" " mutual authentication",sizeof(strTmp)); printf("Kerberos authentication failed!\r\n%s\r\n",strTmp); krb5_errno = -1; makestr(&krb5_errmsg,strTmp); auth_finished(AUTH_REJECT); return AUTH_FAILURE; } #ifdef CK_ENCRYPTION if (k5_session_key) { if ( tn_auth_krb5_des_bug ) { /* !ALLOW_KRB_3DES_ENCRYPT */ skey.type = SK_DES; skey.length = 8; #ifdef HEIMDAL skey.data = k5_session_key->keyvalue.data; #else /* HEIMDAL */ skey.data = k5_session_key->contents; #endif /* HEIMDAL */ } else { #ifdef HEIMDAL switch ( k5_session_key->keytype ) { case ETYPE_DES_CBC_CRC: case ETYPE_DES_CBC_MD5: case ETYPE_DES_CBC_MD4: skey.type = SK_DES; skey.length = 8; break; default: skey.type = SK_GENERIC; skey.length = k5_session_key->length; encrypt_dont_support(ENCTYPE_DES_CFB64); encrypt_dont_support(ENCTYPE_DES_OFB64); } skey.data = k5_session_key->keyvalue.data; #else /* HEIMDAL */ switch ( k5_session_key->enctype ) { case ENCTYPE_DES_CBC_CRC: case ENCTYPE_DES_CBC_MD5: case ENCTYPE_DES_CBC_MD4: skey.type = SK_DES; skey.length = 8; default: skey.type = SK_GENERIC; skey.length = k5_session_key->length; encrypt_dont_support(ENCTYPE_DES_CFB64); encrypt_dont_support(ENCTYPE_DES_OFB64); } skey.data = k5_session_key->contents; #endif /* HEIMDAL */ } encrypt_session_key(&skey, AUTH_CLIENT_TO_SERVER); } #endif /* CK_ENCRYPTION */ } if ( cnt > 0 ) { char *s; int len; ckstrncpy(strTmp,"Kerberos V5 accepts you as ",sizeof(strTmp)); len = strlen(strTmp); if ( len + cnt < sizeof(strTmp) ) { s = strTmp + strlen(strTmp); memcpy(s,data,cnt); s[cnt] = 0; } } accept_complete = 1; printf("%s\r\n",strTmp); #ifdef FORWARD if (forward_flag #ifdef COMMENT /* Marc Horowitz has successfully argued that it is indeed safe to send Forwarded credentials to an untrusted host. */ && (auth_how & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL #endif /* COMMENT */ ) kerberos5_forward(); #endif /* FORWARD */ krb5_errno = 0; makestr(&krb5_errmsg,strTmp); auth_finished(AUTH_USER); return AUTH_SUCCESS; case KRB5_RESPONSE: #ifdef TLS_VERIFY if ((how & AUTH_ENCRYPT_MASK) == AUTH_ENCRYPT_START_TLS && !krb5_tls_verified) { printf( "Man in the middle attack detected. Session terminated.\r\n"); #ifndef BETATEST netclos(); #endif /* BETATEST */ krb5_errno = -1; makestr(&krb5_errmsg,"TLS not verified"); auth_finished(AUTH_REJECT); return AUTH_FAILURE; } if((ssl_active_flag || tls_active_flag) && (how & AUTH_ENCRYPT_MASK) == AUTH_ENCRYPT_START_TLS) { printf("TLS session parameters verified by Kerberos 5\r\n"); } #endif /* TLS_VERIFY */ if ((how & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) { /* the rest of the reply should contain a krb_ap_rep */ krb5_ap_rep_enc_part *reply; krb5_data inbuf; krb5_error_code r; inbuf.length = cnt; inbuf.data = (char *)data; if (r = krb5_rd_rep(k5_context, auth_context, &inbuf, &reply)) { com_err(NULL, r, "while authorizing. (5)"); krb5_errno = r; makestr(&krb5_errmsg,error_message(krb5_errno)); auth_finished(AUTH_REJECT); return AUTH_FAILURE; } krb5_free_ap_rep_enc_part(k5_context, reply); #ifdef CK_ENCRYPTION if (encrypt_flag && k5_session_key) { if ( tn_auth_krb5_des_bug ) { /* !ALLOW_KRB_3DES_ENCRYPT */ skey.type = SK_DES; skey.length = 8; #ifdef HEIMDAL skey.data = k5_session_key->keyvalue.data; #else /* HEIMDAL */ skey.data = k5_session_key->contents; #endif /* HEIMDAL */ } else { #ifdef HEIMDAL switch ( k5_session_key->keytype ) { case ETYPE_DES_CBC_CRC: case ETYPE_DES_CBC_MD5: case ETYPE_DES_CBC_MD4: skey.type = SK_DES; skey.length = 8; default: skey.type = SK_GENERIC; skey.length = k5_session_key->length; } skey.data = k5_session_key->keyvalue.data; #else /* HEIMDAL */ switch ( k5_session_key->enctype ) { case ENCTYPE_DES_CBC_CRC: case ENCTYPE_DES_CBC_MD5: case ENCTYPE_DES_CBC_MD4: skey.type = SK_DES; skey.length = 8; break; default: skey.type = SK_GENERIC; skey.length = k5_session_key->length; } skey.data = k5_session_key->contents; #endif /* HEIMDAL */ } encrypt_session_key(&skey, AUTH_CLIENT_TO_SERVER); } #endif /* ENCRYPTION */ mutual_complete = 1; } ckstrncpy(strTmp,"Remote machine has been mutually authenticated", sizeof(strTmp)); krb5_errno = 0; makestr(&krb5_errmsg,strTmp); printf("%s\r\n",strTmp); auth_finished(AUTH_USER); return AUTH_SUCCESS; #ifdef FORWARD case KRB5_FORWARD_ACCEPT: forwarded_tickets = 1; ckstrncpy(strTmp,"Remote machine has accepted forwarded credentials", sizeof(strTmp)); krb5_errno = 0; makestr(&krb5_errmsg,strTmp); printf("%s\r\n",strTmp); return AUTH_SUCCESS; case KRB5_FORWARD_REJECT: forwarded_tickets = 0; if (cnt > 0) { char *s; int len; len = ckstrncpy(strTmp, "Kerberos V5 refuses forwarded credentials because ", sizeof(strTmp)); if ( len + cnt < sizeof(strTmp) ) { s = strTmp + strlen(strTmp); memcpy(s, data, cnt); s[cnt] = 0; } } else ckstrncpy(strTmp, "Kerberos V5 refuses forwarded credentials", sizeof(strTmp)); printf("%s\r\n",strTmp); krb5_errno = -1; makestr(&krb5_errmsg,strTmp); return AUTH_SUCCESS; #endif /* FORWARD */ #ifdef TLS_VERIFY case KRB5_TLS_VERIFY: if ((how & AUTH_ENCRYPT_MASK) == AUTH_ENCRYPT_START_TLS) { krb5_data reply, msg; char tls_verify[24]; krb5_replay_data repdata; krb5_error_code r; ssl_get_server_finished(&tls_verify[0],12); ssl_get_client_finished(&tls_verify[12],12); reply.data = (char *)data; reply.length = cnt; krb5_auth_con_genaddrs(k5_context, auth_context, ttyfd, KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR); if (r = krb5_rd_safe(k5_context,auth_context,&reply,&msg,&repdata)) { com_err("", r, "decoding tls verifier"); krb5_errno = r; makestr(&krb5_errmsg,"TLS verify failure"); auth_finished(AUTH_REJECT); return(AUTH_FAILURE); } if ( msg.length == 24 && !memcmp(msg.data,tls_verify,24) ) krb5_tls_verified = 1; krb5_free_data_contents(k5_context,&msg); if (krb5_tls_verified) return(AUTH_SUCCESS); } printf("Man in the middle attack detected. Session terminated.\r\n"); netclos(); krb5_errno = -1; makestr(&krb5_errmsg,"TLS verify failure"); auth_finished(AUTH_REJECT); return(AUTH_FAILURE); #endif /* CK_SSL */ default: krb5_errno = -1; makestr(&krb5_errmsg,"Unknown reply type"); auth_finished(AUTH_REJECT); return AUTH_FAILURE; /* Unknown reply type */ } } #ifdef FORWARD /* Decode, decrypt and store the forwarded creds in the local ccache. */ /* Needed for KRB5_FORWARD */ static krb5_error_code rd_and_store_for_creds(context, auth_context, inbuf, client) krb5_context context; krb5_auth_context auth_context; krb5_data *inbuf; krb5_const_principal client; { krb5_creds ** creds=NULL; krb5_error_code retval; krb5_ccache ccache=NULL; #ifdef HEIMDAL /* Heimdal Telnetd creates the cache file at this point and sets the KRB5CCNAME environment variable. struct passwd *pwd; char ccname[1024]; pwd = getpwnam(szUserNameRequested); if (pwd == NULL) break; snprintf(ccname, sizeof(ccname)-1, "FILE:/tmp/krb5cc_%u",pwd->pw_uid); retval = krb5_cc_resolve(context,ccname,&ccache); chown(ccname + 5, pwd->pw_uid, -1); */ #endif /* HEIMDAL */ if (retval = k5_get_ccache(context,&ccache,NULL)) return(retval); #ifdef HEIMDAL if ((retval = krb5_cc_initialize(context, ccache, client))) return(retval); if ((retval = krb5_rd_cred(context, auth_context, ccache, inbuf))) return(retval); #else /* HEIMDAL */ if ((retval = krb5_rd_cred(context, auth_context, inbuf, &creds, NULL))) return(retval); if ((retval = krb5_cc_initialize(context, ccache, creds[0]->client))) goto cleanup; if ((retval = krb5_cc_store_cred(context, ccache, creds[0]))) goto cleanup; if ((retval = krb5_cc_close(context, ccache))) goto cleanup; cleanup: krb5_free_tgt_creds(context, creds); #endif /* HEIMDAL */ return retval; } #endif /* FORWARD */ /* * * K5_auth_is. * */ static int #ifdef CK_ANSIC k5_auth_is(int how, unsigned char *data, int cnt) #else k5_auth_is(how,data,cnt) int how; unsigned char *data; int cnt; #endif { int r = 0; krb5_principal server; krb5_keyblock *newkey = NULL; krb5_data outbuf; char errbuf[128]=""; char *getenv(); #ifndef HEIMDAL krb5_authenticator *authenticator; krb5_keytab keytabid = 0; #endif /* HEIMDAL */ krb5_data inbuf; #ifdef CK_ENCRYPTION Session_Key skey; #endif /* CK_ENCRYPTION */ char princ[256]=""; int len; data += 4; /* Point to status byte */ cnt -= 4; ckhexdump("k5_auth_is data",data,cnt); debug(F111,"k5_auth_is","how",how); if (cnt-- < 1) { auth_finished(AUTH_REJECT); return AUTH_FAILURE; } switch (*data++) { case KRB_AUTH: k5_auth.data = (char *)data; k5_auth.length = cnt; debug(F110,"k5_auth_is","KRB_AUTH",0); debug(F111,"k5_auth_is","auth_context",auth_context); if (!r && !auth_context) { r = krb5_auth_con_init(k5_context, &auth_context); debug(F111,"k5_auth_is","krb5_auth_con_init",r); } #ifdef HEIMDAL if (!r) r = krb5_auth_con_setaddrs_from_fd(k5_context, auth_context, &ttyfd); if (!r) r = krb5_sock_to_principal(k5_context, 0, "host", KRB5_NT_SRV_HST, &server); if (!r) #else /* HEIMDAL */ if (!r) { krb5_rcache rcache = NULL; r = krb5_auth_con_getrcache(k5_context, auth_context, &rcache); debug(F111,"k5_auth_is","krb5_auth_con_getrcache",r); if (!r && !rcache) { /* Do not resolve server's principal name, we will check */ /* for validity after the krb5_rd_req() call. */ r = krb5_sname_to_principal(k5_context, 0, 0, KRB5_NT_SRV_HST, &server); debug(F111,"k5_auth_is","krb5_sname_to_principal",r); if (!r) { r = krb5_get_server_rcache(k5_context, krb5_princ_component(k5_context, server, 0), &rcache); debug(F111,"k5_auth_is","krb5_get_server_rcache",r); krb5_free_principal(k5_context, server); } } if (!r) { r = krb5_auth_con_setrcache(k5_context, auth_context, rcache); debug(F111,"k5_auth_is","krb5_auth_con_setrcache",r); } } if (!r && k5_keytab) { r = krb5_kt_resolve(k5_context, k5_keytab, &keytabid); debug(F111,"k5_auth_is","krb5_kt_resolve",r); } #endif /* HEIMDAL */ if (!r) { r = krb5_rd_req(k5_context, &auth_context, &k5_auth, #ifdef HEIMDAL server, NULL, NULL, #else /* HEIMDAL */ NULL, keytabid, NULL, #endif /* HEIMDAL */ &k5_ticket); debug(F111,"k5_auth_is","krb5_rd_req",r); } if (r) { (void) ckstrncpy(errbuf, "krb5_rd_req failed: ",sizeof(errbuf)); (void) ckstrncat(errbuf, error_message(r),sizeof(errbuf)); goto errout; } #ifdef HEIMDAL krb5_free_principal(k5_context, server); { char type_check[26]; /* UPDATE for START_TLS. AUTH_ENCRYPT_START_TLS and inclusion of */ /* client and then server finished messages. */ type_check[0] = AUTHTYPE_KERBEROS_V5; type_check[1] = how; /* not broken into parts */ #ifdef CK_SSL if ((how & AUTH_ENCRYPT_MASK) == AUTH_ENCRYPT_START_TLS) { ssl_get_client_finished(&type_check[2],12); ssl_get_server_finished(&type_check[14],12); ckhexdump("k5_auth_is type_check",type_check,26); } #endif /* CK_SSL */ r = krb5_verify_authenticator_checksum(k5_context, auth_context, type_check, #ifdef CK_SSL ((how & AUTH_ENCRYPT_MASK) == AUTH_ENCRYPT_START_TLS) ? 26 : #endif /* CK_SSL */ 2); } #else /* HEIMDAL */ len = krb5_princ_component(k5_context,k5_ticket->server,0)->length; if (len < 256) { memcpy(princ, krb5_princ_component(k5_context,k5_ticket->server,0)->data, len); princ[len] = '\0'; } if ( strcmp((krb5_d_srv ? krb5_d_srv : KRB5_SERVICE_NAME), princ) ) { debug(F110,"k5_auth_is incorrect service name",princ,0); ckstrncpy(errbuf,"incorrect service name: ",sizeof(errbuf)); ckstrncat(errbuf,krb5_d_srv ? krb5_d_srv : KRB5_SERVICE_NAME, sizeof(errbuf)); ckstrncat(errbuf," != ",sizeof(errbuf)); ckstrncat(errbuf,princ,sizeof(errbuf)); goto errout; } r = krb5_auth_con_getauthenticator(k5_context, auth_context, &authenticator); debug(F111,"k5_auth_is","krb5_auth_con_getauthenticator",r); if (r) { (void) ckstrncpy(errbuf, "krb5_auth_con_getauthenticator failed: ", sizeof(errbuf) ); (void) ckstrncat(errbuf, error_message(r),sizeof(errbuf)); goto errout; } if (authenticator->checksum) { char type_check[26]; krb5_checksum *cksum = authenticator->checksum; krb5_keyblock *key; /* UPDATE for START_TLS. AUTH_ENCRYPT_START_TLS and inclusion of */ /* client and then server finished messages. */ type_check[0] = AUTHTYPE_KERBEROS_V5; type_check[1] = how; /* not broken into parts */ #ifdef CK_SSL if ((how & AUTH_ENCRYPT_MASK) == AUTH_ENCRYPT_START_TLS) { ssl_get_client_finished(&type_check[2],12); ssl_get_server_finished(&type_check[14],12); ckhexdump("k5_auth_is type_check",type_check,26); } #endif /* CK_SSL */ r = krb5_auth_con_getkey(k5_context, auth_context, &key); debug(F111,"k5_auth_is","krb5_auth_con_getkey",r); if (r) { (void) ckstrncpy(errbuf, "krb5_auth_con_getkey failed: ", sizeof(errbuf)); (void) ckstrncat(errbuf, error_message(r),sizeof(errbuf)); goto errout; } r = krb5_verify_checksum(k5_context, cksum->checksum_type, cksum, &type_check, ((how & AUTH_ENCRYPT_MASK) == AUTH_ENCRYPT_START_TLS) ? 26 : 2, key->contents, key->length ); debug(F111,"k5_auth_is","krb5_verify_checksum",r); if (r) { (void) ckstrncpy(errbuf, "checksum verification failed: ", sizeof(errbuf) ); (void) ckstrncat(errbuf, error_message(r),sizeof(errbuf)); goto errout; } krb5_free_keyblock(k5_context, key); } else { if ((how & AUTH_ENCRYPT_MASK) == AUTH_ENCRYPT_USING_TELOPT) { (void) strcpy(errbuf, "authenticator is missing required checksum"); goto errout; } } krb5_free_authenticator(k5_context, authenticator); #endif /* HEIMDAL */ #ifdef TLS_VERIFY if ((how & AUTH_ENCRYPT_MASK) == AUTH_ENCRYPT_START_TLS) { krb5_data in, msg; char tls_verify[24]; krb5_replay_data repdata; ssl_get_server_finished(&tls_verify[0],12); ssl_get_client_finished(&tls_verify[12],12); in.data = tls_verify; in.length = 24; krb5_auth_con_genaddrs(k5_context, auth_context, ttyfd, KRB5_AUTH_CONTEXT_GENERATE_LOCAL_FULL_ADDR); if (r = krb5_mk_safe(k5_context,auth_context,&in,&msg,&repdata)) { com_err("", r, "encoding tls verifier"); (void) ckstrncat(errbuf, error_message(r),sizeof(errbuf)); goto errout; } SendK5AuthSB(KRB5_TLS_VERIFY, msg.data, msg.length); krb5_free_data_contents(k5_context,&msg); } #endif /* CK_SSL */ if ((how & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) { /* do ap_rep stuff here */ if ((r = krb5_mk_rep(k5_context, #ifdef HEIMDAL &auth_context, #else /* HEIMDAL */ auth_context, #endif /* HEIMDAL */ &outbuf))) { debug(F111,"k5_auth_is","krb5_mk_rep",r); (void) ckstrncpy(errbuf, "Make reply failed: ",sizeof(errbuf)); (void) ckstrncat(errbuf, error_message(r),sizeof(errbuf)); goto errout; } debug(F111,"k5_auth_is","krb5_mk_rep",r); SendK5AuthSB(KRB5_RESPONSE, outbuf.data, outbuf.length); mutual_complete = 1; } #ifdef HEIMDAL { char * name = NULL; if (krb5_unparse_name(k5_context, k5_ticket->client, &name)) { szUserNameAuthenticated[0] = '\0'; } else { ckstrncpy(szUserNameAuthenticated,UIDBUFLEN,name); free(name); } } #else /* HEIMDAL */ if ( krb5_aname_to_localname(k5_context, k5_ticket->enc_part2->client, UIDBUFLEN,szUserNameAuthenticated) ) szUserNameAuthenticated[0] = '\0'; #endif /* HEIMDAL */ SendK5AuthSB(KRB_ACCEPT, szUserNameAuthenticated, szUserNameAuthenticated[0] ? -1 : 0); accept_complete = 1; ckmakmsg(strTmp,sizeof(strTmp), "Kerberos5 identifies him as ``", szUserNameAuthenticated,"''",NULL); printf("%s\r\n",strTmp); if (szUserNameRequested[0] && krb5_kuserok(k5_context, #ifdef HEIMDAL k5_ticket->client, #else /* HEIMDAL */ k5_ticket->enc_part2->client, #endif /* HEIMDAL */ szUserNameRequested)) auth_finished(AUTH_VALID); else auth_finished(AUTH_USER); krb5_auth_con_getremotesubkey(k5_context, auth_context, &newkey); if (k5_session_key) { krb5_free_keyblock(k5_context, k5_session_key); k5_session_key = 0; } if (newkey) { krb5_copy_keyblock(k5_context, newkey, &k5_session_key); krb5_free_keyblock(k5_context, newkey); } else { krb5_copy_keyblock(k5_context, #ifdef HEIMDAL &k5_ticket->ticket.key, #else /* HEIMDAL */ k5_ticket->enc_part2->session, #endif /* HEIMDAL */ &k5_session_key); } #ifdef CK_ENCRYPTION #ifdef HEIMDAL skey.type = k5_session_key->keyvalue.length == 8 ? SK_DES : SK_GENERIC; skey.length = k5_session_key->keyvalue.length; skey.data = k5_session_key->keyvalue.data; #else /* HEIMDAL */ skey.type = k5_session_key->length == 8 ? SK_DES : SK_GENERIC; skey.length = k5_session_key->length; skey.data = k5_session_key->contents; #endif /* HEIMDAL */ encrypt_session_key(&skey, AUTH_SERVER_TO_CLIENT); #endif /* CK_ENCRYPTION */ debug(F100,"k5_auth_is AUTH_SUCCESS","",0); krb5_errno = r; if ( krb5_errno ) makestr(&krb5_errmsg,error_message(krb5_errno)); else makestr(&krb5_errmsg,strTmp); return AUTH_SUCCESS; #ifdef FORWARD case KRB5_FORWARD: if ( !forward_flag ) { SendK5AuthSB(KRB5_FORWARD_REJECT, "forwarded credentials are being refused.", -1); return(AUTH_SUCCESS); } inbuf.length = cnt; inbuf.data = (char *)data; if ( #ifndef HEIMDAL (!krb5_d_no_addresses && (r = krb5_auth_con_genaddrs(k5_context,auth_context,g_kstream->fd, KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR))) || #endif /* HEIMDAL */ (r = rd_and_store_for_creds(k5_context, auth_context,&inbuf, #ifdef HEIMDAL k5_ticket->client #else /* HEIMDAL */ k5_ticket->enc_part2->client #endif /* HEIMDAL */ ))) { (void) ckstrncpy(errbuf, "Read forwarded creds failed: ", sizeof(errbuf)); (void) ckstrncat(errbuf, error_message(r),sizeof(errbuf)); SendK5AuthSB(KRB5_FORWARD_REJECT, errbuf, -1); printf("Could not read forwarded credentials\r\n"); krb5_errno = r; makestr(&krb5_errmsg,error_message(krb5_errno)); } else { SendK5AuthSB(KRB5_FORWARD_ACCEPT, 0, 0); ckstrncpy(strTmp,"Forwarded credentials obtained",sizeof(strTmp)); printf("%s\r\n",strTmp); krb5_errno = 0; makestr(&krb5_errmsg,strTmp); } /* A failure to accept forwarded credentials is not an */ /* authentication failure. */ return AUTH_SUCCESS; #endif /* FORWARD */ default: printf("Unknown Kerberos option %d\r\n", data[-1]); SendK5AuthSB(KRB_REJECT, 0, 0); break; } auth_finished(AUTH_REJECT); return AUTH_FAILURE; errout: SendK5AuthSB(KRB_REJECT, errbuf, -1); krb5_errno = r; makestr(&krb5_errmsg,errbuf); printf("%s\r\n", errbuf); if (auth_context) { krb5_auth_con_free(k5_context, auth_context); auth_context = 0; } auth_finished(AUTH_REJECT); return AUTH_FAILURE; } #ifdef FORWARD int #ifdef CK_ANSIC kerberos5_forward(void) #else kerberos5_forward() #endif { krb5_error_code r; krb5_ccache ccache=NULL; krb5_principal client = 0; krb5_principal server = 0; krb5_data forw_creds; #ifdef HEIMDAL krb5_creds creds; #endif /* HEIMDAL */ forw_creds.data = 0; r = k5_get_ccache(k5_context,&ccache,NULL); if ( r ) { com_err(NULL, r, "Kerberos V5: could not get default ccache"); krb5_errno = r; makestr(&krb5_errmsg,error_message(krb5_errno)); return(AUTH_FAILURE); } if ((r = krb5_cc_get_principal(k5_context, ccache, &client))) { com_err(NULL, r, "Kerberos V5: could not get default principal"); goto cleanup; } #ifdef HEIMDAL memset(&creds, 0, sizeof(creds)); creds.client = client; if (r = krb5_build_principal(k5_context, &creds.server, strlen(client->realm), client->realm, "krbtgt", client->realm, NULL)) { com_err(NULL, r, "Kerberos V5: could not get principal"); goto cleanup; } creds.times.endtime = 0; if (r = krb5_get_forwarded_creds(k5_context, auth_context, ccache, 0, szHostName, &creds, &forw_creds)) { com_err(NULL, r, "Kerberos V5: error getting forwarded creds"); goto cleanup; } #else /* HEIMDAL */ /* we should not need to make this call since we are storing the */ /* server's principal in fwd_server from our call to */ /* krb5_sname_to_principal() in k5_auth_send() */ if (fwd_server == NULL) { if ((r = krb5_sname_to_principal(k5_context, szHostName, krb5_d_srv ? krb5_d_srv : KRB5_SERVICE_NAME, KRB5_NT_SRV_HST, &server))) { com_err(NULL, r, "Kerberos V5: could not make server principal"); goto cleanup; } } if (!krb5_d_no_addresses && (r = krb5_auth_con_genaddrs(k5_context, auth_context, g_kstream->fd, KRB5_AUTH_CONTEXT_GENERATE_LOCAL_FULL_ADDR))) { com_err(NULL, r, "Kerberos V5: could not gen local full address"); goto cleanup; } if (r = krb5_fwd_tgt_creds(k5_context, auth_context, 0, client, fwd_server ? fwd_server : server, ccache, forwardable_flag, &forw_creds)) { com_err(NULL, r, "Kerberos V5: error getting forwardable credentials"); goto cleanup; } #endif /* HEIMDAL */ /* Send forwarded credentials */ if (!SendK5AuthSB(KRB5_FORWARD, forw_creds.data, forw_creds.length)) { printf("Kerberos V5 forwarding error!\r\n%s\r\n", "Not enough room for authentication data"); } cleanup: if (client) krb5_free_principal(k5_context, client); if (server) krb5_free_principal(k5_context, server); #ifdef HEIMDAL krb5_data_free(&forw_creds); #else /* HEIMDAL */ krb5_free_data_contents(k5_context,&forw_creds); #endif /* HEIMDAL */ krb5_cc_close(k5_context, ccache); krb5_errno = r; makestr(&krb5_errmsg,krb5_errno?error_message(krb5_errno):"OK"); return(r?AUTH_FAILURE:AUTH_SUCCESS); } #endif /* FORWARD */ #else /* KRB5 */ int ck_krb5_autoget_TGT(char * dummy) { return(0); } #ifdef CK_KERBEROS int #ifdef CK_ANSIC ck_krb5_initTGT( struct krb_op_data * op, struct krb5_init_data * init, struct krb4_init_data * k4_init) #else ck_krb5_initTGT(op,init,k4_init) krb_op_data * op; struct krb5_init_data * init; struct krb4_init_data * k4_init; #endif /* CK_ANSIC*/ { return(-1); } int #ifdef CK_ANSIC ck_krb5_destroy(struct krb_op_data * op) #else ck_krb5_destroy(op) struct krb_op_data * op; #endif { return(-1); } int #ifdef CK_ANSIC ck_krb5_list_creds(struct krb_op_data * op, struct krb5_list_cred_data * lc) #else ck_krb5_list_creds(op,lc) struct krb_op_data * op; struct krb5_list_cred_data * lc; #endif { return(-1); } #else /* CK_KERBEROS */ int #ifdef CK_ANSIC ck_krb5_initTGT(void * op, void * init, void * k4_init ) #else ck_krb5_initTGT(op,init,k4_init) void * op; void * init; void * k4_init; #endif /* CK_ANSIC*/ { return(-1); } int #ifdef CK_ANSIC ck_krb5_destroy(void * op) #else ck_krb5_destroy(op) void * op; #endif { return(-1); } int #ifdef CK_ANSIC ck_krb5_list_creds(void * op, void * lc) #else ck_krb5_list_creds(op,lc) void * op; void * lc; #endif { return(-1); } #endif /* CK_KERBEROS */ #endif /* KRB5 */ #ifdef GSSAPI_KRB5 /* * * gssk5_auth_send - gets authentication bits we need to send to KDC. * * Result is left in k5_auth * * Returns: 0 on failure, 1 on success * */ static int #ifdef CK_ANSIC gssk5_auth_send(int how, int encrypt, int forward) #else gssk5_auth_send(how,encrypt,forward) int how; int encrypt; int forward; #endif { OM_uint32 maj_stat, min_stat; #ifdef KRB5 char * realm = NULL; char tgt[256]; #endif /* KRB5 */ gss_chan.initiator_addrtype = GSS_C_AF_INET; /* OM_uint32 */ gss_chan.initiator_address.length = 4; gss_chan.initiator_address.value = &myctladdr.sin_addr.s_addr; gss_chan.acceptor_addrtype = GSS_C_AF_INET; /* OM_uint32 */ gss_chan.acceptor_address.length = 4; gss_chan.acceptor_address.value = &hisctladdr.sin_addr.s_addr; gss_chan.application_data.length = 0; gss_chan.application_data.value = 0; #ifdef KRB5 realm = ck_krb5_realmofhost(ftp_host); if (realm) { ckmakmsg(tgt,sizeof(tgt),"krbtgt/",realm,"@",realm); debug(F110,"ftp_auth(GSSAPI) TGT",tgt,0); if ( krb5_autoget && !((ck_krb5_tkt_isvalid(NULL,tgt) > 0) || (ck_krb5_is_tgt_valid() > 0)) ) ck_krb5_autoget_TGT(realm); } #endif /* KRB5 */ /* Blob from gss-client */ /* host@hostname */ /* the V5 GSSAPI binding canonicalizes this for us... */ ckmakmsg(gss_stbuf,GSS_BUFSIZ, krb5_d_srv ? krb5_d_srv : KRB5_SERVICE_NAME, "@", szHostName, NULL ); fprintf(stderr, "Authenticating to <%s>...\n", gss_stbuf); gss_send_tok.value = gss_stbuf; gss_send_tok.length = strlen(gss_stbuf); maj_stat = gss_import_name(&min_stat, &gss_send_tok, gss_nt_service_name, &gss_target_name ); if (maj_stat != GSS_S_COMPLETE) { user_gss_error(maj_stat, min_stat, "parsing name"); secure_error("name parsed <%s>\n", gss_stbuf); return(0); } token_ptr = GSS_C_NO_BUFFER; gcontext = GSS_C_NO_CONTEXT; /* structure copy */ fprintf(stderr, "calling gss_init_sec_context\n"); maj_stat = gss_init_sec_context(&min_stat, GSS_C_NO_CREDENTIAL, &gcontext, gss_target_name, gss_mech_krb5, GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | ((forward && forward_flag) ? GSS_C_DELEG_FLAG : 0), 0, (krb5_d_no_addresses ? /* channel bindings */ GSS_C_NO_CHANNEL_BINDINGS : &gss_chan), gss_token_ptr, NULL, /* ignore mech type */ &gss_send_tok, NULL, /* ignore ret_flags */ NULL ); /* ignore time_rec */ if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED) { user_gss_error(maj_stat, min_stat, "initializing context" ); gss_release_name(&min_stat, &gss_target_name); return(0); } return(1); } /* * gssk5_auth_reply -- checks the reply for mutual authentication. */ static int #ifdef CK_ANSIC gssk5_auth_reply(int how, unsigned char *data, int cnt) #else gssk5_auth_reply(how,data,cnt) int how; unsigned char *data; int cnt; #endif { data += 4; /* Point to status byte */ cnt -=5; switch (*data++) { case GSS_REJECT: if (cnt > 0) { char *s; int len; ckstrncpy(strTmp,"GSSAPI refuses authentication because\r\n", sizeof(strTmp)); len = strlen(strTmp); if ( len + cnt < sizeof(strTmp) ) { s = strTmp + strlen(strTmp); memcpy(s, data, cnt); /* safe */ s[cnt] = 0; } } else ckstrncpy(strTmp,"GSSAPI refuses authentication", sizeof(strTmp)); printf("GSSAPI authentication failed!\r\n%s\r\n",strTmp); auth_finished(AUTH_REJECT); return AUTH_FAILURE; case GSS_ACCEPT: if ( cnt > 0 ) { char *s; int len; ckstrncpy(strTmp,"GSSAPI accepts you as ",sizeof(strTmp)); len = strlen(strTmp); if ( len + cnt < sizeof(strTmp) ) { s = strTmp + strlen(strTmp); memcpy(s,data,cnt); s[cnt] = 0; } } accept_complete = 1; printf("%s\r\n",strTmp); auth_finished(AUTH_USER); return AUTH_SUCCESS; case GSS_RESPONSE: gss_token_ptr = &gss_recv_tok; gss_recv_tok.value = data; gss_recv_tok.length = cnt; maj_stat = gss_init_sec_context(&min_stat, GSS_C_NO_CREDENTIAL, &gcontext, gss_target_name, gss_krb5_mech, GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | (forward_flag ? GSS_C_DELEG_FLAG : 0), 0, (krb5_d_no_addresses ? /* channel bindings */ GSS_C_NO_CHANNEL_BINDINGS : &gss_chan), gss_token_ptr, NULL, /* ignore mech type */ &gss_send_tok, NULL, /* ignore ret_flags */ NULL ); /* ignore time_rec */ if ( maj_stat == GSS_S_COMPLETE ) { } else if ( maj_stat == CSS_S_CONTINUE_NEEDED ) { } else { } ckstrncpy(strTmp,"Remote machine has been mutually authenticated", sizeof(strTmp)); printf("%s\r\n",strTmp); auth_finished(AUTH_USER); return AUTH_SUCCESS; default: auth_finished(AUTH_REJECT); return AUTH_FAILURE; /* Unknown reply type */ } } /* * * gssk5_auth_is. * */ static int #ifdef CK_ANSIC k5_auth_is(int how, unsigned char *data, int cnt) #else k5_auth_is(how,data,cnt) int how; unsigned char *data; int cnt; #endif { int replied = 0; gss_cred_id_t server_creds, deleg_creds; gss_name_t client; int ret_flags; gss_buffer_desc name_buf; gss_name_t server_name; OM_uint32 acquire_maj, acquire_min, accept_maj, accept_min, stat_maj, stat_min; gss_OID mechid; gss_buffer_desc tok, out_tok; char gbuf[GSS_BUFSIZ]; u_char gout_buf[GSS_BUFSIZ]; char localname[MAXHOSTNAMELEN]; char service_name[MAXHOSTNAMELEN+10]; char **service; struct hostent *hp; data += 4; /* Point to status byte */ cnt -= 4; ckhexdump("gssk5_auth_is data",data,cnt); debug(F111,"gssk5_auth_is","how",how); if (cnt-- < 1) { auth_finished(AUTH_REJECT); return AUTH_FAILURE; } switch (*data++) { case GSS_AUTH: gss_chan.initiator_addrtype = GSS_C_AF_INET; gss_chan.initiator_address.length = 4; gss_chan.initiator_address.value = &his_addr.sin_addr.s_addr; gss_chan.acceptor_addrtype = GSS_C_AF_INET; gss_chan.acceptor_address.length = 4; gss_chan.acceptor_address.value = &ctrl_addr.sin_addr.s_addr; gss_chan.application_data.length = 0; gss_chan.application_data.value = 0; tok.value = data; tok.length = cnt; if (gethostname(localname, MAXHOSTNAMELEN)) { auth_finished(AUTH_REJECT); return AUTH_FAILURE; } if (!(hp = gethostbyname(localname))) { auth_finished(AUTH_REJECT); return AUTH_FAILURE; } #ifdef HADDRLIST hp = ck_copyhostent(hp); #endif /* HADDRLIST */ strncpy(localname, hp->h_name, sizeof(localname) - 1); localname[sizeof(localname) - 1] = '\0'; sprintf(service_name, "%s@%s", *service, localname); name_buf.value = service_name; name_buf.length = strlen(name_buf.value) + 1; stat_maj = gss_import_name(&stat_min, &name_buf, gss_nt_service_name, &server_name); if (stat_maj != GSS_S_COMPLETE) { auth_finished(AUTH_REJECT); return AUTH_FAILURE; } acquire_maj = gss_acquire_cred(&acquire_min, server_name, 0, GSS_C_NULL_OID_SET, GSS_C_ACCEPT, &server_creds, NULL, NULL); (void) gss_release_name(&stat_min, &server_name); if (acquire_maj != GSS_S_COMPLETE) { reply_gss_error(535, accept_maj, accept_min, "accepting context"); syslog(LOG_ERR, "failed accepting context"); (void) gss_release_cred(&stat_min, &server_creds); if (ret_flags & GSS_C_DELEG_FLAG) (void) gss_release_cred(&stat_min, &deleg_creds); return 0; } gcontext = GSS_C_NO_CONTEXT; accept_maj = gss_accept_sec_context(&accept_min, &gcontext, /* context_handle */ /* verifier_cred_handle */ server_creds, &tok, /* input_token */ (krb5_d_no_addresses ? /* channel bindings */ GSS_C_NO_CHANNEL_BINDINGS : &gss_chan), &client, /* src_name */ &mechid, /* mech_type */ &out_tok, /* output_token */ &ret_flags, NULL, /* ignore time_rec */ /* forwarded credentials */ &deleg_creds ); if (accept_maj!=GSS_S_COMPLETE && accept_maj!=GSS_S_CONTINUE_NEEDED) { reply_gss_error(535, accept_maj, accept_min, "accepting context"); syslog(LOG_ERR, "failed accepting context"); (void) gss_release_cred(&stat_min, &server_creds); if (ret_flags & GSS_C_DELEG_FLAG) (void) gss_release_cred(&stat_min, &deleg_creds); return 0; } if (out_tok.length) { if (kerror = radix_encode(out_tok.value,gbuf,&out_tok.length, 0)) { secure_error("Couldn't encode ADAT reply (%s)", radix_error(kerror)); syslog(LOG_ERR, "couldn't encode ADAT reply"); (void) gss_release_cred(&stat_min, &server_creds); if (ret_flags & GSS_C_DELEG_FLAG) (void) gss_release_cred(&stat_min, &deleg_creds); return(0); } if (stat_maj == GSS_S_COMPLETE) { reply(235, "ADAT=%s", gbuf); replied = 1; } else { /* If the server accepts the security data, and requires additional data, it should respond with reply code 335. */ reply(335, "ADAT=%s", gbuf); } (void) gss_release_buffer(&stat_min, &out_tok); } if (stat_maj == GSS_S_COMPLETE) { /* GSSAPI authentication succeeded */ stat_maj = gss_display_name(&stat_min, client, &client_name, &mechid); if (stat_maj != GSS_S_COMPLETE) { /* "If the server rejects the security data (if a checksum fails, for instance), it should respond with reply code 535." */ reply_gss_error(535, stat_maj, stat_min, "extracting GSSAPI identity name"); syslog(LOG_ERR, "gssapi error extracting identity"); (void) gss_release_cred(&stat_min, &server_creds); if (ret_flags & GSS_C_DELEG_FLAG) (void) gss_release_cred(&stat_min, &deleg_creds); return 0; } auth_type = temp_auth_type; temp_auth_type = NULL; (void) gss_release_cred(&stat_min, &server_creds); if (ret_flags & GSS_C_DELEG_FLAG) { if (want_creds) ftpd_gss_convert_creds(client_name.value, deleg_creds); (void) gss_release_cred(&stat_min, &deleg_creds); } /* If the server accepts the security data, but does not require any additional data (i.e., the security data exchange has completed successfully), it must respond with reply code 235. */ if (!replied) { if (ret_flags & GSS_C_DELEG_FLAG && !have_creds) reply(235, "GSSAPI Authentication succeeded, but could not accept forwarded credentials" ); else reply(235, "GSSAPI Authentication succeeded"); } return(1); } else if (stat_maj == GSS_S_CONTINUE_NEEDED) { /* If the server accepts the security data, and requires additional data, it should respond with reply code 335. */ reply(335, "more data needed"); (void) gss_release_cred(&stat_min, &server_creds); if (ret_flags & GSS_C_DELEG_FLAG) (void) gss_release_cred(&stat_min, &deleg_creds); return(0); } else { /* "If the server rejects the security data (if a checksum fails, for instance), it should respond with reply code 535." */ reply_gss_error(535, stat_maj, stat_min, "GSSAPI failed processing ADAT"); syslog(LOG_ERR, "GSSAPI failed processing ADAT"); (void) gss_release_cred(&stat_min, &server_creds); if (ret_flags & GSS_C_DELEG_FLAG) (void) gss_release_cred(&stat_min, &deleg_creds); return(0); } debug(F100,"gssk5_auth_is AUTH_SUCCESS","",0); krb5_errno = r; if ( krb5_errno ) makestr(&krb5_errmsg,error_message(krb5_errno)); else makestr(&krb5_errmsg,strTmp); return AUTH_SUCCESS; default: printf("Unknown Kerberos option %d\r\n", data[-1]); SendGSSK5AuthSB(GSS_REJECT, 0, 0); break; } auth_finished(AUTH_REJECT); return AUTH_FAILURE; } #endif /* GSSAPI_KRB5 */ #ifdef CK_SRP /* * Copyright (c) 1997 Stanford University * * The use of this software for revenue-generating purposes may require a * license from the owners of the underlying intellectual property. * Specifically, the SRP-3 protocol may not be used for revenue-generating * purposes without a license. * * NOTE: Columbia University has a license. * * Within that constraint, permission to use, copy, modify, and distribute * this software and its documentation for any purpose is hereby granted * without fee, provided that the above copyright notices and this permission * notice appear in all copies of the software and related documentation. * * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. * * IN NO EVENT SHALL STANFORD BE LIABLE FOR ANY SPECIAL, INCIDENTAL, * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ static void srp_encode_length(data, num) unsigned char * data; int num; { *data = (num >> 8) & 0xff; *++data = num & 0xff; } static int srp_decode_length(data) unsigned char * data; { return (((int) *data & 0xff) << 8) | (*(data + 1) & 0xff); } #ifdef PRE_SRP_1_7_3 static int #ifdef CK_ANSIC srp_reply(int how, unsigned char *data, int cnt) #else srp_reply(how,data,cnt) int how; unsigned char *data; int cnt; #endif { struct t_num n; struct t_num g; struct t_num s; struct t_num B; struct t_num * A; char type_check[26]; int pflag; #ifdef CK_ENCRYPTION Session_Key skey; #endif /* ENCRYPTION */ char * str=NULL; data += 4; /* Point to status byte */ cnt -= 4; if(cnt-- < 1) { auth_finished(AUTH_REJECT); return AUTH_FAILURE; } switch(*data++) { case SRP_REJECT: ckmakmsg(strTmp,sizeof(strTmp), "SRP refuses authentication for '",szUserName, "'\r\n",NULL); if (cnt > 0) { int len = strlen(strTmp); if ( len + cnt < sizeof(strTmp) ) { str = strTmp + strlen(strTmp); memcpy(str,data,cnt); str[cnt] = 0; } } printf("SRP authentication failed!\r\n%s\r\n",strTmp); if (tc != NULL) { t_clientclose(tc); tc = NULL; } auth_finished(AUTH_REJECT); return AUTH_FAILURE; case SRP_ACCEPT: if(cnt < RESPONSE_LEN || !srp_waitresp || tc == NULL ) { printf("SRP Protocol error\r\n"); return(auth_resend(AUTHTYPE_SRP)); } srp_waitresp = 0; if(t_clientverify(tc, data) == 0) { printf("SRP accepts you as %s\r\n",szUserName); #ifdef CK_SSL if((ssl_active_flag || tls_active_flag) && (how & AUTH_ENCRYPT_MASK) == AUTH_ENCRYPT_START_TLS) { printf("TLS session parameters verified by SRP\r\n"); } else #endif /* CK_SSL */ #ifdef CK_ENCRYPTION { skey.type = SK_GENERIC; skey.length = SESSION_KEY_LEN; skey.data = tc->session_key; encrypt_session_key(&skey, AUTH_CLIENT_TO_SERVER); } #endif /* ENCRYPTION */ t_clientclose(tc); tc = NULL; accept_complete = 1; auth_finished(AUTH_VALID); return AUTH_SUCCESS; } else { printf("SRP server authentication failed!\r\n"); t_clientclose(tc); tc = NULL; return(auth_resend(AUTHTYPE_SRP)); } break; case SRP_PARAMS: if(!szUserName) { printf("No username available\r\n"); return(auth_resend(AUTHTYPE_SRP)); } n.len = srp_decode_length(data); data += 2; cnt -= 2; if(n.len > cnt) { printf("n too long\r\n"); return(auth_resend(AUTHTYPE_SRP)); } n.data = data; data += n.len; cnt -= n.len; g.len = srp_decode_length(data); data += 2; cnt -= 2; if(g.len > cnt) { printf("g too long\r\n"); return(auth_resend(AUTHTYPE_SRP)); } g.data = data; data += g.len; cnt -= g.len; s.len = srp_decode_length(data); data += 2; cnt -= 2; if(s.len > cnt) { printf("salt too long\r\n"); return(auth_resend(AUTHTYPE_SRP)); } s.data = data; data += s.len; cnt -= s.len; /* If the parameters provided by the server cannot be * validated the following function will fail. */ tc = t_clientopen(szUserName, &n, &g, &s); if (tc == NULL) { printf("SRP parameter initialization error\r\n"); return(auth_resend(AUTHTYPE_SRP)); } A = t_clientgenexp(tc); if(A == NULL) { printf("SRP protocol error\r\n"); return(auth_resend(AUTHTYPE_SRP)); } SendSRPAuthSB(SRP_EXP, A->data, A->len); if ( pwbuf[0] && pwflg ) { printf("SRP using %d-bit modulus for '%s'\r\n", 8 * n.len, szUserName ); ckstrncpy(srp_passwd,pwbuf,sizeof(srp_passwd)); #ifdef OS2 if ( pwcrypt ) ck_encrypt((char *)srp_passwd); #endif /* OS2 */ } else { extern char * srppwprompt; char preface[128]; int ok; if (srppwprompt && srppwprompt[0] && (strlen(srppwprompt) + strlen(szUserName) - 2) < sizeof(preface)) { sprintf(preface,srppwprompt,szUserName); } else { ckmakxmsg( preface,sizeof(preface), "SRP using ",ckitoa(8*n.len),"-bit modulus for '", szUserName, "'", NULL, NULL, NULL, NULL, NULL, NULL, NULL); } ok = uq_txt( preface,"Password: ",2,NULL, srp_passwd,sizeof(srp_passwd)-1,NULL, DEFAULT_UQ_TIMEOUT); if ( !ok ) srp_passwd[0] = '\0'; } t_clientpasswd(tc, srp_passwd); memset(srp_passwd, 0, sizeof(srp_passwd)); return AUTH_SUCCESS; case SRP_CHALLENGE: if(tc == NULL) { printf("SRP protocol error\r\n"); return(auth_resend(AUTHTYPE_SRP)); } #ifndef PRE_SRP_1_4_5 /* * The original SRP AUTH implementation did not protect against * tampering of the auth-type-pairs. Therefore, when the * AUTH_ENCRYPT_MASK bits are zero, no extra data is inserted * into the SRP hash computation. When AUTH_ENCRYPT_START_TLS * is set we also insert the SSL/TLS client and server finished * messages to ensure that there is no man in the middle attack * underway on the SSL/TLS connection. */ if ((how & AUTH_ENCRYPT_MASK) != AUTH_ENCRYPT_OFF) { type_check[0] = AUTHTYPE_SRP; type_check[1] = how; #ifdef CK_SSL if ((how & AUTH_ENCRYPT_MASK) == AUTH_ENCRYPT_START_TLS) { ssl_get_client_finished(&type_check[2],12); ssl_get_server_finished(&type_check[14],12); t_clientaddexdata(tc,type_check,26); } else #endif /* CK_SSL */ t_clientaddexdata(tc,type_check,2); } #endif /* PRE_SRP_1_4_5 */ B.data = data; B.len = cnt; t_clientgetkey(tc, &B); SendSRPAuthSB(SRP_RESPONSE, t_clientresponse(tc), RESPONSE_LEN); srp_waitresp = 1; return AUTH_SUCCESS; default: return(auth_resend(AUTHTYPE_SRP)); } return AUTH_FAILURE; } static int #ifdef CK_ANSIC srp_is(int how, unsigned char *data, int cnt) #else srp_is(how,data,cnt) int how; unsigned char *data; int cnt; #endif { char * pbuf = NULL; char * ptr; #ifdef CK_ENCRYPTION Session_Key skey; #endif struct t_num A; struct t_pw * tpw = NULL; struct t_conf * tconf = NULL; struct passwd * pass; static struct t_num * B = NULL; /* Holder for B */ #ifdef CK_SSL char type_check[26]; #else char type_check[2]; #endif /* CK_SSL */ if ((cnt -= 4) < 1) { auth_finished(AUTH_REJECT); return AUTH_FAILURE; } data += 4; cnt -= 1; switch(*data++) { case SRP_AUTH: /* Send parameters back to client */ if(ts != NULL) { t_serverclose(ts); ts = NULL; } if(!szUserNameRequested[0]) { if (1) printf("No username available\r\n"); SendSRPAuthSB(SRP_REJECT, (void *) "No username supplied", -1); auth_finished(AUTH_REJECT); return(AUTH_FAILURE); } #ifdef IKSD #ifdef CK_LOGIN if (inserver && ckxanon && !strcmp(szUserNameRequested,"anonymous")) { SendSRPAuthSB(SRP_REJECT, (void *) "anonymous login cannot be performed with Secure Remote Password", -1); auth_finished(AUTH_REJECT); return(AUTH_FAILURE); } #endif /* CK_LOGIN */ #endif /* IKSD */ #ifndef PRE_SRP_1_4_4 if(tpw == NULL) { if((tpw = t_openpw(NULL)) == NULL) { if (1) printf("Unable to open password file\r\n"); SendSRPAuthSB(SRP_REJECT, (void *) "No password file", -1); return(AUTH_FAILURE); } } if(tconf == NULL) { if((tconf = t_openconf(NULL)) == NULL) { if (1) printf("Unable to open configuration file\r\n"); SendSRPAuthSB(SRP_REJECT, (void *)"No configuration file", -1); return(AUTH_FAILURE); } } ts = t_serveropenfromfiles(szUserNameRequested, tpw, tconf); t_closepw(tpw); tpw = NULL; t_closeconf(tconf); tconf = NULL; #else /* PRE_SRP_1_4_4 */ #ifdef COMMENT /* the code in this block should no longer be necessary on OS/2 or Windows because I have added functionality to libsrp.lib to find the srp files. 4/22/2000 */ /* On Windows and OS/2 there is no well defined place for the */ /* ETC directory. So we look for either an SRP_ETC or ETC */ /* environment variable in that order. If we find one we */ /* attempt to open the files manually. */ /* We will reuse the strTmp[] for the file names. */ ptr = getenv("SRP_ETC"); if ( !ptr ) ptr = getenv("ETC"); #ifdef NT if ( !ptr ) { DWORD len; len = AUTHTMPBL; len = GetWindowsDirectory(strTmp,len); if ( len > 0 && len < AUTHTMPBL) { if ( !isWin95() ) { if ( len == 1 ) ckstrncat(strTmp,"SYSTEM32/DRIVERS/ETC",sizeof(strTmp)); else ckstrncat(strTmp,"/SYSTEM32/DRIVERS/ETC",sizeof(strTmp)); } } ptr = strTmp; } #endif /* NT */ if ( ptr ) { int len = strlen(ptr); int i; if (ptr != strTmp) strcpy(strTmp,ptr); for ( i=0;in.len + ts->g.len + ts->s.len + 7); ptr = pbuf; srp_encode_length(ptr, ts->n.len); ptr += 2; memcpy(ptr, ts->n.data, ts->n.len); /* safe */ ptr += ts->n.len; srp_encode_length(ptr, ts->g.len); ptr += 2; memcpy(ptr, ts->g.data, ts->g.len); /* safe */ ptr += ts->g.len; srp_encode_length(ptr, ts->s.len); ptr += 2; memcpy(ptr, ts->s.data, ts->s.len); /* safe */ ptr += ts->s.len; SendSRPAuthSB(SRP_PARAMS, pbuf, ptr - pbuf); free(pbuf); pbuf = NULL; B = t_servergenexp(ts); ckstrncpy(szUserNameAuthenticated,szUserNameRequested,UIDBUFLEN); return AUTH_SUCCESS; case SRP_EXP: /* Client is sending A to us, compute challenge & expected response. */ if (ts == NULL || B == NULL) { printf("Protocol error: SRP_EXP unexpected\r\n"); SendSRPAuthSB(SRP_REJECT, (void *) "Protocol error: unexpected EXP", -1 ); return(AUTH_FAILURE); } /* Wait until now to send B, since it contains the key to "u" */ SendSRPAuthSB(SRP_CHALLENGE, B->data, B->len); B = NULL; #ifndef PRE_SRP_1_4_5 /* * The original SRP AUTH implementation did not protect against * tampering of the auth-type-pairs. Therefore, when the * AUTH_ENCRYPT_MASK bits are zero, no extra data is inserted * into the SRP hash computation. When AUTH_ENCRYPT_START_TLS * is set we also insert the SSL/TLS client and server finished * messages to ensure that there is no man in the middle attack * underway on the SSL/TLS connection. */ if ( (how & AUTH_ENCRYPT_MASK) != AUTH_ENCRYPT_OFF ) { type_check[0] = AUTHTYPE_SRP; type_check[1] = how; #ifdef CK_SSL if ((how & AUTH_ENCRYPT_MASK) == AUTH_ENCRYPT_START_TLS) { ssl_get_client_finished(&type_check[2],12); ssl_get_server_finished(&type_check[14],12); } #endif /* CK_SSL */ t_serveraddexdata(ts,type_check, #ifdef CK_SSL ((how & AUTH_ENCRYPT_MASK) == AUTH_ENCRYPT_START_TLS) ? 26 : #endif /* CK_SSL */ 2); } #endif /* PRE_SRP_1_4_5 */ A.data = data; A.len = cnt; ptr = t_servergetkey(ts, &A); if(ptr == NULL) { if (1) printf("Security alert: Trivial session key attempted\r\n"); SendSRPAuthSB(SRP_REJECT, (void *) "Trivial session key detected", -1 ); return(AUTH_FAILURE); } srp_waitresp = 1; return AUTH_SUCCESS; case SRP_RESPONSE: /* Got the response; see if it's correct */ if (!srp_waitresp || ts == NULL ) { if (1) printf("Protocol error: SRP_RESPONSE unexpected\r\n"); SendSRPAuthSB(SRP_REJECT, (void *) "Protocol error: unexpected RESPONSE", -1 ); return(AUTH_FAILURE); } srp_waitresp = 0; /* we got a response */ if (cnt < RESPONSE_LEN) { if (1) printf("Protocol error: malformed response\r\n"); SendSRPAuthSB(SRP_REJECT, (void *) "Protocol error: malformed response", -1 ); return(AUTH_FAILURE); } if (t_serververify(ts, data) == 0) { SendSRPAuthSB(SRP_ACCEPT, t_serverresponse(ts), RESPONSE_LEN); accept_complete = 1; #ifdef CK_ENCRYPTION #ifdef CK_SSL if (!(ssl_active_flag || tls_active_flag)) #endif /* CK_SSL */ { ckhexdump("SRP_RESPONSE ts",ts,sizeof(ts)); ckhexdump("SRP_RESPONSE session_key", ts->session_key, SESSION_KEY_LEN ); skey.type = SK_GENERIC; skey.length = SESSION_KEY_LEN; skey.data = ts->session_key; encrypt_session_key(&skey, AUTH_SERVER_TO_CLIENT); } #endif /* CK_ENCRYPTION */ auth_finished(AUTH_VALID); } else { SendSRPAuthSB(SRP_REJECT, (void *) "Login incorrect", -1); auth_finished(AUTH_REJECT); return(AUTH_FAILURE); } return AUTH_SUCCESS; default: printf("Unknown SRP option %d\r\n", data[-1]); SendSRPAuthSB(SRP_REJECT, (void *) "Unknown option received", -1); return(AUTH_FAILURE); } } #else /* PRE_SRP_1_7_3 */ static int #ifdef CK_ANSIC new_srp_reply(int how, unsigned char *data, int cnt) #else new_srp_reply(how,data,cnt) int how; unsigned char *data; int cnt; #endif { data += 4; /* Point to status byte */ cnt -= 4; if(cnt-- < 1) { /* Matches with data++ */ auth_finished(AUTH_REJECT); return AUTH_FAILURE; } switch(*data++) { case SRP_PARAMS: { struct t_num n; struct t_num g; struct t_num s; cstr * A; if(!szUserName) { printf("No username available\r\n"); return(auth_resend(AUTHTYPE_SRP)); } n.len = srp_decode_length(data); data += 2; cnt -= 2; if(n.len > cnt) { printf("n too long\r\n"); return(auth_resend(AUTHTYPE_SRP)); } n.data = data; data += n.len; cnt -= n.len; g.len = srp_decode_length(data); data += 2; cnt -= 2; if(g.len > cnt) { printf("g too long\r\n"); return(auth_resend(AUTHTYPE_SRP)); } g.data = data; data += g.len; cnt -= g.len; s.len = srp_decode_length(data); data += 2; cnt -= 2; if(s.len != cnt) { printf("invalid salt\r\n"); return(auth_resend(AUTHTYPE_SRP)); } s.data = data; data += s.len; cnt -= s.len; /* If the parameters provided by the server cannot be * validated the following function will fail. */ c_srp = SRP_new(SRP_RFC2945_client_method()); if (c_srp == NULL || SRP_set_username(c_srp, szUserName) != SRP_SUCCESS || SRP_set_params(c_srp,n.data,n.len,g.data,g.len,s.data,s.len) != SRP_SUCCESS) { printf("SRP Parameter initialization error\r\n"); return(auth_resend(AUTHTYPE_SRP)); } A = cstr_new(); if(SRP_gen_pub(c_srp, &A) != SRP_SUCCESS) { printf("SRP Error generating key exchange\r\n"); return(auth_resend(AUTHTYPE_SRP)); } SendSRPAuthSB(SRP_EXP, A->data, A->length); cstr_free(A); if ( pwbuf[0] && pwflg ) { printf("SRP using %d-bit modulus for '%s'\r\n", 8 * n.len, szUserName ); ckstrncpy(srp_passwd,pwbuf,sizeof(srp_passwd)); #ifdef OS2 if ( pwcrypt ) ck_encrypt((char *)srp_passwd); #endif /* OS2 */ } else { extern char * srppwprompt; char preface[128]; int ok; if (srppwprompt && srppwprompt[0] && (strlen(srppwprompt) + strlen(szUserName) - 2) < sizeof(preface)) { sprintf(preface,srppwprompt,szUserName); } else { ckmakxmsg( preface,sizeof(preface), "SRP using ",ckitoa(8*n.len),"-bit modulus for '", szUserName, "'", NULL, NULL, NULL, NULL, NULL, NULL, NULL); } ok = uq_txt(preface,"Password: ",2,NULL, srp_passwd,sizeof(srp_passwd)-1,NULL, DEFAULT_UQ_TIMEOUT); if ( !ok ) srp_passwd[0] = '\0'; } if(SRP_set_auth_password(c_srp, srp_passwd) != SRP_SUCCESS) { memset(srp_passwd, 0, sizeof(srp_passwd)); printf("SRP Error setting client password\r\n"); return(auth_resend(AUTHTYPE_SRP)); } memset(srp_passwd, 0, sizeof(srp_passwd)); return AUTH_SUCCESS; } case SRP_CHALLENGE: { char type_check[26]; cstr * resp = NULL; if(c_srp == NULL) { printf("SRP protocol error\r\n"); return(auth_resend(AUTHTYPE_SRP)); } /* * The original SRP AUTH implementation did not protect against * tampering of the auth-type-pairs. Therefore, when the * AUTH_ENCRYPT_MASK bits are zero, no extra data is inserted * into the SRP hash computation. When AUTH_ENCRYPT_START_TLS * is set we also insert the SSL/TLS client and server finished * messages to ensure that there is no man in the middle attack * underway on the SSL/TLS connection. */ if ((how & AUTH_ENCRYPT_MASK) != AUTH_ENCRYPT_OFF) { type_check[0] = AUTHTYPE_SRP; type_check[1] = how; #ifdef CK_SSL if ((how & AUTH_ENCRYPT_MASK) == AUTH_ENCRYPT_START_TLS) { ssl_get_client_finished(&type_check[2],12); ssl_get_server_finished(&type_check[14],12); SRP_add_ex_data(c_srp, type_check, 26); } else #endif /* CK_SSL */ SRP_add_ex_data(c_srp, type_check, 2); } if(SRP_compute_key(c_srp, &c_key, data, cnt) != SRP_SUCCESS) { printf("SRP ERROR: unable to compute client key\r\n"); return(auth_resend(AUTHTYPE_SRP)); } resp = cstr_new(); if(SRP_respond(c_srp, &resp) != SRP_SUCCESS) { printf("SRP ERROR: unable to compute client response\r\n"); return(auth_resend(AUTHTYPE_SRP)); } SendSRPAuthSB(SRP_RESPONSE, resp->data, resp->length); cstr_free(resp); srp_waitresp = 1; return AUTH_SUCCESS; } case SRP_ACCEPT: { #ifdef CK_ENCRYPTION Session_Key skey; #endif /* ENCRYPTION */ if(cnt < RESPONSE_LEN || !srp_waitresp || c_srp == NULL) { printf("SRP Protocol error\r\n"); return(auth_resend(AUTHTYPE_SRP)); } srp_waitresp = 0; if(SRP_verify(c_srp, data, cnt) == SRP_SUCCESS) { printf("SRP accepts you as %s\r\n",szUserName); #ifdef CK_SSL if((ssl_active_flag || tls_active_flag) && (how & AUTH_ENCRYPT_MASK) == AUTH_ENCRYPT_START_TLS) { printf("TLS session parameters verified by SRP\r\n"); } else #endif /* CK_SSL */ #ifdef CK_ENCRYPTION { skey.type = SK_GENERIC; skey.length = c_key->length; skey.data = c_key->data; encrypt_session_key(&skey, AUTH_CLIENT_TO_SERVER); cstr_clear_free(c_key); c_key = NULL; } #endif /* CK_ENCRYPTION */ accept_complete = 1; auth_finished(AUTH_VALID); SRP_free(c_srp); c_srp = NULL; return AUTH_SUCCESS; } else { printf("[ Error: SRP server authentication failed ]\r\n"); return(auth_resend(AUTHTYPE_SRP)); } } case SRP_REJECT: { char * str=NULL; ckmakmsg(strTmp,sizeof(strTmp), "SRP refuses authentication for '",szUserName, "'\r\n",NULL); if (cnt > 0) { int len = strlen(strTmp); if ( len + cnt < sizeof(strTmp) ) { str = strTmp + strlen(strTmp); memcpy(str,data,cnt); str[cnt] = 0; } } printf("SRP authentication failed!\r\n%s\r\n",strTmp); auth_finished(AUTH_REJECT); return AUTH_FAILURE; } default: printf("Unknown SRP option %d\r\n", data[-1]); return(auth_resend(AUTHTYPE_SRP)); } /* NEVER REACHED */ } static int #ifdef CK_ANSIC new_srp_is(int how, unsigned char *data, int cnt) #else new_srp_is(how,data,cnt) int how; unsigned char *data; int cnt; #endif { char * pbuf = NULL; char * ptr; #ifdef CK_ENCRYPTION Session_Key skey; #endif static cstr * B = NULL; /* Holder for B */ struct t_passwd * pass; cstr * resp; char type_check[26]; if ((cnt -= 4) < 1) { auth_finished(AUTH_REJECT); return AUTH_FAILURE; } data += 4; cnt -= 1; switch(*data++) { case SRP_AUTH: /* Send parameters back to client */ if(s_srp != NULL) { SRP_free(s_srp); s_srp = NULL; } if (B != NULL) { cstr_free(B); B = NULL; } if(!szUserNameRequested[0]) { if (1) printf("No username available\r\n"); SendSRPAuthSB(SRP_REJECT, (void *) "No username supplied", -1); auth_finished(AUTH_REJECT); return(AUTH_FAILURE); } #ifdef IKSD #ifdef CK_LOGIN if (inserver && ckxanon && !strcmp(szUserNameRequested,"anonymous")) { SendSRPAuthSB(SRP_REJECT, (void *) "anonymous login cannot be performed with Secure Remote Password", -1); auth_finished(AUTH_REJECT); return(AUTH_FAILURE); } #endif /* CK_LOGIN */ #endif /* IKSD */ s_srp = SRP_new(SRP_RFC2945_server_method()); if(s_srp == NULL) { printf("Error initializing SRP server\r\n"); SendSRPAuthSB(SRP_REJECT, (void *) "SRP server init failed", -1 ); return(AUTH_FAILURE); } pass = gettpnam(szUserNameRequested); if(pass == NULL) { printf("User %s not found\r\n", szUserNameRequested); SendSRPAuthSB(SRP_REJECT, (void *) "Password not set", -1); return(AUTH_FAILURE); } if(SRP_set_username(s_srp, szUserNameRequested) != SRP_SUCCESS || SRP_set_params(s_srp, pass->tc.modulus.data, pass->tc.modulus.len, pass->tc.generator.data, pass->tc.generator.len, pass->tp.salt.data, pass->tp.salt.len) != SRP_SUCCESS || SRP_set_authenticator(s_srp, pass->tp.password.data, pass->tp.password.len) != SRP_SUCCESS) { printf("Error initializing SRP parameters\r\n"); SendSRPAuthSB(SRP_REJECT,(void *)"SRP parameter init failed", -1); return(AUTH_FAILURE); } pbuf = (char *)malloc(pass->tc.modulus.len + pass->tc.generator.len + pass->tp.salt.len + 7); ptr = pbuf; srp_encode_length(ptr, pass->tc.modulus.len); ptr += 2; memcpy(ptr, pass->tc.modulus.data, pass->tc.modulus.len); ptr += pass->tc.modulus.len; srp_encode_length(ptr, pass->tc.generator.len); ptr += 2; memcpy(ptr, pass->tc.generator.data, pass->tc.generator.len); ptr += pass->tc.generator.len; srp_encode_length(ptr, pass->tp.salt.len); ptr += 2; memcpy(ptr, pass->tp.salt.data, pass->tp.salt.len); ptr += pass->tp.salt.len; SendSRPAuthSB(SRP_PARAMS, pbuf, ptr - pbuf); free(pbuf); pbuf = NULL; if(SRP_gen_pub(s_srp, &B) != SRP_SUCCESS) { printf("Error generating SRP public value\r\n"); SendSRPAuthSB(SRP_REJECT, (void *) "SRP_gen_pub failed", -1); return(AUTH_FAILURE); } ckstrncpy(szUserNameAuthenticated,szUserNameRequested,UIDBUFLEN); return AUTH_SUCCESS; case SRP_EXP: /* Client is sending A to us, compute challenge and expected response. */ if (s_srp == NULL || B == NULL) { printf("Protocol error: SRP_EXP unexpected\r\n"); SendSRPAuthSB(SRP_REJECT, (void *)"Protocol error: unexpected EXP", -1); return(AUTH_FAILURE); } /* Wait until now to send B, since it contains the key to "u" */ SendSRPAuthSB(SRP_CHALLENGE, B->data, B->length); cstr_free(B); B = NULL; /* * The original SRP AUTH implementation did not protect against * tampering of the auth-type-pairs. Therefore, when the * AUTH_ENCRYPT_MASK bits are zero, no extra data is inserted * into the SRP hash computation. When AUTH_ENCRYPT_START_TLS * is set we also insert the SSL/TLS client and server finished * messages to ensure that there is no man in the middle attack * underway on the SSL/TLS connection. */ if ( (how & AUTH_ENCRYPT_MASK) != AUTH_ENCRYPT_OFF ) { type_check[0] = AUTHTYPE_SRP; type_check[1] = how; #ifdef CK_SSL if ((how & AUTH_ENCRYPT_MASK) == AUTH_ENCRYPT_START_TLS) { ssl_get_client_finished(&type_check[2],12); ssl_get_server_finished(&type_check[14],12); SRP_add_ex_data(s_srp, type_check, 26); } else #endif /* CK_SSL */ SRP_add_ex_data(s_srp, type_check, 2); } if(SRP_compute_key(s_srp, &s_key, data, cnt) != SRP_SUCCESS) { printf("Security alert: Trivial session key attempted\r\n"); SendSRPAuthSB(SRP_REJECT, (void *) "Trivial session key detected", -1); return(AUTH_FAILURE); } srp_waitresp = 1; return AUTH_SUCCESS; case SRP_RESPONSE: /* Got the response; see if it's correct */ if (!srp_waitresp || s_srp == NULL) { if (1) printf("Protocol error: SRP_RESPONSE unexpected\r\n"); SendSRPAuthSB(SRP_REJECT, (void *) "Protocol error: unexpected RESPONSE", -1 ); return(AUTH_FAILURE); } srp_waitresp = 0; /* we got a response */ if (cnt < RESPONSE_LEN) { if (1) printf("Protocol error: malformed response\r\n"); SendSRPAuthSB(SRP_REJECT, (void *) "Protocol error: malformed response", -1 ); return(AUTH_FAILURE); } if(SRP_verify(s_srp, data, cnt) == SRP_SUCCESS) { resp = cstr_new(); if(SRP_respond(s_srp, &resp) != SRP_SUCCESS) { printf("Error computing response\r\n"); SendSRPAuthSB(SRP_REJECT, (void *) "Error computing response", -1); return(AUTH_FAILURE); } SendSRPAuthSB(SRP_ACCEPT, resp->data, resp->length); accept_complete = 1; cstr_free(resp); #ifdef CK_ENCRYPTION #ifdef CK_SSL if (!(ssl_active_flag || tls_active_flag)) #endif /* CK_SSL */ { skey.type = SK_GENERIC; skey.length = s_key->length; skey.data = s_key->data; encrypt_session_key(&skey, AUTH_SERVER_TO_CLIENT); cstr_clear_free(s_key); s_key = NULL; } #endif /* CK_ENCRYPTION */ auth_finished(AUTH_VALID); } else { SendSRPAuthSB(SRP_REJECT, (void *) "Login incorrect", -1); auth_finished(AUTH_REJECT); return(AUTH_FAILURE); } return AUTH_SUCCESS; default: printf("Unknown SRP option %d\r\n", data[-1]); SendSRPAuthSB(SRP_REJECT, (void *) "Unknown option received", -1); return(AUTH_FAILURE); } } #endif /* PRE_SRP_1_7_3 */ #endif /* SRP */ #ifdef KRB5 #ifdef KINIT /* * clients/kinit/kinit.c * * Copyright 1990 by the Massachusetts Institute of Technology. * All Rights Reserved. * * Export of this software from the United States of America may * require a specific license from the United States Government. * It is the responsibility of any person or organization contemplating * export to obtain such a license before exporting. * * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and * distribute this software and its documentation for any purpose and * without fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright notice and * this permission notice appear in supporting documentation, and that * the name of M.I.T. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior * permission. M.I.T. makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. * * * Initialize a credentials cache. */ #define KRB5_DEFAULT_OPTIONS 0 #define KRB5_DEFAULT_LIFE 60*60*10 /* 10 hours */ static krb5_data tgtname = { #ifndef HEIMDAL 0, #endif /* HEIMDAL */ KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME }; /* Internal prototypes */ _PROTOTYP(static krb5_error_code krb5_validate_tgt, (krb5_context, krb5_ccache,krb5_principal, krb5_data *)); _PROTOTYP(static krb5_error_code krb5_renew_tgt, (krb5_context, krb5_ccache, krb5_principal, krb5_data *)); _PROTOTYP(static krb5_error_code krb5_tgt_gen, (krb5_context, krb5_ccache, krb5_principal, krb5_data *, int opt)); #ifdef KRB5_HAVE_GET_INIT_CREDS static krb5_error_code KRB5_CALLCONV ck_krb5_prompter( krb5_context context, void *data, const char *name, const char *banner, int num_prompts, krb5_prompt prompts[]) { krb5_error_code errcode = 0; int i; #ifdef KUI struct txtbox * tb = NULL; #else /* KUI */ char * prompt = NULL; #endif /* KUI */ int len = 0, blen=0, nlen=0; debug(F110,"ck_krb5_prompter name",name,0); debug(F110,"ck_krb5_prompter banner",banner,0); debug(F101,"ck_krb5_prompter num_prompts","",num_prompts); if (name) nlen = strlen(name)+2; if (banner) blen = strlen(banner)+2; #ifdef KUI tb = (struct txtbox *) malloc(sizeof(struct txtbox) * num_prompts); if ( tb != NULL ) { int ok; memset(tb,0,sizeof(struct txtbox) * num_prompts); for ( i=0; i < num_prompts; i++ ) { tb[i].t_buf = prompts[i].reply->data; tb[i].t_len = prompts[i].reply->length; tb[i].t_lbl = prompts[i].prompt; tb[i].t_dflt = NULL; tb[i].t_echo = (prompts[i].hidden ? 2 : 1); } ok = uq_mtxt((char *)banner,NULL,num_prompts,tb); if ( ok ) { for ( i=0; i < num_prompts; i++ ) prompts[i].reply->length = strlen(prompts[i].reply->data); } else errcode = -2; } #else /* KUI */ for (i = 0; i < num_prompts; i++) { debug(F111,"ck_krb5_prompter prompt",prompts[i].prompt,i); if ( prompt && len < (nlen + blen + strlen(prompts[i].prompt)+2) ) { free(prompt); prompt = NULL; } if ( !prompt ) prompt = (char *)malloc(nlen + blen + strlen(prompts[i].prompt)+2); if ( !prompt ) { errcode = KRB5_RC_MALLOC; goto cleanup; } len = nlen + blen + strlen(prompts[i].prompt)+2; ckmakxmsg(prompt,len, (char *) (name?name:""), name?"\r\n":"", (char *) (banner?banner:""), banner?"\r\n":"", (char *)prompts[i].prompt, ": ",NULL,NULL,NULL,NULL,NULL,NULL); memset(prompts[i].reply->data, 0, prompts[i].reply->length); if (prompts[i].hidden) { readpass(prompt, prompts[i].reply->data, prompts[i].reply->length); } else { readtext(prompt, prompts[i].reply->data, prompts[i].reply->length); } prompts[i].reply->length = strlen(prompts[i].reply->data); } #endif /* KUI */ cleanup: #ifdef KUI if ( tb ) free(tb); #else /* KUI */ if ( prompt ) free(prompt); #endif /* KUI */ if (errcode) { for (i = 0; i < num_prompts; i++) { memset(prompts[i].reply->data, 0, prompts[i].reply->length); } } return errcode; } /* * I'm not really sure what to do with this. The NRL DLLs use a * different interface for the krb5_prompter callback. It has * one less parameter. This is going to be ugly. */ static krb5_error_code KRB5_CALLCONV ck_NRL_krb5_prompter( krb5_context context, const char *name, const char *banner, int num_prompts, krb5_prompt prompts[]) { return(ck_krb5_prompter(context,NULL,name,banner,num_prompts,prompts)); } #endif /* KRB5_HAVE_GET_INIT_CREDS */ #ifdef KRB524_CONV long try_convert524(krb5_context ctx, krb5_principal me, krb5_ccache cc) { char * progname = "convert524"; krb5_error_code code = 0; int icode = 0; krb5_principal kpcserver = 0; krb5_creds *v5creds = 0; krb5_creds increds; #ifdef OS2 LEASH_CREDENTIALS v4creds; #else /* OS2 */ CREDENTIALS v4creds; #endif /* OS2 */ memset((char *) &increds, 0, sizeof(increds)); /* From this point on, we can goto cleanup because increds is initialized. */ if ((code = krb5_build_principal(ctx, &kpcserver, krb5_princ_realm(ctx, me)->length, krb5_princ_realm(ctx, me)->data, "krbtgt", krb5_princ_realm(ctx, me)->data, NULL))) { com_err(progname, code, "while creating service principal name"); goto cleanup; } memset((char*) &increds, 0, sizeof(increds)); increds.client = me; increds.server = kpcserver; /* Prevent duplicate free calls. */ kpcserver = 0; increds.times.endtime = 0; increds.keyblock.enctype = ENCTYPE_DES_CBC_CRC; if ((code = krb5_get_credentials(ctx, 0, cc, &increds, &v5creds))) { com_err(progname, code, "getting V5 credentials"); goto cleanup; } if ((icode = krb524_convert_creds_kdc(ctx, v5creds, &v4creds))) { com_err(progname, icode, "converting to V4 credentials"); goto cleanup; } /* this is stolen from the v4 kinit */ /* initialize ticket cache */ if ((icode = krb_in_tkt(v4creds.pname, v4creds.pinst, v4creds.realm) != KSUCCESS)) { com_err(progname, icode, "trying to create the V4 ticket file"); goto cleanup; } /* stash ticket, session key, etc. for future use */ if ((icode = krb_save_credentials(v4creds.service, v4creds.instance, v4creds.realm, v4creds.session, v4creds.lifetime, v4creds.kvno, &(v4creds.ticket_st), v4creds.issue_date))) { com_err(progname, icode, "trying to save the V4 ticket"); goto cleanup; } cleanup: memset(&v4creds, 0, sizeof(v4creds)); if (v5creds) krb5_free_creds(ctx, v5creds); increds.client = 0; krb5_free_cred_contents(ctx, &increds); if (kpcserver) krb5_free_principal(ctx, kpcserver); return !(code || icode); } #endif /* KRB524_CONV */ #define NO_KEYTAB int #ifdef CK_ANSIC ck_krb5_initTGT( struct krb_op_data * op, struct krb5_init_data * init, struct krb4_init_data * k4_init) #else ck_krb5_initTGT(op,init,k4_init) krb_op_data * op; struct krb5_init_data * init; struct krb4_init_data * k4_init; #endif /* CK_ANSIC*/ { krb5_context kcontext; krb5_ccache ccache = NULL; krb5_deltat lifetime = KRB5_DEFAULT_LIFE; /* -l option */ krb5_timestamp starttime = 0; krb5_deltat rlife = 0; int options = KRB5_DEFAULT_OPTIONS; int option; int errflg = 0; krb5_error_code code; krb5_principal me=NULL; krb5_principal server=NULL; krb5_creds my_creds; krb5_timestamp now; #ifndef HEIMDAL krb5_address **addrs = (krb5_address **)0; #endif /* HEIMDAL */ int addr_count=0; int i,j; #ifndef NO_KEYTAB int use_keytab = 0; /* -k option */ krb5_keytab keytab = NULL; #endif /* NO_KEYTAB */ struct passwd *pw = 0; int pwsize; char *client_name=NULL, principal[256]="", realm[256]="", numstr[40]=""; char *password=NULL, passwd[80]=""; #ifdef KRB5_HAVE_GET_INIT_CREDS krb5_get_init_creds_opt opts; #endif char * name; int len; if ( !ck_krb5_is_installed() ) return(-1); #ifdef COMMENT printf("Kerberos V initialization\r\n"); #endif /* COMMENT */ code = krb5_init_context(&kcontext); if (code) { com_err("krb5_kinit",code,"while init_context"); krb5_errno = code; makestr(&krb5_errmsg,error_message(krb5_errno)); return(-1); } debug(F110,"krb5_init","krb5_init_context",0); if ((code = krb5_timeofday(kcontext, &now))) { com_err("krb5_kinit",code,"while getting time of day"); goto exit_k5_init; } #ifdef KRB5_HAVE_GET_INIT_CREDS memset(&opts, 0, sizeof(opts)); krb5_get_init_creds_opt_init(&opts); debug(F110,"krb5_init","krb5_get_init_creds_opt_init",0); #endif if ( init->renewable ) { options |= KDC_OPT_RENEWABLE; ckmakmsg(numstr,sizeof(numstr),ckitoa(init->renewable),"m",NULL,NULL); #ifdef HEIMDAL code = -1; #else /* HEIMDAL */ code = krb5_string_to_deltat(numstr, &rlife); #endif /* HEIMDAL */ if (code != 0 || rlife == 0) { printf("Bad renewable time value %s\r\n", numstr); errflg++; } #ifdef KRB5_HAVE_GET_INIT_CREDS krb5_get_init_creds_opt_set_renew_life(&opts, rlife); #endif } if ( init->renew ) { /* renew the ticket */ options |= KDC_OPT_RENEW; } if ( init->validate ) { /* validate the ticket */ options |= KDC_OPT_VALIDATE; } if ( init->proxiable ) { options |= KDC_OPT_PROXIABLE; #ifdef KRB5_HAVE_GET_INIT_CREDS krb5_get_init_creds_opt_set_proxiable(&opts, 1); #endif } if ( init->forwardable ) { options |= KDC_OPT_FORWARDABLE; #ifdef KRB5_HAVE_GET_INIT_CREDS krb5_get_init_creds_opt_set_forwardable(&opts, 1); #endif } #ifndef NO_KEYTAB if ( ) { use_keytab = 1; } if ( ) { if (keytab == NULL && keytab_name != NULL) { code = krb5_kt_resolve(kcontext, keytab_name, &keytab); if (code != 0) { debug(F111,"krb5_init resolving keytab", keytab_name,code); errflg++; } } } #endif /* NO_KEYTAB */ if ( init->lifetime ) { ckmakmsg(numstr,sizeof(numstr),ckitoa(init->lifetime),"m",NULL,NULL); #ifdef HEIMDAL code = -1; #else /* HEIMDAL */ code = krb5_string_to_deltat(numstr, &lifetime); #endif /* HEIMDAL */ if (code != 0 || lifetime == 0) { printf("Bad lifetime value %s\r\n", numstr); errflg++; } #ifdef KRB5_HAVE_GET_INIT_CREDS krb5_get_init_creds_opt_set_tkt_life(&opts, lifetime); #endif } if ( init->postdate ) { /* Convert cmdate() to a time_t value */ struct tm * time_tm; struct tm * cmdate2tm(char *,int); time_tm = cmdate2tm(init->postdate,0); if ( time_tm ) starttime = (krb5_timestamp) mktime(time_tm); if (code != 0 || starttime == 0 || starttime == -1) { krb5_deltat ktmp; #ifdef HEIMDAL code = -1; #else /* HEIMDAL */ code = krb5_string_to_deltat(init->postdate, &ktmp); #endif /* HEIMDAL */ if (code == 0 && ktmp != 0) { starttime = now + ktmp; options |= KDC_OPT_POSTDATED; } else { printf("Bad postdate start time value %s\r\n", init->postdate); errflg++; } } else { options |= KDC_OPT_POSTDATED; } } debug(F110,"krb5_init searching for ccache",op->cache,0); code = k5_get_ccache(kcontext,&ccache,op->cache); if (code != 0) { com_err("krb5_kinit",code,"while getting default ccache"); goto exit_k5_init; } /* This is our realm unless it is changed */ ckstrncpy(realm,init->realm ? init->realm : krb5_d_realm, 256); #ifdef BETATEST /* This code is going to take the realm and attempt to correct */ /* the case. */ { profile_t profile; code = krb5_get_profile(kcontext, &profile); if ( !code ) { const char *names[4]; char ** realms; int found = 0; names[0] = "realms"; names[1] = NULL; code = profile_get_subsection_names(profile,names,&realms); if ( code == 0 ) { int i=0; while ( realms[i] ) { if (ckstrcmp(realm,realms[i],-1,0) == 0) { strcpy(realm,realms[i]); found = 1; break; } i++; } } #ifdef CK_DNS_SRV if ( !found ) { char * dns_realm = NULL; /* We did not find the realm in the profile so let's try DNS */ locate_txt_rr("_kerberos",realm,&dns_realm); if ( dns_realm && ckstrcmp(realm,dns_realm,-1,0) == 0 && ckstrcmp(realm,dns_realm,-1,1) != 0 ) { ckstrncpy(realm,dns_realm,256); free(dns_realm); } } #endif /* CK_DNS_SRV */ } if (init->realm && ckstrcmp(realm,init->realm,-1,0) == 0 && ckstrcmp(realm,init->realm,-1,1) != 0) strcpy(init->realm,realm); if (ckstrcmp(realm,krb5_d_realm,-1,0) == 0 && ckstrcmp(realm,krb5_d_realm,-1,1) != 0) strcpy(krb5_d_realm,realm); } #endif /* BETATEST */ if (init->principal == NULL) { /* No principal name specified */ #ifndef NO_KEYTAB if (use_keytab) { /* Use the default host/service name */ code = krb5_sname_to_principal(kcontext, NULL, NULL, KRB5_NT_SRV_HST, &me); if (code == 0 && krb5_princ_realm(kcontext, me)->length < sizeof(realm)) { /* Save the realm */ memcpy(realm,krb5_princ_realm(kcontext, me)->data, krb5_princ_realm(kcontext, me)->length); /* safe */ realm[krb5_princ_realm(kcontext, me)->length]='\0'; } else { com_err("krb5_kinit", code, "when creating default server principal name"); goto exit_k5_init; } } else #endif /* NO_KEYTAB */ { int len; char * name; /* Get default principal from cache if one exists */ code = krb5_cc_get_principal(kcontext, ccache, &me); #ifdef HEIMDAL name = me->realm; len = strlen(name); #else /* HEIMDAL */ len = krb5_princ_realm(kcontext, me)->length; name = krb5_princ_realm(kcontext, me)->data; #endif /* HEIMDAL */ if (code == 0 && len < sizeof(realm)) { /* Save the realm */ memcpy(realm,name,len); /* safe */ realm[len]='\0'; } else { #ifdef HAVE_PWD_H /* Else search passwd file for client */ pw = getpwuid((int) getuid()); if (pw) { char princ_realm[256]; if ( (strlen(pw->pw_name) + strlen(realm) + 1) > 255 ) goto exit_k5_init; ckstrncpy(principal,pw->pw_name,256); ckstrncpy(princ_realm,pw->pw_name,256); ckstrncat(princ_realm,"@",256); ckstrncat(princ_realm,realm,256); if ((code = krb5_parse_name(kcontext,princ_realm,&me))) { krb5_errno = code; com_err("krb5_kinit",code,"when parsing name", princ_realm); goto exit_k5_init; } } else { printf( "Unable to identify user from password file\r\n"); goto exit_k5_init; } #else /* HAVE_PWD_H */ printf("Unable to identify user\r\n"); goto exit_k5_init; #endif /* HAVE_PWD_H */ } } #ifdef HEIMDAL len = me->name.name_string.len; name = *me->name.name_string.val; #else /* HEIMDAL */ len = krb5_princ_name(kcontext, me)->length; name = krb5_princ_name(kcontext, me)->data; #endif /* HEIMDAL */ if ( len < sizeof(principal) ) { memcpy(principal,name,len); /* safe */ principal[len]='\0'; } } /* Use specified name */ else { char princ_realm[256]; if ( (strlen(init->principal) + (init->instance ? strlen(init->instance)+1 : 0) + strlen(realm) + 2) > 255 ) goto exit_k5_init; ckstrncpy(principal,init->principal,256); ckstrncpy(princ_realm,init->principal,256); if (init->instance) { ckstrncat(princ_realm,"/",256); ckstrncat(princ_realm,init->instance,256); } if (realm[0]) { ckstrncat(princ_realm,"@",256); ckstrncat(princ_realm,realm,256); } if ((code = krb5_parse_name (kcontext, princ_realm, &me))) { com_err("krb5_kinit",code,"when parsing name",princ_realm); goto exit_k5_init; } } if ((code = krb5_unparse_name(kcontext, me, &client_name))) { com_err("krb5_kinit",code,"when unparsing name"); goto exit_k5_init; } debug(F110,"krb5_init client_name",client_name,0); memset((char *)&my_creds, 0, sizeof(my_creds)); my_creds.client = me; if (init->service == NULL) { if ((code = krb5_build_principal_ext(kcontext, &server, strlen(realm),realm, tgtname.length, tgtname.data, strlen(realm),realm, 0))) { com_err("krb5_kinit",code,"while building server name"); goto exit_k5_init; } } else { if (code = krb5_parse_name(kcontext, init->service, &server)) { com_err("krb5_kinit",code,"while parsing service name", init->service); goto exit_k5_init; } } my_creds.server = server; if (options & KDC_OPT_POSTDATED) { my_creds.times.starttime = starttime; my_creds.times.endtime = starttime + lifetime; } else { my_creds.times.starttime = 0; /* start timer when request gets to KDC */ my_creds.times.endtime = now + lifetime; } if (options & KDC_OPT_RENEWABLE) { my_creds.times.renew_till = now + rlife; } else my_creds.times.renew_till = 0; if (options & KDC_OPT_VALIDATE) { krb5_data outbuf; #ifdef KRB5_HAVE_GET_INIT_CREDS code = krb5_get_validated_creds(kcontext, &my_creds, me, ccache, init->service); if ( code == -1 ) #endif { #ifdef HEIMDAL printf("?validate not implemented\r\n"); code = -1; goto exit_k5_init; #else /* HEIMDAL */ code = krb5_validate_tgt(kcontext, ccache, server, &outbuf); #endif /* HEIMDAL */ } if (code) { com_err("krb5_kinit",code,"validating tgt"); goto exit_k5_init; } /* should be done... */ goto exit_k5_init; } if (options & KDC_OPT_RENEW) { krb5_data outbuf; #ifdef KRB5_HAVE_GET_INIT_CREDS code = krb5_get_renewed_creds(kcontext, &my_creds, me, ccache, init->service); if ( code == -1 ) #endif { #ifdef HEIMDAL printf("?renew not implemented\r\n"); code = -1; goto exit_k5_init; #else /* HEIMDAL */ code = krb5_renew_tgt(kcontext, ccache, server, &outbuf); #endif /* HEIMDAL */ } if (code) { com_err("krb5_kinit",code,"while renewing tgt"); goto exit_k5_init; } /* should be done... */ goto store_cred; } #ifndef HEIMDAL if ( init->addrs && !init->no_addresses ) { /* construct an array of krb5_address structs to pass to get_in_tkt */ /* include both the local ip addresses as well as any other that */ /* are specified. */ unsigned long ipaddr; for ( addr_count=0;addr_countaddrs[addr_count] == NULL ) break; if (addr_count > 0) { krb5_address ** local_addrs=NULL; krb5_os_localaddr(kcontext, &local_addrs); i = 0; while ( local_addrs[i] ) i++; addr_count += i; addrs = (krb5_address **) malloc((addr_count+1) * sizeof(krb5_address *)); if ( !addrs ) { krb5_free_addresses(kcontext, local_addrs); goto exit_k5_init; } memset(addrs, 0, sizeof(krb5_address *) * (addr_count+1)); i = 0; while ( local_addrs[i] ) { addrs[i] = (krb5_address *)malloc(sizeof(krb5_address)); if (addrs[i] == NULL) { krb5_free_addresses(kcontext, local_addrs); goto exit_k5_init; } addrs[i]->magic = local_addrs[i]->magic; addrs[i]->addrtype = local_addrs[i]->addrtype; addrs[i]->length = local_addrs[i]->length; addrs[i]->contents = (unsigned char *)malloc(addrs[i]->length); if (!addrs[i]->contents) { krb5_free_addresses(kcontext, local_addrs); goto exit_k5_init; } memcpy(addrs[i]->contents,local_addrs[i]->contents, local_addrs[i]->length); /* safe */ i++; } krb5_free_addresses(kcontext, local_addrs); for ( j=0;imagic = KV5M_ADDRESS; addrs[i]->addrtype = AF_INET; addrs[i]->length = 4; addrs[i]->contents = (unsigned char *)malloc(addrs[i]->length); if (!addrs[i]->contents) goto exit_k5_init; ipaddr = inet_addr(init->addrs[j]); memcpy(addrs[i]->contents,&ipaddr,4); /* safe */ } #ifdef KRB5_HAVE_GET_INIT_CREDS krb5_get_init_creds_opt_set_address_list(&opts,addrs); #endif } } #endif /* !HEIMDAL */ #ifdef KRB5_HAVE_GET_INIT_CREDS if ( init->no_addresses ) krb5_get_init_creds_opt_set_address_list(&opts,NULL); #endif #ifndef NO_KEYTAB if (!use_keytab) #endif { if ( init->password ) { pwsize = strlen(init->password); if ( pwsize ) password = init->password; } else if (init->getk4 && k4_init) { /* When we are requesting that K4 tickets be automatically */ /* acquired when K5 tickets are acquired, we must get the */ /* password up front. */ char prmpt[256]; extern char * k5prprompt; extern char * k5pwprompt; int ok = 0; if ( k5pwprompt && k5pwprompt[0] && (strlen(k5pwprompt) + strlen(principal) + strlen(realm) - 4) < sizeof(prmpt)) { sprintf(prmpt,k5pwprompt,principal,realm); } else ckmakxmsg(prmpt,sizeof(prmpt), k5pwprompt && k5pwprompt[0] ? k5pwprompt : "Kerberos 5 Password for ", principal,"@",realm,": ", NULL,NULL,NULL,NULL,NULL,NULL,NULL ); ok = uq_txt(NULL,prmpt,2,NULL,passwd,80,NULL,DEFAULT_UQ_TIMEOUT); if ( ok ) password = passwd; if ( k4_init->password == NULL ) makestr(&k4_init->password,passwd); } #ifdef KRB5_HAVE_GET_INIT_CREDS debug(F100,"krb5_init calling krb5_get_init_creds_password()","",0); #ifdef OS2 if ( is_NRL_KRB5() ) code = krb5_get_init_creds_password(kcontext, &my_creds, me, password, (void *)ck_NRL_krb5_prompter, NULL, starttime, init->service, &opts); else #endif /* OS2 */ code = krb5_get_init_creds_password(kcontext, &my_creds, me, password, ck_krb5_prompter, NULL, starttime, init->service, &opts); debug(F111,"krb5_init","krb5_get_init_creds_password()",code); if ( code == -1 ) { if (!password) { char prmpt[256]; int ok; ckmakmsg(prmpt,sizeof(prmpt),"Kerberos 5 Password for ", client_name,": ",NULL); ok = uq_txt(NULL,prmpt,2,NULL,passwd,80,NULL, DEFAULT_UQ_TIMEOUT); if ( ok ) password = passwd; else { code = -2; goto exit_k5_init; } } if ( !password ) password = ""; code = krb5_get_in_tkt_with_password(kcontext, options, #ifdef HEIMDAL NULL, #else /* HEIMDAL */ init->no_addresses ? NULL :addrs, #endif /* HEIMDAL */ NULL, NULL, password, NULL, &my_creds, NULL); if ( code ) debug(F111,"krb5_init","krb5_get_in_tkt_with_password()",code); } #else /* KRB5_HAVE_GET_INIT_CREDS */ if (!password) { char prmpt[256]; int ok; ckmakmsg(prmpt,sizeof(prmpt),"Kerberos 5 Password for ", client_name,": ",NULL); ok = uq_txt(NULL,prmpt,2,NULL,passwd,80,NULL,DEFAULT_UQ_TIMEOUT); if ( ok ) password = passwd; else { code = -2; goto exit_k5_init; } } if ( !password ) password = ""; code = krb5_get_in_tkt_with_password(kcontext, options, #ifdef HEIMDAL NULL, #else /* HEIMDAL */ init->no_addresses ? NULL :addrs, #endif /* HEIMDAL */ NULL, NULL, password, NULL, &my_creds, NULL); if ( code ) debug(F111,"krb5_init","krb5_get_in_tkt_with_password()",code); #endif /* KRB5_HAVE_GET_INIT_CREDS */ if ( init->password && pwsize > 0 ) memset(init->password, 0, pwsize); memset(passwd,0,80); } #ifndef NO_KEYTAB else { #ifdef KRB5_HAVE_GET_INIT_CREDS code = krb5_get_init_creds_keytab(kcontext, &my_creds, me, keytab, starttime, init->service, &opts); #ifdef OS2 if ( code == -1) code = krb5_get_in_tkt_with_keytab(kcontext, options, init->no_addresses ? NULL :addrs, NULL, NULL, keytab, NULL, &my_creds, 0); #endif /* OS2 */ #else /* KRB5_HAVE_GET_INIT_CREDS */ code = krb5_get_in_tkt_with_keytab(kcontext, options, #ifdef HEIMDAL NULL, #else /* HEIMDAL */ init->no_addresses ? NULL :addrs, #endif /* HEIMDAL */ NULL, NULL, keytab, NULL, &my_creds, 0); #endif /* KRB5_HAVE_GET_INIT_CREDS */ } #endif if (code) { switch (code) { case KRB5KRB_AP_ERR_BAD_INTEGRITY: printf("Password incorrect\r\n"); goto exit_k5_init; case KRB5KRB_AP_ERR_V4_REPLY: if (init->getk4 && k4_init) { printf("Kerberos 5 Tickets not support by server. "); printf("A version 4 Ticket will be requested.\r\n"); } goto exit_k5_init; default: goto exit_k5_init; } } store_cred: debug(F100,"krb5_init calling krb5_cc_initialize()","",0); code = krb5_cc_initialize (kcontext, ccache, me); if ( code == KRB5_CC_BADNAME ) { /* This is a really ugly hack that should not have to be here. * krb5_cc_initialize should not fail with an error if the * cache already exists. The reason the problem is occuring * is that the krb5 library is no longer calling cc_destroy() * when cc_initialize() is called and the CCAPI implementation * on Windows has not yet been corrected to handle it. To * ensure that K95 will continue to work with both we will call * cc_destroy() if the cc_initialize() call fails with a BADNAME * error. If the cc_destroy() is successful, we will try again. */ debug(F100,"krb5_init calling krb5_cc_destroy()","",0); code = krb5_cc_destroy (kcontext, ccache); if ( !code ) { debug(F100,"krb5_init calling k5_get_ccache()","",0); code = k5_get_ccache(kcontext,&ccache,op->cache); debug(F100,"krb5_init calling krb5_cc_initialize()","",0); code = krb5_cc_initialize (kcontext, ccache, me); } else code = KRB5_CC_BADNAME; } if (code) { com_err("krb5_kinit",code,"when initializing cache",op->cache); goto exit_k5_init; } debug(F100,"krb5_init calling krb5_cc_store_cred()","",0); code = krb5_cc_store_cred(kcontext, ccache, &my_creds); if (code) { com_err("krb5_kinit",code,"while storing credentials"); goto exit_k5_init; } if ( init->getk4 && #ifdef KRB524_CONV !try_convert524(kcontext,me,ccache) && #endif /* KRB524_CONV */ k4_init ) { int k4rc = ck_krb4_initTGT(op,k4_init); if (k4rc < 0) code = -3; } exit_k5_init: debug(F100,"krb5_init exit_k5_init","",0); #ifndef HEIMDAL /* Free krb5_address structures if we created them */ if ( addrs ) { for ( i=0;icontents ) free(addrs[i]->contents); free(addrs[i]); } } } #endif /* HEIMDAL */ krb5_errno = code; makestr(&krb5_errmsg,krb5_errno ? error_message(krb5_errno) : "OK"); if (client_name) krb5_free_unparsed_name(kcontext, client_name); /* my_creds is pointing at server */ debug(F100,"krb5_init calling krb5_free_principal()","",0); krb5_free_principal(kcontext, server); debug(F100,"krb5_init calling krb5_cc_close()","",0); krb5_cc_close(kcontext,ccache); debug(F100,"krb5_init calling krb5_free_context()","",0); krb5_free_context(kcontext); if (code != -2) printf("Result from realm %s: %s\r\n",realm, code==-3?"Unable to retrieve Kerberos IV credentials": code?error_message(code):"OK"); return(code?-1:0); } #ifndef HEIMDAL #define VALIDATE 0 #define RENEW 1 /* stripped down version of krb5_mk_req */ static krb5_error_code #ifdef CK_ANSIC krb5_validate_tgt( krb5_context context, krb5_ccache ccache, krb5_principal server, /* tgtname */ krb5_data *outbuf ) #else krb5_validate_tgt(context, ccache, server, outbuf) krb5_context context; krb5_ccache ccache; krb5_principal server; /* tgtname */ krb5_data *outbuf; #endif { return krb5_tgt_gen(context, ccache, server, outbuf, VALIDATE); } /* stripped down version of krb5_mk_req */ static krb5_error_code #ifdef CK_ANSIC krb5_renew_tgt(krb5_context context, krb5_ccache ccache, krb5_principal server, /* tgtname */ krb5_data *outbuf) #else krb5_renew_tgt(context, ccache, server, outbuf) krb5_context context; krb5_ccache ccache; krb5_principal server; /* tgtname */ krb5_data *outbuf; #endif { return krb5_tgt_gen(context, ccache, server, outbuf, RENEW); } /* stripped down version of krb5_mk_req */ static krb5_error_code #ifdef CK_ANSIC krb5_tgt_gen(krb5_context context, krb5_ccache ccache, krb5_principal server, /* tgtname */ krb5_data *outbuf, int opt) #else krb5_tgt_gen(context, ccache, server, outbuf, opt) krb5_context context; krb5_ccache ccache; krb5_principal server; /* tgtname */ krb5_data *outbuf; int opt; #endif { krb5_error_code retval; krb5_creds * credsp; krb5_creds creds; /* obtain ticket & session key */ memset((char *)&creds, 0, sizeof(creds)); if ((retval = krb5_copy_principal(context, server, &creds.server))) goto cleanup; if ((retval = krb5_cc_get_principal(context, ccache, &creds.client))) goto cleanup_creds; if (opt == VALIDATE) { if ((retval = krb5_get_credentials_validate(context, 0, ccache, &creds, &credsp))) goto cleanup_creds; } else { if ((retval = krb5_get_credentials_renew(context, 0, ccache, &creds, &credsp))) goto cleanup_creds; } /* we don't actually need to do the mk_req, just get the creds. */ cleanup_creds: krb5_free_cred_contents(context, &creds); cleanup: return retval; } #endif /* HEIMDAL */ #endif /* KINIT */ #ifdef KDESTROY int #ifdef CK_ANSIC ck_krb5_destroy(struct krb_op_data * op) #else ck_krb5_destroy(op) struct krb_op_data * op; #endif { krb5_context kcontext; krb5_error_code retval; int c; krb5_ccache ccache = NULL; char *cache_name = NULL; int code; int errflg=0; int quiet = 0; if ( !ck_krb5_is_installed() ) return(-1); code = krb5_init_context(&kcontext); if (code) { debug(F101,"ck_krb5_destroy while initializing krb5","",code); krb5_errno = code; makestr(&krb5_errmsg,error_message(krb5_errno)); return(-1); } code = k5_get_ccache(kcontext,&ccache,op->cache); if (code != 0) { debug(F101,"ck_krb5_destroy while getting ccache", "",code); krb5_free_context(kcontext); krb5_errno = code; makestr(&krb5_errmsg,error_message(krb5_errno)); return(-1); } code = krb5_cc_destroy (kcontext, ccache); if (code != 0) { debug(F101,"ck_krb5_destroy while destroying cache","",code); if ( code == KRB5_FCC_NOFILE ) printf("No ticket cache to destroy.\r\n"); else printf("Ticket cache NOT destroyed!\r\n"); krb5_cc_close(kcontext,ccache); krb5_free_context(kcontext); krb5_errno = code; makestr(&krb5_errmsg,error_message(krb5_errno)); return(-1); } printf("Tickets destroyed.\r\n"); /* Do not call krb5_cc_close() because cache has been destroyed */ krb5_free_context(kcontext); krb5_errno = 0; makestr(&krb5_errmsg,"OK"); return (0); } #else /* KDESTROY */ #ifdef KRB5 int #ifdef CK_ANSIC ck_krb5_destroy(struct krb_op_data * op) #else ck_krb5_destroy(op) struct krb_op_data * op; #endif { printf("?Not implemented.\r\n"); return(-1); } #endif /* KRB5 */ #endif /* KDESTROY */ #ifndef KLIST #ifdef KRB5 int #ifdef CK_ANSIC ck_krb5_list_creds(struct krb_op_data * op, struct krb5_list_cred_data * lc) #else ck_krb5_list_creds(op,lc) struct krb_op_data * op; struct krb5_list_cred_data * lc; #endif { printf("?Not implemented.\r\n"); return(-1); } #endif /* KRB5 */ #else /* KLIST */ static int show_flags = 0, show_time = 0, status_only = 0, show_keys = 0; static int show_etype = 0, show_addr = 0; static char *defname; static char *progname; static krb5_int32 now; static int timestamp_width; _PROTOTYP(static char * etype_string, (krb5_enctype )); _PROTOTYP(static void show_credential,(krb5_context,krb5_creds *)); _PROTOTYP(static int do_ccache, (krb5_context,char *)); _PROTOTYP(static int do_keytab, (krb5_context,char *)); _PROTOTYP(static void printtime, (time_t)); _PROTOTYP(static void fillit, (int, int)); #define DEFAULT 0 #define CCACHE 1 #define KEYTAB 2 int #ifdef CK_ANSIC ck_krb5_list_creds(struct krb_op_data * op, struct krb5_list_cred_data * lc) #else ck_krb5_list_creds(op,lc) struct krb_op_data * op; struct krb5_list_cred_data * lc; #endif { krb5_context kcontext; krb5_error_code retval; int code; char *name = op->cache; int mode; if ( !ck_krb5_is_installed() ) return(-1); code = krb5_init_context(&kcontext); if (code) { debug(F101,"ck_krb5_list_creds while initializing krb5","",code); krb5_errno = code; makestr(&krb5_errmsg,error_message(krb5_errno)); return(-1); } name = op->cache; mode = DEFAULT; show_flags = 0; show_time = 0; status_only = 0; show_keys = 0; show_etype = 0; show_addr = 0; show_flags = lc->flags; show_etype = lc->encryption; show_addr = lc->addr; show_time = 1; show_keys = 1; mode = CCACHE; if ((code = krb5_timeofday(kcontext, &now))) { if (!status_only) debug(F101,"ck_krb5_list_creds while getting time of day.", "",code); krb5_free_context(kcontext); krb5_errno = code; makestr(&krb5_errmsg,error_message(krb5_errno)); return(-1); } else { char tmp[BUFSIZ]; if (!krb5_timestamp_to_sfstring(now, tmp, 20, (char *) NULL) || !krb5_timestamp_to_sfstring(now, tmp, sizeof(tmp), (char *) NULL)) timestamp_width = (int) strlen(tmp); else timestamp_width = 15; } if (mode == DEFAULT || mode == CCACHE) retval = do_ccache(kcontext,name); else retval = do_keytab(kcontext,name); krb5_free_context(kcontext); return(retval); } static int #ifdef CK_ANSIC do_keytab(krb5_context kcontext, char * name) #else do_keytab(kcontext,name) krb5_context kcontext; char * name; #endif { krb5_keytab kt; krb5_keytab_entry entry; krb5_kt_cursor cursor; char buf[BUFSIZ]; /* hopefully large enough for any type */ char *pname; int code = 0; if (name == NULL) { if ((code = krb5_kt_default(kcontext, &kt))) { debug(F101,"ck_krb5_list_creds while getting default keytab", "",code); krb5_errno = code; makestr(&krb5_errmsg,error_message(krb5_errno)); return(-1); } } else { if ((code = krb5_kt_resolve(kcontext, name, &kt))) { debug(F111,"ck_krb5_list_creds while resolving keytab", name,code); krb5_errno = code; makestr(&krb5_errmsg,error_message(krb5_errno)); return(-1); } } if ((code = krb5_kt_get_name(kcontext, kt, buf, BUFSIZ))) { debug(F101,"ck_krb5_list_creds while getting keytab name", "",code); krb5_errno = code; makestr(&krb5_errmsg,error_message(krb5_errno)); return(-1); } printf("Keytab name: %s\r\n", buf); if ((code = krb5_kt_start_seq_get(kcontext, kt, &cursor))) { debug(F101,"ck_krb5_list_creds while starting keytab scan", "",code); krb5_errno = code; makestr(&krb5_errmsg,error_message(krb5_errno)); return(-1); } if (show_time) { printf("KVNO Timestamp"); fillit(timestamp_width - sizeof("Timestamp") + 2, (int) ' '); printf("Principal\r\n"); printf("---- "); fillit(timestamp_width, (int) '-'); printf(" "); fillit(78 - timestamp_width - sizeof("KVNO"), (int) '-'); printf("\r\n"); } else { printf("KVNO Principal\r\n"); printf( "---- --------------------------------------------------------------------\ ------\r\n"); } while ((code = krb5_kt_next_entry(kcontext, kt, &entry, &cursor)) == 0) { if ((code = krb5_unparse_name(kcontext, entry.principal, &pname))) { debug(F101,"ck_krb5_list_creds while unparsing principal name", "",code); krb5_errno = code; makestr(&krb5_errmsg,error_message(krb5_errno)); return(-1); } printf("%4d ", entry.vno); if (show_time) { printtime(entry.timestamp); printf(" "); } printf("%s", pname); if (show_etype) printf(" (%s) " , #ifdef HEIMDAL etype_string(entry.key.keytype) #else /* HEIMDAL */ etype_string(entry.key.enctype) #endif /* HEIMDAL */ ); if (show_keys) { printf(" (0x"); { int i; for (i = 0; i < entry.key.length; i++) printf("%02x", #ifdef HEIMDAL entry.key.keyvalue[i] #else /* HEIMDAL */ entry.key.contents[i] #endif /* HEIMDAL */ ); } printf(")"); } printf("\r\n"); krb5_free_unparsed_name(kcontext,pname); } if (code && code != KRB5_KT_END) { debug(F101,"ck_krb5_list_creds while scanning keytab", "",code); krb5_errno = code; makestr(&krb5_errmsg,error_message(krb5_errno)); return(-1); } if ((code = krb5_kt_end_seq_get(kcontext, kt, &cursor))) { debug(F101,"ck_krb5_list_creds while ending keytab scan", "",code); krb5_errno = code; makestr(&krb5_errmsg,error_message(krb5_errno)); return(-1); } krb5_errno = 0; makestr(&krb5_errmsg,"OK"); return(0); } static int #ifdef CK_ANSIC do_ccache(krb5_context kcontext, char * cc_name) #else do_ccache(kcontext,name) krb5_context kcontext; char * cc_name; #endif { krb5_ccache cache = NULL; krb5_cc_cursor cur; krb5_creds creds; krb5_principal princ=NULL; krb5_flags flags=0; krb5_error_code code = 0; int exit_status = 0; if (status_only) /* exit_status is set back to 0 if a valid tgt is found */ exit_status = 1; code = k5_get_ccache(kcontext,&cache,cc_name); if (code != 0) { debug(F111,"do_ccache while getting ccache", error_message(code),code); krb5_errno = code; makestr(&krb5_errmsg,error_message(krb5_errno)); return(-1); } flags = 0; /* turns off OPENCLOSE mode */ if ((code = krb5_cc_set_flags(kcontext, cache, flags))) { if (code == ENOENT) { debug(F111,"ck_krb5_list_creds (ticket cache)", krb5_cc_get_name(kcontext, cache),code); } else { debug(F111, "ck_krb5_list_creds while setting cache flags (ticket cache)", krb5_cc_get_name(kcontext, cache),code); } printf("No ticket File.\r\n"); krb5_errno = code; makestr(&krb5_errmsg,error_message(krb5_errno)); krb5_cc_close(kcontext,cache); return(-1); } if ((code = krb5_cc_get_principal(kcontext, cache, &princ))) { debug(F101,"ck_krb5_list_creds while retrieving principal name", "",code); krb5_errno = code; makestr(&krb5_errmsg,error_message(krb5_errno)); krb5_cc_close(kcontext,cache); return(-1); } if ((code = krb5_unparse_name(kcontext, princ, &defname))) { debug(F101,"ck_krb5_list_creds while unparsing principal name", "",code); krb5_errno = code; makestr(&krb5_errmsg,error_message(krb5_errno)); krb5_cc_close(kcontext,cache); return(-1); } if (!status_only) { printf("Ticket cache: %s:%s\r\nDefault principal: %s\r\n\r\n", krb5_cc_get_type(kcontext, cache), krb5_cc_get_name(kcontext, cache), defname); printf("Valid starting"); fillit(timestamp_width - sizeof("Valid starting") + 3, (int) ' '); printf("Expires"); fillit(timestamp_width - sizeof("Expires") + 3, (int) ' '); printf("Service principal\r\n"); } if ((code = krb5_cc_start_seq_get(kcontext, cache, &cur))) { debug(F101,"ck_krb5_list_creds while starting to retrieve tickets", "",code); krb5_errno = code; makestr(&krb5_errmsg,error_message(krb5_errno)); krb5_cc_close(kcontext,cache); return(-1); } while (!(code = krb5_cc_next_cred(kcontext, cache, &cur, &creds))) { if (status_only) { if (exit_status && creds.server->length == 2 && strcmp(creds.server->realm.data, princ->realm.data) == 0 && strcmp((char *)creds.server->data[0].data, "krbtgt") == 0 && strcmp((char *)creds.server->data[1].data, princ->realm.data) == 0 && creds.times.endtime > now) exit_status = 0; } else { show_credential(kcontext, &creds); } krb5_free_cred_contents(kcontext, &creds); } printf("\r\n"); if (code == KRB5_CC_END || code == KRB5_CC_NOTFOUND) { if ((code = krb5_cc_end_seq_get(kcontext, cache, &cur))) { debug(F101,"ck_krb5_list_creds while finishing ticket retrieval", "",code); krb5_errno = code; makestr(&krb5_errmsg,error_message(krb5_errno)); krb5_cc_close(kcontext,cache); return(-1); } flags = KRB5_TC_OPENCLOSE; /* turns on OPENCLOSE mode */ if ((code = krb5_cc_set_flags(kcontext, cache, flags))) { debug(F101,"ck_krb5_list_creds while closing ccache", "",code); krb5_errno = code; makestr(&krb5_errmsg,error_message(krb5_errno)); krb5_cc_close(kcontext,cache); return(-1); } krb5_errno = 0; makestr(&krb5_errmsg,"OK"); krb5_cc_close(kcontext,cache); return(0); } else { debug(F101,"ck_krb5_list_creds while retrieving a ticket","",code); krb5_errno = code; makestr(&krb5_errmsg,error_message(krb5_errno)); krb5_cc_close(kcontext,cache); return(-1); } krb5_errno = 0; makestr(&krb5_errmsg,"OK"); krb5_cc_close(kcontext,cache); return(0); } static char * #ifdef CK_ANSIC #ifdef HEIMDAL etype_string(krb5_keytype enctype) #else /* HEIMDAL */ etype_string(krb5_enctype enctype) #endif /* HEIMDAL */ #else #ifdef HEIMDAL etype_string(enctype) krb5_keytype enctype; #else /* HEIMDAL */ etype_string(enctype) krb5_enctype enctype; #endif /* HEIMDAL */ #endif { static char buf[12]; switch (enctype) { case ENCTYPE_NULL: return "NULL"; case ENCTYPE_DES_CBC_CRC: return "DES-CBC-CRC"; case ENCTYPE_DES_CBC_MD4: return "DES-CBC-MD4"; case ENCTYPE_DES_CBC_MD5: return "DES-CBC-MD5"; case ENCTYPE_DES_CBC_RAW: return "DES-CBC-RAW"; case ENCTYPE_DES3_CBC_SHA: return "DES3-CBC-SHA"; case ENCTYPE_DES3_CBC_RAW: return "DES3-CBC-RAW"; case ENCTYPE_DES_HMAC_SHA1: return "DES-HMAC-SHA1"; case ENCTYPE_DES3_CBC_SHA1: return "DES3-CBC-SHA1"; case ENCTYPE_AES128_CTS_HMAC_SHA1_96: return "AES128_CTS-HMAC-SHA1_96"; case ENCTYPE_AES256_CTS_HMAC_SHA1_96: return "AES256_CTS-HMAC-SHA1_96"; case ENCTYPE_ARCFOUR_HMAC: return "RC4-HMAC-NT"; case ENCTYPE_ARCFOUR_HMAC_EXP: return "RC4-HMAC-NT-EXP"; case ENCTYPE_UNKNOWN: return "UNKNOWN"; case ENCTYPE_LOCAL_DES3_HMAC_SHA1: return "LOCAL-DES3-HMAC-SHA1"; case ENCTYPE_LOCAL_RC4_MD4: return "LOCAL-RC4-MD4"; default: ckmakmsg(buf, sizeof(buf),"etype ", ckitoa(enctype),NULL,NULL); return buf; break; } } static char * #ifdef CK_ANSIC flags_string(register krb5_creds *cred) #else flags_string(cred) register krb5_creds *cred; #endif { static char buf[32]; int i = 0; if (cred->ticket_flags & TKT_FLG_FORWARDABLE) buf[i++] = 'F'; if (cred->ticket_flags & TKT_FLG_FORWARDED) buf[i++] = 'f'; if (cred->ticket_flags & TKT_FLG_PROXIABLE) buf[i++] = 'P'; if (cred->ticket_flags & TKT_FLG_PROXY) buf[i++] = 'p'; if (cred->ticket_flags & TKT_FLG_MAY_POSTDATE) buf[i++] = 'D'; if (cred->ticket_flags & TKT_FLG_POSTDATED) buf[i++] = 'd'; if (cred->ticket_flags & TKT_FLG_INVALID) buf[i++] = 'i'; if (cred->ticket_flags & TKT_FLG_RENEWABLE) buf[i++] = 'R'; if (cred->ticket_flags & TKT_FLG_INITIAL) buf[i++] = 'I'; if (cred->ticket_flags & TKT_FLG_HW_AUTH) buf[i++] = 'H'; if (cred->ticket_flags & TKT_FLG_PRE_AUTH) buf[i++] = 'A'; buf[i] = '\0'; return(buf); } static char * #ifdef CK_ANSIC short_date(long *dp) #else short_date(dp) long *dp; #endif { register char *cp; #ifndef ctime extern char *ctime(); #endif /* ctime */ cp = ctime(dp) + 4; cp[15] = '\0'; return (cp); } static VOID #ifdef CK_ANSIC printtime(time_t tv) #else printtime(tv) time_t tv; #endif { char timestring[BUFSIZ]; char format[12]; char fill; fill = ' '; sprintf(format,"%%-%ds",timestamp_width); /* safe */ if (!krb5_timestamp_to_sfstring((krb5_timestamp) tv, timestring, timestamp_width+1, &fill)) { printf(format,timestring); } else { printf(format,short_date(&tv)); } } static void #ifdef CK_ANSIC one_addr(krb5_address *a) #else one_addr(a) krb5_address *a; #endif { struct hostent *h; extern tcp_rdns; if ((a->addrtype == ADDRTYPE_INET) && (a->length == 4)) { if (tcp_rdns != SET_OFF) { h = gethostbyaddr(a->contents, 4, AF_INET); if (h) { #ifdef HADDRLIST h = ck_copyhostent(h); #endif /* HADDRLIST */ printf("%s (%d.%d.%d.%d)", h->h_name, a->contents[0], a->contents[1], a->contents[2], a->contents[3]); } } if (tcp_rdns == SET_OFF || !h) { printf("%d.%d.%d.%d", a->contents[0], a->contents[1], a->contents[2], a->contents[3]); } } else { printf("unknown addr type %d", a->addrtype); } } static VOID #ifdef CK_ANSIC show_credential(krb5_context kcontext, register krb5_creds * cred) #else show_credential(kcontext, cred) krb5_context kcontext; register krb5_creds * cred; #endif { krb5_error_code retval=0; krb5_ticket *tkt=NULL; char *name=NULL, *sname=NULL, *flags=NULL; int extra_field = 0; retval = krb5_unparse_name(kcontext, cred->client, &name); if (retval) { debug(F101,"ck_krb5_list_creds while unparsing client name","",retval); krb5_errno = retval; makestr(&krb5_errmsg,error_message(krb5_errno)); return; } retval = krb5_unparse_name(kcontext, cred->server, &sname); if (retval) { debug(F101,"ck_krb5_list_creds while unparsing server name","",retval); free(name); krb5_errno = retval; makestr(&krb5_errmsg,error_message(krb5_errno)); return; } if (!cred->times.starttime) cred->times.starttime = cred->times.authtime; printtime(cred->times.starttime); printf(" "); if ( time(0) < cred->times.endtime ) printtime(cred->times.endtime); else printf("** expired ** "); printf(" %s\r\n", sname); if (strcmp(name, defname)) { printf(" for client %s", name); extra_field++; } if (cred->times.renew_till) { if (!extra_field) printf(" "); else printf(", "); printf("renew until "); printtime(cred->times.renew_till); extra_field += 2; } if (extra_field > 3) { printf("\r\n"); extra_field = 0; } if (show_flags) { flags = flags_string(cred); if (flags && *flags) { if (!extra_field) printf(" "); else printf(", "); printf("Flags: %s", flags); extra_field++; } } if (extra_field > 2) { printf("\r\n"); extra_field = 0; } if (show_etype) { retval = decode_krb5_ticket(&cred->ticket, &tkt); if (!extra_field) printf(" "); else printf(", "); #ifdef HEIMDAL printf("Etype (skey, tkt): %s, %s ", etype_string(cred->session.keytype), etype_string(tkt->enc_part.keytype)); #else /* HEIMDAL */ printf("Etype (skey, tkt): %s, %s ", etype_string(cred->keyblock.enctype), etype_string(tkt->enc_part.enctype)); #endif /* HEIMDAL */ krb5_free_ticket(kcontext, tkt); extra_field++; } /* if any additional info was printed, extra_field is non-zero */ if (extra_field) printf("\r\n"); if ( show_addr ) { if (!cred->addresses || !cred->addresses[0]) { printf("\tAddresses: (none)\r\n"); } else { int i; for (i=0; cred->addresses[i]; i++) { if (i) printf(" "); else printf(" Addresses: "); one_addr(cred->addresses[i]); printf("\r\n"); } } } krb5_free_unparsed_name(kcontext,name); krb5_free_unparsed_name(kcontext,sname); krb5_errno = 0; makestr(&krb5_errmsg,"OK"); } static VOID #ifdef CK_ANSIC fillit(int num, int c) #else fillit(num, c) int num; int c; #endif { int i; for (i=0; i= '0' && c <= '9') return c - '0'; if (c >= 'A' && c <= 'F') return c - 'A' + 10; if (c >= 'a' && c <= 'f') return c - 'a' + 10; return -1; } /* returns: NULL for ok, pointer to error string for bad input */ static char* #ifdef CK_ANSIC hex_scan_four_bytes(char *out, char *in) #else hex_scan_four_bytes(out, in) char *out; char *in; #endif { int i; int c; char c1; for (i=0; i<8; i++) { if(!in[i]) return "not enough input"; c = hex_scan_nybble(in[i]); if(c<0) return "invalid digit"; c1 = c; i++; if(!in[i]) return "not enough input"; c = hex_scan_nybble(in[i]); if(c<0) return "invalid digit"; *out++ = (c1 << 4) + c; } switch(in[i]) { case 0: case '\r': case '\n': return NULL; default: return "extra characters at end of input"; } } #endif /* COMMENT */ /* ck_krb4_initTGT() returns 0 on success */ int #ifdef CK_ANSIC ck_krb4_initTGT(struct krb_op_data * op, struct krb4_init_data * init) #else ck_krb4_initTGT(op,init) struct krb_op_data * op, struct krb4_init_data * init #endif { char aname[ANAME_SZ+1]; char inst[INST_SZ+1]; char realm[REALM_SZ+1]; char *password=NULL; char passwd[80]=""; char *username = NULL; char *usernameptr=NULL; int iflag, /* Instance */ rflag, /* Realm */ vflag, /* Verbose */ lflag, /* Lifetime */ pflag, /* Preauth */ lifetime=KRB_DEFAULT_LIFE, /* Life Time */ k_errno; register char *cp; register i; if ( !ck_krb4_is_installed() ) return(-1); *inst = *realm = '\0'; iflag = rflag = vflag = lflag = pflag = 0; vflag = init->verbose; pflag = init->preauth; if ( init->lifetime ) { lifetime = init->lifetime<5?1:init->lifetime/5; if ( lifetime > 255 ) lifetime = 255; } else lifetime = KRB_DEFAULT_LIFE; username = init->principal; if (username && username[0] && (k_errno = kname_parse(aname, inst, realm, username)) != AUTH_SUCCESS) { krb4_errno = k_errno; makestr(&krb4_errmsg,krb_get_err_text_entry(k_errno)); printf("%s\r\n", krb_get_err_text_entry(k_errno)); iflag = rflag = 1; username = NULL; } if ( init->realm ) { ckstrncpy(realm,init->realm,REALM_SZ); } if ( init->instance ) { ckstrncpy(inst,init->instance, INST_SZ); } #ifdef COMMENT if ( vflag ) printf("Kerberos IV initialization\r\n"); #endif /* COMMENT */ if (!username || !username[0]) { debug(F100,"ck_krb4_initTGT no username specified","",0); printf("?Invalid principal specified.\r\n"); krb4_errno = -1; makestr(&krb4_errmsg,"No principal specified"); return(-1); } if (!*realm) { ckstrncpy(realm,ck_krb4_getrealm(),REALM_SZ); } if ( init->password ) password = init->password; else { char prmpt[80]; int ok; ckmakxmsg(prmpt,sizeof(prmpt), "Kerberos 4 Password for ",username,"@",realm,": ", NULL,NULL,NULL,NULL,NULL,NULL,NULL); ok = uq_txt(NULL,prmpt,2,NULL,passwd,80,NULL,DEFAULT_UQ_TIMEOUT); if ( ok ) password = passwd; } if (pflag) { k_errno = krb_get_pw_in_tkt_preauth( aname, inst, realm, "krbtgt", realm, lifetime, password); if (k_errno == -1) { /* preauth method not available */ k_errno = krb_get_pw_in_tkt(aname, inst, realm, "krbtgt", realm, lifetime, password); } } else { k_errno = krb_get_pw_in_tkt(aname, inst, realm, "krbtgt", realm, lifetime, password); } memset(passwd,0,sizeof(passwd)); if (k_errno) { printf("%s for principal %s%s%s@%s\r\n", krb_get_err_text_entry(k_errno), aname, inst[0]?".":"", inst, realm); krb4_errno = k_errno; makestr(&krb4_errmsg,krb_get_err_text_entry(k_errno)); return(-1); } else if (vflag) { printf("Result from realm %s: ", realm); printf("%s\r\n", krb_get_err_text_entry(k_errno)); } krb4_errno = k_errno; makestr(&krb4_errmsg,krb_get_err_text_entry(k_errno)); return(0); } #endif /* KINIT */ #ifdef KDESTROY int #ifdef CK_ANSIC ck_krb4_destroy(struct krb_op_data * op) #else ck_krb4_destroy(op) struct krb_op_data * op; #endif { int k_errno=0; if ( !ck_krb4_is_installed() ) return(-1); k_errno = dest_tkt(); krb4_errno = k_errno; makestr(&krb4_errmsg,krb_get_err_text_entry(k_errno)); if (k_errno == 0) printf("Tickets destroyed.\r\n"); else if (k_errno == RET_TKFIL) printf("No tickets to destroy.\r\n"); else { printf("Tickets MAY NOT be destroyed.\r\n"); return(-1); } return(0); } #endif /* KDESTROY */ #ifdef KLIST _PROTOTYP(static int display_tktfile,(char *, int, int, int)); int #ifdef CK_ANSIC ck_krb4_list_creds(struct krb_op_data * op) #else ck_krb4_list_creds(op) struct krb_op_data * op; #endif { int long_form = 1; int tgt_test = 0; int do_srvtab = 0; int show_kvnos = 0; char *tkt_file = NULL; if ( !ck_krb4_is_installed() ) return(-1); if ( op->cache ) tkt_file = op->cache; if ( k4debug ) { show_kvnos = 1; } if (do_srvtab) return(display_srvtab(tkt_file)); else return(display_tktfile(tkt_file, tgt_test, long_form, show_kvnos)); } #ifndef KRB5 static int timestamp_width=0; static char * #ifdef CK_ANSIC short_date(long *dp) #else short_date(dp) long *dp; #endif { register char *cp; extern char *ctime(); cp = ctime(dp) + 4; cp[15] = '\0'; return (cp); } static VOID #ifdef CK_ANSIC printtime(time_t tv) #else printtime(tv) time_t tv; #endif { char timestring[BUFSIZ]; char format[12]; char fill; fill = ' '; sprintf(format,"%%-%ds",timestamp_width); /* safe */ printf(format,short_date(&tv)); } #endif /* KRB5 */ static int #ifdef CK_ANSIC display_tktfile(char *file, int tgt_test, int long_form, int show_kvnos) #else display_tktfile(file,tgt_test,long_form,show_kvnos) char *file; int tgt_test; int long_form; int show_kvnos; #endif { char pname[ANAME_SZ]; char pinst[INST_SZ]; char prealm[REALM_SZ]; char buf1[20], buf2[20]; int k_errno; #ifdef OS2 LEASH_CREDENTIALS creds; #else /* OS2 */ CREDENTIALS creds; #endif /* OS2 */ int header = 1; file = tkt_string(); if (long_form) { printf("Ticket cache: %s\r\n", file); } /* * Since krb_get_tf_realm will return a ticket_file error, * we will call tf_init and tf_close first to filter out * things like no ticket file. Otherwise, the error that * the user would see would be * klist: can't find realm of ticket file: No ticket file (tf_util) * instead of * klist: No ticket file (tf_util) */ /* Open ticket file */ if (k_errno = tf_init(file, R_TKT_FIL)) { if (!tgt_test) printf("%s\r\n", krb_get_err_text_entry (k_errno)); krb4_errno = k_errno; makestr(&krb4_errmsg,krb_get_err_text_entry(k_errno)); return(-1); } /* Close ticket file */ (void) tf_close(); /* * We must find the realm of the ticket file here before calling * tf_init because since the realm of the ticket file is not * really stored in the principal section of the file, the * routine we use must itself call tf_init and tf_close. */ if ((k_errno = krb_get_tf_realm(file, prealm)) != AUTH_SUCCESS) { if (!tgt_test) printf("can't find realm of ticket file: %s\r\n", krb_get_err_text_entry (k_errno)); krb4_errno = k_errno; makestr(&krb4_errmsg,krb_get_err_text_entry(k_errno)); return(-1); } /* Open ticket file */ if (k_errno = tf_init(file, R_TKT_FIL)) { if (!tgt_test) printf("%s\r\n", krb_get_err_text_entry (k_errno)); krb4_errno = k_errno; makestr(&krb4_errmsg,krb_get_err_text_entry(k_errno)); return(-1); } /* Get principal name and instance */ if ((k_errno = tf_get_pname(pname)) || (k_errno = tf_get_pinst(pinst))) { (void) tf_close(); if (!tgt_test) printf("%s\r\n", krb_get_err_text_entry (k_errno)); krb4_errno = k_errno; makestr(&krb4_errmsg,krb_get_err_text_entry(k_errno)); return(-1); } /* * You may think that this is the obvious place to get the * realm of the ticket file, but it can't be done here as the * routine to do this must open the ticket file. This is why * it was done before tf_init. */ if (!tgt_test && long_form) printf("Default principal: %s%s%s%s%s\r\n\r\n", pname, (pinst[0] ? "." : ""), pinst, (prealm[0] ? "@" : ""), prealm); while ((k_errno = tf_get_cred(&creds)) == AUTH_SUCCESS) { if (!tgt_test && long_form && header) { printf("%-17s %-17s %s\r\n", "Valid starting", "Expires", "Service principal"); header = 0; } if (tgt_test) { creds.issue_date += ((unsigned char) creds.lifetime) * 5 * 60; if (!strcmp(creds.service, "krbtgt") && !strcmp(creds.instance, prealm)) { krb4_errno = k_errno; makestr(&krb4_errmsg,krb_get_err_text_entry(k_errno)); (void) tf_close(); if (time(0) < creds.issue_date) { return(0); /* tgt hasn't expired */ } else { return(-1); /* has expired */ } } continue; /* not a tgt */ } if (long_form) { timestamp_width = 17; /* for k5 display function */ /* if available */ printtime(creds.issue_date); printf(" "); creds.issue_date += ((unsigned char) creds.lifetime) * 5 * 60; if ( time(0) < creds.issue_date ) printtime(creds.issue_date); else printf("*** expired *** "); printf(" "); } if (show_kvnos) printf("%s%s%s%s%s (%d)\r\n", creds.service, (creds.instance[0] ? "." : ""), creds.instance, (creds.realm[0] ? "@" : ""), creds.realm, creds.kvno); else printf("%s%s%s%s%s\r\n", creds.service, (creds.instance[0] ? "." : ""), creds.instance, (creds.realm[0] ? "@" : ""), creds.realm); #ifdef OS2 if ( creds.address[0] ) printf(" Address: %s\r\n",creds.address); #endif /* OS2 */ } (void) tf_close(); if (tgt_test) { return(-1); }/* no tgt found */ if (header && long_form && k_errno == EOF) { printf("No tickets in file.\r\n"); } krb4_errno = k_errno; makestr(&krb4_errmsg,krb_get_err_text_entry(k_errno)); return(0); } #ifdef COMMENT /* Just so we remember what the command line interface looked like */ usage() { printf( "Usage: [ -s | -t ] [ -file filename ] [ -srvtab ] [ -version ]\r\n"); return(-1); } #endif /* COMMENT */ /* adapted from getst() in librkb */ /* * ok_getst() takes a file descriptor, a string and a count. It reads * from the file until either it has read "count" characters, or until * it reads a null byte. When finished, what has been read exists in * the given string "s". If "count" characters were actually read, the * last is changed to a null, so the returned string is always null- * terminated. ok_getst() returns the number of characters read, including * the null terminator. * * If there is a read error, it returns -1 (like the read(2) system call) */ static int #ifdef CK_ANSIC ok_getst(int fd, register char *s, int n) #else ok_getst(fd, s, n) int fd; register char *s; int n; #endif { register int count = n; int err; while ((err = read(fd, s, 1)) > 0 && --count) if (*s++ == '\0') return (n - count); if (err < 0) return(-1); *s = '\0'; return (n - count); } int #ifdef CK_ANSIC display_srvtab(char *file) #else display_srvtab(file) char *file; #endif { int stab; char serv[SNAME_SZ]; char inst[INST_SZ]; char rlm[REALM_SZ]; unsigned char key[8]; unsigned char vno; int count; printf("Server key file: %s\r\n", file); #ifdef NT #ifndef O_RDONLY #define O_RDONLY _O_RDONLY #endif /* O_RDONLY */ #endif /* NT */ if ((stab = open(file, O_RDONLY, 0400)) < 0) { perror(file); return(-1); } printf("%-15s %-15s %-10s %s\r\n","Service","Instance","Realm", "Key Version"); printf("------------------------------------------------------\r\n"); /* argh. getst doesn't return error codes, it silently fails */ while (((count = ok_getst(stab, serv, SNAME_SZ)) > 0) && ((count = ok_getst(stab, inst, INST_SZ)) > 0) && ((count = ok_getst(stab, rlm, REALM_SZ)) > 0)) { if (((count = read(stab,(char *) &vno,1)) != 1) || ((count = read(stab,(char *) key,8)) != 8)) { if (count < 0) perror("reading from key file"); else printf("key file truncated\r\n"); return(-1); } printf("%-15s %-15s %-15s %d\r\n",serv,inst,rlm,vno); } if (count < 0) perror(file); (void) close(stab); return(0); } #endif /* KLIST */ #else /* KRB4 */ int ck_krb4_autoget_TGT(char * dummy) { return(-1); } #ifdef CK_KERBEROS int #ifdef CK_ANSIC ck_krb4_initTGT(struct krb_op_data * op, struct krb4_init_data * init) #else ck_krb4_initTGT(op,init) struct krb_op_data * op, struct krb4_init_data * init #endif { return(-1); } #ifdef CK_ANSIC ck_krb4_destroy(struct krb_op_data * op) #else ck_krb4_destroy(op) struct krb_op_data * op; #endif { return(-1); } int #ifdef CK_ANSIC ck_krb4_list_creds(struct krb_op_data * op) #else ck_krb4_list_creds(op) struct krb_op_data * op; #endif { return(-1); } #else /* CK_KERBEROS */ int ck_krb4_initTGT(void * a, void *b) { return(-1); } int ck_krb4_destroy(void *a) { return(-1); } int ck_krb4_list_creds(void *a) { return(-1); } #endif /* CK_KERBEROS */ #endif /* KRB4 */ /* The following functions are used to implement the Kermit Script Language */ /* functions */ struct tkt_list_item { char * name; struct tkt_list_item * next; }; static struct tkt_list_item * k4_tkt_list = NULL; int #ifdef CK_ANSIC ck_krb4_get_tkts(VOID) #else ck_krb4_get_tkts() #endif { #ifdef KRB4 char *file=NULL; char pname[ANAME_SZ]; char pinst[INST_SZ]; char prealm[REALM_SZ]; char buf1[20], buf2[20]; int k_errno; #ifdef OS2 LEASH_CREDENTIALS creds; #else /* OS2 */ CREDENTIALS creds; #endif /* OS2 */ int tkt_count=0; struct tkt_list_item ** list = &k4_tkt_list; while ( k4_tkt_list ) { struct tkt_list_item * next; next = k4_tkt_list->next; free(k4_tkt_list->name); free(k4_tkt_list); k4_tkt_list = next; } if ( !ck_krb4_is_installed() ) return(-1); file = tkt_string(); /* * Since krb_get_tf_realm will return a ticket_file error, * we will call tf_init and tf_close first to filter out * things like no ticket file. Otherwise, the error that * the user would see would be * klist: can't find realm of ticket file: No ticket file (tf_util) * instead of * klist: No ticket file (tf_util) */ /* Open ticket file */ if (k_errno = tf_init(file, R_TKT_FIL)) { return(-1); } /* Close ticket file */ (void) tf_close(); /* * We must find the realm of the ticket file here before calling * tf_init because since the realm of the ticket file is not * really stored in the principal section of the file, the * routine we use must itself call tf_init and tf_close. */ if ((k_errno = krb_get_tf_realm(file, prealm)) != AUTH_SUCCESS) { return(-1); } /* Open ticket file */ if (k_errno = tf_init(file, R_TKT_FIL)) { return(-1); } /* Get principal name and instance */ if ((k_errno = tf_get_pname(pname)) || (k_errno = tf_get_pinst(pinst))) { return(-1); } /* * You may think that this is the obvious place to get the * realm of the ticket file, but it can't be done here as the * routine to do this must open the ticket file. This is why * it was done before tf_init. */ while ((k_errno = tf_get_cred(&creds)) == AUTH_SUCCESS) { char tkt_buf[256]; ckmakxmsg(tkt_buf,sizeof(tkt_buf), creds.service, (creds.instance[0] ? "." : ""), creds.instance, (creds.realm[0] ? "@" : ""), creds.realm, NULL,NULL,NULL,NULL,NULL,NULL,NULL); *list = (struct tkt_list_item *) malloc(sizeof(struct tkt_list_item)); (*list)->name = strdup(tkt_buf); (*list)->next = NULL; list = &((*list)->next); tkt_count++; } tf_close(); return(tkt_count); #else /* KRB4 */ return(0); #endif /* KRB4 */ } char * #ifdef CK_ANSIC ck_krb4_get_next_tkt(VOID) #else ck_krb4_get_next_tkt() #endif { #ifdef KRB4 static char * s=NULL; struct tkt_list_item * next=NULL; if ( s ) { free(s); s = NULL; } if ( k4_tkt_list == NULL ) return(NULL); next = k4_tkt_list->next; s = k4_tkt_list->name; free(k4_tkt_list); k4_tkt_list = next; return(s); #else /* KRB4 */ return(NULL); #endif /* KRB4 */ } int #ifdef CK_ANSIC ck_krb4_tkt_isvalid(char * tktname) #else ck_krb4_tkt_isvalid(tktname) char * tktname; #endif { #ifdef KRB4 char *file=NULL; char pname[ANAME_SZ]; char pinst[INST_SZ]; char prealm[REALM_SZ]; char buf1[20], buf2[20]; int k_errno; time_t issue_t, expire_t, now_t; #ifdef OS2 LEASH_CREDENTIALS creds; #else /* OS2 */ CREDENTIALS creds; #endif /* OS2 */ if ( !ck_krb4_is_installed() ) return(-1); debug(F110,"ck_krb4_tkt_isvalid","tkt_string",0); file = tkt_string(); /* * Since krb_get_tf_realm will return a ticket_file error, * we will call tf_init and tf_close first to filter out * things like no ticket file. Otherwise, the error that * the user would see would be * klist: can't find realm of ticket file: No ticket file (tf_util) * instead of * klist: No ticket file (tf_util) */ /* Open ticket file */ debug(F110,"ck_krb4_tkt_isvalid","tf_init",0); if (k_errno = tf_init(file, R_TKT_FIL)) { return(-1); } /* Close ticket file */ debug(F110,"ck_krb4_tkt_isvalid","tf_close",0); (void) tf_close(); /* * We must find the realm of the ticket file here before calling * tf_init because since the realm of the ticket file is not * really stored in the principal section of the file, the * routine we use must itself call tf_init and tf_close. */ debug(F110,"ck_krb4_tkt_isvalid","krb_get_tf_realm",0); if ((k_errno = krb_get_tf_realm(file, prealm)) != AUTH_SUCCESS) { return(-1); } /* Open ticket file */ debug(F110,"ck_krb4_tkt_isvalid","tf_init",0); if (k_errno = tf_init(file, R_TKT_FIL)) { return(-1); } /* Get principal name and instance */ debug(F110,"ck_krb4_tkt_isvalid","tf_get_name/tf_get_pinst",0); if ((k_errno = tf_get_pname(pname)) || (k_errno = tf_get_pinst(pinst))) { /* Close ticket file */ debug(F110,"ck_krb4_tkt_isvalid","tf_close",0); (void) tf_close(); return(-1); } /* * You may think that this is the obvious place to get the * realm of the ticket file, but it can't be done here as the * routine to do this must open the ticket file. This is why * it was done before tf_init. */ debug(F110,"ck_krb4_tkt_isvalid","tf_get_cred",0); while ((k_errno = tf_get_cred(&creds)) == AUTH_SUCCESS) { char tkt_buf[256]; ckmakxmsg(tkt_buf,sizeof(tkt_buf), creds.service, (creds.instance[0] ? "." : ""), creds.instance, (creds.realm[0] ? "@" : ""), creds.realm, NULL,NULL,NULL,NULL,NULL,NULL,NULL); if ( !strcmp(tktname,tkt_buf) ) { /* we found the ticket we are looking for */ issue_t = creds.issue_date; expire_t = creds.issue_date + ((unsigned char) creds.lifetime) * 5 * 60; now_t = time(0); /* We add a 5 minutes fudge factor to compensate for potential */ /* clock skew errors between the KDC and K95's host OS */ if ( now_t >= (issue_t-300) && now_t < expire_t) { #ifdef OS2 #ifdef CHECKADDRS if ( krb4_checkaddrs ) { extern char myipaddr[20]; /* From ckcnet.c */ if ( !myipaddr[0] ) { int i; char buf[60]; for ( i=0;i<64;i++ ) { if ( getlocalipaddrs(buf,60,i) < 0 ) break; if ( !strcmp(buf,creds.address) ) { /* Close ticket file */ debug(F110,"ck_krb4_tkt_isvalid","tf_close",0); (void) tf_close(); return(1); /* They're the same */ } } /* Close ticket file */ debug(F110,"ck_krb4_tkt_isvalid","tf_close",0); (void) tf_close(); return(0); /* They're different */ } else if ( strcmp(myipaddr,creds.address) ) { /* Close ticket file */ debug(F110,"ck_krb4_tkt_isvalid","tf_close",0); (void) tf_close(); return(0); /* They're different */ } else { /* Close ticket file */ debug(F110,"ck_krb4_tkt_isvalid","tf_close",0); (void) tf_close(); return(1); /* They're the same */ } } else { /* Close ticket file */ debug(F110,"ck_krb4_tkt_isvalid","tf_close",0); (void) tf_close(); return(1); /* They're the same */ } #else /* CHECKADDRS */ /* Close ticket file */ debug(F110,"ck_krb4_tkt_isvalid","tf_close",0); (void) tf_close(); return(1); /* valid but no ip address check */ #endif /* CHECKADDRS */ #else /* OS2 */ /* Close ticket file */ debug(F110,"ck_krb4_tkt_isvalid","tf_close",0); (void) tf_close(); return(1); /* Valid but no ip address check */ #endif /* OS2 */ } else { /* Close ticket file */ debug(F110,"ck_krb4_tkt_isvalid","tf_close",0); (void) tf_close(); return(0); /* expired or otherwise invalid */ } } } /* Close ticket file */ debug(F110,"ck_krb4_tkt_isvalid","tf_close",0); (void) tf_close(); return(0); /* could not find the desired ticket */ #else /* KRB4 */ return(-1); #endif /* KRB4 */ } int #ifdef CK_ANSIC ck_krb4_is_tgt_valid(VOID) #else ck_krb4_is_tgt_valid() #endif { #ifdef KRB4 char tgt[256]; char * s; int rc = 0; s = krb4_d_realm ? krb4_d_realm : ck_krb4_getrealm(); ckmakmsg(tgt,sizeof(tgt),"krbtgt.",s,"@",s); rc = ck_krb4_tkt_isvalid(tgt); debug(F111,"ck_krb4_is_tgt_valid",tgt,rc); return(rc > 0); #else /* KRB4 */ return(0); #endif /* KRB4 */ } int #ifdef CK_ANSIC ck_krb4_tkt_time(char * tktname) #else ck_krb4_tkt_time(tktname) char * tktname; #endif { #ifdef KRB4 char *file=NULL; char pname[ANAME_SZ]; char pinst[INST_SZ]; char prealm[REALM_SZ]; char buf1[20], buf2[20]; int k_errno; #ifdef OS2 LEASH_CREDENTIALS creds; #else /* OS2 */ CREDENTIALS creds; #endif /* OS2 */ if ( !ck_krb4_is_installed() ) return(-1); file = tkt_string(); /* * Since krb_get_tf_realm will return a ticket_file error, * we will call tf_init and tf_close first to filter out * things like no ticket file. Otherwise, the error that * the user would see would be * klist: can't find realm of ticket file: No ticket file (tf_util) * instead of * klist: No ticket file (tf_util) */ /* Open ticket file */ if (k_errno = tf_init(file, R_TKT_FIL)) { return(-1); } /* Close ticket file */ (void) tf_close(); /* * We must find the realm of the ticket file here before calling * tf_init because since the realm of the ticket file is not * really stored in the principal section of the file, the * routine we use must itself call tf_init and tf_close. */ if ((k_errno = krb_get_tf_realm(file, prealm)) != AUTH_SUCCESS) { return(-1); } /* Open ticket file */ if (k_errno = tf_init(file, R_TKT_FIL)) { return(-1); } /* Get principal name and instance */ if ((k_errno = tf_get_pname(pname)) || (k_errno = tf_get_pinst(pinst))) { tf_close(); return(-1); } /* * You may think that this is the obvious place to get the * realm of the ticket file, but it can't be done here as the * routine to do this must open the ticket file. This is why * it was done before tf_init. */ while ((k_errno = tf_get_cred(&creds)) == AUTH_SUCCESS) { char tkt_buf[256]; ckmakxmsg(tkt_buf,sizeof(tkt_buf), creds.service, (creds.instance[0] ? "." : ""), creds.instance, (creds.realm[0] ? "@" : ""), creds.realm, NULL,NULL,NULL,NULL,NULL,NULL,NULL); if ( !strcmp(tktname,tkt_buf) ) { /* we found the ticket we are looking for */ int n = (creds.issue_date + (((unsigned char) creds.lifetime) * 5 * 60)) - time(0); tf_close(); return(n <= 0 ? 0 : n); } } tf_close(); return(0); /* could not find the desired ticket */ #else /* KRB4 */ return(-1); #endif /* KRB4 */ } char * #ifdef CK_ANSIC ck_krb4_getrealm(void) #else ck_krb4_getrealm() #endif { #ifdef KRB4 char *file=NULL; int k_errno; static char realm[256]=""; realm[0]='\0'; if ( !ck_krb4_is_installed() ) return(realm); /* Try to get realm from ticket file */ /* If failure get the local realm */ /* * Since krb_get_tf_realm will return a ticket_file error, * we will call tf_init and tf_close first to filter out * things like no ticket file. */ /* Open ticket file */ file = tkt_string(); if (file == NULL || !file[0]) return(realm); if ((k_errno = tf_init(file, R_TKT_FIL)) == KSUCCESS) { /* Close ticket file */ (void) tf_close(); k_errno = krb_get_tf_realm(file, realm); } if (k_errno != KSUCCESS) { k_errno = krb_get_lrealm(realm, 1); } return(realm); #else /* KRB4 */ return(""); #endif /* KRB4 */ } char * #ifdef CK_ANSIC ck_krb4_getprincipal(void) #else ck_krb4_getprincipal() #endif { #ifdef KRB4 char *file=NULL; int k_errno; static char principal[256]=""; char instance[256]=""; char realm[256]=""; principal[0]='\0'; if ( !ck_krb4_is_installed() ) return(principal); /* Try to get realm from ticket file */ /* If failure get the local realm */ /* * Since krb_get_tf_realm will return a ticket_file error, * we will call tf_init and tf_close first to filter out * things like no ticket file. */ /* Open ticket file */ file = tkt_string(); if (file == NULL || !file[0]) return(principal); if ((k_errno = tf_init(file, R_TKT_FIL)) == KSUCCESS) { /* Close ticket file */ (void) tf_close(); k_errno = krb_get_tf_fullname(file, principal, instance, realm); } return(principal); #else /* KRB4 */ return(""); #endif /* KRB4 */ } static struct tkt_list_item * k5_tkt_list = NULL; int #ifdef CK_ANSIC ck_krb5_get_tkts(char * cc_name) #else ck_krb5_get_tkts(cc_name) char * cc_name; #endif { #ifdef KRB5 #ifndef HEIMDAL krb5_context kcontext; krb5_error_code retval; krb5_ccache cache = NULL; krb5_cc_cursor cur; krb5_creds creds; krb5_principal princ=NULL; krb5_flags flags=0; krb5_error_code code=0; int exit_status = 0; int tkt_count=0; struct tkt_list_item ** list = &k5_tkt_list; while ( k5_tkt_list ) { struct tkt_list_item * next; next = k5_tkt_list->next; free(k5_tkt_list->name); free(k5_tkt_list); k5_tkt_list = next; } if ( !ck_krb5_is_installed() ) return(-1); retval = krb5_init_context(&kcontext); if (retval) { debug(F101,"ck_krb5_get_tkts while initializing krb5","",retval); return(-1); } code = k5_get_ccache(kcontext,&cache,cc_name); if (code != 0) { debug(F111,"ck_krb5_get_tkts while getting ccache", error_message(code),code); tkt_count = -1; goto exit_k5_get_tkt; } flags = 0; /* turns off OPENCLOSE mode */ if ((code = krb5_cc_set_flags(kcontext, cache, flags))) { if (code == ENOENT) { debug(F111,"ck_krb5_get_tkts (ticket cache)", krb5_cc_get_name(kcontext, cache),code); } else { debug(F111, "ck_krb5_get_tkts while setting cache flags (ticket cache)", krb5_cc_get_name(kcontext, cache),code); } tkt_count = -1; goto exit_k5_get_tkt; } if ((code = krb5_cc_get_principal(kcontext, cache, &princ))) { debug(F101,"ck_krb5_get_tkts while retrieving principal name", "",code); tkt_count = -1; goto exit_k5_get_tkt; } if ((code = krb5_unparse_name(kcontext, princ, &defname))) { debug(F101,"ck_krb5_get_tkts while unparsing principal name", "",code); tkt_count = -1; goto exit_k5_get_tkt; } if ((code = krb5_cc_start_seq_get(kcontext, cache, &cur))) { debug(F101,"ck_krb5_get_tkts while starting to retrieve tickets", "",code); tkt_count = -1; goto exit_k5_get_tkt; } while (!(code = krb5_cc_next_cred(kcontext, cache, &cur, &creds))) { char *sname=NULL; retval = krb5_unparse_name(kcontext, creds.server, &sname); if (retval) { debug(F101, "ck_krb5_get_tkts while unparsing server name","",retval); tkt_count = -1; goto exit_k5_get_tkt; } *list = (struct tkt_list_item *) malloc(sizeof(struct tkt_list_item)); (*list)->name = sname; (*list)->next = NULL; list = &((*list)->next); krb5_free_unparsed_name(kcontext,sname); krb5_free_cred_contents(kcontext, &creds); tkt_count++; } if (code == KRB5_CC_END) { if ((code = krb5_cc_end_seq_get(kcontext, cache, &cur))) { debug(F101,"ck_krb5_get_tkts while finishing ticket retrieval", "",code); tkt_count = -1; goto exit_k5_get_tkt; } flags = KRB5_TC_OPENCLOSE; /* turns on OPENCLOSE mode */ if ((code = krb5_cc_set_flags(kcontext, cache, flags))) { debug(F101,"ck_krb5_get_tkts while closing ccache", "",code); tkt_count = -1; goto exit_k5_get_tkt; } } else { debug(F101,"ck_krb5_get_tkts while retrieving a ticket","",code); tkt_count = -1; goto exit_k5_get_tkt; } exit_k5_get_tkt: krb5_free_principal(kcontext,princ); krb5_free_unparsed_name(kcontext,defname); krb5_cc_close(kcontext,cache); krb5_free_context(kcontext); return(tkt_count); #else /* HEIMDAL */ return(-1); #endif /* HEIMDAL */ #else /* KRB5 */ return(0); #endif /* KRB5 */ } char * #ifdef CK_ANSIC ck_krb5_get_next_tkt(VOID) #else ck_krb5_get_next_tkt() #endif { #ifdef KRB5 #ifndef HEIMDAL static char * s=NULL; struct tkt_list_item * next=NULL; if ( s ) { free(s); s = NULL; } if ( k5_tkt_list == NULL ) return(NULL); next = k5_tkt_list->next; s = k5_tkt_list->name; free(k5_tkt_list); k5_tkt_list = next; return(s); #else /* HEIMDAL */ return("Not implemented"); #endif /* HEIMDAL */ #else /* KRB5 */ return(NULL); #endif /* KRB5 */ } char * #ifdef CK_ANSIC ck_krb5_tkt_flags(char * cc_name, char * tktname) #else ck_krb5_tkt_flags(cc_name,tktname) char * cc_name; char * tktname; #endif { #ifdef KRB5 #ifndef HEIMDAL krb5_context kcontext; krb5_error_code retval; krb5_ccache cache = NULL; krb5_cc_cursor cur; krb5_creds creds; krb5_principal princ=NULL; krb5_flags flags=0; krb5_error_code code=0; char * flag_str = ""; if ( !ck_krb5_is_installed() ) return(""); retval = krb5_init_context(&kcontext); if (retval) { debug(F101,"ck_krb5_tkt_flags while initializing krb5","",retval); return(""); } code = k5_get_ccache(kcontext,&cache,cc_name); if (code != 0) { debug(F111,"ck_krb5_tkt_isvalid while getting ccache", error_message(code),code); goto exit_k5_get_tkt; } flags = 0; /* turns off OPENCLOSE mode */ if ((code = krb5_cc_set_flags(kcontext, cache, flags))) { if (code == ENOENT) { debug(F111,"ck_krb5_tkt_flags (ticket cache)", krb5_cc_get_name(kcontext, cache),code); } else { debug(F111, "ck_krb5_tkt_flags while setting cache flags (ticket cache)", krb5_cc_get_name(kcontext, cache),code); } retval = -1; goto exit_k5_get_tkt; } if ((code = krb5_cc_get_principal(kcontext, cache, &princ))) { debug(F101,"ck_krb5_tkt_flags while retrieving principal name", "",code); retval = -1; goto exit_k5_get_tkt; } if ((code = krb5_unparse_name(kcontext, princ, &defname))) { debug(F101,"ck_krb5_tkt_flags while unparsing principal name", "",code); retval = -1; goto exit_k5_get_tkt; } if ((code = krb5_cc_start_seq_get(kcontext, cache, &cur))) { debug(F101,"ck_krb5_tkt_flags while starting to retrieve tickets", "",code); retval = -1; goto exit_k5_get_tkt; } if ((code = krb5_timeofday(kcontext, &now))) { if (!status_only) debug(F101,"ck_krb5_tkt_flags while getting time of day.", "",code); retval = -1; goto exit_k5_get_tkt; } while (!(code = krb5_cc_next_cred(kcontext, cache, &cur, &creds))) { char *sname=NULL; retval = krb5_unparse_name(kcontext, creds.server, &sname); if (retval) { debug(F101, "ck_krb5_tkt_flags while unparsing server name","",retval); retval = -1; krb5_free_cred_contents(kcontext, &creds); goto exit_k5_get_tkt; } if ( !strcmp(sname,tktname) ) { /* we found the ticket we are looking for */ flag_str = flags_string(&creds); krb5_free_unparsed_name(kcontext,sname); krb5_free_cred_contents(kcontext, &creds); code = KRB5_CC_END; break; } krb5_free_unparsed_name(kcontext,sname); krb5_free_cred_contents(kcontext, &creds); } if (code == KRB5_CC_END) { if ((code = krb5_cc_end_seq_get(kcontext, cache, &cur))) { debug(F101,"ck_krb5_tkt_flags while finishing ticket retrieval", "",code); goto exit_k5_get_tkt; } flags = KRB5_TC_OPENCLOSE; /* turns on OPENCLOSE mode */ if ((code = krb5_cc_set_flags(kcontext, cache, flags))) { debug(F101,"ck_krb5_tkt_flags while closing ccache", "",code); goto exit_k5_get_tkt; } } else { debug(F101,"ck_krb5_tkt_flags while retrieving a ticket","",code); goto exit_k5_get_tkt; } exit_k5_get_tkt: krb5_free_principal(kcontext,princ); krb5_free_unparsed_name(kcontext,defname); krb5_cc_close(kcontext,cache); krb5_free_context(kcontext); return(flag_str); #else /* HEIMDAL */ return("Not implemented"); #endif /* HEIMDAL */ #else /* KRB5 */ return(""); #endif /* KRB5 */ } int #ifdef CK_ANSIC ck_krb5_tkt_isvalid(char * cc_name, char * tktname) #else ck_krb5_tkt_isvalid(cc_name,tktname) char * cc_name; char * tktname; #endif { #ifdef KRB5 #ifndef HEIMDAL krb5_context kcontext=NULL; krb5_error_code retval; krb5_ccache cache = NULL; krb5_cc_cursor cur; krb5_creds creds; krb5_principal princ=NULL; krb5_flags flags=0; krb5_error_code code=0; #ifdef CHECKADDRS krb5_address ** myAddrs=NULL; krb5_address ** p=NULL; BOOL Addrfound = FALSE; #endif /*CHECKADDRS*/ if ( !ck_krb5_is_installed() ) return(-1); retval = krb5_init_context(&kcontext); if (retval) { debug(F101,"ck_krb5_tkt_isvalid while initializing krb5","",retval); return(-1); } code = k5_get_ccache(kcontext,&cache,cc_name); if (code != 0) { debug(F111,"ck_krb5_tkt_isvalid while getting ccache", error_message(code),code); goto exit_k5_get_tkt; } flags = 0; /* turns off OPENCLOSE mode */ if ((code = krb5_cc_set_flags(kcontext, cache, flags))) { if (code == ENOENT) { debug(F111,"ck_krb5_tkt_isvalid (ticket cache)", krb5_cc_get_name(kcontext, cache),code); } else { debug(F111, "ck_krb5_tkt_isvalid while setting cache flags (ticket cache)", krb5_cc_get_name(kcontext, cache),code); } retval = -1; goto exit_k5_get_tkt; } if ((code = krb5_cc_get_principal(kcontext, cache, &princ))) { debug(F101,"ck_krb5_tkt_isvalid while retrieving principal name", "",code); retval = -1; goto exit_k5_get_tkt; } if ((code = krb5_unparse_name(kcontext, princ, &defname))) { debug(F101,"ck_krb5_tkt_isvalid while unparsing principal name", "",code); retval = -1; goto exit_k5_get_tkt; } if ((code = krb5_cc_start_seq_get(kcontext, cache, &cur))) { debug(F101,"ck_krb5_tkt_isvalid while starting to retrieve tickets", "",code); retval = -1; goto exit_k5_get_tkt; } if ((code = krb5_timeofday(kcontext, &now))) { if (!status_only) debug(F101,"ck_krb5_tkt_isvalid while getting time of day.", "",code); retval = -1; goto exit_k5_get_tkt; } while (!(code = krb5_cc_next_cred(kcontext, cache, &cur, &creds))) { char *sname=NULL; retval = krb5_unparse_name(kcontext, creds.server, &sname); if (retval) { debug(F101, "ck_krb5_tkt_isvalid while unparsing server name","",retval); retval = -1; krb5_free_cred_contents(kcontext, &creds); goto exit_k5_get_tkt; } if ( !strcmp(sname,tktname) ) { /* we found the ticket we are looking for */ /* We add a 5 minutes fudge factor to compensate for potential */ /* clock skew errors between the KDC and K95's host OS */ retval = ((creds.times.starttime > 0) && now >= (creds.times.starttime - 300) && now < (creds.times.endtime + 300) && !(creds.ticket_flags & TKT_FLG_INVALID)); #ifdef CHECKADDRS if ( retval && krb5_checkaddrs && creds.addresses && creds.addresses[0] ) { /* if we think it is valid, then lets check the IP Addresses */ /* to make sure it is valid for our current connection. */ /* Also make sure it's for the correct IP address */ retval = krb5_os_localaddr(kcontext, &myAddrs); if (retval) { com_err(NULL, retval, "retrieving my IP address"); krb5_free_unparsed_name(kcontext,sname); krb5_free_cred_contents(kcontext, &creds); code = KRB5_CC_END; retval = -1; break; } /* See if any of our addresses match any in cached credentials */ for (Addrfound=FALSE, p=myAddrs; (Addrfound==FALSE) && (*p); p++ ) { if (krb5_address_search(kcontext, *p, creds.addresses)) { Addrfound = TRUE; } } krb5_free_addresses(k5_context, myAddrs); if (Addrfound) { krb5_free_unparsed_name(kcontext,sname); krb5_free_cred_contents(kcontext, &creds); code = KRB5_CC_END; retval = 1; break; } else { krb5_free_unparsed_name(kcontext,sname); krb5_free_cred_contents(kcontext, &creds); code = KRB5_CC_END; retval = 0; break; } } #endif /* CHECKADDRS */ krb5_free_unparsed_name(kcontext,sname); krb5_free_cred_contents(kcontext, &creds); code = KRB5_CC_END; break; } krb5_free_unparsed_name(kcontext,sname); krb5_free_cred_contents(kcontext, &creds); } if (code == KRB5_CC_END) { if ((code = krb5_cc_end_seq_get(kcontext, cache, &cur))) { debug(F101,"ck_krb5_tkt_isvalid while finishing ticket retrieval", "",code); retval = -1; goto exit_k5_get_tkt; } flags = KRB5_TC_OPENCLOSE; /* turns on OPENCLOSE mode */ if ((code = krb5_cc_set_flags(kcontext, cache, flags))) { debug(F101,"ck_krb5_tkt_isvalid while closing ccache", "",code); retval = -1; goto exit_k5_get_tkt; } } else { debug(F101,"ck_krb5_tkt_isvalid while retrieving a ticket","",code); retval = -1; goto exit_k5_get_tkt; } exit_k5_get_tkt: krb5_free_principal(kcontext,princ); krb5_free_unparsed_name(kcontext,defname); krb5_cc_close(kcontext,cache); krb5_free_context(kcontext); return(retval); #else /* HEIMDAL */ return(-1); #endif /* HEIMDAL */ #else /* KRB5 */ return(-1); #endif /* KRB5 */ } int #ifdef CK_ANSIC ck_krb5_is_tgt_valid(VOID) #else ck_krb5_is_tgt_valid() #endif { #ifdef KRB5 #ifndef HEIMDAL char tgt[256]; char * s; int rc = 0; s = ck_krb5_getrealm(krb5_d_cc); ckmakmsg(tgt,sizeof(tgt),"krbtgt/",s,"@",s); rc = ck_krb5_tkt_isvalid(krb5_d_cc,tgt); debug(F111,"ck_krb5_is_tgt_valid",tgt,rc); return(rc>0); #else /* HEIMDAL */ return(-1); #endif /* HEIMDAL */ #else /* KRB5 */ return(0); #endif /* KRB5 */ } int #ifdef CK_ANSIC ck_krb5_tkt_time(char * cc_name, char * tktname) #else ck_krb5_tkt_time(cc_name, tktname) char * cc_name; char * tktname; #endif { #ifdef KRB5 #ifndef HEIMDAL krb5_context kcontext; krb5_error_code retval; krb5_ccache cache = NULL; krb5_cc_cursor cur; krb5_creds creds; krb5_principal princ=NULL; krb5_flags flags=0; krb5_error_code code=0; if ( !ck_krb5_is_installed() ) return(-1); retval = krb5_init_context(&kcontext); if (retval) { debug(F101,"ck_krb5_list_creds while initializing krb5","",retval); return(-1); } code = k5_get_ccache(kcontext,&cache,cc_name); if (code != 0) { debug(F111,"ck_krb5_tkt_time while getting ccache", error_message(code),code); retval = -1; goto exit_k5_get_tkt; } flags = 0; /* turns off OPENCLOSE mode */ if ((code = krb5_cc_set_flags(kcontext, cache, flags))) { if (code == ENOENT) { debug(F111,"ck_krb5_list_creds (ticket cache)", krb5_cc_get_name(kcontext, cache),code); } else { debug(F111, "ck_krb5_list_creds while setting cache flags (ticket cache)", krb5_cc_get_name(kcontext, cache),code); } retval = -1; goto exit_k5_get_tkt; } if ((code = krb5_cc_get_principal(kcontext, cache, &princ))) { debug(F101,"ck_krb5_list_creds while retrieving principal name", "",code); retval = -1; goto exit_k5_get_tkt; } if ((code = krb5_unparse_name(kcontext, princ, &defname))) { debug(F101,"ck_krb5_list_creds while unparsing principal name", "",code); retval = -1; goto exit_k5_get_tkt; } if ((code = krb5_cc_start_seq_get(kcontext, cache, &cur))) { debug(F101,"ck_krb5_list_creds while starting to retrieve tickets", "",code); retval = -1; goto exit_k5_get_tkt; } if ((code = krb5_timeofday(kcontext, &now))) { if (!status_only) debug(F101,"ck_krb5_list_creds while getting time of day.", "",code); krb5_free_context(kcontext); return(-1); } while (!(code = krb5_cc_next_cred(kcontext, cache, &cur, &creds))) { char *sname=NULL; retval = krb5_unparse_name(kcontext, creds.server, &sname); if (retval) { debug(F101, "ck_krb5_list_creds while unparsing server name","",retval); retval = -1; krb5_free_unparsed_name(kcontext,sname); krb5_free_cred_contents(kcontext, &creds); goto exit_k5_get_tkt; } if ( !strcmp(sname,tktname) ) { /* we found the ticket we are looking for */ int valid = (creds.times.starttime && now > creds.times.starttime && now < creds.times.endtime && !(creds.ticket_flags & TKT_FLG_INVALID)); if ( valid ) { retval = creds.times.endtime - now; } else retval = 0; krb5_free_unparsed_name(kcontext,sname); krb5_free_cred_contents(kcontext, &creds); code = KRB5_CC_END; break; } krb5_free_unparsed_name(kcontext,sname); krb5_free_cred_contents(kcontext, &creds); } if (code == KRB5_CC_END) { if ((code = krb5_cc_end_seq_get(kcontext, cache, &cur))) { debug(F101,"ck_krb5_list_creds while finishing ticket retrieval", "",code); retval = -1; goto exit_k5_get_tkt; } flags = KRB5_TC_OPENCLOSE; /* turns on OPENCLOSE mode */ if ((code = krb5_cc_set_flags(kcontext, cache, flags))) { debug(F101,"ck_krb5_list_creds while closing ccache", "",code); retval = -1; goto exit_k5_get_tkt; } } else { debug(F101,"ck_krb5_list_creds while retrieving a ticket","",code); retval = -1; goto exit_k5_get_tkt; } exit_k5_get_tkt: krb5_free_principal(kcontext,princ); krb5_free_unparsed_name(kcontext,defname); krb5_cc_close(kcontext,cache); krb5_free_context(kcontext); return(retval); #else /* HEIMDAL */ return(-1); #endif /* HEIMDAL */ #else /* KRB5 */ return(-1); #endif /* KRB5 */ } char * #ifdef CK_ANSIC ck_krb5_get_cc_name(void) #else ck_krb5_get_cc_name() #endif { #ifdef KRB5 #ifndef HEIMDAL static char cc_name[CKMAXPATH+1]=""; krb5_context kcontext = NULL; krb5_ccache ccache = NULL; krb5_error_code code; char * p=NULL; cc_name[0] = '\0'; if ( !ck_krb5_is_installed() ) return(cc_name); p = getenv("KRB5CCNAME"); if ( !p ) { code = krb5_init_context(&kcontext); if (code) { com_err("ck_krb5_get_cc_name",code,"while init_context"); return(cc_name); } if ((code = krb5_cc_default(kcontext, &ccache))) { com_err("ck_krb5_get_cc_name",code,"while getting default ccache"); goto exit_k5_get_cc; } ckmakmsg(cc_name,sizeof(cc_name), (char *)krb5_cc_get_type(kcontext,ccache),":", (char *)krb5_cc_get_name(kcontext,ccache),NULL); } else { ckstrncpy(cc_name,p,CKMAXPATH); } if ( !strncmp("FILE:",cc_name,5) ) { for ( p=cc_name; *p ; p++ ) if ( *p == '\\' ) *p = '/'; } exit_k5_get_cc: if ( ccache ) krb5_cc_close(kcontext,ccache); if ( kcontext ) krb5_free_context(kcontext); return(cc_name); #else /* HEIMDAL */ return("Not implemented"); #endif /* HEIMDAL */ #else /* KRB5 */ return(""); #endif /* KRB5 */ } char * #ifdef CK_ANSIC ck_krb5_getrealm(char * cc_name) #else ck_krb5_getrealm(cc_name) char * cc_name; #endif { #ifdef KRB5 #ifndef HEIMDAL static char realm[256]=""; krb5_context kcontext; krb5_ccache ccache = NULL; krb5_error_code code; krb5_principal me=NULL; realm[0] = '\0'; if ( !ck_krb5_is_installed() ) return(realm); code = krb5_init_context(&kcontext); if (code) { return(realm); } code = k5_get_ccache(kcontext,&ccache,cc_name); if (code != 0) { goto exit_k5_getrealm; } code = krb5_cc_get_principal(kcontext, ccache, &me); if (code) code = krb5_parse_name(kcontext, "foo", &me); if (code) { goto exit_k5_getrealm; } if ( krb5_princ_realm(kcontext, me)->length < sizeof(realm) ) { memcpy(realm,krb5_princ_realm(kcontext, me)->data, krb5_princ_realm(kcontext, me)->length); /* safe */ realm[krb5_princ_realm(kcontext, me)->length]='\0'; } exit_k5_getrealm: if ( me ) krb5_free_principal(kcontext,me); if ( ccache ) krb5_cc_close(kcontext,ccache); if (kcontext) krb5_free_context(kcontext); return(realm); #else /* HEIMDAL */ return("Not implemented"); #endif /* HEIMDAL */ #else /* KRB5 */ return(""); #endif /* KRB5 */ } char * #ifdef CK_ANSIC ck_krb5_getprincipal(char * cc_name) #else ck_krb5_getprincipal(cc_name) char * cc_name; #endif { #ifdef KRB5 #ifndef HEIMDAL static char principal[UIDBUFLEN+1]=""; krb5_context kcontext; krb5_ccache ccache = NULL; krb5_error_code code; krb5_principal me; char * p=NULL; int i; principal[0] = '\0'; if ( !ck_krb5_is_installed() ) return(principal); code = krb5_init_context(&kcontext); if (code) { return(principal); } code = k5_get_ccache(kcontext,&ccache,cc_name); if (code != 0) { goto exit_k5_getprincipal; } if ((code = krb5_cc_get_principal(kcontext, ccache, &me))) { goto exit_k5_getprincipal; } if ((code = krb5_unparse_name (kcontext, me, &p))) { krb5_free_principal(kcontext,me); goto exit_k5_getprincipal; } ckstrncpy(principal,p,UIDBUFLEN); i = ckindex("@",principal,0,0,0); if (i) principal[i-1] = '\0'; krb5_free_unparsed_name(kcontext,p); exit_k5_getprincipal: if ( ccache ) krb5_cc_close(kcontext,ccache); if (kcontext) krb5_free_context(kcontext); return(principal); #else /* HEIMDAL */ return("Not implemented"); #endif /* HEIMDAL */ #else /* KRB5 */ return(""); #endif /* KRB5 */ } #ifndef CRYPT_DLL int ck_get_crypt_table(struct keytab ** pTable, int * pN) { #ifdef CK_ENCRYPTION return(get_crypt_table(pTable, pN)); #else /* ENCRYPTION */ int i=0; #ifndef OS2 char * tmpstring = NULL; #endif /* OS2 */ if ( *pTable ) { for ( i=0 ; i < *pN ; i++ ) free( (*pTable)[i].kwd ) ; free ( *pTable ) ; } *pTable = NULL; *pN = 0; *pTable = malloc( sizeof(struct keytab) * 2 ) ; if ( !(*pTable) ) return(0); #ifdef OS2 (*pTable)[0].kwd =strdup("automatic"); #else /* OS2 */ makestr(&tmpstring,"automatic"); (*pTable)[0].kwd = tmpstring; tmpstring = NULL; #endif /* OS2 */ (*pTable)[0].kwval = ENCTYPE_ANY; (*pTable)[0].flgs = 0; #ifdef OS2 (*pTable)[1].kwd =strdup("none"); #else /* OS2 */ makestr(&tmpstring,"none"); (*pTable)[1].kwd = tmpstring; tmpstring = NULL; #endif /* OS2 */ (*pTable)[1].kwval = 999; (*pTable)[1].flgs = 0; (*pN) = 2; return(2); #endif /* ENCRYPTION */ } VOID ck_encrypt_send_support() { #ifdef CK_ENCRYPTION encrypt_send_support(); #endif /* ENCRYPTION */ } #endif /* CRYPT_DLL */ /* * * Kstream * * Emulates the kstream package in Kerberos 4 * */ int kstream_destroy() { if (g_kstream != NULL) { auth_destroy(); /* Destroy authorizing */ free(g_kstream); g_kstream=NULL; } return 0; } VOID #ifdef CK_ANSIC kstream_set_buffer_mode(int mode) #else kstream_set_buffer_mode(mode) int mode; #endif { } int #ifdef CK_ANSIC kstream_create_from_fd(int fd, kstream_ptr data) #else kstream_create_from_fd(fd,data) int fd; kstream_ptr data; #endif { int n; g_kstream = malloc(sizeof(struct kstream_int)); if (g_kstream == NULL) return 0; g_kstream->fd = fd; n = auth_init(g_kstream); /* Initialize authorizing */ if (n) { free(g_kstream); g_kstream = NULL; return 0; } g_kstream->encrypt = NULL; g_kstream->decrypt = NULL; g_kstream->encrypt_type = ENCTYPE_ANY; g_kstream->decrypt_type = ENCTYPE_ANY; return 1; } #ifdef CK_KERBEROS #ifdef RLOGCODE static int do_lencheck, use_ivecs; extern int rlog_inband; #ifdef KRB5 void rcmd_stream_init_krb5(in_keyblock, encrypt_flag, lencheck, am_client, protonum) krb5_keyblock *in_keyblock; int encrypt_flag; int lencheck; int am_client; enum krb5_kcmd_proto protonum; { krb5_error_code status; size_t blocksize; if (!encrypt_flag) return; desinbuf.data = des_inbuf; desoutbuf.data = des_outpkt+4; /* Set up des buffers */ k5_session_key = in_keyblock; do_lencheck = lencheck; if ( protonum == KCMD_OLD_PROTOCOL ) { use_ivecs = 0; return; } use_ivecs = 1; if (status = krb5_c_block_size(k5_context, k5_session_key->enctype, &blocksize)) { /* XXX what do I do? */ printf("fatal kerberos 5 crypto library error\n"); ttclos(0); return; } encivec_i[0].length = encivec_i[1].length = encivec_o[0].length = encivec_o[1].length = blocksize; if ((encivec_i[0].data = malloc(encivec_i[0].length * 4)) == NULL) { /* XXX what do I do? */ printf("fatal malloc failed\n"); ttclos(0); return; } encivec_i[1].data = encivec_i[0].data + encivec_i[0].length; encivec_o[0].data = encivec_i[1].data + encivec_i[1].length; encivec_o[1].data = encivec_o[0].data + encivec_o[0].length; /* is there a better way to initialize this? */ memset(encivec_i[0].data, am_client, blocksize); memset(encivec_o[0].data, 1 - am_client, blocksize); memset(encivec_i[1].data, 2 | am_client, blocksize); memset(encivec_o[1].data, 2 | (1 - am_client), blocksize); } #endif /* KRB5 */ int #ifdef CK_ANSIC ck_krb_rlogin(CHAR * hostname, int port, CHAR * localuser, CHAR * remoteuser, CHAR * term_speed, struct sockaddr_in * l_addr, struct sockaddr_in * r_addr, int kversion, int encrypt_flag) #else /* CK_ANSIC */ ck_krb_rlogin(hostname, port, localuser, remoteuser, term_speed, l_addr, r_addr, encrypt_flag) CHAR * hostname; int port; CHAR * localuser; CHAR * remoteuser; CHAR * term_speed; struct sockaddr_in * l_addr; struct sockaddr_in * r_addr; int kversion; int encrypt_flag; #endif /* CK_ANSIC */ { unsigned long status; char * realm=NULL; extern int ttyfd; int c; long msglen; debug(F111,"ck_krb_rlogin",hostname,port); if ( kversion == 4 && !ck_krb4_is_installed() ) { printf("?Kerberos 4 is not installed\r\n"); return(-1); } else if ( kversion == 5 && !ck_krb5_is_installed() ) { printf("?Kerberos 5 is not installed\r\n"); return(-1); } if ( encrypt_flag && !ck_crypt_is_installed() ) { printf("?Encryption is not installed\r\n"); return(-1); } if ( kversion == 5 ) { #ifdef KRB5 krb5_flags authopts=0; krb5_ccache ccache=NULL; char *cksumbuf=NULL; char *service=NULL; char * kcmd_version=NULL; enum krb5_kcmd_proto use_proto; krb5_data cksumdat; krb5_creds *get_cred = 0; krb5_error_code status; krb5_error *error = 0; krb5_ap_rep_enc_part *rep_ret = NULL; krb5_data outbuf; int rc; krb5_int32 seqno=0; krb5_int32 server_seqno=0; char ** realmlist=NULL; int buflen; char tgt[256]; debug(F100,"ck_krb_rlogin version 5","",0); realm = ck_krb5_realmofhost((char *)hostname); if (!realm) { ckstrncpy(strTmp, "Can't find realm for host \"",AUTHTMPBL); ckstrncat(strTmp, (char *)hostname,AUTHTMPBL); ckstrncat(strTmp, "\"",AUTHTMPBL); printf("?Kerberos 5 error: %s\r\n",strTmp); krb5_errno = KRB5_ERR_HOST_REALM_UNKNOWN; makestr(&krb5_errmsg,strTmp); return(0); } ckmakmsg(tgt,sizeof(tgt),"krbtgt/",realm,"@",realm); debug(F110,"ck_rlog_rlogin TGT",tgt,0); if ( krb5_autoget && !((ck_krb5_tkt_isvalid(NULL,tgt) > 0) || (ck_krb5_is_tgt_valid() > 0)) ) ck_krb5_autoget_TGT(realm); buflen = strlen((char *)term_speed) + strlen((char *)remoteuser) + 64; if ((cksumbuf = malloc(buflen)) == 0) { printf("Unable to allocate memory for checksum buffer.\r\n"); return(-1); } ckmakmsg(cksumbuf,buflen,ckuitoa((unsigned short) ntohs(port)),":", (char *)term_speed,(char *)remoteuser); cksumdat.data = cksumbuf; cksumdat.length = strlen(cksumbuf); status = krb5_init_context(&k5_context); if (status) { debug(F110,"ck_krb_rlogin()","unable to init_context",0); return(-1); } desinbuf.data = des_inbuf; desoutbuf.data = des_outpkt+4; /* Set up des buffers */ rc = k5_get_ccache(k5_context,&ccache,NULL); if (rc != 0) { com_err(NULL, rc, "while getting ccache."); return(0); } service = krb5_d_srv ? krb5_d_srv : KRB5_SERVICE_NAME; if (!(get_cred = (krb5_creds *)calloc(1, sizeof(krb5_creds)))) { printf("ck_krb_rlogin: no memory\r\n"); return(-1); } memset(get_cred,0,sizeof(krb5_creds)); status = krb5_sname_to_principal(k5_context, (char *) hostname, service, KRB5_NT_SRV_HST, &get_cred->server); if (status) { printf("ck_krb_rlogin: krb5_sname_to_principal failed: %s\r\n", error_message(status)); return(-1); } ttoc(0); if (status = krb5_cc_get_principal(k5_context, ccache, &get_cred->client) ) { (void) krb5_cc_close(k5_context, ccache); krb5_free_creds(k5_context, get_cred); goto bad; } if (krb5_rlog_ver == KCMD_OLD_PROTOCOL) get_cred->keyblock.enctype=ENCTYPE_DES_CBC_CRC; /* Get ticket from credentials cache or kdc */ status = krb5_get_credentials(k5_context, 0, ccache, get_cred, &ret_cred ); krb5_free_creds(k5_context, get_cred); get_cred = NULL; (void) krb5_cc_close(k5_context, ccache); if (status) goto bad; /* Reset internal flags; these should not be set. */ authopts &= (~OPTS_FORWARD_CREDS); authopts &= (~OPTS_FORWARDABLE_CREDS); if (krb5_auth_con_init(k5_context, &auth_context)) goto bad; if (krb5_auth_con_setflags(k5_context, auth_context, KRB5_AUTH_CONTEXT_RET_TIME)) goto bad; /* Only need local address for mk_cred() to send to krlogind */ if (!krb5_d_no_addresses) if (status = krb5_auth_con_genaddrs(k5_context, auth_context, ttyfd, KRB5_AUTH_CONTEXT_GENERATE_LOCAL_FULL_ADDR )) goto bad; /* Here is where we start to handle the new protocol in earnest */ if ( krb5_rlog_ver == KCMD_PROTOCOL_COMPAT_HACK ) { krb5_boolean is_des; if (status = krb5_c_enctype_compare( k5_context, ENCTYPE_DES_CBC_CRC, #ifdef HEIMDAL ret_cred->session.keytype, #else /* HEIMDAL */ ret_cred->keyblock.enctype, #endif /* HEIMDAL */ &is_des)) { krb5_free_creds(k5_context, ret_cred); ret_cred = NULL; goto bad; } if ( is_des ) { kcmd_version = "KCMDV0.1"; use_proto = KCMD_OLD_PROTOCOL; } else { authopts = AP_OPTS_USE_SUBKEY; kcmd_version = "KCMDV0.2"; use_proto = KCMD_NEW_PROTOCOL; } } else { use_proto = krb5_rlog_ver; switch ( krb5_rlog_ver ) { case KCMD_NEW_PROTOCOL: authopts = AP_OPTS_USE_SUBKEY; kcmd_version = "KCMDV0.2"; break; case KCMD_OLD_PROTOCOL: kcmd_version = "KCMDV0.1"; break; default: goto bad; } } /* call Kerberos library routine to obtain an authenticator, pass it over the socket to the server, and obtain mutual authentication. */ status = krb5_sendauth(k5_context, &auth_context, (krb5_pointer) &ttyfd, kcmd_version, ret_cred->client, ret_cred->server, authopts, &cksumdat, ret_cred, 0, &error, &rep_ret, NULL ); krb5_free_data_contents(k5_context,&cksumdat); if (status) { if ( !quiet ) printf("Couldn't authenticate to server: %s\r\n", error_message(status)); if (error) { if ( !quiet ) { printf("Server returned error code %d (%s)\r\n", error->error, error_message(ERROR_TABLE_BASE_krb5 + error->error)); if (error->text.length) { printf("Error text sent from server: %s\r\n", error->text.data); } } krb5_free_error(k5_context, error); error = 0; } goto bad; } if (rep_ret) { server_seqno = rep_ret->seq_number; krb5_free_ap_rep_enc_part(k5_context, rep_ret); } (void) ttol(remoteuser, strlen((char *)remoteuser)+1); (void) ttol(term_speed, strlen((char *)term_speed)+1); (void) ttol(localuser, strlen((char *)localuser)+1); if (forward_flag) { /* Forward credentials (global) */ if (status = krb5_fwd_tgt_creds( k5_context, auth_context, (char *)hostname, ret_cred->client, ret_cred->server, 0, (forwardable_flag ? OPTS_FORWARDABLE_CREDS : 0), &outbuf ) ) { printf("Error forwarding credentials: %s\r\n", error_message(status)); goto bad2; } /* Send forwarded credentials */ status = krb5_write_message(k5_context, (krb5_pointer)&ttyfd, &outbuf ); } else { /* Dummy write to signal no forwarding */ bad2: outbuf.length = 0; status = krb5_write_message(k5_context, (krb5_pointer)&ttyfd, &outbuf); } if ((c = ttinc(0)) < 0) { if (c==-1) { perror((char *)hostname); } else { printf("ck_krb_rlogin: bad connection with remote host\r\n"); } status = -1; goto bad; } if (c != 0) { while ((c = ttinc(1)) >= 0) { (void) printf("%c",c); if (c == '\n') break; } status = -1; goto bad; } if ( status == 0 ) { /* success */ krb5_keyblock * key = 0; if ( use_proto == KCMD_NEW_PROTOCOL ) { int on = 1; rlog_inband = 1; setsockopt(ttyfd, SOL_SOCKET, SO_OOBINLINE, (char *) &on, sizeof on); status = krb5_auth_con_getlocalsubkey( k5_context, auth_context, &key); if ((status || !key) && encrypt_flag ) goto bad2; } if ( key == 0 ) { #ifdef HEIMDAL key = &ret_cred->session; #else /* HEIMDAL */ key = &ret_cred->keyblock; #endif /* HEIMDAL */ } rcmd_stream_init_krb5(key, encrypt_flag, 1, 1, use_proto); if ( encrypt_flag ) rlog_encrypt = 1; } return (0); /* success */ bad: if ( status && !quiet ) { printf("Kerberos authentication error: %s\r\n", error_message(status)); } if (ret_cred) { krb5_free_creds(k5_context, ret_cred); ret_cred = NULL; } return (status); #else /* KRB5 */ return(-1); #endif /* KRB5 */ } else if (kversion == 4) { #ifdef KRB4 char tgt[4*REALM_SZ+1]; debug(F100,"ck_krb_rlogin version 4","",0); realm = (char *)krb_realmofhost(hostname); if (!realm) { strcpy(strTmp, "Can't find realm for host \""); ckstrncat(strTmp, hostname,AUTHTMPBL); ckstrncat(strTmp, "\"",AUTHTMPBL); printf("?Kerberos 4 error: %s\r\n",strTmp); krb4_errno = 0; makestr(&krb4_errmsg,strTmp); return(0); } ckmakmsg(tgt,sizeof(tgt),"krbtgt.",realm,"@",realm); status = ck_krb4_tkt_isvalid(tgt); if ( status <= 0 && krb4_autoget ) ck_krb4_autoget_TGT(realm); ttoc(0); /* write a NUL */ status = krb_sendauth(encrypt_flag?KOPT_DO_MUTUAL:0, ttyfd, &k4_auth, krb4_d_srv ? krb4_d_srv : KRB4_SERVICE_NAME, hostname, realm, (unsigned long) getpid(), &k4_msg_data, &cred, #ifdef CK_ENCRYPTION &k4_sched, #else /* ENCRYPTION */ NULL, #endif /* ENCRYPTION */ l_addr, r_addr, "KCMDV0.1"); debug(F111,"ck_krb_rlogin","krb_sendauth",status); if (status != KSUCCESS) { printf( "krb_sendauth failed: %s\r\n", krb_get_err_text_entry(status) ); return(-1); } ttol(remoteuser,strlen(remoteuser)+1); ttol(term_speed,strlen(term_speed)+1); reread: if ((c = ttinc(0)) < 0) { printf("rcmd: bad connection with remote host\r\n"); return(-1); } debug(F111,"ck_krb_rlogin","first byte",c); if (c != 0) { char *check = "ld.so: warning:"; /* If rlogind was compiled on SunOS4, and it somehow got the shared library version numbers wrong, it may give an ld.so warning about an old version of a shared library. Just ignore any such warning. Note that the warning is a characteristic of the server; we may not ourselves be running under SunOS4. */ if (c == 'l') { char *p; char cc; p = &check[1]; while ((c = ttinc(0)) >= 0) { if (*p == '\0') { if (c == '\n') break; } else { if (c != *p) break; ++p; } } if (*p == '\0') goto reread; } printf(check); while ((c = ttinc(1)) >= 0) { printf("%c",c); if (c == '\n') break; } debug(F110,"ck_krb_rlogin","fatal error 1",0); return(-1); } #ifdef CK_ENCRYPTION if ( encrypt_flag ) { /* if we are encrypting we need to setup the encryption */ /* routines. */ des_key_sched(cred.session, k4_sched); rlog_encrypt = 1; } #endif /* ENCRYPTION */ #else /* KRB4 */ return(-1); #endif /* KRB4 */ } return(0); /* success */ } #define SRAND srand #define RAND rand #define RAND_TYPE int static long random_confounder(size, fillin) size_t size; char * fillin; { static int seeded = 0; register unsigned char *real_fill; RAND_TYPE rval; if (!seeded) { /* time() defined in 4.12.2.4, but returns a time_t, which is an "arithmetic type" (4.12.1) */ rval = (RAND_TYPE) time(0); SRAND(rval); rval = RAND(); rval ^= getpid(); SRAND(rval); seeded = 1; } real_fill = (unsigned char *)fillin; while (size > 0) { rval = RAND(); *real_fill = rval & 0xff; real_fill++; size--; if (size) { *real_fill = (rval >> 8) & 0xff; real_fill++; size--; } } return 0; } #ifdef KRB5 int krb5_des_avail(fd) int fd; { return(nstored); } int krb5_des_read(fd, buf, len, secondary) int fd; register char *buf; int len; int secondary; { int nreturned = 0; long net_len,rd_len; int cc; krb5_error_code status; unsigned char c; krb5_data plain; krb5_enc_data cipher; debug(F111,"krb5_des_read","len",len); debug(F111,"krb5_des_read","rlog_encrypt",rlog_encrypt); if ( !rlog_encrypt ) { cc = net_read(fd, buf, len); debug(F111,"krb5_des_read","chars read",cc); if ( cc < 0 ) netclos(); return(cc); } if (nstored >= len) { if ( buf ) { memcpy(buf, store_ptr, len); /* safe */ store_ptr += len; nstored -= len; return(len); } else return(0); } else if (nstored) { if ( buf ) { memcpy(buf, store_ptr, nstored); /* safe */ nreturned += nstored; buf += nstored; len -= nstored; nstored = 0; } else return(0); } /* See the comment in v4_des_read. */ while (1) { cc = net_read(fd, &c, 1); /* we should check for non-blocking here, but we'd have to make it save partial reads as well. */ if (cc <= 0) { return cc; /* read error */ } if (cc == 1) { if (c == 0 || !do_lencheck) break; } } rd_len = c; if ((cc = net_read(fd, &c, 1)) != 1) return 0; rd_len = (rd_len << 8) | c; if ((cc = net_read(fd, &c, 1)) != 1) return 0; rd_len = (rd_len << 8) | c; if ((cc = net_read(fd, &c, 1)) != 1) return 0; rd_len = (rd_len << 8) | c; if (status = krb5_c_encrypt_length(k5_context, k5_session_key->enctype, use_ivecs ? rd_len + 4 : rd_len, (size_t *)&net_len)) { errno = status; return(-1); } if ((net_len <= 0) || (net_len > sizeof(des_inbuf))) { /* preposterous length; assume out-of-sync; only recourse is to close connection, so return 0 */ printf("Read size problem.\r\n"); return(0); } if ((cc = net_read(fd, desinbuf.data, net_len)) != net_len ) { /* pipe must have closed, return 0 */ printf( "Read error: length received %d != expected %d.\r\n", cc, net_len ); return(cc); } /* decrypt info */ cipher.enctype = ENCTYPE_UNKNOWN; cipher.ciphertext.length = net_len; cipher.ciphertext.data = desinbuf.data; plain.length = sizeof(storage); plain.data = storage; if ( status = krb5_c_decrypt(k5_context, k5_session_key, KCMD_KEYUSAGE, use_ivecs ? encivec_i + secondary : 0, &cipher,&plain) ) { /* probably out of sync */ printf("Cannot decrypt data from network: %s\r\n", error_message(status)); errno = EIO; return(-1); } store_ptr = storage; nstored = rd_len; if ( use_ivecs ) { int rd_len2; rd_len2 = storage[0] & 0xff; rd_len2 <<= 8; rd_len2 |= storage[1] & 0xff; rd_len2 <<= 8; rd_len2 |= storage[2] & 0xff; rd_len2 <<= 8; rd_len2 |= storage[3] & 0xff; if (rd_len2 != rd_len) { /* cleartext length trashed? */ errno = EIO; return -1; } store_ptr += 4; } if ( !buf ) return(0); #ifdef RLOGCODE /* blah */ if (rlog_inband && (ttnproto == NP_K5LOGIN || ttnproto == NP_EK5LOGIN)) { int i, left, n; for (i = 0; i < nstored; i++) { if (store_ptr[i] == '\377' && store_ptr[i+1] == '\377') { left = nstored - i; n = rlog_ctrl(&store_ptr[i], left); if (n < 0) { left -= (-n); nstored = left; /* flush before, and (-n) bytes */ if (left > 0) memmove(store_ptr, &store_ptr[i-n], left); } else if (n) { left -= n; nstored -= n; if (left > 0) memmove(store_ptr, &store_ptr[n], left); } } } } #endif /* RLOGCODE */ if (nstored > len) { memcpy(buf, store_ptr, len); /* safe */ nreturned += len; store_ptr += len; nstored -= len; } else { memcpy(buf, store_ptr, nstored); /* safe */ nreturned += nstored; nstored = 0; } return(nreturned); } int krb5_des_write(fd, buf, len, secondary) int fd; char *buf; int len; int secondary; { char tmpbuf[2*RLOG_BUFSIZ+8]; unsigned char *len_buf = (unsigned char *) tmpbuf; krb5_error_code status; krb5_data plain; krb5_enc_data cipher; debug(F111,"krb5_des_write","rlog_encrypt",rlog_encrypt); if ( !rlog_encrypt ) { int cc = net_write(fd, buf, len); debug(F111,"net_write","chars written",cc); return(cc != len ? -1 : len); } if (use_ivecs) { unsigned char *lenbuf2 = (unsigned char *) tmpbuf; if (len + 4 > sizeof(tmpbuf)) abort (); lenbuf2[0] = (len & 0xff000000) >> 24; lenbuf2[1] = (len & 0xff0000) >> 16; lenbuf2[2] = (len & 0xff00) >> 8; lenbuf2[3] = (len & 0xff); memcpy (tmpbuf + 4, buf, len); plain.data = tmpbuf; plain.length = len + 4; } else { plain.data = buf; plain.length = len; } cipher.ciphertext.length = sizeof(des_outpkt)-4; cipher.ciphertext.data = desoutbuf.data; if ( status = krb5_c_encrypt(k5_context, k5_session_key, KCMD_KEYUSAGE, use_ivecs ? encivec_o + secondary : 0, &plain, &cipher)) { printf("Write encrypt problem: %s.\r\n", error_message(status)); errno = EIO; return(-1); } desoutbuf.length = cipher.ciphertext.length; len_buf = (unsigned char *) des_outpkt; len_buf[0] = (len & 0xff000000) >> 24; len_buf[1] = (len & 0xff0000) >> 16; len_buf[2] = (len & 0xff00) >> 8; len_buf[3] = (len & 0xff); if (net_write(fd, des_outpkt,desoutbuf.length+4) != desoutbuf.length+4){ printf("Could not write out all data\r\n"); return(-1); } else return(len); } #endif /* KRB5 */ #ifdef KRB4 /* * Note that the encrypted rlogin packets take the form of a four-byte * length followed by encrypted data. On writing the data out, a significant * performance penalty is suffered (at least one RTT per character, two if we * are waiting for a shell to echo) by writing the data separately from the * length. So, unlike the input buffer, which just contains the output * data, the output buffer represents the entire packet. */ int krb4_des_avail(fd) int fd; { return(nstored); } int krb4_des_read(fd, buf, len) int fd; register char *buf; int len; { int nreturned = 0; unsigned long net_len, rd_len; int cc; unsigned char c; int gotzero = 0; debug(F111,"krb4_des_read","rlog_encrypt",rlog_encrypt); debug(F111,"krb4_des_read","len",len); if ( !rlog_encrypt ) { cc = net_read(fd, buf, len); debug(F111,"krb4_des_read","chars read",cc); if ( cc < 0 ) netclos(); return(cc); } if (nstored >= len) { if ( buf ) { debug(F111,"krb4_des_read (nstored >= len)","nstored",nstored); memcpy(buf, store_ptr, len); /* safe */ store_ptr += len; nstored -= len; return(len); } else return(0); } else if (nstored) { if ( buf ) { debug(F111,"krb4_des_read (nstored)","nstored",nstored); memcpy(buf, store_ptr, nstored); /* safe */ nreturned += nstored; buf += nstored; len -= nstored; nstored = 0; } else return(0); } /* We're fetching the length which is MSB first, and the MSB has to be zero unless the client is sending more than 2^24 (16M) bytes in a single write (which is why this code is in rlogin but not rcp or rsh.) The only reasons we'd get something other than zero are: -- corruption of the tcp stream (which will show up when everything else is out of sync too) -- un-caught Berkeley-style "pseudo out-of-band data" which happens any time the user hits ^C twice. The latter is *very* common, as shown by an 'rlogin -x -d' using the CNS V4 rlogin. Mark EIchin 1/95 */ debug(F110,"krb4_des_read", "about to call net_read() this will block", 0 ); do { cc = net_read(fd, &c, 1); debug(F111,"net_read","chars read",cc); if (cc <= 0) { netclos(); return(-1); } if (cc != 1) return 0; /* read error */ if (cc == 1) { if (c == 0) gotzero = 1; } } while (!gotzero); debug(F110,"krb4_des_read","gotzero",0); cc = net_read(fd, &c, 1); debug(F111,"net_read","chars read",cc); if (cc < 0) { netclos(); return(-1); } else if ( cc != 1 ) return(0); net_len = c; cc = net_read(fd, &c, 1); debug(F111,"net_read","chars read",cc); if (cc < 0) { netclos(); return(-1); } else if ( cc != 1 ) return(0); net_len = (net_len << 8) | c; debug(F111,"net_read","chars read",cc); cc = net_read(fd, &c, 1); if (cc < 0) { netclos(); return(-1); } else if ( cc != 1 ) return(0); net_len = (net_len << 8) | c; debug(F111,"krb4_des_read","net_len",net_len); /* Note: net_len is unsigned */ if (net_len > sizeof(des_inbuf)) { /* XXX preposterous length, probably out of sync. act as if pipe closed */ return(0); } /* the writer tells us how much real data we are getting, but we need to read the pad bytes (8-byte boundary) */ #ifndef roundup #define roundup(x,y) ((((x)+(y)-1)/(y))*(y)) #endif /* roundup */ rd_len = roundup(net_len, 8); debug(F111,"krb4_des_read","rd_len",rd_len); cc = net_read(fd, des_inbuf, rd_len); debug(F111,"net_read","chars read",cc); if (cc < 0) { netclos(); return(-1); } else if ( cc != rd_len ) return(0); ckhexdump("krb4_des_read des_inbuf",des_inbuf,8); #ifdef CK_ENCRYPTION #ifdef KRB524 (void) des_pcbc_encrypt(des_inbuf, storage, (net_len < 8) ? 8 : net_len, k4_sched, cred.session, DECRYPT); #else /* KRB524 */ (void) des_pcbc_encrypt((Block *)des_inbuf, (Block *)storage, (net_len < 8) ? 8 : net_len, k4_sched, &cred.session, DECRYPT); #endif /* KRB524 */ #endif /* ENCRYPTION */ ckhexdump("krb4_des_read storage",storage,8); /* * when the cleartext block is < 8 bytes, it is "right-justified" * in the block, so we need to adjust the pointer to the data */ if (net_len < 8) store_ptr = storage + 8 - net_len; else store_ptr = storage; nstored = net_len; if ( !buf ) return(0); if (nstored > len) { memcpy(buf, store_ptr, len); /* safe */ nreturned += len; store_ptr += len; nstored -= len; } else { memcpy(buf, store_ptr, nstored); /* safe */ nreturned += nstored; nstored = 0; } debug(F111,"net_read","nreturned",nreturned); return(nreturned); } int krb4_des_write(fd, buf, len) int fd; char *buf; int len; { static char garbage_buf[8]; unsigned char *len_buf = (unsigned char *) des_outpkt; int cc; debug(F111,"krb4_des_write","rlog_encrypt",rlog_encrypt); if ( !rlog_encrypt ) { cc = net_write(fd, buf, len); debug(F111,"net_write","chars written",cc); return(cc); } /* * pcbc_encrypt outputs in 8-byte (64 bit) increments * * it zero-fills the cleartext to 8-byte padding, * so if we have cleartext of < 8 bytes, we want * to insert random garbage before it so that the ciphertext * differs for each transmission of the same cleartext. * if len < 8 - sizeof(long), sizeof(long) bytes of random * garbage should be sufficient; leave the rest as-is in the buffer. * if len > 8 - sizeof(long), just garbage fill the rest. */ if (len < 8) { random_confounder(8 - len, garbage_buf); /* this "right-justifies" the data in the buffer */ (void) memcpy(garbage_buf + 8 - len, buf, len); /* safe */ ckhexdump("krb4_des_write garbage_buf",garbage_buf,8); } else ckhexdump("krb4_des_write buf",buf,8); #ifdef CK_ENCRYPTION #ifdef KRB524 (void) des_pcbc_encrypt((len < 8) ? garbage_buf : buf, des_outpkt+4, (len < 8) ? 8 : len, k4_sched, cred.session, ENCRYPT); #else /* KRB524 */ (void) des_pcbc_encrypt((Block *)((len < 8) ? garbage_buf : buf), (Block *)(des_outpkt+4), (len < 8) ? 8 : len, k4_sched, &cred.session, ENCRYPT); #endif /* KRB524 */ #endif /* ENCRYPTION */ if ( len < 8 ) ckhexdump("krb4_des_write (post pcbc) garbage_buf",garbage_buf,8); else ckhexdump("krb4_des_write (post pcbc) buf",buf,8); ckhexdump("krb4_des_write (des_outpkt+4)",(des_outpkt+4),8); /* tell the other end the real amount, but send an 8-byte padded packet */ len_buf[0] = (len & 0xff000000) >> 24; len_buf[1] = (len & 0xff0000) >> 16; len_buf[2] = (len & 0xff00) >> 8; len_buf[3] = (len & 0xff); ckhexdump("krb4_des_write des_outpkt len",des_outpkt,12); cc = net_write(fd, des_outpkt, roundup(len,8)+4); debug(F111,"net_write","chars written",cc); return(len); } #endif /* KRB4 */ #endif /* RLOGCODE */ #ifdef KRB524 #ifndef OS2 /* The following functions are missing from the compatibility library */ const char * krb_get_err_text_entry(r) int r; { extern char krb_err_text[]; return(krb_err_txt[r]); } #endif /* OS2 */ #endif /* KRB524 */ #endif /* CK_KERBEROS */ #ifdef CK_KERBEROS #ifdef KRB5_U2U /* Kerberos 5 User to User Client */ int k5_user_to_user_client_auth() { extern int ttyfd; register int retval, i; char **srealms; /* realm(s) of server */ char *princ; /* principal in credentials cache */ krb5_ccache cc; krb5_creds creds, *new_creds; krb5_data reply, msg, msgtext, princ_data; krb5_ticket * ticket = NULL; if (retval = k5_get_ccache(k5_context,&cc,NULL)) { com_err("uu-client", retval, "getting credentials cache"); return(-1); } memset ((char*)&creds, 0, sizeof(creds)); if (retval = krb5_cc_get_principal(k5_context, cc, &creds.client)) { com_err("uu-client", retval, "getting principal name"); return(-1); } if (retval = krb5_get_host_realm(k5_context, szHostName, &srealms)) { com_err("uu-client", retval, "getting realms for \"%s\"", szHostName); return(-1); } if (retval = krb5_build_principal_ext(k5_context, &creds.server, krb5_princ_realm(k5_context, creds.client)->length, krb5_princ_realm(k5_context, creds.client)->data, 6, "krbtgt", krb5_princ_realm(k5_context, creds.client)->length, krb5_princ_realm(k5_context, creds.client)->data, 0)) { com_err("uu-client", retval, "setting up tgt server name"); return(-1); } /* Get TGT from credentials cache */ if (retval = krb5_get_credentials(k5_context, KRB5_GC_CACHED, cc, &creds, &new_creds)) { com_err("uu-client", retval, "getting TGT"); return(-1); } if (retval = krb5_unparse_name(k5_context, creds.client, &princ)) { com_err("uu-client", retval, "printing principal name"); return(-1); } i = strlen(princ) + 1; princ_data.data = princ; princ_data.length = i; /* include null terminator for server's convenience */ retval = krb5_write_message(k5_context, (krb5_pointer) &ttyfd, &princ_data); if (retval) { com_err("uu-client", retval, "sending principal name to server"); return(-1); } krb5_free_unparsed_name(k5_context,princ); retval = krb5_write_message(k5_context, (krb5_pointer) &ttyfd, &new_creds->ticket); if (retval) { com_err("uu-client", retval, "sending ticket to server"); return(-1); } retval = krb5_read_message(k5_context, (krb5_pointer) &ttyfd, &reply); if (retval) { com_err("uu-client", retval, "reading reply from server"); return(-1); } if (retval = krb5_auth_con_init(k5_context, &auth_context)) { com_err("uu-client", retval, "initializing the auth_context"); return(-1); } if (!krb5_d_no_addresses) { if (retval = krb5_auth_con_genaddrs(k5_context, auth_context, ttyfd, KRB5_AUTH_CONTEXT_GENERATE_LOCAL_FULL_ADDR | KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR)) { com_err("uu-client", retval, "generating addrs for auth_context"); return(-1); } } if (retval = krb5_auth_con_setflags(k5_context, auth_context, KRB5_AUTH_CONTEXT_DO_SEQUENCE)) { com_err("uu-client", retval, "initializing the auth_context flags"); return(-1); } if (retval = krb5_auth_con_setuseruserkey(k5_context, auth_context, &new_creds->keyblock)) { com_err("uu-client", retval, "setting useruserkey for authcontext"); return(-1); } /* read the ap_req to get the session key */ retval = krb5_rd_req(k5_context, &auth_context, &reply, NULL, NULL, NULL, &ticket); if (retval) { com_err("uu-client", retval, "reading AP_REQ from server"); return(-1); } if (k5_u2u_read_msg(k5_context,&msg) < 0) return(-1); if ( strcmp("Kermit implements Kerberos 5 User to User",msg.data) ) return(-1); krb5_free_data_contents(k5_context,&msg); msgtext.data = "As do I! :-)"; msgtext.length = strlen(msgtext.data)+1; if (k5_u2u_write_msg(k5_context,&msgtext) < 0) return(-1); if (retval = krb5_unparse_name(k5_context, #ifdef HEIMDAL ticket->client, #else /* HEIMDAL */ ticket->enc_part2->client, #endif /* HEIMDAL */ &princ)) com_err("uu-client", retval, "while unparsing client name"); else { ckstrncpy(szUserNameAuthenticated,princ,UIDBUFLEN); validUser = AUTH_VALID; authentication_version = AUTHTYPE_KERBEROS_V5; if ( !quiet ) printf("Peer name is \"%s\"\n", princ); krb5_free_unparsed_name(k5_context,princ); } return 0; } /* Kerberos 5 User to User Server */ int k5_user_to_user_server_auth() { krb5_data pname_data, tkt_data; int retval; krb5_creds creds, *new_creds; krb5_ccache cc; krb5_data msg, msgtext; extern int ttyfd; if (retval = krb5_read_message(k5_context, (krb5_pointer) &ttyfd, &pname_data)) { com_err ("uu-server", retval, "reading pname"); return(-1); } /* client sends it already null-terminated. */ if ( !quiet ) printf ("Peer name is \"%s\".\n", pname_data.data); ckstrncpy(szUserNameAuthenticated,pname_data.data,UIDBUFLEN); validUser = AUTH_VALID; authentication_version = AUTHTYPE_KERBEROS_V5; if (retval = krb5_read_message(k5_context, (krb5_pointer) &ttyfd, &tkt_data)) { com_err ("uu-server", retval, "reading ticket data"); return(-1); } if (retval = k5_get_ccache(k5_context,&cc,NULL)) { com_err("uu-server", retval, "getting credentials cache"); return(-1); } memset ((char*)&creds, 0, sizeof(creds)); if (retval = krb5_cc_get_principal(k5_context, cc, &creds.client)) { com_err("uu-server", retval, "getting principal name"); return(-1); } if (retval = krb5_parse_name(k5_context, pname_data.data, &creds.server)) { com_err("uu-server", retval, "parsing client name"); return(-1); } creds.second_ticket = tkt_data; if (retval = krb5_get_credentials(k5_context, KRB5_GC_USER_USER, cc, &creds, &new_creds)) { com_err("uu-server", retval, "getting user-user ticket"); return(-1); } /* send a ticket/authenticator to the other side, so it can get the key we're using for the krb_safe below. */ if (retval = krb5_auth_con_init(k5_context, &auth_context)) { com_err("uu-server", retval, "making auth_context"); return(-1); } if (retval = krb5_auth_con_setflags(k5_context, auth_context, KRB5_AUTH_CONTEXT_DO_SEQUENCE)) { com_err("uu-server", retval, "initializing the auth_context flags"); return(-1); } if (!krb5_d_no_addresses) { if (retval = krb5_auth_con_genaddrs(k5_context, auth_context, ttyfd, KRB5_AUTH_CONTEXT_GENERATE_LOCAL_FULL_ADDR | KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR)) { com_err("uu-server", retval, "generating addrs for auth_context"); return(-1); } } if (retval = krb5_auth_con_setuseruserkey(k5_context, auth_context, &new_creds->keyblock)) { com_err("uu-server", retval, "setting useruserkey for authcontext"); return(-1); } if (retval = krb5_mk_req_extended(k5_context, &auth_context, AP_OPTS_USE_SESSION_KEY | AP_OPTS_MUTUAL_REQUIRED, NULL, new_creds, &msg)) { com_err("uu-server", retval, "making AP_REQ"); return(-1); } retval = krb5_write_message(k5_context, (krb5_pointer) &ttyfd, &msg); if (retval) { com_err("uu-server", retval, "writing message to client"); return(-1); } krb5_free_data_contents(k5_context,&msg); msgtext.data = "Kermit implements Kerberos 5 User to User"; msgtext.length = strlen(msgtext.data)+1; if (k5_u2u_write_msg(k5_context,&msgtext) < 0) return(-1); if (k5_u2u_read_msg(k5_context,&msg) < 0) return(-1); if ( strcmp("As do I! :-)",msg.data) ) return(-1); krb5_free_data_contents(k5_context,&msg); return(0); } int k5_u2u_read_msg(krb5_context context, int fd, krb5_data * msg) { int retval; krb5_data reply; retval = krb5_read_message(context, (krb5_pointer) &fd, &reply); if (retval) { com_err("uu-client", retval, "reading reply"); return(-1); } if (retval = krb5_rd_priv(context, auth_context, &reply, msg, NULL)) { com_err("uu-client", retval, "decoding reply"); return(-1); } return(0); } int k5_u2u_write_msg(krb5_context context, int fd, krb5_data * msgtext) { int retval; krb5_data msg; if (retval = krb5_mk_priv(k5_context, auth_context, msgtext, &msg, NULL)) { com_err("uu-server", retval, "encoding message"); return(-1); } retval = krb5_write_message(k5_context, (krb5_pointer) &fd, &msg); krb5_free_data_contents(k5_context,&msg); if (retval) { com_err("uu-server", retval, "writing message"); return(-1); } return(0); } int krb5_u2u_avail(fd) int fd; { return(nstored); } int krb5_u2u_read(fd, buf, len) int fd; register char *buf; int len; { int nreturned = 0; krb5_data msg; debug(F111,"krb5_u2u_read","len",len); if ( !buf ) return(0); if (nstored >= len) { memcpy(buf, store_ptr, len); /* safe */ store_ptr += len; nstored -= len; return(len); } else if (nstored) { memcpy(buf, store_ptr, nstored); /* safe */ nreturned += nstored; buf += nstored; len -= nstored; nstored = 0; } if (k5_u2u_read_msg(k5_context, fd, &msg) < 0) return(-1); if ( msg.length <= len ) { memcpy(buf, msg.data, msg.length); nreturned += msg.length; nstored = 0; } else { memcpy(buf, msg.data, len); nreturned += len; if ( msg.length - len < sizeof(storage) ) { store_ptr = storage; nstored = msg.length - len; memcpy(storage,msg.data+len,nstored); } else { nstored = 0; return(-1); } } return(nreturned); } int krb5_u2u_write(fd, buf, len) int fd; char *buf; int len; { krb5_data msg; msg.length = len; msg.data = buf; if ( k5_u2u_write_msg(k5_context, fd, &msg) < 0 ) return(-1); else return(len); } #endif /* KRB5_U2U */ #endif /* CK_KERBEROS */ #ifdef CK_FORWARD_X /* Copyright (c) 1988 X Consortium Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of the X Consortium shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from the X Consortium. */ /* Modified for stand-alone compiling by * Peter 'Luna' Runestig */ #include #include #include #include #include #define Time_t time_t void XauDisposeAuth (auth) Xauth *auth; { if (auth) { if (auth->address) (void) free (auth->address); if (auth->number) (void) free (auth->number); if (auth->name) (void) free (auth->name); if (auth->data) { (void) bzero (auth->data, auth->data_length); (void) free (auth->data); } free ((char *) auth); } return; } char * XauFileName () { char *slashDotXauthority = "/.Xauthority"; char *name; static char *buf=NULL; static int bsize=0; int size, namelen; extern char * tn_fwdx_xauthority; if ( tn_fwdx_xauthority ) return(tn_fwdx_xauthority); if (name = getenv ("XAUTHORITY")) return(name); name = zhome(); if ( !name ) return(NULL); namelen = strlen (name); size = namelen + strlen(slashDotXauthority) + 1; if (size > bsize) { if (buf) free (buf); buf = malloc ((unsigned) size); if (!buf) return 0; bsize = size; } ckstrncpy (buf, name, bsize); if ( name[namelen-1] != '/' #ifdef OS2 && name[namelen-1] != '\\' #endif /* OS2 */ ) ckstrncat (buf, slashDotXauthority, bsize); else ckstrncat (buf, &slashDotXauthority[1], bsize); return(buf); } static int binaryEqual (a, b, len) register char *a, *b; register int len; { while (len--) if (*a++ != *b++) return 0; return 1; } #ifndef R_OK #define R_OK 04 #endif /* R_OK */ Xauth * XauGetAuthByAddr (family, address_length, address, number_length, number, name_length, name) unsigned int family; unsigned int address_length; const char *address; unsigned int number_length; const char *number; unsigned int name_length; const char *name; { FILE *auth_file; char *auth_name; Xauth *entry; auth_name = XauFileName(); if (!auth_name) return 0; if (access (auth_name, R_OK) != 0) /* checks REAL id */ return 0; auth_file = fopen (auth_name, "rb"); if (!auth_file) return 0; for (;;) { entry = XauReadAuth (auth_file); if (!entry) break; /* * Match when: * either family or entry->family are FamilyWild or * family and entry->family are the same * and * either address or entry->address are empty or * address and entry->address are the same * and * either number or entry->number are empty or * number and entry->number are the same * and * either name or entry->name are empty or * name and entry->name are the same */ /* if ((family == FamilyWild || entry->family == FamilyWild || (entry->family == family && address_length == entry->address_length && binaryEqual (entry->address, address, (int)address_length))) && (number_length == 0 || entry->number_length == 0 || (number_length == entry->number_length && binaryEqual (entry->number, number, (int)number_length))) && (name_length == 0 || entry->name_length == 0 || (entry->name_length == name_length && binaryEqual (entry->name, name, (int)name_length)))) */ /* the original matching code above doesn't seem to meet the matching * algorithm, it doesn't check if "address_length == 0 || * entry->address_length == 0". / Luna 2000-02-09 */ if ((family == FamilyWild || entry->family == FamilyWild || entry->family == family) && (address_length == 0 || entry->address_length == 0 || (address_length == entry->address_length && binaryEqual (entry->address, address, (int)address_length))) && (number_length == 0 || entry->number_length == 0 || (number_length == entry->number_length && binaryEqual (entry->number, number, (int)number_length))) && (name_length == 0 || entry->name_length == 0 || (entry->name_length == name_length && binaryEqual (entry->name, name, (int)name_length)))) break; XauDisposeAuth (entry); } (void) fclose (auth_file); return entry; } static int read_short (shortp, file) unsigned short *shortp; FILE *file; { unsigned char file_short[2]; if (fread ((char *) file_short, (int) sizeof (file_short), 1, file) != 1) return 0; *shortp = file_short[0] * 256 + file_short[1]; return 1; } static int read_counted_string (countp, stringp, file) unsigned short *countp; char **stringp; FILE *file; { unsigned short len; char *data; if (read_short (&len, file) == 0) return 0; if (len == 0) { data = 0; } else { data = malloc ((unsigned) len); if (!data) return 0; if (fread (data, (int) sizeof (char), (int) len, file) != len) { bzero (data, len); free (data); return 0; } } *stringp = data; *countp = len; return 1; } Xauth * XauReadAuth (auth_file) FILE *auth_file; { Xauth local; Xauth *ret; if (read_short (&local.family, auth_file) == 0) return 0; if (read_counted_string (&local.address_length, &local.address, auth_file) == 0) return 0; if (read_counted_string (&local.number_length, &local.number, auth_file) == 0) { if (local.address) free (local.address); return 0; } if (read_counted_string (&local.name_length, &local.name, auth_file) == 0) { if (local.address) free (local.address); if (local.number) free (local.number); return 0; } if (read_counted_string (&local.data_length, &local.data, auth_file) == 0) { if (local.address) free (local.address); if (local.number) free (local.number); if (local.name) free (local.name); return 0; } ret = (Xauth *) malloc (sizeof (Xauth)); if (!ret) { if (local.address) free (local.address); if (local.number) free (local.number); if (local.name) free (local.name); if (local.data) { bzero (local.data, local.data_length); free (local.data); } return 0; } *ret = local; return ret; } static int write_short (s, file) unsigned short s; FILE *file; { unsigned char file_short[2]; file_short[0] = (s & (unsigned)0xff00) >> 8; file_short[1] = s & 0xff; if (fwrite ((char *) file_short, (int) sizeof (file_short), 1, file) != 1) return 0; return 1; } static int write_counted_string (count, string, file) unsigned short count; char *string; FILE *file; { if (write_short (count, file) == 0) return 0; if (fwrite (string, (int) sizeof (char), (int) count, file) != count) return 0; return 1; } int XauWriteAuth (auth_file, auth) FILE *auth_file; Xauth *auth; { if (write_short (auth->family, auth_file) == 0) return 0; if (write_counted_string (auth->address_length, auth->address, auth_file) == 0) return 0; if (write_counted_string (auth->number_length, auth->number, auth_file) == 0) return 0; if (write_counted_string (auth->name_length, auth->name, auth_file) == 0) return 0; if (write_counted_string (auth->data_length, auth->data, auth_file) == 0) return 0; return 1; } #ifdef KRB5 #ifdef K5_XAUTH /* * functions to encode/decode Kerberos V5 principals * into something that can be reasonable spewed over * the wire * * Author: Tom Yu * * Still needs to be fixed up wrt signed/unsigned lengths, but we'll worry * about that later. */ /* * XauKrb5Encode * * this function encodes the principal passed to it in a format that can * easily be dealt with by stuffing it into an X packet. Encoding is as * follows: * length count of the realm name * realm * component count * length of component * actual principal component * etc.... * * Note that this function allocates a hunk of memory, which must be * freed to avoid nasty memory leak type things. All counts are * byte-swapped if needed. (except for the total length returned) * * nevermind.... stuffing the encoded packet in net byte order just to * always do the right thing. Don't have to frob with alignment that way. */ int XauKrb5Encode(princ, outbuf) krb5_principal princ; /* principal to encode */ krb5_data *outbuf; /* output buffer */ { CARD16 i, numparts, totlen = 0, plen, rlen; char *cp, *pdata; rlen = krb5_princ_realm(princ)->length; numparts = krb5_princ_size(princ); totlen = 2 + rlen + 2; /* include room for realm length and component count */ for (i = 0; i < numparts; i++) totlen += krb5_princ_component(princ, i)->length + 2; /* add 2 bytes each time for length */ if ((outbuf->data = (char *)malloc(totlen)) == NULL) return -1; cp = outbuf->data; *cp++ = (char)((int)(0xff00 & rlen) >> 8); *cp++ = (char)(0x00ff & rlen); memcpy(cp, krb5_princ_realm(princ)->data, rlen); /* safe */ cp += rlen; *cp++ = (char)((int)(0xff00 & numparts) >> 8); *cp++ = (char)(0x00ff & numparts); for (i = 0; i < numparts; i++) { plen = krb5_princ_component(princ, i)->length; pdata = krb5_princ_component(princ, i)->data; *cp++ = (char)((int)(0xff00 & plen) >> 8); *cp++ = (char)(0x00ff & plen); memcpy(cp, pdata, plen); /* safe */ cp += plen; } outbuf->length = totlen; return 0; } /* * XauKrb5Decode * * This function essentially reverses what XauKrb5Encode does. * return value: 0 if okay, -1 if malloc fails, -2 if inbuf format bad */ int XauKrb5Decode(inbuf, princ) krb5_data inbuf; krb5_principal *princ; { CARD16 i, numparts, plen, rlen; CARD8 *cp, *pdata; if (inbuf.length < 4) { return -2; } *princ = (krb5_principal)malloc(sizeof (krb5_principal_data)); if (*princ == NULL) return -1; bzero(*princ, sizeof (krb5_principal_data)); cp = (CARD8 *)inbuf.data; rlen = *cp++ << 8; rlen |= *cp++; if (inbuf.length < 4 + (int)rlen + 2) { krb5_free_principal(*princ); return -2; } krb5_princ_realm(*princ)->data = (char *)malloc(rlen); if (krb5_princ_realm(*princ)->data == NULL) { krb5_free_principal(*princ); return -1; } krb5_princ_realm(*princ)->length = rlen; memcpy(krb5_princ_realm(*princ)->data, cp, rlen); /* safe */ cp += rlen; numparts = *cp++ << 8; numparts |= *cp++; krb5_princ_name(*princ) = (krb5_data *)malloc(numparts * sizeof (krb5_data)); if (krb5_princ_name(*princ) == NULL) { krb5_free_principal(*princ); return -1; } krb5_princ_size(*princ) = 0; for (i = 0; i < numparts; i++) { if (cp + 2 > (CARD8 *)inbuf.data + inbuf.length) { krb5_free_principal(*princ); return -2; } plen = *cp++ << 8; plen |= *cp++; if (cp + plen > (CARD8 *)inbuf.data + inbuf.length) { krb5_free_principal(*princ); return -2; } pdata = (CARD8 *)malloc(plen); if (pdata == NULL) { krb5_free_principal(*princ); return -1; } krb5_princ_component(*princ, i)->data = (char *)pdata; krb5_princ_component(*princ, i)->length = plen; memcpy(pdata, cp, plen); /* safe */ cp += plen; krb5_princ_size(*princ)++; } return 0; } #endif /* K5_XAUTH */ #endif /* KRB5 */ #endif /* CK_FORWARD_X */ #endif /* CK_AUTHENTICATION */ /* C K _ A U T H _ I N I T * Initialize the Kerberos system for a pending connection * hostname - a reverse DNS lookup of the hostname when possible * ipaddr - the ip address of the host * username - the name the user wants to connect under not necessarily * the same as principal * socket - the socket handle (ttyfd in Kermit speak) * * Returns: 1 on success and 0 on failure */ int #ifdef CK_ANSIC ck_auth_init( char * hostname, char * ipaddr, char * username, int socket ) #else /* CK_ANSIC */ ck_auth_init( hostname, ipaddr, username, socket ) char * hostname; char * ipaddr; char *username; int socket; #endif /* CK_ANSIC */ { #ifdef CK_AUTHENTICATION #ifdef OS2 if ( !ck_security_loaddll() ) { TELOPT_ME_MODE(TELOPT_AUTHENTICATION) = TN_NG_RF; TELOPT_U_MODE(TELOPT_AUTHENTICATION) = TN_NG_RF; return(0); } #endif /* OS2 */ #endif /* CK_AUTHENTICAITON */ #ifdef CK_ENCRYPTION if ( !!ck_crypt_is_installed() ) { TELOPT_ME_MODE(TELOPT_ENCRYPTION) = TN_NG_RF; TELOPT_U_MODE(TELOPT_ENCRYPTION) = TN_NG_RF; } #endif /* CK_ENCRYPTION */ if (!hostname) hostname = ""; if (!ipaddr) ipaddr = ""; if (!username) username = ""; debug(F110,"ck_auth_init Username",username,0); debug(F110,"ck_auth_init Hostname",hostname,0); debug(F110,"ck_auth_init Ipaddr",ipaddr,0); ckstrncpy( szUserName, username, UIDBUFLEN ); ckstrncpy( szHostName, hostname, UIDBUFLEN ); ckstrncpy( szIP, ipaddr, 16 ); szUserNameRequested[0] = '\0'; szUserNameAuthenticated[0] = '\0'; validUser = AUTH_REJECT; accept_complete = 0; authentication_version = AUTHTYPE_NULL; #ifdef CK_AUTHENTICATION auth_how = 0; auth_crypt = 0; auth_fwd = 0; mutual_complete = 0; if ( sstelnet ) str_data[3] = TELQUAL_REPLY; else str_data[3] = TELQUAL_IS; #endif /* CK_AUTHENTICATION */ #ifdef CK_SRP srp_waitresp = 0; #endif /* SRP */ #ifdef CK_KERBEROS #ifdef KRB5 /* free previous ret_cred */ if ( ret_cred ) { #ifdef CK_ENCRYPTION #ifdef HEIMDAL if ( k5_session_key == &ret_cred->session) k5_session_key = NULL; #else /* HEIMDAL */ if ( k5_session_key == &ret_cred->keyblock) k5_session_key = NULL; #endif /* HEIMDAL */ #endif /* CK_ENCRYPTION */ krb5_free_creds(k5_context, ret_cred); ret_cred = NULL; } if (k5_ticket) { krb5_free_ticket(k5_context, k5_ticket); k5_ticket = NULL; } /* and context */ if ( k5_context ) { krb5_free_context(k5_context); k5_context = NULL; } /* create k5_context */ krb5_init_context(&k5_context); #ifndef MIT_CURRENT #ifndef NO_KRB5_INIT_ETS /* This routine is a no-op in Kerberos 1.4.x and later */ /* and in some installations it can't be found in which case */ /* define NO_KRB5_INIT_ETS */ if (k5_context) krb5_init_ets(k5_context); #endif /* NO_KRB5_INIT_ETS */ #endif /* MIT_CURRENT */ #ifdef KRB524_CONV krb524_init_ets(k5_context); #endif /* KRB524_CONV */ memset(&k5_auth,0,sizeof(k5_auth)); if (auth_context) { krb5_auth_con_free(k5_context, auth_context); auth_context = 0; } #ifdef CK_ENCRYPTION if (k5_session_key) { krb5_free_keyblock(k5_context, k5_session_key); k5_session_key = 0; } #endif /* ENCRYPTION */ #ifdef TLS_VERIFY krb5_tls_verified = 0; #endif /* TLS_VERIFY */ #endif /* KRB5 */ #ifdef KRB4 #ifdef CK_ENCRYPTION /* Initialize buffers used for authentication */ memset(&k4_session_key, 0, sizeof(k4_session_key)); memset(&k4_challenge, 0, sizeof(k4_challenge)); #endif /* CK_ENCRYPTION */ #endif /* KRB4 */ #ifdef RLOGCODE rlog_encrypt = 0; #endif /* RLOGCODE */ nstored = 0; store_ptr = storage; memset(storage,0,sizeof(storage)); #endif /* CK_KERBEROS */ #ifdef CK_ENCRYPTION kstream_destroy(); if (!kstream_create_from_fd(socket, NULL)) return(0); #endif /* CK_ENCRYPTION */ return(1); } void auth_finished(result) int result; { extern char uidbuf[]; extern int sstelnet; validUser = result; switch (result) { case AUTH_REJECT: /* Rejected */ if (sstelnet) uidbuf[0] = '\0'; authentication_version = AUTHTYPE_NULL; break; case AUTH_UNKNOWN: /* We don't know who he is, but he's okay */ if (sstelnet) strcpy(uidbuf,"(unknown)"); break; case AUTH_OTHER: /* We know him, but not his name */ if (sstelnet) strcpy(uidbuf,"(other)"); break; case AUTH_USER: /* We know he name */ case AUTH_VALID: /* We know him, and he needs no password */ if (sstelnet) strcpy(uidbuf,szUserNameRequested); break; } } #ifdef MACOSX #ifdef KRB5 krb5_error_code ck_krb5_write_message(krb5_context con, krb5_pointer ptr, krb5_data *data) { int fd = *((int *)ptr); long msglen; msglen = htonl(data->length); if (net_write(fd,(CHAR *)&msglen,4) != 4) { return(-1); } if ( data->length ) { if (net_write(fd,data->data,data->length) != data->length) { return(-1); } } return(0); } krb5_error_code ck_krb5_read_message( krb5_context context, krb5_pointer ptr, krb5_data * data) { extern int ttyfd; int fd = *((int *)ptr); long msglen; char *p; int i, rc; if (net_read(fd,&msglen,4) < 0) return(-1); data->length = ntohl(msglen); if ( data->length ) { data->data = malloc(data->length); i = 0; while ( i < data->length ) { if ((rc = net_read(fd,&data->data[i],(data->length - i))) < 0) return(-1); i += rc; } } return(0); } #endif /* KRB5 */ #endif /* MACOSX */ #endif /* CK_SECURITY */ ckuath.h0000644000015300001460000002025611330126534011314 0ustar fdckermit/* C K U A T H . H -- "C-Kermit to Authentication" interface */ /* Author: Jeffrey E Altman , Secure Endpoints Inc., New York City. Copyright (C) 1999, 2009, Trustees of Columbia University in the City of New York. All rights reserved. See the C-Kermit COPYING.TXT file or the copyright text in the ckcmai.c module for disclaimer and permissions. */ /* * Based on a concatenation of all necessary include files distributed with * the Kerberos 5 NT Alpha 2 Telnet package from MIT. */ #ifndef KRB5_KERMIT_H #define KRB5_KERMIT_H #ifndef BOOL #define BOOL int #endif /* Header file for encrypted-stream library. * Written by Ken Raeburn (Raeburn@Cygnus.COM). * Copyright (C) 1991, 1992, 1994 by Cygnus Support. * * Permission to use, copy, modify, and * distribute this software and its documentation for any purpose and * without fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright notice and * this permission notice appear in supporting documentation. * Cygnus Support makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. */ #ifndef K5STREAM_H #define K5STREAM_H typedef void *kstream_ptr; /* Data send on the kstream */ struct kstream_data_block { kstream_ptr ptr; size_t length; }; typedef struct kstream_int { /* Object we pass around */ int fd; /* Open socket descriptor */ int (*encrypt)(struct kstream_data_block *, /* output */ struct kstream_data_block *); /* input */ int encrypt_type; int (*decrypt)(struct kstream_data_block *, /* output */ struct kstream_data_block *); /* input */ int decrypt_type; } *kstream; /* Prototypes */ int kstream_destroy(); void kstream_set_buffer_mode(int); int kstream_create_from_fd(int fd, kstream_ptr); int kstream_write(void *, size_t); int kstream_read(void *, size_t); #endif /* K5STREAM_H */ /* * Implements Telnet authentication and encryption */ #ifndef TELNET_AUTH_H #define TELNET_AUTH_H int auth_parse(unsigned char *, int); int auth_init(kstream); void auth_destroy(void); int auth_encrypt(struct kstream_data_block *, struct kstream_data_block *); int auth_decrypt(struct kstream_data_block *, struct kstream_data_block *); extern BOOL forward_flag; extern BOOL forwardable_flag; extern BOOL forwarded_tickets; #endif /* TEL_AUTH_H */ /* C-Kermit specific functions */ _PROTOTYP(void auth_finished,(int)); _PROTOTYP(int ck_auth_init, (char *, char *, char *, int)); _PROTOTYP(int ck_tn_auth_valid, (VOID)); _PROTOTYP(int ck_tn_auth_in_progress,(VOID)); _PROTOTYP(int ck_tn_sb_auth, (char *, int)); _PROTOTYP(int ck_tn_sb_encrypt, (char *, int)); _PROTOTYP(int ck_tn_auth_request, (VOID)); _PROTOTYP(void ck_tn_encrypt, (char *, int)); _PROTOTYP(void ck_tn_decrypt, (char *, int)); _PROTOTYP(void ck_tn_encrypt_start, (VOID)); _PROTOTYP(void ck_tn_encrypt_stop, (VOID)); _PROTOTYP(int ck_tn_authenticated, (VOID)); #ifdef CK_ENCRYPTION _PROTOTYP(int ck_tn_encrypting, (VOID)); _PROTOTYP(int ck_tn_decrypting, (VOID)); #endif /* CK_ENCRYPTION */ #ifdef CK_SSL _PROTOTYP(int ck_tn_tls_negotiate, (VOID)); _PROTOTYP(int SendSSLAuthSB, (int, void *, int)); #endif /* CK_SSL */ #ifdef CK_KERBEROS /* Define MIT_CURRENT to compile the code for use with versions of */ /* Kerberos later than KRB5 1.0.5. Note. This will not compile */ /* successfully in Kermit 95 due to the segmentation of crypto */ /* into a separate DLL. */ #ifndef KRB5_INIT_ETS /* krb5_init_ets() is a no-op in Kerberos 1.4.x and later */ /* and in some installations it can't be found so now by default */ /* we don't use it. */ #define NO_KRB5_INIT_ETS #endif /* KRB5_INIT_ETS */ #define KRB_DEFTIM 600 /* Default lifetime (minutes) */ /* Kerberos structure definitions */ struct krb_op_data { /* Operational data for all actions */ int version; /* Kerberos version */ char * cache; /* Kerberos cache file */ }; struct krb4_init_data { /* INITIALIZE data structure */ int lifetime; char * principal; /* Principal string */ char * instance; char * realm; /* Realm string */ char * password; /* Kerberos password */ int preauth; /* Use preauth mode? */ int verbose; /* Verbose output? */ }; #define KRB5_NUM_OF_ADDRS 16 struct krb5_init_data { /* INITIALIZE data structure */ int forwardable; /* Switch values */ int proxiable; /* Correspond to switch names... */ int lifetime; int renew; int renewable; int validate; char * postdate; char * service; char * principal; /* Principal string */ char * instance; char * realm; /* Realm string */ char * password; /* Kerberos password */ int preauth; /* Use preauth mode? */ int verbose; /* Verbose output? */ int getk4; /* Get K4 TGT? */ char * addrs[KRB5_NUM_OF_ADDRS+1]; /* List of IP Addresses */ int no_addresses; /* Do not include IP Addresses */ }; struct krb5_list_cred_data { /* List Credentials data */ int encryption; int flags; int addr; }; _PROTOTYP(int ck_krb5_autoget_TGT, (char *)); _PROTOTYP(int ck_krb5_initTGT, (struct krb_op_data *,struct krb5_init_data *, struct krb4_init_data *)); _PROTOTYP(int ck_krb5_destroy, (struct krb_op_data *)); _PROTOTYP(int ck_krb5_list_creds, (struct krb_op_data *, struct krb5_list_cred_data *)); _PROTOTYP(char * ck_krb5_getrealm, (char *)); _PROTOTYP(char * ck_krb5_getprincipal, (char *)); _PROTOTYP(char * ck_krb5_get_cc_name, (VOID)); _PROTOTYP(int ck_krb4_autoget_TGT, (char *)); _PROTOTYP(int ck_krb4_initTGT, (struct krb_op_data *,struct krb4_init_data *)); _PROTOTYP(int ck_krb4_destroy, (struct krb_op_data *)); _PROTOTYP(int ck_krb4_list_creds, (struct krb_op_data *)); _PROTOTYP(char * ck_krb4_getrealm, (VOID)); _PROTOTYP(char * ck_krb4_getprincipal, (VOID)); _PROTOTYP(int ck_krb4_get_tkts, (VOID)); _PROTOTYP(char * ck_krb4_get_next_tkt, (VOID)); _PROTOTYP(int ck_krb4_tkt_isvalid,(char *)); _PROTOTYP(int ck_krb4_is_tgt_valid,(VOID)); _PROTOTYP(int ck_krb4_tkt_time,(char *)); _PROTOTYP(int ck_krb5_get_tkts, (char *)); _PROTOTYP(char * ck_krb5_get_next_tkt, (VOID)); _PROTOTYP(int ck_krb5_tkt_isvalid,(char *,char *)); _PROTOTYP(char * ck_krb5_tkt_flags,(char *,char *)); _PROTOTYP(int ck_krb5_is_tgt_valid,(VOID)); _PROTOTYP(int ck_krb5_tkt_time,(char *,char *)); _PROTOTYP(int krb4_des_avail,(int)); _PROTOTYP(int krb4_des_write,(int,char *,int)); _PROTOTYP(int krb4_des_read, (int,char *,int)); _PROTOTYP(int krb5_des_avail,(int)); _PROTOTYP(int krb5_des_write,(int,char *,int,int)); _PROTOTYP(int krb5_des_read, (int,char *,int,int)); _PROTOTYP(int krb5_u2u_avail,(int)); _PROTOTYP(int krb5_u2u_write,(int,char *,int)); _PROTOTYP(int krb5_u2u_read, (int,char *,int)); _PROTOTYP(int k5_user_to_user_server_auth,(VOID)); _PROTOTYP(int k5_user_to_user_client_auth,(VOID)); #endif /* CK_KERBEROS */ _PROTOTYP(int ck_krb5_is_installed,(void)); _PROTOTYP(int ck_krb4_is_installed,(void)); _PROTOTYP(int ck_srp_is_installed,(void)); _PROTOTYP(int ck_ntlm_is_installed,(void)); _PROTOTYP(int ck_crypt_is_installed,(void)); _PROTOTYP(int ck_ssleay_is_installed,(void)); _PROTOTYP(int ck_gssapi_is_installed,(void)); _PROTOTYP(int ck_krypto_is_installed,(void)); _PROTOTYP(VOID ck_encrypt_send_support,(VOID)); _PROTOTYP(int ck_get_crypt_table,(struct keytab **, int *)); _PROTOTYP(char * ck_krb4_realmofhost,(char *)); _PROTOTYP(char * ck_krb5_realmofhost,(char *)); #define FORWARD /* allow forwarding of credential */ #ifdef FORWARD _PROTOTYP(int kerberos5_forward,(VOID)); #endif /* FORWARD */ #define AUTHTYPLSTSZ 8 #endif /*KRB5_KERMIT_H*/ ckucmd.c0000644000015300001460000064542411607556655011332 0ustar fdckermit#include "ckcsym.h" char *cmdv = "Command package 9.0.168, 12 March 2010"; /* C K U C M D -- Interactive command package for Unix */ /* (In reality, it's for all platforms, not just Unix) */ /* Author: Frank da Cruz (fdc@columbia.edu), Columbia University Academic Information Systems, New York City. Copyright (C) 1985, 2010, Trustees of Columbia University in the City of New York. All rights reserved. See the C-Kermit COPYING.TXT file or the copyright text in the ckcmai.c module for disclaimer and permissions. */ #define TOKPRECHECK #define DOCHKVAR /* Command-terminal-to-C-Kermit character mask */ #ifdef OS2 /* K95 */ int cmdmsk = 255; /* (always was 255) */ #else /* All others... */ int cmdmsk = 255; /* 31 Dec 2000 (was 127) */ #endif /* OS2 */ #ifdef BS_DIRSEP /* Directory separator is backslash */ #undef BS_DIRSEP #endif /* BS_DIRSEP */ #ifdef OS2 #define BS_DIRSEP #endif /* BS_DIRSEP */ #define CKUCMD_C #include "ckcdeb.h" /* Formats for debug(), etc. */ #include "ckcker.h" /* Needed for BIGBUFOK definition */ #include "ckcnet.h" /* Needed for server-side Telnet */ #include "ckucmd.h" /* Needed for everything */ #include "ckuusr.h" /* Needed for prompt length */ #ifndef NOARROWKEYS #ifndef NOESCSEQ #ifdef VMSORUNIX #define USE_ARROWKEYS /* Use arrow keys for command recall */ #endif /* VMSORUNIX */ #endif /* NOESCSEQ */ #endif /* NOARROWKEYS */ #undef CKUCMD_C _PROTOTYP( int unhex, (char) ); _PROTOTYP( static VOID cmdclrscn, (void) ); #ifdef CKLEARN _PROTOTYP( VOID learncmd, (char *) ); #endif /* CKLEARN */ static char *moname[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; struct keytab cmonths[] = { { "april", 4, 0 }, { "august", 8, 0 }, { "december", 12, 0 }, { "february", 2, 0 }, { "january", 1, 0 }, { "july", 7, 0 }, { "june", 6, 0 }, { "march", 3, 0 }, { "may", 5, 0 }, { "november", 11, 0 }, { "october", 10, 0 }, { "september", 9, 0 } }; #ifndef NOICP /* The rest only if interactive command parsing selected */ #ifndef NOSPL _PROTOTYP( int chkvar, (char *) ); extern int askflag, echostars; #endif /* NOSPL */ #ifdef CKROOT extern int ckrooterr; #endif /* CKROOT */ #ifdef IKSD extern int inserver; #endif /* IKSD */ int cmfldflgs = 0; /* Flags for cmfld() */ int cmkwflgs = 0; /* Flags from last keyword parse */ static int nomsg = 0; static int blocklvl = 0; /* Block nesting level */ static int linebegin = 0; /* Flag for at start of a line */ static int quoting = 1; /* Quoting is allowed */ static int swarg = 0; /* Parsing a switch argument */ static int xcmfdb = 0; /* Flag for parsing chained fdbs... */ static int chsrc = 0; /* Source of character, 1 = tty */ static int newcmd = 0; /* See addcmd() */ #ifdef BS_DIRSEP static int dirnamflg = 0; #endif /* BS_DIRSEP */ /* Modeled after the DECSYSTEM-20 command parser (the COMND JSYS), RIP. Features: . parses and verifies keywords, filenames, text strings, numbers, other data . displays appropriate menu or help message when user types "?" . does keyword and filename completion when user types ESC or TAB . does partial keyword and filename completion . accepts any unique abbreviation for a keyword . allows keywords to have attributes, like "invisible" and "abbreviation" . can supply defaults for fields omitted by user . provides command retry and recall . provides character, word, and line deletion (but only from the end) . accepts input from keyboard, command files, macros, or redirected stdin . allows for full or half duplex operation, character or line input . allows \-escapes for special characters . allows specification of a user exit to expand variables, etc. . settable prompt, protected from deletion, dynamically re-evaluated each time . allows chained parse functions. Functions: cmsetp - Set prompt (cmprom is prompt string) cmsavp - Save current prompt cmgetp = Get current prompt prompt - Issue prompt cmini - Clear the command buffer (before parsing a new command) cmres - Reset command buffer pointers (before reparsing) cmkey - Parse a keyword or token (also cmkey2) cmswi - Parse a switch cmnum - Parse a number cmifi - Parse an input file name cmofi - Parse an output file name (also cmifip, cmifi2, ...) cmdir - Parse a directory name (also cmdirp) cmfld - Parse an arbitrary field cmtxt - Parse a text string cmdate - Parse a date-time string cmcfm - Parse command confirmation (end of line) cmfdb - Parse any of a list of the foregoing (chained parse functions) Return codes: -9: like -2 except this module already printed the error message -3: no input provided when required -2: input was invalid (e.g. not a number when a number was required) -1: reparse required (user deleted into a preceding field) 0 or greater: success See individual functions for greater detail. Before using these routines, the caller should #include "ckucmd.h" and set the program's prompt by calling cmsetp(). If the file parsing functions cmifi, cmofi, or cmdir are to be used, this module must be linked with a ck?fio file system support module for the appropriate system, e.g. ckufio for Unix. If the caller puts the terminal in character wakeup ("cbreak") mode with no echo, then these functions will provide line editing -- character, word, and line deletion, as well as keyword and filename completion upon ESC and help strings, keyword, or file menus upon '?'. If the caller puts the terminal into character wakeup/noecho mode, care should be taken to restore it before exit from or interruption of the program. If the character wakeup mode is not set, the system's own line editor may be used. NOTE: Contrary to expectations, many #ifdef's have been added to this module. Any operation requiring an #ifdef (like clear screen, get character from keyboard, erase character from screen, etc) should eventually be turned into a call to a function that is defined in ck?tio.c, but then all the ck?tio.c modules would have to be changed... */ /* Includes */ #include "ckcker.h" #include "ckcasc.h" /* ASCII character symbols */ #include "ckucmd.h" /* Command parsing definitions */ #ifdef OSF13 #ifdef CK_ANSIC #ifdef _NO_PROTO #undef _NO_PROTO #endif /* _NO_PROTO */ #endif /* CK_ANSIC */ #endif /* OSF13 */ #ifndef HPUXPRE65 #include /* Error number symbols */ #else #ifndef ERRNO_INCLUDED #include /* Error number symbols */ #endif /* ERRNO_INCLUDED */ #endif /* HPUXPRE65 */ #ifdef OS2 #ifndef NT #define INCL_NOPM #define INCL_VIO /* Needed for ckocon.h */ #include #undef COMMENT #else #define APIRET ULONG #include #endif /* NT */ #include "ckocon.h" #include #endif /* OS2 */ #ifdef OSK #define cc ccount /* OS-9/68K compiler bug */ #endif /* OSK */ #ifdef GEMDOS /* Atari ST */ #ifdef putchar #undef putchar #endif /* putchar */ #define putchar(x) conoc(x) #endif /* GEMDOS */ #ifdef CK_AUTODL extern int cmdadl, justone; #endif /* CK_AUTODL */ extern int timelimit, nzxopts, nopush, nolocal, xcmdsrc, keepallchars; #ifdef CKSYSLOG #ifdef UNIX #ifdef CKXPRINTF /* Our printf macro conflicts with */ #undef printf /* use of "printf" in syslog.h */ #endif /* CKXPRINTF */ #ifdef RTAIX #include #else /* RTAIX */ #include #endif /* RTAIX */ #ifdef CKXPRINTF #define printf ckxprintf #endif /* CKXPRINTF */ #endif /* UNIX */ #endif /* CKSYSLOG */ /* Local variables */ static int psetf = 0, /* Flag that prompt has been set */ cc = 0, /* Character count */ dpx = 0, /* Duplex (0 = full) */ inword = 0; /* In the middle of getting a word */ char *dfprom = "Command? "; /* Default prompt */ #ifndef NOLASTFILE char *lastfile = NULL; /* Last filespec */ static char *tmplastfile = NULL; /* Last filespec candidate */ #endif /* NOLASTFILE */ int cmflgs; /* Command flags */ int cmfsav; /* A saved version of them */ static char pushc = NUL; static char brkchar = NUL; #define CMDEFAULT 1023 static char cmdefault[CMDEFAULT+1]; #ifdef DCMDBUF char *cmdbuf = NULL; /* Command buffer */ char *savbuf = NULL; /* Buffer to save copy of command */ char *atmbuf = NULL; /* Atom buffer - for current field */ char *atxbuf = NULL; /* For expanding the atom buffer */ char *prevcmd = NULL; static char *atybuf = NULL; /* For copying atom buffer */ static char *filbuf = NULL; /* File name buffer */ static char *cmprom = NULL; /* Program's prompt */ static char *cmprxx = NULL; /* Program's prompt, unevaluated */ #ifdef CK_RECALL /* Command recall is available only if we can make profligate use of malloc(). */ #define R_MAX 10 /* How many commands to save */ int cm_recall = R_MAX; /* Size of command recall buffer */ int on_recall = 1; /* Recall feature is ON */ static int no_recall = 0; /* Recall OFF for this cmd only */ static int force_add = 0; /* Force cmd into recall buffer */ static int last_recall = -1; /* Last recall-related action */ /* -1 = none 0 = CR (a command was entered) 1 = Up 2 = Down */ int in_recall = 0; /* Recall buffers are init'd */ static int current = -1, /* Pointer to current command */ rlast = -1; /* Index of last command in buffer */ static char **recall = NULL; /* Array of recall buffer pointers */ #endif /* CK_RECALL */ #else /* !DCMDBUF */ char cmdbuf[CMDBL+4]; /* Command buffer */ char savbuf[CMDBL+4]; /* Buffer to save copy of command */ char atmbuf[ATMBL+4]; /* Atom buffer */ char atxbuf[CMDBL+4]; /* For expanding the atom buffer */ char prevcmd[CMDBL+4]; /* For displaying the last command */ static char atybuf[ATMBL+4]; /* For copying atom buffer */ static char filbuf[ATMBL+4]; /* File name buffer */ static char cmprom[PROMPTL+1]; /* Program's prompt */ static char cmprxx[PROMPTL+1]; /* Program's prompt, unevaluated */ #endif /* DCMDBUF */ /* Command buffer pointers */ #define PPVLEN VNAML /* 20080305 Wolfram Sang (was 24) */ char ppvnambuf[PPVLEN+1] = { NUL, NUL }; char * cmbptr = NULL; /* Current position (for export) */ static char *bp, /* Current command buffer position */ *pp, /* Start of current field */ *np; /* Start of next field */ static int ungw, /* For ungetting words */ atxn; /* Expansion buffer (atxbuf) length */ #ifdef OS2 extern int wideresult; #endif /* OS2 */ extern int cmd_cols, cmd_rows, local, quiet; #ifdef TNCODE #ifdef IAC #undef IAC #endif /* IAC */ #define IAC 255 #endif /* TNCODE */ _PROTOTYP( static int gtword, (int) ); _PROTOTYP( static int addbuf, (char *) ); _PROTOTYP( static int setatm, (char *, int) ); _PROTOTYP( static VOID cmdnewl, (char) ); _PROTOTYP( static VOID cmdchardel, (void) ); _PROTOTYP( static VOID cmdecho, (char, int) ); _PROTOTYP( static int test, (int, int) ); #ifdef GEMDOS _PROTOTYP( extern char *strchr, (char *, int) ); #endif /* GEMDOS */ extern char * dftty; /* The following are for use with chained FDB's */ static int crflag = 0; /* Carriage return was typed */ static int qmflag = 0; /* Question mark was typed */ static int esflag = 0; /* Escape was typed */ /* Directory separator */ #ifdef GEMDOS static char dirsep = '\\'; #else #ifdef datageneral static char dirsep = ':'; #else #ifdef MAC static char dirsep = ':'; #else #ifdef VMS static char dirsep = '.'; #else #ifdef STRATUS static char dirsep = '>'; #else static char dirsep = '/'; /* UNIX, OS/2, OS-9, Amiga, etc. */ #endif /* STRATUS */ #endif /* VMS */ #endif /* MAC */ #endif /* datageneral */ #endif /* GEMDOS */ /* H A S N O P A T H */ /* Returns 0 if filespec s includes any path segments; 1 if it doesn't. */ int hasnopath(s) char * s; { char * p = NULL; if (!s) return(0); if (!*s) return(0); zstrip(s,&p); return(ckstrcmp(s,p,CKMAXPATH,filecase) == 0 ? 1 : 0); } /* C K S P R E A D -- Print string double-spaced */ static char * sprptr = NULL; static char * ckspread(s) char * s; { int n = 0; char * p; n = strlen(s); if (sprptr) free(sprptr); sprptr = malloc(n + n + 3); if (sprptr) { p = sprptr; while (*s) { *p++ = *s++; *p++ = SP; } *p = NUL; } return(sprptr ? sprptr : ""); } /* T E S T -- Bit test */ static int test(x,m) int x, m; { /* Returns 1 if any bits from m are on in x, else 0 */ return((x & m) ? 1 : 0); } /* K W D H E L P -- Given a keyword table, print keywords in columns. */ /* Call with: s - keyword table n - number of entries pat - pattern (left substring) that must match for each keyword pre - prefix to add to each keyword post - suffix to add to each keyword off - offset on first screenful, allowing room for introductory text xhlp - 1 to print any CM_INV keywords that are not also abbreviations. 2 to print CM_INV keywords if CM_HLP also set 4 if it's a switch table (to show ':' if CM_ARG) Arranges keywords in columns with width based on longest keyword. Does "more?" prompting at end of screen. Uses global cmd_rows and cmd_cols for screen size. */ VOID kwdhelp(s,n,pat,pre,post,off,xhlp) struct keytab s[]; int n, off, xhlp; char *pat, *pre, *post; /* kwdhelp */ { int width = 0; int cc; int cols, height, i, j, k, lc, n2 = 0; char *b = NULL, *p, *q; char *pa, *px; char **s2 = NULL; char *tmpbuf = NULL; cc = strlen(pat); if (!s) return; /* Nothing to do */ if (n < 1) return; /* Ditto */ if (off < 0) off = 0; /* Offset for first page */ if (!pre) pre = ""; /* Handle null string pointers */ if (!post) post = ""; lc = off; /* Screen-line counter */ if (xhlp & 4) /* For switches */ tmpbuf = (char *)malloc(TMPBUFSIZ+1); if ((s2 = (char **) malloc(n * sizeof(char *)))) { for (i = 0; i < n; i++) { /* Find longest keyword */ s2[i] = NULL; if (ckstrcmp(s[i].kwd,pat,cc,0)) continue; if (s[i].flgs & CM_PSH /* NOPUSH or nopush screening */ #ifndef NOPUSH && nopush #endif /* NOPUSH */ ) continue; if (s[i].flgs & CM_LOC /* NOLOCAL or nolocal screening */ #ifndef NOLOCAL && nolocal #endif /* NOLOCAL */ ) continue; if (s[i].flgs & CM_INV) { #ifdef COMMENT /* This code does not show invisible keywords at all except for "help ?" */ /* and then only help topics (CM_HLP) in the top-level keyword list. */ if ((xhlp & 2) == 0) continue; else if ((s[i].flgs & CM_HLP) == 0) continue; #else /* This code shows invisible keywords that are not also abbreviations when */ /* ? was typed AFTER the beginning of the field so the user can find out */ /* what they are and (for example) why completion doesn't work at this point */ if (s[i].flgs & CM_ABR) continue; else if ((xhlp & 3) == 0) continue; else if ((xhlp & 2) && ((s[i].flgs & CM_HLP) == 0)) continue; #endif /* COMMENT */ } j = strlen(s[i].kwd); if (!(xhlp & 4) || !tmpbuf) { /* Regular keyword table */ s2[n2++] = s[i].kwd; /* Copy pointers to visible ones */ } else { /* Switches */ ckmakmsg(tmpbuf, /* Make a copy that shows ":" if */ TMPBUFSIZ, /* the switch takes an argument. */ s[i].kwd, (s[i].flgs & CM_ARG) ? ":" : "", NULL, NULL ); makestr(&(s2[n2]),tmpbuf); if (s[i].flgs & CM_ARG) j++; n2++; } if (j > width) width = j; } /* Column width */ n = n2; } if (s2 && (b = (char *) malloc(cmd_cols + 1))) { /* Make a line buffer */ char * bx; bx = b + cmd_cols; width += (int)strlen(pre) + (int)strlen(post) + 2; cols = cmd_cols / width; /* How many columns? */ if (cols < 1) cols = 1; height = n / cols; /* How long is each column? */ if (n % cols) height++; /* Add one for remainder, if any */ for (i = 0; i < height; i++) { /* Loop for each row */ for (j = 0; j < cmd_cols; j++) /* First fill row with blanks */ b[j] = SP; for (j = 0; j < cols; j++) { /* Loop for each column in row */ k = i + (j * height); /* Index of next keyword */ if (k < n) { /* In range? */ pa = pre; px = post; p = s2[k]; /* Point to verb name */ q = b + (j * width) + 1; /* Where to copy it to */ while ((q < bx) && (*q++ = *pa++)) ; /* Copy prefix */ q--; /* Back up over NUL */ while ((q < bx) && (*q++ = *p++)) ; /* Copy filename */ q--; /* Back up over NUL */ while ((q < bx) && (*q++ = *px++)) ; /* Copy suffix */ if (j < cols - 1) { q--; *q = SP; /* Replace the space */ } } } p = b + cmd_cols - 1; /* Last char in line */ while (*p-- == SP) ; /* Trim */ *(p+2) = NUL; printf("%s\n",b); /* Print the line */ if (++lc > (cmd_rows - 2)) { /* Screen full? */ if (!askmore()) /* Do more-prompting... */ goto xkwdhelp; else lc = 0; } } /* printf("\n"); */ /* Blank line at end of report */ } else { /* Malloc failure, no columns */ for (i = 0; i < n; i++) { if (s[i].flgs & CM_INV) /* Use original keyword table */ continue; /* skipping invisible entries */ printf("%s%s%s\n",pre,s[i].kwd,post); if (++lc > (cmd_rows - 2)) { /* Screen full? */ if (!askmore()) /* Do more-prompting... */ goto xkwdhelp; else lc = 0; } } } xkwdhelp: if (xhlp & 4) { if (tmpbuf) free((char *)tmpbuf); for (i = 0; i < n; i++) if (s2[i]) free(s2[i]); } if (s2) free(s2); /* Free array copy */ if (b) free(b); /* Free line buffer */ return; } /* X F I L H E L P -- Given a file list, print names in columns. */ /* Call with: n - number of entries pre - prefix to add to each filename post - suffix to add to each filename off - offset on first screenful, allowing room for introductory text cmdirflg - 1 if only directory names should be listed, 0 to list all files fs - call fileselect() to decide whether to include each file. The rest of the args are the same as for fileselect(). Arranges filenames in columns with width based on longest filename. Does "more?" prompting at end of screen. Uses global cmd_rows and cmd_cols for screen size. */ int #ifdef CK_ANSIC xfilhelp( int n, char *pre, char *post, int off, int cmdirflag, int fs, char *sa, char *sb, char *sna, char *snb, CK_OFF_T minsiz, CK_OFF_T maxsiz, int nbu, int nxlist, char ** xlist ) #else xfilhelp(n,pre,post,off,cmdirflg, fs,sa,sb,sna,snb,minsiz,maxsiz,nbu,nxlist,xlist) int n, off; char *pre, *post; int cmdirflg; int fs; char *sa,*sb,*sna,*snb; CK_OFF_T minsiz,maxsiz; int nbu,nxlist; char ** xlist; #endif /* CK_ANSIC */ { char filbuf[CKMAXPATH + 1]; /* Temp buffer for one filename */ int width = 0; int cols, height, i, j, k, lc, n2 = 0, rc = 0, itsadir = 0; char *b = NULL, *p, *q; char *pa, *px; char **s2 = NULL; #ifdef VMS char * cdp = zgtdir(); #endif /* VMS */ if (n < 1) return(0); if (off < 0) off = 0; /* Offset for first page */ if (!pre) pre = ""; /* Handle null string pointers */ if (!post) post = ""; lc = off; /* Screen-line counter */ if ((s2 = (char **) malloc(n * sizeof(char *)))) { for (i = 0; i < n; i++) { /* Loop through filenames */ itsadir = 0; s2[i] = NULL; /* Initialize each pointer to NULL */ znext(filbuf); /* Get next filename */ if (!filbuf[0]) /* Shouldn't happen */ break; #ifdef COMMENT itsadir = isdir(filbuf); /* Is it a directory? */ if (cmdirflg && !itsadir) /* No, listing directories only? */ continue; /* So skip this one. */ #endif /* COMMENT */ if (fs) if (fileselect(filbuf, sa,sb,sna,snb, minsiz,maxsiz,nbu,nxlist,xlist) < 1) { continue; } #ifdef VMS ckstrncpy(filbuf,zrelname(filbuf,cdp),CKMAXPATH); #endif /* VMS */ j = strlen(filbuf); #ifndef VMS if (itsadir && j < CKMAXPATH - 1 && j > 0) { if (filbuf[j-1] != dirsep) { filbuf[j++] = dirsep; filbuf[j] = NUL; } } #endif /* VMS */ if (!(s2[n2] = malloc(j+1))) { printf("?Memory allocation failure\n"); rc = -9; goto xfilhelp; } if (j <= CKMAXPATH) { strcpy(s2[n2],filbuf); n2++; } else { printf("?Name too long - %s\n", filbuf); rc = -9; goto xfilhelp; } if (j > width) /* Get width of widest one */ width = j; } n = n2; /* How many we actually got */ } sh_sort(s2,NULL,n,0,0,filecase); /* Alphabetize the list */ rc = 1; if (s2 && (b = (char *) malloc(cmd_cols + 1))) { /* Make a line buffer */ char * bx; bx = b + cmd_cols; width += (int)strlen(pre) + (int)strlen(post) + 2; cols = cmd_cols / width; /* How many columns? */ if (cols < 1) cols = 1; height = n / cols; /* How long is each column? */ if (n % cols) height++; /* Add one for remainder, if any */ for (i = 0; i < height; i++) { /* Loop for each row */ for (j = 0; j < cmd_cols; j++) /* First fill row with blanks */ b[j] = SP; for (j = 0; j < cols; j++) { /* Loop for each column in row */ k = i + (j * height); /* Index of next filename */ if (k < n) { /* In range? */ pa = pre; px = post; p = s2[k]; /* Point to filename */ q = b + (j * width) + 1; /* and destination */ while ((q < bx) && (*q++ = *pa++)) ; /* Copy prefix */ q--; /* Back up over NUL */ while ((q < bx) && (*q++ = *p++)) ; /* Copy filename */ q--; /* Back up over NUL */ while ((q < bx) && (*q++ = *px++)) ; /* Copy suffix */ if (j < cols - 1) { q--; *q = SP; /* Replace the space */ } } } p = b + cmd_cols - 1; /* Last char in line */ while (*p-- == SP) ; /* Trim */ *(p+2) = NUL; printf("%s\n",b); /* Print the line */ if (++lc > (cmd_rows - 2)) { /* Screen full? */ if (!askmore()) { /* Do more-prompting... */ rc = 0; goto xfilhelp; } else lc = 0; } } printf("\n"); /* Blank line at end of report */ goto xfilhelp; } else { /* Malloc failure, no columns */ for (i = 0; i < n; i++) { znext(filbuf); if (!filbuf[0]) break; printf("%s%s%s\n",pre,filbuf,post); if (++lc > (cmd_rows - 2)) { /* Screen full? */ if (!askmore()) { /* Do more-prompting... */ rc = 0; goto xfilhelp; } else lc = 0; } } xfilhelp: if (b) free(b); for (i = 0; i < n2; i++) if (s2[i]) free(s2[i]); if (s2) free((char *)s2); return(rc); } } /* Simpler front end for xfilhelp() with shorter arg list when no file selection is needed. */ int filhelp(n,pre,post,off,cmdirflg) int n, off; char *pre, *post; int cmdirflg; { return(xfilhelp(n,pre,post,off,cmdirflg, 0,NULL,NULL,NULL,NULL, (CK_OFF_T)0,(CK_OFF_T)0,0,0,(char **)NULL)); } /* C M S E T U P -- Set up command buffers */ #ifdef DCMDBUF int cmsetup() { if (!(cmdbuf = malloc(CMDBL + 4))) return(-1); if (!(savbuf = malloc(CMDBL + 4))) return(-1); savbuf[0] = '\0'; if (!(prevcmd = malloc(CMDBL + 4))) return(-1); prevcmd[0] = '\0'; if (!(atmbuf = malloc(ATMBL + 4))) return(-1); if (!(atxbuf = malloc(CMDBL + 4))) return(-1); if (!(atybuf = malloc(ATMBL + 4))) return(-1); if (!(filbuf = malloc(ATMBL + 4))) return(-1); if (!(cmprom = malloc(PROMPTL + 4))) return(-1); if (!(cmprxx = malloc(PROMPTL + 4))) return(-1); #ifdef CK_RECALL cmrini(cm_recall); #endif /* CK_RECALL */ return(0); } #endif /* DCMDBUF */ /* C M S E T P -- Set the program prompt. */ VOID cmsetp(s) char *s; { if (!s) s = ""; ckstrncpy(cmprxx,s,PROMPTL); psetf = 1; /* Flag that prompt has been set. */ } /* C M S A V P -- Save a copy of the current prompt. */ VOID #ifdef CK_ANSIC cmsavp(char s[], int n) #else cmsavp(s,n) char s[]; int n; #endif /* CK_ANSIC */ /* cmsavp */ { if (psetf) /* But not if no prompt is set. */ ckstrncpy(s,cmprxx,n); } char * cmgetp() { return(cmprxx); } int cmgbrk() { return(brkchar); } int cmgkwflgs() { return(cmkwflgs); } /* P R O M P T -- Issue the program prompt. */ VOID prompt(f) xx_strp f; { char *sx, *sy; int n; #ifdef CK_SSL extern int ssl_active_flag, tls_active_flag; #endif /* CK_SSL */ #ifdef OS2 extern int display_demo; /* If there is a demo screen to be displayed, display it */ if (display_demo && xcmdsrc == 0) { demoscrn(VCMD); display_demo = 0; } #endif /* OS2 */ if (psetf == 0) /* If no prompt set, set default. */ cmsetp(dfprom); sx = cmprxx; /* Unevaluated copy */ if (f) { /* If conversion function given */ sy = cmprom; /* Evaluate it */ #ifdef COMMENT debug(F101,"prompt sx","",sx); debug(F101,"prompt sy","",sy); #endif /* COMMENT */ n = PROMPTL; if ((*f)(sx,&sy,&n) < 0) /* If evaluation failed */ sx = cmprxx; /* revert to unevaluated copy */ else if (!*cmprom) /* ditto if it came up empty */ sx = cmprxx; else sx = cmprom; } else ckstrncpy(cmprom,sx,PROMPTL); cmprom[PROMPTL-1] = NUL; if (!*sx) /* Don't print if empty */ return; #ifdef OSK fputs(sx, stdout); #else #ifdef MAC printf("%s", sx); #else #ifdef IKSD if (inserver) { /* Print the prompt. */ ttoc(CR); /* If TELNET Server */ ttoc(NUL); /* must folloW CR by NUL */ printf("%s",sx); } else #endif /* IKSD */ printf("\r%s",sx); #ifdef CK_SSL if (!(ssl_active_flag || tls_active_flag)) #endif /* CK_SSL */ fflush(stdout); /* Now! */ #endif /* MAC */ #endif /* OSK */ } #ifndef NOSPL VOID pushcmd(s) char * s; { /* For use with IF command. */ if (!s) s = np; ckstrncpy(savbuf,s,CMDBL); /* Save the dependent clause, */ cmres(); /* and clear the command buffer. */ debug(F110, "pushcmd savbuf", savbuf, 0); } VOID pushqcmd(s) char * s; { /* For use with ELSE command. */ char c, * p = savbuf; /* Dest */ if (!s) s = np; /* Source */ while (*s) { /* Get first nonwhitespace char */ if (*s != SP) break; else s++; } if (*s != '{') { /* If it's not "{" */ pushcmd(s); /* do regular pushcmd */ return; } while ((c = *s++)) { /* Otherwise insert quotes */ if (c == CMDQ) *p++ = CMDQ; *p++ = c; } cmres(); /* and clear the command buffer. */ debug(F110, "pushqcmd savbuf", savbuf, 0); } #endif /* NOSPL */ #ifdef COMMENT /* no longer used... */ VOID popcmd() { ckstrncpy(cmdbuf,savbuf,CMDBL); /* Put back the saved material */ *savbuf = '\0'; /* and clear the save buffer */ cmres(); } #endif /* COMMENT */ /* C M R E S -- Reset pointers to beginning of command buffer. */ VOID cmres() { inword = 0; /* We're not in a word */ cc = 0; /* Character count is zero */ /* Initialize pointers */ pp = cmdbuf; /* Beginning of current field */ bp = cmdbuf; /* Current position within buffer */ np = cmdbuf; /* Where to start next field */ cmfldflgs = 0; cmflgs = -5; /* Parse not yet started. */ ungw = 0; /* Don't need to unget a word. */ } /* C M I N I -- Clear the command and atom buffers, reset pointers. */ /* The argument specifies who is to echo the user's typein -- 1 means the cmd package echoes 0 somebody else (system, front end, terminal) echoes */ VOID cmini(d) int d; { #ifdef DCMDBUF if (!atmbuf) if (cmsetup()<0) fatal("fatal error: unable to allocate command buffers"); #endif /* DCMDBUF */ #ifdef USE_MEMCPY memset(cmdbuf,0,CMDBL); memset(atmbuf,0,ATMBL); #else for (bp = cmdbuf; bp < cmdbuf+CMDBL; bp++) *bp = NUL; for (bp = atmbuf; bp < atmbuf+ATMBL; bp++) *bp = NUL; #endif /* USE_MEMCPY */ *atmbuf = *savbuf = *atxbuf = *atybuf = *filbuf = NUL; blocklvl = 0; /* Block level is 0 */ linebegin = 1; /* At the beginning of a line */ dpx = d; /* Global copy of the echo flag */ debug(F101,"cmini dpx","",dpx); crflag = 0; /* Reset flags */ qmflag = 0; esflag = 0; #ifdef CK_RECALL no_recall = 0; /* Start out with recall enabled */ #endif /* CK_RECALL */ cmres(); /* Sets bp etc */ newcmd = 1; /* See addcmd() */ } #ifndef NOSPL /* The following bits are to allow the command package to call itself in the middle of a parse. To do this, begin by calling cmpush, and end by calling cmpop. As you can see, this is rather expensive. */ #ifdef DCMDBUF struct cmp { int i[5]; /* stack for integers */ char *c[3]; /* stack for pointers */ char *b[8]; /* stack for buffer contents */ }; struct cmp *cmp = 0; #else int cmp_i[CMDDEP+1][5]; /* Stack for integers */ char *cmp_c[CMDDEP+1][5]; /* for misc pointers */ char *cmp_b[CMDDEP+1][7]; /* for buffer contents pointers */ #endif /* DCMDBUF */ int cmddep = -1; /* Current stack depth */ int cmpush() { /* Save the command environment */ char *cp; /* Character pointer */ if (cmddep >= CMDDEP) /* Enter a new command depth */ return(-1); cmddep++; debug(F101,"&cmpush to depth","",cmddep); #ifdef DCMDBUF /* allocate memory for cmp if not already done */ if (!cmp && !(cmp = (struct cmp *) malloc(sizeof(struct cmp)*(CMDDEP+1)))) fatal("cmpush: no memory for cmp"); cmp[cmddep].i[0] = cmflgs; /* First do the global ints */ cmp[cmddep].i[1] = cmfsav; cmp[cmddep].i[2] = atxn; cmp[cmddep].i[3] = ungw; cmp[cmddep].c[0] = bp; /* Then the global pointers */ cmp[cmddep].c[1] = pp; cmp[cmddep].c[2] = np; #else cmp_i[cmddep][0] = cmflgs; /* First do the global ints */ cmp_i[cmddep][1] = cmfsav; cmp_i[cmddep][2] = atxn; cmp_i[cmddep][3] = ungw; cmp_c[cmddep][0] = bp; /* Then the global pointers */ cmp_c[cmddep][1] = pp; cmp_c[cmddep][2] = np; #endif /* DCMDBUF */ /* Now the buffers themselves. A lot of repititious code... */ #ifdef DCMDBUF cp = malloc((int)strlen(cmdbuf)+1); /* 0: Command buffer */ if (cp) strcpy(cp,cmdbuf); cmp[cmddep].b[0] = cp; if (cp == NULL) return(-1); cp = malloc((int)strlen(savbuf)+1); /* 1: Save buffer */ if (cp) strcpy(cp,savbuf); cmp[cmddep].b[1] = cp; if (cp == NULL) return(-1); cmp[cmddep].b[2] = NULL; cp = malloc((int)strlen(atmbuf)+1); /* 3: Atom buffer */ if (cp) strcpy(cp,atmbuf); cmp[cmddep].b[3] = cp; if (cp == NULL) return(-1); cp = malloc((int)strlen(atxbuf)+1); /* 4: Expansion buffer */ if (cp) strcpy(cp,atxbuf); cmp[cmddep].b[4] = cp; if (cp == NULL) return(-1); cp = malloc((int)strlen(atybuf)+1); /* 5: Atom buffer copy */ if (cp) strcpy(cp,atybuf); cmp[cmddep].b[5] = cp; if (cp == NULL) return(-1); cp = malloc((int)strlen(filbuf)+1); /* 6: File name buffer */ if (cp) strcpy(cp,filbuf); cmp[cmddep].b[6] = cp; if (cp == NULL) return(-1); #else cp = malloc((int)strlen(cmdbuf)+1); /* 0: Command buffer */ if (cp) strcpy(cp,cmdbuf); cmp_b[cmddep][0] = cp; if (cp == NULL) return(-1); cp = malloc((int)strlen(savbuf)+1); /* 1: Save buffer */ if (cp) strcpy(cp,savbuf); cmp_b[cmddep][1] = cp; if (cp == NULL) return(-1); cmp_b[cmddep][2] = NULL; cp = malloc((int)strlen(atmbuf)+1); /* 3: Atom buffer */ if (cp) strcpy(cp,atmbuf); cmp_b[cmddep][3] = cp; if (cp == NULL) return(-1); cp = malloc((int)strlen(atxbuf)+1); /* 4: Expansion buffer */ if (cp) strcpy(cp,atxbuf); cmp_b[cmddep][4] = cp; if (cp == NULL) return(-1); cp = malloc((int)strlen(atybuf)+1); /* 5: Atom buffer copy */ if (cp) strcpy(cp,atybuf); cmp_b[cmddep][5] = cp; if (cp == NULL) return(-1); cp = malloc((int)strlen(filbuf)+1); /* 6: File name buffer */ if (cp) strcpy(cp,filbuf); cmp_b[cmddep][6] = cp; if (cp == NULL) return(-1); #endif /* DCMDBUF */ cmini(dpx); /* Initize the command parser */ return(0); } int cmpop() { /* Restore the command environment */ if (cmddep < 0) { debug(F100,"&cmpop called from top level","",0); return(-1); /* Don't pop too much! */ } #ifdef DCMDBUF cmflgs = cmp[cmddep].i[0]; /* First do the global ints */ cmfsav = cmp[cmddep].i[1]; atxn = cmp[cmddep].i[2]; ungw = cmp[cmddep].i[3]; bp = cmp[cmddep].c[0]; /* Then the global pointers */ pp = cmp[cmddep].c[1]; np = cmp[cmddep].c[2]; #else cmflgs = cmp_i[cmddep][0]; /* First do the global ints */ cmfsav = cmp_i[cmddep][1]; atxn = cmp_i[cmddep][2]; ungw = cmp_i[cmddep][3]; bp = cmp_c[cmddep][0]; /* Then the global pointers */ pp = cmp_c[cmddep][1]; np = cmp_c[cmddep][2]; #endif /* DCMDBUF */ /* Now the buffers themselves. */ /* Note: strncpy(), not ckstrncpy() -- Here we WANT the NUL padding... */ #ifdef DCMDBUF if (cmp[cmddep].b[0]) { strncpy(cmdbuf,cmp[cmddep].b[0],CMDBL); /* 0: Command buffer */ free(cmp[cmddep].b[0]); cmp[cmddep].b[0] = NULL; } if (cmp[cmddep].b[1]) { strncpy(savbuf,cmp[cmddep].b[1],CMDBL); /* 1: Save buffer */ free(cmp[cmddep].b[1]); cmp[cmddep].b[1] = NULL; } if (cmp[cmddep].b[3]) { strncpy(atmbuf,cmp[cmddep].b[3],ATMBL); /* 3: Atomic buffer! */ free(cmp[cmddep].b[3]); cmp[cmddep].b[3] = NULL; } if (cmp[cmddep].b[4]) { strncpy(atxbuf,cmp[cmddep].b[4],ATMBL); /* 4: eXpansion buffer */ free(cmp[cmddep].b[4]); cmp[cmddep].b[4] = NULL; } if (cmp[cmddep].b[5]) { strncpy(atybuf,cmp[cmddep].b[5],ATMBL); /* 5: Atom buffer copY */ free(cmp[cmddep].b[5]); cmp[cmddep].b[5] = NULL; } if (cmp[cmddep].b[6]) { strncpy(filbuf,cmp[cmddep].b[6],ATMBL); /* 6: Filename buffer */ free(cmp[cmddep].b[6]); cmp[cmddep].b[6] = NULL; } #else if (cmp_b[cmddep][0]) { strncpy(cmdbuf,cmp_b[cmddep][0],CMDBL); /* 0: Command buffer */ free(cmp_b[cmddep][0]); cmp_b[cmddep][0] = NULL; } if (cmp_b[cmddep][1]) { strncpy(savbuf,cmp_b[cmddep][1],CMDBL); /* 1: Save buffer */ free(cmp_b[cmddep][1]); cmp_b[cmddep][1] = NULL; } if (cmp_b[cmddep][3]) { strncpy(atmbuf,cmp_b[cmddep][3],ATMBL); /* 3: Atomic buffer! */ free(cmp_b[cmddep][3]); cmp_b[cmddep][3] = NULL; } if (cmp_b[cmddep][4]) { strncpy(atxbuf,cmp_b[cmddep][4],ATMBL); /* 4: eXpansion buffer */ free(cmp_b[cmddep][4]); cmp_b[cmddep][4] = NULL; } if (cmp_b[cmddep][5]) { strncpy(atybuf,cmp_b[cmddep][5],ATMBL); /* 5: Atom buffer copY */ free(cmp_b[cmddep][5]); cmp_b[cmddep][5] = NULL; } if (cmp_b[cmddep][6]) { strncpy(filbuf,cmp_b[cmddep][6],ATMBL); /* 6: Filename buffer */ free(cmp_b[cmddep][6]); cmp_b[cmddep][6] = NULL; } #endif /* DCMDBUF */ cmddep--; /* Rise, rise */ debug(F101,"&cmpop to depth","",cmddep); return(cmddep); } #endif /* NOSPL */ #ifdef COMMENT VOID /* Not used */ stripq(s) char *s; { /* Function to strip '\' quotes */ char *t; while (*s) { if (*s == CMDQ) { for (t = s; *t != '\0'; t++) *t = *(t+1); } s++; } } #endif /* COMMENT */ /* Convert tabs to spaces, one for one */ VOID untab(s) char *s; { while (*s) { if (*s == HT) *s = SP; s++; } } /* C M N U M -- Parse a number in the indicated radix */ /* The radix is specified in the arg list. Parses unquoted numeric strings in the given radix. Parses backslash-quoted numbers in the radix indicated by the quote: \nnn = \dnnn = decimal, \onnn = octal, \xnn = Hexadecimal. If these fail, then if a preprocessing function is supplied, that is applied and then a second attempt is made to parse an unquoted decimal string. And if that fails, the preprocessed string is passed to an arithmetic expression evaluator. Returns: -3 if no input present when required, -2 if user typed an illegal number, -1 if reparse needed, 0 otherwise, with argument n set to the number that was parsed */ /* This is the traditional cmnum() that gets an int */ int cmnum(xhlp,xdef,radix,n,f) char *xhlp, *xdef; int radix, *n; xx_strp f; { CK_OFF_T z = (CK_OFF_T)0, check; int x; x = cmnumw(xhlp,xdef,radix,&z,f); *n = z; check = *n; if (check != z) { printf("?Magnitude of result too large for integer - %s\n",ckfstoa(z)); return(-9); } return(x); } /* This is the new cmnum() that gets a "wide" result, whatever CK_OFF_T is defined to be, normally 32 or 64 bits, depending on the platform. fdc, 24 Dec 2005. */ int cmnumw(xhlp,xdef,radix,n,f) char *xhlp, *xdef; int radix; CK_OFF_T *n; xx_strp f; { int x; char *s, *zp, *zq; #ifdef COMMENT char lbrace, rbrace; #endif /* COMMENT */ if (!xhlp) xhlp = ""; if (!xdef) xdef = ""; #ifdef COMMENT if (cmfldflgs & 1) { lbrace = '('; rbrace = ')'; } else { lbrace = '{'; rbrace = '}'; } #endif /* COMMENT */ if (radix != 10 && radix != 8) { /* Just do bases 8 and 10 */ printf("cmnum: illegal radix - %d\n",radix); return(-2); } /* Easy to add others but there has never been a need for it. */ x = cmfld(xhlp,xdef,&s,(xx_strp)0); debug(F101,"cmnum: cmfld","",x); if (x < 0) return(x); /* Parse a field */ zp = atmbuf; /* Edit 192 - Allow any number field to be braced. This lets us include spaces in expressions, but perhaps more important lets us have user-defined functions in numeric fields. */ zp = brstrip(zp); /* Strip braces */ if (cmfldflgs & 1 && *zp == '(') { /* Parens too.. */ x = (int) strlen(atmbuf); if (x > 0) { if (*(atmbuf+x-1) == ')') { *(atmbuf+x-1) = NUL; zp++; } } } if (chknum(zp)) { /* Check for number */ if (radix == 8) { /* If it's supposed to be octal */ zp = ckradix(zp,8,10); /* convert to decimal */ if (!zp) return(-2); if (!strcmp(zp,"-1")) return(-2); } errno = 0; /* Got one, we're done. */ *n = ckatofs(zp); if (errno) { perror(zp); return(-9); } debug(F101,"cmnum 1st chknum ok","",*n); return(0); } else if ((x = xxesc(&zp)) > -1) { /* Check for backslash escape */ #ifndef OS2 *n = x; #else *n = wideresult; #endif /* OS2 */ debug(F101,"cmnum xxesc ok","",*n); return(*zp ? -2 : 0); } else if (f) { /* If conversion function given */ zq = atxbuf; /* Try that */ atxn = CMDBL; if ((*f)(zp,&zq,&atxn) < 0) /* Convert */ return(-2); zp = atxbuf; } debug(F110,"cmnum zp 1",zp,0); if (!*zp) zp = xdef; /* Result empty, substitute default */ debug(F110,"cmnum zp 2",zp,0); if (chknum(zp)) { /* Check again for decimal number */ if (radix == 8) { /* If it's supposed to be octal */ zp = ckradix(zp,8,10); /* convert to decimal */ if (!zp) return(-2); if (!strcmp(zp,"-1")) return(-2); } errno = 0; *n = ckatofs(zp); if (errno) { perror(zp); return(-9); } debug(F101,"cmnum 2nd chknum ok","",*n); return(0); #ifndef NOSPL } else if ((x = xxesc(&zp)) > -1) { /* Check for backslash escape */ #ifndef OS2 *n = x; #else *n = wideresult; #endif /* OS2 */ debug(F101,"cmnum xxesc 2 ok","",*n); return(*zp ? -2 : 0); } else if (f) { /* Not numeric, maybe an expression */ char * p; p = evala(zp); if (chknum(p)) { if (radix == 8) { /* If it's supposed to be octal */ zp = ckradix(zp,8,10); /* convert to decimal */ if (!zp) return(-2); if (!strcmp(zp,"-1")) return(-2); } errno = 0; *n = ckatofs(p); if (errno) { perror(p); return(-9); } debug(F101,"cmnum exp eval ok","",*n); return(0); } else return(-2); #endif /* NOSPL */ } else { /* Not numeric */ return(-2); } } #ifdef CKCHANNELIO extern int z_error; #endif /* CKCHANNELIO */ /* C M O F I -- Parse the name of an output file */ /* Depends on the external function zchko(); if zchko() not available, use cmfld() to parse output file names. Returns: -9 like -2, except message already printed, -3 if no input present when required, -2 if permission would be denied to create the file, -1 if reparse needed, 0 or 1 if file can be created, with xp pointing to name. 2 if given the name of an existing directory. */ int cmofi(xhlp,xdef,xp,f) char *xhlp, *xdef, **xp; xx_strp f; { int x; char *s, *zq; #ifdef DOCHKVAR int tries; #endif /* DOCHKVAR */ #ifdef DTILDE char *dirp; #endif /* DTILDE */ cmfldflgs = 0; if (!xhlp) xhlp = ""; if (!xdef) xdef = ""; if (*xhlp == NUL) xhlp = "Output file"; *xp = ""; debug(F110,"cmofi xdef",xdef,0); x = cmfld(xhlp,xdef,&s,(xx_strp)0); debug(F111,"cmofi cmfld returns",s,x); if (x < 0) return(x); s = brstrip(s); /* Strip enclosing braces */ debug(F110,"cmofi 1.5",s,0); #ifdef DOCHKVAR tries = 0; { char *p = s; /* This is really ugly. If we skip conversion the first time through, then variable names like \%a will be used as filenames (e.g. creating a file called %A in the root directory). If we DON'T skip conversion the first time through, then single backslashes used as directory separators in filenames will be misinterpreted as variable lead-ins. So we prescan to see if it has any variable references. But this module is not supposed to know anything about variables, functions, etc, so this code does not really belong here, but rather it should be at the same level as zzstring(). */ /* Hmmm, this looks a lot like chkvar() except it that includes \nnn number escapes. But why? This makes commands like "mkdir c:\123" impossible. And in fact, "mkdir c:\123" creates a directory called "c:{". What's worse, rmdir(), which *does* call chkvar(), won't let us remove it. So let's at least try making cmofi() symmetrical with cmifi()... */ #ifdef COMMENT char * q; while ( (tries == 0) && (p = strchr(p,CMDQ)) ) { q = *(p+1); /* Char after backslash */ if (!q) /* None, quit */ break; if (isupper(q)) /* If letter, convert to lowercase */ q = tolower(q); if (isdigit(q)) { /* If it's a digit, */ tries = 1; /* assume it's a backslash code */ break; } switch (q) { case CMDQ: /* Double backslash */ tries = 1; /* so call the conversion function */ break; case '%': /* Variable or array reference */ case '&': /* must be followed by letter */ if (isalpha(*(p+2)) || (*(p+2) >= '0' && *(p+2) <= '9')) tries = 1; break; case 'm': case 'v': case '$': /* \m(), \v(), \$() */ if (*(p+2) == '(') if (strchr(p+2,')')) tries = 1; break; case 'f': /* \Fname() */ if (strchr(p+2,'(')) if (strchr(p+2,')')) tries = 1; break; case '{': /* \{...} */ if (strchr(p+2,'}')) tries = 1; break; case 'd': case 'o': /* Decimal or Octal number */ if (isdigit(*(p+2))) tries = 1; break; case 'x': /* Hex number */ if (isdigit(*(p+2)) || ((*(p+2) >= 'a' && *(p+2) <= 'f') || ((*(p+2) >= 'A' && *(p+2) <= 'F')))) tries = 1; default: break; } p++; } #else #ifndef NOSPL if (f) { /* If a conversion function is given */ char *s = p; /* See if there are any variables in */ while (*s) { /* the string and if so, expand them */ if (chkvar(s)) { tries = 1; break; } s++; } } #endif /* NOSPL */ #endif /* COMMENT */ } #ifdef OS2 o_again: #endif /* OS2 */ if (tries == 1) #endif /* DOCHKVAR */ if (f) { /* If a conversion function is given */ zq = atxbuf; /* do the conversion. */ atxn = CMDBL; if ((x = (*f)(s,&zq,&atxn)) < 0) return(-2); s = atxbuf; if (!*s) /* Result empty, substitute default */ s = xdef; } debug(F111,"cmofi 2",s,x); #ifdef DTILDE dirp = tilde_expand(s); /* Expand tilde, if any, */ if (*dirp != '\0') { /* right in the atom buffer. */ if (setatm(dirp,1) < 0) { printf("?Name too long\n"); return(-9); } } s = atmbuf; debug(F110,"cmofi 3",s,0); #endif /* DTILDE */ if (iswild(s)) { printf("?Wildcards not allowed - %s\n",s); return(-2); } debug(F110,"cmofi 4",s,0); #ifdef CK_TMPDIR /* isdir() function required for this! */ if (isdir(s)) { debug(F110,"cmofi 5: is directory",s,0); *xp = s; return(2); } #endif /* CK_TMPDIR */ if (strcmp(s,CTTNAM) && (zchko(s) < 0)) { /* OK to write to console */ #ifdef COMMENT #ifdef OS2 /* We don't try again because we already prescanned the string to see if if it contained anything that could be used by zzstring(). */ if (tries++ < 1) goto o_again; #endif /* OS2 */ #endif /* COMMENT */ /* Note: there are certain circumstances where zchko() can give a false positive, so don't rely on it to catch every conceivable situation in which the given output file can't be created. In other words, we print a message and fail here if we KNOW the file can't be created. If we succeed but the file can't be opened, the code that tries to open the file has to print a message. */ debug(F110,"cmofi 6: failure",s,0); #ifdef CKROOT if (ckrooterr) printf("?Off Limits: %s\n",s); else #endif /* CKROOT */ printf("?Write permission denied - %s\n",s); #ifdef CKCHANNELIO z_error = FX_ACC; #endif /* CKCHANNELIO */ return(-9); } else { debug(F110,"cmofi 7: ok",s,0); *xp = s; return(x); } } /* C M I F I -- Parse the name of an existing file */ /* This function depends on the external functions: zchki() - Check if input file exists and is readable. zxpand() - Expand a wild file specification into a list. znext() - Return next file name from list. If these functions aren't available, then use cmfld() to parse filenames. */ /* Returns -4 EOF -3 if no input present when required, -2 if file does not exist or is not readable, -1 if reparse needed, 0 or 1 otherwise, with: xp pointing to name, wild = 1 if name contains '*' or '?', 0 otherwise. */ #ifdef COMMENT /* This horrible hack has been replaced - see further down */ /* C M I O F I -- Parse an input file OR the name of a nonexistent file. Use this when an existing file is wanted (so we get help, completion, etc), but if a file of the given name does not exist, the name of a new file is accepted. For example, with the EDIT command (edit an existing file, or create a new file). Returns -9 if file does not exist. It is up to the caller to check creatability. */ static int nomsg = 0; int cmiofi(xhlp,xdef,xp,wild,f) char *xhlp, *xdef, **xp; int *wild; xx_strp f; { int msgsave, x; msgsave = nomsg; nomsg = 1; x = cmifi2(xhlp,xdef,xp,wild,0,NULL,f,0); nomsg = msgsave; return(x); } #endif /* COMMENT */ int cmifi(xhlp,xdef,xp,wild,f) char *xhlp, *xdef, **xp; int *wild; xx_strp f; { return(cmifi2(xhlp,xdef,xp,wild,0,NULL,f,0)); } /* cmifip() is called when we want to supply a path or path list to search in case the filename that the user gives is (a) not absolute, and (b) can't be found as given. The path string can be the name of a single directory, or a list of directories separated by the PATHSEP character, defined in ckucmd.h. Look in ckuusr.c and ckuus3.c for examples of usage. */ int cmifip(xhlp,xdef,xp,wild,d,path,f) char *xhlp,*xdef,**xp; int *wild, d; char * path; xx_strp f; { return(cmifi2(xhlp,xdef,xp,wild,0,path,f,0)); } /* C M D I R -- Parse a directory name */ /* This function depends on the external functions: isdir(s) - Check if string s is the name of a directory zchki(s) - Check if input file s exists and what type it is. If these functions aren't available, then use cmfld() to parse dir names. Returns -9 For all sorts of reasons, after printing appropriate error message. -4 EOF -3 if no input present when required, -2 if out of space or other internal error, -1 if reparse needed, 0 or 1, with xp pointing to name, if directory specified, */ int cmdir(xhlp,xdef,xp,f) char *xhlp, *xdef, **xp; xx_strp f; { int wild; return(cmifi2(xhlp,xdef,xp,&wild,0,NULL,f,1)); } /* Like CMDIR but includes PATH search */ int cmdirp(xhlp,xdef,xp,path,f) char *xhlp, *xdef, **xp; char * path; xx_strp f; { int wild; return(cmifi2(xhlp,xdef,xp,&wild,0,path,f,1)); } /* cmifi2() is the base filename parser called by cmifi, cmifip, cmdir, etc. Use it directly when you also want to parse a directory or device name as an input file, as in the DIRECTORY command. Call with: xhlp -- help message on ? xdef -- default response xp -- pointer to result (in our space, must be copied from here) wild -- flag set upon return to indicate if filespec was wild d -- 0 to parse files, 1 to parse files or directories Add 2 to inhibit following of symlinks. path -- search path for files f -- pointer to string processing function (e.g. to evaluate variables) dirflg -- 1 to parse *only* directories, 0 otherwise */ int cmifi2(xhlp,xdef,xp,wild,d,path,f,dirflg) char *xhlp,*xdef,**xp; int *wild, d; char * path; xx_strp f; int dirflg; { extern int recursive, diractive, cdactive, dblquo; int i, x, itsadir, xc, expanded = 0, nfiles = 0, children = -1; int qflag = 0; long y; CK_OFF_T filesize; char *sp = NULL, *zq, *np = NULL; char *sv = NULL, *p = NULL; #ifdef DTILDE char *dirp; #endif /* DTILDE */ #ifndef NOPARTIAL #ifndef OS2 #ifdef OSK /* This large array is dynamic for OS-9 -- should do for others too... */ extern char **mtchs; #else #ifdef UNIX /* OK, for UNIX too */ extern char **mtchs; #else #ifdef VMS extern char **mtchs; #else extern char *mtchs[]; #endif /* VMS */ #endif /* UNIX */ #endif /* OSK */ #endif /* OS2 */ #endif /* NOPARTIAL */ if (!xhlp) xhlp = ""; if (!xdef) xdef = ""; #ifndef NOLASTFILE makestr(&tmplastfile,NULL); #endif /* NOLASTFILE */ nzxopts = 0; /* zxpand() options */ debug(F101,"cmifi d","",d); if (d & 2) { /* d & 2 means don't follow symlinks */ d ^= 2; nzxopts = ZX_NOLINKS; } debug(F101,"cmifi nzxopts","",nzxopts); cmfldflgs = 0; if (path) if (!*path) path = NULL; if (path) { /* Make a copy we can poke */ x = strlen(path); np = (char *) malloc(x + 1); if (np) { strcpy(np, path); path = sp = np; } } debug(F110,"cmifi2 path",path,0); ckstrncpy(cmdefault,xdef,CMDEFAULT); /* Copy default */ xdef = cmdefault; inword = 0; /* Initialize counts & pointers */ cc = 0; xc = 0; *xp = ""; /* Pointer to result string */ if ((x = cmflgs) != 1) { /* Already confirmed? */ #ifdef BS_DIRSEP dirnamflg = 1; x = gtword(0); /* No, get a word */ dirnamflg = 0; #else x = gtword(0); /* No, get a word */ #endif /* BS_DIRSEP */ } else { /* If so, use default, if any. */ if (setatm(xdef,1) < 0) { printf("?Default name too long\n"); if (np) free(np); return(-9); } } i_path: *xp = atmbuf; /* Point to result. */ while (1) { xc += cc; /* Count this character. */ debug(F111,"cmifi gtword",atmbuf,xc); debug(F101,"cmifi switch x","",x); switch (x) { /* x = gtword() return code */ case -10: if (gtimer() > timelimit) { #ifdef IKSD if (inserver) { printf("\r\nIKSD IDLE TIMEOUT: %d sec\r\n", timelimit); doexit(GOOD_EXIT,0); } #endif /* IKSD */ /* if (!quiet) printf("?Timed out\n"); */ return(-10); } else { x = gtword(0); continue; } case -9: printf("Command or field too long\n"); case -4: /* EOF */ case -2: /* Out of space. */ case -1: /* Reparse needed */ if (np) free(np); return(x); case 1: /* CR */ case 0: /* SP */ if (xc == 0) /* If no input... */ *xp = xdef; /* substitute the default */ #ifndef NOLASTFILE makestr(&tmplastfile,*xp); /* Make a copy before bstripping */ #endif /* #ifndef NOLASTFILE */ *xp = brstrip(*xp); /* Strip braces */ if (**xp == NUL) { /* 12 mar 2001 */ if (np) free(np); return(-3); } debug(F110,"cmifi brstrip",*xp,0); #ifndef NOSPL if (f) { /* If a conversion function is given */ #ifdef DOCHKVAR char *s = *xp; /* See if there are any variables in */ int x; while (*s) { /* the string and if so, expand them */ x = chkvar(s); /* debug(F111,"cmifi chkvar",*xp,x); */ if (x) { #endif /* DOCHKVAR */ zq = atxbuf; atxn = CMDBL; if ((*f)(*xp,&zq,&atxn) < 0) { if (np) free(np); return(-2); } *xp = atxbuf; if (!atxbuf[0]) *xp = xdef; #ifdef DOCHKVAR break; } s++; } #endif /* DOCHKVAR */ } #endif /* NOSPL */ if (**xp == NUL) { /* 12 mar 2001 */ if (np) free(np); return(-3); } #ifdef DTILDE if (dirflg) { dirp = tilde_expand(*xp); /* Expand tilde, if any, */ if (*dirp != '\0') { /* in the atom buffer. */ if (setatm(dirp,1) < 0) { printf("Expanded name too long\n"); if (np) free(np); return(-9); } } *xp = atmbuf; debug(F110,"cmifi tilde_expand",*xp,0); } #endif /* DTILDE */ if (!sv) { /* Only do this once */ sv = malloc((int)strlen(*xp)+1); /* Make a safe copy */ if (!sv) { printf("?cmifi: malloc error\n"); if (np) free(np); return(-9); } strcpy(sv,*xp); debug(F110,"cmifi sv",sv,0); } /* This is to get around "cd /" failing because "too many directories match" */ expanded = 0; /* Didn't call zxpand */ #ifdef datageneral debug(F110,"cmifi isdir 1",*xp,0); { int y; char *s; s = *xp; y = strlen(s); if (y > 1 && (s[y-1] == ':' || s[y-1] == '^' || s[y-1] == '=') ) s[y-1] = NUL; } debug(F110,"cmifi isdir 2",*xp,0); #endif /* datageneral */ #ifdef VMS if (dirflg) { if (!strcmp(*xp,"..")) { /* For UNIXers... */ setatm("-",0); *xp = atmbuf; } else if (!strcmp(*xp,".")) { setatm("[]",0); *xp = atmbuf; } } #endif /* VMS */ itsadir = isdir(*xp); /* Is it a directory? */ debug(F111,"cmifi itsadir",*xp,itsadir); #ifdef VMS /* If they said "blah" where "blah.dir" is a directory... */ /* change it to [.blah]. */ if (!itsadir) { char tmpbuf[600]; int flag = 0; char c, * p; p = *xp; while ((c = *p++) && !flag) if (ckstrchr(".[]:*?<>",c)) flag = 1; debug(F111,"cmifi VMS dirname flag",*xp,flag); if (!flag) { ckmakmsg(tmpbuf,TMPBUFSIZ,"[.",*xp,"]",NULL); itsadir = isdir(tmpbuf); if (itsadir) { setatm(tmpbuf,0); *xp = atmbuf; } debug(F111,"cmifi VMS dirname flag itsadir",*xp,itsadir); } } else if (itsadir == 1 && *(xp[0]) == '.' && *(xp[1])) { char *p; if (p = malloc(cc + 4)) { ckmakmsg(p,cc+4,"[",*xp,"]",NULL); setatm(p,0); *xp = atmbuf; debug(F110,"cmdir .foo",*xp,0); free(p); } } else if (itsadir == 2 && !diractive) { int x; /* [FOO]BAR.DIR instead of [FOO.BAR] */ char *p; p = malloc(cc + 4); if (p) { x = cvtdir(*xp,p,ATMBL); /* Convert to [FOO.BAR] */ if (x > 0) { setatm(p,0); *xp = atmbuf; debug(F110,"cmdir cvtdir",*xp,0); } free(p); } } #endif /* VMS */ debug(F101,"cmifi dirflg","",dirflg); if (dirflg) { /* Parsing a directory name? */ /* Yes, does it contain wildcards? */ if (iswild(*xp) || (diractive && (!strcmp(*xp,".") || !strcmp(*xp,".."))) ) { nzxopts |= ZX_DIRONLY; /* Match only directory names */ if (matchdot) nzxopts |= ZX_MATCHDOT; if (recursive) nzxopts |= ZX_RECURSE; debug(F111,"cmifi nzxopts 2",*xp,nzxopts); y = nzxpand(*xp,nzxopts); debug(F111,"cmifi nzxpand 2",*xp,y); nfiles = y; expanded = 1; } else { #ifdef VMS /* This is to allow (e.g.) "cd foo", where FOO.DIR;1 is in the current directory. */ debug(F111,"cmdir itsadir",*xp,itsadir); if (!itsadir) { char *s; int n; s = *xp; n = strlen(s); if (n > 0 && #ifdef COMMENT *s != '[' && s[n-1] != ']' && *s != '<' && s[n-1] != '>' && #else ckindex("[",s,0,0,1) == 0 && ckindex("<",s,0,0,1) == 0 && #endif /* COMMENT */ s[n-1] != ':') { char * dirbuf = NULL; dirbuf = (char *)malloc(n+4); if (dirbuf) { if (*s == '.') ckmakmsg(dirbuf,n+4,"[",s,"]",NULL); else ckmakmsg(dirbuf,n+4,"[.",s,"]",NULL); itsadir = isdir(dirbuf); debug(F111,"cmdir dirbuf",dirbuf,itsadir); if (itsadir) { setatm(dirbuf,0); *xp = atmbuf; debug(F110,"cmdir new *xp",*xp,0); } free(dirbuf); } /* This is to allow CDPATH to work in VMS... */ } else if (n > 0) { char * p; int i, j, k, d; char rb[2] = "]"; if (p = malloc(x + 8)) { ckstrncpy(p,*xp,x+8); i = ckindex(".",p,-1,1,1); d = ckindex(".dir",p,0,0,0); j = ckindex("]",p,-1,1,1); if (j == 0) { j = ckindex(">",p,-1,1,1); rb[0] = '>'; } k = ckindex(":",p,-1,1,1); if (i < j || i < k) i = 0; if (d < j || d < k) d = 0; /* Change [FOO]BAR or [FOO]BAR.DIR */ /* to [FOO.BAR] */ if (j > 0 && j < n) { p[j-1] = '.'; if (d > 0) p[d-1] = NUL; ckstrncat(p,rb,x+8); debug(F110,"cmdir xxx",p,0); } itsadir = isdir(p); debug(F111,"cmdir p",p,itsadir); if (itsadir) { setatm(p,0); *xp = atmbuf; debug(F110,"cmdir new *xp",*xp,0); } free(p); } } } #endif /* VMS */ y = (!itsadir) ? 0 : 1; debug(F111,"cmifi y itsadir",*xp,y); } } else { /* Parsing a filename. */ debug(F110,"cmifi *xp pre-zxpand",*xp,0); #ifndef COMMENT nzxopts |= (d == 0) ? ZX_FILONLY : 0; /* So always expand. */ if (matchdot) nzxopts |= ZX_MATCHDOT; if (recursive) nzxopts |= ZX_RECURSE; y = nzxpand(*xp,nzxopts); #else /* Here we're trying to fix a problem in which a directory name is accepted */ /* as a filename, but this breaks too many other things. */ /* nzxopts = 0; */ if (!d) { if (itsadir & !iswild(*xp)) { debug(F100,"cmifi dir when filonly","",0); printf("?Not a regular file: \"%s\"\n",*xp); if (sv) free(sv); if (np) free(np); return(-9); } else { nzxopts |= ZX_FILONLY; if (matchdot) nzxopts |= ZX_MATCHDOT; if (recursive) nzxopts |= ZX_RECURSE; y = nzxpand(*xp,nzxopts); } } #endif /* COMMENT */ nfiles = y; debug(F111,"cmifi y nzxpand",*xp,y); debug(F111,"cmifi y atmbuf",atmbuf,itsadir); expanded = 1; } /* domydir() calls zxrewind() so we MUST call nzxpand() here */ if (!expanded && diractive) { debug(F110,"cmifi diractive catch-all zxpand",*xp,0); nzxopts |= (d == 0) ? ZX_FILONLY : (dirflg ? ZX_DIRONLY : 0); if (matchdot) nzxopts |= ZX_MATCHDOT; if (recursive) nzxopts |= ZX_RECURSE; y = nzxpand(*xp,nzxopts); debug(F111,"cmifi diractive nzxpand",*xp,y); nfiles = y; expanded = 1; } *wild = (iswild(sv) || (y > 1)) && (itsadir == 0); #ifdef RECURSIVE if (!*wild) *wild = recursive; #endif /* RECURSIVE */ debug(F111,"cmifi sv wild",sv,*wild); debug(F101,"cmifi y","",y); if (dirflg && *wild && cdactive) { if (y > 1) { printf("?Wildcard matches more than one directory\n"); if (sv) free(sv); if (np) free(np); return(-9); } else { znext(*xp); } } if (itsadir && d && !dirflg) { /* It's a directory and not wild */ if (sv) free(sv); /* and it's ok to parse directories */ if (np) free(np); #ifndef NOLASTFILE makestr(&lastfile,tmplastfile); #endif /* NOLASTFILE */ return(x); } if (y == 0) { /* File was not found */ int dosearch = 0; dosearch = (path != NULL); /* A search path was given */ if (dosearch) { dosearch = hasnopath(sv); /* Filename includes no path */ debug(F111,"cmifip hasnopath",sv,dosearch); } if (dosearch) { /* Search the path... */ char * ptr = path; char c; while (1) { c = *ptr; if (c == PATHSEP || c == NUL) { if (!*path) { path = NULL; break; } *ptr = NUL; #ifdef UNIX /* By definition of CDPATH, an empty member denotes the current directory */ if (!*path) ckstrncpy(atmbuf,".",ATMBL); else #endif /* UNIX */ ckstrncpy(atmbuf,path,ATMBL); #ifdef VMS atmbuf[ATMBL] = NUL; /* If we have a logical name, evaluate it recursively */ if (*(ptr-1) == ':') { /* Logical name ends in : */ char *p; int n; while (((n = strlen(atmbuf)) > 0) && atmbuf[n-1] == ':') { atmbuf[n-1] = NUL; for (p = atmbuf; *p; p++) if (islower(*p)) *p = toupper(*p); debug(F111,"cmdir CDPATH LN 1",atmbuf,n); p = getenv(atmbuf); debug(F110,"cmdir CDPATH LN 2",p,0); if (!p) break; strncpy(atmbuf,p,ATMBL); atmbuf[ATMBL] = NUL; } } #else #ifdef OS2 if (*(ptr-1) != '\\' && *(ptr-1) != '/') ckstrncat(atmbuf,"\\",ATMBL); #else #ifdef UNIX if (*(ptr-1) != '/') ckstrncat(atmbuf,"/",ATMBL); #else #ifdef datageneral if (*(ptr-1) != ':') ckstrncat(atmbuf,":",ATMBL); #endif /* datageneral */ #endif /* UNIX */ #endif /* OS2 */ #endif /* VMS */ ckstrncat(atmbuf,sv,ATMBL); debug(F110,"cmifip add path",atmbuf,0); if (c == PATHSEP) ptr++; path = ptr; break; } ptr++; } x = 1; inword = 0; cc = 0; xc = (int) strlen(atmbuf); *xp = ""; goto i_path; } if (d) { if (sv) free(sv); if (np) free(np); return(-2); } else { if (!nomsg) { #ifdef CKROOT if (ckrooterr) printf("?Off Limits: %s\n",sv); else #endif /* CKROOT */ if (!quiet) printf("?No %s match - %s\n", dirflg ? "directories" : "files", sv); } if (sv) free(sv); if (np) free(np); return(-9); } } else if (y < 0) { #ifdef CKROOT if (ckrooterr) printf("?Off Limits: %s\n",sv); else #endif /* CKROOT */ printf("?Too many %s match - %s\n", dirflg ? "directories" : "files", sv); if (sv) free(sv); if (np) free(np); return(-9); } else if (*wild || y > 1) { if (sv) free(sv); if (np) free(np); #ifndef NOLASTFILE makestr(&lastfile,tmplastfile); #endif /* NOLASTFILE */ return(x); } /* If not wild, see if it exists and is readable. */ debug(F111,"cmifi sv not wild",sv,*wild); if (expanded) znext(*xp); /* Get first (only?) matching file */ if (dirflg) /* Maybe wild and expanded */ itsadir = isdir(*xp); /* so do this again. */ filesize = dirflg ? itsadir : zchki(*xp); /* Check accessibility */ if (expanded) { #ifdef ZXREWIND nfiles = zxrewind(); /* Rewind so next znext() gets 1st */ #else nzxopts |= dirflg ? ZX_DIRONLY : 0; if (matchdot) nzxopts |= ZX_MATCHDOT; if (recursive) nzxopts |= ZX_RECURSE; nfiles = nzxpand(*xp,nzxopts); #endif /* ZXREWIND */ } debug(F111,"cmifi nfiles",*xp,nfiles); debug(F101,"cmifi filesize","",filesize); free(sv); /* done with this */ sv = NULL; if (dirflg && !filesize) { printf("?Not a directory - %s\n",*xp); #ifdef CKCHANNELIO z_error = FX_ACC; #endif /* CKCHANNELIO */ return(-9); } else if (filesize == (CK_OFF_T)-3) { if (!xcmfdb) { if (diractive) /* Don't show filename if we're not allowed to see it */ printf("?Read permission denied\n"); else printf("?Read permission denied - %s\n",*xp); } if (np) free(np); #ifdef CKCHANNELIO z_error = FX_ACC; #endif /* CKCHANNELIO */ return(xcmfdb ? -6 : -9); } else if (filesize == (CK_OFF_T)-2) { if (!recursive) { if (np) free(np); if (d) { #ifndef NOLASTFILE makestr(&lastfile,tmplastfile); #endif /* NOLASTFILE */ return(0); } if (!xcmfdb) printf("?File not readable - %s\n",*xp); #ifdef CKCHANNELIO z_error = FX_ACC; #endif /* CKCHANNELIO */ return(xcmfdb ? -6 : -9); } } else if (filesize < (CK_OFF_T)0) { if (np) free(np); if (!nomsg && !xcmfdb) printf("?File not found - %s\n",*xp); #ifdef CKCHANNELIO z_error = FX_FNF; #endif /* CKCHANNELIO */ return(xcmfdb ? -6 : -9); } if (np) free(np); #ifndef NOLASTFILE makestr(&lastfile,tmplastfile); #endif /* NOLASTFILE */ return(x); #ifndef MAC case 2: /* ESC */ debug(F101,"cmifi esc, xc","",xc); if (xc == 0) { if (*xdef) { printf("%s ",xdef); /* If at beginning of field */ #ifdef GEMDOS fflush(stdout); #endif /* GEMDOS */ inword = cmflgs = 0; addbuf(xdef); /* Supply default. */ if (setatm(xdef,0) < 0) { printf("Default name too long\n"); if (np) free(np); return(-9); } } else { /* No default */ bleep(BP_WARN); } break; } if (**xp == '{') { /* Did user type opening brace... */ *xp = *xp + 1; xc--; cc--; qflag = '}'; } else if (dblquo && **xp == '"') { /* or doublequote? */ *xp = *xp + 1; /* If so ignore it and space past it */ xc--; cc--; qflag = '"'; } #ifndef NOSPL if (f) { /* If a conversion function is given */ #ifdef DOCHKVAR char *s = *xp; /* See if there are any variables in */ while (*s) { /* the string and if so, expand it. */ if (chkvar(s)) { #endif /* DOCHKVAR */ zq = atxbuf; atxn = CMDBL; if ((x = (*f)(*xp,&zq,&atxn)) < 0) { if (np) free(np); return(-2); } #ifdef DOCHKVAR /* reduce cc by number of \\ consumed by conversion */ /* function (needed for OS/2, where \ is path separator) */ cc -= (strlen(*xp) - strlen(atxbuf)); #endif /* DOCHKVAR */ *xp = atxbuf; if (!atxbuf[0]) { /* Result empty, use default */ *xp = xdef; cc = strlen(xdef); } #ifdef DOCHKVAR break; } s++; } #endif /* DOCHKVAR */ } #endif /* NOSPL */ #ifdef DTILDE if (dirflg && *(*xp) == '~') { debug(F111,"cmifi tilde_expand A",*xp,cc); dirp = tilde_expand(*xp); /* Expand tilde, if any... */ if (!dirp) dirp = ""; if (*dirp) { int i, xx; char * sp; xc = cc; /* Length of ~thing */ xx = setatm(dirp,0); /* Copy expansion to atom buffer */ debug(F111,"cmifi tilde_expand B",atmbuf,cc); if (xx < 0) { printf("Expanded name too long\n"); if (np) free(np); return(-9); } debug(F111,"cmifi tilde_expand xc","",xc); for (i = 0; i < xc; i++) { cmdchardel(); /* Back up over ~thing */ bp--; } xc = cc; /* How many new ones we just got */ sp = atmbuf; printf("%s",sp); /* Print them */ while ((*bp++ = *sp++)) ; /* Copy to command buffer */ bp--; /* Back up over NUL */ } *xp = atmbuf; } #endif /* DTILDE */ sp = *xp + cc; #ifdef UNIXOROSK if (!strcmp(atmbuf,"..")) { printf(" "); ckstrncat(cmdbuf," ",CMDBL); cc++; bp++; *wild = 0; *xp = atmbuf; break; } else if (!strcmp(atmbuf,".")) { bleep(BP_WARN); if (np) free(np); return(-1); } else { /* This patches a glitch when user types "./foo" */ /* in which the next two chars are omitted from the */ /* expansion. There should be a better fix, however, */ /* since there is no problem with "../foo". */ char *p = *xp; if (*p == '.' && *(p+1) == '/') cc -= 2; } #endif /* UNIXOROSK */ #ifdef datageneral *sp++ = '+'; /* Data General AOS wildcard */ #else *sp++ = '*'; /* Others */ #endif /* datageneral */ *sp-- = '\0'; #ifdef GEMDOS if (!strchr(*xp, '.')) /* abde.e -> abcde.e* */ strcat(*xp, ".*"); /* abc -> abc*.* */ #endif /* GEMDOS */ /* Add wildcard and expand list. */ #ifdef COMMENT /* This kills partial completion when ESC given in path segment */ nzxopts |= dirflg ? ZX_DIRONLY : (d ? 0 : ZX_FILONLY); #else /* nzxopts = 0; */ #endif /* COMMENT */ if (matchdot) nzxopts |= ZX_MATCHDOT; if (recursive) nzxopts |= ZX_RECURSE; y = nzxpand(*xp,nzxopts); nfiles = y; debug(F111,"cmifi nzxpand",*xp,y); if (y > 0) { #ifdef OS2 znext(filbuf); /* Get first */ #ifdef ZXREWIND zxrewind(); /* Must "rewind" */ #else nzxpand(*xp,nxzopts); #endif /* ZXREWIND */ #else /* Not OS2 */ ckstrncpy(filbuf,mtchs[0],CKMAXPATH); #endif /* OS2 */ } else *filbuf = '\0'; filbuf[CKMAXPATH] = NUL; *sp = '\0'; /* Remove wildcard. */ debug(F111,"cmifi filbuf",filbuf,y); debug(F111,"cmifi *xp",*xp,cc); *wild = (y > 1); if (y == 0) { if (!nomsg) { #ifdef CKROOT if (ckrooterr) printf("?Off Limits: %s\n",atmbuf); else #endif /* CKROOT */ printf("?No %s match - %s\n", dirflg ? "directories" : "files", atmbuf); if (np) free(np); return(-9); } else { bleep(BP_WARN); if (np) free(np); return(-1); } } else if (y < 0) { #ifdef CKROOT if (ckrooterr) printf("?Off Limits: %s\n",atmbuf); else #endif /* CKROOT */ printf("?Too many %s match - %s\n", dirflg ? "directories" : "files", atmbuf); if (np) free(np); return(-9); } else if (y > 1 /* Not unique */ #ifndef VMS || (y == 1 && isdir(filbuf)) /* Unique directory */ #endif /* VMS */ ) { #ifndef NOPARTIAL /* Partial filename completion */ int j, k; char c; k = 0; debug(F111,"cmifi partial",filbuf,cc); #ifdef OS2 { int cur = 0, len = 0, len2 = 0, min = strlen(filbuf), found = 0; char localfn[CKMAXPATH+1]; len = min; for (j = 1; j <= y; j++) { znext(localfn); if (dirflg && !isdir(localfn)) continue; found = 1; len2 = strlen(localfn); for (cur = cc; cur < len && cur < len2 && cur <= min; cur++ ) { /* OS/2 or Windows, case doesn't matter */ if (tolower(filbuf[cur]) != tolower(localfn[cur])) break; } if (cur < min) min = cur; } if (!found) min = cc; filbuf[min] = NUL; if (min > cc) k++; } #else /* OS2 */ for (i = cc; (c = filbuf[i]); i++) { for (j = 1; j < y; j++) if (mtchs[j][i] != c) break; if (j == y) k++; else filbuf[i] = filbuf[i+1] = NUL; } #endif /* OS2 */ #ifndef VMS /* isdir() function required for this! */ if (y == 1 && isdir(filbuf)) { /* Dont we already know this? */ int len; len = strlen(filbuf); if (len > 0 && len < ATMBL - 1) { if (filbuf[len-1] != dirsep) { filbuf[len] = dirsep; filbuf[len+1] = NUL; } } /* At this point, before just doing partial completion, we should look first to see if the given directory does indeed have any subdirectories (dirflg) or files (!dirflg); if it doesn't we should do full completion. Otherwise, the result looks funny to the user and "?" blows up the command for no good reason. */ { int flags = 0; filbuf[len+1] = '*'; filbuf[len+2] = NUL; if (dirflg) flags = ZX_DIRONLY; children = nzxpand(filbuf,flags); debug(F111,"cmifi children",filbuf,children); filbuf[len+1] = NUL; nzxpand(filbuf,flags); /* Restore previous list */ if (children == 0) goto NOSUBDIRS; } if (len + 1 > cc) k++; } /* Add doublequotes if there are spaces in the name */ { int x; if (qflag) { x = (qflag == '}'); /* (or braces) */ } else { x = !dblquo; } if (filbuf[0] != '"' && filbuf[0] != '{') k = dquote(filbuf,ATMBL,x); } #endif /* VMS */ debug(F111,"cmifi REPAINT filbuf",filbuf,k); if (k > 0) { /* Got more characters */ debug(F101,"cmifi REPAINT cc","",cc); debug(F101,"cmifi REPAINT xc","",xc); debug(F110,"cmifi REPAINT bp-cc",bp-cc,0); debug(F110,"cmifi REPAINT bp-xc",bp-xc,0); sp = filbuf + cc; /* Point to new ones */ if (qflag || strncmp(filbuf,bp-cc,cc)) { /* Repaint? */ int x; x = cc; if (qflag) x++; for (i = 0; i < x; i++) { cmdchardel(); /* Back up over old partial spec */ bp--; } sp = filbuf; /* Point to new word start */ debug(F110,"cmifi erase ok",sp,0); } cc = k; /* How many new ones we just got */ printf("%s",sp); /* Print them */ while ((*bp++ = *sp++)) ; /* Copy to command buffer */ bp--; /* Back up over NUL */ debug(F110,"cmifi partial cmdbuf",cmdbuf,0); if (setatm(filbuf,0) < 0) { printf("?Partial name too long\n"); if (np) free(np); return(-9); } debug(F111,"cmifi partial atmbuf",atmbuf,cc); *xp = atmbuf; } #endif /* NOPARTIAL */ bleep(BP_WARN); } else { /* Unique, complete it. */ #ifndef VMS #ifdef CK_TMPDIR /* isdir() function required for this! */ NOSUBDIRS: debug(F111,"cmifi unique",filbuf,children); if (isdir(filbuf) && children > 0) { int len; len = strlen(filbuf); if (len > 0 && len < ATMBL - 1) { if (filbuf[len-1] != dirsep) { filbuf[len] = dirsep; filbuf[len+1] = NUL; } } sp = filbuf + cc; bleep(BP_WARN); printf("%s",sp); cc++; while ((*bp++ = *sp++)) ; bp--; if (setatm(filbuf,0) < 0) { printf("?Directory name too long\n"); if (np) free(np); return(-9); } debug(F111,"cmifi directory atmbuf",atmbuf,cc); *xp = atmbuf; } else { /* Not a directory or dirflg */ #endif /* CK_TMPDIR */ #endif /* VMS */ #ifndef VMS /* VMS dir names are special */ #ifndef datageneral /* VS dirnames must not end in ":" */ if (dirflg) { int len; len = strlen(filbuf); if (len > 0 && len < ATMBL - 1) { if (filbuf[len-1] != dirsep) { filbuf[len] = dirsep; filbuf[len+1] = NUL; } } } #endif /* datageneral */ #endif /* VMS */ sp = filbuf + cc; /* Point past what user typed. */ { int x; if (qflag) { x = (qflag == '}'); } else { x = !dblquo; } if (filbuf[0] != '"' && filbuf[0] != '{') dquote(filbuf,ATMBL,x); } if (qflag || strncmp(filbuf,bp-cc,cc)) { /* Repaint? */ int x; x = cc; if (qflag) x++; for (i = 0; i < x; i++) { cmdchardel(); /* Back up over old partial spec */ bp--; } sp = filbuf; /* Point to new word start */ debug(F111,"cmifi after erase sp=",sp,cc); } printf("%s ",sp); /* Print the completed name. */ #ifdef GEMDOS fflush(stdout); #endif /* GEMDOS */ addbuf(sp); /* Add the characters to cmdbuf. */ if (setatm(filbuf,0) < 0) { /* And to atmbuf. */ printf("?Completed name too long\n"); if (np) free(np); return(-9); } inword = cmflgs = 0; *xp = brstrip(atmbuf); /* Return pointer to atmbuf. */ if (dirflg && !isdir(*xp)) { printf("?Not a directory - %s\n", filbuf); if (np) free(np); return(-9); } if (np) free(np); #ifndef NOLASTFILE makestr(&lastfile,tmplastfile); #endif /* NOLASTFILE */ return(0); #ifndef VMS #ifdef CK_TMPDIR } #endif /* CK_TMPDIR */ #endif /* VMS */ } break; case 3: /* Question mark - file menu wanted */ if (*xhlp == NUL) printf(dirflg ? " Directory name" : " Input file specification"); else printf(" %s",xhlp); #ifdef GEMDOS fflush(stdout); #endif /* GEMDOS */ /* If user typed an opening quote or brace, just skip past it */ if (**xp == '"' || **xp == '{') { *xp = *xp + 1; xc--; cc--; } #ifndef NOSPL if (f) { /* If a conversion function is given */ #ifdef DOCHKVAR char *s = *xp; /* See if there are any variables in */ while (*s) { /* the string and if so, expand them */ if (chkvar(s)) { #endif /* DOCHKVAR */ zq = atxbuf; atxn = CMDBL; if ((x = (*f)(*xp,&zq,&atxn)) < 0) { if (np) free(np); return(-2); } #ifdef DOCHKVAR /* reduce cc by number of \\ consumed by conversion */ /* function (needed for OS/2, where \ is path separator) */ cc -= (strlen(*xp) - strlen(atxbuf)); #endif /* DOCHKVAR */ *xp = atxbuf; #ifdef DOCHKVAR break; } s++; } #endif /* DOCHKVAR */ } #endif /* NOSPL */ debug(F111,"cmifi ? *xp, cc",*xp,cc); sp = *xp + cc; /* Insert "*" at end */ #ifdef datageneral *sp++ = '+'; /* Insert +, the DG wild card */ #else *sp++ = '*'; #endif /* datageneral */ *sp-- = '\0'; #ifdef GEMDOS if (! strchr(*xp, '.')) /* abde.e -> abcde.e* */ strcat(*xp, ".*"); /* abc -> abc*.* */ #endif /* GEMDOS */ debug(F110,"cmifi ? wild",*xp,0); nzxopts |= dirflg ? ZX_DIRONLY : (d ? 0 : ZX_FILONLY); debug(F101,"cmifi matchdot","",matchdot); if (matchdot) nzxopts |= ZX_MATCHDOT; if (recursive) nzxopts |= ZX_RECURSE; y = nzxpand(*xp,nzxopts); nfiles = y; *sp = '\0'; if (y == 0) { if (nomsg) { printf(": %s\n",atmbuf); printf("%s%s",cmprom,cmdbuf); fflush(stdout); if (np) free(np); return(-1); } else { #ifdef CKROOT if (ckrooterr) printf("?Off Limits: %s\n",atmbuf); else #endif /* CKROOT */ printf("?No %s match - %s\n", dirflg ? "directories" : "files", atmbuf); if (np) free(np); return(-9); } } else if (y < 0) { #ifdef CKROOT if (ckrooterr) printf("?Off Limits: %s\n",atmbuf); else #endif /* CKROOT */ printf("?Too many %s match - %s\n", dirflg ? "directories" : "files", atmbuf); if (np) free(np); return(-9); } else { printf(", one of the following:\n"); if (filhelp((int)y,"","",1,dirflg) < 0) { if (np) free(np); return(-9); } } printf("%s%s",cmprom,cmdbuf); fflush(stdout); break; #endif /* MAC */ } #ifdef BS_DIRSEP dirnamflg = 1; x = gtword(0); /* No, get a word */ dirnamflg = 0; #else x = gtword(0); /* No, get a word */ #endif /* BS_DIRSEP */ *xp = atmbuf; } } /* C M F L D -- Parse an arbitrary field */ /* Returns: -3 if no input present when required, -2 if field too big for buffer, -1 if reparse needed, 0 otherwise, xp pointing to string result. NOTE: Global flag keepallchars says whether this routine should break on CR or LF: needed for MINPUT targets and DECLARE initializers, where we want to keep control characters if the user specifies them (March 2003). It might have been better to change the calling sequence but that was not practical. */ int cmfld(xhlp,xdef,xp,f) char *xhlp, *xdef, **xp; xx_strp f; { int x, xc; char *zq; inword = 0; /* Initialize counts & pointers */ cc = 0; xc = 0; *xp = ""; debug(F110,"cmfld xdef 1",xdef,0); if (!xhlp) xhlp = ""; if (!xdef) xdef = ""; ckstrncpy(cmdefault,xdef,CMDEFAULT); /* Copy default */ xdef = cmdefault; debug(F111,"cmfld xdef 2",xdef,cmflgs); debug(F111,"cmfld atmbuf 1",atmbuf,xc); if ((x = cmflgs) != 1) { /* Already confirmed? */ x = gtword(0); /* No, get a word */ } else { if (setatm(xdef,0) < 0) { /* If so, use default, if any. */ printf("?Default too long\n"); return(-9); } } *xp = atmbuf; /* Point to result. */ debug(F111,"cmfld atmbuf 2",atmbuf,cmflgs); while (1) { xc += cc; /* Count the characters. */ debug(F111,"cmfld gtword",atmbuf,xc); debug(F101,"cmfld x","",x); switch (x) { case -9: printf("Command or field too long\n"); case -4: /* EOF */ case -3: /* Empty. */ case -2: /* Out of space. */ case -1: /* Reparse needed */ return(x); case 1: /* CR */ case 0: /* SP */ debug(F111,"cmfld 1",atmbuf,xc); if (xc == 0) { /* If no input, return default. */ if (setatm(xdef,0) < 0) { printf("?Default too long\n"); return(-9); } } *xp = atmbuf; /* Point to what we got. */ debug(F111,"cmfld 2",atmbuf,((f) ? 1 : 0)); if (f) { /* If a conversion function is given */ zq = atxbuf; /* employ it now. */ atxn = CMDBL; if ((*f)(*xp,&zq,&atxn) < 0) return(-2); debug(F111,"cmfld 3",atxbuf,xc); /* Replace by new value -- for MINPUT only keep all chars */ if (setatm(atxbuf,keepallchars ? 3:1) < 0) { /* 16 Mar 2003 */ printf("Value too long\n"); return(-9); } *xp = atmbuf; } debug(F111,"cmfld 4",atmbuf,xc); if (**xp == NUL) { /* If variable evaluates to null */ if (setatm(xdef,0) < 0) { printf("?Default too long\n"); return(-9); } if (**xp == NUL) x = -3; /* If still empty, return -3. */ } debug(F111,"cmfld returns",*xp,x); return(x); case 2: /* ESC */ if (xc == 0 && *xdef) { printf("%s ",xdef); /* If at beginning of field, */ #ifdef GEMDOS fflush(stdout); #endif /* GEMDOS */ addbuf(xdef); /* Supply default. */ inword = cmflgs = 0; if (setatm(xdef,0) < 0) { printf("?Default too long\n"); return(-9); } else /* Return as if whole field */ return(0); /* typed, followed by space. */ } else { bleep(BP_WARN); } break; case 3: /* Question mark */ debug(F110,"cmfld QUESTIONMARK",cmdbuf,0); if (*xhlp == NUL) printf(" Please complete this field"); else printf(" %s",xhlp); printf("\n%s%s",cmprom,cmdbuf); fflush(stdout); break; } debug(F111,"cmfld gtword A x",cmdbuf,x); x = gtword(0); debug(F111,"cmfld gtword B x",cmdbuf,x); } } /* C M T X T -- Get a text string, including confirmation */ /* Print help message 'xhlp' if ? typed, supply default 'xdef' if null string typed. Returns: -1 if reparse needed or buffer overflows. 1 otherwise. with cmflgs set to return code, and xp pointing to result string. */ int cmtxt(xhlp,xdef,xp,f) char *xhlp; char *xdef; char **xp; xx_strp f; { int x, i; char *xx, *zq; static int xc; if (!xhlp) xhlp = ""; if (!xdef) xdef = ""; cmfldflgs = 0; cmdefault[0] = NUL; if (*xdef) ckstrncpy(cmdefault,xdef,CMDEFAULT); /* Copy default */ xdef = cmdefault; debug(F101,"cmtxt cmflgs","",cmflgs); inword = 0; /* Start atmbuf counter off at 0 */ cc = 0; if (cmflgs == -1) { /* If reparsing, */ *xp = pp; xc = (int)strlen(*xp); /* get back the total text length, */ bp = *xp; /* and back up the pointers. */ np = *xp; pp = *xp; } else { /* otherwise, */ /* debug(F100,"cmtxt: fresh start","",0); */ *xp = ""; /* start fresh. */ xc = 0; } *atmbuf = NUL; /* And empty the atom buffer. */ rtimer(); /* Reset timer */ if ((x = cmflgs) != 1) { int done = 0; while (!done) { x = gtword(0); /* Get first word. */ *xp = pp; /* Save pointer to it. */ /* debug(F111,"cmtxt:",*xp,cc); */ if (x == -10) { if (gtimer() > timelimit) { /* if (!quiet) printf("?Timed out\n"); */ return(x); } } else done = 1; } } while (1) { /* Loop for each word in text. */ xc += cc; /* Char count for all words. */ /* debug(F111,"cmtxt gtword",atmbuf,xc); */ /* debug(F101,"cmtxt x","",x); */ switch (x) { case -10: if (gtimer() > timelimit) { #ifdef IKSD extern int inserver; if (inserver) { printf("\r\nIKSD IDLE TIMEOUT: %d sec\r\n", timelimit); doexit(GOOD_EXIT,0); } #endif /* IKSD */ /* if (!quiet) printf("?Timed out\n"); */ return(-10); } else { x = gtword(0); continue; } case -9: /* Buffer overflow */ printf("Command or field too long\n"); case -4: /* EOF */ #ifdef MAC case -3: /* Quit/Timeout */ #endif /* MAC */ case -2: /* Overflow */ case -1: /* Deletion */ return(x); case 0: /* Space */ xc++; /* Just count it */ break; case 1: /* CR or LF */ if (xc == 0) *xp = xdef; if (f) { /* If a conversion function is given */ char * sx = atxbuf; zq = atxbuf; /* Point to the expansion buffer */ atxn = CMDBL; /* specify its length */ /* debug(F111,"cmtxt calling (*f)",*xp,atxbuf); */ if ((x = (*f)(*xp,&zq,&atxn)) < 0) return(-2); sx = atxbuf; #ifndef COMMENT cc = 0; while (*sx++) cc++; /* (faster than calling strlen) */ #else cc = (int)strlen(atxbuf); #endif /* COMMENT */ /* Should be equal to (CMDBL - atxn) but isn't always. */ /* Why not? */ if (cc < 1) { /* Nothing in expansion buffer? */ *xp = xdef; /* Point to default string instead. */ #ifndef COMMENT sx = xdef; while (*sx++) cc++; /* (faster than calling strlen) */ #else cc = strlen(xdef); #endif /* COMMENT */ } else { /* Expansion function got something */ *xp = atxbuf; /* return pointer to it. */ } debug(F111,"cmtxt (*f)",*xp,cc); } else { /* No expansion function */ #ifndef COMMENT /* Avoid a strlen() call */ xx = *xp; cc = 0; while (*xx++) cc++; #else /* NO! xc is apparently not always set appropriately */ cc = xc; #endif /* COMMENT */ } xx = *xp; #ifdef COMMENT /* strlen() no longer needed */ for (i = (int)strlen(xx) - 1; i > 0; i--) #else for (i = cc - 1; i > 0; i--) #endif /* COMMENT */ if (xx[i] != SP) /* Trim trailing blanks */ break; else xx[i] = NUL; return(x); case 2: /* ESC */ if (xc == 0) { /* Nothing typed yet */ if (*xdef) { /* Have a default for this field? */ printf("%s ",xdef); /* Yes, supply it */ inword = cmflgs = 0; #ifdef GEMDOS fflush(stdout); #endif /* GEMDOS */ cc = addbuf(xdef); } else bleep(BP_WARN); /* No default */ } else { /* Already in field */ int x; char *p; x = strlen(atmbuf); if (ckstrcmp(atmbuf,xdef,x,0)) { /* Matches default? */ bleep(BP_WARN); /* No */ } else if ((int)strlen(xdef) > x) { /* Yes */ p = xdef + x; printf("%s ", p); #ifdef GEMDOS fflush(stdout); #endif /* GEMDOS */ addbuf(p); inword = cmflgs = 0; debug(F110,"cmtxt: addbuf",cmdbuf,0); } else { bleep(BP_WARN); } } break; case 3: /* Question Mark */ if (*xhlp == NUL) printf(" Text string"); else printf(" %s",xhlp); printf("\n%s%s",cmprom,cmdbuf); fflush(stdout); break; default: printf("?Unexpected return code from gtword() - %d\n",x); return(-2); } x = gtword(0); } } /* C M K E Y -- Parse a keyword */ /* Call with: table -- keyword table, in 'struct keytab' format; n -- number of entries in table; xhlp -- pointer to help string; xdef -- pointer to default keyword; f -- string preprocessing function (e.g. to evaluate variables) pmsg -- 0 = don't print error messages 1 = print error messages 2 = include CM_HLP keywords even if invisible 3 = 1+2 4 = parse a switch (keyword possibly ending in : or =) 8 = don't strip comments (used, e.g., for "help #") Returns: -3 -- no input supplied and no default available -2 -- input doesn't uniquely match a keyword in the table -1 -- user deleted too much, command reparse required n >= 0 -- value associated with keyword */ /* Front ends for cmkey2(): cmkey() - The normal keyword parser cmkeyx() - Like cmkey() but suppresses error messages cmswi() - Switch parser */ int cmkey(table,n,xhlp,xdef,f) /* cmkey */ struct keytab table[]; int n; char *xhlp, *xdef; xx_strp f; { return(cmkey2(table,n,xhlp,xdef,"",f,1)); } int cmkeyx(table,n,xhlp,xdef,f) /* cmkeyx */ struct keytab table[]; int n; char *xhlp, *xdef; xx_strp f; { return(cmkey2(table,n,xhlp,xdef,"",f,0)); } int cmswi(table,n,xhlp,xdef,f) /* cmswi */ struct keytab table[]; int n; char *xhlp, *xdef; xx_strp f; { return(cmkey2(table,n,xhlp,xdef,"",f,4)); } int cmkey2(table,n,xhlp,xdef,tok,f,pmsg) struct keytab table[]; int n; char *xhlp, *xdef; char *tok; xx_strp f; int pmsg; { /* cmkey2 */ extern int havetoken; int i, tl, y, z = 0, zz, xc, wordlen = 0, cmswitch; char *xp, *zq; if (!xhlp) xhlp = ""; if (!xdef) xdef = ""; cmfldflgs = 0; if (!table) { printf("?Keyword table missing\n"); return(-9); } tl = (int)strlen(tok); inword = xc = cc = 0; /* Clear character counters. */ cmswitch = pmsg & 4; /* Flag for parsing a switch */ debug(F101,"cmkey: pmsg","",pmsg); debug(F101,"cmkey: cmflgs","",cmflgs); debug(F101,"cmkey: cmswitch","",cmswitch); /* debug(F101,"cmkey: cmdbuf","",cmdbuf);*/ ppvnambuf[0] = NUL; if ((zz = cmflgs) == 1) { /* Command already entered? */ if (setatm(xdef,0) < 0) { /* Yes, copy default into atom buf */ printf("?Default too long\n"); return(-9); } rtimer(); /* Reset timer */ } else { /* Otherwise get a command word */ rtimer(); /* Reset timer */ if (pmsg & 8) /* 8 is for parsing HELP tokens */ zz = gtword(4); else zz = gtword((pmsg == 4) ? 1 : 0); } debug(F101,"cmkey table length","",n); debug(F101,"cmkey cmflgs","",cmflgs); debug(F101,"cmkey cc","",cc); while (1) { xc += cc; debug(F111,"cmkey gtword xc",atmbuf,xc); debug(F101,"cmkey gtword zz","",zz); switch (zz) { case -10: /* Timeout */ if (gtimer() < timelimit) { if (pmsg & 8) /* 8 is for parsing HELP tokens */ zz = gtword(4); else zz = gtword((pmsg == 4) ? 1 : 0); continue; } else { #ifdef IKSD extern int inserver; if (inserver) { printf("\r\nIKSD IDLE TIMEOUT: %d sec\r\n", timelimit); doexit(GOOD_EXIT,0); } #endif /* IKSD */ return(-10); } case -5: return(cmflgs = 0); case -9: printf("Command or field too long\n"); case -4: /* EOF */ case -3: /* Null Command/Quit/Timeout */ case -2: /* Buffer overflow */ case -1: /* Or user did some deleting. */ return(cmflgs = zz); case 1: /* CR */ case 0: /* User terminated word with space */ case 4: /* or switch ending in : or = */ wordlen = cc; /* Length if no conversion */ if (cc == 0) { /* Supply default if we got nothing */ if ((wordlen = setatm(xdef,(zz == 4) ? 2 : 0)) < 0) { printf("?Default too long\n"); return(-9); } } if (zz == 1 && cc == 0) /* Required field missing */ return(-3); if (f) { /* If a conversion function is given */ char * p2; zq = atxbuf; /* apply it */ p2 = atxbuf; atxn = CMDBL; if ((*f)(atmbuf,&zq,&atxn) < 0) return(-2); debug(F110,"cmkey atxbuf after *f",atxbuf,0); if (!*p2) /* Supply default if we got nothing */ p2 = xdef; ckstrncpy(ppvnambuf,atmbuf,PPVLEN); if ((wordlen = setatm(p2,(zz == 4) ? 2 : 0)) < 0) { printf("Evaluated keyword too long\n"); return(-9); } #ifdef M_UNGW /* This bit lets us save more than one "word". For example, "define \%x echo one two three", "\%x". It works too, but it breaks labels, and therefore WHILE and FOR loops, etc. */ if (p2[wordlen] >= SP) { p2 += wordlen; while (*p2 == SP) p2++; if (*p2) { ungword(); pp = p2; } } #endif /* M_UNGW */ } #ifdef COMMENT /* ^^^ */ if (cmswitch && *atmbuf != '/') { if (pmsg & 1) { bleep(BP_FAIL); printf("?Not a switch - %s\n",atmbuf); } cmflgs = -2; return(-6); } #endif /* COMMENT */ if (cmswitch) { int i; for (i = 0; i < wordlen; i++) { if (atmbuf[i] == ':' || atmbuf[i] == '=') { brkchar = atmbuf[i]; atmbuf[i] = NUL; break; } } } #ifdef TOKPRECHECK /* This was an effective optimization but it breaks sometimes on labels. */ if (tl && !isalpha(atmbuf[0])) { /* Precheck for token */ for (i = 0; i < tl; i++) { /* Save function call to ckstrchr */ if (tok[i] == atmbuf[0]) { debug(F000,"cmkey token:",atmbuf,*atmbuf); ungword(); /* Put back the following word */ return(-5); /* Special return code for token */ } } } #endif /* TOKPRECHECK */ y = lookup(table,atmbuf,n,&z); /* Look up word in the table */ debug(F111,"cmkey lookup",atmbuf,y); debug(F101,"cmkey zz","",zz); debug(F101,"cmkey cmflgs","",cmflgs); debug(F101,"cmkey crflag","",crflag); switch (y) { case -3: /* Nothing to look up */ break; case -2: /* Ambiguous */ cmflgs = -2; if (pmsg & 1) { bleep(BP_FAIL); printf("?Ambiguous - %s\n",atmbuf); return(-9); } return(-2); case -1: /* Not found at all */ #ifndef TOKPRECHECK if (tl) { for (i = 0; i < tl; i++) /* Check for token */ if (tok[i] == *atmbuf) { /* Got one */ debug(F000,"cmkey token:",atmbuf,*atmbuf); ungword(); /* Put back the following word */ return(-5); /* Special return code for token */ } } #endif /* TOKPRECHECK */ if (tl == 0) { /* No tokens were included */ #ifdef OS2 /* In OS/2 and Windows, allow for a disk letter like DOS */ if (isalpha(*atmbuf) && *(atmbuf+1) == ':') return(-7); #endif /* OS2 */ if ((pmsg & 1) && !quiet) { bleep(BP_FAIL); printf("?No keywords match - %s\n",atmbuf); /* cmkey */ } return(cmflgs = -9); } else { if (cmflgs == 1 || cmswitch) /* cmkey2 or cmswi */ return(cmflgs = -6); else return(cmflgs = -2); /* The -6 code is to let caller try another table */ } break; default: #ifdef CK_RECALL if (test(table[z].flgs,CM_NOR)) no_recall = 1; #endif /* CK_RECALL */ if (zz == 4) swarg = 1; cmkwflgs = table[z].flgs; break; } return(y); case 2: /* User terminated word with ESC */ debug(F101,"cmkey Esc cc","",cc); if (cc == 0) { if (*xdef != NUL) { /* Nothing in atmbuf */ printf("%s ",xdef); /* Supply default if any */ #ifdef GEMDOS fflush(stdout); #endif /* GEMDOS */ addbuf(xdef); if (setatm(xdef,0) < 0) { printf("?Default too long\n"); return(-9); } inword = cmflgs = 0; debug(F111,"cmkey: default",atmbuf,cc); } else { debug(F101,"cmkey Esc pmsg","",0); #ifdef COMMENT /* Chained FDBs... The idea is that this function might not have a default, but the next one might. But if it doesn't, there is no way to come back to this one. To be revisited later... */ if (xcmfdb) /* Chained fdb -- try next one */ return(-3); #endif /* COMMENT */ if (pmsg & (1|4)) { /* So for now just beep */ bleep(BP_WARN); } break; } } if (f) { /* If a conversion function is given */ char * pp; zq = atxbuf; /* apply it */ pp = atxbuf; atxn = CMDBL; if ((*f)(atmbuf,&zq,&atxn) < 0) return(-2); if (!*pp) pp = xdef; if (setatm(pp,0) < 0) { printf("Evaluated keyword too long\n"); return(-9); } } y = lookup(table,atmbuf,n,&z); /* Something in atmbuf */ debug(F111,"cmkey lookup y",atmbuf,y); debug(F111,"cmkey lookup z",atmbuf,z); if (y == -2 && z >= 0 && z < n) { /* Ambiguous */ #ifndef NOPARTIAL int j, k, len = 9999; /* Do partial completion */ /* Skip past any abbreviations in the table */ for ( ; z < n; z++) { if ((table[z].flgs & CM_ABR) == 0) break; if (!(table[z].flgs & CM_HLP) || (pmsg & 2)) break; } debug(F111,"cmkey partial z",atmbuf,z); debug(F111,"cmkey partial n",atmbuf,n); for (j = z+1; j < n; j++) { debug(F111,"cmkey partial j",table[j].kwd,j); if (ckstrcmp(atmbuf,table[j].kwd,cc,0)) break; if (table[j].flgs & CM_ABR) continue; if ((table[j].flgs & CM_HLP) && !(pmsg & 2)) continue; k = ckstrpre(table[z].kwd,table[j].kwd); debug(F111,"cmkey partial k",table[z].kwd,k); if (k < len) len = k; /* Length of longest common prefix */ } debug(F111,"cmkey partial len",table[z].kwd,len); if (len != 9999 && len > cc) { ckstrncat(atmbuf,table[z].kwd+cc,ATMBL); atmbuf[len] = NUL; printf("%s",atmbuf+cc); ckstrncat(cmdbuf,atmbuf+cc,CMDBL); xc += (len - cc); cc = len; } #endif /* NOPARTIAL */ bleep(BP_WARN); break; } else if (y == -3) { bleep(BP_WARN); break; } else if (y == -1) { /* Not found */ if ((pmsg & 1) && !quiet) { bleep(BP_FAIL); printf("?No keywords match - \"%s\"\n",atmbuf); } cmflgs = -2; return(-9); } /* If we found it, but it's a help-only keyword and the "help" bit is not set in pmsg, then not found. */ debug(F101,"cmkey flgs","",table[z].flgs); if (test(table[z].flgs,CM_HLP) && ((pmsg & 2) == 0)) { if ((pmsg & 1) && !quiet) { bleep(BP_FAIL); printf("?No keywords match - %s\n",atmbuf); } cmflgs = -2; return(-9); } /* See if the keyword just found has the CM_ABR bit set in its flgs field, and if so, search forwards in the table for a keyword that has the same kwval but does not have CM_ABR (or CM_INV?) set, and then expand using the full keyword. WARNING: This assumes that (a) keywords are in alphabetical order, and (b) the CM_ABR bit is set only if the the abbreviated keyword is a true abbreviation (left substring) of the full keyword. */ if (test(table[z].flgs,CM_ABR)) { int zz; for (zz = z+1; zz < n; zz++) if ((table[zz].kwval == table[z].kwval) && (!test(table[zz].flgs,CM_ABR)) && (!test(table[zz].flgs,CM_INV))) { z = zz; break; } } xp = table[z].kwd + cc; if (cmswitch && test(table[z].flgs,CM_ARG)) { #ifdef VMS printf("%s=",xp); brkchar = '='; #else printf("%s:",xp); brkchar = ':'; #endif /* VMS */ } else { printf("%s ",xp); brkchar = SP; } #ifdef CK_RECALL if (test(table[z].flgs,CM_NOR)) no_recall = 1; #endif /* CK_RECALL */ cmkwflgs = table[z].flgs; #ifdef GEMDOS fflush(stdout); #endif /* GEMDOS */ addbuf(xp); if (cmswitch && test(table[z].flgs,CM_ARG)) { bp--; /* Replace trailing space with : */ #ifdef VMS *bp++ = '='; #else *bp++ = ':'; #endif /* VMS */ *bp = NUL; np = bp; swarg = 1; } inword = 0; cmflgs = 0; debug(F110,"cmkey: addbuf",cmdbuf,0); return(y); case 3: /* User typed "?" */ if (f) { /* If a conversion function is given */ char * pp; zq = atxbuf; /* do the conversion now. */ pp = atxbuf; atxn = CMDBL; if ((*f)(atmbuf,&zq,&atxn) < 0) return(-2); if (setatm(pp,0) < 0) { printf("?Evaluated keyword too long\n"); return(-9); } } y = lookup(table,atmbuf,n,&z); /* Look up what we have so far. */ if (y == -1) { /* Strictly speaking if the main keyword table search fails, then we should look in the token table if one is given. But in practice, tokens are also included in the main keyword table. */ cmflgs = -2; if ((pmsg & 1) && !quiet) { bleep(BP_FAIL); printf(" No keywords match\n"); return(-9); } return(-2); } #ifndef COMMENT /* This is to allow ?-help to work immediately after a token */ /* without having to type an intermediate space */ if (tl) { for (i = 0; i < tl; i++) /* Check for token */ if (tok[i] == *atmbuf) { /* Got one */ debug(F000,"cmkey token:",atmbuf,*atmbuf); ungword(); /* Put back the following word */ cmflgs = 3; /* Force help next time around */ return(-5); /* Special return code for token */ } } #endif /* COMMENT */ if (*xhlp == NUL) printf(" One of the following:\n"); else printf(" %s, one of the following:\n",xhlp); { int x; x = pmsg & (2|4); /* See kwdhelp() comments */ if (atmbuf[0]) /* If not at beginning of field */ x |= 1; /* also show invisibles */ kwdhelp(table,n,atmbuf,"","",1,x); } #ifndef NOSPL if (!havetoken) { extern int topcmd; if (tl > 0 && topcmd != XXHLP) /* This is bad... */ printf("or a macro name (\"do ?\" for a list) "); } #endif /* NOSPL */ if (*atmbuf == NUL && !havetoken) { if (tl == 1) printf("or the token %c\n",*tok); else if (tl > 1) printf("or one of the tokens: %s\n",ckspread(tok)); } printf("%s%s", cmprom, cmdbuf); fflush(stdout); break; default: printf("\n%d - Unexpected return code from gtword\n",zz); return(cmflgs = -2); } zz = (pmsg & 8) ? gtword(4) : gtword((pmsg == 4) ? 1 : 0); debug(F111,"cmkey gtword zz",atmbuf,zz); } } int chktok(tlist) char *tlist; { char *p; p = tlist; while (*p != NUL && *p != *atmbuf) p++; return((*p) ? (int) *p : 0); } /* Routines for parsing and converting dates and times */ #define isdatesep(c) (ckstrchr(" -/._",c)) #define CMDATEBUF 1024 char cmdatebuf[CMDATEBUF+4] = { NUL, NUL }; static char * cmdatebp = cmdatebuf; char * cmdatemsg = NULL; static struct keytab timeunits[] = { { "days", TU_DAYS, 0 }, { "months", TU_MONTHS, 0 }, { "weeks", TU_WEEKS, 0 }, { "wks", TU_WEEKS, 0 }, { "years", TU_YEARS, 0 }, { "yrs", TU_YEARS, 0 } }; static int nunits = (sizeof(timeunits) / sizeof(struct keytab)); #define SYM_NOW 0 #define SYM_TODA 1 #define SYM_TOMO 2 #define SYM_YEST 3 static struct keytab symdaytab[] = { { "now", SYM_NOW, 0 }, { "today", SYM_TODA, 0 }, { "tomorrow", SYM_TOMO, 0 }, { "yesterday", SYM_YEST, 0 } }; static int nsymdays = (sizeof(symdaytab) / sizeof(struct keytab)); static struct keytab daysofweek[] = { { "Friday", 5, 0 }, { "Monday", 1, 0 }, { "Saturday", 6, 0 }, { "Sunday", 0, 0 }, { "Thursday", 4, 0 }, { "Tuesday", 2, 0 }, { "Wednesday", 3, 0 } }; static struct keytab usatz[] = { /* RFC 822 timezones */ { "cdt", 5, 0 }, /* Values are GMT offsets */ { "cst", 6, 0 }, { "edt", 4, 0 }, { "est", 5, 0 }, { "gmt", 0, 0 }, { "mdt", 6, 0 }, { "mst", 7, 0 }, { "pdt", 7, 0 }, { "pst", 8, 0 }, { "utc", 0, 0 }, { "zulu", 0, 0 } }; static int nusatz = (sizeof(usatz) / sizeof(struct keytab)); /* C M C V T D A T E -- Converts free-form date to standard form. */ /* Call with s = pointer to free-format date, time, or date and time. t = 0: return time only if time was given in s. t = 1: always return time (00:00:00 if no time given in s). t = 2: allow time to be > 24:00:00. Returns: NULL on failure; Pointer to "yyyymmdd hh:mm:ss" (local date-time) on success. */ /* Before final release the following long lines should be wrapped. Until then we leave them long since wrapping them wrecks EMACS's C indentation. */ /* asctime pattern */ static char * atp1 = "[A-Z][a-z][a-z] [A-Z][a-z][a-z] [ 0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9] [0-9][0-9][0-9][0-9]"; /* asctime pattern with timezone */ static char * atp2 = "[A-Z][a-z][a-z] [A-Z][a-z][a-z] [ 0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9] [A-Z][A-Z][A-Z] [0-9][0-9][0-9][0-9]"; #define DATEBUFLEN 127 #define YYYYMMDD 12 #define isleap(y) (((y) % 4 == 0 && (y) % 100 != 0) || (y) % 400 == 0) static int mdays[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; #define NEED_DAYS 1 #define NEED_HRS 2 #define NEED_MINS 3 #define NEED_SECS 4 #define NEED_FRAC 5 #define DELTABUF 256 static char deltabuf[DELTABUF]; static char * deltabp = deltabuf; char * cmdelta(yy, mo, dd, hh, mm, ss, sign, dyy, dmo, ddd, dhh, dmm, dss) int yy, mo, dd, hh, mm, ss, sign, dyy, dmo, ddd, dhh, dmm, dss; /* cmdelta */ { int zyy, zmo, zdd, zhh, zmm, zss; long t1, t2, t3, t4; long d1 = 0, d2, d3; char datebuf[DATEBUFLEN+1]; #ifdef DEBUG if (deblog) { debug(F101,"cmdelta yy","",yy); debug(F101,"cmdelta mo","",mo); debug(F101,"cmdelta dd","",dd); debug(F101,"cmdelta hh","",hh); debug(F101,"cmdelta mm","",mm); debug(F101,"cmdelta ss","",ss); debug(F101,"cmdelta sign","",sign); debug(F101,"cmdelta dyy","",dyy); debug(F101,"cmdelta dmo","",dmo); debug(F101,"cmdelta ddd","",ddd); debug(F101,"cmdelta dhh","",dhh); debug(F101,"cmdelta dmm","",dmm); debug(F101,"cmdelta dss","",dss); } #endif /* DEBLOG */ if (yy < 0 || yy > 9999) { makestr(&cmdatemsg,"Base year out of range"); debug(F111,"cmdelta",cmdatemsg,-1); return(NULL); } if (mo < 1 || mo > 12) { makestr(&cmdatemsg,"Base month out of range"); debug(F111,"cmdelta",cmdatemsg,-1); return(NULL); } if (dd < 1 || dd > mdays[mo]) { makestr(&cmdatemsg,"Base day out of range"); debug(F111,"cmdelta",cmdatemsg,-1); return(NULL); } if (hh < 0 || hh > 23) { makestr(&cmdatemsg,"Base hour out of range"); debug(F111,"cmdelta",cmdatemsg,-1); return(NULL); } if (mm < 0 || mm > 59) { makestr(&cmdatemsg,"Base minute out of range"); debug(F111,"cmdelta",cmdatemsg,-1); return(NULL); } if (ss < 0 || ss > 60) { makestr(&cmdatemsg,"Base second out of range"); debug(F111,"cmdelta",cmdatemsg,-1); return(NULL); } sign = (sign < 0) ? -1 : 1; if (dmo != 0) { if (sign > 0) { mo += (sign * dmo); if (mo > 12) { yy += mo / 12; mo = mo % 12; } } else if (sign < 0) { while (dmo > 12) { yy--; dmo -= 12; } if (dmo < mo) { mo -= dmo; } else { yy--; mo = 12 - (dmo - mo); } } } if (dyy != 0) { yy += (sign * dyy); if (yy > 9999 || yy < 0) { makestr(&cmdatemsg,"Result year out of range"); debug(F111,"cmdelta",cmdatemsg,-1); return(NULL); } } sprintf(datebuf,"%04d%02d%02d %02d:%02d:%02d",yy,mo,dd,hh,mm,ss); d1 = mjd(datebuf); debug(F111,"cmdelta mjd",datebuf,d1); t1 = hh * 3600 + mm * 60 + ss; /* Base time to secs since midnight */ t2 = dhh * 3600 + dmm * 60 + dss; /* Delta time, ditto */ t3 = t1 + (sign * t2); /* Get sum (or difference) */ d2 = (sign * ddd); /* Delta days */ d2 += t3 / 86400L; t4 = t3 % 86400L; /* Fractional part of day */ if (t4 < 0) { /* If negative */ d2--; /* one less delta day */ t4 += 86400L; /* get positive seconds */ } hh = (int) (t4 / 3600L); mm = (int) (t4 % 3600L) / 60; ss = (int) (t4 % 3600L) % 60; sprintf(datebuf,"%s %02d:%02d:%02d", mjd2date(d1+d2),hh,mm,ss); { int len, k, n; char * p; len = strlen(datebuf); k = deltabp - (char *)deltabuf; /* Space used */ n = DELTABUF - k - 1; /* Space left */ if (n < len) { /* Not enough? */ deltabp = deltabuf; /* Wrap around */ n = DELTABUF; } ckstrncpy(deltabp,datebuf,n); p = deltabp; deltabp += len + 1; return(p); } } /* Convert Delta Time to Seconds */ int delta2sec(s,result) char * s; long * result; { long ddays = 0L, zz; int dsign = 1, dhours = 0, dmins = 0, dsecs = 0, units; int state = NEED_DAYS; char *p, *p2, *p3, c = 0; char buf[64]; if (!s) s = ""; if (!*s) return(-1); if ((int)strlen(s) > 63) return(-1); ckstrncpy(buf,s,64); p = buf; if (*p != '+' && *p != '-') return(-1); if (*p++ == '-') dsign = -1; while (*p == SP) /* Skip intervening spaces */ p++; while (state) { /* FSA to parse delta time */ if (state < 0 || !isdigit(*p)) return(-1); p2 = p; /* Get next numeric field */ while (isdigit(*p2)) p2++; c = *p2; /* And break character */ *p2 = NUL; /* Terminate the number */ switch (state) { /* Interpret according to state */ case NEED_DAYS: /* Initial */ if ((c == '-') || /* VMS format */ ((c == 'd' || c == 'D') && !isalpha(*(p2+1)))) { /* Days */ ddays = atol(p); if (!*(p2+1)) state = 0; else /* if anything is left */ state = NEED_HRS; /* now we want hours. */ } else if (c == ':') { /* delimiter is colon */ dhours = atoi(p); /* so it's hours */ state = NEED_MINS; /* now we want minutes */ } else if (!c) { /* end of string */ dhours = atoi(p); /* it's still hours */ state = 0; /* and we're done */ } else if (isalpha(c) || c == SP) { if (c == SP) { /* It's a keyword? */ p2++; /* Skip spaces */ while (*p2 == SP) p2++; } else { /* or replace first letter */ *p2 = c; } p3 = p2; /* p2 points to beginning of keyword */ while (isalpha(*p3)) /* Find end of keyword */ p3++; c = *p3; /* NUL it out so we can look it up */ if (*p3) /* p3 points to keyword terminator */ *p3 = NUL; if ((units = lookup(timeunits,p2,nunits,NULL)) < 0) return(-1); *p2 = NUL; /* Re-terminate the number */ *p3 = c; while (*p3 == SP) /* Point at field after units */ p3++; p2 = p3; switch (units) { case TU_DAYS: ddays = atol(p); break; default: return(-1); } if (*p2) { state = NEED_HRS; p2--; } else state = 0; } else { /* Anything else */ state = -1; /* is an error */ } break; case NEED_HRS: /* Looking for hours */ if (c == ':') { dhours = atoi(p); state = NEED_MINS; } else if (!c) { dhours = atoi(p); state = 0; } else { state = -1; } break; case NEED_MINS: /* Looking for minutes */ if (c == ':') { dmins = atoi(p); state = NEED_SECS; } else if (!c) { dmins = atoi(p); state = 0; } else { state = -1; } break; case NEED_SECS: /* Looking for seconds */ if (c == '.') { dsecs = atoi(p); state = NEED_FRAC; } else if (!c) { dsecs = atoi(p); state = 0; } else { state = -1; } break; case NEED_FRAC: /* Fraction of second */ if (!c && rdigits(p)) { if (*p > '4') dsecs++; state = 0; } else { state = -1; } break; } if (c) /* next field if any */ p = p2 + 1; } if (state < 0) return(-1); /* if days > 24854 and sizeof(long) == 32 we overflow */ zz = ddays * 86400L; if (zz < 0L) /* This catches it */ return(-2); zz += dhours * 3600L + dmins * 60L + dsecs; zz *= dsign; *result = zz; return(0); } char * cmcvtdate(s,t) char * s; int t; { int x, i, j, k, hh, mm, ss, ff, pmflag = 0, nodate = 0, len, dow; int units, isgmt = 0, gmtsign = 0, d = 0, state = 0, nday; int kn = 0, ft[8], isletter = 0, f2len = 0; int zhh = 0; /* Timezone adjustments */ int zmm = 0; int zdd = 0; int dsign = 1; /* Delta-time adjustments */ int ddays = 0; int dmonths = 0; int dyears = 0; int dhours = 0; int dmins = 0; int dsecs = 0; int havedelta = 0; char * fld[8], * p = "", * p2, * p3; /* Assorted buffers and pointers */ char * s2, * s3; char * year = NULL, * month = NULL, * day = NULL; char * hour = "00", * min = "00", * sec = "00"; char datesep = 0; char tmpbuf[8]; char xbuf[DATEBUFLEN+1]; char ybuf[DATEBUFLEN+1]; char zbuf[DATEBUFLEN+1]; char yyyymmdd[YYYYMMDD]; char dbuf[26]; char daybuf[3]; char monbuf[3]; char yearbuf[5]; char timbuf[16], *tb, cc; char * dp = NULL; /* Result pointer */ if (!s) s = ""; tmpbuf[0] = NUL; while (*s == SP) s++; /* Gobble any leading blanks */ if (isalpha(*s)) /* Remember if 1st char is a letter */ isletter = 1; len = strlen(s); debug(F110,"cmcvtdate",s,len); if (len == 0) { /* No arg - return current date-time */ dp = ckdate(); goto xcvtdate; } if (len > DATEBUFLEN) { /* Check length of arg */ makestr(&cmdatemsg,"Date-time string too long"); debug(F111,"cmcvtdate",cmdatemsg,-1); return(NULL); } hh = 0; /* Init time to 00:00:00.0 */ mm = 0; ss = 0; ff = 0; ztime(&p); if (!p) p = ""; if (*p) { /* Init time to current time */ x = ckstrncpy(dbuf,p,26); if (x > 17) { hh = atoi(&dbuf[11]); mm = atoi(&dbuf[14]); ss = atoi(&dbuf[17]); } } ckstrncpy(yyyymmdd,zzndate(),YYYYMMDD); /* Init date to current date */ ckstrncpy(yearbuf,yyyymmdd,5); ckstrncpy(monbuf,&yyyymmdd[4],3); ckstrncpy(daybuf,&yyyymmdd[6],3); year = yearbuf; month = monbuf; day = daybuf; nday = atoi(daybuf); ckstrncpy(xbuf,s,DATEBUFLEN); /* Make a local copy we can poke */ s = xbuf; /* Point to it */ s[len] = NUL; if (s[0] == ':') { p = s; goto dotime; } /* Special preset formats... */ if (len >= 14) { /* FTP MDTM all-numeric date */ char c; c = s[14]; /* e.g. 19980615100045.014 */ s[14] = NUL; x = rdigits(s); s[14] = c; if (x) { ckstrncpy(yyyymmdd,s,8+1); year = NULL; p = &s[8]; goto dotime; } } x = 0; /* Becomes > 0 for asctime format */ if (isalpha(s[0])) { if (len == 24) { /* Asctime format? */ /* Sat Jul 14 15:57:32 2001 */ x = ckmatch(atp1,s,0,0); debug(F111,"cmcvtdate asctime",s,x); } else if (len == 28) { /* Or Asctime plus timezone? */ /* Sat Jul 14 15:15:39 EDT 2001 */ x = ckmatch(atp2,s,0,0); debug(F111,"cmcvtdate asctime+timezone",s,x); } } if (x > 0) { /* Asctime format */ int xx; strncpy(yearbuf,s + len - 4,4); yearbuf[4] = NUL; for (i = 0; i < 3; i++) tmpbuf[i] = s[i+4]; tmpbuf[3] = NUL; if ((xx = lookup(cmonths,tmpbuf,12,NULL)) < 0) { makestr(&cmdatemsg,"Invalid month"); debug(F111,"cmcvtdate",cmdatemsg,-1); return(NULL); } debug(F101,"cmcvtdate asctime month","",xx); monbuf[0] = (xx / 10) + '0'; monbuf[1] = (xx % 10) + '0'; monbuf[2] = NUL; daybuf[0] = (s[8] == ' ' ? '0' : s[8]); daybuf[1] = s[9]; daybuf[2] = NUL; xbuf[0] = SP; for (i = 11; i < 19; i++) xbuf[i-10] = s[i]; xbuf[9] = NUL; ckmakmsg(zbuf,18,yearbuf,monbuf,daybuf,xbuf); debug(F110,"cmcvtdate asctime ok",zbuf,0); if (len == 24) { dp = zbuf; goto xcvtdate; } else { int n; n = ckmakmsg(ybuf,DATEBUFLEN-4,zbuf," ",NULL,NULL); ybuf[n++] = s[20]; ybuf[n++] = s[21]; ybuf[n++] = s[22]; ybuf[n++] = NUL; ckstrncpy(xbuf,ybuf,DATEBUFLEN); s = xbuf; isletter = 0; } } /* Check for day of week */ p = s; while (*p == SP) p++; dow = -1; if (*p) { p2 = p; cc = NUL; while (1) { if (*p2 == ',' || *p2 == SP || !*p2) { cc = *p2; /* Save break char */ *p2 = NUL; /* NUL it out */ p3 = p2; /* Remember this spot */ if ((dow = lookup(daysofweek,p,7,NULL)) > -1) { debug(F111,"cmcvtdate dow",p,dow); s = p2; if (cc == ',' || cc == SP) { /* Point to next field */ s++; while (*s == SP) s++; } p = s; debug(F111,"cmcvtdate dow new p",p,dow); break; } else if (isalpha(*p) && cc == ',') { makestr(&cmdatemsg,"Unrecognized day of week"); debug(F111,"cmcvtdate",cmdatemsg,-1); return(NULL); } else { *p3 = cc; break; } } p2++; } } len = strlen(s); /* Update length */ debug(F111,"cmcvtdate s",s,len); debug(F111,"cmcvtdate dow",s,dow); if (dow > -1) { /* Have a day-of-week number */ long zz; int n, j; zz = mjd(zzndate()); /* Get today's MJD */ debug(F111,"cmcvtdate zz","",zz); j = (((int)(zz % 7L)) + 3) % 7; /* Today's day-of-week number */ debug(F111,"cmcvtdate j","",j); hh = 0; /* Init time to midnight */ mm = 0; ss = 0; if (j == dow) { ckstrncpy(yyyymmdd,zzndate(),YYYYMMDD); year = NULL; } else { n = dow - j; /* Days from now */ if (dow < j) n += 7; if (n < 0) n += 7; /* Add to MJD */ zz += n; ckstrncpy(yyyymmdd,mjd2date(zz),YYYYMMDD); /* New date */ year = NULL; } debug(F111,"cmcvtdate A",yyyymmdd,len); if (len == 0) { /* No more fields after this */ ckmakmsg(zbuf,18,yyyymmdd," 00:00:00",NULL,NULL); dp = zbuf; goto xcvtdate; } isletter = 0; if (rdigits(p) && len < 8) /* Next field is time? */ goto dotime; /* If so go straight to time section */ if (isdigit(*p)) { if (*(p+1) == ':') goto dotime; else if (isdigit(*(p+1)) && (*(p+2) == ':')) goto dotime; } } debug(F111,"cmcvtdate B s",s,dow); debug(F111,"cmcvtdate B p",p,dow); if (*s == '+' || *s == '-') { /* Delta time only - skip ahead. */ p = s; goto delta; } #ifdef COMMENT /* What is the purpose of this? It breaks parsing of email dates like "Wed, 13 Feb 2002 17:43:02 -0800 (PST)". Removing this code fixes the problem and Kermit still passes the 'dates' script. - fdc, Sat Nov 26 10:52:45 2005. */ if (dow > -1) { /* Day of week given followed by something that is not a time */ /* or a delta so it can't be valid */ makestr(&cmdatemsg,"Invalid tokens after day of week"); debug(F111,"cmcvtdate fail",cmdatemsg,-1); return(NULL); } #endif /* COMMENT */ /* Handle "today", "yesterday", "tomorrow", and +/- n units */ if (ckstrchr("TtYyNn",s[0])) { int i, k, n, minus = 0; char c; long jd; jd = mjd(ckdate()); debug(F111,"cmcvtdate mjd",s,jd); /* Symbolic date: TODAY, TOMORROW, etc...? */ s2 = s; /* Find end of keyword */ i = 0; while (isalpha(*s2)) { /* and get its length */ i++; s2++; } c = *s2; /* Zap but save delimiter */ *s2 = NUL; k = lookup(symdaytab,s,nsymdays,NULL); /* Look up keyword */ *s2 = c; /* Replace delimiter */ if (k < 0) /* Keyword not found */ goto normal; s3 = &s[i]; while (*s3 == SP) /* Skip whitespace */ s3++; if (*s3 == '_' || *s3 == ':') s3++; switch (k) { /* Have keyword */ case SYM_NOW: /* NOW */ ckstrncpy(ybuf,ckdate(),DATEBUFLEN); ckstrncpy(yyyymmdd,ybuf,YYYYMMDD); year = NULL; if (*s3) { /* No overwriting current time. */ ckstrncat(ybuf," ",DATEBUFLEN); ckstrncat(ybuf,s3,DATEBUFLEN); } break; default: /* Yesterday, Today, and Tomorrow */ if (k == SYM_TOMO) { /* TOMORROW */ strncpy(ybuf,mjd2date(jd+1),8); } else if (k == SYM_YEST) { /* YESTERDAY */ strncpy(ybuf,mjd2date(jd-1),8); } else { /* TODAY */ strncpy(ybuf,ckdate(),8); } strncpy(ybuf+8," 00:00:00",DATEBUFLEN-8); /* Default time is 0 */ ckstrncpy(yyyymmdd,ybuf,YYYYMMDD); year = NULL; if (*s3) { /* If something follows keyword... */ if (isdigit(*s3)) { /* Time - overwrite default time */ strncpy(ybuf+8,s+i,DATEBUFLEN-8); } else { /* Something else, keep default time */ ckstrncat(ybuf," ",DATEBUFLEN); /* and append */ ckstrncat(ybuf,s3,DATEBUFLEN); /* whatever we have */ } } } s = ybuf; /* Point to rewritten date-time */ len = strlen(s); /* Update length */ isletter = 0; /* Cancel this */ } /* Regular free-format non-symbolic date */ normal: debug(F111,"cmcvtdate NORMAL",s,len); debug(F111,"cmcvtdate dow",s,dow); if (yyyymmdd[0] && !year) { ckstrncpy(yearbuf,yyyymmdd,5); ckstrncpy(monbuf,&yyyymmdd[4],3); ckstrncpy(daybuf,&yyyymmdd[6],3); year = yearbuf; month = monbuf; day = daybuf; nday = atoi(daybuf); } if (isdigit(s[0])) { /* Time without date? */ p = s; if (s[1] == ':') { debug(F111,"cmcvtdate NORMAL X1",s,len); goto dotime; } else if (len > 1 && isdigit(s[1]) && s[2] == ':') { debug(F111,"cmcvtdate NORMAL X2",s,len); goto dotime; } else if (rdigits(s) && len < 8) { debug(F111,"cmcvtdate NORMAL X3",s,len); goto dotime; } } if (len >= 8 && isdigit(*s)) { /* Check first for yyyymmdd* */ debug(F111,"cmcvtdate NORMAL A",s,len); cc = s[8]; s[8] = NUL; /* Isolate first 8 characters */ if (rdigits(s)) { /* Have valid time separator? */ p2 = cc ? ckstrchr(" Tt_-:",cc) : NULL; if (!cc || p2) { ckstrncpy(yyyymmdd,s,YYYYMMDD); /* Valid separator */ year = NULL; s += 8; /* or time not given */ if (cc) s++; /* Keep date */ p = s; /* and go handle time */ goto dotime; } else if (!p2) { if (isdigit(cc)) makestr(&cmdatemsg,"Numeric date too long"); else makestr(&cmdatemsg,"Invalid date-time separator"); debug(F111,"cmcvtdate",cmdatemsg,-1); return(NULL); } } s[8] = cc; /* Put this back! */ } debug(F111,"cmcvtdate NORMAL non-yyyymmdd",s,len); /* Free-format date -- figure it out */ #ifdef COMMENT if (*s && !isdigit(*s)) { makestr(&cmdatemsg,"Unrecognized word in date"); debug(F111,"cmcvtdate",cmdatemsg,-1); return(NULL); } #endif /* COMMENT */ for (i = 0; i < 8; i++) /* Field types */ ft[i] = -1; fld[i = 0] = (p = s); /* First field */ while (*p) { /* Get next two fields */ if (isdatesep(*p)) { /* Have a date separator */ if (i == 0) { datesep = *p; } else if (i == 1 && *p != datesep) { makestr(&cmdatemsg,"Inconsistent date separators"); debug(F111,"cmcvtdate",cmdatemsg,-1); return(NULL); } *p++ = NUL; /* Replace by NUL */ if (*p) { /* Now we're at the next field */ while (*p == SP) p++; /* Skip leading spaces */ if (!*p) break; /* Make sure we still have something */ if (i == 2) /* Last one? */ break; fld[++i] = p; /* No, record pointer to this one */ } else { break; } } else if ((*p == 'T' || *p == 't') && isdigit(*(p+1))) { /* Time */ *p++ = NUL; break; } else if (*p == ':') { if (i == 0 && p == s) { nodate = 1; break; } else if (i != 0) { /* After a date */ if (i == 2) { /* OK as date-time separator (VMS) */ *p++ = NUL; break; } if (i < 2) makestr(&cmdatemsg,"Too few fields in date"); else makestr(&cmdatemsg,"Misplaced time separator"); debug(F111,"cmcvtdate",cmdatemsg,-1); return(NULL); } nodate = 1; /* Or without a date */ break; } p++; } if (p > s && i == 0) /* Make sure we have a date */ nodate = 1; /* No date. */ if (nodate && dow > -1) { /* Have implied date from DOW? */ goto dotime; /* Use, use that, go do time. */ } else if (nodate) { /* No date and no implied date */ char *tmp = NULL; /* Substitute today's date */ ztime(&tmp); if (!tmp) tmp = ""; if (!*tmp) { makestr(&cmdatemsg,"Problem supplying current date"); debug(F111,"cmcvtdate",cmdatemsg,-1); return(NULL); } ckstrncpy(dbuf,tmp,26); /* Reformat */ if (dbuf[8] == SP) dbuf[8] = '0'; fld[0] = dbuf+8; /* dd */ dbuf[10] = NUL; fld[1] = dbuf+4; /* mmm */ dbuf[7] = NUL; fld[2] = dbuf+20; /* yyyy */ dbuf[24] = NUL; hh = atoi(&dbuf[11]); mm = atoi(&dbuf[14]); ss = atoi(&dbuf[17]); p = s; /* Back up source pointer to reparse */ } else if (i < 2) { makestr(&cmdatemsg,"Too few fields in date"); debug(F111,"cmcvtdate",cmdatemsg,-1); return(NULL); } /* Have three date fields - see what they are */ for (k = 0, j = 0; j < 3; j++) { /* Get number of non-numeric fields */ ft[j] = rdigits(fld[j]); debug(F111,"cmcvtdate fld",fld[j],j); if (ft[j] == 0) k++; } kn = k; /* How many numeric fields */ month = NULL; /* Strike out default values */ year = NULL; day = NULL; if (k == 2 && ft[2] > 0) { /* Jul 20, 2001 */ int xx; xx = strlen(fld[1]); p3 = fld[1]; if (xx > 0) if (p3[xx-1] == ',') { p3[xx-1] = NUL; if (rdigits(p3)) { k = 1; ft[1] = 1; } else p3[xx-1] = ','; } } if (k > 1) { /* We can have only one non-numeric */ if (nodate) makestr(&cmdatemsg,"Unrecognized word in date"); else if (!ft[2] && isdigit(*(fld[2]))) makestr(&cmdatemsg,"Invalid date-time separator"); else makestr(&cmdatemsg,"Too many non-numeric fields in date"); debug(F111,"cmcvtdate",cmdatemsg,-1); return(NULL); } if (!ft[0]) { k = 0; } else if (!ft[1]) { k = 1; } else if (!ft[2]) { makestr(&cmdatemsg,"Non-digit in third date field"); debug(F111,"cmcvtdate",cmdatemsg,-1); return(NULL); } else k = -1; if (k > -1) { if ((x = lookup(cmonths,fld[k],12,NULL)) < 0) { makestr(&cmdatemsg,"Unknown month"); debug(F111,"cmcvtdate",cmdatemsg,-1); return(NULL); } sprintf(tmpbuf,"%02d",x); month = tmpbuf; } f2len = strlen(fld[2]); /* Length of 3rd field */ if (k == 0) { /* monthname dd, yyyy */ day = fld[1]; year = fld[2]; } else if (((int)strlen(fld[0]) == 4)) { /* yyyy-xx-dd */ year = fld[0]; day = fld[2]; if (!month) month = fld[1]; /* yyyy-mm-dd */ } else if (f2len == 4) { /* xx-xx-yyyy */ year = fld[2]; if (month) { /* dd-name-yyyy */ day = fld[0]; } else { /* xx-xx-yyyy */ int f0, f1; f0 = atoi(fld[0]); f1 = atoi(fld[1]); if (((f0 > 12) && (f1 <= 12)) || (f1 <= 12 && f0 == f1)) { day = fld[0]; /* mm-dd-yyyy */ month = fld[1]; } else if ((f0 <= 12) && (f1 > 12)) { if (!rdigits(fld[1])) { makestr(&cmdatemsg,"Day not numeric"); debug(F111,"cmcvtdate",cmdatemsg,-1); return(NULL); } else { day = fld[1]; /* dd-mm-yyyy */ } month = fld[0]; } else { if (!f0 || !f1) makestr(&cmdatemsg,"Day or month out of range"); else makestr(&cmdatemsg,"Day and month are ambiguous"); debug(F111,"cmcvtdate",cmdatemsg,-1); return(NULL); } } } else if ((f2len < 4) && /* dd mmm yy (RFC822) */ !rdigits(fld[1]) && /* middle field is monthname */ rdigits(fld[2])) { int tmpyear; day = fld[0]; if (!fld[2][1]) { makestr(&cmdatemsg,"Too few digits in year"); debug(F111,"cmcvtdate",cmdatemsg,-1); return(NULL); } tmpyear = atoi(fld[2]); if (tmpyear < 50) /* RFC 2822 windowing */ tmpyear += 2000; else /* This includes 3-digit years. */ tmpyear += 1900; year = ckitoa(tmpyear); } else if ((f2len < 4) && (k < 0) && ((int)strlen(fld[0]) < 4)) { makestr(&cmdatemsg,"Ambiguous numeric date"); debug(F111,"cmcvtdate",cmdatemsg,-1); return(NULL); } else if ((f2len > 4) && ft[2]) { makestr(&cmdatemsg,"Too many digits in year"); debug(F111,"cmcvtdate",cmdatemsg,-1); return(NULL); } else { makestr(&cmdatemsg,"Unexpected date format"); debug(F111,"cmcvtdate",cmdatemsg,-1); return(NULL); } x = atoi(month); sprintf(tmpbuf,"%02d",x); /* 2-digit numeric month */ /* state = 1 = hours state = 2 = minutes state = 3 = seconds state = 4 = fractions of seconds */ dotime: if (isletter && (s == p)) { makestr(&cmdatemsg,"Unknown date-time word"); debug(F111,"cmcvtdate",cmdatemsg,-1); return(NULL); } if (!year && yyyymmdd[0]) { debug(F110,"cmcvtdate dotime yyyymmdd",yyyymmdd,0); for (i = 0; i < 4; i++) yearbuf[i] = yyyymmdd[i]; yearbuf[4] = NUL; monbuf[0] = yyyymmdd[4]; monbuf[1] = yyyymmdd[5]; monbuf[2] = NUL; daybuf[0] = yyyymmdd[6]; daybuf[1] = yyyymmdd[7]; daybuf[2] = NUL; day = daybuf; nday = atoi(daybuf); month = monbuf; year = yearbuf; } if (!year) { makestr(&cmdatemsg,"Internal error - date not defaulted"); debug(F111,"cmcvtdate",cmdatemsg,-1); return(NULL); } /* Get here with day, month, and year set */ debug(F110,"cmcvtdate dotime day",day,0); debug(F110,"cmcvtdate dotime month",month,0); debug(F110,"cmcvtdate dotime year",year,0); debug(F110,"cmcvtdate dotime s",s,0); debug(F110,"cmcvtdate dotime p",p,0); x = atoi(month); if (x > 12 || x < 1) { makestr(&cmdatemsg,"Month out of range"); debug(F111,"cmcvtdate",cmdatemsg,-1); return(NULL); } nday = atoi(day); i = mdays[x]; if (x == 2) if (isleap(atoi(year))) i++; if (nday > i || nday < 1) { makestr(&cmdatemsg,"Day out of range"); debug(F111,"cmcvtdate",cmdatemsg,-1); return(NULL); } if (!*p && t == 0) { sprintf(zbuf,"%04d%02d%02d",atoi(year),atoi(month),nday); dp = zbuf; goto xcvtdate; } if (*p == '+' || *p == '-') { /* GMT offset without a time */ hh = 0; /* so default time to 00:00:00 */ mm = 0; ss = 0; goto cmtimezone; /* and go do timezone */ } if (*p && !isdigit(*p) && *p != ':') { makestr(&cmdatemsg,"Invalid time"); debug(F111,"cmcvtdate",cmdatemsg,-1); return(NULL); } sprintf(yyyymmdd,"%s%s%02d",year,month,nday); /* for tz calculations... */ state = 1; /* Initialize time-parsing FSA */ hh = 0; /* hours */ mm = 0; /* minutes */ ss = 0; /* seconds */ ff = -1; /* fraction */ d = 0; /* Digit counter */ p2 = p; /* Preliminary digit count... */ while (isdigit(*p2)) { d++; p2++; } if (d > 6) { makestr(&cmdatemsg,"Too many time digits"); debug(F111,"cmcvtdate",cmdatemsg,-1); return(NULL); } d = (d & 1 && *p2 != ':') ? 1 : 0; /* Odd implies leading '0' */ while (*p) { /* Get the time, if any */ if (isdigit(*p)) { /* digit */ if (d++ > 1) { state++; d = 1; } switch (state) { case 1: /* Hours */ hh = hh * 10 + (*p - '0'); break; case 2: /* Minutes */ mm = mm * 10 + (*p - '0'); break; case 3: /* Seconds */ ss = ss * 10 + (*p - '0'); break; case 4: /* Fraction of second */ if (ff < 0) ff = (*p > '4') ? 1 : 0; break; } } else if (*p == ':') { /* Colon */ state++; d = 0; if (state > 3) { makestr(&cmdatemsg,"Too many time fields"); debug(F111,"cmcvtdate",cmdatemsg,-1); return(NULL); } } else if (*p == '.') { if (state == 3) { state = 4; d = 0; } else { makestr(&cmdatemsg,"Improper fraction"); debug(F111,"cmcvtdate",cmdatemsg,-1); return(NULL); } } else if (*p == SP) { /* Space */ while (*p && (*p == SP)) /* position to first nonspace */ p++; break; } else if (isalpha(*p)) { /* AM/PM/Z or timezone */ break; } else if (*p == '+' || *p == '-') { /* GMT offset */ break; } else { makestr(&cmdatemsg,"Invalid time characters"); debug(F111,"cmcvtdate",cmdatemsg,-1); return(NULL); } p++; } if (!*p) /* If nothing left */ goto xcmdate; /* go finish up */ /* At this point we have HH, MM, SS, and FF */ /* Now handle the rest: AM, PM, and/or timezone info */ if (!ckstrcmp(p,"am",2,0)) { /* AM/PM... */ pmflag = 0; p += 2; } else if (!ckstrcmp(p,"a.m.",4,0)) { pmflag = 0; p += 4; } else if (!ckstrcmp(p,"pm",2,0)) { pmflag = 1; p += 2; } else if (!ckstrcmp(p,"p.m.",4,0)) { pmflag = 1; p += 4; } if (pmflag && hh < 12) /* If PM was given */ hh += 12; /* add 12 to the hour */ /* Now handle timezone */ cmtimezone: debug(F110,"cmcvtdate timezone",p,0); zhh = 0; /* GMT offset HH */ zmm = 0; /* GMT offset MM */ gmtsign = 0; /* Sign of GMT offset */ isgmt = 0; /* 1 if time is GMT */ while (*p && *p == SP) /* Gobble spaces */ p++; if (!*p) /* If nothing left */ goto xcmdate; /* we're done */ if (isalpha(*p)) { /* Something left */ int zone = 0; /* Alphabetic must be timezone */ p2 = p; /* Isolate timezone */ p++; while (isalpha(*p)) p++; p3 = p; cc = *p; *p = NUL; p = p2; /* Have timezone, look it up */ zone = lookup(usatz,p,nusatz,NULL); debug(F111,"cmcvtdate timezone alpha",p,zone); if (zone < 0) { /* Not found */ makestr(&cmdatemsg,"Unknown timezone"); debug(F111,"cmcvtdate",cmdatemsg,-1); return(NULL); } isgmt++; /* All dates are GMT from here down */ if (zone != 0) { /* But not this one so make it GMT */ hh += zone; /* RFC 822 timezone: EST etc */ debug(F101,"cmcvtdate hh + zone","",hh); if (hh > 23) { /* Offset crosses date boundary */ int i; long jd; jd = mjd(yyyymmdd); /* Get MJD */ jd += hh / 24; /* Add new day(s) */ hh = hh % 24; /* and convert back to yyyymmdd */ ckstrncpy(yyyymmdd,mjd2date(jd),YYYYMMDD); debug(F111,"cmcvtdate zone-adjusted date",yyyymmdd,hh); for (i = 0; i < 4; i++) yearbuf[i] = yyyymmdd[i]; yearbuf[4] = NUL; monbuf[0] = yyyymmdd[4]; monbuf[1] = yyyymmdd[5]; monbuf[2] = NUL; daybuf[0] = yyyymmdd[6]; daybuf[1] = yyyymmdd[7]; daybuf[2] = NUL; day = daybuf; nday = atoi(daybuf); month = monbuf; year = yearbuf; } } p = p3; /* Put back whatever we poked above */ *p = cc; } else if (*p == '+' || *p == '-') { /* GMT/UTC offset */ p3 = p; debug(F110,"cmcvtdate timezone GMT offset",p,0); gmtsign = (*p == '+') ? -1 : 1; isgmt++; p++; while (*p == SP) p++; d = 0; p2 = p; while (isdigit(*p)) { /* Count digits */ d++; p++; } if (d != 4) { /* Strict RFC [2]822 */ isgmt = 0; /* If not exactly 4 digits */ p = p3; /* it's not a GMT offset. */ goto delta; /* So treat it as a delta time. */ } d = (d & 1 && *p != ':') ? 1 : 0; /* Odd implies leading '0' */ p = p2; debug(F111,"cmcvtdate GMT offset sign",p,gmtsign); debug(F101,"cmcvtdate GMT offset d","",d); state = 1; while (*p) { if (isdigit(*p)) { /* digit */ if (d++ > 1) { state++; d = 1; } switch (state) { case 1: zhh = zhh * 10 + (*p - '0'); break; case 2: zmm = zmm * 10 + (*p - '0'); break; default: /* Ignore seconds or fractions */ break; } } else if (*p == ':') { /* Colon */ state++; d = 0; } else if (*p == SP || *p == '(') { break; } else { p = p3; /* Maybe it's not a GMT offset. */ goto delta; /* So treat it as a delta time. */ } p++; } } debug(F110,"cmcvtdate source string after timezone",p,0); if (*p) { /* Anything left? */ p2 = p; while (*p2 == SP) /* Skip past spaces */ p2++; if (*p2 == '(') { /* RFC-822 comment? */ int pc = 1; /* paren counter */ p2++; while (*p2) { if (*p2 == ')') { if (--pc == 0) { p2++; break; } } else if (*p2 == ')') { pc++; } p2++; } while (*p2 == SP) /* Skip past spaces */ p2++; if (!*p2) /* Anything left? */ *p = NUL; /* No, erase comment */ } if (!*p2) /* Anything left? */ goto xcmdate; /* No, done. */ p = p2; delta: debug(F110,"cmcvtdate delta yyyymmdd",yyyymmdd,0); debug(F110,"cmcvtdate delta year",year,0); debug(F110,"cmcvtdate delta p",p,0); if (*p == '+' || *p == '-') { /* Delta time */ int state = NEED_DAYS; /* Start off looking for days */ char c = 0; dsign = 1; /* Get sign */ if (*p++ == '-') dsign = -1; while (*p == SP) /* Skip intervening spaces */ p++; while (state) { /* FSA to parse delta time */ if (state < 0 || !isdigit(*p)) { makestr(&cmdatemsg,"Invalid delta time"); debug(F111,"cmcvtdate",cmdatemsg,-1); return(NULL); } p2 = p; /* Get next numeric field */ while (isdigit(*p2)) p2++; c = *p2; /* And break character */ *p2 = NUL; /* Terminate the number */ switch (state) { /* Interpret according to state */ case NEED_DAYS: /* Initial */ if ((c == '-') || /* VMS format */ ((c == 'd' || c == 'D') && !isalpha(*(p2+1)))) { /* Days */ ddays = atoi(p); if (!*(p2+1)) state = 0; else /* if anything is left */ state = NEED_HRS; /* now we want hours. */ } else if ((c == 'W' || c == 'w') && !isalpha(*(p2+1))) { ddays = atoi(p) * 7; /* weeks... */ if (!*(p2+1)) state = 0; else state = NEED_HRS; } else if ((c == 'M' || c == 'm') && !isalpha(*(p2+1))) { dmonths = atoi(p); /* months... */ if (!*(p2+1)) state = 0; else state = NEED_HRS; } else if ((c == 'Y' || c == 'y') && !isalpha(*(p2+1))) { dyears = atoi(p); /* years... */ if (!*(p2+1)) state = 0; else state = NEED_HRS; } else if (c == ':') { /* delimiter is colon */ dhours = atoi(p); /* so it's hours */ state = NEED_MINS; /* now we want minutes */ } else if (!c) { /* end of string */ dhours = atoi(p); /* it's still hours */ state = 0; /* and we're done */ } else if (isalpha(c) || c == SP) { if (c == SP) { /* It's a keyword? */ p2++; /* Skip spaces */ while (*p2 == SP) p2++; } else { /* or replace first letter */ *p2 = c; } p3 = p2; /* p2 points to beginning of keyword */ while (isalpha(*p3)) /* Find end of keyword */ p3++; c = *p3; /* NUL it out so we can look it up */ if (*p3) /* p3 points to keyword terminator */ *p3 = NUL; units = lookup(timeunits,p2,nunits,NULL); if (units < 0) { makestr(&cmdatemsg,"Invalid units in delta time"); debug(F111,"cmcvtdate",cmdatemsg,-1); return(NULL); } *p2 = NUL; /* Re-terminate the number */ *p3 = c; while (*p3 == SP) /* Point at field after units */ p3++; p2 = p3; switch (units) { case TU_DAYS: ddays = atoi(p); break; case TU_WEEKS: ddays = atoi(p) * 7; break; case TU_MONTHS: dmonths = atoi(p); break; case TU_YEARS: dyears = atoi(p); break; } if (*p2) { state = NEED_HRS; p2--; } else state = 0; } else { /* Anything else */ state = -1; /* is an error */ } break; case NEED_HRS: /* Looking for hours */ debug(F000,"cmcvtdate NEED_HRS",p,c); if (c == ':') { dhours = atoi(p); state = NEED_MINS; } else if (!c) { dhours = atoi(p); state = 0; } else { state = -1; } break; case NEED_MINS: /* Looking for minutes */ if (c == ':') { dmins = atoi(p); state = NEED_SECS; } else if (!c) { dmins = atoi(p); state = 0; } else { state = -1; } break; case NEED_SECS: /* Looking for seconds */ if (c == '.') { dsecs = atoi(p); state = NEED_FRAC; } else if (!c) { dsecs = atoi(p); state = 0; } else { state = -1; } break; case NEED_FRAC: /* Fraction of second */ if (!c && rdigits(p)) { if (*p > '4') dsecs++; state = 0; } else { state = -1; } break; } if (c) /* next field if any */ p = p2 + 1; } havedelta = 1; } else { makestr(&cmdatemsg,"Extraneous material at end"); debug(F111,"cmcvtdate",cmdatemsg,-1); return(NULL); } } xcmdate: if ((t != 2 && hh > 24) || hh < 0) { /* Hour range check */ makestr(&cmdatemsg,"Invalid hours"); debug(F111,"cmcvtdate",cmdatemsg,-1); return(NULL); } if (mm > 59) { /* Minute range check */ makestr(&cmdatemsg,"Invalid minutes"); debug(F111,"cmcvtdate",cmdatemsg,-1); return(NULL); } if (ff > 0) { /* Fraction of second? */ if (ss < 59) { ss++; ff = 0; } else if (mm < 59) { ss = 0; mm++; ff = 0; } else if (hh < 24) { ss = 0; mm = 0; hh++; ff = 0; } /* Must add a day -- leave ff at 1... */ /* (DO SOMETHING ABOUT THIS LATER) */ } if (ss > 60) { /* Seconds range check */ makestr(&cmdatemsg,"Invalid seconds"); /* 60 is ok because of */ debug(F111,"cmcvtdate",cmdatemsg,-1); /* Leap Second. */ return(NULL); } if ((mm < 0 || ss < 0) || (t != 2 && (ss > 0 || mm > 0) && hh > 23)) { makestr(&cmdatemsg,"Invalid minutes or seconds"); debug(F111,"cmcvtdate",cmdatemsg,-1); return(NULL); } debug(F110,"cmcvtdate year",year,0); debug(F110,"cmcvtdate month",month,0); debug(F101,"cmcvtdate nday","",nday); debug(F101,"cmcvtdate hh","",hh); debug(F101,"cmcvtdate mm","",mm); debug(F101,"cmcvtdate ss","",ss); debug(F101,"cmcvtdate gmtsign","",gmtsign); debug(F101,"cmcvtdate zhh","",zhh); debug(F101,"cmcvtdate zmm","",zmm); debug(F101,"cmcvtdate isgmt","",isgmt); #ifdef ZLOCALTIME /* Handle timezone -- first convert to GMT */ zdd = 0; /* Days changed */ if (isgmt && (zmm || zhh)) { /* If GMT offset given */ long sec1, sec2, zz; sec1 = ss + 60 * mm + 3600 * hh; sec2 = gmtsign * (60 * zmm + 3600 * zhh); sec1 += sec2; if (sec1 < 0) { sec1 = 0 - sec1; zdd = 0L - (sec1 / 86400L); sec1 = sec1 % 86400L; } else if (sec1 > 86400L) { zdd = sec1 / 86400L; sec1 = sec1 % 86400L; } ss = sec1 % 60; zz = sec1 / 60; mm = zz % 60; hh = zz / 60; debug(F101,"cmcvtdate NEW hh","",hh); debug(F101,"cmcvtdate NEW mm","",mm); debug(F101,"cmcvtdate NEW dd","",zdd); /* At this point hh:mm:ss is in GMT and zdd is the calendar adjustment */ } #endif /* ZLOCALTIME */ if (yyyymmdd[0] && !year) { ckstrncpy(yearbuf,yyyymmdd,5); ckstrncpy(monbuf,&yyyymmdd[4],3); ckstrncpy(daybuf,&yyyymmdd[6],3); year = yearbuf; month = monbuf; day = daybuf; nday = atoi(daybuf); } sprintf(zbuf,"%04d%02d%02d %02d:%02d:%02d", /* SAFE */ atoi(year),atoi(month),nday,hh,mm,ss ); dp = zbuf; #ifdef ZLOCALTIME /* Now convert from GMT to local time */ if (isgmt) { /* If GMT convert to local time */ debug(F110,"cmcvtdate GMT 1",dp,0); if (zdd) { /* Apply any calendar adjustment */ long zz; zz = mjd(dp) + zdd; sprintf(zbuf,"%s %02d:%02d:%02d",mjd2date(zz),hh,mm,ss); } debug(F110,"cmcvtdate GMT 2",dp,0); if ((p = zlocaltime(dp))) { debug(F110,"cmcvtdate asctime zlocaltime",p,0); if (p) ckstrncpy(zbuf,p,18); } debug(F110,"cmcvtdate GMT 3",dp,0); for (i = 0; i < 4; i++) yearbuf[i] = dp[i]; yearbuf[4] = NUL; monbuf[0] = dp[4]; monbuf[1] = dp[5]; monbuf[2] = NUL; daybuf[0] = dp[6]; daybuf[1] = dp[7]; daybuf[2] = NUL; day = daybuf; nday = atoi(daybuf); month = monbuf; year = yearbuf; hh = atoi(&dp[9]); mm = atoi(&dp[12]); ss = atoi(&dp[15]); } #endif /* ZLOCALTIME */ #ifdef DEBUG if (deblog) { debug(F101,"cmcvtdate hour","",hh); debug(F101,"cmcvtdate minute","",mm); debug(F101,"cmcvtdate second","",ss); } #endif /* DEBLOG */ makestr(&cmdatemsg,NULL); if (havedelta) { #ifdef DEBUG if (deblog) { debug(F110,"cmcvtdate base ",dp,0); debug(F101,"cmcvtdate delta sign","",dsign); debug(F101,"cmcvtdate delta yrs ","",dyears); debug(F101,"cmcvtdate delta mos ","",dmonths); debug(F101,"cmcvtdate delta days","",ddays); debug(F101,"cmcvtdate delta hrs ","",dhours); debug(F101,"cmcvtdate delta mins","",dmins); debug(F101,"cmcvtdate delta secs","",dsecs); } #endif /* DEBLOG */ if (!(dp = cmdelta(atoi(year), atoi(month), nday, hh, mm, ss, dsign, dyears, dmonths, ddays, dhours, dmins, dsecs))) { debug(F111,"cmcvtdate",cmdatemsg,-1); return(NULL); } } xcvtdate: /* Exit point for success */ { int len, k, n; char * p; debug(F110,"cmcvtdate xcvtdate dp",dp,0); if (!dp) dp = ""; /* Shouldn't happen */ if (!*dp) return(NULL); /* ... */ len = strlen(dp); debug(F111,"cmcvtdate result",dp,len); k = cmdatebp - (char *)cmdatebuf; /* Space used */ n = CMDATEBUF - k - 1; /* Space left */ if (n < len) { /* Not enough? */ cmdatebp = cmdatebuf; /* Wrap around */ n = CMDATEBUF; } ckstrncpy(cmdatebp,dp,n); p = cmdatebp; cmdatebp += len + 1; return(p); } } int cmvdate(d) char * d; { /* Verify date-time */ int i; if (!d) return(0); if ((int)strlen(d) != 17) return(0); for (i = 0; i < 8; i++) { if (!isdigit(d[i])) return(0); } if (!isdigit(d[9]) || !isdigit(d[10]) || !isdigit(d[12]) || !isdigit(d[13]) || !isdigit(d[15]) || !isdigit(d[16])) return(0); if (!ckstrchr(" Tt_-:",d[8])) return(0); if (d[11] != ':' && d[14] != ':') return(0); return(1); } /* c m d i f f d a t e -- Get difference between two date-times */ char * cmdiffdate(d1,d2) char * d1, * d2; { char d1buf[9], d2buf[9]; char x1buf[18], x2buf[18]; char * p; int hh1 = 0, mm1 = 0, ss1 = 0; int hh2 = 0, mm2 = 0, ss2 = 0; int hh, mm, ss; int sign; long jd1, jd2, jd, f1, f2, fx; static char result[24], *rp; debug(F110,"cmdiffdate d1 A",d1,0); debug(F110,"cmdiffdate d2 A",d2,0); if (!(p = cmcvtdate(d1,1))) /* Convert dates to standard format */ return(NULL); ckstrncpy(x1buf,p,18); d1 = x1buf; if (!(p = cmcvtdate(d2,1))) return(NULL); ckstrncpy(x2buf,p,18); d2 = x2buf; debug(F110,"cmdiffdate d1 B",d1,0); debug(F110,"cmdiffdate d2 B",d2,0); if (!cmvdate(d1) || !cmvdate(d2)) return(NULL); hh1 = atoi(&d1[9]); /* Get hours, minutes, and seconds */ mm1 = atoi(&d1[12]); /* for first date */ ss1 = atoi(&d1[15]); ckstrncpy(d1buf,d1,9); hh2 = atoi(&d2[9]); /* ditto for second date */ mm2 = atoi(&d2[12]); ss2 = atoi(&d2[15]); ckstrncpy(d2buf,d2,9); jd1 = mjd(d1buf); /* Get the two Julian dates */ jd2 = mjd(d2buf); f1 = ss1 + 60 * mm1 + 3600 * hh1; /* Convert first time to seconds */ f2 = ss2 + 60 * mm2 + 3600 * hh2; /* Ditto for second time */ debug(F101,"cmdiffdate jd1","",jd1); debug(F101,"cmdiffdate f1","",f1); debug(F101,"cmdiffdate jd2","",jd2); debug(F101,"cmdiffdate f2","",f2); if (jd2 > jd1 || (jd1 == jd2 && f2 > f1)) { sign = -1; if (f1 > f2) {jd2--; f2 += 86400L;} jd = jd2 - jd1; fx = f2 - f1; } else { sign = 1; if (f2 > f1) {jd1--; f1 += 86400L;} jd = jd1 - jd2; fx = f1 - f2; } debug(F111,"cmdiffdate sign jd",sign<0?"-":"+",jd); debug(F101,"cmdiffdate fx","",fx); hh = (int) (fx / 3600L); /* Convert seconds to hh:mm:ss */ mm = (int) (fx % 3600L) / 60L; ss = (int) (fx % 3600L) % 60L; rp = result; /* Format the result */ *rp++ = (sign < 0) ? '-' : '+'; if (jd != 0 && hh+mm+ss == 0) { sprintf(rp,"%ldd",jd); } else if (jd == 0) { if (ss == 0) sprintf(rp,"%d:%02d",hh,mm); else sprintf(rp,"%d:%02d:%02d",hh,mm,ss); } else { if (ss == 0) sprintf(rp,"%ldd%d:%02d",jd,hh,mm); else sprintf(rp,"%ldd%d:%02d:%02d",jd,hh,mm,ss); } debug(F110,"cmdiffdate result",result,0); return((char *)result); } #ifndef NOSPL /* s h u f f l e d a t e -- Rearrange date string */ /* Call with: A date string in standard format: yyyymmdd hh:mm:ss (time optional). Options: 1: Reformat date to yyyy-mmm-dd (mmm = English month abbreviation). 2: Reformat date to dd-mmm-yyyy (mmm = English month abbreviation). 3: Reformat as numeric yyyymmddhhmmss. 4: Reformat in asctime() format Sat Nov 26 11:10:34 2005 Returns: Pointer to result if args valid, otherwise original arg pointer. */ char * shuffledate(p,opt) char * p; int opt; { extern char * wkdays[]; int len; char ibuf[32]; static char obuf[48]; char c; int yy, dd, mm; if (!p) p = ""; if (!*p) p = ckdate(); if (opt < 1 || opt > 4) return(p); len = strlen(p); if (len < 8 || len > 31) return(p); if (opt == 4) { /* Asctime format (26 Nov 2005) */ char c, * s; long z; int k; ckstrncpy(ibuf,p,31); k = len; while (k >= 0 && ibuf[k] == CR || ibuf[k] == LF) ibuf[k--] = NUL; while (k >= 0 && ibuf[k] == SP || ibuf[k] == HT) ibuf[k--] = NUL; if (k < 9) ckstrncpy(&ibuf[8]," 00:00:00",9); p = ibuf; z = mjd(p); /* Convert to modified Julian date */ z = z % 7L; if (z < 0) { z = 0 - z; k = 6 - ((int)z + 3) % 7; } else { k = ((int)z + 3) % 7; /* Day of week */ } s = wkdays[k]; obuf[0] = s[0]; /* Day of week */ obuf[1] = s[1]; obuf[2] = s[2]; obuf[3] = SP; /* Space */ c = p[6]; p[6] = NUL; mm = atoi(&ibuf[4]); /* Month */ s = moname[mm-1]; /* Name of month */ p[6] = c; obuf[4] = s[0]; /* Month */ obuf[5] = s[1]; obuf[6] = s[2]; obuf[7] = SP; /* Space */ if (p[6] == '0') /* Date of month */ obuf[8] = SP; else obuf[8] = p[6]; obuf[9] = p[7]; ckstrncpy(&obuf[10],&p[8],10); /* Time */ obuf[19] = SP; /* Space */ obuf[20] = p[0]; /* Year */ obuf[21] = p[1]; obuf[22] = p[2]; obuf[23] = p[3]; obuf[24] = NUL; return((char *)obuf); } if (opt == 3) { ckstrncpy(obuf,p,48); /* yyyymmdd hh:mm:ss */ /* 01234567890123456 */ /* yyyymmddhhmmss */ obuf[8] = obuf[9]; obuf[9] = obuf[10]; obuf[10] = obuf[12]; obuf[11] = obuf[13]; obuf[12] = obuf[15]; obuf[13] = obuf[16]; obuf[14] = NUL; return((char *)obuf); } ckstrncpy(ibuf,p,32); c = ibuf[4]; /* Warning: not Y10K compliant */ ibuf[4] = NUL; if (!rdigits(ibuf)) return(p); yy = atoi(ibuf); if (yy < 1 || yy > 9999) return(p); ibuf[4] = c; c = ibuf[6]; ibuf[6] = NUL; if (!rdigits(&ibuf[4])) return(p); mm = atoi(&ibuf[4]); if (mm < 1 || mm > 12) return(p); ibuf[6] = c; c = ibuf[8]; ibuf[8] = NUL; if (!rdigits(&ibuf[6])) return(p); dd = atoi(&ibuf[6]); ibuf[8] = c; if (dd < 1 || mm > 31) return(p); /* IGNORE WARNINGS ABOUT moname[] REFS OUT OF RANGE - it's prechecked. */ switch (opt) { case 1: sprintf(obuf,"%04d-%s-%02d%s",yy,moname[mm-1],dd,&ibuf[8]); break; case 2: sprintf(obuf,"%02d-%s-%04d%s",dd,moname[mm-1],yy,&ibuf[8]); } return((char *)obuf); } #endif /* NOSPL */ /* C K C V T D A T E -- Like cmcvtdate(), but returns string. */ /* For use by date-related functions */ /* See calling conventions for cmcvtdate() above. */ char * ckcvtdate(p,t) char * p; int t; { char * s; if (!(s = cmcvtdate(p,t))) return(""); /* \fblah() error message */ else return(s); } /* C M D A T E -- Parse a date and/or time */ /* Accepts date in various formats. If the date is recognized, this routine returns 0 or greater with the result string pointer pointing to a buffer containing the date as "yyyymmdd hh:mm:ss". */ int cmdate(xhlp,xdef,xp,quiet,f) char *xhlp, *xdef, **xp; int quiet; xx_strp f; { int x, rc; char *o, *s, *zq, *dp; cmfldflgs = 0; if (!xhlp) xhlp = ""; if (!xdef) xdef = ""; if (!*xhlp) xhlp = "Date and/or time"; *xp = ""; rc = cmfld(xhlp,xdef,&s,(xx_strp)0); debug(F101,"cmdate cmfld rc","",rc); if (rc < 0) return(rc); debug(F110,"cmdate 1",s,0); o = s; /* Remember what they typed. */ s = brstrip(s); debug(F110,"cmdate 2",s,0); x = 0; if (f) { /* If a conversion function is given */ char * pp; zq = atxbuf; /* do the conversion. */ pp = atxbuf; atxn = CMDBL; if ((x = (*f)(s,&zq,&atxn)) < 0) return(-2); if (!*pp) pp = xdef; if (setatm(pp,0) < 0) { if (!quiet) printf("?Evaluated date too long\n"); return(-9); } s = atxbuf; } dp = cmcvtdate(s,1); if (!dp) { if (!quiet) printf("?%s\n",cmdatemsg); return(-9); } *xp = dp; return(0); } #ifdef CK_RECALL /* Command-recall functions */ /* C M R I N I -- Initialize or change size of command recall buffer */ int cmrini(n) int n; { int i; if (recall && in_recall) { /* Free old storage, if any */ for (i = 0; i < cm_recall; i++) { if (recall[i]) { free(recall[i]); recall[i] = NULL; } } free(recall); recall = NULL; } cm_recall = n; /* Set new size */ rlast = current = -1; /* Initialize pointers */ if (n > 0) { recall = (char **)malloc((cm_recall + 1) * sizeof(char *)); if (!recall) return(1); for (i = 0; i < cm_recall; i++) { recall[i] = NULL; } in_recall = 1; /* Recall buffers init'd */ } return(0); } /* C M A D D N E X T -- Force addition of next command */ VOID cmaddnext() { if (on_recall && in_recall) { /* Even if it doesn't come */ force_add = 1; /* from the keyboard */ newcmd = 1; no_recall = 0; } } /* C M G E T C M D -- Find most recent matching command */ char * cmgetcmd(s) char * s; { int i; for (i = current; i >= 0; i--) { /* Search backward thru history list */ if (!recall[i]) continue; /* This one's null, skip it */ if (ckmatch(s,recall[i],0,1)) /* Match? */ return(recall[i]); /* Yes, return pointer */ } return(NULL); /* No match, return NULL pointer */ } #endif /* CK_RECALL */ /* A D D C M D -- Add a command to the recall buffer */ VOID addcmd(s) char * s; { int len = 0, nq = 0; char * p; #ifdef CKLEARN extern int learning; #endif /* CKLEARN */ if (xcmdsrc) /* Only for interactive commands */ return; if (!newcmd) /* The command has been here already */ return; /* so ignore it. */ newcmd = 0; /* It's new but do this only once. */ if (!s) s = cmdbuf; if (s[0]) len = strlen(s); if (len < 1) /* Don't save empty commands */ return; p = s; while (*p) { if (*p++ == '?') nq++; } /* Count question marks */ #ifdef CKLEARN if (learning) /* If a learned script is active */ learncmd(s); /* record this command. */ #endif /* CKLEARN */ debug(F010,"CMD(P)",s,0); /* Maybe record it in the debug log */ #ifdef CKSYSLOG if (ckxlogging) { /* Maybe record it in syslog */ if (ckxsyslog >= SYSLG_CX || ckxsyslog >= SYSLG_CM) cksyslog(SYSLG_CX, 1, "command", s, NULL); } #endif /* CKSYSLOG */ #ifdef CK_RECALL last_recall = 0; if (on_recall && /* Command recall is on? */ cm_recall > 0 && /* Recall buffer size is > 0? */ !no_recall) { /* Not not saving this command? */ if (!force_add && rlast > -1) /* If previous command was identical */ if (!strcmp(s,recall[rlast])) /* don't add another copy */ return; force_add = 0; /* Reset now in case it was set */ if (rlast >= cm_recall - 1) { /* Recall buffer full? */ int i; if (recall[0]) { /* Discard oldest command */ free(recall[0]); recall[0] = NULL; } for (i = 0; i < rlast; i++) { /* The rest */ recall[i] = recall[i+1]; /* move back */ } rlast--; /* Now we have one less */ } rlast++; /* Index of last command in buffer */ current = rlast; /* Also now the current command */ if (current >= cm_recall) { /* Shouldn't happen */ printf("?Command history error\n"); /* but if it does */ on_recall = 0; /* turn off command saving */ #ifdef COMMENT } else if (nq > 0) { /* Have at least one question mark */ recall[current] = malloc(len+nq+1); if (recall[current]) { p = recall[current]; while (*s) { if (*s == '?') *p++ = '\\'; *p++ = *s++; } *p = NUL; } #endif /* COMMENT */ } else { /* Normal case, just copy */ recall[current] = malloc(len+1); if (recall[current]) ckstrncpy(recall[current],s,len+1); } } #endif /* CK_RECALL */ } #ifdef CK_RECALL /* C M H I S T O R Y */ VOID cmhistory() { int i, lc = 1; for (i = 0; i <= current; i++) { printf(" %s\n", recall[i]); if (++lc > (cmd_rows - 2)) { /* Screen full? */ if (!askmore()) /* Do more-prompting... */ break; else lc = 0; } } } int savhistory(s,disp) char *s; int disp; { FILE * fp; int i; fp = fopen(s, disp ? "a" : "w"); if (!fp) { perror(s); return(0); } for (i = 0; i <= current; i++) fprintf(fp,"%s\n", recall[i]); fclose(fp); return(1); } #endif /* CK_RECALL */ #ifdef COMMENT /* apparently not used */ int cmgetlc(s) char * s; { /* Get leading char */ char c; while ((c = *s++) <= SP) { if (!c) break; } return(c); } #endif /* COMMENT */ /* C M C F M -- Parse command confirmation (end of line) */ /* Returns -2: User typed anything but whitespace or newline -1: Reparse needed 0: Confirmation was received */ int cmcfm() { int x, xc; debug(F101,"cmcfm: cmflgs","",cmflgs); debug(F110,"cmcfm: atmbuf",atmbuf,0); inword = xc = cc = 0; setatm("",0); /* (Probably unnecessary) */ while (cmflgs != 1) { x = gtword(0); xc += cc; switch (x) { case -9: printf("Command or field too long\n"); case -4: /* EOF */ case -2: case -1: return(x); case 1: /* End of line */ if (xc > 0) { if (xcmfdb) { return(-6); } else { printf("?Not confirmed - %s\n",atmbuf); return(-9); } } else break; /* Finish up below */ case 2: /* ESC */ if (xc == 0) { bleep(BP_WARN); continue; /* or fall thru. */ } case 0: /* Space */ if (xc == 0) /* If no chars typed, continue, */ continue; /* else fall thru. */ /* else fall thru... */ case 3: /* Question mark */ if (xc > 0) { if (xcmfdb) { return(-6); } else { printf("?Not confirmed - %s\n",atmbuf); return(-9); } } printf( "\n Press the Return or Enter key to confirm the command\n"); printf("%s%s",cmprom,cmdbuf); fflush(stdout); continue; } } debok = 1; return(0); } /* The following material supports chained parsing functions. */ /* See ckucmd.h for FDB and OFDB definitions. */ struct OFDB cmresult = { /* Universal cmfdb result holder */ NULL, /* Address of succeeding FDB struct */ 0, /* Function code */ NULL, /* String result */ 0, /* Integer result */ (CK_OFF_T)0 /* Wide result */ }; VOID cmfdbi(p,fc,s1,s2,s3,n1,n2,f,k,nxt) /* Initialize an FDB */ struct FDB * p; int fc; char * s1, * s2, * s3; int n1, n2; xx_strp f; struct keytab * k; struct FDB * nxt; { p->fcode = fc; p->hlpmsg = s1; p->dflt = s2; p->sdata = s3; p->ndata1 = n1; p->ndata2 = n2; p->spf = f; p->kwdtbl = k; p->nxtfdb = nxt; } /* C M F D B -- Parse a field with several possible functions */ int cmfdb(fdbin) struct FDB * fdbin; { #ifndef NOSPL extern int x_ifnum; /* IF NUMERIC - disables warnings */ #endif /* NOSPL */ struct FDB * in = fdbin; struct OFDB * out = &cmresult; int x = 0, n, r; CK_OFF_T w = (CK_OFF_T)0; char *s, *xp, *m = NULL; int errbits = 0; xp = bp; out->fcode = -1; /* Initialize output struct */ out->fdbaddr = NULL; out->sresult = NULL; out->nresult = 0; /* Currently we make one trip through the FDBs. So if the user types Esc or Tab at the beginning of a field, only the first FDB is examined for a default. If the user types ?, help is given only for one FDB. We should search through the FDBs for all matching possibilities -- and in particular display the pertinent context-sensitive help for each function, rather than the only the first one that works, and then rewind the FDB pointer so we are not locked out of the earlier ones. */ cmfldflgs = 0; while (1) { /* Loop through the chain of FDBs */ nomsg = 1; xcmfdb = 1; s = NULL; n = 0; debug(F101,"cmfdb in->fcode","",in->fcode); switch (in->fcode) { /* Current parsing function code */ case _CMNUM: r = in->ndata1; if (r != 10 && r != 8) r = 10; #ifndef NOSPL x_ifnum = 1; /* Disables warning messages */ #endif /* NOSPL */ x = cmnum(in->hlpmsg,in->dflt,r,&n,in->spf); #ifndef NOSPL x_ifnum = 0; #endif /* NOSPL */ debug(F101,"cmfdb cmnum","",x); if (x < 0) errbits |= 1; break; case _CMNUW: /* Wide cmnum - 24 Dec 2005 */ r = in->ndata1; if (r != 10 && r != 8) r = 10; #ifndef NOSPL x_ifnum = 1; /* Disables warning messages */ #endif /* NOSPL */ x = cmnumw(in->hlpmsg,in->dflt,r,&w,in->spf); #ifndef NOSPL x_ifnum = 0; #endif /* NOSPL */ debug(F101,"cmfdb cmnumw","",w); if (x < 0) errbits |= 1; break; case _CMOFI: x = cmofi(in->hlpmsg,in->dflt,&s,in->spf); debug(F101,"cmfdb cmofi","",x); if (x < 0) errbits |= 2; break; case _CMIFI: x = cmifi2(in->hlpmsg, in->dflt, &s, &n, in->ndata1, in->sdata, in->spf, in->ndata2 ); debug(F101,"cmfdb cmifi2 x","",x); debug(F101,"cmfdb cmifi2 n","",n); if (x < 0) errbits |= 4; break; case _CMFLD: cmfldflgs = in->ndata1; x = cmfld(in->hlpmsg,in->dflt,&s,in->spf); debug(F101,"cmfdb cmfld","",x); if (x < 0) errbits |= 8; break; case _CMTXT: x = cmtxt(in->hlpmsg,in->dflt,&s,in->spf); debug(F101,"cmfdb cmtxt","",x); if (x < 0) errbits |= 16; break; case _CMKEY: x = cmkey2(in->kwdtbl, in->ndata1, in->hlpmsg,in->dflt,in->sdata,in->spf,in->ndata2); debug(F101,"cmfdb cmkey","",x); if (x < 0) errbits |= ((in->ndata2 & 4) ? 32 : 64); break; case _CMCFM: x = cmcfm(); debug(F101,"cmfdb cmcfm","",x); if (x < 0) errbits |= 128; break; default: debug(F101,"cmfdb - unexpected function code","",in->fcode); printf("?cmfdb - unexpected function code: %d\n",in->fcode); } debug(F101,"cmfdb x","",x); debug(F101,"cmfdb cmflgs","",cmflgs); debug(F101,"cmfdb crflag","",crflag); debug(F101,"cmfdb qmflag","",qmflag); debug(F101,"cmfdb esflag","",esflag); if (x > -1) { /* Success */ out->fcode = in->fcode; /* Fill in output struct */ out->fdbaddr = in; out->sresult = s; out->nresult = (in->fcode == _CMKEY) ? x : n; out->wresult = w; out->kflags = (in->fcode == _CMKEY) ? cmkwflgs : 0; debug(F111,"cmfdb out->nresult",out->sresult,out->nresult); debug(F111,"cmfdb out->wresult",out->sresult,out->wresult); nomsg = 0; xcmfdb = 0; /* debug(F111,"cmfdb cmdbuf & crflag",cmdbuf,crflag); */ if (crflag) { cmflgs = 1; } return(x); /* and return */ } in = in->nxtfdb; /* Failed, get next parsing function */ nomsg = 0; xcmfdb = 0; if (!in) { /* No more */ debug(F101,"cmfdb failure x","",x); debug(F101,"cmfdb failure errbits","",errbits); if (x == -6) x = -9; if (x == -9) { #ifdef CKROOT if (ckrooterr) m = "Off Limits"; else #endif /* CKROOT */ /* Make informative messages for a few common cases */ switch (errbits) { case 4+32: m = "Does not match filename or switch"; break; case 4+64: m = "Does not match filename or keyword"; break; case 1+32: m = "Not a number or valid keyword"; break; case 1+64: m = "Not a number or valid switch"; break; default: m = "Not valid in this position"; } printf("?%s: \"%s\"\n",m, atmbuf); } return(x); } if (x != -2 && x != -6 && x != -9 && x != -3) /* Editing or somesuch */ return(x); /* Go back and reparse */ pp = np = bp = xp; /* Back up pointers */ cmflgs = -1; /* Force a reparse */ #ifndef NOSPL if (!askflag) { /* If not executing ASK-class cmd... */ #endif /* NOSPL */ if (crflag) { /* If CR was typed, put it back */ pushc = LF; /* But as a linefeed */ } else if (qmflag) { /* Ditto for Question mark */ pushc = '?'; } else if (esflag) { /* and Escape or Tab */ pushc = ESC; } #ifndef NOSPL } #endif /* NOSPL */ } } /* C M I O F I -- Parse an input file OR the name of a nonexistent file. Replaces the commented-out version above. This one actually works and has the expected straightforward interface. */ int cmiofi(xhlp,xdef,xp,wild,f) char *xhlp, *xdef, **xp; int *wild; xx_strp f; { int x; struct FDB f1, f2; cmfdbi(&f1,_CMIFI,xhlp,xdef,"",0,0,f,NULL,&f2); cmfdbi(&f2,_CMOFI,"","","",0,0,f,NULL,NULL); x = cmfdb(&f1); if (x < 0) { if (x == -3) { x = -9; printf("?Filename required\n"); } } *wild = cmresult.nresult; *xp = cmresult.sresult; return(x); } /* G T W O R D -- Gets a "word" from the command input stream */ /* Usage: retcode = gtword(brk); brk = 0 for normal word breaks (space, CR, Esc, ?) brk = 1 to add ':' and '=' (for parsing switches). These characters act as break characters only if the first character of the field is slash ('/'), i.e. switch introducer. brk = 4 to not strip comments (used only for "help #" and "help ;"). Returns: -10 Timelimit set and timed out -9 if input was too long -4 if end of file (e.g. pipe broken) -3 if null field -2 if command buffer overflows -1 if user did some deleting 0 if word terminates with SP or tab 1 if ... CR 2 if ... ESC 3 if ... ? (question mark) 4 if ... : or = and called with brk != 0 With: pp pointing to beginning of word in buffer bp pointing to after current position atmbuf containing a copy of the word cc containing the number of characters in the word copied to atmbuf */ int ungword() { /* Unget a word */ debug(F101,"ungword cmflgs","",cmflgs); if (ungw) return(0); cmfsav = cmflgs; ungw = 1; cmflgs = 0; return(0); } /* Un-un-get word. Undo ungword() if it has been done. */ VOID unungw() { debug(F010,"unungw atmbuf",atmbuf,0); if (ungw) { ungw = 0; cmflgs = cmfsav; atmbuf[0] = NUL; } } static int gtword(brk) int brk; { int c; /* Current char */ int quote = 0; /* Flag for quote character */ int echof = 0; /* Flag for whether to echo */ int comment = 0; /* Flag for in comment */ char *cp = NULL; /* Comment pointer */ int eintr = 0; /* Flag for syscall interrupted */ int bracelvl = 0; /* nested brace counter [jrs] */ int iscontd = 0; /* Flag for continuation */ int realtty = 0; /* Stdin is really a tty */ char firstnb = NUL; char lastchar = NUL; char prevchar = NUL; char lbrace, rbrace; int dq = 0; /* Doublequote flag */ int dqn = 0; /* and count */ int isesc = 0; #ifdef RTU extern int rtu_bug; #endif /* RTU */ #ifdef IKSD extern int inserver; #endif /* IKSD */ extern int kstartactive; #ifdef datageneral extern int termtype; /* DG terminal type flag */ extern int con_reads_mt; /* Console read asynch is active */ if (con_reads_mt) connoi_mt(); /* Task would interfere w/cons read */ #endif /* datageneral */ #ifdef COMMENT #ifdef DEBUG if (deblog) { debug(F101,"gtword brk","",brk); debug(F101,"gtword cmfldflgs","",cmfldflgs); debug(F101,"gtword swarg","",swarg); debug(F101,"gtword dpx","",dpx); debug(F101,"gtword echof","",echof); #ifndef NOSPL debug(F101,"gtword askflag","",askflag); debug(F101,"gtword timelimit","",timelimit); #ifndef NOLOCAL #ifndef NOXFER #ifdef CK_AUTODL debug(F101,"gtword cmdadl","",cmdadl); #endif /* CK_AUTODL */ #endif /* NOXFER */ #endif /* NOLOCAL */ #endif /* NOSPL */ } #endif /* DEBUG */ #endif /* COMMENT */ realtty = is_a_tty(0); /* Stdin is really a tty? */ if (cmfldflgs & 1) { lbrace = '('; rbrace = ')'; } else { lbrace = '{'; rbrace = '}'; } crflag = 0; qmflag = 0; esflag = 0; if (swarg) { /* No leading space for switch args */ inword = 1; swarg = 0; } if (ungw) { /* Have a word saved? */ #ifdef M_UNGW /* Experimental code to allow ungetting multiple words. */ /* See comments in ckmkey2() above. */ int x; if (np > pp) pp = np; while (*pp == SP) pp++; if (!*pp) { ungw = 0; cmflgs = cmfsav; } else { if ((x = setatm(pp,2)) < 0) { printf("?Saved word too long\n"); return(-9); } if (pp[x] >= SP) { char *p2; p2 = pp; p2 += x; while (*p2 == SP) p2++; if (*p2) { np = p2; ungword(); } } else { ungw = 0; cmflgs = cmfsav; debug(F010,"gtword ungw return atmbuf",atmbuf,0); } } return(cmflgs); #else /* You would think the following should be: while (*pp == SP) pp++; but you would be wrong -- making this change breaks GOTO. */ while (*pp++ == SP) ; if (setatm(pp,2) < 0) { printf("?Saved word too long\n"); return(-9); } ungw = 0; cmflgs = cmfsav; debug(F010,"gtword ungw return atmbuf",atmbuf,0); return(cmflgs); #endif /* M_UNGW */ } pp = np; /* Start of current field */ #ifdef COMMENT #ifdef DEBUG if (deblog) { debug(F110,"gtword cmdbuf",cmdbuf,0); debug(F110,"gtword bp",bp,0); debug(F110,"gtword pp",pp,0); } #endif /* DEBUG */ #endif /* COMMENT */ { /* If we are reparsing we have to recount any braces or doublequotes */ char * p = pp; char c; if (*p == '"') dq++; while ((c = *p++)) if (c == lbrace) bracelvl++; else if (c == rbrace) bracelvl--; else if (dq && c == '"') dqn++; } while (bp < cmdbuf+CMDBL) { /* Big get-a-character loop */ echof = 0; /* Assume we don't echo because */ chsrc = 0; /* character came from reparse buf. */ #ifdef BS_DIRSEP CMDIRPARSE: #endif /* BS_DIRSEP */ c = *bp; if (!c) { /* If no char waiting in reparse buf */ if ((dpx #ifndef NOSPL || echostars #endif /* NOSPL */ ) && (!pushc #ifndef NOSPL || askflag #endif /* NOSPL */ )) /* Get from tty, set echo flag */ echof = 1; c = cmdgetc(timelimit); /* Read a command character. */ #ifdef DEBUG debug(F101,"gtword c","",c); #endif /* DEBUG */ if (timelimit && c < -1) { /* Timed out */ return(-10); } #ifndef NOXFER /* The following allows packet recognition in the command parser. Presently it works only for Kermit packets, and if our current protocol happens to be anything besides Kermit, we simply force it to Kermit. We don't use the APC mechanism here for mechanical reasons, and also because this way, it works even with minimally configured interactive versions. Add Zmodem later... */ #ifdef CK_AUTODL if ((!local && cmdadl) /* Autodownload enabled? */ #ifdef IKS_OPTION || TELOPT_SB(TELOPT_KERMIT).kermit.me_start #endif /* IKS_OPTION */ ) { int k; k = kstart((CHAR)c); /* Kermit S or I packet? */ if (k) { int ksign = 0; if (k < 0) { /* Minus-Protocol? */ #ifdef NOSERVER goto noserver; /* Need server mode for this */ #else ksign = 1; /* Remember */ k = 0 - k; /* Convert to actual protocol */ justone = 1; /* Flag for protocol module */ #endif /* NOSERVER */ } else justone = 0; k--; /* Adjust kstart's return value */ if (k == PROTO_K) { extern int protocol, g_proto; extern CHAR sstate; g_proto = protocol; protocol = PROTO_K; /* Crude... */ sstate = ksign ? 'x' : 'v'; cmdbuf[0] = NUL; return(-3); } } } #ifdef NOSERVER noserver: #endif /* NOSERVER */ #endif /* CK_AUTODL */ #endif /* NOXFER */ chsrc = 1; /* Remember character source is tty. */ brkchar = c; #ifdef IKSD if (inserver && c < 0) { /* End of session? */ debug(F111,"gtword c < 0","exiting",c); return(-4); /* Cleanup and terminate */ } #endif /* IKSD */ #ifdef OS2 if (c < 0) { /* Error */ if (c == -3) { /* Empty word? */ if (blocklvl > 0) /* In a block */ continue; /* so keep looking for block end */ else return(-3); /* Otherwise say we got nothing */ } else { /* Not empty word */ return(-4); /* So some kind of i/o error */ } } #else #ifdef MAC if (c == -3) /* Empty word... */ if (blocklvl > 0) continue; else return(-3); #endif /* MAC */ #endif /* OS2 */ if (c == EOF) { /* This can happen if stdin not tty. */ #ifdef EINTR /* Some operating and/or C runtime systems return EINTR for no good reason, when the end of the standard input "file" is encountered. In cases like this, we get into an infinite loop; hence the eintr counter, which is reset to 0 upon each call to this routine. */ debug(F101,"gtword EOF","",errno); if (errno == EINTR && ++eintr < 4) /* When bg'd process is */ continue; /* fg'd again. */ #endif /* EINTR */ return(-4); } c &= cmdmsk; /* Strip any parity bit */ } /* if desired. */ /* Now we have the next character */ isesc = (c == ESC); /* A real ESC? */ if (!firstnb && c > SP) { /* First nonblank */ firstnb = c; if (c == '"') /* Starts with doublequote */ dq = 1; } if (c == '"') /* Count doublequotes */ dqn++; if (quote && (c == CR || c == LF)) { /* Enter key following quote */ *bp++ = CMDQ; /* Double it */ *bp = NUL; quote = 0; } if (quote == 0) { /* If this is not a quoted character */ switch (c) { case CMDQ: /* Got the quote character itself */ if (!comment && quoting) quote = 1; /* Flag it if not in a comment */ break; case FF: /* Formfeed. */ c = NL; /* Replace with newline */ cmdclrscn(); /* Clear the screen */ break; case HT: /* Horizontal Tab */ if (comment) /* If in comment, */ c = SP; /* substitute space */ else /* otherwise */ c = ESC; /* substitute ESC (for completion) */ break; case ';': /* Trailing comment */ case '#': if (! (brk & 4) ) { /* If not keeping comments */ if (inword == 0 && quoting) { /* If not in a word */ comment = 1; /* start a comment. */ cp = bp; /* remember where it starts. */ } } break; } if (!kstartactive && /* Not in possible Kermit packet */ !comment && c == SP) { /* Space not in comment */ *bp++ = (char) c; /* deposit in buffer if not already */ /* debug(F101,"gtword echof 2","",echof); */ #ifdef BEBOX if (echof) { cmdecho((char) c, 0); /* Echo what was typed. */ fflush(stdout); fflush(stderr); } #else if (echof) { cmdecho((char) c, 0); /* Echo what was typed. */ if (timelimit) fflush(stdout); } #endif /* BEBOX */ if (inword == 0) { /* If leading, gobble it. */ pp++; continue; } else { /* If terminating, return. */ if ((!dq && ((*pp != lbrace) || (bracelvl == 0))) || (dq && dqn > 1 && *(bp-2) == '"')) { np = bp; cmbptr = np; if (setatm(pp,0) < 0) { printf("?Field too long error 1\n"); debug(F111,"gtword too long #1",pp,strlen(pp)); return(-9); } brkchar = c; inword = cmflgs = 0; return(0); } continue; } } if (c == lbrace) { bracelvl++; /* debug(F101,"gtword bracelvl++","",bracelvl); */ } if (c == rbrace && bracelvl > 0) { bracelvl--; /* debug(F101,"gtword bracelvl--","",bracelvl); */ if (linebegin) blocklvl--; } if ((c == '=' || c == ':') && /* ^^^ */ !kstartactive && !comment && brk /* && (firstnb == '/') */ ) { *bp++ = (char) c; /* Switch argument separator */ /* debug(F111,"gtword switch argsep",cmdbuf,brk); */ #ifdef BEBOX if (echof) { cmdecho((char) c, 0); /* Echo what was typed. */ fflush(stdout); fflush(stderr); } #else if (echof) { cmdecho((char) c, 0); /* Echo what was typed. */ if (timelimit) fflush(stdout); } #endif /* BEBOX */ if ((*pp != lbrace) || (bracelvl == 0)) { np = bp; cmbptr = np; if (setatm(pp,2) < 0) { /* ^^^ */ printf("?Field too long error 1\n"); debug(F111,"gtword too long #1",pp,strlen(pp)); return(-9); } inword = cmflgs = 0; brkchar = c; return(4); } } if (c == LF || c == CR) { /* CR or LF. */ if (echof) { cmdnewl((char)c); /* echo it. */ #ifdef BEBOX fflush(stdout); fflush(stderr); #endif /* BEBOX */ } { /* Trim trailing comment and whitespace */ char *qq; if (comment) { /* Erase comment */ while (bp >= cp) /* Back to comment pointer */ *bp-- = NUL; bp++; pp = bp; /* Adjust other pointers */ inword = 0; /* and flags */ comment = 0; cp = NULL; } qq = inword ? pp : (char *)cmdbuf; /* Erase trailing whitespace */ while (bp > qq && (*(bp-1) == SP || *(bp-1) == HT)) { bp--; /* debug(F000,"erasing","",*bp); */ *bp = NUL; } lastchar = (bp > qq) ? *(bp-1) : NUL; prevchar = (bp > qq+1) ? *(bp-2) : NUL; } if (linebegin && blocklvl > 0) /* Blank line in {...} block */ continue; linebegin = 1; /* At beginning of next line */ iscontd = prevchar != CMDQ && (lastchar == '-' || lastchar == lbrace); debug(F101,"gtword iscontd","",iscontd); if (iscontd) { /* If line is continued... */ if (chsrc) { /* If reading from tty, */ if (*(bp-1) == lbrace) { /* Check for "begin block" */ *bp++ = SP; /* Insert a space for neatness */ blocklvl++; /* Count block nesting level */ } else { /* Or hyphen */ bp--; /* Overwrite the hyphen */ } *bp = NUL; /* erase the dash, */ continue; /* and go back for next char now. */ } } else if (blocklvl > 0) { /* No continuation character */ if (chsrc) { /* But we're in a "block" */ *bp++ = ','; /* Add comma */ *bp = NUL; continue; } } else { /* No continuation, end of command. */ *bp = NUL; /* Terminate the command string. */ if (comment) { /* If we're in a comment, */ comment = 0; /* Say we're not any more, */ *cp = NUL; /* cut it off. */ } np = bp; /* Where to start next field. */ cmbptr = np; if (setatm(pp,0) < 0) { /* Copy field to atom buffer */ debug(F111,"gtword too long #2",pp,strlen(pp)); printf("?Field too long error 2\n"); return(-9); } inword = 0; /* Not in a word any more. */ crflag = 1; /* debug(F110,"gtword","crflag is set",0); */ #ifdef CK_RECALL current = rlast; #endif /* CK_RECALL */ cmflgs = 1; if (!xcmdsrc #ifdef CK_RECALL || force_add #endif /* CK_RECALL */ ) addcmd(cmdbuf); return(cmflgs); } } /* This section handles interactive help, completion, editing, and history. Rearranged as a switch statement executed only if we're at top level since there is no need for any of this within command files and macros: Aug 2000. Jun 2001: Even if at top level, skip this if the character was fetched from the reparse or recall buffer, or if stdin is redirected. */ if ((xcmdsrc == 0 /* Only at top level */ #ifndef NOSPL || askflag /* or user is typing ASK response */ #endif /* NOSPL */ ) && chsrc != 0 && realtty) { /* from the real keyboard */ /* Use ANSI / VT100 up and down arrow keys for command recall. */ if (isesc && ( #ifdef IKSD inserver #else 0 #endif /* IKSD */ #ifdef USE_ARROWKEYS || 1 #endif /* USE_ARROWKEYS */ ) ) { /* A real ESC was typed */ int x; msleep(200); /* Wait 1/5 sec */ x = cmdconchk(); /* Was it followed by anything? */ debug(F101,"Arrowkey ESC cmdconchk","",x); if (x > 1) { /* If followed by at least 2 chars */ int c2; c2 = cmdgetc(0); /* Get the first one */ debug(F101,"Arrowkey ESC c2","",c2); if (c2 != '[' && c2 != 'O') { /* If not [ or O */ pushc = c2; /* Push it and take the ESC solo */ } else { c2 = cmdgetc(0); /* Get the second one */ debug(F101,"Arrowkey ESC c3","",c2); switch (c2) { #ifndef NORECALL case 'A': /* Up */ c = BEL; c = C_UP; break; case 'B': /* Down */ c = BEL; c = C_DN; break; case 'C': /* Right */ case 'D': /* Left */ #else default: #endif /* NORECALL */ c = BEL; /* We don't use these yet */ break; } } } } switch (c) { case '?': /* ?-Help */ #ifndef NOSPL if (askflag) /* No help in ASK response */ break; #endif /* NOSPL */ if (quoting && !kstartactive && !comment ) { cmdecho((char) c, 0); *bp = NUL; if (setatm(pp,0) < 0) { debug(F111,"gtword too long ?",pp,strlen(pp)); printf("?Too long\n"); return(-9); } qmflag = 1; return(cmflgs = 3); } case ESC: /* Esc or Tab completion */ if (!comment) { *bp = NUL; if (setatm(pp,0) < 0) { debug(F111,"gtword too long Esc",pp,strlen(pp)); printf("?Too long\n"); return(-9); } esflag = 1; return(cmflgs = 2); } else { bleep(BP_WARN); continue; } case BS: /* Character deletion */ case RUB: if (bp > cmdbuf) { /* If still in buffer... */ cmdchardel(); /* erase it. */ bp--; /* point behind it, */ if (*bp == lbrace) bracelvl--; /* Adjust brace count */ if (*bp == rbrace) bracelvl++; if ((*bp == SP) && /* Flag if current field gone */ (*pp != lbrace || bracelvl == 0)) inword = 0; *bp = NUL; /* Erase character from buffer. */ } else { /* Otherwise, */ bleep(BP_WARN); cmres(); /* and start parsing a new command. */ *bp = *atmbuf = NUL; } if (pp < bp) continue; else return(cmflgs = -1); case LDEL: /* ^U, line deletion */ while ((bp--) > cmdbuf) { cmdchardel(); *bp = NUL; } cmres(); /* Restart the command. */ *bp = *atmbuf = NUL; inword = 0; return(cmflgs = -1); case WDEL: /* ^W, word deletion */ if (bp <= cmdbuf) { /* Beep if nothing to delete */ bleep(BP_WARN); cmres(); *bp = *atmbuf = NUL; return(cmflgs = -1); } bp--; /* Back up over any trailing nonalphanums */ /* This is dependent on ASCII collating sequence */ /* but isalphanum() is not available everywhere. */ for ( ; (bp >= cmdbuf) && ((*bp < '0') || ((*bp > '9') && (*bp < '@')) || ((*bp > 'Z') && (*bp < 'a')) || (*bp > 'z')); bp-- ) { cmdchardel(); *bp = NUL; } /* Now delete back to rightmost remaining nonalphanum */ for ( ; (bp >= cmdbuf) && (*bp) ; bp--) { if ((*bp < '0') || (*bp > '9' && *bp < '@') || (*bp > 'Z' && *bp < 'a') || (*bp > 'z')) break; cmdchardel(); *bp = NUL; } bp++; inword = 0; return(cmflgs = -1); case RDIS: { /* ^R, redisplay */ char *cpx; char cx; *bp = NUL; printf("\n%s",cmprom); cpx = cmdbuf; while ((cx = *cpx++)) { cmdecho(cx,0); } fflush(stdout); continue; } #ifndef NOLASTFILE case VT: if (lastfile) { printf("%s ",lastfile); #ifdef GEMDOS fflush(stdout); #endif /* GEMDOS */ inword = cmflgs = 0; addbuf(lastfile); /* Supply default. */ if (setatm(lastfile,0) < 0) { printf("Last name too long\n"); if (np) free(np); return(-9); } } else { /* No default */ bleep(BP_WARN); } return(0); #endif /* NOLASTFILE */ } #ifdef CK_RECALL if (on_recall && /* Reading commands from keyboard? */ (cm_recall > 0) && /* Saving commands? */ (c == C_UP || c == C_UP2)) { /* Go up one */ if (last_recall == 2 && current > 0) current--; if (current < 0) { /* Nowhere to go, */ bleep(BP_WARN); continue; } if (recall[current]) { /* We have a previous command */ while ((bp--) > cmdbuf) { /* Erase current line */ cmdchardel(); *bp = NUL; } ckstrncpy(cmdbuf,recall[current],CMDBL); #ifdef OSK fflush(stdout); write(fileno(stdout), "\r", 1); printf("%s%s",cmprom,cmdbuf); #else printf("\r%s%s",cmprom,cmdbuf); #endif /* OSK */ current--; } last_recall = 1; return(cmflgs = -1); /* Force a reparse */ } if (on_recall && /* Reading commands from keyboard? */ (cm_recall > 0) && /* Saving commands? */ (c == C_DN)) { /* Down one */ int x = 1; if (last_recall == 1) x++; if (current + x > rlast) { /* Already at bottom, beep */ bleep(BP_WARN); continue; } current += x; /* OK to go down */ if (recall[current]) { while ((bp--) > cmdbuf) { /* Erase current line */ cmdchardel(); *bp = NUL; } ckstrncpy(cmdbuf,recall[current],CMDBL); #ifdef OSK fflush(stdout); write(fileno(stdout), "\r", 1); printf("%s%s",cmprom,cmdbuf); #else printf("\r%s%s",cmprom,cmdbuf); #endif /* OSK */ last_recall = 2; return(cmflgs = -1); /* Force reparse */ } } #endif /* CK_RECALL */ } if (c < SP && quote == 0) { /* Any other unquoted control char */ if (!chsrc) { /* If cmd file, point past it */ bp++; } else { bleep(BP_WARN); } continue; /* continue, don't put in buffer */ } linebegin = 0; /* Not at beginning of line */ #ifdef BEBOX if (echof) { cmdecho((char) c, 0); /* Echo what was typed. */ fflush (stdout); fflush(stderr); } #else #ifdef NOSPL if (echof || chsrc) #else if (echof || (echostars && chsrc)) #endif /* NOSPL */ cmdecho((char) c, 0); /* Echo what was typed. */ #endif /* BEBOX */ } else { /* This character was quoted. */ int qf = 1; quote = 0; /* Unset the quote flag. */ /* debug(F000,"gtword quote 0","",c); */ /* Quote character at this level is only for SP, ?, and controls */ /* If anything else was quoted, leave quote in, and let */ /* the command-specific parsing routines handle it, e.g. \007 */ if (c > 32 && c != '?' && c != RUB && chsrc != 0) { /* debug(F000,"gtword quote 1","",c); */ *bp++ = CMDQ; /* Deposit \ if it came from tty */ qf = 0; /* and don't erase it from screen */ linebegin = 0; /* Not at beginning of line */ #ifdef BS_DIRSEP /* This is a hack to handle "cd \" or "cd foo\" on OS/2 and similar systems. If we were called from cmdir() and the previous character was the quote character, i.e. backslash, and this character is the command terminator, then we stuff an extra backslash into the buffer without echoing, then we stuff the carriage return back in again, and go back and process it, this time with the quote flag off. */ } else if (dirnamflg && (c == CR || c == LF || c == SP)) { /* debug(F000,"gtword quote 2","",c); */ *bp++ = CMDQ; linebegin = 0; /* Not at beginning of line */ *bp = (c == SP ? SP : CR); goto CMDIRPARSE; #endif /* BS_DIRSEP */ } #ifdef BEBOX if (echof) { cmdecho((char) c, qf); /* Echo what was typed. */ fflush (stdout); fflush(stderr); } #else if (echof) cmdecho((char) c, qf); /* Now echo quoted character */ #endif /* BEBOX */ /* debug(F111,"gtword quote",cmdbuf,c); */ } #ifdef COMMENT if (echof) cmdecho((char) c,quote); /* Echo what was typed. */ #endif /* COMMENT */ if (!comment) inword = 1; /* Flag we're in a word. */ if (quote) continue; /* Don't deposit quote character. */ if (c != NL) { /* Deposit command character. */ *bp++ = (char) c; /* and make sure there is a NUL */ #ifdef COMMENT *bp = NUL; /* after it */ #endif /* COMMENT */ } } /* End of big while */ bleep(BP_WARN); printf("?Command too long, maximum length: %d.\n",CMDBL); cmflgs = -2; return(-9); } /* Utility functions */ /* A D D B U F -- Add the string pointed to by cp to the command buffer */ static int addbuf(cp) char *cp; { int len = 0; while ((*cp != NUL) && (bp < cmdbuf+CMDBL)) { *bp++ = *cp++; /* Copy and */ len++; /* count the characters. */ } *bp++ = SP; /* Put a space at the end */ *bp = NUL; /* Terminate with a null */ np = bp; /* Update the next-field pointer */ cmbptr = np; return(len); /* Return the length */ } /* S E T A T M -- Deposit a token in the atom buffer. */ /* Break on space, newline, carriage return, or NUL. Call with: cp = Pointer to string to copy to atom buffer. fcode = 0 means break on whitespace or EOL. fcode = 1 means don't break on space. fcode = 2 means break on space, ':', or '='. fcode = 3 means copy the whole string. Null-terminate the result. Return length of token, and also set global "cc" to this length. Return -1 if token was too long. */ static int setatm(cp,fcode) char *cp; int fcode; { char *ap, *xp, *dqp = NULL, lbrace, rbrace; int bracelvl = 0, dq = 0; register char * s; register int n = 0; if (cmfldflgs & 1) { /* Handle grouping */ lbrace = '('; rbrace = ')'; } else { lbrace = '{'; rbrace = '}'; } cc = 0; /* Character counter */ ap = atmbuf; /* Address of atom buffer */ s = cp; while (*s++) n++; /* Save a call to strlen */ if (n > ATMBL) { printf("?Command buffer overflow\n"); return(-1); } /* debug(F111,"setatm",cp,n); */ if (cp == ap) { /* In case source is atom buffer */ xp = atybuf; /* make a copy */ #ifdef COMMENT strncpy(xp,ap,ATMBL); /* so we can copy it back, edited. */ cp = xp; #else s = ap; while ((*xp++ = *s++)) ; /* We already know it's big enough */ cp = xp = atybuf; #endif /* COMMENT */ } *ap = NUL; /* Zero the atom buffer */ if (fcode == 1) { /* Trim trailing blanks */ while (--n >= 0 && cp[n] == SP) ; cp[n+1] = NUL; } while (*cp == SP) { /* Trim leading spaces */ cp++; n--; } if (*cp == '"') { /* Starts with doublequote? */ dq = 1; dqp = cp; } while (*cp) { if (*cp == lbrace) bracelvl++; else if (*cp == rbrace) bracelvl--; if (bracelvl < 0) bracelvl = 0; if (bracelvl == 0) { if (dq) { if (*cp == SP || *cp == HT) { if (cp > dqp+1) { if (*(cp-1) == '"' && *(cp-2) != CMDQ) { break; } } } } else if ((*cp == SP || *cp == HT) && fcode != 1 && fcode != 3) break; if ((fcode == 2) && (*cp == '=' || *cp == ':')) break; if ((fcode != 3) && (*cp == LF || *cp == CR)) break; } *ap++ = *cp++; cc++; } *ap = NUL; /* Terminate the string. */ /* debug(F111,"setatm result",atmbuf,cc); */ return(cc); /* Return length. */ } /* These functions attempt to hide system dependencies from the mainline code in gtword(). Dummy arg for cmdgetc() needed for compatibility with coninc(), ttinc(), etc, since a pointer to this routine can be passed in place of those to tn_doop(). No longer static. Used by askmore(). Fri Aug 20 15:03:34 1999. */ #define CMD_CONINC /* How we get keyboard chars */ int cmdgetc(timelimit) int timelimit; { /* Get a character from the tty. */ int c; #ifdef IKSD extern int inserver; #endif /* IKSD */ #ifdef CK_LOGIN extern int x_logged; #endif /* CK_LOGIN */ #ifdef TNCODE static int got_cr = 0; extern int ckxech; int tx = 0, is_tn = 0; #endif /* TNCODE */ if (pushc #ifndef NOSPL && !askflag #endif /* NOSPL */ ) { debug(F111,"cmdgetc()","pushc",pushc); c = pushc; pushc = NUL; if (xcmfdb && c == '?') /* Don't echo ? twice if chaining. */ cmdchardel(); return(c); } #ifdef datageneral { char ch; c = dgncinb(0,&ch,1); /* -1 is EOF, -2 TO, * -c is AOS/VS error */ if (c == -2) { /* timeout was enabled? */ resto(channel(0)); /* reset timeouts */ c = dgncinb(0,&ch,1); /* retry this now! */ } if (c < 0) return(-4); /* EOF or some error */ else c = (int) ch & 0177; /* Get char without parity */ /* echof = 1; */ } #else /* Not datageneral */ #ifndef MINIX2 if ( #ifdef IKSD (!local && inserver) || #endif /* IKSD */ timelimit > 0) { #ifdef TNCODE GETNEXTCH: is_tn = !pushc && !local && sstelnet; #endif /* TNCODE */ #ifdef COMMENT c = coninc(timelimit > 0 ? 1 : 0); #else /* COMMENT */ /* This is likely to break the asktimeout... */ c = coninc(timelimit); #endif /* COMMENT */ /* debug(F101,"cmdgetc coninc","",c); */ #ifdef TNCODE if (c >= 0 && is_tn) { /* Server-side Telnet */ switch (c) { case IAC: /* debug(F111,"gtword IAC","c",c); */ got_cr = 0; if ((tx = tn_doop((CHAR)(c & 0xff),ckxech,coninc)) == 0) { goto GETNEXTCH; } else if (tx <= -1) { /* I/O error */ /* If there was a fatal I/O error then ttclos() */ /* has been called and the next GETNEXTCH attempt */ /* will be !is_tn since ttclos() sets sstelnet = 0 */ doexit(BAD_EXIT,-1); /* (or return(-4)? */ } else if (tx == 1) { /* ECHO change */ ckxech = dpx = 1; /* Get next char */ goto GETNEXTCH; } else if (tx == 2) { /* ECHO change */ ckxech = dpx = 0; /* Get next char */ goto GETNEXTCH; } else if (tx == 3) { /* Quoted IAC */ c = 255; /* proceeed with it. */ } #ifdef IKS_OPTION else if (tx == 4) { /* IKS State Change */ goto GETNEXTCH; } #endif /* IKS_OPTION */ else if (tx == 6) { /* Remote Logout */ doexit(GOOD_EXIT,0); } else { goto GETNEXTCH; /* Unknown, get next char */ } break; #ifdef COMMENT case CR: if (!TELOPT_U(TELOPT_BINARY)) { if (got_cr) { /* This means the sender is violating Telnet */ /* protocol because we received two CRs in a */ /* row without getting either LF or NUL. */ /* This will not solve the problem but it */ /* will at least allow two CRs to do something */ /* whereas before the user would have to guess */ /* to send LF or NUL after the CR. */ debug(F100,"gtword CR telnet error","",0); c = LF; } else { debug(F100,"gtword skipping CR","",0); got_cr = 1; /* Remember a CR was received */ goto GETNEXTCH; } } else { debug(F100,"gtword CR to LF","",0); c = LF; } break; case LF: if (!TELOPT_U(TELOPT_BINARY)) { got_cr = 0; debug(F100,"gtword LF","",0); } else { if (got_cr) { got_cr = 0; debug(F100,"gtword skipping LF","",0); goto GETNEXTCH; } } break; case NUL: if (!TELOPT_U(TELOPT_BINARY) && got_cr) { c = LF; debug(F100,"gtword NUL to LF","",0); } else { debug(F100,"gtword NUL","",0); } got_cr = 0; break; #else /* COMMENT */ case CR: if ( !TELOPT_U(TELOPT_BINARY) && got_cr ) { /* This means the sender is violating Telnet */ /* protocol because we received two CRs in a */ /* row without getting either LF or NUL. */ /* This will not solve the problem but it */ /* will at least allow two CRs to do something */ /* whereas before the user would have to guess */ /* to send LF or NUL after the CR. */ debug(F100,"gtword CR telnet error","",0); } else { got_cr = 1; /* Remember a CR was received */ } /* debug(F100,"gtword CR to LF","",0); */ c = LF; break; case LF: if (got_cr) { got_cr = 0; /* debug(F100,"gtword skipping LF","",0); */ goto GETNEXTCH; } break; case NUL: if (got_cr) { got_cr = 0; /* debug(F100,"gtword skipping NUL","",0); */ goto GETNEXTCH; #ifdef COMMENT } else { debug(F100,"gtword NUL","",0); #endif /* COMMENT */ } break; #endif /* COMMENT */ #ifdef IKSD case ETX: /* Ctrl-C... */ case EOT: /* EOT = EOF */ if (inserver #ifdef CK_LOGIN && !x_logged #endif /* CK_LOGIN */ ) return(-4); break; #endif /* IKSD */ default: got_cr = 0; } } #endif /* TNCODE */ } else { #ifdef OS2 c = coninc(0); #else /* OS2 */ #ifdef CMD_CONINC #undef CMD_CONINC #endif /* CMD_CONINC */ c = getchar(); #endif /* OS2 */ } #else /* MINIX2 */ #undef getc #ifdef CMD_CONINC #undef CMD_CONINC #endif /* CMD_CONINC */ c = getc(stdin); /* debug(F101,"cmdgetc getc","",c); */ #endif /* MINIX2 */ #ifdef RTU if (rtu_bug) { #ifdef CMD_CONINC #undef CMD_CONINC #endif /* CMD_CONINC */ c = getchar(); /* RTU doesn't discard the ^Z */ rtu_bug = 0; } #endif /* RTU */ #endif /* datageneral */ return(c); /* Return what we got */ } /* #ifdef USE_ARROWKEYS */ /* Mechanism to use for peeking into stdin buffer */ #ifndef USE_FILE_CNT /* stdin->__cnt */ #ifndef USE_FILE__CNT /* Note: two underscores */ #ifdef HPUX /* HPUX 7-11 */ #ifndef HPUX5 #ifndef HPUX6 #define USE_FILE__CNT #endif /* HPUX6 */ #endif /* HPUX5 */ #else #ifdef ANYSCO /* SCO UNIX, OSR5, Unixware, etc */ #ifndef OLD_UNIXWARE /* But not Unixware 1.x or 2.0 */ #ifndef UNIXWARE2 /* or 2.1.0 */ #define USE_FILE__CNT #endif /* UNIXWARE2 */ #endif /* OLD_UNIXWARE */ #endif /* ANYSCO */ #endif /* HPUX */ #endif /* USE_FILE__CNT */ #endif /* USE_FILE_CNT */ #ifndef USE_FILE_R /* stdin->_r */ #ifndef USE_FILE_CNT #ifndef USE_FILE__CNT #ifdef BSD44 /* {Free,Open,Net}BSD, BSDI */ #define USE_FILE_R #endif /* BSD44 */ #endif /* USE_FILE__CNT */ #endif /* USE_FILE_CNT */ #endif /* USE_FILE_R */ #ifndef USE_FILE_R /* stdin->_cnt */ #ifndef USE_FILE_CNT #ifndef USE_FILE__CNT #define USE_FILE_CNT /* Everybody else (but Linux) */ #endif /* USE_FILE__CNT */ #endif /* USE_FILE_CNT */ #endif /* USE_FILE_R */ /* c m d c o n c h k How many characters are waiting to be read at the console? Normally conchk() would tell us, but in Unix and VMS cmdgetc() uses stdio getchar(), thus bypassing coninc()/conchk(), so we have to peek into the stdin buffer, which is totally nonportable. Which is why this routine is, at least for now, used only for checking for arrow-key sequences from the keyboard after an ESC was read. Wouldn't it be nice if the stdio package had a function that returned the number of bytes waiting to be read from its buffer? Returns 0 or greater always. */ int cmdconchk() { int x = 0, y; y = pushc ? 1 : 0; /* Have command character pushed? */ #ifdef OS2 x = conchk(); /* Check device-driver buffer */ if (x < 0) x = 0; #else /* OS2 */ #ifdef CMD_CONINC /* See cmdgetc() */ x = conchk(); /* Check device-driver buffer */ if (x < 0) x = 0; #else /* CMD_CONINC */ /* Here we must look inside the stdin buffer - highly platform dependent */ #ifdef _IO_file_flags /* Linux */ x = (int) ((stdin->_IO_read_end) - (stdin->_IO_read_ptr)); debug(F101,"cmdconchk _IO_file_flags","",x); #else /* _IO_file_flags */ #ifdef USE_FILE_CNT /* Traditional */ #ifdef VMS debug(F101,"cmdconchk (*stdin)->_cnt","",(*stdin)->_cnt); x = (*stdin)->_cnt; #else #ifdef NOARROWKEYS debug(F101,"cmdconchk NOARROWKEYS x","",0); #else debug(F101,"cmdconchk stdin->_cnt","",stdin->_cnt); x = stdin->_cnt; #endif /* NOARROWKEYS */ #endif /* VMS */ if (x == 0) x = conchk(); if (x < 0) x = 0; #else /* USE_FILE_CNT */ #ifdef USE_FILE__CNT /* HP-UX */ debug(F101,"cmdconchk stdin->__cnt","",stdin->__cnt); x = stdin->__cnt; if (x == 0) x = conchk(); if (x < 0) x = 0; #else /* USE_FILE_CNT */ #ifdef USE_FILE_R /* FreeBSD, OpenBSD, etc */ debug(F101,"cmdconchk stdin->_r","",stdin->_r); x = stdin->_r; if (x == 0) x = conchk(); if (x < 0) x = 0; /* Fill in any others here... */ #endif /* USE_FILE_R */ #endif /* USE_FILE__CNT */ #endif /* USE_FILE_CNT */ #endif /* _IO_file_flags */ #endif /* CMD_CONINC */ #endif /* OS2 */ return(x + y); } /* #endif */ /* USE_ARROWKEYS */ static VOID cmdclrscn() { /* Clear the screen */ ck_cls(); } static VOID /* What to echo at end of command */ #ifdef CK_ANSIC cmdnewl(char c) #else cmdnewl(c) char c; #endif /* CK_ANSIC */ /* cmdnewl */ { #ifdef OS2 #ifdef IKSD extern int inserver; if (inserver && c == LF) putchar(CR); #endif /* IKSD */ #endif /* OS2 */ putchar(c); /* c is the terminating character */ #ifdef WINTCP /* what is this doing here? */ if (c == CR) putchar(NL); #endif /* WINTCP */ /* A.A. Chernov, who sent in changes for FreeBSD, said we also needed this for SVORPOSIX because "setup terminal by termios and curses does not convert \r to \n, so additional \n needed in newline function." But it is also very likely to result in unwanted blank lines. */ #ifdef BSD44 if (c == CR) putchar(NL); #endif /* BSD44 */ #ifdef COMMENT /* OS2 no longer needs this as all CR are converted to NL in coninc() */ /* This eliminates the ugly extra blank lines discussed above. */ #ifdef OS2 if (c == CR) putchar(NL); #endif /* OS2 */ #endif /* COMMENT */ #ifdef aegis if (c == CR) putchar(NL); #endif /* aegis */ #ifdef AMIGA if (c == CR) putchar(NL); #endif /* AMIGA */ #ifdef datageneral if (c == CR) putchar(NL); #endif /* datageneral */ #ifdef GEMDOS if (c == CR) putchar(NL); #endif /* GEMDOS */ #ifdef STRATUS if (c == CR) putchar(NL); #endif /* STRATUS */ } static VOID cmdchardel() { /* Erase a character from the screen */ #ifndef NOSPL if (!echostars) #endif /* NOSPL */ if (!dpx) return; #ifdef datageneral /* DG '\b' is EM (^y or \031) */ if (termtype == 1) /* Erase a character from non-DG screen, */ dgncoub(1,"\010 \010",3); else #endif /* datageneral */ printf("\b \b"); #ifdef GEMDOS fflush(stdout); #else #ifdef BEBOX fflush(stdout); #endif /* BEBOX */ #endif /* GEMDOS */ } static VOID #ifdef CK_ANSIC cmdecho(char c, int quote) #else cmdecho(c,quote) char c; int quote; #endif /* CK_ANSIC */ { /* cmdecho */ #ifdef NOSPL if (!dpx) return; #else if (!echostars) { if (!dpx) return; } else { c = (char)echostars; } #endif /* NOSPL */ /* Echo tty input character c */ if (quote) { putchar(BS); putchar(SP); putchar(BS); #ifdef isprint putchar((CHAR) (isprint(c) ? c : '^' )); #else putchar((CHAR) ((c >= SP && c < DEL) ? c : '^')); #endif /* isprint */ } else { putchar(c); } #ifdef OS2 if (quote==1 && c==CR) putchar((CHAR) NL); #endif /* OS2 */ if (timelimit) fflush(stdout); } /* Return pointer to current position in command buffer. */ char * cmpeek() { return(np); } #endif /* NOICP */ #ifdef NOICP #include "ckcdeb.h" #include "ckucmd.h" #include "ckcasc.h" #endif /* NOICP */ /* X X E S C -- Interprets backslash codes */ /* Returns the int value of the backslash code if it is > -1 and < 256 */ /* and updates the string pointer to first character after backslash code. */ /* If the argument is invalid, leaves pointer unchanged and returns -1. */ int xxesc(s) char **s; { /* Expand backslash escapes */ int x, y, brace, radix; /* Returns the int value */ char hd = '9'; /* Highest digit in radix */ char *p; p = *s; /* pointer to beginning */ if (!p) return(-1); /* watch out for null pointer */ x = *p++; /* character at beginning */ if (x != CMDQ) return(-1); /* make sure it's a backslash code */ x = *p; /* it is, get the next character */ if (x == '{') { /* bracketed quantity? */ p++; /* begin past bracket */ x = *p; brace = 1; } else brace = 0; switch (x) { /* Start interpreting */ case 'd': /* Decimal radix indicator */ case 'D': p++; /* Just point past it and fall thru */ case '0': /* Starts with digit */ case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': radix = 10; /* Decimal */ hd = '9'; /* highest valid digit */ break; case 'o': /* Starts with o or O */ case 'O': radix = 8; /* Octal */ hd = '7'; /* highest valid digit */ p++; /* point past radix indicator */ break; case 'x': /* Starts with x or X */ case 'X': radix = 16; /* Hexadecimal */ p++; /* point past radix indicator */ break; default: /* All others */ #ifdef COMMENT *s = p+1; /* Treat as quote of next char */ return(*p); #else return(-1); #endif /* COMMENT */ } /* For OS/2, there are "wide" characters required for the keyboard * binding, i.e \644 and similar codes larger than 255 (byte). * For this purpose, give up checking for < 256. If someone means * \266 should result in \26 followed by a "6" character, he should * always write \{26}6 anyway. Now, return only the lower byte of * the result, i.e. 10, but eat up the whole \266 sequence and * put the wide result 266 into a global variable. Yes, that's not * the most beautiful programming style but requires the least * amount of changes to other routines. */ if (*p == '{') { /* Sun May 11 20:00:40 2003 */ brace = 1; /* Allow {} after radix indicator */ p++; } if (radix <= 10) { /* Number in radix 8 or 10 */ for ( x = y = 0; (*p) && (*p >= '0') && (*p <= hd) #ifdef OS2 && (y < 5) && (x*radix < KMSIZE); /* the maximum needed value \8196 is 4 digits long */ /* while as octal it requires \1377, i.e. 5 digits */ #else && (y < 3) && (x*radix < 256); #endif /* OS2 */ p++,y++) { x = x * radix + (int) *p - 48; } #ifdef OS2 wideresult = x; /* Remember wide result */ x &= 255; #endif /* OS2 */ if (y == 0 || x > 255) { /* No valid digits? */ *s = p; /* point after it */ return(-1); /* return failure. */ } } else if (radix == 16) { /* Special case for hex */ if ((x = unhex(*p++)) < 0) { *s = p - 1; return(-1); } if ((y = unhex(*p++)) < 0) { *s = p - 2; return(-1); } x = ((x << 4) & 0xF0) | (y & 0x0F); #ifdef OS2 wideresult = x; if ((y = unhex(*p)) >= 0) { p++; wideresult = ((x << 4) & 0xFF0) | (y & 0x0F); x = wideresult & 255; } #endif /* OS2 */ } else x = -1; if (brace && *p == '}' && x > -1) /* Point past closing brace, if any */ p++; *s = p; /* Point to next char after sequence */ return(x); /* Return value of sequence */ } int /* Convert hex string to int */ #ifdef CK_ANSIC unhex(char x) #else unhex(x) char x; #endif /* CK_ANSIC */ /* unhex */ { if (x >= '0' && x <= '9') /* 0-9 is offset by hex 30 */ return(x - 0x30); else if (x >= 'A' && x <= 'F') /* A-F offset by hex 37 */ return(x - 0x37); else if (x >= 'a' && x <= 'f') /* a-f offset by hex 57 */ return(x - 0x57); /* (obviously ASCII dependent) */ else return(-1); } /* L O O K U P -- Lookup the string in the given array of strings */ /* Call this way: v = lookup(table,word,n,&x); table - a 'struct keytab' table. word - the target string to look up in the table. n - the number of elements in the table. x - address of an integer for returning the table array index, or NULL if you don't need a table index. The keyword table must be arranged in ascending alphabetical order; alphabetic case doesn't matter but letters are treated as lowercase for purposes of ordering; thus "^" and "_" come *before* the letters, not after them. Returns the keyword's associated value (zero or greater) if found, with the variable x set to the keyword-table index. If is lookup() is not successful, it returns: -3 if nothing to look up (target was null), -2 if ambiguous, -1 if not found. A match is successful if the target matches a keyword exactly, or if the target is a prefix of exactly one keyword. It is ambiguous if the target matches two or more keywords from the table. Lookup() is the critical routine in scripts and so is optimized with a simple static cache plus some other tricks. Maybe it could be improved further with binary search or hash techniques but I doubt it since most keyword tables are fairly short. */ #ifdef USE_LUCACHE /* Lookup cache */ extern int lusize; /* (initialized in ckuus5.c) */ extern char * lucmd[]; extern int luval[]; extern int luidx[]; extern struct keytab * lutab[]; long luhits = 0L; long lucalls = 0L; long xxhits = 0L; long luloop = 0L; #endif /* USE_LUCACHE */ int lookup(table,cmd,n,x) char *cmd; struct keytab table[]; int n, *x; { register int i, m; int v, len, cmdlen = 0; char c = NUL, c1, *s; /* Get 1st char of search object, if it's null return -3. */ if (!cmd || n < 1) /* Defense de nullarg */ return(-3); c1 = *cmd; /* First character */ if (!c1) /* Make sure there is one */ return(-3); if (isupper(c1)) /* If letter make it lowercase */ c1 = tolower(c1); #ifdef USE_LUCACHE /* lookup() cache */ m = lusize; lucalls++; /* Count this lookup() call */ for (i = 0; i < m; i++) { /* Loop thru cache */ if (*(lucmd[i]) == c1) { /* Same as 1st char of search item? */ if (lutab[i] == table) { /* Yes - same table too? */ if (!strcmp(cmd,lucmd[i])) { /* Yes - compare */ if (x) *x = luidx[i]; /* Match - return index */ luhits++; /* Count cache hit */ return(luval[i]); /* Return associated value */ } } } } #endif /* USE_LUCACHE */ /* Not null, not in cache, look it up */ s = cmd; while (*s++) cmdlen++; /* Length of target */ /* Quick binary search to find last table entry whose first character is lexically less than the first character of the search object. This is the starting point of the next loop, which must go in sequence since it compares adjacent table entries. */ if (n < 5) { /* Not worth it for small tables */ i = 0; } else { int lo = 0; int hi = n; int count = 0; while (lo+2 < hi && ++count < 12) { i = lo + ((hi - lo) / 2); c = *(table[i].kwd); if (isupper(c)) c = tolower(c); if (c < c1) { lo = i; } else { hi = i; } } i = (c < c1) ? lo+1 : lo; #ifdef USE_LUCACHE if (i > 0) xxhits++; #endif /* USE_LUCACHE */ } for ( ; i < n-1; i++) { #ifdef USE_LUCACHE luloop++; #endif /* USE_LUCACHE */ v = 0; c = *(table[i].kwd); if (c) { if (isupper(c)) c = tolower(c); /* The following is a big performance booster but makes it */ /* absolutely essential that all lookup() tables are in order. */ if (c > c1) /* Leave early if past our mark */ return(-1); #ifdef DEBUG /* Use LOG DEBUG to check */ if (deblog) { if (ckstrcmp(table[i].kwd,table[i+1].kwd,0,0) > 0) { printf("TABLE OUT OF ORDER [%s] [%s]\n", table[i].kwd,table[i+1].kwd); } } #endif /* DEBUG */ if (c == c1) { len = 0; s = table[i].kwd; while (*s++) len++; if ((len == cmdlen && !ckstrcmp(table[i].kwd,cmd,len,0)) || ((v = !ckstrcmp(table[i].kwd,cmd,cmdlen,0)) && ckstrcmp(table[i+1].kwd,cmd,cmdlen,0))) { if (x) *x = i; return(table[i].kwval); } } else v = 0; } if (v) { /* Ambiguous */ if (x) *x = i; /* Set index of first match */ return(-2); } } /* Last (or only) element */ if (!ckstrcmp(table[n-1].kwd,cmd,cmdlen,0)) { if (x) *x = n-1; /* debug(F111,"lookup",table[i].kwd,table); */ return(table[n-1].kwval); } else return(-1); } /* x l o o k u p Like lookup, but requires a full (but case-independent) match and does NOT require the table to be in order. */ int xlookup(table,cmd,n,x) struct keytab table[]; char *cmd; int n, *x; { register int i; int len, cmdlen, one = 0; register char c, c2, * s, * s2; if (!cmd) cmd = ""; /* Check args */ if (!*cmd || n < 1) return(-3); c = *cmd; /* First char of string to look up */ if (!*(cmd+1)) { /* Special handling for 1-char names */ cmdlen = 1; if (isupper(c)) c = tolower(c); one = 1; } else { cmdlen = 0; s = cmd; while (*s++) cmdlen++; c = *cmd; if (isupper(c)) c = tolower(c); } if (cmdlen < 1) return(-3); for (i = 0; i < n; i++) { s = table[i].kwd; /* This entry */ if (!s) s = ""; if (!*s) continue; /* Empty table entry */ c2 = *s; if (isupper(c2)) c2 = tolower(c2); if (c != c2) continue; /* First char doesn't match */ if (one) { /* Name is one char long */ if (!*(s+1)) { if (x) *x = i; *cmd = c; return(table[i].kwval); /* So is table entry */ } } else { /* Otherwise do string comparison */ s2 = s; len = 0; while (*s2++) len++; if (len == cmdlen && !ckstrcmp(s,cmd,-1,0)) { if (x) *x = i; return(table[i].kwval); } } } return(-1); } /* Reverse lookup */ char * rlookup(table,n,x) struct keytab table[]; int n, x; { int i; for (i = 0; i < n; i++) { if (table[i].kwval == x) return(table[i].kwd); } return(NULL); } #ifndef NOICP int cmdsquo(x) int x; { quoting = x; return(1); } int cmdgquo() { return(quoting); } #endif /* NOICP */ ckucmd.h0000644000015300001460000002152411266111427011305 0ustar fdckermit/* C K U C M D . H -- Header file for Unix cmd package */ /* Author: Frank da Cruz Columbia University Kermit Project, New York City. Copyright (C) 1985, 2009, Trustees of Columbia University in the City of New York. All rights reserved. See the C-Kermit COPYING.TXT file or the copyright text in the ckcmai.c module for disclaimer and permissions. */ #ifndef CKUCMD_H #define CKUCMD_H /* Command recall */ #ifdef pdp11 /* Not enough room for this */ #ifndef NORECALL #define NORECALL #endif /* NORECALL */ #endif /* pdp11 */ #ifdef DYNAMIC /* Dynamic command buffers */ /* Use malloc() to allocate the many command-related buffers in ckucmd.c. */ #ifndef DCMDBUF #ifndef NORECALL #define NORECALL #endif /* NORECALL */ #endif /* DCMDBUF */ #ifndef NORECALL #define CK_RECALL #else #ifdef CK_RECALL #undef CK_RECALL #endif /* CK_RECALL */ #endif /* NORECALL */ #else #ifndef NORECALL #define NORECALL #endif /* NORECALL */ #endif /* DYNAMIC */ #ifdef NORECALL #ifdef CK_RECALL #undef CK_RECALL #endif /* CK_RECALL */ #endif /* NORECALL */ #ifdef NORECALL #ifndef NOARROWKEYS #define NOARROWKEYS #endif /* NOARROWKEYS */ #endif /* NORECALL */ /* Special getchars */ #ifdef VMS #ifdef getchar /* This is for VMS GCC */ #undef getchar #endif /* getchar */ #define getchar() vms_getchar() int vms_getchar(void); #endif /* VMS */ #ifdef aegis #undef getchar #define getchar() coninc(0) #endif /* aegis */ #ifdef AMIGA #undef getchar #define getchar() coninc(0) #endif /* AMIGA */ #ifdef Plan9 #undef getchar #define getchar() coninc(0) #undef putchar #define putchar(c) conoc(c) #undef printf #define printf conprint #endif /* Plan9 */ /* Sizes of things */ #ifndef CMDDEP #ifdef BIGBUFOK #define CMDDEP 64 /* Maximum command recursion depth */ #else #define CMDDEP 20 #endif /* BIGBUFOK */ #endif /* CMDDEP */ #define HLPLW 78 /* Width of ?-help line */ #define HLPCW 19 /* Width of ?-help column */ #define HLPBL 100 /* Help string buffer length */ #ifdef BIGBUFOK #define ATMBL 10238 /* Command atom buffer length*/ #else #ifdef NOSPL #define ATMBL 256 #else #define ATMBL 1024 #endif /* NOSPL */ #endif /* BIGBUFOK */ #ifndef CMDBL #ifdef NOSPL /* No script programming language, save some space */ #define CMDBL 608 /* Command buffer length */ #else #ifdef BIGBUFOK #define CMDBL 32763 #else #define CMDBL 4092 #endif /* OS2 */ #endif /* NOSPL */ #endif /* CMDBL */ /* Special characters */ #define RDIS 0022 /* Redisplay (^R) */ #define LDEL 0025 /* Delete line (^U) */ #define WDEL 0027 /* Delete word (^W) */ #ifdef CK_RECALL #define C_UP 0020 /* Go Up in recall buffer (^P) */ #define C_UP2 0002 /* Alternate Go Up (^B) for VMS */ #define C_DN 0016 /* Go Down in recall buffer (^N) */ #endif /* CK_RECALL */ /* Keyword flags (bits, powers of 2) */ #define CM_INV 1 /* Invisible keyword */ #define CM_ABR 2 /* Abbreviation for another keyword */ #define CM_HLP 4 /* Help-only keyword */ #define CM_ARG 8 /* An argument is required */ #define CM_NOR 16 /* No recall for this command */ #define CM_PRE 32 /* Long-form cmdline arg for prescan */ #define CM_PSH 64 /* Command disabled if nopush */ #define CM_LOC 128 /* Command disabled if nolocal */ /* A long-form command line option is a keyword using the regular struct keytab and lookup mechanisms. Flags that make sense in this context are CM_ARG, indicating this option requires an argument (operand), and CM_PRE, which means this option must be processed before the initialization file. The absence of CM_PRE means the option is to be processed after the initialization file in the normal manner. */ /* Token flags (numbers) */ #define CMT_COM 0 /* Comment (; or #) */ #define CMT_SHE 1 /* Shell escape (!) */ #define CMT_LBL 2 /* Label (:) */ #define CMT_FIL 3 /* Indirect filespec (@) (not used) */ /* Path separator for path searches */ #ifdef OS2 #define PATHSEP ';' #else #ifdef UNIX #define PATHSEP ':' #else #define PATHSEP ',' #endif /* UNIX */ #endif /* OS2 */ #ifndef CK_KEYTAB #define CK_KEYTAB /* Keyword Table Template perhaps already defined in ckcdeb.h */ struct keytab { /* Keyword table */ char *kwd; /* Pointer to keyword string */ int kwval; /* Associated value */ int flgs; /* Flags (as defined above) */ }; #endif /* CK_KEYTAB */ /* String preprocessing function */ #ifdef CK_ANSIC /* ANSI C */ #ifdef M_SYSV /* SCO Microsoft C wants no args */ typedef int (*xx_strp)(); #else typedef int (*xx_strp)(char *, char **, int *); #endif /* M_SYSV */ #else /* Not ANSI C */ typedef int (*xx_strp)(); #endif /* CK_ANSIC */ /* FLDDB struct */ typedef struct FDB { int fcode; /* Function code */ char * hlpmsg; /* Help message */ char * dflt; /* Default */ char * sdata; /* Additional string data */ int ndata1; /* Additional numeric data 1 */ int ndata2; /* Additional numeric data 2 */ xx_strp spf; /* String processing function */ struct keytab * kwdtbl; /* Keyword table */ struct FDB * nxtfdb; /* Pointer to next alternative */ } fdb; typedef struct OFDB { struct FDB * fdbaddr; /* Address of succeeding FDB struct */ int fcode; /* Function code */ char * sresult; /* String result */ int nresult; /* Integer result */ int kflags; /* Keyword flags if any */ CK_OFF_T wresult; /* Long integer ("wide") result */ } ofdb; #ifndef CKUCMD_C extern struct OFDB cmresult; #endif /* CKUCMD_C */ /* Codes for primary parsing function */ #define _CMNUM 0 /* Number */ #define _CMOFI 1 /* Output file */ #define _CMIFI 2 /* Input file */ #define _CMFLD 3 /* Arbitrary field */ #define _CMTXT 4 /* Text string */ #define _CMKEY 5 /* Keyword */ #define _CMCFM 6 /* Confirmation */ #define _CMDAT 7 /* Date/time */ #define _CMNUW 8 /* Wide version of cmnum */ /* Function prototypes */ _PROTOTYP( int xxesc, (char **) ); _PROTOTYP( int cmrini, (int) ); _PROTOTYP( VOID cmsetp, (char *) ); _PROTOTYP( VOID cmsavp, (char [], int) ); _PROTOTYP( char * cmgetp, (void) ); _PROTOTYP( VOID prompt, (xx_strp) ); _PROTOTYP( VOID pushcmd, (char *) ); _PROTOTYP( VOID cmres, (void) ); _PROTOTYP( VOID cmini, (int) ); _PROTOTYP( int cmgbrk, (void) ); _PROTOTYP( int cmgkwflgs, (void) ); _PROTOTYP( int cmpush, (void) ); _PROTOTYP( int cmpop, (void) ); _PROTOTYP( VOID untab, (char *) ); _PROTOTYP( int cmnum, (char *, char *, int, int *, xx_strp ) ); _PROTOTYP( int cmnumw, (char *, char *, int, CK_OFF_T *, xx_strp ) ); _PROTOTYP( int cmofi, (char *, char *, char **, xx_strp ) ); _PROTOTYP( int cmifi, (char *, char *, char **, int *, xx_strp ) ); _PROTOTYP( int cmiofi, (char *, char *, char **, int *, xx_strp ) ); _PROTOTYP( int cmifip,(char *, char *, char **, int *, int, char *, xx_strp )); _PROTOTYP( int cmifi2,(char *,char *,char **,int *,int,char *,xx_strp,int )); _PROTOTYP( int cmdir, (char *, char *, char **, xx_strp ) ); _PROTOTYP( int cmdirp, (char *, char *, char **, char *, xx_strp ) ); _PROTOTYP( int cmfld, (char *, char *, char **, xx_strp ) ); _PROTOTYP( int cmtxt, (char *, char *, char **, xx_strp ) ); _PROTOTYP( int cmkey, (struct keytab [], int, char *, char *, xx_strp) ); _PROTOTYP( int cmkeyx, (struct keytab [], int, char *, char *, xx_strp) ); _PROTOTYP( int cmkey2,(struct keytab [],int,char *,char *,char *,xx_strp,int)); _PROTOTYP( int cmswi, (struct keytab [], int, char *, char *, xx_strp) ); _PROTOTYP( int cmdate,(char *, char *, char **, int, xx_strp) ); _PROTOTYP( char * cmpeek, (void) ); _PROTOTYP( int cmfdb, (struct FDB *) ); _PROTOTYP( VOID cmfdbi, (struct FDB *, int, char *, char *, char *, int, int, xx_strp, struct keytab *, struct FDB *) ); _PROTOTYP( int chktok, (char *) ); _PROTOTYP( int cmcfm, (void) ); _PROTOTYP( int lookup, (struct keytab [], char *, int, int *) ); _PROTOTYP( VOID kwdhelp, (struct keytab[],int,char *,char *,char *,int,int) ); _PROTOTYP( int ungword, (void) ); _PROTOTYP( VOID unungw, (void) ); _PROTOTYP( int cmdsquo, (int) ); _PROTOTYP( int cmdgquo, (void) ); _PROTOTYP( char * ckcvtdate, (char *, int) ); _PROTOTYP( int cmdgetc, (int)); #ifndef NOARROWKEYS _PROTOTYP( int cmdconchk, (void) ); #endif /* NOARROWKEYS */ #ifdef CK_RECALL _PROTOTYP( char * cmgetcmd, (char *) ); _PROTOTYP( VOID addcmd, (char *) ); _PROTOTYP( VOID cmaddnext, (void) ); #endif /* CK_RECALL */ _PROTOTYP( char * cmcvtdate, (char *, int) ); _PROTOTYP( char * cmdiffdate, (char *, char *) ); _PROTOTYP( char * cmdelta, (int, int,int,int,int,int,int,int,int,int,int,int,int )); _PROTOTYP( char * shuffledate, (char *, int) ); _PROTOTYP( int filhelp, (int, char *, char *, int, int) ); _PROTOTYP( int xfilhelp, (int, char *, char *, int, int, int, char *, char *, char *, char *, CK_OFF_T, CK_OFF_T, int, int, char **) ); _PROTOTYP( int delta2sec, (char *, long *) ); #ifdef DCMDBUF _PROTOTYP( int cmsetup, (void) ); #endif /* DCMDBUF */ #endif /* CKUCMD_H */ /* End of ckucmd.h */ ckucns.c0000644000015300001460000023116211343016510011312 0ustar fdckermit#include "ckcsym.h" char *connv = "CONNECT Command for UNIX:select(), 9.0.139, 1 Mar 2010"; /* C K U C N S -- Terminal connection to remote system, for UNIX */ /* Author: Frank da Cruz , Columbia University Academic Information Systems, New York City. Copyright (C) 1985, 2010, Trustees of Columbia University in the City of New York. All rights reserved. See the C-Kermit COPYING.TXT file or the copyright text in the ckcmai.c module for disclaimer and permissions. */ /* This version of the UNIX CONNECT module uses select(), which is required for Kerberos encryption. Thus it can be used only on UNIX systems that support select() on both TCP/IP and serial connections. A separate module that uses a completely portable fork() structure can be used on systems where select() is not available or does not work as required. */ #include "ckcdeb.h" /* Common things first */ #ifndef NOLOCAL #ifdef OSF13 #ifdef CK_ANSIC #ifdef _NO_PROTO #undef _NO_PROTO #endif /* _NO_PROTO */ #endif /* CK_ANSIC */ #endif /* OSF13 */ #include /* Error numbers */ #ifndef NOTIMEH #include /* For FD_blah */ #ifdef SYSTIMEH /* (IRIX 5.3) */ #include #endif /* SYSTIMEH */ #endif /* NOTIMEH */ #ifdef BSD42HACK /* Why is this necessary? */ #ifndef DCLTIMEVAL #define DCLTIMEVAL #endif /* DCLTIMEVAL */ #endif /* BSD42HACK */ /* Kermit-specific includes */ #include "ckcasc.h" /* ASCII characters */ #include "ckcker.h" /* Kermit things */ #include "ckucmd.h" /* For xxesc() prototype */ #include "ckcnet.h" /* Network symbols */ #ifndef NOCSETS #include "ckcxla.h" /* Character set translation */ #endif /* NOCSETS */ #ifdef BEBOX #include #include #include #endif /* BEBOX */ #include /* Signals */ /* All the following is for select()... */ #ifdef CKTIDLE /* Timeouts only for SET TERM IDLE */ #ifndef DCLTIMEVAL #ifdef UNIXWARE #ifndef UW7 #define DCLTIMEVAL #endif /* UW7 */ #endif /* UNIXWARE */ #endif /* DCLTIMEVAL */ #ifdef DCLTIMEVAL /* Declare timeval ourselves */ struct timeval { long tv_sec; long tv_usec; }; #else /* !DCLTIMEVAL */ #ifndef NOSYSTIMEBH #ifdef SYSTIMEBH #include #endif /* SYSTIMEBH */ #endif /* NOSYSTIMEBH */ #endif /* DCLTIMEVAL */ #endif /* CKTIDLE */ #ifndef SCO_OSR504 #ifdef SELECT_H #include #endif /* SELECT_H */ #endif /* SCO_OSR504 */ #ifndef FD_SETSIZE #ifdef CK_FORWARD_X #define FD_SETSIZE 256 #else #define FD_SETSIZE 32 #endif /* CK_FORWARD_X */ #endif /* FD_SETSIZE */ #ifdef HPUX #ifndef HPUX10 #ifndef HPUX1100 /* The three interior args to select() are (int *) rather than (fd_set *) */ #ifndef INTSELECT #define INTSELECT #endif /* INTSELECT */ #endif /* HPUX1100 */ #endif /* HPUX10 */ #endif /* HPUX */ /* Internal function prototypes */ #ifdef NEWFTP #endif /* NEWFTP */ _PROTOTYP( VOID ttflux, (void) ); _PROTOTYP( VOID doesc, (char) ); _PROTOTYP( int hconne, (void) ); #ifndef NOSHOW _PROTOTYP( VOID shomdm, (void) ); #endif /* NOSHOW */ _PROTOTYP( static int kbget, (void) ); _PROTOTYP( static int ckcputf, (void) ); /* External variables */ extern struct ck_p ptab[]; extern int local, escape, duplex, parity, flow, seslog, sessft, debses, mdmtyp, ttnproto, cmask, cmdmsk, network, nettype, sosi, tnlm, xitsta, what, ttyfd, ttpipe, quiet, backgrd, pflag, tt_crd, tt_lfd, tn_nlm, ttfdflg, tt_escape, justone, carrier, ttpty, hwparity; #ifndef NODIAL extern int dialmhu, dialsta; #endif /* NODIAL */ #ifdef CKLEARN extern FILE * learnfp; extern int learning; static ULONG learnt1; static char learnbuf[LEARNBUFSIZ] = { NUL, NUL }; static int learnbc = 0; static int learnbp = 0; static int learnst = 0; #endif /* CKLEARN */ extern long speed; extern char ttname[], sesfil[], myhost[], *ccntab[]; #ifdef TNCODE extern int tn_b_nlm, tn_rem_echo; #endif /* TNCODE */ #ifdef CK_TRIGGER extern char * tt_trigger[], * triggerval; #endif /* CK_TRIGGER */ #ifdef CKTIDLE extern int tt_idlelimit, tt_idleact; extern char * tt_idlestr; static int idlelimit = 0; #endif /* CKTIDLE */ extern int cx_status; /* CONNECT status code */ extern int nopush; #ifdef CK_APC extern int apcactive; /* Application Program Command (APC) */ extern int apcstatus; /* items ... */ static int apclength = 0; #ifdef DCMDBUF extern char *apcbuf; #else extern char apcbuf[]; #endif /* DCMDBUF */ static int apcbuflen = APCBUFLEN - 2; extern int protocol; #endif /* CK_APC */ #ifndef NOXFER extern int autodl; /* Auto download */ #endif /* NOXFER */ #ifdef CK_AUTODL extern CHAR ksbuf[]; extern CHAR stchr; extern int kstartactive; #endif /* CK_AUTODL */ #ifdef CK_ENCRYPTION extern int me_auth; #endif /* CK_ENCRYPTION */ #ifdef CK_XYZ #ifdef XYZ_INTERNAL static int zmdlok = 1; /* Zmodem autodownloads available */ #else static int zmdlok = 0; /* Depends on external protocol def */ #endif /* XYZ_INTERNAL */ #else static int zmdlok = 0; /* Not available at all */ #endif /* CK_XYZ */ #ifndef NOSETKEY /* Keyboard mapping */ extern KEY *keymap; /* Single-character key map */ extern MACRO *macrotab; /* Key macro pointer table */ static MACRO kmptr = NULL; /* Pointer to current key macro */ #endif /* NOSETKEY */ /* Global variables local to this module */ static int active = 0, quitnow = 0, /* Q was typed */ dohangup = 0, /* H was typed */ inshift = 0, /* SO/SI shift states */ outshift = 0; static char ecbuf[10], *ecbp; /* Escape char buffer & pointer */ #ifdef CK_SMALL #define IBUFL 1536 /* Input buffer length */ #else #define IBUFL 4096 #endif /* CK_SMALL */ static int obc = 0; /* Output buffer count */ #ifndef OXOS #define OBUFL 1024 /* Output buffer length */ #else #define OBUFL IBUFL #endif /* OXOS */ #ifdef BIGBUFOK #define TMPLEN 4096 /* Temporary message buffer length */ #else #define TMPLEN 200 #endif /* BIGBUFOK */ #ifdef DYNAMIC static char *ibuf = NULL, *obuf = NULL, *temp = NULL; /* Buffers */ #else static char ibuf[IBUFL], obuf[OBUFL], temp[TMPLEN]; #endif /* DYNAMIC */ #ifdef TNCODE static char tnopt[4]; #endif /* TNCODE */ #ifdef DYNAMIC static char *ibp; /* Input buffer pointer */ #else static char *ibp = ibuf; /* Input buffer pointer */ #endif /*DYNAMIC */ static int ibc = 0; /* Input buffer count */ #ifdef DYNAMIC static char *obp; /* Output buffer pointer */ #else static char *obp = obuf; /* Output buffer pointer */ #endif /* DYNAMIC */ /* Character-set items */ static int unicode = 0; #ifndef NOCSETS #ifdef CK_ANSIC /* ANSI C prototypes... */ extern CHAR (*xls[MAXTCSETS+1][MAXFCSETS+1])(CHAR); /* Character set */ extern CHAR (*xlr[MAXTCSETS+1][MAXFCSETS+1])(CHAR); /* translation functions */ static CHAR (*sxo)(CHAR); /* Local translation functions */ static CHAR (*rxo)(CHAR); /* for output (sending) terminal chars */ static CHAR (*sxi)(CHAR); /* and for input (receiving) terminal chars. */ static CHAR (*rxi)(CHAR); #else /* Not ANSI C... */ extern CHAR (*xls[MAXTCSETS+1][MAXFCSETS+1])(); /* Character set */ extern CHAR (*xlr[MAXTCSETS+1][MAXFCSETS+1])(); /* translation functions. */ static CHAR (*sxo)(); /* Local translation functions */ static CHAR (*rxo)(); /* for output (sending) terminal chars */ static CHAR (*sxi)(); /* and for input (receiving) terminal chars. */ static CHAR (*rxi)(); #endif /* CK_ANSIC */ extern int language; /* Current language. */ static int langsv; /* For remembering language setting. */ extern struct csinfo fcsinfo[]; /* File character set info. */ extern int tcsr, tcsl; /* Terminal character sets, remote & local. */ static int tcs; /* Intermediate ("transfer") character set. */ static int tcssize = 0; /* Size of tcs */ #ifdef UNICODE /* UTF-8 support */ #ifdef CK_ANSIC extern int (*xl_ufc[MAXFCSETS+1])(USHORT); /* Unicode to FCS */ extern USHORT (*xl_fcu[MAXFCSETS+1])(CHAR); /* FCS to Unicode */ extern int (*xuf)(USHORT); /* Translation function UCS to FCS */ extern USHORT (*xfu)(CHAR); /* Translation function FCS to UCS */ #else extern int (*xl_ufc[MAXFCSETS+1])(); extern USHORT (*xl_fcu[MAXFCSETS+1])(); extern int (*xuf)(); extern USHORT (*xfu)(); #endif /* CK_ANSIC */ #endif /* UNICODE */ #endif /* NOCSETS */ static int printing = 0; /* We do not need to parse and recognize escape sequences if we are being built without character-set support AND without APC support. */ #ifdef NOESCSEQ #ifdef XPRINT #undef XPRINT #endif /* XPRINT */ #else /* NOESCSEQ not defined from outside */ #ifdef NOCSETS /* No character sets */ #ifndef CK_APC /* No APC */ #ifndef XPRINT /* No transparent printing */ #define NOESCSEQ /* So no escape sequence recognizer */ #endif /* XPRINT */ #endif /* CK_APC */ #endif /* NOCSETS */ #endif /* NOESCSEQ */ /* inesc[] and oldesc[] made global 2010/03/01 for INPUT command */ static int escseq = 0; /* 1 = Recognizer is active */ /* static */ int inesc[2] = { 0, 0 }; /* State of sequence recognizer */ /* static */ int oldesc[2] = { -1, -1 }; /* Previous state of recognizer */ #ifdef NOESCSEQ #define ES_NORMAL 0 /* Normal, not in an escape sequence */ #define chkaes(x,y) 0 #else /* As of C-Kermit 5A(178), the CONNECT command skips past ANSI escape sequences to avoid translating the characters within them. This allows the CONNECT command to work correctly with a host that uses a 7-bit ISO 646 national character set, in which characters like '[' would normally be converted to accented letters, ruining the terminal's interpretation (and generation) of escape sequences. As of 5A(190), the CONNECT command responds to APC escape sequences (ESC _ text ESC \) if the user SETs TERMINAL APC ON or UNCHECKED, and the program was built with CK_APC defined. Non-ANSI/ISO-compliant escape sequences are not handled. */ /* States for the escape-sequence recognizer. */ #define ES_NORMAL 0 /* Normal, not in an escape sequence */ #define ES_GOTESC 1 /* Current character is ESC */ #define ES_ESCSEQ 2 /* Inside an escape sequence */ #define ES_GOTCSI 3 /* Inside a control sequence */ #define ES_STRING 4 /* Inside DCS,OSC,PM, or APC string */ #define ES_TERMIN 5 /* 1st char of string terminator */ /* ANSI escape sequence handling. Only the 7-bit form is treated, because translation is not a problem in the 8-bit environment, in which all GL characters are ASCII and no translation takes place. So we don't check for the 8-bit single-character versions of CSI, DCS, OSC, APC, or ST. Here is the ANSI sequence recognizer state table, followed by the code that implements it. Definitions: CAN = Cancel 01/08 Ctrl-X SUB = Substitute 01/10 Ctrl-Z DCS = Device Control Sequence 01/11 05/00 ESC P CSI = Control Sequence Introducer 01/11 05/11 ESC [ ST = String Terminator 01/11 05/12 ESC \ OSC = Operating System Command 01/11 05/13 ESC ] PM = Privacy Message 01/11 05/14 ESC ^ APC = Application Program Command 01/11 05/15 ESC _ ANSI escape sequence recognizer: State Input New State ; Commentary NORMAL (start) ; Start in NORMAL state (any) CAN NORMAL ; ^X cancels (any) SUB NORMAL ; ^Z cancels NORMAL ESC GOTESC ; Begin escape sequence NORMAL other ; NORMAL control or graphic character GOTESC ESC ; Start again GOTESC [ GOTCSI ; CSI GOTESC P STRING ; DCS introducer, consume through ST GOTESC ] STRING ; OSC introducer, consume through ST GOTESC ^ STRING ; PM introducer, consume through ST GOTESC _ STRING ; APC introducer, consume through ST GOTESC 0..~ NORMAL ; 03/00 through 17/14 = Final character GOTESC other ESCSEQ ; Intermediate or ignored control character ESCSEQ ESC GOTESC ; Start again ESCSEQ 0..~ NORMAL ; 03/00 through 17/14 = Final character ESCSEQ other ; Intermediate or ignored control character GOTCSI ESC GOTESC ; Start again GOTCSI @..~ NORMAL ; 04/00 through 17/14 = Final character GOTCSI other ; Intermediate char or ignored control char STRING ESC TERMIN ; Maybe have ST STRING other ; Consume all else TERMIN \ NORMAL ; End of string TERMIN other STRING ; Still in string */ #ifdef XPRINT /* Transparent print support */ /* We can't just print each byte as it comes in because then the printer-off sequence would be sent to the printer. Thus we have to buffer up escape sequences and print them only when they are complete AND we know they are not the printer-off sequence. All printing is done via zsoutx(ZMFILE,s,n). This allows for strings that contain NULs. Don't mix calls to zsoutx() with calls to zchout(), or the output will be scrambled. Also note that when printing a saved-up escape sequence, we never print its final character because that will be printed in the mainline code, upon return from chkaes(). Note that the printer-on sequence is passed to the screen; this is unavoidable, since we don't know what it is until after we get to the end, and for screen display purposes we can't buffer up escape sequences for numerous reasons. Therefore we also must output the printer-off sequence, otherwise a real terminal or emulator will be stuck in print mode. */ extern int tt_print; #define ESCBUFLEN 63 static char escbuf[ESCBUFLEN+1] = { NUL, NUL }; static int escbufc = 0; static int dontprint = 0; VOID printon() { /* Turn printing on */ int x, pp; char * p; extern int printpipe, noprinter; extern char * printername; if (noprinter) { debug(F110,"PRINTER ON NOPRINTER","",0); return; } p = printername; pp = printpipe; if (!p) p = ""; if (!*p) { #ifdef ANYBSD p = "lpr"; #else p = "lp"; #endif /* ANYBSD */ pp = 1; debug(F110,"PRINTER DEFAULT",p,0); } debug(F111,"PRINTER ON",p,pp); if (pp) { /* Printing to pipe */ x = zxcmd(ZMFILE,p); } else { /* Append to file */ struct filinfo xx; xx.bs = 0; xx.cs = 0; xx.rl = 0; xx.org = 0; xx.cc = 0; xx.typ = 0; xx.dsp = XYFZ_A; xx.os_specific = NUL; xx.lblopts = 0; x = zopeno(ZMFILE,p,NULL,&xx); } debug(F101,"PRINTER OPEN","",x); printing = 1; } VOID printoff() { /* Turn printing off */ int x; extern int noprinter; if (noprinter) { printing = 0; debug(F100,"PRINTER OFF NOPRINTER","",0); return; } debug(F100,"PRINTER OFF","",0); if (printing) { x = zclose(ZMFILE); debug(F101,"PRINTER CLOSE","",x); printing = 0; } } #endif /* XPRINT */ /* C H K A E S -- Check ANSI Escape Sequence. Call with EACH character in input stream. src = 0 means c is incoming from remote; 1 = char from keyboard. Sets global inesc[src] variable according to escape sequence state. Returns 0 normally, 1 if an APC sequence is to be executed. Handles transparent printing internally. */ int #ifdef CK_ANSIC chkaes(char c, int src) #else chkaes(c,src) char c; int src; #endif /* CK_ANSIC */ /* chkaes */ { debug(F111,"chkaes entry inesc",ckitoa(src),inesc[src]); debug(F101,"chkaes c","",c); if (src < 0 || src > 1) /* Don't allow bad args. */ return(0); oldesc[src] = inesc[src]; /* Remember previous state */ #ifdef XPRINT if (inesc[src] && !src) { /* Save up escape seq for printing */ if (!c) return(0); /* Ignore NULs */ if (escbufc < ESCBUFLEN) { escbuf[escbufc++] = c; escbuf[escbufc] = NUL; debug(F111,"ESCBUF 1",escbuf,escbufc); } else { /* Buffer overrun */ if (printing && escbufc) /* Print what's there so far */ zsoutx(ZMFILE,escbuf,escbufc); escbufc = 1; /* clear it out */ escbuf[0] = c; /* and start off fresh buffer */ escbuf[1] = NUL; /* with this character. */ } } #endif /* XPRINT */ if (c == CAN || c == SUB) { /* CAN and SUB cancel any sequence */ #ifdef XPRINT if (!src) { if (printing && escbufc > 1) zsoutx(ZMFILE,escbuf,escbufc-1); escbufc = 0; /* Clear buffer */ escbuf[0] = NUL; } #endif /* XPRINT */ inesc[src] = ES_NORMAL; } else /* Otherwise */ switch (inesc[src]) { /* enter state switcher */ case ES_NORMAL: /* NORMAL state */ if (c == ESC) { /* Got an ESC */ inesc[src] = ES_GOTESC; /* Change state to GOTESC */ #ifdef XPRINT if (!src) { escbufc = 1; /* Clear escape sequence buffer */ escbuf[0] = c; /* and deposit the ESC */ escbuf[1] = NUL; debug(F111,"ESCBUF 2",escbuf,escbufc); } #endif /* XPRINT */ } break; /* Otherwise stay in NORMAL state */ case ES_GOTESC: /* GOTESC state - prev char was ESC*/ if (c == '[') { /* Left bracket after ESC is CSI */ inesc[src] = ES_GOTCSI; /* Change to GOTCSI state */ } else if (c == 'P' || (c > 0134 && c < 0140)) { /* P, ], ^, or _ */ inesc[src] = ES_STRING; /* Switch to STRING-absorption state */ #ifdef XPRINT debug(F111,"ESCBUF STRING",escbuf,escbufc); #endif /* XPRINT */ #ifdef CK_APC /* If APC not disabled */ if (!src && c == '_' && (apcstatus & APC_ON)) { debug(F100,"CONNECT APC begin","",0); apcactive = APC_REMOTE; /* Set APC-Active flag */ apclength = 0; /* and reset APC buffer pointer */ } #endif /* CK_APC */ } else if (c > 057 && c < 0177) { /* Final character '0' thru '~' */ inesc[src] = ES_NORMAL; /* Back to normal */ #ifdef XPRINT if (!src) { if (printing && escbufc > 1) { /* Dump esc seq buf to printer */ zsoutx(ZMFILE,escbuf,escbufc-1); debug(F111,"ESCBUF PRINT 1",escbuf,escbufc); } escbufc = 0; /* Clear parameter buffer */ escbuf[0] = NUL; } #endif /* XPRINT */ } else if (c != ESC) { /* ESC in an escape sequence... */ inesc[src] = ES_ESCSEQ; /* starts a new escape sequence */ } break; /* Intermediate or ignored ctrl char */ case ES_ESCSEQ: /* ESCSEQ -- in an escape sequence */ if (c > 057 && c < 0177) { /* Final character '0' thru '~' */ inesc[src] = ES_NORMAL; /* Return to NORMAL state. */ #ifdef XPRINT if (!src) { if (printing && escbufc > 1) { zsoutx(ZMFILE,escbuf,escbufc-1); debug(F111,"ESCBUF PRINT 2",escbuf,escbufc); } escbufc = 0; /* Clear escseq buffer */ escbuf[0] = NUL; } #endif /* XPRINT */ } else if (c == ESC) { /* ESC ... */ inesc[src] = ES_GOTESC; /* starts a new escape sequence */ } break; /* Intermediate or ignored ctrl char */ case ES_GOTCSI: /* GOTCSI -- In a control sequence */ if (c > 077 && c < 0177) { /* Final character '@' thru '~' */ #ifdef XPRINT if (!src && tt_print) { /* Printer enabled? */ if (c == 'i') { /* Final char is "i"? */ char * p = (char *) (escbuf + escbufc - 4); if (!strncmp(p, "\033[5i", 4)) { /* Turn printer on */ printon(); } else if (!strncmp(p, "\033[4i", 4)) { /* Or off... */ int i; printoff(); /* Turn off printer. */ dontprint = 1; for (i = 0; i < escbufc; i++) /* And output the */ ckcputc(escbuf[i]); /* sequence. */ } else if (printing && escbufc > 1) { zsoutx(ZMFILE,escbuf,escbufc-1); debug(F011,"ESCBUF PRINT 3",escbuf,escbufc); } } else if (printing && escbufc > 1) { zsoutx(ZMFILE,escbuf,escbufc-1); debug(F111,"ESCBUF PRINT 4",escbuf,escbufc); } } if (!src) { escbufc = 0; /* Clear esc sequence buffer */ escbuf[0] = NUL; } #endif /* XPRINT */ inesc[src] = ES_NORMAL; /* Return to NORMAL. */ } else if (c == ESC) { /* ESC ... */ inesc[src] = ES_GOTESC; /* starts over. */ } break; case ES_STRING: /* Inside a string */ if (c == ESC) /* ESC may be 1st char of terminator */ inesc[src] = ES_TERMIN; /* Go see. */ #ifdef CK_APC else if (apcactive) { /* If in APC */ if (apclength < apcbuflen) { /* and there is room... */ apcbuf[apclength++] = c; /* deposit this character. */ } else { /* Buffer overrun */ apcactive = 0; /* Discard what we got */ apclength = 0; /* and go back to normal */ apcbuf[0] = 0; /* Not pretty, but what else */ inesc[src] = ES_NORMAL; /* can we do? (ST might not come) */ } } #endif /* CK_APC */ break; /* Absorb all other characters. */ case ES_TERMIN: /* Maybe a string terminator */ if (c == '\\') { /* which must be backslash */ inesc[src] = ES_NORMAL; /* If so, back to NORMAL */ #ifdef XPRINT if (!src) { if (printing && escbufc > 1) { /* If printing... */ /* Print esc seq buffer */ zsoutx(ZMFILE,escbuf,escbufc-1); debug(F111,"ESCBUF PRINT 5",escbuf,escbufc); } escbufc = 0; /* Clear escseq buffer */ escbuf[0] = NUL; } #endif /* XPRINT */ #ifdef CK_APC if (!src && apcactive) { /* If it was an APC string, */ debug(F101,"CONNECT APC terminated","",c); apcbuf[apclength] = NUL; /* terminate it and then ... */ return(1); } #endif /* CK_APC */ } else { /* It's not a backslash so... */ inesc[src] = ES_STRING; /* back to string absorption. */ #ifdef CK_APC if (apcactive) { /* In APC string */ if (apclength+1 < apcbuflen) { /* If enough room */ apcbuf[apclength++] = ESC; /* deposit the Esc */ apcbuf[apclength++] = c; /* and this character too. */ } else { /* Buffer overrun */ apcactive = 0; apclength = 0; apcbuf[0] = 0; inesc[src] = ES_NORMAL; } } #endif /* CK_APC */ } } /* switch() */ debug(F111,"chkaes exit inesc",ckitoa(src),inesc[src]); return(0); } #endif /* NOESCSEQ */ VOID #ifdef CK_ANSIC LOGCHAR(char c) #else LOGCHAR(c) char c; #endif /* CK_ANSIC */ /* LOGCHAR */ { /* Log character c to session log */ /* but skip over escape sequences if session log is text */ if (escseq) { if ((sessft == XYFT_T) && (debses == 0) && (inesc[0] != ES_NORMAL || oldesc[0] != ES_NORMAL)) return; } logchar(c); } /* C K C P U T C -- C-Kermit CONNECT Put Character to Screen */ /* Output is buffered to avoid slow screen writes on fast connections. */ static int ckcputf() { /* Dump the console output buffer */ int x = 0; if (obc > 0) /* If we have any characters, */ x = conxo(obc,obuf); /* dump them, */ obp = obuf; /* reset the pointer */ obc = 0; /* and the counter. */ return(x); /* Return conxo's return code */ } /* NOTE: This is probably the right place for character-set translation, rather than down below in the mainline code. ckcputc() would act like xpnbyte() in ckcfns.c, and ckcgetc() would act like xgnbyte(). This would shield the rest of the code from all the complexities of many-to-one and one-to-many conversions, and would allow handling of Kanji and other CJK sets along with UTF-8 and the rest. */ int ckcputc(c) int c; { int x; *obp++ = c & 0xff; /* Deposit the character */ obc++; /* Count it */ if (ibc == 0 || /* If input buffer about empty */ obc == OBUFL) { /* or output buffer full */ debug(F101,"CONNECT CKCPUTC obc","",obc); x = conxo(obc,obuf); /* dump the buffer, */ obp = obuf; /* reset the pointer */ obc = 0; /* and the counter. */ return(x); /* Return conxo's return code */ } else return(0); } /* C K C G E T C -- C-Kermit CONNECT Get Character */ /* Buffered read from communication device. Returns the next character, refilling the buffer if necessary. On error, returns ttinc's return code (see ttinc() description). Dummy argument for compatible calling conventions with ttinc() so a pointer to this function can be passed to tn_doop(). */ int ckcgetc(dummy) int dummy; { int c, n; #ifdef CK_SSL extern int ssl_active_flag, tls_active_flag; #endif /* CK_SSL */ #ifdef CK_ENCRYPTION /* No buffering for possibly encrypted connections */ if (network && IS_TELNET() && TELOPT_ME(TELOPT_AUTHENTICATION)) return(ttinc(0)); #endif /* CK_ENCRYPTION */ #ifdef CK_SSL if (ssl_active_flag || tls_active_flag) return(ttinc(0)); #endif /* CK_SSL */ if (ibc < 1) { /* Need to refill buffer? */ ibc = 0; /* Yes, reset count */ ibp = ibuf; /* and buffer pointer */ c = ttinc(0); /* Read one character, blocking */ if (c < 0) { /* If error, return error code */ return(c); } else { /* Otherwise, got one character */ *ibp++ = c; /* Advance buffer pointer */ ibc++; /* and count. */ } if ((n = ttchk()) > 0) { /* Any more waiting? */ if (n > (IBUFL - ibc)) /* Get them all at once. */ n = IBUFL - ibc; /* Don't overflow buffer */ if ((n = ttxin(n,(CHAR *)ibp)) > 0) { ibc += n; /* Advance counter */ } } else if (n < 0) { /* Error? */ return(n); /* Return the error code */ } ibp = ibuf; /* Point to beginning of buffer */ } c = *ibp++ & 0xff; /* Get next character from buffer */ ibc--; /* Reduce buffer count */ /* debug(F000,"CKCGETC","",c); */ return(c); /* Return the character */ } /* Keyboard handling, buffered for speed, which is needed when C-Kermit is in CONNECT mode between two other computers that are transferring data. */ static char *kbp; /* Keyboard input buffer pointer */ static int kbc; /* Keyboard input buffer count */ #ifdef CK_SMALL /* Keyboard input buffer length */ #define KBUFL 32 /* Small for PDP-11 UNIX */ #else #define KBUFL 257 /* Regular kernel size for others */ #endif /* CK_SMALL */ #ifdef DYNAMIC static char *kbuf = NULL; #else static char kbuf[KBUFL]; #endif /* DYNAMIC */ /* Macro for reading keystrokes. */ #define CONGKS() (((--kbc)>=0) ? ((int)(*kbp++) & 0377) : kbget()) /* Note that we call read() directly here, normally a no-no, but in this case we know it's UNIX and we're only doing what coninc(0) would have done, except we're reading a block of characters rather than just one. There is, at present, no conxin() analog to ttxin() for chunk reads, and instituting one would only add function-call overhead as it would only be a wrapper for a read() call anyway. Another note: We stick in this read() till the user types something. But we know they already did, since select() said so. Therefore something would need to be mighty wrong before we get stuck here. */ static int /* Keyboard buffer filler */ kbget() { #ifdef EINTR int tries = 10; /* If read() is interrupted, */ int ok = 0; while (tries-- > 0) { /* try a few times... */ #endif /* EINTR */ kbc = conchk(); /* How many chars waiting? */ debug(F101,"kbget kbc","",kbc); if (kbc < 1) kbc = 1; /* If none or dunno, wait for one. */ else if (kbc > KBUFL) /* If too many, */ kbc = KBUFL; /* only read this many. */ if ((kbc = read(0, kbuf, kbc)) < 1) { /* Now read it/them. */ debug(F101,"CONNECT kbget errno","",errno); /* Got an error. */ #ifdef EINTR if (errno == EINTR) /* Interrupted system call. */ continue; /* Try again, up to limit. */ else /* Something else. */ #endif /* EINTR */ return(-1); /* Pass along read() error. */ } #ifdef EINTR else { ok = 1; break; } } if (!ok) return(-1); #endif /* EINTR */ kbp = kbuf; /* Adjust buffer pointer, */ kbc--; /* count, */ return((int)(*kbp++) & 0377); /* and return first character. */ } #ifdef BEBOX /* * CreateSocketPair -- * * This procedure creates a connected socket pair * * Results: * 0 if OK, the error if not OK. * * Side effects: * None */ int socketpair(int *pair) { int servsock; int val; struct sockaddr_in serv_addr, cli_addr; extern char myipaddr[]; debug(F110,"socketpair",myipaddr,0); if (myipaddr[0] == 0) getlocalipaddr(); servsock = socket(AF_INET, SOCK_STREAM, 0); if (servsock == 0) { return h_errno; } debug(F111,"socketpair","socket",servsock); memset(&serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); serv_addr.sin_port = htons(0); val = sizeof(serv_addr); if (bind(servsock, (struct sockaddr *) &serv_addr, val) < 0) { closesocket(servsock); return h_errno; } debug(F111,"socketpair","bind",0); listen(servsock, 1); debug(F111,"socketpair","listen",0); if (getsockname(servsock, (struct sockaddr *) &serv_addr, &val) < 0) { closesocket(servsock); return h_errno; } debug(F111,"socketpair","getsockname",0); pair[0] = socket(AF_INET, SOCK_STREAM, 0); if (pair[0] == 0) { closesocket(servsock); return h_errno; } debug(F111,"socketpair","socket",pair[0]); memset(&cli_addr, 0, sizeof(cli_addr)); cli_addr.sin_family = AF_INET; cli_addr.sin_addr.s_addr = inet_addr(myipaddr[0]?myipaddr:"127.0.0.1"); cli_addr.sin_port = serv_addr.sin_port; if (connect(pair[0],(struct sockaddr *) &cli_addr, sizeof(cli_addr)) < 0) { closesocket(pair[0]); closesocket(servsock); return h_errno; } debug(F111,"socketpair","connect",0); pair[1] = accept(servsock, (struct sockaddr *) &serv_addr, &val); if (pair[1] == 0) { closesocket(pair[0]); closesocket(servsock); return h_errno; } debug(F111,"socketpair","accept",pair[1]); closesocket(servsock); debug(F111,"socketpair","closesocket",0); return 0; } long kbdread(void * param) { int sock = (int) param; char ch; int rc = 0; debug(F111,"kbdread","sock",sock); while (rc >= 0) { rc = read(fileno(stdin), &ch, 1); /* Read a character. */ if (rc > 0) { rc = send(sock,&ch,1,0); /* debug(F000,"kbdread","send()",ch); */ printf("\r\ngot: %c rc = %d\r\n",ch,rc); } else msleep(100); } debug(F110,"kbdread","terminating",0); return(rc); } #endif /* BEBOX */ #ifdef CKLEARN static VOID learnchar(c) int c; { /* Learned script keyboard character */ int cc; char xbuf[8]; if (!learning || !learnfp) return; switch (learnst) { /* Learn state... */ case 0: /* Neutral */ case 1: /* Net */ if (learnbc > 0) { /* Have net characters? */ char buf[LEARNBUFSIZ]; int i, j, n; ULONG t; t = (ULONG) time(0); /* Calculate INPUT timeout */ j = t - learnt1; j += (j / 4) > 0 ? (j / 4) : 1; /* Add some slop */ if (j < 2) j = 2; /* 2 seconds minimum */ fputs("\nINPUT ",learnfp); /* Give INPUT command for them */ fputs(ckitoa(j),learnfp); fputs(" {",learnfp); learnt1 = t; n = LEARNBUFSIZ; if (learnbc < LEARNBUFSIZ) { /* Circular buffer */ n = learnbc; /* hasn't wrapped yet. */ learnbp = 0; } j = 0; /* Copy to linear buffer */ for (i = 0; i < n; i++) { /* Number of chars in circular buf */ cc = learnbuf[(learnbp + i) % LEARNBUFSIZ]; /* Later account for prompts that end with a newline? */ if (cc == CR && j > 0) { if (buf[j-1] != LF) j = 0; } buf[j++] = cc; } for (i = 0; i < j; i++) { /* Now copy out the buffer */ cc = buf[i]; /* interpreting control chars */ if (cc == 0) { /* We don't INPUT NULs */ continue; } else if (cc < SP || /* Controls need quoting */ (cc > 126 && cc < 160)) { ckmakmsg(xbuf,8,"\\{",ckitoa((int)cc),"}",NULL); fputs(xbuf,learnfp); } else { /* Plain character */ putc(cc,learnfp); } } fputs("}\nIF FAIL STOP 1 INPUT timeout",learnfp); learnbc = 0; } learnbp = 0; fputs("\nPAUSE 1\nOUTPUT ",learnfp); /* Emit OUTPUT and fall thru */ case 2: /* Already in Keyboard state */ if (c == 0) { fputs("\\N",learnfp); } else if (c == -7) { fputs("\\B",learnfp); } else if (c == -8) { fputs("\\L",learnfp); } else if (c < SP || (c > 126 && c < 160)) { ckmakmsg(xbuf,8,"\\{",ckitoa((int)c),"}",NULL); fputs(xbuf,learnfp); } else { putc(c,learnfp); } } } #endif /* CKLEARN */ static int printbar = 0; #define OUTXBUFSIZ 15 static CHAR inxbuf[OUTXBUFSIZ+1]; /* Host-to-screen expansion buffer */ static int inxcount = 0; /* and count */ static CHAR outxbuf[OUTXBUFSIZ+1]; /* Keyboard-to-host expansion buf */ static int outxcount = 0; /* and count */ int conect() { int rc = 0; /* Return code: 0 = fail, 1 = OK */ int i, x = 0, prev = -1; /* Reason code in cx_status */ #ifdef CKLEARN int crflag = 0; #endif /* CKLEARN */ register int c = -1, c2, csave; /* Characters */ #ifdef TNCODE int tx; /* For Telnet negotiations */ #endif /* TNCODE */ int apcrc = 0; /* For APC and transparent print */ int n, kbin, scrnout; /* select() items... */ fd_set in, out, err; /* File descriptor sets */ int gotnet = 0; /* Flag for net ready to read */ int gotkbd = 0; /* Flag for keyboard ready to read */ int oldprt = 0; /* Used with printing */ int msgflg = 0; char cbuf[2]; /* Ditto */ #ifdef BEBOX int tid = 0; /* Thread ID */ int pair[2]; /* Socket Pair */ CHAR ch; CHAR buf[64]; #endif /* BEBOX */ cx_status = CSX_INTERNAL; debok = 1; #ifdef BEBOX { /* Create a socket pair to be used for the keyboard input */ if (socketpair(pair)) { debug(F110,"conect","unable to create socket pair",0); return(-1); } debug(F111,"connect","socket pair[0]",pair[0]); debug(F111,"connect","socket pair[1]",pair[1]); /* Assign one end of the socket to kbin */ kbin = pair[0]; tid = spawn_thread(kbdread, "Kbd to Socket Pair", B_NORMAL_PRIORITY, (void *)pair[1] ); resume_thread(tid); debug(F110,"connect","tid",tid); } #else /* BEBOX */ kbin = fileno(stdin); /* stdin file descriptor */ #endif /* BEBOX */ scrnout = fileno(stdout); /* stdout file descriptor */ #ifdef CK_TRIGGER makestr(&triggerval,NULL); /* Reset trigger */ #endif /* CK_TRIGGER */ #ifdef XPRINT escbufc = 0; /* Reset esc-sequence buffer */ escbuf[0] = NUL; #endif /* XPRINT */ cbuf[1] = NUL; ttimoff(); /* Turn off any timer interrupts */ if (!local) { /* Be sure we're not in remote mode */ #ifdef NETCONN #ifdef NEWFTP if (ftpisconnected()) printf("Sorry, you can't CONNECT to an FTP server\n"); else #endif /* NEWFTP */ printf("Sorry, you must SET LINE or SET HOST first\n"); #else printf("Sorry, you must SET LINE first\n"); #endif /* NETCONN */ return(0); } if (speed < 0L && network == 0 && ttfdflg == 0) { printf("Sorry, you must SET SPEED first\n"); return(0); } #ifdef TCPSOCKET if (network && !ttpipe && (nettype != NET_TCPB && nettype != NET_PTY)) { printf("Sorry, network type not supported\n"); return(0); } #endif /* TCPSOCKET */ #ifdef DYNAMIC if (!ibuf) { if (!(ibuf = malloc(IBUFL+1))) { /* Allocate input line buffer */ printf("Sorry, CONNECT input buffer can't be allocated\n"); return(0); } else { ibp = ibuf; ibc = 0; } } if (!obuf) { if (!(obuf = malloc(OBUFL+1))) { /* Allocate output line buffer */ printf("Sorry, CONNECT output buffer can't be allocated\n"); return(0); } else { obp = obuf; obc = 0; } } if (!kbuf) { if (!(kbuf = malloc(KBUFL+1))) { /* Allocate keyboard input buffer */ printf("Sorry, CONNECT keyboard buffer can't be allocated\n"); return(0); } } if (!temp) { if (!(temp = malloc(TMPLEN+1))) { /* Allocate temporary buffer */ printf("Sorry, CONNECT temporary buffer can't be allocated\n"); return(0); } } #else obp = obuf; obc = 0; #endif /* DYNAMIC */ kbp = kbuf; /* Always clear these. */ *kbp = NUL; /* No need to preserve them between */ kbc = 0; /* CONNECT sessions. */ #ifdef DEBUG if (deblog) { debug(F101,"CONNECT conect entry ttyfd","",ttyfd); debug(F101,"CONNECT conect entry ibc","",ibc); debug(F101,"CONNECT conect entry obc","",obc); debug(F101,"CONNECT conect entry kbc","",kbc); #ifdef CK_TRIGGER debug(F110,"CONNECT conect trigger",tt_trigger[0],0); #endif /* CK_TRIGGER */ if (ttyfd > -1) { n = ttchk(); debug(F101,"CONNECT conect entry ttchk","",n); } } #endif /* DEBUG */ if (ttyfd < 0) { /* If communication device not open */ #ifdef TTLEBUF int n = le_inbuf(); debug(F111,"CONNECT le_inbuf()","ttyfd < 0",n); if (n > 0) { while (n--) { CHAR ch; le_getchar(&ch); conoc(ch); } return(0); } #endif /* TTLEBUF */ debug(F101,"CONNECT ttnproto","",ttnproto); debug(F111,"CONNECT opening",ttname,0); /* Open it now */ if (ttopen(ttname, &local, network ? -nettype : mdmtyp, 0 ) < 0) { ckmakmsg(temp,TMPLEN,"Sorry, can't open ",ttname,NULL,NULL); perror(temp); debug(F110,"CONNECT open failure",ttname,0); return(0); } #ifdef IKS_OPTION /* If peer is in Kermit server mode, return now. */ if (TELOPT_SB(TELOPT_KERMIT).kermit.u_start) { cx_status = CSX_IKSD; return(0); } #endif /* IKS_OPTION */ } dohangup = 0; /* Hangup not requested yet */ msgflg = !quiet #ifdef CK_APC && !apcactive #endif /* CK_APC */ ; if (msgflg) { #ifdef NETCONN if (network) { #ifdef CK_ENCRYPTION extern int me_encrypt, u_encrypt; if (ck_tn_encrypting() && ck_tn_decrypting()) printf("SECURE connection to host %s",ttname); else #endif /* CK_ENCRYPTION */ if (ttpipe || ttpty) printf("Connecting via command \"%s\"",ttname); else printf("Connecting to host %s",ttname); } else { #endif /* NETCONN */ printf("Connecting to %s",ttname); if (speed > -1L) printf(", speed %ld",speed); #ifdef NETCONN } #endif /* NETCONN */ if (tt_escape) { printf("\r\n"); shoesc(escape); printf("Type the escape character followed by C to get back,\r\n"); printf("or followed by ? to see other options.\r\n"); } else { printf(".\r\n\nESCAPE CHARACTER IS DISABLED\r\n\n"); } if (seslog) { extern int slogts; char * s = ""; switch (sessft) { case XYFT_D: s = "debug"; break; case XYFT_T: s = slogts ? "timestamped-text" : "text"; break; default: s = "binary"; } printf("Session Log: %s, %s (%d) \r\n",sesfil,s,sessft); } if (debses) printf("Debugging Display...)\r\n"); } /* Condition console terminal and communication line */ if (conbin((char)escape) < 0) { printf("Sorry, can't condition console terminal\n"); fflush(stdout); return(0); } debug(F101,"CONNECT cmask","",cmask); debug(F101,"CONNECT cmdmsk","",cmdmsk); debug(F101,"CONNECT speed before ttvt","",speed); if ((n = ttvt(speed,flow)) < 0) { /* Enter "virtual terminal" mode */ if (!network) { debug(F101,"CONNECT ttvt","",n); tthang(); /* Hang up and close the device. */ ttclos(0); dologend(); if (ttopen(ttname, /* Open it again... */ &local, network ? -nettype : mdmtyp, 0 ) < 0) { cx_status = CSX_INTERNAL; ckmakmsg(temp,TMPLEN,"Sorry, can't reopen ",ttname,NULL,NULL); perror(temp); return(0); } #ifdef IKS_OPTION if (TELOPT_SB(TELOPT_KERMIT).kermit.u_start) { cx_status = CSX_IKSD; return(0); } #endif /* IKS_OPTION */ if (ttvt(speed,flow) < 0) { /* Try virtual terminal mode again. */ conres(); /* Failure this time is fatal. */ printf("Sorry, Can't condition communication line\n"); cx_status = CSX_INTERNAL; return(0); } } } debug(F101,"CONNECT ttvt ok, escape","",escape); /* Despite ttvt() this is still needed in HP-UX */ /* because of the HP-9000 key.*/ signal(SIGINT, SIG_IGN); signal(SIGQUIT, SIG_IGN); debug(F101,"CONNECT carrier-watch","",carrier); if ((!network #ifdef TN_COMPORT || istncomport() #endif /* TN_COMPORT */ ) && (carrier != CAR_OFF)) { int x; x = ttgmdm(); debug(F100,"CONNECT ttgmdm","",x); if ((x > -1) && !(x & BM_DCD)) { #ifndef NOHINTS extern int hints; #endif /* NOHINTS */ debug(F100,"CONNECT ttgmdm CD test fails","",x); conres(); printf("?Carrier required but not detected.\n"); #ifndef NOHINTS cx_status = CSX_CARRIER; if (!hints) return(0); printf("***********************************\n"); printf(" Hint: To CONNECT to a serial device that\n"); printf(" is not presenting the Carrier Detect signal,\n"); printf(" first tell C-Kermit to:\n\n"); printf(" SET CARRIER-WATCH OFF\n\n"); printf("***********************************\n\n"); #endif /* NOHINTS */ return(0); } debug(F100,"CONNECT ttgmdm ok","",0); } /* Now we are connected. */ if (msgflg || printbar) printf("----------------------------------------------------\r\n"); fflush(stdout); #ifndef NOCSETS /* Set up character set translations */ unicode = 0; /* Assume Unicode won't be involved */ tcs = 0; /* "Transfer" or "Other" charset */ sxo = rxo = NULL; /* Initialize byte-to-byte functions */ sxi = rxi = NULL; if (tcsr != tcsl) { /* Remote and local sets differ... */ #ifdef UNICODE if (tcsr == FC_UTF8 || /* Remote charset is UTF-8 */ tcsl == FC_UTF8) { /* or local one is. */ xuf = xl_ufc[tcsl]; /* Incoming Unicode to local */ if (xuf || tcsl == FC_UTF8) { tcs = (tcsr == FC_UTF8) ? tcsl : tcsr; /* The "other" set */ xfu = xl_fcu[tcs]; /* Local byte to remote Unicode */ if (xfu) unicode = (tcsr == FC_UTF8) ? 1 : 2; } tcssize = fcsinfo[tcs].size; /* Size of other character set. */ } else { #endif /* UNICODE */ tcs = gettcs(tcsr,tcsl); /* Get intermediate set. */ sxo = xls[tcs][tcsl]; /* translation function */ rxo = xlr[tcs][tcsr]; /* pointers for output functions */ sxi = xls[tcs][tcsr]; /* and for input functions. */ rxi = xlr[tcs][tcsl]; #ifdef UNICODE } #endif /* UNICODE */ } /* This is to prevent use of zmstuff() and zdstuff() by translation functions. They only work with disk i/o, not with communication i/o. Luckily Russian translation functions don't do any stuffing... */ langsv = language; #ifndef NOCYRIL if (language != L_RUSSIAN) #endif /* NOCYRIL */ language = L_USASCII; #ifdef COMMENT #ifdef DEBUG if (deblog) { debug(F101,"CONNECT tcs","",tcs); debug(F101,"CONNECT tcsl","",tcsl); debug(F101,"CONNECT tcsr","",tcsr); debug(F101,"CONNECT fcsinfo[tcsl].size","",fcsinfo[tcsl].size); debug(F101,"CONNECT fcsinfo[tcsr].size","",fcsinfo[tcsr].size); debug(F101,"CONNECT unicode","",unicode); } #endif /* DEBUG */ #endif /* COMMENT */ #ifdef CK_XYZ #ifndef XYZ_INTERNAL { extern int binary; /* See about ZMODEM autodownloads */ char * s; s = binary ? ptab[PROTO_Z].p_b_rcmd : ptab[PROTO_Z].p_t_rcmd; if (!s) s = ""; zmdlok = (*s != NUL); /* OK if we have external commands */ } #endif /* XYZ_INTERNAL */ #endif /* CK_XYZ */ #ifndef NOESCSEQ /* We need to activate the escape-sequence recognition feature when: (a) translation is elected, AND (b) the local and/or remote set is a 7-bit set other than US ASCII; Or: SET SESSION-LOG is TEXT (so we can strip escape sequences out of the log); Or: SET TERMINAL APC is not OFF (handled in the next statement). */ escseq = (tcs != TC_TRANSP) && /* Not transparent */ (fcsinfo[tcsl].size == 128 || fcsinfo[tcsr].size == 128) && /* 7 bits */ (fcsinfo[tcsl].code != FC_USASCII); /* But not ASCII */ #endif /* NOESCSEQ */ #endif /* NOCSETS */ #ifndef NOESCSEQ if (!escseq) { /* 2009/10/22 */ if (seslog && !sessft) escseq = 1; } #ifdef CK_APC escseq = escseq || (apcstatus & APC_ON); apcactive = 0; /* An APC command is not active */ apclength = 0; /* ... */ #endif /* CK_APC */ #ifdef XPRINT escseq |= tt_print; #endif /* XPRINT */ /* Initial state of recognizer */ inesc[0] = ES_NORMAL; /* Remote to screen */ inesc[1] = ES_NORMAL; /* Keyboard to remote */ debug(F101,"CONNECT escseq","",escseq); #endif /* NOESCSEQ */ if (ttyfd > -1) { /* (just in case...) */ what = W_CONNECT; /* Keep track of what we're doing */ active = 1; } #ifdef CKLEARN if (learning) { /* Learned script active... */ learnbp = 0; /* INPUT buffer pointer */ learnbc = 0; /* INPUT buffer count */ learnst = 0; /* State (0 = neutral, none) */ learnt1 = (ULONG) time(0); } #endif /* CKLEARN */ #ifdef CKTIDLE idlelimit = tt_idlelimit; #endif /* CKTIDLE */ while (active) { /* Big loop... */ debug(F100,"CONNECT top of loop","",0); FD_ZERO(&in); /* Clear select() structs */ FD_ZERO(&out); FD_ZERO(&err); gotkbd = 0; gotnet = ttpeek(); /* Something sitting in ckutio buf */ debug(F101,"CONNECT ttpeek","",gotnet); if ( #ifndef NOSETKEY !kmptr /* Check for key macro active */ #else 1 #endif /* NOSETKEY */ ) { if (obc) { /* No key macro - set up for select */ FD_SET(ttyfd, &out); /* Have stuff to send to net */ } else { FD_SET(kbin, &in); /* Need to read stuff from keyboard */ } #ifdef BEBOX if (!(ibc || gotnet > 0)) FD_SET(ttyfd, &in); /* Need to read stuff from net */ #else /* BEBOX */ if (ibc || gotnet > 0) { FD_SET(scrnout, &out); /* Have stuff to put on screen */ } else { FD_SET(ttyfd, &in); /* Need to read stuff from net */ } #endif /* BEBOX */ FD_SET(ttyfd, &err); #ifdef CK_FORWARD_X fwdx_init_fd_set(&in); #endif /* CK_FORWARD_X */ /* Wait till the first one of the above is ready for i/o */ /* or TERM IDLE-SEND is active and we time out. */ errno = 0; #ifdef CKTIDLE /* This really could be moved out of the loop... */ if (idlelimit) { /* Idle timeout set */ struct timeval tv; if (idlelimit > 0) { /* Positive = sec */ tv.tv_sec = (long) idlelimit; tv.tv_usec = 0L; } else { /* Negative = millisec */ long u = (0 - idlelimit); tv.tv_sec = u / 1000L; tv.tv_usec = ((u % 1000L) * 1000L); } #ifdef INTSELECT c = select(FD_SETSIZE,(int *)&in,(int *)&out,(int *)&err, &tv); #else c = select(FD_SETSIZE, &in, &out, &err, &tv); #endif /* INTSELECT */ } else #endif /* CKTIDLE */ #ifdef INTSELECT c = select(FD_SETSIZE, (int *)&in, (int *)&out, (int *)&err, 0); #else c = select(FD_SETSIZE, &in, &out, &err, 0); #endif /* INTSELECT */ if (c < 1) { #ifdef CKTIDLE if (c == 0) { /* Timeout */ debug(F101,"CONNECT select() timeout","",tt_idleact); switch (tt_idleact) { case IDLE_HANG: { /* Hang up */ int x = 0; #ifndef NODIAL if (dialmhu) x = mdmhup(); if (x < 1) #endif /* NODIAL */ tthang(); /* fall thru deliberately... */ } case IDLE_RET: /* Return to command mode */ cx_status = CSX_IDLE; active = 0; continue; case IDLE_OUT: /* OUTPUT a string */ if (tt_idlestr) { int len = strlen(tt_idlestr); if (len > 0) ttol((CHAR *)tt_idlestr,len); else ttoc(NUL); /* No string, send a NUL */ } else ttoc(NUL); /* No string, send a NUL */ continue; case IDLE_EXIT: /* Exit from Kermit */ doexit(GOOD_EXIT,xitsta); #ifdef TNCODE case IDLE_TAYT: /* Send Telnet Are You There? */ if (network && IS_TELNET()) { tnopt[0] = (CHAR) IAC; tnopt[1] = (CHAR) TN_AYT; tnopt[2] = NUL; if (ttol((CHAR *)tnopt,2) < 0) active = 0; } continue; case IDLE_TNOP: /* Send Telnet NOP */ if (network && IS_TELNET()) { tnopt[0] = (CHAR) IAC; tnopt[1] = (CHAR) TN_NOP; tnopt[2] = NUL; if (ttol((CHAR *)tnopt,2) < 0) active = 0; } continue; #endif /* TNCODE */ } } #endif /* CKTIDLE */ debug(F101,"CONNECT select() errno","",errno); /* A too-big first arg to select() gets EBADF */ #ifdef EINTR if (c == -1) { if (errno == EINTR) { continue; } } #endif /* EINTR */ sleep(1); continue; } #ifndef BEBOX #ifdef DEBUG if (FD_ISSET(scrnout, &out)) { debug(F100,"CONNECT SELECT scrnout","",0); } #endif /* DEBUG */ #endif /* BEBOX */ #ifdef CK_FORWARD_X fwdx_check_sockets(&in); #endif /* CK_FORWARD_X */ if (FD_ISSET(ttyfd, &in)) { /* Read from net? */ debug(F110,"CONNECT SELECT ttyfd","in",0); FD_CLR(ttyfd, &in); gotnet = 1; /* Net is ready */ } if (FD_ISSET(kbin, &in)) { /* Read from keyboard? */ debug(F100,"CONNECT SELECT kbin","",0); FD_CLR(kbin, &in); gotkbd = 1; /* Keyboard is ready */ } if (FD_ISSET(ttyfd, &err)) { debug(F110,"CONNECT SELECT ttyfd","err",0); FD_CLR(ttyfd, &err); #ifdef NETPTY #ifdef HAVE_PTYTRAP /* Special handling for HP-UX pty i/o */ if (ttpty) { if (pty_trap_handler(ttyfd) > 0) { ttclos(0); goto conret1; } continue; } #endif /* HAVE_PTYTRAP */ #endif /* NETPTY */ gotnet = 1; /* Net is ready (don't set if pty) */ } } #ifdef DEBUG if (deblog) { debug(F101,"CONNECT gotkbd","",gotkbd); debug(F101,"CONNECT kbc","",kbc); #ifdef COMMENT #ifndef NOSETKEY debug(F101,"CONNECT kmptr","",kmptr); #endif /* NOSETKEY */ #endif /* COMMENT */ } #endif /* DEBUG */ while (gotkbd || kbc > 0 /* If we have keyboard chars */ #ifndef NOSETKEY || kmptr #endif /* NOSETKEY */ ) { #ifndef NOSETKEY if (kmptr) { /* Have current macro? */ debug(F100,"CONNECT kmptr non NULL","",0); if ((c = (CHAR) *kmptr++) == NUL) { /* Get char from it */ debug(F100,"CONNECT macro empty, continuing","",0); kmptr = NULL; /* If no more chars, */ continue; /* Reset pointer and continue */ } debug(F000,"CONNECT char from macro","",c); } else { /* No macro... */ #endif /* NOSETKEY */ #ifdef BEBOX { int rc = 0; if ((rc = recv(kbin,buf,1,0)) > 0) c = buf[0]; else c = -1; debug(F111,"recv","rc",rc); printf("\r\nrecv: %c rc=%d\r\n",buf[0],rc); } #else /* BEBOX */ c = CONGKS(); /* Yes, read from keyboard */ #endif /* BEBOX */ gotkbd = 0; /* Turn off select() result flag */ #ifndef NOSETKEY } #endif /* NOSETKEY */ if (c == -1) { #ifdef EINTR if (errno == EINTR) continue; #endif /* EINTR */ cx_status = CSX_IOERROR; conoc(BEL); goto conret0; } c &= cmdmsk; /* Do any requested masking */ #ifndef NOSETKEY /* Note: kmptr is NULL if we got character c from the keyboard, and it is not NULL if it came from a macro. In the latter case, we must avoid expanding it again. */ if (!kmptr && macrotab[c]) { /* Macro definition for c? */ debug(F000,"CONNECT macro key",macrotab[c],c); kmptr = macrotab[c]; /* Yes, set up macro pointer */ continue; /* and restart the loop, */ } else c = keymap[c]; /* else use single-char keymap */ #endif /* NOSETKEY */ if ( #ifndef NOSETKEY !kmptr && #endif /* NOSETKEY */ (tt_escape && ((c & 0xff) == escape))) { /* Escape char? */ debug(F000,"CONNECT got escape","",c); #ifdef BEBOX if (recv(kbin,buf,1,0)>=0) c = buf[0]; else c = -1; #else /* BEBOX */ c = CONGKS() & 0x7f; /* Read argument */ #endif /* BEBOX */ doesc((char) c); /* Handle it */ continue; /* Back to loop */ } csave = c; /* Save it before translation */ /* for local echoing. */ #ifdef CKLEARN crflag = (c == CR); /* Remember if it was CR. */ #endif /* CKLEARN */ #ifndef NOCSETS if (inesc[1] == ES_NORMAL) { /* If not inside escape seq.. */ /* Translate character sets */ #ifdef UNICODE int x; if (unicode == 1) { /* Remote is UTF-8 */ outxcount = b_to_u((CHAR)c,outxbuf,OUTXBUFSIZ,tcssize); outxbuf[outxcount] = NUL; } else if (unicode == 2) { /* Local is UTF-8 */ x = u_to_b((CHAR)c); if (x < 0) continue; outxbuf[0] = (unsigned)(x & 0xff); outxcount = 1; outxbuf[outxcount] = NUL; } else { #endif /* UNICODE */ if (sxo) c = (*sxo)((char)c); /* Local-intermediate */ if (rxo) c = (*rxo)((char)c); /* Intermediate-remote */ outxbuf[0] = c; outxcount = 1; outxbuf[outxcount] = NUL; #ifdef UNICODE } #endif /* UNICODE */ } else { outxbuf[0] = c; outxcount = 1; outxbuf[outxcount] = NUL; } if (escseq) apcrc = chkaes((char)c,1); #else /* NOCSETS */ outxbuf[0] = c; outxcount = 1; outxbuf[outxcount] = NUL; #endif /* NOCSETS */ debug(F111,"OUTXBUF",outxbuf,outxcount); for (i = 0; i < outxcount; i++) { c = outxbuf[i]; /* If Shift-In/Shift-Out is selected and we have a 7-bit connection, handle shifting here. */ if (sosi) { /* Shift-In/Out selected? */ if (cmask == 0177) { /* In 7-bit environment? */ if (c & 0200) { /* 8-bit character? */ if (outshift == 0) { /* If not shifted, */ ttoc(dopar(SO)); /* shift. */ outshift = 1; } } else { if (outshift == 1) { /* 7-bit character */ ttoc(dopar(SI)); /* If shifted, */ outshift = 0; /* unshift. */ } } } if (c == SO) outshift = 1; /* User typed SO */ if (c == SI) outshift = 0; /* User typed SI */ } c &= cmask; /* Apply Kermit-to-host mask now. */ if (c == '\015') { /* Carriage Return */ int stuff = -1; if (tnlm) { /* TERMINAL NEWLINE ON */ stuff = LF; /* Stuff LF */ #ifdef TNCODE } else if (network && /* TELNET NEWLINE ON/OFF/RAW */ IS_TELNET()) { switch (!TELOPT_ME(TELOPT_BINARY) ? tn_nlm : tn_b_nlm){ case TNL_CRLF: stuff = LF; break; case TNL_CRNUL: stuff = NUL; break; } #endif /* TNCODE */ } if (stuff > -1) { ttoc(dopar('\015')); /* Send CR */ if (duplex) conoc('\015'); /* Maybe echo CR */ c = stuff; /* Char to stuff */ csave = c; } } #ifdef TNCODE /* If user types the 0xff character (TELNET IAC), it must be doubled. */ else /* Not CR */ if ((dopar((CHAR) c) == IAC) && /* IAC (0xff) */ network && IS_TELNET()) { /* Send one now */ ttoc((char)IAC); /* and the other one just below. */ } #endif /* TNCODE */ /* Send the character */ x = ttoc((char)dopar((CHAR) c)); if (x > -1) { #ifdef CKLEARN if (learning) { /* Learned script active */ if (crflag) { /* User typed CR */ learnchar(CR); /* Handle CR */ learnst = 0; /* Shift to Neutral */ } else { learnchar(c); /* Not CR */ learnst = 2; /* Change state to Keyboard */ } } #endif /* CKLEARN */ if (duplex) { /* If half duplex, must echo */ if (debses) conol(dbchr(csave)); /* the original char */ else /* not the translated one */ conoc((char)csave); if (seslog) { /* And maybe log it too */ c2 = csave; if (sessft == 0 && csave == '\r') c2 = '\n'; LOGCHAR((char)c2); } } } else { perror("\r\nCan't send character"); cx_status = CSX_IOERROR; active = 0; break; } } } if (FD_ISSET(ttyfd, &out)) { FD_CLR(ttyfd, &out); } while (gotnet > 0 || ibc > 0) { gotnet = 0; prev = c; c = ckcgetc(0); /* Get next character */ if (c < 0) { /* Failed... */ ckcputf(); /* Flush CONNECT output buffer */ if (msgflg) { printf("\r\nCommunications disconnect "); #ifdef COMMENT if (c == -3 #ifdef ultrix /* This happens on Ultrix if there's no carrier */ && errno != EIO #endif /* ultrix */ #ifdef UTEK /* This happens on UTEK if there's no carrier */ && errno != EWOULDBLOCK #endif /* UTEK */ ) perror("\r\nCan't read character"); #endif /* COMMENT */ } #ifdef NOSETBUF fflush(stdout); #endif /* NOSETBUF */ dologend(); tthang(); /* Hang up the connection */ debug(F111,"CONNECT i/o error 1",ck_errstr(),errno); cx_status = CSX_HOSTDISC; goto conret0; } #ifdef TNCODE tx = 0; if ((c == NUL) && network && IS_TELNET()) { if (prev == CR) { /* Discard of if peer */ if (!TELOPT_U(TELOPT_BINARY)) { /* not in binary mode */ debug(F111,"CONNECT NUL",ckitoa(prev),c); ckcputf(); /* Flush screen output buffer */ break; } } } debug(F111,"CONNECT","c",c); debug(F111,"CONNECT","network",network); debug(F111,"CONNECT","IS_TELNET",IS_TELNET()); if ((c == IAC) && network && IS_TELNET()) { #ifdef CK_ENCRYPTION int x_auth = TELOPT_ME(TELOPT_AUTHENTICATION); #else int x_auth = 0; #endif /* CK_ENCRYPTION */ int me_bin = TELOPT_ME(TELOPT_BINARY); int u_bin = TELOPT_U(TELOPT_BINARY); debug(F100,"CONNECT got IAC","",0); ckcputf(); /* Dump screen-output buffer */ if ((tx = tn_doop((CHAR)(c & 0xff),duplex,ckcgetc)) == 0) { if (me_bin != TELOPT_ME(TELOPT_BINARY)) { me_bin = TELOPT_ME(TELOPT_BINARY); } else if (u_bin != TELOPT_U(TELOPT_BINARY)) { u_bin = TELOPT_U(TELOPT_BINARY); #ifdef CK_ENCRYPTION /* Here we have to push back any bytes we have read using block reads, so we can read them again using single-character reads, so they can be decrypted in case there was a switch to encryption in the block. Note that we can't handle switches in the encryption state itself this way -- which would be nice, since it would eliminate the need for single-character reads. Why? Because if a series of characters has already been decrypted that shouldn't have been, then (a) it's ruined, and (b) so is the state of the decryption machine. Too bad. */ } else if (TELOPT_ME(TELOPT_AUTHENTICATION) != 0 && TELOPT_ME(TELOPT_AUTHENTICATION) != x_auth ) { if (ttpushback((CHAR *)ibp,ibc) > -1) { ibc = 0; ibp = ibuf; } #endif /* CK_ENCRYPTION */ } continue; } else if (tx == -1) { /* I/O error */ if (msgflg) printf("\r\nCommunications disconnect "); #ifdef NOSETBUF fflush(stdout); #endif /* NOSETBUF */ dologend(); debug(F111,"CONNECT i/o error 2",ck_errstr(),errno); cx_status = CSX_IOERROR; goto conret0; } else if (tx == -2) { /* I/O error */ if (msgflg) printf("\r\nConnection closed by peer"); #ifdef NOSETBUF fflush(stdout); #endif /* NOSETBUF */ dologend(); debug(F111,"CONNECT i/o error 3",ck_errstr(),errno); cx_status = CSX_IOERROR; goto conret0; } else if (tx == -3) { /* I/O error */ if (msgflg) printf("\r\nConnection closed due to telnet policy"); #ifdef NOSETBUF fflush(stdout); #endif /* NOSETBUF */ dologend(); debug(F111,"CONNECT i/o error 4",ck_errstr(),errno); cx_status = CSX_IOERROR; goto conret0; } else if ((tx == 1) && (!duplex)) { /* ECHO change */ duplex = 1; /* Turn on local echo */ continue; } else if ((tx == 2) && (duplex)) { /* ECHO change */ duplex = 0; continue; } else if (tx == 3) { /* Quoted IAC */ c = parity ? 127 : 255; } #ifdef IKS_OPTION else if (tx == 4) { /* IKS State Change */ if (TELOPT_SB(TELOPT_KERMIT).kermit.u_start && !tcp_incoming ) { /* here we need to print a msg that the other */ /* side is in SERVER mode and that REMOTE */ /* commands should be used. And CONNECT mode */ /* should be ended. */ cx_status = CSX_IKSD; active = 0; } } #endif /* IKS_OPTION */ else if (tx == 6) { /* DO LOGOUT was received */ if (msgflg) printf("\r\nRemote Logout "); #ifdef NOSETBUF fflush(stdout); #endif /* NOSETBUF */ debug(F100,"CONNECT Remote Logout","",0); cx_status = CSX_TRIGGER; goto conret0; } else continue; /* Negotiation OK, get next char. */ } else if (parity) c &= 0x7f; /* I'm echoing for the remote */ if (TELOPT_ME(TELOPT_ECHO) && tn_rem_echo) ttoc((char)c); #endif /* TNCODE */ #ifdef CKLEARN /* Learned script: Record incoming chars if not in Keyboard state */ if (learning && learnst != 2) { /* Learned script active */ learnbuf[learnbp++] = c; /* Save for INPUT command */ if (learnbp >= LEARNBUFSIZ) /* in circular buffer */ learnbp = 0; /* wrapping if at end. */ learnbc++; /* Count this byte. */ learnst = 1; /* State is Net. */ } #endif /* CKLEARN */ if (debses) { /* Output character to screen */ char *s; /* Debugging display... */ s = dbchr(c); /* Make char into string */ while (*s) { /* Output each char from string */ ckcputc(*s); if (seslog) /* And maybe log it. */ LOGCHAR((char)*s); s++; } } else { /* Regular display ... */ c &= cmask; /* Apply Kermit-to-remote mask */ if (seslog && sessft) /* If binary session log */ LOGCHAR((char)c); /* log the character now. */ #ifndef NOXFER #ifdef CK_AUTODL /* Autodownload. Check for Kermit S packet prior to translation, since that can change the packet and make it unrecognizable (as when the terminal character set is an ISO 646 one)... Ditto for Zmodem start packet. */ if (autodl /* Autodownload enabled? */ #ifdef IKS_OPTION || TELOPT_SB(TELOPT_KERMIT).kermit.me_start #endif /* IKS_OPTION */ ) { int k = 0; if (kstartactive || c == stchr /* Kermit S or I packet? */ #ifdef COMMENT || adl_kmode == ADLSTR /* Not used in C-Kermit */ #endif /* COMMENT */ ) k = kstart((CHAR)c); #ifdef CK_XYZ if (!k && zmdlok) /* Or an "sz" start? */ k = zstart((CHAR)c); #endif /* CK_XYZ */ if (k) { int ksign = 0; debug(F101,"CONNECT autodownload k","",k); if (k < 0) { /* Minus-Protocol? */ #ifdef NOSERVER goto noserver; /* Need server mode for this */ #else ksign = 1; /* Remember */ k = 0 - k; /* Convert to actual protocol */ justone = 1; /* Flag for protocol module */ #endif /* NOSERVER */ } else justone = 0; k--; /* Adjust [kz]start's return value */ if (k == PROTO_K #ifdef CK_XYZ || k == PROTO_Z #endif /* CK_XYZ */ ) { /* Damage the packet so that it doesn't trigger */ /* autodownload detection downstream. */ if (k == PROTO_K) { int i, len = strlen((char *)ksbuf); for (i = 0; i < len; i++) ckcputc(BS); } #ifdef CK_XYZ else { int i; for (i = 0; i < 3; i++) ckcputc(CAN); } #endif /* CK_XYZ */ #ifndef NOICP /* sprintf is safe here (builtin keywords) */ sprintf(apcbuf, "set proto %s, %s, set proto %s", ptab[k].p_name, ksign ? "server" : "receive", ptab[protocol].p_name ); apclength = strlen(apcbuf); debug(F111,"CONNECT ksbuf",ksbuf,k); debug(F110,"CONNECT autodownload",apcbuf,0); apcactive = APC_LOCAL; ckcputf(); /* Force screen update */ cx_status = CSX_APC; goto conret1; #else /* Here's another way that doesn't require APC, but then we'll have to change all the other CONNECT modules, and then the mainline code that calls them. */ { extern char sstate; sstate = ksign ? 'x' : 'v'; proto(); } #endif /* NOICP */ } } } #ifdef NOSERVER noserver: #endif /* NOSERVER */ #endif /* CK_AUTODL */ #endif /* NOXFER */ if (sosi) { /* Handle SI/SO */ if (c == SO) { /* Shift Out */ inshift = 1; continue; } else if (c == SI) { /* Shift In */ inshift = 0; continue; } if (inshift) c |= 0200; } inxbuf[0] = c; /* In case there is no translation */ inxcount = 1; /* ... */ #ifndef NOCSETS if (inesc[0] == ES_NORMAL /* If not in an escape sequence */ && !printing /* and not in transparent print */ ) { /* Translate character sets */ #ifdef UNICODE int x; if (unicode == 1) { /* Remote is UTF-8 */ x = u_to_b((CHAR)c); if (x == -1) continue; else if (x == -2) { /* LS or PS */ inxbuf[0] = CR; inxbuf[1] = LF; inxcount = 2; } else if (x == -9) { /* UTF-8 error */ inxbuf[0] = '?'; inxbuf[1] = u_to_b2(); inxcount = 2; } else { inxbuf[0] = (unsigned)(x & 0xff); } c = inxbuf[0]; } else if (unicode == 2) { /* Local is UTF-8 */ inxcount = b_to_u((CHAR)c,inxbuf,OUTXBUFSIZ,tcssize); c = inxbuf[0]; } else { #endif /* UNICODE */ if (sxi) c = (*sxi)((CHAR)c); if (rxi) c = (*rxi)((CHAR)c); inxbuf[0] = c; #ifdef UNICODE } #endif /* UNICODE */ } #endif /* NOCSETS */ #ifndef NOESCSEQ if (escseq) { /* If handling escape sequences */ oldprt = printing; /* remember printer state */ apcrc = chkaes((char)c,0); /* and update escseq state. */ if (printing && !oldprt) /* If printer was turned on */ continue; /* don't print final char of escseq */ } #ifdef CK_APC /* If we are handling APCs, we have several possibilities at this point: 1. Ordinary character to be written to the screen. 2. An Esc; we can't write it because it might be the beginning of an APC. 3. The character following an Esc, in which case we write Esc, then char, but only if we have not just entered an APC sequence. */ if (escseq && (apcstatus & APC_ON)) { if (inesc[0] == ES_GOTESC) /* Don't write ESC yet */ continue; else if (oldesc[0] == ES_GOTESC && !apcactive) { ckcputc(ESC); /* Write saved ESC */ if (seslog && !sessft) LOGCHAR((char)ESC); } else if (apcrc) { /* We have an APC */ debug(F111,"CONNECT APC complete",apcbuf,apclength); ckcputf(); /* Force screen update */ cx_status = CSX_APC; goto conret1; } } #endif /* CK_APC */ #endif /* NOESCSEQ */ debug(F111,"INXBUF",inxbuf,inxcount); for (i = 0; i < inxcount; i++) { /* Loop thru */ c = inxbuf[i]; /* input expansion buffer... */ if ( #ifdef CK_APC !apcactive && /* Don't display APC sequences */ #endif /* CK_APC */ !printing /* or transparent print material */ ) { c &= cmdmsk; /* Apply command mask. */ /* Handle bare carriage returns and linefeeds */ if (c == CR && tt_crd) { /* SET TERM CR-DISPLA CRLF? */ ckcputc(c); /* Yes, output CR */ if (seslog && !sessft) LOGCHAR((char)c); c = LF; /* and insert a linefeed */ } if (c == LF && tt_lfd) { /* SET TERM CR-DISPLA CRLF? */ ckcputc(CR); /* Yes, output CR */ if (seslog && !sessft) LOGCHAR((char)CR); } #ifndef NOESCSEQ if (dontprint) { /* Do transparent printing. */ dontprint = 0; continue; } else #endif /* NOESCSEQ */ ckcputc(c); /* Write character to screen */ } if (seslog && !sessft) { /* Handle session log. */ LOGCHAR((char)c); } #ifdef XPRINT if (printing && !inesc[0]) { /* zchout() can't be used because */ /* it's buffered differently. */ cbuf[0] = c; zsoutx(ZMFILE,(char *)cbuf,1); } #endif /* XPRINT */ #ifdef CK_TRIGGER /* Check for trigger string */ if (tt_trigger[0]) { int i; if ((i = autoexitchk((CHAR)c)) > -1) { makestr(&triggerval,tt_trigger[i]); ckcputf(); /* Force screen update */ #ifdef NOSETBUF fflush(stdout); /* I mean really force it */ #endif /* NOSETBUF */ cx_status = CSX_TRIGGER; goto conret1; } } #endif /* CK_TRIGGER */ } } } #ifndef BEBOX if (FD_ISSET(scrnout, &out)) { FD_CLR(scrnout, &out); } #endif /* BEBOX */ } /* End of big loop */ conret1: /* Come here to succeed */ rc = 1; conret0: /* Common exit point */ #ifdef BEBOX { long ret_val; closesocket(pair[0]); closesocket(pair[1]); x = kill(tid,SIGKILLTHR); /* Kill thread */ wait_for_thread (tid, &ret_val); } #endif /* BEBOX */ #ifdef CKLEARN if (learning && learnfp) fputs("\n",learnfp); #endif /* CKLEARN */ conres(); if (dohangup > 0) { #ifdef NETCONN if (network #ifdef TNCODE && !TELOPT_ME(TELOPT_COMPORT) #endif /* TNCODE */ ) ttclos(0); #endif /* NETCONN */ #ifndef COMMENT /* This is bad because if they said SET MODEM HANGUP-METHOD MODEM-COMMAND, they mean it -- we shouldn't fall back on tthang() if mdmhup() fails, because maybe they have some special kind of connection. On the other hand, making this change prevents dialing from working at all in some cases. Further study needed. */ #ifndef NODIAL if (dohangup > 1) /* User asked for it */ if (mdmhup() < 1) /* Maybe hang up via modem */ #endif /* NODIAL */ tthang(); /* And make sure we don't hang up */ #else if (!network) { /* Serial connection. */ #ifndef NODIAL if (dialmhu) /* Hang up the way they said to. */ mdmhup(); else #endif /* NODIAL */ tthang(); } #endif /* COMMENT */ dologend(); dohangup = 0; /* again unless requested again. */ } if (quitnow) /* Exit now if requested. */ doexit(GOOD_EXIT,xitsta); if (msgflg #ifdef CK_APC && !apcactive #endif /* CK_APC */ ) printf("(Back at %s)", *myhost ? myhost : "local UNIX system"); #ifdef CK_APC if (!apcactive) #endif /* CK_APC */ printf("\n"); what = W_NOTHING; /* So console modes set right. */ #ifndef NOCSETS language = langsv; /* Restore language */ #endif /* NOCSETS */ #ifdef CK_APC debug(F101,"CONNECT exit apcactive","",apcactive); debug(F101,"CONNECT exit justone","",justone); #endif /* CK_APC */ if (msgflg) { #ifdef CK_APC if (apcactive == APC_LOCAL) printf("\n"); #endif /* CK_APC */ printf("----------------------------------------------------\n"); printbar = 1; } else printbar = 0; fflush(stdout); return(rc); } /* H C O N N E -- Give help message for connect. */ #define CXM_SER 1 /* Serial connections only */ #define CXM_NET 2 /* Network only (but not Telnet) */ #define CXM_TEL 4 /* Telnet only */ static struct hmsgtab { char * hmsg; int hflags; } hlpmsg[] = { {" ? or H for this message", 0}, {" 0 (zero) to send the NUL (0) character", 0}, {" B to send a BREAK signal (0.275sec)", CXM_SER}, #ifdef NETCONN {" B to send a network BREAK", CXM_NET}, {" B to send a Telnet BREAK", CXM_TEL}, #endif /* NETCONN */ #ifdef CK_LBRK {" L to send a Long BREAK (1.5sec)", CXM_SER}, #endif /* CK_LBRK */ #ifdef NETCONN {" I to send a network interrupt packet", CXM_NET}, {" I to send a Telnet Interrupt request", CXM_TEL}, #ifdef TNCODE {" A to send Telnet Are-You-There?", CXM_TEL}, #endif /* TNCODE */ #endif /* NETCONN */ {" U to hangup and close the connection", 0}, {" Q to hangup and quit Kermit", 0}, {" S for status", 0}, #ifdef NOPUSH {" ! to push to local shell (disabled)", 0}, {" Z to suspend (disabled)", 0}, #else {" ! to push to local shell", 0}, #ifdef NOJC {" Z to suspend (disabled)", 0}, #else {" Z to suspend", 0}, #endif /* NOJC */ #endif /* NOPUSH */ {" \\ backslash code:", 0}, {" \\nnn decimal character code", 0}, {" \\Onnn octal character code", 0}, {" \\Xhh hexadecimal character code;", 0}, {" terminate with Carriage Return.", 0}, {" Type the escape character again to send the escape character itself,", 0}, {" or press the space-bar to resume the CONNECT session.", 0}, {NULL, 0} }; int hconne() { int c, i, cxtype; if (network) cxtype = IS_TELNET() ? CXM_TEL : CXM_NET; else cxtype = CXM_SER; conol("\r\n----------------------------------------------------\r\n"); conoll("Press:"); conol(" C to return to "); conoll(*myhost ? myhost : "the C-Kermit prompt"); for (i = 0; hlpmsg[i].hmsg; i++) { if (!(hlpmsg[i].hflags) || (hlpmsg[i].hflags == cxtype)) conoll(hlpmsg[i].hmsg); } conol("Press a key>"); /* Prompt for command. */ c = CONGKS() & 0177; /* Get character, strip any parity. */ /* No key mapping or translation here */ if (c != CMDQ) conoll(""); conoll("----------------------------------------------------"); return(c); /* Return it. */ } /* D O E S C -- Process an escape character argument */ VOID #ifdef CK_ANSIC doesc(char c) #else doesc(c) char c; #endif /* CK_ANSIC */ /* doesc */ { CHAR d; debug(F101,"CONNECT doesc","",c); while (1) { if (c == escape) { /* Send escape character */ d = dopar((CHAR) c); ttoc((char) d); return; } else /* Or else look it up below. */ if (isupper(c)) c = tolower(c); switch(c) { case 'c': /* Escape back to prompt */ case '\03': cx_status = CSX_ESCAPE; #ifdef NOICP conoll(""); conoll(""); conoll( " WARNING: This version of C-Kermit has no command processor to escape" ); conoll( " back to. To return to your local system, log out from the remote and/or" ); conoll( " use the escape character followed by the letter U to close (hang Up) the" ); conoll( " connection. Resuming your session..." ); conoll(""); return; #else active = 0; conol("\r\n"); return; #endif /* NOICP */ case 'b': /* Send a BREAK signal */ case '\02': #ifdef CKLEARN learnchar(-7); #endif /* CKLEARN */ ttsndb(); return; #ifdef NETCONN case 'i': /* Send Interrupt */ case '\011': #ifdef TCPSOCKET if (network && IS_TELNET()) { /* TELNET */ temp[0] = (CHAR) IAC; /* I Am a Command */ temp[1] = (CHAR) TN_IP; /* Interrupt Process */ temp[2] = NUL; ttol((CHAR *)temp,2); } else #endif /* TCPSOCKET */ conoc(BEL); return; #ifdef TCPSOCKET case 'a': /* "Are You There?" */ case '\01': if (network && IS_TELNET()) { temp[0] = (CHAR) IAC; /* I Am a Command */ temp[1] = (CHAR) TN_AYT; /* Are You There? */ temp[2] = NUL; ttol((CHAR *)temp,2); } else conoc(BEL); return; #endif /* TCPSOCKET */ #endif /* NETCONN */ #ifdef CK_LBRK case 'l': /* Send a Long BREAK signal */ #ifdef CKLEARN learnchar(-8); #endif /* CKLEARN */ ttsndlb(); return; #endif /* CK_LBRK */ case 'u': /* Hangup */ /* case '\010': */ /* No, too dangerous */ cx_status = CSX_USERDISC; dohangup = 2; active = 0; conol("\r\nHanging up "); return; case 'q': /* Quit */ cx_status = CSX_USERDISC; dohangup = 2; quitnow = 1; active = 0; conol("\r\n"); return; case 's': /* Status */ conoll(""); conoll("----------------------------------------------------"); #ifdef PTYORPIPE if (ttpipe) ckmakmsg(temp,TMPLEN," Pipe: \"",ttname,"\"",NULL); else if (ttpty) ckmakmsg(temp,TMPLEN," Pty: \"",ttname,"\"",NULL); else #endif /* PTYORPIPE */ ckmakmsg(temp, TMPLEN, " ", (network ? "Host" : "Device"), ": ", ttname ); conoll(temp); /* The following sprintf's are safe, temp[] size is at least 200 */ if (!network && speed >= 0L) { sprintf(temp,"Speed %ld", speed); conoll(temp); } sprintf(temp," Terminal echo: %s", duplex ? "local" : "remote"); conoll(temp); sprintf(temp," Terminal bytesize: %d", (cmask == 0177) ? 7 : 8); conoll(temp); sprintf(temp," Command bytesize: %d", (cmdmsk == 0177) ? 7 : 8); conoll(temp); if (hwparity) sprintf(temp," Parity[hardware]: %s",parnam(hwparity)); else sprintf(temp," Parity: %s", parnam(parity)); conoll(temp); #ifndef NOXFER sprintf(temp," Autodownload: %s", autodl ? "on" : "off"); conoll(temp); #endif /* NOXFER */ ckmakmsg(temp, /* (would not be safe for sprintf) */ TMPLEN, " Session log: ", *sesfil ? sesfil : "(none)", NULL, NULL ); conoll(temp); #ifndef NOSHOW if (!network) shomdm(); #endif /* NOSHOW */ #ifdef CKLOGDIAL { long z; z = dologshow(0); if (z > -1L) { sprintf(temp," Elapsed time: %s",hhmmss(z)); conoll(temp); } } #endif /* CKLOGDIAL */ conoll("----------------------------------------------------"); return; case 'h': /* Help */ case '?': /* Help */ c = hconne(); continue; case '0': /* Send a null */ c = '\0'; d = dopar((CHAR) c); ttoc((char) d); return; case 'z': case '\032': /* Suspend */ #ifndef NOPUSH if (!nopush) stptrap(0); else conoc(BEL); #else conoc(BEL); #endif /* NOPUSH */ return; case '@': /* Start inferior command processor */ case '!': #ifndef NOPUSH if (!nopush) { conres(); /* Put console back to normal */ zshcmd(""); /* Fork a shell. */ if (conbin((char)escape) < 0) { cx_status = CSX_INTERNAL; printf("Error resuming CONNECT session\n"); active = 0; } } else conoc(BEL); #else conoc(BEL); #endif /* NOPUSH */ return; case SP: /* Space, ignore */ return; default: /* Other */ if (c == CMDQ) { /* Backslash escape */ int x; ecbp = ecbuf; *ecbp++ = c; while (((c = (CONGKS() & cmdmsk)) != '\r') && (c != '\n')) *ecbp++ = c; *ecbp = NUL; ecbp = ecbuf; x = xxesc(&ecbp); /* Interpret it */ if (x >= 0) { /* No key mapping here */ c = dopar((CHAR) x); ttoc((char) c); return; } else { /* Invalid backslash code. */ conoc(BEL); return; } } conoc(BEL); return; /* Invalid esc arg, beep */ } } } #endif /* NOLOCAL */ ckucon.c0000644000015300001460000024274311607576114011333 0ustar fdckermit#include "ckcsym.h" char *connv = "CONNECT Command for UNIX:fork(), 9.0.117, 14 Jul 2011"; /* C K U C O N -- Terminal connection to remote system, for UNIX */ /* Author: Frank da Cruz , Columbia University Academic Information Systems, New York City. Copyright (C) 1985, 2011, Trustees of Columbia University in the City of New York. All rights reserved. See the C-Kermit COPYING.TXT file or the copyright text in the ckcmai.c module for disclaimer and permissions. NOTE: This module has been superseded on most platforms by ckucns.c, which uses select() rather than fork() for multiplexing its i/o. This module is still needed for platforms that do not support select(), and also for its X.25 support. Although the two modules share large amounts of code, their structure is radically different and therefore attempts at merging them have so far been unsuccessful. (November 1998.) Special thanks to Eduard Vopicka, Prague University of Economics, Czech Republic, for valuable contributions to this module in July 1994, and to Neal P. Murphy of the Motorola Cellular Infrastructure Group in 1996 for rearranging the code to allow operation on the BeBox, yet still work in regular UNIX. */ #include "ckcdeb.h" /* Common things first */ #ifndef NOLOCAL #ifdef BEOSORBEBOX static double time_started = 0.0; #include _PROTOTYP( static long concld, (void *) ); #else _PROTOTYP( static VOID concld, (void) ); #endif /* BEOSORBEBOX */ #ifdef NEXT #undef NSIG #include /* For wait() */ #endif /* NEXT */ #include /* Signals */ #ifndef HPUXPRE65 #include /* Error number symbols */ #else #ifndef ERRNO_INCLUDED #include /* Error number symbols */ #endif /* ERRNO_INCLUDED */ #endif /* HPUXPRE65 */ #ifdef ZILOG /* Longjumps */ #include #else #include #endif /* ZILOG */ #include "ckcsig.h" /* Kermit-specific includes */ #include "ckcasc.h" /* ASCII characters */ #include "ckcker.h" /* Kermit things */ #include "ckucmd.h" /* For xxesc() prototype */ #include "ckcnet.h" /* Network symbols */ #ifndef NOCSETS #include "ckcxla.h" /* Character set translation */ #endif /* NOCSETS */ /* Internal function prototypes */ _PROTOTYP( VOID ttflux, (void) ); _PROTOTYP( VOID doesc, (char) ); _PROTOTYP( VOID logchar, (char) ); _PROTOTYP( int hconne, (void) ); #ifndef NOSHOW _PROTOTYP( VOID shomdm, (void) ); #endif /* NOSHOW */ _PROTOTYP( static int kbget, (void) ); _PROTOTYP( static int pipemsg, (int) ); _PROTOTYP( static int ckcputf, (void) ); _PROTOTYP( static VOID ck_sndmsg, (void) ); /* For inter-fork signaling. Normally we use SIGUSR1, except on SCO, where we use SIGUSR2 because SIGUSR1 is used by the system. You can define CK_FORK_SIG to be whatever other signal you might want to use at compile time. We don't use SIGUSR2 everywhere because in some systems, like UnixWare, the default action for SIGUSR2 is to kill the process that gets it. */ #ifndef CK_FORK_SIG #ifndef SIGUSR1 /* User-defined signals */ #define SIGUSR1 30 #endif /* SIGUSR1 */ #ifndef SIGUSR2 #define SIGUSR2 31 #endif /* SIGUSR2 */ #ifdef M_UNIX #define CK_FORK_SIG SIGUSR2 /* SCO - use SIGUSR2 */ #else #define CK_FORK_SIG SIGUSR1 /* Others - use SIGUSR1 */ #endif /* M_UNIX */ #endif /* CK_FORK_SIG */ /* External variables */ extern struct ck_p ptab[]; extern int local, escape, duplex, parity, flow, seslog, sessft, debses, mdmtyp, ttnproto, cmask, cmdmsk, network, nettype, deblog, sosi, tnlm, xitsta, what, ttyfd, ttpipe, quiet, backgrd, pflag, tt_crd, tt_lfd, tn_nlm, ttfdflg, tt_escape, justone, carrier, hwparity; extern long speed; extern char ttname[], sesfil[], myhost[], *ccntab[]; #ifdef TNCODE extern int tn_b_nlm, tn_rem_echo; #endif /* TNCODE */ #ifdef CK_TRIGGER extern char * tt_trigger[], * triggerval; #endif /* CK_TRIGGER */ extern int nopush; #ifdef CK_APC extern int apcactive; /* Application Program Command (APC) */ extern int apcstatus; /* items ... */ static int apclength = 0; #ifdef DCMDBUF extern char *apcbuf; #else extern char apcbuf[]; #endif /* DCMDBUF */ static int apcbuflen = APCBUFLEN - 2; extern int protocol; /* Auto download */ #endif /* CK_APC */ extern int autodl; #ifdef CK_AUTODL extern CHAR ksbuf[]; #endif /* CK_AUTODL */ #ifdef CK_XYZ #ifdef XYZ_INTERNAL static int zmdlok = 1; /* Zmodem autodownloads available */ #else static int zmdlok = 0; /* Depends on external protocol def */ #endif /* XYZ_INTERNAL */ #else static int zmdlok = 0; /* Not available at all */ #endif /* CK_XYZ */ #ifndef NOSETKEY /* Keyboard mapping */ extern KEY *keymap; /* Single-character key map */ extern MACRO *macrotab; /* Key macro pointer table */ static MACRO kmptr = NULL; /* Pointer to current key macro */ #endif /* NOSETKEY */ /* Global variables local to this module */ static int quitnow = 0, /* Q was typed */ jbset = 0, /* Flag whether jmp buf is set. */ dohangup = 0, /* H was typed */ sjval, /* Setjump return value */ goterr = 0, /* Fork/pipe creation error flag */ inshift = 0, /* SO/SI shift states */ outshift = 0; int active = 0; /* Lower fork active flag */ static PID_T parent_id = (PID_T)0; /* Process ID of keyboard fork */ static char ecbuf[10], *ecbp; /* Escape char buffer & pointer */ #ifdef CK_SMALL #define IBUFL 1536 /* Input buffer length */ #else #define IBUFL 4096 #endif /* CK_SMALL */ static int obc = 0; /* Output buffer count */ #ifndef OXOS #define OBUFL 1024 /* Output buffer length */ #else #define OBUFL IBUFL #endif /* OXOS */ #ifdef BIGBUFOK #define TMPLEN 4096 /* Temporary message buffer length */ #else #define TMPLEN 200 #endif /* BIGBUFOK */ #ifdef DYNAMIC static char *ibuf = NULL, *obuf = NULL, *temp = NULL; /* Buffers */ #else static char ibuf[IBUFL], obuf[OBUFL], temp[TMPLEN]; #endif /* DYNAMIC */ #ifdef DYNAMIC static char *ibp; /* Input buffer pointer */ #else static char *ibp = ibuf; /* Input buffer pointer */ #endif /*DYNAMIC */ static int ibc = 0; /* Input buffer count */ #ifdef DYNAMIC static char *obp; /* Output buffer pointer */ #else static char *obp = obuf; /* Output buffer pointer */ #endif /* DYNAMIC */ /* X.25 items */ #ifdef ANYX25 static char *p; /* General purpose pointer */ char x25ibuf[MAXIX25]; /* Input buffer */ char x25obuf[MAXOX25]; /* Output buffer */ int ibufl; /* Length of input buffer */ int obufl; /* Length of output buffer */ unsigned char tosend = 0; int linkid, lcn; static int dox25clr = 0; #ifndef IBMX25 extern CHAR padparms[]; #endif /* IBMX25 */ #endif /* ANYX25 */ static int xpipe[2] = {-1, -1}; /* Pipe descriptor for child-parent messages */ static PID_T pid = (PID_T) 0; /* Process ID of child */ /* Character-set items */ static int unicode = 0; static int escseq = 0; /* 1 = Recognizer is active */ int inesc = 0; /* State of sequence recognizer */ int oldesc = -1; /* Previous state of recognizer */ #define OUTXBUFSIZ 15 static CHAR inxbuf[OUTXBUFSIZ+1]; /* Host-to-screen expansion buffer */ static int inxcount = 0; /* and count */ static CHAR outxbuf[OUTXBUFSIZ+1]; /* Keyboard-to-host expansion buf */ static int outxcount = 0; /* and count */ #ifndef NOCSETS #ifdef CK_ANSIC /* ANSI C prototypes... */ extern CHAR (*xls[MAXTCSETS+1][MAXFCSETS+1])(CHAR); /* Character set */ extern CHAR (*xlr[MAXTCSETS+1][MAXFCSETS+1])(CHAR); /* translation functions */ static CHAR (*sxo)(CHAR); /* Local translation functions */ static CHAR (*rxo)(CHAR); /* for output (sending) terminal chars */ static CHAR (*sxi)(CHAR); /* and for input (receiving) terminal chars. */ static CHAR (*rxi)(CHAR); #else /* Not ANSI C... */ extern CHAR (*xls[MAXTCSETS+1][MAXFCSETS+1])(); /* Character set */ extern CHAR (*xlr[MAXTCSETS+1][MAXFCSETS+1])(); /* translation functions. */ static CHAR (*sxo)(); /* Local translation functions */ static CHAR (*rxo)(); /* for output (sending) terminal chars */ static CHAR (*sxi)(); /* and for input (receiving) terminal chars. */ static CHAR (*rxi)(); #endif /* CK_ANSIC */ extern int language; /* Current language. */ static int langsv; /* For remembering language setting. */ extern struct csinfo fcsinfo[]; /* File character set info. */ extern int tcsr, tcsl; /* Terminal character sets, remote & local. */ static int tcs; /* Intermediate ("transfer") character set. */ static int tcssize = 0; /* Size of tcs */ #ifdef UNICODE /* UTF-8 support */ #ifdef CK_ANSIC extern int (*xl_ufc[MAXFCSETS+1])(USHORT); /* Unicode to FCS */ extern USHORT (*xl_fcu[MAXFCSETS+1])(CHAR); /* FCS to Unicode */ extern int (*xuf)(USHORT); /* Translation function UCS to FCS */ extern USHORT (*xfu)(CHAR); /* Translation function FCS to UCS */ #else extern int (*xl_ufc[MAXFCSETS+1])(); extern USHORT (*xl_fcu[MAXFCSETS+1])(); extern int (*xuf)(); extern USHORT (*xfu)(); #endif /* CK_ANSIC */ #endif /* UNICODE */ #endif /* NOCSETS */ /* We do not need to parse and recognize escape sequences if we are being built without character-set support AND without APC support. */ #ifdef NOCSETS /* No character sets */ #ifndef CK_APC /* No APC */ #ifndef NOESCSEQ #define NOESCSEQ /* So no escape sequence recognizer */ #endif /* NOESCSEQ */ #endif /* CK_APC */ #endif /* NOCSETS */ /* Child process events and messages */ #define CEV_NO 0 /* No event */ #define CEV_HUP 1 /* Communications hangup */ #define CEV_PAD 2 /* X.25 - change PAD parameters */ #define CEV_DUP 3 /* Toggle duplex */ #define CEV_APC 4 /* Execute APC */ #ifdef TNCODE #define CEV_MEBIN 5 /* Change of me_binary */ #define CEV_UBIN 6 /* Change of u_binary */ #endif /* TNCODE */ #define CEV_ADL 7 /* Autodownload */ #define CEV_AUL 8 /* Autoupload */ #define CEV_TRI 9 /* Trigger string */ #ifdef NOESCSEQ #define chkaes(x) 0 #else /* As of edit 178, the CONNECT command skips past ANSI escape sequences to avoid translating the characters within them. This allows the CONNECT command to work correctly with a host that uses a 7-bit ISO 646 national character set, in which characters like '[' would normally be translated into accented characters, ruining the terminal's interpretation (and generation) of escape sequences. As of edit 190, the CONNECT command responds to APC escape sequences (ESC _ text ESC \) if the user SETs TERMINAL APC ON or UNCHECKED, and the program was built with CK_APC defined. Non-ANSI/ISO-compliant escape sequences are not handled. */ /* States for the escape-sequence recognizer. */ #define ES_NORMAL 0 /* Normal, not in an escape sequence */ #define ES_GOTESC 1 /* Current character is ESC */ #define ES_ESCSEQ 2 /* Inside an escape sequence */ #define ES_GOTCSI 3 /* Inside a control sequence */ #define ES_STRING 4 /* Inside DCS,OSC,PM, or APC string */ #define ES_TERMIN 5 /* 1st char of string terminator */ /* ANSI escape sequence handling. Only the 7-bit form is treated, because translation is not a problem in the 8-bit environment, in which all GL characters are ASCII and no translation takes place. So we don't check for the 8-bit single-character versions of CSI, DCS, OSC, APC, or ST. Here is the ANSI sequence recognizer state table, followed by the code that implements it. Definitions: CAN = Cancel 01/08 Ctrl-X SUB = Substitute 01/10 Ctrl-Z DCS = Device Control Sequence 01/11 05/00 ESC P CSI = Control Sequence Introducer 01/11 05/11 ESC [ ST = String Terminator 01/11 05/12 ESC \ OSC = Operating System Command 01/11 05/13 ESC ] PM = Privacy Message 01/11 05/14 ESC ^ APC = Application Program Command 01/11 05/15 ESC _ ANSI escape sequence recognizer: State Input New State ; Commentary NORMAL (start) ; Start in NORMAL state (any) CAN NORMAL ; ^X cancels (any) SUB NORMAL ; ^Z cancels NORMAL ESC GOTESC ; Begin escape sequence NORMAL other ; NORMAL control or graphic character GOTESC ESC ; Start again GOTESC [ GOTCSI ; CSI GOTESC P STRING ; DCS introducer, consume through ST GOTESC ] STRING ; OSC introducer, consume through ST GOTESC ^ STRING ; PM introducer, consume through ST GOTESC _ STRING ; APC introducer, consume through ST GOTESC 0..~ NORMAL ; 03/00 through 17/14 = Final character GOTESC other ESCSEQ ; Intermediate or ignored control character ESCSEQ ESC GOTESC ; Start again ESCSEQ 0..~ NORMAL ; 03/00 through 17/14 = Final character ESCSEQ other ; Intermediate or ignored control character GOTCSI ESC GOTESC ; Start again GOTCSI @..~ NORMAL ; 04/00 through 17/14 = Final character GOTCSI other ; Intermediate char or ignored control char STRING ESC TERMIN ; Maybe have ST STRING other ; Consume all else TERMIN \ NORMAL ; End of string TERMIN other STRING ; Still in string */ /* chkaes() -- Check ANSI Escape Sequence. Call with EACH character in input stream. Sets global inesc variable according to escape sequence state. Returns 0 normally, 1 if an APC sequence is to be executed. */ int #ifdef CK_ANSIC chkaes(char c) #else chkaes(c) char c; #endif /* CK_ANSIC */ /* chkaes */ { oldesc = inesc; /* Remember previous state */ if (c == CAN || c == SUB) /* CAN and SUB cancel any sequence */ inesc = ES_NORMAL; else /* Otherwise */ switch (inesc) { /* enter state switcher */ case ES_NORMAL: /* NORMAL state */ if (c == ESC) /* Got an ESC */ inesc = ES_GOTESC; /* Change state to GOTESC */ break; /* Otherwise stay in NORMAL state */ case ES_GOTESC: /* GOTESC state */ if (c == '[') /* Left bracket after ESC is CSI */ inesc = ES_GOTCSI; /* Change to GOTCSI state */ else if (c == 'P' || (c > 0134 && c < 0140)) { /* P, [, ^, or _ */ inesc = ES_STRING; /* Switch to STRING-absorption state */ #ifdef CK_APC if (c == '_' && pid == 0 && /* APC handled in child only */ (apcstatus & APC_ON)) { /* and only if not disabled. */ debug(F100,"CONNECT APC begin","",0); apcactive = APC_REMOTE; /* Set APC-Active flag */ apclength = 0; /* and reset APC buffer pointer */ } #endif /* CK_APC */ } else if (c > 057 && c < 0177) /* Final character '0' thru '~' */ inesc = ES_NORMAL; /* Back to normal */ else if (c != ESC) /* ESC in an escape sequence... */ inesc = ES_ESCSEQ; /* starts a new escape sequence */ break; /* Intermediate or ignored ctrl char */ case ES_ESCSEQ: /* ESCSEQ -- in an escape sequence */ if (c > 057 && c < 0177) /* Final character '0' thru '~' */ inesc = ES_NORMAL; /* Return to NORMAL state. */ else if (c == ESC) /* ESC ... */ inesc = ES_GOTESC; /* starts a new escape sequence */ break; /* Intermediate or ignored ctrl char */ case ES_GOTCSI: /* GOTCSI -- In a control sequence */ if (c > 077 && c < 0177) /* Final character '@' thru '~' */ inesc = ES_NORMAL; /* Return to NORMAL. */ else if (c == ESC) /* ESC ... */ inesc = ES_GOTESC; /* starts over. */ break; /* Intermediate or ignored ctrl char */ case ES_STRING: /* Inside a string */ if (c == ESC) /* ESC may be 1st char of terminator */ inesc = ES_TERMIN; /* Go see. */ #ifdef CK_APC else if (apcactive && (apclength < apcbuflen)) /* If in APC, */ apcbuf[apclength++] = c; /* deposit this character. */ else { /* Buffer overrun */ apcactive = 0; /* Discard what we got */ apclength = 0; /* and go back to normal */ apcbuf[0] = 0; /* Not pretty, but what else */ inesc = ES_NORMAL; /* can we do? (ST might not come) */ } #endif /* CK_APC */ break; /* Absorb all other characters. */ case ES_TERMIN: /* May have a string terminator */ if (c == '\\') { /* which must be backslash */ inesc = ES_NORMAL; /* If so, back to NORMAL */ #ifdef CK_APC if (apcactive) { /* If it was an APC string, */ debug(F101,"CONNECT APC terminated","",c); apcbuf[apclength] = NUL; /* terminate it and then ... */ return(1); } #endif /* CK_APC */ } else { /* Otherwise */ inesc = ES_STRING; /* Back to string absorption. */ #ifdef CK_APC if (apcactive && (apclength+1 < apcbuflen)) { /* In APC string */ apcbuf[apclength++] = ESC; /* deposit the Esc character */ apcbuf[apclength++] = c; /* and this character too */ } #endif /* CK_APC */ } } return(0); } #endif /* NOESCSEQ */ /* Connect state parent/child communication signal handlers */ /* Routines used by the child process */ static int pipemsg(n) int n; { /* Send message ID to parent */ int code = n & 255; return(write(xpipe[1], &code, sizeof(code))); } /* Environment pointer for CK_FORK_SIG signal handling in child... */ #ifdef CK_POSIX_SIG static sigjmp_buf sig_env; #else static jmp_buf sig_env; #endif /* CK_POSIX_SIG */ static SIGTYP /* CK_FORK_SIG handling in child ... */ forkint(foo) int foo; { /* It is important to disable CK_FORK_SIG before longjmp */ signal(CK_FORK_SIG, SIG_IGN); /* Set to ignore CK_FORK_SIG */ debug(F100,"CONNECT forkint - CK_FORK_SIG", "", 0); /* Force return from ck_sndmsg() */ cklongjmp(sig_env, 1); /* NOTREACHED */ } static VOID ck_sndmsg() { /* Executed by child only ... */ debug(F100,"CONNECT ck_sndmsg, active", "", active); if ( #ifdef CK_POSIX_SIG sigsetjmp(sig_env,1) #else setjmp(sig_env) #endif /* CK_POSIX_SIG */ == 0) { debug(F100,"CONNECT ck_sndmsg signaling parent","",0); signal(CK_FORK_SIG, forkint); /* Set up signal handler */ kill(parent_id, CK_FORK_SIG); /* Kick the parent */ debug(F100,"ck_sndmsg pausing","",0); for (;;) pause(); /* Wait for CK_FORK_SIG or SIGKILL */ /* NOTREACHED */ } /* We come here from forkint() via [sig]cklongjmp(sig_env,1) */ debug(F100,"CONNECT ck_sndmsg is parent - returning", "", 0); } /* Routines used by the parent process */ #ifdef CK_POSIX_SIG /* Environment pointer for CONNECT errors */ static sigjmp_buf con_env; #else static jmp_buf con_env; #endif /* CK_POSIX_SIG */ /* pipeint() handles CK_FORK_SIG signals from the lower (port) fork. It reads a function code from the pipe that connects the two forks, then reads additional data from the pipe, then handles it. */ static SIGTYP pipeint(arg) int arg; { /* Dummy argument */ int code, cx, x, i /* , n */ ; #ifndef NOCCTRAP extern ckjmpbuf cmjbuf; #endif /* NOCCTRAP */ /* IMPORTANT: At this point, the child fork is waiting for CK_FORK_SIG (eventually for SIGKILL) inside of ck_sndmsg(). So we can't get any subsequent CK_FORK_SIG from child before we send it CK_FORK_SIG. */ signal(CK_FORK_SIG, SIG_IGN); /* Ignore CK_FORK_SIG now */ debug(F101,"CONNECT pipeint arg","",arg); read(xpipe[0], &code, sizeof(code)); /* Get function code from pipe */ debug(F101,"CONNECT pipeint code","",code); cx = code & 255; /* 8-bit version of function code */ #ifndef NOCCTRAP #ifndef NOICP #define USECCJMPBUF #endif /* NOICP */ #endif /* NOCCTRAP */ /* Read info passed back up to us by the lower fork, depending on the function requested. The same number of items must be read from the pipe in the same order as the lower fork put them there. Trying to read something that's not there makes the program hang uninterruptibly. Pay close attention -- notice how we fall through some of the cases rather than break; that's deliberate. */ switch (cx) { #ifdef CK_TRIGGER case CEV_TRI: /* Trigger string */ debug(F100,"CONNECT trigger","",0); read(xpipe[0], (char *)&i, sizeof(i)); /* Trigger index */ debug(F101,"CONNECT trigger index","",i); makestr(&triggerval,tt_trigger[i]); /* Make a copy of the trigger */ debug(F110,"CONNECT triggerval",triggerval,0); read(xpipe[0], (char *)&ibc, sizeof(ibc)); /* Copy child's */ debug(F101,"CONNECT trigger ibc (upper)","",ibc); /* input buffer. */ if (ibc > 0) { read(xpipe[0], (char *)&ibp, sizeof(ibp)); read(xpipe[0], ibp, ibc); } /* Fall thru... */ #endif /* CK_TRIGGER */ case CEV_HUP: /* The CEV_HUP case is executed when the other side has hung up on us. In some cases, this happens before we have had a chance to execute the setjmp(con_env,1) call, and in that case we'd better not take the longjmp! A good example is when you TELNET to port 13 on the local host; it prints its asctime() string (26 chars) and then closes the connection. */ #ifdef CK_TRIGGER if (cx == CEV_TRI) sjval = CEV_TRI; /* Set global variable. */ else #endif /* CK_TRIGGER */ sjval = CEV_HUP; if (jbset) { /* jmp_buf is initialized */ cklongjmp(con_env,sjval); /* so do the right thing. */ } else { int x = 0; #ifdef USECCJMPBUF /* jmp_buf not init'd yet a close approximation... */ #ifdef CK_TRIGGER if (cx == CEV_HUP) #endif /* CK_TRIGGER */ ttclos(0); /* Close our end of the connection */ if (pid) { debug(F101,"CONNECT trigger killing pid","",pid); #ifdef BEOSORBEBOX { long ret_val; x = kill(pid,SIGKILLTHR); /* Kill lower fork */ wait_for_thread (pid, &ret_val); } #else #ifdef Plan9 x = kill(pid, SIGKILL); /* (should always use this really) */ #else x = kill(pid,9); /* Kill lower fork (history) */ #endif /* Plan9 */ wait((WAIT_T *)0); /* Wait till gone. */ if (x < 0) { printf("ERROR: Failure to kill pid %ld: %s, errno=%d\n", (long) pid, ck_errstr(), errno); debug(F111,"CONNECT error killing stale pid", ck_errstr(),errno); } pid = (PID_T) 0; #endif /* BEOSORBEBOX */ } conres(); /* Reset the console. */ if (!quiet) { printf("\r\n(Back at %s)\r\n", *myhost ? myhost : #ifdef UNIX "local UNIX system" #else "local system" #endif /* UNIX */ ); } what = W_NOTHING; /* So console modes are set right. */ printf("\r\n"); /* prevent prompt-stomping */ cklongjmp(cmjbuf,0); /* Do what the Ctrl-C handler does */ #else printf("\r\nLongjump failure - fatal\r\n"); doexit(GOOD_EXIT,-1); /* Better than dumping core... */ #endif /* USECCJMPBUF */ } #ifdef USECCJMPBUF #undef USECCJMPBUF #endif /* USECCJMPBUF */ case CEV_DUP: /* Child sends duplex change */ read(xpipe[0], (char *)&duplex, sizeof(duplex)); debug(F101,"CONNECT pipeint duplex","",duplex); break; #ifdef TNCODE case CEV_MEBIN: /* Child sends me_binary change */ read(xpipe[0], (char *)&TELOPT_ME(TELOPT_BINARY), sizeof(TELOPT_ME(TELOPT_BINARY)) ); debug(F101,"CONNECT pipeint me_binary","",TELOPT_ME(TELOPT_BINARY)); break; case CEV_UBIN: /* Child sends u_binary change */ read(xpipe[0], (char *)&TELOPT_U(TELOPT_BINARY), sizeof(TELOPT_U(TELOPT_BINARY)) ); debug(F101,"CONNECT pipeint u_binary","",TELOPT_U(TELOPT_BINARY)); break; #endif /* TNCODE */ #ifdef CK_APC case CEV_AUL: /* Autoupload */ justone = 1; debug(F100,"CONNECT autoupload at parent","",0); #ifdef CK_AUTODL case CEV_ADL: /* Autodownload */ apcactive = APC_LOCAL; if (!justone) debug(F100,"CONNECT autodownload at parent","",0); /* Copy child's Kermit packet if any */ read(xpipe[0], (char *)&x, sizeof(x)); debug(F101,"CONNECT trigger ibc (upper)","",ibc); if (x > 0) read(xpipe[0], (char *)ksbuf, x+1); #endif /* CK_AUTODL */ case CEV_APC: /* Application Program Command */ read(xpipe[0], (char *)&apclength, sizeof(apclength)); read(xpipe[0], apcbuf, apclength+1); /* Include trailing zero byte */ debug(F111,"CONNECT APC at parent",apcbuf,apclength); read(xpipe[0], (char *)&ibc, sizeof(ibc)); /* Copy child's */ if (ibc > 0) { /* input buffer. */ read(xpipe[0], (char *)&ibp, sizeof(ibp)); read(xpipe[0], ibp, ibc); } obc = 0; obp = obuf; *obuf = NUL; /* Because port fork flushed */ sjval = CEV_APC; cklongjmp(con_env,sjval); /* NOTREACHED */ #endif /* CK_APC */ #ifdef SUNX25 case CEV_PAD: /* X.25 PAD parameter change */ debug(F100,"CONNECT pipeint PAD change","",0); read(xpipe[0],padparms,MAXPADPARMS); sjval = CEV_PAD; /* Set global variable. */ #ifdef COMMENT /* We might not need to do this... */ cklongjmp(con_env,sjval); /* NOTREACHED */ #else /* COMMENT */ break; #endif /* COMMENT */ #endif /* SUNX25 */ } signal(CK_FORK_SIG, pipeint); /* Set up signal handler */ kill(pid, CK_FORK_SIG); /* Signal the port fork ... */ } /* C K C P U T C -- C-Kermit CONNECT Put Character to Screen */ /* Output is buffered to avoid slow screen writes on fast connections. NOTE: These could (easily?) become macros ... */ static int ckcputf() { /* Dump the output buffer */ int x = 0; if (obc > 0) /* If we have any characters, */ x = conxo(obc,obuf); /* dump them, */ obp = obuf; /* reset the pointer */ obc = 0; /* and the counter. */ return(x); /* Return conxo's return code */ } int ckcputc(c) int c; { int x; *obp++ = c & 0xff; /* Deposit the character */ obc++; /* Count it */ if (ibc == 0 || /* If input buffer about empty */ obc == OBUFL) { /* or output buffer full */ debug(F101,"CONNECT CKCPUTC obc","",obc); x = conxo(obc,obuf); /* dump the buffer, */ obp = obuf; /* reset the pointer */ obc = 0; /* and the counter. */ return(x); /* Return conxo's return code */ } else return(0); } /* C K C G E T C -- C-Kermit CONNECT Get Character */ /* Buffered read from communication device. Returns the next character, refilling the buffer if necessary. On error, returns ttinc's return code (see ttinc() description). Dummy argument for compatible calling conventions with ttinc(). NOTE: We don't have a macro for this because we have to pass a pointer to this function as an argument to tn_doop(). */ int ckcgetc(dummy) int dummy; { int c, n; #ifdef CK_SSL extern int ssl_active_flag, tls_active_flag; #endif /* CK_SSL */ #ifdef CK_ENCRYPTION /* No buffering for possibly encrypted connections */ if (network && IS_TELNET() && TELOPT_ME(TELOPT_AUTHENTICATION)) return(ttinc(0)); #endif /* CK_ENCRYPTION */ #ifdef CK_SSL if (ssl_active_flag || tls_active_flag) return(ttinc(0)); #endif /* CK_SSL */ #ifdef COMMENT /* too much */ debug(F101,"CONNECT CKCGETC 1 ibc","",ibc); /* Log */ #endif /* COMMENT */ if (ibc < 1) { /* Need to refill buffer? */ ibc = 0; /* Yes, reset count */ ibp = ibuf; /* and buffer pointer */ /* debug(F100,"CONNECT CKCGETC 1 calling ttinc(0)","",0); */ #ifdef COMMENT /* This check is not worth the overhead. Scenario: ttchk() returns 0, so we fall through to the blocking ttinc(). While in ttinc(), the connection is lost. But the read() that ttinc() calls does not notice, and never returns. This happens at least in HP-UX, and can be seen when we turn off the modem. */ if (!network && (carrier != CAR_OFF)) if ((n = ttchk()) < 0) /* Make sure connection is not lost */ return(n); #endif /* COMMENT */ c = ttinc(0); /* Read one character, blocking */ /* debug(F101,"CONNECT CKCGETC 1 ttinc(0)","",c); */ if (c < 0) { /* If error, return error code */ return(c); } else { /* Otherwise, got one character */ *ibp++ = c; /* Advance buffer pointer */ ibc++; /* and count. */ } if ((n = ttchk()) > 0) { /* Any more waiting? */ if (n > (IBUFL - ibc)) /* Get them all at once. */ n = IBUFL - ibc; /* Don't overflow buffer */ if ((n = ttxin(n,(CHAR *)ibp)) > 0) { #ifdef CK_ENCRYPTION if (TELOPT_U(TELOPT_ENCRYPTION)) ck_tn_decrypt(ibp,n); #endif /* CK_ENCRYPTION */ ibc += n; /* Advance counter */ } } else if (n < 0) { /* Error? */ return(n); /* Return the error code */ } debug(F101,"CONNECT CKCGETC 2 ibc","",ibc); /* Log how many */ ibp = ibuf; /* Point to beginning of buffer */ } c = *ibp++ & 0xff; /* Get next character from buffer */ ibc--; /* Reduce buffer count */ return(c); /* Return the character */ } /* Keyboard handling, buffered for speed, which is needed when C-Kermit is in CONNECT mode between two other computers that are transferring data. */ static char *kbp; /* Keyboard input buffer pointer */ static int kbc; /* Keyboard input buffer count */ #ifdef CK_SMALL /* Keyboard input buffer length */ #define KBUFL 32 /* Small for PDP-11 UNIX */ #else #define KBUFL 257 /* Regular kernel size for others */ #endif /* CK_SMALL */ #ifdef DYNAMIC static char *kbuf = NULL; #else static char kbuf[KBUFL]; #endif /* DYNAMIC */ /* Macro for reading keystrokes. */ #define CONGKS() (((--kbc)>=0) ? ((int)(*kbp++) & 0377) : kbget()) /* Note that we call read() directly here, normally a no-no, but in this case we know it's UNIX and we're only doing what coninc(0) would have done, except we're reading a block of characters rather than just one. There is, at present, no conxin() analog to ttxin() for chunk reads, and instituting one would only add function-call overhead as it would only be a wrapper for a read() call anyway. */ /* Another note: We stick in this read() till the user types something. But the other (lower) fork is running too, and on TELNET connections, it will signal us to indicate echo-change negotiations, and this can interrupt the read(). Some UNIXes automatically restart the interrupted system call, others return from it with errno == EINTR. */ static int /* Keyboard buffer filler */ kbget() { #ifdef EINTR int tries = 10; /* If read() is interrupted, */ int ok = 0; while (tries-- > 0) { /* try a few times... */ #endif /* EINTR */ if ((kbc = conchk()) < 1) /* How many chars waiting? */ kbc = 1; /* If none or dunno, wait for one. */ else if (kbc > KBUFL) /* If too many, */ kbc = KBUFL; /* only read this many. */ if ((kbc = read(0, kbuf, kbc)) < 1) { /* Now read it/them. */ debug(F101,"CONNECT kbget errno","",errno); /* Got an error. */ #ifdef EINTR if (errno == EINTR) /* Interrupted system call. */ continue; /* Try again, up to limit. */ else /* Something else. */ #endif /* EINTR */ return(-1); /* Pass along read() error. */ } #ifdef EINTR else { ok = 1; break; } } if (!ok) return(-1); #endif /* EINTR */ kbp = kbuf; /* Adjust buffer pointer, */ kbc--; /* count, */ return((int)(*kbp++) & 0377); /* and return first character. */ } /* C O N C L D -- Interactive terminal connection child function */ static #ifdef BEOSORBEBOX long #else VOID #endif /* BEOSORBEBOX */ concld ( #ifdef BEOSORBEBOX void *bevoid #endif /* BEOSORBEBOX */ ) { int n; /* General purpose counter */ int i; /* For loops... */ int c = -1; /* c is a character, but must be signed integer to pass thru -1, which is the modem disconnection signal, and is different from the character 0377 */ int prev; #ifdef TNCODE int tx; /* tn_doop() return code */ #endif /* TNCODE */ #ifdef CK_TRIGGER int ix; /* Trigger index */ #endif /* CK_TRIGGER */ #ifndef NOESCSEQ int apcrc; #endif /* NOESCSEQ */ #ifdef COMMENT int conret = 0; /* Return value from conect() */ jbchksum = -1L; #endif /* COMMENT */ jbset = 0; /* jmp_buf not set yet, don't use it */ debug(F101,"CONNECT concld entry","",CK_FORK_SIG); /* *** */ /* Inferior reads, prints port input */ if (priv_can()) { /* Cancel all privs */ printf("?setuid error - fatal\n"); doexit(BAD_EXIT,-1); } signal(SIGINT, SIG_IGN); /* In case these haven't been */ signal(SIGQUIT, SIG_IGN); /* inherited from above... */ signal(CK_FORK_SIG, SIG_IGN); /* CK_FORK_SIG not expected yet */ inshift = outshift = 0; /* Initial SO/SI shift state. */ { /* Wait for parent's setup */ int i; while ((i = read(xpipe[0], &c, 1)) <= 0) { if (i < 0) { debug(F101,"CONNECT concld setup error","",i); debug(F111,"CONNECT concld setup error",ck_errstr(),errno); pipemsg(CEV_HUP); /* Read error - hangup */ ck_sndmsg(); /* Send and wait to be killed */ /* NOTREACHED */ } /* Restart interrupted read() */ } } close(xpipe[0]); xpipe[0] = -1; /* Child - prevent future reads */ #ifdef DEBUG if (deblog) { debug(F100,"CONNECT starting port fork","",0); debug(F101,"CONNECT port fork ibc","",ibc); debug(F101,"CONNECT port fork obc","",obc); } #endif /* DEBUG */ what = W_CONNECT; while (1) { /* Fresh read, wait for a character. */ #ifdef ANYX25 if (network && (nettype == NET_SX25)) { bzero(x25ibuf,sizeof(x25ibuf)) ; if ((ibufl = ttxin(MAXIX25,(CHAR *)x25ibuf)) < 0) { #ifndef IBMX25 if (ibufl == -2) { /* PAD parms changes */ pipemsg(CEV_PAD); write(xpipe[1],padparms,MAXPADPARMS); ck_sndmsg(); } else { #endif /* IBMX25 */ if (!quiet) printf("\r\nCommunications disconnect "); dologend(); pipemsg(CEV_HUP); ck_sndmsg(); /* Wait to be killed */ /* NOTREACHED */ #ifndef IBMX25 } #endif /* IBMX25 */ /* pause(); <--- SHOULD BE OBSOLETE NOW! */ /* BECAUSE pause() is done inside of ck_sndmsg() */ } if (debses) { /* Debugging output */ p = x25ibuf ; while (ibufl--) { c = *p++; conol(dbchr(c)); } } else { if (seslog && sessft) /* Binary session log */ logchar((char)c); /* Log char before translation */ if (sosi #ifndef NOCSETS || tcsl != tcsr #endif /* NOCSETS */ ) { /* Character at a time */ for (i = 1; i < ibufl; i++) { c = x25ibuf[i] & cmask; if (sosi) { /* Handle SI/SO */ if (c == SO) { inshift = 1; continue; } else if (c == SI) { inshift = 0; continue; } if (inshift) c |= 0200; } #ifndef NOCSETS if (inesc == ES_NORMAL) { #ifdef UNICODE int x; if (unicode == 1) { /* Remote is UTF-8 */ x = u_to_b((CHAR)c); if (x == -1) continue; else if (x == -2) { /* LS or PS */ inxbuf[0] = CR; inxbuf[1] = LF; inxcount = 2; } else { inxbuf[0] = (unsigned)(x & 0xff); } c = inxbuf[0]; } else if (unicode == 2) { /* Local is UTF-8 */ inxcount = b_to_u((CHAR)c,inxbuf,OUTXBUFSIZ,tcssize); c = inxbuf[0]; } else { #endif /* UNICODE */ if (sxi) c = (*sxi)((CHAR)c); if (rxi) c = (*rxi)((CHAR)c); inxbuf[0] = c; #ifdef UNICODE } #endif /* UNICODE */ } #endif /* NOCSETS */ c &= cmdmsk; /* Apply command mask. */ conoc(c); /* Output to screen */ if (seslog && !sessft) /* and session log */ logchar(c); } } else { /* All at once */ for (i = 1; i < ibufl; i++) x25ibuf[i] &= (cmask & cmdmsk); conxo(ibufl,x25ibuf); if (seslog) zsoutx(ZSFILE,x25ibuf,ibufl); } } continue; } else { /* Not X.25... */ #endif /* ANYX25 */ /* Get the next communication line character from our internal buffer. If the buffer is empty, refill it. */ prev = c; /* Remember previous character */ c = ckcgetc(0); /* Get next character */ /* debug(F101,"CONNECT c","",c); */ if (c < 0) { /* Failed... */ debug(F101,"CONNECT disconnect ibc","",ibc); debug(F101,"CONNECT disconnect obc","",obc); ckcputf(); /* Flush CONNECT output buffer */ if (!quiet) { printf("\r\nCommunications disconnect "); #ifdef COMMENT if ( c == -3 #ifdef ultrix /* This happens on Ultrix if there's no carrier */ && errno != EIO #endif /* ultrix */ #ifdef UTEK /* This happens on UTEK if there's no carrier */ && errno != EWOULDBLOCK #endif /* UTEK */ ) perror("\r\nCan't read character"); #endif /* COMMENT */ } #ifdef NOSETBUF fflush(stdout); #endif /* NOSETBUF */ tthang(); /* Hang up the connection */ debug(F111,"CONNECT concld i/o error",ck_errstr(),errno); pipemsg(CEV_HUP); ck_sndmsg(); /* Wait to be killed */ } #ifdef COMMENT /* too much... */ debug(F101,"CONNECT ** PORT","",c); /* Got character c OK. */ #endif /* COMMENT */ #ifdef TNCODE /* Handle TELNET negotiations... */ if ((c == NUL) && network && IS_TELNET()) { if (prev == CR) /* Discard of */ if (!TELOPT_U(TELOPT_BINARY)) continue; } if ((c == IAC) && network && IS_TELNET()) { int me_bin = TELOPT_ME(TELOPT_BINARY); int u_bin = TELOPT_U(TELOPT_BINARY); debug(F100,"CONNECT got IAC","",0); ckcputf(); /* Dump screen-output buffer */ if ((tx = tn_doop((CHAR)(c & 0xff),duplex,ckcgetc)) == 0) { if (me_bin != TELOPT_ME(TELOPT_BINARY)) { debug(F101, "CONNECT TELNET me_bin", "", TELOPT_ME(TELOPT_BINARY) ); pipemsg(CEV_MEBIN); /* Tell parent */ write(xpipe[1], &TELOPT_ME(TELOPT_BINARY), sizeof(TELOPT_ME(TELOPT_BINARY)) ); ck_sndmsg(); /* Tell the parent fork */ } else if (u_bin != TELOPT_U(TELOPT_BINARY)) { debug(F101, "CONNECT TELNET u_bin", "", TELOPT_U(TELOPT_BINARY) ); pipemsg(CEV_UBIN); /* Tell parent */ write(xpipe[1], &TELOPT_U(TELOPT_BINARY), sizeof(TELOPT_U(TELOPT_BINARY)) ); ck_sndmsg(); /* Tell the parent fork */ } continue; } else if (tx == -1) { /* I/O error */ if (!quiet) printf("\r\nCommunications disconnect "); #ifdef NOSETBUF fflush(stdout); #endif /* NOSETBUF */ dologend(); debug(F111,"CONNECT concld i/o error 2",ck_errstr(),errno); pipemsg(CEV_HUP); ck_sndmsg(); /* Wait to be killed */ /* NOTREACHED */ } else if (tx == -3) { /* I/O error */ if (!quiet) printf("\r\nConnection closed due to telnet policy "); #ifdef NOSETBUF fflush(stdout); #endif /* NOSETBUF */ dologend(); debug(F111,"CONNECT concld i/o error 2",ck_errstr(),errno); pipemsg(CEV_HUP); ck_sndmsg(); /* Wait to be killed */ /* NOTREACHED */ } else if (tx == -2) { /* I/O error */ if (!quiet) printf("\r\nConnection closed by peer "); #ifdef NOSETBUF fflush(stdout); #endif /* NOSETBUF */ dologend(); debug(F111,"CONNECT concld i/o error 2",ck_errstr(),errno); pipemsg(CEV_HUP); ck_sndmsg(); /* Wait to be killed */ /* NOTREACHED */ } else if ((tx == 1) && (!duplex)) { /* ECHO change */ duplex = 1; /* Turn on local echo */ debug(F101,"CONNECT TELNET duplex change","",duplex); pipemsg(CEV_DUP); /* Tell parent */ write(xpipe[1], &duplex, sizeof(duplex)); ck_sndmsg(); /* Tell the parent fork */ continue; } else if ((tx == 2) && (duplex)) { /* ECHO change */ duplex = 0; debug(F101,"CONNECT TELNET duplex change","",duplex); pipemsg(CEV_DUP); write(xpipe[1], &duplex, sizeof(duplex)); ck_sndmsg(); continue; } else if (tx == 3) { /* Quoted IAC */ c = parity ? 127 : 255; } #ifdef IKS_OPTION else if (tx == 4) { /* IKS State Change */ if (TELOPT_SB(TELOPT_KERMIT).kermit.u_start && !tcp_incoming ) { /* here we need to print a msg that the other */ /* side is in SERVER mode and that REMOTE */ /* commands should be used. And CONNECT mode */ /* should be ended. */ active = 0; } } #endif /* IKS_OPTION */ else if (tx == 6) { /* DO LOGOUT received */ if (!quiet) printf("\r\nRemote Logout "); #ifdef NOSETBUF fflush(stdout); #endif /* NOSETBUF */ debug(F100,"CONNECT Remote Logout","",0); pipemsg(CEV_HUP); ck_sndmsg(); /* Wait to be killed */ /* NOTREACHED */ } else continue; /* Negotiation OK, get next char. */ } else if (parity) c &= 0x7f; if (TELOPT_ME(TELOPT_ECHO) && tn_rem_echo) ttoc(c); /* I'm echoing for the remote */ #endif /* TNCODE */ if (debses) { /* Output character to screen */ char *s; /* Debugging display... */ s = dbchr(c); while (*s) ckcputc(*s++); } else { /* Regular display ... */ c &= cmask; /* Apply Kermit-to-remote mask */ #ifdef CK_AUTODL /* Autodownload. Check for Kermit S packet prior to translation, since that can change the packet and make it unrecognizable (as when the terminal character set is an ISO 646 one)... Ditto for Zmodem start packet. */ if (autodl /* Autodownload enabled? */ #ifdef IKS_OPTION || TELOPT_SB(TELOPT_KERMIT).kermit.me_start #endif /* IKS_OPTION */ ) { int k; k = kstart((CHAR)c); /* Kermit S or I packet? */ #ifdef CK_XYZ if (!k && zmdlok) /* Or an "sz" start? */ k = zstart((CHAR)c); #endif /* CK_XYZ */ if (k) { int ksign = 0; debug(F101,"CONNECT autodownload k","",k); if (k < 0) { /* Minus-Protocol? */ #ifdef NOSERVER goto noserver; /* Need server mode for this */ #else ksign = 1; /* Remember */ k = 0 - k; /* Convert to actual protocol */ justone = 1; /* Flag for protocol module */ #endif /* NOSERVER */ } else justone = 0; k--; /* Adjust [kz]start's return value */ if (k == PROTO_K #ifdef CK_XYZ || k == PROTO_Z #endif /* CK_XYZ */ ) { /* Now damage the packet so that it does not */ /* trigger autodownload detection on subsquent */ /* links. */ if (k == PROTO_K) { int i, len = strlen((char*)ksbuf); for (i = 0; i < len; i++) ckcputc(BS); } #ifdef CK_XYZ else { int i; for (i = 0; i < 3; i++) ckcputc(CAN); } #endif /* CK_XYZ */ /* Notify parent */ pipemsg(justone ? CEV_AUL : CEV_ADL); /* Send our memory back up to the top fork thru the pipe. CAREFUL -- Write this stuff in the same order it is to be read! */ /* Copy our Kermit packet to the parent fork */ n = (int) strlen((char *)ksbuf); write(xpipe[1], (char *)&n, sizeof(n)); if (n > 0) write(xpipe[1], (char *)ksbuf, n+1); debug(F111,"CONNECT autodownload ksbuf",ksbuf,n); debug(F101,"CONNECT autodownload justone","", justone); /* Construct the APC command */ sprintf(apcbuf, "set proto %s, %s, set proto %s", ptab[k].p_name, ksign ? "server" : "receive", ptab[protocol].p_name ); apclength = strlen(apcbuf); debug(F111,"CONNECT ksbuf",ksbuf,k); debug(F110,"CONNECT autodownload",apcbuf,0); apcactive = APC_LOCAL; ckcputf(); /* Force screen update */ /* Write buffer including trailing NUL byte */ debug(F101,"CONNECT write xpipe apclength","", apclength); write(xpipe[1], (char *)&apclength, sizeof(apclength) ); debug(F110,"CONNECT write xpipe apcbuf",apcbuf,0); write(xpipe[1], apcbuf, apclength+1); /* Copy our input buffer to the parent fork */ debug(F101,"CONNECT autodownload complete ibc", "",ibc); debug(F101,"CONNECT autodownload complete obc", "",obc); write(xpipe[1], (char *)&ibc, sizeof(ibc)); if (ibc > 0) { write(xpipe[1], (char *)&ibp, sizeof(ibp)); write(xpipe[1], ibp, ibc); } ck_sndmsg(); /* Wait to be killed */ /* NOTREACHED */ } } } #ifdef NOSERVER noserver: #endif /* NOSERVER */ #endif /* CK_AUTODL */ if (sosi) { /* Handle SI/SO */ if (c == SO) { /* Shift Out */ inshift = 1; continue; } else if (c == SI) { /* Shift In */ inshift = 0; continue; } if (inshift) c |= 0200; } inxbuf[0] = c; /* In case there is no translation */ inxcount = 1; /* ... */ #ifndef NOCSETS if (inesc == ES_NORMAL) { /* If not in an escape sequence */ #ifdef UNICODE int x; /* Translate character sets */ CHAR ch; ch = c; if (unicode == 1) { /* Remote is UTF-8 */ x = u_to_b(ch); if (x < 0) continue; inxbuf[0] = (unsigned)(x & 0xff); c = inxbuf[0]; } else if (unicode == 2) { /* Local is UTF-8 */ inxcount = b_to_u(ch,inxbuf,OUTXBUFSIZ,tcssize); c = inxbuf[0]; } else { #endif /* UNICODE */ if (sxi) c = (*sxi)((CHAR)c); if (rxi) c = (*rxi)((CHAR)c); inxbuf[0] = c; #ifdef UNICODE } #endif /* UNICODE */ } #endif /* NOCSETS */ #ifndef NOESCSEQ if (escseq) /* If handling escape sequences */ apcrc = chkaes((char)c); /* update our state */ #ifdef CK_APC /* If we are handling APCs, we have several possibilities at this point: 1. Ordinary character to be written to the screen. 2. An Esc; we can't write it because it might be the beginning of an APC. 3. The character following an Esc, in which case we write Esc, then char, but only if we have not just entered an APC sequence. */ if (escseq && (apcstatus & APC_ON)) { if (inesc == ES_GOTESC) /* Don't write ESC yet */ continue; else if (oldesc == ES_GOTESC && !apcactive) { ckcputc(ESC); /* Write saved ESC */ if (seslog && !sessft) logchar((char)ESC); } else if (apcrc) { /* We have an APC */ debug(F111,"CONNECT APC complete",apcbuf,apclength); ckcputf(); /* Force screen update */ pipemsg(CEV_APC); /* Notify parent */ write(xpipe[1], (char *)&apclength, sizeof(apclength) ); /* Write buffer including trailing NUL byte */ write(xpipe[1], apcbuf, apclength+1); /* Copy our input buffer to the parent fork */ debug(F101,"CONNECT APC complete ibc","",ibc); debug(F101,"CONNECT APC complete obc","",obc); write(xpipe[1], (char *)&ibc, sizeof(ibc)); if (ibc > 0) { write(xpipe[1], (char *)&ibp, sizeof(ibp)); write(xpipe[1], ibp, ibc); } ck_sndmsg(); /* Wait to be killed */ /* NOTREACHED */ } } #endif /* CK_APC */ #endif /* NOESCSEQ */ for (i = 0; i < inxcount; i++) { /* Loop thru */ c = inxbuf[i]; /* input expansion buffer... */ if ( #ifdef CK_APC !apcactive /* Ignore APC sequences */ #else 1 #endif /* CK_APC */ ) { c &= cmdmsk; /* Apply command mask. */ if (c == CR && tt_crd) { /* SET TERM CR-DISPLA CRLF? */ ckcputc(c); /* Yes, output CR */ if (seslog && !sessft) logchar((char)c); c = LF; /* and insert a linefeed */ } if (c == LF && tt_lfd) { /* SET TERM CR-DISPLA CRLF? */ ckcputc(CR); /* Yes, output CR */ if (seslog && !sessft) logchar((char)CR); } ckcputc(c); /* Write character to screen */ } if (seslog && !sessft) /* Handle session log */ logchar((char)c); #ifdef CK_TRIGGER /* Check for trigger string */ if (tt_trigger[0]) if ((ix = autoexitchk((CHAR)c)) > -1) { ckcputf(); /* Force screen update */ #ifdef NOSETBUF fflush(stdout); /* I mean really force it */ #endif /* NOSETBUF */ pipemsg(CEV_TRI); /* Send up trigger indication */ write(xpipe[1], (char *)&ix, sizeof(ix)); /* index */ write(xpipe[1], (char *)&ibc, sizeof(ibc)); if (ibc > 0) { write(xpipe[1], (char *)&ibp, sizeof(ibp)); write(xpipe[1], ibp, ibc); } debug(F100,"CONNECT concld trigger","",0); ck_sndmsg(); /* Wait to be killed */ active = 0; /* Shouldn't be necessary... */ break; } /* NOTREACHED */ #endif /* CK_TRIGGER */ } } #ifdef ANYX25 } #endif /* ANYX25 */ } } /* C O N E C T -- Interactive terminal connection */ int conect() { int n; /* General purpose counter */ int i; /* For loops... */ int c; /* c is a character, but must be signed integer to pass thru -1, which is the modem disconnection signal, and is different from the character 0377 */ int c2; /* A copy of c */ int csave; /* Another copy of c */ #ifndef NOESCSEQ int apcrc; #endif /* NOESCSEQ */ int conret = 0; /* Return value from conect() */ int msgflg = 0; /* jbchksum = -1L; */ jbset = 0; /* jmp_buf not set yet, don't use it */ debok = 1; debug(F101,"CONNECT fork signal","",CK_FORK_SIG); debug(F101,"CONNECT entry pid","",pid); msgflg = !quiet #ifdef CK_APC && !apcactive #endif /* CK_APC */ ; /* The following is to handle a fork left behind if we exit CONNECT mode without killing it, and then return to CONNECT mode. This happened in HP-UX, where the Reset key would raise SIGINT even though SIGINT was set to SIG_IGN. The code below fixes the symptom; the real fix is in the main SIGINT handler (if SIGINT shows up during CONNECT, just return rather than taking the longjmp). */ if (pid) { /* This should be 0 */ int x = 0; debug(F101,"CONNECT entry killing stale pid","",pid); printf("WARNING: Old CONNECT fork seems to be active.\n"); printf("Attempting to remove it..."); #ifdef BEOSORBEBOX { long ret_val; x = kill(pid,SIGKILLTHR); /* Kill lower fork */ wait_for_thread (pid, &ret_val); } #else #ifdef Plan9 x = kill(pid,SIGKILL); /* Kill lower fork */ #else x = kill(pid,9); #endif /* Plan9 */ #endif /* BEOSORBEBOX */ wait((WAIT_T *)0); /* Wait till gone. */ if (x < 0) { printf("ERROR: Failure to kill pid %d: %s, errno=%d\n", (int) pid, ck_errstr(), errno); debug(F111,"CONNECT error killing stale pid",ck_errstr(),pid); } pid = (PID_T) 0; printf("\n"); } signal(CK_FORK_SIG, SIG_IGN); /* Initial CK_FORK_SIG handling, */ /* The following ttimoff() call should not be necessary, but evidently there are cases where a timer is left active and then goes off, taking a longjmp to nowhere after the program's stack has changed. In any case, this is safe because the CONNECT module uses no timer of any kind, and no other timer should be armed while Kermit is in CONNECT mode. */ ttimoff(); /* Turn off any timer interrupts */ #ifdef CK_TRIGGER makestr(&triggerval,NULL); /* Reset trigger */ #endif /* CK_TRIGGER */ if (!local) { #ifdef NETCONN printf("Sorry, you must SET LINE or SET HOST first\n"); #else printf("Sorry, you must SET LINE first\n"); #endif /* NETCONN */ goto conret0; } if (speed < 0L && network == 0 && ttfdflg == 0) { printf("Sorry, you must SET SPEED first\n"); goto conret0; } #ifdef TCPSOCKET if (network && (nettype != NET_TCPB) #ifdef SUNX25 && (nettype != NET_SX25) #endif /* SUNX25 */ #ifdef IBMX25 && (nettype != NET_IX25) #endif /* IBMX25 */ #ifdef NETCMD && (nettype != NET_CMD) #endif /* NETCMD */ #ifdef NETPTY && (nettype != NET_PTY) #endif /* NETPTY */ ) { printf("Sorry, network type not supported\n"); goto conret0; } #endif /* TCPSOCKET */ #ifdef DYNAMIC if (!ibuf) { if (!(ibuf = malloc(IBUFL+1))) { /* Allocate input line buffer */ printf("Sorry, CONNECT input buffer can't be allocated\n"); goto conret0; } else { ibp = ibuf; ibc = 0; } } if (!obuf) { if (!(obuf = malloc(OBUFL+1))) { /* Allocate output line buffer */ printf("Sorry, CONNECT output buffer can't be allocated\n"); goto conret0; } else { obp = obuf; obc = 0; } } if (!kbuf) { if (!(kbuf = malloc(KBUFL+1))) { /* Allocate keyboard input buffer */ printf("Sorry, CONNECT keyboard buffer can't be allocated\n"); goto conret0; } } if (!temp) { if (!(temp = malloc(TMPLEN+1))) { /* Allocate temporary buffer */ printf("Sorry, CONNECT temporary buffer can't be allocated\n"); goto conret0; } } #else #ifdef COMMENT ibp = ibuf; ibc = 0; #endif /* COMMENT */ obp = obuf; obc = 0; #endif /* DYNAMIC */ kbp = kbuf; /* Always clear these. */ *kbp = NUL; /* No need to preserve them between */ kbc = 0; /* CONNECT sessions. */ #ifdef DEBUG if (deblog) { debug(F101,"CONNECT conect entry ttyfd","",ttyfd); debug(F101,"CONNECT conect entry ibc","",ibc); debug(F101,"CONNECT conect entry obc","",obc); debug(F101,"CONNECT conect entry kbc","",kbc); #ifdef CK_TRIGGER debug(F110,"CONNECT conect trigger",tt_trigger[0],0); #endif /* CK_TRIGGER */ if (ttyfd > -1) { n = ttchk(); debug(F101,"CONNECT conect entry ttchk","",n); } } #endif /* DEBUG */ if (ttyfd < 0) { /* If communication device not open */ debug(F101,"CONNECT ttnproto","",ttnproto); debug(F111,"CONNECT opening",ttname,0); /* Open it now */ if (ttopen(ttname, &local, network ? -nettype : mdmtyp, 0 ) < 0) { ckmakmsg(temp,TMPLEN,"Sorry, can't open ",ttname,NULL,NULL); perror(temp); debug(F110,"CONNECT open failure",ttname,0); goto conret0; } #ifdef IKS_OPTION /* If peer is in Kermit server mode, return now. */ if (TELOPT_SB(TELOPT_KERMIT).kermit.u_start) return(0); #endif /* IKS_OPTION */ } dohangup = 0; /* Hangup not requested yet */ #ifdef ANYX25 dox25clr = 0; /* X.25 clear not requested yet */ #endif /* ANYX25 */ if (msgflg) { #ifdef NETCONN if (network) { if (ttpipe) printf("Connecting via command \"%s\"",ttname); else printf("Connecting to host %s",ttname); #ifdef ANYX25 if (nettype == NET_SX25 || nettype == NET_IX25) printf(", Link ID %d, LCN %d",linkid,lcn); #endif /* ANYX25 */ } else { #endif /* NETCONN */ printf("Connecting to %s",ttname); if (speed > -1L) printf(", speed %ld",speed); #ifdef NETCONN } #endif /* NETCONN */ if (tt_escape) { printf("\r\n"); shoesc(escape); printf("Type the escape character followed by C to get back,\r\n"); printf("or followed by ? to see other options.\r\n"); } else { printf(".\r\n\nESCAPE CHARACTER IS DISABLED\r\n\n"); } if (seslog) { extern int slogts; char * s = ""; switch (sessft) { case XYFT_D: s = "debug"; break; case XYFT_T: s = slogts ? "timestamped-text" : "text"; break; default: s = "binary"; } printf("Session Log: %s, %s\r\n",sesfil,s); } if (debses) printf("Debugging Display...)\r\n"); printf("----------------------------------------------------\r\n"); fflush(stdout); } /* Condition console terminal and communication line */ if (conbin((char)escape) < 0) { printf("Sorry, can't condition console terminal\n"); goto conret0; } debug(F101,"CONNECT cmask","",cmask); debug(F101,"CONNECT cmdmsk","",cmdmsk); debug(F101,"CONNECT speed before ttvt","",speed); if ((n = ttvt(speed,flow)) < 0) { /* Enter "virtual terminal" mode */ if (!network) { debug(F101,"CONNECT ttvt","",n); tthang(); /* Hang up and close the device. */ ttclos(0); dologend(); if (ttopen(ttname, /* Open it again... */ &local, network ? -nettype : mdmtyp, 0 ) < 0) { ckmakmsg(temp,TMPLEN,"Sorry, can't reopen ",ttname,NULL,NULL); perror(temp); goto conret0; } #ifdef IKS_OPTION if (TELOPT_SB(TELOPT_KERMIT).kermit.u_start) return(0); #endif /* IKS_OPTION */ if (ttvt(speed,flow) < 0) { /* Try virtual terminal mode again. */ conres(); /* Failure this time is fatal. */ printf("Sorry, Can't condition communication line\n"); goto conret0; } } } debug(F101,"CONNECT ttvt ok, escape","",escape); debug(F101,"CONNECT carrier-watch","",carrier); if ((!network #ifdef TN_COMPORT || istncomport() #endif /* TN_COMPORT */ ) && (carrier != CAR_OFF)) { int x; x = ttgmdm(); debug(F100,"CONNECT ttgmdm","",x); if ((x > -1) && !(x & BM_DCD)) { #ifndef NOHINTS extern int hints; #endif /* NOHINTS */ debug(F100,"CONNECT ttgmdm CD test fails","",x); conres(); printf("?Carrier required but not detected.\n"); #ifndef NOHINTS if (!hints) return(0); printf("***********************************\n"); printf(" Hint: To CONNECT to a serial device that\n"); printf(" is not presenting the Carrier Detect signal,\n"); printf(" first tell C-Kermit to:\n\n"); printf(" SET CARRIER-WATCH OFF\n\n"); printf("***********************************\n\n"); #endif /* NOHINTS */ goto conret0; } debug(F100,"CONNECT ttgmdm ok","",0); } #ifndef NOCSETS /* Set up character set translations */ unicode = 0; /* Assume Unicode won't be involved */ tcs = 0; /* "Transfer" or "Other" charset */ sxo = rxo = NULL; /* Initialize byte-to-byte functions */ sxi = rxi = NULL; if (tcsr != tcsl) { /* Remote and local sets differ... */ #ifdef UNICODE if (tcsr == FC_UTF8 || /* Remote charset is UTF-8 */ tcsl == FC_UTF8) { /* or local one is. */ xuf = xl_ufc[tcsl]; /* Incoming Unicode to local */ if (xuf || tcsl == FC_UTF8) { tcs = (tcsr == FC_UTF8) ? tcsl : tcsr; /* The "other" set */ xfu = xl_fcu[tcs]; /* Local byte to remote Unicode */ if (xfu) unicode = (tcsr == FC_UTF8) ? 1 : 2; } tcssize = fcsinfo[tcs].size; /* Size of other character set. */ } else { #endif /* UNICODE */ tcs = gettcs(tcsr,tcsl); /* Get intermediate set. */ sxo = xls[tcs][tcsl]; /* translation function */ rxo = xlr[tcs][tcsr]; /* pointers for output functions */ sxi = xls[tcs][tcsr]; /* and for input functions. */ rxi = xlr[tcs][tcsl]; #ifdef UNICODE } #endif /* UNICODE */ } /* This is to prevent use of zmstuff() and zdstuff() by translation functions. They only work with disk i/o, not with communication i/o. Luckily Russian translation functions don't do any stuffing... */ langsv = language; #ifndef NOCYRIL if (language != L_RUSSIAN) #endif /* NOCYRIL */ language = L_USASCII; #ifdef COMMENT #ifdef DEBUG if (deblog) { debug(F101,"CONNECT tcs","",tcs); debug(F101,"CONNECT tcsl","",tcsl); debug(F101,"CONNECT tcsr","",tcsr); debug(F101,"CONNECT fcsinfo[tcsl].size","",fcsinfo[tcsl].size); debug(F101,"CONNECT fcsinfo[tcsr].size","",fcsinfo[tcsr].size); debug(F101,"CONNECT unicode","",unicode); } #endif /* DEBUG */ #endif /* COMMENT */ #ifdef CK_XYZ #ifndef XYZ_INTERNAL { extern int binary; /* See about ZMODEM autodownloads */ char * s; s = binary ? ptab[PROTO_Z].p_b_rcmd : ptab[PROTO_Z].p_t_rcmd; if (!s) s = ""; zmdlok = (*s != NUL); /* OK if we have external commands */ } #endif /* XYZ_INTERNAL */ #endif /* CK_XYZ */ #ifndef NOESCSEQ /* We need to activate the escape-sequence recognition feature when: (a) translation is elected, AND (b) the local and/or remote set is a 7-bit set other than US ASCII. Or: SET TERMINAL APC is not OFF (handled in the next statement). */ escseq = (tcs != TC_TRANSP) && /* Not transparent */ (fcsinfo[tcsl].size == 128 || fcsinfo[tcsr].size == 128) && /* 7 bits */ (fcsinfo[tcsl].code != FC_USASCII); /* But not ASCII */ #endif /* NOESCSEQ */ #endif /* NOCSETS */ #ifndef NOESCSEQ #ifdef CK_APC escseq = escseq || (apcstatus & APC_ON); apcactive = 0; /* An APC command is not active */ apclength = 0; /* ... */ #endif /* CK_APC */ inesc = ES_NORMAL; /* Initial state of recognizer */ debug(F101,"CONNECT escseq","",escseq); #endif /* NOESCSEQ */ parent_id = getpid(); /* Get parent's pid for signalling */ debug(F101,"CONNECT parent pid","",parent_id); if (xpipe[0] > -1) /* If old pipe hanging around, close */ close(xpipe[0]); xpipe[0] = -1; if (xpipe[1] > -1) close(xpipe[1]); xpipe[1] = -1; goterr = 0; /* Error flag for pipe & fork */ if (pipe(xpipe) != 0) { /* Create new pipe to pass info */ perror("Can't make pipe"); /* between forks. */ debug(F101,"CONNECT pipe error","",errno); goterr = 1; } else #ifdef BEOSORBEBOX { pid = spawn_thread(concld, "Lower Fork", B_NORMAL_PRIORITY, NULL); resume_thread(pid); } #else if ((pid = fork()) == (PID_T) -1) { /* Pipe OK, make port fork. */ perror("Can't make port fork"); debug(F101,"CONNECT fork error","",errno); goterr = 1; } #endif /* BEOSORBEBOX */ debug(F101,"CONNECT created fork, pid","",pid); if (goterr) { /* Failed to make pipe or fork */ conres(); /* Reset the console. */ if (msgflg) { printf("\r\nCommunications disconnect (Back at %s)\r\n", *myhost ? myhost : #ifdef UNIX "local UNIX system" #else "local system" #endif /* UNIX */ ); } printf("\n"); what = W_NOTHING; /* So console modes are set right. */ #ifndef NOCSETS language = langsv; /* Restore language */ #endif /* NOCSETS */ parent_id = (PID_T) 0; /* Clean up */ goto conret1; } debug(F101,"CONNECT fork pid","",pid); /* Upper fork (KEYB fork) reads keystrokes and sends them out. */ if (pid) { /* pid != 0, so I am the upper fork. */ /* Before doing anything significant, the child fork must wait for a go-ahead character from xpipe[0]. Before starting to wait, we have enough time to clear buffers and set up the signal handler. When done with this, we will allow the child to continue by satisfying its pending read. Remember the child and parent have separate address space. The child has its own copy of input buffers, so we must clear the input buffers in the parent. Otherwise strange effects may occur, like chunks of characters repeatedly echoed on terminal screen. The child process is designed to empty its input buffers by reading all available characters and either echoing them on the terminal screen or saving them for future use in the parent. The latter case happens during APC processing - see the code around CEV_APC occurrences to see how the child passes its ibuf etc to parent via xpipe, for preservation until the next entry to this module, to ensure that no characters are lost between CONNECT sessions. */ /* This one needs a bit of extra explanation... In addition to the CONNECT module's own buffers, which are communicated and synchronized via xpipe, the low-level UNIX communication routines (ttinc, ttxin, etc) are also buffered, statically, in the ckutio.c module. But when the two CONNECT forks split off, the lower fork is updating this buffer's pointers and counts, but the upper fork never finds out about it and still has the old ones. The following UNIX-specific call to the ckutio.c module takes care of this... Without it, we get dual echoing of incoming characters. */ ttflux(); /* At this point, perhaps you are wondering why we use forks at all. It is simply because there is no other method portable among all UNIX variations. Not threads, not select(), ... (Yes, select() is more common now; it might actually be worth writing a variation of this module that works like BSD Telnet, one fork, driven by select()). */ ibp = ibuf; /* Clear ibuf[]. */ ibc = 0; /* Child now has its own copy */ signal(CK_FORK_SIG, pipeint); /* Handler for messages from child. */ write(xpipe[1], ibuf, 1); /* Allow child to proceed */ close(xpipe[1]); xpipe[1] = -1; /* Parent - prevent future writes */ what = W_CONNECT; /* Keep track of what we're doing */ active = 1; debug(F101,"CONNECT keyboard fork duplex","",duplex); /* Catch communication errors or mode changes in lower fork. Note: Some C compilers (e.g. Cray UNICOS) interpret the ANSI C standard about setjmp() in a way that disallows constructions like: if ((var = [sig]setjmp(env)) == 0) ... which prevents the value returned by cklongjmp() from being used at all. So the signal handlers set a global variable, sjval, instead. */ if ( #ifdef CK_POSIX_SIG sigsetjmp(con_env,1) #else setjmp(con_env) #endif /* CK_POSIX_SIG */ == 0) { /* Normal entry... */ jbset = 1; /* Now we have a longjmp buffer */ sjval = CEV_NO; /* Initialize setjmp return code. */ debug(F101,"CONNECT setjmp normal entry","",sjval); #ifdef ANYX25 if (network && (nettype == NET_SX25 || nettype == NET_IX25)) { obufl = 0; bzero (x25obuf,sizeof(x25obuf)); } #endif /* ANYX25 */ /* Here is the big loop that gets characters from the keyboard and sends them out the communication device. There are two components to the communication path: the connection from the keyboard to C-Kermit, and from C-Kermit to the remote computer. The treatment of the 8th bit of keyboard characters is governed by SET COMMAND BYTESIZE (cmdmsk). The treatment of the 8th bit of characters sent to the remote is governed by SET TERMINAL BYTESIZE (cmask). This distinction was introduced in edit 5A(164). */ while (active) { #ifndef NOSETKEY if (kmptr) { /* Have current macro? */ debug(F100,"CONNECT kmptr non NULL","",0); if ((c = (CHAR) *kmptr++) == NUL) { /* Get char from it */ kmptr = NULL; /* If no more chars, */ debug(F100,"CONNECT macro empty, continuing","",0); continue; /* reset pointer and continue */ } debug(F000,"CONNECT char from macro","",c); } else /* No macro... */ #endif /* NOSETKEY */ c = CONGKS(); /* Read from keyboard */ #ifdef COMMENT /* too much... */ debug(F101,"CONNECT ** KEYB","",c); #endif /* COMMENT */ if (c == -1) { /* If read() got an error... */ debug(F101,"CONNECT keyboard read errno","",errno); #ifdef COMMENT /* This seems to cause problems. If read() returns -1, the signal has already been delivered, and nothing will wake up the pause(). */ pause(); /* Wait for transmitter to finish. */ #else #ifdef A986 /* On Altos machines with Xenix 3.0, pressing DEL in connect mode brings us here (reason unknown). The console line discipline at this point has intr = ^C. The communications tty has intr = DEL but we get here after pressing DEL on the keyboard, even when the remote system has been set not to echo. With A986 defined, we stay in the read loop and beep only if the offending character is not DEL. */ if ((c & 127) != 127) conoc(BEL); #else #ifdef EINTR /* This can be caused by the other fork signalling this one about an echoing change during TELNET negotiations. */ if (errno == EINTR) continue; #endif /* EINTR */ conoc(BEL); /* Otherwise, beep */ active = 0; /* and terminate the read loop */ continue; #endif /* A986 */ #endif /* COMMENT */ } c &= cmdmsk; /* Do any requested masking */ #ifndef NOSETKEY /* Note: kmptr is NULL if we got character c from the keyboard, and it is not NULL if it came from a macro. In the latter case, we must avoid expanding it again. */ if (!kmptr && macrotab[c]) { /* Macro definition for c? */ kmptr = macrotab[c]; /* Yes, set up macro pointer */ continue; /* and restart the loop, */ } else c = keymap[c]; /* else use single-char keymap */ #endif /* NOSETKEY */ if ( #ifndef NOSETKEY !kmptr && #endif /* NOSETKEY */ (tt_escape && (c & 0xff) == escape)) { /* Escape char? */ debug(F000,"CONNECT got escape","",c); c = CONGKS() & 0177; /* Got esc, get its arg */ /* No key mapping here */ doesc((char) c); /* Now process it */ } else { /* It's not the escape character */ csave = c; /* Save it before translation */ /* for local echoing. */ #ifndef NOCSETS if (inesc == ES_NORMAL) { /* If not inside escape seq.. */ /* Translate character sets */ #ifdef UNICODE int x; CHAR ch; ch = c; if (unicode == 1) { /* Remote is UTF-8 */ outxcount = b_to_u(ch,outxbuf,OUTXBUFSIZ,tcssize); outxbuf[outxcount] = NUL; } else if (unicode == 2) { /* Local is UTF-8 */ x = u_to_b(ch); /* So translate to remote byte */ if (x < 0) continue; outxbuf[0] = (unsigned)(x & 0xff); outxcount = 1; outxbuf[outxcount] = NUL; } else { #endif /* UNICODE */ /* Local-to-intermediate */ if (sxo) c = (*sxo)((char)c); /* Intermediate-to-remote */ if (rxo) c = (*rxo)((char)c); outxbuf[0] = c; outxcount = 1; outxbuf[outxcount] = NUL; #ifdef UNICODE } #endif /* UNICODE */ } if (escseq) apcrc = chkaes((char)c); #else outxbuf[0] = c; outxcount = 1; outxbuf[outxcount] = NUL; #endif /* NOCSETS */ for (i = 0; i < outxcount; i++) { c = outxbuf[i]; /* If Shift-In/Shift-Out is selected and we have a 7-bit connection, handle shifting here. */ if (sosi) { /* Shift-In/Out selected? */ if (cmask == 0177) { /* In 7-bit environment? */ if (c & 0200) { /* 8-bit character? */ if (outshift == 0) { /* If not shifted, */ ttoc(dopar(SO)); /* shift. */ outshift = 1; } } else { if (outshift == 1) { /* 7-bit character */ ttoc(dopar(SI)); /* If shifted, */ outshift = 0; /* unshift. */ } } } if (c == SO) outshift = 1; /* User typed SO */ if (c == SI) outshift = 0; /* User typed SI */ } c &= cmask; /* Apply Kermit-to-host mask now. */ #ifdef SUNX25 if (network && nettype == NET_SX25) { if (padparms[PAD_ECHO]) { if (debses) conol(dbchr(c)) ; else if ((c != padparms[PAD_CHAR_DELETE_CHAR]) && (c != padparms[PAD_BUFFER_DELETE_CHAR]) && (c != padparms[PAD_BUFFER_DISPLAY_CHAR])) conoc(c) ; if (seslog && !sessft) logchar(c); } if (c == CR && (padparms[PAD_LF_AFTER_CR] == 4 || padparms[PAD_LF_AFTER_CR] == 5)) { if (debses) conol(dbchr(LF)) ; else conoc(LF) ; if (seslog && !sessft) logchar(LF); } if (c == padparms[PAD_BREAK_CHARACTER]) { breakact(); } else if (padparms[PAD_DATA_FORWARD_TIMEOUT]) { tosend = 1; x25obuf [obufl++] = c; } else if (((c == padparms[PAD_CHAR_DELETE_CHAR])|| (c == padparms[PAD_BUFFER_DELETE_CHAR]) || (c == padparms[PAD_BUFFER_DISPLAY_CHAR])) && (padparms[PAD_EDITING])) { if (c == padparms[PAD_CHAR_DELETE_CHAR]) { if (obufl > 0) { conol("\b \b"); obufl--; } else {} } else if (c == padparms[PAD_BUFFER_DELETE_CHAR]) { conol ("\r\nPAD Buffer Deleted\r\n"); obufl = 0; } else if (c==padparms[PAD_BUFFER_DISPLAY_CHAR]) { conol("\r\n"); conol(x25obuf); conol("\r\n"); } else {} } else { x25obuf [obufl++] = c; if (obufl == MAXOX25) tosend = 1; else if (c == CR) tosend = 1; } if (tosend) { if (ttol((CHAR *)x25obuf,obufl) < 0) { perror ("\r\nCan't send characters"); active = 0; } else { bzero (x25obuf,sizeof(x25obuf)); obufl = 0; tosend = 0; } } else {}; } else { #endif /* SUNX25 */ if (c == '\015') { /* Carriage Return */ int stuff = -1; if (tnlm) { /* TERMINAL NEWLINE ON */ stuff = LF; /* Stuff LF */ #ifdef TNCODE } else if (network && /* TELNET NEWLINE */ IS_TELNET()) { switch (!TELOPT_ME(TELOPT_BINARY) ? tn_nlm : tn_b_nlm ) { case TNL_CRLF: stuff = LF; break; case TNL_CRNUL: stuff = NUL; break; } #endif /* TNCODE */ } if (stuff > -1) { ttoc(dopar('\015')); /* Send CR */ if (duplex) conoc('\015'); /* Echo CR */ c = stuff; /* Char to stuff */ csave = c; } } #ifdef TNCODE /* If user types the 0xff character (TELNET IAC), it must be doubled. */ else /* Not CR */ if ((dopar((CHAR) c) == IAC) && /* IAC (0xff) */ network && IS_TELNET()) { /* Send one copy now */ /* and the other one just below. */ ttoc((char)IAC); } #endif /* TNCODE */ /* Send the character */ if (ttoc((char)dopar((CHAR) c)) > -1) { if (duplex) { /* If half duplex, must echo */ if (debses) conol(dbchr(csave)); /* original char */ else /* not the translated one */ conoc((char)csave); if (seslog) { /* And maybe log it too */ c2 = csave; if (sessft == 0 && csave == '\r') c2 = '\n'; logchar((char)c2); } } } else { perror("\r\nCan't send character"); active = 0; } #ifdef SUNX25 } #endif /* SUNX25 */ } /* for... */ } } /* now active == 0 */ signal(CK_FORK_SIG, SIG_IGN); /* Turn off CK_FORK_SIG */ sjval = CEV_NO; /* Set to hangup */ } /* Come here on termination of child */ /* cklongjmp() executed in pipeint() (parent only!) comes here */ /* Now the child fork is gone or is waiting for CK_FORK_SIG in ck_sndmsg(). So we can't get (in the parent) any subsequent CK_FORK_SIG signals until we signal the child with CK_FORK_SIG. */ debug(F100,"CONNECT signaling port fork","",0); signal(CK_FORK_SIG, SIG_IGN); /* Turn this off */ debug(F101,"CONNECT killing port fork","",pid); if (pid) { int x = 0; #ifdef BEOSORBEBOX { long ret_val; x = kill(pid,SIGKILLTHR); /* Kill lower fork */ wait_for_thread(pid, &ret_val); } #else #ifdef Plan9 x = kill(pid,SIGKILL); /* Kill lower fork */ #else x = kill(pid,9); #endif /* Plan9 */ #endif /* BEOSORBEBOX */ wait((WAIT_T *)0); /* Wait till gone. */ if (x < 0) { printf("WARNING: Failure to kill fork, pid %d: %s, errno=%d\n", (int) pid, ck_errstr(), errno); debug(F111,"CONNECT error killing pid",ck_errstr(),errno); } debug(F101,"CONNECT killed port fork","",pid); pid = (PID_T) 0; } if (sjval == CEV_HUP) { /* Read error on comm device */ dohangup = 1; /* so we want to hang up our side */ #ifdef NETCONN if (network) { /* and/or close network connection */ ttclos(0); dologend(); #ifdef SUNX25 if (nettype == NET_SX25) /* If X.25, restore the PAD params */ initpad(); #endif /* SUNX25 */ } #endif /* NETCONN */ } #ifdef CK_APC if (sjval == CEV_APC) { /* Application Program Command rec'd */ apcactive = APC_REMOTE; /* Flag APC as active */ active = 0; /* Flag CONNECT as inactive */ } #endif /* CK_APC */ conres(); /* Reset the console. */ if (dohangup > 0) { /* If hangup requested, do that. */ #ifndef NODIAL if (dohangup > 1) /* User asked for it */ if (mdmhup() < 1) /* Maybe hang up via modem */ #endif /* NODIAL */ tthang(); /* And make sure we don't hang up */ dohangup = 0; /* again unless requested again. */ } #ifdef COMMENT #ifdef NETCONN #ifdef SIGPIPE if (network && sigpiph) /* Restore previous SIGPIPE handler */ (VOID) signal(SIGPIPE, sigpiph); #endif /* SIGPIPE */ #endif /* NETCONN */ #endif /* COMMENT */ #ifdef ANYX25 if (dox25clr) { /* If X.25 Clear requested */ x25clear(); /* do that. */ #ifndef IBMX25 initpad(); #endif /* IBMX25 */ dox25clr = 0; /* But only once. */ } #endif /* ANYX25 */ if (quitnow) doexit(GOOD_EXIT,xitsta); /* Exit now if requested. */ if (msgflg) printf("(Back at %s)", *myhost ? myhost : "local UNIX system"); #ifdef CK_APC if (!apcactive) #endif /* CK_APC */ printf("\n"); what = W_NOTHING; /* So console modes set right. */ #ifndef NOCSETS language = langsv; /* Restore language */ #endif /* NOCSETS */ parent_id = (PID_T) 0; goto conret1; } #ifndef BEOSORBEBOX else { /* *** */ /* Inferior reads, prints port input */ concld(/* (void *)&pid */); } #endif /* BEOSORBEBOX */ conret1: conret = 1; conret0: signal(CK_FORK_SIG, SIG_IGN); /* In case this wasn't done already */ debug(F101,"CONNECT conect exit ibc","",ibc); debug(F101,"CONNECT conect exit obc","",obc); close(xpipe[0]); xpipe[0] = -1; /* Close the pipe */ close(xpipe[1]); xpipe[1] = -1; if (msgflg) { #ifdef CK_APC if (apcactive == APC_LOCAL) printf("\n"); #endif /* CK_APC */ printf("----------------------------------------------------\r\n"); } fflush(stdout); return(conret); } /* H C O N N E -- Give help message for connect. */ int hconne() { int c; static char *hlpmsg[] = { "\r\n ? for this message", "\r\n 0 (zero) to send a null", "\r\n B to send a BREAK", #ifdef CK_LBRK "\r\n L to send a Long BREAK", #endif /* CK_LBRK */ #ifdef NETCONN "\r\n I to send a network interrupt packet", #ifdef TCPSOCKET "\r\n A to send Are You There?", #endif /* TCPSOCKET */ #ifdef ANYX25 "\r\n R to reset X.25 virtual circuit", #endif /* ANYX25 */ #endif /* NETCONN */ "\r\n U to hangup and close the connection", "\r\n Q to hangup and quit Kermit", "\r\n S for status", #ifdef NOPUSH "\r\n ! to push to local shell (disabled)", "\r\n Z to suspend (disabled)", #else "\r\n ! to push to local shell", #ifdef NOJC "\r\n Z to suspend (disabled)", #else "\r\n Z to suspend", #endif /* NOJC */ #endif /* NOPUSH */ "\r\n \\ backslash code:", "\r\n \\nnn decimal character code", "\r\n \\Onnn octal character code", "\r\n \\Xhh hexadecimal character code", "\r\n terminate with carriage return.", "\r\n Type the escape character again to send the escape character, or", "\r\n press the space-bar to resume the CONNECT command.\r\n", "" }; conol("\r\n----------------------------------------------------"); conol("\r\nPress C to return to "); conol(*myhost ? myhost : "the C-Kermit prompt"); conol(", or:"); conola(hlpmsg); /* Print the help message. */ conol("Command>"); /* Prompt for command. */ c = CONGKS() & 0177; /* Get character, strip any parity. */ /* No key mapping or translation here */ if (c != CMDQ) conoll(""); conoll("----------------------------------------------------"); return(c); /* Return it. */ } /* D O E S C -- Process an escape character argument */ VOID #ifdef CK_ANSIC doesc(char c) #else doesc(c) char c; #endif /* CK_ANSIC */ /* doesc */ { CHAR d; debug(F101,"CONNECT doesc","",c); while (1) { if (c == escape) { /* Send escape character */ d = dopar((CHAR) c); ttoc((char) d); return; } else /* Or else look it up below. */ if (isupper(c)) c = tolower(c); switch(c) { case 'c': /* Escape back to prompt */ case '\03': active = 0; conol("\r\n"); return; case 'b': /* Send a BREAK signal */ case '\02': ttsndb(); return; #ifdef NETCONN case 'i': /* Send Interrupt */ case '\011': #ifdef TCPSOCKET #ifndef IP #define IP 244 #endif /* IP */ if (network && IS_TELNET()) { /* TELNET */ temp[0] = (CHAR) IAC; /* I Am a Command */ temp[1] = (CHAR) IP; /* Interrupt Process */ temp[2] = NUL; ttol((CHAR *)temp,2); } else #endif /* TCPSOCKET */ #ifdef SUNX25 if (network && (nettype == NET_SX25)) { (VOID) x25intr(0); /* X.25 interrupt packet */ conol("\r\n"); } else #endif /* SUNX25 */ conoc(BEL); return; #ifdef TCPSOCKET case 'a': /* "Are You There?" */ case '\01': #ifndef AYT #define AYT 246 #endif /* AYT */ if (network && IS_TELNET()) { temp[0] = (CHAR) IAC; /* I Am a Command */ temp[1] = (CHAR) AYT; /* Are You There? */ temp[2] = NUL; ttol((CHAR *)temp,2); } else conoc(BEL); return; #endif /* TCPSOCKET */ #endif /* NETCONN */ #ifdef CK_LBRK case 'l': /* Send a Long BREAK signal */ ttsndlb(); return; #endif /* CK_LBRK */ case 'u': /* Hangup */ /* case '\010': */ /* No, too dangerous */ #ifdef ANYX25 if (network && (nettype == NET_SX25 || nettype == NET_IX25)) dox25clr = 1; else #endif /* ANYX25 */ dohangup = 2; active = 0; conol("\r\nHanging up "); return; #ifdef ANYX25 case 'r': /* Reset the X.25 virtual circuit */ case '\022': if (network && (nettype == NET_SX25 || nettype == NET_IX25)) (VOID) x25reset(0,0); conol("\r\n"); return; #endif /* ANYX25 */ case 'q': /* Quit */ dohangup = 2; quitnow = 1; active = 0; conol("\r\n"); return; case 's': /* Status */ conoll(""); conoll("----------------------------------------------------"); #ifdef NETCMD if (ttpipe) ckmakmsg(temp,TMPLEN," Pipe: \"",ttname,"\"",NULL); else #endif /* NETCMD */ ckmakmsg(temp, TMPLEN, " ", (network ? "Host" : "Device"), ": ", ttname ); conoll(temp); if (!network && speed >= 0L) { sprintf(temp,"Speed %ld", speed); conoll(temp); } sprintf(temp," Terminal echo: %s", duplex ? "local" : "remote"); conoll(temp); sprintf(temp," Terminal bytesize: %d", (cmask == 0177) ? 7 : 8); conoll(temp); sprintf(temp," Command bytesize: %d", (cmdmsk == 0177) ? 7 : 8 ); conoll(temp); if (hwparity) sprintf(temp," Parity[hardware]: %s",parnam((char)hwparity)); else sprintf(temp," Parity: %s", parnam((char)parity)); conoll(temp); sprintf(temp," Autodownload: %s", autodl ? "on" : "off"); conoll(temp); ckmakmsg(temp, /* (would not be safe for sprintf) */ TMPLEN, " Session log: ", *sesfil ? sesfil : "(none)", NULL, NULL ); conoll(temp); #ifndef NOSHOW if (!network) shomdm(); #endif /* NOSHOW */ #ifdef CKLOGDIAL { long z; z = dologshow(0); if (z > -1L) { sprintf(temp," Elapsed time: %s",hhmmss(z)); conoll(temp); } } #endif /* CKLOGDIAL */ conoll("----------------------------------------------------"); return; case 'h': /* Help */ case '?': /* Help */ c = hconne(); continue; case '0': /* Send a null */ c = '\0'; d = dopar((CHAR) c); ttoc((char) d); return; case 'z': case '\032': /* Suspend */ #ifndef NOPUSH if (!nopush) stptrap(0); else conoc(BEL); #else conoc(BEL); #endif /* NOPUSH */ return; case '@': /* Start inferior command processor */ case '!': #ifndef NOPUSH if (!nopush) { conres(); /* Put console back to normal */ zshcmd(""); /* Fork a shell. */ if (conbin((char)escape) < 0) { printf("Error resuming CONNECT session\n"); active = 0; } } else conoc(BEL); #else conoc(BEL); #endif /* NOPUSH */ return; case SP: /* Space, ignore */ return; default: /* Other */ if (c == CMDQ) { /* Backslash escape */ int x; ecbp = ecbuf; *ecbp++ = c; while (((c = (CONGKS() & cmdmsk)) != '\r') && (c != '\n')) *ecbp++ = c; *ecbp = NUL; ecbp = ecbuf; x = xxesc(&ecbp); /* Interpret it */ if (x >= 0) { /* No key mapping here */ c = dopar((CHAR) x); ttoc((char) c); return; } else { /* Invalid backslash code. */ conoc(BEL); return; } } conoc(BEL); return; /* Invalid esc arg, beep */ } } } #endif /* NOLOCAL */ ckudia.c0000644000015300001460000072550511266107527011312 0ustar fdckermit#include "ckcsym.h" char *dialv = "Dial Command, 9.0.160, 16 Oct 2009"; /* C K U D I A -- Module for automatic modem dialing. */ /* Copyright (C) 1985, 2009, Trustees of Columbia University in the City of New York. All rights reserved. See the C-Kermit COPYING.TXT file or the copyright text in the ckcmai.c module for disclaimer and permissions. */ /* Authors: Original (version 1, 1985) author: Herm Fischer, Encino, CA. Contributed to Columbia University in 1985 for inclusion in C-Kermit 4.0. Author and maintainer since 1985: Frank da Cruz, Columbia University, fdc@columbia.edu. Contributions by many others throughout the years, including: Jeffrey Altman, Mark Berryman, Fernando Cabral, John Chmielewski, Joe Doupnik, Richard Hill, Larry Jacobs, Eric Jones, Tom Kloos, Bob Larson, Peter Mauzey, Joe Orost, Kevin O'Gorman, Kai Uwe Rommel, Dan Schullman, Warren Tucker, and many others. */ /* Entry points: ckdial(char * number) Dial a number or answer a call dialhup() Hang up a dialed connection mdmhup() Use modem commands to hang up All other routines are static. Don't call dialhup() or mdmhup() without first calling ckdial(). */ /* This module calls externally defined system-dependent functions for communications i/o, as described in CKCPLM.DOC, the C-Kermit Program Logic Manual, and thus should be portable to all systems that implement those functions, and where alarm() and signal() work. HOW TO ADD SUPPORT FOR ANOTHER MODEM: 1. In ckuusr.h, define a modem-type number symbol (n_XXX) for the new modem, the next highest one. 2. In ckuusr.h, adjust MAX_MDM to the new number of modem types. The remaining steps are in this module: 3. Create a MDMINF structure for it. NOTE: The wake_str should include all invariant setup info, e.g. enable result codes, BREAK transparency, modulation negotiation, etc. See ckcker.h for MDMINF struct definition. 4. Add the address of the MDMINF structure to the modemp[] array, according to the numerical value of the modem-type number. 5. Add the user-visible (SET MODEM) name and corresponding modem number to the mdmtab[] array, in alphabetical order by modem-name string. 6. If this falls into a class like is_rockwell, is_supra, etc, add the new one to the definition of the class. 7. Adjust the gethrn() routine to account for any special numeric result codes (if it's a Hayes compatible modem). 8. Read through the code and add any modem-specific sections as necessary. For most modern Hayes-compatible modems, no specific code will be needed. NOTE: The MINIDIAL symbol is used to build this module to include support for only a minimum number of standard and/or generally useful modem types, namely Hayes 1200 and 2400, ITU-T (CCITT) V.25bis and V.25ter (V.250), Generic-High-Speed, "Unknown", and None. When adding support for a new modem type, keep it outside of the MINIDIAL sections unless it deserves to be in it. */ #include "ckcdeb.h" #ifndef NOLOCAL #ifndef NODIAL #ifndef NOICP #ifndef CK_ATDT #define CK_ATDT #endif /* CK_ATDT */ #ifndef NOOLDMODEMS /* Unless instructed otherwise, */ #define OLDMODEMS /* keep support for old modems. */ #endif /* NOOLDMODEMS */ #ifndef M_OLD /* Hide old modem keywords in SET MODEM table. */ #define M_OLD 0 /* Define as CM_INV to make them invisible. */ #endif /* M_OLD */ #ifndef M_ALIAS #define M_ALIAS 64 #endif /* M_ALIAS */ #ifndef MAC #include #endif /* MAC */ #include "ckcasc.h" #include "ckcker.h" #include "ckucmd.h" #include "ckcnet.h" #include "ckuusr.h" #ifdef OS2ONLY #define INCL_VIO /* Needed for ckocon.h */ #include #undef COMMENT #include "ckocon.h" #endif /* OS2ONLY */ #ifdef NT #include #include #include "cknwin.h" #include "ckntap.h" #endif /* NT */ #ifdef OS2 #include "ckowin.h" #endif /* OS2 */ #ifndef ZILOG #ifdef NT #include #else /* NT */ #include #endif /* NT */ #else #include #endif /* ZILOG */ #include "ckcsig.h" /* C-Kermit signal processing */ #ifdef MAC #define signal msignal #define SIGTYP long #define alarm malarm #define SIG_IGN 0 #define SIGALRM 1 #define SIGINT 2 SIGTYP (*msignal(int type, SIGTYP (*func)(int)))(int); #endif /* MAC */ #ifdef AMIGA #define signal asignal #define alarm aalarm #define SIGALRM (_NUMSIG+1) #define SIGTYP void SIGTYP (*asignal(int type, SIGTYP (*func)(int)))(int); unsigned aalarm(unsigned); #endif /* AMIGA */ #ifdef STRATUS /* VOS doesn't have alarm(), but it does have some things we can work with. However, we have to catch all the signals in one place to do this, so we intercept the signal() routine and call it from our own replacement. */ #define signal vsignal #define alarm valarm SIGTYP (*vsignal(int type, SIGTYP (*func)(int)))(int); int valarm(int interval); #ifdef putchar #undef putchar #endif /* putchar */ #define putchar(x) conoc(x) #ifdef getchar #undef getchar #endif /* getchar */ #define getchar(x) coninc(0) #endif /* STRATUS */ #ifdef OS2 #ifdef putchar #undef putchar #endif /* putchar */ #define putchar(x) conoc(x) #endif /* OS2 */ #ifndef NOHINTS extern int hints; #endif /* NOHINTS */ #ifdef CK_TAPI extern int tttapi; extern int tapipass; #endif /* CK_TAPI */ #ifdef CKLOGDIAL extern int dialog; #endif /* CKLOGDIAL */ char * dialmsg[] = { /* DIAL status strings */ /* Keyed to numbers defined in ckcker.h -- keep in sync! */ "DIAL succeeded", /* 0 DIA_OK */ "Modem type not specified", /* 1 DIA_NOMO */ "Communication device not specified", /* 2 DIA_NOLI */ "Communication device can't be opened", /* 3 DIA_OPEN */ "Speed not specified", /* 4 DIA_NOSP */ "Pre-DIAL hangup failed", /* 5 DIA_HANG */ "Internal error", /* 6 DIA_IE */ "Device input/output error", /* 7 DIA_IO */ "DIAL TIMEOUT expired", /* 8 DIA_TIMO */ "Interrupted by user", /* 9 DIA_INTR */ "Modem not ready", /* 10 DIA_NRDY */ "Partial dial OK", /* 11 DIA_PART */ "Dial directory lookup error", /* 12 DIA_DIR */ "Hangup OK", /* 13 DIA_HUP */ NULL, /* 14 (undef) */ NULL, /* 15 (undef) */ NULL, /* 16 (undef) */ NULL, /* 17 (undef) */ NULL, /* 18 (undef) */ "No response from modem", /* 19 DIA_NRSP */ "Modem command error", /* 20 DIA_ERR */ "Failure to initialize modem", /* 21 DIA_NOIN */ "Phone busy", /* 22 DIA_BUSY */ "No carrier", /* 23 DIA_NOCA */ "No dialtone", /* 24 DIA_NODT */ "Incoming call", /* 25 DIA_RING */ "No answer", /* 26 DIA_NOAN */ "Disconnected", /* 27 DIA_DISC */ "Answered by voice", /* 28 DIA_VOIC */ "Access denied / forbidden call", /* 29 DIA_NOAC */ "Blacklisted", /* 30 DIA_BLCK */ "Delayed", /* 31 DIA_DELA */ "Fax connection", /* 32 DIA_FAX */ "Digital line", /* 33 DIA_DIGI */ "TAPI dialing failure", /* 34 DIA_TAPI */ NULL /* 34 */ }; #ifdef COMMENT #ifdef NOSPL static #endif /* NOSPL */ char modemmsg[128] = { NUL, NUL }; /* DIAL response from modem */ #endif /* COMMENT */ #ifdef NTSIG extern int TlsIndex; #endif /* NTSIG */ int mdmtyp = n_GENERIC; /* Default modem type */ int mdmset = 0; /* User explicitly set a modem type */ int /* SET DIAL parameters */ dialhng = 1, /* DIAL HANGUP, default is ON */ dialdpy = 0, /* DIAL DISPLAY, default is OFF */ mdmspd = 0, /* DIAL SPEED-MATCHING (0 = OFF) */ mdmspk = 1, /* MODEM SPEAKER */ mdmvol = 2, /* MODEM VOLUME */ dialtmo = 0, /* DIAL TIMEOUT */ dialatmo = -1, /* ANSWER TIMEOUT */ dialksp = 0, /* DIAL KERMIT-SPOOF, 0 = OFF */ dialidt = 0, /* DIAL IGNORE-DIALTONE */ #ifndef CK_RTSCTS /* If we can't do RTS/CTS then there's no flow control at first. */ /* So we might easily lose the echo to the init string and the OK */ /* and then give "No response from modem" errors. */ dialpace = 150, /* DIAL PACING */ #else dialpace = -1, #endif /* CK_RTSCTS */ /* 0 = RS232 (drop DTR); 1 = MODEM-COMMAND (e.g. +++ATH0) */ dialmhu = DEFMDMHUP; /* MODEM HANGUP-METHOD */ int dialec = 1, /* DIAL ERROR-CORRECTION */ dialdc = 1, /* DIAL COMPRESSION */ #ifdef VMS /* VMS can only use Xon/Xoff */ dialfc = FLO_XONX, /* DIAL FLOW-CONTROL */ #else dialfc = FLO_AUTO, #endif /* VMS */ dialmth = XYDM_D, /* DIAL METHOD (Tone, Pulse, Defalt) */ dialmauto = 1, /* DIAL METHOD is AUTO */ dialesc = 0; /* DIAL ESCAPE */ int telephony = 0; /* Command-line '-T' option */ long dialmax = 0L, /* Modem's max interface speed */ dialcapas = 0L; /* Modem's capabilities */ int dialsta = DIA_UNK; /* Detailed return code (ckuusr.h) */ #ifdef COMMENT int ans_cid = 0; /* SET ANSWER parameters */ int ans_rings = 0; /* (not used yet...) */ #endif /* COMMENT */ int is_rockwell = 0; int is_motorola = 0; int is_supra = 0; int is_hayeshispd = 0; /* Dialing directory list */ char *dialdir[MAXDDIR]; /* DIAL DIRECTORY filename array */ int ndialdir = 0; /* How many dial directories */ /* User overrides for built-in modem commands */ char *dialini = NULL; /* MODEM INIT-STRING none */ char *dialmstr = NULL; /* MODEM DIALMODE-STRING */ char *dialmprmt = NULL; /* MODEM DIALMODE-PROMPT */ char *dialcmd = NULL; /* MODEM DIAL-COMMAND, default none */ char *dialname = NULL; /* Descriptive name for modem */ char *dialdcon = NULL; /* DC ON command */ char *dialdcoff = NULL; /* DC OFF command */ char *dialecon = NULL; /* EC ON command */ char *dialecoff = NULL; /* EC OFF command */ char *dialaaon = NULL; /* Autoanswer ON command */ char *dialaaoff = NULL; /* Autoanswer OFF command */ char *dialhcmd = NULL; /* Hangup command */ char *dialhwfc = NULL; /* Hardware flow control command */ char *dialswfc = NULL; /* (Local) software f.c. command */ char *dialnofc = NULL; /* No (Local) flow control command */ char *dialtone = NULL; /* Command to force tone dialing */ char *dialpulse = NULL; /* ..to force pulse dialing */ char *dialx3 = NULL; /* Ignore dialtone */ char *mdmname = NULL; char *dialspon = NULL; /* Speaker On command */ char *dialspoff = NULL; /* Speaker Off command */ char *dialvol1 = NULL; /* Volume Low command */ char *dialvol2 = NULL; /* Volume Medium command */ char *dialvol3 = NULL; /* Volume High command */ char *dialini2 = NULL; /* Second init string */ /* Phone number options */ char *dialnpr = NULL; /* DIAL PREFIX, ditto */ char *diallac = NULL; /* DIAL LOCAL-AREA-CODE, ditto */ char *diallcc = NULL; /* DIAL LOCAL-COUNTRY-CODE, ditto */ char *dialixp = NULL; /* DIAL INTL-PREFIX */ char *dialixs = NULL; /* DIAL INTL-SUFFIX */ char *dialldp = NULL; /* DIAL LD-PREFIX */ char *diallds = NULL; /* DIAL LD-SUFFIX */ char *diallcp = NULL; /* DIAL LOCAL-PREFIX */ char *diallcs = NULL; /* DIAL LOCAL-SUFFIX */ char *dialpxi = NULL; /* DIAL PBX-INTERNAL-PREFIX */ char *dialpxo = NULL; /* DIAL PBX-OUTSIDE-PREFIX */ char *dialsfx = NULL; /* DIAL SUFFIX */ char *dialtfp = NULL; /* DIAL TOLL-FREE-PREFIX */ char *callid_date = NULL; /* Caller ID strings */ char *callid_time = NULL; char *callid_name = NULL; char *callid_nmbr = NULL; char *callid_mesg = NULL; extern char * d_name; extern char * dialtfc[]; /* DIAL TOLL-FREE-AREA-CODE */ extern char * dialpxx[]; /* DIAL PBX-EXCHANGE */ extern int ntollfree; extern int ndialpxx; extern char * dialpucc[]; /* DIAL Pulse countries */ extern int ndialpucc; extern char * dialtocc[]; /* DIAL Tone countries */ extern int ndialtocc; char *dialmac = NULL; /* DIAL macro */ /* Countries where pulse dialing must be used (tone is not available) */ static char * pulsecc[] = { NULL }; /* (Unknown at present) */ /* Countries where tone dialing may safely be the default. */ /* "+" marks countries where pulse is also allowed. */ /* Both Pulse and Tone are allowed in Austria & Switzerland but it is not */ /* yet known if Tone is universally in those countries. */ static char * tonecc[] = { "1", /* + North American Numbering Plan */ "31", /* Netherlands */ "32", /* Belgium */ "33", /* France */ "352", /* Luxembourg */ "353", /* Ireland */ "354", /* Iceland */ "358", /* Finland */ "39", /* Italy */ "44", /* + UK */ "45", /* Denmark */ "46", /* Sweden */ "47", /* Norway */ "49", /* + Germany */ NULL }; #ifndef MINIDIAL /* Telebit model codes: ATI Model Numbers Examples --- ------------- -------- 123 Telebit in "total Hayes-1200" emulation mode 960 Telebit in Conventional Command (Hayes) mode 961 RA12C IBM PC internal original Trailblazer 962 RA12E External original Trailblazer 963 RM12C Rackmount original Trailblazer 964 T18PC IBM PC internal Trailblazer-Plus (TB+) 965 T18SA, T2SAA, T2SAS External TB+, T1600, T2000, T3000, WB, and later 966 T18RMM Rackmount TB+ 967 T2MC IBM PS/2 internal TB+ 968 T1000 External T1000 969 ? Qblazer 970 Qblazer Plus 971 T2500 External T2500 972 T2500 Rackmount T2500 */ /* Telebit model codes */ #define TB_UNK 0 /* Unknown Telebit model */ #define TB_BLAZ 1 /* Original TrailBlazer */ #define TB_PLUS 2 /* TrailBlazer Plus */ #define TB_1000 3 /* T1000 */ #define TB_1500 4 /* T1500 */ #define TB_1600 5 /* T1600 */ #define TB_2000 6 /* T2000 */ #define TB_2500 7 /* T2500 */ #define TB_3000 8 /* T3000 */ #define TB_QBLA 9 /* Qblazer */ #define TB_WBLA 10 /* WorldBlazer */ #define TB__MAX 10 /* Highest number */ char *tb_name[] = { /* Array of model names */ "Unknown", /* TB_UNK */ "TrailBlazer", /* TB_BLAZ */ "TrailBlazer-Plus", /* TB_PLUS */ "T1000", /* TB_1000 */ "T1500", /* TB_1500 */ "T1600", /* TB_1600 */ "T2000", /* TB_2000 */ "T2500", /* TB_2500 */ "T3000", /* TB_3000 */ "Qblazer", /* TB_QBLA */ "WorldBlazer", /* TB_WBLA */ "" }; #endif /* MINIDIAL */ extern int flow, local, mdmtyp, quiet, backgrd, parity, seslog, network; extern int carrier, duplex, mdmsav, reliable, setreliable; extern int ttnproto, nettype; extern long speed; extern char ttname[], sesfil[]; #ifndef NOXFER extern CHAR stchr; extern int interrupted; #endif /* NOXFER */ /* Failure codes */ #define F_TIME 1 /* timeout */ #define F_INT 2 /* interrupt */ #define F_MODEM 3 /* modem-detected failure */ #define F_MINIT 4 /* cannot initialize modem */ #ifndef CK_TAPI static #endif /* CK_TAPI */ #ifdef OS2 volatile #endif /* OS2 */ int fail_code = 0; /* Default failure reason. */ static int xredial = 0; static int func_code; /* 0 = dialing, nonzero = answering */ static int partial; static int mymdmtyp = 0; #define DW_NOTHING 0 /* What we are doing */ #define DW_INIT 1 #define DW_DIAL 2 static int dial_what = DW_NOTHING; /* Nothing at first. */ static int nonverbal = 0; /* Hayes in numeric response mode */ static MDMINF * mp; static CHAR escbuf[6]; static long mdmcapas; _PROTOTYP (static VOID dreset, (void) ); _PROTOTYP (static int (*xx_ok), (int,int) ); _PROTOTYP (static int ddinc, (int) ); _PROTOTYP (int dialhup, (void) ); _PROTOTYP (int getok, (int,int) ); _PROTOTYP (char * ck_time, (void) ); _PROTOTYP (static VOID ttslow, (char *, int) ); #ifdef COMMENT _PROTOTYP (static VOID xcpy, (char *, char *, unsigned int) ); #endif /* COMMENT */ _PROTOTYP (static VOID waitfor, (char *) ); _PROTOTYP (static VOID dialoc, (char) ); _PROTOTYP (static int didweget, (char *, char *) ); _PROTOTYP (static VOID spdchg, (long) ); _PROTOTYP (static int dialfail, (int) ); _PROTOTYP (static VOID gethrw, (void) ); _PROTOTYP (static VOID gethrn, (void) ); int dialudt = n_UDEF; /* Number of user-defined type */ /* BEGIN MDMINF STRUCT DEFINITIONS */ /* Declare structures containing modem-specific information. REMEMBER that only the first SEVEN characters of these names are guaranteed to be unique. First declare the three types that are allowed for MINIDIAL versions. */ static MDMINF CCITT = /* CCITT / ITU-T V.25bis autodialer */ /* According to V.25bis: . Even parity is required for giving commands to the modem. . Commands might or might not echo. . Responses ("Indications") from the modem are terminated by CR and LF. . Call setup is accomplished by: - DTE raises DTR (V.24 circuit 108) [ttopen() does this] - Modem raises CTS (V.24 circuit 106) [C-Kermit ignores this] - DTE issues a call request command ("CRN") - Modem responds with "VAL" ("command accepted") - If the call is completed: modem responds with "CNX" ("call connected"); modem turns CTS (106) OFF; modem turns DSR (107) ON; else: modem responds with "CFI " ("call failure indication"). . To clear a call, the DTE turns DTR (108) OFF. . There is no mention of the Carrier Detect circuit (109) in the standard. . There is no provision for "escaping back" to the modem's command mode. It is not known whether there exists in real life a pure V.25bis modem. If there is, this code has never been tested on it. See the Digitel entry. */ { "Any CCITT / ITU-T V.25bis conformant modem", "", /* pulse command */ "", /* tone command */ 40, /* dial_time -- programmable -- */ ",:", /* pause_chars -- "," waits for programmable time */ /* ":" waits for dial tone */ 10, /* pause_time (seconds, just a guess) */ "", /* wake_str (none) */ 200, /* wake_rate (msec) */ "VAL", /* wake_prompt */ "", /* dmode_str (none) */ "", /* dmode_prompt (none) */ "CRN%s\015", /* dial_str */ 200, /* dial_rate (msec) */ 0, /* No esc_time */ 0, /* No esc_char */ "", /* No hup_str */ "", /* hwfc_str */ "", /* swfc_str */ "", /* nofc_str */ "", /* ec_on_str */ "", /* ec_off_str */ "", /* dc_on_str */ "", /* dc_off_str */ "CIC\015", /* aa_on_str */ "DIC\015", /* aa_off_str */ "", /* sb_on_str */ "", /* sb_off_str */ "", /* sp_off_str */ "", /* sp_on_str */ "", /* vol1_str */ "", /* vol2_str */ "", /* vol3_str */ "", /* ignoredt */ "", /* ini2 */ 0L, /* max_speed */ CKD_V25, /* capas */ NULL /* No ok_fn */ }; static MDMINF HAYES = /* Hayes 2400 and compatible modems */ { "Hayes Smartmodem 2400 and compatibles", "ATP\015", /* pulse command */ "ATT\015", /* tone command */ 35, /* dial_time */ ",", /* pause_chars */ 2, /* pause_time */ #ifdef OS2 "ATE1Q0V1&S0&C1&D2\015", /* wake_str */ #else #ifdef VMS "ATQ0&S1\015", /* wake_str */ #else "ATQ0\015", /* wake_str */ #endif /* VMS */ #endif /* OS2 */ 0, /* wake_rate */ "OK\015", /* wake_prompt */ "", /* dmode_str */ "", /* dmode_prompt */ "ATD%s\015", /* dial_str, user supplies D or T */ 0, /* dial_rate */ 1100, /* esc_time */ 43, /* esc_char */ "ATQ0H0\015", /* hup_str */ "", /* hwfc_str */ "", /* swfc_str */ "", /* nofc_str */ "", /* ec_on_str */ "", /* ec_off_str */ "", /* dc_on_str */ "", /* dc_off_str */ "ATS0=1\015", /* aa_on_str */ "ATS0=0\015", /* aa_off_str */ "", /* sb_on_str */ "", /* sb_off_str */ "ATM1\015", /* sp_on_str */ "ATM0\015", /* sp_off_str */ "ATL1\015", /* vol1_str */ "ATL2\015", /* vol2_str */ "ATL3\015", /* vol3_str */ "ATX3\015", /* ignoredt */ "", /* ini2 */ 2400L, /* max_speed */ CKD_AT, /* capas */ getok /* ok_fn */ }; /* The intent of the "unknown" modem is to allow KERMIT to support unknown modems by having the user type the entire autodial sequence (possibly including control characters, etc.) as the "phone number". The protocol and other characteristics of this modem are unknown, with some "reasonable" values being chosen for some of them. The only way to detect if a connection is made is to look for carrier. */ static MDMINF UNKNOWN = /* Information for "Unknown" modem */ { "Unknown", /* name */ "", /* pulse command */ "", /* tone command */ 30, /* dial_time */ "", /* pause_chars */ 0, /* pause_time */ "", /* wake_str */ 0, /* wake_rate */ "", /* wake_prompt */ "", /* dmode_str */ NULL, /* dmode_prompt */ "%s\015", /* dial_str */ 0, /* dial_rate */ 0, /* esc_time */ 0, /* esc_char */ "", /* hup_str */ "", /* hwfc_str */ "", /* swfc_str */ "", /* nofc_str */ "", /* ec_on_str */ "", /* ec_off_str */ "", /* dc_on_str */ "", /* dc_off_str */ "", /* aa_on_str */ "", /* aa_off_str */ "", /* sb_on_str */ "", /* sb_off_str */ "", /* sp_off_str */ "", /* sp_on_str */ "", /* vol1_str */ "", /* vol2_str */ "", /* vol3_str */ "", /* ignoredt */ "", /* ini2 */ 0L, /* max_speed */ 0, /* capas */ NULL /* ok_fn */ }; #ifndef MINIDIAL static MDMINF ATTISN = /* AT&T ISN Network */ { "", /* pulse command */ "", /* tone command */ "AT&T ISN Network", 30, /* Dial time */ "", /* Pause characters */ 0, /* Pause time */ "\015\015\015\015", /* Wake string */ 900, /* Wake rate */ "DIAL", /* Wake prompt */ "", /* dmode_str */ "", /* dmode_prompt */ "%s\015", /* dial_str */ 0, /* dial_rate */ 0, /* esc_time */ 0, /* esc_char */ "", /* hup_str */ "", /* hwfc_str */ "", /* swfc_str */ "", /* nofc_str */ "", /* ec_on_str */ "", /* ec_off_str */ "", /* dc_on_str */ "", /* dc_off_str */ "", /* aa_on_str */ "", /* aa_off_str */ "", /* sb_on_str */ "", /* sb_off_str */ "", /* sp_off_str */ "", /* sp_on_str */ "", /* vol1_str */ "", /* vol2_str */ "", /* vol3_str */ "", /* ignoredt */ "", /* ini2 */ 0L, /* max_speed */ 0, /* capas */ NULL /* ok_fn */ }; static MDMINF ATTMODEM = /* information for AT&T switched-network modems */ /* "Number" following "dial" can include: p's and * t's to indicate pulse or tone (default) dialing, * + for wait for dial tone, , for pause, r for * last number dialed, and, except for 2224B, some * comma-delimited options like o12=y, before number. * "Important" options for the modems: * * All: Except for 2224B, enable option 12 for "transparent * data," o12=y. If a computer port used for both * incoming and outgoing calls is connected to the * modem, disable "enter interactive mode on carriage * return," EICR. The Kermit "dial" command can * function with EIA leads standard, EIAS. * * 2212C: Internal hardware switches at their default * positions (four rockers down away from numbers) * unless EICR is not wanted (rocker down at the 4). * For EIAS, rocker down at the 1. * * 2224B: Front-panel switch position 1 must be up (at the 1, * closed). Disable EICR with position 2 down. * For EIAS, position 4 down. * All switches on the back panel down. * * 2224CEO: All front-panel switches down except either 5 or 6. * Enable interactive flow control with o16=y. * Select normal asynchronous mode with o34=0 (zero). * Disable EICR with position 3 up. For EIAS, 1 up. * Reset the modem after changing switches. * * 2296A: If option 00 (zeros) is present, use o00=0. * Enable interactive flow control with o16=y. * Select normal asynchronous mode with o34=0 (zero). * (available in Microcom Networking version, but * not necessarily other models of the 2296A). * Enable modem-port flow control (if available) with * o42=y. Enable asynchronous operation with o50=y. * Disable EICR with o69=n. For EIAS, o66=n, using * front panel. */ { "AT&T switched-network modems", "", /* pulse command */ "", /* tone command */ 20, /* dial_time */ ",", /* pause_chars */ 2, /* pause_time */ "+", /* wake_str */ 0, /* wake_rate */ "", /* wake_prompt */ "", /* dmode_str */ "", /* dmode_prompt */ "at%s\015", /* dial_str */ 0, /* dial_rate */ 0, /* esc_time */ 0, /* esc_char */ "", /* hup_str */ "", /* hwfc_str */ "", /* swfc_str */ "", /* nofc_str */ "", /* ec_on_str */ "", /* ec_off_str */ "", /* dc_on_str */ "", /* dc_off_str */ "", /* aa_on_str */ "", /* aa_off_str */ "", /* sb_on_str */ "", /* sb_off_str */ "", /* sp_off_str */ "", /* sp_on_str */ "", /* vol1_str */ "", /* vol2_str */ "", /* vol3_str */ "", /* ignoredt */ "", /* ini2 */ 0L, /* max_speed */ CKD_AT, /* capas */ NULL /* ok_fn */ }; static MDMINF ATTDTDM = /* AT&T Digital Terminal Data Module */ /* For dialing: KYBD switch down, others usually up. */ { "AT&T Digital Terminal Data Module", "", /* pulse command */ "", /* tone command */ 20, /* dial_time */ "", /* pause_chars */ 0, /* pause_time */ "", /* wake_str */ 0, /* wake_rate */ "", /* wake_prompt */ "", /* dmode_str */ "", /* dmode_prompt */ "%s\015", /* dial_str */ 0, /* dial_rate */ 0, /* esc_time */ 0, /* esc_char */ "", /* hup_str */ "", /* hwfc_str */ "", /* swfc_str */ "", /* nofc_str */ "", /* ec_on_str */ "", /* ec_off_str */ "", /* dc_on_str */ "", /* dc_off_str */ "", /* aa_on_str */ "", /* aa_off_str */ "", /* sb_on_str */ "", /* sb_off_str */ "", /* sp_off_str */ "", /* sp_on_str */ "", /* vol1_str */ "", /* vol2_str */ "", /* vol3_str */ "", /* ignoredt */ "", /* ini2 */ 0L, /* max_speed */ 0, /* capas */ NULL /* ok_fn */ }; static MDMINF DIGITEL = /* Digitel DT-22 CCITT variant used in Brazil */ /* Attempts to adhere strictly to the V.25bis specification do not produce good results in real life. The modem for which this code was developed: (a) ignores parity; (b) sometimes terminates responses with LF CR instead of CR LF; (c) has a Hayes-like escape sequence; (d) supports a hangup ("HUP") command. Information from Fernando Cabral in Brasilia. */ { "Digitel DT-22 CCITT dialer", "", /* pulse command */ "", /* tone command */ 40, /* dial_time -- programmable -- */ ",:", /* pause_chars -- "," waits for programmable time */ /* ":" waits for dial tone */ 10, /* pause_time (seconds, just a guess) */ "HUP\015", /* wake_str (Not Standard CCITT) */ 200, /* wake_rate (msec) */ "VAL", /* wake_prompt */ "", /* dmode_str (none) */ "", /* dmode_prompt (none) */ "CRN%s\015", /* dial_str */ 200, /* dial_rate (msec) */ 1100, /* esc_time (Not Standard CCITT) */ 43, /* esc_char (Not Standard CCITT) */ "HUP\015", /* hup_str (Not Standard CCITT) */ "", /* hwfc_str */ "", /* swfc_str */ "", /* nofc_str */ "", /* ec_on_str */ "", /* ec_off_str */ "", /* dc_on_str */ "", /* dc_off_str */ "CIC\015", /* aa_on_str */ "DIC\015", /* aa_off_str */ "", /* sb_on_str */ "", /* sb_off_str */ "", /* sp_off_str */ "", /* sp_on_str */ "", /* vol1_str */ "", /* vol2_str */ "", /* vol3_str */ "", /* ignoredt */ "", /* ini2 */ 0L, /* max_speed */ CKD_V25, /* capas */ getok /* ok_fn */ }; static MDMINF H_1200 = /* Hayes 1200 and compatible modems */ { "Hayes Smartmodem 1200 and compatibles", "ATP\015", /* pulse command */ "ATT\015", /* tone command */ 35, /* dial_time */ ",", /* pause_chars */ 2, /* pause_time */ #ifdef OS2 "ATE1Q0V1\015", /* wake_str */ #else "ATQ0\015", /* wake_str */ #endif /* OS2 */ 0, /* wake_rate */ "OK\015", /* wake_prompt */ "", /* dmode_str */ "", /* dmode_prompt */ "ATD%s\015", /* dial_str */ 0, /* dial_rate */ 1100, /* esc_time */ 43, /* esc_char */ "ATQ0H0\015", /* hup_str */ "", /* hwfc_str */ "", /* swfc_str */ "", /* nofc_str */ "", /* ec_on_str */ "", /* ec_off_str */ "", /* dc_on_str */ "", /* dc_off_str */ "ATS0=1\015", /* aa_on_str */ "ATS0=0\015", /* aa_off_str */ "", /* sb_on_str */ "", /* sb_off_str */ "ATM1\015", /* sp_on_str */ "ATM0\015", /* sp_off_str */ "ATL1\015", /* vol1_str */ "ATL2\015", /* vol2_str */ "ATL3\015", /* vol3_str */ "", /* ignoredt */ "", /* ini2 */ 1200L, /* max_speed */ CKD_AT, /* capas */ getok /* ok_fn */ }; static MDMINF H_ULTRA = /* Hayes high-speed */ { "Hayes Ultra/Optima/Accura 96/144/288", /* U,O,A */ "ATP\015", /* pulse command */ "ATT\015", /* tone command */ 35, /* dial_time */ ",", /* pause_chars */ 2, /* pause_time */ #ifdef OS2 "ATE1Q0V1X4N1Y0&S0&C1&D2S37=0S82=128\015", /* wake_str */ #else #ifdef VMS "ATQ0X4N1Y0&S1S37=0S82=128\015", /* wake_str */ #else "ATQ0X4N1Y0S37=0S82=128\015", /* wake_str */ #endif /* VMS */ #endif /* OS2 */ 0, /* wake_rate */ "OK\015", /* wake_prompt */ "", /* dmode_str */ "", /* dmode_prompt */ "ATD%s\015", /* dial_str */ 0, /* dial_rate */ 1100, /* esc_time */ 43, /* esc_char */ "ATQ0H0\015", /* hup_str */ "AT&K3\015", /* hwfc_str */ /* OK for U,O */ "AT&K4\015", /* swfc_str */ /* OK for U,O */ "AT&K0\015", /* nofc_str */ /* OK for U,O */ "AT&Q5S36=7S48=7\015", /* ec_on_str */ /* OK for U,O */ "AT&Q0\015", /* ec_off_str */ /* OK for U,O */ "ATS46=2\015", /* dc_on_str */ "ATS46=0\015", /* dc_off_str */ "ATS0=1\015", /* aa_on_str */ "ATS0=0\015", /* aa_off_str */ "", /* sb_on_str */ "", /* sb_off_str */ "ATM1\015", /* sp_on_str */ "ATM0\015", /* sp_off_str */ "ATL1\015", /* vol1_str */ "ATL2\015", /* vol2_str */ "ATL3\015", /* vol3_str */ "ATX3\015", /* ignoredt */ "", /* ini2 */ 115200L, /* max_speed */ /* (varies) */ CKD_AT|CKD_SB|CKD_EC|CKD_DC|CKD_HW|CKD_SW, /* capas */ getok /* ok_fn */ }; static MDMINF H_ACCURA = /* Hayes Accura */ { /* GUESSING IT'S LIKE ULTRA & OPTIMA */ "Hayes Accura", "ATP\015", /* pulse command */ "ATT\015", /* tone command */ 35, /* dial_time */ ",", /* pause_chars */ 2, /* pause_time */ #ifdef OS2 "ATE1Q0V1X4N1Y0&S0&C1&D2S37=0\015", /* wake_str */ #else #ifdef VMS "ATQ0X4N1Y0&S1S37=0\015", /* wake_str */ #else "ATQ0X4N1Y0S37=0\015", /* wake_str */ #endif /* VMS */ #endif /* OS2 */ 0, /* wake_rate */ "OK\015", /* wake_prompt */ "", /* dmode_str */ "", /* dmode_prompt */ "ATD%s\015", /* dial_str */ 0, /* dial_rate */ 1100, /* esc_time */ 43, /* esc_char */ "ATQ0H0\015", /* hup_str */ "AT&K3\015", /* hwfc_str */ "AT&K4\015", /* swfc_str */ "AT&K0\015", /* nofc_str */ "AT&Q5S36=7S48=7\015", /* ec_on_str */ "AT&Q0\015", /* ec_off_str */ "ATS46=2\015", /* dc_on_str */ "ATS46=0\015", /* dc_off_str */ "ATS0=1\015", /* aa_on_str */ "ATS0=0\015", /* aa_off_str */ "", /* sb_on_str */ "", /* sb_off_str */ "ATM1\015", /* sp_on_str */ "ATM0\015", /* sp_off_str */ "ATL1\015", /* vol1_str */ "ATL2\015", /* vol2_str */ "ATL3\015", /* vol3_str */ "ATX3\015", /* ignoredt */ "", /* ini2 */ 115200L, /* max_speed */ /* (varies) */ CKD_AT|CKD_SB|CKD_EC|CKD_DC|CKD_HW|CKD_SW, /* capas */ getok /* ok_fn */ }; static MDMINF PPI = /* Practical Peripherals */ { "Practical Peripherals V.22bis or higher with V.42 and V.42bis", "ATP\015", /* pulse command */ "ATT\015", /* tone command */ 35, /* dial_time */ ",", /* pause_chars */ 2, /* pause_time */ #ifdef COMMENT /* In newer models S82 (BREAK handling) was eliminated, causing an error. */ #ifdef OS2 "ATQ0X4N1&S0&C1&D2S37=0S82=128\015", /* wake_str */ #else "ATQ0X4N1S37=0S82=128\015", /* wake_str */ #endif /* OS2 */ #else /* So now we use Y0 instead */ #ifdef OS2 "ATE1Q0V1X4N1&S0&C1&D2Y0S37=0\015", /* wake_str */ #else #ifdef VMS "ATQ0X4N1Y0&S1S37=0\015", /* wake_str */ #else "ATQ0X4N1Y0S37=0\015", /* wake_str */ #endif /* VMS */ #endif /* OS2 */ #endif /* COMMENT */ 0, /* wake_rate */ "OK\015", /* wake_prompt */ "", /* dmode_str */ "", /* dmode_prompt */ "ATD%s\015", /* dial_str */ 0, /* dial_rate */ 1100, /* esc_time */ 43, /* esc_char */ "ATQ0H0\015", /* hup_str */ "AT&K3\015", /* hwfc_str */ "AT&K4\015", /* swfc_str */ "AT&K0\015", /* nofc_str */ "AT&Q5S36=7S48=7\015", /* ec_on_str */ "AT&Q0S36=0S48=128\015", /* ec_off_str */ "ATS46=2\015", /* dc_on_str */ "ATS46=0\015", /* dc_off_str */ "ATS0=1\015", /* aa_on_str */ "ATS0=0\015", /* aa_off_str */ "", /* sb_on_str */ "", /* sb_off_str */ "ATM1\015", /* sp_on_str */ "ATM0\015", /* sp_off_str */ "ATL1\015", /* vol1_str */ "ATL2\015", /* vol2_str */ "ATL3\015", /* vol3_str */ "ATX3\015", /* ignoredt */ "", /* ini2 */ 115200L, /* max_speed */ CKD_AT|CKD_SB|CKD_EC|CKD_DC|CKD_HW|CKD_SW, /* capas */ getok /* ok_fn */ }; static MDMINF DATAPORT = /* AT&T Dataport */ { "AT&T / Paradyne DataPort V.32 or higher", "ATP\015", /* pulse command */ "ATT\015", /* tone command */ 35, /* dial_time */ ",", /* pause_chars */ 2, /* pause_time */ /* Note: S41=0 (use highest modulation) omitted, since it is not supported on the V.32 and lower models. So let's not touch it. */ #ifdef OS2 "ATQ0E1V1X6&S0&C1&D2&Q0Y0\\K5S78=0\015", /* wake_str */ #else #ifdef VMS "ATQ0E1X6&S1&Q0Y0\\K5S78=0\015", /* wake_str */ #else "ATQ0E1X6&Q0Y0\\K5S78=0\015", /* wake_str */ #endif /* VMS */ #endif /* OS2 */ 0, /* wake_rate */ "OK\015", /* wake_prompt */ "", /* dmode_str */ "", /* dmode_prompt */ "ATD%s\015", /* dial_str */ 0, /* dial_rate */ 1100, /* esc_time */ 43, /* esc_char */ "ATQ0H0\015", /* hup_str */ "AT\\Q3\015", /* hwfc_str */ "AT\\Q1\\X0\015", /* swfc_str */ "AT\\Q0\015", /* nofc_str */ "AT\\N7\015", /* ec_on_str */ "AT\\N0\015", /* ec_off_str */ "AT%C1\015", /* dc_on_str */ "AT%C0\015", /* dc_off_str */ "ATS0=1\015", /* aa_on_str */ "ATS0=0\015", /* aa_off_str */ "", /* sb_on_str */ "", /* sb_off_str */ "ATM1\015", /* sp_on_str */ "ATM0\015", /* sp_off_str */ "ATL1\015", /* vol1_str */ "ATL2\015", /* vol2_str */ "ATL3\015", /* vol3_str */ "ATX3\015", /* ignoredt */ "", /* ini2 */ 57600L, /* max_speed */ CKD_AT|CKD_SB|CKD_EC|CKD_DC|CKD_HW|CKD_SW, /* capas */ getok /* ok_fn */ }; static MDMINF UCOM_AT = /* Microcom DeskPorte FAST ES 28.8 */ { "Microcom DeskPorte FAST 28.8", "ATP\015", /* pulse command */ "ATT\015", /* tone command */ 35, /* dial_time */ ",", /* pause_chars */ 2, /* pause_time */ #ifdef OS2 "ATE1Q0V1X4\\N0F0&S0&C1&D2\\K5\015", /* wake_str */ #else #ifdef VMS "ATQ0X4F0&S1\\K5\015", /* wake_str */ #else "ATQ0X4F0\\K5\015", /* wake_str */ #endif /* VMS */ #endif /* OS2 */ 0, /* wake_rate */ "OK\015", /* wake_prompt */ "", /* dmode_str */ "", /* dmode_prompt */ "ATD%s\015", /* dial_str */ 0, /* dial_rate */ 1100, /* esc_time */ 43, /* esc_char */ "ATQ0H0\015", /* hup_str */ "AT\\Q3\015", /* hwfc_str */ "AT\\Q1\015", /* swfc_str */ "AT\\H0\\Q0\015", /* nofc_str */ "AT\\N3\015", /* ec_on_str */ "AT\\N0\015", /* ec_off_str */ "AT%C3\015", /* dc_on_str */ "AT%C0\015", /* dc_off_str */ "ATS0=1\015", /* aa_on_str */ "ATS0=0\015", /* aa_off_str */ "AT-J0\015", /* sb_on_str */ "AT-J1\015", /* sb_off_str */ "ATM1\015", /* sp_on_str */ "ATM0\015", /* sp_off_str */ "ATL1\015", /* vol1_str */ "ATL2\015", /* vol2_str */ "ATL3\015", /* vol3_str */ "ATX3\015", /* ignoredt */ "", /* ini2 */ 115200L, /* max_speed */ CKD_AT|CKD_SB|CKD_EC|CKD_DC|CKD_HW|CKD_SW, /* capas */ getok /* ok_fn */ }; static MDMINF ZOOM = /* Zoom Telephonics V.32bis */ { "Zoom Telephonics V.32bis", "ATP\015", /* pulse command */ "ATT\015", /* tone command */ 35, /* dial_time */ ",", /* pause_chars */ 2, /* pause_time */ #ifdef OS2 "ATE1Q0V1N1W1X4&S0&C1&D2S82=128S95=47\015", /* wake_str */ #else #ifdef VMS "ATQ0E1N1W1X4&S1S82=128S95=47\015", /* wake_str */ #else "ATQ0E1N1W1X4S82=128S95=47\015", /* wake_str */ #endif /* VMS */ #endif /* OS2 */ 0, /* wake_rate */ "OK\015", /* wake_prompt */ "", /* dmode_str */ "", /* dmode_prompt */ "ATD%s\015", /* dial_str */ 0, /* dial_rate */ 1100, /* esc_time */ 43, /* esc_char */ "ATQ0H0\015", /* hup_str */ "AT&K3\015", /* hwfc_str */ "AT&K4\015", /* swfc_str */ "AT&K0\015", /* nofc_str */ "AT&Q5S36=7S48=7\015", /* ec_on_str */ "AT&Q0\015", /* ec_off_str */ "ATS46=138\015", /* dc_on_str */ "ATS46=136\015", /* dc_off_str */ "ATS0=1\015", /* aa_on_str */ "ATS0=0\015", /* aa_off_str */ "", /* sb_on_str */ "", /* sb_off_str */ "ATM1\015", /* sp_on_str */ "ATM0\015", /* sp_off_str */ "ATL1\015", /* vol1_str */ "ATL2\015", /* vol2_str */ "ATL3\015", /* vol3_str */ "ATX3\015", /* ignoredt */ "", /* ini2 */ 57600L, /* max_speed */ CKD_AT|CKD_SB|CKD_EC|CKD_DC|CKD_HW|CKD_SW, /* capas */ getok /* ok_fn */ }; static MDMINF ZYXEL = /* ZyXEL U-Series */ { "ZyXEL U-Series V.32bis or higher", "ATP\015", /* pulse command */ "ATT\015", /* tone command */ 35, /* dial_time */ ",", /* pause_chars */ 2, /* pause_time */ #ifdef OS2 "ATE1Q0V1&S0&C1&D2&N0X5&Y1\015", /* wake_str */ #else #ifdef VMS "ATQ0E1&S1&N0X5&Y1\015", /* wake_str */ #else "ATQ0E1&N0X5&Y1\015", /* wake_str */ #endif /* VMS */ #endif /* OS2 */ 0, /* wake_rate */ "OK\015", /* wake_prompt */ "", /* dmode_str */ "", /* dmode_prompt */ "ATD%s\015", /* dial_str */ 0, /* dial_rate */ 1100, /* esc_time */ 43, /* esc_char */ "ATQ0H0\015", /* hup_str */ "AT&H3\015", /* hwfc_str */ "AT&H4\015", /* swfc_str */ "AT&H0\015", /* nofc_str */ "AT&K3\015", /* ec_on_str */ "AT&K0\015", /* ec_off_str */ "AT&K4\015", /* dc_on_str */ "AT&K3\015", /* dc_off_str */ "ATS0=1\015", /* aa_on_str */ "ATS0=0\015", /* aa_off_str */ "", /* sb_on_str */ "", /* sb_off_str */ "ATM1\015", /* sp_on_str */ "ATM0\015", /* sp_off_str */ "ATL1\015", /* vol1_str */ "ATL2\015", /* vol2_str */ "ATL3\015", /* vol3_str */ "ATX3\015", /* ignoredt */ "", /* ini2 */ 57600L, /* max_speed */ CKD_AT|CKD_SB|CKD_EC|CKD_DC|CKD_HW|CKD_SW, /* capas */ getok /* ok_fn */ }; static MDMINF ZOLTRIX = /* Zoltrix */ { "Zoltrix V.32bis and V.34 modems with Rockwell ACI chipset", "ATP\015", /* pulse command */ "ATT\015", /* tone command */ 35, /* dial_time */ ",", /* pause_chars */ 2, /* pause_time */ #ifdef OS2 "ATE1Q0V1F0W1X4Y0&S0&C1&D2\\K5S82=128S95=41\015", /* wake_str */ #else #ifdef VMS "ATQ0E1F0W1X4Y0&S1\\K5S82=128S95=41\015", /* wake_str */ #else "ATQ0E1F0W1X4Y0\\K5S82=128S95=41\015", /* wake_str */ #endif /* VMS */ #endif /* OS2 */ 0, /* wake_rate */ "OK\015", /* wake_prompt */ "", /* dmode_str */ "", /* dmode_prompt */ "ATD%s\015", /* dial_str */ 0, /* dial_rate */ 1100, /* esc_time */ 43, /* esc_char */ "ATQ0H0\015", /* hup_str */ "AT&K3\015", /* hwfc_str */ "AT&K4S32=17S33=19\015", /* swfc_str */ "AT&K0\015", /* nofc_str */ "AT\\N3\015", /* ec_on_str */ "AT\\N1\015", /* ec_off_str */ "ATS46=138%C3\015", /* dc_on_str */ "ATS46=136%C0\015", /* dc_off_str */ "ATS0=1\015", /* aa_on_str */ "ATS0=0\015", /* aa_off_str */ "AT\\N0\015", /* sb_on_str */ "AT&Q0\015", /* sb_off_str */ "ATM1\015", /* sp_on_str */ "ATM0\015", /* sp_off_str */ "ATL1\015", /* vol1_str */ "ATL2\015", /* vol2_str */ "ATL3\015", /* vol3_str */ "ATX3\015", /* ignoredt */ "", /* ini2 */ 57600L, /* max_speed */ CKD_AT|CKD_SB|CKD_EC|CKD_DC|CKD_HW|CKD_SW, /* capas */ getok /* ok_fn */ }; static MDMINF MOTOROLA = { /* Motorola FasTalk II or Lifestyle */ /* "\E" and "\X" commands removed - Motorola Lifestyle doesn't have them. \E0 = Don't echo while online \X0 = Process Xon/Xoff but don't pass through */ "Motorola FasTalk II or Lifestyle", /* Name */ "ATP\015", /* pulse command */ "ATT\015", /* tone command */ 35, /* dial_time */ ",", /* pause_chars */ 2, /* pause_time */ #ifdef OS2 "ATE1Q0V1X4&S0&C1&D2\\K5\\V1\015", /* wake_str */ #else #ifdef VMS "ATQ0E1X4&S1\\K5\\V1\015", /* wake_str */ #else "ATQ0E1X4\\K5\\V1\015", /* wake_str */ #endif /* VMS */ #endif /* OS2 */ 0, /* wake_rate */ "OK\015", /* wake_prompt */ "", /* dmode_str */ "", /* dmode_prompt */ "ATD%s\015", /* dial_str */ 0, /* dial_rate */ 1100, /* esc_time */ 43, /* esc_char */ "ATQ0H0\015", /* hup_str */ "AT\\Q3\015", /* hwfc_str */ "AT\\Q1\015", /* swfc_str */ "AT\\Q0\015", /* nofc_str */ "AT\\N6\015", /* ec_on_str */ "AT\\N1\015", /* ec_off_str */ "AT%C1\015", /* dc_on_str */ "AT%C0\015", /* dc_off_str */ "ATS0=1\015", /* aa_on_str */ "ATS0=0\015", /* aa_off_str */ "AT\\J0\015", /* sb_on_str */ "AT\\J1\015", /* sb_off_str */ "ATM1\015", /* sp_on_str */ "ATM0\015", /* sp_off_str */ "ATL1\015", /* vol1_str */ "ATL2\015", /* vol2_str */ "ATL3\015", /* vol3_str */ "ATX3\015", /* ignoredt */ "", /* ini2 */ 57600L, /* max_speed */ CKD_AT|CKD_SB|CKD_EC|CKD_DC|CKD_HW|CKD_SW, /* capas */ getok /* ok_fn */ }; static MDMINF BOCA = /* Boca */ { "BOCA 14.4 Faxmodem", "ATP\015", /* pulse command */ "ATT\015", /* tone command */ 35, /* dial_time */ ",", /* pause_chars */ 2, /* pause_time */ #ifdef OS2 "ATE1Q0V1F1N1W1&S0&C1&D2\\K5S37=11S82=128S95=47X4\015", /* wake_str */ #else #ifdef VMS "ATQ0E1F1N1W1&S1\\K5S37=11S82=128S95=47X4\015", /* wake_str */ #else "ATQ0E1F1N1W1\\K5S37=11S82=128S95=47X4\015", /* wake_str */ #endif /* VMS */ #endif /* OS2 */ 0, /* wake_rate */ "OK\015", /* wake_prompt */ "", /* dmode_str */ "", /* dmode_prompt */ "ATD%s\015", /* dial_str */ 0, /* dial_rate */ 1100, /* esc_time */ 43, /* esc_char */ "ATQ0H0\015", /* hup_str */ "AT&K3\015", /* hwfc_str */ "AT&K4\015", /* swfc_str */ "AT&K0\015", /* nofc_str */ "AT\\N3S36=7S48=7\015", /* ec_on_str */ "AT\\N1\015", /* ec_off_str */ "ATS46=138\015", /* dc_on_str */ "ATS46=136\015", /* dc_off_str */ "ATS0=1\015", /* aa_on_str */ "ATS0=0\015", /* aa_off_str */ "", /* sb_on_str */ "", /* sb_off_str */ "ATM1\015", /* sp_on_str */ "ATM0\015", /* sp_off_str */ "ATL1\015", /* vol1_str */ "ATL2\015", /* vol2_str */ "ATL3\015", /* vol3_str */ "ATX3\015", /* ignoredt */ "", /* ini2 */ 57600L, /* max_speed */ CKD_AT|CKD_SB|CKD_EC|CKD_DC|CKD_HW|CKD_SW, /* capas */ getok /* ok_fn */ }; static MDMINF INTEL = /* Intel */ { "Intel High-Speed Faxmodem", "ATP\015", /* pulse command */ "ATT\015", /* tone command */ 35, /* dial_time */ ",", /* pause_chars */ 2, /* pause_time */ #ifdef OS2 "ATE1Q0V1Y0X4&S0&C1&D2\\K1\\V2S25=50\015", /* wake_str */ #else #ifdef VMS "ATQ0E1Y0X4&S1\\K1\\V2S25=50\015", /* wake_str */ #else "ATQ0E1Y0X4\\K1\\V2S25=50\015", /* wake_str */ #endif /* VMS */ #endif /* OS2 */ 0, /* wake_rate */ "OK\015", /* wake_prompt */ "ATB1+FCLASS=0\015", /* dmode_str */ "OK\015", /* dmode_prompt */ "ATD%s\015", /* dial_str */ 0, /* dial_rate */ 1100, /* esc_time */ 43, /* esc_char */ "ATQ0H0\015", /* hup_str */ "AT\\G1\\Q3\015", /* hwfc_str */ "AT\\G1\\Q1\\X0\015", /* swfc_str */ "AT\\G0\015", /* nofc_str */ "AT\\J0\\N3\"H3\015", /* ec_on_str */ "AT\\N1\015", /* ec_off_str */ "AT%C1\015", /* dc_on_str */ "AT%C0\015", /* dc_off_str */ "ATS0=1\015", /* aa_on_str */ "ATS0=0\015", /* aa_off_str */ "", /* sb_on_str */ "", /* sb_off_str */ "ATM1\015", /* sp_on_str */ "ATM0\015", /* sp_off_str */ "ATL1\015", /* vol1_str */ "ATL2\015", /* vol2_str */ "ATL3\015", /* vol3_str */ "ATX3\015", /* ignoredt */ "", /* ini2 */ 57600L, /* max_speed */ CKD_AT|CKD_SB|CKD_EC|CKD_DC|CKD_HW|CKD_SW, /* capas */ getok /* ok_fn */ }; static MDMINF MULTITECH = /* Multitech */ { "Multitech MT1432 or MT2834 Series", "ATP\015", /* pulse command */ "ATT\015", /* tone command */ 35, /* dial_time */ ",", /* pause_chars */ 2, /* pause_time */ /* #P0 (= no parity) is not listed in the manual for newer models */ /* so it has been removed from all three copies of the Multitech wake_str */ #ifdef OS2 "ATE1Q0V1X4&S0&C1&D2&E8&Q0\015", /* wake_str */ #else #ifdef VMS "ATQ0E1X4&S1&E8&Q0\015", /* wake_str */ #else "ATQ0E1X4&E8&Q0\015", /* wake_str */ #endif /* VMS */ #endif /* OS2 */ 0, /* wake_rate */ "OK\015", /* wake_prompt */ "", /* dmode_str */ "", /* dmode_prompt */ "ATD%s\015", /* dial_str */ 0, /* dial_rate */ 1100, /* esc_time */ 43, /* esc_char */ "ATQ0H0\015", /* hup_str */ "AT&E4&E7&E8&E11&E13\015", /* hwfc_str */ "AT&E5&E6&E8&E11&E13\015", /* swfc_str */ "AT&E3&E7&E8&E10&E12\015", /* nofc_str */ "AT&E1\015", /* ec_on_str */ "AT&E0\015", /* ec_off_str */ "AT&E15\015", /* dc_on_str */ "AT&E14\015", /* dc_off_str */ "ATS0=1\015", /* aa_on_str */ "ATS0=0\015", /* aa_off_str */ "AT$BA0\015", /* sb_on_str (= "baud adjust off") */ "AT$BA1\015", /* sb_off_str */ "ATM1\015", /* sp_on_str */ "ATM0\015", /* sp_off_str */ "ATL1\015", /* vol1_str */ "ATL2\015", /* vol2_str */ "ATL3\015", /* vol3_str */ "ATX3\015", /* ignoredt */ "", /* ini2 */ 57600L, /* max_speed */ CKD_AT|CKD_SB|CKD_EC|CKD_DC|CKD_HW|CKD_SW, /* capas */ getok /* ok_fn */ }; static MDMINF SUPRA = /* Supra */ { "SupraFAXModem 144 or 288", "ATP\015", /* pulse command */ "ATT\015", /* tone command */ 35, /* dial_time */ ",", /* pause_chars */ 2, /* pause_time */ #ifdef OS2 "ATQ0E1V1N1W0X4Y0&S0&C1&D2\\K5S82=128\015", /* wake_str */ #else #ifdef VMS "ATQ0E1N1W0X4Y0&S1\\K5S82=128\015", /* wake_str */ #else "ATQ0E1N1W0X4Y0\\K5S82=128\015", /* wake_str */ #endif /* VMS */ #endif /* OS2 */ 0, /* wake_rate */ "OK\015", /* wake_prompt */ "", /* dmode_str */ "", /* dmode_prompt */ "ATD%s\015", /* dial_str */ 0, /* dial_rate */ 1100, /* esc_time */ 43, /* esc_char */ "ATQ0H0\015", /* hup_str */ "AT&K3\015", /* hwfc_str */ "AT&K4\015", /* swfc_str */ "AT&K0\015", /* nofc_str */ "AT&Q5\\N3S48=7\015", /* ec_on_str */ "AT&Q0\\N1\015", /* ec_off_str */ "AT%C1S46=138\015", /* dc_on_str */ "AT%C0S46=136\015", /* dc_off_str */ "ATS0=1\015", /* aa_on_str */ "ATS0=0\015", /* aa_off_str */ "", /* sb_on_str */ "", /* sb_off_str */ "ATM1\015", /* sp_on_str */ "ATM\015", /* sp_off_str */ "ATL\015", /* vol1_str */ "ATL2\015", /* vol2_str */ "ATL3\015", /* vol3_str */ "ATX3\015", /* ignoredt */ "", /* ini2 */ 57600L, /* max_speed */ CKD_AT|CKD_SB|CKD_EC|CKD_DC|CKD_HW|CKD_SW, /* capas */ getok /* ok_fn */ }; static MDMINF SUPRAX = /* Supra Express */ { "Diamond Supra Express V.90", "ATP\015", /* pulse command */ "ATT\015", /* tone command */ 35, /* dial_time */ ",", /* pause_chars */ 2, /* pause_time */ #ifdef OS2 "ATQ0E1V1W0X4&C1&D2&S0\\K5\015", /* wake_str */ #else #ifdef VMS "ATQ0E1W0X4&S1\\K5\015", /* wake_str */ #else "ATQ0E1W0X4\\K5\015", /* wake_str */ #endif /* VMS */ #endif /* OS2 */ 0, /* wake_rate */ "OK\015", /* wake_prompt */ "", /* dmode_str */ "", /* dmode_prompt */ "ATD%s\015", /* dial_str */ 0, /* dial_rate */ 1100, /* esc_time */ 43, /* esc_char */ "ATQ0H0\015", /* hup_str */ "AT&K3\015", /* hwfc_str */ "AT&K4\015", /* swfc_str */ "AT&K0\015", /* nofc_str */ "AT\\N3\015", /* ec_on_str */ "AT\\N1\015", /* ec_off_str */ "AT%C2\015", /* dc_on_str */ "AT%C0\015", /* dc_off_str */ "ATS0=1\015", /* aa_on_str */ "ATS0=0\015", /* aa_off_str */ "", /* sb_on_str */ "", /* sb_off_str */ "ATM1\015", /* sp_on_str */ "ATM\015", /* sp_off_str */ "ATL\015", /* vol1_str */ "ATL2\015", /* vol2_str */ "ATL3\015", /* vol3_str */ "ATX3\015", /* ignoredt */ "", /* ini2 */ 230400L, /* max_speed */ CKD_AT|CKD_SB|CKD_EC|CKD_DC|CKD_HW|CKD_SW, /* capas */ getok /* ok_fn */ }; static MDMINF MAXTECH = /* MaxTech */ { "MaxTech XM288EA or GVC FAXModem", "ATP\015", /* pulse command */ "ATT\015", /* tone command */ 35, /* dial_time */ ",", /* pause_chars */ 2, /* pause_time */ #ifdef OS2 "ATQ0E1V1X4Y0&S0&C1&D2&L0&M0\\K5\015", /* wake_str */ #else #ifdef VMS "ATQ0E1X4Y0&L0&M0&S1\\K5\015", /* wake_str */ #else "ATQ0E1X4Y0&L0&M0\\K5\015", /* wake_str */ #endif /* VMS */ #endif /* OS2 */ 0, /* wake_rate */ "OK\015", /* wake_prompt */ "", /* dmode_str */ "", /* dmode_prompt */ "ATD%s\015", /* dial_str */ 0, /* dial_rate */ 1100, /* esc_time */ 43, /* esc_char */ "ATQ0H0\015", /* hup_str */ "AT\\Q3\015", /* hwfc_str */ "AT\\Q1\\X0\015", /* swfc_str */ "AT\\Q0\015", /* nofc_str */ "AT\\N6\015", /* ec_on_str */ "AT\\N0\015", /* ec_off_str */ "AT\\N6%C1\015", /* dc_on_str */ "AT\\N6%C0\015", /* dc_off_str */ "ATS0=1\015", /* aa_on_str */ "ATS0=0\015", /* aa_off_str */ "", /* sb_on_str */ "", /* sb_off_str */ "ATM1\015", /* sp_on_str */ "ATM0\015", /* sp_off_str */ "ATL1\015", /* vol1_str */ "ATL2\015", /* vol2_str */ "ATL3\015", /* vol3_str */ "ATX3\015", /* ignoredt */ "", /* ini2 */ 115200L, /* max_speed */ CKD_AT|CKD_SB|CKD_EC|CKD_DC|CKD_HW|CKD_SW, /* capas */ getok /* ok_fn */ }; static MDMINF ROLM = /* IBM / Siemens / Rolm 8000, 9000, 9751 CBX DCM */ { "IBM/Siemens/Rolm CBX Data Communications Module", "", /* pulse command */ "", /* tone command */ 60, /* dial_time */ "", /* pause_chars */ 0, /* pause_time */ "\015\015", /* wake_str */ 50, /* wake_rate */ "MODIFY?", /* wake_prompt */ "", /* dmode_str */ "", /* dmode_prompt */ "CALL %s\015", /* dial_str */ 0, /* dial_rate */ 0, /* esc_time */ 0, /* esc_char */ "", /* hup_str */ "", /* hwfc_str */ "", /* swfc_str */ "", /* nofc_str */ "", /* ec_on_str */ "", /* ec_off_str */ "", /* dc_on_str */ "", /* dc_off_str */ "", /* aa_on_str */ "", /* aa_off_str */ "", /* sb_on_str */ "", /* sb_off_str */ "", /* sp_off_str */ "", /* sp_on_str */ "", /* vol1_str */ "", /* vol2_str */ "", /* vol3_str */ "", /* ignoredt */ "", /* ini2 */ 19200L, /* max_speed */ 0, /* capas */ NULL /* ok_fn */ }; static MDMINF USR = /* USR Courier and Sportster modems */ { "US Robotics Courier, Sportster, or compatible", "ATP\015", /* pulse command */ "ATT\015", /* tone command */ 35, /* dial_time */ ",", /* pause_chars */ 2, /* pause_time */ #ifdef OS2 "ATQ0E1V1X4&A3&S0&C1&D2&N0&Y3S14=0\015", /* wake_str */ #else #ifdef SUNOS4 "ATQ0X4&A3&S0&N0&Y3S14=0\015", /* wake_str -- needs &S0 in SunOS */ #else #ifdef VMS "ATQ0X4&A3&S1&N0&Y3S14=0\015", /* wake_str -- needs &S1 in VMS */ #else "ATQ0X4&A3&N0&Y3S14=0\015", /* wake_str */ #endif /* VMS */ #endif /* SUNOS4 */ #endif /* OS2 */ 0, /* wake_rate */ "OK\015", /* wake_prompt */ "", /* dmode_str */ "", /* dmode_prompt */ "ATD%s\015", /* dial_str */ 0, /* dial_rate */ 1100, /* esc_time */ 43, /* esc_char */ "ATQ0H0\015", /* hup_str */ "AT&H1&R2&I0\015", /* hwfc_str */ "AT&H2&R1&I2\015", /* swfc_str */ "AT&H0&R1&I0\015", /* nofc_str */ "AT&M4&B1\015", /* ec_on_str */ "AT&M0\015", /* ec_off_str */ "AT&K1\015", /* dc_on_str */ "AT&K0\015", /* dc_off_str */ "ATS0=1\015", /* aa_on_str */ "ATS0=0\015", /* aa_off_str */ "", /* sb_on_str */ "", /* sb_off_str */ "ATM1\015", /* sp_on_str */ "ATM0\015", /* sp_off_str */ "ATL1\015", /* vol1_str */ "ATL2\015", /* vol2_str */ "ATL3\015", /* vol3_str */ "ATX3\015", /* ignoredt */ "", /* ini2 */ 115200L, /* max_speed */ CKD_AT|CKD_SB|CKD_EC|CKD_DC|CKD_HW|CKD_SW, /* capas */ getok /* ok_fn */ }; static MDMINF USRX2 = /* USR XJ-CC1560 X2 56K */ { "US Robotics / Megahertz CC/XJ-CC1560 X2", "ATP\015", /* pulse command */ "ATT\015", /* tone command */ 35, /* dial_time */ ",", /* pause_chars */ 2, /* pause_time */ #ifdef OS2 "ATQ0E1V1X4&A3&S0&B2&C1&D2&N0\015", /* wake_str */ #else #ifdef VMS "ATQ0X4&A3&B2&N0&S1\015", /* wake_str */ #else "ATQ0X4&A3&B2&N0\015", /* wake_str */ #endif /* VMS */ #endif /* OS2 */ 0, /* wake_rate */ "OK\015", /* wake_prompt */ "", /* dmode_str */ "", /* dmode_prompt */ "ATD%s\015", /* dial_str */ 0, /* dial_rate */ 1100, /* esc_time */ 43, /* esc_char */ "ATQ0H0\015", /* hup_str */ "AT&H1&I0\015", /* hwfc_str */ "AT&H2&I2\015", /* swfc_str */ "AT&H0&I0\015", /* nofc_str */ "AT&M4\015", /* ec_on_str */ "AT&M0\015", /* ec_off_str */ "AT&K1\015", /* dc_on_str */ "AT&K0\015", /* dc_off_str */ "ATS0=1\015", /* aa_on_str */ "ATS0=0\015", /* aa_off_str */ "AT&B1\015", /* sb_on_str */ "AT&B0\015", /* sb_off_str */ "ATM1\015", /* sp_on_str */ "ATM0\015", /* sp_off_str */ "ATL1\015", /* vol1_str */ "ATL2\015", /* vol2_str */ "ATL3\015", /* vol3_str */ "ATX3\015", /* ignoredt */ "", /* ini2 */ 115200L, /* max_speed */ CKD_AT|CKD_SB|CKD_EC|CKD_DC|CKD_HW|CKD_SW, /* capas */ getok /* ok_fn */ }; static MDMINF OLDTB = /* Old Telebits */ { "Telebit TrailBlazer, T1000, T1500, T2000, T2500", "ATP\015", /* pulse command */ "ATT\015", /* tone command */ 60, /* dial_time */ ",", /* pause_chars */ 2, /* pause_time */ #ifdef OS2 "\021AAAAATQ0E1V1X1&S0&C1&D2S12=50S50=0S54=3\015", /* wake_str. */ #else #ifdef VMS "\021AAAAATQ0X1S12=50S50=0S54=3\015", /* wake_str. */ #else "\021AAAAATQ0X1&S1S12=50S50=0S54=3\015", /* wake_str. */ #endif /* VMS */ #endif /* OS2 */ 100, /* wake_rate = 100 msec */ "OK\015", /* wake_prompt */ "", /* dmode_str */ "", /* dmode_prompt */ "ATD%s\015", /* dial_str, Note: no T or P */ 80, /* dial_rate */ 1100, /* esc_time (guard time) */ 43, /* esc_char */ "ATQ0H0\015", /* hup_str */ "ATS58=2S68=2\015", /* hwfc_str */ "ATS58=3S68=3S69=0\015", /* swfc_str */ "ATS58=0S68=0\015", /* nofc_str */ "ATS66=1S95=2\015", /* ec_on_str */ "ATS95=0\015", /* ec_off_str */ "ATS110=1S96=1\015", /* dc_on_str */ "ATS110=0S96=0\015", /* dc_off_str */ "ATS0=1\015", /* aa_on_str */ "ATS0=0\015", /* aa_off_str */ "", /* sb_on_str */ "", /* sb_off_str */ "ATM1\015", /* sp_on_str */ "ATM0\015", /* sp_off_str */ "ATL1\015", /* vol1_str */ "ATL2\015", /* vol2_str */ "ATL3\015", /* vol3_str */ "ATX3\015", /* ignoredt */ "", /* ini2 */ 19200L, /* max_speed */ CKD_AT|CKD_SB|CKD_EC|CKD_DC|CKD_HW|CKD_SW|CKD_TB|CKD_KS, /* capas */ getok /* ok_fn */ }; static MDMINF NEWTB = /* New Telebits */ { "Telebit T1600, T3000, QBlazer, WorldBlazer, etc.", "ATP\015", /* pulse command */ "ATT\015", /* tone command */ 60, /* dial_time */ ",", /* pause_chars */ 2, /* pause_time */ #ifdef OS2 "\021AAAAATQ0E1V1X2&S0&C1&D2S12=50S50=0S61=0S63=0\015", /* wake_str. */ #else #ifdef VMS "\021AAAAATQ0X2&S1S12=50S50=0S61=0S63=0\015", /* wake_str. */ #else "\021AAAAATQ0X2S12=50S50=0S61=0S63=0\015", /* wake_str. */ #endif /* VMS */ #endif /* OS2 */ 100, /* wake_rate = 100 msec */ "OK\015", /* wake_prompt */ "", /* dmode_str */ "", /* dmode_prompt */ "ATD%s\015", /* dial_str, Note: no T or P */ 80, /* dial_rate */ 1100, /* esc_time (guard time) */ 43, /* esc_char */ "ATQ0H0\015", /* hup_str */ "ATS58=2S68=2\015", /* hwfc_str */ "ATS58=3S68=3\015", /* swfc_str */ "ATS58=0S68=0\015", /* nofc_str */ "ATS180=3\015", /* ec_on_str */ "ATS180=0\015", /* ec_off_str */ "ATS190=1\015", /* dc_on_str */ "ATS190=0\015", /* dc_off_str */ "ATS0=1\015", /* aa_on_str */ "ATS0=0\015", /* aa_off_str */ "", /* sb_on_str */ "", /* sb_off_str */ "ATM1\015", /* sp_on_str */ "ATM0\015", /* sp_off_str */ "ATL1\015", /* vol1_str */ "ATL2\015", /* vol2_str */ "ATL3\015", /* vol3_str */ "ATX3\015", /* ignoredt */ "", /* ini2 */ 38400L, /* max_speed */ CKD_AT|CKD_SB|CKD_EC|CKD_DC|CKD_HW|CKD_SW|CKD_TB|CKD_KS, /* capas */ getok /* ok_fn */ }; #endif /* MINIDIAL */ static MDMINF DUMMY = /* dummy information for modems that are handled elsewhere */ { "(dummy)", "", /* pulse command */ "", /* tone command */ 30, /* dial_time */ "", /* pause_chars */ 0, /* pause_time */ "", /* wake_str */ 0, /* wake_rate */ "", /* wake_prompt */ "", /* dmode_str */ NULL, /* dmode_prompt */ "%s\015", /* dial_str */ 0, /* dial_rate */ 0, /* esc_time */ 0, /* esc_char */ "", /* hup_str */ "", /* hwfc_str */ "", /* swfc_str */ "", /* nofc_str */ "", /* ec_on_str */ "", /* ec_off_str */ "", /* dc_on_str */ "", /* dc_off_str */ "", /* aa_on_str */ "", /* aa_off_str */ "", /* sb_on_str */ "", /* sb_off_str */ "", /* sp_off_str */ "", /* sp_on_str */ "", /* vol1_str */ "", /* vol2_str */ "", /* vol3_str */ "", /* ignoredt */ "", /* ini2 */ 0L, /* max_speed */ 0, /* capas */ NULL /* ok_fn */ }; #ifndef MINIDIAL static MDMINF RWV32 = /* Generic Rockwell V.32 */ { "Generic Rockwell V.32 modem", /* ATI3, ATI4, and ATI6 for details */ "ATP\015", /* pulse command */ "ATT\015", /* tone command */ 35, /* dial_time */ ",", /* pause_chars */ 2, /* pause_time */ #ifdef OS2 "ATQ0E1V1X4Y0&S0&C1&D2%E2\\K5+FCLASS=0N1S37=0\015", /* wake_str */ #else #ifdef VMS "ATQ0X4Y0&S1%E2\\K5+FCLASS=0N1S37=0\015", /* wake_str */ #else "ATQ0X4Y0%E2\\K5+FCLASS=0N1S37=0\015", /* wake_str */ #endif /* VMS */ #endif /* OS2 */ 0, /* wake_rate */ "OK\015", /* wake_prompt */ "", /* dmode_str */ "", /* dmode_prompt */ "ATD%s\015", /* dial_str */ 0, /* dial_rate */ 1100, /* esc_time */ 43, /* esc_char */ "ATQ0H0\015", /* hup_str */ "AT&K3\015", /* hwfc_str */ "AT&K4S32=17S33=19\015", /* swfc_str */ "AT&K0\015", /* nofc_str */ "AT&Q6\015", /* ec_on_str */ "AT&Q0\015", /* ec_off_str */ "AT%C1\015", /* dc_on_str */ "AT%C0\015", /* dc_off_str */ "ATS0=1\015", /* aa_on_str */ "ATS0=0\015", /* aa_off_str */ "", /* sb_on_str */ "", /* sb_off_str */ "ATM1\015", /* sp_on_str */ "ATM0\015", /* sp_off_str */ "ATL1\015", /* vol1_str */ "ATL2\015", /* vol2_str */ "ATL3\015", /* vol3_str */ "ATX3\015", /* ignoredt */ "", /* ini2 */ 57600L, /* max_speed */ CKD_AT|CKD_SB|CKD_EC|CKD_DC|CKD_HW|CKD_SW, /* capas */ getok /* ok_fn */ }; static MDMINF RWV32B = /* Generic Rockwell V.32bis */ { "Generic Rockwell V.32bis modem", /* ATI3, ATI4, and ATI6 for details */ "ATP\015", /* pulse command */ "ATT\015", /* tone command */ 35, /* dial_time */ ",", /* pause_chars */ 2, /* pause_time */ #ifdef OS2 "ATQ0E1V1X4Y0&S0&C1&D2%E2\\K5+FCLASS=0N1S37=0\015", /* wake_str */ #else #ifdef VMS "ATQ0X4Y0&S1%E2\\K5+FCLASS=0N1S37=0\015", /* wake_str */ #else "ATQ0X4Y0%E2\\K5+FCLASS=0N1S37=0\015", /* wake_str */ #endif /* VMS */ #endif /* OS2 */ 0, /* wake_rate */ "OK\015", /* wake_prompt */ "", /* dmode_str */ "", /* dmode_prompt */ "ATD%s\015", /* dial_str */ 0, /* dial_rate */ 1100, /* esc_time */ 43, /* esc_char */ "ATQ0H0\015", /* hup_str */ "AT&K3\015", /* hwfc_str */ "AT&K4S32=17S33=19\015", /* swfc_str */ "AT&K0\015", /* nofc_str */ "AT&Q5\015", /* ec_on_str */ "AT&Q0\015", /* ec_off_str */ "ATS%C1\015", /* dc_on_str */ "ATS%C0\015", /* dc_off_str */ "ATS0=1\015", /* aa_on_str */ "ATS0=0\015", /* aa_off_str */ "", /* sb_on_str */ "", /* sb_off_str */ "ATM1\015", /* sp_on_str */ "ATM0\015", /* sp_off_str */ "ATL1\015", /* vol1_str */ "ATL2\015", /* vol2_str */ "ATL3\015", /* vol3_str */ "ATX3\015", /* ignoredt */ "", /* ini2 */ 57600L, /* max_speed */ CKD_AT|CKD_SB|CKD_EC|CKD_DC|CKD_HW|CKD_SW, /* capas */ getok /* ok_fn */ }; static MDMINF RWV34 = /* Generic Rockwell V.34 Data/Fax */ { "Generic Rockwell V.34 modem", /* ATI3, ATI4, and ATI6 for details */ "ATP\015", /* pulse command */ "ATT\015", /* tone command */ 35, /* dial_time */ ",", /* pause_chars */ 2, /* pause_time */ #ifdef OS2 "ATQ0V1X4Y0&C1&D2&S0%E2\\K5+FCLASS=0\015", /* wake_str */ #else #ifdef VMS "ATQ0V1X4Y0&C1&D2&S1%E2\\K5+FCLASS=0\015", /* wake_str */ #else "ATQ0V1X4Y0&C1&D2%E2\\K5+FCLASS=0\015", /* wake_str */ #endif /* VMS */ #endif /* OS2 */ 0, /* wake_rate */ "OK\015", /* wake_prompt */ "", /* dmode_str */ "", /* dmode_prompt */ "ATD%s\015", /* dial_str */ 0, /* dial_rate */ 1100, /* esc_time */ 43, /* esc_char */ "ATQ0H0\015", /* hup_str */ "AT&K3\015", /* hwfc_str */ "AT&K4S32=17S33=19\015", /* swfc_str */ "AT&K0\015", /* nofc_str */ "AT&Q5\015", /* ec_on_str */ "AT&Q0\015", /* ec_off_str */ "ATS%C3\015", /* dc_on_str */ "ATS%C0\015", /* dc_off_str */ "ATS0=1\015", /* aa_on_str */ "ATS0=0\015", /* aa_off_str */ "", /* sb_on_str */ "", /* sb_off_str */ "ATM1\015", /* sp_on_str */ "ATM0\015", /* sp_off_str */ "ATL1\015", /* vol1_str */ "ATL2\015", /* vol2_str */ "ATL3\015", /* vol3_str */ "ATX3\015", /* ignoredt */ "", /* ini2 */ 115200L, /* max_speed */ CKD_AT|CKD_SB|CKD_EC|CKD_DC|CKD_HW|CKD_SW, /* capas */ getok /* ok_fn */ }; static MDMINF RWV90 = /* Generic Rockwell V.90 Data/Fax */ { "Generic Rockwell V.90 56K modem", /* ATI3, ATI4, and ATI6 for details */ "ATP\015", /* pulse command */ "ATT\015", /* tone command */ 35, /* dial_time */ ",", /* pause_chars */ 2, /* pause_time */ #ifdef OS2 "ATQ0V1N1X4Y0&C1&D2&S0%E2\\K5+FCLASS=0S37=0\015", /* K95 */ #else #ifdef VMS "ATQ0V1N1X4Y0&C1&D2&S1%E2\\K5+FCLASS=0S37=0\015", /* wake_str */ #else "ATQ0V1N1X4Y0&C1&D2%E2\\K5+FCLASS=0S37=0\015", /* wake_str */ #endif /* VMS */ #endif /* OS2 */ 0, /* wake_rate */ "OK\015", /* wake_prompt */ "", /* dmode_str */ "", /* dmode_prompt */ "ATD%s\015", /* dial_str */ 0, /* dial_rate */ 1100, /* esc_time */ 43, /* esc_char */ "ATQ0H0\015", /* hup_str */ "AT&K3\015", /* hwfc_str */ "AT&K4S32=17S33=19\015", /* swfc_str */ "AT&K0\015", /* nofc_str */ "AT&Q5\015", /* ec_on_str */ "AT&Q0\015", /* ec_off_str */ "AT%C3\015", /* dc_on_str */ "AT%C0\015", /* dc_off_str */ "ATS0=1\015", /* aa_on_str */ "ATS0=0\015", /* aa_off_str */ "", /* sb_on_str */ "", /* sb_off_str */ "ATM1\015", /* sp_on_str */ "ATM0\015", /* sp_off_str */ "ATL1\015", /* vol1_str */ "ATL2\015", /* vol2_str */ "ATL3\015", /* vol3_str */ "ATX3\015", /* ignoredt */ "", /* ini2 */ 115200L, /* max_speed */ CKD_AT|CKD_SB|CKD_EC|CKD_DC|CKD_HW|CKD_SW, /* capas */ getok /* ok_fn */ }; static MDMINF MWAVE = /* IBM Mwave */ { "IBM Mwave Adapter", "ATP\015", /* pulse command */ "ATT\015", /* tone command */ 35, /* dial_time */ ",", /* pause_chars */ 2, /* pause_time */ #ifdef OS2 "ATQ0E1V1X4Y0&S0&C1&D2&M0&Q0&N1\\K3\\T0%E2S28=0\015", /* wake_str */ #else #ifdef VMS "ATQ0X4Y0&M0&S1&Q0&N1&S0\\K3\\T0%E2S28=0\015", /* wake_str */ #else "ATQ0X4Y0&M0&Q0&N1&S0\\K3\\T0%E2S28=0\015", /* wake_str */ #endif /* VMS */ #endif /* OS2 */ 0, /* wake_rate */ "OK\015", /* wake_prompt */ "", /* dmode_str */ "", /* dmode_prompt */ "ATD%s\015", /* dial_str */ 0, /* dial_rate */ 1100, /* esc_time */ 43, /* esc_char */ "ATQ0H0\015", /* hup_str */ "AT\\Q3\015", /* hwfc_str */ "", /* swfc_str (it doesn't!) */ "AT\\Q0\015", /* nofc_str */ "AT\\N7\015", /* ec_on_str */ "AT\\N0\015", /* ec_off_str */ "AT%C1\"H3\015", /* dc_on_str */ "AT%C0\"H0\015", /* dc_off_str */ "ATS0=1\015", /* aa_on_str */ "ATS0=0\015", /* aa_off_str */ "", /* sb_on_str */ "", /* sb_off_str */ "ATM1\015", /* sp_on_str */ "ATM0\015", /* sp_off_str */ "ATL1\015", /* vol1_str */ "ATL2\015", /* vol2_str */ "ATL3\015", /* vol3_str */ "ATX3\015", /* ignoredt */ "", /* ini2 */ 57600L, /* max_speed */ CKD_AT|CKD_SB|CKD_EC|CKD_DC|CKD_HW, /* capas */ getok /* ok_fn */ }; static MDMINF TELEPATH = /* Gateway 2000 Telepath */ { "Gateway 2000 Telepath II 28.8", "ATP\015", /* pulse command */ "ATT\015", /* tone command */ 35, /* dial_time */ ",", /* pause_chars */ 2, /* pause_time */ #ifdef OS2 "ATQ0E1V1X4&S0&C1&D2&N0&Y2#CLS=0S13=0S15=0S19=0\015", /* wake_str */ #else #ifdef VMS "ATQ0X4&N0&S1&Y1#CLS=0S13=0S15=0S19=0\015", /* wake_str */ #else "ATQ0X4&N0&Y1#CLS=0S13=0S15=0S19=0\015", /* wake_str */ #endif /* VMS */ #endif /* OS2 */ 0, /* wake_rate */ "OK\015", /* wake_prompt */ "", /* dmode_str */ "", /* dmode_prompt */ "ATD%s\015", /* dial_str */ 0, /* dial_rate */ 1100, /* esc_time */ 43, /* esc_char */ "ATQ0H0\015", /* hup_str */ "AT&H1&R2\015", /* hwfc_str */ "AT&H2&I2S22=17S23=19\015", /* swfc_str */ "AT&H0&I0&R1\015", /* nofc_str */ "AT&M4&B1\015", /* ec_on_str -- also fixes speed */ "AT&M0\015", /* ec_off_str */ "AT&K1\015", /* dc_on_str */ "AT&K0\015", /* dc_off_str */ "ATS0=1\015", /* aa_on_str */ "ATS0=0\015", /* aa_off_str */ "", /* sb_on_str */ "", /* sb_off_str */ "ATM1\015", /* sp_on_str */ "ATM0\015", /* sp_off_str */ "ATL1\015", /* vol1_str */ "ATL2\015", /* vol2_str */ "ATL3\015", /* vol3_str */ "ATX3\015", /* ignoredt */ "", /* ini2 */ 57600L, /* max_speed */ CKD_AT|CKD_SB|CKD_EC|CKD_DC|CKD_HW|CKD_SW, /* capas */ getok /* ok_fn */ }; static MDMINF CARDINAL = /* Cardinal - based on Rockwell V.34 */ { "Cardinal MVP288X Series", /* ATI3, ATI4, and ATI6 for details */ "ATP\015", /* pulse command */ "ATT\015", /* tone command */ 35, /* dial_time */ ",", /* pause_chars */ 2, /* pause_time */ #ifdef OS2 "ATQ0E1V1X4W1Y0%E2&S0&C1&D2\\K5+FCLASS=0+MS=11,1\015", /* wake_str */ #else #ifdef VMS "ATQ0X4W1Y0&S1%E2\\K5+FCLASS=0+MS=11,1\015", /* wake_str */ #else "ATQ0X4W1Y0%E2\\K5+FCLASS=0+MS=11,1\015", /* wake_str */ #endif /* VMS */ #endif /* OS2 */ 0, /* wake_rate */ "OK\015", /* wake_prompt */ "", /* dmode_str */ "", /* dmode_prompt */ "ATD%s\015", /* dial_str */ 0, /* dial_rate */ 1100, /* esc_time */ 43, /* esc_char */ "ATQ0H0\015", /* hup_str */ "AT&K3\015", /* hwfc_str */ "AT&K4S32=17S33=19\015", /* swfc_str */ "AT&K0\015", /* nofc_str */ "AT&Q5S36=7S48=7\\N3\015", /* ec_on_str */ "AT&Q0S48=128\\N1\015", /* ec_off_str */ "ATS46=138%C1\015", /* dc_on_str */ "ATS46=136%C0\015", /* dc_off_str */ "ATS0=1\015", /* aa_on_str */ "ATS0=0\015", /* aa_off_str */ "", /* sb_on_str */ "", /* sb_off_str */ "ATM1\015", /* sp_on_str */ "ATM0\015", /* sp_off_str */ "ATL1\015", /* vol1_str */ "ATL2\015", /* vol2_str */ "ATL3\015", /* vol3_str */ "ATX3\015", /* ignoredt */ "", /* ini2 */ 115200L, /* max_speed */ CKD_AT|CKD_SB|CKD_EC|CKD_DC|CKD_HW|CKD_SW, /* capas */ getok /* ok_fn */ }; static MDMINF LUCENT = /* Lucent Venus or Data/Fax modem */ { "Lucent Venus chipset", "ATP\015", /* pulse command */ "ATT\015", /* tone command */ 35, /* dial_time */ ",", /* pause_chars */ 2, /* pause_time */ #ifdef OS2 "ATQ0V1N1X4Y0&C1&D2&S0%E2\\K5+FCLASS=0S37=0\015", /* K95 */ #else #ifdef VMS "ATQ0V1N1X4Y0&C1&D2&S1%E2\\K5+FCLASS=0S37=0\015", /* VMS */ #else "ATQ0V1N1X4Y0&C1&D2%E2\\K5+FCLASS=0S37=0\015", /* All others */ #endif /* VMS */ #endif /* OS2 */ 0, /* wake_rate */ "OK\015", /* wake_prompt */ "", /* dmode_str */ "", /* dmode_prompt */ "ATD%s\015", /* dial_str */ 0, /* dial_rate */ 1100, /* esc_time */ 43, /* esc_char */ "ATQ0H0\015", /* hup_str */ "AT&K3\015", /* hwfc_str */ "AT&K4S32=17S33=19\015", /* swfc_str */ "AT&K0\015", /* nofc_str */ "AT&Q5\015", /* ec_on_str */ "AT&Q0\015", /* ec_off_str */ "AT%C1\015", /* dc_on_str */ "AT%C0\015", /* dc_off_str */ "ATS0=1\015", /* aa_on_str */ "ATS0=0\015", /* aa_off_str */ "", /* sb_on_str */ "", /* sb_off_str */ "ATM1\015", /* sp_on_str */ "ATM0\015", /* sp_off_str */ "ATL1\015", /* vol1_str */ "ATL2\015", /* vol2_str */ "ATL3\015", /* vol3_str */ "ATX3\015", /* ignoredt */ "", /* ini2 */ 115200L, /* max_speed */ CKD_AT|CKD_SB|CKD_EC|CKD_DC|CKD_HW|CKD_SW, /* capas */ getok /* ok_fn */ }; static MDMINF CONEXANT = /* Conexant family */ { "Conexant family of modems", "ATP\015", /* pulse command */ "ATT\015", /* tone command */ 35, /* dial_time */ ",", /* pause_chars */ 2, /* pause_time */ #ifdef OS2 "ATQ0V1X4&C1&D2&S0%E1+FCLASS=0\015", /* K95 */ #else #ifdef VMS "ATQ0V1X4&C1&D2&S1%E1+FCLASS=0\015", /* VMS */ #else "ATQ0V1X4&C1&D2%E1+FCLASS=0\015", /* UNIX etc */ #endif /* VMS */ #endif /* OS2 */ 0, /* wake_rate */ "OK\015", /* wake_prompt */ "", /* dmode_str */ "", /* dmode_prompt */ "ATD%s\015", /* dial_str */ 0, /* dial_rate */ 1100, /* esc_time */ 43, /* esc_char */ "ATQ0H0\015", /* hup_str */ "AT&K3\015", /* hwfc_str */ "AT&K4S32=17S33=19\015", /* swfc_str */ "AT&K0\015", /* nofc_str */ "AT&Q5\015", /* ec_on_str */ "AT&Q0\015", /* ec_off_str */ "AT%C3\015", /* dc_on_str */ "AT%C0\015", /* dc_off_str */ "ATS0=1\015", /* aa_on_str */ "ATS0=0\015", /* aa_off_str */ "", /* sb_on_str */ "", /* sb_off_str */ "ATM1\015", /* sp_on_str */ "ATM0\015", /* sp_off_str */ "ATL1\015", /* vol1_str */ "ATL2\015", /* vol2_str */ "ATL3\015", /* vol3_str */ "ATX3\015", /* ignoredt */ "", /* ini2 */ 115200L, /* max_speed */ CKD_AT|CKD_SB|CKD_EC|CKD_DC|CKD_HW|CKD_SW, /* capas */ getok /* ok_fn */ }; static MDMINF PCTEL = /* PCTel chipset */ { "PCTel chipset", "ATP\015", /* pulse command */ "ATT\015", /* tone command */ 35, /* dial_time */ ",", /* pause_chars */ 2, /* pause_time */ #ifdef OS2 "ATQ0V1N1X4Y0&C1&D2&S0%E2\\K5S37=0\015", /* K95 */ #else #ifdef VMS "ATQ0V1N1X4Y0&C1&D2&S1%E2\\K5S37=0\015", /* VMS */ #else "ATQ0V1N1X4Y0&C1&D2%E2\\K5S37=0\015", /* UNIX etc */ #endif /* VMS */ #endif /* OS2 */ 0, /* wake_rate */ "OK\015", /* wake_prompt */ "", /* dmode_str */ "", /* dmode_prompt */ "ATD%s\015", /* dial_str */ 0, /* dial_rate */ 1100, /* esc_time */ 43, /* esc_char */ "ATQ0H0\015", /* hup_str */ "AT&K3\015", /* hwfc_str */ "AT&K4S32=17S33=19\015", /* swfc_str */ "AT&K0\015", /* nofc_str */ "AT\\N3\015", /* ec_on_str */ "AT\\N0\015", /* ec_off_str */ "AT%C1\015", /* dc_on_str */ "AT%C0\015", /* dc_off_str */ "ATS0=1\015", /* aa_on_str */ "ATS0=0\015", /* aa_off_str */ "", /* sb_on_str */ "", /* sb_off_str */ "ATM1\015", /* sp_on_str */ "ATM0\015", /* sp_off_str */ "ATL1\015", /* vol1_str */ "ATL2\015", /* vol2_str */ "ATL3\015", /* vol3_str */ "ATX3\015", /* ignoredt */ "", /* ini2 */ 115200L, /* max_speed */ CKD_AT|CKD_SB|CKD_EC|CKD_DC|CKD_HW|CKD_SW, /* capas */ getok /* ok_fn */ }; static MDMINF ZOOMV34 = /* Zoom Telephonics V.34 */ { "Zoom Telephonics V.34", "ATP\015", /* pulse command */ "ATT\015", /* tone command */ 35, /* dial_time */ ",", /* pause_chars */ 2, /* pause_time */ #ifdef OS2 "ATQ0V1N1W1X4&S0&C1&D2S82=128\015", /* wake_str */ #else #ifdef VMS "ATQ0V1N1W1X4&S1S82=128\015", /* wake_str */ #else "ATQ0V1N1W1X4S82=128S015", /* wake_str */ #endif /* VMS */ #endif /* OS2 */ 0, /* wake_rate */ "OK\015", /* wake_prompt */ "", /* dmode_str */ "", /* dmode_prompt */ "ATD%s\015", /* dial_str */ 0, /* dial_rate */ 1100, /* esc_time */ 43, /* esc_char */ "ATQ0H0\015", /* hup_str */ "AT&K3\015", /* hwfc_str */ "AT&K4\015S32=17S33=19", /* swfc_str */ "AT&K0\015", /* nofc_str */ "AT&Q5\015", /* ec_on_str */ "AT&Q0\015", /* ec_off_str */ "ATS%C3\015", /* dc_on_str */ "ATS%C0\015", /* dc_off_str */ "ATS0=1\015", /* aa_on_str */ "ATS0=0\015", /* aa_off_str */ "", /* sb_on_str */ "", /* sb_off_str */ "ATM1\015", /* sp_on_str */ "ATM0\015", /* sp_off_str */ "ATL1\015", /* vol1_str */ "ATL2\015", /* vol2_str */ "ATL3\015", /* vol3_str */ "ATX3\015", /* ignoredt */ "", /* ini2 */ 57600L, /* max_speed */ CKD_AT|CKD_SB|CKD_EC|CKD_DC|CKD_HW|CKD_SW, /* capas */ getok /* ok_fn */ }; static MDMINF ZOOMV90 = /* ZOOM V.90 */ { "Zoom V.90 56K", "ATP\015", /* pulse command */ "ATT\015", /* tone command */ 35, /* dial_time */ ",", /* pause_chars */ 2, /* pause_time */ #ifdef OS2 "ATQ0V1N1X4Y0&C1&D2&S0%E2\\K5+FCLASS=0S37=0\015", /* K95 */ #else #ifdef VMS "ATQ0V1N1X4Y0&C1&D2&S1%E2\\K5+FCLASS=0S37=0\015", /* VMS */ #else "ATQ0V1N1X4Y0&C1&D2%E2\\K5+FCLASS=0S37=0\015", /* All others */ #endif /* VMS */ #endif /* OS2 */ 0, /* wake_rate */ "OK\015", /* wake_prompt */ "", /* dmode_str */ "", /* dmode_prompt */ "ATD%s\015", /* dial_str */ 0, /* dial_rate */ 1100, /* esc_time */ 43, /* esc_char */ "ATQ0H0\015", /* hup_str */ "AT&K3\015", /* hwfc_str */ "AT&K4S32=17S33=19\015", /* swfc_str */ "AT&K0\015", /* nofc_str */ "AT&Q5\015", /* ec_on_str */ "AT&Q0\015", /* ec_off_str */ "AT%C1\015", /* dc_on_str */ "AT%C0\015", /* dc_off_str */ "ATS0=1\015", /* aa_on_str */ "ATS0=0\015", /* aa_off_str */ "", /* sb_on_str */ "", /* sb_off_str */ "ATM1\015", /* sp_on_str */ "ATM0\015", /* sp_off_str */ "ATL1\015", /* vol1_str */ "ATL2\015", /* vol2_str */ "ATL3\015", /* vol3_str */ "ATX3\015", /* ignoredt */ "", /* ini2 */ 115200L, /* max_speed */ CKD_AT|CKD_SB|CKD_EC|CKD_DC|CKD_HW|CKD_SW, /* capas */ getok /* ok_fn */ }; static MDMINF ZOOMV92 = /* ZOOM V.92 */ { "Zoom V.92 with V.44 compression", "ATP\015", /* pulse command */ "ATT\015", /* tone command */ 35, /* dial_time */ ",", /* pause_chars */ 2, /* pause_time */ #ifdef OS2 "ATQ0V1N1X4Y0&C1&D2&S0%E2\\K5+FCLASS=0S37=0+MS=V92\015", /* K95 */ #else #ifdef VMS "ATQ0V1N1X4Y0&C1&D2&S1%E2\\K5+FCLASS=0S37=0+MS=V92\015", /* VMS */ #else "ATQ0V1N1X4Y0&C1&D2%E2\\K5+FCLASS=0S37=0+MS=V92\015", /* All others */ #endif /* VMS */ #endif /* OS2 */ 0, /* wake_rate */ "OK\015", /* wake_prompt */ "", /* dmode_str */ "", /* dmode_prompt */ "ATD%s\015", /* dial_str */ 0, /* dial_rate */ 1100, /* esc_time */ 43, /* esc_char */ "ATQ0H0\015", /* hup_str */ "AT&K3\015", /* hwfc_str */ "AT&K4S32=17S33=19\015", /* swfc_str */ "AT&K0\015", /* nofc_str */ "AT&Q5\015", /* ec_on_str */ "AT&Q0\015", /* ec_off_str */ "AT%C1+DCS=1,1\015", /* dc_on_str */ "AT%C0\015", /* dc_off_str */ "ATS0=1\015", /* aa_on_str */ "ATS0=0\015", /* aa_off_str */ "", /* sb_on_str */ "", /* sb_off_str */ "ATM1\015", /* sp_on_str */ "ATM0\015", /* sp_off_str */ "ATL1\015", /* vol1_str */ "ATL2\015", /* vol2_str */ "ATL3\015", /* vol3_str */ "ATX3\015", /* ignoredt */ "", /* ini2 */ 115200L, /* max_speed */ CKD_AT|CKD_SB|CKD_EC|CKD_DC|CKD_HW|CKD_SW, /* capas */ getok /* ok_fn */ }; /* Now the "old" modems, all grouped together, and also within "if not defined MINIDIAL"... */ #ifdef OLDMODEMS static MDMINF CERMETEK = /* Information for "Cermetek Info-Mate 212 A" modem */ { "Cermetek Info-Mate 212 A", "", /* pulse command */ "", /* tone command */ 20, /* dial_time */ "BbPpTt", /* pause_chars */ 0, /* pause_time */ " XY\016R\015", /* wake_str */ 200, /* wake_rate */ "", /* wake_prompt */ "", /* dmode_str */ NULL, /* dmode_prompt */ "\016D '%s'\015", /* dial_str */ 200, /* dial_rate */ 0, /* esc_time */ 0, /* esc_char */ "", /* hup_str */ "", /* hwfc_str */ "", /* swfc_str */ "", /* nofc_str */ "", /* ec_on_str */ "", /* ec_off_str */ "", /* dc_on_str */ "", /* dc_off_str */ "", /* aa_on_str */ "", /* aa_off_str */ "", /* sb_on_str */ "", /* sb_off_str */ "", /* sp_off_str */ "", /* sp_on_str */ "", /* vol1_str */ "", /* vol2_str */ "", /* vol3_str */ "", /* ignoredt */ "", /* ini2 */ 1200L, /* max_speed */ 0, /* capas */ NULL /* ok_fn */ }; static MDMINF DF03 = /* information for "DEC DF03-AC" modem */ { "Digital DF03-AC", "", /* pulse command */ "", /* tone command */ 27, /* dial_time */ "=", /* pause_chars */ 15, /* pause_time */ "\001\002", /* wake_str */ 0, /* wake_rate */ "", /* wake_prompt */ "", /* dmode_str */ NULL, /* dmode_prompt */ "%s", /* dial_str */ 0, /* dial_rate */ 0, /* esc_time */ 0, /* esc_char */ "", /* hup_str */ "", /* hwfc_str */ "", /* swfc_str */ "", /* nofc_str */ "", /* ec_on_str */ "", /* ec_off_str */ "", /* dc_on_str */ "", /* dc_off_str */ "", /* aa_on_str */ "", /* aa_off_str */ "", /* sb_on_str */ "", /* sb_off_str */ "", /* sp_off_str */ "", /* sp_on_str */ "", /* vol1_str */ "", /* vol2_str */ "", /* vol3_str */ "", /* ignoredt */ "", /* ini2 */ 0L, /* max_speed */ 0, /* capas */ NULL /* ok_fn */ }; static MDMINF DF100 = /* information for "DEC DF100-series" modem */ /* * The telephone "number" can include "P"s and/or "T"s * within it to indicate that subsequent digits are * to be dialed using pulse or tone dialing. The * modem defaults to pulse dialing. You may modify * the dial string below to explicitly default all * dialing to pulse or tone, but doing so prevents * the use of phone numbers that you may have stored * in the modem's memory. */ { "Digital DF-100", "", /* pulse command */ "", /* tone command */ 30, /* dial_time */ "=", /* pause_chars */ 15, /* pause_time */ "\001", /* wake_str */ 0, /* wake_rate */ "", /* wake_prompt */ "", /* dmode_str */ NULL, /* dmode_prompt */ "%s#", /* dial_str */ 0, /* dial_rate */ 0, /* esc_time */ 0, /* esc_char */ "", /* hup_str */ "", /* hwfc_str */ "", /* swfc_str */ "", /* nofc_str */ "", /* ec_on_str */ "", /* ec_off_str */ "", /* dc_on_str */ "", /* dc_off_str */ "", /* aa_on_str */ "", /* aa_off_str */ "", /* sb_on_str */ "", /* sb_off_str */ "", /* sp_off_str */ "", /* sp_on_str */ "", /* vol1_str */ "", /* vol2_str */ "", /* vol3_str */ "", /* ignoredt */ "", /* ini2 */ 0L, /* max_speed */ 0, /* capas */ NULL /* ok_fn */ }; static MDMINF DF200 = /* information for "DEC DF200-series" modem */ /* * The telephone "number" can include "P"s and/or "T"s * within it to indicate that subsequent digits are * to be dialed using pulse or tone dialing. The * modem defaults to pulse dialing. You may modify * the dial string below to explicitly default all * dialing to pulse or tone, but doing so prevents * the use of phone numbers that you may have stored * in the modem's memory. */ { "Digital DF-200", "", /* pulse command */ "", /* tone command */ 30, /* dial_time */ "=W", /* pause_chars */ /* =: second tone; W: 5 secs */ 15, /* pause_time */ /* worst case */ "\002", /* wake_str */ /* allow stored number usage */ 0, /* wake_rate */ "", /* wake_prompt */ "", /* dmode_str */ NULL, /* dmode_prompt */ #ifdef COMMENT "%s!", /* dial_str */ #else " d %s\015", #endif /* COMMENT */ 0, /* dial_rate */ 0, /* esc_time */ 0, /* esc_char */ "", /* hup_str */ "", /* hwfc_str */ "", /* swfc_str */ "", /* nofc_str */ "", /* ec_on_str */ "", /* ec_off_str */ "", /* dc_on_str */ "", /* dc_off_str */ "", /* aa_on_str */ "", /* aa_off_str */ "", /* sb_on_str */ "", /* sb_off_str */ "", /* sp_off_str */ "", /* sp_on_str */ "", /* vol1_str */ "", /* vol2_str */ "", /* vol3_str */ "", /* ignoredt */ "", /* ini2 */ 0L, /* max_speed */ 0, /* capas */ NULL /* ok_fn */ }; static MDMINF GDC = /* information for "GeneralDataComm 212A/ED" modem */ { "GeneralDataComm 212A/ED", "", /* pulse command */ "", /* tone command */ 32, /* dial_time */ "%", /* pause_chars */ 3, /* pause_time */ "\015\015", /* wake_str */ 500, /* wake_rate */ "$", /* wake_prompt */ "D\015", /* dmode_str */ ":", /* dmode_prompt */ "T%s\015", /* dial_str */ 0, /* dial_rate */ 0, /* esc_time */ 0, /* esc_char */ "", /* hup_str */ "", /* hwfc_str */ "", /* swfc_str */ "", /* nofc_str */ "", /* ec_on_str */ "", /* ec_off_str */ "", /* dc_on_str */ "", /* dc_off_str */ "", /* aa_on_str */ "", /* aa_off_str */ "", /* sb_on_str */ "", /* sb_off_str */ "", /* sp_off_str */ "", /* sp_on_str */ "", /* vol1_str */ "", /* vol2_str */ "", /* vol3_str */ "", /* ignoredt */ "", /* ini2 */ 1200L, /* max_speed */ 0, /* capas */ NULL /* ok_fn */ }; static MDMINF PENRIL = /* information for "Penril" modem */ { "Penril modem", "", /* pulse command */ "", /* tone command */ 50, /* dial_time */ "", /* pause_chars */ 0, /* pause_time */ "\015\015", /* wake_str */ 300, /* wake_rate */ ">", /* wake_prompt */ "k\015", /* dmode_str */ ":", /* dmode_prompt */ "%s\015", /* dial_str */ 0, /* dial_rate */ 0, /* esc_time */ 0, /* esc_char */ "", /* hup_str */ "", /* hwfc_str */ "", /* swfc_str */ "", /* nofc_str */ "", /* ec_on_str */ "", /* ec_off_str */ "", /* dc_on_str */ "", /* dc_off_str */ "", /* aa_on_str */ "", /* aa_off_str */ "", /* sb_on_str */ "", /* sb_off_str */ "", /* sp_off_str */ "", /* sp_on_str */ "", /* vol1_str */ "", /* vol2_str */ "", /* vol3_str */ "", /* ignoredt */ "", /* ini2 */ 0L, /* max_speed */ 0, /* capas */ NULL /* ok_fn */ }; static MDMINF RACAL = /* Racal Vadic VA4492E */ { "Racal Vadic VA4492E", "", /* pulse command */ "", /* tone command */ 35, /* dial_time (manual says modem is hardwired to 60) */ "Kk", /* pause_chars */ 5, /* pause_time */ "\005\015", /* wake_str, ^E^M */ 50, /* wake_rate */ "*", /* wake_prompt */ "D\015", /* dmode_str */ "?", /* dmode_prompt */ "%s\015", /* dial_str */ 0, /* dial_rate */ 1100, /* esc_time */ 5, /* esc_char, ^E */ "\003\004", /* hup_str, ^C^D */ 0, /* hwfc_str */ "", /* swfc_str */ "", /* nofc_str */ "", /* ec_on_str */ "", /* ec_off_str */ "", /* dc_on_str */ "", /* dc_off_str */ "", /* aa_on_str */ "", /* aa_off_str */ "", /* sb_on_str */ "", /* sb_off_str */ "", /* sp_off_str */ "", /* sp_on_str */ "", /* vol1_str */ "", /* vol2_str */ "", /* vol3_str */ "", /* ignoredt */ "", /* ini2 */ 0L, /* max_speed */ 0, /* capas */ NULL /* ok_fn */ }; static MDMINF VENTEL = /* Information for Ven-Tel modem */ { "Ven-Tel", "", /* pulse command */ "", /* tone command */ 20, /* dial_time */ "%", /* pause_chars */ 5, /* pause_time */ "\015\015\015", /* wake_str */ 300, /* wake_rate */ "$", /* wake_prompt */ "K\015", /* dmode_str (was "") */ "Number to call: ", /* dmode_prompt (was NULL) */ "%s\015", /* dial_str (was "") */ 0, /* dial_rate */ 0, /* esc_time */ 0, /* esc_char */ "", /* hup_str */ "", /* hwfc_str */ "", /* swfc_str */ "", /* nofc_str */ "", /* ec_on_str */ "", /* ec_off_str */ "", /* dc_on_str */ "", /* dc_off_str */ "", /* aa_on_str */ "", /* aa_off_str */ "", /* sb_on_str */ "", /* sb_off_str */ "", /* sp_off_str */ "", /* sp_on_str */ "", /* vol1_str */ "", /* vol2_str */ "", /* vol3_str */ "", /* ignoredt */ "", /* ini2 */ 0L, /* max_speed */ 0, /* capas */ NULL /* ok_fn */ }; static MDMINF CONCORD = /* Info for Condor CDS 220 2400b modem */ { "Concord Condor CDS 220 2400b", "", /* pulse command */ "", /* tone command */ 35, /* dial_time */ ",", /* pause_chars */ 2, /* pause_time */ "\015\015", /* wake_str */ 20, /* wake_rate */ "CDS >", /* wake_prompt */ "", /* dmode_str */ NULL, /* dmode_prompt */ "", /* dial_str */ 0, /* dial_rate */ 0, /* esc_time */ 0, /* esc_char */ "", /* hup_str */ "", /* hwfc_str */ "", /* swfc_str */ "", /* nofc_str */ "", /* ec_on_str */ "", /* ec_off_str */ "", /* dc_on_str */ "", /* dc_off_str */ "", /* aa_on_str */ "", /* aa_off_str */ "", /* sb_on_str */ "", /* sb_off_str */ "", /* sp_off_str */ "", /* sp_on_str */ "", /* vol1_str */ "", /* vol2_str */ "", /* vol3_str */ "", /* ignoredt */ "", /* ini2 */ 2400L, /* max_speed */ 0, /* capas */ NULL /* ok_fn */ }; #endif /* OLDMODEMS */ static MDMINF MICROCOM = /* Microcom modems in native SX mode */ /* (long answer only) */ { "Microcom MNP modems in SX command mode", "DP\015", /* pulse command */ "DT\015", /* tone command */ 35, /* dial_time */ ",!@", /* pause_chars (! and @ aren't pure pauses) */ 3, /* pause_time */ /* The following sets 8 bits, no parity, BREAK passthru, and SE0 disables the escape character, which is a single character with no guard time, totally unsafe, so we have no choice but to disable it. Especially since, by default, it is Ctrl-A, which is Kermit's packet-start character. We would change it to something else, which would enable "mdmhup()", but the user wouldn't know about it. Very bad. Note: SE1 sets it to Ctrl-A, SE2 sets it to Ctrl-B, etc (1..31 allowed). Also SE/Q sets it to "Q". */ "SE0;S1P4;SBRK5\015", /* wake_str */ 100, /* wake_rate */ "!", /* wake_prompt */ "", /* dmode_str */ NULL, /* dmode_prompt */ "D%s\015", /* dial_str - number up to 39 chars */ 0, /* dial_rate */ 0, /* esc_time */ 0, /* esc_char - we can't use this */ "", /* hup_str - it's "H" but can't use */ "SF13\015", /* hwfc_str */ "SF11\015", /* swfc_str */ "SF10\015", /* nofc_str */ "BAOFF;SMAUT\015", /* ec_on_str */ "BAON;SMDIR\015", /* ec_off_str */ "COMP1\015", /* dc_on_str */ "COMP0\015", /* dc_off_str */ "AA", /* aa_on_str */ "", /* aa_off_str */ "", /* sb_on_str */ "", /* sb_off_str */ "SA2", /* sp_off_str */ "SA0", /* sp_on_str */ "", /* vol1_str */ "", /* vol2_str */ "", /* vol3_str */ "", /* ignoredt */ "", /* ini2 */ 0L, /* max_speed */ CKD_SB|CKD_EC|CKD_DC|CKD_HW|CKD_SW|CKD_KS, /* capas */ getok /* ok_fn */ }; static MDMINF MICROLINK = /* MicroLink ... */ { /* 14.4TQ,TL,PC;28.8TQ,TQV;2440T/TR */ "ELSA MicroLink 14.4, 28.8, 33.6 or 56K", /* ELSA GmbH, Aachen */ "ATP\015", /* pulse command */ "ATT\015", /* tone command */ 35, /* dial_time */ ",", /* pause_chars */ 2, /* pause_time */ #ifdef OS2 "ATQ0E1V1X4&S0\\D0&C1&D2\\K5\015", /* wake_str */ #else #ifdef VMS "ATQ0X4&S1\\K5\015", /* wake_str */ #else "ATQ0X4\\K5\015", /* wake_str */ #endif /* VMS */ #endif /* OS2 */ 0, /* wake_rate */ "OK\015", /* wake_prompt */ "", /* dmode_str */ "", /* dmode_prompt */ "ATD%s\015", /* dial_str */ 0, /* dial_rate */ 1100, /* esc_time */ 43, /* esc_char */ "ATQ0H\015", /* hup_str */ "AT\\Q3\015", /* hwfc_str */ "AT\\Q1\\X0\015", /* swfc_str */ "AT\\Q0\015", /* nofc_str */ "AT\\N3\015", /* ec_on_str */ "AT\\N0\015", /* ec_off_str */ "AT%C3\015", /* dc_on_str */ "AT%C0\015", /* dc_off_str */ "ATS0=1\015", /* aa_on_str */ "ATS0=0\015", /* aa_off_str */ "\\J0", /* sb_on_str (?) */ "", /* sb_off_str */ "ATM1\015", /* sp_on_str */ "ATM0\015", /* sp_off_str */ "ATL1\015", /* vol1_str */ "ATL2\015", /* vol2_str */ "ATL3\015", /* vol3_str */ "ATX3\015", /* ignoredt */ "", /* ini2 */ 57600L, /* max_speed */ CKD_AT|CKD_SB|CKD_EC|CKD_DC|CKD_HW|CKD_SW, /* capas */ getok /* ok_fn */ }; static MDMINF ULINKV250 = /* MicroLink V.250 */ { /* 56Kflex, V.90; V.250 command set */ "ELSA MicroLink 56K V.250", /* ELSA GmbH, Aachen */ "ATP\015", /* pulse command */ "ATT\015", /* tone command */ 35, /* dial_time */ ",", /* pause_chars */ 2, /* pause_time */ #ifdef OS2 /* \D0 = DSR & CTS always on but hwfc overrides on CTS. */ "ATQ0E1V1X4&S0\\D0&C1&D2\015", /* wake_str */ #else #ifdef VMS "ATQ0X4&S1\015", /* wake_str */ #else "ATQ0X4\015", /* wake_str */ #endif /* VMS */ #endif /* OS2 */ 0, /* wake_rate */ "OK\015", /* wake_prompt */ "", /* dmode_str */ "", /* dmode_prompt */ "ATD%s\015", /* dial_str */ 0, /* dial_rate */ 1100, /* esc_time */ 43, /* esc_char */ "ATQ0H0\015", /* hup_str */ "AT+IFC=2,2\015", /* hwfc_str */ "AT+IFC=1,1\015", /* swfc_str */ "AT+IFC=0,0\015", /* nofc_str */ "AT+ES=3,0\015", /* ec_on_str */ "AT+ES=1,0\015", /* ec_off_str */ "AT+DS=3,0,2048,32\015", /* dc_on_str */ "AT+DS=0,0\015", /* dc_off_str */ "ATS0=1\015", /* aa_on_str */ "ATS0=0\015", /* aa_off_str */ "", /* sb_on_str (?) */ "", /* sb_off_str */ "ATM1\015", /* sp_on_str */ "ATM0\015", /* sp_off_str */ "ATL1\015", /* vol1_str */ "ATL2\015", /* vol2_str */ "ATL3\015", /* vol3_str */ "ATX3\015", /* ignoredt */ "", /* ini2 */ 57600L, /* max_speed */ CKD_AT|CKD_SB|CKD_EC|CKD_DC|CKD_HW|CKD_SW, /* capas */ getok /* ok_fn */ }; #endif /* MINIDIAL */ static MDMINF ITUTV250 = /* ITU-T V.250 conforming modem */ { "Any ITU-T V.25ter/V.250 conformant modem", "ATP\015", /* pulse command */ "ATT\015", /* tone command */ 35, /* dial_time */ ",", /* pause_chars */ 2, /* pause_time */ "ATQ0E1V1X4&C1&D2\015", /* wake_str (no &Sn in V.25) */ 0, /* wake_rate */ "OK\015", /* wake_prompt */ "", /* dmode_str */ "", /* dmode_prompt */ "ATD%s\015", /* dial_str */ 0, /* dial_rate */ 1100, /* esc_time */ 43, /* esc_char */ "ATQ0H0\015", /* hup_str */ "AT+IFC=2,2\015", /* hwfc_str */ "AT+IFC=1,1\015", /* swfc_str */ "AT+IFC=0,0\015", /* nofc_str */ "AT+ES=3,0,2;+EB=1,0,30\015", /* ec_on_str */ "AT+ES=0\015", /* ec_off_str */ "AT+DS=3,0\015", /* dc_on_str */ "AT+DS=0,0\015", /* dc_off_str */ "ATS0=1\015", /* aa_on_str */ "ATS0=0\015", /* aa_off_str */ "", /* sb_on_str */ "", /* sb_off_str */ "ATM1\015", /* sp_on_str */ "ATM0\015", /* sp_off_str */ "ATL1\015", /* vol1_str */ "ATL2\015", /* vol2_str */ "ATL3\015", /* vol3_str */ "ATX3\015", /* ignoredt */ "", /* ini2 */ 57600L, /* max_speed */ CKD_AT|CKD_SB|CKD_EC|CKD_DC|CKD_HW|CKD_SW, /* capas */ getok /* ok_fn */ }; #ifndef CK_TAPI static #endif /* CK_TAPI */ MDMINF GENERIC = /* Generic high speed ... */ { "Generic high-speed AT command set", "ATP\015", /* pulse command */ "ATT\015", /* tone command */ 35, /* dial_time */ ",", /* pause_chars */ 2, /* pause_time */ "", /* wake_str */ 0, /* wake_rate */ "", /* wake_prompt */ "", /* dmode_str */ "", /* dmode_prompt */ "ATD%s\015", /* dial_str */ 0, /* dial_rate */ 1100, /* esc_time */ 43, /* esc_char */ "ATQ0H0\015", /* hup_str */ "", /* hwfc_str */ "", /* swfc_str */ "", /* nofc_str */ "", /* ec_on_str */ "", /* ec_off_str */ "", /* dc_on_str */ "", /* dc_off_str */ "ATS0=1\015", /* aa_on_str */ "ATS0=0\015", /* aa_off_str */ "", /* sb_on_str */ "", /* sb_off_str */ "", /* sp_on_str */ "", /* sp_off_str */ "", /* vol1_str */ "", /* vol2_str */ "", /* vol3_str */ "ATX3\015", /* ignoredt */ "", /* ini2 */ 115200, /* max_speed */ CKD_AT|CKD_SB|CKD_EC|CKD_DC|CKD_HW, /* capas */ getok /* ok_fn */ }; #ifndef MINIDIAL static MDMINF XJACK = /* Megahertz X-Jack */ { "Megahertz X-Jack XJ3144 / CC6144", "ATP\015", /* pulse command */ "ATT\015", /* tone command */ 35, /* dial_time */ ",", /* pause_chars */ 2, /* pause_time */ #ifdef OS2 "ATQ0E1V1X4N1&C1&D2\\K5\015", /* wake_str */ #else "ATQ0X4N1\\K5\015", /* wake_str */ #endif /* OS2 */ 0, /* wake_rate */ "OK\015", /* wake_prompt */ "", /* dmode_str */ "", /* dmode_prompt */ "ATD%s\015", /* dial_str */ 0, /* dial_rate */ 1100, /* esc_time */ 43, /* esc_char */ "ATQ0H\015", /* hup_str */ "AT&K3\015", /* hwfc_str */ "AT&K4\015", /* swfc_str */ "AT&K0\015", /* nofc_str */ "AT\\N3&Q5\015", /* ec_on_str */ "AT\\N1&Q0\015", /* ec_off_str */ "AT%C3\015", /* dc_on_str */ "AT%C0\015", /* dc_off_str */ "ATS0=1\015", /* aa_on_str */ "ATS0=0\015", /* aa_off_str */ "", /* sb_on_str */ "", /* sb_off_str */ "ATM1\015", /* sp_on_str */ "ATM0\015", /* sp_off_str */ "ATL1\015", /* vol1_str */ "ATL2\015", /* vol2_str */ "ATL3\015", /* vol3_str */ "ATX3\015", /* ignoredt */ "", /* ini2 */ 57600L, /* max_speed */ CKD_AT|CKD_SB|CKD_EC|CKD_DC|CKD_HW|CKD_SW, /* capas */ getok /* ok_fn */ }; static MDMINF SPIRITII = /* QuickComm Spirit II */ { "QuickComm Spirit II", "ATP\015", /* pulse command */ "ATT\015", /* tone command */ 35, /* dial_time */ ",", /* pause_chars */ 2, /* pause_time */ "AT&F\015", /* wake_str */ 0, /* wake_rate */ "OK\015", /* wake_prompt */ "", /* dmode_str */ "", /* dmode_prompt */ "ATD%s\015", /* dial_str */ 0, /* dial_rate */ 1100, /* esc_time */ 43, /* esc_char */ "ATQ0H\015", /* hup_str */ "AT*F3\015", /* hwfc_str */ "AT*F2\015", /* swfc_str */ "AT*F0\015", /* nofc_str */ "AT*E6\015", /* ec_on_str */ "AT*E0\015", /* ec_off_str */ "AT*E9\015", /* dc_on_str */ "AT*E0\015", /* dc_off_str */ "ATS0=2\015", /* aa_on_str */ "ATS0=0\015", /* aa_off_str */ "", /* sb_on_str */ "", /* sb_off_str */ "ATM1\015", /* sp_on_str */ "ATM0\015", /* sp_off_str */ "ATL1\015", /* vol1_str */ "ATL2\015", /* vol2_str */ "ATL3\015", /* vol3_str */ "ATX3\015", /* ignoredt */ "", /* ini2 */ 57600L, /* max_speed */ CKD_AT|CKD_SB|CKD_EC|CKD_DC|CKD_HW|CKD_SW, /* capas */ getok /* ok_fn */ }; static MDMINF MONTANA = { /* Motorola Montana */ "Motorola Montana", /* Name */ "ATP\015", /* pulse command */ "ATT\015", /* tone command */ 35, /* dial_time */ ",", /* pause_chars */ 2, /* pause_time */ #ifdef OS2 "ATQ0E1V1X4&S0&C1&D2\\K5\\V1\015", /* wake_str */ #else #ifdef VMS "ATQ0E1X4&S1\\K5\\V1\015", /* wake_str */ #else "ATQ0E1X4\\K5\\V1\015", /* wake_str */ #endif /* VMS */ #endif /* OS2 */ 0, /* wake_rate */ "OK\015", /* wake_prompt */ "", /* dmode_str */ "", /* dmode_prompt */ "ATD%s\015", /* dial_str */ 0, /* dial_rate */ 1100, /* esc_time */ 43, /* esc_char */ "ATQ0H0\015", /* hup_str */ "AT\\Q3\015", /* hwfc_str */ "AT\\Q1\015", /* swfc_str */ "AT\\Q0\015", /* nofc_str */ "AT\\N4\015", /* ec_on_str */ "AT\\N1\015", /* ec_off_str */ "AT%C1\015", /* dc_on_str */ "AT%C0\015", /* dc_off_str */ "ATS0=1\015", /* aa_on_str */ "ATS0=0\015", /* aa_off_str */ "AT\\J0\015", /* sb_on_str */ "AT\\J1\015", /* sb_off_str */ "ATM1\015", /* sp_on_str */ "ATM0\015", /* sp_off_str */ "ATL1\015", /* vol1_str */ "ATL2\015", /* vol2_str */ "ATL3\015", /* vol3_str */ "ATX3\015", /* ignoredt */ "", /* ini2 */ 57600L, /* max_speed */ CKD_AT|CKD_SB|CKD_EC|CKD_DC|CKD_HW|CKD_SW, /* capas */ getok /* ok_fn */ }; static MDMINF COMPAQ = { /* Compaq Data+Fax Modem */ "Compaq Data+Fax Modem", /* Name */ "ATP\015", /* pulse command */ "ATT\015", /* tone command */ 35, /* dial_time */ ",", /* pause_chars */ 2, /* pause_time */ #ifdef OS2 "ATQ0E1V1X4&S0&C1&D2\015", /* wake_str */ #else #ifdef VMS "ATQ0E1X4&S1\015", /* wake_str */ #else "ATQ0E1X4\015", /* wake_str */ #endif /* VMS */ #endif /* OS2 */ 0, /* wake_rate */ "OK\015", /* wake_prompt */ "", /* dmode_str */ "", /* dmode_prompt */ "ATD%s\015", /* dial_str */ 0, /* dial_rate */ 1100, /* esc_time */ 43, /* esc_char */ "ATQ0H0\015", /* hup_str */ "AT\\Q3\015", /* hwfc_str (same as &K3) */ "AT\\Q1\015", /* swfc_str (same as &K4) */ "AT\\Q0\015", /* nofc_str (same as &K0) */ "AT\\N3\015", /* ec_on_str */ "AT\\N0\015", /* ec_off_str */ "AT%C1\015", /* dc_on_str */ "AT%C0\015", /* dc_off_str */ "ATS0=1\015", /* aa_on_str */ "ATS0=0\015", /* aa_off_str */ "AT\\N3\015", /* sb_on_str */ "AT\\N1\015", /* sb_off_str */ "ATM1\015", /* sp_on_str */ "ATM0\015", /* sp_off_str */ "ATL0\015", /* vol1_str */ "ATL2\015", /* vol2_str */ "ATL3\015", /* vol3_str */ "ATX3\015", /* ignoredt */ "", /* ini2 */ 115200L, /* max_speed */ CKD_AT|CKD_SB|CKD_EC|CKD_DC|CKD_HW|CKD_SW, /* capas */ getok /* ok_fn */ }; static MDMINF FUJITSU = { /* Fujitsu */ "Fujitsu Fax/Modem Adapter", /* Name */ "ATP\015", /* pulse command */ "ATT\015", /* tone command */ 35, /* dial_time */ ",", /* pause_chars */ 2, /* pause_time */ #ifdef OS2 "ATQ0E1V1X4&S0&C1&D2\\K5\\N3\015", /* wake_str */ #else #ifdef VMS "ATQ0E1X4&S1\\K5\\N3\015", /* wake_str */ #else "ATQ0E1X4\\K5\\N3\015", /* wake_str */ #endif /* VMS */ #endif /* OS2 */ 0, /* wake_rate */ "OK\015", /* wake_prompt */ "", /* dmode_str */ "", /* dmode_prompt */ "ATD%s\015", /* dial_str */ 0, /* dial_rate */ 1100, /* esc_time */ 43, /* esc_char */ "ATQ0H0\015", /* hup_str */ "AT&K3\\Q3\015", /* hwfc_str */ "AT&K4\\Q1\015", /* swfc_str */ "AT&K0\\Q0\015", /* nofc_str */ "AT\\N3\015", /* ec_on_str */ "AT\\N0\015", /* ec_off_str */ "AT%C1", /* dc_on_str */ "AT%C0", /* dc_off_str */ "ATS0=1\015", /* aa_on_str */ "ATS0=0\015", /* aa_off_str */ "AT\\J0\015", /* sb_on_str */ "AT\\J1\015", /* sb_off_str */ "ATM1\015", /* sp_on_str */ "ATM0\015", /* sp_off_str */ "ATL1\015", /* vol1_str */ "ATL2\015", /* vol2_str */ "ATL3\015", /* vol3_str */ "ATX3\015", /* ignoredt */ "", /* ini2 */ 115200L, /* max_speed */ CKD_AT|CKD_SB|CKD_EC|CKD_DC|CKD_HW|CKD_SW, /* capas */ getok /* ok_fn */ }; static MDMINF MHZATT = /* Megahertz AT&T V.34 */ { "Megahertz AT&T V.34", "ATP\015", /* pulse command */ "ATT\015", /* tone command */ 35, /* dial_time */ ",", /* pause_chars */ 2, /* pause_time */ #ifdef OS2 "ATQ0E1V1X4N1&C1&D2\\K5\015", /* wake_str */ #else "ATQ0X4N1\\K5\015", /* wake_str */ #endif /* OS2 */ 0, /* wake_rate */ "OK\015", /* wake_prompt */ "", /* dmode_str */ "", /* dmode_prompt */ "ATD%s\015", /* dial_str */ 0, /* dial_rate */ 1100, /* esc_time */ 43, /* esc_char */ "ATQ0H\015", /* hup_str */ "AT&K3\015", /* hwfc_str */ "AT&K4\015", /* swfc_str */ "AT&K0\015", /* nofc_str */ "AT\\N3\015", /* ec_on_str */ "AT\\N0\015", /* ec_off_str */ "AT%C1\"H3\015", /* dc_on_str */ "AT%C0\"H0\015", /* dc_off_str */ "ATS0=1\015", /* aa_on_str */ "ATS0=0\015", /* aa_off_str */ "AT\\J0\015", /* sb_on_str */ "AT\\J1\015", /* sb_off_str */ "ATM1\015", /* sp_on_str */ "ATM0\015", /* sp_off_str */ "ATL1\015", /* vol1_str */ "ATL2\015", /* vol2_str */ "ATL3\015", /* vol3_str */ "ATX3\015", /* ignoredt */ "", /* ini2 */ 115200L, /* max_speed */ CKD_AT|CKD_SB|CKD_EC|CKD_DC|CKD_HW|CKD_SW, /* capas */ getok /* ok_fn */ }; static MDMINF SUPRASON = /* SupraSonic */ { "Diamond SupraSonic 288V+", /* Diamond Multimedia Systems Inc */ "ATP\015", /* pulse command */ "ATT\015", /* tone command */ 35, /* dial_time */ ",", /* pause_chars */ 2, /* pause_time */ #ifdef OS2 "ATQ0E1V1N1W0X4Y0&S0&C1&D2\015", /* wake_str */ #else #ifdef VMS "ATQ0E1N1W0X4Y0&S1\015", /* wake_str */ #else "ATQ0E1N1W0X4Y0\015", /* wake_str */ #endif /* VMS */ #endif /* OS2 */ 0, /* wake_rate */ "OK\015", /* wake_prompt */ "", /* dmode_str */ "", /* dmode_prompt */ "ATD%s\015", /* dial_str */ 0, /* dial_rate */ 1100, /* esc_time */ 43, /* esc_char */ "ATQ0H0\015", /* hup_str */ "AT&K3\015", /* hwfc_str */ "AT&K4\015", /* swfc_str */ "AT&K\015", /* nofc_str */ "AT&Q5\\N3S48=7\015", /* ec_on_str */ "AT&Q0\\N1\015", /* ec_off_str */ "AT%C3S46=138\015", /* dc_on_str */ "AT%C0S46=136\015", /* dc_off_str */ "ATS0=1\015", /* aa_on_str */ "ATS0=0\015", /* aa_off_str */ "", /* sb_on_str */ "", /* sb_off_str */ "ATM1\015", /* sp_on_str */ "ATM\015", /* sp_off_str */ "ATL\015", /* vol1_str */ "ATL2\015", /* vol2_str */ "ATL3\015", /* vol3_str */ "ATX3\015", /* ignoredt */ "", /* ini2 */ 115200L, /* max_speed */ CKD_AT|CKD_SB|CKD_EC|CKD_DC|CKD_HW|CKD_SW, /* capas */ getok /* ok_fn */ }; static MDMINF BESTDATA = /* Best Data */ { "Best Data Fax Modem", /* Best Data Fax Modem */ "ATP\015", /* pulse command */ "ATT\015", /* tone command */ 35, /* dial_time */ ",", /* pause_chars */ 2, /* pause_time */ #ifdef OS2 "ATQ0E1V1N1W0X4Y0&S0&C1&D2\015", /* wake_str */ #else #ifdef VMS "ATQ0E1N1W0X4Y0&S1\015", /* wake_str */ #else "ATQ0E1N1W0X4Y0\015", /* wake_str */ #endif /* VMS */ #endif /* OS2 */ 0, /* wake_rate */ "OK\015", /* wake_prompt */ "", /* dmode_str */ "", /* dmode_prompt */ "ATD%s\015", /* dial_str */ 0, /* dial_rate */ 1100, /* esc_time */ 43, /* esc_char */ "ATQ0H0\015", /* hup_str */ "AT&K3\015", /* hwfc_str */ "AT&K4\015", /* swfc_str */ "AT&K\015", /* nofc_str */ "AT&Q6\\N3\015", /* ec_on_str */ "AT&Q0\\N1\015", /* ec_off_str */ "AT%C3\015", /* dc_on_str */ "AT%C0\015", /* dc_off_str */ "ATS0=1\015", /* aa_on_str */ "ATS0=0\015", /* aa_off_str */ "AT\\N3\015", /* sb_on_str */ "AT\\N0\015", /* sb_off_str */ "ATM1\015", /* sp_on_str */ "ATM0\015", /* sp_off_str */ "ATL1\015", /* vol1_str */ "ATL2\015", /* vol2_str */ "ATL3\015", /* vol3_str */ "ATX3\015", /* ignoredt */ "", /* ini2 */ 57600L, /* max_speed */ CKD_AT|CKD_SB|CKD_EC|CKD_DC|CKD_HW|CKD_SW, /* capas */ getok /* ok_fn */ }; static MDMINF ATT1900 = /* AT&T Secure Data STU III 1900 */ { "AT&T Secure Data STU III Model 1900", /* name */ "", /* pulse command */ "", /* tone command */ 35, /* dial_time */ ",", /* pause_chars */ 2, /* pause_time */ #ifdef OS2 "ATQ0E1V1X4\015", /* wake_str */ #else "ATQ0E1X4\015", /* wake_str */ #endif /* OS2 */ 0, /* wake_rate */ "OK\015", /* wake_prompt */ "", /* dmode_str */ "", /* dmode_prompt */ "ATD%s\015", /* dial_str */ 0, /* dial_rate */ 1100, /* esc_time */ 43, /* esc_char */ "ATQ0H0\015", /* hup_str */ "", /* hwfc_str */ "", /* swfc_str */ "", /* nofc_str */ "", /* ec_on_str */ "", /* ec_off_str */ "", /* dc_on_str */ "", /* dc_off_str */ "ATS0=1\015", /* aa_on_str */ "ATS0=0\015", /* aa_off_str */ "", /* sb_on_str */ "", /* sb_off_str */ "", /* sp_on_str */ "", /* sp_off_str */ "", /* vol1_str */ "", /* vol2_str */ "", /* vol3_str */ "", /* ignoredt */ "", /* ini2 */ 9600L, /* max_speed */ CKD_AT|CKD_SB|CKD_HW, /* capas */ getok /* ok_fn */ }; /* Experimentation showed that hardly any of the documented commands did anything other that print ERROR. At first there was no communication at all at 9600 bps -- turns out the interface speed was stuck at 2400. ATS28=130 (given at 2400 bps) allowed it to work at 9600. */ static MDMINF ATT1910 = /* AT&T Secure Data STU III 1910 */ { /* Adds V.32bis, V.42, V.42bis */ "AT&T Secure Data STU III Model 1910", /* name */ /* Believe it or not, "ATT" and "ATP" result in ERROR */ "", /* pulse command */ "", /* tone command */ 35, /* dial_time */ ",", /* pause_chars */ 2, /* pause_time */ #ifdef OS2 "ATQ0E1V1X4\015", /* wake_str */ #else "ATQ0E1X4\015", /* wake_str */ #endif /* OS2 */ 0, /* wake_rate */ "OK\015", /* wake_prompt */ "", /* dmode_str */ "", /* dmode_prompt */ "ATD%s\015", /* dial_str */ 0, /* dial_rate */ 1100, /* esc_time */ 43, /* esc_char */ "ATQ0H0\015", /* hup_str */ "", /* hwfc_str */ "", /* swfc_str */ "", /* nofc_str */ #ifdef COMMENT /* These are evidently read-only registers */ "ATS46=138S47=0\015", /* ec_on_str */ "ATS46=138S47=128\015", /* ec_off_str */ "ATS46=138S47=0\015", /* dc_on_str */ "ATS46=138S47=128\015", /* dc_off_str */ #else "", "", "", "", #endif /* COMMENT */ "ATS0=1\015", /* aa_on_str */ "ATS0=0\015", /* aa_off_str */ "", /* sb_on_str */ "", /* sb_off_str */ "", /* sp_on_str */ "", /* sp_off_str */ "", /* vol1_str */ "", /* vol2_str */ "", /* vol3_str */ "", /* ignoredt */ "", /* ini2 */ 9600L, /* max_speed */ CKD_AT|CKD_SB|CKD_EC|CKD_DC|CKD_HW, /* capas */ getok /* ok_fn */ }; static MDMINF KEEPINTOUCH = /* AT&T KeepinTouch Card Modem */ { "AT&T KeepinTouch V.32bis Card Modem", /* Name */ "ATP\015", /* pulse command */ "ATT\015", /* tone command */ 35, /* dial_time */ ",", /* pause_chars */ 2, /* pause_time */ #ifdef OS2 /* This used to include &C1&S0&D2+Q0 but that gives ERROR */ "ATQ0E1V1X4&S0&C1&D2\\K5\015", /* wake_str */ #else #ifdef VMS "ATQ0E1X4&S1\\K5\015", /* wake_str */ #else "ATQ0E1X4\\K5\015", /* wake_str */ #endif /* VMS */ #endif /* OS2 */ 0, /* wake_rate */ "OK\015", /* wake_prompt */ "", /* dmode_str */ "", /* dmode_prompt */ "ATD%s\015", /* dial_str */ 0, /* dial_rate */ 1100, /* esc_time */ 43, /* esc_char */ "ATQ0H0\015", /* hup_str */ "AT\\Q3\015", /* hwfc_str */ "AT\\Q1\\X0\015", /* swfc_str */ "AT\\Q0\015", /* nofc_str */ "AT\\N3-J1\015", /* ec_on_str */ "AT\\N1\015", /* ec_off_str */ "AT%C3\"H3\015", /* dc_on_str */ "AT%C0\"H0\015", /* dc_off_str */ "ATS0=1\015", /* aa_on_str */ "ATS0=0\015", /* aa_off_str */ "ATN0\\J0\015", /* sb_on_str */ "ATN1\\J1\015", /* sb_off_str */ "ATM1\015", /* sp_on_str */ "ATM0\015", /* sp_off_str */ "", /* vol1_str */ "", /* vol2_str */ "", /* vol3_str */ "ATX3\015", /* ignoredt */ "", /* ini2 */ 57600L, /* max_speed */ CKD_AT|CKD_SB|CKD_EC|CKD_DC|CKD_HW|CKD_SW, /* capas */ getok /* ok_fn */ }; static MDMINF ROLM_AT = /* Rolm data phone with AT command set */ { "Rolm 244PC or 600 Series with AT Command Set", "", /* pulse command */ "", /* tone command */ 35, /* dial_time */ ",", /* pause_chars */ 2, /* pause_time */ #ifdef OS2 "ATE1Q0V1\015", /* wake_str */ #else "ATQ0\015", /* wake_str */ #endif /* OS2 */ 0, /* wake_rate */ "OK\015", /* wake_prompt */ "", /* dmode_str */ "", /* dmode_prompt */ "ATDT%s\015", /* dial_str -- always Tone */ 0, /* dial_rate */ 1100, /* esc_time */ 43, /* esc_char */ "ATQ0H0\015", /* hup_str */ "", /* hwfc_str */ "", /* swfc_str */ "", /* nofc_str */ "", /* ec_on_str */ "", /* ec_off_str */ "", /* dc_on_str */ "", /* dc_off_str */ "ATS0=1\015", /* aa_on_str */ "ATS0=0\015", /* aa_off_str */ "", /* sb_on_str */ "", /* sb_off_str */ "", /* sp_on_str */ "", /* sp_off_str */ "", /* vol1_str */ "", /* vol2_str */ "", /* vol3_str */ "", /* ignoredt */ "", /* ini2 */ 19200L, /* max_speed */ CKD_AT, /* capas */ getok /* ok_fn */ }; static MDMINF ATLAS = /* Atlas / Newcom ixfC 33.6 */ { "Atlas / Newcom 33600ixfC Data/Fax Modem", /* Name */ "ATP\015", /* pulse command */ "ATT\015", /* tone command */ 35, /* dial_time */ ",", /* pause_chars */ 2, /* pause_time */ #ifdef OS2 "ATZ0&FQ0V1&C1&D2\015", /* wake_str */ #else "ATZ0&FQ0V1\015", /* wake_str */ #endif /* OS2 */ 0, /* wake_rate */ "OK\015", /* wake_prompt */ "", /* dmode_str */ "", /* dmode_prompt */ "ATD%s\015", /* dial_str */ 0, /* dial_rate */ 1100, /* esc_time */ 43, /* esc_char */ "ATQ0H0\015", /* hup_str */ "AT&K3\015", /* hwfc_str */ "AT&K4\015", /* swfc_str */ "AT&K0\015", /* nofc_str */ "AT\"H3\015", /* ec_on_str */ "AT\"H0\015", /* ec_off_str */ "AT%C1\015", /* dc_on_str */ "AT%C0\015", /* dc_off_str */ "ATS0=1\015", /* aa_on_str */ "ATS0=0\015", /* aa_off_str */ "ATN0\\J0\015", /* sb_on_str */ "ATN1\\J1\015", /* sb_off_str */ "ATM1\015", /* sp_on_str */ "ATM0\015", /* sp_off_str */ "ATL1\015", /* vol1_str */ "ATL2\015", /* vol2_str */ "ATL3\015", /* vol3_str */ "ATX3\015", /* ignoredt */ "", /* ini2 */ 115200L, /* max_speed */ CKD_AT|CKD_SB|CKD_EC|CKD_DC|CKD_HW|CKD_SW, /* capas */ getok /* ok_fn */ }; static MDMINF CODEX = { /* Motorola Codex */ "Motorola Codex 326X Series", /* Name - AT&V to see settings */ "ATP\015", /* pulse command */ "ATT\015", /* tone command */ 35, /* dial_time */ ",", /* pause_chars */ 2, /* pause_time */ #ifdef OS2 /* &M0=Async (not sync) */ /* *MM0=Automatic modulation negotiation */ /* *DE22=Automatic data rate */ "ATZQ0E1V1X4Y0*DE22*MM0&C1&M0&S0&D2\015", /* wake_str */ #else #ifdef VMS "ATZQ0E1V1X4Y0*DE22*MM0&C1&M0&S1\015", /* wake_str */ #else "ATZQ0E1V1X4Y0*DE22*MM0&C1&M0\015", /* wake_str */ #endif /* VMS */ #endif /* OS2 */ 0, /* wake_rate */ "OK\015", /* wake_prompt */ "", /* dmode_str */ "", /* dmode_prompt */ "ATD%s\015", /* dial_str */ 0, /* dial_rate */ 1100, /* esc_time */ 43, /* esc_char */ "ATQ0H0\015", /* hup_str */ "AT*MF1*FL3\015", /* hwfc_str */ "AT*MF1*FL1\015", /* swfc_str */ "AT*MF0*FL0\015", /* nofc_str */ "AT*EC0*SM3*SC0\015", /* ec_on_str */ "AT*SM0\015", /* ec_off_str */ "AT*DC1\015", /* dc_on_str */ "AT*DC0\015", /* dc_off_str */ "AT*AA5S0=1\015", /* aa_on_str */ "AT*AA5S0=0\015", /* aa_off_str */ "AT*SC1\015", /* sb_on_str */ "AT*SC0\015", /* sb_off_str */ "ATM1\015", /* sp_on_str */ "ATM0\015", /* sp_off_str */ "ATL1\015", /* vol1_str */ "ATL2\015", /* vol2_str */ "ATL3\015", /* vol3_str */ "ATX3*BD2\015", /* ignoredt */ "", /* ini2 */ 115200L, /* max_speed */ CKD_AT|CKD_SB|CKD_EC|CKD_DC|CKD_HW|CKD_SW, /* capas */ getok /* ok_fn */ }; static MDMINF MT5634ZPX = /* Multitech */ { "Multitech MT5634ZPX", /* name */ "ATP\015", /* pulse command */ "ATT\015", /* tone command */ 35, /* dial_time */ ",", /* pause_chars */ 2, /* pause_time */ #ifdef OS2 "ATE1Q0V1X4&S0&C1&D2&Q0\015", /* wake_str */ #else #ifdef VMS "ATQ0E1X4&S1&Q0\015", /* wake_str */ #else "ATQ0E1X4&Q0\015", /* wake_str */ #endif /* VMS */ #endif /* OS2 */ 0, /* wake_rate */ "OK\015", /* wake_prompt */ "", /* dmode_str */ "", /* dmode_prompt */ "ATD%s\015", /* dial_str */ 0, /* dial_rate */ 1100, /* esc_time */ 43, /* esc_char */ "ATQ0H0\015", /* hup_str */ "AT&K3\015", /* hwfc_str */ "AT&K4\015", /* swfc_str */ "AT&K0\015", /* nofc_str */ "AT\\N3\015", /* ec_on_str */ "AT\\N1\015", /* ec_off_str */ "AT%C1\015", /* dc_on_str */ "AT%C0\015", /* dc_off_str */ "ATS0=1\015", /* aa_on_str */ "ATS0=0\015", /* aa_off_str */ "AT\\J0\015", /* sb_on_str */ "AT\\J1\015", /* sb_off_str (NOT SUPPORTED) */ "ATM1\015", /* sp_on_str */ "ATM0\015", /* sp_off_str */ "ATL1\015", /* vol1_str */ "ATL2\015", /* vol2_str */ "ATL3\015", /* vol3_str */ "ATX3\015", /* ignoredt */ "", /* ini2 */ 115200L, /* max_speed */ CKD_AT|CKD_SB|CKD_EC|CKD_DC|CKD_HW|CKD_SW, /* capas */ getok /* ok_fn */ }; static MDMINF MOTSM56 = /* Motorola SM56 Chipset */ { "Motorola SM56 V.90 chipset", /* name */ "ATP\015", /* pulse command */ "ATT\015", /* tone command */ 35, /* dial_time */ ",", /* pause_chars */ 2, /* pause_time */ #ifdef OS2 "ATQ0V1X4&S0&C1&D2*MM16\015", /* wake_str */ #else #ifdef VMS "ATQ0V1X4&S1&C1&D2*MM16\015", /* wake_str */ #else "ATQ0V1X4&C1&D2*MM16\015", /* wake_str */ #endif /* VMS */ #endif /* OS2 */ 0, /* wake_rate */ "OK\015", /* wake_prompt */ "", /* dmode_str */ "", /* dmode_prompt */ "ATD%s\015", /* dial_str */ 0, /* dial_rate */ 1100, /* esc_time */ 43, /* esc_char */ "ATQ0H0\015", /* hup_str */ "AT\\Q3\015", /* hwfc_str */ "AT\\Q1\015", /* swfc_str */ "AT\\Q0\015", /* nofc_str */ "AT\\N7\015", /* ec_on_str */ "AT\\N1\015", /* ec_off_str */ "AT%C1\015", /* dc_on_str */ "AT%C0\015", /* dc_off_str */ "ATS0=1\015", /* aa_on_str */ "ATS0=0\015", /* aa_off_str */ "AT\\J0\015", /* sb_on_str */ "AT\\J1\015", /* sb_off_str (NOT SUPPORTED) */ "ATM1\015", /* sp_on_str */ "ATM0\015", /* sp_off_str */ "ATL1\015", /* vol1_str */ "ATL2\015", /* vol2_str */ "ATL3\015", /* vol3_str */ "ATX3\015", /* ignoredt */ "", /* ini2 */ 115200L, /* max_speed */ CKD_AT|CKD_SB|CKD_EC|CKD_DC|CKD_HW|CKD_SW, /* capas */ getok /* ok_fn */ }; #endif /* MINIDIAL */ /* END MDMINF STRUCT DEFINITIONS */ /* Table to convert modem numbers to MDMINF struct pointers. The entries MUST be in ascending order by modem number, without any "gaps" in the numbers, and starting from one (1). */ MDMINF *modemp[] = { #ifdef MINIDIAL NULL, /* 0 */ &CCITT, /* 1 */ &HAYES, /* 2 */ &UNKNOWN, /* 3 */ &DUMMY, /* 4 */ &GENERIC, /* 5 */ &ITUTV250 /* 6 */ #else /* Not MINIDIAL */ NULL, /* 0 */ &ATTDTDM, /* 1 */ &ATTISN, /* 2 */ &ATTMODEM, /* 3 */ &CCITT, /* 4 */ #ifdef OLDMODEMS &CERMETEK, /* 5 */ &DF03, /* 6 */ &DF100, /* 7 */ &DF200, /* 8 */ &GDC, /* 9 */ #else NULL, NULL, NULL, NULL, NULL, #endif /* OLDMODEMS */ &HAYES, /* 10 */ #ifdef OLDMODEMS &PENRIL, /* 11 */ &RACAL, /* 12 */ #else NULL, NULL, #endif /* OLDMODEMS */ &UNKNOWN, /* 13 */ #ifdef OLDMODEMS &VENTEL, /* 14 */ &CONCORD, /* 15 */ #else NULL, NULL, #endif /* OLDMODEMS */ &DUMMY, /* 16 */ &ROLM, /* 17 */ #ifdef OLDMODEMS &MICROCOM, /* 18 */ #else NULL, #endif /* OLDMODEMS */ &USR, /* 19 USR Courier and Sportster */ &OLDTB, /* 20 Old Telebits */ &DIGITEL, /* 21 Digitel CCITT */ &H_1200, /* 22 Hayes 1200 */ &H_ULTRA, /* 23 Hayes Ultra */ &H_ACCURA, /* 24 Hayes Optima */ &PPI, /* 25 PPI */ &DATAPORT, /* 26 Dataport */ &BOCA, /* 27 Boca */ &MOTOROLA, /* 28 Motorola UDS MOTOROLA */ NULL, /* 29 Digicomm */ NULL, /* 30 Dynalink */ &INTEL, /* 31 Intel */ &UCOM_AT, /* 32 Microcom in AT mode */ &MULTITECH, /* 33 Multitech */ &SUPRA, /* 34 Supra */ &ZOLTRIX, /* 35 Zoltrix */ &ZOOM, /* 36 Zoom */ &ZYXEL, /* 37 ZyXEL */ &DUMMY, /* 38 TAPI */ &NEWTB, /* 39 New-Telebit */ &MAXTECH, /* 40 MaxTech */ &DUMMY, /* 41 User-defined */ &RWV32, /* 42 Rockwell V.32 */ &RWV32B, /* 43 Rockwell V.32bis */ &RWV34, /* 44 Rockwell V.34 */ &MWAVE, /* 45 IBM Mwave */ &TELEPATH, /* 46 Gateway 2000 Telepath II 28.8 */ &MICROLINK, /* 47 MicroLink modems */ &CARDINAL, /* 48 Cardinal */ &GENERIC, /* 49 Generic high-speed */ &XJACK, /* 50 Megahertz-Xjack */ &SPIRITII, /* 51 QuickComm Spirit II */ &MONTANA, /* 52 Motorola Montana */ &COMPAQ, /* 53 Compaq Data+Fax */ &FUJITSU, /* 54 Fujitsu */ &MHZATT, /* 55 Megahertz AT&T V.34 */ &SUPRASON, /* 56 Suprasonic */ &BESTDATA, /* 57 Best Data */ &ATT1900, /* 58 AT&T Secure Data STU III 1900 */ &ATT1910, /* 59 AT&T Secure Data STU III 1910 */ &KEEPINTOUCH, /* 60 AT&T KeepinTouch */ &USRX2, /* 61 USR XJ-1560 X2 */ &ROLM_AT, /* 62 Rolm with AT command set */ &ATLAS, /* 63 Atlas / Newcom */ &CODEX, /* 64 Motorola Codex */ &MT5634ZPX, /* 65 Multitech MT5634ZPX */ &ULINKV250, /* 66 Microlink V.250 56K */ &ITUTV250, /* 67 Generic ITU-T V.250 */ &RWV90, /* 68 Rockwell V.90 56K */ &SUPRAX, /* 69 Diamond Supra Express V.90 */ &LUCENT, /* 70 Lucent Venus chipset */ &PCTEL, /* 71 PCTel */ &CONEXANT, /* 72 Conexant */ &ZOOMV34, /* 73 Zoom V.34 */ &ZOOMV90, /* 74 Zoom V.90 */ &ZOOMV92, /* 75 Zoom V.92 */ &MOTSM56 /* 76 Motorola SM56 chipset */ #endif /* MINIDIAL */ }; /* * Declare modem names and associated numbers for command parsing, * and also for doing number-to-name translation. * * The entries must be in alphabetical order by modem name. */ struct keytab mdmtab[] = { #ifndef MINIDIAL "3com-usr-megahertz-56k", n_USRX2, CM_INV, "acer-v90", n_RWV90, M_ALIAS, "atlas-newcom-33600ifxC", n_ATLAS, 0, "att-1900-stu-iii", n_ATT1900, 0, "att-1910-stu-iii", n_ATT1910, 0, "att-7300", n_ATTUPC, 0, "att-dataport", n_DATAPORT, 0, "att-dtdm", n_ATTDTDM, 0, "att-isn", n_ATTISN, 0, "att-keepintouch", n_KEEPINTOUCH, 0, "att-switched-net", n_ATTMODEM, 0, "att7300", n_ATTUPC, CM_INV, /* old name */ "attdtdm", n_ATTDTDM, CM_INV, /* old name */ "attisn", n_ATTISN, CM_INV, /* old name */ "attmodem", n_ATTMODEM, CM_INV, /* old name */ "bestdata", n_BESTDATA, 0, "boca", n_BOCA, 0, "cardinal", n_CARDINAL, 0, #endif /* MINIDIAL */ "ccitt-v25bis", n_CCITT, CM_INV, /* Name changed to ITU-T */ #ifndef MINIDIAL #ifdef OLDMODEMS "cermetek", n_CERMETEK, M_OLD, #endif /* OLDMODEMS */ "compaq", n_COMPAQ, 0, #ifdef OLDMODEMS "concord", n_CONCORD, M_OLD, #endif /* OLDMODEMS */ "conexant", n_CONEXANT, 0, "courier", n_USR, CM_INV, "dataport", n_DATAPORT, CM_INV, /* == att-dataport */ #ifdef OLDMODEMS "df03-ac", n_DF03, M_OLD, "df100-series", n_DF100, M_OLD, "df200-series", n_DF200, M_OLD, #endif /* OLDMODEMS */ "digitel-dt22", n_DIGITEL, 0, #endif /* MINIDIAL */ "direct", 0, CM_INV, /* Synonym for NONE */ #ifndef MINIDIAL "fujitsu", n_FUJITSU, 0, "gateway-telepath", n_TELEPATH, 0, #ifdef OLDMODEMS "gdc-212a/ed", n_GDC, M_OLD, "ge", n_GENERIC, CM_INV|CM_ABR, "gen", n_GENERIC, CM_INV|CM_ABR, "gendatacomm", n_GDC, CM_INV, /* Synonym for GDC */ #endif /* OLDMODEMS */ #endif /* MINIDIAL */ "gene", n_GENERIC, CM_INV|CM_ABR, "generic", n_GENERIC, 0, "generic-high-speed",n_GENERIC, CM_INV, "h", n_HAYES, CM_INV|CM_ABR, "ha", n_HAYES, CM_INV|CM_ABR, "hay", n_HAYES, CM_INV|CM_ABR, "haye", n_HAYES, CM_INV|CM_ABR, "hayes", n_HAYES, CM_INV|CM_ABR, /* Hayes 2400 */ #ifndef MINIDIAL "hayes-1200", n_H_1200, 0, #endif /* MINIDIAL */ "hayes-2400", n_HAYES, 0, #ifndef MINIDIAL "hayes-high-speed", n_H_ACCURA, 0, "hayes-accura", n_H_ACCURA, CM_INV, "hayes-optima", n_H_ACCURA, CM_INV, "hayes-ultra", n_H_ULTRA, CM_INV, "hst-courier", n_USR, CM_INV, /* Synonym for COURIER */ "intel", n_INTEL, 0, #endif /* MINIDIAL */ "itu-t-v250", n_ITUTV250, 0, "itu-t-v25bis", n_CCITT, 0, /* New name for CCITT */ "itu-t-v25ter/v250",n_ITUTV250, CM_INV, #ifndef MINIDIAL "lucent", n_LUCENT, 0, "maxtech", n_MAXTECH, 0, "megahertz-att-v34", n_MHZATT, 0, /* Megahertzes */ "megahertz-xjack", n_XJACK, CM_INV|CM_ABR, "megahertz-xjack-33.6", n_XJACK, 0, "megahertz-xjack-56k", n_USRX2, 0, /* 3COM/USR/Megahertz 33.6 PC Card */ "mi", n_MICROCOM, CM_INV|CM_ABR, "mic", n_MICROCOM, CM_INV|CM_ABR, "micr", n_MICROCOM, CM_INV|CM_ABR, "micro", n_MICROCOM, CM_INV|CM_ABR, "microc", n_MICROCOM, CM_INV|CM_ABR, "microco", n_MICROCOM, CM_INV|CM_ABR, "microcom", n_MICROCOM, CM_INV|CM_ABR, "microcom-at-mode", n_UCOM_AT, 0, /* Microcom DeskPorte, etc */ "microcom-sx-mode", n_MICROCOM, 0, /* Microcom AX,QX,SX, native mode */ "microlink", n_MICROLINK, 0, "microlink-v250", n_ULINKV250, 0, "motorola-codex", n_CODEX, 0, "motorola-fastalk", n_MOTOROLA, 0, "motorola-lifestyle",n_MOTOROLA, 0, "motorola-montana", n_MONTANA, 0, "motorola-sm56-v90",n_MOTSM56, 0, "mt5634zpx", n_MT5634ZPX, 0, "multitech", n_MULTI, 0, "mwave", n_MWAVE, 0, #endif /* MINIDIAL */ "none", 0, 0, #ifndef MINIDIAL #ifndef OLDTBCODE "old-telebit", n_TELEBIT, 0, #endif /* OLDTBCODE */ "pctel", n_PCTEL, 0, #ifdef OLDMODEMS "penril", n_PENRIL, M_OLD, #endif /* OLDMODEMS */ "ppi", n_PPI, 0, #ifdef OLDMODEMS "racalvadic", n_RACAL, M_OLD, #endif /* OLDMODEMS */ "rockwell-v32", n_RWV32, 0, "rockwell-v32bis", n_RWV32B, 0, "rockwell-v34", n_RWV34, 0, "rockwell-v90", n_RWV90, 0, "rolm", n_ROLM, CM_INV|CM_ABR, "rolm-244pc", n_ROLMAT, 0, "rolm-600-series", n_ROLMAT, 0, "rolm-dcm", n_ROLM, 0, "smartlink-v90", n_USR, M_ALIAS, "spirit-ii", n_SPIRITII, 0, "sportster", n_USR, M_ALIAS, "sup", n_SUPRA, CM_INV|CM_ABR, "supr", n_SUPRA, CM_INV|CM_ABR, "supra", n_SUPRA, CM_INV|CM_ABR, "supra-express-v90",n_SUPRAX, 0, "suprafaxmodem", n_SUPRA, 0, "suprasonic", n_SUPRASON, 0, #ifdef CK_TAPI "tapi", n_TAPI, 0, #endif /* CK_TAPI */ "te", n_TBNEW, CM_INV|CM_ABR, "tel", n_TBNEW, CM_INV|CM_ABR, "telebit", n_TBNEW, 0, "telepath", n_TELEPATH, CM_INV, #endif /* MINIDIAL */ "unknown", n_UNKNOWN, 0, "user-defined", n_UDEF, 0, #ifndef MINIDIAL "usr", n_USR, CM_INV|CM_ABR, "usr-212a", n_HAYES, CM_INV|M_ALIAS, "usr-courier", n_USR, CM_INV, "usr-megahertz-56k", n_USRX2, 0, "usr-sportster", n_USR, CM_INV, "usr-xj1560-x2", n_USRX2, CM_INV, "usrobotics", n_USR, 0, "v25bis", n_CCITT, CM_INV, /* Name changed to ITU-T */ #ifdef OLDMODEMS "ventel", n_VENTEL, M_OLD, #endif /* OLDMODEMS */ "zoltrix-v34", n_ZOLTRIX, 0, "zoltrix-hsp-v90", n_PCTEL, M_ALIAS, "zoltrix-hcf-v90", n_ITUTV250, 0, "zoo", n_ZOOM, CM_INV|CM_ABR, "zoom", n_ZOOM, CM_INV|CM_ABR, "zoom-v32bis", n_ZOOM, 0, "zoom-v34", n_ZOOMV34, 0, "zoom-v90", n_ZOOMV90, 0, "zoom-v92", n_ZOOMV92, 0, "zyxel", n_ZYXEL, 0, #endif /* MINIDIAL */ "", 0, 0 }; int nmdm = (sizeof(mdmtab) / sizeof(struct keytab)) - 1; /* Number of modems */ #define CONNECTED 1 /* For completion status */ #define D_FAILED 2 #define D_PARTIAL 3 static int tries = 0; static int mdmecho = 0; /* Assume modem does not echo */ static char *p; /* For command strings & messages */ #define LBUFL 200 static char lbuf[LBUFL+4]; char modemmsg[LBUFL+4] = { NUL, NUL }; /* DIAL response from modem */ #ifdef DYNAMIC #define RBUFL 256 static char *rbuf = NULL; #else #define RBUFL 63 static char rbuf[RBUFL+1]; #endif /* DYNAMIC */ #ifdef DYNAMIC #define FULLNUML 256 char *fbuf = NULL; /* For full (prefixed) phone number */ #else #define FULLNUML 100 char fbuf[FULLNUML]; #endif /* DYNAMIC */ static ckjmpbuf sjbuf; #ifdef CK_ANSIC static SIGTYP (*savalrm)(int); /* For saving alarm handler */ static SIGTYP (*savint)(int); /* For saving interrupt handler */ #else static SIGTYP (*savalrm)(); /* For saving alarm handler */ static SIGTYP (*savint)(); /* For saving interrupt handler */ #endif /* CK_ANSIC */ #ifdef CKLOGDIAL static VOID dologdial(s) char *s; { char buf2[16]; char * r = NULL; int x, m, n; extern char cxlogbuf[], uidbuf[], myhost[]; if (!s) s = ""; if ((x = strlen(s)) > 0) { /* Replace spaces by underscores */ r = (char *)malloc(x+1); if (r) { int i; for (i = 0; i <= x; i++) { if (s[i] != 0 && s[i] <= SP) r[i] = '_'; else r[i] = s[i]; } s = r; } } p = ckdate(); n = ckstrncpy(cxlogbuf,p,CXLOGBUFL); if (!uidbuf[0]) { debug(F100,"dologdial uidbuf empty","",0); ckstrncpy(uidbuf,(char *)whoami(),UIDBUFLEN); } m = strlen(uidbuf)+strlen(myhost)+strlen(ttname)+strlen(s)+strlen(buf2)+32; if (n+m < CXLOGBUFL-1) { p = cxlogbuf+n; if (diallcc && diallac) { buf2[0] = '+'; ckmakmsg(&buf2[1],15,diallcc,"(",diallac,")"); } else { ckstrncpy(buf2,"Unknown",16); } sprintf(p," %s %s T=DIAL H=%s D=%s N=%s O=%s ", /* safe (prechecked) */ uidbuf, ckgetpid(), myhost, ttname, s, buf2 ); debug(F110,"dologdial cxlogbuf",cxlogbuf,0); } else sprintf(p,"LOGDIAL BUFFER OVERFLOW"); if (r) free(r); } #endif /* CKLOGDIAL */ #ifndef MINIDIAL #ifdef COMMENT static VOID xcpy(to,from,len) /* Copy the given number of bytes */ register char *to, *from; register unsigned int len; { while (len--) *to++ = *from++; } #endif /* COMMENT */ #endif /* MINIDIAL */ static SIGTYP #ifdef CK_ANSIC dialtime(int foo) /* Timer interrupt handler */ #else dialtime(foo) int foo; /* Timer interrupt handler */ #endif /* CK_ANSIC */ /* dialtime */ { fail_code = F_TIME; /* Failure reason = timeout */ debug(F100,"dialtime caught SIGALRM","",0); #ifdef BEBOX #ifdef BE_DR_7 alarm_expired(); #endif /* BE_DR_7 */ #endif /* BEBOX */ #ifdef OS2 signal(SIGALRM, dialtime); #endif /* OS2 */ #ifdef __EMX__ signal(SIGALRM, SIG_ACK); /* Needed for OS/2 */ #endif /* __EMX__ */ #ifdef OSK /* OS-9 */ /* We are in an intercept routine but do not perform a F$RTE (done implicitly by RTS), so we have to decrement the sigmask as F$RTE does. Warning: longjump only restores the CPU registers, NOT the FPU registers. So, don't use FPU at all or at least don't use common FPU (double or float) register variables. */ sigmask(-1); #endif /* OSK */ #ifdef NTSIG if (foo == SIGALRM) PostAlarmSigSem(); else PostCtrlCSem(); #else /* NTSIG */ #ifdef NT cklongjmp(ckjaddr(sjbuf),1); #else /* NT */ cklongjmp(sjbuf,1); #endif /* NT */ #endif /* NTSIG */ /* NOTREACHED */ SIGRETURN; } static SIGTYP #ifdef CK_ANSIC dialint(int foo) /* Keyboard interrupt handler */ #else dialint(foo) int foo; #endif /* CK_ANSIC */ /* dialint */ { fail_code = F_INT; debug(F100,"dialint caught SIGINT","",0); #ifdef OS2 signal(SIGINT, dialint); debug(F100,"dialint() SIGINT caught -- dialint restored","",0) ; #endif /* OS2 */ #ifdef __EMX__ signal(SIGINT, SIG_ACK); /* Needed for OS/2 */ #endif /* __EMX__ */ #ifdef OSK /* OS-9, see comment in dialtime() */ sigmask(-1); #endif /* OSK */ #ifdef NTSIG PostCtrlCSem() ; #ifdef CK_TAPI PostTAPIConnectSem(); PostTAPIAnswerSem(); #endif /* CK_TAPI */ #else /* NTSIG */ #ifdef NT cklongjmp(ckjaddr(sjbuf),1); #else /* NT */ cklongjmp(sjbuf,1); #endif /* NT */ #endif /* NT */ SIGRETURN; } /* Routine to read a character from communication device, handling TELNET protocol negotiations in case we're connected to the modem through a TCP/IP TELNET modem server. */ static int ddinc(n) int n; { #ifdef TNCODE int c = 0; int done = 0; debug(F101,"ddinc entry n","",n); while (!done) { c = ttinc(n); /* debug(F000,"ddinc","",c); */ if (c < 0) return(c); #ifndef OS2 if ((c == IAC) && network && IS_TELNET()) { switch (tn_doop((CHAR)(c & 0xff),duplex,ttinc)) { case 2: duplex = 0; continue; case 1: duplex = 1; default: continue; } } else done = 1; #else /* OS2 */ done = !(c == IAC && network && IS_TELNET()); scriptwrtbuf(c); /* TELNET negotiations handled by emulator */ #endif /* OS2 */ } return(c & 0xff); #else /* TNCODE */ return(ttinc(n)); #endif /* TNCODE */ } static VOID ttslow(s,millisec) char *s; int millisec; { /* Output s-l-o-w-l-y */ #ifdef TCPSOCKET extern int tn_nlm, tn_b_nlm; #endif /* TCPSOCKET */ debug(F111,"ttslow",s,millisec); if (dialdpy && (duplex || !mdmecho)) { /* Echo the command in case modem */ printf("%s\n",s); /* isn't echoing commands. */ #ifdef OS2 { char *s2 = s; /* Echo to emulator */ while (*s2) { scriptwrtbuf((USHORT)*s2++); } scriptwrtbuf((USHORT)CR); scriptwrtbuf((USHORT)LF); } #endif /* OS2 */ } for (; *s; s++) { ttoc(*s); #ifdef TCPSOCKET if (*s == CR && network && IS_TELNET()) { if (!TELOPT_ME(TELOPT_BINARY) && tn_nlm != TNL_CR) ttoc((char)((tn_nlm == TNL_CRLF) ? LF : NUL)); else if (TELOPT_ME(TELOPT_BINARY) && (tn_b_nlm == TNL_CRLF || tn_b_nlm == TNL_CRNUL)) ttoc((char)((tn_b_nlm == TNL_CRLF) ? LF : NUL)); } #endif /* TCPSOCKET */ if (millisec > 0) msleep(millisec); } } /* * Wait for a string of characters. * * The characters are waited for individually, and other characters may * be received "in between". This merely guarantees that the characters * ARE received, and in the order specified. */ static VOID waitfor(s) char *s; { CHAR c, x; while ((c = *s++)) { /* while more characters remain... */ do { /* wait for the character */ x = (CHAR) (ddinc(0) & 0177); debug(F000,"dial waitfor got","",x); if (dialdpy) { if (x != LF) conoc(x); if (x == CR) conoc(LF); } } while (x != c); } } static int didweget(s,r) char *s, *r; { /* Looks in string s for response r */ int lr = (int)strlen(r); /* 0 means not found, 1 means found it */ int i; debug(F110,"didweget",r,0); debug(F110," in",s,0); for (i = (int)strlen(s)-lr; i >= 0; i--) if ( s[i] == r[0] ) if ( !strncmp(s+i,r,lr) ) return( 1 ); return( 0 ); } /* R E S E T -- Reset alarms, etc. on exit. */ static VOID dreset() { debug(F100,"dreset resetting alarm and signal handlers","",0); alarm(0); signal(SIGALRM,savalrm); /* restore alarm handler */ signal(SIGINT,savint); /* restore interrupt handler */ debug(F100,"dreset alarm and signal handlers reset","",0); } /* Call this routine when the modem reports that it has connected at a certain speed, giving that speed as the argument. If the connection speed is not the same as Kermit's current communication speed, AND the modem interface speed is not locked (i.e. DIAL SPEED-MATCHING is not ON), then change the device speed to the one given. */ static VOID #ifdef CK_ANSIC spdchg(long s) #else spdchg(s) long s; #endif /* CK_ANSIC */ /* spdchg */ { int s2; if (!mdmspd) /* If modem interface speed locked, */ return; /* don't do this. */ if (speed != s) { /* Speeds differ? */ s2 = s / 10L; /* Convert to cps expressed as int */ if (ttsspd(s2) < 0) { /* Change speed. */ printf(" WARNING - speed change to %ld failed.\r\n",s); } else { printf(" Speed changed to %ld.\r\n",s); speed = s; /* Update global speed variable */ } } } /* Display all characters received from modem dialer through this routine, for consistent handling of carriage returns and linefeeds. */ static VOID #ifdef CK_ANSIC dialoc(char c) #else dialoc(c) char c; #endif /* CK_ANSIC */ { /* dialoc */ /* Dial Output Character */ if (dialdpy) { if (c != LF) conoc(c); /* Don't echo LF */ if (c == CR) conoc(LF); /* Echo CR as CRLF */ } } #ifndef NOSPL char * getdm(x) int x; { /* Return dial modifier */ MDMINF * mp; int m; int ishayes = 0; m = mdmtyp; if (m < 1) if (mdmsav > -1) m = mdmsav; if (m < 1) return(""); #ifndef MINIDIAL if (m == n_TAPI) m = n_HAYES; #endif /* MINIDIAL */ mp = modemp[m]; ishayes = (dialcapas ? dialcapas : mp->capas) & CKD_AT; switch (x) { case VN_DM_LP: return(ishayes ? "," : ""); case VN_DM_SP: #ifdef MINIDIAL return(""); #else return(m == n_USR ? "/" : ""); #endif /* MINIDIAL */ case VN_DM_PD: return(ishayes ? "P" : ""); case VN_DM_TD: return(ishayes ? "T" : ""); case VN_DM_WA: return(ishayes ? "@" : ""); case VN_DM_WD: return(ishayes ? "W" : ""); case VN_DM_RC: return(ishayes ? ";" : ""); case VN_DM_HF: return(ishayes ? "!" : ""); case VN_DM_WB: return(ishayes ? "$" : ""); } return(""); } #endif /* NOSPL */ static VOID getdialmth() { if (dialmauto && diallcc) { /* If DIAL METHOD AUTO... */ int i; /* and we know our area code... */ for (i = 0; i < ndialtocc; i++) { /* First check Tone countries list */ if (!strcmp(dialtocc[i],diallcc)) { dialmth = XYDM_T; break; } } for (i = 0; i < ndialpucc; i++) { /* Then Pulse countries list */ if (!strcmp(dialpucc[i],diallcc)) { dialmth = XYDM_P; break; } } } } VOID /* Get dialing defaults from environment */ getdialenv() { char *p = NULL; int i, x; makestr(&p,getenv("K_DIAL_DIRECTORY")); if (p) { int i; xwords(p,(MAXDDIR - 2),dialdir,0); for (i = 0; i < (MAXDDIR - 1); i++) { if (!dialdir[i+1]) break; else dialdir[i] = dialdir[i+1]; } ndialdir = i; } xmakestr(&diallcc,getenv("K_COUNTRYCODE")); /* My country code */ xmakestr(&dialixp,getenv("K_LD_PREFIX")); /* My long-distance prefix */ xmakestr(&dialldp,getenv("K_INTL_PREFIX")); /* My international prefix */ xmakestr(&dialldp,getenv("K_TF_PREFIX")); /* Ny Toll-free prefix */ #ifndef NOICP p = getenv("K_DIAL_METHOD"); /* Local dial method */ if (p) if (*p) { extern struct keytab dial_m[]; extern int ndial_m; i = lookup(dial_m,p,ndial_m,&x); if (i > -1) { if (i == XYDM_A) { dialmauto = 1; dialmth = XYDM_D; } else { dialmauto = 0; dialmth = i; } } } #endif /* NOICP */ p = NULL; xmakestr(&p,getenv("K_TF_AREACODE")); /* Toll-free areacodes */ if (p) { int i; xwords(p,7,dialtfc,0); for (i = 0; i < 8; i++) { if (!dialtfc[i+1]) break; else dialtfc[i] = dialtfc[i+1]; } ntollfree = i; free(p); } for (i = 0; i < MAXTPCC; i++) { /* Clear Tone/Pulse country lists */ dialtocc[i] = NULL; dialpucc[i] = NULL; } for (i = 0; i < MAXTPCC; i++) { /* Init Tone country list */ if (tonecc[i]) makestr(&(dialtocc[i]),tonecc[i]); else break; } ndialtocc = i; for (i = 0; i < MAXTPCC; i++) { /* Init Pulse country list */ if (pulsecc[i]) makestr(&(dialpucc[i]),pulsecc[i]); else break; } ndialpucc = i; if (diallcc) { /* Have country code */ if (!strcmp(diallcc,"1")) { /* If it's 1 */ if (!dialldp) /* Set these prefixes... */ makestr(&dialldp,"1"); if (!dialtfp) makestr(&dialtfp,"1"); if (!dialixp) makestr(&dialixp,"011"); if (ntollfree == 0) { /* Toll-free area codes */ if ((dialtfc[0] = malloc(4))) { ckstrncpy(dialtfc[0],"800",4); /* 1970-something */ ntollfree++; if ((dialtfc[1] = malloc(4))) { ckstrncpy(dialtfc[1],"888",4); /* 1996 */ ntollfree++; if ((dialtfc[2] = malloc(4))) { ckstrncpy(dialtfc[2],"877",4); /* 5 April 1998 */ ntollfree++; if ((dialtfc[3] = malloc(4))) { ckstrncpy(dialtfc[3],"866",4); /* 2000? */ ntollfree++; } } } } } } else if (!strcmp(diallcc,"358") && ((int) strcmp(zzndate(),"19961011") > 0) ) { /* Finland */ if (!dialldp) /* Long-distance prefix */ makestr(&dialldp,"9"); if (!dialixp) /* International dialing prefix */ makestr(&dialixp,"990"); } else { /* Not NANP or Finland */ if (!dialldp) makestr(&dialldp,"0"); if (!dialixp) makestr(&dialixp,"00"); } } xmakestr(&diallac,getenv("K_AREACODE")); xmakestr(&dialpxo,getenv("K_PBX_OCP")); xmakestr(&dialpxi,getenv("K_PBX_ICP")); p = getenv("K_PBX_XCH"); #ifdef COMMENT xmakestr(&dialpxx,p); #else if (p) if (*p) { char * s = NULL; char * pp[MAXPBXEXCH+2]; makestr(&s,p); /* Make a copy for poking */ if (s) { xwords(s,MAXPBXEXCH+1,pp,0); /* Note: pp[] is 1-based. */ for (i = 0; i <= MAXPBXEXCH; i++) { if (!pp[i+1]) break; makestr(&(dialpxx[i]),pp[i+1]); ndialpxx++; } makestr(&s,NULL); /* Free poked copy */ } } #endif /* COMMENT */ } static int dialfail(x) int x; { char * s; fail_code = x; debug(F101,"ckudial dialfail","",x); dreset(); /* Reset alarm and signal handlers */ printf("%s Failure: ", func_code == 0 ? "DIAL" : "ANSWER"); if (dialdpy) { /* If showing progress */ debug(F100,"dial display is on","",0); p = ck_time(); /* get current time; */ if (*p) printf("%s: ",p); } switch (fail_code) { /* Type of failure */ case F_TIME: /* Timeout */ if (dial_what == DW_INIT) printf ("Timed out while trying to initialize modem.\n"); else if (dial_what == DW_DIAL) printf ("%s interval expired.\n", func_code == 0 ? "DIAL TIMEOUT" : "ANSWER timeout"); else printf("Timeout.\n"); fflush(stdout); if (mdmcapas & CKD_AT) ttoc('\015'); /* Send CR to interrupt dialing */ /* Some Hayes modems don't fail with BUSY on busy lines */ dialsta = DIA_TIMO; debug(F110,"dial","timeout",0); break; case F_INT: /* Dialing interrupted */ printf ("Interrupted.\n"); fflush(stdout); #ifndef NOXFER interrupted = 1; #endif /* NOXFER */ debug(F111,"dial","interrupted",mdmcapas & CKD_AT); if (mdmcapas & CKD_AT) ttoc('\015'); /* Send CR to interrupt dialing */ dialsta = DIA_INTR; break; case F_MODEM: /* Modem detected a failure */ debug(F111,"dialfail()","lbuf",lbuf); if (lbuf && *lbuf) { printf(" \""); for (s = lbuf; *s; s++) if (isprint(*s)) putchar(*s); /* Display printable reason */ printf ("\""); } else printf(func_code == 0 ? " Call not completed." : " Call did not come in." ); printf("\n"); debug(F110,"dial",lbuf,0); if (dialsta < 0) dialsta = DIA_UNSP; break; case F_MINIT: /* Failure to initialize modem */ printf ("Error initializing modem.\n"); debug(F110,"dial","modem init",0); dialsta = DIA_NOIN; break; default: printf("unknown\n"); debug(F110,"dial","unknown",0); fflush(stdout); if (mdmcapas & CKD_AT) ttoc('\015'); /* Send CR to interrupt dialing */ dialsta = DIA_INTR; } #ifdef DYNAMIC if (rbuf) free(rbuf); rbuf = NULL; if (fbuf) free(fbuf); fbuf = NULL; #endif /* DYNAMIC */ if (dialsta < 0) dialsta = DIA_UERR; /* Set failure code */ return(0); /* Return zero (important) */ } /* C K D I A L -- Dial up the remote system */ /* Returns 1 if call completed, 0 otherwise */ static int mdmwait, mdmstat = 0; #ifndef CK_TAPI static #endif /* CK_TAPI */ int waitct; int mdmwaitd = 10 ; /* dialtmo / mdmwait difference */ static char c; static char *telnbr; static int wr = 0; /* wr = wake rate */ static char * ws; /* ws = wake string */ static char * xnum = NULL; static int inited = 0; static SIGTYP #ifdef CK_ANSIC _dodial(void * threadinfo) #else /* CK_ANSIC */ _dodial(threadinfo) VOID * threadinfo; #endif /* CK_ANSIC */ /* _dodial */ { char c2; char *dcmd, *s, *flocmd = NULL; int x = 0, n = F_TIME; #ifdef NTSIG signal( SIGINT, dialint ); if (threadinfo) { /* Thread local storage... */ TlsSetValue(TlsIndex,threadinfo); } #endif /* NTSIG */ dcmd = dialcmd ? dialcmd : mp->dial_str; if ((int)strlen(dcmd) + (int)strlen(telnbr) > (LBUFL - 2)) { printf("DIAL command + phone number too long!\n"); dreset(); #ifdef DYNAMIC if (rbuf) free(rbuf); rbuf = NULL; if (fbuf) free(fbuf); fbuf = NULL; #endif /* DYNAMIC */ #ifdef NTSIG ckThreadEnd(threadinfo); #endif /* NTSIG */ SIGRETURN; /* No conversation with modem to complete dialing */ } makestr(&xnum,telnbr); getdialmth(); /* Get dial method */ #ifdef CK_ATDT /* Combine the SET DIAL METHOD command with the DIAL command string */ if (!dialcmd && /* Using default DIAL command */ (mdmcapas & CKD_AT) && /* AT command set only */ ((dialmth == XYDM_T && !dialtone) || /* and using default */ (dialmth == XYDM_P && !dialpulse))) { /* modem commands... */ char c; debug(F110,"dial atdt xnum 1",xnum,0); s = dcmd; debug(F110,"dial atdt s",s,0); if (*telnbr != 'T' && *telnbr != 'P' && *telnbr != 't' && *telnbr != 'p' && !ckstrcmp(s,"atd",3,0) && s[3] != 'T' && s[3] != 'P' && s[3] != 't' && s[3] != 'p') { char xbuf[200]; c = (dialmth == XYDM_T) ? 'T' : 'P'; if (islower(s[0])) c = tolower(c); if ((int)strlen(telnbr) < 199) { sprintf(xbuf,"%c%s",c,telnbr); makestr(&xnum,xbuf); } } } #endif /* CK_ATDT */ debug(F111,"_dodial",xnum,xredial); /* Hang up the modem (in case it wasn't "on hook") */ /* But only if SET DIAL HANGUP ON... */ if (!xredial) { /* Modem not initalized yet. */ inited = 0; } if (!xredial || !inited) { if (dialhup() < 0) { /* Hangup first */ debug(F100,"_dodial dialhup failed","",0); #ifndef MINIDIAL if (mdmcapas & CKD_TB) /* Telebits might need a BREAK */ ttsndb(); /* first. */ #endif /* MINIDIAL */ if (dialhng && dialsta != DIA_PART) { /* If hangup failed, */ ttclos(0); /* close and reopen the device. */ if (ttopen(ttname,&local,mymdmtyp,0) < 0) { printf("Sorry, Can't hang up communication device.\n"); printf("Try 'set line %s' again.\n",ttname); dialsta = DIA_HANG; #ifdef DYNAMIC if (rbuf) free(rbuf); rbuf = NULL; if (fbuf) free(fbuf); fbuf = NULL; #endif /* DYNAMIC */ dreset(); #ifdef NTSIG ckThreadEnd(threadinfo); #endif /* NTSIG */ SIGRETURN; } } } inited = 0; /* We hung up so must reinit */ } #ifndef MINIDIAL /* Don't start talking to Rolm too soon */ if (mymdmtyp == n_ROLM && dialsta != DIA_PART) msleep(500); #endif /* MINIDIAL */ if (dialsta != DIA_PART /* Some initial setups. */ #ifndef MINIDIAL && mymdmtyp != n_ATTUPC #endif /* MINIDIAL */ ) { fail_code = F_MINIT; /* Default failure code */ dial_what = DW_INIT; /* What I'm Doing Now */ if (dialdpy) { /* If showing progress, */ p = ck_time(); /* get timestamp. */ if (!inited) if (*p) printf(" Initializing: %s...\n",p); } } #ifndef MINIDIAL #ifdef ATT7300 if (mymdmtyp == n_ATTUPC) { /* For ATT7300/Unix PC's with their special internal modem. Whole dialing process is handled right here, an exception to the normal structure. Timeout and user interrupts are enabled during dialing. attdial() is in file ckutio.c. - jrd */ _PROTOTYP( int attdial, (char *, long, char *) ); fail_code = F_MODEM; /* Default failure code */ dial_what = DW_DIAL; if (dialdpy) { /* If showing progress */ p = ck_time(); /* get current time; */ if (*p) printf(" Dialing: %s...\n",p); } alarm(waitct); /* Set alarm */ if (attdial(ttname,speed,telnbr)) { /* dial internal modem */ dreset(); /* reset alarms, etc. */ printf(" Call failed.\r\n"); dialhup(); /* Hangup the call */ #ifdef DYNAMIC if (rbuf) free(rbuf); rbuf = NULL; if (fbuf) free(fbuf); fbuf = NULL; #endif /* DYNAMIC */ dialsta = DIA_UERR; #ifdef NTSIG ckThreadEnd(threadinfo); #endif /* NTSIG */ SIGRETURN; /* return failure */ } dreset(); /* reset alarms, etc. */ ttpkt(speed,FLO_DIAX,parity); /* cancel dialing ioctl */ if (!quiet && !backgrd) { if (dialdpy) { printf("\n"); printf(" Call complete.\r\n"); } else if (modemmsg[0]) printf(" Call complete: \"%s\".\r\n",(char *)modemmsg); else printf(" Call complete.\r\n"); } #ifdef CKLOGDIAL dologdial(telnbr); #endif /* CKLOGDIAL */ dialsta = DIA_OK; #ifdef DYNAMIC if (rbuf) free(rbuf); rbuf = NULL; if (fbuf) free(fbuf); fbuf = NULL; #endif /* DYNAMIC */ #ifdef NTSIG ckThreadEnd(threadinfo); #endif /* NTSIG */ SIGRETURN; /* No conversation with modem to complete dialing */ } else #endif /* ATT7300 */ #ifdef CK_TAPI if (tttapi && !tapipass) { /* TAPI Dialing */ switch (func_code) { case 0: /* Dial */ if (cktapidial(telnbr)) { fail_code = 0; if (partial) { dialsta = DIA_PART; } else { dialsta = DIA_OK; speed = ttgspd(); } } else { if (dialsta == DIA_PART) cktapihangup(); if (!fail_code) fail_code = F_MODEM; dialsta = DIA_TAPI; } break; case 1: { /* Answer */ long strttime = time((long *)NULL); long diff = 0; do { if (dialatmo > 0) { strttime += diff; waitct -= diff; } fail_code = 0; if (cktapianswer()) { /* SUCCESS */ dialsta = DIA_OK; speed = ttgspd(); break; } else { /* FAILURE */ if (fail_code) { dialsta = DIA_TAPI; break; } else { fail_code = F_MODEM; dialsta = DIA_TAPI; } } if (dialatmo > 0) { diff = time((long *)NULL) - strttime; } } while ((dialatmo > 0) ? (diff < waitct) : 1); break; } } #ifdef NTSIG ckThreadEnd(threadinfo); #endif /* NTSIG */ SIGRETURN; } else #endif /* CK_TAPI */ #endif /* MINIDIAL */ /* Modems with AT command set... */ if ((mdmcapas & CKD_AT) && dialsta != DIA_PART) { if (dialpace > -1) /* Set intercharacter pacing */ wr = dialpace; else wr = mp->wake_rate; if (dialini) /* Get wakeup/init string */ ws = dialini; else ws = mp->wake_str; #ifdef COMMENT if (!ws) ws = "\015"; /* If none, use CR */ #endif /* COMMENT */ /* First get the modem's attention and enable result codes */ for (tries = 0; tries < 5; tries++) { /* Send short command */ if (tries > 0) { ttoc('\015'); /* AT must go first for speed */ msleep(wr); /* detection. */ } if (mymdmtyp == n_GENERIC) /* Force word result codes */ ttslow("ATQ0V1\015",wr); /* for generic modem type */ else ttslow("ATQ0\015",wr); mdmstat = getok(tries < 2 ? 2 : tries, 1); /* Get response */ if (mdmstat > 0) break; /* OK - done */ if (dialdpy && tries > 0) { printf("\r\n No response from modem"); if (tries == 4) { printf(".\r\n"); dialsta = DIA_NRSP; #ifdef DYNAMIC if (rbuf) free(rbuf); rbuf = NULL; if (fbuf) free(fbuf); fbuf = NULL; #endif /* DYNAMIC */ #ifdef NTSIG ckThreadEnd(threadinfo); #endif /* NTSIG */ SIGRETURN; /* return failure */ } printf(", retrying%s...\r\n", (tries > 1) ? " again" : ""); fflush(stdout); } ttflui(); switch (tries) { case 0: msleep(100); break; case 1: ttsndb(); break; default: if (network) { ttsndb(); } else { if (tries == 2) { tthang(); ttflui(); } else { mdmhup(); } inited = 0; } } fflush(stdout); } debug(F101,"_dodial ATQ0 mdmstat","",mdmstat); if (xredial && inited) { /* Redialing... */ ttoc('\015'); /* Cancel previous */ msleep(250); /* Wait a bit */ #ifdef COMMENT /* This wasn't the problem... */ ttflui(); /* Clear out stuff from modem setup */ ttslow("ATS7=60\015",wr); /* Redo carrier wait */ getok(4,1); /* Get response */ #endif /* COMMENT */ alarm(0); /* Just in case... */ ttflui(); /* Clear out stuff from modem setup */ goto REDIAL; /* Skip setup - we already did it */ } /* Do flow control next because a long init string echoing back could cause data overruns, causing us to miss the OK, or (worse) to get out of sync entirely. */ x = 0; /* User said SET DIAL FLOW RTS/CTS */ if (dialfc == FLO_RTSC || /* Even if Kermit's FLOW isn't... */ (dialfc == FLO_AUTO && flow == FLO_RTSC)) { if (dialhwfc) { /* User-defined HWFC string */ if (*dialhwfc) { x = 1; flocmd = dialhwfc; } } else if ((mdmcapas & CKD_HW) && *(mp->hwfc_str)) { x = 1; flocmd = mp->hwfc_str; } } else if (dialfc == FLO_XONX || /* User said SET DIAL FLOW SOFT */ (dialfc == FLO_AUTO && flow == FLO_XONX)) { if (dialswfc) { if (*dialswfc) { x = 1; flocmd = dialswfc; } } else if ((mdmcapas & CKD_SW) && *(mp->swfc_str)) { x = 1; flocmd = mp->swfc_str; } } else if (dialfc == FLO_NONE) { /* User said SET DIAL FLOW NONE */ if (dialnofc) { if (*dialnofc) { x = 1; flocmd = dialnofc; } } else if (mp->nofc_str && *(mp->nofc_str)) { x = 1; flocmd = mp->nofc_str; } } if (x) { /* Send the flow control command */ debug(F110,"_dodial flocmd",flocmd,0); for (tries = 4; tries > 0; tries--) { /* Send the command */ ttslow(flocmd,wr); mdmstat = getok(5,1); if (mdmstat > 0) break; if (dialdpy && tries > 1) printf(" No response from modem, retrying%s...\n", (tries < 4) ? " again" : ""); } #ifdef CK_TTSETFLOW #ifdef CK_RTSCTS /* So far only ckutio.c has ttsetflow(). We have just told the modem to turn on RTS/CTS flow control and the modem has said OK. But we ourselves have not turned it on yet because of the disgusting ttpkt(...FLO_DIAL...) hack. So now, if the computer does not happen to be asserting RTS, the modem will no longer send characters to it. So at EXACTLY THIS POINT, we must enable RTS/CTS in the device driver. */ if (dialfc == FLO_RTSC || (dialfc == FLO_AUTO && flow == FLO_RTSC)) { ttsetflow(FLO_RTSC); } #endif /* CK_RTSCTS */ #endif /* CK_TTSETFLOW */ } ttflui(); /* Clear out stuff from modem setup */ msleep(250); if (!ws) goto xdialec; /* No init string */ if (!*ws) goto xdialec; for (tries = 4; tries > 0; tries--) { /* Send init string */ ttslow(ws,wr); mdmstat = getok(4,1); /* Get response */ if (mdmstat > 0) break; if (dialdpy && tries > 1) printf(" No response from modem, retrying%s...\n", (tries < 4) ? " again" : ""); } debug(F101,"_dodial wake_str mdmstat","",mdmstat); if (mdmstat < 1) { /* Initialized OK? */ dialfail(F_MINIT); /* No, fail. */ #ifdef NTSIG ckThreadEnd(threadinfo); #endif /* NTSIG */ SIGRETURN; } #ifndef MINIDIAL } else if (mymdmtyp == n_ATTDTDM && dialsta != DIA_PART) { /* AT&T ... */ ttsndb(); /* Send BREAK */ #endif /* MINIDIAL */ } else if (dialsta != DIA_PART) { /* All others */ /* Place modem into command mode */ ws = dialini ? dialini : mp->wake_str; if (ws && (int)strlen(ws) > 0) { debug(F111,"_dodial default, wake string", ws, wr); ttslow(ws, wr); } else debug(F100,"_dodial no wake_str","",0); if (mp->wake_prompt && (int)strlen(mp->wake_prompt) > 0) { debug(F110,"_dodial default, waiting for wake_prompt", mp->wake_prompt,0); alarm(10); waitfor(mp->wake_prompt); alarm(0); } else debug(F100,"_dodial no wake_prompt","",0); } /* Handle error correction, data compression, and flow control... */ xdialec: if (dialsta != DIA_PART) { alarm(0); /* Turn off alarm */ debug(F100,"_dodial got wake prompt","",0); msleep(500); /* Allow settling time */ /* Enable/disable error-correction */ x = 0; if (dialec) { /* DIAL ERROR-CORRECTION is ON */ if (dialecon) { /* SET DIAL STRING ERROR-CORRECTION */ if (*dialecon) { x = 1; ttslow(dialecon, wr); } } else if ((mdmcapas & CKD_EC) && *(mp->ec_on_str)) { x = 1; ttslow(mp->ec_on_str, wr); } #ifdef COMMENT else printf( "WARNING - I don't know how to turn on EC for this modem\n" ); #endif /* COMMENT */ } else { if (dialecoff) { /* DIAL ERROR-CORRECTION OFF */ if (*dialecoff) { x = 1; ttslow(dialecoff, wr); } } else if ((mdmcapas & CKD_EC) && *(mp->ec_off_str)) { x = 1; ttslow(mp->ec_off_str, wr); } #ifdef COMMENT else printf( "WARNING - I don't know how to turn off EC for this modem\n" ); #endif /* COMMENT */ } /* debug(F101,"ckudia xx_ok","",xx_ok); */ if (x && xx_ok) { /* Look for OK response */ debug(F100,"ckudia calling xx_ok for EC","",0); x = (*xx_ok)(5,1); debug(F101,"ckudia xx_ok","",x); if (x < 0) { printf("WARNING - Trouble enabling error-correction.\n"); printf( " Likely cause: Your modem is an RPI model, which does not have built-in\n"); printf(" error correction and data compression."); } } /* Enable/disable data compression */ if (x > 0) x = 0; if (dialdc) { if (x < 0 || !dialec) { printf( "WARNING - You can't have compression without error correction.\n"); } else if (dialdcon) { /* SET DIAL STRING ... */ if (*dialdcon) { x = 1; ttslow(dialdcon, wr); } } else if ((mdmcapas & CKD_DC) && *(mp->dc_on_str)) { x = 1; ttslow(mp->dc_on_str, wr); } #ifdef COMMENT else printf( "WARNING - I don't know how to turn on DC for this modem\n" ); #endif /* COMMENT */ } else { if (dialdcoff) { if (*dialdcoff) { x = 1; ttslow(dialdcoff, wr); } } else if ((mdmcapas & CKD_DC) && *(mp->dc_off_str)) { x = 1; ttslow(mp->dc_off_str, wr); } #ifdef COMMENT else printf( "WARNING - I don't know how to turn off compression for this modem\n" ); #endif /* COMMENT */ } if (x && xx_ok) { /* Look for OK response */ x = (*xx_ok)(5,1); if (x < 0) printf("WARNING - Trouble enabling compression\n"); } } #ifndef NOXFER #ifndef MINIDIAL if (mdmcapas & CKD_KS && dialsta != DIA_PART) { /* Kermit spoof */ int r; /* Register */ char tbcmdbuf[64]; /* Command buffer */ switch (mymdmtyp) { case n_MICROCOM: /* Microcoms in SX mode */ if (dialksp) sprintf(tbcmdbuf,"APM1;KMC%d\015",stchr); /* safe */ else sprintf(tbcmdbuf,"APM0\015"); /* safe */ ttslow(tbcmdbuf, MICROCOM.wake_rate); alarm(3); waitfor(mp->wake_prompt); alarm(0); break; case n_TELEBIT: /* Old and new Telebits */ case n_TBNEW: if (!dialksp) { sprintf(tbcmdbuf,"ATS111=0\015"); /* safe */ } else { switch (parity) { /* S111 value depends on parity */ case 'e': r = 12; break; case 'm': r = 13; break; case 'o': r = 11; break; case 's': r = 14; break; case 0: default: r = 10; break; } sprintf(tbcmdbuf,"ATS111=%d S112=%d\015",r,stchr); /* safe */ } ttslow(tbcmdbuf, wr); /* Not all Telebit models have the Kermit spoof, so ignore response. */ if (xx_ok) { /* Get modem's response */ x = (*xx_ok)(5,1); } } } #endif /* MINIDIAL */ #endif /* NOXFER */ /* Speaker */ if (mymdmtyp != n_GENERIC && (mdmcapas & CKD_AT) && (dialsta != DIA_PART) && !dialspon && !dialspoff && !dialvol1 && !dialvol2 &&!dialvol3) { /* AT command set and commands have not been customized */ /* so combine speaker and volume commands. */ if (mdmspk) sprintf(lbuf,"ATM1L%d%c",mdmvol,13); /* safe */ else sprintf(lbuf,"ATM0%c",13); /* safe */ ttslow(lbuf,wr); /* Send command */ getok(5,1); /* Get but ignore response */ } else if (dialsta != DIA_PART) { /* Customized or not AT commands */ x = 0; /* Do it the hard way */ if (mdmspk) { if (dialspon) { if (*dialspon) { x = 1; ttslow(dialspon,wr); } } else { if (mp->sp_on_str[0]) { x = 1; ttslow(mp->sp_on_str,wr); } } } else { /* s = dialspoff ? dialspoff : mp->sp_off_str; */ if (dialspoff) { if (*dialspoff) { x = 1; ttslow(dialspoff,wr); } } else { if (mp->sp_off_str[0]) { x = 1; ttslow(mp->sp_off_str,wr); } } } if (x) { if (xx_ok) /* Get response */ x = (*xx_ok)(5,1); if (x && mdmspk) { /* Good response and speaker on? */ switch (mdmvol) { /* Yes, send volume command. */ case 0: case 1: s = dialvol1 ? dialvol1 : mp->vol1_str; break; case 2: s = dialvol2 ? dialvol2 : mp->vol2_str; break; case 3: s = dialvol3 ? dialvol3 : mp->vol3_str; break; default: s = NULL; } if (s) if (*s) { /* Send volume command. */ ttslow(s, wr); if (xx_ok) /* Get response but ignore it */ (*xx_ok)(5,1); } } } } #ifndef CK_ATDT /* Dialing Method */ if (dialmth && dialsta != DIA_PART) { /* If dialing method specified... */ char *s = ""; /* Do it here... */ if (dialmth == XYDM_T && dialtone) /* Tone */ s = dialtone; else if (dialmth == XYDM_P && dialpulse) /* Pulse */ s = dialpulse; if (s) if (*s) { ttslow(s, wr); if (xx_ok) /* Get modem's response */ (*xx_ok)(5,1); /* (but ignore it...) */ } } #endif /* CK_ATDT */ if (dialidt) { /* Ignore dialtone? */ char *s = ""; s = dialx3 ? dialx3 : mp->ignoredt; if (s) if (*s) { ttslow(s, wr); if (xx_ok) /* Get modem's response */ (*xx_ok)(5,1); /* (but ignore it...) */ } } { char *s = ""; /* Last-minute init string? */ s = dialini2 ? dialini2 : mp->ini2; if (s) if (*s) { ttslow(s, wr); if (xx_ok) /* Get modem's response */ (*xx_ok)(5,1); /* (but ignore it...) */ } } if (func_code == 1) { /* ANSWER (not DIAL) */ char *s; s = dialaaon ? dialaaon : mp->aa_on_str; if (!s) s = ""; if (*s) { /* Here we would handle caller ID */ ttslow(s, (dialpace > -1) ? wr : mp->dial_rate); if (xx_ok) /* Get modem's response */ (*xx_ok)(5,1); /* (but ignore it...) */ } else { printf( "WARNING - I don't know how to enable autoanswer for this modem.\n" ); } /* And skip all the phone-number & dialing stuff... */ alarm(waitct); /* This much time allowed. */ debug(F101,"_dodial ANSWER waitct","",waitct); } else { /* DIAL (not ANSWER) */ if (dialsta != DIA_PART) { /* Last dial was not partial */ char *s = ""; #ifdef COMMENT s = dialaaoff ? dialaaoff : mp->aa_off_str; #endif /* COMMENT */ if (s) if (*s) { ttslow(s, (dialpace > -1) ? wr : mp->dial_rate); if (xx_ok) /* Get modem's response */ (*xx_ok)(5,1); /* (but ignore it...) */ } /* Put modem into dialing mode, if the modem requires it. */ if (mp->dmode_str && *(mp->dmode_str)) { ttslow(mp->dmode_str, (dialpace > -1) ? wr : mp->dial_rate); savalrm = signal(SIGALRM,dialtime); alarm(10); /* Wait for prompt, if any expected */ if (mp->dmode_prompt && *(mp->dmode_prompt)) { waitfor(mp->dmode_prompt); msleep(300); } alarm(0); /* Turn off alarm on dialing prompts */ signal(SIGALRM,savalrm); /* Restore alarm */ } } /* AT-Command-Set non-Generic modem */ if (mdmcapas & CKD_AT && mymdmtyp != n_GENERIC && dialsta != DIA_PART) { if (mdmwait > 255) /* If larger than maximum, */ mdmwait = 255; /* make it maximum. */ if (dialesc > 0 && /* Modem escape character is set */ dialmhu > 0) { /* Hangup method is modem command */ int x = dialesc; if (dialesc < 0 || dialesc > 127) x = 128; sprintf(lbuf, "ATS2=%dS7=%d\015", dialesc ? x : mp->esc_char, mdmwait); /* safe */ } else sprintf(lbuf,"ATS7=%d%c",mdmwait,13); /* safe */ ttslow(lbuf,wr); /* Set it. */ mdmstat = getok(5,1); /* Get response from modem */ /* If it gets an error, go ahead anyway */ debug(F101,"_dodial S7 mdmstat","",mdmstat); } ttflui(); /* Clear out stuff from modem setup */ inited = 1; /* Remember modem is initialized */ REDIAL: if ((int)strlen(dcmd) + (int)strlen(xnum) > LBUFL) ckstrncpy(lbuf,"NUMBER TOO LONG!",LBUFL); else sprintf(lbuf, dcmd, xnum); /* safe (prechecked) */ debug(F110,"dialing",lbuf,0); /* Send the dialing string */ ttslow(lbuf,dialpace > -1 ? wr : mp->dial_rate); fail_code = F_MODEM; /* New default failure code changes */ dial_what = DW_DIAL; /* and our state, too. */ if (dialdpy) { /* If showing progress */ p = ck_time(); /* get current time; */ if (*p) printf(" Dialing: %s...\n",p); #ifdef VMS printf(" \n"); fflush(stdout); #endif /* VMS */ } alarm(waitct); /* This much time allowed. */ debug(F101,"_dodial waitct","",waitct); #ifndef MINIDIAL #ifdef OLDMODEMS switch (mymdmtyp) { case n_RACAL: /* Acknowledge dialing string */ sleep(3); ttflui(); ttoc('\015'); break; case n_VENTEL: waitfor("\012\012"); /* Ignore the first two strings */ break; default: break; } #endif /* OLDMODEMS */ #endif /* MINIDIAL */ } /* Check for connection */ mdmstat = 0; /* No status yet */ lbuf[0] = NUL; /* Default reason for failure */ debug(F101,"dial awaiting response, mymdmtyp","",mymdmtyp); #ifndef NOSPL modemmsg[0] = NUL; #endif /* NOSPL */ while (mdmstat == 0) { /* Till we get a result or time out */ if ((mdmcapas & CKD_AT) && nonverbal) { /* AT command set */ gethrn(); /* In digit result mode */ if (partial && dialsta == DIA_ERR) { /* If we get an error here, the phone is still off hook so we have to hang it up. */ dialhup(); dialsta = DIA_ERR; /* (because dialhup() changes it) */ } continue; } else if (mymdmtyp == n_UNKNOWN) { /* Unknown modem type */ int x, y = waitct; mdmstat = D_FAILED; /* Assume failure. */ while (y-- > -1) { x = ttchk(); if (x > 0) { if (x > LBUFL) x = LBUFL; x = ttxin(x,(CHAR *)lbuf); if ((x > 0) && dialdpy) conol(lbuf); } else if (network #ifdef TN_COMPORT && !istncomport() #endif /* TN_COMPORT */ && x < 0) { /* Connection dropped */ inited = 0; #ifdef NTSIG ckThreadEnd(threadinfo); #endif /* NTSIG */ dialsta = DIA_IO; /* Call it an I/O error */ #ifdef DYNAMIC if (rbuf) free(rbuf); rbuf = NULL; if (fbuf) free(fbuf); fbuf = NULL; #endif /* DYNAMIC */ SIGRETURN; } x = ttgmdm(); /* Try to read modem signals */ if (x < 0) break; /* Can't, fail. */ if (x & BM_DCD) { /* Got signals OK. Carrier present? */ mdmstat = CONNECTED; /* Yes, done. */ break; } /* No, keep waiting. */ sleep(1); } continue; } for (n = -1; n < LBUFL-1; ) { /* Accumulate modem response */ int xx; c2 = (char) (xx = ddinc(0)); /* Read a character, blocking */ if (xx < 1) /* Ignore NULs and errors */ continue; /* (Timeout will handle errors) */ else /* Real character, keep it */ lbuf[++n] = (char) (c2 & 0177); dialoc(lbuf[n]); /* Maybe echo it */ if (mdmcapas & CKD_V25) { /* V.25bis dialing... */ /* This assumes that V.25bis indications are all at least 3 characters long and are terminated by either CRLF or LFCR. */ if (mymdmtyp == n_CCITT) { if (n < 3) continue; if ((lbuf[n] == CR) && (lbuf[n-1] == LF)) break; if ((lbuf[n] == LF) && (lbuf[n-1] == CR)) break; } #ifndef MINIDIAL else if (mymdmtyp == n_DIGITEL) { if (((lbuf[n] == CR) && (lbuf[n-1] == LF)) || ((lbuf[n] == LF) && (lbuf[n-1] == CR))) break; else continue; } #endif /* MINIDIAL */ } else { /* All others, break on CR or LF */ if ( lbuf[n] == CR || lbuf[n] == LF ) break; } } lbuf[++n] = '\0'; /* Terminate response from modem */ debug(F111,"_dodial modem response",lbuf,n); #ifndef NOSPL ckstrncpy(modemmsg,lbuf,LBUFL); /* Call result message */ lbuf[79] = NUL; { int x; /* Strip junk from end */ x = (int)strlen(modemmsg) - 1; while (x > -1) { if (modemmsg[x] < (char) 33) modemmsg[x] = NUL; else break; x--; } } #endif /* NOSPL */ if (mdmcapas & CKD_AT) { /* Hayes AT command set */ gethrw(); /* in word result mode */ if (partial && dialsta == DIA_ERR) { dialhup(); dialsta = DIA_ERR; /* (because dialhup() changes it) */ } continue; } else if (mdmcapas & CKD_V25) { /* CCITT command set */ if (didweget(lbuf,"VAL")) { /* Dial command confirmation */ #ifndef MINIDIAL if (mymdmtyp == n_CCITT) #endif /* MINIDIAL */ continue; /* Go back and read more */ #ifndef MINIDIAL /* Digitel doesn't give an explicit connect confirmation message */ else { int n; for (n = -1; n < LBUFL-1; ) { lbuf[++n] = c2 = (char) (ddinc(0) & 0177); dialoc(lbuf[n]); if (((lbuf[n] == CR) && (lbuf[n-1] == LF)) || ((lbuf[n] == LF) && (lbuf[n-1] == CR))) break; } mdmstat = CONNECTED; /* Assume we're connected */ if (dialdpy && carrier != CAR_OFF) { #ifdef TN_COMPORT if (istncomport()) { int i; for (i = 0; i < 5; i++) { debug(F100,"TN Com Port DCD wait...","",0); if ((n = ttgmdm()) >= 0) { if ((n & BM_DCD)) break; msleep(500); tnc_wait( (CHAR *)"_dodial waiting for DCD",1); } } } else #endif /* TN_COMPORT */ sleep(1); /* Wait a second */ n = ttgmdm(); /* Try to read modem signals */ if ((n > -1) && ((n & BM_DCD) == 0)) printf("WARNING - no carrier\n"); } } #endif /* MINIDIAL */ /* Standard V.25bis stuff */ } else if (didweget(lbuf,"CNX")) { /* Connected */ mdmstat = CONNECTED; } else if (didweget(lbuf, "INV")) { mdmstat = D_FAILED; /* Command error */ dialsta = DIA_ERR; ckstrncpy(lbuf,"INV",LBUFL); } else if (didweget(lbuf,"CFI")) { /* Call Failure */ if (didweget(lbuf,"AB")) { /* Interpret reason code */ ckstrncpy(lbuf,"AB: Timed out",LBUFL); dialsta = DIA_TIMO; } else if (didweget(lbuf,"CB")) { ckstrncpy(lbuf,"CB: Local DCE Busy",LBUFL); dialsta = DIA_NRDY; } else if (didweget(lbuf,"ET")) { ckstrncpy(lbuf,"ET: Busy",LBUFL); dialsta = DIA_BUSY; } else if (didweget(lbuf, "NS")) { ckstrncpy(lbuf,"NS: Number not stored",LBUFL); dialsta = DIA_ERR; } else if (didweget(lbuf,"NT")) { ckstrncpy(lbuf,"NT: No answer",LBUFL); dialsta = DIA_NOAN; } else if (didweget(lbuf,"RT")) { ckstrncpy(lbuf,"RT: Ring tone",LBUFL); dialsta = DIA_RING; } else if (didweget(lbuf,"PV")) { ckstrncpy(lbuf,"PV: Parameter value error",LBUFL); dialsta = DIA_ERR; } else if (didweget(lbuf,"PS")) { ckstrncpy(lbuf,"PS: Parameter syntax error",LBUFL); dialsta = DIA_ERR; } else if (didweget(lbuf,"MS")) { ckstrncpy(lbuf,"MS: Message syntax error",LBUFL); dialsta = DIA_ERR; } else if (didweget(lbuf,"CU")) { ckstrncpy(lbuf,"CU: Command unknown",LBUFL); dialsta = DIA_ERR; } else if (didweget(lbuf,"FC")) { ckstrncpy(lbuf,"FC: Forbidden call",LBUFL); dialsta = DIA_NOAC; } mdmstat = D_FAILED; } else if (didweget(lbuf,"INC")) { /* Incoming Call */ ckstrncpy(lbuf,"INC: Incoming call",LBUFL); dialsta = DIA_RING; mdmstat = D_FAILED; } else if (didweget(lbuf,"DLC")) { /* Delayed Call */ ckstrncpy(lbuf,"DLC: Delayed call",LBUFL); dialsta = DIA_NOAN; mdmstat = D_FAILED; } else /* Response was probably an echo. */ #ifndef MINIDIAL if (mymdmtyp == n_CCITT) #endif /* MINIDIAL */ continue; #ifndef MINIDIAL else /* Digitel: If no error, connect. */ mdmstat = CONNECTED; #endif /* MINIDIAL */ break; } else if (n) { /* Non-Hayes-compatibles... */ switch (mymdmtyp) { #ifndef MINIDIAL case n_ATTMODEM: /* Careful - "Connected" / "Not Connected" */ if (didweget(lbuf,"Busy")) { mdmstat = D_FAILED; dialsta = DIA_BUSY; } else if (didweget(lbuf,"Not connected") || didweget(lbuf,"Not Connected")) { mdmstat = D_FAILED; dialsta = DIA_NOCA; } else if (didweget(lbuf,"No dial tone") || didweget(lbuf,"No Dial Tone")) { mdmstat = D_FAILED; dialsta = DIA_NODT; } else if (didweget(lbuf,"No answer") || didweget(lbuf,"No Answer")) { mdmstat = D_FAILED; dialsta = DIA_NOAN; } else if (didweget(lbuf,"Answered") || didweget(lbuf,"Connected")) { mdmstat = CONNECTED; dialsta = DIA_OK; } break; case n_ATTISN: if (didweget(lbuf,"ANSWERED")) { mdmstat = CONNECTED; dialsta = DIA_OK; } else if (didweget(lbuf,"BUSY")) { mdmstat = D_FAILED; dialsta = DIA_BUSY; } else if (didweget(lbuf,"DISCONNECT")) { mdmstat = D_FAILED; dialsta = DIA_DISC; } else if (didweget(lbuf,"NO ANSWER")) { mdmstat = D_FAILED; dialsta = DIA_NOAN; } else if (didweget(lbuf,"WRONG ADDRESS")) { mdmstat = D_FAILED; dialsta = DIA_NOAC; } break; case n_ATTDTDM: if (didweget(lbuf,"ANSWERED")) { mdmstat = CONNECTED; } else if (didweget(lbuf,"BUSY")) { mdmstat = D_FAILED; dialsta = DIA_BUSY; } else if (didweget(lbuf,"CHECK OPTIONS")) { mdmstat = D_FAILED; dialsta = DIA_ERR; } else if (didweget(lbuf,"DISCONNECTED")) { mdmstat = D_FAILED; dialsta = DIA_DISC; } else if (didweget(lbuf,"DENIED")) { mdmstat = D_FAILED; dialsta = DIA_NOAC; } #ifdef DEBUG #ifdef ATT6300 /* Horrible hack lost in history. */ else if (deblog && didweget(lbuf,"~~")) mdmstat = CONNECTED; #endif /* ATT6300 */ #endif /* DEBUG */ break; #ifdef OLDMODEMS case n_CERMETEK: if (didweget(lbuf,"\016A")) { mdmstat = CONNECTED; ttslow("\016U 1\015",200); /* Make transparent*/ } break; case n_DF03: /* Because response lacks CR or NL . . . */ c = (char) (ddinc(0) & 0177); dialoc(c); debug(F000,"dial df03 got","",c); if ( c == 'A' ) mdmstat = CONNECTED; if ( c == 'B' ) mdmstat = D_FAILED; break; case n_DF100: /* DF100 has short response codes */ if (strcmp(lbuf,"A") == 0) { mdmstat = CONNECTED; /* Attached */ dialsta = DIA_OK; } else if (strcmp(lbuf,"N") == 0) { mdmstat = D_FAILED; dialsta = DIA_NOAN; /* No answer or no dialtone */ } else if (strcmp(lbuf,"E") == 0 || /* Error */ strcmp(lbuf,"R") == 0) { /* "Ready" (?) */ mdmstat = D_FAILED; dialsta = DIA_ERR; /* Command error */ } /* otherwise fall thru... */ case n_DF200: if (didweget(lbuf,"Attached")) { mdmstat = CONNECTED; dialsta = DIA_OK; /* * The DF100 will respond with "Attached" even if DTR * and/or carrier are not present. Another reason to * (also) wait for carrier? */ } else if (didweget(lbuf,"Busy")) { mdmstat = D_FAILED; dialsta = DIA_BUSY; } else if (didweget(lbuf,"Disconnected")) { mdmstat = D_FAILED; dialsta = DIA_DISC; } else if (didweget(lbuf,"Error")) { mdmstat = D_FAILED; dialsta = DIA_ERR; } else if (didweget(lbuf,"No answer")) { mdmstat = D_FAILED; dialsta = DIA_NOAN; } else if (didweget(lbuf,"No dial tone")) { mdmstat = D_FAILED; dialsta = DIA_NODT; } else if (didweget(lbuf,"Speed:)")) { mdmstat = D_FAILED; dialsta = DIA_ERR; } /* * It appears that the "Speed:..." response comes after an * "Attached" response, so this is never seen. HOWEVER, * it would be very handy to detect this and temporarily * reset the speed, since it's a nuisance otherwise. * If we wait for some more input from the modem, how do * we know if it's from the remote host or the modem? * Carrier reportedly doesn't get set until after the * "Speed:..." response (if any) is sent. Another reason * to (also) wait for carrier. */ break; case n_GDC: if (didweget(lbuf,"ON LINE")) mdmstat = CONNECTED; else if (didweget(lbuf,"NO CONNECT")) mdmstat = D_FAILED; break; case n_PENRIL: if (didweget(lbuf,"OK")) { mdmstat = CONNECTED; } else if (didweget(lbuf,"BUSY")) { mdmstat = D_FAILED; dialsta = DIA_BUSY; } else if (didweget(lbuf,"NO RING")) { mdmstat = D_FAILED; dialsta = DIA_NOCA; } break; case n_RACAL: if (didweget(lbuf,"ON LINE")) mdmstat = CONNECTED; else if (didweget(lbuf,"FAILED CALL")) mdmstat = D_FAILED; break; #endif /* OLDMODEMS */ case n_ROLM: if (didweget(lbuf,"CALLING")) mdmstat = 0; else if (didweget(lbuf,"COMPLETE")) mdmstat = CONNECTED; else if (didweget(lbuf,"FAILED") || didweget(lbuf,"ABANDONDED")) { mdmstat = D_FAILED; dialsta = DIA_NOCA; } else if (didweget(lbuf,"NOT AVAILABLE") || didweget(lbuf,"LACKS PERMISSION") || didweget(lbuf,"NOT A DATALINE") || didweget(lbuf,"INVALID DATA LINE NUMBER") || didweget(lbuf,"INVALID GROUP NAME")) { mdmstat = D_FAILED; dialsta = DIA_NOAC; } else if (didweget(lbuf,"BUSY")) { mdmstat = D_FAILED; dialsta = DIA_BUSY; } else if (didweget(lbuf,"DOES NOT ANSWER")) { mdmstat = D_FAILED; dialsta = DIA_NOAN; } break; #ifdef OLDMODEMS case n_VENTEL: if (didweget(lbuf,"ONLINE!") || didweget(lbuf,"Online!")) { mdmstat = CONNECTED; } else if (didweget(lbuf,"BUSY") || didweget(lbuf,"Busy")) { mdmstat = D_FAILED; dialsta = DIA_BUSY; } else if (didweget(lbuf,"DEAD PHONE")) { mdmstat = D_FAILED; dialsta = DIA_DISC; } break; case n_CONCORD: if (didweget(lbuf,"INITIATING")) mdmstat = CONNECTED; else if (didweget(lbuf,"BUSY")) { mdmstat = D_FAILED; dialsta = DIA_BUSY; } else if (didweget(lbuf,"CALL FAILED")) { mdmstat = D_FAILED; dialsta = DIA_NOCA; } break; #endif /* OLDMODEMS */ case n_MICROCOM: /* "RINGBACK" means phone line ringing, continue */ if (didweget(lbuf,"NO CONNECT")) { mdmstat = D_FAILED; dialsta = DIA_NOCA; } else if (didweget(lbuf,"BUSY")) { mdmstat = D_FAILED; dialsta = DIA_BUSY; } else if (didweget(lbuf,"NO DIALTONE")) { mdmstat = D_FAILED; dialsta = DIA_NODT; } else if (didweget(lbuf,"COMMAND ERROR")) { mdmstat = D_FAILED; dialsta = DIA_ERR; } else if (didweget(lbuf,"IN USE")) { mdmstat = D_FAILED; dialsta = DIA_NOAC; } else if (didweget(lbuf,"CONNECT")) { mdmstat = CONNECTED; /* trailing speed ignored */ } break; #endif /* MINIDIAL */ default: printf( "PROGRAM ERROR - No response handler for modem type %d\n", mymdmtyp); mdmstat = D_FAILED; dialsta = DIA_ERR; } } } /* while (mdmstat == 0) */ debug(F101,"_dodial alarm off","",x); alarm(0); if (mdmstat == D_FAILED) { /* Failure detected by modem */ dialfail(F_MODEM); #ifdef NTSIG ckThreadEnd(threadinfo); #endif /* NTSIG */ SIGRETURN; } else if (mdmstat == D_PARTIAL ) { /* Partial dial command OK */ msleep(500); debug(F100,"dial partial","",0); } else { /* Call was completed */ int x; msleep(700); /* In case modem signals blink */ debug(F100,"dial succeeded","",0); if ( #ifndef MINIDIAL mymdmtyp != n_ROLM /* Rolm has weird modem signaling */ #else 1 #endif /* MINIDIAL */ ) { alarm(3); /* In case ttpkt() gets stuck... */ ttpkt(speed,FLO_DIAX,parity); /* Cancel dialing state ioctl */ alarm(0); } /* In case CD went off in the interval between call completion and return from ttpkt()... */ if (carrier != CAR_OFF) { if ((x = ttgmdm()) >= 0) { #ifdef TN_COMPORT if (istncomport() && !(x & BM_DCD)) { int i; for (i = 0; i < 5; i++) { msleep(500); tnc_wait((CHAR *)"_dodial waiting for DCD",1); if ((x = ttgmdm()) >= 0) { if ((x & BM_DCD)) break; } } } #endif /* TN_COMPORT */ if (!(x & BM_DCD)) printf("WARNING: Carrier seems to have dropped...\n"); } } } dreset(); /* Reset alarms and signals. */ if (!quiet && !backgrd) { if (dialdpy && (p = ck_time())) { /* If DIAL DISPLAY ON, */ printf(" %sall complete: %s.\n", /* include timestamp. */ (mdmstat == D_PARTIAL) ? "Partial c" : "C", p ); } else if (modemmsg[0]) { printf (" %sall complete: \"%s\".\n", (mdmstat == D_PARTIAL) ? "Partial c" : "C", (char *)modemmsg ); } else { printf (" %sall complete.\n", (mdmstat == D_PARTIAL) ? "Partial c" : "C" ); } } #ifdef CKLOGDIAL dologdial(telnbr); #endif /* CKLOGDIAL */ #ifdef DYNAMIC if (rbuf) free(rbuf); rbuf = NULL; if (fbuf) free(fbuf); fbuf = NULL; #endif /* DYNAMIC */ dialsta = (mdmstat == D_PARTIAL) ? DIA_PART : DIA_OK; #ifdef NTSIG ckThreadEnd(threadinfo); #endif /* NTSIG */ SIGRETURN; } static SIGTYP #ifdef CK_ANSIC faildial(void * threadinfo) #else /* Not CK_ANSIC */ faildial(threadinfo) VOID * threadinfo; #endif /* CK_ANSIC */ /* faildial */ { debug(F100,"longjmp returns to dial routine","",0); dialfail(fail_code); SIGRETURN; } /* nbr = number to dial (string) x1 = Retry counter x2 = Number counter fc = Function code: 0 == DIAL 1 == ANSWER 2 == INIT/CONFIG 3 == PARTIAL DIAL */ int #ifdef OLD_DIAL ckdial(nbr) char *nbr; #else ckdial(nbr, x1, x2, fc, redial) char *nbr; int x1, x2, fc, redial; #endif /* OLD_DIAL */ /* ckdial */ { #define ERMSGL 50 char errmsg[ERMSGL], *erp; /* For error messages */ int n = F_TIME; char *s; long spdmax; #ifdef OS2 extern int term_io; int term_io_sav = term_io; #endif /* OS2 */ char *mmsg = "Sorry, DIAL memory buffer can't be allocated\n"; /* A DIAL command implies a SET MODEM TYPE command and therefore enables hanging up by modem commands rather than dropping DTR. */ mdmset = 1; /* See mdmhup() */ partial = 0; if (fc == 3) { /* Partial dial requested */ partial = 1; /* Set flag */ fc = 0; /* Treat like regular dialing */ } func_code = fc; /* Make global to this module */ telnbr = nbr; xredial = redial; debug(F111,"ckdial entry partial",ckitoa(fc),partial); debug(F111,"ckdial entry number",nbr,redial); if (fc == 1) { /* ANSWER command? */ /* Reset caller ID strings */ if (callid_date) makestr(&callid_date,NULL); if (callid_time) makestr(&callid_time,NULL); if (callid_name) makestr(&callid_name,NULL); if (callid_nmbr) makestr(&callid_nmbr,NULL); if (callid_mesg) makestr(&callid_mesg,NULL); } #ifdef CK_TAPI_X if (tttapi && tapipass) { if (modemp[n_TAPI] = cktapiGetModemInf()) { mymdmtyp = n_TAPI; } else { mymdmtyp = mdmtyp; modemp[n_TAPI] = &GENERIC; } } else #endif /* CK_TAPI */ mymdmtyp = mdmtyp; if (mymdmtyp < 0) { /* Whoa, network dialing... */ if (mdmsav > -1) mymdmtyp = mdmsav; } if (mymdmtyp < 0) { printf("Invalid modem type %d - internal error.\n",mymdmtyp); dialsta = DIA_NOMO; return(0); } dial_what = DW_NOTHING; /* Doing nothing at first. */ nonverbal = 0; /* These are ONLY for the purpose of interpreting numeric result codes. */ is_motorola = #ifdef MINIDIAL 0 #else mymdmtyp == n_SUPRA || mymdmtyp == n_SUPRASON; #endif /* MINIDIAL */ ; is_motorola = #ifdef MINIDIAL 0 #else mymdmtyp == n_MOTOROLA || mymdmtyp == n_MONTANA; #endif /* MINIDIAL */ ; is_rockwell = #ifdef MINIDIAL 0 #else mymdmtyp == n_RWV32 || mymdmtyp == n_RWV32B || mymdmtyp == n_RWV34 || mymdmtyp == n_RWV90 || mymdmtyp == n_BOCA || mymdmtyp == n_TELEPATH || mymdmtyp == n_CARDINAL || mymdmtyp == n_BESTDATA || mymdmtyp == n_CONEXANT || mymdmtyp == n_PCTEL #endif /* MINIDIAL */ ; is_hayeshispd = #ifdef MINIDIAL 0 #else mymdmtyp == n_H_ULTRA || mymdmtyp == n_H_ACCURA || n_PPI #endif /* MINIDIAL */ ; is_supra = #ifdef MINIDIAL 0 #else mymdmtyp == n_SUPRA || mymdmtyp == n_SUPRAX || n_SUPRASON #endif /* MINIDIAL */ ; mp = modemp[mymdmtyp]; /* Set pointer to modem info */ if (!mp) { printf("Sorry, handler for this modem type not yet filled in.\n"); dialsta = DIA_NOMO; return 0; } debug(F110,"dial number",telnbr,0); #ifdef COMMENT debug(F110,"dial prefix",(dialnpr ? dialnpr : ""), 0); #endif /* COMMENT */ #ifdef DYNAMIC *lbuf = NUL; debug(F101,"DIAL lbuf malloc ok","",LBUFL+1); if (!rbuf) { /* This one might already have been allocated by getok() */ if (!(rbuf = malloc(RBUFL+1))) { /* Allocate input line buffer */ printf("%s", mmsg); dialsta = DIA_IE; return 0; } else debug(F101,"DIAL rbuf malloc ok","",RBUFL+1); } if (!(fbuf = malloc(FULLNUML+1))) { /* Allocate input line buffer */ printf("%s", mmsg); dialsta = DIA_IE; if (rbuf) free(rbuf); rbuf = NULL; return 0; } debug(F101,"DIAL fbuf malloc ok","",FULLNUML+1); #endif /* DYNAMIC */ /* NOTE: mdmtyp, not mymdmtyp */ if (ttopen(ttname,&local,mdmtyp,0) < 0) { /* Open, no carrier wait */ erp = errmsg; if ((int)strlen(ttname) < (ERMSGL - 18)) /* safe, checked */ sprintf(erp,"Sorry, can't open %s",ttname); else sprintf(erp,"Sorry, can't open device"); perror(errmsg); dialsta = DIA_OPEN; #ifdef DYNAMIC if (rbuf) free(rbuf); rbuf = NULL; if (fbuf) free(fbuf); fbuf = NULL; #endif /* DYNAMIC */ return 0; } #ifdef CK_TAPI if (!tttapi) { #endif /* CK_TAPI */ /* Condition console terminal and communication line */ /* Place line into "clocal" dialing state, */ /* important mainly for System V UNIX. */ if (ttpkt(speed,FLO_DIAL,parity) < 0) { ttclos(0); /* If ttpkt fails do all this... */ if (ttopen(ttname,&local,mymdmtyp,0) < 0) { erp = errmsg; if ((int)strlen(ttname) < (ERMSGL - 18)) /* safe, checked */ sprintf(erp,"Sorry, can't reopen %s",ttname); else sprintf(erp,"Sorry, can't reopen device"); perror(errmsg); dialsta = DIA_OPEN; #ifdef DYNAMIC if (rbuf) free(rbuf); rbuf = NULL; if (fbuf) free(fbuf); fbuf = NULL; #endif /* DYNAMIC */ return 0; } /* And try again. */ if ((ttpkt(speed,FLO_DIAL,parity) < 0) #ifdef UNIX && (strcmp(ttname,"/dev/null")) #else #ifdef OSK && (strcmp(ttname,"/nil")) #endif /* OSK */ #endif /* UNIX */ #ifdef CK_TAPI && !tttapi #endif /* CK_TAPI */ ) { printf("Sorry, Can't condition communication line\n"); printf("Try 'set line %s' again\n",ttname); dialsta = DIA_OPEN; #ifdef DYNAMIC if (rbuf) free(rbuf); rbuf = NULL; if (fbuf) free(fbuf); fbuf = NULL; #endif /* DYNAMIC */ return 0; } } #ifdef CK_TAPI } #endif /* CK_TAPI */ /* Modem's escape sequence... */ if (dialesc < 0 || dialesc > 127) c = NUL; else c = (char) (dialesc ? dialesc : mp->esc_char); mdmcapas = dialcapas ? dialcapas : mp->capas; xx_ok = mp->ok_fn; /* Pointer to response reader */ if (mdmcapas & CKD_AT) { /* Hayes compatible */ escbuf[0] = c; escbuf[1] = c; escbuf[2] = c; escbuf[3] = NUL; /* In case this modem type is user-defined */ if (!xx_ok) xx_ok = getok; } else { /* Other */ escbuf[0] = c; escbuf[1] = NUL; /* In case user-defined */ if (mdmcapas & CKD_V25) if (!xx_ok) xx_ok = getok; } /* Partial dialing */ if (mdmcapas & CKD_AT #ifndef MINIDIAL || mymdmtyp == n_MICROCOM #endif /* MINIDIAL */ ) { int x; x = (int) strlen(telnbr); if (x > 0) { if (telnbr[x-1] == ';') { partial = 1; debug(F110,"ckdial sets partial=1:",telnbr,0); } else if (partial) { ckmakmsg(fbuf,FULLNUML,telnbr,";",NULL,NULL); /* add one */ telnbr = fbuf; } } } msleep(500); debug(F101,"ckdial dialtmo","",dialtmo); /* Timeout */ if (fc == 1) { /* ANSWER */ waitct = (dialatmo > -1) ? dialatmo : 0; } else { /* DIAL */ if (dialtmo < 1) { /* Automatic computation. */ #ifdef CK_TAPI if (tttapi && !tapipass) { waitct = 1 * (int)strlen(telnbr) ; /* Worst case dial time */ waitct += 60; /* dialtone + completion wait times */ for (s = telnbr; *s; s++) { /* add in pause characters time */ if (*s == ',') { waitct += 2; /* unless it was changed in the modem */ } else if (*s == 'W' || *s == 'w' || *s == '$' || *s == '@' ) { waitct += 8; } } } else { #endif /* CK_TAPI */ waitct = 1 * (int)strlen(telnbr) ; /* dialtone + completion wait times */ waitct += mp->dial_time; for (s = telnbr; *s; s++) { for (p = mp->pause_chars; *p; p++) if (*s == *p) { waitct += mp->pause_time; break; } } #ifdef CK_TAPI } #endif /* CK_TAPI */ } else { waitct = dialtmo; /* User-specified timeout */ } debug(F101,"ckdial waitct A","",waitct); } /* waitct is our alarm() timer. mdmwait is how long we tell the modem to wait for carrier. We set mdmwait to be 5 seconds less than waitct, to increase the chance that we get a response from the modem before timing out. */ if (waitct <= 0) { /* 0 or negative means wait forever */ #ifdef COMMENT waitct = 254; /* These were backwards in 7.0.196 */ mdmwait = 0; #else waitct = 0; /* Fixed in 7.0.198. */ mdmwait = 254; #endif /* COMMENT */ } else { if (dialtmo < 1) { /* Automatic computation. */ #ifdef XWAITCT /* Addtl wait slop can be defined at compile time */ waitct += XWAITCT; #endif /* XWAITCT */ if (waitct < 60 + mdmwaitd) waitct = 60 + mdmwaitd; } if (mdmcapas & CKD_AT) { /* AT command-set modems */ mdmwait = waitct; /* S7 timeout = what user asked for */ waitct += mdmwaitd; /* Kermit timeout a bit later */ } else { /* Non-AT */ mdmwait = waitct; /* no difference */ } } debug(F101,"ckdial waitct B","",waitct); if (fc == 1) { /* ANSWER */ #ifdef COMMENT /* This is wrong. mdmwait is the value given to S7 in Hayeslike modems. When in autoanswer mode, this is the amount of time the modem waits for carrier once ringing starts. Whereas waitct is the timeout given to the ANSWER command, which is an entirely different thing. Since the default ANSWER timeout is 0 (meaning "wait forever"), the following statement sets S7 to 0, which, on some modems (like the USR Sportster) makes it hang up and report NO CARRIER the instant the phone rings. */ mdmwait = waitct; #else if (mdmwait <= 0) mdmwait = 60; /* Always wait 60 seconds. */ #endif /* COMMENT */ } if (!quiet && !backgrd) { /* Print information messages. */ #ifdef VMS printf(" \n"); fflush(stdout); #endif /* VMS */ if (fc == 1) printf(" Waiting for phone call...\n"); else printf(" %srying: %s...\n", x1 > 0 ? "Ret" : "T", telnbr); if (x1 == 0 && x2 == 0 && dialsta != DIA_PART) { if (network) { printf(" Via modem server: %s, modem: %s\n", ttname, gmdmtyp() ); } else { #ifdef CK_TAPI if (tttapi && !tapipass) printf(" Device: %s, modem: %s", ttname, "TAPI" ); else #endif /* CK_TAPI */ printf(" Device: %s, modem: %s", ttname, gmdmtyp() ); if (speed > -1L) printf(", speed: %ld\n", speed); else printf(", speed: (unknown)\n"); } spdmax = dialmax > 0L ? dialmax : mp->max_speed; #ifndef NOHINTS if (hints && !quiet && !network && spdmax > 0L && speed > spdmax #ifdef CK_TAPI && (!tttapi || tapipass) #endif /* CK_TAPI */ ) { printf("\n*************************\n"); printf( "Interface speed %ld might be too high for this modem type.\n", speed ); printf( "If dialing fails, SET SPEED to %ld or less and try again.\n", spdmax ); printf("(Use SET HINTS OFF to suppress future hints.)\n"); printf("*************************\n"); printf("\n"); } #endif /* NOHINTS */ printf(" %s timeout: ", fc == 0 ? "Dial" : "Answer"); if (waitct > 0) printf("%d seconds\n",mdmwait); else printf(" (none)\n"); printf( #ifdef MAC " Type Command-. to cancel.\n" #else #ifdef UNIX " To cancel: type your interrupt character (normally Ctrl-C).\n" #else " To cancel: type Ctrl-C (hold down Ctrl, press C).\n" #endif /* UNIX */ #endif /* MAC */ ); } } debug(F111,"ckdial",ttname,(int) (speed / 10L)); debug(F101,"ckdial timeout","",waitct); #ifdef OS2 term_io = 0; #endif /* OS2 */ /* Set timer and interrupt handlers. */ savint = signal( SIGINT, dialint ) ; /* And terminal interrupt handler. */ cc_alrm_execute(ckjaddr(sjbuf), 0, dialtime, _dodial, faildial); signal(SIGINT, savint); #ifdef OS2 if (dialsta == DIA_OK) /* Dialing is completed */ DialerSend(OPT_KERMIT_CONNECT, 0); term_io = term_io_sav; #endif /* OS2 */ if (dialsta == DIA_PART || dialsta == DIA_OK) { /* This is needed, e.g., for Telnet modem servers */ if (reliable != SET_OFF || !setreliable) { reliable = SET_OFF; /* Transport is not reliable */ debug(F101,"ckdial reliable","",reliable); } return(1); /* Dial attempt succeeded */ } else { return(0); /* Dial attempt failed */ } } /* ckdial */ /* getok() - wait up to n seconds for OK (0) or ERROR (4) response from modem. Use with Hayeslike or CCITT modems for reading the reply to a nondialing command. Second argument says whether to be strict about numeric result codes, i.e. to require they be preceded by CR or else be the first character in the response, e.g. to prevent the ATH0 echo from looking like a valid response. Strict == 0 is needed for ATI on Telebit, which can return the model number concatenated with the numeric response code, e.g. "9620" ("962" is the model number, "0" is the response code). getok() Returns: 0 if it timed out, 1 if it succeeded, -1 on modem command, i/o, or other error. */ static ckjmpbuf okbuf; static SIGTYP #ifdef CK_ANSIC oktimo(int foo) /* Alarm handler for getok(). */ #else oktimo(foo) int foo; /* Alarm handler for getok(). */ #endif /* CK_ANSIC */ /* oktimo */ { #ifdef OS2 alarm(0); /* signal(SIGALRM,SIG_IGN); */ debug(F100,"oktimo() SIGALRM caught -- SIG_IGN set","",0) ; #endif /* OS2 */ #ifdef OSK /* OS-9, see comment in dialtime(). */ sigmask(-1); #endif /* OSK */ #ifdef NTSIG if ( foo == SIGALRM ) PostAlarmSigSem(); else PostCtrlCSem(); #else /* NTSIG */ #ifdef NT cklongjmp(ckjaddr(okbuf),1); #else /* NT */ cklongjmp(okbuf,1); #endif /* NTSIG */ #endif /* NT */ /* NOTREACHED */ SIGRETURN; } static int okstatus, okn, okstrict; static SIGTYP #ifdef CK_ANSIC dook(void * threadinfo) #else /* CK_ANSIC */ dook(threadinfo) VOID * threadinfo ; #endif /* CK_ANSIC */ /* dook */ { CHAR c; #ifdef DEBUG char * mdmmsg = ""; #endif /* DEBUG */ int i, x; #ifdef IKSD extern int inserver; #endif /* IKSD */ #ifdef NTSIG signal(SIGINT,oktimo); if (threadinfo) { /* Thread local storage... */ TlsSetValue(TlsIndex,threadinfo); } #endif /* NTSIG */ #ifdef CK_LOGIN #ifdef NT #ifdef IKSD if (inserver) setntcreds(); #endif /* IKSD */ #endif /* NT */ #endif /* CK_LOGIN */ if (mdmcapas & CKD_V25) { /* CCITT, easy... */ waitfor("VAL"); okstatus = 1; debug(F111,"Modem_Response(V25)","VAL",okstatus); #ifdef NTSIG ckThreadEnd(threadinfo); #endif /* NTSIG */ SIGRETURN; #ifndef MINIDIAL } else if (mymdmtyp == n_MICROCOM) { /* Microcom in SX mode, also easy */ waitfor(MICROCOM.wake_prompt); /* (I think...) */ debug(F111,"Modem_Response(Microcom)",MICROCOM.wake_prompt,okstatus); okstatus = 1; #ifdef NTSIG ckThreadEnd(threadinfo); #endif /* NTSIG */ SIGRETURN; #endif /* MINIDIAL */ } else { /* Hayes & friends, start here... */ okstatus = 0; /* No status yet. */ for (x = 0; x < RBUFL; x++) /* Initialize response buffer */ rbuf[x] = SP; /* to all spaces */ rbuf[RBUFL] = NUL; /* and terminate with NUL. */ while (okstatus == 0) { /* While no status... */ x = ddinc(okn); /* Read a character */ if (x < 0) { /* I/O error */ okstatus = -1; #ifdef NTSIG ckThreadEnd(threadinfo); #endif /* NTSIG */ SIGRETURN; } #ifdef COMMENT /* too much */ debug(F101,"getok ddinc","",x); /* Got a character. */ #endif /* COMMENT */ c = (char) (x & 0x7f); /* Get low order 7 bits */ if (!c) /* Don't deposit NULs */ continue; /* or else didweget() won't work */ if (dialdpy) conoc((char)c); /* Echo it if requested */ for (i = 0; i < RBUFL-1; i++) /* Rotate buffer */ rbuf[i] = rbuf[i+1]; rbuf[RBUFL-1] = c; /* Deposit character at end */ #ifdef COMMENT /* too much */ debug(F000,"getok:",rbuf,(int) c); /* Log it */ #endif /* COMMENT */ switch (c) { /* Interpret it. */ case CR: /* Got a carriage return. */ switch(rbuf[RBUFL-2]) { /* Look at character before it. */ case '0': /* 0 = OK numeric response */ if (!okstrict || rbuf[RBUFL-3] == CR || rbuf[RBUFL-3] == SP) { nonverbal = 1; okstatus = 1; /* Good response */ } debug(F111,"Modem_Response(Hayes)","0",okstatus); break; case '4': /* 4 = ERROR numeric response */ #ifndef MINIDIAL /* Or Telebit model number 964! */ if (mymdmtyp == n_TELEBIT && isdigit(rbuf[RBUFL-3]) && isdigit(rbuf[RBUFL-4])) break; else #endif /* MINIDIAL */ if (!okstrict || rbuf[RBUFL-3] == CR || rbuf[RBUFL-3] == SP) { nonverbal = 1; okstatus = -1; /* Bad command */ } debug(F111,"Modem_Response(Hayes)","4",okstatus); break; } if (dialdpy && nonverbal) /* If numeric results, */ conoc(LF); /* echo a linefeed too. */ break; case LF: /* Got a linefeed. */ /* Note use of explicit octal codes in the string for CR and LF. We want real CR and LF here, not whatever the compiler happens to replace \r and \n with... */ if (!strcmp(rbuf+RBUFL-4,"OK\015\012")) { /* Good response */ okstatus = 1; debug(F111,"Modem_Response(Hayes)","OK",okstatus); } if (!strcmp(rbuf+RBUFL-3,"OK\012")) { /* Good response */ okstatus = 1; debug(F111,"Modem_Response(Hayes)","OK",okstatus); } else if (!strcmp(rbuf+RBUFL-7,"ERROR\015\012")) { /* Error */ okstatus = -1; debug(F111,"Modem_Response(Hayes)","ERROR",okstatus); } else if (!strcmp(rbuf+RBUFL-6,"ERROR\012")) { /* Error */ okstatus = -1; debug(F111,"Modem_Response(Hayes)","ERROR",okstatus); } break; /* Check whether modem echoes its commands... */ case 't': /* Got little t */ if (!strcmp(rbuf+RBUFL-3,"\015at") || /* See if it's "at" */ !strcmp(rbuf+RBUFL-3," at")) mdmecho = 1; /* debug(F111,"MDMECHO-t",rbuf+RBUFL-2,mdmecho); */ break; case 'T': /* Got Big T */ if (!strcmp(rbuf+RBUFL-3,"\015AT") || /* See if it's "AT" */ !strcmp(rbuf+RBUFL-3," AT")) mdmecho = 1; /* debug(F111,"MDMECHO-T",rbuf+RBUFL-3,mdmecho); */ break; default: /* Other characters, accumulate. */ okstatus = 0; break; } } } debug(F101,"getok","",okstatus); /* <-- It's a lie (why?) */ #ifdef NTSIG ckThreadEnd(threadinfo); #endif /* NTSIG */ SIGRETURN; } static SIGTYP #ifdef CK_ANSIC failok(void * threadinfo) #else /* CK_ANSIC */ failok(threadinfo) VOID * threadinfo; #endif /* CK_ANSIC */ /* failok */ { debug(F100,"longjmp returned to getok()","",0); debug(F100,"getok timeout","",0); SIGRETURN; } int getok(n, strict) int n, strict; { debug(F101,"getok entry n","",n); okstatus = 0; okn = n; okstrict = strict; #ifdef DYNAMIC if (!rbuf) { if (!(rbuf = malloc(RBUFL+1))) { /* Allocate input line buffer */ dialsta = DIA_IE; return(-1); } debug(F101,"GETOK rbuf malloc ok","",RBUFL+1); } #endif /* DYNAMIC */ mdmecho = 0; /* Assume no echoing of commands */ debug(F100,"about to alrm_execute dook()","",0); alrm_execute( ckjaddr(okbuf), n, oktimo, dook, failok ) ; debug(F100,"returning from alrm_execute dook()","",0); ttflui(); /* Flush input buffer */ return(okstatus); /* Return status */ } /* G E T H R N -- Get Hayes Result Numeric */ static VOID gethrn() { char c; int x; /* Hayes numeric result codes (Hayes 1200 and higher): 0 = OK 1 = CONNECT at 300 bps (or 1200 bps on Hayes 1200 with basic code set) 2 = RING 3 = NO CARRIER 4 = ERROR (in command line) 5 = CONNECT 1200 (extended code set) Hayes 2400 and higher: 6 = NO DIALTONE 7 = BUSY 8 = NO ANSWER 9 = (there is no 9) 10 = CONNECT 2400 Reportedly, the codes for Hayes V.32 modems are: 1x = CONNECT 5x = CONNECT 1200 9x = CONNECT 2400 11x = CONNECT 4800 12x = CONNECT 9600 Where: x: suffix: R = RELIABLE RC = RELIABLE COMPRESSED L = LAPM LC = LAPM COMPRESSED And for Telebits, all the above, except no suffix in numeric mode, plus: 11 = CONNECT 4800 12 = CONNECT 9600 13 = CONNECT 14400 14 = CONNECT 19200 15 = CONNECT 38400 16 = CONNECT 57600 20 = CONNECT 300/REL (= MNP) 22 = CONNECT 1200/REL (= MNP) 23 = CONNECT 2400/REL (= MNP) 46 = CONNECT 7512 (i.e. 75/1200) 47 = CONNECT 1275 (i.e. 1200/75) 48 = CONNECT 7200 49 = CONNECT 12000 50 = CONNECT FAST (not on T1600/3000) 52 = RRING 53 = DIALING 54 = NO PROMPTTONE 61 = CONNECT FAST/KERM (Kermit spoof) 70 = CONNECT FAST/COMP (PEP + compression) 71 = CONNECT FAST/KERM/COMP (PEP + compression + Kermit spoof) And for others, lots of special cases below... */ #define NBUFL 8 char nbuf[NBUFL+1]; /* Response buffer */ int i = 0, j = 0; /* Buffer pointers */ debug(F101,"RESPONSE mdmecho","",mdmecho); if (mdmecho) { /* Sponge up dialing string echo. */ while (1) { c = (char) (ddinc(0) & 0x7f); debug(F000,"SPONGE","",c); dialoc(c); if (c == CR) break; } } while (mdmstat == 0) { /* Read response */ for (i = 0; i < NBUFL; i++) /* Clear the buffer */ nbuf[i] = '\0'; i = 0; /* Reset the buffer pointer. */ c = (char) (ddinc(0) & 0177); /* Get first digit of response. */ /* using an untimed, blocking read. */ debug(F000,"RESPONSE-A","",c); dialoc(c); /* Echo it if requested. */ if (!isdigit(c)) /* If not a digit, keep looking. */ continue; nbuf[i++] = c; /* Got first digit, save it. */ while (c != CR && i < 8) { /* Read chars up to CR */ x = ddinc(0) & 0177; /* Get a character. */ c = (char) x; /* Got it OK. */ debug(F000,"RESPONSE-C","",c); if (c != CR) /* If it's not a carriage return, */ nbuf[i++] = c; /* save it. */ dialoc(c); /* Echo it. */ } nbuf[i] = '\0'; /* Done, terminate the buffer. */ debug(F110,"dial hayesnv lbuf",lbuf,0); debug(F111,"dial hayesnv got",nbuf,i); /* Separate any non-numeric suffix from the numeric result code with a null. */ for (j = i-1; (j > -1) && !isdigit(nbuf[j]); j--) nbuf[j+1] = nbuf[j]; j++; nbuf[j++] = '\0'; debug(F110,"dial hayesnv numeric",nbuf,0); debug(F111,"dial hayesnv suffix ",nbuf+j,j); /* Probably phone number echoing. */ if ((int)strlen(nbuf) > 3) continue; /* Now read and interpret the results... */ i = atoi(nbuf); /* Convert to integer */ switch (i) { case 0: mdmstat = D_PARTIAL; /* OK response */ break; case 1: /* CONNECT */ mdmstat = CONNECTED; /* Could be any speed */ break; case 2: /* RING */ if (dialdpy) printf("\r\n Local phone is ringing!\r\n"); mdmstat = D_FAILED; dialsta = DIA_RING; break; case 3: /* NO CARRIER */ if (dialdpy) printf("\r\n No Carrier.\r\n"); mdmstat = D_FAILED; dialsta = DIA_NOCA; break; case 4: /* ERROR */ if (dialdpy) printf("\r\n Modem Command Error.\r\n"); mdmstat = D_FAILED; dialsta = DIA_ERR; break; case 5: /* CONNECT 1200 */ spdchg(1200L); /* Change speed if necessary. */ mdmstat = CONNECTED; break; case 6: /* NO DIALTONE */ #ifndef MINIDIAL if (mymdmtyp == n_MICROLINK && atoi(diallcc) == 49 && dialdpy) printf("\r\n Dial Locked.\r\n"); /* Germany */ else #endif /* MINIDIAL */ if (dialdpy) printf("\r\n No Dialtone.\r\n"); mdmstat = D_FAILED; dialsta = DIA_NODT; break; case 7: /* BUSY */ if (dialdpy) printf("\r\n Busy.\r\n"); mdmstat = D_FAILED; dialsta = DIA_BUSY; break; case 8: /* NO ANSWER */ #ifndef MINIDIAL if (mymdmtyp == n_MICROLINK && atoi(diallcc) == 41 && dialdpy) printf("\r\n Dial Locked.\r\n"); /* Switzerland */ else #endif /* MINIDIAL */ if (dialdpy) printf("\r\n No Answer.\r\n"); mdmstat = D_FAILED; dialsta = DIA_NOAN; break; case 9: #ifndef MINIDIAL if (mymdmtyp == n_XJACK || mymdmtyp == n_SUPRAX) { spdchg(600); break; } /* fall thru */ #endif /* MINIDIAL */ case 10: /* CONNECT 2400 */ spdchg(2400L); /* Change speed if necessary. */ mdmstat = CONNECTED; break; #ifndef MINIDIAL /* Starting here, we get different meanings from different manufacturers */ case 11: if (mymdmtyp == n_USR) { if (dialdpy) printf(" Ringing...\r\n"); } else { spdchg(4800L); /* CONNECT 4800 */ mdmstat = CONNECTED; } break; case 12: if (mymdmtyp == n_USR) { if (dialdpy) printf("\r\n Answered by voice.\r\n"); mdmstat = D_FAILED; dialsta = DIA_VOIC; } else if (mymdmtyp == n_KEEPINTOUCH) { spdchg(7200L); mdmstat = CONNECTED; } else { spdchg(9600L); mdmstat = CONNECTED; } break; case 13: if (mymdmtyp == n_ATT1900 || mymdmtyp == n_ATT1910) { if (dialdpy) printf(" Wait...\r\n"); break; } else if (mymdmtyp == n_USR || mymdmtyp == n_USRX2) spdchg(9600L); else if (is_rockwell || is_supra || mymdmtyp == n_ZOLTRIX || mymdmtyp == n_XJACK) spdchg(7200L); else if (mymdmtyp != n_MICROLINK) spdchg(14400L); mdmstat = CONNECTED; break; case 14: if (is_rockwell || is_supra || mymdmtyp == n_XJACK) spdchg(12000L); else if (mymdmtyp == n_DATAPORT || mymdmtyp == n_MICROLINK) spdchg(14400L); else if (mymdmtyp == n_KEEPINTOUCH) spdchg(9600L); else if (mymdmtyp != n_USR && mymdmtyp != n_ZOLTRIX) spdchg(19200L); mdmstat = CONNECTED; break; case 15: if (is_rockwell || is_supra || mymdmtyp == n_ZOLTRIX || mymdmtyp == n_XJACK) spdchg(14400L); else if (mymdmtyp == n_USR) spdchg(1200L); else if (mymdmtyp == n_ZYXEL || mymdmtyp == n_INTEL) spdchg(7200L); else if (mymdmtyp == n_DATAPORT) spdchg(19200L); else spdchg(38400L); mdmstat = CONNECTED; break; case 16: if (is_rockwell || is_supra || mymdmtyp == n_ZOLTRIX || mymdmtyp == n_XJACK) spdchg(19200L); else if (mymdmtyp == n_USR) spdchg(2400L); else if (mymdmtyp == n_DATAPORT) spdchg(7200L); else if (mymdmtyp != n_ZYXEL && mymdmtyp != n_INTEL) /* 12000 */ spdchg(57600L); mdmstat = CONNECTED; break; case 17: if (mymdmtyp != n_DATAPORT || mymdmtyp == n_XJACK) /* 16800 */ spdchg(38400L); else if (mymdmtyp == n_ZYXEL || mymdmtyp == n_INTEL) spdchg(14400L); else if (mymdmtyp == n_KEEPINTOUCH) spdchg(14400L); else if (mymdmtyp == n_USR) spdchg(9600L); mdmstat = CONNECTED; break; case 18: if (is_rockwell || is_supra || mymdmtyp == n_ZOLTRIX || mymdmtyp == n_XJACK || mymdmtyp == n_MHZATT || mymdmtyp == n_LUCENT) spdchg(57600L); else if (mymdmtyp == n_INTEL) spdchg(19200L); else if (mymdmtyp == n_USR || mymdmtyp == n_USRX2) spdchg(4800L); mdmstat = CONNECTED; break; case 19: if (mymdmtyp == n_DATAPORT) spdchg(300L); else if (mymdmtyp == n_ZYXEL || mymdmtyp == n_INTEL) spdchg(38400L); else spdchg(115200L); mdmstat = CONNECTED; break; case 20: if (mymdmtyp == n_USR || mymdmtyp == n_USRX2) spdchg(7200L); else if (mymdmtyp == n_DATAPORT) spdchg(2400L); else if (mymdmtyp == n_ZYXEL || mymdmtyp == n_INTEL) spdchg(57600L); else spdchg(300L); mdmstat = CONNECTED; break; case 21: if (mymdmtyp == n_DATAPORT) spdchg(4800L); mdmstat = CONNECTED; break; case 22: if (is_rockwell || is_supra || mymdmtyp == n_XJACK) spdchg(8880L); else if (mymdmtyp == n_DATAPORT) spdchg(9600L); else if (mymdmtyp == n_KEEPINTOUCH) spdchg(300L); else if (!is_hayeshispd) spdchg(1200L); mdmstat = CONNECTED; break; case 23: if (is_hayeshispd || is_supra || mymdmtyp == n_MULTI || mymdmtyp == n_XJACK) spdchg(8880L); else if (mymdmtyp != n_DATAPORT && !is_rockwell) /* 12000 */ spdchg(2400L); mdmstat = CONNECTED; break; case 24: if (is_rockwell || is_supra || mymdmtyp == n_XJACK) { mdmstat = D_FAILED; dialsta = DIA_DELA; /* Delayed */ break; } else if (is_hayeshispd || mymdmtyp == n_LUCENT) spdchg(7200L); else if (mymdmtyp == n_DATAPORT) spdchg(14400L); else if (mymdmtyp == n_INTEL || mymdmtyp == n_KEEPINTOUCH) spdchg(1200L); mdmstat = CONNECTED; break; case 25: if (mymdmtyp == n_USR || mymdmtyp == n_USRX2) spdchg(14400L); else if (mymdmtyp == n_LUCENT) spdchg(12000L); else if (is_motorola) spdchg(9600L); else if (mymdmtyp == n_INTEL || mymdmtyp == n_KEEPINTOUCH) spdchg(2400L); mdmstat = CONNECTED; break; case 26: if (mymdmtyp == n_DATAPORT) spdchg(19200L); else if (mymdmtyp == n_INTEL || mymdmtyp == n_KEEPINTOUCH) spdchg(4800L); mdmstat = CONNECTED; break; case 27: if (mymdmtyp == n_DATAPORT) spdchg(38400L); else if (mymdmtyp == n_INTEL || mymdmtyp == n_KEEPINTOUCH) spdchg(7200L); else if (mymdmtyp == n_MHZATT) spdchg(8880L); mdmstat = CONNECTED; break; case 28: if (mymdmtyp == n_DATAPORT) spdchg(7200L); else if (mymdmtyp == n_INTEL || mymdmtyp == n_KEEPINTOUCH) spdchg(9600L); else if (mymdmtyp == n_MHZATT || mymdmtyp == n_LUCENT) spdchg(38400L); mdmstat = CONNECTED; break; case 29: if (is_motorola) spdchg(4800L); else if (mymdmtyp == n_DATAPORT) spdchg(19200L); mdmstat = CONNECTED; break; case 30: if (mymdmtyp == n_INTEL || mymdmtyp == n_KEEPINTOUCH) { spdchg(14400L); mdmstat = CONNECTED; } /* fall thru on purpose... */ case 31: if (mymdmtyp == n_UCOM_AT || mymdmtyp == n_MICROLINK) { spdchg(4800L); mdmstat = CONNECTED; } else if (is_motorola) { spdchg(57600L); mdmstat = CONNECTED; } break; case 32: if (is_rockwell || is_supra || mymdmtyp == n_XJACK) { mdmstat = D_FAILED; dialsta = DIA_BLCK; /* Blacklisted */ } else if (mymdmtyp == n_UCOM_AT || mymdmtyp == n_MICROLINK) { spdchg(9600L); mdmstat = CONNECTED; } else if (mymdmtyp == n_KEEPINTOUCH) { spdchg(300L); mdmstat = CONNECTED; } else if (mymdmtyp == n_INTEL) { spdchg(2400L); mdmstat = CONNECTED; } break; case 33: /* FAX connection */ if (is_rockwell || is_supra || mymdmtyp == n_ZOLTRIX || mymdmtyp == n_XJACK) { mdmstat = D_FAILED; dialsta = DIA_FAX; } else if (mymdmtyp == n_UCOM_AT || is_motorola || mymdmtyp == n_MICROLINK ) { spdchg(9600L); mdmstat = CONNECTED; } else if (mymdmtyp == n_MHZATT) { spdchg(115200L); mdmstat = CONNECTED; } break; case 34: if (mymdmtyp == n_INTEL || mymdmtyp == n_KEEPINTOUCH) { spdchg(1200L); mdmstat = CONNECTED; } else if (mymdmtyp == n_MICROLINK) { spdchg(7200L); mdmstat = CONNECTED; } break; case 35: if (is_rockwell) { spdchg(300L); dialsta = CONNECTED; } else if (is_motorola) { spdchg(14400L); mdmstat = CONNECTED; } else if (mymdmtyp == n_INTEL || mymdmtyp == n_KEEPINTOUCH) { spdchg(2400L); mdmstat = CONNECTED; } else if (mymdmtyp == n_MICROLINK) { spdchg(7200L); mdmstat = CONNECTED; } else if (mymdmtyp == n_ZOLTRIX || mymdmtyp == n_XJACK) /* DATA */ mdmstat = CONNECTED; break; case 36: if (mymdmtyp == n_UCOM_AT) { spdchg(19200L); mdmstat = CONNECTED; } else if (is_motorola) { spdchg(1200L); mdmstat = CONNECTED; } else if (mymdmtyp == n_INTEL || mymdmtyp == n_KEEPINTOUCH) { spdchg(4800L); mdmstat = CONNECTED; } break; case 37: if (mymdmtyp == n_UCOM_AT) { spdchg(19200L); mdmstat = CONNECTED; } else if (is_motorola) { spdchg(2400L); mdmstat = CONNECTED; } else if (mymdmtyp == n_INTEL || mymdmtyp == n_KEEPINTOUCH) { spdchg(7200L); mdmstat = CONNECTED; } break; case 38: if (is_motorola) { spdchg(4800L); mdmstat = CONNECTED; } else if (mymdmtyp == n_INTEL || mymdmtyp == n_KEEPINTOUCH) { spdchg(9600L); mdmstat = CONNECTED; } /* fall thru on purpose... */ case 39: if (mymdmtyp == n_UCOM_AT) { spdchg(38400L); mdmstat = CONNECTED; } else if (is_motorola) { spdchg(9600L); mdmstat = CONNECTED; } else if (mymdmtyp == n_MICROLINK) { spdchg(14400L); mdmstat = CONNECTED; } break; case 40: if (mymdmtyp == n_UCOM_AT) { mdmstat = D_FAILED; dialsta = DIA_NOCA; } else if (is_motorola || mymdmtyp == n_INTEL || mymdmtyp == n_KEEPINTOUCH) { spdchg(14400L); mdmstat = CONNECTED; } break; case 41: if (is_motorola) { spdchg(19200L); mdmstat = CONNECTED; } break; case 42: if (mymdmtyp == n_KEEPINTOUCH) { spdchg(300L); mdmstat = CONNECTED; } else if (is_motorola) { spdchg(38400L); mdmstat = CONNECTED; } /* fall thru on purpose... */ case 43: if (mymdmtyp == n_UCOM_AT) { spdchg(57600L); mdmstat = CONNECTED; } else if (mymdmtyp == n_USRX2) mdmstat = CONNECTED; /* 168000 */ break; case 44: if (is_rockwell) { spdchg(8800L); dialsta = CONNECTED; } else if (is_motorola) { spdchg(7200L); mdmstat = CONNECTED; } else if (mymdmtyp == n_INTEL || mymdmtyp == n_KEEPINTOUCH) { spdchg(1200L); mdmstat = CONNECTED; } break; case 45: if (is_motorola) { spdchg(57600L); mdmstat = CONNECTED; } else if (mymdmtyp == n_INTEL || mymdmtyp == n_KEEPINTOUCH) { spdchg(2400L); mdmstat = CONNECTED; } else if (n_USR) { spdchg(14400L); mdmstat = CONNECTED; } break; case 46: if (is_rockwell) spdchg(1200L); else if (mymdmtyp == n_INTEL || mymdmtyp == n_KEEPINTOUCH) spdchg(4800L); else spdchg(8880L); /* 75/1200 split speed */ mdmstat = CONNECTED; break; case 47: if (is_rockwell) spdchg(2400L); else if (mymdmtyp == n_INTEL || mymdmtyp == n_KEEPINTOUCH) spdchg(7200L); else printf("CONNECT 1200/75 - Not supported by C-Kermit\r\n"); mdmstat = CONNECTED; break; case 48: if (is_rockwell) spdchg(4800L); else if (mymdmtyp == n_INTEL || mymdmtyp == n_KEEPINTOUCH) spdchg(9600L); else spdchg(7200L); mdmstat = CONNECTED; break; case 49: if (is_rockwell) spdchg(7200L); mdmstat = CONNECTED; break; case 50: /* CONNECT FAST */ if (is_rockwell) spdchg(9600L); else if (mymdmtyp == n_INTEL || mymdmtyp == n_KEEPINTOUCH) spdchg(14400L); mdmstat = CONNECTED; break; case 51: if (mymdmtyp == n_UCOM_AT) { mdmstat = D_FAILED; dialsta = DIA_NODT; } break; case 52: /* RRING */ if (mymdmtyp == n_TELEBIT) if (dialdpy) printf(" Ringing...\r\n"); break; case 53: /* DIALING */ if (mymdmtyp == n_TELEBIT) if (dialdpy) printf(" Dialing...\r\n"); break; case 54: if (is_rockwell) { spdchg(19200L); mdmstat = CONNECTED; } else if (mymdmtyp == n_INTEL || mymdmtyp == n_KEEPINTOUCH) { spdchg(1200L); mdmstat = CONNECTED; } else if (mymdmtyp == n_TELEBIT) { if (dialdpy) printf("\r\n No Prompttone.\r\n"); mdmstat = D_FAILED; dialsta = DIA_NODT; } break; case 55: if (mymdmtyp == n_INTEL || mymdmtyp == n_KEEPINTOUCH) { spdchg(2400L); mdmstat = CONNECTED; } break; case 56: if (mymdmtyp == n_INTEL || mymdmtyp == n_KEEPINTOUCH) { spdchg(4800L); mdmstat = CONNECTED; } break; case 57: if (mymdmtyp == n_INTEL || mymdmtyp == n_KEEPINTOUCH) { spdchg(7200L); mdmstat = CONNECTED; } break; case 58: if (mymdmtyp == n_INTEL || mymdmtyp == n_KEEPINTOUCH) { spdchg(9600L); mdmstat = CONNECTED; } break; case 59: if (mymdmtyp == n_INTEL) /* 12000 */ mdmstat = CONNECTED; break; case 60: if (mymdmtyp == n_INTEL || mymdmtyp == n_KEEPINTOUCH) { spdchg(14400L); mdmstat = CONNECTED; } break; case 64: if (mymdmtyp == n_INTEL) { spdchg(1200L); mdmstat = CONNECTED; } else if (is_supra) { spdchg(28800L); mdmstat = CONNECTED; } break; case 65: if (mymdmtyp == n_INTEL || mymdmtyp == n_KEEPINTOUCH) { spdchg(2400L); mdmstat = CONNECTED; } break; case 66: if (mymdmtyp == n_INTEL || mymdmtyp == n_KEEPINTOUCH) { spdchg(4800L); mdmstat = CONNECTED; } break; case 67: if (mymdmtyp == n_INTEL || mymdmtyp == n_KEEPINTOUCH) { spdchg(7200L); mdmstat = CONNECTED; } break; case 68: if (mymdmtyp == n_INTEL || mymdmtyp == n_KEEPINTOUCH) { spdchg(9600L); mdmstat = CONNECTED; } break; case 69: if (mymdmtyp == n_INTEL || mymdmtyp == n_KEEPINTOUCH) /* 12000 */ mdmstat = CONNECTED; break; case 70: if (mymdmtyp == n_INTEL || mymdmtyp == n_KEEPINTOUCH) { spdchg(14400L); mdmstat = CONNECTED; } break; case 73: if (mymdmtyp == n_UCOM_AT) { spdchg(115200L); mdmstat = CONNECTED; break; } /* else fall thru */ if (mymdmtyp == n_TELEBIT) /* Early models only */ mdmstat = CONNECTED; break; case 85: if (mymdmtyp == n_USR || mymdmtyp == n_USRX2) spdchg(19200L); mdmstat = CONNECTED; break; case 91: /* 21600 */ case 99: /* 24000 */ case 103: /* 26400 */ if (mymdmtyp == n_USRX2) mdmstat = CONNECTED; break; case 107: if (mymdmtyp == n_USR || mymdmtyp == n_USRX2) { spdchg(28800L); mdmstat = CONNECTED; } break; case 151: /* 312000 */ case 155: /* 336000 */ if (mymdmtyp == n_USRX2) mdmstat = CONNECTED; break; #endif /* MINIDIAL */ default: #ifndef MINIDIAL if (mymdmtyp == n_USR || mymdmtyp == n_USRX2 || is_hayeshispd || is_rockwell) #endif /* MINIDIAL */ if (i > 12) /* There are hundreds of them... */ mdmstat = CONNECTED; break; } } if (mdmstat == CONNECTED && nbuf[j] != '\0') { if (dialdpy) { printf("\r\n"); if (nbuf[j] == 'R') printf(" RELIABLE"); if (nbuf[j] == 'L') printf(" LAPM"); if (nbuf[j+1] == 'C') printf(" COMPRESSED"); printf("\r\n"); } ckstrncpy(lbuf,nbuf,LBUFL); /* (for messages...) */ } } static VOID /* Get Hayes Result in Word mode */ gethrw() { char *cptr, *s; long conspd; if (mdmspd && !network) { s = lbuf; while (*s != '\0' && *s != 'C') s++; cptr = (*s == 'C') ? s : NULL; conspd = 0L; if ((cptr != NULL) && !strncmp(cptr,"CONNECT ",8)) { if ((int)strlen(cptr) < 9) /* Just CONNECT, */ conspd = 300L; /* use 300 bps */ else if (isdigit(*(cptr+8))) /* not CONNECT FAST */ conspd = atol(cptr + 8); /* CONNECT nnnn */ if (conspd != speed) { if ((conspd / 10L) > 0) { if (ttsspd((int) (conspd / 10L)) < 0) { printf(" Can't change speed to %ld\r\n", conspd); } else { speed = conspd; mdmstat = CONNECTED; if ( !quiet && !backgrd ) printf(" Speed changed to %ld\r\n", conspd); } } } /* Expanded to handle any conceivable speed */ } } #ifndef MINIDIAL if (mymdmtyp == n_TELEBIT) { if (didweget(lbuf,"CONNECT FAST/KERM")) { mdmstat = CONNECTED; if (dialdpy) printf("FAST/KERM "); return; } } #endif /* MINIDIAL */ if (didweget(lbuf,"RRING") || didweget(lbuf,"RINGING") || didweget(lbuf,"DIALING")) { mdmstat = 0; } else if (didweget(lbuf,"CONNECT")) { mdmstat = CONNECTED; } else if (didweget(lbuf,"OK")) { if (partial) { mdmstat = D_PARTIAL; } else { mdmstat = D_FAILED; dialsta = DIA_ERR; } } else if (didweget(lbuf,"NO CARRIER")) { mdmstat = D_FAILED; dialsta = DIA_NOCA; } else if (didweget(lbuf,"NO DIALTONE")) { mdmstat = D_FAILED; dialsta = DIA_NODT; } else if (didweget(lbuf,"NO DIAL TONE")) { mdmstat = D_FAILED; dialsta = DIA_NODT; } else if (didweget(lbuf,"BUSY")) { mdmstat = D_FAILED; dialsta = DIA_BUSY; } else if (didweget(lbuf,"NO ANSWER")) { mdmstat = D_FAILED; dialsta = DIA_NOAN; } else if (didweget(lbuf,"VOICE")) { mdmstat = D_FAILED; dialsta = DIA_VOIC; } else if (didweget(lbuf,"VCON")) { mdmstat = D_FAILED; dialsta = DIA_VOIC; } else if (didweget(lbuf,"NO PROMPT TONE")) { mdmstat = D_FAILED; dialsta = DIA_NODT; } else if (didweget(lbuf,"REMOTE ACCESS FAILED")) { mdmstat = D_FAILED; dialsta = DIA_NOCA; } else if (didweget(lbuf,"FAX")) { mdmstat = D_FAILED; dialsta = DIA_FAX; } else if (didweget(lbuf,"WAIT - CONNECTING") || didweget(lbuf,"WAIT-CONNECTING")) { /* AT&T STU-III 19xx */ mdmstat = 0; } else if (didweget(lbuf,"DELAYED")) { mdmstat = D_FAILED; dialsta = DIA_DELA; } else if (didweget(lbuf,"BLACKLISTED")) { mdmstat = D_FAILED; dialsta = DIA_BLCK; } else if (didweget(lbuf,"COMPRESSION")) { mdmstat = 0; } else if (didweget(lbuf,"PROTOCOL")) { mdmstat = 0; } else if (didweget(lbuf,"DIAL LOCKED")) { /* Germany, Austria, Schweiz */ mdmstat = D_FAILED; dialsta = DIA_BLCK; } else if ( didweget(lbuf,"RING") || didweget(lbuf,"RING1") || /* Distinctive Ring 1 */ didweget(lbuf,"RING2") || /* Distinctive Ring 2 */ didweget(lbuf,"RING3") ) { mdmstat = (func_code == 0) ? D_FAILED : 0; dialsta = DIA_RING; } else if (didweget(lbuf,"ERROR")) { mdmstat = D_FAILED; dialsta = DIA_ERR; } else if (didweget(lbuf,"CARRIER")) { /* Boca / Rockwell family */ #ifdef COMMENT if (is_rockwell) #endif /* COMMENT */ mdmstat = 0; #ifdef COMMENT /* Does CARRIER ever mean the same as CONNECT? */ else mdmstat = CONNECTED; #endif /* COMMENT */ } else if (didweget(lbuf,"DATA")) { /* Boca / Rockwell family */ /* This message is sent when the modem is in FAX mode */ /* So setting this to CONNECTED may not be appropriate */ /* We must send ATO\015 to the modem in response */ /* Then we will get a CONNECTED message */ mdmstat = CONNECTED; } else if (didweget(lbuf,"DIGITAL LINE")) { mdmstat = D_FAILED; dialsta = DIA_DIGI; } else if (didweget(lbuf,"DATE")) { /* Caller ID Date */ debug(F110,"CALLID DATE",lbuf,0); /* Format is "DATE = MMDD" */ makestr(&callid_date,lbuf); } else if (didweget(lbuf,"TIME")) { /* Caller ID Time */ /* Format is "TIME = HHMM" */ debug(F110,"CALLID TIME",lbuf,0); makestr(&callid_time,lbuf); } else if (didweget(lbuf,"NAME")) { /* Caller ID Name */ /* Format is "NAME = " */ debug(F110,"CALLID NAME",lbuf,0); makestr(&callid_name,lbuf); } else if (didweget(lbuf,"NMBR")) { /* Caller ID Number */ /* Format is "NMBR = , 'P' or 'O'" */ /* 'P' means Privacy Requested */ /* 'O' means Out of Service or Not available */ debug(F110,"CALLID NMBR",lbuf,0); makestr(&callid_nmbr,lbuf); } else if (didweget(lbuf,"MESG")) { /* Caller ID Unrecognized Message */ /* Format is "MESG = " */ debug(F110,"CALLID MESG",lbuf,0); makestr(&callid_mesg,lbuf); } } /* Maybe hang up the phone, depending on various SET DIAL parameters. */ int dialhup() { int x = 0; if (dialhng && dialsta != DIA_PART) { /* DIAL HANGUP ON? */ x = mdmhup(); /* Try modem-specific method first */ debug(F101,"dialhup mdmhup","",x); if (x > 0) { /* If it worked, */ dialsta = DIA_HUP; if (dialdpy) printf(" Modem hangup OK\r\n"); /* fine. */ } else if (network /* If we're telnetted to */ #ifdef TN_COMPORT && !istncomport() /* (without RFC 2217) */ #endif /* TN_COMPORT */ ) { dialsta = DIA_HANG; if (dialdpy) /* a modem server, just print a msg */ printf(" WARNING - modem hangup failed\r\n"); /* don't hangup! */ return(0); } else { /* Otherwise */ x = tthang(); /* Tell the OS to turn off DTR. */ if (x > 0) { /* Yes, tell results from tthang() */ dialsta = DIA_HUP; if (dialdpy) printf(" Hangup OK\r\n"); } else if (x == 0) { if (dialdpy) printf(" Hangup skipped\r\n"); } else { dialsta = DIA_HANG; if (dialdpy) perror(" Hangup error"); } ttflui(); } } else if (dialdpy) printf(" Hangup skipped\r\n"); /* DIAL HANGUP OFF */ return(x); } /* M D M H U P -- Sends escape sequence to modem, then sends its hangup command. Returns: 0: If modem type is 0 (direct serial connection), or if modem type is < 0 (network connection), or if no action taken because DIAL MODEM-HANGUP is OFF) or because no hangup string for current modem type, or C-Kermit is in remote mode, or if action taken but there was no positive response from modem; 1: Success: modem is in command state and acknowledged the hangup command; -1: On modem command error. */ int mdmhup() { #ifdef MDMHUP int m, x = 0; int xparity; int savcarr; extern int ttcarr; char *s, *p, c; MDMINF * mp = NULL; debug(F101,"mdmhup dialmhu","",dialmhu); /* MODEM-HANGUP METHOD */ debug(F101,"mdmhup local","",local); if (dialmhu == 0 || local == 0) /* If DIAL MODEM-HANGUP is OFF, */ return(0); /* or not in local mode, fail. */ debug(F101,"mdmhup dialsta","",dialsta); debug(F101,"mdmhup mdmset","",mdmset); if (dialsta != DIA_OK && !mdmset) /* It's not a dialed connection */ return(0); #ifdef CK_TAPI if (tttapi && !tapipass) /* Don't hangup if using TAPI */ return(0); #endif /* CK_TAPI */ #ifdef COMMENT /* No, we still need this for modems that ignore DTR */ if (mymdmtyp == n_GENERIC && !network) return(0); #endif /* COMMENT */ debug(F101,"mdmhup dialesc","",dialesc); if (dialesc < 0) return(0); /* No modem escape-character, fail. */ savcarr = ttcarr; ttcarr = CAR_OFF; x = ttchk(); ttcarr = savcarr; debug(F101,"mdmhup ttchk","",x); if (x < 0) /* There appears to be no connection */ return(0); x = 0; #ifdef OS2 /* In OS/2, if CARRIER is OFF, and there is indeed no carrier signal, any attempt to do i/o at this point can hang the program. This might be true for other operating systems too. */ if (!network /* Not a network connection */ #ifdef TN_COMPORT || istncomport() #endif /* TN_COMPORT */ ) { m = ttgmdm(); /* Get modem signals */ if ((m > -1) && (m & BM_DCD == 0)) /* Check for carrier */ return(0); /* No carrier, skip the rest */ } #endif /* OS2 */ debug(F111,"mdmhup network",ttname,network); debug(F101,"mdmhup mymdmtyp","",mymdmtyp); debug(F101,"mdmhup mdmtyp","",mdmtyp); /* In case of HANGUP before DIAL */ if (network && mdmtyp < 1) /* SET HOST but no subsequent */ return(0); /* SET MODEM TYPE... */ if (mymdmtyp == 0 && mdmtyp > 0) mymdmtyp = mdmtyp; if (mymdmtyp < 1) /* Not using a modem */ return(0); if (mymdmtyp > 0) /* An actual modem... */ mp = modemp[mymdmtyp]; if (!mp) { /* Get pointer to its MDMINF struct */ debug(F100,"mdmhup no MDMINF","",0); return(0); } mdmcapas = dialcapas ? dialcapas : mp->capas; xx_ok = mp->ok_fn; /* Pointer to response reader */ s = dialhcmd ? dialhcmd : mp->hup_str; /* Get hangup command */ if (!s) s = ""; debug(F110,"mdmhup hup_str",s,0); if (!*s) return(0); /* If none, fail. */ if (ttpkt(speed,FLO_DIAL,parity) < 0) /* Condition line for dialing */ return(-1); xparity = parity; /* Set PARITY to NONE temporarily */ parity = 0; /* In case they gave a SET MODEM ESCAPE command recently... */ if (dialesc < 0 || dialesc > 127) c = NUL; else c = (char) (dialesc ? dialesc : mp->esc_char); if (mdmcapas & CKD_AT) { /* Hayes compatible */ escbuf[0] = c; escbuf[1] = c; escbuf[2] = c; escbuf[3] = NUL; } else { /* Other */ escbuf[0] = c; escbuf[1] = NUL; } debug(F110,"mdmhup escbuf",escbuf,0); if (escbuf[0]) { /* Have escape sequence? */ debug(F101,"mdmhup esc_time",0,mp->esc_time); if (mp->esc_time) /* If we have a guard time */ msleep(mp->esc_time); /* Pause for guard time */ debug(F100,"mdmhup pause 1 OK","",0); #ifdef NETCONN /* Send modem's escape sequence */ if (network) { /* Must catch errors here. */ if (ttol((CHAR *)escbuf,(int)strlen((char *)escbuf)) < 0) { parity = xparity; return(-1); } debug(F110,"mdmhup ttslow net ok",escbuf,0); } else { #endif /* NETCONN */ ttslow((char *)escbuf,wr); /* Send escape sequence */ debug(F110,"mdmhup ttslow ok",escbuf,0); #ifdef NETCONN } #endif /* NETCONN */ if (mp->esc_time) /* Pause for guard time again */ msleep(mp->esc_time); else msleep(500); /* Wait half a sec for echoes. */ debug(F100,"mdmhup pause 1 OK","",0); #ifdef COMMENT ttflui(); /* Flush response or echo, if any */ debug(F100,"mdmhup ttflui OK","",0); #endif /* COMMENT */ } ttslow(s,wr); /* Now Send hangup string */ debug(F110,"mdmhup ttslow ok",s,0); /* This is not exactly right, but it works. If we are online: the modem says OK when it gets the escape sequence, and it says NO CARRIER when it gets the hangup command. If we are offline: the modem does NOT say OK (or anything else) when it gets the esc sequence, but it DOES say OK (and not NO CARRIER) when it gets the hangup command. So the following function should read the OK in both cases. Of course, this is somewhat Hayes-specific... */ if (xx_ok) { /* Look for OK response */ debug(F100,"mdmhup calling response function","",0); x = (*xx_ok)(3,1); /* Give it 3 seconds, be strict. */ debug(F101,"mdmhup hangup response","",x); msleep(500); /* Wait half a sec */ ttflui(); /* Get rid of NO CARRIER, if any */ } else { /* No OK function, */ x = 1; /* so assume it worked */ debug(F101,"mdmhup no ok_fn","",x); } parity = xparity; /* Restore prevailing parity */ return(x); /* Return OK function's return code. */ #else /* MDMHUP not defined. */ debug(F100,"mdmhup MDMHUP not defined","",0); return(0); /* Always fail. */ #endif /* MDMHUP */ } #endif /* NOICP */ #else /* NODIAL */ int mdmtyp = 0; /* Default modem type */ int /* To allow NODIAL versions to */ mdmhup() { /* call mdmhup(), so calls to */ return(0); /* mdmhup() need not be within */ } /* #ifndef NODIAL conditionals */ #endif /* NODIAL */ #else int mdmtyp = 0; /* Default modem type */ #endif /* NOLOCAL */ ckufio.c0000644000015300001460000075432611624035016011324 0ustar fdckermit/* C K U F I O -- Kermit file system support for UNIX, Aegis, and Plan 9 */ #define CK_NONBLOCK /* See zoutdump() */ #ifdef aegis char *ckzv = "Aegis File support, 9.0.216, 20 Aug 2011"; #else #ifdef Plan9 char *ckzv = "Plan 9 File support, 9.0.216, 20 Aug 2011"; #else char *ckzv = "UNIX File support, 9.0.216, 20 Aug 2011"; #endif /* Plan9 */ #endif /* aegis */ /* Author: Frank da Cruz , Columbia University Academic Information Systems, New York City, and others noted in the comments below. Note: CUCCA = Previous name of Columbia University Academic Information Systems. Note: AcIS = Previous of Columbia University Information Technology. Copyright (C) 1985, 2011, Trustees of Columbia University in the City of New York. All rights reserved. See the C-Kermit COPYING.TXT file or the copyright text in the ckcmai.c module for disclaimer and permissions. */ /* NOTE TO CONTRIBUTORS: This file, and all the other C-Kermit files, must be compatible with C preprocessors that support only #ifdef, #else, #endif, #define, and #undef. Please do not use #if, logical operators, or other preprocessor features in any of the portable C-Kermit modules. You can, of course, use these constructions in platform-specific modules where you know they are supported. */ /* Include Files */ #ifdef MINIX2 #define _MINIX #endif /* MINIX2 */ #include "ckcsym.h" #include "ckcdeb.h" #include "ckcasc.h" #ifndef NOCSETS #include "ckcxla.h" #endif /* NOCSETS */ /* To avoid pulling in all of ckuusr.h so we copy the few needed prototypes */ struct mtab { /* Macro table, like keyword table */ char *kwd; /* But with pointers for vals */ char *mval; /* instead of ints. */ short flgs; }; _PROTOTYP( int mlook, (struct mtab [], char *, int) ); _PROTOTYP( int dodo, (int, char *, int) ); _PROTOTYP( int parser, ( int ) ); #ifdef COMMENT /* This causes trouble in C-Kermit 8.0. I don't remember the original */ /* reason for this being here but it must have been needed at the time... */ #ifdef OSF13 #ifdef CK_ANSIC #ifdef _NO_PROTO #undef _NO_PROTO #endif /* _NO_PROTO */ #endif /* CK_ANSIC */ #endif /* OSF13 */ #endif /* COMMENT */ #ifndef HPUXPRE65 #include /* Error number symbols */ #else #ifndef ERRNO_INCLUDED #include /* Error number symbols */ #endif /* ERRNO_INCLUDED */ #endif /* HPUXPRE65 */ #include #ifdef MINIX2 #undef MINIX #undef CKSYSLOG #include #include #define NOFILEH #endif /* MINIX2 */ #ifdef MINIX #include #include #include #else #ifdef POSIX #include #else #ifdef SVR3 #include #endif /* SVR3 */ #endif /* POSIX */ #endif /* MINIX */ /* Directory Separator macros, to allow this module to work with both UNIX and OS/2: Because of ambiguity with the command line editor escape \ character, the directory separator is currently left as / for OS/2 too, because the OS/2 kernel also accepts / as directory separator. But this is subject to change in future versions to conform to the normal OS/2 style. */ #ifndef DIRSEP #define DIRSEP '/' #endif /* DIRSEP */ #ifndef ISDIRSEP #define ISDIRSEP(c) ((c)=='/') #endif /* ISDIRSEP */ #ifdef SDIRENT #define DIRENT #endif /* SDIRENT */ #ifdef XNDIR #include #else /* !XNDIR */ #ifdef NDIR #include #else /* !NDIR, !XNDIR */ #ifdef RTU #include "/usr/lib/ndir.h" #else /* !RTU, !NDIR, !XNDIR */ #ifdef DIRENT #ifdef SDIRENT #include #else #include #endif /* SDIRENT */ #else #include #endif /* DIRENT */ #endif /* RTU */ #endif /* NDIR */ #endif /* XNDIR */ #ifdef UNIX /* Pointer arg to wait() allowed */ #define CK_CHILD /* Assume this is safe in all UNIX */ #endif /* UNIX */ extern int binary, recursive, stathack; #ifdef CK_CTRLZ extern int eofmethod; #endif /* CK_CTRLZ */ #include /* Password file for shell name */ #ifdef CK_SRP #include /* SRP Password file */ #endif /* CK_SRP */ #ifdef HPUX10_TRUSTED #include #include #endif /* HPUX10_TRUSTED */ #ifdef COMMENT /* Moved to ckcdeb.h */ #ifdef POSIX #define UTIMEH #else #ifdef HPUX9 #define UTIMEH #endif /* HPUX9 */ #endif /* POSIX */ #endif /* COMMENT */ #ifdef SYSUTIMEH /* if requested, */ #include /* for extra fields required by */ #else /* 88Open spec. */ #ifdef UTIMEH /* or if requested */ #include /* (SVR4, POSIX) */ #ifndef BSD44 #ifndef V7 /* Not sure why this is here. What it implies is that the code bracketed by SYSUTIMEH is valid on all platforms on which we support time functionality. But we know that is not true because the BSD44 and V7 platforms do not support sys/utime.h and the data structures which are defined in them. Now this worked before because prior to today's changes the UTIMEH definition for BSD44 and V7 did not take place until after SYSUTIMEH was defined. It also would not have been a problem if the ordering of all the time blocks was consistent. All but one of the blocks were BSD44, V7, SYSUTIMEH, . That one case is where this problem was triggered. */ #define SYSUTIMEH /* Use this for both cases. */ #endif /* V7 */ #endif /* BSD44 */ #endif /* UTIMEH */ #endif /* SYSUTIMEH */ #ifndef NOTIMESTAMP #ifdef POSIX #ifndef AS400 #define TIMESTAMP #endif /* AS400 */ #endif /* POSIX */ #ifdef BSD44 /* BSD 4.4 */ #ifndef TIMESTAMP #define TIMESTAMP /* Can do file dates */ #endif /* TIMESTAMP */ #include #include #else /* Not BSD44 */ #ifdef BSD4 /* BSD 4.3 and below */ #define TIMESTAMP /* Can do file dates */ #include /* Need this */ #include /* Need this if really BSD */ #else /* Not BSD 4.3 and below */ #ifdef SVORPOSIX /* System V or POSIX */ #ifndef TIMESTAMP #define TIMESTAMP #endif /* TIMESTAMP */ #include /* void tzset(); (the "void" type upsets some compilers) */ #ifndef IRIX60 #ifndef ultrix #ifndef CONVEX9 /* ConvexOS 9.0, supposedly POSIX, has extern char *timezone(int,int) */ #ifndef Plan9 extern long timezone; #endif /* Plan9 */ #endif /* CONVEX9 */ #endif /* ultrix */ #endif /* IRIX60 */ #endif /* SVORPOSIX */ #endif /* BSD4 */ #endif /* BSD44 */ #ifdef COHERENT #include #endif /* COHERENT */ /* Is `y' a leap year? */ #define leap(y) (((y) % 4 == 0 && (y) % 100 != 0) || (y) % 400 == 0) /* Number of leap years from 1970 to `y' (not including `y' itself). */ #define nleap(y) (((y) - 1969) / 4 - ((y) - 1901) / 100 + ((y) - 1601) / 400) #endif /* NOTIMESTAMP */ #ifdef CIE #include /* File status */ #else #include #endif /* CIE */ /* Macro to alleviate isdir() calls internal to this module */ static struct stat STATBUF; #define xisdir(a) ((stat(a,&STATBUF)==-1)?0:(S_ISDIR(STATBUF.st_mode)?1:0)) extern char uidbuf[]; extern int xferlog; extern char * xferfile; int iklogopen = 0; static time_t timenow; #define IKSDMSGLEN CKMAXPATH+512 static char iksdmsg[IKSDMSGLEN]; extern int local; extern int server, en_mkd, en_cwd, en_del; /* Functions (n is one of the predefined file numbers from ckcker.h): zopeni(n,name) -- Opens an existing file for input. zopeno(n,name,attr,fcb) -- Opens a new file for output. zclose(n) -- Closes a file. zchin(n,&c) -- Gets the next character from an input file. zsinl(n,&s,x) -- Read a line from file n, max len x, into address s. zsout(n,s) -- Write a null-terminated string to output file, buffered. zsoutl(n,s) -- Like zsout, but appends a line terminator. zsoutx(n,s,x) -- Write x characters to output file, unbuffered. zchout(n,c) -- Add a character to an output file, unbuffered. zchki(name) -- Check if named file exists and is readable, return size. zchko(name) -- Check if named file can be created. zchkspa(name,n) -- Check if n bytes available to create new file, name. znewn(name,s) -- Make a new unique file name based on the given name. zdelet(name) -- Delete the named file. zxpand(string) -- Expands the given wildcard string into a list of files. znext(string) -- Returns the next file from the list in "string". zxrewind() -- Rewind zxpand list. zxcmd(n,cmd) -- Execute the command in a lower fork on file number n. zclosf() -- Close input file associated with zxcmd()'s lower fork. zrtol(n1,n2) -- Convert remote filename into local form. zltor(n1,n2) -- Convert local filename into remote form. zchdir(dirnam) -- Change working directory. zhome() -- Return pointer to home directory name string. zkself() -- Kill self, log out own job. zsattr(struct zattr *) -- Return attributes for file which is being sent. zstime(f, struct zattr *, x) - Set file creation date from attribute packet. zrename(old, new) -- Rename a file. zcopy(source,destination) -- Copy a file. zmkdir(path) -- Create the directory path if possible zfnqfp(fname,len,fullpath) - Determine full path for file name. zgetfs(name) -- return file size regardless of accessibility zchkpid(pid) -- tell if PID is valid and active */ /* Kermit-specific includes */ /* Definitions here supersede those from system include files. ckcdeb.h is included above. */ #include "ckcker.h" /* Kermit definitions */ #include "ckucmd.h" /* For keyword tables */ #include "ckuver.h" /* Version herald */ char *ckzsys = HERALD; /* File access checking ... There are two calls to access() in this module. If this program is installed setuid or setgid on a Berkeley-based UNIX system that does NOT incorporate the saved-original-effective-uid/gid feature, then, when we have swapped the effective and original uid/gid, access() fails because it uses what it thinks are the REAL ids, but we have swapped them. This occurs on systems where ANYBSD is defined, NOSETREU is NOT defined, and SAVEDUID is NOT defined. So, in theory, we should take care of this situation like so: ifdef ANYBSD ifndef NOSETREU ifndef SAVEDUID define SW_ACC_ID endif endif endif But we can't test such a general scheme everywhere, so let's only do this when we know we have to... */ #ifdef NEXT /* NeXTSTEP 1.0-3.0 */ #define SW_ACC_ID #endif /* NEXT */ /* Support for tilde-expansion in file and directory names */ #ifdef POSIX #define NAMEENV "LOGNAME" #else #ifdef BSD4 #define NAMEENV "USER" #else #ifdef ATTSV #define NAMEENV "LOGNAME" #endif /* ATTSV */ #endif /* BSD4 */ #endif /* POSIX */ /* Berkeley Unix Version 4.x */ /* 4.1bsd support from Charles E Brooks, EDN-VAX */ #ifdef BSD4 #ifdef MAXNAMLEN #define BSD42 #endif /* MAXNAMLEN */ #endif /* BSD4 */ /* Definitions of some system commands */ char *DELCMD = "rm -f "; /* For file deletion */ char *CPYCMD = "cp "; /* For file copy */ char *RENCMD = "mv "; /* For file rename */ char *PWDCMD = "pwd "; /* For saying where I am */ #ifdef COMMENT #ifdef HPUX10 char *DIRCMD = "/usr/bin/ls -l "; /* For directory listing */ char *DIRCM2 = "/usr/bin/ls -l "; /* For directory listing, no args */ #else char *DIRCMD = "/bin/ls -l "; /* For directory listing */ char *DIRCM2 = "/bin/ls -l "; /* For directory listing, no args */ #endif /* HPUX10 */ #else char *DIRCMD = "ls -l "; /* For directory listing */ char *DIRCM2 = "ls -l "; /* For directory listing, no args */ #endif /* COMMENT */ char *TYPCMD = "cat "; /* For typing a file */ #ifdef HPUX char *MAILCMD = "mailx"; /* For sending mail */ #else #ifdef DGUX540 char *MAILCMD = "mailx"; #else #ifdef UNIX #ifdef CK_MAILCMD char *MAILCMD = CK_MAILCMD; /* CFLAGS override */ #else char *MAILCMD = "Mail"; /* Default */ #endif /* CK_MAILCMD */ #else char *MAILCMD = ""; #endif /* UNIX */ #endif /* HPUX */ #endif /* DGUX40 */ #ifdef UNIX #ifdef ANYBSD /* BSD uses lpr to spool */ #ifdef DGUX540 /* And DG/UX */ char * PRINTCMD = "lp"; #else char * PRINTCMD = "lpr"; #endif /* DGUX540 */ #else /* Sys V uses lp */ #ifdef TRS16 /* except for Tandy-16/6000... */ char * PRINTCMD = "lpr"; #else char * PRINTCMD = "lp"; #endif /* TRS16 */ #endif /* ANYBSD */ #else /* Not UNIX */ #define PRINTCMD "" #endif /* UNIX */ #ifdef FT18 /* Fortune For:Pro 1.8 */ #undef BSD4 #endif /* FT18 */ #ifdef BSD4 char *SPACMD = "pwd ; df ."; /* Space in current directory */ #else #ifdef FT18 char *SPACMD = "pwd ; du ; df ."; #else char *SPACMD = "df "; #endif /* FT18 */ #endif /* BSD4 */ char *SPACM2 = "df "; /* For space in specified directory */ #ifdef FT18 #define BSD4 #endif /* FT18 */ #ifdef BSD4 char *WHOCMD = "finger "; #else char *WHOCMD = "who "; #endif /* BSD4 */ /* More system-dependent includes, which depend on symbols defined */ /* in the Kermit-specific includes. Oh what a tangled web we weave... */ #ifdef COHERENT /* */ #define NOFILEH #endif /* COHERENT */ #ifdef MINIX #define NOFILEH #endif /* MINIX */ #ifdef aegis #define NOFILEH #endif /* aegis */ #ifdef unos #define NOFILEH #endif /* unos */ #ifndef NOFILEH #include #endif /* NOFILEH */ #ifndef is68k /* Whether to include */ #ifndef BSD41 /* All but a couple UNIXes have it. */ #ifndef FT18 #ifndef COHERENT #include #endif /* COHERENT */ #endif /* FT18 */ #endif /* BSD41 */ #endif /* is68k */ #ifdef COHERENT #ifdef _I386 #include #else #include #endif /* _I386 */ #endif /* COHERENT */ extern int inserver; /* I am IKSD */ int guest = 0; /* Anonymous user */ #ifdef IKSD extern int isguest; extern char * anonroot; #endif /* IKSD */ #ifdef CK_LOGIN #define GUESTPASS 256 static char guestpass[GUESTPASS] = { NUL, NUL }; /* Anonymous "password" */ static int logged_in = 0; /* Set when user is logged in */ static int askpasswd = 0; /* Have OK user, must ask for passwd */ #ifdef CK_PAM extern int gotemptypasswd; #endif /* CK_PAM */ #endif /* CK_LOGIN */ #ifdef CKROOT static char ckroot[CKMAXPATH+1] = { NUL, NUL }; static int ckrootset = 0; int ckrooterr = 0; #endif /* CKROOT */ _PROTOTYP( VOID ignorsigs, (void) ); _PROTOTYP( VOID restorsigs, (void) ); #ifdef SELECT _PROTOTYP( int ttwait, (int, int) ); /* ckutio.c */ #endif /* SELECT */ /* Change argument to "(const char *)" if this causes trouble. Or... if it causes trouble, then maybe it was already declared in a header file after all, so you can remove this prototype. */ #ifndef NDGPWNAM /* If not defined No Declare getpwnam... */ #ifndef _POSIX_SOURCE #ifndef NEXT #ifndef SVR4 /* POSIX already gave prototypes for these. */ #ifdef IRIX40 _PROTOTYP( struct passwd * getpwnam, (const char *) ); #else #ifdef IRIX51 _PROTOTYP( struct passwd * getpwnam, (const char *) ); #else #ifdef M_UNIX _PROTOTYP( struct passwd * getpwnam, (const char *) ); #else #ifdef HPUX9 _PROTOTYP( struct passwd * getpwnam, (const char *) ); #else #ifdef HPUX10 _PROTOTYP( struct passwd * getpwnam, (const char *) ); #else #ifdef DCGPWNAM _PROTOTYP( struct passwd * getpwnam, (const char *) ); #else _PROTOTYP( struct passwd * getpwnam, (char *) ); #endif /* DCGPWNAM */ #endif /* HPUX10 */ #endif /* HPUX9 */ #endif /* M_UNIX */ #endif /* IRIX51 */ #endif /* IRIX40 */ #ifndef SUNOS4 #ifndef HPUX9 #ifndef HPUX10 #ifndef _SCO_DS _PROTOTYP( struct passwd * getpwuid, (PWID_T) ); #endif /* _SCO_DS */ #endif /* HPUX10 */ #endif /* HPUX9 */ #endif /* SUNOS4 */ _PROTOTYP( struct passwd * getpwent, (void) ); #endif /* SVR4 */ #endif /* NEXT */ #endif /* _POSIX_SOURCE */ #endif /* NDGPWNAM */ #ifdef CK_SHADOW /* Shadow Passwords... */ #include #endif /* CK_SHADOW */ #ifdef CK_PAM /* PAM... */ #ifdef MACOSX #include #else /* MACOSX */ #include #endif /* MACOSX */ #ifndef PAM_SERVICE_TYPE /* Defines which PAM service we are */ #define PAM_SERVICE_TYPE "kermit" #endif /* PAM_SERVICE_TYPE */ #ifdef SOLARIS #define PAM_CONST #else /* SOLARIS */ #define PAM_CONST CONST #endif static char * pam_pw = NULL; int #ifdef CK_ANSIC pam_cb(int num_msg, PAM_CONST struct pam_message **msg, struct pam_response **resp, void *appdata_ptr ) #else /* CK_ANSIC */ pam_cb(num_msg, msg, resp, appdata_ptr) int num_msg; PAM_CONST struct pam_message **msg; struct pam_response **resp; void *appdata_ptr; #endif /* CK_ANSIC */ { int i; debug(F111,"pam_cb","num_msg",num_msg); for (i = 0; i < num_msg; i++) { char message[PAM_MAX_MSG_SIZE]; /* Issue prompt and get response */ debug(F111,"pam_cb","Message",i); debug(F111,"pam_cb",msg[i]->msg,msg[i]->msg_style); if (msg[i]->msg_style == PAM_ERROR_MSG) { debug(F111,"pam_cb","PAM ERROR",0); fprintf(stdout,"%s\n", msg[i]->msg); return(0); } else if (msg[i]->msg_style == PAM_TEXT_INFO) { debug(F111,"pam_cb","PAM TEXT INFO",0); fprintf(stdout,"%s\n", msg[i]->msg); return(0); } else if (msg[i]->msg_style == PAM_PROMPT_ECHO_OFF) { debug(F111,"pam_cb","Reading response, no echo",0); /* Ugly hack. We check to see if a password has been pushed */ /* into zvpasswd(). This would be true if the password was */ /* received by REMOTE LOGIN. */ if (pam_pw) { ckstrncpy(message,pam_pw,PAM_MAX_MSG_SIZE); } else readpass((char *)msg[i]->msg,message,PAM_MAX_MSG_SIZE); } else if (msg[i]->msg_style == PAM_PROMPT_ECHO_ON) { debug(F111,"pam_cb","Reading response, with echo",0); readtext((char *)msg[i]->msg,message,PAM_MAX_MSG_SIZE); } else { debug(F111,"pam_cb","unknown style",0); return(0); } /* Allocate space for this message's response structure */ resp[i] = (struct pam_response *) malloc(sizeof (struct pam_response)); if (!resp[i]) { int j; debug(F110,"pam_cb","malloc failure",0); for (j = 0; j < i; j++) { free(resp[j]->resp); free(resp[j]); } return(0); } /* Allocate a buffer for the response */ resp[i]->resp = (char *) malloc((int)strlen(message) + 1); if (!resp[i]->resp) { int j; debug(F110,"pam_cb","malloc failure",0); for (j = 0; j < i; j++) { free(resp[j]->resp); free(resp[j]); } free(resp[i]); return(0); } /* Return the results back to PAM */ strcpy(resp[i]->resp, message); /* safe (prechecked) */ resp[i]->resp_retcode = 0; } debug(F110,"pam_cb","Exiting",0); return(0); } #endif /* CK_PAM */ /* Define macros for getting file type */ #ifdef OXOS /* Olivetti X/OS 2.3 has S_ISREG and S_ISDIR defined incorrectly, so we force their redefinition. */ #undef S_ISREG #undef S_ISDIR #endif /* OXOS */ #ifdef UTSV /* Same deal for Amdahl UTSV */ #undef S_ISREG #undef S_ISDIR #endif /* UTSV */ #ifdef UNISYS52 /* And for UNISYS UTS V 5.2 */ #undef S_ISREG #undef S_ISDIR #endif /* UNISYS52 */ #ifdef ICLSVR3 /* And for old ICL versions */ #undef S_ISREG #undef S_ISDIR #endif /* ICLSVR3 */ #ifdef ISDIRBUG /* Also allow this from command line */ #ifdef S_ISREG #undef S_ISREG #endif /* S_ISREG */ #ifdef S_ISDIR #undef S_ISDIR #endif /* S_ISDIR */ #endif /* ISDIRBUG */ #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 */ #ifndef S_ISDIR #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) #endif /* S_ISDIR */ /* The following mainly for NeXTSTEP... */ #ifndef S_IWUSR #define S_IWUSR 0000200 #endif /* S_IWUSR */ #ifndef S_IRGRP #define S_IRGRP 0000040 #endif /* S_IRGRP */ #ifndef S_IWGRP #define S_IWGRP 0000020 #endif /* S_IWGRP */ #ifndef S_IXGRP #define S_IXGRP 0000010 #endif /* S_IXGRP */ #ifndef S_IROTH #define S_IROTH 0000004 #endif /* S_IROTH */ #ifndef S_IWOTH #define S_IWOTH 0000002 #endif /* S_IWOTH */ #ifndef S_IXOTH #define S_IXOTH 0000001 #endif /* S_IXOTH */ /* Define maximum length for a file name if not already defined. NOTE: This applies to a path segment (directory or file name), not the entire path string, which can be CKMAXPATH bytes long. */ #ifdef QNX #ifdef _MAX_FNAME #define MAXNAMLEN _MAX_FNAME #else #define MAXNAMLEN 48 #endif /* _MAX_FNAME */ #else #ifndef MAXNAMLEN #ifdef sun #define MAXNAMLEN 255 #else #ifdef FILENAME_MAX #define MAXNAMLEN FILENAME_MAX #else #ifdef NAME_MAX #define MAXNAMLEN NAME_MAX #else #ifdef _POSIX_NAME_MAX #define MAXNAMLEN _POSIX_NAME_MAX #else #ifdef _D_NAME_MAX #define MAXNAMLEN _D_NAME_MAX #else #ifdef DIRSIZ #define MAXNAMLEN DIRSIZ #else #define MAXNAMLEN 14 #endif /* DIRSIZ */ #endif /* _D_NAME_MAX */ #endif /* _POSIX_NAME_MAX */ #endif /* NAME_MAX */ #endif /* FILENAME_MAX */ #endif /* sun */ #endif /* MAXNAMLEN */ #endif /* QNX */ #ifdef COMMENT /* As of 2001-11-03 this is handled in ckcdeb.h */ /* Longest pathname ... */ /* Beware: MAXPATHLEN is one of UNIX's dirty little secrets. Where is it defined? Who knows... , , , , ... There is not necessarily even a definition for it anywhere, or it might have another name. If you get it wrong, bad things happen with getcwd() and/or getwd(). If you allocate a buffer that is too short, getwd() might write over memory and getcwd() will fail with ERANGE. The definitions of these functions (e.g. in SVID or POSIX.1) do not tell you how to determine the maximum path length in order to allocate a buffer that is the right size. */ #ifdef BSD44 #include /* For MAXPATHLEN */ #endif /* BSD44 */ #ifdef COHERENT #include /* for MAXPATHLEN, needed for -DDIRENT */ #endif /* COHERENT */ #endif /* COMMENT */ #ifdef MAXPATHLEN #ifdef MAXPATH #undef MAXPATH #endif /* MAXPATH */ #define MAXPATH MAXPATHLEN #else #ifdef PATH_MAX #define MAXPATH PATH_MAX #else #ifdef _POSIX_PATH_MAX #define MAXPATH _POSIX_PATH_MAX #else #ifdef BSD42 #define MAXPATH 1024 #else #ifdef SVR4 #define MAXPATH 1024 #else #define MAXPATH 255 #endif /* SVR4 */ #endif /* BSD42 */ #endif /* _POSIX_PATH_MAX */ #endif /* PATH_MAX */ #endif /* MAXPATHLEN */ /* Maximum number of filenames for wildcard expansion */ #ifndef MAXWLD /* Already defined in ckcdeb.h so the following is superfluous. */ /* Don't expect changing them to have any effect. */ #ifdef CK_SMALL #define MAXWLD 50 #else #ifdef BIGBUFOK #define MAXWLD 102400 #else #define MAXWLD 8192 #endif /* BIGBUFOK */ #endif /* CK_SMALL */ #endif /* MAXWLD */ static int maxnames = MAXWLD; /* Define the size of the string space for filename expansion. */ #ifndef DYNAMIC #ifdef PROVX1 #define SSPACE 500 #else #ifdef BSD29 #define SSPACE 500 #else #ifdef pdp11 #define SSPACE 500 #else #ifdef aegis #define SSPACE 10000 /* Size of string-generating buffer */ #else /* Default static buffer size */ #ifdef BIGBUFOK #define SSPACE 65000 /* Size of string-generating buffer */ #else #define SSPACE 2000 /* size of string-generating buffer */ #endif /* BIGBUFOK */ #endif /* aegis */ #endif /* pdp11 */ #endif /* BSD29 */ #endif /* PROVX1 */ static char sspace[SSPACE]; /* Buffer for generating filenames */ #else /* is DYNAMIC */ #ifdef CK_64BIT #define SSPACE 2000000000 /* Two billion bytes */ #else #ifdef BIGBUFOK #define SSPACE 10000000 /* Ten million */ #else #define SSPACE 10000 /* Ten thousand */ #endif /* BIGBUFOK */ #endif /* CK_64BIT */ char *sspace = (char *)0; #endif /* DYNAMIC */ static int ssplen = SSPACE; /* Length of string space buffer */ #ifdef DCLFDOPEN /* fdopen() needs declaring because it's not declared in */ _PROTOTYP( FILE * fdopen, (int, char *) ); #endif /* DCLFDOPEN */ #ifdef DCLPOPEN /* popen() needs declaring because it's not declared in */ _PROTOTYP( FILE * popen, (char *, char *) ); #endif /* DCLPOPEN */ extern int nopush; /* More internal function prototypes */ /* * The path structure is used to represent the name to match. * Each slash-separated segment of the name is kept in one * such structure, and they are linked together, to make * traversing the name easier. */ struct path { char npart[MAXNAMLEN+4]; /* name part of path segment */ struct path *fwd; /* forward ptr */ }; #ifndef NOPUSH _PROTOTYP( int shxpand, (char *, char *[], int ) ); #endif /* NOPUSH */ _PROTOTYP( static int fgen, (char *, char *[], int ) ); _PROTOTYP( static VOID traverse, (struct path *, char *, char *) ); _PROTOTYP( static VOID addresult, (char *, int) ); #ifdef COMMENT /* Replaced by ckmatch() */ _PROTOTYP( static int match, (char *, char *) ); #endif /* COMMENT */ _PROTOTYP( char * whoami, (void) ); _PROTOTYP( UID_T real_uid, (void) ); _PROTOTYP( static struct path *splitpath, (char *p) ); _PROTOTYP( char * zdtstr, (time_t) ); _PROTOTYP( time_t zstrdt, (char *, int) ); /* Some systems define these symbols in include files, others don't... */ #ifndef R_OK #define R_OK 4 /* For access */ #endif /* R_OK */ #ifndef W_OK #define W_OK 2 #endif /* W_OK */ #ifndef X_OK #define X_OK 1 #endif /* X_OK */ #ifndef O_RDONLY #define O_RDONLY 000 #endif /* O_RDONLY */ /* syslog and wtmp items for Internet Kermit Service */ extern char * clienthost; /* From ckcmai.c. */ static char fullname[CKMAXPATH+1]; static char tmp2[CKMAXPATH+1]; extern int ckxlogging; #ifdef CKXPRINTF /* Our printf macro conflicts with */ #undef printf /* use of "printf" in syslog.h */ #endif /* CKXPRINTF */ #ifdef CKSYSLOG #ifdef RTAIX #include #else /* RTAIX */ #include #endif /* RTAIX */ #endif /* CKSYSLOG */ #ifdef CKXPRINTF #define printf ckxprintf #endif /* CKXPRINTF */ int ckxanon = 1; /* Anonymous login ok */ int ckxperms = 0040; /* Anonymous file permissions */ int ckxpriv = 1; /* Priv'd login ok */ #ifndef XFERFILE #define XFERFILE "/var/log/iksd.log" #endif /* XFERFILE */ /* wtmp logging for IKSD... */ #ifndef CKWTMP /* wtmp logging not selected */ int ckxwtmp = 0; /* Know this at runtime */ #else /* wtmp file details */ int ckxwtmp = 1; #ifdef UTMPBUG /* Unfortunately... */ /* Some versions of Linux have a file that contains "enum utlogin { local, telnet, rlogin, screen, ... };" This clobbers any program that uses any of these words as variable names, function names, macro names, etc. (Other versions of Linux have this declaration within #if 0 ... #endif.) There is nothing we can do about this other than to not include the stupid file. But we need stuff from it, so... */ #include #include #define UT_LINESIZE 32 #define UT_NAMESIZE 32 #define UT_HOSTSIZE 256 struct timeval { time_t tv_sec; time_t tv_usec; }; struct exit_status { short int e_termination; /* Process termination status. */ short int e_exit; /* Process exit status. */ }; struct utmp { short int ut_type; /* Type of login */ pid_t ut_pid; /* Pid of login process */ char ut_line[UT_LINESIZE]; /* NUL-terminated devicename of tty */ char ut_id[4]; /* Inittab id */ char ut_user[UT_NAMESIZE]; /* Username (not NUL terminated) */ char ut_host[UT_HOSTSIZE]; /* Hostname for remote login */ struct exit_status ut_exit; /* Exit status */ long ut_session; /* Session ID, used for windowing */ struct timeval ut_tv; /* Time entry was made */ int32_t ut_addr_v6[4]; /* Internet address of remote host */ char pad[20]; /* Reserved */ }; #define ut_time ut_tv.tv_sec /* Why should Linux be like anything else? */ #define ut_name ut_user /* ... */ extern void logwtmp __P ((__const char *__ut_line, __const char *__ut_name, __const char *__ut_host)); #else /* Not UTMPBUG */ #ifndef HAVEUTMPX /* Who has */ #ifdef SOLARIS #define HAVEUTMPX #else #ifdef IRIX60 #define HAVEUTMPX #else #ifdef CK_SCOV5 #define HAVEUTMPX #else #ifdef HPUX100 #define HAVEUTMPX #else #ifdef UNIXWARE #define HAVEUTMPX #endif /* UNIXWARE */ #endif /* HPUX100 */ #endif /* CK_SCOV5 */ #endif /* IRIX60 */ #endif /* SOLARIS */ #endif /* HAVEUTMPX */ #ifdef HAVEUTMPX #include #else #ifdef OSF50 /* Because the time_t in the utmp struct is 64 bits but time() wants 32 */ #define __V40_OBJ_COMPAT 1 #endif /* OSF50 */ #include #ifdef OSF50 #undef __V40_OBJ_COMPAT #endif /* OSF50 */ #endif /* HAVEUTMPX */ #endif /* UTMPBUG */ #ifdef HAVEUTMPX #define UTMPSTRUCT utmpx #else #define UTMPSTRUCT utmp #endif /* HAVEUTMPX */ #ifndef WTMPFILE #ifdef QNX #define WTMPFILE "/usr/adm/wtmp.1" #else #ifdef LINUX #define WTMPFILE "/var/log/wtmp" #else #define WTMPFILE "/usr/adm/wtmp" #endif /* QNX */ #endif /* LINUX */ #endif /* WTMPFILE */ char * wtmpfile = NULL; static int wtmpfd = 0; static char cksysline[32] = { NUL, NUL }; #ifndef HAVEUTHOST /* Does utmp include ut_host[]? */ #ifdef HAVEUTMPX /* utmpx always does */ #define HAVEUTHOST #else #ifdef LINUX /* Linux does */ #define HAVEUTHOST #else #ifdef SUNOS4 /* SunOS does */ #define HAVEUTHOST #else #ifdef AIX41 /* AIX 4.1 and later do */ #define HAVEUTHOST #endif /* AIX41 */ #endif /* SUNOS4 */ #endif /* LINUX */ #endif /* HAVEUTMPX */ #endif /* HAVEUTHOST */ #ifdef UW200 PID_T _vfork() { /* To satisfy a library foulup */ return(fork()); /* in Unixware 2.0.x */ } #endif /* UW200 */ VOID #ifdef CK_ANSIC logwtmp(const char * line, const char * name, const char * host) #else logwtmp(line, name, host) char *line, *name, *host; #endif /* CK_ANSIC */ /* logwtmp */ { struct UTMPSTRUCT ut; struct stat buf; /* time_t time(); */ if (!ckxwtmp) return; if (!wtmpfile) makestr(&wtmpfile,WTMPFILE); if (!line) line = ""; if (!name) name = ""; if (!host) host = ""; if (!wtmpfd && (wtmpfd = open(wtmpfile, O_WRONLY|O_APPEND, 0)) < 0) { ckxwtmp = 0; debug(F110,"WTMP open failed",line,0); return; } if (!fstat(wtmpfd, &buf)) { ckstrncpy(ut.ut_line, line, sizeof(ut.ut_line)); #ifdef FREEBSD9 ckstrncpy(ut.ut_user, name, sizeof(ut.ut_user)); #else ckstrncpy(ut.ut_name, name, sizeof(ut.ut_name)); #endif /* FREEBSD9 */ #ifdef HAVEUTHOST /* Not portable */ ckstrncpy(ut.ut_host, host, sizeof(ut.ut_host)); #endif /* HAVEUTHOST */ #ifdef HAVEUTMPX time(&ut.ut_tv.tv_sec); #else #ifdef LINUX /* In light of the following comment perhaps the previous line should */ /* be "#ifndef COMMENT". */ { /* * On 64-bit platforms sizeof(time_t) and sizeof(ut.ut_time) * are not the same and attempt to use an address of * ut.ut_time as an argument to time() call may cause * "unaligned access" trap. */ time_t zz; time(&zz); ut.ut_time = zz; } #else #ifdef CK_64BIT { /* Now (Jan 2006) we can do this for any 64-bit build */ time_t zz; time(&zz); ut.ut_time = zz; } #else time(&ut.ut_time); #endif /* CK_64BIT */ #endif /* LINUX */ #endif /* HAVEUTMPX */ if (write(wtmpfd, (char *)&ut, sizeof(struct UTMPSTRUCT)) != sizeof(struct UTMPSTRUCT)) { #ifndef NOFTRUNCATE #ifndef COHERENT ftruncate(wtmpfd, buf.st_size); /* Error, undo any partial write */ #else chsize(wtmpfd, buf.st_size); /* Error, undo any partial write */ #endif /* COHERENT */ #endif /* NOFTRUNCATE */ debug(F110,"WTMP write error",line,0); } else { debug(F110,"WTMP record OK",line,0); return; } } } #endif /* CKWTMP */ #ifdef CKSYSLOG /* C K S Y S L O G -- C-Kermit system logging function, For use by other modules. This module can, but doesn't have to, use it. Call with: n = SYSLG_xx values defined in ckcdeb.h s1, s2, s3: strings. */ VOID cksyslog(n, m, s1, s2, s3) int n, m; char * s1, * s2, * s3; { int level; if (!ckxlogging) /* syslogging */ return; if (!s1) s1 = ""; /* Fix null args */ if (!s2) s2 = ""; if (!s3) s3 = ""; switch (n) { /* Translate Kermit level */ case SYSLG_DB: /* to syslog level */ level = LOG_DEBUG; break; default: level = m ? LOG_INFO : LOG_ERR; } debug(F110,"cksyslog s1",s1,0); debug(F110,"cksyslog s2",s2,0); debug(F110,"cksyslog s3",s3,0); errno = 0; syslog(level, "%s: %s %s", s1, s2, s3); /* Write syslog record */ debug(F101,"cksyslog errno","",errno); } #endif /* CKSYSLOG */ /* Declarations */ int maxnam = MAXNAMLEN; /* Available to the outside */ int maxpath = MAXPATH; int ck_znewn = -1; #ifdef UNIX char startupdir[MAXPATH+1]; #endif /* UNIX */ int pexitstat = -2; /* Process exit status */ FILE *fp[ZNFILS] = { /* File pointers */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; /* Flags for each file indicating whether it was opened with popen() */ int ispipe[ZNFILS] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; /* Buffers and pointers used in buffered file input and output. */ #ifdef DYNAMIC extern char *zinbuffer, *zoutbuffer; #else extern char zinbuffer[], zoutbuffer[]; #endif /* DYNAMIC */ extern char *zinptr, *zoutptr; extern int zincnt, zoutcnt; extern int wildxpand, wildena; /* Wildcard handling */ static CK_OFF_T iflen = (CK_OFF_T)-1; /* Input file length */ static PID_T pid = 0; /* pid of child fork */ static int fcount = 0; /* Number of files in wild group */ static int nxpand = 0; /* Copy of fcount */ static char nambuf[CKMAXPATH+4]; /* Buffer for a pathname */ #ifndef NOFRILLS #define ZMBUFLEN 200 static char zmbuf[ZMBUFLEN]; /* For mail, remote print strings */ #endif /* NOFRILLS */ char **mtchs = NULL; /* Matches found for filename */ char **mtchptr = NULL; /* Pointer to current match */ /* Z K S E L F -- Kill Self: log out own job, if possible. */ /* Note, should get current pid, but if your system doesn't have */ /* getppid(), then just kill(0,9)... */ #ifndef SVR3 #ifndef POSIX #ifndef OSFPC /* Already declared in unistd.h for SVR3 and POSIX */ #ifdef CK_ANSIC extern PID_T getppid(void); #else #ifndef PS2AIX10 #ifndef COHERENT extern PID_T getppid(); #endif /* COHERENT */ #endif /* PS2AIX10 */ #endif /* CK_ANSIC */ #endif /* OSFPC */ #endif /* POSIX */ #endif /* SVR3 */ int zkself() { /* For "bye", but no guarantee! */ #ifdef PROVX1 return(kill(0,9)); #else #ifdef V7 return(kill(0,9)); #else #ifdef TOWER1 return(kill(0,9)); #else #ifdef FT18 return(kill(0,9)); #else #ifdef aegis return(kill(0,9)); #else #ifdef COHERENT return(kill((PID_T)getpid(),1)); #else #ifdef PID_T exit(kill((PID_T)getppid(),1)); return(0); #else exit(kill(getppid(),1)); return(0); #endif #endif #endif #endif #endif #endif #endif } static VOID getfullname(name) char * name; { char *p = (char *)fullname; int len = 0; fullname[0] = '\0'; /* If necessary we could also chase down symlinks here... */ #ifdef COMMENT /* This works but is incompatible with wuftpd */ if (isguest && anonroot) { ckstrncpy(fullname,anonroot,CKMAXPATH); len = strlen(fullname); if (len > 0) if (fullname[len-1] == '/') len--; } p += len; #endif /* COMMENT */ zfnqfp(name, CKMAXPATH - len, p); while (*p) { if (*p < '!') *p = '_'; p++; } } /* D O I K L O G -- Open Kermit-specific ftp-like transfer log. */ VOID /* Called in ckcmai.c */ doiklog() { if (iklogopen) /* Already open? */ return; if (xferlog) { /* Open iksd log if requested */ if (!xferfile) /* If no pathname given */ makestr(&xferfile,XFERFILE); /* use this default */ if (*xferfile) { xferlog = open(xferfile, O_WRONLY | O_APPEND | O_CREAT, 0660); debug(F101,"doiklog open","",xferlog); if (xferlog < 0) { #ifdef CKSYSLOG syslog(LOG_ERR, "xferlog open failure %s: %m", xferfile); #endif /* CKSYSLOG */ debug(F101,"doiklog open errno","",errno); xferlog = 0; } else iklogopen = 1; } else xferlog = 0; #ifdef CKSYSLOG if (xferlog && ckxlogging) syslog(LOG_INFO, "xferlog: %s open ok", xferfile); #endif /* CKSYSLOG */ } } /* Z O P E N I -- Open an existing file for input. */ /* Returns 1 on success, 0 on failure */ int zopeni(n,name) int n; char *name; { int x; debug(F111,"zopeni",name,n); if ((x = chkfn(n)) != 0) { debug(F111,"zopeni chkfn",ckitoa(n),x); return(0); } zincnt = 0; /* Reset input buffer */ if (n == ZSYSFN) { /* Input from a system function? */ #ifdef COMMENT /*** Note, this function should not be called with ZSYSFN ***/ /*** Always call zxcmd() directly, and give it the real file number ***/ /*** you want to use. ***/ return(zxcmd(n,name)); /* Try to fork the command */ #else debug(F110,"zopeni called with ZSYSFN, failing!",name,0); *nambuf = '\0'; /* No filename. */ return(0); /* fail. */ #endif /* COMMENT */ } if (n == ZSTDIO) { /* Standard input? */ if (is_a_tty(0)) { fprintf(stderr,"Terminal input not allowed"); debug(F110,"zopeni: attempts input from unredirected stdin","",0); return(0); } fp[ZIFILE] = stdin; ispipe[ZIFILE] = 0; return(1); } #ifdef CKROOT debug(F111,"zopeni setroot",ckroot,ckrootset); if (ckrootset) if (!zinroot(name)) { debug(F110,"zopeni setroot violation",name,0); return(0); } #endif /* CKROOT */ fp[n] = fopen(name,"r"); /* Real file, open it. */ /* debug(F111,"zopeni fopen", name, fp[n]); */ #ifdef ZDEBUG /* printf("ZOPENI fp[%d]=%ld\n",n,fp[n]); */ #endif /* ZDEBUG */ ispipe[n] = 0; if (xferlog #ifdef CKSYSLOG || ((ckxsyslog >= SYSLG_FA) && ckxlogging) #endif /* CKSYSLOG */ ) { getfullname(name); debug(F110,"zopeni fullname",fullname,0); } if (fp[n] == NULL) { #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_FA && ckxlogging) { syslog(LOG_INFO, "file[%d] %s: open failed (%m)", n, fullname); perror(fullname); } else #endif /* CKSYSLOG */ perror(name); return(0); } else { #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_FA && ckxlogging) syslog(LOG_INFO, "file[%d] %s: open read ok", n, fullname); #endif /* CKSYSLOG */ clearerr(fp[n]); return(1); } } #ifdef QNX #define DONDELAY #else #ifdef O_NDELAY #define DONDELAY #endif /* O_NDELAY */ #endif /* QNX */ /* Z O P E N O -- Open a new file for output. */ /*ARGSUSED*/ /* zz not used */ int zopeno(n,name,zz,fcb) /* zopeno */ int n; char *name; struct zattr *zz; struct filinfo *fcb; { char p[8]; int append = 0; int istty = 0, filefd = 0; /* As of Version 5A, the attribute structure and the file information */ /* structure are included in the arglist. */ #ifdef DEBUG debug(F111,"zopeno",name,n); if (fcb) { debug(F101,"zopeno fcb disp","",fcb->dsp); debug(F101,"zopeno fcb type","",fcb->typ); debug(F101,"zopeno fcb char","",fcb->cs); } else { debug(F100,"zopeno fcb is NULL","",0); } #endif /* DEBUG */ if (chkfn(n) != 0) /* Already open? */ return(0); /* Nothing to do. */ if ((n == ZCTERM) || (n == ZSTDIO)) { /* Terminal or standard output */ fp[ZOFILE] = stdout; ispipe[ZOFILE] = 0; #ifdef COMMENT /* This seems right but it breaks client server ops */ fp[n] = stdout; ispipe[n] = 0; #endif /* COMMENT */ #ifdef COMMENT #ifdef DEBUG if (n != ZDFILE) debug(F101,"zopeno fp[n]=stdout","",fp[n]); #endif /* DEBUG */ #endif /* COMMENT */ zoutcnt = 0; zoutptr = zoutbuffer; return(1); } /* A real file. Open it in desired mode (create or append). */ #ifdef CKROOT debug(F111,"zopeno setroot",ckroot,ckrootset); if (ckrootset) if (!zinroot(name)) { debug(F110,"zopeno setroot violation",name,0); return(0); } #endif /* CKROOT */ ckstrncpy(p,"w",8); /* Assume write/create mode */ if (fcb) { /* If called with an FCB... */ if (fcb->dsp == XYFZ_A) { /* Does it say Append? */ ckstrncpy(p,"a",8); /* Yes. */ debug(F100,"zopeno append","",0); append = 1; } } if (xferlog #ifdef CKSYSLOG || ((ckxsyslog >= SYSLG_FC) && ckxlogging) #endif /* CKSYSLOG */ ) { getfullname(name); debug(F110,"zopeno fullname",fullname,0); } { /* Allow tty devices to opened as output files 2009/10/20 */ int fd, mode = 0; debug(F110,"zopeno attempting to open",name,0); #ifdef O_NONBLOCK mode = O_NONBLOCK; #else #ifdef O_NDELAY mode = O_NDELAY; #else #ifdef FNDELAY mode = FNDELAY; #endif /* FNDELAY */ #endif /* O_NDELAY */ #endif /* O_NONBLOCK */ debug(F111,"zopeno open mode",name,mode); fd = open(name,O_WRONLY,mode); debug(F111,"zopeno open",name,fd); if (fd > -1) { if (isatty(fd)) { filefd = fd; istty++; } } } debug(F111,"zopeno istty",name,istty); debug(F110,"zopeno fopen arg",p,0); if (istty) fp[n] = fdopen(filefd,p); else fp[n] = fopen(name,p); /* Try to open the file */ ispipe[ZIFILE] = 0; #ifdef ZDEBUG printf("ZOPENO fp[%d]=%ld\n",n,fp[n]); #endif /* ZDEBUG */ if (fp[n] == NULL) { /* Failed */ debug(F101,"zopeno failed errno","",errno); if (istty) close(filefd); #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_FC && ckxlogging) syslog(LOG_INFO, "file[%d] %s: %s failed (%m)", n, fullname, append ? "append" : "create" ); #endif /* CKSYSLOG */ #ifdef COMMENT /* Let upper levels print message. */ perror("Can't open output file"); #endif /* COMMENT */ } else { /* Succeeded */ extern int zofbuffer, zofblock, zobufsize; debug(F101, "zopeno zobufsize", "", zobufsize); if (n == ZDFILE || n == ZTFILE) { /* If debug or transaction log */ setbuf(fp[n],NULL); /* make it unbuffered. */ #ifdef DONDELAY } else if (n == ZOFILE && !zofblock) { /* blocking or nonblocking */ int flags; if ((flags = fcntl(fileno(fp[n]),F_GETFL,0)) > -1) fcntl(fileno(fp[n]),F_SETFL, flags | #ifdef QNX O_NONBLOCK #else O_NDELAY #endif /* QNX */ ); debug(F100,"zopeno ZOFILE nonblocking","",0); #endif /* DONDELAY */ } else if (n == ZOFILE && !zofbuffer) { /* buffered or unbuffered */ setbuf(fp[n],NULL); debug(F100,"zopeno ZOFILE unbuffered","",0); } #ifdef CK_LOGIN /* Enforce anonymous file-creation permission */ if (isguest) if (n == ZWFILE || n == ZMFILE || n == ZOFILE || n == ZDFILE || n == ZTFILE || n == ZPFILE || n == ZSFILE) chmod(name,ckxperms); #endif /* CK_LOGIN */ #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_FC && ckxlogging) syslog(LOG_INFO, "file[%d] %s: %s ok", n, fullname, append ? "append" : "create" ); #endif /* CKSYSLOG */ debug(F100, "zopeno ok", "", 0); } zoutcnt = 0; /* (PWP) reset output buffer */ zoutptr = zoutbuffer; return((fp[n] != NULL) ? 1 : 0); } /* Z C L O S E -- Close the given file. */ /* Returns 0 if arg out of range, 1 if successful, -1 if close failed. */ int zclose(n) int n; { int x = 0, x2 = 0; extern CK_OFF_T ffc; debug(F101,"zclose file number","",n); if (chkfn(n) < 1) return(0); /* Check range of n */ if ((n == ZOFILE) && (zoutcnt > 0)) /* (PWP) output leftovers */ x2 = zoutdump(); if (fp[ZSYSFN] || ispipe[n]) { /* If file is really pipe */ #ifndef NOPUSH x = zclosf(n); /* do it specially */ #else x = EOF; #endif /* NOPUSH */ debug(F101,"zclose zclosf","",x); /* debug(F101,"zclose zclosf fp[n]","",fp[n]); */ } else { if ((fp[n] != stdout) && (fp[n] != stdin)) x = fclose(fp[n]); fp[n] = NULL; #ifdef COMMENT if (n == ZCTERM || n == ZSTDIO) /* See zopeno() */ if (fp[ZOFILE] == stdout) fp[ZOFILE] = NULL; #endif /* COMMENT */ } iflen = -1L; /* Invalidate file length */ if (x == EOF) { /* if we got a close error */ debug(F101,"zclose fclose fails","",x); return(-1); } else if (x2 < 0) { /* or error flushing last buffer */ debug(F101,"zclose error flushing last buffer","",x2); return(-1); /* then return an error */ } else { /* Print log record compatible with wu-ftpd */ if (xferlog && (n == ZIFILE || n == ZOFILE)) { char * s, *p; extern char ttname[]; if (!iklogopen) (VOID) doiklog(); /* Open log if necessary */ debug(F101,"zclose iklogopen","",iklogopen); if (iklogopen) { int len; char * fnam; timenow = time(NULL); #ifdef CK_LOGIN if (logged_in) s = clienthost; else #endif /* CK_LOGIN */ s = (char *)ttname; if (!s) s = ""; if (!*s) s = "*"; #ifdef CK_LOGIN if (logged_in) { p = guestpass; if (!*p) p = "*"; } else #endif /* CK_LOGIN */ p = whoami(); len = 24 + 12 + (int)strlen(s) + 16 + (int)strlen(fullname) + 1 + 1 + 1 + 1 + (int)strlen(p) + 6 + 2 + 12; fnam = fullname; if (!*fnam) fnam = "(pipe)"; if (len > IKSDMSGLEN) sprintf(iksdmsg, /* SAFE */ "%.24s [BUFFER WOULD OVERFLOW]\n",ctime(&timenow)); else sprintf(iksdmsg, /* SAFE */ "%.24s %d %s %s %s %c %s %c %c %s %s %d %s\n", ctime(&timenow), /* date/time */ gtimer(), /* elapsed secs */ s, /* peer name */ ckfstoa(ffc), /* byte count */ fnam, /* full pathname of file */ (binary ? 'b' : 'a'), /* binary or ascii */ "_", /* options = none */ n == ZIFILE ? 'o' : 'i', /* in/out */ #ifdef CK_LOGIN (isguest ? 'a' : 'r'), /* User type */ #else 'r', #endif /* CK_LOGIN */ p, /* Username or guest passwd */ #ifdef CK_LOGIN logged_in ? "iks" : "kermit", /* Record ID */ #else "kermit", #endif /* CK_LOGIN */ 0, /* User ID on client system unknown */ "*" /* Ditto */ ); debug(F110,"zclose iksdmsg",iksdmsg,0); write(xferlog, iksdmsg, (int)strlen(iksdmsg)); } } debug(F101,"zclose returns","",1); return(1); } } /* Z C H I N -- Get a character from the input file. */ /* Returns -1 if EOF, 0 otherwise with character returned in argument */ int zchin(n,c) int n; int *c; { int a; #ifdef IKSD if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) { a = coninc(0); if (*c < 0) return(-1); } else #endif /* IKSD */ /* (PWP) Just in case this gets called when it shouldn't. */ if (n == ZIFILE) { a = zminchar(); /* Note: this catches Ctrl-Z */ if (a < 0) /* (See zinfill()...) */ return(-1); } else { a = getc(fp[n]); if (a == EOF) return(-1); #ifdef CK_CTRLZ /* If SET FILE EOF CTRL-Z, first Ctrl-Z marks EOF */ if (!binary && a == 0x1A && eofmethod == XYEOF_Z) return(-1); #endif /* CK_CTRLZ */ } *c = (CHAR) a & 0377; return(0); } /* Z S I N L -- Read a line from a file */ /* Writes the line into the address provided by the caller. n is the Kermit "channel number". Writing terminates when newline is encountered, newline is not copied. Writing also terminates upon EOF or if length x is exhausted. Returns 0 on success, -1 on EOF or error. */ int zsinl(n,s,x) int n, x; char *s; { int a, z = 0; /* z is return code. */ int count = 0; int len = 0; char *buf; extern CHAR feol; /* Line terminator */ if (!s || chkfn(n) < 1) /* Make sure file is open, etc */ return(-1); buf = s; s[0] = '\0'; /* Don't return junk */ a = -1; /* Current character, none yet. */ while (x--) { /* Up to given length */ int old = 0; if (feol) /* Previous character */ old = a; if (zchin(n,&a) < 0) { /* Read a character from the file */ debug(F101,"zsinl zchin fail","",count); if (count == 0) z = -1; /* EOF or other error */ break; } else count++; if (feol) { /* Single-character line terminator */ if (a == feol) break; } else { /* CRLF line terminator */ if (a == '\015') /* CR, get next character */ continue; if (old == '\015') { /* Previous character was CR */ if (a == '\012') { /* This one is LF, so we have a line */ break; } else { /* Not LF, deposit CR */ *s++ = '\015'; x--; len++; } } } *s = a; /* Deposit character */ s++; len++; } *s = '\0'; /* Terminate the string */ debug(F011,"zsinl",buf,len); return(z); } /* Z X I N -- Read x bytes from a file */ /* Reads x bytes (or less) from channel n and writes them to the address provided by the caller. Returns number of bytes read on success, 0 on EOF or error. */ int zxin(n,s,x) int n, x; char *s; { #ifdef IKSD if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) { int a, i; a = ttchk(); if (a < 1) return(0); for (i = 0; i < a && i < x; i++) s[i] = coninc(0); return(i); } #endif /* IKSD */ return(fread(s, sizeof (char), x, fp[n])); } /* Z I N F I L L -- Buffered file input. (re)fill the file input buffer with data. All file input should go through this routine, usually by calling the zminchar() macro defined in ckcker.h. Returns: Value 0..255 on success, the character that was read. -1 on end of file. -2 on any kind of error other than end of file. -3 timeout when reading from pipe (Kermit packet mode only). */ int zinfill() { extern int kactive, srvping; errno = 0; #ifdef ZDEBUG printf("ZINFILL fp[%d]=%ld\n",ZIFILE,fp[ZIFILE]); #endif /* ZDEBUG */ #ifdef IKSD if (inserver && !local && fp[ZIFILE] == stdin) { int a, i; a = ttchk(); if (a < 0) return(-2); for (i = 0; i < a && i < INBUFSIZE; i++) { zinbuffer[i] = coninc(0); } zincnt = i; /* set pointer to beginning, (== &zinbuffer[0]) */ zinptr = zinbuffer; if (zincnt == 0) return(-1); zincnt--; /* One less char in buffer */ return((int)(*zinptr++) & 0377); /* because we return the first */ } #endif /* IKSD */ debug(F101,"zinfill kactive","",kactive); if (!(kactive && ispipe[ZIFILE])) { if (feof(fp[ZIFILE])) { debug(F100,"ZINFILL feof","",0); #ifdef ZDEBUG printf("ZINFILL EOF\n"); #endif /* ZDEBUG */ return(-1); } } clearerr(fp[ZIFILE]); #ifdef SELECT /* Here we can call select() to get a timeout... */ if (kactive && ispipe[ZIFILE]) { int secs, z = 0; #ifndef NOXFER if (srvping) { secs = 1; debug(F101,"zinfill calling ttwait","",secs); z = ttwait(fileno(fp[ZIFILE]),secs); debug(F101,"zinfill ttwait","",z); } #endif /* NOXFER */ if (z == 0) return(-3); } #endif /* SELECT */ #ifdef DEBUG if (deblog) { int i; debug(F101,"ZINFILL INBUFSIZE","",INBUFSIZE); #ifdef USE_MEMCPY memset(zinbuffer, 0xFF, INBUFSIZE); #else for (i = 0; i < INBUFSIZE; i++) { zinbuffer[i] = 0xFF; #ifdef COMMENT /* Too much! */ debug(F101,"ZINFILL zinbuffer[i]","",i); #endif /* COMMENT */ } #endif /* USE_MEMCPY */ ckstrncpy(zinbuffer,"zinbuffer is a valid buffer",INBUFSIZE); /* debug(F111,"ZINFILL about to call fread",zinbuffer,zinbuffer); */ } #endif /* DEBUG */ /* Note: The following read MUST be nonblocking when reading from a pipe and we want timeouts to work. See zxcmd(). */ zincnt = fread(zinbuffer, sizeof (char), INBUFSIZE, fp[ZIFILE]); debug(F101,"ZINFILL fread","",zincnt); /* Just the size */ #ifdef ZDEBUG printf("FREAD=%d\n",zincnt); #endif /* ZDEBUG */ #ifdef CK_CTRLZ /* If SET FILE EOF CTRL-Z, first Ctrl-Z marks EOF */ if (zincnt > 0 && !binary && eofmethod == XYEOF_Z) { register int i; for (i = 0; i < zincnt; i++) { if (zinbuffer[i] == SUB) { zincnt = i; /* Stop at first Ctrl-Z */ if (i == 0) return(-1); break; } } } #endif /* CK_CTRLZ */ if (zincnt == 0) { /* Got nothing? */ if (ferror(fp[ZIFILE])) { debug(F100,"ZINFILL ferror","",0); debug(F101,"ZINFILL errno","",errno); #ifdef ZDEBUG printf("ZINFILL errno=%d\n",errno); #endif /* ZDEBUG */ #ifdef EWOULDBLOCK return((errno == EWOULDBLOCK) ? -3 : -2); #else return(-2); #endif /* EWOULDBLOCK */ } /* In case feof() didn't work just above -- sometimes it doesn't... */ if (feof(fp[ZIFILE]) ) { debug(F100,"ZINFILL count 0 EOF return -1","",0); return (-1); } else { debug(F100,"ZINFILL count 0 not EOF return -2","",0); return(-2); } } zinptr = zinbuffer; /* set pointer to beginning, (== &zinbuffer[0]) */ zincnt--; /* One less char in buffer */ return((int)(*zinptr++) & 0377); /* because we return the first */ } /* Z S O U T -- Write a string out to the given file, buffered. */ /* Returns 0 on success, -1 on failure */ int zsout(n,s) int n; char *s; { int rc = 0; rc = chkfn(n); if (rc < 1) return(-1); /* Keep this, prevents memory faults */ if (!s) return(0); /* Null pointer, do nothing, succeed */ if (!*s) return(0); /* empty string, ditto */ #ifdef IKSD /* This happens with client-side Kermit server when a REMOTE command was sent from the server to the client and the server is supposed to display the text, but of course there is no place to display it since it is in remote mode executing Kermit protocol. */ if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) { #ifdef COMMENT return(ttol(s,((int)strlen(s)) < 0) ? -1 : 0); #else return(0); #endif /* COMMENT */ } #endif /* IKSD */ if (n == ZSFILE) { int k; k = strlen(s); rc = write(fileno(fp[n]),s,k); return((rc == k) ? 0 : -1); } rc = fputs(s,fp[n]) == EOF ? -1 : 0; if (n == ZWFILE) fflush(fp[n]); return(rc); } /* Z S O U T L -- Write string to file, with line terminator, buffered */ /* Returns 0 on success, -1 on failure */ int zsoutl(n,s) int n; char *s; { if (zsout(n,s) < 0) return(-1); #ifdef IKSD if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) { #ifdef COMMENT return(ttoc(LF)); #else return(0); /* See comments in zsout() */ #endif /* COMMENT */ } #endif /* IKSD */ if (n == ZSFILE) /* Session log is unbuffered */ return(write(fileno(fp[n]),"\n",1) == 1 ? 0 : -1); else if (fputs("\n",fp[n]) == EOF) return(-1); if (n == ZDIFIL || n == ZWFILE) /* Flush connection log records */ fflush(fp[n]); return(0); } /* Z S O U T X -- Write x characters to file, unbuffered. */ /* Returns number of characters written on success, -1 on failure */ int zsoutx(n,s,x) int n, x; char *s; { int k; if (!s) return(0); if (!*s) return(0); #ifdef IKSD if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) { #ifdef COMMENT return(ttol(s,x)); /* See comments in zsout() */ #else return(x); #endif /* COMMENT */ } #endif /* IKSD */ if ((k = (int)strlen(s)) > x) x = k; /* Nothing else would make sense */ #ifdef COMMENT if (chkfn(n) < 1) return(-1); return(write(fp[n]->_file,s,x)); #endif /* COMMENT */ return(write(fileno(fp[n]),s,x) == x ? x : -1); } /* Z C H O U T -- Add a character to the given file. */ /* Should return 0 or greater on success, -1 on failure (e.g. disk full) */ int #ifdef CK_ANSIC zchout(register int n, char c) #else zchout(n,c) register int n; char c; #endif /* CK_ANSIC */ /* zchout() */ { /* if (chkfn(n) < 1) return(-1); */ #ifdef IKSD if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) { #ifdef COMMENT return(ttoc(c)); #else return(0); /* See comments in zsout() */ #endif /* COMMENT */ } #endif /* IKSD */ if (n == ZSFILE) /* Use unbuffered for session log */ return(write(fileno(fp[n]),&c,1) == 1 ? 0 : -1); /* Buffered for everything else */ if (putc(c,fp[n]) == EOF) /* If true, maybe there was an error */ return(ferror(fp[n])?-1:0); /* Check to make sure */ else /* Otherwise... */ return(0); /* There was no error. */ } /* (PWP) buffered character output routine to speed up file IO */ int zoutdump() { int x; char * zp; zoutptr = zoutbuffer; /* Reset buffer pointer in all cases */ #ifdef DEBUG if (deblog) debug(F101,"zoutdump zoutcnt","",zoutcnt); #endif /* DEBUG */ if (zoutcnt == 0) { /* Nothing to output */ return(0); } else if (zoutcnt < 0) { /* Unexpected negative argument */ zoutcnt = 0; /* Reset output buffer count */ return(-1); /* and fail. */ } #ifdef IKSD if (inserver && !local && fp[ZOFILE] == stdout) { #ifdef COMMENT x = ttol(zoutbuffer,zoutcnt); #else x = 1; /* See comments in zsout() */ #endif /* COMMENT */ zoutcnt = 0; return(x > 0 ? 0 : -1); } #endif /* IKSD */ /* Frank Prindle suggested that replacing this fwrite() by an fflush() followed by a write() would improve the efficiency, especially when writing to stdout. Subsequent tests showed a 5-fold improvement. */ #ifdef COMMENT if (x = fwrite(zoutbuffer, 1, zoutcnt, fp[ZOFILE])) ... #endif /* COMMENT */ #ifndef CK_NONBLOCK fflush(fp[ZOFILE]); #endif /* CK_NONBLOCK */ zp = zoutbuffer; while (zoutcnt > 0) { if ((x = write(fileno(fp[ZOFILE]),zp,zoutcnt)) > -1) { #ifdef DEBUG if (deblog) /* Save a function call... */ debug(F101,"zoutdump wrote","",x); #endif /* DEBUG */ zoutcnt -= x; /* Adjust output buffer count */ zp += x; /* and pointer */ } else { #ifdef DEBUG if (deblog) { debug(F101,"zoutdump write error","",errno); debug(F101,"zoutdump write returns","",x); } #endif /* DEBUG */ zoutcnt = 0; /* Reset output buffer count */ return(-1); /* write() failed */ } } return(0); } /* C H K F N -- Internal function to verify file number is ok */ /* Returns: -1: File number n is out of range 0: n is in range, but file is not open 1: n in range and file is open */ int chkfn(n) int n; { /* if (n != ZDFILE) debug(F101,"chkfn","",n); */ if (n < 0 || n >= ZNFILS) { if (n != ZDFILE) debug(F101,"chkfn out of range","",n); return(-1); } else { /* if (n != ZDFILE) debug(F101,"chkfn fp[n]","",fp[n]); */ return((fp[n] == NULL) ? 0 : 1); } } /* Z G E T F S -- Return file size regardless of accessibility */ /* Used for directory listings, etc. Returns: The size of the file in bytes, 0 or greater, if the size can be learned. -1 if the file size can not be obtained. Also (and this is a hack just for UNIX): If the argument is the name of a symbolic link, the global variable issymlink is set to 1, and the global buffer linkname[] gets the link value. And it sets zgfs_dir to 1 if it's a directory, otherwise 0. This lets us avoid numerous redundant calls to stat(). */ int zgfs_link = 0; int zgfs_dir = 0; time_t zgfs_mtime = 0; unsigned int zgfs_mode = 0; #ifdef CKSYMLINK char linkname[CKMAXPATH+1]; #ifndef _IFLNK #define _IFLNK 0120000 #endif /* _IFLNK */ #endif /* CKSYMLINK */ CK_OFF_T zgetfs(name) char *name; { struct stat buf; char fnam[CKMAXPATH+4]; CK_OFF_T size = (CK_OFF_T)-1; int x; int needrlink = 0; char * s; if (!name) name = ""; if (!*name) return(-1); #ifdef UNIX x = strlen(name); if (x == 9 && !strcmp(name,"/dev/null")) return(0); #endif /* UNIX */ s = name; #ifdef DTILDE if (*s == '~') { s = tilde_expand(s); if (!s) s = ""; if (!*s) s = name; } #endif /* DTILDE */ x = ckstrncpy(fnam,s,CKMAXPATH); s = fnam; debug(F111,"zgetfs fnam",s,x); if (x > 0 && s[x-1] == '/') s[x-1] = '\0'; zgfs_dir = 0; /* Assume it's not a directory */ zgfs_link = 0; /* Assume it's not a symlink */ zgfs_mtime = 0; /* No time yet */ zgfs_mode = 0; /* No permission bits yet */ #ifdef CKSYMLINK /* We're doing symlinks? */ #ifdef USE_LSTAT /* OK to use lstat()? */ x = lstat(s,&buf); debug(F101,"STAT","",1); if (x < 0) /* stat() failed */ return(-1); if ( /* Now see if it's a symlink */ #ifdef S_ISLNK S_ISLNK(buf.st_mode) #else #ifdef _IFLNK ((_IFMT & buf.st_mode) == _IFLNK) #endif /* _IFLNK */ #endif /* S_ISLNK */ ) { zgfs_link = 1; /* It's a symlink */ linkname[0] = '\0'; /* Get the name */ x = readlink(s,linkname,CKMAXPATH); debug(F101,"zgetfs readlink",s,x); if (x > -1 && x < CKMAXPATH) { /* It's a link */ linkname[x] = '\0'; size = buf.st_size; /* Remember size of link */ x = stat(s,&buf); /* Now stat the linked-to file */ debug(F101,"STAT","",2); if (x < 0) /* so we can see if it's a directory */ return(-1); } else { ckstrncpy(linkname,"(lookup failed)",CKMAXPATH); } } #else /* !USE_LSTAT */ x = stat(s,&buf); /* No lstat(), use stat() instead */ debug(F101,"STAT","",3); if (x < 0) return(-1); #endif /* USE_LSTAT */ /* Do we need to call readlink()? */ #ifdef NOLINKBITS /* lstat() does not work in SCO operating systems. From "man NS lstat": lstat obtains information about the file named by path. In the case of a symbolic link, lstat returns information about the link, and not the file named by the link. It is only used by the NFS automount daemon and should not be utilized by users. */ needrlink = 1; debug(F101,"zgetfs forced needrlink","",needrlink); #else #ifdef S_ISLNK needrlink = S_ISLNK(buf.st_mode); debug(F101,"zgetfs S_ISLNK needrlink","",needrlink); #else #ifdef _IFLNK needrlink = (_IFMT & buf.st_mode) == _IFLNK; debug(F101,"zgetfs _IFLNK needrlink","",needrlink); #else needrlink = 1; debug(F101,"zgetfs default needrlink","",needrlink); #endif /* _IFLNK */ #endif /* S_ISLNK */ #endif /* NOLINKBITS */ if (needrlink) { linkname[0] = '\0'; errno = 0; x = readlink(s,linkname,CKMAXPATH); #ifdef DEBUG debug(F111,"zgetfs readlink",s,x); if (x < 0) debug(F101,"zgetfs readlink errno","",errno); else debug(F110,"zgetfs readlink result",linkname,0); #endif /* DEBUG */ if (x > -1 && x < CKMAXPATH) { zgfs_link = 1; linkname[x] = '\0'; } } #else /* !CKSYMLINK */ x = stat(s,&buf); /* Just stat the file */ debug(F111,"zgetfs stat",s,x); if (x < 0) /* and get the size */ return(-1); #endif /* CKSYMLINK */ zgfs_mtime = buf.st_mtime; zgfs_mode = buf.st_mode; zgfs_dir = (S_ISDIR(buf.st_mode)) ? 1 : 0; /* Set "is directory" flag */ debug(F111,"zgetfs size",s,size); debug(F111,"zgetfs st_size",s,buf.st_size); return((size < 0L) ? buf.st_size : size); /* Return the size */ } /* Z C H K I -- Check if input file exists and is readable */ /* Returns: >= 0 if the file can be read (returns the size). -1 if file doesn't exist or can't be accessed, -2 if file exists but is not readable (e.g. a directory file). -3 if file exists but protected against read access. For Berkeley Unix, a file must be of type "regular" to be readable. Directory files, special files, and symbolic links are not readable. */ CK_OFF_T zchki(name) char *name; { struct stat buf; char * s; int x, itsadir = 0; extern int zchkid, diractive, matchfifo; if (!name) return(-1); x = strlen(name); if (x < 1) return(-1); s = name; #ifdef UNIX if (x == 9 && !strcmp(s,"/dev/null")) return(0); if (x == 8 && !strcmp(s,"/dev/tty")) return(0); #endif /* UNIX */ #ifdef DTILDE if (*s == '~') { s = tilde_expand(s); if (!s) s = ""; if (!*s) s = name; } #endif /* DTILDE */ #ifdef CKROOT debug(F111,"zchki setroot",ckroot,ckrootset); if (ckrootset) if (!zinroot(name)) { debug(F110,"zchki setroot violation",name,0); return(-1); } #endif /* CKROOT */ x = stat(s,&buf); debug(F101,"STAT","",5); if (x < 0) { debug(F111,"zchki stat fails",s,errno); return(-1); } if (S_ISDIR (buf.st_mode)) itsadir = 1; if (!(itsadir && zchkid)) { /* Unless this... */ if (!S_ISREG (buf.st_mode) /* Must be regular file */ #ifdef S_ISFIFO && (!matchfifo || !S_ISFIFO (buf.st_mode)) /* or FIFO */ #endif /* S_ISFIFO */ ) { debug(F111,"zchki not regular file (or fifo)",s,matchfifo); return(-2); } } debug(F111,"zchki stat ok:",s,x); if (diractive) { /* If listing don't check access */ x = 1; } else { #ifdef SW_ACC_ID debug(F100,"zchki swapping ids for access()","",0); priv_on(); #endif /* SW_ACC_ID */ if ((x = access(s,R_OK)) < 0) x = access(s,X_OK); /* For RUN-class commands */ #ifdef SW_ACC_ID priv_off(); debug(F100,"zchki swapped ids restored","",0); #endif /* SW_ACC_ID */ } if (x < 0) { /* Is the file accessible? */ debug(F111,"zchki access failed:",s,x); /* No */ return(-3); } else { iflen = buf.st_size; /* Yes, remember size */ ckstrncpy(nambuf,s,CKMAXPATH); /* and name globally. */ debug(F111,"zchki access ok:",s,iflen); return((iflen > -1L) ? iflen : 0L); } } /* Z C H K O -- Check if output file can be created */ /* Returns -1 if write permission for the file would be denied, 0 otherwise. NOTE: The design is flawed. There is no distinction among: . Can I overwrite an existing file? . Can I create a file (or directory) in an existing directory? . Can I create a file (or directory) and its parent(s)? */ int zchko(name) char *name; { int i, x, itsadir = 0; char *s = NULL; char * oname; extern int zchkod; /* Used by IF WRITEABLE */ debug(F110,"zchko entry",name,0); if (!name) return(-1); /* Watch out for null pointer. */ oname = name; #ifdef CKROOT debug(F111,"zchko setroot",ckroot,ckrootset); if (ckrootset) if (!zinroot(name)) { debug(F110,"zchko setroot violation",name,0); errno = EACCES; return(-1); } #endif /* CKROOT */ x = (int)strlen(name); /* Get length of filename */ debug(F111,"zchko len",name,x); debug(F111,"zchko zchkod",name,zchkod); #ifdef UNIX /* Writing to null device is OK. */ if (x == 9 && !strcmp(name,"/dev/null")) return(0); if (x == 8 && !strcmp(name,"/dev/tty")) return(0); #endif /* UNIX */ s = name; #ifdef DTILDE if (*s == '~') { s = tilde_expand(s); if (!s) s = ""; if (!*s) s = name; x = strlen(s); } #endif /* DTILDE */ name = s; s = NULL; /* zchkod is a global flag meaning we're checking not to see if the directory file is writeable, but if it's OK to create files IN the directory. */ if (!zchkod && isdir(name)) { /* Directories are not writeable */ debug(F111,"zchko isdir",name,1); return(-1); } s = malloc(x+3); /* Must copy because we can't */ if (!s) { /* write into our argument. */ fprintf(stderr,"zchko: Malloc error 46\n"); return(-1); } ckstrncpy(s,name,x+3); #ifdef UNIX #ifdef NOUUCP { /* 2009/10/20 */ /* Allow tty devices to opened as output files */ int fd, istty = 0, mode = 0; debug(F110,"zchko attempting to open",name,0); /* Don't block on lack of Carrier or other modem signals */ #ifdef O_NONBLOCK mode = O_NONBLOCK; #else #ifdef O_NDELAY mode = O_NDELAY; #else #ifdef FNDELAY mode = FNDELAY; #endif /* FNDELAY */ #endif /* O_NDELAY */ #endif /* O_NONBLOCK */ debug(F111,"zchko open mode",name,mode); fd = open(name,O_WRONLY,mode); /* Must attempt to open it */ debug(F111,"zchko open",name,fd); if (fd > -1) { /* to get a file descriptor */ if (isatty(fd)) /* for isatty() */ istty++; debug(F111,"zchko isatty",name,istty); fd = close(fd); if (istty) { goto doaccess; } } else { debug(F101,"zchko open errno","",errno); x = -1; } } #endif /* NOUUCP */ #endif /* UNIX */ for (i = x; i > 0; i--) { /* Strip filename from right. */ if (ISDIRSEP(s[i-1])) { itsadir = 1; break; } } debug(F101,"zchko i","",i); debug(F101,"zchko itsadir","",itsadir); #ifdef COMMENT /* X/OPEN XPG3-compliant systems fail if argument ends with "/"... */ if (i == 0) /* If no path, use current directory */ strcpy(s,"./"); else /* Otherwise, use given one. */ s[i] = '\0'; #else #ifdef COMMENT /* The following does not work for "foo/bar" where the foo directory does not exist even though we could create it: access("foo/.") fails, but access("foo") works OK. */ /* So now we use "path/." if path given, or "." if no path given. */ s[i++] = '.'; /* Append "." to path. */ s[i] = '\0'; #else /* So NOW we strip path segments from the right as long as they don't */ /* exist -- we only call access() for path segments that *do* exist.. */ /* (But this isn't quite right either since now zchko(/foo/bar/baz/xxx) */ /* succeeds when I have write access to foo and bar but baz doesn't exit.) */ if (itsadir && i > 0) { s[i-1] = '\0'; while (s[0] && !isdir(s)) { for (i = (int)strlen(s); i > 0; i--) { if (ISDIRSEP(s[i-1])) { s[i-1] = '\0'; break; } } if (i == 0) s[0] = '\0'; } } else { s[i++] = '.'; /* Append "." to path. */ s[i] = '\0'; } #endif /* COMMENT */ #endif /* COMMENT */ if (!s[0]) ckstrncpy(s,".",x+3); doaccess: #ifdef SW_ACC_ID debug(F100,"zchko swapping ids for access()","",0); priv_on(); #endif /* SW_ACC_ID */ x = access(s,W_OK); /* Check access of path. */ #ifdef SW_ACC_ID priv_off(); debug(F100,"zchko swapped ids restored","",0); #endif /* SW_ACC_ID */ if (x < 0) debug(F111,"zchko access failed:",s,errno); else debug(F111,"zchko access ok:",s,x); if (s) free(s); /* Free temporary storage */ return((x < 0) ? -1 : 0); /* and return. */ } /* Z D E L E T -- Delete the named file. */ /* Returns: -1 on error, 0 on success */ int zdelet(name) char *name; { int x; #ifdef CK_LOGIN if (isguest) return(-1); #endif /* CK_LOGIN */ #ifdef CKROOT debug(F111,"zdelet setroot",ckroot,ckrootset); if (ckrootset) if (!zinroot(name)) { debug(F110,"zdelet setroot violation",name,0); return(-1); } #endif /* CKROOT */ x = unlink(name); debug(F111,"zdelet",name,x); #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_FC && ckxlogging) { fullname[0] = '\0'; zfnqfp(name,CKMAXPATH,fullname); debug(F110,"zdelet fullname",fullname,0); if (x < 0) syslog(LOG_INFO, "file[] %s: delete failed (%m)", fullname); else syslog(LOG_INFO, "file[] %s: delete ok", fullname); } #endif /* CKSYSLOG */ return(x); } /* Z R T O L -- Convert remote filename into local form */ VOID zrtol(name,name2) char *name, *name2; { nzrtol(name,name2,1,0,CKMAXPATH); } VOID nzrtol(name,name2,fncnv,fnrpath,max) char *name, *name2; int fncnv, fnrpath, max; { /* nzrtol */ char *s, *p; int flag = 0, n = 0; char fullname[CKMAXPATH+1]; int devnull = 0; int acase = 0; if (!name2) return; if (!name) name = ""; debug(F111,"nzrtol name",name,fncnv); #ifdef DTILDE s = name; if (*s == '~') { s = tilde_expand(s); if (!s) s = ""; if (*s) name = s; } #endif /* DTILDE */ /* Handle the path -- we don't have to convert its format, since */ /* the standard path format and our (UNIX) format are the same. */ fullname[0] = NUL; devnull = !strcmp(name,"/dev/null"); if (!devnull && fnrpath == PATH_OFF) { /* RECEIVE PATHNAMES OFF */ zstrip(name,&p); strncpy(fullname,p,CKMAXPATH); } else if (!devnull && fnrpath == PATH_ABS) { /* REC PATHNAMES ABSOLUTE */ strncpy(fullname,name,CKMAXPATH); } else if (!devnull && isabsolute(name)) { /* RECEIVE PATHNAMES RELATIVE */ ckmakmsg(fullname,CKMAXPATH,".",name,NULL,NULL); } else { /* Ditto */ ckstrncpy(fullname,name,CKMAXPATH); } fullname[CKMAXPATH] = NUL; debug(F110,"nzrtol fullname",fullname,0); #ifndef NOTRUNCATE /* The maximum length for any segment of a filename is MAXNAMLEN, defined above. On some platforms (at least QNX) if a segment exceeds this limit, the open fails with ENAMETOOLONG, so we must prevent it by truncating each overlong name segment to the maximum segment length before passing the name to open(). This must be done even when file names are literal, so as not to halt a file transfer unnecessarily. */ { char buf[CKMAXPATH+1]; /* New temporary buffer on stack */ char *p = fullname; /* Source and */ char *s = buf; /* destination pointers */ int i = 0, n = 0; debug(F101,"nzrtol sizing MAXNAMLEN","",MAXNAMLEN); while (*p && n < CKMAXPATH) { /* Copy name to new buffer */ if (++i > MAXNAMLEN) { /* If this segment too long */ while (*p && *p != '/') /* skip past the rest... */ p++; i = 0; /* and reset counter. */ } else if (*p == '/') { /* End of this segment. */ i = 0; /* Reset counter. */ } *s++ = *p++; /* Copy this character. */ n++; } *s = NUL; ckstrncpy(fullname,buf,CKMAXPATH); /* Copy back to original buffer. */ debug(F111,"nzrtol sizing",fullname,n); } #endif /* NOTRUNCATE */ if (!fncnv || devnull) { /* Not converting */ ckstrncpy(name2,fullname,max); /* We're done. */ return; } name = fullname; /* Converting */ p = name2; for (; *name != '\0' && n < maxnam; name++) { if (*name > SP) flag = 1; /* Strip leading blanks and controls */ if (flag == 0 && *name < '!') continue; if (fncnv > 0) { if (*name == SP) { *p++ = '_'; n++; continue; } if (isupper(*name)) /* Check for mixed case */ acase |= 1; else if (islower(*name)) acase |= 2; } *p++ = *name; n++; } *p-- = '\0'; /* Terminate */ while (*p < '!' && p > name2) /* Strip trailing blanks & controls */ *p-- = '\0'; if (*name2 == '\0') { /* Nothing left? */ ckstrncpy(name2,"NONAME",max); /* do this... */ } else if (acase == 1) { /* All uppercase? */ p = name2; /* So convert all letters to lower */ while (*p) { if (isupper(*p)) *p = tolower(*p); p++; } } debug(F110,"nzrtol new name",name2,0); } /* Z S T R I P -- Strip device & directory name from file specification */ /* Strip pathname from filename "name", return pointer to result in name2 */ static char work[CKMAXPATH+1]; VOID zstrip(name,name2) char *name, **name2; { char *cp, *pp; int n = 0; debug(F110,"zstrip before",name,0); if (!name) { *name2 = ""; return; } pp = work; #ifdef DTILDE /* Strip leading tilde */ if (*name == '~') name++; debug(F110,"zstrip after tilde-stripping",name,0); #endif /* DTILDE */ for (cp = name; *cp; cp++) { if (ISDIRSEP(*cp)) { pp = work; n = 0; } else { *pp++ = *cp; if (n++ >= CKMAXPATH) break; } } *pp = '\0'; /* Terminate the string */ *name2 = work; debug(F110,"zstrip after",*name2,0); } /* Z L T O R -- Local TO Remote */ VOID zltor(name,name2) char *name, *name2; { nzltor(name,name2,1,0,CKMAXPATH); } /* N Z L T O R -- New Local TO Remote */ /* fncnv = 0 for no conversion, > 0 for regular conversion, < 0 for minimal. */ VOID nzltor(name,name2,fncnv,fnspath,max) char *name, *name2; int fncnv, fnspath, max; { /* nzltor */ char *cp, *pp; #ifdef COMMENT int dc = 0; #endif /* COMMENT */ int n = 0; char *dotp = NULL; char *dirp = NULL; char fullname[CKMAXPATH+1]; char *p; CHAR c; #ifndef NOCSETS extern int fcharset, /* tcharset, */ language; int langsv; _PROTOTYP ( CHAR (*sxo), (CHAR) ) = NULL; /* Translation functions */ #ifdef CK_ANSIC extern CHAR (*xls[MAXTCSETS+1][MAXFCSETS+1])(CHAR); #else extern CHAR (*xls[MAXTCSETS+1][MAXFCSETS+1])(); #endif /* CK_ANSIC */ langsv = language; language = L_USASCII; #ifdef COMMENT /* Proper translation of filenames must be done elsewhere */ n = tcharset ? tcharset : TC_USASCII; sxo = xls[n][fcharset]; #else sxo = xls[TC_USASCII][fcharset]; #endif /* COMMENT */ #endif /* NOCSETS */ debug(F110,"nzltor name",name,0); /* Handle pathname */ fullname[0] = NUL; if (fnspath == PATH_OFF) { /* PATHNAMES OFF */ zstrip(name,&p); ckstrncpy(fullname,p,CKMAXPATH); } else { /* PATHNAMES RELATIVE or ABSOLUTE */ char * p = name; while (1) { if (!strncmp(p,"../",3)) p += 3; else if (!strncmp(p,"./",2)) p += 2; else break; } if (fnspath == PATH_ABS) { /* ABSOLUTE */ zfnqfp(p,CKMAXPATH,fullname); } else { /* RELATIVE */ ckstrncpy(fullname,p,CKMAXPATH); } } debug(F110,"nzltor fullname",fullname,0); if (!fncnv) { /* Not converting */ ckstrncpy(name2,fullname,max); /* We're done. */ #ifndef NOCSETS langsv = language; #endif /* NOCSETS */ return; } name = fullname; /* Converting */ #ifdef aegis char *namechars; int tilde = 0, bslash = 0; if ((namechars = getenv("NAMECHARS")) != NULL) { if (ckstrchr(namechars, '~' ) != NULL) tilde = '~'; if (ckstrchr(namechars, '\\') != NULL) bslash = '\\'; } else { tilde = '~'; bslash = '\\'; } #endif /* aegis */ pp = work; /* Output buffer */ for (cp = name, n = 0; *cp && n < max; cp++,n++) { /* Convert name chars */ c = *cp; #ifndef NOCSETS if (sxo) c = (*sxo)(c); /* Convert to ASCII */ #endif /* NOCSETS */ if (fncnv > 0 && islower(c)) /* Uppercase letters */ *pp++ = toupper(c); /* Change tilde to hyphen */ else if (c == '~') *pp++ = '-'; else if (fncnv > 0 && 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 controls to 'X' */ *pp++ = 'X'; else if (fncnv > 0 && 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. */ #ifdef COMMENT if (dotp) *dotp = '.'; /* Restore last dot (if any) */ #else if (dotp > dirp) *dotp = '.'; /* Restore last dot in file name */ #endif /* COMMENT */ cp = name2; /* If nothing before dot, */ if (*work == '.') *cp++ = 'X'; /* insert 'X' */ ckstrncpy(cp,work,max); #ifndef NOCSETS language = langsv; #endif /* NOCSETS */ debug(F110,"nzltor name2",name2,0); } /* Z C H D I R -- Change directory */ /* Call with: dirnam = pointer to name of directory to change to, which may be "" or NULL to indicate user's home directory. Returns: 0 on failure 1 on success */ int zchdir(dirnam) char *dirnam; { char *hd, *sp; #ifdef IKSDB _PROTOTYP (int slotdir,(char *,char *)); #endif /* IKSDB */ #ifndef NOSPL extern struct mtab *mactab; /* Main macro table */ extern int nmac; /* Number of macros */ #endif /* NOSPL */ debug(F110,"zchdir",dirnam,0); if (!dirnam) dirnam = ""; if (!*dirnam) /* If argument is null or empty, */ dirnam = zhome(); /* use user's home directory. */ sp = dirnam; debug(F110,"zchdir 2",dirnam,0); #ifdef DTILDE hd = tilde_expand(dirnam); /* Attempt to expand tilde */ if (!hd) hd = ""; if (*hd == '\0') hd = dirnam; /* in directory name. */ #else hd = dirnam; #endif /* DTILDE */ debug(F110,"zchdir 3",hd,0); #ifdef CKROOT debug(F111,"zchdir setroot",ckroot,ckrootset); if (ckrootset) if (!zinroot(hd)) { debug(F110,"zchdir setroot violation",hd,0); return(0); } #endif /* CKROOT */ #ifdef pdp11 /* Just to save some space */ return((chdir(hd) == 0) ? 1 : 0); #else if (chdir(hd) == 0) { /* Try to cd */ #ifdef IKSDB #ifdef CK_LOGIN if (inserver && ikdbopen) slotdir(isguest ? anonroot : "", zgtdir()); #endif /* CK_LOGIN */ #endif /* IKSDB */ #ifndef NOSPL if (nmac) { /* Any macros defined? */ int k; /* Yes */ static int on_cd = 0; if (!on_cd) { on_cd = 1; k = mlook(mactab,"on_cd",nmac); /* Look this up */ if (k >= 0) { /* If found, */ if (dodo(k,zgtdir(),0) > -1) /* set it up, */ parser(1); /* and execute it */ } on_cd = 0; } } #endif /* NOSPL */ return(1); } return(0); #endif /* pdp11 */ } int #ifdef CK_ANSIC zchkpid(unsigned long xpid) #else zchkpid(xpid) unsigned long xpid; #endif /* CK_ANSIC */ { return((kill((PID_T)xpid,0) < 0) ? 0 : 1); } /* Z H O M E -- Return pointer to user's home directory */ static char * zhomdir = NULL; char * zhome() { char * home; #ifdef CKROOT if (ckrootset) return((char *)ckroot); #endif /* CKROOT */ #ifdef Plan9 home = getenv("home"); #else home = getenv("HOME"); #endif /* Plan9 */ makestr(&zhomdir,home); return(home ? zhomdir : "."); } /* Z G T D I R -- Returns a pointer to the current directory */ /* The "preferred" interface for getting the current directory in modern UNIX is getcwd() [POSIX 1003.1 5.2.2]. However, on certain platforms (such as SunOS), it is implemented by forking a shell, feeding it the pwd command, and returning the result, which is not only inefficient but also can result in stray messages to the terminal. In such cases -- as well as when getcwd() is not available at all -- getwd() can be used instead by defining USE_GETWD. However, note that getwd() provides no buffer-length argument and therefore no safeguard against memory leaks. */ #ifndef USE_GETWD #ifdef BSD42 #define USE_GETWD #else #ifdef SUNOS4 #define USE_GETWD #endif /* SUNOS4 */ #endif /* BSD42 */ #endif /* USE_GETWD */ #ifdef pdp11 #define CWDBL 80 /* Save every byte we can... */ #else #define CWDBL CKMAXPATH #endif /* pdp11 */ static char cwdbuf[CWDBL+2]; /* NOTE: The getcwd() prototypes are commented out on purpose. If you get compile-time warnings, search through your system's header files to see which one has the needed prototype, and #include it. Usually it is . See the section for including in ckcdeb.h and make any needed adjustments there (and report them). */ char * zgtdir() { char * buf = cwdbuf; char * s; #ifdef USE_GETWD extern char *getwd(); s = getwd(buf); debug(F110,"zgtdir BSD4 getwd()",s,0); if (!s) s = "./"; return(s); #else #ifdef BSD44 #ifdef DCLGETCWD _PROTOTYP( char * getcwd, (char *, SIZE_T) ); #endif /* DCLGETCWD */ debug(F101,"zgtdir BSD44 CWDBL","",CWDBL); s = getcwd(buf,CWDBL); if (!s) s = "./"; return(s); #else #ifdef MINIX2 #ifdef DCLGETCWD _PROTOTYP( char * getcwd, (char *, SIZE_T) ); #endif /* DCLGETCWD */ debug(F101,"zgtdir MINIX2 CWDBL","",CWDBL); s = getcwd(buf,CWDBL); if (!s) s = "./"; return(s); #else #ifdef SVORPOSIX #ifdef COMMENT /* This non-ANSI prototype can be fatal at runtime! (e.g. in SCO3.2v5.0.5). */ /* Anyway it's already prototyped in some header file that we have included. */ extern char *getcwd(); #else #ifdef DCLGETCWD _PROTOTYP( char * getcwd, (char *, SIZE_T) ); #endif /* DCLGETCWD */ #endif /* COMMENT */ debug(F101,"zgtdir SVORPOSIX CWDBL","",CWDBL); s = getcwd(buf,CWDBL); if (!s) s = "./"; return(s); #else #ifdef COHERENT #ifdef _I386 #ifdef DCLGETCWD extern char *getcwd(); #endif /* DCLGETCWD */ debug(F101,"zgtdir COHERENT _I386 CWDBL","",CWDBL); s = getcwd(buf,CWDBL); if (!s) s = "./"; return(s); #else extern char *getwd(); debug(F101,"zgtdir COHERENT CWDBL","",CWDBL); s = getwd(buf); if (!s) s = "./"; return(s); #endif /* _I386 */ #else #ifdef SUNOS4 debug(F101,"zgtdir SUNOS CWDBL","",CWDBL); s = getcwd(buf,CWDBL); if (!s) s = "./"; return(s); #else return("./"); #endif /* SUNOS4 */ #endif /* COHERENT */ #endif /* SYSVORPOSIX */ #endif /* MINIX2 */ #endif /* BSD44 */ #endif /* USE_GETWD */ } /* Z X C M D -- Run a system command so its output can be read like a file */ #ifndef NOPUSH int zxcmd(filnum,comand) int filnum; char *comand; { int out; int pipes[2]; extern int kactive; /* From ckcpro.w and ckcmai.c */ if (nopush) { debug(F100,"zxcmd fails: nopush","",0); return(-1); } debug(F111,"zxcmd",comand,filnum); if (chkfn(filnum) < 0) return(-1); /* Need a valid Kermit file number. */ if (filnum == ZSTDIO || filnum == ZCTERM) /* But not one of these. */ return(0); out = (filnum == ZIFILE || filnum == ZRFILE) ? 0 : 1 ; debug(F101,"zxcmd out",comand,out); /* Output to a command */ if (out) { /* Need popen() to do this. */ ckstrncpy(fullname,"(pipe)",CKMAXPATH); #ifdef NOPOPEN return(0); /* no popen(), fail. */ #else /* Use popen() to run the command. */ #ifdef _POSIX_SOURCE /* Strictly speaking, popen() is not available in POSIX.1 */ #define DCLPOPEN #endif /* _POSIX_SOURCE */ debug(F110,"zxcmd out",comand,0); if (priv_chk()) { debug(F100,"zxcmd priv_chk failed","",0); return(0); } errno = 0; fp[filnum] = popen(comand,"w"); debug(F111,"zxcmd popen",fp[filnum] ? "OK" : "Failed", errno); if (fp[filnum] == NULL) return(0); #ifdef COMMENT /* I wonder what this is all about... */ close(pipes[0]); /* Don't need the input side */ fp[filnum] = fdopen(pipes[1],"w"); /* Open output stream. */ fp[ZSYSFN] = fp[filnum]; /* Remember. */ #endif /* COMMENT */ ispipe[filnum] = 1; zoutcnt = 0; /* (PWP) reset input buffer */ zoutptr = zoutbuffer; return(1); #endif /* NOPOPEN */ } /* Input from a command */ #ifdef SNI541 /* SINIX-L 5.41 does not like fdopen() */ return(0); #else if (pipe(pipes) != 0) { debug(F100,"zxcmd pipe failure","",0); return(0); /* can't make pipe, fail */ } /* Create a fork in which to run the named process */ if (( #ifdef aegis pid = vfork() /* child */ #else pid = fork() /* child */ #endif /* aegis */ ) == 0) { /* We're in the fork. */ char *shpath, *shname, *shptr; /* Find user's preferred shell */ #ifndef aegis struct passwd *p; char *defshell; #ifdef HPUX10 /* Default shell */ defshell = "/usr/bin/sh"; #else #ifdef Plan9 defshell = "/bin/rc"; #else defshell = "/bin/sh"; #endif /* Plan9 */ #endif /* HPUX10 */ #endif /* aegis */ if (priv_can()) exit(1); /* Turn off any privileges! */ debug(F101,"zxcmd pid","",pid); close(pipes[0]); /* close input side of pipe */ close(0); /* close stdin */ if (open("/dev/null",0) < 0) return(0); /* replace input by null */ #ifndef OXOS #ifndef SVORPOSIX dup2(pipes[1],1); /* BSD: replace stdout & stderr */ dup2(pipes[1],2); /* by the pipe */ #else close(1); /* AT&T: close stdout */ if (dup(pipes[1]) != 1) /* Send stdout to the pipe */ return(0); close(2); /* Send stderr to the pipe */ if (dup(pipes[1]) != 2) return(0); #endif /* SVORPOSIX */ #else /* OXOS */ dup2(pipes[1],1); dup2(pipes[1],2); #endif /* OXOS */ close(pipes[1]); /* Don't need this any more. */ #ifdef aegis if ((shpath = getenv("SERVERSHELL")) == NULL) shpath = "/bin/sh"; #else shpath = getenv("SHELL"); /* What shell? */ if (shpath == NULL) { p = getpwuid( real_uid() ); /* Get login data */ /* debug(F111,"zxcmd shpath","getpwuid()",p); */ if (p == (struct passwd *)NULL || !*(p->pw_shell)) shpath = defshell; else shpath = p->pw_shell; } #endif /* aegis */ shptr = shname = shpath; while (*shptr != '\0') if (*shptr++ == '/') shname = shptr; debug(F110,shpath,shname,0); restorsigs(); /* Restore ignored signals */ execl(shpath,shname,"-c",comand,(char *)NULL); /* Execute the cmd */ exit(0); /* just punt if it failed. */ } else if (pid == (PID_T) -1) { debug(F100,"zxcmd fork failure","",0); return(0); } debug(F101,"zxcmd pid","",pid); close(pipes[1]); /* Don't need the output side */ ispipe[filnum] = 1; /* Remember it's a pipe */ fp[filnum] = fdopen(pipes[0],"r"); /* Open a stream for input. */ #ifdef DONDELAY #ifdef SELECT if (filnum == ZIFILE && kactive) { /* Make pipe reads nonblocking */ int flags, x; if ((flags = fcntl(fileno(fp[filnum]),F_GETFL,0)) > -1) { debug(F101,"zxcmd fcntl 1 pipe flags","",flags); x = fcntl(fileno(fp[filnum]),F_SETFL, flags | #ifdef QNX O_NONBLOCK #else O_NDELAY #endif /* QNX */ ); debug(F101,"zxcmd fcntl 2 result","",x); } } #endif /* SELECT */ #endif /* DONDELAY */ #endif /* SNI541 */ fp[ZSYSFN] = fp[filnum]; /* Remember. */ zincnt = 0; /* (PWP) reset input buffer */ zinptr = zinbuffer; fullname[0] = '\0'; return(1); } /* zxcmd */ /* Z C L O S F - wait for the child fork to terminate and close the pipe. */ /* Used internally by zclose - returns -1 on failure, 1 on success. */ int zclosf(filnum) int filnum; { int wstat, out; int statusp; debug(F101,"zclosf filnum","",filnum); out = (filnum == ZIFILE || filnum == ZRFILE) ? 0 : 1 ; debug(F101,"zclosf out","",out); #ifndef NOPOPEN if (ispipe[filnum] /* In UNIX we use popen() only for output files */ && out ) { int x; x = pclose(fp[filnum]); pexitstat = x >> 8; debug(F101,"zclosf pclose","",x); debug(F101,"zclosf pexitstat","",pexitstat); fp[filnum] = fp[ZSYSFN] = NULL; ispipe[filnum] = 0; return((x != 0) ? -1 : 1); } #endif /* NOPOPEN */ /* debug(F101,"zclosf fp[filnum]","", fp[filnum]); */ /* debug(F101,"zclosf fp[ZSYSFN]","", fp[ZSYSFN]); */ if (pid != (PID_T) 0) { debug(F101,"zclosf killing pid","",pid); #ifdef Plan9 kill(pid, SIGKILL); #else kill(pid,9); #endif /* Plan9 */ #ifndef CK_CHILD /* This is the original code (before 20 April 1997) and has proven totally portable. But it does not give us the process's return code. */ while ((wstat = wait((WAIT_T *)0)) != pid && wstat != -1) ; #else /* Here we try to get the return code. Let's hope this is portable too. */ while ((wstat = wait(&statusp)) != pid && wstat != -1) ; pexitstat = (statusp & 0xff) ? statusp : statusp >> 8; debug(F101,"zclosf wait statusp","",statusp); debug(F101,"zclosf wait pexitstat","",pexitstat); #endif /* CK_CHILD */ pid = 0; } fclose(fp[filnum]); fp[filnum] = fp[ZSYSFN] = NULL; ispipe[filnum] = 0; /* debug(F101,"zclosf fp[filnum]","",fp[filnum]); */ #ifdef CK_CHILD return(pexitstat == 0 ? 1 : -1); #else return(1); #endif /* CK_CHILD */ } #else /* NOPUSH */ int zxcmd(filnum,comand) int filnum; char *comand; { return(0); } int zclosf(filnum) int filnum; { return(EOF); } #endif /* NOPUSH */ /* Z X P A N D -- Expand a wildcard string into an array of strings */ /* As of C-Kermit 7.0, this API is obsolete, replaced by nzxpand(), and this function is only used internally. See nzxpand() below. Returns the number of files that match fnarg, with data structures set up so that first file (if any) will be returned by the next znext() call. Depends on external variable wildxpand: 0 means we expand wildcards internally, nonzero means we call the shell to do it. AND in C-Kermit 8.0.212 and later, on extern wildena: 1 means wildcards are enabled, 0 means disabled, the characters are taken literally. */ static int xdironly = 0; static int xfilonly = 0; static int xmatchdot = 0; static int xrecursive = 0; static int xnobackup = 0; static int xnolinks = 0; static char *freeptr = NULL, **resptr = NULL; /* Copies of caller's args */ static int remlen; /* Remaining space in caller's array */ static int numfnd = 0; /* Number of matches found */ #define MINSPACE 1024 static int initspace(resarry,len) char * resarry[]; int len; { #ifdef DYNAMIC if (len < MINSPACE) len = MINSPACE; if (!sspace) { /* Need to allocate string space? */ while (len >= MINSPACE) { if ((sspace = malloc(len+2))) { /* Got it. */ debug(F101,"fgen string space","",len); break; } len = (len / 2) + (len / 4); /* Didn't, reduce by 3/4 */ } if (len <= MINSPACE) { /* Did we get it? */ fprintf(stderr,"fgen can't malloc string space\n"); return(-1); } ssplen = len; } #endif /* DYNAMIC */ freeptr = sspace; /* This is where matches are copied. */ resptr = resarry; /* Static copies of these so */ remlen = len; /* recursive calls can alter them. */ debug(F101,"initspace ssplen","",ssplen); return(0); } /* Z S E T F I L -- Query or change the size of file list buffers. fc = 1: Change current string space to n, return new size. fc = 2: Return current string space size. fc = 3: Change current maxnames to n, return new maxnames. fc = 4: Return current maxnames. Returns < 0 on error. */ int zsetfil(n, fc) int n, fc; { #ifdef DYNAMIC switch (fc) { case 1: /* Stringspace */ if (sspace) { free(sspace); sspace = NULL; } if (initspace(mtchs,n) < 0) return(-1); case 2: /* Fall thru deliberately */ return(ssplen); case 3: /* Listsize */ if (mtchs) { free((char *)mtchs); mtchs = NULL; } mtchs = (char **)malloc(n * sizeof(char *)); if (!mtchs) return(-1); maxnames = n; case 4: /* Fall thru deliberately */ return(maxnames); } #endif /* DYNAMIC */ return(-1); } #ifndef NONZXPAND #ifndef pdp11 static #endif /* pdp11 */ #endif /* NONZXPAND */ int zxpand(fnarg) char *fnarg; { extern int diractive; char fnbuf[CKMAXPATH+8], * fn, * p; #ifdef DTILDE /* Built with tilde-expansion? */ char *tnam; #endif /* DTILDE */ int x; int haveonedir = 0; if (!fnarg) { /* If no argument provided */ nxpand = fcount = 0; return(0); /* Return zero files found */ } debug(F110,"zxpand entry",fnarg,0); debug(F101,"zxpand xdironly","",xdironly); debug(F101,"zxpand xfilonly","",xfilonly); if (!*fnarg) { /* If no argument provided */ nxpand = fcount = 0; return(0); /* Return zero files found */ } #ifdef CKROOT debug(F111,"zxpand setroot",ckroot,ckrootset); if (ckrootset) if (!zinroot(fnarg)) { debug(F110,"zxpand setroot violation",fnarg,0); nxpand = fcount = 0; return(0); } #endif /* CKROOT */ #ifdef COMMENT /* This would have been perfect, except it makes us return fully qualified pathnames for all files. */ zfnqfp(fnarg,CKMAXPATH,fnbuf); debug(F110,"zxpand zfnqfp",fnbuf,0); s = zgtdir(); debug(F110,"zxpand zgtdir",s,0); p = fnbuf; while (*p && *s) /* Make it relative */ if (*s++ != *p++) break; fn = (*s) ? fnbuf : p; debug(F110,"zxpand fn 0",fn,0); if (!*fn) { fn = fnbuf; fnbuf[0] = '*'; fnbuf[1] = '\0'; } debug(F110,"zxpand fn 0.5",fn,0); #else #ifdef DTILDE /* Built with tilde-expansion? */ if (*fnarg == '~') { /* Starts with tilde? */ tnam = tilde_expand(fnarg); /* Try to expand it. */ ckstrncpy(fnbuf,tnam,CKMAXPATH); } else #endif /* DTILDE */ ckstrncpy(fnbuf,fnarg,CKMAXPATH); fn = fnbuf; /* Point to what we'll work with */ #endif /* COMMENT */ debug(F110,"zxpand fn 1",fn,0); if (!*fn) /* But make sure something is there */ return(0); p = fn + (int)strlen(fn) - 1; if (*p == '/') { /* If last char = / it must be a dir */ if (!xfilonly && !iswild(p)) haveonedir++; ckstrncat(fn, "*", CKMAXPATH+8); /* so append '*' */ } else if (p > fn) { /* If ends in "/." */ if (*(p-1) == '/' && *p == '.') /* change '.' to '*' */ *p = '*'; } else if (p == fn) { /* If it's '.' alone */ if (*p == '.') /* change '.' to '*' */ *p = '*'; } debug(F110,"zxpand fn 2",fn,0); x = isdir(fn); /* Is it a directory? */ debug(F111,"zxpand isdir 1",fn,x); if (x) { /* If so, make it into a wildcard */ if (!xfilonly && !iswild(p)) haveonedir++; if ((x = strlen(fn)) > 0) { if (!ISDIRSEP(fn[x-1])) fn[x++] = DIRSEP; fn[x++] = '*'; fn[x] = '\0'; } } debug(F111,"zxpand fn 3 haveonedir",fn,haveonedir); /* The following allows us to parse a single directory name without opening the directory and looking at its contents. The diractive flag is a horrible hack (especially since DIR /NORECURSIVE turns it off), but otherwise we'd have to change the API. */ debug(F111,"zxpand fn 3 diractive",fn,diractive); if (!diractive && haveonedir) { fcount = 0; if (!mtchs) { mtchs = (char **)malloc(maxnames * sizeof(*mtchs)); if (!mtchs) return(nxpand = fcount); } fcount = 1; debug(F110,"zxpand haveonedir A1",fnarg,0); initspace(mtchs,ssplen); addresult(fnarg,1); if (numfnd < 0) return(-1); mtchptr = mtchs; /* Save pointer for next. */ debug(F111,"zxpand haveonedir A2",*mtchptr,numfnd); return(nxpand = fcount); } #ifndef NOPUSH if (!nopush && wildxpand) /* Who is expanding wildcards? */ fcount = (mtchs == NULL && /* Shell */ (mtchs = (char **)malloc(maxnames * sizeof(*mtchs))) == NULL) ? 0 : shxpand(fn,mtchs,maxnames); else #endif /* NOPUSH */ fcount = (mtchs == NULL && /* Kermit */ (mtchs = (char **)malloc(maxnames * sizeof(*mtchs))) == NULL) ? 0 : fgen(fn,mtchs,maxnames); /* Look up the file. */ if (fcount == 0 && haveonedir) { fcount = 1; debug(F110,"zxpand haveonedir B",fnarg,0); addresult(fnarg,1); if (numfnd < 0) return(-1); } mtchptr = mtchs; /* Save pointer for next. */ nxpand = fcount; #ifdef DEBUG if (deblog) { if (fcount > 1) debug(F111,"zxpand ok",mtchs[0],fcount); else debug(F101,"zxpand fcount","",fcount); } #endif /* DEBUG */ return(fcount); } #ifndef NONZXPAND /* N Z X P A N D -- Expand a file list, with options. */ /* Call with: s = pointer to filename or pattern. flags = option bits: flags & ZX_FILONLY Match regular files flags & ZX_DIRONLY Match directories flags & ZX_RECURSE Descend through directory tree flags & ZX_MATCHDOT Match "dot files" flags & ZX_NOBACKUP Don't match "backup files" flags & ZX_NOLINKS Don't follow symlinks. Returns the number of files that match s, with data structures set up so that first file (if any) will be returned by the next znext() call. */ int nzxpand(s,flags) char * s; int flags; { char * p; int x; debug(F111,"nzxpand",s,flags); x = flags & (ZX_DIRONLY|ZX_FILONLY); xdironly = (x == ZX_DIRONLY); xfilonly = (x == ZX_FILONLY); if (xdironly && xfilonly) { xdironly = 0; xfilonly = 0; } xmatchdot = (flags & ZX_MATCHDOT); debug(F111,"nzxpand xmatchdot 1",s,xmatchdot); /* If xmatchdot not set by caller but pattern implies it, set it anyway */ if (!xmatchdot && ((p = ckstrchr(s,'.')))) { if (p == s && p[1] != '/') { xmatchdot = 1; debug(F111,"nzxpand xmatchdot 2",s,xmatchdot); } else if (p > s) { xmatchdot = (*(p-1) == ',') || (*(p-1) == '{') || (*(p-1) == '/'); debug(F111,"nzxpand xmatchdot 3",s,xmatchdot); } } xrecursive = (flags & ZX_RECURSE); xnobackup = (flags & ZX_NOBACKUP); xnolinks = (flags & ZX_NOLINKS); #ifdef DEBUG if (deblog) { debug(F101,"nzxpand xdironly","",xdironly); debug(F101,"nzxpand xfilonly","",xfilonly); debug(F101,"nzxpand xmatchdot","",xmatchdot); debug(F101,"nzxpand xrecursive","",xrecursive); debug(F101,"nzxpand xnobackup","",xnobackup); debug(F101,"nzxpand xnolinks","",xnolinks); } #endif /* DEBUG */ x = zxpand(s); if (x > 1) sh_sort(mtchs,NULL,x,0,0,1); /* Alphabetize the list */ xdironly = 0; xfilonly = 0; xmatchdot = 0; xrecursive = 0; xnobackup = 0; xnolinks = 0; return(x); } #endif /* NONZXPAND */ #ifndef NOZXREWIND /* Z X R E W I N D -- Rewinds the zxpand() list */ int zxrewind() { /* if (!mtchs) return(-1); */ fcount = nxpand; mtchptr = mtchs; return(nxpand); } #endif /* NOZXREWIND */ /* Z N E X T -- Get name of next file from list created by zxpand(). */ /* Returns >0 if there's another file, with its name copied into the arg string, or 0 if no more files in list. */ int znext(fn) char *fn; { if (fcount-- > 0) { ckstrncpy(fn,*mtchptr++,CKMAXPATH); } else { fn[0] = '\0'; } #ifndef COMMENT debug(F111,"znext",fn,fcount+1); return(fcount+1); #else debug(F111,"znext",fn,fcount); /* Return 0 if no filename to return */ return(fcount); #endif /* COMMENT */ } /* Z C H K S P A -- Check if there is enough space to store the file */ /* Call with file specification f, size n in bytes. Returns -1 on error, 0 if not enough space, 1 if enough space. */ /*ARGSUSED*/ int #ifdef CK_ANSIC zchkspa(char *f, CK_OFF_T n) #else zchkspa(f,n) char *f; CK_OFF_T n; #endif /* CK_ANSIC */ /* zchkspa() */ { /* In UNIX there is no good (and portable) way. */ return(1); /* Always say OK. */ } #ifdef COMMENT /* (not used) */ /* I S B A C K U P -- Tells if given file has a backup suffix */ /* Returns: -1: Invalid argument 0: File does not have a backup suffix >0: Backup suffix number */ int isbackup(fn) char * fn; { /* Get backup suffix number */ int i, j, k, x, state, flag; if (!fn) /* Watch out for null pointers. */ return(-1); if (!*fn) /* And empty names. */ return(-1); flag = state = 0; for (i = (int)strlen(fn) - 1; (!flag && (i > 0)); i--) { switch (state) { case 0: /* State 0 - final char */ if (fn[i] == '~') /* Is tilde */ state = 1; /* Switch to next state */ else /* Otherwise */ flag = 1; /* Quit - no backup suffix. */ break; case 1: /* State 1 - digits */ if (fn[i] == '~' && fn[i-1] == '.') { /* Have suffix */ return(atoi(&fn[i+1])); } else if (fn[i] >= '0' && fn[i] <= '9') { /* In number part */ continue; /* Keep going */ } else { /* Something else */ flag = 1; /* Not a backup suffix - quit. */ } break; } } return(0); } #endif /* COMMENT */ /* Z N E W N -- Make a new name for the given file */ /* Given the name, fn, of a file that already exists, this function builds a new name of the form ".~~", where is argument name (fn), and is a version number, one higher than any existing version number for that file, up to 99999. This format is consistent with that used by GNU EMACS. If the constructed name is too long for the system's maximum, enough characters are truncated from the end of to allow the version number to fit. If no free version numbers exist between 1 and 99999, a version number of "xxxx" is used. Returns a pointer to the new name in argument s. */ #ifdef pdp11 #define ZNEWNBL 63 /* Name buffer length */ #define ZNEWNMD 3 /* Max digits for version number */ #else #define ZNEWNBL CKMAXPATH #define ZNEWNMD 4 #endif /* pdp11 */ #define MAXBUDIGITS 5 static char znewbuf[ZNEWNBL+12]; VOID znewn(fn,s) char *fn, **s; { char * buf; /* Pointer to buffer for new name */ char * xp, * namepart = NULL; /* Pointer to filename part */ struct zfnfp * fnfp; /* znfqfp() result struct pointer */ int d = 0, t, fnlen, buflen; int n, i, k, flag, state; int max = MAXNAMLEN; /* Maximum name length */ char * dname = NULL; buf = znewbuf; *s = NULL; /* Initialize return value */ if (!fn) fn = ""; /* Check filename argument */ i = strlen(fn); /* If incoming file already has a backup suffix, remove it. */ /* Then we'll tack a new on later, which will be the highest for this file. */ if (i <= max && i > 0 && fn[i-1] == '~') { char * p; i--; debug(F111,"znewn suffix removal",fn,i); if ((dname = (char *)malloc(i+1))) { ckstrncpy(dname,fn,i+1); p = dname; 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 */ fn = dname; debug(F111,"znewn suffix removal 2",fn,i); flag = 1; /* done */ } else if (p[i] >= '0' && p[i] <= '9') { /* Number part */ continue; /* Keep going */ } else { /* Something else */ flag = 1; /* Not a backup suffix - quit. */ } break; } } } } if ((fnlen = strlen(fn)) < 1) { /* Get length */ if (dname) free(dname); return; } debug(F111,"znewn",fn,fnlen); debug(F101,"znewn max 1","",max); if (max < 14) max = 14; /* Make max reasonable for any UNIX */ if (max > ZNEWNBL) max = ZNEWNBL; debug(F101,"znewn max 2","",max); if ((fnfp = zfnqfp(fn, ZNEWNBL, buf))) { /* Get fully qualified name */ namepart = fnfp->fname; /* Isolate the filename */ k = strlen(fn); /* Length of name part */ debug(F111,"znewn namepart",namepart,k); } else { if (dname) free(dname); return; } buflen = fnfp->len; /* Length of fully qualified name */ debug(F111,"znewn len",buf,buflen); if (k + MAXBUDIGITS + 3 < max) { /* Backup name fits - no overflow */ /* Make pattern for backup names */ ckstrncpy(buf+buflen,".~*~",ZNEWNBL+12-buflen); n = nzxpand(buf,ZX_FILONLY); /* Expand the pattern */ debug(F111,"znewn A matches",buf,n); while (n-- > 0) { /* Find any existing name.~n~ files */ xp = *mtchptr++; /* Point at matching name */ t = atoi(xp+buflen+2); /* Get number */ if (t > d) d = t; /* Save d = highest version number */ } sprintf(buf+buflen,".~%d~",d+1); /* Yes, make "name.~~" */ debug(F110,"znewn A newname",buf,0); } else { /* Backup name would be too long */ int xlen; /* So we have to eat back into it */ int delta; char buf2[ZNEWNBL+12]; delta = max - k; debug(F101,"znewn B delta","",delta); for (i = MAXBUDIGITS; i > 0; i--) { /* In this case the format of */ ckstrncpy(buf2,buf,ZNEWNBL+12); /* the backup name depends on */ xlen = buflen - i - 3 + delta; /* how many digits are in the */ ckstrncpy(buf2+xlen,".~*~",ZNEWNBL+12-xlen); /* backup number */ n = nzxpand(buf2,ZX_FILONLY); debug(F111,"znewn B matches",buf2,n); if (n > 0) break; } while (n-- > 0) { /* Find any existing name.~n~ files */ xp = *mtchptr++; /* Point at matching name */ t = atoi(xp+xlen+2); /* Get number */ if (t > d) d = t; /* Save d = highest version number */ } if (d > 0) /* If the odometer turned over... */ if ((d % 10) == 9) /* back up one space. */ xlen--; sprintf(buf2+xlen,".~%d~",d+1); /* This just fits */ ckstrncpy(buf,buf2,ZNEWNBL+12); /* (we could be more clever here...) */ debug(F110,"znewn B new name",buf,0); } *s = buf; /* Point to new name */ ck_znewn = d+1; /* Also make it available globally */ if (dname) free(dname); return; } /* Z R E N A M E -- Rename a file */ /* Call with old and new names. If new name is the name of a directory, the 'old' file is moved to that directory. Returns 0 on success, -1 on failure. */ int zrename(old,new) char *old, *new; { char *p, *s; int x; if (!old) old = ""; if (!new) new = ""; debug(F110,"zrename old",old,0); debug(F110,"zrename new",new,0); if (!*old) return(-1); if (!*new) return(-1); #ifdef IKSD #ifdef CK_LOGIN if (inserver && isguest) return(-1); #endif /* CK_LOGIN */ #endif /* IKSD */ #ifdef CKROOT debug(F111,"zrename setroot",ckroot,ckrootset); if (ckrootset) { if (!zinroot(old)) { debug(F110,"zrename old: setroot violation",old,0); return(-1); } if (!zinroot(new)) { debug(F110,"zrename new: setroot violation",new,0); return(-1); } } #endif /* CKROOT */ p = NULL; s = new; if (isdir(new)) { char *q = NULL; x = strlen(new); if (!(p = malloc(strlen(new) + strlen(old) + 2))) return(-1); strcpy(p,new); /* (safe) Directory part */ if (!ISDIRSEP(*(new+x-1))) /* Separator, if needed */ strcat(p,"/"); /* (safe) */ zstrip(old,&q); /* Strip path part from old name */ strcat(p,q); /* cat to new directory (safe) */ s = p; debug(F110,"zrename dir",s,0); } #ifdef DEBUG else debug(F110,"zrename no dir",s,0); #endif /* DEBUG */ #ifdef IKSD if (inserver && (!ENABLED(en_del))) { if (zchki(s) > -1) /* Destination file exists? */ return(-1); } #endif /* IKSD */ x = -1; /* Return code. */ #ifdef RENAME /* Atomic, preferred, uses a single system call, rename(), if available. */ x = rename(old,s); debug(F111,"zrename rename()",old,x); if (x) x = -1; #endif /* RENAME */ /* If rename() failed or not available try link()/unlink() */ if (x < 0) { if (zchko(old) > -1) { /* Requires write access to orignal */ x = link(old,s); debug(F111,"zrename link()",old,x); if (x > -1) { /* Make a link with the new name. */ x = unlink(old); debug(F111,"zrename unlink()",old,x); } /* If link/unlink failed copy and delete */ if (x < 0) { x = zcopy(old,s); debug(F111,"zrename zcopy()",old,x); if (x > -1) { x = zdelet(old); debug(F111,"zrename zdelet()",old,x); } } } } fullname[0] = '\0'; /* Clear this out for next time. */ #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_FC && ckxlogging) { zfnqfp(old,CKMAXPATH,fullname); tmp2[0] = '\0'; zfnqfp(s,CKMAXPATH,tmp2); if (x > -1) syslog(LOG_INFO,"file[] %s: renamed to %s ok", fullname, tmp2); else syslog(LOG_INFO,"file[] %s: rename to %s failed (%m)",fullname,tmp2); } #endif /* CKSYSLOG */ if (p) free(p); return(x); } /* Z C O P Y -- Copy a single file. */ /* Call with source and destination names. If destination is a directory, the source file is copied to that directory with its original name. Returns: 0 on success. <0 on failure: -2 = source file is not a regular file. -3 = source file not found. -4 = permission denied. -5 = source and destination are the same file. -6 = i/o error. -1 = other error. */ int zcopy(source,destination) char *source, *destination; { char *src, *dst; /* Local pointers to filenames */ int x, y, rc; /* Workers */ int in = -1, out = -1; /* i/o file descriptors */ struct stat srcbuf; /* Source file info buffer */ int perms; /* Output file permissions */ char buf[1024]; /* File copying buffer */ if (!source) source = ""; if (!destination) destination = ""; debug(F110,"zcopy src arg",source,0); debug(F110,"zcopy dst arg",destination,0); if (!*source) return(-1); if (!*destination) return(-1); #ifdef IKSD #ifdef CK_LOGIN if (inserver && isguest) return(-4); #endif /* CK_LOGIN */ #endif /* IKSD */ #ifdef CKROOT debug(F111,"zcopy setroot",ckroot,ckrootset); if (ckrootset) { if (!zinroot(source)) { debug(F110,"zcopy source: setroot violation",source,0); return(-1); } if (!zinroot(destination)) { debug(F110,"zcopy destination: setroot violation",destination,0); return(-1); } } #endif /* CKROOT */ src = source; dst = destination; if (stat(src,&srcbuf) == 0) { /* Get source file info */ struct stat dstbuf; /* Destination file info buffer */ debug(F101,"STAT","",6); if (stat(dst,&dstbuf) == 0) { debug(F101,"STAT","",7); if (srcbuf.st_dev == dstbuf.st_dev) if (srcbuf.st_ino == dstbuf.st_ino) { debug(F100,"zcopy files identical: stat()","",0); return(-5); } } } else { /* stat() failed... */ debug(F101,"STAT","",8); debug(F111,"source file not found",src,errno); return(-3); } fullname[0] = '\0'; /* Get full pathnames */ if (zfnqfp(source,CKMAXPATH,fullname)) src = fullname; debug(F110,"zcopy src",src,0); tmp2[0] = '\0'; if (zfnqfp(destination,CKMAXPATH,tmp2)) dst = tmp2; debug(F110,"zcopy dst 1",dst,0); if (!strcmp(src,dst)) { /* Src and dst are same file? */ debug(F100,"zcopy files identical: strcmp()","",0); /* This... */ return(-5); /* should not happen. */ } if (isdir(src)) { /* Source file is a directory? */ debug(F110,"zcopy source is directory",src,0); return(-2); /* Fail */ } if (isdir(dst)) { /* Destination is a directory? */ char *q = NULL; /* Yes, add filename to it. */ x = strlen(dst); if (x < 1) return(-1); if (!ISDIRSEP(*(dst+x-1))) { /* Add separator if needed */ tmp2[x++] = '/'; tmp2[x] = '\0'; } debug(F111,"zcopy dst 2",dst,x); zstrip(src,&q); /* Strip path part from old name */ ckstrncpy(tmp2+x,q,CKMAXPATH-x); /* Concatenate it to new name */ } debug(F110,"zcopy dst 3",dst,0); #ifdef IKSD if (inserver && (!ENABLED(en_del))) { if (zchki(dst) > -1) /* Destination file exists? */ return(-4); } #endif /* IKSD */ perms = umask(0); /* Get user's umask */ umask(perms); /* Put it back! */ perms ^= 0777; /* Flip the bits */ perms &= 0666; /* Zero execute bits from umask */ perms |= (srcbuf.st_mode & 0111); /* OR in source file's execute bits */ rc = -1; /* Default return code */ errno = 0; /* Reset errno */ in = open(src, O_RDONLY, 0); /* Open source file */ debug(F111,"zcopy open source",src,in); if (in > -1) { /* If open... */ /* Open destination file */ #ifdef O_TRUNC out = open(dst, O_WRONLY|O_CREAT|O_TRUNC, perms); #else out = open(dst, O_WRONLY|O_CREAT, perms); #endif /* O_TRUNC */ debug(F111,"zcopy open dest",dst,out); if (out > -1) { /* If open... */ while ((x = read(in,buf,1024)) > 0) { /* Copy in 1K blocks */ y = write(out,buf,x); if (y < 0) { /* On write failure */ x = -1; rc = -6; /* Indicate i/o error */ break; } } debug(F101,"zcopy final read","",x); debug(F101,"zcopy errno","",errno); rc = (x == 0) ? 0 : -6; /* In case of read failure */ } } if (in > -1) close(in); /* Close files */ if (out > -1) close(out); if (rc == -1) { /* Set return code */ switch (errno) { case ENOENT: rc = -3; break; case EACCES: rc = -4; break; case EIO: rc = -6; } } #ifdef CKSYSLOG if (rc > -1 && ckxsyslog >= SYSLG_FC && ckxlogging) { if (rc) syslog(LOG_INFO,"file[] %s: copy to %s failed (%m)", fullname, tmp2); else syslog(LOG_INFO,"file[] %s: copy to %s ok", fullname, tmp2); } #endif /* CKSYSLOG */ return(rc); } /* Z S A T T R */ /* Fills in a Kermit file attribute structure for the file which is to be sent. Returns 0 on success with the structure filled in, or -1 on failure. If any string member is null, then it should be ignored. If any numeric member is -1, then it should be ignored. */ #ifdef CK_PERMS #ifdef CK_GPERMS #undef CK_GPERMS #endif /* CK_GPERMS */ #ifdef UNIX #ifndef S_IRUSR #define S_IRUSR 0400 #endif /* S_IRUSR */ #ifndef S_IWUSR #define S_IXUSR 0200 #endif /* S_IWUSR */ #ifndef S_IXUSR #define S_IXUSR 0100 #endif /* S_IXUSR */ #endif /* UNIX */ #ifdef S_IRUSR #ifdef S_IWUSR #ifdef S_IXUSR #define CK_GPERMS #endif /* S_IXUSR */ #endif /* S_IWUSR */ #endif /* S_IRUSR */ static char gperms[2]; #endif /* CK_GPERMS */ static char lperms[24]; #ifdef CK_PERMS static char xlperms[24]; /* Z S E T P E R M -- Set permissions of a file */ int zsetperm(f,code) char * f; int code; { int x; #ifdef CK_SCO32V4 mode_t mask; #else int mask; #endif /* CK_SCO32V4 */ mask = code; if (inserver && guest) { debug(F110,"zsetperm guest",f,0); return(0); } x = chmod(f,mask); if (x < 0) { debug(F111,"zsetperm error",f,errno); return(0); } debug(F111,"zsetperm ok",f,mask); return(1); } /* Z G P E R M -- Get permissions of a file as an octal string */ char * zgperm(f) char *f; { extern int diractive; int x; char *s = (char *)xlperms; struct stat buf; debug(F110,"zgperm",f,0); if (!f) return("----------"); if (!*f) return("----------"); #ifdef CKROOT debug(F111,"zgperm setroot",ckroot,ckrootset); if (ckrootset) if (!zinroot(f)) { debug(F110,"zgperm setroot violation",f,0); return("----------"); } #endif /* CKROOT */ #ifdef USE_LSTAT if (diractive) x = lstat(f,&buf); else #endif /* USE_LSTAT */ x = stat(f,&buf); debug(F101,"STAT","",9); if (x < 0) return("----------"); sprintf(s,"%o",buf.st_mode); debug(F110,"zgperm",s,0); return(s); } /* Like zgperm() but returns permissions in "ls -l" string format */ static char xsperms[24]; char * ziperm(f) char * f; { extern int diractive; int x; char *s = (char *)xsperms; struct stat buf; unsigned int perms = 0; debug(F110,"ziperm",f,0); if (!f) return(NULL); if (!*f) return(NULL); if (diractive && zgfs_mode != 0) { perms = zgfs_mode; /* zgetfs() already got them */ } else { #ifdef USE_LSTAT if (diractive) x = lstat(f,&buf); else #endif /* USE_LSTAT */ x = stat(f,&buf); debug(F101,"STAT","",10); if (x < 0) return("----------"); perms = buf.st_mode; } switch (perms & S_IFMT) { case S_IFDIR: *s++ = 'd'; break; case S_IFCHR: /* Character special */ *s++ = 'c'; break; case S_IFBLK: /* Block special */ *s++ = 'b'; break; case S_IFREG: /* Regular */ *s++ = '-'; break; #ifdef S_IFLNK case S_IFLNK: /* Symbolic link */ *s++ = 'l'; break; #endif /* S_IFLNK */ #ifdef S_IFSOCK case S_IFSOCK: /* Socket */ *s++ = 's'; break; #endif /* S_IFSOCK */ #ifdef S_IFIFO #ifndef Plan9 #ifndef COHERENT case S_IFIFO: /* FIFO */ *s++ = 'p'; break; #endif /* COHERENT */ #endif /* Plan9 */ #endif /* S_IFIFO */ #ifdef S_IFWHT case S_IFWHT: /* Whiteout */ *s++ = 'w'; break; #endif /* S_IFWHT */ default: /* Unknown */ *s++ = '?'; break; } if (perms & S_IRUSR) /* Owner's permissions */ *s++ = 'r'; else *s++ = '-'; if (perms & S_IWUSR) *s++ = 'w'; else *s++ = '-'; switch (perms & (S_IXUSR | S_ISUID)) { case 0: *s++ = '-'; break; case S_IXUSR: *s++ = 'x'; break; case S_ISUID: *s++ = 'S'; break; case S_IXUSR | S_ISUID: *s++ = 's'; break; } if (perms & S_IRGRP) /* Group permissions */ *s++ = 'r'; else *s++ = '-'; if (perms & S_IWGRP) *s++ = 'w'; else *s++ = '-'; switch (perms & (S_IXGRP | S_ISGID)) { case 0: *s++ = '-'; break; case S_IXGRP: *s++ = 'x'; break; case S_ISGID: *s++ = 'S'; break; case S_IXGRP | S_ISGID: *s++ = 's'; break; } if (perms & S_IROTH) /* World permissions */ *s++ = 'r'; else *s++ = '-'; if (perms & S_IWOTH) *s++ = 'w'; else *s++ = '-'; switch ( #ifdef Plan9 perms & (S_IXOTH) #else perms & (S_IXOTH | S_ISVTX) #endif ) { case 0: *s++ = '-'; break; case S_IXOTH: *s++ = 'x'; break; #ifndef Plan9 case S_ISVTX: *s++ = 'T'; break; case S_IXOTH | S_ISVTX: *s++ = 't'; break; #endif /* Plan9 */ } *s = '\0'; debug(F110,"ziperm",xsperms,0); return((char *)xsperms); } #else char * zgperm(f) char *f; { return("----------"); } char * ziperms(f) char *f; { return("----------"); } #endif /* CK_PERMS */ int zsattr(xx) struct zattr *xx; { CK_OFF_T k; int x; struct stat buf; k = iflen % 1024; /* File length in K */ if (k) k = 1L; xx->lengthk = (iflen / 1024) + k; xx->type.len = 0; /* File type can't be filled in here */ xx->type.val = ""; if (*nambuf) { xx->date.val = zfcdat(nambuf); /* File creation date */ xx->date.len = (int)strlen(xx->date.val); } else { xx->date.len = 0; xx->date.val = ""; } xx->creator.len = 0; /* File creator */ xx->creator.val = ""; xx->account.len = 0; /* File account */ xx->account.val = ""; xx->area.len = 0; /* File area */ xx->area.val = ""; xx->password.len = 0; /* Area password */ xx->password.val = ""; xx->blksize = -1L; /* File blocksize */ xx->xaccess.len = 0; /* File access */ xx->xaccess.val = ""; xx->encoding.len = 0; /* Transfer syntax */ xx->encoding.val = 0; xx->disp.len = 0; /* Disposition upon arrival */ xx->disp.val = ""; xx->lprotect.len = 0; /* Local protection */ xx->lprotect.val = ""; xx->gprotect.len = 0; /* Generic protection */ xx->gprotect.val = ""; x = -1; if (*nambuf) x = stat(nambuf,&buf); debug(F101,"STAT","",11); if (x >= 0) { debug(F111,"zsattr buf.st_mode & 0777",nambuf,buf.st_mode & 0777); /* UNIX filemode as an octal string without filetype bits */ sprintf(lperms,"%o",buf.st_mode & 0777); xx->lprotect.len = (int)strlen(lperms); xx->lprotect.val = (char *)lperms; x = 0; #ifdef CK_GPERMS /* Generic permissions only if we have stat.h symbols defined */ if (buf.st_mode & S_IRUSR) x |= 1; /* Read */ if (buf.st_mode & S_IWUSR) x |= (2+16); /* Write and Delete */ if (buf.st_mode & S_IXUSR) x |= 4; /* Execute */ gperms[0] = tochar(x); gperms[1] = NUL; xx->gprotect.len = 1; xx->gprotect.val = (char *)gperms; #endif /* CK_GPERMS */ } debug(F111,"zsattr lperms",xx->lprotect.val,xx->lprotect.len); debug(F111,"zsattr gperms",xx->gprotect.val,xx->gprotect.len); xx->systemid.val = "U1"; /* U1 = UNIX */ xx->systemid.len = 2; /* System ID */ xx->recfm.len = 0; /* Record format */ xx->recfm.val = ""; xx->sysparam.len = 0; /* System-dependent parameters */ xx->sysparam.val = ""; xx->length = iflen; /* Length */ return(0); } /* Z F C D A T -- Get file creation date */ /* Call with pointer to filename. On success, returns pointer to modification date in yyyymmdd hh:mm:ss format. On failure, returns pointer to null string. */ static char datbuf[40]; char * #ifdef CK_ANSIC zdtstr(time_t timearg) #else zdtstr(timearg) time_t timearg; #endif /* CK_ANSIC */ /* zdtstr */ { #ifndef TIMESTAMP return(""); #else struct tm * time_stamp; struct tm * localtime(); int yy, ss; debug(F101,"zdtstr timearg","",timearg); if (timearg < 0) return(""); time_stamp = localtime(&(timearg)); if (!time_stamp) { debug(F100,"localtime returns null","",0); return(""); } /* We assume that tm_year is ALWAYS years since 1900. Any platform where this is not the case will have problems starting in 2000. */ yy = time_stamp->tm_year; /* Year - 1900 */ debug(F101,"zdtstr tm_year","",time_stamp->tm_year); if (yy > 1000) { debug(F101,"zstrdt YEAR-2000 ALERT 1: localtime year","",yy); } yy += 1900; debug(F101,"zdatstr year","",yy); if (time_stamp->tm_mon < 0 || time_stamp->tm_mon > 11) return(""); if (time_stamp->tm_mday < 0 || time_stamp->tm_mday > 31) return(""); if (time_stamp->tm_hour < 0 || time_stamp->tm_hour > 23) return(""); if (time_stamp->tm_min < 0 || time_stamp->tm_min > 59) return(""); ss = time_stamp->tm_sec; /* Seconds */ if (ss < 0 || ss > 59) /* Some systems give a BIG number */ ss = 0; sprintf(datbuf, #ifdef pdp11 /* For some reason, 2.1x BSD sprintf gets the last field wrong. */ "%04d%02d%02d %02d:%02d:00", #else "%04d%02d%02d %02d:%02d:%02d", #endif /* pdp11 */ yy, time_stamp->tm_mon + 1, time_stamp->tm_mday, time_stamp->tm_hour, time_stamp->tm_min #ifndef pdp11 , ss #endif /* pdp11 */ ); yy = (int)strlen(datbuf); debug(F111,"zdatstr",datbuf,yy); if (yy > 17) datbuf[17] = '\0'; return(datbuf); #endif /* TIMESTAMP */ } char * zfcdat(name) char *name; { #ifdef TIMESTAMP struct stat buffer; extern int diractive; unsigned int mtime; int x; char * s; if (!name) return(""); s = name; if (!*s) return(""); #ifdef CKROOT debug(F111,"zfcdat setroot",ckroot,ckrootset); if (ckrootset) if (!zinroot(name)) { debug(F110,"zfcdat setroot violation",name,0); return(""); } #endif /* CKROOT */ #ifdef DTILDE if (*s == '~') { s = tilde_expand(s); if (!s) s = ""; if (!*s) s = name; } #endif /* DTILDE */ datbuf[0] = '\0'; x = 0; debug(F111,"zfcdat",s,diractive); if (diractive && zgfs_mtime) { mtime = zgfs_mtime; } else { #ifdef USE_LSTAT if (diractive) { x = lstat(s,&buffer); debug(F101,"STAT","",12); debug(F101,"zfcdat lstat","",x); } else { #endif /* USE_LSTAT */ x = stat(s,&buffer); debug(F101,"STAT","",13); debug(F101,"zfcdat stat","",x); #ifdef USE_LSTAT } #endif /* USE_LSTAT */ if (x != 0) { #ifdef USE_LSTAT debug(F111,"zfcdat stat failed",s,errno); #else debug(F111,"zfcdat lstat failed",s,errno); #endif /* USE_LSTAT */ return(""); } debug(F101,"zfcdat buffer.st_mtime","",buffer.st_mtime); mtime = buffer.st_mtime; } return(zdtstr(mtime)); #else return(""); #endif /* TIMESTAMP */ } #ifndef NOTIMESTAMP /* Z S T R D T -- Converts local date string to internal representation */ /* In our case (UNIX) this is seconds since midnite 1 Jan 1970 UTC, suitable for comparison with UNIX file dates. As far as I know, there is no library or system call -- at least nothing reasonably portable -- to convert local time to UTC. */ time_t zstrdt(date,len) char * date; int len; { #ifdef M_UNIX /* SCO UNIX 3.2v2.0 and ODT 2.0 lack prototypes for ftime(). ODT 3.0 (3.2v4.2 OS) has a prototype, which may vary in dependence on the XPG4 supplement presence. So always use what the system header file supplies in ODT 3.0... */ #ifndef ODT30 #ifndef _SCO_DS extern void ftime(); /* extern void ftime(struct timeb *) */ #endif /* _SCO_DS */ #endif /* ODT30 */ #else #ifndef M_XENIX extern int ftime(); #endif /* M_XENIX */ #endif /* M_UNIX */ extern struct tm * localtime(); /* And this should have been declared always through a header file */ #ifdef HPUX10 time_t tmx; long days; #else #ifdef BSD44 time_t tmx; long days; #else long tmx, days; #endif /* BSD44 */ #endif /* HPUX10 */ int i, n, isleapyear; /* J F M A M J J A S O N D */ /* 31 28 31 30 31 30 31 31 30 31 30 31 */ static int monthdays [13] = { 0,0,31,59,90,120,151,181,212,243,273,304,334 }; char s[5]; struct tm *time_stamp; #ifdef BSD44 struct timeval tp[2]; long xtimezone = 0L; #else #ifdef V7 struct utimbuf { time_t timep[2]; /* New access and modificaton time */ } tp; char *tz; long timezone; /* In case timezone not defined in .h file */ #else #ifdef SYSUTIMEH struct utimbuf tp; #else struct utimbuf { time_t atime; time_t mtime; } tp; #endif /* SYSUTIMEH */ #endif /* V7 */ #endif /* BSD44 */ #ifdef ANYBSD long timezone = 0L; static struct timeb tbp; #endif /* ANYBSD */ #ifdef BEBOX long timezone = 0L; #endif /* BEBOX */ debug(F111,"zstrdt",date,len); if ((len == 0) || (len != 17) || (date[8] != ' ') || (date[11] != ':') || (date[14] != ':') ) { debug(F111,"Bad creation date ",date,len); return(-1); } debug(F111,"zstrdt date check 1",date,len); for(i = 0; i < 8; i++) { if (!isdigit(date[i])) { debug(F111,"Bad creation date ",date,len); return(-1); } } debug(F111,"zstrdt date check 2",date,len); i++; for (; i < 16; i += 3) { if ((!isdigit(date[i])) || (!isdigit(date[i + 1]))) { debug(F111,"Bad creation date ",date,len); return(-1); } } debug(F111,"zstrdt date check 3",date,len); #ifdef COMMENT /* was BSD44 */ /* man gettimeofday on BSDI 3.1 says: "The timezone field is no longer used; timezone information is stored out- side the kernel. See ctime(3) for more information." So this chunk of code is effectively a no-op, at least in BSDI 3.x. */ { int x; struct timezone tzp; x = gettimeofday(NULL, &tzp); debug(F101,"zstrdt BSD44 gettimeofday","",x); if (x > -1) xtimezone = tzp.tz_minuteswest * 60L; else xtimezone = 0L; debug(F101,"zstrdt BSD44 timezone","",xtimezone); } #else #ifdef ANYBSD debug(F100,"zstrdt BSD calling ftime","",0); ftime(&tbp); debug(F100,"zstrdt BSD back from ftime","",0); timezone = tbp.timezone * 60L; debug(F101,"zstrdt BSD timezone","",timezone); #else #ifdef SVORPOSIX tzset(); /* Set timezone */ #else #ifdef V7 if ((tz = getenv("TZ")) == NULL) timezone = 0; /* UTC/GMT */ else timezone = atoi(&tz[3]); /* Set 'timezone'. */ timezone *= 60L; #endif /* V7 */ #endif /* SVORPOSIX */ #endif /* ANYBSD */ #endif /* COMMENT (was BSD44) */ debug(F100,"zstrdt so far so good","",0); s[4] = '\0'; for (i = 0; i < 4; i++) /* Fix the year */ s[i] = date[i]; n = atoi(s); debug(F111,"zstrdt year",s,n); if (n < 1970) { debug(F100,"zstrdt fails - year","",n); return(-1); } /* Previous year's leap days. This won't work after year 2100. */ isleapyear = (( n % 4 == 0 && n % 100 !=0) || n % 400 == 0); days = (long) (n - 1970) * 365; days += (n - 1968 - 1) / 4 - (n - 1900 - 1) / 100 + (n - 1600 - 1) / 400; s[2] = '\0'; for (i = 4; i < 16; i += 2) { s[0] = date[i]; s[1] = date[i + 1]; n = atoi(s); switch (i) { case 4: /* MM: month */ if ((n < 1 ) || ( n > 12)) { debug(F111,"zstrdt 4 bad date ",date,len); return(-1); } days += monthdays [n]; if (isleapyear && n > 2) ++days; continue; case 6: /* DD: day */ if ((n < 1 ) || ( n > 31)) { debug(F111,"zstrdt 6 bad date ",date,len); return(-1); } tmx = (days + n - 1) * 24L * 60L * 60L; i++; /* Skip the space */ continue; case 9: /* hh: hour */ if ((n < 0 ) || ( n > 23)) { debug(F111,"zstrdt 9 bad date ",date,len); return(-1); } tmx += n * 60L * 60L; i++; /* Skip the colon */ continue; case 12: /* mm: minute */ if ((n < 0 ) || ( n > 59)) { debug(F111,"zstrdt 12 bad date ",date,len); return(-1); } #ifdef COMMENT /* (was BSD44) */ /* Correct for time zone */ tmx += xtimezone; debug(F101,"zstrdt BSD44 tmx","",tmx); #else #ifdef ANYBSD tmx += timezone; #else #ifndef CONVEX9 /* Don't yet know how to do this here */ #ifdef ultrix tmx += (long) timezone; #else #ifdef Plan9 { extern time_t tzoffset; tmx += tzoffset; } #else #ifndef BSD44 #ifndef NOTIMEZONE tmx += timezone; #endif /* NOTIMEZONE */ #endif /* BSD44 */ #endif /* Plan9 */ #endif /* ultrix */ #endif /* CONVEX9 */ #endif /* ANYBSD */ #endif /* COMMENT (was BSD44) */ tmx += n * 60L; i++; /* Skip the colon */ continue; case 15: /* ss: second */ if ((n < 0 ) || ( n > 59)) { debug(F111,"zstrdt 15 bad date ",date,len); return(-1); } tmx += n; } time_stamp = localtime(&tmx); debug(F101,"zstrdt tmx 1","",tmx); if (!time_stamp) return(-1); #ifdef COMMENT /* Why was this here? */ time_stamp = localtime(&tmx); debug(F101,"zstrdt tmx 2","",tmx); #endif /* COMMENT */ #ifdef BSD44 { /* New to 7.0 - Works in at at least BSDI 3.1 and FreeBSD 2.2.7 */ long zz; zz = time_stamp->tm_gmtoff; /* Seconds away from Zero Meridian */ debug(F101,"zstrdt BSD44 tm_gmtoff","",zz); tmx -= zz; debug(F101,"zstrdt BSD44 tmx 3 (GMT)","",tmx); } #else /* Daylight Savings Time adjustment. Do this everywhere BUT in BSD44 because in BSD44, tm_gmtoff also includes the DST adjustment. */ if (time_stamp->tm_isdst) { tmx -= 60L * 60L; debug(F101,"zstrdt tmx 3 (DST)","",tmx); } #endif /* BSD44 */ n = time_stamp->tm_year; if (n < 300) { n += 1900; } } return(tmx); } #ifdef ZLOCALTIME /* Z L O C A L T I M E -- GMT/UTC time string to local time string */ /* Call with: "yyyymmdd hh:mm:ss" GMT/UTC date-time. Returns: "yyyymmdd hh:mm:ss" local date-time on success, NULL on failure. */ static char zltimbuf[64]; char * zlocaltime(gmtstring) char * gmtstring; { #ifdef M_UNIX /* SCO UNIX 3.2v2.0 and ODT 2.0 lack prototypes for ftime(). ODT 3.0 (3.2v4.2 OS) has a prototype, which may vary in dependence on the XPG4 supplement presence. So always use what the system header file supplies in ODT 3.0... */ #ifndef ODT30 #ifndef _SCO_DS extern void ftime(); /* extern void ftime(struct timeb *) */ #endif /* _SCO_DS */ #endif /* ODT30 */ #else #ifndef M_XENIX extern int ftime(); #endif /* M_XENIX */ #endif /* M_UNIX */ extern struct tm * localtime(); /* And this should have been declared always through a header file */ #ifdef HPUX10 time_t tmx; long days; #else #ifdef BSD44 time_t tmx; long days; #else long tmx, days; #endif /* BSD44 */ #endif /* HPUX10 */ int i, n, x, isleapyear; /* J F M A M J J A S O N D */ /* 31 28 31 30 31 30 31 31 30 31 30 31 */ static int monthdays [13] = { 0,0,31,59,90,120,151,181,212,243,273,304,334 }; char s[5]; struct tm *time_stamp; #ifdef BSD44 struct timeval tp[2]; #else #ifdef V7 struct utimbuf { time_t timep[2]; /* New access and modificaton time */ } tp; #else #ifdef SYSUTIMEH struct utimbuf tp; #else struct utimbuf { time_t atime; time_t mtime; } tp; #endif /* SYSUTIMEH */ #endif /* V7 */ #endif /* BSD44 */ #ifdef ANYBSD static struct timeb tbp; #endif /* ANYBSD */ char * date = gmtstring; int len; len = strlen(date); debug(F111,"zlocaltime",date,len); if ((len == 0) || (len != 17) || (date[8] != ' ') || (date[11] != ':') || (date[14] != ':') ) { debug(F111,"Bad creation date ",date,len); return(NULL); } debug(F111,"zlocaltime date check 1",date,len); for(i = 0; i < 8; i++) { if (!isdigit(date[i])) { debug(F111,"Bad creation date ",date,len); return(NULL); } } debug(F111,"zlocaltime date check 2",date,len); i++; for (; i < 16; i += 3) { if ((!isdigit(date[i])) || (!isdigit(date[i + 1]))) { debug(F111,"Bad creation date ",date,len); return(NULL); } } debug(F111,"zlocaltime date check 3",date,len); debug(F100,"zlocaltime so far so good","",0); s[4] = '\0'; for (i = 0; i < 4; i++) /* Fix the year */ s[i] = date[i]; n = atoi(s); debug(F111,"zlocaltime year",s,n); if (n < 1970) { debug(F100,"zlocaltime fails - year","",n); return(NULL); } /* Previous year's leap days. This won't work after year 2100. */ isleapyear = (( n % 4 == 0 && n % 100 !=0) || n % 400 == 0); days = (long) (n - 1970) * 365; days += (n - 1968 - 1) / 4 - (n - 1900 - 1) / 100 + (n - 1600 - 1) / 400; s[2] = '\0'; for (i = 4; i < 16; i += 2) { s[0] = date[i]; s[1] = date[i + 1]; n = atoi(s); switch (i) { case 4: /* MM: month */ if ((n < 1 ) || ( n > 12)) { debug(F111,"zlocaltime 4 bad date ",date,len); return(NULL); } days += monthdays [n]; if (isleapyear && n > 2) ++days; continue; case 6: /* DD: day */ if ((n < 1 ) || ( n > 31)) { debug(F111,"zlocaltime 6 bad date ",date,len); return(NULL); } tmx = (days + n - 1) * 24L * 60L * 60L; i++; /* Skip the space */ continue; case 9: /* hh: hour */ if ((n < 0 ) || ( n > 23)) { debug(F111,"zlocaltime 9 bad date ",date,len); return(NULL); } tmx += n * 60L * 60L; i++; /* Skip the colon */ continue; case 12: /* mm: minute */ if ((n < 0 ) || ( n > 59)) { debug(F111,"zlocaltime 12 bad date ",date,len); return(NULL); } tmx += n * 60L; i++; /* Skip the colon */ continue; case 15: /* ss: second */ if ((n < 0 ) || ( n > 59)) { debug(F111,"zlocaltime 15 bad date ",date,len); return(NULL); } tmx += n; } /* At this point tmx is the time_t representation of the argument date-time string without any timezone or DST adjustments. Therefore it should be the same as the time_t representation of the GMT/UTC time. Now we should be able to feed it to localtime() and have it converted to a struct tm representing the local time equivalent of the given UTC time. */ time_stamp = localtime(&tmx); if (!time_stamp) return(NULL); } /* Now we simply reformat the struct tm to a string */ x = time_stamp->tm_year; if (time_stamp->tm_year < 70 || time_stamp->tm_year > 8099) return(NULL); if (time_stamp->tm_mon < 0 || time_stamp->tm_mon > 11) return(NULL); if (time_stamp->tm_mday < 1 || time_stamp->tm_mday > 31) return(NULL); if (time_stamp->tm_hour < 0 || time_stamp->tm_hour > 24) return(NULL); if (time_stamp->tm_min < 0 || time_stamp->tm_min > 60) return(NULL); if (time_stamp->tm_sec < 0 || time_stamp->tm_sec > 60) return(NULL); sprintf(zltimbuf,"%04d%02d%02d %02d:%02d:%02d", time_stamp->tm_year + 1900, time_stamp->tm_mon + 1, time_stamp->tm_mday, time_stamp->tm_hour, time_stamp->tm_min, time_stamp->tm_sec ); return((char *)zltimbuf); } #endif /* ZLOCALTIME */ #endif /* NOTIMESTAMP */ /* Z S T I M E -- Set modification date/time+permissions for incoming file */ /* Call with: f = pointer to name of existing file. yy = pointer to a Kermit file attribute structure in which yy->date.val is a date of the form yyyymmdd hh:mm:ss, e.g. 19900208 13:00:00. yy->lprotect.val & yy->gprotect.val are permission/protection values. x = is a function code: 0 means to set the file's attributes as given. 1 means compare the date in struct yy with the file creation date. Returns: -1 on any kind of error. 0 if x is 0 and the attributes were set successfully. 0 if x is 1 and date from attribute structure <= file creation date. 1 if x is 1 and date from attribute structure > file creation date. */ int zstime(f,yy,x) char *f; struct zattr *yy; int x; /* zstime */ { int r = -1; /* Return code */ #ifdef CK_PERMS int setperms = 0; #endif /* CK_PERMS */ int setdate = 0; /* It is ifdef'd TIMESTAMP because it might not work on V7. bk@kullmar.se. */ #ifdef TIMESTAMP #ifdef BSD44 extern int utimes(); #else extern int utime(); #endif /* BSD44 */ struct stat sb; /* At least, the declarations for int functions are not needed anyway */ #ifdef BSD44 struct timeval tp[2]; long xtimezone; #else #ifdef V7 struct utimbuf { time_t timep[2]; /* New access and modificaton time */ } tp; char *tz; long timezone; /* In case not defined in .h file */ #else #ifdef SYSUTIMEH struct utimbuf tp; #else struct utimbuf { time_t atime; time_t mtime; } tp; #endif /* SYSUTIMEH */ #endif /* V7 */ #endif /* BSD44 */ long tm = 0L; if (!f) f = ""; if (!*f) return(-1); if (!yy) return(-1); #ifdef CKROOT debug(F111,"zstime setroot",ckroot,ckrootset); if (ckrootset) if (!zinroot(f)) { debug(F110,"zstime setroot violation",f,0); return(0); } #endif /* CKROOT */ if (yy->date.len == 0) { /* No date in struct */ if (yy->lprotect.len != 0) { /* So go do permissions */ goto zsperms; } else { debug(F100,"zstime: nothing to do","",0); return(0); } } if ((tm = zstrdt(yy->date.val,yy->date.len)) < 0) { debug(F101,"zstime: zstrdt fails","",0); return(-1); } debug(F101,"zstime: tm","",tm); debug(F111,"zstime: A-pkt date ok ",yy->date.val,yy->date.len); if (stat(f,&sb)) { /* Get the time for the file */ debug(F101,"STAT","",14); debug(F111,"zstime: Can't stat file:",f,errno); return(-1); } debug(F101,"STAT","",15); setdate = 1; zsperms: #ifdef CK_PERMS { int i, x = 0, xx, flag = 0; char * s; #ifdef DEBUG char obuf[24]; if (deblog) { debug(F111,"zstime lperms",yy->lprotect.val,yy->lprotect.len); debug(F111,"zstime gperms",yy->gprotect.val,yy->gprotect.len); debug(F110,"zstime system id",yy->systemid.val,0); sprintf(obuf,"%o",sb.st_mode); debug(F110,"zstime file perms before",obuf,0); } #endif /* DEBUG */ #ifdef CK_LOGIN debug(F101,"zstime isguest","",isguest); debug(F101,"zstime ckxperms","",ckxperms); if (isguest) { #ifdef COMMENT /* Clear owner permissions */ sb.st_mode &= (unsigned) 0177077; /* (16 bits) */ #else /* Set permissions from ckxperms variable */ sb.st_mode = ckxperms; #endif /* COMMENT */ debug(F101,"zstime isguest sb.st_mode","",sb.st_mode); #ifdef COMMENT /* We already set them in zopeno() */ setperms = 1; #endif /* COMMENT */ flag = 0; } else #endif /* CK_LOGIN */ if ((yy->lprotect.len > 0 && /* Have local-format permissions */ yy->systemid.len > 0 && /* from A-packet... */ #ifdef UNIX !strcmp(yy->systemid.val,"U1") /* AND you are same as me */ #else 0 #endif /* UNIX */ ) || (yy->lprotect.len < 0) /* OR by inheritance from old file */ ) { flag = 1; s = yy->lprotect.val; /* UNIX filemode */ xx = yy->lprotect.len; if (xx < 0) /* len < 0 means inheritance */ xx = 0 - xx; for (i = 0; i < xx; i++) { /* Decode octal string */ if (*s <= '7' && *s >= '0') { x = 8 * x + (int)(*s) - '0'; } else { flag = 0; break; } s++; } #ifdef DEBUG sprintf(obuf,"%o",x); debug(F110,"zstime octal lperm",obuf,0); #endif /* DEBUG */ } else if (!flag && yy->gprotect.len > 0) { int g; #ifdef CK_SCO32V4 mode_t mask; #else int mask; #endif /* CK_SCO32V4 */ mask = umask(0); /* Get umask */ debug(F101,"zstime mask 1","",mask); umask(mask); /* Put it back */ mask ^= 0777; /* Flip the bits */ debug(F101,"zstime mask 2","",mask); g = xunchar(*(yy->gprotect.val)); /* Decode generic protection */ debug(F101,"zstime gprotect","",g); #ifdef S_IRUSR debug(F100,"zstime S_IRUSR","",0); if (g & 1) x |= S_IRUSR; /* Read permission */ flag = 1; #endif /* S_IRUSR */ #ifdef S_IWUSR debug(F100,"zstime S_IWUSR","",0); if (g & 2) x |= S_IWUSR; /* Write permission */ if (g & 16) x |= S_IWUSR; /* Delete permission */ flag = 1; #endif /* S_IWUSR */ #ifdef S_IXUSR debug(F100,"zstime S_IXUSR","",0); if (g & 4) /* Has execute permission bit */ x |= S_IXUSR; else /* Doesn't have it */ mask &= 0666; /* so also clear it out of mask */ flag = 1; #endif /* S_IXUSR */ debug(F101,"zstime mask x","",x); x |= mask; debug(F101,"zstime mask x|mask","",x); } debug(F101,"zstime flag","",flag); if (flag) { #ifdef S_IFMT debug(F101,"zstime S_IFMT x","",x); sb.st_mode = (sb.st_mode & S_IFMT) | x; setperms = 1; #else #ifdef _IFMT debug(F101,"zstime _IFMT x","",x); sb.st_mode = (sb.st_mode & _IFMT) | x; setperms = 1; #endif /* _IFMT */ #endif /* S_IFMT */ } #ifdef DEBUG sprintf(obuf,"%04o",sb.st_mode); debug(F111,"zstime file perms after",obuf,setperms); #endif /* DEBUG */ } #endif /* CK_PERMS */ debug(F101,"zstime: sb.st_atime","",sb.st_atime); #ifdef BSD44 tp[0].tv_sec = sb.st_atime; /* Access time first */ tp[1].tv_sec = tm; /* Update time second */ debug(F100,"zstime: BSD44 modtime","",0); #else #ifdef V7 tp.timep[0] = tm; /* Set modif. time to creation date */ tp.timep[1] = sb.st_atime; /* Don't change the access time */ debug(F100,"zstime: V7 modtime","",0); #else #ifdef SYSUTIMEH tp.modtime = tm; /* Set modif. time to creation date */ tp.actime = sb.st_atime; /* Don't change the access time */ debug(F100,"zstime: SYSUTIMEH modtime","",0); #else tp.mtime = tm; /* Set modif. time to creation date */ tp.atime = sb.st_atime; /* Don't change the access time */ debug(F100,"zstime: default modtime","",0); #endif /* SYSUTIMEH */ #endif /* V7 */ #endif /* BSD44 */ switch (x) { /* Execute desired function */ case 0: /* Set the creation date of the file */ #ifdef CK_PERMS /* And permissions */ /* NOTE: If we are inheriting permissions from a previous file, and the previous file was a directory, this would turn the new file into a directory too, but it's not, so we try to unset the right bit. Luckily, this code will probably never be executed since the upper level modules do not allow reception of a file that has the same name as a directory. NOTE 2: We change the permissions *before* we change the modification time, otherwise changing the permissions would set the mod time to the present time. */ { int x; debug(F101,"zstime setperms","",setperms); if (S_ISDIR(sb.st_mode)) { debug(F101,"zstime DIRECTORY bit on","",sb.st_mode); sb.st_mode ^= 0040000; debug(F101,"zstime DIRECTORY bit off","",sb.st_mode); } if (setperms) { x = chmod(f,sb.st_mode); debug(F101,"zstime chmod","",x); } } if (x < 0) return(-1); #endif /* CK_PERMS */ if (!setdate) /* We don't have a date */ return(0); /* so skip the following... */ if ( #ifdef BSD44 utimes(f,tp) #else utime(f,&tp) #endif /* BSD44 */ ) { /* Fix modification time */ debug(F111,"zstime 0: can't set modtime for file",f,errno); r = -1; } else { /* Including the modtime here is not portable */ debug(F110,"zstime 0: modtime set for file",f,0); r = 0; } break; case 1: /* Compare the dates */ /* This was st_atime, which was wrong. We want the file-data modification time, st_mtime. */ debug(F111,"zstime 1: compare",f,sb.st_mtime); debug(F111,"zstime 1: compare","packet",tm); r = (sb.st_mtime < tm) ? 0 : 1; break; default: /* Error */ r = -1; } #endif /* TIMESTAMP */ return(r); } /* Find initialization file. */ #ifdef NOTUSED int zkermini() { /* nothing here for Unix. This function added for benefit of VMS Kermit. */ return(0); } #endif /* NOTUSED */ #ifndef UNIX /* Historical -- not used in Unix any more (2001-11-03) */ #ifndef NOFRILLS int zmail(p,f) char *p; char *f; { /* Send file f as mail to address p */ /* Returns 0 on success 2 if mail delivered but temp file can't be deleted -2 if mail can't be delivered -1 on file access error The UNIX version always returns 0 because it can't get a good return code from zsyscmd. */ int n; #ifdef CK_LOGIN if (isguest) return(-2); #endif /* CK_LOGIN */ if (!f) f = ""; if (!*f) return(-1); #ifdef CKROOT debug(F111,"zmail setroot",ckroot,ckrootset); if (ckrootset) if (!zinroot(f)) { debug(F110,"zmail setroot violation",f,0); return(-1); } #endif /* CKROOT */ #ifdef BSD4 /* The idea is to use /usr/ucb/mail, rather than regular mail, so that */ /* a subject line can be included with -s. Since we can't depend on the */ /* user's path, we use the convention that /usr/ucb/Mail = /usr/ucb/mail */ /* and even if Mail has been moved to somewhere else, this should still */ /* find it... The search could be made more reliable by actually using */ /* access() to see if /usr/ucb/Mail exists. */ n = strlen(f); n = n + n + 15 + (int)strlen(p); if (n > ZMBUFLEN) return(-2); #ifdef DGUX540 sprintf(zmbuf,"mailx -s %c%s%c %s < %s", '"', f, '"', p, f); #else sprintf(zmbuf,"Mail -s %c%s%c %s < %s", '"', f, '"', p, f); #endif /* DGUX540 */ zsyscmd(zmbuf); #else #ifdef SVORPOSIX #ifndef OXOS sprintf(zmbuf,"mail %s < %s", p, f); #else /* OXOS */ sprintf(zmbuf,"mailx -s %c%s%c %s < %s", '"', f, '"', p, f); #endif /* OXOS */ zsyscmd(zmbuf); #else *zmbuf = '\0'; #endif #endif return(0); } #endif /* NOFRILLS */ #endif /* UNIX */ #ifndef NOFRILLS int zprint(p,f) char *p; char *f; { /* Print file f with options p */ extern char * printername; /* From ckuus3.c */ extern int printpipe; int n; #ifdef CK_LOGIN if (isguest) return(-2); #endif /* CK_LOGIN */ if (!f) f = ""; if (!*f) return(-1); #ifdef CKROOT debug(F111,"zprint setroot",ckroot,ckrootset); if (ckrootset) if (!zinroot(f)) { debug(F110,"zprint setroot violation",f,0); return(-1); } #endif /* CKROOT */ debug(F110,"zprint file",f,0); debug(F110,"zprint flags",p,0); debug(F110,"zprint printername",printername,0); debug(F101,"zprint printpipe","",printpipe); #ifdef UNIX /* Note use of standard input redirection. In some systems, lp[r] runs setuid to lp (or ...?), so if user has sent a file into a directory that lp does not have read access to, it can't be printed unless it is fed to lp[r] as standard input. */ if (printpipe && printername) { n = 8 + (int)strlen(f) + (int)strlen(printername); if (n > ZMBUFLEN) return(-2); sprintf(zmbuf,"cat %s | %s", f, printername); } else if (printername) { n = 8 + (int)strlen(f) + (int)strlen(printername); if (n > ZMBUFLEN) return(-2); sprintf(zmbuf,"cat %s >> %s", f, printername); } else { n = 4 + (int)strlen(PRINTCMD) + (int)strlen(p) + (int)strlen(f); if (n > ZMBUFLEN) return(-2); sprintf(zmbuf,"%s %s < %s", PRINTCMD, p, f); } debug(F110,"zprint command",zmbuf,0); zsyscmd(zmbuf); #else /* Not UNIX */ *zmbuf = '\0'; #endif /* UNIX */ return(0); } #endif /* NOFRILLS */ /* Wildcard expansion functions... */ static char scratch[MAXPATH+4]; /* Used by both methods */ static int oldmtchs = 0; /* Let shell (ls) expand them. */ #ifdef COMMENT static char *lscmd = "/bin/ls -d"; /* Command to use. */ #else static char *lscmd = "echo"; /* Command to use. */ #endif /* COMMENT */ #ifndef NOPUSH int shxpand(pat,namlst,len) char *pat, *namlst[]; int len; { char *fgbuf = NULL; /* Buffer for forming ls command */ char *p, *q; /* Workers */ int i, x, retcode, itsadir; char c; x = (int)strlen(pat) + (int)strlen(lscmd) + 3; /* Length of ls command */ for (i = 0; i < oldmtchs; i++) { /* Free previous file list */ if (namlst[i] ) { /* If memory is allocated */ free(namlst[i]); /* Free the memory */ namlst[i] = NULL ; /* Remember no memory is allocated */ } } oldmtchs = 0 ; /* Remember there are no matches */ fgbuf = malloc(x); /* Get buffer for command */ if (!fgbuf) return(-1); /* Fail if cannot */ ckmakmsg(fgbuf,x,lscmd," ",pat,NULL); /* Form the command */ zxcmd(ZIFILE,fgbuf); /* Start the command */ i = 0; /* File counter */ p = scratch; /* Point to scratch area */ retcode = -1; /* Assume failure */ while ((x = zminchar()) != -1) { /* Read characters from command */ c = (char) x; if (c == ' ' || c == '\n') { /* Got newline or space? */ *p = '\0'; /* Yes, terminate string */ p = scratch; /* Point back to beginning */ if (zchki(p) == -1) /* Does file exist? */ continue; /* No, continue */ itsadir = isdir(p); /* Yes, is it a directory? */ if (xdironly && !itsadir) /* Want only dirs but this isn't */ continue; /* so skip. */ if (xfilonly && itsadir) /* It's a dir but want only files */ continue; /* so skip. */ x = (int)strlen(p); /* Keep - get length of name */ q = malloc(x+1); /* Allocate space for it */ if (!q) goto shxfin; /* Fail if space can't be obtained */ strcpy(q,scratch); /* (safe) Copy name to space */ namlst[i++] = q; /* Copy pointer to name into array */ if (i >= len) goto shxfin; /* Fail if too many */ } else { /* Regular character */ *p++ = c; /* Copy it into scratch area */ } } retcode = i; /* Return number of matching files */ shxfin: /* Common exit point */ free(fgbuf); /* Free command buffer */ fgbuf = NULL; zclosf(ZIFILE); /* Delete the command fork. */ oldmtchs = i; /* Remember how many files */ return(retcode); } #endif /* NOPUSH */ /* Directory-reading functions for UNIX originally written for C-Kermit 4.0 by Jeff Damens, CUCCA, 1984. */ static char * xpat = NULL; /* Global copy of fgen() pattern */ static char * xpatlast = NULL; /* Rightmost segment of pattern*/ static int xpatslash = 0; /* Slash count in pattern */ static int xpatwild = 0; /* Original pattern is wild */ static int xleafwild = 0; /* Last segment of pattern is wild */ static int xpatabsolute = 0; #ifdef aegis static char bslash; #endif /* aegis */ /* S P L I T P A T H */ /* Splits the slash-separated portions of the argument string into a list of path structures. Returns the head of the list. The structures are allocated by malloc, so they must be freed. Splitpath is used internally by the filename generator. Input: A path string. Returns: A linked list of the slash-separated segments of the input. */ static struct path * splitpath(p) char *p; { struct path *head,*cur,*prv; int i; debug(F111,"splitpath",p,xrecursive); head = prv = NULL; if (!p) return(NULL); if (!*p) return(NULL); if (!strcmp(p,"**")) { /* Fix this */ p = "*"; } if (ISDIRSEP(*p)) p++; /* Skip leading slash if any */ /* Make linked list of path segments from pattern */ while (*p) { cur = (struct path *) malloc(sizeof (struct path)); /* debug(F101,"splitpath malloc","",cur); */ if (cur == NULL) { debug(F100,"splitpath malloc failure","",0); prv -> fwd = NULL; return((struct path *)NULL); } cur -> fwd = NULL; if (head == NULL) /* First, make list head */ head = cur; else /* Not first, link into chain */ prv -> fwd = cur; prv = cur; /* Link from previous to this one */ #ifdef aegis /* treat backslash as "../" */ if (bslash && *p == bslash) { strcpy(cur->npart, ".."); /* safe */ ++p; } else { for (i=0; i < MAXNAMLEN && *p && *p != '/' && *p != bslash; i++) cur -> npart[i] = *p++; cur -> npart[i] = '\0'; /* end this segment */ if (i >= MAXNAMLEN) while (*p && *p != '/' && *p != bslash) p++; } if (*p == '/') p++; #else /* General case (UNIX) */ for (i = 0; i < MAXNAMLEN && !ISDIRSEP(*p) && *p != '\0'; i++) { cur -> npart[i] = *p++; } cur -> npart[i] = '\0'; /* End this path segment */ if (i >= MAXNAMLEN) while (!ISDIRSEP(*p) && *p != '\0') p++; if (ISDIRSEP(*p)) p++; #endif /* aegis */ } if (prv) { makestr(&xpatlast,prv -> npart); debug(F110,"splitpath xpatlast",xpatlast,0); } #ifdef DEBUG /* Show original path list */ if (deblog) { for (i = 0, cur = head; cur; i++) { debug(F111,"SPLITPATH",cur -> npart, i); cur = cur -> fwd; } } #endif /* DEBUG */ return(head); } /* F G E N -- Generate File List */ /* File name generator. It is passed a string, possibly containing wildcards, and an array of character pointers. It finds all the matching filenames and stores pointers to them in the array. The returned strings are allocated from a static buffer local to this module (so the caller doesn't have to worry about deallocating them); this means that successive calls to fgen will wipe out the results of previous calls. Input: A wildcard string, an array to write names to, the length of the array. Returns: The number of matches. The array is filled with filenames that matched the pattern. If there wasn't enough room in the array, -1 is returned. Originally by: Jeff Damens, CUCCA, 1984. Many changes since then. */ static int fgen(pat,resarry,len) char *pat,*resarry[]; int len; { struct path *head; char *sptr, *s; int n; #ifdef aegis char *namechars; int tilde = 0, bquote = 0; if ((namechars = getenv("NAMECHARS")) != NULL) { if (ckstrchr(namechars, '~' ) != NULL) tilde = '~'; if (ckstrchr(namechars, '\\') != NULL) bslash = '\\'; if (ckstrchr(namechars, '`' ) != NULL) bquote = '`'; } else { tilde = '~'; bslash = '\\'; bquote = '`'; } sptr = scratch; /* copy "`node_data", etc. anchors */ if (bquote && *pat == bquote) while (*pat && *pat != '/' && *pat != bslash) *sptr++ = *pat++; else if (tilde && *pat == tilde) *sptr++ = *pat++; while (*pat == '/') *sptr++ = *pat++; if (sptr == scratch) { strcpy(scratch,"./"); /* safe */ sptr = scratch+2; } if (!(head = splitpath(pat))) return(-1); #else /* not aegis */ debug(F111,"fgen pat",pat,len); debug(F110,"fgen current directory",zgtdir(),0); debug(F101,"fgen stathack","",stathack); scratch[0] = '\0'; xpatwild = 0; xleafwild = 0; xpatabsolute = 0; if (!(head = splitpath(pat))) /* Make the path segment list */ return(-1); sptr = scratch; #ifdef COMMENT if (strncmp(pat,"./",2) && strncmp(pat,"../",3)) { #endif /* COMMENT */ if (!ISDIRSEP(*pat)) /* If name is not absolute */ *sptr++ = '.'; /* put "./" in front. */ *sptr++ = DIRSEP; #ifdef COMMENT } #endif /* COMMENT */ *sptr = '\0'; #endif /* aegis */ makestr(&xpat,pat); /* Save copy of original pattern */ debug(F110,"fgen scratch",scratch,0); for (n = 0, s = xpat; *s; s++) /* How many slashes in the pattern */ if (*s == DIRSEP) /* since these are fences for */ n++; /* pattern matching */ xpatslash = n; debug(F101,"fgen xpatslash","",xpatslash); numfnd = 0; /* None found yet */ if (initspace(resarry,ssplen) < 0) return(-1); xpatwild = iswild(xpat); /* Original pattern is wild? */ xpatabsolute = isabsolute(xpat); xleafwild = iswild(xpatlast); debug(F111,"fgen xpat",xpat,xpatwild); debug(F111,"fgen xpatlast",xpatlast,xleafwild); debug(F101,"fgen xpatabsolute","",xpatabsolute); traverse(head,scratch,sptr); /* Go walk the directory tree. */ while (head != NULL) { /* Done - free path segment list. */ struct path *next = head -> fwd; free((char *)head); head = next; } debug(F101,"fgen","",numfnd); return(numfnd); /* Return the number of matches */ } /* Define LONGFN (long file names) automatically for BSD 2.9 and 4.2 */ /* LONGFN can also be defined on the cc command line. */ #ifdef BSD29 #ifndef LONGFN #define LONGFN #endif #endif #ifdef BSD42 #ifndef LONGFN #define LONGFN #endif #endif /* T R A V E R S E -- Traverse a directory tree. Walks the directory tree looking for matches to its arguments. The algorithm is, briefly: If the current pattern segment contains no wildcards, that segment is added to what we already have. If the name so far exists, we call ourselves recursively with the next segment in the pattern string; otherwise, we just return. If the current pattern segment contains wildcards, we open the name we've accumulated so far (assuming it is really a directory), then read each filename in it, and, if it matches the wildcard pattern segment, add that filename to what we have so far and call ourselves recursively on the next segment. Finally, when no more pattern segments remain, we add what's accumulated so far to the result array and increment the number of matches. Inputs: A pattern path list (as generated by splitpath), a string pointer that points to what we've traversed so far (this can be initialized to "/" to start the search at the root directory, or to "./" to start the search at the current directory), and a string pointer to the end of the string in the previous argument, plus the global "recursive", "xmatchdot", and "xdironly" flags. Returns: void, with: mtchs[] containing the array of filename string pointers, and: numfnd containing the number of filenames. Although it might be poor practice, the mtchs[] array is revealed to the outside in case it needs it; for example, to be sorted prior to use. (It is poor practice because not all platforms implement file lists the same way; some don't use an array at all.) Note that addresult() acts as a second-level filter; due to selection criteria outside of the pattern, it might decline to add files that this routine asks it to, e.g. because we are collecting only directory names but not the names of regular files. WARNING: In the course of C-Kermit 7.0 development, this routine became ridiculously complex, in order to meet approximately sixty specific requirements. DON'T EVEN THINK ABOUT MODIFYING THIS ROUTINE! Trust me; it is not possible to fix anything in it without breaking something else. This routine badly needs a total redesign and rewrite. Note: There may be some good applications for realpath() and/or scandir() and/or fts_blah() here, on platforms where they are available. */ static VOID traverse(pl,sofar,endcur) struct path *pl; char *sofar, *endcur; { /* Appropriate declarations for directory routines and structures */ /* #define OPENDIR means to use opendir(), readdir(), closedir() */ /* If OPENDIR not defined, we use open(), read(), close() */ #ifdef DIRENT /* New way, */ #define OPENDIR DIR *fd, *opendir(); struct dirent *dirbuf; struct dirent *readdir(); #else /* !DIRENT */ #ifdef LONGFN /* Old way, with opendir() */ #define OPENDIR DIR *fd, *opendir(); struct direct *dirbuf; #else /* !LONGFN */ int fd; /* Old way, with open() */ struct direct dir_entry; struct direct *dirbuf = &dir_entry; #endif /* LONGFN */ #endif /* DIRENT */ int mopts = 0; /* ckmatch() opts */ int depth = 0; /* Directory tree depth */ char nambuf[MAXNAMLEN+4]; /* Buffer for a filename */ int itsadir = 0, segisdir = 0, itswild = 0, mresult, n, x /* , y */ ; struct stat statbuf; /* For file info. */ debug(F101,"STAT","",16); if (pl == NULL) { /* End of path-segment list */ *--endcur = '\0'; /* Terminate string, overwrite trailing slash */ debug(F110,"traverse add: end of path segment",sofar,0); addresult(sofar,-1); return; } if (stathack) { /* This speeds up the search a lot and we still get good results */ /* but it breaks the tagging of directory names done in addresult */ if (xrecursive || xfilonly || xdironly || xpatslash) { itsadir = xisdir(sofar); debug(F101,"STAT","",17); } else itsadir = (strncmp(sofar,"./",2) == 0); } else { itsadir = xisdir(sofar); debug(F101,"STAT","",18); } debug(F111,"traverse entry sofar",sofar,itsadir); #ifdef CKSYMLINK /* We're doing symlinks? */ #ifdef USE_LSTAT /* OK to use lstat()? */ if (itsadir && xnolinks) { /* If not following symlinks */ int x; struct stat buf; x = lstat(sofar,&buf); debug(F111,"traverse lstat 1",sofar,x); if (x > -1 && #ifdef S_ISLNK S_ISLNK(buf.st_mode) #else #ifdef _IFLNK ((_IFMT & buf.st_mode) == _IFLNK) #endif /* _IFLNK */ #endif /* S_ISLNK */ ) itsadir = 0; } #endif /* USE_LSTAT */ #endif /* CKSYMLINK */ if (!xmatchdot && xpatlast[0] == '.') xmatchdot = 1; if (!xmatchdot && xpat[0] == '.' && xpat[1] != '/' && xpat[1] != '.') xmatchdot = 1; /* ckmatch() options */ if (xmatchdot) mopts |= 1; /* Match dot */ if (!xrecursive) mopts |= 2; /* Dirsep is fence */ debug(F111,"traverse entry xpat",xpat,xpatslash); debug(F111,"traverse entry xpatlast",xpatlast,xmatchdot); debug(F110,"traverse entry pl -> npart",pl -> npart,0); #ifdef RECURSIVE if (xrecursive > 0 && !itsadir) { char * s; /* Recursive descent and this is a regular file */ *--endcur = '\0'; /* Terminate string, overwrite trailing slash */ /* Find the nth slash from the right and match from there... */ /* (n == the number of slashes in the original pattern - see fgen) */ if (*sofar == '/') { debug(F110,"traverse xpatslash absolute",sofar,0); s = sofar; } else { debug(F111,"traverse xpatslash relative",sofar,xpatslash); for (s = endcur - 1, n = 0; s >= sofar; s--) { if (*s == '/') { if (++n >= xpatslash) { s++; break; } } } } #ifndef NOSKIPMATCH /* This speeds things up a bit. */ /* If it causes trouble define NOSKIPMATCH and rebuild. */ if (xpat[0] == '*' && !xpat[1]) x = xmatchdot ? 1 : (s[0] != '.'); else #endif /* NOSKIPMATCH */ x = ckmatch(xpat, s, 1, mopts); /* Match with original pattern */ debug(F111,"traverse xpatslash ckmatch",s,x); if (x > 0) { debug(F110,"traverse add: recursive, match, && !isdir",sofar,0); addresult(sofar,itsadir); } return; } #endif /* RECURSIVE */ debug(F111,"traverse sofar 2",sofar,0); segisdir = ((pl -> fwd) == NULL) ? 0 : 1; itswild = wildena ? (iswild(pl -> npart)) : 0; /* 15 Jun 2005 */ debug(F111,"traverse segisdir",sofar,segisdir); debug(F111,"traverse itswild ",pl -> npart,itswild); #ifdef RECURSIVE if (xrecursive > 0) { /* If recursing and... */ if (segisdir && itswild) /* this is a dir and npart is wild */ goto blah; /* or... */ else if (!xpatabsolute && !xpatwild) /* search object is nonwild */ goto blah; /* then go recurse */ } #endif /* RECURSIVE */ if (!itswild) { /* This path segment not wild? */ #ifdef COMMENT strcpy(endcur,pl -> npart); /* (safe) Append next part. */ endcur += (int)strlen(pl -> npart); /* Advance end pointer */ #else /* strcpy() does not account for quoted metacharacters. We must remove the quotes before doing the stat(). */ { int quote = 0; char c, * s; s = pl -> npart; while ((c = *s++)) { if (!quote) { if (c == CMDQ) { quote = 1; continue; } } *endcur++ = c; quote = 0; } } #endif /* COMMENT */ *endcur = '\0'; /* End new current string. */ if (stat(sofar,&statbuf) == 0) { /* If this piece exists... */ debug(F110,"traverse exists",sofar,0); *endcur++ = DIRSEP; /* add slash to end */ *endcur = '\0'; /* and end the string again. */ traverse(pl -> fwd, sofar, endcur); } #ifdef DEBUG else debug(F110,"traverse not found", sofar, 0); #endif /* DEBUG */ return; } *endcur = '\0'; /* End current string */ debug(F111,"traverse sofar 3",sofar,0); if (!itsadir) return; /* Search is recursive or ... */ /* path segment contains wildcards, have to open and search directory. */ blah: debug(F110,"traverse opening directory", sofar, 0); #ifdef OPENDIR debug(F110,"traverse opendir()",sofar,0); if ((fd = opendir(sofar)) == NULL) { /* Can't open, fail. */ debug(F101,"traverse opendir() failed","",errno); return; } while ((dirbuf = readdir(fd))) #else /* !OPENDIR */ debug(F110,"traverse directory open()",sofar,0); if ((fd = open(sofar,O_RDONLY)) < 0) { debug(F101,"traverse directory open() failed","",errno); return; } while (read(fd, (char *)dirbuf, sizeof dir_entry) > 0) #endif /* OPENDIR */ { /* Read each entry in this directory */ int exists; char *eos, *s; exists = 0; /* On some platforms, the read[dir]() can return deleted files, */ /* e.g. HP-UX 5.00. There is no point in grinding through this */ /* routine when the file doesn't exist... */ if ( /* There actually is an inode... */ #ifdef BSD42 dirbuf->d_ino != -1 #else #ifdef unos dirbuf->d_ino != -1 #else #ifdef QNX dirbuf->d_stat.st_ino != 0 #else #ifdef SOLARIS dirbuf->d_ino != 0 #else #ifdef sun dirbuf->d_fileno != 0 #else #ifdef bsdi dirbuf->d_fileno != 0 #else #ifdef __386BSD__ dirbuf->d_fileno != 0 #else #ifdef __FreeBSD__ dirbuf->d_fileno != 0 #else #ifdef ultrix dirbuf->gd_ino != 0 #else #ifdef Plan9 1 #else dirbuf->d_ino != 0 #endif /* Plan9 */ #endif /* ultrix */ #endif /* __FreeBSD__ */ #endif /* __386BSD__ */ #endif /* bsdi */ #endif /* sun */ #endif /* SOLARIS */ #endif /* QNX */ #endif /* unos */ #endif /* BSD42 */ ) exists = 1; if (!exists) continue; ckstrncpy(nambuf, /* Copy the name */ dirbuf->d_name, MAXNAMLEN ); if (nambuf[0] == '.') { if (!nambuf[1] || (nambuf[1] == '.' && !nambuf[2])) { debug(F110,"traverse skipping",nambuf,0); continue; /* skip "." and ".." */ } } s = nambuf; /* Copy name to end of sofar */ eos = endcur; while ((*eos = *s)) { s++; eos++; } /* Now we check the file for (a) whether it is a directory, and (b) whether its name matches our pattern. If it is a directory, and if we have been told to build a recursive list, then we must descend regardless of whether it matches the pattern. If it is not a directory and it does not match our pattern, we skip it. Note: sofar is the full pathname, nambuf is the name only. */ /* Do this first to save pointless function calls */ if (nambuf[0] == '.' && !xmatchdot) /* Dir name starts with '.' */ continue; if (stathack) { if (xrecursive || xfilonly || xdironly || xpatslash) { itsadir = xisdir(sofar); /* See if it's a directory */ debug(F101,"STAT","",19); } else { itsadir = 0; } } else { itsadir = xisdir(sofar); debug(F101,"STAT","",20); } #ifdef CKSYMLINK #ifdef USE_LSTAT if (itsadir && xnolinks) { /* If not following symlinks */ int x; struct stat buf; x = lstat(sofar,&buf); debug(F111,"traverse lstat 2",sofar,x); if (x > -1 && #ifdef S_ISLNK S_ISLNK(buf.st_mode) #else #ifdef _IFLNK ((_IFMT & buf.st_mode) == _IFLNK) #endif /* _IFLNK */ #endif /* S_ISLNK */ ) itsadir = 0; } #endif /* USE_LSTAT */ #endif /* CKSYMLINK */ #ifdef RECURSIVE if (xrecursive > 0 && itsadir && (xpatlast[0] == '*') && !xpatlast[1] ) { debug(F110, "traverse add: recursive && isdir && segisdir or match", sofar, segisdir ); addresult(sofar,itsadir); if (numfnd < 0) return; } #endif /* RECURSIVE */ debug(F111,"traverse mresult xpat",xpat,xrecursive); debug(F111,"traverse mresult pl -> npart", pl -> npart, ((pl -> fwd) ? 9999 : 0) ); debug(F111,"traverse mresult sofar segisdir",sofar,segisdir); debug(F111,"traverse mresult sofar itsadir",sofar,itsadir); debug(F101,"traverse mresult xmatchdot","",xmatchdot); /* Match the path so far with the pattern after stripping any leading "./" from either or both. The pattern chosen is the full original pattern if the match candidate (sofar) is not a directory, or else just the name part (pl->npart) if it is. */ { char * s1; /* The pattern */ char * s2 = sofar; /* The path so far */ char * s3; /* Worker */ int opts; /* Match options */ s1 = itsadir ? pl->npart : xpat; #ifndef COMMENT /* I can't explain this but it unbreaks "cd blah/sub" */ if (itsadir && !xrecursive && xpatslash > 0 && segisdir == 0 && itswild) { s1 = xpat; debug(F110,"traverse mresult s1 kludge",s1,0); } #endif /* COMMENT */ if (xrecursive && xpatslash == 0) s2 = nambuf; while ((s1[0] == '.') && (s1[1] == '/')) /* Strip "./" */ s1 += 2; while ((s2[0] == '.') && (s2[1] == '/')) /* Ditto */ s2 += 2; opts = mopts; /* Match options */ if (itsadir) /* Current segment is a directory */ opts = mopts & 1; /* No fences */ s3 = s2; /* Get segment depth */ depth = 0; while (*s3) { if (*s3++ == '/') depth++; } #ifndef NOSKIPMATCH /* This speeds things up a bit. */ /* If it causes trouble define NOSKIPMATCH and rebuild. */ if (depth == 0 && (s1[0] == '*') && !s1[1]) mresult = xmatchdot ? 1 : (s2[0] != '.'); else #endif /* NOSKIPMATCH */ mresult = ckmatch(s1,s2,1,opts); /* Match */ } #ifdef DEBUG if (deblog) { debug(F111,"traverse mresult depth",sofar,depth); debug(F101,"traverse mresult xpatslash","",xpatslash); debug(F111,"traverse mresult nambuf",nambuf,mresult); debug(F111,"traverse mresult itswild",pl -> npart,itswild); debug(F111,"traverse mresult segisdir",pl -> npart,segisdir); } #endif /* DEBUG */ if (mresult || /* If match succeeded */ xrecursive || /* Or search is recursive */ depth < xpatslash /* Or not deep enough to match... */ ) { if ( /* If it's not a directory... */ /* The problem here is that segisdir is apparently not set appropriately. If I leave in the !segisdir test, then "dir /recursive blah" (where blah is a directory name) misses some regular files because sometimes segisdir is set and sometimes it's not. But if I comment it out, then "dir /.txt lists every file in * and does not even open up the subdirectories. However, "dir /rec /.txt" works right. */ #ifdef COMMENT mresult && (!itsadir && !segisdir) #else mresult && /* Matched */ !itsadir && /* sofar is not a directory */ ((!xrecursive && !segisdir) || xrecursive) #endif /* COMMENT */ ) { debug(F110, "traverse add: match && !itsadir",sofar,0); addresult(sofar,itsadir); if (numfnd < 0) return; } else if (itsadir && (xrecursive || mresult)) { struct path * xx = NULL; *eos++ = DIRSEP; /* Add directory separator */ *eos = '\0'; /* to end of segment */ #ifdef RECURSIVE /* Copy previous pattern segment to this new directory */ if (xrecursive > 0 && !(pl -> fwd)) { xx = (struct path *) malloc(sizeof (struct path)); pl -> fwd = xx; if (xx) { xx -> fwd = NULL; strcpy(xx -> npart, pl -> npart); /* safe */ } } #endif /* RECURSIVE */ traverse(pl -> fwd, sofar, eos); /* Traverse new directory */ } } } #ifdef OPENDIR closedir(fd); #else /* !OPENDIR */ close(fd); #endif /* OPENDIR */ } /* * addresult: * Adds a result string to the result array. Increments the number * of matches found, copies the found string into our string * buffer, and puts a pointer to the buffer into the caller's result * array. Our free buffer pointer is updated. If there is no * more room in the caller's array, the number of matches is set to -1. * Input: a result string. * Returns: nothing. */ static VOID addresult(str,itsadir) char *str; int itsadir; { int len; if (!freeptr) { debug(F100,"addresult string space not init'd","",0); initspace(mtchs,ssplen); } if (!str) str = ""; debug(F111,"addresult",str,itsadir); if (!*str) return; if (itsadir < 0) { itsadir = xisdir(str); } if ((xdironly && !itsadir) || (xfilonly && itsadir)) { debug(F111,"addresult skip",str,itsadir); return; } while (str[0] == '.' && ISDIRSEP(str[1])) /* Strip all "./" from front */ str += 2; if (--remlen < 0) { /* Elements left in array of names */ debug(F111,"addresult ARRAY FULL",str,numfnd); numfnd = -1; return; } len = (int)strlen(str); /* Space this will use */ debug(F111,"addresult len",str,len); if (len < 1) return; if ((freeptr + len + itsadir + 1) > (sspace + ssplen)) { debug(F111,"addresult OUT OF SPACE",str,numfnd); #ifdef DYNAMIC printf( "?String space %d exhausted - use SET FILE STRINGSPACE to increase\n",ssplen); #else printf("?String space %d exhausted\n",ssplen); #endif /* DYNAMIC */ numfnd = -1; /* Do not record if not enough space */ return; } strcpy(freeptr,str); /* safe */ /* Tag directory names by putting '/' at the end */ if (itsadir && (freeptr[len-1] == '/')) { freeptr[len++] = DIRSEP; freeptr[len] = '\0'; } if (numfnd >= maxnames) { #ifdef DYNAMIC printf( "?Too many files (%d max) - use SET FILE LISTSIZE to increase\n",maxnames); #else printf("?Too many files - %d max\n",maxnames); #endif /* DYNAMIC */ numfnd = -1; return; } str = freeptr; *resptr++ = freeptr; freeptr += (len + 1); numfnd++; debug(F111,"addresult ADD",str,numfnd); } #ifdef COMMENT /* * match(pattern,string): * pattern matcher. Takes a string and a pattern possibly containing * the wildcard characters '*' and '?'. Returns true if the pattern * matches the string, false otherwise. * Orignally by: Jeff Damens, CUCCA, 1984 * No longer used as of C-Kermit 7.0, now we use ckmatch() instead (ckclib.c). * * Input: a string and a wildcard pattern. * Returns: 1 if match, 0 if no match. */ static int match(pattern, string) char *pattern, *string; { char *psave = NULL, *ssave = NULL; /* Backup pointers for failure */ int q = 0; /* Quote flag */ if (*string == '.' && *pattern != '.' && !xmatchdot) { debug(F110,"match skip",string,0); return(0); } while (1) { for (; *pattern == *string; pattern++,string++) /* Skip first */ if (*string == '\0') return(1); /* End of strings, succeed */ if (*pattern == '\\' && q == 0) { /* Watch out for quoted */ q = 1; /* metacharacters */ pattern++; /* advance past quote */ if (*pattern != *string) return(0); continue; } else q = 0; if (q) { return(0); } else { if (*string != '\0' && *pattern == '?') { pattern++; /* '?', let it match */ string++; } else if (*pattern == '*') { /* '*' ... */ psave = ++pattern; /* remember where we saw it */ ssave = string; /* let it match 0 chars */ } else if (ssave != NULL && *ssave != '\0') { /* if not at end */ /* ...have seen a star */ string = ++ssave; /* skip 1 char from string */ pattern = psave; /* and back up pattern */ } else return(0); /* otherwise just fail */ } } } #endif /* COMMENT */ /* The following two functions are for expanding tilde in filenames Contributed by Howie Kaye, CUCCA, developed for CCMD package. */ /* W H O A M I -- Get user's username. */ /* 1) Get real uid 2) See if the $USER environment variable is set ($LOGNAME on AT&T) 3) If $USER's uid is the same as ruid, realname is $USER 4) Otherwise get logged in user's name 5) If that name has the same uid as the real uid realname is loginname 6) Otherwise, get a name for ruid from /etc/passwd */ char * whoami() { #ifdef DTILDE #ifdef pdp11 #define WHOLEN 100 #else #define WHOLEN 257 #endif /* pdp11 */ static char realname[UIDBUFLEN+1]; /* user's name */ static int ruid = -1; /* user's real uid */ char loginname[UIDBUFLEN+1], envname[256]; /* temp storage */ char *c; struct passwd *p; _PROTOTYP(extern char * getlogin, (void) ); debug(F111,"whoami ruid A",realname,ruid); if (ruid != -1) return(realname); ruid = real_uid(); /* get our uid */ debug(F101,"whoami ruid B","",ruid); if (ruid < 0) ruid = getuid(); debug(F101,"whoami ruid C","",ruid); /* how about $USER or $LOGNAME? */ if ((c = getenv(NAMEENV)) != NULL) { /* check the env variable */ ckstrncpy(envname, c, 255); debug(F110,"whoami envname",envname,0); if ((p = getpwnam(envname)) != NULL) { if (p->pw_uid == ruid) { /* get passwd entry for envname */ ckstrncpy(realname, envname, UIDBUFLEN); /* uid's are same */ debug(F110,"whoami realname",realname,0); return(realname); } } } /* can we use loginname() ? */ if ((c = getlogin()) != NULL) { /* name from utmp file */ ckstrncpy (loginname, c, UIDBUFLEN); debug(F110,"whoami loginname",loginname,0); if ((p = getpwnam(loginname)) != NULL) /* get passwd entry */ if (p->pw_uid == ruid) /* for loginname */ ckstrncpy(realname, envname, UIDBUFLEN); /* if uid's are same */ } /* Use first name we get for ruid */ if ((p = getpwuid(ruid)) == NULL) { /* name for uid */ debug(F101,"whoami no username for ruid","",ruid); realname[0] = '\0'; /* no user name */ ruid = -1; return(NULL); } ckstrncpy(realname, p->pw_name, UIDBUFLEN); debug(F110,"whoami realname from getpwuid",realname,0); return(realname); #else return(NULL); #endif /* DTILDE */ } /* T I L D E _ E X P A N D -- expand ~user to the user's home directory. */ char * tilde_expand(dirname) char *dirname; { #ifdef DTILDE #ifdef pdp11 #define BUFLEN 100 #else #define BUFLEN 257 #endif /* pdp11 */ struct passwd *user; static char olddir[BUFLEN+1]; static char oldrealdir[BUFLEN+1]; static char temp[BUFLEN+1]; int i, j; debug(F111,"tilde_expand",dirname,dirname[0]); if (dirname[0] != '~') { /* Not a tilde...return param */ debug(F000,"tilde_expand NOT TILDE","",dirname[0]); return(dirname); } if (!strcmp(olddir,dirname)) { /* Same as last time */ debug(F110,"tilde_expand same as previous",oldrealdir,0); return(oldrealdir); /* so return old answer. */ } else { debug(F110,"tilde_expand working...","",0); j = (int)strlen(dirname); for (i = 0; i < j; i++) /* find username part of string */ if (!ISDIRSEP(dirname[i])) temp[i] = dirname[i]; else break; temp[i] = '\0'; /* tie off with a NULL */ debug(F111,"tilde_expand first part",temp,i); if (i == 1) { /* if just a "~" */ #ifdef IKSD if (inserver) user = getpwnam(uidbuf); /* Get info on current user */ else #endif /* IKSD */ { char * p = whoami(); debug(F110,"tilde_expand p",p,0); if (p) { user = getpwnam(p); debug(F110,"tilde_expand getpwpam ~",user,0); } else { user = NULL; } } } else { debug(F110,"tilde_expand ~user",&temp[1],0); user = getpwnam(&temp[1]); /* otherwise on the specified user */ debug(F110,"tilde_expand getpwpam user",user,0); } } if (user != NULL) { /* valid user? */ ckstrncpy(olddir, dirname, BUFLEN); /* remember the directory */ ckstrncpy(oldrealdir,user->pw_dir, BUFLEN); /* and home directory */ ckstrncat(oldrealdir,&dirname[i], BUFLEN); oldrealdir[BUFLEN] = '\0'; return(oldrealdir); } else { /* invalid? */ ckstrncpy(olddir, dirname, BUFLEN); /* remember for next time */ ckstrncpy(oldrealdir, dirname, BUFLEN); return(oldrealdir); } #else return(NULL); #endif /* DTILDE */ } /* Functions for executing system commands. zsyscmd() executes the system command in the normal, default way for the system. In UNIX, it does what system() does. Thus, its results are always predictable. zshcmd() executes the command using the user's preferred shell. */ int zsyscmd(s) char *s; { #ifdef aegis if (nopush) return(-1); if (!priv_chk()) return(system(s)); #else PID_T shpid; #ifdef COMMENT /* This doesn't work... */ WAIT_T status; #else int status; #endif /* COMMENT */ if (nopush) return(-1); if ((shpid = fork())) { if (shpid < (PID_T)0) return(-1); /* Parent */ while (shpid != (PID_T) wait(&status)) ; return(status); } if (priv_can()) { /* Child: cancel any priv's */ printf("?Privilege cancellation failure\n"); _exit(255); } restorsigs(); /* Restore ignored signals */ #ifdef HPUX10 execl("/usr/bin/sh","sh","-c",s,NULL); perror("/usr/bin/sh"); #else #ifdef Plan9 execl("/bin/rc", "rc", "-c", s, NULL); perror("/bin/rc"); #else execl("/bin/sh","sh","-c",s,NULL); perror("/bin/sh"); #endif /* Plan9 */ #endif /* HPUX10 */ _exit(255); return(0); /* Shut up ANSI compilers. */ #endif /* aegis */ } /* Z _ E X E C -- Overlay ourselves with another program */ #ifndef NOZEXEC #ifdef HPUX5 #define NOZEXEC #else #ifdef ATT7300 #define NOZEXEC #endif /* ATT7300 */ #endif /* HPUX5 */ #endif /* NOZEXEC */ VOID z_exec(p,s,t) char * p, ** s; int t; { /* Overlay ourselves with "p s..." */ #ifdef NOZEXEC printf("EXEC /REDIRECT NOT IMPLEMENTED IN THIS VERSION OF C-KERMIT\n"); debug(F110,"z_exec NOT IMPLEMENTED",p,0); #else int x; extern int ttyfd; debug(F110,"z_exec command",p,0); debug(F110,"z_exec arg 0",s[0],0); debug(F110,"z_exec arg 1",s[1],0); debug(F101,"z_exec t","",t); errno = 0; if (t) { if (ttyfd > 2) { dup2(ttyfd, 0); dup2(ttyfd, 1); /* dup2(ttyfd, 2); */ close(ttyfd); } } restorsigs(); /* Restore ignored signals */ x = execvp(p,s); if (x < 0) debug(F101,"z_exec errno","",errno); #endif /* NOZEXEC */ } /* Z S H C M D -- Execute a shell command (or program thru the shell). Original UNIX code by H. Fischer; copyright rights assigned to Columbia U. Adapted to use getpwuid to find login shell because many systems do not have SHELL in environment, and to use direct calling of shell rather than intermediate system() call. -- H. Fischer (1985); many changes since then. Call with s pointing to command to execute. Returns: -1 on failure to start the command (can't find, can't fork, can't run). 1 if command ran and gave an exit status of 0. 0 if command ran and gave a nonzero exit status. with pexitstatus containing the command's exit status. */ int zshcmd(s) char *s; { PID_T pid; #ifdef NOPUSH return(0); #else if (nopush) return(-1); if (!s) return(-1); while (*s == ' ') s++; debug(F110,"zshcmd command",s,0); #ifdef aegis if ((pid = vfork()) == 0) { /* Make child quickly */ char *shpath, *shname, *shptr; /* For finding desired shell */ if (priv_can()) exit(1); /* Turn off privs. */ if ((shpath = getenv("SHELL")) == NULL) shpath = "/com/sh"; #else /* All Unix systems */ if ((pid = fork()) == 0) { /* Make child */ char *shpath, *shname, *shptr; /* For finding desired shell */ struct passwd *p; #ifdef HPUX10 /* Default */ char *defshell = "/usr/bin/sh"; #else #ifdef Plan9 char *defshell = "/bin/rc"; #else char *defshell = "/bin/sh"; #endif /* Plan9 */ #endif /* HPUX10 */ if (priv_can()) exit(1); /* Turn off privs. */ #ifdef COMMENT /* Old way always used /etc/passwd shell */ p = getpwuid(real_uid()); /* Get login data */ if (p == (struct passwd *) NULL || !*(p->pw_shell)) shpath = defshell; else shpath = p->pw_shell; #else /* New way lets user override with SHELL variable, but does not rely on it. */ /* This allows user to specify a different shell. */ shpath = getenv("SHELL"); /* What shell? */ debug(F110,"zshcmd SHELL",shpath,0); { int x = 0; if (!shpath) { x++; } else if (!*shpath) { x++; } if (x) { debug(F100,"zshcmd SHELL not defined","",0); p = getpwuid( real_uid() ); /* Get login data */ if (p == (struct passwd *)NULL || !*(p->pw_shell)) { shpath = defshell; } else { shpath = p->pw_shell; } debug(F110,"zshcmd shpath from getpwuid",shpath,0); } } #endif /* COMMENT */ #endif /* aegis */ shptr = shname = shpath; while (*shptr != '\0') if (*shptr++ == DIRSEP) shname = shptr; restorsigs(); /* Restore ignored signals */ debug(F110,"zshcmd execl shpath",shpath,0); debug(F110,"zshcmd execl shname",shname,0); if (s == NULL || *s == '\0') { /* Interactive shell requested? */ debug(F100,"zshcmd execl interactive","",0); execl(shpath,shname,"-i",NULL); /* Yes, do that */ } else { /* Otherwise, */ debug(F110,"zshcmd execl command",s,0); execl(shpath,shname,"-c",s,NULL); /* exec the given command */ } /* If execl() failed, */ debug(F101,"zshcmd errno","",errno); perror(shpath); /* print reason and */ exit(BAD_EXIT); /* return bad return code. */ } else { /* Parent */ int wstat; /* ... must wait for child */ #ifdef CK_CHILD int child; /* Child's exit status */ #endif /* CK_CHILD */ SIGTYP (*istat)(), (*qstat)(); if (pid == (PID_T) -1) return(-1); /* fork() failed? */ istat = signal(SIGINT,SIG_IGN); /* Let the fork handle keyboard */ qstat = signal(SIGQUIT,SIG_IGN); /* interrupts itself... */ debug(F110,"zshcmd parent waiting for child",s,0); #ifdef CK_CHILD while (((wstat = wait(&child)) != pid) && (wstat != -1)) #else while (((wstat = wait((WAIT_T *)0)) != pid) && (wstat != -1)) #endif /* CK_CHILD */ ; /* Wait for fork */ signal(SIGINT,istat); /* Restore interrupts */ signal(SIGQUIT,qstat); #ifdef CK_CHILD pexitstat = (child & 0xff) ? child : child >> 8; debug(F101,"zshcmd exit status","",pexitstat); return(child == 0 ? 1 : 0); /* Return child's status */ #endif /* CK_CHILD */ } return(1); #endif /* NOPUSH */ } /* I S W I L D -- Check if filespec is "wild" */ /* Returns: 0 wildcards disabled or argument is empty or is the name of a single file; 1 if it contains wildcard characters. Note: must match the algorithm used by match(), hence no [a-z], etc. */ int iswild(filespec) char *filespec; { char c, *p, *f; int x; int quo = 0; if (!filespec) /* Safety */ return(0); if (!wildena) /* Wildcards disabled - 12 Jun 2005 */ return(0); f = filespec; if (wildxpand) { /* Shell handles wildcarding */ if ((x = nzxpand(filespec,0)) > 1) return(1); if (x == 0) return(0); /* File does not exist */ p = malloc(MAXNAMLEN + 20); znext(p); x = (strcmp(filespec,p) != 0); free(p); p = NULL; return(x); } else { /* We do it ourselves */ while ((c = *filespec++) != '\0') { if (c == '\\' && quo == 0) { quo = 1; continue; } if (!quo && (c == '*' || c == '?' #ifdef CKREGEX #ifndef VMS || c == '[' #endif /* VMS */ || c == '{' #endif /* CKREGEX */ )) { debug(F111,"iswild",f,1); return(1); } quo = 0; } debug(F111,"iswild",f,0); return(0); } } /* I S D I R -- Is a Directory. Tell if string pointer s is the name of an existing directory. Returns 1 if directory, 0 if not a directory. The following no longer applies: If the file is a symlink, we return 1 if it is a directory OR if it is a link to a directory and the "xrecursive" flag is NOT set. This is to allow parsing a link to a directory as if it were a directory (e.g. in the CD or IF DIRECTORY command) but still prevent recursive traversal from visiting the same directory twice. */ #ifdef ISDIRCACHE /* This turns out to be unsafe and gives little benefit anyway. */ /* See notes 28 Sep 2003. Thus ISDIRCACHE is not defined. */ static char prevpath[CKMAXPATH+4] = { '\0', '\0' }; static int prevstat = -1; int clrdircache() { debug(F100,"CLEAR ISDIR CACHE","",0); prevstat = -1; prevpath[0] = NUL; } #endif /* ISDIRCACHE */ int isalink(s) char *s; { #ifndef CKSYMLINK return(0); #else int r = 0; char filbuf[CKMAXPATH+4]; if (readlink(s,filbuf,CKMAXPATH) > -1) r = 1; debug(F110,"isalink readlink",s,r); return(r); #endif /* CKSYMLINK */ } int isdir(s) char *s; { int x, needrlink = 0, islink = 0; struct stat statbuf; char fnam[CKMAXPATH+4]; if (!s) return(0); debug(F110,"isdir entry",s,0); #ifdef DTILDE /* 2005-08-13 */ if (*s == '~') { /* Starts with tilde? */ s = tilde_expand(s); /* Attempt to expand tilde */ if (!s) s = ""; debug(F110,"isdir tilde_expand",s,0); } #endif /* DTILDE */ if (!*s) return(0); #ifdef ISDIRCACHE if (prevstat > -1) { if (s[0] == prevpath[0]) { if (!strcmp(s,prevpath)) { debug(F111,"isdir cache hit",s,prevstat); return(prevstat); } } } #endif /* ISDIRCACHE */ #ifdef CKSYMLINK #ifdef COMMENT /* The following over-clever bit has been commented out because it presumes to know when a symlink might be redundant, which it can't possibly know. Using plain old stat() gives Kermit the same results as ls and ls -R, which is just fine: no surprises. */ #ifdef USE_LSTAT if (xrecursive) { x = lstat(s,&statbuf); debug(F111,"isdir lstat",s,x); } else { #endif /* USE_LSTAT */ x = stat(s,&statbuf); debug(F111,"isdir stat",s,x); #ifdef USE_LSTAT } #endif /* USE_LSTAT */ #else x = stat(s,&statbuf); debug(F111,"isdir stat",s,x); #endif /* COMMENT */ if (x == -1) { debug(F101,"isdir errno","",errno); return(0); } islink = 0; if (xrecursive) { #ifdef NOLINKBITS needrlink = 1; #else #ifdef S_ISLNK islink = S_ISLNK(statbuf.st_mode); debug(F101,"isdir S_ISLNK islink","",islink); #else #ifdef _IFLNK islink = (_IFMT & statbuf.st_mode) == _IFLNK; debug(F101,"isdir _IFLNK islink","",islink); #endif /* _IFLNK */ #endif /* S_ISLNK */ #endif /* NOLINKBITS */ if (needrlink) { if (readlink(s,fnam,CKMAXPATH) > -1) islink = 1; } } #else x = stat(s,&statbuf); if (x == -1) { debug(F101,"isdir errno","",errno); return(0); } debug(F111,"isdir stat",s,x); #endif /* CKSYMLINK */ debug(F101,"isdir islink","",islink); debug(F101,"isdir statbuf.st_mode","",statbuf.st_mode); x = islink ? 0 : (S_ISDIR (statbuf.st_mode) ? 1 : 0); #ifdef ISDIRCACHE prevstat = x; ckstrncpy(prevpath,s,CKMAXPATH+1); #endif /* ISDIRCACHE */ return(x); } #ifdef CK_MKDIR /* Some systems don't have mkdir(), e.g. Tandy Xenix 3.2.. */ /* Z M K D I R -- Create directory(s) if necessary */ /* Call with: A pointer to a file specification that might contain directory information. The filename is expected to be included. If the file specification does not include any directory separators, then it is assumed to be a plain file. If one or more directories are included in the file specification, this routine tries to create them if they don't already exist. Returns: 0 or greater on success, i.e. the number of directories created. -1 on failure to create the directory */ int zmkdir(path) char *path; { char *xp, *tp, c; int x, count = 0; if (!path) path = ""; if (!*path) return(-1); #ifdef CKROOT debug(F111,"zmkdir setroot",ckroot,ckrootset); if (ckrootset) if (!zinroot(path)) { debug(F110,"zmkdir setroot violation",path,0); return(-1); } #endif /* CKROOT */ x = strlen(path); debug(F111,"zmkdir",path,x); if (x < 1 || x > MAXPATH) /* Check length */ return(-1); if (!(tp = malloc(x+1))) /* Make a temporary copy */ return(-1); strcpy(tp,path); /* safe (prechecked) */ #ifdef DTILDE if (*tp == '~') { /* Starts with tilde? */ xp = tilde_expand(tp); /* Attempt to expand tilde */ if (!xp) xp = ""; if (*xp) { char *zp; debug(F110,"zmkdir tilde_expand",xp,0); if (!(zp = malloc(strlen(xp) + 1))) { /* Make a place for it */ free(tp); tp = NULL; return(-1); } free(tp); /* Free previous buffer */ tp = zp; /* Point to new one */ strcpy(tp,xp); /* Copy expanded name to new buffer */ } } #endif /* DTILDE */ debug(F110,"zmkdir tp after tilde_expansion",tp,0); xp = tp; if (ISDIRSEP(*xp)) /* Don't create root directory! */ xp++; /* Go thru filespec from left to right... */ for (; *xp; xp++) { /* Create parts that don't exist */ if (!ISDIRSEP(*xp)) /* Find next directory separator */ continue; c = *xp; /* Got one. */ *xp = NUL; /* Make this the end of the string. */ if (!isdir(tp)) { /* This directory exists already? */ #ifdef CK_LOGIN if (isguest) /* Not allowed for guests */ return(-1); #ifndef NOXFER /* Nor if MKDIR and/or CD are disabled */ else #endif /* CK_LOGIN */ if ((server #ifdef IKSD || inserver #endif /* IKSD */ ) && (!ENABLED(en_mkd) || !ENABLED(en_cwd))) return(-1); #endif /* IKSD */ debug(F110,"zmkdir making",tp,0); x = /* No, try to create it */ #ifdef NOMKDIR -1 /* Systems without mkdir() */ #else mkdir(tp,0777) /* UNIX */ #endif /* NOMKDIR */ ; if (x < 0) { debug(F101,"zmkdir failed, errno","",errno); free(tp); /* Free temporary buffer. */ tp = NULL; return(-1); /* Return failure code. */ } else count++; } *xp = c; /* Replace the separator. */ } free(tp); /* Free temporary buffer. */ return(count); /* Return success code. */ } #endif /* CK_MKDIR */ int zrmdir(path) char *path; { #ifdef CK_LOGIN if (isguest) return(-1); #endif /* CK_LOGIN */ if (!path) path = ""; if (!*path) return(-1); #ifdef CKROOT debug(F111,"zrmdir setroot",ckroot,ckrootset); if (ckrootset) if (!zinroot(path)) { debug(F110,"zrmdir setroot violation",path,0); return(-1); } #endif /* CKROOT */ #ifndef NOMKDIR return(rmdir(path)); #else return(-1); #endif /* NOMKDIR */ } /* Z F S E E K -- Position input file pointer */ /* Call with: CK_OFF_T (32 or 64 bits), 0-based, indicating desired position. Returns: 0 on success. -1 on failure. */ #ifndef NORESEND int #ifdef CK_ANSIC zfseek(CK_OFF_T pos) #else zfseek(pos) CK_OFF_T pos; #endif /* CK_ANSIC */ /* zfseek */ { zincnt = -1; /* Must empty the input buffer */ debug(F101,"zfseek","",pos); return(CKFSEEK(fp[ZIFILE], pos, 0)?-1:0); } #endif /* NORESEND */ /* Z F N Q F P -- Convert filename to fully qualified absolute pathname */ /* Given a possibly unqualified or relative file specification fn, zfnqfp() returns the fully qualified filespec for the same file, returning a struct that contains the length (len) of the result, a pointer (fpath) to the whole result, and a pointer (fname) to where the filename starts. */ static struct zfnfp fnfp = { 0, NULL, NULL }; struct zfnfp * zfnqfp(fname, buflen, buf) char * fname; int buflen; char * buf; { char * s; int len; #ifdef MAXPATHLEN char zfntmp[MAXPATHLEN+4]; #else char zfntmp[CKMAXPATH+4]; #endif /* MAXPATHLEN */ char sb[32], * tmp; int i = 0, j = 0, k = 0, x = 0, y = 0; int itsadir = 0; s = fname; if (!s) return(NULL); if (!*s) return(NULL); if (!buf) return(NULL); /* Initialize the data structure */ fnfp.len = ckstrncpy(buf,fname,buflen); fnfp.fpath = buf; fnfp.fname = NULL; len = buflen; debug(F111,"zfnqfp fname",fname,len); #ifdef DTILDE if (*s == '~') { /* Starts with tilde? */ char * xp; xp = tilde_expand(s); /* Attempt to expand tilde */ debug(F110,"zfnqfp xp",xp,0); /* (realpath() doesn't do this) */ if (!xp) xp = ""; if (*xp) s = xp; } #endif /* DTILDE */ #ifdef CKREALPATH /* N.B.: The realpath() result buffer MUST be MAXPATHLEN bytes long */ /* otherwise we write over memory. */ if (!realpath(s,zfntmp)) { debug(F111,"zfnqfp realpath fails",s,errno); #ifdef COMMENT if (errno != ENOENT) return(NULL); #else /* If realpath() fails use the do-it-yourself method */ /* 16 Jan 2002 */ goto norealpath; #endif /* COMMENT */ } len = strlen(zfntmp); if (len > buflen) { debug(F111,"zfnqfp result too long",ckitoa(buflen),len); return(NULL); } else { ckstrncpy(buf,zfntmp,buflen); } if (buf[len-1] != '/') { if ((itsadir = isdir(buf)) && len < (buflen - 1)) { buf[len++] = '/'; buf[len] = NUL; } } fnfp.len = len; fnfp.fpath = buf; debug(F110,"zfnqfp realpath path",fnfp.fpath,0); tmp = buf + fnfp.len - 1; if (!itsadir) { while (tmp >= buf) { if (*tmp == '/') { fnfp.fname = tmp + 1; debug(F110,"zfnqfp realpath name",fnfp.fname,0); break; } tmp--; } } return(&fnfp); #endif /* CKREALPATH */ norealpath: tmp = zfntmp; while (*s) { /* Remove leading "./" (0 or more) */ debug(F110,"zfnqfp while *s",s,0); if (*s == '.' && *(s+1) == '/') { s += 2; while (*s == '/') s++; } else break; } if (!*s) return(NULL); if (*s == '/') { /* Pathname is absolute */ ckstrncpy(buf,s,len); x = strlen(buf); y = 0; } else { /* Pathname is relative */ char * p; if (p = zgtdir()) { /* So get current directory */ debug(F110,"zfnqfp zgtdir",p,0); x = ckstrncpy(buf,p,len); buf[x++] = '/'; debug(F110,"zfnqfp buf 1",buf,0); len -= x; /* How much room left in buffer */ if ((y = (int)strlen(s)) > len) /* If enough room... */ return(NULL); ckstrncpy(buf+x,s,len); /* ... append the filename */ debug(F110,"zfnqfp buf 2",buf,0); } else { return(NULL); } } /* Buf now holds full path but maybe containing some . or .. tricks */ j = x + y; /* Length of what's in buf */ len = j; debug(F101,"zfnqfp len","",len); /* Catch dangling "/." or "/.." */ if ((j > 1 && buf[j-1] == '.' && buf[j-2] == '/') || (j > 2 && buf[j-1] == '.' && buf[j-2] == '.' && buf[j-3] == '/')) { if (j < buflen - 2) { buf[j] = '/'; buf[j+1] = NUL; } } j = -1; /* j = position of rightmost "/" */ i = 0; /* i = destination index */ tmp[i] = NUL; /* destination is temporary buffer */ for (x = 0; x < len; x++) { /* x = source index */ if (buf[x] == '/') { for (k = 0; k < 4; k++) { sb[k] = buf[x+k]; sb[k+1] = '\0'; if (!sb[k]) break; } if (!strncmp(sb,"/./",3)) { /* Eliminate "./" in "/./" */ x += 1; continue; } else if (!strncmp(sb,"//",2)) { /* Change "//" to "/" */ continue; } else if (!strncmp(sb,"/../",4)) { /* ".." in path */ for (k = i - 1; k >= 0; k--) { /* Back up one level */ if (tmp[k] == '/') { i = k; tmp[i] = NUL; break; } } x += 2; continue; } } if (i >= (buflen - 1)) { debug(F111,"zfnqfp overflow",tmp,i); return(NULL); } tmp[i++] = buf[x]; /* Regular character, copy */ tmp[i] = NUL; if (buf[x] == '/') /* Remember rightmost "/" */ j = i; } ckstrncpy(buf,tmp,buflen-1); /* Copy the result back */ buf[buflen-1] = NUL; if (!buf[0]) { /* If empty, say root */ buf[0] = '/'; buf[2] = NUL; j = 0; i = 1; } if ((itsadir = isdir(buf))) { if (buf[i-1] != '/' && i < (buflen - 1)) { buf[i++] = '/'; buf[i] = NUL; } } if (!itsadir && (j > -1)) { /* Set pointer to basename */ fnfp.fname = (char *)(buf + j); fnfp.fpath = (char *)buf; fnfp.len = i; debug(F111,"zfnqfp path",fnfp.fpath,i); debug(F110,"zfnqfp name",fnfp.fname,0); return(&fnfp); } return(NULL); } /* Z C M P F N -- Compare two filenames */ /* Returns 1 if the two names refer to the same existing file, 0 otherwise. */ int zcmpfn(s1,s2) char * s1, * s2; { char buf1[CKMAXPATH+1]; char buf2[CKMAXPATH+1]; #ifdef USE_LSTAT char linkname[CKMAXPATH+1]; struct stat buf; #endif /* USE_LSTAT */ int x, rc = 0; if (!s1) s1 = ""; if (!s2) s2 = ""; if (!*s1 || !*s2) return(0); #ifdef CKSYMLINK /* We're doing symlinks? */ #ifdef USE_LSTAT /* OK to use lstat()? */ x = lstat(s1,&buf); if (x > -1 && /* Now see if it's a symlink */ #ifdef S_ISLNK S_ISLNK(buf.st_mode) #else #ifdef _IFLNK ((_IFMT & buf.st_mode) == _IFLNK) #endif /* _IFLNK */ #endif /* S_ISLNK */ ) { linkname[0] = '\0'; /* Get the name */ x = readlink(s1,linkname,CKMAXPATH); if (x > -1 && x < CKMAXPATH) { /* It's a link */ linkname[x] = '\0'; s1 = linkname; } } #endif /* USE_LSTAT */ #endif /* CKSYMLINK */ if (zfnqfp(s1,CKMAXPATH,buf1)) { /* Convert to full pathname */ #ifdef CKSYMLINK /* Same deal for second name... */ #ifdef USE_LSTAT x = lstat(s2,&buf); if (x > -1 && #ifdef S_ISLNK S_ISLNK(buf.st_mode) #else #ifdef _IFLNK ((_IFMT & buf.st_mode) == _IFLNK) #endif /* _IFLNK */ #endif /* S_ISLNK */ ) { linkname[0] = '\0'; x = readlink(s2,linkname,CKMAXPATH); if (x > -1 && x < CKMAXPATH) { linkname[x] = '\0'; s2 = linkname; } } #endif /* USE_LSTAT */ #endif /* CKSYMLINK */ if (zfnqfp(s2,CKMAXPATH,buf2)) { debug(F110,"zcmpfn s1",buf1,0); debug(F110,"zcmpfn s2",buf2,0); if (!strncmp(buf1,buf2,CKMAXPATH)) rc = 1; } } debug(F101,"zcmpfn result","",rc); return(rc); } #ifdef CKROOT /* User-mode chroot() implementation */ int zsetroot(s) char * s; { /* Sets the root */ char buf[CKMAXPATH+1]; if (!s) return(-1); if (!*s) return(-1); debug(F110,"zsetroot",s,0); if (!isdir(s)) return(-2); if (!zfnqfp(s,CKMAXPATH,buf)) /* Get full, real path */ return(-3); if (access(buf,R_OK) < 0) { /* Check access */ debug(F110,"zsetroot access denied",buf,0); return(-4); } s = buf; if (ckrootset) { /* If root already set */ if (!zinroot(s)) { /* make sure new root is in it */ debug(F110,"zsetroot new root not in root",ckroot,0); return(-5); } } if (zchdir(buf) < 1) return(-4); /* Change directory to new root */ ckrootset = ckstrncpy(ckroot,buf,CKMAXPATH); /* Now set the new root */ if (ckroot[ckrootset-1] != '/') { ckroot[ckrootset++] = '/'; ckroot[ckrootset] = '\0'; } debug(F111,"zsetroot rootset",ckroot,ckrootset); ckrooterr = 0; /* Reset error flag */ return(1); } char * zgetroot() { /* Returns the root */ if (!ckrootset) return(NULL); return((char *)ckroot); } int zinroot(s) char * s; { /* Checks if file s is in the root */ int x, n; struct zfnfp * f = NULL; char buf[CKMAXPATH+2]; debug(F111,"zinroot setroot",ckroot,ckrootset); ckrooterr = 0; /* Reset global error flag */ if (!ckrootset) /* Root not set */ return(1); /* so it's ok - no need to check */ if (!(f = zfnqfp(s,CKMAXPATH,buf))) /* Get full and real pathname */ return(0); /* Fail if we can't */ n = f->len; /* Length of full pathname */ debug(F111,"zinroot n",buf,n); if (n < (ckrootset - 1) || n > CKMAXPATH) { /* Bad length */ ckrooterr = 1; /* Fail */ return(0); } if (isdir(buf) && buf[n-1] != '/') { /* If it's a directory name */ buf[n++] = '/'; /* make sure it ends with '/' */ buf[n] = '\0'; } x = strncmp(buf,ckroot,ckrootset); /* Compare, case-sensitive */ debug(F111,"zinroot checked",buf,x); if (x == 0) /* OK */ return(1); ckrooterr = 1; /* Not OK */ return(0); } #endif /* CKROOT */ #ifdef CK_LOGIN /* The following code provides support for user login and logout including anonymous accounts. If this feature is to be supported outside of UNIX, it should be spread out among the ck?fio.c modules... */ #ifndef _PATH_BSHELL #define _PATH_BSHELL "/usr/bin/bash" #endif /* _PATH_BSHELL */ #ifndef _PATH_FTPUSERS #define _PATH_FTPUSERS "/etc/ftpusers" #endif /* _PATH_FTPUSERS */ /* * Helper function for sgetpwnam(). */ char * sgetsave(s) char *s; { char *new = malloc((unsigned) strlen(s) + 1); if (new == NULL) { printf("?Local resource failure: malloc\n"); exit(1); /* NOTREACHED */ } (void) strcpy(new, s); /* safe */ return (new); } /* * Save the result of getpwnam(). Used for USER command, since * the data returned must not be clobbered by any other command * (e.g., globbing). */ struct passwd * sgetpwnam(name) char *name; { static struct passwd save; register struct passwd *p; #ifdef CK_SHADOW register struct spwd *sp; #endif /* CK_SHADOW */ char *sgetsave(); #ifdef HPUX10_TRUSTED struct pr_passwd *pr; #endif /* HPUX10_TRUSTED */ #ifdef CK_SHADOW sp = getspnam(name); if (sp == NULL) { debug(F110,"sgetpwnam","getspnam() fails",0); return (NULL); } #endif /* CK_SHADOW */ #ifdef HPUX10_TRUSTED if ((pr = getprpwnam(name)) == NULL) return(NULL); #endif /* HPUX10_TRUSTED */ p = getpwnam(name); /* debug(F111,"sgetpwnam","getpwnam()",p); */ if (p == NULL) return(NULL); if (save.pw_name) { free(save.pw_name); free(save.pw_passwd); free(save.pw_gecos); free(save.pw_dir); free(save.pw_shell); } save = *p; save.pw_name = sgetsave(p->pw_name); #ifdef CK_SHADOW save.pw_passwd = sgetsave(sp->sp_pwdp); #else /* CK_SHADOW */ #ifdef HPUX10_TRUSTED if (pr->uflg.fg_encrypt && pr->ufld.fd_encrypt && *pr->ufld.fd_encrypt) save.pw_passwd = sgetsave(pr->ufld.fd_encrypt); else save.pw_passwd = sgetsave(""); #else /* HPUX10_TRUSTED */ save.pw_passwd = sgetsave(p->pw_passwd); #endif /* HPUX10_TRUSTED */ #endif /* CK_SHADOW */ save.pw_gecos = sgetsave(p->pw_gecos); save.pw_dir = sgetsave(p->pw_dir); save.pw_shell = sgetsave(p->pw_shell); return(&save); } #define CKXLOGBSIZ 256 struct passwd * pw = NULL; char * home = NULL; /* Home directory pointer for glob */ #ifdef CMASK #undef CMASK #endif /* CMASK */ #define CMASK 027 int defumask = CMASK; /* Default umask value */ /* Z V U S E R -- Verify user, Returns 1 if user OK, 0 otherwise. */ /* Sets global passwd pointer pw if named account exists and is acceptable; * sets askpasswd if a PASS command is expected. If logged in previously, * need to reset state. If name is "ftp" or "anonymous", the name is not in * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return. * If account doesn't exist, ask for passwd anyway. Otherwise, check user * requesting login privileges. Disallow anyone who does not have a standard * shell as returned by getusershell(). Disallow anyone mentioned in the file * _PATH_FTPUSERS to allow people such as root and uucp to be avoided. */ _PROTOTYP(static int checkuser, (char *) ); char zvuname[64] = { NUL, NUL }; char zvhome[CKMAXPATH+1] = { NUL, NUL }; #define ZENVUSER 70 #define ZENVHOME CKMAXPATH+12 #define ZENVLOGNAME 74 static char zenvuser[ZENVUSER]; static char zenvhome[ZENVHOME]; static char zenvlogname[ZENVLOGNAME]; #ifdef CK_PAM static char pam_data[500]; struct pam_conv pam_conv = {pam_cb, pam_data}; /* PAM structure */ struct pam_handle * pamh = NULL; /* PAM reference handle */ #endif /* CK_PAM */ int zvuser(name) char *name; { register char *cp = NULL; int x; char *shell; #ifdef GETUSERSHELL _PROTOTYP(char * getusershell, (void) ); #endif /* GETUSERSHELL */ #ifndef NODCLENDUSERSHELL _PROTOTYP(VOID endusershell, (void) ); #endif /* NODCLENDUSERSHELL */ #ifdef CK_PAM int pam_status; const char * reply = NULL; #endif /* CK_PAM */ debug(F111,"user",name,logged_in); if (!name) name = ""; zvuname[0] = NUL; debug(F101,"zvuser ckxsyslog","",ckxsyslog); #ifdef CKSYSLOG debug(F100,"zvuser CKSYSLOG defined","",0); #endif /* CKSYSLOG */ if (logged_in) /* Should not be called if logged in */ return(0); #ifdef CKSYSLOG if (ckxsyslog && ckxlogging) { syslog(LOG_INFO, "login: user %s",name ); } #endif /* CKSYSLOG */ guest = 0; /* Assume not guest */ askpasswd = 0; if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) { debug(F101,"zvuser anonymous ckxanon","",ckxanon); if (!ckxanon) { /* Anonymous login not allowed */ #ifdef CKSYSLOG if (ckxsyslog && ckxlogging) { syslog(LOG_INFO, "login: anonymous login not allowed: %s", clienthost ? clienthost : "(unknown host)" ); } #endif /* CKSYSLOG */ return(0); } if (checkuser("ftp") || checkuser("anonymous")) { debug(F100,"zvuser anon forbidden by ftpusers file","",0); #ifdef CKSYSLOG if (ckxsyslog && ckxlogging) { syslog(LOG_INFO, "login: anonymous login forbidden by ftpusers file: %s", clienthost ? clienthost : "(unknown host)" ); } #endif /* CKSYSLOG */ return(0); } else if ((pw = sgetpwnam("ftp")) != NULL) { debug(F100,"zvuser anon sgetpwnam(ftp) OK","",0); guest = 1; askpasswd = 1; ckstrncpy(zvuname,"anonymous",64); return(1); } else { debug(F100,"zvuser anon sgetpwnam(ftp) FAILED","",0); #ifdef CKSYSLOG if (ckxsyslog && ckxlogging) { syslog(LOG_INFO, "login: anonymous getpwnam(ftp) failed: %s", clienthost ? clienthost : "(unknown host)" ); } #endif /* CKSYSLOG */ return(0); } } pw = sgetpwnam(name); if (pw) { /* Of course some UNIX platforms (like AIX) don't have getusershell(). In that case we can't check if the user's account has been "turned off" or somesuch, e.g. by setting their shell to "/etc/nologin" or somesuch, which runs (usually just printing a message and exiting), but which is not listed in /etc/shells. For that matter, if getusershell() is not available, then probably neither is /etc/shells. */ debug(F100,"zvuser sgetpwnam ok","",0); shell = pw->pw_shell; if (!shell) shell = ""; if (!*shell) shell = _PATH_BSHELL; debug(F110,"zvuser shell",shell,0); #ifdef GETUSERSHELL while ((cp = getusershell()) != NULL) { debug(F110,"zvuser getusershell",cp,0); if ((int)strcmp(cp, shell) == 0) break; } debug(F100,"zvuser endusershell 1","",0); #ifndef NODCLENDUSERSHELL (VOID) endusershell(); #else endusershell(); #endif /* NODCLENDUSERSHELL */ debug(F100,"zvuser endusershell 2","",0); #else /* GETUSERSHELL */ cp = ""; /* Do not refuse if we cannot check */ #endif /* GETUSERSHELL */ x = checkuser(name); debug(F101,"zvuser checkuser","",x); if (cp == NULL) { debug(F100,"zvuser refused 1","",0); pw = (struct passwd *) NULL; #ifdef CKSYSLOG if (ckxsyslog && ckxlogging) { syslog(LOG_INFO, "login: invalid shell %s for %s %s",shell, name, clienthost ? clienthost : "(unknown host)" ); } #endif /* CKSYSLOG */ return(0); } else if (x) { debug(F100,"zvuser refused 2","",0); pw = (struct passwd *) NULL; #ifdef CKSYSLOG if (ckxsyslog && ckxlogging) { syslog(LOG_INFO, "login: %s login forbidden by ftpusers file: %s", name, clienthost ? clienthost : "(unknown host)" ); } #endif /* CKSYSLOG */ return(0); } else { x = 0; #ifdef CK_PAM /* Get PAM authentication details */ debug(F110,"zvuser","calling pam_start",0); if ((pam_status = pam_start(PAM_SERVICE_TYPE,name,&pam_conv,&pamh)) != PAM_SUCCESS) { reply = pam_strerror(NULL, pam_status); debug(F110,"zvuser PAM failure",reply,0); printf("%s\n",reply); #ifdef CKSYSLOG if (ckxsyslog && ckxlogging) { syslog(LOG_INFO, "login: %s refused by PAM \"%s\": %s", name,reply, clienthost ? clienthost : "(unknown host)" ); } #endif /* CKSYSLOG */ return(0); } #endif /* CK_PAM */ askpasswd = 1; ckstrncpy(zvuname,name,64); return(1); } } else { x = 0; debug(F100,"zvuser sgetpwnam NULL","",0); #ifdef CKSYSLOG if (ckxsyslog && ckxlogging) { syslog(LOG_INFO, "login: getpwnam(%s) failed: %s",name, clienthost ? clienthost : "(unknown host)" ); } #endif /* CKSYSLOG */ return(0); } #ifdef FTP_KERBEROS if (auth_type && strcmp(auth_type, "KERBEROS_V4") == 0) { #ifdef COMMENT /* Why sprintf and then printf? */ /* Also, what is kerb_ok? And is the test on it right? */ char buf[CKXLOGBSIZ]; sprintf(buf, "Kerberos user %s%s%s@%s is%s authorized as %s%s", kdata.pname, *kdata.pinst ? "." : "", kdata.pinst, kdata.prealm, (kerb_ok = kuserok(&kdata,name) == 0) ? "" : " not", name, kerb_ok ? "" : "; Password required."); printf("%s", buf); #else printf("Kerberos user %s%s%s@%s is%s authorized as %s%s", kdata.pname, *kdata.pinst ? "." : "", kdata.pinst, kdata.prealm, (kerb_ok = kuserok(&kdata,name) == 0) ? "" : " not", name, kerb_ok ? "" : "; Password required."); #endif /* COMMENT */ if (kerb_ok) return(1); } else return(0); #endif /* FTP_KERBEROS */ } /* Check if the given user is in the forbidden-user file */ static int checkuser(name) char *name; { extern char * userfile; FILE *fd; int i; char line[CKXLOGBSIZ+1]; if (!name) name = ""; i = strlen(name); debug(F111,"checkuser name",name,i); if (!*name) return(1); fd = fopen(userfile ? userfile : _PATH_FTPUSERS, "r"); /* debug(F111,"checkuser userfile",userfile,fd); */ if (fd) { line[0] = '\0'; while (fgets(line, sizeof(line), fd)) { debug(F110,"checkuser line",line,0); if (line[0] <= '#') continue; if (strncmp(line, name, i) == 0) { debug(F110,"checkuser REFUSED",name,0); return(1); } line[0] = '\0'; } (VOID) fclose(fd); } debug(F110,"checkuser OK",name,0); return(0); } /* Z V L O G O U T -- Log out from Internet Kermit Service */ VOID zvlogout() { #ifdef COMMENT /* This could be dangerous */ if (setuid((UID_T)0) < 0) { debug(F100,"zvlogout setuid FAILED","",0); goto bad; } debug(F100,"zvlogout setuid OK","",0); #endif /* COMMENT */ #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_LI && ckxlogging) { cksyslog(SYSLG_LI, 1, "logout",(char *) uidbuf, clienthost); } #endif /* CKSYSLOG */ #ifdef CKWTMP debug(F110,"WTMP logout",cksysline,logged_in); if (logged_in) logwtmp(cksysline, "", ""); #endif /* CKWTMP */ pw = NULL; logged_in = 0; guest = 0; isguest = 0; } #ifdef FTP_KERBEROS kpass(name, p) char *name, *p; { char instance[INST_SZ]; char realm[REALM_SZ]; char tkt_file[20]; KTEXT_ST ticket; AUTH_DAT authdata; unsigned long faddr; struct hostent *hp; if (krb_get_lrealm(realm, 1) != KSUCCESS) return(0); ckstrncpy(tkt_file, TKT_ROOT, 20); ckstrncat(tkt_file, "_ftpdXXXXXX", 20); krb_set_tkt_string(mktemp(tkt_file)); (VOID) ckstrncpy(instance, krb_get_phost(hostname), sizeof(instance)); if ((hp = gethostbyname(instance)) == NULL) return(0); #ifdef HADDRLIST hp = ck_copyhostent(hp); /* safe copy that won't change */ #endif /* HADDRLIST */ bcopy((char *)hp->h_addr, (char *) &faddr, sizeof(faddr)); if (krb_get_pw_in_tkt(name, "", realm, "krbtgt", realm, 1, p) || krb_mk_req(&ticket, "rcmd", instance, realm, 33) || krb_rd_req(&ticket, "rcmd", instance, faddr, &authdata, "") || kuserok(&authdata, name)) { dest_tkt(); return(0); } dest_tkt(); return(1); } #endif /* FTP_KERBEROS */ VOID zsyslog() { #ifdef CKSYSLOG if (ckxsyslog && !ckxlogging) { #ifdef LOG_DAEMON openlog(inserver ? "iksd" : "ckermit", LOG_PID, LOG_DAEMON); #else openlog(inserver ? "iksd" : "ckermit", LOG_PID); #endif /* LOG_DAEMON */ ckxlogging = 1; debug(F100,"zsyslog syslog opened","",0); } #endif /* CKSYSLOG */ } /* Z V P A S S -- Verify password; returns 1 if OK, 0 otherwise */ #ifndef AUTH_USER #define AUTH_USER 3 #endif /* AUTH_USER */ #ifndef AUTH_VALID #define AUTH_VALID 4 #endif /* AUTH_VALID */ #ifdef __FreeBSD__ /* 299 This was necessary in */ #ifndef NODCLINITGROUPS /* FreeBSD 4.4, don't know */ #define NODCLINITGROUPS /* about other versions... */ #endif /* NODCLINITGROUPS */ #endif /* __FreeBSD__ */ int zvpass(p) char *p; { #ifndef NODCLINITGROUPS _PROTOTYP(int initgroups, (const char *, gid_t) ); #endif /* NODCLINITGROUPS */ char *xpasswd, *salt; char * dir = NULL; #ifdef CK_PAM int pam_status; const char * reply = NULL; #endif /* CK_PAM */ if (logged_in || askpasswd == 0) { return(0); } debug(F111,"zvpass",p ? (guest ? p : "xxxxxx") : "(null)",guest); if (!p) p = ""; askpasswd = 0; if (guest && !*p) { /* Guests must specify a password */ #ifdef CKSYSLOG if (ckxsyslog && ckxlogging) { syslog(LOG_INFO, "login: anonymous guests must specify a password" ); } #endif /* CKSYSLOG */ return(0); } if (!guest #ifdef CK_AUTHENTICATION && ck_tn_auth_valid() != AUTH_VALID #endif /* CK_AUTHENTICATION */ ) { /* "ftp" is only account allowed no password */ #ifdef CK_PAM debug(F110,"zvpass","calling pam_set_item(AUTHTOK)",0); if ((pam_status = pam_set_item(pamh,PAM_AUTHTOK,p)) != PAM_SUCCESS) { reply = pam_strerror(pamh, pam_status); debug(F110,"zvpass PAM failure",reply,0); /* if no password given treat as non-fatal error */ /* pam will prompt for password in pam_authenticate() */ if (!p) { printf("%s\n",reply); pam_end(pamh, 0); debug(F100,"zvpass denied","",0); pw = NULL; zvuname[0] = NUL; return(0); } } debug(F110,"zvpass","calling pam_authenticate",0); #ifdef COMMENT if (*p) pam_pw = p; #else /* Make IKSD authentication (using PAM) ask for a password when an invalid username has been given, to avoid disclosing which account names are valid. See #417247 (Debian). */ if (*p #ifdef CK_LOGIN || gotemptypasswd #endif /* CK_LOGIN */ ) pam_pw = p; #endif /* COMMENT */ if ((pam_status = pam_authenticate(pamh, 0)) != PAM_SUCCESS) { reply = pam_strerror(pamh, pam_status); debug(F110,"zvpass PAM failure",reply,0); printf("%s\n",reply); pam_end(pamh, 0); debug(F100,"zvpass denied","",0); pam_pw = NULL; pw = NULL; zvuname[0] = NUL; return(0); } pam_pw = NULL; debug(F110,"zvpass","calling pam_acct_mgmt",0); if ((pam_status = pam_acct_mgmt(pamh, 0)) != PAM_SUCCESS) { reply = pam_strerror(pamh, pam_status); debug(F110,"zvpass PAM failure",reply,0); printf("%s\n",reply); pam_end(pamh, 0); debug(F100,"zvpass denied","",0); pw = NULL; zvuname[0] = NUL; return(0); } debug(F110,"zvpass","PAM validates OK",0); pam_end(pamh,0); #else /* CK_PAM */ if (pw == NULL) salt = "xx"; else salt = pw->pw_passwd; #ifdef HPUX10_TRUSTED xpasswd = bigcrypt(p, salt); #else /* On 64-bit platforms this can give "cast to pointer from integer of different size" warning, but I'm not sure what the effect is at runtime, or what to do about it. */ xpasswd = (char *)crypt(p, salt); #endif /* HPUX10_TRUSTED */ if ( #ifdef FTP_KERBEROS /* null pw_passwd ok if Kerberos password ok */ pw == NULL || ((*pw->pw_passwd != '\0' || strcmp(xpasswd, pw->pw_passwd)) && !kpass(pw->pw_name, p)) #else #ifdef CK_SRP /* check with tpasswd first if there */ pw == NULL || *pw->pw_passwd == '\0' || t_verifypw (pw->pw_name, p) == 0 || (t_verifypw (pw->pw_name, p) < 0 && strcmp (xpasswd, pw->pw_passwd)) #else /* CK_SRP */ /* The strcmp does not catch null passwords! */ (pw == NULL) || (*pw->pw_passwd == '\0') || strcmp(xpasswd, pw->pw_passwd) #endif /* CK_SRP */ #endif /* FTP_KERBEROS */ ) { debug(F100,"zvpass denied","",0); pw = NULL; zvuname[0] = NUL; return(0); } #endif /* CK_PAM */ } (VOID) setgid((GID_T)pw->pw_gid); /* Set group ID */ #ifndef NOINITGROUPS (VOID) initgroups(pw->pw_name, pw->pw_gid); #endif /* NOINITGROUPS */ logged_in = 1; dir = pw->pw_dir; #ifdef CKWTMP /* Open wtmp before chroot */ if (ckxwtmp) { sprintf(cksysline,"iks_%04x", getpid()); /* safe */ logwtmp(cksysline, pw->pw_name, clienthost ? clienthost : "(unknown host)" ); debug(F110,"WTMP login",cksysline,logged_in); } #endif /* CKWTMP */ /* For anonymous users, we chroot to user ftp's home directory unless started with --anonroot:xxx, in which case we chroot to xxx. We must immediately chdir() to the same directory we chroot() to or else the old current directory remains accessible as "." outside the new root. */ if (guest) { if (anonroot) /* Non-default anonymous root */ dir = anonroot; else makestr(&anonroot,dir); errno = 0; debug(F110,"zvpass anon chroot",dir,0); if (chroot(dir) < 0) { debug(F111,"zvpass anon chroot FAILED",dir,errno); goto bad; } errno = 0; if (chdir("/") < 0) { debug(F111,"zvpass anon chdir FAILED",dir,errno); goto bad; } debug(F110,"zvpass anon chroot/chdir OK",dir,0); } else if (chdir(dir) < 0) { /* Not guest */ #ifdef COMMENT if (chdir("/") < 0) { debug(F110,"Non-guest chdir FAILED",dir,0); goto bad; } else printf("?No directory! Logging in with home=/\n"); #else debug(F110,"zvpass non-guest chdir FAILED",dir,0); goto bad; /* Be conservative at first */ #endif /* COMMENT */ } debug(F110,"zvpass non-guest chdir OK",dir,0); if (setuid((UID_T)pw->pw_uid) < 0) { debug(F101,"zvpass setuid FAILED","",pw->pw_uid); goto bad; } debug(F101,"zvpass setuid OK","",pw->pw_uid); guestpass[0] = '\0'; if (guest) { extern int fncact; isguest = 1; fncact = XYFX_R; /* FILE COLLISION = RENAME */ debug(F110,"GUEST fncact=R",p,0); lset(guestpass,"anonymous:",10,32); ckstrncpy(&guestpass[10],p,GUESTPASS-10); home = "/"; printf("Anonymous login.\r\n"); #ifdef SETPROCTITLE /* proctitle declared where? Obviously this code is never compiled. */ sprintf(proctitle, "%s: anonymous/%.*s", clienthost ? clienthost : "(unk)", sizeof(proctitle) - sizeof(clienthost) - sizeof(": anonymous/"), p); setproctitle(proctitle); #endif /* SETPROCTITLE */ #ifdef CKSYSLOG if (ckxsyslog && ckxlogging) { syslog(LOG_INFO, "login: anonymous %s %s", clienthost ? clienthost : "(unknown host)", p ); } #endif /* CKSYSLOG */ } else { /* Real user */ isguest = 0; home = dir; ckstrncpy(guestpass,zvuname,GUESTPASS); printf("User %s logged in.\r\n", pw->pw_name); #ifdef SETPROCTITLE /* not used */ sprintf(proctitle, "%s: %s", clienthost ? clienthost : "(unk)", pw->pw_name ); setproctitle(proctitle); #endif /* SETPROCTITLE */ #ifdef CKSYSLOG if (ckxsyslog && ckxlogging) syslog(LOG_INFO, "login: %s %s", pw->pw_name, clienthost ? clienthost : "(unknown host)" ); #endif /* CKSYSLOG */ } ckstrncpy(zvhome,home,CKMAXPATH); /* Set environment variables */ #ifndef NOPUTENV ckmakmsg(zenvuser,ZENVUSER,"USER=",zvuname,NULL,NULL); putenv((char *)zenvuser); ckmakmsg(zenvlogname,ZENVLOGNAME,"LOGNAME=",zvuname,NULL,NULL); putenv((char *)zenvlogname); ckmakmsg(zenvhome,ZENVHOME,"HOME=",zvhome,NULL,NULL); putenv((char *)zenvhome); #endif /* NOPUTENV */ /* homdir = (char *)zvhome; */ ckstrncpy((char *)uidbuf,(char *)zvuname,64); (VOID) umask(defumask); #ifdef IKSDB if (ikdbopen) { char * p2; int k; extern char dbrec[]; extern unsigned long myflags; extern unsigned int mydbslot; extern struct iksdbfld dbfld[]; #ifdef CK_AUTHENTICATION extern unsigned long myamode, myatype; #endif /* CK_AUTHENTICATION */ myflags |= DBF_LOGGED; #ifdef DEBUG if (deblog) { debug(F101,"zvpass guest","",guest); debug(F111,"zvpass zvuname",zvuname,0); debug(F110,"zvpass guestpass",guestpass,0); debug(F110,"zvpass dir",dir,0); debug(F110,"zvpass home",home,0); debug(F110,"zvpass anonroot",anonroot,0); } #endif /* DEBUG */ p2 = guest ? guestpass : zvuname; if (guest) { p2 = (char *)guestpass; myflags &= ~DBF_USER; } else { p2 = (char *)zvuname; myflags |= DBF_USER; } k = strlen(p2); strncpy(&dbrec[DB_ULEN],ulongtohex((unsigned long)k,4),4); lset(&dbrec[dbfld[db_USER].off],p2,1024,32); strncpy(&dbrec[DB_FLAGS],ulongtohex(myflags,4),4); #ifdef CK_AUTHENTICATION myamode = ck_tn_auth_valid(); strncpy(&dbrec[DB_AMODE],ulongtohex(myamode,4),4); myatype = ck_tn_authenticated(); strncpy(&dbrec[DB_ATYPE],ulongtohex(myatype,4),4); #endif /* CK_AUTHENTICATION */ if (guest) { p2 = dir; } else { p2 = zgtdir(); if (!p2) p2 = ""; if (!*p2) p2 = home; } strncpy(&dbrec[DB_DLEN], ulongtohex((unsigned long)strlen(p2),4), 4 ); lset(&dbrec[dbfld[db_DIR].off],p2,1024,32); updslot(mydbslot); } #endif /* IKSDB */ return(1); bad: /* Common failure exit */ zvuname[0] = NUL; zvlogout(); return(0); } #endif /* CK_LOGIN */ /* Buggy Xenix 2.3.4 cc needs this line after the endif */ ckupty.c0000644000015300001460000014165511575426765011402 0ustar fdckermitchar *ckptyv = "Pseudoterminal support, 9.0.101, 13 Jun 2011"; /* C K U P T Y -- C-Kermit pseudoterminal control functions for UNIX */ /* Last update: Mon Jun 13 11:32:52 2011 */ /* Copyright 1995 by the Massachusetts Institute of Technology. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of M.I.T. not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. Furthermore if you modify this software you must label your software as modified software and not distribute it in such a fashion that it might be confused with the original M.I.T. software. M.I.T. makes no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. Modified for use in C-Kermit, and new material added, by: Jeffrey Altman Secure Endpoints Inc., New York City November 1999 Parameterized for pty file descriptor and function code, Frank da Cruz, Columbia University, New York City Dec 2006 - Sep 2009 */ /* Built and tested successully on: . 4.4BSD, including BSDI/OS, NetBSD, FreeBSD, OpenBSD, Mac OS X . AIX 4.1 and later . DG/UX 5.4R4.11 . Digital UNIX 3.2 and 4.0 . HP-UX 9.00 and later . IRIX 6.0 and later . Linux . Mac OS X 10.4 . NeXTSTEP 3.x . OpenBSD . QNX 4.25 (except PTY process termination not detected) . SCO OSR5.0.5 . SCO Unixware 7 . SINIX 5.42 . Solaris 2.x and 7 . SunOS 4.1.3 Failures include: . SCO UNIX 3.2v4.2 (compile fails with syntax error in ) . HP-UX 8.00 and earlier (no vhangup or ptsname routines) */ #include "ckcsym.h" #include "ckcdeb.h" /* To pick up NETPTY definition */ #ifndef NETPTY /* Selector for PTY support */ char * ptyver = "No PTY support"; #else /* (rest of this module...) */ char * ptyver = "PTY support 8.0.016, 22 Aug 2007"; /* These will no doubt need adjustment... */ #ifndef NEXT #define HAVE_SETSID #endif /* NEXT */ #define HAVE_KILLPG #define HAVE_TTYNAME #define HAVE_WAITPID #ifdef SUNOS41 #define BSD44ORPOSIX #endif /* SUNOS41 */ #ifndef USE_TERMIO #ifdef LINUX #define USE_TERMIO #else #ifdef ATTSV #define USE_TERMIO #else #ifdef HPUX #define USE_TERMIO #else #ifdef AIX #define USE_TERMIO #else #ifdef BSD44ORPOSIX #define USE_TERMIO #else #ifdef IRIX60 #define USE_TERMIO #else #ifdef QNX #define USE_TERMIO #endif /* QNX */ #endif /* IRIX60 */ #endif /* BSD44ORPOSIX */ #endif /* AIX */ #endif /* HPUX */ #endif /* ATTSV */ #endif /* LINUX */ #endif /* USE_TERMIO */ #ifdef QNX #include #endif /* QNX */ #ifdef USE_TERMIO #define POSIX_TERMIOS /* Seems to be a misnomer */ #endif /* USE_TERMIO */ #ifdef NEXT #ifndef GETPGRP_ONEARG #define GETPGRP_ONEARG #endif /* GETPGRP_ONEARG */ #endif /* NEXT */ #ifdef WANT_UTMP /* See ckupty.h */ /* WANT_UTMP is not defined because (a) the utmp/wtmp junk is the most nonportable part of this module, and (b) we're not logging anybody in, we're just running a process, and don't need to write utmp/wtmp records. */ #ifndef HAVE_SETUTXENT /* Who has */ #ifdef SOLARIS #define HAVE_SETUTXENT #else #ifdef IRIX60 #define HAVE_SETUTXENT #else #ifdef CK_SCOV5 #define HAVE_SETUTXENT #else #ifdef HPUX10 #define HAVE_SETUTXENT #else #ifdef UNIXWARE #define HAVE_SETUTXENT #else #ifdef IRIX60 #define HAVE_SETUTXENT #endif /* IRIX60 */ #endif /* UNIXWARE */ #endif /* HPUX10 */ #endif /* CK_SCOV5 */ #endif /* IRIX60 */ #endif /* SOLARIS */ #endif /* HAVE_SETUTXENT */ #ifndef HAVE_UTHOST /* Does utmp include ut_host[]? */ #ifdef HAVE_SETUTXENT /* utmpx always does */ #define HAVE_UTHOST #else #ifdef LINUX /* Linux does */ #define HAVE_UTHOST #else #ifdef SUNOS4 /* SunOS does */ #define HAVE_UTHOST #else #ifdef AIX41 /* AIX 4.1 and later do */ #define HAVE_UTHOST #endif /* AIX41 */ #endif /* SUNOS4 */ #endif /* LINUX */ #endif /* HAVE_SETUTXENT */ #endif /* HAVE_UTHOST */ #ifndef HAVE_UT_HOST #ifndef NO_UT_HOST #define NO_UT_HOST #endif /* NO_UT_HOST */ #endif /* HAVE_UT_HOST */ #endif /* WANT_UTMP */ #ifdef LINUX #define CK_VHANGUP #define HAVE_SYS_SELECT_H #define HAVE_GETUTENT #define HAVE_SETUTENT #define HAVE_UPDWTMP #endif /* LINUX */ #ifdef HPUX10 #define CK_VHANGUP #define VHANG_FIRST #define HAVE_PTSNAME #ifndef HAVE_PTYTRAP #define HAVE_PTYTRAP #endif /* HAVE_PTYTRAP */ #else #ifdef HPUX9 #define CK_VHANGUP #define VHANG_FIRST #define HAVE_PTSNAME #ifndef HAVE_PTYTRAP #define HAVE_PTYTRAP #endif /* HAVE_PTYTRAP */ #endif /* HPUX9 */ #endif /* HPUX10 */ #ifdef SUNOS4 #define CK_VHANGUP #define NO_UT_PID #define VHANG_FIRST #endif /* SUNOS4 */ #ifdef IRIX60 #define CK_VHANGUP #define HAVE__GETPTY #endif /* IRIX60 */ #ifdef SINIX #define HAVE_STREAMS #define HAVE_GRANTPT #define HAVE_PTSNAME #define PUSH_PTEM #define PUSH_LDTERM #define PUSH_TTCOMPAT #endif /* SINIX */ #ifdef ultrix #define MUST_SETPGRP #endif /* ultrix */ #ifdef QNX #define MUST_SETPGRP #define NO_DEVTTY #define INIT_SPTY #endif /* QNX */ #ifdef LINUX #ifdef HAVE_PTMX #define HAVE_GRANTPT #define HAVE_PTSNAME #endif /* HAVE_PTMX */ #else #ifdef HAVE_STREAMS #define HAVE_PTMX #endif /* HAVE_STREAMS */ #endif /* LINUX */ #include "ckupty.h" #ifdef PTYNOBLOCK #ifndef O_NDELAY #ifdef O_NONBLOCK #define O_NDELAY O_NONBLOCK #endif /* O_NONBLOCK */ #endif /* O_NDELAY */ #else /* PTYNOBLOCK */ #ifdef O_NDELAY #undef O_NDELAY #endif /* O_NDELAY */ #define O_NDELAY 0 #endif /* PTYNOBLOCK */ #ifndef ONLCR #define ONLCR 0 #endif /* ONLCR */ #ifdef CK_WAIT_H #include #endif /* CK_WAIT_H */ #ifdef STREAMSPTY #ifndef INIT_SPTY #define INIT_SPTY #endif /* INIT_SPTY */ #include #include #include /* Make sure we don't get the BSD version */ #ifdef HAVE_SYS_TTY_H #include "/usr/include/sys/tty.h" #endif /* HAVE_SYS_TTY_H */ #ifdef HAS_PTYVAR /* Where is this set? */ #include #else /* HAS_PTYVAR */ #ifndef TIOCPKT_FLUSHWRITE #define TIOCPKT_FLUSHWRITE 0x02 #define TIOCPKT_NOSTOP 0x10 #define TIOCPKT_DOSTOP 0x20 #define TIOCPKT_IOCTL 0x40 #endif /* TIOCPKT_FLUSHWRITE */ #endif /* HAS_PTYVAR */ #ifdef HAVE_TTY_H #include #endif /* HAVE_TTY_H */ /* Because of the way ptyibuf is used with streams messages, we need ptyibuf+1 to be on a full-word boundary. The following weirdness is simply to make that happen. */ long ptyibufbuf[BUFSIZ/sizeof(long)+1]; char *ptyibuf = ((char *)&ptyibufbuf[1])-1; char *ptyip = ((char *)&ptyibufbuf[1])-1; char ptyibuf2[BUFSIZ]; unsigned char ctlbuf[BUFSIZ]; struct strbuf strbufc, strbufd; int readstream(); #else /* ! STREAMSPTY */ /* I/O data buffers, pointers, and counters. */ char ptyibuf[BUFSIZ], *ptyip = ptyibuf; char ptyibuf2[BUFSIZ]; #endif /* ! STREAMSPTY */ #ifndef USE_TERMIO struct termbuf { struct sgttyb sg; struct tchars tc; struct ltchars ltc; int state; int lflags; } termbuf, termbuf2; #define cfsetospeed(tp,val) (tp)->sg.sg_ospeed = (val) #define cfsetispeed(tp,val) (tp)->sg.sg_ispeed = (val) #define cfgetospeed(tp) (tp)->sg.sg_ospeed #define cfgetispeed(tp) (tp)->sg.sg_ispeed #else /* USE_TERMIO */ #ifdef SYSV_TERMIO #define termios termio #endif /* SYSV_TERMIO */ #ifndef TCSANOW #ifdef TCSETS #define TCSANOW TCSETS #define TCSADRAIN TCSETSW #define tcgetattr(f, t) ioctl(f, TCGETS, (char *)t) #else /* TCSETS */ #ifdef TCSETA #define TCSANOW TCSETA #define TCSADRAIN TCSETAW #define tcgetattr(f,t) ioctl(f,TCGETA,(char *)t) #else /* TCSETA */ #define TCSANOW TIOCSETA #define TCSADRAIN TIOCSETAW #define tcgetattr(f,t) ioctl(f,TIOCGETA,(char *)t) #endif /* TCSETA */ #endif /* TCSETS */ #define tcsetattr(f,a,t) ioctl(f,a,t) #define cfsetospeed(tp,val) (tp)->c_cflag &= ~CBAUD;(tp)->c_cflag|=(val) #define cfgetospeed(tp) ((tp)->c_cflag & CBAUD) #ifdef CIBAUD #define cfsetispeed(tp,val) \ (tp)->c_cflag &= ~CIBAUD; (tp)->c_cflag |= ((val)<c_cflag & CIBAUD)>>IBSHIFT) #else /* CIBAUD */ #define cfsetispeed(tp,val) (tp)->c_cflag &= ~CBAUD; (tp)->c_cflag|=(val) #define cfgetispeed(tp) ((tp)->c_cflag & CBAUD) #endif /* CIBAUD */ #endif /* TCSANOW */ struct termios termbuf, termbuf2; /* pty control structure */ #ifdef INIT_SPTY static int spty = -1; #endif /* INIT_SPTY */ #endif /* USE_TERMIO */ #ifdef QNX /* 299 */ #ifndef IXANY #define IXANY 0 #endif /* IXANY */ #endif /* QNX */ static int msg = 0; /* Variables available to other modules */ int pty_fork_active = 0; /* pty fork is active */ PID_T pty_fork_pid = -1; /* pty fork pid */ int pty_slave_fd = -1; /* pty slave file descriptor */ int pty_master_fd = -1; /* pty master file descriptor */ /* termbuf routines (begin) */ /* init_termbuf() copy_termbuf(cp) set_termbuf() These three routines are used to get and set the "termbuf" structure to and from the kernel. init_termbuf() gets the current settings. copy_termbuf() hands in a new "termbuf" to write to the kernel, and set_termbuf() writes the structure into the kernel. */ VOID init_termbuf(fd) int fd; { int ttyfd; int rc = 0; ttyfd = fd; #ifdef HAVE_STREAMS debug(F100,"init_termbuf HAVE_STREAMS","",0); #else debug(F100,"init_termbuf HAVE_STREAMS NOT DEFINED","",0); #endif /* HAVE_STREAMS */ #ifdef STREAMSPTY debug(F100,"init_termbuf STREAMSPTY","",0); #else debug(F100,"init_termbuf STREAMSPTY NOT DEFINED","",0); #endif /* STREAMSPTY */ #ifdef INIT_SPTY debug(F100,"init_termbuf INIT_SPTY","",0); #else debug(F100,"init_termbuf INIT_SPTY NOT DEFINED","",0); #endif /* INIT_SPTY */ debug(F101,"init_termbuf ttyfd","",ttyfd); #ifdef INIT_SPTY debug(F101,"init_termbuf spty","",spty); #endif /* INIT_SPTY */ memset(&termbuf,0,sizeof(termbuf)); memset(&termbuf2,0,sizeof(termbuf2)); #ifndef USE_TERMIO rc = ioctl(ttyfd, TIOCGETP, (char *)&termbuf.sg); rc |= ioctl(ttyfd, TIOCGETC, (char *)&termbuf.tc); rc |= ioctl(ttyfd, TIOCGLTC, (char *)&termbuf.ltc); #ifdef TIOCGSTATE rc |= ioctl(ttyfd, TIOCGSTATE, (char *)&termbuf.state); #endif /* TIOCGSTATE */ #else /* USE_TERMIO */ errno = 0; #ifdef INIT_SPTY rc = tcgetattr(spty, &termbuf); debug(F111,"init_termbuf() tcgetattr(spty)",ckitoa(rc),errno); #else rc = tcgetattr(ttyfd, &termbuf); debug(F111,"init_termbuf() tcgetattr(ttyfd)",ckitoa(rc),errno); #endif /* INIT_SPTY */ #endif /* USE_TERMIO */ if (!rc) termbuf2 = termbuf; } #ifdef TIOCPKT_IOCTL VOID copy_termbuf(cp, len) char *cp; int len; { if (len > sizeof(termbuf)) len = sizeof(termbuf); memcpy((char *)&termbuf, cp, len); termbuf2 = termbuf; } #endif /* TIOCPKT_IOCTL */ VOID set_termbuf(fd) int fd; { /* Only make the necessary changes. */ int x; int ttyfd; ttyfd = fd; debug(F101,"set_termbuf ttyfd","",ttyfd); #ifdef INIT_SPTY debug(F101,"set_termbuf spty","",spty); #endif /* INIT_SPTY */ #ifndef USE_TERMIO debug(F100,"set_termbuf USE_TERMIO","",0); if (memcmp((char *)&termbuf.sg, (char *)&termbuf2.sg, sizeof(termbuf.sg))) ioctl(ttyfd, TIOCSETN, (char *)&termbuf.sg); if (memcmp((char *)&termbuf.tc, (char *)&termbuf2.tc, sizeof(termbuf.tc))) ioctl(ttyfd, TIOCSETC, (char *)&termbuf.tc); if (memcmp((char *)&termbuf.ltc, (char *)&termbuf2.ltc, sizeof(termbuf.ltc))) ioctl(ttyfd, TIOCSLTC, (char *)&termbuf.ltc); if (termbuf.lflags != termbuf2.lflags) ioctl(ttyfd, TIOCLSET, (char *)&termbuf.lflags); #else /* USE_TERMIO */ x = memcmp((char *)&termbuf, (char *)&termbuf2, sizeof(termbuf)); debug(F101,"set_termbuf !USE_TERMIO memcmp","",x); x = 1; /* Force this */ if (x) { int x; errno = 0; #ifdef INIT_SPTY debug(F100,"set_termbuf INIT_SPTY","",0); x = tcsetattr(spty, TCSANOW, &termbuf); debug(F111,"set_termbuf tcsetattr(spty)",ckitoa(x),errno); #else debug(F100,"set_termbuf !INIT_SPTY","",0); x = tcsetattr(ttyfd, TCSANOW, &termbuf); debug(F111,"set_termbuf tcsetattr(ttyfd)",ckitoa(x),errno); #endif /* INIT_SPTY */ } #endif /* USE_TERMIO */ } /* termbuf routines (end) */ VOID ptyint_vhangup() { #ifdef CK_VHANGUP #ifdef CK_POSIX_SIG struct sigaction sa; /* Initialize "sa" structure. */ sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sa.sa_handler = SIG_IGN; sigaction(SIGHUP, &sa, (struct sigaction *)0); vhangup(); sa.sa_handler = SIG_DFL; sigaction(SIGHUP, &sa, (struct sigaction *)0); #else /* CK_POSIX_SIG */ signal(SIGHUP,SIG_IGN); vhangup(); signal(SIGHUP,SIG_DFL); #endif /* CK_POSIX_SIG */ #endif /* CK_VHANGUP */ } /* This routine is called twice. It's not particularly important that the setsid() or TIOCSCTTY ioctls succeed (they may not the second time), but rather that we have a controlling terminal at the end. It is assumed that vhangup doesn't exist and confuse the process's notion of controlling terminal on any system without TIOCNOTTY. That is, either vhangup() leaves the controlling terminal in tact, breaks the association completely, or the system provides TIOCNOTTY to get things back into a reasonable state. In practice, vhangup() either breaks the association completely or doesn't effect controlling terminals, so this condition is met. */ long ptyint_void_association() { int con_fd; #ifdef HAVE_SETSID debug(F110, "ptyint_void_association()", "setsid()", 0 ); setsid(); #endif /* HAVE_SETSID */ #ifndef NO_DEVTTY /* Void tty association first */ #ifdef TIOCNOTTY con_fd = open("/dev/tty", O_RDWR); debug(F111, "ptyint_void_association() open(/dev/tty,O_RDWR)", "/dev/tty", con_fd); if (con_fd >= 0) { ioctl(con_fd, TIOCNOTTY, 0); close(con_fd); } #ifdef DEBUG else debug(F101, "ptyint_void_association() open() errno","",errno); #endif /* DEBUG */ #endif /* TIOCNOTTY */ #endif /* NO_DEVTTY */ return(0); } /* PID may be zero for unknown.*/ long pty_cleanup(slave, pid, update_utmp) char *slave; int pid; int update_utmp; { #ifdef VHANG_LAST int retval, fd; #endif /* VHANG_LAST */ debug(F111,"pty_cleanup()",slave,pid); #ifdef WANT_UTMP if (update_utmp) pty_update_utmp(PTY_DEAD_PROCESS, 0, "", slave, (char *)0, PTY_UTMP_USERNAME_VALID ); #endif /* WANT_UTMP */ #ifdef SETUID chmod(slave, 0666); chown(slave, 0, 0); #endif /* SETUID */ #ifdef HAVE_REVOKE revoke(slave); /* Revoke isn't guaranteed to send a SIGHUP to the processes it dissociates from the terminal. The best solution without a Posix mechanism for forcing a hangup is to killpg() the process group of the pty. This will at least kill the shell and hopefully, the child processes. This is not always the case, however. If the shell puts each job in a process group and doesn't pass along SIGHUP, all processes may not die. */ if (pid > 0) { #ifdef HAVE_KILLPG killpg(pid, SIGHUP); #else kill(-(pid), SIGHUP); #endif /*HAVE_KILLPG*/ } #else /* HAVE_REVOKE*/ #ifdef VHANG_LAST { int status; #ifdef CK_POSIX_SIG sigset_t old, new; sigemptyset(&new); sigaddset(&new, SIGCHLD); sigprocmask(SIG_BLOCK, &new, &old); #else /*CK_POSIX_SIG*/ int mask = sigblock(sigmask(SIGCHLD)); #endif /*CK_POSIX_SIG*/ switch (retval = fork()) { case -1: #ifdef CK_POSIX_SIG sigprocmask(SIG_SETMASK, &old, 0); #else /*CK_POSIX_SIG*/ sigsetmask(mask); #endif /*CK_POSIX_SIG*/ return errno; case 0: ptyint_void_association(); if (retval = (pty_open_ctty(slave, &fd, -1))) exit(retval); ptyint_vhangup(); exit(0); break; default: #ifdef HAVE_WAITPID waitpid(retval, &status, 0); #else /*HAVE_WAITPID*/ wait(&status); #endif /* HAVE_WAITPID */ #ifdef CK_POSIX_SIG sigprocmask(SIG_SETMASK, &old, 0); #else /*CK_POSIX_SIG*/ sigsetmask(mask); #endif /*CK_POSIX_SIG*/ break; } } #endif /*VHANG_LAST*/ #endif /* HAVE_REVOKE*/ #ifndef HAVE_STREAMS slave[strlen("/dev/")] = 'p'; #ifdef SETUID chmod(slave, 0666); chown(slave, 0, 0); #endif /* SETUID */ #endif /* HAVE_STREAMS */ return(0); } long pty_getpty(fd, slave, slavelength) int slavelength; int *fd; char *slave; { char *cp; char *p; int i, ptynum; struct stat stb; #ifndef HAVE_OPENPTY #ifndef HAVE__GETPTY char slavebuf[1024]; #endif /* HAVE__GETPTY */ #endif /* HAVE_OPENPTY */ #ifdef HAVE__GETPTY char *slaveret; /* Temp to hold pointer to slave */ #endif /*HAVE__GETPTY*/ #ifdef HAVE_OPENPTY int slavefd; pty_master_fd = -1; debug(F100,"HAVE_OPENPTY","",0); if (openpty(fd, &slavefd, slave, (struct termios *)0, (struct winsize *)0 ) ) { pty_master_fd = *fd; return(1); } close(slavefd); return(0); #else /* HAVE_OPENPTY */ #ifdef HAVE__GETPTY /* This code is included for Irix; as of version 5.3, Irix has /dev/ptmx, but it fails to work properly; even after calling unlockpt, root gets permission denied opening the pty. The code to support _getpty should be removed if Irix gets working streams ptys in favor of maintaining the least needed code paths. */ debug(F100,"HAVE__GETPTY","",0); if ((slaveret = _getpty(fd, O_RDWR | O_NDELAY, 0600, 0)) == 0) { *fd = -1; return(PTY_GETPTY_NOPTY); } if (strlen(slaveret) > slavelength - 1) { close(*fd); *fd = -1; return(PTY_GETPTY_SLAVE_TOOLONG); } else { ckstrncpy(slave, slaveret, slavelength); } return(0); #else /* HAVE__GETPTY */ *fd = open("/dev/ptym/clone", O_RDWR|O_NDELAY); /* HPUX */ if (*fd >= 0) { debug(F110,"pty_getpty()","open(/dev/ptym/clone) success",0); goto have_fd; } #ifdef HAVE_PTMX debug(F100,"HAVE_PTMX","",0); *fd = open("/dev/ptmx",O_RDWR|O_NDELAY); if (*fd >= 0) { debug(F110,"pty_getpty()","open(/dev/ptmx) success",0); goto have_fd; } #endif /* HAVE_PTMX */ *fd = open("/dev/ptc", O_RDWR|O_NDELAY); /* AIX */ if (*fd >= 0) { debug(F110,"pty_getpty()","open(/dev/ptc) success",0); goto have_fd; } *fd = open("/dev/pty", O_RDWR|O_NDELAY); /* sysvimp */ if (*fd >= 0) debug(F110,"pty_getpty()","open(/dev/pty) success",0); have_fd: /* This would be the pty master */ debug(F101,"pty_getpty fd(A)","",*fd); if (*fd >= 0) { pty_master_fd = *fd; #ifdef HAVE_GRANTPT #ifdef HAVE_PTMX debug(F100,"HAVE_GRANTPT","",0); if (grantpt(*fd) || unlockpt(*fd)) return(PTY_GETPTY_STREAMS); #endif /* HAVE_PTMX */ #endif /* HAVE_GRANTPT */ #ifdef HAVE_PTSNAME debug(F100,"HAVE_PTSNAME","",0); p = (char *)ptsname(*fd); debug(F110,"pty_getpty() ptsname()",p,0); #else #ifdef HAVE_TTYNAME debug(F100,"HAVE_TTYNAME","",0); p = ttyname(*fd); debug(F110,"pty_getpty() ttyname()",p,0); #else /* If we don't have either what do we do? */ return(PTY_GETPTY_NOPTY); /* punt */ #endif /* HAVE_TTYNAME */ #endif /* HAVE_PTSNAME */ if (p) { if (strlen(p) > slavelength - 1) { close (*fd); *fd = -1; return(PTY_GETPTY_SLAVE_TOOLONG); } ckstrncpy(slave, p, slavelength); return(0); } if (fstat(*fd, &stb) < 0) { close(*fd); return(PTY_GETPTY_FSTAT); } ptynum = (int)(stb.st_rdev&0xFF); sprintf(slavebuf, "/dev/ttyp%x", ptynum); /* safe */ if (strlen(slavebuf) > slavelength - 1) { close(*fd); *fd = -1; return(PTY_GETPTY_SLAVE_TOOLONG); } debug(F110,"pty_getpty() slavebuf",slavebuf,0); ckstrncpy(slave, slavebuf, slavelength); return(0); } else { for (cp = "pqrstuvwxyzPQRST";*cp; cp++) { sprintf(slavebuf,"/dev/ptyXX"); /* safe */ slavebuf[sizeof("/dev/pty") - 1] = *cp; slavebuf[sizeof("/dev/ptyp") - 1] = '0'; if (stat(slavebuf, &stb) < 0) break; for (i = 0; i < 16; i++) { slavebuf[sizeof("/dev/ptyp") - 1] = "0123456789abcdef"[i]; errno = 0; *fd = open(slavebuf, O_RDWR|O_NDELAY); if (*fd < 0) { debug(F111,"pty_getpty() pty master open error", slavebuf,errno); continue; } debug(F111,"pty_getpty() found pty master",slavebuf,*fd); slavebuf[sizeof("/dev/") - 1] = 't'; /* got pty */ if (strlen(slavebuf) > slavelength -1) { close(*fd); *fd = -1; return(PTY_GETPTY_SLAVE_TOOLONG); } ckstrncpy(slave, slavebuf, slavelength); debug(F110,"pty_getpty slave name",slave,0); pty_master_fd = *fd; return(0); } } return(PTY_GETPTY_NOPTY); } #endif /*HAVE__GETPTY*/ #endif /* HAVE_OPENPTY */ } long pty_init() { #ifdef HAVE_PTYM static char dummy; debug(F100,"HAVE_PTYM","",0); tty_bank = &master_name[strlen("/dev/ptym/pty")]; tty_num = &master_name[strlen("/dev/ptym/ptyX")]; slave_bank = &slave_name[strlen("/dev/pty/tty")]; slave_num = &slave_name[strlen("/dev/pty/ttyX")]; #endif return(0L); } /* The following is an array of modules that should be pushed on the stream. See configure.in for caviats and notes about when this array is used and not used. */ #ifdef HAVE_STREAMS #ifndef HAVE_LINE_PUSH static char *push_list[] = { #ifdef PUSH_PTEM "ptem", #endif #ifdef PUSH_LDTERM "ldterm", #endif #ifdef PUSH_TTCOMPAT "ttcompat", #endif 0 }; #endif /* HAVE_LINE_PUSH */ #endif /* HAVE_STREAMS */ long pty_initialize_slave (fd) int fd; { #ifdef POSIX_TERMIOS #ifndef ultrix struct termios new_termio; #else struct sgttyb b; #endif /* ultrix */ #else struct sgttyb b; #endif /* POSIX_TERMIOS */ int pid; #ifdef POSIX_TERMIOS #ifndef ultrix int rc; #endif /* ultrix */ #endif /* POSIX_TERMIOS */ debug(F111,"pty_initialize_slave()","fd",fd); #ifdef HAVE_STREAMS #ifdef HAVE_LINE_PUSH while (ioctl(fd,I_POP,0) == 0) ; /* Clear out any old lined's */ if (line_push(fd) < 0) { debug(F110,"pty_initialize_slave()","line_push() failed",0); close(fd); fd = -1; return(PTY_OPEN_SLAVE_LINE_PUSHFAIL); } #else /*No line_push */ { char **module = &push_list[0]; while (*module) { if (ioctl(fd, I_PUSH, *(module++)) < 0) { debug(F110,"pty_initialize_slave()","ioctl(I_PUSH) failed",0); return(PTY_OPEN_SLAVE_PUSH_FAIL); } } } #endif /*LINE_PUSH*/ #endif /*HAVE_STREAMS*/ /* Under Ultrix 3.0, the pgrp of the slave pty terminal needs to be set explicitly. Why rlogind works at all without this on 4.3BSD is a mystery. */ #ifdef GETPGRP_ONEARG pid = getpgrp(getpid()); #else pid = getpgrp(); #endif /* GETPGRP_ONEARG */ debug(F111,"pty_initialize_slave()","pid",pid); #ifdef TIOCSPGRP ioctl(fd, TIOCSPGRP, &pid); #endif /* TIOCSPGRP */ #ifdef POSIX_TERMIOS #ifndef ultrix tcsetpgrp(fd, pid); errno = 0; rc = tcgetattr(fd,&new_termio); debug(F111,"pty_initialize_slave tcgetattr(fd)",ckitoa(rc),errno); if (rc == 0) { new_termio.c_cc[VMIN] = 1; new_termio.c_cc[VTIME] = 0; rc = tcsetattr(fd,TCSANOW,&new_termio); debug(F111,"pty_initialize_slave tcsetattr(fd)",ckitoa(rc),errno); } #endif /* ultrix */ #endif /* POSIX_TERMIOS */ return(0L); } #ifdef WANT_UTMP long pty_logwtmp (tty, user, host) char *user, *tty, *host; { #ifdef HAVE_LOGWTMP logwtmp(tty,user,host); return(0); #else struct utmp ut; char *tmpx; char utmp_id[5]; int loggingin = user[0]; /* Will be empty for logout */ #ifndef NO_UT_HOST strncpy(ut.ut_host, host, sizeof(ut.ut_host)); #endif /* NO_UT_HOST */ strncpy(ut.ut_line, tty, sizeof(ut.ut_line)); ut.ut_time = time(0); #ifndef NO_UT_PID ut.ut_pid = getpid(); strncpy(ut.ut_user, user, sizeof(ut.ut_user)); tmpx = tty + strlen(tty) - 2; ckmakmsg(utmp_id,5,"kr",tmpx,NULL,NULL); strncpy(ut.ut_id, utmp_id, sizeof(ut.ut_id)); ut.ut_pid = (loggingin ? getpid() : 0); ut.ut_type = (loggingin ? USER_PROCESS : DEAD_PROCESS); #else strncpy(ut.ut_name, user, sizeof(ut.ut_name)); #endif /* NO_UT_PID */ return(ptyint_update_wtmp(&ut, host, user)); #endif /* HAVE_LOGWTMP */ } #endif /* WANT_UTMP */ /* This routine is called twice. It's not particularly important that the setsid() or TIOCSCTTY ioctls succeed (they may not the second time), but rather that we have a controlling terminal at the end. It is assumed that vhangup doesn't exist and confuse the process's notion of controlling terminal on any system without TIOCNOTTY. That is, either vhangup() leaves the controlling terminal in tact, breaks the association completely, or the system provides TIOCNOTTY to get things back into a reasonable state. In practice, vhangup() either breaks the association completely or doesn't effect controlling terminals, so this condition is met. */ long pty_open_ctty(slave, fd, fc) char * slave; int *fd; int fc; { int retval; debug(F110,"pty_open_ctty() slave",slave,0); /* First, dissociate from previous terminal */ if ((retval = ptyint_void_association()) != 0) { debug(F111, "pty_open_ctty()", "ptyint_void_association() failed", retval ); return(retval); } #ifdef MUST_SETPGRP /* The Ultrix (and other BSD tty drivers) require the process group to be zero in order to acquire the new tty as a controlling tty. */ setpgrp(0,0); debug(F101,"pty_open_ctty MUST_SETPGRP setpgrp(0,0)","",errno); #endif /* MUST_SETPGRP */ errno = 0; *fd = open(slave, O_RDWR); debug(F111,"pty_open_ctty open(slave) fd",slave,*fd); if (*fd < 0) { debug(F111,"pty_open_ctty() open failure", slave, errno); return(PTY_OPEN_SLAVE_OPENFAIL); } #ifdef SOLARIS /* This forces the job to have a controlling terminal. */ close(*fd); *fd = open(slave, O_RDWR); debug(F111,"pty_open_ctty close/open(slave) fd",slave,*fd); #ifdef DEBUG /* This shows that /dev/tty exists */ if (deblog) { int x; x = open("/dev/tty", O_RDWR); debug(F111,"pty_open_ctty open(/dev/tty) fd",slave,x); if (x < 0) debug(F111,"pty_open_ctty open(/dev/tty) errno","",errno); debug(F110,"pty_open_ctty ttyname(/dev/tty)",ttyname(x),0); if (x > -1) close(x); } #endif /* DEBUG */ #endif /* SOLARIS */ #ifdef MUST_SETPGRP setpgrp(0, getpid()); #endif /* MUST_SETPGRP */ #ifdef TIOCSCTTY if ( #ifdef COMMENT fc == 0 #else 1 #endif /* COMMENT */ ) { /* TIOCSCTTY = Make this the job's controlling terminal */ errno = 0; retval = ioctl(*fd, TIOCSCTTY, 0); /* Don't check return.*/ debug(F111,"pty_open_ctty() ioctl TIOCSCTTY",ckitoa(retval),errno); } #endif /* TIOCSCTTY */ return(0L); } long pty_open_slave(slave, fd, fc) char *slave; int *fd; int fc; { int vfd, testfd; long retval; #ifdef CK_POSIX_SIG struct sigaction sa; sigemptyset(&sa.sa_mask); /* Initialize "sa" structure. */ sa.sa_flags = 0; #endif /* CK_POSIX_SIG */ /* First, chmod and chown the slave. If we have vhangup then we really need pty_open_ctty to make sure our controlling terminal is the pty we're opening. However, if we are using revoke or nothing then we just need a file descriiptor for the pty. Considering some OSes in this category break on the second call to open_ctty (currently OSF but others may), we simply use a descriptor if we can. */ #ifdef VHANG_FIRST if ((retval = pty_open_ctty(slave, &vfd, fc)) != 0) { debug(F111, "pty_open_slave() VHANG_FIRST", "pty_open_ctty() failed", retval ); return(retval); } if (vfd < 0) { debug(F111, "pty_open_slave() VHANG_FIRST", "PTY_OPEN_SLAVE_OPENFAIL", vfd ); return(PTY_OPEN_SLAVE_OPENFAIL); } #endif /* VHANG_FIRST */ if (slave == NULL || *slave == '\0') { debug(F110,"pty_open_slave()","PTY_OPEN_SLAVE_TOOSHORT",0); return(PTY_OPEN_SLAVE_TOOSHORT); } #ifdef SETUID if (chmod(slave, 0)) { debug(F110,"pty_open_slave()","PTY_OPEN_SLAVE_CHMODFAIL",0); return(PTY_OPEN_SLAVE_CHMODFAIL); } if (chown(slave, 0, 0 ) == -1 ) { debug(F110,"pty_open_slave()","PTY_OPEN_SLAVE_CHOWNFAIL",0); return(PTY_OPEN_SLAVE_CHOWNFAIL); } #endif /* SETUID */ #ifdef VHANG_FIRST ptyint_vhangup(); close(vfd); #endif /* VHANG_FIRST */ if ((retval = ptyint_void_association()) != 0) { debug(F111, "pty_open_slave()", "ptyint_void_association() failed", retval ); return(retval); } #ifdef HAVE_REVOKE if (revoke (slave) < 0 ) { debug(F110,"pty_open_slave()","PTY_OPEN_SLAVE_REVOKEFAIL",0); return(PTY_OPEN_SLAVE_REVOKEFAIL); } #endif /* HAVE_REVOKE */ /* Open the pty for real. */ retval = pty_open_ctty(slave, fd, fc); debug(F111,"pty_open_slave retval",slave,retval); debug(F111,"pty_open_slave fd",slave,*fd); if (retval != 0) { debug(F111,"pty_open_slave()","pty_open_ctty() failed",retval); return(PTY_OPEN_SLAVE_OPENFAIL); } pty_slave_fd = *fd; /* This is not visible to the upper fork */ debug(F111,"pty_open_slave fd ctty'd",slave,pty_slave_fd); retval = pty_initialize_slave(*fd); debug(F111,"pty_open_slave fd init'd",slave,pty_slave_fd); if (retval) { debug(F111,"pty_open_slave()","pty_initialize_slave() failed",retval); return(retval); } /* (VOID)pty_make_raw(*fd); */ debug(F100,"pty_open_slave OK","",*fd); return(0L); } #ifdef WANT_UTMP #ifndef UTMP_FILE #ifdef _PATH_UTMP #define UTMP_FILE _PATH_UTMP #endif /* _PATH_UTMP */ #endif /* UTMP_FILE */ /* If it is *still* missing, assume /etc/utmp */ #ifndef UTMP_FILE #define UTMP_FILE "/etc/utmp" #endif /* UTMP_FILE */ #ifndef NO_UT_PID #define WTMP_REQUIRES_USERNAME #endif /* NO_UT_PID */ long pty_update_utmp(process_type, pid, username, line, host, flags) int process_type; int pid; char *username, *line, *host; int flags; /* pty_update_utmp */ { struct utmp ent, ut; #ifndef HAVE_SETUTENT struct stat statb; int tty; #endif /* HAVE_SETUTENT */ #ifdef HAVE_SETUTXENT struct utmpx utx; #endif /* HAVE_SETUTXENT */ #ifndef NO_UT_PID char *tmpx; char utmp_id[5]; #endif /* NO_UT_PID */ char userbuf[32]; int fd; debug(F100,"pty_update_utmp()","",0); strncpy(ent.ut_line, line+sizeof("/dev/")-1, sizeof(ent.ut_line)); ent.ut_time = time(0); #ifdef NO_UT_PID if (process_type == PTY_LOGIN_PROCESS) return(0L); #else /* NO_UT_PID */ ent.ut_pid = pid; switch (process_type) { case PTY_LOGIN_PROCESS: ent.ut_type = LOGIN_PROCESS; break; case PTY_USER_PROCESS: ent.ut_type = USER_PROCESS; break; case PTY_DEAD_PROCESS: ent.ut_type = DEAD_PROCESS; break; default: return(PTY_UPDATE_UTMP_PROCTYPE_INVALID); } #endif /*NO_UT_PID*/ #ifndef NO_UT_HOST if (host) strncpy(ent.ut_host, host, sizeof(ent.ut_host)); else ent.ut_host[0] = '\0'; #endif /* NO_UT_HOST */ #ifndef NO_UT_PID if (!strcmp (line, "/dev/console")) { char * s = NULL; #ifdef sun #ifdef __SVR4 s = "co"; #else s = "cons"; #endif /* __SVR4 */ #else s = "cons"; #endif /* sun */ strncpy(ent.ut_id, s, 4); } else { tmpx = line + strlen(line)-1; if (*(tmpx-1) != '/') tmpx--; /* last 2 chars unless it's a '/' */ #ifdef __hpux ckstrncpy(utmp_id, tmpx, 5); #else ckmakmsg(utmp_id,5,"kl",tmpx,NULL,NULL); #endif /* __hpux */ strncpy(ent.ut_id, utmp_id, sizeof(ent.ut_id)); } strncpy(ent.ut_user, username, sizeof(ent.ut_user)); #else strncpy(ent.ut_name, username, sizeof(ent.ut_name)); #endif /* NO_UT_PID */ if (username[0]) strncpy(userbuf, username, sizeof(userbuf)); else userbuf[0] = '\0'; #ifdef HAVE_SETUTENT utmpname(UTMP_FILE); setutent(); /* If we need to preserve the user name in the wtmp structure and Our flags tell us we can obtain it from the utmp and we succeed in obtaining it, we then save the utmp structure we obtain, write out the utmp structure and change the username pointer so it is used by update_wtmp. */ #ifdef WTMP_REQUIRES_USERNAME if ((!username[0]) && (flags&PTY_UTMP_USERNAME_VALID) &&line) { struct utmp *utptr; strncpy(ut.ut_line, line, sizeof(ut.ut_line)); utptr = getutline(&ut); if (utptr) strncpy(userbuf,utptr->ut_user,sizeof(ut.ut_user)); } #endif /* WTMP_REQUIRES_USERNAME */ pututline(&ent); endutent(); #ifdef HAVE_SETUTXENT setutxent(); #ifdef HAVE_GETUTMPX getutmpx(&ent, &utx); #else /* HAVE_GETUTMPX */ /* For platforms like HPUX and Dec Unix which don't have getutmpx */ strncpy(utx.ut_user, ent.ut_user, sizeof(ent.ut_user)); strncpy(utx.ut_id, ent.ut_id, sizeof(ent.ut_id)); strncpy(utx.ut_line, ent.ut_line, sizeof(ent.ut_line)); utx.ut_pid = pid; /* kludge for Irix, etc. to avoid trunc. */ utx.ut_type = ent.ut_type; #ifdef UT_EXIT_STRUCTURE_DIFFER utx.ut_exit.ut_exit = ent.ut_exit.e_exit; #else /* UT_EXIT_STRUCTURE_DIFFER */ /* KLUDGE for now; eventually this will be a feature test... See PR#[40] */ #ifdef __hpux utx.ut_exit.__e_termination = ent.ut_exit.e_termination; utx.ut_exit.__e_exit = ent.ut_exit.e_exit; #else /* __hpux */ /* XXX do nothing for now; we don't even know the struct member exists */ #endif /* __hpux */ #endif /* UT_EXIT_STRUCTURE_DIFFER */ utx.ut_tv.tv_sec = ent.ut_time; utx.ut_tv.tv_usec = 0; #endif /* HAVE_GETUTMPX */ if (host) strncpy(utx.ut_host, host, sizeof(utx.ut_host)); else utx.ut_host[0] = 0; pututxline(&utx); endutxent(); #endif /* HAVE_SETUTXENT */ #else /* HAVE_SETUTENT */ if (flags&PTY_TTYSLOT_USABLE) { tty = ttyslot(); } else { int lc; tty = -1; if ((fd = open(UTMP_FILE, O_RDWR)) < 0) return(errno); for (lc = 0; lseek(fd, (off_t)(lc * sizeof(struct utmp)), SEEK_SET) != -1; lc++ ) { if (read(fd, (char *)&ut, sizeof(struct utmp) ) != sizeof(struct utmp) ) break; if (strncmp(ut.ut_line, ent.ut_line, sizeof(ut.ut_line)) == 0) { tty = lc; #ifdef WTMP_REQUIRES_USERNAME if (!username&&(flags&PTY_UTMP_USERNAME_VALID)) strncpy(userbuf, ut.ut_user, sizeof(ut.ut_user)); #endif /* WTMP_REQUIRES_USERNAME */ break; } } close(fd); } if (tty > 0 && (fd = open(UTMP_FILE, O_WRONLY, 0)) >= 0) { lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET); write(fd, (char *)&ent, sizeof(struct utmp)); close(fd); } #endif /* HAVE_SETUTENT */ /* Don't record LOGIN_PROCESS entries. */ if (process_type == PTY_LOGIN_PROCESS) return(0); else return(ptyint_update_wtmp(&ent, host, userbuf)); } #ifndef WTMP_FILE #ifdef _PATH_WTMP #define WTMP_FILE _PATH_WTMP #endif /* _PATH_WTMP */ #endif /* WTMP_FILE */ #ifndef WTMPX_FILE #ifdef _PATH_WTMPX #ifdef HAVE_UPDWTMPX #define WTMPX_FILE _PATH_WTMPX #endif /* HAVE_UPDWTMPX */ #endif /* _PATH_WTMPX */ #endif /* WTMPX_FILE */ /* If it is *still* missing, assume /usr/adm/wtmp */ #ifndef WTMP_FILE #define WTMP_FILE "/usr/adm/wtmp" #endif /* WTMP_FILE */ #ifdef COMMENT /* The following test can not be made portably */ /* #if defined(__GLIBC__) && (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 1) */ /* This is ugly, but the lack of standardization in the utmp/utmpx space, and what glibc implements and doesn't make available, is even worse. */ /* #undef HAVE_UPDWTMPX */ /* Don't use updwtmpx for glibc 2.1 */ /* #endif */ /* __GLIBC__ etc */ #else /* COMMENT */ #ifdef __GLIBC__ #undef HAVE_UPDWTMPX /* Don't use updwtmpx for glibc period */ #endif /* __GLIBC__ */ #endif /* COMMENT */ long ptyint_update_wtmp(ent,host,user) struct utmp *ent; char *host; char *user; { struct utmp ut; struct stat statb; int fd; time_t uttime; #ifdef HAVE_UPDWTMPX struct utmpx utx; getutmpx(ent, &utx); if (host) strncpy(utx.ut_host, host, sizeof(utx.ut_host) ); else utx.ut_host[0] = 0; if (user) strncpy(utx.ut_user, user, sizeof(utx.ut_user)); updwtmpx(WTMPX_FILE, &utx); #endif /* HAVE_UPDWTMPX */ #ifdef HAVE_UPDWTMP #ifndef HAVE_UPDWTMPX /* This is already performed byupdwtmpx if present.*/ updwtmp(WTMP_FILE, ent); #endif /* HAVE_UPDWTMPX*/ #else /* HAVE_UPDWTMP */ if ((fd = open(WTMP_FILE, O_WRONLY|O_APPEND, 0)) >= 0) { if (!fstat(fd, &statb)) { memset((char *)&ut, 0, sizeof(ut)); #ifdef __hpux strncpy(ut.ut_id, ent->ut_id, sizeof (ut.ut_id)); #endif /* __hpux */ strncpy(ut.ut_line, ent->ut_line, sizeof(ut.ut_line)); strncpy(ut.ut_name, ent->ut_name, sizeof(ut.ut_name)); #ifndef NO_UT_HOST strncpy(ut.ut_host, ent->ut_host, sizeof(ut.ut_host)); #endif /* NO_UT_HOST */ time(&uttime); ut.ut_time = uttime; #ifdef HAVE_GETUTENT #ifdef USER_PROCESS if (ent->ut_name) { if (!ut.ut_pid) ut.ut_pid = getpid(); #ifndef __hpux ut.ut_type = USER_PROCESS; #else /* __hpux */ ut.ut_type = ent->ut_type; #endif /* __hpux */ } else { #ifdef EMPTY ut.ut_type = EMPTY; #else ut.ut_type = DEAD_PROCESS; /* For Linux brokenness*/ #endif /* EMPTY */ } #endif /* USER_PROCESS */ #endif /* HAVE_GETUTENT */ if (write(fd, (char *)&ut, sizeof(struct utmp)) != sizeof(struct utmp)) #ifndef COHERENT ftruncate(fd, statb.st_size); #else chsize(fd, statb.st_size); #endif /* COHERENT */ } close(fd); } #endif /* HAVE_UPDWTMP */ return(0); /* no current failure cases; file not found is not failure! */ } #endif /* WANT_UTMP */ /* This is for ancient Unixes that don't have these tty symbols defined. */ #ifndef PENDIN #define PENDIN ICANON #endif /* PENDIN */ #ifndef FLUSHO #define FLUSHO ICANON #endif /* FLUSHO */ #ifndef IMAXBEL #define IMAXBEL ICANON #endif /* IMAXBEL */ #ifndef EXTPROC #define EXTPROC ICANON #endif /* EXTPROC */ static char Xline[17] = { 0, 0 }; /* getptyslave() Open the slave side of the pty, and do any initialization that is necessary. The return value fd is a file descriptor for the slave side. fc = function code from do_pty() (q.v.) */ int getptyslave(fd, fc) int * fd, fc; { int ttyfd; int t = -1; long retval; #ifdef TIOCGWINSZ struct winsize ws; extern int cmd_rows, cmd_cols; #endif /* TIOCGWINSZ */ ttyfd = *fd; debug(F111,"getptyslave()","ttyfd",ttyfd); /* * Opening the slave side may cause initilization of the * kernel tty structure. We need remember the state of: * if linemode was turned on * terminal window size * terminal speed * so that we can reset them if we need to. */ if ((retval = pty_open_slave(Xline, &t, fc)) != 0) { perror(Xline); msg++; debug(F111,"getptyslave()","Unable to open slave",retval); return(-1); } debug(F111,"getptyslave","t",t); #ifdef INIT_SPTY spty = t; debug(F111,"getptyslave","spty",spty); #endif /* INIT_SPTY */ #ifdef STREAMSPTY if (ioctl(t,I_PUSH,"pckt") < 0) { debug(F111,"getptyslave()","ioctl(I_PUSH) failed",errno); #ifndef _AIX fatal("I_PUSH pckt"); #endif /* _AIX */ } #endif /* STREAMSPTY */ /* Set up the tty modes as we like them to be. */ #ifdef COMMENT /* Originally like this... But this is the master - we want the slave */ /* Anyway, this fails on Solaris and probably other System V OS's */ init_termbuf(ttyfd); #else init_termbuf(t); #endif /* COMMENT */ #ifdef TIOCGWINSZ if (cmd_rows || cmd_cols) { memset((char *)&ws, 0, sizeof(ws)); ws.ws_col = cmd_cols; ws.ws_row = cmd_rows; debug(F101,"getptyslave() doing TIOCSWINSZ...","",t); ioctl(t, TIOCSWINSZ, (char *)&ws); } #endif /* TIOCGWINSZ */ /* For external protocols, put the pty in no-echo mode */ if (fc == 1) { debug(F100,"getptyslave() setting rawmode","",0); /* iflags */ termbuf.c_iflag &= ~(PARMRK|ISTRIP|BRKINT|INLCR|IGNCR|ICRNL); termbuf.c_iflag &= ~(INPCK|IGNPAR|IMAXBEL|IXANY|IXON|IXOFF); termbuf.c_iflag |= IGNBRK; #ifdef IUCLC termbuf.c_iflag &= ~IUCLC; #endif /* IUCLC */ /* oflags */ termbuf.c_oflag &= ~OPOST; #ifdef OXTABS termbuf.c_oflag &= ~OXTABS; #endif /* OXTABS */ #ifdef ONOCR termbuf.c_oflag &= ~ONOCR; #endif /* ONOCR */ #ifdef ONLRET termbuf.c_oflag &= ~ONLRET; #endif /* ONLRET */ #ifdef ONLCR termbuf.c_oflag &= ~ONLCR; #endif /* ONLCR */ /* lflags */ termbuf.c_lflag &= ~ECHO; #ifdef ECHOE termbuf.c_lflag &= ~ECHOE; #endif /* ECHOE */ #ifdef ECHONL termbuf.c_lflag &= ~ECHONL; #endif /* ECHONL */ #ifdef ECHOPRT termbuf.c_lflag &= ~ECHOPRT; #endif /* ECHOPRT */ #ifdef ECHOKE termbuf.c_lflag &= ~ECHOKE; #endif /* ECHOKE */ #ifdef ECHOCTL termbuf.c_lflag &= ~ECHOCTL; #endif /* ECHOCTL */ #ifdef ALTWERASE termbuf.c_lflag &= ~ALTWERASE; #endif /* ALTWERASE */ #ifdef EXTPROC termbuf.c_lflag &= ~EXTPROC; #endif /* EXTPROC */ termbuf.c_lflag &= ~(ICANON|ISIG|IEXTEN|TOSTOP|FLUSHO|PENDIN); #ifdef NOKERNINFO termbuf.c_lflag |= NOKERNINFO; #endif /* NOKERNINFO */ /* termbuf.c_lflag |= NOFLSH; */ termbuf.c_lflag &= ~NOFLSH; /* cflags */ termbuf.c_cflag &= ~(CSIZE|PARENB|PARODD); termbuf.c_cflag |= CS8|CREAD; #ifdef VMIN termbuf.c_cc[VMIN] = 1; #endif /* VMIN */ } else { /* Regular interactive use */ debug(F100,"getptyslave() setting cooked mode","",0); /* Settings for sgtty based systems */ #ifndef USE_TERMIO termbuf.sg.sg_flags |= CRMOD|ANYP|ECHO|XTABS; #endif /* USE_TERMIO */ #ifndef OXTABS #define OXTABS 0 #endif /* OXTABS */ /* Settings for UNICOS and HPUX */ #ifdef CRAY termbuf.c_oflag = OPOST|ONLCR|TAB3; termbuf.c_iflag = IGNPAR|ISTRIP|ICRNL|IXON; termbuf.c_lflag = ISIG|ICANON|ECHO|ECHOE|ECHOK; termbuf.c_cflag = EXTB|HUPCL|CS8; #else /* CRAY */ #ifdef HPUX termbuf.c_oflag = OPOST|ONLCR|TAB3; termbuf.c_iflag = IGNPAR|ISTRIP|ICRNL|IXON; termbuf.c_lflag = ISIG|ICANON|ECHO|ECHOE|ECHOK; termbuf.c_cflag = EXTB|HUPCL|CS8; #else /* HPUX */ #ifdef USE_TERMIO /* Settings for all other termios/termio based systems, other than 4.4BSD. In 4.4BSD the kernel does the initial terminal setup. */ #ifdef BSD42 #ifndef BSD44 termbuf.c_lflag |= ECHO|ICANON|IEXTEN|ISIG; termbuf.c_oflag |= ONLCR|OXTABS|OPOST; termbuf.c_iflag |= ICRNL|IGNPAR; termbuf.c_cflag |= HUPCL; termbuf.c_iflag &= ~IXOFF; #endif /* BSD44 */ #else /* BSD42 */ termbuf.c_lflag |= ECHO|ICANON|IEXTEN|ISIG; termbuf.c_oflag |= ONLCR|OXTABS|OPOST; termbuf.c_iflag |= ICRNL|IGNPAR; termbuf.c_cflag |= HUPCL; termbuf.c_iflag &= ~IXOFF; #endif /* BSD42 */ #endif /* USE_TERMIO */ #endif /* HPUX */ #endif /* CRAY */ } /* Set the tty modes, and make this our controlling tty. */ #ifdef COMMENT /* But this is the master - we want the slave */ set_termbuf(ttyfd); #else set_termbuf(t); #endif /* COMMENT */ if (t != 0) dup2(t, 0); if (t != 1) dup2(t, 1); if (t != 2) { if (fc == 0) { dup2(t, 2); } else if (fc == 1) { /* For external protocols, send stderr to /dev/null */ #ifdef COMMENT int xx; #ifndef COMMENT char * s = "/dev/null"; errno = 0; xx = open(s, O_WRONLY); #else char * s = "pty.log"; errno = 0; xx = open(s, O_CREAT, 0644); #endif /* COMMENT */ debug(F111,"getptyslave redirect stderr",s,errno); dup2(xx,2); #endif /* COMMENT */ } } if (t > 2) close(t); if (ttyfd > 2) { close(ttyfd); ttyfd = -1; *fd = ttyfd; } return(0); } #ifdef HAVE_PTYTRAP /* To be called to determine if a trap is pending on a pty if and only if select() cannot be used. */ int pty_trap_pending(fd) int fd; { int pending; int rc; rc = ioctl(fd, TIOCTRAPSTATUS, (char *)&pending, sizeof(pending)); if (rc == 0) { debug(F101,"pty_trap_pending()","",pending); return(pending); } else { debug(F111,"pty_trap_pending()","ioctl() failed",rc); return(-1); } } /* To be called after select() has returned indicating that an exception is waiting on a pty. It should be called with the file descriptor of the pty. Returns -1 on error; 0 if pty is still open; 1 if pty has closed. */ int pty_trap_handler(fd) int fd; { struct request_info ri; memset(&ri,0,sizeof(ri)); if (ioctl(fd,TIOCREQCHECK,(char *)&ri, sizeof(ri)) != 0) { debug(F111,"pty_trap_handler()","ioctl(TIOCREQCHECK) failed",errno); return(-1); } switch (ri.request) { case TIOCOPEN: debug(F110,"pty_trap_handler()","an open() call",0); break; case TIOCCLOSE: debug(F110,"pty_trap_handler()","a close() call",0); break; default: debug(F110,"pty_trap_handler()","an ioctl() call",0); ri.errno_error = EINVAL; } if (ioctl(fd, TIOCREQSET, (char *)&ri,sizeof(ri)) != 0) { debug(F111,"pty_trap_handler()","ioctl(TIOCREQSET) failed",errno); return(-1); } if (ri.request == TIOCCLOSE) return(1); else return(0); } #endif /* HAVE_PTYTRAP */ VOID exec_cmd(s) char * s; { struct stringarray * q; char ** args = NULL; if (!s) return; if (!*s) return; q = cksplit(1,0,s,NULL,"\\%[]&$+-/=*^_@!{}/<>|.#~'`:;?",7,0,0); if (!q) return; args = q->a_head + 1; #ifdef DEBUG { int i, n; n = q->a_size; for (i = 0; i <= n; i++) { if (!args[i]) { debug(F111,"exec_cmd arg","NULL",i); break; } else { debug(F111,"exec_cmd arg",args[i],i); if (i == n && args[i]) { debug(F101,"exec_cmd SUBSTITUTING NULL","",i); if (strlen(args[i]) == 0) makestr(&(args[i]),NULL); } } } } #endif /* DEBUG */ execvp(args[0],args); } /* Get a pty, scan input lines. */ /* fc = 0 for interactive access; fc = 1 for running external protocols */ static int pty_fc = -1; /* Global copy of fc */ int do_pty(fd, cmd, fc) int * fd; char * cmd; int fc; { long retval; int syncpipe[2]; int i, ttyfd; #ifdef HAVE_PTYTRAP int x; #endif /* HAVE_PTYTRAP */ debug(F101,"CKUPTY.C do_pty fc","",fc); ttyfd = *fd; pty_master_fd = -2; pty_slave_fd = -2; pty_fork_pid = -2; msg = 0; /* Message counter */ pty_init(); /* Find an available pty to use. */ errno = 0; if ((retval = pty_getpty(&ttyfd, Xline, 20)) != 0) { if (msg++ == 0) perror(Xline); debug(F111,"do_pty()","pty_getpty() fails",retval); *fd = ttyfd; return(-1); } *fd = ttyfd; debug(F111,"do_pty() Xline",Xline,ttyfd); #ifdef SIGTTOU /* Ignoring SIGTTOU keeps the kernel from blocking us. we tweak the tty with an ioctl() (in ttioct() in /sys/tty.c in a BSD kernel) */ signal(SIGTTOU, SIG_IGN); #endif /* SIGTTOU */ /* Start up the command on the slave side of the terminal */ if (pipe(syncpipe) < 0) { debug(F110,"do_pty()","pipe() fails",0); perror("pipe() failed"); msg++; debug(F111,"do_pty()","pipe fails",errno); return(-1); } if ((i = fork()) < 0) { /* XXX - need to clean up the allocated pty */ perror("fork() failed"); msg++; debug(F111,"do_pty()","fork fails",errno); return(-1); } if (i) { /* Wait for child before writing to parent side of pty. */ char c; #ifdef HAVE_PTYTRAP int on = 1; #endif /* HAVE_PTYTRAP */ close(syncpipe[1]); errno = 0; if (read(syncpipe[0], &c, 1) == 0) { /* Slave side died */ perror("Pipe read() failed"); msg++; debug(F110,"do_pty()","Slave fails to initialize",0); close(syncpipe[0]); return(-1); } pty_fork_pid = i; /* So we can clean it up later */ pty_fork_active = 1; debug(F101,"do_pty pty_fork_pid","",pty_fork_pid); #ifdef HAVE_PTYTRAP /* HPUX does not allow the master to read end of file. */ /* Therefore, we must determine that the slave has been */ /* closed by trapping the call to close(). */ errno = 0; x = ioctl(ttyfd, TIOCTRAP, (char *)&on); debug(F111,"do_pty ioctl(TIOCTRAP)",ckitoa(x),errno); #endif /* HAVE_PTYTRAP */ debug(F111,"do_pty()","synchronized - pty_fork_pid",pty_fork_pid); close(syncpipe[0]); } else { int x; debug(F101,"do_pty getptyslave ttyfd A","",ttyfd); debug(F110,"do_pty()","Slave starts",0); x = getptyslave(&ttyfd,fc); debug(F101,"do_pty getptyslave","",x); if (x == 0) { debug(F101,"do_pty getptyslave ttyfd B","",ttyfd); #ifdef WANT_UTMP pty_update_utmp(PTY_USER_PROCESS, getpid(), "KERMIT", Xline, cmd, PTY_TTYSLOT_USABLE ); #endif /* WANT_UTMP */ /* Notify our parent we're ready to continue.*/ debug(F110,"do_pty()","slave synchronizing",0); write(syncpipe[1],"y",1); close(syncpipe[0]); close(syncpipe[1]); debug(F110,"do_pty cmd",cmd,""); exec_cmd(cmd); debug(F111,"do_pty()","exec_cmd() returns - why?",errno); } *fd = ttyfd; debug(F110,"do_pty()","getptyslave() fails - exiting",0); exit(1); } *fd = ttyfd; pty_fc = fc; return(getpid()); } /* end of do_pty() */ VOID end_pty() { msg = 0; /* Message counter */ debug(F101,"end_pty pty_fork_pid","",pty_fork_pid); if (Xline[0] && pty_fork_pid >= 0) { pty_cleanup(Xline,pty_fork_pid,1); Xline[0] = '\0'; pty_fork_pid = -1; pty_fork_active = 0; debug(F101,"end_pty pty_fork_active","",pty_fork_active); } pty_fc = -1; } #endif /* NETPTY */ ckupty.h0000644000015300001460000001130511334615141011350 0ustar fdckermit/* C K U P T Y . H -- Includes and definitions for ckupty.c */ /* Copyright 1995 by the Massachusetts Institute of Technology. Modified for use in C-Kermit by: Jeffrey E Altman Secure Endpoints Inc., New York City November 1999 */ #ifndef __PTY_INT_H__ #include /* #define WANT_UTMP */ /* We don't want all the utmp/wtmp stuff */ #ifdef WANT_UTMP #ifdef HAVE_UTMP_H #include #endif /* HAVE_UTMP_H */ #ifdef HAVE_UTMPX_H #include #endif /* HAVE_UTMPX_H */ #endif /* WANT_UTMP */ #ifdef HAVE_UNISTD_H #include #endif /* HAVE_UNISTD_H */ #ifdef __SCO__ #include #endif /* __SCO__ */ #ifdef HAVE_STDLIB_H #include #endif /* HAVE_STDLIB_H */ #include #include #ifndef SUNOS41 #include #endif /* SUNOS41 */ #include #include #include #include #include #include #include #ifdef HAVE_SYS_LABEL_H /* only SunOS 4? */ #include #include #include #endif /* HAVE_SYS_LABEL_H */ #include #ifdef HPUX #include #endif /* HPUX */ #ifdef sysvimp #include #endif /* sysvimp */ #ifdef COMMENT /* I don't think we actually use this for anything */ /* and it kills Slackware builds, where there is no select.h. */ #ifndef NO_SYS_SELECT_H #ifdef HAVE_SYS_SELECT_H #include #endif /* HAVE_SYS_SELECT_H */ #endif /* NO_SYS_SELECT_H */ #endif /* COMMENT */ #ifdef HAVE_STREAMS #include #include #endif /* HAVE_STREAMS */ #ifdef POSIX_TERMIOS #ifndef ultrix #include #else #include #endif /* ultrix */ #else /* POSIX_TERMIOS */ #include #endif /* POSIX_TERMIOS */ #include /* #include */ #ifndef ultrix #include #endif /* ultrix */ /* #include */ /* (now done in ckcdeb.h) */ #ifdef HAVE_STREAMS /* krlogin doesn't test sys/tty... */ #ifdef HAVE_SYS_TTY_H #include #endif /* HAVE_SYS_TTY_H */ #ifdef HAVE_SYS_PTYVAR_H /* Solaris actually uses packet mode, so the real macros are needed too */ #include #endif /* HAVE_SYS_PTYVAR_H */ #endif /* HAVE_STREAMS */ #ifdef COMMENT /* This block moved to ckcdeb.h */ #ifndef NO_OPENPTY /* For NetBSD, see makefile */ #ifndef HAVE_OPENPTY #ifdef __FreeBSD__ #define HAVE_OPENPTY #else #ifdef MACOSX10 #define HAVE_OPENPTY #endif /* MACOSX10 */ #endif /* __FreeBSD__ */ #endif /* HAVE_OPENPTY */ #endif /* NO_OPENPTY */ #endif /* COMMENT */ #ifdef HAVE_VHANGUP #ifndef OPEN_CTTY_ONLY_ONCE /* Breaks under Ultrix and others where you cannot get controlling terminal twice. */ #define VHANG_first #define VHANG_LAST #endif /* OPEN_CTTY_ONLY_ONCE */ #endif /* HAVE_VHANGUP */ /* Internal functions */ _PROTOTYP(long ptyint_void_association,(void)); _PROTOTYP(long ptyint_open_ctty ,(char *, int *)); _PROTOTYP(VOID ptyint_vhangup, (void)); #ifdef WANT_UTMP _PROTOTYP(long ptyint_update_wtmp, (struct utmp *, char *, char *)); #endif /* WANT_UTMP */ #define __PTY_INT_H__ #endif /* __PTY_INT_H__ */ #ifndef __LIBPTY_H__ #ifdef WANT_UTMP /* Constants for pty_update_utmp */ #define PTY_LOGIN_PROCESS 0 #define PTY_USER_PROCESS 1 #define PTY_DEAD_PROCESS 2 #define PTY_TTYSLOT_USABLE (0x1) /* flags to update_utmp*/ #define PTY_UTMP_USERNAME_VALID (0x2) #endif /* WANT_UTMP */ _PROTOTYP(long pty_init,(void)); _PROTOTYP(long pty_getpty, ( int *, char *, int)); _PROTOTYP(long pty_open_slave, (char *, int *, int)); _PROTOTYP(long pty_open_ctty, (char *, int *, int)); _PROTOTYP(long pty_initialize_slave, (int)); #ifdef WANT_UTMP _PROTOTYP(long pty_update_utmp, (int, int, char *, char *, char *, int)); _PROTOTYP(long pty_logwtmp, (char *, char *, char *)); #endif /* WANT_UTMP */ _PROTOTYP(long pty_cleanup, (char *, int, int)); #define PTY_GETPTY_STREAMS (44806912L) #define PTY_GETPTY_FSTAT (44806913L) #define PTY_GETPTY_NOPTY (44806914L) #define PTY_GETPTY_SLAVE_TOOLONG (44806915L) #define PTY_OPEN_SLAVE_OPENFAIL (44806916L) #define PTY_OPEN_SLAVE_CHMODFAIL (44806917L) #define PTY_OPEN_SLAVE_NOCTTY (44806918L) #define PTY_OPEN_SLAVE_CHOWNFAIL (44806919L) #define PTY_OPEN_SLAVE_LINE_PUSHFAIL (44806920L) #define PTY_OPEN_SLAVE_PUSH_FAIL (44806921L) #define PTY_OPEN_SLAVE_REVOKEFAIL (44806922L) #ifdef WANT_UTMP #define PTY_UPDATE_UTMP_PROCTYPE_INVALID (44806923L) #endif /* WANT_UTMP */ #define PTY_OPEN_SLAVE_TOOSHORT (44806924L) #define ERROR_TABLE_BASE_pty (44806912L) extern struct error_table et_pty_error_table; #define __LIBPTY_H__ #endif /* __LIBPTY_H__ */ ckuscr.c0000644000015300001460000004324711266107652011337 0ustar fdckermit#include "ckcsym.h" #ifndef NOICP #ifndef NOSCRIPT char *loginv = "Script Command, 9.0.032, 16 Oct 2009"; /* C K U S C R -- expect-send script implementation */ /* Copyright (C) 1985, 2009, Trustees of Columbia University in the City of New York. All rights reserved. See the C-Kermit COPYING.TXT file or the copyright text in the ckcmai.c module for disclaimer and permissions. Original (version 1, 1985) author: Herm Fischer, Encino, CA. Contributed to Columbia University in 1985 for inclusion in C-Kermit 4.0. Maintained since 1985 by Frank da Cruz, Columbia University, fdc@columbia.edu. The module takes a UUCP-style script of the "expect send [expect send] ..." format. It is intended to operate similarly to the way the common UUCP L.sys login entries work. Conditional responses are supported: expect[-send-expect[...]], as with UUCP. The send keyword EOT sends a Control-d, and the keyword BREAK sends a break. Letters prefixed by '~' are '~b' backspace, '~s' space, '~n' linefeed, '~r' return, '~x' xon, '~t' tab, '~q' ? (not allowed on kermit command lines), '~' ~, '~'', '~"', '~c' don't append return, '~o[o[o]]' octal character. As with some uucp systems, sent strings are followed by ~r (not ~n) unless they end with ~c. Null expect strings (e.g., ~0 or --) cause a short delay, and are useful for sending sequences requiring slight pauses. This module calls externally defined system-dependent functions for communications i/o, as defined in ckcplm.txt, the C-Kermit Program Logic Manual, and thus should be portable to all systems that implement those functions, and where alarm() and signal() work as they do in UNIX. */ #include "ckcdeb.h" #include #ifdef NT #include #else /* NT */ #include #endif /* NT */ #include "ckcasc.h" #include "ckcker.h" #include "ckuusr.h" #include "ckcnet.h" #include "ckcsig.h" _PROTOTYP( VOID flushi, (void) ); _PROTOTYP( static VOID myflsh, (void) ); _PROTOTYP( static int sequenc, (void) ); _PROTOTYP( static VOID recvseq, (void) ); _PROTOTYP( static int outseq, (void) ); #ifdef MAC #define signal msignal #define SIGTYP long #define alarm malarm #define SIG_IGN 0 #define SIGALRM 1 #define SIGINT 2 SIGTYP (*msignal(int type, SIGTYP (*func)(int)))(int); #endif /* MAC */ #ifdef AMIGA #define signal asignal #define alarm aalarm #define SIGALRM (_NUMSIG+1) #define SIGTYP void SIGTYP (*asignal(int type, SIGTYP (*func)(int)))(int); unsigned aalarm(unsigned); #endif /* AMIGA */ #ifdef STRATUS /* VOS doesn't have alarm(), but it does have some things we can work with. */ /* however, we have to catch all the signals in one place to do this, so */ /* we intercept the signal() routine and call it from our own replacement. */ #define signal vsignal #define alarm valarm SIGTYP (*vsignal(int type, SIGTYP (*func)(int)))(int); int valarm(int interval); #endif /* STRATUS */ extern int sessft; extern int local, flow, seslog, mdmtyp, msgflg, duplex, backgrd, secho, quiet; extern int network, nettype, ttnproto; extern long speed; extern char ttname[]; #ifdef NTSIG extern int TlsIndex; #endif /* NTSIG */ #ifdef IKSD extern int inserver; #endif /* IKSD */ static int is_tn = 0; /* Do Telnet negotiations */ #ifndef NOSPL #ifdef DCMDBUF extern struct cmdptr *cmdstk; #else extern struct cmdptr cmdstk[]; #endif /* DCMDBUF */ extern int techo, cmdlvl; extern int mecho; #endif /* NOSPL */ static int scr_echo; /* Whether to echo script commands */ static int exp_alrm = 15; /* Time to wait for expect string */ #define SND_ALRM 15 /* Time to allow for sending string */ #define NULL_EXP 2 /* Time to pause on null expect strg*/ #define DEL_MSEC 300 /* Milliseconds to pause on ~d */ #define SBUFL 512 static char seq_buf[SBUFL+2], *s; /* expect-send sequence buffer */ static int got_it, no_cr; /* Connect state parent/child communication signal handlers */ #ifdef COMMENT #ifdef CK_POSIX_SIG static sigjmp_buf alrmrng; #else static jmp_buf alrmrng; #endif /* CK_POSIX_SIG */ #else static ckjmpbuf alrmrng; #endif /* COMMENT */ static SIGTYP #ifdef CK_ANSIC scrtime(int foo) /* modem read failure handler, */ #else scrtime(foo) int foo; /* Alarm handler */ #endif /* CK_ANSIC */ /* scrtime */ { #ifdef BEBOX #ifdef BE_DR_7 alarm_expired(); #endif /* BE_DR_7 */ #endif /* BEBOX */ #ifdef NTSIG if (foo == SIGALRM) PostAlarmSigSem(); else PostCtrlCSem(); #else /* NTSIG */ #ifdef NT cklongjmp(ckjaddr(alrmrng),1); #else /* NT */ cklongjmp(alrmrng,1); #endif /* NT */ #endif /* NTSIG */ SIGRETURN; } /* Sequence interpreter -- pick up next sequence from command string, decode escapes and place into seq_buf. If string contains a ~d (delay) then sequenc() returns a 1 expecting to be called again after the ~d executes. */ static int sequenc() { int i; char c, oct_char; no_cr = 0; /* output needs cr appended */ for (i = 0; i < SBUFL; ) { if (*s == '\0' || *s == '-' || isspace(*s) ) { /* done */ seq_buf[i] = '\0'; return(0) ; } if (*s == '~') { /* escape character */ s++; switch (c = *s) { case 'n': seq_buf[i++] = LF; break; case 'r': seq_buf[i++] = CR; break; case 't': seq_buf[i++] = '\t'; break; case 'b': seq_buf[i++] = '\b'; break; case 'q': seq_buf[i++] = '?'; break; #ifdef COMMENT /* The default case should catch these now... */ case '~': seq_buf[i++] = '~'; break; case '-': seq_buf[i++] = '-'; break; #endif /* COMMENT */ case '\'': seq_buf[i++] = '\''; break; case '\"': seq_buf[i++] = '\"'; break; case 's': seq_buf[i++] = ' '; break; case 'x': seq_buf[i++] = '\021'; break; case 'c': no_cr = 1; break; case 'd': { /* send what we have & then */ seq_buf[i] = '\0'; /* expect to send rest after */ no_cr = 1; /* sender delays a little */ s++; return(1); } case 'w': { /* wait count */ exp_alrm = 15; /* default to 15 sec */ if (isdigit(*(s+1))) { s++; exp_alrm = *s & 15; if (isdigit(*(s+1)) ) { s++; exp_alrm = exp_alrm * 10 + (*s & 15); } } break; } default: if ( isdigit(c) ) { /* octal character */ oct_char = (char) (c & 7); /* most significant digit */ if (isdigit( *(s+1) ) ) { s++; oct_char = (char) ((oct_char<<3) | ( *s & 7 )); if (isdigit( *(s+1) ) ) { s++; oct_char = (char) ((oct_char<<3) | ( *s & 7 )); } } seq_buf[i++] = oct_char; break; } else seq_buf[i++] = *s; /* Treat ~ as quote */ } } else seq_buf[i++] = *s; /* Plain old character */ s++; } seq_buf[i] = '\0'; return(0); /* end of space, return anyway */ } /* Output buffering for "recvseq" and "flushi" */ #define MAXBURST 256 /* maximum size of input burst */ static CHAR conbuf[MAXBURST]; /* buffer to hold output for console */ static int concnt = 0; /* number of characters buffered */ static CHAR sesbuf[MAXBURST]; /* buffer to hold output for session log */ static int sescnt = 0; /* number of characters buffered */ static VOID myflsh() { if (concnt > 0) { conxo(concnt, (char *) conbuf); concnt = 0; } if (sescnt > 0) { logstr((char *) sesbuf, sescnt); sescnt = 0; } } /* these variables are used to pass data between the recvseq() */ /* and the dorseq(). They are necessary because in some versions */ /* dorseq() is executed in a separate thread and data cannot be */ /* passed by parameter. */ static char *rseqe, * rseqgot, * rseqtrace ; static int rseql; static SIGTYP #ifdef CK_ANSIC dorseq(void * threadinfo) #else /* CK_ANSIC */ dorseq(threadinfo) VOID * threadinfo; #endif /* CK_ANSIC */ /* dorseq */ { int i, x; int burst = 0; /* chars remaining in input burst */ #ifdef NTSIG setint(); if (threadinfo) { /* Thread local storage... */ TlsSetValue(TlsIndex,threadinfo); } #endif /* NTSIG */ #ifdef CK_LOGIN #ifdef NT #ifdef IKSD if (inserver) setntcreds(); #endif /* IKSD */ #endif /* NT */ #endif /* CK_LOGIN */ while (!got_it) { for (i = 0; i < rseql-1; i++) rseqgot[i] = rseqgot[i+1]; x = ttinc(0); /* Read a character */ debug(F101,"recvseq","",x); if (x < 0) { #ifdef NTSIG ckThreadEnd(threadinfo); #endif /* NTSIG */ SIGRETURN; /* Check for error */ } #ifdef NETCONN #ifdef TNCODE /* Check for telnet protocol negotiation */ if (((x & 0xff) == IAC) && is_tn) { /* Telnet negotiation */ myflsh(); burst = 0; switch (tn_doop((CHAR)(x & 0xff),duplex,ttinc)) { case 2: duplex = 0; continue; case 1: duplex = 1; default: continue; } } #endif /* TNCODE */ #endif /* NETCONN */ rseqgot[rseql-1] = (char) (x & 0x7f); /* Got a character */ burst--; /* One less waiting */ if (scr_echo) conbuf[concnt++] = rseqgot[rseql-1]; /* Buffer it */ if (seslog) /* Log it in session log */ #ifdef UNIX if (sessft != 0 || rseqgot[rseql-1] != '\r') #else #ifdef OSK if (sessft != 0 || rseqgot[rseql-1] != '\012') #endif /* OSK */ #endif /* UNIX */ if (rseqgot[rseql-1]) /* Filter out NULs */ sesbuf[sescnt++] = rseqgot[rseql-1]; if ((int)strlen(rseqtrace) < SBUFL-2 ) strcat(rseqtrace,dbchr(rseqgot[rseql-1])); got_it = (!strncmp(rseqe, rseqgot, rseql)); if (burst <= 0) { /* Flush buffered output */ myflsh(); if ((burst = ttchk()) < 0) { /* Get size of next input burst */ #ifdef NTSIG ckThreadEnd(threadinfo); #endif /* NTSIG */ SIGRETURN; } /* prevent overflow of "conbuf" and "sesbuf" */ if (burst > MAXBURST) burst = MAXBURST; } } #ifdef NTSIG ckThreadEnd(threadinfo); #endif /* NTSIG */ SIGRETURN; } static SIGTYP #ifdef CK_ANSIC failrseq(void * threadinfo) #else /* CK_ANSIC */ failrseq(threadinfo) VOID * threadinfo; #endif /* CK_ANSIC */ /* failrseq */ { got_it = 0; /* Timed out here */ SIGRETURN; } /* Receive sequence -- see if expected response comes, return success (or failure) in got_it. */ static VOID recvseq() { char *e, got[7], trace[SBUFL]; int i, l; sequenc(); l = (int)strlen(e=seq_buf); /* no more than 7 chars allowed */ if (l > 7) { e += l-7; l = 7; } tlog(F111,"expecting sequence",e,(long) l); if (l == 0) { /* null sequence, delay a little */ sleep (NULL_EXP); got_it = 1; tlog(F100,"got it (null sequence)","",0L); return; } *trace = '\0'; for (i = 0; i < 7; i++) got[i]='\0'; rseqtrace = trace; rseqe = e; rseqgot = got; rseql = l; alrm_execute(ckjaddr(alrmrng), exp_alrm, scrtime, dorseq, failrseq); tlog(F110,"received sequence: ",trace,0L); tlog(F101,"returning with got-it code","",(long) got_it); myflsh(); /* Flush buffered output */ return; } /* Output A Sequence starting at pointer s, return 0 if okay, 1 if failed to read (modem hangup or whatever) */ static int oseqret = 0; /* Return code for outseq */ /* Out here to prevent clobbering */ /* by longjmp. */ static SIGTYP #ifdef CK_ANSIC dooseq(void * threadinfo) #else /* CK_ANSIC */ dooseq(threadinfo) VOID * threadinfo; #endif /* CK_ANSIC */ { int l; char *sb; #ifdef TCPSOCKET extern int tn_nlm, tn_b_nlm; #endif /* TCPSOCKET */ #ifdef NTSIG setint(); if (threadinfo) { /* Thread local storage... */ TlsSetValue(TlsIndex,threadinfo); } #endif /* NTSIG */ #ifdef CK_LOGIN #ifdef NT #ifdef IKSD if (inserver) setntcreds(); #endif /* IKSD */ #endif /* NT */ #endif /* CK_LOGIN */ l = (int)strlen(seq_buf); tlog(F111,"sending sequence ",seq_buf,(long) l); if (!strcmp(seq_buf,"EOT")) { ttoc(dopar('\004')); if (scr_echo) conol(""); if (seslog && duplex) logstr("",5); } else if (!strcmp(seq_buf,"BREAK") || !strcmp(seq_buf,"\\b") || !strcmp(seq_buf,"\\B")) { ttsndb(); if (scr_echo) conol(""); if (seslog) logstr("{BREAK}",7); } else { if (l > 0) { for ( sb = seq_buf; *sb; sb++) *sb = dopar(*sb); /* add parity */ ttol((CHAR *)seq_buf,l); /* send it */ if (scr_echo && duplex) { #ifndef NOLOCAL #ifdef OS2 { /* Echo to emulator */ char *s = seq_buf; while (*s) { scriptwrtbuf((USHORT)*s); } } #endif /* OS2 */ #endif /* NOLOCAL */ conxo(l,seq_buf); } if (seslog && duplex) /* log it */ logstr(seq_buf,strlen(seq_buf)); } if (!no_cr) { ttoc( dopar(CR) ); #ifdef TCPSOCKET if (is_tn) { if (!TELOPT_ME(TELOPT_BINARY) && tn_nlm != TNL_CR) ttoc((char)((tn_nlm == TNL_CRLF) ? dopar(LF) : dopar(NUL))); else if (TELOPT_ME(TELOPT_BINARY) && (tn_b_nlm == TNL_CRLF || tn_b_nlm == TNL_CRNUL)) ttoc((char)((tn_b_nlm == TNL_CRLF) ? dopar(LF) : dopar(NUL))); } #endif /* TCPSOCKET */ if (seslog && duplex) logchar(dopar(CR)); } } #ifdef NTSIG ckThreadEnd(threadinfo); #endif /* NTSIG */ SIGRETURN; } SIGTYP #ifdef CK_ANSIC failoseq(void * threadinfo) #else /* CK_ANSIC */ failoseq(threadinfo) VOID * threadinfo; #endif /* CK_ANSIC */ /* failoseq */ { oseqret = -1; /* else -- alarm rang */ SIGRETURN; } static int outseq() { int delay; oseqret = 0; /* Initialize return code */ while(1) { delay = sequenc(); alrm_execute( ckjaddr(alrmrng), SND_ALRM, scrtime, dooseq, failoseq ) ; if (!delay) return(oseqret); #ifndef MAC msleep(DEL_MSEC); /* delay, loop to next send */ #endif /* MAC */ } } /* L O G I N -- (historical misnomer) Execute the SCRIPT command */ int dologin(cmdstr) char *cmdstr; { #ifdef OS2 #ifdef NT SIGTYP (* savealm)(int); /* Save incoming alarm function */ #else /* NT */ SIGTYP (* volatile savealm)(int); /* Save incoming alarm function */ #endif /* NT */ #else /* OS2 */ SIGTYP (*savealm)(); /* Save incoming alarm function */ #endif /* OS2 */ char *e; s = cmdstr; /* Make global to this module */ tlog(F100,loginv,"",0L); if (speed < 0L) speed = ttgspd(); if (ttopen(ttname,&local,mdmtyp,0) < 0) { ckmakmsg(seq_buf,SBUFL,"Sorry, can't open ",ttname,NULL,NULL); perror(seq_buf); return(0); } /* Whether to echo script commands ... */ scr_echo = (!quiet && !backgrd && secho); #ifndef NOSPL if (scr_echo && cmdlvl > 1) { if (cmdstk[cmdlvl].src == CMD_TF) scr_echo = techo; if (cmdstk[cmdlvl].src == CMD_MD) scr_echo = mecho; } #endif /* NOSPL */ if (scr_echo) { #ifdef NETCONN if (network) printf("Executing SCRIPT to host %s.\n",ttname); else #endif /* NETCONN */ printf("Executing SCRIPT through %s, speed %ld.\n",ttname,speed); } #ifdef TNCODE /* TELNET input must be scanned for IAC */ is_tn = (local && network && IS_TELNET()) || (!local && sstelnet); #endif /* TNCODE */ *seq_buf = 0; for (e = s; *e; e++) ckstrncat(seq_buf,dbchr(*e),SBUFL); #ifdef COMMENT /* Skip this because it tends to contain a password... */ if (scr_echo) printf("SCRIPT string: %s\n",seq_buf); #endif /* COMMENT */ tlog(F110,"SCRIPT string: ",seq_buf, 0L); /* Condition console terminal and communication line... */ if (ttvt(speed,flow) < 0) { printf("Sorry, Can't condition communication line\n"); return(0); } /* Save initial timer interrupt value */ savealm = signal(SIGALRM,SIG_IGN); flushi(); /* Flush stale input */ /* start expect - send sequence */ while (*s) { /* While not done with buffer */ while (*s && isspace(*s)) s++; /* Skip over separating whitespaces */ /* Gather up expect sequence */ got_it = 0; recvseq(); while (!got_it) { /* Have it yet? */ if (*s++ != '-') /* No, is there a conditional send? */ goto failret; /* No, return failure */ flushi(); /* Yes, flush out input buffer */ if (outseq()) /* If unable to send, */ goto failret; /* return failure. */ if (*s++ != '-') /* If no conditional response here, */ goto failret; /* return failure. */ recvseq(); /* All OK, read response from host. */ } /* Loop back and check got_it */ while (*s && !isspace(*s++) ) ; /* Skip over conditionals */ while (*s && isspace(*s)) s++; /* Skip over separating whitespaces */ flushi(); /* Flush */ if (*s) if (outseq()) goto failret; /* If any */ } signal(SIGALRM,savealm); if (scr_echo) printf("Script successful.\n"); tlog(F100,"Script successful.","",0L); return(1); failret: signal(SIGALRM,savealm); if (scr_echo) printf("Sorry, script failed\n"); tlog(F100,"Script failed","",0L); return(0); } /* F L U S H I -- Flush, but log, SCRIPT input buffer */ VOID flushi() { int n, x; if ( seslog /* Logging session? */ || scr_echo /* Or console echoing? */ #ifdef NETCONN #ifdef TNCODE /* TELNET input must be scanned for IAC */ || is_tn #endif /* TNCODE */ #endif /* NETCONN */ ) { if ((n = ttchk()) < 0) /* Yes, anything in buffer? */ return; if (n > MAXBURST) n = MAXBURST; /* Make sure not too much, */ myflsh(); /* and that buffers are empty. */ while (n-- > 0) { x = ttinc(0); /* Collect a character */ #ifdef NETCONN #ifdef TNCODE /* Check for telnet protocol negotiation */ if (is_tn && ((x & 0xff) == IAC) ) { myflsh(); /* Sync output */ switch (tn_doop((CHAR)(x & 0xff),duplex,ttinc)) { case 2: duplex = 0; break; case 1: duplex = 1; default: break; } /* Recalculate flush count */ if ((n = ttchk()) < 0) return; if (n > MAXBURST) n = MAXBURST; continue; } #endif /* TNCODE */ #endif /* NETCONN */ if (scr_echo) conbuf[concnt++] = (CHAR) x; /* buffer for console */ if (seslog) #ifdef UNIX if (sessft != 0 || x != '\r') #else #ifdef OSK if (sessft != 0 || x != '\012') #endif /* OSK */ #endif /* UNIX */ sesbuf[sescnt++] = (CHAR) x; /* buffer for session log */ } myflsh(); } else ttflui(); /* Otherwise just flush. */ } #else /* NOSCRIPT */ char *loginv = "Script Command Disabled"; #endif /* NOSCRIPT */ #endif /* NOICP */ ckusig.c0000644000015300001460000001611511266110063011312 0ustar fdckermitchar *ckusigv = "Signal support, 9.0.100, 16 Oct 2009"; /* C K U S I G -- Kermit signal handling for Unix and OS/2 systems */ /* Author: Jeffrey Altman (jaltman@secure-endpoints.com), Secure Endpoints Inc., New York City. Copyright (C) 1985, 2009, Trustees of Columbia University in the City of New York. All rights reserved. See the C-Kermit COPYING.TXT file or the copyright text in the ckcmai.c module for disclaimer and permissions. */ #include "ckcsym.h" #include "ckcasc.h" /* ASCII character symbols */ #include "ckcdeb.h" /* Debug & other symbols */ #include "ckcker.h" /* Kermit symbols */ #include "ckcnet.h" /* Network symbols */ #ifndef NOSPL #include "ckuusr.h" #endif /* NOSPL */ #include #ifdef NT #include #include #else /* NT */ #include #endif /* NT */ #include "ckcsig.h" #ifdef NOCCTRAP extern ckjmpbuf cmjbuf; #endif /* NOCCTRAP */ #ifdef MAC #define signal msignal #define SIGTYP long #define alarm malarm #define SIG_IGN 0 #define SIGALRM 1 #define SIGINT 2 SIGTYP (*msignal(int type, SIGTYP (*func)(int)))(int); #endif /* MAC */ #ifdef STRATUS /* We know these are set here. MUST unset them before the definitions. */ #define signal vsignal #define alarm valarm SIGTYP (*vsignal(int type, SIGTYP (*func)(int)))(int); int valarm(int interval); #endif /* STRATUS */ #ifdef AMIGA #define signal asignal #define alarm aalarm #define SIGALRM (_NUMSIG+1) #define SIGTYP void SIGTYP (*asignal(int type, SIGTYP (*func)(int)))(int); unsigned aalarm(unsigned); #endif /* AMIGA */ #ifdef NTASM DWORD ckgetIP(void) { __asm { mov eax, dword ptr [esp+0x10] jmp ckgetIP + 0x18 } return 1; } #endif /* NTASM */ #ifdef NT DWORD exception_filter( void ) { GetExceptionInformation ; return( EXCEPTION_EXECUTE_HANDLER ) ; } void crash( void ) { int x = 0, y = 0 ; x / y ; } #endif /* NT */ #ifndef NOCCTRAP int #ifdef CK_ANSIC cc_execute( ckjptr(sj_buf), ck_sigfunc dofunc, ck_sigfunc failfunc ) #else cc_execute( sj_buf, dofunc, failfunc) ckjptr(sj_buf); ck_sigfunc dofunc; ck_sigfunc failfunc; #endif /* CK_ANSIC */ /* cc_execute */ { int rc = 0 ; #ifdef NTASM DWORD Eip, Esp ; isinterrupted = 0; sj_buf->retcode = 0 ; sj_buf->Id = GetCurrentThreadId() ; memset( &sj_buf->context, 0, sizeof(CONTEXT) ); sj_buf->context.ContextFlags = CONTEXT_FULL ; #ifndef COMMENT GetThreadContext(GetCurrentThread(), &(sj_buf->context) ) ; __asm { mov ecx,dword ptr [sj_buf] mov dword ptr [ecx+0xc4],esp } sj_buf->context.EFlags = 530 ; sj_buf->context.Eip = ckgetIP()+0x0C ; #else /* COMMENT */ __asm { mov eax, dword ptr [sj_buf] push eax mov eax, 0xfffffffe push eax mov eax, 0x00000039 mov edx,esp int 0x2e pop eax pop eax } #endif /* COMMENT */ #endif /* NTASM */ if ( #ifdef NTASM isinterrupted #else cksetjmp(ckjdref(sj_buf)) #endif /* NTASM */ ) { #ifdef NTASM __asm { mov esp, ESPToRestore } isinterrupted = 0 ; #endif /* NTASM */ (*failfunc)(NULL) ; #ifdef NTASM rc = sj_buf->retcode ; #else /* NTASM */ rc = -1 ; #endif /* NTASM */ } else { #ifdef NT __try { (*dofunc)(NULL); } __except(exception_filter()) { debug(F100,"cc_execute __except","",0); debug(F111, "exception_filter", "_exception_code", etExceptionCode() ); longjmp(ckjdref(sj_buf),SIGINT); } #else /* NT */ (*dofunc)(NULL); #endif /* NT */ } return rc ; } #endif /* NOCCTRAP */ int #ifdef CK_ANSIC /* ANSIC C declaration... */ alrm_execute(ckjptr(sj_buf), int timo, ck_sighand handler, ck_sigfunc dofunc, ck_sigfunc failfunc ) #else /* Not ANSIC C ... */ alrm_execute(sj_buf, timo, handler, dofunc, failfunc ) ckjptr(sj_buf); int timo; ck_sighand handler; ck_sigfunc dofunc; ck_sigfunc failfunc; #endif /* CK_ANSIC */ /* alrm_execute */ { int rc = 0; int savalrm = 0; _PROTOTYP(SIGTYP (*savhandler), (int)); savalrm = alarm(timo); savhandler = signal(SIGALRM, handler); #ifdef NTASM sj_buf->retcode = 0 ; sj_buf->Id = GetCurrentThreadId(); memset(&sj_buf->context, 0, sizeof(CONTEXT)); sj_buf->context.ContextFlags = CONTEXT_FULL; #ifndef COMMENT GetThreadContext(GetCurrentThread(), &(sj_buf->context)); #else __asm { mov eax, dword ptr [sj_buf] push eax mov eax, 0xfffffffe push eax mov eax, 0x00000039 mov edx,esp int 0x2e pop eax pop eax } #endif isinterrupted = 0; #endif /* NTASM */ if ( #ifdef NTASM sj_buf->retcode #else cksetjmp(ckjdref(sj_buf)) #endif /* NTASM */ ) { (*failfunc)(NULL) ; rc = -1 ; } else { #ifdef NT __try { (*dofunc)(NULL) ; } __except( exception_filter() ) { debug(F100,"alrm_execute __except","",0); debug(F111,"exception_filter", "_exception_code", GetExceptionCode() ); longjmp(ckjdref(sj_buf),SIGINT); } #else /* NT */ (*dofunc)(NULL) ; #endif /* NT */ } alarm(savalrm) ; if ( savhandler ) signal( SIGALRM, savhandler ) ; return rc ; } int #ifdef CK_ANSIC /* ANSIC C declaration... */ cc_alrm_execute(ckjptr(sj_buf), int timo, ck_sighand handler, ck_sigfunc dofunc, ck_sigfunc failfunc ) #else /* Not ANSIC C ... */ cc_alrm_execute(sj_buf, timo, handler, dofunc, failfunc ) ckjptr(sj_buf); int timo; ck_sighand handler; ck_sigfunc dofunc; ck_sigfunc failfunc; #endif /* CK_ANSIC */ /* cc_alrm_execute */ { int rc = 0; int savalrm = 0; _PROTOTYP(SIGTYP (*savhandler), (int)); savalrm = alarm(timo); savhandler = signal( SIGALRM, handler ); #ifdef NTASM sj_buf->retcode = 0 ; sj_buf->Id = GetCurrentThreadId() ; memset( &sj_buf->context, 0, sizeof(CONTEXT) ); sj_buf->context.ContextFlags = CONTEXT_FULL ; #ifndef COMMENT GetThreadContext( GetCurrentThread(), &(sj_buf->context) ) ; #else __asm { mov eax, dword ptr [sj_buf] push eax mov eax, 0xfffffffe push eax mov eax, 0x00000039 mov edx,esp int 0x2e pop eax pop eax } #endif isinterrupted = 0; #endif /* NTASM */ if ( #ifdef NTASM sj_buf->retcode #else cksetjmp(ckjdref(sj_buf)) #endif /* NTASM */ ) { (*failfunc)(NULL) ; rc = -1 ; } else { #ifdef NT __try { (*dofunc)(NULL) ; } __except( exception_filter() ) { debug(F100,"cc_alrm_execute __except","",0); debug(F111, "exception_filter", "_exception_code", GetExceptionCode() ); longjmp(ckjdref(sj_buf),SIGINT) ; } #else /* NT */ (*dofunc)(NULL) ; #endif /* NT */ } alarm(savalrm); if (savhandler) signal(SIGALRM,savhandler); return(rc); } ckusig.h0000644000015300001460000000410711266111455011323 0ustar fdckermit/* C K U S I G . H */ /* Definitions and prototypes for signal handling */ /* Author: Jeffrey E Altman (jaltman@secure-endpoints.com), Secure Endpoints Inc., New York City. Copyright (C) 1985, 2009, Trustees of Columbia University in the City of New York. All rights reserved. See the C-Kermit COPYING.TXT file or the copyright text in the ckcmai.c module for disclaimer and permissions. */ #ifdef CK_ANSIC typedef void (*ck_sigfunc)(void *); typedef void (*ck_sighand)(int); #else typedef VOID (*ck_sigfunc)(); typedef VOID (*ck_sighand)(); #endif /* CK_ANSIC */ /* Macros for POSIX vs old-style signal handling. */ #ifdef CK_POSIX_SIG typedef sigjmp_buf ckjmpbuf; #else typedef jmp_buf ckjmpbuf; #endif /* CK_POSIX_SIG */ /* Suppose you want to pass the address of a jmp_buf bar to a function foo. Since jmp_buf is normally defined (typedef'd) as an array, you would do it like this: foo(bar), where foo = foo(jmp_buf bar). But suppose a jmp_buf is (say) a struct rather than an array. Then you must do foo(&bar) where foo is foo(jmp_buf * bar). This is controlled here in the traditional fashion, by ifdefs. By default, we assume that jmp_buf is an array. Define the symbol JBNOTARRAY if jmp_buf is not an array. */ #ifndef JBNOTARRAY #ifdef NT #define JBNOTARRAY #endif /* NT */ #endif /* JBNOTARRAY */ #ifdef JBNOTARRAY typedef ckjmpbuf * ckjptr; #define ckjaddr(x) & x #define ckjdref(x) * x #ifdef CK_POSIX_SIG #define cksetjmp(x) sigsetjmp(x,1) #else #define cksetjmp(x) setjmp(x,1) #endif /* CK_POSIX_SIG */ #else /* jmp_buf is an array */ typedef ckjmpbuf ckjptr; #define ckjaddr(x) x #define ckjdref(x) x #ifdef CK_POSIX_SIG #define cksetjmp sigsetjmp #else #define cksetjmp setjmp #endif /* CK_POSIX_SIG */ #endif /* JBNOTARRAY */ _PROTOTYP( int cc_execute, (ckjptr, ck_sigfunc, ck_sigfunc) ); _PROTOTYP( int alrm_execute, (ckjptr, int timo, ck_sighand handler, ck_sigfunc, ck_sigfunc) ); _PROTOTYP( int cc_alrm_execute, (ckjptr, int timo, ck_sighand handler, ck_sigfunc, ck_sigfunc) ); /* End of ckusig.h */ ckutio.c0000644000015300001460000157164711624022633011347 0ustar fdckermit#define CKUTIO_C #ifdef aegis char *ckxv = "Aegis Communications support, 9.0.326, 20 August 2011"; #else #ifdef Plan9 char *ckxv = "Plan 9 Communications support, 9.0.326, 20 August 2011"; #else char *ckxv = "UNIX Communications support, 9.0.326, 20 August 2011"; #endif /* Plan9 */ #endif /* aegis */ /* C K U T I O */ /* C-Kermit interrupt, communications control and I/O functions for UNIX */ /* Author: Frank da Cruz (fdc@columbia.edu), Columbia University Academic Information Systems, New York City. Copyright (C) 1985, 2011, Trustees of Columbia University in the City of New York. All rights reserved. See the C-Kermit COPYING.TXT file or the copyright text in the ckcmai.c module for disclaimer and permissions. */ /* NOTE TO CONTRIBUTORS: This file, and all the other C-Kermit files, must be compatible with C preprocessors that support only #ifdef, #else, #endif, #define, and #undef. Please do not use #if, logical operators, or other preprocessor features in any of the portable C-Kermit modules. You can, of course, use these constructions in platform-specific modules when they are supported by all compilers/preprocessors that could be used on that platform. */ extern int nettype; /* Defined in ckcmai.c */ extern int duplex; /* Includes */ #include "ckcsym.h" /* This must go first */ #include "ckcdeb.h" /* This must go second */ #ifdef OSF13 #ifdef CK_ANSIC #ifdef _NO_PROTO #undef _NO_PROTO #endif /* _NO_PROTO */ #endif /* CK_ANSIC */ #endif /* OSF13 */ #ifndef HPUXPRE65 #include /* Error number symbols */ #else #ifndef ERRNO_INCLUDED #include /* Error number symbols */ #endif /* ERRNO_INCLUDED */ #endif /* HPUXPRE65 */ #ifdef __386BSD__ #define ENOTCONN 57 #else #ifdef __bsdi__ #define ENOTCONN 57 #else #ifdef __FreeBSD__ #define ENOTCONN 57 #endif /* __FreeBSD__ */ #endif /* __bsdi__ */ #endif /* __386BSD__ */ #ifdef SCO_OSR504 #define NBBY 8 #endif /* SCO_OSR504 */ #ifdef Plan9 #define SELECT #include #include #define FD_SETSIZE (3 * sizeof(long) * 8) static struct timeval tv; #endif /* Plan9 */ #ifdef CLIX #include #endif /* CLIX */ #include "ckcnet.h" /* Symbols for network types. */ #ifdef CK_SSL #include "ck_ssl.h" #endif /* CK_SSL */ /* The directory-related includes are here because we need to test some file-system-related symbols to find out which system we're being compiled under. For example, MAXNAMLEN is defined in BSD4.2 but not 4.1. */ #ifdef SDIRENT /* Directory bits... */ #define DIRENT #endif /* SDIRENT */ #ifdef XNDIR #include #else /* !XNDIR */ #ifdef NDIR #include #else /* !NDIR, !XNDIR */ #ifdef RTU #include "/usr/lib/ndir.h" #else /* !RTU, !NDIR, !XNDIR */ #ifdef DIRENT #ifdef SDIRENT #include #else #include #endif /* SDIRENT */ #else /* !RTU, !NDIR, !XNDIR, !DIRENT, i.e. all others */ #include #endif /* DIRENT */ #endif /* RTU */ #endif /* NDIR */ #endif /* XNDIR */ #ifdef QNX #include #endif /* QNX */ #ifdef HPUX5 #ifndef TCPSOCKET /* I don't know why this is needed here since we never reference bzero(). */ /* But without it C-Kermit won't link in an HP-UX 5.xx non-TCP build. */ void bzero(s,n) char *s; int n; { extern char * memset(); memset(s,0,n); } #endif /* TCPSOCKET */ #endif /* HPUX5 */ /* Definition of HZ, used in msleep() */ #ifdef MIPS #define HZ ( 1000 / CLOCK_TICK ) #else /* MIPS */ #ifdef ATTSV #ifndef NAP #ifdef TRS16 #define HZ ( 1000 / CLOCK_TICK ) #endif /* TRS16 */ #ifdef NAPHACK #define nap(x) (void)syscall(3112, (x)) #define NAP #endif /* NAPHACK */ #endif /* NAP */ #endif /* ATTSV */ #endif /* MIPS */ #ifdef M_UNIX #undef NGROUPS_MAX /* Prevent multiple definition warnings */ #endif /* M_UNIX */ /* NOTE: HP-UX 8.0 has a , but there is no corresponding library routine, so _poll comes up undefined at link time. */ #ifdef CK_POLL #ifndef AIXRS /* IBM AIX needs special handling */ #include /* "standard" (SVID) i/o multiplexing, etc */ #else /* AIXRS */ #ifdef SVR4 /* AIX 3.2 is like SVID... */ #include #else /* But AIX 3.1 is not ... */ #include /* The include file is in include/sys */ #define events reqevents /* And it does not map IBM-specific member */ #define revents rtnevents /* names to the System V equivalents */ #endif /* SVR4 */ #endif /* AIXRS */ #endif /* CK_POLL */ #include /* Signals */ /* For setjmp and longjmp */ #ifndef ZILOG #include #else #include #endif /* ZILOG */ /* The following test differentiates between 4.1 BSD and 4.2 & later. If you have a 4.1BSD system with the DIRENT library, this test could mistakenly diagnose 4.2BSD and then later enable the use of system calls that aren't defined. If indeed there are such systems, we can use some other way of testing for 4.1BSD, or add yet another compile-time switch. */ #ifdef BSD4 #ifdef MAXNAMLEN #ifndef FT21 /* Except for Fortune. */ #ifndef FT18 #ifndef BELLV10 /* And Bell Labs Research UNIX V10 */ #define BSD42 #endif /* BELLV10 */ #endif /* FT18 */ #endif /* FT21 */ #endif /* MAXNAMLEN */ #endif /* BSD4 */ #ifdef SUNOS41 /* From Christian Corti */ #define BSD44ORPOSIX /* Uni Stuttgart */ #define SVORPOSIX /* February 2010 */ #include #include #include #include #endif /* SUNOS41 */ #ifdef SNI542 #include /* 299 for FIONREAD */ #endif /* SNI542 */ /* Minix 2.0 support added by Terry McConnell, Syracuse University No more sgtty interface, posix compliant. */ #ifdef MINIX2 #define _MINIX /* Needed for some Minix header files */ #define BSD44ORPOSIX #define SVORPOSIX #ifndef MINIX3 #define DCLTIMEVAL #endif /* MINIX3 */ #define NOFILEH #include #include #include #include #undef TIOCGETC /* defined in sys/ioctl.h, but not really supported */ #define TANDEM 0 #endif /* MINIX2 */ /* MINIX 1.0 support added by Charles Hedrick, Rutgers University . MINIX also has V7 enabled. */ #ifdef MINIX #define TANDEM 0 #define MYREAD #define NOSYSIOCTLH #include #endif /* MINIX */ #ifdef CK_REDIR /* needed only for REDIRECT command. */ /* If anybody can figure out how to make this work with NeXTSTEP, be my guest! (NeXTBlah/NeXTBlah/bsd/sys/wait.h does not define WEXITSTATUS) */ #ifndef CK_WAIT_H /* If wait.h not already included... */ #ifdef OSF /* force OSF to select POSIX wait */ #ifdef _BSD /* instead of BSD (see ckcdeb.h) */ #define CK_OSF_BSD #undef _BSD #endif /* _BSD */ #endif /* OSF */ #include /* Include it */ #ifdef OSF #ifdef CK_OSF_BSD #define _BSD /* Restore it */ #undef CK_OSF_BSD #endif /* CK_OSF_BSD */ #endif /* OSF */ #endif /* CK_WAIT_H */ #endif /* CK_REDIR */ #include "ckuver.h" /* Version herald */ char *ckxsys = HERALD; #ifdef CK_UTSNAME #include #ifdef TRU64 /* Tru64 UNIX 4.0 and later */ /* Verified on Tru64 4.0F - might break on 4.0E or earlier */ #include /* (don't know about OSF/1 or DU) */ #include #endif /* TRU64 */ #ifdef SOLARIS25 /* Solaris 2.5 and later */ #include /* (don't know about earlier ones) */ #endif /* SOLARIS25 */ #ifdef UW7 #ifndef SYS_NMLN #define SYS_NMLN 257 #endif /* NMLN */ #endif /* UW7 */ #ifdef HPUX9PLUS static int hpis800 = 0; #endif /* HPUX9PLUS */ #ifdef SYS_NMLN #define CK_SYSNMLN SYS_NMLN #else #ifdef _SYS_NMLN #define CK_SYSNMLN _SYS_NMLN #else #ifdef UTSLEN #define CK_SYSNMLN UTSLEN #else #define CK_SYSNMLN 31 #endif /* UTSLEN */ #endif /* _SYS_NMLN */ #endif /* SYS_NMLN */ char unm_mch[CK_SYSNMLN+1] = { '\0', '\0' }; char unm_mod[CK_SYSNMLN+1] = { '\0', '\0' }; char unm_nam[CK_SYSNMLN+1] = { '\0', '\0' }; char unm_rel[CK_SYSNMLN+1] = { '\0', '\0' }; char unm_ver[CK_SYSNMLN+1] = { '\0', '\0' }; #endif /* CK_UTSNAME */ #ifdef CIE #include /* For chasing symlinks, etc. */ #else #include #endif /* CIE */ #ifdef QNX /* 299 */ #ifndef IXANY #define IXANY 0 #endif /* IXANY */ #endif /* QNX */ /* UUCP lockfile material... */ #ifndef NOUUCP #ifdef USETTYLOCK #ifdef HAVE_LOCKDEV /* Red Hat baudboy/lockdev */ /* Watch out: baudboy.h references open() without making sure it has been declared, resulting in warnings on at least Red Hat 7.3. It's declared in fcntl.h, but we don't include that until later. In this case only, we include it here, and then the second include is harmless because in Red Hat Linux (the only place where you find baudboy.h) fcntl.h is protected from multiple inclusion by _FCNTL_H. - fdc, 10 May 2004. NOTE: Although Linux /usr/sbin/lockdev obviates the need for setuid or setgid bits to access the lockfile, C-Kermit will still need them to access the serial port itself unless the port is open for world read/write. Normally setgid uucp does the trick. Extra: HAVE_LOCKDEV has been added als openSuSE >= 11.3 doesn't use baudboy but ttylock. - jb, 26 Jul 2010 */ #include /* This has to come before baudboy */ #ifdef HAVE_BAUDBOY /* Red Hat baudboy/lockdev */ #include #else /* !HAVE_BAUDBOY */ /* openSuSE lock via ttylock */ #include #endif /* HAVE_BAUDBOY */ #define LOCK_DIR "/var/lock" /* (even though we don't care) */ #else /* !HAVE_LOCKDEV */ #ifdef USE_UU_LOCK #ifdef __FreeBSD__ #include /* FreeBSD */ #else #include /* OpenBSD */ #endif /* HAVE_LOCKDEV */ #endif /* __FreeBSD */ #endif /* USE_UU_LOCK */ #else /* USETTYLOCK */ /* Name of UUCP tty device lockfile */ #ifdef LINUXFSSTND #ifndef HDBUUCP #define HDBUUCP #endif /* HDBUUCP */ #endif /* LINUXFSSTND */ #ifdef ACUCNTRL #define LCKDIR #endif /* ACUCNTRL */ /* PIDSTRING means use ASCII string to represent pid in lockfile. */ #ifndef PIDSTRING #ifdef HDBUUCP #define PIDSTRING #else #ifdef BSD44 #define PIDSTRING #else #ifdef RTAIX #define PIDSTRING #else #ifdef AIXRS #define PIDSTRING #else #ifdef COHERENT #define PIDSTRING #endif /* COHERENT */ #endif /* AIXRS */ #endif /* RTAIX */ #endif /* BSD44 */ #endif /* HDBUUCP */ #endif /* PIDSTRING */ /* Now the PIDSTRING exceptions... */ #ifdef PIDSTRING #ifdef HPUX #undef PIDSTRING #endif /* HPUX */ #endif /* PIDSTRING */ #ifdef __bsdi__ /* BSDI (at least thru 1.1) */ #ifdef PIDSTRING #undef PIDSTRING #endif /* PIDSTRING */ #endif /* __bsdi__ */ #ifdef OSF32 /* Digital UNIX (OSF/1) 3.2 */ #ifdef PIDSTRING #undef PIDSTRING #endif /* PIDSTRING */ #endif /* OSF32 */ /* LOCK_DIR is the name of the lockfile directory. If LOCK_DIR is already defined (e.g. on command line), we don't change it. */ #ifndef LOCK_DIR #ifdef MACOSX #define LOCK_DIR "/var/spool/lock" #endif /* MACOSX */ #endif/* LOCK_DIR */ #ifndef LOCK_DIR #ifdef BSD44 #ifdef __386BSD__ #define LOCK_DIR "/var/spool/lock" #else #ifdef __FreeBSD__ #define LOCK_DIR "/var/spool/lock" #else #ifdef __NetBSD__ #define LOCK_DIR "/var/spool/lock" #else #ifdef __OpenBSD__ #define LOCK_DIR "/var/spool/lock" #else /* So which ones is this for? */ /* Probably original 4.4BSD on Vangogh */ /* Plus who knows about Mac OS X... It doesn't even have a cu program */ #define LOCK_DIR "/var/spool/uucp" #endif /* __OpenBSD__ */ #endif /* __NetBSD__ */ #endif /* __FreeBSD__ */ #endif /* __386BSD__ */ #else #ifdef DGUX430 #define LOCK_DIR "/var/spool/locks" #else #ifdef HPUX10 #define LOCK_DIR "/var/spool/locks" #else #ifdef RTAIX /* IBM RT PC AIX 2.2.1 */ #define LOCK_DIR "/etc/locks" #else #ifdef AIXRS #define LOCK_DIR "/etc/locks" #else #ifdef ISIII #define LOCK_DIR "/etc/locks" #else #ifdef HDBUUCP #ifdef M_SYS5 #define LOCK_DIR "/usr/spool/uucp" #else #ifdef M_UNIX #define LOCK_DIR "/usr/spool/uucp" #else #ifdef SVR4 #define LOCK_DIR "/var/spool/locks" #else #ifdef SUNOS4 #define LOCK_DIR "/var/spool/locks" #else #ifdef LINUXFSSTND #define LOCK_DIR "/var/lock"; #else #define LOCK_DIR "/usr/spool/locks" #endif /* LINUXFSSTND */ #endif /* SUNOS4 */ #endif /* SVR4 */ #endif /* M_UNIX */ #endif /* M_SYS5 */ #else #ifdef LCKDIR #define LOCK_DIR "/usr/spool/uucp/LCK" #else #ifdef COHERENT #define LOCK_DIR "/usr/spool/uucp" #else #define LOCK_DIR "/usr/spool/uucp" #endif /* COHERENT */ #endif /* LCKDIR */ #endif /* HDBUUCP */ #endif /* ISIII */ #endif /* AIXRS */ #endif /* RTAIX */ #endif /* HPUX10 */ #endif /* DGUX430 */ #endif /* BSD44 */ #endif /* !LOCK_DIR (outside ifndef) */ #ifdef OSF2 /* OSF/1 2.0 or later */ #ifdef LOCK_DIR /* (maybe 1.x too, who knows...) */ #undef LOCK_DIR #define LOCK_DIR "/var/spool/locks" #endif /* LOCK_DIR */ #endif /* OSF2 */ #ifdef COMMENT /* Sorry no more lockf() -- we lock first and THEN open the device. */ #ifdef SVR4 #ifndef BSD44 #ifndef LOCKF #define LOCKF /* Use lockf() on tty device in SVR4 */ #endif /* LOCKF */ #endif /* BSD44 */ #endif /* SVR4 */ #endif /* COMMENT */ #ifdef NOLOCKF /* But NOLOCKF cancels LOCKF */ #ifdef LOCKF #undef LOCKF #endif /* LOCKF */ #endif /* NOLOCKF */ /* More about this below... */ #endif /* USETTYLOCK */ #endif /* NOUUCP */ /* MYREAD means use our internally defined nonblocking buffered read routine. */ #ifdef ATTSV #define MYREAD #endif /* ATTSV */ #ifdef ATT7300 #ifndef MYREAD #define MYREAD #endif /* MYREAD */ /* bits for attmodem: internal modem in use, restart getty */ #define ISMODEM 1 #define DOGETY 512 #endif /* ATT7300 */ #ifdef BSD42 #define MYREAD #endif /* BSD42 */ #ifdef POSIX #define MYREAD #endif /* POSIX */ #ifdef __bsdi__ #ifndef O_NDELAY #define O_NDELAY O_NONBLOCK #endif /* O_NDELAY */ #endif /* __bsdi__ */ /* Variables available to outside world: dftty -- Pointer to default tty name string, like "/dev/tty". dfloc -- 0 if dftty is console, 1 if external line. dfprty -- Default parity dfflow -- Default flow control ckxech -- Flag for who echoes console typein: 1 - The program (system echo is turned off) 0 - The system (or front end, or terminal). functions that want to do their own echoing should check this flag before doing so. flfnam -- Name of lock file, including its path, e.g., "/usr/spool/uucp/LCK..cul0" or "/etc/locks/tty77" lkflfn -- Name of link to lock file, including its paths haslock -- Flag set if this kermit established a uucp lock. lockpid -- PID of other process that has desired line open, as string. backgrd -- Flag indicating program executing in background ( & on end of shell command). Used to ignore INT and QUIT signals. rtu_bug -- Set by stptrap(). RTU treats ^Z as EOF (but only when we handle SIGTSTP) Functions for assigned communication line (either external or console tty): sysinit() -- System dependent program initialization syscleanup() -- System dependent program shutdown ttopen(ttname,local,mdmtyp,timo) -- Open the named tty for exclusive access. ttclos() -- Close & reset the tty, releasing any access lock. ttsspd(cps) -- Set the transmission speed of the tty. ttgspd() -- Get (read) the the transmission speed of the tty. ttpkt(speed,flow,parity) -- Put the tty in packet mode and set the speed. ttvt(speed,flow) -- Put the tty in virtual terminal mode. or in DIALING or CONNECTED modem control state. ttres() -- Restore original tty modes. ttscarr(carrier) -- Set carrier control mode, on/off/auto. ttinl(dest,max,timo) -- Timed read line from the tty. ttinc(timo) -- Timed read character from tty. myread() -- Raw mode bulk buffer read, gives subsequent chars one at a time and simulates FIONREAD. myunrd(c) -- Places c back in buffer to be read (one only) ttchk() -- See how many characters in tty input buffer. ttxin(n,buf) -- Read n characters from tty (untimed). ttol(string,length) -- Write a string to the tty. ttoc(c) -- Write a character to the tty. ttflui() -- Flush tty input buffer. ttsndb() -- Send BREAK signal. ttsndlb() -- Send Long BREAK signal. ttlock(ttname) -- "Lock" tty device against uucp collisions. ttunlck() -- Unlock tty device. For ATT7300/Unix PC, System V: attdial(ttname,speed,telnbr) -- dials ATT7300/Unix PC internal modem offgetty(ttname) -- Turns off getty(1m) for comms line ongetty(ttname) -- Restores getty() to comms line */ /* Functions for console terminal: congm() -- Get console terminal modes. concb(esc) -- Put the console in single-character wakeup mode with no echo. conbin(esc) -- Put the console in binary (raw) mode. conres() -- Restore the console to mode obtained by congm(). conoc(c) -- Unbuffered output, one character to console. conol(s) -- Unbuffered output, null-terminated string to the console. conola(s) -- Unbuffered output, array of strings to the console. conxo(n,s) -- Unbuffered output, n characters to the console. conchk() -- Check if characters available at console (bsd 4.2). Check if escape char (^\) typed at console (System III/V). coninc(timo) -- Timed get a character from the console. congks(timo) -- Timed get keyboard scan code. conint() -- Enable terminal interrupts on the console if not background. connoi() -- Disable terminal interrupts on the console if not background. Time functions msleep(m) -- Millisecond sleep ztime(&s) -- Return pointer to date/time string rtimer() -- Reset timer gtimer() -- Get elapsed time since last call to rtimer() */ /* Conditional Includes */ /* Whether to include */ #ifdef RTU /* RTU doesn't */ #define NOFILEH #endif /* RTU */ #ifdef CIE /* CIE does. */ #undef NOFILEH #endif /* CIE */ #ifdef BSD41 /* 4.1 BSD doesn't */ #define NOFILEH #endif /* BSD41 */ #ifdef is68k /* Integrated Solutions 68000 UNIX */ #define NOFILEH /* e.g. on Plexux P60 and Sun-1 */ #endif /* is68k */ #ifdef MINIX /* MINIX */ #define NOFILEH #endif /* MINIX */ #ifdef COHERENT /* Coherent */ #define NOFILEH #endif /* COHERENT */ #ifndef NOFILEH /* Now include if selected. */ #include #endif /* NOFILEH */ /* POSIX */ #ifdef BSD44ORPOSIX /* POSIX uses termios.h */ #define TERMIOS #ifdef __bsdi__ #ifdef POSIX #undef _POSIX_SOURCE /* Get extra stuff from termios.h */ #endif /* POSIX */ #endif /* __bsdi__ */ #include #ifdef LINUX #include #endif /* LINUX */ #ifdef QNX16 #include #else #ifdef QNX6 #include #endif /* QNX6 */ #endif /* QNX16 */ #ifdef __bsdi__ #ifdef POSIX #define _POSIX_SOURCE #endif /* POSIX */ #endif /* __bsdi__ */ #ifndef BSD44 /* Really POSIX */ #ifndef CK_QNX32 /* was CK_QNX32 */ #define NOSYSIOCTLH /* No ioctl's allowed. */ #undef ultrix /* Turn off any ultrix features. */ #endif /* CK_QNX32 */ #endif /* BSD44 */ #endif /* POSIX */ /* System III, System V */ #ifdef ATTSV #ifndef BSD44 #ifndef POSIX #include #endif /* POSIX */ #endif /* BSD44 */ #ifdef TERMIOX /* Need this for termiox structure, RTS/CTS and DTR/CD flow control */ #include struct termiox rctsx; #else #ifdef STERMIOX #ifdef SCO_OSR504 /* Sorry, this is truly disgusting but it's SCO's fault. */ #ifndef _SVID3 #define _CK_SVID3_X #define _SVID3 #endif /* _SVID3 */ #endif /* SCO_OSR504 */ #include struct termiox rctsx; #ifdef CK_SVID3_X #undef _SVID3 #undef CK_SVID3_X #endif /* CK_SVID3_X */ #endif /* STERMIOX */ #endif /* TERMIOX */ #endif /* ATTSV */ #ifdef COHERENT /* Use termio.h, not sgtty.h for Coherent */ #include #endif /* COHERENT */ #ifdef MINIX /* MINIX uses ioctl's */ #define NOSYSIOCTLH /* but has no */ #endif /* MINIX */ /* Others */ #ifndef NOSYSIOCTLH /* Others use ioctl() */ #ifdef SUN4S5 /* This is to get rid of cpp warning messages that occur because all of these symbols are defined by both termios.h and ioctl.h on the SUN. */ #undef ECHO #undef NL0 #undef NL1 #undef TAB0 #undef TAB1 #undef TAB2 #undef XTABS #undef CR0 #undef CR1 #undef CR2 #undef CR3 #undef FF0 #undef FF1 #undef BS0 #undef BS1 #undef TOSTOP #undef FLUSHO #undef PENDIN #undef NOFLSH #endif /* SUN4S5 */ #include #endif /* NOSYSIOCTLH */ /* We really, really, REALLY want FIONREAD, because it is the only way to find out not just *if* stuff is waiting to be read, but how much, which is critical to our sliding-window and streaming procedures, not to mention efficiency of CONNECT, etc. */ #ifdef BELLV10 #include /* For FIONREAD */ #ifdef FIONREAD #define MYREAD #endif /* MYREAD */ #endif /* BELLV10 */ #ifndef FIONREAD /* It wasn't found in ioctl.h or term*.h - try these places: */ #ifdef UNIXWARE #include #else #ifdef SOLARIS #include #endif /* SOLARIS */ #endif /* UNIXWARE */ #endif /* FIONREAD */ #ifdef XENIX /* Was M_UNIX but XENIX implies M_UNIX and applies to XENIX too */ /* included above via "ckcnet.h" defines FIONREAD as something. Due to this, in_chk() uses the FIONREAD instead of RDCHK and the hot keys during file transfer (X to cancel file etc) do not work because FIONREAD doesn't work even though it is defined. NOTE: This might also be true elsewhere. */ #ifdef FIONREAD #undef FIONREAD #endif /* FIONREAD */ #endif /* XENIX */ #ifdef CK_SCOV5 /* Ditto for SCO OpenServer 5.0 */ #ifndef SCO_OSR507 /* 299 */ #ifdef FIONREAD #undef FIONREAD #endif /* FIONREAD */ #endif /* SCO_OSR507 */ #endif /* CK_SCOV5 */ #ifdef SCO_OSR507 /* 299 */ #ifdef RDCHK #undef RDCHK #endif /* RDCHK */ #endif /* SCO_OSR507 */ /* Whether to include */ #ifndef is68k /* Only a few don't have this one. */ #ifndef BSD41 #ifndef FT21 #ifndef FT18 #ifndef COHERENT #include #endif /* COHERENT */ #endif /* FT18 */ #endif /* FT21 */ #endif /* BSD41 */ #endif /* not is68k */ #ifdef COHERENT #ifdef _I386 #include #else #include #endif /* _I386 */ #endif /* COHERENT */ #ifdef ATT7300 /* Unix PC, internal modem dialer */ #include #endif /* ATT7300 */ #ifdef HPUX /* HP-UX variations. */ #define HPUXJOBCTL #include /* HP-UX modem signals */ #ifdef hp9000s500 /* Model 500 */ #undef HPUXJOBCTL #endif /* hp9000s500 */ #ifdef HPUXPRE65 #undef HPUXJOBCTL typedef long mflag; #endif /* HPUXPRE65 */ #ifdef HPUXJOBCTL #include /* HP-UX Berkeley tty support */ #endif /* HPUXJOBCTL */ #endif /* HPUX */ /* Which time.h files to include... See ckcdeb.h for defaults. Note that 0, 1, 2, or all 3 of these can be included according to the symbol definitions. */ #ifndef NOTIMEH #ifdef TIMEH #include #endif /* TIMEH */ #endif /* NOTIMEH */ #ifndef NOSYSTIMEH #ifdef SYSTIMEH #include #endif /* SYSTIMEH */ #endif /* NOSYSTIMEH */ #ifndef NOSYSTIMEBH #ifdef SYSTIMEBH #include #endif /* SYSTIMEBH */ #endif /* NOSYSTIMEBH */ #ifndef NODCLTIMEVAL #ifdef DCLTIMEVAL /* In certain POSIX builds (like Unixware 7), <[sys/]time.h> refuses to define the structs we need to access the higher speeds, so we have to do it ourselves. */ struct timeval { long tv_sec; long tv_usec; }; struct timezone { int tz_minuteswest; int tz_dsttime; }; #endif /* DCLTIMEVAL */ #endif /* NODCLTIMEVAL */ #ifdef __linux__ /* THIS IS OBSOLETE since about Linux 0.92 */ #ifdef OLINUXHISPEED #include #endif /* OLINUXHISPEED */ #ifdef __alpha__ /* Linux on DEC Alpha */ #ifndef __GLIBC__ /* But not with glibc */ #include #endif /* __GLIBC__ */ #endif /* __alpha__ */ #endif /* __linux__ */ #ifdef NOIEXTEN /* This is broken on some systems */ #undef IEXTEN /* like Convex/OS 9.1 */ #endif /* NOIEXTEN */ #ifndef IEXTEN /* Turn off ^O/^V processing. */ #define IEXTEN 0 /* Needed, at least, on BSDI. */ #endif /* IEXTEN */ /* Pick up definitions needed for select() if we don't have them already. Normally they come from but some systems get them from ... Rather than hardwire all of them into the source, we include it if SELECT_H is defined in compile-time CFLAGS. */ #ifndef SCO_OSR504 #ifdef SELECT_H #include #endif /* SELECT_H */ #endif /* SCO_OSR504 */ #ifdef aegis #include "/sys/ins/base.ins.c" #include "/sys/ins/error.ins.c" #include "/sys/ins/ios.ins.c" #include "/sys/ins/sio.ins.c" #include "/sys/ins/pad.ins.c" #include "/sys/ins/time.ins.c" #include "/sys/ins/pfm.ins.c" #include "/sys/ins/pgm.ins.c" #include "/sys/ins/ec2.ins.c" #include "/sys/ins/type_uids.ins.c" #include #undef TIOCEXCL #undef FIONREAD #endif /* aegis */ #ifdef sxaE50 /* PFU Compact A SX/A TISP V10/L50 */ #undef FIONREAD #endif /* sxaE50 */ /* The following #defines are catch-alls for those systems */ /* that didn't have or couldn't find ... */ #ifndef FREAD #define FREAD 0x01 #endif /* FREAD */ #ifndef FWRITE #define FWRITE 0x10 #endif /* FWRITE */ #ifndef O_RDONLY #define O_RDONLY 000 #endif /* O_RDONLY */ /* This is for ancient Unixes that don't have these tty symbols defined. */ #ifndef PENDIN #define PENDIN ICANON #endif /* PENDIN */ #ifndef FLUSHO #define FLUSHO ICANON #endif /* FLUSHO */ #ifndef EXTPROC #define EXTPROC ICANON #endif /* EXTPROC */ #ifdef SVORPOSIX /* Modem signals are also forbidden in the POSIX world. But some POSIX-based platforms let us at them anyway if we know where to look. */ #ifndef NEEDMDMDEFS /* Doesn't work for Linux */ #ifdef UNIXWARE7 #define NEEDMDMDEFS #endif /* UNIXWARE7 */ #endif /* NEEDMDMDEFS */ #ifdef NEEDMDMDEFS #ifndef TIOCMGET #define TIOCMGET (('t'<<8)|29) #endif /* TIOCMGET */ #ifndef TIOCM_DTR #define TIOCM_DTR 0x0002 #endif /* TIOCM_DTR */ #ifndef TIOCM_RTS #define TIOCM_RTS 0x0004 #endif /* TIOCM_RTS */ #ifndef TIOCM_CTS #define TIOCM_CTS 0x0020 #endif /* TIOCM_CTS */ #ifndef TIOCM_CAR #define TIOCM_CAR 0x0040 #endif /* TIOCM_CAR */ #ifndef TIOCM_RNG #define TIOCM_RNG 0x0080 #endif /* TIOCM_RNG */ #ifndef TIOCM_DSR #define TIOCM_DSR 0x0100 #endif /* TIOCM_DSR */ #endif /* NEEDMDMDEFS */ #endif /* SVORPOSIX */ /* Declarations */ #ifdef OXOS #undef TCGETA #undef TCSETA #undef TCSETAW #undef TCSETAF #define TCGETA TCGETS #define TCSETA TCSETS #define TCSETAW TCSETSW #define TCSETAF TCSETSF #define termio termios #endif /* OXOS */ #ifdef SVORPOSIX /* AT&T Sys V or POSIX */ #ifdef UNIXWAREPOSIX /* UnixWare 7 POSIX build */ /* In Unixware POSIX builds, <[sys/]time.h> refuses to define the structs we need to access the higher speeds, so we have to do it ourselves. */ struct timeval { long tv_sec; long tv_usec; }; struct timezone { int tz_minuteswest; int tz_dsttime; }; #endif /* UNIXWAREPOSIX */ #endif /* SVORPOSIX */ #ifdef __GNUC__ #ifdef XENIX /* Because Xenix doesn't declare time() if we're using gcc. */ time_t time(); #endif /* XENIX */ #endif /* __GNUC__ */ /* Special stuff for V7 input buffer peeking */ #ifdef V7 int kmem[2] = { -1, -1}; char *initrawq(), *qaddr[2]={0,0}; #define CON 0 #define TTY 1 #endif /* V7 */ /* dftty is the device name of the default device for file transfer */ /* dfloc is 0 if dftty is the user's console terminal, 1 if an external line */ #ifdef BEOS char * dftty = NULL; char * dfmdm = "none"; int dfloc = 0; /* that goes in local mode by default */ #else #ifndef DFTTY #ifdef PROVX1 char *dftty = "/dev/com1.dout"; /* Only example so far of a system */ char *dfmdm = "none"; int dfloc = 1; /* that goes in local mode by default */ #else char *dftty = CTTNAM; /* Remote by default, use normal */ char *dfmdm = "none"; int dfloc = 0; /* controlling terminal name. */ #endif /* PROVX1 */ #else char *dftty = DFTTY; /* Default location specified on */ char *dfmdm = "none"; /* command line. */ int dfloc = 1; /* controlling terminal name. */ #endif /* DFTTY */ #endif /* BEOS */ #define CON_RES 0 /* Console state is "reset" */ #define CON_CB 1 /* Console state is CBREAK */ #define CON_BIN 2 /* Console state is binary */ static int constate = CON_RES; #define CONI_RES 0 /* Console interrupts are "reset" */ #define CONI_INT 1 /* Console intterupts are set */ #define CONI_NOI 2 /* Console intterupts are disabled */ static int conistate = CONI_RES; #ifdef CK_SMALL #define CONBUFSIZ 15 #else #define CONBUFSIZ 255 #endif /* CK_SMALL */ static char conbuf[CONBUFSIZ]; /* Console readahead buffer */ static int conbufn = 0; /* Chars in readahead buffer */ static char *conbufp = conbuf; /* Next char in readahead buffer */ char cttnam[DEVNAMLEN+1] = { '\0', '\0' }; /* Determined at runtime */ #ifdef RTU int rtu_bug = 0; /* set to 1 when returning from SIGTSTP */ #endif /* RTU */ int dfprty = DEFPAR; /* Default parity (0 = none) */ int ttprty = 0; /* The parity that is in use. */ static int ttpmsk = 0xff; /* Parity stripping mask. */ int ttmdm = 0; /* Modem in use. */ int ttcarr = CAR_AUT; /* Carrier handling mode. */ int dfflow = FLO_NONE; /* Default flow control is NONE */ int backgrd = 0; /* Assume in foreground (no '&' ) */ #ifdef F_SETFL int iniflags = -1; /* fcntl flags for ttyfd */ #endif /* F_SETFL */ int fdflag = 0; /* Flag for redirected stdio */ int ttfdflg = 0; /* Open File descriptor was given */ int tvtflg = 0; /* Flag that ttvt has been called */ long ttspeed = -1L; /* For saving speed */ int ttflow = -9; /* For saving flow */ int ttld = -1; /* Line discipline */ #ifdef sony_news static int km_con = -1; /* Kanji mode for console tty */ static int km_ext = -1; /* Kanji mode for external device */ #endif /* sony_news */ #ifdef PARSENSE static int needpchk = 1; /* Need parity check */ #else static int needpchk = 0; #endif /* PARSENSE */ extern int stopbits; /* Stop bits */ #ifdef HWPARITY /* Unfortunately we must do this with global variables rather than through the tt...() APIs to avoid changing the APIs and the many modules that use them. If hwparity != 0, this indicates 8 data bits + parity, rather than 7 data bits + parity or 8 data bits and no parity, and overrides the regular parity variable, which is communicated to this module thru ttpkt(), and represented locally by the ttprty variable. */ extern int hwparity; /* Hardware parity */ #endif /* HWPARITY */ #ifdef TCPSOCKET #ifdef TCP_NODELAY static int nodelay_sav = -1; #endif /* TCP_NODELAY */ #endif /* TCPSOCKET */ static int sigint_ign = 0; /* SIGINT is ignored */ /* Having this module rely on external globals is bad, but fixing this requires overhaul of the ck*tio.c modules for all the different operating systems supported by C-Kermit. Left for a future release. */ extern int ttnproto; /* Defined in ckcnet.c */ extern int ttnet; /* Defined in ckcnet.c */ extern int nopush, xfrcan, xfrchr, xfrnum; /* Defined in ckcmai.c */ extern int xsuspend, wasclosed; extern int inserver, local; int ckxech = 0; /* 0 if system normally echoes console characters, else 1 */ int ckmaxfiles = 0; /* Max number of open files */ #ifdef CK_ENCRYPTION /* Kerberos */ #include "ckuath.h" extern int me_encrypt, u_encrypt; #endif /* CK_ENCRYPTION */ /* Declarations of variables global within this module */ #ifdef TTLEBUF /* See ckcnet.h */ int ttpush = -1; #define LEBUFSIZ 4096 static CHAR le_buf[LEBUFSIZ]; static int le_start = 0, le_end = 0, le_data = 0; #endif /* TTLEBUF */ #define MSGBUF_SIZE 1024 /* For debugging */ static char msgbuf[MSGBUF_SIZE]; static int gotsigs = 0; static time_t tcount = (time_t)0; /* Elapsed time counter */ static SIGTYP (*saval)() = NULL; /* For saving alarm() handler */ static SIGTYP (*savquit)() = NULL; /* and other signal handlers */ #ifdef SIGUSR1 static SIGTYP (*savusr1)() = NULL; #endif /* SIGUSR1 */ #ifdef SIGUSR2 static SIGTYP (*savusr2)() = NULL; #endif /* SIGUSR2 */ #ifdef SIGPIPE static SIGTYP (*savpipe)() = NULL; #endif /* SIGPIPE */ #ifdef SIGDANGER static SIGTYP (*savdanger)() = NULL; #endif /* SIGDANGER */ #ifndef NOJC static SIGTYP (*jchdlr)() = NULL; /* For checking suspend handler */ #endif /* NOJC */ static int jcshell = -1; /* And flag for result */ /* BREAKNULS is defined for systems that simulate sending a BREAK signal by sending a bunch of NUL characters at low speed. */ #ifdef PROVX1 #ifndef BREAKNULS #define BREAKNULS #endif /* BREAKNULS */ #endif /* PROVX1 */ #ifdef V7 #ifndef BREAKNULS #define BREAKNULS #endif /* BREAKNULS */ #endif /* V7 */ #ifdef BREAKNULS static char /* A string of nulls */ *brnuls = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; #endif /* BREAKNULS */ #ifdef CK_POSIX_SIG /* Longjump buffers */ static sigjmp_buf sjbuf; /* POSIX signal handling */ #else static jmp_buf sjbuf; #endif /* CK_POSIX_SIG */ #ifdef V7 static jmp_buf jjbuf; #endif /* V7 */ /* static */ /* (Not static any more) */ int ttyfd = -1; /* TTY file descriptor */ int ttpipe = 0; /* NETCMD: Use pipe instead of ttyfd */ int ttpty = 0; /* NETPTY: Use pty instead of ttfyd */ #ifdef NETPTY /* These are in ckupty.c */ extern PID_T pty_fork_pid; extern int pty_master_fd, pty_slave_fd; #endif /* NETPTY */ #ifdef NETCMD #ifdef NETCONN static int pipe0[2], pipe1[2]; /* Pipes for net i/o */ #endif /* NETCONN */ static PID_T ttpid = 0; /* Process ID for fork */ static int fdin, fdout; /* File descriptors for pipe */ static FILE * ttout = NULL; /* File pointer for output pipe */ #ifdef DCLFDOPEN /* fdopen() needs declaring because it's not declared in */ _PROTOTYP( FILE * fdopen, (int, char *) ); #endif /* DCLFDOPEN */ #endif /* NETCMD */ extern int pexitstat, quiet; #ifdef Plan9 int ttyctlfd = -1; /* TTY control channel - What? UNIX doesn't have one? */ int consctlfd = -1; /* Console control channel */ int noisefd = -1; /* tone channel */ static int ttylastspeed = -1; /* So we can lie about the speed */ #endif /* Plan9 */ int telnetfd = 0; /* File descriptor is for telnet */ #ifdef NETCONN int x25fd = 0; /* File descriptor is for X.25 */ #endif /* NETCONN */ char lockpid[16] = { '\0', '\0' }; /* PID stored in lockfile, as string */ static int lkf = 0, /* Line lock flag */ cgmf = 0, /* Flag that console modes saved */ xlocal = 0, /* Flag for tty local or remote */ curcarr = 0; /* Carrier mode: require/ignore. */ static int netconn = 0; /* 1 if network connection active */ static char escchr; /* Escape or attn character */ #ifdef CK_SCO32V4 #include #endif /* CK_SCO32V4 */ #ifdef HAVE_TV static struct timeval tv; /* For getting time, from sys/time.h */ #endif /* HAVE_TV */ #ifdef HAVE_TZ static struct timezone tz; #endif /* HAVE_TZ */ #ifdef OSF static struct timeb ftp; /* And from sys/timeb.h */ #endif /* OSF */ #ifdef BSD29 static long xclock; /* For getting time from sys/time.h */ static struct timeb ftp; /* And from sys/timeb.h */ #endif /* BSD29 */ #ifdef BSD41 static long xclock; /* For getting time from sys/time.h */ static struct timeb ftp; /* And from sys/timeb.h */ #endif /* BSD41 */ #ifdef BELLV10 static long xclock; /* For getting time from sys/time.h */ static struct timeb ftp; /* And from sys/timeb.h */ #endif /* BELLV10 */ #ifdef FT21 static long xclock; /* For getting time from sys/time.h */ static struct timeb ftp; /* And from sys/timeb.h */ #endif /* FT21 */ #ifdef TOWER1 static long xclock; /* For getting time from sys/time.h */ static struct timeb ftp; /* And from sys/timeb.h */ #endif /* TOWER1 */ #ifdef COHERENT static long xclock; /* For getting time from sys/time.h */ static struct timeb ftp; /* And from sys/timeb.h */ #endif /* COHERENT */ #ifdef V7 static long xclock; #endif /* V7 */ /* sgtty/termio information... */ #ifdef BSD44ORPOSIX /* POSIX or BSD44 */ static struct termios ttold, ttraw, tttvt, ttcur, ccold, ccraw, cccbrk; #else /* BSD, V7, etc */ #ifdef COHERENT /* Hack alert... */ #define ATTSV #endif /* COHERENT */ #ifdef ATTSV static struct termio ttold = {0}; /* Init'd for word alignment, */ static struct termio ttraw = {0}; /* which is important for some */ static struct termio tttvt = {0}; /* systems, like Zilog... */ static struct termio ttcur = {0}; static struct termio ccold = {0}; static struct termio ccraw = {0}; static struct termio cccbrk = {0}; #else static struct sgttyb /* sgtty info... */ ttold, ttraw, tttvt, ttcur, /* for communication line */ ccold, ccraw, cccbrk; /* and for console */ #ifdef BELLV10 static struct ttydevb /* Device info... */ tdold, tdcur; /* for communication device */ #endif /* BELLV10 */ #ifdef TIOCGETC static struct tchars tchold, tchnoi; static int tcharf; #endif /* TIOCGETC */ #ifdef TIOCGLTC static struct ltchars ltchold, ltchnoi; static int ltcharf; #endif /* TIOCGLTC */ int lmodef = 0; /* Local modes */ int lmode = 0; #endif /* ATTSV */ #endif /* BSD44ORPOSIX */ #ifdef COMMENT /* It picks up the speeds but they don't work */ #ifdef UNIXWARE /* For higher serial speeds */ #ifdef UW7 /* in Unixware 7.0 */ #include /* This picks up 57600 and 115200 */ #endif /* UW7 */ #endif /* UNIXWARE */ #endif /* COMMENT */ #ifdef PROVX1 static struct sgttyb ttbuf; #endif /* PROVX1 */ #ifdef ultrix /* do we really need this? */ static struct sgttyb vanilla; #endif /* ultrix */ #ifdef ATT7300 static int attmodem = 0; /* ATT7300 internal-modem status */ struct updata dialer = {0}; /* Condition dialer for data call */ #endif /* ATT7300 */ #ifndef NOUUCP #define FLFNAML 128 #ifndef USETTYLOCK #ifdef RTAIX char lkflfn[FLFNAML] = { '\0', '\0' }; /* and possible link to it */ #endif /* RTAIX */ char lock2[FLFNAML] = { '\0', '\0' }; /* Name of second lockfile */ #endif /* USETTYLOCK */ #else #define FLFNAML 7 #endif /* NOUUCP */ char flfnam[FLFNAML+1] = { '\0', '\0' }; /* UUCP lock file path name */ int haslock = 0; /* =1 if this kermit locked uucp */ #ifndef OXOS #ifdef SVORPOSIX static int conesc = 0; /* set to 1 if esc char (^\) typed */ #else #ifdef V7 static int conesc = 0; #else #ifdef C70 static int conesc = 0; #endif /* C70 */ #endif /* V7 */ #endif /* SVORPOSIX */ #endif /* OXOS */ /* Local copy of comm device name or network host */ static char ttnmsv[DEVNAMLEN+1] = { '\0', '\0' }; #ifdef USETTYLOCK static char lockname[DEVNAMLEN+1]; /* Ditto, the part after "/dev/". */ #endif /* USETTYLOCK */ #ifdef aegis static status_$t st; /* error status return value */ static short concrp = 0; /* true if console is CRP pad */ static uid_$t ttyuid; /* tty type uid */ static uid_$t conuid; /* stdout type uid */ /* APOLLO Aegis main() * establish acl usage and cleanup handling * this makes sure that CRP pads * get restored to a usable mode */ main(argc,argv) int argc; char **argv; { status_$t status; pfm_$cleanup_rec dirty; PID_T pid = getpid(); /* acl usage according to invoking environment */ default_acl(USE_DEFENV); /* establish a cleanup continuation */ status = pfm_$cleanup(dirty); if (status.all != pfm_$cleanup_set) { /* only handle faults for the original process */ if (pid == getpid() && status.all > pgm_$max_severity) { /* blew up in main process */ status_$t quo; pfm_$cleanup_rec clean; /* restore the console in any case */ conres(); /* attempt a clean exit */ debug(F101, "cleanup fault status", "", status.all); /* doexit(), then send status to continuation */ quo = pfm_$cleanup(clean); if (quo.all == pfm_$cleanup_set) doexit(pgm_$program_faulted,-1); else if (quo.all > pgm_$max_severity) pfm_$signal(quo); /* blew up in doexit() */ } /* send to the original continuation */ pfm_$signal(status); /*NOTREACHED*/ } return(ckcmai(argc, argv)); } #endif /* aegis */ /* ANSI-style prototypes for internal functions. */ /* Functions used outside this module are prototyped in ckcker.h. */ #ifdef apollo _PROTOTYP( SIGTYP timerh, () ); _PROTOTYP( SIGTYP cctrap, () ); _PROTOTYP( SIGTYP esctrp, () ); _PROTOTYP( SIGTYP sig_ign, () ); #else _PROTOTYP( SIGTYP timerh, (int) ); _PROTOTYP( SIGTYP cctrap, (int) ); _PROTOTYP( SIGTYP esctrp, (int) ); #endif /* apollo */ _PROTOTYP( int do_open, (char *) ); _PROTOTYP( static int in_chk, (int, int) ); _PROTOTYP( static int ttrpid, (char *) ); _PROTOTYP( static int ttchkpid, (char *) ); _PROTOTYP( static int ttlock, (char *) ); _PROTOTYP( static int ttunlck, (void) ); _PROTOTYP( static VOID sigchld_handler, (int) ); _PROTOTYP( int mygetbuf, (void) ); _PROTOTYP( int myfillbuf, (void) ); _PROTOTYP( VOID conbgt, (int) ); #ifdef ACUCNTRL _PROTOTYP( VOID acucntrl, (char *, char *) ); #endif /* ACUCNTRL */ #ifdef BSD44ORPOSIX _PROTOTYP( int carrctl, (struct termios *, int) ); #else #ifdef ATTSV _PROTOTYP( int carrctl, (struct termio *, int) ); #else _PROTOTYP( int carrctl, (struct sgttyb *, int) ); #endif /* ATTSV */ #endif /* BSD44ORPOSIX */ #ifdef ATT7300 _PROTOTYP( int attdial, (char *, long, char *) ); _PROTOTYP( int offgetty, (char *) ); _PROTOTYP( int ongetty, (char *) ); #endif /* ATT7300 */ #ifdef BEOSORBEBOX #ifdef SELECT /* BeOS is not capable of using SELECT on anything but sockets */ #undef SELECT #endif /* SELECT */ #include /* #ifdef BE_DR_7 */ static double time_started = 0.0; struct ALARM_STRUCT { thread_id thread; int time; }; static thread_id alarm_thread = -1; static struct ALARM_STRUCT alarm_struct; _PROTOTYP( long do_alarm, (void *) ); _PROTOTYP( unsigned int alarm, (unsigned int) ); _PROTOTYP( void alarm_expired, (void) ); /* #endif */ /* BE_DR_7 */ #endif /* BEOSORBEBOX */ #ifndef xunchar #define xunchar(ch) (((ch) - 32 ) & 0xFF ) /* Character to number */ #endif /* xunchar */ #ifdef CK_ANSIC static char * xxlast(char *s, char c) #else static char * xxlast(s,c) char *s; char c; #endif /* CK_ANSIC */ /* xxlast */ { /* Last occurrence of character c in string s. */ int i; for (i = (int)strlen(s); i > 0; i--) if (s[i-1] == c ) return(s + (i - 1)); return(NULL); } /* Timeout handler for communication line input functions */ /*ARGSUSED*/ SIGTYP timerh(foo) int foo; { ttimoff(); #ifdef BEOSORBEBOX /* #ifdef BE_DR_7 */ alarm_expired(); /* #endif */ /* BE_DR_7 */ #endif /* BEOSORBEBOX */ #ifdef CK_POSIX_SIG siglongjmp(sjbuf,1); #else longjmp(sjbuf,1); #endif /* CK_POSIX_SIG */ } /*ARGSUSED*/ SIGTYP xtimerh(foo) int foo; { /* Like timerh() but does */ #ifdef BEOSORBEBOX /* not reset the timer itself */ /* #ifdef BE_DR_7 */ alarm_expired(); /* #endif */ /* BE_DR_7 */ #endif /* BEOSORBEBOX */ #ifdef CK_POSIX_SIG siglongjmp(sjbuf,1); #else longjmp(sjbuf,1); #endif /* CK_POSIX_SIG */ } /* Control-C trap for communication line input functions */ int cc_int; /* Flag */ SIGTYP (* occt)(); /* For saving old SIGINT handler */ /*ARGSUSED*/ SIGTYP cctrap(foo) int foo; { /* Needs arg for ANSI C */ cc_int = 1; /* signal() prototype. */ return; } /* S Y S I N I T -- System-dependent program initialization. */ /* * ttgwsiz() returns: * 1 tt_rows and tt_cols are known, both altered, both > 0 * 0 tt_rows and/or tt_cols are known, both altered, one or both <= 0 * -1 tt_rows and tt_cols are unknown and unaltered */ extern int tt_rows, tt_cols; static int xttgwsiz() { char *p; int rows = 0, cols = 0; p = getenv("LINES"); debug(F110,"xttgwsiz LINES",p,0); if (p) { rows = atol(p); if (rows > 0) { p = getenv("COLUMNS"); debug(F110,"xttgwsiz COLUMNS",p,0); if (p) { cols = atol(p); if (cols > 0) { tt_rows = rows; tt_cols = cols; return(1); } return(0); } } } return(-1); } #ifdef TTLEBUF VOID le_init() { /* LocalEchoInit() */ int i; for (i = 0; i < LEBUFSIZ; i++) le_buf[i] = '\0'; le_start = 0; le_end = 0; le_data = 0; } VOID le_clean() { /* LocalEchoCleanup() */ le_init(); return; } int le_inbuf() { int rc = 0; if (le_start != le_end) { rc = (le_end - le_start + LEBUFSIZ) % LEBUFSIZ; } debug(F111,"le_inbuf","chars waiting",rc); return(rc); } int #ifdef CK_ANSIC le_putchar(CHAR ch) #else le_putchar(ch) CHAR ch; #endif /* CK_ANSIC */ /* le_putchar */ { #ifdef COMMENT /* In UNIX we do not have another thread taking chars out of the buffer */ while ((le_start - le_end == 1) || (le_start == 0 && le_end == LEBUFSIZ - 1)) { /* Buffer is full */ debug(F111,"le_putchar","Buffer is Full",ch); ReleaseLocalEchoMutex() ; msleep(250); RequestLocalEchoMutex( SEM_INDEFINITE_WAIT ) ; } #else if ((le_start - le_end + LEBUFSIZ)%LEBUFSIZ == 1) { debug(F110,"le_putchar","buffer is full",0); return(-1); } #endif /* COMMENT */ le_buf[le_end++] = ch; if (le_end == LEBUFSIZ) le_end = 0; le_data = 1; return(0); } int #ifdef CK_ANSIC le_puts(CHAR * s, int n) #else le_puts(s,n) CHAR * s; int n; #endif /* CK_ANSIC */ /* le_puts */ { int rc = 0; int i = 0; CHAR * p = (CHAR *)"le_puts"; ckhexdump(p,s,n); for (i = 0; i < n; i++) rc = le_putchar((char)s[i]); debug(F101,"le_puts","",rc); return(rc); } int #ifdef CK_ANSIC le_putstr(CHAR * s) #else le_putstr(s) CHAR * s; #endif /* CK_ANSIC */ /* le_puts */ { CHAR * p; int rc = 0; p = (CHAR *)"le_putstr"; ckhexdump(p,s,(int)strlen((char *)s)); for (p = s; *p && !rc; p++) rc = le_putchar(*p); return(rc); } int #ifdef CK_ANSIC le_getchar(CHAR * pch) #else /* CK_ANSIC */ le_getchar(pch) CHAR * pch; #endif /* CK_ANSIC */ /* le_gatchar */ { int rc = 0; if (le_start != le_end) { *pch = le_buf[le_start]; le_buf[le_start] = 0; le_start++; if (le_start == LEBUFSIZ) le_start = 0; if (le_start == le_end) { le_data = 0; } rc++; } else { *pch = 0; } return(rc); } #endif /* TTLEBUF */ #ifdef COMMENT /* Some systems like OSF/1 use TIOCGSIZE instead of TIOCGWINSZ. But as far as I know, whenever TIOCGSIZE is defined, it is equated to TIOCGWINSZ. For cases where this is not done, try this: */ #ifndef TIOCGWINSZ #ifdef TIOCGSIZE #define TIOCGWINSZ TIOCGSIZE #endif /* TIOCGSIZE */ #endif /* TIOCGWINSZ */ #endif /* COMMENT */ static int tt_xpixel = 0, tt_ypixel = 0; int ttgwsiz() { int x = 0; #ifndef NONAWS #ifdef QNX /* NOTE: TIOCGWSIZ works here too, but only in the 32-bit version. This code works for both the 16- and 32-bit versions. */ extern int dev_size(int, int, int, int *, int *); int r, c; if (dev_size(0, -1, -1, &r, &c) == 0) { debug(F101,"ttgwsiz QNX r","",r); debug(F101,"ttgwsiz QNX c","",c); tt_rows = r; tt_cols = c; return ((r > 0 && c > 0) ? 1 : 0); } else return(xttgwsiz()); #else /* QNX */ #ifdef TIOCGWINSZ /* Note, this was M_UNIX, changed to XENIX to allow cross compilation... */ #ifdef XENIX /* SCO UNIX 3.2v4.0 */ #include /* typedef mblk_t needed by ptem.h */ #include /* for ttgwsiz() */ #endif /* XENIX */ #ifdef I386IX /* Ditto for Interactive */ #include #include #endif /* I386IX */ /* Note, the above might be needed for some other older SVR3 Intel makes... */ struct winsize w; tt_xpixel = 0; tt_ypixel = 0; #ifdef IKSD if (inserver) return(xttgwsiz()); #endif /* IKSD */ x = ioctl(0, (int)TIOCGWINSZ, (char *)&w); debug(F101,"ttgwsiz TIOCGWINSZ","",x); if (x < 0) { return(xttgwsiz()); } else if (w.ws_row > 0 && w.ws_col > 0) { tt_rows = w.ws_row; tt_cols = w.ws_col; tt_xpixel = w.ws_xpixel; tt_ypixel = w.ws_ypixel; debug(F101,"ttgwsiz tt_rows","",tt_rows); debug(F101,"ttgwsiz tt_cols","",tt_cols); return(1); } else { debug(F100,"ttgwsiz TIOCGWINSZ 00","",0); return(xttgwsiz()); } #else return(xttgwsiz()); #endif /* TIOCGWINSZ */ #endif /* QNX */ #endif /* NONAWS */ } #ifdef RLOGCODE _PROTOTYP( int rlog_naws, (void) ); #endif /* RLOGCODE */ #ifndef NOSIGWINCH #ifdef SIGWINCH SIGTYP winchh(foo) int foo; { /* SIGWINCH handler */ int x = 0; #ifdef CK_TTYFD #ifndef VMS extern int ttyfd; #endif /* VMS */ #endif /* CK_TTYFD */ extern int tt_rows, tt_cols, cmd_rows, cmd_cols; #ifdef DEBUG if (deblog) { debug(F100,"***************","",0); debug(F100,"SIGWINCH caught","",0); debug(F100,"***************","",0); #ifdef NETPTY debug(F101,"SIGWINCH pty_fork_pid","",pty_fork_pid); #endif /* NETPTY */ } #endif /* DEUB */ signal(SIGWINCH,winchh); /* Re-arm the signal */ x = ttgwsiz(); /* Get new window size */ cmd_rows = tt_rows; /* Adjust command screen too */ cmd_cols = tt_cols; #ifdef CK_TTYFD if /* If we don't have a connection */ #ifdef VMS /* we're done. */ (vmsttyfd() == -1) #else (ttyfd == -1) #endif /* VMS */ #else (!local) #endif /* CK_TTYFD */ return; #ifdef NETPTY if (pty_fork_pid > -1) { /* "set host" to a PTY? */ int x; #ifdef TIOCSWINSZ struct winsize w; /* Resize the PTY */ errno = 0; w.ws_col = tt_cols; w.ws_row = tt_rows; w.ws_xpixel = tt_xpixel; w.ws_ypixel = tt_ypixel; x = ioctl(ttyfd,TIOCSWINSZ,&w); debug(F101,"winchh TIOCSWINSZ","",x); debug(F101,"winchh TIOCSWINSZ errno","",errno); #endif /* TIOCSWINSZ */ errno = 0; x = kill(pty_fork_pid,SIGWINCH); debug(F101,"winchh kill","",x); debug(F101,"winchh kill errno","",errno); } #endif /* NETPTY */ /* This should be OK. It might seem that sending this from interrupt level could interfere with another TELNET IAC string that was in the process of being sent. But we always send TELNET strings with a single write(), which should prevent mixups. blah_snaws() should protect themselves from being called on the wrong kind of connection. */ #ifdef TCPSOCKET #ifndef NOTTGWSIZ if (x > 0 && tt_rows > 0 && tt_cols > 0) { tn_snaws(); #ifdef RLOGCODE rlog_naws(); #endif /* RLOGCODE */ } #endif /* NOTTGWSIZ */ #endif /* TCPSOCKET */ SIGRETURN; } #endif /* SIGWINCH */ #endif /* NOSIGWINCH */ SIGTYP sighup(foo) int foo; { /* SIGHUP handler */ backgrd = 1; debug(F100,"***************","",0); debug(F100,"SIGHUP received","",0); debug(F100,"***************","",0); doexit(BAD_EXIT,-1); /*NOTREACHED*/ SIGRETURN; /* Shut picky compilers up... */ } #ifdef CK_SCO32V4 /* Exists but there is no prototype in the header files */ _PROTOTYP( char * ttyname, (int) ); #else #ifdef SV68R3V6 _PROTOTYP( char * ttyname, (int) ); #else #ifdef ultrix _PROTOTYP( char * ttyname, (int) ); #else #ifdef HPUX6 _PROTOTYP( char * ttyname, (int) ); #else #ifdef HPUX5 _PROTOTYP( char * ttyname, (int) ); #else #ifdef PS2AIX10 _PROTOTYP( char * ttyname, (int) ); #else #ifdef BSD42 _PROTOTYP( char * ttyname, (int) ); #endif /* BSD42 */ #endif /* PS2AIX10 */ #endif /* HPUX5 */ #endif /* HPUX6 */ #endif /* ultrix */ #endif /* SV68R3V6 */ #endif /* CK_SCO32V4 */ #ifndef SIGUSR1 /* User-defined signals */ #define SIGUSR1 30 #endif /* SIGUSR1 */ #ifndef SIGUSR2 #define SIGUSR2 31 #endif /* SIGUSR2 */ /* ignorsigs() sets certain signals to SIG_IGN. But when a signal is ignored, it remains ignored across exec(), so we have to restore these signals before exec(), which is the purpose of restorsigs(). */ static VOID ignorsigs() { /* Ignore these signals */ savquit = signal(SIGQUIT,SIG_IGN); /* Ignore Quit signal */ #ifdef SIGDANGER /* Ignore danger signals */ /* This signal is sent when the system is low on swap space. Processes that don't handle it are candidates for termination. If swap space doesn't clear out enough, we still might be terminated via kill() -- nothing we can do about that! Conceivably, this could be improved by installing a real signal handler that warns the user, but that would be pretty complicated, since we are not always in control of the screen -- e.g. during remote-mode file transfer. */ savdanger = signal(SIGDANGER,SIG_IGN); /* e.g. in AIX */ #endif /* SIGDANGER */ #ifdef SIGPIPE /* This one comes when a TCP/IP connection is broken by the remote. We prefer to catch this situation by examining error codes from write(). */ savpipe = signal(SIGPIPE,SIG_IGN); #endif /* SIGPIPE */ savusr1 = signal(SIGUSR1,SIG_IGN); /* Ignore user-defined signals */ savusr2 = signal(SIGUSR2,SIG_IGN); } VOID restorsigs() { /* Restore these signals */ (VOID) signal(SIGQUIT,savquit); /* (used in ckufio.c) */ #ifdef SIGDANGER (VOID) signal(SIGDANGER,savdanger); #endif /* SIGDANGER */ #ifdef SIGPIPE (VOID) signal(SIGPIPE,savpipe); #endif /* SIGPIPE */ (VOID) signal(SIGUSR1,savusr1); (VOID) signal(SIGUSR2,savusr2); } int sysinit() { int x; char * s; #ifdef CK_UTSNAME struct utsname name; #endif /* CK_UTSNAME */ extern char startupdir[]; /* BEFORE ANYTHING ELSE: Initialize the setuid package. Change to the user's real user and group ID. If this can't be done, don't run at all. */ x = priv_ini(); #ifdef SUIDDEBUG fprintf(stderr,"PRIV_INI=%d\n",x); #endif /* SUIDDEBUG */ if (x) { if (x & 1) fprintf(stderr,"Fatal: setuid failure.\n"); if (x & 2) fprintf(stderr,"Fatal: setgid failure.\n"); if (x & 4) fprintf(stderr,"Fatal: C-Kermit setuid to root!\n"); exit(1); } signal(SIGINT,SIG_IGN); /* Ignore interrupts at first */ signal(SIGFPE,SIG_IGN); /* Ignore floating-point exceptions */ signal(SIGHUP,sighup); /* Catch SIGHUP */ #ifndef NOSIGWINCH #ifdef SIGWINCH signal(SIGWINCH,winchh); /* Catch window-size change */ #endif /* SIGWINCH */ #endif /* NOSIGWINCH */ #ifdef SIGXFSZ signal(SIGXFSZ,SIG_IGN); /* Ignore writing past file limit */ #endif /* SIGXFSZ */ #ifndef NOJC /* Get the initial job control state. If it is SIG_IGN, that means the shell does not support job control, and so we'd better not suspend ourselves. */ #ifdef SIGTSTP jchdlr = signal(SIGTSTP,SIG_IGN); if (jchdlr == SIG_IGN) { jcshell = 0; debug(F100,"sysinit jchdlr: SIG_IGN","",0); } else if (jchdlr == SIG_DFL) { debug(F100,"sysinit jchdlr: SIG_DFL","",0); jcshell = 1; } else { debug(F100,"sysinit jchdlr: other","",0); jcshell = 3; } (VOID) signal(SIGTSTP,jchdlr); /* Put it back... */ #endif /* SIGTSTP */ #endif /* NOJC */ conbgt(0); /* See if we're in the background */ congm(); /* Get console modes */ (VOID) signal(SIGALRM,SIG_IGN); /* Ignore alarms */ ignorsigs(); /* Ignore some other signals */ #ifdef F_SETFL iniflags = fcntl(0,F_GETFL,0); /* Get stdin flags */ #endif /* F_SETFL */ #ifdef ultrix gtty(0,&vanilla); /* Get sgtty info */ #else #ifdef AUX set42sig(); /* Don't ask! (hakanson@cs.orst.edu) */ #endif /* AUX */ #endif /* ultrix */ /* Warning: on some UNIX systems (SVR4?), ttyname() reportedly opens /dev but never closes it. If it is called often enough, we run out of file descriptors and subsequent open()'s of other devices or files can fail. */ s = NULL; #ifndef MINIX if (isatty(0)) /* Name of controlling terminal */ s = ttyname(0); else if (isatty(1)) s = ttyname(1); else if (isatty(2)) s = ttyname(2); debug(F110,"sysinit ttyname(0)",s,0); #endif /* MINIX */ #ifdef BEOS if (!dftty) makestr(&dftty,s); #endif /* BEOS */ if (s) ckstrncpy((char *)cttnam,s,DEVNAMLEN+1); #ifdef SVORPOSIX if (!cttnam[0]) ctermid(cttnam); #endif /* SVORPOSIX */ if (!cttnam[0]) ckstrncpy((char *)cttnam,dftty,DEVNAMLEN+1); debug(F110,"sysinit CTTNAM",CTTNAM,0); debug(F110,"sysinit cttnam",cttnam,0); ttgwsiz(); /* Get window (screen) dimensions. */ #ifndef NOSYSCONF #ifdef _SC_OPEN_MAX ckmaxfiles = sysconf(_SC_OPEN_MAX); #endif /* _SC_OPEN_MAX */ #endif /* NOSYSCONF */ #ifdef Plan9 if (!backgrd) { consctlfd = open("/dev/consctl", O_WRONLY); /*noisefd = open("/dev/noise", O_WRONLY)*/ } ckxech = 1; #endif /* Plan9 */ #ifdef CK_UTSNAME if (uname(&name) > -1) { ckstrncpy(unm_mch,name.machine,CK_SYSNMLN); ckstrncpy(unm_nam,name.sysname,CK_SYSNMLN); ckstrncpy(unm_rel,name.release,CK_SYSNMLN); ckstrncpy(unm_ver,name.version,CK_SYSNMLN); #ifdef DEBUG if (deblog) { debug(F110,"sysinit uname machine",unm_mch,0); debug(F110,"sysinit uname sysname",unm_nam,0); debug(F110,"sysinit uname release",unm_rel,0); debug(F110,"sysinit uname version",unm_ver,0); } #endif /* DEBUG */ #ifdef HPUX9PLUS if (name.machine[5] == '8') hpis800 = 1; else hpis800 = 0; debug(F101,"sysinit hpis800","",hpis800); #endif /* HPUX9PLUS */ #ifdef TRU64 getsysinfo(GSI_PLATFORM_NAME, unm_mod, CK_SYSNMLN, 0, 0); debug(F110,"sysinit getsysinfo model",unm_mod,0); #endif /* TRU64 */ #ifdef SOLARIS25 sysinfo(SI_PLATFORM, unm_mod, CK_SYSNMLN); debug(F110,"sysinit sysinfo model",unm_mod,0); #endif /* SOLARIS25 */ } #endif /* CK_UTSNAME */ #ifdef CK_ENVIRONMENT { #ifdef TNCODE extern char tn_env_acct[], tn_env_disp[], tn_env_job[], tn_env_prnt[], tn_env_sys[]; #endif /* TNCODE */ extern char uidbuf[]; extern char * whoami(); char *p; #ifdef CKSENDUID uidbuf[0] = '\0'; #ifdef IKSD if (!inserver) { #endif /* IKSD */ p = getenv("USER"); debug(F110,"sysinit uidbuf from USER",uidbuf,0); if (!p) p = ""; if (!*p) { p = getenv("LOGNAME"); debug(F110,"sysinit uidbuf from LOGNAME",uidbuf,0); } if (!p) p = ""; if (!*p) { p = whoami(); debug(F110,"sysinit uidbuf from whoami()",uidbuf,0); } if (!p) p = ""; ckstrncpy(uidbuf, *p ? p : "UNKNOWN", UIDBUFLEN); #ifdef IKSD } #endif /* IKSD */ debug(F110,"sysinit final uidbuf",uidbuf,0); #endif /* CKSENDUID */ #ifdef TNCODE if ((p = getenv("JOB"))) ckstrncpy(tn_env_job,p,63); if ((p = getenv("ACCT"))) ckstrncpy(tn_env_acct,p,63); if ((p = getenv("PRINTER"))) ckstrncpy(tn_env_prnt,p,63); if ((p = getenv("DISPLAY"))) ckstrncpy(tn_env_disp,p,63); #ifdef aegis ckstrncpy(tn_env_sys,"Aegis",64); #else #ifdef Plan9 ckstrncpy(tn_env_sys,"Plan9",64); #else ckstrncpy(tn_env_sys,"UNIX",64); #endif /* Plan9 */ #endif /* aegis */ #endif /* TNCODE */ } #endif /* CK_ENVIRONMENT */ #ifdef CK_SNDLOC { extern char * tn_loc; char *p; if (p = getenv("LOCATION")) if (tn_loc = (char *)malloc((int)strlen(p)+1)) strcpy(tn_loc,p); /* safe */ } #endif /* CK_SNDLOC */ ckstrncpy(startupdir, zgtdir(), CKMAXPATH); startupdir[CKMAXPATH] = '\0'; x = strlen(startupdir); if (x <= 0) { startupdir[0] = '/'; startupdir[1] = '\0'; } else if (startupdir[x-1] != '/') { startupdir[x] = '/'; startupdir[x+1] = '\0'; } debug(F110,"sysinit startupdir",startupdir,0); #ifdef TTLEBUF le_init(); #endif /* TTLEBUF */ #ifdef BSD44ORPOSIX /* This should catch the ncurses platforms */ /* Some platforms don't have putenv(), like NeXTSTEP */ putenv("NCURSES_NO_SETBUF=1"); #endif /* BSD44ORPOSIX */ return(0); } /* S Y S C L E A N U P -- System-dependent program cleanup. */ int syscleanup() { #ifdef F_SETFL if (iniflags > -1) fcntl(0,F_SETFL,iniflags); /* Restore stdin flags */ #endif /* F_SETFL */ #ifdef ultrix stty(0,&vanilla); /* Get sgtty info */ #endif /* ultrix */ #ifdef NETCMD if (ttpid) kill(ttpid,9); #endif /* NETCMD */ return(0); } /* T T O P E N -- Open a tty for exclusive access. */ /* Call with: ttname: character string - device name or network host name. lcl: If called with lcl < 0, sets value of lcl as follows: 0: the terminal named by ttname is the job's controlling terminal. 1: the terminal named by ttname is not the job's controlling terminal. But watch out: if a line is already open, or if requested line can't be opened, then lcl remains (and is returned as) -1. modem: Less than zero: ttname is a network host name. Zero or greater: ttname is a terminal device name. Zero means a local connection (don't use modem signals). Positive means use modem signals. timo: 0 = no timer. nonzero = number of seconds to wait for open() to return before timing out. Returns: 0 on success -5 if device is in use -4 if access to device is denied -3 if access to lock directory denied -2 upon timeout waiting for device to open -1 on other error */ static int ttotmo = 0; /* Timeout flag */ /* Flag kept here to avoid being clobbered by longjmp. */ int ttopen(ttname,lcl,modem,timo) char *ttname; int *lcl, modem, timo; { #ifdef BSD44 #define ctermid(x) strcpy(x,"") #else #ifdef SVORPOSIX #ifndef CIE extern char *ctermid(); /* Wish they all had this! */ #else /* CIE Regulus */ #define ctermid(x) strcpy(x,"") #endif /* CIE */ #endif /* SVORPOSIX */ #endif /* BSD44 */ #ifdef ultrix int temp = 0; #endif /* ultrix */ #ifndef OPENFIRST char fullname[DEVNAMLEN+1]; #endif /* OPENFIRST */ char * fnam; /* Full name after expansion */ int y; #ifndef pdp11 #define NAMEFD /* Feature to allow name to be an open file descriptor */ #endif /* pdp11 */ #ifdef NAMEFD char *p; debug(F101,"ttopen telnetfd","",telnetfd); #endif /* NAMEFD */ debug(F110,"ttopen ttname",ttname,0); debug(F110,"ttopen ttnmsv",ttnmsv,0); debug(F101,"ttopen modem","",modem); debug(F101,"ttopen netconn","",netconn); debug(F101,"ttopen ttyfd","",ttyfd); debug(F101,"ttopen *lcl","",*lcl); debug(F101,"ttopen ttmdm","",ttmdm); debug(F101,"ttopen ttnet","",ttnet); ttpmsk = 0xff; lockpid[0] = '\0'; if (ttyfd > -1) { /* If device already opened */ if (!strncmp(ttname,ttnmsv,DEVNAMLEN)) /* are new & old names equal? */ return(0); /* Yes, nothing to do - just return */ ttnmsv[0] = '\0'; /* No, clear out old name */ ttclos(ttyfd); /* close old connection. */ } wasclosed = 0; /* New connection, not closed yet. */ ttpipe = 0; /* Assume it's not a pipe */ ttpty = 0; /* or a pty... */ #ifdef NETCONN /* This is a bit tricky... Suppose that previously Kermit had dialed a telnet modem server ("set host xxx:2001, set modem type usr, dial ..."). Then the connection was closed (ttyfd = -1), and then a REDIAL command was given. At this point we've obliterated the negative modem type hack, and so would treat the IP hostname as a device name, and would then fail because of "No such device or directory". But the previous connection has left behind some clues, so let's use them... */ if (ttyfd < 0) { /* Connection is not open */ if (!strcmp(ttname,ttnmsv)) { /* Old and new names the same? */ if (((netconn > 0) && (ttmdm < 0)) || ((ttnet > 0) && (!ckstrchr(ttname,'/')) && (ckstrchr(ttname,':'))) ) { int x, rc; x = (ttmdm < 0) ? -ttmdm : ttnet; rc = netopen(ttname, lcl, x); debug(F111,"ttopen REOPEN netopen",ttname,rc); if (rc > -1) { netconn = 1; xlocal = *lcl = 1; } else { netconn = 0; } gotsigs = 0; return(rc); } } } #endif /* NETCONN */ #ifdef MAXNAMLEN debug(F100,"ttopen MAXNAMLEN defined","",0); #else debug(F100,"ttopen MAXNAMLEN *NOT* defined","",0); #endif #ifdef BSD4 debug(F100,"ttopen BSD4 defined","",0); #else debug(F100,"ttopen BSD4 *NOT* defined","",0); #endif /* BSD4 */ #ifdef BSD42 debug(F100,"ttopen BSD42 defined","",0); #else debug(F100,"ttopen BSD42 *NOT* defined","",0); #endif /* BSD42 */ #ifdef MYREAD debug(F100,"ttopen MYREAD defined","",0); #else debug(F100,"ttopen MYREAD *NOT* defined","",0); #endif /* MYREAD */ #ifdef NETCONN if (modem < 0) { /* modem < 0 = code for network */ int x; ttmdm = modem; modem = -modem; /* Positive network type number */ fdflag = 0; /* Stdio not redirected. */ netconn = 1; /* And it's a network connection */ debug(F111,"ttopen net",ttname,modem); #ifdef NAMEFD for (p = ttname; isdigit(*p); p++) ; /* Check for all digits */ if (*p == '\0' && (telnetfd || x25fd)) { /* Avoid X.121 addresses */ ttyfd = atoi(ttname); /* Is there a way to test it's open? */ ttfdflg = 1; /* We got an open file descriptor */ debug(F111,"ttopen net ttfdflg",ttname,ttfdflg); debug(F101,"ttopen net ttyfd","",ttyfd); ckstrncpy(ttnmsv,ttname,DEVNAMLEN); /* Remember the "name". */ x = 1; /* Return code is "good". */ if (telnetfd) { ttnet = NET_TCPB; if (ttnproto != NP_TCPRAW) ttnproto = NP_TELNET; #ifdef SUNX25 } else if (x25fd) { ttnet = NET_SX25; ttnproto = NP_NONE; #endif /* SUNX25 */ } } else { /* Host name or address given */ #ifdef NETPTY if (modem == NET_PTY) { int x; if (nopush) { debug(F100,"ttopen PTY: nopush","",0); return(-1); } ttnet = NET_PTY; ttnproto = NP_NONE; netconn = 1; /* but we don't use network i/o */ ttpty = 1; debug(F110,"ttopen PTY",ttname,0); x = do_pty(&ttyfd,ttname,0); if (x > -1) { ckstrncpy(ttnmsv,ttname,DEVNAMLEN); xlocal = *lcl = 1; /* It's local */ } else { ttpty = 0; netconn = 0; } gotsigs = 0; return(x); } #endif /* NETPTY */ #ifdef NETCMD /* dup2() is not available on older System V platforms like AT&T 3Bx. For those systems we punt by not defining NETCMD, but we might be able to do better -- see workarounds for this problem in ckufio.c (search for dup2). */ if (modem == NET_CMD) { if (nopush) { debug(F100,"ttopen pipe: nopush","",0); return(-1); } if (pipe(pipe0) || pipe(pipe1)) { perror("Pipe error"); return(-1); } ttpid = fork(); /* Make a fork */ switch (ttpid) { case -1: /* Error making fork */ close(pipe0[0]); close(pipe0[1]); close(pipe1[0]); close(pipe1[1]); perror("Fork error"); return(-1); case 0: /* Child. */ close(pipe0[0]); close(pipe1[1]); dup2(pipe0[1], 1); close(pipe0[1]); dup2(pipe1[0], 0); close(pipe1[0]); system(ttname); _exit(0); default: /* Parent */ close(pipe0[1]); close(pipe1[0]); fdin = pipe0[0]; /* Read from pipe */ fdout = pipe1[1]; /* Write to pipe */ ttout = fdopen(fdout,"w"); /* Get stream so we can */ if (!ttout) { /* make it unbuffered. */ perror("fdopen failure"); return(-1); } setbuf(ttout,NULL); ckstrncpy(ttnmsv,ttname,DEVNAMLEN); xlocal = *lcl = 1; /* It's local */ netconn = 1; /* Call it a network connection */ ttmdm = modem; /* Remember network type */ ttyfd = fdin; ttpipe = 1; gotsigs = 0; return(0); } } #endif /* NETCMD */ #endif /* NAMEFD */ x = netopen(ttname, lcl, modem); /* (see ckcnet.h) */ if (x > -1) { ckstrncpy(ttnmsv,ttname,DEVNAMLEN); } else netconn = 0; #ifdef NAMEFD } #endif /* NAMEFD */ #ifdef sony_news /* Sony NEWS */ if (ioctl(ttyfd,TIOCKGET,&km_ext) < 0) { /* Get Kanji mode */ perror("ttopen error getting Kanji mode (network)"); debug(F111,"ttopen error getting Kanji mode","network",0); km_ext = -1; /* Make sure this stays undefined. */ } #endif /* sony_news */ xlocal = *lcl = 1; /* Network connections are local. */ debug(F101,"ttopen net x","",x); #ifdef COMMENT /* Let netopen() do this */ if (x > -1 && !x25fd) x = tn_ini(); /* Initialize TELNET protocol */ #endif /* COMMENT */ gotsigs = 0; return(x); } else { /* Terminal device */ #endif /* NETCONN */ #ifdef NAMEFD /* This code lets you give Kermit an open file descriptor for a serial communication device, rather than a device name. Kermit assumes that the line is already open, locked, conditioned with the right parameters, etc. */ for (p = ttname; isdigit(*p); p++) ; /* Check for all-digits */ if (*p == '\0') { ttyfd = atoi(ttname); /* Is there a way to test it's open? */ debug(F111,"ttopen got open fd",ttname,ttyfd); ckstrncpy(ttnmsv,ttname,DEVNAMLEN); /* Remember the "name". */ if (ttyfd >= 0 && ttyfd < 3) /* If it's stdio... */ xlocal = *lcl = 0; /* we're in remote mode */ else /* otherwise */ xlocal = *lcl = 1; /* local mode. */ netconn = 0; /* Assume it's not a network. */ tvtflg = 0; /* Might need to initialize modes. */ ttmdm = modem; /* Remember modem type. */ fdflag = 0; /* Stdio not redirected. */ ttfdflg = 1; /* Flag we were opened this way. */ debug(F111,"ttopen non-net ttfdflg",ttname,ttfdflg); debug(F101,"ttopen non-net ttyfd","",ttyfd); #ifdef sony_news /* Sony NEWS */ /* Get device Kanji mode */ if (ioctl(ttyfd,TIOCKGET,&km_ext) < 0) { perror("ttopen error getting Kanji mode"); debug(F101,"ttopen error getting Kanji mode","",0); km_ext = -1; /* Make sure this stays undefined. */ } #endif /* sony_news */ gotsigs = 0; return(0); /* Return success */ } #endif /* NAMEFD */ #ifdef NETCONN } #endif /* NETCONN */ /* Here we have to open a serial device of the given name. */ netconn = 0; /* So it's not a network connection */ occt = signal(SIGINT, cctrap); /* Set Control-C trap, save old one */ sigint_ign = 0; tvtflg = 0; /* Flag for use by ttvt(). */ /* 0 = ttvt not called yet for this device */ fdflag = (!isatty(0) || !isatty(1)); /* Flag for stdio redirected */ debug(F101,"ttopen fdflag","",fdflag); ttmdm = modem; /* Make this available to other fns */ xlocal = *lcl; /* Make this available to other fns */ /* Code for handling bidirectional tty lines goes here. */ /* Use specified method for turning off logins and suppressing getty. */ #ifdef ACUCNTRL /* Should put call to priv_on() here, but that would be very risky! */ acucntrl("disable",ttname); /* acucntrl() program. */ /* and priv_off() here... */ #else #ifdef ATT7300 if ((attmodem & DOGETY) == 0) /* offgetty() program. */ attmodem |= offgetty(ttname); /* Remember response. */ #endif /* ATT7300 */ #endif /* ACUCNTRL */ #ifdef OPENFIRST /* 1985-2001: opens device first then gets lock; reason: Kermit usually has to run setuid or setgid in order to create a lockfile. If you give a SET LINE command for a device that happens to be your job's controlling terminal, Kermit doesn't have to create a lockfile, and in fact should not create one, and would fail if it tried to if it did not have the required privileges. But you can't find out if two tty device names are equivalent until you have a file descriptor that you can give to ttyname(). But this can cause a race condition between Kermit and [m]getty. So see the [#]else part... */ /* In the following section, we open the tty device for read/write. If a modem has been specified via "set modem" prior to "set line" then the O_NDELAY parameter is used in the open, provided this symbol is defined (e.g. in fcntl.h), so that the program does not hang waiting for carrier (which in most cases won't be present because a connection has not been dialed yet). O_NDELAY is removed later on in ttopen(). It would make more sense to first determine if the line is local before doing this, but because ttyname() requires a file descriptor, we have to open it first. See do_open(). Now open the device using the desired treatment of carrier. If carrier is REQUIRED, then open could hang forever, so an optional timer is provided. If carrier is not required, the timer should never go off, and should do no harm... */ ttotmo = 0; /* Flag no timeout */ debug(F101,"ttopen timo","",timo); debug(F101,"ttopen xlocal","",xlocal); if (timo > 0) { int xx; saval = signal(SIGALRM,timerh); /* Timed, set up timer. */ xx = alarm(timo); /* Timed open() */ debug(F101,"ttopen alarm","",xx); if ( #ifdef CK_POSIX_SIG sigsetjmp(sjbuf,1) #else setjmp(sjbuf) #endif /* CK_POSIX_SIG */ ) { ttotmo = 1; /* Flag timeout. */ } else ttyfd = do_open(ttname); ttimoff(); debug(F111,"ttopen","modem",modem); debug(F101,"ttopen ttyfd","",ttyfd); debug(F101,"ttopen alarm return","",ttotmo); } else { errno = 0; ttyfd = do_open(ttname); } debug(F111,"ttopen ttyfd",ttname,ttyfd); if (ttyfd < 0) { /* If couldn't open, fail. */ debug(F101,"ttopen errno","",errno); if (errno > 0 && !quiet) perror(ttname); /* Print message */ #ifdef ATT7300 if (attmodem & DOGETY) /* was getty(1m) running before us? */ ongetty(ttnmsv); /* yes, restart on tty line */ attmodem &= ~DOGETY; /* no phone in use, getty restored */ #else #ifdef ACUCNTRL /* Should put call to priv_on() here, but that would be risky! */ acucntrl("enable",ttname); /* acucntrl() program. */ /* and priv_off() here... */ #endif /* ACUNTRL */ #endif /* ATT7300 */ signal(SIGINT,occt); /* Put old Ctrl-C trap back. */ if (errno == EACCES) { /* Device is protected against user */ debug(F110,"ttopen EACCESS",ttname,0); /* Return -4 */ return(-4); } else return(ttotmo ? -2 : -1); /* Otherwise -2 if timeout, or -1 */ } #ifdef QNX { extern int qnxportlock; x = qnxopencount(); debug(F101,"ttopen qnxopencount","",x); debug(F101,"ttopen qnxportlock","",qnxportlock); if (x < 0 && qnxportlock) { ttclos(0); printf("?Can't get port open count\n"); printf("(Try again with SET QNX-PORT-LOCK OFF)\n"); return(-1); /* Indicate device is in use */ } if (x > 1) { /* 1 == me */ if (qnxportlock) ttclos(0); return(-2); /* Indicate device is in use */ else if (!quiet) printf("WARNING: \"%s\" looks busy...\n",ttdev); } } #endif /* QNX */ #ifdef Plan9 /* take this opportunity to open the control channel */ if (p9openttyctl(ttname) < 0) #else /* Make sure it's a real tty. */ if (!ttfdflg && !isatty(ttyfd) && strcmp(ttname,"/dev/null")) #endif /* Plan9 */ { fprintf(stderr,"%s is not a terminal device\n",ttname); debug(F111,"ttopen not a tty",ttname,errno); close(ttyfd); ttyfd = -1; wasclosed = 1; signal(SIGINT,occt); return(-1); } #ifdef aegis /* Apollo C runtime claims that console pads are tty devices, which * is reasonable, but they aren't any good for packet transfer. */ ios_$inq_type_uid((short)ttyfd, ttyuid, st); if (st.all != status_$ok) { fprintf(stderr, "problem getting tty object type: "); error_$print(st); } else if (ttyuid != sio_$uid) { /* reject non-SIO lines */ close(ttyfd); ttyfd = -1; wasclosed = 1; errno = ENOTTY; perror(ttname); signal(SIGINT,occt); return(-1); } #endif /* aegis */ sigint_ign = (occt == SIG_IGN) ? 1 : 0; ckstrncpy(ttnmsv,ttname,DEVNAMLEN); /* Keep copy of name locally. */ /* Caller wants us to figure out if line is controlling tty */ if (*lcl < 0) { if (strcmp(ttname,CTTNAM) == 0) { /* "/dev/tty" always remote */ xlocal = 0; debug(F111,"ttopen ttname=CTTNAM",ttname,xlocal); } else if (strcmp(ttname,cttnam) == 0) { xlocal = 0; debug(F111,"ttopen ttname=cttnam",ttname,xlocal); } else if (cttnam[0]) { #ifdef BEBOX_DR7 x = ttnmsv; /* ttyname() is broken */ #else x = ttyname(ttyfd); /* Get real name of ttname. */ #endif /* BEBOX_DR7 */ if (!x) x = ""; if (*x) xlocal = ((strncmp(x,cttnam,DEVNAMLEN) == 0) ? 0 : 1); else xlocal = 1; debug(F111,"ttopen ttyname(ttyfd) xlocal",x,xlocal); } } #ifndef NOFDZERO /* Note, the following code was added so that Unix "idle-line" snoopers */ /* would not think Kermit was idle when it was transferring files, and */ /* maybe log people out. */ if (xlocal == 0) { /* Remote mode */ if (fdflag == 0) { /* Standard i/o is not redirected */ debug(F100,"ttopen setting ttyfd = 0","",0); #ifdef LYNXOS /* On Lynx OS, fd 0 is open for read only. */ dup2(ttyfd,0); #endif /* LYNXOS */ close(ttyfd); /* Use file descriptor 0 */ ttyfd = 0; } else { /* Standard i/o is redirected */ debug(F101,"ttopen stdio redirected","",ttyfd); } } #endif /* NOFDZERO */ /* Now check if line is locked -- if so fail, else lock for ourselves */ /* Note: After having done this, don't forget to delete the lock if you */ /* leave ttopen() with an error condition. */ lkf = 0; /* Check lock */ if (xlocal > 0) { int xx; int xpid; if ((xx = ttlock(ttname)) < 0) { /* Can't lock it. */ debug(F111,"ttopen ttlock fails",ttname,xx); /* WARNING - This close() can hang if tty is an empty socket... */ close(ttyfd); /* Close the device. */ ttyfd = -1; /* Erase its file descriptor. */ wasclosed = 1; signal(SIGINT,occt); /* Put old SIGINT back. */ sigint_ign = (occt == SIG_IGN) ? 1 : 0; if (xx == -2) { /* If lockfile says device in use, */ #ifndef NOUUCP debug(F111,"ttopen reading lockfile pid",flfnam,xx); xpid = ttrpid(flfnam); /* Try to read pid from lockfile */ if (xpid > -1) { /* If we got a pid */ if (!quiet) printf("Locked by process %d\n",xpid); /* tell them. */ sprintf(lockpid,"%d",xpid); /* Record it too */ debug(F110,"ttopen lockpid",lockpid,0); } else if (*flfnam) { extern char *DIRCMD; char *p = NULL; int x; x = (int)strlen(flfnam) + (int)strlen(DIRCMD) + 2; p = malloc(x); /* Print a directory listing. */ /* Note: priv_on() won't help here, because we do not pass privs along to to inferior processes, in this case ls. So if the real user does not have directory-listing access to the lockfile directory, this will result in something like "not found". That's why we try this only as a last resort. */ if (p) { /* If we got the space... */ ckmakmsg(p,x,DIRCMD," ",flfnam,NULL); zsyscmd(p); /* Get listing. */ if (p) { /* free the space */ free(p); p = NULL; } } } #endif /* NOUUCP */ return(-5); /* Code for device in use */ } else return(-3); /* Access denied */ } else lkf = 1; } #else /* OPENFIRST */ /* 27 Oct 2001: New simpler code that gets the lock first and then opens the device, which eliminates the race condition. The downside is you can no longer say "set line /dev/ttyp0" or whatever, where /dev/ttyp0 is your login terminal, without trying to create a lockfile, which fails if C-Kermit lacks privs, and if it succeeds, it has created a lockfile where it didn't create one before. */ xlocal = *lcl; /* Is the device my login terminal? */ debug(F111,"ttopen xlocal","A",xlocal); fnam = ttname; if (strcmp(ttname,CTTNAM) && netconn == 0) { if (zfnqfp(ttname,DEVNAMLEN+1,fullname)) { if ((int)strlen(fullname) > 0) fnam = fullname; } } debug(F110,"ttopen fnam",fnam,0); if (xlocal < 0) { xlocal = (strcmp(fnam,CTTNAM) != 0); } debug(F111,"ttopen xlocal","B",xlocal); lkf = 0; /* No lock yet */ if (xlocal > 0) { /* If not... */ int xx; int xpid; xx = ttlock(fnam); /* Try to lock it. */ debug(F101,"ttopen ttlock","",xx); if (xx < 0) { /* Can't lock it. */ debug(F111,"ttopen ttlock fails",fnam,xx); if (xx == -2) { /* If lockfile says device in use, */ #ifndef NOUUCP debug(F111,"ttopen reading lockfile pid",flfnam,xx); xpid = ttrpid(flfnam); /* Try to read pid from lockfile */ if (xpid > -1) { /* If we got a pid */ if (!quiet) printf("Locked by process %d\n",xpid); /* tell them. */ ckstrncpy(lockpid,ckitoa(xpid),16); debug(F110,"ttopen lockpid",lockpid,0); #ifndef NOPUSH } else if (flfnam[0] && !nopush) { extern char *DIRCMD; char *p = NULL; int x; x = (int)strlen(flfnam) + (int)strlen(DIRCMD) + 2; p = malloc(x); /* Print a directory listing. */ /* Note: priv_on() won't help here, because we do not pass privs along to to inferior processes, in this case ls. So if the real user does not have directory-listing access to the lockfile directory, this will result in something like "not found". That's why we try this only as a last resort. */ if (p) { /* If we got the space... */ ckmakmsg(p,x,DIRCMD," ",flfnam,NULL); zsyscmd(p); /* Get listing. */ if (p) { /* free the space */ free(p); p = NULL; } } #endif /* NOPUSH */ } #endif /* NOUUCP */ return(-5); /* Code for device in use */ } else return(-3); /* Access denied */ } else lkf = 1; } /* Have lock -- now it's safe to open the device */ debug(F101,"ttopen lkf","",lkf); debug(F101,"ttopen timo","",timo); ttotmo = 0; /* Flag no timeout */ if (timo > 0) { int xx; saval = signal(SIGALRM,timerh); /* Timed, set up timer. */ xx = alarm(timo); /* Timed open() */ debug(F101,"ttopen alarm","",xx); if ( #ifdef CK_POSIX_SIG sigsetjmp(sjbuf,1) #else setjmp(sjbuf) #endif /* CK_POSIX_SIG */ ) { ttotmo = 1; /* Flag timeout. */ } else { ttyfd = do_open(fnam); } ttimoff(); debug(F111,"ttopen timed ttyfd",fnam,ttyfd); } else { errno = 0; ttyfd = do_open(fnam); debug(F111,"ttopen untimed ttyfd",fnam,ttyfd); } if (ttyfd < 0) { /* If couldn't open, fail. */ debug(F111,"ttopen errno",fnam,errno); debug(F111,"ttopen xlocal","C",xlocal); if (xlocal == 0) { debug(F100,"ttopen substituting 0","",0); ttyfd = 0; } else { if (errno > 0 && !quiet) { debug(F111,"ttopen perror",fnam,errno); perror(fnam); /* Print message */ } if (ttunlck()) /* Release the lock file */ fprintf(stderr,"Warning, problem releasing lock\r\n"); } } if (ttyfd < 0) { /* ttyfd is still < 0? */ #ifdef ATT7300 if (attmodem & DOGETY) /* was getty(1m) running before us? */ ongetty(ttnmsv); /* yes, restart on tty line */ attmodem &= ~DOGETY; /* no phone in use, getty restored */ #else #ifdef ACUCNTRL /* Should put call to priv_on() here, but that would be risky! */ acucntrl("enable",fnam); /* acucntrl() program. */ /* and priv_off() here... */ #endif /* ACUNTRL */ #endif /* ATT7300 */ signal(SIGINT,occt); /* Put old Ctrl-C trap back. */ if (errno == EACCES) { /* Device is protected against user */ debug(F110,"ttopen EACCESS",fnam,0); /* Return -4 */ return(-4); } else return(ttotmo ? -2 : -1); /* Otherwise -2 if timeout, or -1 */ } /* Make sure it's a real tty. */ #ifdef Plan9 /* take this opportunity to open the control channel */ if (p9openttyctl(fnam) < 0) #else if (!ttfdflg && !isatty(ttyfd) && strcmp(fnam,"/dev/null")) #endif /* Plan9 */ { fprintf(stderr,"%s is not a terminal device\n",fnam); debug(F111,"ttopen not a tty",fnam,errno); if (ttunlck()) /* Release the lock file */ fprintf(stderr,"Warning, problem releasing lock\r\n"); close(ttyfd); ttyfd = -1; wasclosed = 1; signal(SIGINT,occt); return(-1); } #ifdef aegis /* Apollo C runtime claims that console pads are tty devices, which is reasonable, but they aren't any good for packet transfer. */ ios_$inq_type_uid((short)ttyfd, ttyuid, st); if (st.all != status_$ok) { fprintf(stderr, "problem getting tty object type: "); error_$print(st); } else if (ttyuid != sio_$uid) { /* Reject non-SIO lines */ close(ttyfd); ttyfd = -1; wasclosed = 1; errno = ENOTTY; perror(fnam); signal(SIGINT,occt); return(-1); } #endif /* aegis */ sigint_ign = (occt == SIG_IGN) ? 1 : 0; ckstrncpy(ttnmsv,ttname,DEVNAMLEN); /* Keep copy of name locally. */ /* Caller wants us to figure out if line is controlling tty */ if (*lcl < 0) { char * s; if (strcmp(fnam,CTTNAM) == 0) { /* "/dev/tty" always remote */ xlocal = 0; debug(F111,"ttopen fnam=CTTNAM",fnam,xlocal); } else if (strcmp(fnam,cttnam) == 0) { xlocal = 0; debug(F111,"ttopen fnam=cttnam",fnam,xlocal); } else if (cttnam[0]) { #ifdef BEBOX_DR7 s = ttnmsv; /* ttyname() is broken */ #else s = ttyname(ttyfd); /* Get real name of ttname. */ #endif /* BEBOX_DR7 */ if (!s) s = ""; if (*s) xlocal = ((strncmp(s,cttnam,DEVNAMLEN) == 0) ? 0 : 1); else xlocal = 1; debug(F111,"ttopen ttyname(ttyfd) xlocal",s,xlocal); } } #ifndef NOFDZERO /* Note, the following code was added so that Unix "idle-line" snoopers */ /* would not think Kermit was idle when it was transferring files, and */ /* maybe log people out. */ if (xlocal == 0) { /* Remote mode */ if (fdflag == 0) { /* Standard i/o is not redirected */ debug(F100,"ttopen setting ttyfd = 0","",0); #ifdef LYNXOS /* On Lynx OS, fd 0 is open for read only. */ dup2(ttyfd,0); #endif /* LYNXOS */ close(ttyfd); /* Use file descriptor 0 */ ttyfd = 0; } else { /* Standard i/o is redirected */ debug(F101,"ttopen stdio redirected","",ttyfd); } } #endif /* NOFDZERO */ #endif /* OPENFIRST */ /* Got the line, now set the desired value for local. */ if (*lcl != 0) *lcl = xlocal; /* Some special stuff for v7... */ #ifdef V7 #ifndef MINIX if (kmem[TTY] < 0) { /* If open, then skip this. */ qaddr[TTY] = initrawq(ttyfd); /* Init the queue. */ if ((kmem[TTY] = open("/dev/kmem", 0)) < 0) { fprintf(stderr, "Can't read /dev/kmem in ttopen.\n"); perror("/dev/kmem"); exit(1); } } #endif /* !MINIX */ #endif /* V7 */ /* No failure returns after this point */ #ifdef ultrix ioctl(ttyfd, TIOCMODEM, &temp); #ifdef TIOCSINUSE if (xlocal && ioctl(ttyfd, TIOCSINUSE, NULL) < 0) { if (!quiet) perror(fnam); } #endif /* TIOCSINUSE */ #endif /* ultrix */ /* Get tty device settings */ #ifdef BSD44ORPOSIX /* POSIX */ tcgetattr(ttyfd,&ttold); debug(F101,"ttopen tcgetattr ttold.c_lflag","",ttold.c_lflag); tcgetattr(ttyfd,&ttraw); debug(F101,"ttopen tcgetattr ttraw.c_lflag","",ttraw.c_lflag); tcgetattr(ttyfd,&tttvt); debug(F101,"ttopen tcgetattr tttvt.c_lflag","",tttvt.c_lflag); #else /* BSD, V7, and all others */ #ifdef ATTSV /* AT&T UNIX */ ioctl(ttyfd,TCGETA,&ttold); debug(F101,"ttopen ioctl TCGETA ttold.c_lflag","",ttold.c_lflag); ioctl(ttyfd,TCGETA,&ttraw); ioctl(ttyfd,TCGETA,&tttvt); #else #ifdef BELLV10 ioctl(ttyfd,TIOCGETP,&ttold); debug(F101,"ttopen BELLV10 ttold.sg_flags","",ttold.sg_flags); ioctl(ttyfd,TIOCGDEV,&tdold); debug(F101,"ttopen BELLV10 tdold.flags","",tdold.flags); #else gtty(ttyfd,&ttold); debug(F101,"ttopen gtty ttold.sg_flags","",ttold.sg_flags); #endif /* BELLV10 */ #ifdef sony_news /* Sony NEWS */ if (ioctl(ttyfd,TIOCKGET,&km_ext) < 0) { /* Get console Kanji mode */ perror("ttopen error getting Kanji mode"); debug(F101,"ttopen error getting Kanji mode","",0); km_ext = -1; /* Make sure this stays undefined. */ } #endif /* sony_news */ #ifdef TIOCGETC debug(F100,"ttopen TIOCGETC","",0); tcharf = 0; /* In remote mode, also get */ if (xlocal == 0) { /* special characters */ if (ioctl(ttyfd,TIOCGETC,&tchold) < 0) { debug(F100,"ttopen TIOCGETC failed","",0); } else { tcharf = 1; /* It worked. */ ioctl(ttyfd,TIOCGETC,&tchnoi); /* Get another copy */ debug(F100,"ttopen TIOCGETC ok","",0); } } #else debug(F100,"ttopen TIOCGETC not defined","",0); #endif /* TIOCGETC */ #ifdef TIOCGLTC debug(F100,"ttopen TIOCGLTC","",0); ltcharf = 0; /* In remote mode, also get */ if (xlocal == 0) { /* local special characters */ if (ioctl(ttyfd,TIOCGLTC,<chold) < 0) { debug(F100,"ttopen TIOCGLTC failed","",0); } else { ltcharf = 1; /* It worked. */ ioctl(ttyfd,TIOCGLTC,<chnoi); /* Get another copy */ debug(F100,"ttopen TIOCGLTC ok","",0); } } #else debug(F100,"ttopen TIOCGLTC not defined","",0); #endif /* TIOCGLTC */ #ifdef TIOCLGET debug(F100,"ttopen TIOCLGET","",0); lmodef = 0; if (ioctl(ttyfd,TIOCLGET,&lmode) < 0) { debug(F100,"ttopen TIOCLGET failed","",0); } else { lmodef = 1; debug(F100,"ttopen TIOCLGET ok","",0); } #endif /* TIOCLGET */ #ifdef BELLV10 ioctl(ttyfd,TIOCGETP,&ttraw); ioctl(ttyfd,TIOCGETP,&tttvt); #else gtty(ttyfd,&ttraw); /* And a copy of it for packets*/ gtty(ttyfd,&tttvt); /* And one for virtual tty service */ #endif /* BELLV10 */ #endif /* ATTSV */ #endif /* BSD44ORPOSIX */ /* Section for changing line discipline. It's restored in ttres(). */ #ifdef AIXRS #ifndef AIX41 { union txname ld_name; int ld_idx = 0; ttld = 0; do { ld_name.tx_which = ld_idx++; ioctl(ttyfd, TXGETCD, &ld_name); if (!strncmp(ld_name.tx_name, "rts", 3)) ttld |= 1; } while (*ld_name.tx_name); debug(F101,"AIX line discipline","",ttld); } #endif /* AIX41 */ #endif /* AIXRS */ #ifdef BSD41 /* For 4.1BSD only, force "old" tty driver, new one botches TANDEM. */ { int k; ioctl(ttyfd, TIOCGETD, &ttld); /* Get and save line discipline */ debug(F101,"4.1bsd line discipline","",ttld); k = OTTYDISC; /* Switch to "old" discipline */ k = ioctl(ttyfd, TIOCSETD, &k); debug(F101,"4.1bsd tiocsetd","",k); } #endif /* BSD41 */ #ifdef aegis /* This was previously done before the last two TCGETA or gtty above, * in both the ATTSV and not-ATTSV case. If it is not okay to have only * one copy if it here instead, give us a shout! */ sio_$control((short)ttyfd, sio_$raw_nl, false, st); if (xlocal) { /* ignore breaks from local line */ sio_$control((short)ttyfd, sio_$int_enable, false, st); sio_$control((short)ttyfd, sio_$quit_enable, false, st); } #endif /* aegis */ #ifdef VXVE ttraw.c_line = 0; /* STTY line 0 for VX/VE */ tttvt.c_line = 0; /* STTY line 0 for VX/VE */ ioctl(ttyfd,TCSETA,&ttraw); #endif /* vxve */ /* If O_NDELAY was used during open(), then remove it now. */ #ifdef O_NDELAY debug(F100,"ttopen O_NDELAY","",0); if (xlocal > 0) { if (fcntl(ttyfd, F_GETFL, 0) & O_NDELAY) { debug(F100,"ttopen fcntl O_NDELAY","",0); #ifndef aegis if (fcntl(ttyfd,F_SETFL, fcntl(ttyfd, F_GETFL, 0) & ~O_NDELAY) < 0) { debug(F100,"ttopen fcntl failure to unset O_NDELAY","",0); perror("Can't unset O_NDELAY"); } #endif /* aegis */ /* Some systems, notably Xenix (don't know how common this is in * other systems), need special treatment to get rid of the O_NDELAY * behaviour on read() with respect to carrier presence (i.e. read() * returning 0 when carrier absent), even though the above fcntl() * is enough to make read() wait for input when carrier is present. * This magic, in turn, requires CLOCAL for working when the carrier * is absent. But if xlocal == 0, presumably you already have CLOCAL * or you have a carrier, otherwise you wouldn't be running this. */ debug(F101,"ttopen xlocal","",xlocal); #ifdef ATTSV #ifdef BSD44ORPOSIX #ifdef COMMENT /* 12 Aug 1997 */ #ifdef __bsdi__ if (xlocal) ttraw.c_cflag |= CLOCAL; #else #ifdef __FreeBSD__ if (xlocal) ttraw.c_cflag |= CLOCAL; #endif /* __FreeBSD__ */ #endif /* __bsdi__ */ #else /* Not COMMENT */ #ifdef CLOCAL if (xlocal) /* Unset this if it's defined. */ ttraw.c_cflag |= CLOCAL; #endif /* CLOCAL */ #endif /* COMMENT */ debug(F101,"ttopen BSD44ORPOSIX calling tcsetattr","",TCSADRAIN); if (tcsetattr(ttyfd, TCSADRAIN, &ttraw) < 0) { debug(F100,"ttopen POSIX tcseattr fails","",0); perror("tcsetattr"); } #else /* !BSD44ORPOSIX */ if (xlocal) { ttraw.c_cflag |= CLOCAL; debug(F100,"ttopen calling ioctl(TCSETA)","",0); errno = 0; if (ioctl(ttyfd, TCSETA, &ttraw) < 0) { debug(F101,"ttopen ioctl(TCSETA) fails","",errno); perror("ioctl(TCSETA)"); } } #endif /* BSD44ORPOSIX */ #endif /* ATTSV */ #ifndef NOCOTFMC /* = NO Close(Open()) To Force Mode Change */ /* Reportedly lets uugetty grab the device in SCO UNIX 3.2 / XENIX 2.3 */ debug(F100,"ttopen executing close/open","",0); close( priv_opn(fnam, O_RDWR) ); /* Magic to force change. */ #endif /* NOCOTFMC */ } } #endif /* O_NDELAY */ /* Instruct the system how to treat the carrier, and set a few other tty * parameters. * * This also undoes the temporary setting of CLOCAL that may have been done * for the close(open()) above (except in Xenix). Also throw in ~ECHO, to * prevent the other end of the line from sitting there talking to itself, * producing garbage when the user performs a connect. * * SCO Xenix unfortunately seems to ignore the actual state of CLOCAL. * Now it thinks CLOCAL is always on. It seems the only real solution for * Xenix is to switch between the lower and upper case device names. * * This section may at some future time expand into setting a complete * collection of tty parameters, or call a function shared with ttpkt()/ * ttvt() that does so. On the other hand, the initial parameters are not * that important, since ttpkt() or ttvt() should always fix that before * any communication is done. Well, we'll see... */ if (xlocal) { curcarr = -2; debug(F100,"ttopen calling carrctl","",0); carrctl(&ttraw, ttcarr == CAR_ON); debug(F100,"ttopen carrctl ok","",0); #ifdef COHERENT #define SVORPOSIX #endif /* COHERENT */ #ifdef SVORPOSIX ttraw.c_lflag &= ~ECHO; ttold.c_lflag &= ~ECHO; #ifdef BSD44ORPOSIX y = tcsetattr(ttyfd, TCSADRAIN, &ttraw); debug(F101,"ttopen tcsetattr","",y); #else y = ioctl(ttyfd, TCSETA, &ttraw); debug(F100,"ttopen ioctl","",y); #endif /* BSD44ORPOSIX */ #else /* BSD, etc */ ttraw.sg_flags &= ~ECHO; ttold.sg_flags &= ~ECHO; #ifdef BELLV10 y = ioctl(ttyfd,TIOCSETP,&ttraw); debug(F100,"ttopen ioctl","",y); #else y = stty(ttyfd,&ttraw); debug(F100,"ttopen stty","",y); #endif /* BELLV10 */ #endif /* SVORPOSIX */ #ifdef COHERENT #undef SVORPOSIX #endif /* COHERENT */ /* ttflui(); */ /* This fails for some reason. */ } /* Get current speed */ #ifndef BEBOX ttspeed = ttgspd(); #else ttspeed = 19200; #endif /* !BEBOX */ debug(F101,"ttopen ttspeed","",ttspeed); /* Done, make entries in debug log, restore Ctrl-C trap, and return. */ debug(F101,"ttopen ttyfd","",ttyfd); debug(F101,"ttopen *lcl","",*lcl); debug(F111,"ttopen lock file",flfnam,lkf); signal(SIGINT,occt); sigint_ign = (occt == SIG_IGN) ? 1 : 0; gotsigs = 0; return(0); } /* D O _ O P E N -- Do the right kind of open() call for the tty. */ int do_open(ttname) char *ttname; { int flags; #ifdef QNX6 /* O_NONBLOCK on /dev/tty makes open() fail */ return(priv_opn(ttname, O_RDWR | ( ((int)strcmp(ttname,"/dev/tty") == 0) ? 0 : (ttcarr != CAR_ON) ? O_NONBLOCK : 0) ) ); #else /* !QNX6 */ #ifndef O_NDELAY /* O_NDELAY not defined */ return(priv_opn(ttname,2)); #else /* O_NDELAY defined */ #ifdef ATT7300 /* Open comms line without waiting for carrier so initial call does not hang because state of "modem" is likely unknown at the initial call -jrd. If this is needed for the getty stuff to work, and the open would not work without O_NDELAY when getty is still on, then this special case is ok. Otherwise, get rid of it. -ske */ return(priv_opn(ttname, O_RDWR | O_NDELAY)); #else /* !ATT7300 */ /* Normal case. Use O_NDELAY according to SET CARRIER. See ttscarr(). */ flags = O_RDWR; debug(F101,"do_open xlocal","",xlocal); debug(F111,"do_open flags A",ttname,flags); if (xlocal && (ttcarr != CAR_ON)) flags |= O_NDELAY; debug(F111,"do_open flags B",ttname,flags); return(priv_opn(ttname, flags)); #endif /* !ATT7300 */ #endif /* O_NDELAY */ #endif /* QNX6 */ } /* T T C L O S -- Close the TTY, releasing any lock. */ static int ttc_state = 0; /* ttclose() state */ static char * ttc_nam[] = { "setup", "hangup", "reset", "close" }; int ttclos(foo) int foo; { /* Arg req'd for signal() prototype */ int xx, x = 0; extern int exithangup; debug(F101,"ttclos ttyfd","",ttyfd); debug(F101,"ttclos netconn","",netconn); debug(F101,"ttclos xlocal","",xlocal); #ifdef NOFDZERO debug(F100,"ttclos NOFDZERO","",0); #endif /* NOFDZERO */ #ifdef COMMENT #ifdef TTLEBUF le_init(); /* No need for any of this */ #endif /* TTLEBUF */ #endif /* COMMENT */ if (ttyfd < 0) /* Wasn't open. */ return(0); if (ttfdflg) /* If we inherited ttyfd from */ return(0); /* another process, don't close it. */ tvtflg = 0; /* (some day get rid of this...) */ gotsigs = 0; #ifdef IKSD if (inserver) { #ifdef TNCODE tn_push(); /* Place any waiting data into input*/ tn_sopt(DO,TELOPT_LOGOUT); /* Send LOGOUT option before close */ TELOPT_UNANSWERED_DO(TELOPT_LOGOUT) = 1; tn_reset(); /* The Reset Telnet Option table. */ #endif /* TNCODE */ #ifdef CK_SSL if (ssl_active_flag) { if (ssl_debug_flag) BIO_printf(bio_err,"calling SSL_shutdown(ssl)\n"); SSL_shutdown(ssl_con); SSL_free(ssl_con); ssl_con = NULL; ssl_active_flag = 0; } if (tls_active_flag) { if (ssl_debug_flag) BIO_printf(bio_err,"calling SSL_shutdown(tls)\n"); SSL_shutdown(tls_con); SSL_free(tls_con); tls_con = NULL; tls_active_flag = 0; } #endif /* CK_SSL */ } #endif /* IKSD */ #ifdef NETCMD if (ttpipe) { /* We've been using a pipe */ /* ttpipe = 0; */ if (ttpid > 0) { int wstat; int statusp; close(fdin); /* Close these. */ close(fdout); fdin = fdout = -1; kill(ttpid,1); /* Kill fork with SIGHUP */ while (1) { wstat = wait(&statusp); if (wstat == ttpid || wstat == -1) break; pexitstat = (statusp & 0xff) ? statusp : statusp >> 8; } ttpid = 0; } netconn = 0; wasclosed = 1; ttyfd = -1; return(0); } #endif /* NETCMD */ #ifdef NETPTY if (ttpty) { #ifndef NODOPTY end_pty(); #endif /* NODOPTY */ close(ttyfd); netconn = 0; wasclosed = 1; ttpty = 0; ttyfd = -1; return(0); } #endif /* NETPTY */ #ifdef NETCONN if (netconn) { /* If it's a network connection. */ debug(F100,"ttclos closing net","",0); netclos(); /* Let the network module close it. */ netconn = 0; /* No more network connection. */ debug(F101,"ttclos ttyfd after netclos","",ttyfd); /* Should be -1 */ return(0); } #endif /* NETCONN */ if (xlocal) { /* We're closing a SET LINE device */ #ifdef FT21 /* Fortune 2.1-specific items ... */ ioctl(ttyfd,TIOCHPCL, NULL); #endif /* FT21 */ #ifdef ultrix /* Ultrix-specific items ... */ #ifdef TIOCSINUSE /* Unset the INUSE flag that we set in ttopen() */ ioctl(ttyfd, TIOCSINUSE, NULL); #endif /* TIOCSINUSE */ ioctl(ttyfd, TIOCNMODEM, &x); #ifdef COMMENT /* What was this? */ ioctl(ttyfd, TIOCNCAR, NULL); #endif /* COMMENT */ #endif /* ultrix */ } /* This is to prevent us from sticking in tthang() or close(). */ #ifdef O_NDELAY #ifndef aegis if (ttyfd > 0) { /* But skip it on stdin. */ debug(F100,"ttclos setting O_NDELAY","",0); x = fcntl(ttyfd,F_SETFL,fcntl(ttyfd,F_GETFL, 0)|O_NDELAY); #ifdef DEBUG if (deblog && x == -1) { perror("Warning - Can't set O_NDELAY"); debug(F101,"ttclos fcntl failure to set O_NDELAY","",x); } #endif /* DEBUG */ } #endif /* aegis */ #endif /* O_NDELAY */ x = 0; ttc_state = 0; if (xlocal #ifdef NOFDZERO || ttyfd > 0 #endif /* NOFDZERO */ ) { saval = signal(SIGALRM,xtimerh); /* Enable timer interrupt. */ xx = alarm(8); /* Allow 8 seconds. */ debug(F101,"ttclos alarm","",xx); if ( #ifdef CK_POSIX_SIG sigsetjmp(sjbuf,1) #else setjmp(sjbuf) #endif /* CK_POSIX_SIG */ ) { /* Timer went off? */ x = -1; #ifdef DEBUG debug(F111,"ttclos ALARM TRAP errno",ckitoa(ttc_state),errno); printf("ttclos() timeout: %s\n", ttc_nam[ttc_state]); #endif /* DEBUG */ } /* Hang up the device (drop DTR) */ errno = 0; debug(F111,"ttclos A",ckitoa(x),ttc_state); if (ttc_state < 1) { ttc_state = 1; debug(F101,"ttclos exithangup","",exithangup); if (exithangup) { alarm(8); /* Re-arm the timer */ debug(F101,"ttclos calling tthang()","",x); x = tthang(); /* Hang up first, then... */ debug(F101,"ttclos tthang()","",x); } #ifndef CK_NOHUPCL /* Oct 2006 - Leave DTR on if SET EXIT HANGUP OFF. Suggested by Soewono Effendi. */ #ifdef HUPCL else { ttold.c_cflag &= ~HUPCL; /* Let's see how this travels */ #ifdef BSD44ORPOSIX tcsetattr(ttyfd,TCSANOW,&ttold); #else /* !BSD44ORPOSIX */ #ifdef ATTSV ioctl(ttyfd,TCSETAW,&ttold); #else /* !ATTSV */ stty(ttyfd,&ttold); #endif /* ATTSV */ #endif /* BSD44ORPOSIX */ } #endif /* HUPCL */ #endif /* CK_NOHUPCL */ } /* Put back device modes as we found them */ errno = 0; debug(F111,"ttclos B",ckitoa(x),ttc_state); if (ttc_state < 2) { ttc_state = 2; /* Don't try to mess with tty modes if tthang failed() */ /* since it probably won't work. */ if (x > -1) { debug(F101,"ttclos calling ttres()","",x); signal(SIGALRM,xtimerh); /* Re-enable the alarm. */ alarm(8); /* Re-arm the timer */ x = ttres(); /* Reset device modes. */ debug(F101,"ttclos ttres()","",x); alarm(0); } } /* Close the device */ errno = 0; debug(F101,"ttclos C","",ttc_state); if (ttc_state < 3) { ttc_state = 3; errno = 0; debug(F101,"ttclos calling close","",x); signal(SIGALRM,xtimerh); /* Re-enable alarm. */ alarm(8); /* Re-arm the timer */ x = close(ttyfd); /* Close the device. */ debug(F101,"ttclos close()","",x); if (x > -1) ttc_state = 3; } debug(F101,"ttclos D","",ttc_state); ttimoff(); /* Turn off timer. */ if (x < 0) { printf("?WARNING - close failed: %s\n",ttnmsv); #ifdef DEBUG if (deblog) { printf("errno = %d\n", errno); debug(F101,"ttclos failed","",errno); } #endif /* DEBUG */ } /* Unlock after closing but before any getty mumbo jumbo */ debug(F100,"ttclos about to call ttunlck","",0); if (ttunlck()) /* Release uucp-style lock */ fprintf(stderr,"Warning, problem releasing lock\r\n"); } /* For bidirectional lines, restore getty if it was there before. */ #ifdef ACUCNTRL /* 4.3BSD acucntrl() method. */ if (xlocal) { debug(F100,"ttclos ACUCNTRL","",0); acucntrl("enable",ttnmsv); /* Enable getty on the device. */ } #else #ifdef ATT7300 /* ATT UNIX PC (3B1, 7300) method. */ if (xlocal) { debug(F100,"ttclos ATT7300 ongetty","",0); if (attmodem & DOGETY) /* Was getty(1m) running before us? */ ongetty(ttnmsv); /* Yes, restart getty on tty line */ attmodem &= ~DOGETY; /* No phone in use, getty restored */ } #endif /* ATT7300 */ #endif /* System-dependent getty-restoring methods */ #ifdef sony_news km_ext = -1; /* Invalidate device's Kanji-mode */ #endif /* sony_news */ ttyfd = -1; /* Invalidate the file descriptor. */ wasclosed = 1; debug(F100,"ttclos done","",0); return(0); } /* T T H A N G -- Hangup phone line or network connection. */ /* Returns: 0 if it does nothing. 1 if it believes that it hung up successfully. -1 if it believes that the hangup attempt failed. */ #define HUPTIME 500 /* Milliseconds for hangup */ #ifdef COMMENT /* The following didn't work but TIOCSDTR does work */ #ifdef UNIXWARE /* Define HUP_POSIX to force non-POSIX builds to use the POSIX hangup method */ #ifndef POSIX /* Such as Unixware 1.x, 2.x */ #ifndef HUP_POSIX #define HUP_POSIX #endif /* HUP_POSIX */ #endif /* POSIX */ #endif /* UNIXWARE */ #endif /* COMMENT */ #ifndef USE_TIOCSDTR #ifdef __NetBSD__ /* Because the POSIX method (set output speed to 0) doesn't work in NetBSD */ #ifdef TIOCSDTR #ifdef TIOCCDTR #define USE_TIOCSDTR #endif /* TIOCCDTR */ #endif /* TIOCSDTR */ #endif /* __NetBSD__ */ #endif /* USE_TIOCSDTR */ #ifndef HUP_CLOSE_POSIX #ifdef OU8 #define HUP_CLOSE_POSIX #else #ifdef CK_SCOV5 #define HUP_CLOSE_POSIX #endif /* CK_SCOV5 */ #endif /* OU8 */ #endif /* HUP_CLOSE_POSIX */ #ifdef NO_HUP_CLOSE_POSIX #ifdef HUP_CLOSE_POSIX #undef HUP_CLOSE_POSIX #endif /* HUP_CLOSE_POSIX */ #endif /* NO_HUP_CLOSE_POSIX */ int tthang() { #ifdef NOLOCAL return(0); #else int x = 0; /* Sometimes used as return code. */ #ifndef POSIX int z; /* worker */ #endif /* POSIX */ #ifdef COHERENT #define SVORPOSIX #endif /* COHERENT */ #ifdef SVORPOSIX /* AT&T, POSIX, HPUX declarations. */ int spdsav; /* for saving speed */ #ifdef HUP_POSIX int spdsavi; #else #ifdef BSD44ORPOSIX int spdsavi; #endif /* BSD44ORPOSIX */ #endif /* HUP_POSIX */ #ifdef HPUX /* Early versions of HP-UX omitted the mflag typedef. If you get complaints about it, just change it to long (or better still, unsigned long). */ mflag dtr_down = 00000000000, modem_rtn, modem_sav; char modem_state[64]; #endif /* HPUX */ int flags; /* fcntl flags */ unsigned short ttc_save; #endif /* SVORPOSIX */ if (ttyfd < 0) return(0); /* Don't do this if not open */ if (xlocal < 1) return(0); /* Don't do this if not local */ #ifdef NETCMD if (ttpipe) return((ttclos(0) < 0) ? -1 : 1); #endif /* NETCMD */ #ifdef NETPTY if (ttpty) return((ttclos(0) < 0) ? -1 : 1); #endif /* NETPTY */ #ifdef NETCONN if (netconn) { /* Network connection. */ #ifdef TN_COMPORT if (istncomport()) { int rc = tnc_set_dtr_state(0); if (rc >= 0) { msleep(HUPTIME); rc = tnc_set_dtr_state(1); } return(rc >= 0 ? 1 : -1); } else #endif /* TN_COMPORT */ return((netclos() < 0) ? -1 : 1); /* Just close it. */ } #endif /* NETCONN */ /* From here down, we handle real tty devices. */ #ifdef HUP_POSIX /* e.g. for Unixware 2, where we don't have a full POSIX build, we still have to use POSIX-style hangup. Thus the duplication of this and the next case, the only difference being we use a local termios struct here, since a different model is used elsewhere. NO LONGER USED as of C-Kermit 8.0 -- it turns out that this method, even though it compiles and executes without error, doesn't actually work (i.e. DTR does not drop), whereas the TIOCSDTR method works just fine, */ { struct termios ttcur; int x; debug(F100,"tthang HUP_POSIX style","",0); x = tcgetattr(ttyfd, &ttcur); /* Get current attributes */ debug(F111,"tthang tcgetattr",ckitoa(errno),x); if (x < 0) return(-1); spdsav = cfgetospeed(&ttcur); /* Get current speed */ debug(F111,"tthang cfgetospeed",ckitoa(errno),spdsav); spdsavi = cfgetispeed(&ttcur); /* Get current speed */ debug(F111,"tthang cfgetispeed",ckitoa(errno),spdsavi); x = cfsetospeed(&ttcur,B0); /* Replace by 0 */ debug(F111,"tthang cfsetospeed",ckitoa(errno),x); if (x < 0) return(-1); x = cfsetispeed(&ttcur,B0); debug(F111,"tthang cfsetispeed",ckitoa(errno),x); if (x < 0) return(-1); x = tcsetattr(ttyfd,TCSADRAIN,&ttcur); debug(F111,"tthang tcsetattr B0",ckitoa(errno),x); if (x < 0) return(-1); msleep(HUPTIME); /* Sleep 0.5 sec */ x = cfsetospeed(&ttcur,spdsav); /* Restore prev speed */ if (x < 0) return(-1); debug(F111,"tthang cfsetospeed prev",ckitoa(errno),x); x = cfsetispeed(&ttcur,spdsavi); debug(F111,"tthang cfsetispeed prev",ckitoa(errno),x); if (x < 0) return(-1); x = tcsetattr(ttyfd,TCSADRAIN,&ttcur); debug(F111,"tthang tcsetattr restore",ckitoa(errno),x); if (x < 0) return(-1); return(1); } #else #ifdef BSD44ORPOSIX #ifdef QNX { int x; x = tcdropline(ttyfd,500); debug(F101,"tthang QNX tcdropline","",x); ttcur.c_cflag |= CLOCAL; x = tcsetattr(ttyfd,TCSADRAIN,&ttcur); debug(F101,"tthang QNX tcsetattr restore","",x); if (x < 0) { debug(F101,"tthang QNX tcsetattr restore errno","",errno); return(-1); } /* Fix flags - ensure O_NONBLOCK is off */ errno = 0; debug(F101,"tthang QNX iniflags","",iniflags); if (fcntl(ttyfd, F_SETFL, iniflags) == -1) { debug(F101,"tthang QNX F_SETFL errno","",errno); return(-1); } return(x); } #else /* QNX */ { int x; #ifdef USE_TIOCSDTR debug(F100,"tthang BSD44ORPOSIX USE_TIOCSDTR","",0); errno = 0; x = ioctl(ttyfd, TIOCCDTR, NULL); debug(F111,"tthang BSD44ORPOSIX ioctl TIOCCDTR",ckitoa(errno),x); if (x < 0) return(-1); msleep(HUPTIME); /* Sleep 0.5 sec */ errno = 0; x = ioctl(ttyfd, TIOCSDTR, NULL); debug(F111,"tthang BSD44ORPOSIX ioctl TIOCSDTR",ckitoa(errno),x); if (x < 0) return(-1); #else /* USE_TIOCSDTR */ #ifdef HUP_CLOSE_POSIX /* In OSR5 versions where TIOCSDTR is not defined (up to and including at least 5.0.6a) the POSIX APIs in the "#else" part below are available but don't work, and no other APIs are available that do work. In this case we have to drop DTR by brute force: close and reopen the port. This code actually works, but all the steps are crucial: setting CLOCAL, the O_NDELAY manipulations, etc. */ debug(F100,"tthang HUP_CLOSE_POSIX close/open","",0); debug(F101,"tthang HUP_CLOSE_POSIX O_NONBLOCK","",O_NONBLOCK); debug(F101,"tthang HUP_CLOSE_POSIX O_NDELAY","",O_NDELAY); errno = 0; x = tcgetattr(ttyfd, &ttcur); /* Get current attributes */ debug(F101,"tthang HUP_CLOSE_POSIX tcgetattr","",x); if (x < 0) { debug(F101,"tthang HUP_CLOSE_POSIX tcgetattr errno","",errno); return(-1); } errno = 0; x = close(ttyfd); /* Close without releasing lock */ if (x < 0) { debug(F101,"tthang HUP_CLOSE_POSIX close errno","",errno); return(-1); } errno = 0; x = msleep(500); /* Pause half a second */ if (x < 0) { /* Or if that doesn't work, 1 sec */ debug(F101,"tthang HUP_CLOSE_POSIX msleep errno","",errno); sleep(1); } errno = 0; ttyfd = priv_opn(ttnmsv, (O_RDWR|O_NDELAY)); /* Reopen the device */ debug(F111,"tthang HUP_CLOSE_POSIX reopen",ttnmsv,ttyfd); if (ttyfd < 0) { debug(F101,"tthang HUP_CLOSE_POSIX reopen errno","",errno); return(-1); } debug(F101,"tthang HUP_CLOSE_POSIX re-ttopen ttyfd","",ttyfd); /* Restore previous attributes */ errno = 0; tvtflg = 0; ttcur.c_cflag |= CLOCAL; x = tcsetattr(ttyfd,TCSADRAIN,&ttcur); debug(F101,"tthang HUP_CLOSE_POSIX tcsetattr restore","",x); if (x < 0) { debug(F101,"tthang HUP_CLOSE_POSIX tcsetattr restore errno", "",errno); return(-1); } /* Fix flags - ensure O_NDELAY and O_NONBLOCK are off */ errno = 0; if ((x = fcntl(ttyfd, F_GETFL, 0)) == -1) { debug(F101,"tthang HUP_CLOSE_POSIX F_GETFL errno","",errno); return(-1); } debug(F101,"tthang HUP_CLOSE_POSIX flags","",x); errno = 0; x &= ~(O_NONBLOCK|O_NDELAY); debug(F101,"tthang HUP_CLOSE_POSIX flags to set","",x); debug(F101,"tthang HUP_CLOSE_POSIX iniflags","",iniflags); if (fcntl(ttyfd, F_SETFL, x) == -1) { debug(F101,"tthang HUP_CLOSE_POSIX F_SETFL errno","",errno); return(-1); } #ifdef DEBUG if (deblog) { if ((x = fcntl(ttyfd, F_GETFL, 0)) > -1) { debug(F101,"tthang HUP_CLOSE_POSIX flags","",x); debug(F101,"tthang HUP_CLOSE_POSIX flags & O_NONBLOCK", "",x&O_NONBLOCK); debug(F101,"tthang HUP_CLOSE_POSIX flags & O_NDELAY", "",x&O_NDELAY); } } #endif /* DEBUG */ #else /* HUP_CLOSE_POSIX */ /* General BSD44ORPOSIX case (Linux, BSDI, FreeBSD, etc) */ debug(F100,"tthang BSD44ORPOSIX B0","",0); x = tcgetattr(ttyfd, &ttcur); /* Get current attributes */ debug(F111,"tthang BSD44ORPOSIX tcgetattr",ckitoa(errno),x); if (x < 0) return(-1); spdsav = cfgetospeed(&ttcur); /* Get current speed */ debug(F111,"tthang BSD44ORPOSIX cfgetospeed",ckitoa(errno),spdsav); spdsavi = cfgetispeed(&ttcur); /* Get current speed */ debug(F111,"tthang BSD44ORPOSIX cfgetispeed",ckitoa(errno),spdsavi); x = cfsetospeed(&ttcur,B0); /* Replace by 0 */ debug(F111,"tthang BSD44ORPOSIX cfsetospeed",ckitoa(errno),x); if (x < 0) return(-1); x = cfsetispeed(&ttcur,B0); debug(F111,"tthang BSD44ORPOSIX cfsetispeed",ckitoa(errno),x); if (x < 0) return(-1); /* This gets EINVAL on NetBSD 1.4.1 because of B0... */ x = tcsetattr(ttyfd,TCSADRAIN,&ttcur); debug(F111,"tthang BSD44ORPOSIX tcsetattr B0",ckitoa(errno),x); if (x < 0) return(-1); msleep(HUPTIME); /* Sleep 0.5 sec */ debug(F101,"tthang BSD44ORPOSIX restore output speed","",spdsav); x = cfsetospeed(&ttcur,spdsav); /* Restore prev speed */ debug(F111,"tthang BSD44ORPOSIX cfsetospeed prev",ckitoa(errno),x); if (x < 0) return(-1); debug(F101,"tthang BSD44ORPOSIX restore input speed","",spdsavi); x = cfsetispeed(&ttcur,spdsavi); debug(F111,"tthang BSD44ORPOSIX cfsetispeed prev",ckitoa(errno),x); if (x < 0) return(-1); ttcur.c_cflag |= CLOCAL; /* Don't expect CD after hangup */ x = tcsetattr(ttyfd,TCSADRAIN,&ttcur); debug(F111,"tthang BSD44ORPOSIX tcsetattr restore",ckitoa(errno),x); if (x < 0) return(-1); #endif /* HUP_CLOSE_POSIX */ #endif /* USE_TIOCSDTR */ return(1); } #endif /* QNX */ #else /* BSD44ORPOSIX */ #ifdef aegis /* Apollo Aegis */ sio_$control((short)ttyfd, sio_$dtr, false, st); /* DTR down */ msleep(HUPTIME); /* pause */ sio_$control((short)ttyfd, sio_$dtr, true, st); /* DTR up */ return(1); #endif /* aegis */ #ifdef ANYBSD /* Any BSD version. */ #ifdef TIOCCDTR /* Except those that don't have this */ debug(F100,"tthang BSD style","",0); if (ioctl(ttyfd,TIOCCDTR,0) < 0) { /* Clear DTR. */ debug(F101,"tthang TIOCCDTR fails","",errno); return(-1); } msleep(HUPTIME); /* For about 1/2 sec */ errno = 0; x = ioctl(ttyfd,TIOCSDTR,0); /* Restore DTR */ if (x < 0) { /* For some reason, this tends to fail with "no such device or address" but the operation still works, probably because of the close/open later on. So let's not scare the user unnecessarily here. */ debug(F101,"tthang TIOCSDTR errno","",errno); /* Log the error */ x = 1; /* Pretend we succeeded */ } else if (x == 0) x = 1; /* Success */ #ifdef COMMENT #ifdef FT21 ioctl(ttyfd, TIOCSAVEMODES, 0); ioctl(ttyfd, TIOCHPCL, 0); close(ttyfd); /* Yes, must do this twice */ if ((ttyfd = open(ttnmsv,2)) < 0) /* on Fortune computers... */ return(-1); /* (but why?) */ else x = 1; #endif /* FT21 */ #endif /* COMMENT */ #endif /* TIOCCDTR */ close(do_open(ttnmsv)); /* Clear i/o error condition */ errno = 0; #ifdef COMMENT /* This is definitely dangerous. Why was it here? */ z = ttvt(ttspeed,ttflow); /* Restore modes. */ debug(F101,"tthang ttvt returns","",z); return(z < 0 ? -1 : 1); #else return(x); #endif /* COMMENT */ #endif /* ANYBSD */ #ifdef ATTSV /* AT&T UNIX section, includes HP-UX and generic AT&T System III/V... */ #ifdef HPUX /* Hewlett Packard allows explicit manipulation of modem signals. */ #ifdef COMMENT /* Old way... */ debug(F100,"tthang HP-UX style","",0); if (ioctl(ttyfd,MCSETAF,&dtr_down) < 0) /* lower DTR */ return(-1); /* oops, can't. */ msleep(HUPTIME); /* Pause half a second. */ x = 1; /* Set return code */ if (ioctl(ttyfd,MCGETA,&modem_rtn) > -1) { /* Get line status. */ if ((modem_rtn & MDCD) != 0) /* Check if CD is low. */ x = -1; /* CD didn't drop, fail. */ } else x = -1; /* Even if above calls fail, RTS & DTR should be turned back on. */ modem_rtn = MRTS | MDTR; if (ioctl(ttyfd,MCSETAF,&modem_rtn) < 0) x = -1; return(x); #else /* New way, from Hellmuth Michaelis */ debug(F100,"tthang HP-UX style, HPUXDEBUG","",0); if (ioctl(ttyfd,MCGETA,&modem_rtn) == -1) { /* Get current status. */ debug(F100,"tthang HP-UX: can't get modem lines, NO HANGUP!","",0); return(-1); } sprintf(modem_state,"%#lx",modem_rtn); debug(F110,"tthang HP-UX: modem lines = ",modem_state,0); modem_sav = modem_rtn; /* Save current modem signals */ modem_rtn &= ~MDTR; /* Turn DTR bit off */ sprintf(modem_state,"%#lx",modem_rtn); debug(F110,"tthang HP-UX: DTR down = ",modem_state,0); if (ioctl(ttyfd,MCSETAF,&modem_rtn) < 0) { /* lower DTR */ debug(F100,"tthang HP-UX: can't lower DTR!","",0); return(-1); /* oops, can't. */ } msleep(HUPTIME); /* Pause half a second. */ x = 1; /* Set return code */ if (ioctl(ttyfd,MCGETA,&modem_rtn) > -1) { /* Get line status. */ sprintf(modem_state,"%#lx",modem_rtn); debug(F110,"tthang HP-UX: modem lines got = ",modem_state,0); if ((modem_rtn & MDCD) != 0) { /* Check if CD is low. */ debug(F100,"tthang HP-UX: DCD not down","",0); x = -1; /* CD didn't drop, fail. */ } else { debug(F100,"tthang HP-UX: DCD down","",0); } } else { x = -1; debug(F100,"tthang HP-UX: can't get DCD status !","",0); } /* Even if above calls fail, DTR should be turned back on. */ modem_sav |= MDTR; if (ioctl(ttyfd,MCSETAF,&modem_sav) < 0) { x = -1; debug(F100,"tthang HP-UX: can't set saved state","",0); } else { sprintf(modem_state,"%#lx",modem_sav); debug(F110,"tthang HP-UX: final modem lines = ",modem_state,0); } return(x); #endif /* COMMENT */ #else /* AT&T but not HP-UX */ /* SVID for AT&T System V R3 defines ioctl's for handling modem signals. */ /* It is not known how many, if any, systems actually implement them, */ /* so we include them here in ifdef's. */ /* Unixware has the TIOCMxxx symbols defined, but calling ioctl() with them gives error 22 (invalid argument). */ #ifndef _IBMR2 /* No modem-signal twiddling for IBM RT PC or RS/6000. In AIX 3.1 and earlier, the ioctl() call is broken. This code could be activated for AIX 3.1 with PTF 2006 or later (e.g. AIX 3.2), but close/open does the job too, so why bother. */ #ifdef TIOCMBIS /* Bit Set */ #ifdef TIOCMBIC /* Bit Clear */ #ifdef TIOCM_DTR /* DTR */ /* Clear DTR, sleep 300 msec, turn it back on. */ /* If any of the ioctl's return failure, go on to the next section. */ z = TIOCM_DTR; /* Code for DTR. */ #ifdef COMMENT /* This was the cause of the troubles with the Solaris Port Monitor. The problem is: RTS never comes back on. Moral: Don't do it! (But why doesn't it come back on? See the TIOCMBIS call...) */ #ifdef TIOCM_RTS /* Lower RTS too if symbol is known. */ z |= TIOCM_RTS; #endif /* TIOCM_RTS */ #endif /* COMMENT */ debug(F101,"tthang TIOCM signal mask","",z); if (ioctl(ttyfd,TIOCMBIC,&z) > -1) { /* Try to lower DTR. */ debug(F100,"tthang TIOCMBIC ok","",0); msleep(HUPTIME); /* Pause half a second. */ if (ioctl(ttyfd,TIOCMBIS,&z) > -1) { /* Try to turn it back on. */ debug(F100,"tthang TIOCMBIS ok","",0); #ifndef CLSOPN return(1); /* Success, done. */ #endif /* CLSOPN */ } else { /* Couldn't raise, continue. */ debug(F101,"tthang TIOCMBIS errno","",errno); } } else { /* Couldn't lower, continue. */ debug(F101,"tthang TIOCMBIC errno","",errno); } #endif /* TIOCM_DTR */ #endif /* TIOCMBIC */ #endif /* TIOCMBIS */ #endif /* _IBMR2 */ /* General AT&T UNIX case, not HPUX. The following code is highly suspect. No two AT&T-based systems seem to do this the same way. The object is simply to turn off DTR and then turn it back on. SVID says the universal method for turning off DTR is to set the speed to zero, and this does seem to do the trick in all cases. But neither SVID nor any known man pages say how to turn DTR back on again. Some variants, like most Xenix implementations, raise DTR again when the speed is restored to a nonzero value. Others require the device to be closed and opened again, but this is risky because getty could seize the device during the instant it is closed. */ /* Return code for ioctl failures... */ #ifdef ATT6300 x = 1; /* ATT6300 doesn't want to fail... */ #else x = -1; #endif /* ATT6300 */ debug(F100,"tthang get settings","",0); if (ioctl(ttyfd,TCGETA,&ttcur) < 0) /* Get current settings. */ return(x); /* Fail if this doesn't work. */ if ((flags = fcntl(ttyfd,F_GETFL,0)) < 0) /* Get device flags. */ return(x); ttc_save = ttcur.c_cflag; /* Remember current speed. */ spdsav = ttc_save & CBAUD; debug(F101,"tthang speed","",spdsav); #ifdef O_NDELAY debug(F100,"tthang turning O_NDELAY on","",0); fcntl(ttyfd, F_SETFL, flags | O_NDELAY); /* Activate O_NDELAY */ #endif /* O_NDELAY */ #ifdef ATT7300 /* This is the way it is SUPPOSED to work */ ttcur.c_cflag &= ~CBAUD; /* Change the speed to zero. */ #else #ifdef RTAIX ttcur.c_cflag &= ~CBAUD; /* Change the speed to zero. */ #else /* This way really works but may be dangerous */ #ifdef u3b2 ttcur.c_cflag = ~(CBAUD|CLOCAL); /* Special for AT&T 3B2s */ /* (CLOCAL must be OFF) */ #else #ifdef SCO3R2 /* SCO UNIX 3.2 */ /* This is complete nonsense, but an SCO user claimed this change made hanging up work. Comments from other SCO UNIX 3.2 users would be appreciated. */ ttcur.c_cflag = CBAUD|B0; #else #ifdef AIXRS /* AIX on RS/6000 */ /* Can't set speed to zero on AIX 3.1 on RS/6000 64-port adapter, even though you can do it on the built-in port and the 8- and 16-port adapters. (Untested on 128-port adapter.) */ ttcur.c_cflag = CLOCAL|HUPCL|spdsav; /* Speed 0 causes EINVAL */ #else /* None of the above */ /* Set everything, including the speed, to zero, except for the CLOCAL and HUPCL bits. */ ttcur.c_cflag = CLOCAL|HUPCL; #endif /* AIXRS */ #endif /* SCO3R2 */ #endif /* u3b2 */ #endif /* RTAIX */ #endif /* ATT7300 */ #ifdef COMMENT /* and if none of those work, try one of these... */ ttcur.c_cflag = 0; ttcur.c_cflag = CLOCAL; ttcur.c_cflag &= ~(CBAUD|HUPCL); ttcur.c_cflag &= ~(CBAUD|CREAD); ttcur.c_cflag &= ~(CBAUD|CREAD|HUPCL); /* or other combinations */ #endif /* COMMENT */ #ifdef TCXONC debug(F100,"tthang TCXONC","",0); if (ioctl(ttyfd, TCXONC, 1) < 0) { debug(F101,"tthang TCXONC failed","",errno); } #endif /* TCXONC */ #ifdef TIOCSTART debug(F100,"tthang TIOCSTART","",0); if (ioctl(ttyfd, TIOCSTART, 0) < 0) { debug(F101,"tthang TIOCSTART failed","",errno); } #endif /* TIOCSTART */ if (ioctl(ttyfd,TCSETAF,&ttcur) < 0) { /* Fail if we can't. */ debug(F101,"tthang TCSETAF failed","",errno); fcntl(ttyfd, F_SETFL, flags); /* Restore flags */ return(-1); /* before returning. */ } msleep(300); /* Give modem time to notice. */ #ifndef NOCOTFMC /* Now, even though it doesn't say this in SVID or any man page, we have */ /* to close and reopen the device. This is not necessary for all systems, */ /* but it's impossible to predict which ones need it and which ones don't. */ #ifdef ATT7300 /* Special handling for ATT 7300 UNIX PC and 3B1, which have "phone" related ioctl's for their internal modems. attmodem has getty status and modem-in-use bit. Reportedly the ATT7300/3B1 PIOCDISC call is necessary, but also ruins the file descriptor, and no other phone(7) ioctl call can fix it. Whatever it does, it seems to escape detection with PIOCGETA and TCGETA. The only way to undo the damage is to close the fd and then reopen it. */ if (attmodem & ISMODEM) { debug(F100,"tthang attmodem close/open","",0); ioctl(ttyfd,PIOCUNHOLD,&dialer); /* Return call to handset. */ ioctl(ttyfd,PIOCDISC,&dialer); /* Disconnect phone. */ close(ttyfd); /* Close and reopen the fd. */ ttyfd = priv_opn(ttnmsv, O_RDWR | O_NDELAY); attmodem &= ~ISMODEM; /* Phone no longer in use. */ } #else /* !ATT7300 */ /* It seems we have to close and open the device for other AT&T systems */ /* too, and this is the place to do it. The following code does the */ /* famous close(open(...)) magic by default. If that doesn't work for you, */ /* then try uncommenting the following statement or putting -DCLSOPN in */ /* the makefile CFLAGS. */ /* #define CLSOPN */ #ifndef SCO32 /* Not needed by, and harmful to, SCO UNIX 3.2 / Xenix 2.3 */ #ifdef O_NDELAY #define OPENFLGS O_RDWR | O_NDELAY #else #define OPENFLGS O_RDWR #endif #ifndef CLSOPN /* This method is used by default, i.e. unless CLSOPN is defined. */ /* It is thought to be safer because there is no window where getty */ /* can seize control of the device. The drawback is that it might not work. */ debug(F101,"tthang close(open()), OPENFLGS","",OPENFLGS); close(priv_opn(ttnmsv, OPENFLGS)); #else /* This method is used if you #define CLSOPN. It is more likely to work */ /* than the previous method, but it's also more dangerous. */ debug(F101,"tthang close/open, OPENFLGS","",OPENFLGS); close(ttyfd); msleep(10); ttyfd = priv_opn(ttnmsv, OPENFLGS); /* Open it again */ #endif /* CLSOPN */ #undef OPENFLGS #endif /* SCO32 */ #endif /* ATT7300 */ #endif /* NOCOTFMC */ /* Now put all flags & modes back the way we found them. */ /* (Does the order of ioctl & fcntl matter ? ) */ debug(F100,"tthang restore settings","",0); ttcur.c_cflag = ttc_save; /* Get old speed back. */ if (ioctl(ttyfd,TCSETAF,&ttcur) < 0) /* ioctl parameters. */ return(-1); #ifdef O_NDELAY /* This is required for IBM RT and RS/6000, probably helps elsewhere too (?). After closing a modem line, the modem will probably not be asserting carrier any more, so we should not require carrier any more. If this causes trouble on non-IBM UNIXes, change the #ifdef to use _IBMR2 rather than O_NDELAY. */ flags &= ~O_NDELAY; /* Don't require carrier on reopen */ #endif /* O_NDELAY */ if (fcntl(ttyfd,F_SETFL,flags) < 0) /* fcntl parameters */ return(-1); return(1); #endif /* not HPUX */ #endif /* ATTSV */ #endif /* BSD44ORPOSIX */ #endif /* HUP_POSIX */ #endif /* NOLOCAL */ } /* Major change in 5A(174). We used to use LPASS8, if it was defined, to allow 8-bit data and Xon/Xoff flow control at the same time. But this LPASS8 business seems to have been causing trouble for everybody but me! For example, Annex terminal servers, commonly used with Encore computers, do not support LPASS8 even though the Encore itself does. Ditto for many other terminal servers, TELNET connections, rlogin connections, etc etc. Now, reportedly, even vanilla 4.3 BSD systems can't do this right on their serial lines, even though LPASS8 is a feature of 4.3BSD. So let's turn it off for everybody. That means we goes back to using raw mode, with no flow control. Phooey. NOTE: This must be done before the first reference to LPASS8 in this file, and after the last #include statment. */ #ifdef LPASS8 #undef LPASS8 #endif /* LPASS8 */ /* T T R E S -- Restore terminal to "normal" mode. */ /* ske@pkmab.se: There are two choices for what this function should do. * (1) Restore the tty to current "normal" mode, with carrier treatment * according to ttcarr, to be used after every kermit command. (2) Restore * the tty to the state it was in before kermit opened it. These choices * conflict, since ttold can't hold both choices of tty parameters. ttres() * is currently being called as in choice (1), but ttold basically holds * the initial parameters, as in (2), and the description at the beginning * of this file says (2). * * I don't think restoring tty parameters after all kermit commands makes * much of a difference. Restoring them upon exit from kermit may be of * some use in some cases (when the line is not restored automatically on * close, by the operating system). * * I can't choose which one it should be, so I haven't changed it. It * probably works as it is, too. It would probably even work even with * ttres() entirely deleted... * * (from fdc: Actually, this function operates in remote mode too, so * it restores the console (command) terminal to whatever mode it was * in before packet operations began, so that commands work right again.) */ int ttres() { /* Restore the tty to normal. */ int x; if (ttyfd < 0) return(-1); /* Not open. */ if (ttfdflg) return(0); /* Don't mess with terminal modes if */ /* we got ttyfd from another process */ #ifdef NETCONN if (netconn) { /* Network connection */ tvtflg = 0; #ifdef TCPSOCKET #ifdef TCP_NODELAY { extern int tcp_nodelay; /* Just put this back if necessary */ if (ttnet == NET_TCPB) { if (nodelay_sav > -1) { no_delay(ttyfd,nodelay_sav); nodelay_sav = -1; } } } #endif /* TCP_NODELAY */ #ifdef TN_COMPORT if (istncomport()) { int rc = -1; if ((rc = tnsetflow(ttflow)) < 0) return(rc); if (ttspeed <= 0) ttspeed = tnc_get_baud(); else if ((rc = tnc_set_baud(ttspeed)) < 0) return(rc); tnc_set_datasize(8); tnc_set_stopsize(stopbits); #ifdef HWPARITY if (hwparity) { switch (hwparity) { case 'e': /* Even */ debug(F100,"ttres 8 bits + even parity","",0); tnc_set_parity(3); break; case 'o': /* Odd */ debug(F100,"ttres 8 bits + odd parity","",0); tnc_set_parity(2); break; case 'm': /* Mark */ debug(F100,"ttres 8 bits + invalid parity: mark","",0); tnc_set_parity(4); break; case 's': /* Space */ debug(F100,"ttres 8 bits + invalid parity: space","",0); tnc_set_parity(5); break; } } else #endif /* HWPARITY */ { tnc_set_parity(1); /* None */ } tvtflg = 0; return(0); } #endif /* TN_COMPORT */ #endif /* TCPSOCKET */ return(0); } #endif /* NETCONN */ #ifdef NETCMD if (ttpipe) return(0); #endif /* NETCMD */ #ifdef NETPTY if (ttpty) return(0); #endif /* NETPTY */ /* Real terminal device, so restore its original modes */ #ifdef BSD44ORPOSIX /* For POSIX like this */ debug(F100,"ttres BSD44ORPOSIX","",0); x = tcsetattr(ttyfd,TCSADRAIN,&ttold); #else /* For all others... */ #ifdef ATTSV /* For AT&T versions... */ debug(F100,"ttres ATTSV","",0); x = ioctl(ttyfd,TCSETAW,&ttold); /* Restore tty modes this way. */ #else /* Here we restore the modes for BSD */ #ifdef LPASS8 /* Undo "pass8" if it were done */ if (lmodef) { if (ioctl(ttyfd,TIOCLSET,&lmode) < 0) debug(F100,"ttres TIOCLSET failed","",0); else debug(F100,"ttres TIOCLSET ok","",0); } #endif /* LPASS8 */ #ifdef CK_DTRCTS /* Undo hardware flow if it were done */ if (lmodef) { if (ioctl(ttyfd,TIOCLSET,&lmode) < 0) debug(F100,"ttres TIOCLSET failed","",0); else debug(F100,"ttres TIOCLSET ok","",0); } #endif /* CK_DTRCTS */ #ifdef TIOCGETC /* Put back special characters */ if (tcharf && (xlocal == 0)) { if (ioctl(ttyfd,TIOCSETC,&tchold) < 0) debug(F100,"ttres TIOCSETC failed","",0); else debug(F100,"ttres TIOCSETC ok","",0); } #endif /* TIOCGETC */ #ifdef TIOCGLTC /* Put back local special characters */ if (ltcharf && (xlocal == 0)) { if (ioctl(ttyfd,TIOCSLTC,<chold) < 0) debug(F100,"ttres TIOCSLTC failed","",0); else debug(F100,"ttres TIOCSLTC ok","",0); } #endif /* TIOCGLTC */ #ifdef BELLV10 debug(F100,"ttres BELLV10","",0); x = ioctl(ttyfd,TIOCSETP,&ttold); /* Restore both structs */ x = ioctl(ttyfd,TIOCSDEV,&tdold); #else debug(F100,"ttres stty","",0); x = stty(ttyfd,&ttold); /* Restore tty modes the old way. */ #endif /* BELLV10 */ if (!xlocal) msleep(100); /* This replaces sleep(1)... */ /* Put back sleep(1) if tty is */ /* messed up after close. */ #endif /* ATTSV */ #endif /* BSD44ORPOSIX */ debug(F101,"ttres result","",x); #ifndef QNX if (x < 0) debug(F101,"ttres errno","",errno); #endif /* QNX */ #ifdef AIXRS #ifndef AIX41 x = ioctl(ttyfd, ttld & 1 ? TXADDCD : TXDELCD, "rts"); debug(F101,"ttres AIX line discipline rts restore","",x); #endif /* AIX41 */ #endif /* AIXRS */ #ifdef BSD41 if (ttld > -1) { /* Put back line discipline */ x = ioctl(ttyfd, TIOCSETD, &ttld); debug(F101,"ttres BSD41 line discipline restore","",x); if (x < 0) debug(F101,"...ioctl errno","",errno); ttld = -1; } #endif /* BSD41 */ #ifdef sony_news x = xlocal ? km_ext : km_con; /* Restore Kanji mode. */ if (x != -1) { /* Make sure we know original modes. */ if (ioctl(ttyfd,TIOCKSET, &x) < 0) { perror("ttres can't set Kanji mode"); debug(F101,"ttres error setting Kanji mode","",x); return(-1); } } debug(F100,"ttres set Kanji mode ok","",0); #endif /* sony_news */ tvtflg = 0; /* Invalidate terminal mode settings */ debug(F101,"ttres return code","",x); return(x); } #ifndef NOUUCP /* T T C H K P I D -- Check lockfile pid */ /* Read pid from lockfile named f, check that it's still valid. If so, return 1. On failure to read pid, return 1. Otherwise, try to delete lockfile f and return 0 if successful, else 1. */ static int ttchkpid(f) char *f; { int pid, mypid, x; pid = ttrpid(f); /* Read pid from file. */ if (pid > -1) { /* If we were able to read the pid.. */ debug(F101,"ttchkpid lock pid","",pid); errno = 0; /* See if process still exists. */ mypid = (int)getpid(); /* Get my own pid. */ debug(F101,"ttchkpid my pid","",mypid); if (pid == mypid) { /* It's me! */ x = -1; /* So I can delete it */ errno = ESRCH; /* pretend it's invalid */ } else { /* It's not me */ x = kill((PID_T)pid, 0); /* See if it's a live process */ debug(F101,"ttchkpid kill errno","",errno); } debug(F101,"ttchkpid pid test","",x); if (x < 0 && errno == ESRCH) { /* pid is invalid */ debug(F111,"removing stale lock",f,pid); if (!backgrd) printf("Removing stale lock %s (pid %d terminated)\n", f, pid); priv_on(); x = unlink(f); /* Remove the lockfile. */ priv_off(); debug(F111,"ttchkpid unlink",f,x); if (x > -1) return(0); /* Device is not locked after all */ else if (!backgrd) perror(f); } return(1); } return(1); /* Failure to read pid */ } #ifdef HPUX /* Aliases (different drivers) for HP-UX dialout devices: */ static char *devprefix[] = { "tty", "ttyd", "cul", "cua", "cuad", "culd", "" }; static int ttydexists = 0; #endif /* HPUX */ /* T T R P I D -- Read pid from lockfile "name" */ static int ttrpid(name) char *name; { long len; int x, fd, pid; short spid; char buf[32]; debug(F110,"ttrpid",name,0); if (!name) return(-1); if (!*name) return(-1); priv_on(); len = zchki(name); /* Get file length */ priv_off(); debug(F101,"ttrpid zchki","",len); if (len < 0) return(-1); if (len > 31) return(-1); priv_on(); fd = open(name,O_RDONLY); /* Try to open lockfile. */ priv_off(); debug(F101,"ttrpid fd","",fd); if (fd <= 0) return(-1); /* Here we try to be flexible and allow for all different binary and string formats at runtime, rather than a specific format for each configuration hardwired at compile time. */ pid = -1; #ifndef COHERENT /* COHERENT uses a string PID but without leading spaces or 0's, so there is no way to tell from the file's length whether it contains a string or binary pid. So for COHERENT only, we only allow string pids. For all others, we decide based on the size of the lockfile. */ if (len > 4) { /* If file > 4 bytes it's a string */ #endif /* COHERENT */ x = read(fd,buf,(int)len); debug(F111,"ttrpid string read",buf,x); if (x < 0) { pid = -1; } else { buf[31] = '\0'; x = sscanf(buf,"%d",&pid); /* Get the integer pid from it. */ } #ifndef COHERENT } else if (len == 4) { /* 4 bytes so binary */ x = read(fd, (char *)&pid, 4); /* Read the bytes into an int */ debug(F101,"ttrpid integer read","",x); if (x < 4) pid = -1; } else if (len == 2) { /* 2 bytes binary */ x = read(fd, (char *)&spid, 2); /* Read the bytes into a short */ debug(F101,"ttrpid short read","",x); if (x < 2) pid = -1; else pid = spid; } else pid = -1; #endif /* COHERENT */ close(fd); /* Close the lockfile */ debug(F101,"ttrpid pid","",pid); return(pid); } #endif /* NOUUCP */ /* T T L O C K */ /* This function attempts to coordinate use of the communication device with other copies of Kermit and any other program that follows the UUCP device-locking conventions, which, unfortunately, vary among different UNIX implementations. The idea is to look for a file of a certain name, the "lockfile", in a certain directory. If such a file is found, then the line is presumed to be in use, and Kermit should not use it. If no such file is found, Kermit attempts to create one so that other programs will not use the same line at the same time. Because the lockfile and/or the directory it's in might lack write permission for the person running Kermit, Kermit could find itself running setuid to uucp or other user that does have the necessary permissions. At startup, Kermit has changed its effective uid to the user's real uid, and so ttlock() must switch back to the original effective uid in order to create the lockfile, and then back again to the real uid to prevent unauthorized access to other directories or files owned by the user the program is setuid to. Totally rewritten for C-Kermit 5A to eliminate windows of vulnerability, based on suggestions from Warren Tucker. Call with pointer to name of tty device. Returns: 0 on success -1 on failure Note: Once privileges are turned on using priv_on(), it is essential that they are turned off again before this function returns. */ #ifdef SVR4 /* Lockfile uses device numbers. */ /* Although I can't find this in writing anywhere (e.g. in SVID for SVR4), it is the behavior of the "reference version" of SVR4, i.e. the Intel port from UNIX Systems Laboratories, then called Univel UnixWare, then called Novell UnixWare, then called SCO Unixware, then called Caldera Open UNIX... It also makes much more sense than device-name-based lockfiles since there can be multiple names for the same device, symlinks, etc. */ #ifndef NOLFDEVNO #ifndef LFDEVNO /* Define this for SVR4 */ #ifndef AIXRS /* But not for RS/6000 AIX 3.2, etc. */ #ifndef BSD44 /* If anybody else needs it... */ #ifndef __386BSD__ #ifndef __FreeBSD__ #ifndef HPUX10 #ifndef IRIX51 /* SGI IRIX 5.1 or later */ #ifndef CK_SCOV5 /* SCO Open Server 5.0 */ #define LFDEVNO #endif /* CK_SCOV5 */ #endif /* IRIX51 */ #endif /* HPUX10 */ #endif /* __FreeBSD__ */ #endif /* __386BSD__ */ #endif /* BSD44 */ #endif /* AIXRS */ #endif /* LFDEVNO */ /* ... define it here or on CC */ #endif /* NOLFDEVNO */ #endif /* SVR4 */ /* command line. */ #ifdef COHERENT #define LFDEVNO #endif /* COHERENT */ /* For platforms where the lockfile name is made from device/major/minor device number, as in SVR4. Which, if we must have lockfiles at all, is by far the best format, since it eliminates all the confusion that stems from multiple names (or drivers) for the same port, not to mention symlinks. It might even be a good idea to start using this form even on platforms where it's not supported, alongside the normal forms for those platforms, in order to get people used to it... */ #ifdef LFDEVNO #ifndef major /* If we didn't find it */ #ifdef SVR4 /* then for Sys V R4 */ #include /* look here */ #else /* or for SunOS versions */ #ifdef SUNOS4 /* ... */ #include /* look here */ #else /* Otherwise take a chance: */ #define major(dev) ( (int) ( ((unsigned)(dev) >> 8) & 0xff)) #define minor(dev) ( (int) ( (dev) & 0xff)) #endif /* SUNOS4 */ #endif /* SVR4 */ #endif /* major */ #endif /* LFDEVNO */ /* No advisory locks if F_TLOCK and F_ULOCK are not defined at this point */ #ifdef LOCKF #ifndef F_TLOCK #undef LOCKF #ifndef NOLOCKF #define NOLOCKF #endif /* NOLOCKF */ #endif /* F_TLOCK */ #endif /* LOCKF */ #ifdef LOCKF #ifndef F_ULOCK #undef LOCKF #ifndef NOLOCKF #define NOLOCKF #endif /* NOLOCKF */ #endif /* F_ULOCK */ #endif /* LOCKF */ static char linkto[DEVNAMLEN+1]; static char * linkdev = NULL; #ifndef NOUUCP #ifdef USETTYLOCK #ifdef LOCK_DIR char * uucplockdir = LOCK_DIR; #else char * uucplockdir = ""; #endif /* LOCK_DIR */ #else #ifdef LOCK_DIR char * uucplockdir = LOCK_DIR; #else char * uucplockdir = ""; #endif /* LOCK_DIR */ #endif /* USETTYLOCK */ #else char * uucplockdir = ""; #endif /* NOUUCP */ #ifdef QNX /* Only for QNX4 */ int /* Visible to outside world */ qnxopencount() { /* Get QNX device open count */ struct _dev_info_entry info; int x; x = -1; /* Unknown */ if (ttyfd > -1) { if (!dev_info(ttyfd, &info)) { debug(F101,"ttlock QNX open_count","",info.open_count); x = info.open_count; } } return(x); } #endif /* QNX */ char * ttglckdir() { /* Get Lockfile directory name */ #ifdef __OpenBSD__ return("/var/spool/lock"); #else /* __OpenBSD__ */ #ifdef __FreeBSD__ return("/var/spool/lock"); #else /* __FreeBSD__ */ #ifdef LOCK_DIR char * s = LOCK_DIR; #endif /* LOCK_DIR */ #ifdef NOUUCP return(""); #else /* NOUUCP */ #ifdef LOCK_DIR return(s); #else /* LOCK_DIR */ return(""); #endif /* LOCK_DIR */ #endif /* NOUUCP */ #endif /* __FreeBSD__ */ #endif /* __OpenBSD__ */ } static int ttlock(ttdev) char *ttdev; { int x, n; int islink = 0; #ifdef __FreeBSD__ char *devname; #endif /* __FreeBSD__ */ #ifdef NOUUCP debug(F100,"ttlock NOUUCP","",0); ckstrncpy(flfnam,"NOLOCK",FLFNAML); haslock = 1; return(0); #else /* !NOUUCP */ #ifdef USETTYLOCK haslock = 0; /* Not locked yet. */ *flfnam = '\0'; /* Lockfile name is empty. */ #ifdef __FreeBSD__ if ((devname = xxlast(ttdev,'/')) != NULL) #ifdef FREEBSD8 ckstrncat(lockname,devname+1,DEVNAMLEN-ckstrncpy(lockname,"pts",4)); #else ckstrncpy(lockname,devname+1,DEVNAMLEN); #endif /* FREEBSD8 */ #else if (!strncmp(ttdev,"/dev/",5) && ttdev[5]) ckstrncpy(lockname,ttdev+5,DEVNAMLEN); #endif /* __FreeBSD__ */ else ckstrncpy(lockname,ttdev,DEVNAMLEN); /* This might be overkill, but it's not clear from the man pages whether ttylock() can be called without calling ttylocked() first, since the doc says that ttylocked() removes any stale lockfiles, but it does not say this about ttylock(). Also the docs don't say what ttylocked() returns in the case when it finds and removes a stale lockfile. So one or both calls to to ttylocked() might be superfluous, but they should do no harm. Also I'm assuming that we have to do all the same ID swapping, etc, with these routines as we do without them. Thus the priv_on/off() sandwich. */ #ifdef USE_UU_LOCK priv_on(); /* Turn on privs */ x = uu_lock(lockname); /* Try to set the lock */ priv_off(); /* Turn privs off */ debug(F111,"ttlock uu_lock",lockname,x); switch (x) { case UU_LOCK_INUSE: return(-2); case UU_LOCK_OK: #ifdef BSD44 ckmakmsg(flfnam,FLFNAML,"/var/spool/lock/LCK..",lockname,NULL,NULL); #endif /* BSD44 */ haslock = 1; return(0); default: return(-1); } #else /* USE_UU_LOCK */ priv_on(); /* Turn on privs */ if (ttylocked(lockname)) { /* This should remove any stale lock */ if (ttylocked(lockname)) { /* so check again. */ priv_off(); return(-5); /* Still locked, fail. */ } } x = ttylock(lockname); /* Lock it. */ priv_off(); /* Turn off privs */ debug(F111,"ttlock lockname",lockname,x); if (x > -1) { /* We don't really know the name of the lockfile, but this is what the man page says it is. In USETTYLOCK builds, it is used only for display by SHOW COMM. */ ckmakmsg(flfnam,FLFNAML,"/etc/locks/LCK..",lockname,NULL,NULL); haslock = 1; } return(x); #endif /* USE_UU_LOCK */ #else /* Systems that don't have ttylock()... */ #ifndef HPUX int lockfd; /* File descriptor for lock file. */ PID_T pid; /* Process id of this process. */ int tries; /* How many times we've tried... */ struct stat devbuf; /* For device numbers (SVR4). */ #ifdef PIDSTRING char pid_str[32]; /* My pid in string format. */ #endif /* PIDSTRING */ char *device, *devname; #define LFNAML 256 /* Max length for lock file name. */ char lockfil[LFNAML]; /* Lock file name */ #ifdef RTAIX char lklockf[LFNAML]; /* Name for link to lock file */ #endif /* RTAIX */ #ifdef CKSYMLINK char symlock[LFNAML]; /* Name for symlink lockfile name */ #endif /* CKSYMLINK */ char tmpnam[LFNAML+30]; /* Temporary lockfile name. */ char *lockdir = LOCK_DIR; /* Defined near top of this file, */ /* or on cc command line. */ haslock = 0; /* Not locked yet. */ *flfnam = '\0'; /* Lockfile name is empty. */ lock2[0] = '\0'; /* Clear secondary lockfile name. */ pid = getpid(); /* Get id of this process. */ /* Construct name of lockfile and temporary file */ /* device = name of tty device without the path, e.g. "ttyh8" */ /* lockfil = name of lock file, without path, e.g. "LCK..ttyh8" */ device = ((devname = xxlast(ttdev,'/')) != NULL ? devname+1 : ttdev); if (stat(ttdev,&devbuf) < 0) return(-1); #ifdef CKSYMLINK islink = 1; /* Assume it's a symlink */ linkto[0] = '\0'; /* But we don't know to what */ #ifdef COMMENT /* This is undependable. If it worked it would save the readlink call if we knew the device name was not a link. */ #ifdef S_ISLNK islink = S_ISLNK(devbuf.st_mode); debug(F101,"ttlock stat S_ISLNK","",islink); #endif /* S_ISLNK */ #endif /* COMMENT */ if (islink) { n = readlink(ttdev,linkto,DEVNAMLEN); /* See if it's a link */ debug(F111,"ttlock readlink",ttdev,n); if (n > -1) /* It is */ linkto[n] = '\0'; else /* It's not */ islink = 0; debug(F111,"ttlock link",linkto,islink); } if (islink) { linkdev = (devname = xxlast(linkto,'/')) ? devname + 1 : linkto; debug(F110,"ttlock linkdev",linkdev,0); } #endif /* CKSYMLINK */ /* On SCO platforms, if we don't have a symlink, then let's pretend the name given for the device is a symlink, because later we will change the name if it contains any uppercase characters. */ #ifdef CK_SCOV5 /* SCO Open Server 5.0 */ if (!islink) { islink = 1; ckstrncpy(linkto,ttdev,DEVNAMLEN); linkdev = (devname = xxlast(linkto,'/')) ? devname + 1 : linkto; debug(F110,"ttlock linkdev",linkdev,0); } #else #ifdef M_XENIX /* SCO Xenix or UNIX */ if (!islink) { islink = 1; ckstrncpy(linkto,ttdev,DEVNAMLEN); linkdev = (devname = xxlast(linkto,'/')) ? devname + 1 : linkto; debug(F110,"ttlock linkdev",linkdev,0); } #endif /* M_XENIX */ #endif /* CK_SCOV5 */ #ifdef ISIII /* Interactive System III, PC/IX */ ckstrncpy(lockfil, device, DEVNAMLEN); #else /* not ISIII */ #ifdef LFDEVNO /* Lockfilename has device numbers. */ #ifdef COHERENT sprintf(lockfil,"LCK..%d.%d", /* SAFE */ major(devbuf.st_rdev), /* major device number */ 0x1f & minor(devbuf.st_rdev)); /* minor device number */ #else /* Note: %d changed to %u in 8.0 -- %u is part of SVID for SVR4 */ /* Lockfile name format verified to agree with Solaris cu, Dec 2001 */ sprintf(lockfil,"LK.%03u.%03u.%03u", /* SAFE */ major(devbuf.st_dev), /* device */ major(devbuf.st_rdev), /* major device number */ minor(devbuf.st_rdev)); /* minor device number */ #endif /* COHERENT */ #else /* Not LFDEVNO */ #ifdef PTX /* Dynix PTX */ if ((device != &ttdev[5]) && (strncmp(ttdev,"/dev/",5) == 0)) { if ((int)strlen(device) + 8 < LFNAML) sprintf(lockfil,"LCK..%.3s%s", &ttdev[5], device); else ckstrncpy(lockfil,"LOCKFILE_NAME_TOO_LONG",LFNAML); } else #endif /* PTX */ if ((int)strlen(device) + 5 < LFNAML) sprintf(lockfil,"LCK..%s", device); else ckstrncpy(lockfil,"LOCKFILE_NAME_TOO_LONG",LFNAML); #ifdef RTAIX ckstrncpy(lklockf,device,DEVNAMLEN); #endif /* RTAIX */ #ifdef CKSYMLINK symlock[0] = '\0'; if (islink) ckmakmsg(symlock,LFNAML, "LCK..", linkdev, NULL, NULL); #endif /* CKSYMLINK */ #endif /* LFDEVNO */ #endif /* ISIII */ #ifdef CK_SCOV5 /* SCO Open Server 5.0 */ { /* Lowercase the entire filename. */ /* SCO says we must do this in V5.0 and later. */ /* BUT... watch out for devices -- like Digiboard Portserver */ /* That can have hundreds of ports... */ char *p = (char *)(lockfil + 5); while (*p) { if (isupper(*p)) *p = (char) tolower(*p); p++; } } #ifdef CKSYMLINK if (islink) { /* If no change */ if (!strcmp(lockfil,symlock)) { /* then no second lockfile needed */ islink = 0; symlock[0] = '\0'; } } #endif /* CKSYMLINK */ #else #ifdef M_XENIX /* SCO Xenix or UNIX */ { int x; char c; x = (int)strlen(lockfil) - 1; /* Get last letter of device name. */ if (x > 0) { /* If it's uppercase, lower it. */ c = lockfil[x]; if (c >= 'A' && c <= 'Z') lockfil[x] += ('a' - 'A'); } } #ifdef CKSYMLINK if (islink) { if (!strcmp(lockfil,symlock)) { /* No change */ islink = 0; /* so no second lockfile */ symlock[0] = '\0'; } } #endif /* CKSYMLINK */ #endif /* M_XENIX */ #endif /* CK_SCOV5 */ /* flfnam = full lockfile pathname, e.g. "/usr/spool/uucp/LCK..ttyh8" */ /* tmpnam = temporary unique, e.g. "/usr/spool/uucp/LTMP..pid" */ ckmakmsg(flfnam,LFNAML,lockdir,"/",lockfil,NULL); #ifdef RTAIX ckmakmsg(lkflfn,FLFNAML,lockdir,"/",lklockf,NULL); #endif /* RTAIX */ #ifndef LFDEVNO #ifdef CKSYMLINK /* If it's a link then also make a lockfile for the real name */ debug(F111,"ttlock link symlock",symlock,islink); if (islink && symlock[0]) { /* But only if the lockfile names would be different. */ /* WARNING: They won't be, e.g. for /dev/ttyd2 => /hw/ttys/ttyd2 */ ckmakmsg(lock2,FLFNAML,lockdir,"/",symlock,NULL); debug(F110,"ttlock lock2",lock2,0); if (!strcmp(lock2,flfnam)) { /* Are lockfile names the same? */ debug(F100,"ttlock lock2 cleared","",0); lock2[0] = '\0'; /* Clear secondary lockfile name. */ } } #endif /* CKSYMLINK */ #endif /* LFDEVNO */ sprintf(tmpnam,"%s/LTMP.%05d",lockdir,(int) pid); /* safe */ debug(F110,"ttlock flfnam",flfnam,0); debug(F110,"ttlock tmpnam",tmpnam,0); priv_on(); /* Turn on privileges if possible. */ lockfd = creat(tmpnam, 0444); /* Try to create temp lock file. */ if (lockfd < 0) { /* Create failed. */ debug(F111,"ttlock creat failed",tmpnam,errno); if (errno == ENOENT) { perror(lockdir); printf("UUCP not installed or Kermit misconfigured\n"); } else { if (!quiet) perror(lockdir); unlink(tmpnam); /* Get rid of the temporary file. */ } priv_off(); /* Turn off privileges!!! */ return(-1); /* Return failure code. */ } /* Now write the pid into the temp lockfile in the appropriate format */ #ifdef PIDSTRING /* For Honey DanBer UUCP, */ sprintf( /* write PID as decimal string */ pid_str, #ifdef LINUXFSSTND /* The "Linux File System Standard" */ #ifdef FSSTND10 /* Version 1.0 calls for */ "%010d\n", /* leading zeros */ #else /* while version 1.2 calls for */ "%10d\n", /* leading spaces */ #endif /* FSSTND10 */ #else #ifdef COHERENT "%d\n", /* with leading nothing */ #else "%10d\n", /* with leading blanks */ #endif /* COHERENT */ #endif /* LINUXFSSTND */ (int) pid ); /* safe */ write(lockfd, pid_str, 11); debug(F111,"ttlock hdb pid string",pid_str,(int) pid); #else /* Not PIDSTRING, use integer PID */ write(lockfd, (char *)&pid, sizeof(pid) ); debug(F101,"ttlock pid","",(int) pid); #endif /* PIDSTRING */ /* Now try to rename the temp file to the real lock file name. */ /* This will fail if a lock file of that name already exists. */ close(lockfd); /* Close the temp lockfile. */ chmod(tmpnam,0444); /* Permission for a valid lock. */ tries = 0; while (!haslock && tries++ < 2) { haslock = (link(tmpnam,flfnam) == 0); /* Create a link to it. */ if (haslock) { /* If we got the lockfile */ #ifdef RTAIX link(flfnam,lkflfn); #endif /* RTAIX */ #ifdef CKSYMLINK #ifndef LFDEVNO if (islink && lock2[0]) link(flfnam,lock2); #endif /* LFDEVNO */ #endif /* CKSYMLINK */ #ifdef COMMENT /* Can't do this any more because device is not open yet so no ttyfd. */ #ifdef LOCKF /* Advisory file locking works on SVR4, so we use it. In fact, it is necessary in some cases, e.g. when SLIP is involved. But it still doesn't seem to prevent multiple users accessing the same device by different names. */ while (lockf(ttyfd, F_TLOCK, 0L) != 0) { debug(F111, "ttlock lockf returns errno", "", errno); if ((++tries >= 3) || (errno != EAGAIN)) { x = unlink(flfnam); /* remove the lockfile */ #ifdef RTAIX unlink(lkflfn); /* And any links to it... */ #endif /* RTAIX */ #ifdef CKSYMLINK #ifndef LFDEVNO if (islink && lock2[0]) unlink(lock2); /* ditto... */ #endif /* LFDEVNO */ #endif /* CKSYMLINK */ debug(F111,"ttlock unlink",flfnam,x); haslock = 0; break; } sleep(2); } if (haslock) /* If we got an advisory lock */ #endif /* LOCKF */ #endif /* COMMENT */ break; /* We're done. */ } else { /* We didn't create a new lockfile. */ priv_off(); if (ttchkpid(flfnam)) { /* Check existing lockfile */ priv_on(); /* cause ttchkpid turns priv_off... */ unlink(tmpnam); /* Delete the tempfile */ debug(F100,"ttlock found tty locked","",0); priv_off(); /* Turn off privs */ return(-2); /* Code for device is in use. */ } priv_on(); } } unlink(tmpnam); /* Unlink (remove) the temp file. */ priv_off(); /* Turn off privs */ return(haslock ? 0 : -1); /* Return link's return code. */ #else /* HPUX */ /* HP-UX gets its own copy of this routine, modeled after the observed behavior of the HP-UX 'cu' program. HP-UX serial device names consist of a base name such as "tty", "ttyd", "cua", "cul", "cuad", or "culd", followed by a unit designator which is a string of digits, possibly containing an imbedded letter "p". Examples (for base name "tty"): /dev/tty0, /dev/tty00, dev/ttyd00, /dev/tty0p0 According to the HP-UX UUCP manual of 1988, the "0p0" notation has been used on Series 800 since HP-UX 2.00, and the "non-p" notation was used on other models. In HP-UX 10.00, "0p0" notation was adopted for all models. However, we make and enforce no such distinctions; either notation is accepted on any model or HP-UX version as a valid unit designator. If a valid unit is specified (as opposed to a designer name or symlink), we check for all aliases of the given unit according to the devprefix[] array. If no lockfiles are found for the given unit, we can have the device; we create a lockfile LCK..name in the lockfile directory appropriate for the HP-UX version (/var/spool/locks for 10.00 and later, /usr/spool/uucp for 9.xx and earlier). If it is a "cua" or "cul" device, a second lockfile is created with the "ttyd" prefix. This is exactly what cu does. If the "set line" device does not have a valid unit designator, then it is used literally and no synomyms are searched for and only one lockfile is created. -fdc, March 1998. */ #define LFNAML 80 /* Max length for lock file name. */ int lockfd; /* File descriptor for lock file. */ PID_T pid; /* Process ID of this process. */ int fpid; /* pid found in existing lockfile. */ int tries; /* How many times we've tried... */ int i, k; /* Workers */ char *device, *devname; /* "/dev/xxx", "xxx" */ char *unit, *p; /* p part of xxx */ char lockfil[LFNAML]; /* Lockfile name (no path) */ char tmpnam[LFNAML]; /* Temporary lockfile name. */ #ifdef HPUX10 /* Lockfile directory */ char *lockdir = "/var/spool/locks"; /* Always this for 10.00 and higher */ #else /* HP-UX 9.xx and below */ #ifdef LOCK_DIR char *lockdir = LOCK_DIR; /* Defined near top of this file */ #else char *lockdir = "/usr/spool/uucp"; /* or not... */ #endif /* LOCK_DIR */ #endif /* HPUX10 */ haslock = 0; /* Not locked yet. */ *flfnam = '\0'; /* Lockfile name is empty. */ lock2[0] = '\0'; /* Second one too. */ pid = getpid(); /* Get my process ID */ /* Construct name of lockfile and temporary file... device = name of tty device without the path, e.g. "tty0p0" lockfil = name of lock file, without path, e.g. "LCK..tty0p0" */ device = ((devname = xxlast(ttdev,'/')) != NULL ? devname+1 : ttdev); debug(F110,"TTLOCK device",device,0); ckmakmsg(lockfil,LFNAML,"LCK..",device,NULL,NULL); k = 0; /* Assume device is not locked */ n = 0; /* Digit counter */ unit = device; /* Unit = p */ while (*unit && !isdigit(*unit)) /* Search for digit... */ unit++; p = unit; /* Verify p format... */ debug(F110,"TTLOCK unit 1",unit,0); /* The unit number is recognized as: (a) any sequence of digits that runs to the end of the string. (b) any (a) that includes one and only one letter "p", with at least one digit before and after it. */ while (isdigit(*p)) p++, n++; /* Get a run of digits */ if (*p && n > 0) { /* Have a "p"? */ if (*p == 'p' && isdigit(*(p+1))) { p++; n = 0; while (isdigit(*p)) p++, n++; } } if (n == 0 || *p) unit = ""; debug(F110,"TTLOCK unit 2",unit,0); if (*unit) { /* Device name has unit number. */ /* The following loop not only searches for the various lockfile */ /* synonyms, but also removes all -- not just one -- stale lockfile */ /* for the device, should there be more than one. See ttchkpid(). */ ttydexists = 0; for (i = 0; *devprefix[i]; i++) { /* For each driver... */ /* Make device name */ ckmakmsg(lock2,FLFNAML,"/dev/",devprefix[i],unit,NULL); priv_on(); /* Privs on */ k = zchki(lock2) != -1; /* See if device exists */ priv_off(); /* Privs off */ debug(F111,"TTLOCK exist",lock2,k); if (k) { if (!strcmp(devprefix[i],"ttyd")) /* ttyd device exists */ ttydexists = 1; /* Make lockfile name */ ckmakmsg(lock2,FLFNAML,lockdir,"/LCK..",devprefix[i],unit); debug(F110,"TTLOCK checking",lock2,0); priv_on(); /* Privs on */ k = zchki(lock2) != -1; /* See if lockfile exists */ priv_off(); /* Privs off */ debug(F111,"TTLOCK check for lock A",lock2,k); if (k) if (ttchkpid(lock2)) { /* If pid still active, fail. */ ckstrncpy(flfnam,lock2,FLFNAML); return(-2); } } } } else { /* Some other device-name format */ /* This takes care of symbolic links, etc... */ /* But does not chase them down! */ ckmakmsg(lock2,FLFNAML,lockdir,"/LCK..",device,NULL); priv_on(); k = zchki(lock2) != -1; /* Check for existing lockfile */ priv_off(); debug(F111,"TTLOCK check for lock B",lock2,k); if (k) if (ttchkpid(lock2)) { /* Check pid from lockfile */ ckstrncpy(flfnam,lock2,FLFNAML); debug(F110,"TTLOCK in use",device,0); debug(F101,"TTLOCK returns","",-2); return(-2); } } /* Get here only if there is no (more) lockfile, so now we make one (or two)... flfnam = full lockfile pathname, e.g. "/usr/spool/uucp/LCK..cul0p0". tmpnam = unique temporary filname, e.g. "/usr/spool/uucp/LTMP..pid". */ ckmakmsg(flfnam,FLFNAML,lockdir,"/",lockfil,NULL); /* SET LINE device */ /* If dialout device, also make one for corresponding dialin device */ lock2[0] = '\0'; if (!strncmp(device,"cu",2) && *unit && ttydexists) ckmakmsg(lock2,FLFNAML,lockdir,"/LCK..ttyd",unit,NULL); if ((int)strlen(lockdir)+12 < LFNAML) sprintf(tmpnam,"%s/LTMP.%05d",lockdir,(int) pid); /* Make temp name */ #ifdef DEBUG if (deblog) { debug(F110,"TTLOCK flfnam",flfnam,0); debug(F110,"TTLOCK lock2",lock2,0); debug(F110,"TTLOCK tmpnam",tmpnam,0); } #endif /* DEBUG */ /* Lockfile permissions... 444 is standard, HP-UX 10.00 uses 664. It doesn't matter. Kermit uses 444; the difference lets us tell whether Kermit created the lock file. */ priv_on(); /* Turn on privileges. */ lockfd = creat(tmpnam, 0444); /* Try to create temporary file. */ if (lockfd < 0) { /* Create failed. */ debug(F111,"TTLOCK creat failed",tmpnam,errno); if (errno == ENOENT) { perror(lockdir); printf("UUCP not installed or Kermit misconfigured\n"); } else { if (!quiet) perror(lockdir); unlink(tmpnam); /* Get rid of the temporary file. */ } priv_off(); /* Turn off privileges!!! */ debug(F101,"TTLOCK returns","",-1); return(-1); /* Return failure code. */ } debug(F110,"TTLOCK temp ok",tmpnam,0); /* Now write our pid into the temp lockfile in integer format. */ i = write(lockfd, (char *)&pid, sizeof(pid)); #ifdef DEBUG if (deblog) { debug(F101,"TTLOCK pid","",pid); debug(F101,"TTLOCK sizeof pid","",sizeof(pid)); debug(F101,"TTLOCK write pid returns","",i); } #endif /* DEBUG */ /* Now try to rename the temporary file to the real lockfile name. This will fail if a lock file of that name already exists, which will catch race conditions with other users. */ close(lockfd); /* Close the temp lockfile. */ chmod(tmpnam,0444); tries = 0; while (!haslock && tries++ < 2) { haslock = (link(tmpnam,flfnam) == 0); /* Create a link to it. */ debug(F101,"TTLOCK link","",haslock); if (haslock) { /* If we made the lockfile... */ #ifdef COMMENT /* We can't do this any more because we don't have a file descriptor yet. */ #ifdef LOCKF /* Can be canceled with -DNOLOCKF */ /* Create an advisory lock on the device through its file descriptor. This code actually seems to work. If it is executed, and then another process tries to open the same device under a different name to circumvent the lockfile, they get a "device busy" error. */ debug(F100,"TTLOCK LOCKF code...","",0); while ( lockf(ttyfd, F_TLOCK, 0L) != 0 ) { debug(F111, "TTLOCK lockf error", "", errno); if ((++tries >= 3) || (errno != EAGAIN)) { x = unlink(flfnam); /* Remove the lockfile */ if (errno == EACCES && !quiet) printf("Device already locked by another process\n"); haslock = 0; break; } sleep(2); } #endif /* LOCKF */ #endif /* COMMENT */ if (haslock) { /* If we made the lockfile ... */ if (lock2[0]) { /* if there is to be a 2nd lockfile */ lockfd = creat(lock2, 0444); /* Create it */ debug(F111,"TTLOCK lock2 creat", lock2, lockfd); if (lockfd > -1) { /* Created OK, write pid. */ write(lockfd, (char *)&pid, sizeof(pid) ); close(lockfd); /* Close and */ chmod(lock2, 0444); /* set permissions. */ } else { /* Not OK, but don't fail. */ lock2[0] = '\0'; /* Just remember it's not there. */ } } break; /* and we're done. */ } } } unlink(tmpnam); /* Unlink (remove) the temp file. */ priv_off(); /* Turn off privs */ i = haslock ? 0 : -1; /* Our return value */ debug(F101,"TTLOCK returns","",i); return(i); #endif /* HPUX */ #endif /* USETTYLOCK */ #endif /* !NOUUCP */ } /* T T U N L O C K */ static int ttunlck() { /* Remove UUCP lockfile(s). */ #ifndef NOUUCP int x; debug(F111,"ttunlck",flfnam,haslock); #ifdef USETTYLOCK if (haslock && *flfnam) { int x; priv_on(); /* Turn on privs */ #ifdef USE_UU_LOCK x = uu_unlock(lockname); #else /* USE_UU_LOCK */ x = ttyunlock(lockname); /* Try to unlock */ #endif /* USE_UU_LOCK */ priv_off(); /* Turn off privs */ if (x < 0 && !quiet) printf("Warning - Can't remove lockfile: %s\n", flfnam); *flfnam = '\0'; /* Erase the name. */ haslock = 0; return(0); } #else /* No ttylock()... */ if (haslock && *flfnam) { /* Don't remove lockfile if we didn't make it ourselves */ if ((x = ttrpid(flfnam)) != (int)getpid()) { debug(F111,"ttunlck lockfile seized",flfnam,x); printf("Warning - Lockfile %s seized by pid %d\n", flfnam, x ); return(0); } priv_on(); /* Turn privileges on. */ errno = 0; x = unlink(flfnam); /* Remove the lockfile. */ debug(F111,"ttunlck unlink",flfnam,x); if (x < 0) { if (errno && !quiet) perror(ttnmsv); printf("Warning - Can't remove lockfile: %s\n", flfnam); } haslock = 0; *flfnam = '\0'; /* Erase the name. */ #ifdef RTAIX errno = 0; x = unlink(lkflfn); /* Remove link to lockfile */ debug(F111,"ttunlck AIX link unlink",lkflfn,x); if (x < 0) { if (errno && !quiet) perror(ttnmsv); printf("Warning - Can't remove link to lockfile: %s\n", lkflfn); } *lkflfn = '\0'; #else if (lock2[0]) { /* If there is a second lockfile, */ errno = 0; x = unlink(lock2); /* remove it too. */ debug(F111,"ttunlck lock2 unlink",lock2,x); if (x < 0) { if (errno && !quiet) perror(ttnmsv); printf("Warning - Can't remove secondary lockfile: %s\n", lock2 ); } lock2[0] = '\0'; /* Forget its name. */ } #endif /* RTAIX */ #ifdef COMMENT #ifdef LOCKF (VOID) lockf(ttyfd, F_ULOCK, 0L); /* Remove advisory lock */ #endif /* LOCKF */ #endif /* COMMENT */ priv_off(); /* Turn privileges off. */ } #endif /* USETTYLOCK */ #endif /* !NOUUCP */ return(0); } /* 4.3BSD-style UUCP line direction control. (Stan Barber, Rice U, 1980-something...) */ #ifndef NOUUCP #ifdef ACUCNTRL VOID acucntrl(flag,ttname) char *flag, *ttname; { char x[DEVNAMLEN+32], *device, *devname; if (strcmp(ttname,CTTNAM) == 0 || xlocal == 0) /* If not local, */ return; /* just return. */ device = ((devname = xxlast(ttname,'/')) != NULL ? devname+1 : ttname); if (strncmp(device,"LCK..",4) == 0) device += 5; ckmakmsg(x,DEVNAMLEN+32,"/usr/lib/uucp/acucntrl ",flag," ",device); debug(F110,"called ",x,0); zsyscmd(x); } #endif /* ACUCNTRL */ #endif /* NOUUCP */ /* T T H F L O W -- Set or Reset hardware flow control. This is an attempt to collect all hardware-flow-control related code into a single module. Thanks to Rick Sladkey and John Kohl for lots of help here. Overview: Hardware flow control is not supported in many UNIX implementions. Even when it is supported, there is no (ha ha) "standard" for the programming interface. In general, 4.3BSD and earlier (sometimes), 4.4BSD, System V, SunOS, AIX, etc, have totally different methods. (And, not strictly relevant here, the programming interface often brings one only to a no-op in the device driver!) Among all these, we have two major types of APIs: those in which hardware flow control is determined by bits in the same termio/termios/sgtty mode word(s) that are used for controlling such items as CBREAK vs RAW mode, and which are also used by the ttvt(), ttpkt(), conbin(), and concb() routines for changing terminal modes. And those that use entirely different mechanisms. In the first category, it is important that any change in the mode bits be reflected in the relevant termio(s)/sgtty structure, so that subsequent changes to that structure do not wipe out the effects of this routine. That is why a pointer, attrs, to the appropriate structure is passed as a parameter to this routine. The second category should give us no worries, since any changes to hardware flow control accomplished by this routine should not affect the termio(s)/ sgtty structures, and therefore will not be undone by later changes to them. The second argument, status, means to turn on hardware flow control if nonzero, and to turn it off if zero. Returns: 0 on apparent success, -1 on probable failure. */ /* The following business is for BSDI, where it was discovered that two separate bits, CCTS_OFLOW and CRTS_IFLOW, are used in hardware flow control, but CTRSCTS is defined (in ) to be just CCTS_OFLOW rather both bits, so hwfc only works in one direction if you use CRTSCTS to control it. Other 4.4BSD-based Unixes such as FreeBSD 4.1, which use these two bits, define CRTSCTS correctly. */ #ifdef FIXCRTSCTS #ifdef CRTSCTS #ifdef CCTS_OFLOW #ifdef CRTS_IFLOW #undef CRTSCTS #define CRTSCTS (CRTS_IFLOW|CCTS_OFLOW) #endif /* CRTS_IFLOW */ #endif /* CCTS_OFLOW */ #endif /* CRTSCTS */ #endif /* FIXCRTSCTS */ static int tthflow(flow, status, attrs) int flow, /* Type of flow control (ckcdeb.h) */ status; /* Nonzero = turn it on */ /* Zero = turn it off */ #ifdef BSD44ORPOSIX /* POSIX or BSD44 */ struct termios *attrs; #else /* System V */ #ifdef ATTSV #ifdef ATT7300 #ifdef UNIX351M /* AT&T UNIX 3.51m can set but not test for hardware flow control */ #define RTSFLOW CTSCD #define CTSFLOW CTSCD #endif /* ATT7300 */ #endif /* UNIX351M */ struct termio *attrs; #else /* BSD, V7, etc */ struct sgttyb *attrs; /* sgtty info... */ #endif /* ATTSV */ #endif /* BSD44ORPOSIX */ /* tthflow */ { int x = 0; /* tthflow() return code */ #ifdef Plan9 return p9tthflow(flow, status); #else #ifndef OXOS /* NOT Olivetti X/OS... */ /* For SunOS 4.0 and later in the BSD environment ... The declarations are copied and interpreted from the System V header files, so we don't actually have to pull in all the System V junk when building C-Kermit for SunOS in the BSD environment, which would be dangerous because having those symbols defined would cause us to take the wrong paths through the code. The code in this section is used in both the BSD and Sys V SunOS versions. */ #ifdef SUNOS41 /* In SunOS 4.1 and later, we use the POSIX calls rather than ioctl calls because GNU CC uses different formats for the _IOxxx macros than regular CC; the POSIX forms work for both. But the POSIX calls are not available in SunOS 4.0. */ #define CRTSCTS 0x80000000 /* RTS/CTS flow control */ #define TCSANOW 0 /* Do it now */ struct termios { unsigned long c_iflag; /* Input modes */ unsigned long c_oflag; /* Output modes */ unsigned long c_cflag; /* Control modes */ unsigned long c_lflag; /* Line discipline modes */ char c_line; CHAR c_cc[17]; }; struct termios temp; _PROTOTYP( int tcgetattr, (int, struct termios *) ); _PROTOTYP( int tcsetattr, (int, int, struct termios *) ); /* When CRTSCTS is set, SunOS won't do output unless both CTS and CD are asserted. So we don't set CRTSCTS unless CD is up. This should be OK, since we don't need RTS/CTS during dialing, and after dialing is complete, we should have CD. If not, we still communicate, but without RTS/CTS. */ int mflags; /* Modem signal flags */ #ifdef NETCMD if (ttpipe) return(0); #endif /* NETCMD */ #ifdef NETPTY if (ttpty) return(0); #endif /* NETPTY */ debug(F101,"tthflow SUNOS41 entry status","",status); if (!status) { /* Turn hard flow off */ if (tcgetattr(ttyfd, &temp) > -1 && /* Get device attributes */ (temp.c_cflag & CRTSCTS)) { /* Check for RTS/CTS */ temp.c_cflag &= ~CRTSCTS; /* It's there, remove it */ x = tcsetattr(ttyfd,TCSANOW,&temp); } } else { /* Turn hard flow on */ if (ioctl(ttyfd,TIOCMGET,&mflags) > -1 && /* Get modem signals */ (mflags & TIOCM_CAR)) { /* Check for CD */ debug(F100,"tthflow SunOS has CD","",0); if (tcgetattr(ttyfd, &temp) > -1 && /* Get device attributes */ !(temp.c_cflag & CRTSCTS)) { /* Check for RTS/CTS */ temp.c_cflag |= CRTSCTS; /* Not there, add it */ x = tcsetattr(ttyfd,TCSANOW,&temp); } } else { x = -1; debug(F100,"tthflow SunOS no CD","",0); } } #else #ifdef QNX struct termios temp; #ifdef NETCMD if (ttpipe) return(0); #endif /* NETCMD */ #ifdef NETPTY if (ttpty) return(0); #endif /* NETPTY */ debug(F101,"tthflow QNX entry status","",status); if (tcgetattr(ttyfd, &temp) > -1) { /* Get device attributes */ if (!status) { /* Turn hard flow off */ if ((temp.c_cflag & (IHFLOW|OHFLOW)) == (IHFLOW|OHFLOW)) { temp.c_cflag &= ~(IHFLOW|OHFLOW); /* It's there, remove it */ attrs->c_cflag &= ~(IHFLOW|OHFLOW); x = tcsetattr(ttyfd,TCSANOW,&temp); } } else { /* Turn hard flow on */ if ((temp.c_cflag & (IHFLOW|OHFLOW)) != (IHFLOW|OHFLOW)) { temp.c_cflag |= (IHFLOW|OHFLOW); /* Not there, add it */ temp.c_iflag &= ~(IXON|IXOFF); /* Bye to IXON/IXOFF */ ttraw.c_lflag |= IEXTEN; /* Must be on */ x = tcsetattr(ttyfd,TCSANOW,&temp); attrs->c_cflag |= (IHFLOW|OHFLOW); attrs->c_iflag &= ~(IXON|IXOFF); } } } else { x = -1; debug(F100, "tthflow QNX getattr fails", "", 0); } #else #ifdef POSIX_CRTSCTS /* POSIX_CRTSCTS is defined in ckcdeb.h or on CC command line. Note: Do not assume CRTSCTS is a one-bit field! */ struct termios temp; #ifdef NETCMD if (ttpipe) return(0); #endif /* NETCMD */ #ifdef NETPTY if (ttpty) return(0); #endif /* NETPTY */ debug(F101,"tthflow POSIX_CRTSCTS entry status","",status); errno = 0; x = tcgetattr(ttyfd, &temp); debug(F111,"tthflow POSIX_CRTSCTS tcgetattr",ckitoa(x),errno); errno = 0; if (x < 0) { x = -1; } else { if (!status) { /* Turn hard flow off */ if ( #ifdef COMMENT /* This can fail because of sign extension */ /* e.g. in Linux where it's Bit 31 */ (temp.c_cflag & CRTSCTS) == CRTSCTS #else (temp.c_cflag & CRTSCTS) != 0 #endif /* COMMENT */ ) { temp.c_cflag &= ~CRTSCTS; /* It's there, remove it */ attrs->c_cflag &= ~CRTSCTS; x = tcsetattr(ttyfd,TCSANOW,&temp); debug(F111,"tthflow POSIX_CRTSCTS OFF tcsetattr", ckitoa(x),errno); } else { /* John Dunlap 2010-01-26 */ debug(F001, "tthflow before forcing off attrs CRTSCTS", "", attrs->c_cflag&CRTSCTS ); attrs->c_cflag &= ~CRTSCTS; /* force it off if !status */ debug(F001, "tthflow after forcing off attrs CRTSCTS", "", attrs->c_cflag&CRTSCTS ); } } else { /* Turn hard flow on */ if ( #ifdef COMMENT /* This can fail because of sign extension */ (temp.c_cflag & CRTSCTS) != CRTSCTS #else (temp.c_cflag & CRTSCTS) == 0 #endif /* COMMENT */ ) { temp.c_cflag |= CRTSCTS; /* Not there, add it */ temp.c_iflag &= ~(IXON|IXOFF|IXANY); /* Bye to IXON/IXOFF */ x = tcsetattr(ttyfd,TCSANOW,&temp); debug(F111,"tthflow POSIX_CRTSCTS ON tcsetattr", ckitoa(x),errno); attrs->c_cflag |= CRTSCTS; attrs->c_iflag &= ~(IXON|IXOFF|IXANY); } } } #else #ifdef SUNOS4 /* SunOS 4.0 (and maybe earlier?). This code is dangerous because it prevents compilation with GNU gcc, which uses different formats for the _IORxxx macros than regular cc. SunOS 4.1 and later can use the POSIX routines above, which work for both cc and gcc. */ #define TCGETS _IOR(T, 8, struct termios) /* Get modes into termios struct */ #define TCSETS _IOW(T, 9, struct termios) /* Set modes from termios struct */ #define CRTSCTS 0x80000000 /* RTS/CTS flow control */ struct termios { unsigned long c_iflag; /* Input modes */ unsigned long c_oflag; /* Output modes */ unsigned long c_cflag; /* Control modes */ unsigned long c_lflag; /* Line discipline modes */ char c_line; CHAR c_cc[17]; }; struct termios temp; #ifdef NETCMD if (ttpipe) return(0); #endif /* NETCMD */ #ifdef NETPTY if (ttpty) return(0); #endif /* NETPTY */ debug(F101,"tthflow entry status","",status); if (ioctl(ttyfd,TCGETS,&temp) > -1) { /* Get terminal modes. */ if (status) { /* Turn hard flow on */ temp.c_cflag |= CRTSCTS; /* Add RTS/CTS to them. */ x = ioctl(ttyfd,TCSETS,&temp); /* Set them again. */ attrs->c_cflag |= CRTSCTS; /* Add to global info. */ } else { /* Turn hard flow off */ temp.c_cflag &= ~CRTSCTS; x = ioctl(ttyfd,TCSETS,&temp); attrs->c_cflag &= ~CRTSCTS; } } #else /* Not SunOS 4.0 or later */ #ifdef AIXRS /* IBM AIX RS/6000 */ #ifndef AIX41 /* But only pre-4.x == SVR4 */ #ifdef NETCMD if (ttpipe) return(0); #endif /* NETCMD */ #ifdef NETPTY if (ttpty) return(0); #endif /* NETPTY */ if (status) { if ((x = ioctl(ttyfd, TXADDCD, "rts")) < 0 && errno != EBUSY) debug(F100,"hardflow TXADDCD (rts) error", "", 0); } else { if ((x = ioctl(ttyfd, TXDELCD, "rts")) < 0 && errno != EINVAL) debug(F100,"hardflow TXDELCD (rts) error", "", 0); } #endif /* AIX41 */ #else /* Not AIX RS/6000 */ #ifdef ATTSV /* System V... */ #ifdef CK_SCOV5 /* SCO Open Server 5.0 */ #define CK_SCOUNIX #else #ifdef M_UNIX /* SCO UNIX 3.2v4.x or earlier */ #define CK_SCOUNIX #endif /* M_UNIX */ #endif /* CK_SCOV5 */ #ifdef SCO_FORCE_RTSXOFF #ifdef CK_SCOUNIX /* But not SCO OpenServer 5.0.4 */ #ifdef SCO_OSR504 /* or later... */ #undef CK_SCOUNIX #endif /* SCO_OSR504 */ #endif /* CK_SCOUNIX */ #endif /* SCO_FORCE_RTSXOFF */ #ifdef CK_SCOUNIX #ifdef POSIX struct termios temp; #ifdef NETCMD if (ttpipe) return(0); #endif /* NETCMD */ #ifdef NETPTY if (ttpty) return(0); #endif /* NETPTY */ debug(F101,"tthflow SCOUNIX POSIX entry status","",status); errno = 0; x = tcgetattr(ttyfd, &temp); debug(F111,"tthflow SCO UNIX POSIX tcgetattr",ckitoa(x),errno); #else /* POSIX */ struct termio temp; #ifdef NETCMD if (ttpipe) return(0); #endif /* NETCMD */ #ifdef NETPTY if (ttpty) return(0); #endif /* NETPTY */ debug(F101,"tthflow SCOUNIX non-POSIX entry status","",status); x = ioctl(ttyfd, TCGETA, &temp); debug(F111,"tthflow SCO UNIX non-POSIX TCGETA",ckitoa(x),errno); #endif /* POSIX */ /* This is not really POSIX, since POSIX does not deal with hardware flow control, but we are using the POSIX APIs. In fact, RTSFLOW and CTSFLOW are defined in termio.h, but within #ifndef _POSIX_SOURCE..#endif. So let's try forcing their definitions here. */ #ifndef CTSFLOW #define CTSFLOW 0020000 debug(F101,"tthflow SCO defining CTSFLOW","",CTSFLOW); #else debug(F101,"tthflow SCO CTSFLOW","",CTSFLOW); #endif /* CTSFLOW */ #ifndef RTSFLOW #define RTSFLOW 0040000 debug(F101,"tthflow SCO defining RTSFLOW","",RTSFLOW); #else debug(F101,"tthflow SCO RTSFLOW","",RTSFLOW); #endif /* RTSFLOW */ #ifndef ORTSFL #define ORTSFL 0100000 debug(F101,"tthflow SCO defining ORTSFL","",ORTSFL); #else debug(F101,"tthflow SCO ORTSFL","",ORTSFL); #endif /* ORTSFL */ if (x != -1) { if (status) { /* Turn it ON */ temp.c_cflag |= RTSFLOW|CTSFLOW; attrs->c_cflag |= RTSFLOW|CTSFLOW; #ifdef ORTSFL temp.c_cflag &= ~ORTSFL; attrs->c_cflag &= ~ORTSFL; #endif /* ORTSFL */ temp.c_iflag &= ~(IXON|IXOFF|IXANY); attrs->c_iflag &= ~(IXON|IXOFF|IXANY); } else { /* Turn it OFF */ #ifdef ORTSFL temp.c_cflag &= ~(RTSFLOW|CTSFLOW|ORTSFL); attrs->c_cflag &= ~(RTSFLOW|CTSFLOW|ORTSFL); #else /* ORTSFL */ temp.c_cflag &= ~(RTSFLOW|CTSFLOW); attrs->c_cflag &= ~(RTSFLOW|CTSFLOW); #endif /* ORTSFL */ } #ifdef POSIX x = tcsetattr(ttyfd, TCSADRAIN, &temp); #else x = ioctl(ttyfd, TCSETA, &temp); #endif /* POSIX */ debug(F101,"tthflow SCO set modes","",x); } #else /* Not SCO UNIX */ #ifdef NETCMD if (ttpipe) return(0); #endif /* NETCMD */ #ifdef NETPTY if (ttpty) return(0); #endif /* NETPTY */ if (!status) { /* Turn it OFF */ #ifdef RTSXOFF debug(F100,"tthflow ATTSV RTS/CTS OFF","",0); rctsx.x_hflag &= ~(RTSXOFF|CTSXON); #ifdef TCSETX x = ioctl(ttyfd,TCSETX,&rctsx); debug(F101,"tthflow ATTSV TCSETX OFF","",x); #else x = -1 debug(F100,"tthflow TCSETX not defined","",0); #endif /* TCSETX */ #else debug(F100,"tthflow ATTSV RTSXOFF not defined","",0); #endif /* RTSXOFF */ #ifdef DTRXOFF debug(F100,"tthflow ATTSV DTR/CD OFF","",0); rctsx.x_hflag &= ~(DTRXOFF|CDXON); x = ioctl(ttyfd,TCSETX,&rctsx); debug(F101,"tthflow ATTSV DTRXOFF OFF","",x); #else debug(F100,"tthflow ATTSV DTRXOFF not defined","",0); #endif /* DTRXOFF */ } else { /* Turn it ON. */ if (flow == FLO_RTSC) { /* RTS/CTS Flow control... */ debug(F100,"tthflow ATTSV RTS/CTS ON","",0); #ifdef RTSXOFF /* This is the preferred way, according to SVID3 */ #ifdef TCGETX x = ioctl(ttyfd,TCGETX,&rctsx); debug(F101,"tthflow TCGETX","",x); if (x > -1) { rctsx.x_hflag |= RTSXOFF | CTSXON; x = ioctl(ttyfd,TCSETX,&rctsx); debug(F100,"tthflow ATTSV ioctl","",x); } #else debug(F100,"tthflow TCGETX not defined","",0); x = -1 #endif /* TCGETX */ #else debug(F100,"tthflow RTSXOFF not defined","",0); x = -1; #endif /* RTSXOFF */ } else if (flow == FLO_DTRC) { /* DTR/CD Flow control... */ debug(F100,"tthflow ATTSV DTR/CD ON","",0); #ifdef DTRXOFF /* This is straight out of SVID R4 */ if (ioctl(ttyfd,TCGETX,&rctsx) > -1) { rctsx.x_hflag &= ~(DTRXOFF|CDXON); x = ioctl(ttyfd,TCSETX,&rctsx); } #else debug(F100,"tthflow ATTSV DTRXOFF not defined","",0); x = -1; #endif /* DTRXOFF */ } } #endif /* CK_SCOUNIX */ #else /* not System V... */ #ifdef CK_DTRCTS #ifdef LDODTR #ifdef LDOCTS #ifdef NETCMD if (ttpipe) return(0); #endif /* NETCMD */ #ifdef NETPTY if (ttpty) return(0); #endif /* NETPTY */ x = LDODTR | LDOCTS; /* Found only on UTEK? */ if (flow == FLO_DTRT && status) { /* Use hardware flow control */ if (lmodef) { x = ioctl(ttyfd,TIOCLBIS,&x); if (x < 0) { debug(F100,"hardflow TIOCLBIS error","",0); } else { lmodef++; debug(F100,"hardflow TIOCLBIS ok","",0); } } } else { if (lmodef) { x = ioctl(ttyfd,TIOCLBIC,&x); if (x < 0) { debug(F100,"hardflow TIOCLBIC error","",0); } else { lmodef++; debug(F100,"hardflow TIOCLBIC ok","",0); } } } #endif /* LDODTR */ #endif /* LDOCTS */ #endif /* CK_DTRCTS */ #endif /* ATTSV */ #endif /* AIXRS */ #endif /* SUNOS4 */ #endif /* QNX */ #endif /* POSIX_CRTSCTS */ #endif /* SUNOS41 */ #else /* OXOS */ struct termios temp; /* Olivetti X/OS ... */ #ifdef NETCMD if (ttpipe) return(0); #endif /* NETCMD */ #ifdef NETPTY if (ttpty) return(0); #endif /* NETPTY */ x = ioctl(ttyfd,TCGETS,&temp); if (x == 0) { temp.c_cflag &= ~(CRTSCTS|CDTRCTS|CBRKFLOW|CDTRDSR|CRTSDSR); if (status) { switch (flow) { case FLO_RTSC: temp.c_cflag |= CRTSCTS; /* RTS/CTS (hard) */ break; case FLO_DTRT: temp.c_cflag |= CDTRCTS; /* DTR/CTS (hard) */ break; } } x = ioctl(ttyfd,TCSETS,&temp); } #endif /* OXOS */ return(x); #endif /* Plan9 */ } /* T T P K T -- Condition the communication line for packets */ /* or for modem dialing */ /* If called with speed > -1, also set the speed. Returns 0 on success, -1 on failure. NOTE: the "xflow" parameter is supposed to be the currently selected type of flow control, but for historical reasons, this parameter is also used to indicate that we are dialing. Therefore, when the true flow control setting is needed, we access the external variable "flow", rather than trusting our "xflow" argument. */ int #ifdef CK_ANSIC ttpkt(long speed, int xflow, int parity) #else ttpkt(speed,xflow,parity) long speed; int xflow, parity; #endif /* CK_ANSIC */ /* ttpkt */ { #ifndef NOLOCAL int s2; int s = -1; #endif /* NOLOCAL */ int x; extern int flow; /* REAL flow-control setting */ if (ttyfd < 0) return(-1); /* Not open. */ debug(F101,"ttpkt parity","",parity); debug(F101,"ttpkt xflow","",xflow); debug(F101,"ttpkt speed","",(int) speed); ttprty = parity; /* Let other tt functions see these. */ ttspeed = speed; /* Make global copy for this module */ ttpmsk = ttprty ? 0177 : 0377; /* Parity stripping mask */ #ifdef PARSENSE needpchk = ttprty ? 0 : 1; /* Parity check needed? */ #else needpchk = 0; #endif /* PARSENSE */ debug(F101,"ttpkt ttpmsk","",ttpmsk); debug(F101,"ttpkt netconn","",netconn); #ifdef NETCONN /* No mode-changing for telnet */ if (netconn) { #ifdef TCPSOCKET #ifdef TCP_NODELAY if (ttnet == NET_TCPB) { /* But turn off Nagle */ extern int tcp_nodelay; nodelay_sav = tcp_nodelay; no_delay(ttyfd,1); } #endif /* TCP_NODELAY */ #ifdef TN_COMPORT if (istncomport()) { int rc = -1; if (tvtflg == 0 && speed == ttspeed && flow == ttflow /* && ttcarr == curcarr */ ) { debug(F100,"ttpkt modes already set, skipping...","",0); return(0); /* Already been called. */ } if (flow != ttflow) { if ((rc = tnsetflow(flow)) < 0) return(rc); ttflow = flow; } if (speed != ttspeed) { if (speed <= 0) speed = tnc_get_baud(); else if ((rc = tnc_set_baud(speed)) < 0) return(rc); ttspeed = speed; } tnc_set_datasize(8); tnc_set_stopsize(stopbits); #ifdef HWPARITY if (hwparity) { switch (hwparity) { case 'e': /* Even */ debug(F100,"ttres 8 bits + even parity","",0); tnc_set_parity(3); break; case 'o': /* Odd */ debug(F100,"ttres 8 bits + odd parity","",0); tnc_set_parity(2); break; case 'm': /* Mark */ debug(F100,"ttres 8 bits + invalid parity: mark","",0); tnc_set_parity(4); break; case 's': /* Space */ debug(F100,"ttres 8 bits + invalid parity: space","",0); tnc_set_parity(5); break; } } else #endif /* HWPARITY */ { tnc_set_parity(1); /* None */ } tvtflg = 0; return(0); } #endif /* TN_COMPORT */ #endif /* TCPSOCKET */ tvtflg = 0; return(0); } #endif /* NETCONN */ #ifdef NETCMD if (ttpipe) return(0); #endif /* NETCMD */ #ifdef NETPTY if (ttpty) return(0); #endif /* NETPTY */ #ifndef Plan9 if (ttfdflg && !isatty(ttyfd)) return(0); #endif /* Plan9 */ #ifdef COHERENT #define SVORPOSIX #endif /* COHERENT */ #ifndef SVORPOSIX /* Berkeley, V7, etc. */ #ifdef LPASS8 /* For some reason, with BSD terminal drivers, you can't set FLOW to XON/XOFF after having previously set it to NONE without closing and reopening the device. Unless there's something I overlooked below... */ if (ttflow == FLO_NONE && flow == FLO_XONX && xlocal == 0) { debug(F101,"ttpkt executing horrible flow kludge","",0); ttclos(0); /* Close it */ x = 0; ttopen(ttnmsv,&x,ttmdm,0); /* Open it again */ } #endif /* LPASS8 */ #endif /* SVORPOSIX */ #ifdef COHERENT /* This must be vestigial since we */ #undef SVORPOSIX /* reverse it a few lines below... */ #endif /* COHERENT */ if (xflow != FLO_DIAL && xflow != FLO_DIAX) ttflow = xflow; /* Now make this available too. */ #ifndef NOLOCAL if (xlocal) { s2 = (int) (speed / 10L); /* Convert bps to cps */ debug(F101,"ttpkt calling ttsspd","",s2); s = ttsspd(s2); /* Check and set the speed */ debug(F101,"ttpkt ttsspd result","",s); carrctl(&ttraw, xflow != FLO_DIAL /* Carrier control */ && (ttcarr == CAR_ON || (ttcarr == CAR_AUT && ttmdm != 0))); tvtflg = 0; /* So ttvt() will work next time */ } #endif /* NOLOCAL */ #ifdef COHERENT #define SVORPOSIX #endif /* COHERENT */ #ifndef SVORPOSIX /* BSD section */ if (flow == FLO_RTSC || /* Hardware flow control */ flow == FLO_DTRC || flow == FLO_DTRT) { tthflow(flow, 1, &ttraw); debug(F100,"ttpkt hard flow, TANDEM off, RAW on","",0); ttraw.sg_flags &= ~TANDEM; /* Turn off software flow control */ ttraw.sg_flags |= RAW; /* Enter raw mode */ } else if (flow == FLO_NONE) { /* No flow control */ debug(F100,"ttpkt no flow, TANDEM off, RAW on","",0); ttraw.sg_flags &= ~TANDEM; /* Turn off software flow control */ tthflow(flow, 0, &ttraw); /* Turn off any hardware f/c too */ ttraw.sg_flags |= RAW; /* Enter raw mode */ } else if (flow == FLO_KEEP) { /* Keep device's original setting */ debug(F100,"ttpkt keeping original TANDEM","",0); ttraw.sg_flags &= ~TANDEM; ttraw.sg_flags |= (ttold.sg_flags & TANDEM); /* NOTE: We should also handle hardware flow control here! */ } /* SET FLOW XON/XOFF is in effect, or SET FLOW KEEP resulted in Xon/Xoff */ if ((flow == FLO_XONX) || (ttraw.sg_flags & TANDEM)) { debug(F100,"ttpkt turning on TANDEM","",0); ttraw.sg_flags |= TANDEM; /* So ask for it. */ #ifdef LPASS8 /* Can pass 8-bit data through? */ /* If the LPASS8 local mode is available, then flow control can always */ /* be used, even if parity is none and we are transferring 8-bit data. */ /* But we only need to do all this if Xon/Xoff is requested. */ /* BUT... this tends not to work through IP or LAT connections, terminal */ /* servers, telnet, rlogin, etc, so it is currently disabled. */ x = LPASS8; /* If LPASS8 defined, then */ debug(F100,"ttpkt executing LPASS8 code","",0); if (lmodef) { /* TIOCLBIS must be too. */ x = ioctl(ttyfd,TIOCLBIS,&x); /* Try to set LPASS8. */ if (x < 0) { debug(F100,"ttpkt TIOCLBIS error","",0); } else { lmodef++; debug(F100,"ttpkt TIOCLBIS ok","",0); } } /* But if we use LPASS8 mode, we must explicitly turn off terminal interrupts of all kinds. */ #ifdef TIOCGETC /* Not rawmode, */ if (tcharf && (xlocal == 0)) { /* must turn off */ tchnoi.t_intrc = -1; /* interrupt character */ tchnoi.t_quitc = -1; /* and quit character. */ tchnoi.t_startc = 17; /* Make sure xon */ tchnoi.t_stopc = 19; /* and xoff not ignored. */ #ifndef NOBRKC tchnoi.t_eofc = -1; /* eof character. */ tchnoi.t_brkc = -1; /* brk character. */ #endif /* NOBRKC */ if (ioctl(ttyfd,TIOCSETC,&tchnoi) < 0) { debug(F100,"ttpkt TIOCSETC failed","",0); } else { tcharf = 1; debug(F100,"ttpkt TIOCSETC ok","",0); } #ifdef COMMENT /* only for paranoid debugging */ if (tcharf) { struct tchars foo; char tchbuf[100]; ioctl(0,TIOCGETC,&foo); sprintf(tchbuf, "intr=%d,quit=%d, start=%d, stop=%d, eof=%d, brk=%d", foo.t_intrc, foo.t_quitc, foo.t_startc, foo.t_stopc, foo.t_eofc, foo.t_brkc); debug(F110,"ttpkt chars",tchbuf,0); } #endif /* COMMENT */ } ttraw.sg_flags |= CBREAK; /* Needed for unknown reason */ #endif /* TIOCGETC */ /* Prevent suspend during packet mode */ #ifdef TIOCGLTC /* Not rawmode, */ if (ltcharf && (xlocal == 0)) { /* must turn off */ ltchnoi.t_suspc = -1; /* suspend character */ ltchnoi.t_dsuspc = -1; /* and delayed suspend character */ if (ioctl(ttyfd,TIOCSLTC,&tchnoi) < 0) { debug(F100,"ttpkt TIOCSLTC failed","",0); } else { ltcharf = 1; debug(F100,"ttpkt TIOCSLTC ok","",0); } } #endif /* TIOCGLTC */ #else /* LPASS8 not defined */ /* Previously, BSD-based implementations always */ /* used rawmode for packets. Now, we use rawmode only if parity is NONE. */ /* This allows the flow control requested above to actually work, but only */ /* if the user asks for parity (which also means they get 8th-bit quoting). */ if (parity) { /* If parity, */ ttraw.sg_flags &= ~RAW; /* use cooked mode */ #ifdef COMMENT /* WHY??? */ if (xlocal) #endif /* COMMENT */ ttraw.sg_flags |= CBREAK; debug(F101,"ttpkt cooked, cbreak, parity","",parity); #ifdef TIOCGETC /* Not rawmode, */ if (tcharf && (xlocal == 0)) { /* must turn off */ tchnoi.t_intrc = -1; /* interrupt character */ tchnoi.t_quitc = -1; /* and quit character. */ tchnoi.t_startc = 17; /* Make sure xon */ tchnoi.t_stopc = 19; /* and xoff not ignored. */ #ifndef NOBRKC tchnoi.t_eofc = -1; /* eof character. */ tchnoi.t_brkc = -1; /* brk character. */ #endif /* NOBRKC */ if (ioctl(ttyfd,TIOCSETC,&tchnoi) < 0) { debug(F100,"ttpkt TIOCSETC failed","",0); } else { tcharf = 1; debug(F100,"ttpkt TIOCSETC ok","",0); } } #endif /* TIOCGETC */ #ifdef TIOCGLTC /* Not rawmode, */ /* Prevent suspend during packet mode */ if (ltcharf && (xlocal == 0)) { /* must turn off */ ltchnoi.t_suspc = -1; /* suspend character */ ltchnoi.t_dsuspc = -1; /* and delayed suspend character */ if (ioctl(ttyfd,TIOCSLTC,&tchnoi) < 0) { debug(F100,"ttpkt TIOCSLTC failed","",0); } else { ltcharf = 1; debug(F100,"ttpkt TIOCSLTC ok","",0); } } #endif /* TIOCGLTC */ } else { /* If no parity, */ ttraw.sg_flags |= RAW; /* must use 8-bit raw mode. */ debug(F101,"ttpkt setting rawmode, parity","",parity); } #endif /* LPASS8 */ } /* End of Xon/Xoff section */ /* Don't echo, don't map CR to CRLF on output, don't fool with case */ #ifdef LCASE ttraw.sg_flags &= ~(ECHO|CRMOD|LCASE); #else ttraw.sg_flags &= ~(ECHO|CRMOD); #endif /* LCASE */ #ifdef TOWER1 ttraw.sg_flags &= ~ANYP; /* Must set this on old Towers */ #endif /* TOWER1 */ #ifdef BELLV10 if (ioctl(ttyfd,TIOCSETP,&ttraw) < 0) /* Set the new modes. */ return(-1); #else errno = 0; if (stty(ttyfd,&ttraw) < 0) { /* Set the new modes. */ debug(F101,"ttpkt stty failed","",errno); return(-1); } #endif /* BELLV10 */ debug(F100,"ttpkt stty ok","",0); #ifdef sony_news x = xlocal ? km_ext : km_con; /* Put line in ASCII mode. */ if (x != -1) { /* Make sure we know original modes. */ x &= ~KM_TTYPE; x |= KM_ASCII; if (ioctl(ttyfd,TIOCKSET, &x) < 0) { perror("ttpkt can't set ASCII mode"); debug(F101,"ttpkt error setting ASCII mode","",x); return(-1); } } debug(F100,"ttpkt set ASCII mode ok","",0); #endif /* sony_news */ if (xlocal == 0) { /* Turn this off so we can read */ signal(SIGINT,SIG_IGN); /* Ctrl-C chars typed at console */ sigint_ign = 1; } tvtflg = 0; /* So ttvt() will work next time */ debug(F100,"ttpkt success","",0); return(0); #endif /* Not ATTSV or POSIX */ /* AT&T UNIX and POSIX */ #ifdef COHERENT #define SVORPOSIX #endif /* COHERENT */ #ifdef SVORPOSIX if (flow == FLO_XONX) { /* Xon/Xoff */ ttraw.c_iflag |= (IXON|IXOFF); tthflow(flow, 0, &ttraw); } else if (flow == FLO_NONE) { /* None */ /* NOTE: We should also turn off hardware flow control here! */ ttraw.c_iflag &= ~(IXON|IXOFF); tthflow(flow, 0, &ttraw); } else if (flow == FLO_KEEP) { /* Keep */ ttraw.c_iflag &= ~(IXON|IXOFF); /* Turn off Xon/Xoff flags */ ttraw.c_iflag |= (ttold.c_iflag & (IXON|IXOFF)); /* OR in old ones */ /* NOTE: We should also handle hardware flow control here! */ #ifdef POSIX_CRTSCTS /* In Linux case, we do this, which is unlikely to be portable */ ttraw.c_cflag &= ~CRTSCTS; /* Turn off RTS/CTS flag */ ttraw.c_cflag |= (ttold.c_cflag & CRTSCTS); /* OR in old one */ #endif /* POSIX_CRTSCTS */ } else if (flow == FLO_RTSC || /* Hardware */ flow == FLO_DTRC || flow == FLO_DTRT) { ttraw.c_iflag &= ~(IXON|IXOFF); /* (190) */ tthflow(flow, 1, &ttraw); } ttraw.c_lflag &= ~(ICANON|ECHO); ttraw.c_lflag &= ~ISIG; /* Do NOT check for interrupt chars */ #ifndef OXOS #ifdef QNX if (flow != FLO_RTSC && flow != FLO_DTRC && flow != FLO_DTRT) #endif /* QNX */ #ifndef COHERENT ttraw.c_lflag &= ~IEXTEN; /* Turn off ^O/^V processing */ #endif /* COHERENT */ #else /* OXOS */ ttraw.c_cc[VDISCARD] = ttraw.c_cc[VLNEXT] = CDISABLE; #endif /* OXOS */ ttraw.c_lflag |= NOFLSH; /* Don't flush */ ttraw.c_iflag |= IGNPAR; /* Ignore parity errors */ #ifdef ATTSV #ifdef BSD44 ttraw.c_iflag &= ~(IGNBRK|INLCR|IGNCR|ICRNL|INPCK|ISTRIP|IXANY); #else ttraw.c_iflag &= ~(IGNBRK|INLCR|IGNCR|ICRNL|IUCLC|INPCK|ISTRIP|IXANY); #endif /* BSD44 */ #else /* POSIX */ ttraw.c_iflag &= ~(IGNBRK|INLCR|IGNCR|ICRNL|INPCK|ISTRIP); #endif /* ATTSV */ ttraw.c_oflag &= ~OPOST; ttraw.c_cflag &= ~(CSIZE); ttraw.c_cflag |= (CS8|CREAD|HUPCL); #ifdef CSTOPB if (xlocal) { if (stopbits == 2) { ttraw.c_cflag |= CSTOPB; /* 2 stop bits */ debug(F100,"ttpkt 2 stopbits","",0); } else if (stopbits == 1) { ttraw.c_cflag &= ~(CSTOPB); /* 1 stop bit */ debug(F100,"ttpkt 1 stopbit","",0); } } #endif /* CSTOPB */ #ifdef HWPARITY if (hwparity && xlocal) { /* Hardware parity */ ttraw.c_cflag |= PARENB; /* Enable parity */ #ifdef COMMENT /* Uncomment this only if needed -- I don't think it is */ ttraw.c_cflag &= ~(CSIZE); /* Clear out character-size mask */ ttraw.c_cflag |= CS8; /* And set it to 8 */ #endif /* COMMENT */ #ifdef IGNPAR ttraw.c_iflag |= IGNPAR; /* Don't discard incoming bytes */ debug(F100,"ttpkt IGNPAR","",0); /* that have parity errors */ #endif /* IGNPAR */ switch (hwparity) { case 'e': /* Even */ ttraw.c_cflag &= ~(PARODD); debug(F100,"ttpkt 8 bits + even parity","",0); break; case 'o': /* Odd */ ttraw.c_cflag |= PARODD; debug(F100,"ttpkt 8 bits + odd parity","",0); break; case 'm': /* Mark */ case 's': /* Space */ /* PAREXT is mentioned in SVID but the details are not given. */ /* PAREXT is not included in POSIX ISO/IEC 9945-1. */ debug(F100,"ttpkt 8 bits + invalid parity","",0); break; } } else { /* We handle parity ourselves */ #endif /* HWPARITY */ ttraw.c_cflag &= ~(PARENB); /* Don't enable parity */ #ifdef HWPARITY } #endif /* HWPARITY */ #ifdef IX370 ttraw.c_cc[4] = 48; /* So Series/1 doesn't interrupt on every char */ ttraw.c_cc[5] = 1; #else #ifndef VEOF /* for DGUX this is VEOF, not VMIN */ ttraw.c_cc[4] = 1; /* [VMIN] return max of this many characters or */ #else #ifndef OXOS #ifdef VMIN ttraw.c_cc[VMIN] = 1; #endif /* VMIN */ #else /* OXOS */ ttraw.c_min = 1; #endif /* OXOS */ #endif /* VEOF */ #ifndef VEOL /* for DGUX this is VEOL, not VTIME */ ttraw.c_cc[5] = 0; /* [VTIME] when this many secs/10 expire w/no input */ #else #ifndef OXOS #ifdef VTIME ttraw.c_cc[VTIME] = 0; #endif /* VTIME */ #else /* OXOS */ ttraw.c_time = 0; #endif /* OXOS */ #endif /* VEOL */ #endif /* IX370 */ #ifdef VINTR /* Turn off interrupt character */ if (xlocal == 0) /* so ^C^C can break us out of */ ttraw.c_cc[VINTR] = 0; /* packet mode. */ #endif /* VINTR */ #ifdef Plan9 if (p9ttyparity('n') < 0) return -1; #else #ifdef BSD44ORPOSIX errno = 0; #ifdef BEOSORBEBOX ttraw.c_cc[VMIN] = 0; /* DR7 can only poll. */ #endif /* BEOSORBEBOX */ #define TESTING234 #ifdef TESTING234 if (1) { debug(F100,"ttpkt TESTING234 rawmode","",0); /* iflags */ ttraw.c_iflag &= ~(PARMRK|ISTRIP|BRKINT|INLCR|IGNCR|ICRNL); ttraw.c_iflag &= ~(INPCK|IGNPAR|IXON|IXOFF); ttraw.c_iflag |= IGNBRK; #ifdef IMAXBEL ttraw.c_iflag &= ~IMAXBEL; #endif /* IMAXBEL */ #ifdef IXANY ttraw.c_iflag &= ~IXANY; #endif /* IXANY */ #ifdef IUCLC ttraw.c_iflag &= ~IUCLC; #endif /* IUCLC */ /* oflags */ ttraw.c_oflag &= ~OPOST; #ifdef OXTABS ttraw.c_oflag &= ~OXTABS; #endif /* OXTABS */ #ifdef ONOCR ttraw.c_oflag &= ~ONOCR; #endif /* ONOCR */ #ifdef ONLRET ttraw.c_oflag &= ~ONLRET; #endif /* ONLRET */ #ifdef ONLCR ttraw.c_oflag &= ~ONLCR; #endif /* ONLCR */ /* lflags */ ttraw.c_lflag &= ~ECHO; #ifdef ECHOE ttraw.c_lflag &= ~ECHOE; #endif /* ECHOE */ #ifdef ECHONL ttraw.c_lflag &= ~ECHONL; #endif /* ECHONL */ #ifdef ECHOPRT ttraw.c_lflag &= ~ECHOPRT; #endif /* ECHOPRT */ #ifdef ECHOKE ttraw.c_lflag &= ~ECHOKE; #endif /* ECHOKE */ #ifdef ECHOCTL ttraw.c_lflag &= ~ECHOCTL; #endif /* ECHOCTL */ #ifdef ALTWERASE ttraw.c_lflag &= ~ALTWERASE; #endif /* ALTWERASE */ #ifdef EXTPROC ttraw.c_lflag &= ~EXTPROC; #endif /* EXTPROC */ ttraw.c_lflag &= ~(ICANON|ISIG|IEXTEN|TOSTOP|FLUSHO|PENDIN); #ifdef NOKERNINFO ttraw.c_lflag |= NOKERNINFO; #endif /* NOKERNINFO */ /* ttraw.c_lflag |= NOFLSH; */ ttraw.c_lflag &= ~NOFLSH; /* cflags */ ttraw.c_cflag &= ~(CSIZE|PARENB|PARODD); ttraw.c_cflag |= CS8|CREAD; #ifdef VMIN ttraw.c_cc[VMIN] = 1; /* Supposedly needed for AIX */ #endif /* VMIN */ } #endif /* TESTING234 */ debug(F100,"ttpkt calling tcsetattr(TCSETAW)","",0); x = tcsetattr(ttyfd,TCSADRAIN,&ttraw); debug(F101,"ttpkt BSD44ORPOSIX tcsetattr","",x); if (x < 0) { debug(F101,"ttpkt BSD44ORPOSIX tcsetattr errno","",errno); return(-1); } #else /* BSD44ORPOSIX */ x = ioctl(ttyfd,TCSETAW,&ttraw); debug(F101,"ttpkt ATTSV ioctl TCSETAW","",x); if (x < 0) { /* set new modes . */ debug(F101,"ttpkt ATTSV ioctl TCSETAW errno","",errno); return(-1); } #endif /* BSD44ORPOSIX */ #endif /* Plan9 */ tvtflg = 0; debug(F100,"ttpkt ok","",0); return(0); #endif /* ATTSV */ #ifdef COHERENT #undef SVORPOSIX #endif /* COHERENT */ } /* T T S E T F L O W -- Set flow control immediately. */ #ifdef COHERENT #define SVORPOSIX #endif /* COHERENT */ int ttsetflow(flow) int flow; { if (ttyfd < 0) /* A channel must be open */ return(-1); debug(F101,"ttsetflow flow","",flow); #ifdef TN_COMPORT if (netconn && istncomport()) { debug(F101,"ttsetflow net modem","",ttmdm); return(tnsetflow(flow)); } #endif /* TN_COMPORT */ #ifdef NETCMD if (ttpipe) return(0); #endif /* NETCMD */ #ifdef NETPTY if (ttpty) return(0); #endif /* NETPTY */ #ifdef COMMENT /* This seems to hurt... */ if (flow == FLO_KEEP) return(0); #endif /* COMMENT */ if (flow == FLO_RTSC || /* Hardware flow control... */ flow == FLO_DTRC || flow == FLO_DTRT) { tthflow(flow, 1, &ttraw); #ifndef SVORPOSIX ttraw.sg_flags &= ~TANDEM; /* Turn off software flow control */ #else ttraw.c_iflag &= ~(IXON|IXOFF); #endif /* SVORPOSIX */ } else if (flow == FLO_XONX) { /* Xon/Xoff... */ #ifndef SVORPOSIX ttraw.sg_flags |= TANDEM; #else ttraw.c_iflag |= (IXON|IXOFF); #endif /* SVORPOSIX */ tthflow(FLO_RTSC, 0, &ttraw); /* Turn off hardware flow control */ } else if (flow == FLO_NONE) { /* No flow control */ #ifndef SVORPOSIX ttraw.sg_flags &= ~TANDEM; /* Turn off software flow control */ #else ttraw.c_iflag &= ~(IXON|IXOFF); #endif /* SVORPOSIX */ tthflow(FLO_RTSC, 0, &ttraw); /* Turn off any hardware f/c too */ } /* Set the new modes... */ #ifndef SVORPOSIX /* BSD and friends */ #ifdef BELLV10 if (ioctl(ttyfd,TIOCSETP,&ttraw) < 0) return(-1); #else #ifndef MINIX2 if (stty(ttyfd,&ttraw) < 0) return(-1); #endif /* MINIX2 */ #endif /* BELLV10 */ #else #ifdef BSD44ORPOSIX /* POSIX */ if (tcsetattr(ttyfd,TCSADRAIN,&ttraw) < 0) return(-1); #else /* System V */ if (ioctl(ttyfd,TCSETAW,&ttraw) < 0) return(-1); #endif /* BSD44ORPOSIX */ #endif /* SVORPOSIX */ return(0); } #ifdef COHERENT #undef SVORPOSIX #endif /* COHERENT */ /* T T V T -- Condition communication device for use as virtual terminal. */ int #ifdef CK_ANSIC ttvt(long speed, int flow) #else ttvt(speed,flow) long speed; int flow; #endif /* CK_ANSIC */ /* ttvt */ { int s, s2, x; debug(F101,"ttvt ttyfd","",ttyfd); debug(F101,"ttvt tvtflg","",tvtflg); debug(F111,"ttvt speed",ckitoa(ttspeed),speed); debug(F111,"ttvt flow",ckitoa(ttflow),flow); debug(F111,"ttvt curcarr",ckitoa(ttcarr),curcarr); /* Note: NetBSD and maybe other BSD44s have cfmakeraw() */ /* Maybe it would be simpler to use it... */ ttpmsk = 0xff; #ifdef NOLOCAL return(conbin((char)escchr)); #else if (ttyfd < 0) { /* Not open. */ if (ttchk() < 0) return(-1); else /* But maybe something buffered. */ return(0); } #ifdef NETCMD if (ttpipe) return(0); #endif /* NETCMD */ #ifdef NETPTY if (ttpty) return(0); #endif /* NETPTY */ #ifdef NETCONN if (netconn) { #ifdef TCPSOCKET #ifdef TCP_NODELAY { extern int tcp_nodelay; if (ttnet == NET_TCPB) { if (nodelay_sav > -1) { no_delay(ttyfd,nodelay_sav); nodelay_sav = -1; } } } #endif /* TCP_NODELAY */ #ifdef TN_COMPORT if (istncomport()) { int rc = -1; if (tvtflg != 0 && speed == ttspeed && flow == ttflow /* && ttcarr == curcarr */ ) { debug(F100,"ttvt modes already set, skipping...","",0); return(0); /* Already been called. */ } if (flow != ttflow) { if ((rc = tnsetflow(flow)) < 0) return(rc); ttflow = flow; } if (speed != ttspeed) { if (speed <= 0) speed = tnc_get_baud(); else if ((rc = tnc_set_baud(speed)) < 0) return(rc); ttspeed = speed; } tnc_set_datasize(8); tnc_set_stopsize(stopbits); #ifdef HWPARITY if (hwparity) { switch (hwparity) { case 'e': /* Even */ debug(F100,"ttres 8 bits + even parity","",0); tnc_set_parity(3); break; case 'o': /* Odd */ debug(F100,"ttres 8 bits + odd parity","",0); tnc_set_parity(2); break; case 'm': /* Mark */ debug(F100,"ttres 8 bits + invalid parity: mark","",0); tnc_set_parity(4); break; case 's': /* Space */ debug(F100,"ttres 8 bits + invalid parity: space","",0); tnc_set_parity(5); break; } } else #endif /* HWPARITY */ { tnc_set_parity(1); /* None */ } tvtflg = 1; return(0); } #endif /* TN_COMPORT */ #endif /* TCPSOCKET */ tvtflg = 1; /* Network connections... */ debug(F100,"ttvt network connection, skipping...","",0); return(0); /* ... require no special setup */ } #endif /* NETCONN */ if (tvtflg != 0 && speed == ttspeed && flow == ttflow /* && ttcarr == curcarr */ ) { debug(F100,"ttvt modes already set, skipping...","",0); return(0); /* Already been called. */ } if (ttfdflg #ifndef Plan9 && !isatty(ttyfd) #endif /* Plan9 */ ) { debug(F100,"ttvt using external fd, skipping...","",0); return(0); } debug(F100,"ttvt setting modes...","",0); if (xlocal) { /* For external lines... */ s2 = (int) (speed / 10L); s = ttsspd(s2); /* Check/set the speed */ carrctl(&tttvt, flow != FLO_DIAL /* Do carrier control */ && (ttcarr == CAR_ON || (ttcarr == CAR_AUT && ttmdm != 0))); } else s = s2 = -1; #ifdef COHERENT #define SVORPOSIX #endif /* COHERENT */ #ifndef SVORPOSIX /* Berkeley, V7, etc */ if (flow == FLO_RTSC || /* Hardware flow control */ flow == FLO_DTRC || flow == FLO_DTRT) { tthflow(flow, 1, &tttvt); debug(F100,"ttvt hard flow, TANDEM off","",0); tttvt.sg_flags &= ~TANDEM; /* Turn off software flow control */ } else if (flow == FLO_XONX) { /* Xon/Xoff flow control */ debug(F100,"ttvt TANDEM on","",0); tttvt.sg_flags |= TANDEM; /* Ask for it. */ tthflow(flow, 0, &tttvt); /* Turn off hardware f/c */ } else if (flow == FLO_NONE) { debug(F100,"ttvt no flow, TANDEM off, RAW on","",0); tttvt.sg_flags &= ~TANDEM; /* Turn off software flow control */ tthflow(flow, 0, &tttvt); /* Turn off any hardware f/c too */ tttvt.sg_flags |= RAW; /* Enter raw mode */ } else if (flow == FLO_KEEP) { /* Keep device's original setting */ debug(F100,"ttvt keeping original TANDEM","",0); tttvt.sg_flags &= ~TANDEM; tttvt.sg_flags |= (ttold.sg_flags & TANDEM); /* NOTE: We should also handle hardware flow control here! */ } tttvt.sg_flags |= RAW; /* Raw mode in all cases */ #ifdef TOWER1 tttvt.sg_flags &= ~(ECHO|ANYP); /* No echo or parity */ #else tttvt.sg_flags &= ~ECHO; /* No echo */ #endif /* TOWER1 */ #ifdef BELLV10 if (ioctl(ttyfd,TIOCSETP,&tttvt) < 0) /* Set the new modes */ return(-1); #else if (stty(ttyfd,&tttvt) < 0) /* Set the new modes */ return(-1); #endif /* BELLV10 */ #else /* It is ATTSV or POSIX */ if (flow == FLO_XONX) { /* Software flow control */ tttvt.c_iflag |= (IXON|IXOFF); /* On if requested. */ tthflow(flow, 0, &tttvt); /* Turn off hardware f/c */ debug(F100,"ttvt SVORPOSIX flow XON/XOFF","",0); } else if (flow == FLO_NONE) { /* NONE */ tttvt.c_iflag &= ~(IXON|IXOFF); /* Turn off Xon/Xoff */ tthflow(flow, 0, &tttvt); /* Turn off hardware f/c */ debug(F100,"ttvt SVORPOSIX flow NONE","",0); } else if (flow == FLO_KEEP) { tttvt.c_iflag &= ~(IXON|IXOFF); /* Turn off Xon/Xoff flags */ tttvt.c_iflag |= (ttold.c_iflag & (IXON|IXOFF)); /* OR in old ones */ #ifdef POSIX_CRTSCTS tttvt.c_cflag &= ~CRTSCTS; /* Turn off RTS/CTS flag */ tttvt.c_cflag |= (ttold.c_cflag & CRTSCTS); /* OR in old one */ #endif /* POSIX_CRTSCTS */ debug(F100,"ttvt SVORPOSIX flow KEEP","",0); } else if (flow == FLO_RTSC || /* Hardware flow control */ flow == FLO_DTRC || flow == FLO_DTRT) { tttvt.c_iflag &= ~(IXON|IXOFF); /* (196) */ tthflow(flow, 1, &tttvt); debug(F100,"ttvt SVORPOSIX flow HARD","",0); } #ifndef OXOS #ifdef COHERENT tttvt.c_lflag &= ~(ISIG|ICANON|ECHO); #else tttvt.c_lflag &= ~(ISIG|ICANON|ECHO|IEXTEN); #endif /* COHERENT */ #ifdef QNX /* Needed for hwfc */ if (flow == FLO_RTSC || flow == FLO_DTRC || flow == FLO_DTRT) tttvt.c_lflag |= IEXTEN; #endif /* QNX */ #else /* OXOS */ tttvt.c_lflag &= ~(ISIG|ICANON|ECHO); tttvt.c_cc[VDISCARD] = tttvt.c_cc[VLNEXT] = CDISABLE; #endif /* OXOS */ tttvt.c_iflag |= (IGNBRK|IGNPAR); /* Stop bits */ #ifdef CSTOPB if (xlocal) { if (stopbits == 2) { tttvt.c_cflag |= CSTOPB; /* 2 stop bits */ debug(F100,"ttvt 2 stopbits","",0); } else if (stopbits == 1) { tttvt.c_cflag &= ~(CSTOPB); /* 1 stop bit */ debug(F100,"ttvt 1 stopbit","",0); } } #endif /* CSTOPB */ /* Parity */ #ifdef HWPARITY if (hwparity && xlocal) { /* Hardware parity */ #ifdef COMMENT /* Uncomment this only if needed -- I don't think it is */ ttraw.c_cflag &= ~(CSIZE); /* Clear out character-size mask */ ttraw.c_cflag |= CS8; /* And set it to 8 */ #endif /* COMMENT */ #ifdef IGNPAR debug(F101,"ttvt hwparity IGNPAR","",IGNPAR); tttvt.c_iflag |= IGNPAR; /* Don't discard incoming bytes */ #endif /* IGNPAR */ tttvt.c_cflag |= PARENB; /* Enable parity */ switch (hwparity) { case 'e': /* Even */ tttvt.c_cflag &= ~(PARODD); debug(F100,"ttvt 8 bits + even parity","",0); break; case 'o': /* Odd */ tttvt.c_cflag |= PARODD; debug(F100,"ttvt 8 bits + odd parity","",0); break; case 'm': /* Mark */ case 's': /* Space */ /* PAREXT is mentioned in SVID but the details are not given. */ /* PAREXT is not included in POSIX ISO/IEC 9945-1. */ debug(F100,"ttvt 8 bits + invalid parity","",0); break; } } else { /* We handle parity ourselves */ #endif /* HWPARITY */ tttvt.c_cflag &= ~(PARENB); /* Don't enable parity */ #ifdef HWPARITY } #endif /* HWPARITY */ #ifdef ATTSV #ifdef BSD44 /* Things not to do... */ tttvt.c_iflag &= ~(INLCR|IGNCR|ICRNL|INPCK|ISTRIP|IXANY); #else tttvt.c_iflag &= ~(INLCR|IGNCR|ICRNL|IUCLC|INPCK|ISTRIP|IXANY); #endif /* BSD44 */ #else /* POSIX */ tttvt.c_iflag &= ~(INLCR|IGNCR|ICRNL|INPCK|ISTRIP); #endif /* ATTSV */ tttvt.c_cflag &= ~(CSIZE); /* Zero out the char size field */ tttvt.c_cflag |= (CS8|CREAD|HUPCL); /* Char size 8, enable receiver, hup */ tttvt.c_oflag &= ~OPOST; /* Don't postprocess output */ #ifndef VEOF /* DGUX termio has VEOF at entry 4, see comment above */ tttvt.c_cc[4] = 1; #else #ifndef OXOS #ifdef VMIN tttvt.c_cc[VMIN] = 1; #endif /* VMIN */ #else /* OXOS */ tttvt.c_min = 1; #endif /* OXOS */ #endif /* VEOF */ #ifndef VEOL /* DGUX termio has VEOL at entry 5, see comment above */ tttvt.c_cc[5] = 0; #else #ifndef OXOS #ifdef VTIME tttvt.c_cc[VTIME] = 0; #endif /* VTIME */ #else /* OXOS */ tttvt.c_time = 0; #endif /* OXOS */ #endif /* VEOL */ #ifdef Plan9 if (p9ttyparity('n') < 0) return -1; #else #ifdef BSD44ORPOSIX errno = 0; #ifdef BEOSORBEBOX tttvt.c_cc[VMIN] = 0; /* DR7 can only poll. */ #endif /* BEOSORBEBOX */ x = tcsetattr(ttyfd,TCSADRAIN,&tttvt); debug(F101,"ttvt BSD44ORPOSIX tcsetattr","",x); if (x < 0) { debug(F101,"ttvt BSD44ORPOSIX tcsetattr errno","",errno); return(-1); } #else /* ATTSV */ x = ioctl(ttyfd,TCSETAW,&tttvt); debug(F101,"ttvt ATTSV ioctl TCSETAW","",x); if (x < 0) { /* set new modes . */ debug(F101,"ttvt ATTSV ioctl TCSETAW errno","",errno); return(-1); } #endif /* BSD44ORPOSIX */ #endif /* Plan9 */ #endif /* ATTSV */ ttspeed = speed; /* Done, remember how we were */ ttflow = flow; /* called, so we can decide how to */ tvtflg = 1; /* respond next time. */ debug(F100,"ttvt ok","",0); return(0); #ifdef COHERENT #undef SVORPOSIX #endif /* COHERENT */ #endif /* NOLOCAL */ } #ifndef NOLOCAL /* Serial speed department . . . */ /* SCO OSR5.0.x might or might not support high speeds. Sometimes they are not defined in the header files but they are supported (e.g. when building with UDK compiler rather than /bin/cc), sometimes vice versa. Even though 5.0.4 was the first release that came with high serial speeds standard, releases back to 5.0.0 could use them if certain patches (or "supplements") were applied to the SIO driver. Plus a lot of SCO installations run third-party drivers. */ #ifdef CK_SCOV5 #ifndef B38400 #define B38400 0000017 #endif /* B38400 */ #ifndef B57600 #define B57600 0000021 #endif /* B57600 */ #ifndef B76800 #define B76800 0000022 #endif /* B76800 */ #ifndef B115200 #define B115200 0000023 #endif /* B115200 */ #ifndef B230400 #define B230400 0000024 #endif /* B230400 */ #ifndef B460800 #define B460800 0000025 #endif /* B460800 */ #ifndef B921600 #define B921600 0000026 #endif /* B921600 */ #endif /* CK_SCOV5 */ /* Plan 9's native speed setting interface lets you set anything you like, but will fail if the hardware doesn't like it, so we allow all the common speeds. */ #ifdef Plan9 #ifndef B50 #define B50 50 #endif /* B50 */ #ifndef B75 #define B75 75 #endif /* B75 */ #ifndef B110 #define B110 110 #endif /* B110 */ #ifndef B134 #define B134 134 #endif /* B134 */ #ifndef B200 #define B200 200 #endif /* B200 */ #ifndef B300 #define B300 300 #endif /* B300 */ #ifndef B1200 #define B1200 1200 #endif /* B1200 */ #ifndef B1800 #define B1800 1800 #endif /* B1800 */ #ifndef B2400 #define B2400 2400 #endif /* B2400 */ #ifndef B4800 #define B4800 4800 #endif /* B4800 */ #ifndef B9600 #define B9600 9600 #endif /* B9600 */ #ifndef B14400 #define B14400 14400 #endif /* B14400 */ #ifndef B19200 #define B19200 19200 #endif /* B19200 */ #ifndef B28800 #define B28800 28800 #endif /* B28800 */ #ifndef B38400 #define B38400 38400 #endif /* B38400 */ #ifndef B57600 #define B57600 57600 #endif /* B57600 */ #ifndef B76800 #define B76800 76800 #endif /* B76800 */ #ifndef B115200 #define B115200 115200 #endif /* B115200 */ #ifndef B230400 #define B230400 230400 #endif /* B230400 */ #ifndef B460800 #define B460800 460800 #endif /* B460800 */ #ifndef B921600 #define B921600 921600 #endif /* B921600 */ #endif /* Plan9 */ /* T T S S P D -- Checks and sets transmission rate. */ /* Call with speed in characters (not bits!) per second. */ /* Returns -1 on failure, 0 if it did nothing, 1 if it changed the speed. */ #ifdef USETCSETSPEED /* The tcsetspeed() / tcgetspeed() interface lets you pass any number at all to be used as a speed to be set, rather than forcing a choice from a predefined list. It seems to be peculiar to UnixWare 7. These are the function codes to be passed to tc[gs]etspeed(), but for some reason they don't seem to be picked up from termios.h. */ #ifndef TCS_ALL #define TCS_ALL 0 #endif /* TCS_ALL */ #ifndef TCS_IN #define TCS_IN 1 #endif /* TCS_IN */ #ifndef TCS_OUT #define TCS_OUT 2 #endif /* TCS_OUT */ #endif /* USETCSETSPEED */ int ttsspd(cps) int cps; { int x; #ifdef POSIX /* Watch out, speed_t should be unsigned, so don't compare with -1, etc... */ speed_t #else int #endif /* POSIX */ s, s2; int ok = 1; /* Speed check result, assume ok */ #ifdef OLINUXHISPEED unsigned int spd_flags = 0; struct serial_struct serinfo; #endif /* OLINUXHISPEED */ debug(F101,"ttsspd cps","",cps); debug(F101,"ttsspd ttyfd","",ttyfd); debug(F101,"ttsspd xlocal","",xlocal); if (ttyfd < 0 || xlocal == 0) /* Don't set speed on console */ return(0); #ifdef NETCONN if (netconn) { #ifdef TN_COMPORT if (istncomport()) return(tnc_set_baud(cps * 10)); else #endif /* TN_COMPORT */ return(0); } #endif /* NETCONN */ #ifdef NETCMD if (ttpipe) return(0); #endif /* NETCMD */ #ifdef NETPTY if (ttpty) return(0); #endif /* NETPTY */ if (cps < 0) return(-1); s = s2 = 0; /* NB: s and s2 might be unsigned */ #ifdef USETCSETSPEED s = cps * 10L; x = tcgetattr(ttyfd,&ttcur); /* Get current speed */ debug(F101,"ttsspd tcgetattr","",x); if (x < 0) return(-1); debug(F101,"ttsspd TCSETSPEED speed","",s); errno = 0; if (s == 8880L) { /* 75/1200 split speed requested */ tcsetspeed(TCS_IN, &ttcur, 1200L); tcsetspeed(TCS_OUT, &ttcur, 75L); } else tcsetspeed(TCS_ALL, &ttcur, s); /* Put new speed in structs */ #ifdef DEBUG if (errno & deblog) { debug(F101,"ttsspd TCSETSPEED errno","",errno); } #endif /* DEBUG */ #ifdef COMMENT tcsetspeed(TCS_ALL, &ttraw, s); tcsetspeed(TCS_ALL, &tttvt, s); tcsetspeed(TCS_ALL, &ttold, s); #else if (s == 8880L) { /* 75/1200 split speed requested */ tcsetspeed(TCS_IN, &ttraw, 1200L); tcsetspeed(TCS_OUT, &ttraw, 75L); tcsetspeed(TCS_IN, &tttvt, 1200L); tcsetspeed(TCS_OUT, &tttvt, 75L); tcsetspeed(TCS_IN, &ttold, 1200L); tcsetspeed(TCS_OUT, &ttold, 75L); } else { tcsetspeed(TCS_ALL, &ttraw, s); tcsetspeed(TCS_ALL, &tttvt, s); tcsetspeed(TCS_ALL, &ttold, s); } #endif /* COMMENT */ x = tcsetattr(ttyfd,TCSADRAIN,&ttcur); /* Set the speed */ debug(F101,"ttsspd tcsetattr","",x); if (x < 0) return(-1); #else /* Not USETCSETSPEED */ /* First check that the given speed is valid. */ switch (cps) { #ifndef MINIX case 0: s = B0; break; case 5: s = B50; break; case 7: s = B75; break; #endif /* MINIX */ case 11: s = B110; break; #ifndef MINIX case 13: s = B134; break; case 15: s = B150; break; case 20: s = B200; break; #endif /* MINIX */ case 30: s = B300; break; #ifndef MINIX case 60: s = B600; break; #endif /* MINIX */ case 120: s = B1200; break; #ifndef MINIX case 180: s = B1800; break; #endif /* MINIX */ case 240: s = B2400; break; case 480: s = B4800; break; #ifndef MINIX case 888: s = B75; s2 = B1200; break; /* 888 = 75/1200 split speed */ #endif /* MINIX */ #ifdef B7200 case 720: s = B7200; break; #endif /* B7200 */ case 960: s = B9600; break; #ifdef B14400 case 1440: s = B14400; break; #endif /* B14400 */ #ifdef B19200 case 1920: s = B19200; break; #else #ifdef EXTA case 1920: s = EXTA; break; #endif /* EXTA */ #endif /* B19200 */ #ifdef B28800 case 2880: s = B28800; break; #endif /* B28800 */ #ifdef B38400 case 3840: s = B38400; #ifdef OLINUXHISPEED spd_flags = ~ASYNC_SPD_MASK; /* Nonzero, but zero flags */ #endif /* OLINUXHISPEED */ break; #else /* B38400 not defined... */ #ifdef EXTB case 3840: s = EXTB; break; #endif /* EXTB */ #endif /* B38400 */ #ifdef HPUX #ifdef _B57600 case 5760: s = _B57600; break; #endif /* _B57600 */ #ifdef _B115200 case 11520: s = _B115200; break; #endif /* _B115200 */ #else #ifdef OLINUXHISPEED /* This bit from : "Only note to make is maybe this: When the ASYNC_SPD_CUST flags are set then setting the speed to 38400 will set the custom speed (and ttgspd returns 38400), but speeds 57600 and 115200 won't work any more because I didn't want to mess up the speed flags when someone is doing sophisticated stuff like custom speeds..." */ case 5760: s = B38400; spd_flags = ASYNC_SPD_HI; break; case 11520: s = B38400; spd_flags = ASYNC_SPD_VHI; break; #else #ifdef B57600 case 5760: s = B57600; break; #endif /* B57600 */ #ifdef B76800 case 7680: s = B76800; break; #endif /* B76800 */ #ifdef B115200 case 11520: s = B115200; break; #endif /* B115200 */ #endif /* OLINUXHISPEED */ #ifdef B153600 case 15360: s = B153600; break; #endif /* B153600 */ #ifdef B230400 case 23040: s = B230400; break; #endif /* B230400 */ #ifdef B307200 case 30720: s = B307200; break; #endif /* B307200 */ #ifdef B460800 case 46080: s = B460800; break; #endif /* 460800 */ #ifdef B921600 case 92160: s = B921600; break; #endif /* B921600 */ #endif /* HPUX */ default: ok = 0; /* Good speed not found, so not ok */ break; } debug(F101,"ttsspd ok","",ok); debug(F101,"ttsspd s","",s); if (!ok) { debug(F100,"ttsspd fails","",0); return(-1); } else { if (!s2) s2 = s; /* Set input speed */ #ifdef Plan9 if (p9ttsspd(cps) < 0) return(-1); #else #ifdef BSD44ORPOSIX x = tcgetattr(ttyfd,&ttcur); /* Get current speed */ debug(F101,"ttsspd tcgetattr","",x); if (x < 0) return(-1); #ifdef OLINUXHISPEED debug(F101,"ttsspd spd_flags","",spd_flags); if (spd_flags && spd_flags != ASYNC_SPD_CUST) { if (ioctl(ttyfd, TIOCGSERIAL, &serinfo) < 0) { debug(F100,"ttsspd: TIOCGSERIAL failed","",0); return(-1); } else debug(F100,"ttsspd: TIOCGSERIAL ok","",0); serinfo.flags &= ~ASYNC_SPD_MASK; serinfo.flags |= (spd_flags & ASYNC_SPD_MASK); if (ioctl(ttyfd, TIOCSSERIAL, &serinfo) < 0) return(-1); } #endif /* OLINUXHISPEED */ cfsetospeed(&ttcur,s); cfsetispeed(&ttcur,s2); cfsetospeed(&ttraw,s); cfsetispeed(&ttraw,s2); cfsetospeed(&tttvt,s); cfsetispeed(&tttvt,s2); cfsetospeed(&ttold,s); cfsetispeed(&ttold,s2); x = tcsetattr(ttyfd,TCSADRAIN,&ttcur); debug(F101,"ttsspd tcsetattr","",x); if (x < 0) return(-1); #else #ifdef ATTSV if (cps == 888) return(-1); /* No split speeds, sorry. */ x = ioctl(ttyfd,TCGETA,&ttcur); debug(F101,"ttsspd TCGETA ioctl","",x); if (x < 0) return(-1); ttcur.c_cflag &= ~CBAUD; ttcur.c_cflag |= s; tttvt.c_cflag &= ~CBAUD; tttvt.c_cflag |= s; ttraw.c_cflag &= ~CBAUD; ttraw.c_cflag |= s; ttold.c_cflag &= ~CBAUD; ttold.c_cflag |= s; x = ioctl(ttyfd,TCSETAW,&ttcur); debug(F101,"ttsspd TCSETAW ioctl","",x); if (x < 0) return(-1); #else #ifdef BELLV10 x = ioctl(ttyfd,TIOCGDEV,&tdcur); debug(F101,"ttsspd TIOCGDEV ioctl","",x); if (x < 0) return(-1); tdcur.ispeed = s2; tdcur.ospeed = s; errno = 0; ok = ioctl(ttyfd,TIOCSDEV,&tdcur); debug(F101,"ttsspd BELLV10 ioctl","",ok); if (ok < 0) { perror(ttnmsv); debug(F101,"ttsspd BELLV10 errno","",ok); return(-1); } #else x = gtty(ttyfd,&ttcur); debug(F101,"ttsspd gtty","",x); if (x < 0) return(-1); ttcur.sg_ospeed = s; ttcur.sg_ispeed = s2; tttvt.sg_ospeed = s; tttvt.sg_ispeed = s2; ttraw.sg_ospeed = s; ttraw.sg_ispeed = s2; ttold.sg_ospeed = s; ttold.sg_ispeed = s2; x = stty(ttyfd,&ttcur); debug(F101,"ttsspd stty","",x); if (x < 0) return(-1); #endif /* BELLV10 */ #endif /* ATTSV */ #endif /* BSD44ORPOSIX */ #endif /* Plan9 */ } return(1); /* Return 1 = success. */ #endif /* USETCSETSPEED */ } #endif /* NOLOCAL */ /* C O N G S P D - Get speed of console terminal */ long congspd() { /* This is a disgusting hack. The right way to do this would be to pass an argument to ttgspd(), but then we'd need to change the Kermit API and all of the ck?tio.c modules. (Currently used only for rlogin.) */ int t1; long spd; #ifdef NETCONN int t2 = netconn; netconn = 0; #endif /* NETCONN */ t1 = ttyfd; ttyfd = -1; spd = ttgspd(); debug(F101,"congspd","",spd); #ifdef NETCONN netconn = t2; #endif /* NETCONN */ ttyfd = t1; return(spd); } /* T T S P D L I S T -- Get list of serial speeds allowed on this platform */ #define NSPDLIST 64 static long spdlist[NSPDLIST]; /* As written, this picks up the speeds known at compile time, and thus apply to the system where C-Kermit was built, rather than to the one where it is running. Suggestions for improvement are always welcome. */ long * ttspdlist() { int i; for (i = 0; i < NSPDLIST; i++) /* Initialize the list */ spdlist[i] = -1L; i = 1; #ifdef USETCSETSPEED /* No way to find out what's legal */ debug(F100,"ttspdlist USETCSETSPEED","",0); spdlist[i++] = 50L; #ifndef UW7 spdlist[i++] = 75L; #endif /* UW7 */ spdlist[i++] = 110L; #ifndef UW7 spdlist[i++] = 134L; #endif /* UW7 */ spdlist[i++] = 150L; spdlist[i++] = 200L; spdlist[i++] = 300L; spdlist[i++] = 600L; spdlist[i++] = 1200L; spdlist[i++] = 1800L; spdlist[i++] = 2400L; spdlist[i++] = 4800L; spdlist[i++] = 8880L; spdlist[i++] = 9600L; spdlist[i++] = 14400L; spdlist[i++] = 19200L; spdlist[i++] = 28800L; #ifndef UW7 spdlist[i++] = 33600L; #endif /* UW7 */ spdlist[i++] = 38400L; spdlist[i++] = 57600L; spdlist[i++] = 76800L; spdlist[i++] = 115200L; #ifndef UW7 spdlist[i++] = 153600L; spdlist[i++] = 230400L; spdlist[i++] = 307200L; spdlist[i++] = 460800L; spdlist[i++] = 921600L; #endif /* UW7 */ #else /* USETCSETSPEED */ debug(F100,"ttspdlist no USETCSETSPEED","",0); #ifdef B50 debug(F101,"ttspdlist B50","",B50); spdlist[i++] = 50L; #endif /* B50 */ #ifdef B75 debug(F101,"ttspdlist B75","",B75); spdlist[i++] = 75L; #endif /* B75 */ #ifdef B110 debug(F101,"ttspdlist B110","",B110); spdlist[i++] = 110L; #endif /* B110 */ #ifdef B134 debug(F101,"ttspdlist B134","",B134); spdlist[i++] = 134L; #endif /* B134 */ #ifdef B150 debug(F101,"ttspdlist B150","",B150); spdlist[i++] = 150L; #endif /* B150 */ #ifdef B200 debug(F101,"ttspdlist B200","",B200); spdlist[i++] = 200L; #endif /* B200 */ #ifdef B300 debug(F101,"ttspdlist B300","",B300); spdlist[i++] = 300L; #endif /* B300 */ #ifdef B600 debug(F101,"ttspdlist B600","",B600); spdlist[i++] = 600L; #endif /* B600 */ #ifdef B1200 debug(F101,"ttspdlist B1200","",B1200); spdlist[i++] = 1200L; #endif /* B1200 */ #ifdef B1800 debug(F101,"ttspdlist B1800","",B1800); spdlist[i++] = 1800L; #endif /* B1800 */ #ifdef B2400 debug(F101,"ttspdlist B2400","",B2400); spdlist[i++] = 2400L; #endif /* B2400 */ #ifdef B4800 debug(F101,"ttspdlist B4800","",B4800); spdlist[i++] = 4800L; #endif /* B4800 */ #ifdef B9600 debug(F101,"ttspdlist B9600","",B9600); spdlist[i++] = 9600L; #endif /* B9600 */ #ifdef B14400 debug(F101,"ttspdlist B14400","",B14400); spdlist[i++] = 14400L; #endif /* B14400 */ #ifdef B19200 debug(F101,"ttspdlist B19200","",B19200); spdlist[i++] = 19200L; #else #ifdef EXTA debug(F101,"ttspdlist EXTA","",EXTA); spdlist[i++] = 19200L; #endif /* EXTA */ #endif /* B19200 */ #ifdef B28800 debug(F101,"ttspdlist B28800","",B28800); spdlist[i++] = 28800L; #endif /* B28800 */ #ifdef B33600 debug(F101,"ttspdlist B33600","",B33600); spdlist[i++] = 33600L; #endif /* B33600 */ #ifdef B38400 debug(F101,"ttspdlist B38400","",B38400); spdlist[i++] = 38400L; #else #ifdef EXTB debug(F101,"ttspdlist EXTB","",EXTB); spdlist[i++] = 38400L; #endif /* EXTB */ #endif /* B38400 */ #ifdef _B57600 debug(F101,"ttspdlist _B57600","",_B57600); spdlist[i++] = 57600L; #else #ifdef B57600 debug(F101,"ttspdlist B57600","",B57600); spdlist[i++] = 57600L; #endif /* B57600 */ #endif /* _B57600 */ #ifdef B76800 debug(F101,"ttspdlist B76800","",B76800); spdlist[i++] = 76800L; #endif /* B76800 */ #ifdef _B115200 debug(F101,"ttspdlist _B115200","",_B115200); spdlist[i++] = 115200L; #else #ifdef B115200 debug(F101,"ttspdlist B115200","",B115200); spdlist[i++] = 115200L; #endif /* B115200 */ #endif /* _B115200 */ #ifdef B153600 debug(F101,"ttspdlist B153600","",B153600); spdlist[i++] = 153600L; #endif /* B153600 */ #ifdef B230400 debug(F101,"ttspdlist B230400","",B230400); spdlist[i++] = 230400L; #endif /* B230400 */ #ifdef B307200 debug(F101,"ttspdlist B307200","",B307200); spdlist[i++] = 307200L; #endif /* B307200 */ #ifdef B460800 debug(F101,"ttspdlist B460800","",B460800); spdlist[i++] = 460800L; #endif /* B460800 */ #ifdef B921600 debug(F101,"ttspdlist B921600","",B921600); spdlist[i++] = 921600L; #endif /* B921600 */ #endif /* USETCSETSPEED */ spdlist[0] = i - 1; /* Return count in 0th element */ debug(F111,"ttspdlist spdlist","0",spdlist[0]); return((long *)spdlist); } /* T T G S P D - Get speed of currently selected tty line */ /* Unreliable. After SET LINE, it returns an actual speed, but not necessarily the real speed. On some systems, it returns the line's nominal speed, from /etc/ttytab. Even if you SET SPEED to something else, this function might not notice. */ long ttgspd() { /* Get current serial device speed */ #ifdef NOLOCAL return(-1L); #else #ifdef POSIX speed_t /* Should be unsigned */ #else int /* Isn't unsigned */ #endif /* POSIX */ s; int x; long ss; #ifdef OLINUXHISPEED unsigned int spd_flags = 0; struct serial_struct serinfo; #endif /* OLINUXHISPEED */ #ifdef NETCONN if (netconn) { #ifdef TN_COMPORT if (istncomport()) return(tnc_get_baud()); else #endif /* TN_COMPORT */ return(-1); /* -1 if network connection */ } #endif /* NETCONN */ #ifdef NETCMD if (ttpipe) return(-1); #endif /* NETCMD */ #ifdef NETPTY if (ttpty) return(-1); #endif /* NETPTY */ debug(F101,"ttgspd ttyfd","",ttyfd); #ifdef USETCSETSPEED x = tcgetattr(ttyfd,&ttcur); /* Get current speed */ debug(F101,"ttgspd tcgetattr","",x); if (x < 0) return(-1); errno = 0; s = tcgetspeed(TCS_ALL, &ttcur); debug(F101,"ttsspd TCGETSPEED speed","",s); if (s == 0) { long s1, s2; s1 = tcgetspeed(TCS_IN, &ttcur); s2 = tcgetspeed(TCS_OUT, &ttcur); if (s1 == 1200L && s2 == 75L) return(8880L); } #ifdef DEBUG if (errno & deblog) { debug(F101,"ttsspd TCGETSPEED errno","",errno); } #endif /* DEBUG */ return(s); #else /* Not USETCSETSPEED */ #ifdef Plan9 if (ttyfd < 0) ss = -1; else ss = ttylastspeed; #else #ifdef OLINUXHISPEED debug(F100,"ttgspd Linux OLINUXHISPEED","",0); #endif /* OLINUXHISPEED */ if (ttyfd < 0) { #ifdef BSD44ORPOSIX s = cfgetospeed(&ccold); debug(F101,"ttgspd cfgetospeed 1 POSIX","",s); #else #ifdef ATTSV s = ccold.c_cflag & CBAUD; debug(F101,"ttgspd c_cflag CBAUD 1 ATTSV","",s); #else s = ccold.sg_ospeed; /* (obtained by congm()) */ debug(F101,"ttgspd sg_ospeed 1","",s); #endif /* ATTSV */ #endif /* BSD44POSIX */ } else { #ifdef BSD44ORPOSIX if (tcgetattr(ttyfd,&ttcur) < 0) return(-1); s = cfgetospeed(&ttcur); debug(F101,"ttgspd cfgetospeed 2 BSDORPOSIX","",s); #ifdef OLINUXHISPEED if (ioctl(ttyfd,TIOCGSERIAL,&serinfo) > -1) spd_flags = serinfo.flags & ASYNC_SPD_MASK; debug(F101,"ttgspd spd_flags","",spd_flags); #endif /* OLINUXHISPEED */ #else #ifdef ATTSV x = ioctl(ttyfd,TCGETA,&ttcur); debug(F101,"ttgspd ioctl 2 ATTSV x","",x); debug(F101,"ttgspd ioctl 2 ATTSV errno","",errno); if (x < 0) return(-1); s = ttcur.c_cflag & CBAUD; debug(F101,"ttgspd ioctl 2 ATTSV speed","",s); #else #ifdef BELLV10 x = ioctl(ttyfd,TIOCGDEV,&tdcur); debug(F101,"ttgspd ioctl 2 BELLV10 x","",x); if (x < 0) return(-1); s = tdcur.ospeed; debug(F101,"ttgspd ioctl 2 BELLV10 speed","",s); #else x = gtty(ttyfd,&ttcur); debug(F101,"ttgspd gtty 2 x","",x); debug(F101,"ttgspd gtty 2 errno","",errno); if (x < 0) return(-1); s = ttcur.sg_ospeed; debug(F101,"ttgspd gtty 2 speed","",s); #endif /* BELLV10 */ #endif /* ATTSV */ #endif /* BSD44ORPOSIX */ } debug(F101,"ttgspd code","",s); #ifdef OLINUXHISPEED debug(F101,"ttgspd spd_flags","",spd_flags); #endif /* OLINUXHISPEED */ switch (s) { #ifdef B0 case B0: ss = 0L; break; #endif /* B0 */ #ifndef MINIX /* MINIX defines the Bxx symbols to be bps/100, so B50==B75, B110==B134==B150, etc, making for many "duplicate case in switch" errors, which are fatal. */ #ifdef B50 case B50: ss = 50L; break; #endif /* B50 */ #ifdef B75 case B75: ss = 75L; break; #endif /* B75 */ #endif /* MINIX */ #ifdef B110 case B110: ss = 110L; break; #endif /* B110 */ #ifndef MINIX #ifdef B134 case B134: ss = 134L; break; #endif /* B134 */ #ifdef B150 case B150: ss = 150L; break; #endif /* B150 */ #endif /* MINIX */ #ifdef B200 case B200: ss = 200L; break; #endif /* B200 */ #ifdef B300 case B300: ss = 300L; break; #endif /* B300 */ #ifdef B600 case B600: ss = 600L; break; #endif /* B600 */ #ifdef B1200 case B1200: ss = 1200L; break; #endif /* B1200 */ #ifdef B1800 case B1800: ss = 1800L; break; #endif /* B1800 */ #ifdef B2400 case B2400: ss = 2400L; break; #endif /* B2400 */ #ifdef B4800 case B4800: ss = 4800L; break; #endif /* B4800 */ #ifdef B7200 case B7200: ss = 7200L; break; #endif /* B7200 */ #ifdef B9600 case B9600: ss = 9600L; break; #endif /* B9600 */ #ifdef B19200 case B19200: ss = 19200L; break; #else #ifdef EXTA case EXTA: ss = 19200L; break; #endif /* EXTA */ #endif /* B19200 */ #ifndef MINIX #ifdef B38400 case B38400: ss = 38400L; #ifdef OLINUXHISPEED switch(spd_flags) { case ASYNC_SPD_HI: ss = 57600L; break; case ASYNC_SPD_VHI: ss = 115200L; break; } #endif /* OLINUXHISPEED */ break; #else #ifdef EXTB case EXTB: ss = 38400L; break; #endif /* EXTB */ #endif /* B38400 */ #endif /* MINIX */ #ifdef HPUX #ifdef _B57600 case _B57600: ss = 57600L; break; #endif /* _B57600 */ #ifdef _B115200 case _B115200: ss = 115200L; break; #endif /* _B115200 */ #else #ifdef B57600 case B57600: ss = 57600L; break; #endif /* B57600 */ #ifdef B76800 case B76800: ss = 76800L; break; #endif /* B76800 */ #ifdef B115200 case B115200: ss = 115200L; break; #endif /* B115200 */ #ifdef B153600 case B153600: ss = 153600L; break; #endif /* B153600 */ #ifdef B230400 case B230400: ss = 230400L; break; #endif /* B230400 */ #ifdef B307200 case B307200: ss = 307200L; break; #endif /* B307200 */ #ifdef B460800 case B460800: ss = 460800L; break; #endif /* B460800 */ #endif /* HPUX */ #ifdef B921600 case B921600: ss = 921600L; break; #endif /* B921600 */ default: ss = -1; break; } #endif /* Plan9 */ debug(F101,"ttgspd speed","",ss); return(ss); #endif /* USETCSETSPEED */ #endif /* NOLOCAL */ } #ifdef MINIX2 /* Another hack alert */ #define MINIX #endif /* MINIX2 */ /* FIONREAD data type... This has been defined as "long" for many, many years, and it worked OK until 64-bit platforms appeared. Thus we use int for 64-bit platforms, but keep long for the others. If we changed the default PEEKTYPE to int, this would probably break 16-bit builds (note that sizeof(long) == sizeof(int) on most 32-bit platforms), many of which we have no way of testing any more. Therefore, do not change the default definition of PEEKTYPE -- only add exceptions to it as needed. */ #ifdef COHERENT #ifdef FIONREAD #undef FIONREAD #endif /* FIONREAD */ /* #define FIONREAD TIOCQUERY */ /* #define PEEKTYPE int */ #else /* Not COHERENT... */ #ifdef OSF32 /* Digital UNIX 3.2 or higher */ #define PEEKTYPE int #else #define PEEKTYPE long /* Elsewhere (see notes above) */ #endif /* OSF32 */ #endif /* COHERENT */ /* ckumyr.c by Kristoffer Eriksson, ske@pkmab.se, 15 Mar 1990. */ #ifdef MYREAD /* Private buffer for myread() and its companions. Not for use by anything * else. ttflui() is allowed to reset them to initial values. ttchk() is * allowed to read my_count. * * my_item is an index into mybuf[]. Increment it *before* reading mybuf[]. * * A global parity mask variable could be useful too. We could use it to * let myread() strip the parity on its own, instead of stripping sign * bits as it does now. */ #ifdef BIGBUFOK #define MYBUFLEN 32768 #else #ifdef pdp11 #define MYBUFLEN 256 #else #define MYBUFLEN 1024 #endif /* pdp11 */ #endif /* BIGBUFOK */ #ifdef ANYX25 #undef MYBUFLEN #define MYBUFLEN 256 /* On X.25 connections, there is an extra control byte at the beginning. */ static CHAR x25buf[MYBUFLEN+1]; /* Communication device input buffer */ static CHAR *mybuf = x25buf+1; #else static CHAR mybuf[MYBUFLEN]; #endif /* ANYX25 */ static int my_count = 0; /* Number of chars still in mybuf */ static int my_item = -1; /* Last index read from mybuf[] */ /* T T P E E K -- Peek into our internal communications input buffers. */ /* NOTE: This routine is peculiar to UNIX, and is used only by the select()-based CONNECT module, ckucns.c. It need not be replicated in the ck?tio.c of other platforms. */ int ttpeek() { #ifdef TTLEBUF int rc = 0; if (ttpush >= 0) rc++; rc += le_inbuf(); if (rc > 0) return(rc); else #endif /* TTLEBUF */ #ifdef MYREAD return(my_count); #else return(0); #endif /* MYREAD */ } /* myread() -- Efficient read of one character from communications line. * * NOTE: myread() and its helpers mygetbuf() and myfillbuf() return raw * bytes from connection, so when the connection is encrypted, these bytes * must be decrypted. * * Uses a private buffer to minimize the number of expensive read() system * calls. Essentially performs the equivalent of read() of 1 character, which * is then returned. By reading all available input from the system buffers * to the private buffer in one chunk, and then working from this buffer, the * number of system calls is reduced in any case where more than one character * arrives during the processing of the previous chunk, for instance high * baud rates or network type connections where input arrives in packets. * If the time needed for a read() system call approaches the time for more * than one character to arrive, then this mechanism automatically compensates * for that by performing bigger read()s less frequently. If the system load * is high, the same mechanism compensates for that too. * * myread() is a macro that returns the next character from the buffer. If the * buffer is empty, mygetbuf() is called. See mygetbuf() for possible error * returns. * * This should be efficient enough for any one-character-at-a-time loops. * For even better efficiency you might use memcpy()/bcopy() or such between * buffers (since they are often better optimized for copying), but it may not * be worth it if you have to take an extra pass over the buffer to strip * parity and check for CTRL-C anyway. * * Note that if you have been using myread() from another program module, you * may have some trouble accessing this macro version and the private variables * it uses. In that case, just add a function in this module, that invokes the * macro. */ #define myread() (--my_count < 0 ? mygetbuf() : 255 & (int)mybuf[++my_item]) /* Specification: Push back up to one character onto myread()'s queue. * * This implementation: Push back characters into mybuf. At least one character * must have been read through myread() before myunrd() may be used. After * EOF or read error, again, myunrd() can not be used. Sometimes more than * one character can be pushed back, but only one character is guaranteed. * Since a previous myread() must have read its character out of mybuf[], * that guarantees that there is space for at least one character. If push * back was really needed after EOF, a small addition could provide that. * * As of 02/2007 myunrd() is used by ttinl(). */ VOID #ifdef CK_ANSIC myunrd(CHAR ch) #else myunrd(ch) CHAR ch; #endif /* CK_ANSIC */ { if (my_item >= 0) { mybuf[my_item--] = ch; ++my_count; } } /* T T P U S H B A C K -- Put n bytes back into the myread buffer */ static CHAR * pushbuf = NULL; /* static int pushed = 0; */ int ttpushback(s,n) CHAR * s; int n; { debug(F101,"ttpushback n","",n); if (pushbuf || n > MYBUFLEN || n < 1) return(-1); debug(F101,"ttpushback my_count","",my_count); if (my_count > 0) { if (!(pushbuf = (CHAR *)malloc(n+1))) return(-1); memcpy(pushbuf,mybuf,my_count); /* pushed = my_count; */ /* (set but never used) */ } memcpy(mybuf,s,n); my_count = n; my_item = -1; return(0); } /* mygetbuf() -- Fill buffer for myread() and return first character. * * This function is what myread() uses when it can't get the next character * directly from its buffer. First, it calls a system dependent myfillbuf() * to read at least one new character into the buffer, and then it returns * the first character just as myread() would have done. This function also * is responsible for all error conditions that myread() can indicate. * * Returns: When OK => a positive character, 0 or greater. * When EOF => -2. * When error => -3, error code in errno. * * Older myread()s additionally returned -1 to indicate that there was nothing * to read, upon which the caller would call myread() again until it got * something. The new myread()/mygetbuf() always gets something. If it * doesn't, then make it do so! Any program that actually depends on the old * behaviour will break. * * The older version also used to return -2 both for EOF and other errors, * and used to set errno to 9999 on EOF. The errno stuff is gone, EOF and * other errors now return different results, although Kermit currently never * checks to see which it was. It just disconnects in both cases. * * Kermit lets the user use the quit key to perform some special commands * during file transfer. This causes read(), and thus also mygetbuf(), to * finish without reading anything and return the EINTR error. This should * be checked by the caller. Mygetbuf() could retry the read() on EINTR, * but if there is nothing to read, this could delay Kermit's reaction to * the command, and make Kermit appear unresponsive. * * The debug() call should be removed for optimum performance. */ int mygetbuf() { int x; errno = 0; #ifdef DEBUG if (deblog && my_count > 0) debug(F101,"mygetbuf IMPROPERLY CALLED with my_count","",my_count); #endif /* DEBUG */ if (my_count <= 0) my_count = myfillbuf(); #ifdef DEBUG #ifdef COMMENT if (deblog) debug(F101, "mygetbuf read", "", my_count); #else /* COMMENT */ ckhexdump("mygetbuf read", mybuf, my_count); #endif /* COMMENT */ #endif /* DEBUG */ x = my_count; if (my_count <= 0) { my_count = 0; my_item = -1; debug(F101,"mygetbuf errno","",errno); #ifdef TCPSOCKET if (netconn && ttnet == NET_TCPB && errno != 0) { if (errno != EINTR) { debug(F101,"mygetbuf TCP error","",errno); ttclos(0); /* Close the connection. */ } return(-3); } #endif /* TCPSOCKET */ if (!netconn && xlocal && errno) { if (errno != EINTR) { debug(F101,"mygetbuf SERIAL error","",errno); x = -3; ttclos(0); /* Close the connection. */ } } return((x < 0) ? -3 : -2); } --my_count; return((unsigned)(0xff & mybuf[my_item = 0])); } /* myfillbuf(): * System-dependent read() into mybuf[], as many characters as possible. * * Returns: OK => number of characters read, always more than zero. * EOF => 0 * Error => -1, error code in errno. * * If there is input available in the system's buffers, all of it should be * read into mybuf[] and the function return immediately. If no input is * available, it should wait for a character to arrive, and return with that * one in mybuf[] as soon as possible. It may wait somewhat past the first * character, but be aware that any such delay lengthens the packet turnaround * time during kermit file transfers. Should never return with zero characters * unless EOF or irrecoverable read error. * * Correct functioning depends on the correct tty parameters being used. * Better control of current parameters is required than may have been the * case in older Kermit releases. For instance, O_NDELAY (or equivalent) can * no longer be sometimes off and sometimes on like it used to, unless a * special myfillbuf() is written to handle that. Otherwise the ordinary * myfillbuf()s may think they have come to EOF. * * If your system has a facility to directly perform the functioning of * myfillbuf(), then use it. If the system can tell you how many characters * are available in its buffers, then read that amount (but not less than 1). * If the system can return a special indication when you try to read without * anything to read, while allowing you to read all there is when there is * something, you may loop until there is something to read, but probably that * is not good for the system load. */ #ifdef SVORPOSIX /* This is for System III/V with VMIN>0, VTIME=0 and O_NDELAY off, * and CLOCAL set any way you like. This way, read() will do exactly * what is required by myfillbuf(): If there is data in the buffers * of the O.S., all available data is read into mybuf, up to the size * of mybuf. If there is none, the first character to arrive is * awaited and returned. */ int myfillbuf() { int fd, n; #ifdef NETCMD if (ttpipe) fd = fdin; else #endif /* NETCMD */ fd = ttyfd; #ifdef sxaE50 /* From S. Dezawa at Fujifilm in Japan. I don't know why this is */ /* necessary for the sxa E50, but it is. */ return read(fd, mybuf, 255); #else #ifdef BEOSORBEBOX while (1) { #ifdef NETCONN if (netconn) { n = netxin(sizeof(mybuf), (char *)mybuf); debug(F101,"BEBOX SVORPOSIX network myfillbuf","",n); } else #endif /* NETCONN */ n = read(fd, mybuf, sizeof(mybuf)); debug(F101,"BEBOX SVORPOSIX notnet myfillbuf","",n); if (n > 0) return(n); snooze(1000.0); } #else /* BEOSORBEBOX */ errno = 0; /* debug(F101,"SVORPOSIX myfillbuf calling read() fd","",fd); */ #ifdef IBMX25 if (netconn && (nettype == NET_IX25)) { /* can't use sizeof because mybuf is a pointer, and not an array! */ n = x25xin( MYBUFLEN, mybuf ); } else #endif /* IBMX25 */ #ifdef CK_SSL if (ssl_active_flag || tls_active_flag) { int error, n = 0; debug(F100,"myfillbuf calling SSL_read() fd","",0); while (n == 0) { if (ssl_active_flag) n = SSL_read(ssl_con, (char *)mybuf, sizeof(mybuf)); else if (tls_active_flag) n = SSL_read(tls_con, (char *)mybuf, sizeof(mybuf)); else break; switch (SSL_get_error(ssl_active_flag?ssl_con:tls_con,n)) { case SSL_ERROR_NONE: if (n > 0) return(n); if (n < 0) return(-2); msleep(50); break; case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_READ: return(-1); case SSL_ERROR_SYSCALL: if (n != 0) return(-1); case SSL_ERROR_WANT_X509_LOOKUP: case SSL_ERROR_SSL: case SSL_ERROR_ZERO_RETURN: default: ttclos(0); return(-3); } } } #endif /* CK_SSL */ #ifdef CK_KERBEROS #ifdef KRB4 #ifdef RLOGCODE if (ttnproto == NP_EK4LOGIN) { debug(F101,"myfillbuf calling krb4_des_read() fd","",ttyfd); if ((n = krb4_des_read(ttyfd,(char *)mybuf,sizeof(mybuf))) < 0) return(-3); else return(n); } #endif /* RLOGCODE */ #endif /* KRB4 */ #ifdef KRB5 #ifdef RLOGCODE if (ttnproto == NP_EK5LOGIN) { debug(F101,"myfillbuf calling krb5_des_read() fd","",ttyfd); if ((n = krb5_des_read(ttyfd,(char *)mybuf,sizeof(mybuf),0)) < 0) return(-3); else return(n); } #endif /* RLOGCODE */ #ifdef KRB5_U2U if (ttnproto == NP_K5U2U) { debug(F101,"myfillbuf calling krb5_u2u_read() fd","",ttyfd); if ((n = krb5_u2u_read(ttyfd,(char *)mybuf,sizeof(mybuf))) < 0) return(-3); else return(n); } #endif /* KRB5_U2U */ #endif /* KRB5 */ #endif /* CK_KERBEROS */ #ifdef NETPTY #ifdef HAVE_PTYTRAP /* Special handling for HP-UX pty i/o */ ptyread: if (ttpty && pty_trap_pending(ttyfd) > 0) { debug(F101,"myfillbuf calling pty_trap_handler() fd","",ttyfd); if (pty_trap_handler(ttyfd) > 0) { ttclos(0); return(-3); } } #endif /* HAVE_PTYTRAP */ #endif /* NETPTY */ debug(F101,"myfillbuf calling read() fd","",ttyfd); n = read(fd, mybuf, sizeof(mybuf)); debug(F101,"SVORPOSIX myfillbuf read","",n); debug(F101,"SVORPOSIX myfillbuf errno","",errno); debug(F101,"SVORPOSIX myfillbuf ttcarr","",ttcarr); if (n < 1) { #ifdef NETPTY #ifdef HAVE_PTYTRAP /* When we have a PTY trap in place the connection cannot */ /* be closed until the trap receives a close indication. */ if (n == 0 && ttpty) goto ptyread; #endif /* HAVE_PTYTRAP */ #endif /* NETPTY */ return(-3); } return(n); #endif /* BEOSORBEBOX */ #endif /* sxaE50 */ } #else /* not AT&T or POSIX */ #ifdef aegis /* This is quoted from the old myread(). The semantics seem to be * alright, but maybe errno would not need to be set even when * there is no error? I don't know aegis. */ int myfillbuf() { int count; #ifdef NETCMD if (ttpipe) fd = fdin; else #endif /* NETCMD */ fd = ttyfd; count = ios_$get((short)fd, ios_$cond_opt, mybuf, 256L, st); errno = EIO; if (st.all == ios_$get_conditional_failed) /* get at least one */ count = ios_$get((short)fd, 0, mybuf, 1L, st); if (st.all == ios_$end_of_file) return(-3); else if (st.all != status_$ok) { errno = EIO; return(-1); } return(count > 0 ? count : -3); } #else /* !aegis */ #ifdef FIONREAD /* This is for systems with FIONREAD. FIONREAD returns the number * of characters available for reading. If none are available, wait * until something arrives, otherwise return all there is. */ int myfillbuf() { PEEKTYPE avail = 0; int x, fd; #ifdef NETCMD if (ttpipe) fd = fdin; else #endif /* NETCMD */ fd = ttyfd; #ifdef SUNX25 /* SunLink X.25 support in this routine from Stefaan A. Eeckels, Eurostat (CEC). Depends on SunOS having FIONREAD, not because we use it, but just so this code is grouped correctly within the #ifdefs. Let's hope Solaris keeps it. We call x25xin() instead of read() so that Q-Bit packets, which contain X.25 service-level information (e.g. PAD parameter changes), can be processed transparently to the upper-level code. This is a blocking read, and so we depend on higher-level code (such as ttinc()) to set any necessary alarms. */ extern int nettype; if (netconn && nettype == NET_SX25) { while ((x = x25xin(sizeof(x25buf), x25buf)) < 1) ; return(x - 1); /* "-1" compensates for extra status byte */ } #endif /* SUNX25 */ #ifdef CK_SSL if (ssl_active_flag || tls_active_flag) { int error, n = 0; while (n == 0) { if (ssl_active_flag) n = SSL_read(ssl_con, (char *)mybuf, sizeof(mybuf)); else n = SSL_read(tls_con, (char *)mybuf, sizeof(mybuf)); switch (SSL_get_error(ssl_active_flag?ssl_con:tls_con,n)) { case SSL_ERROR_NONE: if (n > 0) return(n); if (n < 0) return(-2); msleep(50); break; case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_READ: return(-1); case SSL_ERROR_SYSCALL: if (n != 0) return(-1); case SSL_ERROR_WANT_X509_LOOKUP: case SSL_ERROR_SSL: case SSL_ERROR_ZERO_RETURN: default: ttclos(0); return(-2); } } } #endif /* CK_SSL */ #ifdef CK_KERBEROS #ifdef KRB4 #ifdef RLOGCODE if (ttnproto == NP_EK4LOGIN) { if ((x = krb4_des_read(ttyfd,mybuf,sizeof(mybuf))) < 0) return(-1); else return(x); } #endif /* RLOGCODE */ #endif /* KRB4 */ #ifdef KRB5 #ifdef RLOGCODE if (ttnproto == NP_EK5LOGIN) { if ((x = krb5_des_read(ttyfd,mybuf,sizeof(mybuf),0)) < 0) return(-1); else return(x); } #endif /* RLOGCODE */ #ifdef KRB5_U2U if (ttnproto == NP_K5U2U) { if ((x = krb5_u2u_read(ttyfd,mybuf,sizeof(mybuf))) < 0) return(-1); else return(x); } #endif /* KRB5_U2U */ #endif /* KRB5 */ #endif /* CK_KERBEROS */ errno = 0; debug(F101,"myfillbuf calling FIONREAD ioctl","",xlocal); x = ioctl(fd, FIONREAD, &avail); #ifdef DEBUG if (deblog) { debug(F101,"myfillbuf FIONREAD","",x); debug(F101,"myfillbuf FIONREAD avail","",avail); debug(F101,"myfillbuf FIONREAD errno","",errno); } #endif /* DEBUG */ if (x < 0 || avail == 0) avail = 1; if (avail > MYBUFLEN) avail = MYBUFLEN; errno = 0; x = read(fd, mybuf, (int) avail); #ifdef DEBUG if (deblog) { debug(F101,"myfillbuf avail","",avail); debug(F101,"myfillbuf read","",x); debug(F101,"myfillbuf read errno","",errno); if (x > 0) ckhexdump("myfillbuf mybuf",mybuf,x); } #endif /* DEBUG */ if (x < 1) x = -3; /* read 0 == connection loss */ return(x); } #else /* !FIONREAD */ /* Add other systems here, between #ifdef and #else, e.g. NETCONN. */ /* When there is no other possibility, read 1 character at a time. */ int myfillbuf() { int x; #ifdef CK_SSL if (ssl_active_flag || tls_active_flag) { int error, n = 0; while (n == 0) { if (ssl_active_flag) n = SSL_read(ssl_con, (char *)mybuf, sizeof(mybuf)); else count = SSL_read(tls_con, (char *)mybuf, sizeof(mybuf)); switch (SSL_get_error(ssl_active_flag?ssl_con:tls_con,n)) { case SSL_ERROR_NONE: if (n > 0) return(n); if (n < 0) return(-2); msleep(50); break; case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_READ: return(-1); case SSL_ERROR_SYSCALL: if (n != 0) return(-1); case SSL_ERROR_WANT_X509_LOOKUP: case SSL_ERROR_SSL: case SSL_ERROR_ZERO_RETURN: default: ttclos(0); return(-2); } } } #endif /* CK_SSL */ #ifdef CK_KERBEROS #ifdef KRB4 #ifdef RLOGCODE if (ttnproto == NP_EK4LOGIN) { if ((len = krb4_des_read(ttyfd,mybuf,sizeof(mybuf))) < 0) return(-1); else return(len); } #endif /* RLOGCODE */ #endif /* KRB4 */ #ifdef KRB5 #ifdef RLOGCODE if (ttnproto == NP_EK5LOGIN) { if ((len = krb5_des_read(ttyfd,mybuf,sizeof(mybuf),0)) < 0) return(-1); else return(len); } #endif /* RLOGCODE */ #ifdef KRB5_U2U if (ttnproto == NP_K5U2U) { if ((len = krb5_u2u_read(ttyfd,mybuf,sizeof(mybuf))) < 0) return(-1); else return(len); } #endif /* KRB5_U2U */ #endif /* KRB5 */ #endif /* CK_KERBEROS */ #ifdef NETCMD if (ttpipe) fd = fdin; else #endif /* NETCMD */ fd = ttyfd; x = read(fd, mybuf, 1); return(x > 0 ? x : -3); } #endif /* !FIONREAD */ #endif /* !aegis */ #endif /* !ATTSV */ #endif /* MYREAD */ /* T T _ T N O P T -- Handle Telnet negotions in incoming data */ /* Call with the IAC that was encountered. Returns: -3: If connection has dropped or gone bad. -2: On Telnet protocol error resulting in inconsistent states. 0: If negotiation OK and caller has nothing to do. 1: If packet start character has changed (new value is in global stchr). 255: If there was a quoted IAC as data. or: Not at all if we got a legitimate Telnet Logout request. */ #ifdef TCPSOCKET static int tt_tnopt(n) int n; { /* Handle Telnet options */ /* In case caller did not already check these conditions... */ if (n == IAC && ((xlocal && netconn && IS_TELNET()) || (!xlocal && sstelnet))) { extern int server; int tx = 0; debug(F100,"ttinl calling tn_doop()","",0); tx = tn_doop((CHAR)(n & 0xff),duplex,ttinc); debug(F111,"ttinl tn_doop() returned","tx",tx); switch (tx) { case 0: return(0); case -1: /* I/O error */ ttimoff(); /* Turn off timer */ return(-3); case -2: /* Connection failed. */ case -3: ttimoff(); /* Turn off timer */ ttclos(0); return(-3); case 1: /* ECHO change */ duplex = 1; return(0); case 2: /* ECHO change */ duplex = 0; return(0); case 3: /* Quoted IAC */ n = 255; return((unsigned)255); #ifdef IKS_OPTION case 4: { if (TELOPT_SB(TELOPT_KERMIT).kermit.u_start && server #ifdef IKSD && !inserver #endif /* IKSD */ ) { /* Remote in Server mode */ ttimoff(); /* Turn off timer */ debug(F100,"u_start and !inserver","",0); return(-2); /* End server mode */ } else if (!TELOPT_SB(TELOPT_KERMIT).kermit.me_start && server ) { /* I'm no longer in Server Mode */ debug(F100,"me_start and server","",0); ttimoff(); return(-2); } return(0); } case 5: { /* Start character change */ /* extern CHAR stchr; */ /* start = stchr; */ return(1); } #endif /* IKS_OPTION */ case 6: /* Remote Logout */ ttimoff(); ttclos(0); #ifdef IKSD if (inserver && !local) doexit(GOOD_EXIT,0); else #endif /* IKSD */ return(-2); default: return(0); } } else return(0); } #endif /* TCPSOCKET */ /* T T F L U I -- Flush tty input buffer */ void ttflux() { /* But first... */ #ifdef MYREAD /* Flush internal MYREAD buffer. */ #ifdef TCPSOCKET int dotnopts, x; dotnopts = (((xlocal && netconn && IS_TELNET()) || (!xlocal && sstelnet))); #endif /* TCPSOCKET */ debug(F101,"ttflux my_count","",my_count); #ifdef TCPSOCKET if (dotnopts) { CHAR ch = '\0'; while (my_count > 0) { ch = myread(); #ifdef CK_ENCRYPTION if (TELOPT_U(TELOPT_ENCRYPTION)) ck_tn_decrypt((char *)&ch,1); #endif /* CK_ENCRYPTION */ if (ch == IAC) x = tt_tnopt(ch); } } else #endif /* TCPSOCKET */ #ifdef COMMENT #ifdef CK_ENCRYPTION if (TELOPT_U(TELOPT_ENCRYPTION) && my_count > 0) ck_tn_decrypt(&mybuf[my_item+1],my_count); #endif /* CK_ENCRYPTION */ #endif /* COMMENT */ my_count = 0; /* Reset count to zero */ my_item = -1; /* And buffer index to -1 */ #endif /* MYREAD */ } int ttflui() { int n, fd; #ifdef TCPSOCKET int dotnopts; dotnopts = (((xlocal && netconn && IS_TELNET()) || (!xlocal && sstelnet))); #endif /* TCPSOCKET */ #ifdef NETCMD if (ttpipe) fd = fdin; else #endif /* NETCMD */ fd = ttyfd; #ifdef TTLEBUF ttpush = -1; /* Clear the peek-ahead char */ while (le_data && (le_inbuf() > 0)) { CHAR ch = '\0'; if (le_getchar(&ch) > 0) { /* Clear any more... */ debug(F101,"ttflui le_inbuf ch","",ch); } } #endif /* TTLEBUF */ debug(F101,"ttflui ttpipe","",ttpipe); #ifdef MYREAD /* Flush internal MYREAD buffer *NEXT*, in all cases. */ ttflux(); #endif /* MYREAD */ #ifdef NETCONN /* Network flush is done specially, in the network support module. */ if ((netconn || sstelnet) && !ttpipe && !ttpty) { debug(F100,"ttflui netflui","",0); #ifdef COMMENT #ifdef TN_COMPORT if (istncomport()) tnc_send_purge_data(TNC_PURGE_RECEIVE); #endif /* TN_COMPORT */ #endif /* COMMENT */ return(netflui()); } #endif /* NETCONN */ debug(F101,"ttflui ttyfd","",ttyfd); /* Not network */ if (ttyfd < 0) return(-1); #ifdef aegis sio_$control((short)yfd, sio_$flush_in, true, st); if (st.all != status_$ok) { fprintf(stderr, "flush failed: "); error_$print(st); } else { /* sometimes the flush doesn't work */ for (;;) { char buf[256]; /* eat all the characters that shouldn't be available */ ios_$get((short)fd, ios_$cond_opt, buf, 256L, st); /* (void) */ if (st.all == ios_$get_conditional_failed) break; fprintf(stderr, "flush failed(2): "); error_$print(st); } } #else #ifdef BSD44 /* 4.4 BSD */ n = FREAD; /* Specify read queue */ debug(F100,"ttflui BSD44","",0); ioctl(fd,TIOCFLUSH,&n); #else #ifdef Plan9 #undef POSIX /* Uh oh... */ #endif /* Plan9 */ #ifdef POSIX /* POSIX */ debug(F100,"ttflui POSIX","",0); tcflush(fd,TCIFLUSH); #else #ifdef ATTSV /* System V */ #ifndef VXVE debug(F100,"ttflui ATTSV","",0); ioctl(fd,TCFLSH,0); #endif /* VXVE */ #else /* Not BSD44, POSIX, or Sys V */ #ifdef TIOCFLUSH /* Those with TIOCFLUSH defined */ #ifdef ANYBSD /* Berkeley */ n = FREAD; /* Specify read queue */ debug(F100,"ttflui TIOCFLUSH ANYBSD","",0); ioctl(fd,TIOCFLUSH,&n); #else /* Others (V7, etc) */ debug(F100,"ttflui TIOCFLUSH","",0); ioctl(fd,TIOCFLUSH,0); #endif /* ANYBSD */ #else /* All others... */ /* No system call (that we know about) for input buffer flushing. So see how many there are and read them in a loop, using ttinc(). ttinc() is buffered, so we're not getting charged with a system call per character, just a function call. */ if ((n = ttchk()) > 0) { debug(F101,"ttflui read loop","",n); while ((n--) && ttinc(0) > 0) ; } #endif /* TIOCFLUSH */ #endif /* ATTSV */ #endif /* POSIX */ #ifdef Plan9 #define POSIX #endif /* Plan9 */ #endif /* BSD44 */ #endif /* aegis */ return(0); } int ttfluo() { /* Flush output buffer */ int fd; #ifdef NETCMD if (ttpipe) fd = fdout; else #endif /* NETCMD */ fd = ttyfd; #ifdef Plan9 return 0; #else #ifdef POSIX return(tcflush(fd,TCOFLUSH)); #else #ifdef OXOS return(ioctl(fd,TCFLSH,1)); #else return(0); /* All others, nothing */ #endif /* OXOS */ #endif /* POSIX */ #endif /* Plan9 */ } /* Interrupt Functions */ /* Set up terminal interrupts on console terminal */ #ifndef FIONREAD /* We don't need esctrp() */ #ifndef SELECT /* if we have any of these... */ #ifndef CK_POLL #ifndef RDCHK #ifndef OXOS #ifdef SVORPOSIX SIGTYP esctrp(foo) int foo; { /* trap console escapes (^\) */ signal(SIGQUIT,SIG_IGN); /* ignore until trapped */ conesc = 1; debug(F101,"esctrp caught SIGQUIT","",conesc); } #endif /* SVORPOSIX */ #endif /* OXOS */ #ifdef V7 #ifndef MINIX2 SIGTYP esctrp(foo) int foo; { /* trap console escapes (^\) */ signal(SIGQUIT,SIG_IGN); /* ignore until trapped */ conesc = 1; debug(F101,"esctrp caught SIGQUIT","",conesc); } #endif /* MINIX2 */ #endif /* V7 */ #ifdef C70 SIGTYP esctrp(foo) int foo; { /* trap console escapes (^\) */ conesc = 1; signal(SIGQUIT,SIG_IGN); /* ignore until trapped */ } #endif /* C70 */ #endif /* RDCHK */ #endif /* CK_POLL */ #endif /* SELECT */ #endif /* FIONREAD */ /* C O N B G T -- Background Test */ static int jc = 0; /* 0 = no job control */ /* Call with flag == 1 to prevent signal test, which can not be expected to work during file transfer, when SIGINT probably *is* set to SIG_IGN. Call with flag == 0 to use the signal test, but only if the process-group test fails, as it does on some UNIX systems, where getpgrp() is buggy, requires an argument when the man page says it doesn't, or vice versa. If flag == 0 and the process-group test fails, then we determine background status simply (but not necessarily reliably) from isatty(). conbgt() sets the global backgrd = 1 if we appear to be in the background, and to 0 if we seem to be in the foreground. conbgt() is highly prone to misbehavior. */ VOID conbgt(flag) int flag; { int x = -1, /* process group or SIGINT test */ y = 0; /* isatty() test */ /* Check for background operation, even if not running on real tty, so that background flag can be set correctly. If background status is detected, then Kermit will not issue its interactive prompt or most messages. If your prompt goes away, you can blame (and fix?) this function. */ /* Use process-group test if possible. */ #ifdef POSIX /* We can do it in POSIX */ #define PGROUP_T #else #ifdef BSD4 /* and in BSD 4.x. */ #define PGROUP_T #else #ifdef HPUXJOBCTL /* and in most HP-UX's */ #define PGROUP_T #else #ifdef TIOCGPGRP /* and anyplace that has this ioctl. */ #define PGROUP_T #endif /* TIOCGPGRP */ #endif /* HPUXJOBCTL */ #endif /* BSD4 */ #endif /* POSIX */ #ifdef MIPS /* Except if it doesn't work... */ #undef PGROUP_T #endif /* MIPS */ #ifdef MINIX #undef PGROUP_T #endif /* MINIX */ #ifdef PGROUP_T /* Semi-reliable process-group test. Check whether this process's group is the same as the controlling terminal's process group. This works if the getpgrp() call doesn't lie (as it does in the SUNOS System V environment). */ PID_T mypgrp = (PID_T)0; /* Kermit's process group */ PID_T ctpgrp = (PID_T)0; /* The terminal's process group */ #ifndef _POSIX_SOURCE /* The getpgrp() prototype is obtained from system header files for POSIX and Sys V R4 compilations. Other systems, who knows. Some complain about a duplicate declaration here, others don't, so it's safer to leave it in if we don't know for certain. */ #ifndef SVR4 #ifndef PS2AIX10 #ifndef HPUX9 extern PID_T getpgrp(); #endif /* HPUX9 */ #endif /* PS2AIX10 */ #endif /* SVR4 */ #endif /* _POSIX_SOURCE */ /* Get my process group. */ #ifdef SVR3 /* Maybe this should be ATTSV? */ /* This function is not described in SVID R2 */ mypgrp = getpgrp(); /* debug(F101,"ATTSV conbgt process group","",(int) mypgrp); */ #else #ifdef POSIX mypgrp = getpgrp(); /* debug(F101,"POSIX conbgt process group","",(int) mypgrp); */ #else #ifdef OSFPC mypgrp = getpgrp(); /* debug(F101,"OSF conbgt process group","",(int) mypgrp); */ #else #ifdef QNX mypgrp = getpgrp(); /* debug(F101,"QNX conbgt process group","",(int) mypgrp); */ #else #ifdef OSF32 /* (was OSF40) */ mypgrp = getpgrp(); /* debug(F101,"Digital UNIX conbgt process group","",(int) mypgrp); */ #else /* BSD, V7, etc */ #ifdef MINIX2 mypgrp = getpgrp(); #else mypgrp = getpgrp(0); #endif /* MINIX2 */ /* debug(F101,"BSD conbgt process group","",(int) mypgrp); */ #endif /* OSF32 */ #endif /* QNX */ #endif /* OSFPC */ #endif /* POSIX */ #endif /* SVR3 */ #ifdef MINIX /* MINIX does not support job control so Kermit is always in foreground */ x = 0; #else /* Not MINIX */ /* Now get controlling tty's process group */ #ifdef BSD44ORPOSIX ctpgrp = tcgetpgrp(1); /* The POSIX way */ /* debug(F101,"POSIX conbgt terminal process group","",(int) ctpgrp); */ #else ioctl(1, TIOCGPGRP, &ctpgrp); /* Or the BSD way */ /* debug(F101,"non-POSIX conbgt terminal process group","",(int) ctpgrp); */ #endif /* BSD44ORPOSIX */ if ((mypgrp > (PID_T) 0) && (ctpgrp > (PID_T) 0)) x = (mypgrp == ctpgrp) ? 0 : 1; /* If they differ, then background. */ else x = -1; /* If error, remember. */ debug(F101,"conbgt process group test","",x); #endif /* PGROUP_T */ #endif /* MINIX */ /* Try to see if job control is available */ #ifdef NOJC /* User override */ jc = 0; /* No job control allowed */ debug(F111,"NOJC","jc",jc); #else #ifdef BSD44 jc = 1; #else #ifdef SVR4ORPOSIX /* POSIX actually tells us */ debug(F100,"SVR4ORPOSIX jc test...","",0); #ifdef _SC_JOB_CONTROL #ifdef __bsdi__ jc = 1; #else #ifdef __386BSD__ jc = 1; #else jc = sysconf(_SC_JOB_CONTROL); /* Whatever system says */ if (jc < 0) { debug(F101,"sysconf fails, jcshell","",jcshell); jc = (jchdlr == SIG_DFL) ? 1 : 0; } else debug(F111,"sysconf(_SC_JOB_CONTROL)","jc",jc); #endif /* __386BSD__ */ #endif /* __bsdi__ */ #else #ifdef _POSIX_JOB_CONTROL jc = 1; /* By definition */ debug(F111,"_POSIX_JOB_CONTROL is defined","jc",jc); #else jc = 0; /* Assume job control not allowed */ debug(F111,"SVR4ORPOSIX _SC/POSIX_JOB_CONTROL not defined","jc",jc); #endif /* _POSIX_JOB_CONTROL */ #endif /* _SC_JOB_CONTROL */ #else #ifdef BSD4 jc = 1; /* Job control allowed */ debug(F111,"BSD job control","jc",jc); #else #ifdef SVR3JC jc = 1; /* JC allowed */ debug(F111,"SVR3 job control","jc",jc); #else #ifdef OXOS jc = 1; /* JC allowed */ debug(F111,"X/OS job control","jc",jc); #else #ifdef HPUX9 jc = 1; /* JC allowed */ debug(F111,"HP-UX 9.0 job control","jc",jc); #else #ifdef HPUX10 jc = 1; /* JC allowed */ debug(F111,"HP-UX 10.0 job control","jc",jc); #else jc = 0; /* JC not allowed */ debug(F111,"job control catch-all","jc",jc); #endif /* HPUX10 */ #endif /* HPUX9 */ #endif /* OXOS */ #endif /* SVR3JC */ #endif /* BSD4 */ #endif /* SVR4ORPOSIX */ #endif /* BSD44 */ #endif /* NOJC */ debug(F101,"conbgt jc","",jc); #ifndef NOJC debug(F101,"conbgt jcshell","",jcshell); /* At this point, if jc == 1 but jcshell == 0, it means that the OS supports job control, but the shell or other process we are running under does not (jcshell is set in sysinit()) and so if we suspend ourselves, nothing good will come of it. So... */ if (jc < 0) jc = 0; if (jc > 0 && jcshell == 0) jc = 0; #endif /* NOJC */ /* Another background test. Test if SIGINT (terminal interrupt) is set to SIG_IGN (ignore), which is done by the shell (sh) if the program is started with '&'. Unfortunately, this is NOT done by csh or ksh so watch out! Note, it's safe to set SIGINT to SIG_IGN here, because further down we always set it to something else. Note: as of 16 Jul 1999, we also skip this test if we set SIGINT to SIG_IGN ourselves. */ if (x < 0 && !flag && !sigint_ign) { /* Didn't get good results above... */ SIGTYP (*osigint)(); osigint = signal(SIGINT,SIG_IGN); /* What is SIGINT set to? */ sigint_ign = 1; x = (osigint == SIG_IGN) ? 1 : 0; /* SIG_IGN? */ /* debug(F101,"conbgt osigint","",osigint); */ /* debug(F101,"conbgt signal test","",x); */ } /* Also check to see if we're running with redirected stdio. */ /* This is not really background operation, but we want to act as though */ /* it were. */ #ifdef IKSD if (inserver) { /* Internet Kermit Server */ backgrd = 0; /* is not in the background */ return; } #endif /* IKSD */ y = (isatty(0) && isatty(1)) ? 1 : 0; debug(F101,"conbgt isatty test","",y); #ifdef BSD29 /* The process group and/or signal test doesn't work under these... */ backgrd = !y; #else #ifdef sxaE50 backgrd = !y; #else #ifdef MINIX backgrd = !y; #else #ifdef MINIX2 backgrd = !y; #else if (x > -1) backgrd = (x || !y) ? 1 : 0; else backgrd = !y; #endif /* BSD29 */ #endif /* sxaE50 */ #endif /* MINIX */ #endif /* MINIX2 */ debug(F101,"conbgt backgrd","",backgrd); } /* C O N I N T -- Console Interrupt setter */ /* First arg is pointer to function to handle SIGTERM & SIGINT (like Ctrl-C). Second arg is pointer to function to handle SIGTSTP (suspend). */ VOID /* Set terminal interrupt traps. */ #ifdef CK_ANSIC #ifdef apollo conint(f,s) SIGTYP (*f)(), (*s)(); #else conint(SIGTYP (*f)(int), SIGTYP (*s)(int)) #endif /* apollo */ #else conint(f,s) SIGTYP (*f)(), (*s)(); #endif /* CK_ANSIC */ /* conint */ { debug(F101,"conint conistate","",conistate); conbgt(0); /* Do background test. */ /* Set the desired handlers for hangup and software termination. */ #ifdef SIGTERM signal(SIGTERM,f); /* Software termination */ #endif /* SIGTERM */ /* Prior to July 1999 we used to call sighup() here but now it's called in sysinit() so SIGHUP can be caught during execution of the init file or a kerbang script. */ /* Now handle keyboard stop, quit, and interrupt signals. */ /* Check if invoked in background -- if so signals set to be ignored. */ /* However, if running under a job control shell, don't ignore them. */ /* We won't be getting any, as we aren't in the terminal's process group. */ debug(F101,"conint backgrd","",backgrd); debug(F101,"conint jc","",jc); if (backgrd && !jc) { /* In background, ignore signals */ debug(F101,"conint background ignoring signals, jc","",jc); #ifdef SIGTSTP signal(SIGTSTP,SIG_IGN); /* Keyboard stop */ #endif /* SIGTSTP */ signal(SIGQUIT,SIG_IGN); /* Keyboard quit */ signal(SIGINT,SIG_IGN); /* Keyboard interrupt */ sigint_ign = 1; conistate = CONI_NOI; } else { /* Else in foreground or suspended */ debug(F101,"conint foreground catching signals, jc","",jc); signal(SIGINT,f); /* Catch terminal interrupt */ sigint_ign = (f == SIG_IGN) ? 1 : 0; #ifdef SIGTSTP /* Keyboard stop (suspend) */ /* debug(F101,"conint SIGSTSTP","",s); */ if (s == NULL) s = SIG_DFL; #ifdef NOJC /* No job control allowed. */ signal(SIGTSTP,SIG_IGN); #else /* Job control allowed */ if (jc) /* if available. */ signal(SIGTSTP,s); else signal(SIGTSTP,SIG_IGN); #endif /* NOJC */ #endif /* SIGTSTP */ #ifndef OXOS #ifdef SVORPOSIX #ifndef FIONREAD /* Watch out, we don't know this... */ #ifndef SELECT #ifndef CK_POLL #ifndef RDCHK signal(SIGQUIT,esctrp); /* Quit signal, Sys III/V. */ #endif /* RDCHK */ #endif /* CK_POLL */ #endif /* SELECT */ #endif /* FIONREAD */ if (conesc) conesc = 0; /* Clear out pending escapes */ #else #ifdef V7 signal(SIGQUIT,esctrp); /* V7 like Sys III/V */ if (conesc) conesc = 0; #else #ifdef aegis signal(SIGQUIT,f); /* Apollo, catch it like others. */ #else signal(SIGQUIT,SIG_IGN); /* Others, ignore like 4D & earlier. */ #endif /* aegis */ #endif /* V7 */ #endif /* SVORPOSIX */ #endif /* OXOS */ conistate = CONI_INT; } } /* C O N N O I -- Reset console terminal interrupts */ VOID connoi() { /* Console-no-interrupts */ debug(F101,"connoi conistate","",conistate); #ifdef SIGTSTP signal(SIGTSTP,SIG_IGN); /* Suspend */ #endif /* SIGTSTP */ conint(SIG_IGN,SIG_IGN); /* Interrupt */ sigint_ign = 1; /* Remember we did this ourselves */ #ifdef SIGQUIT signal(SIGQUIT,SIG_IGN); /* Quit */ #endif /* SIGQUIT */ #ifdef SIGTERM signal(SIGTERM,SIG_IGN); /* Term */ #endif /* SIGTERM */ conistate = CONI_NOI; } /* I N I T R A W Q -- Set up to read /dev/kmem for character count. */ #ifdef V7 /* Used in Version 7 to simulate Berkeley's FIONREAD ioctl call. This eliminates blocking on a read, because we can read /dev/kmem to get the number of characters available for raw input. If your system can't or you won't let the world read /dev/kmem then you must figure out a different way to do the counting of characters available, or else replace this by a dummy function that always returns 0. */ /* * Call this routine as: initrawq(tty) * where tty is the file descriptor of a terminal. It will return * (as a char *) the kernel-mode memory address of the rawq character * count, which may then be read. It has the side-effect of flushing * input on the terminal. */ /* * John Mackin, Physiology Dept., University of Sydney (Australia) * ...!decvax!mulga!physiol.su.oz!john * * Permission is hereby granted to do anything with this code, as * long as this comment is retained unmodified and no commercial * advantage is gained. */ #ifndef MINIX #ifndef MINIX2 #ifndef COHERENT #include #include #endif /* COHERENT */ #endif /* MINIX2 */ #endif /* MINIX */ #ifdef COHERENT #include #include #endif /* COHERENT */ char * initrawq(tty) int tty; { #ifdef MINIX return(0); #else #ifdef MINIX2 return(0); #else #ifdef UTS24 return(0); #else #ifdef BSD29 return(0); #else long lseek(); static struct nlist nl[] = { {PROCNAME}, {NPROCNAME}, {""} }; static struct proc *pp; char *qaddr, *p, c; int m; PID_T pid, me; NPTYPE xproc; /* Its type is defined in makefile. */ int catch(); me = getpid(); if ((m = open("/dev/kmem", 0)) < 0) err("kmem"); nlist(BOOTNAME, nl); if (nl[0].n_type == 0) err("proc array"); if (nl[1].n_type == 0) err("nproc"); lseek(m, (long)(nl[1].n_value), 0); read (m, &xproc, sizeof(xproc)); saval = signal(SIGALRM, catch); if ((pid = fork()) == 0) { while(1) read(tty, &c, 1); } alarm(2); if(setjmp(jjbuf) == 0) { while(1) read(tty, &c, 1); } signal(SIGALRM, SIG_DFL); #ifdef DIRECT pp = (struct proc *) nl[0].n_value; #else if (lseek(m, (long)(nl[0].n_value), 0) < 0L) err("seek"); if (read(m, &pp, sizeof(pp)) != sizeof(pp)) err("no read of proc ptr"); #endif lseek(m, (long)(nl[1].n_value), 0); read(m, &xproc, sizeof(xproc)); if (lseek(m, (long)pp, 0) < 0L) err("Can't seek to proc"); if ((p = malloc(xproc * sizeof(struct proc))) == NULL) err("malloc"); if (read(m,p,xproc * sizeof(struct proc)) != xproc*sizeof(struct proc)) err("read proc table"); for (pp = (struct proc *)p; xproc > 0; --xproc, ++pp) { if (pp -> p_pid == (short) pid) goto iout; } err("no such proc"); iout: close(m); qaddr = (char *)(pp -> p_wchan); free (p); kill(pid, SIGKILL); wait((WAIT_T *)0); return (qaddr); #endif #endif #endif #endif } /* More V7-support functions... */ static VOID err(s) char *s; { char buf[200]; ckmakmsg(buf,200,"fatal error in initrawq: ", s, NULL, NULL); perror(buf); doexit(1,-1); } static VOID catch(foo) int foo; { longjmp(jjbuf, -1); } /* G E N B R K -- Simulate a modem break. */ #ifdef MINIX #define BSPEED B110 #else #ifdef MINIX2 #define BSPEED B110 #else #define BSPEED B150 #endif /* MINIX2 */ #endif /* MINIX */ #ifndef MINIX2 VOID genbrk(fn,msec) int fn, msec; { struct sgttyb ttbuf; int ret, sospeed, x, y; ret = ioctl(fn, TIOCGETP, &ttbuf); sospeed = ttbuf.sg_ospeed; ttbuf.sg_ospeed = BSPEED; ret = ioctl(fn, TIOCSETP, &ttbuf); y = (int)strlen(brnuls); x = ( BSPEED * 100 ) / msec; if (x > y) x = y; ret = write(fn, brnuls, (( BSPEED * 100 ) / msec )); ttbuf.sg_ospeed = sospeed; ret = ioctl(fn, TIOCSETP, &ttbuf); ret = write(fn, "@", 1); return; } #endif /* MINIX2 */ #ifdef MINIX2 int genbrk(fn,msec) int fn, msec; { struct termios ttbuf; int ret, x, y; speed_t sospeed; ret = tcgetattr(fn, &ttbuf); sospeed = ttbuf.c_ospeed; ttbuf.c_ospeed = BSPEED; ret = tcsetattr(fn,TCSADRAIN, &ttbuf); y = (int)strlen(brnuls); x = ( BSPEED * 100 ) / msec; if (x > y) x = y; ret = write(fn, brnuls, (( BSPEED * 100 ) / msec )); ttbuf.c_ospeed = sospeed; ret = tcsetattr(fn, TCSADRAIN, &ttbuf); ret = write(fn, "@", 1); return ret; } #endif /* MINIX2 */ #endif /* V7 */ /* I N C H K -- Check if chars waiting to be read on given file descriptor. This routine is a merger of ttchk() and conchk(). Call with: channel == 0 to check console. channel == 1 to check communications connection. and: fd = file descriptor. Returns: >= 0: number of characters waiting, 0 or greater, -1: on any kind of error, -2: if there is (definitely) no connection. Note: In UNIX we don't have to call nettchk() because a socket file descriptor works just like in serial i/o, ioctls and all. (But this will change if we add non-file-descriptor channels, such as IBM X.25 for AIX...) */ static int in_chk(channel, fd) int channel, fd; { int x, n = 0; /* Workers, n = return value */ extern int clsondisc; /* Close on disconnect */ /* The first section checks to make sure we have a connection, but only if we're in local mode. */ #ifdef DEBUG if (deblog) { debug(F111,"in_chk entry",ckitoa(fd),channel); debug(F101,"in_chk ttyfd","",ttyfd); debug(F101,"in_chk ttpty","",ttpty); } #endif /* DEBUG */ /* But don't say connection is gone if we have any buffered-stuff. */ #ifdef TTLEBUF debug(F101,"in_chk ttpush","",ttpush); if (channel == 1) { if (ttpush >= 0) n++; n += le_inbuf(); if (n > 0) return(n); } #endif /* TTLEBUF */ #ifdef NETPTY #ifdef HAVE_PTYTRAP /* Special handling for HP-UX pty i/o */ if (ttpty && pty_trap_pending(ttyfd) > 0) { if (pty_trap_handler(ttyfd) > 0) { ttclos(0); return(-2); } } #endif /* HAVE_PTYTRAP */ #endif /* NETPTY */ if (channel) { /* Checking communications channel */ if (ttyfd < 0) { /* No connection */ return(-2); /* That's what this means */ } else if (xlocal && /* In local mode */ (!netconn /* Serial connection or */ #ifdef TN_COMPORT || istncomport() /* Telnet Com Port */ #endif /* TN_COMPORT */ ) && ttcarr != CAR_OFF /* with CARRIER WATCH ON (or AUTO) */ #ifdef COMMENT #ifdef MYREAD /* Seems like this would be a good idea but it prevents C-Kermit from popping back to the prompt automatically when carrier drops. However, commenting this out prevents us from seeing the NO CARRIER message. Needs more work... */ && my_count < 1 /* Nothing in our internal buffer */ #endif /* MYREAD */ #endif /* COMMENT */ ) { int x; x = ttgmdm(); /* So get modem signals */ debug(F101,"in_chk close-on-disconnect","",clsondisc); if (x > -1) { /* Check for carrier */ if (!(x & BM_DCD)) { /* No carrier */ debug(F101,"in_chk carrier lost","",x); if (clsondisc) /* If "close-on-disconnect" */ ttclos(0); /* close device & release lock. */ return(-2); /* This means "disconnected" */ } /* In case I/O to device after CD dropped always fails */ /* as in Debian Linux 2.1 and Unixware 2.1... */ } else { debug(F101,"in_chk ttgmdm I/O error","",errno); debug(F101,"in_chk ttgmdm gotsigs","",gotsigs); if (gotsigs) { /* If we got signals before... */ if (errno == 5 || errno == 6) { /* I/O error etc */ if (clsondisc) /* like when modem hangs up */ ttclos(0); return(-2); } } /* If we never got modem signals successfully on this */ /* connection before, we can't conclude that THIS failure */ /* means the connection was lost. */ return(0); } } } /* We seem to have a connection so now see if any bytes are waiting on it */ #ifdef CK_SSL if (ssl_active_flag || tls_active_flag) { n += SSL_pending(ssl_active_flag?ssl_con:tls_con); debug(F101,"in_chk SSL_pending","",n); if (n < 0) { ttclos(0); return(-1); } else if (n > 0) { return(n); } } #endif /* CK_SSL */ #ifdef RLOGCODE #ifdef CK_KERBEROS /* It is not safe to read any data when using encrypted Klogin */ if (ttnproto == NP_EK4LOGIN || ttnproto == NP_EK5LOGIN) { #ifdef KRB4 if (ttnproto == NP_EK4LOGIN) { n += krb4_des_avail(ttyfd); debug(F101,"in_chk krb4_des_avail","",n); } #endif /* KRB4 */ #ifdef KRB5 if (ttnproto == NP_EK5LOGIN) { n += krb5_des_avail(ttyfd); debug(F101,"in_chk krb5_des_avail","",n); } #ifdef KRB5_U2U if (ttnproto == NP_K5U2U) { n += krb5_u2u_avail(ttyfd); debug(F101,"in_chk krb5_des_avail","",n); } #endif /* KRB5_U2U */ #endif /* KRB5 */ if (n < 0) /* Is this right? */ return(-1); else return(n); } #endif /* CK_KERBEROS */ #endif /* RLOGCODE */ errno = 0; /* Reset this so we log good info */ #ifdef FIONREAD x = ioctl(fd, FIONREAD, &n); /* BSD and lots of others */ #ifdef DEBUG /* (the more the better) */ if (deblog) { debug(F101,"in_chk FIONREAD return code","",x); debug(F101,"in_chk FIONREAD count","",n); debug(F101,"in_chk FIONREAD errno","",errno); } #endif /* DEBUG */ #else /* FIONREAD not defined */ /* Here, if (netconn && ttnet == NET_TCPB), we might try calling recvmsg() with flags MSG_PEEK|MSG_DONTWAIT on the socket (ttyfd), except this is not portable (MSG_DONTWAIT isn't defined in any of the files that I looked at, but it is needed to prevent the call from blocking), and the msghdr struct differs from place to place, so we would need another avalanche of ifdefs. Still, when FIONREAD is not available, this is the only other known method of asking the OS for the *number* of characters available for reading. */ #ifdef V7 /* UNIX V7: look in kernel memory */ #ifdef MINIX n = 0; /* But not in MINIX */ #else #ifdef MINIX2 n = 0; #else lseek(kmem[TTY], (long) qaddr[TTY], 0); /* 7th Edition Unix */ x = read(kmem[TTY], &n, sizeof(int)); if (x != sizeof(int)) n = 0; #endif /* MINIX2 */ #endif /* MINIX */ #else /* Not V7 */ #ifdef PROVX1 x = ioctl(fd, TIOCQCNT, &ttbuf); /* DEC Pro/3xx Venix V.1 */ n = ttbuf.sg_ispeed & 0377; /* Circa 1984 */ if (x < 0) n = 0; #else #ifdef MYREAD /* Here we skip all the undependable and expensive calls below if we already have something in our internal buffer. This tends to work quite nicely, so the only really bad case remaining is the one in which neither FIONREAD or MYREAD are defined, which is increasingly rare these days. */ if (channel != 0 && my_count > 0) { debug(F101,"in_chk buf my_count","",my_count); n = my_count; /* n was 0 before we got here */ return(n); } #endif /* MYREAD */ /* rdchk(), select(), and poll() tell us *if* data is available to be read, but not how much, so these should be used only as a final resort. Especially since these calls tend to add a lot overhead. */ #ifdef RDCHK /* This mostly SCO-specific */ n = rdchk(fd); debug(F101,"in_chk rdchk","",n); #else /* No RDCHK */ #ifdef SELECT #ifdef Plan9 /* Only allows select on the console ... don't ask */ if (channel == 0) #endif /* Plan9 */ { fd_set rfds; /* Read file descriptors */ #ifdef BELLV10 FD_ZERO(rfds); /* Initialize them */ FD_SET(fd,rfds); /* We want to look at this fd */ #else FD_ZERO(&rfds); /* Initialize them */ FD_SET(fd,&rfds); /* We want to look at this fd */ tv.tv_sec = tv.tv_usec = 0L; /* A 0-valued timeval structure */ #endif /* BELLV10 */ #ifdef Plan9 n = select( FD_SETSIZE, &rfds, (fd_set *)0, (fd_set *)0, &tv ); debug(F101,"in_chk Plan 9 select","",n); #else #ifdef BELLV10 n = select( 128, rfds, (fd_set *)0, (fd_set *)0, 0 ); debug(F101,"in_chk BELLV10 select","",n); #else #ifdef BSD44 n = select( FD_SETSIZE, &rfds, (fd_set *)0, (fd_set *)0, &tv ); debug(F101,"in_chk BSD44 select","",n); #else #ifdef BSD43 n = select( FD_SETSIZE, &rfds, (fd_set *)0, (fd_set *)0, &tv ); debug(F101,"in_chk BSD43 select","",n); #else #ifdef SOLARIS n = select( FD_SETSIZE, &rfds, (fd_set *)0, (fd_set *)0, &tv ); debug(F101,"in_chk SOLARIS select","",n); #else #ifdef QNX6 n = select( FD_SETSIZE, &rfds, (fd_set *)0, (fd_set *)0, &tv ); debug(F101,"in_chk QNX6 select","",n); #else #ifdef QNX n = select( FD_SETSIZE, &rfds, (fd_set *)0, (fd_set *)0, &tv ); debug(F101,"in_chk QNX select","",n); #else #ifdef COHERENT n = select( FD_SETSIZE, &rfds, (fd_set *)0, (fd_set *)0, &tv ); debug(F101,"in_chk COHERENT select","",n); #else #ifdef SVR4 n = select( FD_SETSIZE, &rfds, (fd_set *)0, (fd_set *)0, &tv ); debug(F101,"in_chk SVR4 select","",n); #else #ifdef __linux__ n = select( FD_SETSIZE, &rfds, (fd_set *)0, (fd_set *)0, &tv ); debug(F101,"in_chk LINUX select","",n); #ifdef OSF n = select( FD_SETSIZE, &rfds, (fd_set *)0, (fd_set *)0, &tv ); debug(F101,"in_chk OSF select","",n); #else n = select( FD_SETSIZE, &rfds, (int *)0, (int *)0, &tv ); debug(F101,"in_chk catchall select","",n); #endif /* OSF */ #endif /* __linux__ */ #endif /* SVR4 */ #endif /* COHERENT */ #endif /* QNX */ #endif /* QNX6 */ #endif /* SOLARIS */ #endif /* BSD43 */ #endif /* BSD44 */ #endif /* BELLV10 */ #endif /* Plan9 */ } #else /* Not SELECT */ #ifdef CK_POLL { struct pollfd pfd; pfd.fd = fd; pfd.events = POLLIN; pfd.revents = 0; n = poll(&pfd, 1, 0); debug(F101,"in_chk poll","",n); if ((n > 0) && (pfd.revents & POLLIN)) n = 1; } #endif /* CK_POLL */ #endif /* SELECT */ #endif /* RDCHK */ #endif /* PROVX1 */ #endif /* V7 */ #endif /* FIONREAD */ /* From here down, treat console and communication device differently... */ if (channel == 0) { /* Console */ #ifdef SVORPOSIX #ifndef FIONREAD #ifndef SELECT #ifndef CK_POLL #ifndef RDCHK /* This is the hideous hack used in System V and POSIX systems that don't support FIONREAD, rdchk(), select(), poll(), etc, in which the user's CONNECT-mode escape character is attached to SIGQUIT. Used, obviously, only on the console. */ if (conesc) { /* Escape character typed == SIGQUIT */ debug(F100,"in_chk conesc","",conesc); conesc = 0; signal(SIGQUIT,esctrp); /* Restore signal */ n += 1; } #endif /* RDCHK */ #endif /* CK_POLL */ #endif /* SELECT */ #endif /* FIONREAD */ #endif /* SVORPOSIX */ return(n); /* Done with console */ } if (channel != 0) { /* Communications connection */ #ifdef MYREAD #ifndef FIONREAD /* select() or rdchk(), etc, has told us that something is waiting, but we don't know how much. So we do a read to get it and then we know. Note: This read is NOT nonblocking if nothing is there (because of VMIN=1), but it should be safe in this case since the OS tells us at least one byte is waiting to be read, and MYREAD reads return as much as is there without waiting for any more. Controlled tests on Solaris and Unixware (with FIONREAD deliberately undefined) show this to be true. */ debug(F101,"in_chk read my_count","",my_count); debug(F101,"in_chk read n","",n); if (n > 0 && my_count == 0) { /* This also catches disconnects etc */ /* Do what mygetbuf does except don't grab a character */ my_count = myfillbuf(); my_item = -1; /* ^^^ */ debug(F101,"in_chk myfillbuf my_count","",my_count); if (my_count < 0) return(-1); else n = 0; /* NB: n is replaced by my_count */ } #endif /* FIONREAD */ /* Here we add whatever we think is unread to what is still in our our internal buffer. Thus the importance of setting n to 0 just above. */ debug(F101,"in_chk my_count","",my_count); debug(F101,"in_chk n","",n); if (my_count > 0) n += my_count; #endif /* MYREAD */ } debug(F101,"in_chk result","",n); /* Errors here don't prove the connection has dropped so just say 0 */ return(n < 0 ? 0 : n); } /* T T C H K -- Tell how many characters are waiting in tty input buffer */ int ttchk() { int fd; #ifdef NETCMD if (ttpipe) fd = fdin; else #endif /* NETCMD */ fd = ttyfd; return(in_chk(1,fd)); } /* T T X I N -- Get n characters from tty input buffer */ /* Returns number of characters actually gotten, or -1 on failure */ /* Intended for use only when it is known that n characters are actually */ /* Available in the input buffer. */ int ttxin(n,buf) int n; CHAR *buf; { register int x = 0, c = -2; #ifdef TTLEBUF register int i = 0; #endif /* TTLEBUF */ int fd; if (n < 1) /* Nothing to do */ return(0); #ifdef TTLEBUF if (ttpush >= 0) { buf[0] = ttpush; /* Put pushed char in buffer*/ ttpush = -1; /* Clear the push buffer */ if (ttchk() > 0) return(ttxin(n-1, &buf[1]) + 1); else return(1); } if (le_data) { while (le_inbuf() > 0) { if (le_getchar(&buf[i])) { i++; n--; } } if (ttchk() > 0) return(ttxin(n,&buf[i])+i); else return(i); } #endif /* TTLEBUF */ #ifdef NETCMD if (ttpipe) fd = fdin; else #endif /* NETCMD */ fd = ttyfd; #ifdef SUNX25 if (netconn && (ttnet == NET_SX25)) /* X.25 connection */ return(x25xin(n,buf)); #endif /* SUNX25 */ #ifdef IBMX25 /* riehm: possibly not needed. Test worked with normal reads and writes */ if (netconn && (ttnet == NET_IX25)) { /* X.25 connection */ x = x25xin(n,buf); if (x > 0) buf[x] = '\0'; return(x); } #endif /* IBMX25 */ #ifdef MYREAD debug(F101,"ttxin MYREAD","",n); while (x < n) { c = myread(); if (c < 0) { debug(F101,"ttxin myread returns","",c); if (c == -3) x = -1; break; } buf[x++] = c & ttpmsk; #ifdef RLOGCODE #ifdef CK_KERBEROS /* It is impossible to know how many characters are waiting */ /* to be read when you are using Encrypted Rlogin or SSL */ /* as the transport since the number of real data bytes */ /* can be greater or less than the number of bytes on the */ /* wire which is what ttchk() returns. */ if (netconn && (ttnproto == NP_EK4LOGIN || ttnproto == NP_EK5LOGIN)) if (ttchk() <= 0) break; #endif /* CK_KERBEROS */ #endif /* RLOGCODE */ #ifdef CK_SSL if (ssl_active_flag || tls_active_flag) if (ttchk() <= 0) break; #endif /* CK_SSL */ } #else debug(F101,"ttxin READ","",n); x = read(fd,buf,n); for (c = 0; c < n; c++) /* Strip any parity */ buf[c] &= ttpmsk; #endif /* MYREAD */ debug(F101,"ttxin x","",x); /* Done */ if (x > 0) buf[x] = '\0'; if (x < 0) x = -1; return(x); } /* T T O L -- Write string s, length n, to communication device. */ /* Returns: >= 0 on success, number of characters actually written. -1 on failure. */ #ifdef CK_ENCRYPTION CHAR * xpacket = NULL; int nxpacket = 0; #endif /* CK_ENCRYPTION */ #define TTOLMAXT 5 int ttol(s,n) int n; CHAR *s; { int x, len, tries, fd; #ifdef CKXXCHAR extern int dblflag; /* For SET SEND DOUBLE-CHARACTER */ extern short dblt[]; CHAR *p = NULL, *p2, *s2, c; int n2 = 0; #endif /* CKXXCHAR */ if (ttyfd < 0) /* Not open? */ return(-3); #ifdef DEBUG if (deblog) { /* debug(F101,"ttol ttyfd","",ttyfd); */ ckhexdump("ttol s",s,n); } #endif /* DEBUG */ #ifdef NETCMD if (ttpipe) fd = fdout; else #endif /* NETCMD */ fd = ttyfd; #ifdef CKXXCHAR /* Double any characters that must be doubled. */ debug(F101,"ttol dblflag","",dblflag); if (dblflag) { p = (CHAR *) malloc(n + n + 1); if (p) { s2 = s; p2 = p; n2 = 0; while (*s2) { c = *s2++; *p2++ = c; n2++; if (dblt[(unsigned) c] & 2) { *p2++ = c; n2++; } } s = p; n = n2; s[n] = '\0'; } #ifdef DEBUG ckhexdump("ttol doubled s",s,n); #endif /* DEBUG */ } #endif /* CKXXCHAR */ tries = TTOLMAXT; /* Allow up to this many tries */ len = n; /* Remember original length */ #ifdef CK_ENCRYPTION /* This is to avoid encrypting a packet that is already encrypted, e.g. when we resend a packet directly out of the packet buffer, and also to avoid encrypting a constant (literal) string, which can cause a memory fault. */ if (TELOPT_ME(TELOPT_ENCRYPTION)) { int x; if (nxpacket < n) { if (xpacket) { free(xpacket); xpacket = NULL; nxpacket = 0; } x = n > 10240 ? n : 10240; xpacket = (CHAR *)malloc(x); if (!xpacket) { fprintf(stderr,"ttol malloc failure\n"); return(-1); } else nxpacket = x; } memcpy((char *)xpacket,(char *)s,n); s = xpacket; ck_tn_encrypt((char *)s,n); } #endif /* CK_ENCRYPTION */ while (n > 0 && (tries-- > 0 #ifdef CK_ENCRYPTION /* keep trying if we are encrypting */ || TELOPT_ME(TELOPT_ENCRYPTION) #endif /* CK_ENCRYPTION */ )) { /* Be persistent */ debug(F101,"ttol try","",TTOLMAXT - tries); #ifdef BEOSORBEBOX if (netconn && !ttpipe && !ttpty) x = nettol((char *)s,n); /* Write string to device */ else #endif /* BEOSORBEBOX */ #ifdef IBMX25 if (ttnet == NET_IX25) /* * this is a more controlled way of writing to X25 * STREAMS, however write should also work! */ x = x25write(ttyfd, s, n); else #endif /* IBMX25 */ #ifdef CK_SSL if (ssl_active_flag || tls_active_flag) { int error; /* Write using SSL */ ssl_retry: if (ssl_active_flag) x = SSL_write(ssl_con, s, n); else x = SSL_write(tls_con, s, n); switch (SSL_get_error(ssl_active_flag?ssl_con:tls_con,x)) { case SSL_ERROR_NONE: if (x == n) return(len); s += x; n -= x; goto ssl_retry; case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_READ: x = 0; break; case SSL_ERROR_SYSCALL: if (x != 0) return(-1); case SSL_ERROR_WANT_X509_LOOKUP: case SSL_ERROR_SSL: case SSL_ERROR_ZERO_RETURN: default: ttclos(0); return(-3); } } else #endif /* CK_SSL */ #ifdef CK_KERBEROS #ifdef KRB4 #ifdef RLOGCODE if (ttnproto == NP_EK4LOGIN) { return(krb4_des_write(ttyfd,s,n)); } else #endif /* RLOGCODE */ #endif /* KRB4 */ #ifdef KRB5 #ifdef RLOGCODE if (ttnproto == NP_EK5LOGIN) { return(krb5_des_write(ttyfd,(char *)s,n,0)); } else #endif /* RLOGCODE */ #ifdef KRB5_U2U if (ttnproto == NP_K5U2U) { return(krb5_u2u_write(ttyfd,(char *)s,n)); } else #endif /* KRB5_U2U */ #endif /* KRB5 */ #endif /* CK_KERBEROS */ x = write(fd,s,n); /* Write string to device */ if (x == n) { /* Worked? */ debug(F101,"ttol ok","",x); /* OK */ #ifdef CKXXCHAR if (p) free(p); #endif /* CKXXCHAR */ return(len); /* Done */ } else if (x < 0) { /* No, got error? */ debug(F101,"ttol write error","",errno); #ifdef EWOULDBLOCK if (errno == EWOULDBLOCK) { msleep(10); continue; } else #endif /* EWOULDBLOCK */ #ifdef TCPSOCKET if (netconn && ttnet == NET_TCPB) { debug(F101,"ttol TCP error","",errno); ttclos(0); /* Close the connection. */ x = -3; } #endif /* TCPSOCKET */ #ifdef CKXXCHAR if (p) free(p); #endif /* CKXXCHAR */ return(x); } else { /* No error, so partial success */ debug(F101,"ttol partial","",x); /* This never happens */ s += x; /* Point to part not written yet */ n -= x; /* Adjust length */ if (x > 0) msleep(10); /* Wait 10 msec */ } /* Go back and try again */ } #ifdef CKXXCHAR if (p) free(p); #endif /* CKXXCHAR */ return(n < 1 ? len : -1); /* Return the results */ } /* T T O C -- Output a character to the communication line */ /* This function should only be used for interactive, character-mode operations, like terminal connection, script execution, dialer i/o, where the overhead of the signals and alarms does not create a bottleneck. */ int #ifdef CK_ANSIC ttoc(char c) #else ttoc(c) char c; #endif /* CK_ANSIC */ /* ttoc */ { #define TTOC_TMO 15 /* Timeout in case we get stuck */ int xx, fd; if (ttyfd < 0) /* Check for not open. */ return(-1); #ifdef NETCMD if (ttpipe) fd = fdout; else #endif /* NETCMD */ fd = ttyfd; c &= 0xff; /* debug(F101,"ttoc","",(CHAR) c); */ saval = signal(SIGALRM,timerh); /* Enable timer interrupt */ xx = alarm(TTOC_TMO); /* for this many seconds. */ if (xx < 0) xx = 0; /* Save old alarm value. */ /* debug(F101,"ttoc alarm","",xx); */ if ( #ifdef CK_POSIX_SIG sigsetjmp(sjbuf,1) #else setjmp(sjbuf) #endif /* CK_POSIX_SIG */ ) { /* Timer went off? */ ttimoff(); /* Yes, cancel this alarm. */ if (xx - TTOC_TMO > 0) alarm(xx - TTOC_TMO); /* Restore previous one */ /* debug(F100,"ttoc timeout","",0); */ #ifdef NETCONN if (!netconn) { #endif /* NETCONN */ debug(F101,"ttoc timeout","",c); if (ttflow == FLO_XONX) { debug(F101,"ttoc flow","",ttflow); /* Maybe we're xoff'd */ #ifndef Plan9 #ifdef POSIX /* POSIX way to unstick. */ debug(F100,"ttoc tcflow","",tcflow(ttyfd,TCOON)); #else #ifdef BSD4 /* Berkeley way to do it. */ #ifdef TIOCSTART /* .... Used to be "ioctl(ttyfd, TIOCSTART, 0);". Who knows? */ { int x = 0; debug(F101,"ttoc TIOCSTART","",ioctl(ttyfd, TIOCSTART, &x)); } #endif /* TIOCSTART */ #endif /* BSD4 */ /* Is there a Sys V way to do this? */ #endif /* POSIX */ #endif /* Plan9 */ } #ifdef NETCONN } #endif /* NETCONN */ return(-1); /* Return failure code. */ } else { int rc; #ifdef BEOSORBEBOX #ifdef NETCONN if (netconn && !ttpipe && !ttpty) rc = nettoc(c); else #endif /* BEOSORBEBOX */ #endif /* NETCONN */ #ifdef CK_ENCRYPTION if (TELOPT_ME(TELOPT_ENCRYPTION)) ck_tn_encrypt(&c,1); #endif /* CK_ENCRYPTION */ #ifdef IBMX25 /* riehm: maybe this isn't necessary after all. Test program * worked fine with data being sent and retrieved with normal * read's and writes! */ if (ttnet == NET_IX25) rc = x25write(ttyfd,&c,1); /* as above for X25 streams */ else #endif /* IBMX25 */ #ifdef CK_SSL if (ssl_active_flag || tls_active_flag) { int error; /* Write using SSL */ if (ssl_active_flag) rc = SSL_write(ssl_con, &c, 1); else rc = SSL_write(tls_con, &c, 1); switch (SSL_get_error(ssl_active_flag?ssl_con:tls_con,rc)){ case SSL_ERROR_NONE: break; case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_READ: rc = 0; break; case SSL_ERROR_SYSCALL: if (rc != 0) return(-1); case SSL_ERROR_WANT_X509_LOOKUP: case SSL_ERROR_SSL: case SSL_ERROR_ZERO_RETURN: default: ttclos(0); return(-1); } } else #endif /* CK_SSL */ #ifdef CK_KERBEROS #ifdef KRB4 #ifdef RLOGCODE if (ttnproto == NP_EK4LOGIN) { rc = (krb4_des_write(ttyfd,(char *)&c,1) == 1); } else #endif /* RLOGCODE */ #endif /* KRB4 */ #ifdef KRB5 #ifdef RLOGCODE if (ttnproto == NP_EK5LOGIN) { rc = (krb5_des_write(ttyfd,&c,1,0) == 1); } else #endif /* RLOGCODE */ #ifdef KRB5_U2U if (ttnproto == NP_K5U2U) { rc = (krb5_u2u_write(ttyfd,&c,1) == 1); } else #endif /* KRB5_U2U */ #endif /* KRB5 */ #endif /* CK_KERBEROS */ rc = write(fd,&c,1); /* Try to write the character. */ if (rc < 1) { /* Failed */ ttimoff(); /* Turn off the alarm. */ alarm(xx); /* Restore previous alarm. */ debug(F101,"ttoc errno","",errno); /* Log the error, */ return(-1); /* and return the error code. */ } } ttimoff(); /* Success, turn off the alarm. */ alarm(xx); /* Restore previous alarm. */ return(0); /* Return good code. */ } /* T T I N L -- Read a record (up to break character) from comm line. */ /* Reads up to "max" characters from the connection, terminating on: (a) the packet length field if the "turn" argument is zero, or (b) on the packet-end character (eol) if the "turn" argument is nonzero (c) a certain number of Ctrl-C's in a row Returns: >= 0, the number of characters read upon success; -1 if "max" exceeded, timeout, or other correctable error; -2 on user interruption (c); -3 on fatal error like connection lost. The name of this routine dates from the early days when Kermit packets were, indeed, always lines of text. That was before control-character unprefixing and length-driven packet framing were introduced, which this version handle. NB: this routine is ONLY for reading incoming Kermit packets, nothing else. To read other kinds of incoming material, use ttinc() or ttxin(). The bytes that were input are copied into "dest" with their parity bits stripped if parity was selected. Returns the number of bytes read. Bytes after the eol are available upon the next call to this function. The idea is to minimize the number of system calls per packet, and also to minimize timeouts. This function is the inner loop of the protocol and must be as efficient as possible. The current strategy is to use myread(), a macro to manage buffered (and generally nonblocking) reads. WARNING: This function calls parchk(), which is defined in another module. Normally, ckutio.c does not depend on code from any other module, but there is an exception in this case because all the other ck?tio.c modules also need to call parchk(), so it's better to have it defined in a common place. */ #ifdef CTRLC #undef CTRLC #endif /* CTRLC */ #define CTRLC '\03' /* We have four different declarations here because: (a) to allow Kermit to be built without the automatic parity sensing feature (b) one of each type for ANSI C, one for non-ANSI. */ #ifndef NOXFER static int pushedback = 0; int #ifdef PARSENSE #ifdef CK_ANSIC ttinl(CHAR *dest, int max,int timo, CHAR eol, CHAR start, int turn) #else ttinl(dest,max,timo,eol,start,turn) int max,timo,turn; CHAR *dest, eol, start; #endif /* CK_ANSIC */ #else /* not PARSENSE */ #ifdef CK_ANSIC ttinl(CHAR *dest, int max,int timo, CHAR eol) #else ttinl(dest,max,timo,eol) int max,timo; CHAR *dest, eol; #endif /* CK_ANSIC */ #endif /* PARSENSE */ /* ttinl */ { #ifndef MYREAD CHAR ch, dum; #endif /* MYREAD */ #ifdef PARSENSE int pktlen = -1; int lplen = 0; int havelen = 0; #endif /* PARSENSE */ int fd; int sopmask = 0xff; /* Start-Of-Packet mask */ #ifdef CKXXCHAR extern short dblt[]; /* Ignore-character table */ extern int ignflag; #endif /* CKXXCHAR */ #ifdef TCPSOCKET extern CHAR stchr; #endif /* TCPSOCKET */ int x; #ifdef STREAMING extern int streaming; extern int sndtyp; #endif /* STREAMING */ if (ttyfd < 0) return(-3); /* Not open. */ /* In February 2007 I fixed ttinl() to work better under the truly awful conditions encountered by the AM-APEX oceanographic floats that gather hurricane data and phone home using Iridium satellite modems, which under certain conditions, can send two packets back to back after a long pause. In this case the second packet would be ignored because the SOH was skipped due to the ttflui() call. But the reworked lookahead/pushback logic broke Kermit transfers on encrypted connections. This was fixed 12-13 August 2007. All of this happened after 8.0.212 Dev.27 was released and before Dev.28, so no harm done other than the delay. */ debug(F101,"ttinl max","",max); debug(F101,"ttinl timo","",timo); #ifdef NETCMD if (ttpipe) fd = fdin; else #endif /* NETCMD */ fd = ttyfd; #ifdef COMMENT if (xlocal && conchk() > 0) /* Allow for console interruptions */ return(-1); #endif /* COMMENT */ *dest = '\0'; /* Clear destination buffer */ if (timo < 0) timo = 0; /* Safety */ if (timo) { /* Don't time out if timo == 0 */ int xx; saval = signal(SIGALRM,timerh); /* Enable timer interrupt */ xx = alarm(timo); /* Set it. */ debug(F101,"ttinl alarm","",xx); } if ( #ifdef CK_POSIX_SIG sigsetjmp(sjbuf,1) #else setjmp(sjbuf) #endif /* CK_POSIX_SIG */ ) { /* Timer went off? */ debug(F100,"ttinl timout","",0); /* Get here on timeout. */ /* debug(F110," with",(char *) dest,0); */ ttimoff(); /* Turn off timer */ return(-1); /* and return error code. */ } else { register int i, n = -1; /* local variables */ int ccn = 0; #ifdef PARSENSE register int flag = 0; debug(F000,"ttinl start","",start); #endif /* PARSENSE */ ttpmsk = ttprty ? 0177 : 0377; /* Set parity stripping mask. */ sopmask = needpchk ? 0177 : ttpmsk; /* And SOP matching mask. */ /* Now read into destination, stripping parity and looking for the */ /* the packet terminator, and also for several Ctrl-C's typed in a row. */ i = 0; /* Destination index */ debug(F101,"ttinl eol","",eol); while (i < max-1) { #ifdef MYREAD errno = 0; /* On encrypted connections myread returns encrypted bytes */ n = myread(); debug(F000,"TTINL myread char","",n); if (n < 0) { /* Timeout or i/o error? */ #ifdef DEBUG if (deblog) { debug(F101,"ttinl myread failure, n","",n); debug(F101,"ttinl myread errno","",errno); } #endif /* DEBUG */ /* Don't let EINTR break packets. */ if (n == -3) { if (errno == EINTR && i > 0) { debug(F111,"ttinl EINTR myread i","continuing",i); continue; } else { debug(F110,"ttinl non-EINTR -3","closing",0); wasclosed = 1; ttimoff(); /* Turn off timer */ ttclos(0); return(n); } } else if (n == -2 && netconn /* && timo == 0 */ ) { /* Here we try to catch broken network connections */ /* even when ioctl() and read() do not catch them */ debug(F111,"ttinl network myread failure","closing",n); wasclosed = 1; ttimoff(); ttclos(0); return(-3); } #ifdef STREAMING /* Streaming and no data to read */ else if (n == 0 && streaming && sndtyp == 'D') return(0); #endif /* STREAMING */ break; /* Break out of while loop */ } #else /* not MYREAD (is this code used anywhere any more?) */ /* The non-MYREAD code dates from the 1980s and was needed on certain platforms where there were no nonblocking reads. -fdc, 2007/02/22. */ if ((n = read(fd, &n, 1)) < 1) break; /* Error - break out of while loop */ #endif /* MYREAD */ /* Get here with char in n */ #ifdef CK_ENCRYPTION if (TELOPT_U(TELOPT_ENCRYPTION) && !pushedback) { CHAR ch = n; ck_tn_decrypt((char *)&ch,1); n = ch; debug(F000,"TTINL decryp char","",n); } pushedback = 0; #endif /* CK_ENCRYPTION */ #ifdef TCPSOCKET if (n == IAC && /* Handle Telnet options */ ((xlocal && netconn && IS_TELNET()) || (!xlocal && sstelnet))) { n = tt_tnopt(n); if (n < 0) return(n); #ifndef NOPARSEN else if (n == 1) start = stchr; #endif /* NOPARSEN */ if (n != 255) /* No data - go back for next char */ continue; } /* Quoted IAC - keep going */ #endif /* TCPSOCKET */ #ifdef CKXXCHAR if (ignflag) if (dblt[(unsigned) n] & 1) /* Character to ignore? */ continue; #endif /* CKXXCHAR */ /* Use parity mask, rather than always stripping parity, to check for cancellation. Otherwise, runs like \x03\x83\x03 in a packet could cancel the transfer when parity is NONE. (Note that \x03\x03\x03 is extremely unlikely due to run-length encoding.) */ /* Check cancellation */ if (!xlocal && xfrcan && ((n & ttpmsk) == xfrchr)) { if (++ccn >= xfrnum) { /* If xfrnum in a row, bail out. */ if (timo) { /* Clear timer. */ ttimoff(); } if (xfrchr < 32) printf("^%c...\r\n",(char)(xfrchr+64)); else printf("Canceled...\r\n"); return(-2); } } else ccn = 0; /* No cancellation, reset counter, */ #ifdef PARSENSE /* Restructured code allows for a new packet to appear somewhere in the middle of a previous one. -fdc, 24 Feb 2007. */ if ((n & sopmask) == start) { /* Start of Packet */ debug(F101,"ttinl SOP i","",i); flag = 1; /* Flag that we are in a packet */ havelen = 0; /* Invalidate previous length */ pktlen = -1; /* (if any) in case we were */ lplen = 0; /* alread processand a packet */ i = 0; /* and reset the dest buffer pointer */ } if (flag == 0) { /* No SOP yet... */ debug(F000,"ttinl skipping","",n); continue; } dest[i++] = n & ttpmsk; /* If we have not been instructed to wait for a turnaround character, we can go by the packet length field. If turn != 0, we must wait for the end of line (eol) character before returning. This is an egregious violation of all principles of layering... (Less egregious in C-Kermit 9.0, in which we go by the length field but also look for the eol in case it arrives early, e.g. if the length field was corrupted upwards.) */ if (!havelen) { if (i == 2) { if ((dest[1] & 0x7f) < 32) /* Garbage in length field */ return(-1); /* fdc - 13 Apr 2010 */ pktlen = xunchar(dest[1] & 0x7f); if (pktlen > 94) /* Rubout in length field */ return(-1); /* fdc - 13 Apr 2010 */ if (pktlen > 1) { havelen = 1; debug(F101,"ttinl pktlen value","",pktlen); } } else if (i == 5 && pktlen == 0) { lplen = xunchar(dest[4] & 0x7f); } else if (i == 6 && pktlen == 0) { pktlen = lplen * 95 + xunchar(dest[5] & 0x7f) + 5; havelen = 1; debug(F101,"ttinl extended length","",pktlen); } } /* Suppose we looked at the sequence number here and found it was out of range? This would mean either (a) incoming packets had SOP unprefixed and we are out of sync, or (b) the packet is damaged. Since (a) is bad practice, let's ignore it. So what should we do here if we know the packet is damaged? 1. Nothing -- keep trying to read the packet till we find what we think is the end, or we time out, and let the upper layer decide what to do. But since either the packet is corrupt or we are out of sync, our criterion for finding the end does not apply and we are likely to time out (or swallow a piece of the next packet) if our assumed length is too long. (This was the behavior prior to version 7.0.) 2. set flag = 0 and continue? This would force us to wait for the next packet to come in, and therefore (in the nonwindowing case), would force a timeout in the other Kermit. 3. set flag = 0 and continue, but only if the window size is > 1 and the window is not blocked? Talk about cheating! 4. Return a failure code and let the upper layer decide what to do. This should be equivalent to 3, but without the cheating. So let's do it that way... But note that we must ignore the parity bit in case this is the first packet and we have not yet run parchk(). */ if (i == 3) { /* Peek at sequence number */ x = xunchar((dest[i-1] & 0x7f)); /* If it's not in range... */ if (x < 0 || x > 63) { debug(F111,"ttinl bad seq",dest,x); if (timo) ttimoff(); return(-1); /* return a nonfatal error */ } } #else /* PARSENSE */ dest[i++] = n & ttpmsk; #endif /* PARSENSE */ /* Check for end of packet */ if ( ((n & ttpmsk) == eol) /* Always break on the eol char */ #ifdef PARSENSE || /* fdc - see notes of 13 Apr 2010 */ /* Purely length-driven if SET HANDSHAKE NONE (i.e. turn == 0). This allows packet terminators and handshake characters to appear literally inside a packet data field. */ (havelen && (i > pktlen+1) && (!turn || (turn && (n & 0x7f) == turn))) /* (turn, not eol) */ #endif /* PARSENSE */ ) { /* Here we have either read the last byte of the packet based on its length field, or else we have read the packet terminator (eol) or the half-duplex line-turnaround char (turn). */ #ifndef PARSENSE debug(F101,"ttinl got eol","",eol); /* (or turn) */ dest[i] = '\0'; /* Yes, terminate the string, */ /* debug(F101,"ttinl i","",i); */ #else /* PARSENSE */ #ifdef DEBUG if (deblog) { if ((n & ttpmsk) != eol) { debug(F101,"ttinl EOP length","",pktlen); debug(F000,"ttinl EOP current char","",n); debug(F101,"ttinl EOP packet buf index","",i); } else debug(F101,"ttinl got eol","",eol); } #endif /* DEBUG */ #ifdef MYREAD /* The packet was read based on its length. This leaves the packet terminator unread, and so ttchk() will always return at least 1 because of this, possibly giving a false positive to the "is there another packet waiting?" test. But if we know the terminator (or any other interpacket junk) is there, we can safely get rid of it. NOTE: This code reworked to (a) execute even if the debug log isn't active; and (b) actually work. -fdc, 2007/02/22. And again 2007/08/12-13 to also work on encrypted connections. */ debug(F101,"TTINL my_count","",my_count); if ((n & ttpmsk) != eol) { /* Not the packet terminator */ int x; while (my_count > 0) { x = myread(); /* (was ttinc(0) */ debug(F000,"TTINL lkread char","",x); #ifdef CK_ENCRYPTION if (TELOPT_U(TELOPT_ENCRYPTION)) { CHAR ch = x; ck_tn_decrypt((char *)&ch,1); x = ch; debug(F000,"TTINL lkdecr char","",x); } #endif /* CK_ENCRYPTION */ /* Note: while it might seem more elegant to simply push back the encrypted byte, that desynchronizes the decryption stream; the flag is necessary so we don't try to decrypt the same byte twice. */ if ((x & ttpmsk) == start) { /* Start of next packet */ myunrd(x); /* Push back the decrypted byte */ pushedback = 1; /* And set flag */ debug(F000,"TTINL lkpush char","",x); break; } } } #endif /* MYREAD */ dest[i] = '\0'; /* Terminate the string, */ if (needpchk) { /* Parity checked yet? */ if (ttprty == 0) { /* No, check. */ if ((ttprty = parchk(dest,start,i)) > 0) { int j; debug(F101,"ttinl senses parity","",ttprty); debug(F110,"ttinl packet before",dest,0); ttpmsk = 0x7f; for (j = 0; j < i; j++) dest[j] &= 0x7f; /* Strip parity from packet */ debug(F110,"ttinl packet after ",dest,0); } else ttprty = 0; /* Restore if parchk error */ } sopmask = ttpmsk; needpchk = 0; } #endif /* PARSENSE */ if (timo) /* Turn off timer if it was on */ ttimoff(); ckhexdump("ttinl got",dest,i); #ifdef STREAMING /* ttinl() was called because there was non-packet */ /* data sitting in the back channel. Ignore it. */ if (streaming && sndtyp == 'D') return(-1); #endif /* STREAMING */ return(i); } } /* End of while() */ ttimoff(); return(n); } } #endif /* NOXFER */ /* T T I N C -- Read a character from the communication line */ /* On success, returns the character that was read, >= 0. On failure, returns -1 or other negative myread error code, or -2 if connection is broken or ttyfd < 0. or -3 if session limit has expired, or -4 if something or other... NOTE: The API does not provide for ttinc() returning a special code upon timeout, but we need it. So for this we have a global variable, ttinctimo. */ static int ttinctimo = 0; /* Yuk */ int ttinc(timo) int timo; { int n = 0, fd; int is_tn = 0; CHAR ch = 0; ttinctimo = 0; if (ttyfd < 0) return(-2); /* Not open. */ is_tn = (xlocal && netconn && IS_TELNET()) || (!xlocal && sstelnet); #ifdef TTLEBUF if (ttpush >= 0) { debug(F111,"ttinc","ttpush",ttpush); ch = ttpush; ttpush = -1; return(ch); } if (le_data) { if (le_getchar(&ch) > 0) { debug(F111,"ttinc le_getchar","ch",ch); return(ch); } } #endif /* TTLEBUF */ #ifdef NETCMD if (ttpipe) fd = fdin; else #endif /* NETCMD */ fd = ttyfd; if ((timo <= 0) /* Untimed. */ #ifdef MYREAD || (my_count > 0) /* Buffered char already waiting. */ #endif /* MYREAD */ ) { #ifdef MYREAD /* Comm line failure returns -1 thru myread, so no &= 0377 */ n = myread(); /* Wait for a character... */ /* debug(F000,"ttinc MYREAD n","",n); */ #ifdef CK_ENCRYPTION /* debug(F101,"ttinc u_encrypt","",TELOPT_U(TELOPT_ENCRYPTION)); */ if (TELOPT_U(TELOPT_ENCRYPTION) && n >= 0) { ch = n; ck_tn_decrypt((char *)&ch,1); n = ch; } #endif /* CK_ENCRYPTION */ #ifdef NETPTY if (ttpty && n < 0) { debug(F101,"ttinc error on pty","",n); ttclos(0); return(n); } #endif /* NETPTY */ #ifdef TNCODE if ((n > -1) && is_tn) return((unsigned)(n & 0xff)); else #endif /* TNCODE */ return(n < 0 ? n : (unsigned)(n & ttpmsk)); #else /* MYREAD */ while ((n = read(fd,&ch,1)) == 0) /* Wait for a character. */ /* Shouldn't have to loop in ver 5A. */ #ifdef NETCONN if (netconn) { /* Special handling for net */ netclos(); /* If read() returns 0 it means */ netconn = 0; /* the connection has dropped. */ errno = ENOTCONN; return(-2); } #endif /* NETCONN */ ; /* debug(F101,"ttinc","",ch); */ #ifdef TNCODE if ((n > 0) && is_tn) { #ifdef CK_ENCRYPTION if (TELOPT_U(TELOPT_ENCRYPTION)) { ck_tn_decrypt(&ch,1); n = ch; } #endif /* CK_ENCRYPTION */ return((unsigned)(ch & 0xff)); } else #endif /* TNCODE */ return((n < 0) ? -4 : ((n == 0) ? -1 : (unsigned)(ch & ttpmsk))); #endif /* MYREAD */ } else { /* Timed read */ int oldalarm; saval = signal(SIGALRM,timerh); /* Set up handler, save old one. */ oldalarm = alarm(timo); /* Set alarm, save old one. */ if ( #ifdef CK_POSIX_SIG sigsetjmp(sjbuf,1) #else setjmp(sjbuf) #endif /* CK_POSIX_SIG */ ) { /* Timer expired */ ttinctimo = 1; n = -1; /* set flag */ } else { #ifdef MYREAD n = myread(); /* If managing own buffer... */ debug(F101,"ttinc myread","",n); ch = n; #else n = read(fd,&ch,1); /* Otherwise call the system. */ if (n == 0) n = -1; debug(F101,"ttinc read","",n); #endif /* MYREAD */ #ifdef CK_ENCRYPTION if (TELOPT_U(TELOPT_ENCRYPTION) && n >= 0) { ck_tn_decrypt((char *)&ch,1); } #endif /* CK_ENCRYPTION */ if (n >= 0) n = (unsigned) (ch & 0xff); else n = (n < 0) ? -4 : -2; /* Special return codes. */ } ttimoff(); /* Turn off the timer */ if (oldalarm > 0) { if (n == -1) /* and restore any previous alarm */ oldalarm -= timo; if (oldalarm < 0) /* adjusted by our timeout interval */ oldalarm = 0; if (oldalarm) { debug(F101,"ttinc restoring oldalarm","",oldalarm); alarm(oldalarm); } } #ifdef NETCONN if (netconn) { if (n == -2) { /* read() returns 0 */ netclos(); /* on network read failure */ netconn = 0; errno = ENOTCONN; } } #endif /* NETCONN */ #ifdef TNCODE if ((n > -1) && is_tn) return((unsigned)(n & 0xff)); else #endif /* TNCODE */ /* Return masked char or neg. */ return( (n < 0) ? n : (unsigned)(n & ttpmsk) ); } } /* S N D B R K -- Send a BREAK signal of the given duration */ static int #ifdef CK_ANSIC sndbrk(int msec) { /* Argument is milliseconds */ #else sndbrk(msec) int msec; { #endif /* CK_ANSIC */ #ifndef POSIX int x, n; #endif /* POSIX */ #ifdef OXOS #define BSDBREAK #endif /* OXOS */ #ifdef ANYBSD #define BSDBREAK #endif /* ANYBSD */ #ifdef BSD44 #define BSDBREAK #endif /* BSD44 */ #ifdef COHERENT #ifdef BSDBREAK #undef BSDBREAK #endif /* BSDBREAK */ #endif /* COHERENT */ #ifdef BELLV10 #ifdef BSDBREAK #undef BSDBREAK #endif /* BSDBREAK */ #endif /* BELLV10 */ #ifdef PROVX1 char spd; #endif /* PROVX1 */ debug(F101,"ttsndb ttyfd","",ttyfd); if (ttyfd < 0) return(-1); /* Not open. */ #ifdef Plan9 return p9sndbrk(msec); #else #ifdef NETCONN #ifdef NETCMD if (ttpipe) /* Pipe */ return(ttoc('\0')); #endif /* NETCMD */ #ifdef NETPTY if (ttpty) return(ttoc('\0')); #endif /* NETPTY */ if (netconn) /* Send network BREAK */ return(netbreak()); #endif /* NETCONN */ if (msec < 1 || msec > 5000) return(-1); /* Bad argument */ #ifdef POSIX /* Easy in POSIX */ { int x; debug(F111,"sndbrk POSIX",ckitoa(msec),(msec/375)); errno = 0; x = tcsendbreak(ttyfd,msec / 375); debug(F111,"sndbrk tcsendbreak",ckitoa(errno),x); return(x); } #else #ifdef PROVX1 gtty(ttyfd,&ttbuf); /* Get current tty flags */ spd = ttbuf.sg_ospeed; /* Save speed */ ttbuf.sg_ospeed = B50; /* Change to 50 baud */ stty(ttyfd,&ttbuf); /* ... */ n = (int)strlen(brnuls); /* Send the right number of nulls */ x = msec / 91; if (x > n) x = n; write(ttyfd,brnuls,n); ttbuf.sg_ospeed = spd; /* Restore speed */ stty(ttyfd,&ttbuf); /* ... */ return(0); #else #ifdef aegis sio_$control((short)ttyfd, sio_$send_break, msec, st); return(0); #else #ifdef BSDBREAK n = FWRITE; /* Flush output queue. */ /* Watch out for int vs long problems in &n arg! */ debug(F101,"sndbrk BSDBREAK","",msec); ioctl(ttyfd,TIOCFLUSH,&n); /* Ignore any errors.. */ if (ioctl(ttyfd,TIOCSBRK,(char *)0) < 0) { /* Turn on BREAK */ perror("Can't send BREAK"); return(-1); } x = msleep(msec); /* Sleep for so many milliseconds */ if (ioctl(ttyfd,TIOCCBRK,(char *)0) < 0) { /* Turn off BREAK */ perror("BREAK stuck!!!"); doexit(BAD_EXIT,-1); /* Get out, closing the line. */ /* with bad exit status */ } return(x); #else #ifdef ATTSV /* No way to send a long BREAK in Sys V, so send a bunch of regular ones. (Actually, Sys V R4 is *supposed* to have the POSIX tcsendbreak() function, but there's no way for this code to know for sure.) */ debug(F101,"sndbrk ATTSV","",msec); x = msec / 275; for (n = 0; n < x; n++) { /* Reportedly the cast breaks this function on some systems */ /* But then why was it here in the first place? */ if (ioctl(ttyfd,TCSBRK, /* (char *) */ 0) < 0) { perror("Can't send BREAK"); return(-1); } } return(0); #else #ifdef V7 debug(F101,"sndbrk V7","",msec); return(genbrk(ttyfd,250)); /* Simulate a BREAK */ #else debug(F101,"sndbrk catchall","",msec); ttoc(0);ttoc(0);ttoc(0);ttoc(0); return(0); #endif /* V7 */ #endif /* BSDBREAK */ #endif /* ATTSV */ #endif /* aegis */ #endif /* PROVX1 */ #endif /* POSIX */ #endif /* Plan9 */ } /* T T S N D B -- Send a BREAK signal */ int ttsndb() { #ifdef TN_COMPORT if (netconn && istncomport()) return((tnsndb(275L) >= 0) ? 0 : -1); else #endif /* TN_COMPORT */ return(sndbrk(275)); } /* T T S N D L B -- Send a Long BREAK signal */ int ttsndlb() { #ifdef TN_COMPORT if (netconn && istncomport()) return((tnsndb(1800L) >= 0) ? 0 : -1); else #endif /* TN_COMPORT */ return(sndbrk(1500)); } /* M S L E E P -- Millisecond version of sleep(). */ /* Call with number of milliseconds (thousandths of seconds) to sleep. Intended only for small intervals. For big ones, just use sleep(). Highly system-dependent. Returns 0 always, even if it didn't work. */ /* Define MSLFTIME for systems that must use an ftime() loop. */ #ifdef ANYBSD /* For pre-4.2 BSD versions */ #ifndef BSD4 #define MSLFTIME #endif /* BSD4 */ #endif /* ANYBSD */ #ifdef TOWER1 /* NCR Tower OS 1.0 */ #define MSLFTIME #endif /* TOWER1 */ #ifdef COHERENT /* Coherent... */ #ifndef _I386 /* Maybe Coherent/386 should get this, too */ #define MSLFTIME /* Opinions are divided */ #endif /* _I386 */ #endif /* COHERENT */ #ifdef COMMENT #ifdef GETMSEC /* Millisecond timer */ static long msecbase = 0L; /* Unsigned long not portable */ long getmsec() { /* Milliseconds since base time */ struct timeval xv; struct timezone xz; long secs, msecs; if ( #ifdef GTODONEARG gettimeofday(&tv) #else #ifdef PTX gettimeofday(&tv, NULL) #else gettimeofday(&tv, &tz) #endif /* PTX */ #endif /* GTODONEARG */ < 0) return(-1); if (msecbase == 0L) { /* First call, set base time. */ msecbase = tv.tv_sec; debug(F101,"getmsec base","",msecbase); } return(((tv.tv_sec - msecbase) * 1000L) + (tv.tv_usec / 1000L)); } #endif /* GETMSEC */ #endif /* COMMENT */ #ifdef SELECT int ttwait(fd, secs) int fd, secs; { int x; fd_set rfds; FD_ZERO(&rfds); FD_SET(fd,&rfds); tv.tv_sec = secs; tv.tv_usec = 0L; errno = 0; if ((x = select(FD_SETSIZE, #ifdef HPUX9 (int *) #else #ifdef HPUX1000 (int *) #endif /* HPUX1000 */ #endif /* HPUX9 */ &rfds, 0, 0, &tv)) < 0) { debug(F101,"ttwait select errno","",errno); return(0); } else { debug(F101,"ttwait OK","",errno); x = FD_ISSET(fd, &rfds); debug(F101,"ttwait select x","",x); return(x ? 1 : 0); } } #endif /* SELECT */ int msleep(m) int m; { /* Other possibilities here are: nanosleep(), reportedly defined in POSIX.4. sginap(), IRIX only (back to what IRIX version I don't know). */ #ifdef Plan9 return _SLEEP(m); #else #ifdef BEOSORBEBOX snooze(m*1000); #else /* BEOSORBEBOX */ #ifdef SELECT int t1, x; debug(F101,"msleep SELECT 1","",m); if (m <= 0) return(0); if (m >= 1000) { /* Catch big arguments. */ sleep(m/1000); m = m % 1000; if (m < 10) return(0); } debug(F101,"msleep SELECT 2","",m); #ifdef BELLV10 x = select( 0, (fd_set *)0, (fd_set *)0, (fd_set *)0, m ); debug(F101,"msleep BELLV10 select","",x); #else /* BELLV10 */ #ifdef HPUX9 gettimeofday(&tv, &tz); #else #ifndef COHERENT #ifdef GTODONEARG if (gettimeofday(&tv) < 0) #else #ifdef PTX if (gettimeofday(&tv,NULL) < 0) #else #ifdef NOTIMEZONE if (gettimeofday(&tv, NULL) < 0) /* wonder what this does... */ #else if (gettimeofday(&tv, &tz) < 0) #endif /* NOTIMEZONE */ #endif /* PTX */ #endif /* GTODONEARG */ return(-1); t1 = tv.tv_sec; /* Seconds */ #endif /* COHERENT */ #endif /* HPUX9 */ tv.tv_sec = 0; /* Use select() */ tv.tv_usec = m * 1000L; #ifdef BSD44 x = select( 0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &tv ); debug(F101,"msleep BSD44 select","",x); #else /* BSD44 */ #ifdef __linux__ x = select( 0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &tv ); debug(F101,"msleep __linux__ select","",x); #else /* __linux__ */ #ifdef BSD43 x = select( 0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &tv ); debug(F101,"msleep BSD43 select","",x); #else /* BSD43 */ #ifdef QNX6 x = select( 0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &tv ); debug(F101,"msleep QNX6 select","",x); #else /* QNX6 */ #ifdef QNX x = select( 0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &tv ); debug(F101,"msleep QNX select","",x); #else /* QNX */ #ifdef COHERENT x = select( 0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &tv ); debug(F101,"msleep COHERENT select","",x); #else /* COHERENT */ #ifdef HPUX1000 /* 10.00 only, not 10.10 or later */ x = select( 0, (int *)0, (int *)0, (int *)0, &tv ); debug(F101,"msleep HP-UX 10.00 select","",x); #else /* HPUX1000 */ #ifdef SVR4 x = select( 0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &tv ); debug(F101,"msleep SVR4 select","",x); #else /* SVR4 */ #ifdef OSF40 x = select( 0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &tv ); debug(F101,"msleep OSF40 select","",x); #else /* OSF40 */ #ifdef PTX x = select( 0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &tv ); debug(F101,"msleep OSF40 select","",x); #else x = select( 0, (int *)0, (int *)0, (int *)0, &tv ); debug(F101,"msleep catch-all select","",x); #endif /* PTX */ #endif /* OSF40 */ #endif /* HP1000 */ #endif /* SVR4 */ #endif /* COHERENT */ #endif /* QNX */ #endif /* QNX6 */ #endif /* BSD43 */ #endif /* __linux__ */ #endif /* BSD44 */ #endif /* BELLV10 */ return(0); #else /* Not SELECT */ #ifdef CK_POLL /* We have poll() */ struct pollfd pfd; /* Supply a valid address for poll() */ #ifdef ODT30 /* But in SCO ODT 3.0 */ #ifdef NAP /* we should use nap() instead */ debug(F101,"msleep ODT 3.0 NAP","",m); /* because using poll() here */ nap((long)m); /* seems to break dialing. */ return(0); #else debug(F101,"msleep ODT 3.0 POLL","",m); poll(&pfd, 0, m); return(0); #endif /* NAP */ #else debug(F101,"msleep POLL","",m); poll(&pfd, 0, m); return(0); #endif /* ODT30 */ /* We could handle the above more cleanly by just letting nap() always take precedence over poll() in this routine, but there is no way to know whether that would break something else. */ #else /* Not POLL */ #ifdef USLEEP /* "This routine is implemented using setitimer(2); it requires eight system calls...". In other words, it might take 5 minutes to sleep 10 milliseconds... */ debug(F101,"msleep USLEEP","",m); if (m >= 1000) { /* Catch big arguments. */ sleep(m/1000); m = m % 1000; if (m < 10) return(0); } usleep((unsigned int)(m * 1000)); return(0); #else #ifdef aegis time_$clock_t dur; debug(F101,"msleep aegis","",m); dur.c2.high16 = 0; dur.c2.low32 = 250 * m; /* one millisecond = 250 four microsecond ticks */ time_$wait(time_$relative, dur, st); return(0); #else #ifdef PROVX1 debug(F101,"msleep Venix","",m); if (m <= 0) return(0); sleep(-((m * 60 + 500) / 1000)); return(0); #else #ifdef NAP debug(F101,"msleep NAP","",m); nap((long)m); return(0); #else #ifdef ATTSV #ifndef BSD44 extern long times(); /* Or #include ? */ #endif /* BSD44 */ long t1, t2, tarray[4]; int t3; char *cp = getenv("HZ"); int CLOCK_TICK; int hertz; if (cp && (hertz = atoi(cp))) { CLOCK_TICK = 1000 / hertz; } else { /* probably single user mode */ #ifdef HZ CLOCK_TICK = 1000 / HZ; #else static warned = 0; /* HZ always exists in, for instance, SCO Xenix, so you don't have to * make special #ifdefs for XENIX here, like in ver 4F. Also, if you * have Xenix, you have should have nap(), so the best is to use -DNAP * in the makefile. Most systems have HZ. */ CLOCK_TICK = 17; /* 1/60 sec */ if (!warned) { printf("warning: environment variable HZ bad... using HZ=%d\r\n", 1000 / CLOCK_TICK); warned = 1; } #endif /* !HZ */ } debug(F101,"msleep ATTSV","",m); if (m <= 0) return(0); if (m >= 1000) { /* Catch big arguments. */ sleep(m/1000); m = m % 1000; if (m < 10) return(0); } if ((t1 = times(tarray)) < 0) return(-1); while (1) { if ((t2 = times(tarray)) < 0) return(-1); t3 = ((int)(t2 - t1)) * CLOCK_TICK; if (t3 > m) return(t3); } #else /* Not ATTSV */ #ifdef MSLFTIME /* Use ftime() loop... */ int t1, t3 = 0; debug(F101,"msleep MSLFTIME","",m); if (m <= 0) return(0); if (m >= 1000) { /* Catch big arguments. */ sleep(m/1000); m = m % 1000; if (m < 10) return(0); } #ifdef QNX ftime(&ftp); /* void ftime() in QNX */ #else if (ftime(&ftp) < 0) return(-1); /* Get base time. */ #endif /* QNX */ t1 = ((ftp.time & 0xff) * 1000) + ftp.millitm; while (1) { ftime(&ftp); /* Get current time and compare. */ t3 = (((ftp.time & 0xff) * 1000) + ftp.millitm) - t1; if (t3 > m) return(0); } #else /* This includes true POSIX, which has no way to do this. */ debug(F101,"msleep busy loop","",m); if (m >= 1000) { /* Catch big arguments. */ sleep(m/1000); m = m % 1000; if (m < 10) return(0); } if (m > 0) while (m > 0) m--; /* Just a dumb busy loop */ return(0); #endif /* MSLFTIME */ #endif /* ATTSV */ #endif /* NAP */ #endif /* PROVX1 */ #endif /* aegis */ #endif /* CK_POLL */ #endif /* SELECT */ #endif /* BEOSORBEBOX */ #endif /* USLEEP */ #endif /* Plan9 */ } /* R T I M E R -- Reset elapsed time counter */ VOID rtimer() { tcount = time( (time_t *) 0 ); } /* G T I M E R -- Get current value of elapsed time counter in seconds */ int gtimer() { int x; x = (int) (time( (time_t *) 0 ) - tcount); debug(F101,"gtimer","",x); return( (x < 0) ? 0 : x ); } #ifdef GFTIMER /* Floating-point timers. Require not only floating point support, but also gettimeofday(). */ static struct timeval tzero; VOID rftimer() { #ifdef GTODONEARG /* Account for Mot's definition */ (VOID) gettimeofday(&tzero); #else (VOID) gettimeofday(&tzero, (struct timezone *)0); #endif /* GTODONEARG */ } CKFLOAT gftimer() { struct timeval tnow, tdelta; CKFLOAT s; #ifdef DEBUG char fpbuf[64]; #endif /* DEBUG */ #ifdef GTODONEARG /* Account for Mot's definition */ (VOID) gettimeofday(&tnow); #else (VOID) gettimeofday(&tnow, (struct timezone *)0); #endif /* GTODONEARG */ tdelta.tv_sec = tnow.tv_sec - tzero.tv_sec; tdelta.tv_usec = tnow.tv_usec - tzero.tv_usec; if (tdelta.tv_usec < 0) { tdelta.tv_sec--; tdelta.tv_usec += 1000000; } s = (CKFLOAT) tdelta.tv_sec + ((CKFLOAT) tdelta.tv_usec / 1000000.0); if (s < GFMINTIME) s = GFMINTIME; #ifdef DEBUG if (deblog) { sprintf(fpbuf,"%f",s); debug(F110,"gftimer",fpbuf,0); } #endif /* DEBUG */ return(s); } #endif /* GFTIMER */ /* Z T I M E -- Return asctime()-format date/time string */ /* NOTE: as a side effect of calling this routine, we can also set the following two variables, giving the micro- and milliseconds (fractions of seconds) of the clock time. Currently this is done only in BSD-based builds that use gettimeofday(). When these variables are not filled in, they are left with a value of -1L. */ static char asctmbuf[64]; VOID ztime(s) char **s; { #ifdef GFTIMER /* The gettimeofday() method, which also sets ztmsec and ztusec, works for all GFTIMER builds. NOTE: ztmsec and ztusec are defined in ckcmai.c, and extern declarations for them are in ckcdeb.h; thus they are declared in this file by inclusion of ckcdeb.h. */ char *asctime(); struct tm *localtime(); struct tm *tp; ztmsec = -1L; ztusec = -1L; if (!s) debug(F100,"ztime s==NULL","",0); #ifdef GTODONEARG /* No 2nd arg in Motorola SV88 and some others */ if (gettimeofday(&tv) > -1) #else #ifndef COHERENT #ifdef PTX if (gettimeofday(&tv,NULL) > -1) #else #ifdef NOTIMEZONE if (gettimeofday(&tv, NULL) > -1) /* wonder what this does... */ #else if (gettimeofday(&tv, &tz) > -1) #endif /* NOTIMEZONE */ #endif /* PTX */ #endif /* COHERENT */ #endif /* GTODONEARG */ { /* Fill in tm struct */ ztusec = tv.tv_usec; /* Microseconds */ ztmsec = ztusec / 1000L; /* Milliseconds */ #ifdef HPUX9 { time_t zz; zz = tv.tv_sec; tp = localtime(&zz); /* Convert to local time */ } #else #ifdef HPUX1000 { time_t zz; zz = tv.tv_sec; tp = localtime(&zz); } #else #ifdef LINUX { /* avoid unaligned access trap on 64-bit platforms */ time_t zz; zz = tv.tv_sec; tp = localtime(&zz); } #else #ifdef MACOSX tp = localtime((time_t *)&tv.tv_sec); /* Convert to local time */ #else tp = localtime(&tv.tv_sec); #endif /* MACOSX */ #endif /* LINUX */ #endif /* HPUX1000 */ #endif /* HPUX9 */ if (s) { char * s2; s2 = asctime(tp); /* Convert result to ASCII string */ asctmbuf[0] = '\0'; if (s2) ckstrncpy(asctmbuf,s2,64); *s = asctmbuf; debug(F111,"ztime GFTIMER gettimeofday",*s,ztusec); } } #else /* Not GFTIMER */ #undef ZTIMEV7 /* Which systems need to use */ #ifdef COHERENT /* old UNIX Version 7 way... */ #define ZTIMEV7 #endif /* COHERENT */ #ifdef TOWER1 #define ZTIMEV7 #endif /* TOWER1 */ #ifdef ANYBSD #ifndef BSD42 #define ZTIMEV7 #endif /* BSD42 */ #endif /* ANYBSD */ #ifdef V7 #ifndef MINIX #define ZTIMEV7 #endif /* MINIX */ #endif /* V7 */ #ifdef POSIX #define ZTIMEV7 #endif /* POSIX */ #ifdef HPUX1020 /* Prototypes are in , included above. */ time_t clock_storage; clock_storage = time((void *) 0); if (s) { *s = ctime(&clock_storage); debug(F110,"ztime: HPUX 10.20",*s,0); } #else #ifdef ATTSV /* AT&T way */ /* extern long time(); */ /* Theoretically these should */ char *ctime(); /* already been dcl'd in */ time_t clock_storage; clock_storage = time( #ifdef IRIX60 (time_t *) #else #ifdef BSD44 (time_t *) #else (long *) #endif /* BSD44 */ #endif /* IRIX60 */ 0 ); if (s) { *s = ctime( &clock_storage ); debug(F110,"ztime: ATTSV",*s,0); } #else #ifdef PROVX1 /* Venix 1.0 way */ int utime[2]; time(utime); if (s) { *s = ctime(utime); debug(F110,"ztime: PROVX1",*s,0); } #else #ifdef BSD42 /* 4.2BSD way */ char *asctime(); struct tm *localtime(); struct tm *tp; gettimeofday(&tv, &tz); ztusec = tv.tv_usec; ztmsec = tv.tv_usec / 1000L; tp = localtime(&tv.tv_sec); if (s) { *s = asctime(tp); debug(F111,"ztime: BSD42",*s,ztusec); } #else #ifdef MINIX /* MINIX way */ #ifdef COMMENT extern long time(); /* Already got these from */ extern char *ctime(); #endif /* COMMENT */ time_t utime[2]; time(utime); if (s) { *s = ctime(utime); debug(F110,"ztime: MINIX",*s,0); } #else #ifdef ZTIMEV7 /* The regular way */ char *asctime(); struct tm *localtime(); struct tm *tp; long xclock; /* or unsigned long for BeBox? */ time(&xclock); tp = localtime(&xclock); if (s) { *s = asctime(tp); debug(F110,"ztime: ZTIMEV7",*s,0); } #else /* Catch-all for others... */ if (s) { *s = "Day Mon 00 00:00:00 0000\n"; /* Dummy in asctime() format */ debug(F110,"ztime: catch-all",*s,0); } #endif /* ZTIMEV7 */ #endif /* MINIX */ #endif /* BSD42 */ #endif /* PROVX1 */ #endif /* ATTSV */ #endif /* HPUX1020 */ #endif /* GFTIMER */ } /* C O N G M -- Get console terminal modes. */ /* Saves initial console mode, and establishes variables for switching between current (presumably normal) mode and other modes. Should be called when program starts, but only after establishing whether program is in the foreground or background. Returns 1 if it got the modes OK, 0 if it did nothing, -1 on error. */ int congm() { int fd; if (backgrd || !isatty(0)) { /* If in background. */ cgmf = -1; /* Don't bother, modes are garbage. */ return(-1); } if (cgmf > 0) return(0); /* Already did this. */ debug(F100,"congm getting modes","",0); /* Need to do it. */ #ifdef aegis ios_$inq_type_uid(ios_$stdin, conuid, st); if (st.all != status_$ok) { fprintf(stderr, "problem getting stdin objtype: "); error_$print(st); } concrp = (conuid == mbx_$uid); conbufn = 0; #endif /* aegis */ #ifndef BEBOX if ((fd = open(CTTNAM,2)) < 0) { /* Open controlling terminal */ #ifdef COMMENT fprintf(stderr,"Error opening %s\n", CTTNAM); perror("congm"); return(-1); #else fd = 0; #endif /* COMMENT */ } #else fd = 0; #endif /* !BEBOX */ #ifdef BSD44ORPOSIX if (tcgetattr(fd,&ccold) < 0) return(-1); if (tcgetattr(fd,&cccbrk) < 0) return(-1); if (tcgetattr(fd,&ccraw) < 0) return(-1); #else #ifdef ATTSV if (ioctl(fd,TCGETA,&ccold) < 0) return(-1); if (ioctl(fd,TCGETA,&cccbrk) < 0) return(-1); if (ioctl(fd,TCGETA,&ccraw) < 0) return(-1); #ifdef VXVE cccbrk.c_line = 0; /* STTY line 0 for CDC VX/VE */ if (ioctl(fd,TCSETA,&cccbrk) < 0) return(-1); ccraw.c_line = 0; /* STTY line 0 for CDC VX/VE */ if (ioctl(fd,TCSETA,&ccraw) < 0) return(-1); #endif /* VXVE */ #else #ifdef BELLV10 if (ioctl(fd,TIOCGETP,&ccold) < 0) return(-1); if (ioctl(fd,TIOCGETP,&cccbrk) < 0) return(-1); if (ioctl(fd,TIOCGETP,&ccraw) < 0) return(-1); debug(F101,"cccbrk.sg_flags orig","", cccbrk.sg_flags); #else if (gtty(fd,&ccold) < 0) return(-1); if (gtty(fd,&cccbrk) < 0) return(-1); if (gtty(fd,&ccraw) < 0) return(-1); #endif /* BELLV10 */ #endif /* ATTSV */ #endif /* BSD44ORPOSIX */ #ifdef sony_news /* Sony NEWS */ if (ioctl(fd,TIOCKGET,&km_con) < 0) { /* Get console Kanji mode */ perror("congm error getting Kanji mode"); debug(F101,"congm error getting Kanji mode","",0); km_con = -1; /* Make sure this stays undefined. */ return(-1); } #endif /* sony_news */ if (fd > 0) close(fd); cgmf = 1; /* Flag that we got them. */ return(1); } static VOID congetbuf(x) int x; { int n; n = CONBUFSIZ - (conbufp - conbuf); /* How much room left in buffer? */ if (x > n) { debug(F101,"congetbuf char loss","",x-n); x = n; } x = read(0,conbufp,x); conbufn += x; debug(F111,"congetbuf readahead",conbuf,x); } /* C O N C B -- Put console in cbreak mode. */ /* Returns 0 if ok, -1 if not */ int #ifdef CK_ANSIC concb(char esc) #else concb(esc) char esc; #endif /* CK_ANSIC */ /* concb */ { int x, y = 0; debug(F101,"concb constate","",constate); debug(F101,"concb cgmf","",cgmf); debug(F101,"concb backgrd","",backgrd); if (constate == CON_CB) return(0); if (cgmf < 1) /* Did we get console modes yet? */ if (!backgrd) /* No, in background? */ congm(); /* No, try to get them now. */ if (cgmf < 1) /* Still don't have them? */ return(0); /* Give up. */ debug(F101,"concb ttyfd","",ttyfd); debug(F101,"concb ttfdflg","",ttfdflg); #ifdef COMMENT /* This breaks returning to prompt after protocol with "-l 0" */ /* Commented out July 1998 */ if (ttfdflg && ttyfd >= 0 && ttyfd < 3) return(0); #endif /* COMMENT */ x = isatty(0); debug(F101,"concb isatty","",x); if (!x) return(0); /* Only when running on real ttys */ debug(F101,"concb xsuspend","",xsuspend); if (backgrd) /* Do nothing if in background. */ return(0); escchr = esc; /* Make this available to other fns */ ckxech = 1; /* Program can echo characters */ #ifdef aegis conbufn = 0; if (concrp) return(write(1, "\035\002", 2)); if (conuid == input_pad_$uid) {pad_$raw(ios_$stdin, st); return(0);} #endif /* aegis */ #ifdef COHERENT #define SVORPOSIX #endif /* COHERENT */ #ifdef Plan9 x = p9concb(); #else #ifndef SVORPOSIX /* BSD, V7, etc */ debug(F101,"cccbrk.sg_flags concb 1","", cccbrk.sg_flags); debug(F101,"concb stty CBREAK","",0); cccbrk.sg_flags |= (CBREAK|CRMOD); /* Set to character wakeup, */ cccbrk.sg_flags &= ~ECHO; /* no echo. */ debug(F101,"cccbrk.sg_flags concb 2","", cccbrk.sg_flags); errno = 0; /* BSD stty() clears the console buffer. So if anything is waiting in it, we have to read it now to avoid losing it. */ x = conchk(); if (x > 0) congetbuf(x); #ifdef BELLV10 x = ioctl(0,TIOCSETP,&cccbrk); #else x = stty(0,&cccbrk); debug(F101,"cccbrk.sg_flags concb x","", x); #endif /* BELLV10 */ #else /* Sys V and POSIX */ #ifndef OXOS debug(F101,"concb cccbrk.c_flag","",cccbrk.c_lflag); #ifdef QNX /* Don't mess with IEXTEN */ cccbrk.c_lflag &= ~(ICANON|ECHO); #else #ifdef COHERENT cccbrk.c_lflag &= ~(ICANON|ECHO); #else cccbrk.c_lflag &= ~(ICANON|ECHO|IEXTEN); #endif /* COHERENT */ #endif /* QNX */ cccbrk.c_lflag |= ISIG; /* Allow signals in command mode. */ cccbrk.c_iflag |= IGNBRK; /* But ignore BREAK signal */ cccbrk.c_iflag &= ~BRKINT; #else /* OXOS */ debug(F100,"concb OXOS is defined","",0); cccbrk.c_lflag &= ~(ICANON|ECHO); cccbrk.c_cc[VDISCARD] = cccbrk.c_cc[VLNEXT] = CDISABLE; #endif /* OXOS */ #ifdef COMMENT /* Believe it or not, in SCO UNIX, VSUSP is greater than NCC, and so this array reference is out of bounds. It's only a debug() call so who needs it. */ #ifdef VSUSP debug(F101,"concb c_cc[VSUSP]","",cccbrk.c_cc[VSUSP]); #endif /* VSUSP */ #endif /* COMMENT */ #ifndef VINTR debug(F101,"concb c_cc[0]","",cccbrk.c_cc[0]); cccbrk.c_cc[0] = 003; /* Interrupt char is Control-C */ #else debug(F101,"concb c_cc[VINTR]","",cccbrk.c_cc[0]); cccbrk.c_cc[VINTR] = 003; #endif /* VINTR */ #ifndef VQUIT cccbrk.c_cc[1] = escchr; /* escape during packet modes */ #else cccbrk.c_cc[VQUIT] = escchr; #endif /* VQUIT */ #ifndef VEOF cccbrk.c_cc[4] = 1; #else #ifndef OXOS #ifdef VMIN cccbrk.c_cc[VMIN] = 1; #endif /* VMIN */ #else /* OXOS */ cccbrk.c_min = 1; #endif /* OXOS */ #endif /* VEOF */ #ifdef ZILOG cccbrk.c_cc[5] = 0; #else #ifndef VEOL cccbrk.c_cc[5] = 1; #else #ifndef OXOS #ifdef VTIME cccbrk.c_cc[VTIME] = 1; #endif /* VTIME */ #else /* OXOS */ cccbrk.c_time = 1; #endif /* OXOS */ #endif /* VEOL */ #endif /* ZILOG */ errno = 0; #ifdef BSD44ORPOSIX /* Set new modes */ x = tcsetattr(0,TCSADRAIN,&cccbrk); #else /* ATTSV */ /* or the POSIX way */ x = ioctl(0,TCSETAW,&cccbrk); /* the Sys V way */ #endif /* BSD44ORPOSIX */ #endif /* SVORPOSIX */ #ifdef COHERENT #undef SVORPOSIX #endif /* COHERENT */ debug(F101,"concb x","",x); debug(F101,"concb errno","",errno); #ifdef V7 #ifndef MINIX if (kmem[CON] < 0) { qaddr[CON] = initrawq(0); if((kmem[CON] = open("/dev/kmem", 0)) < 0) { fprintf(stderr, "Can't read /dev/kmem in concb.\n"); perror("/dev/kmem"); exit(1); } } #endif /* MINIX */ #endif /* V7 */ #endif /* Plan9 */ if (x > -1) constate = CON_CB; debug(F101,"concb returns","",x); return(x); } /* C O N B I N -- Put console in binary mode */ /* Returns 0 if ok, -1 if not */ int #ifdef CK_ANSIC conbin(char esc) #else conbin(esc) char esc; #endif /* CK_ANSIC */ /* conbin */ { int x; debug(F101,"conbin constate","",constate); if (constate == CON_BIN) return(0); if (!isatty(0)) return(0); /* only for real ttys */ congm(); /* Get modes if necessary. */ debug(F100,"conbin","",0); escchr = esc; /* Make this available to other fns */ ckxech = 1; /* Program can echo characters */ #ifdef aegis conbufn = 0; if (concrp) return(write(1, "\035\002", 2)); if (conuid == input_pad_$uid) { pad_$raw(ios_$stdin, st); return(0); } #endif /* aegis */ #ifdef COHERENT #define SVORPOSIX #endif /* COHERENT */ #ifdef Plan9 return p9conbin(); #else #ifdef SVORPOSIX #ifndef OXOS #ifdef QNX ccraw.c_lflag &= ~(ISIG|ICANON|ECHO); #else #ifdef COHERENT ccraw.c_lflag &= ~(ISIG|ICANON|ECHO); #else ccraw.c_lflag &= ~(ISIG|ICANON|ECHO|IEXTEN); #endif /* COHERENT */ #endif /* QNX */ #else /* OXOS */ ccraw.c_lflag &= ~(ISIG|ICANON|ECHO); ccraw.c_cc[VDISCARD] = ccraw.c_cc[VLNEXT] = CDISABLE; #endif /* OXOS */ ccraw.c_iflag |= IGNPAR; /* Note that for terminal sessions we disable Xon/Xoff flow control to allow the passage ^Q and ^S as data characters for EMACS, and to allow XMODEM transfers to work when C-Kermit is in the middle, etc. Hardware flow control, if in use, is not affected. */ #ifdef ATTSV #ifdef BSD44 ccraw.c_iflag &= ~(IGNBRK|INLCR|IGNCR|ICRNL|IXON|IXANY|IXOFF |INPCK|ISTRIP); #else ccraw.c_iflag &= ~(IGNBRK|INLCR|IGNCR|ICRNL|IUCLC|IXON|IXANY|IXOFF |INPCK|ISTRIP); #endif /* BSD44 */ #else /* POSIX */ ccraw.c_iflag &= ~(IGNBRK|INLCR|IGNCR|ICRNL|IXON|IXOFF|INPCK|ISTRIP); #endif /* ATTSV */ ccraw.c_oflag &= ~OPOST; #ifdef COMMENT /* WHAT THE HECK WAS THIS FOR? The B9600 setting (obviously) prevents CONNECT from working at any speed other than 9600 when you are logged in to the 7300 on a serial line. Maybe some of the other flags are necessary -- if so, put back the ones that are needed. This code is supposed to work the same, no matter whether you are logged in to the 7300 on the real console device, or through a serial port. */ #ifdef ATT7300 ccraw.c_cflag = CLOCAL | B9600 | CS8 | CREAD | HUPCL; #endif /* ATT7300 */ #endif /* COMMENT */ /*** Kermit used to put the console in 8-bit raw mode, but some users have *** pointed out that this should not be done, since some sites actually *** use terminals with parity settings on their Unix systems, and if we *** override the current settings and stop doing parity, then their terminals *** will display blotches for characters whose parity is wrong. Therefore, *** the following two lines are commented out (Larry Afrin, Clemson U): *** *** ccraw.c_cflag &= ~(PARENB|CSIZE); *** ccraw.c_cflag |= (CS8|CREAD); *** *** Sys III/V sites that have trouble with this can restore these lines. ***/ #ifndef VINTR ccraw.c_cc[0] = 003; /* Interrupt char is Ctrl-C */ #else ccraw.c_cc[VINTR] = 003; #endif /* VINTR */ #ifndef VQUIT ccraw.c_cc[1] = escchr; /* Escape during packet mode */ #else ccraw.c_cc[VQUIT] = escchr; #endif /* VQUIT */ #ifndef VEOF ccraw.c_cc[4] = 1; #else #ifndef OXOS #ifdef VMIN ccraw.c_cc[VMIN] = 1; #endif /* VMIN */ #else /* OXOS */ ccraw.c_min = 1; #endif /* OXOS */ #endif /* VEOF */ #ifdef ZILOG ccraw.c_cc[5] = 0; #else #ifndef VEOL ccraw.c_cc[5] = 1; #else #ifndef OXOS #ifdef VTIME ccraw.c_cc[VTIME] = 1; #endif /* VTIME */ #else /* OXOS */ ccraw.c_time = 1; #endif /* OXOS */ #endif /* VEOL */ #endif /* ZILOG */ #ifdef BSD44ORPOSIX x = tcsetattr(0,TCSADRAIN,&ccraw); /* Set new modes. */ #else x = ioctl(0,TCSETAW,&ccraw); #endif /* BSD44ORPOSIX */ #else /* Berkeley, etc. */ x = conchk(); /* Because stty() is destructive */ if (x > 0) congetbuf(x); ccraw.sg_flags |= (RAW|TANDEM); /* Set rawmode, XON/XOFF (ha) */ ccraw.sg_flags &= ~(ECHO|CRMOD); /* Set char wakeup, no echo */ #ifdef BELLV10 x = ioctl(0,TIOCSETP,&ccraw); #else x = stty(0,&ccraw); #endif /* BELLV10 */ #endif /* SVORPOSIX */ #endif /* Plan9 */ if (x > -1) constate = CON_BIN; debug(F101,"conbin returns","",x); return(x); #ifdef COHERENT #undef SVORPOSIX #endif /* COHERENT */ } /* C O N R E S -- Restore the console terminal */ int conres() { int x; debug(F101,"conres cgmf","",cgmf); debug(F101,"conres constate","",constate); if (cgmf < 1) /* Do nothing if modes unchanged */ return(0); if (constate == CON_RES) return(0); if (!isatty(0)) return(0); /* only for real ttys */ debug(F100,"conres isatty ok","",0); ckxech = 0; /* System should echo chars */ #ifdef aegis conbufn = 0; if (concrp) return(write(1, "\035\001", 2)); if (conuid == input_pad_$uid) { pad_$cooked(ios_$stdin, st); constate = CON_RES; return(0); } #endif /* aegis */ #ifdef Plan9 p9conres(); #else #ifdef BSD44ORPOSIX debug(F100,"conres restoring tcsetattr","",0); x = tcsetattr(0,TCSADRAIN,&ccold); #else #ifdef ATTSV debug(F100,"conres restoring ioctl","",0); x = ioctl(0,TCSETAW,&ccold); #else /* BSD, V7, and friends */ #ifdef sony_news /* Sony NEWS */ if (km_con != -1) ioctl(0,TIOCKSET,&km_con); /* Restore console Kanji mode */ #endif /* sony_news */ msleep(100); debug(F100,"conres restoring stty","",0); x = conchk(); /* Because stty() is destructive */ if (x > 0) congetbuf(x); #ifdef BELLV10 x = ioctl(0,TIOCSETP,&ccold); #else x = stty(0,&ccold); #endif /* BELLV10 */ #endif /* ATTSV */ #endif /* BSD44ORPOSIX */ #endif /* Plan9 */ if (x > -1) constate = CON_RES; debug(F101,"conres returns","",x); return(x); } /* C O N O C -- Output a character to the console terminal */ int #ifdef CK_ANSIC conoc(char c) #else conoc(c) char c; #endif /* CK_ANSIC */ /* conoc */ { #ifdef IKSD if (inserver && !local) return(ttoc(c)); #ifdef CK_ENCRYPTION if (inserver && TELOPT_ME(TELOPT_ENCRYPTION)) ck_tn_encrypt(&c,1); #endif /* CK_ENCRYPTION */ #endif /* IKSD */ #ifdef Plan9 return conwrite(&c,1); #else return(write(1,&c,1)); #endif /* Plan9 */ } /* C O N X O -- Write x characters to the console terminal */ int conxo(x,s) int x; char *s; { #ifdef IKSD if (inserver && !local) return(ttol((CHAR *)s,x)); #ifdef CK_ENCRYPTION if (inserver && TELOPT_ME(TELOPT_ENCRYPTION)) ck_tn_encrypt(s,x); #endif /* CK_ENCRYPTION */ #endif /* IKSD */ #ifdef Plan9 return(conwrite(s,x)); #else return(write(1,s,x)); #endif /* Plan9 */ } /* C O N O L -- Write a line to the console terminal */ int conol(s) char *s; { int len; if (!s) s = ""; /* Always do this! */ len = strlen(s); if (len == 0) return(0); #ifdef IKSD if (inserver && !local) return(ttol((CHAR *)s,len)); #ifdef CK_ENCRYPTION if (inserver && TELOPT_ME(TELOPT_ENCRYPTION)) { if (nxpacket < len) { if (xpacket) { free(xpacket); xpacket = NULL; nxpacket = 0; } len = len > 10240 ? len : 10240; xpacket = (CHAR *)malloc(len); if (!xpacket) { fprintf(stderr,"ttol malloc failure\n"); return(-1); } else nxpacket = len; } memcpy(xpacket,s,len); s = (char *)xpacket; ck_tn_encrypt(s,len); } #endif /* CK_ENCRYPTION */ #endif /* IKSD */ #ifdef Plan9 return(conwrite(s,len)); #else return(write(1,s,len)); #endif /* Plan9 */ } /* C O N O L A -- Write an array of lines to the console terminal */ int conola(s) char *s[]; { char * p; int i, x; if (!s) return(0); for (i = 0; ; i++) { p = s[i]; if (!p) p = ""; /* Let's not dump core shall we? */ if (!*p) break; #ifdef IKSD if (inserver && !local) x = ttol((CHAR *)p,(int)strlen(p)); else #endif /* IKSD */ x = conol(p); if (x < 0) return(-1); } return(0); } /* C O N O L L -- Output a string followed by CRLF */ int conoll(s) char *s; { CHAR buf[3]; buf[0] = '\r'; buf[1] = '\n'; buf[2] = '\0'; if (!s) s = ""; #ifdef IKSD if (inserver && !local) { if (*s) ttol((CHAR *)s,(int)strlen(s)); return(ttol(buf,2)); } #endif /* IKSD */ if (*s) conol(s); #ifdef IKSD #ifdef CK_ENCRYPTION if (inserver && TELOPT_ME(TELOPT_ENCRYPTION)) ck_tn_encrypt((char *)buf,2); #endif /* CK_ENCRYPTION */ #endif /* IKSD */ #ifdef Plan9 return(conwrite(buf, 2)); #else return(write(1,buf,2)); #endif /* Plan9 */ } /* C O N C H K -- Return how many characters available at console */ /* We could also use select() here to cover a few more systems that are not covered by any of the following, e.g. HP-UX 9.0x on the model 800. */ int conchk() { static int contyp = 0; /* +1 for isatty, -1 otherwise */ if (contyp == 0) /* This prevents unnecessary */ contyp = (isatty(0) ? 1 : -1); /* duplicated calls to isatty() */ debug(F101,"conchk contyp","",contyp); if (backgrd || (contyp < 0)) return(0); #ifdef aegis if (conbufn > 0) return(conbufn); /* use old count if nonzero */ /* read in more characters */ conbufn = ios_$get(ios_$stdin, ios_$cond_opt, conbuf, (long)sizeof(conbuf), st); if (st.all != status_$ok) conbufn = 0; conbufp = conbuf; return(conbufn); #else #ifdef IKSD if (inserver && !local) return(in_chk(1,ttyfd)); else #endif /* IKSD */ return(in_chk(0,0)); #endif /* aegis */ } /* C O N I N C -- Get a character from the console */ /* Call with timo > 0 to do a timed read, timo == 0 to do an untimed blocking read. Upon success, returns the character. Upon failure, returns -1. A timed read that does not complete within the timeout period returns -2. */ int coninc(timo) int timo; { int n = 0; CHAR ch; int xx; if (conbufn > 0) { /* If something already buffered */ --conbufn; return((unsigned)(*conbufp++ & 0xff)); } errno = 0; /* Clear this */ #ifdef IKSD if (inserver && !local) { xx = ttinc(timo); if (xx < 0) return(ttinctimo ? -2 : -1); else return(xx); } #endif /* IKSD */ #ifdef aegis /* Apollo Aegis only... */ debug(F101,"coninc timo","",timo); fflush(stdout); if (conchk() > 0) { --conbufn; return((unsigned)(*conbufp++ & 0xff)); } #endif /* aegis */ #ifdef TTLEBUF if ( #ifdef IKSD inserver && #endif /* IKSD */ !xlocal ) { if (ttpush >= 0) { debug(F111,"ttinc","ttpush",ttpush); ch = ttpush; ttpush = -1; return(ch); } if (le_data) { if (le_getchar(&ch) > 0) { debug(F111,"ttinc LocalEchoInBuf","ch",ch); return(ch); } } } #endif /* TTLEBUF */ if (timo <= 0) { /* Untimed, blocking read. */ while (1) { /* Keep trying till we get one. */ n = read(0, &ch, 1); /* Read a character. */ if (n == 0) continue; /* Shouldn't happen. */ if (n > 0) { /* If read was successful, */ #ifdef IKSD #ifdef CK_ENCRYPTION debug(F100,"coninc decrypt 1","",0); if (inserver && !local && TELOPT_U(TELOPT_ENCRYPTION)) ck_tn_decrypt((char *)&ch,1); #endif /* CK_ENCRYPTION */ #endif /* IKSD */ return((unsigned)(ch & 0xff)); /* return the character. */ } /* Come here if read() returned an error. */ debug(F101, "coninc(0) errno","",errno); /* Log the error. */ #ifndef OXOS #ifdef SVORPOSIX #ifdef CIE /* CIE Regulus has no EINTR symbol? */ #ifndef EINTR #define EINTR 4 #endif /* EINTR */ #endif /* CIE */ /* This routine is used for several different purposes. In CONNECT mode, it is used to do an untimed, blocking read from the keyboard in the lower CONNECT fork. During local-mode file transfer, it reads a character from the console to interrupt the file transfer (like A for a status report, X to cancel a file, etc). Obviously, we don't want the reads in the latter case to be blocking, or the file transfer would stop until the user typed something. Unfortunately, System V does not allow the console device input buffer to be sampled nondestructively (e.g. by conchk()), so a kludge is used instead. During local-mode file transfer, the SIGQUIT signal is armed and trapped by esctrp(), and this routine pretends to have read the quit character from the keyboard normally. But, kludge or no kludge, the read() issued by this command, under System V only, can fail if a signal -- ANY signal -- is caught while the read is pending. This can occur not only when the user types the quit character, but also during telnet negotiations, when the lower CONNECT fork signals the upper one about an echoing mode change. When this happens, we have to post the read() again. This is apparently not a problem in BSD-based UNIX versions. */ if (errno == EINTR) /* Read interrupted. */ if (conesc) { /* If by SIGQUIT, */ conesc = 0; /* the conesc variable is set, */ return(escchr); /* so return the escape character. */ } else continue; /* By other signal, try again. */ #else /* This might be dangerous, but let's do this on non-System V versions too, since at least one SunOS 4.1.2 user complains of immediate disconnections upon first making a TELNET connection. */ if (errno == EINTR) /* Read interrupted. */ continue; #endif /* SVORPOSIX */ #else /* OXOS */ if (errno == EINTR) /* Read interrupted. */ continue; #endif /* OXOS */ return(-1); /* Error */ } } #ifdef DEBUG if (deblog && timo <= 0) { debug(F100,"coninc timeout logic error","",0); timo = 1; } #endif /* DEBUG */ /* Timed read... */ saval = signal(SIGALRM,timerh); /* Set up timeout handler. */ xx = alarm(timo); /* Set the alarm. */ debug(F101,"coninc alarm set","",timo); if ( #ifdef CK_POSIX_SIG sigsetjmp(sjbuf,1) #else setjmp(sjbuf) #endif /* CK_POSIX_SIG */ ) /* The read() timed out. */ n = -2; /* Code for timeout. */ else n = read(0, &ch, 1); ttimoff(); /* Turn off timer */ if (n > 0) { /* Got character OK. */ #ifdef IKSD #ifdef CK_ENCRYPTION debug(F100,"coninc decrypt 2","",0); if (inserver && !local && TELOPT_U(TELOPT_ENCRYPTION)) ck_tn_decrypt((char *)&ch,1); #endif /* CK_ENCRYPTION */ #endif /* IKSD */ return((unsigned)(ch & 0xff)); /* Return it. */ } /* read() returned an error. Same deal as above, but without the loop. */ debug(F101, "coninc(timo) n","",n); debug(F101, "coninc(timo) errno","",errno); #ifndef OXOS #ifdef SVORPOSIX if (n == -1 && errno == EINTR && conesc != 0) { conesc = 0; return(escchr); /* User entered escape character. */ } #endif /* SVORPOSIX */ if (n == 0 && errno > 0) { /* It's an error */ return(-1); } #endif /* ! OXOS */ return(n); } /* C O N G K S -- Console Get Keyboard Scancode */ #ifndef congks /* This function needs to be filled in with the various system-dependent system calls used by SUNOS, NeXT OS, Xenix, Aviion, etc, to read a full keyboard scan code. Unfortunately there aren't any. */ int congks(timo) int timo; { #ifdef IKSD if (inserver && !local) return(ttinc(timo)); #endif /* IKSD */ return(coninc(timo)); } #endif /* congks */ #ifdef ATT7300 /* A T T D I A L -- Dial up the remote system using internal modem * Purpose: to open and dial a number on the internal modem available on the * ATT7300 UNIX PC. Written by Joe Doupnik. Superceeds version written by * Richard E. Hill, Dickinson, TX. which employed dial(3c). * Uses information in and our status int attmodem. */ attdial(ttname,speed,telnbr) char *ttname,*telnbr; long speed; { char *telnum; attmodem &= ~ISMODEM; /* modem not in use yet */ /* Ensure O_NDELAY is set, else i/o traffic hangs */ /* We turn this flag off once the dial is complete */ fcntl(ttyfd, F_SETFL, fcntl(ttyfd, F_GETFL, 0) | O_NDELAY); /* Condition line, check availability & DATA mode, turn on speaker */ if (ioctl(ttyfd,PIOCOFFHOOK, &dialer) == -1) { printf("cannot access phone\n"); ttclos(0); return (-2); } ioctl(ttyfd,PIOCGETP,&dialer); /* get phone dialer parameters */ if (dialer.c_lineparam & VOICE) { /* phone must be in DATA mode */ printf(" Should not dial with modem in VOICE mode.\n"); printf(" Exit Kermit, switch to DATA and retry call.\n"); ttclos(0); return (-2); } #ifdef ATTTONED /* Old way, tone dialing only. */ dialer.c_lineparam = DATA | DTMF; /* Dial with tones, */ dialer.c_lineparam &= ~PULSE; /* not with pulses. */ #else /* Leave current pulse/tone state alone. */ /* But what about DATA? Add it back if you have trouble. */ /* sys/phone says you get DATA automatically by opening device RDWR */ #endif dialer.c_waitdialtone = 5; /* wait 5 sec for dialtone */ #ifdef COMMENT dialer.c_feedback = SPEAKERON|NORMSPK|RINGON; /* control speaker */ #else /* sys/phone says RINGON used only for incoming voice calls */ dialer.c_feedback &= ~(SOFTSPK|LOUDSPK); dialer.c_feedback |= SPEAKERON|NORMSPK; #endif dialer.c_waitflash = 500; /* 0.5 sec flash hook */ if(ioctl(ttyfd,PIOCSETP,&dialer) == -1) { /* set phone parameters */ printf("Cannot set modem characteristics\n"); ttclos(0); return (-2); } ioctl(ttyfd,PIOCRECONN,0); /* Turns on speaker for pulse */ #ifdef COMMENT fprintf(stderr,"Phone line status. line_par:%o dialtone_wait:%o \ line_status:%o feedback:%o\n", dialer.c_lineparam, dialer.c_waitdialtone, dialer.c_linestatus, dialer.c_feedback); #endif attmodem |= ISMODEM; /* modem is now in-use */ sleep(1); for (telnum = telnbr; *telnum != '\0'; telnum++) /* dial number */ #ifdef ATTTONED /* Tone dialing only */ if (ioctl(ttyfd,PIOCDIAL,telnum) != 0) { perror("Error in dialing"); ttclos(0); return(-2); } #else /* Allow Pulse or Tone dialing */ switch (*telnum) { case 't': case 'T': case '%': /* Tone dialing requested */ dialer.c_lineparam |= DTMF; dialer.c_lineparam &= ~PULSE; if (ioctl(ttyfd,PIOCSETP,&dialer) == -1) { printf("Cannot set modem to tone dialing\n"); ttclos(0); return(-2); } break; case 'd': case 'D': case 'p': case 'P': case '^': dialer.c_lineparam |= PULSE; dialer.c_lineparam &= ~DTMF; if (ioctl(ttyfd,PIOCSETP,&dialer) == -1) { printf("Cannot set modem to pulse dialing\n"); ttclos(0); return(-2); } break; default: if (ioctl(ttyfd,PIOCDIAL,telnum) != 0) { perror("Dialing error"); ttclos(0); return(-2); } break; } #endif ioctl(ttyfd,PIOCDIAL,"@"); /* terminator for data call */ do { /* wait for modems to Connect */ if (ioctl(ttyfd,PIOCGETP,&dialer) != 0) { /* get params */ perror("Cannot get modems to connect"); ttclos(0); return(-2); } } while ((dialer.c_linestatus & MODEMCONNECTED) == 0); /* Turn off O_NDELAY flag now. */ fcntl(ttyfd, F_SETFL, fcntl(ttyfd, F_GETFL, 0) & ~O_NDELAY); signal(SIGHUP, sighup); /* hangup on loss of carrier */ return(0); /* return success */ } /* Offgetty, ongetty functions. These function get the 'getty(1m)' off and restore it to the indicated line. Shell's return codes are: 0: Can't do it. Probably a user logged on. 1: No need. No getty on that line. 2: Done, you should restore the getty when you're done. DOGETY System(3), however, returns them as 0, 256, 512, respectively. Thanks to Kevin O'Gorman, Anarm Software Systems. getoff.sh looks like: geton.sh looks like: setgetty $1 0 setgetty $1 1 err=$? exit $? sleep 2 exit $err */ /* O F F G E T T Y -- Turn off getty(1m) for the communications tty line * and get status so it can be restarted after the line is hung up. */ int offgetty(ttname) char *ttname; { char temp[30]; while (*ttname != '\0') ttname++; /* seek terminator of path */ ttname -= 3; /* get last 3 chars of name */ sprintf(temp,"/usr/bin/getoff.sh %s",ttname); return(zsyscmd(temp)); } /* O N G E T T Y -- Turn on getty(1m) for the communications tty line */ int ongetty(ttname) char *ttname; { char temp[30]; while (*ttname != '\0') ttname++; /* comms tty path name */ ttname -= 3; sprintf(temp,"/usr/bin/geton.sh %s",ttname); return(zsyscmd(temp)); } #endif /* ATT7300 */ /* T T S C A R R -- Set ttcarr variable, controlling carrier handling. * * 0 = Off: Always ignore carrier. E.g. you can connect without carrier. * 1 = On: Heed carrier, except during dialing. Carrier loss gives disconnect. * 2 = Auto: For "modem direct": The same as "Off". * For real modem types: Heed carrier during connect, but ignore * it anytime else. Compatible with pre-5A C-Kermit versions. * * As you can see, this setting does not affect dialing, which always ignores * carrier (unless there is some special exception for some modem type). It * does affect ttopen() if it is set before ttopen() is used. This setting * takes effect on the next call to ttopen()/ttpkt()/ttvt(). And they are * (or should be) always called before any communications is tried, which * means that, practically speaking, the effect is immediate. * * Of course, nothing of this applies to remote mode (xlocal = 0). * * Someone has yet to uncover how to manipulate the carrier in the BSD * environment (or any non-termio using environment). Until that time, this * will simply be a no-op for BSD. * * Note that in previous versions, the carrier was most often left unchanged * in ttpkt()/ttvt() unless they were called with FLO_DIAL or FLO_DIAX. This * has changed. Now it is controlled by ttcarr in conjunction with these * modes. */ int ttscarr(carrier) int carrier; { ttcarr = carrier; debug(F101, "ttscarr","",ttcarr); return(ttcarr); } /* C A R R C T L -- Set tty modes for carrier treatment. * * Sets the appropriate bits in a termio or sgttyb struct for carrier control * (actually, there are no bits in sgttyb for that), or performs any other * operations needed to control this on the current system. The function does * not do the actual TCSETA or stty, since often we want to set other bits too * first. Don't call this function when xlocal is 0, or the tty is not opened. * * We don't know how to do anything like carrier control on non-ATTSV systems, * except, apparently, ultrix. See above. It is also known that this doesn't * have much effect on a Xenix system. For Xenix, one should switch back and * forth between the upper and lower case device files. Maybe later. * Presently, Xenix will stick to the mode it was opened with. * * carrier: 0 = ignore carrier, 1 = require carrier. * The current state is saved in curcarr, and checked to save labour. */ #ifdef SVORPOSIX int #ifdef BSD44ORPOSIX carrctl(ttpar, carrier) struct termios *ttpar; int carrier; #else /* ATTSV */ carrctl(ttpar, carrier) struct termio *ttpar; int carrier; #endif /* BSD44ORPOSIX */ /* carrctl */ { debug(F101, "carrctl","",carrier); if (carrier) ttpar->c_cflag &= ~CLOCAL; else ttpar->c_cflag |= CLOCAL; return(0); } #else /* Berkeley, V7, et al... */ int carrctl(ttpar, carrier) struct sgttyb *ttpar; int carrier; { debug(F101, "carrctl","",carrier); if (carrier == curcarr) return(0); curcarr = carrier; #ifdef ultrix #ifdef COMMENT /* Old code from somebody at DEC that tends to get stuck, time out, etc. */ if (carrier) { ioctl(ttyfd, TIOCMODEM, &temp); ioctl(ttyfd, TIOCHPCL, 0); } else { /* (According to the manuals, TIOCNCAR should be preferred */ /* over TIOCNMODEM...) */ ioctl(ttyfd, TIOCNMODEM, &temp); } #else /* New code from Jamie Watson that, he says, eliminates the problems. */ if (carrier) { ioctl(ttyfd, TIOCCAR); ioctl(ttyfd, TIOCHPCL); } else { ioctl(ttyfd, TIOCNCAR); } #endif /* COMMENT */ #endif /* ultrix */ return(0); } #endif /* SVORPOSIX */ /* T T G M D M -- Get modem signals */ /* Looks for RS-232 modem signals, and returns those that are on in as its return value, in a bit mask composed of the BM_xxx values defined in ckcdeb.h. Returns: -3 Not implemented -2 if the communication device does not have modem control (e.g. telnet) -1 on error. >= 0 on success, with a bit mask containing the modem signals that are on. */ /* Define the symbol K_MDMCTL if we have Sys V R3 / 4.3 BSD style modem control, namely the TIOCMGET ioctl. */ #ifdef BSD43 #define K_MDMCTL #endif /* BSD43 */ #ifdef SUNOS4 #define K_MDMCTL #endif /* SUNOS4 */ /* SCO OpenServer R5.0.4. The TIOCMGET definition is hardwired in because it is skipped in termio.h when _POSIX_SOURCE is defined. But _POSIX_SOURCE must be defined in order to get the high serial speeds that are new to 5.0.4. However, the regular SCO drivers do not implement TIOCMGET, so the ioctl() returns -1 with errno 22 (invalid function). But third-party drivers, e.g. for Digiboard, do implement it, and so it should work on ports driven by those drivers. */ #ifdef SCO_OSR504 #ifndef TIOCMGET #define TIOCMGET (('t'<<8)|29) #endif /* TIOCMGET */ #endif /* SCO_OSR504 */ #ifdef CK_SCOV5 /* Because POSIX strictness in won't let us see these. */ #ifndef TIOCM_DTR #define TIOCM_DTR 0x0002 /* data terminal ready */ #define TIOCM_RTS 0x0004 /* request to send */ #define TIOCM_CTS 0x0020 /* clear to send */ #define TIOCM_CAR 0x0040 /* carrier detect */ #define TIOCM_RNG 0x0080 /* ring */ #define TIOCM_DSR 0x0100 /* data set ready */ #define TIOCM_CD TIOCM_CAR #define TIOCM_RI TIOCM_RNG #endif /* TIOCM_DTR */ #endif /* CK_SCOV5 */ #ifdef QNX #define K_MDMCTL #else #ifdef TIOCMGET #define K_MDMCTL #endif /* TIOCMGET */ #endif /* QNX */ /* "A serial communication program that can't read modem signals is like a car without windows." */ int ttgmdm() { #ifdef QNX #include unsigned long y, mdmbits[2]; int x, z = 0; if (xlocal && ttyfd < 0) return(-1); #ifdef NETCONN if (netconn) { /* Network connection */ #ifdef TN_COMPORT if (istncomport()) { gotsigs = 1; return(tngmdm()); } else #endif /* TN_COMPORT */ return(-2); /* No modem signals */ } #endif /* NETCONN */ #ifdef NETCMD if (ttpipe) return(-2); #endif /* NETCMD */ #ifdef NETPTY if (ttpty) return(-2); #endif /* NETPTY */ mdmbits[0] = 0L; mdmbits[1] = 0L; /* * From : * * SERIAL devices (all Dev.ser versions) * 0 : DTR 8 = Data Bits 0 16 - reserved 24 - reserved * 1 : RTS 9 = Data Bits 1 17 - reserved 25 - reserved * 2 = Out 1 10 = Stop Bits 18 - reserved 26 - reserved * 3 = Int Enable 11 = Par Enable 19 - reserved 27 - reserved * 4 = Loop 12 = Par Even 20 = CTS 28 - reserved * 5 - reserved 13 = Par Stick 21 = DSR 29 - reserved * 6 - reserved 14 : Break 22 = RI 30 - reserved * 7 - reserved 15 = 0 23 = CD 31 - reserved */ errno = 0; x = qnx_ioctl(ttyfd, QCTL_DEV_CTL, &mdmbits[0], 8, &mdmbits[0], 4); debug(F101,"ttgmdm qnx_ioctl","",x); debug(F101,"ttgmdm qnx_ioctl errno","",errno); if (!x) { debug(F101,"ttgmdm qnx_ioctl mdmbits[0]","",mdmbits[0]); debug(F101,"ttgmdm qnx_ioctl mdmbits[1]","",mdmbits[1]); y = mdmbits[0]; if (y & 0x000001L) z |= BM_DTR; /* Bit 0 */ if (y & 0x000002L) z |= BM_RTS; /* Bit 1 */ if (y & 0x100000L) z |= BM_CTS; /* Bit 20 */ if (y & 0x200000L) z |= BM_DSR; /* Bit 21 */ if (y & 0x400000L) z |= BM_RNG; /* Bit 22 */ if (y & 0x800000L) z |= BM_DCD; /* Bit 23 */ debug(F101,"ttgmdm qnx result","",z); debug(F110,"ttgmdm qnx CD = ",(z & BM_DCD) ? "On" : "Off", 0); gotsigs = 1; return(z); } else return(-1); #else /* QNX */ #ifdef HPUX /* HPUX has its own way */ int x, z; #ifdef HPUX10 /* Modem flag word */ mflag y; /* mflag typedef'd in */ #else #ifdef HPUX9 mflag y; #else #ifdef HPUX8 mflag y; #else unsigned long y; /* Not sure about pre-8.0... */ #endif /* HPUX8 */ #endif /* HPUX9 */ #endif /* HPUX10 */ if (xlocal && ttyfd < 0) return(-1); #ifdef NETCONN if (netconn) { /* Network connection */ #ifdef TN_COMPORT if (istncomport()) { gotsigs = 1; return(tngmdm()); } else #endif /* TN_COMPORT */ return(-2); /* No modem signals */ } #endif /* NETCONN */ #ifdef NETCMD if (ttpipe) return(-2); #endif /* NETCMD */ #ifdef NETPTY if (ttpty) return(-2); #endif /* NETPTY */ if (xlocal) /* Get modem signals */ x = ioctl(ttyfd,MCGETA,&y); else x = ioctl(0,MCGETA,&y); if (x < 0) return(-1); debug(F101,"ttgmdm","",y); z = 0; /* Initialize return value */ /* Now set bits for each modem signal that is reported to be on. */ #ifdef MCTS /* Clear To Send */ debug(F101,"ttgmdm HPUX CTS","",y & MCTS); if (y & MCTS) z |= BM_CTS; #endif #ifdef MDSR /* Data Set Ready */ debug(F101,"ttgmdm HPUX DSR","",y & MDSR); if (y & MDSR) z |= BM_DSR; #endif #ifdef MDCD /* Carrier */ debug(F101,"ttgmdm HPUX DCD","",y & MDCD); if (y & MDCD) z |= BM_DCD; #endif #ifdef MRI /* Ring Indicate */ debug(F101,"ttgmdm HPUX RI","",y & MRI); if (y & MRI) z |= BM_RNG; #endif #ifdef MDTR /* Data Terminal Ready */ debug(F101,"ttgmdm HPUX DTR","",y & MDTR); if (y & MDTR) z |= BM_DTR; #endif #ifdef MRTS /* Request To Send */ debug(F101,"ttgmdm HPUX RTS","",y & MRTS); if (y & MRTS) z |= BM_RTS; #endif gotsigs = 1; return(z); #else /* ! HPUX */ #ifdef K_MDMCTL /* Note, TIOCMGET might already have been defined in or elsewhere. If not, we try including -- if this blows up then more ifdefs are needed. */ #ifndef TIOCMGET #include #endif /* TIOCMGET */ int x, y, z; debug(F100,"ttgmdm K_MDMCTL defined","",0); #ifdef NETCONN if (netconn) { /* Network connection */ #ifdef TN_COMPORT if (istncomport()) { gotsigs = 1; return(tngmdm()); } else #endif /* TN_COMPORT */ return(-2); /* No modem signals */ } #endif /* NETCONN */ #ifdef NETCMD if (ttpipe) return(-2); #endif /* NETCMD */ #ifdef NETPTY if (ttpty) return(-2); #endif /* NETPTY */ if (xlocal && ttyfd < 0) return(-1); if (xlocal) x = ioctl(ttyfd,TIOCMGET,&y); /* Get modem signals. */ else x = ioctl(0,TIOCMGET,&y); debug(F101,"ttgmdm TIOCMGET ioctl","",x); if (x < 0) { debug(F101,"ttgmdm errno","",errno); return(-1); } debug(F101,"ttgmdm bits","",y); z = 0; /* Initialize return value. */ #ifdef TIOCM_CTS /* Clear To Send */ if (y & TIOCM_CTS) z |= BM_CTS; debug(F101,"ttgmdm TIOCM_CTS defined","",TIOCM_CTS); #else debug(F100,"ttgmdm TIOCM_CTS not defined","",0); #endif #ifdef TIOCM_DSR /* Data Set Ready */ if (y & TIOCM_DSR) z |= BM_DSR; debug(F101,"ttgmdm TIOCM_DSR defined","",TIOCM_DSR); #else debug(F100,"ttgmdm TIOCM_DSR not defined","",0); #endif #ifdef TIOCM_CAR /* Carrier */ if (y & TIOCM_CAR) z |= BM_DCD; debug(F101,"ttgmdm TIOCM_CAR defined","",TIOCM_CAR); #else debug(F100,"ttgmdm TIOCM_CAR not defined","",0); #endif #ifdef TIOCM_RNG /* Ring Indicate */ if (y & TIOCM_RNG) z |= BM_RNG; debug(F101,"ttgmdm TIOCM_RNG defined","",TIOCM_RNG); #else debug(F100,"ttgmdm TIOCM_RNG not defined","",0); #endif #ifdef TIOCM_DTR /* Data Terminal Ready */ if (y & TIOCM_DTR) z |= BM_DTR; debug(F101,"ttgmdm TIOCM_DTR defined","",TIOCM_DTR); #else debug(F100,"ttgmdm TIOCM_DTR not defined","",0); #endif #ifdef TIOCM_RTS /* Request To Send */ if (y & TIOCM_RTS) z |= BM_RTS; debug(F101,"ttgmdm TIOCM_RTS defined","",TIOCM_RTS); #else debug(F100,"ttgmdm TIOCM_RTS not defined","",0); #endif gotsigs = 1; return(z); #else /* !K_MDMCTL catch-All */ debug(F100,"ttgmdm K_MDMCTL not defined","",0); #ifdef TIOCMGET debug(F100,"ttgmdm TIOCMGET defined","",0); #else debug(F100,"ttgmdm TIOCMGET not defined","",0); #endif /* TIOCMGET */ #ifdef _SVID3 debug(F100,"ttgmdm _SVID3 defined","",0); #else debug(F100,"ttgmdm _SVID3 not defined","",0); #endif /* _SVID3 */ #ifdef NETCONN if (netconn) { /* Network connection */ #ifdef TN_COMPORT if (istncomport()) { gotsigs = 1; return(tngmdm()); } else #endif /* TN_COMPORT */ return(-2); /* No modem signals */ } #endif /* NETCONN */ #ifdef NETCMD if (ttpipe) return(-2); #endif /* NETCMD */ #ifdef NETPTY if (ttpty) return(-2); #endif /* NETPTY */ return(-3); /* Sorry, I don't know how... */ #endif /* K_MDMCTL */ #endif /* HPUX */ #endif /* QNX */ } /* P S U S P E N D -- Put this process in the background. */ /* Call with flag nonzero if suspending is allowed, zero if not allowed. Returns 0 on apparent success, -1 on failure (flag was zero, or kill() returned an error code. */ int psuspend(flag) int flag; { #ifdef RTU extern int rtu_bug; #endif /* RTU */ if (flag == 0) return(-1); #ifdef NOJC return(-1); #else #ifdef SIGTSTP /* The big question here is whether job control is *really* supported. There's no way Kermit can know for sure. The fact that SIGTSTP is defined does not guarantee the Unix kernel supports it, and the fact that the Unix kernel supports it doesn't guarantee that the user's shell (or other process that invoked Kermit) supports it. */ #ifdef RTU rtu_bug = 1; #endif /* RTU */ if (kill(0,SIGSTOP) < 0 #ifdef MIPS /* Let's try this for MIPS too. */ && kill(getpid(),SIGSTOP) < 0 #endif /* MIPS */ ) { /* If job control, suspend the job */ perror("suspend"); debug(F101,"psuspend error","",errno); return(-1); } debug(F100,"psuspend ok","",0); return(0); #else return(-1); #endif /* SIGTSTP */ #endif /* NOJC */ } /* setuid package, by Kristoffer Eriksson, with contributions from Dean Long and fdc. */ /* The following is for SCO when CK_ANSILIBS is defined... */ #ifdef M_UNIX #ifdef CK_ANSILIBS #ifndef NOGETID_PROTOS #define NOGETID_PROTOS #endif /* NOGETID_PROTOS */ #endif /* CK_ANSILIBS */ #endif /* M_UNIX */ #ifndef _POSIX_SOURCE #ifndef SUNOS4 #ifndef NEXT #ifndef PS2AIX10 #ifndef sequent #ifndef HPUX9 #ifndef HPUX10 #ifndef COHERENT #ifndef NOGETID_PROTOS _PROTOTYP( UID_T getuid, (void) ); _PROTOTYP( UID_T geteuid, (void) ); _PROTOTYP( UID_T getreuid, (void) ); _PROTOTYP( UID_T getgid, (void) ); _PROTOTYP( UID_T getegid, (void) ); _PROTOTYP( UID_T getregid, (void) ); #endif /* NOGETID_PROTOS */ #else _PROTOTYP( UID_T getreuid, (void) ); _PROTOTYP( UID_T getregid, (void) ); #endif /* COHERENT */ #endif /* HPUX10 */ #endif /* HPUX9 */ #endif /* sequent */ #endif /* PS2AIX10 */ #endif /* NEXT */ #endif /* SUNOS4 */ #endif /* _POSIX_SOURCE */ /* Subject: Set-user-id To: fdc@watsun.cc.columbia.edu (Frank da Cruz) Date: Sat, 21 Apr 90 4:48:25 MES From: Kristoffer Eriksson This is a set of functions to be used in programs that may be run set-user-id and/or set-group-id. They handle both the case where the program is not run with such privileges (nothing special happens then), and the case where one or both of these set-id modes are used. The program is made to run with the user's real user and group ids most of the time, except for when more privileges are needed. Don't set-user-id to "root". This works on System V and POSIX. In BSD, it depends on the "saved-set-user-id" feature. */ #define UID_ROOT 0 /* Root user and group ids */ #define GID_ROOT 0 /* The following code defines the symbol SETEUID for UNIX systems based on BSD4.4 (either -Encumbered or -Lite). This program will then use seteuid() and setegid() instead of setuid() and setgid(), which still don't allow arbitrary switching. It also avoids setreuid() and setregid(), which are included in BSD4.4 for compatibility only, are insecure, and print warnings to stderr under at least one system (NetBSD 1.0). Note that POSIX systems should still use setuid() and setgid(); the seteuid() and setegid() functions are BSD4.4 extensions to the POSIX model. Mike Long , 8/94. */ #ifdef BSD44 #define SETEUID #endif /* BSD44 */ /* The following construction automatically defines the symbol SETREUID for UNIX versions based on Berkeley Unix 4.2 and 4.3. If this symbol is defined, then this program will use getreuid() and getregid() calls in preference to getuid() and getgid(), which in Berkeley-based Unixes do not allow arbitrary switching back and forth of real & effective uid. This construction also allows -DSETREUID to be put on the cc command line for any system that has and wants to use setre[ug]id(). It also prevents automatic definition of SETREUID if -DNOSETREU is included on the cc command line (or otherwise defined). */ #ifdef FT18 /* None of this for Fortune. */ #define NOSETREU #endif /* FT18 */ #ifdef ANYBSD #ifndef BSD29 #ifndef BSD41 #ifndef SETREUID #ifndef NOSETREU #ifndef SETEUID #define SETREUID #endif /* SETEUID */ #endif /* NOSETREU */ #endif /* SETREUID */ #endif /* !BSD41 */ #endif /* !BSD29 */ #endif /* ANYBSD */ /* Variables for user and group IDs. */ static UID_T realuid = (UID_T) -1, privuid = (UID_T) -1; static GID_T realgid = (GID_T) -1, privgid = (GID_T) -1; /* P R I V _ I N I -- Initialize privileges package */ /* Called as early as possible in a set-uid or set-gid program to store the * set-to uid and/or gid and step down to the users real uid and gid. The * stored id's can be temporarily restored (allowed in System V) during * operations that require the privilege. Most of the time, the program * should execute in unpriviliged state, to not impose any security threat. * * Note: Don't forget that access() always uses the real id:s to determine * file access, even with privileges restored. * * Returns an error mask, with error values or:ed together: * 1 if setuid() fails, * 2 if setgid() fails, and * 4 if the program is set-user-id to "root", which can't be handled. * * Only the return value 0 indicates real success. In case of failure, * those privileges that could be reduced have been, at least, but the * program should be aborted none-the-less. * * Also note that these functions do not expect the uid or gid to change * without their knowing. It may work if it is only done temporarily, but * you're on your own. */ int priv_ini() { int err = 0; #ifndef HAVE_LOCKDEV /* Save real ID:s. */ realuid = getuid(); realgid = getgid(); /* Save current effective ID:s, those set to at program exec. */ privuid = geteuid(); privgid = getegid(); /* If running set-uid, go down to real uid, otherwise remember that * no privileged uid is available. * * Exceptions: * * 1) If the real uid is already "root" and the set-uid uid (the * initial effective uid) is not "root", then we would have trouble * if we went "down" to "root" here, and then temporarily back to the * set-uid uid (not "root") and then again tried to become "root". I * think the "saved set-uid" is lost when changing uid from effective * uid "root", which changes all uid, not only the effective uid. But * in this situation, we can simply go to "root" and stay there all * the time. That should give sufficient privilege (understatement!), * and give the right uids for subprocesses. * * 2) If the set-uid (the initial effective uid) is "root", and we * change uid to the real uid, we can't change it back to "root" when * we need the privilege, for the same reason as in 1). Thus, we can't * handle programs that are set-user-id to "root" at all. The program * should be stopped. Use some other uid. "root" is probably too * privileged for such things, anyway. (The uid is reverted to the * real uid until termination.) * * These two exceptions have the effect that the "root" uid will never * be one of the two uids that are being switched between, which also * means we don't have to check for such cases in the switching * functions. * * Note that exception 1) is handled by these routines (by constantly * running with uid "root", while exception 2) is a serious error, and * is not provided for at all in the switching functions. */ if (realuid == privuid) privuid = (UID_T) -1; /* Not running set-user-id. */ /* If running set-gid, go down to real gid, otherwise remember that * no privileged gid is available. * * There are no exception like there is for the user id, since there * is no group id that is privileged in the manner of uid "root". * There could be equivalent problems for group changing if the * program sometimes ran with uid "root" and sometimes not, but * that is already avoided as explained above. * * Thus we can expect always to be able to switch to the "saved set- * gid" when we want, and back to the real gid again. You may also * draw the conclusion that set-gid provides for fewer hassles than * set-uid. */ #ifdef SUIDDEBUG fprintf(stderr,"UID_ROOT=%d\n",UID_ROOT); fprintf(stderr,"realuid=%d\n",realuid); fprintf(stderr,"privuid=%d\n",privuid); #endif /* SUIDDEBUG */ if (realgid == privgid) /* If not running set-user-id, */ privgid = (GID_T) -1; /* remember it this way. */ err = priv_off(); /* Turn off setuid privilege. */ if (privuid == UID_ROOT) /* If setuid to root, */ err |= 4; /* return this error. */ if (realuid == UID_ROOT) { /* If real id is root, */ privuid = (UID_T) -1; /* stay root at all times. */ #ifdef ATT7300 /* If Kermit installed SUID uucp and user is running as root */ err &= ~1; /* System V R0 does not save UID */ #endif /* ATT7300 */ } #endif /* HAVE_LOCKDEV */ return(err); } /* Macros for hiding the differences in UID/GID setting between various Unix * systems. These macros should always be called with both the privileged ID * and the non-privileged ID. The one in the second argument, will become the * effective ID. The one in the first argument will be retained for later * retrieval. */ #ifdef SETREUID #ifdef SAVEDUID /* On BSD systems with the saved-UID feature, we just juggle the effective * UID back and forth, and leave the real UID at its true value. The kernel * allows switching to both the current real UID, the effective UID, and the * UID which the program is set-UID to. The saved set-UID always holds the * privileged UID for us, and the real UID will always be the non-privileged, * and we can freely choose one of them for the effective UID at any time. */ #define switchuid(hidden,active) setreuid( (UID_T) -1, active) #define switchgid(hidden,active) setregid( (GID_T) -1, active) #else /* SETREUID,!SAVEDUID */ /* On systems with setreXid() but without the saved-UID feature, notably * BSD 4.2, we swap the real and effective UIDs each time. It's * the effective UID that we are interested in, but we have to retain the * unused UID somewhere to enable us to restore it later, and we do this * in the real UID. The kernel only allows switching to either the current * real or the effective UID, unless you're "root". */ #define switchuid(hidden,active) setreuid(hidden,active) #define switchgid(hidden,active) setregid(hidden,active) #endif #else /* !SETREUID, !SAVEDUID */ #ifdef SETEUID /* BSD 4.4 works similarly to System V and POSIX (see below), but uses seteXid() instead of setXid() to change effective IDs. In addition, the seteXid() functions work the same for "root" as for other users. */ #define switchuid(hidden,active) seteuid(active) #define switchgid(hidden,active) setegid(active) #else /* !SETEUID */ /* On System V and POSIX, the only thing we can change is the effective UID * (unless the current effective UID is "root", but initsuid() avoids that for * us). The kernel allows switching to the current real UID or to the saved * set-UID. These are always set to the non-privileged UID and the privileged * UID, respectively, and we only change the effective UID. This breaks if * the current effective UID is "root", though, because for "root" setuid/gid * becomes more powerful, which is why initsuid() treats "root" specially. * Note: That special treatment maybe could be ignored for BSD? Note: For * systems that don't fit any of these four cases, we simply can't support * set-UID. */ #define switchuid(hidden,active) setuid(active) #define switchgid(hidden,active) setgid(active) #endif /* SETEUID */ #endif /* SETREUID */ /* P R I V _ O N -- Turn on the setuid and/or setgid */ /* Go to the privileged uid (gid) that the program is set-user-id * (set-group-id) to, unless the program is running unprivileged. * If setuid() fails, return value will be 1. If getuid() fails it * will be 2. Return immediately after first failure, and the function * tries to restore any partial work done. Returns 0 on success. * Group id is changed first, since it is less serious than user id. */ int priv_on() { #ifndef HAVE_LOCKDEV if (privgid != (GID_T) -1) if (switchgid(realgid,privgid)) return(2); if (privuid != (UID_T) -1) if (switchuid(realuid,privuid)) { if (privgid != (GID_T) -1) switchgid(privgid,realgid); return(1); } #endif /* HAVE_LOCKDEV */ return(0); } /* P R I V _ O F F -- Turn on the real uid and gid */ /* Return to the unprivileged uid (gid) after an temporary visit to * privileged status, unless the program is running without set-user-id * (set-group-id). Returns 1 for failure in setuid() and 2 for failure * in setgid() or:ed together. The functions tries to return both uid * and gid to unprivileged state, regardless of errors. Returns 0 on * success. */ int priv_off() { int err = 0; #ifndef HAVE_LOCKDEV if (privuid != (UID_T) -1) if (switchuid(privuid,realuid)) err |= 1; if (privgid != (GID_T) -1) if (switchgid(privgid,realgid)) err |= 2; #endif /* HAVE_LOCKDEV */ return(err); } /* Turn off privilege permanently. No going back. This is necessary before * a fork() on BSD43 machines that don't save the setUID or setGID, because * we swap the real and effective ids, and we don't want to let the forked * process swap them again and get the privilege back. It will work on other * machines too, such that you can rely on its effect always being the same, * for instance, even when you're in priv_on() state when this is called. * (Well, that part about "permanent" is on System V only true if you follow * this with a call to exec(), but that's what we want it for anyway.) * Added by Dean Long -- dlong@midgard.ucsc.edu */ int priv_can() { #ifndef HAVE_LOCKDEV #ifdef SETREUID int err = 0; if (privuid != (UID_T) -1) if (setreuid(realuid,realuid)) err |= 1; if (privgid != (GID_T) -1) if (setregid(realgid,realgid)) err |= 2; return(err); #else #ifdef SETEUID int err = 0; if (privuid != (UID_T) -1) if (setuid(realuid)) { debug(F101,"setuid failed","",errno); err |= 1; debug(F101,"ruid","",getuid()); debug(F101,"euid","",geteuid()); } debug(F101,"setuid","",realuid); if (privgid != (GID_T) -1) if (setgid(realgid)) { debug(F101,"setgid failed","",errno); err |= 2; debug(F101,"rgid","",getgid()); debug(F101,"egid","",getegid()); } debug(F101,"setgid","",realgid); return(err); #else /* Easy way of using setuid()/setgid() instead of setreuid()/setregid().*/ return(priv_off()); #endif /* SETEUID */ #endif /* SETREUID */ #else return(0); #endif /* HAVE_LOCKDEV */ } /* P R I V _ O P N -- For opening protected files or devices. */ int priv_opn(name, modes) char *name; int modes; { int x; priv_on(); /* Turn privileges on */ debug(F111,"priv_opn",name,modes); errno = 0; x = open(name, modes); /* Try to open the device */ debug(F101,"priv_opn result","",x); debug(F101,"priv_opn errno","",errno); priv_off(); /* Turn privileges off */ return(x); /* Return open's return code */ } /* P R I V _ C H K -- Check privileges. */ /* Try to turn them off. If turning them off did not succeed, cancel them */ int priv_chk() { int x, y = 0; x = priv_off(); /* Turn off privs. */ if (x != 0 || getuid() == privuid || geteuid() == privuid) y = priv_can(); if (x != 0 || getgid() == privgid || getegid() == privgid) y = y | priv_can(); return(y); } UID_T real_uid() { return(realuid); } VOID ttimoff() { /* Turn off any timer interrupts */ /* int xx; */ /* As of 5A(183), we set SIGALRM to SIG_IGN (to ignore alarms) rather than to SIG_DFL (to catch alarms, or if there is no handler, to exit). This is to cure (mask, really) a deeper problem with stray alarms that occurs on some systems, possibly having to do with sleep(), that caused core dumps. It should be OK to do this, because no code in this module uses nested alarms. (But we still have to watch out for SCRIPT and DIAL...) */ /* xx = */ alarm(0); /* debug(F101,"ttimoff alarm","",xx); */ if (saval) { /* Restore any previous */ signal(SIGALRM,saval); /* alarm handler. */ /* debug(F101,"ttimoff alarm restoring saval","",saval); */ saval = NULL; } else { signal(SIGALRM,SIG_IGN); /* Used to be SIG_DFL */ /* debug(F100,"ttimoff alarm SIG_IGN","",0); */ } } int tt_is_secure() { /* Tells whether the current connection is secure */ if (ttyfd == -1) return(0); if (0 #ifdef SSHBUILTIN || IS_SSH() #endif /* SSHBUILTIN */ #ifdef CK_ENCRYPTION || ck_tn_encrypting() && ck_tn_decrypting() #endif /* CK_ENCRYPTION */ #ifdef CK_SSL || tls_active_flag || ssl_active_flag #endif /* CK_SSL */ #ifdef RLOGCODE #ifdef CK_KERBEROS #ifdef CK_ENCRYPTION || ttnproto == NP_EK4LOGIN || ttnproto == NP_EK5LOGIN #endif /* CK_ENCRYPTION */ #endif /* CK_KERBEROS */ #endif /* RLOGCODE */ ) return(1); return(0); } #ifdef CK_REDIR /* External protocol handler parameters from ckuus3.c */ extern int exp_handler, exp_stderr, exp_timo; #ifdef SELECT #ifdef NETPTY /* The right size is 24576 */ #ifndef PTY_PBUF_SIZE /* Size of buffer to read from pty */ #define PTY_PBUF_SIZE 24576 /* and write to net. */ #endif /* PTY_PBUF_SIZE */ #ifndef PTY_TBUF_SIZE /* Size of buffer to read from net */ #define PTY_TBUF_SIZE 24576 /* and write to pty. */ #endif /* PTY_TBUF_SIZE */ #ifdef O_NDELAY /* Whether to use nonblocking */ #ifndef PTY_NO_NDELAY /* reads on the pseudoterminal */ #ifndef PTY_USE_NDELAY #define PTY_USE_NDELAY #endif /* PTY_USE_NDELAY */ #endif /* PTY_NO_NDELAY */ #endif /* O_NDELAY */ #ifndef HAVE_OPENPTY #ifndef USE_CKUPTY_C #define USE_CKUPTY_C #endif /* USE_CKUPTY_C */ #endif /* HAVE_OPENPTY */ VOID pty_make_raw(fd) int fd; { int x = -23, i; #ifdef BSD44ORPOSIX /* POSIX */ struct termios tp; #else #ifdef ATTSV /* AT&T UNIX */ #ifdef CK_ANSIC struct termio tp = {0}; #else struct termio tp; #endif /* CK_ANSIC */ #else struct sgttyb tp; /* Traditional */ #endif /* ATTSV */ #endif /* BSD44ORPOSIX */ debug(F101,"pty_make_raw fd","",fd); errno = 0; #ifdef BSD44ORPOSIX /* POSIX */ x = tcgetattr(fd,&tp); debug(F101,"pty_make_raw tcgetattr","",x); #else #ifdef ATTSV /* AT&T UNIX */ x = ioctl(fd,TCGETA,&tp); debug(F101,"pty_make_raw TCGETA ioctl","",x); #else x = gtty(fd,&tp); debug(F101,"pty_make_raw ttty","",x); #endif /* ATTSV */ #endif /* BSD44ORPOSIX */ debug(F101,"pty_make_raw GET errno","",errno); #ifdef USE_CFMAKERAW errno = 0; cfmakeraw(&tp); debug(F101,"pty_make_raw cfmakeraw errno","",errno); #else /* USE_CFMAKERAW */ #ifdef COMMENT /* This very simple version recommended by Serg Iakolev doesn't work */ tp.c_lflag &= ~(ECHO|ICANON|IEXTEN|ISIG); tp.c_iflag &= ~(BRKINT|ICRNL|INPCK|ISTRIP|IXON); tp.c_cflag &= ~(CSIZE|PARENB); tp.c_cflag |= CS8; tp.c_oflag &= ~(OPOST); tp.c_cc[VMIN] = 1; tp.c_cc[VTIME] = 0; debug(F101,"pty_make_raw 1 c_cc[] NCCS","",NCCS); debug(F101,"pty_make_raw 1 iflags","",tp.c_iflag); debug(F101,"pty_make_raw 1 oflags","",tp.c_oflag); debug(F101,"pty_make_raw 1 lflags","",tp.c_lflag); debug(F101,"pty_make_raw 1 cflags","",tp.c_cflag); #else #ifdef COMMENT /* In this version we unset everything and then set only the bits we know we need. */ /* iflags */ tp.c_iflag = 0L; tp.c_iflag |= IGNBRK; #ifdef IMAXBEL tp.c_iflag |= IMAXBEL; #endif /* IMAXBEL */ /* oflags */ tp.c_oflag = 0L; /* lflags */ tp.c_lflag = 0L; #ifdef NOKERNINFO tp.c_lflag |= NOKERNINFO; #endif /* NOKERNINFO */ /* cflags */ tp.c_cflag = 0L; tp.c_cflag |= CS8|CREAD; for (i = 0; i < NCCS; i++) { /* No special characters */ tp.c_cc[i] = 0; } #ifdef VMIN tp.c_cc[VMIN] = 1; /* But always wait for input */ #endif /* VMIN */ debug(F101,"pty_make_raw 2 c_cc[] NCCS","",NCCS); debug(F101,"pty_make_raw 2 iflags","",tp.c_iflag); debug(F101,"pty_make_raw 2 oflags","",tp.c_oflag); debug(F101,"pty_make_raw 2 lflags","",tp.c_lflag); debug(F101,"pty_make_raw 2 cflags","",tp.c_cflag); #else /* COMMENT */ /* In this version we set or unset every single flag explicitly. It works a bit better than the simple version just above, but it's still far from adequate. */ /* iflags */ tp.c_iflag &= ~(PARMRK|ISTRIP|BRKINT|INLCR|IGNCR|ICRNL); tp.c_iflag &= ~(INPCK|IGNPAR|IXANY|IXON|IXOFF); tp.c_iflag |= IGNBRK; #ifdef IMAXBEL #ifdef COMMENT tp.c_iflag |= IMAXBEL; #else tp.c_iflag &= ~IMAXBEL; #endif /* COMMENT */ #endif /* IMAXBEL */ #ifdef IUCLC tp.c_iflag &= ~IUCLC; #endif /* IUCLC */ /* oflags */ #ifdef BSDLY tp.c_oflag &= ~BSDLY; #endif /* BSDLY */ #ifdef CRDLY tp.c_oflag &= ~CRDLY; #endif /* CRDLY */ #ifdef FFDLY tp.c_oflag &= ~FFDLY; #endif /* FFDLY */ #ifdef NLDLY tp.c_oflag &= ~NLDLY; #endif /* NLDLY */ #ifdef TABDLY tp.c_oflag &= ~TABDLY; #endif /* TABDLY */ #ifdef VTDLY tp.c_oflag &= ~VTDLY; #endif /* VTDLY */ #ifdef OFDEL tp.c_oflag &= ~OFDEL; #endif /* OFDEL */ #ifdef OFILL tp.c_oflag &= ~OFILL; #endif /* OFILL */ #ifdef OLCUC tp.c_oflag &= ~OLCUC; #endif /* OLCUC */ #ifdef CMSPAR tp.c_oflag &= ~CMSPAR; #endif /* CMSPAR */ tp.c_oflag &= ~OPOST; #ifdef OXTABS tp.c_oflag &= ~OXTABS; #endif /* OXTABS */ #ifdef COMMENT #ifdef ONOCR tp.c_oflag &= ~ONOCR; /* Maybe should be |=? */ tp.c_oflag |= ONOCR; /* makes no difference either way */ #endif /* ONOCR */ #endif /* COMMENT */ #ifdef ONOEOT tp.c_oflag &= ~ONOEOT; #endif /* ONOEOT */ #ifdef ONLRET tp.c_oflag &= ~ONLRET; #endif /* ONLRET */ #ifdef ONLCR tp.c_oflag &= ~ONLCR; #endif /* ONLCR */ #ifdef OCRNL tp.c_oflag &= ~OCRNL; #endif /* OCRNL */ /* lflags */ tp.c_lflag &= ~ECHO; #ifdef ECHOE tp.c_lflag &= ~ECHOE; #endif /* ECHOE */ #ifdef ECHONL tp.c_lflag &= ~ECHONL; #endif /* ECHONL */ #ifdef ECHOPRT tp.c_lflag &= ~ECHOPRT; #endif /* ECHOPRT */ #ifdef ECHOKE tp.c_lflag &= ~ECHOKE; #endif /* ECHOKE */ #ifdef ECHOCTL tp.c_lflag &= ~ECHOCTL; #endif /* ECHOCTL */ #ifdef XCASE tp.c_lflag &= ~XCASE; #endif /* XCASE */ #ifdef ALTWERASE tp.c_lflag &= ~ALTWERASE; #endif /* ALTWERASE */ #ifdef EXTPROC tp.c_lflag &= ~(ICANON|ISIG|IEXTEN|TOSTOP|FLUSHO|PENDIN|EXTPROC); #else tp.c_lflag &= ~(ICANON|ISIG|IEXTEN|TOSTOP|FLUSHO|PENDIN); #endif /* EXTPROC */ #ifdef NOKERNINFO tp.c_lflag |= NOKERNINFO; #endif /* NOKERNINFO */ #ifndef COMMENT tp.c_lflag &= ~NOFLSH; /* TRY IT THE OTHER WAY? */ #else tp.c_lflag |= NOFLSH; /* No, this way is worse */ #endif /* COMMENT */ /* cflags */ tp.c_cflag &= ~(CSIZE|PARENB|PARODD); tp.c_cflag |= CS8|CREAD; #ifdef MDMBUF tp.c_cflag &= ~(MDMBUF); #else #ifdef CCAR_OFLOW tp.c_cflag &= ~(CCAR_OFLOW); /* two names for the same thing */ #endif /* CCAR_OFLOW */ #endif /* MDMBUF */ #ifdef CCTS_OFLOW tp.c_cflag &= ~(CCTS_OFLOW); #endif /* CCTS_OFLOW */ #ifdef CDSR_OFLOW tp.c_cflag &= ~(CDSR_OFLOW); #endif /* CDSR_OFLOW */ #ifdef CDTR_IFLOW tp.c_cflag &= ~(CDTR_IFLOW); #endif /* CDTR_IFLOW */ #ifdef CRTS_IFLOW tp.c_cflag &= ~(CRTS_IFLOW); #endif /* CRTS_IFLOW */ #ifdef CRTSXOFF tp.c_cflag &= ~(CRTSXOFF); #endif /* CRTSXOFF */ #ifdef CRTSCTS tp.c_cflag &= ~(CRTSCTS); #endif /* CRTSCTS */ #ifdef CLOCAL tp.c_cflag &= ~(CLOCAL); #endif /* CLOCAL */ #ifdef CSTOPB tp.c_cflag &= ~(CSTOPB); #endif /* CSTOPB */ #ifdef HUPCL tp.c_cflag &= ~(HUPCL); #endif /* HUPCL */ for (i = 0; i < NCCS; i++) { /* No special characters */ tp.c_cc[i] = 0; } #ifdef VMIN tp.c_cc[VMIN] = 1; /* But always wait for input */ #endif /* VMIN */ debug(F101,"pty_make_raw 3 c_cc[] NCCS","",NCCS); debug(F101,"pty_make_raw 3 iflags","",tp.c_iflag); debug(F101,"pty_make_raw 3 oflags","",tp.c_oflag); debug(F101,"pty_make_raw 3 lflags","",tp.c_lflag); debug(F101,"pty_make_raw 3 cflags","",tp.c_cflag); #endif /* COMMENT */ #endif /* COMMENT */ errno = 0; #ifdef BSD44ORPOSIX /* POSIX */ x = tcsetattr(fd,TCSANOW,&tp); debug(F101,"pty_make_raw tcsetattr","",x); #else #ifdef ATTSV /* AT&T UNIX */ x = ioctl(fd,TCSETA,&tp); debug(F101,"pty_make_raw ioctl","",x); #else x = stty(fd,&tp); /* Traditional */ debug(F101,"pty_make_raw stty","",x); #endif /* ATTSV */ #endif /* BSD44ORPOSIX */ debug(F101,"pty_make_raw errno","",errno); #endif /* __NetBSD__ */ } static int pty_chk(fd) int fd; { int x, n = 0; errno = 0; #ifdef FIONREAD x = ioctl(fd, FIONREAD, &n); /* BSD and most others */ ckmakmsg(msgbuf,500, "pty_chk ioctl FIONREAD errno=", ckitoa(errno), " count=", ckitoa(n)); debug(F100,msgbuf,"",0); #else #ifdef RDCHK n = rdchk(fd); debug(F101,"pty_chk rdchk","",n); #else n = 1; #endif /* RDCHK */ #endif /* FIONREAD */ return((n > -1) ? n : 0); } static int pty_get_status(fd,pid) int fd; PID_T pid; { int x, status = -1; PID_T w; debug(F101,"pty_get_status fd","",fd); debug(F101,"pty_get_status pid","",pid); if (pexitstat > -1) return(pexitstat); #ifdef COMMENT /* Not only unnecessary but harmful */ errno = 0; x = kill(pty_fork_pid,0); debug(F101,"pty_get_status kill value","",x); debug(F101,"pty_get_status kill errno","",errno); if (x > -1 && errno != ESRCH) return(-1); /* Fork still there */ /* Fork seems to be gone */ #endif /* COMMENT */ errno = 0; x = waitpid(pty_fork_pid,&status,WNOHANG); debug(F111,"pty_get_status waitpid",ckitoa(errno),x); if (x <= 0 && errno == 0) { debug(F101,"pty_get_status waitpid return","",-1); return(-1); } if (x > 0) { if (x != pty_fork_pid) debug(F101, "pty_get_status waitpid pid doesn't match","",pty_fork_pid); debug(F101,"pty_get_status waitpid status","",status); debug(F101,"pty_get_status waitpid errno","",errno); if (WIFEXITED(status)) { debug(F100,"pty_get_status WIFEXITED","",0); status = WEXITSTATUS(status); debug(F101,"pty_get_status fork exit status","",status); #ifdef COMMENT end_pty(); #endif /* COMMENT */ close(fd); pexitstat = status; } else { debug(F100,"pty_get_status waitpid unexpected status","",0); } } debug(F101,"pty_get_status return status","",status); return(status); } /* t t p t y c m d -- Run command on pty and forward to net */ /* Needed for running external protocols on secure connections. For example, if C-Kermit has made an SSL/TLS or Kerberos Telnet connection, and then needs to transfer a file with Zmodem, which is an external program, this routine reads Zmodem's output, encrypts it, and then forwards it out the connection, and reads the encrypted data stream coming in from the connection, decrypts it, and forwards it to Zmodem. Works like a TCP/IP port forwarder except one end is a pty rather than a socket, which introduces some complications: . On most platforms, select() always indicates the output side of the pty has characters waiting to be read, even when it doesn't, even when the pty process has already exited. . Nonblocking reads must be used on the pty, because there is no way on certain platforms (e.g. NetBSD) to find out how many characters are available to be read (the FIONREAD ioctl always says 0). The code also allows for blocking reads (if O_NDELAY and O_NONBLOCK are not defined, or if PTY_NO_NDELAY is defined), but on some platforms this can result in single-byte reads and writes (NetBSD again). . Testing for "EOF" on the pty is problematic. select() never gives any indication. After the pty process has exited and the fork has disappeared, read() can still return with 0 bytes read but without an error (NetBSD); no known test on the pty file descriptor will indicate that it is no longer valid. The process ID of the pty fork can be tested on some platforms (NetBSD, luckily) but not others (Solaris, Linux). On the network side, we use ttinc() and ttoc(), which, for network connections, handle any active security methods. Call with s = command. Returns 0 on failure, 1 on success. fdc - December 2006 - August 2007. NOTE: This code defaults to nonblocking reads if O_NDELAY or O_NONBLOCK are defined in the header files, which should be true of every recent Unix platform. If this causes trouble somewhere, define PTY_NO_NDELAY, e.g. when building C-Kermit: touch ckutio.c make platformname KFLAGS=-DPTY_NO_NODELAY */ static int have_pty = 0; /* Do we have a pty? */ static SIGTYP (*save_sigchld)() = NULL; /* For catching SIGCHLD */ static VOID sigchld_handler(sig) int sig; { have_pty = 0; /* We don't have a pty */ #ifdef DEBUG if (save_sigchld) { (VOID) signal(SIGCHLD,save_sigchld); save_sigchld = NULL; } if (deblog) { debug(F100,"**************","",0); debug(F100,"SIGCHLD caught","",0); debug(F100,"**************","",0); } #endif /* DEBUG */ } #define HAVE_IAC 1 #define HAVE_CR 2 int ttptycmd(s) char *s; { CHAR tbuf[PTY_TBUF_SIZE]; /* Read from net, write to pty */ int tbuf_avail = 0; /* Pointers for tbuf */ int tbuf_written = 0; static int in_state = 0; /* For TELNET IAC and NVT in */ static int out_prev = 0; /* Simpler scheme for out */ CHAR pbuf[PTY_PBUF_SIZE]; /* Read from pty, write to net */ CHAR dbuf[PTY_PBUF_SIZE + PTY_PBUF_SIZE + 1]; /* Double-size buffer */ int pbuf_avail = 0; /* Pointers for pbuf */ int pbuf_written = 0; int ptyfd = -1; /* Pty file descriptor */ int have_net = 0; /* We have a network connection */ int pty_err = 0; /* Got error on pty */ int net_err = 0; /* Got error on net */ int status = -1; /* Pty process exit status */ int rc = 0; /* Our return code */ int x1 = 0, x2 = 0; /* Workers... */ int c, n, m, t, x; /* Workers */ long seconds_to_wait = 0L; /* select() timeout */ struct timeval tv, *tv2; /* For select() */ #ifdef INTSELECT int in, out, err; /* For select() */ #else fd_set in, out, err; #endif /* INTSELECT */ int nfds = 0; /* For select() */ int pset = 0, tset = 0, pnotset = 0, tnotset = 0; /* stats/debuggin only */ int read_net_bytes = 0; /* Stats */ int write_net_bytes = 0; /* Stats */ int read_pty_bytes = 0; /* Stats */ int write_pty_bytes = 0; /* Stats */ int is_tn = 0; /* TELNET protocol is active */ int masterfd = -1; int slavefd = -1; #ifndef USE_CKUPTY_C struct termios term; struct winsize twin; struct stringarray * q; char ** args = NULL; #endif /* USE_CKUPTY_C */ in_state = 0; /* No previous character yet */ if (ttyfd == -1) { printf("?Sorry, communication channel is not open\n"); return(0); } else { have_net = 1; } if (nopush) { debug(F100,"ttptycmd fail: nopush","",0); return(0); } if (!s) s = ""; /* Defense de bogus arguments */ if (!*s) return(0); pexitstat = -1; /* Fork process exit status */ #ifdef TNCODE is_tn = (xlocal && netconn && IS_TELNET()) || /* Telnet protocol active */ (!xlocal && sstelnet); #endif /* TNCODE */ debug(F110,"ttptycmd command",s,0); debug(F101,"ttptycmd ttyfd","",ttyfd); debug(F101,"ttptycmd is_tn","",is_tn); debug(F101,"ttptycmd ckermit pid","",getpid()); #ifdef USE_CKUPTY_C /* Call ckupty.c module to get and set up the pty fork */ /* fc 1 == "run an external protocol" */ debug(F100,"ttptycmd using ckupty.c","",0); if (do_pty(&ptyfd,s,1) < 0) { /* Start the command on a pty */ debug(F100,"ttptycmd do_pty fails","",0); return(0); } masterfd = ptyfd; pty_master_fd = ptyfd; #ifdef COMMENT slavefd = pty_slave_fd; /* This is not visible to us */ #endif /* COMMENT */ debug(F111,"ttptycmd ptyfd","USE_CKUPTY_C",ptyfd); debug(F111,"ttptycmd masterfd","USE_CKUPTY_C",masterfd); debug(F111,"ttptycmd fork pid","USE_CKUPTY_C",pty_fork_pid); #ifndef SOLARIS /* "ioctl inappropriate on device" for pty master */ pty_make_raw(masterfd); #endif /* SOLARIS */ #else /* USE_CKUPTY_C */ debug(F100,"ttptycmd OPENPTY","",0); if (tcgetattr(0, &term) == -1) { /* Get controlling terminal's modes */ perror("tcgetattr"); return(0); } if (ioctl(0, TIOCGWINSZ, (char *) &twin) == -1) { /* and window size */ perror("ioctl TIOCGWINSZ"); return(0); } if (openpty(&masterfd, &slavefd, NULL, NULL, NULL) == -1) { debug(F101,"ttptycmd openpty failed errno","",errno); perror("opentpy"); return(0); } debug(F101,"ttptycmd openpty masterfd","",masterfd); debug(F101,"ttptycmd openpty slavefd","",slavefd); pty_master_fd = masterfd; pty_slave_fd = slavefd; debug(F101,"ttptycmd openpty pty_master_fd","",pty_master_fd); /* Put pty master in raw mode but let forked app control the slave */ pty_make_raw(masterfd); #ifdef COMMENT #ifdef TIOCREMOTE /* TIOCREMOTE,0 = disable all termio processing */ x = ioctl(masterfd, TIOCREMOTE, 1); debug(F111,"ttptycmd ioctl TIOCREMOTE",ckitoa(x),errno); #endif /* TIOCREMOTE */ #ifdef TIOCTTY /* TIOCTTY,0 = disable all termio processing */ x = ioctl(masterfd, TIOCTTY, 0); debug(F111,"ttptycmd ioctl TIOCTTY",ckitoa(x),errno); #endif /* TIOCTTY */ #endif /* COMMENT */ have_pty = 1; /* We have an open pty */ save_sigchld = signal(SIGCHLD, sigchld_handler); /* Catch fork quit */ pty_fork_pid = fork(); /* Make fork for external protocol */ debug(F101,"ttptycmd pty_fork_pid","",pty_fork_pid); if (pty_fork_pid == -1) { perror("fork"); return(0); } else if (pty_fork_pid == 0) { /* In new fork */ int x; debug(F101,"ttptycmd new fork pid","",getpid()); close(masterfd); /* Slave quarters no masters allowed */ x = setsid(); debug(F101,"ttptycmd new fork setsid","",x); if (x == -1) { perror("ttptycmd setsid"); exit(1); } signal(SIGINT,SIG_IGN); /* Let upper fork catch this */ #ifdef COMMENT #ifdef TIOCSCTTY /* Make pty the controlling terminal for the process */ /* THIS CAUSES AN INFINITE SIGWINCH INTERRUPT LOOP */ x = ioctl(slavefd, TIOCSCTTY, NULL); debug(F101,"ttptycmd TIOCSCTTY","",x); #endif /* TIOCSCTTY */ #endif /* COMMENT */ /* Initialize slave pty modes and size to those of our terminal */ if (tcsetattr(slavefd, TCSANOW, &term) == -1) { perror("ttptycmd tcsetattr"); exit(1); } if (ioctl(slavefd, TIOCSWINSZ, &twin) == -1) { perror("ttptycmd ioctl"); exit(1); } #ifdef COMMENT #ifdef TIOCNOTTY /* Disassociate this process from its terminal */ /* THIS HAS NO EFFECT */ x = ioctl(slavefd, TIOCNOTTY, NULL); debug(F101,"ttptycmd TIOCNOTTY","",x); #endif /* TIOCNOTTY */ #endif /* COMMENT */ #ifdef COMMENT #ifdef SIGTTOU /* Ignore terminal output interrupts */ /* THIS HAS NO EFFECT */ debug(F100,"ttptycmd ignoring SIGTTOU","",0); signal(SIGTTOU, SIG_IGN); #endif /* SIGTTOU */ #ifdef SIGTSTP /* Ignore terminal output interrupts */ /* THIS HAS NO EFFECT */ debug(F100,"ttptycmd ignoring SIGTSTP","",0); signal(SIGTSTP, SIG_IGN); #endif /* SIGTSTP */ #endif /* COMMENT */ pty_make_raw(slavefd); /* Put it in rawmode */ errno = 0; if (dup2(slavefd, STDIN_FILENO) != STDIN_FILENO || dup2(slavefd, STDOUT_FILENO) != STDOUT_FILENO) { debug(F101,"ttptycmd new fork dup2 error","",errno); perror("ttptycmd dup2"); exit(1); } debug(F100,"ttptycmd new fork dup2 ok","",0); /* Parse external protocol command line */ q = cksplit(1,0,s,NULL,"\\%[]&$+-/=*^_@!{}/<>|.#~'`:;?",7,0,0); if (!q) { debug(F100,"ttptycmd cksplit failed","",0); exit(1); } else { int i, n; debug(F100,"ttptycmd cksplit ok","",0); n = q->a_size; args = q->a_head + 1; for (i = 0; i <= n; i++) { if (!args[i]) { break; } else { /* sometimes cksplit() doesn't terminate the list */ if ((i == n) && args[i]) { if ((int)strlen(args[i]) == 0) makestr(&(args[i]),NULL); } } } } #ifdef COMMENT /* Putting the slave pty in rawmode should not be necessary because the external protocol program is supposed to do that itself. Yet doing this here cuts down on Zmodem binary-file transmission errors by 30-50% but still doesn't eliminate them. */ pty_make_raw(STDIN_FILENO); pty_make_raw(STDOUT_FILENO); #endif /* COMMENT */ debug(F100,"ttptycmd execvp'ing external protocol","",0); execvp(args[0],args); perror("execvp failed"); debug(F101,"ttptycmd execvp failed","",errno); close(slavefd); exit(1); } /* (there are better ways to do this...) */ msleep(1000); /* Make parent wait for child to be ready */ ptyfd = masterfd; /* We talk to the master */ #endif /* USE_CKUPTY_C */ debug(F101,"ttptycmd ptyfd","",ptyfd); if (ptyfd < 0) { printf("?Failure to get pty\n"); return(-9); } have_pty = 1; /* We have an open pty or we wouldn't he here */ debug(F101,"ttptycmd PTY_PBUF_SIZE","",PTY_PBUF_SIZE); debug(F101,"ttptycmd PTY_TBUF_SIZE","",PTY_TBUF_SIZE); #ifdef PTY_USE_NDELAY /* NOTE: If select() and ioctl(ptyfd,FIONREAD,&n) return true indications on the pty, we don't need nonblocking reads. Performance of either method seems to be about the same, so use whatever works. */ errno = 0; x = fcntl(ptyfd,F_SETFL,fcntl(ptyfd,F_GETFL, 0)|O_NDELAY); ckmakmsg(msgbuf,500, "ttptycmd set O_NDELAY errno=", ckitoa(errno), " fcntl=", ckitoa(x)); debug(F100,msgbuf,"",0); #endif /* PTY_USE_NDELAY */ #ifdef COMMENT /* Not necessary, the protocol module already did this */ #ifdef USE_CFMAKERAW if (tcgetattr(ttyfd, &term) > -1) { cfmakeraw(&term); debug(F101,"ttptycmd net cfmakeraw errno","",errno); x tcsetattr(ttyfd, TCSANOW, &term); debug(F101,"ttptycmd net tcsetattr","",x); debug(F101,"ttptycmd net tcsetattr","",errno); } #else if (local) /* Put network connection in */ ttpkt(ttspeed,ttflow,ttprty); /* "packet mode". */ else conbin((char)escchr); /* OR... pty_make_raw(0) */ #endif /* USE_CFMAKERAW */ #endif /* COMMENT */ #ifdef TNCODE if (is_tn) { debug(F101,"<<< ttptycmd TELOPT_ME_BINARY","",TELOPT_ME(TELOPT_BINARY)); debug(F101,"<<< ttptycmd TELOPT_U_BINARY","",TELOPT_U(TELOPT_BINARY)); } #endif /* TNCODE */ debug(F101,"ttptycmd entering loop - seconds_to_wait","",seconds_to_wait); while (have_pty || have_net) { FD_ZERO(&in); /* Initialize select() structs */ FD_ZERO(&out); FD_ZERO(&err); /* (not used because useless) */ nfds = -1; debug(F101,"ttptycmd loop top have_pty","",have_pty); debug(F101,"ttptycmd loop top have_net","",have_net); /* Pty is open and we have room to read from it? */ if (have_pty && pbuf_avail < PTY_PBUF_SIZE) { debug(F100,"ttptycmd FD_SET ptyfd in","",0); FD_SET(ptyfd, &in); nfds = ptyfd; } /* Network is open and we have room to read from it? */ if (have_net && have_pty && tbuf_avail < PTY_TBUF_SIZE) { debug(F100,"ttptycmd FD_SET ttyfd in","",0); FD_SET(ttyfd, &in); if (ttyfd > nfds) nfds = ttyfd; } /* Pty is open and we have stuff to write to it? */ if (have_pty && tbuf_avail - tbuf_written > 0) { debug(F100,"ttptycmd FD_SET ptyfd out","",0); FD_SET (ptyfd, &out); if (ptyfd > nfds) nfds = ptyfd; } /* Net is open and we have stuff to write to it? */ debug(F101,"ttptycmd pbuf_avail-pbuf_written","", pbuf_avail - pbuf_written); if (have_net && pbuf_avail - pbuf_written > 0) { debug(F100,"ttptycmd FD_SET ttyfd out","",0); FD_SET (ttyfd, &out); if (ttyfd > nfds) nfds = ttyfd; } /* We don't use err because it's not really for errors, */ /* but for out of band data on the TCP socket, which, if it is */ /* to be handled at all, is handled in the tt*() routines */ nfds++; /* 0-based to 1-based */ debug(F101,"ttptycmd nfds","",nfds); if (!nfds) { debug(F100,"ttptycmd NO FDs set for select","",0); if (have_pty) { /* This is not right -- sleeping won't accomplish anything */ debug(F101,"ttptycmd msleep","",100); msleep(100); } else { debug(F100,"ttptycmd no pty - quitting loop","",0); break; } } errno = 0; if (seconds_to_wait > 0L) { /* Timeout in case nothing happens */ tv.tv_sec = seconds_to_wait; /* for a long time */ tv.tv_usec = 0L; tv2 = &tv; } else { tv2 = NULL; } x = select(nfds, &in, &out, NULL, tv2); debug(F101,"ttptycmd select","",x); if (x < 0) { if (errno == EINTR) continue; debug(F101,"ttptycmd select error","",errno); break; } if (x == 0) { debug(F101,"ttptycmd +++ select timeout","",seconds_to_wait); if (have_pty) { status = pty_get_status(ptyfd,pty_fork_pid); debug(F101,"ttptycmd pty_get_status A","",status); if (status > -1) pexitstat = status; have_pty = 0; } break; } /* We want to handle any pending writes first to make room */ /* for new incoming. */ if (FD_ISSET(ttyfd, &out)) { /* Can write to net? */ CHAR * s; s = pbuf + pbuf_written; /* Current spot for sending */ #ifdef TNCODE if (is_tn) { /* ttol() doesn't double IACs */ CHAR c; /* Rewrite string with IACs doubled */ int i; s = pbuf + pbuf_written; /* Source */ x = 0; /* Count */ for (i = 0; i < pbuf_avail - pbuf_written; i++) { c = s[i]; /* Next character */ if (c == IAC) { /* If it's IAC */ dbuf[x++] = c; /* put another one */ debug(F000,">>> QUOTED IAC","",c); } else if (c != 0x0a && out_prev == 0x0d) { /* Bare CR */ if (!TELOPT_ME(TELOPT_BINARY)) { /* NVT rule */ c = 0x00; dbuf[x++] = c; debug(F000,">>> CR-NUL","",c); } } dbuf[x++] = c; /* Copy and count it */ debug(F000,">>> char",ckitoa(in_state),c); out_prev = c; } s = dbuf; /* New source */ } else #endif /* TNCODE */ x = pbuf_avail - pbuf_written; /* How much to send */ debug(F101,"ttptycmd bytes to send","",x); x = ttol(s, x); debug(F101,">>> ttol","",x); if (x < 0) { net_err++; debug(F111,"ttptycmd ttol error",ckitoa(x),errno); x = 0; } write_net_bytes += x; pbuf_written += x; } if (FD_ISSET(ptyfd, &out)) { /* Can write to pty? */ debug(F100,"ttptycmd FD_ISSET ptyfd out","",0); errno = 0; #ifndef COMMENT x = write(ptyfd,tbuf + tbuf_written,tbuf_avail - tbuf_written); #else /* Byte loop to rule out data overruns in the pty */ /* (it makes no difference) */ { char *p = tbuf+tbuf_written; int n = tbuf_avail - tbuf_written; for (x = 0; x < n; x++) { msleep(10); if (write(ptyfd,&(p[x]),1) < 0) break; } } #endif /* COMMENT */ debug(F111,"ttptycmd ptyfd write",ckitoa(errno),x); if (x > 0) { tbuf_written += x; write_pty_bytes += x; } else { x = 0; pty_err++; if (pexitstat < 0) { status = pty_get_status(ptyfd,pty_fork_pid); debug(F101,"ttptycmd pty_get_status B","",status); if (status > -1) pexitstat = status; have_pty = 0; } debug(F100,"ttptycmd +++ ptyfd write error","",0); } } if (FD_ISSET(ttyfd, &in)) { /* Can read from net? */ tset++; debug(F100,"ttptycmd FD_ISSET ttyfd in","",0); n = in_chk(1,ttyfd); debug(F101,"ttptycmd in_chk(ttyfd)","",n); if (n < 0 || ttyfd == -1) { debug(F101,"ttptycmd +++ ttyfd errno","",errno); net_err++; } else if (n > 0) { if (n > PTY_TBUF_SIZE - tbuf_avail) n = PTY_TBUF_SIZE - tbuf_avail; debug(F101,"ttptycmd net read size adjusted","",n); if (xlocal && netconn) { /* We have to use a byte loop here because ttxin() does not decrypt or, for that matter, handle Telnet. */ int c; CHAR * p; p = tbuf + tbuf_avail; for (x = 0; x < n; x++) { if ((c = ttinc(0)) < 0) break; if (!is_tn) { /* Not Telnet - keep all bytes */ *p++ = (CHAR)c; debug(F000,"<<< char","",c); #ifdef TNCODE } else { /* Telnet - must handle IAC and NVT */ debug(F000,"<<< char",ckitoa(in_state),c); switch (c) { case 0x00: /* NUL */ if (in_state == HAVE_CR) { debug(F000,"<<< SKIP","",c); } else { *p++ = c; debug(F000,"<<< Keep","",c); } in_state = 0; break; case 0x0d: /* CR */ if (!TELOPT_U(TELOPT_BINARY)) in_state = HAVE_CR; *p++ = c; debug(F000,"<<< Keep","",c); break; #ifdef COMMENT case 0x0f: /* Ctrl-O */ case 0x16: /* Ctrl-V */ *p++ = 0x16; *p++ = c; debug(F000,"<<< QUOT","",c); break; #endif /* COMMENT */ case 0xff: /* IAC */ if (in_state == HAVE_IAC) { debug(F000,"<<< KEEP","",c); *p++ = c; in_state = 0; } else { debug(F000,"<<< SKIP","",c); in_state = HAVE_IAC; } break; default: /* All others */ if (in_state == HAVE_IAC) { #ifdef COMMENT /* tn_doop() will consume an unknown number of bytes and we'll overshoot the for-loop. The only Telnet command I've ever seen arrive here is a Data Mark, which comes when the remote protocol exits and the remote job returns to its shell prompt. On the assumption it's a 1-byte command, we don't write out the IAC or the command, and we clear the state. If we called tn_doop() we'd have no way of knowing how many bytes it took from the input stream. */ int xx; xx = tn_doop((CHAR)c,duplex,ttinc); debug(F111,"<<< DOOP",ckctoa(c),xx); #else debug(F101,"<<< DOOP","",c); #endif /* COMMENT */ in_state = 0; } else { *p++ = c; debug(F000,"<<< keep","",c); in_state = 0; } } #endif /* TNCODE */ } } ckmakmsg(msgbuf,500, "ttptycmd read net [ttinc loop] errno=", ckitoa(errno), " count=", ckitoa(x)); debug(F100,msgbuf,"",0); } else { x = ttxin(n,tbuf+tbuf_avail); debug(F101,"ttptycmd ttxin x","",x); } if (x < 0) { debug(F101,"ttptycmd read net error","",x); net_err++; } tbuf_avail += x; read_net_bytes += x; } } else tnotset++; if (FD_ISSET(ptyfd, &in)) { /* Read from pty? */ pset++; debug(F100,"ttptycmd FD_ISSET ptyfd in","",0); #ifdef PTY_USE_NDELAY n = PTY_PBUF_SIZE; #else /* This does not work on nonblocking channels on certain platforms such as NetBSD. */ n = pty_chk(ptyfd); #endif /* PTY_USE_NDELAY */ debug(F101,"ttptycmd pty_chk() n","",n); if (n < 0) n = 0; if (n > 0) { if (n > PTY_PBUF_SIZE - pbuf_avail) n = PTY_PBUF_SIZE - pbuf_avail; debug(F101,"ttptycmd pty read size adjusted","",n); errno = 0; x = read(ptyfd,pbuf+pbuf_avail,n); #ifdef DEBUG if (deblog) { ckmakmsg(msgbuf,500, "ttptycmd read pty errno=", ckitoa(errno), " count=", ckitoa(x)); debug(F100,msgbuf,"",0); } #endif /* DEBUG */ if (x < 0 && errno == EAGAIN) x = 0; if (x < 0) { /* This works on Solaris and Linux */ pty_err++; /* but not NetBSD */ debug(F100,"TERMINATION TEST A","",0); #ifdef COMMENT if (errno == EIO) rc = 1; #endif /* COMMENT */ if (pexitstat < 0) { status = pty_get_status(ptyfd,pty_fork_pid); debug(F101,"ttptycmd pty_get_status C","",status); if (status > -1) pexitstat = status; } have_pty = 0; x = 0; } if (x == 0 && !pty_err) { /* This works on NetBSD but */ debug(F100,"TERMINATION TEST B","",0); status = pexitstat > -1 ? pexitstat : pty_get_status(ptyfd,pty_fork_pid); debug(F101,"ttptycmd pty_get_status D","",status); if (status > -1) { pexitstat = status; pty_err++; have_pty = 0; } else { /* Select() lied */ pty_err = 0; /* pty still there but has nothing */ msleep(100); /* sleep a bit */ } x = 0; } /* Hopefully the next two are no longer needed... */ if (!pty_err && ( #ifndef PTY_USE_NDELAY x < 1 || errno #else errno != 0 && errno != EAGAIN #endif /* PTY_USE_NDELAY */ )) { debug(F100,"TERMINATION TEST C","",0); pty_err++; debug(F101,"ttptycmd SET pty_err","",pty_err); if (errno == EIO) /* errno == EIO is like EOF */ rc = 1; if (x < 0) x = 0; } #ifdef COMMENT #ifdef DEBUG if (deblog) { pbuf[pbuf_avail + x] = '\0'; debug(F111,"ttptycmd added to pty buffer", pbuf+pbuf_avail,x); } #endif /* DEBUG */ #endif /* COMMENT */ pbuf_avail += x; read_pty_bytes += x; } else { /* n == 0 with blocking reads */ debug(F100, "PTY READ RETURNED ZERO BYTES - SHOULD NOT HAPPEN", "",0); } } else pnotset++; /* If writes have caught up to reads, reset the buffers */ if (pbuf_written == pbuf_avail) pbuf_written = pbuf_avail = 0; if (tbuf_written == tbuf_avail) tbuf_written = tbuf_avail = 0; /* See if we can exit */ x1 = pbuf_avail - pbuf_written; x2 = tbuf_avail - tbuf_written; debug(F101,"ttptycmd pty_err LOOP EXIT TEST pty_err","",pty_err); debug(F101,"ttptycmd pty_err LOOP EXIT TEST x1 [write to net]","",x1); debug(F101,"ttptycmd pty_err LOOP EXIT TEST x2 [write to pty]","",x2); debug(F101,"ttptycmd pty_err LOOP EXIT TEST rc","",rc); debug(F101,"ttptycmd pty_err LOOP EXIT TEST status","",status); debug(F101,"ttptycmd pty_err LOOP EXIT TEST pexitstat","",pexitstat); if (net_err) { /* Net error? */ debug(F101,"ttptycmd net_err LOOP EXIT TEST net_err","",net_err); if (have_net) { if (local) { ttclos(0); printf("?Connection closed\n"); } have_net = 0; } debug(F101,"ttptycmd net_err LOOP EXIT TEST x1","",x1); if (x1 == 0) break; } if (pty_err) { /* Pty error? */ if (have_pty) { if (pexitstat < 0) { status = pty_get_status(ptyfd,pty_fork_pid); debug(F101,"ttptycmd pty_get_status E","",status); if (status > -1) pexitstat = status; } have_pty = 0; } if (x1 == 0 && x2 == 0) { /* If buffers are caught up */ rc = 1; /* set preliminary return to success */ debug(F101,"ttptycmd pty_err LOOP EXIT TEST rc 2","",rc); break; /* and exit the loop */ } } } debug(F101,"ttptycmd +++ have_pty","",have_pty); if (have_pty) { /* In case select() failed */ #ifdef USE_CKUPTY_C end_pty(); close(ptyfd); #else close(slavefd); close(masterfd); #endif /* USE_CKUPTY_C */ } pty_master_fd = -1; debug(F101,"ttptycmd +++ pexitstat","",pexitstat); if (pexitstat < 0) { /* Try one last time to get status */ status = pty_get_status(ptyfd,pty_fork_pid); debug(F101,"ttptycmd pty_get_status F","",status); if (status > -1) pexitstat = status; } debug(F101,"ttptycmd +++ final pexitstat","",pexitstat); if (deblog) { /* Stats for debug log */ debug(F101,"ttptycmd +++ pset ","",pset); debug(F101,"ttptycmd +++ pnotset","",pnotset); debug(F101,"ttptycmd +++ tset ","",tset); debug(F101,"ttptycmd +++ tnotset","",tnotset); debug(F101,"ttptycmd +++ read_pty_bytes","",read_pty_bytes); debug(F101,"ttptycmd +++ write_net_bytes","",write_net_bytes); debug(F101,"ttptycmd +++ read_net_bytes","",read_net_bytes); debug(F101,"ttptycmd +++ write_pty_bytes","",write_pty_bytes); } /* If we got the external protocol's exit status from waitpid(), we use that to set our return code. If not, we fall back on whatever rc was previously set to, namely 1 (success) if the pty fork seemed to terminate, 0 otherwise. */ if (save_sigchld) { /* Restore this if we changed it */ (VOID) signal(SIGCHLD,save_sigchld); save_sigchld = NULL; } msleep(500); x = kill(pty_fork_pid,SIGHUP); /* In case it's still there */ pty_fork_pid = -1; debug(F101,"ttptycmd fork kill SIGHUP","",x); if (pexitstat > -1) rc = (pexitstat == 0 ? 1 : 0); debug(F101,"ttptycmd +++ rc","",rc); if (!local) { /* If in remote mode */ conres(); /* restore console to CBREAK mode */ concb((char)escchr); } return(rc); } #endif /* NETPTY */ #endif /* SELECT */ /* T T R U N C M D -- Redirect an external command over the connection. */ /* TTRUNCMD is the routine that was originally used for running external protocols. It is very simple and works fine provided (a) the connection is not encrypted, and (b) the external protocol uses standard i/o (file descriptors 0 and 1) for file transfer. */ int ttruncmd(s) char *s; { PID_T pid; /* pid of lower fork */ int wstat; /* for wait() */ int x; int statusp; if (ttyfd == -1) { printf("?Sorry, device is not open\n"); return(0); } if (nopush) { debug(F100,"ttruncmd fail: nopush","",0); return(0); } #ifdef NETPTY /*************** It might also be necessary to use the pty routine for other reasons, e.g. because the external program does not use stdio. */ #ifdef NETCONN /* If we have a network connection we use a different routine because (a) if the connection is encrypted, the mechanism used here can't deal with it; and (b) it won't handle any network protocols either, e.g. Telnet, Rlogin, K5 U-to-U, etc. However, this routine works much better (faster, more transparent) on serial connections and when C-Kermit is in remote mode (i.e. is on the far end). */ /* For testing always use this */ if (netconn) return(ttptycmd(s)); #endif /* NETCONN */ /***************/ #else /* NETPTY */ if (tt_is_secure()) { printf("?Sorry, \ external protocols over secure connections not supported in this OS.\n" ); return(0); } #endif /* NETPTY */ conres(); /* Make console normal */ pexitstat = -4; if ((pid = fork()) == 0) { /* Make a child fork */ if (priv_can()) /* Child: turn off privs. */ exit(1); dup2(ttyfd, 0); /* Give stdin/out to the line */ dup2(ttyfd, 1); x = system(s); debug(F101,"ttruncmd system",s,x); _exit(x ? BAD_EXIT : 0); } else { SIGTYP (*istat)(), (*qstat)(); if (pid == (PID_T) -1) /* fork() failed? */ return(0); istat = signal(SIGINT,SIG_IGN); /* Let the fork handle keyboard */ qstat = signal(SIGQUIT,SIG_IGN); /* interrupts itself... */ #ifdef COMMENT while (((wstat = wait(&statusp)) != pid) && (wstat != -1)) ; #else /* Not COMMENT */ while (1) { wstat = wait(&statusp); debug(F101,"ttruncmd wait","",wstat); if (wstat == pid || wstat == -1) break; } #endif /* COMMENT */ pexitstat = (statusp & 0xff) ? statusp : statusp >> 8; debug(F101,"ttruncmd wait statusp","",statusp); debug(F101,"ttruncmd wait pexitstat","",pexitstat); signal(SIGINT,istat); /* Restore interrupts */ signal(SIGQUIT,qstat); } concb((char)escchr); /* Restore console to CBREAK mode */ return(statusp == 0 ? 1 : 0); } #endif /* CK_REDIR */ struct tm * #ifdef CK_ANSIC cmdate2tm(char * date, int gmt) /* date as "yyyymmdd hh:mm:ss" */ #else cmdate2tm(date,gmt) char * date; int gmt; #endif { /* date as "yyyymmdd hh:mm:ss" */ static struct tm _tm; time_t now; if (strlen(date) != 17 || date[8] != ' ' || date[11] != ':' || date[14] != ':') return(NULL); time(&now); if (gmt) _tm = *gmtime(&now); else _tm = *localtime(&now); _tm.tm_year = (date[0]-'0')*1000 + (date[1]-'0')*100 + (date[2]-'0')*10 + (date[3]-'0')-1900; _tm.tm_mon = (date[4]-'0')*10 + (date[5]-'0')-1; _tm.tm_mday = (date[6]-'0')*10 + (date[7]-'0'); _tm.tm_hour = (date[9]-'0')*10 + (date[10]-'0'); _tm.tm_min = (date[12]-'0')*10 + (date[13]-'0'); _tm.tm_sec = (date[15]-'0')*10 + (date[16]-'0'); /* Should we set _tm.tm_isdst to -1 here? */ _tm.tm_wday = 0; _tm.tm_yday = 0; return(&_tm); } #ifdef OXOS #undef kill #endif /* OXOS */ #ifdef OXOS int priv_kill(pid, sig) int pid, sig; { int i; if (priv_on()) debug(F100,"priv_kill priv_on failed","",0); i = kill(pid, sig); if (priv_off()) debug(F100,"priv_kill priv_off failed","",0); return(i); } #endif /* OXOS */ #ifdef BEOSORBEBOX /* #ifdef BE_DR_7 */ /* alarm() function not supplied with Be OS DR7 - this one contributed by Neal P. Murphy. */ /* This should mimic the UNIX/POSIX alarm() function well enough, with the caveat that one's SIGALRM handler must call alarm_expired() to clean up vars and wait for the alarm thread to finish. */ unsigned int alarm(unsigned int seconds) { long time_left = 0; /* If an alarm is active, turn it off, saving the unused time */ if (alarm_thread != -1) { /* We'll be generous and count partial seconds as whole seconds. */ time_left = alarm_struct.time - ((system_time() - time_started) / 1000000.0); /* Kill the alarm thread */ kill_thread (alarm_thread); /* We need to clean up as though the alarm occured. */ time_started = 0; alarm_struct.thread = -1; alarm_struct.time = 0; alarm_expired(); } /* Set a new alarm clock, if requested. */ if (seconds > 0) { alarm_struct.thread = find_thread(NULL); alarm_struct.time = seconds; time_started = system_time(); alarm_thread = spawn_thread (do_alarm, "alarm_thread", B_NORMAL_PRIORITY, (void *) &alarm_struct ); resume_thread (alarm_thread); } /* Now return [unused time | 0] */ return ((unsigned int) time_left); } /* This function is the departure from UNIX/POSIX alarm handling. In the case of Be's missing alarm() function, this stuff needs to be done in the SIGALRM handler. When Be implements alarm(), this function call can be eliminated from user's SIGALRM signal handlers. */ void alarm_expired(void) { long ret_val; if (alarm_thread != -1) { wait_for_thread (alarm_thread, &ret_val); alarm_thread = -1; } } /* This is the function that snoozes the requisite number of seconds and then SIGALRMs the calling thread. Note that kill() wants a pid_t arg, whilst Be uses thread_id; currently they are both typdef'ed as long, but I'll do the cast anyway. This function is run in a separate thread. */ long do_alarm (void *alarm_struct) { snooze ((double) ((struct ALARM_STRUCT *) alarm_struct)->time * 1000000.0); kill ((pid_t)((struct ALARM_STRUCT *) alarm_struct)->thread, SIGALRM); time_started = 0; ((struct ALARM_STRUCT *) alarm_struct)->thread = -1; ((struct ALARM_STRUCT *) alarm_struct)->time = 0; } /* #endif */ /* BE_DR_7 */ #endif /* BEOSORBEBOX */ #ifdef Plan9 int p9ttyctl(char letter, int num, int param) { char cmd[20]; int len; if (ttyctlfd < 0) return -1; cmd[0] = letter; if (num) len = sprintf(cmd + 1, "%d", param) + 1; else { cmd[1] = param; len = 2; } if (write(ttyctlfd, cmd, len) == len) { cmd[len] = 0; /* fprintf(stdout, "wrote '%s'\n", cmd); */ return 0; } return -1; } int p9ttyparity(char l) { return p9ttyctl('p', 0, l); } int p9tthflow(int flow, int status) { return p9ttyctl('m', 1, status); } int p9ttsspd(int cps) { if (p9ttyctl('b', 1, cps * 10) < 0) return -1; ttylastspeed = cps * 10; return 0; } int p9openttyctl(char *ttname) { char name[100]; if (ttyctlfd >= 0) { close(ttyctlfd); ttyctlfd = -1; ttylastspeed = -1; } sprintf(name, "%sctl", ttname); ttyctlfd = open(name, 1); return ttyctlfd; } int p9concb() { if (consctlfd >= 0) { if (write(consctlfd, "rawon", 5) == 5) return 0; } return -1; } int p9conbin() { return p9concb(); } int p9conres() { if (consctlfd >= 0) { if (write(consctlfd, "rawoff", 6) == 6) return 0; } return -1; } int p9sndbrk(int msec) { if (ttyctlfd >= 0) { char cmd[20]; int i = sprintf(cmd, "k%d", msec); if (write(ttyctlfd, cmd, i) == i) return 0; } return -1; } int conwrite(char *buf, int n) { int x; static int length = 0; static int holdingcr = 0; int normal = 0; for (x = 0; x < n; x++) { char c = buf[x]; if (c == 007) { if (normal) { write(1, buf + (x - normal), normal); length += normal; normal = 0; } /* write(noisefd, "1000 300", 8); */ holdingcr = 0; } else if (c == '\r') { if (normal) { write(1, buf + (x - normal), normal); length += normal; normal = 0; } holdingcr = 1; } else if (c == '\n') { write(1, buf + (x - normal), normal + 1); normal = 0; length = 0; holdingcr = 0; } else if (c == '\b') { if (normal) { write(1, buf + (x - normal), normal); length += normal; normal = 0; } if (length) { write(1, &c, 1); length--; } holdingcr = 0; } else { if (holdingcr) { char b = '\b'; while (length-- > 0) write(1, &b, 1); length = 0; /* compiler bug */ } holdingcr = 0; normal++; } } if (normal) { write(1, buf + (x - normal), normal); length += normal; } return n; } void conprint(char *fmt, ...) { static char buf[1000]; /* not safe if on the stack */ va_list ap; int i; va_start(ap, fmt); i = vsprintf(buf, fmt, ap); conwrite(buf, i); } #endif /* Plan9 */ /* fprintf, printf, perror replacements... */ /* f p r i n t f */ #ifdef UNIX #ifdef CK_ANSIC #include #else /* CK_ANSIC */ #ifdef __GNUC__ #include #else #include #endif /* __GNUC__ */ #endif /* CK_ANSIC */ #ifdef fprintf #undef fprintf static char str1[4096]; static char str2[4096]; int #ifdef CK_ANSIC ckxfprintf(FILE * file, const char * format, ...) #else /* CK_ANSIC */ ckxfprintf(va_alist) va_dcl #endif /* CK_ANSIC */ /* ckxfprintf */ { int i, j, len, got_cr; va_list args; int rc = 0; #ifdef CK_ANSIC va_start(args, format); #else /* CK_ANSIC */ char * format; FILE * file; va_start(args); file = va_arg(args,FILE *); format = va_arg(args,char *); #endif /* CK_ANSIC */ if (!inserver || (file != stdout && file != stderr && file != stdin)) { rc = vfprintf(file,format,args); } else { unsigned int c; rc = vsprintf(str1, format, args); len = strlen(str1); if (len >= sizeof(str1)) { debug(F101,"ckxfprintf() buffer overflow","",len); doexit(BAD_EXIT,1); } for (i = 0, j = 0, got_cr = 0; i < len && j < sizeof(str1)-2; i++, j++ ) { /* We can't use 255 as a case label because of signed chars */ c = (unsigned)(str1[i] & 0xff); #ifdef TNCODE if (c == 255) { if (got_cr && !TELOPT_ME(TELOPT_BINARY)) str2[j++] = '\0'; str2[j++] = IAC; str2[j] = IAC; got_cr = 0; } else #endif /* TNCODE */ switch (c) { case '\r': if (got_cr #ifdef TNCODE && !TELOPT_ME(TELOPT_BINARY) #endif /* TNCODE */ ) str2[j++] = '\0'; str2[j] = str1[i]; got_cr = 1; break; case '\n': if (!got_cr) str2[j++] = '\r'; str2[j] = str1[i]; got_cr = 0; break; default: if (got_cr #ifdef TNCODE && !TELOPT_ME(TELOPT_BINARY) #endif /* TNCODE */ ) str2[j++] = '\0'; str2[j] = str1[i]; got_cr = 0; } } if (got_cr #ifdef TNCODE && !TELOPT_ME(TELOPT_BINARY) #endif /* TNCODE */ ) str2[j++] = '\0'; #ifdef CK_ENCRYPTION #ifdef TNCODE if (TELOPT_ME(TELOPT_ENCRYPTION)) ck_tn_encrypt(str2,j); #endif /* TNCODE */ #endif /* CK_ENCRYPTION */ #ifdef CK_SSL if (inserver && (ssl_active_flag || tls_active_flag)) { /* Write using SSL */ char * p = str2; ssl_retry: if (ssl_active_flag) rc = SSL_write(ssl_con, p, j); else rc = SSL_write(tls_con, p, j); debug(F111,"ckxfprintf","SSL_write",rc); switch (SSL_get_error(ssl_active_flag?ssl_con:tls_con,rc)) { case SSL_ERROR_NONE: if (rc == j) break; p += rc; j -= rc; goto ssl_retry; case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_READ: case SSL_ERROR_SYSCALL: if (rc != 0) return(-1); case SSL_ERROR_WANT_X509_LOOKUP: case SSL_ERROR_SSL: case SSL_ERROR_ZERO_RETURN: default: rc = 0; } } else #endif /* CK_SSL */ fwrite(str2,sizeof(char),j,stdout); } va_end(args); return(rc); } #endif /* fprintf */ /* p r i n t f */ #ifdef printf #undef printf int #ifdef CK_ANSIC ckxprintf(const char * format, ...) #else /* CK_ANSIC */ ckxprintf(va_alist) va_dcl #endif /* CK_ANSIC */ /* ckxprintf */ { int i, j, len, got_cr; va_list args; int rc = 0; #ifdef CK_ANSIC va_start(args, format); #else /* CK_ANSIC */ char * format; va_start(args); format = va_arg(args,char *); #endif /* CK_ANSIC */ if (!inserver) { rc = vprintf(format, args); } else { unsigned int c; rc = vsprintf(str1, format, args); len = strlen(str1); if (len >= sizeof(str1)) { debug(F101,"ckxprintf() buffer overflow","",len); doexit(BAD_EXIT,1); } for (i = 0, j = 0, got_cr=0; i < len && j < sizeof(str1)-2; i++, j++ ) { c = (unsigned)(str1[i] & 0xff); #ifdef TNCODE if (c == 255) { if (got_cr && !TELOPT_ME(TELOPT_BINARY)) str2[j++] = '\0'; str2[j++] = IAC; str2[j] = IAC; got_cr = 0; } else #endif /* TNCODE */ switch (c) { case '\r': if (got_cr #ifdef TNCODE && !TELOPT_ME(TELOPT_BINARY) #endif /* TNCODE */ ) str2[j++] = '\0'; str2[j] = str1[i]; got_cr = 1; break; case '\n': if (!got_cr) str2[j++] = '\r'; str2[j] = str1[i]; got_cr = 0; break; default: if (got_cr #ifdef TNCODE && !TELOPT_ME(TELOPT_BINARY) #endif /* TNCODE */ ) str2[j++] = '\0'; str2[j] = str1[i]; got_cr = 0; break; } } if (got_cr #ifdef TNCODE && !TELOPT_ME(TELOPT_BINARY) #endif /* TNCODE */ ) str2[j++] = '\0'; #ifdef CK_ENCRYPTION #ifdef TNCODE if (TELOPT_ME(TELOPT_ENCRYPTION)) ck_tn_encrypt(str2,j); #endif /* TNCODE */ #endif /* CK_ENCRYPTION */ #ifdef CK_SSL if (inserver && (ssl_active_flag || tls_active_flag)) { char * p = str2; /* Write using SSL */ ssl_retry: if (ssl_active_flag) rc = SSL_write(ssl_con, p, j); else rc = SSL_write(tls_con, p, j); debug(F111,"ckxprintf","SSL_write",rc); switch (SSL_get_error(ssl_active_flag?ssl_con:tls_con,rc)) { case SSL_ERROR_NONE: if (rc == j) break; p += rc; j -= rc; goto ssl_retry; case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_READ: case SSL_ERROR_SYSCALL: if (rc != 0) return(-1); case SSL_ERROR_WANT_X509_LOOKUP: case SSL_ERROR_SSL: case SSL_ERROR_ZERO_RETURN: default: rc = 0; } } else #endif /* CK_SSL */ rc = fwrite(str2,sizeof(char),j,stdout); } va_end(args); return(rc); } #endif /* printf */ /* p e r r o r */ #ifdef perror #undef perror _PROTOTYP(char * ck_errstr,(VOID)); #ifdef NEXT void #else #ifdef CK_SCOV5 void #else int #endif /* CK_SCOV5 */ #endif /* NEXT */ #ifdef CK_ANSIC ckxperror(const char * str) #else /* CK_ANSIC */ ckxperror(str) char * str; #endif /* CK_ANSIC */ /* ckxperror */ { char * errstr = ck_errstr(); #ifndef NEXT #ifndef CK_SCOV5 return #endif /* CK_SCOV5 */ #endif /* NEXT */ ckxprintf("%s%s %s\n",str,*errstr?":":"",errstr); } #endif /* perror */ #endif /* UNIX */ #ifdef MINIX2 /* Minix doesn't have a gettimeofday call (but MINIX3 does). * We fake one here using time(2) */ #ifndef MINIX3 int gettimeofday(struct timeval *tp, struct timezone *tzp) { tp->tv_usec = 0L; /* Close enough for horseshoes */ if(time(&(tp->tv_sec))==-1) return(-1); return(0); } #endif /* MINIX3 */ #ifndef MINIX3 int readlink(const char *path, void *buf, size_t bufsiz) { errno = ENOSYS; return(-1); } #endif /* MINIX3 */ #endif /* MINIX2 */ ckuus2.c0000644000015300001460000204572411602614613011257 0ustar fdckermit#ifdef SSHTEST #define SSHBUILTIN #endif /* SSHTEST */ /* C K U U S 2 -- User interface strings & help text module for C-Kermit */ /* Authors: Frank da Cruz , The Kermit Project, Columbia University, New York City Jeffrey E Altman Secure Endpoints Inc., New York City Copyright (C) 1985, 2011, Trustees of Columbia University in the City of New York. All rights reserved. See the C-Kermit COPYING.TXT file or the copyright text in the ckcmai.c module for disclaimer and permissions. This module contains HELP command and other long text strings. IMPORTANT: Character string constants longer than about 250 are not portable. Longer strings should be broken up into arrays of strings and accessed with hmsga() rather than hmsg(). (This statement was true in the 1980s and probably is not a big concern in the 21st Century, but you never know; there still might exist some 16-bit platforms and C compilers that have restrictions like this. */ #include "ckcsym.h" #include "ckcdeb.h" #include "ckcnet.h" #include "ckcasc.h" #include "ckcker.h" #include "ckuusr.h" #include "ckcxla.h" #ifdef OS2 #ifdef NT #include #else /* not NT */ #define INCL_KBD #ifdef OS2MOUSE #define INCL_MOU #endif /* OS2MOUSE */ #define INCL_DOSMISC #define INCL_DOSDEVICES #include /* This pulls in a whole load of stuff */ #undef COMMENT #endif /* NT */ #include "ckocon.h" #include "ckokvb.h" #include "ckokey.h" #endif /* OS2 */ extern char * ck_cryear; /* For copyright notice */ extern xx_strp xxstring; extern char * ccntab[]; /* hlptok contains the string for which the user requested help. This is useful for distinguishing synonyms, in case different help text is needed depending on which synonym was given. */ extern char * hlptok; #ifndef NOIKSD extern int inserver; #endif /* IKSD */ #ifndef NOICP extern int cmflgs; #ifdef DCMDBUF extern char *cmdbuf, *atmbuf; #else extern char cmdbuf[], atmbuf[]; #endif /* DCMDBUF */ #endif /* NOICP */ extern char *xarg0; extern int nrmt, nprm, dfloc, local, parity, escape; extern int turn, flow; extern int binary, quiet, keep; extern int success, xaskmore; #ifdef OS2 extern int tt_rows[], tt_cols[]; #else /* OS2 */ extern int tt_rows, tt_cols; #endif /* OS2 */ extern int cmd_rows, cmd_cols; extern long speed; extern char *dftty, *versio, *ckxsys; #ifndef NOHELP extern char *helpfile; #endif /* NOHELP */ extern struct keytab prmtab[]; #ifndef NOXFER extern struct keytab remcmd[]; #endif /* NOXFER */ #ifndef NOICP /* Interactive help strings */ /* Top-level HELP text. IMPORTANT: Also see tophlpi[] for IKSD. */ static char *tophlp[] = { "Trustees of Columbia University in the City of New York.\n", #ifndef NOHELP " Type EXIT to exit.", #ifdef OS2 " Type INTRO for a brief introduction to the Kermit Command screen.", " Type LICENSE to see the Kermit 95 license.", #else " Type INTRO for a brief introduction to C-Kermit.", " Type LICENSE to see the C-Kermit license.", #endif /* OS2 */ " Type HELP followed by a command name for help about a specific command.", #ifndef NOPUSH #ifdef UNIX " Type MANUAL to access the C-Kermit manual page.", #else #ifdef VMS " Type MANUAL to access the C-Kermit help topic.", #else #ifdef OS2 " Type MANUAL to access the K95 manual.", #else " Type MANUAL to access the C-Kermit manual.", #endif /* OS2 */ #endif /* VMS */ #endif /* UNIX */ #endif /* NOPUSH */ " Type NEWS for news about new features.", " Type SUPPORT to learn how to get technical support.", " Press ? (question mark) at the prompt, or anywhere within a command,", " for a menu (context-sensitive help, menu on demand).", #else "Press ? for a list of commands; see documentation for detailed descriptions.", #endif /* NOHELP */ #ifndef NOCMDL #ifndef NOHELP " ", " Type HELP OPTIONS for help with command-line options.", #endif /* NOHELP */ #endif /* NOCMDL */ " ", #ifndef OS2 #ifdef MAC "Documentation for Command Window: \"Using C-Kermit\" by Frank da Cruz and", "Christine M. Gianone, Digital Press, 1997, ISBN: 1-55558-164-1", #else "DOCUMENTATION: \"Using C-Kermit\" by Frank da Cruz and Christine M. Gianone,", "2nd Edition, Digital Press / Butterworth-Heinemann 1997, ISBN 1-55558-164-1,", "plus supplements at http://kermit.columbia.edu/ckermit.html#doc.", #endif /* MAC */ #endif /* OS2 */ #ifdef MAC " ", "Also see the Mac Kermit Doc and Bwr files on the Mac Kermit diskette.\n", #else #ifdef HPUX10 " ", "See the files in /usr/share/lib/kermit/ for additional information.", #endif /* HPUX10 */ #endif /* MAC */ "" }; #ifndef NOIKSD static char *tophlpi[] = { /* Top-level help for IKSD */ "Trustees of Columbia University in the City of New York.\n", #ifndef NOHELP " Type INTRO for a brief introduction to Kermit commands.", " Type VERSION for version and copyright information.", " Type HELP followed by a command name for help about a specific command.", " Type SUPPORT to learn how to get technical support.", " Type LOGOUT (or EXIT) to log out.", " Press ? (question mark) at the prompt, or anywhere within a command,", " for a menu (context-sensitive help, menu on demand).", #else "Press ? for a list of commands; see documentation for detailed descriptions.", #endif /* NOHELP */ " ", "DOCUMENTATION: \"Using C-Kermit\" by Frank da Cruz and Christine M. Gianone,", "2nd Edition, Digital Press (1997), ISBN 1-55558-164-1. More info at the", "Kermit Project website, http://kermit.columbia.edu/.", "" }; #endif /* NOIKSD */ #ifndef NOHELP char *newstxt[] = { #ifdef OS2 "Welcome to Kermit 95 3.x.x. Major new features since 2.1.3 include:", #else "Welcome to C-Kermit 9.0.300. New features since 8.0.211 include:", #endif /* OS2 */ " . Open Source Simplified 3-Clause BSD License", " . Full 64-bit memory model on platforms that support it", " . Large file support (64-bit file size) on most platforms", " . Long integer variables and constants in commands and scripts", " . Bigger maximum command and macro lengths", " . Bigger filename expansion space", " . New super-flexible RENAME command", " . New COPY and DIRECTORY command options", " . New TOUCH command", " . Raw SSL/TLS connections for connecting to POP3 and similar services", " . At prompt, Ctrl-K recalls most recent filename", " . Scripting and performance improvements and bug fixes", " ", "Documentation:", " 1. http://kermit.columbia.edu/usingckermit.html", " \"Using C-Kermit\", second edition (1997), current with C-Kermit 6.0.", " ", " 2. http://kermit.columbia.edu/ckermit70.html", " which documents the new features of C-Kermit 7.0.", " ", " 3. http://kermit.columbia.edu/ckermit80.html", " which documents the new features of C-Kermit 8.0.", " ", " 4. http://kermit.columbia.edu/ckermit90.html", " which documents the new features of C-Kermit 9.0.", " ", "If the release date shown by the VERSION command is long past, be sure to", "check with the Kermit website to see if there have been updates:", " ", #ifdef OS2 " http://kermit.columbia.edu/k95.html (Kermit 95 home page)", #else " http://kermit.columbia.edu/ckermit.html (C-Kermit home page)", #endif /* OS2 */ " http://kermit.columbia.edu/ (Kermit Project home page)", "" }; #endif /* NOHELP */ #ifndef NOHELP char *introtxt[] = { #ifdef OS2 "Welcome to K-95, Kermit communications software for:", #else #ifdef UNIX #ifdef HPUX "Welcome to HP-UX C-Kermit communications software for:", #else "Welcome to UNIX C-Kermit communications software for:", #endif /* HPUX */ #else #ifdef VMS "Welcome to VMS C-Kermit communications software for:", #else #ifdef VOS "Welcome to VOS C-Kermit communications software for:", #else #ifdef MAC "Welcome to Mac Kermit communications software for:", #else "Welcome to C-Kermit communications software for:", #endif /* MAC */ #endif /* VOS */ #endif /* VMS */ #endif /* UNIX */ #endif /* OS2 */ #ifndef NOXFER " . Error-free and efficient file transfer", #endif /* NOXFER */ #ifndef NOLOCAL #ifdef OS2 " . VT320/220/102/100/52, ANSI, Wyse, Linux, Televideo, and other emulations", #else #ifdef MAC " . VT220 terminal emulation", #else " . Terminal connection", #endif /* MAC */ #endif /* OS2 */ #endif /* NOLOCAL */ #ifndef NOSPL " . Script programming", #endif /* NOSPL */ #ifndef NOICS " . International character set conversion", #endif /* NOICS */ #ifndef NODIAL #ifndef NOSPL " . Numeric and alphanumeric paging", #endif /* NOSPL */ #endif /* NODIAL */ #ifndef NOLOCAL " ", "Supporting:", " . Serial connections, direct or dialed.", #ifndef NODIAL " . Automatic modem dialing", #endif /* NODIAL */ #ifdef TCPSOCKET " . TCP/IP network connections:", #ifdef TNCODE " - Telnet sessions", #endif /* TNCODE */ #ifdef SSHBUILTIN " - SSH v1 and v2 connections", #else #ifdef ANYSSH " - SSH connections via external agent", #endif /* ANYSSH */ #endif /* SSHBUILTIN */ #ifdef RLOGCODE " - Rlogin sessions", #endif /* RLOGCODE */ #ifdef NEWFTP " - FTP sessions", #endif /* NEWFTP */ #ifdef CKHTTP " - HTTP 1.1 sessions", #endif /* CKHTTP */ #ifdef IKSD " - Internet Kermit Service", #endif /* IKSD */ #endif /* TCPSOCKET */ #ifdef ANYX25 " . X.25 network connections", #endif /* ANYX25 */ #ifdef OS2 #ifdef DECNET " . DECnet/PATHWORKS LAT Ethernet connections", #endif /* DECNET */ #ifdef SUPERLAT " . Meridian Technologies' SuperLAT connections", #endif /* SUPERLAT */ #ifdef NPIPE " . Named-pipe connections", #endif /* NPIPE */ #ifdef CK_NETBIOS " . NETBIOS connections", #endif /* CK_NETBIOS */ #endif /* OS2 */ #endif /* NOLOCAL */ " ", "While typing commands, you may use the following special characters:", " . DEL, RUBOUT, BACKSPACE, CTRL-H: Delete the most recent character typed.", " . CTRL-W: Delete the most recent word typed.", " . CTRL-U: Delete the current line.", " . CTRL-R: Redisplay the current line.", #ifdef CK_RECALL #ifdef OS2 " . Uparrow: Command recall - go backwards in command recall buffer.", " . Downarrow: Command recall - go forward in command recall buffer.", #ifndef NOIKSD " (Note: Arrow keys can be used only on the PC's physical keyboard.)", #endif /* NOIKSD */ #endif /* OS2 */ " . CTRL-P: Command recall - go backwards in command recall buffer.", " . CTRL-B: Command recall - same as Ctrl-P.", " . CTRL-N: Command recall - go forward in command recall buffer.", #endif /* CK_RECALL */ #ifndef NOLASTFILE " . CTRL-K: Insert the most recently entered local file specifiction.", #endif /* NOLASTFILE */ " . ? (question mark) Display a menu for the current command field." , " . ESC (or TAB or Ctrl-I) Attempt to complete the current field.", " . \\ (backslash) include the following character literally", #ifndef NOSPL " or introduce a backslash code, variable, or function.", #else " or introduce a numeric backslash code.", #endif /* NOSPL */ " ", "IMPORTANT: Since backslash (\\) is Kermit's command-line escape character,", "you must enter DOS, Windows, or OS/2 pathnames using either forward slash (/)" , "or double backslash (\\\\) as the directory separator in most contexts.", "Examples: C:/TMP/README.TXT, C:\\\\TMP\\\\README.TXT.", " ", "Command words other than filenames can be abbreviated in most contexts.", " ", "Basic commands:", " EXIT Exit from Kermit", " HELP Request general help", " HELP command Request help about the given command", " TAKE Execute commands from a file", " TYPE Display a file on your screen", " ORIENTATION Explains directory structure", " ", #ifndef NOXFER "Commands for file transfer:", " SEND Send files", " RECEIVE Receive files", " GET Get files from a Kermit server", #ifdef CK_RESEND " RESEND Recover an interrupted send", " REGET Recover an interrupted get from a server", #endif /* CK_RESEND */ #ifndef NOSERVER " SERVER Be a Kermit server", #endif /* NOSERVER */ " ", "File-transfer speed selection:", " FAST Use fast settings -- THIS IS THE DEFAULT", " CAUTIOUS Use slower, more cautious settings", " ROBUST Use extremely slow and cautious settings", " ", "File-transfer performance fine tuning:", " SET RECEIVE PACKET-LENGTH Kermit packet size", " SET WINDOW Number of sliding window slots", " SET PREFIXING Amount of control-character prefixing", #endif /* NOXFER */ #ifndef NOLOCAL " ", "To make a direct serial connection:", #ifdef OS2 #ifdef NT #ifdef CK_TAPI " SET PORT TAPI Select TAPI communication device", #endif /* CK_TAPI */ " SET PORT Select serial communication device", #else " SET PORT Select serial communication port or server", #endif /* NT */ #else " SET LINE Select serial communication device", #endif /* OS2 */ " SET SPEED Select communication speed", " SET PARITY Communications parity (if necessary)", #ifdef CK_RTSCTS " SET FLOW Communications flow control, such as RTS/CTS", #else " SET FLOW Communications flow control, such as XON/XOFF", #endif /* CK_RTSCTS */ " CONNECT Begin terminal connection", #ifndef NODIAL " ", "To dial out with a modem:", " SET DIAL DIRECTORY Specify dialing directory file (optional)", " SET DIAL COUNTRY-CODE Country you are dialing from (*)", " SET DIAL AREA-CODE Area-code you are dialing from (*)", " LOOKUP Lookup entries in your dialing directory (*)", " SET MODEM TYPE Select modem type", #ifdef OS2 #ifdef NT #ifdef CK_TAPI " SET PORT TAPI Select TAPI communication device", #endif /* CK_TAPI */ " SET PORT Select serial communication device", #else " SET PORT Select serial communication port or server", #endif /* NT */ #else " SET LINE Select serial communication device", #endif /* OS2 */ " SET SPEED Select communication speed", " SET PARITY Communications parity (if necessary)", " DIAL Dial the phone number", " CONNECT Begin terminal connection", " ", #ifdef OS2 "Further info: HELP DIAL, HELP SET MODEM, HELP SET PORT, HELP SET DIAL", #else "Further info: HELP DIAL, HELP SET MODEM, HELP SET LINE, HELP SET DIAL", #endif /* OS2 */ "(*) (For use with optional dialing directory)", #endif /* NODIAL */ #ifdef NETCONN " ", "To make a network connection:", #ifndef NODIAL " SET NETWORK DIRECTORY Specify a network services directory (optional)", " LOOKUP Lookup entries in your network directory", #endif /* NODIAL */ " SET NETWORK TYPE Select network type (if more than one available)", " SET HOST Make a network connection but stay in command mode", " CONNECT Begin terminal connection", #ifdef TNCODE " TELNET Select a Telnet host and CONNECT to it", #endif /* TNCODE */ #ifdef RLOGCODE " RLOGIN Select an Rlogin host and CONNECT to it", #endif /* RLOGCODE */ #ifdef ANYSSH " SSH [ OPEN ] Select an SSH host and CONNECT to it", #endif /* ANYSSH */ #ifdef NEWFTP " FTP [ OPEN ] Make an FTP connection", #endif /* NEWFTP */ #ifdef CKHTTP " HTTP OPEN Make an HTTP connection", #endif /* CKHTTP */ #endif /* NETCONN */ #ifdef NT " ", "To return from the terminal window to the K-95> prompt:", #else #ifdef OS2 " ", "To return from the terminal window to the K/2> prompt:", #else " ", "To return from a terminal connection to the C-Kermit prompt:", #endif /* OS2 */ #endif /* NT */ #ifdef OS2 " \ Press the key or key-combination shown after \"Command:\" in the status line", " (such as Alt-x) or type your escape character followed by the letter C.", #else " Type your escape character followed by the letter C.", #endif /* OS2 */ " ", "To display your escape character:", " SHOW ESCAPE", " ", "To display other settings:", " SHOW COMMUNICATIONS, SHOW TERMINAL, SHOW FILE, SHOW PROTOCOL, etc.", #else /* !NOLOCAL */ " ", "To display settings:", " SHOW COMMUNICATIONS, SHOW FILE, SHOW PROTOCOL, etc.", #endif /* NOLOCAL */ " ", "The manual for C-Kermit is the book \"Using C-Kermit\". For information", "about the manual, visit:", " http://kermit.columbia.edu/usingckermit.html", " ", #ifdef OS2 "For a Kermit 95 tutorial, visit:", " http://kermit.columbia.edu/k95tutor.html", " ", #endif /* OS2 */ "For an online C-Kermit tutorial, visit:", " http://kermit.columbia.edu/ckututor.html", " ", "To learn about script programming and automation:", " http://kermit.columbia.edu/ckscripts.html", " ", "For further information about a particular command, type HELP xxx,", "where xxx is the name of the command. For documentation, news of new", "releases, and information about other Kermit software, visit the", "Kermit Project website:", " ", " http://kermit.columbia.edu/", " ", "For information about technical support please visit this page:", " ", " http://kermit.columbia.edu/support.html", "" }; static char * hmxymatch[] = { "SET MATCH { DOTFILE, FIFO } { ON, OFF }", " Tells whether wildcards should match dotfiles (files whose names begin", " with period) or UNIX FIFO special files. MATCH FIFO default is OFF.", " MATCH DOTFILE default is OFF in UNIX, ON elsewhere.", "" }; #ifndef NOSEXP static char * hmxysexp[] = { "SET SEXPRESSION { DEPTH-LIMIT, ECHO-RESULT, TRUNCATE-ALL-RESULTS }", " DEPTH-LIMIT sets a limit on the depth of nesting or recursion in", " S-Expressions to prevent the program from crashing from memory exhaustion.", " ECHO-RESULT tells whether S-Expression results should be displayed as", " a result of evaluating an expression; the default is to display only at", " top (interactive) level; OFF means never display; ON means always display.", " TRUNCATE-ALL-RESULTS ON means the results of all arithmetic operations", " should be forced to integers (whole numbers) by discarding any fractional", " part. The default is OFF. SHOW SEXPRESSION displays the current settings." ,"" }; #endif /* NOSEXP */ #ifdef OS2 #ifdef KUI static char * hmxygui[] = { "SET GUI CLOSE OFF", " Disables the System Menu Close and File->Exit functions which could be", " used to exit Kermit. Once disabled these functions cannot be re-enabled", " and the only way to exit Kermit is via the Kermit Script EXIT command.", " ", "SET GUI DIALOGS { ON, OFF }", " ON means that popups, alerts, use GUI dialogs; OFF means to use", " text-mode popups or prompts. ON by default.", " ", "SET GUI FONT name size", " Chooses the font and size. Type \"set gui font ?\" to see the list of", " choices. The size can be a whole number or can contain a decimal point", " and a fraction (which is rounded to the nearest half point).", " ", "SET GUI MENUBAR OFF", " Disables menubar functions which could be used to modify the Kermit", " session configuration. Once disabled the menubar functions cannot be", " re-enabled.", " ", "SET GUI RGBCOLOR colorname redvalue greenvalue bluevalue", " Specifies the red-green-blue mixture to be used to render the given", " color name. Type \"set gui rgbcolor\" to see a list of colornames.", " the RGB values are whole numbers from 0 to 255.", " ", "SET GUI TOOLBAR OFF", " Disables toolbar functions which could be used to modify the Kermit", " session configuration. Once disabled the toolbar functions cannot be", " re-enabled.", " ", "SET GUI WINDOW POSITION x y", " Moves the K95 window to the given X,Y coordinates, pixels from top left.", " (Not yet implemented -- use command-line options to do this.)", " ", "SET GUI WINDOW RESIZE-MODE { CHANGE-DIMENSIONS, SCALE-FONT }", " Default is CHANGE-DIMENSIONS.", "", "SET GUI WINDOW RUN-MODE { MAXIMIZE, MINIMIZE, RESTORE }", " Changes the run mode state of the GUI window.", "" }; #endif /* KUI */ #endif /* OS2 */ #ifdef ANYSSH static char * hmxxssh[] = { #ifdef SSHBUILTIN "Syntax: SSH { ADD, AGENT, CLEAR, KEY, [ OPEN ], V2 } operands...", " Performs an SSH-related action, depending on the keyword that follows:", " ", "SSH ADD LOCAL-PORT-FORWARD local-port host port", " Adds a port forwarding triplet to the local port forwarding list.", " The triplet specifies a local port to be forwarded and the hostname /", " ip-address and port number to which the port should be forwarded from", " the remote host. Port forwarding is activated at connection", " establishment and continues until the connection is terminated.", " ", "SSH ADD REMOTE-PORT-FORWARD remote-port host port", " Adds a port forwarding triplet to the remote port forwarding list.", " The triplet specifies a remote port to be forwarded and the", " hostname/ip-address and port number to which the port should be", " forwarded from the local machine. Port forwarding is activated at", " connection establishment and continues until the connection is", " terminated.", " ", "SSH AGENT ADD [ identity-file ]", " Adds the contents of the identity-file (if any) to the SSH AGENT", " private key cache. If no identity-file is specified, all files", " specified with SET SSH IDENTITY-FILE are added to the cache.", " ", "SSH AGENT DELETE [ identity-file ]", " Deletes the contents of the identity-file (if any) from the SSH AGENT", " private key cache. If no identity-file is specified, all files", " specified with SET SSH IDENTITY-FILE are deleted from the cache.", " ", "SSH AGENT LIST [ /FINGERPRINT ]", " Lists the contents of the SSH AGENT private key cache. If /FINGERPRINT", " is specified, the fingerprint of the private keys are displayed instead", " of the keys.", " ", "SSH CLEAR LOCAL-PORT-FORWARD", " Clears the local port forwarding list.", " ", "SSH CLEAR REMOTE-PORT-FORWARD", " Clears the remote port forwarding list.", " ", "SSH KEY commands:", " The SSH KEY commands create and manage public and private key pairs", " (identities). There are three forms of SSH keys. Each key pair is", " stored in its own set of files:", " ", " Key Type Private Key File Public Key File", " v1 RSA keys \\v(appdata)ssh/identity \\v(appdata)ssh/identity.pub", " v2 RSA keys \\v(appdata)ssh/id_rsa \\v(appdata)ssh/id_rsa.pub", " v2 DSA keys \\v(appdata)ssh/id_dsa \\v(appdata)ssh/id_dsa.pub", " ", " Keys are stored using the OpenSSH keyfile format. The private key", " files can be (optionally) protected by specifying a passphrase. A", " passphrase is a longer version of a password. English text provides", " no more than 2 bits of key data per character. 56-bit keys can be", " broken by a brute force attack in approximately 24 hours. When used,", " private key files should therefore be protected by a passphrase of at", " least 40 characters (about 80 bits).", " ", " To install a public key file on the host, you must transfer the file", " to the host and append it to your \"authorized_keys\" file. The file", " permissions must be 600 (or equivalent).", " ", "SSH KEY CHANGE-PASSPHRASE [ /NEW-PASSPHRASE:passphrase", " /OLD-PASSPHRASE:passphrase ] filename", " This re-encrypts the specified private key file with a new passphrase.", " The old passphrase is required. If the passphrases (and filename) are", " not provided Kermit prompts your for them.", " ", "SSH KEY CREATE [ /BITS:bits /PASSPHRASE:passphrase", " /TYPE:{ V1-RSA, V2-DSA, V2-RSA } /V1-RSA-COMMENT:comment ] filename", " This command creates a new private/public key pair. The defaults are:", " BITS:1024 and TYPE:V2-RSA. The filename is the name of the private", " key file. The public key is created with the same name with .pub", " appended to it. If a filename is not specified Kermit prompts you for", " it. V1 RSA key files may have an optional comment, which is ignored", " for other key types.", " ", "SSH KEY DISPLAY [ /FORMAT:{FINGERPRINT,IETF,OPENSSH,SSH.COM} ] filename", " This command displays the contents of a public or private key file.", " The default format is OPENSSH.", " ", "SSH KEY V1 SET-COMMENT filename comment", " This command replaces the comment associated with a V1 RSA key file.", " ", "SSH [ OPEN ] host [ port ] [ /COMMAND:command /USER:username", " /PASSWORD:pwd /VERSION:{ 1, 2 } /X11-FORWARDING:{ ON, OFF } ]", " This command establishes a new connection using SSH version 1 or", " version 2 protocol. The connection is made to the specified host on", " the SSH port (you can override the port by including a port name or", " number after the host name). Once the connection is established the", " authentication negotiations begin. If the authentication is accepted,", " the local and remote port forwarding lists are used to establish the", " desired connections. If X11 Forwarding is active, this results in a", " remote port forwarding between the X11 clients on the remote host and", " X11 Server on the local machine. If a /COMMAND is provided, the", " command is executed on the remote host in place of your default shell.", " ", " An example of a /COMMAND to execute C-Kermit in SERVER mode is:", " SSH OPEN hostname /COMMAND:{kermit -x -l 0}", " ", "SSH V2 REKEY", " Requests that an existing SSH V2 connection generate new session keys.", #else /* SSHBUILTIN */ "Syntax: SSH [ options ] [ command ]", " Makes an SSH connection using the external ssh program via the SET SSH", " COMMAND string, which is \"ssh -e none\" by default. Options for the", " external ssh program may be included. If the hostname is followed by a", " command, the command is executed on the host instead of an interactive", " shell.", #endif /* SSHBUILTIN */ "" }; static char *hmxyssh[] = { #ifdef SSHBUILTIN "SET SSH AGENT-FORWARDING { ON, OFF }", " If an authentication agent is in use, setting this value to ON", " results in the connection to the agent being forwarded to the remote", " computer. The default is OFF.", " ", "SET SSH CHECK-HOST-IP { ON, OFF }", " Specifies whether the remote host's ip-address should be checked", " against the matching host key in the known_hosts file. This can be", " used to determine if the host key changed as a result of DNS spoofing.", " The default is ON.", " ", "SET SSH COMPRESSION { ON, OFF }", " Specifies whether compression will be used. The default is ON.", " ", "SET SSH DYNAMIC-FORWARDING { ON, OFF }", " Specifies whether Kermit is to act as a SOCKS4 service on port 1080", " when connected to a remote host via SSH. When Kermit acts as a SOCKS4", " service, it accepts connection requests and forwards the connections", " through the remote host. The default is OFF.", " ", "SET SSH GATEWAY-PORTS { ON, OFF }", " Specifies whether Kermit should act as a gateway for forwarded", " connections received from the remote host. The default is OFF.", " ", "SET SSH GSSAPI DELEGATE-CREDENTIALS { ON, OFF }", " Specifies whether Kermit should delegate GSSAPI credentials to ", " the remote host after authentication. Delegating credentials allows", " the credentials to be used from the remote host. The default is OFF.", " ", "SET SSH HEARTBEAT-INTERVAL ", " Specifies a number of seconds of idle time after which an IGNORE", " message will be sent to the server. This pulse is useful for", " maintaining connections through HTTP Proxy servers and Network", " Address Translators. The default is OFF (0 seconds).", " ", "SET SSH IDENTITY-FILE filename [ filename [ ... ] ]", " Specifies one or more files from which the user's authorization", " identities (private keys) are to be read when using public key", " authorization. These are files used in addition to the default files:", " ", " \\v(appdata)ssh/identity V1 RSA", " \\v(appdata)ssh/id_rsa V2 RSA", " \\v(appdata)ssh/id_dsa V2 DSA", " ", "SET SSH KERBEROS4 TGT-PASSING { ON, OFF }", " Specifies whether Kermit should forward Kerberos 4 TGTs to the host.", " The default is OFF.", " ", "SET SSH KERBEROS5 TGT-PASSING { ON, OFF }", " Specifies whether Kermit should forward Kerberos 5 TGTs to to the", " host. The default is OFF.", " ", "SET SSH PRIVILEGED-PORT { ON, OFF }", " Specifies whether a privileged port (less than 1024) should be used", " when connecting to the host. Privileged ports are not required except", " when using SSH V1 with Rhosts or RhostsRSA authorization. The default", " is OFF.", " ", "SET SSH QUIET { ON, OFF }", " Specifies whether all messages generated in conjunction with SSH", " protocols should be suppressed. The default is OFF.", " ", "SET SSH STRICT-HOST-KEY-CHECK { ASK, ON, OFF }", " Specifies how Kermit should behave if the the host key check fails.", " When strict host key checking is OFF, the new host key is added to the", " protocol-version-specific user-known-hosts-file. When strict host key", " checking is ON, the new host key is refused and the connection is", " dropped. When set to ASK, Kermit prompt you to say whether the new", " host key should be accepted. The default is ASK.", " ", " Strict host key checking protects you against Trojan horse attacks.", " It depends on you to maintain the contents of the known-hosts-file", " with current and trusted host keys.", " ", "SET SSH USE-OPENSSH-CONFIG { ON, OFF }", " Specifies whether Kermit should parse an OpenSSH configuration file", " after applying Kermit's SET SSH commands. The configuration file", " would be located at \\v(home)ssh/ssh_config. The default is OFF.", " ", "SET SSH V1 CIPHER { 3DES, BLOWFISH, DES }", " Specifies which cipher should be used to protect SSH version 1", " connections. The default is 3DES.", " ", "SET SSH V1 GLOBAL-KNOWN-HOSTS-FILE filename", " Specifies the location of the system-wide known-hosts file. The", " default is:", " ", " \v(common)ssh_known_hosts", " ", "SET SSH V1 USER-KNOWN-HOSTS-FILE filename", " Specifies the location of the user-known-hosts-file. The default", " location is:", " ", " \\v(appdata)ssh/known_hosts", " ", "SET SSH V2 AUTHENTICATION { EXTERNAL-KEYX, GSSAPI, HOSTBASED, ", " KEYBOARD-INTERACTIVE, PASSWORD, PUBKEY, SRP-GEX-SHA1 } [ ... ]", " Specifies an ordered list of SSH version 2 authentication methods to", " be used when connecting to the remote host. The default list is:", " ", " external-keyx gssapi hostbased publickey srp-gex-sha1 publickey", " keyboard-interactive password none", " ", "SET SSH V2 AUTO-REKEY { ON, OFF }", " Specifies whether Kermit automatically issues rekeying requests", " once an hour when SSH version 2 in in use. The default is ON.", " ", "SET SSH V2 CIPHERS { 3DES-CBC, AES128-CBC AES192-CBC AES256-CBC", " ARCFOUR BLOWFISH-CBC CAST128-CBC RIJNDAEL128-CBC RIJNDAEL192-CBC", " RIJNDAEL256-CBC }", " Specifies an ordered list of SSH version ciphers to be used to encrypt", " the established connection. The default list is:", " ", " aes128-cbc 3des-cbc blowfish-cbc cast128-cbc arcfour aes192-cbc", " aes256-cbc", " ", " \"rijndael\" is an alias for \"aes\".", " ", "SET SSH V2 GLOBAL-KNOWN-HOSTS-FILE filename", " Specifies the location of the system-wide known-hosts file. The default", " location is:", " ", " \\v(common)ssh/known_hosts2", " ", "SET SSH V2 HOSTKEY-ALGORITHMS { SSH-DSS, SSH-RSA }", " Specifies an ordered list of hostkey algorithms to be used to verify", " the identity of the host. The default list is", " ", " ssh-rsa ssh-dss", " ", "SET SSH V2 MACS { HMAC-MD5 HMAC-MD5-96 HMAC-RIPEMD160 HMAC-SHA1", " HMAC-SHA1-96 }", " Specifies an ordered list of Message Authentication Code algorithms to", " be used for integrity protection of the established connection. The", " default list is:", " ", " hmac-md5 hmac-sha1 hmac-ripemd160 hmac-sha1-96 hmac-md5-96", " ", "SET SSH V2 USER-KNOWN-HOSTS-FILE filename", " Specifies the location of the user-known-hosts file. The default", " location is:", " ", " \\v(appdata)ssh/known_hosts2", " ", "SET SSH VERBOSE level", " Specifies how many messages should be generated by the OpenSSH engine.", " The level can range from 0 to 7. The default value is 2.", " ", "SET SSH VERSION { 1, 2, AUTOMATIC }", " Specifies which SSH version should be negotiated. The default is", " AUTOMATIC which means use version 2 if supported; otherwise to fall", " back to version 1.", " ", "SET SSH X11-FORWARDING { ON, OFF }", " Specifies whether X Windows System Data is to be forwarded across the", " established SSH connection. The default is OFF. When ON, the DISPLAY", " value is either set using the SET TELNET ENV DISPLAY command or read", " from the DISPLAY environment variable.", " ", "SET SSH XAUTH-LOCATION filename", " Specifies the location of the xauth executable (if provided with the", " X11 Server software.)", #else /* SSHBUILTIN */ "Syntax: SET SSH COMMAND command", " Specifies the external command to be used to make an SSH connection.", " By default it is \"ssh -e none\" (ssh with no escape character).", #endif /* SSHBUILTIN */ "" }; #endif /* ANYSSH */ #ifdef NEWFTP static char *hmxygpr[] = { "Syntax: SET GET-PUT-REMOTE { AUTO, FTP, KERMIT}", " Tells Kermit whether GET, PUT, and REMOTE commands should be directed", " at a Kermit server or an FTP server. The default is AUTO, meaning that", " if you have only one active connection, the appropriate action is taken", " when you give a GET, PUT, or REMOTE command. SET GET-PUT-REMOTE FTP forces" , " Kermit to treat GET, PUT, and REMOTE as FTP client commands; setting this", " to KERMIT forces these commands to be treated as Kermit client commands.", " NOTE: PUT includes SEND, MPUT, MSEND, and all other similar commands.", " Also see HELP REMOTE, HELP SET LOCUS, HELP FTP.", "" }; #endif /* NEWFTP */ #ifdef LOCUS static char *hmxylocus[] = { #ifdef KUI "Syntax: SET LOCUS { ASK, AUTO, LOCAL, REMOTE }", #else "Syntax: SET LOCUS { AUTO, LOCAL, REMOTE }", #endif /* KUI */ " Specifies whether unprefixed file management commands should operate", " locally or (when there is a connection to a remote FTP or Kermit", " server) sent to the server. The affected commands are: CD (CWD), PWD,", " CDUP, DIRECTORY, DELETE, RENAME, MKDIR, and RMDIR. To force any of", " these commands to be executed locally, give it an L-prefix: LCD, LDIR,", " etc. To force remote execution, use the R-prefix: RCD, RDIR, and so", " on. SHOW COMMAND shows the current Locus.", " ", " By default, the Locus for file management commands is switched", " automatically whenever you make or close a connection: if you make an", " FTP connection, the Locus becomes REMOTE; if you close an FTP connection", " or make any other kind of connection, the Locus becomes LOCAL.", #ifdef KUI " ", " There are two kinds of automatic switching: ASK (the default) which", " asks you if it's OK to switch, and AUTO, which switches without asking.", #endif /* KUI */ " ", " If you give a SET LOCUS LOCAL or SET LOCUS REMOTE command, this sets", " the locus as indicated and disables automatic switching.", #ifdef KUI " SET LOCUS AUTO or SET LOCUS ASK restores automatic switching.", " You can also change Locus switching and behavior in the Actions menu.", #else " SET LOCUS AUTO restores automatic switching.", #endif /* KUI */ "", }; #endif /* LOCUS */ #ifdef UNIX #ifndef NOPUTENV static char *hmxxputenv[] = { "Syntax: PUTENV name value", " Creates or modifies the environment variable with the given name to have", " the given value. Purpose: to pass parameters to subprocesses without", " having them appear on the command line. If the value is blank (empty),", " the variable is deleted. The result is visible to this instantiation of", " C-Kermit via \\$(name) and to any inferior processes by whatever method", " they use to access environment variables. The value may be enclosed in", " doublequotes or braces, but it need not be; if it is the outermost", " doublequotes or braces are removed.", " ", " Note the syntax:", " PUTENV name value", " not:", " PUTENV name=value", " ", " There is no equal sign between name and value, and the name itself may", " not include an equal sign.", "", }; #endif /* NOPUTENV */ #endif /* UNIX */ static char *hmxxtak[] = { "Syntax: TAKE filename [ arguments ]", " Tells Kermit to execute commands from the named file. Optional argument", " words, are automatically assigned to the macro argument variables \\%1", " through \\%9. Kermit command files may themselves contain TAKE commands,", " up to any reasonable depth of nesting.", "" }; #ifdef TCPSOCKET static char *hmxxfirew[] = { #ifdef OS2 "Firewall Traversal in Kermit 95", #else "Firewall Traversal in C-Kermit", #endif " ", #ifndef NEWFTP #ifndef CKHTTP #ifndef CK_SOCKS #define NOFIREWALL #endif #endif #endif #ifdef NOFIREWALL "This version of Kermit was built with no support for firewall traversal", "protocols. Kermit can be built with support for HTTP Proxy Servers,", "SOCKS authorized firewall traversal, and FTP Passive connection modes.", " ", #else /* NOFIREWALL */ #ifdef CKHTTP "The simplist form of firewall traversal is the HTTP CONNECT command. The", "CONNECT command was implemented to allow a public web server which usually", "resides on the boundary between the public and private networks to forward", "HTTP requests from clients on the private network to public web sites. To", "allow secure web connections, the HTTP CONNECT command authenticates the", "client with a username/password and then establishes a tunnel to the", "desired host.", " ", "Web servers that support the CONNECT command can be configured to allow", "outbound connections for authenticated users to any TCP/IP hostname-port", "combination accessible to the Web server. HTTP CONNECT can be used only", "with TCP-based protocols. Protocols such as Kerberos authentication that", "use UDP/IP cannot be tunneled using HTTP CONNECT.", " ", "SET TCP HTTP-PROXY [switches] [[:]]", " If a hostname or ip-address is specified, Kermit uses the given", " proxy server when attempting outgoing TCP connections. If no hostnamer", " or ip-address is specified, any previously specified Proxy server is", " removed. If no port number is specified, the \"http\" service is used.", " [switches] can be one or more of:", " /AGENT: /USER: /PASSWORD:", " Switch parameters are used when connecting to the proxy server and", " override any other values associated with the connection.", " ", #endif /* CKHTTP */ #ifdef CK_SOCKS "In the early 1990s as firewalls were becoming prevalent, David Koblas", "developed the SOCKS protocol for TCP/IP firewall traversal. Two versions", "of SOCKS are currently in use: Version 4.2 lets TCP/IP client applications", "traverse firewalls, similar to HTTP CONNECT, except that the SOCKS client", "is aware of the public source IP address and port, which can be used within", "the application protocol to assist in securing the connection (e.g. FTP", "sessions secured with GSSAPI Kerberos 5).", " ", "In 1995 the IETF issued SOCKS Protocol Version 5 (RFC 1928), which is", "significantly more general than version 4. Besides supporting client-", "to-server TCP/IP connections, it also includes:", " ", " . Authenticated firewall traversal of UDP/IP packets.", " . Authenticated binding of incoming public ports on the firewall.", " ", "This lets a service on the private network offer public services. It also", "lets client applications like FTP establish a temporary public presence", "that can be used by the FTP server to create a data channel. By allowing", "the client to bind to a public port on the firewall and be aware of the", "public address, SOCKS 5 lets the application protocol communicate this", "information to the server.", " ", #ifdef OS2 #ifdef NT "Kermit 95 supports SOCKS 4.2. The SOCKS Server is specified with:", " ", " SET TCP SOCKS-SERVER hostname/ip-address", " ", "The SOCKS.CONF file is found by examining the ETC environment variable;", "searching in \\WINDOWS on Windows 95/98/ME; or the", "\\WINDOWS\\SYSTEM\\DRIVERS\\ETC directory on NT\\2000\\XP systems.", #else /* NT */ "Kermit/2 provides support for SOCKS 4.2 servers when using IBM TCP/IP 2.0,", "IBM OS/2 WARP, or a compatible protocol stack. SOCKS is one popular means", "of implementing a firewall between a private network and the Internet.", " ", "Kermit/2 shares the same SOCKS environment variables as IBM Gopher. It also", "supports the use of local SOCKS configuration files.", " ", "To specify the default SOCKS Server, add SET SOCKS_SERVER= to your", "CONFIG.SYS file.", " ", "If you must use a SOCKS Distributed Name Server, add SET SOCKS_NS= to your", "CONFIG.SYS file.", " ", "If you must use a specific with your SOCKS server, be sure to add SET USER=", "to your CONFIG.SYS file. Otherwise, \"os2user\" is used by default.", " ", "The SOCKS configuration file must be placed in the directory pointed to by", "the ETC environment variable as declared in your CONFIG.SYS file. The name", "should be SOCKS.CONF. On a FAT file system, use SOCKS.CNF.", " ", "The format of the lines in the SOCKS configuration file are as follows:", " ", " . # comments", " . deny [*=userlist] dst_addr dst_mask [op port]", " . direct [*=userlist] dst_addr dst_mask [op port]", " . sockd [@=serverlist] [*=userlist] dst_addr dst_mask [op port]", " ", "op must be one of 'eq', 'neq', 'lt', 'gt', 'le', or 'ge'. dst_addr,", "dst_mask, and port may be either numeric or name equivalents.", " ", "Kermit/2 ignores the [*=userlist] and [@=serverlist] fields. Matches are", "determined on a first match not a best match basis. Addresses for which no", "match is found default to \"sockd\".", " ", "For completeness: Fields in square brackets are optional. The optional", "@=serverlist field with a 'sockd' line specifies the list of SOCKS servers", "the client should try (in the given order) instead of the default SOCKS", "server. If the @=serverlist part is omitted, then the default SOCKS server", "is used. Commas are used in the userlist and serverlist as separators, no", "white spaces are allowed.", #endif /* NT */ " ", #else /* OS2 */ #ifdef CK_SOCKS5 "This version of C-Kermit supports SOCKS version 5.", #else /* CK_SOCKS5 */ "This version of C-Kermit supports SOCKS version 4.", #endif /* CK_SOCKS5 */ "See the man page (or other system documentation) for information on", "configuring the SOCKS library via the /etc/socks.conf file.", #endif /* OS2 */ " ", #endif /* CK_SOCKS */ #ifdef NEWFTP "FTP is one of the few well-known Internet services that requires", "multiple connections. As described above, FTP originally required the", "server to establish the data connection to the client using a destination", "address and port provided by the client. This doesn't work with port", "filtering firewalls.", " ", "Later, FTP protocol added a \"passive\" mode, in which connections for", "the data channels are created in the reverse direction. Instead of the", "server establishing a connection to the client, the client makes a second", "connection with the server as the destination. This works just fine as", "long as the client is behind the firewall and the server is in public", "address space. If the server is behind a firewall then the traditional", "active mode must be used. If both the client and server are behind their", "own port filtering firewalls then data channels cannot be established.", " ", "In Kermit's FTP client, passive mode is controlled with the command:", " ", " SET FTP PASSIVE-MODE { ON, OFF }", " ", "The default is ON, meaning to use passive mode.", #endif /* NEWFTP */ #endif /* NOFIREWALL */ "" }; #endif /* TCPSOCKET */ static char *hmxxsave[] = { "Syntax: SAVE item filename { NEW, APPEND }", " Saves the requested material in the given file. A new file is created", " by default; include APPEND at the end of the command to append to an", " existing file. Items:", #ifndef NOSETKEY " KEYMAP Saves the current key settings.", #endif /* NOSETKEY */ #ifdef CK_RECALL " COMMAND HISTORY Saves the current command recall (history) buffer", #endif /* CK_RECALL */ #ifdef OS2 " COMMAND SCROLLBACK Saves the current command-screen scrollback buffer", " TERMINAL SCROLLBACK Saves the current terminal-screen scrollback buffer", #endif /* OS2 */ "" }; #ifdef CKROOT static char *hmxxchroot[] = { "Syntax: SET ROOT directoryname", " Sets the root for file access to the given directory and disables access", " to system and shell commands and external programs. Once this command", " is given, no files or directories outside the tree rooted by the given", " directory can be opened, read, listed, deleted, renamed, or accessed in", " any other way. This command can not be undone by a subsequent SET ROOT", " command. Primarily for use with server mode, to restrict access of", " clients to a particular directory tree. Synonym: CHROOT.", "" }; #endif /* CKROOT */ static char *hmxxscrn[] = { "Syntax: SCREEN { CLEAR, CLEOL, MOVE row column }", #ifdef OS2 " Performs screen-formatting actions.", #else " Performs screen-formatting actions. Correct operation of these commands", " depends on proper terminal setup on both ends of the connection -- mainly", " that the host terminal type is set to agree with the kind of terminal or", " the emulation you are viewing C-Kermit through.", #endif /* OS2 */ " ", "SCREEN CLEAR", " Moves the cursor to home position and clears the entire screen.", #ifdef OS2 " Synonyms: CLS, CLEAR SCREEN, CLEAR COMMAND-SCREEN ALL", #else " Synonyms: CLS, CLEAR SCREEN.", #endif /* OS2 */ " ", "SCREEN CLEOL", " Clears from the current cursor position to the end of the line.", #ifdef OS2 " Synonym: CLEAR COMMAND-SCREEN EOL", #endif /* OS2 */ " ", "SCREEN MOVE row column", " Moves the cursor to the indicated row and column. The row and column", " numbers are 1-based so on a 24x80 screen, the home position is 1 1 and", " the lower right corner is 24 80. If a row or column number is given that", " too large for what Kermit or the operating system thinks is your screen", " size, the appropriate number is substituted.", " ", "Also see:", #ifdef OS2 " HELP FUNCTION SCRNCURX, HELP FUNCTION SCRNCURY, HELP FUNCTION SCRSTR,", #endif /* OS2 */ " SHOW VARIABLE TERMINAL, SHOW VARIABLE COLS, SHOW VAR ROWS, SHOW COMMAND.", "" }; #ifndef NOSPL static char *hmfword[] = { "Function \\fword(s1,n1,s2,s3,n2,n3) - Extracts a word from a string.", " s1 = source string.", " n1 = word number (1-based) counting from left; if negative, from right.", " s2 = optional break set.", " s3 = optional include set (or ALL, CSV, or TSV).", " n2 = optional grouping mask.", " n3 = optional separator flag:", " 0 = collapse adjacent separators;", " 1 = don't collapse adjacent separators.", " ", " \\fword() returns the n1th \"word\" of the string s1, according to the", " criteria specified by the other parameters.", " ", " The BREAK SET is the set of all characters that separate words. The", " default break set is all characters except ASCII letters and digits.", " ASCII (C0) control characters are treated as break characters by default,", " as are spacing and punctuation characters, brackets, and so on, and", " all 8-bit characters.", " ", " The INCLUDE SET is the set of characters that are to be treated as ", " parts of words even though they normally would be separators. The", " default include set is empty. Three special symbolic include sets are", " also allowed:", " ", " ALL (meaning include all bytes that are not in the break set)", " CSV (special treatment for Comma-Separated-Value records)", " TSV (special treatment for Tab-Separated-Value records)", " ", " For operating on 8-bit character sets, the include set should be ALL.", " ", " If the GROUPING MASK is given and is nonzero, words can be grouped by", " quotes or brackets selected by the sum of the following:", " ", " 1 = doublequotes: \"a b c\"", " 2 = braces: {a b c}", " 4 = apostrophes: 'a b c'", " 8 = parentheses: (a b c)", " 16 = square brackets: [a b c]", " 32 = angle brackets: ", " ", " Nesting is possible with {}()[]<> but not with quotes or apostrophes.", " ", "Returns string:", " Word number n1, if there is one, otherwise an empty string.", " ", "Also see:", " HELP FUNCTION SPLIT", "" }; static char *hmxxprompt[] = { "Syntax: PROMPT [ text ]", " Enters interactive command level from within a script in such a way that", " the script can be continued with an END or RETURN command. STOP, EXIT,", " SHOW STACK, TRACE, and Ctrl-C all have their normal effects. The PROMPT", " command allows variables to be examined or changed, or any other commands", " to be given, in any number, prior to returning to the script, allowing", " Kermit to serve as its own debugger; adding the PROMPT command to a script", " is like setting a breakpoint. If the optional text is included, it is", " used as the new prompt for this level, e.g. \"prompt Breakpoint_1>\".", "" }; static char *hxxinp[] = { "Syntax: INPUT [ /COUNT:n /CLEAR /NOMATCH /NOWRAP ] \ { number-of-seconds, time-of-day } [ text ]", " ", "Examples:", " INPUT 5 Login:", " INPUT 23:59:59 RING", " INPUT /NOMATCH 10", " INPUT /CLEAR 10 \\13\\10", " INPUT /CLEAR 10 \\13\\10$\32", " INPUT /COUNT:256 10", " INPUT 10 \\fpattern(<*@*.*>)", " ", " Waits up to the given number of seconds, or until the given time of day,", " for the given text to arrive on the connection. For use in script programs", " with IF FAILURE or IF SUCCESS, which tell whether the desired text arrived", " within the given amount of time.", " ", " The text, if given, can be a regular text or it can be a \\pattern()", " invocation, in which case it is treated as a pattern rather than a literal", " string (HELP PATTERNS for details).", " ", " If the /COUNT: switch is included, INPUT waits for the given number of", " characters to arrive.", " ", " If no text is specified, INPUT waits for the first character that arrives", " and makes it available in the \\v(inchar) variable. This is equivalent to", " including a /COUNT: switch with an argument of 1.", " ", " If the /NOMATCH switch is included, INPUT does not attempt to match any", " characters, but continues reading from the communication connection until", " the timeout interval expires. If the timeout interval is 0, the INPUT", " command does not wait; i.e. the given text must already be available for", " reading for the INPUT command to succeed. If the interval is negative,", " the INPUT command waits forever.", " ", " The INPUT buffer, \\v(input), is normally circular. Incoming material is", " appended to it until it reaches the end, and then it wraps around to the", " beginning. If the /CLEAR switch is given, INPUT clears its buffer before", " reading from the connection.", " ", " Typical example of use:", " ", " INPUT 10 login:", " IF FAIL EXIT 1 \"Timed out waiting for login prompt\"", " LINEOUT myuserid", " INPUT 10 Password:", " IF FAIL EXIT 1 \"Timed out waiting for Password prompt\"", " LINEOUT xxxxxxx", " ", " The /NOWRAP switch means that INPUT should return with failure status", " and with \\v(instatus) set to 6 if its internal buffer fills up before", " any of the other completion criteria are met. This allows for capture", " of the complete incoming data stream (except NUL bytes, which are", " discarded). CAUTION: if the search target (if any) spans the INPUT buffer", " boundary, INPUT will not succeed.", " ", " \ \\v(instatus) values are: 0 (success), 1 (timed out), 2 (keyboard interrupt),", " 3 (internal error), 4 (I/O error or connection lost), 5 (INPUT disabled),", " and 6 (buffer filled and /NOWRAP set); these are shown by \\v(inmessage).", " ", " Also see OUTPUT, MINPUT, REINPUT, SET INPUT and CLEAR. See HELP PAUSE for", " details on time-of-day format and HELP PATTERNS for pattern syntax.", ""}; static char *hxxout[] = { "Syntax: OUTPUT text", " Sends the text out the communications connection, as if you had typed it", " during CONNECT mode. The text may contain backslash codes, variables,", " etc, plus the following special codes:", " ", " \\N - Send a NUL (ASCII 0) character (you can't use \\0 for this).", " \\B - Send a BREAK signal.", " \\L - Send a Long BREAK signal.", " ", "Also see SET OUTPUT.", "" }; #endif /* NOSPL */ static char *hxypari[] = { "SET PARITY NONE", " Chooses 8 data bits and no parity.", " ", "SET PARITY { EVEN, ODD, MARK, SPACE }", " Chooses 7 data bits plus the indicated kind of parity.", " Forces 8th-bit prefixing during file transfer.", " ", #ifdef HWPARITY "SET PARITY HARDWARE { EVEN, ODD }", " Chooses 8 data bits plus the indicated kind of parity.", " ", "Also see SET TERMINAL BYTESIZE, SET SERIAL, and SET STOP-BITS.", #else "Also see SET TERMINAL BYTESIZE and SET SERIAL.", #endif /* HWPARITY */ ""}; #ifndef NOLOCAL static char *hxyesc[] = { #ifdef OS2 "Syntax: SET ESCAPE number", " Decimal ASCII value for escape character during CONNECT, normally 29", " (Control-]). Type the escape character followed by C to get back to the", " C-Kermit prompt or followed by ? to see other options, or use the \\Kexit", " keyboard verb, normally assigned to Alt-x.", #else #ifdef NEXT "Syntax: SET ESCAPE number", " Decimal ASCII value for escape character during CONNECT, normally 29", " (Control-]). Type the escape character followed by C to get back to the", " C-Kermit prompt or followed by ? to see other options.", #else "Syntax: SET ESCAPE number", " Decimal ASCII value for escape character during CONNECT, normally 28", " (Control-\\). Type the escape character followed by C to get back to the", " C-Kermit prompt or followed by ? to see other options.", #endif /* NEXT */ #endif /* OS2 */ " ", "You may also enter the escape character as ^X (circumflex followed by a", "letter or one of: @, ^, _, [, \\, or ], to indicate a control character;", "for example, SET ESC ^_ sets your escape character to Ctrl-Underscore.", " ", "You can also specify an 8-bit character (128-255) as your escape character,", "either by typing it literally or by entering its numeric code.", "" }; #endif /* NOLOCAL */ #ifndef NOSPL static char *hxyout[] = { "SET OUTPUT PACING ", " How many milliseconds to pause after sending each OUTPUT character,", " normally 0.", " ", "SET OUTPUT SPECIAL-ESCAPES { ON, OFF }", " Whether to process the special OUTPUT-only escapes \\B, \\L, and \\N.", " Normally ON (they are processed).", "" }; static char *hxyinp[] = { "Syntax: SET INPUT parameter value", " ", #ifdef CK_AUTODL "SET INPUT AUTODOWNLOAD { ON, OFF }", " Controls whether autodownloads are allowed during INPUT command execution.", " ", #endif /* CK_AUTODL */ "SET INPUT BUFFER-LENGTH number-of-bytes", " Removes the old INPUT buffer and creates a new one with the given length.", " ", "SET INPUT CANCELLATION { ON, OFF }", " Whether an INPUT in progress can be can interrupted from the keyboard.", " ", "SET INPUT CASE { IGNORE, OBSERVE }", " Tells whether alphabetic case is to be significant in string comparisons.", " This setting is local to the current macro or command file, and is", " inherited by subordinate macros and take files.", " ", "SET INPUT ECHO { ON, OFF }", " Tells whether to display arriving characters read by INPUT on the screen.", " ", #ifdef CKFLOAT "SET INPUT SCALE-FACTOR ", " A number to multiply all INPUT timeouts by, which may include a fractional", " part, e.g. 2.5. All INPUT commands that specify a timeout in seconds", " (as opposed to a specific time of day) have their time limit adjusted", " automatically by this factor, which is also available in the built-in", " read-only variable \\v(inscale). The default value is 1.0.", " ", #endif /* CKFLOAT */ "SET INPUT SILENCE ", " The maximum number to seconds of silence (no input at all) before the", " INPUT command times out, 0 for no maximum.", " ", #ifdef OS2 "SET INPUT TERMINAL { ON, OFF }", " Determines whether the data received during an INPUT command is displayed", " in the terminal window. Default is ON.", " ", #endif /* OS2 */ "SET INPUT TIMEOUT-ACTION { PROCEED, QUIT }", " Tells whether to proceed or quit from a script program if an INPUT command", " fails. PROCEED (default) allows use of IF SUCCESS / IF FAILURE commands.", " This setting is local to the current macro or command file, and is", " inherited by subordinate macros and take files.", "" }; static char *hxyfunc[] = { "SET FUNCTION DIAGNOSTICS { ON, OFF }", " Whether to issue diagnostic messages for illegal function calls and", " references to nonexistent built-in variables. ON by default.", " ", "SET FUNCTION ERROR { ON, OFF }", " Whether an illegal function call or reference to a nonexistent built-in", " variable should cause a command to fail. OFF by default.", "" }; #endif /* NOSPL */ static char *hxyxyz[] = { #ifdef CK_XYZ #ifdef XYZ_INTERNAL /* This is for built-in protocols */ "Syntax: SET PROTOCOL { KERMIT, XMODEM, YMODEM, ZMODEM } [ s1 s2 [ s3 ] ]", " Selects protocol to use for transferring files. String s1 is a command to", " send to the remote host prior to SENDing files with this protocol in", " binary mode; string s2 is the same thing but for text mode. Use \"%s\" in", " any of these strings to represent the filename(s). If the protocol is", " KERMIT, you may also specify a string s3, the command to start a Kermit", " server on the remote host when you give a GET, REGET, REMOTE, or other", " client command. Use { braces } if any command contains spaces. Examples:", " ", " set proto xmodem {rx %s} {rx -a %s}", " set proto kermit {kermit -YQir} {kermit -YQTr} {kermit -YQx}", #else /* This is for when non-Kermit protocols are external */ "Syntax: \ SET PROTOCOL { KERMIT, XMODEM, YMODEM, ZMODEM } [ s1 s2 s3 s4 s5 s6 ]", " Selects protocol to use for transferring files. s1 and s2 are commands to", " output prior to SENDing with this protocol, to automatically start the", " RECEIVE process on the other end in binary or text mode, respectively.", " If the protocol is KERMIT, s3 is the command to start a Kermit server on", " the remote computer, and there are no s4-s6 commands. Otherwise, s3 and", " s4 are commands used on this computer for sending files with this protocol", " in binary or text mode, respectively; s5 and s6 are the commands for", " receiving files with this protocol. Use \"%s\" in any of these strings", " to represent the filename(s). Use { braces } if any command contains", " spaces. Examples:", " ", " set proto kermit {kermit -YQir} {kermit -YQTr} {kermit -YQx}", " set proto ymodem rb {rb -a} {sb %s} {sb -a %s} rb rb", " ", "External protocols require REDIRECT and external file transfer programs that", "use redirectable standard input/output.", #endif /* XYZ_INTERNAL */ #else "Syntax: \ SET PROTOCOL KERMIT [ s1 [ s2 [ s3 ] ] ]", " Lets you specify the autoupload binary, autoupload text, and autoserver", " command strings to be sent to the remote system in advance of any SEND", " or GET commands. By default these are \"kermit -ir\", \"kermit -r\", and", " \"kermit -x\". Use { braces } around any command that contains spaces.", " Example:", " ", " set proto kermit {kermit -Yir} {kermit -YTr} {kermit -Yx}", #endif /* CK_XYZ */ " ", " SHOW PROTOCOL displays the current settings.", ""}; static char *hmxxbye = "Syntax: BYE\n\ Shut down and log out a remote Kermit server"; #ifdef CK_PERMS #ifdef UNIX static char *hmxxchmod[] = { "Syntax: CHMOD [ switches ] code filespec", " UNIX only. Changes permissions of the given file(s) to the given code,", " which must be an octal number such as 664 or 775. Optional switches:", " ", " /FILES Only change permissions of regular files.", " /DIRECTORIES Only change permissions of directory files.", " /TYPE:BINARY Only change permissions of binary files.", " /TYPE:TEXT Only change permissions of text files.", " /DOTFILES Include files whose names begin with dot (.).", " /RECURSIVE Change permissions in subdirectories too.", " /LIST List each file (synonym: /VERBOSE).", " /NOLIST Operate silently (synonym: /QUIET).", " /PAGE When listing, pause at end of each screen (implies /LIST).", " /NOPAGE When listing, don't pause at end of each screen.", " /SIMULATE Show what would be done but don't actually do it.", "" }; #endif /* UNIX */ #endif /* CK_PERMS */ #ifndef NOSPL #ifndef NOSEXP static char *hmxxsexp[] = { "Syntax: (operation operand [ operand [ ... ] ])", " ", " C-Kermit includes a simple LISP-like S-Expression parser operating on", " numbers only. An S-Expression is always enclosed in parentheses. The", " parentheses can contain (a) a number, (b) a variable, (c) a function that", " returns a number, or (d) an operator followed by one or more operands.", " Operands can be any of (a) through (c) or an S-Expression. Numbers can be", " integers or floating-point. Any operand that is not a number and does not", " start with backslash (\\) is treated as a Kermit macro name. Operators:", " ", " Operator Action Example Value", " EVAL (.) Returns the contained value (6) 6", " QUOTE (') Inhibits evaluation of following value (quote a) a", " SETQ Assigns a value to a global variable (setq a 2) 2", " LET Assigns a value to a local variable (let b -1.3) -1.3", " + Adds all operands (1 or more) (+ a b) 0.7", " - Subtracts all operands (1 or more) (- 9 5 2 1) 1", " * Multiplies all operands (1 or more) (* a (+ b 1) 3) -1.8", " / Divides all operands (1 or more) (/ b a 2) -0.325", " ^ Raise given number to given power (^ 3 2) 9", " ++ Increments a variable (++ a 1.2) 3.2", " -- Decrements a variable (-- a) 1", " ABS Absolute value of 1 operand (abs (* a b 3)) 7.8", " MAX Maximum of all operands (1 or more) (max 1 2 3 4) 4", " MIN Minimum of all operands (1 or more) (min 1 2 3 4) 1", " MOD Modulus of all operands (1 or more) (mod 7 4 2) 1", " TRUNCATE Integer part of floating-point operand (truncate 1.333) 1", " CEILING Ceiling of floating-point operand (ceiling 1.25) 2", " FLOOR Floor of floating-point operand (floor 1.25) 1", " ROUND Operand rounded to nearest integer (round 1.75) 2", " SQRT Square root of 1 operand (sqrt 2) 1.414..", " EXP e (2.71828..) to the given power (exp -1) 0.367..", " SIN Sine of angle expressed in radians (sin (/ pi 2)) 1.0", " COS Cosine of given number (cos pi) -1.0", " TAN Tangent of given number (tan pi) 0.0", " LOG Natural log (base e) of given number (log 2.7183) 1.000..", " LOG10 Log base 10 of given number (log10 1000) 3.0", " ", "Predicate operators return 0 if false, 1 if true, and if it is the outermost", "operator, sets SUCCESS or FAILURE accordingly:", " ", " < Operands in strictly descending order (< 6 5 4 3 2 1) 1", " <= Operands in descending order (<= 6 6 5 4 3 2) 1", " != Operands are not equal (!= 1 1 1.0) 0", " = (==) All operands are equal (= 3 3 3 3) 1", " > Operands in strictly ascending order (> 1 2 3 4 5 6) 1", " >= Operands in ascending order (> 1 1 2 3 4 5) 1", " AND (&&) Operands are all true (and 1 1 1 1 0) 0", " OR (||) At least one operand is true (or 1 1 1 1 0) 1", " XOR Logical Exclusive OR (xor 3 1) 0", " NOT (!) Reverses truth value of operand (not 3) 0", " ", "Bit-oriented operators:", " ", " & Bitwise AND (& 7 2) 2", " | Bitwise OR (| 1 2 3 4) 7", " # Bitwise Exclusive OR (# 3 1) 2", " ~ Reverses all bits (~ 3) -4", " ", "Operators that work on truth values:", " ", " IF Conditional evaluation (if (1) 2 3) 2", " ", "Operators can also be names of Kermit macros that return either numeric", "values or no value at all.", " ", "Built-in constants are:", " ", " t True (1)", " nil False (empty)", " pi The value of Pi (3.1415926...)", " ", "If SET SEXPRESSION TRUNCATE-ALL-RESULTS is ON, all results are trunctated", "to integer values by discarding any fractional part. Otherwise results", "are floating-point if there fractional part and integer otherwise.", "If SET SEXPRESSION ECHO-RESULT is AUTO (the default), the value of the", "S-Expression is printed if the S-Expression is given at top level; if ON,", "it is printed at any level; if OFF it is not printed. At all levels, the", "variable \\v(sexpression) is set to the most recent S-Expression, and", "\\v(svalue) is set to its value. You can use the \\fsexpresssion() function", "to evaluate an S-Expression anywhere in a Kermit command.", "" }; #endif /* NOSEXP */ #endif /* NOSPL */ static char *hmxxgrep[] = { #ifdef UNIXOROSK "Syntax: GREP [ options ] pattern filespec", #else "Syntax: FIND [ options ] pattern filespec", #endif /* UNIXOROSK */ " Searches through the given file or files for the given character string", " or pattern. In the normal case, all lines containing any text that matches" , " the pattern are printed. Pattern syntax is as described in HELP PATTERNS", " except that '*' is implied at the beginning unless the pattern starts with", " '^' and also at the end unless the pattern ends with '$'. Therefore,", " \"grep something *.txt\" lists all lines in all *.txt files that contain", " the word \"something\", but \"grep ^something *.txt\" lists only the lines", " that START with \"something\". The command succeeds if any of the given", " files contained any lines that match the pattern, otherwise it fails.", #ifdef UNIXOROSK " Synonym: FIND.", #else " Synonym: GREP.", #endif /* UNIXOROSK */ " ", "File selection options:", " /NOBACKUPFILES", " Excludes backup files (like oofa.txt.~3~) from the search.", " /DOTFILES", " Includes files whose names start with dot (.) in the search.", " /NODOTFILES", " Excludes files whose names start with dot (.) from the search.", #ifdef RECURSIVE " /RECURSIVE", " Searches through files in subdirectories too.", #endif /* RECURSIVE */ " /TYPE:TEXT", " Search only text files (requires FILE SCAN ON).", " /TYPE:BINARY", " Search only binary files (requires FILE SCAN ON).", " ", "Pattern-matching options:", " /NOCASE", " Ignores case of letters (ASCII only) when comparing.", " /NOMATCH", " Searches for lines that do NOT match the pattern.", " /EXCEPT:pattern", " Exclude lines that match the main pattern that also match this pattern.", " ", "Display options:", " /COUNT:variable-name", " For each file, prints only the filename and a count of matching lines", " and assigns the total match count to the variable, if one is given.", " /NAMEONLY", " Prints the name of each file that contains at least one matching line,", " one name per line, rather than showing each matching line.", " /NOLIST", " Doesn't print anything (but sets SUCCESS or FAILURE appropriately).", " /LINENUMBERS", " Precedes each file line by its line number within the file.", " /PAGE", " Pauses after each screenful.", " /NOPAGE", " Doesn't pause after each screenful.", " /OUTPUT:name", " Sends results to the given file. If this switch is omitted, the", " results appear on your screen. This switch overrides any express or", " implied /PAGE switch.", ""}; static char *hmxxdir[] = { #ifdef DOMYDIR "Syntax: DIRECTORY [ switches ] [ filespec [ filespec [ ... ] ] ]", #ifdef LOCUS " If LOCUS is REMOTE or LOCUS is AUTO and you have an FTP connection,", " this command is equivalent to REMOTE DIRECTORY (RDIR). Otherwise:", " ", #endif /* LOCUS */ " Lists local files. The filespec may be a filename, possibly containing", " wildcard characters, or a directory name. If no filespec is given, all", " files in the current directory are listed. If a directory name is given,", " all the files in it are listed. Multiple filespecs can be given.", " Optional switches:", " ", " /BRIEF List filenames only.", #ifdef CK_PERMS " /VERBOSE + Also list permissions, size, and date.", #else " /VERBOSE + Also list date and size.", #endif /* CK_PERMS */ " /FILES Show files but not directories.", " /DIRECTORIES Show directories but not files.", " /ALL + Show both files and directories.", " /ARRAY:&a Store file list in specified array (e.g. \\%a[]).", " /PAGE Pause after each screenful.", " /NOPAGE Don't pause after each screenful.", " /TOP:n Only show the top n lines of the directory listing.", #ifdef UNIXOROSK " /DOTFILES Include files whose names start with dot (period).", " /NODOTFILES + Don't include files whose names start with dot.", " /FOLLOWLINKS Follow symbolic links.", " /NOFOLLOWLINKS + Don't follow symbolic links.", " /NOLINKS Don't list symbolic links at all.", " /BACKUP + Include backup files (names end with .~n~).", " /NOBACKUPFILES Don't include backup files.", #endif /* UNIXOROSK */ " /OUTPUT:file Store directory listing in the given file.", " /HEADING Include heading and summary.", " /NOHEADING + Don't include heading or summary.", " /COUNT:var Put the number of matching files in the given variable.", " /SUMMARY Print only count and total size of matching files.", " /XFERMODE Show pattern-based transfer mode (T=Text, B=Binary).", " /TYPE: Show only files of the specified type (text or binary).", " /MESSAGE:text Add brief message to each listing line.", " /NOMESSAGE + Don't add message to each listing line.", " /NOXFERMODE + Don't show pattern-based transfer mode", " /ISODATE + In verbose listings, show date in ISO 8061 format.", " /ENGLISHDATE In verbose listings, show date in \"English\" format.", #ifdef RECURSIVE " /RECURSIVE Descend through subdirectories.", " /NORECURSIVE + Don't descend through subdirectories.", #endif /* RECURSIVE */ " /SORT:key Sort by key, NAME, DATE, or SIZE; default key is NAME.", " /NOSORT + Don't sort.", " /ASCENDING + If sorting, sort in ascending order.", " /REVERSE If sorting, sort in reverse order.", " ", "Factory defaults are marked with +. Default for paging depends on SET", "COMMAND MORE-PROMPTING. Use SET OPTIONS DIRECTORY [ switches ] to change", "defaults; use SHOW OPTIONS to display customized defaults. Also see", "WDIRECTORY.", #else "Syntax: DIRECTORY [ filespec ]", " Lists the specified file or files. If no filespec is given, all files", " in the current directory are listed.", #endif /* DOMYDIR */ ""}; static char *hmxxtouc[] = { "Syntax: TOUCH [ switches ] filespec", " Updates the modification time of the given file or files to the current", " date and time. If the filespec is the name of a single file that does not", " exist, the file is created. The optional switches are the same as for", " the DIRECTORY command. TOUCH operates silently unless the /VERBOSE", " switch is given, in which case it lists each file it affects.", ""}; #ifndef NOSPL static char *hmxxkcd[] = { "Syntax: KCD symbolic-directory-name", " Kermit Change Directory: Like CD (q.v.) but (a) always acts locally, and", " (b) takes a symbolic directory name rather than an actual directory name.", " The symbolic names correspond to Kermit's directory-valued built-in", " variables, such as \\v(download), \\v(exedir), and so on. Here's the list:" , " ", #ifdef NT " appdata Your personal Kermit 95 application data directory", " common Kermit 95's application data directory for all users", " desktop Your Windows desktop", #endif /* NT */ " download Your download directory (if any)", #ifdef OS2ORUNIX " exedir The directory where the Kermit executable resides", #endif /* OS2ORUNIX */ " home Your home, login, or default directory", " inidir The directory where Kermit's initialization was found", #ifdef UNIX " lockdir The UNIX UUCP lockfile directory on this computer", #endif /* UNIX */ #ifdef NT " personal Your \"My Documents\" directory", #endif /* NT */ " startup Your current directory at the time Kermit started", " textdir The directory where Kermit text files reside, if any", " tmpdir Your temporary directory", " ", " Also see CD, SET FILE DOWNLOAD, SET TEMP-DIRECTORY.", "" }; #endif /* NOSPL */ static char *hmxxcwd[] = { #ifdef LOCUS " If LOCUS is REMOTE or LOCUS is AUTO and you have an FTP connection,", " this command is equivalent to REMOTE CD (RCD). Otherwise:", " ", #endif /* LOCUS */ #ifdef vms "Syntax: CD [ directory or device:directory ]", " Change Working Directory. Equivalent to VMS SET DEFAULT command.", #else #ifdef datageneral "Change Working Directory, equivalent to AOS/VS 'dir' command.", #else #ifdef OS2 "Syntax: CD [ disk or directory name ]", " Change Disk or Directory. If a disk or directory name is not specified,", " your current directory becomes the one specified by HOME environment", " variable, if any. A disk letter must be followed by a colon.", #else "Syntax: CD [ directory name ]", " Change Directory. Changes your current, working, default directory to the", " one given, so that future non-absolute filename references are relative to", " this directory. If the directory name is omitted, your home (login)", " directory is supplied.", #endif /* OS2 */ #endif /* datageneral */ #endif /* vms */ " C-Kermit's default prompt shows your current directory.", " Synonyms: LCD, CWD.", #ifdef LOCUS " Also see: SET LOCUS, PWD, CDUP, BACK, REMOTE CD (RCD), SET CD, SET PROMPT.", #else " Also see: PWD, CDUP, BACK, REMOTE CD (RCD), SET CD, SET PROMPT.", #endif /* LOCUS */ #ifndef NOSPL " And see: HELP KCD.", #endif /* NOSPL */ " Relevant environment variables: CDPATH, HOME.", ""}; static char *hmxxdel[] = { "Syntax: DELETE [ switches... ] filespec", #ifdef LOCUS " If LOCUS is REMOTE or LOCUS is AUTO and you have an FTP connection,", " this command is equivalent to REMOTE DELETE (RDELETE). Otherwise:", " ", #endif /* LOCUS */ " Deletes a file or files on the computer where C-Kermit is running.", " The filespec may denote a single file or can include wildcard characters", " to match multiple files. RM is a synonym for DELETE. Switches include:", " ", "/AFTER:date-time", #ifdef VMS " Specifies that only those files created after the given date-time are", #else " Specifies that only those files modified after the given date-time are", #endif /* VMS */ " to be deleted. HELP DATE for info about date-time formats.", " ", "/BEFORE:date-time", #ifdef VMS " Specifies that only those files modified before the given date-time", #else " Specifies that only those files modified before the given date-time", #endif /* VMS */ " are to be deleted.", " ", "/NOT-AFTER:date-time", #ifdef VMS " Specifies that only those files modified at or before the given date-time", #else " Specifies that only those files modified at or before the given date-time", #endif /* VMS */ " are to be deleted.", " ", "/NOT-BEFORE:date-time", #ifdef VMS " Specifies that only those files modified at or after the given date-time", #else " Specifies that only those files modified at or after the given date-time", #endif /* VMS */ " are to be deleted.", " ", "/LARGER-THAN:number", " Specifies that only those files longer than the given number of bytes are", " to be deleted.", " ", "/SMALLER-THAN:number", " Specifies that only those files smaller than the given number of bytes are", " to be sent.", " ", "/EXCEPT:pattern", " Specifies that any files whose names match the pattern, which can be a", " regular filename or may contain wildcards, are not to be deleted. To", " specify multiple patterns (up to 8), use outer braces around the group", " and inner braces around each pattern:", " ", " /EXCEPT:{{pattern1}{pattern2}...}", " ", #ifdef UNIXOROSK "/DOTFILES", " Include (delete) files whose names begin with \".\".", " ", "/NODOTFILES", " Skip (don't delete) files whose names begin with \".\".", " ", #endif /* UNIXOROSK */ "/TYPE:TEXT", " Delete only regular text files (requires FILE SCAN ON)", " ", "/TYPE:BINARY", " Delete only regular binary files (requires FILE SCAN ON)", " ", "/DIRECTORIES", " Include directories. If this switch is not given, only regular files", " are deleted. If it is given, Kermit attempts to delete any directories", " that match the given file specification, which succeeds only if the", " directory is empty.", " ", #ifdef RECURSIVE "/RECURSIVE", " The DELETE command applies to the entire directory tree rooted in the", " current or specified directory. When the /DIRECTORIES switch is also", " given, Kermit deletes all the (matching) files in each directory before", " attempting to delete the directory itself.", " ", #endif /* RECURSIVE */ #ifdef UNIX #ifdef RECURSIVE "/ALL", " This is a shortcut for /RECURSIVE /DIRECTORIES /DOTFILES.", #else "/ALL", " This is a shortcut for /DIRECTORIES /DOTFILES.", #endif /* RECURSIVE */ #else /* !UNIX */ #ifdef RECURSIVE "/ALL", " This is a shortcut for /RECURSIVE /DIRECTORIES.", #else "/ALL", " This is a synonym for /DIRECTORIES.", #endif /* RECURSIVE */ #endif /* UNIX */ " ", "/LIST", " List each file and tell whether it was deleted. Synonyms: /LOG, /VERBOSE.", " ", "/NOLIST", " Don't list files while deleting. Synonyms: /NOLOG, /QUIET.", " ", "/HEADING", " Print heading and summary information.", " ", "/NOHEADING", " Don't print heading and summary information.", " ", "/SUMMARY", " Like /HEADING /NOLIST, but only prints the summary line.", " ", "/PAGE", " If listing, pause after each screenful.", " ", "/NOPAGE", " Don't pause after each screenful.", " ", "/ASK", " Interactively ask permission to delete each file. Reply Yes or OK to", " delete it, No not to delete it, Quit to cancel the DELETE command, and", " Go to go ahead and delete all the rest of the files without asking.", " ", "/NOASK", " Delete files without asking permission.", " ", "/SIMULATE", " Preview files selected for deletion without actually deleting them.", " Implies /LIST.", " ", "Use SET OPTIONS DELETE to make selected switches effective for every DELETE", "command \ unless you override them; use SHOW OPTIONS to see selections currently", #ifdef LOCUS "in effect. Also see HELP SET LOCUS, HELP PURGE, HELP WILDCARD.", #else "in effect. Also see HELP PURGE, HELP WILDCARD.", #endif /* LOCUS */ ""}; #ifndef NOHTTP static char *hmxxhttp[] = { "Syntax:", #ifdef CK_SSL "HTTP [ ] OPEN [{ /SSL, /TLS }] ", #else "HTTP [ ] OPEN ", #endif /*CK_SSL */ " Instructs Kermit to open a new connection for HTTP communication with", " the specified host on the specified port. The default port is \"http\".", #ifdef CK_SSL " If /SSL or /TLS are specified or if the service is \"https\" or port 443,", " a secure connection will be established using the current authentication", " settings. See HELP SET AUTH for details.", #endif /* CK_SSL */ " If are specified, they are applied to all subsequent HTTP", " actions (GET, PUT, ...) until an HTTP CLOSE command is executed.", " A URL can be included in place of the hostname and service or port.", " ", "HTTP CLOSE", " Instructs Kermit to close any open HTTP connection and clear any saved", " switch values.", " ", "HTTP [ ] CONNECT [:]", " Instructs the server to establish a connection with the specified host", " and to redirect all data transmitted between Kermit and the host for the", " life of the connection.", " ", "HTTP [ ] GET [ ]", " Retrieves the named file on the currently open HTTP connection. The", " default local filename is the same as the remote filename, but with any", " path stripped. If you want the file to be displayed on the screen instead", " of stored on disk, include the /TOSCREEN switch and omit the local", " filename. If you give a URL instead of a remote filename, this commands", " opens the connection, GETs the file, and closes the connection; the same", " is true for the remaining HTTP commands for which you can specify a", " remote filename, directory name, or path.", " ", "HTTP [ ] HEAD [ ]", " Like GET except without actually getting the file; instead it gets only", " the headers, storing them into the given file (if a local filename is", " specified), one line per header item as shown in the /ARRAY: switch", " description.", " ", "HTTP [ ] INDEX [ ]", " Retrieves the file listing for the given server directory.", " NOTE: This command is not supported by most Web servers, and even when", " the server understand it, there is no stardard response format.", " ", "HTTP [ ] POST [ /MIME-TYPE: ] ", " [ ]", " Used to send a response as if it were sent from a form. The data to be", " posted must be read from a file.", " ", "HTTP [ ] PUT [ /MIME-TYPE: ] ", " [ ]", " Uploads the given local file to server file. If the remote filename is", " omitted, the local name is used, but with any path stripped.", " ", "HTTP [ ] DELETE ", " Instructs the server to delete the specified filename.", " ", "where are:", "/AGENT:", " Identifies the client to the server; \"C-Kermit\" or \"Kermit-95\"", " by default.", " ", "/HEADER:", " Used for specifying any optional headers. A list of headers is provided", " using braces for grouping:", " ", " /HEADER:{{:}{:}...}", " ", " For a listing of valid value and formats see RFC 1945:", " \"Hypertext Transfer Protocol -- HTTP/1.0\". A maximum of eight headers", " may be specified.", " ", "/TOSCREEN", " Display server responses on the screen.", " ", "/USER:", " In case a page requires a username for access.", " ", "/PASSWORD:", " In case a page requires a password for access.", " ", "/ARRAY:", " Tells Kermit to store the response headers in the given array, one line", " per element. The array need not be declared in advance. Example:", " ", " http /array:c get kermit/index.html", " show array c", " Dimension = 9", " 1. Date: Fri, 26 Nov 1999 23:12:22 GMT", " 2. Server: Apache/1.3.4 (Unix)", " 3. Last-Modified: Mon, 06 Sep 1999 22:35:58 GMT", " 4. ETag: \"bc049-f72-37d441ce\"", " 5. Accept-Ranges: bytes", " 6. Content-Length: 3954", " 7. Connection: close ", " 8. Content-Type: text/html", " ", "As you can see, the header lines are like MIME e-mail header lines:", "identifier, colon, value. The /ARRAY switch is the only method available", "to a script to process the server responses for a POST or PUT command.", " ", "" }; #endif /* NOHTTP */ #ifdef CK_KERBEROS static char *hmxxauth[] = { "Syntax:", "AUTHENTICATE { KERBEROS4, KERBEROS5 [ switches ] } [ switches ]", " Obtains or destroys Kerberos tickets and lists information about them.", " Actions are INITIALIZE, DESTROY, and LIST-CREDENTIALS. KERBEROS4 can be", " abbreviated K4 or KRB4; KERBEROS5 can be abbreviated K5 or KRB5. Use ? to", " see which keywords, switches, or other quantities are valid at each point", " in the command.", " ", " The actions are INITIALIZE, DESTROY, and LIST-CREDENTIALS:", " ", " AUTH { K4, K5 } { INITIALIZE [switches], DESTROY,", " LIST-CREDENTIALS [switches] }", " ", " The INITIALIZE action is the most complex, and its format is different", " for Kerberos 4 and Kerberos 5. The format for Kerberos 4 is:", " ", " AUTH K4 INITIALIZE [ /INSTANCE: /LIFETIME: -", " /PASSWORD: /PREAUTH /REALM: ]", " ", " All switches are optional. Kerberos 4 INITIALIZE switches are:", " ", " /INSTANCE:", " Allows an Instance (such as a hostname) to be specified.", " ", " /LIFETIME:", " Specifies the requested lifetime in minutes for the ticket. If no", " lifetime is specified, 600 minutes is used. If the lifetime is greater", " than the maximum supported by the ticket granting service, the resulting", " lifetime is shortened accordingly.", " ", " /NOT-PREAUTH", " Instructs Kermit to send a ticket getting ticket (TGT) request to the", " KDC without any preauthentication data.", " ", " /PASSWORD:", " Allows a password to be included on the command line or in a script", " file. If no /PASSWORD switch is included, you are prompted on a separate" , " line. The password switch is provided on a use-at-your-own-risk basis", " for use in automated scripts. WARNING: Passwords should not be stored in" , " files.", " ", " /PREAUTH", " Instructs Kermit to send a preauthenticated Ticket-Getting Ticket (TGT)", " request to the KDC instead of a plaintext request. The default when", " supported by the Kerberos libraries.", " ", " /REALM:", " Allows a realm to be specified (overriding the default realm).", " ", " ", " Your identity in the given or default Kerberos realm, of the form:", " userid[.instance[.instance]]@[realm] ", " Can be omitted if it is the same as your username or SET LOGIN USERID", " value on the client system.", " ", " The format for Kerberos 5 is as follows:", " ", " AUTH K5 [ /CACHE: ] { INITIALIZE [ switches ], DESTROY,", " LIST-CREDENTIALS ...}", " ", "The INITIALIZE command for Kerberos 5 can include a number of switches;", "all are optional:", " ", "AUTH K5 [ /CACHE: ] INITITIALIZE [ /ADDRESSES:", " /FORWARDABLE /KERBEROS4 /LIFETIME: /PASSWORD:", " /POSTDATE: /PROXIABLE /REALM: /RENEW /RENEWABLE:", " /SERVICE: /VALIDATE ]", " ", " All Kerberos 5 INITIALIZE switches are optional:", " ", " /ADDRESSES:{list of ip-addresses}", " Specifies a list of IP addresses that should be placed in the Ticket", " Getting Ticket in addition to the local machine addresses.", " ", " /FORWARDABLE", " Requests forwardable tickets.", " ", " /INSTANCE:", " Allows an Instance (such as a hostname) to be specified.", " ", " /KERBEROS4", " Instructs Kermit to get Kerberos 4 tickets in addition to Kerberos 5", " tickets. If Kerberos 5 tickets are not supported by the server, a", " mild warning is printed and Kerberos 4 tickets are requested.", " ", " /LIFETIME:", " Specifies the requested lifetime in minutes for the ticket. If no", " lifetime is specified, 600 minutes is used. If the lifetime is greater", " than the maximum supported by the ticket granting service, the resulting", " lifetime is shortened.", " ", " /NO-KERBEROS4", " Instructs Kermit to not attempt to retrieve Kerberos 4 credentials.", " ", " /NOT-FORWARDABLE", " Requests non-forwardable tickets.", " ", " /NOT-PROXIABLE", " Requests non-proxiable tickets.", " ", " /PASSWORD:", " Allows a password to be included on the command line or in a script", " file. If no /PASSWORD switch is included, you are prompted on a separate" , " line. The password switch is provided on a use-at-your-own-risk basis", " for use in automated scripts. WARNING: Passwords should not be stored in" , " files.", " ", " /POSTDATE:", " Requests a postdated ticket, valid starting at . Postdated", " tickets are issued with the invalid flag set, and need to be fed back to", " the KDC before use with the /VALIDATE switch. Type HELP DATE for info", " on date-time formats.", " ", " /PROXIABLE", " Requests proxiable tickets.", " ", " /REALM:", " Allows an alternative realm to be specified.", " ", " /RENEW", " Requests renewal of a renewable Ticket-Granting Ticket. Note that ", " an expired ticket cannot be renewed even if it is within its renewable ", " lifetime.", " ", " /RENEWABLE:", " Requests renewable tickets, with a total lifetime of minutes.", " ", " /SERVICE:", " Allows a service other than the ticket granting service to be specified.", " ", " /VALIDATE", " Requests that the Ticket Granting Ticket in the cache (with the invalid", " flag set) be passed to the KDC for validation. If the ticket is within", " its requested time range, the cache is replaced with the validated", " ticket.", " ", " ", " Your identity in the given or default Kerberos realm, of the form:", " userid[/instance][@realm] ", " Can be omitted if it is the same as your username or SET LOGIN USERID", " value on the client system.", " ", " Note: Kerberos 5 always attempts to retrieve a Ticket-Getting Ticket (TGT)", " using the preauthenticated TGT request.", " ", " AUTHORIZE K5 LIST-CREDENTIALS [ /ADDRESSES /FLAGS /ENCRYPTION ]", " ", " Shows start time, expiration time, service or principal name, plus", " the following additional information depending the switches:", " ", " /ADDRESSES displays the hostnames and/or IP addresses embedded within", " the tickets.", " ", " /FLAGS provides the following information (if applicable) for each ticket:", " F - Ticket is Forwardable", " f - Ticket was Forwarded", " P - Ticket is Proxiable", " p - Ticket is a Proxy", " D - Ticket may be Postdated", " d - Ticket has been Postdated", " i - Ticket is Invalid", " R - Ticket is Renewable", " I - Ticket is the Initial Ticket", " H - Ticket has been authenticated by Hardware", " A - Ticket has been Pre-authenticated", " ", " /ENCRYPTION displays the encryption used by each ticket (if applicable):", " DES-CBC-CRC", " DES-CBC-MD4", " DES-CBC-MD5", " DES3-CBC-SHA", "" }; #endif /* CK_KERBEROS */ #ifndef NOCSETS static char *hmxxassoc[] = { "ASSOCIATE FILE-CHARACTER-SET ", " Tells C-Kermit that whenever the given file-character set is selected, and", " SEND CHARACTER-SET (q.v.) is AUTOMATIC, the given transfer character-set", " is selected automatically.", " ", "ASSOCIATE XFER-CHARACTER-SET ", " Tells C-Kermit that whenever the given transfer-character set is selected,", " either by command or by an announcer attached to an incoming text file,", " and SEND CHARACTER-SET is AUTOMATIC, the specified file character-set is", " to be selected automatically. Synonym: ASSOCIATE TRANSFER-CHARACTER-SET.", " ", "Use SHOW ASSOCIATIONS to list the current character-set associations, and", "SHOW CHARACTER-SETS to list the current settings.", "" }; #endif /* NOCSETS */ static char *hmxxpat[] = { "A \"pattern\" is notation used in a search string when searching through", "text. C-Kermit uses three kinds of patterns: floating patterns, anchored", "patterns, and wildcards. Wildcards are anchored patterns that are used to", "match file names; type HELP WILDCARD to learn about them.", " ", "In a pattern, certain characters are special:", " ", "* Matches any sequence of zero or more characters. For example, \"k*t\"", " matches all strings that start with \"k\" and end with \"t\" including", " \"kt\", \"kit\", \"knight\", or \"kermit\".", " ", #ifdef VMS "% Matches any single character. For example, \"k%%%%t\" matches all strings", #else "? Matches any single character. For example, \"k????t\" matches all strings", #endif /* VMS */ " that are exactly 6 characters long and start with \"k\" and end with", #ifdef VMS " with \"t\".", #else " with \"t\". When typing commands at the prompt, you must precede any", " question mark to be used for matching by a backslash (\\) to override the", " normal function of question mark in interactive commands, which is to", " provide menus and file lists.", #endif /* VMS */ " ", #ifdef OS2ORUNIX #ifdef CKREGEX "[abc]", " Square brackets enclosing a list of characters matches any character in", " the list. Example: h[aou]t matches hat, hot, and hut.", " ", "[a-z]", " Square brackets enclosing a range of characters matches any character in", " the range; a hyphen (-) separates the low and high elements of the range.", " For example, [a-z] matches any character from a to z.", " ", "[acdm-z]", " Lists and ranges may be combined. This example matches a, c, d, or any", " letter from m through z.", " ", "{string1,string2,...}", " Braces enclose a list of strings to be matched. For example:", " ker{mit,nel,beros} matches kermit, kernel, and kerberos. The strings", " may themselves contain *, ?, [abc], [a-z], or other lists of strings.", #endif /* CKREGEX */ #endif /* OS2ORUNIX */ #ifndef NOSPL " ", "To force a special pattern character to be taken literally, precede it with", "a backslash, e.g. [a\\-z] matches a, hyphen, or z rather than a through z.", " ", "A floating pattern can also include the following special characters:", " ", "^ (First character of pattern) Anchors the pattern at the beginning.", "$ (Last character of pattern) Anchors the pattern at the end.", " ", "If a floating pattern does not start with \"^\", the pattern can match", "anywhere in the string instead of only at the beginning; in other words, a", "leading \"*\" is assumed. Similarly, if the pattern doesn't end with \"$\",", "a trailing \"*\" is assumed.", " ", "The following commands and functions use floating patterns:", " GREP [ ] ", " TYPE /MATCH: ", " \\farraylook(,)", " \\fsearch(,[,])", " \\frsearch(,[,])", " The /EXCEPT: clause in SEND, GET, DELETE, etc.", " ", "Example:", " \\fsearch(abc,xxabcxxx) succeeds because xxabcxx contains abc.", " \\fsearch(^abc,xxabcxx) fails because xxabcxx does not start with abc.", " ", "All other commands and functions use anchored patterns, meaning that ^ and $", "are not treated specially, and * is not assumed at the beginning or end of", "the pattern. This is true mainly of filename patterns (wildcards), since", "you would not want a command like \"delete x\" to delete all files whose", "names contained \"x\"!", " ", "You can use anchored patterns not only in filenames, but also in SWITCH", "case labels, in the INPUT and MINPUT commands, and in file binary- and", "text-patterns for filenames. The IF MATCH pattern is also anchored.", #endif /* NOSPL */ "" }; static char *hmxxwild[] = { "A \"wildcard\" is a notation used in a filename to match multiple files.", "For example, in \"send *.txt\" the asterisk is a wildcard. Kermit commands", "that accept filenames also accepts wildcards, except commands that are", "allowed to operate on only one file, such as TRANSMIT.", "This version of Kermit accepts the following wildcards:", " ", "* Matches any sequence of zero or more characters. For example, \"ck*.c\"", " matches all files whose names start with \"ck\" and end with \".c\"", " including \"ck.c\".", " ", #ifdef VMS "% Matches any single character. For example, \"ck%.c\" matches all files", #else "? Matches any single character. For example, \"ck?.c\" matches all files", #endif /* VMS */ " whose names are exactly 5 characters long and start with \"ck\" and end", #ifdef VMS " with \".c\".", #else " with \".c\". When typing commands at the prompt, you must precede any", " question mark to be used for matching by a backslash (\\) to override the", " normal function of question mark in interactive commands, which is to", " provide menus and file lists. You don't, however, need to quote filename", " question marks in command files (script programs).", #endif /* VMS */ " ", #ifdef OS2ORUNIX #ifdef CKREGEX "[abc]", " Square brackets enclosing a list of characters matches any character in", " the list. Example: ckuusr.[ch] matches ckuusr.c and ckuusr.h.", " ", "[a-z]", " Square brackets enclosing a range of characters matches any character in", " the range; a hyphen (-) separates the low and high elements of the range.", " For example, [a-z] matches any character from a to z.", " ", "[acdm-z]", " Lists and ranges may be combined. This example matches a, c, d, or any", " letter from m through z.", " ", "{string1,string2,...}", " Braces enclose a list of strings to be matched. For example:", " ck{ufio,vcon,cmai}.c matches ckufio.c, ckvcon.c, or ckcmai.c. The strings", " may themselves contain *, ?, [abc], [a-z], or other lists of strings.", #endif /* CKREGEX */ #endif /* OS2ORUNIX */ " ", "To force a special pattern character to be taken literally, precede it with", "a backslash, e.g. [a\\-z] matches a, hyphen, or z rather than a through z.", "Or tell Kermit to SET WILDCARD-EXPANSION OFF before entering or referring", "to the filename.", " ", #ifndef NOSPL "Similar notation can be used in general-purpose string matching. Type HELP", "PATTERNS for details. Also see HELP SET MATCH.", #endif /* NOSPL */ "" }; #ifndef NOXFER static char *hmxxfast[] = { "FAST, CAUTIOUS, and ROBUST are predefined macros that set several", "file-transfer parameters at once to achieve the desired file-transfer goal.", "FAST chooses a large packet size, a large window size, and a fair amount of", "control-character unprefixing at the risk of possible failure on some", "connections. FAST is the default tuning in C-Kermit 7.0 and later. In case", "FAST file transfers fail for you on a particular connection, try CAUTIOUS.", "If that fails too, try ROBUST. You can also change the definitions of each", "macro with the DEFINE command. To see the current definitions, type", "\"show macro fast\", \"show macro cautious\", or \"show macro robust\".", "" }; #endif /* NOXFER */ #ifdef VMS static char * hmxxpurge[] = { "Syntax: PURGE [ switches ] [ filespec ]", " Runs the DCL PURGE command. Switches and filespec are not parsed or", " verified by Kermit, but passed directly to DCL.", "" }; #else #ifdef CKPURGE static char * hmxxpurge[] = { "Syntax: PURGE [ switches ] [ filespec ]", " Deletes backup files; that is, files whose names end in \".~n~\", where", " n is a number. PURGE by itself deletes all backup files in the current", " directory. Switches:", " ", "/AFTER:date-time", #ifdef VMS " Specifies that only those files created after the given date-time are", #else " Specifies that only those files modified after the given date-time are", #endif /* VMS */ " to be purged. HELP DATE for info about date-time formats.", " ", "/BEFORE:date-time", #ifdef VMS " Specifies that only those files modified before the given date-time", #else " Specifies that only those files modified before the given date-time", #endif /* VMS */ " are to be purged.", " ", "/NOT-AFTER:date-time", #ifdef VMS " Specifies that only those files modified at or before the given date-time", #else " Specifies that only those files modified at or before the given date-time", #endif /* VMS */ " are to be purged.", " ", "/NOT-BEFORE:date-time", #ifdef VMS " Specifies that only those files modified at or after the given date-time", #else " Specifies that only those files modified at or after the given date-time", #endif /* VMS */ " are to be purged.", " ", "/LARGER-THAN:number", " Specifies that only those files longer than the given number of bytes are", " to be purged.", " ", "/SMALLER-THAN:number", " Specifies that only those files smaller than the given number of bytes are", " to be purged.", " ", "/EXCEPT:pattern", " Specifies that any files whose names match the pattern, which can be a", " regular filename or may contain wildcards, are not to be purged. To", " specify multiple patterns (up to 8), use outer braces around the group", " and inner braces around each pattern:", " ", " /EXCEPT:{{pattern1}{pattern2}...}", " ", #ifdef UNIXOROSK "/DOTFILES", " Include (purge) files whose names begin with \".\".", " ", "/NODOTFILES", " Skip (don't purge) files whose names begin with \".\".", " ", #endif /* UNIXOROSK */ #ifdef RECURSIVE "/RECURSIVE", " Descends through the current or specified directory tree.", " ", #endif /* RECURSIVE */ "/KEEP:n", " Retain the 'n' most recent (highest-numbered) backup files for each file.", " By default, none are kept. If /KEEP is given without a number, 1 is used.", " ", "/LIST", " Display each file as it is processed and say whether it is purged or kept.", " Synonyms: /LOG, /VERBOSE.", " ", "/NOLIST", " The PURGE command should operate silently (default).", " Synonyms: /NOLOG, /QUIET.", " ", "/HEADING", " Print heading and summary information.", " ", "/NOHEADING", " Don't print heading and summary information.", " ", "/PAGE", " When /LIST is in effect, pause at the end of each screenful, even if", " COMMAND MORE-PROMPTING is OFF.", " ", "/NOPAGE", " Don't pause, even if COMMAND MORE-PROMPTING is ON.", " ", "/ASK", " Interactively ask permission to delete each backup file.", " ", "/NOASK", " Purge backup files without asking permission.", " ", "/SIMULATE", " Inhibits the actual deletion of files; use to preview which files would", " actually be deleted. Implies /LIST.", " ", "Use SET OPTIONS PURGE [ switches ] to change defaults; use SHOW OPTIONS to", "display customized defaults. Also see HELP DELETE, HELP WILDCARD.", "" }; #endif /* CKPURGE */ #endif /* VMS */ static char *hmxxclo[] = { "Syntax: CLOSE [ item ]", " Close the indicated item. The default item is CONNECTION, which is the", " current SET LINE or SET HOST connection. The other items are:", " ", #ifdef CKLOGDIAL " CX-LOG (connection log, opened with LOG CX)", #endif /* CKLOGDIAL */ #ifndef NOLOCAL " SESSION-LOG (opened with LOG SESSION)", #endif /* NOLOCAL */ #ifdef TLOG " TRANSACTION-LOG (opened with LOG TRANSACTIONS)", #endif /* TLOG */ " PACKET-LOG (opened with LOG PACKETS)", #ifdef DEBUG " DEBUG-LOG (opened with LOG DEBUG)", #endif /* DEBUG */ #ifndef NOSPL " READ-FILE (opened with OPEN READ)", " WRITE-FILE (opened with OPEN WRITE or OPEN APPEND)", #endif /* NOSPL */ " ", "Type HELP LOG and HELP OPEN for further info.", "" }; #ifdef CKLEARN static char * hmxxlearn[] = { "Syntax: LEARN [ /ON /OFF /CLOSE ] [ filename ]", " Records a login script. If you give a filename, the file is opened for", " subsequent recording. If you don't give any switches, /ON is assumed.", " /ON enables recording to the current file (if any); /OFF disables", " recording. /CLOSE closes the current file (if any). After LEARN /CLOSE", " or exit from Kermit, your script is available for execution by the TAKE", " command.", "" }; #endif /* CKLEARN */ #ifdef CK_MINPUT static char *hmxxminp[] = { "Syntax: MINPUT [ switches ] n [ string1 [ string2 [ ... ] ] ]", "Example: MINPUT 5 Login: {Username: } {NO CARRIER} BUSY RING", " For use in script programs. Waits up to n seconds for any one of the", " strings to arrive on the communication device. If no strings are given,", " the command waits for any character at all to arrive. Strings are", " separated by spaces; use {braces} or \"doublequotes\" for grouping. If", " any of the strings is encountered within the timeout interval, the command", " succeeds and the \\v(minput) variable is set to the number of the string", " that was matched: 1, 2, 3, etc. If none of the strings arrives, the", " command times out, fails, and \\v(minput) is set to 0. In all other", " respects, MINPUT is like INPUT. See HELP INPUT for the available switches", " and other details of operation.", "" }; #endif /* CK_MINPUT */ #ifndef NOLOCAL static char *hmxxcon[] = { "Syntax: CONNECT (or C, or CQ) [ switches ]", " Connect to a remote computer via the serial communications device given in", #ifdef OS2 " the most recent SET PORT command, or to the network host named in the most", #else " the most recent SET LINE command, or to the network host named in the most", #endif /* OS2 */ " recent SET HOST command. Type the escape character followed by C to get", " back to the C-Kermit prompt, or followed by ? for a list of CONNECT-mode", #ifdef OS2 " escape commands. You can also assign the \\Kexit verb to the key or", " key-combination of your choice; by default it is assigned to Alt-x.", #else " escape commands.", " ", "Include the /QUIETLY switch to suppress the informational message that", "tells you how to escape back, etc. CQ is a synonym for CONNECT /QUIETLY.", #endif /* OS2 */ " ", "Other switches include:", #ifdef CK_TRIGGER " ", "/TRIGGER:string", " One or more strings to look for that will cause automatic return to", " command mode. To specify one string, just put it right after the", " colon, e.g. \"/TRIGGER:Goodbye\". If the string contains any spaces, you", " must enclose it in braces, e.g. \"/TRIGGER:{READY TO SEND...}\". To", " specify more than one trigger, use the following format:", " ", " /TRIGGER:{{string1}{string2}...{stringn}}", " ", " Upon return from CONNECT mode, the variable \\v(trigger) is set to the", " trigger string, if any, that was actually encountered. This value, like", " all other CONNECT switches applies only to the CONNECT command with which", " it is given, and overrides (temporarily) any global SET TERMINAL TRIGGER", " string that might be in effect.", #endif /* CK_TRIGGER */ #ifdef OS2 " ", "/IDLE-LIMIT:number", " The number of seconds of idle time, after which Kermit returns", " automatically to command mode; default 0 (no limit).", " ", "/IDLE-INTERVAL:number", " The number of seconds of idle time, after which Kermit automatically", " transmits the idle string.", " ", "/IDLE-STRING:string", " The string to transmit whenever the idle interval has passed.", " ", "/TIME-LIMIT:number", " The maximum number of seconds for which the CONNECT session may last.", " The default is 0 (no limit). If a nonzero number is given, Kermit returns", " automatically to command mode after this many seconds.", #endif /* OS2 */ "" }; #endif /* NOLOCAL */ static char *hmxxmget[] = { "Syntax: MGET [ switches... ] remote-filespec [ remote-filespec ... ]", " ", "Just like GET (q.v.) except allows a list of remote file specifications,", "separated by spaces.", "" }; static char *hmxxget[] = { "Syntax: GET [ switches... ] remote-filespec [ as-name ]", " Tells the other Kermit, which must be in (or support autoswitching into)", " server mode, to send the named file or files. If the remote-filespec or", " the as-name contain spaces, they must be enclosed in braces. If as-name", " is the name of an existing local directory, incoming files are placed in", " that directory; if it is the name of directory that does not exist, Kermit", " tries to create it. Optional switches include:", " ", "/AS-NAME:text", " Specifies \"text\" as the name to store the incoming file under, or", " directory to store it in. You can also specify the as-name as the second", " filename on the GET command line.", " ", "/BINARY", " Performs this transfer in binary mode without affecting the global", " transfer mode.", " ", "/COMMAND", " Receives the file into the standard input of a command, rather than saving", " it on disk. The /AS-NAME or the second \"filename\" on the GET command", " line is interpreted as the name of a command.", " ", "/DELETE", " Asks the other Kermit to delete the file (or each file in the group)", " after it has been transferred successfully.", " ", "/EXCEPT:pattern", " Specifies that any files whose names match the pattern, which can be a", " regular filename, or may contain \"*\" and/or \"?\" metacharacters,", " are to be refused. To specify multiple patterns (up to 8), use outer", " braces around the group, and inner braces around each pattern:", " ", " /EXCEPT:{{pattern1}{pattern2}...}", " ", "/FILENAMES:{CONVERTED,LITERAL}", " Overrides the global SET FILE NAMES setting for this transfer only.", " ", "/FILTER:command", " Causes the incoming file to passed through the given command (standard", " input/output filter) before being written to disk.", " ", #ifdef VMS "/IMAGE", " Transfer in image mode.", " ", #endif /* VMS */ #ifdef CK_LABELED "/LABELED", " VMS and OS/2 only: Specifies labeled transfer mode.", " ", #endif /* CK_LABELED */ "/MOVE-TO:directory-name", " Specifies that each file that arrives should be moved to the specified", " directory after, and only if, it has been received successfully.", " ", "/PATHNAMES:{OFF,ABSOLUTE,RELATIVE,AUTO}", " Overrides the global SET RECEIVE PATHNAMES setting for this transfer.", " ", "/PIPES:{ON,OFF}", " Overrides the TRANSFER PIPES setting for this command only. ON allows", " reception of files with names like \"!tar xf -\" to be automatically", " directed to a pipeline.", " ", "/QUIET", " When sending in local mode, this suppresses the file-transfer display.", " ", "/RECOVER", " Used to recover from a previously interrupted transfer; GET /RECOVER", " is equivalent REGET. Works only in binary mode.", " ", "/RECURSIVE", " Tells the server to descend through the directory tree when locating", " the files to be sent.", " ", "/RENAME-TO:string", " Specifies that each file that arrives should be renamed as specified", " after, and only if, it has been received successfully. The string should", " normally contain variables like \\v(filename) or \\v(filenum).", " ", "/TEXT", " Performs this transfer in text mode without affecting the global", " transfer mode.", " ", "/TRANSPARENT", " Inhibits character-set translation of incoming text files for the duration", " of the GET command without affecting subsequent commands.", " ", "Also see HELP MGET, HELP SEND, HELP RECEIVE, HELP SERVER, HELP REMOTE.", ""}; static char *hmxxlg[] = { "Syntax: LOG (or L) log-type [ filename [ { NEW, APPEND } ] ]", " ", "Record information in a log file:", " ", #ifdef CKLOGDIAL "CX", " Connections made with SET LINE, SET PORT, SET HOST, DIAL, TELNET, etc.", " The default filename is CX.LOG in your home directory and APPEND is the", " default mode for opening.", " ", #endif /* CKLOGDIAL */ #ifdef DEBUG "DEBUG", " Debugging information, to help track down bugs in the C-Kermit program.", " The default log name is debug.log in current directory.", " ", #endif /* DEBUG */ "PACKETS", " Kermit packets, to help with protocol problems. The default filename is", " packet.log in current directory.", " ", #ifndef NOLOCAL "SESSION", " Records your CONNECT session (default: session.log in current directory).", " ", #endif /* NOLOCAL */ #ifdef TLOG "TRANSACTIONS", " Names and statistics about files transferred (default: transact.log in", " current directory; see HELP SET TRANSACTION-LOG for transaction-log format", " options.)", " ", #endif /* TLOG */ "If you include the APPEND keyword after the filename, the existing log file,", "if any, is appended to; otherwise a new file is created (except APPEND is", "the default for the connection log). Use CLOSE to stop logging.", #ifdef OS2ORUNIX " ", "Note: The filename can also be a pipe, e.g.:", " ", " log transactions |lpr", " log debug {| grep \"^TELNET\" > debug.log}", " ", "Braces are required if the pipeline or filename contains spaces.", #endif /* OS2ORUNIX */ "" }; #ifndef NOSCRIPT static char *hmxxlogi[] = { "\ Syntax: SCRIPT text", " A limited and cryptic \"login assistant\", carried over from old C-Kermit", " releases for comptability, but not recommended for use. Instead, please", " use the full script programming language described in chapters 17-19 of", " \"Using C-Kermit\".", " ", " Login to a remote system using the text provided. The login script", " is intended to operate similarly to UNIX uucp \"L.sys\" entries.", " A login script is a sequence of the form:", " ", " expect send [expect send] . . .", " ", " where 'expect' is a prompt or message to be issued by the remote site, and", " 'send' is the names, numbers, etc, to return. The send may also be the", " keyword EOT to send Control-D, or BREAK (or \\\\b) to send a break signal.", " Letters in send may be prefixed by ~ to send special characters:", " ", " ~b backspace, ~s space, ~q '?', ~n linefeed, ~r return, ~c don\'t", " append a return, and ~o[o[o]] for octal of a character. As with some", " UUCP systems, sent strings are followed by ~r unless they end with ~c.", " ", " Only the last 7 characters in each expect are matched. A null expect,", " e.g. ~0 or two adjacent dashes, causes a short delay. If you expect", " that a sequence might not arrive, as with uucp, conditional sequences", " may be expressed in the form:", " ", " -send-expect[-send-expect[...]]", " ", " where dashed sequences are followed as long as previous expects fail.", "" }; #endif /* NOSCRIPT */ #ifndef NOFRILLS static char * hmxxtyp[] = { "Syntax: TYPE [ switches... ] file", " Displays a file on the screen. Pauses automatically at end of each", " screenful if COMMAND MORE-PROMPTING is ON. Optional switches:", " ", " /PAGE", " Pause at the end of each screenful even if COMMAND MORE-PROMPTING OFF.", " Synonym: /MORE", " /NOPAGE", " Don't pause at the end of each screen even if COMMAND MORE-PROMPTING ON." , " /HEAD:n", " Only type the first 'n' lines of the file.", " /TAIL:n", " Only type the last 'n' lines of the file.", " /MATCH:pattern", " Only type lines that match the given pattern. HELP WILDCARDS for info", " info about patterns. /HEAD and /TAIL apply after /MATCH.", " /PREFIX:string", " Print the given string at the beginning of each line.", " /NUMBER", " Add line numbers (conflicts with /PREFIX)", " /WIDTH:number", " Truncate each line at the given column number before printing.", #ifdef KUI " Or when combined with /GUI specifies the width of the dialog box.", " /HEIGHT:number", " When combined with /GUI specifies the height of the dialog box.", " /GUI:string", " Specifies the title to use for the dialog box.", #endif /* KUI */ " /COUNT", " Count lines (and matches) and print the count(s) but not the lines.", #ifdef UNICODE " /CHARACTER-SET:name", " Translates from the named character set.", #ifndef OS2 " /TRANSLATE-TO:name", " Translates to the named character set (default = current file charset).", #endif /* OS2 */ " /TRANSPARENT", " Inhibits character-set translation.", #endif /* UNICODE */ " /OUTPUT:name", " Sends results to the given file. If this switch is omitted, the", " results appear on your screen. This switch overrides any express or", " implied /PAGE switch.", " ", "You can use SET OPTIONS TYPE to set the defaults for /PAGE or /NOPAGE and", "/WIDTH. Use SHOW OPTIONS to see current TYPE options.", "" }; static char * hmxxcle[] = { "Syntax: CLEAR [ item-name ]", " ", "Clears the named item. If no item is named, DEVICE-AND-INPUT is assumed.", " ", " ALARM Clears any pending alarm (see SET ALARM).", #ifdef CK_APC " APC-STATUS Clears Application Program Command status.", #endif /* CK_APC */ #ifdef PATTERNS " BINARY-PATTERNS Clears the file binary-patterns list.", #endif /* PATTERNS */ #ifdef OS2 " COMMAND-SCREEN Clears the current command screen.", #endif /* OS2 */ " DEVICE Clears the current port or network input buffer.", " DEVICE-AND-INPUT Clears both the device and the INPUT buffer.", " DIAL-STATUS Clears the \\v(dialstatus) variable.", " \ INPUT Clears the INPUT-command buffer and the \\v(input) variable.", " KEYBOARD-BUFFER Clears the command terminal keyboard input buffer.", #ifdef OS2 " \ SCROLLBACK empties the scrollback buffer including the current screen.", #endif /* OS2 */ " SEND-LIST Clears the current SEND list (see ADD).", #ifdef OS2 " \ TERMINAL-SCREEN Clears the current screen a places it into the scrollback.", " buffer.", #endif /* OS2 */ #ifdef PATTERNS " TEXT-PATTERNS Clears the file text-patterns list.", #endif /* PATTERNS */ ""}; #endif /* NOFRILLS */ static char * hmxxdate[] = { "Syntax: DATE [ date-time [ timezone ] ] [ delta-time ]", " Prints a date-time in standard format: yyyymmdd_hh:mm:ss.", " Various date-time formats are accepted:", " ", " . The date, if given, must precede the time.", " . The year must be four digits or else a 2-digit format dd mmm yy,", " in which case if (yy < 50) yyyy = yy + 2000; else yyyy = yy + 1900.", " . If the year comes first, the second field is the month.", " . The day, month, and year may be separated by spaces, /, -, or underscore." ," . The date and time may be separated by spaces or underscore.", " . The month may be numeric (1 = January) or spelled out or abbreviated in", " English.", " . The time may be in 24-hour format or 12-hour format.", " . If the hour is 12 or less, AM is assumed unless AM or PM is included.", " . If the date is omitted but a time is given, the current date is supplied." , " . If the time is given but date omitted, 00:00:00 is supplied.", " . If both the date and time are omitted, the current date and time are", " supplied.", " ", " The following shortcuts can also be used in place of dates:", " ", " NOW", " Stands for the current date and time.", " ", " TODAY", " Today's date, optionally followed by a time; 00:00:00 if no time given.", " ", " YESTERDAY", " Yesterday's date, optionally followed by a time (default 00:00:00).", " ", " TOMORROW", " Tomorrows's date, optionally followed by a time (default 00:00:00).", " ", " Timezone specifications are similar to those used in e-mail and HTTP", " headers, either a USA timezone name, e.g. EST, or a signed four-digit", " timezone offset, {+,-}hhmm, e.g., -0500; it is used to convert date-time," , " a local time in that timezone, to GMT which is then converted to the", " local time at the host. If no timezone is given, the date-time is local." ," To convert local time (or a time in a specified timezone) to UTC (GMT),", " use the function \futcdate().", " ", " Delta times are given as {+,-}[number date-units][hh[:mm[:ss]]]", " A date in the future/past relative to the date-time; date-units may be", " DAYS, WEEKS, MONTHS, YEARS: +3days, -7weeks, +3:00, +1month 8:00.", " ", "All the formats shown above are acceptable as arguments to date-time switches" , "such as /AFTER: or /BEFORE:, and to functions such as \\fcvtdate(),", "\\fdiffdate(), and \\futcdate(), that take date-time strings as arguments.", "" }; #ifndef NOXFER static char * hmxxsen[] = { "Syntax: SEND (or S) [ switches...] [ filespec [ as-name ] ]", " Sends the file or files specified by filespec. If the filespec is omitted", " the SEND-LIST is used (HELP ADD for more info). The filespec may contain", " wildcard characters. An 'as-name' may be given to specify the name(s)", " the files(s) are sent under; if the as-name is omitted, each file is", " sent under its own name. Also see HELP MSEND, HELP WILDCARD.", " Optional switches include:", " ", #ifndef NOSPL "/ARRAY:", " Specifies that the data to be sent comes from the given array, such as", " \\&a[]. A range may be specified, e.g. SEND /ARRAY:&a[100:199]. Leave", " the brackets empty or omit them altogether to send the whole 1-based array." , " Include /TEXT to have Kermit supply a line terminator at the end of each", " array element (and translate character sets if character-set translations", " are set up), or /BINARY to treat the array as one long string of characters" , " to be sent as-is. If an as-name is not specified, the array is sent with", " the name _ARRAY_X_, where \"X\" is replaced by actual array letter.", " ", #endif /* NOSPL */ "/AS-NAME:", " Specifies as the name to send the file under instead of its real", " name. This is equivalent to giving an as-name after the filespec.", " ", "/BINARY", " Performs this transfer in binary mode without affecting the global", " transfer mode.", " ", "/TEXT", " Performs this transfer in text mode without affecting the global", " transfer mode.", " ", "/TRANSPARENT", " Inhibits character-set translation for text files for the duration of", " the SEND command without affecting subsequent commands.", " ", "/NOBACKUPFILES", " Skip (don't send) Kermit or EMACS backup files (files with names that", " end with .~n~, where n is a number).", " ", #ifdef UNIXOROSK "/DOTFILES", " Include (send) files whose names begin with \".\".", " ", "/NODOTFILES", " Don't send files whose names begin with \".\".", " ", "/FOLLOWLINKS", " Send files that are pointed to by symbolic links.", " ", "/NOFOLLOWLINKS", " Skip over symbolic links (default).", " ", #endif /* UNIXOROSK */ #ifdef VMS "/IMAGE", " Performs this transfer in image mode without affecting the global", " transfer mode.", " ", #endif /* VMS */ #ifdef CK_LABELED "/LABELED", " Performs this transfer in labeled mode without affecting the global", " transfer mode.", " ", #endif /* CK_LABELED */ "/COMMAND", " Sends the output from a command, rather than the contents of a file.", " The first \"filename\" on the SEND command line is interpreted as the name", " of a command; the second (if any) is the as-name.", " ", "/FILENAMES:{CONVERTED,LITERAL}", " Overrides the global SET FILE NAMES setting for this transfer only.", " ", "/PATHNAMES:{OFF,ABSOLUTE,RELATIVE}", " Overrides the global SET SEND PATHNAMES setting for this transfer.", " ", "/FILTER:command", " Specifies a command \ (standard input/output filter) to pass the file through", " before sending it.", " ", "/DELETE", " Deletes the file (or each file in the group) after it has been sent", " successfully (applies only to real files).", " ", "/QUIET", " When sending in local mode, this suppresses the file-transfer display.", " ", "/RECOVER", " Used to recover from a previously interrupted transfer; SEND /RECOVER", " is equivalent RESEND (use in binary mode only).", " ", "/RECURSIVE", " Tells C-Kermit to look not only in the given or current directory for", " files that match the filespec, but also in all its subdirectories, and", " all their subdirectories, etc.", " ", "/RENAME-TO:name", " Tells C-Kermit to rename each source file that is sent successfully to", " the given name (usually you should include \\v(filename) in the new name,", " which is replaced by the original filename.", " ", "/MOVE-TO:directory", " Tells C-Kermit to move each source file that is sent successfully to", " the given directory.", " ", "/STARTING:number", " Starts sending the file from the given byte position.", " SEND /STARTING:n filename is equivalent to PSEND filename n.", " ", "/SUBJECT:text", " Specifies the subject of an email message, to be used with /MAIL. If the", " text contains spaces, it must be enclosed in braces.", " ", "/MAIL:address", " Sends the file as e-mail to the given address; use with /SUBJECT:.", " ", "/PRINT:options", " Sends the file to be printed, with optional options for the printer.", " ", #ifdef CK_XYZ "/PROTOCOL:name", " Uses the given protocol to send the file (Kermit, Zmodem, etc) for this", " transfer without changing global protocol.", " ", #endif /* CK_XYZ */ "/AFTER:date-time", #ifdef VMS " Specifies that only those files created after the given date-time are", #else " Specifies that only those files modified after the given date-time are", #endif /* VMS */ " to be sent. HELP DATE for info about date-time formats.", " ", "/BEFORE:date-time", #ifdef VMS " Specifies that only those files modified before the given date-time", #else " Specifies that only those files modified before the given date-time", #endif /* VMS */ " are to be sent.", " ", "/NOT-AFTER:date-time", #ifdef VMS " Specifies that only those files modified at or before the given date-time", #else " Specifies that only those files modified at or before the given date-time", #endif /* VMS */ " are to be sent.", " ", "/NOT-BEFORE:date-time", #ifdef VMS " Specifies that only those files modified at or after the given date-time", #else " Specifies that only those files modified at or after the given date-time", #endif /* VMS */ " are to be sent.", " ", "/LARGER-THAN:number", " Specifies that only those files longer than the given number of bytes are", " to be sent.", " ", "/SMALLER-THAN:number", " Specifies that only those files smaller than the given number of bytes are", " to be sent.", " ", "/EXCEPT:pattern", " Specifies that any files whose names match the pattern, which can be a", " regular filename, or may contain \"*\" and/or \"?\" metacharacters,", " are not to be sent. To specify multiple patterns (up to 8), use outer", " braces around the group, and inner braces around each pattern:", " ", " /EXCEPT:{{pattern1}{pattern2}...}", " ", "/TYPE:{ALL,TEXT,BINARY}", " Send only files of the given type (see SET FILE SCAN).", " ", "/LISTFILE:filename", " Specifies the name of a file that contains the list of names of files", " that are to be sent. The filenames should be listed one name per line", " in this file (but a name can contain wildcards).", " ", "Also see HELP RECEIVE, HELP GET, HELP SERVER, HELP REMOTE.", ""}; static char *hmxxrc[] = { "Syntax: RECEIVE (or R) [ switches... ] [ as-name ]", " Wait for a file to arrive from the other Kermit, which must be given a", " SEND command. If the optional as-name is given, the incoming file or", " files are stored under that name, otherwise it will be stored under", #ifndef CK_TMPDIR " the name it arrives with.", #else #ifdef OS2 " the name it arrives with. If the filespec denotes a disk and/or", " directory, the incoming file or files will be stored there.", #else " the name it arrives with. If the filespec denotes a directory, the", " incoming file or files will be placed in that directory.", #endif /* OS2 */ #endif /* CK_TMPDIR */ " ", "Optional switches include:", " ", "/AS-NAME:text", " Specifies \"text\" as the name to store the incoming file under.", " You can also specify the as-name as a filename on the command line.", " ", "/BINARY", " Skips text-mode conversions unless the incoming file arrives with binary", " attribute", " ", "/COMMAND", " Receives the file into the standard input of a command, rather than saving", " it on disk. The /AS-NAME or the \"filename\" on the RECEIVE command line", " is interpreted as the name of a command.", " ", "/EXCEPT:pattern", " Specifies that any files whose names match the pattern, which can be a", " regular filename, or may contain \"*\" and/or \"?\" metacharacters,", " are to be refused. To specify multiple patterns (up to 8), use outer", " braces around the group, and inner braces around each pattern:", " ", " /EXCEPT:{{pattern1}{pattern2}...}", " ", "/FILENAMES:{CONVERTED,LITERAL}", " Overrides the global SET FILE NAMES setting for this transfer only.", " ", "/FILTER:command", " Causes the incoming file to passed through the given command (standard", " input/output filter) before being written to disk.", " ", #ifdef VMS "/IMAGE", " Receives the file in image mode.", " ", #endif /* VMS */ #ifdef CK_LABELED "/LABELED", " Specifies labeled transfer mode.", " ", #endif /* CK_LABELED */ "/MOVE-TO:directory-name", " Specifies that each file that arrives should be moved to the specified", " directory after, and only if, it has been received successfully.", " ", "/PATHNAMES:{OFF,ABSOLUTE,RELATIVE,AUTO}", " Overrides the global SET RECEIVE PATHNAMES setting for this transfer.", " ", "/PIPES:{ON,OFF}", " Overrides the TRANSFER PIPES setting for this command only. ON allows", " reception of files with names like \"!tar xf -\" to be automatically", " directed to a pipeline.", " ", "/PROTOCOL:name", " Use the given protocol to receive the incoming file(s).", " ", "/QUIET", " When sending in local mode, this suppresses the file-transfer display.", " ", "/RECURSIVE", " Equivalent to /PATHNAMES:RELATIVE.", " ", "/RENAME-TO:string", " Specifies that each file that arrives should be renamed as specified", " after, and only if, it has been received successfully. The string should", " normally contain variables like \\v(filename) or \\v(filenum).", " ", "/TEXT", " Forces text-mode conversions unless the incoming file has the binary", " attribute", " ", "/TRANSPARENT", " Inhibits character-set translation of incoming text files for the duration", " of the RECEIVE command without affecting subsequent commands.", " ", "Also see HELP SEND, HELP GET, HELP SERVER, HELP REMOTE.", "" }; #ifndef NORESEND static char *hmxxrsen = "\ Syntax: RESEND filespec [name]\n\n\ Resends the file or files, whose previous transfer was interrupted.\n\ Picks up from where previous transfer left off, IF the receiver kept the\n\ partially received file. Works only for binary-mode transfers.\n\ Requires file-transfer partner to support recovery. Synonym: REPUT."; static char *hmxxrget = "\ Syntax: REGET filespec\n\n\ Ask a server to RESEND a file to C-Kermit."; static char *hmxxpsen = "\ Syntax: PSEND filespec position [name]\n\n\ Just like SEND, except sends the file starting at the given byte position."; #endif /* NORESEND */ #ifndef NOMSEND static char *hmxxmse[] = { "Syntax: MSEND [ switches... ] filespec [ filespec [ ... ] ]", " Sends the files specified by the filespecs. One or more filespecs may be", " listed, separated by spaces. Any or all filespecs may contain wildcards", " and they may be in different directories. Alternative names cannot be", " given. Switches include /BINARY /DELETE /MAIL /PROTOCOL /QUIET /RECOVER", " /TEXT /TYPE; see HELP SEND for descriptions.", "" }; #endif /* NOMSEND */ static char *hmxxadd[] = { #ifndef NOMSEND "ADD SEND-LIST filespec [ [ ] ]", " Adds the specified file or files to the current SEND list. Use SHOW", " SEND-LIST and CLEAR SEND-LIST to display and clear the list; use SEND", " by itself to send the files from it.", " ", #endif /* NOMSEND */ #ifdef PATTERNS "ADD BINARY-PATTERNS [ [ ... ] ]", " Adds the pattern(s), if any, to the SET FILE BINARY-PATTERNS list.", " ", "ADD TEXT-PATTERNS [ [ ... ] ]", " Adds the pattern(s), if any, to the SET FILE TEXT-PATTERNS list.", " Use SHOW PATTERNS to see the lists. See HELP SET FILE for further info.", #endif /* PATTERNS */ ""}; static char *hmxxremv[] = { #ifdef PATTERNS "REMOVE BINARY-PATTERNS [ [ ... ] ]", " Removes the pattern(s), if any, from the SET FILE BINARY-PATTERNS list", " ", "REMOVE TEXT-PATTERNS [ [ ... ] ]", " Removes the given patterns from the SET FILE TEXT-PATTERNS list.", " Use SHOW PATTERNS to see the lists. See HELP SET FILE for further info.", #endif /* PATTERNS */ ""}; #endif /* NOXFER */ #ifndef NOSERVER static char *hmxxser = "Syntax: SERVER\n\ Enter server mode on the current connection. All further commands\n\ are taken in packet form from the other Kermit program. Use FINISH,\n\ BYE, or REMOTE EXIT to get C-Kermit out of server mode."; #endif /* NOSERVER */ static char *hmhset[] = { " The SET command establishes communication, file, scripting, or other", " parameters. The SHOW command can be used to display the values of", " SET parameters. Help is available for each individual parameter;", " type HELP SET ? to see what's available.", "" }; #ifndef NOSETKEY static char *hmhskey[] = { "Syntax: SET KEY k text", "Or: SET KEY CLEAR", " Configure the key whose \"scan code\" is k to send the given text when", " pressed during CONNECT mode. SET KEY CLEAR restores all the default", " key mappings. If there is no text, the default key binding is restored", #ifndef NOCSETS " for the key k. SET KEY mappings take place before terminal character-set", " translation.", #else " the key k.", #endif /* NOCSETS */ #ifdef OS2 " ", " The text may contain \"\\Kverbs\" to denote actions, to stand for DEC", " keypad, function, or editing keys, etc. For a list of available keyboard", " verbs, type SHOW KVERBS.", #endif /* OS2 */ " ", " To find out the scan code and mapping for a particular key, use the", " SHOW KEY command.", ""}; #endif /* NOSETKEY */ static char *hmxychkt[] = { "Syntax: SET BLOCK-CHECK number", " ", "Type of block check to be used for error detection on file-transfer", "packets: 1, 2, 3, 4, or 5. This command must be given to the file", "sender prior to the transfer.", " ", "Type 1 is standard and supported by all Kermit protocol implementations,", " but it's only a 6-bit checksum, represented in a single printable ASCII", " character. It's fine for reliable connections (error-correcting modems,", " TCP/IP, etc) but type 3 is recommended for connections where errors can", " occur.", " ", "Type 2 is a 12-bit checksum represented in two printable characters.", " ", "Type 3 is a 16-bit cyclic redundancy check, the strongest error", " detection method supported by Kermit protocol, represented in three", " printable characters.", " ", "Type 4 (alias \"BLANK-FREE-2\") is a 12-bit checksum guaranteed to", " contain no blanks in its representation; this is needed for connections", " where trailing blanks are stripped from incoming lines of text.", " ", "Type 5 (alias \"FORCE-3\") means to force a Type 3 block check on", " every packet, including the first packet, which normally has a type 1", " block check. This is for use in critical applications on noisy", " connections. As with types 2, 3, and 4, if the Kermit file", " transfer partner does not support this type, the transfer fails", " immediately at the beginning of the transfer.", "" }; static char * hmxydeb[] = { "Syntax: SET DEBUG { SESSION, ON, OFF, TIMESTAMP, MESSAGES }", " ", "SET DEBUG ON", #ifdef DEBUG " Opens a debug log file named debug.log in the current directory.", " Use LOG DEBUG if you want specify a different log file name or path.", #else " (Has no effect in this version of Kermit.)", #endif /* DEBUG */ " ", "SET DEBUG OFF", " Stops debug logging and session debugging.", " ", "SET DEBUG SESSION", #ifndef NOLOCAL " Displays control and 8-bit characters symbolically during CONNECT mode.", " Equivalent to SET TERMINAL DEBUG ON.", #else " (Has no effect in this version of Kermit.)", #endif /* NOLOCAL */ " ", "SET DEBUG TIMESTAMP { ON, OFF }", " Enables/Disables timestamps on debug log entries.", " ", "SET DEBUG MESSAGES { ON, OFF, STDERR } [C-Kermit 9.0]", " Enables/Disables messages printed by the DEBUG command.", " SET DEBUG OFF causes DEBUG messages not to be printed.", " SET DEBUG ON sends DEBUG messages to standard output (stdout);", " SET DEBUG STDERR sends DEBUG messages to standard error (stderr);", "" }; #ifdef CK_SPEED static char *hmxyqctl[] = { "Syntax: SET CONTROL-CHARACTER { PREFIXED, UNPREFIXED } { ..., ALL }", " ", " is the numeric ASCII code for a control character 1-31,127-159,255." , " The word \"ALL\" means all characters in this range.", " ", " PREFIXED means the given control character must be converted to a", " printable character and prefixed, the default for all control characters.", " ", " UNPREFIXED means you think it is safe to send the given control", " character as-is, without a prefix. USE THIS OPTION AT YOUR OWN RISK!", " ", " SHOW CONTROL to see current settings. SET CONTROL PREFIXED ALL is", " recommended for safety. You can include multiple values in one", " command, separated by spaces.", "" }; #endif /* CK_SPEED */ #ifndef NODIAL static char *hxymodm[] = { "Syntax: SET MODEM ...", " ", "Note: Many of the SET MODEM parameters are configured automatically when", "you SET MODEM TYPE, according to the modem's capabilities. SHOW MODEM to", "see them. Also see HELP DIAL and HELP SET DIAL.", " ", "SET MODEM TYPE ", " Tells Kermit which kind of modem you have, so it can issue the", " appropriate modem-specific commands for configuration, dialing, and", " hanging up. For a list of the modem types known to Kermit, type \"set", " modem type ?\". The default modem type is GENERIC, which should work", " with any AT command-set modem that is configured for error correction,", " data compression, and hardware flow control. Use SET MODEM TYPE NONE", " for direct serial, connections. Use SET MODEM TYPE USER-DEFINED to use", " a type of modem that is not built in to Kermit, and then use SET MODEM", " CAPABILITIES, SET MODEM, DIAL-COMMAND, and SET MODEM COMMAND to tell", " Kermit how to configure and control it.", " ", "SET MODEM CAPABILITIES ", " Use this command for changing Kermit's idea of your modem's capabilities,", " for example, if your modem is supposed to have built-in error correction", " but in fact does not. Also use this command to define the capabilities", " of a USER-DEFINED modem. Capabilities are:", " ", " AT AT-commands", " DC data-compression", " EC error-correction", " HWFC hardware-flow", " ITU v25bis-commands", " SWFC software-flow", " KS kermit-spoof", " SB speed-buffering", " TB Telebit", " ", "SET MODEM CARRIER-WATCH { AUTO, ON, OFF }", " Synonym for SET CARRIER-WATCH (q.v.)", " ", "SET MODEM COMPRESSION { ON, OFF }", " Enables/disables the modem's data compression feature, if any.", " ", "SET MODEM DIAL-COMMAND ", " The text replaces Kermit's built-in modem dialing command. It must", " include '%s' (percent s) as a place-holder for the telephone numbers", " given in your DIAL commands.", " ", "SET MODEM ERROR-CORRECTION { ON, OFF }", " Enables/disables the modem's error-correction feature, if any.", " ", "SET MODEM ESCAPE-CHARACTER number", " Numeric ASCII value of modem's escape character, e.g. 43 for '+'.", " For Hayes-compatible modems, Kermit uses three copies, e.g. \"+++\".", " ", "SET MODEM FLOW-CONTROL {AUTO, NONE, RTS/CTS, XON/XOFF}", " Selects the type of local flow control to be used by the modem.", " ", "SET MODEM HANGUP-METHOD { MODEM-COMMAND, RS232-SIGNAL, DTR }", " How hangup operations should be done. MODEM-COMMAND means try to", " escape back to the modem's command processor and give a modem-specific", " hangup command. RS232-SIGNAL means turn off the DTR signal. DTR is a", " synonym for RS232-SIGNAL.", " ", "SET MODEM KERMIT-SPOOF {ON, OFF}", " If the selected modem type supports the Kermit protocol directly,", " use this command to turn its Kermit protocol function on or off.", " ", "SET MODEM MAXIMUM-SPEED ", " Specify the maximum interface speed for the modem.", " ", "SET MODEM NAME ", " Descriptive name for a USER-DEFINED modem.", " ", "SET MODEM SPEAKER {ON, OFF}", " Turns the modem's speaker on or off during dialing.", " ", "SET MODEM SPEED-MATCHING {ON, OFF}", " ON means that C-Kermit changes its serial interface speed to agree with", " the speed reported by the modem's CONNECT message, if any. OFF means", " Kermit should not change its interface speed.", " ", "SET MODEM VOLUME {LOW, MEDIUM, HIGH}", " Selects the desired modem speaker volume for when the speaker is ON.", " ", "SET MODEM COMMAND commands are used to override built-in modem commands for", "each modem type, or to fill in commands for the USER-DEFINED modem type.", "Omitting the optional [ text ] restores the built-in modem-specific command,", "if any:", " ", "SET MODEM COMMAND AUTOANSWER {ON, OFF} [ text ]", " Modem commands to turn autoanswer on and off.", " ", "SET MODEM COMMAND COMPRESSION {ON, OFF} [ text ]", " Modem commands to turn data compression on and off.", " ", "SET MODEM COMMAND ERROR-CORRECTION {ON, OFF} [ text ]", " Modem commands to turn error correction on and off.", " ", "SET MODEM COMMAND HANGUP [ text ]", " Command that tells the modem to hang up the connection.", " ", "SET MODEM COMMAND IGNORE-DIALTONE [ text ]", " Command that tells the modem not to wait for dialtone before dialing.", " ", "SET MODEM COMMAND INIT-STRING [ text ]", " The 'text' is a replacement for C-Kermit's built-in initialization command", " for the modem.", " ", "SET MODEM COMMAND PREDIAL-INIT [ text ]", " A second INIT-STRING that is to be sent to the modem just prior to \ dialing.", " ", "SET MODEM COMMAND HARDWARE-FLOW [ text ]", " Modem command to enable hardware flow control (RTS/CTS) in the modem.", " ", "SET MODEM COMMAND SOFTWARE-FLOW [ text ]", " Modem command to enable local software flow control (Xon/Xoff) in modem.", " ", "SET MODEM COMMAND SPEAKER { ON, OFF } [ text ]", " Modem command to turn the modem's speaker on or off.", " ", "SET MODEM COMMAND NO-FLOW-CONTROL [ text ]", " Modem command to disable local flow control in the modem.", " ", "SET MODEM COMMAND PULSE [ text ]", " Modem command to select pulse dialing.", " ", "SET MODEM COMMAND TONE [ text ]", " Modem command to select tone dialing.", " ", "SET MODEM COMMAND VOLUME { LOW, MEDIUM, HIGH } [ text ]", " Modem command to set the modem's speaker volume.", ""}; static char *hmxydial[] = { "The SET DIAL command establishes or changes all parameters related to", "dialing the telephone. Also see HELP DIAL and HELP SET MODEM. Use SHOW", "DIAL to display all of the SET DIAL values.", " ", "SET DIAL COUNTRY-CODE ", " Tells Kermit the telephonic country-code of the country you are dialing", " from, so it can tell whether a portable-format phone number from your", " dialing directory will result in a national or an international call.", " Examples: 1 for USA, Canada, Puerto Rico, etc; 7 for Russia, 39 for Italy,", " 351 for Portugal, 47 for Norway, 44 for the UK, 972 for Israel, 81 for", " Japan, ...", " ", " If you have not already set your DIAL INTL-PREFIX and LD-PREFIX, then this", " command sets default values for them: 011 and 1, respectively, for country", " code 1; 00 and 0, respectively, for all other country codes. If these are", " not your true international and long-distance dialing prefixes, then you", " should follow this command by DIAL INTL-PREFIX and LD-PREFIX to let Kermit", " know what they really are.", " ", "SET DIAL AREA-CODE [ ]", " Tells Kermit the area or city code that you are dialing from, so it can", " tell whether a portable-format phone number from the dialing directory is", " local or long distance. Be careful not to include your long-distance", " dialing prefix as part of your area code; for example, the area code for", " central London is 171, not 0171.", " ", "SET DIAL CONFIRMATION {ON, OFF}", " Kermit does various transformations on a telephone number retrieved from", " the dialing directory prior to dialing (use LOOKUP to see them).", " In case the result might be wrong, you can use SET DIAL CONFIRM ON to have", " Kermit ask you if it is OK to dial the number, and if not, to let you type", " in a replacement.", " ", "SET DIAL CONNECT { AUTO, ON, OFF }", " Whether to CONNECT (enter terminal mode) automatically after successfully", " dialing. ON means to do this; OFF means not to. AUTO (the default) means", " do it if the DIAL command was given interactively, but don't do it if the", " DIAL command was issued from a macro or command file. If you specify ON", " or AUTO, you may follow this by one of the keywords VERBOSE or QUIET, to", " indicate whether the verbose 4-line 'Connecting...' message is to be", " displayed if DIAL succeeds and Kermit goes into CONNECT mode.", " ", "SET DIAL CONVERT-DIRECTORY {ASK, ON, OFF}", " The format of Kermit's dialing directory changed in version 5A(192). This", " command tells Kermit what to do when it encounters an old-style directory:", " ASK you whether to convert it, or convert it automatically (ON), or leave", " it alone (OFF). Old-style directories can still be used without", " conversion, but the parity and speed fields are ignored.", " ", "SET DIAL DIRECTORY [ filename [ filename [ filename [ ... ] ] ] ]", " The name(s) of your dialing directory file(s). If you do not supply any", " filenames, the dialing directory feature is disabled and all numbers are", " dialed literally as given in the DIAL command. If you supply more than", " one directory, all of them are searched.", " ", "SET DIAL SORT {ON, OFF}", " When multiple entries are obtained from your dialing directory, they are", " sorted in \"cheapest-first\" order. If this does not produce the desired", " effect, SET DIAL SORT OFF to disable sorting, and the numbers will be", " dialed in the order in which they were found.", " ", "SET DIAL DISPLAY {ON, OFF}", " Whether to display dialing progress on the screen; default is OFF.", " ", "SET DIAL HANGUP {ON, OFF}", " Whether to hang up the phone prior to dialing; default is ON.", " ", "SET DIAL IGNORE-DIALTONE {ON, OFF}", " Whether to ignore dialtone when dialing; default is OFF.", " ", #ifndef NOSPL "SET DIAL MACRO [ name ]", " Specify the name of a macro to execute on every phone number dialed, just", " prior to dialing it, in order to perform any last-minute alterations.", " ", #endif /* NOSPL */ "SET DIAL METHOD {AUTO, DEFAULT, TONE, PULSE}", " Whether to use the modem's DEFAULT dialing method, or to force TONE or", " PULSE dialing. AUTO (the default) means to choose tone or pulse dialing", " based on the country code. (Also see SET DIAL TONE-COUNTRIES and SET DIAL", " PULSE-COUNTRIES.)", " ", "SET DIAL PACING number", " How many milliseconds to pause between sending each character to the modem", " dialer. The default is -1, meaning to use the number from the built-in", " modem database.", " ", "SET DIAL PULSE-COUNTRIES [ cc [ cc [ ... ] ] ]", " Sets the list of countries in which pulse dialing is required. Each cc", " is a country code.", " ", "SET DIAL TEST { ON, OFF }", " OFF for normal dialing. Set to ON to test dialing procedures without", " actually dialing.", " ", "SET DIAL TONE-COUNTRIES [ cc [ cc [ ... ] ] ]", " Sets the list of countries in which tone dialing is available. Each cc", " is a country code.", " ", "SET DIAL TIMEOUT number", " How many seconds to wait for a dialed call to complete. Use this command", " to override the DIAL command's automatic timeout calculation. A value", " of 0 turns off this feature and returns to Kermit's automatic dial", " timeout calculation.", " ", "SET DIAL RESTRICT { INTERNATIONAL, LOCAL, LONG-DISTANCE, NONE }", " Prevents placing calls of the type indicated, or greater. For example", " SET DIAL RESTRICT LONG prevents placing of long-distance and international", " calls. If this command is not given, there are no restrictions. Useful", " when dialing a list of numbers fetched from a dialing directory.", " ", "SET DIAL RETRIES ", " How many times to redial each number if the dialing result is busy or no", " no answer, until the call is successfully answered. The default is 0", " because automatic redialing is illegal in some countries.", " ", "SET DIAL INTERVAL ", " How many seconds to pause between automatic redial attempts; default 10.", " ", "The following commands apply to all phone numbers, whether given literally", "or found in the dialing directory:", " ", "SET DIAL PREFIX [ text ]", " Establish a prefix to be applied to all phone numbers that are dialed,", " for example to disable call waiting.", " ", "SET DIAL SUFFIX [ text ]", " Establish a suffix to be added after all phone numbers that are dialed.", " ", "The following commands apply only to portable-format numbers obtained from", "the dialing directory; i.e. numbers that start with a \"+\" sign and", "country code, followed by area code in parentheses, followed by the phone", "number.", " ", "SET DIAL LC-AREA-CODES [ ]", " Species a list of area codes to which dialing is local, i.e. does not", " require the LD-PREFIX. Up to 32 area codes may be listed, separated by", " spaces. Any area codes in this list will be included in the final dial", " string so do not include your own area code if it should not be dialed.", " ", "SET DIAL LC-PREFIX [ ]", " Specifies a prefix to be applied to local calls made from portable dialing", " directory entries. Normally no prefix is used for local calls.", " ", "SET DIAL LC-SUFFIX [ ]", " Specifies a suffix to be applied to local calls made from portable dialing", " directory entries. Normally no suffix is used for local calls.", " ", "SET DIAL LD-PREFIX [ ]", " Your long-distance dialing prefix, to be used with portable dialing", " directory entries that result in long-distance calls.", " ", "SET DIAL LD-SUFFIX [ ]", " Long-distance dialing suffix, if any, to be used with portable dialing", " directory entries that result in long-distance calls. This would normally", " be used for appending a calling-card number to the phone number.", " ", "SET DIAL FORCE-LONG-DISTANCE { ON, OFF }", " Whether to force long-distance dialing for calls that normally would be", " local. For use (e.g.) in France.", " ", "SET DIAL TOLL-FREE-AREA-CODE [ [ [ ... ] ] ]", " Tells Kermit the toll-free area code(s) in your country.", " ", "SET DIAL TOLL-FREE-PREFIX [ ]", " You toll-free dialing prefix, in case it is different from your long-", " distance dialing prefix.", " ", "SET DIAL INTL-PREFIX ", " Your international dialing prefix, to be used with portable dialing", " directory entries that result in international calls.", " ", "SET DIAL INTL-SUFFIX ", " International dialing suffix, if any, to be used with portable dialing", " directory entries that result in international calls.", " ", "SET DIAL PBX-OUTSIDE-PREFIX ", " Use this to tell Kermit how to get an outside line when dialing from a", " Private Branch Exchange (PBX).", " ", "SET DIAL PBX-EXCHANGE [ [ ... ] ]", " If PBX-OUTSIDE-PREFIX is set, then you can use this command to tell Kermit", " the leading digits of one or more local phone numbers that identify it as", " being on your PBX, so it can make an internal call by deleting those digits" , " from the phone number.", " ", "SET DIAL PBX-INTERNAL-PREFIX ", " If PBX-EXCHANGE is set, and Kermit determines from it that a call is", " internal, then this prefix, if any, is added to the number prior to", " \ dialing. Use this if internal calls from your PBX require a special prefix.", "" }; #endif /* NODIAL */ static char *hmxyflo[] = { "Syntax: SET FLOW [ switch ] value", " ", #ifndef NOLOCAL " Selects the type of flow control to use during file transfer, terminal", " connection, and script execution.", #else " Selects the type of flow control to use during file transfer.", #endif /* NOLOCAL */ " ", " Switches let you associate a particular kind of flow control with each", " kind of connection: /REMOTE, /MODEM, /DIRECT-SERIAL, /TCPIP, etc; type", " \"set flow ?\" for a list of available switches. Then whenever you make", " a connection, the associated flow-control is chosen automatically.", " The flow-control values are NONE, KEEP, XON/XOFF, and possibly RTS/CTS", " and some others; again, type \"set flow ?\" for a list. KEEP tells Kermit", " not to try to change the current flow-control method for the connection.", " ", " If you omit the switch and simply supply a value, this value becomes the", " current flow control type, overriding any default value that might have", " been chosen in your most recent SET LINE, SET PORT, or SET HOST, or other", " connection-establishment command.", " ", " Type SHOW FLOW-CONTROL to see the current defaults for each connection type" , " as well as the current connection type and flow-control setting. SHOW", " COMMUNICATIONS also shows the current flow-control setting.", ""}; static char *hmxyf[] = { "Syntax: SET FILE parameter value", " ", "Sets file-related parameters. Use SHOW FILE to view them. Also see SET", "(and SHOW) TRANSFER and PROTOCOL.", " ", #ifdef VMS "SET FILE TYPE { TEXT, BINARY, IMAGE, LABELED }", #else #ifdef STRATUS "SET FILE TYPE { TEXT, BINARY, LABELED }", #else #ifdef MAC "SET FILE TYPE { TEXT, BINARY, MACBINARY }", #else "SET FILE TYPE { TEXT, BINARY }", #endif /* STRATUS */ #endif /* MAC */ #endif /* VMS */ " How file contents are to be treated during file transfer in the absence", " of any other indication. TYPE can be TEXT for conversion of record format", " and character set, which is usually needed when transferring text files", " between unlike platforms (such as UNIX and Windows), or BINARY for no", " conversion if TRANSFER MODE is MANUAL, which is not the default. Use", " BINARY with TRANSFER MODE MANUAL for executable programs or binary data or", " whenever you wish to duplicate the original contents of the file, byte for" , " byte. In most modern Kermit programs, the file sender informs the receiver" , " of the file type automatically. However, when sending files from C-Kermit", " to an ancient or non-Columbia Kermit implementation, you might need to set", " the corresponding file type at the receiver as well.", " ", #ifdef VMS " FILE TYPE settings of TEXT and BINARY have no effect when sending files,", " since VMS C-Kermit determines each file's type automatically from its", " record format: binary for fixed, text for others. For incoming files,", " these settings are effective only in the absence of a file-type indication", " from the sender.", " ", " You may include an optional record-format after the word BINARY. This may", " be FIXED (the default) or UNDEFINED. UNDEFINED is used when you need to", " receive binary files in binary mode and have them stored with UNDEFINED", " record format, which is required by certain VMS applications.", " ", " Two additional VMS file types are also supported: IMAGE and LABELED.", " IMAGE means raw block i/o, no interference from RMS, applies to file", " transmission only, and overrides the normal automatica file type", " determination. LABELED means to send or interpret RMS attributes", " with the file.", " ", #else " When TRANSFER MODE is AUTOMATIC (as it is by default), various automatic", " methods (depending on the platform) are used to determine whether a file", " is transferred in text or binary mode; these methods (which might include", " content scan (see SET FILE SCAN below), filename pattern matching (SET FILE" , " PATTERNS), client/server \"kindred-spirit\" recognition, or source file", " record format) supersede the FILE TYPE setting but can, themselves, be", " superseded by including a /BINARY or /TEXT switch in the SEND, GET, or", " RECEIVE command.", " ", " When TRANSFER MODE is MANUAL, the automatic methods are skipped for sending" , " files; the FILE TYPE setting is used instead, which can be superseded on", " a per-command basis with a /TEXT or /BINARY switch.", #endif /* VMS */ " ", #ifndef NOXFER "SET FILE BYTESIZE { 7, 8 }", " Normally 8. If 7, Kermit truncates the 8th bit of all file bytes.", " ", #ifndef NOCSETS "SET FILE CHARACTER-SET name", " Tells the encoding of the local file, ASCII by default.", " The names ITALIAN, PORTUGUESE, NORWEGIAN, etc, refer to 7-bit ISO-646", " national character sets. LATIN1 is the 8-bit ISO 8859-1 Latin Alphabet 1", " for Western European languages.", " NEXT is the 8-bit character set of the NeXT workstation.", " The CPnnn sets are for PCs. MACINTOSH-LATIN is for the Macintosh.", #ifndef NOLATIN2 " LATIN2 is ISO 8859-2 for Eastern European languages that are written with", " Roman letters. Mazovia is a PC code page used in Poland.", #endif /* NOLATIN2 */ #ifdef CYRILLIC " KOI-CYRILLIC, CYRILLIC-ISO, and CP866 are 8-bit Cyrillic character sets.", " SHORT-KOI is a 7-bit ASCII coding for Cyrillic. BULGARIA-PC is a PC code", " page used in Bulgaria", #endif /* CYRILLIC */ #ifdef HEBREW " HEBREW-ISO is ISO 8859-8 Latin/Hebrew. CP862 is the Hebrew PC code page.", " HEBREW-7 is like ASCII with the lowercase letters replaced by Hebrew.", #endif /* HEBREW */ #ifdef GREEK " GREEK-ISO is ISO 8859-7 Latin/Greek. CP869 is the Greek PC code page.", " ELOT-927 is like ASCII with the lowercase letters replaced by Greek.", #endif /* GREEK */ #ifdef KANJI " JAPANESE-EUC, JIS7-KANJI, DEC-KANJI, and SHIFT-JIS-KANJI are Japanese", " Kanji character sets.", #endif /* KANJI */ #ifdef UNICODE " UCS-2 is the 2-byte form of the Universal Character Set.", " UTF-8 is the serialized form of the Universal Character Set.", #endif /* UNICODE */ " Type SET FILE CHAR ? for a complete list of file character sets.", " ", "SET FILE DEFAULT 7BIT-CHARACTER-SET", " When automatically switching among different kinds of files while sending", " this tells the character set to be used for 7-bit text files.", " ", "SET FILE DEFAULT 8BIT-CHARACTER-SET", " This tells the character set to be used for 8-bit text files when", " switching automatically among different kinds of files.", " ", #endif /* NOCSETS */ "SET FILE COLLISION option", " Tells what to do when a file arrives that has the same name as", " an existing file. The options are:", " BACKUP (default) - Rename the old file to a new, unique name and store", " the incoming file under the name it was sent with.", " OVERWRITE - Overwrite (replace) the existing file.", " APPEND - Append the incoming file to the end of the existing file.", " REJECT - Refuse and/or discard the incoming file (= DISCARD).", " RENAME - Give the incoming file a unique name.", " UPDATE - Accept the incoming file only if newer than the existing file.", " ", "SET FILE DESTINATION { DISK, PRINTER, SCREEN, NOWHERE }", " DISK (default): Store incoming files on disk.", " PRINTER: Send incoming files to SET PRINTER device.", " SCREEN: Display incoming files on screen (local mode only).", " NOWHERE: Do not put incoming files anywhere (use for calibration).", " ", "SET FILE DISPLAY option", " Selects the format of the file transfer display for local-mode file", " transfer. The choices are:", " ", " BRIEF A line per file, showing size, mode, status, and throughput.", " SERIAL One dot is printed for every K bytes transferred.", " CRT Numbers are continuously updated on a single screen line.", " This format can be used on any video display terminal.", #ifdef CK_CURSES " FULLSCREEN A fully formatted 24x80 screen showing lots of information.", " This requires a terminal or terminal emulator.", #endif /* CK_CURSES */ " NONE No file transfer display at all.", " ", "SET FILE DOWNLOAD-DIRECTORY [ ]", " The directory into which all received files should be placed. By default,", " received files go into your current directory.", " ", #endif /* NOXFER */ #ifdef CK_CTRLZ "SET FILE EOF { CTRL-Z, LENGTH }", " End-Of-File detection method, normally LENGTH. Applies only to text-mode", " transfers. When set to CTRL-Z, this makes the file sender treat the first", " Ctrl-Z in the input file as the end of file (EOF), and it makes the file", " receiver tack a Ctrl-Z onto the end of the output file if it does not", " already end with Ctrl-Z.", " ", #endif /* CK_CTRLZ */ "SET FILE END-OF-LINE { CR, CRLF, LF }", " Use this command to specify nonstandard line terminators for text files.", " ", #ifndef NOXFER "SET FILE INCOMPLETE { AUTO, KEEP, DISCARD }", " What to do with an incompletely received file: KEEP, DISCARD, or AUTO.", " AUTO (the default) means DISCARD if transfer is in text mode, KEEP if it", " is in binary mode.", " ", #ifdef VMS "SET FILE LABEL { ACL, BACKUP-DATE, NAME, OWNER, PATH } { ON, OFF }", " Tells which items to include (ON) or exclude (OFF) in labeled file", " transfers", " ", #else #ifdef OS2 "SET FILE LABEL { ARCHIVE, READ-ONLY, HIDDEN, SYSTEM, EXTENDED } { ON, OFF }", " Tells which items to include (ON) or exclude (OFF) in labeled file", " transfers.", " ", #endif /* OS2 */ #endif /* VMS */ #ifdef UNIX #ifdef DYNAMIC "SET FILE LISTSIZE number", " Changes the size of the internal wildcard expansion list. Use SHOW FILE", " to see the current size. Use this command to increase the size if you get", " a \"?Too many files\" error. Also see SET FILE STRINGSPACE.", " ", #endif /* DYNAMIC */ #endif /* UNIX */ "SET FILE NAMES { CONVERTED, LITERAL }", " File names are normally CONVERTED to \"common form\" during transmission", " (e.g. lowercase to uppercase, extra periods changed to underscore, etc).", " LITERAL means use filenames literally (useful between like systems). Also", " see SET SEND PATHNAMES and SET RECEIVE PATHNAMES.", " ", #ifdef UNIX "SET FILE OUTPUT { { BUFFERED, UNBUFFERED } [ size ], BLOCKING, NONBLOCKING }", " Lets you control the disk output buffer for incoming files. Buffered", " blocking writes are normal. Nonblocking writes might be faster on some", " systems but might also be risky, depending on the underlying file service.", " Unbuffered writes might be useful in critical applications to ensure that", " cached disk writes are not lost in a crash, but will probably also be", " slower. The optional size parameter after BUFFERED or UNBUFFERED lets you", " change the disk output buffer size; this might make a difference in", " performance.", " ", #endif /* UNIX */ #ifdef PATTERNS "SET FILE PATTERNS { ON, OFF, AUTO }", " ON means to use filename pattern lists to determine whether to send a file", " in text or binary mode. OFF means to send all files in the prevailing", " mode. AUTO (the default) is like ON if the other Kermit accepts Attribute", " packets and like OFF otherwise. FILE PATTERNS are used only if FILE SCAN", " is OFF (see SET FILE SCAN).", " ", "SET FILE BINARY-PATTERNS [ [ ... ] ]", " Zero or more filename patterns which, if matched, cause a file to be sent", " in binary mode when FILE PATTERNS are ON. HELP WILDCARDS for a description" , " of pattern syntax. SHOW PATTERNS to see the current file pattern lists.", " ", "SET FILE TEXT-PATTERNS [ [ ... ] ]", " Zero or more filename patterns which, if matched, cause a file to be sent", " in text mode when FILE PATTERNS is ON; if a file does not match a text or", " binary pattern, the prevailing SET FILE TYPE is used.", " ", #endif /* PATTERNS */ #ifdef VMS "SET FILE RECORD-LENGTH number", " Sets the record length for received files of type BINARY. Use this to", " receive VMS BACKUP savesets or other fixed-format files that do not use", " the default record length of 512.", " ", #endif /* VMS */ "SET FILE SCAN { ON [ size ], OFF }", " If TRANSFER MODE is AUTOMATIC and FILE SCAN is ON (as it is by default)", " Kermit peeks at the file's contents to see if it's text or binary. Use", " SET FILE SCAN OFF to disable file peeking, while still keeping TRANSFER", " MODE automatic to allow name patterns and other methods. The optional", " size is the number of file bytes to scan, 49152 by default. -1 means to", " scan the whole file. Also see SET FILE PATTERNS.", " ", #ifdef UNIX #ifdef DYNAMIC "SET FILE STRINGSPACE number", " Changes the size (in bytes) of the internal buffer that holds lists of", " filenames such as wildcard expansion lists. Use SHOW FILE to see the", " current size. Use this command to increase the size if you get a", " \"?String space exhausted\" error. Also see SET FILE LISTSIZE.", " ", #endif /* DYNAMIC */ #endif /* UNIX */ #ifdef UNICODE "SET FILE UCS BOM { ON, OFF }", " Whether to write a Byte Order Mark when creating a UCS-2 file.", " ", "SET FILE UCS BYTE-ORDER { BIG-ENDIAN, LITTLE-ENDIAN }", " Byte order to use when creating UCS-2 files, and to use when reading UCS-2", " files that do not start with a Byte Order Mark.", " ", #endif /* UNICODE */ "SET FILE WARNING { ON, OFF }", " SET FILE WARNING is superseded by the newer command, SET FILE", " COLLISION. SET FILE WARNING ON is equivalent to SET FILE COLLISION RENAME", " and SET FILE WARNING OFF is equivalent to SET FILE COLLISION OVERWRITE.", #endif /* NOXFER */ "" }; static char *hmxyhsh[] = { "Syntax: SET HANDSHAKE { NONE, XON, LF, BELL, ESC, CODE number }", " Character to use for half duplex line turnaround handshake during file", " transfer. C-Kermit waits for this character from the other computer", " before sending its next packet. Default is NONE; you can give one of the", " other names like BELL or ESC, or use SET HANDSHAKE CODE to specify the", " numeric code value of the handshake character. Type SET HANDSH ? for a", " complete list of possibilities.", "" }; #ifndef NOSERVER static char *hsetsrv[] = { "SET SERVER CD-MESSAGE {ON,OFF}", " Tells whether the server, after successfully executing a REMOTE CD", " command, should send the contents of the new directory's READ.ME", " (or similar) file to your screen.", " ", "SET SERVER CD-MESSAGE FILE name", " Tells the name of the file to be displayed as a CD-MESSAGE, such as", " READ.ME (SHOW SERVER tells the current CD-MESSAGE FILE name).", " To specify more than one filename to look for, use {{name1}{name2}..}.", " Synonym: SET CD MESSAGE FILE .", " ", "SET SERVER DISPLAY {ON,OFF}", " Tells whether local-mode C-Kermit during server operation should put a", " file transfer display on the screen. Default is OFF.", " ", "SET SERVER GET-PATH [ directory [ directory [ ... ] ] ]", " Tells the C-Kermit server where to look for files whose names it receives", " from client GET commands when the names are not fully specified pathnames.", " Default is no GET-PATH, so C-Kermit looks only in its current directory.", " ", "SET SERVER IDLE-TIMEOUT seconds", " Idle time limit while in server mode, 0 for no limit.", #ifndef OS2 " NOTE: SERVER IDLE-TIMEOUT and SERVER TIMEOUT are mutually exclusive.", #endif /* OS2 */ " ", "SET SERVER KEEPALIVE {ON,OFF}", " Tells whether C-Kermit should send \"keepalive\" packets while executing", " REMOTE HOST commands, which is useful in case the command takes a long", " time to produce any output and therefore might cause the operation to time", " out. ON by default; turn it OFF if it causes trouble with the client or", " slows down the server too much.", " ", "SET SERVER LOGIN [ username [ password [ account ] ] ]", " Sets up a username and optional password which must be supplied before", " the server will respond to any commands other than REMOTE LOGIN. The", " account is ignored. If you enter SET SERVER LOGIN by itself, then login", " is no longer required. Only one SET SERVER LOGIN command can be in effect", " at a time; C-Kermit does not support multiple user/password pairs.", " ", "SET SERVER TIMEOUT n", " Server command wait timeout interval, how often the C-Kermit server issues", " a NAK while waiting for a command packet. Specify 0 for no NAKs at all.", " Default is 0.", "" }; #endif /* NOSERVER */ static char *hmhrmt[] = { #ifdef NEWFTP "The REMOTE command sends file management instructions or other commands", "to a Kermit or FTP server. If you have a single connection, the command is", "directed to the server you are connected to; if you have multiple connections" , "the command is directed according to your GET-PUT-REMOTE setting.", #else "The REMOTE command sends file management instructions or other commands", "to a Kermit server. There should already be a Kermit running in server", "mode on the other end of the connection.", #endif /* NEWFTP */ "Type REMOTE ? to see a list of available remote commands. Type HELP REMOTE", "xxx to get further information about a particular remote command xxx.", " ", "All REMOTE commands except LOGIN and LOGOUT have R-command shortcuts;", "for example, RDIR for REMOTE DIR, RCD for REMOTE CD, etc.", " ", #ifdef NEWFTP #ifdef LOCUS "Also see: HELP SET LOCUS, HELP FTP, HELP SET GET-PUT-REMOTE.", #else "Also see: HELP FTP, HELP SET GET-PUT-REMOTE.", #endif /* LOCUS */ #else #ifdef LOCUS "Also see: HELP SET LOCUS.", #endif /* LOCUS */ #endif /* NEWFTP */ "" }; #ifndef NOSPL static char *ifhlp[] = { "Syntax: IF [NOT] condition commandlist", " ", "If the condition is (is not) true, do the commandlist. The commandlist", "can be a single command, or a list of commands separated by commas and", "enclosed in braces. The condition can be a single condition or a group of", "conditions separated by AND (&&) or OR (||) and enclosed in parentheses.", "If parentheses are used they must be surrounded by spaces. Examples:", " ", " IF EXIST oofa.txt ", " IF ( EXIST oofa.txt || = \\v(nday) 3 ) ", " IF ( EXIST oofa.txt || = \\v(nday) 3 ) { , , ... }", " ", "The conditions are:", " ", " SUCCESS - The previous command succeeded", " OK - Synonym for SUCCESS", " FAILURE - The previous command failed", " ERROR - Synonym for FAILURE", " FLAG - Succeeds if SET FLAG ON, fails if SET FLAG OFF", " BACKGROUND - C-Kermit is running in the background", #ifdef CK_IFRO " FOREGROUND - C-Kermit is running in the foreground", " REMOTE-ONLY - C-Kermit was started with the -R command-line option", #else " FOREGROUND - C-Kermit is running in the foreground", #endif /* CK_IFRO */ " KERBANG - A Kerbang script is running", " ALARM - SET ALARM time has passed", " ASKTIMEOUT - The most recent ASK, ASKQ, GETC, or GETOK timed out", " EMULATION - Succeeds if executed while in CONNECT mode", #ifdef OS2 " TAPI - Current connection is via a Microsoft TAPI device", #endif /* OS2 */ " ", " MS-KERMIT - Program is MS-DOS Kermit", " C-KERMIT - Program is C-Kermit", " K-95 - Program is Kermit 95", " GUI - Program runs in a GUI window", " ", " AVAILABLE CRYPTO - Encryption is available", " AVAILABLE KERBEROS4 - Kerberos 4 authentication is available", " AVAILABLE KERBEROS5 - Kerberos 5 authentication is available", " AVAILABLE NTLM - NTLM authentication is available", " AVAILABLE SRP - SRP authentication is available", " AVAILABLE SSL - SSL/TLS authentication is available", " MATCH string pattern - Succeeds if string matches pattern", #ifdef CKFLOAT " FLOAT number - Succeeds if floating-point number", #endif /* CKFLOAT */ " COMMAND word - Succeeds if word is built-in command", " DEFINED variablename or macroname - The named variable or macro is defined", " DECLARED arrayname - The named array is declared", " NUMERIC variable or constant - The variable or constant is numeric", " EXIST filename - The named file exists", " ABSOLUTE filename - The filename is absolute, not relative", #ifdef CK_TMPDIR " DIRECTORY string - The string is the name of a directory", #endif /* CK_TMPDIR */ #ifdef UNIX " LINK string - The string is a symbolic link", #endif /* UNIX */ " READABLE filename - Succeeds if the file is readable", " WRITEABLE filename - Succeeds if the file is writeable", #ifdef ZFCDAT " NEWER file1 file2 - The 1st file is newer than the 2nd one", #endif /* ZFCDAT */ " OPEN { READ-FILE,SESSION-LOG,...} - The given file or log is open", #ifndef NOLOCAL " OPEN CONNECTION - A connection is open", #endif /* NOLOCAL */ " KBHIT - A key has been pressed", " ", " VERSION - equivalent to \"if >= \\v(version) ...\"", " COUNT - subtract one from COUNT, execute the command if the result is", " greater than zero (see SET COUNT)", " ", " EQUAL s1 s2 - s1 and s2 (character strings or variables) are equal", " LLT s1 s2 - s1 is lexically (alphabetically) less than s2", " LGT s1 s1 - s1 is lexically (alphabetically) greater than s2", " ", " = n1 n2 - n1 and n2 (numbers or variables containing numbers) are equal", " < n1 n2 - n1 is arithmetically less than n2", " <= n1 n2 - n1 is arithmetically less than or equal to n2", " > n1 n2 - n1 is arithmetically greater than n2", " >= n1 n2 - n1 is arithmetically greater than or equal to n2", " ", " (number by itself) - fails if the number is 0, succeeds otherwise", " ", " TRUE - always succeeds", " FALSE - always fails", " ", "The IF command may be followed on the next line by an ELSE command. Example:", " ", " IF < \\%x 10 ECHO It's less", " ELSE echo It's not less", " ", "It can also include an ELSE part on the same line if braces are used:", " ", " IF < \\%x 10 { ECHO It's less } ELSE { ECHO It's not less }", " ", "Also see HELP WILDCARD (for IF MATCH pattern syntax).", "" }; static char *hmxxeval[] = { "Syntax: EVALUATE variable expression", " Evaluates the expression and assigns its value to the given variable.", " The expression can contain numbers and/or numeric-valued variables or", " functions, combined with mathematical operators and parentheses in", " traditional notation. Operators include +-/*(), etc. Example:", " EVALUATE \\%n (1+1) * (\\%a / 3).", " ", " NOTE: Prior to C-Kermit 7.0, the syntax was \"EVALUATE expression\"", " (no variable), and the result was printed. Use SET EVAL { OLD, NEW }", " to choose the old or new behavior, which is NEW by default.", " ", "Alse see: HELP FUNCTION EVAL.", "" }; #endif /* NOSPL */ static char *hmxxexit[] = { "Syntax: EXIT (or QUIT) [ number [ text ] ]", " Exits from the Kermit program, closing all open files and devices.", " If a number is given it becomes Kermit's exit status code. If text is", " included, it is printed. Also see SET EXIT.", "" }; #ifndef NOSPL static char *ifxhlp[] = { "\ Syntax: XIF condition { commandlist } [ ELSE { commandlist } ]", " Obsolete. Same as IF (see HELP IF).", "" }; static char *forhlp[] = { "\ Syntax: FOR variablename initial-value final-value increment { commandlist }", " FOR loop. Execute the comma-separated commands in the commandlist the", " number of times given by the initial value, final value and increment.", " Example: FOR \\%i 10 1 -1 { pause 1, echo \\%i }", "" }; static char *whihlp[] = { "\ Syntax: WHILE condition { commandlist }", " WHILE loop. Execute the comma-separated commands in the bracketed", " commandlist while the condition is true. Conditions are the same as for", " IF commands.", "" }; static char *swihlp[] = { "Syntax: SWITCH { case-list }", " Selects from a group of commands based on the value of a variable.", " The case-list is a series of lines like these:", " ", " :x, command, command, ..., break", " ", " where \"x\" is a possible value for the variable. At the end of the", " case-list, you can put a \"default\" label to catch when the variable does", " not match any of the labels:", " ", " :default, command, command, ...", " ", "The case label \"x\" can be a character, a string, a variable, a function", "invocation, a pattern, or any combination of these. See HELP WILDCARDS", "for information about patterns.", ""}; static char *openhlp[] = { "Syntax: OPEN mode filename", " For use with READ and WRITE commands. Open the local file in the", " specified mode: READ, WRITE, or APPEND. !READ and !WRITE mean to read", " from or write to a system command rather than a file. Examples:", " ", " OPEN READ oofa.txt", " OPEN !READ sort foo.bar", "" }; static char *hxxask[] = { "Syntax: ASK [ switches ] variablename [ prompt ]", "Example: ASK \\%n { What is your name\\? }", " Issues the prompt and defines the variable to be whatever is typed in", " response, up to the terminating carriage return. Use braces to preserve", " leading and/or trailing spaces in the prompt.", " ", "Syntax: ASKQ [ switches ] variablename [ prompt ]", "Example: ASKQ \\%p { Password:}", " Like ASK except the response does not echo on the screen or, if specified", " it echoes as asterisks or other specified character.", " ", "Switches:", " /DEFAULT:text", " Text to supply if the user enters a blank response or the /TIMEOUT", " limit expired with no response.", " ", " /ECHO:char", " (ASKQ only) Character to be echoed each time the user presses a key", " corresponding to a printable character. This lets users see what they are", " doing when they are typing (e.g.) passwords, and makes editing easier.", " ", #ifdef OS2 " /POPUP", " The prompt and response dialog takes place in a text-mode popup.", " K95 only; in C-Kermit this switch is ignored.", " ", #ifdef KUI " /GUI", " The prompt and response dialog takes place in a GUI popup.", " K95 GUI version only; in C-Kermit and the K95 console version,", " this switch is ignored.", " ", #endif /* KUI */ #endif /* OS2 */ " /TIMEOUT:number", " If the response is not entered within the given number of seconds, the", " command fails. This is equivalent to setting ASK-TIMER to a positive", " number, except it applies only to this command. Also see SET ASK-TIMER.", " NOTE: If a /DEFAULT: value was also given, it is supplied automatically", " upon timeout and the command does NOT fail.", " ", " /QUIET", " Suppresses \"?Timed out\" message when /TIMEOUT is given and user doesn't", " respond within the time limit.", ""}; static char *hxxgetc[] = { "Syntax: GETC variablename [ prompt ]", "Example: GETC \\%c { Type any character to continue...}", " Issues the prompt and sets the variable to the first character you type.", " Use braces to preserve leading and/or trailing spaces in the prompt.", " ", "Also see SET ASK-TIMER.", ""}; static char *hmxytimer[] = { "Syntax: SET ASK-TIMER number", " For use with ASK, ASKQ, GETOK, and GETC. If ASK-TIMER is set to a number", " greater than 0, these commands will time out after the given number of", " seconds with no response. This command is \"sticky\", so to revert to", " \ untimed ASKs after a timed one, use SET ASK-TIMER 0. Also see IF ASKTIMEOUT.", ""}; static char *hxxdot[] = { "Syntax: . ", " Assigns the value to the variable in the manner indicated by the", " assignment operator:", " = Copies without evaluation (like DEFINE).", " := Copies with evaluation (like ASSIGN).", " ::= Copies with arithmetic evaluation (like EVALUATE).", ""}; static char *hxxdef[] = { "Syntax: DEFINE name [ definition ]", " Defines a macro or variable. Its value is the definition, taken", " literally. No expansion or evaluation of the definition is done. Thus", " if the definition includes any variable or function references, their", " names are included, rather than their values (compare with ASSIGN). If", " the definition is omitted, then the named variable or macro is undefined.", " ", "A typical macro definition looks like this:", " ", " DEFINE name command, command, command, ...", " ", "for example:", " ", " DEFINE vax set parity even, set duplex full, set flow xon/xoff", " ", "which defines a Kermit command macro called 'vax'. The definition is a", "comma-separated list of Kermit commands. Use the DO command to execute", "the macro, or just type its name, followed optionally by arguments.", " ", "The definition of a variable can be anything at all, for example:", " ", " DEFINE \\%a Monday", " DEFINE \\%b 3", " ", "These variables can be used almost anywhere, for example:", " ", " ECHO Today is \\%a", " SET BLOCK-CHECK \\%b", "" }; static char *hxxass[] = { "Syntax: ASSIGN variablename string.", "Example: ASSIGN \\%a My name is \\%b.", " Assigns the current value of the string to the variable (or macro).", " The definition string is fully evaluated before it is assigned, so that", " the values of any variables that are contained are used, rather than their", " names. Compare with DEFINE. To illustrate the difference, try this:", " ", " DEFINE \\%a hello", " DEFINE \\%x \\%a", " ASSIGN \\%y \\%a", " DEFINE \\%a goodbye", " ECHO \\%x \\%y", " ", " This prints 'goodbye hello'.", "" }; static char *hxxdec[] = { "Syntax: DECREMENT variablename [ number ]", " Decrement (subtract one from) the value of a variable if the current value", " is numeric. If the number argument is given, subtract that number", " instead.", " ", "Examples: DECR \\%a, DECR \\%a 7, DECR \\%a \\%n", "" }; static char *hxxinc[] = { "Syntax: INCREMENT variablename [ number ]", " Increment (add one to) the value of a variable if the current value is", " numeric. If the number argument is given, add that number instead.", " ", "Examples: INCR \\%a, INCR \\%a 7, INCR \\%a \\%n", "" }; #endif /* NOSPL */ #ifdef ANYX25 #ifndef IBMX25 static char *hxxpad[] = { "Syntax: PAD command", "X.25 PAD commands:", " ", " PAD CLEAR - Clear the virtual call", " PAD STATUS - Return the status of virtual call", " PAD RESET - Send a reset packet", " PAD INTERRUPT - Send an interrupt packet", ""}; #endif /* IBMX25 */ static char *hxyx25[] = { "Syntax: SET X.25 option { ON [ data ], OFF }", " ", "X.25 call options:", " CLOSED-USER-GROUP { ON index, OFF }", " Enable or disable closed user group call, where index is the group", " index, 0 to 99.", " REVERSE-CHARGE { ON, OFF }", " Tell whether you want to reverse the charges for the call.", " CALL-USER-DATA { ON string, OFF }", " Specify call user-data for the X.25 call.", ""}; #endif /* ANYX25 */ static char *hxyprtr[] = { #ifdef PRINTSWI "Syntax: SET PRINTER [ switches ] [ name ]", " ", " Specifies the printer to be used for transparent-print, autoprint, and", " screen-dump material during terminal emulation, as well as for the PRINT", " command, plus various options governing print behavior.", " ", "Switches for specifying the printer by type:", " ", "/NONE", " Include this switch to specify that all printer actions should simply be", " skipped. Use this, for example, if you have no printer.", " ", "/DOS-DEVICE[:name]", " Include this to declare a DOS printer and to specify its name, such as", " PRN, LPT1, etc.", " ", #ifdef NT "/WINDOWS-QUEUE[:[queue-name]]", " Include this to declare a Windows printer and specify its queue name.", " Type question mark (?) after the colon (:) to see a list of known queue", " names. If the colon is absent, the switch indicates the currently", " selected printer is a Windows Print Queue. If the colon is provided", " and the name is absent, the Windows Print Queue chosen as the Default", " Printer is selected.", " ", #endif /* NT */ "/FILE[:name]", " Specifies that all printer material is to be appended to the named file,", " rather than being sent to a printer. If the file does not exist, it is", " created the first time any material is to be printed.", " ", "/PIPE[:name]", " Specifies that all printer material is to be sent as standard input to", " the program or command whose name is given. Example:", " ", " SET PRINTER /PIPE:{textps > lpt1}", " ", "If you give a printer name without specifying any of these switches, then it", "is assumed to be a DOS printer device or filename unless the name given", "(after removing enclosing braces, if any) starts with \"|\", \ in which case it", "is a pipe. Examples:", " ", " SET PRINTER LPT1 <-- DOS device", " SET PRINTER {| textps > lpt1} <-- Pipe", " ", "The next group of switches tells whether the printer is one-way or", "bidirectional (two-way):", " ", "/OUTPUT-ONLY", " Include this to declare the printer capable only of receiving material to", " be printed, but not sending anything back. This is the normal kind of", " printer, Kermit's default kind, and the opposite of /BIDIRECTIONAL.", " ", "/BIDIRECTIONAL", " Include this to declare the printer bidirectional. This is the opposite ", " of /OUTPUT-ONLY. You can also use this option with serial printers, even", " if they aren't bidirectional, in case you need to specify speed, flow", " control, or parity.", " ", "The next group applies only to bidirectional and/or serial printers:", " ", "/FLOW-CONTROL:{NONE,XON/XOFF,RTS/CTS,KEEP}", " Flow control to use with a serial bidirectional printer, default KEEP;", #ifdef NT " i.e. use whatever the Windows driver for the port normally uses.", #else " i.e. use whatever the OS/2 driver for the port normally uses.", #endif /* NT */ " ", "/PARITY:{NONE,EVEN,ODD,SPACE,MARK}", " Parity to use with a serial printer, default NONE; i.e. use 8 data bits", " and no parity. If you omit the colon and the keyword, NONE is selected.", " ", "/SPEED:number", " Interface speed, in bits per second, to use with a serial printer, such as", " 2400, 9600, 19200, etc. Type SET PRINTER /SPEED:? for a list of possible", " speeds.", " ", "The next group deals with print jobs -- how to identify them, how to start", "them, how to terminate them:", " ", "/TIMEOUT[:number]", " Used with host-directed transparent or auto printing, this is the number", " of seconds to wait after the host closes the printer before terminating", " the print job if the printer is not opened again during the specified", " amount of time.", " ", "/JOB-HEADER-FILE[:filename]", " The name of a file to be sent to the printer at the beginning of each", " print job, as a burst page, or to configure the printer. Normally no file", " is is sent.", " ", "/END-OF-JOB-STRING[:string]", " String of characters to be sent to the printer at the end of the print", " job, usually used to force the last or only page out of the printer. When", " such a string is needed, it usually consists of a single formfeed: \"set", " printer /end-of-job:{\\12}\". No end-of-job string is sent unless you", " specify one with this option. If the string contains any spaces or", " control characters (even in backslash notation, as above), enclose it in", " braces.", " ", "The next group is for use with printers that print only PostScript:", " ", "/POSTSCRIPT or /PS", " Indicates that K95 should convert all text to PostScript before sending", " it to the printer. The fixed-pitch Courier-11 font is used.", " ", "/WIDTH:number", " Specifies the width of the page in characters. If this switch is not", " given, 80 is used.", " ", "/HEIGHT:number", " Specifies the height of the page in lines. If this switch is not given", " 66 is used.", " ", "/NOPOSTSCRIPT or /NOPS", " Indicates that K95 should not convert all text to PostScript before", " sending it to the printer.", " ", "The final switch is for use with AutoPrint mode and Screen Dumps", " ", "/CHARACTER-SET:", " Specifies the character set used by the printer which may be different", " from both the character set used by the host and by the local computer.", " The default value is CP437.", " ", "SHOW PRINTER displays your current printer settings.", #else #ifdef UNIX "Syntax: SET PRINTER [ { |command, filename } ]", " Specifies the command (such as \"|lpr\") or filename to be used by the", " PRINT command. If a filename is given, each PRINT command appends to the", " given file. If the SET PRINTER argument contains spaces, it must be", " enclosed in braces, e.g. \"set printer {| lpr -Plaser}\". If the argument", " is omitted the default value is restored. SHOW PRINTER lists the current", " printer. See HELP PRINT for further info.", #else "Sorry, SET PRINTER not available yet.", #endif /* UNIX */ #endif /* PRINTSWI */ ""}; #ifdef OS2 #ifdef BPRINT static char *hxybprtr[] = { "Syntax: SET BPRINTER [ portname speed [ parity [ flow-control ] ] ]", " (Obsolete, replaced by SET PRINTER /BIDIRECTIONAL.)", ""}; #endif /* BPRINT */ #endif /* OS2 */ static char *hxyexit[] = { "Syntax: SET EXIT HANGUP { ON, OFF }", " When ON (which is the default), C-Kermit executes an implicit HANGUP and", " CLOSE command on the communications device or connection when it exits.", " When OFF, Kermit skips this sequence.", " ", "Syntax: SET EXIT ON-DISCONNECT { ON, OFF }", " When ON, C-Kermit EXITs automatically when a network connection", " is terminated either by the host or by issuing a HANGUP command.", " ", "Syntax: SET EXIT STATUS number", #ifdef NOSPL " Set C-Kermit's program return code to the given number.", #else " Set C-Kermit's program return code to the given number, which can be a", " constant, variable, function result, or arithmetic expression.", #endif /* NOSPL */ " ", "Syntax: SET EXIT WARNING { ON, OFF, ALWAYS }", " When EXIT WARNING is ON, issue a warning message and ask for confirmation", " before EXITing if a connection to another computer might still be open.", " When EXIT WARNING is ALWAYS, confirmation is always requested. When OFF", " it is never requested. The default is ON.", "" }; #ifndef NOSPL static char *hxxpau[] = { "Syntax: PAUSE [ { number-of-seconds, hh:mm:ss } ]", "Example: PAUSE 3 or PAUSE 14:52:30", " Do nothing for the specified number of seconds or until the given time of", " day in 24-hour hh:mm:ss notation. If the time of day is earlier than the", " current time, it is assumed to be tomorrow. If no argument given, one", " second is used. The pause can be interrupted by typing any character on", " the keyboard unless SLEEP CANCELLATION is OFF. If interrupted, PAUSE", " fails, otherwise it succeeds. Synonym: SLEEP.", "" }; static char *hxxmsl[] = { "Syntax: MSLEEP [ number ]", "Example: MSLEEP 500", " Do nothing for the specified number of milliseconds; if no number given,", " 100 milliseconds.","" }; #endif /* NOSPL */ #ifndef NOPUSH extern int nopush; static char *hxxshe[] = { "Syntax: !, @, RUN, PUSH, or SPAWN, optionally followed by a command.", " Gives the command to the local operating system's command processor, and", " displays the results on the screen. If the command is omitted, enters the", " system's command line interpreter or shell; exit from it (the command for", " this is usually EXIT or QUIT or LOGOUT) to return to Kermit.", "" }; #endif /* NOPUSH */ #ifndef NOXMIT static char *hxxxmit[] = { "Syntax: TRANSMIT [ switches ] filename", " Sends the contents of a file, without any error checking or correction,", " to the computer on the other end of your SET LINE or SET HOST connection", " (or if C-Kermit is in remote mode, displays it on the screen). The", " filename is the name of a single file (no wildcards) to be sent or, if", " the /PIPE switch is included, the name of a command whose output is to be", " sent.", " ", " The file is sent according to your current FILE TYPE setting (BINARY or", " TEXT), which you can override with a /BINARY or /TEXT switch without", " changing the global setting. In text mode, it is sent a line at a time,", " with carriage return at the end of each line (as if you were typing it at", " your keyboard), and C-Kermit waits for a linefeed to echo before sending", " the next line; use /NOWAIT to eliminate the feedback requirement. In", " binary mode, it is sent a character at a time, with no feedback required.", " ", " Normally the transmitted material is echoed to your screen. Use SET", " TRANSMIT ECHO OFF or the /NOECHO switch to suppress echoing. Note that", " TRANSMIT /NOECHO /NOWAIT /BINARY is a special case, that more or less", " blasts the file out at full speed.", " ", #ifndef NOCSETS " Character sets are translated according to your current FILE and TERMINAL", " CHARACTER-SET settings when TRANSMIT is in text mode. Include /TRANSPARENT" , " to disable character-set translation in text mode (/TRANSPARENT implies", " /TEXT).", " ", #endif /* NOCSETS */ " There can be no guarantee that the other computer will receive the file", " correctly and completely. Before you start the TRANSMIT command, you", " must put the other computer in data collection mode, for example by", " starting a text editor. TRANSMIT may be interrupted by Ctrl-C. Synonym:", " XMIT. See HELP SET TRANSMIT for further information.", "" }; #endif /* NOXMIT */ #ifndef NOCSETS static char *hxxxla[] = { "Syntax: TRANSLATE file1 cs1 cs2 [ file2 ]", " Translates file1 from the character set cs1 into the character set cs2", " and stores the result in file2. The character sets can be any of", " C-Kermit's file character sets. If file2 is omitted, the translation", " is displayed on the screen. An appropriate intermediate character-set", " is chosen automatically, if necessary. Synonym: XLATE. Example:", " ", " TRANSLATE lasagna.lat latin1 italian lasagna.nrc", " ", " Multiple files can be translated if file2 is a directory or device name,", " rather than a filename, or if file2 is omitted.", "" }; #endif /* NOCSETS */ #ifndef NOSPL static char *hxxwai[] = { "Syntax: WAIT { number-of-seconds, hh:mm:ss } [ ]", " ", "Examples:", " wait 5 cd cts", " wait 23:59:59 cd", " ", " Waits up to the given number of seconds or the given time of day for the", " specified item or event, which can be FILE, the name(s) of one or more", " modem signals, or nothing. If nothing is specified, WAIT acts like SLEEP.", " If one or more modem signal names are given, Kermit waits for the specified" , " modem signals to appear on the serial communication device.", " Sets FAILURE if the signals do not appear in the given time or interrupted", " from the keyboard during the waiting period.", " ", "Signals:", " cd = Carrier Detect;", " dsr = Dataset Ready;", " cts = Clear To Send;", " ri = Ring Indicate.", " ", "If you want Kermit to wait for a file event, then the syntax is:", " ", " WAIT ", " ", " Nesting is possible with {}()[]<> but not with quotes or apostrophes.", " ", "Returns integer:", " Number of words in source string.", " ", "Also see:", " HELP FUNCTION WORD", "" }; /* D O H F U N C -- Give help for a function */ int dohfunc(xx) int xx; { /* int x; */ if (xx == -3) { printf("\n Type SHOW FUNCTIONS to see a list of available functions.\n" ); printf( " Type HELP FUNCTION for help on a particular function.\n"); printf( " For function settings use HELP SET FUNCTION and SHOW SCRIPTS.\n\n"); return(0); } if (xx == FN_WORD) /* Long help message */ return(hmsga(hmfword)); printf("\n"); switch (xx) { case FN_IND: /* Index (of string 1 in string 2) */ case FN_RIX: /* Rindex (index from right) */ printf("\\f%sindex(s1,s2,n1,n2)\n\ s1 = string to look for.\n\ s2 = string to look in.\n\ n1 = optional 1-based starting position, default = 1.\n\ n2 = optional desired occurrence number, default = 1.\n", xx == FN_RIX ? "r" : "" ); printf("Returns integer:\n\ 1-based position of %smost occurrence of s1 in s2, ignoring the %smost\n\ (n1-1) characters in s2; returns 0 if s1 not found in s2.\n", xx == FN_IND ? "left" : "right", xx == FN_IND ? "left" : "right" ); break; case FN_COUNT: /* Count occurrences of s1 in s2 */ printf("\\fcount(s1,s2,n1)\n\ s1 = string or character to look for.\n\ s2 = string to look in.\n\ n1 = optional 1-based starting position, default = 1.\n"); printf("Returns integer:\n\ Number of occurrences of s1 in s2, 0 or more.\n"); break; case FN_SEARCH: /* Search for pattern */ case FN_RSEARCH: /* Search for pattern from right */ printf("\\f%ssearch(s1,s2,n1,n2)\n\ s1 = pattern to look for.\n\ s2 = string to look in.\n\ n1 = optional 1-based offset, default = 1.\n\ n2 = optional desired occurrence of match, default = 1.\n", xx == FN_RSEARCH ? "r" : "" ); printf("Returns integer:\n\ 1-based position of %smost match for s1 in s2, ignoring the %smost\n\ (n1-1) characters in s2; returns 0 if no match.\n", xx == FN_SEARCH ? "left" : "right", xx == FN_SEARCH ? "left" : "right" ); printf(" See HELP WILDCARDS for info about patterns.\n"); break; case FN_LEN: /* Length (of string) */ printf("\\flength(s1)\n\ s1 = string.\n"); printf("Returns integer:\n\ Length of string s1.\n"); break; case FN_LIT: /* Literal (don't expand the string) */ printf("\\fliteral(s1)\n\ s1 = string.\n"); printf("Returns string:\n\ s1 literally without evaluation.\n"); break; case FN_LOW: /* Lower (convert to lowercase) */ printf("\\flower(s1)\n\ s1 = string.\n"); printf("Returns string:\n\ s1 with uppercase letters converted to lowercase.\n"); break; case FN_MAX: /* Max (maximum) */ printf("\\fmaximum(n1,n2)\n\ n1 = integer.\n\ n2 = integer.\n"); printf("Returns integer:\n\ The greater of n1 and n2.\n"); break; case FN_MIN: /* Min (minimum) */ printf("\\fminimum(n1,n2)\n\ n1 = integer.\n\ n2 = integer.\n"); printf("Returns integer:\n\ The lesser of n1 and n2.\n"); break; case FN_MOD: /* Mod (modulus) */ printf("\\fmodulus(n1,n2)\n\ n1 = integer.\n\ n2 = integer.\n"); printf("Returns integer:\n\ The remainder after dividing n1 by n2.\n"); break; case FN_EVA: /* Eval (evaluate arith expression) */ printf("\\fevaluate(e)\n\ e = arithmetic expression.\n"); printf("Returns integer:\n\ The result of evaluating the expression.\n"); break; case FN_SUB: /* Substr (substring) */ printf("\\fsubstring(s1,n1,n2)\n\ s1 = string.\n\ n1 = integer, 1-based starting position, default = 1.\n\ n2 = integer, length, default = length(s1) - n1 + 1.\n"); printf("Returns string:\n\ Substring of s1 starting at n1, length n2.\n"); break; case FN_UPP: /* Upper (convert to uppercase) */ printf("\\fupper(s1)\n\ s1 = string.\n"); printf("Returns string:\n\ s1 with lowercase letters converted to uppercase.\n"); break; case FN_REV: /* Reverse (a string) */ printf("\\freverse(s1)\n\ s1 = string.\n"); printf("Returns string:\n\ s1 with its characters in reverse order.\n"); break; case FN_REP: /* Repeat (a string) */ printf("\\frepeat(s1,n1)\n\ s1 = string.\n\ n1 = integer.\n"); printf("Returns string:\n\ s1 repeated n1 times.\n"); break; case FN_EXE: /* Execute (a macro) */ printf("\\fexecute(m1,a1,a2,a3,...)\n\ m1 = macro name.\n\ a1 = argument 1.\n\ a2 = argument 2, etc\n"); printf("Returns string:\n\ The return value of the macro (HELP RETURN for further info).\n"); break; case FN_LPA: /* LPAD (left pad) */ case FN_RPA: /* RPAD (right pad) */ printf("\\f%cpad(s1,n1,c1)\n\ s1 = string.\n\ n1 = integer.\n\ c1 = character, default = space.\n", xx == FN_LPA ? 'l' : 'r'); printf("Returns string:\n\ s1 %s-padded with character c1 to length n1.\n", xx == FN_LPA ? "left" : "right"); break; case FN_DEF: /* Definition of a macro, unexpanded */ printf("\\fdefinition(m1)\n\ m1 = macro name.\n"); printf("Returns string:\n\ Literal definition of macro m1.\n"); break; case FN_CON: /* Contents of a variable, ditto */ printf("\\fcontents(v1)\n\ v1 = variable name such as \\%%a.\n"); printf("Returns string:\n\ Literal definition of variable v1, evaluated one level only.\n"); break; case FN_FIL: /* Next file */ printf("\\fnextfile()\n"); printf("Returns string:\n\ Name of next file from list created by most recent \\f[r]files() or\n\ \\f[r]dir()invocation, or an empty string if there are no more files in\n\ the list.\n"); break; case FN_FC: /* File count */ printf("\\ffiles(f1[,&a]) - File list.\n\ f1 = file specification, possibly containing wildcards.\n\ &a = optional name of array to assign file list to.\n"); printf("Returns integer:\n\ The number of regular files that match f1. Use with \\fnextfile().\n"); break; case FN_CHR: /* Character (like BASIC CHR$()) */ printf("\\fcharacter(n1)\n\ n1 = integer.\n"); printf("Returns character:\n\ The character whose numeric code is n1.\n"); break; case FN_RIG: /* Right (like BASIC RIGHT$()) */ printf("\\fright(s1,n1)\n\ s1 = string.\n\ n1 = integer, default = length(s1).\n"); printf("Returns string:\n\ The rightmost n1 characters of string s1.\n"); break; case FN_LEF: /* Left (like BASIC LEFT$()) */ printf("\\fleft(s1,n1)\n\ s1 = string.\n\ n1 = integer, default = length(s1).\n"); printf("Returns string:\n\ The leftmost n1 characters of string s1.\n"); break; case FN_COD: /* Code value of character */ printf("\\fcode(s1)\n\ c1 = character.\n"); printf("Returns integer:\n\ The numeric code of the first character in string s1, or 0 if s1 empty.\n"); break; case FN_RPL: /* Replace */ printf("\\freplace(s1,s2,s3[,n1])\n\ s1 = original string.\n\ s2 = match string.\n\ s3 = replacement string.\n\ n1 = occurrence.\n"); printf("Returns string:\n\ s1 with occurrence number n1 of s2 replaced by s3.\n\ If n1 = 0 or omitted, all occurrences are replaced.\n\ If n1 < 0, occurrences are counted from the right.\n"); break; case FN_FD: /* File date */ printf("\\fdate(f1)\n\ f1 = filename.\n"); #ifdef VMS printf("Returns string:\n\ Creation date of file f1, format: yyyymmdd hh:mm:ss.\n"); #else printf("Returns string:\n\ Modification date of file f1, format: yyyymmdd hh:mm:ss.\n"); #endif /* VMS */ break; case FN_FS: /* File size */ printf("\\fsize(f1)\n\ f1 = filename.\n"); printf("Returns integer:\n\ Size of file f1.\n"); break; case FN_VER: /* Verify */ printf("\\fverify(s1,s2,n1)\n\ s1 = string of characters to look for.\n\ s2 = string to look in.\n\ n1 = starting position in s2.\n"); printf("Returns integer:\n\ 1-based position of first character in s2 that is not also in s1,\n\ or -1 if s1 is empty, or 0 if all characters in s2 are also in s1.\n"); break; case FN_IPA: /* Find and return IP address */ printf("\\fipaddress(s1,n1)\n\ s1 = string.\n\ n1 = 1-based integer starting position, default = 1.\n"); printf("Returns string:\n\ First IP address in s1, scanning from left starting at position n1.\n"); break; case FN_HEX: /* Hexify */ printf("\\fhexify(s1)\n\ s1 = string.\n"); printf("Returns string:\n\ The hexadecimal representation of s1. Also see \\fn2hex().\n"); break; case FN_UNH: /* Unhexify */ printf("\\funhexify(h1)\n\ h1 = Hexadecimal string.\n"); printf("Returns string:\n\ The result of unhexifying s1, or nothing if s1 is not a hex string.\n"); break; case FN_UNTAB: /* Untabify */ printf("\\funtabify(s1)\n\ s1 = string.\n"); printf("Returns string:\n\ The result of converting tabs in s1 to spaces assuming tab stops every\n\ 8 spaces.\n"); break; case FN_BRK: /* Break */ case FN_SPN: /* Span */ printf("\\f%s(s1,s2,n1)\n\ s1 = string to look in.\n\ s2 = string of characters to look for.\n\ n1 = 1-based integer starting position, default = 1.\n", xx == FN_BRK ? "break" : "span" ); printf("Returns string:\n\ s1 up to the first occurrence of any character%salso in s2,\n\ scanning from the left starting at position n1.\n", xx == FN_SPN ? " not " : " "); break; case FN_TRM: /* Trim */ case FN_LTR: /* Left-Trim */ printf("\\f%s(s1,s2)\n\ s1 = string to look in.\n\ s2 = string of characters to look for, default = blanks and tabs.\n", xx == FN_TRM ? "trim" : "ltrim"); printf("Returns string:\n\ s1 with all characters that are also in s2 trimmed from the %s.\n.", xx == FN_TRM ? "right" : "left"); break; case FN_CAP: /* Capitalize */ printf("\\fcapitalize(s1)\n\ s1 = string.\n"); printf("Returns string:\n\ s1 with its first letter converted to uppercase and the remaining\n\ letters to lowercase.\n"); printf("Synonym: \\fcaps(s1)\n"); break; case FN_TOD: /* Time-of-day-to-secs-since-midnite */ printf("\\ftod2secs(s1)\n\ s1 = time-of-day string, hh:mm:ss, 24-hour format.\n"); printf("Returns number:\n\ Seconds since midnight.\n"); break; case FN_FFN: /* Full file name */ printf("\\fpathname(f1)\n\ f1 = filename, possibly wild.\n"); printf("Returns string:\n\ Full pathname of f1.\n"); break; case FN_CHK: /* Checksum of text */ printf("\\fchecksum(s1)\n\ s1 = string.\n"); printf("Returns integer:\n\ 16-bit checksum of string s1.\n"); break; case FN_CRC: /* CRC-16 of text */ printf("\\fcrc16(s1)\n\ s1 = string.\n"); printf("Returns integer:\n\ 16-bit cyclic redundancy check of string s1.\n"); break; case FN_BSN: /* Basename of file */ printf("\\fbasename(f1)\n\ f1 = filename, possibly wild.\n"); printf("Returns string:\n\ Filename f1 stripped of all device and directory information.\n"); break; case FN_CMD: /* Output of a command (cooked) */ printf("\\fcommand(s1)\n\ s1 = string\n"); printf("Returns string:\n\ Output of system command s1, if any, with final line terminator stripped.\n" ); break; case FN_RAW: /* Output of a command (raw) */ printf("\\frawcommand(s1)\n\ s1 = string\n"); printf("Returns string:\n\ Output of system command s1, if any.\n"); break; case FN_STX: /* Strip from right */ printf("\\fstripx(s1,c1)\n\ s1 = string to look in.\n\ c1 = character to look for, default = \".\".\n"); printf("Returns string:\n\ s1 up to the rightmost occurrence of character c1.\n" ); break; case FN_STL: /* Strip from left */ printf("\\flop(s1[,c1[,n1]])\n\ s1 = string to look in.\n\ c1 = character to look for, default = \".\".\n\ n1 = occurrence of c1, default = 1.\n"); printf("Returns string:\n\ The part of s1 after the n1th leftmost occurrence of character c1.\n" ); break; case FN_LOPX: /* Strip from right */ printf("\\flopx(s1,c1)\n\ s1 = string to look in.\n\ c1 = character to look for, default = \".\".\n\ n1 = occurrence of c1, default = 1.\n"); printf("Returns string:\n\ The part of s1 after the n1th rightmost occurrence of character c1.\n" ); break; case FN_STN: /* Strip n chars */ printf("\\fstripn(s1,n1)\n\ s1 = string to look in.\n\ n1 = integer, default = 0.\n"); printf("Returns string:\n\ s1 with n1 characters removed from the right.\n" ); break; case FN_STB: /* Strip enclosing brackets */ printf("\\fstripb(s1[,c1[,c2]])\n\ s1 = original string.\n\ c1 = optional first character\n"); printf("\ c2 = optional final character.\n"); printf("Returns string:\n\ s1 with the indicated enclosing characters removed. If c1 and c2 not\n\ specified, any matching brackets, braces, parentheses, or quotes are\n"); printf("\ assumed. If c1 is given but not c2, the appropriate c2 is assumed.\n\ if both c1 and c2 are given, they are used as-is.\n" ); printf( "Alternative format:\n\ Include a grouping mask number in place of c1 and omit c2 to specify more\n\ than one possibility at once; see \\fword() for details.\n" ); break; #ifdef OS2 case FN_SCRN_CX: /* Screen Cursor X Pos */ printf("\\fscrncurx()\n"); printf("Returns integer:\n\ The 0-based X coordinate (column) of the Terminal screen cursor.\n"); break; case FN_SCRN_CY: /* Screen Cursor Y Pos */ printf("\\fscrncury()\n"); printf("Returns integer:\n\ The 0-based Y coordinate (row) of the Terminal screen cursor.\n"); break; case FN_SCRN_STR: /* Screen String */ printf("\\fscrnstr(ny,nx,n1)\n\ ny = integer.\n\ nx = integer.\n\ n1 = integer.\n"); printf("Returns string:\n\ The string at Terminal-screen coordinates (nx,ny), length n1,\n\ blanks included. Coordinates start at 0. Default values are\n\ 0 for ny and nx, and line width for n1.\n"); break; #endif /* OS2 */ case FN_2HEX: /* Num to hex */ printf("\\fn2hex(n1) - Number to hex\n n1 = integer.\n"); printf("Returns string:\n The hexadecimal representation of n1.\n"); break; case FN_2OCT: /* Num to hex */ printf("\\fn2octal(n1) - Number to octal\n n1 = integer.\n"); printf("Returns string:\n The octal representation of n1.\n"); break; #ifdef RECURSIVE case FN_DIR: /* Recursive directory count */ printf("\\fdirectories(f1) - Directory list.\n\ f1 = directory specification, possibly containing wildcards.\n\ &a = optional name of array to assign directory list to.\n"); printf("Returns integer:\n\ The number of directories that match f1; use with \\fnextfile().\n"); break; case FN_RFIL: /* Recursive file count */ printf("\\frfiles(f1[,&a]) - Recursive file list.\n\ f1 = file specification, possibly containing wildcards.\n\ &a = optional name of array to assign file list to.\n"); printf("Returns integer:\n\ The number of files whose names match f1 in the current or given\n\ directory tree; use with \\fnextfile().\n"); break; case FN_RDIR: /* Recursive directory count */ printf("\\frdirectories(f1) - Recursive directory list.\n\ f1 = directory specification, possibly containing wildcards.\n\ &a = optional name of array to assign directory list to.\n"); printf("Returns integer:\n\ The number of directories that match f1 in the current or given directory\n\ tree. Use with \\fnextfile().\n"); break; #endif /* RECURSIVE */ case FN_DNAM: /* Directory part of a filename */ printf("\\fdirname(f) - Directory part of a filename.\n\ f = a file specification.\n"); printf("Returns directory name:\n\ The full name of the directory that the file is in, or if the file is a\n\ directory, its full name.\n"); break; #ifndef NORANDOM case FN_RAND: /* Random number */ printf("\\frandom(n) - Random number.\n\ n = a positive integer.\n"); printf("Returns integer:\n\ A random number between 0 and n-1.\n"); break; #endif /* NORANDOM */ case FN_SPLIT: /* Split */ #ifdef COMMENT printf("\\fsplit(s1,&a,s2,s3,n2,n3) - \ Assign string words to an array.\n\ s1 = source string\n &a = array designator\n s2 = optional break set.\n"); printf(" s3 = optional include set.\n"); printf(" n2 = optional grouping mask.\n"); printf(" n3 = optional separator flag.\n"); printf(" s2, s3, n2, n3 are as in \\fword().\n"); printf( " All arguments are optional; if \\&a[] already exists, it is recycled;\n\ if array not specified, the count is returned but no array is created.\n"); printf("Returns integer:\n\ Number of words in source string.\n"); #else hmsga(hfsplit); #endif /* COMMENT */ break; case FN_DTIM: /* CVTDATE */ printf("\\fcvtdate([date-time][,n1]) - Date/time conversion.\n"); printf(" Converts date and/or time to standard format.\n"); printf(" If no date/time given, returns current date/time.\n"); printf(" [date-time], if given, is free-format date and/or time.\n"); printf(" HELP DATE for info about date-time formats.\n"); printf("Returns string:\n\ Standard-format date and time: yyyymmdd hh:mm:ss (numeric)\n"); printf(" If n1 is given:\n\ n1 = 1: yyyy-mmm-dd hh:mm:ss (mmm = English 3-letter month abbreviation)\n\ n1 = 2: dd-mmm-yyyy hh:mm:ss (ditto)\n\ n1 = 3: yyyymmddhhmmss (all numeric)\n\ n1 = 4: Day Mon dd hh:mm:ss yyyy (asctime)\n\ Other: yyyymmdd hh:mm:dd"); break; case FN_JDATE: /* DOY */ printf("\\fdoy([date-time]) - Day of Year.\n"); printf(" Converts date and/or time to day-of-year (DOY) format.\n"); printf(" If no date/time given, returns current date.\n"); printf(" [date-time], if given, is free-format date and/or time.\n"); printf(" HELP DATE for info about date-time formats.\n"); printf("Returns numeric string:\n\ DOY: yyyyddd, where ddd is 1-based day number in year.\n"); break; case FN_PNCVT: printf("\\fdialconvert(phone-number) - Convert phone number.\n"); printf(" Converts the given phone number for dialing according\n"); printf( " to the prevailing dialing rules -- country code, area code, etc.\n"); printf("Returns string:\n\ The dial string that would be used if the same phone number had been\n\ given to the DIAL command.\n" ); break; case FN_DATEJ: /* DOY2DATE */ printf("\\fdoy2date([doy[ time]]) - Day of Year to Date.\n"); printf(" Converts yyyymmm to yyyymmdd\n"); printf(" If time included, it is converted to 24-hour format."); printf( "Returns standard date or date-time string yyyymmdd hh:mm:ss\n"); break; case FN_MJD: printf("\\fmjd([[date][ time]]) - Modified Julian Date (MJD).\n"); printf( " Converts date and/or time to MJD, the number of days since 17 Nov 1858.\n"); printf(" HELP DATE for info about date-time formats.\n"); printf("Returns: integer.\n"); break; case FN_MJD2: printf("\\fmjd2date(mjd) - Modified Julian Date (MJD) to Date.\n"); printf(" Converts MJD to standard-format date.\n"); printf("Returns: yyyymmdd.\n"); break; case FN_DAY: printf("\\fday([[date][ time]]) - Day of Week.\n"); printf("Returns day of week of given date as Mon, Tue, etc.\n"); printf("HELP DATE for info about date-time formats.\n"); break; case FN_NDAY: printf("\\fnday([[date][ time]]) - Numeric Day of Week.\n"); printf( "Returns numeric day of week of given date, 0=Sun, 1=Mon, ..., 6=Sat.\n"); printf("HELP DATE for info about date-time formats.\n"); break; case FN_TIME: printf("\\ftime([[date][ time]]) - Time.\n"); printf( "Returns time portion of given date and/or time in hh:mm:ss format.\n"); printf("If no argument given, returns current time.\n"); printf("HELP DATE for info about date-time formats.\n"); break; case FN_NTIM: printf("\\fntime([[date][ time]]) - Numeric Time.\n"); printf( "Returns time portion of given date and/or time as seconds since midnight.\n"); printf("If no argument given, returns current time.\n"); printf("HELP DATE for info about date-time formats.\n"); break; case FN_N2TIM: printf("\\fn2time(seconds) - Numeric Time to Time.\n"); printf( "Returns the given number of seconds in hh:mm:ss format.\n"); break; case FN_PERM: printf("\\fpermissions(file) - Permissions of File.\n"); printf( #ifdef UNIX "Returns permissions of given file as they would be shown by \"ls -l\".\n" #else #ifdef VMS "Returns permissions of given file as they would be shown by \"dir /prot\".\n" #else "Returns the permissions of the given file.\n" #endif /* VMS */ #endif /* UNIX */ ); break; case FN_ALOOK: printf("\\farraylook(pattern,&a) - Lookup pattern in array.\n\ pattern = String or pattern\n"); printf(" &a = array designator, can include range specifier.\n"); printf( "Returns number:\n\ The index of the first matching array element or -1 if none.\n"); printf("More info:\n\ HELP PATTERN for pattern syntax.\n HELP ARRAY for arrays.\n"); break; case FN_TLOOK: printf( "\\ftablelook(keyword,&a,[c]) - Lookup keyword in keyword table.\n\ pattern = String\n"); printf(" keyword = keyword to look up (can be abbreviated).\n"); printf(" &a = array designator, can include range specifier.\n"); printf(" This array must be in alphabetical order.\n"); printf(" c = Optional field delimiter, colon(:) by default.\n"); printf( "Returns number:\n\ 1 or greater, index of array element that uniquely matches given keyword;\n" ); printf( "or -2 if keyword was ambiguous, or -1 if keyword empty or not found.\n" ); printf("Also see:\n\ HELP FUNC ARRAYLOOK for a similar function.\n HELP ARRAY for arrays.\n"); break; case FN_ABS: /* Absolute */ printf("\\fabsolute(n1)\n\ n1 = integer.\n"); printf("Returns integer:\n\ The absolute (unsigned) value of n1.\n"); break; #ifdef FNFLOAT case FN_FPABS: printf("\\ffpabsolute(f1,d)\n\ f1 = floating-point number or integer.\n\ d = integer.\n"); printf("Returns floating-point number:\n\ The absolute (unsigned) value of f1 to d decimal places.\n"); break; case FN_FPADD: printf("\\ffpadd(f1,f2,d)\n\ f1,f2 = floating-point numbers or integers.\n\ d = integer.\n"); printf("Returns floating-point number:\n\ The sum of f1 and f2 to d decimal places.\n"); break; case FN_FPSUB: printf("\\ffpsubtract(f1,f2,d)\n\ f1,f2 = floating-point numbers or integers.\n\ d = integer.\n"); printf("Returns floating-point number:\n\ f1 minus f2 to d decimal places.\n"); break; case FN_FPMUL: printf("\\ffpmultiply(f1,f2,d)\n\ f1,f2 = floating-point numbers or integers.\n\ d = integer.\n"); printf("Returns floating-point number:\n\ The product of f1 and f2 to d decimal places.\n"); break; case FN_FPDIV: printf("\\ffpdivide(f1,f2,d)\n\ f1,f2 = floating-point numbers or integers.\n\ d = integer.\n"); printf("Returns floating-point number:\n\ f1 divided by f2 to d decimal places.\n"); break; case FN_FPMAX: printf("\\ffpmaximum(f1,f2,d)\n\ f1,f2 = floating-point numbers or integers.\n\ d = integer.\n"); printf("Returns floating-point number:\n\ The maximum of f1 and f2 to d decimal places.\n"); break; case FN_FPMIN: printf("\\ffpminimum(f1,f2,d)\n\ f1,f2 = floating-point numbers or integers.\n\ d = integer.\n"); printf("Returns floating-point number:\n\ The minimum of f1 and f2 to d decimal places.\n"); break; case FN_FPMOD: printf("\\ffpmodulus(f1,f2,d)\n\ f1,f2 = floating-point numbers or integers.\n\ d = integer.\n"); printf("Returns floating-point number:\n\ The modulus of f1 and f2 to d decimal places.\n"); break; case FN_FPPOW: printf("\\ffpraise(f1,f2,d)\n\ f1,f2 = floating-point numbers or integers.\n\ d = integer.\n"); printf("Returns floating-point number:\n\ f1 raised to the power f2, to d decimal places.\n"); break; case FN_FPCOS: printf("\\ffpcosine(f1,d)\n\ f1 = floating-point number or integer.\n\ d = integer.\n"); printf("Returns floating-point number:\n\ The cosine of angle f1 (in radians) to d decimal places.\n"); break; case FN_FPSIN: printf("\\ffpsine(f1,d)\n\ f1 = floating-point number or integer.\n\ d = integer.\n"); printf("Returns floating-point number:\n\ The sine of angle f1 (in radians) to d decimal places.\n"); break; case FN_FPTAN: printf("\\ffptangent(f1,d)\n\ f1 = floating-point number or integer.\n\ d = integer.\n"); printf("Returns floating-point number:\n\ The tangent of angle f1 (in radians) to d decimal places.\n"); break; case FN_FPEXP: printf("\\ffpexp(f1,d)\n\ f1 = floating-point number or integer.\n\ d = integer.\n"); printf("Returns floating-point number:\n\ e (the base of natural logarithms) raised to the f1 power,\n\ to d decimal places.\n"); break; case FN_FPINT: printf("\\ffpint(f1)\n\ f1 = floating-point number or integer.\n"); printf("Returns integer:\n\ The integer part of f1.\n"); break; case FN_FPLOG: printf("\\ffplog10(f1,d)\n\ f1 = floating-point number or integer.\n\ d = integer.\n"); printf("Returns floating-point number:\n\ The logarithm, base 10, of f1 to d decimal places.\n"); break; case FN_FPLN: printf("\\ffplogn(f1,d)\n\ f1 = floating-point number or integer.\n\ d = integer.\n"); printf("Returns floating-point number:\n\ The natural logarithm of f1 to d decimal places.\n"); break; case FN_FPROU: printf("\\ffpround(f1,d)\n\ f1 = floating-point number or integer.\n\ d = integer.\n"); printf("Returns floating-point number:\n\ f1 rounded to d decimal places.\n"); break; case FN_FPSQR: printf("\\ffpsqrt(f1,d)\n\ f1 = floating-point number or integer.\n\ d = integer.\n"); printf("Returns floating-point number:\n\ The square root of f1 to d decimal places.\n"); break; #endif /* FNFLOAT */ #ifdef CKCHANNELIO case FN_FEOF: printf("\\f_eof(n1)\n\ n1 = channel number.\n"); printf("Returns number:\n\ 1 if channel n1 at end of file, 0 otherwise.\n"); break; case FN_FPOS: printf("\\f_pos(n1)\n\ n1 = channel number.\n"); printf("Returns number:\n\ Read/write pointer of channel n1 as byte number.\n"); break; case FN_NLINE: printf("\\f_line(n1)\n\ n1 = channel number.\n"); printf("Returns number:\n\ Read/write pointer of channel n1 as line number.\n"); break; case FN_FILNO: printf("\\f_handle(n1)\n\ n1 = channel number.\n"); printf("Returns number:\n\ File %s of open file on channel n1.\n", #ifdef OS2 "handle" #else "descriptor" #endif /* OS2 */ ); break; case FN_FSTAT: printf("\\f_status(n1)\n\ n1 = channel number.\n"); printf("Returns number:\n\ Sum of open modes of channel n1: 1 = read; 2 = write; 4 = append, or:\n\ 0 if not open.\n"); break; case FN_FGCHAR: printf("\\f_getchar(n1)\n\ n1 = channel number.\n"); printf(" Reads a character from channel n1 and returns it.\n"); break; case FN_FGLINE: printf("\\f_getline(n1)\n\ n1 = channel number.\n"); printf(" Reads a line from channel n1 and returns it.\n"); break; case FN_FGBLK: printf("\\f_getblock(n1,n2)\n\ n1 = channel number, n2 = size\n"); printf( " Reads a block of n2 characters from channel n1 and returns it.\n"); break; case FN_FPCHAR: printf("\\f_putchar(n1,c)\n\ n1 = channel number, c = character\n"); printf(" Writes a character to channel n1.\n\ Returns number:\n\ 1 if successful, otherwise a negative error code.\n"); break; case FN_FPLINE: printf("\\f_putline(n1,s1)\n\ n1 = channel number, s1 = string\n"); printf( " Writes the string s1 to channel n1 and adds a line terminator.\n\ Returns number:\n"); printf(" How many characters written if successful;\n\ Otherwise a negative error code.\n" ); break; case FN_FPBLK: printf("\\f_putblock(n1,s1)\n\ n1 = channel number, s1 = string\n"); printf( " Writes the string s1 to channel n1.\n\ Returns number:\n"); printf(" How many characters written if successful;\n\ Otherwise a negative error code.\n" ); break; case FN_FERMSG: printf("\\f_errmsg([n1])\n\ n1 = numeric error code, \\v(f_error) by default.\n"); printf(" Returns the associated error message string.\n"); break; #endif /* CKCHANNELIO */ case FN_AADUMP: printf("\\faaconvert(name,&a[,&b])\n\ name = name of associative array, &a and &b = names of regular arrays.\n"); printf( " Converts the given associative array into two regular arrays, &a and &b,\n\ containing the indices and values, respectively:\n"); printf("Returns number:\n\ How many elements were converted.\n"); break; #ifdef CK_KERBEROS case FN_KRB_TK: printf("\\fkrbtickets(n)\n\ n = Kerberos version number (4 or 5).\n\ Returns string:\n\ The number of active Kerberos 4 or 5 tickets.\n\ Resets the ticket list used by \\fkrbnextticket(n).\n"); break; case FN_KRB_NX: printf("\\fkrbnextticket(n)\n\ n = Kerberos version number (4 or 5).\n\ Returns string:\n\ The next ticket in the Kerberos 4 or 5 ticket list that was set up by\n\ the most recent invocation of \\fkrbtickets(n).\n"); break; case FN_KRB_IV: printf("\\fkrbisvalid(n,name)\n\ n = Kerberos version number (4 or 5).\n\ name = a ticket name as returned by \\fkrbnextticket(n).\n\ Returns number:\n\ 1 if the ticket is valid, 0 if not valid.\n\ A ticket is valid if all the following conditions are true:\n\n"); printf("\n\ (i) it exists in the current cache file;\n\ (ii) it is not expired;\n\ (iii) it is not marked invalid (K5 only);\n\ (iv) it was issued from the current IP address\n"); printf("\n This value can be used in an IF statement, e.g.:\n\n"); printf(" if \\fkrbisvalid(4,krbtgt.FOO.BAR.EDU@FOO.BAR.EDU) ...\n"); break; case FN_KRB_TT: printf("\\fkrbtimeleft(n,name)\n\ n = Kerberos version number (4 or 5).\n\ name = a ticket name as returned by \\fkrbnextticket(n).\n\ Returns string:\n\ The number of seconds remaining in the ticket's lifetime.\n"); break; case FN_KRB_FG: printf("\\fkrbflags(n,name)\n\ n = Kerberos version number (4 or 5).\n\ name = a ticket name as returned by \\fkrbnextticket(n).\n\ Returns string:\n"); printf( " The flags string as reported with AUTH K5 LIST /FLAGS. This string can\n\ be searched for a particular flag using the \\findex() function when\n\ SET CASE is ON (for case sensitive searches). Flag strings are only\n\ available for K5 tickets.\n"); break; #endif /* CK_KERBEROS */ case FN_PATTERN: printf("\\fpattern(s)\n\ s = string\n\ Returns string: s with any variables, etc, evaluated in the normal manner.\n" ); printf("\ For use with INPUT, MINPUT, and REINPUT to declare that a search target is\n\ a pattern rather than a literal string.\n"); break; case FN_HEX2N: printf("\\fhex2n(s)\n\ s = hexadecimal number\n\ Returns decimal equivalent.\n"); break; case FN_HEX2IP: printf("\\fhex2ip(s)\n\ s = 8-digit hexadecimal number\n\ Returns the equivalent decimal dotted IP address.\n"); break; case FN_IP2HEX: printf("\\fip2hex(s)\n\ s = decimal dotted IP address\n\ Returns the equivalent 8-digit hexadecimal number.\n"); break; case FN_OCT2N: printf("\\foct2n(s)\n\ s = octal number\n\ Returns decimal equivalent.\n"); break; case FN_TOB64: printf("\\b64encode(s)\n\ s = string containing no NUL bytes\n\ Returns Base-64 encoding of string.\n"); break; case FN_FMB64: printf("\\b64decode(s)\n\ s = string in Base-64 notation\n\ Returns the decoded string or an error code if s not valid.\n"); break; case FN_RADIX: printf("\\fradix(s,n1,n2)\n\ s = number in radix n1\n\ Returns the number's representation in radix n2.\n"); break; case FN_JOIN: printf("\\fjoin(&a[,s[,n1[,n2]]])\n\ &a = array designator, can include range specifier.\n\ s = optional separator.\n"); printf("\ n1 = nonzero to put grouping around elements that contain spaces;\n\ see \\fword() grouping mask for values of n.\n"); printf("\ n2 = 0 or omitted to put spaces between elements; nonzero to omit them.\n"); printf("\ Returns the (selected) elements of the array joined to together,\n\ separated by the separator.\n"); printf("\n\ If s is CSV (literally), that means the array is to be transformed into a\n\ comma-separated list. No other arguments are needed. If s is TSV, then\n\ a tab-separated list is created.\n"); break; case FN_SUBST: printf("\\fsubstitute(s1,s2,s3)\n\ s1 = Source string.\n\ s2 = List of characters to be translated.\n\ s3 = List of characters to translate them to.\n"); printf( " Returns: s1, with each character that is in s2 replaced by the\n\ corresponding character in s3. s2 and s3 can contain ASCII ranges,\n\ like [a-z]. Any characters in s2 that don't have corresponding\n\ characters in s3 (after range expansion) are removed from the result.\n\ This function works only with single-byte character-sets\n"); break; #ifndef NOSEXP case FN_SEXP: printf("\\fsexpression(s1)\n\ s1 = S-Expression.\n"); printf(" Returns: The result of evaluating s1.\n"); break; #endif /* NOSEXP */ case FN_CMDSTK: printf("\\fcmdstack(n1,n2)\n\ n1 = Command-stack level, 0 to \\v(cmdlevel), default \\v(cmdlevel).\n\ n2 = Function code, 0 or 1.\n"); printf("Returns:\n"); printf(" n2 = 0: name of object at stack level n1\n\ n2 = 1: type of object at stack level n1:\n\ 0 = interactive prompt\n\ 1 = command file\n\ 2 = macro\n" ); break; #ifdef CKFLOAT case FN_DIFDATE: printf("\\fdiffdates(d1,d2)\n\ d1 = free-format date and/or time (default = NOW).\n\ d2 = ditto.\n"); printf("Returns:\n"); printf(" Difference expressed as delta time:\n"); printf(" Negative if d2 is later than d1, otherwise positive.\n"); break; #endif /* CKFLOAT */ case FN_CMPDATE: printf("\\fcmpdates(d1,d2)\n\ d1 = free-format date and/or time (default = NOW).\n\ d2 = ditto.\n"); printf("Returns:\n"); printf(" 0 if d1 is equal to d2;\n\ 1 if d1 is later than d2;\n\ -1 if d1 is earlier than d2.\n"); break; case FN_TOGMT: printf("\\futcdate(d1)\n\ d1 = free-format date and/or time (default = NOW).\n"); printf("Returns:\n"); printf(" Date-time converted to UTC (GMT) yyyymmdd hh:mm:ss.\n"); break; #ifdef TCPSOCKET case FN_HSTADD: printf("\\faddr2name(s)\n\ s = numeric IP address.\n"); printf("Returns:\n"); printf(" Corresponding IP hostname if found, otherwise null.\n"); break; case FN_HSTNAM: printf("\\fname2addr(s)\n\ s = IP host name.\n"); printf("Returns:\n"); printf(" Corresponding numeric IP address if found, else null.\n"); break; #endif /* TCPSOCKET */ case FN_DELSEC: printf("\\fdelta2secs(dt)\n\ dt = Delta time, e.g. +3d14:27:52.\n"); printf("Returns:\n"); printf(" The corresponding number of seconds.\n"); break; case FN_PC_DU: printf("\\fdos2unixpath(p)\n\ p = string, DOS pathname.\n"); printf("Returns:\n"); printf(" The argument converted to a Unix pathname.\n"); break; case FN_PC_UD: printf("\\funix2dospath(p)\n\ p = string, Unix pathname.\n"); printf("Returns:\n"); printf(" The argument converted to a DOS pathname.\n"); break; #ifdef FN_ERRMSG case FN_ERRMSG: printf("\\ferrstring(n)\n\ n = platform-dependent numeric error code.\n"); printf("Returns:\n"); printf(" The corresponding error string.\n"); break; #endif /* FN_ERRMSG */ case FN_KWVAL: printf("\\fkeywordvalue(s1[,s2])\n\ s1 = string of the form \"name=value\"\n\ s2 = one more separator characters (default separator is \"=\")\n"); printf(" Assigns the value, if any, to the named macro and sets\n"); printf(" the \\v(lastkeywordvalue) to the macro name.\n"); printf(" If no value is given, the macro is undefined.\n"); printf("Returns:\n"); printf(" -1 on error\n"); printf(" 0 if no keyword or value were found\n"); printf(" 1 if a keyword was found but no value\n"); printf(" 2 if a keyword and a value were found\n"); printf("Synonym: \\kwvalue(s1[,s2])\n"); break; #ifdef COMMENT case FN_SLEEP: printf("\\fsleep(n)\n\ n = number of seconds\n"); printf(" Pauses for the given number of seconds.\n"); printf("Returns: the empty string.\n"); break; case FN_MSLEEP: printf("\\fmsleep(n)\n\ n = number of milliseconds\n"); printf(" Pauses for the given number of milliseconds.\n"); printf("Returns: the empty string.\n"); break; #endif /* COMMENT */ #ifdef NT case FN_SNAME: printf("\\fshortpathname(s)\n\ s = file or directory name string\n"); printf(" Returns the short path form of the given input.\n"); break; case FN_LNAME: printf("\\flongpathname(s)\n\ s = file or directory name string\n"); printf(" Returns the long path form of the given input.\n"); break; #else case FN_SNAME: printf("\\fshortpathname(s)\n\ Synonym for \fpathname()\n"); break; case FN_LNAME: printf("\\flongpathname(s)\n\ Synonym for \fpathname()\n"); break; #endif /* NT */ case FN_EMAIL: printf("\\femailaddress(s)\n\ s = From: or Sender: header from an RFC2822-format email message\n"); printf(" Extracts and returns the email address.\n"); break; case FN_PICTURE: printf("\\fpictureinfo(s[,&a])\n\ s = File specification of a JPG or GIF picture file.\n\ &a = Optional array name.\n\n"); printf("Returns integer:\n\ 0 if file not found or not recognized;\n\ 1 if orientation is landscape;\n\ 2 if orientation is portrait.\n\n"); printf(" If an array name is included, element 1 is filled \ in with the image width\n\ in pixels, and element 2 the image height.\n"); break; case FN_PID: printf("\\fgetpidinfo(n1)\n\ n1 = Numeric process ID\n"); printf("Returns integer:\n\ -1 on failure to get information;\n\ 1 if n1 is the ID of an active process;\n\ 0 if the process does not exist.\n"); break; case FN_FUNC: printf("\\ffunction(s1)\n\ s1 = name of function.\n"); printf("Returns integer:\n\ 1 if s1 is the name of an available built-in function;\n\ 0 otherwise.\n"); break; case FN_RECURSE: printf("\\frecurse(s1)\n\ s1 = name of \\&x or \\&x[] type variable\n"); printf("Returns the result of evaluating the variable recursively.\n"); break; case FN_SQUEEZE: printf("\\fsqueeze(s)\n\ s = string\n\ Returns string with leading and trailing whitespace removed, Tabs\n\ converted to Spaces, and multiple spaces converted to single spaces.\n"); break; #ifndef NOCSETS case FN_XLATE: printf("\\fcvtcset(s,cset1,cset2)\n\ s = string\n\ Returns string converted from character set cset1 to cset2, where cset1\n\ and cset2 are names of File Character-Sets \ ('set file char ?' for a list).\n"); break; #endif /* NOCSETS */ case FN_UNPCT: printf("\\fdecodehex(s1[,s2])\n\ s1, s2 = strings\n\ Decodes a string s1 that contains prefixed hex bytes. s2 is the prefix;\n\ the default is %%%%. You can specify any other prefix one or two bytes\n\ long. If the prefix contains letters (such as 0x), case is ingored.\n\ Returns string s1 with hex escapes replaced by the bytes they \ represent.\n"); break; case FN_STRINGT: printf("\\fstringtype(s)\n\ s = string\n\ Returns a string representing the type of its string argument s1:\n\ 7BIT, 8BIT, UTF8, TEXT, or BINARY. TEXT means some kind of text\n\ other than 7BIT, 8BIT, or UTF8 (this probably will never appear).\n"); break; case FN_STRCMP: printf("\\fstrcmp(s1,s2[,case[,start[,length]]])\n\ s1, s2 = strings\n\ case, start, length = numbers or arithmetic expressions.\n\ case = 0 [default] means to do a case-independent comparison;\n\ nonzero case requests a case-sensitive comparison.\n\ The optional start and length arguments apply to both s1 and s2\n\ and allow specification of substrings if it is not desired to compare\n\ the whole strings. Results for non-ASCII strings are implentation-\n\ and locale-dependent.\n\ Returns a number:\n\ -1: s1 is lexically less than s2;\n\ 0: s1 and s2 are lexically equal;\n\ 2: s1 is lexically greater than s2.\n"); break; default: printf("Sorry, help not available for \"%s\"\n",cmdbuf); } printf("\n"); return(0); } #endif /* NOSPL */ #ifdef OS2 #ifndef NOKVERBS /* D O H K V E R B -- Give help for a Kverb */ int dohkverb(xx) int xx; { int x,i,found,button,event; if (xx == -3) { printf("\n Type SHOW KVERBS to see a list of available Kverbs.\n" ); printf( " Type HELP KVERB to see the current definition of a given Kverb.\n\n" ); return(-9); } if (xx < 0) return(xx); if ((x = cmcfm()) < 0) return(x); switch ( xx ) { /* DEC VT keyboard key definitions */ case K_COMPOSE : /* Compose key */ printf("\\Kcompose Compose an accented character\n"); break; case K_C_UNI16 : /* UCS2 key */ printf("\\Kucs2 Enter a Unicode character\n"); break; /* DEC arrow keys */ case K_UPARR : /* DEC Up Arrow key */ printf("\\Kuparr Transmit Terminal Up Arrow sequence\n"); break; case K_DNARR : /* DEC Down Arrow key */ printf("\\Kdnarr Transmit Terminal Down Arrow sequence\n"); break; case K_RTARR : /* DEC Right Arrow key */ printf("\\Krtarr Transmit Terminal Right Arrow sequence\n"); break; case K_LFARR : /* DEC Left Arrow key */ printf("\\Klfarr Transmit Terminal Left Arrow sequence\n"); break; case K_PF1 : /* DEC PF1 key */ printf("\\Kpf1,\\Kgold Transmit DEC PF1 sequence\n"); break; case K_PF2 : /* DEC PF2 key */ printf("\\Kpf2 Transmit DEC PF2 sequence\n"); break; case K_PF3 : /* DEC PF3 key */ printf("\\Kpf3 Transmit DEC PF3 sequence\n"); break; case K_PF4 : /* DEC PF4 key */ printf("\\Kpf4 Transmit DEC PF4 sequence\n"); break; case K_KP0 : /* DEC Keypad 0 */ printf("\\Kkp0 Transmit DEC Keypad-0 sequence\n"); break; case K_KP1 : /* DEC Keypad 1 */ printf("\\Kkp1 Transmit DEC Keypad-1 sequence\n"); break; case K_KP2 : /* etc ... through 9 */ printf("\\Kkp2 Transmit DEC Keypad-2 sequence\n"); break; case K_KP3 : printf("\\Kkp3 Transmit DEC Keypad-3 sequence\n"); break; case K_KP4 : printf("\\Kkp4 Transmit DEC Keypad-4 sequence\n"); break; case K_KP5 : printf("\\Kkp5 Transmit DEC Keypad-5 sequence\n"); break; case K_KP6 : printf("\\Kkp6 Transmit DEC Keypad-6 sequence\n"); break; case K_KP7 : printf("\\Kkp7 Transmit DEC Keypad-7 sequence\n"); break; case K_KP8 : printf("\\Kkp8 Transmit DEC Keypad-8 sequence\n"); break; case K_KP9 : printf("\\Kkp9 Transmit DEC Keypad-9 sequence\n"); break; case K_KPCOMA : /* DEC keypad comma */ printf("\\Kkpcoma Transmit DEC Keypad-Comma sequence\n"); break; case K_KPMINUS : /* DEC keypad minus */ printf("\\Kkpminus Transmit DEC Keypad-Minus sequence\n"); break; case K_KPDOT : /* DEC keypad period */ printf("\\Kkpdot Transmit DEC Keypad-Period sequence\n"); break; case K_KPENTER : /* DEC keypad enter */ printf("\\Kkpenter Transmit DEC Keypad-Enter sequence\n"); break; /* DEC Top-Rank F keys */ case K_DECF1 : /* DEC F1 key */ printf("\\Kdecf1 Transmit DEC F1 sequence for PC keyboard\n"); break; case K_DECF2 : /* DEC F2 key */ printf("\\Kdecf2 Transmit DEC F2 sequence for PC keyboard\n"); break; case K_DECF3 : /* DEC F3 key */ printf("\\Kdecf3 Transmit DEC F3 sequence for PC keyboard\n"); break; case K_DECF4 : /* DEC F4 key */ printf("\\Kdecf4 Transmit DEC F4 sequence for PC keyboard\n"); break; case K_DECF5 : /* DEC F5 key */ printf("\\Kdecf5 Transmit DEC F5 sequence for PC keyboard\n"); break; case K_DECHOME: /* DEC HOME key */ printf("\\Kdechome Transmit DEC HOME sequence for PC keyboard\n"); break; case K_DECF6 : /* DEC F6 key */ printf("\\Kdecf6 Transmit DEC F6 sequence\n"); break; case K_DECF7 : /* etc, through F20 */ printf("\\Kdecf7 Transmit DEC F7 sequence\n"); break; case K_DECF8 : printf("\\Kdecf8 Transmit DEC F8 sequence\n"); break; case K_DECF9 : printf("\\Kdecf9 Transmit DEC F9 sequence\n"); break; case K_DECF10 : printf("\\Kdecf10 Transmit DEC F10 sequence\n"); break; case K_DECF11 : printf("\\Kdecf11 Transmit DEC F11 sequence\n"); break; case K_DECF12 : printf("\\Kdecf12 Transmit DEC F12 sequence\n"); break; case K_DECF13 : printf("\\Kdecf13 Transmit DEC F13 sequence\n"); break; case K_DECF14 : printf("\\Kdecf14 Transmit DEC F14 sequence\n"); break; case K_DECHELP : /* DEC Help key */ printf("\\Kdecf15,\\Kdechelp Transmit DEC HELP sequence\n"); break; case K_DECDO : /* DEC Do key */ printf("\\Kdecf16,\\Kdecdo Transmit DEC DO sequence\n"); break; case K_DECF17 : printf("\\Kdecf17 Transmit DEC F17 sequence\n"); break; case K_DECF18 : printf("\\Kdecf18 Transmit DEC F18 sequence\n"); break; case K_DECF19 : printf("\\Kdecf19 Transmit DEC F19 sequence\n"); break; case K_DECF20 : printf("\\Kdecf20 Transmit DEC F20 sequence\n"); break; /* DEC editing keys */ case K_DECFIND : /* DEC Find key */ printf("\\Kdecfind Transmit DEC FIND sequence\n"); break; case K_DECINSERT : /* DEC Insert key */ printf("\\Kdecinsert Transmit DEC INSERT HERE sequence\n"); break; case K_DECREMOVE : /* DEC Remove key */ printf("\\Kdecremove Transmit DEC REMOVE sequence\n"); break; case K_DECSELECT : /* DEC Select key */ printf("\\Kdecfselect Transmit DEC SELECT sequence\n"); break; case K_DECPREV : /* DEC Previous Screen key */ printf("\\Kdecprev Transmit DEC PREV SCREEN sequence\n"); break; case K_DECNEXT : /* DEC Next Screen key */ printf("\\Kdecnext Transmit DEC NEXT SCREEN sequence\n"); break; /* DEC User-Defined Keys */ case K_UDKF1 : /* F1 - F5 are XTERM extensions */ printf("\\Kudkf1 Transmit XTERM F1 User Defined Key sequence\n"); break; case K_UDKF2 : printf("\\Kudkf2 Transmit XTERM F2 User Defined Key sequence\n"); break; case K_UDKF3 : printf("\\Kudkf3 Transmit XTERM F3 User Defined Key sequence\n"); break; case K_UDKF4 : printf("\\Kudkf4 Transmit XTERM F4 User Defined Key sequence\n"); break; case K_UDKF5 : printf("\\Kudkf5 Transmit XTERM F5 User Defined Key sequence\n"); break; case K_UDKF6 : /* DEC User Defined Key F6 */ printf("\\Kudkf6 Transmit DEC F6 User Defined Key sequence\n"); break; case K_UDKF7 : /* DEC User Defined Key F7 */ printf("\\Kudkf7 Transmit DEC F7 User Defined Key sequence\n"); break; case K_UDKF8 : /* etc ... through F20 */ printf("\\Kudkf8 Transmit DEC F8 User Defined Key sequence\n"); break; case K_UDKF9 : printf("\\Kudkf9 Transmit DEC F9 User Defined Key sequence\n"); break; case K_UDKF10 : printf("\\Kudkf10 Transmit DEC F10 User Defined Key sequence\n"); break; case K_UDKF11 : printf("\\Kudkf11 Transmit DEC F11 User Defined Key sequence\n"); break; case K_UDKF12 : printf("\\Kudkf12 Transmit DEC F12 User Defined Key sequence\n"); break; case K_UDKF13 : printf("\\Kudkf13 Transmit DEC F13 User Defined Key sequence\n"); break; case K_UDKF14 : printf("\\Kudkf14 Transmit DEC F14 User Defined Key sequence\n"); break; case K_UDKHELP : printf( "\\Kudkhelp,\\Kudkf15 Transmit DEC HELP User Defined Key sequence\n"); break; case K_UDKDO : printf( "\\Kudkdo,\\Kudkf16 Transmit DEC DO User Defined Key sequence\n"); break; case K_UDKF17 : printf("\\Kudkf17 Transmit DEC F17 User Defined Key sequence\n"); break; case K_UDKF18 : printf("\\Kudkf18 Transmit DEC F18 User Defined Key sequence\n"); break; case K_UDKF19 : printf("\\Kudkf19 Transmit DEC F19 User Defined Key sequence\n"); break; case K_UDKF20 : printf("\\Kudkf20 Transmit DEC F20 User Defined Key sequence\n"); break; /* Emacs Keys */ case K_EMACS_OVER: printf( "\\Kemacs_overwrite Transmit EMACS Overwrite toggle command sequence\n"); break; /* Kermit screen-scrolling keys */ case K_DNONE : /* Screen rollback: down one line */ printf("\\Kdnone Screen rollback: down one line\n"); break; case K_DNSCN : /* Screen rollback: down one screen */ printf("\\Kdnscn Screen rollback: down one screen\n"); break; case K_UPONE : /* Screen rollback: Up one line */ printf("\\Kupone Screen rollback: up one line\n"); break; case K_UPSCN : /* Screen rollback: Up one screen */ printf("\\Kupscn Screen rollback: up one screen\n"); break; case K_ENDSCN : /* Screen rollback: latest screen */ printf("\\Kendscn Screen rollback: latest screen\n"); break; case K_HOMSCN : /* Screen rollback: oldest screen */ printf("\\Khomscn Screen rollback: oldest screen\n"); break; case K_GO_BOOK : /* Scroll to bookmark */ printf("\\Kgobook Screen rollback: go to bookmark\n"); break; case K_GOTO : /* Scroll to line number */ printf("\\Kgoto Screen rollback: go to line number\n"); break; case K_LFONE : /* Horizontal Scroll: Left one cell */ printf("\\Klfone Horizontal Scroll: Left one column\n"); break; case K_LFPAGE : /* Horizontal Scroll: Left one page */ printf("\\Klfpage Horizontal Scroll: Left eight columns\n"); break; case K_LFALL : printf("\\Klfall Horizontal Scroll: Left to margin\n"); break; case K_RTONE : /* Horizontal Scroll: Right one cell */ printf("\\Krtone Horizontal Scroll: Right one column\n"); break; case K_RTPAGE : /* Horizontal Scroll: Right one page */ printf("\\Krtpage Horizontal Scroll: Right eight columns\n"); break; case K_RTALL : printf("\\Krtall Horizontal Scroll: Right to margin\n"); break; /* Keyboard language switching verbs */ case K_KB_ENG : /* English keyboard mode */ printf("\\Kkbenglish Switch to Normal (English) keyboard mode\n"); break; case K_KB_HEB : /* Hebrew keyboard mode */ printf("\\Kkbhebrew Switch to Hebrew keyboard mode\n"); break; case K_KB_RUS : /* Russian keyboard mode */ printf("\\Kkbrussian Switch to Russian keyboard mode\n"); break; case K_KB_EMA : /* Emacs keyboard mode */ printf("\\Kkbemacs Switch to EMACS keyboard mode\n"); break; case K_KB_WP : /* Word Perfect 5.1 mode */ printf("\\Kkbwp Switch to Word Perfect 5.1 keyboard mode\n"); break; /* Mark Mode actions */ case K_MARK_START : /* Enter Mark Mode/Start marking */ printf("\\Kmarkstart Mark Mode: Enter mode or Start marking\n"); break; case K_MARK_CANCEL : /* Exit Mark Mode - Do Nothing */ printf("\\Kmarkcancel Mark Mode: Cancel mode\n"); break; case K_MARK_COPYCLIP: /* Exit Mark Mode - Copy data to clipboard */ printf("\\Kmarkcopyclip Mark Mode: Copy marked text to clipboard\n"); break; case K_MARK_COPYHOST: /* Exit Mark Mode - Copy data to host */ printf("\\Kmarkcopyhost Mark Mode: Copy marked text to host\n"); break; case K_MARK_SELECT : /* Exit Mark Mode - Select */ printf( "\\Kmarkselect Mark Mode: Place marked text into \\v(select)\n"); break; case K_BACKSRCH : /* Search Backwards for text */ printf("\\Kbacksearch Search: Begin backward search for text\n"); break; case K_FWDSRCH : /* Search Forwards for text */ printf("\\Kfwdsearch Search: Begin forward search for text\n"); break; case K_BACKNEXT : /* Search Backwards for next instance of text */ printf( "\\Kbacknext Search: Find next instance of text backwards\n"); break; case K_FWDNEXT : /* Search Forwards for next instance of text */ printf("\\Kfwdnext Search: Find next instance of text forwards\n"); break; /* Miscellaneous Kermit actions */ case K_EXIT : /* Return to command parser */ printf("\\Kexit Toggle between COMMAND and CONNECT modes\n"); break; case K_BREAK : /* Send a BREAK */ printf("\\Kbreak Transmit BREAK signal to host\n"); break; case K_RESET : /* Reset emulator */ printf("\\Kreset Reset Terminal Emulator to user defaults\n"); break; case K_DOS : /* Push to DOS (i.e. OS/2) */ printf("\\Kdos,\\Kos2 Push to Command Shell\n"); break; case K_HANGUP : /* Hang up the connection */ printf("\\Khangup Hangup the active connection\n"); break; case K_DUMP : /* Dump/Print current screen */ printf( "\\Kdump Dump/copy current screen to SET PRINTER device/file\n"); break; case K_LBREAK : /* Send a Long BREAK */ printf("\\Klbreak Transmit LONG BREAK signal to host\n"); break; case K_NULL : /* Send a NUL */ printf("\\Knull Transmit NULL ('\0') character to host\n"); break; case K_HELP : /* Pop-up help */ printf("\\Khelp Raise Pop-Up help display\n"); break; case K_HOLDSCRN : /* Hold screen */ printf("\\Kholdscrn Pause data input during CONNECT mode\n"); break; case K_IGNORE : /* Ignore this key, don't even beep */ printf("\\Kignore Ignore key\n"); break; case K_LOGOFF : /* Turn off session logging */ printf("\\Klogoff Turn off session logging (see \\Ksession)\n"); break; case K_LOGON : /* Turn on session logging */ printf("\\Klogon Turn on session logging (see \\Ksession)\n"); break; case K_SESSION: printf( "\\Ksession Toggle on/off session logging to 'session.log'\n"); break; case K_AUTODOWN: printf("\\Kautodown Toggle on/off terminal autodownload.\n"); break; case K_BYTESIZE: printf( "\\Kbytesize Toggle terminal bytesize between 7 and 8 bits.\n"); break; #ifdef COMMENT case MODELINE: case K_NETHOLD : /* Put network connection on hold */ case K_NEXTSESS : /* Toggle to next network session */ #endif /* COMMENT */ case K_CURSOR_URL: printf( "\\Kurl Treat text under cursor position as a URL\n"); break; case K_STATUS : /* Show status */ printf( "\\Kstatus Toggle statusline (None, Indicator, Host Writeable)\n"); break; case K_TERMTYPE : /* Toggle term type: text/graphics */ printf("\\Ktermtype Toggle Terminal Type\n"); break; case K_PRTCTRL : /* Print Controller mode */ printf("\\Kprtctrl Toggle Ctrl-Print (transparent) mode\n"); break; case K_PRINTFF : /* Print formfeed */ printf("\\Kprintff Output Form Feed to SET PRINTER device\n"); break; case K_FLIPSCN : /* Flip screen */ printf("\\Kflipscn Reverse foreground and background colors\n"); break; case K_DEBUG : /* Toggle debugging */ printf("\\Kdebug Toggle Terminal Debug mode\n"); break; case K_TN_SAK : /* TELNET Secure Access Key */ printf("\\Ktn_sak TELNET: IBM Secure Access Key\n"); printf(" Used to request a Trusted Shell with AIX\n"); break; case K_TN_AO : /* TELNET Cancel Output */ printf("\\Ktn_ao TELNET: Transmit Cancel-Output request\n"); break; case K_TN_AYT : /* TELNET Are You There */ printf("\\Ktnayt TELNET: Transmit Are You There? request\n"); break; case K_TN_EC : /* TELNET Erase Character */ printf("\\Ktn_ec TELNET: Transmit Erase Character request\n"); break; case K_TN_EL : /* TELNET Erase Line */ printf("\\Ktn_el TELNET: Transmit Erase Line request\n"); break; case K_TN_GA : /* TELNET Go Ahead */ printf("\\Ktn_ga TELNET: Transmit Go Ahead request\n"); break; case K_TN_IP : /* TELNET Interrupt Process */ printf("\\Ktn_ip TELNET: Transmit Interrupt Process request\n"); break; case K_TN_LOGOUT : /* TELNET Logout */ printf("\\Ktn_logout TELNET: Transmit Do Logout Option\n"); break; case K_TN_NAWS : /* TELNET NAWS */ printf( "\\Ktn_naws TELNET: Transmit Window Size if NAWS is active\n"); break; case K_PASTE : /* Paste data from clipboard */ printf("\\Kpaste Paste data from clipboard to host\n"); break; case K_CLRSCRN : /* Clear Terminal Screen */ printf("\\Kclearscreen Clear the Terminal screen\n"); break; case K_PRTAUTO : /* Print Auto mode */ printf("\\Kprtauto Toggle Auto-Print mode\n"); break; case K_PRTCOPY : /* Print Copy mode */ printf("\\Kprtcopy Toggle Copy-Print mode\n"); break; case K_ANSWERBACK : /* Transmit Answerback String */ printf("\\Kanswerback Transmit answerback string to host\n"); break; case K_SET_BOOK : /* Set Bookmark */ printf("\\Ksetbook Set bookmark\n"); break; case K_QUIT : /* Quit Kermit */ printf("\\Kquit Hangup connection and quit kermit\n"); break; case K_KEYCLICK : /* Toggle Keyclick */ printf("\\Kkeyclick Toggle Keyclick mode\n"); break; case K_LOGDEBUG : /* Toggle Debug Log File */ printf("\\Klogdebug Toggle Debug Logging to File\n"); break; case K_FNKEYS : /* Show Function Key Labels */ printf("\\Kfnkeys Display Function Key Labels\n"); break; #ifdef OS2MOUSE /* Mouse only Kverbs */ case K_MOUSE_CURPOS : printf("\\Kmousecurpos Mouse: Move host cursor to position\n"); break; case K_MOUSE_MARK : printf( "\\Kmousemark Mouse: Mark text for selection (drag event only)\n"); break; case K_MOUSE_URL : printf("\\Kmouseurl Mouse: Start browser with selected URL\n"); break; #endif /* OS2MOUSE */ /* ANSI Function Key definitions */ case K_ANSIF01 : printf("\\Kansif01 Transmit SCOANSI/AT386: F1 \n"); break; case K_ANSIF02 : printf("\\Kansif02 Transmit SCOANSI/AT386: F2 \n"); break; case K_ANSIF03 : printf("\\Kansif03 Transmit SCOANSI/AT386: F3 \n"); break; case K_ANSIF04 : printf("\\Kansif04 Transmit SCOANSI/AT386: F4 \n"); break; case K_ANSIF05 : printf("\\Kansif05 Transmit SCOANSI/AT386: F5 \n"); break; case K_ANSIF06 : printf("\\Kansif06 Transmit SCOANSI/AT386: F6 \n"); break; case K_ANSIF07 : printf("\\Kansif07 Transmit SCOANSI/AT386: F7 \n"); break; case K_ANSIF08 : printf("\\Kansif08 Transmit SCOANSI/AT386: F8 \n"); break; case K_ANSIF09 : printf("\\Kansif09 Transmit SCOANSI/AT386: F9 \n"); break; case K_ANSIF10 : printf("\\Kansif10 Transmit SCOANSI/AT386: F10\n"); break; case K_ANSIF11 : printf("\\Kansif11 Transmit SCOANSI/AT386: F11\n"); break; case K_ANSIF12 : printf("\\Kansif12 Transmit SCOANSI/AT386: F12\n"); break; case K_ANSIF13 : printf("\\Kansif13 Transmit SCOANSI/AT386: Shift-F1 \n"); break; case K_ANSIF14 : printf("\\Kansif14 Transmit SCOANSI/AT386: Shift-F2 \n"); break; case K_ANSIF15 : printf("\\Kansif15 Transmit SCOANSI/AT386: Shift-F3 \n"); break; case K_ANSIF16 : printf("\\Kansif16 Transmit SCOANSI/AT386: Shift-F4 \n"); break; case K_ANSIF17 : printf("\\Kansif17 Transmit SCOANSI/AT386: Shift-F5 \n"); break; case K_ANSIF18 : printf("\\Kansif18 Transmit SCOANSI/AT386: Shift-F6 \n"); break; case K_ANSIF19 : printf("\\Kansif19 Transmit SCOANSI/AT386: Shift-F7 \n"); break; case K_ANSIF20 : printf("\\Kansif20 Transmit SCOANSI/AT386: Shift-F8 \n"); break; case K_ANSIF21 : printf("\\Kansif21 Transmit SCOANSI/AT386: Shift-F9 \n"); break; case K_ANSIF22 : printf("\\Kansif22 Transmit SCOANSI/AT386: Shift-F10\n"); break; case K_ANSIF23 : printf("\\Kansif23 Transmit SCOANSI/AT386: Shift-F11\n"); break; case K_ANSIF24 : printf("\\Kansif24 Transmit SCOANSI/AT386: Shift-F12\n"); break; case K_ANSIF25 : printf("\\Kansif25 Transmit SCOANSI/AT386: Ctrl-F1 \n"); break; case K_ANSIF26 : printf("\\Kansif26 Transmit SCOANSI/AT386: Ctrl-F2 \n"); break; case K_ANSIF27 : printf("\\Kansif27 Transmit SCOANSI/AT386: Ctrl-F3 \n"); break; case K_ANSIF28 : printf("\\Kansif28 Transmit SCOANSI/AT386: Ctrl-F4 \n"); break; case K_ANSIF29 : printf("\\Kansif29 Transmit SCOANSI/AT386: Ctrl-F5 \n"); break; case K_ANSIF30 : printf("\\Kansif30 Transmit SCOANSI/AT386: Ctrl-F6 \n"); break; case K_ANSIF31 : printf("\\Kansif31 Transmit SCOANSI/AT386: Ctrl-F7 \n"); break; case K_ANSIF32 : printf("\\Kansif32 Transmit SCOANSI/AT386: Ctrl-F8 \n"); break; case K_ANSIF33 : printf("\\Kansif33 Transmit SCOANSI/AT386: Ctrl-F9 \n"); break; case K_ANSIF34 : printf("\\Kansif34 Transmit SCOANSI/AT386: Ctrl-F10\n"); break; case K_ANSIF35 : printf("\\Kansif35 Transmit SCOANSI/AT386: Ctrl-F11\n"); break; case K_ANSIF36 : printf("\\Kansif36 Transmit SCOANSI/AT386: Ctrl-F12\n"); break; case K_ANSIF37 : printf("\\Kansif37 Transmit SCOANSI/AT386: Ctrl-Shift-F1 \n"); break; case K_ANSIF38 : printf("\\Kansif38 Transmit SCOANSI/AT386: Ctrl-Shift-F2 \n"); break; case K_ANSIF39 : printf("\\Kansif39 Transmit SCOANSI/AT386: Ctrl-Shift-F3 \n"); break; case K_ANSIF40 : printf("\\Kansif40 Transmit SCOANSI/AT386: Ctrl-Shift-F4 \n"); break; case K_ANSIF41 : printf("\\Kansif41 Transmit SCOANSI/AT386: Ctrl-Shift-F5 \n"); break; case K_ANSIF42 : printf("\\Kansif42 Transmit SCOANSI/AT386: Ctrl-Shift-F6 \n"); break; case K_ANSIF43 : printf("\\Kansif43 Transmit SCOANSI/AT386: Ctrl-Shift-F7 \n"); break; case K_ANSIF44 : printf("\\Kansif44 Transmit SCOANSI/AT386: Ctrl-Shift-F8 \n"); break; case K_ANSIF45 : printf("\\Kansif45 Transmit SCOANSI/AT386: Ctrl-Shift-F9 \n"); break; case K_ANSIF46 : printf("\\Kansif46 Transmit SCOANSI/AT386: Ctrl-Shift-F10\n"); break; case K_ANSIF47 : printf("\\Kansif47 Transmit SCOANSI/AT386: Ctrl-Shift-F11\n"); break; case K_ANSIF48 : printf("\\Kansif48 Transmit SCOANSI/AT386: Ctrl-Shift-F12\n"); break; case K_ANSIF49 : printf("\\Kansif49 Transmit SCOANSI/AT386: Home\n"); break; case K_ANSIF50 : printf("\\Kansif50 Transmit SCOANSI/AT386: Up Arrow\n"); break; case K_ANSIF51 : printf("\\Kansif51 Transmit SCOANSI/AT386: PgUp\n"); break; case K_ANSIF52 : printf("\\Kansif52 Transmit SCOANSI/AT386: Ctrl-Shift-Subtract\n"); break; case K_ANSIF53 : printf("\\Kansif53 Transmit SCOANSI/AT386: Left Arrow\n"); break; case K_ANSIF54 : printf("\\Kansif54 Transmit SCOANSI/AT386: Clear\n"); break; case K_ANSIF55 : printf("\\Kansif55 Transmit SCOANSI/AT386: Right Arrow\n"); break; case K_ANSIF56 : printf("\\Kansif56 Transmit SCOANSI/AT386: Shift-Add\n"); break; case K_ANSIF57 : printf("\\Kansif57 Transmit SCOANSI/AT386: End\n"); break; case K_ANSIF58 : printf("\\Kansif58 Transmit SCOANSI/AT386: Down Arrow\n"); break; case K_ANSIF59 : printf("\\Kansif59 Transmit SCOANSI/AT386: PgDn\n"); break; case K_ANSIF60 : printf("\\Kansif60 Transmit SCOANSI/AT386: Insert\n"); break; case K_ANSIF61 : printf("\\Kansif61 Transmit SCOANSI/AT386: (not named)\n"); break; /* WYSE Function Keys (unshifted) */ case K_WYF01 : printf("\\Kwyf01 Transmit WYSE 30/50/60/160: F1\n"); break; case K_WYF02 : printf("\\Kwyf02 Transmit WYSE 30/50/60/160: F2\n"); break; case K_WYF03 : printf("\\Kwyf03 Transmit WYSE 30/50/60/160: F3\n"); break; case K_WYF04 : printf("\\Kwyf04 Transmit WYSE 30/50/60/160: F4\n"); break; case K_WYF05 : printf("\\Kwyf05 Transmit WYSE 30/50/60/160: F5\n"); break; case K_WYF06 : printf("\\Kwyf06 Transmit WYSE 30/50/60/160: F6\n"); break; case K_WYF07 : printf("\\Kwyf07 Transmit WYSE 30/50/60/160: F7\n"); break; case K_WYF08 : printf("\\Kwyf08 Transmit WYSE 30/50/60/160: F8\n"); break; case K_WYF09 : printf("\\Kwyf09 Transmit WYSE 30/50/60/160: F9\n"); break; case K_WYF10 : printf("\\Kwyf10 Transmit WYSE 30/50/60/160: F10\n"); break; case K_WYF11 : printf("\\Kwyf11 Transmit WYSE 30/50/60/160: F11\n"); break; case K_WYF12 : printf("\\Kwyf12 Transmit WYSE 30/50/60/160: F12\n"); break; case K_WYF13 : printf("\\Kwyf13 Transmit WYSE 30/50/60/160: F13\n"); break; case K_WYF14 : printf("\\Kwyf14 Transmit WYSE 30/50/60/160: F14\n"); break; case K_WYF15 : printf("\\Kwyf15 Transmit WYSE 30/50/60/160: F15\n"); break; case K_WYF16 : printf("\\Kwyf16 Transmit WYSE 30/50/60/160: F16\n"); break; case K_WYF17 : printf("\\Kwyf17 Transmit WYSE 30/50/60/160: F17\n"); break; case K_WYF18 : printf("\\Kwyf18 Transmit WYSE 30/50/60/160: F18\n"); break; case K_WYF19 : printf("\\Kwyf19 Transmit WYSE 30/50/60/160: F19\n"); break; case K_WYF20 : printf("\\Kwyf20 Transmit WYSE 30/50/60/160: F20\n"); break; /* WYSE Function Keys (shifted) */ case K_WYSF01 : printf("\\Kwysf01 Transmit WYSE 30/50/60/160: Shift-F1\n"); break; case K_WYSF02 : printf("\\Kwysf02 Transmit WYSE 30/50/60/160: Shift-F2\n"); break; case K_WYSF03 : printf("\\Kwysf03 Transmit WYSE 30/50/60/160: Shift-F3\n"); break; case K_WYSF04 : printf("\\Kwysf04 Transmit WYSE 30/50/60/160: Shift-F4\n"); break; case K_WYSF05 : printf("\\Kwysf05 Transmit WYSE 30/50/60/160: Shift-F5\n"); break; case K_WYSF06 : printf("\\Kwysf06 Transmit WYSE 30/50/60/160: Shift-F6\n"); break; case K_WYSF07 : printf("\\Kwysf07 Transmit WYSE 30/50/60/160: Shift-F7\n"); break; case K_WYSF08 : printf("\\Kwysf08 Transmit WYSE 30/50/60/160: Shift-F8\n"); break; case K_WYSF09 : printf("\\Kwysf09 Transmit WYSE 30/50/60/160: Shift-F9\n"); break; case K_WYSF10 : printf("\\Kwysf10 Transmit WYSE 30/50/60/160: Shift-F10\n"); break; case K_WYSF11 : printf("\\Kwysf11 Transmit WYSE 30/50/60/160: Shift-F11\n"); break; case K_WYSF12 : printf("\\Kwysf12 Transmit WYSE 30/50/60/160: Shift-F12\n"); break; case K_WYSF13 : printf("\\Kwysf13 Transmit WYSE 30/50/60/160: Shift-F13\n"); break; case K_WYSF14 : printf("\\Kwysf14 Transmit WYSE 30/50/60/160: Shift-F14\n"); break; case K_WYSF15 : printf("\\Kwysf15 Transmit WYSE 30/50/60/160: Shift-F15\n"); break; case K_WYSF16 : printf("\\Kwysf16 Transmit WYSE 30/50/60/160: Shift-F16\n"); break; case K_WYSF17 : printf("\\Kwysf17 Transmit WYSE 30/50/60/160: Shift-F17\n"); break; case K_WYSF18 : printf("\\Kwysf18 Transmit WYSE 30/50/60/160: Shift-F18\n"); break; case K_WYSF19 : printf("\\Kwysf19 Transmit WYSE 30/50/60/160: Shift-F19\n"); break; case K_WYSF20 : printf("\\Kwysf20 Transmit WYSE 30/50/60/160: Shift-F20\n"); break; /* WYSE Edit and Special Keys */ case K_WYBS : printf("\\Kwybs Transmit WYSE 30/50/60/160: Backspace\n"); break; case K_WYCLRLN : printf("\\Kwyclrln Transmit WYSE 30/50/60/160: Clear Line\n"); break; case K_WYSCLRLN : printf("\\Kwysclrln Transmit WYSE 30/50/60/160: Shift-Clear Line\n"); break; case K_WYCLRPG : printf("\\Kwyclrpg Transmit WYSE 30/50/60/160: Clear Page\n"); break; case K_WYSCLRPG : printf("\\Kwysclrpg Transmit WYSE 30/50/60/160: Shift-Clear Page\n"); break; case K_WYDELCHAR : printf("\\Kwydelchar Transmit WYSE 30/50/60/160: Delete Char\n"); break; case K_WYDELLN : printf("\\Kwydelln Transmit WYSE 30/50/60/160: Delete Line\n"); break; case K_WYENTER : printf("\\Kwyenter Transmit WYSE 30/50/60/160: Enter\n"); break; case K_WYESC : printf("\\Kwyesc Transmit WYSE 30/50/60/160: Esc\n"); break; case K_WYHOME : printf("\\Kwyhome Transmit WYSE 30/50/60/160: Home\n"); break; case K_WYSHOME : printf("\\Kwyshome Transmit WYSE 30/50/60/160: Shift-Home\n"); break; case K_WYINSERT : printf("\\Kwyinsert Transmit WYSE 30/50/60/160: Insert\n"); break; case K_WYINSCHAR : printf("\\Kwyinschar Transmit WYSE 30/50/60/160: Insert Char\n"); break; case K_WYINSLN : printf("\\Kwyinsln Transmit WYSE 30/50/60/160: Insert Line\n"); break; case K_WYPGNEXT : printf("\\Kwypgnext Transmit WYSE 30/50/60/160: Page Next\n"); break; case K_WYPGPREV : printf("\\Kwypgprev Transmit WYSE 30/50/60/160: Page Previous\n"); break; case K_WYREPLACE : printf("\\Kwyreplace Transmit WYSE 30/50/60/160: Replace \n"); break; case K_WYRETURN : printf("\\Kwyreturn Transmit WYSE 30/50/60/160: Return \n"); break; case K_WYTAB : printf("\\Kwytab Transmit WYSE 30/50/60/160: Tab \n"); break; case K_WYSTAB : printf("\\Kwystab Transmit WYSE 30/50/60/160: Shift-Tab \n"); break; case K_WYPRTSCN : printf("\\Kwyprtscn Transmit WYSE 30/50/60/160: Print Screen \n"); break; case K_WYSESC : printf("\\Kwysesc Transmit WYSE 30/50/60/160: Shift-Esc \n"); break; case K_WYSBS : printf("\\Kwysbs Transmit WYSE 30/50/60/160: Shift-Backspace\n"); break; case K_WYSENTER : printf("\\Kwysenter Transmit WYSE 30/50/60/160: Shift-Enter\n"); break; case K_WYSRETURN : printf("\\Kwysreturn Transmit WYSE 30/50/60/160: Shift-Return\n"); break; case K_WYUPARR : printf("\\Kwyuparr Transmit WYSE 30/50/60/160: Up Arrow\n"); break; case K_WYDNARR : printf("\\Kwydnarr Transmit WYSE 30/50/60/160: Down Arrow\n"); break; case K_WYLFARR : printf("\\Kwylfarr Transmit WYSE 30/50/60/160: Left Arrow\n"); break; case K_WYRTARR : printf("\\Kwyrtarr Transmit WYSE 30/50/60/160: Right Arrow\n"); break; case K_WYSUPARR : printf("\\Kwysuparr Transmit WYSE 30/50/60/160: Shift-Up Arrow\n"); break; case K_WYSDNARR : printf("\\Kwysdnarr Transmit WYSE 30/50/60/160: Shift-Down Arrow\n"); break; case K_WYSLFARR : printf("\\Kwyslfarr Transmit WYSE 30/50/60/160: Shift-Left Arrow\n"); break; case K_WYSRTARR : printf("\\Kwysrtarr Transmit WYSE 30/50/60/160: Shift-Right Arrow\n"); break; case K_WYSEND: printf("\\Kwysend Transmit WYSE 30/50/60/160: Send\n"); break; case K_WYSSEND: printf("\\Kwyssend Transmit WYSE 30/50/60/160: Shift-Send\n"); break; /* Data General Function Keys (unshifted) */ case K_DGF01 : printf("\\Kdgf01 Transmit Data General: F1 \n"); break; case K_DGF02 : printf("\\Kdgf01 Transmit Data General: F2 \n"); break; case K_DGF03 : printf("\\Kdgf01 Transmit Data General: F3 \n"); break; case K_DGF04 : printf("\\Kdgf01 Transmit Data General: F4 \n"); break; case K_DGF05 : printf("\\Kdgf01 Transmit Data General: F5 \n"); break; case K_DGF06 : printf("\\Kdgf01 Transmit Data General: F6 \n"); break; case K_DGF07 : printf("\\Kdgf01 Transmit Data General: F7 \n"); break; case K_DGF08 : printf("\\Kdgf01 Transmit Data General: F8 \n"); break; case K_DGF09 : printf("\\Kdgf01 Transmit Data General: F9 \n"); break; case K_DGF10 : printf("\\Kdgf01 Transmit Data General: F10 \n"); break; case K_DGF11 : printf("\\Kdgf01 Transmit Data General: F11 \n"); break; case K_DGF12 : printf("\\Kdgf01 Transmit Data General: F12 \n"); break; case K_DGF13 : printf("\\Kdgf01 Transmit Data General: F13 \n"); break; case K_DGF14 : printf("\\Kdgf01 Transmit Data General: F14 \n"); break; case K_DGF15 : printf("\\Kdgf01 Transmit Data General: F15 \n"); break; /* Data General Function Keys (shifted) */ case K_DGSF01 : printf( "\\Kdgsf01 Transmit Data General: Shift-F1 \n"); break; case K_DGSF02 : printf( "\\Kdgsf02 Transmit Data General: Shift-F2 \n"); break; case K_DGSF03 : printf( "\\Kdgsf03 Transmit Data General: Shift-F3 \n"); break; case K_DGSF04 : printf( "\\Kdgsf04 Transmit Data General: Shift-F4 \n"); break; case K_DGSF05 : printf( "\\Kdgsf05 Transmit Data General: Shift-F5 \n"); break; case K_DGSF06 : printf( "\\Kdgsf06 Transmit Data General: Shift-F6 \n"); break; case K_DGSF07 : printf( "\\Kdgsf07 Transmit Data General: Shift-F7 \n"); break; case K_DGSF08 : printf( "\\Kdgsf08 Transmit Data General: Shift-F8 \n"); break; case K_DGSF09 : printf( "\\Kdgsf09 Transmit Data General: Shift-F9 \n"); break; case K_DGSF10 : printf( "\\Kdgsf10 Transmit Data General: Shift-F10 \n"); break; case K_DGSF11 : printf( "\\Kdgsf11 Transmit Data General: Shift-F11 \n"); break; case K_DGSF12 : printf( "\\Kdgsf12 Transmit Data General: Shift-F12 \n"); break; case K_DGSF13 : printf( "\\Kdgsf13 Transmit Data General: Shift-F13 \n"); break; case K_DGSF14 : printf( "\\Kdgsf14 Transmit Data General: Shift-F14 \n"); break; case K_DGSF15 : printf( "\\Kdgsf15 Transmit Data General: Shift-F15 \n"); break; /* Data General Function Keys (control) */ case K_DGCF01 : printf( "\\Kdgcf01 Transmit Data General: Ctrl-F1 \n"); break; case K_DGCF02 : printf( "\\Kdgcf02 Transmit Data General: Ctrl-F2 \n"); break; case K_DGCF03 : printf( "\\Kdgcf03 Transmit Data General: Ctrl-F3 \n"); break; case K_DGCF04 : printf( "\\Kdgcf04 Transmit Data General: Ctrl-F4 \n"); break; case K_DGCF05 : printf( "\\Kdgcf05 Transmit Data General: Ctrl-F5 \n"); break; case K_DGCF06 : printf( "\\Kdgcf06 Transmit Data General: Ctrl-F6 \n"); break; case K_DGCF07 : printf( "\\Kdgcf07 Transmit Data General: Ctrl-F7 \n"); break; case K_DGCF08 : printf( "\\Kdgcf08 Transmit Data General: Ctrl-F8 \n"); break; case K_DGCF09 : printf( "\\Kdgcf09 Transmit Data General: Ctrl-F9 \n"); break; case K_DGCF10 : printf( "\\Kdgcf10 Transmit Data General: Ctrl-F10 \n"); break; case K_DGCF11 : printf( "\\Kdgcf11 Transmit Data General: Ctrl-F11 \n"); break; case K_DGCF12 : printf( "\\Kdgcf12 Transmit Data General: Ctrl-F12 \n"); break; case K_DGCF13 : printf( "\\Kdgcf13 Transmit Data General: Ctrl-F13 \n"); break; case K_DGCF14 : printf( "\\Kdgcf14 Transmit Data General: Ctrl-F14 \n"); break; case K_DGCF15 : printf( "\\Kdgcf15 Transmit Data General: Ctrl-F15 \n"); break; /* Data General Function Keys (control shifted) */ case K_DGCSF01 : printf( "\\Kdgcsf01 Transmit Data General: Ctrl-Shift-F1 \n"); break; case K_DGCSF02 : printf( "\\Kdgcsf02 Transmit Data General: Ctrl-Shift-F2 \n"); break; case K_DGCSF03 : printf( "\\Kdgcsf03 Transmit Data General: Ctrl-Shift-F3 \n"); break; case K_DGCSF04 : printf( "\\Kdgcsf04 Transmit Data General: Ctrl-Shift-F4 \n"); break; case K_DGCSF05 : printf( "\\Kdgcsf05 Transmit Data General: Ctrl-Shift-F5 \n"); break; case K_DGCSF06 : printf( "\\Kdgcsf06 Transmit Data General: Ctrl-Shift-F6 \n"); break; case K_DGCSF07 : printf( "\\Kdgcsf07 Transmit Data General: Ctrl-Shift-F7 \n"); break; case K_DGCSF08 : printf( "\\Kdgcsf08 Transmit Data General: Ctrl-Shift-F8 \n"); break; case K_DGCSF09 : printf( "\\Kdgcsf09 Transmit Data General: Ctrl-Shift-F9 \n"); break; case K_DGCSF10 : printf( "\\Kdgcsf10 Transmit Data General: Ctrl-Shift-F10 \n"); break; case K_DGCSF11 : printf( "\\Kdgcsf11 Transmit Data General: Ctrl-Shift-F11 \n"); break; case K_DGCSF12 : printf( "\\Kdgcsf12 Transmit Data General: Ctrl-Shift-F12 \n"); break; case K_DGCSF13 : printf( "\\Kdgcsf13 Transmit Data General: Ctrl-Shift-F13 \n"); break; case K_DGCSF14 : printf( "\\Kdgcsf14 Transmit Data General: Ctrl-Shift-F14 \n"); break; case K_DGCSF15 : printf( "\\Kdgcsf15 Transmit Data General: Ctrl-Shift-F15 \n"); break; case K_DGUPARR : printf("\\Kdguparr Transmit Data General: Up Arrow \n"); break; case K_DGDNARR : printf("\\Kdgdnarr Transmit Data General: Down Arrow \n"); break; case K_DGLFARR : printf("\\Kdglfarr Transmit Data General: Left Arrow \n"); break; case K_DGRTARR : printf("\\Kdgrtarr Transmit Data General: Right Arrow \n"); break; case K_DGSUPARR : printf("\\Kdgsuparr Transmit Data General: Shift-Up Arrow \n"); break; case K_DGSDNARR : printf("\\Kdgsdnarr Transmit Data General: Shift-Down Arrow \n"); break; case K_DGSLFARR : printf("\\Kdgslfarr Transmit Data General: Shift-Left Arrow \n"); break; case K_DGSRTARR : printf("\\Kdgsrtarr Transmit Data General: Shift-Right Arrow \n"); break; case K_DGERASEPAGE : printf("\\Kdgerasepage Transmit Data General: Erase Page \n"); break; case K_DGC1 : printf("\\Kdgc1 Transmit Data General: C1 \n"); break; case K_DGC2 : printf("\\Kdgc2 Transmit Data General: C2 \n"); break; case K_DGERASEEOL : printf("\\Kdgeraseeol Transmit Data General: Erase EOL \n"); break; case K_DGC3 : printf("\\Kdgc3 Transmit Data General: C3 \n"); break; case K_DGC4 : printf("\\Kdgc4 Transmit Data General: C4 \n"); break; case K_DGCMDPRINT : printf("\\Kdgcmdprint Transmit Data General: Command Print \n"); break; case K_DGHOME : printf("\\Kdghome Transmit Data General: Home \n"); break; case K_DGSERASEPAGE : printf("\\Kdgserasepage Transmit Data General: Erase Page \n"); break; case K_DGSC1 : printf("\\Kdgsc1 Transmit Data General: Shift-C1 \n"); break; case K_DGSC2 : printf("\\Kdgsc2 Transmit Data General: Shift-C2 \n"); break; case K_DGSERASEEOL : printf("\\Kdgseraseeol Transmit Data General: Shift-Erase EOL \n"); break; case K_DGSC3 : printf("\\Kdgsc3 Transmit Data General: Shift-C3 \n"); break; case K_DGSC4 : printf("\\Kdgsc4 Transmit Data General: Shift-C4 \n"); break; case K_DGSCMDPRINT : printf("\\Kdgscmdprint Transmit Data General: Shift-Command Print\n"); break; case K_DGBS : printf("\\Kdgbs Transmit Data General: Backspace \n"); break; case K_DGSHOME : printf("\\Kdshome Transmit Data General: Shift-Home \n"); break; /* Televideo Function Keys (unshifted) */ case K_TVIF01 : printf("\\Ktvif01 Transmit Televideo: F1 \n"); break; case K_TVIF02 : printf("\\Ktvif02 Transmit Televideo: F2 \n"); break; case K_TVIF03 : printf("\\Ktvif03 Transmit Televideo: F3 \n"); break; case K_TVIF04 : printf("\\Ktvif04 Transmit Televideo: F4 \n"); break; case K_TVIF05 : printf("\\Ktvif05 Transmit Televideo: F5 \n"); break; case K_TVIF06 : printf("\\Ktvif06 Transmit Televideo: F6 \n"); break; case K_TVIF07 : printf("\\Ktvif07 Transmit Televideo: F7 \n"); break; case K_TVIF08 : printf("\\Ktvif08 Transmit Televideo: F8 \n"); break; case K_TVIF09 : printf("\\Ktvif09 Transmit Televideo: F9 \n"); break; case K_TVIF10 : printf("\\Ktvif10 Transmit Televideo: F10 \n"); break; case K_TVIF11 : printf("\\Ktvif11 Transmit Televideo: F11 \n"); break; case K_TVIF12 : printf("\\Ktvif12 Transmit Televideo: F12 \n"); break; case K_TVIF13 : printf("\\Ktvif13 Transmit Televideo: F13 \n"); break; case K_TVIF14 : printf("\\Ktvif14 Transmit Televideo: F14 \n"); break; case K_TVIF15 : printf("\\Ktvif15 Transmit Televideo: F15 \n"); break; case K_TVIF16 : printf("\\Ktvif16 Transmit Televideo: F16 \n"); break; /* Televideo Function Keys (shifted) */ case K_TVISF01 : printf("\\Ktvisf01 Transmit Televideo: Shift-F1 \n"); break; case K_TVISF02 : printf("\\Ktvisf02 Transmit Televideo: Shift-F2 \n"); break; case K_TVISF03 : printf("\\Ktvisf03 Transmit Televideo: Shift-F3 \n"); break; case K_TVISF04 : printf("\\Ktvisf04 Transmit Televideo: Shift-F4 \n"); break; case K_TVISF05 : printf("\\Ktvisf05 Transmit Televideo: Shift-F5 \n"); break; case K_TVISF06 : printf("\\Ktvisf06 Transmit Televideo: Shift-F6 \n"); break; case K_TVISF07 : printf("\\Ktvisf07 Transmit Televideo: Shift-F7 \n"); break; case K_TVISF08 : printf("\\Ktvisf08 Transmit Televideo: Shift-F8 \n"); break; case K_TVISF09 : printf("\\Ktvisf09 Transmit Televideo: Shift-F9 \n"); break; case K_TVISF10 : printf("\\Ktvisf10 Transmit Televideo: Shift-F10\n"); break; case K_TVISF11 : printf("\\Ktvisf11 Transmit Televideo: Shift-F11\n"); break; case K_TVISF12 : printf("\\Ktvisf12 Transmit Televideo: Shift-F12\n"); break; case K_TVISF13 : printf("\\Ktvisf13 Transmit Televideo: Shift-F13\n"); break; case K_TVISF14 : printf("\\Ktvisf14 Transmit Televideo: Shift-F14\n"); break; case K_TVISF15 : printf("\\Ktvisf15 Transmit Televideo: Shift-F15\n"); break; case K_TVISF16 : printf("\\Ktvisf16 Transmit Televideo: Shift-F16\n"); break; /* Televideo Edit and Special Keys */ case K_TVIBS : printf("\\Ktvibs Transmit Televideo: Backspace \n"); break; case K_TVICLRLN : printf("\\Ktviclrln Transmit Televideo: Clear Line \n"); break; case K_TVISCLRLN : printf("\\Ktvisclrln Transmit Televideo: Shift-Clear Line\n"); break; case K_TVICLRPG : printf("\\Ktviclrpg Transmit Televideo: Clear Page \n"); break; case K_TVISCLRPG : printf("\\Ktvisclrpg Transmit Televideo: Shift-Clear Page\n"); break; case K_TVIDELCHAR : printf("\\Ktvidelchar Transmit Televideo: Delete Char \n"); break; case K_TVIDELLN : printf("\\Ktvidelln Transmit Televideo: Delete Line \n"); break; case K_TVIENTER : printf("\\Ktvienter Transmit Televideo: Enter \n"); break; case K_TVIESC : printf("\\Ktviesc Transmit Televideo: Esc \n"); break; case K_TVIHOME : printf("\\Ktvihome Transmit Televideo: Home \n"); break; case K_TVISHOME : printf("\\Ktvishome Transmit Televideo: Shift-Home \n"); break; case K_TVIINSERT : printf("\\Ktviinsert Transmit Televideo: Insert \n"); break; case K_TVIINSCHAR : printf("\\Ktviinschar Transmit Televideo: Insert Char \n"); break; case K_TVIINSLN : printf("\\Ktviinsln Transmit Televideo: Insert Line \n"); break; case K_TVIPGNEXT : printf("\\Ktvipgnext Transmit Televideo: Page Next \n"); break; case K_TVIPGPREV : printf("\\Ktvipgprev Transmit Televideo: Page Previous \n"); break; case K_TVIREPLACE : printf("\\Ktvireplace Transmit Televideo: Replace \n"); break; case K_TVIRETURN : printf("\\Ktvireturn Transmit Televideo: Return \n"); break; case K_TVITAB : printf("\\Ktvitab Transmit Televideo: Tab \n"); break; case K_TVISTAB : printf("\\Ktvistab Transmit Televideo: Shift-Tab \n"); break; case K_TVIPRTSCN : printf("\\Ktviprtscn Transmit Televideo: Print Screen \n"); break; case K_TVISESC : printf("\\Ktvisesc Transmit Televideo: Shift-Esc \n"); break; case K_TVISBS : printf("\\Ktvisbs Transmit Televideo: Shift-Backspace \n"); break; case K_TVISENTER : printf("\\Ktvisenter Transmit Televideo: Shift-Enter \n"); break; case K_TVISRETURN : printf("\\Ktvisreturn Transmit Televideo: Shift-Return \n"); break; case K_TVIUPARR : printf("\\Ktviuparr Transmit Televideo: Up Arrow \n"); break; case K_TVIDNARR : printf("\\Ktvidnarr Transmit Televideo: Down Arrow \n"); break; case K_TVILFARR : printf("\\Ktvilfarr Transmit Televideo: Left Arrow \n"); break; case K_TVIRTARR : printf("\\Ktvirtarr Transmit Televideo: Right Arrow \n"); break; case K_TVISUPARR : printf("\\Ktvisuparr Transmit Televideo: Shift-Up Arrow \n"); break; case K_TVISDNARR : printf("\\Ktvisdnarr Transmit Televideo: Shift-Down Arrow\n"); break; case K_TVISLFARR : printf("\\Ktvislfarr Transmit Televideo: Shift-Left Arrow\n"); break; case K_TVISRTARR : printf("\\Ktvisrtarr Transmit Televideo: Shift-Right Arrow\n"); break; case K_TVISEND: printf("\\Ktvisend Transmit Televideo: Send\n"); break; case K_TVISSEND: printf("\\Ktvissend Transmit Televideo: Shift-Send\n"); break; /* HP Function and Edit keys */ case K_HPF01 : printf("\\Khpf01 Transmit Hewlett-Packard: F1 \n"); break; case K_HPF02 : printf("\\Khpf02 Transmit Hewlett-Packard: F2 \n"); break; case K_HPF03 : printf("\\Khpf03 Transmit Hewlett-Packard: F3 \n"); break; case K_HPF04 : printf("\\Khpf04 Transmit Hewlett-Packard: F4 \n"); break; case K_HPF05 : printf("\\Khpf05 Transmit Hewlett-Packard: F5 \n"); break; case K_HPF06 : printf("\\Khpf06 Transmit Hewlett-Packard: F6 \n"); break; case K_HPF07 : printf("\\Khpf07 Transmit Hewlett-Packard: F7 \n"); break; case K_HPF08 : printf("\\Khpf08 Transmit Hewlett-Packard: F8 \n"); break; case K_HPF09 : printf("\\Khpf09 Transmit Hewlett-Packard: F9 \n"); break; case K_HPF10 : printf("\\Khpf10 Transmit Hewlett-Packard: F10 \n"); break; case K_HPF11 : printf("\\Khpf11 Transmit Hewlett-Packard: F11 \n"); break; case K_HPF12 : printf("\\Khpf12 Transmit Hewlett-Packard: F12 \n"); break; case K_HPF13 : printf("\\Khpf13 Transmit Hewlett-Packard: F13 \n"); break; case K_HPF14 : printf("\\Khpf14 Transmit Hewlett-Packard: F14 \n"); break; case K_HPF15 : printf("\\Khpf15 Transmit Hewlett-Packard: F15 \n"); break; case K_HPF16 : printf("\\Khpf16 Transmit Hewlett-Packard: F16 \n"); break; case K_HPRETURN : printf("\\Khpreturn Transmit Hewlett-Packard: Return\n"); break; case K_HPENTER : printf("\\Khpenter Transmit Hewlett-Packard: Enter (keypad)\n"); break; case K_HPBACKTAB : printf("\\Khpbacktab Transmit Hewlett-Packard: Back Tab\n"); break; /* Siemens Nixdorf International 97801-5xx kverbs */ case K_SNI_DOUBLE_0 : printf("\\Ksni_00 Transmit SNI-97801-5xx: Double-Zero\n"); break; case K_SNI_C_DOUBLE_0 : printf( "\\Ksni_c_00 Transmit SNI-97801-5xx: Ctrl-Double-Zero\n"); break; case K_SNI_C_CE : printf("\\Ksni_c_ce Transmit SNI-97801-5xx: Ctrl-CE\n"); break; case K_SNI_C_COMPOSE : printf("\\Ksni_c_compose Transmit SNI-97801-5xx: Ctrl-Compose\n"); break; case K_SNI_C_DELETE_CHAR : printf( "\\Ksni_c_del_char Transmit SNI-97801-5xx: Ctrl-Delete Char\n"); break; case K_SNI_C_DELETE_LINE : printf( "\\Ksni_c_del_line Transmit SNI-97801-5xx: Ctrl-Delete Line\n"); break; case K_SNI_C_DELETE_WORD : printf( "\\Ksni_c_del_word Transmit SNI-97801-5xx: Ctrl-Delete Word\n"); break; case K_SNI_C_CURSOR_DOWN : printf( "\\Ksni_c_dnarr Transmit SNI-97801-5xx: Ctrl-Cursor Down\n"); break; case K_SNI_C_ENDMARKE : printf("\\Ksni_c_endmarke Transmit SNI-97801-5xx: Ctrl-End Marke\n"); break; case K_SNI_C_F01 : printf("\\Ksni_c_f01 Transmit SNI-97801-5xx: Ctrl-F1\n"); break; case K_SNI_C_F02 : printf("\\Ksni_c_f02 Transmit SNI-97801-5xx: Ctrl-F2\n"); break; case K_SNI_C_F03 : printf("\\Ksni_c_f03 Transmit SNI-97801-5xx: Ctrl-F3\n"); break; case K_SNI_C_F04 : printf("\\Ksni_c_f04 Transmit SNI-97801-5xx: Ctrl-F4\n"); break; case K_SNI_C_F05 : printf("\\Ksni_c_f05 Transmit SNI-97801-5xx: Ctrl-F5\n"); break; case K_SNI_C_F06 : printf("\\Ksni_c_f06 Transmit SNI-97801-5xx: Ctrl-F6\n"); break; case K_SNI_C_F07 : printf("\\Ksni_c_f07 Transmit SNI-97801-5xx: Ctrl-F7\n"); break; case K_SNI_C_F08 : printf("\\Ksni_c_f08 Transmit SNI-97801-5xx: Ctrl-F8\n"); break; case K_SNI_C_F09 : printf("\\Ksni_c_f09 Transmit SNI-97801-5xx: Ctrl-F9\n"); break; case K_SNI_C_F10 : printf("\\Ksni_c_f10 Transmit SNI-97801-5xx: Ctrl-F10\n"); break; case K_SNI_C_F11 : printf("\\Ksni_c_f11 Transmit SNI-97801-5xx: Ctrl-F11\n"); break; case K_SNI_C_F12 : printf("\\Ksni_c_f12 Transmit SNI-97801-5xx: Ctrl-F12\n"); break; case K_SNI_C_F13 : printf("\\Ksni_c_f13 Transmit SNI-97801-5xx: Ctrl-F13\n"); break; case K_SNI_C_F14 : printf("\\Ksni_c_f14 Transmit SNI-97801-5xx: Ctrl-F14\n"); break; case K_SNI_C_F15 : printf("\\Ksni_c_f15 Transmit SNI-97801-5xx: Ctrl-F15\n"); break; case K_SNI_C_F16 : printf("\\Ksni_c_f16 Transmit SNI-97801-5xx: Ctrl-F16\n"); break; case K_SNI_C_F17 : printf("\\Ksni_c_f17 Transmit SNI-97801-5xx: Ctrl-F17\n"); break; case K_SNI_C_F18 : printf("\\Ksni_c_f18 Transmit SNI-97801-5xx: Ctrl-F18\n"); break; case K_SNI_C_USER1 : printf( "\\Ksni_c_user1 Transmit SNI-97801-5xx: Ctrl-Key below F18\n"); break; case K_SNI_C_F19 : printf("\\Ksni_c_f19 Transmit SNI-97801-5xx: Ctrl-F19\n"); break; case K_SNI_C_USER2 : printf( "\\Ksni_c_user2 Transmit SNI-97801-5xx: Ctrl-Key below F19\n"); break; case K_SNI_C_F20 : printf("\\Ksni_c_f20 Transmit SNI-97801-5xx: Ctrl-F20\n"); break; case K_SNI_C_USER3 : printf( "\\Ksni_c_user3 Transmit SNI-97801-5xx: Ctrl-Key below F20\n"); break; case K_SNI_C_F21 : printf("\\Ksni_c_f21 Transmit SNI-97801-5xx: Ctrl-F21\n"); break; case K_SNI_C_USER4 : printf( "\\Ksni_c_user4 Transmit SNI-97801-5xx: Ctrl-Key below F21\n"); break; case K_SNI_C_F22 : printf("\\Ksni_c_f22 Transmit SNI-97801-5xx: Ctrl-F22\n"); break; case K_SNI_C_USER5 : printf( "\\Ksni_c_user5 Transmit SNI-97801-5xx: Ctrl-Key below F22\n"); break; case K_SNI_C_HELP : printf("\\Ksni_c_help Transmit SNI-97801-5xx: Ctrl-Help\n"); break; case K_SNI_C_HOME : printf("\\Ksni_c_home Transmit SNI-97801-5xx: Ctrl-Home\n"); break; case K_SNI_C_INSERT_CHAR : printf( "\\Ksni_c_ins_char Transmit SNI-97801-5xx: Ctrl-Insert Char\n"); break; case K_SNI_C_INSERT_LINE : printf( "\\Ksni_c_ins_line Transmit SNI-97801-5xx: Ctrl-Insert Line\n"); break; case K_SNI_C_INSERT_WORD : printf( "\\Ksni_c_ins_word Transmit SNI-97801-5xx: Ctrl-Insert Word\n"); break; case K_SNI_C_LEFT_TAB : printf("\\Ksni_c_left_tab Transmit SNI-97801-5xx: Ctrl-Left Tab\n"); break; case K_SNI_C_CURSOR_LEFT : printf( "\\Ksni_c_lfarr Transmit SNI-97801-5xx: Ctrl-Cursor Left\n"); break; case K_SNI_C_MODE : printf("\\Ksni_c_mode Transmit SNI-97801-5xx: Ctrl-Mode\n"); break; case K_SNI_C_PAGE : printf("\\Ksni_c_page Transmit SNI-97801-5xx: Ctrl-Page\n"); break; case K_SNI_C_PRINT : printf("\\Ksni_c_print Transmit SNI-97801-5xx: Ctrl-Print\n"); break; case K_SNI_C_CURSOR_RIGHT: printf( "\\Ksni_c_rtarr Transmit SNI-97801-5xx: Ctrl-Cursor Right\n"); break; case K_SNI_C_SCROLL_DOWN : printf( "\\Ksni_c_scroll_dn Transmit SNI-97801-5xx: Ctrl-Scroll Down\n"); break; case K_SNI_C_SCROLL_UP : printf("\\Ksni_c_scroll_up Transmit SNI-97801-5xx: Ctrl-Scroll Up\n"); break; case K_SNI_C_START : printf("\\Ksni_c_start Transmit SNI-97801-5xx: Ctrl-Start\n"); break; case K_SNI_C_CURSOR_UP : printf("\\Ksni_c_uparr Transmit SNI-97801-5xx: Ctrl-Cursor Up\n"); break; case K_SNI_C_TAB : printf("\\Ksni_c_tab Transmit SNI-97801-5xx: Ctrl-Tab\n"); break; case K_SNI_CE : printf("\\Ksni_ce Transmit SNI-97801-5xx: CE\n"); break; case K_SNI_CH_CODE: printf("\\Ksni_ch_code Toggle SNI-97801-5xx: CH.CODE function.\n"); break; case K_SNI_COMPOSE : printf("\\Ksni_compose Transmit SNI-97801-5xx: Compose\n"); break; case K_SNI_DELETE_CHAR : printf("\\Ksni_del_char Transmit SNI-97801-5xx: Delete Char\n"); break; case K_SNI_DELETE_LINE : printf("\\Ksni_del_line Transmit SNI-97801-5xx: Delete Line\n"); break; case K_SNI_DELETE_WORD : printf("\\Ksni_del_word Transmit SNI-97801-5xx: Delete Word\n"); break; case K_SNI_CURSOR_DOWN : printf("\\Ksni_dnarr Transmit SNI-97801-5xx: Cursor Down\n"); break; case K_SNI_ENDMARKE : printf("\\Ksni_endmarke Transmit SNI-97801-5xx: End Marke\n"); break; case K_SNI_F01 : printf("\\Ksni_f01 Transmit SNI-97801-5xx: F1\n"); break; case K_SNI_F02 : printf("\\Ksni_f02 Transmit SNI-97801-5xx: F2\n"); break; case K_SNI_F03 : printf("\\Ksni_f03 Transmit SNI-97801-5xx: F3\n"); break; case K_SNI_F04 : printf("\\Ksni_f04 Transmit SNI-97801-5xx: F4\n"); break; case K_SNI_F05 : printf("\\Ksni_f05 Transmit SNI-97801-5xx: F5\n"); break; case K_SNI_F06 : printf("\\Ksni_f06 Transmit SNI-97801-5xx: F6\n"); break; case K_SNI_F07 : printf("\\Ksni_f07 Transmit SNI-97801-5xx: F7\n"); break; case K_SNI_F08 : printf("\\Ksni_f08 Transmit SNI-97801-5xx: F8\n"); break; case K_SNI_F09 : printf("\\Ksni_f09 Transmit SNI-97801-5xx: F9\n"); break; case K_SNI_F10 : printf("\\Ksni_f10 Transmit SNI-97801-5xx: F10\n"); break; case K_SNI_F11 : printf("\\Ksni_f11 Transmit SNI-97801-5xx: F11\n"); break; case K_SNI_F12 : printf("\\Ksni_f12 Transmit SNI-97801-5xx: F12\n"); break; case K_SNI_F13 : printf("\\Ksni_f13 Transmit SNI-97801-5xx: F13\n"); break; case K_SNI_F14 : printf("\\Ksni_f14 Transmit SNI-97801-5xx: F14\n"); break; case K_SNI_F15 : printf("\\Ksni_f15 Transmit SNI-97801-5xx: F15\n"); break; case K_SNI_F16 : printf("\\Ksni_f16 Transmit SNI-97801-5xx: F16\n"); break; case K_SNI_F17 : printf("\\Ksni_f17 Transmit SNI-97801-5xx: F17\n"); break; case K_SNI_F18 : printf("\\Ksni_f18 Transmit SNI-97801-5xx: F18\n"); break; case K_SNI_USER1 : printf("\\Ksni_user1 Transmit SNI-97801-5xx: Key below F18\n"); break; case K_SNI_F19 : printf("\\Ksni_f19 Transmit SNI-97801-5xx: F19\n"); break; case K_SNI_USER2 : printf("\\Ksni_user2 Transmit SNI-97801-5xx: Key below F19\n"); break; case K_SNI_F20 : printf("\\Ksni_f20 Transmit SNI-97801-5xx: F20\n"); break; case K_SNI_USER3 : printf("\\Ksni_user3 Transmit SNI-97801-5xx: Key below F20\n"); break; case K_SNI_F21 : printf("\\Ksni_f21 Transmit SNI-97801-5xx: F21\n"); break; case K_SNI_USER4 : printf("\\Ksni_user4 Transmit SNI-97801-5xx: Key below F21\n"); break; case K_SNI_F22 : printf("\\Ksni_f22 Transmit SNI-97801-5xx: F22\n"); break; case K_SNI_USER5 : printf("\\Ksni_user5 Transmit SNI-97801-5xx: Key below F22\n"); break; case K_SNI_HELP : printf("\\Ksni_help Transmit SNI-97801-5xx: Help\n"); break; case K_SNI_HOME : printf("\\Ksni_home Transmit SNI-97801-5xx: Home\n"); break; case K_SNI_INSERT_CHAR : printf("\\Ksni_ins_char Transmit SNI-97801-5xx: Insert Char\n"); break; case K_SNI_INSERT_LINE : printf("\\Ksni_ins_line Transmit SNI-97801-5xx: Insert Line\n"); break; case K_SNI_INSERT_WORD : printf("\\Ksni_ins_word Transmit SNI-97801-5xx: Insert Word\n"); break; case K_SNI_LEFT_TAB : printf("\\Ksni_left_tab Transmit SNI-97801-5xx: Left Tab\n"); break; case K_SNI_CURSOR_LEFT : printf("\\Ksni_lfarr Transmit SNI-97801-5xx: Cursor Left\n"); break; case K_SNI_MODE : printf("\\Ksni_mode Transmit SNI-97801-5xx: Mode\n"); break; case K_SNI_PAGE : printf("\\Ksni_page Transmit SNI-97801-5xx: Page\n"); break; case K_SNI_PRINT : printf("\\Ksni_print Transmit SNI-97801-5xx: Print\n"); break; case K_SNI_CURSOR_RIGHT : printf("\\Ksni_rtarr Transmit SNI-97801-5xx: Cursor Right\n"); break; case K_SNI_S_DOUBLE_0 : printf( "\\Ksni_s_00 Transmit SNI-97801-5xx: Shift-Double-Zero\n"); break; case K_SNI_S_CE : printf("\\Ksni_s_ce Transmit SNI-97801-5xx: Shift-CE\n"); break; case K_SNI_S_COMPOSE : printf("\\Ksni_s_compose Transmit SNI-97801-5xx: Shift-Compose\n"); break; case K_SNI_S_DELETE_CHAR : printf( "\\Ksni_s_del_char Transmit SNI-97801-5xx: Shift-Delete Char\n"); break; case K_SNI_S_DELETE_LINE : printf( "\\Ksni_s_del_line Transmit SNI-97801-5xx: Shift-Delete Line\n"); break; case K_SNI_S_DELETE_WORD : printf( "\\Ksni_s_del_word Transmit SNI-97801-5xx: Shift-Delete Word\n"); break; case K_SNI_S_CURSOR_DOWN : printf( "\\Ksni_s_dnarr Transmit SNI-97801-5xx: Shift-Cursor Down\n"); break; case K_SNI_S_ENDMARKE : printf("\\Ksni_s_endmarke Transmit SNI-97801-5xx: Shift-End Marke\n"); break; case K_SNI_S_F01 : printf("\\Ksni_s_f01 Transmit SNI-97801-5xx: Shift-F1\n"); break; case K_SNI_S_F02 : printf("\\Ksni_s_f02 Transmit SNI-97801-5xx: Shift-F2\n"); break; case K_SNI_S_F03 : printf("\\Ksni_s_f03 Transmit SNI-97801-5xx: Shift-F3\n"); break; case K_SNI_S_F04 : printf("\\Ksni_s_f04 Transmit SNI-97801-5xx: Shift-F4\n"); break; case K_SNI_S_F05 : printf("\\Ksni_s_f05 Transmit SNI-97801-5xx: Shift-F5\n"); break; case K_SNI_S_F06 : printf("\\Ksni_s_f06 Transmit SNI-97801-5xx: Shift-F6\n"); break; case K_SNI_S_F07 : printf("\\Ksni_s_f07 Transmit SNI-97801-5xx: Shift-F7\n"); break; case K_SNI_S_F08 : printf("\\Ksni_s_f08 Transmit SNI-97801-5xx: Shift-F8\n"); break; case K_SNI_S_F09 : printf("\\Ksni_s_f09 Transmit SNI-97801-5xx: Shift-F9\n"); break; case K_SNI_S_F10 : printf("\\Ksni_s_f10 Transmit SNI-97801-5xx: Shift-F10\n"); break; case K_SNI_S_F11 : printf("\\Ksni_s_f11 Transmit SNI-97801-5xx: Shift-F11\n"); break; case K_SNI_S_F12 : printf("\\Ksni_s_f12 Transmit SNI-97801-5xx: Shift-F12\n"); break; case K_SNI_S_F13 : printf("\\Ksni_s_f13 Transmit SNI-97801-5xx: Shift-F13\n"); break; case K_SNI_S_F14 : printf("\\Ksni_s_f14 Transmit SNI-97801-5xx: Shift-F14\n"); break; case K_SNI_S_F15 : printf("\\Ksni_s_f15 Transmit SNI-97801-5xx: Shift-F15\n"); break; case K_SNI_S_F16 : printf("\\Ksni_s_f16 Transmit SNI-97801-5xx: Shift-F16\n"); break; case K_SNI_S_F17 : printf("\\Ksni_s_f17 Transmit SNI-97801-5xx: Shift-F17\n"); break; case K_SNI_S_F18 : printf("\\Ksni_s_f18 Transmit SNI-97801-5xx: Shift-F18\n"); break; case K_SNI_S_USER1 : printf( "\\Ksni_s_user1 Transmit SNI-97801-5xx: Shift-Key below F18\n"); break; case K_SNI_S_F19 : printf("\\Ksni_s_f19 Transmit SNI-97801-5xx: Shift-F19\n"); break; case K_SNI_S_USER2 : printf( "\\Ksni_s_user2 Transmit SNI-97801-5xx: Shift-Key below F19\n"); break; case K_SNI_S_F20 : printf("\\Ksni_s_f20 Transmit SNI-97801-5xx: Shift-F20\n"); break; case K_SNI_S_USER3 : printf( "\\Ksni_s_user3 Transmit SNI-97801-5xx: Shift-Key below F20\n"); break; case K_SNI_S_F21 : printf("\\Ksni_s_f21 Transmit SNI-97801-5xx: Shift-F21\n"); break; case K_SNI_S_USER4 : printf( "\\Ksni_s_user4 Transmit SNI-97801-5xx: Shift-Key below F21\n"); break; case K_SNI_S_F22 : printf("\\Ksni_s_f22 Transmit SNI-97801-5xx: Shift-F22\n"); break; case K_SNI_S_USER5 : printf( "\\Ksni_s_user5 Transmit SNI-97801-5xx: Shift-Key below F22\n"); break; case K_SNI_S_HELP : printf("\\Ksni_s_help Transmit SNI-97801-5xx: Shift-Help\n"); break; case K_SNI_S_HOME : printf("\\Ksni_s_home Transmit SNI-97801-5xx: Shift-Home\n"); break; case K_SNI_S_INSERT_CHAR : printf( "\\Ksni_s_ins_char Transmit SNI-97801-5xx: Shift-Insert Char\n"); break; case K_SNI_S_INSERT_LINE : printf( "\\Ksni_s_ins_line Transmit SNI-97801-5xx: Shift-Insert Line\n"); break; case K_SNI_S_INSERT_WORD : printf( "\\Ksni_s_ins_word Transmit SNI-97801-5xx: Shift-Insert Word\n"); break; case K_SNI_S_LEFT_TAB : printf("\\Ksni_s_left_tab Transmit SNI-97801-5xx: Shift-Left Tab\n"); break; case K_SNI_S_CURSOR_LEFT : printf( "\\Ksni_s_lfarr Transmit SNI-97801-5xx: Shift-Cursor Left\n"); break; case K_SNI_S_MODE : printf("\\Ksni_s_mode Transmit SNI-97801-5xx: Shift-Mode\n"); break; case K_SNI_S_PAGE : printf("\\Ksni_s_page Transmit SNI-97801-5xx: Shift-Page\n"); break; case K_SNI_S_PRINT : printf("\\Ksni_s_print Transmit SNI-97801-5xx: Shift-Print\n"); break; case K_SNI_S_CURSOR_RIGHT: printf( "\\Ksni_s_rtarr Transmit SNI-97801-5xx: Shift-Cursor Right\n"); break; case K_SNI_S_SCROLL_DOWN : printf( "\\Ksni_s_scroll_dn Transmit SNI-97801-5xx: Shift-Scroll Down\n"); break; case K_SNI_S_SCROLL_UP : printf("\\Ksni_s_scroll_up Transmit SNI-97801-5xx: Shift-Scroll Up\n"); break; case K_SNI_S_START : printf("\\Ksni_s_start Transmit SNI-97801-5xx: Shift-Start\n"); break; case K_SNI_S_CURSOR_UP : printf("\\Ksni_s_uparr Transmit SNI-97801-5xx: Shift-Cursor Up\n"); break; case K_SNI_S_TAB : printf("\\Ksni_s_tab Transmit SNI-97801-5xx: Shift-Tab\n"); break; case K_SNI_SCROLL_DOWN : printf("\\Ksni_scroll_dn Transmit SNI-97801-5xx: Scroll Down\n"); break; case K_SNI_SCROLL_UP : printf("\\Ksni_scroll_up Transmit SNI-97801-5xx: Scroll Up\n"); break; case K_SNI_START : printf("\\Ksni_start Transmit SNI-97801-5xx: Start\n"); break; case K_SNI_TAB : printf("\\Ksni_tab Transmit SNI-97801-5xx: Tab\n"); break; case K_SNI_CURSOR_UP : printf("\\Ksni_uparr Transmit SNI-97801-5xx: Cursor Up\n"); break; case K_BA80_ATTR: printf("\\Kba80_attr Transmit BA80: Attr\n"); break; case K_BA80_C_KEY: printf("\\Kba80_c_key Transmit BA80: C\n"); break; case K_BA80_CLEAR: printf("\\Kba80_clear Transmit BA80: Clear\n"); break; case K_BA80_CMD: printf("\\Kba80_cmd Transmit BA80: Cmd\n"); break; case K_BA80_COPY: printf("\\Kba80_copy Transmit BA80: Copy\n"); break; case K_BA80_DEL: printf("\\Kba80_del Transmit BA80: Delete\n"); break; case K_BA80_DEL_B: printf("\\Kba80_del_b Transmit BA80: Delete B\n"); break; case K_BA80_DO: printf("\\Kba80_do Transmit BA80: Do\n"); break; case K_BA80_END: printf("\\Kba80_end Transmit BA80: End\n"); break; case K_BA80_ENV: printf("\\Kba80_env Transmit BA80: Env\n"); break; case K_BA80_EOP: printf("\\Kba80_eop Transmit BA80: EOP\n"); break; case K_BA80_ERASE: printf("\\Kba80_erase Transmit BA80: Erase\n"); break; case K_BA80_FMT: printf("\\Kba80_fmt Transmit BA80: Format\n"); break; case K_BA80_HELP: printf("\\Kba80_help Transmit BA80: Help\n"); break; case K_BA80_HOME: printf("\\Kba80_home Transmit BA80: Home\n"); break; case K_BA80_INS: printf("\\Kba80_ins Transmit BA80: Insert\n"); break; case K_BA80_INS_B: printf("\\Kba80_ins_b Transmit BA80: Insert B\n"); break; case K_BA80_MARK: printf("\\Kba80_mark Transmit BA80: Mark\n"); break; case K_BA80_MOVE: printf("\\Kba80_move Transmit BA80: Move\n"); break; case K_BA80_PA01: printf("\\Kba80_pa01 Transmit BA80: PA1\n"); break; case K_BA80_PA02: printf("\\Kba80_pa02 Transmit BA80: PA2\n"); break; case K_BA80_PA03: printf("\\Kba80_pa03 Transmit BA80: PA3\n"); break; case K_BA80_PA04: printf("\\Kba80_pa04 Transmit BA80: PA4\n"); break; case K_BA80_PA05: printf("\\Kba80_pa05 Transmit BA80: PA5\n"); break; case K_BA80_PA06: printf("\\Kba80_pa06 Transmit BA80: PA6\n"); break; case K_BA80_PA07: printf("\\Kba80_pa07 Transmit BA80: PA7\n"); break; case K_BA80_PA08: printf("\\Kba80_pa08 Transmit BA80: PA8\n"); break; case K_BA80_PA09: printf("\\Kba80_pa09 Transmit BA80: PA9\n"); break; case K_BA80_PA10: printf("\\Kba80_pa10 Transmit BA80: PA10\n"); break; case K_BA80_PA11: printf("\\Kba80_pa11 Transmit BA80: PA11\n"); break; case K_BA80_PA12: printf("\\Kba80_pa12 Transmit BA80: PA12\n"); break; case K_BA80_PA13: printf("\\Kba80_pa13 Transmit BA80: PA13\n"); break; case K_BA80_PA14: printf("\\Kba80_pa14 Transmit BA80: PA14\n"); break; case K_BA80_PA15: printf("\\Kba80_pa15 Transmit BA80: PA15\n"); break; case K_BA80_PA16: printf("\\Kba80_pa16 Transmit BA80: PA16\n"); break; case K_BA80_PA17: printf("\\Kba80_pa17 Transmit BA80: PA17\n"); break; case K_BA80_PA18: printf("\\Kba80_pa18 Transmit BA80: PA18\n"); break; case K_BA80_PA19: printf("\\Kba80_pa19 Transmit BA80: PA19\n"); break; case K_BA80_PA20: printf("\\Kba80_pa20 Transmit BA80: PA20\n"); break; case K_BA80_PA21: printf("\\Kba80_pa21 Transmit BA80: PA21\n"); break; case K_BA80_PA22: printf("\\Kba80_pa22 Transmit BA80: PA22\n"); break; case K_BA80_PA23: printf("\\Kba80_pa23 Transmit BA80: PA23\n"); break; case K_BA80_PA24: printf("\\Kba80_pa24 Transmit BA80: PA24\n"); break; case K_BA80_PGDN: printf("\\Kba80_pgdn Transmit BA80: Page Down\n"); break; case K_BA80_PGUP: printf("\\Kba80_pgup Transmit BA80: Page Up\n"); break; case K_BA80_PICK: printf("\\Kba80_pick Transmit BA80: Pick\n"); break; case K_BA80_PRINT: printf("\\Kba80_print Transmit BA80: Print\n"); break; case K_BA80_PUT: printf("\\Kba80_put Transmit BA80: Put\n"); break; case K_BA80_REFRESH: printf("\\Kba80_refresh Transmit BA80: Refresh \n"); break; case K_BA80_RESET: printf("\\Kba80_reset Transmit BA80: Reset\n"); break; case K_BA80_RUBOUT: printf("\\Kba80_rubout Transmit BA80: Rubout\n"); break; case K_BA80_SAVE: printf("\\Kba80_save Transmit BA80: Save\n"); break; case K_BA80_SOFTKEY1: printf("\\Kba80_softkey1 Transmit BA80: Softkey 1\n"); break; case K_BA80_SOFTKEY2: printf("\\Kba80_softkey2 Transmit BA80: Softkey 2\n"); break; case K_BA80_SOFTKEY3: printf("\\Kba80_softkey3 Transmit BA80: Softkey 3\n"); break; case K_BA80_SOFTKEY4: printf("\\Kba80_softkey4 Transmit BA80: Softkey 4\n"); break; case K_BA80_SOFTKEY5: printf("\\Kba80_softkey5 Transmit BA80: Softkey 5\n"); break; case K_BA80_SOFTKEY6: printf("\\Kba80_softkey6 Transmit BA80: Softkey 6\n"); break; case K_BA80_SOFTKEY7: printf("\\Kba80_softkey7 Transmit BA80: Softkey 7\n"); break; case K_BA80_SOFTKEY8: printf("\\Kba80_softkey8 Transmit BA80: Softkey 8\n"); break; case K_BA80_SOFTKEY9: printf("\\Kba80_softkey9 Transmit BA80: Softkey 9\n"); break; case K_BA80_UNDO: printf("\\Kba80_undo Transmit BA80: Undo\n"); break; case K_I31_F01: printf("\\Ki31_f01 Transmit IBM 31xx: F1\n"); break; case K_I31_F02: printf("\\Ki31_f02 Transmit IBM 31xx: F2\n"); break; case K_I31_F03: printf("\\Ki31_f03 Transmit IBM 31xx: F3\n"); break; case K_I31_F04: printf("\\Ki31_f04 Transmit IBM 31xx: F4\n"); break; case K_I31_F05: printf("\\Ki31_f05 Transmit IBM 31xx: F5\n"); break; case K_I31_F06: printf("\\Ki31_f06 Transmit IBM 31xx: F6\n"); break; case K_I31_F07: printf("\\Ki31_f07 Transmit IBM 31xx: F7\n"); break; case K_I31_F08: printf("\\Ki31_f08 Transmit IBM 31xx: F8\n"); break; case K_I31_F09: printf("\\Ki31_f09 Transmit IBM 31xx: F9\n"); break; case K_I31_F10: printf("\\Ki31_f10 Transmit IBM 31xx: F10\n"); break; case K_I31_F11: printf("\\Ki31_f11 Transmit IBM 31xx: F11\n"); break; case K_I31_F12: printf("\\Ki31_f12 Transmit IBM 31xx: F12\n"); break; case K_I31_F13: printf("\\Ki31_f13 Transmit IBM 31xx: F13\n"); break; case K_I31_F14: printf("\\Ki31_f14 Transmit IBM 31xx: F14\n"); break; case K_I31_F15: printf("\\Ki31_f15 Transmit IBM 31xx: F15\n"); break; case K_I31_F16: printf("\\Ki31_f16 Transmit IBM 31xx: F16\n"); break; case K_I31_F17: printf("\\Ki31_f17 Transmit IBM 31xx: F17\n"); break; case K_I31_F18: printf("\\Ki31_f18 Transmit IBM 31xx: F18\n"); break; case K_I31_F19: printf("\\Ki31_f19 Transmit IBM 31xx: F19\n"); break; case K_I31_F20: printf("\\Ki31_f20 Transmit IBM 31xx: F20\n"); break; case K_I31_F21: printf("\\Ki31_f21 Transmit IBM 31xx: F21\n"); break; case K_I31_F22: printf("\\Ki31_f22 Transmit IBM 31xx: F22\n"); break; case K_I31_F23: printf("\\Ki31_f23 Transmit IBM 31xx: F23\n"); break; case K_I31_F24: printf("\\Ki31_f24 Transmit IBM 31xx: F24\n"); break; case K_I31_F25: printf("\\Ki31_f25 Transmit IBM 31xx: F25\n"); break; case K_I31_F26: printf("\\Ki31_f26 Transmit IBM 31xx: F26\n"); break; case K_I31_F27: printf("\\Ki31_f27 Transmit IBM 31xx: F27\n"); break; case K_I31_F28: printf("\\Ki31_f28 Transmit IBM 31xx: F28\n"); break; case K_I31_F29: printf("\\Ki31_f29 Transmit IBM 31xx: F29\n"); break; case K_I31_F30: printf("\\Ki31_f30 Transmit IBM 31xx: F30\n"); break; case K_I31_F31: printf("\\Ki31_f31 Transmit IBM 31xx: F31\n"); break; case K_I31_F32: printf("\\Ki31_f32 Transmit IBM 31xx: F32\n"); break; case K_I31_F33: printf("\\Ki31_f33 Transmit IBM 31xx: F33\n"); break; case K_I31_F34: printf("\\Ki31_f34 Transmit IBM 31xx: F34\n"); break; case K_I31_F35: printf("\\Ki31_f35 Transmit IBM 31xx: F35\n"); break; case K_I31_F36: printf("\\Ki31_f36 Transmit IBM 31xx: F36\n"); break; case K_I31_PA1: printf("\\Ki31_pa1 Transmit IBM 31xx: PA1\n"); break; case K_I31_PA2: printf("\\Ki31_pa2 Transmit IBM 31xx: PA2\n"); break; case K_I31_PA3: printf("\\Ki31_pa3 Transmit IBM 31xx: PA3\n"); break; case K_I31_RESET: printf("\\Ki31_reset Transmit IBM 31xx: Reset\n"); break; case K_I31_JUMP: printf("\\Ki31_jump Transmit IBM 31xx: Jump\n"); break; case K_I31_CLEAR: printf("\\Ki31_clear Transmit IBM 31xx: Clear\n"); break; case K_I31_ERASE_EOF: printf("\\Ki31_erase_eof Transmit IBM 31xx: Erase to End of Field\n"); break; case K_I31_ERASE_EOP: printf("\\Ki31_eop Transmit IBM 31xx: Erase to End of Page\n"); break; case K_I31_ERASE_INP: printf("\\Ki31_inp Transmit IBM 31xx: Erase Input Operation\n"); break; case K_I31_INSERT_CHAR: printf("\\Ki31_ins_char Transmit IBM 31xx: Insert Character\n"); break; case K_I31_INSERT_SPACE: printf("\\Ki31_ins_space Transmit IBM 31xx: Insert Space\n"); break; case K_I31_DELETE: printf("\\Ki31_delete Transmit IBM 31xx: Delete Character\n"); break; case K_I31_INS_LN: printf("\\Ki31_ins_line Transmit IBM 31xx: Insert Line\n"); break; case K_I31_DEL_LN: printf("\\Ki31_del_ln Transmit IBM 31xx: Delete Line\n"); break; case K_I31_PRINT_LINE: printf("\\Ki31_prt_line Transmit IBM 31xx: Print Line\n"); break; case K_I31_PRINT_MSG: printf("\\Ki31_prt_msg Transmit IBM 31xx: Print Message\n"); break; case K_I31_PRINT_SHIFT: printf("\\Ki31_prt_shift Transmit IBM 31xx: Print Shift\n"); break; case K_I31_CANCEL: printf("\\Ki31_cancel Transmit IBM 31xx: Cancel\n"); break; case K_I31_SEND_LINE: printf("\\Ki31_send_line Transmit IBM 31xx: Send Line\n"); break; case K_I31_SEND_MSG: printf("\\Ki31_send_msg Transmit IBM 31xx: Send Message\n"); break; case K_I31_SEND_PAGE: printf("\\Ki31_send_page Transmit IBM 31xx: Send Page\n"); break; case K_I31_HOME: printf("\\Ki31_home Transmit IBM 31xx: Home\n"); break; case K_I31_BACK_TAB: printf("\\Ki31_back_tab Transmit IBM 31xx: Back Tab\n"); break; case K_SUN_STOP: printf("\\Ksunstop Transmit SUN Console: Stop\n"); break; case K_SUN_AGAIN: printf("\\Ksunagain Transmit SUN Console: Again\n"); break; case K_SUN_PROPS: printf("\\Ksunprops Transmit SUN Console: Props\n"); break; case K_SUN_UNDO: printf("\\Ksunundo Transmit SUN Console: Undo\n"); break; case K_SUN_FRONT: printf("\\Ksunfront Transmit SUN Console: Front\n"); break; case K_SUN_COPY: printf("\\Ksuncopy Transmit SUN Console: Copy\n"); break; case K_SUN_OPEN: printf("\\Ksunopen Transmit SUN Console: Open\n"); break; case K_SUN_PASTE: printf("\\Ksunpaste Transmit SUN Console: Paste\n"); break; case K_SUN_FIND: printf("\\Ksunfind Transmit SUN Console: Find\n"); break; case K_SUN_CUT: printf("\\Ksuncut Transmit SUN Console: Cut\n"); break; case K_SUN_HELP: printf("\\Ksunhelp Transmit SUN Console: Help\n"); break; default: printf("No additional help available for this kverb\n"); } printf("\n"); /* This is not the proper way to do it since it doesn't show */ /* all emulations, nor does it show the special modes, but it */ /* is better than nothing. */ printf("Current bindings:\n"); found = 0; for (i = 256; i < KMSIZE ; i++) { con_event evt = mapkey(i); if (evt.type != kverb) continue; if ((evt.kverb.id & ~F_KVERB) == xx) { found = 1; printf(" \\%-4d - %s\n",i,keyname(i)); } } #ifdef OS2MOUSE for ( button = 0 ; button < MMBUTTONMAX ; button++ ) for ( event = 0 ; event < MMEVENTSIZE ; event++ ) if ( mousemap[button][event].type == kverb ) { if ( (mousemap[button][event].kverb.id & ~F_KVERB) == xx ) { found = 1; printf(" Mouse - %s\n",mousename(button,event)); } } #endif /* OS2MOUSE */ if ( !found ) { printf(" (none)\n"); } return(0); } #endif /* NOKVERBS */ #endif /* OS2 */ #ifndef NOXFER /* D O H R M T -- Give help about REMOTE command */ static char *hrset[] = { "Syntax: REMOTE SET parameter value", "Example: REMOTE SET FILE TYPE BINARY", " Asks the Kermit server to set the named parameter to the given value.", " Equivalent to typing the corresponding SET command directly to the other", " Kermit if it were in interactive mode.", "" }; int dohrmt(xx) int xx; { int x; if (xx == -3) return(hmsga(hmhrmt)); if (xx < 0) return(xx); if ((x = cmcfm()) < 0) return(x); switch (xx) { case XZCPY: return(hmsg("Syntax: REMOTE COPY source destination\n\ Asks the Kermit server to copy the source file to destination.\n\ Synonym: RCOPY.")); case XZCWD: #ifdef NEWFTP return(hmsg("Syntax: REMOTE CD [ name ]\n\ Asks the Kermit or FTP server to change its working directory or device.\n\ If the device or directory name is omitted, restore the default.\n\ Synonym: RCD.")); #else return(hmsg("Syntax: REMOTE CD [ name ]\n\ Asks the Kermit server to change its working directory or device.\n\ If the device or directory name is omitted, restore the default.\n\ Synonym: RCD.")); #endif /* NEWFTP */ case XZDEL: #ifdef NEWFTP return(hmsg("Syntax: REMOTE DELETE filespec\n\ Asks the Kermit or FTP server to delete the named file(s).\n\ Synonym: RDEL.")); #else return(hmsg("Syntax: REMOTE DELETE filespec\n\ Asks the Kermit server to delete the named file(s).\n\ Synonym: RDEL.")); #endif /* NEWFTP */ case XZMKD: #ifdef NEWFTP return(hmsg("Syntax: REMOTE MKDIR directory-name\n\ Asks the Kermit or FTP server to create the named directory.\n\ Synonym: RMKDIR.")); #else return(hmsg("Syntax: REMOTE MKDIR directory-name\n\ Asks the Kermit server to create the named directory.\n\ Synonym: RMKDIR.")); #endif /* NEWFTP */ case XZRMD: #ifdef NEWFTP return(hmsg("Syntax: REMOTE RMDIR directory-name\n\ Asks the Kermit or FTP server to remove the named directory.\n\ Synonym: RRMDIR.")); #else return(hmsg("Syntax: REMOTE RMDIR directory-name\n\ Asks the Kermit server to remove the named directory.\n\ Synonym: RRMDIR.")); #endif /* NEWFTP */ case XZDIR: #ifdef NEWFTP return(hmsg("Syntax: REMOTE DIRECTORY [ filespec ]\n\ Asks the Kermit or FTP server to provide a directory listing of the named\n\ file(s) or if no file specification is given, of all files in its current\n\ directory. Synonym: RDIR.")); #else return(hmsg("Syntax: REMOTE DIRECTORY [ filespec ]\n\ Asks the Kermit server to provide a directory listing of the named\n\ file(s) or if no file specification is given, of all files in its current\n\ directory. Synonym: RDIR.")); #endif /* NEWFTP */ case XZHLP: #ifdef NEWFTP return(hmsg("Syntax: REMOTE HELP\n\ Asks the Kermit or FTP server to list the services it provides.\n\ Synonym: RHELP.")); #else return(hmsg("Syntax: REMOTE HELP\n\ Asks the Kermit server to list the services it provides.\n\ Synonym: RHELP.")); #endif /* NEWFTP */ case XZHOS: return(hmsg("Syntax: REMOTE HOST command\n\ Sends a command to the other computer in its own command language\n\ through the Kermit server that is running on that host. Synonym: RHOST.")); #ifndef NOFRILLS case XZKER: return(hmsg("Syntax: REMOTE KERMIT command\n\ Sends a command to the remote Kermit server in its own command language.\n\ Synonym: RKERMIT.")); case XZLGI: return(hmsg("Syntax: REMOTE LOGIN user password [ account ]\n\ Logs in to a remote Kermit server that requires you login. Note: RLOGIN\n\ is NOT a synonym for REMOTE LOGIN.")); case XZLGO: return(hmsg("Syntax: REMOTE LOGOUT\n\ Logs out from a remote Kermit server to which you have previously logged in." )); case XZMSG: return(hmsg("Syntax: REMOTE MESSAGE text\n\ Sends a short text message to the remote Kermit server.")); case XZPRI: return(hmsg("Syntax: REMOTE PRINT filespec [ options ]\n\ Sends the specified file(s) to the remote Kermit and ask it to have the\n\ file printed on the remote system's printer, using any specified options.\n\ Synonym: RPRINT.")); #endif /* NOFRILLS */ case XZREN: #ifdef NEWFTP return(hmsg("Syntax: REMOTE RENAME filespec newname\n\ Asks the Kermit or FTP server to rename the file. Synonym: RRENAME.")); #else return(hmsg("Syntax: REMOTE RENAME filespec newname\n\ Asks the Kermit server to rename the file. Synonym: RRENAME.")); #endif /* NEWFTP */ case XZSET: return(hmsga(hrset)); case XZSPA: return(hmsg("Syntax: REMOTE SPACE [ name ]\n\ Asks the Kermit server to tell you about its disk space on the current\n\ disk or directory, or in the one that you name. Synonym: RSPACE.")); #ifndef NOFRILLS case XZTYP: #ifdef NEWFTP return(hmsg("Syntax: REMOTE TYPE file\n\ Asks the Kermit or FTP server to send the named file to your screen.\n\ Synonym: RTYPE.")); #else return(hmsg("Syntax: REMOTE TYPE file\n\ Asks the Kermit server to send the named file(s) to your screen.\n\ Synonym: RTYPE.")); #endif /* NEWFTP */ case XZWHO: return(hmsg("Syntax: REMOTE WHO [ name ]\n\ Asks the Kermit server to list who's logged in, or to give information\n\ about the named user. Synonym: RWHO.")); #endif /* NOFRILLS */ #ifndef NOSPL case XZQUE: return(hmsg( "Syntax: [ REMOTE ] QUERY { KERMIT, SYSTEM, USER } variable-name\n\ Asks the Kermit server to send the value of the named variable of the\n\ given type, and make it available in the \\v(query) variable. When the\n\ type is KERMIT functions may also be specified as if they were variables.")); case XZASG: return(hmsg( "Syntax: REMOTE ASSIGN variable-name [ value ]\n\ Assigns the given value to the named global variable on the server.\n\ Synonyms: RASG, RASSIGN.")); #endif /* NOSPL */ case XZPWD: return(hmsg( #ifdef NEWFTP "Syntax: REMOTE PWD\n\ Asks the Kermit server to display its current working directory.\n\ Synonym: RPWD.")); #else "Syntax: REMOTE PWD\n\ Asks the Kermit or FTP server to display its current working directory.\n\ Synonym: RPWD.")); #endif /* NEWFTP */ case XZXIT: #ifdef NEWFTP return(hmsg("Syntax: REMOTE EXIT\n\ Asks the Kermit server to exit (without disconnecting), or closes an FTP\n\ connection. Synonym: REXIT, and (for FTP only) BYE, FTP BYE.")); #else return(hmsg("Syntax: REMOTE EXIT\n\ Asks the Kermit server to exit. Synonym: REXIT.")); #endif /* NEWFTP */ default: if ((x = cmcfm()) < 0) return(x); printf("?Sorry, no help available - \"%s\"\n",cmdbuf); return(-9); } } #endif /* NOXFER */ #endif /* NOHELP */ #endif /* NOICP */ ckuus3.c0000644000015300001460000154752611601674127011273 0ustar fdckermit#ifdef SSHTEST #define SSHBUILTIN #endif /* SSHTEST */ #include "ckcsym.h" /* Symbol definitions */ /* C K U U S 3 -- "User Interface" for C-Kermit, part 3 */ /* Authors: Frank da Cruz , The Kermit Project, Columbia University, New York City Jeffrey E Altman Secure Endpoints Inc., New York City Copyright (C) 1985, 2011, Trustees of Columbia University in the City of New York. All rights reserved. See the C-Kermit COPYING.TXT file or the copyright text in the ckcmai.c module for disclaimer and permissions. */ /* SET command (but much material has been split off into ckuus7.c). */ /* Kermit-specific includes. Definitions here supersede those from system include files. */ #include "ckcdeb.h" /* Debugging & compiler things */ #include "ckcasc.h" /* ASCII character symbols */ #include "ckcker.h" /* Kermit application definitions */ #include "ckcxla.h" /* Character set translation */ #include "ckcnet.h" /* Network symbols */ char pwbuf[PWBUFL+1] = { NUL, NUL }; int pwflg = 0; int pwcrypt = 0; #ifndef NOICP #ifdef CK_AUTHENTICATION #include "ckuath.h" #endif /* CK_AUTHENTICATION */ #ifdef CK_SSL #include "ck_ssl.h" #endif /* CK_SSL */ #include "ckuusr.h" /* User interface symbols */ #ifdef OS2 #include "ckcuni.h" #ifdef SSHBUILTIN #include "ckossh.h" #endif /* SSHBUILTIN */ #ifdef CK_NETBIOS #include #ifdef COMMENT /* Would you believe */ #undef COMMENT /* defines this ? */ #endif /* COMMENT */ #include "ckonbi.h" extern UCHAR NetBiosAdapter; #endif /* CK_NETBIOS */ #include "ckocon.h" #include "ckokey.h" #ifndef NOTERM extern unsigned char colorcmd; /* Command-screen colors */ extern struct keytab ttyclrtab[]; extern int nclrs; extern int tt_cols[], tt_rows[], tt_szchng[], tt_status[]; #endif /* NOTERM */ _PROTOTYP(int setprty, (void)); extern char startupdir[], exedir[]; extern int tt_modechg; #ifdef NT #include #include #include "ckntap.h" /* Microsoft TAPI */ #endif /* NT */ #endif /* OS2 */ #ifndef OS2 extern char * exedir; #endif /* OS2 */ #ifdef CK_RECALL extern int cm_retry; #endif /* CK_RECALL */ #ifdef NEWFTP extern int ftpisopen(); #endif /* NEWFTP */ extern int cmdint; extern int srvidl; #ifdef CKFLOAT extern CKFLOAT floatval; /* (see isfloat()) */ #endif /* CKFLOAT */ #ifndef NOPUSH #ifndef NOFRILLS #ifdef VMS char editor[CKMAXPATH + 1] = "edit"; #else char editor[CKMAXPATH + 1] = { NUL, NUL }; #endif /* VMS */ char editopts[128] = { NUL, NUL }; char editfile[CKMAXPATH + 1] = { NUL, NUL }; #ifdef BROWSER char browser[CKMAXPATH + 1] = { NUL, NUL }; char browsopts[128] = { NUL, NUL }; char browsurl[4096] = { NUL, NUL }; #endif /* BROWSER */ #endif /* NOFRILLS */ #endif /* NOPUSH */ #ifndef NOFRILLS #ifndef NORENAME _PROTOTYP(int setrename, (void)); #endif /* NORENAME */ #endif /* NOFRILLS */ /* Variables */ int cmd_quoting = 1; int cmd_err = 1; extern int hints, xcmdsrc; #ifdef CK_KERBEROS char * k4pwprompt = NULL; /* Kerberos 4 password prompt */ char * k4prprompt = NULL; /* Kerberos 4 principal prompt */ char * k5pwprompt = NULL; /* Kerberos 5 password prompt */ char * k5prprompt = NULL; /* Kerberos 5 principal prompt */ #endif /* CK_KERBEROS */ #ifdef CK_SRP char * srppwprompt = NULL; #endif /* CK_SRP */ extern char * ckprompt, * ikprompt; /* Default prompt */ extern xx_strp xxstring; extern char * cdmsgfile[], * cdmsgstr; extern int local, server, success, dest, sleepcan, inserver, flow, autoflow, binary, parity, escape, what, turn, duplex, backgrd, hwparity, stopbits, turnch, mdmtyp, network, quiet, nettype, carrier, debses, debtim, cdtimo, nlangs, bgset, pflag, msgflg, cmdmsk, xsuspend, techo, pacing, xitwarn, xitsta, outesc, cmd_cols, cmd_rows, ckxech, xaskmore, haveline, didsetlin, isguest, mdmsav, clearrq, saveask, debmsg; extern int reliable, setreliable, matchdot, matchfifo, dir_dots; #ifndef NOSERVER extern int en_pri; #endif /* NOSERVER */ #ifdef IKSDCONF extern int iksdcf; #endif /* IKSDCONF */ #ifdef TCPSOCKET extern int tn_exit; #endif /* TCPSOCKET */ #ifdef TNCODE char * tn_pr_uid = NULL; #endif /* TNCODE */ extern int exitonclose; #ifndef NOKVERBS extern int nkverbs; extern struct keytab kverbs[]; #endif /* NOKVERBS */ extern int ttnproto; /* Network protocol */ extern char *ccntab[]; /* Names of control chars */ #ifdef CK_APC extern int apcactive, apcstatus; #endif /* CK_APC */ #ifndef NOSCRIPT extern int secho; /* Whether SCRIPT cmd should echo */ #endif /* NOSCRIPT */ #ifdef DCMDBUF extern char *atmbuf, *atxbuf; #else extern char atmbuf[], atxbuf[]; #endif /* DCMDBUF */ extern int cmflgs; extern char psave[]; extern char uidbuf[]; extern int sl_uid_saved; int DeleteStartupFile = 0; extern int cmdlvl; /* Overall command level */ #ifndef NOSPL _PROTOTYP( static int parsdir, (int) ); char prmbuf[PWBUFL+1] = { NUL, NUL }; int fndiags = 1; /* Function diagnostics on/off */ int fnerror = 1; /* Function error treatment */ #ifdef DCMDBUF extern int *count, *takerr, *merror, *inpcas; #else extern int count[], takerr[], merror[], inpcas[]; #endif /* DCMDBUF */ extern int mecho; /* Macro echo */ extern long ck_alarm; extern char alrm_date[], alrm_time[]; #else extern int takerr[]; #endif /* NOSPL */ extern int x_ifnum; extern int bigsbsiz, bigrbsiz; /* Packet buffers */ extern long speed; /* Terminal speed */ extern char ttname[]; /* Communication device name */ extern char myhost[] ; extern char inidir[]; /* Ini File directory */ #ifndef NOSETKEY extern KEY *keymap; /* Character map for SET KEY (1:1) */ extern MACRO *macrotab; /* Macro map for SET KEY (1:string) */ #endif /* NOSETKEY */ #ifdef OS2 int wideresult; /* For wide OS/2 scan codes/cmnum() */ #endif /* OS2 */ #ifndef NOLOCAL #ifdef OS2 extern int tt_scrsize[]; /* Scrollback buffer Sizes */ #endif /* OS2 */ #endif /* NOLOCAL */ /* Printer settings */ extern char * printername; /* NULL if printer not redirected */ extern int printpipe; extern int noprinter; #ifdef PRINTSWI int printtimo = 0; char * printterm = NULL; char * printsep = NULL; int printertype = 0; #ifdef BPRINT int printbidi = 0; /* SET BPRINTER (bidirectional) */ long pportspeed = 0L; /* Bidirection printer port speed, */ int pportparity = 0; /* parity, */ int pportflow = FLO_KEEP; /* and flow control */ #endif /* BPRINT */ #ifdef OS2 extern int txt2ps; /* Text2PS conversion? */ extern int ps_width, ps_length; /* Text2PS dimensions */ #endif /* OS2 */ #endif /* PRINTSWI */ #ifdef OS2 extern int tcp_avail; /* Nonzero if TCP/IP is available */ #ifdef DECNET extern int dnet_avail; /* Ditto for DECnet */ #endif /* DECNET */ #ifdef SUPERLAT extern int slat_avail; #endif /* SUPERLAT */ #endif /* OS2 */ static struct keytab logintab[] = { { "password", LOGI_PSW, CM_INV }, { "prompt", LOGI_PRM, CM_INV }, { "userid", LOGI_UID, 0 } }; #ifndef NOCSETS /* system-independent character sets, defined in ckcxla.[ch] */ extern struct csinfo tcsinfo[]; extern struct langinfo langs[]; /* Other character-set related variables */ extern int tcharset, tslevel, language; #endif /* NOCSETS */ /* File-transfer variable declarations */ #ifndef NOXFER #ifdef CK_AUTODL extern int cmdadl; #endif /* CK_AUTODL */ #ifndef NOSERVER extern int ngetpath; extern char * getpath[]; #endif /* NOSERVER */ extern struct ck_p ptab[]; extern CHAR sstate; /* Protocol start state */ extern CHAR myctlq; /* Control-character prefix */ extern CHAR myrptq; /* Repeat-count prefix */ extern int protocol, size, spsiz, spmax, urpsiz, srvtim, srvcdmsg, slostart, srvdis, xfermode, ckdelay, keep, maxtry, unkcs, bctr, bctf, ebqflg, swcapr, wslotr, lscapr, lscapu, spsizr, rptena, rptmin, docrc, xfrcan, xfrchr, xfrnum, xfrbel, xfrint, srvping, g_xfermode, xfrxla; #ifdef PIPESEND extern int usepipes; #endif /* PIPESEND */ #ifdef CKXXCHAR /* DOUBLE / IGNORE char table */ extern int dblflag, ignflag, dblchar; extern short dblt[]; #endif /* CKXXCHAR */ #ifdef CK_SPEED extern short ctlp[]; /* Control-prefix table */ extern int prefixing; static struct keytab pfxtab[] = { "all", PX_ALL, 0, "cautious", PX_CAU, 0, "minimal", PX_WIL, 0, "none", PX_NON, 0 }; #endif /* CK_SPEED */ #endif /* NOXFER */ /* Declarations from cmd package */ #ifdef DCMDBUF extern char *cmdbuf; /* Command buffer */ extern char *line; extern char *tmpbuf; #else extern char cmdbuf[]; /* Command buffer */ extern char line[]; /* Character buffer for anything */ extern char tmpbuf[]; #endif /* DCMDBUF */ /* From main ckuser module... */ extern char *tp, *lp; /* Temporary buffer */ extern int tlevel; /* Take Command file level */ #ifndef NOLOCAL extern int sessft; /* Session-log file type */ extern int slogts; /* Session-log timestamps on/off */ extern int slognul; /* Lines null-terminated */ #endif /* NOLOCAL */ char * tempdir = NULL; #ifdef VMS int vms_msgs = 1; /* SET MESSAGES */ extern int batch; #endif /* VMS */ /* Keyword tables for SET commands */ #ifdef CK_SPEED struct keytab ctltab[] = { "prefixed", 1, 0, /* Note, the values are important. */ "unprefixed", 0, 0 }; #endif /* CK_SPEED */ static struct keytab oldnew[] = { "new", 0, 0, "old", 1, 0 }; #define MCH_FIFO 1 #define MCH_DOTF 2 struct keytab matchtab[] = { { "dotfile", MCH_DOTF, 0 }, { "fifo", MCH_FIFO, 0 } }; int nmatchtab = (sizeof(matchtab) / sizeof(struct keytab)); #ifndef NOSPL static struct keytab functab[] = { "diagnostics", FUNC_DI, 0, "error", FUNC_ER, 0 }; static int nfunctab = (sizeof(functab) / sizeof(struct keytab)); struct keytab outptab[] = { /* SET OUTPUT parameters */ "pacing", 0, 0, /* only one so far... */ "special-escapes", 1, 0 }; int noutptab = (sizeof(outptab) / sizeof(struct keytab)); /* How many */ #endif /* NOSPL */ struct keytab chktab[] = { /* Block check types */ "1", 1, 0, /* 1 = 6-bit checksum */ "2", 2, 0, /* 2 = 12-bit checksum */ "3", 3, 0, /* 3 = 16-bit CRC */ "4", 4, 0, /* Same as B */ "5", 5, 0, /* Same as F */ "blank-free-2", 4, CM_INV, /* B = 12-bit checksum, no blanks */ "force-3", 5, CM_INV /* F = Force CRC on ALL packets */ }; static int nchkt = (sizeof(chktab) / sizeof(struct keytab)); struct keytab rpttab[] = { /* SET REPEAT */ "counts", 0, 0, /* On or Off */ #ifdef COMMENT "minimum", 1, 0, /* Threshhold */ #endif /* COMMENT */ "prefix", 2, 0 /* Repeat-prefix character value */ }; #ifndef NOLOCAL /* For SET [ MODEM ] CARRIER, and also for SET DIAL CONNECT */ struct keytab crrtab[] = { "automatic", CAR_AUT, 0, /* 2 */ "off", CAR_OFF, 0, /* 0 */ "on", CAR_ON, 0 /* 1 */ }; int ncrr = 3; #endif /* NOLOCAL */ struct keytab ooatab[] = { /* On/Off/Auto table */ "automatic", SET_AUTO, 0, /* 2 */ "off", SET_OFF, 0, /* 0 */ "on", SET_ON, 0 /* 1 */ }; struct keytab ooetab[] = { /* On/Off/Stderr table 2010/03/12 */ "off", SET_OFF, 0, /* for SET DEBUG MESSAGES */ "on", SET_ON, 0, "s", 2, CM_ABR|CM_INV, "st", 2, CM_ABR|CM_INV, "std", 2, CM_ABR|CM_INV, "stderr", 2, 0, "stdout", SET_ON, CM_INV }; static int nooetab = (sizeof(ooetab) / sizeof(struct keytab)); struct keytab ooktab[] = { /* On/Off/Ask table */ "ask", 2, 0, /* 2 */ "off", SET_OFF, 0, /* 0 */ "on", SET_ON, 0 /* 1 */ }; struct keytab qvtab[] = { /* Quiet/Verbose table */ "quiet", 1, 0, "verbose", 0, 0 }; int nqvt = 2; /* For SET DEBUG */ #define DEB_OFF 0 #define DEB_ON 1 #define DEB_SES 2 #define DEB_TIM 3 #define DEB_LEN 4 #define DEB_MSG 5 struct keytab dbgtab[] = { "linelength", DEB_LEN, CM_INV, "m", DEB_MSG, CM_ABR|CM_INV, "message", DEB_MSG, 0, "msg", DEB_MSG, CM_INV, "off", DEB_OFF, 0, "on", DEB_ON, 0, "session", DEB_SES, 0, "timestamps", DEB_TIM, 0 }; int ndbg = (sizeof(dbgtab) / sizeof(struct keytab)); #ifndef NOLOCAL /* Transmission speeds */ #ifdef TTSPDLIST /* Speed table constructed at runtime . . . */ struct keytab * spdtab = NULL; int nspd = 0; #else /* Note, the values are encoded in cps rather than bps because 19200 and higher are too big for some ints. All but 75bps are multiples of ten. Result of lookup in this table must be multiplied by 10 to get actual speed in bps. If this number is 70, it must be changed to 75. If it is 888, this means 75/1200 split speed. The values are generic, rather than specific to UNIX. We can't use B75, B1200, B9600, etc, because non-UNIX versions of C-Kermit will not necessarily have these symbols defined. The BPS_xxx symbols are Kermit-specific, and are defined in ckcdeb.h or on the CC command line. Like all other keytabs, this one must be in "alphabetical" order, rather than numeric order. */ struct keytab spdtab[] = { "0", 0, CM_INV, "110", 11, 0, #ifdef BPS_115K "115200",11520, 0, #endif /* BPS_115K */ "1200", 120, 0, #ifdef BPS_134 "134.5", 134, 0, #endif /* BPS_134 */ #ifdef BPS_14K "14400", 1440, 0, #endif /* BPS_14K */ #ifdef BPS_150 "150", 15, 0, #endif /* BPS_150 */ #ifdef BPS_1800 "1800", 180, 0, #endif /* BPS_150 */ #ifdef BPS_19K "19200", 1920, 0, #endif /* BPS_19K */ #ifdef BPS_200 "200", 20, 0, #endif /* BPS_200 */ #ifdef BPS_230K "230400", 23040, 0, #endif /* BPS_230K */ "2400", 240, 0, #ifdef BPS_28K "28800", 2880, 0, #endif /* BPS_28K */ "300", 30, 0, #ifdef BPS_3600 "3600", 360, 0, #endif /* BPS_3600 */ #ifdef BPS_38K "38400", 3840, 0, #endif /* BPS_38K */ #ifdef BPS_460K "460800", 46080, 0, /* Need 32 bits for this... */ #endif /* BPS_460K */ "4800", 480, 0, #ifdef BPS_50 "50", 5, 0, #endif /* BPS_50 */ #ifdef BPS_57K "57600", 5760, 0, #endif /* BPS_57K */ "600", 60, 0, #ifdef BPS_7200 "7200", 720, 0, #endif /* BPS_7200 */ #ifdef BPS_75 "75", 7, 0, #endif /* BPS_75 */ #ifdef BPS_7512 "75/1200",888, 0, /* Code "888" for split speed */ #endif /* BPS_7512 */ #ifdef BPS_76K "76800", 7680, 0, #endif /* BPS_76K */ #ifdef BPS_921K "921600", 92160,0, /* Need 32 bits for this... */ #endif /* BPS_921K */ "9600", 960, 0 }; int nspd = (sizeof(spdtab) / sizeof(struct keytab)); /* How many speeds */ #endif /* TTSPDLIST */ #ifdef TN_COMPORT struct keytab tnspdtab[] = { /* RFC 2217 TELNET COMPORT Option */ "115200", 11520, 0, /* (add any other defined speeds) */ "1200", 120, 0, "14400", 1440, 0, "19200", 1920, 0, "230400", 23040, 0, "2400", 240, 0, "28800", 2880, 0, "300", 30, 0, "38400", 3840, 0, "460800", 46080, 0, "4800", 480, 0, "57600", 5760, 0, "600", 60, 0, "9600", 960, 0 }; int ntnspd = (sizeof(tnspdtab) / sizeof(struct keytab)); /* How many speeds */ #endif /* TN_COMPORT */ #endif /* NOLOCAL */ #ifndef NOCSETS extern struct keytab lngtab[]; /* Languages for SET LANGUAGE */ extern int nlng; #endif /* NOCSETS */ #ifndef NOLOCAL /* Duplex keyword table */ struct keytab dpxtab[] = { "full", 0, 0, "half", 1, 0 }; #endif /* NOLOCAL */ /* Flow Control */ struct keytab cxtypesw[] = { #ifdef DECNET "/decnet", CXT_DECNET, 0, #endif /* DECNET */ "/direct-serial", CXT_DIRECT, 0, #ifdef DECNET "/lat", CXT_LAT, 0, #else #ifdef SUPERLAT "/lat", CXT_LAT, 0, #endif /* SUPERLAT */ #endif /* DECNET */ "/modem", CXT_MODEM, 0, #ifdef NPIPE "/named-pipe", CXT_NPIPE, 0, #endif /* NPIPE */ #ifdef NETBIOS "/netbios", CXT_NETBIOS, 0, #endif /* NETBIOS */ "/remote", CXT_REMOTE, 0, #ifdef TCPSOCKET "/tcpip", CXT_TCPIP, 0, #endif /* TCPSOCKET */ #ifdef ANYX25 "/x.25", CXT_X25, 0, #endif /* ANYX25 */ "", 0, 0 }; int ncxtypesw = (sizeof(cxtypesw) / sizeof(struct keytab)); #ifdef TN_COMPORT struct keytab tnflotab[] = { /* SET FLOW-CONTROL keyword table */ "dtr/cd", FLO_DTRC, 0, /* for RFC 2217 Telnet COMPORT */ "dtr/cts", FLO_DTRT, 0, "keep", FLO_KEEP, 0, "none", FLO_NONE, 0, "rts/cts", FLO_RTSC, 0, "xon/xoff", FLO_XONX, 0 }; int ntnflo = (sizeof(tnflotab) / sizeof(struct keytab)); #endif /* TN_COMPORT */ struct keytab flotab[] = { /* SET FLOW-CONTROL keyword table */ "automatic", FLO_AUTO, CM_INV, /* Not needed any more */ #ifdef CK_DTRCD "dtr/cd", FLO_DTRC, 0, #endif /* CK_DTRCD */ #ifdef CK_DTRCTS "dtr/cts", FLO_DTRT, 0, #endif /* CK_DTRCTS */ "keep", FLO_KEEP, 0, "none", FLO_NONE, 0, #ifdef CK_RTSCTS "rts/cts", FLO_RTSC, 0, #endif /* CK_RTSCTS */ #ifndef Plan9 "xon/xoff", FLO_XONX, 0, #endif /* Plan9 */ "", 0, 0 }; int nflo = (sizeof(flotab) / sizeof(struct keytab)) - 1; /* Handshake characters */ struct keytab hshtab[] = { "bell", 007, 0, "code", 998, 0, "cr", 015, 0, "esc", 033, 0, "lf", 012, 0, "none", 999, 0, /* (can't use negative numbers) */ "xoff", 023, 0, "xon", 021, 0 }; int nhsh = (sizeof(hshtab) / sizeof(struct keytab)); #ifndef NOLOCAL static struct keytab sfttab[] = { /* File types for SET SESSION-LOG */ "ascii", XYFT_T, CM_INV, "binary", XYFT_B, 0, "debug", XYFT_D, 0, "null-padded-lines", 998, 0, "text", XYFT_T, 0, "timestamped-text", 999, 0 }; static int nsfttab = (sizeof(sfttab) / sizeof(struct keytab)); #endif /* NOLOCAL */ #ifndef NODIAL #ifdef NETCONN /* Networks directory depends */ int nnetdir = 0; /* on DIAL code -- fix later... */ char *netdir[MAXDDIR+2]; #endif /* NETCONN */ _PROTOTYP( static int setdial, (int) ); _PROTOTYP( static int setdcd, (void) ); _PROTOTYP( static int cklogin, (void) ); #ifndef MINIDIAL #ifdef OLDTBCODE extern int tbmodel; /* Telebit model ID */ #endif /* OLDTBCODE */ #endif /* MINIDIAL */ extern MDMINF *modemp[]; /* Pointers to modem info structs */ extern struct keytab mdmtab[]; /* Modem types (in module ckudia.c) */ extern int nmdm; /* Number of them */ _PROTOTYP(static int dialstr,(char **, char *)); extern int dialhng, dialtmo, dialksp, dialdpy, dialmhu, dialec, dialdc; extern int dialrtr, dialint, dialudt, dialsrt, dialrstr, mdmwaitd; extern int mdmspd, dialfc, dialmth, dialesc, dialfld, dialidt, dialpace; extern int mdmspk, mdmvol, dialtest; int dialcvt = 2; /* DIAL CONVERT-DIRECTORY */ int dialcnf = 0; /* DIAL CONFIRMATION */ int dialcon = 2; /* DIAL CONNECT */ int dialcq = 0; /* DIAL CONNECT AUTO quiet/verbose */ extern long dialmax, dialcapas; int usermdm = 0; extern int ndialdir; extern char *dialini, *dialmstr, *dialmprmt, *dialdir[], *dialcmd, *dialnpr, *dialdcon, *dialdcoff, *dialecon, *dialecoff, *dialhcmd, *dialx3, *dialhwfc, *dialswfc, *dialnofc, *dialtone, *dialpulse, *dialname, *diallac; extern char *diallcc, *dialixp, *dialixs, *dialldp, *diallds, *dialtfp, *dialpxi, *dialpxo, *dialsfx, *dialaaon, *dialaaoff; extern char *diallcp, *diallcs, *dialini2, *dialmac; extern char *dialspoff, *dialspon, *dialvol1, *dialvol2, *dialvol3; char *dialtocc[MAXTPCC] = { NULL, NULL }; int ndialtocc = 0; char *dialpucc[MAXTPCC] = { NULL, NULL }; int ndialpucc = 0; char *dialtfc[MAXTOLLFREE] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; int ntollfree = 0; char *dialpxx[MAXPBXEXCH] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; int ndialpxx = 0; char *diallcac[MAXLOCALAC] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; int nlocalac = 0; static struct keytab drstrtab[] = { "international", 5, 0, "local", 2, 0, "long-distance", 4, 0, "none", 6, 0 }; static struct keytab dcnvtab[] = { "ask", 2, 0, "off", 0, 0, "on", 1, 0 }; struct keytab setmdm[] = { "capabilities", XYDCAP, 0, "carrier-watch", XYDMCD, 0, "command", XYDSTR, 0, "compression", XYDDC, CM_INV, "data-compression", XYDDC, 0, "dial-command", XYDDIA, 0, "error-correction", XYDEC, 0, "escape-character", XYDESC, 0, "flow-control", XYDFC, 0, "hangup-method", XYDMHU, 0, #ifndef NOXFER "kermit-spoof", XYDKSP, 0, #endif /* NOXFER */ "maximum-speed", XYDMAX, 0, "name", XYDNAM, 0, "speaker", XYDSPK, 0, "speed-matching", XYDSPD, 0, "type", XYDTYP, 0, "volume", XYDVOL, 0 }; int nsetmdm = (sizeof(setmdm) / sizeof(struct keytab)); struct keytab voltab[] = { "high", 3, 0, "low", 1, 0, "medium", 2, 0 }; struct keytab mdmcap[] = { "at-commands", CKD_AT, 0, "compression", CKD_DC, 0, "dc", CKD_DC, CM_INV, "ec", CKD_EC, CM_INV, "error-correction", CKD_EC, 0, "hardware-flow", CKD_HW, 0, "hwfc", CKD_HW, CM_INV, "itu", CKD_V25, CM_INV, "kermit-spoof", CKD_KS, 0, "ks", CKD_KS, CM_INV, "sb", CKD_SB, CM_INV, "software-flow", CKD_SW, 0, "speed-buffering", CKD_SB, 0, "swfc", CKD_SW, CM_INV, "tb", CKD_TB, CM_INV, "telebit", CKD_TB, 0, "v25bis-commands", CKD_V25, 0 }; int nmdmcap = (sizeof(mdmcap) / sizeof(struct keytab)); #ifdef COMMENT /* SET ANSWER not implemented yet */ static struct keytab answertab[] = { { "caller-id", XYA_CID, 0 }; { "rings", XYA_RNG, 0 }; { "", 0, 0 } }; static int nanswertab = (sizeof(answertab) / sizeof(struct keytab)) - 1; #endif /* COMMENT */ struct keytab dialtab[] = { /* SET DIAL table */ "area-code", XYDLAC, 0, /* Also still includes items */ "compression", XYDDC, CM_INV, /* that were moved to SET MODEM, */ "confirmation", XYDCNF, 0, /* but they are CM_INVisible... */ "connect", XYDCON, 0, "convert-directory",XYDCVT, 0, "country-code", XYDLCC, 0, "dial-command", XYDDIA, CM_INV, "directory", XYDDIR, 0, "display", XYDDPY, 0, "escape-character", XYDESC, CM_INV, "error-correction", XYDEC, CM_INV, "flow-control", XYDFC, CM_INV, "force-long-distance", XYDFLD, 0, "hangup", XYDHUP, 0, "ignore-dialtone", XYDIDT, 0, "interval", XYDINT, 0, "in", XYDINI, CM_INV|CM_ABR, "init-string", XYDINI, CM_INV, "intl-prefix", XYDIXP, 0, "intl-suffix", XYDIXS, 0, #ifndef NOXFER "kermit-spoof", XYDKSP, CM_INV, #endif /* NOXFER */ "lc-area-codes", XYDLLAC, 0, "lc-prefix", XYDLCP, 0, "lc-suffix", XYDLCS, 0, "ld-prefix", XYDLDP, 0, "ld-suffix", XYDLDS, 0, "local-area-code", XYDLAC, CM_INV, "local-prefix", XYDLCP, CM_INV, "local-suffix", XYDLCS, CM_INV, "m", XYDMTH, CM_INV|CM_ABR, #ifndef NOSPL "macro", XYDMAC, 0, /* 195 */ #endif /* NOSPL */ #ifdef MDMHUP "me", XYDMTH, CM_INV|CM_ABR, #endif /* MDMHUP */ "method", XYDMTH, 0, "mnp-enable", XYDMNP, CM_INV, /* obsolete but still accepted */ #ifdef MDMHUP "modem-hangup", XYDMHU, CM_INV, #endif /* MDMHUP */ "pacing", XYDPAC, 0, "pbx-exchange", XYDPXX, 0, "pbx-inside-prefix",XYDPXI, 0, "pbx-outside-prefix",XYDPXO, 0, "prefix", XYDNPR, 0, "pulse-countries", XYDPUCC, 0, "restrict", XYDRSTR, 0, "retries", XYDRTM, 0, "sort", XYDSRT, 0, "speed-matching", XYDSPD, CM_INV, "string", XYDSTR, CM_INV, "suffix", XYDSFX, 0, "test", XYDTEST, 0, "timeout", XYDTMO, 0, "tf-area-code", XYDTFC, CM_INV, "tf-prefix", XYDTFP, CM_INV, "toll-free-area-code",XYDTFC,0, "toll-free-prefix", XYDTFP, 0, "tone-countries", XYDTOCC, 0 }; int ndial = (sizeof(dialtab) / sizeof(struct keytab)); #ifdef MDMHUP struct keytab mdmhang[] = { "dtr", 0, 0, "modem-command", 1, 0, "rs232-signal", 0, 0, "v24-signal", 0, CM_INV }; #endif /* MDMHUP */ static struct keytab mdmcmd[] = { "autoanswer", XYDS_AN, 0, /* autoanswer */ "compression", XYDS_DC, 0, /* data compression */ "dial-mode-prompt", XYDS_MP, 0, /* dial mode prompt */ "dial-mode-string", XYDS_MS, 0, /* dial mode string */ "error-correction", XYDS_EC, 0, /* error correction */ "hangup-command", XYDS_HU, 0, /* hangup command */ "hardware-flow", XYDS_HW, 0, /* hwfc */ "ignore-dialtone", XYDS_ID, 0, /* ignore dialtone */ "init-string", XYDS_IN, 0, /* init string */ "no-flow-control", XYDS_NF, 0, /* no flow control */ "predial-init", XYDS_I2, 0, /* last-minute setup commands */ "pulse", XYDS_DP, 0, /* pulse */ "software-flow", XYDS_SW, 0, /* swfc */ "speaker", XYDS_SP, 0, /* Speaker */ "tone", XYDS_DT, 0, /* tone */ "volume", XYDS_VO, 0 /* Volume */ }; static int nmdmcmd = (sizeof(mdmcmd) / sizeof(struct keytab)); struct keytab dial_fc[] = { "auto", FLO_AUTO, 0, "none", FLO_NONE, 0, "rts/cts", FLO_RTSC, 0, "xon/xoff", FLO_XONX, 0 }; struct keytab dial_m[] = { /* DIAL METHOD */ "auto", XYDM_A, 0, "default", XYDM_D, 0, "pulse", XYDM_P, 0, "tone", XYDM_T, 0 }; int ndial_m = (sizeof(dial_m)/sizeof(struct keytab)); #endif /* NODIAL */ #ifdef CK_TAPI struct keytab tapitab[] = { /* Top-Level Microsoft TAPI */ "configure-line", XYTAPI_CFG, 0, "dialing-properties", XYTAPI_DIAL, 0 }; int ntapitab = (sizeof(tapitab)/sizeof(struct keytab)); struct keytab settapitab[] = { /* SET Microsoft TAPI */ "inactivity-timeout", XYTAPI_INA, 0, "line", XYTAPI_LIN, 0, "location", XYTAPI_LOC, 0, "manual-dialing", XYTAPI_MAN, 0, "modem-dialing", XYTAPI_PASS, 0, "modem-lights", XYTAPI_LGHT, 0, "phone-number-conversions", XYTAPI_CON, 0, "port", XYTAPI_LIN, CM_INV, "post-dial-terminal", XYTAPI_PST, 0, "pre-dial-terminal", XYTAPI_PRE, 0, "use-windows-configuration", XYTAPI_USE, 0, "wait-for-credit-card-tone", XYTAPI_BNG, 0 }; int nsettapitab = (sizeof(settapitab)/sizeof(struct keytab)); struct keytab * tapiloctab = NULL; /* Microsoft TAPI Locations */ int ntapiloc = 0; extern struct keytab * tapilinetab; /* Microsoft TAPI Line Devices */ extern int ntapiline; extern int tttapi; /* TAPI in use */ extern int tapipass; /* TAPI Passthrough mode */ extern int tapiconv; /* TAPI Conversion mode */ extern int tapilights; extern int tapipreterm; extern int tapipostterm; extern int tapimanual; extern int tapiinactivity; extern int tapibong; extern int tapiusecfg; #endif /* CK_TAPI */ #ifndef NOPUSH extern int nopush; extern int wildena; #ifdef UNIX struct keytab wildtab[] = { /* SET WILDCARD-EXPANSION */ #ifdef UNIX "kermit", WILD_KER, 0, /* By Kermit */ #endif /* UNIX */ "off", WILD_OFF, 0, /* Disabled */ "on", WILD_ON, 0, /* Enabled */ #ifdef UNIX "shell", WILD_SHE, 0, /* By Shell */ #endif /* UNIX */ "", 0, 0 }; int nwild = (sizeof(wildtab) / sizeof(struct keytab)) - 1; struct keytab wdottab[] = { /* cont'd */ "/match-dot-files", 1, 0, "/no-match-dot-files", 0, 0 }; extern int wildxpand; #endif /* UNIX */ #endif /* NOPUSH */ #ifdef NETCONN extern struct keytab netcmd[], netkey[]; extern int nnets, nnetkey; #ifdef TCPSOCKET extern struct keytab tcpopt[]; extern int ntcpopt; #endif /* TCPSOCKET */ #ifdef NPIPE char pipename[PIPENAML+1] = { NUL, NUL }; #endif /* NPIPE */ #ifdef CK_NETBIOS extern unsigned char NetBiosName[]; #endif /* CK_NETBIOS */ #endif /* NETCONN */ #ifdef ANYX25 struct keytab x25tab[] = { "call-user-data", XYUDAT, 0, "closed-user-group", XYCLOS, 0, "reverse-charge", XYREVC, 0 }; int nx25 = (sizeof(x25tab) / sizeof(struct keytab)); #ifndef IBMX25 struct keytab padx3tab[] = { "break-action", PAD_BREAK_ACTION, 0, "break-character", PAD_BREAK_CHARACTER, 0, "character-delete", PAD_CHAR_DELETE_CHAR, 0, "cr-padding", PAD_PADDING_AFTER_CR, 0, "discard-output", PAD_SUPPRESSION_OF_DATA, 0, "echo", PAD_ECHO, 0, "editing", PAD_EDITING, 0, "escape", PAD_ESCAPE, 0, "forward", PAD_DATA_FORWARD_CHAR, 0, "lf-padding", PAD_PADDING_AFTER_LF, 0, "lf-insert", PAD_LF_AFTER_CR, 0, "line-delete", PAD_BUFFER_DELETE_CHAR, 0, "line-display", PAD_BUFFER_DISPLAY_CHAR, 0, "line-fold", PAD_LINE_FOLDING, 0, "pad-flow-control", PAD_FLOW_CONTROL_BY_PAD, 0, "service-signals", PAD_SUPPRESSION_OF_SIGNALS, 0, "timeout", PAD_DATA_FORWARD_TIMEOUT, 0, /* Speed is read-only */ "transmission-rate", PAD_LINE_SPEED, 0, "user-flow-control", PAD_FLOW_CONTROL_BY_USER, 0 }; int npadx3 = (sizeof(padx3tab) / sizeof(struct keytab)); #endif /* IBMX25 */ #endif /* ANYX25 */ #ifdef TLOG static struct keytab vbtab[] = { "brief", 0, 0, #ifdef OS2ORUNIX "ftp", 2, 0, #else #ifdef VMS "ftp", 2, 0, #endif /* def VMS */ #endif /* OS2ORUNIX */ "verbose", 1, 0 }; int nvb = (sizeof(vbtab) / sizeof(struct keytab)); #endif /* TLOG */ #ifdef CKSYSLOG static struct keytab syslogtab[] = { "all", SYSLG_CX, 0, "commands", SYSLG_CM, 0, "connection", SYSLG_AC, 0, "debug", SYSLG_DB, 0, "dial", SYSLG_DI, 0, "file-access", SYSLG_FA, 0, "file-create", SYSLG_FC, 0, "login", SYSLG_LI, 0, "none", SYSLG_NO, 0, "protocol", SYSLG_PR, 0 }; int nsyslog = (sizeof(syslogtab) / sizeof(struct keytab)); #endif /* CKSYSLOG */ /* Parity keyword table */ struct keytab partbl[] = { "even", 'e', 0, #ifdef HWPARITY "hardware",'H', 0, #endif /* HWPARITY */ "mark", 'm', 0, "none", 0 , 0, "odd", 'o', 0, "space", 's', 0 }; int npar = (sizeof(partbl) / sizeof(struct keytab)); #ifdef HWPARITY struct keytab hwpartbl[] = { /* Add mark and space if needed and possible */ "even", 'e', 0, #ifdef OS2 "mark", 'm', 0, #endif /* OS2 */ "odd", 'o', 0, #ifdef OS2 "space", 's', 0, #endif /* OS2 */ "", 0, 0 }; int nhwpar = (sizeof(hwpartbl) / sizeof(struct keytab)) - 1; #endif /* HWPARITY */ /* On/Off table */ struct keytab onoff[] = { "off", 0, 0, "on", 1, 0 }; #define XYCD_M 0 /* CD MESSAGE */ #define XYCD_P 1 /* CD PATH */ #define XYCD_H 2 /* CD HOME */ struct keytab cdtab[] = { "home", XYCD_H, 0, "message", XYCD_M, 0, "path", XYCD_P, 0 }; int ncdtab = (sizeof(cdtab) / sizeof(struct keytab)); struct keytab cdmsg[] = { "file", 2, 0, "off", 0, 0, "on", 1, 0 }; int ncdmsg = (sizeof(cdmsg) / sizeof(struct keytab)); static struct keytab xittab[] = { /* SET EXIT */ "hangup", 3, 0, /* ...HANGUP */ "on-disconnect", 2, 0, /* ...ON-DISCONNECT */ "status", 0, 0, /* ...STATUS */ "warning", 1, 0 /* ...WARNING */ }; int nexit = (sizeof(xittab) / sizeof(struct keytab)); struct keytab xitwtab[] = { /* SET EXIT WARNING */ "always", 2, 0, /* even when not connected */ "off", 0, 0, /* no warning */ "on", 1, 0 /* when connected */ }; int nexitw = (sizeof(xitwtab) / sizeof(struct keytab)); struct keytab rltab[] = { "local", 1, 0, /* ECHO values */ "off", 0, CM_INV, "on", 1, CM_INV, "remote", 0, 0 }; int nrlt = (sizeof(rltab) / sizeof(struct keytab)); /* Incomplete File Disposition table */ struct keytab ifdtab[] = { "discard", SET_OFF, 0, "keep", SET_ON, 0 }; struct keytab ifdatab[] = { "auto", SET_AUTO, 0, "discard", SET_OFF, 0, "keep", SET_ON, 0 }; char * ifdnam[] = { "discard", "keep", "auto" }; /* SET TAKE parameters table */ static struct keytab taktab[] = { "echo", 0, 0, "error", 1, 0, "off", 2, CM_INV, /* For compatibility */ "on", 3, CM_INV /* with MS-DOS Kermit... */ }; #ifndef NOSPL #ifdef COMMENT /* not used */ static struct keytab suftab[] = { /* (what to do with) STARTUP-FILE */ "delete", 1, 0, "keep", 0, 0 }; #endif /* COMMENT */ /* SET MACRO parameters table */ static struct keytab smactab[] = { "echo", 0, 0, "error", 1, 0 }; #endif /* NOSPL */ #ifndef NOSCRIPT static struct keytab scrtab[] = { "echo", 0, 0 }; #endif /* NOSCRIPT */ /* SET COMMAND table */ /* SET COMMAND items... */ #define SCMD_BSZ 0 /* BYTESIZE */ #define SCMD_RCL 1 /* RECALL */ #define SCMD_RTR 2 /* RETRY */ #define SCMD_QUO 3 /* QUOTING */ #define SCMD_COL 4 /* COLOR */ #define SCMD_HIG 5 /* HEIGHT */ #define SCMD_WID 6 /* WIDTH */ #define SCMD_CUR 7 /* CURSOR-POSITION */ #define SCMD_SCR 8 /* SCROLLBACK */ #define SCMD_MOR 9 /* MORE-PROMPTING */ #define SCMD_INT 10 /* INTERRUPTION */ #define SCMD_ADL 11 /* AUTODOWNLOAD */ #define SCMD_STA 12 /* STATUSLINE */ #define SCMD_DBQ 13 /* DOUBLEQUOTING */ #define SCMD_CBR 14 /* CBREAK */ #define SCMD_BFL 15 /* BUFFER-SIZE (not used) */ #define SCMD_ERR 16 /* ERROR */ #define SCMD_VAR 17 /* VARIABLE-EVALUATION */ static struct keytab scmdtab[] = { #ifdef CK_AUTODL "autodownload", SCMD_ADL, 0, #endif /* CK_AUTODL */ #ifdef COMMENT /* To implement this requires that we change CMDBL and ATMBL from compile-time symbols to runtime variables. Not a big deal, but not trivial either. */ "buffer-size", SCMD_BFL, 0, #endif /* COMMENT */ "bytesize", SCMD_BSZ, 0, "cbreak", SCMD_CBR, CM_INV, #ifdef OS2 "color", SCMD_COL, 0, "cursor-position", SCMD_CUR, 0, #endif /* OS2 */ #ifdef DOUBLEQUOTING "doublequoting", SCMD_DBQ, 0, #endif /* DOUBLEQUOTING */ "error-display", SCMD_ERR, 0, "height", SCMD_HIG, 0, "interruption", SCMD_INT, 0, "more-prompting", SCMD_MOR, 0, "quoting", SCMD_QUO, 0, #ifdef CK_RECALL "recall-buffer-size", SCMD_RCL, 0, #endif /* CK_RECALL */ #ifdef CK_RECALL "retry", SCMD_RTR, 0, #endif /* CK_RECALL */ #ifdef OS2 #ifdef ONETERMUPD "scrollback", SCMD_SCR, 0, "statusline", SCMD_STA, 0, #endif /* ONETERMUPD */ #endif /* OS2 */ "variable-evaluation", SCMD_VAR,0, "width", SCMD_WID, 0 }; static int nbytt = (sizeof(scmdtab) / sizeof(struct keytab)); #ifndef NOSERVER /* Server parameters table */ static struct keytab srvtab[] = { "cd-message", XYSERC, 0, "display", XYSERD, 0, "get-path", XYSERP, 0, "idle-timeout", XYSERI, 0, "keepalive", XYSERK, 0, "login", XYSERL, 0, "timeout", XYSERT, 0 }; static int nsrvt = (sizeof(srvtab) / sizeof(struct keytab)); #endif /* NOSERVER */ static struct keytab sleeptab[] = { /* SET SLEEP table */ "cancellation", 0, 0 }; static struct keytab tstab[] = { /* SET TRANSFER/XFER table */ "bell", XYX_BEL, 0, #ifdef XFRCAN "cancellation", XYX_CAN, 0, #endif /* XFRCAN */ #ifndef NOCSETS "character-set", XYX_CSE, 0, #endif /* NOCSETS */ #ifndef NOSPL "crc-calculation", XYX_CRC, 0, #endif /* NOSPL */ "display", XYX_DIS, 0, "interruption", XYX_INT, 0, "locking-shift", XYX_LSH, 0, "message", XYX_MSG, 0, "mode", XYX_MOD, 0, "msg", XYX_MSG, CM_INV, #ifdef PIPESEND "pipes", XYX_PIP, 0, #endif /* PIPESEND */ #ifdef CK_XYZ "protocol", XYX_PRO, 0, #endif /* CK_XYZ */ "report", XYX_RPT, 0, "slow-start", XYX_SLO, 0, #ifndef NOCSETS "translation", XYX_XLA, 0, #else "translation", XYX_XLA, CM_INV, #endif /* NOCSETS */ "xlation", XYX_XLA, CM_INV, "", 0, 0 }; static int nts = (sizeof(tstab) / sizeof(struct keytab)) - 1; static struct keytab rtstab[] = { /* REMOTE SET TRANSFER/XFER table */ #ifndef NOCSETS "character-set", XYX_CSE, 0, #endif /* NOCSETS */ "mode", XYX_MOD, 0 }; static int nrts = (sizeof(rtstab) / sizeof(struct keytab)); struct keytab xfrmtab[] = { /* TRANSFER MODE table */ "automatic", XMODE_A, 0, "manual", XMODE_M, 0 }; #ifdef LOCUS extern int locus, autolocus; static struct keytab locustab[] = { #ifdef KUI { "ask", 3, 0 }, /* Presently implemented in GUI only */ #endif /* KUI */ { "auto", 2, 0 }, { "local", 1, 0 }, { "remote", 0, 0 } }; static int nlocustab = (sizeof(locustab) / sizeof(struct keytab)); #endif /* LOCUS */ #ifndef NOCSETS /* SET TRANSFER CHARACTER-SET table */ extern struct keytab tcstab[]; extern int ntcs; #endif /* NOCSETS */ /* SET TRANSFER LOCKING-SHIFT table */ struct keytab lstab[] = { "forced", 2, 0, "off", 0, 0, "on", 1, 0 }; int nls = (sizeof(lstab) / sizeof(struct keytab)); /* SET TELNET tables */ #ifdef TNCODE extern int tn_nlm, tn_b_nlm, tn_b_meu, tn_b_ume, tn_b_xfer, tn_sb_bug; extern int tn_no_encrypt_xfer, tn_auth_krb5_des_bug; extern int tn_wait_flg, tn_duplex, tn_delay_sb, tn_sfu; extern int sl_tn_saved; extern int tn_infinite; extern int tn_rem_echo; extern int tn_deb; extern int tn_auth_how; extern int tn_auth_enc; #ifdef CK_FORWARD_X extern char * tn_fwdx_xauthority; #endif /* CK_FORWARD_X */ #ifdef CK_AUTHENTICATION static struct keytab setauth[] = { #ifdef CK_KERBEROS "k4", AUTH_KRB4, CM_INV, "k5", AUTH_KRB5, CM_INV, "kerberos4", AUTH_KRB4, 0, "kerberos5", AUTH_KRB5, 0, "kerberos_iv",AUTH_KRB4, CM_INV, "kerberos_v", AUTH_KRB5, CM_INV, "krb4", AUTH_KRB4, CM_INV, "krb5", AUTH_KRB5, CM_INV, #endif /* CK_KERBEROS */ #ifdef CK_SRP "srp", AUTH_SRP, 0, #endif /* CK_SRP */ #ifdef CK_SSL "ssl", AUTH_SSL, 0, "tls", AUTH_TLS, 0, #endif /* CK_SSL */ "", 0, 0 }; static int nsetauth = sizeof(setauth)/sizeof(struct keytab) - 1; #ifdef CK_KERBEROS extern char * krb5_d_principal; /* Default principal */ extern char * krb5_d_instance; extern char * krb5_d_realm; /* Default realm */ extern char * krb5_d_cc; /* Default credentials cache */ extern char * krb5_d_srv; /* Default service name */ extern int krb5_d_lifetime; /* Default lifetime */ extern int krb5_d_forwardable; extern int krb5_d_proxiable; extern int krb5_d_renewable; extern int krb5_autoget; extern int krb5_autodel; extern int krb5_d_getk4; extern int krb5_checkaddrs; /* Check TGT Addrs */ extern int krb5_d_no_addresses; extern char * krb5_d_addrs[]; extern char * k5_keytab; /* Keytab file */ extern struct krb4_init_data krb4_init; extern char * krb4_d_principal; /* Default principal */ extern char * krb4_d_realm; /* Default realm */ extern char * krb4_d_srv; /* Default service name */ extern int krb4_d_lifetime; /* Default lifetime */ extern int krb4_d_preauth; extern char * krb4_d_instance; extern int krb4_autoget; extern int krb4_autodel; extern int krb4_checkaddrs; /* Check TGT Addrs */ extern char * k4_keytab; /* Keytab file */ #ifdef KRB4 extern int k4debug; #endif /* KRB4 */ static struct keytab krbver[] = { "4", 4, 0, "5", 5, 0, "iv", 4, CM_INV, "v", 5, CM_INV }; static int nkrbver = sizeof(krbver)/sizeof(struct keytab); static struct keytab kdestab[] = { "never", KRB_DEL_NO, 0, "no", KRB_DEL_NO, CM_INV, "on-close", KRB_DEL_CL, 0, "on-exit", KRB_DEL_EX, 0 }; static int nkdestab = sizeof(kdestab)/sizeof(struct keytab); static struct keytab k4tab[] = { "autodel", XYKRBDEL, CM_INV, "autodestroy", XYKRBDEL, 0, "autoget", XYKRBGET, 0, "check-address", XYKRBADR, 0, "debug", XYKRBDBG, CM_INV, "instance", XYKRBINS, 0, "keytab", XYKRBKTB, 0, "lifetime", XYKRBLIF, 0, "preauth", XYKRBPRE, 0, "principal", XYKRBPR, 0, "prompt", XYKRBPRM, 0, "realm", XYKRBRL, 0, "service-name", XYKRBSRV, 0 }; static int nk4tab = sizeof(k4tab)/sizeof(struct keytab); static struct keytab k5tab[] = { "addresses", XYKRBADD, 0, "autodelete", XYKRBDEL, CM_INV, "autodestroy", XYKRBDEL, 0, "autoget", XYKRBGET, 0, "cc", XYKRBCC, CM_INV, "check-address", XYKRBADR, 0, "credentials-cache", XYKRBCC, 0, "forwardable", XYKRBFWD, 0, "get-k4-tgt", XYKRBK5K4,0, "instance", XYKRBINS, 0, "keytab", XYKRBKTB, 0, "lifetime", XYKRBLIF, 0, "no-addresses", XYKRBNAD, 0, "principal", XYKRBPR, 0, "prompt", XYKRBPRM, 0, "proxiable", XYKRBPRX, 0, "realm", XYKRBRL, 0, "renewable", XYKRBRNW, 0, "service-name", XYKRBSRV, 0 }; static int nk5tab = sizeof(k5tab)/sizeof(struct keytab); #define KRB_PW_PRM 1 #define KRB_PR_PRM 2 static struct keytab krbprmtab[] = { "password", KRB_PW_PRM, 0, "principal", KRB_PR_PRM, 0 }; #endif /* CK_KERBEROS */ #ifdef CK_SRP static struct keytab srptab[] = { "prompt", XYSRPPRM, 0 }; static int nsrptab = sizeof(srptab)/sizeof(struct keytab); #define SRP_PW_PRM 1 static struct keytab srpprmtab[] = { "password", SRP_PW_PRM, 0 }; #endif /* CK_SRP */ #ifdef CK_SSL static struct keytab ssltab[] = { "certs-ok", XYSSLCOK, CM_INV, "cipher-list", XYSSLCL, 0, "crl-dir", XYSSLCRLD, 0, "crl-file", XYSSLCRL, 0, "debug", XYSSLDBG, 0, "dh-key-file", XYSSLDKFL, CM_INV, "dh-param-file", XYSSLDPFL, 0, "dsa-cert-chain-file", XYSSLDCCF, 0, "dsa-cert-file", XYSSLDCFL, 0, "dsa-key-file", XYSSLDKFL, 0, "dummy", XYSSLDUM, CM_INV, "only", XYSSLON, CM_INV, "random-file", XYSSLRND, 0, "rsa-cert-chain-file", XYSSLRCCF, 0, "rsa-cert-file", XYSSLRCFL, 0, "rsa-key-file", XYSSLRKFL, 0, "verbose", XYSSLVRB, 0, "verify", XYSSLVRF, 0, "verify-dir", XYSSLVRFD, 0, "verify-file", XYSSLVRFF, 0 }; static int nssltab = sizeof(ssltab)/sizeof(struct keytab); static struct keytab sslvertab[] = { "fail-if-no-peer-cert", SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, 0, "no", SSL_VERIFY_NONE, 0, "none", SSL_VERIFY_NONE, CM_INV, "off", SSL_VERIFY_NONE, CM_INV, "on", SSL_VERIFY_PEER, CM_INV, "peer-cert", SSL_VERIFY_PEER, 0 }; static int nsslvertab = sizeof(sslvertab)/sizeof(struct keytab); #endif /* CK_SSL */ #endif /* CK_AUTHENTICATION */ #ifdef CK_ENCRYPTION int cx_type = CX_AUTO; extern int sl_cx_type; #endif /* CK_ENCRYPTION */ extern char *tcp_address; #ifndef NOHTTP extern char * tcp_http_proxy; extern char * tcp_http_proxy_user; extern char * tcp_http_proxy_pwd; extern char * tcp_http_proxy_agent; #endif /* NOHTTP */ #ifdef NT #ifdef CK_SOCKS extern char *tcp_socks_svr; extern char *tcp_socks_user; #ifdef CK_SOCKS_NS extern char *tcp_socks_ns; #endif /* CK_SOCKS_NS */ #endif /* CK_SOCKS */ #endif /* NT */ #define UPW_USER 1 #define UPW_PASS 2 #define UPW_AGENT 3 static struct keytab userpass[] = { { "/agent", UPW_AGENT, CM_ARG }, { "/password", UPW_PASS, CM_ARG }, { "/user", UPW_USER, CM_ARG }, }; static int nuserpass = sizeof(userpass)/sizeof(struct keytab); static struct keytab tnnegtab[] = { /* TELNET NEGOTIATION table */ "accepted", TN_NG_AC, 0, "refused", TN_NG_RF, 0, "req", TN_NG_RQ, CM_INV|CM_ABR, "requ", TN_NG_RQ, CM_INV|CM_ABR, "reque", TN_NG_RQ, CM_INV|CM_ABR, "reques", TN_NG_RQ, CM_INV|CM_ABR, "request", TN_NG_RQ, CM_INV|CM_ABR, "requeste", TN_NG_RQ, CM_INV|CM_ABR, "requested", TN_NG_RQ, 0, "required", TN_NG_MU, 0 }; static int ntnnegtab = sizeof(tnnegtab)/sizeof(struct keytab); #ifdef CK_ENCRYPTION static struct keytab typkwd[] = { "/type", 0, CM_ARG }; static struct keytab tnenctab[] = { /* TELNET ENCRYPTION table */ "accepted", TN_NG_AC, CM_INV, "refused", TN_NG_RF, CM_INV, "req", TN_NG_RQ, CM_INV|CM_ABR, "requ", TN_NG_RQ, CM_INV|CM_ABR, "reque", TN_NG_RQ, CM_INV|CM_ABR, "reques", TN_NG_RQ, CM_INV|CM_ABR, "request", TN_NG_RQ, CM_INV|CM_ABR, "requeste", TN_NG_RQ, CM_INV|CM_ABR, "requested", TN_NG_RQ, CM_INV, "required", TN_NG_MU, CM_INV, "start", TN_EN_START, CM_INV, "stop", TN_EN_STOP, CM_INV, "type", TN_EN_TYP, 0 }; static int ntnenc = sizeof(tnenctab)/sizeof(struct keytab) ; #endif /* CK_ENCRYPTION */ #ifdef CK_FORWARD_X static struct keytab tnfwdxtab[] = { /* TELNET FORWARD-X table */ "no-encryption", 1, CM_INV, "xauthority-file", 0, 0 }; static int ntnfwdx = sizeof(tnfwdxtab)/sizeof(struct keytab) ; #endif /* CK_FORWARD_X */ static struct keytab tnbugtab[] = { /* TELNET BUG table */ "auth-krb5-des", 4, 0, "binary-me-means-u-too", 0, 0, "binary-u-means-me-too", 1, 0, "infinite-loop-check", 2, 0, "sb-implies-will-do", 3, 0 }; #ifdef CK_ENVIRONMENT static struct keytab tnenvtab[] = { /* TELNET ENVIRONMENT table */ "acct", TN_ENV_ACCT, 0, "display", TN_ENV_DISP, 0, "job", TN_ENV_JOB, 0, "location", TN_ENV_LOC, 0, "off", TN_ENV_OFF, CM_INV, "on", TN_ENV_ON, CM_INV, "printer", TN_ENV_PRNT, 0, "systemtype",TN_ENV_SYS, 0, "user", TN_ENV_USR, 0, "uservar", TN_ENV_UVAR, 0, "", 0, 0 }; static int ntnenv = sizeof(tnenvtab)/sizeof(struct keytab) - 1; #endif /* CK_ENVIRONMENT */ #ifdef CK_AUTHENTICATION static struct keytab tnauthtab[] = { /* TELNET AUTHENTICATION table */ "accepted", TN_NG_AC, CM_INV, "encrypt-flag", TN_AU_ENC, 0, "forwarding", TN_AU_FWD, 0, "how-flag", TN_AU_HOW, 0, "refused", TN_NG_RF, CM_INV, "req", TN_NG_RQ, CM_INV|CM_ABR, "requ", TN_NG_RQ, CM_INV|CM_ABR, "reque", TN_NG_RQ, CM_INV|CM_ABR, "reques", TN_NG_RQ, CM_INV|CM_ABR, "request", TN_NG_RQ, CM_INV|CM_ABR, "requeste", TN_NG_RQ, CM_INV|CM_ABR, "requested", TN_NG_RQ, CM_INV, "required", TN_NG_MU, CM_INV, "type", TN_AU_TYP, 0 }; static int ntnauth = sizeof(tnauthtab)/sizeof(struct keytab) ; struct keytab autyptab[] = { /* TELNET AUTHENTICATION TYPE table */ "automatic", AUTH_AUTO, 0, #ifdef CK_KERBEROS "k4", AUTH_KRB4, CM_INV, "k5", AUTH_KRB5, CM_INV, "kerberos4", AUTH_KRB4, 0, "kerberos5", AUTH_KRB5, 0, "kerberos_iv",AUTH_KRB4, CM_INV, "kerberos_v", AUTH_KRB5, CM_INV, "krb4", AUTH_KRB4, CM_INV, "krb5", AUTH_KRB5, CM_INV, #endif /* CK_KERBEROS */ "none", AUTH_NONE, 0, #ifdef NT "ntlm", AUTH_NTLM, 0, #endif /* NT */ #ifdef CK_SRP "srp", AUTH_SRP, 0, #endif /* CK_SRP */ #ifdef CK_SSL "ssl", AUTH_SSL, 0, #endif /* CK_SSL */ "", 0, 0 }; int nautyp = sizeof(autyptab)/sizeof(struct keytab) - 1; struct keytab auhowtab[] = { /* TELNET AUTHENTICATION HOW table */ "any", TN_AUTH_HOW_ANY, 0, "mutual", TN_AUTH_HOW_MUTUAL, 0, "one-way", TN_AUTH_HOW_ONE_WAY, 0, "", 0, 0 }; int nauhow = sizeof(auhowtab)/sizeof(struct keytab) - 1; struct keytab auenctab[] = { /* TELNET AUTHENTICATION ENCRYPT table */ "any", TN_AUTH_ENC_ANY, 0, "none", TN_AUTH_ENC_NONE, 0, "telopt", TN_AUTH_ENC_TELOPT, 0, #ifdef CK_SSL "tls", TN_AUTH_ENC_TLS, 0, #endif /* CK_SSL */ "", 0, 0 }; int nauenc = sizeof(auenctab)/sizeof(struct keytab) - 1; #endif /* CK_AUTHENTICATION */ #define TN_NL_BIN 3 #define TN_NL_NVT 4 static struct keytab tn_nlmtab[] = { /* TELNET NEWLINE-MODE table */ "binary-mode", TN_NL_BIN, 0, /* Binary mode */ "nvt", TN_NL_NVT, 0, /* NVT mode */ "off", TNL_CRNUL, CM_INV, /* CR-NUL (TELNET spec) */ "on", TNL_CRLF, CM_INV, /* CR-LF (TELNET spec) */ "raw", TNL_CR, CM_INV /* CR only (out of spec) */ }; static int ntn_nlm = (sizeof(tn_nlmtab) / sizeof(struct keytab)); static struct keytab tnlmtab[] = { /* TELNET NEWLINE-MODE table */ "cr", TNL_CR, CM_INV, /* CR only (out of spec) */ "cr-lf", TNL_CRLF, CM_INV, /* CR-LF (TELNET spec) */ "cr-nul", TNL_CRNUL, CM_INV, /* CR-NUL (TELNET spec) */ "lf", TNL_LF, CM_INV, /* LF instead of CR-LF */ "off", TNL_CRNUL, 0, /* CR-NUL (TELNET spec) */ "on", TNL_CRLF, 0, /* CR-LF (TELNET spec) */ "raw", TNL_CR, 0 /* CR only (out of spec) */ }; static int ntnlm = (sizeof(tnlmtab) / sizeof(struct keytab)); struct keytab tntab[] = { #ifdef CK_AUTHENTICATION "authentication", CK_TN_AU, 0, #endif /* CK_AUTHENTICATION */ "b", CK_TN_BM, CM_INV|CM_ABR, "bi", CK_TN_BM, CM_INV|CM_ABR, "bin", CK_TN_BM, CM_INV|CM_ABR, "bina", CK_TN_BM, CM_INV|CM_ABR, "binar", CK_TN_BM, CM_INV|CM_ABR, "binary", CK_TN_BM, CM_INV|CM_ABR, "binary-", CK_TN_BM, CM_INV|CM_ABR, "binary-mode", CK_TN_BM, CM_INV, "binary-transfer-mode", CK_TN_XF, 0, "binary-xfer-mode", CK_TN_XF, CM_INV, "bug", CK_TN_BUG, 0, "debug", CK_TN_DB, 0, "delay-sb", CK_TN_DL, 0, "echo", CK_TN_EC, 0, #ifdef CK_ENCRYPTION "encryption", CK_TN_ENC, 0, #endif /* CK_ENCRYPTION */ #ifdef CK_ENVIRONMENT "environment", CK_TN_ENV, 0, #endif /* CK_ENVIRONMENT */ #ifdef CK_FORWARD_X "forward-x", CK_TN_FX, 0, #endif /* CK_FORWARD_X */ #ifdef IKS_OPTION "kermit", CK_TN_IKS, CM_INV, #endif /* IKS_OPTION */ #ifdef CK_SNDLOC "location", CK_TN_LOC, 0, #endif /* CK_SNDLOC */ #ifdef CK_NAWS "naws", CK_TN_NAWS, CM_INV, #endif /* CK_NAWS */ "newline-mode", CK_TN_NL, 0, "no-encrypt-during-xfer", CK_TN_NE, CM_INV, "prompt-for-userid",CK_TN_PUID,0, "remote-echo", CK_TN_RE, 0, #ifdef CK_SSL "start-tls", CK_TN_TLS, CM_INV, #endif /* CK_SSL */ #ifdef NT "sfu-compatibility", CK_TN_SFU, 0, #else "sfu-compatibility", CK_TN_SFU, CM_INV, #endif /* NT */ "terminal-type", CK_TN_TT, 0, "wait-for-negotiations", CK_TN_WAIT, 0, #ifdef CK_ENVIRONMENT "xdisplay-location",CK_TN_XD, CM_INV, #endif /* CK_ENVIRONMENT */ "", 0, 0 }; int ntn = (sizeof(tntab) / sizeof(struct keytab)) - 1; struct keytab tnopttab[] = { #ifdef CK_AUTHENTICATION "authentication", CK_TN_AU, 0, #else "authentication", CK_TN_AU, CM_INV, #endif /* CK_AUTHENTICATION */ "binary-mode", CK_TN_BM, 0, #ifdef TN_COMPORT "c", CK_TN_CPC, CM_INV|CM_ABR, "co", CK_TN_CPC, CM_INV|CM_ABR, "com", CK_TN_CPC, CM_INV|CM_ABR, "com-port-control",CK_TN_CPC, 0, "comport-control", CK_TN_CPC, CM_INV, #else /* TN_COMPORT */ "com-port-control",CK_TN_CPC, CM_INV, "comport-control", CK_TN_CPC, CM_INV, #endif /* TN_COMPORT */ "echo", CK_TN_EC, 0, #ifdef CK_ENCRYPTION "encryption", CK_TN_ENC, 0, #else "encryption", CK_TN_ENC, CM_INV, #endif /* CK_ENCRYPTION */ #ifdef CK_FORWARD_X "forward-x", CK_TN_FX, 0, #else /* CK_FORWARD_X */ "forward-x", CK_TN_FX, CM_INV, #endif /* CK_FORWARD_X */ "ibm-sak", CK_TN_SAK, CM_INV, #ifdef IKS_OPTION "kermit", CK_TN_IKS, 0, #else "kermit", CK_TN_IKS, CM_INV, #endif /* IKS_OPTION */ "lflow", CK_TN_FLW, CM_INV, "logout", CK_TN_LOG, 0, #ifdef CK_NAWS "naws", CK_TN_NAWS, 0, #else "naws", CK_TN_NAWS, CM_INV, #endif /* CK_NAWS */ #ifdef CK_ENVIRONMENT "new-environment", CK_TN_ENV, 0, #else "new-environment", CK_TN_ENV, CM_INV, #endif /* CK_ENVIRONMENT */ "pragma-heartbeat",CK_TN_PHR, CM_INV, "pragma-logon", CK_TN_PLG, CM_INV, "pragma-sspi", CK_TN_PSP, CM_INV, "sak", CK_TN_SAK, CM_INV, #ifdef CK_SNDLOC "send-location", CK_TN_LOC, 0, #else "send-location", CK_TN_LOC, CM_INV, #endif /* CK_SNDLOC */ "sga", CK_TN_SGA, CM_INV|CM_ABR, #ifdef CK_SSL "start-tls", CK_TN_TLS, 0, #else "start-tls", CK_TN_TLS, CM_INV, #endif /* CK_SSL */ "suppress-go-aheads", CK_TN_SGA, 0, "terminal-type", CK_TN_TT, 0, "ttype", CK_TN_TT, CM_INV|CM_ABR, #ifdef CK_ENVIRONMENT "xdisplay-location", CK_TN_XD, 0, #else "xdisplay-location", CK_TN_XD, CM_INV, #endif /* CK_ENVIRONMENT */ "", 0, 0 }; int ntnopt = (sizeof(tnopttab) / sizeof(struct keytab)) - 1; struct keytab tnoptsw[] = { "/client", CK_TN_CLIENT, 0, "/server", CK_TN_SERVER, 0 }; int ntnoptsw = (sizeof(tnoptsw) / sizeof(struct keytab)); #endif /* TNCODE */ struct keytab ftrtab[] = { /* Feature table */ #ifndef NOCSETS /* 0 = we have it, 1 = we don't */ "character-sets", 0, 0, #else "character-sets", 1, 0, #endif /* NOCSETS */ #ifndef NOCYRIL "cyrillic", 0, 0, #else "cyrillic", 1, 0, #endif /* NOCYRIL */ #ifndef NOLOGDIAL "cx-log", 0, 0, #else "cx-log", 1, 0, #endif /* NOLOGDIAL */ #ifndef NODEBUG "debug", 0, 0, #else "debug", 1, 0, #endif /* NODEBUG */ #ifndef NODIAL "dial", 0, 0, #else "dial", 1, 0, #endif /* NODIAL */ #ifdef DYNAMIC "dynamic-memory", 0, 0, #else "dynamic-memory", 1, 0, #endif /* DYNAMIC */ #ifndef NOXFER "file-transfer", 0, 0, #else "file-transfer", 1, 0, #endif /* NOXFER */ #ifdef XXFWD "forward", 0, 0, #else "forward", 1, 0, #endif /* XXFWD */ #ifdef NEWFTP "ftp", 0, 0, #else "ftp", 1, 0, #endif /* NEWFTP */ #ifdef CK_CURSES "fullscreen-display", 0, 0, #else "fullscreen-display", 1, 0, #endif /* CK_CURSES */ #ifdef GREEK "greek", 0, 0, #else "greek", 1, 0, #endif /* GREEK */ #ifdef HEBREW "hebrew", 0, 0, #else "hebrew", 1, 0, #endif /* HEBREW */ #ifndef NOHELP "help", 0, 0, #else "help", 1, 0, #endif /* NOHELP */ #ifndef NOIKSD "iksd", 0, 0, #else "iksd", 1, 0, #endif /* NOIKSD */ #ifndef NOSPL "if-command", 0, 0, #else "if-command", 1, 0, #endif /* NOSPL */ #ifndef NOJC #ifdef UNIX "job-control", 0, 0, #else "job-control", 1, 0, #endif /* UNIX */ #else "job-control", 1, 0, #endif /* NOJC */ #ifdef KANJI "kanji", 0, 0, #else "kanji", 1, 0, #endif /* KANJI */ #ifndef NOXFER "kermit", 0, 0, #else "kermit", 1, 0, #endif /* NOXFER */ #ifdef CK_KERBEROS "kerberos", 0, 0, #else "kerberos", 1, 0, #endif /* CK_KERBEROS */ #ifndef NOCSETS "latin1", 0, 0, #else "latin1", 1, 0, #endif /* NOCSETS */ #ifdef LATIN2 "latin2", 0, 0, #else "latin2", 1, 0, #endif /* LATIN2 */ #ifdef CKLEARN "learned-scripts", 0, 0, #else "learned-scripts", 1, 0, #endif /* CKLEARN */ #ifndef NOLOCAL "making-connections", 0, 0, #else "making-connections", 1, 0, #endif /* NOLOCAL */ #ifdef NETCONN "network", 0, 0, #else "network", 1, 0, #endif /* NETCONN */ #ifdef NT #ifdef CK_AUTHENTICATION "ntlm", 1, 0, #else /* CK_AUTHENTICATION */ "ntlm", 0, 0, #endif /* CK_AUTHENTICATION */ #else /* NT */ "ntlm", 0, 0, #endif /* NT */ #ifdef PIPESEND "pipes", 0, 0, #else #ifdef NETCMD "pipes", 0, 0, #endif /* NETCMD */ #endif /* PIPESEND */ #ifndef PIPESEND #ifndef NETCMD "pipes", 1, 0, #endif /* PIPESEND */ #endif /* NETCMD */ #ifdef NETPTY "pty", 0, 0, #else "pty", 1, 0, #endif /* NETPTY */ #ifndef NOPUSH "push", 0, 0, #else "push", 1, 0, #endif /* PUSH */ #ifdef CK_REDIR "redirect", 0, 0, #else "redirect", 1, 0, #endif /* CK_REDIR */ #ifdef CK_RTSCTS "rts/cts", 0, 0, #else "rts/cts", 1, 0, #endif /* RTS/CTS */ #ifndef NOSCRIPT "script-command", 0, 0, #else "script-command", 1, 0, #endif /* NOSCRIPT */ #ifndef NOSERVER "server-mode", 0, 0, #else "server-mode", 1, 0, #endif /* NOSERVER */ #ifndef NOSEXP "sexpression", 0, 0, #else "sexpression", 1, 0, #endif /* NOSEXP */ #ifdef SFTP_BUILTIN "sftp", 1, 0, #else "sftp", 0, 0, #endif /* SFTP_BUILTIN */ #ifndef NOSHOW "show-command", 0, 0, #else "show-command", 1, 0, #endif /* NOSHOW */ #ifdef CK_SRP "srp", 0, 0, #else "srp", 1, 0, #endif /* CK_SRP */ #ifdef SSHBUILTIN "ssh", 0, 0, #else /* SSHBUILTIN */ "ssh", 1, 0, #endif /* SSHBUILTIN */ #ifdef CK_SSL "ssl/tls", 0, 0, #else "ssl/tls", 1, 0, #endif /* CK_SSL */ #ifndef NOXMIT "transmit", 0, 0, #else "transmit", 1, 0, #endif /* NOXMIT */ #ifdef UNICODE "unicode", 0, 0, #else "unicode", 1, 0, #endif /* UNICODE */ #ifdef CK_XYZ "xyzmodem", 0, 0, #else "xyzmodem", 1, 0, #endif /* CK_XYZ */ "", 0, 0 }; int nftr = (sizeof(ftrtab) / sizeof(struct keytab)) - 1; struct keytab desttab[] = { /* SET DESTINATION */ #ifdef CALIBRATE "calibrate", DEST_N, CM_INV, #endif /* CALIBRATE */ "disk", DEST_D, 0, #ifdef CALIBRATE "nowhere", DEST_N, 0, #endif /* CALIBRATE */ "printer", DEST_P, 0, "screen", DEST_S, 0 }; int ndests = (sizeof(desttab) / sizeof(struct keytab)); #ifndef NOSPL /* Used only with script programming items... */ #ifndef NOSERVER /* This is just to avoid some */ #define CK_PARSDIR /* "statement not reached" */ #else /* complaints... */ #ifndef NODIAL #define CK_PARSDIR #endif /* NODIAL */ #endif /* NOSERVER */ /* cx == 0 means dial directory cx == 1 means network directory cx == 2 means a directory path list */ static int parsdir(cx) int cx; { int i, x, y, dd; /* Workers */ int nxdir; char *s; char ** xdir; char *pp[MAXGETPATH]; /* Temporary name pointers */ #ifdef ZFNQFP struct zfnfp * fnp; #ifdef OS2 char * env; char dirpath[4096]; #else /* OS2 */ char dirpath[1024]; /* For fully qualified filenames */ #endif /* OS2 */ #endif /* ZFNQFP */ int max = 0; /* Maximum number of things to parse */ char c; #ifndef NODIAL if (cx == 0) { /* Dialing */ nxdir = ndialdir; xdir = dialdir; max = MAXDDIR; } else #ifdef NETCONN if (cx == 1) { /* Network */ nxdir = nnetdir; xdir = netdir; max = MAXDDIR; } else #endif /* NETCONN */ #endif /* NODIAL */ #ifndef NOSERVER if (cx == 2) { /* GET path */ nxdir = ngetpath; xdir = getpath; max = MAXGETPATH; } else /* Called with invalid function code */ #endif /* NOSERVER */ return(-2); for (i = 0; i < MAXGETPATH; i++) /* Init these. */ pp[i] = NULL; #ifdef CK_PARSDIR dd = 0; /* Temporary name counter */ while (1) { if (cx != 2) { /* Dialing or Network Directory */ #ifdef OS2 int len; char * appdata0 = NULL, * appdata1 = NULL; #ifdef NT env = getenv("K95PHONES"); makestr(&appdata0,(char *)GetAppData(0)); makestr(&appdata1,(char *)GetAppData(1)); #else /* NT */ env = getenv("K2PHONES"); #endif /* NT */ if (!env) env = getenv("K95PHONES"); if (!env) env = ""; dirpath[0] = '\0'; len = strlen(env) + 2*strlen(startupdir) + 2*strlen(inidir) + (appdata0?2*strlen(appdata0):0) + (appdata1?2*strlen(appdata1):0) + 2*strlen(zhome()) + 2*strlen(exedir) + 8*strlen("PHONES/") + 12; if (len < 4096) /* SAFE */ sprintf(dirpath, "%s%s%s;%s%s;%s%s%s%s%s%s%s%s%s;%s%s;%s;%s%s", /* Semicolon-separated path list */ env, (env[0] && env[strlen(env)-1] == ';') ? "" : ";", startupdir, startupdir, "PHONES/", appdata1 ? appdata1 : "", appdata1 ? "Kermit 95;" : "", appdata1 ? appdata1 : "", appdata1 ? "Kermit 95/PHONES/;" : "", appdata0 ? appdata0 : "", appdata0 ? "Kermit 95;" : "", appdata0 ? appdata0 : "", appdata0 ? "Kermit 95/PHONES/;" : "", inidir, inidir, "PHONES/", zhome(), zhome(), "PHONES/", exedir, exedir, "PHONES/" ); #ifdef NT makestr(&appdata0,NULL); makestr(&appdata1,NULL); #endif /* NT */ #else #ifdef UNIX y = 1024; s = dirpath; zzstring("\\v(home)",&s,&y); #endif /* UNIX */ #endif /* OS2 */ y = cmifip( "Names of one or more directory files, separated by spaces", "",&s,&x,0, #ifdef OS2ORUNIX dirpath, #else NULL, #endif /* OS2ORUNIX */ xxstring ); } else { /* List of directory names */ x = 0; y = cmdir("Directory name","",&s,xxstring); } if (y < 0) { if (y == -3) { /* EOL or user typed */ if ((y = cmcfm()) < 0) return(y); for (i = 0; i < max; i++) { /* Clear these */ if (i < nxdir && xdir[i]) { free(xdir[i]); } xdir[i] = (i < dd) ? pp[i] : NULL; } #ifndef NODIAL if (cx == 0) ndialdir = dd; #ifdef NETCONN if (cx == 1) nnetdir = dd; #endif /* NETCONN */ #endif /* NODIAL */ #ifndef NOSERVER if (cx == 2) ngetpath = dd; #endif /* NOSERVER */ return(success = 1); } else { /* Parse error */ for (i = 0; i < dd; i++) { /* Free temp storage */ if (pp[i]) free(pp[i]); /* but don't change */ pp[i] = NULL; /* anything else */ } return(y); } } if (x) { printf("?Wildcards not allowed\n"); return(-9); } #ifdef CK_TMPDIR if (cx == 2 && !isdir(s)) { printf("?Not a directory - %s\n", s); return(-9); } #endif /* CK_TMPDIR */ #ifdef ZFNQFP if (cx < 2) { if (!isabsolute(s)) { /* If not relative get full path */ if ((fnp = zfnqfp(s,TMPBUFSIZ - 1,tmpbuf))) { if (fnp->fpath) if ((int) strlen(fnp->fpath) > 0) s = fnp->fpath; } } } #endif /* ZFNQFP */ c = NUL; x = strlen(s); if (x > 0) /* Get last char */ c = s[x-1]; debug(F000,"parsdir s",s,c); if ((pp[dd] = malloc(strlen(s)+2)) == NULL) { printf("?Internal error - malloc\n"); for (i = 0; i < dd; i++) { /* Free temp storage */ if (pp[i]) free(pp[i]); pp[i] = NULL; } return(-9); } else { /* Have storage for name */ strcpy(pp[dd],s); /* Copy string into new storage */ debug(F111,"parsdir pp[dd] 1",pp[dd],dd); #ifndef NOXFER if (cx == 2) { /* If we are parsing directories */ char dirsep[2]; extern int myindex; /* Append directory separator if */ extern struct sysdata sysidlist[]; /* it is missing... */ debug(F101,"parsdir myindex","",myindex); if (myindex > -1) if (sysidlist[myindex].sid_unixlike) if (c != sysidlist[myindex].sid_dirsep) { dirsep[0] = sysidlist[myindex].sid_dirsep; dirsep[1] = NUL; strcat(pp[dd], (char *) dirsep); /* safe */ } } #endif /* NOXFER */ debug(F111,"parsdir pp[dd] 2",pp[dd],dd); if (++dd > max) { printf("?Too many directories - %d max\n", max); for (i = 0; i < dd; i++) { /* Free temp storage */ if (pp[i]) free(pp[i]); pp[i] = NULL; } } } } #endif /* CK_PARSDIR */ } #endif /* NOSPL */ #ifndef NOSERVER static int cklogin() { int x; char * s; char username[LOGINLEN+1]; char password[LOGINLEN+1]; char account[LOGINLEN+1]; extern char * x_user, * x_passwd, * x_acct; extern int x_login, x_logged; username[0] = NUL; password[0] = NUL; account[0] = NUL; x = cmfld("username", "", &s, xxstring); if (x != -3) { if (x < 0) return(x); if ((int)strlen(s) > LOGINLEN) { printf("\"%s\" - too long, %d max\n", s, LOGINLEN); return(-9); } ckstrncpy(username,s,LOGINLEN+1); x = cmfld("password", "", &s, xxstring); if (x != -3) { if (x < 0) return(x); if ((int)strlen(s) > LOGINLEN) { printf("\"%s\" - too long, %d max\n", s, LOGINLEN); return(-9); } ckstrncpy(password,s,LOGINLEN+1); x = cmfld("account", "", &s, xxstring); if (x != -3) { if (x < 0) return(x); if ((int)strlen(s) > LOGINLEN) { printf("\"%s\" - too long, %d max\n", s, LOGINLEN); return(-9); } ckstrncpy(account,s,LOGINLEN+1); if ((x = cmcfm()) < 0) return(x); } } } makestr(&x_user,username); makestr(&x_passwd,password); makestr(&x_acct,account); x_login = (x_user) ? 1 : 0; x_logged = 0; return(1); } #endif /* NOSERVER */ #ifndef NOLOCAL static int setdcd() { int x, y, z = 0; if ((y = cmkey(crrtab,ncrr,"","automatic",xxstring)) < 0) return(y); if (y == CAR_ON) { x = cmnum("Carrier wait timeout, seconds","0",10,&z,xxstring); if (x < 0) return(x); } if ((x = cmcfm()) < 0) return(x); carrier = ttscarr(y); cdtimo = z; return(1); } #endif /* NOLOCAL */ extern struct keytab yesno[]; extern int nyesno; /* g e t y e s n o */ static struct keytab q0yesno[] = { /* Yes/No/Quit keyword table */ "no", 0, 0, "ok", 1, 0, "yes", 1, 0 }; static int nq0yesno = (sizeof(q0yesno) / sizeof(struct keytab)); static struct keytab q1yesno[] = { /* Yes/No/Quit keyword table */ "no", 0, 0, "ok", 1, 0, "quit", 2, 0, "yes", 1, 0 }; static int nq1yesno = (sizeof(q1yesno) / sizeof(struct keytab)); static struct keytab q2yesno[] = { /* Yes/No/Quit keyword table */ "go", 3, 0, "no", 0, 0, "ok", 1, 0, "yes", 1, 0 }; static int nq2yesno = (sizeof(q2yesno) / sizeof(struct keytab)); static struct keytab q3yesno[] = { /* Yes/No/Quit keyword table */ "go", 3, 0, "no", 0, 0, "ok", 1, 0, "quit", 2, 0, "yes", 1, 0 }; static int nq3yesno = (sizeof(q3yesno) / sizeof(struct keytab)); /* Ask question, get yes/no answer */ int getyesno(msg, flags) char * msg; int flags; { #ifdef CK_RECALL extern int on_recall; /* around Password prompting */ #endif /* CK_RECALL */ int y, z; #ifndef NOLOCAL #ifdef OS2 extern int vmode, win95_popup, startflags; int vmode_sav = vmode; #endif /* OS2 */ #endif /* NOLOCAL */ #ifdef CK_APC if ( apcactive != APC_INACTIVE && (apcstatus & APC_NOINP) ) { return(success = 0); } #endif /* CK_APC */ #ifndef NOLOCAL #ifdef OS2 #ifdef COMMENT if (win95_popup && !(startflags & 96) #ifdef IKSD && !inserver #endif /* IKSD */ ) return(popup_readyesno(vmode,NULL,msg,flags)); #endif /* COMMENT */ if (vmode == VTERM) { vmode = VCMD; VscrnIsDirty(VTERM); VscrnIsDirty(VCMD); } #endif /* OS2 */ #endif /* NOLOCAL */ #ifdef VMS /* In VMS, whenever a TAKE file or macro is active, we restore the original console modes so Ctrl-C/Ctrl-Y can work. But here we go interactive again, so we have to temporarily put them back. */ if (!xcmdsrc) concb((char)escape); #endif /* VMS */ #ifdef CK_RECALL on_recall = 0; #endif /* CK_RECALL */ cmsavp(psave,PROMPTL); /* Save old prompt */ cmsetp(msg); /* Make new prompt */ z = 0; /* Initialize answer to No. */ cmini(ckxech); /* Initialize parser. */ do { prompt(NULL); /* Issue prompt. */ switch (flags) { case 0: y = cmkey(q0yesno,nq0yesno,"","",NULL); break; case 1: y = cmkey(q1yesno,nq1yesno,"","",NULL); break; case 2: y = cmkey(q2yesno,nq2yesno,"","",NULL); break; default: y = cmkey(q3yesno,nq3yesno,"","",NULL); } if (y < 0) { if (y == -4) { /* EOF */ z = y; break; } else if (y == -3) /* No answer? */ printf(" Please respond; type '?' to see valid answers.\n"); cmini(ckxech); } else { z = y; /* Save answer */ y = cmcfm(); /* Get confirmation */ } } while (y < 0); /* Continue till done */ cmsetp(psave); /* Restore real prompt */ #ifdef VMS if (cmdlvl > 0) /* In VMS and not at top level, */ conres(); /* restore console again. */ #endif /* VMS */ #ifndef NOLOCAL #ifdef OS2 if (vmode != vmode_sav) { vmode = VTERM; VscrnIsDirty(VCMD); VscrnIsDirty(VTERM); } #endif /* OS2 */ #endif /* NOLOCAL */ return(z); } #ifdef KUI extern HWND hwndConsole; _PROTOTYP(int gui_txt_dialog,(char *,char *,int,char *,int,char *,int)); _PROTOTYP(int gui_mtxt_dialog,(char *,int,struct txtbox [])); _PROTOTYP(int gui_position,(int, int)); _PROTOTYP(int gui_resize_mode,(int)); _PROTOTYP(int gui_win_run_mode,(int)); _PROTOTYP(int gui_saveas_dialog,(char *,char *, int, char *, char *, int)); extern int gui_dialog; #endif /* KUI */ /* u q _ o k -- User Query, get Yes/No or OK Cancel */ /* Call with: preface: Explanatory text to print, or NULL. prompt: Prompt. mask: Bitmask for legal responses: 1 = OK or Yes; 2 = No or Cancel. help: Help text (array of strings or NULL) [not used by parser]. dflt: Default response (1 or 2) [not used by parser]. Returns: -1: Invalid argument(s). 0: User said No or Cancel. 1 User said Yes or OK. Notes: preface and prompt should not include final line terminator but may include embedded ones. Help text is in case GUI dialog needs a Help button; final element of help-string array is "". dflt is used by GUI to highlight the default response button. */ int #ifdef CK_ANSIC uq_ok(char * preface, char * prompt, int mask,char ** help, int dflt) #else /* CK_ANSIC */ uq_ok(preface,prompt,mask,help,dflt) char * preface, * prompt, ** help; int mask, dflt; #endif /* CK_ANSIC */ /* uq_ok */ { int rc, len; char * text=NULL; if (!prompt) return(-1); if ((mask & 3) == 1) { /* OK (GUI only) */ #ifdef KUI if ( gui_dialog ) { /* This one is for popup help, alerts, etc */ if (preface) { len = strlen(preface) + strlen(prompt) + 4; text = (char *)malloc(len); ckmakmsg(text,len,preface,"\n",prompt,NULL); } rc = MessageBox(hwndConsole, text ? text : prompt, prompt, MB_OK | MB_ICONINFORMATION | MB_TASKMODAL); ShowWindowAsync(hwndConsole,SW_SHOWNORMAL); SetForegroundWindow(hwndConsole); if (text) free(text); if (!rc) return(-1); else return(1); } else #endif /* KUI */ { if (preface) /* Just display the text, if any */ printf("%s\n",preface); if (prompt) printf("%s\n",prompt); return(1); } } else if ((mask & 3) == 3) { /* Yes/No or OK/Cancel */ #ifdef KUI if ( gui_dialog ) { if (preface) { len = strlen(preface) + strlen(prompt) + 4; text = (char *)malloc(len); ckmakmsg(text,len,preface,"\n",prompt,NULL); } rc = MessageBox(hwndConsole, text ? text : prompt, prompt, MB_YESNO | MB_ICONINFORMATION | MB_TASKMODAL | (dflt == 2 ? MB_DEFBUTTON2 : MB_DEFBUTTON1)); ShowWindowAsync(hwndConsole,SW_SHOWNORMAL); SetForegroundWindow(hwndConsole); if (text) free(text); if (!rc) return(-1); else if (rc == IDNO || rc == IDCANCEL) return(0); else return(1); } else #endif /* KUI */ { if (preface) printf("%s\n",preface); return(getyesno(prompt,0)); } } else { printf("?Internal error: uq_ok()\n"); return(-1); } } /* u q _ t x t -- User Query, get single text response */ /* Call with: preface: Explanatory text to print, or NULL. prompt: Prompt. echo: 0 = don't echo; 1 = echo; 2 = echo with asterisks. help: Help text (array of strings or NULL) [not used by parser]. buf: Pointer to result buffer. buflen: Length of result buffer. dflt: Default response text or NULL [not used by parser]. timer: Optional Timeout Returns: 0: User said No or Cancel. 1 User said Yes or OK. Notes: preface, prompt, and help as for uq_ok(). */ int #ifdef CK_ANSIC uq_txt(char * preface, char * prompt, int echo, char ** help, char * buf, int buflen, char *dflt, int timer) #else /* CK_ANSIC */ uq_txt(preface,prompt,echo,help,buf,buflen,dflt,timer) char * preface, * prompt, ** help, * buf, * dflt; int buflen, echo, timer; #endif /* CK_ANSIC */ { #ifndef NOLOCAL #ifdef OS2 extern int vmode; extern int startflags; extern int win95_popup; #endif /* OS2 */ #endif /* NOLOCAL */ int rc; if (buflen < 1 || !buf) return(0); #ifdef KUI if ( gui_dialog ) { rc = gui_txt_dialog(preface,prompt,echo,buf,buflen,dflt,timer); if ( rc > -1 ) return(rc); /* Otherwise, the dialog could not be created. Fallback to text mode */ } #endif /* KUI */ #ifndef NOLOCAL #ifdef OS2 if (win95_popup && !(startflags & 96) #ifdef IKSD && !inserver #endif /* IKSD */ ) { debok = 0; /* Don't log */ if (echo == 1) popup_readtext(vmode,preface,prompt,buf,buflen,0); else popup_readpass(vmode,preface,prompt,buf,buflen,0); debok = 1; return(1); } #endif /* OS2 */ #endif /* NOLOCAL */ if (preface) printf("%s\n",preface); if (echo == 1) readtext(prompt,buf,buflen); else readpass(prompt,buf,buflen); return(1); /* (no buttons in parser) */ } /* u q _ m t x t -- User Query, get multiple text responses */ /* Call with: preface: Explanatory text to print, or NULL. help: Help text (array of strings or NULL) [not used by parser]. n: Number of responses wanted. field: Array of struct txtbox, one element per field, see ckuusr.h. Returns: 0: User said No or Cancel. 1 User said Yes or OK. Notes: preface and help as for uq_ok(). */ int #ifdef CK_ANSIC uq_mtxt(char * preface,char **help, int n, struct txtbox field[]) #else /* CK_ANSIC */ uq_mtxt(preface,help,n,field) char * preface; char ** help; int n; struct txtbox field[]; #endif /* CK_ANSIC */ { #ifndef NOLOCAL #ifdef OS2 extern int vmode; extern int startflags; extern int win95_popup; #endif /* OS2 */ #endif /* NOLOCAL */ int i, rc; if (n < 1 || !field) return(0); #ifdef KUI if ( gui_dialog ) { rc = gui_mtxt_dialog(preface, n, field); if ( rc > -1 ) return(rc); /* Otherwise, the dialog could not be created. Fallback to text mode */ } #endif /* KUI */ #ifndef NOLOCAL #ifdef OS2 if (win95_popup && !(startflags & 96) #ifdef IKSD && !inserver #endif /* IKSD */ ) { debok = 0; /* Don't log */ for (i = 0; i < n; i++) { if (field[i].t_echo == 1) popup_readtext(vmode,preface,field[i].t_lbl,field[i].t_buf,field[i].t_len,0); else popup_readpass(vmode,preface,field[i].t_lbl,field[i].t_buf,field[i].t_len,0); } debok = 1; return(1); } #endif /* OS2 */ #endif /* NOLOCAL */ if (preface) printf("%s\n",preface); for (i = 0; i < n; i++) { if (field[i].t_echo == 1) readtext(field[i].t_lbl,field[i].t_buf,field[i].t_len); else readpass(field[i].t_lbl,field[i].t_buf,field[i].t_len); } return(1); } /* u q _ f i l e -- User Query, get file or directory name */ /* Call with: preface: Explanatory text to print, or NULL. prompt: Prompt string. fc: Function code: 1 = input (existing) file 2 = existing directory 3 = create new output file 4 = output file allowing append access help: Help text (array of strings or NULL) [not used by parser]. dflt: Default response. result: Pointer to result buffer. rlength: Length of result buffer. Returns: -1: Invalid argument, result too long, or other error. 0: User Canceled. 1: OK, with file/pathname copied to result buffer. 2: Like 1, but for output file that is to be appended to. Notes: 1. preface and prompt should not include final line terminator but may include embedded ones. Help text is in case GUI dialog needs a Help button; final element of help-string array is "". 2. The default might be a filename, a directory name, a relative pathname, or an absolute pathname. This routine must convert it into into a fully qualified (absolute) pathname so the user knows exactly where the file is to be found or stored. In addition, the Windows version of this routine must separate the directory part from the name part, so it can display the given directory in the file dialog, and put name in the filename box to be edited, replaced, or accepted. 3. When called with FC 4, the Windows version should include "New" and "Append" buttons in the dialog. so the user can say whether the file should overwrite any file of the same name, or be appended to it. */ int #ifdef CK_ANSIC uq_file(char * preface, char * fprompt, int fc, char ** help, char * dflt, char * result, int rlength) #else /* CK_ANSIC */ uq_file(preface,fprompt,fc,help,dflt,result,rlength) char * preface, * fprompt, ** help, * dflt, * result; int fc, rlength; #endif /* CK_ANSIC */ /* uq_file */ { int rc = -1, x, y, z; char * s, * p, * fullpath; char filebuf[CKMAXPATH+1]; #ifdef CK_RECALL extern int on_recall; #endif /* CK_RECALL */ #ifdef KUI if ( gui_dialog ) { rc = gui_saveas_dialog(preface,fprompt,fc,dflt,result,rlength); return rc; } #endif /* KUI */ #ifdef CK_RECALL on_recall = 0; #endif /* CK_RECALL */ if (preface) /* If prefatory text given... */ printf("%s\n",preface); /* display it. */ cmsavp(psave,PROMPTL); /* Save old prompt */ /* We get the full pathname of the proposed output file just so */ /* we can show it to the user but we don't use it ourselves. */ p = NULL; /* Build new prompt */ if (!dflt) dflt = ""; if (*dflt) /* Have default filename */ zfnqfp(dflt,CKMAXPATH+1,filebuf); /* Get full path */ else ckmakmsg(filebuf,CKMAXPATH+1,zgtdir(),"newfile",NULL,NULL); fullpath = filebuf; x = strlen(fullpath); /* If no prompt given, build one that shows the proposed full pathname. */ if (!fprompt) fprompt = ""; if (!*fprompt) fprompt = x ? " Filename" : " Filename: "; y = strlen(fprompt); if (x > 0) { /* Have default pathname? */ p = (char *)malloc(x + y + 7); /* Get temp storage */ if (p) { /* Build prompt */ ckmakmsg(p,x+y+7,fprompt," [",fullpath,"]: "); fprompt = p; } } cmsetp(fprompt); /* Make new prompt */ if (p) free(p); /* Free temp storage */ cmini(ckxech); /* Initialize parser. */ x = -1; do { prompt(NULL); /* Issue prompt. */ switch (fc) { /* Parse depends on function code */ case 1: /* Input file */ x = cmifi("Name of existing file",dflt,&s,&y,xxstring); rc = 1; break; case 2: /* Directory */ x = cmdir("Directory name",dflt,&s,xxstring); rc = 1; break; case 3: /* New output file */ /* Fall thru... */ case 4: /* Output file - Append allowed */ x = cmofi("Output file specification",dflt,&s,xxstring); rc = (fc == 4) ? 1 : 2; break; default: /* Bad function code */ goto x_uq_file; } if (x < 0) { /* Parse error */ filebuf[0] = NUL; if (x == -4) { /* EOF */ break; } else if (x == -3) /* No answer? */ printf(fc == 2 ? " Please enter a directory name.\n" : " Please enter a filename.\n" ); cmini(ckxech); } else { z = strlen(s); if (z > rlength || ckstrncpy(filebuf,brstrip(s),CKMAXPATH+1) < z) { printf("?Name too long\n"); x = -9; } else x = cmcfm(); /* Get confirmation */ } if (fc == 1 && x > -1 && y > 0) { printf("?Wildcards not allowed\n"); x = -9; } } while (x < 0); /* Continue till done */ x_uq_file: if (x < 0) rc = -1; cmsetp(psave); /* Restore real prompt */ if (rc > 0) ckstrncpy(result,filebuf,rlength); return(rc); } #ifdef CK_PERMS #ifdef UNIX _PROTOTYP( int zsetperm, (char *, int)); /* CHMOD command for UNIX only */ #define CHM_DIR 0 #define CHM_DOT 1 #define CHM_FIL 2 #define CHM_LIS 3 #define CHM_NOL 4 #define CHM_QUI 5 #define CHM_REC 6 #define CHM_VRB 7 #define CHM_PAG 8 #define CHM_NOP 9 #define CHM_TYP 10 #define CHM_SIM 11 static struct keytab uchmodsw[] = { "/directories", CHM_DIR, 0, "/dotfiles", CHM_DOT, 0, "/files", CHM_FIL, 0, "/list", CHM_LIS, 0, "/nolist", CHM_NOL, 0, "/nopage", CHM_NOP, 0, "/page", CHM_PAG, 0, "/quiet", CHM_QUI, CM_INV, "/recursive", CHM_REC, 0, "/simulate", CHM_SIM, 0, "/type", CHM_TYP, CM_ARG, "/verbose", CHM_VRB, CM_INV, }; static int nchmodsw = (sizeof(uchmodsw) / sizeof(struct keytab)); int douchmod() { extern int recursive, nscanfile, diractive; #ifdef CK_TTGWSIZ extern int tt_rows, tt_cols; int n = 0; #endif /* CK_TTGWSIZ */ int i, files = 1, t1 = 1, t2 = 0, x, y, z, verbose = 0, rc = 1, paging; int xmode = -1, fs = 0, getval = 0, simulate = 0, wild = 0; char c, * s; struct FDB sw, nu; if (xaskmore < 0) { #ifdef CK_TTGWSIZ xaskmore = 1; #else xaskmore = 0; #endif /* CK_TTGWSIZ */ } paging = xaskmore; cmfdbi(&sw, /* First FDB - command switches */ _CMKEY, /* fcode */ "Octal file permission code, or switch", "", /* default */ "", /* addtl string data */ nchmodsw, /* addtl numeric data 1: tbl size */ 4, /* addtl numeric data 2: 4 = cmswi */ xxstring, /* Processing function */ uchmodsw, /* Keyword table */ &nu /* Pointer to next FDB */ ); cmfdbi(&nu, _CMNUM, /* Number */ "", /* Help message */ "", /* Default */ "", /* N/A */ 8, /* Radix = 8 */ 0, /* N/A */ xxstring, /* Processing function */ NULL, /* N/A */ NULL /* Next */ ); while (1) { if ((x = cmfdb(&sw)) < 0) { if (x == -3) { x = -9; printf("?Filename required\n"); } return(x); } if (cmresult.fcode != _CMKEY) break; c = cmgbrk(); getval = (c == ':' || c == '='); if (getval && !(cmgkwflgs() & CM_ARG)) { printf("?This switch does not take an argument\n"); return(-9); } if (!getval && (cmgkwflgs() & CM_ARG)) { printf("?This switch requires an argument\n"); return(-9); } switch (cmresult.nresult) { case CHM_DIR: t1 = 1; t2 = 1; break; case CHM_DOT: matchdot = 1; break; case CHM_FIL: t1 = 0; t2 = 0; break; case CHM_LIS: case CHM_VRB: verbose = 1; break; case CHM_NOL: case CHM_QUI: verbose = 0; break; case CHM_REC: recursive = 1; break; case CHM_PAG: verbose = 1; paging = 1; break; case CHM_NOP: paging = 0; break; case CHM_SIM: simulate = 1; break; case CHM_TYP: { extern struct keytab txtbin[]; if ((x = cmkey(txtbin,3,"","",xxstring)) < 0) return(x); if (x == 2) { /* ALL */ xmode = -1; } else { /* TEXT or BINARY only */ xmode = x; fs = 1; } break; } } } z = cmresult.nresult; x = cmifi2("File specification","",&s,&wild,t1,NULL,xxstring,t2); if (x < 0) { if (x == -3) { printf("?A file specification is required\n"); return(-9); } else return(x); } ckstrncpy(tmpbuf,s,TMPBUFSIZ); s = tmpbuf; if ((x = cmcfm()) < 0) return(x); #ifdef ZXREWIND if (wild) files = zxrewind(); #else if (wild) files = nzxpand(s,0); #endif /* ZXREWIND */ if (paging > -1) xaskmore = paging; #ifdef CK_TTGWSIZ if (verbose && paging) { #ifdef OS2 ttgcwsz(); #else /* OS2 */ if (ttgwsiz() > 0) { if (tt_rows > 0 && tt_cols > 0) { cmd_rows = tt_rows; cmd_cols = tt_cols; } } #endif /* OS2 */ } #endif /* CK_TTGWSIZ */ for (i = 0; i < files; i++) { if (files == 1 && wild == 0) { /* For "chmod 777 ." */ ckstrncpy(line,s,LINBUFSIZ); } else { x = znext(line); if (x < 1) { if (i == 0) { printf("?No files match - \"%s\"\n",line); return(-9); } return(1); } } if (fs) { #ifdef VMSORUNIX /* If /TYPE:TEXT or BINARY given, skip directories and links */ /* since they are neither text nor binary. */ extern int zgfs_dir, zgfs_link; zgetfs(line); if (zgfs_dir || zgfs_link) continue; #else if (zchki(line) < 0) continue; #endif /* VMSORUNIX */ /* Regular file, scan it */ switch (scanfile(line,&y,nscanfile)) { case FT_BIN: if (xmode != 1) continue; break; case FT_TEXT: case FT_7BIT: case FT_8BIT: #ifdef UNICODE case FT_UTF8: case FT_UCS2: #endif /* UNICODE */ if (xmode != 0) continue; } } if (simulate) { #ifdef UNIX extern int zchkod; /* Unidentified Flying */ int xx = zchkod; /* API Extension... */ zchkod = 1; #endif /* UNIX */ if (zchko(line) < 0) printf("%s - Access denied\n",line); else printf("%s - OK\n",line); #ifdef UNIX zchkod = xx; #endif /* UNIX */ } else { if (zsetperm(line,z) < 1) { if (verbose || files < 2) { printf("%s: %s\n",line,ck_errstr()); } rc = 0; } else if (verbose) { printf("%s %s\n",ziperm(line),line); } } #ifdef CK_TTGWSIZ if (verbose && paging) { /* Pause at end of screen */ if (cmd_rows > 0 && cmd_cols > 0) { if (++n > cmd_rows - 3) { if (!askmore()) break; else n = 0; } } } #endif /* CK_TTGWSIZ */ } return(success = rc); } #endif /* UNIX */ #endif /* CK_PERMS */ #ifndef NOSPL /* S-Expressions */ #ifndef NOSEXP struct keytab sexptab[] = { "depth-limit", 1, 0, "echo-result", 0, 0, "truncate-all-results", 2 }; static int sexpmaxdep = 1000; /* Maximum depth */ #define xxfloat(s,x) \ ((isdigit(*s)||(*s=='-')||(*s=='+')||(*s=='.')||(*s=='\040'))?isfloat(s,x):0) #define SX_ADD 1 /* Symbols for built-in operators */ #define SX_SUB 2 #define SX_MUL 3 #define SX_DIV 4 #define SX_POW 5 #define SX_SET 6 #define SX_MOD 7 #define SX_EVA 8 #define SX_EXP 9 #define SX_AEQ 10 #define SX_ALT 11 #define SX_AGT 12 #define SX_ALE 13 #define SX_AGE 14 #define SX_MIN 15 #define SX_MAX 16 #define SX_SQR 17 #define SX_FLR 18 #define SX_CEI 19 #define SX_TRU 20 #define SX_ABS 21 #define SX_ROU 22 #define SX_LET 23 #define SX_LGN 24 #define SX_LGX 25 #define SX_FLO 26 #define SX_IFC 27 #define SX_NOT 28 #define SX_NEQ 29 #define SX_AND 30 #define SX_LOR 31 #define SX_SIN 32 #define SX_COS 33 #define SX_TAN 34 #define SX_BWA 35 #define SX_BWO 36 #define SX_BWX 37 #define SX_BWN 38 #define SX_XOR 39 #define SX_INC 40 #define SX_DEC 41 #define SX_QUO 42 #define SX_STR 43 /* Operator flags */ #define SXF_PRE 256 /* Predicate */ #define SXF_ONE 512 /* Requires one arg */ #define SXF_TWO 1024 /* Requires two args or more */ #define SXF_FLO 2048 /* Coerce to floating-point */ /* Built-in constants */ #define SXC_NIL 1 /* NIL */ #define SXC_PI 2 /* PI */ #define SXC_T 3 /* T */ /* This is an xlookup() table and so need not be in "alhabetical" order. Therefore entries are arranged to minimize search for most common operators. */ static struct keytab sexpops[] = { /* Built-in operators */ "setq", SX_SET, 0, /* Global assignment */ "+", SX_ADD, 0, /* Simple arithmetic */ "-", SX_SUB, 0, "*", SX_MUL, 0, "/", SX_DIV, SXF_TWO, "^", SX_POW, SXF_TWO, "if", SX_IFC, SXF_TWO, /* IF */ "let", SX_LET, 0, /* Local assignment */ "not", SX_NOT, SXF_ONE, /* NOT */ "mod", SX_MOD, SXF_TWO, /* Modulus */ "<", SX_ALT, SXF_PRE, /* Comparisons */ ">", SX_AGT, SXF_PRE, "<=", SX_ALE, SXF_PRE, "=", SX_AEQ, SXF_PRE, ">=", SX_AGE, SXF_PRE, "!=", SX_NEQ, SXF_PRE, "++", SX_INC, SXF_ONE|SXF_TWO, /* Increment */ "--", SX_DEC, SXF_ONE|SXF_TWO, /* Decrement */ "**", SX_POW, SXF_TWO, /* Common synonyms */ "==", SX_AEQ, SXF_PRE, "!", SX_NOT, SXF_ONE, ".", SX_EVA, 0, "and", SX_AND, 0, /* Logical operators */ "or", SX_LOR, 0, "xor", SX_XOR, SXF_TWO, "max", SX_MAX, SXF_ONE|SXF_TWO, /* Max and min */ "min", SX_MIN, SXF_ONE|SXF_TWO, "%", SX_MOD, SXF_TWO, /* More synonyms */ "||", SX_LOR, 0, "&&", SX_AND, 0, "quote", SX_QUO, SXF_ONE, "string", SX_STR, SXF_ONE, "eval", SX_EVA, 0, /* Assorted commands */ "abs", SX_ABS, SXF_ONE, "truncate",SX_TRU, SXF_ONE|SXF_FLO, "round", SX_ROU, SXF_ONE|SXF_TWO|SXF_FLO, "ceiling", SX_CEI, SXF_ONE|SXF_FLO, "floor", SX_FLR, SXF_ONE|SXF_FLO, "float", SX_FLO, SXF_ONE|SXF_FLO, #ifdef FNFLOAT "sqrt", SX_SQR, SXF_ONE|SXF_FLO, /* Floating point functions */ "exp", SX_EXP, SXF_ONE|SXF_FLO, "sin", SX_SIN, SXF_ONE|SXF_FLO, "cos", SX_COS, SXF_ONE|SXF_FLO, "tan", SX_TAN, SXF_ONE|SXF_FLO, "log", SX_LGN, SXF_ONE|SXF_FLO, "log10", SX_LGX, SXF_ONE|SXF_FLO, #endif /* FNFLOAT */ "#", SX_BWX, SXF_TWO, /* Bitwise operators */ "&", SX_BWA, 0, "|", SX_BWO, 0, "~", SX_BWN, SXF_ONE, "", 0, 0 /* (end) */ }; static int nsexpops = (sizeof(sexpops) / sizeof(struct keytab)) - 1; static struct keytab sexpconsts[] = { /* Built-in constants */ "nil", SXC_NIL, 0, /* NIL (false) */ "pi", SXC_PI, 0, /* Pi (3.1415926...) */ "t", SXC_T, 0, /* T (true) */ "", 0, 0 }; static int nsexpconsts = (sizeof(sexpconsts) / sizeof(struct keytab)) - 1; int sexprc = 0; /* S-Expression error flag */ int sexppv = -1; /* Predicate value */ static int sexptrunc = 0; /* Flag to force all results to int */ #define SXMLEN 64 /* Macro arg list initial length */ #include /* Floating-point functions */ _PROTOTYP( char * fpformat, (CKFLOAT, int, int) ); _PROTOTYP( CKFLOAT ckround, (CKFLOAT, int, char *, int) ); extern char math_pi[]; /* Value of Pi */ extern int sexpecho; /* SET SEXPRESSION ECHO value */ extern char * sexpval; /* Last top-level S-Expression value */ extern char * lastsexp; /* Last S-Expression */ int sexprmax = 0; /* Longest result (for stats) */ int sexpdmax = 0; /* Max depth reached (for stats) */ int sexpdep = 0; /* dosexp() recursion depth */ static int * sxrlen = NULL; /* Result stack string sizes */ static char ** sxresult = NULL; /* Result stack */ /* s h o s e x p -- Show S-Expression info */ VOID shosexp() { printf("\n"); printf(" sexpression echo-result: %s\n",showooa(sexpecho)); printf(" sexpression depth-limit: %d\n",sexpmaxdep); printf("\n"); printf(" maximum depth reached: %d\n",sexpdmax); printf(" longest result returned: %d\n",sexprmax); printf("\n"); printf(" truncate all results: %s\n",showoff(sexptrunc)); printf("\n"); printf(" last sexpression: %s\n",lastsexp ? lastsexp : "(none)"); printf(" last value: %s\n",sexpval ? sexpval : "(none)"); printf("\n"); } static char * sexpdebug(s) char * s; { /* For debugging -- includes recursion depth in each debug entry */ static char buf[64]; ckmakmsg(buf,64,"dosexp[",ckitoa(sexpdep),"] ",s); return((char *)buf); } /* d o s e x p -- S-Expression Reader */ /* Returns value as string (empty, numeric, or non-numeric) */ static char sxroundbuf[32]; /* For ROUND result */ char * dosexp(s) char *s; { /* s = S-Expression */ extern struct mtab *mactab; /* Macro table */ extern int maclvl, nmac; extern char *mrval[]; extern int makestrlen; /* (see makestr()) */ struct stringarray * q = NULL; /* cksplit() return type */ char * p[SEXPMAX+1], ** p2; /* List items (must be on stack) */ char * line = NULL; /* For building macro argument list */ int nosplit = 0; int linelen = 0; int linepos = 0; int quote = 0; /* LISP quote flag */ char * s2; /* Workers */ int kw, kwflags, mx = 0, x = 0; int not = 0, truncate = 0, builtin = 0; int fpflag = 0, quit = 0, macro = 0; CK_OFF_T result = 0, i, j, k, n = 0; CKFLOAT fpj, fpresult = 0.0; /* Floating-point results */ int pflag = 0; /* Have predicate */ int presult = 0; /* Predicate result */ int mustfree = 0; /* If we malloc'd we must free */ sexppv = -1; /* Predicate value */ s2 = ""; /* Default return value */ debug(F111,sexpdebug("entry 1"),s,sexprc); if (++sexpdep > sexpmaxdep) { /* Keep track of depth */ printf("?S-Expression depth limit exceeded: %d\n",sexpmaxdep); sexprc++; debug(F111,sexpdebug("max depth exceeded"),s,sexprc); } if (sexpdep > sexpdmax) /* For stats */ sexpdmax = sexpdep; if (sexprc) /* Error, quit all levels */ goto xdosexp; /* Always goto common exit point */ debug(F111,sexpdebug("entry 2"),s,sexprc); if (!s) s = ""; /* Null or empty arg */ while (*s == SP) s++; /* Strip leading spaces */ if (!*s) /* so empty result */ goto xdosexp; /* Allocate result stack upon first use, or after it has been resized with SET SEXP DEPTH-LIMIT. */ if (!sxresult) { sxresult = (char **)malloc(sexpmaxdep * sizeof(char *)); if (!sxresult) { printf("?Memory allocation failure - \"%s\"\n", s); sexprc++; goto xdosexp; } sxrlen = (int *)malloc(sexpmaxdep * sizeof(int)); if (!sxrlen) { printf("?Memory allocation failure - \"%s\"\n", s); sexprc++; goto xdosexp; } for (i = 0; i < sexpmaxdep; i++) { sxresult[i] = NULL; /* Result pointers */ sxrlen[i] = 0; /* Buffer sizes */ } } s2 = s; /* s2 is the result pointer */ k = 0; /* Length accumulator */ if (s[0] == '(') { /* Starts with open paren? */ while (*s2++) k++; /* Get length */ if (s[k-1] == ')') { /* Strip outer parens if any */ s[k-1] = NUL; s++; k -= 2; while (*s == SP) { /* Strip leading spaces from result */ s++; k--; } while (k > 0 && s[k-1] == SP) { /* And trailing spaces. */ s[k-1] = NUL; k--; } } if (!*s) { /* If nothing remains */ s2 = ""; /* return empty result. */ goto xdosexp; } } /* Break result up into "words" (an SEXP counts as a word) */ for (i = 0; i < SEXPMAX+1; i++ ) { /* Clear the operands */ p[i] = NULL; } if (!*(s+1) || !*(s+2)) { /* No need to call cksplit() */ n = 1; /* if it's one or two chars. */ p[1] = s; /* No need to malloc this either. */ nosplit = 1; debug(F101,sexpdebug("nosplit"),"",n); if (s[0] == '(') { /* () empty */ s2 = ""; goto xdosexp; } } else { nosplit = 0; q = cksplit(1,SEXPMAX,s,NULL,"\\%[]&$+-/=*^_@!{}/<>|.#~'`:;?",8,39,0); if (!q) goto xdosexp; n = q->a_size; /* Number of items */ debug(F101,sexpdebug("split"),"",n); if (n < 0 || n > SEXPMAX) { /* Check for too many */ printf("?Too many operands: max = %d\n",SEXPMAX); sexprc++; goto xdosexp; } if (n == 0) /* None, result is NULL, done. */ goto xdosexp; if (n == 1 && s[0] == '(') { /* One but it's another SEXP */ s2 = dosexp(s); goto xdosexp; } p2 = q->a_head; /* Point to result array. */ for (i = 1; i <= n; i++) { /* We must copy it because */ p[i] = NULL; /* recursive calls to dosexp() */ if (p2[i]) /* write over the same array */ makestr(&(p[i]),p2[i]); } if (s[0] == '(') { /* Operator is an S-Expression */ s2 = dosexp(p[1]); /* Replace it by its value */ makestr(&(p[1]),s2); } mustfree++; /* Remember to free it */ } debug(F110,sexpdebug("head"),p[1],0); if (n == 1 && p[1]) { if (*(p[1]) == '\047') { /* Apostrophe = LISP quote character */ s2 = p[1]; goto xdosexp; } } /* This section sidesteps xlookup() of the most common operators. It's not necessary but it speeds up SEXP-heavy loops by about 10%. */ kwflags = 0; if (n > 0) { /* Look up the operator */ s2 = p[1]; /* Prelookup optimization... */ if (!s2) s2 = ""; if (!*s2) goto xdosexp; kw = 0; x = 0; if (isdigit(*s2)) { /* Digit */ x = -2; } else if (isalpha(*s2) && !*(s2+1)) { /* Single letter */ x = -1; } else if (*s2 == 's' || *s2 == 'S') { /* SETQ */ s2++; if (*s2 == 'e' || *s2 == 'E') { s2++; if (*s2 == 't' || *s2 == 'T') { s2++; if (*s2 == 'q' || *s2 == 'Q') { if (!*(s2+1)) { x = SX_SET; kwflags = 0; builtin = 1; } } } } } if (!x) { if (!*(s2+1)) { /* Common single-character ops */ if (*s2 == '+') { x = SX_ADD; kwflags = 0; builtin = 1; } else if (*s2 == '-') { x = SX_SUB; kwflags = 0; builtin = 1; } else if (*s2 == '*') { x = SX_MUL; kwflags = 0; builtin = 1; } else if (*s2 == '/') { x = SX_DIV; kwflags = SXF_TWO; builtin = 1; } } if (!x) { /* None of the above, look it up */ x = xlookup(sexpops,p[1],nsexpops,&kw); if (x > 0) { kwflags = sexpops[kw].flgs; builtin = 1; } } } } /* If none of the above, check built-in constants */ if (x == -1) { x = xlookup(sexpconsts,p[1],nsexpconsts,&kw); if (x > 0) { switch (x) { case SXC_NIL: s2 = ""; goto xdosexp; case SXC_PI: s2 = math_pi; goto xdosexp; case SXC_T: s2 = "1"; goto xdosexp; } } } if (n == 1) { /* Not an expression */ if (builtin) { /* Built-in operand? */ switch (x) { /* Operators with default values */ case SX_EVA: s2 = ""; goto xdosexp; case SX_MUL: /* (*) */ s2 = sexpval ? sexpval : "1"; goto xdosexp; case SX_AND: /* (AND) */ case SX_BWA: /* Bitwise (&) */ result++; case SX_LOR: /* (OR) */ case SX_BWO: /* Bitwise (|) */ case SX_ADD: /* (+) */ case SX_SUB: /* (-) */ s2 = result ? "1" : "0"; goto xdosexp; } } else { /* Not a built-in operand */ char * p1; p1 = p[1]; while (*p1 == SP) p1++; if (!isalpha(*p1)) { if (xxfloat(p1,0) > 0) { /* Is it a number? */ s2 = p1; while (*s2 == '+') s2++; } else if (*p1 == '(') { /* An S-Expression? */ #ifdef COMMENT s2 = dosexp(s2); #else s2 = dosexp(p1); #endif /* COMMENT */ } goto xdosexp; } else if (x < 1) { /* Is it a variable? */ j = mxlook(mactab,p[1],nmac); /* Look it up */ debug(F111,sexpdebug("n==1 mxlook"),p[1],j); s2 = (j > -1) ? mactab[j].mval : ""; if (!s2) s2 = ""; if (xxfloat(s2,0) > 0) /* Macro value is a number */ goto xdosexp; if (j > -1) { /* It's a macro */ mx = j; x = j; /* whose definition is not numeric */ if (*s2 == '(') { /* Is it an S-Expression? */ /* We have to allocate memory on the stack */ /* to call ourselves recursively on it */ /* otherwise we'll wipe out the macro definition */ char * s3 = NULL; /* int k = 0; */ s3 = s2; while (*s3++) k++; s3 = (char *)malloc(k + 4); if (s3) { strcpy(s3,s2); /* SAFE */ s2 = dosexp(s3); /* Evaluate it */ free(s3); } else { printf("?Memory allocation failure - \"%s\"\n",s2); sexprc++; } goto xdosexp; } if (*s2 == '\047') { s2++; #ifdef COMMENT /* Dumps core if petty optimization was taken */ makestr(&(p[1]),s2); #else if (!nosplit && p[1]) free(p[1]); p[1] = (char *)malloc((int)strlen(s2) + 1); #endif /* COMMENT */ s2 = p[1]; if (!s2) s2 = ""; if (*s2 == '(') { if (s2[makestrlen-1] == ')') { s2[makestrlen-1] = NUL; s2++; } } debug(F110,sexpdebug("'A"),s2,0); goto xdosexp; } macro++; /* Not an S-Expression */ } else { /* Not found in macro table */ printf("?Not defined - \"%s\"\n", p[1]); sexprc++; goto xdosexp; } } } } else if (x < 1 && !macro) { /* n > 1 and not a built-in operator */ x = mxlook(mactab,p[1],nmac); /* See if it's a macro */ debug(F111,sexpdebug("n!=1 mxlook"),p[1],x); if (x < 0) { printf("?Invalid operand - \"%s\"\n",p[1]); sexprc++; goto xdosexp; } mx = x; macro++; } if (builtin) { /* Built-in operator... */ if (kwflags) { int flgs; if ((flgs = (kwflags & (SXF_ONE|SXF_TWO)))) { switch (flgs) { case (SXF_ONE|SXF_TWO): if (n < 2) { printf("?Too few operands - \"%s\"\n",s); sexprc++; goto xdosexp; } break; case SXF_TWO: if (n < 3) { printf("?Too few operands - \"%s\"\n",s); sexprc++; goto xdosexp; } break; case SXF_ONE: if (n != 2) { printf("?Too %s operands - \"%s\"\n", (n > 2) ? "many" : "few", s); sexprc++; goto xdosexp; } } } if (kwflags & SXF_PRE) { /* Predicate? */ if (n < 2) { printf("?Too few operands - \"%s\"\n",s); sexprc++; goto xdosexp; } pflag = 1; presult = 1; } if (kwflags & SXF_FLO) /* Operator requires floating point */ fpflag++; /* Force it */ if (x == SX_ROU) { /* ROUND can have 1 or 2 arguments */ if (n < 2 || n > 3) { printf("?Too %s operands - \"%s\"\n", (n > 3) ? "many" : "few", s); sexprc++; goto xdosexp; } } if (x == SX_ROU) { /* But they are not "cumulative" like other SEXP args */ /* So this case is handled specially */ char buf1[32], buf2[32]; float r; char * s0, * s1; char * q0, * q1; s0 = p[2]; if (!s0) s0 = ""; if (!*s0) s0 = "0"; q0 = dosexp(s0); ckstrncpy(buf1,q0,32); q0 = buf1; s1 = p[3]; if (!s1) s1 = ""; if (!*s1) s1 = "0"; q1 = dosexp(s1); if (!q1) q1 = ""; if (!*q1) q1 = "0"; ckstrncpy(buf2,q1,32); q1 = buf2; r = ckround(atof(q0),(int)(atof(q1)),sxroundbuf,31); s2 = sxroundbuf; sexprc = 0; goto xdosexp; } } if (x == SX_SET || x == SX_LET || /* Assignment is special */ x == SX_INC || x == SX_DEC) { int rc; char c, * m, * s3; if (n == 1) { s2 = ""; goto xdosexp; } s2 = NULL; for (i = 1; i < n; i += 2) { /* Loop thru operand pairs */ rc = 0; s3 = p[i+1]; c = *s3; debug(F110,sexpdebug("target p"),s3,0); /* Make sure target doesn't have multiple words */ while (*s3) { if (*s3 < '!') { rc = 1; break; }; s3++; } s3 = p[i+1]; if (rc) { /* If it does it must have been */ char * s4; /* an SEXP so evaluate it */ s3 = dosexp(s3); s4 = s3; rc = 0; while (*s4) { if (*s4 < '!') { rc = 1; break; }; s4++; } if (rc == 0) makestr(&(p[i+1]),s3); } /* And that it's not a number, etc. */ if (rc > 0 || isdigit(c) || c == '(') { printf("?Invalid assignment - \"%s\"\n",s); sexprc++; goto xdosexp; } else if (isalpha(c)) { rc = xlookup(sexpconsts,s3,nsexpconsts,NULL); if (rc > 0) { printf("?Assignment to constant - \"%s\"\n",s); sexprc++; goto xdosexp; } } /* If ++ or --, get current value of variable */ if (x == SX_INC || x == SX_DEC) { int ok = 1; char buf[32]; if (c == CMDQ) { /* A backslash variable */ int n = 32; char * s = buf; buf[0] = NUL; if (zzstring(s3,&s,&n) < 0 || !buf[0]) ok = 0; s2 = buf; } else { /* A macro */ if ((k = mxlook(mactab,s3,nmac)) < 0) ok = 0; else s2 = mactab[k].mval; } if (!ok) { printf("?Not defined - \"%s\"\n",p[i+1]); sexprc++; goto xdosexp; } if (!s2) s2 = ""; k = xxfloat(s2,0); if (k < 1) { printf("?Not numeric - \"%s\"\n",p[i+1]); sexprc++; goto xdosexp; } while (*s2 == '+') s2++; result = ckatofs(s2); fpresult = floatval; if (k > 1 || fpresult != result) fpflag++; } if (n < i+2) { /* Variable with no value */ s2 = ""; if (x == SX_SET || x == SX_LET) { delmac(p[i+1],1); /* Delete the variable */ break; } else { s2 = "1"; } } else { /* Variable with value */ k = xxfloat(p[i+2],0); /* Is it a number? */ if (k > 0) { s2 = p[i+2]; while (*s2 == '+') s2++; } else { s2 = dosexp(p[i+2]); /* Have value, evaluate it */ if (sexprc) goto xdosexp; if (!s2) s2 = ""; if (!*s2 && (x == SX_INC || x == SX_DEC)) continue; } } if (x == SX_INC || x == SX_DEC) { k = xxfloat(s2,0); if (k < 1) { printf("?Not numeric - \"%s\"\n",s2); sexprc++; goto xdosexp; } while (*s2 == '+') s2++; j = ckatofs(s2); if (k > 1) { fpj = floatval; fpflag++; } else { fpj = (CKFLOAT)j; } if (x == SX_INC) { result += j; fpresult += fpj; } else if (x == SX_DEC) { result -= j; fpresult -= fpj; } #ifdef FNFLOAT if (result != fpresult) fpflag++; #endif /* FNFLOAT */ s2 = (fpflag && !sexptrunc) ? fpformat(fpresult,0,0) : ckfstoa(result); } if (x == SX_LET && cmdlvl > 0) /* LET makes var local */ addlocal(p[i+1]); if ((rc = addmac(p[i+1],s2)) < 0) { /* Add the value */ switch (rc) { case -3: m = "Array not declared"; break; case -2: m = "Subscript out of range"; break; case -4: m = "Out of memory"; break; default: m = "Error creating variable"; } printf("?%s - \"%s\"\n",m,s); sexprc++; goto xdosexp; } if (s2) result = ckatofs(s2); } goto xdosexp; } else if (x == SX_IFC) { /* Conditional expression */ int true = 0; if (n > 4) { printf("?Too many operands: IF - \"%s\"\n",s); sexprc++; goto xdosexp; } s2 = dosexp(p[2]); if (sexprc) goto xdosexp; if (s2) { j = ckatofs(s2); if (xxfloat(s2,0) == 2) { fpflag++; fpresult = (CKFLOAT)result; fpj = floatval; } else { fpj = atof(s2); } true = ((fpj != 0.0) ? 1 : 0); } if (!true && n < 4) { s2 = NULL; } else { s2 = dosexp(true ? p[3] : p[4]); if (sexprc) goto xdosexp; j = s2 ? ckatofs(s2) : 0; if (xxfloat(s2,0) == 2) { fpflag++; fpresult = (CKFLOAT)result; fpj = floatval; } else { fpj = s2 ? atof(s2) : 0.0; } fpresult = fpj; result = j; } goto xdosexp; } else if (x == SX_QUO) { #ifndef COMMENT int xx; xx = strlen(p[2]); p[3] = (char *)malloc(xx+4); s2 = p[3]; ckmakmsg(p[3],xx+4,"'(",p[2],")",NULL); n++; #else s2 = p[2]; #endif /* COMMENT */ goto xdosexp; } else if (x == SX_STR) { int xx; s2 = dosexp(p[2]); if (sexprc) goto xdosexp; xx = strlen(s2); p[3] = (char *)malloc(xx+4); ckmakmsg(p[3],xx+4,"'(",s2,")",NULL); s2 = p[3]; n++; goto xdosexp; } } /* Arithmetic operator or macro - Loop thru operands */ quit = 0; /* Short-circuit flag. */ if (macro && n > 1) { /* If operator is a macro */ if (!line) { /* allocate local buffer for */ line = (char *)malloc(SXMLEN); /* the evaluated argument list. */ if (!line) { printf("?Memory allocation failure - \"%s\"\n",p[1]); sexprc++; goto xdosexp; } linelen = SXMLEN; /* debug(F101,"dosexp macro arg buffer","",linelen); */ } linepos = 0; line[linepos] = NUL; } for (i = 1; ((i < n) && !sexprc && !quit); i++) { /* Loop thru operands */ quote = 0; s2 = p[i+1]; /* Get operand */ if (!s2) s2 = ""; #ifdef COMMENT if (*s2 == '\047') { /* Is it quoted? */ debug(F110,sexpdebug("'B"),s2,0); s2++; /* Space past the quote */ quote++; if (*s2 == '(') { /* Quoted S-Expression? */ char c4, * s4 = s2+1; /* Strip outer parens */ while ((c4 = *s4++)) { if (c4 == ')' && !*s4) { s2++; *(s4-1) = NUL; break; } } } debug(F110,sexpdebug("'C"),s2,0); } else { /* Not quoted */ s2 = dosexp(p[i+1]); /* evaluate it */ if (sexprc) goto xdosexp; if (!s2) s2 = ""; if (!macro && x == SX_EVA) continue; } #else if (*s2 != '\047') { /* Is it quoted? */ s2 = dosexp(p[i+1]); /* No, evaluate it */ if (sexprc) goto xdosexp; if (!s2) s2 = ""; if (!macro && x == SX_EVA) continue; } if (*s2 == '\047') { /* Is result quoted? */ debug(F110,sexpdebug("'B"),s2,0); s2++; /* Space past the quote */ quote++; if (*s2 == '(') { /* Quoted S-Expression? */ char c4, * s4 = s2+1; /* Strip outer parens */ while ((c4 = *s4++)) { if (c4 == ')' && !*s4) { s2++; *(s4-1) = NUL; break; } } } debug(F110,sexpdebug("'C"),s2,0); } #endif /* COMMENT */ if (macro) { debug(F111,sexpdebug("macro arg"),s2,i); if (!*s2) quote++; if (!quote) { register char c4, * s4 = s2; while ((c4 = *s4++)) if (c4 == SP) { quote++; break; } } if (quote) line[linepos++] = '{'; while ((line[linepos++] = *s2++)) { if (linepos > linelen - 3) { char * tmp = NULL; line[linepos] = NUL; linelen += SXMLEN; tmp = (char *) malloc(linelen); if (!tmp) { printf("?Memory re-allocation failure - \"%s...\"\n", line); sexprc++; goto xdosexp; } strcpy(tmp,line); free(line); line = tmp; } } linepos--; /* Back up over NUL */ if (quote) line[linepos++] = '}'; /* End quote group */ line[linepos++] = SP; /* add a space */ line[linepos] = NUL; /* and a NUL */ continue; } if (!quote) { /* Built-in operator... */ s2 = dosexp(s2); if (sexprc) goto xdosexp; if (!s2) s2 = ""; } if (x == SX_EVA) continue; if (!*s2) { /* An empty value is not a legal number */ /* but it is a legal truth value */ if (x != SX_AND && x != SX_LOR && x != SX_NOT) { printf("?Not Numeric - \"%s\"\n",p[i+1]); sexprc++; goto xdosexp; } j = 0; fpj = 0.0; } else { j = ckatofs(s2); /* Switch to floating-point upon encountering any f.p. arg */ /* OR... if integer is too big */ if (!fpflag) if (xxfloat(s2,0) == 2) fpflag++; fpj = atof(s2); } if (i == 1) { /* Initial result is first operand */ result = (n == 2 && x == SX_SUB) ? 0-j : j; fpresult = (n == 2 && x == SX_SUB) ? -fpj : fpj; if ((x == SX_AND && result == 0) || /* Short circuit */ (x == SX_LOR && result != 0)) quit++; if (!(kwflags & SXF_ONE)) /* Command w/single arg */ continue; } if (x == SX_MOD || x == SX_DIV) { if (!result) fpflag++; if (!fpj) { printf("?Divide by zero - \"%s\"\n",cmdbuf); sexprc++; goto xdosexp; } } switch (x) { /* Accumulate result */ case SX_EVA: /* EVAL */ result = j; fpresult = fpj; break; case SX_ADD: /* + */ result += j; fpresult += fpj; #ifdef FNFLOAT if (result != fpresult) fpflag++; #endif /* FNFLOAT */ break; case SX_SUB: /* - */ result -= j; fpresult -= fpj; #ifdef FNFLOAT if (result != fpresult) fpflag++; #endif /* FNFLOAT */ break; case SX_MUL: /* * */ result *= j; fpresult *= fpj; #ifdef FNFLOAT if (result != fpresult) fpflag++; #endif /* FNFLOAT */ break; case SX_AND: /* AND */ result = result && j; if (!result) quit++; fpresult = fpresult && fpj; break; case SX_LOR: /* OR */ result = result || j; if (!result) quit++; fpresult = fpresult || fpj; break; case SX_MOD: /* Modulus */ result = result % j; #ifdef FNFLOAT fpresult = (CKFLOAT)fmod(fpresult,fpj); if (result != fpresult) fpflag++; #else fpresult = result; #endif /* FNFLOAT */ break; case SX_DIV: /* / */ if (j) { result /= j; fpresult /= fpj; #ifdef FNFLOAT if (result != fpresult) fpflag++; #endif /* FNFLOAT */ } else { fpresult /= fpj; result = fpj; #ifdef FNFLOAT fpflag++; #endif /* FNFLOAT */ } break; case SX_AEQ: /* Test for equality */ if (fpflag) { if (fpresult != fpj) presult = 0; } else { if (result != j) presult = 0; } break; case SX_NEQ: /* Test for ineqality */ if (fpflag) { if (fpresult == fpj) presult = 0; } else { if (result == j) presult = 0; } break; case SX_ALE: /* Arithmetic less-equal */ if (fpflag) { if (fpj < fpresult) presult = 0; fpresult = fpj; } else { if (j < result) presult = 0; result = j; } break; case SX_ALT: /* Arithmetic less-than */ if (fpflag) { if (fpj <= fpresult) presult = 0; fpresult = fpj; } else { if (j <= result) presult = 0; result = j; } break; case SX_AGT: /* Arithmetic greater-than */ if (fpflag) { if (fpj >= fpresult) presult = 0; fpresult = fpj; } else { if (j >= result) presult = 0; result = j; } break; case SX_AGE: /* Arithmetic greater-equal */ if (fpflag) { if (fpj > fpresult) presult = 0; fpresult = fpj; } else { if (j > result) presult = 0; result = j; } break; case SX_POW: /* Raise to power */ #ifdef FNFLOAT { double dummy; if (!fpj) { fpresult = 1.0; } else if ((!fpresult && fpj <= 0.0)) { printf("?Divide by zero - \"%s\"\n",cmdbuf); sexprc++; goto xdosexp; } else if (fpresult < 0.0 && modf(fpj,&dummy)) { printf("?Domain error - \"%s\"\n",cmdbuf); sexprc++; goto xdosexp; } else { fpresult = (CKFLOAT)pow(fpresult,fpj); } } #endif /* FNFLOAT */ if (j == 0) { result = 1; } else { CK_OFF_T z, sign = 0; if (j < 0) { if (result == 0) { printf("?Divide by zero - \"%s\"\n",cmdbuf); sexprc++; goto xdosexp; } j = 0 - j; sign++; } z = result; while (--j > 0) result *= z; if (sign) result = 1 / result; } #ifdef FNFLOAT if (result != fpresult) fpflag++; #endif /* FNFLOAT */ break; #ifdef FNFLOAT case SX_EXP: /* e to the given power */ fpresult = (CKFLOAT) exp(fpj); break; case SX_LGN: /* Natural log */ case SX_LGX: /* Log base 10 */ case SX_SQR: /* Square root */ if (fpj < 0.0) { printf("?Argument out of range - \"%s\"\n",cmdbuf); sexprc++; goto xdosexp; } if (x == SX_SQR) fpresult = (CKFLOAT) sqrt(fpj); else if (x == SX_LGN) fpresult = (CKFLOAT) log(fpj); else fpresult = (CKFLOAT) log10(fpj); break; case SX_SIN: /* sine */ fpresult = (CKFLOAT) sin(fpj); break; case SX_COS: /* cosine */ fpresult = (CKFLOAT) cos(fpj); break; case SX_TAN: /* tangent */ fpresult = (CKFLOAT) tan(fpj); break; #endif /* FNFLOAT */ case SX_CEI: /* Ceiling */ if (j != fpj) if (fpj > 0.0) fpj += 1.0; fpresult = fpj; fpflag = 1; truncate = 1; break; case SX_FLR: /* Floor */ if (j != fpj) if (fpj < 0.0) fpj -= 1.0; fpresult = fpj; fpflag = 1; truncate = 1; break; case SX_TRU: /* Truncate */ fpresult = fpj; fpflag = 1; truncate = 1; break; case SX_ABS: /* Absolute value */ result = (j < 0) ? 0 - j : j; #ifdef FNFLOAT fpresult = (fpj < 0.0) ? 0.0 - fpj : fpj; if (result != fpresult) fpflag++; #endif /* FNFLOAT */ break; case SX_MAX: /* Max */ if (j != fpj) fpflag++; if (fpflag) { if (fpj > fpresult) fpresult = fpj; } else if (j > result) result = j; break; case SX_MIN: /* Min */ if (j != fpj) fpflag++; if (fpflag) { if (fpj < fpresult) fpresult = fpj; } else if (j < result) result = j; break; case SX_FLO: /* Float */ fpflag++; fpresult = result; fpj = j; break; case SX_NOT: /* NOT (reverse truth value) */ fpflag = 0; not++; break; case SX_BWA: /* Bitwise AND */ fpflag = 0; result &= j; break; case SX_BWO: /* Bitwise OR */ fpflag = 0; result |= j; break; case SX_BWX: /* Bitwise XOR */ case SX_XOR: /* Logical XOR */ if (n > 3) { printf("?Too many operands - \"%s\"\n",s); sexprc++; goto xdosexp; } fpflag = 0; if (x == SX_BWX) { result ^= j; } else { result = (result && !j) || (!result && j); if (result) result = 1; } break; case SX_BWN: /* Bitwise Not */ fpflag = 0; result = ~result; break; default: printf("BAD OP [%s]\n",p[1]); sexprc++; } } if (!pflag) /* Not a predicate */ sexppv = -1; /* So unset this */ /* domacro: */ if (macro) { /* User-defined macro */ extern int fsexpflag; /* (see fneval():ckuus4.c) */ int lookagain = 0; /* Maybe the macro table changed */ if (mactab[mx].kwd) { /* Check and see */ if (ckstrcmp(mactab[mx].kwd,p[1],-1,0)) lookagain++; } else lookagain++; if (lookagain) { /* The table changed */ mx = mxlook(mactab,p[1],nmac); /* Get the macro's new index */ debug(F111,sexpdebug("macro moved"),p[1],mx); if (mx < 0) { /* Yikes! */ printf("?Macro disappeared! - \"%s\"\n",p[1]); sexprc++; goto xdosexp; } } debug(F111,sexpdebug("macro mx"),mactab[mx].kwd,mx); if (fsexpflag) { /* If embedded in a function call */ if (cmpush() > -1) { /* get a new copy of the parsing */ extern int ifc; /* environment, */ int k, ifcsav = ifc; /* save the IF state */ dodo(mx,line,0); /* Set up the macro */ k = parser(1); /* Call the parser to execute it */ cmpop(); /* Pop back to previous level */ ifc = ifcsav; /* restore IF state */ if (k == 0) /* If no error */ s2 = mrval[maclvl+1]; /* get return value, if any */ if (!s2) s2 = ""; debug(F110,sexpdebug("macro return"),s2,0); } else { printf("?Resources exhausted - \"%s\"\n",s); sexprc++; } } else { /* Not embedded in a function call */ dodo(mx,line,0); /* As above but without cmpush/pop() */ k = parser(1); if (k == 0) s2 = mrval[maclvl+1]; if (!s2) s2 = ""; } } else if (pflag) { /* Predicate */ if (not) presult = presult ? 0 : 1; sexppv = presult; /* So set predicate value (0 or 1) */ s2 = presult ? "1" : "0"; } else if (fpflag && !sexptrunc) { /* Result is floating-point */ if (not) fpresult = fpresult ? 0.0 : 1.0; s2 = fpformat(fpresult,0,0); } else if (x != SX_EVA) { if (not) result = result ? 0 : 1; s2 = ckfstoa(result); } /* Common exit point. Always come here to exit. */ xdosexp: if (!s2) s2 = ""; if (!sexprc && s2) { /* Have a result */ char * sx; char * q2 = s2; int xx = 0; if (*s2) { while (*q2++) xx++; /* Get length */ if (xx > sexprmax) /* (stats) */ sexprmax = xx; } else xx = 0; if (xx > sxrlen[sexpdep] || !sxresult[sexpdep]) { int k; k = xx + xx / 4; if (k < 32) k = 32; if (sxresult[sexpdep]) free(sxresult[sexpdep]); if ((sxresult[sexpdep] = (char *)malloc(k))) { sxrlen[sexpdep] = k; } else { printf("?Memory allocation failure - \"%s\"\n",s2); sexprc++; } } sx = sxresult[sexpdep]; /* Point to result buffer */ while ((*sx++ = *s2++)) ; /* copy result. */ if (fpflag && truncate) { /* Floating point + truncate */ sx = sxresult[sexpdep]; /* at decimal point */ for (i = xx - 1; i >= 0; i--) { if (sx[i] == '.') { sx[i] = NUL; if (i == 0) { /* If nothing left */ sx[0] = '0'; /* put a zero. */ sx[1] = NUL; } } } } } if (line) /* If macro arg buffer allocated */ free(line); /* free it. */ if (mustfree) { /* And free local copy of split list */ for (i = 1; i <= n; i++) { if (p[i]) free(p[i]); } } debug(F111,sexpdebug("exit"),sxresult[sexpdep],sexprc); return(sxresult[sexpdep--]); } #endif /* NOSEXP */ #endif /* NOSPL */ int /* CHECK command */ dochk() { int x, y; if ((y = cmkey(ftrtab,nftr,"","",xxstring)) < 0) return(y); ckstrncpy(line,atmbuf,LINBUFSIZ); if ((y = cmcfm()) < 0) return(y); #ifndef NOPUSH if (!ckstrcmp(line,"push",(int)strlen(line),0)) { if (msgflg) /* If at top level... */ printf(" push%s available\n", nopush ? " not" : ""); else if (nopush && !backgrd) printf(" CHECK: push not available\n"); return(success = 1 - nopush); } #endif /* NOPUSH */ #ifdef PIPESEND if (!ckstrcmp(line,"pipes",(int)strlen(line),0)) { if (msgflg) /* If at top level... */ printf(" pipes%s available\n", (nopush || protocol != PROTO_K) ? " not" : ""); else if ((nopush || protocol != PROTO_K) && !backgrd) printf(" CHECK: pipes not available\n"); return(success = 1 - nopush); } #endif /* PIPESEND */ y = lookup(ftrtab,line,nftr,&x); /* Look it up */ debug(F111,"dochk",ftrtab[x].kwd,y); if (msgflg) /* If at top level... */ printf(" %s%s available\n", ftrtab[x].kwd, y ? " not" : ""); else if (y && !backgrd) printf(" CHECK: %s not available\n", ftrtab[x].kwd); return(success = 1 - y); } #ifndef NOLOCAL #ifdef CKLOGDIAL /* Connection log and elapsed-time reporting */ extern char cxlogbuf[]; /* Log record buffer */ extern char diafil[]; /* Log file name */ extern int dialog, cx_active; /* Flags */ static long cx_prev = 0L; /* Elapsed time of previous session */ #endif /* CKLOGDIAL */ #endif /* NOLOCAL */ VOID dologend() { /* Write record to connection log */ #ifdef LOCUS extern int locus, autolocus; #endif /* LOCUS */ #ifndef NOLOCAL #ifdef CKLOGDIAL long d1, d2, t1, t2; char buf[32], * p; #endif /* CKLOGDIAL */ #endif /* NOLOCAL */ #ifdef LOCUS if (autolocus) { int x = locus; #ifdef NEWFTP debug(F101,"dologend ftpisconnected","",ftpisconnected()); setlocus(ftpisconnected() ? 0 : 1, 1); #else setlocus(1,1); #endif /* NEWFTP */ } #endif /* LOCUS */ #ifndef NOLOCAL #ifdef CKLOGDIAL debug(F101,"dologend dialog","",dialog); debug(F101,"dologend cxlogbuf[0]","",cxlogbuf[0]); #ifdef CKSYSLOG debug(F101,"dologend ckxlogging","",ckxlogging); #endif /* CKSYSLOG */ if (!cx_active || !cxlogbuf[0]) /* No active record */ return; cx_active = 0; /* Record is not active */ debug(F111,"dologend cxlogbuf 1",cxlogbuf,cx_active); d1 = mjd((char *)cxlogbuf); /* Get start date of this session */ ckstrncpy(buf,ckdate(),31); /* Get current date */ d2 = mjd(buf); /* Convert them to mjds */ p = cxlogbuf; /* Get start time */ p[11] = NUL; p[14] = NUL; /* Convert to seconds */ t1 = atol(p+9) * 3600L + atol(p+12) * 60L + atol(p+15); p[11] = ':'; p[14] = ':'; p = buf; /* Get end time */ p[11] = NUL; p[14] = NUL; t2 = atol(p+9) * 3600L + atol(p+12) * 60L + atol(p+15); t2 = ((d2 - d1) * 86400L) + (t2 - t1); /* Compute elapsed time */ debug(F101,"dologend t2","",t2); if (t2 > -1L) { cx_prev = t2; p = hhmmss(t2); debug(F110,"dologend hhmmss",p,0); ckstrncat(cxlogbuf,"E=",CXLOGBUFL); /* Append to log record */ ckstrncat(cxlogbuf,p,CXLOGBUFL); debug(F110,"dologend cxlogbuf 2",cxlogbuf,0); } else cx_prev = 0L; debug(F101,"dologend cx_prev","",cx_prev); if (dialog) { /* If logging */ int x; x = diaopn(diafil,1,1); /* Open log in append mode */ debug(F101,"dologend diaopn","",x); x = zsoutl(ZDIFIL,cxlogbuf); /* Write the record */ debug(F101,"dologend zsoutl","",x); x = zclose(ZDIFIL); /* Close the log */ debug(F101,"dologend zclose","",x); } #ifdef CKSYSLOG debug(F101,"dologend ckxlogging","",ckxlogging); if (ckxlogging) { int x; x = ckindex("T=DIAL",cxlogbuf,0,0,1); debug(F111,"dologend ckxsyslog",cxlogbuf,ckxsyslog); debug(F111,"dologend ckindex","T=DIAL",x); if (x > 0) { if (ckxsyslog >= SYSLG_DI) { debug(F110,"dologend syslog",cxlogbuf+18,0); cksyslog(SYSLG_DI,1,"CONNECTION",(char *)(cxlogbuf+18),""); } else if (ckxsyslog >= SYSLG_AC) { debug(F110,"dologend syslog",cxlogbuf+18,0); cksyslog(SYSLG_AC,1,"CONNECTION",(char *)(cxlogbuf+18),""); } } } #endif /* CKSYSLOG */ #endif /* CKLOGDIAL */ #endif /* NOLOCAL */ } #ifndef NOLOCAL #ifdef CKLOGDIAL /* D O L O G S H O W -- Show session/connection info */ /* Call with fc == 1 to show, fc == 0 to only calculate. */ /* Returns session elapsed time in seconds. */ /* If no session active, returns elapsed time of previous session, if any, */ /* otherwise 0 */ long dologshow(fc) int fc; { /* SHOW (current) CONNECTION */ long d1, d2, t1, t2 = 0, prev; char c, buf1[32], buf2[32], * info[32], * p, * s; char * xlogbuf, xbuf[CXLOGBUFL+1]; int i, x = 0, z, ftp = 0, active = 0; #ifdef NEWFTP extern char ftplogbuf[]; extern long ftplogprev; extern int ftplogactive; if (fc & W_FTP) { fc &= 63; ftp = 1; xlogbuf = ftplogbuf; prev = ftplogprev; active = ftplogactive; } else { #endif /* NEWFTP */ ftp = 0; xlogbuf = cxlogbuf; prev = cx_prev; active = cx_active; #ifdef NEWFTP } #endif /* NEWFTP */ debug(F101,"dologshow local","",local); debug(F101,"dologshow ftp","",ftp); debug(F111,"dologshow active",xlogbuf,active); if (!xlogbuf[0]) { if (fc) { if (didsetlin || ftp) printf(" %s: No record.\n", ftp ? "FTP" : "Kermit"); else printf(" %s: No connection.\n", ftp ? "FTP" : "Kermit"); } return(prev); } #ifdef NEWFTP if (ftp) { z = ftpisconnected() ? 1 : -1; } else { #endif /* NEWFTP */ if (local) { /* See if we have an open connection */ z = ttchk(); debug(F101,"dologshow ttchk","",z); z = (z > -1) ? 1 : -2; } else { z = active ? 1 : -2; } #ifdef NEWFTP } #endif /* NEWFTP */ if (z < 0L) { if (!fc) return(prev); else t2 = prev; } /* Note: NOT ckstrncpy! */ strncpy(buf1,xlogbuf,17); /* Copy of just the timestamp */ buf1[17] = NUL; /* Terminate it */ ckstrncpy(xbuf,xlogbuf+18,CXLOGBUFL); /* Copy that can be poked */ debug(F111,"dologshow prev",xbuf,prev); xwords(xbuf,31,info,1); /* Break up into fields */ d1 = mjd(buf1); /* Convert start time to MJD */ ckstrncpy(buf2,ckdate(),31); /* Current date */ d2 = mjd(buf2); /* Convert to MJD */ p = buf1; /* Point to start time */ p[11] = NUL; p[14] = NUL; /* Convert to seconds */ t1 = atol(p+9) * 3600L + atol(p+12) * 60L + atol(p+15); p[11] = ':'; p[14] = ':'; p = buf2; /* Ditto for current time */ p[11] = NUL; p[14] = NUL; if (z > -1L) { t2 = atol(p+9) * 3600L + atol(p+12) * 60L + atol(p+15); t2 = ((d2 - d1) * 86400L) + (t2 - t1); /* Elapsed time so far */ } if (fc) { p = NULL; if (t2 > -1L) /* Convert seconds to hh:mm:ss */ p = hhmmss(t2); if (z > -1) s = "Active"; else if (z == -2) s = "Closed"; else s = "Unknown"; printf("\n"); /* Show results */ printf(" Status: %s\n",s); printf(" Opened: %s\n",buf1); printf(" User: %s\n",info[1] ? info[1] : ""); printf(" PID: %s\n",info[2] ? info[2] : ""); for (i = 3; info[i]; i++) { c = info[i][0]; s = (info[i]) ? info[i]+2 : ""; switch (c) { case 'T': printf(" Type: %s\n", s); break; case 'N': printf(" To: %s\n", s); break; case 'P': printf(" Port: %s\n", s); break; case 'H': printf(" From: %s\n", s); break; case 'D': printf(" Device: %s\n", s); break; case 'O': printf(" Origin: %s\n", s); break; case 'E': break; default: printf(" %s\n",info[i] ? info[i] : ""); } } if (z < 0L) printf(" Elapsed time: %s\n", hhmmss(t2)); else printf(" Elapsed time: %s\n", p ? p : "(unknown)"); x = 0; #ifdef NETCONN #ifdef SSHBUILTIN if ( IS_SSH() ) x++; #endif /* SSHBUILTIN */ #ifdef CK_ENCRYPTION if (ck_tn_encrypting() && ck_tn_decrypting()) x++; #endif /* CK_ENCRYPTION */ #ifdef CK_SSL if (tls_active_flag || ssl_active_flag) x++; #endif /* CK_SSL */ #ifdef RLOGCODE #ifdef CK_KERBEROS #ifdef CK_ENCRYPTION if (ttnproto == NP_EK4LOGIN || ttnproto == NP_EK5LOGIN) x++; #endif /* CK_ENCRYPTION */ #endif /* CK_KERBEROS */ #endif /* RLOGCODE */ #endif /* NETCONN */ if (z > 0) printf(" Encrypted: %s\n", x ? "Yes" : "No"); printf(" Log: %s\n", dialog ? diafil : "(none)"); printf("\n"); } return(t2 > -1L ? t2 : 0L); } VOID dologline() { char * p; int n, m = 0; dologend(); /* Previous session not closed out? */ cx_active = 1; /* Record is active */ cx_prev = 0L; p = ckdate(); /* Get timestamp */ n = ckstrncpy(cxlogbuf,p,CXLOGBUFL-1); /* Start record off with it */ if (!uidbuf[0]) { debug(F100,"dologline uidbuf empty","",0); #ifdef UNIX /* Who has whoami()... */ ckstrncpy(uidbuf,(char *)whoami(),UIDBUFLEN); #else #ifdef STRATUS ckstrncpy(uidbuf,(char *)whoami(),UIDBUFLEN); #else ckstrncpy(uidbuf,"UNKNOWN",UIDBUFLEN); #endif /* STRATUS */ #endif /* UNIX */ } m = strlen(uidbuf) + strlen(myhost) + strlen(ttname) + 32; if (n+m < CXLOGBUFL-1) { /* Add serial device info */ p = cxlogbuf+n; sprintf(p," %s %s T=SERIAL H=%s D=%s ", /* SAFE */ uidbuf, ckgetpid(), myhost, ttname ); } else ckstrncpy(cxlogbuf,"LOGLINE BUFFER OVERFLOW",CXLOGBUFL); debug(F110,"dologline",cxlogbuf,0); } #ifdef NETCONN VOID dolognet() { char * p, * s = "NET", * uu = uidbuf; char * port = ""; int n, m, tcp = 0; char * h = NULL; dologend(); /* Previous session not closed out? */ cx_prev = 0L; cx_active = 1; /* Record is active */ p = ckdate(); n = ckstrncpy(cxlogbuf,p,CXLOGBUFL); #ifdef TCPSOCKET if (nettype == NET_TCPB || nettype == NET_TCPA) { tcp++; s = "TCP"; } else if (nettype == NET_SSH) { s = "SSH"; tcp++; } #endif /* TCPSOCKET */ #ifdef ANYX25 if (nettype == NET_SX25 || nettype == NET_VX25 || nettype == NET_IX25) s = "X25"; #endif /* ANYX25 */ #ifdef DECNET if (nettype == NET_DEC) s = "DECNET"; #endif /* DECNET */ #ifdef SUPERLAT if (nettype == NET_SLAT) s = "SUPERLAT"; #endif /* SUPERLAT */ #ifdef CK_NETBIOS if (nettype == NET_BIOS) s = "NETBIOS"; #endif /* CK_NETBIOS */ if (!uu[0]) { debug(F100,"dolognet uidbuf empty","",0); #ifdef OS2ORUNIX /* Who has whoami()... */ uu = (char *)whoami(); #else #ifdef STRATUS uu = (char *)whoami(); #else uu = "UNKNOWN"; #endif /* STRATUS */ #endif /* UNIX */ } #ifdef TCPSOCKET if (tcp) { int k; makestr(&h,myhost); if ((k = ckindex(":",h,0,0,0)) > 0) { h[k-1] = NUL; port = &h[k]; } else { int svcnum = gettcpport(); if (svcnum > 0) port = ckitoa(svcnum); else port = "unk"; } } #endif /* TCPSOCKET */ m = strlen(uu) + strlen(myhost) + strlen(ttname) + strlen(s) + 32; if (n+m < CXLOGBUFL-1) { /* SAFE */ p = cxlogbuf+n; sprintf(p," %s %s T=%s N=%s H=%s P=%s ", uu, ckgetpid(), s, ttname, myhost, port ); } else ckstrncpy(cxlogbuf,"LOGNET BUFFER OVERFLOW",CXLOGBUFL); debug(F110,"dolognet cxlogbuf",cxlogbuf,0); if (h) makestr(&h,NULL); } #endif /* NETCONN */ #endif /* CKLOGDIAL */ #ifndef NODIAL /* Parse a DIAL-related string, stripping enclosing braces, if any. */ static int dialstr(p,msg) char **p; char *msg; { int x; char *s; if ((x = cmtxt(msg, "", &s, xxstring)) < 0) return(x); s = brstrip(s); /* Strip braces around. */ debug(F110,"dialstr",s,0); makestr(p,*s?s:NULL); return(success = 1); } VOID initmdm(x) int x; { MDMINF * p; int m; mdmtyp = x; /* Set global modem type */ debug(F101,"initmdm mdmtyp","",mdmtyp); debug(F101,"initmdm usermdm","",usermdm); if (x < 1) return; m = usermdm ? usermdm : mdmtyp; p = modemp[m]; /* Point to modem info struct, and */ /* debug(F101,"initmdm p","",p); */ if (p) { dialec = p->capas & CKD_EC; /* set DIAL ERROR-CORRECTION, */ dialdc = p->capas & CKD_DC; /* DIAL DATA-COMPRESSION, and */ mdmspd = p->capas & CKD_SB ? 0 : 1; /* DIAL SPEED-MATCHING from it. */ dialfc = FLO_AUTO; /* Modem's local flow control.. */ dialmax = p->max_speed; dialcapas = p->capas; dialesc = p->esc_char; } else if (mdmtyp > 0) { printf("WARNING: modem info for \"%s\" not filled in yet\n", gmdmtyp() ); } /* Reset or set the SET DIAL STRING items ... */ #ifdef DEBUG if (deblog) { debug(F110,"initmdm dialini",dialini,0); debug(F110,"initmdm dialmstr ",dialmstr,0); debug(F110,"initmdm dialmprmt",dialmprmt,0); debug(F110,"initmdm dialcmd",dialcmd,0); debug(F110,"initmdm dialdcon",dialdcon,0); debug(F110,"initmdm dialdcoff",dialdcoff,0); debug(F110,"initmdm dialecon",dialecon,0); debug(F110,"initmdm dialecoff",dialecoff,0); debug(F110,"initmdm dialhcmd",dialhcmd,0); debug(F110,"initmdm dialhwfc",dialhwfc,0); debug(F110,"initmdm dialswfc",dialswfc,0); debug(F110,"initmdm dialnofc",dialnofc,0); debug(F110,"initmdm dialtone",dialtone,0); debug(F110,"initmdm dialpulse",dialpulse,0); debug(F110,"initmdm dialname",dialname,0); debug(F110,"initmdm dialaaon",dialaaon,0); debug(F110,"initmdm dialaaoff",dialaaoff,0); debug(F110,"initmdm dialx3",dialx3,0); debug(F110,"initmdm dialspon",dialspon,0); debug(F110,"initmdm dialspoff",dialspoff,0); debug(F110,"initmdm dialvol1",dialvol1,0); debug(F110,"initmdm dialvol2",dialvol2,0); debug(F110,"initmdm dialvol3",dialvol3,0); debug(F110,"initmdm dialini2",dialini2,0); } #endif /* DEBUG */ if (usermdm && p) { /* USER-DEFINED: copy info from specified template */ makestr(&dialini ,p->wake_str); makestr(&dialmstr ,p->dmode_str); makestr(&dialmprmt,p->dmode_prompt); makestr(&dialcmd ,p->dial_str); makestr(&dialdcon ,p->dc_on_str); makestr(&dialdcoff,p->dc_off_str); makestr(&dialecon ,p->ec_on_str); makestr(&dialecoff,p->ec_off_str); makestr(&dialhcmd ,p->hup_str); makestr(&dialhwfc ,p->hwfc_str); makestr(&dialswfc ,p->swfc_str); makestr(&dialnofc ,p->nofc_str); makestr(&dialtone ,p->tone); makestr(&dialpulse,p->pulse); makestr(&dialname ,"This space available (use SET MODEM NAME)"); makestr(&dialaaon ,p->aa_on_str); makestr(&dialaaoff,p->aa_off_str); makestr(&dialx3 ,p->ignoredt); makestr(&dialspon ,p->sp_on_str); makestr(&dialspoff,p->sp_off_str); makestr(&dialvol1 ,p->vol1_str); makestr(&dialvol2 ,p->vol2_str); makestr(&dialvol3 ,p->vol3_str); makestr(&dialini2 ,p->ini2); } else { /* Not user-defined, so wipe out overrides */ if (dialini) makestr(&dialini,NULL); /* Init-string */ if (dialmstr) makestr(&dialmstr,NULL); /* Dial-mode-str */ if (dialmprmt) makestr(&dialmprmt,NULL); /* Dial-mode-pro */ if (dialcmd) makestr(&dialcmd,NULL); /* Dial-command */ if (dialdcon) makestr(&dialdcon,NULL); /* DC ON command */ if (dialdcoff) makestr(&dialdcoff,NULL); /* DC OFF command */ if (dialecon) makestr(&dialecon,NULL); /* EC ON command */ if (dialecoff) makestr(&dialecoff,NULL); /* EC OFF command */ if (dialhcmd) makestr(&dialhcmd,NULL); /* Hangup command */ if (dialhwfc) makestr(&dialhwfc,NULL); /* Flow control... */ if (dialswfc) makestr(&dialswfc,NULL); /* */ if (dialnofc) makestr(&dialnofc,NULL); /* */ if (dialtone) makestr(&dialtone,NULL); /* Dialing method */ if (dialpulse) makestr(&dialpulse,NULL); /* */ if (dialname) makestr(&dialname,NULL); /* Modem name */ if (dialaaon) makestr(&dialaaon,NULL); /* Autoanswer On */ if (dialaaoff) makestr(&dialaaoff,NULL); /* Autoanswer Off */ if (dialx3) makestr(&dialx3,NULL); /* Ignore dialtone */ if (dialspon) makestr(&dialspon,NULL); /* Speaker On */ if (dialspoff) makestr(&dialspoff,NULL); /* Speaker Off */ if (dialvol1) makestr(&dialvol1,NULL); /* Low volume */ if (dialvol2) makestr(&dialvol2,NULL); /* Medium volume */ if (dialvol3) makestr(&dialvol3,NULL); /* High volume */ if (dialini2) makestr(&dialini2,NULL); /* Init string 2 */ } if (autoflow) /* Maybe change flow control */ setflow(); #ifndef MINIDIAL #ifdef OLDTBCODE tbmodel = 0; /* If it's a Telebit, we don't know the model yet */ #endif /* OLDTBCODE */ #endif /* MINIDIAL */ } #ifdef COMMENT /* Not implemented yet */ int setanswer() { int x, y; extern int ans_cid, ans_ring; if ((x = cmkey(answertab,nanswertab,"","",xxstring)) < 0) return(x); switch (x) { case XYA_CID: return(seton(&ans_cid)); case XYA_RNG: y = cmnum("How many rings before answering","1",10,&x,xxstring); y = setnum(&ans_rings,x,y,254); return(y); } } #endif /* COMMENT */ int setmodem() { /* SET MODEM */ int x, y, z; long zz; struct FDB k1, k2; extern int mdmset; cmfdbi(&k1,_CMKEY, "Modem parameter","","",nsetmdm, 0, xxstring, setmdm, &k2); cmfdbi(&k2,_CMKEY,"","","",nmdm,0,xxstring,mdmtab,NULL); x = cmfdb(&k1); if (x < 0) { /* Error */ if (x == -2 || x == -9) printf("?No keywords match: \"%s\"\n",atmbuf); return(x); } y = cmresult.nresult; /* Keyword value */ if (cmresult.fdbaddr == &k2) { /* Modem-type keyword table */ if ((x = cmcfm()) < 0) return(x); usermdm = 0; initmdm(cmresult.nresult); /* Set the modem type. */ return(success = 1); /* Done */ } switch (cmresult.nresult) { /* SET MODEM keyword table. */ #ifdef MDMHUP case XYDMHU: /* DIAL MODEM-HANGUP */ if ((y = cmkey(mdmhang,4,"how to hang up modem", "modem-command", xxstring)) < 0) return(y); if ((x = cmcfm()) < 0) return(x); dialmhu = y; #ifdef COMMENT /* Nope, I fixed it (2001 11 08) */ #ifdef CK_SCOV5 if (dialmhu == 0 && !quiet) { printf( "\n WARNING: RS-232 signal sampling and manipulation do not work\n" ); printf( " in the standard SCO OSR5 serial i/o drivers. SET MODEM HANGUP-METHOD\n" ); printf( " MODEM-COMMAND is recommended for OSR5.\n\n" ); } #endif /* CK_SCOV5 */ #endif /* COMMENT */ return(success = 1); #endif /* MDMHUP */ case XYDCAP: zz = 0L; y = 0; while (y != -3) { if ((y = cmkey(mdmcap,nmdmcap, "capability of modem", "", xxstring)) < 0) { if (y == -3) break; else return(y); } zz |= y; } if ((x = cmcfm()) < 0) return(x); dialcapas = zz; debug(F101,"setmodem autoflow","",autoflow); debug(F101,"setmodem flow 1","",flow); if (autoflow) /* Maybe change flow control */ setflow(); debug(F101,"setmodem flow 2","",flow); mdmspd = zz & CKD_SB ? 0 : 1; /* Set MODEM SPEED-MATCHING from it. */ return(success = 1); case XYDMAX: #ifdef TN_COMPORT if (network && istncomport()) x = cmkey(tnspdtab,ntnspd,line,"",xxstring); else #endif /* TN_COMPORT */ x = cmkey(spdtab,nspd,line,"",xxstring); if (x < 0) { if (x == -3) printf("?value required\n"); return(x); } if ((y = cmcfm()) < 0) return(y); dialmax = (long) x * 10L; if (dialmax == 70) dialmax = 75; return(success = 1); case XYDSTR: /* These moved from SET DIAL */ case XYDDC: case XYDEC: case XYDESC: case XYDFC: case XYDKSP: case XYDSPD: case XYDDIA: return(setdial(x)); case XYDTYP: if ((y = cmkey(mdmtab,nmdm,"modem type","none", xxstring)) < 0) return(y); if (y == dialudt) { /* User-defined modem type */ if ((x = cmkey(mdmtab,nmdm,"based on existing modem type", "unknown", xxstring)) < 0) return(x); } if ((z = cmcfm()) < 0) return(z); usermdm = 0; usermdm = (y == dialudt) ? x : 0; initmdm(y); mdmset = (mdmtyp > 0); return(success = 1); case XYDNAM: return(dialstr(&dialname,"Descriptive name for modem")); case XYDMCD: /* SET MODEM CARRIER-WATCH */ return(setdcd()); case XYDSPK: /* SET MODEM SPEAKER */ return(seton(&mdmspk)); case XYDVOL: /* SET MODEM VOLUME */ if ((x = cmkey(voltab,3,"","medium",xxstring)) < 0) return(x); if ((y = cmcfm()) < 0) return(y); mdmvol = x; return(success = 1); default: printf("Unexpected SET MODEM parameter\n"); return(-9); } } static int /* Set DIAL command options */ setdial(y) int y; { int x = 0, z = 0; char *s = NULL; if (y < 0) if ((y = cmkey(dialtab,ndial,"","",xxstring)) < 0) return(y); switch (y) { case XYDHUP: /* DIAL HANGUP */ return(seton(&dialhng)); case XYDINI: /* DIAL INIT-STRING */ return(dialstr(&dialini,"Modem initialization string")); case XYDNPR: /* DIAL PREFIX */ return(dialstr(&dialnpr,"Telephone number prefix")); case XYDDIA: /* DIAL DIAL-COMMAND */ x = cmtxt("Dialing command for modem,\n\ include \"%s\" to stand for phone number,\n\ for example, \"set dial dial-command ATDT%s\\13\"", "", &s, xxstring); if (x < 0 && x != -3) /* Handle parse errors */ return(x); s = brstrip(s); /* Strip braces or quotes */ y = x = strlen(s); /* Get length of text */ if (y > 0) { /* If there is any text (left), */ for (x = 0; x < y; x++) { /* make sure they included "%s" */ if (s[x] != '%') continue; if (s[x+1] == 's') break; } if (x == y) { printf( "?Dial-command must contain \"%cs\" for phone number.\n",'%'); return(-9); } } if (dialcmd) { /* Free any previous string. */ free(dialcmd); dialcmd = (char *) 0; } if (y > 0) { dialcmd = malloc(y + 1); /* Allocate space for it */ if (dialcmd) strcpy(dialcmd,s); /* and make a safe copy. */ } return(success = 1); #ifndef NOXFER case XYDKSP: /* DIAL KERMIT-SPOOF */ return(seton(&dialksp)); #endif /* NOXFER */ case XYDTMO: /* DIAL TIMEOUT */ y = cmnum("Seconds to wait for call completion","0",10,&x,xxstring); if (y < 0) return(y); y = cmnum("Kermit/modem timeout differential","10",10,&z,xxstring); if (y < 0) return(y); if ((y = cmcfm()) < 0) return(y); dialtmo = x; mdmwaitd = z; case XYDESC: /* DIAL ESCAPE-CHARACTER */ y = cmnum("ASCII value of character to escape back to modem", "43",10,&x,xxstring); y = setnum(&dialesc,x,y,128); if (y > -1 && dialesc < 0) /* No escape character */ dialmhu = 0; /* So no hangup by modem command */ return(y); case XYDDPY: /* DIAL DISPLAY */ return(seton(&dialdpy)); case XYDSPD: /* DIAL SPEED-MATCHING */ /* used to be speed-changing */ if ((y = seton(&mdmspd)) < 0) return(y); #ifdef COMMENT mdmspd = 1 - mdmspd; /* so here we reverse the meaning */ #endif /* COMMENT */ return(success = 1); case XYDMNP: /* DIAL MNP-ENABLE */ case XYDEC: /* DIAL ERROR-CORRECTION */ x = seton(&dialec); if (x > 0) if (!dialec) dialdc = 0; /* OFF also turns off compression */ return(x); case XYDDC: /* DIAL COMPRESSION */ x = seton(&dialdc); if (x > 0) if (dialdc) dialec = 1; /* ON also turns on error correction */ return(x); #ifdef MDMHUP case XYDMHU: /* DIAL MODEM-HANGUP */ return(seton(&dialmhu)); #endif /* MDMHUP */ #ifndef NOSPL case XYDDIR: /* DIAL DIRECTORY (zero or more) */ return(parsdir(0)); /* 0 means DIAL */ #endif /* NOSPL */ case XYDSTR: /* DIAL STRING */ if ((y = cmkey(mdmcmd,nmdmcmd,"","",xxstring)) < 0) return(y); switch (y) { case XYDS_AN: /* Autoanswer ON/OFF */ case XYDS_DC: /* Data compression ON/OFF */ case XYDS_EC: /* Error correction ON/OFF */ if ((x = cmkey(onoff,2,"","on",xxstring)) < 0) return(x); sprintf(tmpbuf,"Modem's command to %sable %s", /* SAFE */ x ? "en" : "dis", (y == XYDS_DC) ? "compression" : ((y == XYDS_EC) ? "error-correction" : "autoanswer") ); if (x) { if (y == XYDS_DC) return(dialstr(&dialdcon,tmpbuf)); else if (y == XYDS_EC) return(dialstr(&dialecon,tmpbuf)); else return(dialstr(&dialaaon,tmpbuf)); } else { if (y == XYDS_DC) return(dialstr(&dialdcoff,tmpbuf)); else if (y == XYDS_EC) return(dialstr(&dialecoff,tmpbuf)); else return(dialstr(&dialaaoff,tmpbuf)); } case XYDS_HU: /* hangup command */ return(dialstr(&dialhcmd,"Modem's hangup command")); case XYDS_HW: /* hwfc */ return(dialstr(&dialhwfc, "Modem's command to enable hardware flow control")); case XYDS_IN: /* init */ return(dialstr(&dialini,"Modem's initialization string")); case XYDS_NF: /* no flow control */ return(dialstr(&dialnofc, "Modem's command to disable local flow control")); case XYDS_PX: /* prefix */ return(dialstr(&dialnpr,"Telephone number prefix for dialing")); case XYDS_SW: /* swfc */ return(dialstr(&dialswfc, "Modem's command to enable local software flow control")); case XYDS_DT: /* tone dialing */ return(dialstr(&dialtone, "Command to configure modem for tone dialing")); case XYDS_DP: /* pulse dialing */ return(dialstr(&dialpulse, "Command to configure modem for pulse dialing")); case XYDS_MS: /* dial mode string */ return(dialstr(&dialmstr, "Command to enter dial mode")); case XYDS_MP: /* dial mode prompt */ return(dialstr(&dialmprmt, "Modem response upon entering dial mode")); case XYDS_SP: /* SPEAKER OFF */ if ((x = cmkey(onoff,2,"","on",xxstring)) < 0) return(x); if (x) return(dialstr(&dialspon,"Command to turn modem speaker on")); else return(dialstr(&dialspoff,"Command to turn modem speaker off")); case XYDS_VO: /* VOLUME LOW */ if ((x = cmkey(voltab,3,"","medium",xxstring)) < 0) return(x); switch (x) { case 0: case 1: return(dialstr(&dialvol1, "Command for low modem speaker volume")); case 2: return(dialstr(&dialvol2, "Command for medium modem speaker volume")); case 3: return(dialstr(&dialvol3, "Command for high modem speaker volume")); default: return(-2); } case XYDS_ID: /* IGNORE-DIALTONE */ return(dialstr(&dialx3, "Command to tell modem to ignore dialtone")); case XYDS_I2: /* PREDIAL-INIT */ return(dialstr(&dialini2, "Command to send to modem just prior to dialing")); default: printf("?Unexpected SET DIAL STRING parameter\n"); } case XYDFC: /* DIAL FLOW-CONTROL */ if ((y = cmkey(dial_fc,4,"","auto",xxstring)) < 0) return(y); if ((x = cmcfm()) < 0) return(x); dialfc = y; return(success = 1); case XYDMTH: { /* DIAL METHOD */ extern int dialmauto; if ((y = cmkey(dial_m,ndial_m,"","default",xxstring)) < 0) return(y); if ((x = cmcfm()) < 0) return(x); if (y == XYDM_A) { /* AUTO */ dialmauto = 1; /* local country code, if known. */ dialmth = XYDM_D; } else { dialmauto = 0; /* use the method specified */ dialmth = y; } return(success = 1); } case XYDRTM: y = cmnum("Number of times to try dialing a number", "1",10,&x,xxstring); z = setnum(&dialrtr,x,y,-1); if (z > -1 && dialrtr < 0) { printf("?Sorry, negative dial retries not valid: %d\n",dialrtr); return(-9); } return(z); case XYDINT: y = cmnum("Seconds to wait between redial attempts", "30",10,&x,xxstring); z = setnum(&dialint,x,y,-1); if (z > -1 && dialint < 0) { printf("?Sorry, negative dial interval not valid: %d\n",dialint); return(-9); } return(z); case XYDLAC: /* DIAL AREA-CODE */ if ((x = dialstr(&diallac,"Area code you are calling from")) < 0) return(x); if (diallac) { if (!rdigits(diallac)) { printf("?Sorry, area code must be numeric\n"); if (*diallac == '(') printf("(please omit the parentheses)\n"); if (*diallac == '/') printf("(no slashes, please)\n"); if (diallac) free(diallac); diallac = NULL; return(-9); } } return(x); case XYDCNF: /* CONFIRMATION */ return(success = seton(&dialcnf)); case XYDCVT: /* CONVERT-DIRECTORY */ if ((y = cmkey(dcnvtab,3,"","ask",xxstring)) < 0) return(y); if ((x = cmcfm()) < 0) return(x); dialcvt = y; return(success = 1); case XYDLCC: /* DIAL COUNTRY-CODE */ x = dialstr(&diallcc,"Country code you are calling from"); if (x < 1) return(x); if (diallcc) { if (!rdigits(diallcc)) { printf("?Sorry, country code must be numeric\n"); if (*diallcc == '+') printf("(please omit the plus sign)\n"); if (diallcc) free(diallcc); diallcc = NULL; return(-9); } if (!strcmp(diallcc,"1")) { /* Set defaults for USA and Canada */ if (!dialldp) /* Long-distance prefix */ makestr(&dialldp,"1"); if (!dialixp) /* International dialing prefix */ makestr(&dialixp,"011"); if (ntollfree == 0) { /* Toll-free area codes */ if ((dialtfc[0] = malloc(4))) { strcpy(dialtfc[0],"800"); /* 1970-something */ ntollfree++; if ((dialtfc[1] = malloc(4))) { strcpy(dialtfc[1],"888"); /* 1996 */ ntollfree++; if ((dialtfc[2] = malloc(4))) { strcpy(dialtfc[2],"877"); /* 5 April 1998 */ ntollfree++; if ((dialtfc[3] = malloc(4))) { strcpy(dialtfc[3],"866"); /* 2000? */ ntollfree++; } } } } } if (!dialtfp) /* Toll-free dialing prefix */ makestr(&dialtfp,"1"); #ifdef COMMENT /* The time for this is past */ } else if (!strcmp(diallcc,"358") && ((int) strcmp(zzndate(),"19961011") > 0) ) { /* Finland */ if (!dialldp) /* Long-distance prefix */ makestr(&dialldp,"9"); if (!dialixp) /* International dialing prefix */ makestr(&dialixp,"990"); #endif /* COMMENT */ } else { /* Everywhere else ... */ if (!dialldp) { if ((dialldp = malloc(4))) strcpy(dialldp,"0"); } if (!dialixp) { if ((dialixp = malloc(4))) strcpy(dialixp,"00"); } } if (!strcmp(diallcc,"33")) /* France */ dialfld = 1; /* Long-distance dialing is forced */ } return(success = 1); case XYDIXP: /* DIAL INTL-PREFIX */ return(dialstr(&dialixp,"International dialing prefix")); case XYDIXS: /* DIAL INTL-SUFFIX */ return(dialstr(&dialixs,"International dialing suffix")); case XYDLDP: /* DIAL LD-PREFIX */ return(dialstr(&dialldp,"Long-distance dialing prefix")); case XYDLDS: /* DIAL LD-SUFFIX */ return(dialstr(&diallds,"Long-distance dialing suffix")); case XYDLCP: /* DIAL LC-PREFIX */ return(dialstr(&diallcp,"Local dialing prefix")); case XYDLCS: /* DIAL LC-SUFFIX */ return(dialstr(&diallcs,"Local dialing suffix")); #ifdef COMMENT case XYDPXX: /* DIAL PBX-EXCHANGE */ return(dialstr(&dialpxx,"Exchange of PBX you are calling from")); #endif /* COMMENT */ case XYDPXI: { /* DIAL PBX-INTERNAL-PREFIX */ #ifdef COMMENT return(dialstr(&dialpxi, "Internal-call prefix of PBX you are calling from")); #else int x; if ((x = cmtxt("Internal-call prefix of PBX you are calling from", "",&s,NULL)) < 0) /* Don't evaluate */ return(x); #ifndef NOSPL if (*s) { char c, * p = tmpbuf; if (*s == '\\') { c = *(s+1); if (isupper(c)) c = tolower(c); if (c != 'f' && ckstrcmp(s,"\\v(d$px)",8,0) && ckstrcmp(s,"\\v(d$pxx)",9,0) && ckstrcmp(s,"\\v(d$p)",7,0)) { x = TMPBUFSIZ; zzstring(s,&p,&x); s = tmpbuf; } } } #endif /* NOSPL */ makestr(&dialpxi,s); return(1); } #endif /* COMMENT */ case XYDPXO: /* DIAL PBX-OUTSIDE-PREFIX */ return(dialstr(&dialpxo, "Outside-line prefix of PBX you are calling from")); case XYDSFX: /* DIAL INTL-SUFFIX */ return(dialstr(&dialsfx," Telephone number suffix for dialing")); case XYDSRT: /* DIAL SORT */ return(success = seton(&dialsrt)); case XYDPXX: /* DIAL PBX-EXCHANGE */ case XYDTFC: { /* DIAL TOLL-FREE-AREA-CODE */ int n, i; /* (zero or more of them...) */ char * p[MAXTOLLFREE]; /* Temporary pointers */ char * m; for (n = 0; n < MAXTOLLFREE; n++) { if (n == 0) { m = (y == XYDTFC) ? "Toll-free area code(s) in the country you are calling from" : "Exchange(s) of PBX you are calling from"; } else { m = (y == XYDTFC) ? "Another toll-free area code" : "Another PBX exchange"; } if ((x = cmfld(m,"",&s,xxstring)) < 0) break; if (s) { int k; k = (int) strlen(s); if (k > 0) { if ((p[n] = malloc(k + 1))) strcpy(p[n], s); /* safe */ } else break; } else break; } if (x == -3) { /* Command was successful */ int m; m = (y == XYDTFC) ? ntollfree : ndialpxx; if ((x = cmcfm()) < 0) return(x); x = 1; for (i = 0; i < m; i++) { /* Remove old list, if any */ if (y == XYDTFC) makestr(&(dialtfc[i]),NULL); else makestr(&(dialpxx[i]),NULL); } if (y == XYDTFC) ntollfree = n; /* New count */ else ndialpxx = n; for (i = 0; i < n; i++) { /* New list */ if (y == XYDTFC) makestr(&(dialtfc[i]),p[i]); else makestr(&(dialpxx[i]),p[i]); } x = 1; } for (i = 0; i < n; i++) if (p[i]) free(p[i]); return(x); } case XYDTFP: /* TOLL-FREE-PREFIX */ return(dialstr(&dialtfp, " Long-distance prefix for toll-free dialing")); case XYDCON: /* CONNECT */ z = -1; if ((y = cmkey(crrtab,ncrr,"","auto",xxstring)) < 0) return(y); if (y != CAR_OFF) /* AUTO or ON? */ if ((z = cmkey(qvtab,nqvt,"","verbose",xxstring)) < 0) return(z); if ((x = cmcfm()) < 0) return(x); if (z > -1) dialcq = z; dialcon = y; return(success = 1); case XYDRSTR: /* RESTRICT */ if ((y = cmkey(drstrtab,4,"","none",xxstring)) < 0) return(y); if ((x = cmcfm()) < 0) return(x); dialrstr = y; return(success = 1); case XYDLLAC: { /* Local area-code list */ int n, i; /* (zero or more of them...) */ char * p[MAXLOCALAC]; /* Temporary pointers */ for (n = 0; n < MAXLOCALAC; n++) { if ((x = cmfld( "Area code to which calls from your area are local", "",&s,xxstring)) < 0) break; if (s) { int k; k = (int) strlen(s); if (k > 0) { if ((p[n] = malloc(k + 1))) strcpy(p[n], s); /* safe */ } else break; } else break; } if (x == -3) { /* Command was successful */ if ((x = cmcfm()) < 0) return(x); for (i = 0; i < nlocalac; i++) /* Remove old list, if any */ if (diallcac[i]) { free(diallcac[i]); diallcac[i] = NULL; } nlocalac = n; /* New count */ for (i = 0; i < nlocalac; i++) /* New list */ diallcac[i] = p[i]; return(success = 1); } else { /* Parse error, undo everything */ for (i = 0; i < n; i++) if (p[i]) free(p[i]); return(x); } } case XYDFLD: return(success = seton(&dialfld)); case XYDIDT: /* DIAL IGNORE-DIALTONE */ return(seton(&dialidt)); case XYDPAC: y = cmnum( "Milliseconds to pause between each character sent to dialer", "",10,&x,xxstring); return(setnum(&dialpace,x,y,9999)); #ifndef NOSPL case XYDMAC: if ((x = cmfld("Name of macro to execute just prior to dialing", "",&s,xxstring)) < 0) { if (x == -3) s = NULL; else return(x); } if (s) { if (!*s) { s = NULL; } else { ckstrncpy(line,s,LINBUFSIZ); s = line; } } if ((x = cmcfm()) < 0) return(x); makestr(&dialmac,s); return(success = 1); #endif /* NOSPL */ case XYDPUCC: /* Pulse country codes */ case XYDTOCC: { /* Tone country codes */ int n, i; char * p[MAXTPCC]; char * m; for (n = 0; n < MAXTPCC; n++) { if (n == 0) { m = (y == XYDPUCC) ? "Country code where Pulse dialing is required" : "Country code where Tone dialing is available"; } else m = "Another country code"; if ((x = cmfld(m,"",&s,xxstring)) < 0) break; if (s) { int k; k = (int) strlen(s); if (k > 0) { if ((p[n] = malloc(k + 1))) strcpy(p[n], s); /* safe */ } else break; } else break; } if (x == -3) { /* Command was successful */ int m; m = (y == XYDPUCC) ? ndialpucc : ndialtocc; if ((x = cmcfm()) < 0) return(x); x = 1; for (i = 0; i < m; i++) { /* Remove old list, if any */ if (y == XYDPUCC) makestr(&(dialpucc[i]),NULL); else makestr(&(dialtocc[i]),NULL); } if (y == XYDPUCC) { ndialpucc = n; /* New count */ } else { ndialtocc = n; } for (i = 0; i < n; i++) { /* New list */ if (y == XYDPUCC) { makestr(&(dialpucc[i]),p[i]); } else { makestr(&(dialtocc[i]),p[i]); } } x = 1; } for (i = 0; i < n; i++) if (p[i]) free(p[i]); return(x); } case XYDTEST: return(seton(&dialtest)); default: printf("?Unexpected SET DIAL parameter\n"); return(-9); } } #ifndef NOSHOW int /* SHOW MODEM */ shomodem() { MDMINF * p; int x, n, mdm; char c; long zz; #ifdef IKSD if (inserver) { printf("Sorry, command disabled\r\n"); return(success = 0); } #endif /* IKSD */ shmdmlin(); printf("\n"); mdm = (mdmtyp > 0) ? mdmtyp : mdmsav; p = (mdm > 0) ? modemp[mdm] : NULL; if (p) { printf(" %s\n\n", dialname ? dialname : p->name); printf(" Modem capabilities: "); zz = dialcapas ? dialcapas : p->capas; if (!zz) { printf(" (none)"); } else { if (zz & CKD_AT) printf(" AT"); if (zz & CKD_V25) printf(" ITU"); if (zz & CKD_SB) printf(" SB"); if (zz & CKD_EC) printf(" EC"); if (zz & CKD_DC) printf(" DC"); if (zz & CKD_HW) printf(" HWFC"); if (zz & CKD_SW) printf(" SWFC"); if (zz & CKD_KS) printf(" KS"); if (zz & CKD_TB) printf(" TB"); } printf("\n Modem carrier-watch: "); if (carrier == CAR_OFF) printf("off\n"); else if (carrier == CAR_ON) printf("on\n"); else if (carrier == CAR_AUT) printf("auto\n"); else printf("unknown\n"); printf(" Modem maximum-speed: "); zz = (dialmax > 0L) ? dialmax : p->max_speed; if (zz > 0) printf("%ld bps\n", zz); else printf("(unknown)\n"); printf(" Modem error-correction: %s\n", dialec ? "on" : "off"); printf(" Modem compression: %s\n", dialdc ? "on" : "off"); printf(" Modem speed-matching: %s", mdmspd ? "on" : "off"); printf(" (interface speed %s)\n", mdmspd ? "changes" : "is locked"); printf(" Modem flow-control: "); if (dialfc == FLO_NONE) printf("none\n"); else if (dialfc == FLO_XONX) printf("xon/xoff\n"); else if (dialfc == FLO_RTSC) printf("rts/cts\n"); else if (dialfc == FLO_AUTO) printf("auto\n"); printf(" Modem hangup-method: %s\n", dialmhu ? "modem-command" : "rs232-signal" ); printf(" Modem speaker: %s\n", showoff(mdmspk)); printf(" Modem volume: %s\n", (mdmvol == 2) ? "medium" : ((mdmvol <= 1) ? "low" : "high")); printf(" Modem kermit-spoof: %s\n", dialksp ? "on" : "off"); c = (char) (x = (dialesc ? dialesc : p->esc_char)); printf(" Modem escape-character: %d", x); if (isprint(c)) printf(" (= \"%c\")",c); printf( "\n\nMODEM COMMANDs (* = set automatically by SET MODEM TYPE):\n\n"); debug(F110,"show dialini",dialini,0); printf(" %c Init-string: ", dialini ? ' ' : '*' ); shods(dialini ? dialini : p->wake_str); printf(" %c Dial-mode-string: ", dialmstr ? ' ' : '*' ); shods(dialmstr ? dialmstr : p->dmode_str); n = local ? 19 : 20; if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" %c Dial-mode-prompt: ", dialmprmt ? ' ' : '*' ); shods(dialmprmt ? dialmprmt : p->dmode_prompt); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" %c Dial-command: ", dialcmd ? ' ' : '*' ); shods(dialcmd ? dialcmd : p->dial_str); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" %c Compression on: ", dialdcon ? ' ' : '*' ); if (!dialdcon) debug(F110,"dialdcon","(null)",0); else debug(F110,"dialdcon",dialdcon,0); shods(dialdcon ? dialdcon : p->dc_on_str); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" %c Compression off: ", dialdcoff ? ' ' : '*' ); shods(dialdcoff ? dialdcoff : p->dc_off_str); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" %c Error-correction on: ", dialecon ? ' ' : '*' ); shods(dialecon ? dialecon : p->ec_on_str); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" %c Error-correction off: ", dialecoff ? ' ' : '*' ); shods(dialecoff ? dialecoff : p->ec_off_str); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" %c Autoanswer on: ", dialaaon ? ' ' : '*' ); shods(dialaaon ? dialaaon : p->aa_on_str); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" %c Autoanswer off: ", dialaaoff ? ' ' : '*' ); shods(dialaaoff ? dialaaoff : p->aa_off_str); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" %c Speaker on: ", dialspon ? ' ' : '*' ); shods(dialspon ? dialspon : p->sp_on_str); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" %c Speaker off: ", dialspoff ? ' ' : '*' ); shods(dialspoff ? dialspoff : p->sp_off_str); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" %c Volume low: ", dialvol1 ? ' ' : '*' ); shods(dialvol1 ? dialvol1 : p->vol1_str); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" %c Volume medium: ", dialvol2 ? ' ' : '*' ); shods(dialvol2 ? dialvol2 : p->vol2_str); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" %c Volume high: ", dialvol3 ? ' ' : '*' ); shods(dialvol3 ? dialvol3 : p->vol3_str); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" %c Hangup-command: ", dialhcmd ? ' ' : '*' ); shods(dialhcmd ? dialhcmd : p->hup_str); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" %c Hardware-flow: ", dialhwfc ? ' ' : '*' ); shods(dialhwfc ? dialhwfc : p->hwfc_str); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" %c Software-flow: ", dialswfc ? ' ' : '*' ); shods(dialswfc ? dialswfc : p->swfc_str); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" %c No-flow-control: ", dialnofc ? ' ' : '*' ); shods(dialnofc ? dialnofc : p->nofc_str); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" %c Pulse: ", dialpulse ? ' ' : '*'); shods(dialpulse ? dialpulse : p->pulse); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" %c Tone: ", dialtone ? ' ' : '*'); shods(dialtone ? dialtone : p->tone); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" %c Ignore-dialtone: ", dialx3 ? ' ' : '*'); shods(dialx3 ? dialx3 : p->ignoredt); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" %c Predial-init: ", dialini2 ? ' ' : '*'); shods(dialini2 ? dialini2 : p->ini2); if (++n > cmd_rows - 4) if (!askmore()) return(0); else n = 0; printf("\n For more info: SHOW DIAL and SHOW COMMUNICATIONS\n"); } else if (mdm > 0) { printf("Modem info for \"%s\" not filled in yet\n", gmdmtyp()); } else printf( " No modem selected, so DIAL and most SET MODEM commands have no effect.\n\ Use SET MODEM TYPE to select a modem.\n"); return(success = 1); } #endif /* NOSHOW */ #endif /* NODIAL */ #ifdef CK_TAPI int /* TAPI action commands */ dotapi() { int x,y; char *s; if (!TAPIAvail) { printf("\nTAPI is unavailable on this system.\n"); return(-9); } if ((y = cmkey(tapitab,ntapitab,"MS TAPI command","",xxstring)) < 0) return(y); switch (y) { case XYTAPI_CFG: { /* TAPI CONFIGURE-LINE */ extern struct keytab * tapilinetab; extern struct keytab * _tapilinetab; extern int ntapiline; extern int LineDeviceId; int lineID=LineDeviceId; if (TAPIAvail) cktapiBuildLineTable(&tapilinetab, &_tapilinetab, &ntapiline); if (tapilinetab && _tapilinetab && ntapiline > 0) { int i=0, j = 9999, k = -1; if ( LineDeviceId == -1 ) { /* Find out what the lowest numbered TAPI device is */ /* and use it as the default. */ for (i = 0; i < ntapiline; i++ ) { if (tapilinetab[i].kwval < j) { k = i; } } } else { /* Find the LineDeviceId in the table and use that entry */ for (i = 0; i < ntapiline; i++ ) { if (tapilinetab[i].kwval == LineDeviceId) { k = i; break; } } } if (k >= 0) s = _tapilinetab[k].kwd; else s = ""; if ((y = cmkey(_tapilinetab,ntapiline, "TAPI device name",s,xxstring)) < 0) return(y); lineID = y; } if ((x = cmcfm()) < 0) return(x); #ifdef IKSD if (inserver) { printf("Sorry, command disabled\r\n"); return(success = 0); } #endif /* ISKD */ cktapiConfigureLine(lineID); break; } case XYTAPI_DIAL: /* TAPI DIALING-PROPERTIES */ if ((x = cmcfm()) < 0) return(x); #ifdef IKSD if (inserver) { printf("Sorry, command disabled\r\n"); return(success = 0); } #endif /* ISKD */ cktapiDialingProp(); break; } return(success = 1); } static int /* SET TAPI command options */ settapi() { int x, y; char *s; if (!TAPIAvail) { printf("\nTAPI is unavailable on this system.\n"); return(-9); } if ((y = cmkey(settapitab,nsettapitab,"MS TAPI option","",xxstring)) < 0) return(y); switch (y) { case XYTAPI_USE: return (success = seton(&tapiusecfg)); case XYTAPI_LGHT: return (success = seton(&tapilights)); case XYTAPI_PRE: return (success = seton(&tapipreterm)); case XYTAPI_PST: return (success = seton(&tapipostterm)); case XYTAPI_INA: y = cmnum("seconds of inactivity before auto-disconnect", "0",10,&x,xxstring); return(setnum(&tapiinactivity,x,y,65535)); case XYTAPI_BNG: y = cmnum("seconds to wait for credit card tone", "8",10,&x,xxstring); return(setnum(&tapibong,x,y,90)); case XYTAPI_MAN: return (success = seton(&tapimanual)); case XYTAPI_CON: /* TAPI CONVERSIONS */ return (success = setonaut(&tapiconv)); case XYTAPI_LIN: /* TAPI LINE */ x = setlin(XYTAPI_LIN,1,0); if (x > -1) didsetlin++; return(x); case XYTAPI_PASS: { /* TAPI PASSTHROUGH */ #ifdef NODIAL printf("\n?Modem-dialing not supported\n"); return(-9); #else /* NODIAL */ /* Passthrough became Modem-dialing which is an antonym */ success = seton(&tapipass); tapipass = !tapipass; return (success); #endif /* NODIAL */ } case XYTAPI_LOC: { /* TAPI LOCATION */ extern char tapiloc[]; extern int tapilocid; int i = 0, j = 9999, k = -1; cktapiBuildLocationTable(&tapiloctab, &ntapiloc); if (!tapiloctab || !ntapiloc) { printf("\nNo TAPI Locations are configured for this system\n"); return(-9); } if (tapilocid == -1) tapilocid = cktapiGetCurrentLocationID(); /* Find the current tapiloc entry */ /* and use it as the default. */ for (k = 0; k < ntapiloc; k++) { if (tapiloctab[k].kwval == tapilocid) break; } if (k >= 0 && k < ntapiloc) s = tapiloctab[k].kwd; else s = ""; if ((y = cmkey(tapiloctab,ntapiloc, "TAPI location",s,xxstring)) < 0) return(y); if ((x = cmcfm()) < 0) return(x); #ifdef IKSD if (inserver) { printf("Sorry, command disabled\r\n"); return(success = 0); } #endif /* IKSD */ cktapiFetchLocationInfoByID( y ); #ifndef NODIAL CopyTapiLocationInfoToKermitDialCmd(); #endif /* NODIAL */ } break; } return(success=1); } #endif /* CK_TAPI */ #endif /* NOLOCAL */ #ifndef NOSPL /* Method for evaluating \%x and \&x[] variables */ static struct keytab varevaltab[] = { { "recursive", 1, 0 }, { "simple", 0, 0 } }; static int nvarevaltab = (sizeof(varevaltab) / sizeof(struct keytab)); int setvareval() { int x = 0, y = 0; extern int vareval; #ifdef DCMDBUF extern int * xvarev; #else extern int xvarev[]; #endif /* DCMDBUF */ if ((x = cmkey(varevaltab, nvarevaltab, "Method for evaluating \\%x and \\&x[] variables", "", xxstring)) < 0) return(x); if ((y = cmcfm()) < 0) return(y); xvarev[cmdlvl] = x; vareval = x; return(success = 1); } #ifdef CK_ANSIC /* SET ALARM */ int setalarm(long xx) #else int setalarm(xx) long xx; #endif /* CK_ANSIC */ /* setalarm */ { #ifdef COMMENT int yyyy, mm, dd, x; char *s; long zz; char buf[6]; #endif /* COMMENT */ long sec, jd; char xbuf[20], * p; debug(F101,"setalarm xx","",xx); ck_alarm = 0L; /* 0 = no alarm (in case of error) */ if (xx < 0L) { printf("%ld - illegal value, must be 0 or positive\n", xx); return(-9); } if (xx == 0L) { /* CLEAR ALARM */ alrm_date[0] = NUL; alrm_time[0] = NUL; return(1); } #ifdef COMMENT x = 8; /* Get current date */ s = alrm_date; if (zzstring("\\v(ndate)",&s,&x) < 0) { printf("Internal date error, sorry.\n"); alrm_date[0] = SP; return(-9); } x = 5; /* Get current time */ s = alrm_time; if (zzstring("\\v(ntime)",&s,&x) < 0) { printf("Internal time error, sorry.\n"); alrm_time[0] = SP; return(-9); } sprintf(buf,"%05ld",atol(alrm_time)); /* SAFE (20) */ ckstrncpy(alrm_time,buf,8); debug(F110,"SET ALARM date (1)",alrm_date,0); debug(F110,"SET ALARM time (1)",alrm_time,0); if ((zz = atol(alrm_time) + xx) < 0L) { printf("Internal time conversion error, sorry.\n"); return(-9); } if (zz >= 86400L) { /* Alarm crosses midnight */ char d[10]; /* Local date buffer */ int lastday; /* Last day of this month */ ckstrncpy(d,alrm_date,8); /* We'll have to change the date */ x = (zz / 86400L); /* How many days after today */ dd = atoi((char *)(d+6)); /* Parse yyyymmdd */ d[6] = NUL; /* into yyyy, mm, dd ... */ mm = atoi((char *)(d+4)); d[4] = NUL; yyyy = atoi((char *)d); /* How many days in this month */ lastday = mdays[mm]; if (mm == 2 && yyyy % 4 == 0) /* Works thru 2099 AD... */ lastday++; if (dd + x > lastday) { /* Dumb loop */ int y; x -= (mdays[mm] - dd); /* Deduct rest of this month's days */ /* There's a more elegant way to do this... */ while (1) { mm++; /* Next month */ if (mm > 12) { /* Wrap around */ mm = 1; /* Jan, next year */ yyyy++; } y = mdays[mm]; /* Days in new month */ if (mm == 2 && yyyy % 4 == 0) /* Feb in leap year */ y++; /* Works until 2100 AD */ if (x - y < 1) break; x -= y; } dd = x; /* Day of alarm month */ } else dd += x; sprintf(alrm_date,"%04d%02d%02d",yyyy,mm,dd); /* SAFE (24) */ zz = zz % 86400L; } sprintf(alrm_time,"%ld",zz); /* SAFE (24) */ debug(F110,"SET ALARM date (2)",alrm_date,0); debug(F110,"SET ALARM time (2)",alrm_time,0); ck_alarm = xx; #else /* Jul 1998 */ ckstrncpy(xbuf,ckcvtdate("",1),20); /* Get current date and time */ p = xbuf; ckstrncpy(alrm_date,xbuf,10); alrm_date[8] = NUL; sec = atol(p+9) * 3600L + atol(p+12) * 60L + atol(p+15); debug(F110,"SET ALARM date (1)",alrm_date,0); debug(F101,"SET ALARM time (1)","",sec); if ((sec += xx) < 0L) { printf("Internal time conversion error, sorry.\n"); return(-9); } if (sec >= 86400L) { /* Alarm crosses midnight */ long days; days = sec / 86400L; jd = mjd(p) + days; /* Get modified Julian date */ ckstrncpy(alrm_date,mjd2date(jd),10); sec %= 86400L; } sprintf(alrm_time,"%05ld",sec); /* SAFE (24) */ debug(F110,"SET ALARM date (2)",alrm_date,0); debug(F110,"SET ALARM time (2)",alrm_time,0); ck_alarm = 1; /* Alarm is set */ #endif /* COMMENT */ return(success = 1); } #endif /* NOSPL */ #ifndef NOSETKEY int dosetkey() { /* SET KEY */ int x, y; int flag = 0; int kc; /* Key code */ char *s; /* Key binding */ #ifndef NOKVERBS char *p; /* Worker */ #endif /* NOKVERBS */ #ifdef OS2 extern int os2gks; extern int mskkeys; extern int initvik; #endif /* OS2 */ x_ifnum = 1; y = cmnum("numeric key code, or the word CLEAR,","",10,&kc,xxstring); x_ifnum = 0; if (y < 0) { debug(F111,"SET KEY",atmbuf,y); if (y == -2) { /* Not a valid number */ if ((y = strlen(atmbuf)) < 0) /* Check for SET KEY CLEAR */ return(-2); if (ckstrcmp(atmbuf,"clear",y,0)) return(-2); if ((x = cmcfm()) < 0) return(x); for (y = 0; y < KMSIZE; y++) { keymap[y] = (KEY) y; macrotab[y] = NULL; } #ifdef OS2 keymapinit(); /* Special OS/2 initializations */ initvik = 1; /* Update the VIK table */ #endif /* OS2 */ return(1); } else if (y == -3) { /* SET KEY */ printf(" Press key to be defined: "); /* Prompt for a keystroke */ #ifdef UNIX #ifdef NOSETBUF fflush(stdout); #endif /* NOSETBUF */ #endif /* UNIX */ conbin((char)escape); /* Put terminal in binary mode */ #ifdef OS2 os2gks = 0; /* Turn off Kverb preprocessing */ #endif /* OS2 */ kc = congks(0); /* Get character or scan code */ #ifdef OS2 os2gks = 1; /* Turn on Kverb preprocessing */ #endif /* OS2 */ concb((char)escape); /* Restore terminal to cbreak mode */ if (kc < 0) { /* Check for error */ printf("?Error reading key\n"); return(0); } #ifdef OS2 shokeycode(kc,-1); /* Show current definition */ #else shokeycode(kc); /* Show current definition */ #endif /* OS2 */ flag = 1; /* Remember it's a multiline command */ } else /* Error */ return(y); } /* Normal SET KEY command... */ #ifdef OS2 if (mskkeys) kc = msktock(kc); #endif /* OS2 */ if (kc < 0 || kc >= KMSIZE) { printf("?key code must be between 0 and %d\n", KMSIZE - 1); return(-9); } if (kc == escape) { printf("Sorry, %d is the CONNECT-mode escape character\n",kc); return(-9); } #ifdef OS2 wideresult = -1; #endif /* OS2 */ if (flag) { cmsavp(psave,PROMPTL); cmsetp(" Enter new definition: "); cmini(ckxech); cmflgs = 0; prompt(NULL); } def_again: if (flag) cmres(); if ((y = cmtxt("key definition,\n\ or Ctrl-C to cancel this command,\n\ or Enter to restore default definition", "",&s,NULL)) < 0) { if (flag) /* Handle parse errors */ goto def_again; else return(y); } s = brstrip(s); #ifndef NOKVERBS p = s; /* Save this place */ #endif /* NOKVERBS */ /* If the definition included any \Kverbs, quote the backslash so the \Kverb will still be in the definition when the key is pressed. We don't do this in zzstring(), because \Kverbs are valid only in this context and nowhere else. We use this code active for all versions that support SET KEY, even if they don't support \Kverbs, because otherwise \K would behave differently for different versions. */ for (x = 0, y = 0; s[x]; x++, y++) { /* Convert \K to \\K */ if ((x > 0) && (s[x] == 'K' || s[x] == 'k') ) { /* Have K */ if ((x == 1 && s[x-1] == CMDQ) || (x > 1 && s[x-1] == CMDQ && s[x-2] != CMDQ)) { line[y++] = CMDQ; /* Make it \\K */ } if (x > 1 && s[x-1] == '{' && s[x-2] == CMDQ) { line[y-1] = CMDQ; /* Have \{K */ line[y++] = '{'; /* Make it \\{K */ } } line[y] = s[x]; } line[y++] = NUL; /* Terminate */ s = line + y + 1; /* Point to after it */ x = LINBUFSIZ - (int) strlen(line) - 1; /* Calculate remaining space */ if ((x < (LINBUFSIZ / 2)) || (zzstring(line, &s, &x) < 0)) { /* Expand variables, etc. */ printf("?Key definition too long\n"); if (flag) cmsetp(psave); return(-9); } s = line + y + 1; /* Point to result. */ #ifndef NOKVERBS /* Special case: see if the definition starts with a \Kverb. If it does, point to it with p, otherwise set p to NULL. */ p = s; if (*p++ == CMDQ) { if (*p == '{') p++; p = (*p == 'k' || *p == 'K') ? p + 1 : NULL; } #endif /* NOKVERBS */ if (macrotab[kc]) { /* Possibly free old macro from key. */ free((char *)macrotab[kc]); macrotab[kc] = NULL; } switch (strlen(s)) { /* Action depends on length */ case 0: /* Reset to default binding */ keymap[kc] = (KEY) kc; break; case 1: /* Single character */ keymap[kc] = (CHAR) *s; break; default: /* Character string */ #ifndef NOKVERBS if (p) { y = xlookup(kverbs,p,nkverbs,&x); /* Look it up */ debug(F101,"set key kverb lookup",0,y); /* exact match required */ if (y > -1) { keymap[kc] = F_KVERB | y; break; } } #endif /* NOKVERBS */ keymap[kc] = (KEY) kc; macrotab[kc] = (MACRO) malloc(strlen(s)+1); if (macrotab[kc]) strcpy((char *) macrotab[kc], s); /* safe */ break; } if (flag) cmsetp(psave); #ifdef OS2 initvik = 1; /* Update VIK table */ #endif /* OS2 */ return(1); } #endif /* NOSETKEY */ #ifdef STOPBITS struct keytab stoptbl[] = { { "1", 1, 0 }, { "2", 2, 0 } }; #endif /* STOPBITS */ static struct keytab sertbl[] = { { "7E1", 0, 0 }, { "7E2", 1, 0 }, { "7M1", 2, 0 }, { "7M2", 3, 0 }, { "7O1", 4, 0 }, { "7O2", 5, 0 }, { "7S1", 6, 0 }, { "7S2", 7, 0 }, #ifdef HWPARITY { "8E1", 9, 0 }, { "8E2", 10, 0 }, #endif /* HWPARITY */ { "8N1", 8, 0 }, #ifdef HWPARITY { "8N2", 11, 0 }, { "8O1", 12, 0 }, { "8O2", 13, 0 }, #endif /* HWPARITY */ { "", 0, 0 } }; static int nsertbl = (sizeof(sertbl) / sizeof(struct keytab)) - 1; static char * sernam[] = { /* Keep this in sync with sertbl[] */ "7E1", "7E2", "7M1", "7M2", "7O1", "7O2", "7S1", "7S2", "8N1", "8E1", "8E2", "8N2", "8O1", "8O2" }; static struct keytab optstab[] = { /* SET OPTIONS table */ #ifndef NOFRILLS { "delete", XXDEL, 0}, /* DELETE */ #endif /* NOFRILLS */ { "directory", XXDIR, 0}, /* DIRECTORY */ #ifdef CKPURGE { "purge", XXPURGE, 0}, /* PURGE */ #endif /* CKPURGE */ { "type", XXTYP, 0}, /* TYPE */ { "", 0, 0} }; static int noptstab = (sizeof(optstab) / sizeof(struct keytab)) - 1; #ifndef NOXFER /* PROTOCOL SELECTION. Kermit is always available. If CK_XYZ is defined at compile time, then the others become selections also. In OS/2 and Windows, they are integrated and the various SET commands (e.g. "set file type") affect them as they would Kermit. In other OS's (UNIX, VMS, etc), they are external protocols which are run via Kermit's REDIRECT mechanism. All we do is collect and verify the filenames and pass them along to the external protocol. */ struct keytab protos[] = { #ifdef CK_XYZ "g", PROTO_G, CM_INV, #endif /* CK_XYZ */ "kermit", PROTO_K, 0, #ifdef CK_XYZ "other", PROTO_O, 0, "x", PROTO_X, CM_INV|CM_ABR, "xmodem", PROTO_X, 0, "xmodem-crc", PROTO_XC, 0, "y", PROTO_Y, CM_INV|CM_ABR, "ymodem", PROTO_Y, 0, "ymodem-g", PROTO_G, 0, "zmodem", PROTO_Z, 0 #endif /* CK_XYZ */ }; int nprotos = (sizeof(protos) / sizeof(struct keytab)); #ifndef XYZ_INTERNAL #ifndef NOPUSH #define EXP_HANDLER 1 #define EXP_STDERR 2 #define EXP_TIMO 3 static struct keytab extprotab[] = { { "handler", EXP_HANDLER, 0 }, { "redirect-stderr", EXP_STDERR, 0 }, { "timeout", EXP_TIMO, 0 } }; static int nxtprotab = (sizeof(extprotab) / sizeof(struct keytab)); #endif /* NOPUSH */ #endif /* XYZ_INTERNAL */ #define XPCMDLEN 71 _PROTOTYP(static int protofield, (char *, char *, char *)); _PROTOTYP(static int setproto, (void)); static int protofield(current, help, px) char * current, * help, * px; { char *s, tmpbuf[XPCMDLEN+1]; int x; if (current) /* Put braces around default */ ckmakmsg(tmpbuf,TMPBUFSIZ,"{",current,"}",NULL); else tmpbuf[0] = NUL; if ((x = cmfld(help, (char *)tmpbuf, &s, xxstring)) < 0) return(x); if ((int)strlen(s) > XPCMDLEN) { printf("?Sorry - maximum length is %d\n", XPCMDLEN); return(-9); } else if (*s) { strcpy(px,s); /* safe */ } else { px = NULL; } return(x); } static int setproto() { /* Select a file transfer protocol */ /* char * s = NULL; */ int x = 0, y; char s1[XPCMDLEN+1], s2[XPCMDLEN+1], s3[XPCMDLEN+1]; char s4[XPCMDLEN+1], s5[XPCMDLEN+1], s6[XPCMDLEN+1], s7[XPCMDLEN+1]; char * p1 = s1, * p2 = s2, *p3 = s3; char * p4 = s4, * p5 = s5, *p6 = s6, *p7 = s7; #ifdef XYZ_INTERNAL extern int p_avail; #else #ifndef CK_REDIR x = 1; #endif /* CK_REDIR */ #endif /* XYZ_INTERNAL */ s1[0] = NUL; s2[0] = NUL; s3[0] = NUL; s4[0] = NUL; s5[0] = NUL; s6[0] = NUL; if ((y = cmkey(protos,nprotos,"","kermit",xxstring)) < 0) return(y); if (x && y != PROTO_K) { printf( "?Sorry, REDIRECT capability required for external protocols.\n"); return(-9); } if ((x = protofield(ptab[y].h_b_init, "Optional command to send to host prior to uploading in binary mode", p1)) < 0) { if (x == -3) { protocol = y; /* Set protocol but don't change */ return(1); /* anything else */ } else return(x); } if ((x = protofield(ptab[y].h_t_init, "Optional command to send to host prior to uploading in text mode", p2)) < 0) { if (x == -3) goto protoexit; else return(x); } if (y == PROTO_K) { if ((x = protofield(ptab[y].h_x_init, "Optional command to send to host to start Kermit server", p3)) < 0) { if (x == -3) goto protoexit; else return(x); } } #ifndef XYZ_INTERNAL /* If XYZMODEM are external... */ if (y != PROTO_K) { if ((x = protofield(ptab[y].p_b_scmd, "External command to SEND in BINARY mode with this protocol", p4)) < 0) { if (x == -3) goto protoexit; else return(x); } if ((x = protofield(ptab[y].p_t_scmd, "External command to SEND in TEXT mode with this protocol", p5)) < 0) { if (x == -3) goto protoexit; else return(x); } if ((x = protofield(ptab[y].p_b_rcmd, "External command to RECEIVE in BINARY mode with this protocol", p6)) < 0) { if (x == -3) goto protoexit; else return(x); } if ((x = protofield(ptab[y].p_t_rcmd, "External command to RECEIVE in TEXT mode with this protocol", p7)) < 0) { if (x == -3) goto protoexit; else return(x); } } #endif /* XYZ_INTERNAL */ if ((x = cmcfm()) < 0) /* Confirm the command */ return(x); protoexit: /* Common exit from this routine */ #ifdef XYZ_INTERNAL if (!p_avail) { bleep(BP_WARN); printf("\n?X,Y, and Zmodem are unavailable\n"); return(success = 0); } #endif /* XYZ_INTERNAL */ p1 = brstrip(p1); p2 = brstrip(p2); p3 = brstrip(p3); p4 = brstrip(p4); p5 = brstrip(p5); p6 = brstrip(p6); p7 = brstrip(p7); initproto(y,p1,p2,p3,p4,p5,p6,p7); return(success = 1); } #ifndef NOPUSH #ifndef XYZ_INTERNAL #define DEF_EXP_TIMO 12 /* Default timeout for external protocol (seconds) */ int exp_handler = 0; /* These are exported */ int exp_timo = DEF_EXP_TIMO; int exp_stderr = SET_AUTO; VOID shoextern() { /* Invoked by SHOW PROTOCOL */ printf("\n External-protocol handler: %s\n", exp_handler ? (exp_handler == 1 ? "pty" : "system") : "automatic"); #ifdef COMMENT printf(" External-protocol redirect-stderr: %s\n", showooa(exp_stderr)); #endif /* COMMENT */ printf(" External-protocol timeout: %d (sec)\n", exp_timo); } static struct keytab setexternhandler[] = { { "automatic", 0, 0 }, { "pty", 1, 0 }, { "system", 2, 0 } }; int setextern() { /* SET EXTERNAL-PROTOCOL */ int x, y; if ((x = cmkey(extprotab,nxtprotab,"","",xxstring)) < 0) return(x); switch (x) { case EXP_HANDLER: if ((x = cmkey(setexternhandler,3,"","automatic",xxstring)) < 0) return(x); if ((y = cmcfm()) < 0) return(y); exp_handler = x; break; #ifdef COMMENT case EXP_STDERR: if ((x = cmkey(ooatab,3,"","automatic",xxstring)) < 0) return(x); if ((y = cmcfm()) < 0) return(y); exp_stderr = x; break; #endif /* COMMENT */ case EXP_TIMO: y = cmnum("Inactivity timeout, seconds,",ckitoa(DEF_EXP_TIMO), 10,&x,xxstring); return(setnum(&exp_timo,x,y,-1)); } return(success = 1); } #endif /* XYZ_INTERNAL */ #endif /* NOPUSH */ int setdest() { int x, y; if ((y = cmkey(desttab,ndests,"","disk",xxstring)) < 0) return(y); if ((x = cmcfm()) < 0) return(x); dest = y; return(1); } #endif /* NOXFER */ #ifdef DECNET struct keytab dnettab[] = { #ifndef OS2ONLY "cterm", NP_CTERM, 0, #endif /* OS2ONLY */ "lat", NP_LAT, 0 }; int ndnet = (sizeof(dnettab) / sizeof(struct keytab)); #endif /* DECNET */ /* S E T P R I N T E R -- SET PRINTER command */ #ifdef PRINTSWI static struct keytab prntab[] = { /* SET PRINTER switches */ "/bidirectional", PRN_BID, 0, #ifdef OS2 "/character-set", PRN_CS, CM_ARG, #endif /* OS2 */ "/command", PRN_PIP, CM_ARG, "/dos-device", PRN_DOS, CM_ARG, "/end-of-job-string",PRN_TRM, CM_ARG, "/file", PRN_FIL, CM_ARG, #ifdef BPRINT "/flow-control", PRN_FLO, CM_ARG, #endif /* BPRINT */ "/job-header-file", PRN_SEP, CM_ARG, #ifdef OS2 "/length", PRN_LEN, CM_ARG, #endif /* OS2 */ "/none", PRN_NON, 0, #ifdef OS2 "/nopostscript", PRN_RAW, 0, "/nops", PRN_RAW, CM_INV, #endif /* OS2 */ "/output-only", PRN_OUT, 0, #ifdef BPRINT "/parity", PRN_PAR, CM_ARG, #endif /* BPRINT */ "/pipe", PRN_PIP, CM_ARG|CM_INV, #ifdef OS2 "/postscript", PRN_PS, 0, "/ps", PRN_PS, CM_INV, #endif /* OS2 */ "/separator", PRN_SEP, CM_ARG|CM_INV, #ifdef BPRINT "/speed", PRN_SPD, CM_ARG, #endif /* BPRINT */ "/timeout", PRN_TMO, CM_ARG, "/terminator", PRN_TRM, CM_ARG|CM_INV, #ifdef OS2 #ifdef NT "/w", PRN_WIN, CM_ARG|CM_ABR|CM_INV, "/wi", PRN_WIN, CM_ARG|CM_ABR|CM_INV, #endif /* NT */ "/width", PRN_WID, CM_ARG, #endif /* OS2 */ #ifdef NT "/windows-queue", PRN_WIN, CM_ARG, #endif /* NT */ "", 0, 0 }; int nprnswi = (sizeof(prntab) / sizeof(struct keytab)) - 1; #endif /* PRINTSWI */ static int setprinter(xx) int xx; { int x, y; char * s; char * defname = NULL; #ifdef OS2 extern int prncs; #endif /* OS2 */ #ifdef BPRINT char portbuf[64]; long portspeed = 0L; int portparity = 0; int portflow = 0; #endif /* BPRINT */ #ifdef PRINTSWI int c, i, n, wild, confirmed = 0; /* Workers */ int getval = 0; /* Whether to get switch value */ struct stringint pv[PRN_MAX+1]; /* Temporary array for switch values */ struct FDB sw, of, cm; /* FDBs for each parse function */ int haveque = 0; int typeset = 0; #endif /* PRINTSWI */ #ifdef NT struct keytab * printtab = NULL, * _printtab = NULL; int nprint = 0, printdef=0; #endif /* NT */ #ifdef OS2 defname = "PRN"; /* default */ #else #ifdef VMS defname = "LPT:"; #else #ifdef UNIX defname = "|lpr"; #endif /* UNIX */ #endif /* VMS */ #endif /* OS2 */ #ifdef PRINTSWI #ifdef NT haveque = Win32EnumPrt(&printtab,&_printtab,&nprint,&printdef); haveque = haveque && nprint; #endif /* NT */ for (i = 0; i <= PRN_MAX; i++) { /* Initialize switch values */ pv[i].sval = NULL; /* to null pointers */ pv[i].ival = -1; /* and -1 int values */ pv[i].wval = (CK_OFF_T)-1; /* and -1 wide values */ } if (xx == XYBDCP) { /* SET BPRINTER == /BIDIRECTIONAL */ pv[PRN_BID].ival = 1; pv[PRN_OUT].ival = 0; } /* Initialize defaults based upon current printer settings */ if (printername) { defname = printername; switch (printertype) { case PRT_WIN: pv[PRN_WIN].ival = 1; break; case PRT_DOS: pv[PRN_DOS].ival = 1; break; case PRT_PIP: pv[PRN_PIP].ival = 1; break; case PRT_FIL: pv[PRN_FIL].ival = 1; break; case PRT_NON: pv[PRN_NON].ival = 1; break; } } #ifdef BPRINT /* only set the BIDI flag if we are bidi */ if (printbidi) pv[PRN_BID].ival = 1; /* serial port parameters may be set for non-bidi devices */ pv[PRN_SPD].ival = pportspeed / 10L; pv[PRN_PAR].ival = pportparity; pv[PRN_FLO].ival = pportflow; #endif /* BPRINT */ if (printtimo) pv[PRN_TMO].ival = printtimo; if (printterm) { pv[PRN_TRM].ival = 1; makestr(&pv[PRN_TRM].sval,printterm); } if (printsep) { pv[PRN_SEP].ival = 1; makestr(&pv[PRN_SEP].sval,printsep); } if (txt2ps) { pv[PRN_PS].ival = 1; pv[PRN_WID].ival = ps_width; pv[PRN_LEN].ival = ps_length; } else { pv[PRN_RAW].ival = 1; } /* Set up chained parse functions... */ cmfdbi(&sw, /* First FDB - command switches */ _CMKEY, /* fcode */ "Switch", /* hlpmsg */ "", /* default */ "", /* addtl string data */ nprnswi, /* addtl numeric data 1: tbl size */ 4, /* addtl numeric data 2: 4 = cmswi */ xxstring, /* Processing function */ prntab, /* Keyword table */ &cm /* Pointer to next FDB */ ); cmfdbi(&cm, /* Second fdb for confirmation */ _CMCFM, /* fcode */ "", /* hlpmsg */ "", /* default */ "", /* addtl string data */ 0, /* addtl numeric data 1 */ 0, /* addtl numeric data 2 */ NULL, NULL, &of ); cmfdbi(&of, /* Third FDB for printer name */ _CMOFI, /* fcode */ "Printer or file name", /* hlpmsg */ defname, /* default */ "", /* addtl string data */ 0, /* addtl numeric data 1: tbl size */ 0, /* addtl numeric data 2: 4 = cmswi */ xxstring, /* Processing function */ NULL, /* Nothing */ NULL ); while (1) { /* Parse 0 or more switches */ x = cmfdb(&sw); /* Parse switch or other thing */ debug(F101,"setprinter cmfdb","",x); if (x < 0) /* Error */ goto xsetprn; /* or reparse needed */ if (cmresult.fcode != _CMKEY) /* Break out if not a switch */ break; if (cmresult.fdbaddr != &sw) /* Advanced usage :-) */ break; c = cmgbrk(); /* Get break character */ getval = (c == ':' || c == '='); /* to see how they ended the switch */ n = cmresult.nresult; /* Numeric result = switch value */ debug(F101,"setprinter switch","",n); switch (n) { /* Process the switch */ case PRN_PS: /* Text to Postscript */ pv[PRN_PS].ival = 1; pv[PRN_BID].ival = 0; pv[PRN_OUT].ival = 1; pv[PRN_RAW].ival = 0; break; case PRN_RAW: /* Non-Postscript */ pv[PRN_PS].ival = 0; pv[PRN_RAW].ival = 1; break; case PRN_BID: /* Bidirectional */ pv[PRN_BID].ival = 1; pv[PRN_OUT].ival = 0; pv[PRN_PS].ival = 0; pv[PRN_RAW].ival = 1; break; case PRN_OUT: /* Output-only */ pv[PRN_OUT].ival = 1; pv[PRN_BID].ival = 0; pv[PRN_PS].ival = 0; pv[PRN_RAW].ival = 1; break; case PRN_NON: /* NONE */ typeset++; pv[n].ival = 1; pv[PRN_SPD].ival = 0; pv[PRN_PAR].ival = 0; pv[PRN_FLO].ival = FLO_KEEP; break; #ifdef UNIX case PRN_WIN: #endif /* UNIX */ case PRN_DOS: /* DOS printer name */ case PRN_FIL: /* Or filename */ case PRN_PIP: typeset++; if (pv[n].sval) free(pv[n].sval); pv[n].sval = NULL; pv[PRN_NON].ival = 0; /* Zero any previous selections */ pv[PRN_WIN].ival = 0; pv[PRN_DOS].ival = 0; pv[PRN_FIL].ival = 0; pv[PRN_PIP].ival = 0; pv[n].ival = 1; /* Flag this one */ if (!getval) break; /* No value wanted */ if (n == PRN_FIL) { /* File, check accessibility */ int wild = 0; if ((x = cmiofi("Filename","kermit.prn",&s,&wild,xxstring))< 0) if (x == -9) { if (zchko(s) < 0) { printf("Can't create \"%s\"\n",s); return(x); } } else goto xsetprn; if (iswild(s)) { printf("?A single file please\n"); return(-9); } pv[PRN_SPD].ival = 0; pv[PRN_PAR].ival = 0; pv[PRN_FLO].ival = FLO_KEEP; } else if ((x = cmfld(n == PRN_DOS ? /* Value wanted - parse it */ "DOS printer device name" : /* Help message */ (n == PRN_PIP ? "Program name" : "Filename"), n == PRN_DOS ? "PRN" : /* Default */ "", &s, xxstring )) < 0) goto xsetprn; s = brstrip(s); /* Strip enclosing braces */ while (*s == SP) /* Strip leading blanks */ s++; if (n == PRN_PIP) { /* If /PIPE: */ if (*s == '|') { /* strip any extraneous pipe sign */ s++; while (*s == SP) s++; } pv[PRN_SPD].ival = 0; pv[PRN_PAR].ival = 0; pv[PRN_FLO].ival = FLO_KEEP; } if ((y = strlen(s)) > 0) /* Anything left? */ if (pv[n].sval = (char *) malloc(y+1)) /* Yes, keep it */ strcpy(pv[n].sval,s); /* safe */ break; #ifdef NT case PRN_WIN: /* Windows queue name */ typeset++; if (pv[n].sval) free(pv[n].sval); pv[n].sval = NULL; pv[PRN_NON].ival = 0; pv[PRN_DOS].ival = 0; pv[PRN_FIL].ival = 0; pv[n].ival = 1; pv[PRN_SPD].ival = 0; pv[PRN_PAR].ival = 0; pv[PRN_FLO].ival = FLO_KEEP; if (!getval || !haveque) break; if ((x = cmkey(_printtab,nprint,"Print queue name", _printtab[printdef].kwd,xxstring)) < 0) { if (x != -2) goto xsetprn; if (pv[PRN_WIN].sval) free(pv[PRN_WIN].sval); s = atmbuf; if ((y = strlen(s)) > 0) if (pv[n].sval = (char *)malloc(y+1)) strcpy(pv[n].sval,s); /* safe */ } else { if (pv[PRN_WIN].sval) free(pv[PRN_WIN].sval); for (i = 0; i < nprint; i++) { if (x == printtab[i].kwval) { s = printtab[i].kwd; break; } } if ((y = strlen(s)) > 0) if (pv[n].sval = (char *)malloc(y+1)) strcpy(pv[n].sval,s); /* safe */ } break; #endif /* NT */ case PRN_SEP: /* /JOB-HEADER (separator) */ if (pv[n].sval) free(pv[n].sval); pv[n].sval = NULL; pv[n].ival = 1; if (!getval) break; if ((x = cmifi("Filename","",&s,&y,xxstring)) < 0) goto xsetprn; if (y) { printf("?Wildcards not allowed\n"); x = -9; goto xsetprn; } if ((y = strlen(s)) > 0) if (pv[n].sval = (char *) malloc(y+1)) strcpy(pv[n].sval,s); /* safe */ break; case PRN_TMO: /* /TIMEOUT:number */ pv[n].ival = 0; if (!getval) break; if ((x = cmnum("Seconds","0",10,&y,xxstring)) < 0) goto xsetprn; if (y > 999) { printf("?Sorry - 999 is the maximum\n"); x = -9; goto xsetprn; } else pv[n].ival = y; break; case PRN_TRM: /* /END-OF-JOB:string */ if (pv[n].sval) free(pv[n].sval); pv[n].sval = NULL; pv[n].ival = 1; if (!getval) break; if ((x = cmfld("String (enclose in braces if it contains spaces)", "",&s,xxstring)) < 0) goto xsetprn; s = brstrip(s); if ((y = strlen(s)) > 0) if (pv[n].sval = (char *) malloc(y+1)) strcpy(pv[n].sval,s); /* safe */ break; #ifdef BPRINT case PRN_FLO: if (!getval) break; if ((x = cmkey(flotab,nflo, "Serial printer-port flow control", "rts/cts",xxstring)) < 0) goto xsetprn; pv[n].ival = x; break; #ifndef NOLOCAL case PRN_SPD: if (!getval) break; /* TN_COMPORT here too? */ if ((x = cmkey(spdtab, /* Speed (no default) */ nspd, "Serial printer-port interface speed", "9600", xxstring) ) < 0) goto xsetprn; pv[n].ival = x; break; #endif /* NOLOCAL */ case PRN_PAR: pv[n].ival = 0; if (!getval) break; if ((x = cmkey(partbl,npar,"Serial printer-port parity", "none",xxstring)) < 0) goto xsetprn; pv[n].ival = x; break; #endif /* BPRINT */ #ifdef OS2 case PRN_LEN: if (!getval) break; if ((x = cmnum("PS page length", "66",10,&y,xxstring)) < 0) goto xsetprn; pv[n].ival = y; break; case PRN_WID: if (!getval) break; if ((x = cmnum("PS page width", "80",10,&y,xxstring)) < 0) goto xsetprn; pv[n].ival = y; break; case PRN_CS: pv[n].ival = 0; if (!getval) break; if ((y = cmkey( #ifdef CKOUNI txrtab,ntxrtab, #else /* CKOUNI */ ttcstab,ntermc, #endif /* CKOUNI */ "auto-print/printscreen character-set", "cp437",xxstring)) < 0) goto xsetprn; pv[n].ival = y; break; #endif /* OS2 */ default: printf("?Unexpected switch value - %d\n",cmresult.nresult); x = -9; goto xsetprn; } } line[0] = NUL; /* Initialize printer name value */ switch (cmresult.fcode) { /* How did we get here? */ case _CMOFI: /* They typed a filename */ ckstrncpy(line,cmresult.sresult,LINBUFSIZ); /* Name */ wild = cmresult.nresult; /* Wild flag */ if (!typeset) { /* A printer name without a type */ pv[PRN_NON].ival = 0; /* is supposed to be treated as */ pv[PRN_WIN].ival = 0; /* a DOS or Pipe printer. We */ pv[PRN_FIL].ival = 0; /* clear all the flags and let */ pv[PRN_PIP].ival = 0; /* the code below dope out the */ pv[PRN_DOS].ival = 0; /* type. */ } #ifdef NT else if (pv[PRN_WIN].ival && lookup(_printtab,line,nprint,&y)) { /* invalid Window Queue name */ printf("?invalid Windows Printer Queue name: \"%s\"\r\n",line); x = -9; goto xsetprn; } #endif /* NT */ if ((x = cmcfm()) < 0) /* Confirm the command */ goto xsetprn; break; case _CMCFM: /* They entered the command */ if (pv[PRN_DOS].ival > 0) ckstrncpy(line,pv[PRN_DOS].sval ? pv[PRN_DOS].sval : "",LINBUFSIZ); else if (pv[PRN_WIN].ival > 0) ckstrncpy(line,pv[PRN_WIN].sval ? pv[PRN_WIN].sval : "",LINBUFSIZ); else if (pv[PRN_FIL].ival > 0) ckstrncpy(line,pv[PRN_FIL].sval ? pv[PRN_FIL].sval : "",LINBUFSIZ); else if (pv[PRN_PIP].ival > 0) ckstrncpy(line,pv[PRN_PIP].sval ? pv[PRN_PIP].sval : "",LINBUFSIZ); break; default: /* By mistake */ printf("?Unexpected function code: %d\n",cmresult.fcode); x = -9; goto xsetprn; } #else /* No PRINTSWI */ if ((x = cmofi("Printer or file name",defname,&s,xxstring)) < 0) return(x); if (x > 1) { printf("?Directory names not allowed\n"); return(-9); } while (*s == SP || *s == HT) s++; /* Trim leading whitespace */ ckstrncpy(line,s,LINBUFSIZ); /* Make a temporary safe copy */ if ((x = cmcfm()) < 0) return(x); /* Confirm the command */ #endif /* PRINTSWI */ #ifdef IKSD if (inserver && (isguest #ifndef NOSERVER || !ENABLED(en_pri) #endif /* NOSERVER */ )) { printf("Sorry, printing disabled\r\n"); return(success = 0); } #endif /* ISKD */ #ifdef PRINTSWI #ifdef BPRINT if (printbidi) { /* If bidi printing active */ #ifndef UNIX bprtstop(); /* Stop it before proceeding */ #endif /* UNIX */ printbidi = 0; } if (pv[PRN_SPD].ival > 0) { portspeed = (long) pv[PRN_SPD].ival * 10L; if (portspeed == 70L) portspeed = 75L; } if (pv[PRN_PAR].ival > 0) portparity = pv[PRN_PAR].ival; if (pv[PRN_FLO].ival > 0) portflow = pv[PRN_FLO].ival; #endif /* BPRINT */ #endif /* PRINTSWI */ s = line; /* Printer name, if given */ #ifdef OS2ORUNIX #ifdef PRINTSWI if (pv[PRN_PIP].ival > 0) { /* /PIPE was given? */ printpipe = 1; noprinter = 0; if (*s == '|') { /* It might still have a pipe sign */ s++; /* if name give later */ while (*s == SP) /* so remove it and spaces */ s++; } } else #endif /* PRINTSWI */ if (*s == '|') { /* Or pipe implied by name? */ s++; /* Point past pipe sign */ while (*s == SP) /* Gobble whitespace */ s++; if (*s) { printpipe = 1; noprinter = 0; } } else { printpipe = 0; } #ifdef PRINTSWI #ifdef BPRINT if (printpipe && pv[PRN_BID].ival > 0) { printf("?Sorry, pipes not allowed for bidirectional printer\n"); return(-9); } #endif /* BPRINT */ #endif /* PRINTSWI */ #endif /* OS2ORUNIX */ #ifdef OS2 if ( pv[PRN_CS].ival > 0 ) prncs = pv[PRN_CS].ival; if ( pv[PRN_PS].ival > 0 ) { txt2ps = 1; ps_width = pv[PRN_WID].ival <= 0 ? 80 : pv[PRN_WID].ival; ps_length = pv[PRN_LEN].ival <= 0 ? 66 : pv[PRN_LEN].ival; } #endif /* OS2 */ y = strlen(s); /* Length of name of new print file */ if (y > 0 #ifdef OS2 && ((y != 3) || (ckstrcmp(s,"PRN",3,0) != 0)) #endif /* OS2 */ ) { if (printername) { /* Had a print file before? */ free(printername); /* Remove its name */ printername = NULL; } printername = (char *) malloc(y + 1); /* Allocate space for it */ if (!printername) { printf("?Memory allocation failure\n"); return(-9); } strcpy(printername,s); /* (safe) Copy new name to new space */ debug(F110,"printername",printername,0); } #ifdef PRINTSWI /* Set printer type from switches that were given explicitly */ if (pv[PRN_NON].ival > 0) { /* No printer */ printertype = PRT_NON; noprinter = 1; printpipe = 0; } else if (pv[PRN_FIL].ival > 0) { /* File */ printertype = PRT_FIL; noprinter = 0; printpipe = 0; } else if (pv[PRN_PIP].ival > 0) { /* Pipe */ printertype = PRT_PIP; noprinter = 0; printpipe = 1; } else if (pv[PRN_WIN].ival > 0) { /* Windows print queue */ printertype = PRT_WIN; noprinter = 0; printpipe = 0; } else if (pv[PRN_DOS].ival > 0) { /* DOS device */ printertype = PRT_DOS; noprinter = 0; printpipe = 0; } else if (line[0]) { /* Name given without switches */ noprinter = 0; printertype = printpipe ? PRT_PIP : PRT_DOS; #ifdef NT /* was the command SET PRINTER windows-queue ? */ y = lookup(_printtab,line,nprint,&x); if (y >= 0) { printertype = PRT_WIN; if (pv[PRN_WIN].sval) free(pv[PRN_WIN].sval); if (printername) { /* Had a print file before? */ free(printername); /* Remove its name */ printername = NULL; } pv[PRN_WIN].sval = NULL; pv[PRN_WIN].ival = 1; s = printtab[x].kwd; /* Get full new name */ if ((y = strlen(s)) > 0) { makestr(&pv[PRN_WIN].sval,s); makestr(&printername,s); if (!printername) { printf("?Memory allocation failure\n"); return(-9); } debug(F110,"printername",printername,0); } } else if ( y == -2 ) { /* Ambiguous Print Queue Name */ printf("?Ambiguous printer name provided.\n"); return(-9); } #endif /* NT */ } #ifdef BPRINT /* Port parameters may be set for non-bidi mode */ pportspeed = portspeed; /* Set parameters */ pportparity = portparity; pportflow = portflow; if (pv[PRN_BID].ival > 0) { /* Bidirectional */ #ifdef UNIX printbidi = 1; /* (just to test parsing...) */ #else printbidi = bprtstart(); /* Start bidirectional printer */ #endif /* UNIX */ return(success = printbidi); } else printbidi = 0; /* Not BPRINTER, unset flag */ #endif /* BPRINT */ if (pv[PRN_TMO].ival > -1) { /* Take care of timeout */ printtimo = pv[PRN_TMO].ival; } if (pv[PRN_TRM].ival > 0) { /* Termination string */ if (printterm) { free(printterm); printterm = NULL; } if (pv[PRN_TRM].sval) makestr(&printterm,pv[PRN_TRM].sval); } if (pv[PRN_SEP].ival > 0) { /* and separator file */ if (printsep) { free(printsep); printsep = NULL; } if (pv[PRN_SEP].sval) makestr(&printsep,pv[PRN_SEP].sval); } #endif /* PRINTSWI */ #ifdef UNIXOROSK if (!printpipe #ifdef PRINTSWI && !noprinter #endif /* PRINTSWI */ ) { /* File - check access */ if (zchko(s) < 0) { printf("?Access denied - %s\n",s); x = -9; goto xsetprn; } } #endif /* UNIXOROSK */ x = 1; /* Return code */ xsetprn: /* Common exit */ #ifdef PRINTSWI for (i = 0; i <= PRN_MAX; i++) { /* Free malloc'd memory */ if (pv[i].sval) free(pv[i].sval); } #endif /* PRINTSWI */ success = (x > 0) ? 1 : 0; return(x); } #ifdef ANYSSH /* The SET SSH command */ #define SSH_CMD 1 /* SET SSH COMMAND */ #ifdef SSHBUILTIN /* Built-in SET SSH options */ #define SSH_ADD 2 /* Add */ #define SSH_AFW 3 /* Agent-forwarding */ #define SSH_CHI 4 /* Check Host IP */ #define SSH_XFW 5 /* X11-forwarding */ #define SSH_DYF 6 /* Dynamic forwarding */ #define SSH_GWP 7 /* Gatewa portgs */ #define SSH_GSS 8 /* GSSAPI */ #define SSH_KBD 9 /* KBD Interactive Devices */ #define SSH_K4 10 /* Kerberos 4 */ #define SSH_K5 11 /* Kerberos 5 */ #define SSH_SHK 12 /* Strict Host Key Check */ #define SSH_V1 13 /* SSH V1 */ #define SSH_V2 14 /* SSH V2 */ #define SSH_PRP 15 /* Privd port */ #define SSH_CMP 16 /* Compression */ #define SSH_XAL 17 /* X Auth Location */ #define SSH_SHH 18 /* Quiet */ #define SSH_VER 19 /* Version */ #define SSH_VRB 20 /* Verbosity level */ #define SSH_IDF 21 /* Identity File */ #define SSH_CFG 22 /* Use OpenSSH Config */ #define SSH_HBT 23 /* Heartbeat Interval */ #endif /* SSHBUILTIN */ static struct keytab sshtab[] = { /* SET SSH command table */ #ifdef SSHBUILTIN { "agent-forwarding", SSH_AFW, 0 }, { "check-host-ip", SSH_CHI, 0 }, { "compression", SSH_CMP, 0 }, { "dynamic-forwarding", SSH_DYF, 0 }, { "gateway-ports", SSH_GWP, 0 }, { "gssapi", SSH_GSS, 0 }, { "heartbeat-interval", SSH_HBT, 0 }, { "identity-file", SSH_IDF, 0 }, #ifdef COMMENT { "kbd-interactive-devices", SSH_KBD, 0 }, #endif /* COMMENT */ { "k4", SSH_K4, CM_INV }, { "k5", SSH_K5, CM_INV }, { "kerberos4", SSH_K4, 0 }, { "kerberos5", SSH_K5, 0 }, { "krb4", SSH_K4, CM_INV }, { "krb5", SSH_K5, CM_INV }, { "privileged-port", SSH_PRP, 0 }, { "quiet", SSH_SHH, 0 }, { "strict-host-key-check", SSH_SHK, 0 }, { "use-openssh-config", SSH_CFG, 0 }, { "v1", SSH_V1, 0 }, { "v2", SSH_V2, 0 }, { "verbose", SSH_VRB, 0 }, { "version", SSH_VER, 0 }, { "x11-forwarding", SSH_XFW, 0 }, { "xauth-location", SSH_XAL, 0 }, #else #ifdef SSHCMD { "command", SSH_CMD, 0 }, #endif /* SSHCMD */ #endif /* SSHBUILTIN */ { "", 0, 0 } }; static int nsshtab = (sizeof(sshtab) / sizeof(struct keytab)) - 1; #ifdef SSHBUILTIN static struct keytab sshver[] = { /* SET SSH VERSION command table */ { "1", 1, 0 }, { "2", 2, 0 }, { "automatic", 0, 0 } }; #define SSHA_CRS 1 #define SSHA_DSA 2 #define SSHA_GSS 3 #define SSHA_HOS 4 #define SSHA_KBD 5 #define SSHA_K4 6 #define SSHA_K5 7 #define SSHA_PSW 8 #define SSHA_PK 9 #define SSHA_SKE 10 #define SSHA_TIS 11 #define SSHA_EXT 12 #define SSHA_SRP 13 static struct keytab ssh2aut[] = { /* SET SSH V2 AUTH command table */ { "external-keyx", SSHA_EXT, 0 }, { "gssapi", SSHA_GSS, 0 }, { "hostbased", SSHA_HOS, 0 }, { "keyboard-interactive", SSHA_KBD, 0 }, { "password", SSHA_PSW, 0 }, { "publickey", SSHA_PK, 0 }, { "srp-gex-sha1", SSHA_SRP, 0 }, { "", 0, 0 } }; static int nssh2aut = (sizeof(ssh2aut) / sizeof(struct keytab)) - 1; #define SSHF_LCL 1 #define SSHF_RMT 2 static struct keytab addfwd[] = { /* SET SSH ADD command table */ { "local-port-forward", SSHF_LCL, 0 }, { "remote-port-forward", SSHF_RMT, 0 }, { "", 0, 0 } }; static int naddfwd = (sizeof(addfwd) / sizeof(struct keytab)) - 1; #define SSH1_CIF 1 #define SSH1_GNH 2 #define SSH1_UNH 3 #define SSH1_K54 4 #define SSH2_CIF 1 #define SSH2_GNH 2 #define SSH2_UNH 3 #define SSH2_ARK 4 #define SSH2_HKA 5 #define SSH2_MAC 6 #define SSH2_AUT 7 static struct keytab sshv1tab[] = { /* SET SSH V1 command table */ { "cipher", SSH1_CIF, 0 }, { "global-known-hosts-file", SSH1_GNH, 0 }, { "k5-reuse-k4-messages", SSH1_K54, CM_INV }, { "user-known-hosts-file", SSH1_UNH, 0 }, { "", 0, 0 } }; static int nsshv1tab = (sizeof(sshv1tab) / sizeof(struct keytab)) - 1; static struct keytab sshv2tab[] = { /* SET SSH V2 command table */ { "authentication", SSH2_AUT, 0 }, { "auto-rekey", SSH2_ARK, 0 }, { "ciphers", SSH2_CIF, 0 }, { "global-known-hosts-file", SSH2_GNH, 0 }, { "hostkey-algorithms", SSH2_HKA, 0 }, { "macs", SSH2_MAC, 0 }, { "user-known-hosts-file", SSH2_UNH, 0 }, { "", 0, 0 } }; static int nsshv2tab = (sizeof(sshv2tab) / sizeof(struct keytab)) - 1; #define SSHC_3DES 1 /* 3DES */ #define SSHC_3CBC 2 /* 3DES-CBC */ #define SSHC_A128 3 /* AES128-CBC */ #define SSHC_A192 4 /* AES192-CBC */ #define SSHC_A256 5 /* AES256-CBC */ #define SSHC_ARC4 6 /* ARCFOUR */ #define SSHC_FISH 7 /* BLOWFISH */ #define SSHC_BCBC 9 /* BLOWFISH-CBC */ #define SSHC_C128 8 /* CAST128-CBC */ #define SSHC_1DES 10 /* DES */ static struct keytab ssh1ciphers[] = { { "3des", SSHC_3DES, 0 }, { "blowfish", SSHC_FISH, 0 }, { "des", SSHC_1DES, 0 }, { "", 0, 0 } }; static int nssh1ciphers = (sizeof(ssh1ciphers) / sizeof(struct keytab)) - 1; static struct keytab ssh2ciphers[] = { /* SET SSH V2 CIPHERS command table */ { "3des-cbc", SSHC_3DES, 0 }, { "aes128-cbc", SSHC_A128, 0 }, { "aes192-cbc", SSHC_A192, 0 }, { "aes256-cbc", SSHC_A256, 0 }, { "arcfour", SSHC_ARC4, 0 }, { "blowfish-cbc", SSHC_FISH, 0 }, { "cast128-cbc", SSHC_C128, 0 }, { "rijndael128-cbc", SSHC_A128, 0 }, { "rijndael192-cbc", SSHC_A192, 0 }, { "rijndael256-cbc", SSHC_A256, 0 }, { "", 0, 0 } }; static int nssh2ciphers = (sizeof(ssh2ciphers) / sizeof(struct keytab)) - 1; #define SSHM_SHA 1 /* HMAC-SHA1 */ #define SSHM_SHA_96 2 /* HMAC-SHA1-96 */ #define SSHM_MD5 3 /* HMAC-MD5 */ #define SSHM_MD5_96 4 /* HMAC-MD5-96 */ #define SSHM_RIPE 5 /* HMAC-RIPEMD160 */ static struct keytab ssh2macs[] = { /* SET SSH V2 MACS command table */ { "hmac-md5", SSHM_MD5, 0 }, { "hmac-md5-96", SSHM_MD5_96, 0 }, { "hmac-ripemd160", SSHM_RIPE, 0 }, { "hmac-sha1", SSHM_SHA, 0 }, { "hmac-sha1-96", SSHM_SHA_96, 0 }, { "", 0, 0 } }; static int nssh2macs = (sizeof(ssh2macs) / sizeof(struct keytab)) - 1; static struct keytab tgtpass[] = { { "tgt-passing", 1, 0, }, { "", 0, 0 } }; static int ntgtpass = (sizeof(tgtpass) / sizeof(struct keytab)) - 1; static struct keytab gssapitab[] = { { "delegate-credentials", 1, 0, }, { "key-exchange", 2, CM_INV, }, { "", 0, 0 } }; static int ngssapitab = (sizeof(gssapitab) / sizeof(struct keytab)) - 1; #define HKA_RSA 1 #define HKA_DSS 2 static struct keytab hkatab[] = { { "ssh-dss", HKA_DSS, 0, }, { "ssh-rsa", HKA_RSA, 0, }, { "", 0, 0 } }; static int nhkatab = (sizeof(hkatab) / sizeof(struct keytab)) - 1; int /* SET SSH variables */ ssh_afw = 0, /* agent forwarding */ ssh_xfw = 0, /* x11 forwarding */ ssh_prp = SET_OFF, /* privileged ports */ ssh_cmp = 1, /* compression */ ssh_shh = 0, /* quiet */ ssh_ver = 0, /* protocol version (auto,1,2) */ ssh_vrb = 2, /* Report errors */ ssh_chkip = 0, /* SSH Check Host IP flag */ ssh_gwp = 0, /* gateway ports */ ssh_dyf = 0, /* dynamic forwarding */ ssh_gsd = 0, /* gssapi delegate credentials */ ssh_k4tgt = 0, /* k4 tgt passing */ ssh_k5tgt = 0, /* k5 tgt passing */ ssh_shk = 2, /* Strict host key (no, yes, ask) */ ssh2_ark = 1, /* Auto re-key */ ssh_cas = 0, /* command as subsys */ ssh_cfg = 0, /* use OpenSSH config? */ ssh_gkx = 1, /* gssapi key exchange */ ssh_k5_is_k4 = 1, /* some SSH v1 use same codes */ ssh_hbt = 0, /* heartbeat (seconds) */ ssh_dummy = 0; /* bottom of list */ char /* The following are to be malloc'd */ * ssh1_cif = NULL, /* v1 cipher */ * ssh2_cif = NULL, /* v2 cipher list */ * ssh2_mac = NULL, /* v2 mac list */ * ssh2_auth = NULL, /* v2 authentication list */ * ssh_hst = NULL, /* hostname */ * ssh_prt = NULL, /* port/service */ * ssh_cmd = NULL, /* command to execute */ * ssh_xal = NULL, /* xauth-location */ * ssh1_gnh = NULL, /* v1 global known hosts file */ * ssh1_unh = NULL, /* v1 user known hosts file */ * ssh2_gnh = NULL, /* v2 global known hosts file */ * ssh2_unh = NULL, /* v2 user known hosts file */ * ssh2_hka = NULL, /* Host Key Algorithms */ * xxx_dummy = NULL; char * ssh_idf[32] = { /* Identity file list */ NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL }; char * ssh_tmp[32] = { /* Temp identity file list */ NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL }; int ssh_idf_n = 0; extern int ssh_pf_lcl_n, ssh_pf_rmt_n; extern struct ssh_pf ssh_pf_lcl[32]; /* Port forwarding structs */ extern struct ssh_pf ssh_pf_rmt[32]; /* (declared in ckuusr.c) */ #endif /* SSHBUILTIN */ #ifdef SFTP_BUILTIN static struct keytab sftptab[] = { { "end-of-line", XY_SFTP_EOL, 0, }, { "remote-character-set", XY_SFTP_RCS, 0, }, { "", 0, 0 } }; static int nsftptab = (sizeof(sftptab) / sizeof(struct keytab)) - 1; #endif /* SFTP_BUILTIN */ VOID shossh() { #ifdef SSHBUILTIN int i, n = 0; /* ADD askmore()! */ printf("\nSSH is built in:\n\n"); printf(" ssh host: %s\n",showstring(ssh_hst)); printf(" ssh port: %s\n",showstring(ssh_prt)); printf(" ssh command: %s\n",showstring(ssh_cmd)); printf(" ssh agent-forwarding: %s\n",showoff(ssh_afw)); printf(" ssh check-host-ip: %s\n",showoff(ssh_chkip)); printf(" ssh compression: %s\n",showoff(ssh_cmp)); printf(" ssh dynamic-forwarding: %s\n",showoff(ssh_dyf)); if (ssh_pf_lcl[0].p1 && ssh_pf_lcl[0].host && ssh_pf_lcl[0].p2) { printf(" ssh forward-local-port: %d %s %d\n", ssh_pf_lcl[0].p1, ssh_pf_lcl[0].host, ssh_pf_lcl[0].p2); for ( n=1;n 0) { int len = 0; for (j = 0; j < i; j++) { for (x = 0; x < nssh2aut; x++) if (ssh2aut[x].kwval == tmp[j] && !ssh2aut[x].flgs) break; len += strlen(ssh2aut[x].kwd) + 1; } ssh2_auth = malloc(len); ssh2_auth[0] = '\0'; for (j = 0; j < i; j++) { for (x = 0; x < nssh2aut; x++) if (ssh2aut[x].kwval == tmp[j] && !ssh2aut[x].flgs) break; ckstrncat(ssh2_auth,ssh2aut[x].kwd,len); if (j < i - 1) ckstrncat(ssh2_auth,",",len); } } return(success = 1); #undef TMPCNT } case SSH2_CIF: { #define TMPCNT 12 int i, j, tmp[TMPCNT]; for (i = 0; i < TMPCNT; i++) tmp[i] = 0; for (i = 0; i < TMPCNT; i++) { if ((y = cmkey(ssh2ciphers,nssh2ciphers, "","", xxstring)) < 0) { if (y == -3) break; return(y); } for (j = 0; j < i; j++) { if (tmp[j] == y) { printf("\r\n?Choice has already been used.\r\n"); return(-9); } } tmp[i] = y; } if ((z = cmcfm()) < 0) return(z); if (ssh2_cif) { free(ssh2_cif); ssh2_cif = NULL; } if (i > 0) { int len = 0; for (j=0; j < i; j++) { for (x = 0; x < nssh2ciphers; x++) if (ssh2ciphers[x].kwval == tmp[j] && !ssh2ciphers[x].flgs) break; len += strlen(ssh2ciphers[x].kwd) + 1; } ssh2_cif = malloc(len); ssh2_cif[0] = '\0'; for (j = 0; j < i; j++) { for (x = 0; x < nssh2ciphers; x++) if (ssh2ciphers[x].kwval == tmp[j] && !ssh2ciphers[x].flgs) break; ckstrncat(ssh2_cif,ssh2ciphers[x].kwd,len); if (j < i - 1) ckstrncat(ssh2_cif,",",len); } } return(success = 1); #undef TMPCNT } case SSH2_MAC: { #define TMPCNT 12 int i, j, tmp[TMPCNT]; for (i = 0; i < TMPCNT; i++) tmp[i] = 0; for (i = 0; i < TMPCNT; i++) { if ((y = cmkey(ssh2macs,nssh2macs,"","", xxstring)) < 0) { if (y == -3) break; return(y); } for (j = 0; j < i; j++) { if (tmp[j] == y) { printf("\r\n?Choice has already been used.\r\n"); return(-9); } } tmp[i] = y; } if ((z = cmcfm()) < 0) return(z); if (ssh2_mac) { free(ssh2_mac); ssh2_mac = NULL; } if (i > 0) { int len = 0; for (j = 0; j < i; j++) { for (x = 0; x < nssh2macs; x++) if (ssh2macs[x].kwval == tmp[j] && !ssh2macs[x].flgs) break; len += strlen(ssh2macs[x].kwd) + 1; } ssh2_mac = malloc(len); ssh2_mac[0] = '\0'; for (j=0; j < i; j++) { for (x = 0; x < nssh2macs; x++) if (ssh2macs[x].kwval == tmp[j] && !ssh2macs[x].flgs) break; ckstrncat(ssh2_mac,ssh2macs[x].kwd,len); if (j < i - 1) ckstrncat(ssh2_mac,",",len); } } return(success = 1); #undef TMPCNT } case SSH2_HKA: { #define TMPCNT 12 int i, j, tmp[TMPCNT]; for (i = 0; i < TMPCNT; i++) tmp[i] = 0; for (i = 0; i < TMPCNT; i++) { if ((y = cmkey(hkatab,nhkatab, "","", xxstring)) < 0) { if (y == -3) break; return(y); } for (j = 0; j < i; j++) { if (tmp[j] == y) { printf("\r\n?Choice has already been used.\r\n"); return(-9); } } tmp[i] = y; } if ((z = cmcfm()) < 0) return(z); if (ssh2_hka) { free(ssh2_hka); ssh2_hka = NULL; } if (i > 0) { int len = 0; for (j=0; j < i; j++) { for (x = 0; x < nhkatab; x++) if (hkatab[x].kwval == tmp[j] && !hkatab[x].flgs) break; len += strlen(hkatab[x].kwd) + 1; } ssh2_hka = malloc(len); ssh2_hka[0] = '\0'; for (j = 0; j < i; j++) { for (x = 0; x < nhkatab; x++) if (hkatab[x].kwval == tmp[j] && !hkatab[x].flgs) break; ckstrncat(ssh2_hka,hkatab[x].kwd,len); if (j < i - 1) ckstrncat(ssh2_hka,",",len); } } return(success = 1); #undef TMPCNT } case SSH2_GNH: case SSH2_UNH: if ((x = cmifi("Filename","",&s,&z,xxstring)) < 0) { if (x != -3) return(x); } else { ckstrncpy(line,s,LINBUFSIZ); if (zfnqfp(line,TMPBUFSIZ,tmpbuf)) ckstrncpy(line,tmpbuf,LINBUFSIZ); } s = (x == -3) ? NULL : line; if ((x = cmcfm()) < 0) return(x); switch (y) { case SSH2_GNH: makestr(&ssh2_gnh,s); break; case SSH2_UNH: makestr(&ssh2_unh,s); break; default: return(success = 0); } return(success = 1); } case SSH_VRB: /* Verbosity level */ y = cmnum("SSH verbosity level, 0-7","2",10,&x,xxstring); return(setnum(&ssh_vrb,x,y,7)); case SSH_VER: /* Version */ if ((y = cmkey(sshver,3,"","auto", xxstring)) < 0) return(y); if ((x = cmcfm()) < 0) return(x); ssh_ver = y; /* 0 == AUTO */ #ifndef SSHTEST sl_ssh_ver_saved = 0; #endif /* SSHTEST */ return(success = 1); case SSH_IDF: { /* Identity file */ int i, n; for (i = 0; i < 32; i++) { if ((x = cmifi("Filename","",&s,&y,xxstring)) < 0) { if (x == -3) break; return(x); } if (!zfnqfp(s,LINBUFSIZ,line)) ckstrncpy(line,s,LINBUFSIZ); makestr(&ssh_tmp[i],line); } n = i; if ((x = cmcfm()) < 0) { for (i = 0; i < n; i++) makestr(&(ssh_tmp[i]),NULL); return(x); } for (i = 0; i < 32; i++) { makestr(&(ssh_idf[i]),NULL); if (i < n) { ssh_idf[i] = ssh_tmp[i]; ssh_tmp[i] = NULL; } else { makestr(&(ssh_tmp[i]),NULL); } } ssh_idf_n = n; return(success = 1); } case SSH_XFW: /* X11-forwarding */ success = seton(&ssh_xfw); #ifndef SSHTEST if (success) sl_ssh_xfw_saved = 0; #endif /* SSHTEST */ return(success); case SSH_XAL: /* SSH Xauth Location */ if ((x = cmifi("Path to executable", "",&s,&y,xxstring)) < 0) { if (x != -3) return(x); } else { ckstrncpy(line,s,LINBUFSIZ); if (zfnqfp(line,TMPBUFSIZ,tmpbuf)) ckstrncpy(line,tmpbuf,LINBUFSIZ); } s = (x == -3) ? NULL : line; if ((x = cmcfm()) < 0) return(x); makestr(&ssh_xal,s); return(success = 1); case SSH_CFG: /* Use OpenSSH Config */ return(success = seton(&ssh_cfg)); #endif /* SSHBUILTIN */ default: return(-2); } } #endif /* ANYSSH */ #ifdef SFTP_BUILTIN static int dosetsftp() { int cx, x, y, z; char * s; if ((cx = cmkey(sftptab,nsftptab,"","", xxstring)) < 0) return(cx); switch (cx) { case XY_SFTP_EOL: case XY_SFTP_RCS: default: return(-2); } } #endif /* SFTP_BUILTIN */ #ifdef KUI #include "ikui.h" extern ULONG RGBTable[16]; #define GUI_RGB 1 #define GUI_WIN 2 #define GUI_FON 3 #define GUI_DIA 4 #define GUI_TLB 5 #define GUI_MNB 6 #define GUI_CLS 7 #define GUIW_POS 1 #define GUIW_RES 2 #define GUIW_RUN 3 #define GUIWR_NON 0 #define GUIWR_FON 1 #define GUIWR_DIM 2 #define GUIWN_RES 1 #define GUIWN_MIN 2 #define GUIWN_MAX 3 static struct keytab guitab[] = { { "close", GUI_CLS, 0 }, { "dialogs", GUI_DIA, 0 }, { "font", GUI_FON, 0 }, { "menubar", GUI_MNB, 0 }, { "rgbcolor", GUI_RGB, 0 }, { "toolbar", GUI_TLB, 0 }, { "window", GUI_WIN, 0 }, { "", 0, 0} }; static int nguitab = (sizeof(guitab) / sizeof(struct keytab)); static struct keytab guiwtab[] = { { "position", GUIW_POS, 0 }, { "resize-mode", GUIW_RES, 0 }, { "run-mode", GUIW_RUN, 0 }, { "", 0, 0} }; static int nguiwtab = (sizeof(guiwtab) / sizeof(struct keytab)); static struct keytab guiwrtab[] = { { "change-dimensions", GUIWR_DIM, 0 }, { "none", GUIWR_NON, 0 }, { "scale-font", GUIWR_FON, 0 }, { "", 0, 0} }; static int nguiwrtab = (sizeof(guiwrtab) / sizeof(struct keytab)); static struct keytab guiwntab[] = { { "maximize", GUIWN_MAX, 0 }, { "minimize", GUIWN_MIN, 0 }, { "restore", GUIWN_RES, 0 }, { "", 0, 0} }; static int nguiwntab = (sizeof(guiwntab) / sizeof(struct keytab)); static struct keytab rgbtab[] = { { "black", 0, 0 }, { "blue", 1, 0 }, { "brown", 6, 0 }, { "cyan", 3, 0 }, { "darkgray", 8, 0 }, { "dgray", 8, CM_INV }, { "green", 2, 0 }, { "lblue", 9, CM_INV }, { "lcyan", 11, CM_INV }, { "lgreen", 10, CM_INV }, { "lgray", 7, CM_INV }, { "lightblue", 9, 0 }, { "lightcyan", 11, 0 }, { "lightgreen", 10, 0 }, { "lightgray", 7, 0 }, { "lightmagenta", 13, 0 }, { "lightred", 12, 0 }, { "lmagenta", 13, CM_INV }, { "lred", 12, CM_INV }, { "magenta", 5, 0 }, { "red", 4, 0 }, { "white", 15, 0 }, { "yellow", 14, 0 }, }; int nrgb = (sizeof(rgbtab) / sizeof(struct keytab)); VOID shogui() { extern gui_dialog; extern HWND getHwndKUI(); unsigned char cmdsav = colorcmd; int i, red, green, blue, lines=0; char * s; printf("GUI paramters:\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return; else lines = 0; } printf(" Dialogs: %s\n",showoff(gui_dialog)); if (++lines > cmd_rows - 3) { if (!askmore()) return; else lines = 0; } printf(" Position: %d,%d\n",get_gui_window_pos_x(), get_gui_window_pos_y()); if (++lines > cmd_rows - 3) { if (!askmore()) return; else lines = 0; } printf(" Resolution: %d x %d\n",GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN)); if (++lines > cmd_rows - 3) { if (!askmore()) return; else lines = 0; } printf(" Run-mode: %s\n",IsIconic(getHwndKUI()) ? "minimized" : IsZoomed(getHwndKUI()) ? "maximized" : "restored"); if (++lines > cmd_rows - 3) { if (!askmore()) return; else lines = 0; } switch ( get_gui_resize_mode() ) { case GUIWR_NON: s = "none"; break; case GUIWR_FON: s = "scales font"; break; case GUIWR_DIM: s= "changes dimensions"; break; } printf(" Resize-mode: %s\n",s); if (++lines > cmd_rows - 3) { if (!askmore()) return; else lines = 0; } printf("\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return; else lines = 0; } printf("RGB Color Table:\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return; else lines = 0; } printf(" Color Red Green Blue\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return; else lines = 0; } printf(" ------------------------------------------\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return; else lines = 0; } for (i = 0; i < nrgb; i++) { if (!rgbtab[i].flgs) { blue = (RGBTable[rgbtab[i].kwval] & 0x00FF0000)>>16; green = (RGBTable[rgbtab[i].kwval] & 0x0000FF00)>>8; red = (RGBTable[rgbtab[i].kwval] & 0x000000FF); printf(" %-18s %3d %3d %3d ",rgbtab[i].kwd,red,green,blue); colorcmd = rgbtab[i].kwval << 4; printf("********"); colorcmd = cmdsav; printf("\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return; else lines = 0; } } } printf("\n"); } int setrgb() { int cx, red = 0, blue = 0, green = 0, z, x; if ((cx = cmkey(rgbtab,nrgb,"","",xxstring)) < 0) return(cx); if ((z = cmnum("Red value, 0-255","",10,&red,xxstring)) < 0) return(z); if ((z = cmnum("Green value, 0-255","",10,&green,xxstring)) < 0) return(z); if ((z = cmnum("Blue value, 0-255","",10,&blue,xxstring)) < 0) return(z); if ((x = cmcfm()) < 0) return(x); if (cx > 15 || red > 255 || blue > 255 || green > 255) return(-2); RGBTable[cx] = (unsigned)(((unsigned)blue << 16) | (unsigned)((unsigned)green << 8) | (unsigned)red); return(success = 1); } /* Set GUI window position: XY coordinates of upper left corner, expressed as pixel numbers in the current screen resolution. (0,0) means put ourselves in the upper left corner. Can we check for out of bounds? */ int setguiwin() { int cx, x, y, z; if ((cx = cmkey(guiwtab,nguiwtab,"","",xxstring)) < 0) return(cx); switch (cx) { case GUIW_POS: if ((z = cmnum("X coordinate (pixel number)","",10,&x,xxstring)) < 0) return(z); if ((z = cmnum("Y coordinate (pixel number)","",10,&y,xxstring)) < 0) return(z); if ((z = cmcfm()) < 0) return(z); if (x < 0 || y < 0) { printf("?Coordinates must be 0 or greater\n"); return(-9); } gui_position(x,y); return(success = 1); case GUIW_RES: if ((x = cmkey(guiwrtab,nguiwrtab,"","",xxstring)) < 0) return(x); if ((z = cmcfm()) < 0) return(z); gui_resize_mode(x); return(success = 1); case GUIW_RUN: if ((x = cmkey(guiwntab,nguiwntab,"","",xxstring)) < 0) return(x); if ((z = cmcfm()) < 0) return(z); gui_win_run_mode(x); return(success = 1); default: return(-2); } } int setguifont() { /* Assumes that CKFLOAT is defined! */ extern struct keytab * term_font; extern struct keytab * _term_font; extern int tt_font, tt_font_size, ntermfont; int x, y, z; char *s; if (ntermfont == 0) BuildFontTable(&term_font, &_term_font, &ntermfont); if (!(term_font && _term_font && ntermfont > 0)) { printf("?Internal error: Failure to enumerate fonts\n"); return(-9); } if ((x = cmkey(_term_font,ntermfont,"","",xxstring)) < 0) return(x); if ((z = cmfld("Height of font in points","12",&s,xxstring)) < 0) return(z); if (isfloat(s,0) < 1) { /* (sets floatval) */ printf("?Integer or floating-point number required\n"); return(-9); } if (floatval < 0.5) { printf("?Positive number required\n"); return(-9); } if ((z = cmcfm()) < 0) return(z); tt_font = x; /* Font index */ tt_font_size = (int)(floatval * 2); /* Font size in half points */ KuiSetProperty(KUI_TERM_FONT, (long)tt_font, (long)tt_font_size); return(success = 1); } VOID setguidialog(x) int x; { extern int gui_dialog; gui_dialog = x; KuiSetProperty(KUI_GUI_DIALOGS, (long)x, 0L); } VOID setguimenubar(x) int x; { KuiSetProperty(KUI_GUI_MENUBAR, (long)x, 0L); } VOID setguitoolbar(x) int x; { KuiSetProperty(KUI_GUI_TOOLBAR, (long)x, 0L); } VOID setguiclose(x) int x; { KuiSetProperty(KUI_GUI_CLOSE, (long)x, 0L); } int setgui() { int cx, x, rc; if ((cx = cmkey(guitab,nguitab,"","",xxstring)) < 0) return(cx); switch (cx) { case GUI_DIA: rc = seton(&x); if (rc >= 0) setguidialog(x); return(rc); case GUI_FON: return(setguifont()); case GUI_RGB: return(setrgb()); case GUI_WIN: return(setguiwin()); case GUI_TLB: rc = seton(&x); if (rc >= 0) setguitoolbar(x); return(rc); case GUI_MNB: rc = seton(&x); if (rc >= 0) setguimenubar(x); return(rc); case GUI_CLS: rc = seton(&x); if (rc >= 0) setguiclose(x); return(rc); default: return(-2); } } #endif /* KUI */ VOID setexitwarn(x) int x; { xitwarn = x; #ifdef KUI KuiSetProperty(KUI_EXIT_WARNING, (long)x, 0L); #endif /* KUI */ } #ifndef NOLOCAL VOID setdebses(x) int x; { #ifdef OS2 if ((debses != 0) && (x == 0)) /* It was on and we turned it off? */ os2debugoff(); /* Fix OS/2 coloration */ #endif /* OS2 */ debses = x; #ifdef KUI KuiSetProperty(KUI_TERM_DEBUG,x,0); #endif /* KUI */ } #endif /* NOLOCAL */ /* D O P R M -- Set a parameter. */ /* Returns: -2: illegal input -1: reparse needed 0: success */ int doprm(xx,rmsflg) int xx, rmsflg; { int i = 0, x = 0, y = 0, z = 0; long zz = 0L; char *s = NULL, *p = NULL; #ifdef OS2 char portbuf[64]; long portspeed = 0L; int portparity = 0; int portflow = 0; #endif /* OS2 */ #ifndef NOSETKEY #ifdef OS2 if (xx == XYMSK) return(setmsk()); #endif /* OS2 */ #endif /* NOSETKEY */ if (xx == XYFLAG) { /* SET FLAG */ extern int ooflag; return(success = seton(&ooflag)); } if (xx == XYPRTR /* SET PRINTER (or BPRINTER) */ #ifdef BPRINT || xx == XYBDCP #endif /* BPRINT */ ) return(setprinter(xx)); switch (xx) { #ifdef ANYX25 /* SET X25 ... */ case XYX25: return(setx25()); #ifndef IBMX25 case XYPAD: /* SET PAD ... */ return(setpadp()); #endif /* IBMX25 */ #endif /* ANYX25 */ #ifndef NOXFER case XYEOL: /* These have all been moved to set send/receive... */ case XYLEN: /* Let the user know what to do. */ case XYMARK: case XYNPAD: case XYPADC: case XYTIMO: printf("...Use SET SEND or SET RECEIVE instead.\n"); printf("Type HELP SET SEND or HELP SET RECEIVE for more info.\n"); return(success = 0); case XYATTR: /* File Attribute packets */ return(setat(rmsflg)); case XYIFD: /* Incomplete file disposition */ if ((y = cmkey(ifdatab,3,"","auto",xxstring)) < 0) return(y); if ((x = cmcfm()) < 0) return(x); if (rmsflg) { sstate = setgen('S', "310", y == 0 ? "0" : (y == 1 ? "1" : "2"), "" ); return((int) sstate); } else { keep = y; return(success = 1); } #endif /* NOXFER */ case XYMATCH: /* [ REMOTE ] SET MATCH... */ #ifndef NOXFER if ((z = cmkey(matchtab,nmatchtab,"","",xxstring)) < 0) return(z); if (rmsflg) { if ((y = cmkey(onoff,2,"","on",xxstring)) < 0) return(y); if ((x = cmcfm()) < 0) return(x); switch (z) { case MCH_DOTF: return(sstate = setgen('S',"330", y == 0 ? "0" : "1", "")); case MCH_FIFO: return(sstate = setgen('S',"331", y == 0 ? "0" : "1", "")); default: return(-2); } } #endif /* NOXFER */ switch (z) { case MCH_FIFO: return(success = seton(&matchfifo)); case MCH_DOTF: x = seton(&matchdot); if (x < 0) return(x); dir_dots = -1; return(success = x); default: return(-2); } #ifndef NOSPL case XYINPU: /* SET INPUT */ return(setinp()); #endif /* NOSPL */ #ifdef NETCONN case XYNET: { /* SET NETWORK */ struct FDB k1, k2; cmfdbi(&k1,_CMKEY,"","","",nnetkey, 0, xxstring, netkey, &k2); cmfdbi(&k2,_CMKEY,"","","",nnets, 0, xxstring, netcmd, NULL); #ifdef OS2 /* Hide network-type keywords for networks not installed */ for (z = 0; z < nnets; z++) { if (netcmd[z].kwval == NET_TCPB && tcp_avail == 0) netcmd[z].flgs = CM_INV; #ifdef SSHBUILTIN if (netcmd[z].kwval == NET_SSH && !ck_ssleay_is_installed()) netcmd[z].flgs = CM_INV; #endif /* SSHBUILTIN */ #ifdef DECNET else if (netcmd[z].kwval == NET_DEC && dnet_avail == 0) netcmd[z].flgs = CM_INV; #endif /* DECNET */ #ifdef CK_NETBIOS else if (netcmd[z].kwval == NET_BIOS && netbiosAvail == 0) netcmd[z].flgs = CM_INV; #endif /* CK_NETBIOS */ #ifdef SUPERLAT else if (netcmd[z].kwval == NET_SLAT && slat_avail == 0) netcmd[z].flgs = CM_INV; #endif /* SUPERLAT */ } if (tcp_avail) /* Default network type */ ckstrncpy(tmpbuf,"tcp/ip",TMPBUFSIZ); #ifdef SSHBUILTIN else if ( ck_ssleay_is_installed() ) ckstrncpy(tmpbuf,"ssh",TMPBUFSIZ); #endif /* SSHBUILTIN */ #ifdef DECNET else if (dnet_avail) ckstrncpy(tmpbuf,"decnet",TMPBUFSIZ); #endif /* DECNET */ #ifdef SUPERLAT else if (slat_avail) ckstrncpy(tmpbuf,"superlat",TMPBUFSIZ); #endif /* SUPERLAT */ #ifdef CK_NETBIOS else if (netbiosAvail) ckstrncpy(tmpbuf,"netbios",TMPBUFSIZ); #endif /* CK_NETBIOS */ else ckstrncpy(tmpbuf,"named-pipe",TMPBUFSIZ); #else /* OS2 */ #ifdef TCPSOCKET ckstrncpy(tmpbuf,"tcp/ip",TMPBUFSIZ); #else #ifdef ANYX25 ckstrncpy(tmpbuf,"x.25",TMPBUFSIZ); #else ckstrncpy(tmpbuf,"",TMPBUFSIZ); #endif /* ANYX25 */ #endif /* TCPSOCKET */ #endif /* OS2 */ x = cmfdb(&k1); if (x < 0) { /* Error */ if (x == -2 || x == -9) printf("?No keywords match: \"%s\"\n",atmbuf); return(x); } z = cmresult.nresult; /* Keyword value */ if (cmresult.fdbaddr == &k1) { /* Which table? */ #ifndef NOSPL #ifndef NODIAL if (z == XYNET_D) return(parsdir(1)); #endif /* NODIAL */ #endif /* NOSPL */ if ((z = cmkey(netcmd,nnets,"",tmpbuf,xxstring)) < 0) return(z); } #ifdef NETCMD if (z == NET_CMD && nopush) { printf("\n?Sorry, access to external commands is disabled\n"); return(-9); } #endif /* NETCMD */ #ifndef NOPUSH #ifdef NETPTY if (z == NET_PTY && nopush) { printf("\n?Sorry, access to external commands is disabled\n"); return(-9); } #endif /* NETPTY */ #endif /* NOPUSH */ #ifdef OS2 if (z == NET_TCPB && tcp_avail == 0) { printf( "\n?Sorry, either TCP/IP is not available on this system or\n\ necessary DLLs did not load. Use SHOW NETWORK to check network status.\n"); return(-9); #ifdef SSHBUILTIN } else if (z == NET_SSH && !ck_ssleay_is_installed()) { printf("\n?Sorry, SSH is not available on this system.\n") ; return(-9); #endif /* SSHBUILTIN */ #ifdef CK_NETBIOS } else if (z == NET_BIOS && netbiosAvail == 0) { printf("\n?Sorry, NETBIOS is not available on this system.\n") ; return(-9); #endif /* CK_NETBIOS */ #ifdef DECNET } else if (z == NET_DEC && dnet_avail == 0) { printf("\n?Sorry, DECnet is not available on this system.\n") ; return(-9); #endif /* DECNET */ #ifdef SUPERLAT } else if (z == NET_SLAT && slat_avail == 0) { printf("\n?Sorry, SuperLAT is not available on this system.\n") ; return(-9); #endif /* SUPERLAT */ } #endif /* OS2 */ #ifdef NPIPEORBIOS if (z == NET_PIPE || /* Named pipe -- also get pipename */ z == NET_BIOS) { /* NETBIOS -- also get local name */ char *defnam; #ifdef CK_NETBIOS char tmpnbnam[NETBIOS_NAME_LEN+1]; #endif /* CK_NETBIOS */ /* Construct default name */ if (z == NET_PIPE) { /* Named pipe */ defnam = "kermit"; /* Default name is always "kermit" */ } #ifdef CK_NETBIOS else { /* NetBIOS */ if (NetBiosName[0] != SP) { /* If there is already a name, */ char *p = NULL; int n; /* use it as the default. */ ckstrncpy(tmpnbnam,NetBiosName,NETBIOS_NAME_LEN+1); /* convert trailing spaces to NULs */ p = &tmpnbnam[NETBIOS_NAME_LEN-1]; while (*p == SP) { *p = NUL; p--; } defnam = tmpnbnam; } else if (*myhost) /* Otherwise use this PC's host name */ defnam = (char *) myhost; else /* Otherwise use "kermit" */ defnam = "kermit"; } #endif /* CK_NETBIOS */ if ((y = cmtxt((z == NET_PIPE) ? "name of named-pipe" : "local NETBIOS name", defnam, &s, xxstring)) < 0) return(y); #ifdef NPIPE pipename[0] = NUL; #endif /* NPIPE */ if ((y = (int) strlen(s)) < 1) { printf("?You must also specify a %s name\n", (z == NET_PIPE) ? "pipe" : "local NETBIOS" ); return(-9); } #ifdef CK_NETBIOS if (z == NET_BIOS) { if (!netbiosAvail) { printf("?NETBIOS is not available on this system.\n") ; return(-9); } if (y - NETBIOS_NAME_LEN > 0) { printf("?NETBIOS name too long, %ld maximum\n", NETBIOS_NAME_LEN); return(-9); } else if ( !strcmp(s,tmpnbnam) ) { nettype = z; /* Returning to old connection... */ return(success = 1); /* Done */ } else if (strcmp(" ",NetBiosName)) { printf("?NETBIOS name already assigned to \"%s\"\n", NetBiosName); return(-9); } else { NCB ncb; APIRET rc; ckstrncpy(NetBiosName,s,16); for (x = y; x < NETBIOS_NAME_LEN; x++) NetBiosName[x] = SP; NetBiosName[NETBIOS_NAME_LEN] = NUL; printf("Checking \"%s\" as a unique NetBIOS name...\n", NetBiosName); rc = NCBAddName( NetbeuiAPI, &ncb, NetBiosAdapter, NetBiosName ); if (rc) { printf( "?Sorry, \"%s\" is already in use by another NetBIOS node.\n", NetBiosName); for (x = 0; x < NETBIOS_NAME_LEN; x++) NetBiosName[x] = SP; return(-9); } } } #endif /* CK_NETBIOS */ #ifdef NPIPE if (z == NET_PIPE) ckstrncpy(pipename,s,PIPENAML); #endif /* NPIPE */ } else #endif /* NPIPEORBIOS */ #ifdef DECNET if (z == NET_DEC) { /* Determine if we are using LAT or CTERM */ if ((y = cmkey(dnettab, ndnet,"DECNET protocol","lat",xxstring)) < 0) return(y); if ((x = cmcfm()) < 0) return(x); ttnproto = y; } else #endif /* DECNET */ #ifdef NETDLL if (z == NET_DLL) { /* Find out which DLL they are using */ char dllname[256]=""; char * p=NULL; if ((x = cmifi("Dynamic load library", "",&p,&y,xxstring)) < 0) { if (x == -3) { printf("?Name of DLL required\n"); return(-9); } return(x); } ckstrncpy(dllname,p,256); if ((x = cmcfm()) < 0) return(x); if (netdll_load(dllname) < 0) /* Try to load the dll */ return(success = 0); else { nettype = z; return(success = 1); } } else #endif /* NETDLL */ if ((x = cmcfm()) < 0) return(x); nettype = z; if ( #ifdef DECNET (nettype != NET_DEC) && #endif /* DECNET */ #ifdef NPIPE (nettype != NET_PIPE) && #endif /* NPIPE */ #ifdef CK_NETBIOS (nettype != NET_BIOS) && #endif /* CK_NETBIOS */ #ifdef NETFILE (nettype != NET_FILE) && #endif /* NETFILE */ #ifdef NETCMD (nettype != NET_CMD) && #endif /* NETCMD */ #ifdef NETPTY (nettype != NET_PTY) && #endif /* NETPTY */ #ifdef NETDLL (nettype != NET_DLL) && #endif /* NETDLL */ #ifdef SUPERLAT (nettype != NET_SLAT) && #endif /* SUPERLAT */ (nettype != NET_SX25) && (nettype != NET_VX25) && #ifdef IBMX25 (nettype != NET_IX25) && #endif /* IBMX25 */ #ifdef SSHBUILTIN (nettype != NET_SSH) && #endif /* SSHBUILTIN */ (nettype != NET_TCPB)) { printf("?Network type not supported\n"); return(success = 0); } else { return(success = 1); } } #ifndef NOTCPOPTS #ifdef TCPSOCKET case XYTCP: { extern int ttyfd; if ((z = cmkey(tcpopt,ntcpopt,"TCP option","nodelay",xxstring)) < 0) return(z); switch (z) { #ifndef NOHTTP case XYTCP_HTTP_PROXY: { struct FDB sw, tx; int n, x; char ubuf[LOGINLEN+1], pbuf[LOGINLEN+1], abuf[256]; ubuf[0] = pbuf[0] = abuf[0] = 0; cmfdbi(&sw, /* First FDB - switches */ _CMKEY, /* fcode */ "HTTP proxy server host[:port] or switch", "", /* default */ "", /* addtl string data */ nuserpass, /* addtl numeric data 1: tbl size */ 4, /* addtl numeric data 2: 4 = cmswi */ xxstring, /* Processing function */ userpass, /* Keyword table */ &tx /* Pointer to next FDB */ ); cmfdbi(&tx, _CMTXT, /* fcode */ "HTTP proxy server host[:port]", "", /* default */ "", /* addtl string data */ 0, /* addtl numeric data 1 */ 0, /* addtl numeric data 2 */ xxstring, NULL, NULL ); while (1) { if ((x = cmfdb(&sw)) < 0) { if (x == -3) { x = -9; printf("?Hostname required\n"); } return(x); } if (cmresult.fcode != _CMKEY) break; n = cmresult.nresult; switch (n) { case UPW_USER: case UPW_PASS: case UPW_AGENT: if ((x = cmfld((n == UPW_USER) ? "Username" : ((n == UPW_PASS) ? "Password" : "Agent"), "", &s, xxstring)) < 0) { if (x != -3) return(x); } ckstrncpy((n == UPW_USER) ? ubuf : ((n == UPW_PASS) ? pbuf : abuf), s, (n == UPW_AGENT) ? 256 : (LOGINLEN+1)); } } if (cmresult.fcode != _CMTXT) return(-2); s = cmresult.sresult; if (s) if (!*s) s = NULL; #ifdef IKSDCONF if (iksdcf) return(success = 0); #endif /* IKSDCONF */ makestr(&tcp_http_proxy_user,ubuf[0]?ubuf:NULL); makestr(&tcp_http_proxy_pwd,pbuf[0]?pbuf:NULL); makestr(&tcp_http_proxy_agent,abuf[0]?abuf:NULL); makestr(&tcp_http_proxy,s); memset(pbuf,0,sizeof(pbuf)); return(success = 1); } #endif /* NOHTTP */ /* It would have been easy to combine XYTCP_SOCKS_SVR with the previous one except for the #ifdefs... */ #ifdef NT #ifdef CK_SOCKS case XYTCP_SOCKS_SVR: { char ubuf[LOGINLEN+1], pbuf[LOGINLEN+1]; char * p = getenv("SOCKS_SERVER"); struct FDB sw, tx; int n, x; if (!p) p = ""; cmfdbi(&sw, /* First FDB - switches */ _CMKEY, /* fcode */ "SOCKS server host[:port] or switch", "", /* default */ "", /* addtl string data */ nuserpass, /* addtl numeric data 1: tbl size */ 4, /* addtl numeric data 2: 4 = cmswi */ xxstring, /* Processing function */ userpass, /* Keyword table */ &tx /* Pointer to next FDB */ ); cmfdbi(&tx, _CMTXT, /* fcode */ "SOCKS server host[:port]", p, /* default */ "", /* addtl string data */ 0, /* addtl numeric data 1 */ 0, /* addtl numeric data 2 */ xxstring, NULL, NULL ); while (1) { if ((x = cmfdb(&sw)) < 0) { if (x == -3) { x = -9; printf("?Hostname required\n"); } return(x); } if (cmresult.fcode != _CMKEY) break; n = cmresult.nresult; switch (n) { case UPW_USER: case UPW_PASS: if ((x = cmfld((n == UPW_USER) ? "Username" : "Password", "", &s, xxstring)) < 0) { if (x != -3) return(x); } ckstrncpy((n == UPW_USER) ? ubuf : pbuf, s, LOGINLEN+1); } } if (cmresult.fcode != _CMTXT) return(-2); s = cmresult.sresult; if (s) if (!*s) s = NULL; #ifdef IKSDCONF if (iksdcf) return(success = 0); #endif /* IKSDCONF */ makestr(&tcp_socks_user,ubuf); memset(pbuf,0,sizeof(pbuf)); makestr(&tcp_socks_svr,s); return(success = 1); } #ifdef CK_SOCKS_NS case XYTCP_SOCKS_NS: { char * p = getenv("SOCKS_NS"); if (!p) p = ""; if ((y = cmtxt("hostname or IP of SOCKS Name Server",p, &s,xxstring)) < 0) return(y); #ifdef IKSDCONF if (iksdcf) return(success = 0); #endif /* IKSDCONF */ if (tcp_socks_ns) { free(tcp_socks_ns); /* Free any previous storage */ tcp_socks_ns = NULL; } if (s == NULL || *s == NUL) { /* If none given */ tcp_socks_ns = NULL; /* remove the override string */ return(success = 1); } else if ((tcp_socks_ns = malloc(strlen(s)+1))) { strcpy(tcp_socks_ns,s); return(success = 1); } else return(success = 0); } #endif /* CK_SOCKS_NS */ #endif /* CK_SOCKS */ #endif /* NT */ case XYTCP_ADDRESS: if ((y = cmtxt("preferred IP Address for TCP connections","", &s,xxstring)) < 0) return(y); #ifdef IKSDCONF if (iksdcf) return(success = 0); #endif /* IKSDCONF */ if (tcp_address) { free(tcp_address); /* Free any previous storage */ tcp_address = NULL; } if (s == NULL || *s == NUL) { /* If none given */ tcp_address = NULL; /* remove the override string */ return(success = 1); } else if ((tcp_address = malloc(strlen(s)+1))) { strcpy(tcp_address,s); return(success = 1); } else return(success = 0); #ifdef SO_KEEPALIVE case XYTCP_KEEPALIVE: if ((z = cmkey(onoff,2,"","on",xxstring)) < 0) return(z); if ((y = cmcfm()) < 0) return(y); #ifdef IKSDCONF if (iksdcf) return(success = 0); #endif /* IKSDCONF */ #ifdef SSHBUILTIN if (network && nettype == NET_SSH && ssh_sock != -1) success = keepalive(ssh_sock,z); else #endif /* SSHBUILTIN */ success = keepalive(ttyfd,z); return(success); #endif /* SO_KEEPALIVE */ #ifdef SO_DONTROUTE case XYTCP_DONTROUTE: if ((z = cmkey(onoff,2,"","off",xxstring)) < 0) return(z); if ((y = cmcfm()) < 0) return(y); #ifdef IKSDCONF if (iksdcf) return(success = 0); #endif /* IKSDCONF */ #ifdef SSHBUILTIN if (network && nettype == NET_SSH && ssh_sock != -1) success = dontroute(ssh_sock,z); else #endif /* SSHBUILTIN */ success = dontroute(ttyfd,z); return(success); #endif /* SO_DONTROUTE */ #ifdef TCP_NODELAY case XYTCP_NODELAY: if ((z = cmkey(onoff,2,"","off",xxstring)) < 0) return(z); if ((y = cmcfm()) < 0) return(y); #ifdef IKSDCONF if (iksdcf) return(success = 0); #endif /* IKSDCONF */ #ifdef SSHBUILTIN if (network && nettype == NET_SSH && ssh_sock != -1) success = no_delay(ssh_sock,z); else #endif /* SSHBUILTIN */ success = no_delay(ttyfd,z); return(success); case XYTCP_NAGLE: /* The inverse of NODELAY */ if ((z = cmkey(onoff,2,"","on",xxstring)) < 0) return(z); if ((y = cmcfm()) < 0) return(y); #ifdef IKSDCONF if (iksdcf) return(success = 0); #endif /* IKSDCONF */ #ifdef SSHBUILTIN if (network && nettype == NET_SSH && ssh_sock != -1) success = no_delay(ssh_sock,z); else #endif /* SSHBUILTIN */ success = no_delay(ttyfd,!z); return(success); #endif /* TCP_NODELAY */ #ifdef SO_LINGER case XYTCP_LINGER: if ((z = cmkey(onoff,2,"","on",xxstring)) < 0) return(z); if (z) { /* if on, we need a timeout value */ if ((x = cmnum("Linger timeout in 10th of a millisecond", "0",10,&y,xxstring)) < 0) return(x); } else y = 0; if ((x = cmcfm()) < 0) return(x); #ifdef IKSDCONF if (iksdcf) return(success = 0); #endif /* IKSDCONF */ #ifdef SSHBUILTIN if (network && nettype == NET_SSH && ssh_sock != -1) success = ck_linger(ssh_sock,z,y); else #endif /* SSHBUILTIN */ success = ck_linger(ttyfd,z,y); return(success); #endif /* SO_LINGER */ #ifdef SO_SNDBUF case XYTCP_SENDBUF: x = cmnum("Send buffer size, bytes","8192",10,&z,xxstring); if (x < 0) return(x); if ((x = cmcfm()) < 0) return(x); #ifdef IKSDCONF if (iksdcf) return(success = 0); #endif /* IKSDCONF */ #ifdef SSHBUILTIN if (network && nettype == NET_SSH && ssh_sock != -1) success = sendbuf(ssh_sock,z); else #endif /* SSHBUILTIN */ success = sendbuf(ttyfd,z); return(success); #endif /* SO_SNDBUF */ #ifdef SO_RCVBUF case XYTCP_RECVBUF: x = cmnum("Receive buffer size, bytes","8192",10,&z,xxstring); if (x < 0) return(x); if ((x = cmcfm()) < 0) return(x); #ifdef IKSDCONF if (iksdcf) return(success = 0); #endif /* IKSDCONF */ /* Note: The following is not 16-bit safe */ #ifndef QNX16 if (x > 52248) { printf("?Warning: receive buffers larger than 52248 bytes\n"); printf(" may not be understood by all hosts. Performance\n"); printf(" may suffer.\n"); return(-9); } #endif /* QNX16 */ #ifdef SSHBUILTIN if (network && nettype == NET_SSH && ssh_sock != -1) success = recvbuf(ssh_sock,z); else #endif /* SSHBUILTIN */ success = recvbuf(ttyfd,z); return(success); #endif /* SO_RCVBUF */ #ifdef VMS #ifdef DEC_TCPIP case XYTCP_UCX: { /* UCX 2.0 port swabbing bug */ extern int ucx_port_bug; return(success = seton(&ucx_port_bug)); } #endif /* DEC_TCPIP */ #endif /* VMS */ case XYTCP_RDNS: { extern int tcp_rdns; return(success = setonaut(&tcp_rdns)); } #ifdef CK_DNS_SRV case XYTCP_DNS_SRV: { extern int tcp_dns_srv; return(success = seton(&tcp_dns_srv)); } #endif /* CK_DNS_SRV */ default: return(0); } } #endif /* TCPSOCKET */ #endif /* NOTCPOPTS */ #endif /* NETCONN */ } switch (xx) { #ifndef NOLOCAL #ifdef NETCONN case XYHOST: { /* SET HOST */ z = ttnproto; /* Save protocol in case of failure */ #ifdef DECNET if (nettype != NET_DEC) #endif /* DECNET */ ttnproto = NP_NONE; if ((y = setlin(XYHOST,1,0)) <= 0) { /* Sets success to 1 */ debug(F101,"SET HOST fail mdmtyp","",mdmtyp); ttnproto = z; /* Failed, restore protocol */ success = 0; } didsetlin++; debug(F101,"SET HOST OK mdmtyp","",mdmtyp); debug(F101,"SET HOST reliable","",reliable); return(y); } #endif /* NETCONN */ case XYLINE: /* SET LINE (= SET PORT) */ debug(F101,"setlin flow 1","",flow); x = setlin(xx,1,0); if (x > -1) didsetlin++; debug(F101,"SET LINE setlin","",x); debug(F101,"SET LINE flow","",flow); debug(F101,"SET LINE local","",local); debug(F101,"SET LINE reliable","",reliable); return(x); #endif /* NOLOCAL */ #ifndef NOSETKEY case XYKEY: /* SET KEY */ return(dosetkey()); #endif /* NOSETKEY */ #ifndef NOCSETS case XYLANG: /* Language */ if ((y = cmkey(lngtab,nlng,"","none",xxstring)) < 0) return(y); if ((x = cmcfm()) < 0) return(x); /* Look up language and get associated character sets */ for (i = 0; (i < nlangs) && (langs[i].id != y); i++) ; if (i >= nlangs) { printf("?internal error, sorry\n"); return(success = 0); } /* */ language = i; /* All good, set the language, */ return(success = 1); #endif /* NOCSETS */ #ifndef MAC case XYBACK: /* BACKGROUND */ if ((z = cmkey(onoff,2,"","",xxstring)) < 0) return(z); if ((y = cmcfm()) < 0) return(y); #ifdef COMMENT bgset = z; /* 0 = off (foreground) */ #ifdef VMS /* 1 = on (background) */ if (batch && bgset == 0) /* To enable echoing of commands */ ckxech = 1; /* in VMS batch logs */ #endif /* VMS */ #else /* COMMENT */ if (z) { /* 1 = Background */ bgset = 1; backgrd = 1; #ifdef VMS batch = 1; #endif /* VMS */ } else { /* 0 = Foreground */ bgset = 0; backgrd = 0; #ifdef VMS batch = 0; #endif /* VMS */ } #endif /* COMMENT */ success = 1; bgchk(); return(success); #endif /* MAC */ case XYQUIE: { /* QUIET */ #ifdef DCMDBUF extern int * xquiet; #else extern int xquiet[]; #endif /* DCMDBUF */ x = seton(&quiet); if (x < 0) return(x); xquiet[cmdlvl] = quiet; return(success = x); } #ifndef NOXFER case XYBUF: { /* BUFFERS */ #ifdef DYNAMIC int sb, rb; if ((y = cmnum("Send buffer size","",10,&sb,xxstring)) < 0) { if (y == -3) printf("?Buffer size required\n"); return(y); } if (sb < 0) { if (*atmbuf == '-') printf("?Negative numbers can't be used here\n"); else printf("?Integer overflow, use a smaller number please\n"); return(-9); } else if (sb < 80) { printf("?Too small\n"); return(-9); } if ((y=cmnum("Receive buffer size",ckitoa(sb),10,&rb,xxstring)) < 0) return(y); if (rb < 0) { if (*atmbuf == '-') printf("?Negative numbers can't be used here\n"); else printf("?Integer overflow, use a smaller number please\n"); return(-9); } else if (rb < 80) { printf("?Too small\n"); return(-9); } if ((y = cmcfm()) < 0) return(y); if ((y = inibufs(sb,rb)) < 0) return(y); y = adjpkl(urpsiz,wslotr,bigrbsiz); /* Maybe adjust packet sizes */ if (y != urpsiz) urpsiz = y; y = adjpkl(spsiz,wslotr,bigsbsiz); if (y != spsiz) spsiz = spmax = spsizr = y; return(success = 1); #else printf("?Sorry, not available\n"); return(success = 0); #endif /* DYNAMIC */ } case XYCHKT: /* BLOCK-CHECK */ if ((x = cmkey(chktab,nchkt,"","3",xxstring)) < 0) return(x); if ((y = cmcfm()) < 0) return(y); if (x == 5) { bctf = 1; #ifdef COMMENT printf("?5 - Not implemented yet\n"); return(success = 0); #endif /* COMMENT */ } bctr = x; /* Set local too even if REMOTE SET */ if (rmsflg) { if (x == 4) { tmpbuf[0] = 'B'; tmpbuf[1] = '\0'; } else ckstrncpy(tmpbuf,ckitoa(x),TMPBUFSIZ); sstate = setgen('S', "400", tmpbuf, ""); return((int) sstate); } else { return(success = 1); } #endif /* NOXFER */ #ifndef NOLOCAL #ifndef MAC /* The Mac has no RS-232 */ case XYCARR: /* CARRIER-WATCH */ return(setdcd()); #endif /* MAC */ #endif /* NOLOCAL */ } #ifdef TNCODE switch (xx) { /* Avoid long switch statements... */ case XYTELOP: { int c, n; /* Workers */ int getval = 0; /* Whether to get switch value */ int tnserver = 0; /* Client by default */ int opt = -1; /* Telnet Option */ struct FDB sw, op; /* FDBs for each parse function */ #ifdef CK_AUTHENTICATION extern int sl_topt_a_s_saved; extern int sl_topt_a_c_saved; extern int sl_topt_e_s_saved; extern int sl_topt_e_c_saved; #endif /* CK_AUTHENTICATION */ #ifdef IKSD if (inserver) /* Server by default when IKSD */ tnserver = 1; #endif /* IKSD */ /* Set up chained parse functions... */ cmfdbi(&op, /* First fdb - telopts*/ _CMKEY, /* fcode */ "/client, /server or", /* hlpmsg */ "", /* default */ "", /* addtl string data */ ntnopt, /* addtl numeric data 1 */ 0, /* addtl numeric data 2 */ xxstring, tnopttab, &sw ); cmfdbi(&sw, /* Second FDB - command switches */ _CMKEY, /* fcode */ "", /* hlpmsg */ "", /* default */ "", /* addtl string data */ ntnoptsw, /* addtl numeric data 1: tbl size */ 4, /* addtl numeric data 2: 4 = cmswi */ xxstring, /* Processing function */ tnoptsw, /* Keyword table */ NULL /* Pointer to next FDB */ ); while (opt < 0) { /* Parse 0 or more switches */ x = cmfdb(&op); /* Parse switch or other thing */ debug(F101,"XYTELOP cmfdb","",x); if (x < 0) /* Error */ return(x); /* or reparse needed */ if (cmresult.fcode != _CMKEY) /* Break out if not a switch */ break; c = cmgbrk(); /* Get break character */ getval = (c == ':' || c == '='); /* see how switch ended */ if (getval && !(cmresult.kflags & CM_ARG)) { printf("?This switch does not take arguments\n"); return(-9); } z = cmresult.nresult; /* Numeric result = switch value */ debug(F101,"XYTELOP switch","",z); switch (z) { /* Process the switch */ case CK_TN_CLIENT: tnserver = 0; break; case CK_TN_SERVER: tnserver = 1; break; case CK_TN_EC: opt = TELOPT_ECHO; break; case CK_TN_TT: opt = TELOPT_TTYPE; break; case CK_TN_BM: opt = TELOPT_BINARY; break; case CK_TN_ENV: opt = TELOPT_NEWENVIRON; break; case CK_TN_LOC: opt = TELOPT_SNDLOC; break; case CK_TN_AU: opt = TELOPT_AUTHENTICATION; break; case CK_TN_FX: opt = TELOPT_FORWARD_X; break; case CK_TN_ENC: opt = TELOPT_ENCRYPTION; break; case CK_TN_IKS: opt = TELOPT_KERMIT; break; case CK_TN_TLS: opt = TELOPT_START_TLS; break; case CK_TN_XD: opt = TELOPT_XDISPLOC; break; case CK_TN_NAWS: opt = TELOPT_NAWS; break; case CK_TN_SGA: opt = TELOPT_SGA; break; case CK_TN_PHR: opt = TELOPT_PRAGMA_HEARTBEAT; break; case CK_TN_PSP: opt = TELOPT_SSPI_LOGON; break; case CK_TN_PLG: opt = TELOPT_PRAGMA_LOGON; break; case CK_TN_SAK: opt = TELOPT_IBM_SAK; break; case CK_TN_CPC: opt = TELOPT_COMPORT; break; case CK_TN_LOG: opt = TELOPT_LOGOUT; break; case CK_TN_FLW: opt = TELOPT_LFLOW; break; default: printf("?Unexpected value - %d\n",z); return(-9); } #ifdef COMMENT if (cmresult.fdbaddr == &op) break; #endif /* COMMENT */ } switch (opt) { case TELOPT_ECHO: /* Options only the Server WILL */ case TELOPT_FORWARD_X: case TELOPT_SEND_URL: case TELOPT_IBM_SAK: case TELOPT_LOGOUT: if ((x = cmkey(tnnegtab, ntnnegtab, "desired server state", TELOPT_MODE(tnserver?TELOPT_DEF_S_ME_MODE(opt):TELOPT_DEF_C_U_MODE(opt)), xxstring) ) < 0) return(x); if ((z = cmcfm()) < 0) return(z); if (tnserver) { TELOPT_DEF_S_ME_MODE(opt) = x; TELOPT_ME_MODE(opt) = x; } else { TELOPT_DEF_C_U_MODE(opt) = x; TELOPT_U_MODE(opt) = x; } break; case TELOPT_TTYPE: /* Options only the Client WILL */ case TELOPT_NEWENVIRON: case TELOPT_SNDLOC: case TELOPT_AUTHENTICATION: case TELOPT_START_TLS: case TELOPT_XDISPLOC: case TELOPT_NAWS: case TELOPT_LFLOW: case TELOPT_COMPORT: if ((x = cmkey(tnnegtab, ntnnegtab, "desired client state", TELOPT_MODE(!tnserver?TELOPT_DEF_S_U_MODE(opt):TELOPT_DEF_C_ME_MODE(opt)), xxstring) ) < 0) return(x); if ((z = cmcfm()) < 0) return(z); if (tnserver) { TELOPT_DEF_S_U_MODE(opt) = x; TELOPT_U_MODE(opt) = x; #ifdef CK_AUTHENTICATION if (opt == TELOPT_AUTHENTICATION) sl_topt_a_s_saved = 0; #endif /* CK_AUTHENTICATION */ } else { TELOPT_DEF_C_ME_MODE(opt) = x; TELOPT_ME_MODE(opt) = x; #ifdef CK_AUTHENTICATION if (opt == TELOPT_AUTHENTICATION) sl_topt_a_c_saved = 0; #endif /* CK_AUTHENTICATION */ } break; default: if ((x = cmkey(tnnegtab, ntnnegtab, tnserver ? "desired server state" : "desired client state", TELOPT_MODE(tnserver?TELOPT_DEF_S_ME_MODE(opt):TELOPT_DEF_C_ME_MODE(opt)), xxstring ) ) < 0) return(x); if ((y = cmkey(tnnegtab, ntnnegtab, !tnserver ? "desired server state" : "desired client state", TELOPT_MODE(!tnserver?TELOPT_DEF_S_U_MODE(opt):TELOPT_DEF_C_U_MODE(opt)), xxstring ) ) < 0) return(y); if ((z = cmcfm()) < 0) return(z); if (tnserver) { TELOPT_DEF_S_ME_MODE(opt) = x; TELOPT_ME_MODE(opt) = x; TELOPT_DEF_S_U_MODE(opt) = y; TELOPT_U_MODE(opt) = y; #ifdef CK_ENCRYPTION if (opt == TELOPT_ENCRYPTION) sl_topt_e_s_saved = 0; #endif /* CK_ENCRYPTION */ } else { TELOPT_DEF_C_ME_MODE(opt) = x; TELOPT_ME_MODE(opt) = x; TELOPT_DEF_C_U_MODE(opt) = y; TELOPT_U_MODE(opt) = y; #ifdef CK_ENCRYPTION if (opt == TELOPT_ENCRYPTION) sl_topt_e_c_saved = 0; #endif /* CK_ENCRYPTION */ } } return(success = 1); } case XYTEL: /* TELNET */ if ((z = cmkey(tntab,ntn,"parameter for TELNET negotiations", "", xxstring)) < 0) return(z); switch (z) { case CK_TN_EC: /* ECHO */ if ((x = cmkey(rltab,nrlt, "initial TELNET echoing state", "local",xxstring)) < 0) return(x); if ((y = cmcfm()) < 0) return(y); tn_duplex = x; return(success = 1); case CK_TN_RE: /* REMOTE-ECHO */ return(success = seton(&tn_rem_echo)); case CK_TN_DB: /* DEBUG */ return(success = seton(&tn_deb)); case CK_TN_TT: /* TERMINAL TYPE */ if ((y = cmtxt("terminal type for TELNET connections","", &s,xxstring)) < 0) return(y); if (tn_term) { free(tn_term); /* Free any previous storage */ tn_term = NULL; } if (s == NULL || *s == NUL) { /* If none given */ tn_term = NULL; /* remove the override string */ return(success = 1); } else if ((tn_term = malloc(strlen(s)+1))) { strcpy(tn_term,s); return(success = 1); } else return(success = 0); #ifdef CK_FORWARD_X case CK_TN_FX: /* FORWARD-X */ if ((x=cmkey(tnfwdxtab,ntnfwdx,"","xauthority-file",xxstring)) < 0) return(x); switch (x) { case 0: { /* Xauthority-File */ x = cmifi("Full path of .Xauthority file","",&s,&y,xxstring); if (x < 0 && x != -3) return(x); makestr(&tn_fwdx_xauthority,s); return(success = 1); } case 1: { /* No-Encryption */ extern int fwdx_no_encrypt; return(success = seton(&fwdx_no_encrypt)); } } return(success = 0); #endif /* CK_FORWARD_X */ case CK_TN_NL: /* TELNET NEWLINE-MODE */ if ((x = cmkey(tn_nlmtab,ntn_nlm,"","nvt",xxstring)) < 0) return(x); if (x == TN_NL_BIN) { if ((x = cmkey(tnlmtab,ntnlm,"","raw",xxstring)) < 0) return(x); if ((y = cmcfm()) < 0) return(y); tn_b_nlm = x; return(success = 1); } else if (x == TN_NL_NVT) { if ((x = cmkey(tnlmtab,ntnlm,"","on",xxstring)) < 0) return(x); if ((y = cmcfm()) < 0) return(y); tn_nlm = x; return(success = 1); } else { if ((y = cmcfm()) < 0) return(y); tn_nlm = x; return(success = 1); } case CK_TN_XF: /* BINARY-TRANSFER-MODE */ if ((z = cmkey(onoff,2,"","on",xxstring)) < 0) return(z); if ((y = cmcfm()) < 0) return(y); tn_b_xfer = z; return(success = 1); case CK_TN_NE: /* NO-ENCRYPT-DURING-XFER */ if ((z = cmkey(onoff,2,"","on",xxstring)) < 0) return(z); if ((y = cmcfm()) < 0) return(y); #ifdef CK_APC /* Don't let this be set remotely */ if (apcactive == APC_LOCAL || (apcactive == APC_REMOTE && !(apcstatus & APC_UNCH))) return(success = 0); #endif /* CK_APC */ tn_no_encrypt_xfer = z; return(success = 1); case CK_TN_BM: /* BINARY-MODE */ if ((x = cmkey(tnnegtab,ntnnegtab,"","refused",xxstring)) < 0) return(x); if ((y = cmcfm()) < 0) return(y); TELOPT_DEF_S_ME_MODE(TELOPT_BINARY) = x; TELOPT_DEF_S_U_MODE(TELOPT_BINARY) = x; TELOPT_DEF_C_ME_MODE(TELOPT_BINARY) = x; TELOPT_DEF_C_U_MODE(TELOPT_BINARY) = x; return(success = 1); #ifdef IKS_OPTION case CK_TN_IKS: /* KERMIT */ if ((x = cmkey(tnnegtab,ntnnegtab,"DO","accept",xxstring)) < 0) return(x); if ((y = cmkey(tnnegtab,ntnnegtab,"WILL","accept",xxstring)) < 0) return(y); if ((z = cmcfm()) < 0) return(z); TELOPT_DEF_S_ME_MODE(TELOPT_KERMIT) = y; TELOPT_DEF_S_U_MODE(TELOPT_KERMIT) = x; TELOPT_DEF_C_ME_MODE(TELOPT_KERMIT) = y; TELOPT_DEF_C_U_MODE(TELOPT_KERMIT) = x; return(success = 1); #endif /* IKS_OPTION */ #ifdef CK_SSL case CK_TN_TLS: /* START_TLS */ if ((x = cmkey(tnnegtab,ntnnegtab,"me","accept",xxstring)) < 0) return(x); if ((y = cmkey(tnnegtab,ntnnegtab,"u","accept",xxstring)) < 0) return(y); if ((z = cmcfm()) < 0) return(z); TELOPT_DEF_S_ME_MODE(TELOPT_START_TLS) = x; TELOPT_DEF_S_U_MODE(TELOPT_START_TLS) = y; TELOPT_DEF_C_ME_MODE(TELOPT_START_TLS) = x; TELOPT_DEF_C_U_MODE(TELOPT_START_TLS) = y; return(success = 1); #endif /* CK_SSL */ #ifdef CK_NAWS case CK_TN_NAWS: /* NAWS */ if ((x = cmkey(tnnegtab,ntnnegtab,"me","accept",xxstring)) < 0) return(x); if ((y = cmkey(tnnegtab,ntnnegtab,"u","accept",xxstring)) < 0) return(y); if ((z = cmcfm()) < 0) return(z); TELOPT_DEF_S_ME_MODE(TELOPT_NAWS) = x; TELOPT_DEF_S_U_MODE(TELOPT_NAWS) = y; TELOPT_DEF_C_ME_MODE(TELOPT_NAWS) = x; TELOPT_DEF_C_U_MODE(TELOPT_NAWS) = y; return(success = 1); #endif /* CK_NAWS */ #ifdef CK_AUTHENTICATION case CK_TN_AU: /* AUTHENTICATION */ if ((x = cmkey(tnauthtab,ntnauth,"","",xxstring)) < 0) return(x); if (x == TN_AU_FWD) { extern int forward_flag; return(success = seton(&forward_flag)); } else if (x == TN_AU_TYP) { extern int auth_type_user[]; extern int sl_auth_type_user[]; extern int sl_auth_saved; int i, j, atypes[AUTHTYPLSTSZ]; for (i = 0; i < AUTHTYPLSTSZ; i++) { if ((y = cmkey(autyptab,nautyp,"", i == 0 ? "automatic" : "" , xxstring)) < 0) { if (y == -3) break; return(y); } if (i > 0 && (y == AUTHTYPE_AUTO || y == AUTHTYPE_NULL)) { printf( "\r\n?Choice may only be used in first position.\r\n"); return(-9); } for (j = 0; j < i; j++) { if (atypes[j] == y) { printf("\r\n?Choice has already been used.\r\n"); return(-9); } } atypes[i] = y; if (y == AUTHTYPE_NULL || y == AUTHTYPE_AUTO) { i++; break; } } if (i < AUTHTYPLSTSZ) atypes[i] = AUTHTYPE_NULL; if ((z = cmcfm()) < 0) return(z); sl_auth_saved = 0; for (i = 0; i < AUTHTYPLSTSZ; i++) { auth_type_user[i] = atypes[i]; sl_auth_type_user[i] = 0; } } else if (x == TN_AU_HOW) { if ((y = cmkey(auhowtab,nauhow,"","any",xxstring)) < 0) return(y); if ((z = cmcfm()) < 0) return(z); tn_auth_how = y; } else if (x == TN_AU_ENC) { if ((y = cmkey(auenctab,nauenc,"","encrypt",xxstring)) < 0) return(y); if ((z = cmcfm()) < 0) return(z); tn_auth_enc = y; } else { if ((y = cmcfm()) < 0) return(y); TELOPT_DEF_C_ME_MODE(TELOPT_AUTHENTICATION) = x; TELOPT_DEF_S_U_MODE(TELOPT_AUTHENTICATION) = x; } return(success = 1); #endif /* CK_AUTHENTICATION */ #ifdef CK_ENCRYPTION case CK_TN_ENC: { /* ENCRYPTION */ int c, tmp = -1; int getval = 0; static struct keytab * tnetbl = NULL; static int ntnetbl = 0; if ((y = cmkey(tnenctab,ntnenc,"","",xxstring)) < 0) return(y); switch (y) { case TN_EN_TYP: x = ck_get_crypt_table(&tnetbl,&ntnetbl); debug(F101,"ck_get_crypt_table x","",x); debug(F101,"ck_get_crypt_table n","",ntnetbl); if (x < 1 || !tnetbl || ntnetbl < 1) /* Didn't get it */ x = 0; if (!x) { printf("?Oops, types not loaded\n"); return(-9); } if ((x = cmkey(tnetbl,ntnetbl,"type of encryption", "automatic",xxstring)) < 0) return(x); if ((z = cmcfm()) < 0) return(z); cx_type = x; sl_cx_type = 0; break; case TN_EN_START: if ((z = cmcfm()) < 0) return(z); #ifdef CK_APC /* Don't let this be set remotely */ if (apcactive == APC_LOCAL || apcactive == APC_REMOTE && !(apcstatus & APC_UNCH)) return(success = 0); #endif /* CK_APC */ ck_tn_enc_start(); break; case TN_EN_STOP: if ((z = cmcfm()) < 0) return(z); #ifdef CK_APC /* Don't let this be set remotely */ if (apcactive == APC_LOCAL || apcactive == APC_REMOTE && !(apcstatus & APC_UNCH)) return(success = 0); #endif /* CK_APC */ ck_tn_enc_stop(); break; default: if ((z = cmcfm()) < 0) return(z); TELOPT_DEF_C_ME_MODE(TELOPT_ENCRYPTION) = y; TELOPT_DEF_C_U_MODE(TELOPT_ENCRYPTION) = y; TELOPT_DEF_S_ME_MODE(TELOPT_ENCRYPTION) = y; TELOPT_DEF_S_U_MODE(TELOPT_ENCRYPTION) = y; } return(success = 1); } #endif /* CK_ENCRYPTION */ case CK_TN_BUG: /* BUG */ if ((x = cmkey(tnbugtab,4,"", "binary-me-means-u-too",xxstring)) < 0) return(x); if ((z = cmkey(onoff,2,"","off",xxstring)) < 0) return(z); if ((y = cmcfm()) < 0) return(y); switch (x) { case 0: tn_b_meu = z; break; case 1: tn_b_ume = z; break; case 2: tn_infinite = z; break; case 3: tn_sb_bug = z; break; case 4: tn_auth_krb5_des_bug = z; break; } return(success = 1); #ifdef CK_ENVIRONMENT case CK_TN_XD: /* XDISPLOC */ if ((x = cmkey(tnnegtab,ntnnegtab,"me","accept",xxstring)) < 0) return(x); if ((y = cmkey(tnnegtab,ntnnegtab,"u","accept",xxstring)) < 0) return(y); if ((z = cmcfm()) < 0) return(z); TELOPT_DEF_S_ME_MODE(TELOPT_XDISPLOC) = x; TELOPT_DEF_S_U_MODE(TELOPT_XDISPLOC) = y; TELOPT_DEF_C_ME_MODE(TELOPT_XDISPLOC) = x; TELOPT_DEF_C_U_MODE(TELOPT_XDISPLOC) = y; return(success = 1); case CK_TN_ENV: { char * msg = "value of telnet environment variable"; extern int tn_env_flg; extern char tn_env_acct[], tn_env_disp[], tn_env_job[], tn_env_prnt[], tn_env_sys[]; extern char * tn_loc; if ((x = cmkey(tnenvtab,ntnenv,"","",xxstring)) < 0) return(x); if (x == TN_ENV_UVAR) { /* User variables */ char * uvar=NULL; char * uval=NULL; char * env; extern char * tn_env_uservar[8][2]; /* Get the user variable name */ if ((x = cmfld("Name of Environment Variable","",&s, xxstring)) < 0) return(x); makestr(&uvar,s); env = getenv(uvar); if (!env) env = ""; if ((x = cmtxt("Value of Environment Variable",env, &s,xxstring)) < 0) return(x); if (*s) makestr(&uval,s); /* Now that we have the variable and perhaps a value */ /* there are three possibilities: (1) new variable */ /* and associated value; (2) variable already exists */ /* but we have a new value; (3) variable already */ /* exists but no new value therefore the user wants */ /* to clear variable. */ /* Try to find an existing variable */ for (x = 0; x < 8; x++) { if (!ckstrcmp(tn_env_uservar[x][0],uvar,-1,0)) { if (uval) { free(tn_env_uservar[x][1]); tn_env_uservar[x][1] = uval; free(uvar); return(success = 1); } else { free(tn_env_uservar[x][0]); tn_env_uservar[x][0] = NULL; free(tn_env_uservar[x][1]); tn_env_uservar[x][1] = NULL; free(uvar); return(success = 1); } } } /* Couldn't find one; look for empty location to insert */ for (x = 0; x < 8; x++) { if (!tn_env_uservar[x][0]) { tn_env_uservar[x][0] = uvar; tn_env_uservar[x][1] = uval; return(success = 1); } } printf("?Sorry, no space for variable.\n"); return(success = 0); } if (x == TN_ENV_OFF || x == TN_ENV_ON) { if ((y = cmcfm()) < 0) return(y); #ifdef IKSD if (inserver) { printf("?Sorry, command disabled.\r\n"); return(success = 0); } #endif /* IKSD */ tn_env_flg = x == TN_ENV_OFF ? 0 : 1; return(success = 1); } /* Not ON/OFF - Get the value */ z = cmdgquo(); cmdsquo(0); if ((y = cmtxt(msg, "", &s, xxstring)) < 0) { cmdsquo(z); return(y); } cmdsquo(z); #ifdef IKSD if (inserver) return(success = 0); #endif /* IKSD */ if ((int)strlen(s) > 63) { printf("Sorry, too long\n"); return(-9); } switch (x) { case TN_ENV_USR: ckstrncpy(uidbuf,s,UIDBUFLEN); sl_uid_saved = 0; break; case TN_ENV_ACCT: ckstrncpy(tn_env_acct,s,64); break; case TN_ENV_DISP: ckstrncpy(tn_env_disp,s,64); break; case TN_ENV_JOB: ckstrncpy(tn_env_job,s,64); break; case TN_ENV_PRNT: ckstrncpy(tn_env_prnt,s,64); break; case TN_ENV_SYS: ckstrncpy(tn_env_sys,s,64); break; case TN_ENV_LOC: if (!*s) s = NULL; makestr(&tn_loc,s); break; case TN_ENV_UVAR: printf("\n?Not yet implemented\n"); break; } return(success = 1); } #endif /* CK_ENVIRONMENT */ #ifdef CK_SNDLOC case CK_TN_LOC: { /* LOCATION */ extern char * tn_loc; if ((y = cmtxt("Location string","",&s,xxstring)) < 0) return(y); if (!*s) s = NULL; makestr(&tn_loc,s); return(success = 1); } #endif /* CK_SNDLOC */ case CK_TN_SFU: /* Microsoft SFU compatibility */ if ((z = cmkey(onoff,2,"","on",xxstring)) < 0) return(z); if ((y = cmcfm()) < 0) return(y); tn_sfu = z; return(success = 1); break; case CK_TN_WAIT: /* WAIT-FOR-NEGOTIATIONS */ if ((z = cmkey(onoff,2,"","on",xxstring)) < 0) return(z); if ((y = cmcfm()) < 0) return(y); #ifdef IKSD if (inserver && #ifdef IKSDCONF iksdcf #else 1 #endif /* IKSDCONF */ ) { printf("?Sorry, command disabled.\r\n"); return(success = 0); } #endif /* IKSD */ tn_wait_flg = z; sl_tn_saved = 0; return(success = 1); case CK_TN_DL: /* DELAY SUBNEGOTIATIONS */ if ((z = cmkey(onoff,2,"","on",xxstring)) < 0) return(z); if ((y = cmcfm()) < 0) return(y); #ifdef IKSD if (inserver && #ifdef IKSDCONF iksdcf #else 1 #endif /* IKSDCONF */ ) { printf("?Sorry, command disabled.\r\n"); return(success = 0); } #endif /* IKSD */ tn_delay_sb = z; return(success = 1); case CK_TN_PUID: { /* PROMPT-FOR-USERID */ int i,len; if ((y = cmtxt("Prompt string","",&s,xxstring)) < 0) return(y); s = brstrip(s); /* we must check to make sure there are no % fields */ len = strlen(s); for (i = 0; i < len; i++) { if (s[i] == '%') { if (s[i+1] != '%') { printf("%% fields are not used in this command.\n"); return(-9); } i++; } } makestr(&tn_pr_uid,s); return(success = 1); } default: return(-2); } } #endif /* TNCODE */ switch (xx) { #ifndef NOSPL case XYCOUN: /* SET COUNT */ x = cmnum("Positive number","0",10,&z,xxstring); if (x < 0) return(x); if ((x = cmcfm()) < 0) return(x); if (z < 0) { printf("?A positive number, please\n"); return(0); } debug(F101,"XYCOUN: z","",z); return(success = setnum(&count[cmdlvl],z,0,10000)); #endif /* NOSPL */ #ifndef NOSPL case XYCASE: return(success = seton(&inpcas[cmdlvl])); #endif /* NOSPL */ case XYCMD: /* COMMAND ... */ if ((y = cmkey(scmdtab,nbytt,"","",xxstring)) < 0) return(y); switch (y) { case SCMD_CBR: if ((y = cmcfm()) < 0) return(y); concb((char)escape); return(success = 1); case SCMD_BSZ: if ((y = cmnum("bytesize for command characters, 7 or 8","7",10,&x, xxstring)) < 0) return(y); if (x != 7 && x != 8) { printf("\n?The choices are 7 and 8\n"); return(success = 0); } if ((y = cmcfm()) < 0) return(y); if (x == 7) cmdmsk = 0177; else if (x == 8) cmdmsk = 0377; return(success = 1); #ifdef CK_RECALL case SCMD_RCL: if ((y = cmnum("maximum number of commands in recall buffer","10", 10,&x,xxstring)) < 0) return(y); if ((y = cmcfm()) < 0) return(y); return(success = cmrini(x)); #endif /* CK_RECALL */ #ifdef CK_RECALL case SCMD_RTR: return(success = seton(&cm_retry)); #endif /* CK_RECALL */ case SCMD_MOR: /* More-prompting */ success = seton(&xaskmore); if (success) saveask = xaskmore; return(success); case SCMD_QUO: if ((x = seton(&y)) < 0) return(x); cmdsquo(y); /* Do it the right way */ cmd_quoting = y; /* Also keep a global copy */ /* Set string-processing function */ #ifdef datageneral xxstring = y ? zzstring : (xx_strp) NULL; #else #ifdef CK_ANSIC xxstring = y ? zzstring : (xx_strp) NULL; #else xxstring = y ? zzstring : (xx_strp) NULL; #endif /* CK_ANSIC */ #endif /* datageneral */ return(success = 1); #ifdef OS2 #ifndef NOLOCAL case SCMD_COL: { /* Command-screen colors */ int fg, bg; fg = cmkey(ttyclrtab, nclrs, "foreground color and then background color", "white", xxstring); if (fg < 0) return(fg); if ((bg = cmkey(ttyclrtab,nclrs, "background color","black",xxstring)) < 0) return(bg); if ((y = cmcfm()) < 0) return(y); colorcmd = fg | bg << 4; return(success = 1); } case SCMD_SCR: /* Command Scrollback size */ if ((y = cmnum("COMMAND scrollback buffer size, lines","512",10,&x, xxstring)) < 0) return(y); /* The max number of lines is the RAM */ /* we can actually dedicate to a */ /* scrollback buffer given the maximum */ /* process memory space of 512MB */ if (x < 256 || x > 2000000L) { printf("\n?The size must be between 256 and 2,000,000.\n"); return(success = 0); } if ((y = cmcfm()) < 0) return(y); tt_scrsize[VCMD] = x; VscrnInit( VCMD ); return(success = 1); case SCMD_WID: { if ((y = cmnum("Number of columns in display window", "80",10,&x,xxstring)) < 0) return(y); if ((y = cmcfm()) < 0) return(y); os2_setcmdwidth(x); return(success = 1); } case SCMD_HIG: if ((y = cmnum("Number of rows in display window", "24",10,&x,xxstring)) < 0) return(y); if ((y = cmcfm()) < 0) return(y); os2_setcmdheight(x); return(success = 1); case SCMD_STA: { extern int marginbot; if ((y = cmkey(onoff,2,"","on",xxstring)) < 0) return(y); if ((x = cmcfm()) < 0) return(x); if (y != tt_status[VCMD]) { /* Might need to fixup the margins */ tt_status[VCMD] = y; if (y) { tt_szchng[VCMD] = 2; tt_rows[VCMD]--; cmd_rows--; VscrnInit(VCMD); /* Height set here */ printf("\n"); } else { tt_szchng[VCMD] = 1; tt_rows[VCMD]++; cmd_rows++; VscrnInit(VCMD); /* Height set here */ } } return(success = 1); } case SCMD_CUR: { int row, col; position * ppos; ppos = VscrnGetCurPos(VCMD); #ifdef NT #define itoa _itoa #endif /* NT */ itoa(ppos->y+1, tmpbuf, 10); if ((y = cmnum("row (1-based)",tmpbuf,10,&row,xxstring)) < 0) return(y); itoa(ppos->x+1, tmpbuf, 10); if ((y = cmnum("col (1-based)",tmpbuf,10,&col,xxstring)) < 0) return(y); if ((x = cmcfm()) < 0) return(x); lgotoxy( VCMD, col, row ) ; VscrnIsDirty( VCMD ); return(success=1); } #endif /* NOLOCAL */ #else case SCMD_WID: y = cmnum("Command screen width, characters","80",10,&x,xxstring); return(setnum(&cmd_cols,x,y,1024)); case SCMD_HIG: y = cmnum("Command screen height, rows","24",10,&x,xxstring); return(setnum(&cmd_rows,x,y,1024)); #endif /* OS2 */ case SCMD_INT: return(seton(&cmdint)); #ifdef CK_AUTODL case SCMD_ADL: return(seton(&cmdadl)); #endif /* CK_AUTODL */ #ifdef DOUBLEQUOTING case SCMD_DBQ: { extern int dblquo; return(seton(&dblquo)); } #endif /* DOUBLEQUOTING */ case SCMD_ERR: y = cmnum("Error message verbosity level, 0-3","1",10,&x,xxstring); return(setnum(&cmd_err,x,y,3)); case SCMD_VAR: return(setvareval()); default: return(-2); } } switch (xx) { case XYDFLT: /* SET DEFAULT = CD */ return(success = docd(XXCWD)); case XYDEBU: /* SET DEBUG { on, off, session } */ if ((y = cmkey(dbgtab,ndbg,"","",xxstring)) < 0) return(y); if (y == DEB_TIM) #ifdef COMMENT return(seton(&debtim) < 0 ? x : (success = 1)); #else /* why this change? */ return(success = seton(&debtim)); #endif /* COMMENT */ #ifdef IKSD if (inserver && isguest) { printf("?Sorry, command disabled.\r\n"); return(success = 0); } #endif /* IKSD */ switch (y) { case DEB_LEN: y = cmnum("Max length for debug log strings","",10,&x,xxstring); if ((z = setnum(&debxlen,x,y,-1)) < 0) return(z); if ((x = cmcfm()) < 0) return(x); return(success = 1); case DEB_OFF: if ((x = cmcfm()) < 0) return(x); #ifndef NOLOCAL setdebses(0); #endif /* NOLOCAL */ #ifdef DEBUG if (deblog) doclslog(LOGD); #endif /* DEBUG */ return(success = 1); case DEB_ON: if ((x = cmcfm()) < 0) return(x); #ifdef DEBUG deblog = debopn("debug.log", 0); return(success = deblog ? 1 : 0); #else printf("?Sorry, debug log feature not enabled\n"); return(success = 0); #endif /* DEBUG */ case DEB_SES: if ((x = cmcfm()) < 0) return(x); #ifndef NOLOCAL setdebses(1); #endif /* NOLOCAL */ return(success = 1); case DEB_MSG: /* Debug messages 2010/03/12 */ if ((y = cmkey(ooetab,nooetab,"","on",xxstring)) < 0) return(y); if ((x = cmcfm()) < 0) return(x); debmsg = y; return(1); } break; #ifndef NOXFER case XYDELA: /* SET DELAY */ y = cmnum("Number of seconds before starting to send", "5",10,&x,xxstring); if (x < 0) x = 0; return(success = setnum(&ckdelay,x,y,999)); #endif /* NOXFER */ default: break; } switch (xx) { #ifdef CK_TAPI case XYTAPI: return(settapi()); #endif /* CK_TAPI */ #ifndef NODIAL case XYDIAL: /* SET MODEM or SET DIAL */ return(setdial(-1)); case XYMODM: return(setmodem()); #ifdef COMMENT /* not implemented yet */ case XYANSWER: /* SET ANSWER */ return(setanswer()); #endif /* COMMENT */ #endif /* NODIAL */ #ifndef NOLOCAL case XYDUPL: /* SET DUPLEX */ if ((y = cmkey(dpxtab,2,"","full",xxstring)) < 0) return(y); if ((x = cmcfm()) < 0) return(x); duplex = y; return(success = 1); case XYLCLE: /* LOCAL-ECHO (= DUPLEX) */ return(success = seton(&duplex)); case XYESC: /* SET ESCAPE */ return(success = setcc(ckitoa(DFESC),&escape)); #endif /* NOLOCAL */ case XYEXIT: /* SET EXIT */ if ((z = cmkey(xittab,nexit,"","",xxstring)) < 0) return(z); switch (z) { case 0: /* STATUS */ y = cmnum("EXIT status code","",10,&x,xxstring); return(success = setnum(&xitsta,x,y,-1)); case 1: /* WARNING */ if ((z = cmkey(xitwtab,nexitw,"","",xxstring)) < 0) return(z); if ((y = cmcfm()) < 0) return(y); setexitwarn(z); return(success = 1); case 2: success = seton(&exitonclose); #ifdef TCPSOCKET if (success) tn_exit = exitonclose; #endif /* TCPSOCKET */ return(success); case 3: { extern int exithangup; return((success = seton(&exithangup))); } default: return(-2); } /* End of SET EXIT switch() */ default: break; } switch (xx) { case XYFILE: /* SET FILE */ return(setfil(rmsflg)); case XYFLOW: { /* FLOW-CONTROL */ extern int cxflow[]; struct FDB k1, k2; int tncomport = 0; char * m; #ifdef TN_COMPORT if (network && istncomport()) tncomport = 1; #endif /* TN_COMPORT */ if (tncomport) { m = "Flow control type, one of the following:\n\ dtr/cd dtr/cts keep none rts/cts xon/xoff\n\ or connection type"; } else { /* All this is because chained FDB's don't give chained help yet */ m = #ifdef Plan9 #ifdef CK_RTSCTS "Flow control type, one of the following:\n\ keep none rts/cts\n\ or connection type", #else "Flow control type, one of the following:\n\ keep none\n\ or connection type"; #endif /* CK_RTSCTS */ #else #ifdef CK_RTSCTS #ifdef CK_DTRCD #ifdef CK_DTRCTS "Flow control type, one of the following:\n\ dtr/cd dtr/cts keep none rts/cts xon/xoff\n\ or connection type"; #else /* CK_DTRCTS */ "Flow control type, one of the following:\n\ dtr/cd keep none rts/cts xon/xoff\n\ or connection type"; #endif /* CK_DTRCTS */ #else /* CK_DTRCD */ #ifdef CK_DTRCTS "Flow control type, one of the following:\n\ dtr/cts keep none rts/cts xon/xoff\n\ or connection type"; #else /* CK_DTRCTS */ "Flow control type, one of the following:\n\ keep none rts/cts xon/xoff\n\ or connection type"; #endif /* CK_DTRCTS */ #endif /* CK_DTRCD */ #else "Flow control type, one of the following:\n\ keep none xon/xoff\n\ or connection type"; #endif /* CK_RTSCTS */ #endif /* Plan9 */ } cmfdbi(&k1,_CMKEY,m,"","",ncxtypesw, 4, xxstring, cxtypesw, &k2); cmfdbi(&k2, _CMKEY, "", "", "", #ifdef TN_COMPORT (tncomport ? ntnflo : nflo), #else nflo, #endif /* TN_COMPORT */ 0, xxstring, #ifdef TN_COMPORT (tncomport ? tnflotab : flotab), #else flotab, #endif /* TN_COMPORT */ NULL ); x = cmfdb(&k1); if (x < 0) { /* Error */ if (x == -2 || x == -9) printf("?No keywords or switches match: \"%s\"\n",atmbuf); return(x); } z = cmresult.nresult; /* Keyword value */ if (cmresult.fdbaddr == &k2) { /* Flow-control type keyword table */ if ((x = cmcfm()) < 0) /* Set it immediately */ return(x); flow = z; debug(F101,"set flow","",flow); #ifdef CK_SPEED if (flow == FLO_XONX) /* Xon/Xoff forces prefixing */ ctlp[XON] = ctlp[XOFF] = ctlp[XON+128] = ctlp[XOFF+128] = 1; #endif /* CK_SPEED */ autoflow = (flow == FLO_AUTO); return(success = 1); /* Done */ } debug(F101,"set flow /blah 1","",z); /* SET FLOW /for-what */ if ((y = cmkey(flotab,nflo,"Flow control type","none",xxstring)) < 0) return(y); if ((x = cmcfm()) < 0) return(x); debug(F101,"set flow /blah 2","",y); if (y == FLO_AUTO) { printf( "?Sorry, \"automatic\" can not be assigned to a connection type.\n"); return(-9); } else if (z >= 0 && z <= CXT_MAX) cxflow[z] = y; debug(F101,"set flow","",flow); debug(F101,"set flow autoflow","",autoflow); return(success = 1); } case XYHAND: /* HANDSHAKE */ if ((y = cmkey(hshtab,nhsh,"","none",xxstring)) < 0) return(y); if (y == 998) { if ((x = cmnum("ASCII value","",10,&y,xxstring)) < 0) return(x); if ((y < 1) || ((y > 31) && (y != 127))) { printf("?Character must be in ASCII control range\n"); return(-9); } } if ((x = cmcfm()) < 0) return(x); turn = (y > 0127) ? 0 : 1; turnch = y; return(success = 1); #ifndef NOSPL case XYMACR: /* SET MACRO */ if ((y = cmkey(smactab,2,"","",xxstring)) < 0) return(y); switch (y) { case 0: return(success = seton(&mecho)); case 1: return(success = seton(&merror[cmdlvl])); default: return(-2); } #endif /* NOSPL */ case XYMSGS: #ifdef VMS if ((z = cmkey(onoff,2,"","",xxstring)) < 0) return(z); if ((y = cmcfm()) < 0) return(y); vms_msgs = z; printf("Sorry, SET MESSAGES not implemented yet\n"); return(success = 0); #endif /* VMS */ default: break; } switch (xx) { case XYPARI: /* PARITY */ if ((y = cmkey(partbl,npar,"","none",xxstring)) < 0) return(y); /* If parity not none, then we also want 8th-bit prefixing */ #ifdef HWPARITY if (y == 'H') { /* Hardware */ if ((x = cmkey(hwpartbl,nhwpar,"","even",xxstring)) < 0) return(x); } #endif /* HWPARITY */ if ((z = cmcfm()) < 0) return(z); #ifdef HWPARITY if (y == 'H') { /* 8 data bits plus hardware parity */ parity = 0; #ifndef NOXFER ebqflg = 0; #endif /* NOXFER */ hwparity = x; } else { /* 7 data bits + software parity */ hwparity = 0; #endif /* HWPARITY */ parity = y; #ifndef NOXFER ebqflg = (parity) ? 1 : 0; #endif /* NOXFER */ #ifdef HWPARITY } #endif /* HWPARITY */ #ifdef TN_COMPORT if (network && istncomport()) tnsettings(parity, 0); #endif /* TN_COMPORT */ return(success = 1); #ifndef NOFRILLS case XYPROM: /* SET PROMPT */ /* Note: xxstring not invoked here. Instead, it is invoked every time the prompt is issued. This allows the prompt string to contain variables that can change, like \v(dir), \v(time), etc. */ ckmakmsg(line, /* Default might have trailing space */ LINBUFSIZ, "{", inserver ? ikprompt : ckprompt, "}", NULL ); if ((x = cmtxt("Program's command prompt",line,&s,NULL)) < 0) return(x); s = brstrip(s); /* Remove enclosing braces, if any */ cmsetp(s); /* Set the prompt */ return(success = 1); #endif /* NOFRILLS */ #ifndef NOXFER case XYRETR: /* RETRY: per-packet retry limit */ y = cmnum("Maximum retries per packet","10",10,&x,xxstring); if (x < 0) x = 0; if ((x = setnum(&maxtry,x,y,999)) < 0) return(x); #ifdef COMMENT if (maxtry <= wslotr) { printf("?Retry limit must be greater than window size\n"); return(success = 0); } #endif /* COMMENT */ if (rmsflg) { sstate = setgen('S', "403", ckitoa(maxtry), ""); return((int) sstate); } else return(success = x); #endif /* NOXFER */ #ifndef NOSERVER case XYSERV: /* SET SERVER items */ if ((y = cmkey(srvtab,nsrvt,"","",xxstring)) < 0) return(y); switch (y) { case XYSERI: if ((y = cmnum("Number of seconds, or 0 for no idle timeout", "0",10,&x,xxstring)) < 0) return(y); if (x < 0) x = 0; if ((y = cmcfm()) < 0) return(y); #ifndef OS2 srvtim = 0; #endif /* OS2 */ srvidl = x; return(success = 1); case XYSERT: if ((y = cmnum("Interval for server NAKs, 0 = none", ckitoa(DSRVTIM), 10,&x, xxstring)) < 0) return(y); if (x < 0) { printf( "\n?Specify a positive number, or 0 for no server NAKs\n"); return(0); } if ((y = cmcfm()) < 0) return(y); if (rmsflg) { sstate = setgen('S', "404", ckitoa(x), ""); return((int) sstate); } else { #ifndef OS2 srvidl = 0; #endif /* OS2 */ srvtim = x; /* Set the server timeout variable */ return(success = 1); } case XYSERD: /* SERVER DISPLAY */ return(success = seton(&srvdis)); /* ON or OFF... */ #ifndef NOSPL case XYSERP: /* SERVER GET-PATH */ return(parsdir(2)); #endif /* NOSPL */ case XYSERL: /* SERVER LOGIN */ return(cklogin()); case XYSERC: /* SERVER CD-MESSAGE */ x = rmsflg ? cmkey(onoff,2,"","",xxstring) : cmkey(cdmsg,3,"","",xxstring); if (x < 0) return(x); if (x == 2) { /* CD-MESSAGE FILE */ if ((x = cmtxt("Name of file","",&s,NULL)) < 0) return(x); if (!*s) { s = NULL; srvcdmsg = 0; } makestr(&cdmsgstr,s); makelist(cdmsgstr,cdmsgfile,8); return(success = 1); } if ((y = cmcfm()) < 0) /* CD-MESSAGE ON/OFF */ return(y); if (rmsflg) { sstate = setgen('S', "420", x ? "1" : "0", ""); return((int) sstate); } else { if (x > 0) srvcdmsg |= 1; else srvcdmsg &= 2; return(success = 1); } case XYSERK: /* SERVER KEEPALIVE */ return(success = seton(&srvping)); /* ON or OFF... */ default: return(-2); } #endif /* NOSERVER */ } switch (xx) { #ifdef UNIX #ifndef NOJC case XYSUSP: /* SET SUSPEND */ seton(&xsuspend); /* on or off... */ return(success = 1); #endif /* NOJC */ #endif /* UNIX */ case XYTAKE: /* SET TAKE */ if ((y = cmkey(taktab,4,"","",xxstring)) < 0) return(y); switch (y) { case 0: return(success = seton(&techo)); #ifndef NOSPL case 1: return(success = seton(&takerr[cmdlvl])); #else case 1: return(success = seton(&takerr[tlevel])); #endif /* NOSPL */ case 2: techo = 0; return(success = 1); /* For compatibility with */ case 3: techo = 1; return(success = 1); /* MS-DOS Kermit */ default: return(-2); } #ifndef NOSCRIPT case XYSCRI: /* SET SCRIPT */ if ((y = cmkey(scrtab,1,"","echo",xxstring)) < 0) return(y); switch (y) { case 0: return(success = seton(&secho)); default: return(-2); } #endif /* NOSCRIPT */ default: break; } #ifndef NOLOCAL switch (xx) { case XYTERM: /* SET TERMINAL */ x = settrm(); success = (x > 0) ? 1 : 0; return(x); #ifdef NT case XYWIN95: /* SET WIN95 workarounds */ x = setwin95(); success = (x > 0 ? 1 : 0); return(x); #endif /* NT */ #ifdef OS2 case XYDLR: /* SET DIALER workarounds */ x = setdialer(); success = (x > 0 ? 1 : 0); return(x); case XYTITLE: /* SET TITLE of window */ x = settitle(); success = (x > 0 ? 1 : 0); return(x); #endif /* OS2 */ #ifdef OS2MOUSE case XYMOUSE: /* SET MOUSE */ return(success = setmou()); #endif /* OS2MOUSE */ case XYBELL: /* SET BELL */ return(success = setbell()); #ifdef OS2 case XYPRTY: return(success = setprty() ); #endif /* OS2 */ default: break; } #endif /* NOLOCAL */ switch (xx) { /* SET SEND/RECEIVE protocol parameters. */ #ifndef NOXFER case XYRECV: case XYSEND: return(setsr(xx,rmsflg)); #endif /* NOXFER */ #ifndef NOLOCAL case XYSESS: /* SESSION-LOG */ if ((x = cmkey(sfttab,nsfttab,"type of file", #ifdef OS2 "binary", #else /* OS2 */ "text", #endif /* OS2 */ xxstring ) ) < 0) return(x); if ((y = cmcfm()) < 0) return(y); if (x == 999) { /* TIMESTAMPED-TEXT */ sessft = XYFT_T; /* Implies text */ slogts = 1; /* and timestamps */ } else if (x == 998) { /* NULL-PADDED-LINES */ slognul = 1; /* adds NUL after ^J */ } else { /* A regular type */ sessft = x; /* The type */ slogts = 0; /* No timestampes */ } return(success = 1); case XYSPEE: /* SET SPEED */ lp = line; if (local && !network) { ckmakmsg(lp, LINBUFSIZ, "Transmission rate for ", ttname, " (bits per second)", NULL ); } else { ckstrncpy(lp, "Serial-port speed (bits per second)", LINBUFSIZ ); } zz = -1L; #ifdef TN_COMPORT if (network && istncomport()) x = cmkey(tnspdtab,ntnspd,line,"",xxstring); else #endif /* TN_COMPORT */ x = cmkey(spdtab,nspd,line,"",xxstring); if (x < 0) { if (x == -3) printf("?value required\n"); #ifdef USETCSETSPEED /* In this case, any number can be tried */ /* There's a parse error message but the request still goes thru */ if (rdigits(atmbuf)) zz = atol(atmbuf); else #endif /* USETCSETSPEED */ return(x); } if ((y = cmcfm()) < 0) return(y); #ifdef IKSD if (inserver) { printf("?Sorry, command disabled.\r\n"); return(success = 0); } #endif /* IKSD */ if (!local) { printf("?SET SPEED has no effect without prior SET LINE\n"); return(success = 0); } else if (network #ifdef TN_COMPORT && !istncomport() #endif /* TN_COMPORT */ ) { printf("\n?Speed cannot be set for network connections\n"); return(success = 0); } /* Note: This way of handling speeds is not 16-bit safe for speeds greater than 230400. The argument to ttsspd() should have been a long. */ #ifdef USETCSETSPEED if (zz > -1L) x = zz / 10L; #endif /* USETCSETSPEED */ zz = (long) x * 10L; if (zz == 130L) zz = 134L; if (zz == 70L) zz = 75L; /* (see spdtab[] definition) */ if (ttsspd(x) < 0) { /* Call ttsspd with cps, not bps! */ printf("?Unsupported line speed - %ld\n",zz); return(success = 0); } else { #ifdef CK_TAPI if (!tttapi || tapipass) speed = ttgspd(); /* Read it back */ else speed = zz; #else /* CK_TAPI */ speed = ttgspd(); /* Read it back */ #endif /* CK_TAPI */ if (speed != zz) { /* Call ttsspd with cps, not bps! */ printf("?SET SPEED fails, speed is %ld\n",speed); return(success = 0); } if (pflag && !xcmdsrc) { if (speed == 8880) printf("%s, 75/1200 bps\n",ttname); else if (speed == 134) printf("%s, 134.5 bps\n",ttname); else printf("%s, %ld bps\n",ttname,speed); } return(success = 1); } #endif /* NOLOCAL */ #ifndef NOXFER case XYXFER: /* SET TRANSFER */ if ((y = cmkey(rmsflg ? rtstab : tstab, /* (or REMOTE SET TRANSFER) */ rmsflg ? nrts : nts, "","character-set",xxstring)) < 0) return(y); switch (y) { #ifdef XFRCAN case XYX_CAN: /* CANCELLATION */ if ((z = cmkey(onoff,2,"","",xxstring)) < 0) return(z); if (z == 0) { /* OFF */ if ((y = cmcfm()) < 0) return(y); xfrcan = 0; } else { if ((y = cmnum("ASCII code for cancellation character", "3",10,&x, xxstring)) < 0) return(y); if (x > 31 && x != 127) { printf("Cancel character must be 0-31 or 127\n"); return(-9); } if ((y = cmnum("How many required to cause cancellation", "2",10,&z, xxstring)) < 0) return(y); if (z < 2) { printf("Number must be 2 or greater\n"); return(-9); } if ((y = cmcfm()) < 0) return(y); xfrcan = 1; /* CANCELLATION ON */ xfrchr = x; /* Using this character */ xfrnum = z; /* Needing this many of them */ } return(success = 1); #endif /* XFRCAN */ #ifndef NOCSETS case XYX_CSE: /* CHARACTER-SET */ if ((y = cmkey(tcstab,ntcs,"","transparent",xxstring)) < 0) return(y); if ((x = cmcfm()) < 0) return(x); if (rmsflg) { sstate = setgen('S', "405", tcsinfo[y].designator, ""); return((int) sstate); } else { extern int s_cset, fcharset, axcset[], tcs_save; tslevel = (y == TC_TRANSP) ? 0 : 1; /* transfer syntax level */ xfrxla = tslevel; tcharset = y; /* transfer character set */ /* SEND CHARACTER-SET AUTO */ if (tslevel > 0 && s_cset == XMODE_A) if (y > -1 && y <= MAXTCSETS) if (axcset[y] > -1 && axcset[y] > MAXFCSETS) fcharset = axcset[y]; /* Auto-pick file charset */ setxlatype(tcharset,fcharset); /* Translation type */ tcs_save = -1; return(success = 1); } #endif /* NOCSETS */ case XYX_LSH: /* LOCKING-SHIFT */ if ((y = cmkey(lstab,nls,"","on",xxstring)) < 0) return(y); if ((x = cmcfm()) < 0) return(x); lscapr = (y == 1) ? 1 : 0; /* ON: requested = 1 */ lscapu = (y == 2) ? 2 : 0; /* FORCED: used = 1 */ return(success = 1); /* #ifdef CK_XYZ */ case XYX_PRO: /* Protocol */ #ifndef OS2 if (inserver) { printf("?Sorry, only Kermit protocol is available\n"); return(-9); } #endif /* OS2 */ return(setproto()); /* #endif */ /* CK_XYZ */ case XYX_MOD: /* Mode */ if ((y = cmkey(xfrmtab,2,"","automatic",xxstring)) < 0) return(y); if ((x = cmcfm()) < 0) return(x); if (rmsflg) { sstate = setgen('S', "410", y == XMODE_A ? "0" : "1", ""); return((int)sstate); } g_xfermode = y; xfermode = y; #ifdef NEWFTP if (ftpisopen()) { /* If an FTP connection is open */ extern int ftp_xfermode; /* change its transfer mode too */ ftp_xfermode = xfermode; } #endif /* NEWFTP */ return(success = 1); #ifndef NOLOCAL case XYX_DIS: /* Display */ return(doxdis(1)); /* 1 == Kermit */ #endif /* NOLOCAL */ case XYX_SLO: /* Slow-start */ return(seton(&slostart)); #ifndef NOSPL case XYX_CRC: /* CRC */ return(seton(&docrc)); #endif /* NOSPL */ case XYX_BEL: /* Bell */ return(seton(&xfrbel)); #ifdef PIPESEND case XYX_PIP: /* Pipes */ #ifndef NOPUSH if (nopush) { #endif /* NOPUSH */ printf("Sorry, access to pipes is disabled\n"); return(-9); #ifndef NOPUSH } else #endif /* NOPUSH */ return(seton(&usepipes)); #endif /* PIPESEND */ case XYX_INT: /* Interruption */ return(seton(&xfrint)); case XYX_XLA: return(seton(&xfrxla)); /* Translation */ case XYX_MSG: { extern char * xfrmsg; if ((x = cmtxt("Prompt string","",&s,xxstring)) < 0) return(x); if (!*s) s = NULL; makestr(&xfrmsg,s); return(success = 1); } case XYX_RPT: { extern int whereflg; return(seton(&whereflg)); } default: return(-2); } #endif /* NOXFER */ } switch (xx) { #ifndef NOXMIT case XYXMIT: /* SET TRANSMIT */ return(setxmit()); #endif /* NOXMIT */ #ifndef NOXFER #ifndef NOCSETS case XYUNCS: /* UNKNOWN-CHARACTER-SET */ if ((y = cmkey(ifdtab,2,"","discard",xxstring)) < 0) return(y); if ((x = cmcfm()) < 0) return(x); unkcs = y; return(success = 1); #endif /* NOCSETS */ #endif /* NOXFER */ #ifndef NOPUSH #ifdef UNIX case XYWILD: /* WILDCARD-EXPANSION */ if ((y = cmkey(wildtab,nwild, "Wildcard expansion option","on",xxstring)) < 0) return(y); if ((z = cmkey(wdottab, 2, "whether to match filenames that start with \".\"", "/no-match-dot-files", xxstring) ) < 0) return(z); if ((x = cmcfm()) < 0) return(x); if (nopush) { if (y > 0) { printf("Shell expansion is disabled\n"); return(success = 0); } } switch (y) { case WILD_ON: wildena = 1; break; case WILD_OFF: wildena = 0; break; case WILD_KER: wildxpand = 0; /* These are the previous */ break; /* hardwired values */ case WILD_SHE: wildxpand = 1; break; } matchdot = z; return(success = 1); #endif /* UNIX */ #endif /* NOPUSH */ #ifndef NOXFER case XYWIND: /* WINDOW-SLOTS */ if (protocol == PROTO_K) { y = cmnum("Window size for Kermit protocol, 1 to 32", "1", 10, &x, xxstring); y = setnum(&z,x,y,MAXWS); /* == 32 */ } #ifdef CK_XYZ else if (protocol == PROTO_Z) { y = cmnum("Window size for ZMODEM protocol, 0 to 65535", "0", 10, &x, xxstring); y = setnum(&z,x,y,65535); } #endif /* CK_XYZ */ else { y = cmnum("Window size for current protocol", "", 10, &x, xxstring); y = setnum(&z,x,y,65472); /* Doesn't matter - we won't use it */ } if (y < 0) return(y); if (protocol == PROTO_K) { if (z < 1) z = 1; } #ifdef CK_XYZ else if (protocol == PROTO_Z) { /* Zmodem windowing is closer to Kermit packet length */ /* than Kermit window size. If Window size is zero */ /* an end of frame and CRC is sent only at the end of */ /* the file (default). Otherwise, an End of Frame */ /* and CRC are sent after Window Size number of bytes */ if (z < 0) /* Disable windowing */ z = 0; } else { printf("?SET WINDOW does not apply to %s protocol\n", ptab[protocol].p_name ); } #endif /* CK_XYZ */ #ifdef COMMENT /* This is taken care of automatically now in protocol negotiation */ if (maxtry < z) { printf("?Window slots must be less than retry limit\n"); return(success = 0); } #endif /* COMMENT */ if (protocol == PROTO_K && rmsflg) { /* Set remote window size */ wslotr = z; /* Set local window size too */ ptab[protocol].winsize = wslotr; sstate = setgen('S', "406", ckitoa(z), ""); return((int) sstate); } wslotr = z; /* Set requested window size here */ ptab[protocol].winsize = wslotr; /* and in protocol-specific table */ if (protocol == PROTO_K) { /* And for Kermit only... */ swcapr = (wslotr > 1) ? 1 : 0; /* set window bit in capas word */ if (wslotr > 1) { /* Window size > 1? */ /* Maybe adjust packet size */ y = adjpkl(urpsiz,wslotr,bigrbsiz); if (y != urpsiz) { /* Did it change? */ urpsiz = y; if (msgflg) printf( " Adjusting receive packet-length to %d for %d window slots\n", urpsiz, wslotr ); } } } return(success = 1); #endif /* NOXFER */ } switch (xx) { #ifndef NOSPL case XYOUTP: /* OUTPUT command parameters */ if ((y = cmkey(outptab,noutptab,"OUTPUT command parameter","pacing", xxstring)) < 0) return(y); switch(y) { /* Which parameter */ case OUT_PAC: /* PACING */ y = cmnum("Milliseconds to pause between each OUTPUT character", "100", 10,&x,xxstring); y = setnum(&z,x,y,16383); /* Verify and get confirmation */ if (y < 0) return(y); if (z < 0) z = 0; /* (save some space) */ pacing = z; return(success = 1); case OUT_ESC: /* Special-escapes */ return(seton(&outesc)); default: /* (shouldn't happen) */ return(-2); } #endif /* NOSPL */ #ifdef CK_SPEED case XYQCTL: { short *p; int zz; if ((z = cmkey(ctltab,2, "control-character prefixing option","" ,xxstring)) < 0) return(z); /* Make space for a temporary copy of the prefixing table */ p = (short *)malloc(256 * sizeof(short)); if (!p) { printf("?Internal error - malloc failure\n"); return(-9); } for (i = 0; i < 256; i++) p[i] = ctlp[i]; /* Copy current table */ switch (z) { case 0: /* UNPREFIXED control character */ case 1: /* PREFIXED control character */ while (1) { /* Collect a list of numbers */ #ifndef NOSPL x_ifnum = 1; /* Turn off complaints from eval() */ #endif /* NOSPL */ if ((x = cmnum((z == 0) ? "\n Numeric ASCII value of control character that needs NO prefix,\n\ or the word \"all\", or carriage return to complete the list" : "\n Numeric ASCII value of control character that MUST BE prefixed,\n\ or the word \"all\", or carriage return to complete the list", "",10,&y,xxstring )) < 0) { #ifndef NOSPL x_ifnum = 0; #endif /* NOSPL */ if (x == -3) { if ((x = cmcfm()) < 0) return(x); break; } if (x == -2) { if (p) { free((char *)p); p = NULL; } debug(F110,"SET CONTROL atmbuf",atmbuf,0); if (!ckstrcmp(atmbuf,"all",3,0) || /* "ALL" */ !ckstrcmp(atmbuf,"al",2,0) || !ckstrcmp(atmbuf,"a",1,0)) { if ((x = cmcfm()) < 0) /* Get confirmation */ return(x); prefixing = z ? PX_ALL : PX_NON; setprefix(prefixing); return(success = 1); } else { /* Not number, not ALL */ printf( "?Please specify a number or the word ALL\n"); return(-9); } } else { if (p) free((char *)p); return(x); } } #ifndef NOSPL x_ifnum = 0; #endif /* NOSPL */ #ifdef UNPREFIXZERO zz = 0; #else #ifndef OS2 zz = 1 - z; #else zz = 0; /* Allow 0 (but only for Zmodem) */ #endif /* OS2 */ #endif /* UNPREFIXZERO */ /* printf("x = %d, y = %d, z = %d, zz = %d\n", x,y,z,zz); */ if ((y > 31 && y < 127) || /* A specific numeric value */ (y > 159 && y < 255) || /* Check that it is a valid */ (y < zz) || /* control code. */ (y > 255)) { printf("?Values allowed are: %d-31, 127-159, 255\n",zz); if (p) free((char *)p); return(-9); } x = y & 127; /* Get 7-bit value */ if (z == 0) { /* If they are saying it is safe... */ /* If flow control is Xon/Xoff */ if (((flow == FLO_XONX) && /* XON & XOFF chars not safe. */ (x == XON || x == XOFF)) ) { if (msgflg) printf( "Sorry, not while Xon/Xoff is in effect.\n"); if (p) free((char *)p); return(-9); } #ifdef TNCODE else if (network && IS_TELNET() && (y == CR || (unsigned) y == (unsigned) 255)) { if (msgflg) printf("Sorry, not on a TELNET connection.\n"); if (p) free((char *)p); return(-9); } #endif /* TNCODE */ } p[y] = (char) z; /* All OK, set flag */ } /* End of while loop */ /* Get here only if they have made no mistakes. Copy temporary table back to permanent one, then free temporary table and return successfully. */ for (i = 0; i < 256; i++) ctlp[i] = p[i]; if (p) free((char *)p); if (z > 0) clearrq = 0; /* 199 (see SET PREFIXING) */ return(success = 1); default: return(-2); } } #endif /* CK_SPEED */ } switch (xx) { #ifndef NOXFER case XYREPT: if ((y = cmkey(rpttab,2, "repeat-count compression parameter","",xxstring)) < 0) return(y); switch(y) { case 0: return(success = seton(&rptena)); /* REPEAT COUNTS = ON, OFF */ case 1: /* REPEAT MININUM number */ printf("(not implemented yet, nothing happens)\n"); return(-9); case 2: /* REPEAT PREFIX char */ if ((x = cmnum("ASCII value","",10,&z,xxstring)) < 0) return(x); if ((x = cmcfm()) < 0) return(x); if ((z > 32 && z < 63) || (z > 95 && z < 127)) { if (y == 1) rptmin = (CHAR) z; else myrptq = (CHAR) z; return(success = 1); } else { printf("?Illegal value for prefix character\n"); return(-9); } } #endif /* NOXFER */ #ifndef NOSPL case XYALRM: { #ifndef COMMENT int yy; long zz; zz = -1L; yy = x_ifnum; x_ifnum = 1; /* Turn off internal complaints */ y = cmnum("Seconds from now, or time of day as hh:mm:ss", "0" ,10, &x, xxstring); x_ifnum = yy; if (y < 0) { if (y == -2) { /* Invalid number or expression */ zz = tod2sec(atmbuf); /* Convert to secs since midnight */ if (zz < 0L) { printf("?Number, expression, or time of day required\n"); return(-9); } else { char now[32]; /* Current time */ char *p; long tnow; p = now; ztime(&p); tnow = atol(p+11) * 3600L + atol(p+14) * 60L + atol(p+17); if (zz < tnow) /* User's time before now */ zz += 86400L; /* So make it tomorrow */ zz -= tnow; /* Seconds from now. */ } } else return(y); } if (x < 0) { printf("?Alarm time is in the past.\n"); return(-9); } if ((y = cmcfm()) < 0) return(y); if (zz > -1L) { /* Time of day given? */ x = zz; if (zz != (long) x) { printf( "Sorry, arithmetic overflow - hh:mm:ss not usable on this platform.\n" ); return(-9); } } return(setalarm((long)x)); } #else /* This is to allow long values where int and long are not the same, e.g. on 16-bit systems. But something is wrong with it. */ if ((y = cmtxt("seconds from now", "0", &s, xxstring)) < 0) return(y); if (rdigits(s)) { return(setalarm(atol(s))); } else { printf("%s - not a number\n",s); return(-9); } #endif /* COMMENT */ #endif /* NOSPL */ #ifndef NOXFER case XYPROTO: return(setproto()); #endif /* NOXFER */ /* C-Kermit unprefixes control characters automatically on network connections if CLEAR-CHANNEL is ON, which it is by default. But not all network connections are transparent to all control characters. For example, the DEC-20, even when you TELNET to it, is sensitive to Ctrl-O and Ctrl-T. If you tell C-Kermit to SET CONTROL PREFIX 15 and/or 20, it doesn't help because CLEAR-CHANNEL is still in effect. If the user goes to the trouble to set up some prefixing, then Kermit should do what the user said. In C-Kermit 7.1 Alpha.03 we change the code to set clearrq to 0 if the user gives a SET PREFIXING or SET CONTROL PREFIX command. */ #ifdef CK_SPEED case XYPREFIX: { #ifdef COMMENT extern int clearrq; #endif /* COMMENT */ if ((z = cmkey(pfxtab, 4, "control-character prefixing option", "", xxstring)) < 0) return(z); if ((x = cmcfm()) < 0) return(x); clearrq = 0; /* 199 */ setprefix(z); #ifdef COMMENT if (hints && (z == PX_ALL || z == PX_CAU) && clearrq) { printf("Hint: Use SET CLEAR-CHANNEL OFF to disable negotiation of\n"); printf(" SET PREFIXING NONE during file transfers on reliable\n"); printf(" connections.\n"); } #endif /* COMMENT */ return(success = 1); } #endif /* CK_SPEED */ #ifndef NOSPL case XYLOGIN: if ((z = cmkey(logintab, 3, "value for login script","userid", xxstring)) < 0) return(z); x = cmdgquo(); if (z == LOGI_PSW) cmdsquo(0); if ((y = cmtxt("text","", &s, NULL)) < 0) { cmdsquo(x); return(y); } cmdsquo(x); #ifdef IKSD if (inserver) return(success = 0); #endif /* IKSD */ s = brstrip(s); if ((int)strlen(s) > 63) { printf("Sorry, too long\n"); return(-9); } switch(z) { case LOGI_UID: ckstrncpy(uidbuf,s,UIDBUFLEN); sl_uid_saved = 0; break; case LOGI_PSW: ckstrncpy(pwbuf,s,PWBUFL); if (pwbuf[0]) { pwflg = 1; #ifdef OS2 pwcrypt = 1; #else /* OS2 */ pwcrypt = 0; #endif /* OS2 */ } break; case LOGI_PRM: ckstrncpy(prmbuf,s,PWBUFL); } return(success = 1); #endif /* NOSPL */ } switch (xx) { case XYSTARTUP: if ((y = cmkey(ifdtab,2,"","discard",xxstring)) < 0) return(y); if ((x = cmcfm()) < 0) return(x); DeleteStartupFile = (y != 0) ? 0 : 1; return(success = 1); case XYTMPDIR: x = cmdir("Name of temporary directory","",&s,xxstring); if (x == -3) s = ""; else if (x < 0) return(x); if ((x = cmcfm()) < 0) return(x); makestr(&tempdir,s); return(tempdir ? 1 : 0); #ifndef NOXFER case XYDEST: /* DESTINATION */ return(setdest()); #endif /* NOXFER */ #ifndef NOPUSH #ifndef NOFRILLS /* Editor, Browser, and FTP Client */ case XYEDIT: /* EDITOR */ #ifdef IKSD if (inserver) { printf("?Sorry, command disabled.\r\n"); return(success = 0); } #endif /* IKSD */ #ifdef CK_APC /* Don't let this be set remotely */ if (apcactive == APC_LOCAL || (apcactive == APC_REMOTE && !(apcstatus & APC_UNCH))) return(success = 0); #endif /* CK_APC */ #ifdef OS2ORUNIX { char *p = getenv("PATH"); char *e; e = editor[0] ? (char *) editor : getenv("EDITOR"); if (!e) e = ""; if (p) x = cmifip("Name of preferred editor",e,&s,&y,0,p,xxstring); else x = cmifi("Full path of preferred editor",e,&s,&y,xxstring); if (x < 0 && x != -3) return(x); } #else #ifdef VMS if ((y = cmtxt("DCL command for editing", "edit", &s, NULL)) < 0) { if (x != -3) return(x); } #else if ((x = cmifi("Full path of preferred editor","",&s,&y,xxstring))<0) { if (x != -3) return(x); } #endif /* VMS */ #endif /* OS2ORUNIX */ #ifdef VMS ckstrncpy(editor,s,CKMAXPATH); editopts[0] = NUL; #else if (y != 0) { printf("?A single file please\n"); return(-2); } ckstrncpy(line,s,LINBUFSIZ); if ((x = cmtxt("editor command-line options","",&s,NULL)) < 0) return(x); ckstrncpy(tmpbuf,s,TMPBUFSIZ); if ((z = cmcfm()) < 0) return(z); if (line[0]) { zfnqfp(line,CKMAXPATH,editor); ckstrncpy(editopts,tmpbuf,128); } else { editor[0] = NUL; editopts[0] = NUL; } #endif /* VMS */ return(success = 1); #ifndef NOFTP #ifndef SYSFTP #ifdef TCPSOCKET case XYFTPX: return(dosetftp()); /* SET FTP */ #endif /* TCPSOCKET */ #endif /* SYSFTP */ #endif /* NOFTP */ #ifdef BROWSER #ifndef NOFTP #ifdef SYSFTP case XYFTP: /* SET FTP-CLIENT */ #endif /* SYSFTP */ #endif /* NOFTP */ case XYBROWSE: /* SET BROWSER */ { char *p = getenv("PATH"); char *app = (char *) browser, *opts = (char *) browsopts; #ifndef NOFTP #ifdef SYSFTP extern char ftpapp[], ftpopts[]; if (xx == XYFTP) { app = (char *)ftpapp; opts = (char *)ftpopts; } #endif /* SYSFTP */ #endif /* NOFTP */ #ifdef IKSD if (inserver) { printf("?Sorry, command disabled.\r\n"); return(success = 0); } #endif /* IKSD */ #ifdef CK_APC /* Don't let this be set remotely */ if (apcactive == APC_LOCAL || (apcactive == APC_REMOTE && !(apcstatus & APC_UNCH))) return(success = 0); #endif /* CK_APC */ #ifdef OS2ORUNIX if (p) x = cmifip(xx == XYBROWSE ? "Name of preferred browser" : "Name of preferred ftp client", #ifdef OS2 xx == XYFTP ? "ftp.exe" : "" #else xx == XYFTP ? "ftp" : "" #endif /* OS2 */ ,&s,&y,0,p,xxstring ); else x = cmifi(xx == XYBROWSE ? "Full path of preferred browser" : "Full path of preferred ftp client", "",&s,&y,xxstring ); if (x < 0 && x != -3) return(x); #else #ifdef VMS if ((x = cmtxt("DCL command to start your preferred Web browser", "", &s, NULL)) < 0) { if (x != -3) return(x); } #else if ((x = cmifi("Full path of preferred browser","",&s,&y,xxstring) ) < 0) { if (x != -3) return(x); } #endif /* VMS */ #endif /* OS2ORUNIX */ #ifdef VMS ckstrncpy(app,s,CKMAXPATH); *opts = NUL; #else if (y != 0) { printf("?A single file please\n"); return(-2); } ckstrncpy(line,s,LINBUFSIZ); if ((x = cmtxt(xx == XYBROWSE ? "browser command-line options" : "ftp client command-line options", "",&s,NULL) ) < 0) return(x); ckstrncpy(tmpbuf,s,TMPBUFSIZ); if ((z = cmcfm()) < 0) return(z); if (line[0]) { zfnqfp(line,CKMAXPATH,app); ckstrncpy(opts, tmpbuf, 128); } else { *app = NUL; *opts = NUL; } #endif /* VMS */ return(success = 1); } #endif /* BROWSER */ #endif /* NOFRILLS */ #endif /* NOPUSH */ #ifdef CK_CTRLZ case XYEOF: { /* SET EOF */ extern int eofmethod; extern struct keytab eoftab[]; if ((x = cmkey(eoftab,3,"end-of-file detection method","", xxstring)) < 0) return(x); if ((y = cmcfm()) < 0) return(y); eofmethod = x; return(success = 1); } #endif /* CK_CTRLZ */ #ifdef SESLIMIT case XYLIMIT: { /* Session-Limit (length of session in seconds) */ extern int seslimit; #ifdef OS2 extern int downloaded; #endif /* OS2 */ y = cmnum("Maximum length of session, seconds","0",10,&x,xxstring); if (inserver && #ifdef IKSDCONF iksdcf #else 1 #endif /* IKSDCONF */ #ifdef OS2 || downloaded #endif /* OS2 */ ) { if ((z = cmcfm()) < 0) return(z); printf("?Sorry, command disabled.\r\n"); return(success = 0); } return(setnum(&seslimit,x,y,86400)); } #endif /* SESLIMIT */ case XYRELY: { /* SET RELIABLE */ if ((x = cmkey(ooatab,3,"","automatic",xxstring)) < 0) return(x); if ((y = cmcfm()) < 0) return(y); reliable = x; setreliable = (x != SET_AUTO); debug(F101,"SET RELIABLE reliable","",reliable); return(success = 1); } #ifdef STREAMING case XYSTREAM: { /* SET STREAMING */ extern int streamrq; if ((x = cmkey(ooatab,3,"","automatic",xxstring)) < 0) return(x); if ((y = cmcfm()) < 0) return(y); streamrq = x; return(success = 1); } #endif /* STREAMING */ #ifdef CKSYSLOG case XYSYSL: { if ((x = cmkey(syslogtab,nsyslog,"","",xxstring)) < 0) return(x); if ((y = cmcfm()) < 0) return(y); #ifdef IKSD if (inserver && #ifdef IKSDCONF iksdcf #else 1 #endif /* IKSDCONF */ ) { printf("?Sorry, command disabled.\n"); return(success = 0); } #endif /* IKSD */ #ifdef CK_APC /* Don't let this be set remotely */ if (apcactive == APC_LOCAL || (apcactive == APC_REMOTE && !(apcstatus & APC_UNCH))) return(success = 0); #endif /* CK_APC */ ckxsyslog = x; return(success = 1); } #endif /* CKSYSLOG */ #ifdef TLOG case XYTLOG: { /* SET TRANSACTION-LOG */ extern int tlogsep; if ((x = cmkey(vbtab,nvb,"","verbose",xxstring)) < 0) return(x); if (x == 0) { if ((y = cmtxt("field separator",",",&s,NULL)) < 0) return(y); s = brstrip(s); if (*s) { if (s[1]) { printf("?A single character, please.\n"); return(-9); } else if ((*s >= '0' && *s <= '9') || (*s >= 'A' && *s <= 'Z') || (*s >= 'a' && *s <= 'z')) { printf("?A non-alphanumeric character, please.\n"); return(-9); } else tlogsep = *s; } } else { if ((y = cmcfm()) < 0) return(y); } #ifdef IKSD if (inserver && isguest) { printf("?Sorry, command disabled.\n"); return(success = 0); } #endif /* IKSD */ #ifdef CK_APC /* Don't let this be set remotely */ if (apcactive == APC_LOCAL || (apcactive == APC_REMOTE && !(apcstatus & APC_UNCH))) return(success = 0); #endif /* CK_APC */ tlogfmt = x; return(success = 1); } #endif /* TLOG */ case XYCLEAR: { /* SET CLEARCHANNEL */ if ((x = cmkey(ooatab,3,"","automatic",xxstring)) < 0) return(x); if ((y = cmcfm()) < 0) return(y); clearrq = x; return(success = 1); } #ifdef CK_AUTHENTICATION case XYAUTH: { /* SET AUTHENTICATION */ #ifdef CK_KERBEROS int kv = 0; extern struct krb_op_data krb_op; #endif /* CK_KERBEROS */ char * p = NULL; if ((x = cmkey(setauth,nsetauth,"authentication type","",xxstring)) < 0) return(x); switch (x) { #ifdef CK_KERBEROS case AUTH_KRB4: kv = 4; break; /* Don't assume values are same */ case AUTH_KRB5: kv = 5; break; #endif /* CK_KERBEROS */ #ifdef CK_SRP case AUTH_SRP: break; #endif /* CK_SRP */ #ifdef CK_SSL case AUTH_SSL: case AUTH_TLS: break; #endif /* CK_SSL */ default: printf("?Authorization type not supported yet - \"%s\"\n", atmbuf); return(-9); } #ifdef IKSD if (inserver && #ifdef IKSDCONF iksdcf #else 1 #endif /* IKSDCONF */ ) { if ((y = cmcfm()) < 0) return(y); printf("?Sorry, command disabled.\n"); return(success = 0); } #endif /* IKSD */ #ifdef CK_APC /* Don't let this be set remotely */ if (apcactive == APC_LOCAL || apcactive == APC_REMOTE && !(apcstatus & APC_UNCH)) { if ((y = cmcfm()) < 0) return(y); return(success = 0); } #endif /* CK_APC */ switch(x) { #ifdef CK_KERBEROS case AUTH_KRB4: case AUTH_KRB5: { if ((x = cmkey(kv == 4 ? k4tab : k5tab, kv == 4 ? nk4tab : nk5tab, "Kerberos parameter","",xxstring)) < 0) { return(x); } s = ""; switch (x) { #ifdef KRB4 case XYKRBDBG: if (kv == 4) { if ((y = seton(&k4debug)) < 0) return(y); #ifdef NT ck_krb4_debug(k4debug); #endif /* NT */ } else { return(-9); } break; #endif /* KRB4 */ case XYKRBLIF: if ((y = cmnum("TGT lifetime","600",10,&z,xxstring)) < 0) return(y); break; case XYKRBPRE: if (kv == 4) { if ((y = seton(&krb4_d_preauth)) < 0) return(y); } else { return(-9); } break; case XYKRBINS: if ((y = cmtxt("Instance name","",&s,xxstring)) < 0) return(y); break; case XYKRBFWD: if (kv == 5) { if ((y = seton(&krb5_d_forwardable)) < 0) return(y); } else { return(-9); } break; case XYKRBPRX: if (kv == 5) { if ((y = seton(&krb5_d_proxiable)) < 0) return(y); } else { return(-9); } break; case XYKRBRNW: if ((y = cmnum("TGT renewable lifetime", "0",10,&z,xxstring)) < 0) return(y); break; case XYKRBADR: if (kv == 5) { if ((y = seton(&krb5_checkaddrs)) < 0) return(y); } else { if ((y = seton(&krb4_checkaddrs)) < 0) return(y); } break; case XYKRBNAD: if (kv == 5) { if ((y = seton(&krb5_d_no_addresses)) < 0) return(y); } break; case XYKRBADD: if (kv == 5) { char * tmpaddrs[KRB5_NUM_OF_ADDRS]; for (i = 0; i < KRB5_NUM_OF_ADDRS; i++) tmpaddrs[i] = NULL; if ((y = cmfld("List of IP addresses","",&s,xxstring)) < 0) return(y); makelist(s,tmpaddrs,KRB5_NUM_OF_ADDRS); if ((y = cmcfm()) < 0) { for (i = 0; i < KRB5_NUM_OF_ADDRS; i++) { if (tmpaddrs[i] != NULL) free(tmpaddrs[i]); } return(y); } for (i = 0; i < KRB5_NUM_OF_ADDRS && tmpaddrs[i]; i++) { if (inet_addr(tmpaddrs[i]) == 0xffffffff) { printf("invalid ip address: %s\n", tmpaddrs[i]); for (i = 0; i < KRB5_NUM_OF_ADDRS; i++) { if (tmpaddrs[i] != NULL) free(tmpaddrs[i]); } return(-9); } } for (i = 0; i < KRB5_NUM_OF_ADDRS && krb5_d_addrs[i]; i++) { if (krb5_d_addrs[i]) free(krb5_d_addrs[i]); krb5_d_addrs[i] = NULL; } for (i = 0; i < KRB5_NUM_OF_ADDRS && tmpaddrs[i]; i++) { krb5_d_addrs[i] = tmpaddrs[i]; tmpaddrs[i] = NULL; } krb5_d_addrs[i] = NULL; return(success = 1); } break; case XYKRBGET: if (kv == 5) { if ((y = seton(&krb5_autoget)) < 0) return(y); } else { if ((y = seton(&krb4_autoget)) < 0) return(y); } break; case XYKRBDEL: if ((z = cmkey(kdestab,nkdestab, "Auto Destroy Tickets", "never",xxstring)) < 0) return(z); break; case XYKRBPR: if ((y = cmtxt("User ID",uidbuf,&s,xxstring)) < 0) return(y); break; case XYKRBRL: if ((y = cmtxt("Name of realm","",&s,xxstring)) < 0) return(y); break; case XYKRBKTB: y = cmifi("Filename","",&s,&z,xxstring); if (y != -3) { if (y < 0) return(y); if (z) { printf("?Wildcards not allowed\n"); return(-9); } } break; case XYKRBCC: if ((y = cmofi("Filename","",&s,xxstring)) < 0) return(y); break; case XYKRBSRV: if ((y = cmtxt("Name of service to use in ticket", (kv == 4 ? "rcmd" : "host"), &s, xxstring )) < 0) return(y); break; case XYKRBK5K4: if (kv == 5) { if ((y = seton(&krb5_d_getk4)) < 0) return(y); } else { return(-9); } break; case XYKRBPRM: /* Prompt */ if ((z = cmkey(krbprmtab,2,"","",xxstring)) < 0) return(z); if ((y = cmtxt((z == KRB_PW_PRM) ? "Text of prompt;\nmay contain \"%s\" to be replaced by principal name" : "Text of prompt", "", &s, xxstring ) ) < 0) return(y); break; } ckstrncpy(line,s,LINBUFSIZ); s = line; if ((y = cmcfm()) < 0) return(y); #ifdef IKSD if (inserver && #ifdef IKSDCONF iksdcf #else /* IKSDCONF */ 1 #endif /* IKSDCONF */ ) return(success = 0); #endif /* IKSD */ switch (x) { /* Copy value to right place */ case XYKRBLIF: /* Lifetime */ if (kv == 4) krb4_d_lifetime = z; else krb5_d_lifetime = z; break; case XYKRBRNW: if (kv == 5) krb5_d_renewable = z; break; case XYKRBPR: /* Principal */ s = brstrip(s); /* Strip braces around. */ if (kv == 4) makestr(&krb4_d_principal,s); else makestr(&krb5_d_principal,s); break; case XYKRBINS: /* Instance */ if (kv == 4) makestr(&krb4_d_instance,s); else makestr(&krb5_d_instance,s); break; case XYKRBRL: /* Realm */ if (kv == 4) makestr(&krb4_d_realm,s); else makestr(&krb5_d_realm,s); break; case XYKRBKTB: /* Key Table */ if (kv == 4) makestr(&k4_keytab,s); else makestr(&k5_keytab,s); break; case XYKRBCC: /* Credentials cache */ makestr(&krb5_d_cc,s); break; case XYKRBSRV: /* Service Name */ if (kv == 4) makestr(&krb4_d_srv,s); else makestr(&krb5_d_srv,s); break; case XYKRBDEL: if (kv == 5) krb5_autodel = z; else krb4_autodel = z; break; case XYKRBPRM: /* Prompt */ s = brstrip(s); switch (z) { case KRB_PW_PRM: { /* Password */ /* Check that there are no more than */ /* two % fields and % must followed by 's'. */ int i,n,len; len = strlen(s); for (i = 0, n = 0; i < len; i++) { if (s[i] == '%') { if (s[i+1] != '%') { if (s[i+1] != 's') { printf( "Only %%s fields are permitted.\n" ); return(-9); } if (++n > 2) { printf( "Only two %%s fields are permitted.\n"); return(-9); } } i++; } } if (kv == 5) makestr(&k5pwprompt,s); else makestr(&k4pwprompt,s); break; } case KRB_PR_PRM: { /* Principal */ /* Check to make sure there are no % fields */ int i,len; len = strlen(s); for (i = 0; i < len; i++) { if (s[i] == '%') { if (s[i+1] != '%') { printf( "%% fields are not used in this command.\n"); return(-9); } i++; } } if (kv == 5) makestr(&k5prprompt,s); else makestr(&k4prprompt,s); break; } } } break; } #endif /* CK_KERBEROS */ #ifdef CK_SRP case AUTH_SRP: { if ((x = cmkey(srptab, nsrptab, "SRP parameter","",xxstring)) < 0) { return(x); } s = ""; switch (x) { case XYSRPPRM: /* Prompt */ if ((z = cmkey(srpprmtab,1,"","",xxstring)) < 0) return(z); if ((y = cmtxt( "Text of prompt;\nmay contain one \"%s\" to be replaced by the username", "", &s, xxstring ) ) < 0) return(y); break; } ckstrncpy(line,s,LINBUFSIZ); s = line; if ((y = cmcfm()) < 0) return(y); switch (x) { /* Copy value to right place */ case XYSRPPRM: /* Prompt */ s = brstrip(s); switch (z) { case SRP_PW_PRM: { /* Password */ /* Check %s fields */ int i,n,len; len = strlen(s); for (i = 0, n = 0; i < len; i++) { if (s[i] == '%') { if (s[i+1] != '%') { if (s[i+1] != 's') { printf( "Only %%s fields are permitted.\n"); return(-9); } if (++n > 1) { printf( "Only one %%s field is permitted.\n"); return(-9); } } i++; } } makestr(&srppwprompt,s); break; } } } break; } #endif /* CK_SRP */ #ifdef CK_SSL case AUTH_SSL: case AUTH_TLS: { if ((z = cmkey(ssltab, nssltab, (x == AUTH_SSL ? "SSL parameter" : "TLS parameter"), "",xxstring)) < 0) return(z); s = ""; switch (z) { case XYSSLRCFL: /* SSL/TLS RSA Certs file */ case XYSSLRCCF: /* SSL/TLS RSA Certs Chain file */ case XYSSLRKFL: /* SSL/TLS RSA Key File */ case XYSSLDCFL: /* SSL/TLS DSA Certs file */ case XYSSLDCCF: /* SSL/TLS DSA Certs Chain file */ case XYSSLDKFL: /* SSL/TLS DH Key File */ case XYSSLDPFL: /* SSL/TLS DH Param File */ case XYSSLCRL: /* SSL/TLS CRL File */ case XYSSLVRFF: /* SSL/TLS Verify File */ case XYSSLRND: /* SSL/TLS Random File */ y = cmifi("Filename","",&s,&x,xxstring); if (y != -3) { if (y < 0) return(y); if (x) { printf("?Wildcards not allowed\n"); return(-9); } } ckstrncpy(line,s,LINBUFSIZ); s = line; s = brstrip(s); if ((y = cmcfm()) < 0) return(y); switch (z) { case XYSSLRCFL: /* SSL/TLS RSA Certs file */ if (!s[0] && ssl_rsa_cert_file) { free(ssl_rsa_cert_file); ssl_rsa_cert_file = NULL; } else if (s[0]) { makestr(&ssl_rsa_cert_file,s); if (!ssl_rsa_key_file) makestr(&ssl_rsa_key_file,s); } break; case XYSSLRCCF: /* SSL/TLS RSA Certs Chain file */ if (!s[0] && ssl_rsa_cert_chain_file) { free(ssl_rsa_cert_chain_file); ssl_rsa_cert_chain_file = NULL; } else if (s[0]) { makestr(&ssl_rsa_cert_chain_file,s); } break; case XYSSLRKFL: /* SSL/TLS RSA Key File */ if (!s[0] && ssl_rsa_key_file) { free(ssl_rsa_key_file); ssl_rsa_key_file = NULL; } else if (s[0]) { makestr(&ssl_rsa_key_file,s); } break; case XYSSLDCFL: /* SSL/TLS DSA Certs file */ if (!s[0] && ssl_dsa_cert_file) { free(ssl_dsa_cert_file); ssl_dsa_cert_file = NULL; } else if (s[0]) { makestr(&ssl_dsa_cert_file,s); if (!ssl_dh_key_file) makestr(&ssl_dh_key_file,s); } break; case XYSSLDCCF: /* SSL/TLS DSA Certs Chain file */ if (!s[0] && ssl_dsa_cert_chain_file) { free(ssl_dsa_cert_chain_file); ssl_dsa_cert_chain_file = NULL; } else if (s[0]) { makestr(&ssl_dsa_cert_chain_file,s); } break; case XYSSLDKFL: /* SSL/TLS DH Key File */ if (!s[0] && ssl_dh_key_file) { free(ssl_dh_key_file); ssl_dh_key_file = NULL; } else if (s[0]) { makestr(&ssl_dh_key_file,s); } break; case XYSSLDPFL: /* SSL/TLS DH Param File */ if (!s[0] && ssl_dh_param_file) { free(ssl_dh_param_file); ssl_dh_param_file = NULL; } else if (s[0]) { makestr(&ssl_dh_param_file,s); } break; case XYSSLCRL: /* SSL/TLS CRL File */ if (!s[0] && ssl_crl_file) { free(ssl_crl_file); ssl_crl_file = NULL; } else if (s[0]) { makestr(&ssl_crl_file,s); } break; case XYSSLVRFF: /* SSL/TLS Verify File */ if (!s[0] && ssl_verify_file) { free(ssl_verify_file); ssl_verify_file = NULL; } else if (s[0]) { makestr(&ssl_verify_file,s); } break; case XYSSLRND: /* SSL/TLS Random File */ if (!s[0] && ssl_rnd_file) { free(ssl_rnd_file); ssl_rnd_file = NULL; } else if (s[0]) { makestr(&ssl_rnd_file,s); } break; } break; case XYSSLCRLD: case XYSSLVRFD: { char * d = NULL; if (z == XYSSLVRFD) d= getenv("SSL_CERT_DIR"); if (d == NULL) d = ""; if ((y = cmdir("Directory",d,&s,xxstring)) < 0) if (y != -3) return(y); ckstrncpy(line,s,LINBUFSIZ); s = line; s = brstrip(s); if ((y = cmcfm()) < 0) return(y); switch(z) { case XYSSLCRLD: if (!s[0] && ssl_crl_dir) { free(ssl_crl_dir); ssl_crl_dir = NULL; } else if (s[0]) { makestr(&ssl_crl_dir,s); } break; case XYSSLVRFD: if (!s[0] && ssl_verify_dir) { free(ssl_verify_dir); ssl_verify_dir = NULL; } else if (s[0]) { makestr(&ssl_verify_dir,s); } break; } break; } case XYSSLCOK: /* SSL/TLS Certs-Ok flag */ if ((y = seton(&ssl_certsok_flag)) < 0) return(y); break; case XYSSLDBG: /* SSL/TLS Debug flag */ if ((y = seton(&ssl_debug_flag)) < 0) return(y); break; case XYSSLON: /* SSL/TLS Only flag */ switch (x) { case AUTH_SSL: if ((y = seton(&ssl_only_flag)) < 0) return(y); break; case AUTH_TLS: if ((y = seton(&tls_only_flag)) < 0) return(y); break; } break; case XYSSLVRB: /* SSL/TLS Verbose flag */ if ((y = seton(&ssl_verbose_flag)) < 0) return(y); break; case XYSSLVRF: /* SSL/TLS Verify flag */ if ((x = cmkey(sslvertab, nsslvertab, "SSL/TLS verify mode", "peer-cert",xxstring)) < 0) return(x); if ((y = cmcfm()) < 0) return(y); ssl_verify_flag = x; break; case XYSSLDUM: if ((y = seton(&ssl_dummy_flag)) < 0) return(y); break; case XYSSLCL: { /* SSL/TLS Cipher List */ #ifdef COMMENT /* This code is used to generate a colon delimited */ /* list of the ciphers currently in use to be used */ /* as the default for cmtxt(). However, a better */ /* default is simply the magic keyword "ALL". */ CHAR def[1024] = ""; if (ssl_con != NULL) { CHAR * p = NULL, *q = def; int i, len; for (i = 0; ; i++) { p = (CHAR *) SSL_get_cipher_list(ssl_con,i); if (p == NULL) break; len = strlen(p); if (q+len+1 >= def+1024) break; if (i != 0) *q++ = ':'; strcpy(q,p); q += len; } } #endif /* COMMENT */ char * p = getenv("SSL_CIPHER"); if (!p) p = "ALL"; if ((y = cmtxt( "Colon-delimited list of ciphers or ALL (case-sensitive)", p, &s, xxstring ) ) < 0) return(y); makestr(&ssl_cipher_list,s); if (ssl_con == NULL) { SSL_library_init(); ssl_ctx = (SSL_CTX *) SSL_CTX_new((SSL_METHOD *)TLSv1_method()); if (ssl_ctx != NULL) ssl_con= (SSL *) SSL_new(ssl_ctx); } if (ssl_con) { SSL_set_cipher_list(ssl_con,ssl_cipher_list); } break; } } break; } #endif /* CK_SSL */ default: break; } return(success = 1); } #endif /* CK_AUTHENTICATION */ #ifndef NOSPL case XYFUNC: if ((x = cmkey(functab,nfunctab,"","diagnostics",xxstring)) < 0) return(x); switch (x) { case FUNC_DI: return(seton(&fndiags)); case FUNC_ER: return(seton(&fnerror)); default: return(-2); } #endif /* NOSPL */ case XYSLEEP: /* SET SLEEP / PAUSE */ if ((x = cmkey(sleeptab,1,"","cancellation",xxstring)) < 0) return(x); return(seton(&sleepcan)); case XYCD: /* SET CD */ if ((x = cmkey(cdtab,ncdtab,"","",xxstring)) < 0) return(x); switch (x) { case XYCD_H: { /* SET CD HOME */ extern char * myhome; if ((y = cmdir("Directory name",zhome(),&s,xxstring)) < 0) return(y); makestr(&myhome,s); return(success = 1); } case XYCD_M: /* SET CD MESSAGE */ if ((x = cmkey(cdmsg,ncdmsg,"","",xxstring)) < 0) return(x); if (x == 2) { /* CD MESSAGE FILE */ if ((x = cmtxt("Name of file","",&s,NULL)) < 0) return(x); if (!*s) { s = NULL; #ifndef NOXFER srvcdmsg = 0; #endif /* NOXFER */ } makestr(&cdmsgstr,s); makelist(cdmsgstr,cdmsgfile,8); return(success = 1); } if ((y = cmcfm()) < 0) return(y); /* CD-MESSAGE ON/OFF */ #ifndef NOXFER if (x > 0) srvcdmsg |= 2; else srvcdmsg &= 1; #endif /* NOXFER */ return(success = 1); case XYCD_P: { /* SET CD PATH */ extern char * ckcdpath; if ((x = cmtxt("CD PATH string","",&s,xxstring)) < 0) return(x); makestr(&ckcdpath,s); return(success = 1); } } #ifndef NOLOCAL #ifdef STOPBITS case XYSTOP: /* STOP-BITS */ if ((x = cmkey(stoptbl,2,"Stop bits for serial device","", xxstring)) < 0) return(x); if ((y = cmcfm()) < 0) return(y); if (x > 0 && x < 3) { stopbits = x; #ifdef TN_COMPORT if (network && istncomport()) { tnsettings(-1, x); return(success = 1); } #endif /* TN_COMPORT */ #ifdef HWPARITY return(success = 1); #else /* HWPARITY */ return(-2); #endif /* HWPARITY */ } else return(-2); #endif /* STOPBITS */ case XYDISC: { extern int clsondisc; return(seton(&clsondisc)); } case XYSERIAL: { /* char c; */ extern int cmask; if ((x = cmkey(sertbl,nsertbl, "Serial device character size, parity, and stop bits", "8N1", xxstring)) < 0) return(x); ckstrncpy(line,atmbuf,LINBUFSIZ); /* Associated keyword string */ s = line; if ((y = cmcfm()) < 0) return(y); ckstrncpy(line,sernam[x],LINBUFSIZ); s = line; if (s[0] != '8' && s[0] != '7') /* Char size */ return(-2); else z = s[0] - '0'; if (isupper(s[1])) /* Parity */ s[1] = tolower(s[1]); if (s[2] != '1' && s[2] != '2') /* Stop bits */ return(-2); else stopbits = s[2] - '0'; if (z == 8) { /* 8 bits + parity (or not) */ parity = 0; /* Set parity */ hwparity = (s[1] == 'n') ? 0 : s[1]; setcmask(8); /* Also set TERM BYTESIZE to 8 */ } else { /* 7 bits plus parity */ parity = (s[1] == 'n') ? 0 : s[1]; hwparity = 0; setcmask(7); /* Also set TERM BYTESIZE to 7 */ } #ifdef TN_COMPORT if (network && !istncomport()) tnsettings(parity, stopbits); #endif /* TN_COMPORT */ return(success = 1); /* from SET SERIAL */ } case XYOPTS: { /* SET OPTIONS */ extern int setdiropts(); extern int settypopts(); #ifdef CKPURGE extern int setpurgopts(); #endif /* CKPURGE */ if ((x = cmkey(optstab,noptstab,"for command","", xxstring)) < 0) return(x); switch (x) { #ifndef NOFRILLS case XXDEL: return(setdelopts()); #endif /* NOFRILLS */ case XXDIR: return(setdiropts()); case XXTYP: return(settypopts()); #ifdef CKPURGE case XXPURGE: return(setpurgopts()); #endif /* CKPURGE */ default: return(-2); } } #endif /* NOLOCAL */ #ifndef NOXFER case XYQ8FLG: { extern int q8flag; return(seton(&q8flag)); } case XYTIMER: { extern int asktimer; y = cmnum("Time limit for ASK command, seconds","0",10,&x,xxstring); #ifdef QNX16 return(setnum(&asktimer,x,y,32767)); #else return(setnum(&asktimer,x,y,86400)); #endif /* QNX16 */ } case XYFACKB: { extern int fackbug; return(seton(&fackbug)); } #endif /* NOXFER */ case XYHINTS: return(seton(&hints)); #ifndef NOSPL case XYEVAL: { extern int oldeval; if ((x = cmkey(oldnew,2,"","", xxstring)) < 0) return(x); if ((y = cmcfm()) < 0) return(y); oldeval = x; return(success = 1); } #endif /* NOSPL */ #ifndef NOXFER case XYFACKP: { extern int fackpath; return(seton(&fackpath)); } #endif /* NOXFER */ case XYQNXPL: { extern int qnxportlock; return(seton(&qnxportlock)); } #ifndef NOCMDL #ifdef IKSD case XYIKS: { int setiks(); return(setiks()); } #endif /* IKSD */ #endif /* NOCMDL */ #ifdef CKROOT case XYROOT: return(dochroot()); #endif /* CKROOT */ #ifndef NOSPL #ifndef NOSEXP case XYSEXP: { if ((x = cmkey(sexptab,3,"","", xxstring)) < 0) return(x); switch (x) { case 0: if ((x = cmkey(ooatab,3,"","automatic", xxstring)) < 0) return(x); if ((y = cmcfm()) < 0) return(y); sexpecho = x; break; case 1: { int i, xx; xx = sexpmaxdep; if ((y = cmnum("Maximum recursion depth", "1000",10,&x,xxstring)) < 0) return(y); z = setnum(&sexpmaxdep,x,y,-1); if (z < 0) return(z); if (sxresult) { /* Free old stack if allocated */ for (i = 0; i < xx; i++) if (sxresult[i]) free(sxresult[i]); free((char *)sxresult); if (sxrlen) free((char *)sxrlen); sxresult = NULL; sxrlen = NULL; } break; } case 2: return(seton(&sexptrunc)); } return(success = 1); } #endif /* NOSEXPL */ #endif /* NOSPL */ #ifdef NEWFTP case XYGPR: { extern struct keytab gprtab[]; extern int ftpget; if ((x = cmkey(gprtab,3,"","kermit", xxstring)) < 0) return(x); if ((y = cmcfm()) < 0) return(y); ftpget = x; return(success = 1); } #endif /* NEWFTP */ #ifdef ANYSSH case XYSSH: return(dosetssh()); #endif /* ANYSHH */ #ifdef SFTP_BUILTIN case XYSFTP: return(dosetsftp()); #endif /* SFTP_BUILTIN */ #ifdef LOCUS case XYLOCUS: if ((x = cmkey(locustab,nlocustab,"", #ifdef KUI "ask" #else "auto" #endif /* KUI */ ,xxstring)) < 0) return(x); if ((y = cmcfm()) < 0) return(y); if (x == 2 || x == 3) { /* AUTO or ASK */ setautolocus(x - 1); /* Two forms of automatic locusing */ /* setlocus(1,0); */ /* we're not changing the locus here */ } else { /* LOCAL or REMOTE */ setautolocus(0); /* No automatic Locus changing */ setlocus(x,0); /* Set Locus as requested */ } return(success = 1); #endif /* LOCUS */ #ifdef KUI case XYGUI: return(setgui()); #endif /* KUI */ #ifndef NOFRILLS #ifndef NORENAME case XY_REN: /* SET RENAME */ return(setrename()); #endif /* NORENAME */ #endif /* NOFRILLS */ #ifndef NOPUSH #ifdef CK_REDIR #ifndef NOXFER case XYEXTRN: /* SET EXTERNAL-PROTOCOL */ return(setextern()); #endif /* NOXFER */ #endif /* CK_REDIR */ #endif /* NOPUSH */ #ifndef NOSPL case XYVAREV: /* SET VARIABLE-EVALUATION */ return(setvareval()); #endif /* NOSPL */ default: if ((x = cmcfm()) < 0) return(x); printf("Not implemented - %s\n",cmdbuf); return(success = 0); } } /* H U P O K -- Is Hangup OK? Issues a warning and gets OK from user depending on whether a connection seems to be open and what the SET EXIT WARNING setting is. Returns: 0 if not OK to hang up or exit (i.e. user said No); nonzero if OK. Argument x is used to differentiate the EXIT command from SET LINE / HOST. */ int hupok(x) int x; { /* Returns 1 if OK, 0 if not OK */ int y, z = 1; extern int exithangup; #ifdef VMS extern int batch; if (batch) /* No warnings in batch */ return(1); #else #ifdef UNIX if (backgrd) /* No warnings in background */ return(1); #endif /* UNIX */ #endif /* VMS */ #ifndef K95G debug(F101,"hupok local","",local); if (!local) /* No warnings in remote mode */ return(1); #endif /* K95G */ if (x == 0 && exithangup == 0) /* EXIT and EXIT HANGUP is OFF */ return(1); debug(F101,"hupok x","",x); debug(F101,"hupok xitwarn","",xitwarn); debug(F101,"hupok network","",network); debug(F101,"hupok haveline","",haveline); if ((local && xitwarn) || /* Is a connection open? */ (!x && xitwarn == 2)) { /* Or Always give warning on EXIT */ int needwarn = 0; char warning[256]; if (network) { if (ttchk() >= 0) needwarn = 1; /* A connection seems to be open but it can't possibly be */ if (!haveline) needwarn = 0; if (needwarn) { if (strcmp(ttname,"*")) ckmakmsg(warning,256, " A network connection to ",ttname, " might still be active.\n",NULL); else ckstrncpy(warning, " An incoming network connection might still be active.\n", 256); } } else { /* Serial connection */ if (carrier == CAR_OFF) /* SET CARRIER OFF */ needwarn = 0; /* so we don't care about carrier. */ else if ((y = ttgmdm()) >= 0) /* else, get modem signals */ needwarn = (y & BM_DCD); /* Check for carrier */ else /* If we can't get modem signals... */ needwarn = (ttchk() >= 0); /* A connection seems to be open but it can't possibly be */ if (!haveline || !exithangup) needwarn = 0; if (needwarn) ckmakmsg(warning,256, " A serial connection might still be active on ", ttname,".\n",NULL); } /* If a warning was issued, get user's permission to EXIT. */ if (needwarn || (!x && xitwarn == 2 #ifndef K95G && local #endif /* K95G */ )) { if ( !needwarn ) ckstrncpy(warning, "No active connections", 256); #ifdef COMMENT printf("%s",warning); z = getyesno(x ? "OK to close? " : "OK to exit? ",0); debug(F101,"hupok getyesno","",z); if (z < -3) z = 0; #else z = uq_ok(warning, x ? "OK to close? " : "OK to exit? ", 3, NULL, 0 ); debug(F101,"hupok uq_ok","",z); if (z < 0) z = 0; #endif /* COMMENT */ } } return(z); } #ifndef NOSHOW VOID shoctl() { /* SHOW CONTROL-PREFIXING */ #ifdef CK_SPEED int i; #ifdef OS2 int zero; #endif /* OS2 */ printf( "\ncontrol quote = %d, applied to (0 = unprefixed, 1 = prefixed):\n\n", myctlq); #ifdef OS2 #ifndef UNPREFIXZERO zero = ctlp[0]; if (protocol == PROTO_K) /* Zero can't be unprefixed */ ctlp[0] = 1; /* for Kermit */ #endif /* UNPREFIXZERO */ #endif /* OS2 */ for (i = 0; i < 16; i++) { printf(" %3d: %d %3d: %d ",i,ctlp[i], i+16, ctlp[i+16]); if (i == 15) printf(" 127: %d",ctlp[127]); else printf(" "); printf(" %3d: %d %3d: %d ",i+128,ctlp[i+128], i+144, ctlp[i+144]); if (i == 15) printf(" 255: %d",ctlp[255]); printf("\n"); } printf("\n"); #ifndef UNPREFIXZERO #ifdef OS2 ctlp[0] = zero; #endif /* OS2 */ #endif /* UNPREFIXZERO */ #endif /* CK_SPEED */ } #ifndef NOXFER VOID shodbl() { /* SHOW DOUBLE/IGNORE */ #ifdef CKXXCHAR int i, n = 0; printf("\nSET SEND DOUBLE characters:\n"); for (i = 0; i < 255; i++) { if (dblt[i] & 2) { n++; printf(" %d", i); } } if (n == 0) printf(" (none)"); n = 0; printf("\nSET RECEIVE IGNORE characters:\n"); for (i = 0; i < 255; i++) { if (dblt[i] & 1) { n++; printf(" %d", i); } } if (n == 0) printf(" (none)"); printf("\n\n"); #endif /* CKXXCHAR */ } #endif /* NOXFER */ #endif /* NOSHOW */ #ifndef NOPUSH #ifdef CK_REXX /* Rexx command. Note, this is not OS/2-specific, because Rexx also runs on other systems where C-Kermit also runs, like the Amiga. */ #define REXBUFL 100 /* Change this if neccessary */ char rexxbuf[REXBUFL] = { '\0' }; /* Rexx's return value (string) */ int dorexx() { int x, y; char *rexxcmd; if ((x = cmtxt("Rexx command","",&rexxcmd,xxstring)) < 0) return(x); #ifdef IKSD if (inserver) { printf("?Sorry, command disabled.\r\n"); return(success = 0); } #endif /* IKSD */ #ifdef CK_APC /* Don't let this be set remotely */ if (apcactive == APC_LOCAL || apcactive == APC_REMOTE && !(apcstatus & APC_UNCH)) return(success = 0); #endif /* CK_APC */ ckstrncpy(line,rexxcmd,LINBUFSIZ); rexxcmd = line; #ifdef OS2 return(os2rexx(rexxcmd,rexxbuf,REXBUFL)); #else /* !OS2 */ printf("Sorry, nothing happens.\n"); return(success = 0); #endif /* OS2 */ } #endif /* CK_REXX */ #endif /* NOPUSH */ #else /* NOICP */ VOID dologend() { /* Dummy write record to connection log */ } #endif /* NOICP */ ckuus4.c0000644000015300001460000171423511601157102011252 0ustar fdckermit#include "ckcsym.h" /* C K U U S 4 -- "User Interface" for C-Kermit, part 4 */ /* Authors: Frank da Cruz , The Kermit Project, Columbia University, New York City Jeffrey E Altman Secure Endpoints Inc., New York City Copyright (C) 1985, 2011, Trustees of Columbia University in the City of New York. All rights reserved. See the C-Kermit COPYING.TXT file or the copyright text in the ckcmai.c module for disclaimer and permissions. */ /* File ckuus4.c -- Functions moved from other ckuus*.c modules to even out their sizes. */ #include "ckcdeb.h" #include "ckcasc.h" #include "ckcker.h" #include "ckcnet.h" /* Network symbols */ #include "ckuusr.h" #include "ckuver.h" #include "ckcxla.h" /* Character sets */ #ifdef CK_AUTHENTICATION #include "ckuath.h" #endif /* CK_AUTHENTICATION */ #ifdef CK_SSL #include "ck_ssl.h" #endif /* CK_SSL */ #ifdef VMS #include /* For \v(errno) */ extern char * ckvmserrstr(unsigned long); #ifndef OLD_VMS #include /* Not for VAX C 2.4 */ #else #include #endif /* OLD_VMS */ _PROTOTYP(int vmsttyfd, (void) ); #endif /* VMS */ #ifdef OS2 #ifndef NT #define INCL_NOPM #define INCL_VIO /* Needed for ckocon.h */ #include #undef COMMENT #else #include #include #include "ckntap.h" #define APIRET ULONG #endif /* NT */ #include "ckocon.h" #include "ckoetc.h" int StartedFromDialer = 0; HWND hwndDialer = 0; LONG KermitDialerID = 0; #ifdef putchar #undef putchar #endif /* putchar */ #define putchar(x) conoc(x) #ifdef CK_PID #include #endif /* CK_PID */ #endif /* OS2 */ #ifdef KUI extern struct keytab * term_font; extern int ntermfont, tt_font, tt_font_size; #endif /* KUI */ extern xx_strp xxstring; #ifdef DEC_TCPIP #include #include #include #endif /* DEC_TCPIP */ #ifdef FNFLOAT #include /* Floating-point functions */ #endif /* FNFLOAT */ extern int quiet, network, xitsta, escape, nopush, xferstat, exitonclose, tn_exit, ttnproto, autodl, flow, byteorder, what, lastxfer; extern int filepeek, nscanfile, makestrlen; extern char * k_info_dir; #ifndef MAC #ifndef AMIGA extern int ttyfd; #endif /* MAC */ #endif /* AMIGA */ #ifdef TNCODE extern int tn_nlm, tn_b_nlm, tn_b_xfer, tn_sb_bug; extern int tn_rem_echo; extern int tn_b_meu, tn_b_ume, tn_auth_krb5_des_bug; #endif /* TNCODE */ static char * lastkwval = NULL; char * xferfile = NULL; int xferlog = 0; extern int local, xargc, stayflg, rcflag, bgset, backgrd, cfilef, inserver, srvcdmsg, success; #ifdef VMS extern int batch; #endif /* VMS */ extern char cmdfil[], *versio, *ckxsys, **xargv; #ifdef DEBUG extern char debfil[]; /* Debug log file name */ extern int debtim; #endif /* DEBUG */ extern int noinit; static char ndatbuf[10]; char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; char * zzndate() { /* Returns today's date as yyyymmdd */ char * p = NULL; int x; /* WARNING - This will fail if asctime() returns non-English month names */ ztime(&p); /* Get "asctime" string */ if (p == NULL || *p == NUL) return(""); for (x = 20; x < 24; x++) /* yyyy */ ndatbuf[x - 20] = p[x]; ndatbuf[6] = (char) ((p[8] == ' ') ? '0' : p[8]); ndatbuf[7] = p[9]; /* dd */ for (x = 0; x < 12; x++) /* mm */ if (!strncmp(p+4,months[x],3)) break; if (x == 12) { ndatbuf[4] = ndatbuf[5] = '?'; } else { x++; ndatbuf[4] = (char) ((x < 10) ? '0' : '1'); ndatbuf[5] = (char) ((x % 10) + 48); } ndatbuf[8] = NUL; debug(F110,"zzndate return",ndatbuf,0); return((char *)ndatbuf); } #ifdef DCMDBUF extern struct cmdptr *cmdstk; extern char *line, *tmpbuf; #else extern struct cmdptr cmdstk[]; extern char line[], tmpbuf[]; #endif /* DCMDBUF */ #ifdef OS2 extern char exedir[]; #else extern char * exedir; #endif /* OS2 */ extern int nettype; #ifndef NOICP /* Most of this file... */ #ifdef CKLOGDIAL extern char diafil[]; #endif /* CKLOGDIAL */ #ifndef AMIGA #ifndef MAC #include #endif /* MAC */ #endif /* AMIGA */ #ifdef SV68 /* July 2006 believe it or not */ #ifndef SEEK_CUR #include #endif /* SEEK_CUR */ #endif /* SV68 */ #ifdef SCO32 /* June 2011 believe it or not... */ #ifdef XENIX #ifndef SEEK_CUR #include #endif /* SEEK_CUR */ #endif /* XENIX */ #endif /* SCO32 */ #ifdef STRATUS /* Stratus Computer, Inc. VOS */ #ifdef putchar #undef putchar #endif /* putchar */ #define putchar(x) conoc(x) #ifdef getchar #undef getchar #endif /* getchar */ #define getchar(x) coninc(0) #endif /* STRATUS */ #ifdef ANYX25 extern int revcall, closgr, cudata; int x25ver; extern char udata[]; #ifndef IBMX25 extern int npadx3; extern CHAR padparms[]; extern struct keytab padx3tab[]; #endif /* !IBMX25 */ #ifdef IBMX25 /* global variables only available for IBM X.25 - possibly interesting for * other implementations */ extern x25addr_t local_nua; extern x25addr_t remote_nua; #endif /* IBMX25 */ #endif /* ANYX25 */ #ifdef NETCONN #ifndef NODIAL extern int nnetdir; extern char *netdir[]; #endif /* NODIAL */ extern char ipaddr[]; #ifdef CK_NETBIOS extern unsigned short netbiosAvail; extern unsigned long NetbeuiAPI; extern unsigned char NetBiosName[]; extern unsigned char NetBiosAdapter; extern unsigned char NetBiosLSN; #endif /* CK_NETBIOS */ #ifdef TCPSOCKET extern char myipaddr[]; extern int tcp_rdns; #ifdef CK_DNS_SRV extern int tcp_dns_srv; #endif /* CK_DNS_SRV */ extern char * tcp_address; #ifndef NOHTTP extern char * tcp_http_proxy; #endif /* NOHTTP */ #ifdef NT #ifdef CK_SOCKS extern char * tcp_socks_svr; #ifdef CK_SOCKS_NS extern char * tcp_socks_ns; #endif /* CK_SOCKS_NS */ #endif /* CK_SOCKS */ #endif /* NT */ #ifndef NOTCPOPTS #ifdef SOL_SOCKET #ifdef SO_LINGER extern int tcp_linger; extern int tcp_linger_tmo; #endif /* SO_LINGER */ #ifdef SO_DONTROUTE extern int tcp_dontroute; #endif /* SO_DONTROUTE */ #ifdef TCP_NODELAY extern int tcp_nodelay; #endif /* TCP_NODELAY */ #ifdef SO_SNDBUF extern int tcp_sendbuf; #endif /* SO_SNDBUF */ #ifdef SO_RCVBUF extern int tcp_recvbuf; #endif /* SO_RCVBUF */ #ifdef SO_KEEPALIVE extern int tcp_keepalive; #endif /* SO_KEEPALIVE */ #endif /* SOL_SOCKET */ #endif /* NOTCPOPTS */ #endif /* TCPSOCKET */ #endif /* NETCONN */ extern char * floname[]; #ifndef NOSPL extern int vareval; /* Variable evaluation method */ extern int fndiags; /* Function diagnostics on/off */ extern int divbyzero; int itsapattern = 0; int isinbuflen = 0; int isjoin = 0; #ifdef CK_APC extern int apcactive; /* Nonzero = APC command was rec'd */ extern int apcstatus; /* Are APC commands being processed? */ #ifdef DCMDBUF extern char *apcbuf; /* APC command buffer */ #else extern char apcbuf[]; #endif /* DCMDBUF */ #endif /* CK_APC */ extern char evalbuf[]; /* EVALUATE result */ extern char uidbuf[], pwbuf[], prmbuf[]; _PROTOTYP( static char * fneval, (char *, char * [], int, char * ) ); _PROTOTYP( static VOID myflsh, (void) ); _PROTOTYP( static char * getip, (char *) ); _PROTOTYP( int delta2sec, (char *, long *) ); #ifdef NEWFTP _PROTOTYP( char * ftp_cpl_mode, (void) ); _PROTOTYP( char * ftp_dpl_mode, (void) ); _PROTOTYP( char * ftp_authtype, (void) ); #endif /* NEWFTP */ #ifndef NOHTTP _PROTOTYP( char * http_host, (void) ); _PROTOTYP( int http_isconnected, (void) ); _PROTOTYP( char * http_security, (void) ); #endif /* NOHTTP */ #ifndef NOSEXP _PROTOTYP( char * dosexp, (char *) ); int fsexpflag = 0; #endif /* NOSEXP */ static char hexdigits[16] = { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' }; extern char * tempdir; #ifdef CK_REXX extern char rexxbuf[]; #endif /* CK_REXX */ extern int tfline[]; char *wkdays[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; #endif /* NOSPL */ #ifdef OS2 extern char startupdir[], inidir[]; #else #ifdef VMSORUNIX extern char startupdir[]; #endif /* VMSORUNIX */ #endif /* OS2 */ #ifdef OS2 _PROTOTYP (int os2getcp, (void) ); #ifdef TCPSOCKET extern char tcpname[]; #endif /* TCPSOCKET */ extern int tcp_avail; #ifdef DECNET extern int dnet_avail; #endif /* DECNET */ #ifdef SUPERLAT extern int slat_avail; #endif /* SUPERLAT */ #ifndef NOTERM extern int tt_type, max_tt; extern struct tt_info_rec tt_info[]; #endif /* NOTERM */ extern int tt_rows[], tt_cols[]; #else /* OS2 */ extern int tt_rows, tt_cols; #endif /* OS2 */ #ifdef CK_TAPI extern int tttapi; extern int tapipass; extern struct keytab * tapilinetab; extern struct keytab * _tapilinetab; extern int ntapiline; #endif /* CK_TAPI */ extern struct keytab colxtab[]; extern int ncolx; extern char ttname[], *zinptr, *kermrc; extern char inidir[]; extern int activecmd, remonly, cmd_rows, cmd_cols, parity, seslog, sessft, sosi, hwparity, tsecs, xargs, zincnt, tlevel, insilence, cmdmsk, timint, timef, inbufsize, dialog, binary, carrier, cdtimo, cmask, duplex, fmask, inecho, nmac, turnch, turn, kbchar; #ifndef NOXFER extern CHAR eol, mypadc, mystch, padch, seol, stchr, * epktmsg, feol; extern char *cksysid; extern struct ck_p ptab[]; extern int protocol, prefixing, xfrbel, xfrcan, xfrint, xfrchr, xfrnum, pktpaus, lscapr, lscapu, xfermode, dest, slostart, maxrps, maxsps, maxtry, mypadn, npad, pkttim, bigrbsiz, bigsbsiz, keep, atcapr, autopar, bctr, bctu, crunched, ckdelay, ebq, ebqflg, pktlog, retrans, rpackets, rptflg, rptq, rtimo, spackets, spsiz, spsizf, spsizr, timeouts, fncact, fncnv, urpsiz, wmax, wslotn, wslotr, fdispla, spmax, fnrpath, fnspath, crc16; #endif /* NOXFER */ #ifdef OS2 extern int zxpn; extern int viewonly; #endif /* OS2 */ #ifndef NOXFER #ifdef GFTIMER extern CKFLOAT fptsecs, fpxfsecs; #endif /* GFTIMER */ extern long xfsecs, tfcps; #ifdef CK_TMPDIR extern char *dldir; #endif /* CK_TMPDIR */ #endif /* NOXFER */ #ifdef RECURSIVE extern int recursive; #endif /* RECURSIVE */ #ifdef VMS extern int frecl; #endif /* VMS */ extern CK_OFF_T ffc, tfc, tlci, tlco; extern long filcnt, rptn, speed, ccu, ccp, vernum, xvernum; #ifndef NOSPL extern char fspec[], myhost[]; #endif /* NOSPL */ extern char *tfnam[]; /* Command file names */ extern char pktfil[], /* Packet log file name */ #ifdef TLOG trafil[], /* Transaction log file name */ #endif /* TLOG */ sesfil[]; /* Session log file name */ #ifndef NOXMIT /* TRANSMIT command variables */ extern char xmitbuf[]; extern int xmitf, xmitl, xmitx, xmits, xmitw, xmitt; #endif /* NOXMIT */ extern int cmdlvl; #ifndef NOSPL /* Script programming language items */ extern char **a_ptr[]; /* Arrays */ extern int a_dim[]; static char * inpmatch = NULL; #ifdef CKFLOAT char * inpscale = NULL; #endif /* CKFLOAT */ extern char * inpbuf, inchar[]; /* Buffers for INPUT and REINPUT */ extern char *inpbp; /* And pointer to same */ static char *r3 = (char *)0; extern int incount; /* INPUT character count */ extern int m_found; /* MINPUT result */ extern int maclvl; /* Macro invocation level */ extern struct mtab *mactab; /* Macro table */ extern char *mrval[]; extern int macargc[], topargc; #ifdef COMMENT extern char *m_line[]; extern char *topline; #endif /* COMMENT */ extern char *m_arg[MACLEVEL][10]; /* You have to put in the dimensions */ extern char *g_var[GVARS]; /* for external 2-dimensional arrays. */ #ifdef DCMDBUF extern int *count, *inpcas; #else extern int count[], inpcas[]; #endif /* DCMDBUF */ #endif /* NOSPL */ #ifdef UNIX extern int haslock; /* For UUCP locks */ extern char flfnam[]; #ifndef USETTYLOCK extern char lock2[]; #endif /* USETTYLOCK */ #endif /* UNIX */ #ifdef OS2ORUNIX extern int maxnam, maxpath; /* Longest name, path length */ #endif /* OS2ORUNIX */ extern int mdmtyp, mdmsav; #ifndef NODIAL /* DIAL-related variables */ extern char modemmsg[]; extern MDMINF *modemp[]; /* Pointers to modem info structs */ extern int nmdm, dialhng, dialtmo, dialksp, dialdpy, dialsrt, dialsta; extern int dialrtr, dialint, dialrstr, dialcon, dialcq, dialfld; extern int mdmspd, dialec, dialdc, dialmth, dialmauto, dialesc; extern char *dialnum, *dialini, *dialdir[], *dialcmd, *dialnpr, *dialdcon, *dialdcoff, *dialecon, *dialecoff, *dialhcmd, *diallac, *dialhwfc, *dialswfc, *dialnofc, *dialpulse, *dialtone, *dialname, *dialaaon, *dialaaoff, *dialmac; extern char *diallcc, *dialixp, *dialixs, *dialldp, *diallds, *dialpxi, *dialpxo, *dialsfx, *dialtfp; extern char *diallcp, *diallcs; extern int ntollfree, ndialpxx, nlocalac; extern char *dialtfc[], *diallcac[], *dialpxx[], *matchpxx; extern int ndialpucc, ndialtocc; extern char *dialtocc[], *dialpucc[]; extern int ndialdir, dialcnf, dialcvt, dialidt, dialpace; extern long dialmax, dialcapas; extern struct keytab mdmtab[]; #ifdef BIGBUFOK #define ARGBUFSIZ 8191 #else #define ARGBUFSIZ 1023 #endif /* BIGBUFOK */ #ifdef BIGBUFOK extern char * dialmsg[]; #endif /* BIGBUFOK */ #endif /* NODIAL */ #ifndef NOCSETS /* Translation stuff */ extern int nfilc; extern struct keytab fcstab[]; extern int fcharset, tcharset, tslevel, language, nlng, tcsr, tcsl; extern int dcset7, dcset8; extern struct keytab lngtab[]; extern struct csinfo fcsinfo[], tcsinfo[]; extern struct langinfo langs[]; #ifdef CK_ANSIC extern CHAR (*xls[MAXTCSETS+1][MAXFCSETS+1])(CHAR); /* Character set */ extern CHAR (*xlr[MAXTCSETS+1][MAXFCSETS+1])(CHAR); /* translation functions */ #else extern CHAR (*xls[MAXTCSETS+1][MAXFCSETS+1])(); /* Character set */ extern CHAR (*xlr[MAXTCSETS+1][MAXFCSETS+1])(); /* translation functions. */ #endif /* CK_ANSIC */ #ifdef UNICODE extern int ucsbom, ucsorder; #endif /* UNICODE */ #endif /* NOCSETS */ #ifndef NOSPL /* Built-in variable names, maximum length VNAML (20 characters) */ struct keytab vartab[] = { { "_line", VN_TFLN, CM_INV}, /* 192 */ #ifdef OS2 { "_regname", VN_REGN, CM_INV}, /* 1.1.12 */ { "_regorg", VN_REGO, CM_INV}, /* 1.1.12 */ { "_regnum", VN_REGS, CM_INV}, /* 1.1.12 */ #endif /* OS2 */ { "apcactive", VN_APC, CM_INV}, /* 192 */ #ifdef NT { "appdata", VN_APPDATA, 0}, /* 201 */ #endif /* NT */ { "argc", VN_ARGC, 0}, { "args", VN_ARGS, 0}, { "authname", VN_AUTHN, 0}, /* 196 */ { "authstate", VN_AUTHS, 0}, /* 195 */ { "authtype", VN_AUTHT, 0}, /* 195 */ { "bits", VN_BITS, 0}, /* 212 */ { "blockcheck",VN_BLK, 0}, /* 195 */ #ifdef BROWSER { "browser", VN_BROWSR,0}, /* 193 */ { "browsopts", VN_BROPT, 0}, /* 193 */ { "browsurl", VN_URL, 0}, /* 193 */ { "buildid", VN_BUILD, 0}, /* 199 */ #endif /* BROWSER */ { "byteorder", VN_BYTE, 0}, /* 195 */ #ifndef NOCSETS { "charset", VN_CSET, 0}, /* 192 */ #endif /* NOCSETS */ { "cmdbufsize",VN_CMDBL, 0}, /* 195 */ { "cmdfile", VN_CMDF, 0}, { "cmdlevel", VN_CMDL, 0}, { "cmdsource", VN_CMDS, 0}, { "cols", VN_COLS, 0}, /* 190 */ #ifdef NT { "common", VN_COMMON, 0}, /* 201 */ #endif /* NT */ { "connection",VN_CONN, 0}, /* 190 */ { "count", VN_COUN, 0}, #ifndef NOXFER { "cps", VN_CPS, 0}, /* 190 */ #endif /* NOXFER */ { "cpu", VN_CPU, 0}, #ifndef NOXFER { "crc16", VN_CRC16, 0}, /* 192 */ { "ctty", VN_TTYNAM,0}, /* 196 */ #endif /* NOXFER */ #ifndef NOLOGDIAL #ifndef NOLOCAL { "cx_time", VN_CXTIME,0}, /* 195 */ { "cx_status", VN_CX_STA,0}, /* 199 */ #endif /* NOLOCAL */ #endif /* NOLOGDIAL */ #ifndef NODIAL { "d$ac", VN_D_AC, 0}, /* 192 */ { "d$cc", VN_D_CC, 0}, /* 192 */ { "d$ip", VN_D_IP, 0}, /* 192 */ { "d$lc", VN_D_LCP, 0}, /* 193 */ { "d$lcp", VN_D_LCP, CM_INV}, /* 193 */ { "d$lp", VN_D_LP, 0}, /* 192 */ { "d$px", VN_D_PXX, 0}, /* 195 */ { "d$pxx", VN_D_PXX, CM_INV}, /* 195 */ #endif /* NODIAL */ { "date", VN_DATE, 0}, { "day", VN_DAY, 0}, #ifdef NT { "desktop", VN_DESKTOP, 0}, /* 201 */ #endif /* NT */ #ifndef NODIAL { "dialcount", VN_DRTR, 0}, /* 195 */ { "dialmessage",VN_DMSG, 0}, /* 212 */ { "dialnumber",VN_DNUM, 0}, /* 192 */ { "dialresult",VN_MDMSG, 0}, /* 192 */ { "dialstatus",VN_DIAL, 0}, /* 190 */ { "dialsuffix",VN_PDSFX, 0}, /* 193 */ { "dialtype", VN_DTYPE, 0}, /* 193 */ #endif /* NODIAL */ { "directory", VN_DIRE, 0}, #ifndef NODIAL { "dm_hf", VN_DM_HF, 0}, /* 199 */ { "dm_lp", VN_DM_LP, 0}, /* 195 */ { "dm_sp", VN_DM_SP, 0}, /* 195 */ { "dm_pd", VN_DM_PD, 0}, /* 195 */ { "dm_td", VN_DM_TD, 0}, /* 195 */ { "dm_wa", VN_DM_WA, 0}, /* 195 */ { "dm_wb", VN_DM_WB, 0}, /* 199 */ { "dm_wd", VN_DM_WD, 0}, /* 195 */ { "dm_rc", VN_DM_RC, 0}, /* 195 */ #endif /* NODIAL */ #ifndef NOXFER { "download", VN_DLDIR, 0}, /* 192 */ #endif /* NOXFER */ { "editor", VN_EDITOR,0}, { "editfile", VN_EDFILE,0}, { "editopts", VN_EDOPT, 0}, { "errno", VN_ERRNO, 0}, /* 192 */ { "errstring", VN_ERSTR, 0}, /* 192 */ { "escape", VN_ESC, 0}, /* 193 */ { "evaluate", VN_EVAL, 0}, /* 190 */ #ifdef OS2ORUNIX { "exedir", VN_EXEDIR,0}, /* 192 */ #endif /* OS2ORUNIX */ { "exitstatus",VN_EXIT, 0}, #ifdef CKCHANNELIO { "f_count", VN_FCOU, 0}, /* 195 */ { "f_error", VN_FERR, 0}, /* 195 */ { "f_max", VN_FMAX, 0}, /* 195 */ { "fileerror", VN_FERR, CM_INV}, /* 195 */ { "filemax", VN_FERR, CM_INV}, /* 195 */ #endif /* CKCHANNELIO */ { "filename", VN_FNAM, 0}, /* 193 */ { "filenumber",VN_FNUM, 0}, /* 193 */ { "filespec", VN_FILE, 0}, { "fsize", VN_FFC, 0}, /* 190 */ #ifdef GFTIMER { "ftime", VN_FTIME, 0}, /* 199 */ #else { "ftime", VN_NTIM, CM_INV}, #endif /* GFTIMER */ #ifndef NOFTP #ifndef SYSFTP { "ftp_code", VN_FTP_C, 0}, /* 199 */ { "ftp_cpl", VN_FTP_B, 0}, /* 199 */ { "ftp_connected", VN_FTP_X, 0}, /* 199 */ { "ftp_dpl", VN_FTP_D, 0}, /* 199 */ { "ftp_getputremote", VN_FTP_G, 0}, /* 199 */ { "ftp_host", VN_FTP_H, 0}, /* 199 */ { "ftp_loggedin", VN_FTP_L, 0}, /* 199 */ { "ftp_message", VN_FTP_M, 0}, /* 199 */ { "ftp_msg", VN_FTP_M, CM_INV}, /* 199 */ { "ftp_security", VN_FTP_Z, 0}, /* 199 */ { "ftp_server", VN_FTP_S, 0}, /* 199 */ #endif /* SYSFTP */ #endif /* NOFTP */ { "ftype", VN_MODE, 0}, /* 190 */ #ifdef KUI { "gui_fontname", VN_GUI_FNM, 0}, /* 205 */ { "gui_fontsize", VN_GUI_FSZ, 0}, /* 205 */ { "gui_runmode", VN_GUI_RUN, 0}, /* 205 */ { "gui_xpos", VN_GUI_XP, 0}, /* 205 */ { "gui_xres", VN_GUI_XR, 0}, /* 205 */ { "gui_ypos", VN_GUI_YP, 0}, /* 205 */ { "gui_yres", VN_GUI_YR, 0}, /* 205 */ #endif /* KUI */ { "herald", VN_HERALD, 0}, { "home", VN_HOME, 0}, { "host", VN_HOST, 0}, { "hour", VN_HOUR, 0}, /* 200 */ #ifndef NOHTTP { "http_code", VN_HTTP_C, 0}, /* 199 */ { "http_connected", VN_HTTP_N, 0}, /* 199 */ { "http_host", VN_HTTP_H, 0}, /* 199 */ { "http_message", VN_HTTP_M, 0}, /* 199 */ { "http_security", VN_HTTP_S, 0}, /* 199 */ #endif /* NOHTTP */ { "hwparity", VN_HWPAR, 0}, /* 195 */ { "input", VN_IBUF, 0}, { "inchar", VN_ICHR, 0}, { "incount", VN_ICNT, 0}, { "inidir", VN_INI, 0}, /* 192 */ { "inmatch", VN_MATCH, 0}, /* 196 */ { "inmessage", VN_INPMSG,0}, /* 212 */ { "inscale", VN_ISCALE,0}, /* 210 */ { "instatus", VN_ISTAT, 0}, /* 192 */ { "intime", VN_INTIME,0}, /* 193 */ { "inwait", VN_INTMO, 0}, /* 195 */ { "ip", VN_IPADDR, CM_ABR|CM_INV}, { "ipaddress", VN_IPADDR,0}, /* 192 */ { "iprompt", VN_PROMPT,0}, /* 199 */ { "kbchar", VN_KBCHAR,0}, /* 196 */ #ifndef NOLOCAL #ifdef OS2 { "keyboard", VN_KEYB, 0}, #endif /* OS2 */ #endif /* NOLOCAL */ #ifdef CK_KERBEROS { "krb4errmsg", VN_K4EMSG,0}, { "krb4errno", VN_K4ENO, 0}, { "krb4principal", VN_K4PRN, 0}, { "krb4realm", VN_K4RLM, 0}, { "krb4service", VN_K4SRV, 0}, { "krb5cc", VN_K5CC, 0}, { "krb5errmsg", VN_K5EMSG,0}, { "krb5errno", VN_K5ENO, 0}, { "krb5principal", VN_K5PRN, 0}, { "krb5realm", VN_K5RLM, 0}, { "krb5service", VN_K5SRV, 0}, #endif /* CK_KERBEROS */ { "lastcommand", VN_PREVCMD, 0}, /* 299 */ #ifndef NOLASTFILE { "lastfilespec", VN_LASTFIL, 0}, /* 212 */ #endif /* NOLASTFILE */ { "lastkeywordvalue", VN_LASTKWV, 0}, /* 212 */ { "lastkwvalue", VN_LASTKWV, CM_ABR|CM_INV}, /* 212 */ { "line", VN_LINE, 0}, { "local", VN_LCL, 0}, #ifdef UNIX { "lockdir", VN_LCKDIR,0}, /* 195 */ { "lockpid", VN_LCKPID,0}, /* 195 */ #endif /* UNIX */ { "log_connection", VN_LOG_CON, 0}, /* 206 */ { "log_debug", VN_LOG_DEB, 0}, /* 206 */ { "log_packet", VN_LOG_PKT, 0}, /* 206 */ { "log_session", VN_LOG_SES, 0}, /* 206 */ { "log_transaction", VN_LOG_TRA, 0},/* 206 */ { "maclevel", VN_MACLVL,0}, /* 195 */ { "macro", VN_MAC, 0}, #ifdef FNFLOAT { "math_e", VN_MA_E, 0}, /* 195 */ { "math_pi", VN_MA_PI, 0}, /* 195 */ { "math_precision", VN_MA_PR, 0}, /* 195 */ #endif /* FNFLOAT */ { "minput", VN_MINP, 0}, /* 192 */ { "model", VN_MODL, 0}, /* 193 */ { "modem", VN_MDM, 0}, #ifndef NOLOCAL #ifdef OS2 { "mousecurx", VN_MOU_X, 0}, /* K95 1.1.14 */ { "mousecury", VN_MOU_Y, 0}, /* K95 1.1.14 */ #endif /* OS2 */ #endif /* NOLOCAL */ #ifndef NODIAL { "m_aa_off", VN_M_AAX, 0}, /* all 192... */ { "m_aa_on", VN_M_AAO, 0}, { "m_dc_off", VN_M_DCX, 0}, { "m_dc_on", VN_M_DCO, 0}, { "m_dial", VN_M_DCM, 0}, { "m_ec_off", VN_M_ECX, 0}, { "m_ec_on", VN_M_ECO, 0}, { "m_fc_hw", VN_M_HWF, 0}, { "m_fc_no", VN_M_NFC, 0}, { "m_fc_sw", VN_M_SWF, 0}, { "m_hup", VN_M_HUP, 0}, { "m_init", VN_M_INI, 0}, { "m_name", VN_M_NAM, 0}, /* 195 */ { "m_pulse", VN_M_PDM, 0}, { "m_sig_cd", VN_MS_CD, 0}, /* 195 */ { "m_sig_cts", VN_MS_CTS,0}, /* 195 */ { "m_sig_dsr", VN_MS_DSR,0}, /* 195 */ { "m_sig_dtr", VN_MS_DTR,0}, /* 195 */ { "m_sig_ri", VN_MS_RI, 0}, /* 195 */ { "m_sig_rts", VN_MS_RTS,0}, /* 195 */ { "m_tone", VN_M_TDM, 0}, #endif /* NODIAL */ { "name", VN_NAME, 0}, { "ndate", VN_NDAT, 0}, { "nday", VN_NDAY, 0}, { "newline", VN_NEWL, 0}, { "ntime", VN_NTIM, 0}, { "osname", VN_OSNAM, 0}, /* 193 */ { "osrelease", VN_OSREL, 0}, /* 193 */ { "osversion", VN_OSVER, 0}, /* 193 */ #ifndef NOXFER { "packetlen", VN_RPSIZ, 0}, /* 192 */ #endif /* NOXFER */ { "parity", VN_PRTY, 0}, /* 190 */ { "password", VN_PWD, CM_INV}, /* 192 */ #ifdef NT { "personal", VN_PERSONAL, 0}, /* 201 */ #endif /* NT */ #ifdef PEXITSTAT { "pexitstat", VN_PEXIT, 0}, /* 193 */ #endif /* PEXITSTAT */ #ifdef CK_PID { "pid", VN_PID, 0}, /* 193 */ #endif /* CK_PID */ { "platform", VN_SYSV, 0}, { "printer", VN_PRINT, 0}, /* 193 */ { "program", VN_PROG, 0}, { "prompt", VN_PRM, CM_INV}, /* 192 */ #ifndef NOXFER { "protocol", VN_PROTO, 0}, /* 192 */ { "p_8bit", VN_P_8BIT,0}, /* 193 */ { "p_ctl", VN_P_CTL, 0}, /* 193 */ { "p_rpt", VN_P_RPT, 0}, /* 193 */ { "query", VN_QUE, 0}, /* 190 */ #endif /* NOXFER */ { "remoteip", VN_HOSTIP,0}, /* 212 */ { "return", VN_RET, 0}, #ifdef CK_REXX { "rexx", VN_REXX, 0}, /* 190 */ #endif /* CK_REXX */ #ifdef TN_COMPORT { "rfc2217_signature", VN_TNC_SIG, 0}, /* 201 */ { "rfc2717_signature", VN_TNC_SIG, CM_INV}, /* 202 */ #endif /* TN_COMPORT */ { "rows", VN_ROWS, 0}, /* 190 */ #ifndef NOSEXP { "sdepth", VN_LSEXP,0}, /* 199 */ #endif /* NOSEXP */ { "secure", VN_SECURE, 0}, /* 199 */ #ifndef NOLOCAL #ifdef OS2 { "select", VN_SELCT, 0}, /* 192 */ #endif /* OS2 */ #endif /* NOLOCAL */ { "sendlist", VN_SNDL, 0}, { "serial", VN_SERIAL,0}, /* 195 */ { "setlinemsg",VN_SLMSG, 0}, /* 195 */ #ifndef NOSEXP { "sexpression",VN_SEXP, 0}, /* 199 */ #endif /* NOSEXP */ { "speed", VN_SPEE, 0}, #ifdef OS2 { "space", VN_SPA, 0}, { "startup", VN_STAR, 0}, /* 190 */ #else #ifdef UNIX { "startup", VN_STAR, 0}, /* 193 */ #else #ifdef VMS { "startup", VN_STAR, 0}, /* 193 */ #endif /* VMS */ #endif /* UNIX */ #endif /* OS2 */ { "status", VN_SUCC, 0}, #ifndef NOSEXP { "svalue", VN_VSEXP, 0}, /* 199 */ #endif /* NOSEXP */ #ifndef NOXFER { "sysid", VN_SYSI, 0}, #endif /* NOXFER */ { "system", VN_SYST, 0}, { "terminal", VN_TTYP, 0}, #ifdef OS2 #ifndef NOKVERBS { "termkey", VN_TRMK, CM_INV}, /* 192 */ #endif /* NOKVERBS */ #endif /* OS2 */ { "test", VN_TEST, 0}, /* 193 */ { "textdir", VN_TXTDIR,0}, /* 195 */ #ifndef NOXFER { "tfsize", VN_TFC, 0}, { "tftime", VN_TFTIM, 0}, /* 195 */ #endif /* NOXFER */ { "time", VN_TIME, 0}, { "timestamp", VN_NOW, 0}, /* 200 */ { "tmpdir", VN_TEMP, 0}, /* 192 */ #ifdef CK_TRIGGER { "trigger", VN_TRIG, 0}, /* 193 */ #endif /* CK_TRIGGER */ #ifdef CK_TTYFD { "ttyfd", VN_TTYF, 0}, #endif /* CK_TTYFD */ { "ty_ln", VN_TY_LN, 0}, /* 195 */ { "ty_lc", VN_TY_LC, 0}, /* 195 */ { "ty_lm", VN_TY_LM, 0}, /* 195 */ #ifdef BROWSER { "url", VN_URL, CM_INV}, /* 193 */ #endif /* BROWSER */ { "userid", VN_UID, 0}, /* 192 */ { "vareval", VN_VAREVAL, 0}, /* 212 */ { "version", VN_VERS, 0}, #ifndef NOXFER { "window", VN_WINDO, 0}, /* 192 */ #endif /* NOXFER */ #ifdef IBMX25 { "x25local_nua", VN_X25LA, 0}, /* 193 */ { "x25remote_nua", VN_X25RA, 0}, /* 193 */ #endif /* IBMX25 */ #ifdef CK_SSL { "x509_issuer", VN_X509_I, 0}, { "x509_subject", VN_X509_S, 0}, #endif /* CK_SSL */ #ifndef NOXFER { "xferstatus",VN_XFSTAT,0}, /* 193 */ { "xfermsg", VN_XFMSG, 0}, /* 193 */ { "xfer_badpackets", VN_XF_BC, 0}, /* 195 */ { "xfer_timeouts", VN_XF_TM, 0}, /* 195 */ { "xfer_retransmits",VN_XF_RX, 0}, /* 195 */ #endif /* NOXFER */ { "xprogram", VN_XPROG, 0}, /* 193 */ { "xversion", VN_XVNUM, 0} /* 192 */ }; int nvars = (sizeof(vartab) / sizeof(struct keytab)); #endif /* NOSPL */ #ifndef NOSPL struct keytab fnctab[] = { /* Function names */ #ifdef OS2 { ".oox", FN_OOX, CM_INV}, /* ... */ #endif /* OS2 */ #ifdef CKCHANNELIO { "_eof", FN_FEOF, 0}, { "_errmsg", FN_FERMSG, 0}, { "_getblock", FN_FGBLK, 0}, { "_getchar", FN_FGCHAR, 0}, { "_getline", FN_FGLINE, 0}, { "_handle", FN_FILNO, 0}, { "_line", FN_NLINE, 0}, { "_pos", FN_FPOS, 0}, { "_putblock", FN_FPBLK, 0}, { "_putchar", FN_FPCHAR, 0}, { "_putline", FN_FPLINE, 0}, { "_status", FN_FSTAT, 0}, #endif /* CKCHANNELIO */ { "aaconvert", FN_AADUMP, 0}, /* Associative Array conversion */ { "absolute", FN_ABS, 0}, /* Absolute value */ #ifdef TCPSOCKET { "addr2name", FN_HSTADD,CM_INV}, /* IP Address to Hostname */ { "addrtoname", FN_HSTADD,CM_INV}, /* IP Address to Hostname */ #endif /* TCPSOCKET */ { "arraylook", FN_ALOOK,0}, /* Array lookup */ { "b64decode", FN_FMB64,0}, /* Base-64 conversion */ { "b64encode", FN_TOB64,0}, /* ... */ { "basename", FN_BSN, 0}, /* Basename */ { "break", FN_BRK, 0}, /* Break (as in Snobol) */ { "ca", FN_CAP, CM_INV|CM_ABR}, /* Abbreviation for capitablize */ { "cap", FN_CAP, CM_INV|CM_ABR}, /* Abbreviation for capitablize */ { "capitalize", FN_CAP, 0}, /* First Letter -> uppercase */ { "caps", FN_CAP, CM_INV}, /* ditto */ { "character", FN_CHR, 0}, /* Character from code */ { "checksum", FN_CHK, 0}, /* Checksum */ { "cmdstack", FN_CMDSTK,0}, /* Command stack */ { "cmpdates", FN_CMPDATE,0}, /* Compare dates */ { "code", FN_COD, 0}, /* Code from character */ #ifndef NOPUSH { "command", FN_CMD, 0}, /* Output from a command */ #endif /* NOPUSH */ { "contents", FN_CON, 0}, /* Definition (contents) of variable */ { "count", FN_COUNT, 0}, /* Occurrences of string in string */ { "crc16", FN_CRC, 0}, /* CRC-16 */ #ifdef OS2 { "crypt", FN_CRY, CM_INV}, #endif /* OS2 */ { "cvtcset", FN_XLATE, 0}, /* Convert character set */ { "cvtdate", FN_DTIM, 0}, /* Convert free date/time to std */ #ifdef ZFCDAT { "date", FN_FD, 0}, /* File modification/creation date */ #endif /* ZFCDAT */ { "day", FN_DAY, 0}, /* Day of week */ { "dayofyear", FN_JDATE,0}, /* Date to Day of Year */ { "decodehex", FN_UNPCT, 0}, /* Decode string with hex escapes */ { "definition", FN_DEF, 0}, /* Return definition of given macro */ { "delta2secs", FN_DELSEC, 0}, /* Delta time to seconds */ { "deltatosecs", FN_DELSEC, CM_INV}, /* Delta time to seconds */ #ifndef NODIAL { "dialconvert",FN_PNCVT,0}, /* Convert portable phone number */ #endif /* NODIAL */ { "diffdates", FN_DIFDATE,0}, /* Difference of two date-times */ { "dimension", FN_DIM, 0}, /* Dimension of array */ { "dir", FN_DIR, CM_INV|CM_ABR}, /* Abbreviation for direct.. */ { "dire", FN_DIR, CM_INV|CM_ABR}, /* Abbreviation for direct.. */ { "direc", FN_DIR, CM_INV|CM_ABR}, /* Abbreviation for direct.. */ { "direct", FN_DIR, CM_INV|CM_ABR}, /* Abbreviation for direct.. */ { "directo", FN_DIR, CM_INV|CM_ABR}, /* Abbreviation for direct.. */ { "director", FN_DIR, CM_INV|CM_ABR}, /* Abbreviation for direct.. */ { "directories",FN_DIR, 0}, /* List of directories */ { "directory", FN_DIR, CM_INV}, /* List of directories */ { "dirname", FN_DNAM, 0}, /* Directory part of filename */ { "dos2unixpath",FN_PC_DU, }, /* DOS to UNIX path */ { "dostounixpath",FN_PC_DU, CM_INV}, /* DOS to UNIX path */ { "doy", FN_JDATE,CM_INV}, /* Date to Day of Year */ { "doy2date", FN_DATEJ,0}, /* Day of Year to date */ { "doytodate", FN_DATEJ,CM_INV}, /* Day of Year to date */ { "emailaddress",FN_EMAIL, 0}, /* Email address */ #ifdef FN_ERRMSG { "errstring", FN_ERRMSG,0}, /* Error code to message */ #endif /* FN_ERRMSG */ { "evaluate", FN_EVA, 0}, /* Evaluate given arith expression */ { "execute", FN_EXE, 0}, /* Execute given macro */ { "files", FN_FC, 0}, /* File count */ #ifdef FNFLOAT { "fpabsolute", FN_FPABS, 0}, /* Floating-point absolute value */ { "fpadd", FN_FPADD, 0}, /* FP add */ { "fpcosine", FN_FPCOS, 0}, /* FP cosine */ { "fpdivide", FN_FPDIV, 0}, /* FP divide */ { "fpexp", FN_FPEXP, 0}, /* FP e to the x */ { "fpint", FN_FPINT, 0}, /* FP to integer */ { "fplog10", FN_FPLOG, 0}, /* FP base-10 logarithm */ { "fplogn", FN_FPLN, 0}, /* FP natural logarithm */ { "fpmaximum", FN_FPMAX, 0}, /* FP maxinum */ { "fpminimum", FN_FPMIN, 0}, /* FP mininum */ { "fpmodulus", FN_FPMOD, 0}, /* FP modulus */ { "fpmultiply", FN_FPMUL, 0}, /* FP multiply */ { "fpraise", FN_FPPOW, 0}, /* FP raise to a power */ { "fpround", FN_FPROU, 0}, /* FP round */ { "fpsine", FN_FPSIN, 0}, /* FP sine */ { "fpsqrt", FN_FPSQR, 0}, /* FP square root */ { "fpsubtract", FN_FPSUB, 0}, /* FP subtract */ { "fptangent", FN_FPTAN, 0}, /* FP tangent */ #endif /* FNFLOAT */ { "function", FN_FUNC, 0 }, /* Test for existence of a function */ { "getpidinfo", FN_PID, 0 }, /* Get PID info */ { "hex2ip", FN_HEX2IP,0}, /* Hex to IP address */ { "hextoip", FN_HEX2IP,CM_INV}, /* Hex to IP address */ { "hex2n", FN_HEX2N, CM_INV}, /* Hex to decimal number */ { "hexify", FN_HEX, 0}, /* Hexify (string) */ { "index", FN_IND, 0}, /* Index (string search) */ { "ip2hex", FN_IP2HEX,0}, /* IP address to hex */ { "iptohex", FN_IP2HEX,CM_INV}, /* IP address to hex */ { "ipaddress", FN_IPA, 0}, /* Find and return IP address */ { "jdate", FN_JDATE, CM_INV}, /* Date to Day of Year */ { "join", FN_JOIN, 0}, /* Join array elements */ { "keywordvalue", FN_KWVAL, 0}, /* Keyword=Value */ #ifdef CK_KERBEROS { "krbflags", FN_KRB_FG, 0}, /* Kerberos functions */ { "krbisvalid", FN_KRB_IV, 0}, { "krbnextticket", FN_KRB_NX, 0}, { "krbtickets", FN_KRB_TK, 0}, { "krbtimeleft", FN_KRB_TT, 0}, #endif /* CK_KERBEROS */ { "kwvalue", FN_KWVAL, CM_INV}, /* Keyword=Value */ { "left", FN_LEF, 0}, /* Leftmost n characters of string */ { "length", FN_LEN, 0}, /* Return length of argument */ { "literal", FN_LIT, 0}, /* Return argument literally */ #ifdef NT { "longpathname",FN_LNAME,0}, /* GetLongPathName() */ #else { "longpathname",FN_FFN,CM_INV}, #endif /* NT */ { "lop", FN_STL, 0}, /* Lop */ { "lopx", FN_LOPX, 0}, /* Lopx */ { "lower", FN_LOW, 0}, /* Return lowercased argument */ { "lpad", FN_LPA, 0}, /* Return left-padded argument */ { "ltrim", FN_LTR, 0}, /* Left-Trim */ { "maximum", FN_MAX, 0}, /* Return maximum of two arguments */ { "minimum", FN_MIN, 0}, /* Return minimum of two arguments */ { "mjd", FN_MJD, 0}, /* Date to Modified Julian Date */ { "mjd2date", FN_MJD2, 0}, /* MJD to Date */ { "mjdtodate", FN_MJD2, CM_INV}, /* MJD to Date */ { "modulus", FN_MOD, 0}, /* Return modulus of two arguments */ #ifdef COMMENT { "msleep", FN_MSLEEP,0}, /* Sleep for n milliseconds */ #endif /* COMMENT */ { "n2hex", FN_2HEX, CM_INV}, /* Number to hex */ { "n2octal", FN_2OCT, CM_INV}, /* Number to octal */ { "n2time", FN_N2TIM,0}, /* Number to hh:mm:ss */ #ifdef TCPSOCKET { "name2addr", FN_HSTNAM,CM_INV}, /* Hostname to IP Address */ #endif /* TCPSOCKET */ { "nday", FN_NDAY, 0}, /* Numeric day of week */ { "nextfile", FN_FIL, 0}, /* Next file in list */ { "ntime", FN_NTIM, 0}, /* Time to seconds since midnight */ { "ntohex", FN_2HEX, CM_INV}, /* Number to hex */ { "ntooctal", FN_2OCT, CM_INV}, /* Number to octal */ { "ntotime", FN_N2TIM,CM_INV}, /* Number to hh:mm:ss */ { "oct2n", FN_OCT2N,CM_INV}, /* Octal to decimal number */ { "octton", FN_OCT2N,CM_INV}, /* Octal to decimal number */ { "pathname", FN_FFN, 0}, /* Full file name */ { "pattern", FN_PATTERN, 0}, /* Pattern (for INPUT) */ #ifdef CK_PERMS { "permissions",FN_PERM, 0}, /* Permissions of file */ #else { "permissions",FN_PERM, CM_INV}, /* Permissions of file */ #endif /* CK_PERMS */ #ifdef SEEK_CUR { "pictureinfo",FN_PICTURE, 0 }, /* Picture orientation/dimensions */ #endif /* SEEK_CUR */ { "radix", FN_RADIX, 0 }, /* Radix conversion */ #ifndef NORANDOM { "random", FN_RAND, 0}, /* Random number */ #endif /* NORANDOM */ #ifndef NOPUSH { "rawcommand", FN_RAW, 0}, /* Output from a command (raw) */ #endif /* NOPUSH */ #ifdef RECURSIVE { "rdirectories", FN_RDIR, 0}, /* Recursive directory list */ #endif /* RECURSIVE */ { "recurse", FN_RECURSE, 0}, /* Recursive variable evaluation */ #ifdef RECURSIVE { "rfiles", FN_RFIL, 0}, /* Recursive file list */ #endif /* RECURSIVE */ { "rep", FN_REP, CM_INV|CM_ABR}, { "repeat", FN_REP, 0}, /* Repeat argument given # of times */ { "replace", FN_RPL, 0}, /* Replace characters in string */ { "reverse", FN_REV, 0}, /* Reverse the argument string */ { "right", FN_RIG, 0}, /* Rightmost n characters of string */ { "rindex", FN_RIX, 0}, /* Right index */ { "rpad", FN_RPA, 0}, /* Right-pad the argument */ { "rsearch", FN_RSEARCH, 0}, /* R-L Search for pattern in string */ #ifdef OS2 { "scrncurx", FN_SCRN_CX, 0}, /* Screen Cursor X Pos */ { "scrncury", FN_SCRN_CY, 0}, /* Screen Cursor Y Pos */ { "scrnstr", FN_SCRN_STR, 0}, /* Screen String */ #endif /* OS2 */ { "search", FN_SEARCH, 0}, /* L-R Search for pattern in string */ #ifndef NOSEXP { "sexpression",FN_SEXP, 0}, /* S-Expression */ #endif /* NOSEXP */ #ifdef NT { "shortpathname",FN_SNAME,0}, /* GetShortPathName() */ #else { "shortpathname",FN_FFN,CM_INV}, #endif /* NT */ { "size", FN_FS, 0}, /* File size */ #ifdef COMMENT { "sleep", FN_SLEEP,0}, /* Sleep for n seconds */ #endif /* COMMENT */ { "span", FN_SPN, 0}, /* Span - like Snobol */ { "split", FN_SPLIT,0}, /* Split string into words */ { "squeeze", FN_SQUEEZE,0}, /* Squeeze whitespace in string */ { "strcmp", FN_STRCMP,0}, /* String comparison */ { "stringtype", FN_STRINGT,0}, /* String type (7-bit, 8-bit, UTF-8) */ { "stripb", FN_STB, 0}, /* Strip enclosing braces/brackets */ { "stripn", FN_STN, 0}, /* Strip n chars */ { "stripx", FN_STX, 0}, /* Strip suffix */ { "su", FN_SUB, CM_INV|CM_ABR}, { "sub", FN_SUB, CM_INV|CM_ABR}, { "subs", FN_SUB, CM_INV|CM_ABR}, { "subst", FN_SUB, CM_INV|CM_ABR}, { "substitute", FN_SUBST,0}, /* Substitute chars */ { "substring", FN_SUB, 0}, /* Extract substring from argument */ { "tablelook", FN_TLOOK,0}, /* Table lookup */ { "time", FN_TIME, 0}, /* Free-format time to hh:mm:ss */ { "tod2secs", FN_NTIM, CM_INV}, /* Time-of-day-to-secs-since-midnite */ { "todtosecs", FN_NTIM, CM_INV}, /* Time-of-day-to-secs-since-midnite */ { "trim", FN_TRM, 0}, /* Trim */ { "unhexify", FN_UNH, 0}, /* Unhexify */ { "unix2dospath",FN_PC_UD, 0}, /* UNIX to DOS path */ { "unixtodospath",FN_PC_UD, CM_INV}, /* UNIX to DOS path */ { "untabify", FN_UNTAB,0}, /* Untabify */ { "upper", FN_UPP, 0}, /* Return uppercased argument */ { "utcdate", FN_TOGMT,0}, /* Date-time to UTC (GMT) */ { "verify", FN_VER, 0}, /* Verify */ { "word", FN_WORD, 0}, /* Extract a word */ { "", 0, 0} }; int nfuncs = (sizeof(fnctab) / sizeof(struct keytab)) - 1; #endif /* NOSPL */ #ifndef NOSPL /* Buffer for expansion of */ #ifdef BIGBUFOK /* built-in variables. */ #define VVBUFL 1024 #else #define VVBUFL 256 #endif /* BIGBUFOK */ char vvbuf[VVBUFL+1]; #endif /* NOSPL */ struct keytab disptb[] = { /* Log file disposition */ { "append", 1, 0}, { "new", 0, 0} }; #ifdef CKFLOAT /* I N I T F L O A T -- Deduce floating-point precision by inspection */ int fp_rounding = 0; /* Nonzero if printf("%f") rounds */ int fp_digits = 0; /* Digits of floating point precision */ #ifdef COMMENT /* For looking at internal floating-point representations */ static char fp_xbuf[128]; static char * tohex(s, n) CHAR * s; int n; { int x; char * p = fp_xbuf; while (n-- > 0) { x = (*s >> 4) & 0x0f; *p++ = hexdigits[x]; x = *s++ & 0x0f; *p++ = hexdigits[x]; } *p = NUL; return((char *)fp_xbuf); } #endif /* COMMENT */ char math_pi[] = "3.1415926535897932384626433832795"; char math_e[] = "2.7182818284590452353602874713527"; VOID initfloat() { char * buf = NULL; int i, x, y; /* We malloc a big temporary buffer for sprintf() to minimize likelihood of (and damage from) sprintf buffer overflows. In any case, the only way this could happen would be if sprintf() itself had bugs, since the format descriptor says to cut it off at 250 decimal places. */ if ((buf = (char *)malloc(4096))) { sprintf(buf,"%0.250f",(10.0 / 3.0)); for (i = 2; i < 250 && buf[i] == '3'; i++) ; x = i - 1; debug(F111,"initfloat 10.0/3.0",buf,x); sprintf(buf,"%0.250f",(4.0 / 9.0)); for (i = 2; i < 250 && buf[i] == '4'; i++) ; y = i - 1; debug(F111,"initfloat 4.0/9.0",buf,y); fp_digits = (x < y) ? x : y; if (fp_digits < sizeof(math_pi) - 1) { math_pi[fp_digits+1] = NUL; math_e[fp_digits+1] = NUL; } sprintf(buf,"%0.6f",(7.0 / 9.0)); if (buf[7] == '8') fp_rounding = 1; debug(F111,"initfloat 7.0/9.0",buf,fp_rounding); debug(F101,"initfloat precision","",fp_digits); free(buf); } } #endif /* CKFLOAT */ /* P R E S C A N -- A quick look through the command-line options for items that must be handled before the initialization file is executed. */ #ifdef NT extern int StartedFromDialer; #endif /* NT */ #ifdef OS2 extern int k95stdio; unsigned long startflags = 0L; #endif /* OS2 */ static char * findinpath(arg) char * arg; { #ifdef OS2 char * scriptenv, * keymapenv; int len; #endif /* OS2 */ #ifdef DCMDBUF extern char * cmdbuf; #else extern char cmdbuf[]; #endif /* DCMDBUF */ char takepath[4096]; char * s; int x, z; /* Set up search path... */ #ifdef OS2 char * appdata0 = NULL, *appdata1 = NULL; #ifdef NT scriptenv = getenv("K95SCRIPTS"); keymapenv = getenv("K95KEYMAPS"); makestr(&appdata0,(char *)GetAppData(0)); makestr(&appdata1,(char *)GetAppData(1)); #else /* NT */ scriptenv = getenv("K2SCRIPTS"); keymapenv = getenv("K2KEYMAPS"); #endif /* NT */ if (!scriptenv) scriptenv = getenv("CK_SCRIPTS"); if (!scriptenv) scriptenv = ""; if (!keymapenv) keymapenv = getenv("CK_KEYMAPS"); if (!keymapenv) keymapenv = ""; debug(F110,"startupdir",startupdir,0); debug(F110,"common appdata directory",appdata1,0); debug(F110,"appdata directory",appdata0,0); debug(F110,"inidir",inidir,0); debug(F110,"home",zhome(),0); debug(F110,"exedir",exedir,0); len = strlen(scriptenv) + strlen(keymapenv) + 3*strlen(startupdir) + 3*strlen(inidir) + 3*strlen(zhome()) + 3*strlen(exedir) + (appdata0 ? 3*strlen(appdata0) : 0) + (appdata1 ? 3*strlen(appdata1) : 0) + 6*strlen("SCRIPTS/") + 6*strlen("KEYMAPS/") + 16; if (len >= 4096) { /* SAFE (length is checked) */ takepath[0] = '\0'; debug(F111,"findinpath error - path length too long","len",len); } else sprintf(takepath, /* semicolon-separated path list */ "%s%s%s%s%s;%s%s;%s%s;%s%s%s%s%s%s%s%s%s%s%s%s%s;%s%s;%s%s;%s;%s%s;%s%s", scriptenv, (scriptenv[0] && scriptenv[strlen(scriptenv)-1]==';')?"":";", keymapenv, (keymapenv[0] && keymapenv[strlen(keymapenv)-1]==';')?"":";", startupdir, startupdir, "SCRIPTS/", startupdir, "KEYMAPS/", appdata1 ? appdata1 : "", appdata1 ? "Kermit 95;" : "", appdata1 ? appdata1 : "", appdata1 ? "Kermit 95/SCRIPTS/;" : "", appdata1 ? appdata1 : "", appdata1 ? "Kermit 95/KEYMAPS/;" : "", appdata0 ? appdata0 : "", appdata0 ? "Kermit 95;" : "", appdata0 ? appdata0 : "", appdata0 ? "Kermit 95/SCRIPTS/;" : "", appdata0 ? appdata0 : "", appdata0 ? "Kermit 95/KEYMAPS/;" : "", inidir, inidir, "SCRIPTS/", inidir, "KEYMAPS/", zhome(), zhome(), "SCRIPTS/", zhome(), "KEYMAPS/", exedir, exedir, "SCRIPTS/", exedir, "KEYMAPS/" ); debug(F110,"findinpath takepath",takepath,0); #ifdef NT makestr(&appdata0,NULL); makestr(&appdata1,NULL); #endif /* NT */ #else /* not OS2 */ #ifndef NOSPL z = 1024; /* Look in home directory */ s = takepath; zzstring("\\v(home)",&s,&z); #else takepath[0] = '\0'; #endif /* NOSPL */ #endif /* OS2 */ /* All the logic for searching the take path is in the command parser. So even though we aren't parsing commands, we initialize and call the parser from here, with the purported filename stuffed into the command buffer, followed by some carriage returns to make the parser return. If the file is not found, or otherwise not accessible, the parser prints an appropriate message, and then we just exit. */ cmdini(); /* Allocate command buffers etc */ cmini(0); /* Initialize them */ /* Stuff filename into command buf with braces in case of spaces */ ckmakmsg(cmdbuf,CMDBL,"{",arg,"}",NULL); debug(F110,"findinpath cmdbuf",cmdbuf,0); ckstrncat(cmdbuf,"\r\r",CMDBL); /* And some carriage returns */ if (cmifip("","",&s,&x,0,takepath,xxstring) < 0) return(NULL); cmres(); return(s); } static int tr_int; /* Flag if TRANSMIT interrupted */ #ifndef MAC SIGTYP #ifdef CK_ANSIC trtrap(int foo) /* TRANSMIT interrupt trap */ #else trtrap(foo) int foo; /* TRANSMIT interrupt trap */ #endif /* CK_ANSIC */ /* trtrap */ { #ifdef __EMX__ signal(SIGINT, SIG_ACK); #endif tr_int = 1; /* (Need arg for ANSI C) */ SIGRETURN; } #endif /* MAC */ #endif /* NOICP */ #ifdef UNIX VOID getexedir() { extern char * xarg0; long xx; /* Unix provides no standard service for this. We look in argv[0], and if we're lucky there's a full pathname. If not we do a PATH search. */ if (ckstrchr(xarg0,'/')) { /* Global copy of argv[0] */ int i, k; char * p = NULL; if ((k = ckstrncpy(tmpbuf,xarg0,TMPBUFSIZ-2)) > 0) { p = tmpbuf; /* Convert to fully qualified pathname */ if (tmpbuf[0]) if (tmpbuf[0] != '/') { line[0] = NUL; zfnqfp(tmpbuf,LINBUFSIZ-2,(char *)line); if (line[0]) p = line; } xx = zchki(p); if (xx > -1) { /* Is the result an existing file? */ k = strlen(p); for (i = k-1; i > 0; i--) { /* Yes, strip name part */ if (p[i] == '/') { if (i < k-1) p[i+1] = NUL; break; } } } makestr(&exedir,p); /* Save the result */ } } if (!exedir && xarg0) { /* Not found? */ char * p; p = getenv("PATH"); /* Search the PATH */ if (p) { /* If there is one... */ char * q, * PATH = NULL; int k; makestr(&PATH,p); /* Pokeable copy of PATH string */ if (PATH) { /* If malloc succeeded... */ p = PATH; while (p && *p) { /* Loop through segments */ q = ckstrchr(p,':'); /* End of this segment */ if (q == p) { /* Null PATH segment */ p++; /* Skip over colon */ continue; } if (q) /* If not at end of PATH string */ *q++ = NUL; /* zero out the colon */ if ((k = ckstrncpy(tmpbuf,p,TMPBUFSIZ)) > 0) { if (tmpbuf[k-1] != '/') { /* Copy this PATH segment */ tmpbuf[k++] = '/'; /* Append '/' if needed */ tmpbuf[k] = NUL; } /* Append the argv[0] value */ if (ckstrncpy(&tmpbuf[k],xarg0,TMPBUFSIZ) > 0) { if (zchki(tmpbuf) > -1) { /* File exists? */ tmpbuf[k] = NUL; /* Yes, we're done */ zfnqfp(tmpbuf,LINBUFSIZ,(char *)line); makestr(&exedir,line); break; } } else break; } else break; p = q; /* Not found, go to next segment */ } /* while */ free(PATH); /* Free PATH copy */ } } if (!exedir) { /* Still nothing? */ if (zchki(xarg0) > -1) { /* Maybe it's in the current dir */ zfnqfp(zgtdir(),LINBUFSIZ,(char *)line); makestr(&exedir,line); } } } if (!exedir) { /* Still nothing? */ makestr(&exedir,"/"); /* Fake it with with root. */ } } #endif /* UNIX */ int arg_x = 0; static int x_prescan = 0; /* The argument y once meant something but I can't imagine what so now it's ignored. (Prior to 22 Aug 98, prescan() was called twice by main(), and the arg differentiated the two calls. But this caused all sorts of problems & confusion, so I commented out the second call. This issue might need to be revisited.) */ VOID prescan(dummy) int dummy; { /* Arg is ignored. */ extern int howcalled; int yargc; char **yargv; char x; char *yp, *yy; #ifdef DEBUG int debcount = 0; #endif /* DEBUG */ int z; if (x_prescan) /* Only run once */ return; x_prescan = 1; yargc = xargc; /* Make copy of arg vector */ yargv = xargv; #ifndef NOICP #ifdef DCMDBUF if (!kermrc) if (!(kermrc = (char *) malloc(KERMRCL+1))) fatal("prescan: no memory for kermrc"); #endif /* DCMDBUF */ ckstrncpy(kermrc,KERMRC,KERMRCL); /* Default init file name */ #endif /* NOICP */ #ifdef OS2 yp = getenv("K95STARTFLAGS"); if (yp) { startflags = atoi(yp); } #endif /* OS2 */ #ifdef IKSD if (howcalled == I_AM_IKSD) /* Internet Kermit Service daemon */ inserver = 1; /* (See inserver section of ckcmai) */ #endif /* IKSD */ /* Command line options for Kermit */ #ifndef NOCMDL if (yargc > 1 && *yargv[1] != '-' && (yargv[1][0] != '=') #ifdef KERBANG && (yargv[1][0] != '+') #endif /* KERBANG */ #ifdef IKSD && (howcalled != I_AM_IKSD) #endif /* IKSD */ ) { /* Filename as 1st argument */ #ifndef NOICP char *s; #endif /* NOICP */ #ifndef NOURL extern int haveurl; extern struct urldata g_url; if (urlparse(yargv[1],&g_url)) { if (!ckstrcmp(g_url.svc,"ftp",-1,0) || !ckstrcmp(g_url.svc,"ftps",-1,0)) { haveurl = 1; howcalled = I_AM_FTP; } else if (!ckstrcmp(g_url.svc,"telnet",-1,0) || !ckstrcmp(g_url.svc,"telnets",-1,0)) { haveurl = 1; howcalled = I_AM_TELNET; } else if (!ckstrcmp(g_url.svc,"ssh",-1,0)) { haveurl = 1; howcalled = I_AM_SSH; } else if (!ckstrcmp(g_url.svc,"iksd",-1,0) || !ckstrcmp(g_url.svc,"kermit",-1,0)) { haveurl = 1; howcalled = I_AM_KERMIT; } else if (!ckstrcmp(g_url.svc,"http",-1,0) || !ckstrcmp(g_url.svc,"https",-1,0)) { haveurl = 1; howcalled = I_AM_HTTP; } if (haveurl) { while (--yargc > 0) { /* Go through command-line args */ yargv++; /* looking for -Y and -d */ yp = *yargv+1; if (**yargv == '-') { x = *(*yargv+1); while (x) { switch (x) { #ifndef NOICP case '+': case '-': if (doxarg(yargv,1) < 0) { fatal("Extended argument error"); } yp = ""; break; #endif /* NOICP */ case 'Y': noinit++; break; case 'q': quiet = 1; break; case 'h': noinit = 1; #ifdef OS2 startflags |= 2; /* No network DLLs */ startflags |= 4; /* No TAPI DLLs */ startflags |= 8; /* No Security DLLs */ startflags |= 16; /* No Zmodem DLLs */ startflags |= 32; /* Stdin */ startflags |= 64; /* Stdout */ #endif /* OS2 */ break; case 'd': /* = SET DEBUG ON */ #ifdef DEBUG if (debcount++ > 0) debtim = 1; if (!deblog) deblog = debopn("debug.log",0); #endif /* DEBUG */ break; #ifdef OS2 case 'W': if (*(yp+1)) fatal("invalid argument bundling after -W"); yargv++, yargc--; if (yargc < 1) fatal("Window handle missing"); hwndDialer = (HWND) atol(*yargv); StartedFromDialer = 1; yargv++, yargc--; KermitDialerID = atol(*yargv) ; break; case '#': /* K95 initialization options */ if (*(yp+1)) { fatal("invalid argument bundling"); } yargv++, yargc--; if (yargc < 1) fatal("-# argument missing"); startflags |= atol(*yargv); break; #endif /* OS2 */ } if (!yp) break; x = *++yp; } } } return; } } /* after this point non-Kermit personalities must return */ switch (howcalled) { case I_AM_KERMIT: case I_AM_IKSD: case I_AM_SSHSUB: break; default: return; } #endif /* NOURL */ #ifndef NOICP /* If it is not a URL that we recognize, try to treat it as a file */ if (!isabsolute(yargv[1])) /* If not absolute */ s = findinpath(yargv[1]); /* Look in PATH */ else s = yargv[1]; if (!s) doexit(BAD_EXIT,xitsta); zfnqfp(s,CKMAXPATH,cmdfil); /* In case of CD in file */ yargc -= 1; /* Skip past the filename */ yargv += 1; /* Otherwise we'll get an error */ #endif /* NOICP */ } #ifndef NOCMDL #ifdef NEWFTP if (howcalled == I_AM_FTP) { /* Kermit's FTP client personality */ while (--yargc > 0) { /* Go through command-line args */ yargv++; /* looking for -Y and -d */ yp = *yargv+1; if (**yargv == '-') { x = *(*yargv+1); while (x) { switch (x) { #ifndef NOICP case '+': case '-': if (doxarg(yargv,1) < 0) { fatal("Extended argument error"); } yp = ""; break; #endif /* NOICP */ case 'Y': noinit++; break; case 'q': quiet = 1; break; case 'h': noinit = 1; #ifdef OS2 startflags |= 2; /* No network DLLs */ startflags |= 4; /* No TAPI DLLs */ startflags |= 8; /* No Security DLLs */ startflags |= 16; /* No Zmodem DLLs */ startflags |= 32; /* Stdin */ startflags |= 64; /* Stdout */ #endif /* OS2 */ break; case 'd': /* = SET DEBUG ON */ #ifdef DEBUG if (debcount++ > 0) debtim = 1; if (!deblog) deblog = debopn("debug.log",0); #endif /* DEBUG */ break; #ifdef OS2 case 'W': if (*(yp+1)) fatal("invalid argument bundling after -W"); yargv++, yargc--; if (yargc < 1) fatal("Window handle missing"); hwndDialer = (HWND) atol(*yargv); StartedFromDialer = 1; yargv++, yargc--; KermitDialerID = atol(*yargv) ; break; case '#': /* K95 initialization options */ if (*(yp+1)) { fatal("invalid argument bundling"); } yargv++, yargc--; if (yargc < 1) fatal("-# argument missing"); startflags |= atol(*yargv); break; #endif /* OS2 */ } if (!yp) break; x = *++yp; } } } return; } #endif /* NEWFTP */ #endif /* NOCMDL */ while (--yargc > 0) { /* Go through command-line args */ yargv++; yp = *yargv+1; /* Pointer for bundled args */ if (**yargv == '=') /* Same rules as cmdlin()... */ return; debug(F110,"prescan *yargv",*yargv,0); #ifndef NOICP #ifdef KERBANG yy = *yargv; if (!strcmp(yy,"+") || (*yy == '+' && *(yy+1) < (char)33)) { char * s; yargv++; noinit = 1; if (!*yargv) return; cfilef = 1; s = findinpath(*yargv); if (s) { zfnqfp(s,CKMAXPATH,cmdfil); return; } else doexit(BAD_EXIT,xitsta); } #endif /* KERBANG */ #endif /* NOICP */ if (!strcmp(*yargv,"--")) /* getopt() conformance */ return; #ifdef VMS else if (**yargv == '/') continue; #endif /* VMS */ else if (**yargv == '-') { /* Got an option (begins with dash) */ x = *(*yargv+1); /* Get option letter */ while (x) { /* Allow for bundled options */ debug(F000,"prescan arg","",x); switch (x) { #ifndef NOICP case '+': case '-': if (doxarg(yargv,1) < 0) { fatal("Extended argument error"); } #ifndef COMMENT /* Jeff 28 Apr 2003 */ yp = NULL; /* (not "") */ #else yargv++, yargc--; yp = *yargv; #endif /* COMMENT */ break; #endif /* NOICP */ case '7': /* Undocumented... */ sstelnet = 1; /* (because it doesn't work) */ break; #ifdef IKSD case 'A': { char * p; inserver = 1; /* Flag that we are doing this */ srvcdmsg = 2; /* Preset this */ /* See inserver section of ckcmai.c for more settings */ #ifdef OS2 if (*(yp+1)) { fatal("invalid argument bundling after -A"); } #ifdef NT /* Support for Pragma Systems Telnet/Terminal Servers */ p = getenv("PRAGMASYS_INETD_SOCK"); if (p && atoi(p) != 0) { ttname[0] = '$'; ckstrncpy(&ttname[1],p,TTNAMLEN-1); break; } #endif /* NT */ yargv++, yargc--; if (yargc < 1 || **yargv == '-') { fatal("-A argument missing"); } else { ttname[0] = '$'; ckstrncpy(&ttname[1],*yargv,TTNAMLEN-1); } #endif /* OS2 */ break; } #endif /* IKSD */ #ifdef OS2 case 'W': if (*(yp+1)) fatal("invalid argument bundling after -W"); yargv++, yargc--; if (yargc < 1) fatal("Window handle missing"); #ifdef COMMENT if (dummy) { yargv++, yargc--; break; } else { #endif /* COMMENT */ hwndDialer = (HWND) atol(*yargv); StartedFromDialer = 1; yargv++, yargc--; KermitDialerID = atol(*yargv) ; #ifdef COMMENT } #endif /* COMMENT */ break; case '#': /* K95 initialization options */ if (*(yp+1)) { fatal("invalid argument bundling"); } yargv++, yargc--; if (yargc < 1) fatal("-# argument missing"); startflags |= atol(*yargv); break; #endif /* OS2 */ #ifndef NOSPL case 'M': /* My User Name */ if (*(yp+1)) { fatal("invalid argument bundling"); } yargv++, yargc--; if ((yargc < 1) || (**yargv == '-')) { fatal("missing username"); } if ((int)strlen(*yargv) > UIDBUFLEN) { fatal("username too long"); } #ifdef COMMENT /* This can't work. uidbuf is overwritten in sysinit() which has yet to be called. This cannot be set in prescan(). */ #ifdef IKSD if (!inserver) #endif /* IKSD */ ckstrncpy(uidbuf,*yargv,UIDBUFLEN); #endif /* COMMENT */ break; #endif /* NOSPL */ case 'R': /* Remote-only advisory */ #ifdef CK_IFRO remonly = 1; #endif /* CK_IFRO */ break; case 'S': /* STAY */ stayflg = 1; break; case 'h': noinit = 1; #ifdef OS2 startflags |= 2; /* No network DLLs */ startflags |= 4; /* No TAPI DLLs */ startflags |= 8; /* No Security DLLs */ startflags |= 16; /* No Zmodem DLLs */ startflags |= 32; /* Stdin */ startflags |= 64; /* Stdout */ #endif /* OS2 */ break; #ifndef NOICP case 'Y': /* No init file */ noinit = 1; break; #endif /* NOICP */ case 'q': quiet = 1; break; case 'd': /* = SET DEBUG ON */ #ifdef DEBUG if (debcount++ > 0) debtim = 1; if (!deblog) deblog = debopn("debug.log",0); #endif /* DEBUG */ break; case 'x': /* Server */ arg_x = 1; /* Note in advance */ break; #ifndef NOICP case 'y': /* Alternative init file */ noinit = 0; yargv++, yargc--; if (yargc < 1) fatal("missing name in -y"); /* Replace init file name */ ckstrncpy(kermrc,*yargv,KERMRCL); rcflag = 1; /* Flag that this has been done */ debug(F111,"prescan kermrc",kermrc,rcflag); break; #endif /* NOICP */ case 'z': /* = SET BACKGROUND OFF */ bgset = 0; backgrd = 0; #ifdef VMS batch = 0; #endif /* VMS */ break; case 'B': /* Force background (batch) */ bgset = 1; backgrd = 1; #ifdef VMS batch = 1; #endif /* VMS */ break; #ifdef CK_NETBIOS case 'N': { int n ; yargv++, yargc--; #ifdef COMMENT if (y) break; #endif /* COMMENT */ if (strlen(*yargv) != 1 || (*yargv)[0] == 'X') { NetBiosAdapter = -1; } else { n = atoi(*yargv); if (n >= 0 && n <= 9) NetBiosAdapter = n; else NetBiosAdapter = -1; } } break; #endif /* CK_NETBIOS */ default: break; } if (!yp) break; x = *++yp; /* See if options are bundled */ } } } #endif /* NOCMDL */ } /* G E T T C S -- Get Transfer (Intermediate) Character Set */ /* Given two file character sets, this routine picks out the appropriate "transfer" character set to use for translating between them. The transfer character set number is returned. Translation between two file character sets is done, for example, by the CONNECT, TRANSMIT, and TRANSLATE commands. Translation between Kanji character sets is not yet supported. */ int gettcs(cs1,cs2) int cs1, cs2; { #ifdef NOCSETS /* No character-set support */ return(0); /* so no translation */ #else int tcs = TC_TRANSP; #ifdef KANJI /* Kanji not supported yet */ if (fcsinfo[cs1].alphabet == AL_JAPAN || fcsinfo[cs2].alphabet == AL_JAPAN ) tcs = TC_TRANSP; else #endif /* KANJI */ #ifdef CYRILLIC /* I can't remember why we don't test both sets here, but I think there must have been a reason... */ if (fcsinfo[cs2].alphabet == AL_CYRIL) tcs = TC_CYRILL; else #endif /* CYRILLIC */ #ifdef HEBREW if (fcsinfo[cs1].alphabet == AL_HEBREW || fcsinfo[cs2].alphabet == AL_HEBREW ) tcs = TC_HEBREW; else #endif /* HEBREW */ #ifdef GREEK if (fcsinfo[cs1].alphabet == AL_GREEK || fcsinfo[cs2].alphabet == AL_GREEK ) tcs = TC_GREEK; else #endif /* GREEK */ /* Roman sets ... */ #ifdef LATIN2 /* East European */ if (cs1 == FC_2LATIN || cs2 == FC_2LATIN || /* Latin-2 */ cs1 == FC_CP852 || cs2 == FC_CP852 || /* CP852 */ cs1 == FC_CP1250 || cs2 == FC_CP1250 || /* Windows Latin-2 */ cs1 == FC_MAZOVIA || cs2 == FC_MAZOVIA) /* Polish Mazovia */ tcs = TC_2LATIN; else #endif /* LATIN2 */ /* West European Euro-aware */ if (cs1 == FC_CP858 || cs1 == FC_9LATIN || cs2 == FC_CP858 || cs2 == FC_9LATIN) tcs = TC_9LATIN; else /* Traditional West European */ tcs = TC_1LATIN; return(tcs); #endif /* NOCSETS */ } #ifndef NOLOCAL /* D O C O N E C T -- Do the connect command */ /* q = 0 means issue normal informational message about how to get back, etc. q != 0 means to skip the message. */ int doconect(q,async) int q, async; { int x; /* Return code */ #ifdef CK_AUTODL extern CHAR ksbuf[]; #endif /* CK_AUTODL */ #ifndef NOKVERBS /* Keyboard macro material */ extern int keymac, keymacx; #endif /* NOKVERBS */ extern int justone, adl_err; int qsave; /* For remembering "quiet" value */ #ifdef OS2 extern int term_io; extern int display_demo; int term_io_save; #ifdef KUI extern int kui_async; #endif /* KUI */ #endif /* OS2 */ int is_tn = 0; #ifdef IKSD if (inserver) { if (!quiet) printf("?Sorry, IKSD cannot CONNECT.\r\n"); return(success = 0); } #endif /* IKSD */ is_tn = #ifdef TNCODE (local && network && IS_TELNET()) || (!local && sstelnet) #else 0 #endif /* TNCODE */ ; /* Saving, changing, and restoring the global "quiet" variable around calls to conect() to control whether the verbose CONNECT message is printed is obviously less elegant than passing a parameter to conect(), but we do it this way to avoid the need to change all of the ck?con.c modules. NOTE: it is important to restore the value immediately upon return in case there is an autodownload or APC. */ qsave = quiet; /* Save it */ if (!quiet && q > -1) quiet = q; /* Use argument temporarily */ conres(); /* Put console back to normal */ debug(F101,"doconect justone 1","",justone); #ifdef CK_AUTODL ksbuf[0] = NUL; /* Autodownload packet buffer */ #endif /* CK_AUTODL */ #ifdef OS2 display_demo = 1; /* Remember to display demo */ #endif /* OS2 */ #ifdef IKS_OPTION if (is_tn && TELOPT_U(TELOPT_KERMIT) && ttchk() >= 0 #ifdef OS2 && !viewonly #endif /* OS2 */ ) { /* If the remote side is in a state of IKS START-SERVER */ /* we request that the state be changed. We will detect */ /* a failure to adhere to the request when we call ttinc() */ if (!iks_wait(KERMIT_REQ_STOP,0) && !tcp_incoming) { if (!quiet) { printf("\r\nEnter Client/Server Mode... Use:\r\n\r\n"); printf( " REMOTE LOGIN to log in to the server if necessary.\r\n"); printf(" SEND and GET for file transfer.\r\n"); printf(" REMOTE commands for file management.\r\n"); printf(" FINISH to terminate Client/Server mode.\r\n"); printf(" BYE to terminate and close connection.\r\n"); printf(" REMOTE HELP for additional information.\r\n\r\n"); } quiet = qsave; return(0); /* Failure */ } } /* Let our peer know our state. */ #ifdef CK_AUTODL if (is_tn && TELOPT_ME(TELOPT_KERMIT) #ifdef OS2 && !viewonly #endif /* OS2 */ ) { if (autodl && !TELOPT_SB(TELOPT_KERMIT).kermit.me_start) { tn_siks(KERMIT_START); /* Send Kermit-Server Start */ } else if (!autodl && TELOPT_SB(TELOPT_KERMIT).kermit.me_start) { tn_siks(KERMIT_STOP); } } #else /* CK_AUTODL */ if (is_tn && TELOPT_ME(TELOPT_KERMIT) && TELOPT_SB(TELOPT_KERMIT).kermit.me_start) tn_siks(KERMIT_STOP); #endif /* CK_AUTODL */ #endif /* IKS_OPTION */ debug(F101,"doconect flow","",flow); #ifdef OS2 debug(F101,"doconect async","",async); #ifdef KUI if (kui_async) async = 1;; #endif /* KUI */ x = conect(async); /* Connect the first time */ #else /* OS2 */ x = conect(); #endif /* OS2 */ debok = 1; #ifdef IKS_OPTION if (TELOPT_U(TELOPT_KERMIT) && TELOPT_SB(TELOPT_KERMIT).kermit.u_start && !tcp_incoming && !quiet && ttchk() >= 0 ) { printf("\r\nEnter Client/Server Mode... Use:\r\n\r\n"); printf( " REMOTE LOGIN to log in to the server if necessary.\r\n"); printf(" SEND and GET for file transfer.\r\n"); printf(" REMOTE commands for file management.\r\n"); printf(" FINISH to terminate Client/Server mode.\r\n"); printf(" BYE to terminate and close connection.\r\n"); printf(" REMOTE HELP for additional information.\r\n\r\n"); } #endif /* IKS_OPTION */ quiet = qsave; /* Restore "quiet" value */ debug(F101,"doconect justone 2","",justone); #ifdef NETCONN if (network && tn_exit && ttchk() < 0) doexit(GOOD_EXIT,xitsta); /* Exit with good status */ #endif /* NETCONN */ #ifdef OS2ORUNIX /* Exit on disconnect if the port is not open or carrier detect */ if (exitonclose && (ttchk() < 0)) doexit(GOOD_EXIT,xitsta); #endif /* OS2ORUNIX */ #ifdef CKCONINTB4CB /* The order makes a difference in HP-UX 8.00. */ /* The other order makes it think it's in the background when it */ /* returns from CONNECT (Apr 1999). */ setint(); concb((char)escape); /* Restore console for commands */ #else /* This is how it has always been so better leave it */ /* this way for all non-HP-UX-8.00 builds. */ concb((char)escape); /* Restore console for commands */ setint(); #endif /* CKCONINTB4CB */ #ifdef OS2 if (!async) { term_io_save = term_io; /* Disable I/O by emulator */ term_io = 0; #endif /* OS2 */ #ifdef CK_APC /* If an APC command was received during CONNECT mode, we define it now as a macro, execute the macro, and then return to CONNECT mode. We do this in a WHILE loop in case additional APCs come during subsequent CONNECT sessions. */ debug(F101,"doconect apcactive","",apcactive); debug(F101,"doconect success","",success); while (x > 0 && (apcactive == APC_LOCAL || (apcactive == APC_REMOTE && apcstatus != APC_OFF))) { debug(F101,"doconect justone 3","",justone); if (mlook(mactab,"_apc_commands",nmac) == -1) { debug(F110,"doconect about to execute APC",apcbuf,0); domac("_apc_commands",apcbuf,cmdstk[cmdlvl].ccflgs|CF_APC); delmac("_apc_commands",1); #ifdef DEBUG } else { debug(F100,"doconect APC in progress","",0); #endif /* DEBUG */ } debug(F101,"doconect apcactive after domac","",apcactive); if (!apcactive) { /* In case CLEAR APC was in APC */ debug(F101,"doconect quit APC loop: apcactive","",apcactive); break; } /* Also don't reconnect if autodownload failed - very confusing! */ /* Let them view the local screen to see what happened. - fdc */ /* This should be conditional. If someone is relying on the */ /* connect mode autodownload for the kermit server to use with */ /* a remotely executed script we should be able to return to */ /* connect mode on the failure. What we really need to do is */ /* report the status of the transfer and then return to CONNECT. */ /* In unix this would simply be a printf(), but in K95 it could */ /* use a popup dialog to report the status. - Jeff */ #ifndef NOXFER debug(F101,"doconect xferstat","",xferstat); if (apcactive == APC_LOCAL && !xferstat && adl_err != 0) { debug(F101,"doconect quit APC loop: xferstat","",xferstat); apcactive = APC_INACTIVE; break; } #endif /* NOXFER */ #ifdef OS2 msleep(250); #endif /* OS2 */ debug(F101,"doconect justone 4","",justone); qsave = quiet; /* Do this again... */ if (!quiet && q > -1) quiet = q; #ifdef CK_AUTODL ksbuf[0] = NUL; #endif /* CK_AUTODL */ #ifdef IKS_OPTION #ifdef CK_AUTODL if (is_tn && TELOPT_ME(TELOPT_KERMIT) && !TELOPT_SB(TELOPT_KERMIT).kermit.me_start && autodl #ifdef CK_APC && !apcactive #endif /* CK_APC */ #ifdef OS2 && !viewonly #endif /* OS2 */ ) { tn_siks(KERMIT_START); /* Send Kermit-Server Start */ } #endif /* CK_AUTODL */ #endif /* IKS_OPTION */ #ifndef OS2 x = conect(); /* Re-CONNECT. */ #else /* OS2 */ x = conect(0); term_io = term_io_save; #endif /* OS2 */ debok = 1; quiet = qsave; debug(F101,"doconect justone 5","",justone); #ifdef NETCONN if (network && ttchk() < 0) { if (tn_exit || exitonclose) doexit(GOOD_EXIT,xitsta); else break; } #endif /* NETCONN */ #ifdef OS2ORUNIX /* If connection dropped */ if (ttchk() < 0) { concb((char)escape); /* Restore console. */ if (exitonclose) doexit(GOOD_EXIT,xitsta); else break; } #endif /* OS2ORUNIX */ } /* Loop back for more. */ #endif /* CK_APC */ #ifndef NOKVERBS if ((keymac > 0) && (keymacx > -1)) { /* Executing a keyboard macro? */ /* Set up the macro and return */ /* Do not clear the keymac flag */ #ifdef OS2 term_io = term_io_save; #endif /* OS2 */ return(dodo(keymacx,NULL,CF_KMAC|cmdstk[cmdlvl].ccflgs)); } #endif /* NOKVERBS */ #ifdef OS2 term_io = term_io_save; } /* if (!async) */ #endif /* OS2 */ #ifdef CKCONINTB4CB /* The order makes a difference in HP-UX 8.00. */ /* The other order makes it think it's in the background when it */ /* returns from CONNECT (Apr 1999). */ setint(); concb((char)escape); /* Restore console for commands */ #else /* This is how it has always been so better leave it */ /* this way for all non-HP-UX-8.00 builds. */ concb((char)escape); /* Restore console for commands */ setint(); #endif /* CKCONINTB4CB */ #ifdef OS2 if (!async) #endif /* OS2 */ what = W_COMMAND; /* Back in command mode. */ return(x); /* Done. */ } #endif /* NOLOCAL */ #ifndef NOICP #ifdef COMMENT /* It seemed that this was needed for OS/2, in which \v(cmdfile) and other file-oriented variables or functions can return filenames containing backslashes, which are subsequently interpreted as quotes rather than directory separators (e.g. see commented section for VN_CMDF below). But the problem can't be cured at this level. Example: type \v(cmdfile) Without doubling, the filename is parsed correctly, but then when passed to UNIX 'cat' through the shell, the backslash is removed, and then cat can't open the file. With doubling, the filename is not parsed correctly and the TYPE command fails immediately with a "file not found" error. */ /* Utility routine to double all backslashes in a string. s1 is pointer to source string, s2 is pointer to destination string, n is length of destination string, both NUL-terminated. Returns 0 if OK, -1 if not OK (destination string too short). */ int dblbs(s1,s2,n) char *s1, *s2; int n; { int i = 0; while (*s1) { if (*s1 == '\\') { if (++i > n) return(-1); *s2++ = '\\'; } if (++i > n) return(-1); *s2++ = *s1++; } *s2 = NUL; return(0); } #endif /* COMMENT */ char * gmdmtyp() { /* Get modem type */ #ifndef NODIAL int i, x; debug(F111,"gmdmtyp","mdmtyp",mdmtyp); debug(F111,"gmdmtyp","mdmsav",mdmsav); x = mdmtyp; if (x < 0) /* In case of network dialing */ x = mdmsav; if (x < 1) return("none"); else for (i = 0; i < nmdm; i++) if ((mdmtab[i].kwval == x) && (mdmtab[i].flgs == 0)) return(mdmtab[i].kwd); #endif /* NODIAL */ return("none"); } #ifndef NOXMIT #ifndef NOLOCAL /* T R A N S M I T -- Raw upload */ /* Obey current line, duplex, parity, flow, text/binary settings. */ /* Returns 0 upon apparent success, 1 on obvious failure. */ /*** Things to add: . Make both text and binary mode obey set file bytesize. . Maybe allow user to specify terminators other than CR? . Maybe allow user to specify prompts other than single characters? . Make STATISTICS also work for TRANSMIT. . If TRANSMIT is done without echo, make some kind of (optional) display. . Make the same optimization for binary-mode transmit that was done for text-mode (in the no-echo / no-prompt / no-pause case). ***/ /* T R A N S M I T -- Raw upload */ /* s is the filename, t is the turnaround (prompt) character */ /* Maximum number of characters to buffer. Must be less than LINBUFSIZ */ #ifdef OS2 #define XMBUFS 4096 /* For compatibility with XYZmodem */ #else /* OS2 */ #define XMBUFS 1024 #endif /* OS2 */ #ifdef TNCODE #ifndef IAC #define IAC 255 #endif /* IAC */ #endif /* TNCODE */ #define OUTXBUFSIZ 15 static CHAR inxbuf[OUTXBUFSIZ+1]; /* Host-to-screen expansion buffer */ static int inxcount = 0; /* and count */ static CHAR outxbuf[OUTXBUFSIZ+1]; /* Keyboard-to-host expansion buf */ static int outxcount = 0; /* and count */ /* T R A N S M I T -- Unguarded non-protocol file transmission */ /* Call with: char * s: Name of file to transmit. char t: Turnaround char for text-mode transmission (normally LF). int xlate: nonzero = charset translation for text-mode xfer, 0 = skip. int binary: nonzero = transmit in binary mode, 0 = in text mode. */ #define XBBUFSIZ 508 /* For binary blasting */ static CHAR xbbuf[XBBUFSIZ+4]; int #ifdef CK_ANSIC transmit(char * s, char t, int xlate, int binary, int xxecho) #else transmit(s,t,xlate,binary,xxecho) char *s; char t; int xlate, binary, xxecho; #endif /* CK_ANSIC */ /* transmit */ { #ifdef MAC extern char sstate; int count = 100; #else int count = 0; #ifdef OS2 #ifdef NT SIGTYP (* oldsig)(int); /* For saving old interrupt trap. */ #else /* NT */ SIGTYP (* volatile oldsig)(int); #endif /* NT */ #else /* OS2 */ SIGTYP (* oldsig)(); #endif /* OS2 */ #endif /* MAC */ int eof = 0; /* End of File flag */ int eol = 0; /* End of Line flag */ int rc = 1; /* Return code. 0=fail, 1=succeed. */ int myflow; /* Local copy of global flow... */ int is_tn = 0; /* Do Telnet negotiations */ int xbufsiz = XMBUFS; /* Size of TRANSMIT buffer */ int x, y, c, i; /* Int workers... */ int control = 0; /* Echo loop control */ long nbytes = 0; /* File byte count */ long zz; /* Long worker */ char *p; /* Char * worker */ #ifdef PIPESEND extern int pipesend; #endif /* PIPESEND */ #ifndef NOCSETS int tcs = TC_TRANSP; /* Intermediate (xfer) char set */ int langsv = L_USASCII; /* Save current language */ int unicode = 0; int tcssize = 0; #ifdef CK_ANSIC /* ANSI C prototypes... */ CHAR (*sxo)(CHAR); CHAR (*rxo)(CHAR); CHAR (*sxi)(CHAR); CHAR (*rxi)(CHAR); #else /* Not ANSI C... */ CHAR (*sxo)(); CHAR (*rxo)(); CHAR (*sxi)(); CHAR (*rxi)(); #endif /* CK_ANSIC */ #ifdef UNICODE union ck_short uc; int bomorder = 0; #ifdef CK_ANSIC extern int (*xl_ufc[MAXFCSETS+1])(USHORT); /* Unicode to FCS */ extern USHORT (*xl_fcu[MAXFCSETS+1])(CHAR); /* FCS to Unicode */ extern int (*xuf)(USHORT); extern USHORT (*xfu)(CHAR); #else extern int (*xl_ufc[MAXFCSETS+1])(); extern USHORT (*xl_fcu[MAXFCSETS+1])(); extern int (*xuf)(); extern USHORT (*xfu)(); #endif /* CK_ANSIC */ #endif /* UNICODE */ #endif /* NOCSETS */ debug(F101,"xmit t","",t); debug(F101,"xmit xlate","",xlate); debug(F101,"xmit binary","",binary); #ifdef PIPESEND if (pipesend) { if (nopush) return(-2); if (zxcmd(ZIFILE,s) < 1) { printf("?Can't start command: %s\n",s); return(0); } } else #endif /* PIPESEND */ if (zopeni(ZIFILE,s) == 0) { /* Open the file to be transmitted */ printf("?Can't open file %s\n",s); return(0); } x = -1; /* Open the communication channel */ if (ttopen(ttname,&x,mdmtyp,cdtimo) < 0) { /* (no harm if already open) */ printf("Can't open device %s\n",ttname); return(0); } zz = x ? speed : -1L; if (binary) { /* Binary file transmission */ myflow = (flow == FLO_XONX) ? FLO_NONE : flow; if (ttvt(zz,myflow) < 0) { /* So no Xon/Xoff! */ printf("Can't condition line\n"); return(0); } } else { if (ttpkt(zz,flow,parity) < 0) { /* Put the line in "packet mode" */ printf("Can't condition line\n"); /* so Xon/Xoff will work, etc. */ return(0); } } is_tn = #ifdef TNCODE (local && network && IS_TELNET()) || (!local && sstelnet) #else 0 #endif /* TNCODE */ ; #ifndef NOCSETS /* Set up character set translations */ tcs = 0; /* "Transfer" or "Other" charset */ sxo = rxo = NULL; /* Initialize byte-to-byte functions */ sxi = rxi = NULL; unicode = 0; /* Assume Unicode won't be involved */ if (!binary && xlate) { /* Set up charset translations */ /* In the SENDING direction, we are converting from the local file's character-set (fcharset) to the remote terminal charset (tcsr). In the RECEIVING direction (echoing) we are converting from the remote end of the terminal charset (tcsr) to its local end (tcsl), which is not necessarily the same as the file character-set. Especially when the file character set is UCS-2, which is not a valid terminal character set. The various combinations are represented in this table: FCS = File Character Set RCS = Remote Terminal Character Set CCS = Console (Local Terminal) Character Set 8 4 2 1 FCS FCS RCS CCS UCS UTF UTF UTF 0 0 0 0 = 0 = No translation 0 0 0 1 = 1 = FCS -> RCS, Echo RCS -> UTF 0 0 1 0 = 2 = FCS -> UTF, Echo UTF -> CCS 0 0 1 1 = 3 = FCS -> UTF, Echo no translation 0 1 0 0 = 4 = UTF -> RCS, Echo RCS -> CCS 0 1 0 1 = 5 = UTF -> RCS, Echo RCS -> UTF 0 1 1 0 = 6 = UTF -> UTF, Echo UTF -> CCS 0 1 1 1 = 7 = No translation 1 0 0 0 = 8 = UCS -> RCS, Echo RCS -> CCS 1 0 0 1 = 9 = UCS -> RCS, Echo RCS -> UTF 1 0 1 0 = 10 = UCS -> UTF, Echo UTF -> CCS 1 0 1 1 = 11 = UCS -> UTF, Echo no translation */ #ifdef UNICODE xfu = NULL; /* Unicode translation functions */ xuf = NULL; bomorder = ucsorder; /* UCS-2 byte order */ if (fcharset == FC_UCS2) /* File charset is UCS-2 */ unicode |= 8; else if (fcharset == FC_UTF8) /* File charset is UTF-8 */ unicode |= 4; if (tcsr == FC_UTF8) /* Remote term charset is UTF-8 */ unicode |= 2; if (tcsl == FC_UTF8) /* Local term charset is UTF-8 */ unicode |= 1; #endif /* UNICODE */ /* When Unicode not involved -- TCS is the intermediate (xfer) set, and: sxo = File-to-Intermediate charset function rxo = Intermediate-to-Remote-Terminal charset function sxi = Remote-Terminal-to-Intermediate rxi = Intermediate-to-Local-Terminal */ tcs = gettcs(tcsr,fcharset); /* Get intermediate set. */ sxo = xls[tcs][fcharset]; /* translation function */ rxo = xlr[tcs][tcsr]; /* pointers for output functions */ sxi = xls[tcs][tcsr]; /* and for input functions. */ rxi = xlr[tcs][tcsl]; /* At this point we have unicode nonzero if Unicode is involved in the conversion, and to 0 if it is not. The following is to prevent use of zmstuff() and zdstuff() by translation functions (stuffing works with file i/o, not with communication i/o). */ langsv = language; /* Save current SET LANGUAGE */ language = L_USASCII; /* No language-specific translations */ } #endif /* NOCSETS */ i = 0; /* Beginning of buffer. */ #ifndef MAC #ifndef AMIGA oldsig = signal(SIGINT, trtrap); /* Save current interrupt trap. */ #endif /* AMIGA */ #endif /* MAC */ tr_int = 0; /* Have not been interrupted (yet). */ rc = 1; /* Return code presumed good. */ #ifdef VMS conres(); #endif /* VMS */ #ifndef NOCSETS debug(F101,"XMIT unicode","",unicode); #ifdef UNICODE debug(F101,"XMIT bomorder","",bomorder); #endif /* UNICODE */ #endif /* NOCSETS */ c = 0; /* Initial condition */ while (c > -1 && !eof) { /* Loop for all characters in file */ eol = 0; #ifdef MAC /* * It is expensive to run the miniparser so don't do it for * every character. */ if (--count < 0) { count = 100; miniparser(1); if (sstate == 'a') { sstate = '\0'; goto xmitfail; } } #else /* Not MAC */ if (tr_int) { /* Interrupted? */ printf("^C...\n"); /* Print message */ goto xmitfail; } #endif /* MAC */ c = zminchar(); /* Get a file character */ #ifdef COMMENT /* too much */ #ifdef DEBUG if (deblog) { if (c < 0) debug(F101,"XMIT zminchar","",c); else debug(F000,"XMIT zminchar","",c); } #endif /* DEBUG */ #endif /* COMMENT */ if (c < -1) { /* Other error */ printf("?TRANSMIT file read error: %s\n",ck_errstr()); goto xmitfail; } else if (c > -1) { nbytes++; c &= fmask; /* Apply SET FILE BYTESIZE mask */ } else if (c == -1) { eof = 1; debug(F101,"XMIT eof","",eof); } if (binary) { /* Binary... */ if (c == -1) { /* If EOF */ rc = 1; /* Success */ eof = 1; goto xmitexit; /* Done */ } if (!xmitw && !xxecho) { /* Special "blast" mode */ if (count == XBBUFSIZ) { /* File input buffer full... */ while (count > 0) { errno = 0; y = ttol(xbbuf,count); if (y < 0) { /* try to send it. */ printf("?TRANSMIT output error: %s\n", ck_errstr()); debug(F111,"XMIT binary ttol error", ck_errstr(),errno); rc = 0; break; } if (y < 0) break; count -= y; } count = 0; } xbbuf[count++] = c; #ifdef TNCODE if (c == IAC && is_tn) /* Telnet IAC */ xbbuf[count++] = IAC; /* must be doubled */ #endif /* TNCODE */ continue; } if (ttoc(dopar((char) c)) < 0) { /* else just send the char */ printf("?Can't transmit character\n"); goto xmitfail; } #ifdef TNCODE if (c == IAC && is_tn) /* Quote Telnet IAC */ ttoc((char)IAC); #endif /* TNCODE */ if (xmitw) /* Pause if requested */ msleep(xmitw); if (xxecho) { /* SET TRANSMIT ECHO ON? */ if (duplex) { /* Yes, for half duplex */ #ifndef NOLOCAL #ifdef OS2 /* Echo to emulator */ scriptwrtbuf((USHORT)(c & cmdmsk)); #endif /* OS2 */ #endif /* NOLOCAL */ if (conoc((char)(c & cmdmsk)) < 0) /* echo locally. */ goto xmitfail; } else { /* For full duplex, */ int i, n; /* display whatever is there. */ n = ttchk(); /* See how many chars are waiting */ if (n < 0) { /* Connection dropped? */ printf("?Connection lost\n"); goto xmitfail; } for (i = 0; i < n; i++) { /* Read and echo that many. */ x = ttinc(xmitt); /* Timed read just in case. */ if (x > -1) { /* If no timeout */ if (parity) x &= 0x7f; /* display the char, */ #ifndef NOLOCAL #ifdef OS2 /* Echo to emulator */ scriptwrtbuf((USHORT)x); #endif /* OS2 */ #endif /* NOLOCAL */ if (conoc((char)(x & cmdmsk)) < 0) { printf("?Output error\n"); goto xmitfail; } } else if (x == -2) { printf("Connection closed.\n"); ttclos(1); goto xmitfail; } else if (x == -3) { printf( "Session Limit exceeded - closing connection.\n" ); ttclos(1); goto xmitfail; } else { printf("?Communications error\n"); goto xmitfail; } } } } else ttflui(); /* Not echoing, just flush input. */ } else { /* Text mode, line at a time. */ #ifdef UNICODE if (fcharset == FC_UCS2 && xlate) { /* Special for UCS-2 */ char xbuf[8]; x = 1 - (nbytes & 1); /* Odd or even byte */ if (x == 0) /* Note: 1 = the 1st, 0 = 2nd, etc */ uc.x_short = 0; if (bomorder) /* Little Endian */ x = 1 - x; /* Save byte in appropriate half */ debug(F101,"XMIT UCS2 x","",x); uc.x_char[x] = (CHAR) (c & 0xff); if (nbytes & 1) /* First byte, go back for next */ continue; if (nbytes == 2) { /* UCS-2 Byte Order Mark */ if (uc.x_short == (USHORT) 0xfeff) { debug(F100,"XMIT UCS2 BOM FEFF","",bomorder); continue; } else if (uc.x_short == (USHORT) 0xfffe) { bomorder = 1 - bomorder; debug(F100,"XMIT UCS2 BOM FFFE (swap)","",bomorder); continue; } } sprintf(xbuf,"%04X",uc.x_short); /* SAFE */ debug(F111,"XMIT UCS2",xbuf,uc.x_short); if (nbytes & 1) /* Special eol test for UCS-2 */ if (uc.x_short == '\n') eol = 1; #ifdef COMMENT if (uc.x_short == 0x2028 || uc.x_short == 0x2029) eol = 1; #endif /* COMMENT */ } else #endif /* UNICODE */ if (c == '\n') { /* Normal eol test otherwise */ eol = 1; } if (eol) { /* End of line? */ int stuff = -1; debug(F101,"XMIT eol length","",i); if (i == 0) { /* Blank line? */ if (xmitf) /* Yes, insert fill if asked. */ line[i++] = dopar((char) xmitf); } if (i == 0 || ((char) line[i-1]) != ((char) dopar(CR))) line[i++] = dopar(CR); /* Terminate it with CR */ if (xmitl) { stuff = LF; #ifdef TNCODE } else if (is_tn && (tn_nlm != TNL_CR)) { /* TELNET NEWLINE ON/OFF/RAW */ stuff = (tn_nlm == TNL_CRLF) ? LF : NUL; #endif /* TNCODE */ } if (stuff > -1) line[i++] = dopar((char)stuff); line[i] = NUL; debug(F111,"XMIT eol line",line,i); } else if (c != -1) { /* Not a newline, regular character */ int k, x; outxbuf[0] = c; /* In case of no translation */ outxcount = 1; /* Assume result is one byte */ #ifndef NOCSETS switch (unicode) { case 0: /* No Unicode involved */ case 1: if (xlate) { /* If not /TRANSPARENT */ /* Local-to-intermediate */ if (sxo) c = (*sxo)((char)c); /* Intermediate-to-remote */ if (rxo) c = (*rxo)((char)c); outxbuf[0] = c; } break; #ifdef UNICODE case 2: /* Local byte to UTF-8 */ case 3: xfu = xl_fcu[fcharset]; tcssize = fcsinfo[fcharset].size; outxcount = b_to_u((CHAR)c,outxbuf,OUTXBUFSIZ,tcssize); break; case 4: /* Local UTF-8 to remote byte */ case 5: xuf = xl_ufc[tcsr]; x = u_to_b((CHAR)c); /* Convert to byte */ if (x == -1) { /* If more input bytes needed */ continue; /* go back and get them */ } else if (x == -2) { /* LS or PS (shouldn't happen) */ outxbuf[0] = CR; } else if (x == -9) { /* UTF-8 error */ outxbuf[0] = '?'; /* Insert error char */ outxbuf[1] = u_to_b2(); /* Insert next char */ outxcount = 2; } else { outxbuf[0] = /* Otherwise store result */ (unsigned)(x & 0xff); } break; case 6: /* UTF-8 to UTF-8 */ case 7: break; case 8: /* UCS-2 to byte */ case 9: xuf = xl_ufc[tcsr]; outxbuf[0] = (*xuf)(uc.x_short); break; case 10: case 11: { /* UCS-2 to UTF-8 */ int j; CHAR * buf = NULL; x = ucs2_to_utf8(uc.x_short,&buf); if (x < 0) { outxbuf[0] = 0xff; /* (= U+FFFD) */ outxbuf[1] = 0xbd; x = 2; } for (j = 0; j < x; j++) outxbuf[j] = buf[j]; outxcount = x; break; } #endif /* UNICODE */ } #endif /* NOCSETS */ outxbuf[outxcount] = NUL; debug(F111,"XMIT outxbuf",outxbuf,outxcount); /* Now the input character (1 or more bytes) is translated into the output expansion buffer (1 or more bytes); outxcount = number of bytes to add to the TRANSMIT line buffer, which we do here, taking care of parity, SI/SO processing, and quoting Telnet IACs. */ for (k = 0; k < outxcount; k++) { c = outxbuf[k]; if (xmits && parity && (c & 0200)) { /* If shifting */ line[i++] = dopar(SO); /* needs to be done, */ line[i++] = dopar((char)c); /* do it here, */ line[i++] = dopar(SI); /* crudely. */ } else { line[i++] = dopar((char)c); #ifdef TNCODE if (c == (CHAR)IAC && is_tn) line[i++] = (CHAR)IAC; #endif /* TNCODE */ } } } /* Send characters if buffer full, or at end of line, or at end of file. (End of line only if echoing, waiting for a prompt, or pausing.) */ debug(F000,"XMIT c",ckitoa(i),c); if (i >= xbufsiz || eof || (eol && (xxecho || xmitw || t))) { p = line; line[i] = '\0'; debug(F111,"transmit buf",p,i); if (ttol((CHAR *)p,i) < 0) { /* try to send it. */ printf("?TRANSMIT output error: %s\n",ck_errstr()); rc = 0; break; } i = 0; /* Reset buffer pointer. */ /* Now we handle the echo. If the user wants to see it, or if we have to wait for the turnaround character, t. If the echo is being displayed, and terminal character-set translation is required, we do it here. */ if (duplex && xxecho) { /* If local echo, echo it */ if (parity || cmdmsk == 0x7f) { /* Strip hi bits */ char *ss = line; /* if necessary */ while (*ss) { *ss &= 0x7f; ss++; } } #ifndef NOLOCAL #ifdef OS2 { /* Echo to emulator */ char *ss = p; while (*ss) { scriptwrtbuf((USHORT)*ss); ss++; } } #endif /* OS2 */ #endif /* NOLOCAL */ if (conoll(p) < 0) goto xmitfail; } if (xmitw) /* Sleep TRANSMIT PAUSE interval */ msleep(xmitw); control = 0; /* Readback loop control */ if (t != 0 && eol) /* TRANSMIT PROMPT given and at EOL */ control |= 1; if (xxecho && !duplex) /* Echo desired and is remote */ control |= 2; if (control) { /* Do this if reading back the echo */ int n; x = 0; while (1) { if (control & 1) { /* Termination criterion */ if (x == t) /* for turnaround */ break; } else if (control & 2) { /* And for echoing */ if ((n = ttchk()) < 1) break; } if ((x = ttinc(xmitt)) < 0) { /* Read with timeout */ switch (x) { case -2: printf("Connection closed.\n"); ttclos(1); goto xmitfail; case -3: printf( "Session Limit exceeded - closing connection.\n" ); ttclos(1); /* full thru... */ goto xmitfail; default: printf("?Timeout\n"); goto xmitfail; } } if (x > -1 && (control & 2)) { /* Echo any echoes */ if (parity) x &= 0x7f; c = x; #ifndef NOLOCAL #ifdef OS2 scriptwrtbuf((USHORT)x); #endif /* OS2 */ #endif /* NOLOCAL */ inxbuf[0] = c; inxcount = 1; #ifndef NOCSETS switch (unicode & 3) { /* Remote bits */ case 0: if (xlate) { if (sxi) c = (*sxi)((CHAR)c); if (rxi) c = (*rxi)((CHAR)c); inxbuf[0] = c; } break; #ifdef UNICODE case 1: /* Remote Byte to local UTF-8 */ xfu = xl_fcu[tcsr]; tcssize = fcsinfo[tcsr].size; inxcount = b_to_u((CHAR)c, inxbuf, OUTXBUFSIZ, tcssize ); break; case 2: /* Remote UTF-8 to local Byte */ xuf = xl_ufc[tcsl]; x = u_to_b((CHAR)c); if (x < 0) continue; inxbuf[0] = (unsigned)(x & 0xff); break; case 3: /* UTF-8 to UTF-8 */ break; #endif /* UNICODE */ } #endif /* NOCSETS */ inxbuf[inxcount] = NUL; if (conxo(inxcount,(char *)inxbuf) < 0) goto xmitfail; } } } else /* Not echoing */ ttflui(); /* Just flush input buffer */ } /* End of buffer-dumping block */ } /* End of text mode */ if (eof) { rc = 1; goto xmitexit; } } /* End of character-reading loop */ xmitfail: /* Failure exit point */ rc = 0; xmitexit: /* General exit point */ if (rc > 0) { if (binary && !xmitw && !xxecho) { /* "blasting"? */ while (count > 0) { /* Partial buffer still to go? */ errno = 0; y = ttol(xbbuf,count); if (y < 0) { printf("?TRANSMIT output error: %s\n", ck_errstr()); debug(F111,"XMIT binary eof ttol error", ck_errstr(),errno); rc = 0; break; } count -= y; } } else if (!binary && *xmitbuf) { /* Anything to send at EOF? */ p = xmitbuf; /* Yes, point to string. */ while (*p) /* Send it. */ ttoc(dopar(*p++)); /* Don't worry about echo here. */ } } #ifndef AMIGA #ifndef MAC signal(SIGINT,oldsig); /* Put old signal action back. */ #endif /* MAC */ #endif /* AMIGA */ #ifdef VMS concb(escape); /* Put terminal back, */ #endif /* VMS */ zclose(ZIFILE); /* Close file, */ #ifndef NOCSETS language = langsv; /* restore language, */ #endif /* NOCSETS */ ttres(); /* and terminal modes, */ return(rc); /* and return successfully. */ } #endif /* NOLOCAL */ #endif /* NOXMIT */ #ifndef NOCSETS _PROTOTYP( CHAR (*sxx), (CHAR) ); /* Local translation function */ _PROTOTYP( CHAR (*rxx), (CHAR) ); /* Local translation function */ _PROTOTYP( CHAR zl1as, (CHAR) ); /* Latin-1 to ascii */ _PROTOTYP( CHAR xl1as, (CHAR) ); /* ditto */ /* X L A T E -- Translate a local file from one character set to another */ /* Translates input file (fin) from character set csin to character set csout and puts the result in the output file (fout). The two character sets are file character sets from fcstab. */ int xlate(fin, fout, csin, csout) char *fin, *fout; int csin, csout; { #ifndef MAC #ifdef OS2 extern int k95stdout; extern int wherex[], wherey[]; extern unsigned char colorcmd; #ifdef NT SIGTYP (* oldsig)(int); /* For saving old interrupt trap. */ #else /* NT */ SIGTYP (* volatile oldsig)(int); /* For saving old interrupt trap. */ #endif /* NT */ #else /* OS2 */ SIGTYP (* oldsig)(); #endif /* OS2 */ #endif /* MAC */ #ifdef CK_ANSIC int (*fn)(char); /* Output function pointer */ #else int (*fn)(); #endif /* CK_ANSIC */ extern int xlatype; int filecode; /* Code for output file */ int scrnflg = 0; int z = 1; /* Return code. */ int x, c, c2; /* Workers */ #ifndef UNICODE int tcs; #endif /* UNICODE */ ffc = 0L; if (zopeni(ZIFILE,fin) == 0) { /* Open the file to be translated */ #ifdef COMMENT /* An error message was already printed by zopeni() */ printf("?Can't open input file %s\n",fin); #endif /* COMMENT */ return(0); } #ifdef MAC /* If user specified no output file, it goes to the screen. For the Mac, this must be done a special way (result goes to a new window); the Mac doesn't have a "controlling terminal" device name. */ filecode = !strcmp(fout,CTTNAM) ? ZCTERM : ZOFILE; #else #ifdef VMS filecode = !strcmp(fout,CTTNAM) ? ZCTERM : ZMFILE; #else #ifdef OS2 filecode = (!stricmp(fout,"con") || !stricmp(fout,"con:")) ? ZCTERM : ZMFILE; if ((filecode == ZCTERM) && !k95stdout && !inserver) csout = FC_UCS2; #else /* OS2 */ filecode = ZOFILE; #endif /* OS2 */ #endif /* VMS */ #endif /* MAC */ if (zopeno(filecode,fout,NULL,NULL) == 0) { /* And the output file */ printf("?Can't open output file %s\n",fout); return(0); } #ifndef AMIGA #ifndef MAC oldsig = signal(SIGINT, trtrap); /* Save current interrupt trap. */ #endif /* MAC */ #endif /* AMIGA */ scrnflg = (filecode == ZCTERM); /* Set output function */ if (scrnflg) fn = NULL; else if (filecode == ZMFILE) fn = putmfil; else fn = putfil; tr_int = 0; /* Have not been interrupted (yet). */ z = 1; /* Return code presumed good. */ if (!scrnflg && !quiet) printf(" %s (%s) => %s (%s)\n", /* Say what we're doing. */ fin, fcsinfo[csin].keyword, fout,fcsinfo[csout].keyword ); #ifndef UNICODE /* Non-Unicode picks the "most appropriate" transfer character set as the intermediate set, which results in loss of any characters that the source and target sets have in common, but are lacking from the intermediate set. */ #ifdef KANJI /* Special handling for Japanese... */ if (fcsinfo[csin].alphabet == AL_JAPAN || fcsinfo[csout].alphabet == AL_JAPAN) { USHORT eu; int c, x, y; xpnbyte(-1,0,0,NULL); /* Reset output machine */ xlatype = XLA_JAPAN; while ((c = xgnbyte(FC_JEUC,csin,NULL)) > -1) { /* Get an EUC byte */ if (tr_int) { /* Interrupted? */ printf("^C...\n"); /* Print message */ z = 0; break; } /* Send EUC byte to output machine */ if ((x = xpnbyte(c,TC_JEUC,csout,fn)) < 0) { z = -1; break; } } goto xxlate; } #endif /* KANJI */ /* Regular bytewise conversion... */ tcs = gettcs(csin,csout); /* Get intermediate set. */ if (csin == csout) { /* Input and output sets the same? */ sxx = rxx = NULL; /* If so, no translation. */ } else { /* Otherwise, set up */ if (tcs < 0 || tcs > MAXTCSETS || csin < 0 || csin > MAXFCSETS || csout < 0 || csout > MAXFCSETS) { debug(F100,"XLATE csets out of range","",0); sxx = rxx = NULL; } else { sxx = xls[tcs][csin]; /* translation function */ rxx = xlr[tcs][csout]; /* pointers. */ if (rxx == zl1as) rxx = xl1as; } } while ((c = zminchar()) != -1) { /* Loop for all characters in file */ if (tr_int) { /* Interrupted? */ printf("^C...\n"); /* Print message */ z = 0; break; } if (sxx) c = (*sxx)((CHAR)c); /* From fcs1 to tcs */ if (rxx) c = (*rxx)((CHAR)c); /* from tcs to fcs2 */ if (zchout(filecode,(char)c) < 0) { /* Output xlated character */ z = -1; break; } } goto xxlate; /* Done. */ #else /* UNICODE */ /* Use Unicode as the intermediate character set. It's simple and gives little or no loss, but the overhead is a bit higher. */ initxlate(csin,csout); /* Set up translation functions */ if (xlatype == XLA_NONE) { while ((c = zminchar()) != -1) { /* Loop for all characters in file */ if (tr_int) { /* Interrupted? */ printf("^C...\n"); /* Print message */ z = 0; break; } if (zchout(filecode,(char)c) < 0) { /* Output xlated character */ z = -1; break; } } goto xxlate; /* Done. */ } #ifndef NOLOCAL #ifdef OS2 if (csout == FC_UCS2 && /* we're translating to UCS-2 */ filecode == ZCTERM && /* for the real screen... */ !k95stdout && !inserver ) { union { USHORT ucs2; UCHAR bytes[2]; } output; while (1) { /* In this case we go two-by-two. */ if ((c = xgnbyte(FC_UCS2,csin,NULL)) < 0) break; output.bytes[0] = c; if ((c = xgnbyte(FC_UCS2,csin,NULL)) < 0) break; output.bytes[1] = c; if (tr_int) { /* Interrupted? */ printf("^C...\n"); /* Print message */ z = 0; break; } VscrnWrtUCS2StrAtt(VCMD, &output.ucs2, 1, wherey[VCMD], wherex[VCMD], &colorcmd ); } } else #endif /* OS2 */ #endif /* NOLOCAL */ /* General case: Get next byte translated from fcs to UCS-2 */ #ifdef COMMENT while ((c = xgnbyte(FC_UCS2,csin,NULL)) > -1 && (c2 = xgnbyte(FC_UCS2,csin,NULL)) > -1) { extern int fileorder; if (tr_int) { /* Interrupted? */ printf("^C...\n"); /* Print message */ z = 0; break; } debug(F001,"XLATE c","",c); debug(F001,"XLATE c2","",c2); /* And then send UCS-2 byte to translate-and-output machine */ if ((x = xpnbyte(fileorder?c2:c,TC_UCS2,csout,fn)) < 0) { z = -1; break; } if ((x = xpnbyte(fileorder?c:c2,TC_UCS2,csout,fn)) < 0) { z = -1; break; } } #else while ((c = xgnbyte(FC_UCS2,csin,NULL)) > -1) { if (tr_int) { /* Interrupted? */ printf("^C...\n"); /* Print message */ z = 0; break; } if ((x = xpnbyte(c,TC_UCS2,csout,fn)) < 0) { z = -1; break; } } #endif /* COMMENT */ #endif /* UNICODE */ xxlate: /* Common exit point */ #ifndef AMIGA #ifndef MAC signal(SIGINT,oldsig); /* Put old signal action back. */ #endif /* MAC */ #endif /* AMIGA */ tr_int = 0; if (z < 0) { if (z == -1) printf("?File output error: %s\n",ck_errstr()); z = 0; } zclose(ZIFILE); /* Close files */ zclose(filecode); /* ... */ return(success = z); /* and return status. */ } int doxlate() { #ifdef OS2ONLY extern int tt_font; #endif /* OS2ONLY */ #ifdef UNIX extern char ** mtchs; /* zxpand() file list */ #endif /* UNIX */ int x, y, incs, outcs, multiple = 0, wild = 0, fc = 0, len = 0; int ofisdir = 0; char * s, * tocs = ""; if ((x = cmifi("File(s) to translate","",&s,&wild,xxstring)) < 0) { if (x == -3) { printf("?Name of an existing file\n"); return(-9); } else return(x); } ckstrncpy(line,s,LINBUFSIZ); /* Save copy of string just parsed. */ if ((incs = cmkey(fcstab,nfilc,"from character-set","",xxstring)) < 0) return(incs); #ifdef OS2 if (isunicode()) tocs = "ucs2"; else #endif /* OS2 */ tocs = getdcset(); if ((outcs = cmkey(fcstab,nfilc,"to character-set",tocs,xxstring)) < 0) return(outcs); if ((x = cmofi("output file",CTTNAM,&s,xxstring)) < 0) return(x); if (x > 1) ofisdir = 1; len = ckstrncpy(tmpbuf,s,TMPBUFSIZ); if ((y = cmcfm()) < 0) return(y); /* Confirm the command */ if (len < 1) return(-2); if (ofisdir) multiple = 2; else if (wild) { if (isdir(tmpbuf)) multiple = 2; else if (!strcmp(tmpbuf,CTTNAM)) multiple = 1; #ifdef OS2 else if (!stricmp(tmpbuf,"con") || !stricmp(tmpbuf,"con:")) multiple = 1; #else #ifdef UNIXOROSK else if (!strncmp(tmpbuf,"/dev/",4)) multiple = 1; #endif /* UNIXOROSK */ #endif /* OS2 */ if (!multiple) { printf("?A single file please\n"); return(-9); } } if (!multiple) { /* Just one file */ return(success = xlate(line,tmpbuf,incs,outcs)); } else { /* Translate multiple files */ char dirbuf[CKMAXPATH+4]; int k; #ifndef ZXREWIND int flags = ZX_FILONLY; #endif /* ZXREWIND */ if (multiple == 2) { /* Target is a directory */ k = ckstrncpy(dirbuf,tmpbuf,CKMAXPATH+1) - 1; if (k < 0) return(-2); #ifdef OS2ORUNIX if (dirbuf[k] != '/') { dirbuf[k+1] = '/'; dirbuf[k+2] = NUL; } #else #ifdef OSK if (dirbuf[k] != '/') { dirbuf[k+1] = '/'; dirbuf[k+2] = NUL; } #else #ifdef VMS if (ckmatch("*.DIR;1",s,0,0)) k = cvtdir(tmpbuf,dirbuf,TMPBUFSIZ); if (dirbuf[k] != ']' && dirbuf[k] != '>' && dirbuf[k] != ':') return(-2); #else #ifdef datageneral if (dirbuf[k] != ':') { dirbuf[k+1] = ':'; dirbuf[k+2] = NUL; } #else #ifdef STRATUS if (dirbuf[k] != '>') { dirbuf[k+1] = '>'; dirbuf[k+2] = NUL; } #endif /* STRATUS */ #endif /* datageneral */ #endif /* VMS */ #endif /* OSK */ #endif /* OS2ORUNIX */ } #ifdef ZXREWIND fc = zxrewind(); /* Rewind the file list */ #else if (matchdot) flags |= ZX_MATCHDOT; fc = nzxpand(line,flags); #endif /* ZXREWIND */ if (fc < 1) { printf("?Wildcard expansion error\n"); return(-9); } #ifdef UNIX sh_sort(mtchs,NULL,fc,0,0,filecase); /* Sort the file list */ #endif /* UNIX */ while (1) { /* Loop through the files */ znext(line); if (!line[0]) break; if (multiple == 2) ckmakmsg(tmpbuf,TMPBUFSIZ,dirbuf,line,NULL,NULL); if (xlate(line,tmpbuf,incs,outcs) < 1) return(success = 0); } } return(success = 1); } #endif /* NOCSETS */ static char hompthbuf[CKMAXPATH+1]; char * homepath() { int x; extern char * myhome; char * h; h = myhome ? myhome : zhome(); hompthbuf[0] = NUL; #ifdef UNIXOROSK x = ckstrncpy(hompthbuf,h,CKMAXPATH+1); if (x <= 0) { hompthbuf[0] = '/'; hompthbuf[1] = NUL; } else if (x < CKMAXPATH - 2 && hompthbuf[x-1] != '/') { hompthbuf[x] = '/'; hompthbuf[x+1] = NUL; } return(hompthbuf); #else #ifdef STRATUS if (strlen(h) < CKMAXPATH) sprintf(hompthbuf,"%s>",h); /* SAFE */ return(hompthbuf); #else return(h); #endif /* STRATUS */ #endif /* UNIXOROSK */ } /* D O L O G -- Do the log command */ int dolog(x) int x; { int y, disp; char *s = NULL, * p = NULL, * q = NULL; extern int isguest; #ifdef ZFNQFP struct zfnfp * fnp; #endif /* ZFNQFP */ if (isguest) { printf("?Anonymous log creation not allowed\n"); return(-9); } switch (x) { /* Which log... */ #ifdef DEBUG case LOGD: q = "debug.log"; y = cmofi("Name of debugging log file",q,&s,xxstring); break; #endif /* DEBUG */ case LOGP: q = "packet.log"; y = cmofi("Name of packet log file",q,&s,xxstring); break; #ifndef NOLOCAL case LOGS: q = "session.log"; y = cmofi("Name of session log file",q,&s,xxstring); break; #endif /* NOLOCAL */ #ifdef TLOG case LOGT: q = "transact.log"; y = cmofi("Name of transaction log file",q,&s,xxstring); break; #endif /* TLOG */ #ifdef CKLOGDIAL case LOGM: { int m, n; char mypath[CKMAXPATH+1]; q = CXLOGFILE; m = ckstrncpy(mypath,homepath(),CKMAXPATH); n = strlen(CXLOGFILE); if (m + n < CKMAXPATH) ckstrncat(mypath,CXLOGFILE,CKMAXPATH); else ckstrncpy(mypath,CXLOGFILE,CKMAXPATH); y = cmofi("Name of connection log file",mypath,&s,xxstring); break; } #endif /* CKLOGDIAL */ default: printf("\n?Unknown log designator - %d\n",x); return(-2); } if (y < 0) return(y); if (y == 2) { /* If they gave a directory name */ int k; char * ds = "/"; k = strlen(s); if (k > 0 && s[k-1] == '/') ds = ""; ckmakmsg(tmpbuf,TMPBUFSIZ,s,ds,q,NULL); s = tmpbuf; } #ifdef ZFNQFP #ifdef OS2ORUNIX if (*s != '|') /* Allow for pipes */ #else #ifdef OSK if (*s != '|') #endif /* OSK */ #endif /* OS2ORUNIX */ if ((fnp = zfnqfp(s,TMPBUFSIZ - 1,tmpbuf))) { if (fnp->fpath) if ((int) strlen(fnp->fpath) > 0) s = fnp->fpath; } /* else if error keep original string */ #endif /* ZFNQFP */ ckstrncpy(line,s,LINBUFSIZ); s = line; #ifdef MAC y = 0; #else p = "new"; #ifdef TLOG if ((x == LOGT && tlogfmt == 2) || x == LOGM) p = "append"; #endif /* TLOG */ if ((y = cmkey(disptb,2,"Disposition",p,xxstring)) < 0) return(y); #endif /* MAC */ disp = y; if ((y = cmcfm()) < 0) return(y); switch (x) { #ifdef DEBUG case LOGD: return(deblog = debopn(s,disp)); #endif /* DEBUG */ #ifndef NOXFER case LOGP: return(pktlog = pktopn(s,disp)); #endif /* NOXFER */ #ifndef NOLOCAL case LOGS: setseslog(sesopn(s,disp)); return(seslog); #endif /* NOLOCAL */ #ifdef TLOG case LOGT: return(tralog = traopn(s,disp)); #endif /* TLOG */ #ifdef CKLOGDIAL case LOGM: return(dialog = diaopn(s,disp,0)); #endif /* CKLOGDIAL */ default: return(-2); } } #ifndef NOXFER int pktopn(s,disp) char *s; int disp; { static struct filinfo xx; if (!s) s = ""; if (!*s) return(0); debug(F111,"pktopn",s,disp); zclose(ZPFILE); #ifdef OS2ORUNIX if (s[0] == '|') { /* Pipe */ char * p = s + 1; debug(F110,"pktopn p",p,0); while (*p) { if (*p != ' ') break; else p++; } debug(F110,"pktopn pipe",p,0); pktlog = zxcmd(ZPFILE,p); debug(F101,"pktopn seslog","",seslog); } else { /* File */ #endif /* OS2ORUNIX */ if (disp) { xx.bs = 0; xx.cs = 0; xx.rl = 0; xx.org = 0; xx.cc = 0; xx.typ = 0; xx.dsp = XYFZ_A; xx.os_specific = ""; xx.lblopts = 0; pktlog = zopeno(ZPFILE,s,NULL,&xx); } else pktlog = zopeno(ZPFILE,s,NULL,NULL); if (!pktlog && !quiet) printf("?%s - %s\n",s,ck_errstr()); #ifdef OS2ORUNIX } #endif /* OS2ORUNIX */ if (pktlog > 0) ckstrncpy(pktfil,s,CKMAXPATH+1); else *pktfil = '\0'; return(pktlog); } #endif /* NOXFER */ int traopn(s,disp) char *s; int disp; { #ifdef TLOG static struct filinfo xx; if (!s) s = ""; if (!*s) return(0); debug(F111,"traopn",s,disp); debug(F101,"traopn tlogfmt","",tlogfmt); zclose(ZTFILE); #ifdef OS2ORUNIX if (tlogfmt == 2) { /* FTP format is special... */ VOID doiklog(); if (!disp) /* Append? */ if (zchki(s) > -1) /* No - does file exist? */ (VOID) zdelet(s); /* Yes - delete it. */ xferlog = 1; ckstrncpy(trafil,s,CKMAXPATH); makestr(&xferfile,s); doiklog(); return(1); } if (s[0] == '|') { /* Pipe */ char * p = s + 1; debug(F110,"traopn p",p,0); while (*p) { if (*p != ' ') break; else p++; } debug(F110,"traopn pipe",p,0); tralog = zxcmd(ZTFILE,p); debug(F101,"traopn tralog","",tralog); } #endif /* OS2ORUNIX */ if (s[0] != '|') { /* File */ if (disp) { xx.bs = 0; xx.cs = 0; xx.rl = 0; xx.org = 0; xx.cc = 0; xx.typ = 0; xx.dsp = XYFZ_A; xx.os_specific = ""; xx.lblopts = 0; tralog = zopeno(ZTFILE,s,NULL,&xx); } else tralog = zopeno(ZTFILE,s,NULL,NULL); } if (!tralog && !quiet) printf("?%s - %s\n",s,ck_errstr()); if (tralog > 0 && tlogfmt > 0) { ckstrncpy(trafil,s,CKMAXPATH); tlog(F110,"Transaction Log:",versio,0L); #ifndef MAC tlog(F100,ckxsys,"",0L); #endif /* MAC */ ztime(&s); tlog(F100,s,"",0L); } else *trafil = '\0'; return(tralog); #else return(0); #endif /* TLOG */ } #ifndef NOLOCAL int sesopn(s,disp) char * s; int disp; { static struct filinfo xx; extern int tsstate; tsstate = 0; /* Session log timestamp state */ if (!s) s = ""; if (!*s) return(0); debug(F111,"sesopn",s,disp); zclose(ZSFILE); #ifdef OS2ORUNIX if (s[0] == '|') { /* Pipe */ char * p = s + 1; debug(F110,"sesopn p",p,0); while (*p) { if (*p != ' ') break; else p++; } debug(F110,"sesopn pipe",p,0); setseslog(zxcmd(ZSFILE,p)); debug(F101,"sesopn seslog","",seslog); } else { /* File */ #endif /* OS2ORUNIX */ if (disp) { xx.bs = 0; xx.cs = 0; xx.rl = 0; xx.org = 0; xx.cc = 0; xx.typ = 0; xx.dsp = XYFZ_A; xx.os_specific = ""; xx.lblopts = 0; setseslog(zopeno(ZSFILE,s,NULL,&xx)); } else setseslog(zopeno(ZSFILE,s,NULL,NULL)); if (!seslog && !quiet) printf("?%s - %s\n",s,ck_errstr()); #ifdef OS2ORUNIX } #endif /* OS2ORUNIX */ if (seslog > 0) ckstrncpy(sesfil,s,CKMAXPATH+1); else *sesfil = '\0'; return(seslog); } #endif /* NOLOCAL */ #endif /* NOICP */ int debopn(s,disp) char *s; int disp; { #ifdef DEBUG #ifdef CK_UTSNAME extern char unm_mch[], unm_nam[], unm_rel[], unm_ver[], unm_mod[]; #endif /* CK_UTSNAME */ #ifdef OS2 extern char ckxsystem[]; #endif /* OS2 */ char *tp; static struct filinfo xx; if (!s) s = ""; if (!*s) return(0); zclose(ZDFILE); #ifdef OS2ORUNIX if (s[0] == '|') { /* Pipe */ char * p = s + 1; debug(F110,"debopn p",p,0); while (*p) { if (*p != ' ') break; else p++; } debug(F110,"debopn pipe",p,0); deblog = zxcmd(ZDFILE,p); debug(F101,"debopn deblog","",deblog); } else { /* File */ #endif /* OS2ORUNIX */ if (disp) { xx.bs = 0; xx.cs = 0; xx.rl = 0; xx.org = 0; xx.cc = 0; xx.typ = 0; xx.dsp = XYFZ_A; xx.os_specific = ""; xx.lblopts = 0; deblog = zopeno(ZDFILE,s,NULL,&xx); } else deblog = zopeno(ZDFILE,s,NULL,NULL); if (!deblog && !quiet) printf("?%s - %s\n",s,ck_errstr()); #ifdef OS2ORUNIX } #endif /* OS2ORUNIX */ if (deblog > 0) { ckstrncpy(debfil,s,CKMAXPATH+1); debug(F110,"Debug Log ",versio,0); #ifndef MAC #ifdef OS2 debug(F110,ckxsys,ckxsystem,0); #else /* OS2 */ debug(F100,ckxsys,"",0); #endif /* OS2 */ #endif /* MAC */ #ifdef CK_UTSNAME if (unm_mch[0]) { debug(F110,"uname machine",unm_mch,0); if (unm_mod[0]) debug(F110,"uname model ",unm_mod,0); debug(F110,"uname sysname",unm_nam,0); debug(F110,"uname release",unm_rel,0); debug(F110,"uname version",unm_ver,0); } #ifdef KTARGET { char * s; /* Makefile target */ s = KTARGET; if (!s) s = ""; if (!*s) s = "(unknown)"; debug(F110,"build target",s,0); } #endif /* KTARGET */ deblog = 0; ztime(&tp); deblog = 1; debug(F100,tp,"",0); #endif /* UTSNAME */ debug(F101,"byteorder","",byteorder); #ifndef NOICP #ifndef NOLOCAL if (local) { debug(F110,"Active connection: ",ttname,0); if (!network) { debug(F101,"Speed","",speed); if (hwparity) debug(F110,"Parity[hardware]",parnam((char)hwparity),0); else debug(F110,"Parity",parnam((char)parity),0); deblog = 0; debug(F110,"Modem",gmdmtyp(),0); deblog = 1; } } else { debug(F110,"Active connection: ","none",0); } #endif /* NOLOCAL */ #endif /* NOICP */ } else *debfil = '\0'; return(deblog); #else return(0); #endif /* MAC */ } /* C K D A T E -- Returns current date/time in standard format */ static char nowbuf[18]; char * ckdate() { extern struct keytab cmonths[]; int x; char * t; /* Substitute today's date */ char dbuf[32]; ztime(&t); /* 012345678901234567890123 */ /* Sat Jul 4 12:16:43 1998 */ ckstrncpy(dbuf,t,32); t = dbuf; debug(F110,"ckdate dbuf",dbuf,0); nowbuf[0] = t[20]; nowbuf[1] = t[21]; nowbuf[2] = t[22]; nowbuf[3] = t[23]; nowbuf[4] = NUL; debug(F110,"ckdate nowbuf",nowbuf,0); t[7] = NUL; if ((x = lookup(cmonths,t+4,12,NULL)) < 0) { debug(F110,"ckdate bad month",t,0); return(""); } sprintf(nowbuf+4,"%02d",x); /* SAFE */ nowbuf[6] = (t[8] == SP) ? '0' : t[8]; nowbuf[7] = t[9]; nowbuf[8] = ' '; nowbuf[9] = NUL; debug(F110,"ckdate nowbuf",nowbuf,0); for (x = 11; x < 19; x++) nowbuf[x-2] = t[x]; nowbuf[17] = NUL; debug(F110,"ckdate nowbuf",nowbuf,0); return((char *)nowbuf); } #ifndef NOICP #ifdef CKLOGDIAL /* fc = 0 for initial open, meaning open, then close immediately. fc > 0 for subsequent opens, meaning open for use, leave open. */ int diaopn(s,disp,fc) char *s; int disp, fc; { static struct filinfo xx; if (!s) s = ""; if (!*s) return(0); debug(F110,"diaopn log",s,0); debug(F101,"diaopn fc",s,fc); debug(F101,"diaopn disp 1",s,disp); if (fc) disp = 1; /* Force append if open for use */ debug(F101,"diaopn disp 2",s,disp); zclose(ZDIFIL); /* In case a log was already open */ #ifdef OS2ORUNIX if (s[0] == '|') { /* Pipe */ char * p = s + 1; debug(F110,"diaopn p",p,0); while (*p) { if (*p != ' ') break; else p++; } debug(F110,"diaopn pipe",p,0); dialog = zxcmd(ZDIFIL,p); debug(F101,"diaopn dialog","",dialog); } else { /* File */ #endif /* OS2ORUNIX */ if (disp) { xx.bs = 0; xx.cs = 0; xx.rl = 0; xx.org = 0; xx.cc = 0; xx.typ = 0; xx.dsp = XYFZ_A; xx.os_specific = ""; xx.lblopts = 0; dialog = zopeno(ZDIFIL,s,NULL,&xx); } else dialog = zopeno(ZDIFIL,s,NULL,NULL); if (!dialog) printf("?%s - %s\n",s,ck_errstr()); #ifdef OS2ORUNIX } #endif /* OS2ORUNIX */ if (dialog > 0) ckstrncpy(diafil,s,CKMAXPATH+1); else *diafil = '\0'; if (fc == 0) /* Initial open */ zclose(ZDIFIL); /* close it */ return(dialog); } #endif /* CKLOGDIAL */ #ifndef NOSHOW /* SHOW command routines */ char * shoxm() { char * s; switch (binary) { case XYFT_T: s = "text"; break; #ifdef VMS case XYFT_B: s = "binary fixed"; break; case XYFT_I: s = "image"; break; case XYFT_L: s = "labeled"; break; case XYFT_U: s = "binary undef"; break; #else #ifdef MAC case XYFT_B: s = "binary"; break; case XYFT_M: s = "macbinary"; break; #else case XYFT_B: s = "binary"; break; #ifdef CK_LABELED case XYFT_L: s = "labeled"; break; #endif /* CK_LABELED */ #endif /* MAC */ #endif /* VMS */ default: s = "unknown"; break; } return(s); } #ifndef NOXFER VOID /* SHOW TRANSFER */ shoxfer() { extern int docrc, usepipes, xfrxla, whereflg; extern char * xfrmsg; printf("\n"); printf(" Transfer Bell: %s\n",showoff(xfrbel)); printf(" Transfer Interruption: %s\n",showoff(xfrint)); printf(" Transfer Cancellation: %s\n",showoff(xfrcan)); #ifndef NOCSETS printf(" Transfer Translation: %s\n",showoff(xfrxla)); printf(" Transfer Character-set: "); if (tcharset == TC_TRANSP) printf("Transparent\n"); else printf("%s\n",tcsinfo[tcharset].keyword); #endif /* NOCSETS */ printf(" Transfer CRC-calculation: %s\n",showoff(docrc)); printf(" Transfer Display: "); switch (fdispla) { case XYFD_N: printf("%s\n","none"); break; case XYFD_R: printf("%s\n","serial"); break; case XYFD_C: printf("%s\n","fullscreen"); break; case XYFD_S: printf("%s\n","crt"); break; case XYFD_B: printf("%s\n","brief"); break; case XYFD_G: printf("%s\n","gui"); break; } printf(" Transfer Message: %s\n", xfrmsg ? xfrmsg : "(none)"); printf(" Transfer Locking-shift: "); if (lscapu == 2) { printf("forced"); } else { printf("%s", (lscapr ? "enabled" : "disabled")); if (lscapr) printf(",%s%s", (lscapu ? " " : " not "), "used"); } printf("\n Transfer Mode: %s\n", xfermode == XMODE_A ? "automatic" : "manual" ); printf(" Transfer Pipes: %s\n", showoff(usepipes)); printf(" Transfer Protocol: %s\n",ptab[protocol].p_name); printf(" Transfer Report: %s\n",showoff(whereflg)); printf(" Transfer Slow-start: %s\n",showoff(slostart)); printf("\n"); } #endif /* NOXFER */ VOID shoflow() { int i, x; extern int cxflow[], cxtype, ncxname, nfloname, autoflow; extern char * cxname[]; printf("\nConnection type: %s\n",cxname[cxtype]); if (autoflow) { printf("Current flow-control: %s\n", floname[cxflow[cxtype]]); printf("Switches automatically: yes\n"); } else { printf("Current flow-control: %s\n", floname[flow]); printf("Switches automatically: no\n"); } printf("\nDefaults by connection type:\n"); debug(F111,"shoflow cxtype",cxname[cxtype],cxtype); debug(F101,"shoflow flow","",flow); for (i = 0; i < ncxname; i++) { #ifdef NOLOCAL if (i > 0) break; #endif /* NOLOCAL */ #ifndef NETCONN if (i > 2) break; #endif /* NETCONN */ #ifndef DECNET if (i == CXT_DECNET) continue; #endif /* DECNET */ #ifndef DECNET #ifndef SUPERLAT if (i == CXT_LAT) continue; #endif /* SUPERLAT */ #endif /* DECNET */ #ifndef CK_NETBIOS if (i == CXT_NETBIOS) continue; #endif /* CK_NETBIOS */ #ifndef NPIPE if (i == CXT_NPIPE) continue; #endif /* NPIPE */ #ifndef NETCMD if (i == CXT_PIPE) continue; #endif /* NETCMD */ #ifndef ANYX25 if (i == CXT_X25) continue; #endif /* ANYX25 */ x = cxflow[i]; debug(F101,"shoflow x","",x); if (x < nfloname) printf(" %-14s: %s\n",cxname[i],floname[x]); else printf(" %-14s: (%d)\n",cxname[i],x); } printf("\n"); } #ifndef NOLOCAL #ifdef ANYX25 int shox25(n) int n; { if (nettype == NET_SX25) { printf("SunLink X.25 V%d.%d",x25ver / 10,x25ver % 10); if (ttnproto == NP_X3) printf(", PAD X.3, X.28, X.29 protocol,"); printf("\n"); if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0; printf(" Reverse charge call %s", revcall ? "selected" : "not selected"); printf (", Closed user group "); if (closgr > -1) printf ("%d",closgr); else printf ("not selected"); printf("\n"); if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0; printf(" Call user data %s.\n", cudata ? udata : "not selected"); if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0; } else if (nettype == NET_VX25) { if (ttnproto == NP_X3) printf(", PAD X.3, X.28, X.29 protocol,"); printf("\n"); if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0; printf(" Reverse charge call %s", revcall ? "selected" : "not selected"); printf (", Closed user group [unsupported]"); if (closgr > -1) printf ("%d",closgr); else printf ("not selected"); printf (","); printf("\n"); if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0; printf(" Call user data %s.\n", cudata ? udata : "not selected"); if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0; } else if (nettype == NET_IX25) { printf("AIX NPI X.25\n"); if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0; printf("\n Reverse charge call %s", revcall ? "selected" : "not selected"); printf (", Closed user group [unsupported]"); if (closgr > -1) printf ("%d",closgr); else printf ("not selected"); printf (","); printf("\n Call user data %s.\n", cudata ? udata : "not selected"); } return(n); } #ifndef IBMX25 int shopad(n) int n; { int i; printf("\nX.3 PAD Parameters:\n"); if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0; for (i = 0; i < npadx3; i++) { printf(" [%d] %s %d\n",padx3tab[i].kwval,padx3tab[i].kwd, padparms[padx3tab[i].kwval]); if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0; } return(n); } #endif /* IBMX25 */ #endif /* ANYX25 */ VOID shoparc() { extern int reliable, stopbits, clsondisc; int i; char *s; long zz; #ifdef NEWFTP if (ftpisconnected()) { shoftp(1); printf("\n"); } #endif /* NEWFTP */ printf("Communications Parameters:\n"); if (network #ifdef IKSD || inserver #endif /* IKSD */ ) { printf(" Network Host: %s%s",ttname, (reliable == SET_ON || (reliable == SET_AUTO && !local) #ifdef TN_COMPORT && !istncomport() #endif /* TN_COMPORT */ #ifdef IKSD || inserver #endif /* IKSD */ ) ? " (reliable)" : ""); #ifdef TN_COMPORT if (istncomport()) { int modemstate; char * oflow, * iflow = "", * parity, * stopsize, * signature; int baud = tnc_get_baud(); switch (tnc_get_oflow()) { case TNC_CTL_OFLOW_NONE: oflow = "none"; break; case TNC_CTL_OFLOW_XON_XOFF: oflow = "xon/xoff"; break; case TNC_CTL_OFLOW_RTS_CTS: oflow = "rts/cts"; break; case TNC_CTL_OFLOW_DCD: oflow = "dcd"; break; case TNC_CTL_OFLOW_DSR: oflow = "dsr"; break; default: oflow = "(unknown)"; } switch (tnc_get_iflow()) { case TNC_CTL_IFLOW_NONE: iflow = "none"; break; case TNC_CTL_IFLOW_XON_XOFF: iflow = "xon/xoff"; break; case TNC_CTL_IFLOW_RTS_CTS: iflow = "rts/cts"; break; case TNC_CTL_IFLOW_DTR: break; default: iflow = oflow; } switch (tnc_get_parity()) { case TNC_PAR_NONE: parity = "none"; break; case TNC_PAR_ODD: parity = "odd"; break; case TNC_PAR_EVEN: parity = "even"; break; case TNC_PAR_MARK: parity = "mark"; break; case TNC_PAR_SPACE: parity = "space"; break; default: parity = "(unknown)"; } switch (tnc_get_stopsize()) { case TNC_SB_1: stopsize = "1"; break; case TNC_SB_1_5: stopsize = "1.5"; break; case TNC_SB_2: stopsize = "2"; break; default: stopsize = "(unknown)"; } signature = (char *)tnc_get_signature(); printf("\n Signature : %s\n",signature?signature:""); if (baud <= 0) printf(" Speed : (unknown)\n"); else printf(" Speed : %d\n", baud); printf(" Outbound Flow Control: %s\n", oflow); printf(" Inbound Flow Control : %s\n", iflow); printf(" Parity : %s\n", parity); printf(" Data Size : %d\n", tnc_get_datasize()); printf(" Stop Bits : %s\n", stopsize); printf(" DTR Signal : %d\n", tnc_get_dtr_state()); printf(" RTS Signal : %d\n", tnc_get_rts_state()); printf(" Modem State:\n"); modemstate = tnc_get_ms(); if (modemstate & TNC_MS_EDGE_RING) printf(" Trailing Edge Ring Detector On\n"); else printf(" Trailing Edge Ring Detector Off\n"); if (modemstate & TNC_MS_CTS_SIG) printf(" CTS Signal On\n"); else printf(" CTS Signal Off\n"); if (modemstate & TNC_MS_DSR_SIG) printf(" DSR Signal On\n"); else printf(" DSR Signal Off\n"); if (modemstate & TNC_MS_RI_SIG) printf(" Ring Indicator On\n"); else printf(" Ring Indicator Off\n"); if (modemstate & TNC_MS_RLSD_SIG) printf(" RLSD (CD) Signal On\n"); else printf(" RLSD (CD) Signal Off\n"); printf("\n"); } #endif /* TN_COMPORT */ } else { printf(" %s: %s%s, speed: ", #ifdef OS2 "Port", #else "Line", #endif /* OS2 */ ttname, #ifdef CK_TTYFD (local && #ifdef VMS vmsttyfd() < 0 #else ttyfd == -1 #endif /* VMS */ ) ? " (closed)" : (reliable == SET_ON ? " (reliable)" : "") #else "" #endif /* CK_TTYFD */ ); if ( #ifdef CK_TTYFD #ifdef VMS vmsttyfd() < 0 #else ttyfd == -1 #endif /* VMS */ || #endif /* CK_TTYFD */ (zz = ttgspd()) < 0) { printf("unknown"); } else { if (speed == 8880) printf("75/1200"); else if (speed == 134) printf("134.5"); else printf("%ld",zz); } } if (network #ifdef IKSD || inserver #endif /* IKSD */ ) printf("\n Mode: "); else printf(", mode: "); if (local) printf("local"); else printf("remote"); if (network == 0 #ifdef IKSD && !inserver #endif/* IKSD */ ) { #ifdef CK_TAPI if (tttapi && !tapipass ) printf(", modem: %s","TAPI"); else #endif /* CK_TAPI */ printf(", modem: %s",gmdmtyp()); } else { #ifdef NETCONN if (nettype == NET_TCPA) printf(", TCP/IP"); if (nettype == NET_TCPB) printf(", TCP/IP"); if (nettype == NET_DEC) { if (ttnproto == NP_LAT) printf(", DECnet LAT"); else if ( ttnproto == NP_CTERM ) printf(", DECnet CTERM"); else printf(", DECnet"); } if (nettype == NET_SLAT) printf(", Meridian Technologies' SuperLAT"); #ifdef NETFILE if (nettype == NET_FILE) printf(", local file"); #endif /* NETFILE */ #ifdef NETCMD if (nettype == NET_CMD) printf(", pipe"); #endif /* NETCMD */ #ifdef NETPTY if (nettype == NET_PTY) printf(", pseudoterminal"); #endif /* NETPTY */ #ifdef NETDLL if (nettype == NET_DLL) printf(", dynamic load library"); #endif /* NETDLL */ if (nettype == NET_PIPE) printf(", Named Pipes"); #ifdef SSHBUILTIN if (nettype == NET_SSH) printf(", Secure Shell protocol (SECURE)"); #endif /* SSHBUILTIN */ #ifdef ANYX25 if (shox25(0) < 0) return; #endif /* ANYX25 */ if (IS_TELNET()) { printf(", telnet protocol"); if (0 #ifdef CK_ENCRYPTION || ck_tn_encrypting() && ck_tn_decrypting() #endif /* CK_ENCRYPTION */ #ifdef CK_SSL || tls_active_flag || ssl_active_flag #endif /* CK_SSL */ ) printf(" (SECURE)"); } #ifdef RLOGCODE else if (ttnproto == NP_RLOGIN || ttnproto == NP_K4LOGIN || ttnproto == NP_K5LOGIN) printf(", rlogin protocol"); else if (ttnproto == NP_EK4LOGIN || ttnproto == NP_EK5LOGIN) printf(", rlogin protocol (SECURE)"); #endif /* RLOGCODE */ #ifdef CK_KERBEROS #ifdef KRB5 else if (ttnproto == NP_K5U2U) printf(", Kerberos 5 User to User protocol (SECURE)"); #endif /* KRB5 */ #endif /* CK_KERBEROS */ #endif /* NETCONN */ } printf("\n"); if (hwparity && local && !network) s = parnam((char)hwparity); else s = parnam((char)parity); printf(" Parity: %s%s",hwparity ? "hardware " : "", s); #ifndef NOLOCAL if (local && !network) { int sb; char c; c = s[0]; if (islower(c)) c = toupper(c); sb = stopbits; if (sb < 1) { sb = (speed > 0 && speed <= 110L) ? 2 : 1; printf(", stop-bits: (default)"); } else { printf(", stop-bits: %d",sb); } if (hwparity) printf(" (8%c%d)",c,sb); else if (parity) printf(" (7%c%d)",c,sb); else printf(" (8N%d)",sb); printf("\n D"); } else printf(", d"); #endif /* NOLOCAL */ printf("uplex: %s, ", duplex ? "half" : "full"); debug(F101,"shoparp flow","",flow); printf("flow: %s", floname[flow]); printf(", handshake: "); if (turn) printf("%d\n",turnch); else printf("none\n"); #ifdef COMMENT if (local && !network) { /* SET CARRIER-WATCH */ #endif /* COMMENT */ if (carrier == CAR_OFF) s = "off"; else if (carrier == CAR_ON) s = "on"; else if (carrier == CAR_AUT) s = "auto"; else s = "unknown"; printf(" Carrier-watch: %s", s); if (carrier == CAR_ON) { if (cdtimo) printf(", timeout: %d sec", cdtimo); else printf(", timeout: none"); } #ifdef COMMENT } #endif /* COMMENT */ printf(", close-on-disconnect: %s\n",showoff(clsondisc)); #ifdef UNIX /* UUCP lockfile, UNIX only */ if (local) { #ifndef NOUUCP if (!network && haslock && *flfnam) printf(" Lockfile: %s",flfnam); #ifndef USETTYLOCK if (!network && haslock && lock2[0]) printf("\n Secondary lockfile: %s",lock2); #endif /* USETTYLOCK */ #else #ifdef QNX { extern int qnxportlock, qnxopencount(); if (local) printf(" Qnx-port-lock: %s, Open count: %d", showoff(qnxportlock), qnxopencount() ); else printf(" Qnx-port-lock: %s", showoff(qnxportlock)); } #endif /* QNX */ #endif /* NOUUCP */ printf("\n"); } else { char * s; s = ttglckdir(); if (!s) s = ""; printf(" Lockfile directory: %s\n", *s ? s : "(none)"); } #endif /* UNIX */ #ifndef MACOSX if (!local) { printf(" Typical port device name: %s\n",ttgtpn()); } #endif /* MACOSX */ if (local) { int i; i = parity ? 7 : 8; if (i == 8) i = (cmask == 0177) ? 7 : 8; printf(" Terminal bytesize: %d,",i); printf(" escape character: %d (^%c)\n",escape,ctl(escape)); } } int shotcp(n) int n; { #ifdef TCPSOCKET if (nettype == NET_TCPA || nettype == NET_TCPB) { printf("SET TCP parameters:\n"); if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0; printf(" Reverse DNS lookup: %s\n", showooa(tcp_rdns)); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; #ifdef CK_DNS_SRV printf(" DNS Service Records lookup: %s\n", showooa(tcp_dns_srv)); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; #endif /* CK_DNS_SRV */ #ifndef NOTCPOPTS #ifdef SOL_SOCKET #ifdef SO_KEEPALIVE printf(" Keepalive: %s\n", showoff(tcp_keepalive)); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; #endif /* SO_KEEPALIVE */ #ifdef SO_LINGER printf(" Linger: %s", tcp_linger ? "on, " : "off\n" ); if (tcp_linger) { if (tcp_linger_tmo) printf("%d x 10 milliseconds\n",tcp_linger_tmo); else printf("no timeout\n"); } if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; #endif /* SO_LINGER */ #ifdef SO_DONTROUTE printf(" DontRoute: %s\n", tcp_dontroute ? "on" : "off" ); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; #endif /* SO_DONTROUTE */ #ifdef TCP_NODELAY printf(" Nodelay: %s\n", showoff(tcp_nodelay)); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; #endif /* TCP_NODELAY */ #ifdef SO_SNDBUF if (tcp_sendbuf <= 0) printf(" Send buffer: (default size)\n"); else printf(" Send buffer: %d bytes\n", tcp_sendbuf); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; #endif /* SO_SNDBUF */ #ifdef SO_RCVBUF if (tcp_recvbuf <= 0) printf(" Receive buffer: (default size)\n"); else printf(" Receive buffer: %d bytes\n", tcp_recvbuf); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; #endif /* SO_RCVBUF */ #endif /* SOL_SOCKET */ #endif /* NOTCPOPTS */ printf(" address: %s\n",tcp_address ? tcp_address : "(none)"); if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0; #ifndef NOHTTP printf(" http-proxy: %s\n",tcp_http_proxy ? tcp_http_proxy : "(none)"); if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0; #endif /* NOHTTP */ #ifdef NT #ifdef CK_SOCKS printf(" socks-server: %s\n",tcp_socks_svr ? tcp_socks_svr : "(none)"); if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0; #ifdef CK_SOCKS_NS printf(" socks-name-server: %s\n", tcp_socks_ns ? tcp_socks_ns : "(none)"); if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0; #endif /* CK_SOCKS_NS */ #endif /* CK_SOCKS */ #endif /* NT */ } #endif /* TCPSOCKET */ return(n); } #ifdef TNCODE int shotopt(n) int n; { int opt; printf("%-21s %12s %12s %12s %12s\n\n", "Telnet Option","Me (client)","U (client)", "Me (server)","U (server)"); n += 2; if (n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0; for ( opt = TELOPT_FIRST; opt <= TELOPT_LAST; opt++) { switch (opt) { case TELOPT_AUTHENTICATION: case TELOPT_ENCRYPTION: case TELOPT_TTYPE: case TELOPT_NAWS: case TELOPT_BINARY: case TELOPT_NEWENVIRON: case TELOPT_SNDLOC: case TELOPT_XDISPLOC: case TELOPT_SGA: case TELOPT_ECHO: case TELOPT_KERMIT: case TELOPT_START_TLS: case TELOPT_FORWARD_X: case TELOPT_COMPORT: break; default: continue; } printf("%03d %-17s ", opt, TELOPT(opt) ); printf("%12s %12s ", TELOPT_MODE(TELOPT_DEF_C_ME_MODE(opt)), TELOPT_MODE(TELOPT_DEF_C_U_MODE(opt)) ); printf("%12s %12s\n", TELOPT_MODE(TELOPT_DEF_S_ME_MODE(opt)), TELOPT_MODE(TELOPT_DEF_S_U_MODE(opt)) ); if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0; if (sstelnet) printf("%21s %12s %12s %12s %12s\n", "", "", "", (TELOPT_ME(opt)?"WILL":"WONT"), (TELOPT_U(opt)?"DO":"DONT") ); else printf("%21s %12s %12s %12s %12s\n", "", (TELOPT_ME(opt)?"WILL":"WONT"), (TELOPT_U(opt)?"DO":"DONT"), "", "" ); if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0; } return(n); } int shotel(n) int n; { extern int tn_duplex; #ifdef CK_ENVIRONMENT extern int tn_env_flg; extern char tn_env_acct[]; extern char tn_env_job[]; extern char tn_env_prnt[]; extern char tn_env_sys[]; extern char * tn_env_uservar[8][2]; int x; #endif /* CK_ENVIRONMENT */ #ifdef CK_SNDLOC extern char * tn_loc; #endif /* CK_SNDLOC */ printf("SET TELNET parameters:\n echo: %s\n NVT newline-mode: ", tn_duplex ? "local" : "remote"); switch (tn_nlm) { case TNL_CRNUL: printf("%s\n","off (cr-nul)"); break; case TNL_CRLF: printf("%s\n","on (cr-lf)"); break; case TNL_CR: printf("%s\n","raw (cr)"); break; case TNL_LF: printf("%s\n","(lf)"); break; } if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0; #ifdef CK_AUTHENTICATION { int type = ck_tn_authenticated(); printf(" authentication: "); switch (sstelnet ? TELOPT_U_MODE(TELOPT_AUTHENTICATION) : TELOPT_ME_MODE(TELOPT_AUTHENTICATION) ) { case TN_NG_AC: printf( "accepted " ); break; case TN_NG_RF: printf( "refused " ); break; case TN_NG_RQ: printf( "requested"); break; case TN_NG_MU: printf( "required "); break; } #ifdef CK_SSL if ((ssl_active_flag || tls_active_flag) && ck_tn_auth_valid() == AUTH_VALID && (!TELOPT_U(TELOPT_AUTHENTICATION) || type == AUTHTYPE_NULL || type == AUTHTYPE_AUTO)) printf(" in use: X.509 certificate\n"); else #endif /* CK_SSL */ printf(" in use: %s\n",AUTHTYPE_NAME(type)); if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0; if (forward_flag) printf(" credentials forwarding requested %s\n", forwarded_tickets ? "and completed" : "but not completed"); else printf(" credentials forwarding disabled\n"); if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0; } #endif /* CK_AUTHENTICATION */ #ifdef CK_ENCRYPTION { int i,x; int e_type = ck_tn_encrypting(); int d_type = ck_tn_decrypting(); char * e_str = NULL, * d_str = NULL; static struct keytab * tnetbl = NULL; static int ntnetbl = 0; x = ck_get_crypt_table(&tnetbl,&ntnetbl); for (i = 0; i < ntnetbl; i++) { if (e_type == tnetbl[i].kwval) e_str = tnetbl[i].kwd; if (d_type == tnetbl[i].kwval) d_str = tnetbl[i].kwd; } printf(" encryption: "); switch (TELOPT_ME_MODE(TELOPT_ENCRYPTION)) { /* This should be changed to report both ME and U modes */ case TN_NG_AC: printf( "accepted " ); break; case TN_NG_RF: printf( "refused " ); break; case TN_NG_RQ: printf( "requested"); break; case TN_NG_MU: printf( "required "); break; } printf(" in use: "); switch ((e_type ? 1 : 0) | (d_type ? 2 : 0)) { case 0: printf("plain text in both directions"); break; case 1: printf("%s output, plain text input",e_str); break; case 2: printf("plain text output, %s input",d_str); break; case 3: printf("%s output, %s input",e_str,d_str); break; } printf("\n"); if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0; } #endif /* CK_ENCRYPTION */ #ifdef IKS_OPTION printf(" kermit: "); switch (TELOPT_U_MODE(TELOPT_KERMIT)) { case TN_NG_AC: printf( "u, accepted; " ); break; case TN_NG_RF: printf( "u, refused; " ); break; case TN_NG_RQ: printf( "u, requested; "); break; case TN_NG_MU: printf( "u, required; "); break; } switch (TELOPT_ME_MODE(TELOPT_KERMIT)) { case TN_NG_AC: printf( "me, accepted; " ); break; case TN_NG_RF: printf( "me, refused; " ); break; case TN_NG_RQ: printf( "me, requested; "); break; case TN_NG_MU: printf( "me, required; "); break; } if (TELOPT_U(TELOPT_KERMIT)) printf(" u, %s", TELOPT_SB(TELOPT_KERMIT).kermit.u_start ? "started" : "stopped" ); else printf(" u, n/a"); if (TELOPT_ME(TELOPT_KERMIT)) printf(" me, %s;", TELOPT_SB(TELOPT_KERMIT).kermit.me_start ? "started" : "stopped" ); else printf(" me, n/a;"); printf("\n"); if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0; #endif /* IKS_OPTION */ printf(" BINARY newline-mode: "); switch (tn_b_nlm) { case TNL_CRNUL: printf("%s\n","off (cr-nul)"); break; case TNL_CRLF: printf("%s\n","on (cr-lf)"); break; case TNL_CR: printf("%s\n","raw (cr)"); break; case TNL_LF: printf("%s\n","(lf)"); break; } if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0; printf(" binary-mode: "); switch (TELOPT_U_MODE(TELOPT_BINARY)) { case TN_NG_AC: printf( "u, accepted; " ); break; case TN_NG_RF: printf( "u, refused; " ); break; case TN_NG_RQ: printf( "u, requested; "); break; case TN_NG_MU: printf( "u, required; "); break; } switch (TELOPT_ME_MODE(TELOPT_BINARY)) { case TN_NG_AC: printf( "me, accepted; " ); break ; case TN_NG_RF: printf( "me, refused; " ); break; case TN_NG_RQ: printf( "me, requested; "); break; case TN_NG_MU: printf( "me, required; "); break; } printf("u, %s; me, %s\n", TELOPT_U(TELOPT_BINARY) ? "BINARY" : "NVT", TELOPT_ME(TELOPT_BINARY) ? "BINARY" : "NVT" ); if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0; printf(" binary-transfer-mode: %s\n",showoff(tn_b_xfer)); if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0; printf(" bug binary-me-means-u-too: %s\n",showoff(tn_b_meu)); if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0; printf(" bug binary-u-means-me-too: %s\n",showoff(tn_b_ume)); if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0; printf(" bug sb-implies-will-do: %s\n",showoff(tn_sb_bug)); if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0; printf(" bug auth-krb5-des: %s\n",showoff(tn_auth_krb5_des_bug)); if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0; printf(" terminal-type: "); if (tn_term) { printf("%s\n",tn_term); } else { char *p; #ifdef OS2 p = (tt_type >= 0 && tt_type <= max_tt) ? tt_info[tt_type].x_name : "UNKNOWN"; #else p = getenv("TERM"); #endif /* OS2 */ if (p) printf("none (%s will be used)\n",p); else printf("none\n"); } if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0; #ifdef CK_ENVIRONMENT printf(" environment: %s\n", showoff(tn_env_flg)); if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0; printf(" ACCOUNT: %s\n",tn_env_acct); if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0; printf(" DISPLAY: %s\n",(char *)tn_get_display() ? (char *)tn_get_display() : ""); if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0; printf(" JOB : %s\n",tn_env_job); if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0; printf(" PRINTER: %s\n",tn_env_prnt); if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0; #ifndef NOSPL printf(" USER : %s\n",uidbuf); if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0; #endif /* NOSPL */ printf(" SYSTEM : %s\n",tn_env_sys); if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0; for (x = 0; x < 8; x++) { if (tn_env_uservar[x][0] && tn_env_uservar[x][1]) { printf(" %-7s: %s\n",tn_env_uservar[x][0], tn_env_uservar[x][1]); if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0; } } #endif /* CK_ENVIRONMENT */ #ifdef CK_SNDLOC printf(" LOCATION: %s\n", tn_loc ? tn_loc : ""); if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0; #endif /* CK_SNDLOC */ #ifdef CK_FORWARD_X printf(" .Xauthority-file: %s\n", (char *)XauFileName() ? (char *)XauFileName() : "(none)"); if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0; #endif /* CK_FORWARD_X */ return(n); } #endif /* TNCODE */ #ifdef CK_NETBIOS static int shonb(n) int n; { printf("NETBIOS parameters:\n"); if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0; printf(" API : %s\n", NetbeuiAPI ? "NETAPI.DLL - IBM Extended Services or Novell Netware Requester" : "ACSNETB.DLL - IBM Network Transport Services/2" ) ; if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0; printf(" Local Name: [%s]\n", NetBiosName); if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0; printf(" Adapter : %d\n", NetBiosAdapter); if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0; if (NetBiosLSN > 0xFF) { printf(" Session : %d\n", NetBiosLSN); } else { printf(" Session : none active\n"); } if (++n > cmd_rows - 3) if (!askmore()) return(-1); else n = 0; return(n); } #endif /* CK_NETBIOS */ #ifndef NONET int shonet() { #ifndef NETCONN printf("\nNo networks are supported in this version of C-Kermit\n"); #else #ifdef NOLOCAL printf("\nNo networks are supported in this version of C-Kermit\n"); #else /* rest of this routine */ int i, n = 4; #ifndef NODIAL if (nnetdir <= 1) { printf("\nNetwork directory: %s\n",netdir[0] ? netdir[0] : "(none)"); n++; } else { int i; printf("\nNetwork directories:\n"); for (i = 0; i < nnetdir; i++) { printf("%2d. %s\n",i,netdir[i]); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; } } #endif /* NODIAL */ #ifdef SSHCMD { extern char * sshcmd; printf("SSH COMMAND: %s\n",sshcmd ? sshcmd : "ssh -e none"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; } #endif /* SSHCMD */ #ifdef OS2 printf("\nNetwork availability:\n"); #else printf("\nSupported networks:\n"); #endif /* OS2 */ if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; #ifdef VMS #ifdef TCPWARE printf(" Process Software Corporation TCPware for OpenVMS"); #else #ifdef MULTINET printf(" TGV MultiNet TCP/IP"); #else #ifdef WINTCP printf(" WOLLONGONG WIN/TCP"); #else #ifdef DEC_TCPIP { static $DESCRIPTOR(tcp_desc,"_TCP0:"); int status; long devclass; static int itmcod = DVI$_DEVCLASS; #ifdef COMMENT status = LIB$GETDVI(&itmcod, 0, &tcp_desc, &devclass); #else /* Martin Zinser 9/96 */ status = lib$getdvi(&itmcod, 0, &tcp_desc, &devclass); #endif /* COMMENT */ if ((status & 1) && (devclass == DC$_SCOM)) printf(" Process Software Corporation TCPware for OpenVMS"); else #ifdef UCX50 printf(" DEC TCP/IP Services for (Open)VMS 5.0"); #else printf(" DEC TCP/IP Services for (Open)VMS"); #endif /* UCX50 */ } #else #ifdef CMU_TCPIP printf(" CMU-OpenVMS/IP"); #else printf(" None"); #endif /* CMU_TCPIP */ #endif /* DEC_TCPIP */ #endif /* WINTCP */ #endif /* MULTINET */ #endif /* TCPWARE */ if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; #ifdef TNCODE printf(", TELNET protocol\n\n"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; n = shotel(n); if (n < 0) return(0); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; #endif /* TNCODE */ printf("\n"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf("\n"); n = shotcp(++n); if (n < 0) return(0); #else /* Not VMS */ #ifdef SUNX25 printf(" SunLink X.25\n"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; #endif /* SUNX25 */ #ifdef STRATUSX25 printf(" Stratus VOS X.25\n"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; #endif /* STRATUSX25 */ #ifdef IBMX25 printf(" IBM AIX X.25\n"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; #endif /* IBMX25 */ #ifdef HPX25 printf(" HP-UX X.25\n"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; #endif /* HPX25 */ #ifdef SSHBUILTIN if (ck_ssleay_is_installed()) printf(" SSH V1 and V2 protocols\n"); else printf(" SSH V1 and V2 protocols - not available\n"); #endif /* SSHBUILTIN */ #ifdef DECNET #ifdef OS2 #ifdef NT if (dnet_avail) printf(" DECnet, LAT and CTERM protocols\n"); else printf(" DECnet, LAT and CTERM protocols - not available\n"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; #else /* NT */ if (dnet_avail) printf(" DECnet, LAT protocol\n"); else printf(" DECnet, LAT protocol - not available\n"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; #endif /* NT */ #else printf(" DECnet\n"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; #endif /* OS2 */ #endif /* DECNET */ #ifdef NPIPE printf(" Named Pipes\n"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; #endif /* NPIPE */ #ifdef CK_NETBIOS if (netbiosAvail) printf(" NETBIOS\n"); else printf(" NETBIOS - not available\n"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; #endif /* CK_NETBIOS */ #ifdef SUPERLAT if (slat_avail) printf(" SuperLAT\n"); else printf(" SuperLAT - not available\n") ; if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; #endif /* SUPERLAT */ #ifdef TCPSOCKET if ( #ifdef OS2 tcp_avail #else 1 #endif /* OS2 */ ) { char ipaddr[16]; if (getlocalipaddrs(ipaddr,16,0) < 0) { #ifdef OS2ONLY printf(" TCP/IP via %s\n", tcpname); #else printf(" TCP/IP\n"); #endif /* OS2ONLY */ if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; } else { int i = 1; #ifdef OS2ONLY printf(" TCP/IP [%16s] via %s\n", ipaddr, tcpname); #else printf(" TCP/IP [%16s]\n",ipaddr); #endif /* OS2ONLY */ if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; while (getlocalipaddrs(ipaddr,16,i++) >= 0) { printf(" [%16s]\n",ipaddr); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; } } if (nettype == NET_TCPB) { printf("\n"); n = shotcp(++n); if (n < 0) return(0); #ifdef TNCODE printf("\n"); n = shotel(++n); if (n < 0) return(0); #endif /* TNCODE */ if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; } #ifdef OS2 } else { printf(" TCP/IP - not available%s\n",tcpname[0] ? tcpname : "" ); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; #endif /* OS2 */ } #endif /* TCPSOCKET */ #ifdef CK_NETBIOS if (netbiosAvail && nettype == NET_BIOS) { printf("\n") ; if ((n = shonb(++n)) < 0) return(0); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; } #endif /* CK_NETBIOS */ #endif /* VMS */ printf("\nActive network connection:\n"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; if (network) { printf(" Host: %s",ttname); if ((nettype == NET_TCPA || nettype == NET_TCPB) && *ipaddr) printf(" [%s]",ipaddr); } else printf(" Host: none"); printf(", via: "); if (nettype == NET_TCPA || nettype == NET_TCPB) printf("tcp/ip\n"); else if (nettype == NET_SX25) printf("SunLink X.25\n"); else if (nettype == NET_VX25) printf("Stratus VOS X.25\n"); else if (nettype == NET_IX25) printf("IBM AIX X.25\n"); else if (nettype == NET_HX25) printf("HP-UX X.25\n"); else if (nettype == NET_DEC) { if ( ttnproto == NP_LAT ) printf("DECnet LAT\n"); else if ( ttnproto == NP_CTERM ) printf("DECnet CTERM\n"); else printf("DECnet\n"); } else if (nettype == NET_PIPE) printf("Named Pipes\n"); else if (nettype == NET_BIOS) printf("NetBIOS\n"); else if (nettype == NET_SLAT) printf("SuperLAT\n"); #ifdef NETFILE else if ( nettype == NET_FILE ) printf("local file\n"); #endif /* NETFILE */ #ifdef NETCMD else if ( nettype == NET_CMD ) printf("pipe\n"); #endif /* NETCMD */ #ifdef NETPTY else if ( nettype == NET_PTY ) printf("pseudoterminal\n"); #endif /* NETPTY */ #ifdef NETDLL else if ( nettype == NET_DLL ) printf("dynamic link library\n"); #endif /* NETDLL */ if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; #ifdef ANYX25 if ((nettype == NET_SX25) || (nettype == NET_VX25) || (nettype == NET_IX25)) if ((n = shox25(n)) < 0) return(0); #endif /* ANYX25 */ #ifdef SSHBUILTIN if (nettype == NET_SSH) { printf("Secure Shell protocol\n"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; } #endif /* SSHBUILTIN */ if (nettype == NET_TCPA || nettype == NET_TCPB) { #ifdef RLOGCODE if (ttnproto == NP_RLOGIN) { printf(" LOGIN (rlogin) protocol\n"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; } #ifdef CK_KERBEROS else if (ttnproto == NP_K4LOGIN) { printf(" Kerberos 4 LOGIN (klogin) protocol\n"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; } else if (ttnproto == NP_EK4LOGIN) { printf(" Encrypted Kerberos 4 LOGIN (eklogin) protocol\n"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; } else if (ttnproto == NP_K5LOGIN) { printf(" Kerberos 5 LOGIN (klogin) protocol\n"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; } else if (ttnproto == NP_EK5LOGIN) { printf(" Encrypted Kerberos 5 LOGIN (eklogin) protocol\n"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; } #endif /* CK_KERBEROS */ #endif /* RLOGCODE */ #ifdef CK_KERBEROS if (ttnproto == NP_K5U2U) { printf(" Kerberos 5 User to User protocol\n"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; } #endif /* CK_KERBEROS */ #ifdef TNCODE if (IS_TELNET()) { printf(" TELNET protocol\n"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" Echoing is currently %s\n",duplex ? "local" : "remote"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; } #endif /* TNCODE */ if (ttnproto == NP_TCPRAW) { printf(" Raw TCP socket\n"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; } } printf("\n"); #endif /* NOLOCAL */ #endif /* NETCONN */ return(0); } #endif /* NONET */ #ifndef NODIAL VOID shodial() { if (mdmtyp >= 0 || local != 0) doshodial(); } VOID shods(s) char *s; { /* Show a dial-related string */ char c; if (s == NULL || !(*s)) { /* Empty? */ printf("(none)\n"); } else { /* Not empty. */ while ((c = *s++)) /* Can contain controls */ if (c == '\\') /* a backslash */ printf("\\\\"); else if (c > 31 && c < 127) { putchar(c); } else printf("\\{%d}",c); printf("\n"); } } int doshodial() { int i, n = 2; printf(" Dial status: %d", dialsta); #ifdef BIGBUFOK if (dialsta > 90) printf(" = Unknown error"); else if (dialsta < 0) printf(" = (none)"); else if (dialsta < 35 && dialmsg[dialsta]) printf(" = %s", dialmsg[dialsta]); #endif /* BIGBUFOK */ n++; if (ndialdir <= 1) { printf("\n Dial directory: %s\n",dialdir[0] ? dialdir[0] : "(none)"); } else { int i; printf("\n Dial directories:\n"); for (i = 0; i < ndialdir; i++) printf("%2d. %s\n",i+1,dialdir[i]); n += ndialdir; } printf(" Dial method: "); if (dialmauto) printf("auto "); else if (dialmth == XYDM_D) printf("default"); else if (dialmth == XYDM_P) printf("pulse "); else if (dialmth == XYDM_T) printf("tone "); printf(" Dial sort: %s\n",dialsrt ? "on" : "off"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" Dial hangup: %s Dial display: %s\n", dialhng ? "on " : "off", dialdpy ? "on" : "off"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; if (dialrtr > 0) { printf(" Dial retries: %-12d Dial interval: %d\n", dialrtr, dialint); } else { printf(" Dial retries: (auto) Dial interval: %d\n", dialint); } if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" Dial timeout: "); #ifdef CK_TAPI if (tttapi && !tapipass) printf("(tapi)"); else #endif /* CK_TAPI */ if (dialtmo > 0) printf("%4d sec", dialtmo); else printf("0 (auto)"); printf(" Redial number: %s\n",dialnum ? dialnum : "(none)"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" Dial confirmation: %s Dial convert-directory: %s\n", dialcnf ? "on " : "off", dialcvt ? ((dialcvt == 1) ? "on" : "ask") : "off"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" Dial ignore-dialtone: %s", dialidt ? "on " : "off"); printf(" Dial pacing: %d\n",dialpace); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf( " Dial prefix: %s\n", dialnpr ? dialnpr : "(none)"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf( " Dial suffix: %s\n", dialsfx ? dialsfx : "(none)"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf( " Dial country-code: %-12s", diallcc ? diallcc : "(none)"); printf("Dial connect: %s", dialcon ? ((dialcon == 1) ? "on" : "auto") : "off"); if (dialcon != CAR_OFF) printf(" %s", dialcq ? "quiet" : "verbose"); printf( "\n Dial area-code: %-12s", diallac ? diallac : "(none)"); n++; if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf("Dial restrict: "); if (dialrstr == 5) printf("international\n"); else if (dialrstr == 4) printf("long-distance\n"); else if (dialrstr == 2) printf("local\n"); else if (dialrstr == 6) printf("none\n"); else printf("?\n"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" Dial lc-area-codes: "); if (nlocalac == 0) printf("(none)"); else for (i = 0; i < nlocalac; i++) printf("%s ", diallcac[i]); printf( "\n Dial lc-prefix: %s\n", diallcp ? diallcp : "(none)"); n++; if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf( " Dial lc-suffix: %s\n", diallcs ? diallcs : "(none)"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf( " Dial ld-prefix: %s\n", dialldp ? dialldp : "(none)"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf( " Dial ld-suffix: %s\n", diallds ? diallds : "(none)"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf( " Dial force-long-distance %s\n", showoff(dialfld)); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf( " Dial intl-prefix: %s\n", dialixp ? dialixp : "(none)"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf( " Dial intl-suffix: %s\n", dialixs ? dialixs : "(none)"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf( " Dial toll-free-area-code: "); if (ntollfree == 0) printf("(none)"); else for (i = 0; i < ntollfree; i++) printf("%s ", dialtfc[i]); printf("\n"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf( " Dial pulse-countries: "); if (ndialpucc == 0) printf("(none)"); else for (i = 0; i < ndialpucc; i++) printf("%s ", dialpucc[i]); printf("\n"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf( " Dial tone-countries: "); if (ndialtocc == 0) printf("(none)"); else for (i = 0; i < ndialtocc; i++) printf("%s ", dialtocc[i]); printf("\n"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf( " Dial toll-free-prefix: %s\n", dialtfp ? dialtfp : (dialldp ? dialldp : "(none)") ); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" Dial pbx-exchange: "); if (ndialpxx == 0) printf("(none)"); else for (i = 0; i < ndialpxx; i++) printf("%s ", dialpxx[i]); printf("\n"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf( " Dial pbx-inside-prefix: %s\n", dialpxi ? dialpxi : "(none)"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf( " Dial pbx-outside-prefix: %s\n", dialpxo ? dialpxo : "(none)"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf( " Dial macro: %s\n", dialmac ? dialmac : "(none)"); return(0); } #endif /* NODIAL */ #endif /* NOLOCAL */ /* Show File Parameters */ static char * pathval(x) int x; { switch (x) { case PATH_OFF: return("off"); case PATH_ABS: return("absolute"); case PATH_REL: return("relative"); case PATH_AUTO: return("auto"); default: return("unknown"); } } VOID shofil() { char *s; int i = 0, n = 1; extern char * ifdnam[]; extern int wildena; #ifdef UNIX extern int wildxpand; #endif /* UNIX */ extern char * snd_move, * snd_rename, * rcv_move, * rcv_rename; #ifdef PATTERNS extern int patterns; #endif /* PATTERNS */ extern char * rfspec, * sfspec; #ifdef UNIX extern int zobufsize, zofbuffer, zofblock; #endif /* UNIX */ #ifdef CK_CTRLZ extern int eofmethod; #endif /* CK_CTRLZ */ printf("\n"); #ifdef VMS printf(" File record-Length: %5d\n",frecl); n++; #endif /* VMS */ #ifndef NOXFER printf(" Transfer mode: %s\n", xfermode == XMODE_A ? "automatic" : "manual" ); n++; #ifdef PATTERNS printf(" File patterns: %s", showooa(patterns)); if (xfermode == XMODE_M && patterns) printf(" (but disabled by TRANSFER-MODE MANUAL)"); else if (patterns) printf(" (SHOW PATTERNS for list)"); printf("\n"); n++; #endif /* PATTERNS */ if (filepeek) printf(" File scan: on %d\n", nscanfile); else printf(" File scan: off\n"); if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0; if (xfermode == XMODE_A) printf(" Default file type: %s\n",shoxm()); else printf(" File type: %s\n",shoxm()); n++; if (fncnv == XYFN_L) s = "literal"; else if (fncnv == XYFN_C) s = "converted"; else s = "(unknown)"; printf(" File names: %s\n",s); n++; printf(" Send pathnames: %s\n", pathval(fnspath)); n++; printf(" Receive pathnames: %s\n", pathval(fnrpath)); n++; #ifdef UNIXOROSK printf(" Match dot files: %s\n", matchdot ? "yes" : "no"); n++; #ifdef UNIX printf(" Wildcard-expansion: %s (%s)\n", showoff(wildena), wildxpand ? "shell" : "kermit"); n++; #endif /* UNIX */ #endif /* UNIXOROSK */ printf(" File collision: "); for (i = 0; i < ncolx; i++) if (colxtab[i].kwval == fncact) break; printf("%s\n", (i == ncolx) ? "unknown" : colxtab[i].kwd); if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0; printf(" File destination: %s\n", (dest == DEST_D) ? "disk" : ((dest == DEST_S) ? "screen" : ((dest == DEST_N) ? "nowhere" : "printer")) ); if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0; s = (keep >= 0 && keep <= 2) ? ifdnam[keep] : "keep"; printf(" File incomplete: %s\n",s); if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0; printf(" File bytesize: %d\n",(fmask == 0177) ? 7 : 8); if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0; #ifndef NOCSETS printf(" File character-set: %s\n",fcsinfo[fcharset].keyword); if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0; printf(" File default 7-bit: %s\n",fcsinfo[dcset7].keyword); if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0; printf(" File default 8-bit: %s\n",fcsinfo[dcset8].keyword); if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0; #ifdef UNICODE printf(" File UCS bom: %s\n",showoff(ucsbom)); if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0; printf(" File UCS byte-order: %s-endian\n", ucsorder ? "little" : "big"); if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0; printf(" Computer byteorder: %s-endian\n", byteorder ? "little" : "big"); if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0; #endif /* UNICODE */ #endif /* NOCSETS */ printf(" File end-of-line: "); i = feol; switch (feol) { case XYFA_C: printf("%s\n","cr"); break; case XYFA_L: printf("%s\n","lf"); break; case XYFA_2: printf("%s\n","crlf"); break; default: printf("%d\n",i); } if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0; #endif /* NOXFER */ #ifdef CK_CTRLZ printf(" File eof: %s\n", eofmethod ? "ctrl-z" : "length"); if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0; #endif /* CK_CTRLZ */ #ifndef NOXFER #ifdef CK_TMPDIR printf(" File download-directory: %s\n", dldir ? dldir : "(none)"); if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0; #ifdef COMMENT i = 256; s = line; zzstring("\\v(tmpdir)",&s,&i); printf(" Temporary directory: %s\n", line); if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0; #endif /* COMMENT */ #endif /* CK_TMPDIR */ #ifdef VMS { extern int vmssversions, vmsrversions; printf(" Send version-numbers: %s\n",showoff(vmssversions)); if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0; printf(" Receive version-numbers: %s\n",showoff(vmsrversions)); if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0; } #endif /* VMS */ printf(" Send move-to: %s\n", snd_move ? snd_move : "(none)"); if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0; printf(" Send rename-to: %s\n", snd_rename ? snd_rename : "(none)"); if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0; printf(" Receive move-to: %s\n", rcv_move ? rcv_move : "(none)"); if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0; printf(" Receive rename-to: %s\n", rcv_rename ? rcv_rename : "(none)"); if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0; #endif /* NOXFER */ #ifdef KERMRC printf(" Initialization file: %s\n", noinit ? "(none)" : #ifdef CK_SYSINI CK_SYSINI #else kermrc #endif /* CK_SYSINI */ ); #endif /* KERMRC */ if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0; if (k_info_dir) { printf(" Kermit doc files: %s\n", k_info_dir); if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0; } #ifdef CKROOT s = zgetroot(); printf(" Root set: %s\n", s ? s : "(none)"); #endif /* CKROOT */ #ifdef UNIX printf(" Disk output buffer: %d (writes are %s, %s)\n", zobufsize, zofbuffer ? "buffered" : "unbuffered", zofblock ? "blocking" : "nonblocking" ); if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0; #ifdef DYNAMIC printf(" Stringspace: %d\n", zsetfil(0,2)); if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0; printf(" Listsize: %d\n", zsetfil(0,4)); if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0; #endif /* DYNAMIC */ #endif /* UNIX */ #ifdef OS2ORUNIX printf(" Longest filename: %d\n", maxnam); if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0; printf(" Longest pathname: %d\n", maxpath); if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0; #endif /* OS2ORUNIX */ printf(" Last file sent: %s\n", sfspec ? sfspec : "(none)"); if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0; printf(" Last file received: %s\n", rfspec ? rfspec : "(none)"); if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0; printf("\n Also see:\n"); n++; if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0; printf(" SHOW PROTOCOL, SHOW XFER"); #ifdef CK_LABELED printf(", SHOW LABELED"); #endif /* CK_LABELED */ #ifdef PATTERNS printf(", SHOW PATTERNS"); #endif /* PATTERNS */ #ifdef STREAMING printf(", SHOW STREAMING"); #endif /* STREAMING */ #ifndef NOCSETS printf(", SHOW CHARACTER-SETS"); #endif /* NOCSETS */ printf("\n\n"); } #ifndef NOXFER VOID shoparp() { /* Protocol */ extern int docrc, skipbup; char *s; #ifdef CK_TIMERS extern int rttflg; #endif /* CK_TIMERS */ printf("Protocol: %s\n",ptab[protocol].p_name); if (protocol == PROTO_K) { printf("\nProtocol Parameters: Send Receive"); if (timef) printf("\n Timeout (used=%2d):%7d*%8d ", timint, rtimo, pkttim); else printf("\n Timeout (used=%2d):%7d%9d ", timint, rtimo, pkttim); #ifdef XFRCAN printf(" Cancellation: %s",showoff(xfrcan)); if (xfrcan) printf(" %d %d", xfrchr, xfrnum); #endif /* XFRCAN */ printf("\n Padding: %11d%9d", npad, mypadn); if (bctr == 4) printf(" Block Check: blank-free-2\n"); else printf(" Block Check: %6d\n",bctr); printf( " Pad Character:%11d%9d", padch, mypadc); printf(" Delay: %6d\n",ckdelay); printf( " Pause: %11d%9d", pktpaus, pktpaus); printf(" Attributes: %s\n",showoff(atcapr)); printf( " Packet Start: %11d%9d", mystch, stchr); printf(" Max Retries: %6d%s\n", maxtry, (maxtry == 0) ? " (unlimited)" : "" ); printf( " Packet End: %11d%9d", seol, eol); if (ebqflg) printf(" 8th-Bit Prefix: '%c'",ebq); else printf(" 8th-Bit Prefix: ('%c' but not used)",ebq); printf( "\n Packet Length:%11d ", spmax); printf("%8d ", urpsiz); if (rptflg) printf(" Repeat Prefix: '%c'",rptq); else printf(" Repeat Prefix: ('%c' but not used)",rptq); printf( "\n Maximum Length: %9d%9d", maxsps, maxrps); printf(" Window Size:%7d set, %d used\n",wslotr,wmax); printf( " Buffer Size: %11d%9d", bigsbsiz, bigrbsiz); printf(" Locking-Shift: "); if (lscapu == 2) { printf("forced"); } else { printf("%s", (lscapr ? "enabled" : "disabled")); if (lscapr) printf(",%s%s", (lscapu ? " " : " not "), "used"); } printf("\n\n"); if (!(s = ptab[protocol].h_b_init)) s = ""; printf(" Auto-upload command (binary): "); if (*s) { shostrdef((CHAR *)s); printf("\n"); } else { printf("(none)\n"); } if (!(s = ptab[protocol].h_t_init)) s = ""; printf(" Auto-upload command (text): "); if (*s) { shostrdef((CHAR *)s); printf("\n"); } else { printf("(none)\n"); } if (!(s = ptab[protocol].h_x_init)) s = ""; printf(" Auto-server command: "); if (*s) { shostrdef((CHAR *)s); printf("\n"); } else { printf("(none)\n"); } tmpbuf[0] = NUL; #ifdef CK_TIMERS if (rttflg) { extern int mintime, maxtime; sprintf(tmpbuf," Packet timeouts: dynamic %d:%d", /* SAFE */ mintime, maxtime); } else { sprintf(tmpbuf," Packet timeouts: fixed"); /* SAFE */ } #endif /* CK_TIMERS */ if (tmpbuf[0]) printf("%-31s",tmpbuf); printf("Send backup: %s\n",showoff(!skipbup)); printf(" Transfer mode: %s", xfermode == XMODE_A ? "automatic " : "manual " ); printf(" Transfer slow-start: %s, crc: %s\n", showoff(slostart), showoff(docrc) ); #ifdef PIPESEND { extern int usepipes; printf(" Transfer pipes: %s ",usepipes ? "on " : "off"); } #endif /* PIPESEND */ #ifndef NOCSETS printf(" Transfer character-set: "); if (tcharset == TC_TRANSP) printf("transparent\n"); else printf("%s\n", tcsinfo[tcharset].keyword ); #endif /* NOCSETS */ #ifdef PIPESEND { extern char * sndfilter, * rcvfilter; printf(" Send filter: %s\n", sndfilter ? sndfilter : "(none)"); printf(" Receive filter: %s\n", rcvfilter ? rcvfilter : "(none)"); } #endif /* PIPESEND */ printf("\nAlso see:\n"); printf(" SHOW FILE, SHOW XFER"); #ifdef CK_LABELED printf(", SHOW LABELED"); #endif /* CK_LABELED */ #ifdef PATTERNS printf(", SHOW PATTERNS"); #endif /* PATTERNS */ #ifdef STREAMING printf(", SHOW STREAMING"); #endif /* STREAMING */ #ifndef NOCSETS printf(", SHOW CHARACTER-SETS"); #endif /* NOCSETS */ } #ifdef CK_XYZ #ifdef XYZ_INTERNAL if (protocol != PROTO_K) { int i; int x; printf(" File type: %s\n", binary ? "binary" : "text"); if (protocol == PROTO_Z) { /* Zmodem */ printf(" Window size: "); if (ptab[protocol].winsize < 1) printf("none\n"); else printf("%d\n",wslotr); #ifdef COMMENT printf(" Packet (frame) length: "); if (ptab[protocol].spktlen < 0) printf("none\n"); else printf("%d\n",spmax); #endif /* COMMENT */ } else { if (ptab[protocol].spktlen >= 1000) printf(" 1K packets\n"); else printf(" 128-byte packets\n"); } printf(" Pathname stripping when sending: %s\n", showoff(ptab[protocol].fnsp) ); printf(" Pathname stripping when receiving: %s\n", showoff(ptab[protocol].fnrp) ); printf(" Filename collision action: "); for (i = 0; i < ncolx; i++) if (colxtab[i].kwval == fncact) break; printf("%-12s", (i == ncolx) ? "unknown" : colxtab[i].kwd); printf("\n Escape control characters: "); x = ptab[protocol].prefix; if (x == PX_ALL) printf("all\n"); else if (x == PX_CAU || x==PX_WIL) printf("minimal\n"); else printf("none\n"); if (!(s = ptab[protocol].h_b_init)) s = ""; printf(" Autoreceive command (binary): %s\n", *s ? s : "(none)"); if (!(s = ptab[protocol].h_t_init)) s = ""; printf(" Autoreceive command (text): %s\n", *s ? s : "(none)"); } #else #ifndef NOPUSH if (protocol != PROTO_K) { _PROTOTYP( VOID shoextern, (void) ); printf("\nExecuted by external commands:\n\n"); s = ptab[protocol].p_b_scmd; if (!s) s = ""; printf(" SEND command (binary): %s\n", *s ? s : "(none)"); s = ptab[protocol].p_t_scmd; if (!s) s = ""; printf(" SEND command (text): %s\n", *s ? s : "(none)"); s = ptab[protocol].p_b_rcmd; if (!s) s = ""; printf(" RECEIVE command (binary): %s\n", *s ? s : "(none)"); s = ptab[protocol].p_t_rcmd; if (!s) s = ""; printf(" RECEIVE command (text): %s\n", *s ? s : "(none)"); s = ptab[protocol].h_b_init; if (!s) s = ""; printf(" Autoreceive command (binary): %s\n", *s ? s : "(none)"); s = ptab[protocol].h_t_init; if (!s) s = ""; printf(" Autoreceive command (text): %s\n", *s ? s : "(none)"); (VOID) shoextern(); } #endif /* NOPUSH */ #endif /* XYZ_INTERNAL */ #endif /* CK_XYZ */ } #endif /* NOXFER */ #ifndef NOCSETS /* Character-set items */ extern int s_cset, r_cset, axcset[], afcset[]; extern struct keytab xfrmtab[]; VOID shoparl() { #ifdef COMMENT int i; /* Misleading... */ printf("\nAvailable Languages:\n"); for (i = 0; i < MAXLANG; i++) { printf(" %s\n",langs[i].description); } #else printf("\nLanguage-specific translation rules: %s\n", language == L_USASCII ? "none" : langs[language].description); shocharset(); printf("\n\n"); #endif /* COMMENT */ } VOID shocharset() { int x; #ifdef COMMENT char * s = "Unknown"; extern int xlatype; #endif /* COMMENT */ #ifndef NOXFER extern int xfrxla; #endif /* NOXFER */ debug(F101,"SHOW FILE CHAR","",fcharset); printf("\n"); #ifndef NOXFER printf(" Transfer Translation: %s\n", showoff(xfrxla)); if (!xfrxla) { printf( " Because transfer translation is off, the following are ignored:\n\n"); } #endif /* NOXFER */ printf(" File Character-Set: %s (%s), ", fcsinfo[fcharset].keyword, fcsinfo[fcharset].name ); if ((x = fcsinfo[fcharset].size) == 128) printf("7-bit"); else if (x == 256) printf("8-bit"); else printf("multibyte"); printf("\n"); printf(" File Scan: %s\n",showoff(filepeek)); printf(" Default 7bit-Character-Set: %s\n",fcsinfo[dcset7].keyword); printf(" Default 8bit-Character-Set: %s\n",fcsinfo[dcset8].keyword); printf(" Transfer Character-Set"); #ifdef COMMENT if (tslevel == TS_L2) printf(": (international)"); else #endif /* COMMENT */ if (tcharset == TC_TRANSP) printf(": Transparent"); else printf(": %s (%s)",tcsinfo[tcharset].keyword, tcsinfo[tcharset].name); printf("\n"); #ifdef COMMENT switch (xlatype) { case XLA_NONE: s = "None"; break; case XLA_BYTE: s = "Byte"; break; case XLA_JAPAN: s = "Japanese"; break; case XLA_UNICODE: s = "Unicode"; break; } printf("\n Translation type: %s\n",s); #endif /* COMMENT */ printf(" SEND character-set-selection: %s\n",xfrmtab[s_cset].kwd); printf(" RECEIVE character-set-selection: %s\n",xfrmtab[r_cset].kwd); if (s_cset == XMODE_A || r_cset == XMODE_A) printf( " (Use SHOW ASSOCIATIONS to list automatic character-set selections.)\n" ); } VOID showassoc() { int i, k, n = 4; char * s; printf("\nFor incoming files:\n\n"); printf("Transfer Character-Set File Character-Set\n"); for (i = 1; i <= MAXTCSETS; i++) { k = axcset[i]; if (k < 0 || k > MAXFCSETS) s = "(none)"; else s = fcsinfo[k].keyword; if (!s) s = ""; if (!*s) s = "(none)"; printf(" %-25s%s\n",tcsinfo[i].keyword,s); if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0; } printf("\nFor outbound files:\n\n"); n += 2; if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0; printf("File Character-Set Transfer Character-Set\n"); if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0; for (i = 0; i <= MAXFCSETS; i++) { k = afcset[i]; if (k < 0 || k > MAXTCSETS) s = "(none)"; else s = tcsinfo[k].keyword; if (!s) s = ""; if (!*s) s = "(none)"; printf(" %-25s%s\n",fcsinfo[i].keyword,s); if (++n > cmd_rows - 3) if (!askmore()) return; else n = 0; } } #endif /* NOCSETS */ VOID shopar() { printf("Show what? (Type \"show ?\" for a list of possibilities.)\n"); } #endif /* NOSHOW */ #ifndef NOXFER /* D O S T A T -- Display file transfer statistics. */ int dostat(brief) int brief; { extern long filrej, peakcps; extern int lastspmax, streamed, cleared, streamok; extern char whoareu[]; int n = 0, ftp = 0; extern int docrc, interrupted, fatalio; ftp = lastxfer & W_FTP; #ifdef CK_TTGWSIZ #ifdef OS2 if (tt_cols[VTERM] < 0 || tt_rows[VTERM] < 0) ttgwsiz(); #else /* OS2 */ if (ttgwsiz() > 0) { if (tt_rows > 0 && tt_cols > 0) { cmd_rows = tt_rows; cmd_cols = tt_cols; } } #endif /* OS2 */ #endif /* CK_TTGWSIZ */ debug(F101,"dostat xferstat","",xferstat); if (xferstat < 0) { printf(" No file transfers yet.\n"); return(1); } n = 0; if (brief) { printf("\n"); n++; }; printf(" protocol : %s\n", ftp ? "ftp" : ptab[protocol].p_name); n++; printf(" status : "); if (xferstat) printf("SUCCESS\n"); else if (interrupted) printf("FAILURE (interrupted)\n"); else if (fatalio) printf("FAILURE (i/o error)\n"); else printf("FAILURE\n"); #ifndef XYZ_INTERNAL if (!ftp && protocol != PROTO_K) { printf("\n external protocol statistics not available\n"); return(1); } #endif /* XYZ_INTERNAL */ n++; if (!ftp) { if (!xferstat > 0) { if (docrc) printf(" crc-16 of file(s) : %ld\n", crc16); else printf(" crc-16 of file(s) : (disabled)\n"); n++; } if (!xferstat && *epktmsg) { printf(" reason : %s\n", epktmsg); n++; } } if (!brief) { #ifdef NEWFTP if (ftp) { extern char ftp_srvtyp[]; printf(" remote system type : %s\n",ftp_srvtyp); } else #endif /* NEWFTP */ if (whoareu[0]) { printf(" remote system type : %s\n", getsysid((char *)whoareu)); n++; } printf(" files transferred : %ld\n",filcnt - filrej); if (!ftp) printf(" files not transferred : %ld\n",filrej); printf(" characters last file : %s\n",ckfstoa(ffc)); printf(" total file characters : %s\n",ckfstoa(tfc)); n += ftp ? 3 : 4; if (!ftp) { printf(" communication line in : %s\n",ckfstoa(tlci)); printf(" communication line out : %s\n",ckfstoa(tlco)); printf(" packets sent : %d\n", spackets); printf(" packets received : %d\n", rpackets); n += 4; } } if (ftp) goto dotimes; printf(" damaged packets rec'd : %d\n", crunched); printf(" timeouts : %d\n", timeouts); printf(" retransmissions : %d\n", retrans); n += 3; if (!brief) { if (filcnt > 0) { printf(" parity : %s",parnam((char)parity)); n++; if (autopar) { printf(" (detected automatically)"); n++; } printf( "\n control characters : %ld prefixed, %ld unprefixed\n", ccp, ccu); n++; printf(" 8th bit prefixing : "); n++; if (ebqflg) printf("yes [%c]\n",ebq); else printf("no\n"); n++; printf(" locking shifts : %s\n", lscapu ? "yes" : "no"); n++; } } if (++n > cmd_rows - 3) { if (!askmore()) return(1); else n = 0; } if (streamed > 0) printf(" window slots used : (streaming)\n"); else printf(" window slots used : %d of %d\n", wmax, wslotr); if (++n > cmd_rows - 3) { if (!askmore()) return(1); else n = 0; } printf(" reliable: : %s%s\n", streamok ? "" : "not ", "negotiated"); if (++n > cmd_rows - 3) { if (!askmore()) return(1); else n = 0; } printf(" clearchannel: : %s%s\n", cleared ? "" : "not ", "negotiated"); if (++n > cmd_rows - 3) { if (!askmore()) return(1); else n = 0; } if (!brief) { printf(" packet length : %d (send), %d (receive)\n", lastspmax, urpsiz); if (++n > cmd_rows - 3) { if (!askmore()) return(1); else n = 0; } printf(" compression : "); if (rptflg) printf("yes [%c] (%ld)\n",(char) rptq,rptn); else printf("no\n"); if (++n > cmd_rows - 3) { if (!askmore()) return(1); else n = 0; } if (bctu == 4) printf(" block check type used : blank-free-2\n"); else printf(" block check type used : %d\n",bctu); if (++n > cmd_rows - 3) { if (!askmore()) return(1); else n = 0; } } dotimes: #ifdef GFTIMER #ifdef COMMENT printf(" elapsed time : %0.3f sec, %s\n", fptsecs,hhmmss(tsecs)); #endif /* COMMENT */ printf(" elapsed time : %s (%0.3f sec)\n", hhmmss((long)(fptsecs + 0.5)),fptsecs); #else #ifdef COMMENT printf(" elapsed time : %s (%d sec)\n",hhmmss(tsecs),tsecs); #endif /* COMMENT */ printf(" elapsed time : %d sec, %s\n",tsecs,hhmmss(tsecs)); #endif /* GFTIMER */ if (++n > cmd_rows - 3) { if (!askmore()) return(1); else n = 0; } if (!ftp && local && !network && !brief) { if (speed <= 0L) speed = ttgspd(); if (speed > 0L) { if (speed == 8880) printf(" transmission rate : 75/1200 bps\n"); else printf(" transmission rate : %ld bps\n",speed); if (++n > cmd_rows - 3) { if (!askmore()) return(1); else n = 0; } } } if (!ftp && local && !network && /* Only makes sense for */ mdmtyp == 0 && /* direct serial connections */ speed > 99L && /* when we really know the speed */ speed != 8880L ) { int eff; eff = (((tfcps * 100L) / (speed / 100L)) + 5L) / 10L; printf(" effective data rate : %ld cps (%d%%)\n",tfcps,eff); } else printf(" effective data rate : %ld cps\n", tfcps); if (!ftp && peakcps > 0L && peakcps > tfcps) printf(" peak data rate : %ld cps\n", peakcps); if (brief) printf("\nUse STATISTICS /VERBOSE for greater detail.\n\n"); return(1); } #endif /* NOXFER */ #ifndef NOSPL /* The INPUT command */ /* NOTE: An INPUT timeout of 0 means to perform a nonblocking read of the material that has already arrived and is waiting to be read, and perform matches against it, without doing any further reads. It should succeed or fail instantaneously. */ /* Output buffering for "doinput" */ #ifdef pdp11 #define MAXBURST 16 /* Maximum size of input burst */ #else #define MAXBURST 1024 #endif /* pdp11 */ #ifdef OSK static CHAR *conbuf; /* Buffer to hold output for console */ #else static CHAR conbuf[MAXBURST]; /* Buffer to hold output for console */ #endif /* OSK */ static int concnt = 0; /* Number of characters buffered */ #ifdef OSK static CHAR *sesbuf; /* Buffer to hold output for session log */ #else static CHAR sesbuf[MAXBURST]; /* Buffer to hold output for session log */ #endif /* OSK */ static int sescnt = 0; /* Number of characters buffered */ extern int debses; /* TERMINAL DEBUG ON/OFF */ static VOID /* Flush INPUT echoing */ myflsh() { /* and session log output. */ if (concnt > 0) { if (debses) { /* Terminal debugging? */ int i; for (i = 0; i < concnt; i++) conol(dbchr(conbuf[i])); } else conxo(concnt, (char *) conbuf); concnt = 0; } if (sescnt > 0) { logstr((char *) sesbuf, sescnt); sescnt = 0; } } /* Execute the INPUT and MINPUT commands */ int instatus = -1; long inetime = -1L; int inwait = 0; int nowrap = 0; /* For returning the input sequence that matched */ #ifdef BIGBUFOK #define MATCHBUFSIZ 8191 #else #define MATCHBUFSIZ 1023 #endif /* BIGBUFOK */ static char * matchbuf = NULL; static int matchindex = 0; static int burst = 0; /* Chars remaining in input burst */ /* timo = How long to wait: < 0 = Wait forever 0 = Don't wait at all - material must already have arrived > 0 = Wait this many seconds ms = Array of strings to wait for. mp = Array of flags. If mp[i] == 0, ms[i] is literal, else it's a pattern. flags = bit mask INPSW_NOM = /NOMATCH = 1 INPSW_CLR = /CLEAR = 2 INPSW_NOW = /NOWRAP = 4 INPSW_COU = /COUNT = 8 count = /COUNT: value if a /COUNT switch was given. Returns: 0 on failure, 1 on success. */ #ifndef ES_NORMAL #define ES_NORMAL 0 #endif /* ES_NORMAL */ extern int inesc[], oldesc[]; int doinput(timo,ms,mp,flags,count) int timo; char *ms[]; int mp[]; int flags; int count; { extern int inintr; #ifdef CK_AUTODL extern int inautodl; #endif /* CK_AUTODL */ int x, y, i, t, rt, icn, anychar = 0, mi[MINPMAX]; #ifdef GFTIMER CKFLOAT fpt = 0.0; #endif /* GFTIMER */ int savecount = 0; int nomatch = 0; int clearfirst = 0; int lastchar = 0; int waiting = 0; int imask = 0; char ch, *xp, *s; CHAR c; #ifndef NOLOCAL #ifdef OS2 extern int term_io; int term_io_save; #endif /* OS2 */ #endif /* NOLOCAL */ #ifdef TNCODE static int cr = 0; #endif /* TNCODE */ int is_tn = 0; #ifdef SSHBUILTIN extern int ssh_cas; extern char * ssh_cmd; #endif /* SSHBUILTIN */ int noescseq = 0; /* Filter escape sequences */ debug(F101,"input count","",count); debug(F101,"input flags","",flags); /* CK_BURST enables the INPUT speedup code, which depends on ttchk() returning accurate information. If INPUT fails with this code enabled, change the above "#define" to "#undef". */ #define CK_BURST /***** CHANGE THIS TO A SET INPUT PARAMETER *****/ noescseq = (sessft == XYFT_T); /* Filter escape sequences */ imask = cmask; if (parity) imask = 0x7f; inwait = timo; /* For \v(inwait) */ /* Options from command switches */ nowrap = flags & INPSW_NOW; /* 4 = /NOWRAP */ nomatch = flags & INPSW_NOM; /* 1 = /NOMATCH */ clearfirst = flags & INPSW_CLR; /* 2 = /CLEAR */ savecount = count; makestr(&inpmatch,NULL); if (!matchbuf) { matchbuf = malloc(MATCHBUFSIZ+1); matchbuf[0] = NUL; } matchindex = 0; /* If last time through we returned because of /NOWRAP and buffer full */ /* now we have to clear the buffer to make room for another load. */ if (nowrap && instatus == INP_BF) clearfirst = 1; if (clearfirst) { /* INPUT /CLEAR */ int i; myflsh(); /* Flush screen and log buffers */ for (i = 0; i < inbufsize; i++) inpbuf[i] = NUL; inpbp = inpbuf; } is_tn = #ifdef TNCODE (local && network && IS_TELNET()) || (!local && sstelnet) #else 0 #endif /* TNCODE */ ; #ifdef CK_SSL if (is_tn) if (ssl_raw_flag || tls_raw_flag) is_tn = 0; #endif /* CK_SSL */ instatus = INP_IE; /* 3 = internal error */ kbchar = 0; #ifdef OSK if (conbuf == NULL) { if ((conbuf = (CHAR *)malloc(MAXBURST*2)) == NULL) { return(0); } sesbuf = conbuf + MAXBURST; } #endif /* OSK */ #ifndef NOLOCAL if (local) { /* In local mode... */ if ((waiting = ttchk()) < 0) { /* check that connection is open */ if (!quiet) { if ((!network #ifdef TN_COMPORT || istncomport() #endif /* TN_COMPORT */ ) && carrier != CAR_OFF) printf("?Carrier detect failure on %s.\n", ttname); else printf("?Connection %s %s is not open.\n", network ? "to" : "on", ttname ); } instatus = INP_IO; return(0); } debug(F101,"doinput waiting","",waiting); y = ttvt(speed,flow); /* Put line in "ttvt" mode */ if (y < 0) { printf("?INPUT initialization error\n"); instatus = INP_IO; return(0); /* Watch out for failure. */ } } #endif /* NOLOCAL */ #ifdef SSHBUILTIN if ( network && nettype == NET_SSH && ssh_cas && ssh_cmd && !(strcmp(ssh_cmd,"kermit") && strcmp(ssh_cmd,"sftp"))) { if (!quiet) printf("?SSH Subsystem active: %s\n", ssh_cmd); instatus = INP_IKS; return(0); } #endif /* SSHBUILTIN */ debug(F111,"doinput ms[0]",ms[0],waiting); if (!ms[0] || isemptystring(ms[0])) { /* No search string was given nor */ if (count < 2) /* a /COUNT: switch so we just */ anychar = 1; /* wait for the first character */ } if (nomatch) anychar = 0; /* Don't match anything */ if (!anychar && waiting == 0 && timo == 0) return(0); #ifndef NODEBUG if (deblog) { char xbuf[24]; debug(F101,"doinput anychar","",anychar); debug(F101,"doinput timo","",timo); debug(F101,"doinput echo","",inecho); #ifdef CK_BURST debug(F101,"doinput burst","",burst); #endif /* CK_BURST */ y = -1; while (ms[++y]) { sprintf(xbuf,"doinput string %2d",y); /* SAFE (24) */ debug(F111,xbuf,ms[y],mp[y]); } } #endif /* NODEBUG */ #ifdef IKS_OPTION if (is_tn) { /* If the remote side is in a state of IKS START-SERVER */ /* we request that the state be changed. We will detect */ /* a failure to adhere to the request when we call ttinc() */ if (TELOPT_U(TELOPT_KERMIT) && TELOPT_SB(TELOPT_KERMIT).kermit.u_start) iks_wait(KERMIT_REQ_STOP,0); /* Send Request-Stop */ #ifdef CK_AUTODL /* If we are processing packets during INPUT and we have not */ /* sent a START message, do so now. */ if (inautodl && TELOPT_ME(TELOPT_KERMIT) && !TELOPT_SB(TELOPT_KERMIT).kermit.me_start) { tn_siks(KERMIT_START); /* Send Kermit-Server Start */ } #endif /* CK_AUTODL */ } #endif /* IKS_OPTION */ x = 0; /* Return code, assume failure */ instatus = INP_TO; /* Status, assume timeout */ for (y = 0; y < MINPMAX; y++) /* Initialize... */ mi[y] = 0; /* ..string pattern match position */ if (!inpcas[cmdlvl]) { /* INPUT CASE = IGNORE? */ y = -1; while ((xp = ms[++y])) { /* Convert each target to lowercase */ while (*xp) { if (isupper(*xp)) *xp = (char) tolower(*xp); xp++; } } } rtimer(); /* Reset timer. */ #ifdef GFTIMER rftimer(); /* Floating-point timer too. */ #endif /* GFTIMER */ inetime = -1L; /* Initialize elapsed time. */ t = 0; /* Time now is 0. */ m_found = 0; /* Default to timed-out */ incount = 0; /* Character counter */ rt = (timo == 0) ? 0 : 1; /* Character-read timeout interval */ #ifndef NOLOCAL #ifdef OS2 term_io_save = term_io; /* Disable I/O by emulator */ term_io = 0; #endif /* OS2 */ #endif /* NOLOCAL */ while (1) { /* Character-getting loop */ #ifdef CK_APC /* Check to see if there is an Autodown or other APC command */ if (apcactive == APC_LOCAL || (apcactive == APC_REMOTE && apcstatus != APC_OFF)) { if (mlook(mactab,"_apc_commands",nmac) == -1) { debug(F110,"doinput about to execute APC",apcbuf,0); domac("_apc_commands",apcbuf,cmdstk[cmdlvl].ccflgs|CF_APC); delmac("_apc_commands",1); apcactive = APC_INACTIVE; #ifdef DEBUG } else { debug(F100,"doinput APC in progress","",0); #endif /* DEBUG */ } } #endif /* CK_APC */ if (timo == 0 && waiting < 1) { /* Special exit criterion */ instatus = INP_TO; /* for timeout == 0 */ break; } if (local) { /* One case for local */ y = ttinc(rt); /* Get character from comm device */ debug(F101,"doinput ttinc(rt) returns","",y); if (y < -1) { /* Connection failed. */ instatus = INP_IO; /* Status = i/o error */ #ifndef NOLOCAL #ifdef OS2 term_io = term_io_save; #endif /* OS2 */ #endif /* NOLOCAL */ switch (y) { case -2: /* Connection lost */ if (local && !network && carrier != CAR_OFF) { dologend(); printf("Connection closed.\n"); ttclos(1); } break; case -3: dologend(); printf("Session Limit exceeded - closing connection.\n"); ttclos(1); default: break; } debug(F111,"doinput Connection failed","returning 0",y); return(0); } if (inintr) { debug(F111,"doinput","inintr",inintr); if ((icn = conchk()) > 0) { /* Interrupted from keyboard? */ debug(F101,"input interrupted from keyboard","",icn); kbchar = coninc(0); if (kbchar >= 0) { while (--icn > 0) { debug(F110,"doinput","absorbing",0); coninc(0); /* Yes, absorb what was typed. */ } instatus = INP_UI; /* Fail and remember why. */ break; } } } } else { /* Another for remote */ y = coninc(rt); debug(F101,"doinput coninc(rt) returns","",y); } if (y > -1) { /* A character arrived */ debug(F111,"doinput","a character arrived",y); if (timo == 0) waiting--; #ifndef OS2 #define TN_NOLO #endif /* OS2 */ #ifdef NOLOCAL #define TN_NOLO #endif /* NOLOCAL */ #ifdef TN_NOLO debug(F100,"doinput TN_NOLO","",0); #ifdef TNCODE /* Check for telnet protocol negotiation */ if (is_tn) { switch (y & 0xff) { case IAC: cr = 0; myflsh(); /* Break from input burst for tn_doop() */ #ifdef CK_BURST burst = 0; #endif /* CK_BURST */ waiting -= 2; /* (not necessarily...) */ switch (tn_doop((CHAR)(y & 0xff),duplex,ttinc)) { case 2: duplex = 0; continue; case 1: duplex = 1; continue; #ifdef IKS_OPTION case 4: if (TELOPT_SB(TELOPT_KERMIT).kermit.u_start && !tcp_incoming) { instatus = INP_IKS; printf( " Internet Kermit Service in SERVER mode.\n Please use REMOTE commands.\n" ); break; } continue; #endif /* IKS_OPTION */ case 6: /* TELNET DO LOGOUT received */ continue; case 7: case 3: /* A quoted IAC */ break; default: continue; } case CR: cr = 1; break; case NUL: if (!TELOPT_U(TELOPT_BINARY) && cr) { cr = 0; continue; } cr = 0; break; default: cr = 0; } /* I'm echoing remote chars */ if (TELOPT_ME(TELOPT_ECHO) && tn_rem_echo) ttoc((char)y); } #endif /* TNCODE */ #ifdef CK_AUTODL /* Check for file transfer packets */ if (inautodl) autodown(y); #endif /* CK_AUTODL */ #else /* TN_NOLO */ debug(F100,"doinput !TN_NOLO","",0); #ifdef TNCODE /* Check for telnet protocol negotiation */ if (is_tn) { int tx; switch (y & 0xff) { case IAC: myflsh(); /* Break from input burst for tn_doop() */ #ifdef CK_BURST burst = 0; #endif /* CK_BURST */ #ifdef IKS_OPTION tx = scriptwrtbuf((USHORT)y); if (tx == 4) { if (TELOPT_U(TELOPT_KERMIT) && TELOPT_SB(TELOPT_KERMIT).kermit.u_start && !tcp_incoming ) { instatus = INP_IKS; printf( " Internet Kermit Service in SERVER mode.\n Please use REMOTE commands.\n" ); break; } } else if (tx == 6) { /* TELNET DO LOGOUT received */ } #else /* IKS_OPTION */ /* Handles Telnet negotiations */ tx = scriptwrtbuf((USHORT)y); if (tx == 6) { /* TELNET DO LOGOUT received */ } #endif /* IKS_OPTION */ waiting -= 2; /* (not necessarily...) */ cr = 0; continue; /* and autodownload check */ case CR: cr = 1; tx = scriptwrtbuf((USHORT)y); if (tx == 6) { /* TELNET DO LOGOUT received */ } break; case NUL: cr = 0; if (!TELOPT_U(TELOPT_BINARY) && cr) continue; tx = scriptwrtbuf((USHORT)y); if (tx == 6) { /* TELNET DO LOGOUT received */ } break; default: cr = 0; tx = scriptwrtbuf((USHORT)y); if (tx == 6) { /* TELNET DO LOGOUT received */ } } /* I'm echoing remote chars */ if (TELOPT_ME(TELOPT_ECHO) && tn_rem_echo) ttoc((CHAR)y); } else #endif /* TNCODE */ /* Handles terminal emulation responses */ scriptwrtbuf((USHORT)y); #endif /* TN_NOLO */ /* Real input character to be checked */ #ifdef CK_BURST burst--; /* One less character waiting */ debug(F101,"doinput burst","",burst); #endif /* CK_BURST */ c = (CHAR) (imask & (CHAR) y); /* Mask off any parity */ inchar[0] = c; /* Remember character for \v(inchar) */ #ifdef COMMENT #ifdef CK_BURST /* Update "lastchar" time only once during input burst */ if (burst <= 0) #endif /* CK_BURST */ #endif /* COMMENT */ lastchar = gtimer(); /* Remember when it came */ if (c == '\0') { /* NUL, we can't use it */ if (anychar) { /* Except if any character will do? */ x = 1; /* Yes, done. */ instatus = INP_OK; incount = 1; /* This must be the first and only. */ break; } else goto refill; /* Otherwise continue INPUTting */ } *inpbp++ = c; /* Store char in circular buffer */ incount++; /* Count it for \v(incount) */ if (flags & INPSW_COU) { /* INPUT /COUNT */ if (--count < 1) { x = 1; instatus = INP_OK; incount = savecount; break; } } if (matchbuf) { if (matchindex < MATCHBUFSIZ) { matchbuf[matchindex++] = c; matchbuf[matchindex] = NUL; } } #ifdef MAC { extern char *ttermw; /* fake pointer cast */ if (inecho) { outchar(ttermw, c); /* echo to terminal window */ /* this might be too much overhead to do here ? */ updatecommand(ttermw); } } #else /* Not MAC */ if (inecho) { /* Buffer console output */ conbuf[concnt++] = c; } #endif /* MAC */ #ifndef OS2 if (seslog) { int dummy = 0, skip = 0; #ifndef NOLOCAL if (noescseq) { dummy = chkaes(c,0); if (inesc[0] != ES_NORMAL || oldesc[0] != ES_NORMAL) skip = 1; } #endif /* NOLOCAL */ #ifdef UNIXOROSK if (sessft == XYFT_T) { #ifdef UNIX if (c == '\r') #else #ifdef OSK if (c == '\012') #endif /* OSK */ #endif /* UNIX */ skip = 1; } #endif /* UNIXOROSK */ if (!skip) sesbuf[sescnt++] = c; /* Buffer session log output */ } #endif /* OS2 */ if (anychar) { /* Any character will do? */ x = 1; instatus = INP_OK; break; } if (!inpcas[cmdlvl]) { /* Ignore alphabetic case? */ if (isupper(c)) /* Yes, convert input char to lower */ c = (CHAR) tolower(c); } debug(F000,"doinput char","",c); /* Here is the matching section */ y = -1; /* Loop thru search strings */ while (!nomatch && (s = ms[++y])) { /* ...as many as we have. */ if (mp[y]) { /* Pattern match? */ #ifdef COMMENT int j; /* This is gross but it works... */ /* We could just as easily have prepended '*' to the */ /* pattern and skipped the loop, except then we would */ /* not have any way to identify the matching string. */ for (j = 0; j < matchindex; j++) { if (ckmatch(s,&matchbuf[j],1,1)) { matchindex = j; instatus = INP_OK; x = 1; break; } } if (x > 0) break; #else /* July 2001 - ckmatch() returns match position. */ /* It works and it's not gross. */ /* (4 = floating pattern) */ x = ckmatch(s,matchbuf,inpcas[cmdlvl],1+4); if (x > 0) { matchindex = x - 1; instatus = INP_OK; x = 1; break; } #endif /* COMMENT */ continue; } /* Literal match. */ i = mi[y]; /* Match-position in search string. */ debug(F000,"compare char","",(CHAR)s[i]); if (c == (CHAR) s[i]) { /* Check for match */ i++; /* Got one, go to next character */ } else { /* Don't have a match */ int j; for (j = i; i > 0; ) { /* Back up in search string */ i--; /* (Do this here to prevent compiler foulup) */ /* j is the length of the substring that matched */ if (c == (CHAR) s[i]) { if (!strncmp(s,&s[j-i],i)) { i++; /* c actually matches -- cfk */ break; } } } } if ((CHAR) s[i] == (CHAR) '\0') { /* Matched to end? */ ckstrncpy(matchbuf,ms[y],MATCHBUFSIZ); matchindex = 0; instatus = INP_OK; /* Yes, */ x = 1; break; /* done. */ } mi[y] = i; /* No, remember match-position */ } if (x == 1) { /* Set \v(minput) result */ instatus = INP_OK; m_found = y + 1; break; } if (inpbp >= inpbuf + inbufsize) { /* Reached end of buffer? */ if (nowrap) { /* If /NOWRAP...*/ instatus = INP_BF; /* ...return indicating buffer full. */ *inpbp = NUL; goto xinput; } *inpbp = NUL; /* Make it null-terminated */ inpbp = inpbuf; /* Yes. */ } } #ifdef CK_BURST else if (y <= -1 && burst > 0) { debug(F111,"doinput (y<=-1&&burst>0)","burst",burst); /* A timeout occurred so there can't */ burst = 0; /* be data waiting; must check timo */ } refill: if (burst <= 0) { /* No buffered chars remaining... */ myflsh(); /* Flush buffered output */ if (local) { /* Get size of next input burst */ burst = ttchk(); if (burst < 0) { /* ttchk() says connection is closed */ instatus = INP_IO; /* Status = i/o error */ #ifndef NOLOCAL #ifdef OS2 term_io = term_io_save; #endif /* OS2 */ #endif /* NOLOCAL */ if ((!network #ifdef TN_COMPORT || istncomport() #endif /* TN_COMPORT */ ) && carrier != CAR_OFF) { /* The test is written this way because the Microsoft compiler * is producing bad code if written: * * if (network && (!istncomport() || carrier == CAR_OFF) ) */ break; } else { printf("Fatal error - disconnected.\n"); ttclos(1); break; } } if (inintr) { if ((icn = conchk()) > 0) { /* Interrupt from keyboard? */ kbchar = coninc(0); debug(F101,"input interrupted from keyboard","",icn); while (--icn > 0) coninc(0); /* Yes, absorb chars. */ break; /* And fail. */ } } } else { burst = conchk(); } debug(F101,"doinput burst","",burst); /* Prevent overflow of "conbuf" and "sesbuf" */ if (burst > MAXBURST) burst = MAXBURST; /* Did not match, timer exceeded? */ t = gtimer(); debug(F111,"doinput gtimer","burst",t); debug(F101,"doinput timo","",timo); if ((t >= timo) && (timo > 0)) break; else if (insilence > 0 && (t - lastchar) > insilence) break; } else { debug(F111,"doinput (burst > 0)","burst",burst); } #else /* CK_BURST */ refill: myflsh(); /* Flush buffered output */ /* Did not match, timer exceeded? */ t = gtimer(); debug(F111,"doinput gtimer","no burst",t); debug(F101,"doinput timo","",timo); if ((t >= timo) && (timo > -1)) break; else if (insilence > 0 && (t - lastchar) > insilence) break; #endif /* CK_BURST */ } /* Still have time left, continue. */ xinput: myflsh(); /* Flush buffered output */ if (instatus == INP_BF) { /* Buffer full and /NOWAIT */ x = 0; /* Must not succeed */ } else { /* Buffer full and /NOWAIT */ if (nomatch) x = 1; /* Succeed if nomatch and timed out */ if (x > 0 && !nomatch) instatus = 0; } #ifndef NOLOCAL #ifdef OS2 term_io = term_io_save; #endif /* OS2 */ #endif /* NOLOCAL */ #ifdef COMMENT #ifdef IKS_OPTION #ifdef CK_AUTODL if (is_tn && TELOPT_ME(TELOPT_KERMIT) && inautodl) { tn_siks(KERMIT_STOP); /* Send Kermit-Server Stop */ } #endif /* CK_AUTODL */ #endif /* IKS_OPTION */ #endif /* COMMENT */ #ifdef GFTIMER fpt = gftimer(); /* Get elapsed time */ /* If a long is 32 bits, it would take about 50 days for this to overflow. */ inetime = (int)(fpt * (CKFLOAT)1000.0); #else inetime = (int)(gtimer() * 1000); #endif /* GFTIMER */ if (x > 0) makestr(&inpmatch,&matchbuf[matchindex]); /* \v(inmatch) */ return(x); /* Return the return code. */ } #endif /* NOSPL */ #ifndef NOSPL /* REINPUT Command */ /* Note, the timeout parameter is required, but ignored. Syntax is compatible with MS-DOS Kermit except timeout can't be omitted. This function only looks at the characters already received and does not read any new characters from the connection. */ int doreinp(timo,s,pat) int timo; char *s; int pat; { int x, y, i; char *xx, *xp, *xq = (char *)0; CHAR c; if (!s) s = ""; debug(F101,"doreinput pat","",pat); y = (int)strlen(s); debug(F111,"doreinput search",s,y); if (y > inbufsize) { /* If search string longer than */ debug(F101,"doreinput inbufsize","",inbufsize); return(0); /* input buffer, fail. */ } makestr(&inpmatch,NULL); if (!matchbuf) matchbuf = malloc(MATCHBUFSIZ+1); matchindex = 0; x = 0; /* Return code, assume failure */ i = 0; /* String pattern match position */ if (!inpcas[cmdlvl]) { /* INPUT CASE = IGNORE? */ xp = malloc(y+2); /* Make a separate copy of the */ if (!xp) { /* search string. */ printf("?malloc error 6\n"); return(x); } else xq = xp; /* Keep pointer to beginning. */ while (*s) { /* Yes, convert to lowercase */ *xp = *s; if (isupper(*xp)) *xp = (char) tolower(*xp); xp++; s++; } *xp = NUL; /* Terminate it! */ s = xq; /* Move search pointer to it. */ } xx = *inpbp ? inpbp : inpbuf; /* Current INPUT buffer pointer */ do { c = *xx++; /* Get next character */ if (!c) break; if (xx >= inpbuf + inbufsize) /* Wrap around if necessary */ xx = inpbuf; if (!inpcas[cmdlvl]) { /* Ignore alphabetic case? */ if (isupper(c)) c = (CHAR) tolower(c); /* Yes */ } if (pat) { int j; if (matchbuf) { if (matchindex < MATCHBUFSIZ) { matchbuf[matchindex++] = c; matchbuf[matchindex] = NUL; } for (j = 0; j < matchindex; j++) { /* Gross but effective */ if (ckmatch(s,&matchbuf[j],1,1)) { debug(F101,"GOT IT","",j); matchindex = j; x = 1; break; } } } if (x > 0) break; continue; } debug(F000,"doreinp char","",c); debug(F000,"compare char","",(CHAR) s[i]); if (((char) c) == ((char) s[i])) { /* Check for match */ i++; /* Got one, go to next character */ } else { /* Don't have a match */ int j; for (j = i; i > 0; ) { /* [jrs] search backwards for it */ i--; if (((char) c) == ((char) s[i])) { if (!strncmp(s,&s[j-i],i)) { i++; break; } } } } /* [jrs] or return to zero from -1 */ if (s[i] == '\0') { /* Matched all the way to end? */ ckstrncpy(matchbuf,s,MATCHBUFSIZ); matchindex = 0; x = 1; /* Yes, */ break; /* done. */ } } while (xx != inpbp && x < 1); /* Until back where we started. */ if (!inpcas[cmdlvl]) if (xq) free(xq); /* Free this if it was malloc'd. */ makestr(&inpmatch,&matchbuf[matchindex]); /* \v(inmatch) */ return(x); /* Return search result. */ } /* X X S T R I N G -- Interpret strings containing backslash escapes */ /* Z Z S T R I N G -- (new name...) */ /* Copies result to new string. strips enclosing braces or doublequotes. interprets backslash escapes. returns 0 on success, nonzero on failure. tries to be compatible with MS-DOS Kermit. Syntax of input string: string = chars | "chars" | {chars} chars = (c*e*)* where c = any printable character, ascii 32-126 and e = a backslash escape and * means 0 or more repetitions of preceding quantity backslash escape = \operand operand = {number} | number | fname(operand) | v(name) | $(name) | m(name) number = [r]n[n[n]]], i.e. an optional radix code followed by 1-3 digits radix code is oO (octal), xX (hex), dD or none (decimal) (see xxesc()). */ #ifndef NOFRILLS int yystring(s,s2) char *s; char **s2; { /* Reverse a string */ int x; static char *new; new = *s2; if (!s || !new) return(-1); /* Watch out for null pointers. */ if ((x = (int)strlen(s)) == 0) { /* Recursion done. */ *new = '\0'; return(0); } x--; /* Otherwise, call self */ *new++ = s[x]; /* to reverse rest of string. */ s[x] = 0; return(yystring(s,&new)); } #endif /* NOFRILLS */ static char ipabuf[16] = { NUL }; /* IP address buffer */ static char * getip(s) char *s; { char c=NUL; /* Workers... */ int i=0, p=0, d=0; int state = 0; /* State of 2-state FSA */ while ((c = *s++)) { switch(state) { case 0: /* Find first digit */ i = 0; /* Output buffer index */ ipabuf[i] = NUL; /* Initialize output buffer */ p = 0; /* Period counter */ d = 0; /* Digit counter */ if (isdigit(c)) { /* Have first digit */ d = 1; /* Count it */ ipabuf[i++] = c; /* Copy it */ state = 1; /* Change state */ } break; case 1: /* In numeric field */ if (isdigit(c)) { /* Have digit */ if (++d > 3) /* Too many */ state = 0; /* Start over */ else /* Not too many */ ipabuf[i++] = c; /* Keep it */ } else if (c == '.' && p < 3) { /* Have a period */ p++; /* Count it */ if (d == 0) /* Not preceded by a digit */ state = 0; /* Start over */ else /* OK */ ipabuf[i++] = c; /* Keep it */ d = 0; /* Reset digit counter */ } else if (p == 3 && d > 0) { /* Not part of address */ ipabuf[i] = NUL; /* If we have full IP address */ return((char *)ipabuf); /* Return it */ } else { /* Otherwise */ state = 0; /* Start over */ ipabuf[0] = NUL; /* (in case no more chars left) */ } } } /* Fall thru at end of string */ ipabuf[i] = NUL; /* Maybe we have one */ return((p == 3 && d > 0) ? (char *)ipabuf : ""); } #endif /* NOSPL */ /* Date Routines */ /* Z J D A T E -- Convert yyyymmdd date to Day of Year */ static int jdays[12] = { 0,31,59,90,120,151,181,212,243,273,304,334 }; static int ldays[12] = { 0,31,60,91,121,152,182,213,244,274,305,335 }; static char zjdbuf[12] = { NUL, NUL }; /* Deinde, ne in posterum a XII kalendas aprilis aequinoctium recedat, statuimus bissextum quarto quoque anno (uti mos est) continuari debere, praeterquam in centesimis annis; qui, quamvis bissextiles antea semper fuerint, qualem etiam esse volumus annum MDC, post eum tamen qui deinceps consequentur centesimi non omnes bissextiles sint, sed in quadringentis quibusque annis primi quique tres centesimi sine bissexto transigantur, quartus vero quisque centesimus bissextilis sit, ita ut annus MDCC, MDCCC, MDCCCC bissextiles non sint. Anno vero MM, more consueto dies bissextus intercaletur, februario dies XXIX continente, idemque ordo intermittendi intercalandique bissextum diem in quadringentis quibusque annis perpetuo conservetur. - Gregorius XIII, Anno Domini MDLXXXII. */ char * zjdate(date) char * date; { /* date = yyyymmdd */ char year[5]; char month[3]; char day[3]; int d, m, x, y; int leapday, j; char * time = NULL; if (!date) date = ""; /* Validate arg */ x = strlen(date); if (x < 1) return("0"); if (x < 8) return("-1"); for (x = 0; x < 8; x++) if (!isdigit(date[x])) return("-1"); if (date[8]) if (date[9]) time = date + 9; year[0] = date[0]; /* Isolate year */ year[1] = date[1]; year[2] = date[2]; year[3] = date[3]; year[4] = '\0'; month[0] = date[4]; /* Month */ month[1] = date[5]; month[2] = '\0';; day[0] = date[6]; /* And day */ day[1] = date[7]; day[2] = '\0'; leapday = 0; /* Assume no leap day */ y = atoi(year); m = atoi(month); d = atoi(day); if (m > 2) { /* No Leap day before March */ if (y % 4 == 0) { /* If year is divisible by 4 */ leapday = 1; /* It's a Leap year */ if (y % 100 == 0) { /* Except if divisible by 100 */ if (y % 400 != 0) /* but not by 400 */ leapday = 0; } } } j = jdays[m - 1] + d + leapday; /* Day of year */ if (time) sprintf(zjdbuf,"%04d%03d %s",y,j,time); /* SAFE */ else sprintf(zjdbuf,"%04d%03d",y,j); /* SAFE */ return((char *)zjdbuf); } static char jzdbuf[32]; /* J Z D A T E -- Convert Day of Year to yyyyddmm date */ char * jzdate(date) char * date; { /* date = yyyyddd */ char year[5]; /* with optional time */ char day[4]; char * time = NULL, * p; int d, m, x, y; int leapday, j; int * zz; if (!date) date = ""; /* Validate arg */ x = strlen(date); debug(F111,"jzdate len",date,x); if (x < 1) return("0"); if (x < 7) return("-1"); if (x > 8) time = date + 8; for (x = 0; x < 7; x++) if (!isdigit(date[x])) return("-1"); year[0] = date[0]; /* Isolate year */ year[1] = date[1]; year[2] = date[2]; year[3] = date[3]; year[4] = '\0'; debug(F110,"jzdate year",year,0); day[0] = date[4]; /* And day */ day[1] = date[5]; day[2] = date[6]; day[3] = '\0'; debug(F110,"jzdate day",day,0); j = atoi(day); if (j > 366) return("-1"); leapday = 0; /* Assume no leap day */ y = atoi(year); if (y % 4 == 0) { /* If year is divisible by 4 */ leapday = 1; /* It's a Leap year */ if (y % 100 == 0) { /* Except if divisible by 100 */ if (y % 400 != 0) /* but not by 400 */ leapday = 0; } } debug(F101,"jzdate leapday","",leapday); zz = leapday ? ldays : jdays; for (x = 0; x < 11; x++) if (j > zz[x] && j <= zz[x+1]) break; m = x + 1; debug(F101,"jzdate m","",m); d = j - zz[x]; debug(F101,"jzdate d","",d); if (time) sprintf(jzdbuf,"%04d%02d%02d %s",y,m,d,time); /* SAFE */ else sprintf(jzdbuf,"%04d%02d%02d",y,m,d); /* SAFE */ debug(F101,"jzdate jzdbuf",jzdbuf,0); p = ckcvtdate((char *)jzdbuf, 0); /* Convert to standard form */ ckstrncpy(jzdbuf,p,32); if (!time) jzdbuf[8] = NUL; /* Remove time if not wanted */ return((char *)jzdbuf); } /* M J D -- Modified Julian Date */ /* Call with: Standard-format date-time string: yyyymmdd[ hh:mm:ss]. The time, if any, is ignored. Returns: -1L on error, otherwise: The number of days since 17 Nov 1858 as a whole number: 16 Nov 1858 = -1, 17 Nov 1858 = 0, 18 Nov 1858 = 1, 19 Nov 1858 = 2, ... The Modified Julian Date is defined by the International Astronomical Union as the true Julian date minus 2400000.5 days. The true Julian date is the number days since since noon of 1 January 4713 BCE of the Julian proleptic calendar. Conversions between calendar dates and Julian dates, however, assume Gregorian dating. */ long mjd(date) char * date; { char year[5]; char month[3]; char day[3]; int x, a, d, m, y; long z; if (!date) date = ""; /* Validate arg */ x = strlen(date); if (x < 1) return(0L); if (x < 8) return(-1L); for (x = 0; x < 8; x++) if (!isdigit(date[x])) return(-1L); year[0] = date[0]; /* Isolate year */ year[1] = date[1]; year[2] = date[2]; year[3] = date[3]; year[4] = '\0'; month[0] = date[4]; /* Month */ month[1] = date[5]; month[2] = '\0';; m = atoi(month); day[0] = date[6]; /* And day */ day[1] = date[7]; day[2] = '\0'; d = atoi(day); a = (14-m)/12; /* Calculate true Julian date */ y = atoi(year) + 4800 - a; m = m + 12 * a - 3; z = d + (long)(306*m+5)/10 + (long)(y*365) + y/4 - y/100 + y/400 - 32045L; z -= 2400001L; /* Convert JD to MJD */ return(z); } static char mjd2dbuf[32]; /* M J D 2 D A T E -- Converts MJD to yyyymmdd */ char * #ifdef CK_ANSIC mjd2date(long mjd) #else mjd2date(mjd) long mjd; #endif /* CK_ANSIC */ /* mjd2date */ { long jd, l, n; int d, m, y; jd = (long)(mjd + 2400001L); l = jd + 68569; n = 4 * l / 146097L; l = l - (146097 * n + 3) / 4; y = 4000 * (l + 1) / 1461001L; l = l - 1461 * y / 4 + 31; m = 80 * l / 2447; d = l - 2447 * m / 80; l = m / 11; m = m + 2 - 12 * l; y = 100 * (n - 49) + y + l; sprintf(mjd2dbuf,"%04d%02d%02d",y,m,d); /* SAFE */ return((char *)mjd2dbuf); } #ifndef NOSPL static char ** flist = (char **) NULL; /* File list for \fnextfile() */ static int flistn = 0; /* Number of items in file list */ /* The function return-value buffer must be global, since fneval() returns a pointer to it. fneval() is called only by zzstring(), which always copies the result out of this buffer to somewhere else, so it's OK to have only one buffer for this in most cases. However, since function calls can be nested -- e.g. functions whose arguments are functions, or recursive functions, at some point we should convert this to an array of buffers, indexed by function depth (which might or might not be the same as the "depth" variable). Also, since function results are potentially quite big, we'd need to allocate and deallocate dynamically as we descend and ascend function depth. Left for a future release... */ char fnval[FNVALL+2]; /* Function return value */ static int fndepth = 0; /* (we don't actually use this yet) */ int fnsuccess = 1; extern int fnerror; /* f p f o r m a t -- Floating-point number nicely formatted. */ /* Returns results from a circular 1K buffer. Don't count on too many results remaining available at once; it could be anywhere from 5 to maybe 100, depending on the sizes of the results. */ #ifdef CKFLOAT #define FPFMTSIZ 1024 static char fpfmtbuf[FPFMTSIZ] = { NUL, NUL }; static int fpfbufpos = 0; /* (why was this char before?) */ char * fpformat(fpresult,places,round) CKFLOAT fpresult; int places, round; { char fbuf[16]; /* For creating printf format */ int nines = 0, sign = 0, x, y, i, j, size = 0; char * buf; CKFLOAT ftmp; x = places ? places : (fp_digits ? fp_digits : 6); debug(F101,"fpformat fpresult","",fpresult); debug(F101,"fpformat places","",places); debug(F101,"fpformat fpfbufpos 1","",fpfbufpos); ftmp = fpresult; if (ftmp < 0.0) ftmp = 0.0 - fpresult; #ifdef FNFLOAT if (!fp_rounding && /* If printf doesn't round, */ (places > 0 || /* round result to decimal places. */ (places == 0 && round))) fpresult += (0.5 / pow(10.0,(CKFLOAT)places)); y = (ftmp == 0.0) ? 1 : (int)log10(ftmp); size = y + x + 3; /* Estimated length of result */ if (fpresult < 0.0) size++; #else size = 200; /* No way to estimate, be generous */ #endif /* FNFLOAT */ debug(F101,"fpformat size","",size); if (fpfbufpos > (FPFMTSIZ - size)) /* Wrap around if necessary */ fpfbufpos = 0; debug(F101,"fpformat fpfbufpos 1","",fpfbufpos); buf = &fpfmtbuf[fpfbufpos]; if (places > 0) { /* If places specified */ /* use specified places to write given number of digits */ sprintf(fbuf,"%%0.%df",places); /* SAFE */ sprintf(buf,fbuf,fpresult); /* SAFE */ } else { /* Otherwise... */ /* Go for max precision */ sprintf(fbuf,"%%0.%df",fp_digits); /* SAFE */ sprintf(buf,fbuf,fpresult); /* SAFE */ } if (buf[0] == '-') sign = 1; debug(F111,"fpresult 1 errno",buf,errno); /* Check for over/underflow */ debug(F111,"fpresult 1 fpfbufpos",buf,fpfbufpos); /* Give requested decimal places */ for (i = sign; i < FPFMTSIZ && buf[i]; i++) { if (buf[i] == '.') /* First find the decimal point */ break; else if (i > fp_digits + sign - 1) /* replacing garbage */ buf[i] = '0'; /* digits with 0... */ } if (buf[i] == '.') { /* Have decimal point */ int gotend = 0; /* places < 0 so truncate fraction */ if (places < 0 || (places == 0 && round)) { buf[i] = NUL; } else if (places > 0) { /* d > 0 so this many decimal places */ i++; /* First digit after decimal */ for (j = 0; j < places; j++) { /* Truncate after d decimal */ if (!buf[j+i]) /* places or extend to d */ gotend = 1; /* decimal places. */ if (gotend || j+i+sign > fp_digits) buf[j+i] = '0'; } buf[j+i] = NUL; } else { /* places == 0 so Do The Right Thing */ for (j = (int)strlen(buf) - 1; j > i+1; j--) { if ((j - sign) > fp_digits) buf[j] = '0'; if (buf[j] == '0') buf[j] = NUL; /* Strip useless trailing 0's. */ else break; } } } fpfmtbuf[FPFMTSIZ-1] = NUL; j = strlen(buf); sign = 0; for (i = j-1; i >= 0; i--) { if (buf[i] == '9') nines++; else break; } /* Do something about xx.xx99999999... */ if (nines > 5) { if (isdigit(buf[i]) && i < FPFMTSIZ - 2) { buf[i] = buf[i] + 1; buf[i+1] = '0'; buf[i+2] = '\0'; } } if (!strncmp(buf,"-0.0",FPFMTSIZ)) ckstrncpy(buf,"0.0",FPFMTSIZ); fpfbufpos += (int)strlen(buf) + 1; return((char *)buf); } #endif /* CKFLOAT */ static VOID evalerr(fn) char * fn; { if (fndiags) { if (divbyzero) ckmakmsg(fnval,FNVALL,"",NULL); else ckmakmsg(fnval,FNVALL,"",NULL); } } static int ckcindex(c,s) char c, *s; { int rc; if (!c || !s) return(0); for (rc = 0; s[rc]; rc++) { if (c == s[rc]) return(rc+1); } return(0); } static char * dokwval(s,sep) char * s, * sep; { char c = '\0', * p, * kw = NULL, * vp = NULL; char * rc = "0"; /* Return code */ int x = 0; if (!s) return(rc); if (!*s) return(rc); debug(F110,"kwval arg",s,0); debug(F110,"kwval sep",sep,0); p = (char *)malloc((int)strlen(s)+1); if (!p) goto xdokwval; strcpy(p,s); /* SAFE */ s = p; while (*s < '!' && *s > '\0') /* Get first nonblank */ s++; if (!*s) goto xdokwval; if (ckcindex(*s,sep)) /* Separator but no keyword */ goto xdokwval; kw = s; /* Keyword */ while (*s > ' ') { if (ckcindex(*s,sep)) { /* keyword=... */ c = *s; break; } s++; } if (*kw) rc = "1"; /* Have keyword, promote return code */ *s++ = NUL; /* Terminate keyword */ while (*s < '!' && *s > '\0') /* Skip blanks */ s++; if (!c && ckcindex(*s,sep)) { c = *s++; /* Have separator */ while (*s < '!' && *s > '\0') /* Skip blanks */ s++; } if (c) { vp = s; if (*vp) rc = "2"; /* Have value, another promotion */ #ifdef COMMENT while (*s > ' ') /* Skip to end */ s++; *s = NUL; /* Terminate value */ #endif /* COMMENT */ } debug(F110,"kwval c",ckctoa(c),0); debug(F110,"kwval keyword",kw,0); debug(F110,"kwval value",vp,0); makestr(&lastkwval,kw); vp = brstrip(vp); debug(F110,"kwval value",vp,0); x = addmac(kw,vp); debug(F111,"kwval addmac",kw,x); xdokwval: if (p) free(p); return((x < 0) ? "-1" : rc); } static int isaarray(s) char * s; { /* Is s an associative array element */ int state = 0; CHAR c; if (!s) return(0); while ((c = *s++)) { if (!isprint(c)) { return(0); } else if (c == '<') { if (state != 0) return(0); state = 1; } else if (c == '>') { return((state != 1 || *s) ? 0 : 1); } } return(0); } static char * /* Evaluate builtin functions */ fneval(fn,argp,argn,xp) char *fn, *argp[]; int argn; char * xp; { int i=0, j=0, k=0, len1=0, len2=0, len3=0, n=0, t=0, x=0, y=0; int cx, failed = 0; /* Return code, 0 = ok */ long z = 0L; char *bp[FNARGS + 1]; /* Pointers to malloc'd strings */ char c = NUL; char *p = NULL, *s = NULL; char *val1 = NULL, *val2 = NULL; /* Pointers to numeric string values */ #ifdef RECURSIVE int rsave = recursive; #endif /* RECURSIVE */ #ifdef OS2 int zsave = zxpn; #endif /* OS2 */ if (!fn) fn = ""; /* Protect against null pointers */ if (!*fn) return(""); for (i = 0; i < FNARGS; i++) /* Initialize argument pointers */ bp[i] = NULL; /* IMPORTANT: Note that argn is not an accurate count of the number of arguments. We can't really tell if an argument is null until after we execute the code below. So argn is really the maximum number of arguments we might have. Argn should always be at least 1, even if the function is called with empty parentheses (but don't count on it). */ debug(F111,"fneval",fn,argn); debug(F110,"fneval",argp[0],0); if (argn > FNARGS) /* Discard excess arguments */ argn = FNARGS; fndepth++; debug(F101,"fneval fndepth","",fndepth); p = fnval; fnval[0] = NUL; y = lookup(fnctab,fn,nfuncs,&x); /* Look up the function name */ cx = y; /* Because y is too generic... */ if (cx < 0) { /* Not found */ failed = 1; if (fndiags) { /* FUNCTION DIAGNOSTIC ON */ int x; x = strlen(fn); /* The following sprintf's are safe */ switch (cx) { case -1: if (x + 32 < FNVALL) sprintf(fnval,"",fn); else sprintf(fnval,""); break; case -2: if (x + 26 < FNVALL) sprintf(fnval,"",fn); else sprintf(fnval,""); break; case -3: sprintf(fnval,""); break; default: if (x + 26 < FNVALL) sprintf(fnval,"",fn); else sprintf(fnval,""); break; } } goto fnend; /* Always leave via common exit */ } fn = fnctab[x].kwd; /* Full name of function */ if (argn < 0) { failed = 1; p = fnval; if (fndiags) sprintf(fnval,"",fn); goto fnend; } if (cx == FN_LIT) { /* literal(arg1) */ debug(F010,"flit",xp,0); p = xp ? xp : ""; /* Return a pointer to arg itself */ goto fnend; } #ifdef DEBUG if (deblog) { int j; for (j = 0; j < argn; j++) debug(F111,"fneval arg",argp[j],j); } #endif /* DEBUG */ for (j = argn-1; j >= 0; j--) { /* Uncount empty trailing args */ if (!argp[j]) argn--; else if (!*(argp[j])) argn--; else break; } debug(F111,"fneval argn",fn,argn); /* \fliteral() and \fcontents() are special functions that do not evaluate their arguments, and are treated specially here. After these come the functions whose arguments are evaluated in the normal way. */ #ifdef COMMENT /* (moved up) */ if (cx == FN_LIT) { /* literal(arg1) */ debug(F110,"flit",xp,0); p = xp ? xp : ""; /* Return a pointer to arg itself */ goto fnend; } #endif /* COMMENT */ if (cx == FN_CON) { /* Contents of variable, unexpanded. */ char c; int subscript = 0; if (!(p = argp[0]) || !*p) { failed = 1; p = fnval; if (fndiags) sprintf(fnval,""); goto fnend; } p = brstrip(p); if (*p == CMDQ) p++; if ((c = *p) == '%') { /* Scalar variable. */ c = *++p; /* Get ID character. */ p = ""; /* Assume definition is empty */ if (!c) { /* Double paranoia */ failed = 1; p = fnval; if (fndiags) sprintf(fnval,""); goto fnend; } if (c >= '0' && c <= '9') { /* Digit for macro arg */ if (maclvl < 0) /* Digit variables are global */ p = g_var[c]; /* if no macro is active */ else /* otherwise */ p = m_arg[maclvl][c - '0']; /* they're on the stack */ } else if (c == '*') { #ifdef COMMENT p = (maclvl > -1) ? m_line[maclvl] : topline; if (!p) p = ""; #else int nx = FNVALL; char * sx = fnval; p = fnval; #ifdef COMMENT if (cmdsrc() == 0 && topline) p = topline; else #endif /* COMMENT */ if (zzstring("\\fjoin(&_[],{ },1)",&sx,&nx) < 0) { failed = 1; p = fnval; if (fndiags) sprintf(fnval,""); debug(F110,"zzstring fcontents(\\%*)",p,0); } #endif /* COMMENT */ } else { if (isupper(c)) c -= ('a'-'A'); p = g_var[c]; /* Letter for global variable */ } if (!p) p = ""; goto fnend; } else if (c == '&') { /* Array reference. */ int vbi, d; if (arraynam(p,&vbi,&d) < 0) { /* Get name and subscript */ failed = 1; p = fnval; if (fndiags) sprintf(fnval,""); goto fnend; } subscript = chkarray(vbi,d); /* Check the array */ if (subscript >= 0) { /* Array is declared? */ vbi -= ARRAYBASE; /* Convert name to index */ if (a_dim[vbi] >= d) { /* If subscript in range */ char **ap; ap = a_ptr[vbi]; /* get data pointer */ if (ap) { /* and if there is one */ p = ap[d]; /* return it */ goto fnend; } } } else { /* Array not declared or element */ fnval[0] = NUL; /* out of range - return null string */ p = fnval; /* fdc 2010-12-30 */ goto fnend; } } else { failed = 1; p = fnval; if (fndiags) sprintf(fnval,""); goto fnend; } } p = fnval; /* Default result pointer */ fnval[0] = NUL; /* Default result = empty string */ for (i = 0; i < argn; i++) { /* Loop to expand each argument */ n = MAXARGLEN; /* Allow plenty of space */ bp[i] = s = malloc(n+1); /* Allocate space for this argument */ if (bp[i] == NULL) { /* Handle failure to get space */ failed = 1; if (fndiags) ckmakmsg(fnval,FNVALL,"",NULL); goto fnend; } p = argp[i] ? argp[i] : ""; /* Point to this argument */ /* Trim leading and trailing spaces from the original argument, before evaluation. This code new to edit 184. Fixed in edit 199 to trim blanks BEFORE stripping braces. */ { int x, j; x = strlen(p); j = x - 1; /* Trim trailing whitespace */ while (j > 0 && (*(p + j) == SP || *(p + j) == HT)) *(p + j--) = NUL; while (*p == SP || *p == HT) /* Strip leading whitespace */ p++; x = strlen(p); if (*p == '{' && *(p+x-1) == '}') { /* NOW strip braces */ p[x-1] = NUL; p++; x -= 2; } } /* Now evaluate the argument */ debug(F111,"fneval calling zzstring",p,n); t = zzstring(p,&s,&n); /* Expand arg into new space */ debug(F101,"fneval zzstring","",t); debug(F101,"fneval zzstring","",n); if (t < 0) { debug(F101,"fneval zzstring fails, arg","",i); failed = 1; if (fndiags) { if (n == 0) ckmakmsg(fnval,FNVALL, "",NULL); else ckmakmsg(fnval,FNVALL, "",NULL); } goto fnend; } debug(F111,"fneval arg",bp[i],i); } #ifdef DEBUG if (deblog) { int j; for (j = 0; j < argn; j++) { debug(F111,"fneval arg post eval",argp[j],j); debug(F111,"fneval evaluated arg",bp[j],j); } } #endif /* DEBUG */ { /* Adjust argn for empty trailing arguments. */ /* For example when an arg is a variable name but the */ /* variable has no value. July 2006. */ int j, old; char *p; old = argn; for (j = argn - 1; j >= 0; j--) { p = bp[j]; if (!p) argn--; else if (!*p) argn--; else break; } #ifdef DEBUG if (argn != old) debug(F101,"fneval adjusted argn","",argn); #endif /* DEBUG */ } /* From this point on, bp[0..argn-1] are not NULL and all must be freed before returning. */ if (argn < 1) { /* Catch required args missing */ switch (cx) { case FN_DEF: case FN_EVA: case FN_EXE: case FN_CHR: case FN_COD: case FN_MAX: case FN_MIN: case FN_MOD: case FN_FD: case FN_FS: case FN_TOD: case FN_FFN: case FN_BSN: case FN_RAW: case FN_CMD: case FN_2HEX: case FN_2OCT: case FN_DNAM: #ifdef FN_ERRMSG case FN_ERRMSG: #endif /* FN_ERRMSG */ #ifdef CK_KERBEROS case FN_KRB_TK: case FN_KRB_NX: case FN_KRB_IV: case FN_KRB_TT: case FN_KRB_FG: #endif /* CK_KERBEROS */ case FN_MJD2: case FN_N2TIM: case FN_DIM: case FN_DATEJ: case FN_PNCVT: case FN_PERM: case FN_ALOOK: case FN_TLOOK: case FN_ABS: case FN_AADUMP: case FN_JOIN: #ifdef CKFLOAT case FN_FPABS: case FN_FPEXP: case FN_FPLOG: case FN_FPLN: case FN_FPMOD: case FN_FPSQR: case FN_FPADD: case FN_FPDIV: case FN_FPMUL: case FN_FPPOW: case FN_FPSUB: case FN_FPINT: case FN_FPROU: case FN_FPSIN: case FN_FPCOS: case FN_FPTAN: #endif /* CKFLOAT */ #ifdef TCPSOCKET case FN_HSTADD: case FN_HSTNAM: #endif /* TCPSOCKET */ case FN_DELSEC: #ifdef COMMENT case FN_KWVAL: case FN_SLEEP: case FN_MSLEEP: #endif /* COMMENT */ #ifdef NT case FN_SNAME: case FN_LNAME: #endif /* NT */ failed = 1; p = fnval; if (fndiags) ckmakmsg(fnval,FNVALL,"",NULL); goto fnend; } } p = fnval; /* Reset these again. */ fnval[0] = NUL; switch (cx) { /* Do function on expanded args. */ #ifdef TCPSOCKET case FN_HSTADD: p = ckaddr2name(bp[0]); goto fnend; case FN_HSTNAM: p = ckname2addr(bp[0]); goto fnend; #endif /* TCPSOCKET */ case FN_DEF: /* \fdefinition(arg1) */ k = isaarray(bp[0]) ? mxxlook(mactab,bp[0],nmac) : mxlook(mactab,bp[0],nmac); p = (k > -1) ? mactab[k].mval : ""; goto fnend; case FN_EVA: /* \fevaluate(arg1) */ p = *(bp[0]) ? evalx(bp[0]) : ""; if (!*p && fndiags) { failed = 1; p = fnval; evalerr(fn); } goto fnend; case FN_EXE: /* \fexecute(arg1) */ j = (int)strlen(s = bp[0]); /* Length of macro invocation */ p = ""; /* Initialize return value to null */ if (j) { /* If there is a macro to execute */ while (*s == SP) s++,j--; /* strip leading spaces */ p = s; /* remember beginning of macro name */ for (i = 0; i < j; i++) { /* find end of macro name */ if (*s == SP) break; s++; } if (*s == SP) { /* if there was a space after */ *s++ = NUL; /* terminate the macro name */ while (*s == SP) s++; /* skip past any extra spaces */ } else s = ""; /* maybe there are no arguments */ if (p && *p) { k = mlook(mactab,p,nmac); /* Look up the macro name */ debug(F111,"fexec mlook",p,k); } else k = -1; if (k < 0) { char * p2 = p; failed = 1; p = fnval; if (fndiags) ckmakxmsg(fnval,FNVALL, "", NULL,NULL,NULL,NULL,NULL,NULL,NULL); goto fnend; } /* This is just a WEE bit dangerous because we are copying up to 9 arguments into the space reserved for one. It won't overrun the buffer, but if there are lots of long arguments we might lose some. The other problem is that if the macro has more than 3 arguments, the 4th through last are all concatenated onto the third. (The workaround is to use spaces rather than commas to separate them.) Leaving it like this to avoid having to allocate tons more buffers. */ if (argn > 1) { /* Commas used instead of spaces */ int i; char *p = bp[0]; /* Reuse this space */ *p = NUL; /* Make into dodo() arg list */ for (i = 1; i < argn; i++) { ckstrncat(p,bp[i],MAXARGLEN); ckstrncat(p," ",MAXARGLEN); } s = bp[0]; /* Point to new list */ } p = ""; /* Initialize return value */ if (k >= 0) { /* If macro found in table */ /* Go set it up (like DO cmd) */ if ((j = dodo(k,s,cmdstk[cmdlvl].ccflgs)) > 0) { if (cmpush() > -1) { /* Push command parser state */ extern int ifc; int ifcsav = ifc; /* Push IF condition on stack */ k = parser(1); /* Call parser to execute the macro */ cmpop(); /* Pop command parser */ ifc = ifcsav; /* Restore IF condition */ if (k == 0) { /* No errors, ignore action cmds. */ p = mrval[maclvl+1]; /* If OK, set return value. */ if (p == NULL) p = ""; } } else { /* Can't push any more */ debug(F100,"zzstring fneval fexec failure","",0); printf("\n?\\fexec() too deeply nested\n"); while (cmpop() > -1) ; p = ""; } } } } debug(F110,"zzstring fneval fexecute final p",p,0); goto fnend; #ifdef RECURSIVE case FN_RDIR: /* \frdir..() - Recursive dir count */ case FN_RFIL: /* \frfiles() - Recursive file count */ /* recursive = 2; */ /* fall thru... */ #endif /* RECURSIVE */ case FN_FC: /* \ffiles() - File count. */ case FN_DIR: { /* \ffdir.() - Directory count. */ char abuf[16], *s; char ** ap = NULL; int x, xflags = 0; if (matchdot) xflags |= ZX_MATCHDOT; if (cx == FN_RDIR || cx == FN_RFIL) { xflags |= ZX_RECURSE; #ifdef CKSYMLINK /* Recursive - don't follow symlinks */ xflags |= ZX_NOLINKS; #endif /* CKSYMLINK */ } failed = 0; if (argn < 1) { p = "0"; goto fnend; } if (cx == FN_DIR || cx == FN_RDIR) { /* Only list directories */ debug(F100,"FN_DIR or FN_RDIR","",0); xflags |= ZX_DIRONLY; #ifdef OS2 zxpn = 1; /* Use the alternate list */ #endif /* OS2 */ } else { /* List only files */ debug(F100,"Not FN_DIR or FN_RDIR","",0); xflags |= ZX_FILONLY; #ifdef OS2 zxpn = 1; /* Use the alternate list */ #endif /* OS2 */ } if (*(bp[0])) { k = nzxpand(bp[0],xflags); if (k < 0) k = 0; sprintf(fnval,"%d",k); /* SAFE */ p = fnval; } else p = "0"; if (argn > 1) { /* Assign list to array */ fnval[0] = NUL; /* Initial return value */ ckstrncpy(abuf,bp[1],16); /* Get array reference */ s = abuf; if (*s == CMDQ) s++; failed = 1; /* Assume it's bad */ p = fnval; /* Point to result */ if (fndiags) /* Default is this error message */ ckmakmsg(fnval,FNVALL, "",NULL); if (s[0] != '&') /* "Address" of array */ goto fnend; if (s[2]) if (s[2] != '[' || s[3] != ']') goto fnend; if (s[1] >= 64 && s[1] < 91) /* Convert upper to lower */ s[1] += 32; if ((x = dclarray(s[1],k)) < 0) /* File list plus count */ goto fnend; failed = 0; /* Unset failure flag */ ap = a_ptr[x]; /* Point to array we just declared */ sprintf(fnval,"%d",k); /* SAFE */ } #ifdef OS2 if (ap) { /* We are making an array */ int i; char tmp[16]; ap[0] = NULL; /* Containing number of files */ makestr(&(ap[0]),ckitoa(k)); ckstrncpy(tmp,fnval,16); /* Save return value */ for (i = 1; i <= k; i++) { /* Fill it */ ap[i] = NULL; znext(fnval); /* Next filename */ if (!*fnval) /* No more, done */ break; /* In case a premature end */ makestr(&(ap[i]),fnval); } #ifdef ZXREWIND k = zxrewind(); /* Reset the file expansion */ #else k = nzxpand(bp[0],xflags); #endif /* ZXREWIND */ ckstrncpy(fnval,tmp,FNVALL); /* Restore return value */ } #else /* OS2 */ { /* Make copies of the list */ int i; char tmp[16]; if (flist) { /* Free old file list, if any */ for (i = 0; flist[i]; i++) { /* and each string */ free(flist[i]); flist[i] = NULL; } free((char *)flist); } ckstrncpy(tmp,fnval,16); /* Save our return value */ flist = (char **) malloc((k+1) * sizeof(char *)); /* New array */ if (flist) { for (i = 0; i <= k; i++) { /* Fill it */ flist[i] = NULL; znext(fnval); /* Next filename */ if (!*fnval) /* No more, done */ break; makestr(&(flist[i]),fnval); } if (ap) { /* If array pointer given */ ap[0] = NULL; makestr(&(ap[0]),ckitoa(k)); for (i = 0; i < k; i++) { /* Copy file list to array */ ap[i+1] = NULL; makestr(&(ap[i+1]),flist[i]); } } } ckstrncpy(fnval,tmp,FNVALL); /* Restore return value */ flistn = 0; /* Reset global list pointer */ } #endif /* OS2 */ #ifdef RECURSIVE recursive = rsave; #endif /* RECURSIVE */ #ifdef OS2 zxpn = zsave; #endif /* OS2 */ } goto fnend; case FN_FIL: /* \fnextfile() - Next file in list. */ p = fnval; /* (no args) */ *p = NUL; #ifdef OS2 zxpn = 1; /* OS/2 - use the alternate list */ znext(p); /* Call system-dependent function */ zxpn = zsave; /* Restore original list */ #else if (flist) /* Others, use our own list. */ if (flist[flistn]) p = flist[flistn++]; #endif /* OS2 */ goto fnend; } /* Break up big switch... */ switch (cx) { case FN_IND: /* \findex(s1,s2,start,occurrence) */ case FN_RIX: /* \frindex(s1,s2,start,occurrence) */ case FN_SEARCH: /* \fsearch(pat,string,start,occ) */ case FN_RSEARCH: /* \frsearch(pat,string,start,occ) */ case FN_COUNT: { /* \fcount(s1,s2,start) */ int i = 0, right = 0, search = 0, count = 0; int desired = 1; right = (cx == FN_RIX || cx == FN_RSEARCH); search = (cx == FN_SEARCH || cx == FN_RSEARCH); count = (cx == FN_COUNT); p = "0"; if (argn > 1) { /* Only works if we have 2 or 3 args */ int start = 0; char * pat = NULL; len1 = (int)strlen(pat = bp[0]); /* length of string to look for */ len2 = (int)strlen(s = bp[1]); /* length of string to look in */ if (len1 < 1 || len2 < 1) /* Watch out for empty strings */ goto fnend; start = right ? -1 : 0; /* Default starting position */ if (argn > 2) { val1 = *(bp[2]) ? evalx(bp[2]) : "1"; if (argn > 3) { val2 = *(bp[3]) ? evalx(bp[3]) : "1"; if (chknum(val2)) desired = atoi(val2); if (desired * len1 > len2) goto fnend; } if (chknum(val1)) { int t; t = atoi(val1); if (!search) { /* Index or Rindex */ j = len2 - len1; /* Length difference */ t--; /* Convert position to 0-based */ if (t < 0) t = 0; start = t; if (!right && start < 0) start = 0; } else { /* Search or Rsearch */ int x; if (t < 0) t = 0; if (right) { /* Right to left */ if (t > len2) t = len2; start = len2 - t - 1; if (start < 0) goto fnend; x = len2 - t; s[x] = NUL; } else { /* Left to right */ start = t - 1; if (start < 0) start = 0; if (start >= len2) goto fnend; } } } else { failed = 1; evalerr(fn); p = fnval; goto fnend; } } if (count) { /* \fcount() */ int j; for (i = 0; start < len2; i++) { j = ckindex(pat,bp[1],start,0,inpcas[cmdlvl]); if (j == 0) break; start = j; } } else if (search) { /* \fsearch() or \frsearch() */ if (right && pat[0] == '^') { right = 0; start = 0; } if (right) { /* From right */ int k, j = 1; if (start < 0) start = len2 - 1; i = 0; while (start >= 0 && j <= desired) { for (i = start; (i >= 0) && !(k = ckmatch(pat,s+i,inpcas[cmdlvl],1+4)); i--) ; if (k < 1) { /* No match */ i = 0; break; } if (j == desired) { /* The match we want? */ i += k; /* Yes, return string index */ break; } j++; /* No, count this match */ s[i] = NUL; /* null it out */ start = i-1; /* move left and look again */ } } else { /* From left */ int j; i = 0; for (j = 1; j <= desired && start < len2; j++) { i = ckmatch(pat,&s[start],inpcas[cmdlvl],1+4); if (i == 0 || j == desired) break; start += i + 1; } if (j == desired && i != 0) i += start; else i = 0; } } else { /* index or rindex */ int j = 0; i = 0; for (j = 1; j <= desired && start < len2; j++) { i = ckindex(pat,bp[1],start,right,inpcas[cmdlvl]); if (i == 0 || j == desired) break; start = (right) ? len2 - i + 1 : i; } if (j != desired) i = 0; } sprintf(fnval,"%d",i); /* SAFE */ p = fnval; } goto fnend; } case FN_RPL: /* \freplace(s1,s2,s3) */ /* s = bp[0] = source string bp[1] = match string bp[2] = replacement string bp[3] = which occurrence (default = all); p = fnval = destination (result) string */ if (argn < 1) /* Nothing */ goto fnend; if (argn < 2) { /* Only works if we have 2 or 3 args */ ckstrncpy(p,bp[0],FNVALL); } else { int occur = 0, xx = 0, j2; len1 = (int)strlen(bp[0]); /* length of string to look in */ len2 = (int)strlen(bp[1]); /* length of string to look for */ len3 = (argn < 3) ? 0 : (int)strlen(bp[2]); /* Len of replacemnt */ j = len1 - len2 + 1; j2 = j; if (argn > 3) { if (chknum(bp[3])) { occur = atoi(bp[3]); } else { failed = 1; if (fndiags) ckmakmsg(fnval,FNVALL, "",NULL); goto fnend; } } /* If args out of whack... */ if (j < 1 || len1 == 0 || len2 == 0) { ckstrncpy(p,bp[0],FNVALL); /* just return original string */ p[FNVALL] = NUL; } else { ragain: s = bp[0]; /* Point to beginning of string */ while (j-- > 0) { /* For each character */ if (!ckstrcmp(bp[1],s,len2,inpcas[cmdlvl]) && (occur == 0 || occur == ++xx)) { if (len3) { ckstrncpy(p,bp[2],FNVALL); p += len3; } s += len2; /* and skip past it. */ } else { /* No, */ *p++ = *s++; /* just copy this character */ } } *p = NUL; while ((*p++ = *s++)); if (occur < 0) { /* cheap... */ occur = xx + occur + 1; xx = 0; p = fnval; j = j2; if (occur > 0) goto ragain; } } } p = fnval; goto fnend; case FN_CHR: /* \fcharacter(arg1) */ val1 = *(bp[0]) ? evalx(bp[0]) : ""; if (chknum(val1)) { /* Must be numeric */ i = atoi(val1); if (i >= 0 && i < 256) { /* Must be an 8-bit value */ p = fnval; *p++ = (char) i; *p = NUL; p = fnval; } else { failed = 1; if (fndiags) ckmakmsg(fnval,FNVALL, "",NULL); } } else { failed = 1; evalerr(fn); } goto fnend; case FN_COD: /* \fcode(char) */ if ((int)strlen(bp[0]) > 0) { p = fnval; i = *bp[0]; sprintf(p,"%d",(i & 0xff)); /* SAFE */ } else p = "0"; /* Can't happen */ goto fnend; case FN_LEN: /* \flength(arg1) */ if (argn > 0) { p = fnval; sprintf(p,"%d",(int)strlen(bp[0])); /* SAFE */ } else p = "0"; goto fnend; case FN_LOW: /* \flower(arg1) */ s = bp[0] ? bp[0] : ""; p = fnval; while (*s) { if (isupper(*s)) *p = (char) tolower(*s); else *p = *s; p++; s++; } *p = NUL; p = fnval; goto fnend; case FN_MAX: /* \fmax(arg1,arg2) */ case FN_MIN: /* \fmin(arg1,arg2) */ case FN_MOD: /* \fmod(arg1,arg2) */ val1 = *(bp[0]) ? evalx(bp[0]) : ""; #ifdef COMMENT /* No longer necessary because evalx() no longer overwrites its */ /* result every time it's called (2000/09/23). */ free(bp[0]); bp[0] = NULL; #endif /* COMMENT */ if (argn > 1) { #ifdef COMMENT /* Ditto... */ bp[0] = malloc((int)strlen(val1)+1); if (bp[0]) strcpy(bp[0],val1); /* safe */ val1 = bp[0]; #endif /* COMMENT */ val2 = *(bp[1]) ? evalx(bp[1]) : ""; if (chknum(val1) && chknum(val2)) { i = atoi(val1); j = atoi(val2); switch (y) { case FN_MAX: if (j < i) j = i; break; case FN_MIN: if (j > i) j = i; break; case FN_MOD: if (j == 0) { failed = 1; if (fndiags) ckmakmsg(fnval,FNVALL, "",NULL); else fnval[0] = NUL; goto fnend; } else j = i % j; } p = fnval; sprintf(p,"%d",j); /* SAFE */ } else { failed = 1; evalerr(fn); } } else p = val1; goto fnend; } /* Break up big switch... */ switch (y) { case FN_SUB: /* \fsubstr(arg1,arg2,arg3) */ case FN_RIG: /* \fright(arg1,arg2) */ case FN_LEF: /* \fleft(arg1,arg2) */ if (argn < 1) goto fnend; val1 = ""; if (argn > 1) if (*(bp[1])) val1 = evalx(bp[1]); #ifdef COMMENT if (bp[1]) free(bp[1]); /* Have to copy this */ bp[1] = malloc((int)strlen(val1)+1); if (!bp[1]) { failed = 1; if (fndiags) { p = fnval; ckmakmsg(fnval,FNVALL, "",NULL); } goto fnend; } strcpy(bp[1],val1); /* safe */ val1 = bp[1]; #endif /* COMMENT */ val2 = ""; if (argn > 2) if (*(bp[2])) val2 = evalx(bp[2]); if ( ((argn > 1) && (int)strlen(val1) && !rdigits(val1)) || ((cx == FN_SUB) && ((argn > 2) && (int)strlen(val2) && !rdigits(val2))) ) { failed = 1; evalerr(fn); } else { int lx; p = fnval; /* pointer to result */ lx = strlen(bp[0]); /* length of arg1 */ if (cx == FN_SUB) { /* substring */ k = (argn > 2) ? atoi(val2) : MAXARGLEN; /* length */ j = (argn > 1) ? atoi(val1) : 1; /* start pos for substr */ } else if (cx == FN_LEF) { /* left */ k = (argn > 1) ? atoi(val1) : lx; j = 1; } else { /* right */ k = (argn > 1) ? atoi(val1) : lx; /* length */ j = lx - k + 1; /* start pos for right */ if (j < 1) j = 1; } if (k > 0 && j <= lx) { /* if start pos in range */ s = bp[0]+j-1; /* point to source string */ for (i = 0; (i < k) && (*p++ = *s++); i++) ; /* copy */ } *p = NUL; /* terminate the result */ p = fnval; /* and point to it. */ } goto fnend; case FN_UPP: /* \fupper(arg1) */ s = bp[0] ? bp[0] : ""; p = fnval; while (*s) { if (islower(*s)) *p = (char) toupper(*s); else *p = *s; p++; s++; } *p = NUL; p = fnval; goto fnend; case FN_REP: /* \frepeat(text,number) */ if (argn < 1) goto fnend; val1 = "1"; if (argn > 1) if (*(bp[1])) val1 = evalx(bp[1]); if (chknum(val1)) { /* Repeat count */ n = atoi(val1); debug(F111,"SUNDAY frepeat n",val1,n); if (n > 0) { /* Make n copies */ p = fnval; *p = '\0'; k = (int)strlen(bp[0]); /* Make sure string has some length */ debug(F111,"SUNDAY frepeat k","",k); debug(F111,"SUNDAY frepeat FNVALL","",FNVALL); if (k * n >= FNVALL) { /* But not too much... */ failed = 1; if (fndiags) ckmakmsg(fnval,FNVALL, "",NULL); else fnval[0] = NUL; p = fnval; goto fnend; } if (k > 0) { /* If there is something to copy */ for (i = 0; i < n; i++) { /* Copy loop */ s = bp[0]; for (j = 0; j < k; j++) { if ((p - fnval) >= FNVALL) break; /* shouldn't happen... */ else *p++ = *s++; } } *p = NUL; } } } else { failed = 1; evalerr(fn); } p = fnval; goto fnend; #ifndef NOFRILLS case FN_REV: /* \freverse() */ if (argn < 1) goto fnend; yystring(bp[0],&p); goto fnend; #endif /* NOFRILLS */ case FN_RPA: /* \frpad() and \flpad() */ case FN_LPA: if (argn < 1) goto fnend; val1 = ""; if (argn > 1) if (*(bp[1])) val1 = evalx(bp[1]); if (argn == 1) { /* If a number wasn't given */ p = fnval; /* just return the original string */ ckstrncpy(p,bp[0],FNVALL); } else if (argn > 1 && !*val1) { failed = 1; evalerr(fn); } else /* if (chknum(val1)) */ { /* Repeat count */ char pc; n = atoi(val1); if (n >= 0) { p = fnval; k = (int)strlen(bp[0]); /* Length of string to be padded */ if (k >= n) { /* It's already long enough */ ckstrncpy(p,bp[0],FNVALL); } else { if (n + k <= FNVALL) { pc = (char) ((argn < 3) ? SP : *bp[2]); if (!pc) pc = SP; if (cx == FN_RPA) { /* RPAD */ strncpy(p,bp[0],k); /* (leave it like this) */ p[k] = NUL; p += k; for (i = k; i < n; i++) *p++ = pc; } else { /* LPAD */ n -= k; for (i = 0; i < n; i++) *p++ = pc; strncpy(p,bp[0],k); /* (leave it like this) */ p[k] = NUL; p += k; } } *p = NUL; } } } p = fnval; goto fnend; #ifdef ZFCDAT case FN_FD: /* \fdate(filename) */ p = fnval; s = zfcdat(bp[0]); if (!s) s = ""; if (!*s) { failed = 1; if (fndiags) ckmakmsg(fnval,FNVALL,"",NULL); goto fnend; } ckstrncpy(fnval,s,FNVALL); #endif /* ZFCDAT */ goto fnend; } /* Break up big switch... */ switch (y) { case FN_FS: { /* \fsize(filename) */ CK_OFF_T z; p = fnval; z = zchki(bp[0]); if (z < (CK_OFF_T)0) { failed = 1; if (fndiags) { if (z == (CK_OFF_T)-1) ckmakmsg(fnval,FNVALL, "",NULL); else if (z == (CK_OFF_T)-2) ckmakmsg(fnval,FNVALL, "",NULL); else if (z == (CK_OFF_T)-3) ckmakmsg(fnval,FNVALL, "",NULL); else ckmakmsg(fnval,FNVALL, "",NULL); } goto fnend; } ckstrncpy(fnval,ckfstoa(z),FNVALL); goto fnend; } case FN_VER: /* \fverify() */ p = "-1"; if (argn == 1) /* No second arg */ goto fnend; else if (!bp[1]) /* Or second arg null */ goto fnend; else if (!*(bp[1])) /* or empty. */ goto fnend; p = "0"; if (argn > 1) { /* Only works if we have 2 or 3 args */ int start; char *s2, ch1, ch2; start = 0; if (argn > 2) { /* Starting position specified */ val1 = *(bp[2]) ? evalx(bp[2]) : "0"; if (chknum(val1)) { start = atoi(val1) /* - 1 */; if (start < 0) start = 0; if (start > (int)strlen(bp[1])) goto verfin; } else { failed = 1; evalerr(fn); goto fnend; } } i = start; p = "0"; for (s = bp[1] + start; *s; s++,i++) { ch1 = *s; if (!inpcas[cmdlvl]) if (islower(ch1)) ch1 = toupper(ch1); j = 0; for (s2 = bp[0]; *s2; s2++) { ch2 = *s2; if (!inpcas[cmdlvl]) if (islower(ch2)) ch2 = toupper(ch2); if (ch1 == ch2) { j = 1; break; } } if (j == 0) { sprintf(fnval,"%d",i+1); /* SAFE */ p = fnval; break; } } } verfin: goto fnend; case FN_IPA: /* Find and return IP address */ if (argn > 0) { /* in argument string. */ int start = 0; if (argn > 1) { /* Starting position specified */ if (chknum(bp[1])) { start = atoi(bp[1]) - 1; if (start < 0) start = 0; } else { failed = 1; if (fndiags) ckmakmsg(fnval,FNVALL, "",NULL); goto fnend; } } p = getip(bp[0]+start); } else p = ""; goto fnend; #ifdef OS2 case FN_CRY: p = ""; if (argn > 0) { p = fnval; ckstrncpy(p,bp[0],FNVALL); ck_encrypt(p); } goto fnend; case FN_OOX: p = ""; if (argn > 0) p = (char *) ck_oox(bp[0], (argn > 1) ? bp[1] : ""); goto fnend; #endif /* OS2 */ case FN_HEX: /* \fhexify(arg1) */ if (argn < 1) goto fnend; if ((int)strlen(bp[0]) < (FNVALL / 2)) { s = bp[0]; p = fnval; while (*s) { x = (*s >> 4) & 0x0f; *p++ = hexdigits[x]; x = *s++ & 0x0f; *p++ = hexdigits[x]; } *p = NUL; p = fnval; } goto fnend; case FN_UNTAB: /* \funtab(arg1) */ if (argn < 1) goto fnend; if ((int)strlen(bp[0]) < (FNVALL * 2)) { s = bp[0]; p = fnval; if (untabify(bp[0],p,FNVALL) < 0) { failed = 1; if (fndiags) ckmakmsg(fnval,FNVALL, "",NULL); } goto fnend; } case FN_UNH: { /* \funhex(arg1) */ int c[2], i; if (argn < 1) goto fnend; if ((int)strlen(bp[0]) < (FNVALL * 2)) { s = bp[0]; p = fnval; while (*s) { for (i = 0; i < 2; i++) { c[i] = *s++; if (!c[i]) { p = ""; goto unhexfin; } if (islower(c[i])) c[i] = toupper(c[i]); if (c[i] >= '0' && c[i] <= '9') { c[i] -= 0x30; } else if (c[i] >= 'A' && c[i] <= 'F') { c[i] -= 0x37; } else { failed = 1; if (fndiags) ckmakmsg(fnval, FNVALL, "", NULL ); goto fnend; } } *p++ = ((c[0] << 4) & 0xf0) | (c[1] & 0x0f); } *p = NUL; p = fnval; } unhexfin: goto fnend; } case FN_BRK: { /* \fbreak() */ char * c; /* Characters to break on */ char c2, s2; int start = 0; int done = 0; if (argn < 1) goto fnend; if (argn > 2) { s = bp[2] ? bp[2] : "0"; if (chknum(s)) { start = atoi(s); if (start < 0) start = 0; if (start > (int)strlen(bp[0])) goto brkfin; } else { failed = 1; if (fndiags) ckmakmsg(fnval,FNVALL, "",NULL); goto fnend; } } s = bp[0] + start; /* Source pointer */ while (*s && !done) { s2 = *s; if (!inpcas[cmdlvl] && islower(s2)) s2 = toupper(s2); c = bp[1] ? bp[1] : ""; /* Character to break on */ while (*c) { c2 = *c; if (!inpcas[cmdlvl] && islower(c2)) c2 = toupper(c2); if (c2 == s2) { done = 1; break; } c++; } if (done) break; *p++ = *s++; } *p = NUL; /* terminate the result */ p = fnval; /* and point to it. */ brkfin: goto fnend; } case FN_SPN: { /* \fspan() */ char *q; char c1, c2; int start = 0; if (argn < 1) goto fnend; if (argn > 2) { /* Starting position */ s = bp[2] ? bp[2] : "0"; if (chknum(s)) { start = atoi(s); if (start < 0) start = 0; } else { failed = 1; if (fndiags) ckmakmsg(fnval,FNVALL, "",NULL); goto fnend; } } s = bp[0] + start; /* Source pointer */ if (argn > 1 && (int)strlen(bp[1]) > 0 && start <= (int)strlen(bp[0])) { while (*s) { /* Loop thru source string */ q = bp[1]; /* Span string */ c1 = *s; if (!inpcas[cmdlvl]) if (islower(c1)) c1 = toupper(c1); x = 0; while ((c2 = *q++)) { if (!inpcas[cmdlvl]) if (islower(c2)) c2 = toupper(c2); if (c1 == c2) { x = 1; break; } } if (!x) break; *p++ = *s++; } *p = NUL; /* Terminate and return the result */ p = fnval; } goto fnend; } } /* Break up big switch... */ switch (y) { case FN_TRM: /* \ftrim(s1[,s2]) */ case FN_LTR: /* \fltrim(s1[,s2]) */ if (argn < 1) goto fnend; if ((len1 = (int)strlen(bp[0])) > 0) { if (len1 > FNVALL) len1 = FNVALL; s = " \t\r\n"; if (argn > 1) /* Trim list given */ s = bp[1]; len2 = (int)strlen(s); if (len2 < 1) { /* or not... */ s = " \t\r\n"; /* Default is to trim whitespace */ len2 = 2; } if (cx == FN_TRM) { /* Trim from right */ char * q, p2, q2; ckstrncpy(fnval,bp[0],FNVALL); /* Copy string to output */ p = fnval + len1 - 1; /* Point to last character */ while (p >= (char *)fnval) { /* Go backwards */ q = s; /* Point to trim list */ p2 = *p; if (!inpcas[cmdlvl]) if (islower(p2)) p2 = toupper(p2); while (*q) { /* Is this char in trim list? */ q2 = *q; if (!inpcas[cmdlvl]) if (islower(q2)) q2 = toupper(q2); if (p2 == q2) { /* Yes, null it out */ *p = NUL; break; } q++; } if (!*q) /* Trim list exhausted */ break; /* So we're done. */ p--; /* Else keep trimming */ } } else { /* Trim from left */ char * q, p2, q2; p = bp[0]; /* Source */ while (*p) { p2 = *p; if (!inpcas[cmdlvl]) if (islower(p2)) p2 = toupper(p2); q = s; while (*q) { /* Is this char in trim list? */ q2 = *q; if (!inpcas[cmdlvl]) if (islower(q2)) q2 = toupper(q2); if (p2 == q2) { /* Yes, point past it */ p++; /* and try next source character */ break; } q++; /* No, try next trim character */ } if (!*q) /* Trim list exhausted */ break; /* So we're done. */ } ckstrncpy(fnval,p,FNVALL); } p = fnval; } else p = ""; goto fnend; case FN_CAP: /* \fcapitalize(arg1) */ if (argn < 1) goto fnend; s = bp[0]; p = fnval; x = 0; while ((c = *s++)) { if (isalpha(c)) { if (x == 0) { x = 1; if (islower(c)) c = toupper(c); } else if (isupper(c)) c = tolower(c); } *p++ = c; } *p = NUL; p = fnval; goto fnend; #ifdef COMMENT case FN_TOD: /* Time of day to secs since midnite */ sprintf(fnval,"%ld",tod2sec(bp[0])); /* SAFE */ goto fnend; #endif /* COMMENT */ case FN_FFN: /* Full pathname of file */ zfnqfp(bp[0],FNVALL,p); if (!p) p = ""; goto fnend; case FN_CHK: { /* \fchecksum() */ long chk = 0; p = (argn > 0) ? bp[0] : ""; while (*p) chk += *p++; sprintf(fnval,"%lu",chk); /* SAFE */ p = fnval; goto fnend; } #ifndef NOXFER case FN_CRC: /* \fcrc16() */ if (argn > 0) sprintf(fnval,"%u", /* SAFE */ chk3((CHAR *)bp[0],(int)strlen(bp[0]))); else p = "0"; goto fnend; #endif /* NOXFER */ case FN_BSN: /* \fbasename() */ zstrip(bp[0],&p); goto fnend; #ifndef NOLOCAL #ifdef OS2 case FN_SCRN_CX: /* \fscrncurx() */ p = fnval; sprintf(p,"%d",(int)VscrnGetCurPos(VTERM)->x); /* SAFE */ goto fnend; case FN_SCRN_CY: /* \fscrncury() */ p = fnval; sprintf(p,"%d",(int)VscrnGetCurPos(VTERM)->y); /* SAFE */ goto fnend; case FN_SCRN_STR: { /* \fscrnstr() */ videoline * line = NULL; viocell * cells = NULL; int row = 0, col = 0, len = 0; /* NOTE: On Unicode systems, the screen contents are stored in */ /* in Unicode. Therefore, we should really be performing a */ /* conversion to the local character set. */ /* 6/18/2000 - added the translation to lcs */ if (bp[0] == NULL || bp[0][0] == '\0') { row = 0; } else { if (chknum(bp[0])) { row = atoi(bp[0]); if (row < 0) row = 0; } else { failed = 1; if (fndiags) ckmakmsg(fnval,FNVALL, "",NULL); goto fnend; } } line = VscrnGetLineFromTop( VTERM, (USHORT) row ); if (line != NULL) { if (bp[1] == NULL || bp[1][0] == '\0') col = 0; else { if (chknum(bp[0])) { col = atoi(bp[1]); if (col < 0 || col >= line->width) col = 0; } else { failed = 1; if (fndiags) ckmakmsg(fnval,FNVALL, "",NULL); goto fnend; } } if (bp[2] == NULL || bp[2][0] == '\0') { len = line->width - (col+1); } else { if (!chknum(bp[2])) { failed = 1; if (fndiags) ckmakmsg(fnval,FNVALL, "",NULL); goto fnend; } len = atoi(bp[2]); if (len < 0 || len > line->width) len = line->width; } cells = line->cells; for (i = 0; i < len; i++) { int pos = i + col; if (pos < line->width) { if (isunicode()) fnval[i] = (CHAR) utolxlat(cells[pos].c); else fnval[i] = (CHAR) (cells[pos].c & 0xFF); if (fnval[i] == 0) fnval[i] = SP; } else fnval[i] = SP; } fnval[i] = '\0'; } else { fnval[0] = '\0'; } p = fnval; goto fnend; } #endif /* OS2 */ #endif /* NOLOCAL */ #ifndef NOPUSH case FN_RAW: /* \frawcommand() */ case FN_CMD: { /* \fcommand() */ int x, c, n = FNVALL; x = 0; /* Completion flag */ /* ZIFILE can be safely used because we can't possibly be transferring a file while executing this function. */ if (!nopush && zxcmd(ZIFILE,bp[0]) > 0) { /* Open the command */ while (n-- > -1) { /* Read from it */ if ((c = zminchar()) < 0) { x = 1; /* EOF - set completion flag */ if (cx == FN_CMD) { /* If not "rawcommand" */ p--; /* remove trailing newlines */ while (*p == CR || *p == LF) p--; p++; } *p = NUL; /* Terminate the string */ break; } else /* Command still running */ *p++ = c; /* Copy the bytes */ } zclose(ZIFILE); /* Close the command */ } /* Return null string if command's output was too long. */ p = fnval; if (!x) { failed = 1; if (fndiags) ckmakmsg(fnval,FNVALL, "",NULL); } goto fnend; } #endif /* NOPUSH */ } /* Break up big switch... */ switch (y) { case FN_STX: /* \fstripx(string,c) */ if (!(s = bp[0])) /* Make sure there is a string */ goto fnend; c = '.'; /* Character to strip from */ if (argn > 1) if (*bp[1]) c = *bp[1]; n = ckstrncpy(fnval,bp[0],FNVALL); while (--n >= 0) { if (fnval[n] == c) { fnval[n] = NUL; break; } } p = fnval; goto fnend; case FN_STL: /* \flop(string,c) */ case FN_LOPX: { /* \flopx(string,c) */ int n = 1; if (!(s = bp[0])) /* Make sure there is a string */ goto fnend; c = '.'; /* Character to strip to */ if (argn > 1) if (*bp[1]) c = *bp[1]; if (argn > 2) if (*bp[2]) { #ifndef NOFLOAT n = 0; if (isfloat(bp[2],0)) { n = (int)floatval; if (n < 0) n = 0; } else #endif /* NOFLOAT */ n = atoi(bp[2]); } x = 0; if (cx == FN_LOPX) { /* Lopx (from right) */ if (n == 0) goto fnend; s += strlen(s) - 1; /* We already know it's > 0 */ while (s-- >= bp[0]) { if (*s == c) { n--; if (n == 0) { s++; x = 1; break; } } } if (!x) s = ""; } else { /* Lop (from left) */ if (n == 0) { p = bp[0]; goto fnend; } while (*s++) { if (*(s-1) == c) { if (--n == 0) { x = 1; break; } } } if (!x) s = bp[0]; } ckstrncpy(fnval,s,FNVALL); p = fnval; goto fnend; } case FN_STN: /* \fstripn(string,n) */ if (argn < 1) /* Remove n chars from right */ goto fnend; val1 = "0"; if (argn > 1) if (*(bp[1])) val1 = evalx(bp[1]); if (!chknum(val1)) { failed = 1; evalerr(fn); goto fnend; } n = atoi(val1); if (n < 0) n = 0; k = (int)strlen(s = bp[0]) - n; if (k < 0) k = 0; p = fnval; while (k-- > 0) *p++ = *s++; *p = NUL; p = fnval; goto fnend; case FN_STB: { /* \fstripb(string,c) */ char c2 = NUL; int i, k = 0; char * gr_opn = "\"{'([<"; /* Group open brackets */ char * gr_cls = "\"}')]>"; /* Group close brackets */ p = fnval; *p = NUL; if (!(s = bp[0])) /* Make sure there is a string */ goto fnend; if ((x = strlen(s)) < 1) goto fnend; c = NUL; /* Brace/bracket kind */ if (argn > 1) { if (*bp[1]) { if (chknum(bp[1])) { k = atoi(bp[1]); if (k < 0) k = 63; for (i = 0; i < 6; i++) { if (k & (1< 2) if (*bp[2]) c2 = *bp[2]; if (*s == c) { if (!c2) { switch (c) { case '(': c2 = ')'; break; case '[': c2 = ']'; break; case '{': c2 = '}'; break; case '<': c2 = '>'; break; case '"': c2 = '"'; break; case 39: c2 = 39; break; case 96: c2 = 39; break; default: if (argn == 2) { c2 = c; } else { strncpy(fnval,s,x); /* Leave it like this */ fnval[x] = NUL; goto fnend; } } } if (s[x-1] == c2) { strncpy(fnval,s+1,x-2); /* Leave it like this */ fnval[x-2] = NUL; goto fnend; } } strncpy(fnval,s,x); fnval[x] = NUL; goto fnend; } case FN_2HEX: /* Number to hex */ case FN_2OCT: /* Number to octal */ val1 = evalx(bp[0]); if (!*val1) { failed = 1; evalerr(fn); goto fnend; } sprintf(fnval, cx == FN_2HEX ? "%lx" : "%lo", atol(val1)); /* SAFE */ if (cx == FN_2HEX && (int)(strlen(fnval)&1)) sprintf(fnval,"0%lx",atol(val1)); /* SAFE */ p = fnval; goto fnend; case FN_DNAM: { /* Directory part of file name */ char *s; zfnqfp(bp[0],FNVALL,p); /* Get full name */ if (!isdir(p)) { /* Is it already a directory? */ zstrip(p,&s); /* No get basename */ if (*s) { x = ckindex(s,p,0,0,0); /* Pos of latter in former */ if (x > 0) p[x-1] = NUL; } } if (!p) p = ""; goto fnend; } #ifndef NORANDOM case FN_RAND: /* Random number */ #ifdef CK_SSL if (RAND_bytes((unsigned char *)&k,sizeof(k)) < 0) #endif /* CK_SSL */ k = rand(); x = 0; if (argn > 0) { if (!chknum(bp[0])) { failed = 1; if (fndiags) ckmakmsg(fnval,FNVALL, "",NULL); goto fnend; } x = atoi(bp[0]); } #ifdef COMMENT sprintf(fnval,"%d", (x > 0 && k > 0) || (x < 0 && k < 0) ? k % x : (x == 0 ? 0 : (0 - (k % (-x))))); #else debug(F111,"rand",ckitoa(x),k); #ifdef SUNOS4 /* This is really strange but on SunOS, if we are requesting random numbers */ /* between 0 and 4 or less, they always come out in sequence: 0 1 2 3 0 1 2 */ /* Shifting the result of rand() in this case gives a more random result. */ if (x < 5) k = k >> 5; #endif /* SUNOS4 */ if ((x > 0 && k > 0) || (x < 0 && k < 0)) x = k % x; else if (x == 0) x = 0; else x = 0 - (k % (-x)); debug(F101,"rand x","",x); sprintf(fnval,"%d", x); /* SAFE */ #endif /* COMMENT */ p = fnval; goto fnend; #endif /* NORANDOM */ } /* Break up big switch... */ switch (y) { case FN_SPLIT: /* \fsplit(s1,a,s2,s3,mask) */ case FN_WORD: { /* \fword(s1,n,s2,s3,mask) */ int wordnum = 0; int splitting = 0; int x; int array = 0; int grouping = 0; int nocollapse = 0; char * sep = ""; char * notsep = ""; char * bp0 = NULL; char * bp1 = NULL; char abuf[16]; struct stringarray * q = NULL; splitting = (cx == FN_SPLIT); /* Our job */ debug(F101,"FN_SPLIT splitting","",splitting); fnval[0] = splitting ? '0' : NUL; /* Initial return value */ fnval[1] = NUL; p = fnval; bp0 = bp[0]; /* Source string */ if (!bp0) bp0 = ""; debug(F111,"fsplit bp[0]",bp0,argn); if (argn < 1 || !*bp0) /* If none, return default value */ goto fnend; bp1 = bp[1]; /* Function-dependent arg */ if (!bp1) bp1 = ""; /* (array or number) */ debug(F110,"fsplit bp[1]",bp1,0); if (bp[5]) { if (!chknum(bp[5])) { failed = 1; ckmakmsg(fnval,FNVALL, "",NULL); goto fnend; } x = atoi(bp[5]); nocollapse = x; } if (!splitting) { /* \fword(): n = desired word number */ val1 = "1"; /* Default is first word */ if (argn > 1) /* Word number supplied */ if (*bp1) val1 = evalx(bp1); if (!chknum(val1)) { failed = 1; ckmakmsg(fnval,FNVALL, "",NULL); goto fnend; } n = atoi(val1); } else if (argn > 1 && *bp1) { /* \fsplit(): n = word count */ ckstrncpy(abuf,bp1,16); /* Get array reference */ debug(F110,"fsplit abuf 1",abuf,0); failed = 1; /* Assume it's bad */ if (fndiags) /* Default is this error message */ ckmakmsg(fnval,FNVALL, "",NULL); if (abuf[0] != '&') /* "Address" of array */ goto fnend; /* It's bad */ if (abuf[2]) { /* Check for brackets */ if (abuf[2] != '[' || abuf[3] != ']') { goto fnend; /* Bad */ } } debug(F110,"fsplit abuf 2",abuf,0); if (abuf[1] > 64 && abuf[1] < 91) /* Convert upper to lower */ abuf[1] += 32; if (abuf[1] < 97 || abuf[1] > 122) { /* Check for a-z */ goto fnend; } debug(F110,"fsplit abuf 3",abuf,0); array = 1; fnval[0] = NUL; /* No error, erase message */ failed = 0; /* Unset failure flag */ n = 0; /* Initialize word counter */ } if (argn > 2) /* Have break set? */ sep = bp[2]; debug(F111,"fsplit sep",sep,argn); if (argn > 3) /* Have include set? */ notsep = bp[3]; debug(F111,"fsplit notsep",notsep,argn); if (argn > 4) { /* Have grouping set? */ char * bp4 = bp[4]; debug(F111,"fsplit bp4",bp4,argn); if (!bp4) bp4 = "0"; if (!*bp4) bp4 = "0"; if (chknum(bp4)) { grouping = atoi(bp4); if (grouping == -1) grouping = 127; } else { failed = 1; if (fndiags) ckmakmsg(fnval,FNVALL, "",NULL); goto fnend; } } /* Args parsed, now do the work */ debug(F111,"fsplit bp0",bp0,n); q = cksplit(splitting,n,bp0,sep,notsep,grouping,0,nocollapse); wordnum = q ? q->a_size : -1; /* Check result */ if (wordnum < 0) { failed = 1; /* Failure */ if (fndiags) ckmakmsg(fnval,FNVALL, (wordnum == -1) ? "", NULL ); goto fnend; } if (splitting) { /* \fsplit() result */ ckstrncpy(fnval,ckitoa(wordnum),FNVALL); if (array) { /* Array was not declared. */ int i; if ((x = dclarray(abuf[1],wordnum)) < 0) { /* Declare it. */ failed = 1; if (fndiags) ckmakmsg(fnval,FNVALL, "",NULL); goto fnend; } for (i = 1; i <= wordnum; i++) { /* Copy results */ makestr(&(a_ptr[x][i]),q->a_head[i]); } a_ptr[x][0] = NULL; /* Array is 1-based */ makestr(&(a_ptr[x][0]),fnval); /* Element = size */ } } else { /* \fword() result */ char * s; s = q->a_head[1]; if (!s) s = ""; ckstrncpy(fnval,s,FNVALL); } goto fnend; /* Done */ } } /* Break up big switch... */ switch (y) { #ifdef CK_KERBEROS case FN_KRB_TK: /* Kerberos tickets */ case FN_KRB_NX: /* Kerberos next ticket */ case FN_KRB_IV: /* Kerberos ticket is valid */ case FN_KRB_FG: /* Kerberos Ticket flags */ case FN_KRB_TT: { /* Kerberos ticket time */ int kv = 0; /* Kerberos version */ int n = 0; char * s = NULL; if (rdigits(bp[0])) { kv = atoi(bp[0]); } else { failed = 1; if (fndiags) ckmakmsg(fnval,FNVALL, "",NULL); goto fnend; } if (kv != 4 && kv != 5) { failed = 1; if (fndiags) ckmakmsg(fnval,FNVALL, "",NULL); goto fnend; } if ((cx == FN_KRB_IV || cx == FN_KRB_TT || cx == FN_KRB_FG) && argn < 2) { failed = 1; if (fndiags) ckmakmsg(fnval,FNVALL,"",NULL); goto fnend; } switch (y) { case FN_KRB_TK: /* Number of Kerberos tickets */ #ifdef CK_AUTHENTICATION switch (kv) { case 4: n = ck_krb4_get_tkts(); sprintf(fnval, "%d", (n >= 0) ? n : 0); /* SAFE */ goto fnend; case 5: { extern char * krb5_d_cc; n = ck_krb5_get_tkts(krb5_d_cc); sprintf(fnval, "%d", (n >= 0) ? n : 0); /* SAFE */ goto fnend; } } #else sprintf(fnval,"%d",0); /* SAFE */ #endif /* CK_AUTHENTICATION */ goto fnend; case FN_KRB_NX: /* Kerberos next ticket */ #ifdef CK_AUTHENTICATION switch (kv) { case 4: s = ck_krb4_get_next_tkt(); ckstrncpy(fnval, s ? s : "",FNVALL); goto fnend; case 5: s = ck_krb5_get_next_tkt(); ckstrncpy(fnval, s ? s : "",FNVALL); goto fnend; } #else sprintf(fnval,"k%d next-ticket-string",kv); /* SAFE */ #endif /* CK_AUTHENTICATION */ goto fnend; case FN_KRB_IV: /* Kerberos ticket is valid */ #ifdef CK_AUTHENTICATION /* Return 1 if valid, 0 if not */ switch (kv) { case 4: n = ck_krb4_tkt_isvalid(bp[1]); sprintf(fnval, "%d", n > 0 ? 1 : 0); /* SAVE */ goto fnend; case 5: { extern char * krb5_d_cc; n = ck_krb5_tkt_isvalid(krb5_d_cc,bp[1]); sprintf(fnval,"%d", n > 0 ? 1 : 0); /* SAFE */ goto fnend; } } #else sprintf(fnval,"%d",0); /* SAFE */ #endif /* CK_AUTHENTICATION */ goto fnend; case FN_KRB_TT: /* Kerberos ticket time */ #ifdef CK_AUTHENTICATION switch (kv) { case 4: n = ck_krb4_tkt_time(bp[1]); sprintf(fnval,"%d", n >= 0 ? n : 0); /* SAFE */ goto fnend; case 5: { extern char * krb5_d_cc; n = ck_krb5_tkt_time(krb5_d_cc,bp[1]); sprintf(fnval,"%d", n >= 0 ? n : 0); /* SAFE */ goto fnend; } } #else ckstrncpy(fnval,"600",FNVALL); /* Some time */ #endif /* CK_AUTHENTICATION */ goto fnend; case FN_KRB_FG: /* Kerberos ticket flags */ #ifdef CK_AUTHENTICATION switch (kv) { case 4: fnval[0] = '\0'; goto fnend; case 5: { extern char * krb5_d_cc; ckstrncpy(fnval,ck_krb5_tkt_flags(krb5_d_cc,bp[1]),FNVALL); goto fnend; } } #else fnval[0] = '\0'; #endif /* CK_AUTHENTICATION */ goto fnend; } p = fnval; goto fnend; } #endif /* CK_KERBEROS */ #ifdef FN_ERRMSG case FN_ERRMSG: if (rdigits(bp[0])) { k = atoi(bp[0]); } else { failed = 1; if (fndiags) ckmakmsg(fnval,FNVALL,"",NULL); goto fnend; } #ifdef VMS ckstrncpy(fnval,ckvmserrstr(k),FNVALL); #else x = errno; errno = k; ckstrncpy(fnval,ck_errstr(),FNVALL); errno = x; #endif /* VMS */ p = fnval; goto fnend; #endif /* FN_ERRMSG */ case FN_DIM: { int max; char abuf[16], *s; fnval[0] = NUL; /* Initial return value */ ckstrncpy(abuf,bp[0],16); /* Get array reference */ s = abuf; if (*s == CMDQ) s++; failed = 1; /* Assume it's bad */ p = fnval; /* Point to result */ if (fndiags) /* Default is this error message */ ckmakmsg(fnval,FNVALL,"",NULL); if (s[0] != '&') { /* "Address" of array */ goto fnend; } if (s[2]) { if (s[2] != '[' || s[3] != ']') { goto fnend; } } if (s[1] >= 64 && s[1] < 91) /* Convert upper to lower */ s[1] += 32; if (s[1] < 95 || s[1] > 122) { /* Check for a-z */ goto fnend; /* Bad */ } if ((max = chkarray(s[1],1)) < 1) /* (second arg was 1) */ max = 0; failed = 0; /* Unset failure flag */ sprintf(fnval,"%d",max); /* SAFE */ goto fnend; } } /* Break up big switch... */ switch (y) { case FN_JDATE: if (argn < 1) /* Check number of args */ p = ckdate(); /* None, get today's date-time */ else /* Some */ p = bp[0]; /* Use first */ p = ckcvtdate(p,0); /* Convert to standard form */ ckstrncpy(fnval,zjdate(p),FNVALL); /* Convert to Julian */ p = fnval; /* Point to result */ failed = 0; if (*p == '-') { failed = 1; if (fndiags) /* Default is this error message */ ckmakmsg(fnval,FNVALL,"",NULL); } goto fnend; case FN_DATEJ: ckstrncpy(fnval,jzdate(bp[0]),FNVALL); /* Convert to yyyy */ p = fnval; /* Point to result */ failed = 0; if (*p == '-') { failed = 1; if (fndiags) /* Default is this error message */ ckmakmsg(fnval,FNVALL,"",NULL); } goto fnend; case FN_DTIM: /* \fcvtdate() */ case FN_TIME: /* Free-format time to hh:mm:ss */ case FN_NTIM: /* Time to sec since midnight */ s = (argn > 0) ? bp[0] : ""; if (!s) s = ""; if (!*s) p = ckdate(); /* None, get today's date */ else /* Some */ p = bp[0]; /* Use first */ { char * s; s = p; while (*s) { if (*s < 32) { *s = NUL; break; } s++; } /* do { if (*s < '!') *s = NUL; break; } while (*s++); */ } p = ckcvtdate(p,2); /* Convert to standard form */ if (*p == '<') { failed = 1; if (fndiags) /* Default is this error message */ ckmakmsg(fnval,FNVALL, "",NULL); p = fnval; goto fnend; } if (argn > 1) { /* Format code */ s = bp[1]; if (!s) s = ""; if (!*s) s = "0"; if (!chknum(s)) { failed = 1; if (fndiags) ckmakmsg(fnval,FNVALL, "",NULL); p = fnval; goto fnend; } x = atoi(s); /* if (x) */ p = shuffledate(p,x); } if (cx == FN_TIME) { p += 9; } else if (cx == FN_NTIM) { long sec = 0L; p[11] = NUL; p[14] = NUL; sec = atol(p+9) * 3600L + atol(p+12) * 60L + atol(p+15); sprintf(fnval,"%ld",sec); /* SAFE */ p = fnval; } goto fnend; case FN_MJD: /* Modified Julian Date */ if (argn < 1) /* Check number of args */ p = zzndate(); /* None, get today's date-time */ else /* Some */ p = bp[0]; /* Use first */ p = ckcvtdate(p,0); /* Convert to standard form */ if (*p == '-') { failed = 1; if (fndiags) /* Default is this error message */ ckmakmsg(fnval,FNVALL,"",NULL); goto fnend; } /* Convert to modified Julian date */ sprintf(fnval,"%ld",mjd(p)); /* SAFE */ p = fnval; /* Point to result */ goto fnend; case FN_MJD2: { long k = 0L; int n = 0; p = evalx(bp[0]); if (*p == '-') { p++; n = 1; } if (!rdigits(p)) { failed = 1; evalerr(fn); p = fnval; goto fnend; } else { k = atol(p); if (n) k = -k; } ckstrncpy(fnval,mjd2date(k),FNVALL); /* Convert to Date */ p = fnval; /* Point to result */ failed = 0; goto fnend; } #ifndef NODIAL case FN_PNCVT: { /* Convert phone number */ extern char * pncvt(); failed = 0; p = pncvt(bp[0]); if (!p) p = ""; if (!*p) { failed = 1; if (fndiags) /* Default is this error message */ ckmakmsg(fnval,FNVALL, "",NULL); } goto fnend; } #endif /* NODIAL */ case FN_DAY: case FN_NDAY: if (argn < 1) /* Check number of args */ p = zzndate(); /* None, get today's date-time */ else /* Some */ p = bp[0]; /* Use first */ p = ckcvtdate(p,0); /* Convert to standard form */ if (*p == '-') { failed = 1; if (fndiags) /* Default is this error message */ ckmakmsg(fnval,FNVALL,"",NULL); goto fnend; } failed = 0; z = mjd(p); /* Convert to modified Julian date */ z = z % 7L; if (z < 0) { z = 0 - z; k = 6 - ((int)z + 3) % 7; } else { k = ((int)z + 3) % 7; /* Day of week */ } p = fnval; /* Point to result */ if (cx == FN_NDAY) sprintf(fnval,"%d",k); /* SAFE */ else ckstrncpy(fnval,wkdays[k],FNVALL); goto fnend; case FN_N2TIM: { /* Sec since midnight to hh:mm:ss */ long k = 0L; int n = 0, hh, mm, ss; char * s = bp[0]; if (argn < 1) /* If no arg substitute 0 */ s = "0"; p = evalx(s); /* Evaluate expression silently */ if (*p == '-') { /* Check result for minus sign */ p++; n = 1; } if (!rdigits(p)) { /* Check for numeric */ failed = 1; ckmakmsg(fnval,FNVALL, "",NULL); p = fnval; goto fnend; } else { k = atol(p); if (n) k = -k; } if (k < 0) { /* Check for negative */ failed = 1; if (fndiags) ckmakmsg(fnval,FNVALL, "",NULL); p = fnval; goto fnend; } hh = k / 3600L; /* Have positive number */ mm = (k % 3600L) / 60L; /* break it down... */ ss = ((k % 3600L) % 60L); sprintf(fnval,"%02d:%02d:%02d",hh,mm,ss); /* SAFE */ p = fnval; failed = 0; goto fnend; } case FN_PERM: { /* File permissions */ p = fnval; z = zchki(bp[0]); if (z < 0) { failed = 1; if (fndiags) { if (z == -1) ckmakmsg(fnval,FNVALL, "",NULL); else if (z == -2) ckmakmsg(fnval,FNVALL, "",NULL); else if (z == -3) ckmakmsg(fnval,FNVALL, "",NULL); else ckmakmsg(fnval,FNVALL, "",NULL); } goto fnend; } #ifdef CK_PERMS ckstrncpy(fnval,ziperm(bp[0]),FNVALL); #else ckstrncpy(fnval,"(unknown)",FNVALL); #endif /* CK_PERMS */ goto fnend; } case FN_TLOOK: /* tablelook() */ case FN_ALOOK: { /* arraylook() */ int i, x, hi, lo, max, cmdlen; char abuf[16], *s, *pat; char kwbuf[256]; char delim = ':'; failed = 1; /* Assume failure */ ckstrncpy(fnval,"-1",FNVALL); pat = bp[0]; /* Point to search pattern */ if (!pat) pat = ""; /* Watch out for NULL pointer */ cmdlen = strlen(pat); /* Get pattern length */ if (argn < 2 /* || cmdlen < 1 */ ) { /* Need two args */ if (fndiags) ckmakmsg(fnval,FNVALL,"",NULL); goto fnend; } ckstrncpy(abuf,bp[1],16); /* Get array reference */ if (argn > 2) delim = *(bp[2]); s = abuf; if ((x = arraybounds(s,&lo,&hi)) < 0) { /* Get index and bounds */ if (fndiags) ckmakmsg(fnval,FNVALL,"",NULL); goto fnend; } p = fnval; /* Point to result */ max = a_dim[x]; /* Size of array */ if (lo < 0) lo = 0; /* Use given range if any */ if (lo > max) lo = max; if (hi < 0) hi = max; if (hi > max) hi = max; failed = 0; /* Unset failure flag */ if (max < 1) goto fnend; kwbuf[255] = NUL; for (i = lo; i <= hi; i++) { if (!a_ptr[x][i]) continue; if (cx == FN_ALOOK) { if (ckmatch(pat,a_ptr[x][i],inpcas[cmdlvl],1+4)) { sprintf(fnval,"%d",i); /* SAFE */ goto fnend; } } else if (cx == FN_TLOOK) { char * aa; int j = 0, v = 0, len; if (i == hi) break; aa = a_ptr[x][i]; /* Point to this array element */ if (!aa) aa = ""; while (j < 254 && *aa) { /* Isolate keyword */ if (*aa == delim) break; kwbuf[j++] = *aa++; } kwbuf[j] = NUL; len = j; v = 0; if ((len == cmdlen && !ckstrcmp(kwbuf,pat,len,0)) || ((v = !ckstrcmp(kwbuf,pat,cmdlen,0)) && ckstrcmp(a_ptr[x][i+1],pat,cmdlen,0))) { sprintf(fnval,"%d",i); /* SAFE */ goto fnend; } if (v) { /* Ambiguous */ ckstrncpy(fnval,"-2",FNVALL); goto fnend; } } } if (cx == FN_TLOOK) { /* tablelook() last element */ ckstrncpy(fnval,"-1",FNVALL); if (!ckstrcmp(a_ptr[x][hi],pat,cmdlen,0)) sprintf(fnval,"%d",hi); /* SAFE */ } goto fnend; } case FN_TOB64: /* Base-64 conversion */ case FN_FMB64: p = fnval; *p = NUL; if (argn < 1) goto fnend; if (cx == FN_TOB64) { x = b8tob64(bp[0],-1,fnval,FNVALL); } else { x = strlen(bp[0]); if (x % 4) { /* length must be multiple of 4 */ failed = 1; ckmakmsg(fnval,FNVALL, "",NULL); goto fnend; } b64tob8(NULL,0,NULL,0); /* Reset */ x = b64tob8(bp[0],-1,fnval,FNVALL); b64tob8(NULL,0,NULL,0); /* Reset again */ } if (x < 0) { failed = 1; if (fndiags) { char * m = "INTERNAL_ERROR"; switch (x) { case -1: m = "ARG_TOO_LONG"; break; case -2: m = "ARG_OUT_OF_RANGE"; break; } if (ckmakmsg(fnval,FNVALL," 0) ckstrncat(fnval,"()>",FNVALL); } } goto fnend; case FN_ABS: { char * s; s = bp[0]; if (*s == '-' || *s == '+') s++; if (!rdigits(s)) { if (fndiags) ckmakmsg(fnval,FNVALL, "",NULL); goto fnend; } ckstrncpy(fnval,s,FNVALL); goto fnend; } case FN_AADUMP: { char abuf[16], *s = NULL, **ap = NULL, **vp = NULL; char pattern[VNAML]; int slen, i, j, k, first = -1; extern int xdelmac(); p = fnval; if (argn < 2) { if (fndiags) ckmakmsg(fnval,FNVALL,"",NULL); goto fnend; } debug(F101,"aaconvert argn","",argn); s = bp[0]; slen = strlen(s); /* Count elements so we can create the array */ ckmakmsg(pattern,VNAML,s,"<*>",NULL,NULL); for (k = 0, i = 0; i < nmac; i++) { if (ckmatch(pattern,mactab[i].kwd,0,1)) { if (first < 0) /* Remember location of first match */ first = i; k++; } } debug(F101,"aaconvert matches","",k); debug(F101,"aaconvert first","",first); fnval[0] = NUL; /* Initial return value */ ckstrncpy(abuf,bp[1],16); /* Get array reference */ s = abuf; if (*s == CMDQ) s++; p = fnval; /* Point to result */ if (fndiags) /* Default is this error message */ ckmakmsg(fnval,FNVALL,"",NULL); if (s[0] != '&') /* Address of array */ goto fnend; if (s[2]) if (s[2] != '[' || s[3] != ']') goto fnend; if (s[1] >= 64 && s[1] < 91) /* Convert upper to lower */ s[1] += 32; if ((x = dclarray(s[1],k)) < 0) /* Declare array to size */ goto fnend; ap = a_ptr[x]; /* Point to array we just declared */ /* debug(F111,"aaconvert array 1",abuf,ap); */ abuf[0] = NUL; if (argn > 2) { ckstrncpy(abuf,bp[2],16); /* Get value array reference */ s = abuf; if (*s == CMDQ) s++; if (s[0] != '&') /* Address of array */ goto fnend; if (s[2]) if (s[2] != '[' || s[3] != ']') goto fnend; if (s[1] >= 64 && s[1] < 91) /* Convert upper to lower */ s[1] += 32; if ((x = dclarray(s[1],k)) < 0) goto fnend; vp = a_ptr[x]; /* Point to array we just declared */ } /* debug(F111,"aaconvert array 2",abuf,vp); */ makestr(&ap[0],ckitoa(k)); if (vp) makestr(&vp[0],ckitoa(k)); if (fndiags) ckmakmsg(fnval,FNVALL,"",NULL); /* Copy macro index & value to the arrays and then remove the */ /* macro, so the 'first' pointer keeps indicating the next one. */ /* We could combine the initial counting loop with this one but */ /* then it would be harder to create the array and anyway this */ /* function is plenty fast as it is. */ for (i = 1; i <= k; ) { if (!ckmatch(pattern,mactab[first].kwd,0,1)) { debug(F111,"aaconvert oddball",mactab[first].kwd,first); first++; continue; } ckstrncpy(tmpbuf,mactab[first].kwd,TMPBUFSIZ); /* Macro name */ s = tmpbuf; /* Make writeable copy */ s += slen; /* Isolate "index" */ j = strlen(s) - 1; if (*s != '<' || *(s+j) != '>') { /* Check syntax */ /* This shouldn't happen */ debug(F111,"aaconvert ERROR",mactab[first].kwd,first); goto fnend; } *(s+j) = NUL; /* Remove final '>' */ debug(F111,"aaconvert",s+1,i); makestr(&(ap[i]),s+1); /* Set first array to index */ if (vp) makestr(&(vp[i]),mactab[first].mval); /* 2nd to value */ if (xdelmac(first) < 0) goto fnend; i++; } sprintf(fnval,"%d",k); /* SAFE */ p = fnval; /* Return size of array */ debug(F110,"aaconvert return",p,0); failed = 0; /* Unset failure flag */ goto fnend; } } /* End of switch() */ #ifdef FNFLOAT /* Floating-point functions. To be included only if FNFLOAT is defined, which should happen only if CKFLOAT is also defined, and if the math library is linked in. Even then, we might have float-vs-double confusion as well as confusion about what the final "%f" format effector is supposed to reference (32 bits, 64 bits, etc). Expect trouble if CKFLOAT does not match the data type of math library functions or args. */ if (cx == FN_FPABS || /* Floating-point functions */ cx == FN_FPADD || cx == FN_FPDIV || cx == FN_FPEXP || cx == FN_FPLOG || cx == FN_FPLN || cx == FN_FPMOD || cx == FN_FPMAX || cx == FN_FPMIN || cx == FN_FPMUL || cx == FN_FPPOW || cx == FN_FPSQR || cx == FN_FPINT || cx == FN_FPSUB || cx == FN_FPROU || cx == FN_FPSIN || cx == FN_FPCOS || cx == FN_FPTAN) { CKFLOAT farg[2], fpresult = 0.0; char fpbuf[64], * bp0; double dummy; /* int sign = 0; */ int i, j, places = 0; int argcount = 1; failed = 1; p = fnval; bp0 = bp[0]; if (!bp0) bp0 = "0"; else if (!*bp0) bp0 = "0"; if (!isfloat(bp0,0)) { k = mxlook(mactab,bp0,nmac); bp0 = (k > -1) ? mactab[k].mval : NULL; if (bp0) { if (!isfloat(bp0,0)) { if (fndiags) ckmakmsg(fnval,FNVALL, "",NULL); goto fnend; } } } if (cx == FN_FPINT) { /* Float to int */ failed = 0; ckstrncpy(fnval,bp0,FNVALL); for (i = 0; fnval[i]; i++) { if (fnval[i] == '.') { fnval[i] = NUL; break; } } goto fnend; } switch (y) { /* These need 2 args */ case FN_FPADD: case FN_FPDIV: case FN_FPMOD: case FN_FPMAX: case FN_FPMIN: case FN_FPMUL: case FN_FPPOW: case FN_FPSUB: argcount = 2; } /* Missing arguments are supplied as 0.0 */ debug(F111,fn,"argcount",argcount); for (i = 0; i < argcount; i++) { /* Get floating-point args */ #ifdef DEBUG if (deblog) { ckmakmsg(fpbuf, 64, "bp[", ckitoa(i), bp[i] ? bp[i] : "(null)", "]" ); debug(F100,fpbuf,"",0); } #endif /* DEBUG */ if (!bp[i]) { farg[i] = 0.0; } else if (!*(bp[i])) { farg[i] = 0.0; } else if (!isfloat(bp[i],0)) { char * tmp; k = mxlook(mactab,bp[i],nmac); tmp = (k > -1) ? mactab[k].mval : NULL; if (tmp) { if (!isfloat(tmp,0)) { if (fndiags) ckmakmsg(fnval,FNVALL, "",NULL); goto fnend; } } } farg[i] = floatval; #ifdef DEBUG if (deblog) { sprintf(fpbuf,"farg[%d]=%f",i,farg[i]); /* SAFE */ debug(F100,fpbuf,"",0); } #endif /* DEBUG */ } if (bp[argcount]) { /* Get decimal places */ char * s; s = bp[argcount]; if (!s) s = ""; if (!*s) s = "0"; s = evalx(s); if (!s) s = ""; if (!*s) { evalerr(fn); goto fnend; } places = atoi(s); } errno = 0; failed = 0; switch (y) { /* Now do the requested function */ case FN_FPABS: /* Floating-point absolute value */ #ifndef COMMENT fpresult = fabs(farg[0]); #else if (farg[0] < 0.0) fpresult = 0.0 - farg[0]; #endif /* COMMENT */ break; case FN_FPADD: /* FP add */ fpresult = farg[0] + farg[1]; break; case FN_FPDIV: /* FP divide */ case FN_FPMOD: /* FP modulus */ if (!farg[1]) { failed = 1; if (fndiags) ckmakmsg(fnval,FNVALL, "",NULL); } else fpresult = (cx == FN_FPDIV) ? (farg[0] / farg[1]) : fmod(farg[0],farg[1]); break; case FN_FPEXP: /* FP e to the x */ fpresult = (CKFLOAT) exp(farg[0]); break; case FN_FPLOG: /* FP base-10 logarithm */ case FN_FPLN: /* FP natural logarithm */ if (farg[0] < 0.0) { failed = 1; if (fndiags) ckmakmsg(fnval,FNVALL, "",NULL); } else fpresult = (cx == FN_FPLOG) ? log10(farg[0]) : log(farg[0]); break; case FN_FPMUL: /* FP multiply */ fpresult = farg[0] * farg[1]; break; case FN_FPPOW: /* FP raise to a power */ fpresult = modf(farg[1],&dummy); if ((!farg[0] && farg[1] <= 0.0) || (farg[0] < 0.0 && fpresult)) { failed = 1; if (fndiags) ckmakmsg(fnval,FNVALL, "",NULL); } else fpresult = pow(farg[0],farg[1]); break; case FN_FPSQR: /* FP square root */ if (farg[0] < 0.0) { failed = 1; if (fndiags) ckmakmsg(fnval,FNVALL, "",NULL); } else fpresult = sqrt(farg[0]); break; case FN_FPSUB: /* FP subtract */ fpresult = farg[0] - farg[1]; break; case FN_FPROU: /* FP round */ fpresult = farg[0]; break; case FN_FPSIN: /* FP sine */ fpresult = (CKFLOAT) sin(farg[0]); break; case FN_FPCOS: /* FP cosine */ fpresult = (CKFLOAT) cos(farg[0]); break; case FN_FPTAN: /* FP tangent */ fpresult = (CKFLOAT) tan(farg[0]); break; case FN_FPMAX: fpresult = (farg[0] > farg[1]) ? farg[0] : farg[1]; break; case FN_FPMIN: fpresult = (farg[0] < farg[1]) ? farg[0] : farg[1]; break; } /* Get here with fpresult = function result */ if (errno) { /* If range or domain error */ failed = 1; if (fndiags) ckmakmsg(fnval,FNVALL, "",NULL); } if (failed) /* and/or any other kind of error, */ goto fnend; /* fail. */ #ifndef COMMENT /* Call routine containing code that was formerly inline */ ckstrncpy(fnval,fpformat(fpresult,places,cx == FN_FPROU),FNVALL); #else { char fbuf[16]; /* For creating printf format */ if (!fp_rounding && /* If printf doesn't round, */ (places > 0 || /* round result to decimal places. */ (places == 0 && cx == FN_FPROU))) fpresult += (0.5 / pow(10.0,(CKFLOAT)places)); if (places > 0) { /* If places specified */ /* use specified places to write given number of digits */ sprintf(fbuf,"%%0.%df",places); /* SAFE */ sprintf(fnval,fbuf,fpresult); /* SAFE */ } else { /* Otherwise... */ #ifdef COMMENT /* Here we want to print exactly fp_digits significant digits, no matter which side of the decimal point they are on. That is, we want want the default format to show the maximum number of non-garbage digits, AND we want the last such digit to be rounded. Of course there is no way to do that, since the digit after the last non-garbage digit is, well, garbage. So the following clever ruse does no good. */ int sign = 0, m = 0; sprintf(fnval,"%f",fpresult); if (fnval[0] == '-') sign = 1; for (i = sign; i < FNVALL; i++) { if (isdigit(fnval[i])) m++; else break; } if (m > 1) { int d = fp_digits - m; if (d < 1) d = 1; sprintf(fbuf,"%%%d.%df",fp_digits+sign+1,d); } else { sprintf(fbuf,"%%0.%df",fp_digits); } sprintf(fnval,fbuf,fpresult); #else /* Go for max precision */ sprintf(fbuf,"%%0.%df",fp_digits); /* SAFE */ sprintf(fnval,fbuf,fpresult); /* SAFE */ #endif /* COMMENT */ } if (fnval[0] == '-') sign = 1; } debug(F111,"fpresult 1",fnval,errno); /* Check for over/underflow */ for (i = sign; fnval[i]; i++) { /* Give requested decimal places */ if (fnval[i] == '.') /* First find the decimal point */ break; else if (i > fp_digits + sign - 1) /* replacing garbage */ fnval[i] = '0'; /* digits with 0... */ } if (fnval[i] == '.') { /* Have decimal point */ int gotend = 0; /* d < 0 so truncate fraction */ if (places < 0 || (places == 0 && cx == FN_FPROU)) { fnval[i] = NUL; } else if (places > 0) { /* d > 0 so this many decimal places */ i++; /* First digit after decimal */ for (j = 0; j < places; j++) { /* Truncate after d decimal */ if (!fnval[j+i]) /* places or extend to d */ gotend = 1; /* decimal places. */ if (gotend || j+i+sign > fp_digits) fnval[j+i] = '0'; } fnval[j+i] = NUL; } else { /* d == 0 so Do The Right Thing */ for (j = (int)strlen(fnval) - 1; j > i+1; j--) { if ((j - sign) > fp_digits) fnval[j] = '0'; if (fnval[j] == '0') fnval[j] = NUL; /* Strip useless trailing 0's. */ else break; } } } #endif /* COMMENT */ debug(F111,"fpresult 2",fnval,errno); goto fnend; } #endif /* FNFLOAT */ #ifdef CKCHANNELIO if (cx == FN_FSTAT || /* File functions */ cx == FN_FPOS || cx == FN_FEOF || cx == FN_FGCHAR || cx == FN_FGLINE || cx == FN_FGBLK || cx == FN_FPCHAR || cx == FN_FPLINE || cx == FN_FPBLK || cx == FN_NLINE || cx == FN_FERMSG || cx == FN_FILNO) { int x = 0, t = 0, channel; long z; extern int z_maxchan; failed = 1; /* Assume failure */ p = fnval; /* until we validate args */ if (cx == FN_FERMSG) { extern int z_error; if (argn < 1) { x = z_error; } else if (chknum(bp[0])) { x = atoi(bp[0]); } else if (fndiags) ckmakmsg(fnval,FNVALL, "",NULL); failed = 0; ckstrncpy(fnval,ckferror(x),FNVALL); goto fnend; } if (argn < 1) { /* All file functions need channel */ if (cx == FN_FSTAT) { /* Except f_status(), e.g. when */ fnval[0] = '0'; /* called with a variable that */ fnval[1] = NUL; /* hasn't been defined yet. */ failed = 0; } else { if (fndiags) ckmakmsg(fnval,FNVALL,"",NULL); } goto fnend; } if (rdigits(bp[0])) { /* Channel must be numeric */ channel = atoi(bp[0]); } else { /* Fail if it isn't */ if (fndiags) ckmakmsg(fnval,FNVALL, "",NULL); goto fnend; } if (channel < 0 || channel > z_maxchan) { /* Check channel range */ if (fndiags) ckmakmsg(fnval,FNVALL, "",NULL); goto fnend; } x = z_getmode(channel); /* Find out about the channel */ failed = 0; /* Assume success from here down */ if (cx == FN_FSTAT) { /* Status / modes of channel */ if (x > -1) x &= FM_RWB; /* Mask out irrelevant bits */ else /* In this case not open is OK */ x = 0; /* 0 if not open, 1-7 if open */ sprintf(fnval,"%d",x); /* SAFE */ goto fnend; } else if (x < 1) { /* Not \f_status() so must be open */ failed = 1; if (fndiags) ckmakmsg(fnval,FNVALL,"",NULL); goto fnend; } switch (y) { /* Do the requested function */ case FN_FPOS: /* Get position */ z = z_getpos(channel); /* FIX THIS */ sprintf(fnval,"%ld",z); /* SAFE */ goto fnend; case FN_NLINE: /* Get line number */ z = z_getline(channel); /* FIX THIS */ sprintf(fnval,"%ld",z); /* SAFE */ goto fnend; case FN_FEOF: /* Check EOF */ t = 0; if (x & FM_EOF) t = 1; sprintf(fnval,"%d",t); /* SAFE */ goto fnend; case FN_FILNO: /* Get file handle */ x = z_getfnum(channel); sprintf(fnval,"%d",x); /* SAFE */ goto fnend; case FN_FPBLK: /* Read or write block */ case FN_FGBLK: if (argn < 2) { if (fndiags) ckmakmsg(fnval,FNVALL, "",NULL); goto fnend; } if (rdigits(bp[1])) { t = atoi(bp[1]); } else { if (fndiags) ckmakmsg(fnval,FNVALL, "",NULL); goto fnend; } case FN_FGCHAR: /* Read or write character or line */ case FN_FPCHAR: case FN_FGLINE: case FN_FPLINE: fnval[0] = NUL; switch (y) { case FN_FGCHAR: t = z_in(channel,fnval,FNVALL,1,1); break; case FN_FGLINE: t = z_in(channel,fnval,FNVALL,FNVALL-1,0); break; case FN_FGBLK: if (t >= FNVALL) t = FNVALL - 1; t = z_in(channel,fnval,FNVALL,t,1); break; case FN_FPCHAR: t = z_out(channel,bp[1],1,1); break; case FN_FPLINE: t = z_out(channel,bp[1],-1,0); break; case FN_FPBLK: t = z_out(channel,bp[1],-1,1); break; } if (t < 0) { /* Handle read/write error */ failed = 1; if (fndiags && t != FX_EOF) ckmakmsg(fnval,FNVALL, "",NULL); goto fnend; } if (cx == FN_FGCHAR) /* Null terminate char */ fnval[1] = NUL; /* Write (put) functions return numeric status code */ if (cx == FN_FPCHAR || cx == FN_FPLINE || cx == FN_FPBLK) sprintf(fnval,"%d",t); /* SAFE */ goto fnend; } } #endif /* CKCHANNELIO */ if (cx == FN_SQUEEZE) { /* String function \fsqueeze() */ /* Squeeze out whitespace */ /* Add options later for whether to trim leading and trailing blanks */ /* and what to do about control characters, 8-bit whitespace, etc */ int started = 0; /* Flag for first non-whitespace */ int n = 0; /* Blank/Tab counter */ s = bp[0] ? bp[0] : ""; p = fnval; /* Result buffer */ while (*s) { /* While there is input */ if (!started && (*s == ' ' || *s == '\011')) { s++; /* Skip past leading whitespace */ continue; } started++; /* Leading whitespace was skipped */ if (*s != ' ' && *s != '\011') { /* Have a nonspace char */ n = 0; /* reset space counter */ *p++ = *s++; /* copy char to destination */ continue; } if (n++ > 0) { /* Have blank or tab */ s++; /* don't copy more than one */ continue; } *p++ = ' '; /* Deposit one space */ s++; /* and go to next source char */ } if (*(p-1) == ' ') p--; /* Remove trailing space */ *p = NUL; /* Terminate string */ p = fnval; /* point to beginning */ goto fnend; /* Done. */ } if (cx == FN_PATTERN) { /* \fpattern() for INPUT */ itsapattern = 1; if (argn > 0) { p = fnval; ckstrncpy(fnval,bp[0],FNVALL); } else p = ""; goto fnend; } if (cx == FN_HEX2N || cx == FN_OCT2N) { /* \fhex2n(), \foct2n() */ p = "0"; if (argn < 1) goto fnend; p = ckradix(bp[0], ((cx == FN_HEX2N) ? 16 : 8), 10); if (!p) { if (fndiags) ckmakmsg(fnval,FNVALL, "",NULL); goto fnend; } failed = 0; ckstrncpy(fnval,p,FNVALL); p = fnval; goto fnend; } if (cx == FN_HEX2IP) { int c[2], ip[4], i, k; p = "0"; if (argn < 1) goto fnend; s = bp[0]; if ((int)strlen(s) != 8) { failed = 1; if (fndiags) ckmakmsg(fnval,FNVALL, "",NULL); goto fnend; } p = fnval; for (k = 0; k < 8; k += 2) { for (i = 0; i < 2; i++) { c[i] = *s++; if (islower(c[i])) c[i] = toupper(c[i]); if (c[i] >= '0' && c[i] <= '9') { c[i] -= 0x30; } else if (c[i] >= 'A' && c[i] <= 'F') { c[i] -= 0x37; } else { failed = 1; if (fndiags) ckmakmsg(fnval,FNVALL, "",NULL); goto fnend; } ip[k/2] = c[0] << 4 | c[1]; } sprintf(p,"%d.%d.%d.%d",ip[0],ip[1],ip[2],ip[3]); /* SAFE */ } goto fnend; } if (cx == FN_IP2HEX) { int ip[4], i; char * q; p = "00000000"; if (argn < 1) goto fnend; s = bp[0]; p = fnval; for (i = 0; i < 3; i++) { q = ckstrchr(s,'.'); if (q) { *q++ = NUL; ip[i] = atoi(s); s = q; } else { failed = 1; if (fndiags) ckmakmsg(fnval,FNVALL, "",NULL); goto fnend; } } ip[3] = atoi(s); sprintf(p,"%02x%02x%02x%02x",ip[0],ip[1],ip[2],ip[3]); /* SAFE */ goto fnend; } if (cx == FN_RADIX) { failed = 1; p = fnval; if (argn < 3) { if (fndiags) ckmakmsg(fnval,FNVALL,"",NULL); goto fnend; } if (!rdigits(bp[1]) || !rdigits(bp[2])) { if (fndiags) ckmakmsg(fnval,FNVALL, "",NULL); goto fnend; } p = ckradix(bp[0],atoi(bp[1]),atoi(bp[2])); if (!p) { if (fndiags) ckmakmsg(fnval,FNVALL, "",NULL); goto fnend; } failed = 0; ckstrncpy(fnval,p,FNVALL); p = fnval; goto fnend; } if (cx == FN_JOIN) { int i, x, y, z, flag, flag2, hi, lo, max, seplen, grouping = 0; char abuf[16], c, *s, *q, *sep = NULL; char * gr_opn = "\"{'([<"; /* Group open brackets */ char * gr_cls = "\"}')]>"; /* Group close brackets */ char lb[2], rb[2]; /* Selected left and right brackets */ int csv = 0, tsv = 0; /* Function flags */ char specialchar = 0; /* Field char that triggers grouping */ char *s2 = NULL; /* Address of malloc'd storage */ failed = 1; /* Assume failure */ fnval[0] = NUL; debug(F101,"FNJOIN ARGN","",argn); ckstrncpy(abuf,bp[0],16); /* Get array reference */ s = abuf; if ((x = arraybounds(s,&lo,&hi)) < 0) { /* Get index and bounds */ if (fndiags) ckmakmsg(fnval,FNVALL,"",NULL); goto fnend; } p = fnval; /* Point to result */ max = a_dim[x]; /* Size of array */ if (lo < 0) lo = 1; /* Use given range if any */ if (lo > max) lo = max; #ifdef COMMENT hi = max; #else /* This is a workaround for the problem in which the dimension of the \&_[] array (but not its contents) grows upon entry to a SWITCH block. But this code prevents the dimension from growing. Go figure. */ if (hi < 0) { /* Bounds not given */ if (x) /* Regular array */ hi = max; else /* Argument vector array */ for (hi = max; hi >= lo; hi--) { /* ignore any trailing */ if (!a_ptr[x][hi]) continue; /* empty elements */ if (!*(a_ptr[x][hi])) continue; break; } } #endif /* COMMENT */ if (hi > max) hi = max; failed = 0; /* Unset failure flag */ if (max < 1) goto fnend; sep = " "; /* Separator */ lb[0] = NUL; /* Group start char (as string) */ rb[0] = NUL; lb[1] = NUL; /* Group end char as string */ rb[1] = NUL; if (argn > 1) { if (bp[1]) if (*bp[1]) { /* If arg1 given and not empty */ if (!strcmp(bp[1],"CSV")) { /* Special "CSV" symbolic arg */ csv++; /* Make a comma separated list */ sep = ","; /* Comma */ specialchar = *sep; /* Separator is special character */ grouping = 1; /* Group with doublequotes */ lb[0] = '"'; /* and here */ rb[0] = '"'; /* they are */ } else if (!strcmp(bp[1],"TSV")) { /* "TSV" symbolic arg */ tsv++; /* Make a Tab separated list */ sep = "\011"; /* Tab */ specialchar = *sep; grouping = 0; /* No grouping */ } else /* Normal case */ sep = bp[1]; /* use the separator char specified */ } } if (argn > 2 && !csv && !tsv) { /* Grouping? */ char * bp2 = bp[2]; if (!bp2) bp2 = "0"; if (!*bp2) bp2 = "0"; if (chknum(bp2)) { grouping = atoi(bp2); if (grouping < 0 || grouping > 63) grouping = 1; } else { failed = 1; if (fndiags) ckmakmsg(fnval,FNVALL, "",NULL); goto fnend; } if (grouping) { /* Take lowest-order one */ int j, k; /* and set the others to 0 */ for (k = 0; k < 6; k++) { j = 1 << k; if (grouping & j) { lb[0] = gr_opn[k]; rb[0] = gr_cls[k]; break; } } } } if (!csv && !tsv) { /* Normal case, not CSV or TSV */ specialchar = SP; /* Special character is space */ if (argn > 3) /* Nonzero 4th arg for no separator */ if (chknum(bp[3])) if (atoi(bp[3]) > 0) sep = NULL; if (!sep) { sep = ""; seplen = 0; } else seplen = strlen(sep); } for (i = lo; i <= hi; i++) { /* Loop thru selected array elements */ s = a_ptr[x][i]; /* Get next element */ if (!s) s = ""; flag = 0; /* Flag to indicate grouping needed */ flag2 = 0; /* Flag for internal doublequotes */ if (grouping) { /* Does this element need quoting? */ q = s; /* Look for special character */ while ((c = *q++)) { /* If found */ if (c == specialchar) /* grouping is required */ flag++; if (csv && (c == '"')) /* Character that needs doubling */ flag2++; /* in comma-separated list */ if (flag && !csv) /* Exit early if no more to do */ break; } } y = strlen(s); /* Get length of this element */ if ((y > 0) && csv && !flag) { /* CSV item needs grouping */ if (s[0] == SP || s[y-1] == SP || /* if it has leading */ s[0] == HT || s[y-1] == HT) /* or trailing whitespace */ flag++; /* then it needs grouping */ } if (flag || flag2) { /* String needs grouping or quoting */ char *ss = s; q = (char *)malloc(y + flag2 + 3); /* Make new buffer */ if (q) { s2 = q; /* and this is what to free */ if (flag) /* If grouping */ *q++ = lb[0]; /* put opening group quote */ while (*ss) { /* Loop through string */ if (flag2 && (*ss == '"')) /* If CSV and this a '"' */ *q++ = *ss; /* double it. */ *q++ = *ss++; /* Copy the character */ } if (flag) /* If grouping */ *q++ = rb[0]; /* add closing group quote */ *q = NUL; /* terminate the result. */ s = s2; y = strlen(s); } } z = 0; /* Number of chars copied */ flag = 0; /* flag is now buffer-overrun flag */ if (y > 0) /* If this string is not empty */ z = ckstrncat(fnval,s,FNVALL); /* copy it. */ if (s2) free(s2); /* Free temp storage */ if (z < y) /* Now check for buffer overrun. */ flag++; if (!flag && *sep && i < hi) { /* If buffer still has room */ z = ckstrncat(fnval,sep,FNVALL); /* copy delimiter */ if (z < seplen) flag++; } if (flag) { failed = 1; if (fndiags) ckmakmsg(fnval,FNVALL, "",NULL); goto fnend; } } isjoin = 1; goto fnend; } if (cx == FN_SUBST) { /* \fsubstitute() */ CHAR c, * s, * r, * tp[2], buf1[256], buf2[256], buf3[256]; int len, i, j, state = 0, lo = 0, hi = 0; failed = 0; p = fnval; /* Result pointer */ *p = NUL; if (!bp[0]) /* No target, no result*/ goto fnend; len = strlen(bp[0]); /* Length of source */ if (len == 0) goto fnend; if (len > FNVALL) { failed = 1; if (fndiags) ckmakmsg(fnval,FNVALL, "",NULL); goto fnend; } if (!bp[1]) { ckstrncpy(bp[0],fnval,FNVALL); goto fnend; } tp[0] = buf1; /* For s2-s3 interpretation loop */ tp[1] = buf2; for (i = 0; i < 256; i++) { /* Initialize working buffers */ buf1[i] = 0; /* s2 expansion buffer */ buf2[i] = 0; /* s3 expansion buffer */ buf3[i] = i; /* Translation table */ } for (i = 0; i < 2; i++) { /* Interpret s2 and s3 */ s = (CHAR *)bp[i+1]; /* Arg pointer */ if (!s) s = (CHAR *)""; r = tp[i]; /* To construct interpreted arg */ j = 0; /* Output buf pointer */ state = 0; /* Initial state */ while (c = *s++) { /* Loop thru arg chars */ if (j > 255) /* Output buf full */ break; switch (state) { case 0: /* Normal state */ switch (c) { case '\\': /* Have quote */ state = 1; break; case '[': /* Have range starter */ state = 2; break; default: /* Anything else */ r[j++] = c; break; } continue; case 1: /* Quoted char */ r[j++] = c; state = 0; continue; case 2: /* Range bottom */ lo = c; state++; continue; case 3: /* Range separater */ if (c != '-') { failed = 1; if (fndiags) ckmakmsg(fnval,FNVALL, "",NULL); goto fnend; } state++; continue; case 4: /* Range top */ hi = c; state++; continue; case 5: /* Range end */ if (c != ']') { failed = 1; if (fndiags) ckmakmsg(fnval,FNVALL, "",NULL); goto fnend; } for (k = lo; k <= hi && j < 255; k++) /* Fill in */ r[j++] = k; lo = 0; hi = 0; /* Reset */ state = 0; continue; } } } for (i = 0; i < 256 && buf1[i]; i++) { /* Create translation table */ k = (unsigned)buf1[i]; buf3[k] = buf2[i]; } s = (CHAR *)bp[0]; /* Point to source string */ for (i = 0; i < len; i++) { /* Translation loop */ k = (unsigned)s[i]; /* Get next char */ if (!buf3[k]) /* Remove this char */ continue; *p++ = buf3[k]; /* Substitute this char */ } *p = NUL; p = fnval; goto fnend; } #ifndef NOSEXP if (cx == FN_SEXP) { /* \fsexpression(arg1) */ char * p2; fsexpflag++; p = (argn > 0) ? dosexp(bp[0]) : ""; fsexpflag--; p2 = fnval; while ((*p2++ = *p++)) ; p = fnval; goto fnend; } #endif /* NOSEXP */ if (cx == FN_CMDSTK) { /* \fcmdstack(n1,n2) */ int i, j, k; char * s; if (bp[0]) val1 = *(bp[0]) ? evalx(bp[0]) : ckitoa(cmdlvl); else val1 = ckitoa(cmdlvl); #ifdef COMMENT free(bp[0]); /* (evalx() always uses same buffer) */ bp[0] = NULL; /* (not any more!) */ #endif /* COMMENT */ failed = 1; if (argn > 1) { #ifdef COMMENT makestr(&(bp[0]),val1); val1 = bp[0]; #endif /* COMMENT */ val2 = *(bp[1]) ? evalx(bp[1]) : "0"; if (!(chknum(val1) && chknum(val2))) { if (fndiags) ckmakmsg(fnval,FNVALL, "",NULL); goto fnend; } } else { val1 = ckitoa(cmdlvl); val2 = "0"; } i = atoi(val1); /* Level */ j = atoi(val2); /* Flags */ if (i < 0 || i > cmdlvl) { if (fndiags) ckmakmsg(fnval,FNVALL, "",NULL); goto fnend; } failed = 0; p = fnval; k = cmdstk[i].src; /* What (prompt, file, macro) */ if (j) { ckstrncpy(fnval,ckitoa(k),FNVALL); goto fnend; } switch (k) { case CMD_KB: ckstrncpy(fnval,"(prompt)",FNVALL); break; case CMD_TF: s = tfnam[cmdstk[i].lvl]; if (!zfnqfp(s,FNVALL,fnval)) ckstrncpy(fnval,s,FNVALL); break; case CMD_MD: ckstrncpy(fnval,m_arg[cmdstk[i].lvl][0],FNVALL); break; } goto fnend; } #ifdef CKFLOAT if (cx == FN_DIFDATE) { /* \fdiffdates(d1,d2) */ char * d1, * d2; d1 = bp[0] ? bp[0] : ckdate(); d2 = bp[1] ? bp[1] : ckdate(); p = (char *)cmdiffdate(d1,d2); if (!p) { failed = 1; if (fndiags) { ckmakmsg(fnval,FNVALL,"",NULL); p = fnval; } } goto fnend; } #endif /* CKFLOAT */ if (cx == FN_CMPDATE) { /* \fcmddates(d1,d2) */ int x = 0; char d1[18], d2[18], * dp; failed = 0; d1[0] = NUL; d2[0] = NUL; p = fnval; dp = cmcvtdate(bp[0],1); if (dp) { ckstrncpy(d1,dp,18); if ((dp = cmcvtdate(bp[1],1))) { ckstrncpy(d2,dp,18); x = 1; } } if (x == 0) { failed = 1; if (fndiags) ckmakmsg(fnval,FNVALL,"",NULL); } else { x = strcmp(d1,d2); if (x > 0) x = 1; else if (x < 0) x = -1; sprintf(fnval,"%d",x); } goto fnend; } if (cx == FN_TOGMT) { /* \futcdate(d1) */ char * d1, * dp; char datebuf[32]; char d2[32]; p = fnval; failed = 1; if ((dp = cmcvtdate(bp[0],1))) { /* The given date */ ckstrncpy(datebuf,dp,18); ckstrncpy(d2,dp,18); /* local time */ ckstrncat(datebuf,"Z",19); /* Same time GMT */ if ((dp = cmcvtdate(datebuf,1))) /* converted to local time */ ckstrncpy(datebuf,dp,18); if ((p = (char *)cmdiffdate(d2,datebuf))) { /* Get offset */ ckstrncat(d2,p,32); /* Append offset to local time */ if ((dp = cmcvtdate(d2,1))) { failed = 0; ckstrncpy(fnval,dp,FNVALL); p = fnval; } } } if (failed && fndiags) ckmakmsg(fnval,FNVALL,"",NULL); goto fnend; } if (cx == FN_DELSEC) { /* \fdelta2secs(delta-time) */ long secs; p = fnval; if ((x = delta2sec(bp[0],&secs)) < 0) { failed = 1; if (fndiags) ckmakmsg(fnval,FNVALL, (x == -1) ? "", NULL ); goto fnend; } sprintf(p,"%ld",secs); goto fnend; } if (cx == FN_PC_DU) { char c, * s = bp[0]; if (!s) s = ""; p = fnval; while ((c = *s++)) { if (c == ':') { if (*s != '\\') *p++ = '/'; } else if (c == '\\') { *p++ = '/'; } else { *p++ = c; } } *p = NUL; p = fnval; goto fnend; } if (cx == FN_PC_UD) { /* Unix to DOS path */ char c, * s = bp[0]; if (!s) s = ""; if (*s == '~') { /* Skip leading tilde */ s++; if (*s == '/') s++; } p = fnval; while ((c = *s++)) *p ++ = (c == '/') ? '\\' : c; *p = NUL; p = fnval; goto fnend; } if (cx == FN_KWVAL) { /* Keyword=Value */ p = dokwval(bp[0],bp[1]?bp[1]:"="); goto fnend; } #ifdef COMMENT /* Cute idea but doesn't work */ if (cx == FN_SLEEP || cx == FN_MSLEEP) { p = ""; if (chknum(bp[0])) { x = atoi(bp[0]); } else { failed = 1; if (fndiags) { ckmakmsg(fnval,FNVALL, "",NULL); p = fnval; } goto fnend; } if (cx == FN_SLEEP) x *= 1000; msleep(x); goto fnend; } #endif /* COMMENT */ #ifdef NT if (cx == FN_SNAME) { GetShortPathName(bp[0],fnval,FNVALL); goto fnend; } if (cx == FN_LNAME) { ckGetLongPathName(bp[0],fnval,FNVALL); goto fnend; } #endif /* NT */ /* \femailaddress(): Picks the email address out of an RFC 2822 From: or Sender: header. Added 26 Nov 2005. Handles all common, and some uncommon, cases but doesn't totally bother about nested comments. Needed this for fetching email from a POP server and then constructing the BSD "From " line. Works with or without the "From: " or "Sender: " tag. */ if (cx == FN_EMAIL) { char c, * s = bp[0], * s2, * s3, * ap = ""; int k, state = 0, quote = 0, infield = 0; int pc = 0; /* For nested comments */ if (!s) s = ""; if (!*s) goto xemail; if (ckindex("From: ",s,0,0,0) == 1) s += 5; if (ckindex("Sender: ",s,0,0,0) == 1) s += 7; k = strlen(s); /* Strip junk from end */ if (k < 1) goto xemail; k--; while (k >= 0 && s[k] == CR || s[k] == LF) s[k--] = NUL; while (k >= 0 && s[k] == SP || s[k] == HT) s[k--] = NUL; if (k == 0) goto xemail; #ifndef COMMENT /* Simple method if not 100% foolproof */ k = 0; for (s2 = s; *s2; s2++) { /* Find at-sign */ if (*s2 == '@') { k++; /* If more than one use rightmost */ s3 = s2; } } if (k < 1) /* No at-sign */ goto xemail; for (ap = s3-1; ap >= s; ap--) { /* Back up to beginning of address */ if (isspace(*ap) || *ap == '<') { ap++; break; } if (ap == s) break; } for (s2 = s3+1; *s2; s2++) { /* Find end of address */ if (isspace(*s2) || *s2 == '>') break; } *s2-- = NUL; if (*ap == '[' && *s2 == ']') { /* Handle [blah@blah.blah] */ ap++; *s2 = NUL; } if (!ckstrcmp(ap,"mailto:",7,0)) /* Handle mailto: URLs */ ap += 7; #else /* Too complicated and error-prone */ k = 0; for (s2 = s; *s2; s2++) { /* Strip leading whitespace */ if (*s2 == SP || *s2 == HT) { k = 1; break; } } if (!k) { /* Simple address */ ap = s; goto xemail; } do { /* Not simple, have to extract it */ if (quote) { quote = 0; continue; } else if (*s == '\\') { quote = 1; continue; } switch (state) { case 0: if (!infield && *s == '"') { /* Quoted string */ infield = 1; c = '"'; state = 1; } else if (!infield && *s == '(') { /* Comment in parens */ pc++; infield = 1; c = ')'; if (*ap) *s = NUL; state = 1; } else if (!infield && *s == '<') { /* Address */ infield = 1; c = '>'; ap = s+1; state = 2; } else if (infield && (*s == SP || *s == HT)) { infield = 0; } else { /* One or more bare words */ infield = 1; /* Could be an address */ if (!*ap) ap = s; /* Could be comments */ } continue; case 1: /* In Quoted string or Comment */ if (infield && *s == c) { /* Look for end */ infield = 0; *s++ = NUL; while (*s == SP || *s == HT) s++; if (!*ap) ap = s; state = 0; } continue; case 2: /* In address */ if (infield && *s == c) { /* Looking for end */ infield = 0; *s = NUL; break; } } } while (*s++); xemail: if (*ap) { while (*ap == SP || *ap == HT) ap++; } k = strlen(ap) - 1; while (k >= 0 && (ap[k] == SP || ap[k] == HT)) ap[k--] = NUL; if (*ap) { failed = 0; if (*ap == '<') { k = strlen(ap); if (*(ap+k-1) == '>') { ap[k-1] = NUL; ap++; } } } else failed = 1; /* Here we might also want check against "*@*.*" */ #endif /* COMMENt */ xemail: ckstrncpy(fnval,ap,FNVALL); goto fnend; } #ifdef SEEK_CUR /* \fpicture(): Get dimensions of GIF or JPG image. fdc June 2006 */ if (cx == FN_PICTURE) { FILE *fp = NULL; int c, x, w = 0, h = 0, eof = 0; unsigned int i, j, k; unsigned char buf[1024]; char abuf[16], * p, * s; char ** ap = NULL; p = fnval; /* Point to result */ failed = 1; /* Assume failure */ if (argn > 1) { int xi; ckstrncpy(abuf,bp[1],16); /* Get array reference */ s = abuf; if (*s == CMDQ) s++; if (fndiags) /* Default is this error message */ ckmakmsg(fnval,FNVALL, "",NULL); if (s[0] != '&') /* "Address" of array */ goto fnend; if (s[2]) if (s[2] != '[' || s[3] != ']') goto fnend; if (s[1] >= 64 && s[1] < 91) /* Convert upper to lower */ s[1] += 32; if ((xi = dclarray(s[1],2)) < 0) /* Two elements */ goto fnend; ap = a_ptr[xi]; /* Point to array we just declared */ } s = bp[0]; /* Filename */ failed = 0; /* From here on we don't fail */ p[0] = '0'; /* Default return value */ p[1] = NUL; if (!ckmatch("*.{jpg,jpeg,gif}$",s,0,1+4)) /* Appropriate name? */ goto fnend; /* No, fail */ fp = fopen(s, "r"); /* Open it */ if (fp == NULL) { /* Can't, fail */ p[0] = '-'; p[1] = '1'; p[2] = NUL; /* Return -1 */ goto fnend; } k = strlen(s); if (!ckstrcmp(&s[k-4],".gif",4,0)) { /* GIF file */ if (fread(buf,1,10,fp) != 10) { fclose(fp); goto fnend; } /* Check signature */ if (ckstrcmp((char *)buf,"GIF87a",6,0) && ckstrcmp((char *)buf,"GIF89a",6,0)) { fclose(fp); goto fnend; } w = buf[6] + 256 * buf[7]; h = buf[8] + 256 * buf[9]; goto picend; } else if (!ckstrcmp(&s[k-4],".jpg",4,0) || /* JPEG file */ !ckstrcmp(&s[k-5],".jpeg",5,0)) { if (fread(buf,1,2,fp) != 2) { fclose(fp); goto fnend; } if (buf[0] != 0xff || buf[1] != 0xd8) { /* Check signature */ fclose(fp); goto fnend; } eof = 0; while (!eof) { /* Loop for each marker */ while (!eof) { /* Find next marker */ c = getc(fp); if (c == (unsigned int)EOF) { eof++; break; } if (c == 0xff) { buf[0] = c; c = getc(fp); if (c == (unsigned int)EOF) { eof++; break; } buf[1] = c; if (c == 0xd9) eof++; if (c >= 0xc0 && c <= 0xfe) break; } } if (eof) break; x = buf[1]; if (x == 0xc0 || x == 0xc1 || x == 0xc2 || x == 0xc3 || x == 0xc9 || x == 0xca || x == 0xcb) { if (fread(buf,1,7,fp) != 7) { fclose(fp); goto fnend; } h = buf[3] * 256 + buf[4]; w = buf[5] * 256 + buf[6]; goto picend; } else { /* Not a desired field */ if (feof(fp)) { eof++; break; } if (fread(buf,1,2,fp) != 2) { /* Length of this field */ fclose(fp); goto fnend; } j = 256 * buf[0] + buf[1] - 2; /* Skip next field */ if (CKFSEEK(fp,(CK_OFF_T)j,SEEK_CUR) != 0) { fclose(fp); goto fnend; } } } } picend: fclose(fp); if (ap) { makestr(&(ap[0]),"2"); makestr(&(ap[1]),ckitoa(w)); makestr(&(ap[2]),ckitoa(h)); } if (w > 0 && h > 0) { if (w > h) p[0] = '1'; if (h >= w) p[0] = '2'; } goto fnend; } #endif /* SEEK_CUR */ if (cx == FN_PID) { int x = -1; if (chknum(bp[0])) { /* Need numeric argument */ int pid; pid = atoi(bp[0]); /* Convert to int */ #ifdef UNIX if (kill(pid,0) < 0) { /* Test it */ if (errno == #ifdef ESRCH ESRCH /* No such process */ #else 3 #endif /* ESRCH */ ) x = 0; } else /* Process exists */ x = 1; #endif /* UNIX */ } sprintf(fnval,"%d",x); /* SAFE */ goto fnend; } if (cx == FN_FUNC) { char * s = bp[0]; p = "0"; debug(F111,"ffunc",s,argn); if (argn > 0) { int x, y; for (p = s; *p; p++) { /* Chop off trailing parens if any */ if (*p == '(') { *p = NUL; break; } } /* Chop off leading "\\f" or "\f" or "f" */ p = s; if (*p == CMDQ) /* Allow for \\f... */ p++; if (*p == CMDQ && (*(p+1) == 'f' || *(p+1) == 'F')) { /* or \f */ p += 2; } else if (*p == 'f' || *p == 'F') { /* or just f */ p++; } y = lookup(fnctab,p,nfuncs,&x); /* Look up the result */ debug(F111,"ffunc",p,y); p = (y > -1) ? "1" : "0"; } goto fnend; } if (cx == FN_RECURSE) { int t, n; char * s; fnval[0] = NUL; /* Default result is empty string */ s = bp[0]; /* Check for null argument */ if (!s) s = ""; /* or empty argument */ if (!*s) goto fnend; /* in which case return empty string */ n = FNVALL; /* Not empty, max size for result */ s = fnval; /* Location of result */ { /* Force VARIABLE-EVALUATION SIMPLE RECURSIVE */ /* NOTE: This is vulnerable to SIGINT and whatnot... */ int tmp = vareval; /* Save VARIABLE-EVALUATION setting */ vareval = 1; /* Force it to RECURSIVE */ zzstring(bp[0],&s,&n); /* Expand arg into result space */ vareval = tmp; /* Restore VARIABLE-EVALUATION */ } goto fnend; } if (cx == FN_XLATE) { /* f_cvtcset() */ #ifdef NOFRILLS ckstrncpy(fnval,bp[0],FNVALL); #else #ifndef NOUNICODE _PROTOTYP( char * cvtstring, (char *, int, int) ); char * string, * cset1, * cset2; int id1, id2; #endif /* NOUNICODE */ fnval[0] = NUL; #ifdef NOUNICODE ckstrncpy(fnval,bp[0],FNVALL); #else string = bp[0] ? bp[0] : ""; /* String to convert */ if (!*string) goto fnend; /* It's empty */ cset1 = bp[1] ? bp[1] : "ascii"; /* Current charset of string */ cset2 = bp[2] ? bp[2] : "ascii"; /* Charset to convert to */ id1 = lookup(fcstab,cset1,nfilc,NULL); /* Lookup 'from' set */ if (id1 < 0) { failed = 1; ckmakmsg(fnval,FNVALL,"",NULL); goto fnend; } id2 = lookup(fcstab,cset2,nfilc,NULL); /* Lookup 'to' set */ if (id2 < 0) { failed = 1; ckmakmsg(fnval,FNVALL,"",NULL); goto fnend; } string = cvtstring(string,id1,id2); ckstrncpy(fnval,string,FNVALL); #endif /* NOUNICODE */ #endif /* NOFRILLS */ goto fnend; } /* Decode strings containing hex escapes */ if (cx == FN_UNPCT) { /* \fdecodehex() */ char *s1, *s2; char *prefix; /* Can be 1 or 2 chars */ char buf[3]; int n = 0, k; p = fnval; *p = NUL; if (argn < 1) goto fnend; /* Empty string */ s1 = bp[0] ? bp[0] : ""; /* Original string */ prefix = bp[1] ? bp[1] : "%%"; /* Hex byte prefix */ n = (int)strlen(prefix); /* Length of prefix */ if (n < 1 || n > 2) { /* must be 1 or 2 */ ckmakmsg(fnval,FNVALL, "",NULL); goto xunpct; } while (*s1) { if (!ckstrcmp(s1,prefix,n,0)) { /* Case-independent */ if (!*(s1+n)) { ckmakmsg(fnval,FNVALL, "",NULL); goto xunpct; } buf[0] = *(s1+n); /* First hex character */ buf[1] = *(s1+n+1); /* Second hex character */ buf[2] = NUL; if ((k = ckhexbytetoint((char *)buf)) > -1) { *p++ = (char) k; /* Deposit decoded result */ s1 += 2+n; /* and advance the source pointer */ } else { /* Fail on conversion error */ ckmakmsg(fnval,FNVALL, "",NULL); goto xunpct; } } else { /* Not a hex escape sequence */ *p++ = *s1++; /* Just copy the character */ } } *p = NUL; /* Terminate the result string */ failed = 0; /* Say we didn't fail */ p = fnval; /* Set up result pointer */ goto fnend; /* and finish */ xunpct: /* Error exit */ p = fnval; failed = 1; goto fnend; } /* Check a string for encoding family */ if (cx == FN_STRINGT) { /* \fstringtype() */ p = "UNK"; switch (scanstring(bp[0])) { case FT_7BIT: p = "7BIT"; break; case FT_8BIT: p = "8BIT"; break; case FT_UTF8: p = "UTF8"; break; case FT_UCS2: p = "UCS2"; break; case FT_TEXT: p = "TEXT"; break; case FT_BIN: p = "BINARY"; break; } ckstrncpy(fnval,p,FNVALL); p = fnval; goto fnend; } /* String compare s1, s2, [ case ], [ start ] , [ len ] */ if (cx == FN_STRCMP) { int docase = 0; /* Case matters or not */ int start = 0; /* Start of substring */ int len = -1; /* Length of substring to compare */ int x; char * s1, * s2; /* workers */ p = "0"; /* Return value */ if (argn == 0) { /* Two null strings are equal */ ckstrncpy(fnval,p,FNVALL); p = fnval; goto fnend; } if (argn == 1) { /* Non-null string > null string */ p = "1"; ckstrncpy(fnval,p,FNVALL); p = fnval; goto fnend; } if (argn > 2) { s = *(bp[2]) ? evalx(bp[2]) : "0"; /* 0 = caseless */ if (chknum(s)) docase = atoi(s); if (argn > 3) { s = *(bp[3]) ? evalx(bp[3]) : "1"; /* start is 1-based */ if (chknum(s)) start = atoi(s); if (argn > 4) { s = *(bp[4]) ? evalx(bp[4]) : "-1"; /* -1 = whole thing */ if (chknum(s)) len = atoi(s); } } } if (start > 0) start--; /* start is 0-based internally */ s1 = bp[0]; /* Get length of first arg */ x = (int)strlen(s1); if (x > start) /* Point to start position of s1 */ s1 += start; else s1 = ""; s2 = bp[1]; /* Get length of second arg */ x = (int)strlen(s2); if (x > start) /* Point to start position of s2 */ s2 += start; else s2 = ""; x = ckstrcmp(s,s2,len,docase); p = ckitoa(x); ckstrncpy(fnval,p,FNVALL); p = fnval; goto fnend; } /* Note: when adding new functions remember to update dohfunc in ckuus2.c. */ failed = 1; if (fndiags) ckmakmsg(fnval,FNVALL,"",NULL); fnend: /* Free temporary storage for aguments */ for (k = 0; k < argn; k++) if (bp[k]) free(bp[k]); fndepth--; if (failed) { /* Handle failure */ debug(F111,"fnend",fnval,errno); if (!p) p = ""; if (p[0]) { /* In case this wasn't caught above... */ k = strlen(p); if (p[0] != '<' && p[k-1] != '>') { ckmakmsg(fnval,FNVALL,"",NULL); p = fnval; } } else { ckmakmsg(fnval,FNVALL,"",NULL); p = fnval; } if (fnerror) /* SET FUNCTION ERROR ON */ fnsuccess = 0; /* Make command fail (see ckuus5.c) */ debug(F111,"fneval failed",p,fnsuccess); if (fndiags) /* SET FUNCTION DIAGNOSTICS ON */ printf("?%s\n",p); /* Print error message now. */ else return(""); /* Return nothing. */ } return(p); } #endif /* NOSPL */ static char ckpidbuf[32] = "????"; #ifdef VMS _PROTOTYP(long zgpid,(void)); #endif /* VMS */ char * ckgetpid() { /* Return pid as string */ #ifdef CK_PID #ifdef OS2 #define getpid _getpid unsigned long zz; #else long zz; #endif /* OS2 */ #ifdef VMS zz = zgpid(); #else zz = getpid(); #endif /* VMS */ sprintf(ckpidbuf,"%ld",zz); /* SAFE */ #endif /* CK_PID */ return((char *)ckpidbuf); } #ifndef NOSPL #define EMBUFLEN 128 /* Error message buffer length */ static char embuf[EMBUFLEN+1]; char * /* Evaluate builtin variable */ nvlook(s) char *s; { int x, y, cx; long z; char *p; #ifndef NODIAL MDMINF * m; #endif /* NODIAL */ #ifndef NOKVERBS /* Keyboard macro material */ extern int keymac, keymacx; #endif /* NOKVERBS */ #ifdef CK_LOGIN extern int isguest; #endif /* CK_LOGIN */ if (!s) s = ""; x = strlen(s); if (fndiags) { /* FUNCTION DIAGNOSTIC ON */ if (x + 32 < EMBUFLEN) sprintf(embuf,"",s); /* SAFE */ else sprintf(embuf,""); /* SAFE */ } else /* FUNCTION DIAGNOSTIC OFF */ embuf[0] = NUL; x = VVBUFL; p = vvbuf; if (zzstring(s,&p,&x) < 0) { /* e.g. for \v(\%a) */ y = -1; } else { s = vvbuf; y = lookup(vartab,s,nvars,&x); } cx = y; /* y is too generic */ #ifndef NODIAL m = (mdmtyp > 0) ? modemp[mdmtyp] : NULL; /* For \v(m_xxx) variables */ #endif /* NODIAL */ debug(F101,"nvlook y","",y); switch (y) { case VN_ARGC: /* ARGC */ sprintf(vvbuf,"%d",maclvl < 0 ? topargc : macargc[maclvl]); /* SAFE */ return(vvbuf); case VN_ARGS: /* ARGS */ sprintf(vvbuf,"%d",xargs); /* SAFE */ return(vvbuf); case VN_COUN: /* COUNT */ sprintf(vvbuf,"%d",count[cmdlvl]); /* SAFE */ return(vvbuf); case VN_DATE: /* DATE */ ztime(&p); /* Get "asctime" string */ if (p == NULL || *p == NUL) return(NULL); vvbuf[0] = p[8]; /* dd */ vvbuf[1] = p[9]; vvbuf[2] = SP; vvbuf[3] = p[4]; /* mmm */ vvbuf[4] = p[5]; vvbuf[5] = p[6]; vvbuf[6] = SP; for (x = 20; x < 24; x++) /* yyyy */ vvbuf[x - 13] = p[x]; vvbuf[11] = NUL; return(vvbuf); case VN_NDAT: /* Numeric date */ ckstrncpy(vvbuf,zzndate(),VVBUFL); return(vvbuf); case VN_DIRE: /* DIRECTORY */ s = zgtdir(); /* Get current directory */ if (!s) #ifdef UNIXOROSK s = "./"; #else #ifdef VMS s = "[]"; #else s = ""; #endif /* VMS */ #endif /* UNIXOROSK */ ckstrncpy(vvbuf,s,VVBUFL); s = vvbuf; #ifdef UNIXOROSK x = strlen(s); if (x < VVBUFL - 1) { if (s[x-1] != '/') { s[x] = '/'; s[x+1] = NUL; } } #endif /* UNIXOROSK */ return(s); case VN_FILE: /* filespec */ return(fspec); case VN_HOST: /* host name */ if (*myhost) { /* If known */ return(myhost); /* return it. */ } else { /* Otherwise */ ckstrncpy(vvbuf,"unknown",VVBUFL); /* just say "unknown" */ return(vvbuf); } case VN_SYST: /* System type */ #ifdef UNIX ckstrncpy(vvbuf,"UNIX",VVBUFL); #else #ifdef VMS ckstrncpy(vvbuf,"VMS",VVBUFL); #else #ifdef OSK ckstrncpy(vvbuf,"OS9/68K",VVBUFL); #else #ifdef AMIGA ckstrncpy(vvbuf,"Amiga",VVBUFL); #else #ifdef MAC ckstrncpy(vvbuf,"Macintosh",VVBUFL); #else #ifdef OS2 #ifdef NT ckstrncpy(vvbuf,"WIN32",VVBUFL) ; #else /* NT */ ckstrncpy(vvbuf,"OS/2",VVBUFL); #endif /* NT */ #else #ifdef datageneral ckstrncpy(vvbuf,"AOS/VS",VVBUFL); #else #ifdef GEMDOS ckstrncpy(vvbuf,"Atari_ST",VVBUFL); #else #ifdef STRATUS ckstrncpy(vvbuf,"Stratus_VOS",VVBUFL); #else ckstrncpy(vvbuf,"unknown",VVBUFL); #endif /* STRATUS */ #endif /* GEMDOS */ #endif /* datageneral */ #endif /* OS2 */ #endif /* MAC */ #endif /* AMIGA */ #endif /* OSK */ #endif /* VMS */ #endif /* UNIX */ return(vvbuf); case VN_SYSV: /* System herald */ #ifdef IKSD #ifdef CK_LOGIN if (inserver && isguest) return(""); #endif /* CK_LOGIN */ #endif /* IKSD */ for (x = y = 0; x < VVBUFL; x++) { if (ckxsys[x] == SP && y == 0) continue; vvbuf[y++] = (char) ((ckxsys[x] == SP) ? '_' : ckxsys[x]); } vvbuf[y] = NUL; return(vvbuf); } /* Break up long switch statements... */ switch(y) { case VN_TIME: /* TIME. Assumes that ztime returns */ ztime(&p); /* "Thu Feb 8 12:00:00 1990" */ if (p == NULL || *p == NUL) /* like asctime()! */ return(""); for (x = 11; x < 19; x++) /* copy hh:mm:ss */ vvbuf[x - 11] = p[x]; /* to vvbuf */ vvbuf[8] = NUL; /* terminate */ return(vvbuf); /* and return it */ case VN_NTIM: /* Numeric time */ ztime(&p); /* "Thu Feb 8 12:00:00 1990" */ if (p == NULL || *p == NUL) /* like asctime()! */ return(NULL); z = atol(p+11) * 3600L + atol(p+14) * 60L + atol(p+17); sprintf(vvbuf,"%ld",z); /* SAFE */ return(vvbuf); #ifdef CK_TTYFD case VN_TTYF: /* TTY file descriptor */ sprintf(vvbuf,"%d", /* SAFE */ #ifdef VMS vmsttyfd() #else ttyfd #endif /* VMS */ ); return(vvbuf); #endif /* CK_TTYFD */ case VN_VERS: /* Numeric Kermit version number */ sprintf(vvbuf,"%ld",vernum); /* SAFE */ return(vvbuf); case VN_XVNUM: /* Product-specific version number */ sprintf(vvbuf,"%ld",xvernum); /* SAFE */ return(vvbuf); case VN_HOME: /* Home directory */ return(homepath()); case VN_IBUF: /* INPUT buffer */ return((char *)inpbuf); case VN_ICHR: /* INPUT character */ inchar[1] = NUL; return((char *)inchar); case VN_ICNT: /* INPUT character count */ sprintf(vvbuf,"%d",incount); /* SAFE */ return(vvbuf); case VN_SPEE: { /* Transmission SPEED */ long t; t = ttgspd(); if (t < 0L) sprintf(vvbuf,"unknown"); /* SAFE */ else sprintf(vvbuf,"%ld",t); /* SAFE */ return(vvbuf); } case VN_SUCC: /* SUCCESS flag */ /* Note inverted sense */ sprintf(vvbuf,"%d",(success == 0) ? 1 : 0); /* SAFE */ return(vvbuf); case VN_LINE: { /* LINE */ #ifdef DEBUG if (deblog) { debug(F111,"\\v(line) local",ttname,local); debug(F111,"\\v(line) inserver","",inserver); #ifdef TNCODE debug(F111,"\\v(line) tcp_incoming","",tcp_incoming); #endif /* TNCODE */ #ifdef CK_TAPI debug(F111,"\\v(line) tttapi","",tttapi); #endif /* CK_TAPI */ } #endif /* DEBUG */ #ifdef CK_TAPI if (tttapi) { /* If I have made a TAPI connection */ int i; /* return the TAPI device name */ for (i = 0; i < ntapiline; i++) { if (!strcmp(ttname,tapilinetab[i].kwd)) { p = _tapilinetab[i].kwd; return(p); } } } #endif /* CK_TAPI */ #ifndef NOXFER if (inserver /* If I am a TCP server */ #ifdef TNCODE || tcp_incoming #endif /* TNCODE */ ) #ifdef TCPSOCKET p = ckgetpeer(); /* return peer name */ else #endif /* TCPSOCKET */ #endif /* NOXFER */ if (local) /* Otherwise if in local mode */ p = (char *) ttname; /* return SET LINE / SET HOST name */ else /* Otherwise */ p = ""; /* return empty string */ if (!p) /* In case ckgetpeer() returns */ p = ""; /* null pointer... */ debug(F110,"\\v(line) p",p,0); if (!*p) p = (char *) ttname; return(p); } case VN_PROG: /* Program name */ return("C-Kermit"); } /* Break up long switch statements... */ switch(y) { case VN_RET: /* Value of most recent RETURN */ debug(F111,"\\v(return)",mrval[maclvl+1],maclvl+1); p = mrval[maclvl+1]; if (p == NULL) p = ""; return(p); case VN_FFC: /* Size of most recent file */ sprintf(vvbuf, "%s", ckfstoa(ffc)); /* SAFE */ return(vvbuf); case VN_TFC: /* Size of most recent file group */ sprintf(vvbuf, "%s", ckfstoa(tfc)); /* SAFE */ return(vvbuf); case VN_CPU: /* CPU type */ #ifdef IKSD #ifdef CK_LOGIN if (inserver && isguest) return(""); #endif /* CK_LOGIN */ #endif /* IKSD */ #ifdef OS2 { char * getcpu(void) ; return getcpu(); } #else /* OS2 */ #ifdef CKCPU return(CKCPU); /* Traditionally, compile-time value */ #else #ifdef CK_UTSNAME { /* But if none, try runtime value */ extern char unm_mch[]; return((char *)unm_mch); } #else p = getenv("HOSTTYPE"); /* 20091116 */ if (p) if (*p) return(p); return("unknown"); #endif /* CK_UTSNAME */ #endif /* CKCPU */ #endif /* OS2 */ case VN_CMDL: /* Command level */ sprintf(vvbuf, "%d", cmdlvl); /* SAFE */ return(vvbuf); case VN_DAY: /* Day of week */ ztime(&p); if (p != NULL && *p != NUL) /* ztime() succeeded. */ ckstrncpy(vvbuf,p,4); else vvbuf[0] = NUL; /* ztime() failed. */ return(vvbuf); /* Return what we got. */ case VN_NDAY: { /* Numeric day of week */ long k; z = mjd(zzndate()); /* Get modified Julian date */ k = (((int)(z % 7L)) + 3) % 7; /* Get day number */ sprintf(vvbuf,"%ld",k); /* SAFE */ return(vvbuf); } case VN_LCL: /* Local (vs remote) mode */ ckstrncpy(vvbuf, local ? "1" : "0",VVBUFL); return(vvbuf); case VN_CMDS: /* Command source */ if (cmdstk[cmdlvl].src == CMD_KB) ckstrncpy(vvbuf,"prompt",VVBUFL); else if (cmdstk[cmdlvl].src == CMD_MD) ckstrncpy(vvbuf,"macro",VVBUFL); else if (cmdstk[cmdlvl].src == CMD_TF) ckstrncpy(vvbuf,"file",VVBUFL); else ckstrncpy(vvbuf,"unknown",VVBUFL); return(vvbuf); case VN_CMDF: /* Current command file name */ #ifdef COMMENT /* (see comments above) */ if (tfnam[tlevel]) { /* (near dblbs declaration) */ dblbs(tfnam[tlevel],vvbuf,VVBUFL); return(vvbuf); } else return(""); #else if (tlevel < 0) return(""); else return(tfnam[tlevel] ? tfnam[tlevel] : ""); #endif /* COMMENT */ case VN_MAC: /* Current macro name */ return((maclvl > -1) ? m_arg[maclvl][0] : ""); case VN_EXIT: sprintf(vvbuf,"%d",xitsta); /* SAFE */ return(vvbuf); } /* Break up long switch statements... */ switch(y) { case VN_PRTY: { /* Parity */ char *ss; switch (parity) { case 0: ss = "none"; break; case 'e': ss = "even"; break; case 'm': ss = "mark"; break; case 'o': ss = "odd"; break; case 's': ss = "space"; break; default: ss = "unknown"; break; } ckstrncpy(vvbuf,ss,VVBUFL); return(vvbuf); } case VN_DIAL: sprintf(vvbuf,"%d", /* SAFE */ #ifndef NODIAL dialsta #else -1 #endif /* NODIAL */ ); return(vvbuf); #ifndef NODIAL case VN_DMSG: #ifdef BIGBUFOK ckstrncpy(vvbuf,dialmsg[dialsta],VVBUFL); /* Safe if src == NULL */ #endif /* BIGBUFOK */ return((char *)vvbuf); #endif /* NODIAL */ #ifdef OS2 case VN_KEYB: ckstrncpy(vvbuf,conkbg(),VVBUFL); return(vvbuf); case VN_SELCT: { #ifndef NOLOCAL const char * selection = GetSelection(); return( (char *) (selection ? selection : "" )) ; #else return(""); #endif /* NOLOCAL */ } #endif /* OS2 */ #ifndef NOXFER case VN_CPS: sprintf(vvbuf,"%ld",tfcps); /* SAFE */ return(vvbuf); #endif /* NOXFER */ case VN_MODE: /* File transfer mode */ switch (binary) { case XYFT_T: ckstrncpy(vvbuf,"text",VVBUFL); break; case XYFT_B: case XYFT_U: ckstrncpy(vvbuf,"binary",VVBUFL); break; case XYFT_I: ckstrncpy(vvbuf,"image",VVBUFL); break; case XYFT_L: ckstrncpy(vvbuf,"labeled",VVBUFL); break; case XYFT_M: ckstrncpy(vvbuf,"macbinary",VVBUFL); break; default: ckstrncpy(vvbuf,"unknown",VVBUFL); } return(vvbuf); #ifdef CK_REXX case VN_REXX: return(rexxbuf); #endif /* CK_REXX */ case VN_NEWL: /* System newline char or sequence */ #ifdef UNIX ckstrncpy(vvbuf,"\n",VVBUFL); #else #ifdef datageneral ckstrncpy(vvbuf,"\n",VVBUFL); #else #ifdef OSK ckstrncpy(vvbuf,"\15",VVBUFL); /* Remember, these are octal... */ #else #ifdef MAC ckstrncpy(vvbuf,"\15",VVBUFL); #else #ifdef OS2 ckstrncpy(vvbuf,"\15\12",VVBUFL); #else #ifdef STRATUS ckstrncpy(vvbuf,"\n",VVBUFL); #else #ifdef VMS ckstrncpy(vvbuf,"\15\12",VVBUFL); #else #ifdef AMIGA ckstrncpy(vvbuf,"\n",VVBUFL); #else #ifdef GEMDOS ckstrncpy(vvbuf,"\n",VVBUFL); #else ckstrncpy(vvbuf,"\n",VVBUFL); #endif /* GEMDOS */ #endif /* AMIGA */ #endif /* VMS */ #endif /* STRATUS */ #endif /* OS2 */ #endif /* MAC */ #endif /* OSK */ #endif /* datageneral */ #endif /* UNIX */ return(vvbuf); case VN_ROWS: /* ROWS */ case VN_COLS: /* COLS */ ckstrncpy(vvbuf,(cx == VN_ROWS) ? "24" : "80",VVBUFL); /* Default */ #ifdef CK_TTGWSIZ #ifdef OS2 if (tt_cols[VTERM] < 0 || tt_rows[VTERM] < 0) ttgwsiz(); sprintf(vvbuf,"%d", /* SAFE */ (cx == VN_ROWS) ? tt_rows[VTERM] : tt_cols[VTERM]); #else /* OS2 */ if (ttgwsiz() > 0) /* Get window size */ if (tt_cols > 0 && tt_rows > 0) /* sets tt_rows, tt_cols */ sprintf(vvbuf,"%d", /* SAFE */ (cx == VN_ROWS) ? tt_rows : tt_cols); #endif /* OS2 */ #endif /* CK_TTGWSIZ */ return(vvbuf); case VN_TTYP: #ifdef NOTERM ckstrncpy(vvbuf,"unknown",VVBUFL); #else #ifdef OS2 sprintf(vvbuf, "%s", /* SAFE */ (tt_type >= 0 && tt_type <= max_tt) ? tt_info[tt_type].x_name : "unknown" ); #else #ifdef MAC ckstrncpy(vvbuf,"vt320",VVBUFL); #else p = getenv("TERM"); ckstrncpy(vvbuf,p ? p : "unknown",VVBUFL+1); #endif /* MAC */ #endif /* OS2 */ #endif /* NOTERM */ return(vvbuf); case VN_MINP: /* MINPUT */ sprintf(vvbuf, "%d", m_found); /* SAFE */ return(vvbuf); } /* Break up long switch statements... */ switch(y) { case VN_CONN: /* CONNECTION */ if (!local) { ckstrncpy(vvbuf,"remote",VVBUFL); } else { if (!network) ckstrncpy(vvbuf,"serial",VVBUFL); #ifdef TCPSOCKET else if (nettype == NET_TCPB || nettype == NET_TCPA) { if (ttnproto == NP_TELNET) ckstrncpy(vvbuf,"tcp/ip_telnet",VVBUFL); #ifdef CK_SSL else if (ttnproto == NP_SSL || ttnproto == NP_SSL_RAW) ckstrncpy(vvbuf,"tcp/ip_ssl",VVBUFL); else if (ttnproto == NP_TLS || ttnproto == NP_SSL_RAW) ckstrncpy(vvbuf,"tcp/ip_tls",VVBUFL); #endif /* CK_SSL */ else ckstrncpy(vvbuf,"tcp/ip",VVBUFL); } #endif /* TCPSOCKET */ #ifdef SSHBUILTIN else if (nettype == NET_SSH) ckstrncpy(vvbuf,"tcp/ip_ssh",VVBUFL); #endif /* SSHBUILTIN */ #ifdef ANYX25 else if (nettype == NET_SX25 || nettype == NET_VX25 || nettype == NET_IX25 ) ckstrncpy(vvbuf,"x.25",VVBUFL); #endif /* ANYX25 */ #ifdef DECNET else if (nettype == NET_DEC) { if (ttnproto == NP_LAT) ckstrncpy(vvbuf,"decnet_lat",VVBUFL); else if ( ttnproto == NP_CTERM ) ckstrncpy(vvbuf,"decnet_cterm",VVBUFL); else ckstrncpy(vvbuf,"decnet",VVBUFL); } #endif /* DECNET */ #ifdef SUPERLAT else if (nettype == NET_SLAT) ckstrncpy(vvbuf,"superlat",VVBUFL); #endif /* SUPERLAT */ #ifdef NETFILE else if (nettype == NET_FILE) ckstrncpy(vvbuf,"local_file",VVBUFL); #endif /* NETFILE */ #ifdef NETCMD else if (nettype == NET_CMD) ckstrncpy(vvbuf,"pipe",VVBUFL); #endif /* NETCMD */ #ifdef NETPTY else if (nettype == NET_PTY) ckstrncpy(vvbuf,"pseudoterminal",VVBUFL); #endif /* NETPTY */ #ifdef NETDLL else if (nettype == NET_DLL) ckstrncpy(vvbuf,"dynamic_link_library",VVBUFL); #endif /* NETDLL */ #ifdef NPIPE else if (nettype == NET_PIPE) ckstrncpy(vvbuf,"named_pipe",VVBUFL); #endif /* NPIPE */ #ifdef CK_NETBIOS else if (nettype == NET_BIOS) ckstrncpy(vvbuf,"netbios",VVBUFL); #endif /* CK_NETBIOS */ else ckstrncpy(vvbuf,"unknown",VVBUFL); } return(vvbuf); #ifndef NOXFER case VN_SYSI: /* System ID, Kermit code */ return((char *)cksysid); #endif /* NOXFER */ #ifdef OS2 case VN_SPA: { unsigned long space = zdskspace(0); if (space > 0 && space < 1024) sprintf(vvbuf,"-1"); else sprintf(vvbuf,"%lu",space); /* SAFE */ return(vvbuf); } #endif /* OS2 */ #ifndef NOXFER case VN_QUE: { extern char querybuf[]; return(querybuf); } #endif /* NOXFER */ #ifndef NOCSETS case VN_CSET: #ifdef OS2 sprintf(vvbuf,"cp%d",os2getcp()); /* SAFE */ #else ckstrncpy(vvbuf,fcsinfo[fcharset].keyword,VVBUFL+1); #endif /* OS2 */ return(vvbuf); #endif /* NOCSETS */ #ifdef OS2 case VN_EXEDIR: return(exedir); case VN_STAR: return(startupdir); #else case VN_EXEDIR: return(exedir ? exedir : ""); #ifdef VMSORUNIX case VN_STAR: return(startupdir); #endif /* VMSORUNIX */ #endif /* OS2 */ case VN_INI: return(inidir); case VN_MDM: return(gmdmtyp()); case VN_EVAL: return(evalbuf); #ifndef NODIAL case VN_D_CC: /* DIAL COUNTRY-CODE */ return(diallcc ? diallcc : ""); case VN_D_AC: /* DIAL AREA-CODE */ return(diallac ? diallac : ""); case VN_D_IP: /* DIAL INTERNATIONAL-PREFIX */ return(dialixp ? dialixp : ""); case VN_D_LP: /* DIAL LD-PREFIX */ return(dialldp ? dialldp : ""); case VN_D_LCP: /* DIAL LOCAL-PREFIX */ return(diallcp ? diallcp : ""); case VN_D_PXX: /* DIAL PBX-EXCHANGE that matched */ return(matchpxx ? matchpxx : ""); #else case VN_D_CC: /* DIAL COUNTRY-CODE */ case VN_D_AC: /* DIAL AREA-CODE */ case VN_D_IP: /* DIAL INTERNATIONAL-PREFIX */ case VN_D_LP: /* DIAL LD-PREFIX */ case VN_D_LCP: /* DIAL LOCAL-PREFIX */ case VN_D_PXX: /* DIAL PBX-EXCHANGE */ return(""); #endif /* NODIAL */ case VN_UID: #ifdef UNIX { #ifdef IKSD if (inserver) return((char *)uidbuf); else #endif /* IKSD */ if (uidbuf[0]) return((char *)uidbuf); else return(whoami()); } #else return((char *)uidbuf); #endif /* UNIX */ } /* Break up long switch statements... */ switch(y) { case VN_PWD: #ifdef OS2 if (activecmd == XXOUT || activecmd == XXLNOUT) { ckstrncpy(vvbuf,pwbuf,VVBUFL); ck_encrypt((char *)vvbuf); return((char *)vvbuf); } else #endif /* OS2 */ return((char *)pwbuf); case VN_PRM: return((char *)prmbuf); case VN_PROTO: #ifdef NOXFER return("none"); #else #ifdef CK_XYZ return(ptab[protocol].p_name); #else return("kermit"); #endif /* CK_XYZ */ #endif /* NOXFER */ #ifndef NOXFER #ifdef CK_TMPDIR case VN_DLDIR: return(dldir ? dldir : ""); #endif /* CK_TMPDIR */ #endif /* NOXFER */ #ifndef NODIAL case VN_M_INI: /* Modem init string */ return(dialini ? dialini : (m ? m->wake_str : "")); case VN_M_DCM: /* Modem dial command */ return(dialcmd ? dialcmd : (m ? m->dial_str : "")); case VN_M_DCO: /* Modem data compression on */ return(dialdcon ? dialdcon : (m ? m->dc_on_str : "")); case VN_M_DCX: /* Modem data compression off */ return(dialdcoff ? dialdcoff : (m ? m->dc_off_str : "")); case VN_M_ECO: /* Modem error correction on */ return(dialecon ? dialecon : (m ? m->ec_on_str : "")); case VN_M_ECX: /* Modem error correction off */ return(dialecoff ? dialecoff : (m ? m->ec_off_str : "")); case VN_M_AAO: /* Modem autoanswer on */ return(dialaaon ? dialaaon : (m ? m->aa_on_str : "")); case VN_M_AAX: /* Modem autoanswer off */ return(dialaaoff ? dialaaoff : (m ? m->aa_off_str : "")); case VN_M_HUP: /* Modem hangup command */ return(dialhcmd ? dialhcmd : (m ? m->hup_str : "")); case VN_M_HWF: /* Modem hardware flow command */ return(dialhwfc ? dialhwfc : (m ? m->hwfc_str : "")); case VN_M_SWF: /* Modem software flow command */ return(dialswfc ? dialswfc : (m ? m->swfc_str : "")); case VN_M_NFC: /* Modem no flow-control command */ return(dialnofc ? dialnofc : (m ? m->nofc_str : "")); case VN_M_PDM: /* Modem pulse dialing mode */ return(dialpulse ? dialpulse : (m ? m->pulse : "")); case VN_M_TDM: /* Modem tone dialing mode */ return(dialtone ? dialtone : (m ? m->tone : "")); case VN_M_NAM: /* Modem full name */ return(dialname ? dialname : (m ? m->name : "")); #else case VN_M_INI: /* Modem init string */ case VN_M_DCM: /* Modem dial command */ case VN_M_DCO: /* Modem data compression on */ case VN_M_DCX: /* Modem data compression off */ case VN_M_ECO: /* Modem error correction on */ case VN_M_ECX: /* Modem error correction off */ case VN_M_AAO: /* Modem autoanswer on */ case VN_M_AAX: /* Modem autoanswer off */ case VN_M_HUP: /* Modem hangup command */ case VN_M_HWF: /* Modem hardware flow command */ case VN_M_SWF: /* Modem software flow command */ case VN_M_NFC: /* Modem no flow-control command */ case VN_M_PDM: /* Modem pulse dialing mode */ case VN_M_TDM: /* Modem tone dialing mode */ case VN_M_NAM: return(""); #endif /* NODIAL */ case VN_ISTAT: /* INPUT status */ sprintf(vvbuf, "%d", instatus); /* SAFE */ return(vvbuf); case VN_TEMP: /* Temporary directory */ if (tempdir) { p = tempdir; } else { #ifdef OS2 #ifdef NT p = getenv("K95TMP"); #else p = getenv("K2TMP"); #endif /* NT */ if ( !p ) #endif /* OS2 */ p = getenv("CK_TMP"); if (!p) p = getenv("TMPDIR"); if (!p) p = getenv("TEMP"); if (!p) p = getenv("TMP"); #ifdef OS2ORUNIX if (p) { int len = strlen(p); if (p[len-1] != '/' #ifdef OS2 && p[len-1] != '\\' #endif /* OS2 */ ) { static char foo[CKMAXPATH]; ckstrncpy(foo,p,CKMAXPATH); ckstrncat(foo,"/",CKMAXPATH); p = foo; } } else #else /* OS2ORUNIX */ if (!p) #endif /* OS2ORUNIX */ #ifdef UNIX /* Systems that have a standard */ p = "/tmp/"; /* temporary directory... */ #else #ifdef datageneral p = ":TMP:"; #else p = ""; #endif /* datageneral */ #endif /* UNIX */ } ckstrncpy(vvbuf,p,VVBUFL); p = vvbuf; /* This needs generalizing for VOS, AOS/VS, etc... */ while (*p) { #ifdef OS2 if (*p == '\\') *p = '/'; #endif /* OS2 */ p++; } #ifndef VMS if (p > vvbuf) { char c = /* Directory termination character */ #ifdef MAC ':' #else #ifdef datageneral ':' #else #ifdef STRATUS '>' #else '/' #endif /* STRATUS */ #endif /* datageneral */ #endif /* MAC */ ; if (*(p-1) != c) { *p++ = c; *p = NUL; } } #endif /* VMS */ return(vvbuf); } /* Break up long switch statements... */ switch(y) { case VN_ERRNO: /* Error number */ #ifdef VMS { extern int vms_lasterr; sprintf(vvbuf, "%d", vms_lasterr); /* SAFE */ } #else sprintf(vvbuf, "%d", errno); /* SAFE */ #endif /* VMS */ return(vvbuf); case VN_ERSTR: /* Error string */ ckstrncpy(vvbuf,ck_errstr(),VVBUFL); return(vvbuf); #ifndef NOXFER case VN_RPSIZ: /* RECEIVE packet-length */ sprintf(vvbuf,"%d",urpsiz); /* SAFE */ return(vvbuf); case VN_WINDO: /* WINDOW slots */ sprintf(vvbuf,"%d",wslotr); /* SAFE */ return(vvbuf); #endif /* NOXFER */ case VN_TFLN: /* TAKE-file line number */ if (tlevel > -1) { sprintf(vvbuf, "%d", tfline[tlevel]); /* SAFE */ return(vvbuf); } else return("0"); case VN_MDMSG: /* DIALRESULT */ #ifndef NODIAL return((char *)modemmsg); #else return(""); #endif /* NODIAL */ case VN_DNUM: /* DIALNUMBER */ #ifndef NODIAL return(dialnum ? (char *) dialnum : ""); #else return(""); #endif /* NODIAL */ case VN_APC: sprintf(vvbuf, "%d", /* SAFE */ #ifdef CK_APC apcactive #else 0 #endif /* CK_APC */ ); return((char *)vvbuf); #ifdef OS2 #ifndef NOKVERBS case VN_TRMK: sprintf(vvbuf, "%d", keymac); /* SAFE */ return((char *)vvbuf); #endif /* NOKVERBS */ #endif /* OS2 */ case VN_IPADDR: #ifdef TCPSOCKET #ifndef OSK /* This dumps core on OS-9 for some reason, but only if executed */ /* before we have made a TCP connection. This is obviously not */ /* the ideal fix. */ if (!myipaddr[0]) getlocalipaddr(); #endif /* OSK */ #endif /* TCPSOCKET */ ckstrncpy(vvbuf, #ifdef TCPSOCKET (char *)myipaddr, #else "", #endif /* TCPSOCKET */ VVBUFL); return((char *)vvbuf); #ifndef NOXFER case VN_CRC16: /* CRC-16 of most recent transfer */ sprintf(vvbuf,"%d",crc16); /* SAFE */ return(vvbuf); #endif /* NOXFER */ #ifdef CK_PID case VN_PID: #ifdef IKSD #ifdef CK_LOGIN if (inserver && isguest) return(""); #endif /* CK_LOGIN */ #endif /* IKSD */ return(ckgetpid()); #endif /* CK_PID */ #ifndef NOXFER case VN_FNAM: { /* \v(filename) */ extern char filnam[], ofn1[], *sfspec, *rrfspec; char * tmp; switch (what) { /* File transfer is in progress */ #ifdef NEWFTP case (W_FTP|W_RECV): case (W_FTP|W_SEND): return((char *)filnam); #endif /* NEWFTP */ case W_RECV: case W_REMO: return((char *)ofn1); default: /* Most recent file transferred */ if (filnam[0]) { /* (if any) */ return((char *)filnam); } else if (lastxfer & W_SEND && sfspec) { if (fnspath == PATH_OFF) zstrip(sfspec,&tmp); else tmp = sfspec; return(tmp); } else if (lastxfer & W_RECV && rrfspec) { if (fnrpath == PATH_OFF) zstrip(rrfspec,&tmp); else tmp = rrfspec; return(tmp); } else return(""); } } case VN_FNUM: /* \v(filenum) */ sprintf(vvbuf,"%ld",filcnt); /* SAFE */ return((char *)vvbuf); #endif /* NOXFER */ #ifdef PEXITSTAT case VN_PEXIT: { extern int pexitstat; sprintf(vvbuf,"%d",pexitstat); /* SAFE */ return((char *)vvbuf); } #endif /* PEXITSTAT */ #ifndef NOXFER case VN_P_8BIT: vvbuf[0] = parity ? ebq : NUL; vvbuf[1] = NUL; return((char *)vvbuf); case VN_P_CTL: { extern CHAR myctlq; vvbuf[0] = myctlq; vvbuf[1] = NUL; return((char *)vvbuf); } case VN_P_RPT: { extern int rptena; vvbuf[0] = rptena ? rptq : NUL; vvbuf[1] = NUL; return((char *)vvbuf); } #endif /* NOXFER */ #ifdef OS2 case VN_REGN: return(get_reg_name()); case VN_REGO: return(get_reg_corp()); case VN_REGS: return(get_reg_sn()); #endif /* OS2 */ } /* Break up long switch statements... */ switch(y) { case VN_XPROG: #ifdef OS2 #ifdef NT #ifdef KUI return("K-95G"); #else return("K-95"); #endif /* KUI */ #else return("K/2"); #endif /* NT */ #else return("C-Kermit"); #endif /* OS2 */ case VN_EDITOR: #ifdef NOFRILLS return(""); #else #ifdef NOPUSH return(""); #else { extern char editor[]; char *ss; if (!editor[0]) { ss = getenv("EDITOR"); if (ss) { ckstrncpy(editor,ss,CKMAXPATH); } } debug(F110,"\\v(editor)",editor,0); return(editor[0] ? (char *)editor : ""); } #endif /* NOPUSH */ #endif /* NOFRILLS */ case VN_EDOPT: #ifdef NOFRILLS return(""); #else #ifdef NOPUSH return(""); #else { extern char editopts[]; return(editopts[0] ? (char *)editopts : ""); } #endif /* NOPUSH */ #endif /* NOFRILLS */ case VN_EDFILE: #ifdef NOFRILLS return(""); #else #ifdef NOPUSH return(""); #else { extern char editfile[]; return(editfile[0] ? (char *)editfile : ""); } #endif /* NOPUSH */ #endif /* NOFRILLS */ #ifdef BROWSER case VN_BROWSR: { extern char browser[]; if (!browser[0]) { s = getenv("BROWSER"); if (s) ckstrncpy(browser,s,CKMAXPATH); } return(browser[0] ? (char *)browser : ""); } case VN_BROPT: { extern char browsopts[]; return(browsopts[0] ? (char *)browsopts : ""); } case VN_URL: { extern char browsurl[]; return(browsurl[0] ? (char *)browsurl : ""); } #endif /* BROWSER */ case VN_HERALD: return((char *)versio); case VN_TEST: { /* test */ extern char * ck_s_test, * ck_s_tver; if (!ck_s_test) ck_s_test = ""; if (!ck_s_tver) ck_s_tver = ""; if (*ck_s_test) { ckstrncpy(vvbuf,ck_s_test,VVBUFL); if (*ck_s_tver) { ckstrncat(vvbuf,".",VVBUFL); ckstrncat(vvbuf,ck_s_tver,VVBUFL); } } else ckstrncpy(vvbuf,"0",VVBUFL); return((char *)vvbuf); } #ifndef NOXFER case VN_XFSTAT: /* xferstatus */ x = xferstat; /* Like success */ if (x > -1) x = (x == 0) ? 1 : 0; /* External value is reversed */ sprintf(vvbuf,"%d",x); /* SAFE */ return((char *)vvbuf); case VN_XFMSG: /* xfermsg */ return((char *)epktmsg); #ifndef NOMSEND case VN_SNDL: { /* sendlist */ extern int filesinlist; sprintf(vvbuf,"%d",filesinlist); /* SAFE */ return((char *)vvbuf); } #endif /* NOMSEND */ #endif /* NOXFER */ #ifdef CK_TRIGGER case VN_TRIG: { extern char * triggerval; return(triggerval ? triggerval : ""); } #endif /* CK_TRIGGER */ #ifdef OS2MOUSE #ifdef OS2 case VN_MOU_X: { extern int MouseCurX; sprintf(vvbuf,"%d",MouseCurX); /* SAFE */ return((char *)vvbuf); } case VN_MOU_Y: { extern int MouseCurY; sprintf(vvbuf,"%d",MouseCurY); /* SAFE */ return((char *)vvbuf); } #endif /* OS2 */ #endif /* OS2MOUSE */ case VN_PRINT: { extern int printpipe; extern char * printername; #ifdef PRINTSWI extern int noprinter; if (noprinter) return(""); #endif /* PRINTSWI */ ckmakmsg(vvbuf,VVBUFL, printpipe ? "|" : "", printername ? printername : #ifdef OS2 "PRN", #else "(default)", #endif /* OS2 */ NULL, NULL ); return((char *)vvbuf); } } /* Break up long switch statements... */ switch(y) { case VN_ESC: /* Escape character */ sprintf(vvbuf,"%d",escape); /* SAFE */ return((char *)vvbuf); case VN_INTIME: sprintf(vvbuf,"%ld",inetime); /* SAFE */ return((char *)vvbuf); case VN_INTMO: sprintf(vvbuf,"%d",inwait); /* SAFE */ return((char *)vvbuf); case VN_SECURE: if (0 #ifdef SSHBUILTIN || IS_SSH() #endif /* SSHBUILTIN */ #ifdef CK_ENCRYPTION || ck_tn_encrypting() && ck_tn_decrypting() #endif /* CK_ENCRYPTION */ #ifdef CK_SSL || tls_active_flag || ssl_active_flag #endif /* CK_SSL */ ) return("1"); else return("0"); case VN_AUTHN: #ifdef CK_AUTHENTICATION { extern char szUserNameAuthenticated[]; return((char *)szUserNameAuthenticated); } #else /* CK_AUTHENTICATION */ return((char *)""); #endif /* CK_AUTHENTICATION */ case VN_AUTHS: #ifdef CK_AUTHENTICATION switch (ck_tn_auth_valid()) { case AUTH_UNKNOWN: return((char *)"unknown"); case AUTH_OTHER: return((char *)"other"); case AUTH_USER: return((char *)"user"); case AUTH_VALID: return((char *)"valid"); case AUTH_REJECT: default: return((char *)"rejected"); } #else /* CK_AUTHENTICATION */ return((char *)"rejected"); #endif /* CK_AUTHENTICATION */ case VN_AUTHT: #ifdef CK_AUTHENTICATION #ifdef CK_SSL if ((ssl_active_flag || tls_active_flag) && ck_tn_auth_valid() == AUTH_VALID && (sstelnet ? (!TELOPT_U(TELOPT_AUTHENTICATION)) : (!TELOPT_ME(TELOPT_AUTHENTICATION))) || ck_tn_authenticated() == AUTHTYPE_NULL || ck_tn_authenticated() == AUTHTYPE_AUTO) return("X_509_CERTIFICATE"); else #endif /* CK_SSL */ return(AUTHTYPE_NAME(ck_tn_authenticated())); #else /* CK_AUTHENTICATION */ return((char *)"NULL"); #endif /* CK_AUTHENTICATION */ #ifdef CK_KERBEROS case VN_K4PRN: { extern char * krb4_d_principal; if (krb4_d_principal) ckstrncpy(vvbuf,krb4_d_principal,VVBUFL+1); else *vvbuf = NUL; return((char *)vvbuf); } case VN_K5PRN: { extern char * krb5_d_principal; if (krb5_d_principal) ckstrncpy(vvbuf,krb5_d_principal,VVBUFL+1); else *vvbuf = NUL; return((char *)vvbuf); } case VN_K4RLM: { extern char * krb4_d_realm; if (krb4_d_realm) { ckstrncpy(vvbuf,krb4_d_realm,VVBUFL+1); } else { char * s = ck_krb4_getrealm(); ckstrncpy(vvbuf,s ? s : "",VVBUFL+1); } return((char *)vvbuf); } case VN_K4SRV: { extern char * krb4_d_srv; if (krb4_d_srv) ckstrncpy(vvbuf,krb4_d_srv,VVBUFL+1); else ckstrncpy(vvbuf,"rcmd",VVBUFL); return((char *)vvbuf); } case VN_K5RLM: { extern char * krb5_d_realm; extern char * krb5_d_cc; if (krb5_d_realm) { ckstrncpy(vvbuf,krb5_d_realm,VVBUFL+1); } else { char * s = ck_krb5_getrealm(krb5_d_cc); ckstrncpy(vvbuf,s,VVBUFL+1); } return((char *)vvbuf); } case VN_K5CC: { extern char * krb5_d_cc; if (krb5_d_cc) ckstrncpy(vvbuf,krb5_d_cc,VVBUFL+1); else ckstrncpy(vvbuf,ck_krb5_get_cc_name(),VVBUFL+1); return((char *)vvbuf); } case VN_K5SRV: { extern char * krb5_d_srv; if (krb5_d_srv) ckstrncpy(vvbuf,krb5_d_srv,VVBUFL+1); else ckstrncpy(vvbuf,"host",VVBUFL); return((char *)vvbuf); } case VN_K4ENO: { extern int krb4_errno; sprintf(vvbuf,"%d",krb4_errno); /* SAFE */ return((char *)vvbuf); } case VN_K5ENO: { extern int krb5_errno; sprintf(vvbuf,"%d",krb5_errno); /* SAFE */ return((char *)vvbuf); } case VN_K4EMSG: { extern char * krb4_errmsg; ckstrncpy(vvbuf,krb4_errmsg?krb4_errmsg:"",VVBUFL+1); return((char *)vvbuf); } case VN_K5EMSG: { extern char * krb5_errmsg; ckstrncpy(vvbuf,krb5_errmsg,VVBUFL+1); return((char *)vvbuf); } #endif /* CK_KERBEROS */ #ifdef CK_SSL case VN_X509_S: if (ssl_active_flag) ckstrncpy(vvbuf,ssl_get_subject_name(ssl_con),VVBUFL+1); else if (tls_active_flag) ckstrncpy(vvbuf,ssl_get_subject_name(tls_con),VVBUFL+1); else ckstrncpy(vvbuf,"",VVBUFL+1); return((char *)vvbuf); case VN_X509_I: if (ssl_active_flag) ckstrncpy(vvbuf,ssl_get_issuer_name(ssl_con),VVBUFL+1); else if (tls_active_flag) ckstrncpy(vvbuf,ssl_get_issuer_name(tls_con),VVBUFL+1); else ckstrncpy(vvbuf,"",VVBUFL+1); return((char *)vvbuf); #endif /* CK_SSL */ case VN_OSNAM: #ifdef IKSD #ifdef CK_LOGIN if (inserver && isguest) return(""); #endif /* CK_LOGIN */ #endif /* IKSD */ #ifdef CK_UTSNAME { extern char unm_nam[]; return((char *)unm_nam); } #else for (x = y = 0; x < VVBUFL; x++) { if (ckxsys[x] == SP && cx == 0) continue; vvbuf[y++] = (char) ((ckxsys[x] == SP) ? '_' : ckxsys[x]); } vvbuf[y] = NUL; return(vvbuf); #endif /* CK_UTSNAME */ case VN_OSVER: { #ifdef CK_UTSNAME extern char unm_ver[]; #ifdef IKSD #ifdef CK_LOGIN if (inserver && isguest) return(""); #endif /* CK_LOGIN */ #endif /* IKSD */ return((char *)unm_ver); #else return(""); #endif /* CK_UTSNAME */ } case VN_OSREL: { #ifdef CK_UTSNAME extern char unm_rel[]; #ifdef IKSD #ifdef CK_LOGIN if (inserver && isguest) return(""); #endif /* CK_LOGIN */ #endif /* IKSD */ return((char *)unm_rel); #else return(""); #endif /* CK_UTSNAME */ } } /* Break up long switch statements... */ switch(y) { case VN_NAME: { extern char * myname; return(myname); } case VN_MODL: { #ifdef CK_UTSNAME extern char unm_mod[], unm_mch[]; int y = VVBUFL - 1; char * s = unm_mod; #endif /* CK_UTSNAME */ #ifdef IKSD #ifdef CK_LOGIN if (inserver && isguest) return(""); #endif /* CK_LOGIN */ #endif /* IKSD */ #ifdef COMMENT /* was HPUX */ if (!unm_mod[0] && !nopush) zzstring("\\fcommand(model)",&s,&y); /* Another possibility would be: "\\fcommand(ksh -c 'whence model 1>&- && model || uname -m')" But that would depend on having ksh. */ #else #ifdef OSF32 /* Digital UNIX 3.2 and higher... */ /* Note: Ultrix has /etc/sizer, but it is not publicly executable. */ /* sizer -c outputs 'cpu:"DECxxxx"' */ if (!unm_mod[0]) { char * p; int flag = 0; zzstring("\\fcommand(/usr/sbin/sizer -c)",&s,&y); debug(F110,"DU model",unm_mod,0); s = unm_mod; p = unm_mod; while (*p) { /* Extract the part in quotes */ if (*p == '"') { if (flag) break; flag = 1; p++; continue; } if (flag) *s++ = *p; p++; } *s = NUL; } #endif /* OSF32 */ #endif /* COMMENT */ #ifdef CK_UTSNAME if (unm_mod[0]) return((char *)unm_mod); else return((char *)unm_mch); #else return(""); #endif /* CK_UTSNAME */ } #ifdef IBMX25 /* X.25 variables (local and remote address) */ case VN_X25LA: if (!local_nua[0] && !x25local_nua(local_nua)) *vvbuf = NULL; else ckstrncpy(vvbuf,local_nua,VVBUFL+1); return((char *)vvbuf); case VN_X25RA: if (!remote_nua[0]) *vvbuf = NULL; else ckstrncpy(vvbuf,remote_nua,VVBUFL+1); return((char *)vvbuf); #endif /* IBMX25 */ #ifndef NODIAL case VN_PDSFX: { extern char pdsfx[]; return((char *)pdsfx); } case VN_DTYPE: { extern int dialtype; sprintf(vvbuf,"%d",dialtype); /* SAFE */ return((char *)vvbuf); } #endif /* NODIAL */ #ifdef UNIX case VN_LCKPID: { extern char lockpid[]; return((char *)lockpid); } #endif /* UNIX */ #ifndef NOXFER case VN_BLK: sprintf(vvbuf,"%d",bctr); /* SAFE */ return((char *)vvbuf); case VN_TFTIM: sprintf(vvbuf, /* SAFE */ #ifdef GFTIMER "%ld", (long)(fptsecs + 0.5) #else "%d", tsecs #endif /* GFTIMER */ ); return((char *)vvbuf); #endif /* NOXFER */ case VN_HWPAR: case VN_SERIAL: { int sb; char c, * ss; extern int stopbits; vvbuf[0] = NUL; if (hwparity && local && !network) ss = parnam((char)hwparity); else ss = parnam((char)parity); if (cx == VN_HWPAR) { ckstrncpy(vvbuf,ss,VVBUFL); return((char *)vvbuf); } c = ss[0]; if (islower(c)) c = toupper(c); sb = stopbits; if (sb < 1) sb = (speed > 0 && speed <= 110L) ? 2 : 1; if (hwparity) sprintf(vvbuf," 8%c%d",c,sb); /* SAFE */ else if (parity) sprintf(vvbuf," 7%c%d",c,sb); /* SAFE */ else sprintf(vvbuf," 8N%d",sb); /* SAFE */ return((char *)vvbuf); } #ifdef UNIX case VN_LCKDIR: { #ifndef NOUUCP extern char * uucplockdir; ckstrncpy(vvbuf,uucplockdir,VVBUFL); x = strlen(vvbuf); if (x > 0) { if (vvbuf[x-1] != '/') { vvbuf[x] = '/'; vvbuf[x+1] = NUL; } } #else vvbuf[0] = NUL; #endif /* NOUUCP */ return((char *)vvbuf); } #endif /* UNIX */ } /* Break up long switch statements... */ switch(y) { #ifndef NODIAL case VN_DM_LP: case VN_DM_SP: case VN_DM_PD: case VN_DM_TD: case VN_DM_WA: case VN_DM_WD: case VN_DM_HF: case VN_DM_WB: case VN_DM_RC: { extern char * getdm(); ckstrncpy(vvbuf,getdm(y),VVBUFL); return((char *)vvbuf); } #endif /* NODIAL */ case VN_TY_LN: case VN_TY_LC: { extern int typ_lines; sprintf(vvbuf,"%d",typ_lines); /* SAFE */ return((char *)vvbuf); } case VN_TY_LM: { extern int typ_mtchs; sprintf(vvbuf,"%d",typ_mtchs); /* SAFE */ return((char *)vvbuf); } case VN_MACLVL: sprintf(vvbuf,"%d",maclvl); /* SAFE */ return((char *)vvbuf); } /* Break up long switch statements... */ switch(y) { #ifndef NOLASTFILE case VN_LASTFIL: { extern char * lastfile; return(lastfile ? lastfile : ""); } #endif /* NOLASTFILE */ #ifndef NOXFER case VN_XF_BC: sprintf(vvbuf,"%d",crunched); /* SAFE */ return((char *)vvbuf); case VN_XF_TM: sprintf(vvbuf,"%d",timeouts); /* SAFE */ return((char *)vvbuf); case VN_XF_RX: sprintf(vvbuf,"%d",retrans); /* SAFE */ return((char *)vvbuf); #endif /* NOXFER */ case VN_MS_CD: /* Modem signals */ case VN_MS_CTS: case VN_MS_DSR: case VN_MS_DTR: case VN_MS_RI: case VN_MS_RTS: { int x, z = -1; x = ttgmdm(); /* Try to get them */ if (x > -1) { switch (y) { case VN_MS_CD: z = (x & BM_DCD) ? 1 : 0; break; case VN_MS_DSR: z = (x & BM_DSR) ? 1 : 0; break; case VN_MS_CTS: z = (x & BM_CTS) ? 1 : 0; break; #ifdef MAC case VN_MS_DTR: z = (x & BM_DTR) ? 1 : 0; break; #else #ifndef STRATUS case VN_MS_RI: z = (x & BM_RNG) ? 1 : 0; break; #ifndef NT case VN_MS_DTR: z = (x & BM_DTR) ? 1 : 0; break; case VN_MS_RTS: z = (x & BM_RTS) ? 1 : 0; break; #endif /* NT */ #endif /* STRATUS */ #endif /* MAC */ } } sprintf(vvbuf,"%d",z); /* SAFE */ return((char *)vvbuf); } case VN_MATCH: /* INPUT MATCH */ return(inpmatch ? inpmatch : ""); #ifdef CKFLOAT case VN_ISCALE: /* INPUT SCALE-FACTOR */ return(inpscale ? inpscale : "1.0"); #endif /* CKFLOAT */ case VN_SLMSG: { /* SET LINE / HOST message */ extern char * slmsg; vvbuf[0] = NUL; if (slmsg) ckstrncpy(vvbuf,slmsg,VVBUFL); return(vvbuf); } case VN_TXTDIR: /* TEXTDIR */ return(k_info_dir ? k_info_dir : ""); #ifdef FNFLOAT case VN_MA_PI: return(math_pi); case VN_MA_E: return(math_e); case VN_MA_PR: sprintf(vvbuf,"%d",fp_digits); /* SAFE */ return(vvbuf); #endif /* FNFLOAT */ case VN_CMDBL: sprintf(vvbuf,"%d",CMDBL); /* SAFE */ return(vvbuf); #ifdef CKCHANNELIO case VN_FERR: { extern int z_error; sprintf(vvbuf,"%d",z_error); /* SAFE */ return(vvbuf); } case VN_FMAX: { extern int z_maxchan; sprintf(vvbuf,"%d",z_maxchan); /* SAFE */ return(vvbuf); } case VN_FCOU: { extern int z_filcount; sprintf(vvbuf,"%d",z_filcount); /* SAFE */ return(vvbuf); } #endif /* CKCHANNELIO */ #ifndef NODIAL case VN_DRTR: { extern int dialcount; sprintf(vvbuf,"%d",dialcount); /* SAFE */ return(vvbuf); } #endif /* NODIAL */ #ifndef NOLOGDIAL #ifndef NOLOCAL case VN_CXTIME: sprintf(vvbuf,"%ld",dologshow(0)); /* SAFE */ return(vvbuf); #endif /* NOLOCAL */ #endif /* NOLOGDIAL */ case VN_BYTE: sprintf(vvbuf,"%d",byteorder); /* SAFE */ return(vvbuf); case VN_KBCHAR: vvbuf[0] = NUL; vvbuf[1] = NUL; if (kbchar > 0) vvbuf[0] = (kbchar & 0xff); return(vvbuf); case VN_TTYNAM: { #ifdef HAVECTTNAM extern char cttnam[]; return((char *)cttnam); #else return(CTTNAM); #endif /* HAVECTTNAM */ } case VN_PROMPT: return(cmgetp()); case VN_BUILD: { extern char * buildid; return(buildid); } #ifndef NOSEXP case VN_SEXP: { extern char * lastsexp; return(lastsexp ? lastsexp : ""); } case VN_VSEXP: { extern char * sexpval; return(sexpval ? sexpval : ""); } case VN_LSEXP: { extern int sexpdep; ckstrncpy(vvbuf,ckitoa(sexpdep),VVBUFL); return(vvbuf); } #endif /* NOSEXP */ #ifdef GFTIMER case VN_FTIME: { CKFLOAT f; ztime(&p); if (p == NULL || *p == NUL) return(NULL); z = atol(p+11) * 3600L + atol(p+14) * 60L + atol(p+17); f = (CKFLOAT)z + ((CKFLOAT)ztusec) / 1000000.0; sprintf(vvbuf,"%f",f); /* SAFE */ return(vvbuf); } #endif /* GFTIMER */ #ifndef NOHTTP case VN_HTTP_C: { /* HTTP Code */ extern int http_code; return(ckitoa(http_code)); } case VN_HTTP_N: /* HTTP Connected */ return( http_isconnected() ? "1" : "0"); case VN_HTTP_H: /* HTTP Host */ return( (char *)http_host() ); case VN_HTTP_M: { /* HTTP Message */ extern char http_reply_str[]; return((char *)http_reply_str); } case VN_HTTP_S: /* HTTP Security */ return((char *)http_security()); #endif /* NOHTTP */ #ifdef NEWFTP case VN_FTP_B: return((char *)ftp_cpl_mode()); case VN_FTP_D: return((char *)ftp_dpl_mode()); case VN_FTP_Z: return((char *)ftp_authtype()); case VN_FTP_C: { extern int ftpcode; return(ckitoa(ftpcode)); } case VN_FTP_M: { extern char ftp_reply_str[]; if (isdigit(ftp_reply_str[0]) && isdigit(ftp_reply_str[1]) && isdigit(ftp_reply_str[2]) && ftp_reply_str[3] == ' ') return(&ftp_reply_str[4]); else return(ftp_reply_str); } case VN_FTP_S: { extern char ftp_srvtyp[]; return((char *)ftp_srvtyp); } case VN_FTP_H: { extern char * ftp_host; return(ftp_host ? ftp_host : ""); } case VN_FTP_X: { /* FTP Connected */ return(ftpisconnected() ? "1" : "0"); } case VN_FTP_L: { /* FTP Logged in */ return(ftpisloggedin() ? "1" : "0"); } case VN_FTP_G: { /* FTP GET-PUT-REMOTE */ extern int ftpget; char * s = ""; switch (ftpget) { case 0: s = "kermit"; break; case 1: s = "ftp"; break; case 2: s = "auto"; break; } return(s); } #endif /* NEWFTP */ #ifndef NOLOCAL case VN_CX_STA: { /* CONNECT status */ extern int cx_status; return(ckitoa(cx_status)); } #endif /* NOLOCAL */ case VN_NOW: /* Timestamp */ return(ckcvtdate(p,0)); case VN_HOUR: /* Hour of the day */ ztime(&p); /* "Thu Feb 8 12:00:00 1990" */ if (!p) p = ""; if (!*p) return(p); vvbuf[0] = p[11]; vvbuf[1] = p[12]; vvbuf[2] = NUL; return(vvbuf); /* and return it */ case VN_BITS: /* Bits (16, 32, 64) */ if (sizeof(long) > 4) return(ckitoa(8*sizeof(long))); else return(ckitoa(8*sizeof(int))); case VN_LASTKWV: /* 212 */ return(lastkwval ? lastkwval : ""); case VN_HOSTIP: { /* 212 */ #ifdef TCPSOCKET extern char hostipaddr[]; return((char *)hostipaddr); #else return(""); #endif /* TCPSOCKET */ } case VN_INPMSG: switch (instatus) { case INP_OK: return("SUCCESS"); case INP_TO: return("Timed out"); case INP_UI: return("Keyboard interrupt"); case INP_IE: return("Internal error"); case INP_IO: return("I/O error or connection lost"); case INP_IKS: return("INPUT disabled"); case INP_BF: return("Buffer filled and /NOWRAP set"); default: return("Unknown"); } case VN_VAREVAL: /* 212 */ return(vareval ? "recursive" : "simple"); case VN_LOG_CON: /* \v(...) for log files */ #ifdef CKLOGDIAL return(diafil); #else return(""); #endif case VN_LOG_PKT: #ifndef NOXFER return(pktfil); #else return(""); #endif case VN_LOG_SES: #ifndef NOLOCAL return(sesfil); #else return(""); #endif case VN_LOG_TRA: #ifdef TLOG return(trafil); #else return(""); #endif case VN_LOG_DEB: #ifdef DEBUG return(debfil); #else return(""); #endif case VN_PREVCMD: { extern char * prevcmd; return(prevcmd ? prevcmd : ""); } } #ifndef NODIAL switch (y) { /* Caller ID values */ extern char * callid_date, * callid_time, * callid_name, * callid_nmbr, * callid_mesg; case VN_CI_DA: return(callid_date ? callid_date : ""); case VN_CI_TI: return(callid_time ? callid_time : ""); case VN_CI_NA: return(callid_name ? callid_name : ""); case VN_CI_NU: return(callid_nmbr ? callid_nmbr : ""); case VN_CI_ME: return(callid_mesg ? callid_mesg : ""); } /* End of variable-name switches */ #endif /* NODIAL */ #ifdef NT switch (y) { case VN_PERSONAL: p = (char *)GetPersonal(); if (p) { GetShortPathName(p,vvbuf,VVBUFL); return(vvbuf); } return(""); case VN_DESKTOP: p = (char *)GetDesktop(); if (p) { GetShortPathName(p,vvbuf,VVBUFL); return(vvbuf); } return(""); case VN_COMMON: p = (char *)GetAppData(1); if (p) { ckmakmsg(vvbuf,VVBUFL,p,"Kermit 95/",NULL,NULL); GetShortPathName(vvbuf,vvbuf,VVBUFL); return(vvbuf); } return(""); case VN_APPDATA: p = (char *)GetAppData(0); if (p) { ckmakmsg(vvbuf,VVBUFL,p,"Kermit 95/",NULL,NULL); GetShortPathName(vvbuf,vvbuf,VVBUFL); return(vvbuf); } return(""); } #endif /* NT */ #ifdef TN_COMPORT switch (y) { case VN_TNC_SIG: { p = (char *) tnc_get_signature(); ckstrncpy(vvbuf,p ? p : "",VVBUFL); return(vvbuf); } } #endif /* TN_COMPORT */ #ifdef KUI switch (y) { case VN_GUI_RUN: { extern HWND getHwndKUI(); if ( IsIconic(getHwndKUI()) ) return("minimized"); if ( IsZoomed(getHwndKUI()) ) return("maximized"); return("restored"); } case VN_GUI_XP: sprintf(vvbuf,"%d",get_gui_window_pos_x()); /* SAFE */ return(vvbuf); case VN_GUI_YP: sprintf(vvbuf,"%d",get_gui_window_pos_y()); /* SAFE */ return(vvbuf); case VN_GUI_XR: sprintf(vvbuf,"%d",GetSystemMetrics(SM_CXSCREEN)); /* SAFE */ return(vvbuf); case VN_GUI_YR: sprintf(vvbuf,"%d",GetSystemMetrics(SM_CYSCREEN)); /* SAFE */ return(vvbuf); case VN_GUI_FNM: if ( ntermfont > 0 ) { int i; for (i = 0; i < ntermfont; i++) { if (tt_font == term_font[i].kwval) { ckstrncpy(vvbuf,term_font[i].kwd,VVBUFL); return(vvbuf); } } } return("(unknown)"); case VN_GUI_FSZ: ckstrncpy(vvbuf,ckitoa(tt_font_size/2),VVBUFL); if ( tt_font_size % 2 ) ckstrncat(vvbuf,".5",VVBUFL); return(vvbuf); } #endif /* KUI */ fnsuccess = 0; if (fnerror) { fnsuccess = 0; } if (fndiags) { if (!embuf[0]) ckstrncpy(embuf,"",EMBUFLEN); printf("?%s\n",embuf); return((char *)embuf); } else return(""); } #endif /* NOSPL */ /* X X S T R I N G -- Expand variables and backslash codes. int xxtstring(s,&s2,&n); Expands \ escapes via recursive descent. Argument s is a pointer to string to expand (source). Argument s2 is the address of where to put result (destination). Argument n is the length of the destination string (to prevent overruns). Returns -1 on failure, 0 on success, with destination string null-terminated and s2 pointing to the terminating null, so that subsequent characters can be added. Failure reasons include destination buffer is filled up. */ #define XXDEPLIM 100 /* Recursion depth limit */ /* In Windows the stack is limited to 256K so big character arrays like vnambuf can't be on the stack in recursive functions like zzstring(). But that's no reason use malloc() in Unix or VMS, which don't have this kind of restriction. */ #ifdef DVNAMBUF /* Dynamic vnambuf[] */ #undef DVNAMBUF /* Clean slate */ #endif /* DVNAMBUF */ #ifndef NOSPL /* Only if SPL included */ #ifdef OS2 /* Only for K95 */ #define DVNAMBUF #endif /* OS2 */ #endif /* NOSPL */ int zzstring(s,s2,n) char *s; char **s2; int *n; { int x, /* Current character */ y, /* Worker */ pp, /* Paren level */ kp, /* Brace level */ argn, /* Function argument counter */ n2, /* Local copy of n */ d, /* Array dimension */ vbi, /* Variable id (integer form) */ argl, /* String argument length */ nx, /* Save original length */ quoting = 0; /* 299 */ char vb, /* Variable id (char form) */ *vp, /* Pointer to variable definition */ *new, /* Local pointer to target string */ #ifdef COMMENT *old, /* Save original target pointer */ #endif /* COMMENT */ *p, /* Worker */ *q, /* Worker */ *s3; /* Worker */ int x3; /* Worker */ char *r = (char *)0; /* For holding function args */ char *r2 = (char *)0; char *r3p; #ifndef NOSPL #ifdef DVNAMBUF char * vnambuf = NULL; /* Buffer for variable/function name */ #else /* DVNAMBUF */ char vnambuf[VNAML]; /* Buffer for variable/function name */ #endif /* DVNAMBUF */ char *argp[FNARGS]; /* Pointers to function args */ #endif /* NOSPL */ static int depth = 0; /* Call depth, avoid overflow */ n2 = *n; /* Make local copies of args */ nx = n2; #ifdef COMMENT /* This is always 32K in BIGBUFOK builds */ if (depth == 0) debug(F101,"zzstring top-level n","",n2); #endif /* COMMENT */ new = *s2; /* for one less level of indirection */ #ifdef COMMENT old = new; #endif /* COMMENT */ #ifndef NOSPL itsapattern = 0; /* For \fpattern() */ isjoin = 0; /* For \fjoin() */ #endif /* NOSPL */ depth++; /* Sink to a new depth */ if (depth > XXDEPLIM) { /* Too deep? */ printf("?definition is circular or too deep\n"); debug(F101,"zzstring fail","",depth); depth = 0; *new = NUL; return(-1); } if (!s || !new) { /* Watch out for null pointers */ debug(F101,"zzstring fail 2","",depth); if (new) *new = NUL; depth = 0; return(-1); } s3 = s; argl = 0; while (*s3++) argl++; /* Get length of source string */ debug(F010,"zzstring entry",s,0); if (argl == 0) { /* Empty string */ debug(F111,"zzstring empty arg",s,argl); depth = 0; *new = NUL; return(0); } if (argl < 0) { /* Watch out for garbage */ debug(F101,"zzstring fail 3","",depth); *new = NUL; depth = 0; return(-1); } #ifdef DVNAMBUF debug(F100,"vnambuf malloc...","",0); vnambuf = malloc(VNAML); if (vnambuf == NULL) { printf("?Out of memory"); return(-1); } debug(F100,"vnambuf malloc ok","",0); #endif /* DVNAMBUF */ while ((x = *s)) { /* Loop for all characters */ if (x != CMDQ) { /* Is it the command-quote char? */ *new++ = *s++; /* No, normal char, just copy */ if (--n2 < 0) { /* and count it, careful of overflow */ debug(F101,"zzstring overflow 1","",depth); depth = 0; #ifdef DVNAMBUF if (vnambuf) free(vnambuf); #endif /* DVNAMBUF */ return(-1); } continue; } /* We have the command-quote character. */ x = *(s+1); /* Get the following character. */ if (isupper(x)) x = tolower(x); switch (x) { /* Act according to variable type */ #ifndef NOSPL case 0: /* It's a lone backslash */ *new++ = *s++; if (--n2 < 0) { debug(F101,"zzstring overflow 2","",0); #ifdef DVNAMBUF if (vnambuf) free(vnambuf); #endif /* DVNAMBUF */ return(-1); } break; case '%': /* Variable */ s += 2; /* Get the letter or digit */ vb = *s++; /* and move source pointer past it */ vp = NULL; /* Assume definition is empty */ if (vb >= '0' && vb <= '9') { /* Digit for macro arg */ if (maclvl < 0) /* Digit variables are global */ vp = g_var[vb]; /* if no macro is active */ else /* otherwise */ vp = m_arg[maclvl][vb - '0']; /* they're on the stack */ } else if (vb == '*') { /* Macro args string */ #ifdef COMMENT /* This doesn't take changes into account */ vp = (maclvl >= 0) ? m_line[maclvl] : topline; if (!vp) vp = ""; #else char * ss = new; if (zzstring("\\fjoin(&_[],,1)",&new,&n2) < 0) { #ifdef DVNAMBUF if (vnambuf) free(vnambuf); #endif /* DVNAMBUF */ return(-1); } debug(F110,"zzstring \\%*",ss,0); break; #endif /* COMMENT */ } else { if (isupper(vb)) vb += ('a'-'A'); vp = g_var[vb]; /* Letter for global variable */ } if (!vp) vp = ""; #ifdef COMMENT if (vp) { /* If definition not empty */ #endif /* COMMENT */ if (vareval) { debug(F010,"zzstring %n vp",vp,0); /* call self to evaluate it */ if (zzstring(vp,&new,&n2) < 0) { debug(F101,"zzstring fail 6","",depth); #ifdef DVNAMBUF if (vnambuf) free(vnambuf); #endif /* DVNAMBUF */ return(-1); /* Pass along failure */ } } else { while ((*new++ = *vp++)) /* copy it to output string. */ if (--n2 < 0) { if (q) free(q); debug(F101,"zzstring overflow 4.5","",depth); #ifdef DVNAMBUF if (vnambuf) free(vnambuf); #endif /* DVNAMBUF */ return(-1); } new--; /* Back up over terminating null */ n2++; /* to allow for further deposits. */ } #ifdef COMMENT } else { debug(F110,"zzstring %n vp","(NULL)",0); n2 = nx; new = old; *new = NUL; } #endif /* COMMENT */ break; case '&': /* An array reference */ x = arraynam(s,&vbi,&d); /* Get name and subscript */ debug(F111,"zzstring arraynam",s,x); if (x < 0) { debug(F101,"zzstring fail 7","",depth); #ifdef DVNAMBUF if (vnambuf) free(vnambuf); #endif /* DVNAMBUF */ return(-1); } pp = 0; /* Bracket counter */ while (*s) { /* Advance source pointer... */ if (*s == '[') pp++; if (*s == ']' && --pp == 0) break; s++; } if (*s == ']') s++; /* ...past the closing bracket. */ x = chkarray(vbi,d); /* Array is declared? */ debug(F101,"zzstring chkarray","",x); if (x > -1) { #ifdef COMMENT char * s1 = NULL; #endif /* COMMENT */ vbi -= ARRAYBASE; /* Convert name to index */ if (a_dim[vbi] >= d) { /* If subscript in range */ char **ap; ap = a_ptr[vbi]; /* get data pointer */ if (ap) { /* and if there is one */ if (ap[d]) { /* If definition not empty */ debug(F111,"zzstring ap[d]",ap[d],d); if (vareval) { if (zzstring(ap[d],&new,&n2) < 0) { debug(F101,"zzstring fail 8","",depth); #ifdef DVNAMBUF if (vnambuf) free(vnambuf); #endif /* DVNAMBUF */ return(-1); /* Pass along failure */ } } else { vp = ap[d]; while ((*new++ = *vp++)) /* copy to result */ if (--n2 < 0) { if (q) free(q); debug(F101, "zzstring overflow 8.5","",depth); #ifdef DVNAMBUF if (vnambuf) free(vnambuf); #endif /* DVNAMBUF */ return(-1); } new--; /* Back up over terminating null */ n2++; /* to allow for further deposits. */ } } } else { /* old = new; */ n2 = nx; } } } break; case 'f': /* A builtin function */ q = vnambuf; /* Copy the name */ y = 0; /* into a separate buffer */ s += 2; /* point past 'F' */ while (y++ < VNAML) { if (*s == '(') { s++; break; } /* Look for open paren */ if ((*q = *s) == NUL) break; /* or end of string */ s++; q++; } *q = NUL; /* Terminate function name */ if (y >= VNAML) { /* Handle pathological case */ while (*s && (*s != '(')) /* of very long string entered */ s++; /* as function name. */ if (*s == ')') s++; /* Skip past it. */ } r = r2 = malloc(argl+2); /* And make a place to copy args */ /* debug(F101,"zzstring r2","",r2); */ if (!r2) { /* Watch out for malloc failure */ debug(F101,"zzstring fail 9","",depth); *new = NUL; depth = 0; #ifdef DVNAMBUF if (vnambuf) free(vnambuf); #endif /* DVNAMBUF */ return(-1); } if (r3) free(r3); /* And another to copy literal arg string */ r3 = malloc(argl+2); /* debug(F101,"zzstring r3","",r3); */ if (!r3) { debug(F101,"zzstring fail 10","",depth); depth = 0; *new = NUL; if (r2) free(r2); #ifdef DVNAMBUF if (vnambuf) free(vnambuf); #endif /* DVNAMBUF */ return(-1); } else r3p = r3; argn = 0; /* Argument counter */ argp[argn++] = r; /* Point to first argument */ y = 0; /* Completion flag */ pp = 1; /* Paren level (already have one). */ kp = 0; while (1) { /* Copy each argument, char by char. */ *r3p++ = *s; /* This is a literal copy for \flit */ if (!*s) break; if (*s == '{') { /* Left brace */ kp++; } if (*s == '}') { /* Right brace */ kp--; } if (*s == '(' && kp <= 0) { /* Open paren not in brace */ pp++; /* Count it */ } *r = *s; /* Now copy resulting byte */ if (!*r) /* If NUL, done. */ break; if (*r == ')' && kp <= 0) { /* Closing paren, count it. */ if (--pp == 0) { /* Final one? */ *r = NUL; /* Make it a terminating null */ *(r3p - 1) = NUL; s++; /* Point past it in source string */ y = 1; /* Flag we've got all the args */ break; /* Done with while loop */ } } if (*r == ',' && kp <= 0) { /* Comma */ if (pp == 1) { /* If not within ()'s, */ if (argn >= FNARGS) { /* Too many args */ s++; r++; /* Keep collecting flit() string */ continue; } *r = NUL; /* New arg, skip past comma */ argp[argn++] = r+1; /* In range, point to new arg */ } /* Otherwise just skip past */ } s++; r++; /* Advance pointers */ } if (!y) /* If we didn't find closing paren */ argn = -1; #ifdef DEBUG if (deblog) { char buf[24]; debug(F111,"zzstring function name",vnambuf,y); debug(F010,"zzstring function r3",r3,0); for (y = 0; y < argn; y++) { sprintf(buf,"arg %2d ",y); debug(F010,buf,argp[y],0); } } #endif /* DEBUG */ { /* In case the function name itself is constructed */ char buf[64]; char * p = buf; int n = 64; if (zzstring(vnambuf,&p,&n) > -1) ckstrncpy(vnambuf,buf,64); } vp = fneval(vnambuf,argp,argn,r3); /* Evaluate the function. */ if (vp) { /* If definition not empty */ while ((*new++ = *vp++)) { /* copy it to output string */ if (--n2 < 0) { /* watch out for overflow */ debug(F101,"zzstring fail 12","",depth); if (r2) { free(r2); r2 = NULL; } if (r3) { free(r3); r3 = NULL; } #ifdef DVNAMBUF if (vnambuf) free(vnambuf); #endif /* DVNAMBUF */ return(-1); } } new--; /* Back up over terminating null */ n2++; /* to allow for further deposits. */ } if (r2) { free(r2); r2 = NULL; } if (r3) { free(r3); r3 = NULL; } break; case 'q': /* 299 String to be take literally */ quoting = 1; /* 299 */ case '$': /* An environment variable */ case 'v': /* Or a named builtin variable. */ case 'm': /* Or a macro /long variable */ case 's': /* 196 Macro substring */ case ':': /* 196 \-variable substring */ pp = 0; p = s+2; /* $/V/M must be followed by (name) */ if (*p != '(') { /* as in \$(HOME) or \V(count) */ *new++ = *s++; /* If not, just copy it */ if (--n2 < 0) { debug(F101,"zzstring overflow 3","",depth); #ifdef DVNAMBUF if (vnambuf) free(vnambuf); #endif /* DVNAMBUF */ return(-1); } break; } pp++; p++; /* Point to 1st char of name */ q = vnambuf; /* Copy the name */ y = 0; /* into a separate buffer */ debug(F110,">>>> \\q(ARG)",p,0); while (y++ < VNAML) { /* Watch out for name too long */ if (*p == '(') { /* Parens can be nested... */ if (*(p-1) != CMDQ) /* 299 */ pp++; } else if (*p == ')') { /* Name properly terminated with ')' */ if (*(p-1) != CMDQ) /* 299 */ pp--; if (pp == 0) { p++; /* Move source pointer past ')' */ break; } } if ((*q = *p) == NUL) /* String ends before ')' */ break; p++; q++; /* Advance pointers */ } *q = NUL; /* Terminate the variable name */ if (y >= VNAML) { /* Handle pathological case */ while (*p && (*p != ')')) /* of very long string entered */ p++; /* as variable name. */ if (*p == ')') p++; /* Skip ahead to the end of it. */ } s = p; /* Adjust global source pointer */ s3 = vnambuf; x3 = 0; while (*s3++) x3++; p = malloc(x3 + 1); /* Make temporary space */ if (p && !quoting) { /* If we got the space */ vp = vnambuf; /* Point to original */ strcpy(p,vp); /* (safe) Make a copy of it */ y = VNAML; /* Length of name buffer */ zzstring(p,&vp,&y); /* Evaluate the copy */ free(p); /* Free the temporary space */ p = NULL; } debug(F110,"zzstring vname",vnambuf,0); q = NULL; if (x == 'q') { /* 299 Quoting this string */ vp = vnambuf; /* 299 */ debug(F110,">>> VP",vp,0); } else if (x == '$') { /* Look up its value */ vp = getenv(vnambuf); /* This way for environment variable */ } else if (x == 'm' || x == 's' || x == ':') { /* Macro / substr */ int k, x1 = -1, x2 = -1; char c = NUL; k = strlen(vnambuf); /* \s(name[n:m]) -- Compact substring notation */ if ((x == 's' || x == ':') && (k > 1)) { /* Substring wanted */ int bprc; if (vnambuf[k-1] == ']') { int i; for (i = 0; i < k-1; i++) { if (vnambuf[i] == '[') { bprc = boundspair(vnambuf,":_.",&x1,&x2,&c); debug(F111,"zzstring boundspair",vnambuf,bprc); debug(F000,"zzstring boundspair c","",c); if (bprc > -1) { vnambuf[i] = NUL; if (x1 < 1) x1 = 1; x1--; /* Adjust to 0-base */ } break; } } } } if (x == ':') { /* Variable type (s or :) */ vp = vnambuf; } else { y = isaarray(vnambuf) ? mxxlook(mactab,vnambuf,nmac) : mxlook(mactab,vnambuf,nmac); if (y > -1) { /* Got definition */ vp = mactab[y].mval; } else { vp = NULL; } } debug(F111,"zzstring vp",vp,(vp==NULL)?0:strlen(vp)); if (vp) { if ((x == 's' || x == ':') && (k > 1)) { /* Compact substring notation */ if (x2 == 0) { /* Length */ vp = NULL; } else if (x1 > -1) { /* Start */ k = strlen(vp); debug(F101,">>> k","",k); /* If it's off the end, result is empty */ if (x1 > k) { vp = NULL; } else if (k > 0) { /* Stay in bounds */ if (c == '_' && x2 > k) /* startpos_endpos */ x2 = k; if (c == ':' && x1 + x2 > k) /* start:length */ x2 = -1; debug(F101,">>> x2","",x2); debug(F000,">>> c","",c); if ((q = malloc(k+1))) { strcpy(q,vp); /* safe */ if (c == '.') { q[x1+1] = NUL; debug(F000,"XXX. q",q,c); } if (c == ':') { /* start:length */ if ((x2 > -1) && ((x1 + x2) <= k)) { q[x1+x2] = NUL; } debug(F000,"XXX: q",q,c); } else if (c == '_') { /* start_endpos */ if (x1 >= x2) { q[x1 = 0] = NUL; } else if (x2 < k && x2 > -1) { q[x2] = NUL; } debug(F000,"XXX_ q",q,c); } vp = q+x1; } else vp = NULL; } else vp = NULL; } debug(F110,"XXX vnambuf",vnambuf,0); debug(F000,"XXX c","",c); debug(F101,"XXX x1","",x1); debug(F101,"XXX x2","",x2); debug(F110,"XXX result",vp,0); #ifdef DEBUG if (deblog) { if (!vp) { } else { k = strlen(vp); } } #endif /* DEBUG */ } } } else { /* or */ vp = nvlook(vnambuf); /* this way for builtin variable */ } if (vp) { /* If definition not empty */ while ((*new++ = *vp++)) /* copy it to output string. */ if (--n2 < 0) { if (q) free(q); debug(F101,"zzstring overflow 4","",depth); #ifdef DVNAMBUF if (vnambuf) free(vnambuf); #endif /* DVNAMBUF */ return(-1); } new--; /* Back up over terminating null */ n2++; /* to allow for further deposits. */ } if (q) { free(q); q = NULL; } break; #endif /* NOSPL */ /* Handle \nnn even if NOSPL. */ #ifndef NOKVERBS case 'K': case 'k': { extern struct keytab kverbs[]; extern int nkverbs; #define K_BUFLEN 30 char kbuf[K_BUFLEN + 1]; /* Key verb name buffer */ int x, y, z, brace = 0; s += 2; /* We assume that the verb name is {braced}, or it extends to the end of the string, s, or it ends with a space, control character, or backslash. */ p = kbuf; /* Copy verb name into local buffer */ x = 0; if (*s == '{') { s++; brace++; } while ((x++ < K_BUFLEN) && (*s > SP) && (*s != CMDQ)) { if (brace && *s == '}') { s++; break; } *p++ = *s++; } brace = 0; *p = NUL; /* Terminate. */ p = kbuf; /* Point back to beginning */ debug(F110,"zzstring kverb",p,0); y = xlookup(kverbs,p,nkverbs,&x); /* Look it up */ debug(F101,"zzstring lookup",0,y); if (y > -1) { dokverb(VCMD,y); #ifndef NOSPL } else { /* Is it a macro? */ y = mxlook(mactab,p,nmac); if (y > -1) { debug(F111,"zzstring mxlook",p,y); if ((z = dodo(y,NULL,cmdstk[cmdlvl].ccflgs)) > 0) { if (cmpush() > -1) { /* Push command parser state */ extern int ifc; int ifcsav = ifc; /* Push IF condition on stack */ y = parser(1); /* New parser to execute macro */ cmpop(); /* Pop command parser */ ifc = ifcsav; /* Restore IF condition */ if (y == 0) { /* No errors, ignore actions */ p = mrval[maclvl+1]; /* If OK set return val */ if (p == NULL) p = ""; } } else { /* Can't push any more */ debug(F101,"zzstring pushed too deep","",depth); printf( "\n?Internal error: zzstring stack overflow\n" ); while (cmpop() > -1); p = ""; } } } #endif /* NOSPL */ } break; } #endif /* NOKVERBS */ default: /* Maybe it's a backslash code */ y = xxesc(&s); /* Go interpret it */ if (y < 0) { /* Upon failure */ *new++ = (char) x; /* Just quote the next character */ s += 2; /* Move past the pair */ n2 -= 2; if (n2 < 0) { debug(F101,"zzstring overflow 5","",depth); #ifdef DVNAMBUF if (vnambuf) free(vnambuf); #endif /* DVNAMBUF */ return(-1); } continue; /* and go back for more */ } else { *new++ = (char) y; /* else deposit interpreted value */ if (--n2 < 0) { debug(F101,"zzstring overflow 6","",depth); #ifdef DVNAMBUF if (vnambuf) free(vnambuf); #endif /* DVNAMBUF */ return(-1); } } } } *new = NUL; /* Terminate the new string */ debug(F010,"zzstring while exit",*s2,0); depth--; /* Adjust stack depth gauge */ *s2 = new; /* Copy results back into */ *n = n2; /* the argument addresses */ debug(F101,"zzstring ok","",depth); #ifdef DVNAMBUF if (vnambuf) free(vnambuf); #endif /* DVNAMBUF */ return(0); /* and return. */ } #endif /* NOICP */ ckuus5.c0000644000015300001460000136735711624030437011272 0ustar fdckermit#include "ckcsym.h" int xcmdsrc = 0; #ifdef NOICP int cmdsrc() { return(0); } #endif /* NOICP */ /* C K U U S 5 -- "User Interface" for C-Kermit, part 5 */ /* Authors: Frank da Cruz , The Kermit Project, Columbia University, New York City Jeffrey E Altman Secure Endpoints Inc., New York City Copyright (C) 1985, 2011, Trustees of Columbia University in the City of New York. All rights reserved. See the C-Kermit COPYING.TXT file or the copyright text in the ckcmai.c module for disclaimer and permissions. */ /* Includes */ #include "ckcdeb.h" #include "ckcasc.h" #include "ckcker.h" #include "ckuusr.h" #ifdef DCMDBUF char *line; /* Character buffer for anything */ char *tmpbuf; #else char line[LINBUFSIZ+1]; char tmpbuf[TMPBUFSIZ+1]; /* Temporary buffer */ #endif /* DCMDBUF */ #ifndef NOICP #include "ckcnet.h" #ifndef NOCSETS #include "ckcxla.h" #endif /* NOCSETS */ #ifdef MAC #include "ckmasm.h" #endif /* MAC */ #ifdef CK_SSL #include "ck_ssl.h" #endif /* CK_SSL */ extern char * ck_cryear; /* (ckcmai.c) Latest C-Kermit copyright year */ #ifdef OS2 #include "ckoetc.h" #ifndef NT #define INCL_NOPM #define INCL_VIO /* Needed for ckocon.h */ #include #undef COMMENT #else /* NT */ #include #define TAPI_CURRENT_VERSION 0x00010004 #include #include #include "ckntap.h" #define APIRET ULONG extern int DialerHandle; extern int StartedFromDialer; #endif /* NT */ #include "ckocon.h" #include "ckokey.h" #ifdef KUI #include "ikui.h" #endif /* KUI */ #ifdef putchar #undef putchar #endif /* putchar */ #define putchar(x) conoc(x) extern int cursor_save ; extern bool cursorena[] ; #endif /* OS2 */ /* 2010-03-09 SMS. VAX C V3.1-051 needs for off_t. */ #ifdef VMS #include #endif /* def VMS */ /* For formatted screens, "more?" prompting, etc. */ #ifdef FT18 #define isxdigit(c) isdigit(c) #endif /* FT18 */ #ifdef STRATUS /* Stratus Computer, Inc. VOS */ #ifdef putchar #undef putchar #endif /* putchar */ #define putchar(x) conoc(x) #ifdef getchar #undef getchar #endif /* getchar */ #define getchar(x) coninc(0) #endif /* STRATUS */ /* External variables */ extern int carrier, cdtimo, local, quiet, backgrd, bgset, sosi, xsuspend, binary, escape, xargs, flow, cmdmsk, duplex, ckxech, seslog, what, inserver, diractive, tlevel, cwdf, nfuncs, msgflg, remappd, hints, mdmtyp, zincnt, cmask, rcflag, success, xitsta, pflag, tnlm, tn_nlm, xitwarn, debses, xaskmore, parity, saveask, wasclosed, whyclosed, cdactive, rcdactive, keepallchars, cmd_err; #ifdef LOCUS extern int locus, autolocus; #endif /* LOCUS */ #ifndef NOMSEND extern int addlist; #endif /* NOMSEND */ #ifdef CK_SPEED extern int prefixing; #endif /* CK_SPEED */ extern int g_matchdot; #ifdef RECURSIVE extern int recursive; #endif /* RECURSIVE */ extern int xfiletype; #ifdef IKSDCONF extern char * iksdconf; extern int iksdcf; #endif /* IKSDCONF */ #ifdef CK_RECALL extern int on_recall; #endif /* CK_RECALL */ extern int ngetpath, exitonclose; extern char * getpath[]; extern CHAR * epktmsg; extern char * snd_move; extern char * snd_rename; extern char * rcv_move; extern char * rcv_rename; extern char * g_snd_move; extern char * g_snd_rename; extern char * g_rcv_move; extern char * g_rcv_rename; extern char * nm[]; #ifdef CK_UTSNAME extern char unm_mch[]; extern char unm_mod[]; extern char unm_nam[]; extern char unm_rel[]; extern char unm_ver[]; #endif /* CK_UTSNAME */ #ifndef NOPUSH #ifndef NOFRILLS extern char editor[]; extern char editfile[]; extern char editopts[]; #ifdef BROWSER extern char browser[]; extern char browsopts[]; extern char browsurl[]; #endif /* BROWSER */ #endif /* NOFRILLS */ #endif /* NOPUSH */ #ifndef NOFRILLS #ifndef NORENAME _PROTOTYP(VOID shorename, (void)); #endif /* NORENAME */ #endif /* NOFRILLS */ #ifndef NOSERVER extern char * x_user, * x_passwd, * x_acct; #endif /* NOSERVER */ #ifdef CKLOGDIAL extern int dialog; extern char diafil[]; #endif /* CKLOGDIAL */ #ifdef CKROOT extern int ckrooterr; #endif /* CKROOT */ #ifndef NOSPL extern int cfilef, xxdot, vareval; extern char cmdfil[]; struct localvar * localhead[CMDSTKL]; struct localvar * localtail = NULL; struct localvar * localnext = NULL; _PROTOTYP( VOID shosexp, (void) ); _PROTOTYP( static VOID shoinput, (void) ); _PROTOTYP( static char gettok, (void) ); _PROTOTYP( static VOID factor, (void) ); _PROTOTYP( static VOID term, (void) ); _PROTOTYP( static VOID termp, (void) ); _PROTOTYP( static VOID exprp, (void) ); _PROTOTYP( static VOID expr, (void) ); _PROTOTYP( static VOID simple, (void) ); _PROTOTYP( static VOID simpler, (void) ); _PROTOTYP( static VOID simplest, (void) ); _PROTOTYP( static CK_OFF_T xparse, (void) ); #endif /* NOSPL */ #ifndef NOSHOW _PROTOTYP( int sho_iks, (void) ); #endif /* NOSHOW */ #ifdef MAC char * ckprompt = "Mac-Kermit>"; /* Default prompt for Macintosh */ char * ikprompt = "IKSD>"; #else /* Not MAC */ #ifdef NOSPL #ifdef OS2 char * ckprompt = "K-95> "; /* Default prompt for Win32 */ char * ikprompt = "IKSD> "; #else char * ckprompt = "C-Kermit>"; char * ikprompt = "IKSD>"; #endif /* NT */ #else /* NOSPL */ #ifdef OS2 /* Default prompt for OS/2 and Win32 */ #ifdef NT char * ckprompt = "[\\freplace(\\flongpath(\\v(dir)),/,\\\\)] K-95> "; char * ikprompt = "[\\freplace(\\flongpath(\\v(dir)),/,\\\\)] IKSD> "; #else /* NT */ char * ckprompt = "[\\freplace(\\v(dir),/,\\\\)] K-95> "; char * ikprompt = "[\\freplace(\\v(dir),/,\\\\)] IKSD> "; #endif /* NT */ #else /* OS2 */ #ifdef VMS char * ckprompt = "\\v(dir) C-Kermit>"; /* Default prompt VMS */ char * ikprompt = "\\v(dir) IKSD>"; #else char * ckprompt = "(\\v(dir)) C-Kermit>"; /* Default prompt for others */ char * ikprompt = "(\\v(dir)) IKSD>"; #endif /* VMS */ #endif /* NT */ #endif /* NOSPL */ #endif /* MAC */ #ifndef CCHMAXPATH #define CCHMAXPATH 257 #endif /* CCHMAXPATH */ char inidir[CCHMAXPATH] = { NUL, NUL }; /* Directory INI file executed from */ #ifdef TNCODE extern int tn_b_nlm; /* TELNET BINARY newline mode */ #endif /* TNCODE */ #ifndef NOKVERBS extern struct keytab kverbs[]; /* Table of \Kverbs */ extern int nkverbs; /* Number of \Kverbs */ #endif /* NOKVERBS */ #ifndef NOPUSH extern int nopush; #endif /* NOPUSH */ #ifdef CK_RECALL extern int cm_recall; #endif /* CK_RECALL */ extern char *ccntab[]; /* Printer stuff */ extern char *printername; extern int printpipe; #ifdef BPRINT extern int printbidi, pportparity, pportflow; extern long pportspeed; #endif /* BPRINT */ #ifdef OS2 _PROTOTYP (int os2getcp, (void) ); _PROTOTYP (int os2getcplist, (int *, int) ); #ifdef OS2MOUSE extern int tt_mouse; #endif /* OS2MOUSE */ extern int tt_update, tt_updmode, updmode, tt_utf8; #ifndef IKSDONLY extern int tt_status[]; #endif /* IKSDONLY */ #ifdef PCFONTS extern struct keytab term_font[]; #else #ifdef KUI extern struct keytab * term_font; #endif /* KUI */ #endif /* PCFONTS */ extern int ntermfont, tt_font, tt_font_size; extern unsigned char colornormal, colorunderline, colorstatus, colorhelp, colorselect, colorborder, colorgraphic, colordebug, colorreverse, colorcmd, coloritalic; extern int priority; extern struct keytab prtytab[]; extern int nprty; char * cmdmac = NULL; #endif /* OS2 */ #ifdef VMS _PROTOTYP (int zkermini, (char *, int, char *) ); #endif /* VMS */ extern long vernum; extern int inecho, insilence, inbufsize, nvars, inintr; extern char *protv, *fnsv, *cmdv, *userv, *ckxv, *ckzv, *ckzsys, *xlav, *cknetv, *clcmds; #ifdef OS2 extern char *ckyv; #endif /* OS2 */ #ifdef CK_AUTHENTICATION extern char * ckathv; #endif /* CK_AUTHENTICATION */ #ifdef CK_SSL extern char * cksslv; #endif /* CK_SSL */ #ifdef CK_ENCRYPTION #ifndef CRYPT_DLL extern char * ckcrpv; #endif /* CRYPT_DLL */ #endif /* CK_ENCRYPTION */ #ifdef SSHBUILTIN extern char *cksshv; #ifdef SFTP_BUILTIN extern char *cksftpv; #endif /* SFTP_BUILTIN */ #endif /* SSHBUILTIN */ #ifdef TNCODE extern char *cktelv; #endif /* TNCODE */ #ifndef NOFTP #ifndef SYSFTP extern char * ckftpv; #endif /* SYSFTP */ #endif /* NOFTP */ extern int srvidl; #ifdef OS2 extern char *ckonetv; extern int interm; #ifdef CK_NETBIOS extern char *ckonbiv; #endif /* CK_NETBIOS */ #ifdef OS2MOUSE extern char *ckomouv; #endif /* OS2MOUSE */ #endif /* OS2 */ #ifndef NOLOCAL extern char *connv; #endif /* NOLOCAL */ #ifndef NODIAL extern char *dialv; #endif /* NODIAL */ #ifndef NOSCRIPT extern char *loginv; extern int secho; #endif /* NOSCRIPT */ #ifndef NODIAL extern int nmdm, dirline; extern struct keytab mdmtab[]; #endif /* NODIAL */ extern int network, nettype, ttnproto; #ifdef OS2 #ifndef NOTERM /* SET TERMINAL items... */ extern int tt_type, tt_arrow, tt_keypad, tt_wrap, tt_answer, tt_scrsize[]; extern int tt_bell, tt_roll[], tt_ctstmo, tt_cursor, tt_pacing, tt_type_mode; extern char answerback[]; extern struct tt_info_rec tt_info[]; /* Indexed by terminal type */ extern int max_tt; #endif /* NOTERM */ #endif /* OS2 */ _PROTOTYP( VOID shotrm, (void) ); _PROTOTYP( int shofea, (void) ); #ifdef OS2 extern int tt_rows[], tt_cols[]; #else /* OS2 */ extern int tt_rows, tt_cols; #endif /* OS2 */ extern int cmd_rows, cmd_cols; #ifdef CK_TMPDIR extern int f_tmpdir; /* Directory changed temporarily */ extern char savdir[]; /* Temporary directory */ #endif /* CK_TMPDIR */ #ifndef NOLOCAL extern int tt_crd, tt_lfd, tt_escape; #endif /* NOLOCAL */ #ifndef NOCSETS extern int language, nfilc, tcsr, tcsl, tcs_transp, fcharset; extern struct keytab fcstab[]; extern struct csinfo fcsinfo[]; #ifndef MAC extern struct keytab ttcstab[]; #endif /* MAC */ #endif /* NOCSETS */ extern long speed; #ifndef NOXMIT extern int xmitf, xmitl, xmitp, xmitx, xmits, xmitw, xmitt; extern char xmitbuf[]; #endif /* NOXMIT */ extern char **xargv, *versio, *ckxsys, *dftty, *lp; #ifdef DCMDBUF extern char *cmdbuf, *atmbuf; /* Command buffers */ #ifndef NOSPL extern char *savbuf; /* Command buffers */ #endif /* NOSPL */ #else extern char cmdbuf[], atmbuf[]; /* Command buffers */ #ifndef NOSPL extern char savbuf[]; /* Command buffers */ #endif /* NOSPL */ #endif /* DCMDBUF */ extern char toktab[], ttname[], psave[]; extern CHAR sstate, feol; extern int cmflgs, techo, repars, ncmd; extern struct keytab cmdtab[]; #ifndef NOSETKEY KEY *keymap; #ifndef OS2 #define mapkey(x) keymap[x] #endif /* OS2 */ MACRO *macrotab; _PROTOTYP( VOID shostrdef, (CHAR *) ); #endif /* NOSETKEY */ extern int cmdlvl; #ifndef NOSPL extern struct mtab *mactab; extern struct keytab mackey[]; extern struct keytab vartab[], fnctab[], iftab[]; extern int maclvl, nmac, mecho, fndiags, fnerror, fnsuccess, nif; #endif /* NOSPL */ FILE *tfile[MAXTAKE]; /* TAKE file stack */ char *tfnam[MAXTAKE]; int tfline[MAXTAKE]; int topcmd = -1; /* cmdtab index of current command */ int havetoken = 0; extern int dblquo; /* Doublequoting enabled */ #ifdef DCMDBUF /* Initialization filespec */ char *kermrc = NULL; #else char kermrcb[KERMRCL]; char *kermrc = kermrcb; #endif /* DCMDBUF */ int noherald = 0; int cm_retry = 1; /* Command retry enabled */ xx_strp xxstring = zzstring; #ifndef NOXFER extern int displa, bye_active, protocol, pktlog, remfile, rempipe, unkcs, keep, lf_opts, fncnv, pktpaus, autodl, xfrcan, xfrchr, xfrnum, srvtim, srvdis, query, retrans, streamed, reliable, crunched, timeouts, fnrpath, autopath, rpackets, spackets, epktrcvd, srvping; #ifdef CK_AUTODL extern int inautodl, cmdadl; #endif /* CK_AUTODL */ #ifndef NOSERVER extern int en_asg, en_cwd, en_cpy, en_del, en_dir, en_fin, en_bye, en_ret, en_get, en_hos, en_que, en_ren, en_sen, en_set, en_spa, en_typ, en_who, en_mai, en_pri, en_mkd, en_rmd, en_xit, en_ena; #endif /* NOSERVER */ extern int atcapr, atenci, atenco, atdati, atdato, atleni, atleno, atblki, atblko, attypi, attypo, atsidi, atsido, atsysi, atsyso, atdisi, atdiso; #ifdef STRATUS extern int atfrmi, atfrmo, atcrei, atcreo, atacti, atacto; #endif /* STRATUS */ #ifdef CK_PERMS extern int atlpri, atlpro, atgpri, atgpro; #endif /* CK_PERMS */ #ifdef CK_LOGIN extern char * anonfile; /* Anonymous login init file */ extern char * anonroot; /* Anonymous file-system root */ extern char * userfile; /* Forbidden user file */ extern int isguest; /* Flag for anonymous user */ #endif /* CK_LOGIN */ #endif /* NOXFER */ #ifdef DCMDBUF int *xquiet = NULL; int *xvarev = NULL; #else int xquiet[CMDSTKL]; int xvarev[CMDSTKL]; #endif /* DCMDBUF */ char * prstring[CMDSTKL]; #ifndef NOSPL extern long ck_alarm; extern char alrm_date[], alrm_time[]; /* Local declarations */ static int nulcmd = 0; /* Flag for next cmd to be ignored */ /* Definitions for predefined macros */ /* First, the single-line macros, installed with addmac()... */ /* IBM-LINEMODE macro */ char *m_ibm = "set parity mark, set dupl half, set handsh xon, set flow none"; /* FATAL macro */ char *m_fat = "if def \\%1 echo \\%1, if not = \\v(local) 0 hangup, stop 1"; #ifdef CK_SPEED #ifdef IRIX65 char *m_fast = "set win 30, set rec pack 4000, set prefix cautious"; #else #ifdef IRIX /* Because of bug in telnet server */ char *m_fast = "set window 30, set rec pack 4000, set send pack 4000,\ set pref cautious"; #else #ifdef pdp11 char *m_fast = "set win 3, set rec pack 1024, set prefix cautious"; #else #ifdef BIGBUFOK char *m_fast = "set win 30, set rec pack 4000, set prefix cautious"; #else char *m_fast = "set win 4, set rec pack 2200, set prefix cautious"; #endif /* BIGBUFOK */ #endif /* IRIX */ #endif /* IRIX65 */ #endif /* pdp11 */ #ifdef pdp11 char *m_cautious = "set win 2, set rec pack 512, set prefixing cautious"; #else char *m_cautious = "set win 4, set rec pack 1000, set prefixing cautious"; #endif /* pdp11 */ char *m_robust = "set win 1, set rec pack 90, set prefixing all, \ set reliable off, set clearchannel off"; #else #ifdef BIGBUFOK #ifdef IRIX65 char *m_fast = "set win 30, set rec pack 4000"; #else #ifdef IRIX char *m_fast = "set win 30, set rec pack 4000, set send pack 4000"; #else char *m_fast = "set win 30, set rec pack 4000"; #endif /* IRIX */ #endif /* IRIX65 */ #else /* Not BIGBUFOK */ char *m_fast = "set win 4, set rec pack 2200"; #endif /* BIGBUFOK */ char *m_cautious = "set win 4, set rec pack 1000"; char *m_robust = "set win 1, set rec pack 90, set reliable off"; #endif /* CK_SPEED */ #ifdef VMS char *m_purge = "run purge \\%*"; #endif /* VMS */ #ifdef OS2 char *m_manual = "browse \\v(exedir)docs/manual/kermit95.htm"; #endif /* OS2 */ /* Now the multiline macros, defined with addmmac()... */ /* FOR macro for \%i-style loop variables (see dofor()...) */ char *for_def[] = { "_assign _for\\v(cmdlevel) { _getargs,", "def \\\\\\%1 \\feval(\\%2),:_..top,if \\%5 \\\\\\%1 \\%3 goto _..bot,", "\\%6,:_..inc,incr \\\\\\%1 \\%4,goto _..top,:_..bot,_putargs},", "def break goto _..bot, def continue goto _..inc,", "do _for\\v(cmdlevel) \\%1 \\%2 \\%3 \\%4 { \\%5 },_assign _for\\v(cmdlevel)", ""}; /* This is the FOR macro when the loop variable is itself a macro */ char *foz_def[] = { "_assign _for\\v(cmdlevel) { _getargs,", "def \\%1 \\feval(\\%2),:_..top,if \\%5 \\%1 \\%3 goto _..bot,", "\\%6,:_..inc,incr \\%1 \\%4,goto _..top,:_..bot,_putargs},", "def break goto _..bot, def continue goto _..inc,", "do _for\\v(cmdlevel) \\%1 \\%2 \\%3 \\%4 { \\%5 },_assign _for\\v(cmdlevel)", ""}; /* WHILE macro */ char *whil_def[] = { "_assign _whi\\v(cmdlevel) {_getargs,", ":_..inc,\\%1,\\%2,goto _..inc,:_..bot,_putargs},", "_def break goto _..bot, _def continue goto _..inc,", "do _whi\\v(cmdlevel),_assign _whi\\v(cmdlevel)", ""}; /* SWITCH macro */ char *sw_def[] = { "_assign _sw_\\v(cmdlevel) {_getargs,", "_forward {\\%1},\\%2,:default,:_..bot,_putargs},_def break goto _..bot,", "do _sw_\\v(cmdlevel),_assign _sw_\\v(cmdlevel)", ""}; /* XIF macro */ char *xif_def[] = { "_assign _if\\v(cmdlevel) {_getargs,\\%1,_putargs},", "do _if\\v(cmdlevel),_assign _if\\v(cmdlevel)", ""}; /* Variables declared here for use by other ckuus*.c modules. Space is allocated here to save room in ckuusr.c. */ #ifdef DCMDBUF struct cmdptr *cmdstk; int *ifcmd = NULL, *count = NULL, *iftest = NULL, *intime = NULL, *inpcas = NULL, *takerr = NULL, *merror = NULL; #else struct cmdptr cmdstk[CMDSTKL]; int ifcmd[CMDSTKL], count[CMDSTKL], iftest[CMDSTKL], intime[CMDSTKL], inpcas[CMDSTKL], takerr[CMDSTKL], merror[CMDSTKL]; #endif /* DCMDBUF */ /* Macro stack */ #ifdef COMMENT char *topline = NULL; /* Program invocation arg line */ char *m_line[MACLEVEL] = { NULL, NULL }; /* Stack of macro invocation lines */ #endif /* COMMENT */ char **m_xarg[MACLEVEL]; /* Pointers to arg vector arrays */ int n_xarg[MACLEVEL]; /* Sizes of arg vector arrays */ char *m_arg[MACLEVEL][NARGS]; /* Args of each level */ int macargc[MACLEVEL]; /* Argc of each level */ char *macp[MACLEVEL]; /* Current position in each macro */ char *macx[MACLEVEL]; /* Beginning of each macro def */ char *mrval[MACLEVEL]; /* RETURN value at each level */ int lastcmd[MACLEVEL]; /* Last command at each level */ int topargc = 0; /* Argc at top level */ char **topxarg = NULL; /* Argv at top level */ char *toparg[MAXARGLIST+2]; /* Global Variables */ char *g_var[GVARS+1]; /* Global \%a..z pointers */ extern char varnam[]; /* \%x variable name buffer */ /* Arrays -- Dimension must be 'z' - ARRAYBASE + 1 */ /* Note: a_link[x] < 0 means no link; >= 0 is a link */ char **a_ptr[32]; /* Array pointers, for arrays a-z */ int a_dim[32]; /* Dimensions for each array */ int a_link[32]; /* Link (index of linked-to-array) */ char **aa_ptr[CMDSTKL][32]; /* Array stack for automatic arrays */ int aa_dim[CMDSTKL][32]; /* Dimensions for each array */ /* INPUT command buffers and variables */ char * inpbuf = NULL; /* Buffer for INPUT and REINPUT */ extern char * inpbp; /* Global/static pointer to it */ char inchar[2] = { NUL, NUL }; /* Last character that was INPUT */ int incount = 0; /* INPUT character count */ extern int instatus; /* INPUT status */ static char * i_text[] = { /* INPUT status text */ "success", "timeout", "interrupted", "internal error", "i/o error" }; char lblbuf[LBLSIZ]; /* Buffer for labels */ #else /* NOSPL */ int takerr[MAXTAKE]; #endif /* NOSPL */ static char *prevdir = NULL; int pacing = 0; /* OUTPUT pacing */ char *tp; /* Temporary buffer pointer */ int timelimit = 0, asktimer = 0; /* Timers for time-limited commands */ #ifdef CK_APC /* Application Program Command (APC) */ int apcactive = APC_INACTIVE; int apcstatus = APC_OFF; /* OFF by default everywhere */ #ifdef DCMDBUF char *apcbuf; #else char apcbuf[APCBUFLEN]; #endif /* DCMDBUF */ #endif /* CK_APC */ extern char pktfil[], #ifdef DEBUG debfil[], #endif /* DEBUG */ #ifdef TLOG trafil[], #endif /* TLOG */ sesfil[]; #ifndef NOFRILLS extern int rmailf, rprintf; /* REMOTE MAIL & PRINT items */ extern char optbuf[]; #endif /* NOFRILLS */ extern int noinit; /* Flat to skip init file */ #ifndef NOSPL static struct keytab kcdtab[] = { /* Symbolic directory names */ #ifdef NT { "appdata", VN_APPDATA, 0 }, { "common", VN_COMMON, 0 }, { "desktop", VN_DESKTOP, 0 }, #endif /* NT */ { "download", VN_DLDIR, 0 }, #ifdef OS2ORUNIX { "exedir", VN_EXEDIR, 0 }, #endif /* OS2ORUNIX */ { "home", VN_HOME, 0 }, { "inidir", VN_INI, 0 }, #ifdef UNIX { "lockdir", VN_LCKDIR, 0 }, #endif /* UNIX */ #ifdef NT { "my_documents",VN_PERSONAL, 0 }, { "personal", VN_PERSONAL, CM_INV }, #endif /* NT */ { "startup", VN_STAR, 0 }, { "textdir", VN_TXTDIR, 0 }, { "tmpdir", VN_TEMP, 0 } }; static int nkcdtab = (sizeof(kcdtab) / sizeof(struct keytab)); #endif /* NOSPL */ #ifndef NOSPL _PROTOTYP( VOID freelocal, (int) ); _PROTOTYP( static CK_OFF_T expon, (CK_OFF_T, CK_OFF_T) ); _PROTOTYP( static CK_OFF_T gcd, (CK_OFF_T, CK_OFF_T) ); _PROTOTYP( static CK_OFF_T fact, (CK_OFF_T) ); int /* Initialize macro data structures. */ macini() { /* Allocate mactab and preset the first element. */ int i; if (!(mactab = (struct mtab *) malloc(sizeof(struct mtab) * MAC_MAX))) return(-1); mactab[0].kwd = NULL; mactab[0].mval = NULL; mactab[0].flgs = 0; for (i = 0; i < MACLEVEL; i++) localhead[i] = NULL; return(0); } #endif /* NOSPL */ /* C M D S R C -- Returns current command source */ /* 0 = top level, 1 = file, 2 = macro, -1 = error (shouldn't happen) */ /* As of 19 Aug 2000 this routine is obsolete. The scalar global variable xcmdsrc can be checked instead to save the overhead of a function call. */ int cmdsrc() { #ifdef COMMENT return(xcmdsrc); #else #ifndef NOSPL if (cmdlvl == 0) return(0); else if (cmdstk[cmdlvl].src == CMD_MD) return(2); else if (cmdstk[cmdlvl].src == CMD_TF) return(1); else return(-1); #else if (tlevel < 0) return(0); else return(1); #endif /* NOSPL */ #endif /* COMMENT */ } /* C M D I N I -- Initialize the interactive command parser */ static int cmdinited = 0; /* Command parser initialized */ extern int cmdint; /* Interrupts are allowed */ #ifdef CK_AUTODL int cmdadl = 1; /* Autodownload */ #else int cmdadl = 0; #endif /* CK_AUTODL */ char * k_info_dir = NULL; /* Where to find text files */ #ifdef UNIX static char * txtdir[] = { "/usr/local/doc/", /* Linux, SunOS, ... */ "/usr/share/lib/", /* HP-UX 10.xx... */ "/usr/share/doc/", /* Other possibilities... */ "/usr/local/lib/", /* NOTE: Each of these is tried */ "/usr/local/share/", /* as is, and also with a kermit */ "/usr/local/share/doc/", /* subdirectory. */ "/usr/local/share/lib/", "/opt/kermit/", /* Solaris */ "/opt/kermit/doc/", "/opt/", "/usr/doc/", "/doc/", "" }; #endif /* UNIX */ /* lookup() cache to speed up script execution. This is a static cache. Items are stored in decreasing frequency of reference based on statistics from a range of scripts. This gives better performance than a dynamic cache, which would require a lot more code and also would require system-dependent elements including system calls (e.g. to get subsecond times for entry aging). */ #ifdef USE_LUCACHE /* Set in ckuusr.h */ #define LUCACHE 32 /* Change this to reduce cache size */ int lusize = 0; char * lucmd[LUCACHE]; int luval[LUCACHE]; int luidx[LUCACHE]; struct keytab * lutab[LUCACHE]; #endif /* USE_LUCACHE */ static VOID luinit() { /* Initialize lookup() cache */ int x, y; #ifdef USE_LUCACHE x = lookup(cmdtab,"if",ncmd,&y); lucmd[lusize] = "if"; luval[lusize] = x; luidx[lusize] = y; lutab[lusize] = cmdtab; if (++lusize > LUCACHE) return; x = lookup(iftab,"not",nif,&y); lucmd[lusize] = "not"; luval[lusize] = x; luidx[lusize] = y; lutab[lusize] = iftab; if (++lusize > LUCACHE) return; x = lookup(vartab,"cmdlevel",nvars,&y); lucmd[lusize] = "cmdlevel"; luval[lusize] = x; luidx[lusize] = y; lutab[lusize] = vartab; if (++lusize > LUCACHE) return; x = lookup(cmdtab,"goto",ncmd,&y); lucmd[lusize] = "goto"; luval[lusize] = x; luidx[lusize] = y; lutab[lusize] = cmdtab; if (++lusize > LUCACHE) return; x = lookup(iftab,">",nif,&y); lucmd[lusize] = ">"; luval[lusize] = x; luidx[lusize] = y; lutab[lusize] = iftab; if (++lusize > LUCACHE) return; x = lookup(cmdtab,"incr",ncmd,&y); lucmd[lusize] = "incr"; luval[lusize] = x; luidx[lusize] = y; lutab[lusize] = cmdtab; if (++lusize > LUCACHE) return; x = lookup(cmdtab,"def",ncmd,&y); lucmd[lusize] = "def"; luval[lusize] = x; luidx[lusize] = y; lutab[lusize] = cmdtab; if (++lusize > LUCACHE) return; x = lookup(cmdtab,"_assign",ncmd,&y); lucmd[lusize] = "_assign"; luval[lusize] = x; luidx[lusize] = y; lutab[lusize] = cmdtab; if (++lusize > LUCACHE) return; x = lookup(cmdtab,"echo",ncmd,&y); lucmd[lusize] = "echo"; luval[lusize] = x; luidx[lusize] = y; lutab[lusize] = cmdtab; if (++lusize > LUCACHE) return; x = lookup(fnctab,"eval",nfuncs,&y); lucmd[lusize] = "eval"; luval[lusize] = x; luidx[lusize] = y; lutab[lusize] = fnctab; if (++lusize > LUCACHE) return; x = lookup(fnctab,"lit",nfuncs,&y); lucmd[lusize] = "lit"; luval[lusize] = x; luidx[lusize] = y; lutab[lusize] = fnctab; if (++lusize > LUCACHE) return; x = lookup(cmdtab,"do",ncmd,&y); lucmd[lusize] = "do"; luval[lusize] = x; luidx[lusize] = y; lutab[lusize] = cmdtab; if (++lusize > LUCACHE) return; x = lookup(cmdtab,"_getargs",ncmd,&y); lucmd[lusize] = "_getargs"; luval[lusize] = x; luidx[lusize] = y; lutab[lusize] = cmdtab; if (++lusize > LUCACHE) return; x = lookup(iftab,"<",nif,&y); lucmd[lusize] = "<"; luval[lusize] = x; luidx[lusize] = y; lutab[lusize] = iftab; if (++lusize > LUCACHE) return; x = lookup(cmdtab,"_putargs",ncmd,&y); lucmd[lusize] = "_putargs"; luval[lusize] = x; luidx[lusize] = y; lutab[lusize] = cmdtab; if (++lusize > LUCACHE) return; x = lookup(cmdtab,"asg",ncmd,&y); lucmd[lusize] = "asg"; luval[lusize] = x; luidx[lusize] = y; lutab[lusize] = cmdtab; if (++lusize > LUCACHE) return; x = lookup(cmdtab,"else",ncmd,&y); lucmd[lusize] = "else"; luval[lusize] = x; luidx[lusize] = y; lutab[lusize] = cmdtab; #endif /* USE_LUCACHE */ } VOID cmdini() { int i = 0, x = 0, y = 0, z = 0, skip = 0; char * p; #ifdef TTSPDLIST long * ss = NULL; extern int nspd; extern struct keytab * spdtab; #endif /* TTSPDLIST */ #ifndef NOSPL /* On stack to allow recursion! */ char vnambuf[VNAML]; /* Buffer for variable names */ #endif /* NOSPL */ if (cmdinited) /* Already initialized */ return; /* Don't do it again */ for (i = 0; i < CMDSTKL; i++) /* Prompt strings for each */ prstring[i] = NULL; /* command level */ #ifndef NOCSETS p = getenv("K_CHARSET"); /* Set default file character set */ if (p) { /* from environment */ x = lookup(fcstab,p,nfilc,&y); if (x > -1) fcharset = x; } #endif /* NOCSETS */ p = getenv("K_INFO_DIRECTORY"); /* Find Kermit info directory */ if (p && *p && strlen(p) <= CKMAXPATH) makestr(&k_info_dir,p); if (!k_info_dir) { p = getenv("K_INFO_DIR"); if (p && *p && strlen(p) <= CKMAXPATH) makestr(&k_info_dir,p); } #ifdef UNIX if (k_info_dir) { /* Look for Kermit docs directory */ if (zchki(k_info_dir) == -2) { char xbuf[CKMAXPATH+32], *s = ""; if (ckrchar(k_info_dir) != '/') s = "/"; ckmakmsg(xbuf,CKMAXPATH+32,k_info_dir,s,"ckubwr.txt",NULL); if (zchki(xbuf) < 0) makestr(&k_info_dir,NULL); } } if (!k_info_dir) { char xbuf[CKMAXPATH+32]; int i; for (i = 0; *(txtdir[i]); i++) { ckmakmsg(xbuf,CKMAXPATH+32,txtdir[i],"ckubwr.txt",NULL,NULL); if (zchki(xbuf) > 0) { makestr(&k_info_dir,txtdir[i]); debug(F110,"k_info_dir 1",k_info_dir,0); break; } ckmakmsg(xbuf,CKMAXPATH+32, txtdir[i],"kermit/","ckubwr.txt",NULL); if (zchki(xbuf) > 0) { ckmakmsg(xbuf,CKMAXPATH+32,txtdir[i],"kermit/",NULL,NULL); makestr(&k_info_dir,xbuf); debug(F110,"k_info_dir 2",k_info_dir,0); break; } ckmakmsg(xbuf,CKMAXPATH+32, txtdir[i],"ckermit/","ckubwr.txt",NULL); if (zchki(xbuf) > 0) { ckmakmsg(xbuf,CKMAXPATH+32,txtdir[i],"ckermit/",NULL,NULL); makestr(&k_info_dir,xbuf); debug(F110,"k_info_dir 3",k_info_dir,0); break; } } if (k_info_dir) { /* Make sure it ends with "/" */ if (ckrchar(k_info_dir) != '/') { char xbuf[CKMAXPATH+32]; ckmakmsg(xbuf,CKMAXPATH+32,k_info_dir,"/",NULL,NULL); makestr(&k_info_dir,xbuf); } } } #else #ifdef OS2 { char xdir[CKMAXPATH+8], *s = ""; extern char startupdir[]; xdir[0] = NUL; if (ckrchar(startupdir) != '/') s = "/"; if (strlen(s) + strlen(startupdir) + 5 < CKMAXPATH + 8 ) ckmakmsg(xdir,CKMAXPATH+8,s,startupdir,"DOC/",NULL); makestr(&k_info_dir,xdir); } #endif /* OS2 */ #endif /* UNIX */ #ifdef TTSPDLIST if (!spdtab && (ss = ttspdlist())) { /* Get speed list if necessary */ int j, k, m = 0, n; /* Create sorted keyword table */ char buf[16]; char * p; if ((spdtab = (struct keytab *) malloc(sizeof(struct keytab) * ss[0]))) { for (i = 1; i <= ss[0]; i++) { /* ss[0] = number of elements */ if (ss[i] < 1L) break; /* Shouldn't happen */ buf[0] = NUL; /* Make string */ sprintf(buf,"%ld",ss[i]); /* SAFE */ if (ss[i] == 8880L) ckstrncpy(buf,"75/1200",sizeof(buf)); if (ss[i] == 134L) ckstrncat(buf,".5",16); n = strlen(buf); if ((n > 0) && (p = (char *)malloc(n+1))) { if (m > 0) { /* Have at least one in list */ for (j = 0; /* Find slot */ j < m && strcmp(buf,spdtab[j].kwd) > 0; j++ ) ; if (j < m) { /* Must insert */ for (k = m-1; k >= j; k--) { /* Move others down */ spdtab[k+1].kwd = spdtab[k].kwd; spdtab[k+1].flgs = spdtab[k].flgs; spdtab[k+1].kwval = spdtab[k].kwval; } } } else /* First one */ j = 0; ckstrncpy(p,buf,n+1); /* Add new speed */ spdtab[j].kwd = p; spdtab[j].flgs = 0; spdtab[j].kwval = (int) ss[i] / 10; m++; /* Count this one */ } } } nspd = m; } #endif /* TTSPDLIST */ #ifndef NOSPL /* Allocate INPUT command buffer */ if (!inpbuf) { if (!(inpbuf = (char *) malloc(INPBUFSIZ+8))) fatal("cmdini: no memory for INPUT buffer"); } for (x = 0; x < INPBUFSIZ; x++) /* Initialize it */ inpbuf[x] = NUL; inpbp = inpbuf; /* Initialize pointer */ inbufsize = INPBUFSIZ; /* and size. */ #endif /* NOSPL */ #ifdef DCMDBUF if (cmsetup() < 0) fatal("Can't allocate command buffers!"); #ifndef NOSPL /* Allocate command stack allowing command parser to call itself */ if (!(cmdstk = (struct cmdptr *) malloc(sizeof(struct cmdptr)*CMDSTKL))) fatal("cmdini: no memory for cmdstk"); if (!(ifcmd = (int *) malloc(sizeof(int)*CMDSTKL))) fatal("cmdini: no memory for ifcmd"); if (!(count = (int *) malloc(sizeof(int)*CMDSTKL))) fatal("cmdini: no memory for count"); if (!(iftest = (int *) malloc(sizeof(int)*CMDSTKL))) fatal("cmdini: no memory for iftest"); if (!(intime = (int *) malloc(sizeof(int)*CMDSTKL))) fatal("cmdini: no memory for intime"); if (!(inpcas = (int *) malloc(sizeof(int)*CMDSTKL))) fatal("cmdini: no memory for inpcas"); if (!(takerr = (int *) malloc(sizeof(int)*CMDSTKL))) fatal("cmdini: no memory for takerr"); if (!(merror = (int *) malloc(sizeof(int)*CMDSTKL))) fatal("cmdini: no memory for merror"); if (!(xquiet = (int *) malloc(sizeof(int)*CMDSTKL))) fatal("cmdini: no memory for xquiet"); if (!(xvarev = (int *) malloc(sizeof(int)*CMDSTKL))) fatal("cmdini: no memory for xvarev"); if (!kermrc) if (!(kermrc = (char *) malloc(KERMRCL+1))) fatal("cmdini: no memory for kermrc"); #ifdef CK_APC /* Application Program Command buffer */ if (!(apcbuf = malloc(APCBUFLEN + 1))) fatal("cmdini: no memory for apcbuf"); #endif /* CK_APC */ #endif /* NOSPL */ /* line[] and tmpbuf[] are the two string buffers used by the command parser */ if (!(line = malloc(LINBUFSIZ + 1))) fatal("cmdini: no memory for line"); if (!(tmpbuf = malloc(LINBUFSIZ + 1))) fatal("cmdini: no memory for tmpbuf"); #endif /* DCMDBUF */ #ifndef NOSPL #ifdef CK_MINPUT { /* Initialize MINPUT pointers */ int i; extern char *ms[]; for (i = 0; i < MINPMAX; i++) ms[i] = NULL; } #endif /* CK_MINPUT */ if (macini() < 0) /* Allocate macro buffers */ fatal("Can't allocate macro buffers!"); ifcmd[0] = 0; /* Command-level related variables. */ iftest[0] = 0; /* Initialize variables at top level */ count[0] = 0; /* of stack... */ intime[0] = 0; inpcas[0] = 0; takerr[0] = 0; merror[0] = 0; xquiet[0] = quiet; xvarev[0] = vareval; #endif /* NOSPL */ #ifndef NOSPL cmdlvl = 0; /* Initialize the command stack */ xcmdsrc = CMD_KB; cmdstk[cmdlvl].src = CMD_KB; /* Source is console */ cmdstk[cmdlvl].lvl = 0; /* Level is 0 */ cmdstk[cmdlvl].ccflgs = 0; /* No flags */ #endif /* NOSPL */ tlevel = -1; /* Take file level = keyboard */ for (i = 0; i < MAXTAKE; i++) /* Initialize command file names */ tfnam[i] = NULL; cmsetp(ckprompt); /* Set up C-Kermit's prompt */ /* Can't set IKSD prompt here since */ /* we do not yet know if we are IKSD */ #ifndef NOSPL initmac(); /* Initialize macro table */ /* Predefine built-in one-line macros */ addmac("ibm-linemode",m_ibm); /* IBM-LINEMODE */ addmac("fatal",m_fat); /* FATAL macro */ y = addmac("fast",m_fast); /* FAST macro */ addmac("cautious",m_cautious); /* CAUTIOUS macro */ addmac("robust",m_robust); /* ROBUST macro */ #ifdef OS2 addmac("manual",m_manual); /* MANUAL macro */ #endif /* OS2 */ #ifdef VMS addmac("purge",m_purge); /* PURGE macro */ #endif /* VMS */ /* Predefine built-in multiline macros; these are top-level commands that are implemented internally as macros. NOTE: When adding a new one of these, remember to update the END and RETURN commands to account for it, or else END and RETURN from within it won't work right. */ x = addmmac("_forx",for_def); /* FOR macro */ if (x > -1) mactab[x].flgs = CM_INV; x = addmmac("_forz",foz_def); /* Other FOR macro */ if (x > -1) mactab[x].flgs = CM_INV; x = addmmac("_xif",xif_def); /* XIF macro */ if (x > -1) mactab[x].flgs = CM_INV; x = addmmac("_while",whil_def); /* WHILE macro */ if (x > -1) mactab[x].flgs = CM_INV; x = addmmac("_switx",sw_def); /* SWITCH macro */ if (x > -1) mactab[x].flgs = CM_INV; /* Fill in command-line argument vector */ sprintf(vnambuf,"\\&@[%d]",xargs); /* SAFE */ if (inserver) { /* But hidden in IKSD */ y = -1; xargs = 0; } else y = arraynam(vnambuf,&x,&z); /* goes in array \&@[] */ tmpbuf[0] = NUL; if (y > -1) { int j = -1; int yy = 0; dclarray((char)x,z); /* Declare the array */ #ifndef NOTAKEARGS /* Macro argument vector */ sprintf(vnambuf,"\\&_[%d]",z); /* SAFE */ yy = arraynam(vnambuf,&x,&z); /* goes in array \&_[] */ if (yy > -1) /* Name is OK */ dclarray((char)x,z); /* Declare the array */ #endif /* NOTAKEARGS */ skip = 0; for (i = 0; i < xargs; i++) { /* Fill the arrays */ sprintf(vnambuf,"\\&@[%d]",i); /* SAFE */ addmac(vnambuf,xargv[i]); if (cfilef && i == 0) continue; #ifdef KERBANG if (skip) { j = 0; skip = 0; continue; } #endif /* KERBANG */ if (j < 0 && /* Assign items after "=" or "--"*/ (!strcmp(xargv[i],"=") || !strcmp(xargv[i],"--")) ) { j = 0; /* to \%1..\%9 */ #ifdef KERBANG } else if (j < 0 && (!strcmp(xargv[i],"+") || !strncmp(xargv[i],"+ ",2) || !strncmp(xargv[i],"+\t",2)) ) { skip = 1; continue; #endif /* KERBANG */ } else if (j > -1) { j++; if (j <= 9) { vnambuf[0] = '\\'; vnambuf[1] = '%'; vnambuf[2] = (char)(j+'0'); vnambuf[3] = NUL; addmac(vnambuf,xargv[i]); } if (yy > -1) { char c, * p; int flag = 0; p = xargv[i]; makestr(&(toparg[j]),p); while ((c = *p++)) { if (c == SP) { flag++; break; } } if (flag) ckstrncat(tmpbuf,"\"",TMPBUFSIZ); ckstrncat(tmpbuf,xargv[i],TMPBUFSIZ); if (flag) ckstrncat(tmpbuf,"\"",TMPBUFSIZ); ckstrncat(tmpbuf," ",TMPBUFSIZ); } } } if (cfilef) { addmac("\\%0",cmdfil); if (yy > -1) makestr(&(toparg[0]),cmdfil); } else { addmac("\\%0",xargv[0]); if (yy > -1) makestr(&(toparg[0]),xargv[0]); } if (yy > -1) { topargc = (j < 0) ? 1 : j + 1; topxarg = toparg; #ifdef COMMENT /* This needs work */ if (!cfilef) makestr(&topline,tmpbuf); #endif /* COMMENT */ } else { topargc = 0; topxarg = NULL; } a_dim[0] = topargc - 1; a_ptr[0] = topxarg; debug(F111,"a_dim[0]","A",a_dim[0]); } *vnambuf = NUL; #endif /* NOSPL */ luinit(); /* Initialize lookup() cache */ /* Get our home directory now. This needed in lots of places. */ cmdinited = 1; } #ifdef NT _PROTOTYP(char * GetAppData,(int)); #endif /* NT */ VOID doinit() { #ifdef CKROOT extern int ckrooterr; #endif /* CKROOT */ int x = 0, ok = 0; #ifdef OS2 char * ptr = 0; #endif /* OS2 */ if (!cmdinited) cmdini(); #ifdef MAC return; /* Mac Kermit has no init file */ #else /* !MAC */ /* If skipping init file ('-Y' on Kermit command line), return now. */ if (noinit) { kermrc[0] = '\0'; inidir[0] = '\0'; /* But returning from here results in inidir[] never being set to anything. Instead it should be set to wherever the init file *would* have been executed from. So this bit of code should be removed, and then we should sprinkle "if (noinit)" tests throughout the following code until we have set inidir[], and then return without actually taking the init file. */ return; } #ifdef OS2 /* The -y init file must be fully specified or in the current directory. KERMRC is looked for via INIT, DPATH and PATH in that order. Finally, our own executable file path is taken and the .EXE suffix is replaced by .INI and this is tried as the initialization file. */ #ifdef CK_LOGIN debug(F101,"doinit inserver","",inserver); debug(F101,"doinit isguest","",isguest); debug(F110,"doinit anonfile",anonfile,0); if (isguest && anonfile) { ckstrncpy(line, anonfile, LINBUFSIZ+1); } else #endif /* CK_LOGIN */ if (rcflag) { ckstrncpy(line,kermrc,LINBUFSIZ+1); #ifdef CK_LOGIN } else if (inserver) { char * appdata = NULL; #ifdef NT appdata = GetAppData(1); if ( appdata ) { ckmakmsg(line,LINBUFSIZ+1,appdata, "Kermit 95/k95.ini",NULL,NULL); if ( zchki(line) < 0 ) line[0] = '\0'; } if (line[0] == 0) { appdata = GetAppData(0); if ( appdata ) { ckmakmsg(line,LINBUFSIZ+1,appdata, "Kermit 95/k95.ini",NULL,NULL); if ( zchki(line) < 0 ) line[0] = '\0'; } } #endif /* NT */ if (line[0] == 0) { appdata = zhome(); if ( appdata ) { ckmakmsg(line,LINBUFSIZ+1,appdata, #ifdef NT "k95.ini", #else /* NT */ "k2.ini", #endif /* NT */ NULL,NULL); if ( zchki(line) < 0 ) line[0] = '\0'; } } debug(F110,"doinit inserver inifile",line,0); #endif /* CK_LOGIN */ } else { char * env = 0; #ifdef NT env = getenv("K95.KSC"); #else env = getenv("K2.KSC"); #endif /* NT */ if (!env) { #ifdef NT env = getenv("K95.INI"); #else env = getenv("K2.INI"); #endif /* NT */ } if (!env) env = getenv("CKERMIT.INI"); if (!env) env = getenv("CKERMIT_INI"); line[0] = '\0'; debug(F110,"doinit env",env,0); if (env) ckstrncpy(line,env,LINBUFSIZ+1); #ifdef NT if (line[0] == 0) { env = GetAppData(1); if ( env ) { ckmakmsg(line,LINBUFSIZ+1,env,"Kermit 95/k95.ini",NULL,NULL); if ( zchki(line) < 0 ) line[0] = '\0'; } } if (line[0] == 0) { env = GetAppData(0); if ( env ) { ckmakmsg(line,LINBUFSIZ+1,env,"Kermit 95/k95.ini",NULL,NULL); if ( zchki(line) < 0 ) line[0] = '\0'; } } #endif /* NT */ if (line[0] == 0) { env = zhome(); if ( env ) { ckmakmsg(line,LINBUFSIZ+1,env, #ifdef NT "k95.ini", #else /* NT */ "k2.ini", #endif /* NT */ NULL,NULL); if ( zchki(line) < 0 ) line[0] = '\0'; } } if (line[0] == 0) _searchenv(kermrc,"INIT",line); if (line[0] == 0) _searchenv(kermrc,"DPATH",line); if (line[0] == 0) _searchenv(kermrc,"PATH",line); if (line[0] == 0) { char *pgmptr = GetLoadPath(); if (pgmptr && strlen(pgmptr) < LINBUFSIZ-8) { lp = strrchr(pgmptr, '\\'); if (lp) { strncpy(line, pgmptr, lp - pgmptr); #ifdef NT strcpy(line + (lp - pgmptr), "/k95.ini"); #else /* NT */ strcpy(line + (lp - pgmptr), "/k2.ini"); #endif /* NT */ } else { lp = strrchr(pgmptr, '.'); if (lp) { strncpy(line, pgmptr, lp - pgmptr); strcpy(line + (lp - pgmptr), ".ini"); } } } } } #ifdef CKROOT if (!zinroot(line)) { debug(F110,"doinit setroot violation",line,0); return; } #endif /* CKROOT */ debug(F110,"doinit fopen()",line,0); if ((tfile[0] = fopen(line,"r")) != NULL) { ok = 1; tlevel = 0; tfline[tlevel] = 0; if (tfnam[tlevel] = malloc(strlen(line)+1)) strcpy(tfnam[tlevel],line); /* safe */ #ifndef NOSPL cmdlvl++; xcmdsrc = CMD_TF; cmdstk[cmdlvl].src = CMD_TF; cmdstk[cmdlvl].lvl = tlevel; cmdstk[cmdlvl].ccflgs = 0; ifcmd[cmdlvl] = 0; iftest[cmdlvl] = 0; count[cmdlvl] = count[cmdlvl-1]; /* Inherit from previous level */ intime[cmdlvl] = intime[cmdlvl-1]; inpcas[cmdlvl] = inpcas[cmdlvl-1]; takerr[cmdlvl] = takerr[cmdlvl-1]; merror[cmdlvl] = merror[cmdlvl-1]; xquiet[cmdlvl] = quiet; xvarev[cmdlvl] = vareval; #endif /* NOSPL */ debug(F110,"doinit init file",line,0); } else { debug(F100,"doinit no init file","",0); } ckstrncpy(kermrc,line,KERMRCL); for (ptr = kermrc; *ptr; ptr++) /* Convert backslashes to slashes */ if (*ptr == '\\') *ptr = '/'; #else /* not OS2 */ lp = line; lp[0] = '\0'; debug(F101,"doinit rcflag","",rcflag); #ifdef GEMDOS zkermini(line, rcflag, kermrc); #else #ifdef VMS { int x; x = zkermini(line,LINBUFSIZ,kermrc); debug(F111,"CUSTOM zkermini",line,x); if (x == 0) line[0] = NUL; } #else /* not VMS */ #ifdef CK_LOGIN debug(F101,"doinit isguest","",isguest); if (isguest) ckstrncpy(lp, anonfile ? anonfile : kermrc, LINBUFSIZ); else #endif /* CK_LOGIN */ if (rcflag) { /* If init file name from cmd line */ ckstrncpy(lp,kermrc,LINBUFSIZ); /* use it, */ } else { /* otherwise... */ #ifdef CK_INI_A /* If we've a system-wide init file */ /* And it takes precedence over the user's... */ ckstrncpy(lp,CK_SYSINI,KERMRCL); /* Use it */ if (zchki(lp) < 0) { /* (if it exists...) */ #endif /* CK_INI_A */ char * homdir; char * env = 0; line[0] = NUL; /* Add support for environment variable */ env = getenv("CKERMIT.INI"); if (!env) env = getenv("CKERMIT_INI"); if (env) ckstrncpy(lp,env,KERMRCL); if (lp[0] == 0) { homdir = zhome(); if (homdir) { /* Home directory for init file. */ ckstrncpy(lp,homdir,KERMRCL); #ifdef STRATUS ckstrncat(lp,">",KERMRCL);/* VOS dirsep */ #else if (lp[0] == '/') ckstrncat(lp,"/",KERMRCL); #endif /* STRATUS */ } ckstrncat(lp,kermrc,KERMRCL);/* Append default file name */ } #ifdef CK_INI_A } #endif /* CK_INI_A */ #ifdef CK_INI_B /* System-wide init defined? */ /* But user's ini file takes precedence */ if (zchki(lp) < 0) /* If user doesn't have her own, */ ckstrncpy(lp,CK_SYSINI,KERMRCL); /* use system-wide one. */ #endif /* CK_INI_B */ } #endif /* VMS */ #endif /* GEMDOS */ #ifdef AMIGA reqoff(); /* Disable requestors */ #endif /* AMIGA */ #ifdef USE_CUSTOM /* If no init file was found, execute the customization file */ debug(F111,"CUSTOM 1",line,rcflag); if ((!line[0] || zchki(line) < 0) && !rcflag) { int x; #ifdef OS2 x = ckmakestr(line,LINBUFSIZ,GetAppData(1),"/","K95CUSTOM.INI",NULL); debug(F111,"CUSTOM 2",line,x); if (zchki(line) < 0) { x = ckmakestr(line,LINBUFSIZ,GetAppData(0),"/","K95USER.INI",NULL); debug(F111,"CUSTOM 3",line,x); } #else /* OS2 */ x = ckstrncpy(line,zhome(),LINBUFSIZ); #ifndef VMS /* VMS zhome() returns "SYS$LOGIN:" */ if (line[x-1] != DIRSEP) { line[x++] = DIRSEP; line[x] = NUL; } #endif /* VMS */ x = ckstrncat(line,MYCUSTOM,LINBUFSIZ); debug(F111,"CUSTOM 4",line,x); #endif /* OS2 */ } debug(F110,"CUSTOM 5",line,0); #endif /* USE_CUSTOM */ #ifdef CKROOT if (!zinroot(line)) { debug(F110,"doinit setroot violation",line,0); return; } #endif /* CKROOT */ debug(F110,"doinit ini file is",line,0); if ((tfile[0] = fopen(line,"r")) != NULL) { /* Try to open init file. */ ok = 1; tlevel = 0; tfline[tlevel] = 0; if ((tfnam[tlevel] = malloc(strlen(line)+1))) strcpy(tfnam[tlevel],line); /* safe */ ckstrncpy(kermrc,line,KERMRCL); #ifndef NOSPL cmdlvl++; ifcmd[cmdlvl] = 0; iftest[cmdlvl] = 0; count[cmdlvl] = count[cmdlvl-1]; /* Inherit from previous level */ intime[cmdlvl] = intime[cmdlvl-1]; inpcas[cmdlvl] = inpcas[cmdlvl-1]; takerr[cmdlvl] = takerr[cmdlvl-1]; merror[cmdlvl] = merror[cmdlvl-1]; xquiet[cmdlvl] = quiet; xvarev[cmdlvl] = vareval; debug(F101,"doinit open ok","",cmdlvl); xcmdsrc = CMD_TF; cmdstk[cmdlvl].src = CMD_TF; cmdstk[cmdlvl].lvl = tlevel; cmdstk[cmdlvl].ccflgs = 0; #endif /* NOSPL */ } else if (rcflag) { /* Print an error message only if a specific file was asked for. */ printf("?%s - %s\n", ck_errstr(), line); } #ifdef datageneral /* If CKERMIT.INI not found in home directory, look in searchlist */ if (/* homdir && */ (tlevel < 0)) { ckstrncpy(lp,kermrc,LINBUFSIZ); if ((tfile[0] = fopen(line,"r")) != NULL) { ok = 1; tlevel = 0; tfline[tlevel] = 0; if (tfnam[tlevel] = malloc(strlen(line)+1)) strcpy(tfnam[tlevel],line); /* safe */ #ifndef NOSPL cmdlvl++; xcmdsrc = CMD_TF; cmdstk[cmdlvl].src = CMD_TF; cmdstk[cmdlvl].lvl = tlevel; cmdstk[cmdlvl].ccflgs = 0; ifcmd[cmdlvl] = 0; iftest[cmdlvl] = 0; count[cmdlvl] = count[cmdlvl-1]; /* Inherit from previous level */ intime[cmdlvl] = intime[cmdlvl-1]; inpcas[cmdlvl] = inpcas[cmdlvl-1]; takerr[cmdlvl] = takerr[cmdlvl-1]; merror[cmdlvl] = merror[cmdlvl-1]; xquiet[cmdlvl] = quiet; xvarev[cmdlvl] = vareval; #endif /* NOSPL */ } } #endif /* datageneral */ #ifdef AMIGA /* Amiga... */ reqpop(); /* Restore requestors */ #endif /* AMIGA */ #endif /* OS2 */ #endif /* MAC */ /* Assign value to inidir */ if (!ok) { inidir[0] = NUL; } else { ckstrncpy(inidir, kermrc, CCHMAXPATH); x = strlen(inidir); if (x > 0) { int i; for (i = x - 1; i > 0; i-- ) { if (ISDIRSEP(inidir[i])) { inidir[i+1] = NUL; break; } } } #ifdef NT GetShortPathName(inidir,inidir,CCHMAXPATH); #endif /* NT */ } } VOID doiksdinit() { #ifdef CK_SSL /* IKSD doesn't request client certs */ ssl_verify_flag = SSL_VERIFY_NONE; #endif /* CK_SSL */ if (!cmdinited) cmdini(); #ifdef IKSDCONF #ifdef OS2 line[0] = '\0'; _searchenv(iksdconf,"INIT",line); if (line[0] == 0) _searchenv(iksdconf,"DPATH",line); if (line[0] == 0) _searchenv(iksdconf,"PATH",line); if (line[0] == 0) { char *pgmptr = GetLoadPath(); if (pgmptr && strlen(pgmptr) < LINBUFSIZ-8) { lp = strrchr(pgmptr, '\\'); if (lp) { strncpy(line, pgmptr, lp - pgmptr); strcpy(line + (lp - pgmptr), "\\"); strcpy(line + (lp - pgmptr + 1), iksdconf); } else { lp = strrchr(pgmptr, '.'); if (lp) { strncpy(line, pgmptr, lp - pgmptr); strcpy(line + (lp - pgmptr), ".ksc"); } } } } debug(F110,"doiksdinit() line",line,0); tfile[0] = fopen(line,"r"); #else /* OS2 */ tfile[0] = fopen(iksdconf,"r"); #endif /* OS2 */ if (tfile[0] != NULL) { tlevel = 0; tfline[tlevel] = 0; #ifdef OS2 if (tfnam[tlevel] = malloc(strlen(line)+1)) strcpy(tfnam[tlevel],line); #else /* OS2 */ if ((tfnam[tlevel] = malloc(strlen(iksdconf)+1))) strcpy(tfnam[tlevel],iksdconf); #endif /* OS2 */ #ifndef NOSPL cmdlvl++; xcmdsrc = CMD_TF; cmdstk[cmdlvl].src = CMD_TF; cmdstk[cmdlvl].lvl = tlevel; cmdstk[cmdlvl].ccflgs = 0; ifcmd[cmdlvl] = 0; iftest[cmdlvl] = 0; count[cmdlvl] = count[cmdlvl-1]; /* Inherit from previous level */ intime[cmdlvl] = intime[cmdlvl-1]; inpcas[cmdlvl] = inpcas[cmdlvl-1]; takerr[cmdlvl] = takerr[cmdlvl-1]; merror[cmdlvl] = merror[cmdlvl-1]; xquiet[cmdlvl] = quiet; xvarev[cmdlvl] = vareval; #endif /* NOSPL */ debug(F110,"doiksdinit file ok",tfnam[tlevel],0); } else { debug(F110,"doiksdinit open failed",tfnam[tlevel],0); } #endif /* IKSDCONF */ } #ifndef NOSPL /* G E T N C M Get next command from current macro definition. Command is copied into string pointed to by argument s, max length n. Returns: 0 if a string was copied; -1 if there was no string to copy. */ int getncm(s,n) char *s; int n; { int y = 0; /* Character counter */ int quote = 0; int kp = 0; /* Brace up-down counter */ int pp = 0; /* Parenthesis up-down counter */ #ifndef NODQMACRO int dq = 0; /* Doublequote counter */ #endif /* NODQMACRO */ char *s2; /* Copy of destination pointer */ s2 = s; /* Initialize string pointers */ *s = NUL; /* and destination buffer */ /* debug(F010,"getncm entry",macp[maclvl],0); */ for (y = 0; /* Loop for n bytes max */ macp[maclvl] && *macp[maclvl] && y < n; y++, s++, macp[maclvl]++) { *s = *macp[maclvl]; /* Get next char from macro def */ #ifndef COMMENT /* This is to allow quoting of parentheses, commas, etc, in function arguments, but it breaks just about everything else. DON'T REMOVE THIS COMMENT! (Otherwise you'll wind up adding the same code again and breaking everything again.) <-- The preceding warning should be obsolete since the statements below have been fixed, but in case of fire, remove the "n" from the <#>ifndef above. NEW WARNING: code added 12 Apr 2002 to exempt the opening brace in \{nnn} from being treated as a quoted brace. */ if (!quote && *s == CMDQ) { quote = 1; continue; } if (quote) { int notquote = 0; quote = 0; if (*s == '{') { /* Check for \{nnn} (8.0.203) */ char c, * p; p = macp[maclvl] + 1; while ((c = *p++)) { if (isdigit(c)) continue; else if (c == '}') { notquote++; break; } else { break; } } } if (notquote == 0) continue; } #endif /* COMMENT */ /* Allow braces around macro definition to prevent commas from being turned to end-of-lines and also treat any commas within parens as text so that multiple-argument functions won't cause the command to break prematurely. 19 Oct 2001: Similar treatment was added for doublequotes, so define foo { echo "one, two, three" } would work as expected. This doesn't seem to have broken anything but if something comes up later, rebuild with NODQMACRO defined. */ if (*s == '{') kp++; /* Count braces */ if (*s == '}' && kp > 0) kp--; if (*s == '(') pp++; /* Count parentheses. */ if (*s == ')' && pp > 0) pp--; #ifndef NODQMACRO #ifndef COMMENT /* Too many false positives */ /* No, not really -- this is indeed the best we can do */ /* Reverted to this method Sun May 11 18:43:45 2003 */ if (*s == '"') dq = 1 - dq; /* Account for doublequotes */ #else /* Fri Apr 4 13:21:29 2003 */ /* The code below breaks the SWITCH statement */ /* There is no way to make this work -- it would require */ /* building in all the knowledge of command parser. */ if (dblquo && (*s == '"')) { /* Have doublequote */ if (dq == 1) { /* Close quote only if... */ if ((*(macp[maclvl]+1) == SP) || /* followed by space or... */ (!*(macp[maclvl]+1)) || /* at end or ... */ /* Next char is command separator... */ /* Sun May 11 17:24:12 2003 */ (kp < 1 && pp < 1 && (*(macp[maclvl]+1) == ',')) ) dq = 0; /* Close the quote */ } else if (dq == 0) { /* Open quote only if at beginning or preceded by space */ if (s > s2) { if (*(s-1) == SP) dq = 1; } else if (s == s2) { dq = 1; } } } #endif /* COMMENT */ #endif /* NODQMACRO */ if (*s == ',' && pp <= 0 && kp <= 0 #ifndef NODQMACRO && dq == 0 #endif /* NODQMACRO */ ) { macp[maclvl]++; /* Comma not in {} or () */ /* debug(F110,"next cmd",s,0); */ kp = pp = 0; /* so we have the next command */ break; } } /* Reached end. */ #ifdef COMMENT /* DON'T DO THIS - IT BREAKS EVERYTHING */ *s = NUL; #endif /* COMMENT */ if (*s2 == NUL) { /* If nothing was copied, */ /* debug(F100,"XXX getncm eom","",0); */ popclvl(); /* pop command level. */ return(-1); } else { /* otherwise, tack CR onto end */ *s++ = CR; *s = '\0'; /* debug(F110,"XXX getncm OK",s,0); */ if (mecho && pflag) /* If MACRO ECHO ON, echo the cmd */ printf("%s\n",s2); } return(0); } /* D O M A C -- Define and then execute a macro */ int domac(name, def, flags) char *name, *def; int flags; { int x, m; #ifndef NOLOCAL #ifdef OS2 extern int term_io; int term_io_sav = term_io; term_io = 0; /* Disable Terminal Emulator I/O */ #endif /* OS2 */ #endif /* NOLOCAL */ m = maclvl; /* Current macro stack level */ x = addmac(name, def); /* Define a new macro */ if (x > -1) { /* If successful, */ dodo(x,NULL,flags); /* start it (increments maclvl). */ while (maclvl > m) { /* Keep going till done with it, */ debug(F101,"domac loop maclvl 1","",maclvl); sstate = (CHAR) parser(1); /* parsing & executing each command, */ debug(F101,"domac loop maclvl 2","",maclvl); if (sstate) proto(); /* including protocol commands. */ } debug(F101,"domac loop exit maclvl","",maclvl); } #ifndef NOLOCAL #ifdef OS2 term_io = term_io_sav; #endif /* OS2 */ #endif /* NOLOCAL */ return(success); } #endif /* NOSPL */ /* G E T N C T Get next command from TAKE (command) file. Call with: s Pointer to buffer to read into n Length of buffer f File descriptor of file to read from flag 0 == keep line terminator on and allow continuation 1 == discard line terminator and don't allow continuation Call with flag == 0 to read a command from a TAKE file; Call with flag != 0 to read a line from a dialing or network directory. In both cases, trailing comments and/or trailing whitespace is/are stripped. If flag == 0, continued lines are combined into one line. A continued line is one that ends in hypen, or any line in a "block", which starts with "{" at the end of a line and ends with a matching "}" at the beginning of a subsequent line; blocks may be nested. Returns: 0 if a string was copied, -1 on EOF, -2 on malloc failure -3 if line is not properly terminated -4 if (possibly continued) line is too long. */ static int lpxlen = 0; int getnct(s,n,f,flag) char *s; int n; FILE *f; int flag; { int i = 0, len = 0, buflen = 0; char c = NUL, cc = NUL, ccl = NUL, ccx = NUL, *s2 = NULL; char *lp = NULL, *lpx = NULL, *lp2 = NULL, *lp3 = NULL, *lastcomma = NULL; char * prev = NULL; int bc = 0; /* Block counter */ s2 = s; /* Remember original pointer */ prev = s2; /* Here too */ buflen = n; /* Remember original buffer length */ if (n < 0) return(-2); /* Allocate a line buffer only if we don't have one that's big enough */ debug(F111,"getnct",ckitoa(lpxlen),n); if (lpx && (n > lpxlen)) { /* Have one already */ debug(F101,"getnct new buffer","",lpxlen); free(lpx); /* But it's not big enough */ lpx = NULL; /* Free current one */ lpxlen = 0; } if (!lpx) { /* Get new one */ if (!(lpx = (char *) malloc(n))) { debug(F101,"getnct malloc failure","",0); printf("?Memory allocation failure [getnct:%d]\n",n); return(-2); } lpxlen = n; } lp2 = lpx; #ifdef KLUDGE /* NOTE: No longer used as of 14 Aug 2000 */ lp2++; #endif /* KLUDGE */ while (1) { /* Loop to read lines from file */ debug(F101,"getnct while (1)","",n); if (fgets(lp2,n,f) == NULL) { /* Read a line into lp2 */ debug(F110,"getnct EOF",s2,0); /* EOF */ free(lpx); /* Free temporary storage */ lpx = NULL; *s = NUL; /* Make destination be empty */ return(-1); /* Return failure code */ } #ifndef NODIAL if (flag) /* Count this line */ dirline++; else #endif /* NODIAL */ tfline[tlevel]++; len = strlen(lp2) - 1; /* Position of line terminator */ if (len == 0 && lp2[0] != '\n') { /* Last line in file has one char */ lp2[++len] = '\n'; /* that is not a newline */ lp2[len] = NUL; } debug(F010,"getnct",lp2,0); if (len < 0) len = 0; if (techo && pflag) /* If TAKE ECHO ON, */ printf("%3d. %s", /* echo it this line. */ #ifndef NODIAL flag ? dirline : #endif /* NODIAL */ tfline[tlevel], lp2 ); lp3 = lp2; /* Working pointer */ i = len; /* Get first nonwhitespace character */ while (i > 0 && (*lp3 == SP || *lp3 == HT)) { i--; lp3++; } if (i == 0 && bc > 0) /* Blank line in {...} block */ continue; /* Isolate, remove, and check terminator */ c = lp2[len]; /* Value of line terminator */ /* debug(F101,"getnct terminator","",c); */ if (c < LF || c > CR) { /* It's not a terminator */ /* debug(F111,"getnct bad line",lp2,c); */ if (feof(f) && len > 0 && len < n) { /* Kludge Alert... */ if (!quiet) printf("WARNING: Last line of %s lacks terminator\n", s2 == cmdbuf ? "command file" : "directory file"); c = lp2[++len] = '\n'; /* No big deal - supply one. */ } else { /* Something's wrong, fail. */ free(lpx); lpx = NULL; return(-3); } } /* Trim trailing whitespace */ for (i = len - 1; i > -1 && lp2[i] <= SP; i--) /* Trim */ ; /* debug(F101,"getnct i","",i); */ lp2[i+1] = NUL; /* Terminate the string */ /* debug(F110,"getnct lp2",lp2,0); */ lp = lp2; /* Make a working pointer */ /* Remove trailing or full-line comment */ while ((cc = *lp)) { if (cc == ';' || cc == '#') { /* Comment introducer? */ if (lp == lp2) { /* First char on line */ *lp = NUL; break; } else if (*(lp - 1) == SP || *(lp - 1) == HT) { lp--; *lp = NUL; /* Or preceded by whitespace */ break; } } lp++; } if (lp > lp2) lp--; /* Back up over the NUL */ /* Now trim any space that preceded the comment */ while ((*lp == SP || *lp == HT) && lp >= lp2) { *lp = NUL; if (lp <= lp2) break; lp--; } /* debug(F110,"getnct comment trimmed",lp2,0); */ len = strlen(lp2); /* Length after trimming */ if (n - len < 2) { /* Check remaining space */ debug(F111,"getnct command too long",s2,buflen); printf("?Line too long, maximum length: %d.\n",buflen); free(lpx); return(-4); } ccl = (len > 0) ? lp2[len-1] : 0; /* Last character in line */ ccx = (len > 1) ? lp2[len-2] : 0; /* Penultimate char in line */ #ifdef COMMENT /* Line containing only whitespace and ,- */ if ((len > 1) && (lp3 == lp2+len-2) && (ccl == '-') && (ccx == ',')) continue; #endif /* COMMENT */ #ifdef KLUDGE /* If it is a command and it begins with a token (like ! or .) that is not followed by a space, insert a space now; otherwise cmkey() can get mighty confused. */ if (s == s2 && !flag) { char *p = toktab; while (*p) { if (*p == *lp3 && *(p+1) != SP) { debug(F110,"getnct token",p,0); *lp3-- = SP; *lp3 = *p; if (lp3 < lp2) { lp2--; len++; } break; } else p++; } } #endif /* KLUDGE */ lp = lp2; while ((*s++ = *lp++)) /* Copy result to target buffer */ n--; /* accounting for length */ s--; /* Back up over the NUL */ /* Check whether this line is continued */ if (flag) /* No line continuation when flag=1 */ break; /* So break out of read-lines loop */ #ifdef COMMENT debug(F000,"getnct first char","",*lp3); debug(F000,"getnct last char","",ccl); debug(F000,"getnct next-to-last char","",ccx); #endif /* COMMENT */ if (bc > 0 && *lp3 == '}') { /* First char on line is '}' */ bc--; /* Decrement block counter */ } if (bc == 0 && /* Line is continued if bc > 0 */ #ifdef COMMENT /* Not supported as of C-Kermit 6.0 */ ccl != CMDQ && /* or line ends with CMDQ */ #endif /* COMMENT */ ccl != '-' && /* or line ends with dash */ ccl != '{') /* or line ends with opening brace */ break; /* None of those, we're done. */ if (ccl == '-' || ccl == '{') /* Continuation character */ if (ccx == CMDQ) /* But it's quoted */ break; /* so ignore it */ if (ccl == '{') { /* Last char on line is '{'? */ bc++; /* Count the block opener. */ } else if (ccl == '-') { /* Explicit continue? */ char c, * ss; int state = 0, nn; s--; /* Yes, back up over terminators */ n++; /* and over continuation character */ nn = n; /* Save current count */ ss = s; /* and pointer */ s--; /* Back up over dash */ n++; while (state < 2 && s >= prev) { /* Check for "{,-" */ n++; c = *s--; if (c <= SP) continue; if (c != ',' && c != '{') break; switch (state) { case 0: /* Looking for comma */ if (c == ',') state = 1; break; case 1: /* Looking for left brace */ if (c == '{') { state = 2; s += 2; *s = NUL; bc++; } break; } } if (state != 2) { s = ss; n = nn; } *s = NUL; } else { /* None of those but (bc > 0) */ lastcomma = s; *s++ = ','; /* and insert a comma */ n--; } #ifdef COMMENT debug(F101,"getnct bc","",bc); debug(F100,"getnct continued","",0); #endif /* COMMENT */ *s = NUL; prev = s; } /* read-lines while loop */ if (lastcomma) *lastcomma = SP; if (!flag) /* Tack line terminator back on */ *s++ = c; *s++ = NUL; /* Terminate the string */ untab(s2); /* Done, convert tabs to spaces */ #ifdef DEBUG if (!flag) { debug(F010,"CMD(F)",s2,0); } #endif /* DEBUG */ free(lpx); /* Free temporary storage */ return(0); /* Return success */ } VOID shostack() { /* Dump the command stack */ int i; char *p; #ifndef NOSPL for (i = cmdlvl; i > 0; i--) { if (cmdstk[i].src == CMD_TF) { p = tfnam[cmdstk[i].lvl]; if (zfnqfp(p,TMPBUFSIZ,tmpbuf)) p = tmpbuf; printf(" %2d. File : %s (line %d)\n", i, p, tfline[cmdstk[i].lvl] ); } else if (cmdstk[i].src == CMD_MD) { char * m; m = m_arg[cmdstk[i].lvl][0]; /* Name of this macro */ if (i > 0) { /* Special handling for 2-level */ char *s; /* built-in macros... */ s = m_arg[cmdstk[i-1].lvl][0]; /* Name next level up */ if (s && cmdstk[i-1].src == CMD_MD) { if (!strcmp(s,"_forx")) m = "FOR"; else if (!strcmp(s,"_xif")) m = "XIF"; else if (!strcmp(s,"_while")) m = "WHILE"; else if (!strcmp(s,"_switx")) m = "SWITCH"; } } printf(" %2d. Macro : %s\n",i,m); } else if (cmdstk[i].src == CMD_KB) { printf(" %2d. Prompt:\n",i); } else { printf(" %2d. ERROR : Command source unknown\n",i); } } #else for (i = tlevel; i > -1; i--) { p = tfnam[i]; if (zfnqfp(p,TMPBUFSIZ,tmpbuf)) p = tmpbuf; printf(" %2d. File : %s (line %d)\n", i, p, tfline[i] ); } #endif /* NOSPL */ if (i == 0) printf(" %2d. Prompt: (top level)\n",0); } /* For command error messages - avoid dumping out the contents of some */ /* some huge FOR loop if it contains a syntax error. */ static char * cmddisplay(s, cx) char * s; int cx; { static char buf[80]; if ((int)strlen(s) > 70) { sprintf(buf,"%.64s...",s); /* SAFE */ s = buf; } return(s); } static VOID cmderr() { if (xcmdsrc > 0) { switch (cmd_err) { /* SET COMMAND ERROR-DISPLAY */ case 0: break; case 1: case 2: if (tlevel > -1) { #ifndef NOSPL if (xcmdsrc == 2) printf( "In macro or block defined in file: %s starting about line %d\n", tfnam[tlevel] ? tfnam[tlevel] : "", tfline[tlevel] ); else #endif /* NOSPL */ printf("File: %s, Line: %d\n", tfnam[tlevel] ? tfnam[tlevel] : "", tfline[tlevel] ); } #ifndef NOSPL if (cmd_err == 2) { if (cmdstk[cmdlvl].src == CMD_MD) { /* Executing a macro? */ int m; m = cmdstk[cmdlvl].lvl; if (mlook(mactab,m_arg[m][0],nmac) >= 0) printf("Macro name: %s\n", m_arg[m][0]); } } #endif /* NOSPL */ break; case 3: printf("Command stack:\n"); shostack(); } } } /* P A R S E R -- Top-level interactive command parser. */ /* Call with: m = 0 for normal behavior: keep parsing and executing commands until an action command is parsed, then return with a Kermit start-state as the value of this function. m = 1 to parse only one command, can also be used to call parser() recursively. m = 2 to read but do not execute one command. In all cases, parser() returns: 0 if no Kermit protocol action required > 0 with a Kermit protocol start-state. < 0 upon error. */ int parser(m) int m; { int tfcode, xx, yy, zz; /* Workers */ int is_tn = 0; int cdlost = 0; #ifndef NOSPL int inlevel; /* Level we were called at */ extern int askflag, echostars; #endif /* NOSPL */ char *cbp; /* Command buffer pointer */ #ifdef MAC extern char *lfiles; /* Fake extern cast */ #endif /* MAC */ extern int interrupted; #ifndef NOXFER extern int sndcmd, getcmd, fatalio, clearrq; #endif /* NOXFER */ #ifdef AMIGA reqres(); /* Restore AmigaDOS requestors */ #endif /* AMIGA */ #ifdef OS2 if (cursor_save > -1) { /* Restore cursor if it was */ cursorena[VCMD] = cursor_save; /* turned off during file transfer */ cursor_save = -1; } #endif /* OS2 */ #ifdef IKSDB if (ikdbopen) slotstate(what,"COMMAND PROMPT","",""); /* IKSD database */ #endif /* IKSDB */ is_tn = (local && network && IS_TELNET()) || (!local && sstelnet); if (!xcmdsrc) /* If at top (interactive) level ... */ concb((char)escape); /* put console in 'cbreak' mode. */ #ifdef CK_TMPDIR /* If we were cd'd temporarily to another device or directory ... */ if (f_tmpdir) { int x; x = zchdir((char *) savdir); /* ... restore previous directory */ f_tmpdir = 0; /* and remember we did it. */ debug(F111,"parser tmpdir restoring",savdir,x); } #endif /* CK_TMPDIR */ #ifndef NOSPL inlevel = cmdlvl; /* Current macro level */ #ifdef DEBUG if (deblog) { debug(F101,"&parser entry maclvl","",maclvl); debug(F101,"&parser entry inlevel","",inlevel); debug(F101,"&parser entry tlevel","",tlevel); debug(F101,"&parser entry cmdlvl","",cmdlvl); debug(F101,"&parser entry m","",m); } #endif /* DEBUG */ #endif /* NOSPL */ #ifndef NOXFER ftreset(); /* Reset global file settings */ #endif /* NOXFER */ /* sstate becomes nonzero when a command has been parsed that requires some action from the protocol module. Any non-protocol actions, such as local directory listing or terminal emulation, are invoked directly from below. */ sstate = 0; /* Start with no start state. */ #ifndef NOXFER #ifndef NOSPL query = 0; /* QUERY not active */ #endif /* NOSPL */ #ifndef NOHINTS if (!success) { if (local && !network && carrier != CAR_OFF) { int x; /* Serial connection */ x = ttgmdm(); /* with carrier checking */ if (x > -1) { if (!(x & BM_DCD)) { cdlost = 1; fatalio = 1; } } } } #ifdef DEBUG if (deblog) { debug(F101,"parser top what","",what); debug(F101,"parser top interrupted","",interrupted); debug(F101,"parser top cdlost","",cdlost); debug(F101,"parser top sndcmd","",sndcmd); debug(F101,"parser top getcmd","",getcmd); } #endif /* DEBUG */ if (cdlost && !interrupted && (sndcmd || getcmd)) { printf("?Connection broken (carrier signal lost)\n"); } if (sndcmd && protocol == PROTO_K && !success && hints && !interrupted && !fatalio && !xcmdsrc) { int x = 0, n = 0; printf("\n*************************\n"); printf("SEND-class command failed.\n"); printf(" Packets sent: %d\n", spackets); printf(" Retransmissions: %d\n",retrans); printf(" Timeouts: %d\n", timeouts); printf(" Damaged packets: %d\n", crunched); if (epktrcvd) { printf(" Transfer canceled by receiver.\n"); printf(" Receiver's message: \"%s\"\n",(char *)epktmsg); n++; } else { if (epktmsg) if (*epktmsg) { printf(" Fatal Kermit Protocol Error: %s\n",epktmsg); n++; } } if (local && !network && carrier != CAR_OFF) { int xx; /* Serial connection */ xx = ttgmdm(); /* with carrier checking */ if (xx > -1) { if (!(xx & BM_DCD)) cdlost = 1; } } #ifdef UNIX if (errno != 0) #endif /* UNIX */ { printf(" Most recent local OS error: \"%s\"\n",ck_errstr()); n++; } printf( "\nHINTS... If the preceding error message%s not explain the failure:\n", (n > 1) ? "s do" : " does" ); #ifndef NOLOCAL if (local) { if (rpackets == 0) { printf(" . Did you start a Kermit receiver on the far end?\n"); } else { printf( " . Try changing the remote Kermit's FLOW-CONTROL setting.\n"); if (!network && mdmtyp > 0) if ((3 * crunched) > spackets) printf( " . Try placing a new call to get a cleaner connection.\n"); } } else if (rpackets > 0) { if (flow == FLO_NONE) printf(" . Give me a SET FLOW XON/XOFF command and try again.\n"); else printf(" . Give me a SET FLOW NONE command and try again.\n"); } x++; #endif /* NOLOCAL */ if ((3 * timeouts) > spackets) printf(" . Adjust the timeout method (see HELP SET SEND).\n"); if ((3 * retrans) > spackets) printf(" . Increase the retry limit (see HELP SET RETRY).\n"); #ifdef CK_SPEED if (prefixing != PX_ALL && rpackets > 2) { printf(" . Try it again with: SET PREFIXING ALL\n"); x++; } #endif /* CK_SPEED */ #ifdef STREAMING if (streamed) { printf(" . Try it again with: SET STREAMING OFF\n"); x++; } else if (reliable) { printf(" . Try it again with: SET RELIABLE OFF\n"); x++; } #endif /* STREAMING */ #ifdef CK_SPEED if (clearrq > 0 && prefixing == PX_NON) { printf(" . Try it again with: SET CLEAR-CHANNEL OFF\n"); x++; } #endif /* CK_SPEED */ if (!parity) { printf(" . Try it again with: SET PARITY SPACE\n"); x++; } printf(" . %sive a ROBUST command and try again.\n", (x > 0) ? "As a last resort, g" : "G" ); printf("Also:\n"); printf(" . Be sure the source file has read permission.\n"); printf(" . Be sure the target directory has write permission.\n"); /* if the file was 2G or larger make sure other Kermit supports LFs... */ printf(" . Be sure the target disk has sufficient space.\n"); printf("(Use SET HINTS OFF to suppress hints.)\n"); printf("*************************\n\n"); } debug(F101,"topcmd","",topcmd); if (getcmd && protocol == PROTO_K && !success && hints && !interrupted && !fatalio && !xcmdsrc) { int x = 0; extern int urpsiz, wslotr; printf("\n*************************\n"); printf("RECEIVE- or GET-class command failed.\n"); printf(" Packets received: %d\n", rpackets); printf(" Damaged packets: %d\n", crunched); printf(" Timeouts: %d\n", timeouts); if (rpackets > 0) printf(" Packet length: %d\n", urpsiz); if (epktrcvd) { printf(" Transfer canceled by sender.\n"); printf(" Sender's message: \"%s\"\n",(char *)epktmsg); } #ifdef UNIX if (errno != 0) #endif /* UNIX */ printf(" Most recent local error: \"%s\"\n",ck_errstr()); printf( "\nHINTS... If the preceding error message%s not explain the failure:\n", epktrcvd ? "s do" : " does" ); #ifndef NOLOCAL if (local) { if (topcmd == XXGET) printf(" . Did you start a Kermit SERVER on the far end?\n"); if (rpackets == 0) { if (topcmd != XXGET) printf(" . Did you start a Kermit SENDer on the far end?\n"); } else { printf( " . Choose a different FLOW-CONTROL setting and try again.\n"); } } else if (topcmd == XXGET) printf(" . Is the other Kermit in (or does it have) SERVER mode?\n"); if (rpackets > 0 && urpsiz > 90) printf(" . Try smaller packets (SET RECEIVE PACKET-LENGTH).\n"); if (rpackets > 0 && wslotr > 1 && !streamed) printf(" . Try a smaller window size (SET WINDOW).\n"); if (!local && rpackets > 0) { if (flow == FLO_NONE) printf(" . Give me a SET FLOW XON/XOFF command and try again.\n"); else printf(" . Give me a SET FLOW NONE command and try again.\n"); } x++; #endif /* NOLOCAL */ #ifdef STREAMING if (streamed) { printf(" . Try it again with: SET STREAMING OFF\n"); x++; } else if (reliable && local) { printf(" . Try it again with: SET RELIABLE OFF\n"); x++; } else #endif /* STREAMING */ if (!parity) { printf(" . Try it again with: SET PARITY SPACE\n"); x++; } printf((x > 0) ? " . As a last resort, give a ROBUST command and try again.\n" : " . Give a ROBUST command and try again.\n" ); printf("Also:\n"); printf(" . Be sure the target directory has write permission.\n"); printf(" . Be sure the target disk has sufficient space.\n"); printf(" . Try telling the %s to SET PREFIXING ALL.\n", topcmd == XXGET ? "server" : "sender" ); printf(" . Try giving a ROBUST command to the %s.\n", topcmd == XXGET ? "server" : "sender" ); printf("(Use SET HINTS OFF to suppress hints.)\n"); printf("*************************\n\n"); } #endif /* NOHINTS */ getcmd = 0; sndcmd = 0; interrupted = 0; #endif /* NOXFER */ while (sstate == 0) { /* Parse cmds until action requested */ debug(F100,"parse top","",0); what = W_COMMAND; /* Now we're parsing commands. */ rcdactive = 0; /* REMOTE CD not active */ keepallchars = 0; /* MINPUT not active */ #ifdef OS2 if (apcactive == APC_INACTIVE) WaitCommandModeSem(-1); #endif /* OS2 */ #ifdef IKS_OPTION if ((local && !xcmdsrc && is_tn && TELOPT_ME(TELOPT_KERMIT) && TELOPT_SB(TELOPT_KERMIT).kermit.me_start) || (!local && !cmdadl && TELOPT_ME(TELOPT_KERMIT) && TELOPT_SB(TELOPT_KERMIT).kermit.me_start) ) { tn_siks(KERMIT_STOP); } #endif /* IKS_OPTION */ #ifndef NOXFER if (autopath) { fnrpath = PATH_AUTO; autopath = 0; } remfile = 0; /* Clear these in case REMOTE */ remappd = 0; /* command was interrupted... */ rempipe = 0; makestr(&snd_move,g_snd_move); /* Restore these */ makestr(&rcv_move,g_rcv_move); makestr(&snd_rename,g_snd_rename); makestr(&rcv_rename,g_rcv_rename); #endif /* NOXFER */ /* Take requested action if there was an error in the previous command */ setint(); debug(F101,"parser tlevel","",tlevel); debug(F101,"parser cmd_rows","",cmd_rows); #ifndef NOLOCAL debug(F101,"parser wasclosed","",wasclosed); if (wasclosed) { /* If connection was just closed */ #ifndef NOSPL int k; k = mlook(mactab,"on_close",nmac); /* Look up "on_close" */ if (k >= 0) { /* If found, */ /* printf("ON_CLOSE CMD LOOP\n"); */ dodo(k,ckitoa(whyclosed),0); /* Set it up */ } #endif /* NOSPL */ whyclosed = WC_REMO; wasclosed = 0; } #endif /* NOLOCAL */ #ifndef NOSPL xxdot = 0; /* Clear this... */ debug(F101,"parser success","",success); if (success == 0) { if (cmdstk[cmdlvl].src == CMD_TF && takerr[cmdlvl]) { printf("Command file terminated by error.\n"); popclvl(); if (cmdlvl == 0) return(0); } if (cmdstk[cmdlvl].src == CMD_MD && merror[cmdlvl]) { printf("Command error: macro terminated.\n"); popclvl(); if (m && (cmdlvl < inlevel)) return((int) sstate); } } nulcmd = (m == 2); debug(F101,"parser nulcmd","",nulcmd); #else if (success == 0 && tlevel > -1 && takerr[tlevel]) { printf("Command file terminated by error.\n"); popclvl(); cmini(ckxech); /* Clear the cmd buffer. */ if (tlevel < 0) /* Just popped out of cmd files? */ return(0); /* End of init file or whatever. */ } #endif /* NOSPL */ #ifdef MAC /* Check for TAKE initiated by menu. */ if ((tlevel == -1) && lfiles) startlfile(); #endif /* MAC */ /* If in TAKE file, check for EOF */ #ifndef NOSPL #ifdef MAC if #else while #endif /* MAC */ ((cmdstk[cmdlvl].src == CMD_TF) /* If end of take file */ && (tlevel > -1) && feof(tfile[tlevel])) { popclvl(); /* pop command level */ cmini(ckxech); /* and clear the cmd buffer. */ if (cmdlvl == 0) { /* Just popped out of all cmd files? */ return(0); /* End of init file or whatever. */ } } #ifdef MAC miniparser(1); if (sstate == 'a') { /* if cmd-. cancel */ debug(F100, "parser: cancel take due to sstate", "", sstate); sstate = '\0'; dostop(); return(0); /* End of init file or whatever. */ } #endif /* MAC */ #else /* NOSPL */ if ((tlevel > -1) && feof(tfile[tlevel])) { /* If end of take */ popclvl(); /* Pop up one level. */ cmini(ckxech); /* and clear the cmd buffer. */ if (tlevel < 0) /* Just popped out of cmd files? */ return(0); /* End of init file or whatever. */ } #endif /* NOSPL */ #ifndef NOSPL debug(F101,"parser cmdlvl","",cmdlvl); debug(F101,"parser cmdsrc","",cmdstk[cmdlvl].src); if (cmdstk[cmdlvl].src == CMD_MD) { /* Executing a macro? */ debug(F100,"parser macro","",0); maclvl = cmdstk[cmdlvl].lvl; /* Get current level */ debug(F101,"parser maclvl","",maclvl); cbp = cmdbuf; /* Copy next cmd to command buffer. */ *cbp = NUL; if (*savbuf) { /* In case then-part of 'if' command */ ckstrncpy(cbp,savbuf,CMDBL); /* was saved, restore it. */ *savbuf = '\0'; } else { /* Else get next cmd from macro def */ if (getncm(cbp,CMDBL) < 0) { #ifdef DEBUG if (deblog) { debug(F101,"parser end of macro m","",m); debug(F101,"parser end of macro cmdlvl","",cmdlvl); debug(F101,"parser end of macro inlevel","",inlevel); } #endif /* DEBUG */ if (m && (cmdlvl < inlevel)) return((int) sstate); else /* if (!m) */ continue; } } debug(F010,"CMD(M)",cmdbuf,0); } else if (cmdstk[cmdlvl].src == CMD_TF) #else if (tlevel > -1) #endif /* NOSPL */ { #ifndef NOSPL debug(F111,"parser savbuf",savbuf,tlevel); if (*savbuf) { /* In case THEN-part of IF command */ ckstrncpy(cmdbuf,savbuf,CMDBL); /* was saved, restore it. */ *savbuf = '\0'; } else #endif /* NOSPL */ /* Get next line from TAKE file */ if ((tfcode = getnct(cmdbuf,CMDBL,tfile[tlevel],0)) < 0) { debug(F111,"parser tfcode",tfile[tlevel],tfcode); if (tfcode < -1) { /* Error */ printf("?Error in TAKE command file: %s\n", (tfcode == -2) ? "Memory allocation failure" : "Line too long or contains NUL characters" ); dostop(); } continue; /* -1 means EOF */ } /* If interactive, get next command from user. */ } else { /* User types it in. */ if (pflag) prompt(xxstring); cmini(ckxech); } /* Now we know where next command is coming from. Parse and execute it. */ repars = 1; /* 1 = command needs parsing */ #ifndef NOXFER displa = 0; /* Assume no file transfer display */ #endif /* NOXFER */ while (repars) { /* Parse this cmd until entered. */ debug(F101,"parser top of while loop","",0); xaskmore = saveask; /* Restore global more-prompting */ diractive = 0; /* DIR command not active */ cdactive = 0; /* CD command not active */ #ifndef NOSPL askflag = 0; /* ASK command not active */ echostars = 0; /* Nor ASKQ */ debok = 1; /* Undisable debugging */ #endif /* NOSPL */ #ifdef RECURSIVE /* In case of "send /recursive ./?" etc */ recursive = 0; /* This is never sticky */ #endif /* RECURSIVE */ xfiletype = -1; /* Reset this between each command */ #ifndef NOMSEND addlist = 0; #endif /* NOMSEND */ #ifdef CK_RECALL on_recall = 1; #endif /* CK_RECALL */ /* This might have been changed by a switch */ if (g_matchdot > -1) { matchdot = g_matchdot; g_matchdot = -1; } cmres(); /* Reset buffer pointers. */ #ifdef OS2 #ifdef COMMENT /* we check to see if a macro is waiting to be executed */ /* if so, we call domac on it */ if (cmdmac) { ckstrncpy(cmdbuf, cmdmac, CMDBL); free(cmdmac); cmdmac = NULL; } #endif /* COMMENT */ #endif /* OS2 */ #ifndef NOXFER bye_active = 0; #endif /* NOXFER */ havetoken = 0; xx = cmkey2(cmdtab,ncmd,"Command","",toktab,xxstring,1); debug(F101,"top-level cmkey2","",xx); if (xx == -5) { yy = chktok(toktab); debug(F101,"top-level cmkey token","",yy); #ifndef COMMENT /* Either way makes absolutely no difference */ debug(F110,"NO UNGWORD",atmbuf,0); /* ungword(); */ #else debug(F110,"TOKEN UNGWORD",atmbuf,0); ungword(); #endif /* COMMENT */ switch (yy) { case '#': xx = XXCOM; break; /* Comment */ case ';': xx = XXCOM; break; /* Comment */ #ifndef NOSPL case '.': xx = XXDEF; xxdot = 1; break; /* Assignment */ case ':': xx = XXLBL; break; /* GOTO label */ #endif /* NOSPL */ #ifndef NOPUSH #ifdef CK_REDIR case '<': #endif /* CK_REDIR */ case '@': case '!': if (nopush) { char *s; int x; if ((x = cmtxt("Text to be ignored","",&s,NULL)) < 0) return(x); success = 0; xx = XXCOM; } else { switch(yy) { #ifdef CK_REDIR case '<': xx = XXFUN; break; /* REDIRECT */ #endif /* CK_REDIR */ case '@': case '!': xx = XXSHE; break; /* Shell escape */ } } break; #endif /* NOPUSH */ #ifdef CK_RECALL case '^': xx = XXREDO; break; #endif /* CK_RECALL */ #ifndef NOSPL case '{': xx = XXMACRO; break; case '(': xx = XXSEXP; break; #endif /* NOSPL */ default: if (!quiet && !cmd_err) { printf("\n?Invalid - \"%s\"\n", cmddisplay((char *)cmdbuf,xx) ); cmderr(); } xx = -2; } havetoken = 1; debug(F101,"HAVE TOKEN","",xx); } if (xx > -1) { topcmd = xx; /* Top-level command index */ #ifndef NOSPL if (maclvl > -1) lastcmd[maclvl] = xx; #endif /* NOSPL */ debug(F101,"topcmd","",topcmd); debug(F101,"cmflgs","",cmflgs); } #ifndef NOSPL /* Special handling for IF..ELSE */ debug(F101,"cmdlvl","",cmdlvl); debug(F101,"ifcmd[cmdlvl]","",ifcmd[cmdlvl]); if (ifcmd[cmdlvl]) /* Count stmts after IF */ ifcmd[cmdlvl]++; if (ifcmd[cmdlvl] > 2 && xx != XXELS && xx != XXCOM) ifcmd[cmdlvl] = 0; /* Execute the command and take action based on return code. */ if (nulcmd) { /* Ignoring this command? */ xx = XXCOM; /* Make this command a comment. */ } fnsuccess = 1; /* For catching \function() errors */ #endif /* NOSPL */ debug(F101,"calling docmd()","",xx); zz = docmd(xx); /* Parse rest of command & execute. */ #ifndef NOSPL { /* For \v(lastcommand) */ extern char * prevcmd; /* The exception list kind of a hack but let's try it... */ if (ckstrcmp(cmdbuf,"_getarg",7,0) && ckstrcmp(cmdbuf,"if ",3,0) && ckstrcmp(cmdbuf,"xif ",4,0) && ckstrcmp(cmdbuf,"do _if",6,0) && ckstrcmp(cmdbuf,"_assign _if",11,0)) ckstrncpy(prevcmd,cmdbuf,CMDBL); } #endif /* NOSPL */ #ifndef NOSPL if (fnerror && !fnsuccess) success = 0; #endif /* NOSPL */ debug(F101,"docmd returns","",zz); /* debug(F011,"cmdbuf",cmdbuf,30); */ /* debug(F011,"atmbuf",atmbuf,30); */ #ifdef MAC if (tlevel > -1) { if (sstate == 'a') { /* if cmd-. cancel */ debug(F110, "parser: cancel take, sstate:", "a", 0); sstate = '\0'; dostop(); return(0); /* End of init file or whatever. */ } } #endif /* MAC */ switch (zz) { case -4: /* EOF (e.g. on redirected stdin) */ doexit(GOOD_EXIT,xitsta); /* ...exit successfully */ case -1: /* Reparse needed */ repars = 1; /* Just set reparse flag and... */ continue; #ifdef OS2 case -7: /* They typed a disk letter */ if (!zchdir((char *)cmdbuf)) { perror((char *)cmdbuf); success = 0; } else success = 1; repars = 0; continue; #endif /* OS2 */ case -6: /* Invalid command given w/no args */ case -2: { /* Invalid command given w/args */ int x = 0; char * eol = ""; x = strlen(cmdbuf); /* Avoid blank line */ if (x > 0) { if (cmdbuf[x-1] != LF) eol = "\n"; printf("?Invalid: %s%s", cmddisplay(cmdbuf,xx),eol ); } else printf("?Invalid\n"); } case -9: /* Bad, error message already done */ success = 0; debug(F110,"top-level cmkey failed",cmdbuf,0); /* If in background w/ commands coming stdin, terminate */ if (pflag == 0 && tlevel < 0) fatal("Kermit command error in background execution"); /* Command retry feature, edit 190. If we're at interactive prompting level, reprompt the user with as much of the command as didn't fail. */ #ifdef CK_RECALL if (cm_retry && !xcmdsrc) { /* If at top level */ int len; char *p, *s; len = strlen(cmdbuf); /* Length of command buffer */ p = malloc(len + 1); /* Allocate space for copy */ if (p) { /* If we got the space copy */ strcpy(p,cmdbuf); /* the command buffer (SAFE). */ /* Chop off final field, the one that failed. */ s = p + len - 1; /* Point to end */ while (*s == SP && s > p) /* Trim blanks */ s--; while (*s != SP && s > p) /* Trim last field */ s--; if (s > p) /* Keep the space */ s++; /* after last good field */ if (s >= p) /* Cut off remainder */ *s = NUL; cmini(ckxech); /* Reinitialize the parser */ ckstrncpy(cmdbuf,p,CMDBL); /* Copy result back */ free(p); /* Free temporary storage */ p = NULL; prompt(xxstring); /* Reprint the prompt */ printf("%s",cmdbuf); /* Reprint partial command */ repars = 1; /* Force reparse */ continue; } } else #endif /* CK_RECALL */ cmderr(); cmini(ckxech); /* (fall thru) */ case -3: /* Empty command OK at top level */ repars = 0; /* Don't need to reparse. */ continue; /* Go back and get another command. */ default: /* Command was successful. */ #ifndef NOSPL debug(F101,"parser preparing to continue","",maclvl); #endif /* NOSPL */ debug(F101,"parser success","",success); repars = 0; /* Don't need to reparse. */ continue; /* Go back and get another command. */ } } #ifndef NOSPL debug(F101,"parser breaks out of while loop","",maclvl); if (m && (cmdlvl < inlevel)) return((int) sstate); #endif /* NOSPL */ } /* Got an action command, return start state. */ return((int) sstate); } #ifndef NOSPL /* OUTPUT command. Buffering and pacing added by L.I. Kirby, 5A(189), June 1993. */ #define OBSIZE 80 /* Size of local character buffer */ static int obn; /* Buffer offset (high water mark) */ static char obuf[OBSIZE+1]; /* OUTPUT buffer. */ static char *obp; /* Pointer to output buffer. */ _PROTOTYP( static int oboc, (char) ); _PROTOTYP( static int xxout, (char *, int) ); static int #ifdef CK_ANSIC xxout(char *obuf, int obsize) #else xxout(obuf, obsize) char *obuf; int obsize; #endif /* CK_ANSIC */ /* xxout */ { /* OUTPUT command's output function */ int i, rc; debug(F101,"xxout obsize","",obsize); debug(F101,"xxout pacing","",pacing); debug(F111,"xxout string",obuf,strlen(obuf)); rc = 0; /* Initial return code. */ if (!obuf || (obsize <= 0)) /* Nothing to output. */ goto xxout_x; /* Return successfully */ rc = -1; /* Now assume failure */ if (pacing == 0) { /* Is pacing enabled? */ if ((local ? /* No, write entire string at once */ ttol((CHAR *)obuf, obsize) : /* to communications device */ conxo(obsize, obuf)) /* or to console */ != obsize) goto xxout_x; } else { for (i = 0; i < obsize; i++) { /* Write individual chars */ if ((local ? ttoc(obuf[i]) : conoc(obuf[i])) < 0) goto xxout_x; msleep(pacing); } } if (duplex) { #ifdef OS2 if (inecho && local) { #ifndef NOLOCAL for (i = 0; i < obsize; i++) { /* Write to emulator */ scriptwrtbuf((USHORT)obuf[i]); /* which also logs session */ } #endif /* NOLOCAL */ conxo(obsize,obuf); } else if (seslog) { /* or log session here */ logstr((char *) obuf, obsize); } #else /* OS2 */ if (seslog) { logstr((char *) obuf, obsize); } if (inecho && local) { conxo(obsize,obuf); } #endif /* OS2 */ } rc = 0; /* Success */ xxout_x: obn = 0; /* Reset count */ obp = obuf; /* and pointers */ return(rc); /* return our return code */ } #ifdef COMMENT /* Macros for OUTPUT command execution, to make it go faster. */ #define obfls() ((xxout(obuf,obn)<0)?-1:0) #define oboc(c) ((*obp++=(char)(c)),*obp=0,(((++obn)>=OBSIZE)?obfls():0)) #else /* The macros cause some compilers to generate bad code. */ static int #ifdef CK_ANSIC oboc(char c) #else oboc(c) char c; #endif /* CK_ANSIC */ /* oboc */ { /* OUTPUT command's output function */ *obp++ = c; /* Deposit character */ *obp = NUL; /* Flush buffer if it's now full */ return(((++obn) >= OBSIZE) ? xxout(obuf,obn) : 0); } #endif /* COMMENT */ /* Routines for handling local variables -- also see popclvl(). */ VOID freelocal(m) int m; { /* Free local variables */ struct localvar * v, * tv; /* at macro level m... */ debug(F101,"freelocal level","",m); if (m < 0) return; v = localhead[m]; /* List head for level m */ while (v) { if (v->lv_name) /* Variable name */ free(v->lv_name); if (v->lv_value) /* Value */ free(v->lv_value); tv = v; /* Save pointer to this node */ v = v->lv_next; /* Get next one */ if (tv) /* Free this one */ free((char *)tv); } localhead[m] = (struct localvar *) NULL; /* Done, set list head to NULL */ } #define MAXLOCALVAR 64 /* Return a pointer to the definition of a user-defined variable */ static char * vardef(s,isarray,x1,x2) char * s; int * isarray, * x1, * x2; { char * p; char c; *isarray = 0; if (!s) return(NULL); if (!*s) return(NULL); p = s; if (*s == CMDQ) { p++; if (!*p) return(NULL); if ((c = *p) == '%') { /* Scalar variable. */ c = *++p; /* Get ID character. */ p = ""; /* Assume definition is empty */ if (!c) return(NULL); if (c >= '0' && c <= '9') { /* Digit for macro arg */ if (maclvl < 0) /* Digit variables are global */ return(g_var[c]); /* if no macro is active */ else /* otherwise */ return(m_arg[maclvl][c - '0']); /* they're on the stack */ } else if (isalpha(c)) { if (isupper(c)) c -= ('a'-'A'); return(g_var[c]); /* Letter for global variable */ } else return(NULL); } else if (c == '&') { /* Array reference. */ int x, vbi, d; x = arraynam(p,&vbi,&d); /* Get name and subscript */ if (x > -1 || d == -17) { *isarray = 1; *x1 = vbi; *x2 = (d == -17) ? 0 : d; } if (x < 0) return(NULL); if (chkarray(vbi,d) >= 0) { /* Array is declared? */ vbi -= ARRAYBASE; /* Convert name to index */ if (a_dim[vbi] >= d) { /* If subscript in range */ char **ap; ap = a_ptr[vbi]; return((ap) ? ap[d] : NULL); } } } return(NULL); } else { int k; k = mxlook(mactab,s,nmac); return((k > -1) ? mactab[k].mval : NULL); } } int addlocal(p) char * p; { int x, z, isarray = 0; char * s; struct localvar * v, *prev = (struct localvar *)NULL; extern int tra_asg; int tra_tmp; tra_tmp = tra_asg; s = vardef(p,&isarray,&x,&z); /* Get definition of variable */ if (isarray) { /* Arrays are handled specially */ pusharray(x,z); return(0); } if (!s) s = ""; if ((v = localhead[cmdlvl])) { /* Already have some at this level? */ while (v) { /* Find end of list */ prev = v; v = v->lv_next; } } v = (struct localvar *) malloc(sizeof(struct localvar)); if (!v) { printf("?Failure to allocate storage for local variables"); return(-9); } if (!localhead[cmdlvl]) /* If first, set list head */ localhead[cmdlvl] = v; else /* Otherwise link previous to this */ prev->lv_next = v; prev = v; /* And make this previous */ v->lv_next = (struct localvar *) NULL; /* No next yet */ if (!(v->lv_name = (char *) malloc((int) strlen(p) + 1))) return(-1); strcpy(v->lv_name, p); /* Copy name into new node (SAFE) */ if (*s) { if (!(v->lv_value = (char *) malloc((int) strlen(s) + 1))) return(-1); strcpy(v->lv_value, s); /* Copy value into new node (SAFE) */ } else v->lv_value = NULL; tra_asg = 0; delmac(p,1); /* Delete the original macro */ tra_asg = tra_tmp; return(0); } int dolocal() { /* Do the LOCAL command */ int i, x; char * s; char * list[MAXLOCALVAR+2]; /* Up to 64 variables per line */ if ((x = cmtxt("Variable name(s)","",&s,NULL)) < 0) return(x); xwords(s,MAXLOCALVAR,list,0); /* Break up line into "words" */ /* Note: Arrays do not use the localhead list, but have their own stack */ for (i = 1; i < MAXLOCALVAR && list[i]; i++) { /* Go through the list */ if (addlocal(list[i]) < 0) goto localbad; } return(success = 1); localbad: printf("?Failure to allocate storage for local variables"); freelocal(cmdlvl); return(-9); } /* D O O U T P U T -- Returns 0 on failure, 1 on success */ #ifndef NOKVERBS #define K_BUFLEN 30 #define SEND_BUFLEN 255 #define sendbufd(x) { osendbuf[sendndx++] = x;\ if (sendndx == SEND_BUFLEN) {dooutput(s,cx); sendndx = 0;}} #endif /* NOKVERBS */ int outesc = 1; /* Process special OUTPUT escapes */ int dooutput(s, cx) char *s; int cx; { #ifdef SSHBUILTIN extern int ssh_cas; extern char * ssh_cmd; #endif /* SSHBUILTIN */ int x, xx, y, quote; /* Workers */ int is_tn = 0; is_tn = (local && network && IS_TELNET()) || (!local && sstelnet); debug(F111,"dooutput s",s,(int)strlen(s)); if (local) { /* Condition external line */ #ifdef NOLOCAL goto outerr; #else if (ttchk() < 0) { if (!network) { if (carrier != CAR_OFF) { int x; x = ttgmdm(); if ((x > -1) && ((x & BM_DCD) == 0)) { printf( "?Carrier signal required but not present - Try SET CARRIER-WATCH OFF.\n" ); return(0); } } else { printf( "?Problem with serial port or modem or cable - Try SHOW COMMUNICATIONS.\n" ); return(0); } } printf("?Connection %s %s is not open or not functioning.\n", network ? "to" : "on", ttname ); return(0); } if (ttvt(speed,flow) < 0) { printf("?OUTPUT initialization error\n"); return(0); } #endif /* NOLOCAL */ } #ifdef SSHBUILTIN if ( network && nettype == NET_SSH && ssh_cas && ssh_cmd && !(strcmp(ssh_cmd,"kermit") && strcmp(ssh_cmd,"sftp"))) { if (!quiet) printf("?SSH Subsystem active: %s\n", ssh_cmd); return(0); } #endif /* SSHBUILTIN */ if (!cmdgquo()) { /* COMMAND QUOTING OFF */ x = strlen(s); /* Just send the string literally */ xx = local ? ttol((CHAR *)s,x) : conxo(x,s); return(success = (xx == x) ? 1 : 0); } quote = 0; /* Initialize backslash (\) quote */ obn = 0; /* Reset count */ obp = obuf; /* and pointers */ outagain: while ((x = *s++)) { /* Loop through the string */ y = 0; /* Error code, 0 = no error. */ debug(F000,"dooutput","",x); if (quote) { /* This character is quoted */ #ifndef NOKVERBS if (x == 'k' || x == 'K') { /* \k or \K */ extern struct keytab kverbs[]; extern int nkverbs; extern char * keydefptr; extern int keymac; extern int keymacx; int x, y, brace = 0; int pause; char * p, * b; char kbuf[K_BUFLEN + 1]; /* Key verb name buffer */ char osendbuf[SEND_BUFLEN +1]; int sendndx = 0; if (xxout(obuf,obn) < 0) /* Flush buffer */ goto outerr; debug(F100,"OUTPUT KVERB","",0); /* Send a KVERB */ { /* Have K verb? */ if (!*s) { break; } /* We assume that the verb name is {braced}, or it extends to the end of the string, s, or it ends with a space, control character, or backslash. */ p = kbuf; /* Copy verb name into local buffer */ x = 0; while ((x++ < K_BUFLEN) && (*s > SP) && (*s != CMDQ)) { if (brace && *s == '}') { break; } *p++ = *s++; } if (*s && !brace) /* If we broke because of \, etc, */ s--; /* back up so we get another look. */ brace = 0; *p = NUL; /* Terminate. */ p = kbuf; /* Point back to beginning */ debug(F110,"dooutput kverb",p,0); y = xlookup(kverbs,p,nkverbs,&x); /* Look it up */ debug(F101,"dooutput lookup",0,y); if (y > -1) { if (sendndx) { dooutput(osendbuf,cx); sendndx = 0; } dokverb(VCMD,y); #ifndef NOSPL } else { /* Is it a macro? */ y = mxlook(mactab,p,nmac); if (y > -1) { cmpush(); keymac = 1; /* Flag for key macro active */ keymacx = y; /* Key macro index */ keydefptr = s; /* Where to resume next time */ debug(F111,"dooutput mxlook",keydefptr,y); parser(1); cmpop(); } #endif /* NOSPL */ } } quote = 0; continue; } else #endif /* NOKVERBS */ if (outesc && (x == 'n' || x == 'N')) { /* \n or \N */ if (xxout(obuf,obn) < 0) /* Flush buffer */ goto outerr; debug(F100,"OUTPUT NUL","",0); /* Send a NUL */ if (local) ttoc(NUL); else conoc(NUL); quote = 0; continue; } else if (outesc && (x == 'b' || x == 'B')) { /* \b or \B */ if (xxout(obuf,obn) < 0) /* Flush buffer first */ goto outerr; debug(F100,"OUTPUT BREAK","",0); #ifndef NOLOCAL ttsndb(); /* Send BREAK signal */ #else if (local) ttoc(NUL); else conoc(NUL); #endif /* NOLOCAL */ quote = 0; /* Turn off quote flag */ continue; /* and not the b or B */ #ifdef CK_LBRK } else if (outesc && (x == 'l' || x == 'L')) { /* \l or \L */ if (xxout(obuf,obn) < 0) /* Flush buffer first */ goto outerr; debug(F100,"OUTPUT Long BREAK","",0); #ifndef NOLOCAL ttsndlb(); /* Send Long BREAK signal */ #else if (local) ttoc(NUL); else conoc(NUL); #endif /* NOLOCAL */ quote = 0; /* Turn off quote flag */ continue; /* and not the l or L */ #endif /* CK_LBRK */ } else if (x == CMDQ) { /* Backslash itself */ debug(F100,"OUTPUT CMDQ","",0); xx = oboc(dopar(CMDQ)); /* Output the backslash. */ if (xx < 0) goto outerr; quote = 0; continue; } else { /* if \ not followed by special esc */ /* Note: Atari ST compiler won't allow macro call in "if ()" */ xx = oboc(dopar(CMDQ)); /* Output the backslash. */ if (xx < 0) goto outerr; quote = 0; /* Turn off quote flag */ } } else if (x == CMDQ) { /* This is the quote character */ quote = 1; /* Go back and get next character */ continue; /* which is quoted */ } xx = oboc(dopar((char)x)); /* Output this character */ debug(F111,"dooutput",obuf,obn); if (xx < 0) goto outerr; #ifdef COMMENT if (seslog && duplex) { /* Log the character if log is on */ logchar((char)x); } #endif /* COMMENT */ if (x == '\015') { /* String contains carriage return */ int stuff = -1, stuff2 = -1; if (tnlm) { /* TERMINAL NEWLINE ON */ stuff = LF; /* Stuff LF */ } #ifdef TNCODE /* TELNET NEWLINE ON/OFF/RAW */ if (is_tn) { switch (TELOPT_ME(TELOPT_BINARY) ? /* NVT or BINARY */ tn_b_nlm : tn_nlm ) { case TNL_CR: break; case TNL_CRNUL: stuff2 = stuff; stuff = NUL; break; case TNL_CRLF: stuff2 = stuff; stuff = LF; break; } } #endif /* TNCODE */ if (stuff > -1) { /* Stuffing another character... */ xx = oboc(dopar((CHAR)stuff)); if (xx < 0) goto outerr; #ifdef COMMENT if (seslog && duplex) { /* Log stuffed char if appropriate */ logchar((char)stuff); } #endif /* COMMENT */ } if (stuff2 > -1) { /* Stuffing another character... */ xx = oboc(dopar((CHAR)stuff2)); if (xx < 0) goto outerr; #ifdef COMMENT if (seslog && duplex) { /* Log stuffed char if appropriate */ logchar((char)stuff2); } #endif /* COMMENT */ } if (xxout(obuf,obn) < 0) /* Flushing is required here! */ goto outerr; } } if (cx == XXLNOUT) { s = "\015"; cx = 0; goto outagain; } if (quote == 1) /* String ended with backslash */ xx = oboc(dopar(CMDQ)); if (obn > 0) /* OUTPUT done */ if (xxout(obuf,obn) < 0) /* Flush the buffer if necessary. */ goto outerr; return(1); outerr: /* OUTPUT command error handler */ if (msgflg) printf("?OUTPUT execution error\n"); return(0); /* Remove "local" OUTPUT macro defininitions */ #ifdef COMMENT /* No more macros ... */ #undef oboc #undef obfls #endif /* COMMENT */ } #endif /* NOSPL */ /* Display version herald and initial prompt */ VOID herald() { int x = 0, i; extern int srvcdmsg; extern char * cdmsgfile[]; char * ssl; char * krb4; char * krb5; #ifndef NOCMDL extern char * bannerfile; debug(F110,"herald bannerfile",bannerfile,0); if (bannerfile) { concb((char)escape); if (dotype(bannerfile,1,0,0,NULL,0,NULL,0,0,NULL,0) > 0) { debug(F111,"herald","srvcdmsg",srvcdmsg); if (srvcdmsg) { for (i = 0; i < 8; i++) { debug(F111,"herald cdmsgfile[i]",cdmsgfile[i],i); if (zchki(cdmsgfile[i]) > -1) { printf("\n"); dotype(cdmsgfile[i], xaskmore,0,0,NULL,0,NULL,0,0,NULL,0); break; } } } return; } } #endif /* NOCMDL */ #ifdef COMMENT /* The following generates bad code in SCO compilers. */ /* Observed in both OSR5 and Unixware 2 -- after executing this */ /* statement when all conditions are false, x has a value of -32. */ if (noherald || quiet || bgset > 0 || (bgset != 0 && backgrd != 0)) x = 1; #else x = 0; if (noherald || quiet) x = 1; else if (bgset > 0) x = 1; else if (bgset < 0 && backgrd > 0) x = 1; #endif /* COMMENT */ ssl = ""; krb4 = ""; krb5 = ""; #ifdef CK_AUTHENTICATION #ifdef CK_SSL ssl = "+SSL"; #endif /* CK_SSL */ #ifdef KRB4 krb4 = "+KRB4"; #endif /* KRB4 */ #ifdef KRB5 krb5 = "+KRB5"; #endif /* KRB5 */ #endif /* CK_AUTHENTICATION */ if (x == 0) { #ifdef datageneral printf("%s, for%s\n",versio,ckxsys); #else #ifdef OSK printf("%s, for%s\n",versio,ckxsys); #else #ifdef CK_64BIT printf("%s, for%s%s%s%s (64-bit)\n\r",versio,ckxsys,ssl,krb4,krb5); #else printf("%s, for%s%s%s%s\n\r",versio,ckxsys,ssl,krb4,krb5); #endif/* CK_64BIT */ #endif /* OSK */ #endif /* datageneral */ printf(" Copyright (C) 1985, %s,\n", ck_cryear); printf(" Trustees of Columbia University in the City of New York.\n"); #ifdef OS2 shoreg(); #endif /* OS2 */ if (!quiet && !backgrd) { #ifdef COMMENT /* "Default file-transfer mode is AUTOMATIC" is useless information... */ char * s; extern int xfermode; #ifdef VMS s = "AUTOMATIC"; #else if (xfermode == XMODE_A) { s = "AUTOMATIC"; } else { s = gfmode(binary,1); } if (!s) s = ""; #endif /* VMS */ if (*s) printf("Default file-transfer mode is %s\n", s); #endif /* COMMENT */ debug(F111,"herald","srvcdmsg",srvcdmsg); if (srvcdmsg) { for (i = 0; i < 8; i++) { debug(F111,"herald cdmsgfile[i]",cdmsgfile[i],i); if (zchki(cdmsgfile[i]) > -1) { printf("\n"); dotype(cdmsgfile[i], xaskmore,0,0,NULL,0,NULL,0,0,NULL,0); break; } } } printf("Type ? or HELP for help.\n"); } } } /* G F M O D E -- Get File (transfer) Mode */ char * gfmode(binary,upcase) int binary, upcase; { char * s; switch (binary) { case XYFT_T: s = upcase ? "TEXT" : "text"; break; #ifdef VMS case XYFT_B: s = upcase ? "BINARY FIXED" : "binary fixed"; break; case XYFT_I: s = upcase ? "IMAGE" : "image"; break; case XYFT_L: s = upcase ? "LABELED" : "labeled"; break; case XYFT_U: s = upcase ? "BINARY UNDEF" : "binary undef"; break; #else #ifdef MAC case XYFT_B: s = upcase ? "BINARY" : "binary"; break; case XYFT_M: s = upcase ? "MACBINARY" : "macbinary"; break; #else case XYFT_B: s = upcase ? "BINARY" : "binary"; break; #ifdef CK_LABELED case XYFT_L: s = upcase ? "LABELED" : "labeled"; break; #endif /* CK_LABELED */ #endif /* MAC */ #endif /* VMS */ case XYFT_X: s = upcase ? "TENEX" : "tenex"; break; default: s = ""; } return(s); } #ifndef NOSPL static int isaa(s) char * s; { /* Is associative array */ char c; int x; if (!s) s = ""; if (!*s) return(0); s++; while ((c = *s++)) { if (c == '<') { x = strlen(s); return ((*(s+x-1) == '>') ? 1 : 0); } } return(0); } /* M L O O K -- Lookup the macro name in the macro table */ /* Call this way: v = mlook(table,word,n); table - a 'struct mtab' table. word - the target string to look up in the table. n - the number of elements in the table. The keyword table must be arranged in ascending alphabetical order, and all letters must be lowercase. Returns the table index, 0 or greater, if the name was found, or: -3 if nothing to look up (target was null), -2 if ambiguous, -1 if not found. A match is successful if the target matches a keyword exactly, or if the target is a prefix of exactly one keyword. It is ambiguous if the target matches two or more keywords from the table. */ int mlook(table,cmd,n) struct mtab table[]; char *cmd; int n; { register int i; int v, w, cmdlen = 0; char c = 0, c1, * s; if (!cmd) cmd = ""; for (s = cmd; *s; s++) cmdlen++; /* (instead of strlen) */ debug(F111,"MLOOK",cmd,cmdlen); c1 = *cmd; if (isupper(c1)) c1 = tolower(c1); if (cmdlen < 1) return(-3); /* Not null, look it up */ if (n < 12) { /* Not worth it for small tables */ i = 0; } else { /* Binary search for where to start */ int lo = 0; int hi = n; int count = 0; while (lo+2 < hi && ++count < 12) { i = lo + ((hi - lo) / 2); c = *(table[i].kwd); if (isupper(c)) c = tolower(c); if (c < c1) { lo = i; } else { hi = i; } } i = (c < c1) ? lo+1 : lo; } for ( ; i < n-1; i++) { s = table[i].kwd; if (!s) s = ""; if (!*s) continue; /* Empty table entry */ c = *s; if (isupper(c)) c = tolower(c); if (c1 != c) continue; /* First char doesn't match */ if (!ckstrcmp(s,cmd,-1,0)) /* Have exact match? */ return(i); v = !ckstrcmp(s,cmd,cmdlen,0); w = ckstrcmp(table[i+1].kwd,cmd,cmdlen,0); if (v && w) /* Have abbreviated match? */ return(i); if (v) /* Ambiguous? */ return(-2); if (w > 0) /* Past our alphabetic area? */ return(-1); } /* Last (or only) element */ if (!ckstrcmp(table[n-1].kwd,cmd,cmdlen,0)) return(n-1); return(-1); } /* mxlook is like mlook, but an exact full-length match is required */ int mxlook(table,cmd,n) char *cmd; struct mtab table[]; int n; { register int i; int w, cmdlen = 0, one = 0; register char c = 0, c1, * s; if (!cmd) cmd = ""; /* Check args */ for (s = cmd; *s; s++) cmdlen++; /* (instead of strlen) */ debug(F111,"MXLOOK",cmd,cmdlen); c1 = *cmd; /* First char of string to look up */ if (isupper(c1)) c1 = tolower(c1); if (!*(cmd+1)) /* Special handling for 1-char names */ one = 1; if (cmdlen < 1) /* Nothing to look up */ return(-3); if (n < 12) { /* Not worth it for small tables */ i = 0; } else { /* Binary search for where to start */ int lo = 0; int hi = n; int count = 0; while (lo+2 < hi && ++count < 12) { i = lo + ((hi - lo) / 2); c = *(table[i].kwd); if (isupper(c)) c = tolower(c); if (c < c1) { lo = i; } else { hi = i; } } i = (c < c1) ? lo+1 : lo; } for ( ; i < n; i++) { /* Look thru table */ s = table[i].kwd; /* This entry */ if (!s) s = ""; if (!*s) continue; /* Empty table entry */ c = *s; if (isupper(c)) c = tolower(c); if (c1 != c) continue; /* First char doesn't match */ if (one) { /* Name is one char long */ if (!*(s+1)) return(i); /* So is table entry */ } #ifdef COMMENT if (((int)strlen(s) == cmdlen) && (!ckstrcmp(s,cmd,cmdlen,0))) return(i); #else w = ckstrcmp(s,cmd,-1,0); if (!w) return(i); if (w > 0) return(-1); #endif /* COMMENT */ } return(-1); } /* mxxlook is like mxlook, but case-sensitive */ int mxxlook(table,cmd,n) char *cmd; struct mtab table[]; int n; { int i, cmdlen; if (!cmd) cmd = ""; if (((cmdlen = strlen(cmd)) < 1) || (n < 1)) return(-3); /* debug(F111,"mxxlook target",cmd,n); */ for (i = 0; i < n; i++) { if (((int)strlen(table[i].kwd) == cmdlen) && (!strncmp(table[i].kwd,cmd,cmdlen))) return(i); } return(-1); } static int traceval(nam, val) char * nam, * val; { /* For TRACE command */ if (val) printf(">>> %s: \"%s\"\n", nam, val); else printf(">>> %s: (undef)\n", nam); return(0); } #ifdef USE_VARLEN /* Not used */ /* V A R L E N -- Get length of variable's value. Given a variable name, return the length of its definition or 0 if the variable is not defined. If it is defined, set argument s to point to its definition. Otherwise set s to NULL. */ int varlen(nam,s) char *nam; char **s; { /* Length of value of variable */ int x, z; char *p = NULL, c; *s = NULL; if (!nam) return(0); /* Watch out for null pointer */ if (*nam == CMDQ) { nam++; if (*nam == '%') { /* If it's a variable name */ if (!(c = *(nam+1))) return(0); /* Get letter or digit */ p = (char *)0; /* Initialize value pointer */ if (maclvl > -1 && c >= '0' && c <= '9') { /* Digit? */ p = m_arg[maclvl][c - '0']; /* Pointer from macro-arg table */ } else { /* It's a global variable */ if (c < 33 || c > GVARS) return(0); p = g_var[c]; /* Get pointer from global-var table */ } } else if (*nam == '&') { /* An array reference? */ char **q; if (arraynam(nam,&x,&z) < 0) /* If syntax is bad */ return(-1); /* return -1. */ x -= ARRAYBASE; /* Convert name to number. */ if ((q = a_ptr[x]) == NULL) /* If array not declared, */ return(0); /* return -2. */ if (z > a_dim[x]) /* If subscript out of range, */ return(0); /* return -3. */ p = q[z]; } } else { /* Macro */ z = isaa(nam); x = z ? mxxlook(mactab,nam,nmac) : mlook(mactab,nam,nmac); if (x < 0) return(0); p = mactab[x].mval; } if (p) *s = p; else p = ""; return((int)strlen(p)); } #endif /* USE_VARLEN */ /* This routine is for the benefit of those compilers that can't handle long string constants or continued lines within them. Long predefined macros like FOR, WHILE, and XIF have their contents broken up into arrays of string pointers. This routine concatenates them back into a single string again, and then calls the real addmac() routine to enter the definition into the macro table. */ int addmmac(nam,s) char *nam, *s[]; { /* Add a multiline macro definition */ int i, x, y; char *p; x = 0; /* Length counter */ for (i = 0; (y = (int)strlen(s[i])) > 0; i++) { /* Add up total length */ debug(F111,"addmmac line",s[i],y); x += y; } debug(F101,"addmmac lines","",i); debug(F101,"addmmac loop exit","",y); debug(F111,"addmmac length",nam,x); if (x < 0) return(-1); p = malloc(x+1); /* Allocate space for all of it. */ if (!p) { printf("?addmmac malloc error: %s\n",nam); debug(F110,"addmmac malloc error",nam,0); return(-1); } *p = '\0'; /* Start off with null string. */ for (i = 0; *s[i]; i++) /* Concatenate them all together. */ ckstrncat(p,s[i],x+1); y = (int)strlen(p); /* Final precaution. */ debug(F111,"addmmac constructed string",p,y); if (y == x) { y = addmac(nam,p); /* Add result to the macro table. */ } else { debug(F100,"addmmac length mismatch","",0); printf("\n!addmmac internal error!\n"); y = -1; } free(p); /* Free the temporary copy. */ return(y); } /* Here is the real addmac routine. */ /* Returns -1 on failure, macro table index >= 0 on success. */ int mtchanged = 0; int addmac(nam,def) char *nam, *def; { /* Add a macro to the macro table */ int i, x, y, z, namlen, deflen, flag = 0; int replacing = 0, deleting = 0; char * p = NULL, c, *s; extern int tra_asg; int tra_tmp; if (!nam) return(-1); if (!*nam) return(-1); #ifdef IKSD if (inserver && #ifdef IKSDCONF iksdcf #else /* IKSDCONF */ 1 #endif /* IKSDCONF */ ) { if (!ckstrcmp("on_exit",nam,-1,0) || !ckstrcmp("on_logout",nam,-1,0)) return(-1); } #endif /* IKSD */ namlen = 0; p = nam; while (*p++) namlen++; /* (instead of strlen) */ tra_tmp = tra_asg; /* trace... */ debug(F111,"addmac nam",nam,namlen); if (!def) { /* Watch out for null pointer */ deflen = 0; debug(F111,"addmac def","(null pointer)",deflen); } else { deflen = 0; p = def; while (*p++) deflen++; /* (instead of strlen) */ debug(F010,"addmac def",def,0); } #ifdef USE_VARLEN /* NOT USED */ /* This does not boost performance much because varlen() does a lot */ x = varlen(nam,&s); if (x > 0 && x >= deflen) { strcpy(s,def); /* NOT USED */ flag = 1; p = s; } #endif /* USE_VARLEN */ if (*nam == CMDQ) nam++; /* Backslash quote? */ if (*nam == '%') { /* Yes, if it's a variable name, */ if (!(c = *(nam + 1))) return(-1); /* Variable name letter or digit */ if (!flag) { tra_asg = 0; delmac(nam,0); /* Delete any old value. */ tra_asg = tra_tmp; } if (deflen < 1) { /* Null definition */ p = NULL; /* Better not malloc or strcpy! */ } else if (!flag) { /* A substantial definition */ p = malloc(deflen + 1); /* Allocate space for it */ if (!p) { printf("?addmac malloc error 2\n"); return(-1); } else strcpy(p,def); /* Copy def into new space (SAFE) */ } /* Now p points to the definition, or is a null pointer */ if (c >= '0' && c <= '9') { /* \%0-9 variable */ if (maclvl < 0) { /* Are we calling or in a macro? */ g_var[c] = p; /* No, it's top level one */ makestr(&(toparg[c - '0']),p); /* Take care \&_[] too */ } else { /* Yes, it's a macro argument */ m_arg[maclvl][c - '0'] = p; /* Assign the value */ makestr(&(m_xarg[maclvl][c - '0']),p); /* And a copy here */ } } else { /* It's a \%a-z variable */ if (c < 33 || (unsigned int)c > GVARS) return(-1); if (isupper(c)) c = (char) tolower(c); g_var[c] = p; /* Put pointer in global-var table */ } if (tra_asg) traceval(nam,p); return(0); } else if (*nam == '&') { /* An array reference? */ char **q = NULL; int rc = 0; if ((y = arraynam(nam,&x,&z)) < 0) /* If syntax is bad */ return(-1); /* return -1. */ if (chkarray(x,z) < 0) /* If array not declared or */ rc = -2; /* subscript out of range, ret -2 */ if (!flag) { tra_asg = 0; delmac(nam,0); /* Delete any old value. */ tra_asg = tra_tmp; } x -= ARRAYBASE; /* Convert name letter to index. */ if (x > 'z' - ARRAYBASE + 1) rc = -1; if (rc != -1) { if ((q = a_ptr[x]) == NULL) /* If array not declared, */ return(-3); /* return -3. */ } if (rc < 0) return(rc); if (!flag) { if (deflen > 0) { if ((p = malloc(deflen+1)) == NULL) { /* Allocate space */ printf("addmac macro error 7: %s\n",nam); return(-4); /* for new def, return -4 on fail. */ } strcpy(p,def); /* Copy def into new space (SAFE). */ } else p = NULL; } q[z] = p; /* Store pointer to it. */ if (x == 0) { /* Arg vector array */ if (z >= 0 && z <= 9) { /* Copy values to corresponding */ if (maclvl < 0) { /* \%1..9 variables. */ makestr(&(toparg[z]),p); } else { makestr(&(m_arg[maclvl][z]),p); } } } if (tra_asg) traceval(nam,p); return(0); /* Done. */ } /* Not a macro argument or a variable, so it's a macro definition */ #ifdef USE_VARLEN if (flag) { if (tra_asg) traceval(nam,p); return(0); } #endif /* USE_VARLEN */ x = isaa(nam) ? /* If it's an associative array */ mxxlook(mactab,nam,nmac) : /* look it up this way */ mxlook(mactab,nam,nmac); /* otherwise this way. */ if (x > -1) { /* If found... */ if (deflen > 0) /* and a new definition was given */ replacing = 1; /* we're replacing */ else /* otherwise */ deleting = 1; /* we're deleting */ } if (deleting) { /* Deleting... */ if (delmac(nam,0) < 0) return(-1); mtchanged++; if (tra_asg) traceval(nam,p); return(0); } else if (deflen < 1) /* New macro with no definition */ return(0); /* Nothing to do. */ if (replacing) { /* Replacing an existing macro */ if (mactab[x].mval) { /* If it currently has a definition, */ free(mactab[x].mval); /* free it. */ mactab[x].mval = NULL; } mtchanged++; y = x; /* Replacement index. */ } else { /* Adding a new macro... */ char c1, c2; /* Use fast lookup to find the */ c1 = *nam; /* alphabetical slot. */ if (isupper(c1)) c1 = (char) tolower(c1); if (nmac < 5) { /* Not worth it for small tables */ y = 0; } else { /* First binary search to find */ int lo = 0; /* where to start */ int hi = nmac; int count = 0; char c = 0; while (lo+2 < hi && ++count < 12) { y = lo + ((hi - lo) / 2); c = *(mactab[y].kwd); if (isupper(c)) c = (char) tolower(c); if (c < c1) { lo = y; } else { hi = y; } } y = (c < c1) ? lo+1 : lo; } /* Now search linearly from starting location */ for ( ; y < MAC_MAX && mactab[y].kwd != NULL; y++) { c2 = *(mactab[y].kwd); if (isupper(c2)) c2 = (char) tolower(c2); if (c1 > c2) continue; if (c1 < c2) break; if (ckstrcmp(nam,mactab[y].kwd,-1,0) <= 0) break; } if (y == MAC_MAX) { /* Macro table is full. */ debug(F101,"addmac table overflow","",y); printf("?Macro table overflow\n"); return(-1); } if (mactab[y].kwd != NULL) { /* Must insert */ for (i = nmac; i > y; i--) { /* Move the rest down one slot */ mactab[i].kwd = mactab[i-1].kwd; mactab[i].mval = mactab[i-1].mval; mactab[i].flgs = mactab[i-1].flgs; } } mtchanged++; p = malloc(namlen + 1); /* Allocate space for name */ if (!p) { printf("?Addmac: Out of memory - \"%s\"\n",nam); return(-1); } strcpy(p,nam); /* Copy name into new space (SAFE) */ mactab[y].kwd = p; /* Add pointer to table */ } if (deflen > 0) { /* If we have a definition */ p = malloc(deflen + 1); /* Get space */ if (p == NULL) { printf("?Space exhausted - \"%s\"\n", nam); if (mactab[y].kwd) { free(mactab[y].kwd); mactab[y].kwd = NULL; } return(-1); } else { strcpy(p,def); /* Copy the definition (SAFE) */ } } else { /* definition is empty */ p = NULL; } mactab[y].mval = p; /* Macro points to definition */ mactab[y].flgs = 0; /* No flags */ if (!replacing) /* If new macro */ nmac++; /* count it */ if (tra_asg) traceval(nam,p); return(y); } int xdelmac(x) int x; { /* Delete a macro given its index */ int i; extern int tra_asg; if (x < 0) return(x); if (tra_asg) traceval(mactab[x].kwd,NULL); if (mactab[x].kwd) { /* Free the storage for the name */ free(mactab[x].kwd); mactab[x].kwd = NULL; } if (mactab[x].mval) { /* and for the definition */ free(mactab[x].mval); mactab[x].mval = NULL; } for (i = x; i < nmac; i++) { /* Now move up the others. */ mactab[i].kwd = mactab[i+1].kwd; mactab[i].mval = mactab[i+1].mval; mactab[i].flgs = mactab[i+1].flgs; } nmac--; /* One less macro */ mactab[nmac].kwd = NULL; /* Delete last item from table */ mactab[nmac].mval = NULL; mactab[nmac].flgs = 0; mtchanged++; return(0); } int delmac(nam,exact) char *nam; int exact; { /* Delete the named macro */ int x, z; char *p, c; extern int tra_asg; if (!nam) return(0); /* Watch out for null pointer */ debug(F110,"delmac nam",nam,0); #ifdef IKSD if ( inserver && #ifdef IKSDCONF iksdcf #else /* IKSDCONF */ 1 #endif /* IKSDCONF */ ) { if (!ckstrcmp("on_exit",nam,-1,0) || !ckstrcmp("on_logout",nam,-1,0)) return(-1); } #endif /* IKSD */ if (*nam == CMDQ) nam++; if (*nam == '%') { /* If it's a variable name */ if (!(c = *(nam+1))) return(0); /* Get variable name letter or digit */ p = (char *)0; /* Initialize value pointer */ if (maclvl < 0 && c >= '0' && c <= '9') { /* Top-level digit? */ p = toparg[c - '0']; if (p) if (p != g_var[c]) { free(p); toparg[c - '0'] = NULL; } p = g_var[c]; g_var[c] = NULL; /* Zero the table entry */ } else if (maclvl > -1 && c >= '0' && c <= '9') { /* Digit? */ p = m_xarg[maclvl][c - '0']; if (p) if (p != g_var[c]) { free(p); m_xarg[maclvl][c - '0'] = NULL; } p = m_arg[maclvl][c - '0']; /* Get pointer from macro-arg table */ m_arg[maclvl][c - '0'] = NULL; /* Zero the table pointer */ } else { /* It's a global variable */ if (c < 33 || (unsigned int)c > GVARS) return(0); p = g_var[c]; /* Get pointer from global-var table */ g_var[c] = NULL; /* Zero the table entry */ } if (p) { debug(F010,"delmac def",p,0); free(p); /* Free the storage */ p = NULL; } else debug(F110,"delmac def","(null pointer)",0); if (tra_asg) traceval(nam,NULL); return(0); } if (*nam == '&') { /* An array reference? */ char **q; if (arraynam(nam,&x,&z) < 0) /* If syntax is bad */ return(-1); /* return -1. */ x -= ARRAYBASE; /* Convert name to number. */ if ((q = a_ptr[x]) == NULL) /* If array not declared, */ return(-2); /* return -2. */ if (z > a_dim[x]) /* If subscript out of range, */ return(-3); /* return -3. */ if (q[z]) { /* If there is an old value, */ debug(F010,"delmac def",q[z],0); if (x != 0) /* Macro arg vector is just a copy */ free(q[z]); /* Others are real so free them */ q[z] = NULL; if (x == 0) { /* Arg vector array */ if (z >= 0 && z <= 9) { /* Copy values to corresponding */ if (maclvl < 0) { /* \%1..9 variables. */ makestr(&(toparg[z]),NULL); } else { makestr(&(m_arg[maclvl][z]),NULL); } } } if (tra_asg) traceval(nam,NULL); } else debug(F010,"delmac def","(null pointer)",0); } /* Not a variable or an array, so it must be a macro. */ z = isaa(nam); debug(F111,"delmac isaa",nam,z); debug(F111,"delmac exact",nam,exact); x = z ? mxxlook(mactab,nam,nmac) : exact ? mxlook(mactab,nam,nmac) : mlook(mactab,nam,nmac); if (x < 0) { debug(F111,"delmac mlook",nam,x); return(x); } return(xdelmac(x)); } VOID initmac() { /* Init macro & variable tables */ int i, j, x; nmac = 0; /* No macros */ for (i = 0; i < MAC_MAX; i++) { /* Initialize the macro table */ mactab[i].kwd = NULL; mactab[i].mval = NULL; mactab[i].flgs = 0; } mtchanged++; x = (MAXARGLIST + 1) * sizeof(char **); for (i = 0; i < MACLEVEL; i++) { /* Init the macro argument tables */ m_xarg[i] = (char **) malloc(x); mrval[i] = NULL; /* Macro return value */ /* Pointer to entire argument vector, level i, for \&_[] array */ for (j = 0; j <= MAXARGLIST; j++) { /* Macro argument list */ if (j < 10) /* For the \%0..\%9 variables */ m_arg[i][j] = NULL; /* Pointer to arg j, level i. */ if (m_xarg[i]) /* For \&_[] - all args. */ m_xarg[i][j] = NULL; } } for (i = 0; i < GVARS; i++) { /* And the global variables table */ g_var[i] = NULL; } /* And the table of arrays */ for (i = 0; i < (int) 'z' - ARRAYBASE + 1; i++) { a_ptr[i] = (char **) NULL; /* Null pointer for each */ a_dim[i] = 0; /* and a dimension of zero */ a_link[i] = -1; for (j = 0; j < CMDSTKL; j++) { aa_ptr[j][i] = (char **) NULL; aa_dim[j][i] = 0; } } } int popclvl() { /* Pop command level, return cmdlvl */ extern int tra_cmd; struct localvar * v; int i, topcmd; debug(F101,"popclvl cmdlvl","",cmdlvl); if (cmdlvl > 0) { if ((v = localhead[cmdlvl])) { /* Did we save any variables? */ while (v) { /* Yes */ if (v->lv_value) /* Copy old ones back */ addmac(v->lv_name,v->lv_value); else delmac(v->lv_name,1); v = v->lv_next; } freelocal(cmdlvl); /* Free local storage */ } /* Automatic arrays do not use the localhead list */ for (i = 0; i < 28; i++) { /* Free any local arrays */ if (aa_ptr[cmdlvl][i]) { /* Does this one exist? */ dclarray((char)(i+ARRAYBASE),-1); /* Destroy global one */ a_ptr[i] = aa_ptr[cmdlvl][i]; a_dim[i] = aa_dim[cmdlvl][i]; aa_ptr[cmdlvl][i] = (char **)NULL; aa_dim[cmdlvl][i] = 0; } else if (aa_dim[cmdlvl][i] == -23) { /* Secret code */ dclarray((char)(i+ARRAYBASE),-1); /* (see pusharray()) */ aa_ptr[cmdlvl][i] = (char **)NULL; aa_dim[cmdlvl][i] = 0; } /* Otherwise do nothing - it is a local array that was declared */ /* at a level above this one so leave it alone. */ } } if (cmdlvl < 1) { /* If we're already at top level */ cmdlvl = 0; /* just make sure all the */ tlevel = -1; /* stack pointers are set right */ maclvl = -1; /* and return */ } else if (cmdstk[cmdlvl].src == CMD_TF) { /* Reading from TAKE file? */ debug(F101,"popclvl tlevel","",tlevel); if (tlevel > -1) { /* Yes, */ fclose(tfile[tlevel]); /* close it */ if (tra_cmd) printf("[%d] -F: \"%s\"\n",cmdlvl,tfnam[tlevel]); debug(F111,"CMD -F",tfnam[tlevel],cmdlvl); if (tfnam[tlevel]) { /* free storage for name */ free(tfnam[tlevel]); tfnam[tlevel] = NULL; } tlevel--; /* and pop take level */ cmdlvl--; /* and command level */ quiet = xquiet[cmdlvl]; vareval = xvarev[cmdlvl]; } else tlevel = -1; } else if (cmdstk[cmdlvl].src == CMD_MD) { /* In a macro? */ topcmd = lastcmd[maclvl]; debug(F101,"popclvl maclvl","",maclvl); if (maclvl > -1) { /* Yes, */ #ifdef COMMENT int i; char **q; #endif /* COMMENT */ macp[maclvl] = ""; /* set macro pointer to null string */ *cmdbuf = '\0'; /* clear the command buffer */ if ((maclvl > 0) && /* 2 May 1999 */ (m_arg[maclvl-1][0]) && (!strncmp(m_arg[maclvl-1][0],"_xif",4) || !strncmp(m_arg[maclvl-1][0],"_for",4) || !strncmp(m_arg[maclvl-1][0],"_swi",4) || !strncmp(m_arg[maclvl-1][0],"_whi",4)) && mrval[maclvl+1]) { makestr(&(mrval[maclvl-1]),mrval[maclvl+1]); } if (maclvl+1 < MACLEVEL) { if (mrval[maclvl+1]) { /* Free any deeper return values. */ free(mrval[maclvl+1]); mrval[maclvl+1] = NULL; } } if (tra_cmd) printf("[%d] -M: \"%s\"\n",cmdlvl,m_arg[cmdstk[cmdlvl].lvl][0]); debug(F111,"CMD -M",m_arg[cmdstk[cmdlvl].lvl][0],cmdlvl); maclvl--; /* Pop macro level */ cmdlvl--; /* and command level */ debug(F101,"popclvl mac new maclvl","",maclvl); debug(F010,"popclvl mac mrval[maclvl+1]",mrval[maclvl+2],0); quiet = xquiet[cmdlvl]; vareval = xvarev[cmdlvl]; if (maclvl > -1) { a_ptr[0] = m_xarg[maclvl]; a_dim[0] = n_xarg[maclvl] - 1; debug(F111,"a_dim[0]","B",a_dim[0]); } else { a_ptr[0] = topxarg; a_dim[0] = topargc - 1; debug(F111,"a_dim[0]","C",a_dim[0]); } } else { maclvl = -1; } #ifndef NOSEXP debug(F101,"popclvl topcmd","",topcmd); if (topcmd == XXSEXP) { extern char * sexpval; makestr(&(mrval[maclvl+1]),sexpval); } #endif /* NOSEXP */ } else { cmdlvl--; } debug(F101,"popclvl cmdlvl","",cmdlvl); if (prstring[cmdlvl]) { cmsetp(prstring[cmdlvl]); makestr(&(prstring[cmdlvl]),NULL); } #ifndef MAC if (cmdlvl < 1 || xcmdsrc == CMD_KB) { /* If at prompt */ setint(); concb((char)escape); /* Go into cbreak mode */ } #endif /* MAC */ xcmdsrc = cmdstk[cmdlvl].src; debug(F101,"popclvl xcmdsrc","",xcmdsrc); debug(F101,"popclvl tlevel","",tlevel); return(cmdlvl < 1 ? 0 : cmdlvl); /* Return command level */ } #else /* No script programming language */ int popclvl() { /* Just close current take file. */ if (tlevel > -1) { /* if any... */ if (tfnam[tlevel]) { free(tfnam[tlevel]); tfnam[tlevel] = NULL; } fclose(tfile[tlevel--]); } if (tlevel == -1) { /* And if back at top level */ setint(); concb((char)escape); /* and go back into cbreak mode. */ } xcmdsrc = tlevel > -1 ? CMD_TF : 0; return(tlevel + 1); } #endif /* NOSPL */ #ifndef NOSPL static int iseom(m) char * m; { /* Test if at end of macro def */ if (!m) m = ""; debug(F111,"iseom",m,maclvl); while (*m) { /* Anything but Space and Comma means more macro is left */ if ((*m > SP) && (*m != ',')) { debug(F111,"iseom return",m,0); return(0); } m++; } debug(F111,"iseom return",m,1); return(1); /* Nothing left */ } #endif /* NOSPL */ /* Pop all command levels that can be popped */ int prepop() { if (cmdlvl > 0) { /* If command level is > 0 and... */ while ( #ifndef NOSPL ((cmdstk[cmdlvl].src == CMD_TF) && /* Command source is file */ #endif /* NOSPL */ (tlevel > -1) && feof(tfile[tlevel])) /* And at end of file... */ #ifndef NOSPL /* Or command source is macro... */ || ((cmdstk[cmdlvl].src == CMD_MD) && (maclvl > -1) && iseom(macp[maclvl]))) /* and at end of macro, then... */ #endif /* NOSPL */ { popclvl(); /* pop command level. */ } } return(cmdlvl < 1 ? 0 : cmdlvl); /* Return command level */ } /* STOP - get back to C-Kermit prompt, no matter where from. */ int dostop() { extern int cmddep; while (popclvl()) ; /* Pop all macros & take files */ #ifndef NOSPL if (cmddep > -1) /* And all recursive cmd pkg invocations */ while (cmpop() > -1) ; #endif /* NOSPL */ cmini(ckxech); /* Clear the command buffer. */ return(0); } /* Close the given log */ int doclslog(x) int x; { int y; switch (x) { #ifdef DEBUG case LOGD: if (deblog <= 0) { printf("?Debugging log wasn't open\n"); return(0); } debug(F100,"Debug Log Closed","",0L); *debfil = '\0'; deblog = 0; return(zclose(ZDFILE)); #endif /* DEBUG */ #ifndef NOXFER case LOGP: if (pktlog <= 0) { printf("?Packet log wasn't open\n"); return(0); } *pktfil = '\0'; pktlog = 0; return(zclose(ZPFILE)); #endif /* NOXFER */ #ifndef NOLOCAL case LOGS: if (seslog <= 0) { printf("?Session log wasn't open\n"); return(0); } *sesfil = '\0'; setseslog(0); return(zclose(ZSFILE)); #endif /* NOLOCAL */ #ifdef TLOG case LOGT: { #ifdef IKSD extern int iklogopen, xferlog; #endif /* IKSD */ if (tralog <= 0 #ifdef IKSD && !iklogopen #endif /* IKSD */ ) { if (msgflg) printf("?Transaction log wasn't open\n"); return(0); } #ifdef IKSD if (iklogopen && !inserver) { close(xferlog); iklogopen = 0; } #endif /* IKSD */ if (tralog) { tlog(F100,"Transaction Log Closed","",0L); zclose(ZTFILE); } *trafil = '\0'; tralog = 0; return(1); } #endif /* TLOG */ #ifdef CKLOGDIAL case LOGM: if (dialog <= 0) { if (msgflg) printf("?Connection log wasn't open\n"); return(0); } *diafil = '\0'; dialog = 0; return(zclose(ZDIFIL)); #endif /* CKLOGDIAL */ #ifndef NOSPL case LOGW: /* WRITE file */ case LOGR: /* READ file */ y = (x == LOGR) ? ZRFILE : ZWFILE; if (chkfn(y) < 1) /* If no file to close */ return(1); /* succeed silently. */ return(zclose(y)); /* Otherwise, close the file. */ #endif /* NOSPL */ default: printf("\n?Unexpected log designator - %d\n", x); return(0); } } static int slc = 0; /* Screen line count */ char * showstring(s) char * s; { return(s ? s : "(null)"); } char * showoff(x) int x; { return(x ? "on" : "off"); } char * showooa(x) int x; { switch (x) { case SET_OFF: return("off"); case SET_ON: return("on"); case SET_AUTO: return("automatic"); default: return("(unknown)"); } } #ifdef GEMDOS isxdigit(c) int c; { return(isdigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')); } #endif /* GEMDOS */ #ifndef NOSETKEY #ifdef OS2 static struct keytab shokeytab[] = { /* SHOW KEY modes */ "all", 1, 0, "one", 0, 0 }; static int nshokey = (sizeof(shokeytab) / sizeof(struct keytab)); #define SHKEYDEF TT_MAX+5 struct keytab shokeymtab[] = { "aaa", TT_AAA, CM_INV, /* AnnArbor */ "adm3a", TT_ADM3A, 0, /* LSI ADM-3A */ "adm5", TT_ADM5, 0, /* LSI ADM-5 */ "aixterm", TT_AIXTERM, 0, /* IBM AIXterm */ "annarbor", TT_AAA, 0, /* AnnArbor */ "ansi-bbs", TT_ANSI, 0, /* ANSI.SYS (BBS) */ "at386", TT_AT386, 0, /* Unixware ANSI */ "avatar/0+", TT_ANSI, 0, /* AVATAR/0+ */ "ba80", TT_BA80, 0, /* Nixdorf BA80 */ "be", TT_BEOS, CM_INV|CM_ABR, "beos-ansi", TT_BEOS, CM_INV, /* BeOS ANSI */ "beterm", TT_BEOS, 0, /* BeOS Console */ "d200", TT_DG200, CM_INV|CM_ABR, /* Data General DASHER 200 */ "d210", TT_DG210, CM_INV|CM_ABR, /* Data General DASHER 210 */ "d217", TT_DG217, CM_INV|CM_ABR, /* Data General DASHER 217 */ "default", SHKEYDEF, 0, "dg200", TT_DG200, 0, /* Data General DASHER 200 */ "dg210", TT_DG210, 0, /* Data General DASHER 210 */ "dg217", TT_DG217, 0, /* Data General DASHER 217 */ "emacs", TT_KBM_EMACS, 0, /* Emacs mode */ "h19", TT_H19, CM_INV, /* Heath-19 */ "heath19", TT_H19, 0, /* Heath-19 */ "hebrew", TT_KBM_HEBREW, 0, /* Hebrew mode */ "hft", TT_HFT, 0, /* IBM HFT */ "hp2621a", TT_HP2621, 0, /* HP 2621A */ "hpterm", TT_HPTERM, 0, /* HP TERM */ "hz1500", TT_HZL1500, 0, /* Hazeltine 1500 */ "ibm3151", TT_IBM31, 0, /* IBM 3101-xx,3161 */ "linux", TT_LINUX, 0, /* Linux */ "qansi", TT_QANSI, 0, /* QNX ANSI */ "qnx", TT_QNX, 0, /* QNX */ "russian", TT_KBM_RUSSIAN, 0, /* Russian mode */ "scoansi", TT_SCOANSI, 0, /* SCO ANSI */ "sni-97801", TT_97801, 0, /* Sinix 97801 */ "sun", TT_SUN, 0, /* Sun Console */ #ifdef OS2PM #ifdef COMMENT "tek4014", TT_TEK40, 0, #endif /* COMMENT */ #endif /* OS2PM */ "tty", TT_NONE, 0, "tvi910+", TT_TVI910, 0, "tvi925", TT_TVI925, 0, "tvi950", TT_TVI950, 0, "vc404", TT_VC4404, 0, "vc4404", TT_VC4404, CM_INV, "vip7809", TT_VIP7809, 0, "vt100", TT_VT100, 0, "vt102", TT_VT102, 0, "vt220", TT_VT220, 0, "vt220pc", TT_VT220PC, 0, "vt320", TT_VT320, 0, "vt320pc", TT_VT320PC, 0, "vt52", TT_VT52, 0, "wp", TT_KBM_WP, 0, "wy160", TT_WY160, 0, "wy30", TT_WY30, 0, "wy370", TT_WY370, 0, "wy50", TT_WY50, 0, "wy60", TT_WY60, 0, "wyse30", TT_WY30, CM_INV, "wyse370", TT_WY370, CM_INV, "wyse50", TT_WY50, CM_INV, "wyse60", TT_WY60, CM_INV }; int nshokeym = (sizeof(shokeymtab) / sizeof(struct keytab)); #endif /* OS2 */ VOID #ifdef OS2 shokeycode(c,m) int c, m; #else shokeycode(c) int c; #endif /* shokeycode */ { KEY ch; CHAR *s; #ifdef OS2 int i; con_event km; #else /* OS2 */ int km; #endif /* OS2 */ #ifdef OS2 extern int mskkeys; char * mstr = ""; if (c >= KMSIZE) { bleep(BP_FAIL); return; } #else /* OS2 */ printf(" Key code \\%d => ", c); #endif /* OS2 */ #ifndef OS2 km = mapkey(c); #ifndef NOKVERBS if (IS_KVERB(km)) { /* \Kverb? */ int i, kv; kv = km & ~(F_KVERB); printf("Verb: "); for (i = 0; i < nkverbs; i++) if (kverbs[i].kwval == kv) { printf("\\K%s",kverbs[i].kwd); break; } printf("\n"); } else #endif /* NOKVERBS */ if (IS_CSI(km)) { int xkm = km & 0xFF; if (xkm <= 32 || xkm >= 127) printf("String: \\{27}[\\{%d}\n",xkm); else printf("String: \\{27}[%c\n",xkm); } else if (IS_ESC(km)) { int xkm = km & 0xFF; if (xkm <= 32 || xkm >= 127) printf("String: \\{27}\\{%d}\n",xkm); else printf("String: \\{27}%c\n",xkm); } else if (macrotab[c]) { /* See if there's a macro */ printf("String: "); /* If so, display its definition */ s = macrotab[c]; shostrdef(s); printf("\n"); #ifndef NOKVERBS } else if (km >= 0x100) { /* This means "undefined" */ printf("Undefined\n"); #endif /* NOKVERBS */ } else { /* No macro, show single character */ printf("Character: "); ch = km; if (ch < 32 || ch == 127 #ifdef OS2 || ch > 255 #endif /* OS2 */ #ifndef NEXT #ifndef AUX #ifndef XENIX #ifndef OS2 || (ch > 127 && ch < 160) #endif /* OS2 */ #endif /* XENIX */ #endif /* AUX */ #endif /* NEXT */ ) /* These used to be %d, but gcc 1.93 & later complain about type mismatches. %u is supposed to be totally portable. */ printf("\\%u",(unsigned int) ch); else printf("%c \\%u",(CHAR) (ch & 0xff),(unsigned int) ch); if (ch == (KEY) c) printf(" (self, no translation)\n"); else printf("\n"); } #else /* OS2 */ if (m < 0) { km = mapkey(c); mstr = "default"; } else { km = maptermkey(c,m); for (i = 0; i < nshokeym; i++) { if (m == shokeymtab[i].kwval) { mstr = shokeymtab[i].kwd; break; } } } s = keyname(c); debug(F111,"shokeycode mstr",mstr,m); debug(F111,"shokeycode keyname",s,c); printf(" %sKey code \\%d %s (%s) => ", mskkeys ? "mskermit " : "", mskkeys ? cktomsk(c) : c, s == NULL ? "" : s, mstr); switch (km.type) { #ifndef NOKVERBS case kverb: { int i, kv; kv = km.kverb.id & ~(F_KVERB); printf("Verb: "); for (i = 0; i < nkverbs; i++) { if (kverbs[i].kwval == kv) { printf("\\K%s",kverbs[i].kwd); break; } } printf("\n"); break; } #endif /* NOKVERBS */ case csi: { int xkm = km.csi.key & 0xFF; if (xkm <= 32 || xkm >= 127) printf("String: \\{27}[\\{%d}\n",xkm); else printf("String: \\{27}[%c\n",xkm); break; } case esc: { int xkm = km.esc.key & 0xFF; if (xkm <= 32 || xkm >= 127) printf("String: \\{%d}\\{%d}\n",ISDG200(tt_type)?30:27,xkm); else printf("String: \\{%d}%c\n",ISDG200(tt_type)?30:27,xkm); break; } case macro: { printf("String: "); /* Macro, display its definition */ shostrdef(km.macro.string); printf("\n"); break; } case literal: { printf("Literal string: "); /* Literal, display its definition */ shostrdef(km.literal.string); printf("\n"); break; } case error: { if (c >= 0x100) { printf("Undefined\n"); } else { printf("Character: "); ch = c; if (ch < 32 || ch == 127 || ch > 255 #ifndef NEXT #ifndef AUX #ifndef XENIX #ifndef OS2 || (ch > 127 && ch < 160) #endif /* OS2 */ #endif /* XENIX */ #endif /* AUX */ #endif /* NEXT */ ) /* These used to be %d, but gcc 1.93 & later complain about type mismatches. %u is supposed to be totally portable. */ printf("\\%u",(unsigned int) ch); else printf("%c \\%u",(CHAR) (ch & 0xff),(unsigned int) ch); printf(" (self, no translation)\n"); } break; } case key: { printf("Character: "); ch = km.key.scancode; if (ch < 32 || ch == 127 || ch > 255 #ifndef NEXT #ifndef AUX #ifndef XENIX #ifndef OS2 || (ch > 127 && ch < 160) #else || (ch > 127) #endif /* OS2 */ #endif /* XENIX */ #endif /* AUX */ #endif /* NEXT */ ) /* These used to be %d, but gcc 1.93 & later complain about type mismatches. %u is supposed to be totally portable. */ printf("\\%u",(unsigned int) ch); else printf("%c \\%u",(CHAR) (ch & 0xff),(unsigned int) ch); if (ch == (KEY) c) printf(" (self, no translation)\n"); else printf("\n"); break; } } #endif /* OS2 */ } #endif /* NOSETKEY */ VOID shostrdef(s) CHAR * s; { CHAR ch; if (!s) s = (CHAR *)""; while ((ch = *s++)) { if (ch < 32 || ch == 127 || ch == 255 /* Systems whose native character sets have graphic characters in C1... */ #ifndef NEXT /* NeXT */ #ifndef AUX /* Macintosh */ #ifndef XENIX /* IBM PC */ #ifdef OS2 /* It doesn't matter whether the local host can display 8-bit characters; they are not portable among character-sets and fonts. Who knows what would be displayed... */ || (ch > 127) #else /* OS2 */ || (ch > 127 && ch < 160) #endif /* OS2 */ #endif /* XENIX */ #endif /* AUX */ #endif /* NEXT */ ) printf("\\{%d}",ch); /* Display control characters */ else putchar((char) ch); /* in backslash notation */ } } #define xxdiff(v,sys) strncmp(v,sys,strlen(sys)) #ifndef NOSHOW VOID shover() { #ifdef OS2 extern char ckxsystem[]; #endif /* OS2 */ extern char *ck_patch, * cklibv; printf("\nVersions:\n %s\n",versio); printf(" Numeric: %ld\n",vernum); #ifdef OS2 printf(" Operating System: %s\n", ckxsystem); #else /* OS2 */ printf(" Built for: %s\n", ckxsys); #ifdef CK_UTSNAME if (unm_nam[0]) printf(" Running on: %s %s %s %s\n", unm_nam,unm_ver,unm_rel,unm_mch); #endif /* CK_UTSNAME */ printf(" Patches: %s\n", *ck_patch ? ck_patch : "(none)"); #endif /* OS2 */ if (xxdiff(ckxv,ckxsys)) printf(" %s for%s\n",ckxv,ckxsys); else printf(" %s\n",ckxv); if (xxdiff(ckzv,ckzsys)) printf(" %s for%s\n",ckzv,ckzsys); else printf(" %s\n",ckzv); printf(" %s\n",cklibv); printf(" %s\n",protv); printf(" %s\n",fnsv); printf(" %s\n %s\n",cmdv,userv); #ifndef NOCSETS printf(" %s\n",xlav); #endif /* NOCSETS */ #ifndef MAC #ifndef NOLOCAL printf(" %s\n",connv); #ifdef OS2 printf(" %s\n",ckyv); #endif /* OS2 */ #endif /* NOLOCAL */ #endif /* MAC */ #ifndef NODIAL printf(" %s\n",dialv); #endif /* NODIAL */ #ifndef NOSCRIPT printf(" %s\n",loginv); #endif /* NOSCRIPT */ #ifdef NETCONN printf(" %s\n",cknetv); #ifdef OS2 printf(" %s\n",ckonetv); #ifdef CK_NETBIOS printf(" %s\n",ckonbiv); #endif /* CK_NETBIOS */ #endif /* OS2 */ #endif /* NETCONN */ #ifdef TNCODE printf(" %s\n",cktelv); #endif /* TNCODE */ #ifdef SSHBUILTIN printf(" %s\n",cksshv); #ifdef SFTP_BUILTIN printf(" %s\n",cksftpv); #endif /* SFTP_BUILTIN */ #endif /* SSHBUILTIN */ #ifdef OS2 #ifdef OS2MOUSE printf(" %s\n",ckomouv); #endif /* OS2MOUSE */ #endif /* OS2 */ #ifdef NEWFTP printf(" %s\n",ckftpv); #endif /* NEWFTP */ #ifdef CK_AUTHENTICATION printf(" %s\n",ckathv); #endif /* CK_AUTHENTICATION */ #ifdef CK_ENCRYPTION #ifdef CRYPT_DLL printf(" %s\n",ck_crypt_dll_version()); #else /* CRYPT_DLL */ printf(" %s\n",ckcrpv); #endif /* CRYPT_DLL */ #endif /* CK_ENCRYPTION */ #ifdef CK_SSL printf(" %s\n",cksslv); #endif /* CK_SSL */ printf("\n"); } #ifdef CK_LABELED VOID sholbl() { #ifdef VMS printf("VMS Labeled File Features:\n"); printf(" acl %s (ACL info %s)\n", showoff(lf_opts & LBL_ACL), lf_opts & LBL_ACL ? "preserved" : "discarded"); printf(" backup-date %s (backup date/time %s)\n", showoff(lf_opts & LBL_BCK), lf_opts & LBL_BCK ? "preserved" : "discarded"); printf(" name %s (original filename %s)\n", showoff(lf_opts & LBL_NAM), lf_opts & LBL_NAM ? "preserved" : "discarded"); printf(" owner %s (original file owner id %s)\n", showoff(lf_opts & LBL_OWN), lf_opts & LBL_OWN ? "preserved" : "discarded"); printf(" path %s (original file's disk:[directory] %s)\n", showoff(lf_opts & LBL_PTH), lf_opts & LBL_PTH ? "preserved" : "discarded"); #else #ifdef OS2 printf("OS/2 Labeled File features (attributes):\n"); printf(" archive: %s\n", showoff(lf_opts & LBL_ARC)); printf(" extended: %s\n", showoff(lf_opts & LBL_EXT)); printf(" hidden: %s\n", showoff(lf_opts & LBL_HID)); printf(" read-only: %s\n", showoff(lf_opts & LBL_RO )); printf(" system: %s\n", showoff(lf_opts & LBL_SYS)); #endif /* OS2 */ #endif /* VMS */ } #endif /* CK_LABELED */ VOID shotcs(csl,csr) int csl, csr; { /* Show terminal character set */ #ifndef NOCSETS #ifdef OS2 #ifndef NOTERM extern struct _vtG G[4], *GL, *GR; extern int decnrcm, sni_chcode; extern int tt_utf8, dec_nrc, dec_kbd, dec_lang; extern prncs; printf(" Terminal character-sets:\n"); if (IS97801(tt_type_mode)) { if (cmask == 0377) printf(" Mode: 8-bit Mode\n"); else printf(" Mode: 7-bit Mode\n"); printf(" CH.CODE is %s\n",sni_chcode?"On":"Off"); } else if (ISVT100(tt_type_mode)) { if (decnrcm) printf(" Mode: 7-bit National Mode\n"); else printf(" Mode: 8-bit Multinational Mode\n"); } if ( isunicode() ) printf(" Local: Unicode display / %s input\n", csl == TX_TRANSP ? "transparent" : csl == TX_UNDEF ? "undefined" : txrinfo[csl]->keywd); else printf(" Local: %s\n", csl == TX_TRANSP ? "transparent" : csl == TX_UNDEF ? "undefined" : txrinfo[csl]->keywd); if ( tt_utf8 ) { printf(" Remote: UTF-8\n"); } else { printf(" Remote: %sG0: %s (%s)\n", GL == &G[0] ? "GL->" : GR == &G[0] ? "GR->" : " ", txrinfo[G[0].designation]->keywd, G[0].designation == TX_TRANSP ? "" : G[0].size == cs94 ? "94 chars" : G[0].size == cs96 ? "96 chars" : "multi-byte"); printf(" %sG1: %s (%s)\n", GL == &G[1] ? "GL->" : GR == &G[1] ? "GR->" : " ", txrinfo[G[1].designation]->keywd, G[1].designation == TX_TRANSP ? "" : G[1].size == cs94 ? "94 chars" : G[1].size == cs96 ? "96 chars" : "multi-byte"); printf(" %sG2: %s (%s)\n", GL == &G[2] ? "GL->" : GR == &G[2] ? "GR->" : " ", txrinfo[G[2].designation]->keywd, G[2].designation == TX_TRANSP ? "" : G[2].size == cs94 ? "94 chars" : G[2].size == cs96 ? "96 chars" : "multi-byte"); printf(" %sG3: %s (%s)\n", GL == &G[3] ? "GL->" : GR == &G[3] ? "GR->" : " ", txrinfo[G[3].designation]->keywd, G[3].designation == TX_TRANSP ? "" : G[3].size == cs94 ? "94 chars" : G[3].size == cs96 ? "96 chars" : "multi-byte"); } printf("\n"); printf(" Keyboard character-sets:\n"); printf(" Multinational: %s\n",txrinfo[dec_kbd]->keywd); printf(" National: %s\n",txrinfo[dec_nrc]->keywd); printf("\n"); printf(" Printer character-set: %s\n",txrinfo[prncs]->keywd); #endif /* NOTERM */ #else /* OS2 */ #ifndef MAC char *s; debug(F101,"TERM LOCAL CSET","",csl); debug(F101,"TERM REMOTE CSET","",csr); printf(" Terminal character-set: "); if (tcs_transp) { /* No translation */ printf("transparent\n"); } else { /* Translation */ printf("%s (remote) %s (local)\n", fcsinfo[csr].keyword,fcsinfo[csl].keyword); if (csr != csl) { switch(gettcs(csr,csl)) { case TC_USASCII: s = "ascii"; break; case TC_1LATIN: s = "latin1-iso"; break; case TC_2LATIN: s = "latin2-iso"; break; case TC_CYRILL: s = "cyrillic-iso"; break; case TC_JEUC: s = "japanese-euc"; break; case TC_HEBREW: s = "hebrew-iso"; break; case TC_GREEK: s = "greek-iso"; break; case TC_9LATIN: s = "latin9-iso"; break; default: s = "transparent"; break; } if (strcmp(s,fcsinfo[csl].keyword) && strcmp(s,fcsinfo[csr].keyword)) printf(" (via %s)\n",s); } } #endif /* MAC */ #endif /* OS2 */ #endif /* NOCSETS */ } #ifndef NOLOCAL #ifdef OS2 extern char htab[]; VOID shotabs() { int i,j,k,n; printf("Tab Stops:\n\n"); for (i = 0, j = 1, k = VscrnGetWidth(VCMD); i < MAXTERMCOL; ) { do { printf("%c",htab[++i]=='T'?'T':'-'); } while (i % k && i < MAXTERMCOL); printf("\n"); for ( ; j <= i; j++) { switch ( j%10 ) { case 1: printf("%c",j == 1 ? '1' : '.'); break; case 2: case 3: case 4: case 5: case 6: case 7: printf("%c",'.'); break; case 8: n = (j+2)/100; if (n) printf("%d",n); else printf("%c",'.'); break; case 9: n = (j+1)%100/10; if (n) printf("%d",n); else if (j>90) printf("0"); else printf("%c",'.'); break; case 0: printf("0"); break; } } printf("\n"); } #ifdef COMMENT for (i = 1; i <= 70; i++) printf("%c",htab[i]=='T'?'T':'-'); printf("\n1.......10........20........30........40........50........60\ ........70\n\n"); for (; i <= 140; i++) printf("%c",htab[i]=='T'?'T':'-'); printf("\n........80........90.......100.......110.......120.......130\ .......140\n\n"); for (; i <= 210; i++) printf("%c",htab[i]=='T'?'T':'-'); printf("\n.......150.......160.......170.......180.......190.......200\ .......210\n\n"); for (; i <= 255; i++) printf("%c",htab[i]=='T'?'T':'-'); printf("\n.......220.......230.......240.......250..255\n"); #endif } #endif /* OS2 */ #endif /* NOLOCAL */ #ifdef OS2MOUSE VOID shomou() { int button, event, id, i; char * name = ""; printf("Mouse settings:\n"); printf(" Button Count: %d\n",mousebuttoncount()); printf(" Active: %s\n\n",showoff(tt_mouse)); for (button = 0; button < MMBUTTONMAX; button++) for (event = 0; event < MMEVENTSIZE; event++) if (mousemap[button][event].type != error) switch (mousemap[button][event].type) { case key: printf(" %s = Character: %c \\%d\n", mousename(button,event), mousemap[button][event].key.scancode, mousemap[button][event].key.scancode ); break; case kverb: id = mousemap[button][event].kverb.id & ~(F_KVERB); if (id != K_IGNORE) { for (i = 0; i< nkverbs; i++) if (id == kverbs[i].kwval) { name = kverbs[i].kwd; break; } printf(" %s = Kverb: \\K%s\n", mousename(button,event), name ); } break; case macro: printf(" %s = Macro: ", mousename(button,event) ); shostrdef(mousemap[button][event].macro.string); printf("\n"); break; } } #endif /* OS2MOUSE */ #ifndef NOLOCAL VOID shotrm() { char *s; extern char * getiact(); extern int tt_print, adl_err; #ifndef NOTRIGGER extern char * tt_trigger[]; #endif /* NOTRIGGER */ #ifdef CKTIDLE extern char * tt_idlesnd_str; extern int tt_idlesnd_tmo; extern int tt_idlelimit, tt_idleact; #endif /* CKTIDLE */ #ifdef OS2 extern int wy_autopage, autoscroll, sgrcolors, colorreset, user_erasemode, decscnm, decscnm_usr, tt_diff_upd, tt_senddata, wy_blockend, marginbell, marginbellcol, tt_modechg, dgunix; int lines = 0; #ifdef KUI extern CKFLOAT tt_linespacing[]; extern tt_cursor_blink; #endif /* KUI */ #ifdef PCFONTS int i; char *font; if (IsOS2FullScreen()) { /* Determine the font name */ if (!os2LoadPCFonts()) { for (i = 0; i < ntermfont; i++) { if (tt_font == term_font[i].kwval) { font = term_font[i].kwd; break; } } } else { font = "(DLL not available)"; } } else { font = "(full screen only)"; } #endif /* PCFONTS */ #ifdef KUI char font[64] = "(unknown)"; if ( ntermfont > 0 ) { int i; for (i = 0; i < ntermfont; i++) { if (tt_font == term_font[i].kwval) { ckstrncpy(font,term_font[i].kwd,59); ckstrncat(font," ",64); ckstrncat(font,ckitoa(tt_font_size/2),64); if ( tt_font_size % 2 ) ckstrncat(font,".5",64); break; } } } #endif /* KUI */ printf("Terminal parameters:\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return; else lines = 0; } printf(" %19s: %1d%-12s %13s: %1d%-14s\n", "Bytesize: Command", (cmdmsk == 0377) ? 8 : 7, " bits","Terminal", (cmask == 0377) ? 8 : 7," bits"); if (++lines > cmd_rows - 3) { if (!askmore()) return; else lines = 0; } printf(" %19s: %-13s","Type", (tt_type >= 0 && tt_type <= max_tt) ? tt_info[tt_type].x_name : "unknown" ); if (tt_type >= 0 && tt_type <= max_tt) if (strlen(tt_info[tt_type].x_id)) printf(" %13s: %s","ID",tt_info[tt_type].x_id); printf("\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return; else lines = 0; } printf(" %19s: %-13s %13s: %-15s\n","Echo", duplex ? "local" : "remote","Locking-shift",showoff(sosi)); if (++lines > cmd_rows - 3) { if (!askmore()) return; else lines = 0; } printf(" %19s: %-13s %13s: %-15s\n","Newline-mode", showoff(tnlm), "Cr-display",tt_crd ? "crlf" : "normal"); if (++lines > cmd_rows - 3) { if (!askmore()) return; else lines = 0; } printf(" %19s: %-13s %13s: %-15s\n","Cursor", #ifdef KUI (tt_cursor == 2) ? (tt_cursor_blink ? "full (*)" : "full (.)") : (tt_cursor == 1) ? (tt_cursor_blink ? "half (*)" : "half (.)") : (tt_cursor_blink ? "underline (*)" : "underline (.)"), #else /* KUI */ (tt_cursor == 2) ? "full" : (tt_cursor == 1) ? "half" : "underline", #endif /* KUI */ #ifdef CK_AUTODL "autodownload",autodl == TAD_ON ? (adl_err ? "on, error stop" : "on, error continue") : autodl == TAD_ASK ? (adl_err ? "ask, error stop" : "ask, error continue") : "off" #else /* CK_AUTODL */ "", "" #endif /* CK_AUTODL */ ); if (++lines > cmd_rows - 3) { if (!askmore()) return; else lines = 0; } printf(" %19s: %-13s %13s: %-15s\n","Arrow-keys", tt_arrow ? "application" : "cursor", "Keypad-mode", tt_keypad ? "application" : "numeric" ); if (++lines > cmd_rows - 3) { if (!askmore()) return; else lines = 0; } /* Just to make sure we are using current info */ updanswerbk(); /* This line doesn't end with '\n' because the answerback string is terminated with a newline */ printf(" %19s: %-13s %13s: %-15s","Answerback", showoff(tt_answer),"response",answerback); switch (tt_bell) { case XYB_NONE: s = "none"; break; case XYB_VIS: s= "visible"; break; case XYB_AUD | XYB_BEEP: s="beep"; break; case XYB_AUD | XYB_SYS: s="system sounds"; break; default: s="(unknown)"; } printf(" %19s: %-13s %13s: %-15s\n","Bell",s, "Wrap",showoff(tt_wrap)); if (++lines > cmd_rows - 3) { if (!askmore()) return; else lines = 0; } printf(" %19s: %-13s %13s: %-15s\n","Autopage",showoff(wy_autopage), "Autoscroll",showoff(autoscroll)); if (++lines > cmd_rows - 3) { if (!askmore()) return; else lines = 0; } printf(" %19s: %-13s %13s: %-15s\n","SGR Colors",showoff(sgrcolors), "ESC[0m color",colorreset?"default-color":"current-color"); if (++lines > cmd_rows - 3) { if (!askmore()) return; else lines = 0; } printf(" %19s: %-13s %13s: %-15s\n", "Erase color",user_erasemode?"default-color":"current-color", "Screen mode",decscnm?"reverse":"normal"); if (++lines > cmd_rows - 3) { if (!askmore()) return; else lines = 0; } printf(" %19s: %-13d %13s: %-15d\n","Transmit-timeout",tt_ctstmo, "Output-pacing",tt_pacing); if (++lines > cmd_rows - 3) { if (!askmore()) return; else lines = 0; } printf(" %19s: %-13d %13s: %s\n","Idle-timeout",tt_idlelimit, "Idle-action", getiact()); if (++lines > cmd_rows - 3) { if (!askmore()) return; else lines = 0; } printf(" %19s: %-13s %13s: %-15s\n","Send data", showoff(tt_senddata),"End of Block", wy_blockend?"crlf/etx":"us/cr"); if (++lines > cmd_rows - 3) { if (!askmore()) return; else lines = 0; } #ifndef NOTRIGGER printf(" %19s: %-13s %13s: %d seconds\n","Auto-exit trigger", tt_trigger[0],"Output pacing",tt_pacing ); if (++lines > cmd_rows - 3) { if (!askmore()) return; else lines = 0; } #endif /* NOTRIGGER */ printf(" %19s: %-13s %13s: %-15d\n","Margin bell", showoff(marginbell),"at column", marginbellcol); if (++lines > cmd_rows - 3) { if (!askmore()) return; else lines = 0; } switch (tt_modechg) { case TVC_DIS: s = "disabled"; break; case TVC_ENA: s = "enabled"; break; case TVC_W95: s = "win95-restricted"; break; default: s = "(unknown)"; } printf(" %19s: %-13s %13s: %-15s\n","DG Unix mode", showoff(dgunix),"Video change", s); if (++lines > cmd_rows - 3) { if (!askmore()) return; else lines = 0; } #ifdef CK_APC if (apcstatus == APC_ON) s = "on"; else if (apcstatus == APC_OFF) s = "off"; else if (apcstatus == APC_ON|APC_UNCH) s = "unchecked"; else if (apcstatus == APC_ON|APC_NOINP) s = "no-input"; else if (apcstatus == APC_ON|APC_UNCH|APC_NOINP) s = "unchecked-no-input"; printf(" %19s: %-13s %13s: %-15s\n", "APC", s, #ifdef PCFONTS "Font (VGA)",font #else /* PCFONTS */ #ifdef KUI "Font",font #else "Font","(not supported)" #endif /* KUI */ #endif /* PCFONTS */ ); #endif /* CK_APC */ if (++lines > cmd_rows - 3) { if (!askmore()) return; else lines = 0; } #ifdef CK_TTGWSIZ /* Console terminal screen size */ if (tt_cols[VTERM] < 0 || tt_rows[VTERM] < 0) ttgwsiz(); /* Try to get latest size */ #endif /* CK_TTGWSIZ */ printf(" %19s: %-13d %13s: %-15d\n","Height",tt_rows[VTERM], "Width",tt_cols[VTERM]); if (++lines > cmd_rows - 3) { if (!askmore()) return; else lines = 0; } #ifdef KUI printf(" %19s: %-12f %14s: %-15d\n","Line spacing",tt_linespacing[VTERM], "Display Height",VscrnGetDisplayHeight(VTERM)); if (++lines > cmd_rows - 3) { if (!askmore()) return; else lines = 0; } #endif /* KUI */ printf(" %19s: %-13s %13s: %d lines\n","Roll-mode", tt_roll[VTERM]?"insert":"overwrite","Scrollback", tt_scrsize[VTERM]); if (++lines > cmd_rows - 3) { if (!askmore()) return; else lines = 0; } if (updmode == tt_updmode) if (updmode == TTU_FAST) s = "fast (fast)"; else s = "smooth (smooth)"; else if (updmode == TTU_FAST) s = "fast (smooth)"; else s = "smooth (fast)"; printf(" %19s: %-13s %13s: %d ms\n","Screen-update: mode",s, "interval",tt_update); if (++lines > cmd_rows - 3) { if (!askmore()) return; else lines = 0; } printf(" %19s: %-13s %13s: %-15s\n", "Screen-optimization",showoff(tt_diff_upd), "Status line",showoff(tt_status[VTERM])); if (++lines > cmd_rows - 3) { if (!askmore()) return; else lines = 0; } printf(" %19s: %-13s %13s: %-15s\n","Debug", showoff(debses),"Session log", seslog? sesfil : "(none)" ); if (++lines > cmd_rows - 3) { if (!askmore()) return; else lines = 0; } /* Display colors (should become SHOW COLORS) */ { USHORT row, col; char * colors[16] = { "black","blue","green","cyan","red","magenta","brown","lgray", "dgray","lblue","lgreen","lcyan","lred","lmagent","yellow","white" }; printf("\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return; else lines = 0; } printf(" Color:"); #ifndef ONETERMUPD GetCurPos(&row, &col); WrtCharStrAtt("border", 6, row, 9, &colorborder ); WrtCharStrAtt("debug", 5, row, 17, &colordebug ); WrtCharStrAtt("helptext", 8, row, 25, &colorhelp ); WrtCharStrAtt("reverse", 7, row, 34, &colorreverse ); WrtCharStrAtt("select", 6, row, 42, &colorselect ); WrtCharStrAtt("status", 6, row, 50, &colorstatus ); WrtCharStrAtt("terminal", 8, row, 58, &colornormal ); WrtCharStrAtt("underline", 9, row, 67, &colorunderline ); #endif /* ONETERMUPD */ row = VscrnGetCurPos(VCMD)->y+1; VscrnWrtCharStrAtt(VCMD, "border", 6, row, 9, &colorborder ); VscrnWrtCharStrAtt(VCMD, "debug", 5, row, 17, &colordebug ); VscrnWrtCharStrAtt(VCMD, "helptext", 8, row, 25, &colorhelp ); VscrnWrtCharStrAtt(VCMD, "reverse", 7, row, 34, &colorreverse ); VscrnWrtCharStrAtt(VCMD, "select", 6, row, 42, &colorselect ); VscrnWrtCharStrAtt(VCMD, "status", 6, row, 50, &colorstatus ); VscrnWrtCharStrAtt(VCMD, "terminal", 8, row, 58, &colornormal ); VscrnWrtCharStrAtt(VCMD, "underline", 9, row, 67, &colorunderline ); printf("\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return; else lines = 0; } /* Foreground color names */ printf("%6s: %-8s%-8s%-9s%-8s%-8s%-8s%-9s%-9s\n","fore", "", colors[colordebug&0x0F], colors[colorhelp&0x0F], colors[colorreverse&0x0F], colors[colorselect&0x0F], colors[colorstatus&0x0F], colors[colornormal&0x0F], colors[colorunderline&0x0F]); if (++lines > cmd_rows - 3) { if (!askmore()) return; else lines = 0; } /* Background color names */ printf("%6s: %-8s%-8s%-9s%-8s%-8s%-8s%-9s%-9s\n","back", colors[colorborder], colors[colordebug>>4], colors[colorhelp>>4], colors[colorreverse>>4], colors[colorselect>>4], colors[colorstatus>>4], colors[colornormal>>4], colors[colorunderline>>4] ); if (++lines > cmd_rows - 3) { if (!askmore()) return; else lines = 0; } printf("\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return; else lines = 0; } printf(" Color:"); #ifndef ONETERMUPD GetCurPos(&row, &col); WrtCharStrAtt("graphic", 7, row, 9, &colorgraphic ); WrtCharStrAtt("command", 7, row, 17, &colorcmd ); WrtCharStrAtt("italic", 6, row, 26, &coloritalic ); #endif /* ONETERMUPD */ row = VscrnGetCurPos(VCMD)->y+1; VscrnWrtCharStrAtt(VCMD, "graphic", 7, row, 9, &colorgraphic ); VscrnWrtCharStrAtt(VCMD, "command", 7, row, 17, &colorcmd ); VscrnWrtCharStrAtt(VCMD, "italic", 6, row, 26, &coloritalic ); printf("\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return; else lines = 0; } /* Foreground color names */ printf("%6s: %-8s%-8s%-8s\n","fore", colors[colorgraphic&0x0F], colors[colorcmd&0x0F], colors[coloritalic&0x0F]); if (++lines > cmd_rows - 3) { if (!askmore()) return; else lines = 0; } /* Background color names */ printf("%6s: %-8s%-8s%-8s\n","back", colors[colorgraphic>>4], colors[colorcmd>>4], colors[coloritalic>>4]); if (++lines > cmd_rows - 3) { if (!askmore()) return; else lines = 0; } } printf("\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return; else lines = 0; } { extern int trueblink, truedim, truereverse, trueunderline, trueitalic; printf( " Attribute: \ blink: %-3s dim: %-3s italic: %-3s reverse: %-3s underline: %-3s\n", trueblink?"on":"off", truedim?"on":"off", trueitalic?"on":"off", truereverse?"on":"off", trueunderline?"on":"off"); if (++lines > cmd_rows - 3) { if (!askmore()) return; else lines = 0; } } { extern vtattrib WPattrib; printf(" ASCII Protected chars: %s%s%s%s%s%s%s\n", WPattrib.blinking?"blink ":"", WPattrib.italic?"italic ":"", WPattrib.reversed?"reverse ":"", WPattrib.underlined?"underline ":"", WPattrib.bold?"bold ":"", WPattrib.dim?"dim ":"", WPattrib.invisible?"invisible ":""); if (++lines > cmd_rows - 3) { if (!askmore()) return; else lines = 0; } } printf("\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return; else lines = 0; } (VOID) shoesc(escape); if (++lines > cmd_rows - 3) { if (!askmore()) return; else lines = 0; } printf(" See SHOW CHARACTER-SETS for character-set info\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return; else lines = 0; } #else /* OS2 */ /* Beginning of new non-OS2 version */ printf("\n"); printf("Terminal parameters:\n"); printf(" %19s: %1d%-12s %13s: %1d%-14s\n", "Bytesize: Command", (cmdmsk == 0377) ? 8 : 7, " bits","Terminal", (cmask == 0377) ? 8 : 7," bits"); s = getenv("TERM"); #ifdef XPRINT printf(" %19s: %-13s %13s: %-15s\n", "Type", s ? s : "(unknown)", "Print", showoff(tt_print) ); #else printf(" %19s: %-13s\n","Type", s ? s : "(unknown)"); #endif /* XPRINT */ printf(" %19s: %-13s %13s: %-15s\n","Echo", duplex ? "local" : "remote","Locking-shift",showoff(sosi)); printf(" %19s: %-13s %13s: %-15s\n","Newline-mode", showoff(tnlm),"Cr-display",tt_crd ? "crlf" : "normal"); #ifdef CK_APC if (apcstatus == APC_ON) s = "on"; else if (apcstatus == APC_OFF) s = "off"; else if (apcstatus == (APC_ON|APC_UNCH)) s = "unchecked"; else if (apcstatus == (APC_ON|APC_NOINP)) s = "no-input"; else if (apcstatus == (APC_ON|APC_UNCH|APC_NOINP)) s = "unchecked-no-input"; printf(" %19s: %-13s %13s: %-15s\n", "APC", s, #ifdef CK_AUTODL "Autodownload", autodl ? (adl_err ? "on, error stop" : "on, error continue") : "off" #else "","" #endif /* CK_AUTODL */ ); #endif /* CK_APC */ #ifdef CK_TTGWSIZ /* Console terminal screen size */ ttgwsiz(); /* Try to get latest size */ printf(" %19s: %-13d %13s: %-15d\n","Height",tt_rows, "Width", tt_cols); #endif /* CK_TTGWSIZ */ printf(" %19s: %-13s %13s: %-15s\n","Debug", showoff(debses),"Session log", seslog? sesfil : "(none)" ); #ifdef CKTIDLE printf(" %19s: %-13d %13s: %s\n","Idle-timeout",tt_idlelimit, "Idle-action", getiact()); #endif /* CKTIDLE */ printf(" %19s: %-13s ","Lf-display", tt_lfd ? "crlf" : "normal"); #ifdef UNIX #ifndef NOJC printf("%13s: %-15s","Suspend", showoff(xsuspend)); #endif /* NOJC */ #endif /* UNIX */ printf("\n"); #ifndef NOTRIGGER printf(" %19s: %-13s\n","Trigger", tt_trigger[0] ? tt_trigger[0] : "(none)"); #endif /* NOTRIGGER */ printf("\n"); (VOID) shoesc(escape); #ifndef NOCSETS shotcs(tcsl,tcsr); /* Show terminal character sets */ #endif /* NOCSETS */ #endif /* OS2 */ } VOID shmdmlin() { /* Briefly show modem & line */ #ifndef NODIAL #ifndef MINIDIAL #ifdef OLDTBCODE extern int tbmodel; _PROTOTYP( char * gtbmodel, (void) ); #endif /* OLDTBCODE */ #endif /* MINIDIAL */ #endif /* NODIAL */ if (local) #ifdef OS2 printf(" Port: %s, Modem type: ",ttname); #else printf(" Line: %s, Modem type: ",ttname); #endif /* OS2 */ else printf( #ifdef OS2 " Communication device not yet selected with SET PORT\n Modem type: " #else " Communication device not yet selected with SET LINE\n Modem type: " #endif /* OS2 */ ); #ifndef NODIAL printf("%s",gmdmtyp()); #ifndef MINIDIAL #ifdef OLDTBCODE if (tbmodel) printf(" (%s)",gtbmodel()); /* Telebit model info */ #endif /* OLDTBCODE */ #endif /* MINIDIAL */ #else printf("(disabled)"); #endif /* NODIAL */ } #ifdef CK_TAPI void shotapi(int option) { int rc=0,k ; char *s=NULL; LPDEVCFG lpDevCfg = NULL; LPCOMMCONFIG lpCommConfig = NULL; LPMODEMSETTINGS lpModemSettings = NULL; DCB * lpDCB = NULL; extern struct keytab * tapiloctab; /* Microsoft TAPI Locations */ extern int ntapiloc; extern struct keytab * tapilinetab; /* Microsoft TAPI Line Devices */ extern int ntapiline; extern int tttapi; /* TAPI in use */ extern int tapipass; /* TAPI Passthrough mode */ extern int tapiconv; /* TAPI Conversion mode */ extern int tapilights; extern int tapipreterm; extern int tapipostterm; extern int tapimanual; extern int tapiinactivity; extern int tapibong; extern int tapiusecfg; extern char tapiloc[]; extern int tapilocid; extern int TAPIAvail; if (!TAPIAvail) { printf("TAPI Support not enabled\r\n"); return; } switch (option) { case 0: printf("TAPI Settings:\n"); printf(" Line: %s\n", tttapi ? ttname : "(none in use)"); cktapiBuildLocationTable(&tapiloctab, &ntapiloc); if (tapilocid == -1) tapilocid = cktapiGetCurrentLocationID(); /* Find the current tapiloc entry */ /* and use it as the default. */ for (k = 0; k < ntapiloc; k++) { if (tapiloctab[k].kwval == tapilocid) break; } if (k >= 0 && k < ntapiloc) s = tapiloctab[k].kwd; else s = "(unknown)"; printf(" Location: %s\n",s); printf(" Modem-dialing: %s\n",tapipass?"off":"on"); printf(" Phone-number-conversions: %s\n", tapiconv==CK_ON?"on":tapiconv==CK_AUTO?"auto":"off"); printf(" Modem-lights: %s %s\n",tapilights?"on ":"off", tapipass?"(n/a)":""); printf(" Predial-terminal: %s %s\n",tapipreterm?"on ":"off", tapipass?"(n/a)":""); printf(" Postdial-terminal: %s %s\n",tapipostterm?"on ":"off", tapipass?"(n/a)":""); printf(" Manual-dial: %s %s\n",tapimanual?"on ":"off", tapipass?"(n/a)":""); printf(" Inactivity-timeout: %d seconds %s\n",tapiinactivity, tapipass?"(n/a)":""); printf(" Wait-for-bong: %d seconds %s\n",tapibong, tapipass?"(n/a)":""); printf(" Use-windows-configuration: %s %s\n", tapiusecfg?"on ":"off", tapipass?"(n/a)":""); printf("\n"); #ifdef BETATEST if (tapipass) { printf("K-95 uses the TAPI Line in an exclusive mode. Other applications\n"); printf("may open the device but may not place calls nor answer calls.\n"); printf("Dialing is performed using the K-95 dialing procedures. SET MODEM\n"); printf("TYPE TAPI after the SET TAPI LINE command to activate the modem\n"); printf("definition associated with the active TAPI LINE device.\n\n"); } else { printf("K-95 uses the TAPI Line in a cooperative mode. Other applications\n"); printf("may open the device, place and answer calls. Dialing is performed\n"); printf("by TAPI. K-95 SET MODEM commands are not used.\n\n"); } if (tapiconv == CK_ON || tapiconv == CK_AUTO && !tapipass) { printf( "Phone numbers are converted from canonical to dialable form by TAPI\n"); printf("using the dialing rules specified in the TAPI Dialing Properties\n"); printf("dialog.\n\n"); } else { printf( "Phone numbers are converted from canonical to dialable form by K-95\n"); printf( "using the dialing rules specified with the SET DIAL commands. TAPI\n"); printf( "Dialing Properties are imported automaticly upon startup and whenever\n"); printf("the TAPI Dialing Properties are altered or when the TAPI Location\n"); printf("is changed.\n\n"); } #endif /* BETATEST */ if (tapipass) { printf("Type SHOW MODEM to see MODEM configuration.\n"); if (tapiconv == CK_ON) printf("Type SHOW DIAL to see DIAL-related items.\n"); } else { if (tapiconv == CK_ON || tapiconv == CK_AUTO) printf("Type SHOW DIAL to see DIAL-related items.\n"); } break; case 1: cktapiDisplayTapiLocationInfo(); break; case 2: rc = cktapiGetModemSettings(&lpDevCfg,&lpModemSettings, &lpCommConfig,&lpDCB); if (rc) { cktapiDisplayModemSettings(lpDevCfg,lpModemSettings, lpCommConfig,lpDCB); } else { printf("?Unable to retrieve Modem Settings\n"); } break; case 3: { HANDLE hModem = GetModemHandleFromLine((HLINE)0); if (hModem) DisplayCommProperties(hModem); else printf("?Unable to retrieve a valid Modem Handle\n"); CloseHandle(hModem); break; } } printf("\n"); } #endif /* CK_TAPI */ #endif /* NOLOCAL */ #ifdef PATTERNS static VOID shopat() { extern char * binpatterns[], * txtpatterns[]; extern int patterns, filepeek; char **p, *s; int i, j, k, n, flag, width; #ifdef CK_TTGWSIZ ttgwsiz(); /* Try to get latest size */ #ifdef OS2 width = tt_cols[VCMD]; #else /* OS2 */ width = tt_cols; #endif /* OS2 */ if (width < 1) #endif /* CK_TTGWSIZ */ width = 80; printf("\n"); printf(" Set file type: %s\n",gfmode(binary,1)); printf(" Set file patterns: %s", showooa(patterns)); #ifdef CK_LABELED if (binary == XYFT_L) printf(" (but SET FILE TYPE LABELED overrides)\n"); else #endif /* CK_LABELED */ #ifdef VMS if (binary == XYFT_I) printf(" (but SET FILE TYPE IMAGE overrides)\n"); else #endif /* VMS */ if (filepeek) printf(" (but SET FILE SCAN ON overrides)\n"); else printf("\n"); printf(" Maximum patterns allowed: %d\n", FTPATTERNS); for (k = 0; k < 2; k++) { /* For each kind of patter */ printf("\n"); if (k == 0) { /* binary... */ printf(" File binary-patterns: "); p = binpatterns; } else { /* text... */ printf(" File text-patterns: "); p = txtpatterns; } if (!p[0]) { printf("(none)\n"); } else { printf("\n "); n = 2; for (i = 0; i < FTPATTERNS; i++) { /* For each pattern */ if (!p[i]) /* Done */ break; s = p[i]; /* Look for embedded space */ for (j = 0, flag = 1; *s; s++, j++) /* and also get length */ if (*s == SP) flag = 3; n += j + flag; /* Length of this line */ if (n >= width - 1) { printf("\n "); n = j+2; } printf(flag == 3 ? " {%s}" : " %s", p[i]); } if (n > 2) printf("\n"); } } printf("\n"); } #endif /* PATTERNS */ #ifndef NOSPL static VOID shooutput() { printf(" Output pacing: %d (milliseconds)\n",pacing); printf(" Output special-escapes: %s\n", showoff(outesc)); } static VOID shoinput() { #ifdef CKFLOAT extern char * inpscale; #endif /* CKFLOAT */ #ifdef CK_AUTODL printf(" Input autodownload: %s\n", showoff(inautodl)); #endif /* CK_AUTODL */ printf(" Input cancellation: %s\n", showoff(inintr)); printf(" Input case: %s\n", inpcas[cmdlvl] ? "observe" : "ignore"); printf(" Input buffer-length: %d\n", inbufsize); printf(" Input echo: %s\n", showoff(inecho)); printf(" Input silence: %d (seconds)\n", insilence); #ifdef OS2 printf(" Input terminal: %s\n", showoff(interm)); #endif /* OS2 */ printf(" Input timeout: %s\n", intime[cmdlvl] ? "quit" : "proceed"); #ifdef CKFLOAT printf(" Input scale-factor: %s\n", inpscale ? inpscale : "1.0"); #endif /* CKFLOAT */ if (instatus < 0) printf(" Last INPUT: -1 (INPUT command not yet given)\n"); else printf(" Last INPUT: %d (%s)\n", instatus,i_text[instatus]); } #endif /* NOSPL */ #ifndef NOSPL int showarray() { #ifdef COMMENT char * p, * q, ** ap; int i; #endif /* COMMENT */ char *s; int x = 0, y; int range[2]; if ((y = cmfld("Array name","",&s,NULL)) < 0) if (y != -3) return(y); ckstrncpy(line,s,LINBUFSIZ); s = line; if ((y = cmcfm()) < 0) return(y); if (*s) { char ** ap; if ((x = arraybounds(s,&(range[0]),&(range[1]))) < 0) { printf("?Bad array: %s\n",s); return(-9); } ap = a_ptr[x]; if (!ap) { printf("Array not declared: %s\n", s); return(success = 1); } else { int i, n, max; max = (range[1] > 0) ? range[1] : ((range[0] > 0) ? range[0] : a_dim[x]); if (range[0] < 0) range[0] = 0; if (max > a_dim[x]) max = a_dim[x]; n = 1; printf("\\&%c[]: Dimension = %d",arrayitoa(x),a_dim[x]); if (a_link[x] > -1) printf(" (Link to \\&%c[])",arrayitoa(a_link[x])); printf("\n"); for (i = range[0]; i <= max; i++) { if (ap[i]) { printf("%3d. %s\n",i,ap[i]); if (xaskmore) { if (cmd_cols > 0) { x = strlen(ap[i]) + 5; y = (x % cmd_cols) ? 1 : 0; n += (x / cmd_cols) + y; } else { n++; } if (n > (cmd_rows - 3)) { if (!askmore()) break; else n = 0; } } } } } return(1); } /* All arrays - just show name and dimension */ for (y = 0; y < (int) 'z' - ARRAYBASE + 1; y++) { if (a_ptr[y]) { if (x == 0) printf("Declared arrays:\n"); x = 1; printf(" \\&%c[%d]", (y == 1) ? 64 : y + ARRAYBASE, a_dim[y]); if (a_link[y] > -1) printf(" => \\&%c[]",arrayitoa(a_link[y])); printf("\n"); } if (!x) printf(" No arrays declared\n"); } return(1); } #endif /* NOSPL */ int doshow(x) int x; { int y, z, i; long zz; extern int optlines; char *s; #ifdef OS2 extern int os2gks; extern int tt_kb_mode; #endif /* OS2 */ extern int srvcdmsg; extern char * cdmsgstr, * ckcdpath; #ifndef NOSETKEY if (x == SHKEY) { /* SHOW KEY */ int c; #ifdef OS2 if ((x = cmkey(shokeytab,nshokey,"How many keys should be shown?", "one",xxstring)) < 0) return(x); switch (tt_kb_mode) { case KBM_EM: s = "emacs"; break; case KBM_HE: s = "hebrew"; break; case KBM_RU: s = "russian"; break; case KBM_EN: default: s = "default"; break; } if ((z = cmkey(shokeymtab,nshokeym,"Which definition should be shown?", s,xxstring)) < 0) return(z); if (z == SHKEYDEF) z = -1; #endif /* OS2 */ if ((y = cmcfm()) < 0) return(y); #ifdef IKSD if (inserver) { printf("Sorry, command disabled.\r\n"); return(success = 0); } #endif /* IKSD */ #ifdef MAC printf("Not implemented\n"); return(0); #else /* Not MAC */ #ifdef OS2 if (x) { con_event evt; for (c = 0; c < KMSIZE; c++) { evt = (z < 0) ? mapkey(c) : maptermkey(c,z); if (evt.type != error) { shokeycode(c,z); } } } else { #endif /* OS2 */ printf(" Press key: "); #ifdef UNIX #ifdef NOSETBUF fflush(stdout); #endif /* NOSETBUF */ #endif /* UNIX */ conbin((char)escape); /* Put terminal in binary mode */ #ifdef OS2 os2gks = 0; /* Raw scancode processing */ #endif /* OS2 */ c = congks(0); /* Get character or scan code */ #ifdef OS2 os2gks = 1; /* Cooked scancode processing */ #endif /* OS2 */ concb((char)escape); /* Restore terminal to cbreak mode */ if (c < 0) { /* Check for error */ printf("?Error reading key\n"); return(0); } #ifndef OS2 /* Do NOT mask when it can be a raw scan code, perhaps > 255 */ c &= cmdmsk; /* Apply command mask */ #endif /* OS2 */ printf("\n"); #ifdef OS2 shokeycode(c,z); #else /* OS2 */ shokeycode(c); #endif /* OS2 */ #ifdef OS2 } #endif /* OS2 */ return(1); #endif /* MAC */ } #ifndef NOKVERBS if (x == SHKVB) { /* SHOW KVERBS */ if ((y = cmcfm()) < 0) return(y); #ifdef IKSD if (inserver) { printf("Sorry, command disabled.\r\n"); return(success = 0); } #endif /* IKSD */ printf("\nThe following %d keyboard verbs are available:\n\n",nkverbs); kwdhelp(kverbs,nkverbs,"","\\K","",3,0); printf("\n"); return(1); } #ifdef OS2 if (x == SHUDK) { /* SHOW UDKs */ extern void showudk(void); if ((y = cmcfm()) < 0) return(y); #ifdef IKSD if (inserver) { printf("Sorry, command disabled.\r\n"); return(success = 0); } #endif /* IKSD */ showudk(); return(1); } #endif /* OS2 */ #endif /* NOKVERBS */ #endif /* NOSETKEY */ #ifndef NOSPL if (x == SHMAC) { /* SHOW MACRO */ struct FDB kw, fl, cm; int i, k, n = 0, left, flag, confirmed = 0; char * p, *q[64]; for (i = 0; i < nmac; i++) { /* copy the macro table */ mackey[i].kwd = mactab[i].kwd; /* into a regular keyword table */ mackey[i].kwval = i; /* with value = pointer to macro tbl */ mackey[i].flgs = mactab[i].flgs; } p = line; left = LINBUFSIZ; while (!confirmed && n < 64) { cmfdbi(&kw, /* First FDB - macro table */ _CMKEY, /* fcode */ "Macro name", /* hlpmsg */ "", /* default */ "", /* addtl string data */ nmac, /* addtl numeric data 1: tbl size */ 0, /* addtl numeric data 2: 4 = cmswi */ xxstring, /* Processing function */ mackey, /* Keyword table */ &fl /* Pointer to next FDB */ ); cmfdbi(&fl, /* 2nd FDB - something not in mactab */ _CMFLD, /* fcode */ "", /* hlpmsg */ "", /* default */ "", /* addtl string data */ 0, /* addtl numeric data 1 */ 0, /* addtl numeric data 2 */ xxstring, NULL, &cm ); cmfdbi(&cm, /* 3rd FDB - Confirmation */ _CMCFM, /* fcode */ "", /* hlpmsg */ "", /* default */ "", /* addtl string data */ 0, /* addtl numeric data 1 */ 0, /* addtl numeric data 2 */ NULL, NULL, NULL ); x = cmfdb(&kw); /* Parse something */ if (x < 0) return(x); s = atmbuf; /* What the user typed */ switch (cmresult.fcode) { case _CMKEY: /* If it was a keyword */ y = mlook(mactab,atmbuf,nmac); /* get full name */ if (y > -1) s = mactab[y].kwd; /* (fall thru on purpose...) */ case _CMFLD: k = ckstrncpy(p,s,left) + 1; /* Copy result to list */ left -= k; if (left <= 0) { *p = NUL; break; } q[n++] = p; /* Point to this item */ p += k; /* Move buffer pointer past it */ break; case _CMCFM: /* End of command */ confirmed++; default: break; } } if (n == 0) { printf("Macros:\n"); slc = 1; for (y = 0; y < nmac; y++) if (shomac(mactab[y].kwd,mactab[y].mval) < 0) break; return(1); } slc = 0; for (i = 0; i < n; i++) { flag = 0; s = q[i]; if (!s) s = ""; if (!*s) continue; if (iswild(s)) { /* Pattern match */ for (k = 0, x = 0; x < nmac; x++) { if (ckmatch(s,mactab[x].kwd,0,1)) { shomac(mactab[x].kwd,mactab[x].mval); k++; } } if (!k) x = -1; else continue; } else { /* Exact match */ x = mxlook(mactab,s,nmac); flag = 1; } if (flag && x == -1) x = mlook(mactab,s,nmac); switch (x) { case -3: /* Nothing to look up */ case -1: /* Not found */ printf("%s - (not defined)\n",s); break; case -2: /* Ambiguous, matches more than one */ printf("%s - ambiguous\n",s); break; default: /* Matches one exactly */ shomac(mactab[x].kwd,mactab[x].mval); break; } } return(1); } #endif /* NOSPL */ /* Other SHOW commands only have two fields. Get command confirmation here, then handle with big switch() statement. */ #ifndef NOSPL if (x != SHBUI && x != SHARR) #endif /* NOSPL */ if ((y = cmcfm()) < 0) return(y); #ifdef COMMENT /* This restriction is too general. */ #ifdef IKSD if (inserver && #ifdef CK_LOGIN isguest #else 0 #endif /* CK_LOGIN */ ) { printf("Sorry, command disabled.\r\n"); return(success = 0); } #endif /* IKSD */ #endif /* COMMENT */ switch (x) { #ifdef ANYX25 #ifndef IBMX25 case SHPAD: shopad(0); break; #endif /* IBMX25 */ #endif /* ANYX25 */ case SHNET: #ifdef NOLOCAL printf(" No network support in this version of C-Kermit.\n"); #else #ifndef NETCONN printf(" No network support in this version of C-Kermit.\n"); #else shonet(); #endif /* NETCONN */ #endif /* NOLOCAL */ break; case SHPAR: shopar(); break; #ifndef NOXFER case SHATT: shoatt(); break; #endif /* NOXFER */ #ifndef NOSPL case SHCOU: printf(" %d\n",count[cmdlvl]); break; #endif /* NOSPL */ #ifndef NOSERVER case SHSER: /* Show Server */ i = 0; #ifndef NOFRILLS printf("Function: Status:\n"); i++; printf(" GET %s\n",nm[en_get]); i++; printf(" SEND %s\n",nm[en_sen]); i++; printf(" MAIL %s\n",nm[inserver ? 0 : en_mai]); i++; printf(" PRINT %s\n",nm[inserver ? 0 : en_pri]); i++; #ifndef NOSPL printf(" REMOTE ASSIGN %s\n",nm[en_asg]); i++; #endif /* NOSPL */ printf(" REMOTE CD/CWD %s\n",nm[en_cwd]); i++; #ifdef ZCOPY printf(" REMOTE COPY %s\n",nm[en_cpy]); i++; #endif /* ZCOPY */ printf(" REMOTE DELETE %s\n",nm[en_del]); printf(" REMOTE DIRECTORY %s\n",nm[en_dir]); printf(" REMOTE HOST %s\n",nm[inserver ? 0 : en_hos]); i += 3; #ifndef NOSPL printf(" REMOTE QUERY %s\n",nm[en_que]); i++; #endif /* NOSPL */ printf(" REMOTE MKDIR %s\n",nm[en_mkd]); printf(" REMOTE RMDIR %s\n",nm[en_rmd]); printf(" REMOTE RENAME %s\n",nm[en_ren]); printf(" REMOTE SET %s\n",nm[en_set]); printf(" REMOTE SPACE %s\n",nm[en_spa]); printf(" REMOTE TYPE %s\n",nm[en_typ]); printf(" REMOTE WHO %s\n",nm[inserver ? 0 : en_who]); printf(" BYE %s\n",nm[en_bye]); printf(" FINISH %s\n",nm[en_fin]); printf(" EXIT %s\n",nm[en_xit]); printf(" ENABLE %s\n",nm[en_ena]); i += 11; #endif /* NOFRILLS */ if (i > cmd_rows - 3) { if (!askmore()) return(1); else i = 0; } printf("Server timeout: %d\n",srvtim); if (++i > cmd_rows - 3) { if (!askmore()) return(1); else i = 0; } printf("Server idle-timeout: %d\n",srvidl); if (++i > cmd_rows - 3) { if (!askmore()) return(1); else i = 0; } printf("Server keepalive %s\n", showoff(srvping)); if (++i > cmd_rows - 3) { if (!askmore()) return(1); else i = 0; } printf("Server cd-message %s\n", showoff(srvcdmsg)); if (srvcdmsg && cdmsgstr) printf("Server cd-message %s\n", cdmsgstr); if (++i > cmd_rows - 3) { if (!askmore()) return(1); else i = 0; } printf("Server display: %s\n", showoff(srvdis)); if (++i > cmd_rows - 3) { if (!askmore()) return(1); else i = 0; } printf("Server login: "); if (!x_user) { printf("(none)\n"); } else { printf("\"%s\", \"%s\", \"%s\"\n", x_user, x_passwd ? x_passwd : "", x_acct ? x_acct : "" ); } if (++i > cmd_rows - 3) { if (!askmore()) return(1); else i = 0; } printf("Server get-path: "); if (ngetpath == 0) { printf(" (none)\n"); } else { printf("\n"); i += 3; for (x = 0; x < ngetpath; x++) { if (getpath[x]) printf(" %d. %s\n", x, getpath[x]); if (++i > (cmd_rows - 3)) { /* More than a screenful... */ if (!askmore()) break; else i = 0; } } } break; #endif /* NOSERVER */ case SHSTA: /* Status of last command */ printf(" %s\n", success ? "SUCCESS" : "FAILURE"); return(0); /* Don't change it */ case SHSTK: { /* Stack for MAC debugging */ #ifdef MAC long sp; sp = -1; loadA0 ((char *)&sp); /* set destination address */ SPtoaA0(); /* move SP to destination */ printf("Stack at 0x%x\n", sp); show_queue(); /* more debugging */ break; #else shostack(); #endif /* MAC */ break; } #ifndef NOLOCAL #ifdef OS2 case SHTAB: /* SHOW TABS */ #ifdef IKSD if (inserver) { printf("Sorry, command disabled.\r\n"); return(success = 0); } #endif /* IKSD */ shotabs(); break; #endif /* OS2 */ case SHTER: /* SHOW TERMINAL */ #ifdef IKSD if (inserver) { printf("Sorry, command disabled.\r\n"); return(success = 0); } #endif /* IKSD */ shotrm(); break; #ifdef OS2 case SHVSCRN: /* SHOW Virtual Screen - for debug */ shovscrn(); break; #endif /* OS2 */ #endif /* NOLOCAL */ #ifdef OS2MOUSE case SHMOU: /* SHOW MOUSE */ #ifdef IKSD if (inserver) { printf("Sorry, command disabled.\r\n"); return(success = 0); } #endif /* IKSD */ shomou(); break; #endif /* OS2MOUSE */ #ifndef NOFRILLS case SHVER: shover(); break; #endif /* NOFRILLS */ #ifndef NOSPL case SHBUI: /* Built-in variables */ line[0] = NUL; if ((y = cmtxt("Variable name or pattern","",&s,xxstring)) < 0) return(y); ckstrncpy(line,s,LINBUFSIZ); /* if (line[0]) ckstrncat(line,"*",LINBUFSIZ); */ case SHFUN: /* or built-in functions */ #ifdef CK_TTGWSIZ #ifdef OS2 if (tt_cols[VTERM] < 0 || tt_rows[VTERM] < 0) ttgwsiz(); #else /* OS2 */ if (ttgwsiz() > 0) { /* Get current screen size */ if (tt_rows > 0 && tt_cols > 0) { cmd_rows = tt_rows; cmd_cols = tt_cols; } } #endif /* OS2 */ #endif /* CK_TTGWSIZ */ if (x == SHFUN) { /* Functions */ printf("\nThe following functions are available:\n\n"); kwdhelp(fnctab,nfuncs,"","\\F","()",3,0); printf("\n"); #ifndef NOHELP printf( "HELP FUNCTION gives the calling conventions of the given function.\n\n" ); #endif /* NOHELP */ break; } else { /* Variables */ int j, flag = 0, havearg = 0; struct stringarray * q = NULL; char ** pp; if (line[0]) { /* Have something to search for */ havearg = 1; /* Maybe a list of things */ q = cksplit(1,0,line,NULL,"_-^$*?[]{}",0,0,0); if (!q) break; pp = q->a_head; } i = 0; for (y = 0; y < nvars; y++) { if ((vartab[y].flgs & CM_INV)) continue; if (havearg) { /* If I have something to match */ char * s2; for (flag = 0, j = 1; j <= q->a_size && !flag; j++) { s2 = pp[j] ? pp[j] : ""; #ifdef COMMENT /* This is not needed because it's what the 4 arg does in ckmatch() */ len = strlen(s2); if (len > 0) { if (s2[len-1] != '$') {/* To allow anchors */ ckmakmsg(line,LINBUFSIZ,pp[j],"*",NULL,NULL); s2 = line; } } #endif /* COMMENT */ if (ckmatch(s2,vartab[y].kwd,0,4) > 0) { flag = 1; /* Matches */ break; } } if (!flag) /* Doesn't match */ continue; } s = nvlook(vartab[y].kwd); printf(" \\v(%s) = ",vartab[y].kwd); if (vartab[y].kwval == VN_NEWL) { /* \v(newline) */ while (*s) /* Show control chars symbolically */ printf("\\{%d}",*s++); printf("\n"); } else if (vartab[y].kwval == VN_IBUF || /* \v(input) */ vartab[y].kwval == VN_QUE || /* \v(query) */ #ifdef OS2 vartab[y].kwval == VN_SELCT || /* \v(select) */ #endif /* OS2 */ (vartab[y].kwval >= VN_M_AAA && /* modem ones */ vartab[y].kwval <= VN_M_ZZZ) ) { int r = 12; /* This one can wrap around */ char buf[10]; while (*s) { if (isprint(*s)) { buf[0] = *s; buf[1] = NUL; r++; } else { sprintf(buf,"\\{%d}",*s); /* SAFE */ r += (int) strlen(buf); } if (r >= cmd_cols - 1) { printf("\n"); r = 0; i++; } printf("%s",buf); s++; } printf("\n"); } else printf("%s\n",s); if (++i > (cmd_rows - 3)) { /* More than a screenful... */ if ((y >= nvars - 1) || !askmore()) break; else i = 0; } } } break; case SHVAR: /* Global variables */ x = 0; /* Variable count */ slc = 1; /* Screen line count for "more?" */ for (y = 33; y < GVARS; y++) if (g_var[y]) { if (x++ == 0) printf("Global variables:\n"); sprintf(line," \\%%%c",y); /* SAFE */ if (shomac(line,g_var[y]) < 0) break; } if (!x) printf(" No variables defined\n"); break; case SHARG: { /* Args */ char * s1, * s2; if (maclvl > -1) { printf("Macro arguments at level %d (\\v(argc) = %d):\n", maclvl, macargc[maclvl] ); for (y = 0; y < macargc[maclvl]; y++) { s1 = m_arg[maclvl][y]; if (!s1) s1 = "(NULL)"; s2 = m_xarg[maclvl][y]; if (!s2) s2 = "(NULL)"; if (y < 10) printf(" \\%%%d = %s\n",y,s1); else printf(" \\&_[%d] = %s\n",y,s2); } } else { printf("Top-level arguments (\\v(argc) = %d):\n", topargc); for (y = 0; y < topargc; y++) { s1 = g_var[y + '0']; if (!s1) s1 = "(NULL)"; s2 = toparg[y]; if (!s2) s2 = "(NULL)"; if (y < 10 && g_var[y]) printf(" \\%%%d = %s\n",y,s1); if (toparg[y]) printf(" \\&_[%d] = %s\n",y,s2); } } } break; case SHARR: /* Arrays */ return(showarray()); #endif /* NOSPL */ #ifndef NOXFER case SHPRO: /* Protocol parameters */ shoparp(); printf("\n"); break; #endif /* NOXFER */ #ifndef NOLOCAL case SHCOM: /* Communication parameters */ printf("\n"); shoparc(); #ifdef OS2 { int i; char *s = "(unknown)"; for (i = 0; i < nprty; i++) if (prtytab[i].kwval == priority) { s = prtytab[i].kwd; break; } printf(" Priority: %s\n", s ); } #endif /* OS2 */ printf("\n"); #ifdef NETCONN if (!network #ifdef IKSD && !inserver #endif /* IKSD */ ) { #endif /* NETCONN */ shomdm(); printf("\n"); #ifdef NETCONN } #endif /* NETCONN */ #ifndef NODIAL #ifdef IKSD if ( !inserver ) #endif /* IKSD */ { printf("Type SHOW DIAL to see DIAL-related items.\n"); printf("Type SHOW MODEM to see modem-related items.\n"); #ifdef CK_TAPI printf("Type SHOW TAPI to see TAPI-related items.\n"); #endif /* CK_TAPI */ printf("\n"); } #endif /* NODIAL */ break; #endif /* NOLOCAL */ case SHFIL: /* File parameters */ shofil(); /* printf("\n"); */ /* (out o' space) */ break; #ifndef NOCSETS case SHLNG: /* Languages */ shoparl(); break; #endif /* NOCSETS */ #ifndef NOSPL case SHSCR: /* Scripts */ printf(" Command quoting: %s\n", showoff(cmdgquo())); printf(" Take echo: %s\n", showoff(techo)); printf(" Take error: %s\n", showoff(takerr[cmdlvl])); printf(" Macro echo: %s\n", showoff(mecho)); printf(" Macro error: %s\n", showoff(merror[cmdlvl])); printf(" Quiet: %s\n", showoff(quiet)); printf(" Variable evaluation: %s [\\%%x and \\&x[] variables]\n", vareval ? "recursive" : "simple"); printf(" Function diagnostics: %s\n", showoff(fndiags)); printf(" Function error: %s\n", showoff(fnerror)); #ifdef CKLEARN { extern char * learnfile; extern int learning; if (learnfile) { printf(" LEARN file: %s (%s)\n", learnfile, learning ? "ON" : "OFF" ); } else printf(" LEARN file: (none)\n"); } #endif /* CKLEARN */ shoinput(); shooutput(); #ifndef NOSCRIPT printf(" Script echo: %s\n", showoff(secho)); #endif /* NOSCRIPT */ printf(" Command buffer length: %d\n", CMDBL); printf(" Atom buffer length: %d\n", ATMBL); break; #endif /* NOSPL */ #ifndef NOXMIT case SHXMI: printf("\n"); printf(" File type: %s\n", binary ? "binary" : "text"); #ifndef NOCSETS printf(" File character-set: %s\n", fcsinfo[fcharset].keyword); #ifdef OS2 if ( isunicode() ) { printf(" Terminal Character (remote): %s\n", tt_utf8 ? "utf-8" : tcsr == TX_TRANSP ? "transparent" : tcsr == TX_UNDEF ? "undefined" : txrinfo[tcsr]->keywd); printf(" Terminal Character (local): %s\n", tcsl == TX_TRANSP ? "transparent" : tcsl == TX_UNDEF ? "undefined" : txrinfo[tcsl]->keywd); } else { printf(" Terminal Character (remote): %s\n", tt_utf8 ? "utf-8" : tcsr == TX_TRANSP ? "transparent" : tcsr == TX_UNDEF ? "undefined" : txrinfo[tcsr]->keywd); printf(" Terminal Character (local): %s\n", tcsl == TX_TRANSP ? "transparent" : tcsl == TX_UNDEF ? "undefined" : txrinfo[tcsl]->keywd); } #else /* OS2 */ printf(" Terminal character-set (remote): %s\n", fcsinfo[tcsr].keyword); printf(" Terminal character-set (local): %s\n", fcsinfo[tcsl].keyword); #endif /* OS2 */ #endif /* NOCSETS */ printf(" Terminal bytesize: %d\n", (cmask == 0xff) ? 8 : 7); printf(" Terminal echo: %s\n", duplex ? "local" : "remote"); printf(" Transmit EOF: "); if (*xmitbuf == NUL) { printf("(none)\n"); } else { char *p; p = xmitbuf; while (*p) { if (*p < SP) printf("^%c",ctl(*p)); else printf("%c",*p); p++; } printf("\n"); } if (xmitf) printf(" Transmit Fill: %d\n", xmitf); else printf(" Transmit Fill: (none)\n"); printf(" Transmit Linefeed: %s\n",showoff(xmitl)); if (xmitp) printf(" Transmit Prompt: %d (%s)\n", xmitp, chartostr(xmitp) ); else printf(" Transmit Prompt: (none)\n"); printf(" Transmit Echo: %s\n", showoff(xmitx)); printf(" Transmit Locking-Shift: %s\n", showoff(xmits)); printf(" Transmit Pause: %d (millisecond%s)\n", xmitw, (xmitw == 1) ? "" : "s" ); printf(" Transmit Timeout: %d (second%s)\n", xmitt, (xmitt == 1) ? "" : "s" ); printf("\n"); break; #endif /* NOXMIT */ #ifndef NODIAL case SHMOD: /* SHOW MODEM */ #ifdef IKSD if (inserver) { printf("Sorry, command disabled.\r\n"); return(success = 0); } #endif /* IKSD */ shomodem(); /* Show SET MODEM items */ break; #endif /* NODIAL */ #ifndef MAC case SHDFLT: printf("%s\n",zgtdir()); break; #endif /* MAC */ #ifndef NOLOCAL case SHESC: #ifdef IKSD if (inserver) { printf("Sorry, command disabled.\r\n"); return(success = 0); } #endif /* IKSD */ return(shoesc(escape)); #ifndef NODIAL case SHDIA: /* SHOW DIAL */ #ifdef IKSD if (inserver) { printf("Sorry, command disabled.\r\n"); return(success = 0); } #endif /* IKSD */ shmdmlin(); printf(", speed: "); if ((zz = ttgspd()) < 0) { printf("unknown"); } else { if (zz == 8880) printf("75/1200"); else printf("%ld",zz); } if (carrier == CAR_OFF) s = "off"; else if (carrier == CAR_ON) s = "on"; else if (carrier == CAR_AUT) s = "auto"; else s = "unknown"; printf(", carrier: %s", s); if (carrier == CAR_ON) { if (cdtimo) printf(", timeout: %d sec", cdtimo); else printf(", timeout: none"); } printf("\n"); doshodial(); if (local #ifdef NETCONN && !network #endif /* NETCONN */ ) { printf("Type SHOW MODEM to see modem settings.\n"); #ifdef CK_TAPI printf("Type SHOW TAPI to see TAPI-related items\n"); #endif /* CK_TAPI */ printf("Type SHOW COMMUNICATIONS to see modem signals.\n"); } break; #endif /* NODIAL */ #endif /* NOLOCAL */ #ifndef NOXFER #ifdef CK_LABELED case SHLBL: /* Labeled file info */ sholbl(); break; #endif /* CK_LABELED */ #endif /* NOXFER */ case SHCSE: /* Character sets */ #ifdef NOCSETS printf( " Character set translation is not supported in this version of C-Kermit\n"); #else shocharset(); #ifndef NOXFER printf("\n Unknown-Char-Set: %s\n", unkcs ? "Keep" : "Discard"); #endif /* NOXFER */ #ifdef OS2 printf("\n"); #endif /* OS2 */ shotcs(tcsl,tcsr); printf("\n"); #ifdef OS2 /* PC Code Page information */ { char cpbuf[128]; int cplist[16], cps; int activecp; cps = os2getcplist(cplist, sizeof(cplist)); sprintf(cpbuf, /* SAFE */ "%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d", cps > 1 ? cplist[1] : 0, cps > 2 ? cplist[2] : 0, cps > 3 ? cplist[3] : 0, cps > 4 ? cplist[4] : 0, cps > 5 ? cplist[5] : 0, cps > 6 ? cplist[6] : 0, cps > 7 ? cplist[7] : 0, cps > 8 ? cplist[8] : 0, cps > 9 ? cplist[9] : 0, cps > 10 ? cplist[10] : 0, cps > 11 ? cplist[11] : 0, cps > 12 ? cplist[12] : 0 ); printf(" Code Pages:\n"); activecp = os2getcp(); if ( activecp ) { printf(" Active: %d\n",activecp); if (!isWin95()) printf(" Available: %s\n",cpbuf); } else printf(" Active: n/a\n"); printf("\n"); } #endif /* OS2 */ #endif /* NOCSETS */ break; case SHFEA: /* Features */ shofea(); break; #ifdef CK_SPEED case SHCTL: /* Control-Prefix table */ shoctl(); break; #endif /* CK_SPEED */ case SHEXI: { extern int exithangup; printf("\n Exit warning %s\n", xitwarn ? (xitwarn == 1 ? "on" : "always") : "off"); printf(" Exit on-disconnect: %s\n", showoff(exitonclose)); printf(" Exit hangup: %s\n", showoff(exithangup)); printf(" Current exit status: %d\n\n", xitsta); break; } case SHPRT: { #ifdef PRINTSWI extern int printtimo, printertype, noprinter; extern char * printterm, * printsep; extern int prncs; #ifdef BPRINT extern int printbidi; #endif /* BPRINT */ #endif /* PRINTSWI */ #ifdef IKSD if (inserver && #ifdef CK_LOGIN isguest #else /* CK_LOGIN */ 0 #endif /* CK_LOGIN */ ) { printf("Sorry, command disabled.\r\n"); return(success = 0); } #endif /* IKSD */ #ifdef PRINTSWI if (noprinter) { printf("Printer: (none)\n\n"); break; } #endif /* PRINTSWI */ printf("Printer: %s%s\n", printpipe ? "| " : "", printername ? printername : #ifdef OS2 "PRN" #else "(default)" #endif /* OS2 */ ); #ifdef PRINTSWI #ifdef BPRINT if (printbidi) { printf(" /BIDIRECTIONAL\n"); if (pportspeed > 0) printf(" /SPEED:%ld\n",pportspeed); printf(" /PARITY:%s\n",parnam((char)pportparity)); printf(" /FLOW:%s\n", pportflow == FLO_NONE ? "NONE" : (pportflow == FLO_RTSC ? "RTS/CTS" : "XON/XOFF") ); } else printf(" /OUTPUT-ONLY\n"); #endif /* BPRINT */ switch (printertype) { case PRT_NON: printf(" /NONE\n"); break; case PRT_FIL: printf(" /FILE\n"); break; case PRT_PIP: printf(" /PIPE\n"); break; case PRT_DOS: printf(" /DOS-DEVICE\n"); break; case PRT_WIN: printf(" /WINDOWS-QUEUE\n"); break; } printf(" /TIMEOUT:%d\n",printtimo); if (printterm) { printf(" /END-OF-JOB-STRING:"); shostrdef(printterm); printf("\n"); } else printf(" /END-OF-JOB-STRING:(none)\n"); printf(" /JOB-HEADER-FILE:%s\n",printsep ? printsep : "(none)"); printf(" /CHARACTER-SET: %s\n",txrinfo[prncs]->keywd); #endif /* PRINTSWI */ printf("\n"); break; } case SHCMD: { #ifdef DOUBLEQUOTING extern int dblquo; #endif /* DOUBLEQUOTING */ #ifdef CK_AUTODL printf(" Command autodownload: %s\n",showoff(cmdadl)); #else printf(" Command autodownload: (not available)\n"); #endif /* CK_AUTODL */ printf(" Command bytesize: %d bits\n", (cmdmsk == 0377) ? 8 : 7); printf(" Command error-display: %d\n", cmd_err); #ifdef CK_RECALL printf(" Command recall-buffer-size: %d\n",cm_recall); #else printf(" Command recall-buffer not available in this version\n"); #endif /* CK_RECALL */ #ifdef CK_RECALL printf(" Command retry: %s\n",showoff(cm_retry)); #else printf(" Command retry not available in this version\n"); #endif /* CK_RECALL */ printf(" Command interruption: %s\n", showoff(cmdint)); printf(" Command quoting: %s\n", showoff(cmdgquo())); #ifdef DOUBLEQUOTING printf(" Command doublequoting: %s\n", showoff(dblquo)); #endif /* DOUBLEQUOTING */ printf(" Command more-prompting: %s\n", showoff(xaskmore)); printf(" Command height: %d\n", cmd_rows); printf(" Command width: %d\n", cmd_cols); #ifndef IKSDONLY #ifdef OS2 printf(" Command statusline: %s\n",showoff(tt_status[VCMD])); #endif /* OS2 */ #endif /* IKSDONLY */ #ifdef LOCUS printf(" Locus: %s", autolocus ? (autolocus == 2 ? "ask" : "auto") : (locus ? "local" : "remote")); if (autolocus) printf(" (%s)", locus ? "local" : "remote"); printf("\n"); #endif /* LOCUS */ printf(" Hints: %s\n", showoff(hints)); printf(" Quiet: %s\n", showoff(quiet)); printf(" Maximum command length: %d\n", CMDBL); #ifndef NOSPL { char * s; int k; printf(" Maximum number of macros: %d\n", MAC_MAX); printf(" Macros defined: %d\n", nmac); printf(" Maximum macro depth: %d\n", MACLEVEL); printf(" Maximum TAKE depth: %d\n", MAXTAKE); s = "(not defined)"; k = mlook(mactab,"on_unknown_command",nmac); if (k > -1) if (mactab[k].mval) s = mactab[k].mval; printf(" ON_UNKNOWN_COMMAND: %s\n",s); } #endif /* NOSPL */ #ifdef UNIX #ifndef NOJC printf(" Suspend: %s\n", showoff(xsuspend)); #endif /* NOJC */ #endif /* UNIX */ printf(" Access to external commands and programs%s allowed\n", #ifndef NOPUSH !nopush ? "" : #endif /* NOPUSH */ " not"); break; } #ifndef NOSPL case SHALRM: if (ck_alarm) printf("Alarm at %s %s\n",alrm_date,alrm_time); else printf("(no alarm set)\n"); break; #endif /* NOSPL */ #ifndef NOMSEND case SHSFL: { extern struct filelist * filehead; if (!filehead) { printf("send-list is empty\n"); } else { struct filelist * flp; char * s; flp = filehead; while (flp) { s = flp->fl_alias; if (!s) s = "(none)"; printf("%s, mode: %s, alias: %s\n", flp->fl_name, gfmode(flp->fl_mode,0), s ); flp = flp->fl_next; } } } break; #endif /* NOMSEND */ #ifdef CKXXCHAR case SHDBL: shodbl(); break; #endif /* CKXXCHAR */ #ifndef NOPUSH #ifndef NOFRILLS case SHEDIT: if (!editor[0]) { s = getenv("EDITOR"); if (s) ckstrncpy(editor,s,CKMAXPATH); } printf("\n editor: %s\n", editor[0] ? editor : "(none)"); if (editor[0]) { printf(" options: %s\n", editopts[0] ? editopts : "(none)"); printf(" file: %s\n", editfile[0] ? editfile : "(none)"); } printf("\n"); break; #ifdef BROWSER case SHBROWSE: if (!browser[0]) { s = getenv("BROWSER"); if (s) ckstrncpy(browser,s,CKMAXPATH); } printf("\n browser: %s\n", browser[0] ? browser : "(none)"); if (browser[0]) { printf(" options: %s\n", browsopts[0] ? browsopts : "(none)"); printf(" url: %s\n", browsurl[0] ? browsurl : "(none)"); } printf("\n"); break; #endif /* BROWSER */ #endif /* NOFRILLS */ #endif /* NOPUSH */ #ifndef NOLOCAL #ifdef CK_TAPI case SHTAPI: /* TAPI options */ #ifdef IKSD if (inserver) { printf("Sorry, command disabled.\r\n"); return(success = 0); } #endif /* IKSD */ shotapi(0); break; case SHTAPI_L: /* TAPI Locations */ #ifdef IKSD if (inserver) { printf("Sorry, command disabled.\r\n"); return(success = 0); } #endif /* IKSD */ shotapi(1); break; case SHTAPI_M: /* TAPI Modem */ #ifdef IKSD if (inserver) { printf("Sorry, command disabled.\r\n"); return(success = 0); } #endif /* IKSD */ shotapi(2); break; case SHTAPI_C: /* TAPI Comm */ #ifdef IKSD if (inserver) { printf("Sorry, command disabled.\r\n"); return(success = 0); } #endif /* IKSD */ shotapi(3); break; #endif /* CK_TAPI */ case SHTCP: /* SHOTCP */ printf("\n"); shotcp(0); printf("\n"); break; #ifdef TNCODE case SHTEL: /* TELNET */ printf("\n"); shotel(0); printf("\n"); break; case SHTOPT: /* TELNET OPTIONS */ printf("\n"); shotopt(0); printf("\n"); break; #endif /* TNCODE */ #ifdef CK_TRIGGER case SHTRIG: { extern char * tt_trigger[], * triggerval; int i; if (!tt_trigger[0]) { printf(" Triggers: (none)\n"); } else { printf(" Triggers:\n"); for (i = 0; i < TRIGGERS; i++) { if (!tt_trigger[i]) break; printf(" \"%s\"\n",tt_trigger[i]); } printf(" Most recent trigger encountered: "); if (triggerval) printf("\"%s\"\n",triggerval); else printf("(none)\n"); } break; } #endif /* CK_TRIGGER */ #endif /* NOLOCAL */ #ifndef NOSPL case SHINP: shoinput(); break; #endif /* NOSPL */ case SHLOG: { #ifndef MAC #ifdef IKSD if (inserver && #ifdef CK_LOGIN isguest #else /* CK_LOGIN */ 0 #endif /* CK_LOGIN */ ) { printf("Sorry, command disabled.\r\n"); return(success = 0); } #endif /* IKSD */ #ifdef DEBUG printf("\n Debug log: %s", deblog ? debfil : "(none)"); { extern int debtim; if (debtim) printf(" (timestamps)"); printf("\n"); } #endif /* DEBUG */ #ifndef NOXFER printf(" Packet log: %s\n", pktlog ? pktfil : "(none)"); #endif /* NOXFER */ #ifndef NOLOCAL printf(" Session log: %s", seslog ? sesfil : "(none)"); { extern int sessft, slogts, slognul; switch (sessft) { case XYFT_T: printf(" (text)"); break; case XYFT_B: printf(" (binary)"); break; case XYFT_D: printf(" (debug)"); break; } if (slogts) printf("(timestamped)"); if (slognul) printf("(null-padded)"); printf("\n"); } #endif /* NOLOCAL */ #ifdef TLOG printf(" Transaction log: %s (%s)\n", (tralog ? (*trafil ? trafil : "(none)") : "(none)"), (tlogfmt ? ((tlogfmt == 2) ? "ftp" : "verbose") : "brief") ); #endif /* TLOG */ #ifdef CKLOGDIAL printf(" Connection log: %s\n", dialog ? diafil : "(none)"); #endif /* CKLOGDIAL */ printf("\n"); #endif /* MAC */ break; } #ifndef NOSPL case SHOUTP: /* OUTPUT */ shooutput(); break; #endif /* NOSPL */ #ifdef PATTERNS case SHOPAT: /* PATTERNS */ shopat(); break; #endif /* PATTERNS */ #ifdef STREAMING case SHOSTR: { /* STREAMING */ extern int streamrq, clearrq, cleared; extern long tfcps; debug(F101,"SHOW RELIABLE reliable","",reliable); printf("\n Reliable: %s\n",showooa(reliable)); printf(" Clearchannel: %s\n",showooa(clearrq)); printf(" Streaming: %s\n\n",showooa(streamrq)); if ((!local && (streamrq == SET_ON)) || (streamrq == SET_AUTO && reliable)) printf(" Streaming will be done if requested.\n"); else if ((streamrq == SET_OFF) || ((streamrq == SET_AUTO) && !reliable)) printf(" Streaming will not be requested and will not be done.\n"); else if ((streamrq == SET_ON) || ((streamrq == SET_AUTO) && reliable)) printf( " Streaming will be requested and will be done if the other Kermit agrees.\n"); printf(" Last transfer: %sstreaming%s, %ld cps.\n", streamed > 0 ? "" : "no ", cleared ? ", clearchannel" : "", tfcps ); printf("\n"); break; } #endif /* STREAMING */ case SHOIKS: return(sho_iks()); break; #ifdef CK_AUTHENTICATION case SHOAUTH: return(sho_auth(0)); #endif /* CK_AUTHENTICATION */ #ifndef NOFTP case SHOFTP: { #ifdef IKSD if (inserver) { printf("Sorry, command disabled.\r\n"); return(success = 0); } #endif /* IKSD */ #ifdef SYSFTP { extern char ftpapp[], ftpopts[]; printf(" ftp-client: %s\n", ftpapp[0] ? ftpapp : "(none)"); if (ftpapp[0]) printf(" ftp options: %s\n", ftpopts[0] ? ftpopts : "(none)"); } #else #ifdef NEWFTP shoftp(0); #else printf("(No FTP client included in this version of Kermit.)\n"); #endif /* NEWFTP */ #endif /* SYSFTP */ break; } #endif /* NOFTP */ #ifndef NOCMDL case SHXOPT: { #ifdef IKSDB extern int dbenabled; extern char * dbfile, * dbdir; #endif /* IKSDB */ #ifdef CKWTMP extern int ckxwtmp; extern char * wtmpfile; #endif /* CKWTMP */ #ifdef CK_LOGIN extern int ckxanon, xferlog, logintimo; extern char * xferfile; #ifdef UNIX extern int ckxpriv; #endif /* UNIX */ #ifdef CK_PERMS extern int ckxperms; #endif /* CK_PERMS */ #endif /* CK_LOGIN */ extern char * bannerfile, * helpfile; #ifdef IKSD if (inserver && #ifdef CK_LOGIN isguest #else /* CK_LOGIN */ 0 #endif /* CK_LOGIN */ ) { printf("Sorry, command disabled.\r\n"); return(success = 0); } #endif /* IKSD */ printf("\n"); if (!cmdint) printf(" --nointerrupts\n"); printf(" --bannerfile=%s\n",bannerfile ? bannerfile : "(null)"); printf(" --cdfile:%s\n",cdmsgstr ? cdmsgstr : "(null)"); printf(" --cdmessage:%d\n",srvcdmsg); printf(" --helpfile:%d\n",helpfile); if (inserver) { printf("\n"); break; } #ifdef CKSYSLOG #ifdef SYSLOGLEVEL printf(" --syslog:%d (forced)\n",ckxsyslog); #else printf(" --syslog:%d\n",ckxsyslog); #endif /* SYSLOGLEVEL */ #endif /* CKSYSLOG */ #ifdef CKWTMP printf(" --wtmplog:%d\n",ckxwtmp); printf(" --wtmpfile=%s\n",wtmpfile ? wtmpfile : "(null)"); #endif /* CKWTMP */ #ifdef IKSD #ifdef CK_LOGIN printf(" --anonymous:%d\n",ckxanon); #ifdef UNIX printf(" --privid:%d\n",ckxpriv); #endif /* UNIX */ #ifdef CK_PERMS printf(" --permission:%04o\n",ckxperms); #endif /* CK_PERMS */ printf(" --initfile:%s\n",anonfile ? anonfile : "(null)"); printf(" --userfile:%s\n",userfile ? userfile : "(null)"); printf(" --root:%s\n",anonroot ? anonroot : "(null)"); printf(" --xferlog=%d\n",xferlog); printf(" --xferfile=%s\n",xferfile ? xferfile : "(null)"); printf(" --timeout=%d\n",logintimo); #endif /* CK_LOGIN */ #ifdef IKSDB printf(" --database=%d\n",dbenabled); printf(" --dbfile=%s\n",dbfile ? dbfile : "(null)"); if (dbdir) printf(" (db directory=[%s])\n",dbdir); #endif /* IKSDB */ #ifdef IKSDCONF printf(" IKSD conf=%s\n",iksdconf); #endif /* IKSDCONF */ #endif /* IKSD */ printf("\n"); break; } #endif /* NOCMDL */ case SHCD: { extern char * myhome; s = getenv("CDPATH"); if (!s) s = "(none)"; printf("\n current directory: %s\n", zgtdir()); printf(" previous directory: %s\n", prevdir ? prevdir : "(none)"); printf(" cd home: %s\n", homepath()); printf(" cd path: %s\n", ckcdpath ? ckcdpath : s); printf(" cd message: %s\n", showoff(srvcdmsg & 2)); printf(" server cd-message: %s\n", showoff(srvcdmsg & 1)); printf(" cd message file: %s\n\n",cdmsgstr ? cdmsgstr : "(none)"); break; } #ifndef NOCSETS case SHASSOC: (VOID) showassoc(); break; #endif /* NOCSETS */ #ifdef CKLOGDIAL case SHCONNX: #ifdef NEWFTP if (ftpisconnected()) { extern char cxlogbuf[]; dologshow(W_FTP | 1); if (cxlogbuf[0]) dologshow(1); } else { #endif /* NEWFTP */ dologshow(1); #ifdef NEWFTP } #endif /* NEWFTP */ break; #endif /* CKLOGDIAL */ case SHOPTS: optlines = 0; #ifndef NOFRILLS (VOID) showdelopts(); #endif /* NOFRILLS */ #ifdef DOMYDIR (VOID) showdiropts(); #endif /* DOMYDIR */ #ifdef CKPURGE (VOID) showpurgopts(); #endif /* CKPURGE */ (VOID) showtypopts(); break; #ifndef NOLOCAL case SHOFLO: (VOID) shoflow(); break; #endif /* NOLOCAL */ #ifndef NOXFER case SHOXFER: (VOID) shoxfer(); break; #endif /* NOXFER */ #ifdef CK_RECALL case SHHISTORY: (VOID) cmhistory(); break; #endif /* CK_RECALL */ #ifndef NOSEXP #ifndef NOSPL case SHSEXP: (VOID) shosexp(); break; #endif /* NOSPL */ #endif /* NOSEXP */ #ifdef ANYSSH case SHOSSH: (VOID) shossh(); break; #endif /* ANYSSH */ #ifdef KUI case SHOGUI: (VOID) shogui(); break; #endif /* KUI */ #ifndef NOFRILLS #ifndef NORENAME case SHOREN: (VOID) shorename(); break; #endif /* NORENAME */ #endif /* NOFRILLS */ default: printf("\nNothing to show...\n"); return(-2); } return(success = 1); } #ifndef NOXFER int shoatt() { printf("Attributes: %s\n", showoff(atcapr)); if (!atcapr) return(0); printf(" Blocksize: %s\n", showoff(atblki)); printf(" Date: %s\n", showoff(atdati)); printf(" Disposition: %s\n", showoff(atdisi)); printf(" Encoding (Character Set): %s\n", showoff(atenci)); printf(" Length: %s\n", showoff(atleni)); printf(" Type (text/binary): %s\n", showoff(attypi)); printf(" System ID: %s\n", showoff(atsidi)); printf(" System Info: %s\n", showoff(atsysi)); #ifdef CK_PERMS printf(" Permissions In: %s\n", showoff(atlpri)); printf(" Permissions Out: %s\n", showoff(atlpro)); #endif /* CK_PERMS */ #ifdef STRATUS printf(" Format: %s\n", showoff(atfrmi)); printf(" Creator: %s\n", showoff(atcrei)); printf(" Account: %s\n", showoff(atacti)); #endif /* STRATUS */ return(0); } #endif /* NOXFER */ #ifndef NOSPL int /* SHOW MACROS */ shomac(s1, s2) char *s1, *s2; { int x, n, pp; pp = 0; /* Parenthesis counter */ debug(F110,"shomac s1",s1,0); debug(F110,"shomac s2",s2,0); #ifdef IKSD if ( inserver && #ifdef IKSDCONF iksdcf #else /* IKSDCONF */ 1 #endif /* IKSDCONF */ ) { if (!ckstrcmp("on_exit",s1,-1,0) || !ckstrcmp("on_logout",s1,-1,0)) return(0); } #endif /* IKSD */ if (!s1) return(0); else printf("%s = ",s1); /* Print blank line and macro name */ n = (int)strlen(s1) + 4; /* Width of current line */ if (!s2) s2 = "(not defined)"; while ((x = *s2++)) { /* Loop thru definition */ if (x == '(') pp++; /* Treat commas within parens */ if (x == ')') pp--; /* as ordinary text */ if (pp < 0) pp = 0; /* Outside parens, */ if (x == ',' && pp == 0) { /* comma becomes comma-dash-NL. */ putchar(','); putchar('-'); x = '\n'; } if (inserver && (x == '\n')) /* Send CR before LF */ putchar(CR); putchar((CHAR)x); /* Output the character */ if (x == '\n') { /* If it was a newline */ #ifdef UNIX #ifdef NOSETBUF fflush(stdout); #endif /* NOSETBUF */ #endif /* UNIX */ putchar(' '); /* Indent the next line 1 space */ while(*s2 == ' ') s2++; /* skip past leading blanks */ n = 2; /* restart the character counter */ slc++; /* and increment the line counter. */ } else if (++n > (cmd_cols - 1)) { /* If line is too wide */ putchar('-'); /* output a dash */ if (inserver) putchar(CR); /* and a carriage return */ putchar(NL); /* and a newline */ #ifdef UNIX #ifdef NOSETBUF fflush(stdout); #endif /* NOSETBUF */ #endif /* UNIX */ n = 1; /* and restart the char counter */ slc++; /* and increment the line counter */ } if (n < 3 && slc > (cmd_rows - 3)) { /* If new line and screen full */ if (!askmore()) return(-1); /* ask if they want more. */ n = 1; /* They do, start a new line */ slc = 0; /* and restart line counter */ } } if (inserver) putchar(CR); putchar(NL); /* End of definition */ if (++slc > (cmd_rows - 3)) { if (!askmore()) return(-1); slc = 0; } return(0); } #endif /* NOSPL */ #endif /* NOSHOW */ int x_ifnum = 0; /* Flag for IF NUMERIC active */ #ifndef NOSPL /* Evaluate an arithmetic expression. */ /* Code adapted from ev, by Howie Kaye of Columbia U & others. */ static int xerror = 0; int divbyzero = 0; static char *cp; static CK_OFF_T tokval; static char curtok; static CK_OFF_T expval; #define LONGBITS (8*sizeof (CK_OFF_T)) #define NUMBER 'N' #define N_EOT 'E' /* Replacement for strchr() and index(), neither of which seem to be universal. */ static char * #ifdef CK_ANSIC windex(char * s, char c) #else windex(s,c) char *s, c; #endif /* CK_ANSIC */ /* windex */ { while (*s != NUL && *s != c) s++; if (*s == c) return(s); else return(NULL); } /* g e t t o k Returns the next token. If token is a NUMBER, sets tokval appropriately. */ static char gettok() { char tbuf[80] /* ,*tp */ ; /* Buffer to accumulate number */ while (isspace(*cp)) /* Skip past leading spaces */ cp++; debug(F110,"GETTOK",cp,0); switch (*cp) { case '$': /* ??? */ case '+': /* Add */ case '-': /* Subtract or Negate */ case '@': /* Greatest Common Divisor */ case '*': /* Multiply */ case '/': /* Divide */ case '%': /* Modulus */ case '<': /* Left shift */ case '>': /* Right shift */ case '&': /* And */ case '|': /* Or */ case '#': /* Exclusive Or */ case '~': /* Not */ case '^': /* Exponent */ case '!': /* Factorial */ case '(': /* Parens for grouping */ case ')': return(*cp++); /* operator, just return it */ case '\n': case '\0': return(N_EOT); /* End of line, return that */ } #ifdef COMMENT /* This is the original code, which allows only integer numbers. */ if (isxdigit(*cp)) { /* Digit, must be a number */ int radix = 10; /* Default radix */ for (tp = tbuf; isxdigit(*cp); cp++) *tp++ = (char) (isupper(*cp) ? tolower(*cp) : *cp); *tp = '\0'; /* End number */ switch(isupper(*cp) ? tolower(*cp) : *cp) { /* Examine break char */ case 'h': case 'x': radix = 16; cp++; break; /* if radix signifier... */ case 'o': case 'q': radix = 8; cp++; break; case 't': radix = 2; cp++; break; } for (tp = tbuf, tokval = 0; *tp != '\0'; tp++) { int dig; dig = *tp - '0'; /* Convert number */ if (dig > 10) dig -= 'a'-'0'-10; if (dig >= radix) { if (cmdlvl == 0 && !x_ifnum && !xerror) printf("?Invalid digit '%c' in number\n",*tp); xerror = 1; return(NUMBER); } tokval = radix*tokval + dig; } return(NUMBER); } if (cmdlvl == 0 && !x_ifnum && !xerror) printf("Invalid character '%c' in input\n",*cp); xerror = 1; cp++; return(gettok()); #else /* This code allows non-numbers to be treated as macro names */ { int i, x; char * s, * cp1; cp1 = cp; tp = tbuf; for (i = 0; i < 80; i++) { /* Look ahead to next break character */ /* pretty much anything that is not in the switch() above. */ if (isalpha(*cp) || isdigit(*cp) || *cp == '_' || *cp == ':' || *cp == '.' || *cp == '[' || *cp == ']' || *cp == '{' || *cp == '}' ) tbuf[i] = *cp++; else break; } if (i >= 80) { printf("Too long - \"%s\"\n", cp1); xerror = 1; cp++; return(gettok()); } if (xerror) return(NUMBER); tbuf[i] = NUL; s = tbuf; if (!isdigit(tbuf[0])) { char * s2 = NULL; x = mxlook(mactab,tbuf,nmac); debug(F111,"gettok mxlook",tbuf,x); if (x < 0) { if (cmdlvl == 0 && !x_ifnum && !xerror) printf("Bad number - \"%s\"\n",tbuf); xerror = 1; cp++; return(gettok()); } s2 = mactab[x].mval; if (!s2) s2 = ""; if (*s2) s = s2; } #ifdef CKFLOAT x = isfloat(s,0); #else x = chknum(s); #endif /* CKFLOAT */ if (x > 0) { tokval = ckatofs(s); } else { if (cmdlvl == 0 && !x_ifnum && !xerror) printf("Bad number - \"%s\"\n",tbuf); xerror = 1; cp++; return(gettok()); } return(NUMBER); } #endif /* COMMENT */ } static CK_OFF_T #ifdef CK_ANSIC expon(CK_OFF_T x, CK_OFF_T y) #else expon(x,y) CK_OFF_T x,y; #endif /* CK_ANSIC */ /* expon */ { CK_OFF_T result = 1; int sign = 1; if (y < 0) return(0); if (x < 0) { x = -x; if (y & 1) sign = -1; } while (y != 0) { if (y & 1) result *= x; y >>= 1; if (y != 0) x *= x; } return(result * sign); } /* * factor ::= simple | simple ^ factor * */ static VOID factor() { CK_OFF_T oldval; simple(); if (curtok == '^') { oldval = expval; curtok = gettok(); factor(); expval = expon(oldval,expval); } } /* * termp ::= NULL | {*,/,%,&} factor termp * */ static VOID termp() { while (curtok == '*' || curtok == '/' || curtok == '%' || curtok == '&') { CK_OFF_T oldval; char op; op = curtok; curtok = gettok(); /* skip past operator */ oldval = expval; factor(); switch(op) { case '*': expval = oldval * expval; break; case '/': case '%': if (expval == 0) { if (!x_ifnum) printf("?Divide by zero\n"); xerror = 1; divbyzero = 1; expval = -1; } else expval = (op == '/') ? (oldval / expval) : (oldval % expval); break; case '&': expval = oldval & expval; break; } } } static CK_OFF_T #ifdef CK_ANSIC fact(CK_OFF_T x) #else fact(x) CK_OFF_T x; #endif /* CK_ANSIC */ /* fact */ { /* factorial */ CK_OFF_T result = 1; while (x > 1) result *= x--; return(result); } /* * term ::= factor termp * */ static VOID term() { factor(); termp(); } static CK_OFF_T #ifdef CK_ANSIC gcd(CK_OFF_T x, CK_OFF_T y) #else gcd(x,y) CK_OFF_T x,y; #endif /* CK_ANSIC */ /* gcd */ { /* Greatest Common Divisor */ int nshift = 0; if (x < 0) x = -x; if (y < 0) y = -y; /* validate arguments */ if (x == 0 || y == 0) return(x + y); /* this is bogus */ while (!((x & 1) | (y & 1))) { /* get rid of powers of 2 */ nshift++; x >>= 1; y >>= 1; } while (x != 1 && y != 1 && x != 0 && y != 0) { while (!(x & 1)) x >>= 1; /* eliminate unnecessary */ while (!(y & 1)) y >>= 1; /* powers of 2 */ if (x < y) { /* force x to be larger */ CK_OFF_T t; t = x; x = y; y = t; } x -= y; } if (x == 0 || y == 0) return((x + y) << nshift); /* gcd is non-zero one */ else return((CK_OFF_T) 1 << nshift); /* else gcd is 1 */ } /* * exprp ::= NULL | {+,-,|,...} term exprp * */ static VOID exprp() { while (windex("+-|<>#@",curtok) != NULL) { CK_OFF_T oldval; char op; op = curtok; curtok = gettok(); /* skip past operator */ oldval = expval; term(); switch(op) { case '+' : expval = oldval + expval; break; case '-' : expval = oldval - expval; break; case '|' : expval = oldval | expval; break; case '#' : expval = oldval ^ expval; break; case '@' : expval = gcd(oldval,expval); break; case '<' : expval = oldval << expval; break; case '>' : expval = oldval >> expval; break; } } } /* * expr ::= term exprp * */ static VOID expr() { term(); exprp(); } static CK_OFF_T xparse() { curtok = gettok(); expr(); #ifdef COMMENT if (curtok == '$') { curtok = gettok(); if (curtok != NUMBER) { if (cmdlvl == 0 && !x_ifnum) printf("?Illegal radix\n"); xerror = 1; return(0); } curtok = gettok(); } #endif /* COMMENT */ if (curtok != N_EOT) { if (cmdlvl == 0 && !x_ifnum && !xerror) printf("?Extra characters after expression\n"); xerror = 1; } return(expval); } char * /* Silent front end for evala() */ evalx(s) char *s; { char * p; int t; t = x_ifnum; x_ifnum = 1; p = evala(s); x_ifnum = t; return(p); } char * evala(s) char *s; { CK_OFF_T v; /* Numeric value */ if (!s) return(""); xerror = 0; /* Start out with no error */ divbyzero = 0; cp = s; /* Make the argument global */ v = xparse(); /* Parse the string */ return(xerror ? "" : ckfstoa(v)); /* Return empty string on error */ } /* * simplest ::= NUMBER | ( expr ) * */ static VOID simplest() { char * p; p = cp; if (curtok == NUMBER) expval = tokval; else if (curtok == '(') { curtok = gettok(); /* skip over paren */ expr(); if (curtok != ')') { if (cmdlvl == 0 && !x_ifnum && !xerror) printf("?Missing right parenthesis\n"); xerror = 1; } debug(F110,"GETTOK SIMPLEST ()",p,0); } else { if (cmdlvl == 0 && !x_ifnum && !xerror) printf("?Operator unexpected\n"); xerror = 1; } curtok = gettok(); } /* * simpler ::= simplest | simplest ! * */ static VOID simpler() { simplest(); if (curtok == '!') { curtok = gettok(); expval = fact(expval); } } /* * simple ::= {-,~,!} simpler | simpler * */ static VOID simple() { if (curtok == '-' || curtok == '~' || curtok == '!' || curtok == '+') { int op = curtok; curtok = gettok(); /* skip over - sign */ simpler(); /* parse the factor again */ if (op != '+') expval = (op == '-') ? -expval : ((op == '!') ? !expval : ~expval); } else simpler(); } /* D C L A R R A Y -- Declare an array */ /* Call with: char a = single character designator for the array, e.g. "a". int n = size of array. -1 means to undeclare the array. Returns: 0 or greater on success, having created the requested array with with n+1 elements, 0..n. If an array of the same name existed previously, it is destroyed. The new array has all its elements initialized to NULL pointers. When an array is successfully created, the return value is its index (0 = 'a', 1 = 'b', and so on.) -1 on failure (because 'a' out of range or malloc failure). */ int #ifdef CK_ANSIC dclarray(char a, int n) #else dclarray(a,n) char a; int n; #endif /* CK_ANSIC */ /* dclarray */ { char c, **p; int i, n2, rc; if (a > 63 && a < 91) a += 32; /* Convert letters to lowercase */ if (a < ARRAYBASE || a > 122) /* Verify name */ return(-1); if (n < 0) /* Check arg */ return(-1); if (n+1 < 0) /* MAXINT+1 wraps around */ return(-1); c = a; a -= ARRAYBASE; /* Convert name to number */ rc = a; /* Array index will be return code */ if ((p = a_ptr[a]) != NULL) { /* Delete old array of same name */ if (a_link[a] > -1) { /* Is it a link? */ if (n == 0) { /* If we're just deleting it */ a_ptr[a] = (char **) NULL; /* clear all the info. */ a_dim[a] = 0; a_link[a] = -1; return(0); } /* Not deleting */ a = a_link[a]; /* Switch to linked-to array */ } n2 = a_dim[a]; /* Real array */ for (i = 0; i <= n2; i++) { /* First delete its elements */ if (p[i]) { free(p[i]); p[i] = NULL; } } free((char *)a_ptr[a]); /* Then the element list */ if (n == 0) { /* If undeclaring this array... */ for (i = 0; i < 122 - ARRAYBASE; i++) { /* Any linked arrays? */ if (i != a && a_link[i] == a) { /* Find them */ a_ptr[i] = (char **) NULL; /* and remove them */ a_dim[i] = 0; a_link[i] = -1; } } } a_ptr[a] = (char **) NULL; /* Remove pointer to element list */ a_dim[a] = 0; /* Set dimension at zero. */ a_link[a] = -1; /* Unset link word */ } if (n < 0) /* If only undeclaring, */ return(0); /* we're done. */ p = (char **) malloc((n+1) * sizeof(char **)); /* Allocate for new array */ if (p == NULL) return(-1); /* Check */ a_ptr[a] = p; /* Save pointer to member list */ a_dim[a] = n; /* Save dimension */ for (i = 0; i <= n; i++) /* Initialize members to null */ p[i] = NULL; for (i = 0; i < (int) 'z' - ARRAYBASE; i++) { /* Any linked arrays? */ if (i != a && a_link[i] == a) { /* Find and update them */ a_ptr[i] = p; a_dim[i] = n; } } return(rc); } /* X A R R A Y -- Convert array name to array index */ int xarray(s) char * s; { char buf[8]; int x; char c; if (!s) s = ""; debug(F110,"xarray",s,0); if (!*s) return(-1); x = strlen(s); buf[0] = NUL; buf[1] = NUL; buf[2] = s[0]; buf[3] = (x > 0) ? s[1] : NUL; buf[4] = (x > 1) ? s[2] : NUL; buf[5] = (x > 2) ? s[3] : NUL; buf[6] = NUL; debug(F110,"xarray buf[3]",&buf[3],0); s = buf+2; if (*s == '&') { buf[1] = CMDQ; s--; } else if (*s != CMDQ) { buf[0] = CMDQ; buf[1] = '&'; s = buf; } debug(F110,"xarray s",s,0); c = *(s+2); if (isupper(c)) c = tolower(c); if (c == '@') c = 96; x = (int)c - ARRAYBASE; if (*(s+3) == '[') *(s+3) = NUL; if (x < 0) { return(-1); } if (x > ('z' - ARRAYBASE)) { debug(F101,"xarray x out of range","",x); return(-1); } if (*(s+3)) { debug(F110,"xarray syntax",s,0); return(-1); } return(x); } /* boundspair() -- parses blah[n:m] For use with array segment specifiers and compact substring notation. Ignores the "blah" part, gets the values of n and m, which can be numbers, variables, or arithmetic expressions; anything that resolves to a number. Call with: s - string to parse sep - array of permissible bounds separator chars lo - pointer to low-bound result (or -1) hi - pointer to hi-bound result (or -1) zz - pointer to separator char that was encountered (or NUL) Returns: -1 on failure 0 on success */ int #ifdef CK_ANSIC boundspair(char *s, char *sep, int *lo, int *hi, char *zz) #else boundspair(s,sep,lo,hi,zz) char *s, *sep, *zz; int *lo, *hi; #endif /* CK_ANSIC */ { int i, x, y, range[2], bc = 0; char c = NUL, *s2 = NULL, buf[256], *p, *q, *r, *e[2], *tmp = NULL; debug(F110,"boundspair s",s,0); debug(F110,"boundspair sep",sep,0); *lo = -1; /* Default bounds */ *hi = -1; *zz = 0; /* Default bounds separator */ range[0] = -1; /* It's OK -- get contents */ range[1] = -1; /* of subscript brackets */ if (!s) s = ""; if (!*s) return(-1); makestr(&tmp,s); /* Make a pokeable copy */ p = tmp; q = NULL; r = NULL; for (p = s; *p; p++) { /* Get the two elements */ if (*p == '[') { bc++; /* Bracket counter */ if (bc == 1 && !q) q = p+1; } else if (*p == ']') { bc--; if (bc == 0 && q) *p = NUL; } else if (bc == 1) { /* If within brackers */ s2 = ckstrchr(sep,*p); /* Check for separator */ if (s2) { debug(F000,"boundspair *s2","",*s2); if (c) { debug(F000,"boundspair","Too many separators",*s2); makestr(&tmp,NULL); return(-1); } c = *s2; /* Separator character */ *p = NUL; r = p+1; } } } if (bc == 0 && !q) { /* This allows such constructions as "show array a" */ debug(F110,"boundspair","no brackets",0); makestr(&tmp,NULL); return(0); } if (bc != 0 || !q) { debug(F110,"boundspair","unbalanced or missing brackets",0); makestr(&tmp,NULL); return(-1); } if (!q) q = ""; if (!*q) q = "-1"; if (!r) r = ""; if (!*r) r = "-1"; e[0] = q; e[1] = r; debug(F000,"boundspair c","",c); debug(F110,"boundspair q",q,0); debug(F110,"boundspair r",r,0); for (i = 0; i < 2 && e[i]; i++) { y = 255; /* Expand variables, etc. */ s = buf; zzstring(e[i],&s,&y); s = evalx(buf); /* Evaluate it arithmetically */ if (s) if (*s) ckstrncpy(buf,s,256); if (!chknum(buf)) { /* Did we get a number? */ debug(F110,"boundspair element not numeric",buf,0); makestr(&tmp,NULL); /* No, fail. */ return(-1); } range[i] = atoi(buf); } makestr(&tmp,NULL); /* Free temporary poked string */ *lo = range[0]; /* Return what we got */ *hi = range[1]; *zz = c; debug(F101,"boundspair lo","",*lo); debug(F101,"boundspair hi","",*hi); return(0); } /* A R R A Y B O U N D S -- Parse array segment notation \&a[n:m] */ /* Call with s = array reference, plus two pointers to ints. Returns -1 on error, or array index, with the two ints set as follows: \&a[] -1, -1 \&a[3] 3, -1 \&a[3:17] 3, 17 The array need not be declared -- this routine is just for parsing. */ int arraybounds(s,lo,hi) char * s; int * lo, * hi; { int i, x, y, range[2]; char zz, buf[256], * p, * q; char * tmp = NULL; *lo = -1; /* Default bounds */ *hi = -1; if (!s) s = ""; /* Defense de null args */ if (!*s) return(-1); x = xarray(s); /* Check basic structure */ debug(F111,"arraybounds xarray",s,x); if (x < 0) /* Not OK, fail. */ return(-1); y = boundspair(s,":",lo,hi,&zz); debug(F111,"arraybounds boundspair",s,y); debug(F101,"arraybounds lo","",*lo); debug(F101,"arraybounds hi","",*hi); if (y < 0) /* Get bounds */ return(-1); return(x); } /* A R R A Y N A M -- Parse an array name */ /* Call with pointer to string that starts with the array reference. String may begin with either \& or just &. On success, Returns letter ID (always lowercase) in argument c, which can also be accent grave (` = 96; '@' is converted to grave); Dimension or subscript in argument n; IMPORTANT: These arguments must be provided by the caller as addresses of ints (not chars), for example: char *s; int x, y; arraynam(s,&x,&y); On failure, returns a negative number, with args n and c set to zero. */ int arraynam(ss,c,n) char *ss; int *c; int *n; { int i, y, pp, len; char x; char *s, *p, *sx, *vnp; /* On stack to allow for recursive calls... */ char vnbuf[ARRAYREFLEN+1]; /* Entire array reference */ char ssbuf[ARRAYREFLEN+1]; /* Subscript in "plain text" */ char sxbuf[16]; /* Evaluated subscript */ *c = *n = 0; /* Initialize return values */ len = strlen(ss); for (pp = 0,i = 0; i < len; i++) { /* Check length */ if (ss[i] == '[') { pp++; } else if (ss[i] == ']') { if (--pp == 0) break; } } if (i > ARRAYREFLEN) { printf("?Array reference too long - %s\n",ss); return(-9); } ckstrncpy(vnbuf,ss,ARRAYREFLEN); vnp = vnbuf; if (vnbuf[0] == CMDQ && vnbuf[1] == '&') vnp++; if (*vnp != '&') { printf("?Not an array - %s\n",vnbuf); return(-9); } x = *(vnp + 1); /* Fold case of array name */ /* We don't use isupper & tolower here on purpose because these */ /* would produce undesired effects with accented letters. */ if (x > 63 && x < 91) x = *(vnp + 1) = (char) (x + 32); if ((x < ARRAYBASE) || (x > 122) || (*(vnp+2) != '[')) { if (msgflg) { printf("?Invalid format for array name - %s\n",vnbuf); return(-9); } else return(-2); } *c = x; /* Return the array name */ s = vnp+3; /* Get dimension */ p = ssbuf; pp = 1; /* Bracket counter */ for (i = 0; i < ARRAYREFLEN && *s != NUL; i++) { /* Copy up to ] */ if (*s == '[') pp++; if (*s == ']' && --pp == 0) break; *p++ = *s++; } if (*s != ']') { printf("?No closing bracket on array dimension - %s\n",vnbuf); return(-9); } p--; /* Trim whitespace from end */ while (*p == SP || *p == HT) p--; p++; *p = NUL; /* Terminate subscript with null */ p = ssbuf; /* Point to beginning of subscript */ while (*p == SP || *p == HT) /* Trim whitespace from beginning */ p++; sx = sxbuf; /* Where to put expanded subscript */ y = 16; { /* Even if VARIABLE-EVALUATION SIMPLE use RECURSIVE for subscripts */ /* NOTE: This is vulnerable to SIGINT and whatnot... */ int tmp = vareval; /* Save VARIABLE-EVALUATION setting */ vareval = 1; /* Force it to RECURSIVE */ zzstring(p,&sx,&y); /* Convert variables, etc. */ vareval = tmp; /* Restore VARIABLE-EVALUATION */ } sx = sxbuf; while (*sx == SP) sx++; /* debug(F110,"arraynam sx","",sx); */ if (!*sx) { /* Empty brackets... */ *n = -17; /* (Secret code :-) */ return(-2); } p = evala(sx); /* Run it thru \fneval()... */ if (p) if (*p) ckstrncpy(sxbuf,p,16); /* We know it has to be a number. */ if (!chknum(sxbuf)) { /* Make sure it's all digits */ if (msgflg) { printf("?Array dimension or subscript missing or not numeric\n"); return(-9); } else return(-2); } if ((y = atoi(sxbuf)) < 0) { if (cmflgs == 0) printf("\n"); if (msgflg) { printf("?Array dimension or subscript not positive or zero\n"); return(-9); } else return(-2); } *n = y; /* Return the subscript or dimension */ return(0); } /* chkarray returns 0 or greater if array exists, negative otherwise */ int chkarray(a,i) int a, i; { /* Check if array is declared */ int x; /* and if subscript is in range */ if (a == 64) a = 96; /* Convert atsign to grave accent */ x = a - ARRAYBASE; /* Values must be in range 95-122 */ #ifdef COMMENT if (x == 0 && maclvl < 0) /* Macro arg vector but no macro */ return(0); #endif /* COMMENT */ if (x < 0 || x > 'z' - ARRAYBASE) /* Not in range */ return(-2); if (a_ptr[x] == NULL) return(-1); /* Not declared */ if (i > a_dim[x]) return(-2); /* Declared but out of range. */ return(a_dim[x]); /* All ok, return dimension */ } #ifdef COMMENT /* This isn't used. */ char * arrayval(a,i) int a, i; { /* Return value of \&a[i] */ int x; char **p; /* (possibly NULL) */ if (a == 64) a = 96; /* Convert atsign to grave accent */ x = a - ARRAYBASE; /* Values must be in range 95-122 */ if (x < 0 || x > 27) return(NULL); /* Not in range */ if ((x > 0) && (p = a_ptr[x]) == NULL) /* Array not declared */ return(NULL); if (i > a_dim[x]) /* Subscript out of range. */ return(NULL); return(p[i]); /* All ok, return pointer to value. */ } #endif /* COMMENT */ /* pusharray() is called when an array name is included in a LOCAL statement. It moves the pointers from the global definition to the stack, and removes the global definition. Later, if the same array is declared in the local context, it occupies the global definition in the normal way. But when popclvl() is called, it replaces the global definition with the one saved here. The "secret code" is used to indicate to popclv() that it should remove the global array when popping through this level -- otherwise if a local array were declared that had no counterpart at any higher level, it would never be deleted. This allows Algol-like inheritance to work both on the way down and on the way back up. */ int pusharray(x,z) int x, z; { int y; debug(F000,"pusharray x","",x); debug(F101,"pusharray z","",z); y = chkarray(x,z); debug(F101,"pusharray y","",y); x -= ARRAYBASE; /* Convert name letter to index. */ if (x < 0 || x > 27) return(-1); if (y < 0) { aa_ptr[cmdlvl][x] = (char **) NULL; aa_dim[cmdlvl][x] = -23; /* Secret code (see popclvl()) */ } else { aa_ptr[cmdlvl][x] = a_ptr[x]; aa_dim[cmdlvl][x] = y; } a_ptr[x] = (char **) NULL; a_dim[x] = 0; return(0); } /* P A R S E V A R -- Parse a variable name or array reference. */ /* Call with: s = pointer to candidate variable name or array reference. *c = address of integer in which to return variable ID. *i = address of integer in which to return array subscript. Returns: -2: syntax error in variable name or array reference. 1: successful parse of a simple variable, with ID in c. 2: successful parse of an array reference, w/ID in c and subscript in i. */ int parsevar(s,c,i) char *s; int *c, *i; { char *p; int x,y,z; p = s; if (*s == CMDQ) s++; /* Point after backslash */ if (*s != '%' && *s != '&') { /* Make sure it's % or & */ printf("?Not a variable name - %s\n",p); return(-9); } if ((int)strlen(s) < 2) { printf("?Incomplete variable name - %s\n",p); return(-9); } if (*s == '%' && *(s+2) != '\0') { printf("?Only one character after '%%' in variable name, please\n"); return(-9); } if (*s == '&' && *(s+2) != '[') { printf("?Array subscript expected - %s\n",p); return(-9); } if (*s == '%') { /* Simple variable. */ y = *(s+1); /* Get variable ID letter/char */ if (isupper(y)) y -= ('a'-'A'); /* Convert upper to lower case */ *c = y; /* Set the return values. */ *i = -1; /* No array subscript. */ return(1); /* Return 1 = simple variable */ } if (*s == '&') { /* Array reference. */ y = arraynam(s,&x,&z); /* Go parse it. */ debug(F101,"parsevar arraynam","",y); if ((y) < 0) { if (y == -2) return(pusharray(x,z)); if (y != -9) printf("?Invalid array reference - %s\n",p); return(-9); } if (chkarray(x,z) < 0) { /* Check if declared, etc. */ printf("?Array not declared or subscript out of range\n"); return(-9); } *c = x; /* Return array letter */ *i = z; /* and subscript. */ return(2); } return(-2); /* None of the above. */ } #define VALN 32 /* Get the numeric value of a variable */ /* Call with pointer to variable name, pointer to int for return value. Returns: 0 on success with second arg containing the value. -1 on failure (bad variable syntax, variable not defined or not numeric). */ int varval(s,v) char *s; CK_OFF_T *v; { char valbuf[VALN+1]; /* s is pointer to variable name */ char name[256]; char *p; int y; if (*s != CMDQ) { /* Handle macro names too */ ckmakmsg(name,256,"\\m(",s,")",NULL); s = name; } p = valbuf; /* Expand variable into valbuf. */ y = VALN; if (zzstring(s,&p,&y) < 0) return(-1); p = valbuf; /* Make sure value is numeric */ if (!*p) { /* Be nice -- let an undefined */ valbuf[0] = '0'; /* variable be treated as 0. */ valbuf[1] = NUL; } if (chknum(p)) { /* Convert numeric string to int */ *v = ckatofs(p); /* OK */ } else { /* Not OK */ p = evala(p); /* Maybe it's an expression */ if (!chknum(p)) /* Did it evaluate? */ return(-1); /* No, failure. */ else /* Yes, */ *v = ckatofs(p); /* success */ } return(0); } /* Increment or decrement a variable */ /* Returns -1 on failure, 0 on success */ int incvar(s,x,z) char *s; CK_OFF_T x; int z; { /* Increment a numeric variable */ CK_OFF_T n; /* s is pointer to variable name */ /* x is amount to increment by */ /* z != 0 means add */ /* z = 0 means subtract */ if (varval(s,&n) < 0) /* Convert numeric string to int */ return(-1); if (z) /* Increment it by the given amount */ n += x; else /* or decrement as requested. */ n -= x; addmac(s,ckfstoa(n)); /* Replace old variable */ return(0); } /* D O D O -- Do a macro */ /* Call with x = macro table index, s = pointer to arguments. Returns 0 on failure, 1 on success. */ int dodo(x,s,flags) int x; char *s; int flags; { int y; extern int tra_asg, tra_cmd; int tra_tmp; #ifndef NOLOCAL #ifdef OS2 extern int term_io; int term_io_sav = term_io; #endif /* OS2 */ #endif /* NOLOCAL */ if (x < 0) /* It can happen! */ return(-1); tra_tmp = tra_asg; if (++maclvl >= MACLEVEL) { /* Make sure we have storage */ debug(F101,"dodo maclvl too deep","",maclvl); --maclvl; printf("Macros nested too deeply\n"); return(0); } macp[maclvl] = mactab[x].mval; /* Point to the macro body */ macx[maclvl] = mactab[x].mval; /* Remember where the beginning is */ #ifdef COMMENT makestr(&(m_line[maclvl]),s); /* Entire arg string for "\%*" */ #endif /* COMMENT */ cmdlvl++; /* Entering a new command level */ if (cmdlvl >= CMDSTKL) { /* Too many macros + TAKE files? */ debug(F101,"dodo cmdlvl too deep","",cmdlvl); cmdlvl--; printf("?TAKE files and DO commands nested too deeply\n"); return(0); } #ifdef DEBUG if (deblog) { debug(F111,"CMD +M",mactab[x].kwd,cmdlvl); debug(F010,"CMD ->",s,0); } #endif /* DEBUG */ #ifdef VMS conres(); /* So Ctrl-C, etc, will work. */ #endif /* VMS */ #ifndef NOLOCAL #ifdef OS2 term_io = 0; /* Disable terminal emulator I/O */ #endif /* OS2 */ #endif /* NOLOCAL */ ifcmd[cmdlvl] = 0; iftest[cmdlvl] = 0; count[cmdlvl] = count[cmdlvl-1]; /* Inherit COUNT from previous level */ intime[cmdlvl] = intime[cmdlvl-1]; /* Inherit previous INPUT TIMEOUT */ inpcas[cmdlvl] = inpcas[cmdlvl-1]; /* and INPUT CASE */ takerr[cmdlvl] = takerr[cmdlvl-1]; /* and TAKE ERROR */ merror[cmdlvl] = merror[cmdlvl-1]; /* and MACRO ERROR */ xquiet[cmdlvl] = quiet; xvarev[cmdlvl] = vareval; xcmdsrc = CMD_MD; cmdstk[cmdlvl].src = CMD_MD; /* Say we're in a macro */ cmdstk[cmdlvl].lvl = maclvl; /* and remember the macro level */ cmdstk[cmdlvl].ccflgs = flags & ~CF_IMAC; /* Set flags */ /* Initialize return value except in FOR, WHILE, IF, and SWITCH macros */ if (!(flags & CF_IMAC) && mrval[maclvl]) { free(mrval[maclvl]); mrval[maclvl] = NULL; } /* Clear old %0..%9 arguments */ addmac("%0",mactab[x].kwd); /* Define %0 = name of macro */ makestr(&(m_xarg[maclvl][0]),mactab[x].kwd); varnam[0] = '%'; varnam[2] = '\0'; tra_asg = 0; for (y = 1; y < 10; y++) { /* Clear args %1..%9 */ if (m_arg[maclvl][y]) { /* Don't call delmac() unless */ varnam[1] = (char) (y + '0'); /* we have to... */ delmac(varnam,0); } } tra_asg = tra_tmp; /* Assign the new args one word per arg, allowing braces to group words */ xwords(s,MAXARGLIST,NULL,0); #ifndef NOLOCAL #ifdef OS2 term_io = term_io_sav; #endif /* OS2 */ #endif /* NOLOCAL */ if (tra_cmd) printf("[%d] +M: \"%s\"\n",cmdlvl,mactab[x].kwd); return(1); } /* Insert "literal" quote around each comma-separated command to prevent */ /* its premature expansion. Only do this if object command is surrounded */ /* by braces. */ static char* flit = "\\flit("; int litcmd(src,dest,n) char **src, **dest; int n; { int bc = 0, pp = 0; char c, *s, *lp, *ss; s = *src; lp = *dest; debug(F010,"litcmd",s,0); while (*s == SP) s++; /* Strip extra leading spaces */ if (*s == '{') { /* Starts with brace */ pp = 0; /* Paren counter */ bc = 1; /* Count leading brace */ *lp++ = *s++; /* Copy it */ if (--n < 1) return(-1); /* Check space */ while (*s == SP) s++; /* Strip interior leading spaces */ ss = flit; /* Point to "\flit(" */ while ((*lp++ = *ss++)) /* Copy it */ if (--n < 1) /* and check space */ return(-1); lp--; /* Back up over null */ while (*s) { /* Go thru rest of text */ c = *s; if (c == '{') bc++; /* Count brackets */ if (c == '(') pp++; /* and parens */ if (c == ')') { /* Right parenthesis. */ pp--; /* Count it. */ if (pp < 0) { /* An unbalanced right paren... */ #ifdef COMMENT /* The problem here is that "\{" appears to be a quoted brace and therefore isn't counted; then the "}" matches an earlier opening brace, causing (e.g.) truncation of macros by getncm(). */ if (n < 5) /* Out of space in dest buffer? */ return(-1); /* If so, give up. */ *lp++ = CMDQ; /* Must be quoted to prevent */ *lp++ = '}'; /* premature termination of */ *lp++ = '4'; /* \flit(...) */ *lp++ = '1'; *lp++ = '}'; n -= 5; #else /* Here we rely on the fact the \nnn never takes more than 3 digits */ if (n < 4) /* Out of space in dest buffer? */ return(-1); /* If so, give up. */ *lp++ = CMDQ; /* Must be quoted to prevent */ *lp++ = '0'; /* premature termination of */ *lp++ = '4'; /* \flit(...) */ *lp++ = '1'; n -= 4; #endif /* COMMENT */ pp++; /* Uncount it. */ s++; continue; } } if (c == '}') { /* Closing brace. */ if (--bc == 0) { /* Final one? */ *lp++ = ')'; /* Add closing paren for "\flit()" */ if (--n < 1) return(-1); *lp++ = c; if (--n < 1) return(-1); s++; break; } } *lp++ = c; /* General case */ if (--n < 1) return(-1); s++; } *lp = NUL; } else { /* No brackets around, */ while ((*lp++ = *s++)) /* just copy. */ if (--n < 1) return(-1); lp--; } *src = s; /* Return updated source */ *dest = lp; /* and destination pointers */ if (bc) /* Fail if braces unbalanced */ return(-1); else /* Otherwise succeed. */ return(0); } #endif /* NOSPL */ /* Functions moved here from ckuusr.c to even out the module sizes... */ /* Breaks up string s -- IN PLACE! -- into a list of up to max words. Pointers to each word go into the array list[]. max is the maximum number of words (pointers). If list is NULL, then they are added to the macro table. flag = 0 means the last field is to be one word, like all the other fields, so anything after it is discarded. flag = 1 means the last field extends to the end of the string, even if there are lots of words left, so the last field contains the remainder of the string. */ VOID xwords(s,max,list,flag) char *s; int max; char *list[]; int flag; { char *p; int b, i, k, q, y, z; #ifndef NOSPL int macro; macro = (list == NULL); debug(F010,"xwords",s,0); #endif /* NOSPL */ #ifdef XWORDSDEBUG printf("XWORDS string=%s\n",s); printf("XWORDS max=%d\n",max); #endif /* XWORDSDEBUG */ p = s; /* Pointer to beginning of string */ q = 0; /* Flag for doublequote removal */ b = 0; /* Flag for outer brace removal */ k = 0; /* Flag for in-word */ y = 0; /* Brace nesting level */ z = 0; /* "Word" counter, 0 thru max */ if (list) for (i = 0; i <= max; i++) /* Initialize pointers */ list[i] = NULL; if (flag) max--; while (1) { /* Go thru word list */ if (!s || (*s == '\0')) { /* No more characters? */ if (k != 0) { /* Was I in a word? */ if (z == max) break; /* Yes, only go up to max. */ z++; /* Count this word. */ #ifdef XWORDSDEBUG printf("1 z++ = %d\n", z); #endif /* XWORDSDEBUG */ #ifndef NOSPL if (macro) { /* Doing macro args */ if (z < 10) { varnam[1] = (char) (z + '0'); /* Assign last arg */ addmac(varnam,p); } if (z <= max) { #ifdef COMMENT if (maclvl < 0) addmac(varnam,p); else #endif /* COMMENT */ makestr(&(m_xarg[maclvl][z]),p); } } else { /* Not doing macro args */ #endif /* NOSPL */ list[z] = p; /* Assign pointer. */ #ifdef XWORDSDEBUG printf("[1]LIST[%d]=\"%s\"\n",z,list[z]); #endif /* XWORDSDEBUG */ #ifndef NOSPL } #endif /* NOSPL */ break; /* And get out. */ } else break; /* Was not in a word */ } if (k == 0 && (*s == SP || *s == HT)) { /* Eat leading blanks */ s++; continue; } else if (q == 0 && *s == '{') { /* An opening brace */ if (k == 0 && y == 0) { /* If leading brace */ p = s+1; /* point past it */ b = 1; /* and flag that we did this */ } k = 1; /* Flag that we're in a word */ y++; /* Count the brace. */ } else if (q == 0 && *s == '}') { /* A closing brace. */ y--; /* Count it. */ if (y <= 0 && b != 0) { /* If it matches the leading brace */ char c; c = *(s+1); if (!c || c == SP || c == HT) { /* at EOL or followed by SP */ *s = SP; /* change it to a space */ b = 0; /* and we're not in braces any more */ } } #ifdef DOUBLEQUOTING /* Opening doublequote */ } else if (k == 0 && b == 0 && *s == '"' && dblquo) { y++; p = s+1; /* point past it */ q = 1; /* and flag that we did this */ k = 1; /* Flag that we're in a word */ /* Closing double quote */ } else if (q > 0 && k > 0 && b == 0 && *s == '"' && dblquo) { char c; c = *(s+1); if (!c || c == SP || c == HT) { /* at EOL or followed by SP */ y--; *s = SP; /* change it to a space */ q = 0; /* and we're not in quotes any more */ } #endif /* DOUBLEQUOTING */ } else if (*s != SP && *s != HT) { /* Nonspace means we're in a word */ if (k == 0) { /* If we weren't in a word before, */ p = s; /* Mark the beginning */ if (flag && z == max) { /* Want last word to be remainder? */ z++; #ifdef XWORDSDEBUG printf("1 z++ = %d\n", z); #endif /* XWORDSDEBUG */ list[z] = p; /* Yes, point to it */ #ifdef XWORDSDEBUG printf("[4]LIST[%d]=\"%s\"\n",z,list[z]); #endif /* XWORDSDEBUG */ break; /* and quit */ } k = 1; /* Set in-word flag */ } } /* If we're not inside a braced quantity, and we are in a word, and */ /* we have hit whitespace, then we have a word. */ if ((y < 1) && (k != 0) && (*s == SP || *s == HT) && !b) { if (!flag || z < max) /* if we don't want to keep rest */ *s = '\0'; /* terminate the arg with null */ k = 0; /* say we're not in a word any more */ y = 0; /* start braces off clean again */ if (z == max) break; /* Only go up to max. */ z++; /* count this arg */ #ifdef XWORDSDEBUG printf("1 z++ = %d\n", z); #endif /* XWORDSDEBUG */ #ifndef NOSPL if (macro) { if (z < 10) { varnam[1] = (char) (z + '0'); /* compute its name */ addmac(varnam,p); /* add it to the macro table */ } if (z <= max) { #ifdef COMMENT if (maclvl < 0) addmac(varnam,p); else #endif /* COMMENT */ makestr(&(m_xarg[maclvl][z]),p); } } else { #endif /* NOSPL */ list[z] = p; #ifdef XWORDSDEBUG printf("[2]LIST[%d]=\"%s\"\n",z,list[z]); #endif /* XWORDSDEBUG */ #ifndef NOSPL } #endif /* NOSPL */ p = s+1; } s++; /* Point past this character */ } if ((z == 0) && (y > 1)) { /* Extra closing brace(s) at end */ z++; #ifndef NOSPL if (macro) { if (z < 10) { varnam[1] = z + '0'; /* compute its name */ addmac(varnam,p); /* Add rest of line to last arg */ } if (z <= max) { #ifdef COMMENT if (maclvl < 0) addmac(varnam,p); else #endif /* COMMENT */ makestr(&(m_xarg[maclvl][z]),p); } } else { #endif /* NOSPL */ list[z] = p; #ifdef XWORDSDEBUG printf("[3]LIST[%d]=\"%s\"\n",z,list[z]); #endif /* XWORDSDEBUG */ #ifndef NOSPL } #endif /* NOSPL */ } #ifndef NOSPL if (macro) { /* Macro */ if (maclvl < 0) { a_dim[0] = z; /* Array dimension is one less */ topargc = z + 1; /* than \v(argc) */ debug(F111,"a_dim[0]","D",a_dim[0]); } else { macargc[maclvl] = z + 1; /* Set \v(argc) variable */ n_xarg[maclvl] = z + 1; /* This is the actual number */ a_ptr[0] = m_xarg[maclvl]; /* Point \&_[] at the args */ a_dim[0] = z; /* And give it this dimension */ debug(F111,"a_dim[0]","E",a_dim[0]); } } #endif /* NOSPL */ return; } #ifndef NOSPL /* D O S H I F T -- Do the SHIFT Command; shift macro args left by n */ /* Note: at some point let's consolidate m_arg[][] and m_xarg[][]. */ int doshift(n) int n; { /* n = shift count */ int i, top, level; char /* *s, *m, */ buf[6]; /* Buffer to build scalar names */ char * sx = tmpbuf; int nx = TMPBUFSIZ; debug(F101,"SHIFT count","",n); debug(F101,"SHIFT topargc","",topargc); if (n < 1) /* Stay in range */ return(n == 0 ? 1 : 0); level = maclvl; top = (level < 0) ? topargc : macargc[level]; if (n >= top) n = top - 1; #ifdef DEBUG if (deblog) { debug(F101,"SHIFT count 2","",n); debug(F101,"SHIFT level","",level); if (level > -1) debug(F101,"SHIFT macargc[level]","",macargc[level]); } #endif /* DEBUG */ buf[0] = '\\'; /* Initialize name template */ buf[1] = '%'; buf[2] = NUL; buf[3] = NUL; for (i = 1; i <= n; i++) { /* Free shifted-over args */ if (level < 0) { makestr(&(toparg[i]),NULL); } else { makestr(&(m_xarg[level][i]),NULL); } if (i < 10) { /* Is this necessary? */ buf[2] = (char)(i+'0'); delmac(buf,0); } } for (i = 1; i <= top-n; i++) { /* Shift remaining args */ if (level < 0) { #ifdef COMMENT toparg[i] = toparg[i+n]; /* Full vector */ #else makestr(&(toparg[i]),toparg[i+n]); /* Full vector */ #endif /* COMMENT */ if (i < 10) /* Scalars... */ makestr(&(g_var[i+'0']),toparg[i+n]); } else { #ifdef COMMENT m_xarg[level][i] = m_xarg[level][i+n]; #else makestr(&(m_xarg[level][i]),m_xarg[level][i+n]); #endif /* COMMENT */ if (i < 10) { buf[2] = (char)(i+'0'); debug(F010,"SHIFT buf",buf,0); addmac(buf,m_xarg[level][i+n]); } } } for (i = top-n; i <= top; i++) { /* Clear n args from the end */ if (level < 0) { #ifdef COMMENT toparg[i] = NULL; #else makestr(&(toparg[i]),NULL); #endif /* COMMENt */ if (i < 10) makestr(&(g_var[i+'0']),NULL); } else { #ifdef COMMENT m_xarg[level][i] = NULL; #else makestr(&(m_xarg[level][i]),NULL); #endif /* COMMENt */ if (i < 10) { buf[2] = (char)(i+'0'); delmac(buf,0); } } } if (level > -1) { /* Macro args */ macargc[level] -= n; /* Adjust count */ n_xarg[maclvl] = macargc[level]; /* Here too */ a_dim[0] = macargc[level] - 1; /* Adjust array dimension */ debug(F111,"a_dim[0]","F",a_dim[0]); zzstring("\\fjoin(&_[],{ },1)",&sx,&nx); /* Handle \%* */ #ifdef COMMENT makestr(&(m_line[level]),tmpbuf); #endif /* COMMENT */ } else { /* Ditto for top level */ topargc -= n; a_dim[0] = topargc - 1; debug(F111,"a_dim[0]","G",a_dim[0]); zzstring("\\fjoin(&_[],{ },1)",&sx,&nx); #ifdef COMMENT makestr(&topline,tmpbuf); #endif /* COMMENT */ } return(1); } #endif /* NOSPL */ int docd(cx) int cx; { /* Do the CD command */ int x; extern int server, srvcdmsg, cdactive; extern char * cdmsgfile[], * ckcdpath; char *s, *p; #ifdef MAC char temp[34]; #endif /* MAC */ #ifdef IKSDCONF extern int iksdcf; #endif /* IKSDCONF */ #ifndef NOFRILLS if (cx == XXBACK) { if ((x = cmcfm()) < 0) cwdf = 1; if (prevdir) { s = zgtdir(); if (!zchdir(prevdir)) { cwdf = 0; perror(s); } else { makestr(&prevdir,s); } } return(cwdf); } #endif /* NOFRILLS */ if (cx == XXCDUP) { #ifdef VMS s = "[-]"; #else #ifdef datageneral s = "^"; #else s = ".."; #endif /* datageneral */ #endif /* VMS */ ckstrncpy(line,s,LINBUFSIZ); goto gocd; } #ifndef NOSPL if (cx == XXKCD) { /* Symbolic (Kermit) CD */ char * p; int n, k; x = cmkey(kcdtab,nkcdtab,"Symbolic directory name","home",xxstring); if (x < 0) return(x); x = lookup(kcdtab,atmbuf,nkcdtab,&k); /* Get complete keyword */ if (x < 0) { printf("?Lookup error\n"); /* shouldn't happen */ return(-9); } if ((x = cmcfm()) < 0) return(x); if (k == VN_HOME) { /* HOME: allow SET HOME to override */ ckstrncpy(line,homepath(),LINBUFSIZ); } else { /* Other symbolic name */ /* Convert to variable syntax */ ckmakmsg(tmpbuf,TMPBUFSIZ,"\\v(",kcdtab[k].kwd,")",NULL); p = line; /* Expand the variable */ n = LINBUFSIZ; zzstring(tmpbuf,&p,&n); if (!line[0]) { /* Fail if variable not defined */ printf("?%s - not defined\n",tmpbuf); return(success = 0); } } s = line; /* All OK, go try to CD... */ goto gocd; } #endif /* NOSPL */ cdactive = 1; #ifdef GEMDOS if ((x = cmdir("Name of local directory, or carriage return", homepath(), &s, NULL ) ) < 0 ) return(x); #else #ifdef OS2 if ((x = cmdirp("Name of PC disk and/or directory,\n\ or press the Enter key for the default", homepath(), &s, ckcdpath ? ckcdpath : getenv("CDPATH"), xxstring ) ) < 0 ) return(x); #else #ifdef MAC x = ckstrncpy(temp,homepath(),32); if (x > 0) if (temp[x-1] != ':') { temp[x] = ':'; temp[x+1] = NUL; } if ((x = cmtxt("Name of Macintosh volume and/or folder,\n\ or press the Return key for the desktop on the boot disk", temp,&s, xxstring)) < 0 ) return(x); #else if ((x = cmdirp("Carriage return for home directory,\n\ or name of directory on this computer", #ifdef VMS "SYS$LOGIN", /* With no colon */ #else homepath(), /* In VMS this is "SYS$LOGIN:" */ #endif /* VMS */ &s, ckcdpath ? ckcdpath : getenv("CDPATH"), xxstring )) < 0) return(x); #endif /* MAC */ #endif /* OS2 */ #endif /* GEMDOS */ ckstrncpy(line,s,LINBUFSIZ); /* Make a safe copy */ s = line; #ifdef VMS if (ckmatch("*.DIR;1$",s,0,0)) if (cvtdir(s,tmpbuf,TMPBUFSIZ) > 0) s = tmpbuf; #endif /* VMS */ debug(F110,"docd",s,0); #ifndef MAC if ((x = cmcfm()) < 0) /* Get confirmation */ return(x); #endif /* MAC */ gocd: #ifdef datageneral x = strlen(line); /* homdir ends in colon, */ if (x > 1 && line[x-1] == ':') /* and "dir" doesn't like that... */ line[x-1] = NUL; #endif /* datageneral */ #ifdef MAC cwdf = 1; if (!zchdir(s)) { cwdf = 0; if (*s != ':') { /* If it failed, */ char *p; /* supply leading colon */ int len = (int)strlen(s) + 2; p = malloc(len); /* and try again... */ if (p) { strcpy(p,":"); /* safe */ strcat(p,s); /* safe */ if (zchdir(p)) cwdf = 1; free(p); p = NULL; } } } if (!cwdf) perror(s); #else p = zgtdir(); if (!zchdir(s)) { cwdf = 0; #ifdef CKROOT if (ckrooterr) printf("?Off limits: \"%s\"\n",s); else #endif /* CKROOT */ perror(s); } else cwdf = 1; #endif /* MAC */ x = 0; if (cwdf) { makestr(&prevdir,p); debug(F111,"docd","srvcdmsg",srvcdmsg); if (srvcdmsg #ifdef IKSDCONF && !(inserver && !iksdcf) #endif /* IKSDCONF */ ) { int i; for (i = 0; i < 8; i++) { debug(F111,"docd cdmsgfile[i]",cdmsgfile[i],i); if (zchki(cdmsgfile[i]) > -1) { x = 1; dotype(cdmsgfile[i],xaskmore,0,0,NULL,0,NULL,0,0,NULL,0); break; } } } } /* xdocd: */ if (!x && srvcdmsg && !server #ifdef IKSDCONF && !(inserver && !iksdcf) #endif /* IKSDCONF */ && !quiet && !xcmdsrc) printf("%s\n", zgtdir()); return(cwdf); } static int on_ctrlc = 0; VOID fixcmd() { /* Fix command parser after interruption */ #ifndef NOSPL #ifndef NOONCTRLC if (nmac) { /* Any macros defined? */ int k; /* Yes */ char * s = "on_ctrlc"; /* Name of Ctrl-C handling macro */ k = mlook(mactab,s,nmac); /* Look it up. */ if (k >= 0) { /* If found, */ if (on_ctrlc++ == 0) { /* if not already executing, */ if (dodo(k,"",0) > -1) /* set it up, */ parser(1); /* execute it, */ } delmac(s,1); /* and undefine it. */ } } on_ctrlc = 0; #endif /* NOONCTRLC */ #endif /* NOSPL */ dostop(); /* Back to top level (also calls conint()). */ bgchk(); /* Check background status */ if (*psave) { /* If old prompt saved, */ cmsetp(psave); /* restore it. */ *psave = NUL; } success = 0; /* Tell parser last command failed */ } #ifndef NOSHOW /* SHOW FEATURES */ /* Note, presently optlist[] index overflow is not checked. There is plenty of room (less than 360 entries for 1000 slots). When space starts to get tight, check for noptlist >= NOPTLIST every time noptlist is incremented. */ #define NOPTLIST 1024 static int noptlist = 0; static char * optlist[NOPTLIST+1]; static int hpos = 0; int prtopt(lines,s) int * lines; char *s; { /* Print an option */ int y, i; /* Does word wrap. */ if (!s) s = ""; i = *lines; if (!*s) { /* Empty argument */ if (hpos > 0) { /* means to end this line. */ printf("\n"); /* Not needed if already at */ if (++i > (cmd_rows - 3)) { /* beginning of new line. */ if (!askmore()) return(0); else i = 0; } } printf("\n"); /* And then make a blank line */ if (++i > (cmd_rows - 3)) { if (!askmore()) return(0); else i = 0; } hpos = 0; *lines = i; return(1); } y = (int)strlen(s) + 1; hpos += y; debug(F101,"prtopt hpos","",hpos); debug(F101,"prtopt cmd_cols","",cmd_cols); if ( #ifdef OS2 hpos > ((cmd_cols > 40) ? (cmd_cols - 1) : 79) #else /* OS2 */ hpos > ((tt_cols > 40) ? (tt_cols - 1) : 79) #endif /* OS2 */ ) { printf("\n"); if (++i > (cmd_rows - 3)) { if (!askmore()) return(0); else i = 0; } printf(" %s",s); hpos = y; } else printf(" %s",s); *lines = i; return(1); } static VOID initoptlist() { int i; if (noptlist > 0) return; for (i = 0; i < NOPTLIST; i++) optlist[i] = NULL; #ifdef MAC #ifdef MPW makestr(&(optlist[noptlist++]),"MPW"); #endif /* MPW */ #endif /* MAC */ #ifdef MAC #ifdef THINK_C makestr(&(optlist[noptlist++]),"THINK_C"); #endif /* THINK_C */ #endif /* MAC */ #ifdef __386__ makestr(&(optlist[noptlist++]),"__386__"); #endif /* __386__ */ /* Memory models... */ #ifdef __FLAT__ makestr(&(optlist[noptlist++]),"__FLAT__"); #endif /* __FLAT__ */ #ifdef __SMALL__ makestr(&(optlist[noptlist++]),"__SMALL__"); #endif /* __SMALL__ */ #ifdef __MEDIUM__ makestr(&(optlist[noptlist++]),"__MEDIUM__"); #endif /* __MEDIUM__ */ #ifdef __COMPACT__ makestr(&(optlist[noptlist++]),"__COMPACT__"); #endif /* __COMPACT__ */ #ifdef __LARGE__ makestr(&(optlist[noptlist++]),"__LARGE__"); #endif /* __LARGE__ */ #ifdef DEBUG #ifdef IFDEBUG makestr(&(optlist[noptlist++]),"IFDEBUG"); #else makestr(&(optlist[noptlist++]),"DEBUG"); #endif /* IFDEBUG */ #endif /* DEBUG */ #ifdef TLOG makestr(&(optlist[noptlist++]),"TLOG"); #endif /* TLOG */ #ifdef BIGBUFOK makestr(&(optlist[noptlist++]),"BIGBUFOK"); #endif /* BIGBUFOK */ #ifdef INPBUFSIZ sprintf(line,"INPBUFSIZ=%d",INPBUFSIZ); /* SAFE */ makestr(&(optlist[noptlist++]),line); #endif /* INPBUFSIZE */ #ifdef LINBUFSIZ sprintf(line,"LINBUFSIZ=%d",LINBUFSIZ); /* SAFE */ makestr(&(optlist[noptlist++]),line); #endif /* LINBUFSIZE */ #ifdef INBUFSIZE sprintf(line,"INBUFSIZE=%d",INBUFSIZE); /* SAFE */ makestr(&(optlist[noptlist++]),line); #endif /* INBUFSIZE */ #ifdef OBUFSIZE sprintf(line,"OBUFSIZE=%d",OBUFSIZE); /* SAFE */ makestr(&(optlist[noptlist++]),line); #endif /* OBUFSIZE */ #ifdef FD_SETSIZE sprintf(line,"FD_SETSIZE=%d",FD_SETSIZE); /* SAFE */ makestr(&(optlist[noptlist++]),line); #endif /* FD_SETSIZE */ #ifdef XFRCAN makestr(&(optlist[noptlist++]),"XFRCAN"); #endif /* XFRCAN */ #ifdef XPRINT makestr(&(optlist[noptlist++]),"XPRINT"); #endif /* XPRINT */ #ifdef PIPESEND makestr(&(optlist[noptlist++]),"PIPESEND"); #endif /* PIPESEND */ #ifdef CK_SPEED makestr(&(optlist[noptlist++]),"CK_SPEED"); #endif /* CK_SPEED */ #ifdef CK_FAST makestr(&(optlist[noptlist++]),"CK_FAST"); #endif /* CK_FAST */ #ifdef CK_APC makestr(&(optlist[noptlist++]),"CK_APC"); #endif /* CK_APC */ #ifdef CK_AUTODL makestr(&(optlist[noptlist++]),"CK_AUTODL"); #endif /* CK_AUTODL */ #ifdef CK_MKDIR makestr(&(optlist[noptlist++]),"CK_MKDIR"); #endif /* CK_MKDIR */ #ifdef NOMKDIR makestr(&(optlist[noptlist++]),"NOMKDIR"); #endif /* NOMKDIR */ #ifdef CK_LABELED makestr(&(optlist[noptlist++]),"CK_LABELED"); #endif /* CK_LABELED */ #ifdef NODIAL makestr(&(optlist[noptlist++]),"NODIAL"); #endif /* NODIAL */ #ifdef MINIDIAL makestr(&(optlist[noptlist++]),"MINIDIAL"); #endif /* MINIDIAL */ #ifdef WHATAMI makestr(&(optlist[noptlist++]),"WHATAMI"); #endif /* WHATAMI */ #ifdef DYNAMIC makestr(&(optlist[noptlist++]),"DYNAMIC"); #endif /* DYNAMIC */ #ifndef NOSPL sprintf(line,"CMDDEP=%d",CMDDEP); /* SAFE */ makestr(&(optlist[noptlist++]),line); #endif /* NOSPL */ #ifdef MAXPATHLEN sprintf(line,"MAXPATHLEN=%d",MAXPATHLEN); /* SAFE */ makestr(&(optlist[noptlist++]),line); #endif /* MAXPATHLEN */ #ifdef DEVNAMLEN sprintf(line,"DEVNAMLEN=%d",DEVNAMLEN); /* SAFE */ makestr(&(optlist[noptlist++]),line); #endif /* DEVNAMLEN */ #ifdef NO_PARAM_H makestr(&(optlist[noptlist++]),"NO_PARAM_H"); #endif /* NO_PARAM_H */ #ifdef INCL_PARAM_H makestr(&(optlist[noptlist++]),"INCL_PARAM_H"); #endif /* INCL_PARAM_H */ sprintf(line,"CKMAXPATH=%d",CKMAXPATH); /* SAFE */ makestr(&(optlist[noptlist++]),line); sprintf(line,"CKMAXOPEN=%d",CKMAXOPEN); /* SAFE */ makestr(&(optlist[noptlist++]),line); sprintf(line,"Z_MAXCHAN=%d",Z_MAXCHAN); /* SAFE */ makestr(&(optlist[noptlist++]),line); #ifdef OPEN_MAX sprintf(line,"OPEN_MAX=%d",OPEN_MAX); /* SAFE */ makestr(&(optlist[noptlist++]),line); #endif /* OPEN_MAX */ #ifdef _POSIX_OPEN_MAX sprintf(line,"_POSIX_OPEN_MAX=%d",_POSIX_OPEN_MAX); /* SAFE */ makestr(&(optlist[noptlist++]),line); #endif /* _POSIX_OPEN_MAX */ #ifdef CKCHANNELIO { extern int z_maxchan; #ifdef UNIX extern int ckmaxfiles; sprintf(line,"ckmaxfiles=%d",ckmaxfiles); /* SAFE */ makestr(&(optlist[noptlist++]),line); #endif /* UNIX */ sprintf(line,"z_maxchan=%d",z_maxchan); /* SAFE */ makestr(&(optlist[noptlist++]),line); } #endif /* CKCHANNELIO */ #ifdef FOPEN_MAX sprintf(line,"FOPEN_MAX=%d",FOPEN_MAX); /* SAFE */ makestr(&(optlist[noptlist++]),line); #endif /* FOPEN_MAX */ #ifdef MAXGETPATH sprintf(line,"MAXGETPATH=%d",MAXGETPATH); /* SAFE */ makestr(&(optlist[noptlist++]),line); #endif /* MAXGETPATH */ #ifdef CMDBL sprintf(line,"CMDBL=%d",CMDBL); /* SAFE */ makestr(&(optlist[noptlist++]),line); #endif /* CMDBL */ #ifdef VNAML sprintf(line,"VNAML=%d",VNAML); /* SAFE */ makestr(&(optlist[noptlist++]),line); #endif /* VNAML */ #ifdef ARRAYREFLEN sprintf(line,"ARRAYREFLEN=%d",ARRAYREFLEN); /* SAFE */ makestr(&(optlist[noptlist++]),line); #endif /* ARRAYREFLEN */ #ifdef UIDBUFLEN sprintf(line,"UIDBUFLEN=%d",UIDBUFLEN); /* SAFE */ makestr(&(optlist[noptlist++]),line); #endif /* UIDBUFLEN */ #ifdef FORDEPTH sprintf(line,"FORDEPTH=%d",FORDEPTH); /* SAFE */ makestr(&(optlist[noptlist++]),line); #endif /* FORDEPTH */ #ifdef MAXTAKE sprintf(line,"MAXTAKE=%d",MAXTAKE); /* SAFE */ makestr(&(optlist[noptlist++]),line); #endif /* MAXTAKE */ #ifdef MACLEVEL sprintf(line,"MACLEVEL=%d",MACLEVEL); /* SAFE */ makestr(&(optlist[noptlist++]),line); #endif /* MACLEVEL */ #ifdef MAC_MAX sprintf(line,"MAC_MAX=%d",MAC_MAX); /* SAFE */ makestr(&(optlist[noptlist++]),line); #endif /* MAC_MAX */ #ifdef _LARGEFILE_SOURCE makestr(&(optlist[noptlist++]),"_LARGEFILE_SOURCE"); #endif /* _LARGEFILE_SOURCE */ #ifdef _FILE_OFFSET_BITS sprintf(line,"_FILE_OFFSET_BITS=%d",_FILE_OFFSET_BITS); /* SAFE */ makestr(&(optlist[noptlist++]),line); #endif /* _FILE_OFFSET_BITS */ #ifdef __USE_FILE_OFFSET64 makestr(&(optlist[noptlist++]),"__USE_FILE_OFFSET64"); #endif /* __USE_FILE_OFFSET64 */ #ifdef __USE_LARGEFILE64 makestr(&(optlist[noptlist++]),"__USE_LARGEFILE64"); #endif /* __USE_LARGEFILE64 */ #ifdef COMMENT #ifdef CHAR_MAX sprintf(line,"CHAR_MAX=%llx",CHAR_MAX); /* SAFE */ makestr(&(optlist[noptlist++]),line); #endif /* CHAR_MAX */ #ifdef UCHAR_MAX sprintf(line,"UCHAR_MAX=%llx",UCHAR_MAX); /* SAFE */ makestr(&(optlist[noptlist++]),line); #endif /* UCHAR_MAX */ #ifdef SHRT_MAX sprintf(line,"SHRT_MAX=%llx",SHRT_MAX); /* SAFE */ makestr(&(optlist[noptlist++]),line); #endif /* SHRT_MAX */ #ifdef USHRT_MAX sprintf(line,"USHRT_MAX=%llx",USHRT_MAX); /* SAFE */ makestr(&(optlist[noptlist++]),line); #endif /* USHRT_MAX */ #ifdef INT_MAX sprintf(line,"INT_MAX=%llx",INT_MAX); /* SAFE */ makestr(&(optlist[noptlist++]),line); #endif /* INT_MAX */ #ifdef UINT_MAX sprintf(line,"UINT_MAX=%llx",UINT_MAX); /* SAFE */ makestr(&(optlist[noptlist++]),line); #endif /* UINT_MAX */ #ifdef MAX_LONG sprintf(line,"MAX_LONG=%llx",MAX_LONG); /* SAFE */ makestr(&(optlist[noptlist++]),line); #endif /* MAX_LONG */ #ifdef LONG_MAX sprintf(line,"LONG_MAX=%llx",LONG_MAX); /* SAFE */ makestr(&(optlist[noptlist++]),line); #endif /* LONG_MAX */ #ifdef ULONG_MAX sprintf(line,"ULONG_MAX=%llx",ULONG_MAX); /* SAFE */ makestr(&(optlist[noptlist++]),line); #endif /* ULONG_MAX */ #ifdef MAXINT sprintf(line,"MAXINT=%llx",MAXINT); /* SAFE */ makestr(&(optlist[noptlist++]),line); #endif /* MAXINT */ #ifdef MAXLONG sprintf(line,"MAXLONG=%llx",MAXLONG); /* SAFE */ makestr(&(optlist[noptlist++]),line); #endif /* MAXLONG */ #ifdef NT #ifdef LLONG_MAX sprintf(line,"LLONG_MAX=%I64x",LLONG_MAX); /* SAFE */ makestr(&(optlist[noptlist++]),line); #endif /* LLONG_MAX */ #ifdef ULLONG_MAX sprintf(line,"ULLONG_MAX=%I64x",ULLONG_MAX); /* SAFE */ makestr(&(optlist[noptlist++]),line); #endif /* ULLONG_MAX */ #ifdef MAXLONGLONG sprintf(line,"MAXLONGLONG=%I64x",MAXLONGLONG); /* SAFE */ makestr(&(optlist[noptlist++]),line); #endif /* MAXLONGLONG */ #else #ifdef LLONG_MAX sprintf(line,"LLONG_MAX=%llx",LLONG_MAX); /* SAFE */ makestr(&(optlist[noptlist++]),line); #endif /* LLONG_MAX */ #ifdef ULLONG_MAX sprintf(line,"ULLONG_MAX=%llx",ULLONG_MAX); /* SAFE */ makestr(&(optlist[noptlist++]),line); #endif /* ULLONG_MAX */ #ifdef MAXLONGLONG sprintf(line,"MAXLONGLONG=%llx",MAXLONGLONG); /* SAFE */ makestr(&(optlist[noptlist++]),line); #endif /* MAXLONGLONG */ #endif #ifdef _INTEGRAL_MAX_BITS sprintf(line,"_INTEGRAL_MAX_BITS=%d",_INTEGRAL_MAX_BITS); /* SAFE */ makestr(&(optlist[noptlist++]),line); #endif /* _INTEGRAL_MAX_BITS */ #endif /* COMMENT */ #ifdef MINPUTMAX sprintf(line,"MINPUTMAX=%d",MINPUTMAX); /* SAFE */ makestr(&(optlist[noptlist++]),line); #endif /* MINPUTMAX */ #ifdef MAXWLD sprintf(line,"MAXWLD=%d",MAXWLD); /* SAFE */ makestr(&(optlist[noptlist++]),line); #else #ifdef OS2 makestr(&(optlist[noptlist++]),"MAXWLD=unlimited"); #endif /* OS2 */ #endif /* MAXWLD */ #ifdef MSENDMAX sprintf(line,"MSENDMAX=%d",MSENDMAX); /* SAFE */ makestr(&(optlist[noptlist++]),line); #endif /* MSENDMAX */ #ifdef MAXDDIR sprintf(line,"MAXDDIR=%d",MAXDDIR); /* SAFE */ makestr(&(optlist[noptlist++]),line); #endif /* MAXDDIR */ #ifdef MAXDNUMS sprintf(line,"MAXDNUMS=%d",MAXDNUMS); /* SAFE */ makestr(&(optlist[noptlist++]),line); #endif /* MAXDNUMS */ #ifdef UNIX makestr(&(optlist[noptlist++]),"UNIX"); #endif /* UNIX */ #ifdef VMS makestr(&(optlist[noptlist++]),"VMS"); #ifdef __VMS_VER sprintf(line,"__VMS_VER=%d",__VMS_VER); /* SAFE */ makestr(&(optlist[noptlist++]),line); #endif /* __VMS_VER */ #ifdef VMSV70 makestr(&(optlist[noptlist++]),"VMSV70"); #endif /* VMSV70 */ #ifdef OLD_VMS makestr(&(optlist[noptlist++]),"OLD_VMS"); #endif /* OLD_VMS */ #ifdef vms makestr(&(optlist[noptlist++]),"vms"); #endif /* vms */ #ifdef VMSV60 makestr(&(optlist[noptlist++]),"VMSV60"); #endif /* VMSV60 */ #ifdef VMSV80 makestr(&(optlist[noptlist++]),"VMSV80"); #endif /* VMSV80 */ #ifdef VMSSHARE makestr(&(optlist[noptlist++]),"VMSSHARE"); #endif /* VMSSHARE */ #ifdef NOVMSSHARE makestr(&(optlist[noptlist++]),"NOVMSSHARE"); #endif /* NOVMSSHARE */ #endif /* VMS */ #ifdef datageneral makestr(&(optlist[noptlist++]),"datageneral"); #endif /* datageneral */ #ifdef apollo makestr(&(optlist[noptlist++]),"apollo"); #endif /* apollo */ #ifdef aegis makestr(&(optlist[noptlist++]),"aegis"); #endif /* aegis */ #ifdef A986 makestr(&(optlist[noptlist++]),"A986"); #endif /* A986 */ #ifdef AMIGA makestr(&(optlist[noptlist++]),"AMIGA"); #endif /* AMIGA */ #ifdef CONVEX9 makestr(&(optlist[noptlist++]),"CONVEX9"); #endif /* CONVEX9 */ #ifdef CONVEX10 makestr(&(optlist[noptlist++]),"CONVEX10"); #endif /* CONVEX9 */ #ifdef MAC makestr(&(optlist[noptlist++]),"MAC"); #endif /* MAC */ #ifdef AUX makestr(&(optlist[noptlist++]),"AUX"); #endif /* AUX */ #ifdef OS2 makestr(&(optlist[noptlist++]),"OS2"); #ifdef NT makestr(&(optlist[noptlist++]),"NT"); #endif /* NT */ #endif /* OS2 */ #ifdef OSK makestr(&(optlist[noptlist++]),"OS9"); #endif /* OSK */ #ifdef MSDOS makestr(&(optlist[noptlist++]),"MSDOS"); #endif /* MSDOS */ #ifdef DIRENT makestr(&(optlist[noptlist++]),"DIRENT"); #endif /* DIRENT */ #ifdef SDIRENT makestr(&(optlist[noptlist++]),"SDIRENT"); #endif /* SDIRENT */ #ifdef NDIR makestr(&(optlist[noptlist++]),"NDIR"); #endif /* NDIR */ #ifdef XNDIR makestr(&(optlist[noptlist++]),"XNDIR"); #endif /* XNDIR */ #ifdef SAVEDUID makestr(&(optlist[noptlist++]),"SAVEDUID"); #endif /* SAVEDUID */ #ifdef RENAME makestr(&(optlist[noptlist++]),"RENAME"); #endif /* RENAME */ #ifdef CK_TMPDIR makestr(&(optlist[noptlist++]),"CK_TMPDIR"); #endif /* CK_TMPDIR */ #ifdef NOCCTRAP makestr(&(optlist[noptlist++]),"NOCCTRAP"); #endif /* NOCCTRAP */ #ifdef NOCOTFMC makestr(&(optlist[noptlist++]),"NOCOTFMC"); #endif /* NOCOTFMC */ #ifdef NOFRILLS makestr(&(optlist[noptlist++]),"NOFRILLS"); #endif /* NOFRILLS */ #ifdef PARSENSE makestr(&(optlist[noptlist++]),"PARSENSE"); #endif /* PARSENSE */ #ifdef TIMEH makestr(&(optlist[noptlist++]),"TIMEH"); #endif /* TIMEH */ #ifdef NOTIMEH makestr(&(optlist[noptlist++]),"TIMEH"); #endif /* NOTIMEH */ #ifdef SYSTIMEH makestr(&(optlist[noptlist++]),"SYSTIMEH"); #endif /* SYSTIMEH */ #ifdef NOSYSTIMEH makestr(&(optlist[noptlist++]),"SYSTIMEH"); #endif /* NOSYSTIMEH */ #ifdef SYSTIMEBH makestr(&(optlist[noptlist++]),"SYSTIMEBH"); #endif /* SYSTIMEBH */ #ifdef NOSYSTIMEBH makestr(&(optlist[noptlist++]),"SYSTIMEBH"); #endif /* NOSYSTIMEBH */ #ifdef UTIMEH makestr(&(optlist[noptlist++]),"UTIMEH"); #endif /* UTIMEH */ #ifdef SYSUTIMEH makestr(&(optlist[noptlist++]),"SYSUTIMEH"); #endif /* SYSUTIMEH */ #ifdef CK_NEED_SIG makestr(&(optlist[noptlist++]),"CK_NEED_SIG"); #endif /* CK_NEED_SIG */ #ifdef CK_TTYFD makestr(&(optlist[noptlist++]),"CK_TTYFD"); #endif /* CK_TTYFD */ #ifdef NETCONN makestr(&(optlist[noptlist++]),"NETCONN"); #endif /* NETCONN */ #ifdef TCPSOCKET makestr(&(optlist[noptlist++]),"TCPSOCKET"); #ifdef NOTCPOPTS makestr(&(optlist[noptlist++]),"NOTCPOPTS"); #endif /* NOTCPOPTS */ #ifdef CK_DNS_SRV makestr(&(optlist[noptlist++]),"CK_DNS_SRV"); #endif /* CK_DNS_SRV */ #ifdef NO_DNS_SRV makestr(&(optlist[noptlist++]),"NO_DNS_SRV"); #endif /* NO_DNS_SRV */ #ifdef CKGHNLHOST makestr(&(optlist[noptlist++]),"CKGHNLHOST"); #endif /* CKGHNLHOST */ #ifdef NOLISTEN makestr(&(optlist[noptlist++]),"NOLISTEN"); #endif /* NOLISTEN */ #ifdef SOL_SOCKET makestr(&(optlist[noptlist++]),"SOL_SOCKET"); #endif /* SOL_SOCKET */ #ifdef SO_OOBINLINE makestr(&(optlist[noptlist++]),"SO_OOBINLINE"); #endif /* SO_OOBINLNE */ #ifdef SO_DONTROUTE makestr(&(optlist[noptlist++]),"SO_DONTROUTE"); #endif /* SO_DONTROUTE */ #ifdef SO_KEEPALIVE makestr(&(optlist[noptlist++]),"SO_KEEPALIVE"); #endif /* SO_KEEPALIVE */ #ifdef SO_LINGER makestr(&(optlist[noptlist++]),"SO_LINGER"); #endif /* SO_LINGER */ #ifdef TCP_NODELAY makestr(&(optlist[noptlist++]),"TCP_NODELAY"); #endif /* TCP_NODELAY */ #ifdef SO_SNDBUF makestr(&(optlist[noptlist++]),"SO_SNDBUF"); #endif /* SO_SNDBUF */ #ifdef SO_RCVBUF makestr(&(optlist[noptlist++]),"SO_RCVBUF"); #endif /* SO_RCVBUF */ #ifdef h_addr makestr(&(optlist[noptlist++]),"h_addr"); #endif /* h_addr */ #ifdef HADDRLIST makestr(&(optlist[noptlist++]),"HADDRLIST"); #endif /* HADDRLIST */ #ifdef CK_SOCKS makestr(&(optlist[noptlist++]),"CK_SOCKS"); #ifdef CK_SOCKS5 makestr(&(optlist[noptlist++]),"CK_SOCKS5"); #endif /* CK_SOCKS5 */ #ifdef CK_SOCKS_NS makestr(&(optlist[noptlist++]),"CK_SOCKS_NS"); #endif /* CK_SOCKS_NS */ #endif /* CK_SOCKS */ #ifdef RLOGCODE makestr(&(optlist[noptlist++]),"RLOGCODE"); #endif /* RLOGCODE */ #ifdef NETCMD makestr(&(optlist[noptlist++]),"NETCMD"); #endif /* NETCMD */ #ifdef NONETCMD makestr(&(optlist[noptlist++]),"NONETCMD"); #endif /* NONETCMD */ #ifdef NETPTY makestr(&(optlist[noptlist++]),"NETPTY"); #endif /* NETPTY */ #ifdef CK_ENVIRONMENT makestr(&(optlist[noptlist++]),"CK_ENVIRONMENT"); #endif /* CK_ENVIRONMENT */ #endif /* TCPSOCKET */ #ifdef TNCODE makestr(&(optlist[noptlist++]),"TNCODE"); #endif /* TNCODE */ #ifdef CK_FORWARD_X makestr(&(optlist[noptlist++]),"CK_FORWARD_X"); #endif /* CK_FORWARD_X */ #ifdef TN_COMPORT makestr(&(optlist[noptlist++]),"TN_COMPORT"); #endif /* TN_COMPORT */ #ifdef MULTINET makestr(&(optlist[noptlist++]),"MULTINET"); #endif /* MULTINET */ #ifdef DEC_TCPIP makestr(&(optlist[noptlist++]),"DEC_TCPIP"); #endif /* DEC_TCPIP */ #ifdef TCPWARE makestr(&(optlist[noptlist++]),"TCPWARE"); #endif /* TCPWARE */ #ifdef UCX50 makestr(&(optlist[noptlist++]),"UCX50"); #endif /* UCX50 */ #ifdef CMU_TCPIP makestr(&(optlist[noptlist++]),"CMU_TCPIP"); #endif /* CMU_TCPIP */ #ifdef TTLEBUF makestr(&(optlist[noptlist++]),"TTLEBUF"); #endif /* TTLEBUF */ #ifdef NETLEBUF makestr(&(optlist[noptlist++]),"NETLEBUF"); #endif /* NETLEBUF */ #ifdef IKS_OPTION makestr(&(optlist[noptlist++]),"IKS_OPTION"); #endif /* IKS_OPTION */ #ifdef IKSDB makestr(&(optlist[noptlist++]),"IKSDB"); #endif /* IKSDB */ #ifdef IKSDCONF makestr(&(optlist[noptlist++]),"IKSDCONF"); #endif /* IKSDCONF */ #ifdef CK_LOGIN makestr(&(optlist[noptlist++]),"CK_LOGIN"); #endif /* CK_LOGIN */ #ifdef CK_PAM makestr(&(optlist[noptlist++]),"CK_PAM"); #endif /* CK_PAM */ #ifdef CK_SHADOW makestr(&(optlist[noptlist++]),"CK_SHADOW"); #endif /* CK_SHADOW */ #ifdef CONGSPD makestr(&(optlist[noptlist++]),"CONGSPD"); #endif /* CONGSPD */ #ifdef SUNX25 makestr(&(optlist[noptlist++]),"SUNX25"); #endif /* SUNX25 */ #ifdef IBMX25 makestr(&(optlist[noptlist++]),"IBMX25"); #endif /* IBMX25 */ #ifdef HPX25 makestr(&(optlist[noptlist++]),"HPX25"); #endif /* HPX25 */ #ifdef DECNET makestr(&(optlist[noptlist++]),"DECNET"); #endif /* DECNET */ #ifdef SUPERLAT makestr(&(optlist[noptlist++]),"SUPERLAT"); #endif /* SUPERLAT */ #ifdef NPIPE makestr(&(optlist[noptlist++]),"NPIPE"); #endif /* NPIPE */ #ifdef CK_NETBIOS makestr(&(optlist[noptlist++]),"CK_NETBIOS"); #endif /* CK_NETBIOS */ #ifdef ATT7300 makestr(&(optlist[noptlist++]),"ATT7300"); #endif /* ATT7300 */ #ifdef ATT6300 makestr(&(optlist[noptlist++]),"ATT6300"); #endif /* ATT6300 */ #ifdef HDBUUCP makestr(&(optlist[noptlist++]),"HDBUUCP"); #endif /* HDBUUCP */ #ifdef USETTYLOCK makestr(&(optlist[noptlist++]),"USETTYLOCK"); #endif /* USETTYLOCK */ #ifdef USE_UU_LOCK makestr(&(optlist[noptlist++]),"USE_UU_LOCK"); #endif /* USE_UU_LOCK */ #ifdef HAVE_LOCKDEV makestr(&(optlist[noptlist++]),"HAVE_LOCKDEV"); #endif /* HAVE_LOCKDEV */ #ifdef HAVE_BAUDBOY makestr(&(optlist[noptlist++]),"HAVE_BAUDBOY"); #endif /* HAVE_BAUDBOY */ #ifdef HAVE_OPENPTY makestr(&(optlist[noptlist++]),"HAVE_OPENPTY"); #endif /* HAVE_OPENPTY */ #ifdef TTPTYCMD makestr(&(optlist[noptlist++]),"TTPTYCMD"); #endif /* TTPTYCMD */ #ifdef NOUUCP makestr(&(optlist[noptlist++]),"NOUUCP"); #endif /* NOUUCP */ #ifdef LONGFN makestr(&(optlist[noptlist++]),"LONGFN"); #endif /* LONGFN */ #ifdef RDCHK makestr(&(optlist[noptlist++]),"RDCHK"); #endif /* RDCHK */ #ifdef SELECT makestr(&(optlist[noptlist++]),"SELECT"); #endif /* SELECT */ #ifdef USLEEP makestr(&(optlist[noptlist++]),"USLEEP"); #endif /* USLEEP */ #ifdef NAP makestr(&(optlist[noptlist++]),"NAP"); #endif /* NAP */ #ifdef NAPHACK makestr(&(optlist[noptlist++]),"NAPHACK"); #endif /* NAPHACK */ #ifdef CK_POLL makestr(&(optlist[noptlist++]),"CK_POLL"); #endif /* CK_POLL */ #ifdef NOIEXTEN makestr(&(optlist[noptlist++]),"NOIEXTEN"); #endif /* NOIEXTEN */ #ifdef EXCELAN makestr(&(optlist[noptlist++]),"EXCELAN"); #endif /* EXCELAN */ #ifdef INTERLAN makestr(&(optlist[noptlist++]),"INTERLAN"); #endif /* INTERLAN */ #ifdef NOFILEH makestr(&(optlist[noptlist++]),"NOFILEH"); #endif /* NOFILEH */ #ifdef NOSYSIOCTLH makestr(&(optlist[noptlist++]),"NOSYSIOCTLH"); #endif /* NOSYSIOCTLH */ #ifdef DCLPOPEN makestr(&(optlist[noptlist++]),"DCLPOPEN"); #endif /* DCLPOPEN */ #ifdef NOSETBUF makestr(&(optlist[noptlist++]),"NOSETBUF"); #endif /* NOSETBUF */ #ifdef NOXFER makestr(&(optlist[noptlist++]),"NOXFER"); #endif /* NOXFER */ #ifdef NOCURSES makestr(&(optlist[noptlist++]),"NOCURSES"); #endif /* NOCURSES */ #ifdef NOSERVER makestr(&(optlist[noptlist++]),"NOSERVER"); #endif /* NOSERVER */ #ifdef NOPATTERNS makestr(&(optlist[noptlist++]),"NOPATTERNS"); #else #ifdef PATTERNS makestr(&(optlist[noptlist++]),"PATTERNS"); #endif /* PATTERNS */ #endif /* NOPATTERNS */ #ifdef NOCKEXEC makestr(&(optlist[noptlist++]),"NOCKEXEC"); #else #ifdef CKEXEC makestr(&(optlist[noptlist++]),"CKEXEC"); #endif /* CKEXEC */ #endif /* NOCKEXEC */ #ifdef NOAUTODL makestr(&(optlist[noptlist++]),"NOAUTODL"); #endif /* NOAUTODL */ #ifdef NOMSEND makestr(&(optlist[noptlist++]),"NOMSEND"); #endif /* NOMSEND */ #ifdef NOFDZERO makestr(&(optlist[noptlist++]),"NOFDZERO"); #endif /* NOFDZERO */ #ifdef NOPOPEN makestr(&(optlist[noptlist++]),"NOPOPEN"); #endif /* NOPOPEN */ #ifdef NOPARTIAL makestr(&(optlist[noptlist++]),"NOPARTIAL"); #endif /* NOPARTIAL */ #ifdef NOKVERBS makestr(&(optlist[noptlist++]),"NOKVERBS"); #endif /* NOKVERBS */ #ifdef NOSETREU makestr(&(optlist[noptlist++]),"NOSETREU"); #endif /* NOSETREU */ #ifdef LCKDIR makestr(&(optlist[noptlist++]),"LCKDIR"); #endif /* LCKDIR */ #ifdef ACUCNTRL makestr(&(optlist[noptlist++]),"ACUCNTRL"); #endif /* ACUCNTRL */ #ifdef BSD4 makestr(&(optlist[noptlist++]),"BSD4"); #endif /* BSD4 */ #ifdef BSD44 makestr(&(optlist[noptlist++]),"BSD44"); #endif /* BSD44 */ #ifdef BSD41 makestr(&(optlist[noptlist++]),"BSD41"); #endif /* BSD41 */ #ifdef BSD43 makestr(&(optlist[noptlist++]),"BSD43"); #endif /* BSD43 */ #ifdef BSD29 makestr(&(optlist[noptlist++]),"BSD29"); #endif /* BSD29 */ #ifdef BSDI makestr(&(optlist[noptlist++]),"BSDI"); #endif /* BSDI */ #ifdef __bsdi__ makestr(&(optlist[noptlist++]),"__bsdi__"); #endif /* __bsdi__ */ #ifdef __NetBSD__ makestr(&(optlist[noptlist++]),"__NetBSD__"); #endif /* __NetBSD__ */ #ifdef __OpenBSD__ makestr(&(optlist[noptlist++]),"__OpenBSD__"); #endif /* __OpenBSD__ */ #ifdef __FreeBSD__ makestr(&(optlist[noptlist++]),"__FreeBSD__"); #endif /* __FreeBSD__ */ #ifdef FREEBSD4 makestr(&(optlist[noptlist++]),"FREEBSD4"); #endif /* FREEBSD4 */ #ifdef FREEBSD8 makestr(&(optlist[noptlist++]),"FREEBSD8"); #endif /* FREEBSD8 */ #ifdef FREEBSD9 makestr(&(optlist[noptlist++]),"FREEBSD9"); #endif /* FREEBSD9 */ #ifdef __linux__ makestr(&(optlist[noptlist++]),"__linux__"); #endif /* __linux__ */ #ifdef LINUX_HI_SPD makestr(&(optlist[noptlist++]),"LINUX_HI_SPD"); #endif /* LINUX_HI_SPD */ #ifdef LYNXOS makestr(&(optlist[noptlist++]),"LYNXOS"); #endif /* LYNXOS */ #ifdef V7 makestr(&(optlist[noptlist++]),"V7"); #endif /* V7 */ #ifdef AIX370 makestr(&(optlist[noptlist++]),"AIX370"); #endif /* AIX370 */ #ifdef RTAIX makestr(&(optlist[noptlist++]),"RTAIX"); #endif /* RTAIX */ #ifdef HPUX makestr(&(optlist[noptlist++]),"HPUX"); #endif /* HPUX */ #ifdef HPUX9 makestr(&(optlist[noptlist++]),"HPUX9"); #endif /* HPUX9 */ #ifdef HPUX10 makestr(&(optlist[noptlist++]),"HPUX10"); #endif /* HPUX10 */ #ifdef HPUX1000 makestr(&(optlist[noptlist++]),"HPUX1000"); #endif /* HPUX1000 */ #ifdef HPUX1100 makestr(&(optlist[noptlist++]),"HPUX1100"); #endif /* HPUX1100 */ #ifdef HPUXPRE65 makestr(&(optlist[noptlist++]),"HPUXPRE65"); #endif /* HPUXPRE65 */ #ifdef DGUX makestr(&(optlist[noptlist++]),"DGUX"); #endif /* DGUX */ #ifdef DGUX430 makestr(&(optlist[noptlist++]),"DGUX430"); #endif /* DGUX430 */ #ifdef DGUX540 makestr(&(optlist[noptlist++]),"DGUX540"); #endif /* DGUX540 */ #ifdef DGUX543 makestr(&(optlist[noptlist++]),"DGUX543"); #endif /* DGUX543 */ #ifdef DGUX54410 makestr(&(optlist[noptlist++]),"DGUX54410"); #endif /* DGUX54410 */ #ifdef DGUX54411 makestr(&(optlist[noptlist++]),"DGUX54411"); #endif /* DGUX54411 */ #ifdef sony_news makestr(&(optlist[noptlist++]),"sony_news"); #endif /* sony_news */ #ifdef CIE makestr(&(optlist[noptlist++]),"CIE"); #endif /* CIE */ #ifdef XENIX makestr(&(optlist[noptlist++]),"XENIX"); #endif /* XENIX */ #ifdef SCO_XENIX makestr(&(optlist[noptlist++]),"SCO_XENIX"); #endif /* SCO_XENIX */ #ifdef ISIII makestr(&(optlist[noptlist++]),"ISIII"); #endif /* ISIII */ #ifdef I386IX makestr(&(optlist[noptlist++]),"I386IX"); #endif /* I386IX */ #ifdef RTU makestr(&(optlist[noptlist++]),"RTU"); #endif /* RTU */ #ifdef PROVX1 makestr(&(optlist[noptlist++]),"PROVX1"); #endif /* PROVX1 */ #ifdef PYRAMID makestr(&(optlist[noptlist++]),"PYRAMID"); #endif /* PYRAMID */ #ifdef TOWER1 makestr(&(optlist[noptlist++]),"TOWER1"); #endif /* TOWER1 */ #ifdef UTEK makestr(&(optlist[noptlist++]),"UTEK"); #endif /* UTEK */ #ifdef ZILOG makestr(&(optlist[noptlist++]),"ZILOG"); #endif /* ZILOG */ #ifdef TRS16 makestr(&(optlist[noptlist++]),"TRS16"); #endif /* TRS16 */ #ifdef MINIX makestr(&(optlist[noptlist++]),"MINIX"); #endif /* MINIX */ #ifdef MINIX2 makestr(&(optlist[noptlist++]),"MINIX2"); #endif /* MINIX2 */ #ifdef MINIX3 makestr(&(optlist[noptlist++]),"MINIX3"); #endif /* MINIX3 */ #ifdef MINIX315 makestr(&(optlist[noptlist++]),"MINIX315"); #endif /* MINIX315 */ #ifdef C70 makestr(&(optlist[noptlist++]),"C70"); #endif /* C70 */ #ifdef AIXPS2 makestr(&(optlist[noptlist++]),"AIXPS2"); #endif /* AIXPS2 */ #ifdef AIXRS makestr(&(optlist[noptlist++]),"AIXRS"); #endif /* AIXRS */ #ifdef UTSV makestr(&(optlist[noptlist++]),"UTSV"); #endif /* UTSV */ #ifdef ATTSV makestr(&(optlist[noptlist++]),"ATTSV"); #endif /* ATTSV */ #ifdef SVR3 makestr(&(optlist[noptlist++]),"SVR3"); #endif /* SVR3 */ #ifdef SVR4 makestr(&(optlist[noptlist++]),"SVR4"); #endif /* SVR4 */ #ifdef DELL_SVR4 makestr(&(optlist[noptlist++]),"DELL_SVR4"); #endif /* DELL_SVR4 */ #ifdef ICL_SVR4 makestr(&(optlist[noptlist++]),"ICL_SVR4"); #endif /* ICL_SVR4 */ #ifdef OSF makestr(&(optlist[noptlist++]),"OSF"); #endif /* OSF */ #ifdef OSF1 makestr(&(optlist[noptlist++]),"OSF1"); #endif /* OSF1 */ #ifdef __OSF makestr(&(optlist[noptlist++]),"__OSF"); #endif /* __OSF */ #ifdef __OSF__ makestr(&(optlist[noptlist++]),"__OSF__"); #endif /* __OSF__ */ #ifdef __osf__ makestr(&(optlist[noptlist++]),"__osf__"); #endif /* __osf__ */ #ifdef __OSF1 makestr(&(optlist[noptlist++]),"__OSF1"); #endif /* __OSF1 */ #ifdef __OSF1__ makestr(&(optlist[noptlist++]),"__OSF1__"); #endif /* __OSF1__ */ #ifdef PTX makestr(&(optlist[noptlist++]),"PTX"); #endif /* PTX */ #ifdef POSIX makestr(&(optlist[noptlist++]),"POSIX"); #endif /* POSIX */ #ifdef BSD44ORPOSIX makestr(&(optlist[noptlist++]),"BSD44ORPOSIX"); #endif /* BSD44ORPOSIX */ #ifdef SVORPOSIX makestr(&(optlist[noptlist++]),"SVORPOSIX"); #endif /* SVORPOSIX */ #ifdef SVR4ORPOSIX makestr(&(optlist[noptlist++]),"SVR4ORPOSIX"); #endif /* SVR4ORPOSIX */ #ifdef OS2ORVMS makestr(&(optlist[noptlist++]),"OS2ORVMS"); #endif /* OS2ORVMS */ #ifdef OS2ORUNIX makestr(&(optlist[noptlist++]),"OS2ORUNIX"); #endif /* OS2ORUNIX */ #ifdef VMSORUNIX makestr(&(optlist[noptlist++]),"VMSORUNIX"); #endif /* VMSORUNIX */ #ifdef VMS64BIT makestr(&(optlist[noptlist++]),"VMS64BIT"); /* VMS on Alpha or IA64 */ #endif /* VMS64BIT */ #ifdef VMSI64 makestr(&(optlist[noptlist++]),"VMSI64"); /* VMS on IA64 */ #endif /* VMSI64 */ #ifdef _POSIX_SOURCE makestr(&(optlist[noptlist++]),"_POSIX_SOURCE"); #endif /* _POSIX_SOURCE */ #ifdef _XOPEN_SOURCE makestr(&(optlist[noptlist++]),"_XOPEN_SOURCE"); #endif #ifdef _ALL_SOURCE makestr(&(optlist[noptlist++]),"_ALL_SOURCE"); #endif #ifdef _SVID3 makestr(&(optlist[noptlist++]),"_SVID3"); #endif /* _SVID3 */ #ifdef Plan9 makestr(&(optlist[noptlist++]),"Plan9"); #endif /* Plan9 */ #ifdef SOLARIS makestr(&(optlist[noptlist++]),"SOLARIS"); #ifdef SOLARIS24 makestr(&(optlist[noptlist++]),"SOLARIS24"); #endif /* SOLARIS24 */ #ifdef SOLARIS25 makestr(&(optlist[noptlist++]),"SOLARIS25"); #endif /* SOLARIS25 */ #ifdef SOLARIS26 makestr(&(optlist[noptlist++]),"SOLARIS26"); #endif /* SOLARIS26 */ #ifdef SOLARIS7 makestr(&(optlist[noptlist++]),"SOLARIS7"); #endif /* SOLARIS7 */ #ifdef SOLARIS8 makestr(&(optlist[noptlist++]),"SOLARIS8"); #endif /* SOLARIS8 */ #ifdef SOLARIS9 makestr(&(optlist[noptlist++]),"SOLARIS9"); #endif /* SOLARIS9 */ #ifdef SOLARIS10 makestr(&(optlist[noptlist++]),"SOLARIS10"); #endif /* SOLARIS10 */ #endif /* SOLARIS */ #ifdef SUNOS4 makestr(&(optlist[noptlist++]),"SUNOS4"); #endif /* SUNOS4 */ #ifdef SUN4S5 makestr(&(optlist[noptlist++]),"SUN4S5"); #endif /* SUN4S5 */ #ifdef IRIX makestr(&(optlist[noptlist++]),"IRIX"); #endif /* IRIX */ #ifdef ENCORE makestr(&(optlist[noptlist++]),"ENCORE"); #endif /* ENCORE */ #ifdef ultrix makestr(&(optlist[noptlist++]),"ultrix"); #endif #ifdef sxaE50 makestr(&(optlist[noptlist++]),"sxaE50"); #endif #ifdef mips makestr(&(optlist[noptlist++]),"mips"); #endif #ifdef MIPS makestr(&(optlist[noptlist++]),"MIPS"); #endif #ifdef vax makestr(&(optlist[noptlist++]),"vax"); #endif #ifdef VAX makestr(&(optlist[noptlist++]),"VAX"); #endif #ifdef alpha makestr(&(optlist[noptlist++]),"alpha"); #endif #ifdef ALPHA makestr(&(optlist[noptlist++]),"ALPHA"); #endif #ifdef __ALPHA makestr(&(optlist[noptlist++]),"__ALPHA"); #endif #ifdef __alpha makestr(&(optlist[noptlist++]),"__alpha"); #endif #ifdef __AXP makestr(&(optlist[noptlist++]),"__AXP"); #endif #ifdef AXP makestr(&(optlist[noptlist++]),"AXP"); #endif #ifdef axp makestr(&(optlist[noptlist++]),"axp"); #endif #ifdef __ALPHA__ makestr(&(optlist[noptlist++]),"__ALPHA__"); #endif #ifdef __alpha__ makestr(&(optlist[noptlist++]),"__alpha__"); #endif #ifdef sun makestr(&(optlist[noptlist++]),"sun"); #endif #ifdef sun3 makestr(&(optlist[noptlist++]),"sun3"); #endif #ifdef sun386 makestr(&(optlist[noptlist++]),"sun386"); #endif #ifdef _SUN makestr(&(optlist[noptlist++]),"_SUN"); #endif #ifdef sun4 makestr(&(optlist[noptlist++]),"sun4"); #endif #ifdef sparc makestr(&(optlist[noptlist++]),"sparc"); #endif #ifdef _CRAY makestr(&(optlist[noptlist++]),"_CRAY"); #endif /* _CRAY */ #ifdef NEXT33 makestr(&(optlist[noptlist++]),"NEXT33"); #endif #ifdef NEXT makestr(&(optlist[noptlist++]),"NEXT"); #endif #ifdef NeXT makestr(&(optlist[noptlist++]),"NeXT"); #endif #ifdef MACH makestr(&(optlist[noptlist++]),"MACH"); #endif #ifdef MACOSX makestr(&(optlist[noptlist++]),"MACOSX"); #endif #ifdef MACOSX10 makestr(&(optlist[noptlist++]),"MACOSX10"); #endif #ifdef MACOSX103 makestr(&(optlist[noptlist++]),"MACOSX10e"); #endif #ifdef COMMENT /* not used */ #ifdef MACOSX103 makestr(&(optlist[noptlist++]),"MACOSX103"); #endif #endif /* COMMENT */ #ifdef sgi makestr(&(optlist[noptlist++]),"sgi"); #endif #ifdef M_SYS5 makestr(&(optlist[noptlist++]),"M_SYS5"); #endif #ifdef __SYSTEM_FIVE makestr(&(optlist[noptlist++]),"__SYSTEM_FIVE"); #endif #ifdef sysV makestr(&(optlist[noptlist++]),"sysV"); #endif #ifdef M_XENIX /* SCO Xenix V and UNIX/386 */ makestr(&(optlist[noptlist++]),"M_XENIX"); #endif #ifdef M_UNIX /* SCO UNIX */ makestr(&(optlist[noptlist++]),"M_UNIX"); #endif #ifdef _M_UNIX /* SCO UNIX 3.2v4 = ODT 2.0 */ makestr(&(optlist[noptlist++]),"_M_UNIX"); #endif #ifdef CK_SCOV5 makestr(&(optlist[noptlist++]),"CK_SCOV5"); #endif #ifdef SCO_OSR504 makestr(&(optlist[noptlist++]),"SCO_OSR504"); #endif #ifdef M_IA64 makestr(&(optlist[noptlist++]),"M_IA64"); #endif #ifdef _M_IA64 makestr(&(optlist[noptlist++]),"_M_IA64"); #endif #ifdef ia64 makestr(&(optlist[noptlist++]),"ia64"); #endif #ifdef _ia64 makestr(&(optlist[noptlist++]),"_ia64"); #endif #ifdef _ia64_ makestr(&(optlist[noptlist++]),"_ia64_"); #endif #ifdef __ia64 makestr(&(optlist[noptlist++]),"__ia64"); #endif #ifdef M_I686 makestr(&(optlist[noptlist++]),"M_I686"); #endif #ifdef _M_I686 makestr(&(optlist[noptlist++]),"_M_I686"); #endif #ifdef i686 makestr(&(optlist[noptlist++]),"i686"); #endif #ifdef M_I586 makestr(&(optlist[noptlist++]),"M_I586"); #endif #ifdef _M_I586 makestr(&(optlist[noptlist++]),"_M_I586"); #endif #ifdef i586 makestr(&(optlist[noptlist++]),"i586"); #endif #ifdef M_I486 makestr(&(optlist[noptlist++]),"M_I486"); #endif #ifdef _M_I486 makestr(&(optlist[noptlist++]),"_M_I486"); #endif #ifdef i486 makestr(&(optlist[noptlist++]),"i486"); #endif #ifdef M_I386 makestr(&(optlist[noptlist++]),"M_I386"); #endif #ifdef _M_I386 makestr(&(optlist[noptlist++]),"_M_I386"); #endif #ifdef i386 makestr(&(optlist[noptlist++]),"i386"); #endif #ifdef __i386 makestr(&(optlist[noptlist++]),"__i386"); #endif #ifdef __x86 makestr(&(optlist[noptlist++]),"__x86"); #endif #ifdef __amd64 makestr(&(optlist[noptlist++]),"__amd64"); #endif #ifdef _ILP32 makestr(&(optlist[noptlist++]),"_ILP32"); #endif #ifdef _ILP64 makestr(&(optlist[noptlist++]),"_ILP64"); #endif #ifdef _LP32 makestr(&(optlist[noptlist++]),"_LP32"); #endif #ifdef _LP64 makestr(&(optlist[noptlist++]),"_LP64"); #endif #ifdef __LP32__ makestr(&(optlist[noptlist++]),"__LP32__"); #endif #ifdef __LP64__ makestr(&(optlist[noptlist++]),"__LP64__"); #endif #ifdef _XGP4_2 makestr(&(optlist[noptlist++]),"_XGP4_2"); #endif #ifdef __ppc__ makestr(&(optlist[noptlist++]),"__ppc__"); #endif #ifdef __ppc32__ makestr(&(optlist[noptlist++]),"__ppc32__"); #endif #ifdef __ppc64__ makestr(&(optlist[noptlist++]),"__ppc64__"); #endif #ifdef CK_64BIT makestr(&(optlist[noptlist++]),"CK_64BIT"); #endif #ifdef i286 makestr(&(optlist[noptlist++]),"i286"); #endif #ifdef M_I286 makestr(&(optlist[noptlist++]),"M_I286"); #endif #ifdef __sparc makestr(&(optlist[noptlist++]),"__sparc"); #endif #ifdef __sparcv8 makestr(&(optlist[noptlist++]),"__sparcv8"); #endif #ifdef __sparcv9 makestr(&(optlist[noptlist++]),"__sparcv9"); #endif #ifdef mc68000 makestr(&(optlist[noptlist++]),"mc68000"); #endif #ifdef mc68010 makestr(&(optlist[noptlist++]),"mc68010"); #endif #ifdef mc68020 makestr(&(optlist[noptlist++]),"mc68020"); #endif #ifdef mc68030 makestr(&(optlist[noptlist++]),"mc68030"); #endif #ifdef mc68040 makestr(&(optlist[noptlist++]),"mc68040"); #endif #ifdef M_68000 makestr(&(optlist[noptlist++]),"M_68000"); #endif #ifdef M_68010 makestr(&(optlist[noptlist++]),"M_68010"); #endif #ifdef M_68020 makestr(&(optlist[noptlist++]),"M_68020"); #endif #ifdef M_68030 makestr(&(optlist[noptlist++]),"M_68030"); #endif #ifdef M_68040 makestr(&(optlist[noptlist++]),"M_68040"); #endif #ifdef m68k makestr(&(optlist[noptlist++]),"m68k"); #endif #ifdef m88k makestr(&(optlist[noptlist++]),"m88k"); #endif #ifdef pdp11 makestr(&(optlist[noptlist++]),"pdp11"); #endif #ifdef iAPX makestr(&(optlist[noptlist++]),"iAPX"); #endif #ifdef hpux makestr(&(optlist[noptlist++]),"hpux"); #endif #ifdef __hpux makestr(&(optlist[noptlist++]),"__hpux"); #endif #ifdef __hp9000s800 makestr(&(optlist[noptlist++]),"__hp9000s800"); #endif #ifdef __hp9000s700 makestr(&(optlist[noptlist++]),"__hp9000s700"); #endif #ifdef __hp9000s500 makestr(&(optlist[noptlist++]),"__hp9000s500"); #endif #ifdef __hp9000s300 makestr(&(optlist[noptlist++]),"__hp9000s300"); #endif #ifdef __hp9000s200 makestr(&(optlist[noptlist++]),"__hp9000s200"); #endif #ifdef AIX makestr(&(optlist[noptlist++]),"AIX"); #endif #ifdef _AIXFS makestr(&(optlist[noptlist++]),"_AIXFS"); #endif #ifdef u370 makestr(&(optlist[noptlist++]),"u370"); #endif #ifdef u3b makestr(&(optlist[noptlist++]),"u3b"); #endif #ifdef u3b2 makestr(&(optlist[noptlist++]),"u3b2"); #endif #ifdef multimax makestr(&(optlist[noptlist++]),"multimax"); #endif #ifdef balance makestr(&(optlist[noptlist++]),"balance"); #endif #ifdef ibmrt makestr(&(optlist[noptlist++]),"ibmrt"); #endif #ifdef _IBMRT makestr(&(optlist[noptlist++]),"_IBMRT"); #endif #ifdef ibmrs6000 makestr(&(optlist[noptlist++]),"ibmrs6000"); #endif #ifdef _AIX makestr(&(optlist[noptlist++]),"_AIX"); #endif /* _AIX */ #ifdef _IBMR2 makestr(&(optlist[noptlist++]),"_IBMR2"); #endif #ifdef UNIXWARE makestr(&(optlist[noptlist++]),"UNIXWARE"); #endif #ifdef QNX makestr(&(optlist[noptlist++]),"QNX"); #ifdef __QNX__ makestr(&(optlist[noptlist++]),"__QNX__"); #ifdef __16BIT__ makestr(&(optlist[noptlist++]),"__16BIT__"); #endif #ifdef CK_QNX16 makestr(&(optlist[noptlist++]),"CK_QNX16"); #endif #ifdef __32BIT__ makestr(&(optlist[noptlist++]),"__32BIT__"); #endif #ifdef CK_QNX32 makestr(&(optlist[noptlist++]),"CK_QNX32"); #endif #endif /* __QNX__ */ #endif /* QNX */ #ifdef QNX6 makestr(&(optlist[noptlist++]),"QNX6"); #endif /* QNX6 */ #ifdef NEUTRINO makestr(&(optlist[noptlist++]),"NEUTRINO"); #endif /* NEUTRINO */ #ifdef __STRICT_BSD__ makestr(&(optlist[noptlist++]),"__STRICT_BSD__"); #endif #ifdef __STRICT_ANSI__ makestr(&(optlist[noptlist++]),"__STRICT_ANSI__"); #endif #ifdef _ANSI_C_SOURCE makestr(&(optlist[noptlist++]),"_ANSI_C_SOURCE"); #endif #ifdef __STDC__ makestr(&(optlist[noptlist++]),"__STDC__"); #endif #ifdef cplusplus makestr(&(optlist[noptlist++]),"cplusplus"); #endif #ifdef __DECC makestr(&(optlist[noptlist++]),"__DECC"); #ifdef __DECC_VER sprintf(line,"__DECC_VER=%d",__DECC_VER); /* SAFE */ makestr(&(optlist[noptlist++]),line); #endif /* __DECC_VER */ #endif /* __DECC */ #ifdef __CRTL_VER sprintf(line,"__CRTL_VER=%d",__CRTL_VER); /* SAFE */ makestr(&(optlist[noptlist++]),line); #endif /* __CRTL_VER */ #ifdef __GNUC__ /* gcc in ansi mode */ makestr(&(optlist[noptlist++]),"__GNUC__"); #endif #ifdef GNUC /* gcc in traditional mode */ makestr(&(optlist[noptlist++]),"GNUC"); #endif #ifdef __EGCS__ /* egcs in ansi mode */ makestr(&(optlist[noptlist++]),"__EGCS__"); #endif #ifdef __egcs__ /* egcs in ansi mode */ makestr(&(optlist[noptlist++]),"__egcs__"); #endif #ifdef __WATCOMC__ makestr(&(optlist[noptlist++]),"__WATCOMC__"); #endif #ifdef CK_ANSIC makestr(&(optlist[noptlist++]),"CK_ANSIC"); #endif #ifdef CK_ANSILIBS makestr(&(optlist[noptlist++]),"CK_ANSILIBS"); #endif #ifdef CKCONINTB4CB makestr(&(optlist[noptlist++]),"CKCONINTB4CB"); #endif /* CKCONINTB4CB */ #ifdef NOTERMCAP makestr(&(optlist[noptlist++]),"NOTERMCAP"); #endif /* NOTERMCAP */ #ifdef __GLIBC__ makestr(&(optlist[noptlist++]),"__GLIBC__"); #endif #ifdef _SC_JOB_CONTROL makestr(&(optlist[noptlist++]),"_SC_JOB_CONTROL"); #endif #ifdef _POSIX_JOB_CONTROL makestr(&(optlist[noptlist++]),"_POSIX_JOB_CONTROL"); #endif #ifdef SIG_I makestr(&(optlist[noptlist++]),"SIG_I"); #endif /* SIG_I */ #ifdef SIG_V makestr(&(optlist[noptlist++]),"SIG_V"); #endif /* SIG_V */ #ifdef CK_POSIX_SIG makestr(&(optlist[noptlist++]),"CK_POSIX_SIG"); #endif #ifdef SVR3JC makestr(&(optlist[noptlist++]),"SVR3JC"); #endif #ifdef _386BSD makestr(&(optlist[noptlist++]),"_386BSD"); #endif #ifdef _BSD makestr(&(optlist[noptlist++]),"_BSD"); #endif #ifdef USE_MEMCPY makestr(&(optlist[noptlist++]),"USE_MEMCPY"); #endif /* USE_MEMCPY */ #ifdef USE_LSTAT makestr(&(optlist[noptlist++]),"USE_LSTAT"); #endif /* USE_LSTAT */ #ifdef TERMIOX makestr(&(optlist[noptlist++]),"TERMIOX"); #endif /* TERMIOX */ #ifdef STERMIOX makestr(&(optlist[noptlist++]),"STERMIOX"); #endif /* STERMIOX */ #ifdef CK_CURSES makestr(&(optlist[noptlist++]),"CK_CURSES"); #endif /* CK_CURSES */ #ifdef CK_NEWTERM makestr(&(optlist[noptlist++]),"CK_NEWTERM"); #endif /* CK_NEWTERM */ #ifdef CK_WREFRESH makestr(&(optlist[noptlist++]),"CK_WREFRESH"); #endif /* CK_WREFRESH */ #ifdef CK_PCT_BAR makestr(&(optlist[noptlist++]),"CK_PCT_BAR"); #endif /* CK_PCT_BAR */ #ifdef CK_DTRCD makestr(&(optlist[noptlist++]),"CK_DTRCD"); #endif /* CK_DTRCD */ #ifdef CK_DTRCTS makestr(&(optlist[noptlist++]),"CK_DTRCTS"); #endif /* CK_DTRCTS */ #ifdef CK_RTSCTS makestr(&(optlist[noptlist++]),"CK_RTSCTS"); #endif /* CK_RTSCTS */ #ifdef POSIX_CRTSCTS makestr(&(optlist[noptlist++]),"POSIX_CRTSCTS"); #endif /* POSIX_CRTSCTS */ #ifdef FIXCRTSCTS makestr(&(optlist[noptlist++]),"FIXCRTSCTS"); #endif /* FIXCRTSCTS */ #ifdef HWPARITY makestr(&(optlist[noptlist++]),"HWPARITY"); #endif /* HWPARITY */ #ifdef CK_SYSINI #ifdef CK_INI_A makestr(&(optlist[noptlist++]),"CK_INI_A"); ckmakmsg(line,LINBUFSIZ,"CK_SYSINI=\"",CK_SYSINI,"\"",NULL); makestr(&(optlist[noptlist++]),line); #else #ifdef CK_INI_B makestr(&(optlist[noptlist++]),"CK_INI_B"); ckmakmsg(line,LINBUFSIZ,"CK_SYSINI=\"",CK_SYSINI,"\"",NULL); makestr(&(optlist[noptlist++]),line); #else makestr(&(optlist[noptlist++]),"CK_SYSINI"); #endif /* CK_INI_B */ #endif /* CK_INI_A */ #endif /* CK_DSYSINI */ #ifdef CK_DSYSINI makestr(&(optlist[noptlist++]),"CK_DSYSINI"); #endif /* CK_DSYSINI */ #ifdef CK_TTGWSIZ makestr(&(optlist[noptlist++]),"CK_TTGWSIZ"); #endif /* CK_TTGWSIZ */ #ifdef CK_NAWS makestr(&(optlist[noptlist++]),"CK_NAWS"); #endif /* CK_NAWS */ #ifdef MDMHUP makestr(&(optlist[noptlist++]),"MDMHUP"); #endif /* MDMHUP */ #ifdef HUP_CLOSE_POSIX makestr(&(optlist[noptlist++]),"HUP_CLOSE_POSIX"); #endif /* HUP_CLOSE_POSIX */ #ifdef NO_HUP_CLOSE_POSIX makestr(&(optlist[noptlist++]),"NO_HUP_CLOSE_POSIX"); #endif /* NO_HUP_CLOSE_POSIX */ #ifdef DCMDBUF makestr(&(optlist[noptlist++]),"DCMDBUF"); #endif /* DCMDBUF */ #ifdef CK_RECALL makestr(&(optlist[noptlist++]),"CK_RECALL"); #endif /* CK_RECALL */ #ifdef BROWSER makestr(&(optlist[noptlist++]),"BROWSER"); #endif /* BROWSER */ #ifdef CLSOPN makestr(&(optlist[noptlist++]),"CLSOPN"); #endif /* CLSOPN */ #ifdef STRATUS makestr(&(optlist[noptlist++]),"STRATUS"); #endif /* STRATUS */ #ifdef __VOS__ makestr(&(optlist[noptlist++]),"__VOS__"); #endif /* __VOS__ */ #ifdef STRATUSX25 makestr(&(optlist[noptlist++]),"STRATUSX25"); #endif /* STRATUSX25 */ #ifdef OS2MOUSE makestr(&(optlist[noptlist++]),"OS2MOUSE"); #endif /* OS2MOUSE */ #ifdef CK_REXX makestr(&(optlist[noptlist++]),"CK_REXX"); #endif /* CK_REXX */ #ifdef CK_TIMERS makestr(&(optlist[noptlist++]),"CK_TIMERS"); #endif /* CK_TIMERS */ #ifdef TTSPDLIST makestr(&(optlist[noptlist++]),"TTSPDLIST"); #endif /* TTSPDLIST */ #ifdef CK_PERMS makestr(&(optlist[noptlist++]),"CK_PERMS"); #endif /* CK_PERMS */ #ifdef CKTUNING makestr(&(optlist[noptlist++]),"CKTUNING"); #endif /* CKTUNING */ #ifdef NEWFTP makestr(&(optlist[noptlist++]),"NEWFTP"); #endif /* NEWFTP */ #ifdef SYSFTP makestr(&(optlist[noptlist++]),"SYSFTP"); #endif /* SYSFTP */ #ifdef NOFTP makestr(&(optlist[noptlist++]),"NOFTP"); #endif /* NOFTP */ #ifdef CKHTTP makestr(&(optlist[noptlist++]),"CKHTTP"); #endif /* CKHTTP */ #ifdef NOHTTP makestr(&(optlist[noptlist++]),"NOHTTP"); #endif /* NOHTTP */ #ifdef CKROOT makestr(&(optlist[noptlist++]),"CKROOT"); #endif /* CKROOT */ #ifdef CKREALPATH makestr(&(optlist[noptlist++]),"CKREALPATH"); #endif /* CKREALPATH */ #ifdef STREAMING makestr(&(optlist[noptlist++]),"STREAMING"); #endif /* STREAMING */ #ifdef UNPREFIXZERO makestr(&(optlist[noptlist++]),"UNPREFIXZERO"); #endif /* UNPREFIXZERO */ #ifdef CKREGEX makestr(&(optlist[noptlist++]),"CKREGEX"); #endif /* CKREGEX */ #ifdef ZXREWIND makestr(&(optlist[noptlist++]),"ZXREWIND"); #endif /* ZXREWIND */ #ifdef CKSYSLOG makestr(&(optlist[noptlist++]),"CKSYSLOG"); #endif /* CKSYSLOG */ #ifdef SYSLOGLEVEL sprintf(line,"SYSLOGLEVEL=%d",SYSLOGLEVEL); /* SAFE */ makestr(&(optlist[noptlist++]),line); #endif /* SYSLOGLEVEL */ #ifdef NOSEXP makestr(&(optlist[noptlist++]),"NOSEXP"); #endif /* NOSEXP */ #ifdef CKLEARN makestr(&(optlist[noptlist++]),"CKLEARN"); #else #ifdef NOLOEARN makestr(&(optlist[noptlist++]),"NOLOEARN"); #endif /* NOLOEARN */ #endif /* CKLEARN */ #ifdef NOFLOAT makestr(&(optlist[noptlist++]),"NOFLOAT"); #else #ifdef FNFLOAT makestr(&(optlist[noptlist++]),"FNFLOAT"); #endif /* FNFLOAT */ #ifdef CKFLOAT #ifdef GFTIMER makestr(&(optlist[noptlist++]),"GFTIMER"); #endif /* GFTIMER */ #ifdef CKFLOAT_S ckmakmsg(line,LINBUFSIZ,"CKFLOAT=",CKFLOAT_S,NULL,NULL); makestr(&(optlist[noptlist++]),line); #else makestr(&(optlist[noptlist++]),"CKFLOAT"); #endif /* CKFLOAT_S */ #endif /* CKFLOAT */ #endif /* NOFLOAT */ #ifdef SSH makestr(&(optlist[noptlist++]),"SSH"); #endif /* SSH */ #ifdef NETDLL makestr(&(optlist[noptlist++]),"NETDLL"); #endif /* NETDLL */ #ifdef NETFILE makestr(&(optlist[noptlist++]),"NETFILE"); #endif /* NETFILE */ #ifdef CK_TAPI makestr(&(optlist[noptlist++]),"CK_TAPI"); #endif /* CK_TAPI */ #ifdef CK_SSL makestr(&(optlist[noptlist++]),"CK_SSL"); #ifdef OPENSSL_VERSION_TEXT ckmakmsg(line,LINBUFSIZ, "OPENSSL_VERSION_TEXT=","\"",OPENSSL_VERSION_TEXT,"\""); makestr(&(optlist[noptlist++]),line); #endif /* OPENSSL_VERSION_TEXT */ #endif /* CK_SSL */ debug(F101,"initoptlist noptlist","",noptlist); sh_sort(optlist,NULL,noptlist,0,0,0); } int shofea() { int i; int flag = 0; int lines = 1; #ifdef FNFLOAT extern int fp_digits, fp_rounding; #endif /* FNFLOAT */ extern int byteorder; printf("%s\n",versio); if (inserver) return(1); debug(F101,"shofea NOPTLIST","",NOPTLIST); initoptlist(); debug(F101,"shofea noptlist","",noptlist); #ifdef OS2 #ifdef NT #ifdef _M_ALPHA printf("Microsoft Windows Operating Systems for Alpha CPUs.\n"); #else /* _M_ALPHA */ #ifdef _M_PPC printf("Microsoft Windows Operating Systems for PowerPC CPUs.\n"); #else /* _M_PPC */ #ifdef _M_MRX000 printf("Microsoft Windows Operating Systems for MIPS CPUs.\n"); #else /* _M_MRX000 */ #ifdef _M_IX86 printf("Microsoft Windows Operating Systems for 32-bit Intel CPUs.\n"); #else /* _M_IX86 */ UNKNOWN WINDOWS PLATFORM #endif /* _M_IX86 */ #endif /* _M_MRX000 */ #endif /* _M_PPC */ #endif /* _M_ALPHA */ #else /* NT */ #ifdef M_I286 printf("IBM OS/2 16-bit.\n"); #else printf("IBM OS/2 32-bit.\n"); #endif /* M_I286 */ #endif /* NT */ lines++; #endif /* OS2 */ printf("\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } printf("Major optional features included:\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } if (sizeof(CK_OFF_T) == 8) { printf(" Large files and large integers (64 bits)\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } } #ifdef NETCONN printf(" Network support (type SHOW NET for further info)\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } #ifdef IKS_OPTION printf(" Telnet Kermit Option\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } #endif /* IKS_OPTION */ #ifdef CK_AUTHENTICATION printf(" Telnet Authentication Option\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } #ifdef CK_KERBEROS #ifdef KRB4 #ifdef KRB5 printf(" Kerberos(TM) IV and Kerberos V authentication\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } #else /* KRB5 */ printf(" Kerberos(TM) IV authentication\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } #endif /* KRB5 */ #else /* KRB4 */ #ifdef KRB5 printf(" Kerberos(TM) V authentication\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } #endif /* KRB5 */ #endif /* KRB4 */ #endif /* CK_KERBEROS */ #ifdef CK_SRP printf(" SRP(TM) (Secure Remote Password) authentication\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } #endif /* CK_SRP */ #ifdef CK_SSL printf(" Secure Sockets Layer (SSL)\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } printf(" Transport Layer Security (TLS)\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } #endif /* CK_SSL */ #ifdef SSHBUILTIN printf(" Secure Shell (SSH) [internal]\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } #endif /* SSHBUILTIN */ #ifdef SSHCMD printf(" Secure Shell (SSH) [external]\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } #endif /* SSHCMD */ #ifdef CK_ENCRYPTION printf(" Telnet Encryption Option\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } #ifdef CK_DES printf(" Telnet DES Encryption\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } #endif /* CK_DES */ #ifdef CK_CAST printf(" Telnet CAST Encryption\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } #endif /* CK_CAST */ #ifdef CK_KERBEROS #ifdef KRB5 #ifdef ALLOW_KRB_3DES_ENCRYPT printf(" Kerberos 3DES/AES Telnet Encryption\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } #endif /* ALLOW_KRB_3DES_ENCRYPT */ #endif /* KRB5 */ #endif /* CK_KERBEROS */ #endif /* CK_ENCRYPTION */ #endif /* CK_AUTHENTICATION */ #ifdef CK_FORWARD_X printf(" X Windows forwarding\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } #endif /* CK_FORWARD_X */ #ifdef TN_COMPORT printf(" Telnet Remote Com Port Control Option\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } #endif /* TN_COMPORT */ #ifdef CK_SOCKS #ifdef CK_SOCKS5 printf(" SOCKS 5\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } #else /* CK_SOCKS5 */ printf(" SOCKS 4\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } #endif /* CK_SOCKS5 */ #endif /* CK_SOCKS */ #ifdef NEWFTP printf(" Built-in FTP client\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } #endif /* NEWFTP */ #ifdef CKHTTP printf(" Built-in HTTP client\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } #endif /* CKHTTP */ #endif /* NETCONN */ #ifdef CK_RTSCTS printf(" Hardware flow control\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } #endif /* CK_RTSCTS */ #ifdef CK_XYZ #ifdef XYZ_INTERNAL printf(" Built-in XYZMODEM protocols\n"); #else printf(" External XYZMODEM protocol support\n"); #endif /* XYZ_INTERNAL */ if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } #endif /* CK_XYZ */ #ifndef NOCSETS printf(" Latin-1 (West European) character-set translation\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } #ifdef LATIN2 printf(" Latin-2 (East European) character-set translation\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } #endif /* LATIN2 */ #ifdef CYRILLIC printf(" Cyrillic (Russian, Ukrainian, etc) character-set translation\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } #endif /* CYRILLIC */ #ifdef GREEK printf(" Greek character-set translation\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } #endif /* GREEK */ #ifdef HEBREW printf(" Hebrew character-set translation\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } #endif /* HEBREW */ #ifdef KANJI printf(" Japanese character-set translation\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } #endif /* KANJI */ #ifdef UNICODE printf(" Unicode character-set translation\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } #endif /* UNICODE */ #ifdef CKOUNI if (isunicode()) printf(" Unicode support for ISO-2022 Terminal Emulation\n"); else printf(" Unicode translation for Terminal Character-Sets\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } #endif /* CKOUNI */ #endif /* NOCSETS */ #ifdef NETPTY printf(" Pseudoterminal control\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } #endif /* NETPTY */ #ifdef CK_REDIR printf(" REDIRECT command\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } #endif /* CK_REDIR */ #ifdef CK_RESEND printf(" RESEND command\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } #endif /* CK_RESEND */ #ifndef NOXFER #ifdef CK_CURSES printf(" Fullscreen file transfer display\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } #endif /* CK_CURSES */ #endif /* NOXFER */ #ifdef CK_SPEED printf(" Control-character unprefixing\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } #endif /* CK_SPEED */ #ifdef STREAMING printf(" Streaming\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } #endif /* STREAMING */ #ifdef CK_AUTODL printf(" Autodownload\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } #endif /* CK_AUTODL */ #ifdef OS2MOUSE printf(" Mouse support\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } #endif /* OS2MOUSE */ #ifdef CK_REXX printf(" REXX script language interface\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } #endif /* CK_REXX */ #ifdef IKSD #ifdef CK_LOGIN printf(" Internet Kermit Service with user login support\n"); #else /* CK_LOGIN */ printf(" Internet Kermit Service without user login support\n"); #endif /* CK_LOGIN */ if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } #endif /* IKSD */ printf("\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } printf("Major optional features not included:\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } if (sizeof(CK_OFF_T) <= 4) { printf(" No large files or large integers\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } } #ifdef NOXFER printf(" No file-transfer protocols\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } flag = 1; #else #ifndef CK_CURSES #ifndef MAC printf(" No fullscreen file transfer display\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } flag = 1; #endif /* MAC */ #endif /* CK_CURSES */ #ifdef NOSERVER printf(" No server mode\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } flag = 1; #endif /* NOSERVER */ #ifndef CK_SPEED printf(" No control-character unprefixing\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } flag = 1; #endif /* CK_SPEED */ #ifndef STREAMING printf(" No streaming\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } flag = 1; #endif /* STREAMING */ #ifndef CK_AUTODL printf(" No autodownload\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } flag = 1; #endif /* CK_AUTODL */ #ifndef CK_XYZ printf(" No built-in XYZMODEM protocols\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } flag = 1; #endif /* CK_XYZ */ #ifdef NOFLOAT printf(" No floating-point arithmetic\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } flag = 1; printf(" No S-Expressions (LISP interpreter)\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } flag = 1; #else #ifdef NOSEXP printf(" No S-Expressions (LISP interpreter)\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } flag = 1; #endif /* NOSEXP */ #endif /* NOFLOAT */ #ifdef NOTLOG printf(" No transaction log\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } flag = 1; #endif /* NOTLOG */ #endif /* NOXFER */ #ifdef NODEBUG printf(" No debugging\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } flag = 1; #endif /* NODEBUG */ #ifdef NOHELP printf(" No built-in help\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } flag = 1; #endif /* NOHELP */ #ifdef NOLOCAL printf(" No making connections\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } flag = 1; #else #ifndef NETCONN printf(" No network support\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } flag = 1; #else /* NETCONN */ #ifndef IKS_OPTION printf(" No Telnet Kermit Option\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } flag = 1; #endif /* IKS_OPTION */ #endif /* NETCONN */ #ifdef NOSSH printf(" No Secure Shell (SSH)\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } #endif /* NOSSH */ #ifndef CK_AUTHENTICATION printf(" No Kerberos(TM) authentication\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } printf(" No SRP(TM) (Secure Remote Password) protocol\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } printf(" No Secure Sockets Layer (SSL) protocol\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } printf(" No Transport Layer Security (TLS) protocol\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } printf(" No encryption\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } flag = 1; #else /* CK_AUTHENTICATION */ #ifndef CK_KERBEROS printf(" No Kerberos(TM) authentication\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } flag = 1; #else /* CK_KERBEROS */ #ifndef KRB4 printf(" No Kerberos(TM) IV authentication\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } flag = 1; #endif /* KRB4 */ #ifndef KRB5 printf(" No Kerberos(TM) V authentication\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } flag = 1; #endif /* KRB5 */ #endif /* CK_KERBEROS */ #ifndef CK_SRP printf(" No SRP(TM) (Secure Remote Password) authentication\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } flag = 1; #endif /* CK_SRP */ #ifndef CK_SSL printf(" No Secure Sockets Layer (SSL) protocol\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } printf(" No Transport Layer Security (TLS) protocol\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } flag = 1; #endif /* CK_SSL */ #ifndef CK_ENCRYPTION printf(" No Telnet Encryption Option\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } flag = 1; #else /* CK_ENCRYPTION */ #ifndef OS2 #ifndef CK_DES printf(" No Telnet DES encryption\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } flag = 1; #endif /* CK_DES */ #ifndef CK_CAST printf(" No Telnet CAST encryption\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } flag = 1; #endif /* CK_CAST */ #ifdef COMMENT #ifdef CK_KERBEROS #ifdef KRB5 #ifndef ALLOW_KRB_3DES_ENCRYPT printf(" No Kerberos 3DES/AES Telnet Encryption\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } #endif /* ALLOW_KRB_3DES_ENCRYPT */ #endif /* KRB5 */ #endif /* CK_KERBEROS */ #endif /* COMMENT */ #endif /* OS2 */ #endif /* CK_ENCRYPTION */ #endif /* CK_AUTHENTICATION */ #ifndef CK_FORWARD_X printf(" No X Windows forwarding\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } #endif /* CK_FORWARD_X */ #ifndef TN_COMPORT printf(" No Telnet Remote Com Port Control Option\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } #endif /* TN_COMPORT */ #ifndef CK_SOCKS printf(" No SOCKS\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } #endif /* CK_SOCKS */ #ifndef NEWFTP printf(" No built-in FTP client\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } #endif /* NEWFTP */ #ifdef NOHTTP printf(" No built-in HTTP client\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } #endif /* NOHTTP */ #ifdef NODIAL printf(" No DIAL command\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } flag = 1; #else #ifdef MINIDIAL printf(" Support for most modem types excluded\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } flag = 1; #endif /* MINIDIAL */ #endif /* NODIAL */ #endif /* NOLOCAL */ #ifndef CK_RTSCTS #ifndef MAC printf(" No hardware flow control\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } flag = 1; #endif /* MAC */ #endif /* CK_RTSCTS */ #ifdef NOXMIT printf(" No TRANSMIT command\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } flag = 1; #endif /* NOXMIT */ #ifdef NOSCRIPT printf(" No SCRIPT command\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } flag = 1; #endif /* NOSCRIPT */ #ifdef NOSPL printf(" No script programming features\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } flag = 1; #endif /* NOSPL */ #ifdef NOCSETS printf(" No character-set translation\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } flag = 1; #else #ifndef LATIN2 printf(" No Latin-2 character-set translation\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } flag = 1; #endif /* LATIN2 */ #ifdef NOGREEK printf(" No Greek character-set translation\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } flag = 1; #endif /* NOGREEK */ #ifdef NOHEBREW printf(" No Hebrew character-set translation\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } flag = 1; #endif /* NOHEBREW */ #ifdef NOUNICODE printf(" No Unicode character-set translation\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } flag = 1; #endif /* NOUNICODE */ #ifdef NOCYRIL printf(" No Cyrillic character-set translation\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } flag = 1; #endif /* NOCYRIL */ #ifndef KANJI printf(" No Kanji character-set translation\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } flag = 1; #endif /* KANJI */ #endif /* NOCSETS */ #ifdef NOCMDL printf(" No command-line arguments\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } flag = 1; #endif /* NOCMDL */ #ifdef NOPUSH printf(" No escape to system\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } flag = 1; #endif /* NOPUSH */ #ifdef NOJC #ifdef UNIX printf(" No UNIX job control\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } flag = 1; #endif /* UNIX */ #endif /* NOJC */ #ifdef NOSETKEY printf(" No SET KEY command\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } flag = 1; #endif /* NOSETKEY */ #ifndef CK_REDIR printf(" No REDIRECT or PIPE command\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } flag = 1; #endif /* CK_REDIR */ #ifdef UNIX #ifndef NETPTY printf(" No pseudoterminal control\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } flag = 1; #endif /* NETPTY */ #endif /* UNIX */ #ifndef CK_RESEND printf(" No RESEND command\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } flag = 1; #endif /* CK_RESEND */ #ifdef OS2 #ifdef __32BIT__ #ifndef OS2MOUSE printf(" No Mouse support\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } flag = 1; #endif /* __32BIT__ */ #endif /* OS2 */ #endif /* OS2MOUSE */ #ifdef OS2 #ifndef NT #ifndef CK_REXX printf(" No REXX script language interface\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } flag = 1; #endif /* CK_REXX */ #endif /* NT */ #endif /* OS2 */ #ifndef IKSD printf(" No Internet Kermit Service\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } flag = 1; #endif /* IKSD */ if (flag == 0) { printf(" None\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } } printf("\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } #ifdef CK_UTSNAME if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } printf("Host info:\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } printf(" Machine: %s\n",unm_mch[0] ? unm_mch : "(unknown)"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } printf(" Model: %s\n",unm_mod[0] ? unm_mod : "(unknown)"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } printf(" OS: %s\n",unm_nam[0] ? unm_nam : "(unknown)"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } printf(" OS Release: %s\n",unm_rel[0] ? unm_rel : "(unknown)"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } printf(" OS Version: %s\n",unm_ver[0] ? unm_ver : "(unknown)"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } printf("\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } #endif /* CK_UTSNAME */ /* Print compile-time (-D) options, as well as C preprocessor predefined symbols that might affect us... */ #ifdef KTARGET { char * s; /* Makefile target */ s = KTARGET; if (!s) s = ""; if (!*s) s = "(unknown)"; printf("\n"); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } printf("Target: %s\n", s); if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } } #endif /* KTARGET */ #ifdef __VERSION__ #ifdef __GNUC__ printf("GCC version: %s\n", __VERSION__); #else printf("Compiler version: %s\n", __VERSION__); #endif /* __GNUC__ */ if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } #endif /* __VERSION__ */ #ifdef __DATE__ /* GNU and other ANSI */ #ifdef __TIME__ printf("Compiled %s %s, options:\n", __DATE__, __TIME__); #else printf("Compiled %s, options:\n", __DATE__); #endif /* __TIME__ */ #else /* !__DATE__ */ printf("Compiler options:\n"); #endif /* __DATE__ */ if (++lines > cmd_rows - 3) { if (!askmore()) return(1); else lines = 0; } for (i = 0; i < noptlist; i++) /* Print sorted option list */ if (!prtopt(&lines,optlist[i])) return(0); if (!prtopt(&lines,"")) return(0); /* Start a new section */ /* Sizes of data types */ ckmakmsg(line, LINBUFSIZ, "byte order: ", byteorder ? "little" : "big", " endian", NULL ); { /* Whether to use %d or %ld with sizeof is a portability issue, so... */ int size = 0; if (!prtopt(&lines,line)) return(0); if (!prtopt(&lines,"")) return(0); /* Start a new section */ size = (int)sizeof(int); sprintf(line,"sizeofs: int=%d",size); /* SAFE */ if (!prtopt(&lines,line)) return(0); size = (int)sizeof(long); sprintf(line,"long=%d",size); /* SAFE */ if (!prtopt(&lines,line)) return(0); #ifndef OS2 /* Windows doesn't have off_t */ size = (int)sizeof(off_t); sprintf(line,"off_t=%d",size); /* SAFE */ if (!prtopt(&lines,line)) return(0); #endif /* OS2 */ size = (int)sizeof(CK_OFF_T); sprintf(line,"CK_OFF_T=%d",size); /* SAFE */ if (!prtopt(&lines,line)) return(0); #ifdef BIGBUFOK size = (int)sizeof(size_t); sprintf(line,"size_t=%d",size); /* SAFE */ if (!prtopt(&lines,line)) return(0); #endif /* BIGBUFOK */ size = (int)sizeof(short); sprintf(line,"short=%d",size); /* SAFE */ if (!prtopt(&lines,line)) return(0); size = (int)sizeof(char); sprintf(line,"char=%d",size); /* SAFE */ if (!prtopt(&lines,line)) return(0); size = (int)sizeof(char *); sprintf(line,"char*=%d",size); /* SAFE */ if (!prtopt(&lines,line)) return(0); size = (int)sizeof(float); sprintf(line,"float=%d",size); /* SAFE */ if (!prtopt(&lines,line)) return(0); size = (int)sizeof(double); sprintf(line,"double=%d",size); /* SAFE */ if (!prtopt(&lines,line)) return(0); } #ifdef FNFLOAT if (!prtopt(&lines,"")) return(0); /* Start a new section */ if (!prtopt(&lines,"floating-point:")) return(0); sprintf(line,"precision=%d",fp_digits); /* SAFE */ if (!prtopt(&lines,line)) return(0); sprintf(line,"rounding=%d",fp_rounding); /* SAFE */ if (!prtopt(&lines,line)) return(0); #endif /* FNFLOAT */ prtopt(&lines,""); return(0); } #endif /* NOSHOW */ #endif /* NOICP */ ckuus6.c0000644000015300001460000141307011573441567011270 0ustar fdckermit#include "ckcsym.h" #ifndef NOICP /* Authors: Frank da Cruz , The Kermit Project, Columbia University, New York City Jeffrey E Altman Secure Endpoints Inc., New York City Copyright (C) 1985, 2011, Trustees of Columbia University in the City of New York. All rights reserved. See the C-Kermit COPYING.TXT file or the copyright text in the ckcmai.c module for disclaimer and permissions. */ /* Includes */ #include "ckcdeb.h" #include "ckcasc.h" #include "ckcker.h" #include "ckuusr.h" #include "ckcxla.h" #include "ckcnet.h" /* Network symbols */ #include #ifndef NOSTAT #ifdef VMS /* 2010-03-09 SMS. VAX C needs help to find "sys". It's easier not to try. */ #include #else /* def VMS */ #include #endif /* def VMS [else] */ #endif /* NOSTAT */ #ifdef VMS #ifndef TCPSOCKET #include #endif /* TCPSOCKET */ #endif /* VMS */ #ifdef datageneral #define fgets(stringbuf,max,fd) dg_fgets(stringbuf,max,fd) #endif /* datageneral */ #ifdef QNX6 #define readblock kreadblock #endif /* QNX6 */ /* External Kermit Variables, see ckmain.c for description. */ extern xx_strp xxstring; extern int local, xitsta, binary, parity, escape, flow, cmd_rows, turn, turnch, duplex, ckxech, seslog, dfloc, cnflg, tlevel, pflag, msgflg, mdmtyp, zincnt, quiet, repars, techo, network, nzxopts, what, filepeek, recursive; extern int xaskmore, tt_rows, tt_cols, cmd_cols, g_matchdot, diractive, xcmdsrc, nscanfile, reliable, nolinks, cmflgs; #ifdef VMSORUNIX extern int zgfs_dir, zgfs_link; #endif /* VMSORUNIX */ #ifdef CK_IFRO extern int remonly; #endif /* CK_IFRO */ #ifdef OS2 extern int StartedFromDialer ; extern int vmode; extern int k95stdout; #ifndef NT #define INCL_NOPM #define INCL_VIO /* Needed for ckocon.h */ #include #undef COMMENT #else #define APIRET ULONG #include #include #include "ckntap.h" #endif /* NT */ #include "ckocon.h" #endif /* OS2 */ extern long vernum, speed; extern char *versio, *protv, *ckxv, *ckzv, *fnsv, *connv, *dftty, *cmdv; extern char *dialv, *loginv, *for_def[], *whil_def[], *xif_def[], *sw_def[]; extern char *foz_def[]; extern char *ckxsys, *ckzsys; #ifndef OS2 extern char *DIRCMD; #ifndef UNIX extern char *DELCMD; #endif /* UNIX */ #endif /* OS2 */ extern char ttname[], filnam[]; extern CHAR sstate, feol; extern char *zinptr; #ifdef UNIX extern char ** mtchs; /* zxpand() file list */ #endif /* UNIX */ #ifndef NOXFER extern int oopts, omode, oname, opath; /* O-Packet options */ extern int stdinf, sndsrc, size, rpsiz, urpsiz, fncnv, fnrpath, displa, stdouf, isguest, pktlog, nfils, keep, maxrps, fblksiz, frecl, frecfm, atcapr, atdiso, spsizf, spsiz, spsizr, spmax, wslotr, prefixing, fncact, fnspath, nprotos, g_proto, g_urpsiz, g_spsizf, g_spsiz, g_spsizr, g_spmax, g_wslotr, g_prefixing, g_fncact, g_fncnv, g_fnspath, g_fnrpath, xfrxla, g_xfrxla; extern char *cmarg, *cmarg2; #ifndef NOMSEND /* Multiple SEND */ extern char *msfiles[]; #endif /* NOMSEND */ extern char fspec[]; /* Most recent filespec */ extern int fspeclen; #ifdef CK_TMPDIR extern int f_tmpdir; /* Directory changed temporarily */ extern char savdir[]; /* For saving current directory */ #endif /* CK_TMPDIR */ extern struct keytab protos[]; /* File transfer protocols */ extern struct ck_p ptab[NPROTOS]; #endif /* NOXFER */ #ifdef DCMDBUF /* Declarations from cmd package */ extern char *cmdbuf, *atmbuf; /* Command buffers */ #else extern char cmdbuf[], atmbuf[]; /* Command buffers */ #endif /* DCMDBUF */ extern int nopush; #ifndef NOSPL int askflag = 0; /* ASK-class command active */ int echostars = 0; /* ASKQ should echo asterisks */ extern char **a_ptr[]; extern int a_dim[]; extern char **m_xarg[]; extern int n_xarg[]; extern struct mtab *mactab; extern int nmac; extern long ck_alarm; extern char alrm_date[], alrm_time[]; extern int x_ifnum; #endif /* NOSPL */ extern int inserver; /* I am IKSD */ extern int backgrd; /* Kermit executing in background */ extern char psave[]; /* For saving & restoring prompt */ extern char *tp; /* Temporary buffer */ int readblock = 4096; /* READ buffer size */ CHAR * readbuf = NULL; /* Pointer to read buffer */ int readsize = 0; /* Number of chars actually read */ int getcmd = 0; /* GET-class command was given */ extern int zchkod, zchkid; /* C K U U S 6 -- "User Interface" for Unix Kermit (Part 6) */ struct keytab deltab[] = { /* DELETE Command Options */ { "/all", DEL_ALL, CM_INV }, { "/after", DEL_AFT, CM_ARG }, { "/ask", DEL_ASK, 0 }, { "/before", DEL_BEF, CM_ARG }, { "/directories", DEL_DIR, 0 }, { "/dotfiles", DEL_DOT, 0 }, { "/except", DEL_EXC, CM_ARG }, { "/heading", DEL_HDG, 0 }, { "/l", DEL_LIS, CM_INV|CM_ABR }, { "/larger-than", DEL_LAR, CM_ARG }, { "/list", DEL_LIS, 0 }, { "/log", DEL_LIS, CM_INV }, { "/noask", DEL_NAS, 0 }, { "/nodotfiles", DEL_NOD, 0 }, { "/noheading", DEL_NOH, 0 }, { "/nol", DEL_NOL, CM_INV|CM_ABR }, { "/nolist", DEL_NOL, 0 }, { "/nolog", DEL_NOL, CM_INV }, { "/nopage", DEL_NOP, 0 }, { "/not-after", DEL_NAF, CM_ARG }, { "/not-before", DEL_NBF, CM_ARG }, { "/not-since", DEL_NAF, CM_INV|CM_ARG }, { "/page", DEL_PAG, 0 }, { "/quiet", DEL_QUI, CM_INV }, { "/recursive", DEL_REC, 0 }, { "/simulate", DEL_SIM, 0 }, { "/since", DEL_AFT, CM_ARG|CM_INV }, { "/smaller-than", DEL_SMA, CM_ARG }, { "/summary", DEL_SUM, 0 }, { "/tree", DEL_ALL, 0 }, { "/type", DEL_TYP, CM_ARG }, { "/verbose", DEL_VRB, CM_INV } }; int ndeltab = sizeof(deltab)/sizeof(struct keytab); /* /QUIET-/VERBOSE (/LIST-/NOLIST) (/LOG-/NOLOG) table */ struct keytab qvswtab[] = { { "/l", DEL_LIS, CM_INV|CM_ABR }, { "/list", DEL_LIS, 0 }, { "/log", DEL_LIS, CM_INV }, { "/nol", DEL_NOL, CM_INV|CM_ABR }, { "/nolist", DEL_NOL, 0 }, { "/nolog", DEL_NOL, CM_INV }, { "/quiet", DEL_QUI, CM_INV }, { "/verbose", DEL_VRB, CM_INV } }; int nqvswtab = sizeof(qvswtab)/sizeof(struct keytab); static struct keytab renamsw[] = { { "/collision", REN_OVW, CM_ARG }, #ifndef NOUNICODE { "/convert", REN_XLA, CM_ARG }, #endif /* NOUNICODE */ { "/fixspaces", REN_SPA, CM_ARG }, { "/l", DEL_LIS, CM_INV|CM_ABR }, { "/list", DEL_LIS, 0 }, { "/log", DEL_LIS, CM_INV }, { "/lower", REN_LOW, CM_ARG }, { "/nol", DEL_NOL, CM_INV|CM_ABR }, { "/nolist", DEL_NOL, 0 }, { "/nolog", DEL_NOL, CM_INV }, { "/quiet", DEL_QUI, CM_INV }, { "/replace", REN_RPL, CM_ARG }, { "/simulate", DEL_SIM, 0 }, { "/upper", REN_UPP, CM_ARG }, { "/verbose", DEL_VRB, CM_INV } }; static int nrenamsw = sizeof(renamsw)/sizeof(struct keytab); static struct keytab renamset[] = { { "collision", REN_OVW, 0 }, { "list", DEL_LIS, 0 } }; static int nrenamset = sizeof(renamset)/sizeof(struct keytab); /* Args for RENAME /LOWER: and /UPPER: */ static struct keytab r_upper[] = { { "all", 1, 0 }, { "lower", 0, 0 } }; static struct keytab r_lower[] = { { "all", 1, 0 }, { "upper", 0, 0 } }; /* Args for RENAME /COLLISION... */ #define RENX_FAIL 0 #define RENX_OVWR 1 #define RENX_SKIP 2 static struct keytab r_collision[] = { { "fail", RENX_FAIL, 0 }, { "overwrite", RENX_OVWR, 0 }, { "proceed", RENX_SKIP, CM_INV }, { "skip", RENX_SKIP, 0 } }; static int nr_collision = sizeof(r_collision)/sizeof(struct keytab); struct keytab copytab[] = { { "/append", 998, 0 }, #ifndef NOSPL { "/fromb64", 997, 0 }, #endif /* NOSPL */ { "/l", DEL_LIS, CM_INV|CM_ABR }, { "/list", DEL_LIS, 0 }, { "/log", DEL_LIS, CM_INV }, { "/nol", DEL_NOL, CM_INV|CM_ABR }, { "/nolist", DEL_NOL, 0 }, { "/nolog", DEL_NOL, CM_INV }, { "/overwrite", 994, CM_ARG }, #ifndef NOXFER { "/preserve", 995, 0 }, #endif /* NOXFER */ { "/quiet", DEL_QUI, CM_INV }, { "/swap-bytes", 999, 0 }, #ifndef NOSPL { "/tob64", 996, 0 }, #endif /* NOSPL */ { "/verbose", DEL_VRB, CM_INV } }; int ncopytab = sizeof(copytab)/sizeof(struct keytab); #define OVW_ALWAYS 0 #define OVW_NEVER 1 #define OVW_OLDER 2 #define OVW_NEWER 3 static struct keytab ovwtab[] = { { "always", OVW_ALWAYS, 0 }, { "never", OVW_NEVER, 0 }, { "newer", OVW_NEWER, 0 }, { "older", OVW_OLDER, 0 } }; static int novwtab = 4; #ifndef NOXFER static struct keytab gettab[] = { /* GET options */ { "/as-name", SND_ASN, CM_ARG }, { "/binary", SND_BIN, 0 }, #ifdef CALIBRATE { "/calibrate", SND_CAL, CM_INV }, #endif /* CALIBRATE */ #ifdef PIPESEND { "/command", SND_CMD, CM_PSH }, #endif /* PIPESEND */ { "/delete", SND_DEL, 0 }, { "/except", SND_EXC, CM_ARG }, { "/filenames", SND_NAM, CM_ARG }, #ifdef PIPESEND { "/filter", SND_FLT, CM_ARG|CM_PSH }, #endif /* PIPESEND */ #ifdef VMS { "/image", SND_IMG, 0 }, { "/labeled", SND_LBL, 0 }, #else { "/image", SND_BIN, CM_INV }, #endif /* VMS */ #ifdef CK_TMPDIR { "/move-to", SND_MOV, CM_ARG }, #endif /* CK_TMPDIR */ { "/pathnames", SND_PTH, CM_ARG }, { "/pipes", SND_PIP, CM_ARG|CM_PSH }, { "/quiet", SND_SHH, 0 }, #ifdef CK_RESEND { "/recover", SND_RES, 0 }, #endif /* CK_RESEND */ { "/recursive", SND_REC, 0 }, { "/rename-to", SND_REN, CM_ARG }, #ifdef COMMENT { "/smaller-than", SND_SMA, CM_ARG }, #endif /* COMMENT */ { "/subdirectories", SND_REC, CM_INV }, { "/text", SND_TXT, 0 }, { "/transparent", SND_XPA, 0 } }; #define NGETTAB sizeof(gettab)/sizeof(struct keytab) static int ngettab = NGETTAB; static struct keytab rcvtab[] = { /* RECEIVE options */ { "/as-name", SND_ASN, CM_ARG }, { "/binary", SND_BIN, 0 }, #ifdef CALIBRATE { "/calibrate", SND_CAL, CM_INV }, #endif /* CALIBRATE */ #ifdef PIPESEND { "/command", SND_CMD, CM_PSH }, #endif /* PIPESEND */ { "/except", SND_EXC, CM_ARG }, { "/filenames", SND_NAM, CM_ARG }, #ifdef PIPESEND { "/filter", SND_FLT, CM_ARG|CM_PSH }, #endif /* PIPESEND */ #ifdef VMS { "/image", SND_IMG, 0 }, { "/labeled", SND_LBL, 0 }, #else { "/image", SND_BIN, CM_INV }, #endif /* VMS */ #ifdef CK_TMPDIR { "/move-to", SND_MOV, CM_ARG }, #endif /* CK_TMPDIR */ { "/pathnames", SND_PTH, CM_ARG }, { "/pipes", SND_PIP, CM_ARG|CM_PSH }, #ifdef CK_XYZ { "/protocol", SND_PRO, CM_ARG }, #else { "/protocol", SND_PRO, CM_ARG|CM_INV }, #endif /* CK_XYZ */ { "/quiet", SND_SHH, 0 }, { "/recursive", SND_REC, 0 }, { "/rename-to", SND_REN, CM_ARG }, { "/text", SND_TXT, 0 }, { "/transparent", SND_XPA, 0 } }; #define NRCVTAB sizeof(rcvtab)/sizeof(struct keytab) static int nrcvtab = NRCVTAB; #endif /* NOXFER */ /* WAIT table */ #define WAIT_FIL 997 #define WAIT_MDM 998 struct keytab waittab[] = { { "cd", BM_DCD, CM_INV }, /* (Carrier Detect) */ { "cts", BM_CTS, CM_INV }, /* (Clear To Send) */ { "dsr", BM_DSR, CM_INV }, /* (Data Set Ready) */ { "file", WAIT_FIL, 0 }, /* New category selector keywords */ { "modem-signals", WAIT_MDM, 0 }, /* ... */ { "ri", BM_RNG, CM_INV } /* (Ring Indicator) */ }; int nwaittab = (sizeof(waittab) / sizeof(struct keytab)); /* Modem signal table */ struct keytab mstab[] = { { "cd", BM_DCD, 0 }, /* Carrier Detect */ { "cts", BM_CTS, 0 }, /* Clear To Send */ { "dsr", BM_DSR, 0 }, /* Data Set Ready */ { "ri", BM_RNG, 0 } /* Ring Indicator */ }; int nms = (sizeof(mstab) / sizeof(struct keytab)); #define WF_MOD 1 #define WF_DEL 2 #define WF_CRE 3 struct keytab wfswi[] = { /* WAIT FILE switches */ { "creation", WF_CRE, 0 }, /* Wait for file to be created */ { "deletion", WF_DEL, 0 }, /* Wait for file to be deleted */ { "modification", WF_MOD, 0 } /* Wait for file to be modified */ }; int nwfswi = (sizeof(wfswi) / sizeof(struct keytab)); #ifndef NOSPL struct keytab asgtab[] = { /* Assignment operators for "." */ { "::=", 2, 0 }, /* ASSIGN and EVALUATE */ { ":=", 1, 0 }, /* ASSIGN */ { "=", 0, 0 } /* DEFINE */ }; int nasgtab = (sizeof(asgtab) / sizeof(struct keytab)); struct keytab opntab[] = { #ifndef NOPUSH { "!read", OPN_PI_R, CM_INV }, { "!write", OPN_PI_W, CM_INV }, #endif /* NOPUSH */ { "append", OPN_FI_A, 0 }, { "host", OPN_NET, 0 }, #ifdef OS2 { "line", OPN_SER, CM_INV }, { "port", OPN_SER, 0 }, #else { "line", OPN_SER, 0 }, { "port", OPN_SER, CM_INV }, #endif /* OS2 */ { "read", OPN_FI_R, 0 }, { "write", OPN_FI_W, 0 } }; int nopn = (sizeof(opntab) / sizeof(struct keytab)); /* IF conditions */ #define XXIFCO 0 /* IF COUNT */ #define XXIFER 1 /* IF ERRORLEVEL */ #define XXIFEX 2 /* IF EXIST */ #define XXIFFA 3 /* IF FAILURE */ #define XXIFSU 4 /* IF SUCCESS */ #define XXIFNO 5 /* IF NOT */ #define XXIFDE 6 /* IF DEFINED */ #define XXIFEQ 7 /* IF EQUAL (strings) */ #define XXIFAE 8 /* IF = (numbers) */ #define XXIFLT 9 /* IF < (numbers) */ #define XXIFGT 10 /* IF > (numbers) */ #define XXIFLL 11 /* IF Lexically Less Than (strings) */ #define XXIFLG 12 /* IF Lexically Greater Than (strings) */ #define XXIFEO 13 /* IF EOF (READ file) */ #define XXIFBG 14 /* IF BACKGROUND */ #define XXIFNU 15 /* IF NUMERIC */ #define XXIFFG 16 /* IF FOREGROUND */ #define XXIFDI 17 /* IF DIRECTORY */ #define XXIFNE 18 /* IF NEWER */ #define XXIFRO 19 /* IF REMOTE-ONLY */ #define XXIFAL 20 /* IF ALARM */ #define XXIFSD 21 /* IF STARTED-FROM-DIALER */ #define XXIFTR 22 /* IF TRUE */ #define XXIFNT 23 /* IF FALSE */ #define XXIFTM 24 /* IF TERMINAL-MACRO */ #define XXIFEM 25 /* IF EMULATION */ #define XXIFOP 26 /* IF OPEN */ #define XXIFLE 27 /* IF <= */ #define XXIFGE 28 /* IF >= */ #define XXIFIP 29 /* IF INPATH */ #define XXIFTA 30 /* IF TAPI */ #define XXIFMA 31 /* IF MATCH */ #define XXIFFL 32 /* IF FLAG */ #define XXIFAB 33 /* IF ABSOLUTE */ #define XXIFAV 34 /* IF AVAILABLE */ #define XXIFAT 35 /* IF ASKTIMEOUT */ #define XXIFRD 36 /* IF READABLE */ #define XXIFWR 37 /* IF WRITEABLE */ #define XXIFAN 38 /* IF ... AND ... */ #define XXIFOR 39 /* IF ... OR ... */ #define XXIFLP 40 /* IF left parenthesis */ #define XXIFRP 41 /* IF right parenthesis */ #define XXIFNQ 42 /* IF != (== "NOT =") */ #define XXIFQU 43 /* IF QUIET */ #define XXIFCK 44 /* IF C-KERMIT */ #define XXIFK9 45 /* IF K-95 */ #define XXIFMS 46 /* IF MS-KERMIT */ #define XXIFWI 47 /* IF WILD */ #define XXIFLO 48 /* IF LOCAL */ #define XXIFCM 49 /* IF COMMAND */ #define XXIFFP 50 /* IF FLOAT */ #define XXIFIK 51 /* IF IKS */ #define XXIFKB 52 /* IF KBHIT */ #define XXIFKG 53 /* IF KERBANG */ #define XXIFVE 54 /* IF VERSION */ #define XXIFDC 55 /* IF DECLARED */ #define XXIFGU 56 /* IF GUI */ #define XXIFLN 57 /* IF LINK */ #define XXIFDB 58 /* IF DEBUG */ struct keytab iftab[] = { /* IF commands */ { "!", XXIFNO, 0 }, { "!=", XXIFNQ, 0 }, { "&&", XXIFAN, 0 }, { "(", XXIFLP, 0 }, { ")", XXIFRP, 0 }, { "<", XXIFLT, 0 }, { "<=", XXIFLE, 0 }, { "=", XXIFAE, 0 }, { "==", XXIFAE, CM_INV }, { ">", XXIFGT, 0 }, { ">=", XXIFGE, 0 }, { "absolute", XXIFAB, 0 }, { "alarm", XXIFAL, 0 }, { "and", XXIFAN, 0 }, { "asktimeout", XXIFAT, 0 }, { "available", XXIFAV, 0 }, { "background", XXIFBG, 0 }, { "c-kermit", XXIFCK, 0 }, { "command", XXIFCM, 0 }, { "count", XXIFCO, 0 }, { "dcl", XXIFDC, CM_INV }, { "debug", XXIFDB, 0 }, { "declared", XXIFDC, 0 }, { "defined", XXIFDE, 0 }, #ifdef CK_TMPDIR { "directory", XXIFDI, 0 }, #endif /* CK_TMPDIR */ { "emulation", XXIFEM, 0 }, #ifdef COMMENT { "eof", XXIFEO, 0 }, #endif /* COMMENT */ { "equal", XXIFEQ, 0 }, { "error", XXIFFA, CM_INV }, { "exist", XXIFEX, 0 }, { "failure", XXIFFA, 0 }, { "false", XXIFNT, 0 }, { "flag", XXIFFL, 0 }, #ifdef CKFLOAT { "float", XXIFFP, 0 }, #endif /* CKFLOAT */ { "foreground", XXIFFG, 0 }, #ifdef OS2 { "gui", XXIFGU, 0 }, #else { "gui", XXIFGU, CM_INV }, #endif /* OS2 */ #ifdef IKSD { "iksd", XXIFIK, 0 }, #else { "iksd", XXIFIK, CM_INV }, #endif /* IKSD */ { "integer", XXIFNU, CM_INV }, { "k-95", XXIFK9, 0 }, { "kbhit", XXIFKB, 0 }, #ifdef UNIX { "kerbang", XXIFKG, 0 }, #else { "kerbang", XXIFKG, CM_INV }, #endif /* UNIX */ { "lgt", XXIFLG, 0 }, #ifdef UNIX { "link", XXIFLN, 0 }, #endif /* UNIX */ { "llt", XXIFLL, 0 }, { "local", XXIFLO, 0 }, { "match", XXIFMA, 0 }, { "ms-kermit", XXIFMS, CM_INV }, #ifdef ZFCDAT { "newer", XXIFNE, 0 }, #endif /* ZFCDAT */ { "not", XXIFNO, 0 }, { "numeric", XXIFNU, 0 }, { "ok", XXIFSU, CM_INV }, { "open", XXIFOP, 0 }, { "or", XXIFOR, 0 }, { "quiet", XXIFQU, 0 }, { "readable", XXIFRD, 0 }, { "remote-only",XXIFRO, 0 }, { "started-from-dialer",XXIFSD, CM_INV }, { "success", XXIFSU, 0 }, { "tapi", XXIFTA, 0 }, #ifdef OS2 { "terminal-macro", XXIFTM, 0 }, #else { "terminal-macro", XXIFTM, CM_INV }, #endif /* OS2 */ { "true", XXIFTR, 0 }, { "version", XXIFVE, 0 }, { "wild", XXIFWI, 0 }, { "writeable", XXIFWR, 0 }, { "||", XXIFOR, 0 }, { "", 0, 0 } }; int nif = (sizeof(iftab) / sizeof(struct keytab)) - 1; struct keytab iotab[] = { /* Keywords for IF OPEN */ { "!read-file", ZRFILE, CM_INV }, { "!write-file", ZWFILE, CM_INV }, { "append-file", ZWFILE, CM_INV }, { "connection", 8888, 0 }, #ifdef CKLOGDIAL { "cx-log", 7777, 0 }, #endif /* CKLOGDIAL */ { "debug-log", ZDFILE, 0 }, { "error", 9999, 0 }, { "packet-log", ZPFILE, 0 }, { "read-file", ZRFILE, 0 }, { "screen", ZSTDIO, 0 }, { "session-log", ZSFILE, 0 }, { "transaction-log", ZTFILE, 0 }, { "write-file", ZWFILE, 0 } }; int niot = (sizeof(iotab) / sizeof(struct keytab)); #endif /* NOSPL */ /* Variables and prototypes */ _PROTOTYP(static int doymdir,(int)); _PROTOTYP(static int renameone,(char *,char *, int,int,int,int,int,int,int,int,int,int,int)); #ifdef NETCONN extern int nnetdir; /* How many network directories */ #endif /* NETCONN */ #ifdef CK_SECURITY _PROTOTYP(int ck_krb4_is_installed,(void)); _PROTOTYP(int ck_krb5_is_installed,(void)); _PROTOTYP(int ck_ntlm_is_installed,(void)); _PROTOTYP(int ck_srp_is_installed,(void)); _PROTOTYP(int ck_ssleay_is_installed,(void)); _PROTOTYP(int ck_ssh_is_installed,(void)); _PROTOTYP(int ck_crypt_is_installed,(void)); #else #define ck_krb4_is_installed() (0) #define ck_krb5_is_installed() (0) #define ck_ntlm_is_installed() (0) #define ck_srp_is_installed() (0) #define ck_ssleay_is_installed() (0) #define ck_ssh_is_installed() (0) #define ck_crypt_is_installed() (0) #endif /* CK_SECURITY */ #define AV_KRB4 1 #define AV_KRB5 2 #define AV_NTLM 3 #define AV_SRP 4 #define AV_SSL 5 #define AV_CRYPTO 6 #define AV_SSH 7 struct keytab availtab[] = { /* Available authentication types */ { "crypto", AV_CRYPTO, CM_INV }, /* and encryption */ { "encryption", AV_CRYPTO, 0 }, { "k4", AV_KRB4, CM_INV }, { "k5", AV_KRB5, CM_INV }, { "kerberos4", AV_KRB4, 0 }, { "kerberos5", AV_KRB5, 0 }, { "krb4", AV_KRB4, CM_INV }, { "krb5", AV_KRB5, CM_INV }, { "ntlm", AV_NTLM, 0 }, { "srp", AV_SRP, 0 }, { "ssh", AV_SSH, 0 }, { "ssl", AV_SSL, 0 }, { "tls", AV_SSL, 0 }, { "", 0, 0 } }; int availtabn = sizeof(availtab)/sizeof(struct keytab)-1; #ifndef NODIAL _PROTOTYP(static int ddcvt, (char *, FILE *, int) ); _PROTOTYP(static int dncvt, (int, int, int, int) ); _PROTOTYP(char * getdname, (void) ); static int partial = 0; /* For partial dial */ static char *dscopy = NULL; int dialtype = -1; char *dialnum = (char *)0; /* Remember DIAL number for REDIAL */ int dirline = 0; /* Dial directory line number */ extern char * dialdir[]; /* Dial directory file names */ extern int dialdpy; /* DIAL DISPLAY on/off */ extern int ndialdir; /* How many dial directories */ extern int ntollfree; /* Toll-free call info */ extern int ndialpxx; /* List of PBX exchanges */ extern char *dialtfc[]; char * matchpxx = NULL; /* PBX exchange that matched */ extern int nlocalac; /* Local area-code list */ extern char * diallcac[]; extern int tttapi; #ifdef CK_TAPI extern int tapiconv; /* TAPI Conversions */ extern int tapipass; /* TAPI Passthrough */ #endif /* CK_TAPI */ extern int dialatmo; extern char * dialnpr, * dialsfx; extern char * dialixp, * dialixs, * dialmac; extern char * dialldp, * diallds, * dialtfp; extern char * dialpxi, * dialpxo, * diallac; extern char * diallcp, * diallcs, * diallcc; extern char * dialpxx[]; extern int dialcnf; /* DIAL CONFIRMATION */ int dialfld = 0; /* DIAL FORCE-LONG-DISTANCE */ int dialsrt = 1; /* DIAL SORT ON */ int dialrstr = 6; /* DIAL RESTRICTION */ int dialtest = 0; /* DIAL TEST */ int dialcount = 0; /* \v(dialcount) */ extern int dialsta; /* Dial status */ int dialrtr = -1, /* Dial retries */ dialint = 10; /* Dial retry interval */ extern long dialcapas; /* Modem capabilities */ extern int dialcvt; /* DIAL CONVERT-DIRECTORY */ #endif /* NODIAL */ #ifndef NOSPL #define IFCONDLEN 256 int ifc, /* IF case */ not = 0, /* Flag for IF NOT */ ifargs = 0; /* Count of IF condition words */ char ifcond[IFCONDLEN]; /* IF condition text */ char *ifcp; /* Pointer to IF condition text */ extern int vareval; #ifdef DCMDBUF extern int *ifcmd, *count, *iftest, *intime, *inpcas, *takerr, *merror, *xquiet, *xvarev; #else extern int ifcmd[]; /* Last command was IF */ extern int iftest[]; /* Last IF was true */ extern int count[]; /* For IF COUNT, one for each cmdlvl */ extern int intime[]; /* Ditto for other stackables... */ extern int inpcas[]; extern int takerr[]; extern int merror[]; extern int xquiet[]; extern int xvarev[]; #endif /* DCMDBUF */ #else extern int takerr[]; #endif /* NOSPL */ #ifdef DCMDBUF extern char *line; /* Character buffer for anything */ extern char *tmpbuf; #else extern char line[], tmpbuf[]; #endif /* DCMDBUF */ extern char *lp; /* Pointer to line buffer */ int cwdf = 0; /* CWD has been done */ /* Flags for ENABLE/DISABLE */ extern int en_cwd, en_cpy, en_del, en_dir, en_fin, en_get, en_hos, en_ren, en_sen, en_set, en_spa, en_typ, en_who, en_bye, en_asg, en_que, en_ret, en_mai, en_pri, en_mkd, en_rmd, en_xit, en_ena; extern FILE *tfile[]; /* File pointers for TAKE command */ extern char *tfnam[]; /* Names of TAKE files */ extern int tfline[]; /* TAKE-file line number */ extern int success; /* Command success/failure flag */ extern int cmdlvl; /* Current position in command stack */ #ifndef NOSPL extern int maclvl; /* Macro to execute */ extern char *macx[]; /* Index of current macro */ extern char *mrval[]; /* Macro return value */ extern char *macp[]; /* Pointer to macro */ extern int macargc[]; /* ARGC from macro invocation */ #ifdef COMMENT extern char *m_line[]; #endif /* COMMENT */ extern char *m_arg[MACLEVEL][NARGS]; /* Stack of macro arguments */ extern char *g_var[]; /* Global variables %a, %b, etc */ #ifdef DCMDBUF extern struct cmdptr *cmdstk; /* The command stack itself */ #else extern struct cmdptr cmdstk[]; /* The command stack itself */ #endif /* DCMDBUF */ #endif /* NOSPL */ #define xsystem(s) zsyscmd(s) static int x, y, z = 0; static char *s, *p; #ifdef OS2 _PROTOTYP( int os2settitle, (char *, int) ); #endif /* OS2 */ extern struct keytab yesno[], onoff[], fntab[]; extern int nyesno, nfntab; #ifndef NOSPL /* Do the ASK, ASKQ, GETOK, and READ commands */ int asktimedout = 0; #define ASK_PUP 1 #define ASK_TMO 2 #define ASK_GUI 3 #define ASK_QUI 4 #define ASK_DEF 5 #define ASK_ECH 6 static struct keytab asktab[] = { { "/default", ASK_DEF, CM_ARG }, { "/gui", ASK_GUI, #ifdef KUI 0 #else /* KUI */ CM_INV #endif /* KUI */ }, { "/popup", ASK_PUP, #ifdef OS2 0 #else /* OS2 */ CM_INV #endif /* OS2 */ }, { "/quiet", ASK_QUI, 0 }, { "/timeout", ASK_TMO, CM_ARG }, { "", 0, 0 } }; static int nasktab = sizeof(asktab)/sizeof(struct keytab)-1; static struct keytab askqtab[] = { { "/default", ASK_DEF, CM_ARG }, { "/echo", ASK_ECH, CM_ARG }, { "/gui", ASK_GUI, #ifdef KUI 0 #else /* KUI */ CM_INV #endif /* KUI */ }, { "/noecho", ASK_QUI, CM_INV }, { "/popup", ASK_PUP, #ifdef OS2 0 #else /* OS2 */ CM_INV #endif /* OS2 */ }, { "/quiet", ASK_QUI, 0 }, { "/timeout", ASK_TMO, CM_ARG }, { "", 0, 0 } }; static int naskqtab = sizeof(askqtab)/sizeof(struct keytab)-1; int doask(cx) int cx; { extern int asktimer, timelimit; #ifdef CK_RECALL extern int on_recall; #endif /* CK_RECALL */ int echochar = 0; int popupflg = 0; int guiflg = 0; int nomsg = 0; int mytimer = 0; #ifdef CK_APC extern int apcactive, apcstatus; #endif /* CK_APC */ char dfbuf[1024]; /* Buffer for default answer */ char * dfanswer = NULL; /* Pointer to it */ char vnambuf[VNAML+1]; /* Buffer for variable names */ char *vnp = NULL; /* Pointer to same */ dfbuf[0] = NUL; vnambuf[0] = NUL; #ifdef CK_APC if ( apcactive != APC_INACTIVE && (apcstatus & APC_NOINP) ) { return(success = 0); } #endif /* CK_APC */ mytimer = asktimer; /* Inherit global ASK timer */ echostars = 0; /* For ASKQ */ if (cx == XXASK || cx == XXASKQ) { struct FDB sw, fl; int getval; char c; if (cx == XXASKQ) /* Don't log ASKQ response */ debok = 0; cmfdbi(&sw, /* First FDB - command switches */ _CMKEY, /* fcode */ "Variable name or switch", "", /* default */ "", /* addtl string data */ ((cx == XXASK) ? nasktab : naskqtab), /* Table size */ 4, /* addtl numeric data 2: 4 = cmswi */ xxstring, /* Processing function */ ((cx == XXASK) ? asktab : askqtab), /* Keyword table */ &fl /* Pointer to next FDB */ ); cmfdbi(&fl, /* Anything that doesn't match */ _CMFLD, /* fcode */ "", /* hlpmsg */ "", /* default */ "", /* addtl string data */ 0, /* addtl numeric data 1 */ 0, /* addtl numeric data 2 */ NULL, NULL, NULL ); while (1) { /* Parse 0 or more switches */ x = cmfdb(&sw); /* Parse something */ if (x < 0) return(x); if (cmresult.fcode != _CMKEY) /* Break out if not a switch */ break; c = cmgbrk(); if ((getval = (c == ':' || c == '=')) && !(cmgkwflgs() & CM_ARG)) { printf("?This switch does not take an argument\n"); return(-9); } if (!getval && (cmgkwflgs() & CM_ARG)) { printf("?This switch requires an argument\n"); return(-9); } switch (cmresult.nresult) { case ASK_QUI: nomsg = 1; if (cx == XXASKQ) echostars = 0; break; case ASK_PUP: popupflg = 1; break; case ASK_GUI: guiflg = 1; break; case ASK_TMO: { if ((y = cmnum("seconds","1",10,&x,xxstring)) < 0) return(y); if (x < 0) x = 0; mytimer = x; break; } case ASK_ECH: { if ((y = cmfld("Character to echo","*",&s,xxstring)) < 0) return(y); echochar = *s; break; } case ASK_DEF: { if ((y = cmfld("Text to supply if reply is empty", "",&s,xxstring)) < 0) return(y); ckstrncpy(dfbuf,s,1024); dfanswer = dfbuf; break; } default: return(-2); } } /* Have variable name, make copy. */ ckstrncpy(vnambuf,cmresult.sresult,VNAML); vnp = vnambuf; if (vnambuf[0] == CMDQ && (vnambuf[1] == '%' || vnambuf[1] == '&')) vnp++; y = 0; if (*vnp == '%' || *vnp == '&') { if ((y = parsevar(vnp,&x,&z)) < 0) return(y); } } else if (cx != XXGOK && cx != XXRDBL) { /* Get variable name */ if ((y = cmfld("Variable name","",&s,NULL)) < 0) { if (y == -3) { printf("?Variable name required\n"); return(-9); } else return(y); } ckstrncpy(vnambuf,s,VNAML); /* Make a copy. */ vnp = vnambuf; if (vnambuf[0] == CMDQ && (vnambuf[1] == '%' || vnambuf[1] == '&')) vnp++; y = 0; if (*vnp == '%' || *vnp == '&') { if ((y = parsevar(vnp,&x,&z)) < 0) return(y); } } if (cx == XXREA || cx == XXRDBL) { /* READ or READBLOCK command */ if ((y = cmcfm()) < 0) /* Get confirmation */ return(y); if (chkfn(ZRFILE) < 1) { /* File open? */ printf("?Read file not open\n"); return(success = 0); } if (!(s = (char *)readbuf)) { /* Where to read into. */ printf("?Oops, no READ buffer!\n"); return(success = 0); } y = zsinl(ZRFILE, s, readblock); /* Read a line. */ debug(F111,"read zsinl",s,y); if (y < 0) { /* On EOF or other error, */ zclose(ZRFILE); /* close the file, */ delmac(vnp,0); /* delete the variable, */ return(success = 0); /* and return failure. */ } else { /* Read was OK. */ readsize = (int) strlen(s); success = (addmac(vnp,s) < 0 ? 0 : 1); /* Define variable */ debug(F111,"read addmac",vnp,success); return(success); /* Return success. */ } } /* ASK, ASKQ, GETOK, or GETC */ if (cx == XXGOK) { /* GETOK can take switches */ struct FDB sw, fl; int getval; char c; cmfdbi(&sw, /* First FDB - command switches */ _CMKEY, /* fcode */ "Variable name or question prompt", "", /* default */ "", /* addtl string data */ nasktab, /* addtl numeric data 1: tbl size */ 4, /* addtl numeric data 2: 4 = cmswi */ xxstring, /* Processing function */ asktab, /* Keyword table */ &fl /* Pointer to next FDB */ ); cmfdbi(&fl, /* Anything that doesn't match */ _CMTXT, /* fcode */ "", /* hlpmsg */ "", /* default */ "", /* addtl string data */ 0, /* addtl numeric data 1 */ 0, /* addtl numeric data 2 */ NULL, NULL, NULL ); while (1) { /* Parse 0 or more switches */ x = cmfdb(&sw); /* Parse something */ if (x < 0) return(x); if (cmresult.fcode != _CMKEY) /* Break out if not a switch */ break; c = cmgbrk(); if ((getval = (c == ':' || c == '=')) && !(cmgkwflgs() & CM_ARG)) { printf("?This switch does not take an argument\n"); return(-9); } if (!getval && (cmgkwflgs() & CM_ARG)) { printf("?This switch requires an argument\n"); return(-9); } switch (cmresult.nresult) { case ASK_PUP: popupflg = 1; break; case ASK_GUI: guiflg = 1; break; case ASK_TMO: { if ((y = cmnum("seconds","1",10,&x,xxstring)) < 0) return(y); if (x < 0) x = 0; mytimer = x; break; } case ASK_DEF: { if ((y = cmfld("Text to supply if reply is empty", "",&s,xxstring)) < 0) return(y); ckstrncpy(dfbuf,s,1024); dfanswer = dfbuf; break; } case ASK_QUI: nomsg = 1; break; default: return(-2); } } p = cmresult.sresult; } else if ((y = cmtxt( "Prompt,\n\ enclose in { braces } or \" quotes \" to preserve leading and trailing\n\ spaces, precede question mark with backslash (\\).", "",&p,xxstring)) < 0) return(y); if (!p) p = ""; #ifndef NOLOCAL #ifdef OS2 if (popupflg) { /* Popup requested */ int len = -1; ckstrncpy(tmpbuf,brstrip(p),TMPBUFSIZ); p = tmpbuf; if (cx == XXASK || cx == XXASKQ) { if (cx == XXASK) len = popup_readtext(vmode,NULL,p,line,LINBUFSIZ,mytimer); else len = popup_readpass(vmode,NULL,p,line,LINBUFSIZ,mytimer); asktimedout = ( len < 0 && mytimer ); } else if (cx == XXGOK) { printf("?Sorry, GETOK /POPUP not implemented yet\n"); timelimit = 0; return(-9); } if (len >= 0) { y = addmac(vnp,(char *)line); /* Add it to the macro table. */ } else if ( asktimedout && dfanswer ) { y = addmac(vnp,dfanswer); /* Add it to the macro table. */ asktimedout = 0; len = 0; } timelimit = 0; return(success = ((len >= 0) && (y >= 0)) && !asktimedout); } #ifdef KUI if (guiflg) { /* GUI requested */ int rc, n; char * s1; s1 = tmpbuf; n = TMPBUFSIZ-1; zzstring(brstrip(p),&s1,&n); p = tmpbuf; if (cx == XXASK || cx == XXASKQ) { rc = gui_txt_dialog(NULL,p,(cx == XXASK), line,LINBUFSIZ,dfanswer,mytimer); asktimedout = (rc == -1 && mytimer); if (rc == 1) { y = addmac(vnp,(char *)line); /* Add it to the macro table. */ } else if ( asktimedout && dfanswer ) { y = addmac(vnp,dfanswer); /* Add default to macro table. */ asktimedout = 0; rc = 1; } timelimit = 0; return(success = (rc == 1 && (y >= 0)) && !asktimedout); } else if (cx == XXGOK) { int x; x = lookup(yesno,dfanswer,nyesno,NULL); if (x != 1) x = 2; rc = uq_ok(NULL, p, 3, NULL, x); return(success = (rc == 1)); } } #endif /* KUI */ #endif /* OS2 */ #endif /* NOLOCAL */ concb((char)escape); /* Enter CBREAK mode */ cmsavp(psave,PROMPTL); /* Save old prompt */ cmsetp(brstrip(p)); /* Make new prompt */ reprompt: if (cx == XXASKQ) { /* For ASKQ, */ cmini(0); /* no-echo mode. */ if (echochar) echostars = echochar; } else { /* For others, regular echoing. */ cmini(ckxech); echostars = 0; } askflag = 1; x = -1; /* This means to reparse. */ cmflgs = 0; if (pflag) prompt(xxstring); /* Issue prompt. */ asktimedout = 0; /* Handle timed responses. */ timelimit = mytimer; reparse: cmres(); if (cx == XXGOK) { /* GETOK */ #ifdef CK_RECALL on_recall = 0; #endif /* CK_RECALL */ askflag = 0; /* GETOK uses keyword table */ x = cmkey(yesno,nyesno,"",dfanswer,xxstring); if (x < 0) { /* Parse error */ if (x == -10) { char * ds; ds = dfanswer ? dfanswer : "No"; if (!nomsg) printf("?Timed out, assuming \"%s\"",ds); printf("\n"); asktimedout = 1; x = lookup(yesno,ds,nyesno,NULL); if (x != 1) x = 0; goto gokdone; } else if (x == -3) { /* No answer? */ printf("Please respond Yes or No\n"); /* Make them answer */ cmini(ckxech); goto reprompt; } else if (x == -1) { goto reparse; } else goto reprompt; } if (cmcfm() < 0) /* Get confirmation */ goto reparse; gokdone: askflag = 0; cmsetp(psave); /* Restore prompt */ #ifdef VMS if (cmdlvl > 0) /* In VMS and not at top level, */ conres(); /* restore console again. */ #endif /* VMS */ timelimit = 0; return(x); /* Return success or failure */ } else if (cx == XXGETC /* GETC */ #ifdef OS2 || cx == XXGETK /* or GETKEYCODE */ #endif /* OS2 */ ) { /* GETC */ char tmp[16]; conbin((char)escape); /* Put keyboard in raw mode */ #ifndef NOSETKEY #ifdef OS2 if (cx == XXGETK) { /* GETKEYCODE */ extern int os2gks; int t; t = os2gks; /* Turn off kverb recognition */ os2gks = 0; x = congks(timelimit); /* Read a key event, blocking */ os2gks = t; /* Put back kverb recognition */ } else /* GETC */ #endif /* OS2 */ #endif /* NOSETKEY */ { debug(F101,"GETC conchk","",conchk()); x = coninc(timelimit); /* Just read one character */ debug(F101,"GETC coninc","",x); } concb((char)escape); /* Put keyboard back in cbreak mode */ if (x > -1) { if (xcmdsrc == 0) printf("\r\n"); #ifdef OS2 if (cx == XXGETK) { /* GETKEYCODE */ sprintf(tmp,"%d",x); /* SAFE */ } else { #endif /* OS2 */ tmp[0] = (char) (x & 0xff); tmp[1] = NUL; #ifdef OS2 } #endif /* OS2 */ y = addmac(vnp,tmp); /* Add it to the macro table. */ debug(F111,"getc/getk addmac",vnp,y); } else y = -1; cmsetp(psave); /* Restore old prompt. */ if (x < -1) { asktimedout = 1; if (!quiet && !nomsg) printf("?Timed out"); printf("\n"); } timelimit = 0; return(success = ((y < 0 ? 0 : 1) && (asktimedout == 0))); } else { /* ASK or ASKQ */ #ifdef CK_RECALL on_recall = 0; /* Don't put response in recall buf */ #endif /* CK_RECALL */ askflag = 1; /* ASK[Q] always goes to terminal */ y = cmdgquo(); /* Get current quoting */ cmdsquo(0); /* Turn off quoting */ while (x == -1) { /* Prompt till they answer */ x = cmtxt("Please respond.",dfanswer,&s,NULL); debug(F111,"ASK cmtxt",s,x); cmres(); } cmdsquo(y); /* Restore previous quoting */ if (cx == XXASKQ) /* ASKQ must echo CRLF here */ printf("\r\n"); if (x == -10 && dfanswer) { /* Don't fail on timeout if */ s = dfanswer; /* a default was specified */ asktimedout = 0; /* and don't fail */ x = 0; } if (x < 0) { /* If cmtxt parse error, */ cmsetp(psave); /* restore original prompt */ #ifdef VMS if (cmdlvl > 0) /* In VMS and not at top level, */ conres(); /* restore console again. */ #endif /* VMS */ if (x == -10) { /* Timed out with no response */ if (!nomsg) printf("?Timed out"); printf("\n"); asktimedout = 1; if (dfanswer) /* Supply default answer if any */ s = dfanswer; success = x = 0; /* (was "x = -9;") */ } timelimit = 0; return(x); /* and return cmtxt's error code. */ } if (!s || *s == NUL) { /* If user typed a bare CR, */ cmsetp(psave); /* Restore old prompt, */ delmac(vnp,0); /* delete variable if it exists, */ #ifdef VMS if (cmdlvl > 0) /* In VMS and not at top level, */ conres(); /* restore console again. */ #endif /* VMS */ timelimit = 0; return(success = 1); /* and return. */ } y = addmac(vnp,s); /* Add it to the macro table. */ debug(F111,"ask addmac",vnp,y); cmsetp(psave); /* Restore old prompt. */ #ifdef VMS if (cmdlvl > 0) /* In VMS and not at top level, */ conres(); /* restore console again. */ #endif /* VMS */ timelimit = 0; return(success = (y < 0 ? 0 : 1) && (asktimedout == 0)); } } #endif /* NOSPL */ #ifndef NOSPL int doincr(cx) int cx; { /* INCREMENT, DECREMENT */ char vnambuf[VNAML+1]; /* Buffer for variable names */ int eval = 0; CK_OFF_T x; eval = (cx == XX_DECR || cx == XX_INCR); if ((y = cmfld("Variable name","",&s, eval ? xxstring : NULL)) < 0) { if (y == -3) { printf("?Variable name required\n"); return(-9); } else return(y); } ckstrncpy(vnambuf,s,VNAML); if ((y = cmnumw("by amount","1",10,&x,xxstring)) < 0) return(y); if ((y = cmcfm()) < 0) return(y); z = (cx == XX_INCR || cx == XXINC) ? 1 : 0; /* Increment or decrement? */ if (incvar(vnambuf,x,z) < 0) { printf("?Variable %s not defined or not numeric\n",vnambuf); return(success = 0); } return(success = 1); } /* Used by doundef() */ static int xxundef(s,verbose,simulate) char * s; int verbose, simulate; { int rc = 0; if (!s) return(0); if (*s == CMDQ && *(s+1) == '%') { char c = *(s+2), * p = NULL; if (c >= '0' && c <= '9') { if (maclvl < 0) p = g_var[c]; else p = m_arg[maclvl][c - '0']; } else { if (isupper(c)) c += ('a'-'A'); if (c >= 'a' && c <= 'z') p = g_var[c]; } if (!p) return(-1); } if (verbose) printf(" %s ",s); if (simulate) { printf("(SELECTED)\n"); } else if ((x = delmac(s,1)) > -1) { /* Full name required */ rc = 1; if (verbose) printf("(OK)\n"); } else if (verbose) printf("(FAILED)\n"); return(rc); } /* Do the (_)DEFINE, (_)ASSIGN, and UNDEFINE commands */ #define UND_MAT 1 #define UND_VRB 2 #define UND_EXC 3 #define UND_SIM 3 static struct keytab undefswi[] = { { "/list", UND_VRB, 0 }, #ifdef COMMENT { "/except", UND_EXC, CM_ARG }, #endif /* COMMENT */ { "/matching", UND_MAT, 0 }, { "/simulate", UND_SIM, 0 }, { "/verbose", UND_VRB, CM_INV } }; static int nundefswi = sizeof(undefswi) / sizeof(struct keytab); #define UNDEFMAX 64 static char ** undeflist = NULL; int doundef(cx) int cx; { /* UNDEF, _UNDEF */ int i, j, n, rc = 0, arraymsg = 0; int domatch = 0, verbose = 0, errors = 0, simulate = 0, flag = 0; char *vnp, vnbuf[4]; #ifdef COMMENT char *except = NULL; #endif /* COMMENT */ struct FDB sw, fl; int getval; char c; if (!undeflist) { /* Allocate list if necessary */ undeflist = (char **)malloc(UNDEFMAX * sizeof(char *)); if (!undeflist) { printf("?Memory allocation failure\n"); return(-9); } for (i = 0; i < UNDEFMAX; i++) undeflist[i] = NULL; } cmfdbi(&sw, /* First FDB - command switches */ _CMKEY, /* fcode */ "Variable name or switch", "", /* default */ "", /* addtl string data */ nundefswi, /* addtl numeric data 1: tbl size */ 4, /* addtl numeric data 2: 4 = cmswi */ xxstring, /* Processing function */ undefswi, /* Keyword table */ &fl /* Pointer to next FDB */ ); cmfdbi(&fl, /* Anything that doesn't match */ _CMFLD, /* fcode */ "", /* hlpmsg */ "", /* default */ "", /* addtl string data */ 0, /* addtl numeric data 1 */ 0, /* addtl numeric data 2 */ (cx == XXUNDEF) ? NULL : xxstring, NULL, NULL ); while (1) { /* Parse 0 or more switches */ x = cmfdb(&sw); /* Parse something */ if (x < 0) return(x); if (cmresult.fcode != _CMKEY) /* Break out if not a switch */ break; c = cmgbrk(); if ((getval = (c == ':' || c == '=')) && !(cmgkwflgs() & CM_ARG)) { printf("?This switch does not take an argument\n"); return(-9); } switch (cmresult.nresult) { case UND_MAT: domatch = 1; break; case UND_SIM: simulate = 1; /* fall thru on purpose */ case UND_VRB: verbose = 1; break; #ifdef COMMENT case UND_EXC: if (!getval) break; if ((x = cmfld("Pattern","",&s,xxstring)) < 0) { if (x == -3) { printf("?Pattern required\n"); x = -9; } goto xgetx; } makestr(&except,cmresult.sresult); break; #endif /* COMMENT */ default: return(-2); } } n = 0; makestr(&(undeflist[n++]),cmresult.sresult); for (i = 1; i < UNDEFMAX; i++) { x = cmfld("Macro or variable name","",&s, ((cx == XXUNDEF) ? NULL : xxstring) ); if (x == -3) { if ((y = cmcfm()) < 0) return(y); break; } else if (y < 0) { return(y); } makestr(&(undeflist[n++]),s); } /* Now we have a list of n variables or patterns to undefine */ for (i = 0; i < n; i++) { flag = 0; if (!(vnp = undeflist[i])) continue; if (vnp[0] == CMDQ && (vnp[1] == '%' || vnp[1] == '&')) { flag++; vnp++; } if (!domatch) { /* Pattern match not requested */ if (flag) { if ((y = parsevar(vnp,&x,&z)) < 0) { vnp--; if (verbose) printf(" %s...error\n",vnp); continue; } vnp--; } x = xxundef(vnp,verbose,simulate); if (x > -1) { if (!x && !simulate) errors++; rc += x; } continue; } /* Pattern match requested */ if (!flag) { /* It's a macro */ for (j = 0; j < nmac; j++) { if (ckmatch(vnp,mactab[j].kwd,0,1)) { x = xxundef(mactab[j].kwd,verbose,simulate); if (x > -1) { rc += x; if (!x) errors++; } if (!simulate) j--; /* Because mactab shifted up */ } } } else if (vnp[0] == '%') { /* It's a \%x variable */ vnbuf[0] = CMDQ; vnbuf[1] = '%'; vnbuf[3] = NUL; for (j = '0'; j <= 'z'; j++) { /* 0..9 a..z */ vnbuf[2] = j; if (ckmatch(vnp,&vnbuf[1],0,1)) { x = xxundef(vnbuf,verbose,simulate); if (x > -1) { if (!x) errors++; rc += x; } } if (j == '9') j = (int)'a' - 1; /* 9 -> a */ } } else if (vnp[0] == '&') { if (!arraymsg && !quiet) { printf("?UNDEFINE /MATCH can't be used with arrays.\n"); printf("(Type HELP ARRAY to see other methods.)\n"); } arraymsg++; errors++; } } if (verbose) printf("undefined: %d, errors: %d\n",rc,errors); for (i = 0; i < UNDEFMAX; i++) { /* Check them all */ if (undeflist[i]) { /* in case we were interrupted */ free(undeflist[i]); /* previously... */ undeflist[i] = NULL; } } return(success = (errors == 0) ? 1 : 0); } int dodef(cx) int cx; { extern int xxdot; extern char ppvnambuf[]; int doeval = 0; char vnambuf[VNAML+1]; /* Buffer for variable names */ char *vnp; /* Pointer to same */ int k, mydot; mydot = xxdot; /* Copy */ xxdot = 0; /* and reset */ /* In case we got here from a command that begins like ".\%a", cmkey() has already evaluated \%a, but we don't want that, so we retrieve the variable name from a special pre-evaluation buffer in the command module, and we undo the "unget word" that would be done because of the token, because if the variable was defined, it will unget its value rather than its name. */ s = NULL; if (mydot && ppvnambuf[0] == '.' && ppvnambuf[1]) { s = ppvnambuf+1; unungw(); } if (!s) { if (cx == XXDFX || cx == XXASX) /* Evaluate variable name */ y = cmfld("Macro or variable name","",&s,xxstring); else /* Don't evaluate the variable name */ y = cmfld("Macro or variable name","",&s,NULL); if (y < 0) { if (y == -3) { printf("?Variable name required\n"); return(-9); } else return(y); } } k = strlen(s); if (k > VNAML) { printf("?Name too long: \"%s\"\n",s); return(-9); } ckstrncpy(vnambuf,s,VNAML); vnambuf[VNAML] = NUL; vnp = vnambuf; if (vnambuf[0] == CMDQ && (vnambuf[1] == '%' || vnambuf[1] == '&')) vnp++; if (*vnp == '%' || *vnp == '&') { if ((y = parsevar(vnp,&x,&z)) < 0) return(y); #ifdef COMMENT if (cx == XXUNDEF) { /* Undefine */ if ((y = cmtxt("Text to be ignored","",&s,NULL)) < 0) return(y); delmac(vnp,0); return(success = 1); } #endif /* COMMENT */ debug(F101,"dodef parsevar x","",x); if (mydot) { if ((doeval = cmkey(asgtab,nasgtab,"operator","=",NULL)) < 0) return(doeval); if (doeval > 0) /* Type of assignment */ cx = XXASS; } if (y == 1) { /* Simple variable */ if ((y = cmtxt("Definition of variable","",&s,NULL)) < 0) return(y); s = brstrip(s); debug(F110,"xxdef var name",vnp,0); debug(F110,"xxdef var def",s,0); } else if (y == 2) { /* Array element */ if ((y = arraynam(vnp,&x,&z)) < 0) return(y); if (x == 96) { printf("?Argument vector array is read-only\n"); return(-9); } if (chkarray(x,z) < 0) return(-2); if ((y = cmtxt("Definition of array element","",&s,NULL)) < 0) return(y); debug(F110,"xxdef array ref",vnp,0); debug(F110,"xxdef array def",s,0); } } else { /* Macro */ #ifdef COMMENT if (cx == XXUNDEF) { /* Undefine */ if ((y = cmtxt("Text to be ignored","",&s,NULL)) < 0) return(y); delmac(vnp,0); return(success = 1); } #endif /* COMMENT */ if (mydot) { if ((doeval = cmkey(asgtab,nasgtab,"operator","=",NULL)) < 0) return(doeval); if (doeval > 0) cx = XXASS; } if ((y = cmtxt("Definition of macro","",&s,NULL)) < 0) return(y); #ifdef DEBUG if (deblog) { debug(F110,"xxdef macro name",vnp,0); debug(F010,"xxdef macro def",s,0); } #endif /* DEBUG */ s = brstrip(s); } if (*s == NUL) { /* No arg given, undefine */ delmac(vnp,1); /* silently... */ return(success = 1); /* even if it doesn't exist... */ } /* Defining a new macro or variable */ if (cx == XXASS || cx == XXASX) { /* ASSIGN rather than DEFINE? */ int t; t = LINBUFSIZ-1; lp = line; /* If so, expand its value now */ zzstring(s,&lp,&t); s = line; } if (doeval == 2) { /* Arithmetic evaluation wanted too? */ ckstrncpy(line,evala(s),LINBUFSIZ); line[LINBUFSIZ] = NUL; } /* debug(F111,"calling addmac",s,(int)strlen(s)); */ y = addmac(vnp,s); /* Add it to the appropriate table. */ if (y < 0) { printf("?%s failed\n",(cx == XXASS || cx == XXASX) ? "ASSIGN" : "DEFINE"); return(success = 0); } else if (cx == XXASX || cx == XXDFX) /* For _ASG or _DEF, */ return(1); /* don't change success variable */ else return(success = 1); } #endif /* NOSPL */ #ifndef NODIAL /* L U D I A L -- Lookup up dialing directory entry. Call with string to look up and file descriptor of open dialing directory file. On success, returns number of matches found, with numbers stored in an array accessible via getdnum(). */ static char *dn_p[MAXDNUMS + 1]; /* Dial Number pointers */ static char *dn_p2[MAXDNUMS + 1]; /* Converted dial number pointers */ static int dn_x[MAXDNUMS + 1]; /* Type of call */ static int dncount = 0; char * d_name = NULL; /* Dial name pointer */ char * /* Get dial directory entry name */ getdname() { return(d_name ? d_name : ""); } char * getdnum(n) int n; { /* Get dial number n from directory */ if (n < 0 || n > dncount || n > MAXDNUMS) return(""); else return(dn_p[n]); } char * /* Check area code for spurious leading digit */ chk_ac(i,buf) int i; char buf[]; { char *p; if (!buf) return(""); p = (char *) buf; /* Country we are calling: */ if (i == 44 || /* UK */ i == 49 || /* Germany */ i == 39 || /* Italy */ i == 31 || /* Netherlands */ i == 351 || /* Portugal */ i == 55 || /* Brazil */ i == 972 || /* Israel */ i == 41 || /* Switzerland */ i == 43 || /* Austria */ i == 42 || /* Czech Republic */ i == 36 || /* Hungary */ i == 30 || /* Greece */ i == 352 || /* Luxembourg */ i == 48 || /* Poland */ i == 27 || /* South Africa */ i == 33 || /* France (as of 1997) */ i == 358 /* Finland (ditto) */ ) { if (buf[0] == '0') p++; } return(p); } /* Call Is Long Distance -- Expand this to cover 10-digit local dialing etc */ /* src = area code of caller dest = area code of callee Returns: 0 if call is local 1 if call is long distance 2 if call is local but area code must be dialed anyway */ static int callisld(src, dest) char * src, * dest; { int i; if (dialfld) /* Force long distance? */ return(1); if (!strcmp(src,dest)) { /* Area codes are the same */ for (i = 0; i < nlocalac; i++) /* Is AC in the lc-area-codes list? */ if (!strcmp(src,diallcac[i])) return(2); /* Yes so must be dialed */ return(0); /* No so don't dial it. */ } for (i = 0; i < nlocalac; i++) /* ACs not the same so look in list */ if (!strcmp(dest,diallcac[i])) /* Match */ return(2); /* So local call with area code */ return(1); /* Not local so long-distance */ } char pdsfx[64] = { NUL, NUL }; #ifndef NOSPL static char * xdial(s) char *s; { /* Run dial string thru macro */ int x, m; char * s2; s2 = NULL; makestr(&s2,s); /* Copy the argument */ if (!dialmac) /* Dial macro name given? */ return(NULL); if ((x = mxlook(mactab,dialmac,nmac)) < 0) /* Is the macro defined? */ return(NULL); m = maclvl; x = dodo(x,s2,0); /* Set up the macro */ if (s2) free(s2); if (x > 0) { while (maclvl > m) /* Execute the parser */ parser(1); return(mrval[maclvl+1]); /* Return the result */ } return(NULL); } #endif /* NOSPL */ static int dncvt(k,cx, prefix, suffix) int k, cx, prefix, suffix; { /* Dial Number Convert */ int i, j, n, what; /* cx is top-level command index */ char *ss; /* prefix - add prefixes? */ char *p, *p2, *pxo; /* suffix - add suffixes? */ char *lac; char *npr; char *sfx; /* char *psfx; */ char ccbuf[128]; int cc; char acbuf[24]; char *acptr; char outbuf[256]; /* First pass for strict (punctuation-based) interpretation. If it fails, we try the looser (length-based) one. */ dialtype = -1; what = 0; /* Type of call */ s = dn_p[k]; /* Number to be converted. */ debug(F111,"dncvt",s,k); if (dn_p2[k]) { free(dn_p2[k]); dn_p2[k] = NULL; } if (!s) { printf("Error - No phone number to convert\n"); return(-1); } if ((int)strlen(s) > 200) { ckstrncpy(outbuf,s,40); printf("?Too long: \"%s...\"\n",outbuf); return(-1); } npr = (prefix && dialnpr) ? dialnpr : ""; sfx = (suffix && dialsfx) ? dialsfx : ""; /* if (partial) psfx = dialsfx ? dialsfx : ""; */ pxo = (prefix && dialpxo) ? dialpxo : ""; lac = diallac ? diallac : ""; /* Local area code */ outbuf[0] = NUL; /* Initialize conversion buffer */ ss = s; /* Remember original string */ if (*s != '+') { /* Literal number */ dn_x[k] = DN_UNK; /* Sort key is "unknown". */ ckmakmsg(outbuf,256, /* Sandwich it between */ pxo,npr,s,sfx /* DIAL PREFIX and SUFFIX */ ); #ifdef CK_TAPI if (tttapi && /* TAPI does its own conversions */ !tapipass && /* if not in passthru mode */ tapiconv == CK_AUTO || /* and TAPI conversions are AUTO */ tapiconv == CK_ON /* OR if TAPI conversions are ON */ ) { char * p = NULL; dialtype = -2; if (!cktapiConvertPhoneNumber(dn_p[k], &p)) return(-1); makestr(&dn_p2[k], p); if (p) free(p); return(0); } else #endif /* CK_TAPI */ makestr(&dn_p2[k], outbuf); /* Not TAPI */ dialtype = what; return(0); /* Done. */ } i = 0; /* Portable number */ s++; /* Tiptoe past the plus sign */ ccbuf[0] = NUL; /* Do country code first */ if (!diallcc) { /* Do we know our own? */ if (cx != XXLOOK) printf("Error - prior SET DIAL COUNTRY-CODE command required\n"); return(-1); } /* Parse the number */ while (1) { /* Get the country code */ while (*s == HT || *s == SP) s++; if (!s) /* Not in standard format */ break; if (*s == '(') { /* Beginning of area code */ s++; /* Skip past parenthesis */ ccbuf[i] = NUL; /* End of country code */ if (!s) { /* Check for end of string */ printf("Error - phone number ends prematurely: \"%s\"\n",ss); return(-1); } break; } else { /* Collect country code */ if (isdigit(*s)) ccbuf[i++] = *s; /* copy this character */ s++; if (!*s || i > 127) /* watch out for memory leak */ break; } } cc = atoi(ccbuf); /* Numeric version of country code */ i = 0; /* Now get area code */ acbuf[0] = NUL; /* Initialize area-code buffer */ acptr = acbuf; /* and pointer. */ while (1) { while (*s == HT || *s == SP) /* Ignore whitespace */ s++; if (!s) /* String finished */ break; if (*s == ')') { /* End of area code */ s++; /* Skip past parenthesis */ acbuf[i] = NUL; /* Terminate area-code buffer */ break; } else { /* Part of area code */ if (isdigit(*s)) /* If it's a digit, */ acbuf[i++] = *s; /* copy this character */ s++; /* Point to next */ if (!*s || i > 23) /* Watch out for overflow */ break; } } /* Here we strip any leading 0 for countries that we know have 0 as a long-distance prefix and do not have any area codes that start with 0 (formerly also ditto for "9" in Finland...) */ i = atoi(ccbuf); acptr = chk_ac(i,acbuf); while (*s == HT || *s == SP) /* Skip whitespace */ s++; /* printf("S=[%s], ACPTR=[%s]\n",s,acptr); */ if (*s && *acptr) { /* Area code was delimited */ while (*s == '-' || *s == '.') /* Skip past gratuitious punctuation */ s++; if (!*s) s--; /* But not to end of string */ if (strcmp(diallcc,ccbuf)) { /* Out of country? */ if (!dialixp) { /* Need intl-prefix */ if (cx != XXLOOK) printf("Error - No international dialing prefix defined\n"); return(-1); } what = dn_x[k] = DN_INTL; p = (prefix && dialixp) ? dialixp : ""; /* Intl-prefix */ p2 = (suffix && dialixs) ? dialixs : ""; /* Intl-suffix */ /* Form the final phone number */ #ifdef COMMENT sprintf(pdsfx,"%s%s",p2,sfx); /* UNSAFE */ sprintf(outbuf, "%s%s%s%s%s%s%s%s", pxo,npr,p,ccbuf,acptr,s,p2,sfx ); #else ckmakmsg(pdsfx,64,p2,sfx,NULL,NULL); ckmakxmsg(outbuf,256,pxo,npr,p,ccbuf,acptr,s,p2,sfx, NULL,NULL,NULL,NULL); #endif /* COMMENT */ } else if ((x = callisld(lac,acptr)) >= 1) { /* In-country LD */ if (!diallac && cx != XXLOOK) { /* Don't know my own area code */ if (cc == 1) printf("WARNING - Prior SET DIAL AREA-CODE needed\n"); } if (x == 2) { /* Local call with area code */ what = dn_x[k] = DN_LOCAL; /* Local-call */ p = (prefix && diallcp) ? diallcp : ""; /* local-prefix */ p2 = (suffix && diallcs) ? diallcs : ""; /* local-suffix */ } else { what = dn_x[k] = DN_LONG; /* Long-distance */ for (i = 0; i < ntollfree; i++) { /* But toll-free too? */ if (!strcmp(acptr,dialtfc[i])) { what = dn_x[k] = DN_FREE; break; } } if (what == DN_FREE) { /* Toll-free call */ p = (prefix && dialtfp) ? dialtfp : ((prefix && dialldp) ? dialldp : ""); p2 = ""; /* no suffix */ } else { /* normal long distance */ p = (prefix && dialldp) ? dialldp : ""; /* ld-prefix */ p2 = (suffix && diallds) ? diallds : ""; /* ld-suffix */ } } /* Form the number to be dialed */ #ifdef COMMENT sprintf(outbuf,"%s%s%s%s%s%s%s", pxo,npr,p,acptr,s,p2,sfx ); sprintf(pdsfx,"%s%s",p2,sfx); #else ckmakxmsg(outbuf,256, pxo,npr,p,acptr,s,p2,sfx, NULL,NULL,NULL,NULL,NULL); ckmakmsg(pdsfx,64,p2,sfx,NULL,NULL); #endif /* COMMENT */ } else { /* Same country, same area code */ what = dn_x[k] = DN_LOCAL; /* So it's a local call. */ if (!prefix || !(dialpxo || ndialpxx)) { /* Not dialing from PBX */ p = (prefix && diallcp) ? diallcp : ""; /* local-prefix */ p2 = (suffix && diallcs) ? diallcs : ""; /* local-suffix */ #ifdef COMMENT if (x == 2) sprintf(outbuf,"%s%s%s%s%s%s",npr,p,acptr,s,p2,sfx); else sprintf(outbuf,"%s%s%s%s%s",npr,p,s,p2,sfx); sprintf(pdsfx,"%s%s",p2,sfx); #else if (x == 2) ckmakxmsg(outbuf,256, npr,p,acptr,s,p2,sfx, NULL,NULL,NULL,NULL,NULL,NULL); else ckmakxmsg(outbuf,256, npr,p,s,p2,sfx, NULL,NULL,NULL,NULL,NULL,NULL,NULL); ckmakmsg(pdsfx,64,p2,sfx,NULL,NULL); #endif /* COMMENT */ } else { /* Dialing from a PBX and not TAPI */ if (ndialpxx) { /* Is it internal? */ #ifdef COMMENT i = (int) strlen(dialpxx); j = (int) strlen(s); x = -1; if (j > i) x = ckstrcmp(dialpxx,s,i,0); #else int kx; x = -1; j = (int) strlen(s); for (kx = 0; kx < ndialpxx; kx++) { i = (int) strlen(dialpxx[kx]); if (j > i) if (!(x = ckstrcmp(dialpxx[kx],s,i,0))) break; } #endif /* COMMENT */ if (!x) { char * icp, buf[32]; makestr(&matchpxx,dialpxx[kx]); debug(F111,"dncvt matchpxx",matchpxx,kx); what = dn_x[kx] = DN_INTERN; /* Internal call. */ s += i; /* Internal-call prefix */ icp = dialpxi; #ifndef NOSPL if (icp) { if (*icp == '\\') { char c, *bp; int n; c = *(icp+1); if (isupper(c)) c = tolower(c); if (c == 'v' || c == 'f') { n = 32; bp = buf; zzstring(icp,&bp,&n); icp = buf; } } } #endif /* NOSPL */ p = (prefix && icp) ? icp : ""; #ifdef COMMENT sprintf(outbuf,"%s%s%s%s",npr,p,s,sfx); #else ckmakmsg(outbuf,256,npr,p,s,sfx); #endif /* COMMENT */ } else { /* External local call */ /* local-prefix */ p = (prefix && diallcp) ? diallcp : ""; /* local-suffix */ p2 = (prefix && diallcs) ? diallcs : ""; #ifdef COMMENT if (x == 2) sprintf(outbuf,"%s%s%s%s%s%s%s", dialpxo ? dialpxo : "", npr,p,acptr,s,p2,sfx); else sprintf(outbuf, "%s%s%s%s%s%s", dialpxo ? dialpxo : "", npr,p,s,p2,sfx ); #else if (x == 2) ckmakxmsg(outbuf, 256, dialpxo ? dialpxo : "", npr,p,acptr,s,p2,sfx, NULL,NULL,NULL,NULL,NULL); else ckmakxmsg(outbuf, 256, dialpxo ? dialpxo : "", npr,p,s,p2,sfx, NULL,NULL,NULL,NULL,NULL,NULL); #endif /* COMMENT */ } } } } } else { /* Area code was not delimited */ char xbuf[256]; /* Comparison based only on length */ char ybuf[256]; int x, j; s = ss; for (i = 0; i < 255; i++) { if (!*s) break; while (!isdigit(*s)) { /* Pay attention only to digits */ s++; if (!*s) break; } xbuf[i] = *s++; } xbuf[i] = NUL; x = 1; /* Assume LD */ n = 0; if (!dialfld) { /* If LD not forced */ for (j = 0; j < nlocalac; j++) { /* check local AC list? */ ckmakmsg(ybuf,256,diallcc,diallcac[j],NULL,NULL); n = (int) strlen(ybuf); if (n > 0 && !ckstrcmp(xbuf,ybuf,n,0)) { x = 2; break; } } if (x == 1) { /* Or exact match with local CC+AC? */ ckmakmsg(ybuf,256,diallcc,lac,NULL,NULL); n = (int) strlen(ybuf); if (n > 0 && !ckstrcmp(xbuf,ybuf,n,0)) x = 0; } } if (x == 0 || x == 2) { /* Local call */ int xx,kx; /* Begin 1 Dec 2001... */ /* Account for PBX internal calls */ if (ndialpxx) { xx = -1; j = (int) strlen(ybuf); for (kx = 0; kx < ndialpxx; kx++) { i = (int) strlen(dialpxx[kx]); if (j >= i) if (!(xx = ckstrcmp(dialpxx[kx],&xbuf[j],i,0))) break; } } if (!xx) { char * icp, buf[32]; makestr(&matchpxx,dialpxx[kx]); debug(F111,"dncvt matchpxx",matchpxx,kx); what = dn_x[kx] = DN_INTERN; /* Internal call. */ s = xbuf + j + i; icp = dialpxi; /* Internal-call prefix */ #ifndef NOSPL if (icp) { if (*icp == '\\') { char c, *bp; int n; c = *(icp+1); if (isupper(c)) c = tolower(c); if (c == 'v' || c == 'f') { n = 32; bp = buf; zzstring(icp,&bp,&n); icp = buf; } } } #endif /* NOSPL */ p = (prefix && icp) ? icp : ""; ckmakmsg(outbuf,256,npr,p,s,sfx); /* End 1 Dec 2001... */ } else { /* Not PBX internal */ dn_x[k] = DN_LOCAL; p = (prefix && diallcp) ? diallcp : ""; p2 = (suffix && diallcs) ? diallcs : ""; s = (char *) (xbuf + ((x == 0) ? n : (int)strlen(diallcc))); ckmakxmsg(outbuf,256, pxo,npr,p,s,p2,sfx, NULL,NULL,NULL,NULL,NULL,NULL); ckmakmsg(pdsfx,64,p2,sfx,NULL,NULL); } } else { /* Not local */ n = ckstrncpy(ybuf,diallcc,256); if (n > 0 && !ckstrcmp(xbuf,ybuf,n,0)) { /* Long distance */ dn_x[k] = DN_LONG; p = (prefix && dialldp) ? dialldp : ""; p2 = (suffix && diallds) ? diallds : ""; s = xbuf + n; while (*s == '-' || *s == '.') s++; #ifdef COMMENT sprintf(outbuf,"%s%s%s%s%s%s",pxo,npr,p,s,p2,sfx); sprintf(pdsfx,"%s%s",p2,sfx); #else ckmakxmsg(outbuf,256, pxo,npr,p,s,p2,sfx, NULL,NULL,NULL,NULL,NULL,NULL); ckmakmsg(pdsfx,64,p2,sfx,NULL,NULL); #endif /* COMMENT */ } else { dn_x[k] = DN_INTL; /* International */ if (!dialixp) { if (cx != XXLOOK) { printf( "Error - No international dialing prefix defined\n" ); return(-1); } } p = (prefix && dialixp) ? dialixp : ""; p2 = (suffix && dialixs) ? dialixs : ""; #ifdef COMMENT sprintf(outbuf,"%s%s%s%s%s%s",pxo,npr,p,xbuf,p2,sfx); sprintf(pdsfx,"%s%s",p2,sfx); #else ckmakxmsg(outbuf,256, pxo,npr,p,xbuf,p2,sfx, NULL,NULL,NULL,NULL,NULL,NULL); ckmakmsg(pdsfx,64,p2,sfx,NULL,NULL); #endif /* COMMENT */ } } } #ifdef CK_TAPI if (tttapi && /* TAPI performs the conversions */ !tapipass && tapiconv == CK_AUTO || tapiconv == CK_ON ) { p = NULL; dialtype = -2; if (!cktapiConvertPhoneNumber(dn_p[k],&p)) return(-1); makestr(&dn_p2[k], p); if (p) free(p); return(0); } else { #endif /* CK_TAPI */ makestr(&dn_p2[k], outbuf); #ifdef CK_TAPI } #endif /* CK_TAPI */ dialtype = what; return(0); } static int ddcvt(s, f, n) char * s; FILE * f; int n; { /* Dial Directory Convert */ char linebuf[1024], *s2; /* Buffers and pointers */ #ifdef VMS char * temp = NULL; #endif /* VMS */ char *info[8]; /* Pointers to words from entry */ FILE * f2 = NULL; int x, rc; rc = -1; debug(F110,"ddcvt file",s,0); if (!s || !f) /* No filename or file */ return(-1); if (!*s) fclose(f); znewn(s,&s2); /* s2 = address of static buffer */ debug(F110,"ddcvt newname",s2,0); #ifdef VMS /* In VMS, znewn() returns the same file name with a new version number */ makestr(&temp,s); /* Swap - otherwise the new */ s = s2; /* version has the older version */ s2 = temp; /* number... */ debug(F110,"ddcvt after swap s",s,0); debug(F110,"ddcvt after swap s2",s2,0); makestr(&(dialdir[n]),s); /* New file gets new version number */ debug(F110,"ddcvt after makestr s2",s2,0); debug(F111,"ddcvt dialdir[n]",dialdir[n],n); #else if (zrename(s,s2) < 0) { /* Not VMS - rename old file */ perror(s2); /* to new (wierd) name. */ goto ddexit; } #endif /* VMS */ debug(F110,"ddcvt s2 (old)",s2,0); if ((f = fopen(s2,"r")) == NULL) { /* Reopen old file with wierd name */ debug(F110,"ddcvt s2 open error",ck_errstr(),0); dirline = 0; /* (or in VMS, old version) */ perror(s2); goto ddexit; } debug(F110,"ddcvt fopen(s2) OK",s2,0); debug(F110,"ddcvt s (new)",s,0); if ((f2 = fopen(s,"w")) == NULL) { /* Create new file with old name */ debug(F110,"ddcvt s open error",ck_errstr(),0); perror(s); /* (or in VMS, new version) */ goto ddexit; } debug(F110,"ddcvt fopen(s) OK",s,0); printf("\nSaving old directory as %s.\nConverting %s...",s2,s); fprintf(f2,"; %s - Kermit dialing directory\n", s); fprintf(f2,"%-16s %-20s ; %5s %-6s ; %s\n", "; Name","Number","Speed","Parity","Comment" ); while (1) { linebuf[0] = NUL; /* Read a line */ if (fgets(linebuf,1023,f) == NULL) break; debug(F110,"ddcvt linebuf",linebuf,0); if (!linebuf[0]) { /* Empty line */ fprintf(f2,"\n"); continue; } x = (int) strlen(linebuf); /* Strip line terminator, */ while (x-- > 0) { /* if any. */ if (linebuf[x] <= SP) linebuf[x] = NUL; else break; } xwords(linebuf,5,info,1); /* Parse it the old way */ for (x = 1; x < 6; x++) if (!info[x]) info[x] = ""; fprintf(f2,"%-16s %-20s ; %5s %-6s %s\n", info[1],info[2],info[3],info[4],info[5] ); } printf(" OK\n\n"); rc = 0; /* Success */ ddexit: if (f) fclose(f); if (f2) fclose(f2); #ifdef VMS if (temp) free(temp); #endif /* VMS */ return(rc); } int /* s = name to look up */ #ifdef CK_ANSIC /* cx = index of command */ ludial(char *s, int cx) /* (DIAL, LOOKUP, etc) */ #else ludial(s, cx) char *s; int cx; #endif /* CK_ANSIC */ /* ludial */ { int dd, n1, n2, n3, i, j, t; /* Workers */ int olddir, newdir, oldentry, newentry; int pass = 0; int oldflg = 0; int ambiguous = 0; /* Flag for lookup was ambiguous */ char *info[7]; /* Pointers to words from entry */ char *pp; /* Pointer to element of array */ FILE * f; char *line; /* File input buffer */ /* #define LUDEBUG */ #ifdef LUDEBUG int zz = 1; #endif /* LUDEBUG */ if (!s || ndialdir < 1) /* Validate arguments */ return(-1); if ((n1 = (int) strlen(s)) < 1) /* Length of string to look up */ return(-1); if (!(line = malloc(1024))) /* Allocate input buffer */ return(-1); #ifdef LUDEBUG if (zz) printf("LUDIAL 1 s[%s], n1=%d\n",s,n1); #endif /* LUDEBUG */ pass = 0; lu_again: f = NULL; /* Dial directory file descriptor */ t = dncount = 0; /* Dial-number match count */ dd = 0; /* Directory counter */ olddir = 0; newdir = 0; /* We need to recognize both old- and new-style directories. But we can't allow old-style and new-style entries in the same directory because there is no way to tell for sure the difference between an old-style entry like this: foo 5551212 9600 and a new-style literal entry like this: foo 555 9600 I.e. is the "9600" a speed, or part of the phone number? */ while (1) { /* We make one pass */ if (!f) { /* Directory not open */ if (dd >= ndialdir) /* No directories left? */ break; /* Done. */ debug(F111,"ludial dialdir[dd]",dialdir[dd],dd); if ((f = fopen(dialdir[dd],"r")) == NULL) { /* Open it */ perror(dialdir[dd]); /* Can't, print message saying why */ if (line) { free(line); line = NULL; } dd++; /* Go on to next one, if any... */ continue; } dirline = 0; /* Directory file line number */ if (dialdpy && !pass) printf("Opening: %s...\n",dialdir[dd]); dd++; if (!oldflg) olddir = 0; newdir = 0; } oldentry = 0; newentry = 0; line[0] = NUL; if (getnct(line,1023,f,1) < 0) { /* Read a line */ if (f) { /* f can be clobbered! */ fclose(f); /* Close the file */ f = NULL; /* Indicate next one needs opening */ oldflg = 0; } continue; } if (!line[0]) /* Empty line */ continue; #ifdef LUDEBUG if (zz) printf("LUDIAL 2 s[%s]\n",s); #endif /* LUDEBUG */ /* Make a copy and parse it the old way */ /* A copy is needed because xwords() pokes NULs into the string */ if ((pp = malloc((int)strlen(line) + 1))) { strcpy(pp,line); /* safe */ xwords(pp,5,info,0); /* Parse it the old way */ #ifdef LUDEBUG if (zz) printf("LUDIAL 3 s[%s]\n",s); #endif /* LUDEBUG */ if (!info[1]) continue; if (*info[1] == ';') { /* If full-line comment, */ newdir = 1; /* (only new directories have them) */ continue; /* keep reading. */ } if (!info[2]) continue; if (*info[2] == '+') newentry = 1; if (info[4]) { if ((*info[4] == '=') || !ckstrcmp(info[4],"none", 4,0) || !ckstrcmp(info[4],"even", 4,0) || !ckstrcmp(info[4],"space",5,0) || !ckstrcmp(info[4],"mark", 4,0) || !ckstrcmp(info[4],"odd", 3,0) ) oldentry = 1; } } if (pp) { free(pp); pp = NULL; } /* Check consistency */ if ((oldentry || olddir) && (newentry || newdir)) { printf( "\nERROR: You seem to have old- and new-format entries mixed in your\n"); printf( "dialing directory. You'll have to edit it by hand to convert it to the\n"); #ifndef NOHELP printf("new format. Type HELP DIAL for further information.\n\n"); #else printf("new format.\n\n"); #endif /* NOHELP */ if (line) { free(line); line = NULL; } return(-1); } if (!olddir && oldentry) { int convert = 0; olddir = 1; if (dialcvt == 2) { /* 2 == ASK */ sprintf(tmpbuf, "WARNING: Old-style dialing directory detected:\n%s", line); convert = uq_ok(tmpbuf, "Shall I convert it for you? ",3,NULL,0); } else convert = dialcvt; if (convert) { debug(F111,"ludial calling ddcvt",dialdir[dd-1],dd); if (ddcvt(dialdir[dd-1],f,dd-1) < 0) { debug(F111,"ludial ddcvt failed",dialdir[dd-1],dd); oldflg = 1; printf( " Sorry, can't convert."); printf( " Will ignore speed and parity fields, continuing...\n\n"); } else { olddir = newdir = 0; debug(F111,"ludial ddcvt ok",dialdir[dd-1],dd); } dd--; f = NULL; continue; } else { if (dialcvt == 2) printf( " OK, will ignore speed and parity fields, continuing...\n\n"); olddir = 1; } } #ifdef LUDEBUG if (zz) printf("LUDIAL XX s[%s], n1=%d\n",s,n1); #endif /* LUDEBUG */ /* Now parse again for real */ if (oldentry) /* Parse it the old way */ xwords(line,5,info,0); else /* Parse it the new way */ xwords(line,2,info,1); #ifdef LUDEBUG if (zz) printf("LUDIAL YY s[%s], n1=%d\n",s,n1); if (zz) printf("%s [%s]\n",info[1],info[2]); #endif /* LUDEBUG */ if (info[1]) { /* First word is entry name */ if ((n3 = (int) strlen(info[1])) < 1) /* Its length */ continue; /* If no first word, keep reading. */ if (n3 < n1) /* Search name is longer */ continue; /* Can't possibly match */ if (ambiguous && n3 != n1) continue; #ifdef LUDEBUG if (zz) printf("MATCHING: [%s] [%s], n1=%d\n",s,info[1],n1); #endif /* LUDEBUG */ if (ckstrcmp(s,info[1],n1,0)) /* Caseless string comparison */ continue; #ifdef LUDEBUG if (zz) printf("MATCH OK: [%s] [%s], n1=%d\n",s,info[1],n1); #endif /* LUDEBUG */ if (!info[2]) /* No phone number given */ continue; if ((n2 = (int) strlen(info[2])) < 1) /* Length of phone number */ continue; /* Ignore empty phone numbers */ /* Got one */ if (!(pp = (char *)malloc(n2 + 1))) { /* Allocate storage for it */ printf("?internal error - ludial malloc 1\n"); if (line) { free(line); line = NULL; } dncount = 0; return(-1); } strcpy(pp,info[2]); /* safe */ if (dncount > MAXDNUMS) { printf("Warning: %d matches found, %d max\n", dncount, MAXDNUMS ); dncount = MAXDNUMS; break; } dn_p[dncount++] = pp; /* Add pointer to array. */ if (dncount == 1) { /* First one... */ if (d_name) free(d_name); if (!(d_name = (char *)malloc(n3 + 1))) { /* Save its name */ printf("?internal error - ludial malloc 2\n"); if (line) { free(line); line = NULL; } dncount = 0; return(-1); } t = n3; /* And its length */ strcpy(d_name,info[1]); /* safe */ } else { /* Second or subsequent one */ #ifdef LUDEBUG if (zz) printf("d_name=[%s],info[1]=%s,t=[%d]\n",d_name,info[1],t); #endif /* LUDEBUG */ if ((int) strlen(info[1]) == t) /* Lengths compare */ if (!ckstrcmp(d_name,info[1],t,0)) /* Caseless compare OK */ continue; /* Name given by user matches entries with different names */ if (ambiguous) /* Been here before */ break; ambiguous = 1; /* Now an exact match is required */ for (j = 0; j < dncount; j++) { /* Clean out previous list */ if (dn_p[j]) { free(dn_p[j]); dn_p[j] = NULL; } } pass++; /* Second pass... */ goto lu_again; /* Do it all over again. */ } } } if (line) free(line); if (dncount == 0 && ambiguous) { printf(" Lookup: \"%s\" - ambiguous%s\n", s, cx == XXLOOK ? "" : " - dialing skipped" ); return(-2); } return(dncount); } char * pncvt(s) char *s; { /* Phone number conversion */ char *p = NULL; /* (just a wrapper for dncvt() */ char *q = NULL; static char pnbuf[128]; makestr(&p,dn_p[0]); /* Save these in case they are */ makestr(&q,dn_p2[0]); /* being used */ makestr(&dn_p[0],s); /* Copy the argument string to here */ dncvt(0,XXLOOK,1,1); /* Convert it */ if (!dn_p2[0]) /* Put result where can return it */ pnbuf[0] = NUL; else ckstrncpy(pnbuf,dn_p2[0],127); makestr(&dn_p[0],p); /* Restore these */ makestr(&dn_p2[0],q); makestr(&p,NULL); /* Free these */ makestr(&q,NULL); return((char *)pnbuf); } int dodial(cx) int cx; { /* DIAL or REDIAL */ int i = 0, x = 0; /* Workers */ int sparity = -1; /* For saving global parity value */ int previous = 0; int len = 0; int literal = 0; int flowsave; int lufound = 0; /* Did any lookup succeed? */ int prefix = 1; int postfix = 1; int wasalpha = 0; int xredial = 0; int braces = 0; char *p = NULL, *s3 = NULL, * sav = NULL; int j = 0, t = 0, n = 0; int xretries, xlcc; #ifdef COMMENT debug(F101,"dodial cx","",cx); debug(F111,"dodial diallcc",diallcc,diallcc); #endif /* COMMENT */ xretries = dialrtr; /* If retries not set, */ if (diallcc) { /* choose default based on */ xlcc = atoi(diallcc); /* local country code. */ if (xretries < 0) { switch (xlcc) { case 1: xretries = 10; break; /* No restrictions in NANP */ /* Add other country codes here */ /* that are known to have no restrictions on redialing. */ default: xretries = 1; } } } if (cx == XXPDIA) { /* Shortcut... */ cx = XXDIAL; partial = 1; debug(F100,"PDIAL sets partial=1","",0); postfix = 0; /* Do not add postfix */ } else { partial = 0; debug(F100,"DIAL sets partial=0","",0); } previous = dialsta; /* Status of previous call, if any */ if (previous == DIA_PART) { prefix = 0; /* do not add prefix */ } s = NULL; /* Initialize user's dial string */ if (cx == XXRED) { /* REDIAL or... */ if ((y = cmcfm()) < 0) return(y); } else if (cx == XXANSW) { /* ANSWER or ... */ if ((y = cmnum("timeout (seconds)","0",10,&x,xxstring)) < 0) return(y); dialatmo = x; if ((y = cmcfm()) < 0) return(y); } else { /* DIAL or LOOKUP */ if (ndialdir > 0) s3 = "Number to dial or entry from dial directory"; else s3 = "Number to dial"; if ((x = cmtxt(s3, dialnum ? dialnum : "",&s,xxstring)) < 0) return(x); if (s) { len = (int) strlen(s); ckstrncpy(tmpbuf,s,TMPBUFSIZ); /* Save literal copy */ #ifdef COMMENT if (len > 1) { /* Strip outer braces if given */ if (*s == '{') { if (s[len-1] == '}') { s[len-1] = NUL; s++; len -= 2; } } } #else s = brstrip(s); /* Strip outer braces or quotes */ #endif /* COMMENT */ } } if (cx != XXLOOK) { /* Not LOOKUP */ #ifdef IKSD if (inserver) { printf("Sorry, dialing is disabled.\r\n"); return(success = 0); } #endif /* IKSD */ #ifdef CK_TAPI if (tttapi && !tapipass) { ; /* Skip the modem test if TAPI */ } else #endif /* CK_TAPI */ if (mdmtyp < 1 && !dialtest) { if (network #ifdef TN_COMPORT && !istncomport() #endif /* TN_COMPORT */ ) printf("Please SET HOST first, and then SET MODEM TYPE\n"); else printf("Sorry, you must SET MODEM TYPE first\n"); dialsta = DIA_NOMO; return(success = 0); } if (!local && !dialtest) { printf("Sorry, you must SET %s or SET HOST first\n", #ifdef OS2 "PORT" #else "LINE" #endif /* OS2 */ ); dialsta = DIA_NOLI; return(success = 0); } if ((!network #ifdef TN_COMPORT || istncomport() #endif /* TN_COMPORT */ ) && !dialtest && #ifdef CK_TAPI !tttapi && #endif /* CK_TAPI */ (speed < 0L) #ifdef UNIX && (strcmp(ttname,"/dev/null")) #else #ifdef OSK && (strcmp(ttname,"/nil")) #endif /* OSK */ #endif /* UNIX */ ) { printf("\nSorry, you must SET SPEED first\n"); dialsta = DIA_NOSP; return(success = 0); } } if (cx != XXANSW) { for (j = 0; j < MAXDNUMS; j++) { /* Initialize dial-number list */ if (!dialnum) { /* First time dialing */ dn_p[j] = NULL; /* initialize all pointers. */ dn_p2[j] = NULL; } else if (dn_p[j]) { /* Not the first time, */ free(dn_p[j]); /* free previous, if any, */ dn_p[j] = NULL; /* then set to NULL. */ if (dn_p2[j]) free(dn_p2[j]); dn_p2[j] = NULL; } else break; /* Already NULL */ } if (len == 0) s = NULL; if (!s) s = dialnum; if (!s) { if (cx == XXLOOK) printf("?Lookup what?\n"); else printf("%s\n", (cx == XXRED) ? "?No DIAL command given yet" : "?You must specify a number to dial" ); return(-9); } /* Now we have the "raw" dial or lookup string and s is not NULL */ makestr(&dscopy,s); /* Put it in a safe place */ s = dscopy; n = 0; debug(F111,"dodial",s,ndialdir); wasalpha = 0; if (isalpha(*s)) { wasalpha = 1; if (ndialdir > 0) { /* Do we have a dialing directory? */ n = ludial(s,cx); /* Look up what the user typed */ if (n == 0) printf(" Lookup: \"%s\" - not found%s\n", s, cx == XXLOOK ? "" : " - dialing as given\n" ); } debug(F101,"dodial",s,n); if (n < 0 && cx != XXLOOK) { /* Error out if they wanted to dial */ if (n == -1) /* -2 means ludial already gave msg */ printf(" Lookup: fatal error - dialing skipped\n"); dialsta = DIA_DIR; return(-9); } if (n > 0) /* A successful lookup */ lufound = 1; } else if (*s == '=') { /* If number starts with = sign */ s++; /* strip it */ literal = 1; /* remember this */ while (*s == SP) s++; /* and then also any leading spaces */ } else if (tmpbuf[0] == '{' && tmpbuf[1] == '{') { makelist(tmpbuf,dn_p,MAXDNUMS); makestr(&dscopy,tmpbuf); s = tmpbuf; for (n = 0; n < MAXDNUMS; n++) /* (have to count how many) */ if (!dn_p[n]) break; braces = 1; } if (cx == XXLOOK && !wasalpha && !braces) { /* We've been told to lookup a number or a quoted name */ char *p; n = 0; p = literal ? s : pncvt(dscopy); if (!p) p = ""; if (*p) { printf("%s => %s\n", dscopy, p); return(success = 1); } else { printf("?Bad phone number\n"); return(success = 0); } } /* Save DIAL or successful LOOKUP string for future DIAL or REDIAL */ /* But don't save pieces of partial dial ... */ debug(F101,"DIAL save dialnum partial","",partial); debug(F101,"DIAL save dialnum previous","",previous); if ((cx == XXDIAL && partial == 0 && previous != DIA_PART) || (cx == XXLOOK && n > 0)) { makestr(&dialnum,dscopy); if (!quiet && dscopy && !dialnum) printf("WARNING - memory allocation failure: redial number\n"); } if (n > 0) { if (!quiet && !backgrd && !braces /* && dialdpy */ ) { if (!strcmp(d_name,s)) printf(" Lookup: \"%s\" - exact match\n",s); else printf(" Lookup: \"%s\" - uniquely matches \"%s\"\n", s, d_name ); } if ((cx == XXLOOK) || ((n > 1) && !quiet && !backgrd /* && dialdpy */ )) { printf(" %d telephone number%sfound for \"%s\"%s\n", n, (n == 1) ? " " : "s ", s, (n > 0) ? ":" : "." ); s3 = getdname(); } for (i = 0; i < n; i++) { /* Convert */ dn_x[i] = -1; if (dncvt(i,cx,prefix,postfix) < 0) { if (cx != XXLOOK) { dialsta = DIA_DIR; return(-9); } } } if (dialsrt && n > 1) { /* Sort into optimal order */ for (i = 0; i < n-1; i++) { for (j = i+1; j < n; j++) { if (dn_x[j] < dn_x[i]) { t = dn_x[j]; dn_x[j] = dn_x[i]; dn_x[i] = t; p = dn_p[j]; dn_p[j] = dn_p[i]; dn_p[i] = p; p = dn_p2[j]; dn_p2[j] = dn_p2[i]; dn_p2[i] = p; } } } } if ((cx == XXLOOK) || ((n > 1) && !quiet && !backgrd /* && dialdpy */ )) { int nn = n; #ifndef NOSPL char * p; #endif /* NOSPL */ if (cx != XXLOOK) if (n > 12) nn = 12; for (i = 0; i < nn; i++) { printf("%3d. %-12s %-20s => %-20s (%d)\n",i+1, s3, dn_p[i], dn_p2[i] ? dn_p2[i] : "(processing failed)", dn_x[i] ); } if (cx != XXLOOK && n != nn) printf("And %d more...\n", n - nn); } } else if (n == 0) { /* Not found in directory */ makestr(&(dn_p[0]),literal ? s : dscopy); makestr(&d_name,literal ? s : dscopy); dncount = 1; n = 1; if (dncvt(0,cx,prefix,postfix) < 0) { /* In case they typed a */ dialsta = DIA_DIR; /* portable-format number ... */ return(-9); } } #ifndef NONET #ifdef NETCONN /* It's not good that the networks directory depends on NOT-NODIAL.. */ if (cx == XXLOOK && dscopy) { /* Networks here too... */ extern char *nh_p[], *nh_p2[], *n_name; extern char *nh_px[4][MAXDNUMS+1]; n = -1; if (nnetdir > 0) { /* Do we have a network directory? */ dirline = 0; n = lunet(dscopy); /* Look up what the user typed */ } if (n > -1) { int k; if (n > 0) /* A successful lookup */ lufound = 1; if (cx == XXLOOK && n == 0) printf(" Lookup: \"%s\" - not found\n",dscopy); else printf("%s %d network entr%s found for \"%s\"%s\n", cx == XXLOOK ? " Lookup:" : "", n, (n == 1) ? "y" : "ies", dscopy, (n > 0) ? ":" : "." ); for (i = 0; i < n; i++) { printf("%3d. %-12s => %-9s %s", i+1,n_name,nh_p2[i],nh_p[i]); for (k = 0; k < 4; k++) { if (nh_px[k][i]) { printf(" %s",nh_px[k][i]); } else break; } printf("\n"); } } } #endif /* NETCONN */ #endif /* NONET */ if (cx == XXLOOK) return(success = lufound); } /* cx != XXANSW */ #ifdef VMS conres(); /* So Ctrl-C/Y will work */ #endif /* VMS */ /* Some modems do not react well to parity. Also, if we are dialing through a TCP/IP TELNET modem server, parity can be fatally misinterpreted as TELNET negotiations. This should work even if the user interrupts the DIAL command, because the DIAL module has its own interrupt handler. BUT... if, for some reason, a dialing device actually *requires* parity (e.g. CCITT V.25bis says that even parity should be used), this might prevent successful dialing. For that reason, we don't do this for V.25bis modems. */ sparity = parity; /* Save current parity */ if ((dialcapas & CKD_V25) == 0) /* If not V.25bis... */ parity = 0; /* Set parity to NONE */ flowsave = flow; /* These modems use some kind of screwy flow control while in command mode, and do not present CTS as they should. So if RTS/CTS is set (or even if it isn't) disable flow control during dialing. */ #ifndef MINIDIAL if (mdmtyp == n_ATT1910 || mdmtyp == n_ATT1900) { flow = FLO_NONE; /* This is not enough */ #ifdef CK_TTSETFLOW ttsetflow(FLO_NONE); /* Really turn it off */ #endif /* CK_TTSETFLOW */ } #endif /* MINIDIAL */ if (!network #ifdef TN_COMPORT || istncomport() #endif /* TN_COMPORT */ ) { int x; if ((x = ttgmdm()) > -1) { if (!x && msgflg) { printf( "WARNING - No modem signals detected. Is your modem turned on? If not,\n\ use Ctrl-C to interrupt dialing, turn on your modem, then %s.\n", cx == XXANSW ? "ANSWER again" : "REDIAL" ); } if (flow == FLO_RTSC) { if (!(x & BM_CTS)) { if (msgflg) printf( "WARNING - SET FLOW RTS/CTS is in effect but modem's CTS signal is off.\n\ Disabling flow control temporarily %s...\n", cx == XXANSW ? "while waiting for call" : "during dialing" ); flow = FLO_NONE; } } } } if (cx == XXANSW) { /* ANSWER */ success = ckdial("",0,0,1,0); goto dialfin; } /* Edit 192 adds the ability to dial repeatedly. */ i = 0; dialcount = 0; do { if (i > 0) printf("\nDial attempt %d of %d...\n", i+1, xretries); dialcount = i+1; success = 0; /* And the ability to dial alternate numbers. */ /* Loop to dial each in a list of numbers for the same name... */ for (j = 0; j < n && !success; j++) { /* until one answers. */ s = dn_p2[j]; /* Next number in list */ if (dn_x[j] >= dialrstr) { /* Dial restriction */ printf("Restricted: %s, skipping...\n",dn_p[j]); continue; } xredial = (i == 0 && j == 0) ? 0 : 1; if (!s) s = dn_p[j]; #ifndef NOSPL sav = s; p = xdial(s); /* Apply DIAL macro now */ if (p) if (*p) s = p; #endif /* NOSPL */ /* Dial confirmation */ /* NOTE: the uq_xxx() calls allow for a GUI dialog */ if (i == 0 && dialcnf) { char msgbuf[128]; ckmakmsg(msgbuf,128,"Dialing ",s,NULL,NULL); x = uq_ok(msgbuf,"Is this number correct? ",3,NULL,0); if (!x) { #ifndef COMMENT x = uq_txt( /* Allow GUI dialog */ #ifdef OS2 " Please enter the correct number,\r\n or press Enter to skip.", #else " Please enter the correct number,\r\n or press Return to skip.", #endif /* OS2 */ "Corrected phone number: ", 1, NULL, atmbuf, ATMBL, s, DEFAULT_UQ_TIMEOUT ); if (x && atmbuf[0]) { /* They gave a new one */ s = atmbuf; makestr(&(dn_p2[j]), s); } #else /* COMMENT */ #ifdef CK_RECALL extern int on_recall; #endif /* CK_RECALL */ cmsavp(psave,PROMPTL); cmsetp( #ifdef OS2 " Please enter the correct number,\r\n or press Enter to skip: " #else " Please enter the correct number,\r\n or press Return to skip: " #endif /* OS2 */ ); cmini(ckxech); x = -1; if (pflag) prompt(NULL); #ifdef CK_RECALL on_recall = 0; #endif /* CK_RECALL */ y = cmdgquo(); cmdsquo(0); while (x < 0) { x = cmtxt("Corrected phone number","",&s,NULL); cmres(); } if ((int) strlen(s) < 1) { cmsetp(psave); continue; } makestr(&(dn_p2[j]), s); cmdsquo(y); cmsetp(psave); #endif /* COMMENT */ } } if (dialtest) { /* Just testing */ if (i + j == 0) printf("\nTESTING...\n"); if (dialmac) printf(" Number: \"%s\" => \"%s\"\n",sav,s); else printf(" Number: \"%s\"\n",s); dialsta = DIA_BUSY; success = 0; } else { what |= W_DIALING; success = ckdial(s,i,j,partial ? 3 : 0, xredial); /* Dial it */ what &= ~(W_DIALING); if (!success) { if (dialsta < 8 || /* Break out if unrecoverable error */ dialsta == DIA_INTR || dialsta == DIA_ERR || previous == DIA_PART ) break; } } } if (success) /* Succeeded, leave the outer loop */ break; if (dialsta < 8 || /* Break out if unrecoverable error */ dialsta == DIA_INTR || /* Interrupted */ dialsta == DIA_NODT || /* No dialtone */ dialsta == DIA_NOAC || /* Access forbidden */ dialsta == DIA_BLCK || /* Blacklisted */ dialsta == DIA_DIR || /* Dialing directory error */ dialsta == DIA_ERR || /* Modem command error */ previous == DIA_PART) break; if (++i >= xretries) /* Break out if too many tries */ break; if (!backgrd && !quiet) { if (dialint > 5) printf( "\nWill redial in %d second%s- press any key to redial immediately.\n", dialint, dialint == 1 ? " " : "s " ); printf("Ctrl-C to cancel...\n"); } x = dialint; /* Redial interval */ while (x-- > 0) { if ((y = conchk()) > 0) { /* Did they type something? */ while (y--) coninc(0); /* Yes, absorb it */ break; /* And wake up */ } sleep(1); /* No interrupt, sleep a sec */ } } while (!success); dialfin: if (cx != XXLOOK) { if (!success) bleep((short) BP_FAIL); else if (!quiet) bleep((short) BP_NOTE); #ifdef OS2 setint(); /* Fix OS/2 interrupts */ #endif /* OS2 */ if (sparity > -1) parity = sparity; /* Restore parity if we saved it */ flow = flowsave; #ifdef OS2 ttres(); /* Restore DIAL device */ #endif /* OS2 */ #ifdef VMS concb((char)escape); /* Restore console */ #endif /* VMS */ #ifdef OS2 { /* Set session title */ char * p, name[72]; /* in window list. */ char * q; if (cx == XXANSW) { q = "Incoming call"; } else { if (d_name) q = d_name; else if (dialnum) q = dialnum; else if (ttname[0]) q = ttname; else q = ""; } p = name; if (success) { strncpy(name,q,48); while (*p) { /* Uppercase it for emphasis. */ if (islower(*p)) *p = toupper(*p); p++; } } else name[0] = NUL ; os2settitle((char *) name, TRUE); } #endif /* OS2 */ } if (cx != XXLOOK) { if (success) { if (reliable == SET_AUTO) { /* It's not a reliable connection. */ reliable = SET_OFF; debug(F101,"dodial reliable","",reliable); } } else { #ifndef NOHINTS extern int hints; if (hints && !quiet && dialsta != 9) { /* 9 == User interrupted */ extern int dialmhu, dialhng, dialdpy; extern char * dialmsg[]; printf("\n*************************\n"); printf("DIAL-class command failed.\n"); printf("Modem type: %s\n", gmdmtyp()); printf("Device: %s\n", ttname); printf("Speed: %ld\n", speed); printf("Dial status: %d",dialsta); if (dialsta < 35 && dialmsg[dialsta]) printf(" [%s]",dialmsg[dialsta]); printf("\n"); if (dialsta == DIA_TIMO || dialsta == DIA_NRDY || (dialsta > 13 && dialsta != DIA_BUSY && dialsta != DIA_NOAN) ) { switch (dialsta) { case DIA_TIMO: printf( " . SET DIAL TIMEOUT to a greater value and try again.\n" ); break; case DIA_NRSP: case DIA_NRDY: case DIA_NOIN: printf( " . Is the modem turned on?\n" ); printf( " . Are you using the right communication port?\n" ); break; case DIA_NODT: printf( " . Is the modem connected to the telephone line?\n" ); } if (mdmtyp == n_GENERIC) { printf( " . Please choose a specific modem type with SET MODEM TYPE and try again.\n" ); printf( " SET MODEM TYPE ? to see the list of known modem types.\n" ); } else { printf( " . Are you sure you have chosen the appropriate modem type?\n" ); } if (speed > 19200L) { printf( " . Maybe the interface speed (%ld) is too fast:\n", speed ); printf( " SET SPEED to a lower speed and try again.\n" ); printf( " SET SPEED ? to see the list of valid speeds.\n" ); } if (dialhng) { if (dialmhu) printf( " . SET MODEM HANGUP-METHOD RS232 and try again.\n" ); else printf( " . SET MODEM HANGUP-METHOD MODEM-COMMAND and try again.\n" ); printf( " . If that doesn't work, try again with SET DIAL HANGUP OFF.\n" ); } else { printf( " . Give a HANGUP or SET DIAL HANGUP ON command and try again.\n" ); } if (!dialdpy) printf( " . Use SET DIAL DISPLAY ON to watch the dialog between Kermit and modem.\n" ); } #ifndef NOSHOW printf( " . SHOW COMMUNICATIONS, SHOW MODEM, SHOW DIAL to see current settings.\n" ); #endif /* NOSHOW */ #ifndef NOHELP printf( " . HELP SET MODEM, HELP SET DIAL, and HELP DIAL for more information.\n" ); #endif /* NOHELP */ printf("(Use SET HINTS OFF to suppress future hints.)\n"); printf("*************************\n\n"); } #endif /* NOHINTS */ } } return(success); } #endif /* NODIAL */ /* D O T Y P E -- Type (display) a file with various options... */ #ifdef BIGBUFOK #define TYPBUFL 16384 #else #define TYPBUFL 256 #endif /* BIGBUFOK */ int typ_lines = 0; /* \v(ty_ln) */ int typ_mtchs = 0; /* \v(ty_lm) */ static int typ_int = 0; /* Flag if TYPE interrupted */ #ifdef UNICODE extern int fcharset, fileorder, byteorder, ucsorder; #define TYPXBUFL TYPBUFL+TYPBUFL+TYPBUFL+4 static char * mp = NULL; static char * mbuf = NULL; static long xn = 0L; static int #ifdef CK_ANSIC storechar(char c) #else storechar(c) char c; #endif /* CK_ANSIC */ { if (!mp) return(-1); if (++xn > TYPXBUFL) return(-1); debug(F111,"storechar xn",ckitoa((int)c),xn); *mp++ = c; return(0); } #endif /* UNICODE */ static FILE * ofp = NULL; /* For /OUTPUT: file */ static int typeline(buf,len,outcs,ofp) char * buf; int len, outcs; FILE * ofp; { register int i; debug(F011,"typeline buf",buf,len); /* debug(F101,"typeline outcs","",outcs); */ #ifdef OS2 #ifndef NOLOCAL #ifdef UNICODE /* In K95 only, the buffer is guaranteed to be in UCS-2 if outcs >= 0. */ /* Len is its length in bytes. There is no line terminator. */ /* outcs is the file character-set number (FC_xxx) of the target set */ /* that was requested by the user. */ if (!inserver && !k95stdout) { extern int wherex[], wherey[]; extern unsigned char colorcmd; VscrnWrtUCS2StrAtt( VCMD, (unsigned short *)buf, len/2, wherey[VCMD], wherex[VCMD], &colorcmd); printf("\r\n"); return(0); } #endif /* UNICODE */ #endif /* NOLOCAL */ #endif /* OS2 */ /* In Unix, VMS, etc, the line has already been converted to the desired */ /* character-set, if one was given. OR... on all platforms, including in */ /* K95, we don't know the character set. In either case we dump the line */ /* byte by byte in case it contains NULs (printf() would truncate). */ #ifdef COMMENT for (i = 0; i < len; i++) putchar(buf[i]); #else for (i = 0; i < len; i++) { if (ofp == stdout) { putchar(buf[i]); } else { putc(buf[i],ofp); } } #endif /* COMMENT */ #ifdef IKSD if (inserver) { #ifdef UNICODE if (outcs == FC_UCS2) { if (ofp == stdout) { putchar(NUL); } else { putc(NUL,ofp); } } #endif /* UNICODE */ if (ofp == stdout) { putchar('\r'); } else { putc('\r',ofp); } } #endif /* IKSD */ #ifdef UNICODE if (outcs == FC_UCS2) { if (ofp == stdout) { putchar(NUL); } else { putc(NUL,ofp); } } #endif /* UNICODE */ if (ofp == stdout) { putchar('\n'); } else { putc('\n',ofp); } fflush(stdout); return(0); } static int /* Get translated line */ typegetline(incs, outcs, buf, n) int incs, outcs, n; char * buf; { int x = 0, c0, c1, len = 0, count = 0, eof = 0, xlate = 0; #ifdef UNICODE int xxn = -1; int yyn = -9; xn = 0L; #ifdef DEBUG if (deblog && typ_lines == 0) { debug(F101,"typegetline incs","",incs); debug(F101,"typegetline outcs","",outcs); debug(F101,"typegetline feol","",feol); debug(F101,"typegetline byteorder","",byteorder); debug(F101,"typegetline ucsorder ","",ucsorder); debug(F111,"typegetline fileorder","1",fileorder); } #endif /* DEBUG */ if (incs < 0) /* Shouldn't happen */ return(-2); if (outcs == -1) /* Can happen */ outcs = incs; if (incs != outcs || incs == FC_UCS2) { /* See if we should translate */ xlate = 1; if (!mbuf) { /* Allocate buffer if not allocated */ mbuf = (char *)malloc(TYPXBUFL+1); /* yet */ if (!mbuf) { printf("WARNING: Translation buffer allocation failure.\n"); printf("Translation will be skipped...\n"); xlate = 0; } } } if (xlate) { /* Translating... */ mp = mbuf; /* Reset working buffer pointer */ /* Here we call xgnbyte() in a loop, having it return UCS-2 bytes. In K95, we use UCS-2 directly. Elsewhere, we feed the UCS-2 bytes into xpnbyte() to convert them to the desired target character set. But since we are using UCS-2, we have several sources for confusion: (1) xgnbyte() might return in LE or BE byte order, with no explicit indication of what the order is; but (2) xpnbyte() wants BE; but (3) Windows wants LE. */ while (1) { if (typ_int) /* Quit if interrupted */ return(0); c0 = xgnbyte(FC_UCS2,incs,NULL); /* Convert to UCS-2 */ debug(F000,"typegetline c0","",c0); if (c0 < 0) { /* EOF */ eof++; break; } c1 = xgnbyte(FC_UCS2,incs,NULL); /* Convert to UCS-2 */ debug(F000,"typegetline c1","",c1); if (c1 < 0) { /* EOF */ eof++; break; } #ifdef DEBUG if (deblog && typ_lines == 0) { if (count == 0) /* Check fileorder after BOM */ debug(F111,"typegetline fileorder","2",fileorder); } #endif /* DEBUG */ #ifdef COMMENT /* Now we have the two UCS-2 bytes. Which order are they in? */ if (fileorder > 0) { /* Little Endian */ int t; /* So swap them */ debug(F100,"typegetline swapping","",0); t = c1; c1 = c0; c0 = t; } #endif /* COMMENT */ if (c0 == 0 && c1 == 0x0D) /* Now see if we have EOL */ yyn = xn; if (c0 == 0 && c1 == 0x0A) /* Now see if we have EOL */ xxn = xn; count++; /* Count byte */ /* Give the two bytes to xpnbyte() in BE order */ if ((x = xpnbyte(c0,TC_UCS2,outcs,storechar)) < 0) return(-1); if ((x = xpnbyte(c1,TC_UCS2,outcs,storechar)) < 0) return(-1); if (xxn > -1) { /* Have end of line? */ xn = xxn; if (yyn == xxn - 2) /* Adjust for CRLF */ xn = yyn; break; /* And break out of loop. */ } } mbuf[xn] = NUL; if (xn > n) /* Can truncate here... */ xn = n; memcpy(buf,mbuf,xn); debug(F011,"typegetline xlate",buf,xn); return((eof && (xn == 0)) ? -1 : xn); } #endif /* UNICODE */ #ifdef COMMENT /* We can't use this because, stupidly, zsinl() doesn't return a length. */ /* It could be changed but then we'd have to change all ck?fio.c modules */ x = zsinl(ZIFILE,buf,n); #else /* So instead, we copy zsinl() to here... */ /* But note: This does not necessarily handle UCS-2 alignment properly; */ /* that's what the code in the first section of this routine is for. */ /* But it does tolerate files that contain NULs. */ { int a; char *s; s = buf; a = -1; /* Current character, none yet. */ debug(F101,"typegetline zsinl simulation","",n); while (n--) { /* Up to given length */ #ifdef COMMENT int old = 0; if (feol) /* Previous character */ old = a; #endif /* COMMENT */ if (zchin(ZIFILE,&a) < 0) { /* Read a character from the file */ debug(F101,"typegetline zchin fail","",count); if (count == 0) x = -1; /* EOF or other error */ break; } else count++; if (feol) { /* Single-character line terminator */ if (a == feol) break; } else { /* CRLF line terminator */ #ifdef COMMENT /* Debug log shows that in Windows, is returned as . */ /* Apparently we're not reading the file in binary mode. */ if (a == '\015') /* CR, get next character */ continue; if (old == '\015') { /* Previous character was CR */ if (a == '\012') { /* This one is LF, so we have a line */ break; } else { /* Not LF, deposit CR */ *s++ = '\015'; n--; len++; } } #else if (a == LF) { if (s[len] == CR) { /* This probably won't happen */ s[len] = NUL; s--; len--; } break; } #endif /* COMMENT */ } *s = a; /* Deposit character */ s++; len++; } *s = '\0'; /* Terminate the string */ } #endif /* COMMENT */ return(x < 0 ? -1 : len); } #ifndef MAC SIGTYP #ifdef CK_ANSIC tytrap(int foo) /* TYPE interrupt trap */ #else tytrap(foo) int foo; #endif /* CK_ANSIC */ /* tytrap */ { #ifdef __EMX__ signal(SIGINT, SIG_ACK); #endif debug(F100,"type tytrap SIGINT","",0); typ_int = 1; /* (Need arg for ANSI C) */ SIGRETURN; } #endif /* MAC */ int dotype(file, paging, first, head, pat, width, prefix, incs, outcs, outfile, z) char * file, * pat, * prefix; int paging, first, head, width, incs, outcs; char * outfile; int z; /* dotype */ { extern CK_OFF_T ffc; char buf[TYPBUFL+2]; char * s = NULL; int rc = 1, lines = 0, ucs2 = 0; char ** tail = NULL; int * tlen = NULL; int tailing = 0, counting = 0; int x, c, n, i, j, k = 0; int number = 0, save, len, pfxlen = 0, evalpfx = 1; #ifdef UNICODE int ucsbom_sav; extern int ucsbom; #endif /* UNICODE */ #ifdef NT int gui = 0; #endif /* NT */ #ifndef MAC #ifdef OS2 #ifdef NT SIGTYP (* oldsig)(int); /* For saving old interrupt trap. */ #else /* NT */ SIGTYP (* volatile oldsig)(int); #endif /* NT */ #else /* OS2 */ SIGTYP (* oldsig)(); #endif /* OS2 */ #endif /* MAC */ #ifdef KUI if (outfile == (char *)1) { gui = 1; outfile = ""; } #endif /* KUI */ if (!file) file = ""; if (!*file) return(-2); if (ofp != stdout) { /* In case of previous interruption */ if (ofp) fclose(ofp); ofp = stdout; } if (!outfile) outfile = ""; if (outfile[0]) { ofp = fopen(outfile,"w"); /* Open output file */ if (!ofp) { printf("?Can't open output file %s: %s\n",outfile,ck_errstr()); ofp = stdout; return(-9); } } number = z; if (number && prefix) prefix = NULL; #ifdef UNICODE ucsbom_sav = ucsbom; /* We are not creating a file */ ucsbom = 0; /* Do not use BOM bytes */ #endif /* UNICODE */ typ_int = 0; save = binary; /* Save file type */ debug(F101,"dotype incs","",incs); debug(F101,"dotype outcs","",outcs); #ifdef UNICODE debug(F111,"dotype fileorder","A",fileorder); #ifdef OS2 if (!inserver && !k95stdout) outcs = FC_UCS2; #endif /* OS2 */ if (outcs == FC_UCS2) /* Output is UCS-2? */ ucs2 = 1; if (fileorder < 0) fileorder = ucsorder; debug(F111,"dotype fileorder","B",fileorder); #endif /* UNICODE */ #ifdef CK_TTGWSIZ #ifdef OS2 ttgcwsz(); #else /* OS2 */ /* Check whether window size changed */ if (ttgwsiz() > 0) { if (tt_rows > 0 && tt_cols > 0) { cmd_rows = tt_rows; cmd_cols = tt_cols; debug(F101,"dotype cmd_rows","",cmd_rows); debug(F101,"dotype cmd_cols","",cmd_cols); } } #endif /* OS2 */ #endif /* CK_TTGWSIZ */ if (prefix) pfxlen = strlen(prefix); if (paging < 0) { /* Count only, don't print */ counting = 1; prefix = NULL; width = 0; paging = 0; } if (ucs2) /* Crude... */ width *= 2; #ifdef OS2 if (*file) { ckstrncpy(buf, file, TYPBUFL); /* Change / to \. */ p = buf; while (*p) { if (*p == '/') *p = '\\'; p++; } file = buf; } else { rc = 0; goto xdotype; } #endif /* OS2 */ if (zchki(file) == -2) { /* It's a directory */ debug(F111,"dotype zchki failure",file,-2); if (xcmdsrc == 0) { printf("?Not a regular file: \"%s\"\n",file); rc = -9; } else rc = 0; goto xdotype; } if (!zopeni(ZIFILE, file)) { /* Not a directory, open it */ debug(F111,"dotype zopeni failure",file,0); if (xcmdsrc == 0) { printf("?Can't open file: \"%s\"\n",file); rc = -9; } else rc = 0; goto xdotype; } #ifndef AMIGA #ifndef MAC errno = 0; oldsig = signal(SIGINT, tytrap); /* Save current interrupt trap. */ /* debug(F111,"type SIGINT trap set",ckitoa(errno),oldsig); */ #endif /* MAC */ #endif /* AMIGA */ if (paging > -1) /* More-prompting */ xaskmore = paging; binary = 0; if (head < 0) { /* "tail" was requested */ tailing = 1; /* Set flag */ head = 0 - head; /* Get absolute number of lines */ if (!counting) { tail = (char **) malloc(head * sizeof(char *)); /* Allocate list */ if (!tail) { printf("?Memory allocation failure\n"); goto xdotype; } tlen = (int *) malloc(head * sizeof(int)); if (!tlen) { printf("?Memory allocation failure\n"); goto xdotype; } for (i = 0; i < head; i++) { /* Initialize each pointer in list. */ tail[i] = NULL; tlen[i] = 0; } } } typ_lines = 0; typ_mtchs = 0; #ifdef UNICODE if (outcs > -1 && (incs != outcs || incs == FC_UCS2)) { /* Translating? */ ffc = (CK_OFF_T)0; initxlate(incs,outcs); /* Set up translation functions */ } else #endif /* UNICODE */ outcs = -1; /* Means we don't know the charset */ debug(F101,"dotype ffc","",ffc); debug(F101,"dotype outcs 2","",outcs); #ifdef UNICODE debug(F111,"dotype fileorder","C",fileorder); #endif /* UNICODE */ /* Allow the buffer to contain NULs */ for (n = first; (len = typegetline(incs,outcs,buf,TYPBUFL)) > -1; lines++ ) { debug(F011,"dotype line",buf,len); #ifndef MAC if (typ_int) { /* Interrupted? */ typ_int = 0; debug(F101,"type interrupted line","",lines); printf("^C...\n"); /* Print message */ if (ofp != stdout) { /* Close any output file */ if (ofp) fclose(ofp); ofp = stdout; } goto xxdotype; } #endif /* MAC */ typ_lines++; /* For \v(ty_ln) */ if (pat) /* Matching? */ if (!ckmatch(pat,buf,1,1+4)) /* Line matches pattern? */ continue; /* No, skip it */ typ_mtchs++; if (head > 0 && !tailing && lines == head) /* Handle /HEAD:n */ break; buf[TYPBUFL+1] = NUL; /* Just in case... */ if (prefix) { /* Add specified prefix to each line */ char pbuf[64]; char * pp; pp = prefix; #ifndef NOSPL if (evalpfx) { /* Prefix is a variable? */ int n = 63; /* Maybe - evaluate it and see */ char * p = pbuf; zzstring(prefix,&p,&n); /* If there is no change */ if (!strcmp(prefix,pbuf)) { /* it's not a variable */ evalpfx = 0; /* So don't do this again. */ } else { /* It was a variable */ pp = pbuf; /* So substitute its value */ pfxlen = 63 - n; /* and get its new length */ } } #endif /* NOSPL */ if (len + pfxlen + 2 < TYPBUFL) { /* Shift right to make room for prefix */ memcpy((char *)line+pfxlen,(char *)buf,len); lset((char *)line,pp,pfxlen,SP); debug(F110,"dotype prefix",line,pfxlen); len += pfxlen; memcpy((char *)buf,(char *)line,len); } } else if (number) { /* Line numbers */ int x; sprintf(line,"%4d. ",typ_lines); x = strlen(line); len += x; if (len < LINBUFSIZ) { memcpy((char *)&line[x],(char *)buf,len); memcpy((char *)buf,(char *)line,len); } } if (width > 0 && width <= TYPBUFL) { /* Truncate at given width. */ char * obuf = line; /* But to do that first we must */ int i,k,z; /* expand tabs; assume every 8 cols. */ line[0] = NUL; for (i = 0, k = 0; i < width; k++) { /* Character loop... */ if (!buf[k]) /* No more chars in this line, done. */ break; if (buf[k] != '\t') { /* If it's not a tab */ if (i >= LINBUFSIZ) /* Check for overflow */ break; obuf[i++] = buf[k]; /* and then deposit it. */ obuf[i] = NUL; /* Keep it null-terminated */ continue; } z = 8 - (i % 8); /* It's a tab, expand it. */ if (z == 0) z = 8; for (j = 0; j < z && i < LINBUFSIZ; j++) { #ifdef UNICODE if (ucs2 && !ucsorder) obuf[i++] = NUL; #endif /* UNICODE */ obuf[i++] = ' '; #ifdef UNICODE if (ucs2 && ucsorder) obuf[i++] = NUL; #endif /* UNICODE */ } obuf[i++] = NUL; obuf[i] = NUL; } obuf[width] = NUL; /* Now truncate at given width. */ #ifdef COMMENT /* This doesn't work for UCS-2 because it contains NULs */ ckstrncpy(buf,obuf,TYPBUFL); /* and copy it back (again?) */ #else memcpy((char *)buf,(char *)obuf,i); /* Copy it back */ #endif /* COMMENT */ len = (i > width) ? width : i; /* Spare us another strlen()... */ } if (tailing) { /* If /TAIL:n... */ k = lines % head; /* save this line in circular buffer */ if (!counting) { if (tail[k]) free(tail[k]); tail[k] = malloc(len+2); if (!tail[k]) { printf("?Memory allocation failure\n"); goto xdotype; } memcpy(tail[k],buf,len); tlen[k] = len; continue; } } if (counting) /* If only counting */ continue; /* we're done with this line */ if (paging) { /* Displaying this line... */ int u; u = len; /* Length in BYTES */ if (ucs2) /* If outputting in UCS-2 */ u /= 2; /* convert length to CHARACTERS */ x = (u / cmd_cols) + 1; /* Crudely allow for wrap */ if (cmd_rows > 0 && cmd_cols > 0) n += x; /* This assumes terminal will wrap */ } #ifdef KUI if ( gui ) { int i; unsigned short * uch = (unsigned short *)buf; for ( i=0; i 0 && ofp == stdout) { /* Pause at end of screen */ if (cmd_rows > 0 && cmd_cols > 0) { if (n > cmd_rows - 3) { if (!askmore()) goto xdotype; else n = 0; } } } #endif /* CK_TTGWSIZ */ } xdotype: if (counting) { fprintf(ofp, "%s: %d line%s\n",file,typ_lines,typ_lines == 1 ? "" : "s"); if (pat) fprintf(ofp, "%s: %d match%s\n",pat,typ_mtchs,typ_mtchs == 1 ? "" : "es"); goto xxdotype; } if (tailing && tail) { /* Typing tail of file? */ if (lines < head) { /* Yes, show the lines we saved */ k = 0; /* Show all lines */ } else { /* More lines than tail number */ lines = k; /* Last line to show */ k++; /* First line to show */ if (k >= head) k = 0; } n = first; /* Output line counter */ for (i = k ;; i++) { /* Loop thru circular buffer */ #ifndef MAC if (typ_int) { /* Interrupted? */ printf("^C...\n"); /* Print message */ goto xxdotype; } #endif /* MAC */ j = i % head; /* Index of this line */ s = tail[j]; /* Point to line to display */ if (!s) /* (shouldn't happen...) */ break; if (paging) { /* Crudely allow for line wrap */ x = tlen[j]; if (ucs2) x /= 2; x = x / cmd_cols + 1; if (cmd_rows > 0 && cmd_cols > 0) n += x; } typeline(s,tlen[j],outcs,ofp); /* Display this line */ if (paging && ofp == stdout) { /* Pause at end of screen */ if (cmd_rows > 0 && cmd_cols > 0) { if (n > cmd_rows - 3) { if (!askmore()) break; else n = 0; } } } tail[j] = NULL; free(s); /* Free the line */ if (i % head == lines) /* When to stop */ break; } free((char *)tail); /* Free the list */ tail = NULL; if (tlen) free((char *)tlen); tlen = NULL; } /* Come here when finished or on SIGINT */ xxdotype: #ifndef AMIGA #ifndef MAC signal(SIGINT,oldsig); /* Put old signal action back. */ #endif /* MAC */ #endif /* AMIGA */ if (tailing && tail) { for (i = 0; i < head; i++) { /* Free each line. */ if (tail[i]) free(tail[i]); } free((char *)tail); /* Free list pointer */ if (tlen) free((char *)tlen); } x = zclose(ZIFILE); /* Done, close the input file */ if (ofp != stdout) { /* Close any output file */ if (ofp) fclose(ofp); ofp = stdout; } binary = save; /* Restore text/binary mode */ #ifdef UNICODE ucsbom = ucsbom_sav; /* Restore BOM usage */ #endif /* UNICODE */ #ifdef KUI if ( gui ) gui_text_popup_wait(-1); /* Wait for user to close the dialog */ #endif /* KUI */ return(rc); } /* GREP command */ #define GREP_CASE 0 /* /CASE */ #define GREP_COUN 1 /* /COUNT */ #define GREP_DOTF 2 /* /DOTFILES */ #define GREP_NAME 3 /* /NAMEONLY */ #define GREP_NOBK 4 /* /NOBACKUP */ #define GREP_NODO 5 /* /NODOTFILES */ #define GREP_NOLI 6 /* /NOLIST */ #define GREP_NOMA 7 /* /INVERT = /NOMATCH */ #define GREP_NOPA 8 /* /NOPAGE */ #define GREP_NUMS 9 /* /LINENUMBERS */ #define GREP_PAGE 10 /* /PAGE */ #define GREP_RECU 11 /* /RECURSIVE */ #define GREP_TYPE 12 /* /TYPE: */ #define GREP_OUTP 13 /* /OUTPUTFILE: */ #define GREP_EXCP 14 /* /EXCEPT: */ static struct keytab greptab[] = { { "/count", GREP_COUN, CM_ARG }, { "/dotfiles", GREP_DOTF, 0 }, { "/except", GREP_EXCP, CM_ARG }, { "/linenumbers", GREP_NUMS, 0 }, { "/nameonly", GREP_NAME, 0 }, { "/nobackupfiles",GREP_NOBK, 0 }, { "/nocase", GREP_CASE, 0 }, { "/nodotfiles", GREP_NODO, 0 }, { "/nolist", GREP_NOLI, 0 }, { "/nomatch", GREP_NOMA, 0 }, { "/nopage", GREP_NOPA, 0 }, { "/output", GREP_OUTP, CM_ARG }, { "/page", GREP_PAGE, 0 }, { "/quiet", GREP_NOLI, CM_INV }, #ifdef RECURSIVE { "/recursive", GREP_RECU, 0 }, #endif /* RECURSIVE */ { "/type", GREP_TYPE, CM_ARG }, { "", 0, 0 } }; static int ngreptab = sizeof(greptab)/sizeof(struct keytab)-1; static char * grep_except = NULL; int dogrep() { int match, x, y, fc, getval, mc = 0, count = 0, bigcount = 0; int fline = 0, sline = 0, wild = 0, len = 0; int xmode = -1, scan = 0; char c, name[CKMAXPATH+1], outfile[CKMAXPATH+1], *p, *s, *cv = NULL; FILE * fp = NULL; int /* Switch values and defaults */ gr_coun = 0, gr_name = 0, gr_nobk = 0, gr_case = 1, gr_noli = 0, gr_noma = 0, gr_nums = 0, gr_excp = 0, gr_page = xaskmore; struct FDB sw, fl; g_matchdot = matchdot; /* Save global matchdot setting */ outfile[0] = NUL; makestr(&grep_except,NULL); if (ofp != stdout) { /* In case of previous interruption */ if (ofp) fclose(ofp); ofp = stdout; } cmfdbi(&sw, /* First FDB - command switches */ _CMKEY, /* fcode */ "String or pattern to search for, or switch", "", /* default */ "", /* addtl string data */ ngreptab, /* addtl numeric data 1: tbl size */ 4, /* addtl numeric data 2: 4 = cmswi */ xxstring, /* Processing function */ greptab, /* Keyword table */ &fl /* Pointer to next FDB */ ); cmfdbi(&fl, /* Anything that doesn't match */ _CMFLD, /* fcode */ "", /* hlpmsg */ "", /* default */ "", /* addtl string data */ 0, /* addtl numeric data 1 */ 0, /* addtl numeric data 2 */ xxstring, /* xxstring */ NULL, NULL ); while (1) { /* Parse 0 or more switches */ x = cmfdb(&sw); /* Parse something */ if (x < 0) return(x); if (cmresult.fcode != _CMKEY) /* Break out if not a switch */ break; c = cmgbrk(); if ((getval = (c == ':' || c == '=')) && !(cmgkwflgs() & CM_ARG)) { printf("?This switch does not take an argument\n"); return(-9); } if ((cmresult.nresult != GREP_COUN) && !getval && (cmgkwflgs() & CM_ARG)) { printf("?This switch requires an argument\n"); return(-9); } switch (cmresult.nresult) { case GREP_COUN: { gr_coun++; if (getval) { if ((x = cmfld("Variable for result","",&s,NULL)) < 0) return(x); makestr(&cv,s); } break; } case GREP_CASE: gr_case=0; break; case GREP_NAME: gr_name++; gr_noli=0; break; case GREP_NOBK: gr_nobk++; break; case GREP_NOLI: gr_noli++; gr_name=0; gr_nums=0; break; case GREP_NOMA: gr_noma++; break; case GREP_NOPA: gr_page=0; break; case GREP_NUMS: gr_nums++; gr_noli=0; break; case GREP_PAGE: gr_page++; gr_noli=0; break; case GREP_NODO: matchdot = 0; break; case GREP_DOTF: matchdot = 1; break; #ifdef RECURSIVE case GREP_RECU: recursive = 1; break; #endif /* RECURSIVE */ case GREP_TYPE: { extern struct keytab txtbin[]; if ((x = cmkey(txtbin,3,"","",xxstring)) < 0) return(x); if (x == 2) { /* ALL */ xmode = -1; } else { /* TEXT or BINARY only */ xmode = x; scan = 1; } break; } case GREP_OUTP: /* Send output to file */ if ((x = cmofi("File for GREP'd lines","",&s,xxstring)) < 0) return(x); ckstrncpy(outfile,s,CKMAXPATH); break; case GREP_EXCP: /* Exception pattern */ if (getval) { if ((x = cmfld("Exception pattern", "", &s, xxstring )) < 0) return(x); gr_excp++; makestr(&grep_except,s); } } } if (outfile[0]) { ofp = fopen(outfile,"w"); /* Open output file */ if (!ofp) { printf("?Can't open output file %s: %s\n",outfile,ck_errstr()); ofp = stdout; return(-9); } gr_page = 0; } s = cmresult.sresult; s = brstrip(s); /* Strip braces from pattern */ if (!*s) { printf("?Pattern required\n"); return(-9); } ckstrncpy(tmpbuf,s,TMPBUFSIZ); /* Save pattern */ if ((x = cmifi("File(s) to search","",&s,&wild,xxstring)) < 0) { if (x == -3) { printf("?File specification required\n"); x = -9; } return(x); } s = brstrip(s); /* Strip braces from filename */ #ifndef ZXREWIND ckstrncpy(line,s,LINBUFSIZ); #endif /* ZXREWIND */ if ((y = cmcfm()) < 0) return(y); if (gr_page > -1) xaskmore = gr_page; /* Paging... */ p = tmpbuf; /* Point to pattern */ #ifdef COMMENT /* Now this is done in ckmatch */ if (*p == '^') { /* '^' anchors pattern to beginning */ p++; } else if (*p != '*') { /* Otherwise prepend implied '*' */ tmpbuf[0] = '*'; p = tmpbuf; } x = strlen(p); /* Get length of result */ if (x > 0 && x < TMPBUFSIZ) { /* '$' at end anchors pattern to end */ if (p[x-1] == '$') { p[x-1] = NUL; } else if (p[x-1] != '*') { p[x] = '*'; p[x+1] = NUL; } } #endif /* COMMENT */ debug(F111,"grep pat",p,x); #ifdef ZXREWIND fc = zxrewind(); /* Rewind the file list */ #else { int flags = ZX_FILONLY; /* Expand file list */ if (matchdot) flags |= ZX_MATCHDOT; if (recursive) flags |= ZX_RECURSE; fc = nzxpand(line,flags); } #endif /* ZXREWIND */ #ifdef UNIX sh_sort(mtchs,NULL,fc,0,0,filecase); #endif /* UNIX */ debug(F101,"grep cmd_rows","",cmd_rows); debug(F101,"grep cmd_cols","",cmd_cols); while (1) { /* Loop for each file */ znext(name); /* Get next file */ if (!name[0]) /* No more, done */ break; if (gr_nobk) /* Skipping backup files? */ if (ckmatch("*.~[1-9]*~",name,1,1)) /* Backup file? */ continue; /* Yes, skip */ if (scan) { /* /TYPE: given? */ switch (scanfile(name,&y,nscanfile)) { /* Yes, scan the file */ case FT_BIN: if (xmode != 1) continue; break; case FT_TEXT: case FT_7BIT: case FT_8BIT: #ifdef UNICODE case FT_UTF8: case FT_UCS2: #endif /* UNICODE */ if (xmode != 0) continue; } } fp = fopen(name,"r"); /* Open */ if (!fp) /* Can't */ continue; /* Skip */ count = 0; /* Match count, this file */ fline = 0; /* Line count, this file */ while (1) { /* Loop for each line */ if (fgets(line,LINBUFSIZ,fp) == NULL) { /* Get next line */ fclose(fp); fp = NULL; debug(F100,"GREP EOF","",0); break; } fline++; /* Count this line */ line[LINBUFSIZ] = NUL; /* Make sure it's terminated */ debug(F111,"GREP",line,fline); len = (int)strlen(line); /* Get length */ while (len > 0 && (line[len-1] == '\n' || line[len-1] == '\r')) line[--len] = NUL; /* Chop off terminators */ match = ckmatch(p,line,gr_case,1+4); /* Match against pattern */ if (match && gr_excp) { if (ckmatch(grep_except,line,gr_case,1+4)) match = 0; } if (gr_noma) /* Invert match sense if requested */ match = !match; if (match) { /* Have a matching line */ mc++; /* Total match count */ count++; /* Match count this file */ if (gr_name) { /* Don't care how many lines match */ fclose(fp); /* Close the file */ fp = NULL; /* and quit the line-reading loop. */ break; } if (gr_coun || gr_noli) /* Not listing each line */ continue; /* so don't print anything now. */ if (wild) { /* If searching multiple files */ fprintf(ofp,"%s:",name); /* print filename. */ len += (int)strlen(name) + 1; } if (gr_nums) { /* If line numbers wanted */ char nbuf[32]; len += ckmakmsg(nbuf,32,ckitoa(fline),":",NULL,NULL); fprintf(ofp,"%s",nbuf); } if (cmd_rows > 0 && cmd_cols > 0) sline += (len / cmd_cols) + 1; fprintf(ofp,"%s\n",line); /* Print the line. */ if (sline > cmd_rows - 3) { if (!askmore()) goto xgrep; else sline = 0; } } } if (!gr_noli) { /* If not not listing... */ x = 0; if (gr_coun) { /* Show match count only */ fprintf(ofp,"%s:%d\n",name,count); x++; } else if (gr_name && count > 0) { /* Show name only */ fprintf(ofp,"%s\n",name); x++; } if (x > 0) { if (++sline > cmd_rows - 3) { if (!askmore()) goto xgrep; else sline = 0; } } } bigcount += count; /* Overall count */ } xgrep: #ifndef NOSPL if (gr_coun && cv) { /* /COUNT:blah */ addmac(cv,ckitoa(bigcount)); /* set the variable */ makestr(&cv,NULL); /* free this */ } #endif /* NOSPL */ if (fp) fclose(fp); /* close input file if still open */ if (ofp != stdout) { /* Close any output file */ if (ofp) fclose(ofp); ofp = stdout; } return(success = mc ? 1 : 0); } /* System-independent directory */ static char ** dirlist = NULL; static int ndirlist = 0; static VOID freedirlist() { if (dirlist) { int i; for (i = 0; i < ndirlist; i++) { if (dirlist[i]) free(dirlist[i]); } free((char *)dirlist); dirlist = NULL; } ndirlist = 0; } static struct keytab dirswtab[] = { /* DIRECTORY command switches */ { "/after", DIR_AFT, CM_ARG }, { "/all", DIR_ALL, 0 }, #ifndef NOSPL { "/array", DIR_ARR, CM_ARG }, #endif /* NOSPL */ { "/ascending", DIR_ASC, 0 }, { "/backup", DIR_BUP, 0 }, { "/before", DIR_BEF, CM_ARG }, { "/brief", DIR_BRF, 0 }, { "/count", DIR_COU, CM_ARG }, { "/descending", DIR_DSC, CM_INV }, { "/directories", DIR_DIR, 0 }, { "/dotfiles", DIR_DOT, 0 }, { "/englishdate", DIR_DAT, 0 }, { "/except", DIR_EXC, CM_ARG }, { "/files", DIR_FIL, 0 }, { "/heading", DIR_HDG, 0 }, { "/isodate", DIR_ISO, 0 }, { "/larger-than", DIR_LAR, CM_ARG }, #ifdef CKSYMLINK { "/followlinks", DIR_LNK, 0 }, #endif /* CKSYMLINK */ { "/message", DIR_MSG, CM_ARG }, { "/nobackupfiles",DIR_NOB, 0 }, { "/nodotfiles", DIR_NOD, 0 }, #ifdef CKSYMLINK { "/nofollowlinks",DIR_NLK, 0 }, #endif /* CKSYMLINK */ { "/noheading", DIR_NOH, 0 }, #ifdef CKSYMLINK { "/nolinks", DIR_NOL, 0 }, #endif /* CKSYMLINK */ { "/nomessage", DIR_NOM, 0 }, #ifdef CK_TTGWSIZ { "/nopage", DIR_NOP, 0 }, #endif /* CK_TTGWSIZ */ #ifdef RECURSIVE { "/norecursive", DIR_NOR, 0 }, #else #ifdef VMS { "/norecursive", DIR_NOR, 0 }, #else #ifdef datageneral { "/norecursive", DIR_NOR, 0 }, #endif /* datageneral */ #endif /* VMS */ #endif /* RECURSIVE */ { "/nosort", DIR_NOS, 0 }, { "/not-after", DIR_NAF, CM_ARG }, { "/not-before", DIR_NBF, CM_ARG }, { "/not-since", DIR_NAF, CM_INV|CM_ARG }, { "/noxfermode", DIR_NOT, 0 }, { "/output", DIR_OUT, CM_ARG }, #ifdef CK_TTGWSIZ { "/page", DIR_PAG, 0 }, #endif /* CK_TTGWSIZ */ #ifdef RECURSIVE { "/recursive", DIR_REC, 0 }, #else #ifdef VMS { "/recursive", DIR_REC, 0 }, #else #ifdef datageneral { "/recursive", DIR_REC, 0 }, #endif /* datageneral */ #endif /* VMS */ #endif /* RECURSIVE */ { "/reverse", DIR_DSC, 0 }, { "/since", DIR_AFT, CM_ARG|CM_INV }, { "/smaller-than",DIR_SMA, CM_ARG }, { "/sort", DIR_SRT, CM_ARG }, { "/summary", DIR_SUM, 0 }, { "/top", DIR_TOP, CM_ARG }, { "/type", DIR_BIN, CM_ARG }, { "/xfermode", DIR_TYP, 0 }, { "/verbose", DIR_VRB, 0 }, { "",0,0 } }; static int ndirswtab = (sizeof(dirswtab) / sizeof(struct keytab)) - 1; static struct keytab dirsort[] = { /* DIRECTORY /SORT: options */ { "date", DIRS_DT, 0 }, { "name", DIRS_NM, 0 }, { "size", DIRS_SZ, 0 } }; static int ndirsort = (sizeof(dirsort) / sizeof(struct keytab)); static int dir_date = -1; /* Option defaults (-1 means none) */ static int dir_page = -1; static int dir_verb = 1; static int dir_msg = -1; #ifdef VMS static int dir_sort = -1; /* Names are already sorted in VMS */ static int dir_rvrs = -1; #else static int dir_sort = 1; /* Sort by default */ static int dir_rvrs = 0; /* Not in reverse */ #endif /* VMS */ static int dir_skey = DIRS_NM; /* By name */ #ifdef RECURSIVE static int dir_recu = -1; #endif /* RECURSIVE */ static int dir_mode = -1; static int dir_show = -1; /* Show all files by default */ int dir_dots = -1; /* Except dot files */ int dir_back = 1; int dir_head = 0; static char * dirmsg = NULL; static int dirmsglen = 0; #ifndef NOSHOW VOID showdiropts() { int x = 0; extern int optlines; prtopt(&optlines,"DIRECTORY"); if (dir_show > 0) { prtopt(&optlines,(dir_show == 1) ? "/FILES" : ((dir_show == 2) ? "/DIRECTORIES" : "/ALL")); x++; } else { prtopt(&optlines,"/ALL"); x++; } if (dir_verb > -1) { prtopt(&optlines,dir_verb ? "/VERBOSE" : "/BRIEF"); x++; } if (dir_page > -1) { prtopt(&optlines,dir_page ? "/PAGE" : "/NOPAGE"); x++; } if (dir_date > -1) { prtopt(&optlines,dir_date ? "/ENGLISHDATE" : "/ISODATE"); x++; } if (dir_dots > -1) { prtopt(&optlines,dir_dots ? "/DOTFILES" : "/NODOTFILES"); x++; } if (dir_back > -1) { prtopt(&optlines,dir_back ? "/BACKUP" : "/NOBACKUP"); x++; } if (dir_head > -1) { prtopt(&optlines,dir_head ? "/HEADING" : "/NOHEADING"); x++; } #ifdef RECURSIVE if (dir_recu > -1) { prtopt(&optlines,dir_recu ? "/RECURSIVE" : "/NORECURSIVE"); x++; } #endif /* RECURSIVE */ if (dir_mode > -1) { prtopt(&optlines,dir_mode ? "/XFERMODE" : "/NOXFERMODE"); x++; } if (dir_sort == 0) { x++; prtopt(&optlines,"/NOSORT "); } else if (dir_sort > 0) { x++; if (dir_skey == DIRS_NM) s = "/SORT:NAME"; else if (dir_skey == DIRS_SZ) s = "/SORT:SIZE"; else if (dir_skey == DIRS_DT) s = "/SORT:DATE"; prtopt(&optlines,s); } if (dir_rvrs > -1) { prtopt(&optlines,dir_rvrs ? "/REVERSE" : "/ASCENDING"); x++; } if (dir_msg > -1) { if (dir_msg == 0) { prtopt(&optlines,"/NOMESSAGE"); } else { ckmakmsg(tmpbuf,TMPBUFSIZ,"/MESSAGE:{",dirmsg,"}",NULL); prtopt(&optlines,tmpbuf); } x++; } if (!x) prtopt(&optlines,"(no options set)"); prtopt(&optlines,""); } #endif /* NOSHOW */ int setdiropts() { /* Set DIRECTORY option defaults */ int xb = -1, xv = -1, xp = -1, xd = -1, xh = -1, xf = -1; int xk = -1, xr = -1, xs = -1, xx = -1, xm = -1, xa = -1, xg = -1; int getval; char c; while (1) { if ((y = cmswi(dirswtab,ndirswtab,"Switch","",xxstring)) < 0) { if (y == -3) break; else return(y); } c = cmgbrk(); if ((getval = (c == ':' || c == '=')) && !(cmgkwflgs() & CM_ARG)) { printf("?This switch does not take an argument\n"); return(-9); } if (!getval && (cmgkwflgs() & CM_ARG)) { printf("?This switch requires an argument\n"); return(-9); } switch (y) { case DIR_BRF: xv = 0; break; case DIR_VRB: xv = 1; break; case DIR_PAG: xp = 1; break; case DIR_NOP: xp = 0; break; case DIR_ISO: xd = 0; break; case DIR_DAT: xd = 1; break; case DIR_HDG: xh = 1; break; case DIR_NOH: xh = 0; break; case DIR_DOT: xf = 1; break; case DIR_NOD: xf = 0; break; case DIR_ALL: xa = 3; break; case DIR_DIR: xa = 2; break; case DIR_FIL: xa = 1; break; case DIR_SRT: x = DIRS_NM; if (getval) if ((x = cmkey(dirsort,ndirsort,"Sort key","name",xxstring)) < 0) return(x); xk = x; xs = 1; break; case DIR_NOS: xs = 0; break; case DIR_ASC: xx = 0; break; case DIR_DSC: xx = 1; break; case DIR_REC: xr = 1; break; case DIR_NOR: xr = 0; break; case DIR_TYP: xm = 1; break; case DIR_NOT: xm = 0; break; case DIR_BUP: xb = 1; break; case DIR_NOB: xb = 0; break; case DIR_NOM: xg = 0; break; case DIR_MSG: if (getval) if ((x = cmfld("Message to append to each line", "", &s, xxstring )) < 0) return(x); xg = 1; ckstrncpy(tmpbuf,brstrip(s),TMPBUFSIZ); break; default: printf("?This option can not be set\n"); return(-9); } } if ((x = cmcfm()) < 0) /* Get confirmation */ return(x); if (xv > -1) dir_verb = xv; /* Confirmed, save defaults */ if (xp > -1) dir_page = xp; if (xd > -1) dir_date = xd; if (xh > -1) dir_head = xh; if (xs > -1) dir_sort = xs; if (xk > -1) dir_skey = xk; if (xx > -1) dir_rvrs = xx; if (xf > -1) dir_dots = xf; if (xa > -1) dir_show = xa; if (xm > -1) dir_mode = xm; if (xb > -1) dir_back = xb; #ifdef RECURSIVE if (xr > -1) dir_recu = xr; #endif /* RECURSIVE */ if (xg > -1) dir_msg = xg; if (xg > 0) makestr(&dirmsg,tmpbuf); return(success = 1); } int domydir(cx) int cx; { /* Internal DIRECTORY command */ extern char *months[]; #ifdef VMS _PROTOTYP( char * zrelname, (char *,char *) ); char * cdp = NULL; #endif /* VMS */ char name[CKMAXPATH+1], outfile[CKMAXPATH+1], *p = NULL, c = NUL; char linebuf[CKMAXPATH+256]; char * mstr = NULL, * dstr = NULL, * s2 = NULL, * cv = NULL; CK_OFF_T len = (CK_OFF_T)0, nbytes = (CK_OFF_T)0; CK_OFF_T minsize = (CK_OFF_T)-1, maxsize = (CK_OFF_T)-1; long ndirs = 0, nfiles = 0, nmatches = 0; int verbose = 0, wild = 0, page = 0, n = 0, engdate = 0, summary = 0; int heading = 0, xsort = 0, reverse = 0, sortby = 0, msg = 0; int k, i = 0, x = 0, nx = 0, skey = 0, dlen = 0, itsadir = 0; int show = 3, xfermod = 0, backup = 1, rc = 0, getval = 0; int touch = 0; int fs = 0; int multiple = 0; int cmifn1 = 1, cmifn2 = 0; int dir_top = 0, dir_cou = 0; int dontshowlinks = 0; int dontfollowlinks = 0; int arrayindex = -1; struct FDB sw, fi, fl; char dbuf[32], xbuf[32]; #ifndef NOSPL char array = NUL; char ** ap = NULL; #endif /* NOSPL */ char * dir_aft = NULL, * dir_bef = NULL, * dir_naf = NULL, * dir_nbf = NULL, * dir_exc = NULL; char * xlist[16]; debug(F101,"domydir cx","",cx); g_matchdot = matchdot; /* Save global matchdot setting */ #ifdef COMMENT nolinks = 2; /* (it should already be 2) */ #endif /* COMMENT */ outfile[0] = NUL; /* No output file yet */ if (ofp != stdout) { /* In case of previous interruption */ if (ofp) fclose(ofp); ofp = stdout; } for (i = 0; i < 16; i++) xlist[i] = NULL; dir_top = 0; name[0] = NUL; freedirlist(); /* In case not freed last time */ page = dir_page > -1 ? dir_page : xaskmore; /* Set option defaults */ engdate = dir_date > -1 ? dir_date : 0; verbose = dir_verb > -1 ? dir_verb : 1; heading = dir_head > -1 ? dir_head : 0; xsort = dir_sort > -1 ? dir_sort : 0; sortby = dir_skey > -1 ? dir_skey : 0; reverse = dir_rvrs > -1 ? dir_rvrs : 0; msg = dir_msg > -1 ? dir_msg : 0; #ifdef UNIXOROSK if (dir_dots > -1) matchdot = dir_dots; #endif /* UNIXOROSK */ xfermod = dir_mode > -1 ? dir_mode : 0; backup = dir_back > -1 ? dir_back : 1; #ifdef RECURSIVE recursive = dir_recu > -1 ? dir_recu : 0; #endif /* RECURSIVE */ show = dir_show > -1 ? dir_show : 3; if (cx == XXWDIR) { /* WDIRECTORY */ debug(F100,"domydir WDIRECTORY","",0); reverse = 1; /* Reverse chronological order */ xsort = 1; sortby = DIRS_DT; } else if (cx == XXHDIR) { /* HDIRECTORY */ debug(F100,"domydir HDIRECTORY","",0); reverse = 1; /* Reverse order by size */ xsort = 1; sortby = DIRS_SZ; } else if (cx == XXTOUC) { touch = 1; verbose = 0; } #ifdef CK_TTGWSIZ #ifdef OS2 ttgcwsz(); /* Screen length for more-prompting */ #else /* OS2 */ /* Check whether window size changed */ if (ttgwsiz() > 0) { if (tt_rows > 0 && tt_cols > 0) { cmd_rows = tt_rows; cmd_cols = tt_cols; } } #endif /* OS2 */ #endif /* CK_TTGWSIZ */ diractive = 1; cmifn1 = nolinks | 1; /* 1 = files or directories */ cmifn2 = 0; /* 0 = not directories only */ again: cmfdbi(&sw, /* First FDB - command switches */ _CMKEY, /* fcode */ "Enter or Return to confirm the command, or\n\ file specification, or switch", "", /* default */ "", /* addtl string data */ ndirswtab, /* addtl numeric data 1: tbl size */ 4, /* addtl numeric data 2: 4 = cmswi */ xxstring, /* Processing function */ dirswtab, /* Keyword table */ &fi /* Pointer to next FDB */ ); cmfdbi(&fi, /* 2nd FDB - filespec to match */ _CMIFI, /* fcode */ "File specification", /* hlpmsg */ #ifdef datageneral "+", /* Default filespec is wildcard */ #else /* that matches all files... */ #ifdef VMS "*.*", #else "*", #endif /* VMS */ #endif /* datageneral */ "", /* addtl string data */ cmifn1, cmifn2, /* 1 = only dirs; 0 files or dirs */ xxstring, NULL, &fl ); cmfdbi(&fl, /* Anything that doesn't match */ _CMFLD, /* fcode */ "", /* hlpmsg */ "", /* default */ "", /* addtl string data */ 0, /* addtl numeric data 1 */ 0, /* addtl numeric data 2 */ xxstring, NULL, NULL ); while (1) { /* Parse 0 or more switches */ x = cmfdb(&sw); /* Parse something */ debug(F101,"domydir cmfdb","",x); if (x < 0) return(x); if (cmresult.fcode != _CMKEY) /* Break out if not a switch */ break; c = cmgbrk(); if ((getval = (c == ':' || c == '=')) && !(cmgkwflgs() & CM_ARG)) { printf("?This switch does not take an argument\n"); return(-9); } k = cmresult.nresult; if (!getval && (cmgkwflgs() & CM_ARG) && k != DIR_TOP && k != DIR_COU) { printf("?This switch requires an argument\n"); return(-9); } switch (k) { case DIR_COU: { dir_cou++; if (getval) { if ((x = cmfld("Variable for result","",&s,NULL)) < 0) return(x); makestr(&cv,s); } break; } case DIR_BRF: verbose = 0; break; case DIR_VRB: verbose = 1; break; #ifdef CK_TTGWSIZ case DIR_PAG: page = 1; break; case DIR_NOP: page = 0; break; #endif /* CK_TTGWSIZ */ case DIR_ISO: engdate = 0; break; case DIR_DAT: engdate = 1; break; case DIR_HDG: heading = 1; break; case DIR_NOH: heading = 0; break; #ifdef UNIXOROSK case DIR_DOT: matchdot = 1; break; case DIR_NOD: matchdot = 0; break; #endif /* UNIXOROSK */ case DIR_ALL: show = 3; cmifn1 |= 1; cmifn2 = 0; goto again; case DIR_DIR: show = 2; cmifn1 |= 1; cmifn2 = 1; goto again; case DIR_FIL: show = 1; cmifn1 &= ~(1); cmifn2 = 0; goto again; case DIR_SRT: x = DIRS_NM; if (c == ':' || c == '=') if ((x = cmkey(dirsort,ndirsort,"Sort key","name",xxstring)) < 0) return(x); xsort = 1; sortby = x; break; case DIR_BUP: backup = 1; fs++; break; case DIR_NOB: backup = 0; fs++; break; case DIR_NOS: xsort = 0; break; case DIR_ASC: reverse = 0; break; case DIR_DSC: reverse = 1; break; #ifdef RECURSIVE case DIR_REC: recursive = 1; diractive = 1; break; case DIR_NOR: recursive = 0; diractive = 0; break; #endif /* RECURSIVE */ case DIR_TYP: xfermod = 1; break; case DIR_NOT: xfermod = 0; break; #ifdef CKSYMLINK case DIR_LNK: /* Follow links */ #ifdef COMMENT /* A command switch shouldn't be setting a global value! */ nolinks = 0; #endif /* COMMENT */ cmifn1 &= ~(2); dontfollowlinks = 0; goto again; case DIR_NLK: /* Don't follow links */ #ifdef COMMENT nolinks = 2; #endif /* COMMENT */ cmifn1 &= ~(2); dontfollowlinks = 1; goto again; case DIR_NOL: /* Don't show links at all */ dontshowlinks = 1; goto again; #endif /* CKSYMLINK */ case DIR_NOM: msg = 0; break; case DIR_MSG: if (c == ':' || c == '=') if ((x = cmfld("Message to append to each line", "", &s, xxstring )) < 0) return(x); msg = 1; ckstrncpy(tmpbuf,brstrip(s),TMPBUFSIZ); break; case DIR_SMA: case DIR_LAR: { CK_OFF_T y; if (!getval) break; if ((x = cmnumw("File size in bytes","0",10,&y,xxstring)) < 0) return(x); fs++; show = 1; switch (cmresult.nresult) { case DIR_SMA: minsize = y; break; case DIR_LAR: maxsize = y; break; } break; } case DIR_TOP: dir_top = 10; if (!getval) break; if ((x = cmnum("How many lines to show","10",10,&y,xxstring))< 0) return(x); dir_top = y; break; #ifndef NOSPL case DIR_ARR: if (c != ':' && c != '=') { printf("?Array name required\n"); return(-9); } if ((x = cmfld("Array name (a single letter will do)", "", &s, NULL )) < 0) { if (x == -3) { printf("?Array name required\n"); return(-9); } else return(x); } if (!*s) { printf("?Array name required\n"); return(-9); } s2 = s; if (*s == CMDQ) s++; if (*s == '&') s++; if (!isalpha(*s)) { printf("?Bad array name - \"%s\"\n",s2); return(-9); } array = *s++; if (isupper(array)) array = tolower(array); if (*s && (*s != '[' || *(s+1) != ']')) { printf("?Bad array name - \"%s\"\n",s2); return(-9); } break; #endif /* NOSPL */ case DIR_AFT: case DIR_BEF: case DIR_NAF: case DIR_NBF: if (!getval) break; if ((x = cmdate("File-time","",&s,0,xxstring)) < 0) { if (x == -3) { printf("?Date-time required\n"); rc = -9; } else rc = x; goto xdomydir; } fs++; switch (k) { case DIR_AFT: makestr(&dir_aft,s); break; case DIR_BEF: makestr(&dir_bef,s); break; case DIR_NAF: makestr(&dir_naf,s); break; case DIR_NBF: makestr(&dir_nbf,s); break; } break; case DIR_EXC: if (!getval) break; if ((x = cmfld("Pattern","",&s,xxstring)) < 0) { if (x == -3) { printf("?Pattern required\n"); rc = -9; } else rc = x; goto xdomydir; } fs++; makestr(&dir_exc,s); break; case DIR_SUM: summary = 1; break; case DIR_BIN: { extern struct keytab txtbin[]; extern int xfiletype; if (!getval) break; if ((x = cmkey(txtbin,3,"","all",xxstring)) < 0) { rc = x; goto xdomydir; } if (x == 2) { xfiletype = -1; } else { xfiletype = x; fs = 1; } break; } case DIR_OUT: if ((x = cmofi("File for directory listing","",&s,xxstring)) < 0) return(x); ckstrncpy(outfile,s,CKMAXPATH+1); break; default: printf("?Sorry, not implemented yet - \"%s\"\n", atmbuf); goto xdomydir; } } ckstrncpy(line,cmresult.sresult,LINBUFSIZ); /* Safe copy of filespec */ /* ^^^ START MULTIPLE */ while (!touch) { x = cmfld("Another filespec or Enter","",&s,xxstring); if (x == -3) break; if (x < 0) return(x); ckstrncat(line,",",LINBUFSIZ); ckstrncat(line,s,LINBUFSIZ); multiple++; } ckmakmsg(tmpbuf,TMPBUFSIZ,"{",line,"}",NULL); ckstrncpy(line,tmpbuf,LINBUFSIZ); cmresult.nresult = 1; cmresult.fcode = _CMIFI; /* ^^^ END MULTIPLE */ s = line; if ((x = cmcfm()) < 0) /* Get confirmation */ return(x); /* Command is TOUCH and file doesn't exist. */ if (touch) { /* TOUCH */ if ((cmresult.fcode == _CMIFI && zchki(s) == (CK_OFF_T)-1)) { FILE * fp; s = brstrip(s); if (!iswild(s)) { #ifdef UNIX if (s[0] == '~') s = tilde_expand(s); #endif /* UNIX */ fp = fopen(s,"w"); /* Create file */ if (!fp) { printf("?TOUCH %s: %s\n",s,ck_errstr()); rc = -9; goto xdomydir; } fclose(fp); cx = XXDIR; /* Now maybe list it. */ multiple++; /* Force new directory scan */ } } } else if (cmresult.fcode != _CMIFI) { /* Nothing matched */ /* Note - this never gets executed because after the "begin multiple" hack above, the result is always _CMIFI). */ char * m; if (*s == '/') #ifdef UNIXOROSK m = "does not match switch or name of accessible file"; #else #ifdef OS2 m = "does not match switch or name of accessible file"; #else m = "no switches match"; #endif /* OS2 */ #endif /* UNIXOROSX */ else m = "not found or not accessible"; printf("\"%s\" - %s\n",s,m); rc = -9; goto xdomydir; } #ifdef COMMENT /* This can't be right because it's based on _CMCFM */ wild = cmresult.nresult; /* Wildcard was given? */ debug(F111,"domydir cmifi2",s,wild); #else wild = 0; #endif /* COMMENT */ if (outfile[0]) { /* If an output file was specified */ ofp = fopen(outfile,"w"); /* open it */ if (!ofp) { printf("?Can't open output file %s: %s\n",outfile,ck_errstr()); ofp = stdout; return(-9); } page = 0; } #ifdef OS2 if (!wild) { if (zchki(s) == -2) { /* Found a directory */ p = s + (int)strlen(s) - 1; /* Yes */ if (*p == '\\' || *p == '/') strcat(s, "*"); else if (*p == ':') strcat(s, "./*"); else strcat(s, "/*"); wild = 1; /* Now it's wild */ } } #else if (!wild) if (isdir(s)) { /* Is it a directory? */ p = s + (int)strlen(s) - 1; /* Yes */ #ifdef VMS { /* Convert from FOO.DIR;1 to [x.FOO] if necessary */ char buf[CKMAXPATH+1]; debug(F000,"domydir directory 0",s,*p); if (cvtdir(s,buf,CKMAXPATH) > 0) ckstrncpy(line,buf,LINBUFSIZ); } #endif /* VMS */ debug(F000,"domydir directory 1",s,*p); #ifdef VMS if (*p == ']' || *p == '>' || *p == ':') strcat(s, "*.*"); #else #ifdef datageneral if (*p == ':') strcat(s, "+"); else strcat(s, ":+"); #else #ifdef STRATUS if (*p == '>') strcat(s, "*"); else strcat(s, ">*"); #endif /* STRATUS */ #endif /* datageneral */ #endif /* VMS */ wild = 1; /* Now it's wild */ debug(F000,"domydir directory 2",s,*p); } #endif /* OS2 */ #ifdef ZXREWIND /* cmifi() already called nzxpand so we can just re-use the same list. */ if (!multiple) { x = zxrewind(); /* Rewind the list */ debug(F111,"domydir zxrewind",s,x); } else { #endif /* ZXREWIND */ /* In case we gave multiple filespecs they are now in {a,b,c} list format. Which is a valid wildcard. We pass it to nzxpand() to get back the list of files that match. This is fine for DIRECTORY but it's not find for TOUCH because we want TOUCH to see those names so it can create the files. So for now at least, if TOUCH is to be used to create files -- as opposed to changing the timestamps of existing files -- it can only do one file at a time. */ nzxopts = (show == ZX_DIRONLY) ? ZX_DIRONLY : (show == ZX_FILONLY ? ZX_FILONLY : 0); if (matchdot) nzxopts |= ZX_MATCHDOT; if (recursive) nzxopts |= ZX_RECURSE; x = nzxpand(s,nzxopts); /* Expand file list */ debug(F111,"domydir nzxpand",s,x); #ifdef ZXREWIND } #endif /* ZXREWIND */ #ifndef NOSPL if (array) { int n, xx; n = (x < 0) ? 0 : x; if ((xx = dclarray(array,n)) < 0) { printf("?Array declaration failure\n"); rc = -9; goto xdomydir; } arrayindex = xx; ap = a_ptr[xx]; /* Pointer to list of elements */ if (ap) /* Set element 0 to dimension */ makestr(&(ap[0]),"0"); /* which so far is zero */ if (n < 1) { /* No files matched, done. */ rc = 0; goto xdomydir; } } else #endif /* NOSPL */ if (!touch && x < 1) { #ifdef CKROOT extern int ckrooterr; if (ckrooterr) printf("?Off limits: %s\n",s); else #endif /* CKROOT */ if (x == 0 && isdir(s)) printf("?Empty directory - \"%s\"\n", s); else printf("?%s %s match - \"%s\"\n", (x == 0) ? "No" : "Too many", (show == 2) ? "directories" : "files", brstrip(s) ); rc = -9; goto xdomydir; } nx = x; /* Remember how many files */ if (nx < 2) xsort = 0; /* Skip sorting if none or one */ if (msg) { makestr(&dirmsg,tmpbuf); dirmsglen = strlen(tmpbuf); } #ifdef VMS cdp = zgtdir(); /* Get current directory */ debug(F110,"domydir VMS zgtdir",cdp,0); #endif /* VMS */ if (xsort && verbose) { /* If sorting, allocate space */ if (!(dirlist = (char **) malloc((x + 1) * sizeof(char **)))) { if (!quiet) { printf("* Warning: Failure to allocate memory for sorting.\n"); printf("* Will proceed without sorting...\n"); } xsort = 0; } debug(F101,"domydir sort malloc","",xsort); } /* Display the listing */ #ifndef NOSPL if (array) /* Storing instead of printing */ heading = 0; #endif /* NOSPL */ if (heading) { /* If /HEADING print heading */ zfnqfp(s,TMPBUFSIZ,tmpbuf); fprintf(ofp,"\nDirectory of %s\n\n",tmpbuf); n += 3; } if (page > -1) /* Paging */ xaskmore = page; if (!verbose && !touch) { /* /BRIEF */ if (outfile[0]) { /* To file */ int k = 0; znext(name); while (name[0]) { /* One line per file */ k++; if (fs) if (fileselect(name, dir_aft,dir_bef,dir_naf,dir_nbf, minsize,maxsize,!backup,16,xlist) < 1) { znext(name); continue; } fprintf(ofp,"%s\n",name); znext(name); } if (heading) fprintf(ofp,"Files: %d\n\n",k); rc = 1; goto xdomydir; } else { rc = xfilhelp(x,"","",n,0,1, dir_aft,dir_bef,dir_naf,dir_nbf, minsize,maxsize,!backup,16,xlist); if (rc < 0) goto xdomydir; if (heading && rc > 0) fprintf(ofp,"Files: %d\n\n",x); /* (Might scroll a line or 2) */ rc = 1; goto xdomydir; } } ndirs = nfiles = 0L; /* Initialize counters */ nbytes = (CK_OFF_T)0; if (dir_exc) /* Have exception list? */ makelist(dir_exc,xlist,16); /* Yes, convert to array */ diractive = 1; znext(name); /* Get next file */ while (name[0]) { /* Loop for each file */ if (fs) if (fileselect(name, dir_aft,dir_bef,dir_naf,dir_nbf, minsize,maxsize,!backup,16,xlist) < 1) { znext(name); continue; } len = zgetfs(name); /* Get file length */ debug(F111,"domydir zgetfs",name,len); #ifdef VMSORUNIX itsadir = zgfs_dir; /* See if it's a directory */ #else itsadir = (len == (CK_OFF_T)-2 || isdir(name)); #endif /* VMSOUNIX */ debug(F111,"domydir itsadir",name,itsadir); if ((itsadir && (show == 1)) || (!itsadir && (show == 2))) { znext(name); continue; } /* Get here when we know we have selected this file */ nmatches++; if (itsadir) { /* Accumulate totals for summary */ ndirs++; } else { nfiles++; nbytes += len; } dstr = NULL; if (cx == XXTOUC) { /* Command was TOUCH, not DIRECTORY */ char * filename; struct zattr xx; dstr = ckcvtdate("",0); xx.date.val = dstr; xx.date.len = (int)strlen(xx.date.val); xx.lprotect.len = 0; debug(F110,"domydir touch",name,0); debug(F110,"domydir touch",dstr,0); if (zstime(name,&xx,0) < 0) { printf("?TOUCH %s: %s\n",name,ck_errstr()); rc = -9; goto xdomydir; } if (!verbose) { /* No listing so skip detail */ znext(name); continue; } } if (summary) { /* Summary only, no detail */ znext(name); continue; } #ifndef NOSPL if (array) { debug(F111,"domydir array",name,nfiles); if (ap) makestr(&(ap[nmatches]),name); znext(name); continue; } #endif /* NOSPL */ /* NOTE: The sprintf's in this routine should be safe. They involve permission strings, date/time strings, and filenames, all of which have known maximum lengths; none of these items is input from users. The destination buffers are large enough to hold maximum sizes for any and all items. NOTE 2: If command was TOUCH, dstr was already set just above. */ if (!dstr) { /* Get file's modification date/time */ dstr = zfcdat(name); debug(F111,"domydir zcfdat",dstr,0); } if (!dstr) dstr = ""; { /* Note that zfcdat() always returns "" or yyyymmdd hh:mm:ss, so any warnings about possible out-of-bounds dstr[] array refs do not apply. This block of code is to stifle the warnings and also allows for any out-of-spec zfcdat() implementations. */ int x; char * p = "00000000 00:00:00"; x = ckstrncpy(xbuf,dstr,32); if (x < 17) ckstrncpy(&xbuf[x],p+x,32-x); dstr = xbuf; } if (engdate) { /* English date requested? */ short month, day, year, hour, minute, seconds; month = (dstr[4]-48)*10 + (dstr[5]-48); mstr = (month > 0 && month <= 12) ? months[month-1] : "xxx"; day = (dstr[6]-48)*10 + (dstr[7]-48); year = (((dstr[0]-48)*10 + (dstr[1]-48))*10 + (dstr[2]-48))*10 + (dstr[3]-48); hour = (dstr[9]-48)*10 + (dstr[10]-48); minute = (dstr[12]-48)*10 + (dstr[13]-48); seconds = (dstr[15]-48)*10 + (dstr[16]-48); sprintf(dbuf, /* SAFE */ "%2d-%s-%4d %02d:%02d:%02d", day,mstr,year,hour,minute,seconds ); dstr = dbuf; } else { /* ISO date */ dbuf[0] = dstr[0]; /* yyyy */ dbuf[1] = dstr[1]; dbuf[2] = dstr[2]; dbuf[3] = dstr[3]; dbuf[4] = '-'; dbuf[5] = dstr[4]; /* mm (numeric) */ dbuf[6] = dstr[5]; dbuf[7] = '-'; dbuf[8] = dstr[6]; /* dd */ dbuf[9] = dstr[7]; strcpy(dbuf+10,dstr+8); /* hh:mm:ss */ dstr = dbuf; } dlen = strlen(dbuf); /* Length of date */ name[CKMAXPATH] = NUL; #ifdef CK_PERMS #ifdef VMSORUNIX p = ziperm(name); /* Get permissions */ debug(F110,"ziperm perms",p,0); #else p = zgperm(name); debug(F110,"zgperm perms",p,0); #endif /* VMSORUNIX */ #else p = NULL; debug(F110,"NULL perms",p,0); #endif /* CK_PERMS */ #ifdef VMS /* Get relative name to save space -- VMS fullnames are long... */ ckstrncpy(name,zrelname(name,cdp),CKMAXPATH); #endif /* VMS */ if (itsadir && len < (CK_OFF_T)0) { /* Directory */ #ifdef VMS sprintf(linebuf,"%-22s%-10s %s %s",p,"",dstr,name); #else if (p) sprintf(linebuf,"%10s%-10s %s %s",p,"",dstr,name); else sprintf(linebuf,"%-10s %s %s", "", dstr, name); #endif /* VMS */ } else { /* Regular file */ #ifdef VMS sprintf(linebuf,"%-22s%10s %s %s", p, ckfstoa(len), dstr, name); #else if (p) sprintf(linebuf,"%10s%10s %s %s", p, ckfstoa(len), dstr, name); else sprintf(linebuf,"%10s %s %s", ckfstoa(len), dstr, name); #endif /* VMS */ } #ifdef UNIX #ifdef CKSYMLINK if (zgfs_link) { /* If it's a symlink */ if (dontshowlinks) { /* If /NOLINKS don't show it */ znext(name); continue; } } if (zgfs_link && !dontfollowlinks) { /* Symlink and following links */ int n, m; /* Show what the link points to */ extern char linkname[]; n = strlen(linebuf); m = strlen(linkname) + n; if (m < CKMAXPATH + 58) strcpy(linebuf+n, " -> "); /* safe (checked) */ if (m + 4 < CKMAXPATH - 58) strcpy(linebuf+n+4, linkname); /* safe (checked) */ } else #endif /* CKSYMLINK */ #endif /* UNIX */ if (xfermod) { /* Show transfer mode */ int i, x, y; char * s = ""; y = -1; x = scanfile(name,&y,nscanfile); switch (x) { case FT_TEXT: s = " (T)"; break; case FT_7BIT: s = " (T)(7BIT)"; break; case FT_8BIT: s = " (T)(8BIT)"; break; #ifdef UNICODE case FT_UTF8: s = " (T)(UTF8)"; break; case FT_UCS2: s = y ? " (T)(UCS2LE)" : " (T)(UCS2BE)"; break; #endif /* UNICODE */ case FT_BIN: s = " (B)"; break; } if (!*s) { s = binary ? " (B)" : " (T)"; } if (*s) { int n; n = strlen(linebuf); if (n + 4 < CKMAXPATH - 58) strcpy(linebuf+n, s); /* safe (checked) */ } } if (msg && dirmsg) { int n; n = strlen(linebuf); if (n + dirmsglen + 2 < CKMAXPATH) sprintf((char *)(linebuf+n)," %s", dirmsg); /* SAFE */ } if (xsort) { /* Sorting - save line */ i = strlen(linebuf); if ((ndirlist >= nx) || !(dirlist[ndirlist] = (char *)malloc(i+1))) { printf("?Memory allocation error - try /NOSORT\n"); rc = -9; goto xdomydir; } strcpy(dirlist[ndirlist],linebuf); /* safe */ ndirlist++; } znext(name); /* Peek ahead to next file */ if (!xsort) { if (!touch || (touch && verbose)) fprintf(ofp,"%s\n",linebuf); if (page && (name[0] || heading)) { /* If /PAGE */ if (cmd_cols > 0) { int x = strlen(linebuf); int y; y = (x % cmd_cols) ? 1 : 0; n += x / cmd_cols + y; } else { n++; } #ifdef CK_TTGWSIZ if (n > (cmd_rows - 3)) { /* Do more-prompting */ if (!askmore()) { rc = 0; goto xdomydir; } else n = 0; } #endif /* CK_TTGWSIZ */ } } } #ifndef NOSPL if (array) { if (ap) makestr(&(ap[0]),ckitoa(nmatches)); rc = 1; goto xdomydir; } #endif /* NOSPL */ if (xsort) { int namepos; skey = 0; #ifdef VMS namepos = dlen + 35; switch (sortby) { case DIRS_NM: skey = namepos; break; case DIRS_DT: skey = 33; break; case DIRS_SZ: skey = 21; } #else if (p) { namepos = dlen + 24; switch (sortby) { case DIRS_NM: skey = namepos; break; case DIRS_DT: skey = 22; break; case DIRS_SZ: skey = 10; } } else { namepos = dlen + 14; switch (sortby) { case DIRS_NM: skey = namepos; break; case DIRS_DT: skey = 12; break; case DIRS_SZ: skey = 0; } } #endif /* VMS */ sh_sort(dirlist,NULL,ndirlist,skey,reverse,filecase); if (dir_top > 0 && dir_top < ndirlist) ndirlist = dir_top; for (i = 0; i < ndirlist; i++) { fprintf(ofp,"%s\n",dirlist[i]); if (page && (i < ndirlist -1 || heading)) { /* If /PAGE */ if (cmd_cols > 0) { int x = strlen(dirlist[i]); int y; y = (x % cmd_cols) ? 1 : 0; n += ((int)strlen(dirlist[i]) / cmd_cols) + y; } else { n++; } #ifdef CK_TTGWSIZ if (n > (cmd_rows - 3)) { /* Do more-prompting */ if (!askmore()) { rc = 0; goto xdomydir; } else n = 0; } #endif /* CK_TTGWSIZ */ } } } if (heading || summary) { #ifdef CKFLOAT CKFLOAT gm; #endif /* CKFLOAT */ fprintf(ofp,"\n%ld director%s, %ld file%s, %s byte%s", ndirs, (ndirs == 1) ? "y" : "ies", nfiles, (nfiles == 1) ? "" : "s", ckfstoa(nbytes), (nbytes == 1) ? "" : "s" ); #ifdef CKFLOAT gm = ((CKFLOAT) nbytes ) / 1000000.0; if (gm > 1000.0) fprintf(ofp," (%0.2fGB)",(gm / 1000.0)); else if (gm >= 0.01) fprintf(ofp," (%0.2fMB)",gm); #endif /* CKFLOAD */ fprintf(ofp,"\n\n"); } else if (dir_cou && !cv) { fprintf(ofp,"\n Files: %ld\n\n",nfiles); } xdomydir: #ifndef NOSPL if (dir_cou && cv) { /* /COUNT:var */ addmac(cv,ckitoa(nfiles)); /* set the variable */ makestr(&cv,NULL); /* free this */ } if (ap) { /* If we have a result array */ if (a_dim[arrayindex] > nmatches) /* but it was not filled */ a_dim[arrayindex] = nmatches; /* adjust dimension */ } #endif /* NOSPL */ if (g_matchdot > -1) { matchdot = g_matchdot; /* Restore these... */ g_matchdot = -1; } freedirlist(); if (ofp != stdout) { /* Close any output file */ if (ofp) fclose(ofp); ofp = stdout; } if (rc > 0) success = 1; return(rc); } int dodir(cx) int cx; { /* Do the DIRECTORY command */ char *dc , *msg; #ifdef OS2 return(domydir(cx)); #else /* OS2 */ if (nopush #ifdef DOMYDIR /* Builds that domydir() by default */ || (cx == XXDIR || cx == XXLDIR || cx == XXWDIR || cx == XXHDIR || cx == XXTOUC) #endif /* DOMYDIR */ ) return(domydir(cx)); /* Built-in directory command */ /* Use the system's directory command. */ msg = (cx == XXLS) ? "Arguments for ls" : "Directory and/or file specification"; if ((x = cmtxt(msg,"",&s,xxstring)) < 0) return(x); ckstrncpy(tmpbuf,s,TMPBUFSIZ); /* Copy the filespec */ s = tmpbuf; if ((y = cmcfm()) < 0) return(y); lp = line; if (!(dc = getenv("CK_DIR"))) dc = DIRCMD; ckmakmsg(lp,LINBUFSIZ,dc," ",s,NULL); debug(F110,"DIR",line,0); #ifdef VMS conres(); #endif /* VMS */ x = zshcmd(line); #ifdef VMS concb((char)escape); #endif /* VMS */ return(success = (x < 1) ? 0 : 1); #endif /* OS2 */ } #ifndef NOSERVER #ifndef NOFRILLS /* Do the ENABLE and DISABLE commands */ int doenable(y,x) int y, x; { #ifdef CK_LOGIN if (isguest) /* IKSD: Don't let guests */ return(0); /* enable anything that's disabled */ #endif /* CK_LOGIN */ switch (x) { case EN_ALL: en_cwd = en_cpy = en_del = en_dir = en_fin = en_get = y; en_ren = en_sen = en_set = en_spa = en_typ = en_ret = y; if (!inserver) en_who = en_mai = en_pri = y; en_mkd = en_rmd = y; en_xit = y; #ifndef datageneral en_bye = y; #endif /* datageneral */ #ifndef NOPUSH if (!nopush && !inserver) en_hos = y; #endif /* NOPUSH */ #ifndef NOSPL en_asg = en_que = y; #endif /* NOSPL */ break; case EN_BYE: #ifndef datageneral /* In Data General AOS/VS Kermit can't log out its superior process. */ en_bye = y; #endif /* datageneral */ break; case EN_CPY: en_cpy = y; break; case EN_CWD: en_cwd = y; #ifdef IKSD if (inserver && y == 0) { fnrpath = PATH_OFF; fnspath = PATH_OFF; } #endif /* IKSD */ break; case EN_DEL: /* Deleting of files */ en_del = y; break; case EN_DIR: en_dir = y; break; case EN_FIN: en_fin = y; break; case EN_GET: en_get = y; break; #ifndef NOPUSH case EN_HOS: if (!nopush) en_hos = y; break; #endif /* NOPUSH */ case EN_REN: en_ren = y; break; case EN_SEN: en_sen = y; break; case EN_SET: en_set = y; break; case EN_SPA: en_spa = y; break; case EN_TYP: en_typ = y; break; case EN_WHO: en_who = y; break; #ifndef NOSPL case EN_ASG: en_asg = y; break; case EN_QUE: en_que = y; break; #endif /* NOSPL */ case EN_RET: en_del = y; break; case EN_MAI: #ifdef CK_LOGIN if (isguest && y) { printf("?Sorry, not valid for guests\n"); return(-9); } #endif /* CK_LOGIN */ en_mai = y; break; case EN_PRI: #ifdef CK_LOGIN if (isguest && y) { printf("?Sorry, not valid for guests\n"); return(-9); } #endif /* CK_LOGIN */ en_pri = y; break; case EN_MKD: en_mkd = y; break; case EN_RMD: en_rmd = y; break; case EN_XIT: en_xit = y; break; case EN_ENA: if (((y & 1) && !(en_ena & 1)) || ((y & 2) && !(en_ena & 2))) { printf("?Sorry, DISABLE ENABLE can not be undone\n"); return(-9); } else { en_ena = y; break; } default: return(-2); } return(1); } #endif /* NOFRILLS */ #endif /* NOSERVER */ #ifndef NOFRILLS static int del_lis = 0; static int del_dot = 0; static int del_hdg = 0; static int del_pag = -1; static int del_ask = 0; #ifndef NOSHOW VOID showdelopts() { int x = 0; extern int optlines; prtopt(&optlines,""); prtopt(&optlines,"DELETE"); if (del_ask > -1) { prtopt(&optlines, del_ask ? "/ASK" : "/NOASK"); x++; } #ifdef UNIXOROSK if (del_dot > -1) { prtopt(&optlines, del_dot ? "/DOTFILES" : "/NODOTFILES"); x++; } #endif /* UNIXOROSK */ if (del_lis > -1) { prtopt(&optlines, del_lis ? "/LIST" : "/NOLIST"); x++; } if (del_hdg > -1) { prtopt(&optlines, del_hdg ? "/HEADING" : "/NOHEADING"); x++; } #ifndef CK_TTGWSIZ if (del_pag > -1) { prtopt(&optlines, del_pag ? "/PAGE" : "/NOPAGE"); x++; } #endif /* CK_TTGWSIZ */ if (!x) prtopt(&optlines,"(no options set)"); prtopt(&optlines,""); } #endif /* NOSHOW */ int setdelopts() { int x_lis = -1, x_pag = -1, x_dot = -1, x_hdg = -1, x_ask = -1; int getval = 0; char c; while (1) { if ((y = cmswi(deltab,ndeltab,"Switch","",xxstring)) < 0) { if (y == -3) break; else return(y); } c = cmgbrk(); if ((getval = (c == ':' || c == '=')) && !(cmgkwflgs() & CM_ARG)) { printf("?This switch does not take an argument\n"); return(-9); } if (!getval && (cmgkwflgs() & CM_ARG)) { printf("?This switch requires an argument\n"); return(-9); } switch (y) { case DEL_DOT: x_dot = 1; break; case DEL_NOD: x_dot = 0; break; case DEL_HDG: x_hdg = 1; break; case DEL_LIS: x_lis = 1; break; case DEL_NOL: x_lis = 0; break; #ifndef CK_TTGWSIZ case DEL_PAG: x_pag = 1; break; case DEL_NOP: x_pag = 0; break; #endif /* CK_TTGWSIZ */ case DEL_QUI: x_lis = 0; break; case DEL_VRB: x_lis = 1; break; case DEL_ASK: x_ask = 1; break; case DEL_NAS: x_ask = 0; break; default: printf("?Sorry, this option can not be set\n"); return(-9); } } if ((x = cmcfm()) < 0) /* Get confirmation */ return(x); if (x_pag > -1) del_pag = x_pag; if (x_dot > -1) del_dot = x_dot; if (x_hdg > -1) del_hdg = x_hdg; if (x_lis > -1) del_lis = x_lis; if (x_ask > -1) del_ask = x_ask; return(success = 1); } #ifdef OS2 static char ** xmtchs = NULL; static int xmtchn = 0; #endif /* OS2 */ int dodel() { /* DELETE */ int i, j, k, x; int fs = 0; /* Need to call fileselect() */ int len = 0; int bad = 0; int getval = 0, asking = 0; int simulate = 0, rc = 0; CK_OFF_T minsize = -1L, maxsize = -1L; int havename = 0, confirmed = 0; int qflag = 0; int summary = 0; int deldirs = 0; int deltree = 0; int itsadir = 0; int argisdir = 0; int xmode = -1, scan = 0, skip = 0; #ifdef COMMENT int pass = 0; #endif /* COMMENT */ char c; char * deldef = ""; char safebuf[CKMAXPATH+1]; struct FDB sw, fi, fl; char * del_aft = NULL, * del_bef = NULL, * del_naf = NULL, * del_nbf = NULL, * del_exc = NULL; int x_lis = 0, /* x_dot = -1, */ x_hdg = 0; char * dxlist[8]; for (i = 0; i < 8; i++) dxlist[i] = NULL; g_matchdot = matchdot; if (del_lis > -1) x_lis = del_lis; if (del_dot > -1) matchdot = del_dot; if (del_hdg > -1) x_hdg = del_hdg; if (del_pag > -1) xaskmore = del_pag; if (del_ask > -1) asking = del_ask; diractive = 1; nolinks = 2; /* By default don't follow links */ cmfdbi(&sw, /* First FDB - command switches */ _CMKEY, /* fcode */ "File specification;\n or switch", "", /* default */ "", /* addtl string data */ ndeltab, /* addtl numeric data 1: tbl size */ 4, /* addtl numeric data 2: 4 = cmswi */ xxstring, /* Processing function */ deltab, /* Keyword table */ &fi /* Pointer to next FDB */ ); cmfdbi(&fl, /* Anything that doesn't match */ _CMFLD, /* fcode */ "", /* hlpmsg */ "", /* default */ "", /* addtl string data */ 0, /* addtl numeric data 1 */ 0, /* addtl numeric data 2 */ xxstring, NULL, NULL ); again: cmfdbi(&fi, /* 2nd FDB - file to delete */ _CMIFI, /* fcode */ "File(s) to delete", /* hlpmsg */ deldef, /* default */ "", /* addtl string data */ nolinks | deldirs, /* 0 = files, 1 = files or dirs */ 0, /* 1 = dirs only */ xxstring, NULL, &fl ); while (!havename && !confirmed) { x = cmfdb(&sw); /* Parse something */ if (x < 0) { /* Error */ if (x == -3) break; if (x == -2 || x == -9) printf("?Does not match switch or filename: \"%s\"\n",atmbuf); return(x); } if (cmresult.fcode != _CMKEY) /* Break out if not a switch */ break; c = cmgbrk(); /* Get break character */ if ((getval = (c == ':' || c == '=')) && !(cmgkwflgs() & CM_ARG)) { printf("?This switch does not take an argument\n"); rc = -9; goto xdelete; } if (!getval && (cmgkwflgs() & CM_ARG)) { printf("?This switch requires an argument\n"); rc = -9; goto xdelete; } switch (k = cmresult.nresult) { case DEL_AFT: case DEL_BEF: case DEL_NAF: case DEL_NBF: if (!getval) break; if ((x = cmdate("File-time","",&s,0,xxstring)) < 0) { if (x == -3) { printf("?Date-time required\n"); x = -9; } else rc = x; goto xdelete; } fs++; deltree = 0; switch (k) { case DEL_AFT: makestr(&del_aft,s); break; case DEL_BEF: makestr(&del_bef,s); break; case DEL_NAF: makestr(&del_naf,s); break; case DEL_NBF: makestr(&del_nbf,s); break; } break; case DEL_DOT: matchdot = 1; break; case DEL_NOD: matchdot = 0; break; case DEL_ALL: fs = 0; #ifdef VMS deldef = "*.*"; /* UNIX, Windows, OS/2 */ #else #ifdef datageneral deldef = "+"; /* AOS/VS */ #else deldef = "*"; /* UNIX, Windows, OS/2, VMS... */ #endif /* datageneral */ #endif /* VMS */ deltree = 1; nolinks = 2; matchdot = 1; recursive = 1; /* Fall through purposely... */ case DEL_DIR: deldirs = 1; goto again; case DEL_EXC: if (!getval) break; if ((x = cmfld("Pattern","",&s,xxstring)) < 0) { if (x == -3) { printf("?Pattern required\n"); x = -9; } else rc = x; goto xdelete; } fs++; deltree = 0; makestr(&del_exc,s); break; case DEL_HDG: x_hdg = 1; break; #ifdef RECURSIVE case DEL_REC: recursive = 1; break; #endif /* RECURSIVE */ case DEL_LIS: x_lis = 1; break; case DEL_SUM: summary = 1; x_lis = 0; x_hdg = 1; break; case DEL_NOL: x_lis = 0; break; #ifndef CK_TTGWSIZ case DEL_PAG: xaskmore = 1; break; case DEL_NOP: xaskmore = 0; break; #endif /* CK_TTGWSIZ */ case DEL_QUI: qflag = 1; x_lis = 0; break; case DEL_VRB: x_lis = 1; break; case DEL_SMA: case DEL_LAR: if (!getval) break; if ((x = cmnum("File size in bytes","0",10,&y,xxstring)) < 0) return(x); fs++; deltree = 0; switch (cmresult.nresult) { case DEL_SMA: minsize = y; break; case DEL_LAR: maxsize = y; break; } break; case DEL_SIM: simulate = 1; x_lis = 1; break; case DEL_ASK: asking = 1; break; case DEL_NAS: asking = 0; break; case DEL_TYP: { extern struct keytab txtbin[]; if (!getval) break; if ((x = cmkey(txtbin,3,"","",xxstring)) < 0) return(x); if (x == 2) { /* ALL */ xmode = -1; } else { /* TEXT or BINARY only */ xmode = x; scan = 1; } break; } default: printf("?Not implemented yet - \"%s\"\n",atmbuf); return(-9); } } if (qflag && (cmresult.fcode == _CMFLD)) { if ((x = cmcfm()) < 0) return(x); else return(success = 0); } if (cmresult.fcode != _CMIFI) { if (*atmbuf) { int x; if (iswild(atmbuf) && nzxpand(atmbuf,nzxopts) == 0) printf("?No files match: %s\n",brstrip(atmbuf)); else if ((x = zchki(atmbuf)) == -1) printf("?File not found: %s\n",brstrip(atmbuf)); else if (x == -2) printf("?Not a regular file: %s\n",atmbuf); else /* printf("?Not a deletable file: %s\n",atmbuf); */ goto tryanyway; } else { printf("?A file specification is required\n"); } return(-9); } tryanyway: ckstrncpy(tmpbuf,cmresult.sresult,TMPBUFSIZ); /* Safe copy of filespec */ if (deldirs) { ckstrncpy(safebuf,cmresult.sresult,CKMAXPATH); #ifdef VMSORUNIX len = zgetfs(tmpbuf); /* Is it a directory name? */ argisdir = zgfs_dir; /* Then because of how zxpand() */ if (argisdir && zgfs_link) /* works, we have to add it to */ argisdir = 0; /* the list. */ if (itsadir) len = -2; #else len = zchki(tmpbuf); if (len < 0) argisdir = isdir(tmpbuf); #endif /* VMSORUNIX */ } debug(F110,"DELETE file",tmpbuf,0); if ((x = cmcfm()) < 0) return(x); #ifdef IKSD #ifdef CK_LOGIN if (inserver && isguest) { printf("?Sorry, DELETE unavailable to guests\n"); return(-9); } #endif /* CK_LOGIN */ #endif /* IKSD */ #ifndef OS2ORUNIX if (simulate) { printf("?Sorry, /SIMULATE not implemented on this platform\n"); return(-9); } #endif /* OS2ORUNIX */ #ifdef COMMENT /* (not needed) */ if (!iswild(tmpbuf)) { char *m; errno = 0; x = zchki(tmpbuf); if (x < 0) { switch (x) { case -2: m = "Not a regular file"; break; case -1: m = "File not found or not accessible"; break; default: m = errno ? ck_errstr() : "Can't delete"; } printf("?%s: \"%s\"\n",m,tmpbuf); return(-9); } } #endif /* COMMENT */ makelist(del_exc,dxlist,8); /* tmpbuf[] has the name - now do any needed conversions on it */ #ifdef OS2 { /* Lower level functions change / to \, not good for CMD.EXE. */ char *p = tmpbuf; while (*p) { /* Change them back to \ */ if (*p == '/') *p = '\\'; p++; } } #endif /* OS2 */ #ifdef VMS if (iswild(tmpbuf)) { #ifdef COMMENT /* Does not handle '.' as version separator */ char *p = tmpbuf; x = 0; while (*p) { if (*p == ';') { x = 1; break; } else p++; } if (!x) ckstrncat(tmpbuf,";*",TMPBUFSIZ); #else j = 0; x = 0; /* for end_dot and number of dots */ i = strlen(tmpbuf); if (tmpbuf[i] == ';') { ckstrncat(tmpbuf,"0",TMPBUFSIZ); } else { if (tmpbuf[i--] == '.') j++; for (; i >= 0; i--) { if (tmpbuf[i] == ';' || tmpbuf[i] == ':' || tmpbuf[i] == ']' || tmpbuf[i] == '>') break; else if (tmpbuf[i] == '.') x++; } if (tmpbuf[i] != ';') { /* dot may have been used */ if (j) { /* last char is dot */ if (x) /* second is version separator */ ckstrncat(tmpbuf,"0",TMPBUFSIZ); else /* 'foo.' */ ckstrncat(tmpbuf,";0",TMPBUFSIZ); } else if (x == 1) /* lacking a version separator */ ckstrncat(tmpbuf,";0",TMPBUFSIZ); else if (x == 0) /* x == 2 has a version */ ckstrncat(tmpbuf,".*;0",TMPBUFSIZ); } } #endif /* COMMENT */ } #endif /* VMS */ debug(F110,"dodel tmpbuf",tmpbuf,0); /* Filename */ #ifndef OS2ORUNIX /* No built-in DELETE code... */ /* Construct system command. */ ckmakmsg(line,LINBUFSIZ,DELCMD," ",tmpbuf,NULL); #else #ifdef VMS if (asking) { /* Maybe overwrite in VMS */ if (x_lis) /* if options are needed... */ ckmakmsg(line,LINBUFSIZ,DELCMD," /confirm/log ",tmpbuf,NULL); else ckmakmsg(line,LINBUFSIZ,DELCMD," /confirm ",tmpbuf,NULL); } else if (x_lis) ckmakmsg(line,LINBUFSIZ,DELCMD," /log ",tmpbuf,NULL); conres(); #endif /* VMS */ debug(F110,"dodel line",line,0); #endif /* OS2ORUNIX */ #ifdef MAC success = (zdelet(tmpbuf) == 0); #else /* !MAC ... */ #ifdef OS2ORUNIX { int filespace = 0; int count = 0; int lines = 0; int n = 0; s = tmpbuf; #ifdef CK_TTGWSIZ #ifdef OS2 ttgcwsz(); #else /* OS2 */ /* Check whether window size changed */ if (ttgwsiz() > 0) { if (tt_rows > 0 && tt_cols > 0) { cmd_rows = tt_rows; cmd_cols = tt_cols; } } #endif /* OS2 */ #endif /* CK_TTGWSIZ */ if (x_hdg > 0 && !summary) { printf("Deleting %s...%s\n", s, simulate ? " (SIMULATION)" : ""); n += 2; } #ifdef ZXREWIND z = zxrewind(); /* Rewind file list */ #else if (!deldirs) nzxopts = ZX_FILONLY; if (recursive) nzxopts |= ZX_RECURSE; if (matchdot) nzxopts |= ZX_MATCHDOT; errno = 0; z = nzxpand(s,nzxopts); /* Expand file list */ #endif /* ZXREWIND */ debug(F111,"dodel",s,z); /* If deleting directories, sort in reverse order */ /* so we delete the files first, then the directory. */ #ifdef OS2 /* In K95, we have no mtchs array, nor any control over */ /* the order in which znext() returns filenames, so we */ /* must copy the array and sort it. */ { int i; if (xmtchs) { /* Free previous list in case */ debug(F101,"dodel freeing previous list","",xmtchn); for (i = 0; i < xmtchn; i++) /* it wasn't freed last time. */ if (xmtchs[i]) free(xmtchs[i]); free(xmtchs); } xmtchn = 0; xmtchs = (char **)malloc(z * (sizeof(char **))); /* Make new one */ if (!xmtchs) { printf("?Memory allocation failure\n"); return(-9); } for (i = 0; i < z; i++) { xmtchs[i] = NULL; znext(tmpbuf); if (!*tmpbuf) break; makestr(&(xmtchs[i]),tmpbuf); if (!xmtchs[i]) { printf("?Memory allocation failure\n"); xmtchn = i - 1; rc = -9; goto xdelete; } /* debug(F111,"dodel add",xmtchs[i],i); */ } xmtchn = i; debug(F101,"dodel xmtchn","",xmtchn); sh_sort(xmtchs,NULL,z,0,deldirs,0); } #else #ifdef UNIX sh_sort(mtchs,NULL,z,0,deldirs,filecase); #endif /* UNIX */ #endif /* OS2 */ if (z > 0) { int i; #ifdef OS2 int ix = 0; #endif /* OS2 */ success = 1; if (x_hdg > 0) printf("\n"); while ( #ifdef OS2 ix < xmtchn #else 1 #endif /* OS2 */ ) { /* Loop for all files */ #ifdef OS2 ckstrncpy(tmpbuf,xmtchs[ix++],TMPBUFSIZ); #else znext(tmpbuf); /* Get next file */ #endif /* OS2 */ if (!*tmpbuf) { /* No more */ if (deldirs && recursive && argisdir) { ckstrncpy(tmpbuf,safebuf,TMPBUFSIZ); argisdir = 0; /* (only do this once) */ } else break; } skip = 0; if (!deltree) { if (fs) if (fileselect(tmpbuf, del_aft,del_bef,del_naf,del_nbf, minsize,maxsize,0,8,dxlist) < 1) { skip++; } } if (!skip && scan && itsadir) { skip++; } if (!skip && scan) { switch (scanfile(tmpbuf,&y,nscanfile)) { case FT_BIN: if (xmode != 1) skip++; break; case FT_TEXT: case FT_7BIT: case FT_8BIT: #ifdef UNICODE case FT_UTF8: case FT_UCS2: #endif /* UNICODE */ if (xmode != 0) skip++; } } if (!skip && asking) { int x; ckmakmsg(line,LINBUFSIZ," Delete ",tmpbuf,"? ",NULL); x = getyesno(line,3); switch (x) { case 0: continue; /* no */ case 1: break; /* yes */ case 2: goto xdelete; /* quit */ case 3: asking = 0; break; /* go */ } } #ifdef VMSORUNIX len = zgetfs(tmpbuf); /* Get length and accessibility */ itsadir = zgfs_dir; if (itsadir && zgfs_link) { /* Treat links to directories */ itsadir = 0; /* as regular files */ if (scan) /* But not if /TYPE: was given */ skip++; } if (itsadir) /* (emulate non-Unix code) */ len = -2; #else len = zchki(tmpbuf); /* Get accessibility */ if (len < 0) /* See if it's a directory */ itsadir = isdir(tmpbuf); #endif /* VMSORUNIX */ if (skip) { #ifdef COMMENT /* Too verbose */ if (x_lis > 0) { lines++; printf(" %s (SKIPPED)\n",tmpbuf); #ifdef CK_TTGWSIZ if (++n > cmd_rows - 3) if (!askmore()) goto xdelete; else n = 0; #endif /* CK_TTGWSIZ */ } #endif /* COMMENT */ continue; } debug(F111,"DELETE len",tmpbuf,len); if (simulate) { filespace += len; count++; if (x_lis > 0) { lines++; printf(" %s (SELECTED)\n",tmpbuf); if (++n > cmd_rows - 3) { int xx; xx = askmore(); if (!xx) goto xdelete; else n = 0; } } } else if (len >= 0 || !itsadir) { /* Regular file */ zdelet(tmpbuf); /* or symlink, etc... */ if (zchki(tmpbuf) < 0) { filespace += len; count++; if (x_lis > 0) { lines++; printf(" %s (OK)\n",tmpbuf); if (++n > cmd_rows - 3) if (!askmore()) goto xdelete; else n = 0; } } else { bad++; success = 0; if (x_lis > 0) { lines++; printf(" %s (FAILED: %s)\n",tmpbuf,ck_errstr()); if (++n > cmd_rows - 3) if (!askmore()) goto xdelete; else n = 0; } } } else if (/* pass > 0 && */ deldirs && itsadir) { /* It's a directory */ if (zrmdir(tmpbuf) > -1) { /* Only works if empty */ count++; if (x_lis > 0) { lines++; printf(" %s (OK)\n",tmpbuf); if (++n > cmd_rows - 3) if (!askmore()) goto xdelete; else n = 0; } } else { success = 0; if (x_lis > 0) { lines++; printf(" %s (FAILED: %s)\n", tmpbuf, ck_errstr()); if (++n > cmd_rows - 3) if (!askmore()) goto xdelete; else n = 0; } } } else if (x_lis > 0) { lines++; if (isdir(tmpbuf)) printf(" %s (FAILED: directory)\n",tmpbuf); else printf(" %s (FAILED: not a regular file)\n",tmpbuf); if (++n > cmd_rows - 3) if (!askmore()) goto xdelete; else n = 0; } } if (x_hdg > 0) { if (lines > 0) printf("\n"); if (++n > cmd_rows - 3) if (!askmore()) goto xdelete; else n = 0; printf("%d file%s %sdeleted, %d byte%s %sfreed%s\n", count, count != 1 ? "s" : "", simulate ? "would be " : "", filespace, filespace != 1 ? "s" : "", simulate ? "would be " : "", simulate ? " (maybe)" : "" ); } if (!x_lis && !success && !quiet) { printf("?DELETE failed for %d file%s \ (use DELETE /LIST to see details)\n", bad, bad == 1 ? "" : "s" ); } } else if (x_lis > 0) { if (errno) printf("?%s: %s\n",ck_errstr(), tmpbuf); else printf("?Can't delete: %s\n",tmpbuf); } } #else /* OS2ORUNIX */ #ifndef VMS /* Others - let the system do it. */ xsystem(line); x = nzxpand(tmpbuf,nzxopts); success = (x > 0) ? 0 : 1; if (x_hdg > 0) printf("%s - %sdeleted\n", tmpbuf, success ? "" : "not "); #else if (asking) printf("\n"); x = xsystem(line); /* zshcmd returns 1 for success */ success = (x > 0) ? 1 : 0; if (x_hdg > 0 && !asking) printf("%s - %sdeleted\n", tmpbuf, success ? "" : "not "); concb((char)escape); #endif /* VMS */ #endif /* OS2ORUNIX */ #endif /* MAC */ xdelete: if (g_matchdot > -1) { matchdot = g_matchdot; /* Restore these... */ g_matchdot = -1; } #ifdef OS2 if (xmtchs) { int i; debug(F101,"dodel freeing list","",xmtchn); for (i = 0; i < xmtchn; i++) if (xmtchs[i]) free(xmtchs[i]); free(xmtchs); xmtchs = NULL; xmtchn = 0; } #endif /* OS2 */ debug(F101,"dodel result","",rc); return((rc < 0) ? rc : success); } #endif /* NOFRILLS */ #ifndef NOSPL /* The ELSE command */ _PROTOTYP( VOID pushqcmd, (char *) ); int doelse() { if (!ifcmd[cmdlvl]) { printf("?ELSE doesn't follow IF\n"); return(-2); } #ifdef COMMENT /* Wrong. This prevents IF..ELSE IF...ELSE IF...ELSE IF...ELSE... from working. */ ifcmd[cmdlvl] = 0; #endif /* COMMENT */ if (!iftest[cmdlvl]) { /* If IF was false do ELSE part */ if (maclvl > -1 || tlevel > -1) { /* In macro or command file */ debug(F100,"doelse pushing","",0); #ifndef COMMENT pushcmd(NULL); /* save rest of command. */ #else /* This fixes certain obscure problems */ /* but breaks many other constructions that must work. */ pushqcmd(NULL); #endif /* COMMENT */ } else { /* If interactive, */ cmini(ckxech); /* just start a new command */ printf("\n"); /* (like in MS-DOS Kermit) */ if (pflag) prompt(xxstring); } } else { /* Condition is false */ if ((y = cmtxt("command to be ignored","",&s,NULL)) < 0) return(y); /* Gobble up rest of line */ } return(0); } #endif /* NOSPL */ #ifndef NOSPL int doswitch() { char *lp, *ap; /* Macro argument pointer */ int len = 0, x, y, pp = 0; char brbuf[3]; /* Get variable name */ tmpbuf[0] = NUL; brbuf[0] = '{'; brbuf[1] = '}'; brbuf[2] = NUL; y = cmfld("Variable name","",&s,xxstring); debug(F111,"doswitch cmfld",s,y); if (y < 0) { if (y == -3) /* Because brstrip() writes */ s = brbuf; /* into its argument. */ else return(y); } debug(F110,"doswitch A",s,0); if (!strcmp(s,"(")) { pp++; if ((y = cmfld("Variable name","",&s,xxstring)) < 0) { if (y == -3) s = brbuf; else return(y); debug(F110,"doswitch B",s,0); } } len = ckstrncpy(tmpbuf,brstrip(s),TMPBUFSIZ); if (tmpbuf[0] == CMDQ) { if (chkvar(s) < 1) { printf("?Variable name required\n"); return(-9); } } if (pp > 0) { /* If open paren given parse closing */ if ((y = cmfld("Closing parenthesis","",&s,NULL)) < 0) return(y); if (strcmp(atmbuf,")")) { printf("?Closing parenthesis required\n"); return(-9); } } lp = line; x = ckstrncpy(lp,"_switx ",LINBUFSIZ); /* _switx + space */ lp += x; ap = lp; debug(F010,"SWITCH a",line,0); #ifdef COMMENT x = ckmakmsg(lp,LINBUFSIZ-x,tmpbuf," ",NULL,NULL); /* variable name + SP */ #else { /* variable name + SP */ char * p = tmpbuf; if (len > 0) { if (tmpbuf[0] == '(' && tmpbuf[len-1] == ')') { tmpbuf[len-1] = NUL; p++; } } x = ckmakmsg(lp,LINBUFSIZ-x,"{",brstrip(p),"}"," "); } #endif /* COMMENT */ debug(F010,"SWITCH b",line,0); lp += x; /* Get body */ if ((y = cmtxt("series of cases","",&s,NULL)) < 0) return(y); if ((y = (int)strlen(s)) < 1) return(-2); if (s[0] != '{' && s[y-1] != '}') { /* Supply braces if missing */ ckmakmsg(tmpbuf,TMPBUFSIZ,"{ ",s," }",NULL); s = tmpbuf; } if (litcmd(&s,&lp,(LINBUFSIZ - (lp - (char *)line) - 2)) < 0) { printf("?Unbalanced braces\n"); return(0); } debug(F010,"SWITCH c",line,0); x = mlook(mactab,"_switx",nmac); /* Look up SWITCH macro definition */ if (x < 0) { /* Not there? */ addmmac("_switx",sw_def); /* Put it back. */ if ((x = mlook(mactab,"_switx",nmac)) < 0) { /* Look it up again. */ printf("?SWITCH macro definition gone!\n"); /* Shouldn't happen. */ return(success = 0); } } debug(F010,"SWITCH command",line,0); /* Execute the SWITCH macro. */ success = dodo(x,ap,cmdstk[cmdlvl].ccflgs | CF_IMAC); debug(F101,"SWITCH status","",success); return(success); } int dofor() { /* The FOR command. */ int i, fx, fy, fz; /* loop variables */ char *ap, *di; /* macro argument pointer */ int pp = 0; /* Paren level */ int mustquote = 0; for (i = 0; i < 2; i++) { if ((y = cmfld("Variable name","",&s,NULL)) < 0) { if (y == -3) { printf("?Variable name required\n"); return(-9); } else return(y); } if (strcmp(s,"(")) break; pp++; } #ifdef COMMENT if ((y = parsevar(s,&x,&z)) < 0) /* Check variable. */ return(y); #else if (*s == CMDQ) /* If loop variable starts with */ mustquote++; /* backslash, mustquote is > 0. */ #endif /* COMMENT */ lp = line; /* Build a copy of the command */ ckstrncpy(lp,"_forx ",LINBUFSIZ); lp += (int)strlen(line); /* "_for" macro. */ ap = lp; /* Save pointer to macro args. */ if (*s == CMDQ) s++; /* Skip past backslash if any. */ while ((*lp++ = *s++)) ; /* copy it */ lp--; *lp++ = SP; /* add a space */ if ((y = cmnum("initial value","",10,&fx,xxstring)) < 0) { if (y == -3) return(-2); else return(y); } debug(F101,"dofor fx","",fx); s = atmbuf; /* Copy the atom buffer */ if ((int)strlen(s) < 1) goto badfor; /* In edit 192, we change the loop variables to be evaluated at loop entry, not each time through the loop. This was required in order to allow \v(argc) to be used as a loop variable, or in a loop-variable expression. Thus, we can't have FOR loops that modify their own exit conditions by changing the final value or the increment. The problem with \v(argc) was that it is on the macro stack; after entry into the _forx macro, it is at the wrong place. */ sprintf(tmpbuf,"%d",fx); /* (SAFE) Substitute actual value */ s = tmpbuf; while ((*lp++ = *s++)) ; /* (what they actually typed) */ lp--; *lp++ = SP; #ifdef DEBUG *lp = NUL; debug(F110,"FOR A",line,0); #endif /* DEBUG */ if ((y = cmnum("final value","",10,&fy,xxstring)) < 0) { if (y == -3) return(-2); else return(y); } debug(F101,"dofor fy","",fy); s = atmbuf; /* Same deal */ if ((int)strlen(s) < 1) goto badfor; sprintf(tmpbuf,"%d",fy); /* SAFE */ s = tmpbuf; while ((*lp++ = *s++)) ; lp--; *lp++ = SP; #ifdef DEBUG *lp = NUL; debug(F110,"FOR B",line,0); #endif /* DEBUG */ x_ifnum = 1; /* Increment or parenthesis */ di = (fx < fy) ? "1" : "-1"; /* Default increment */ if ((y = cmnum("increment",di,10,&fz,xxstring)) < 0) { debug(F111,"dofor increment",atmbuf,y); x_ifnum = 0; if (y == -3) { /* Premature termination */ return(-2); } else if (y == -2) { /* Maybe closing paren */ if (!strcmp(atmbuf,")")) { pp--; /* Count it */ s = di; /* supply default interval */ fz = atoi(s); } else /* Not closing paren, invalid */ return(y); } else /* Other error */ return(y); } else { /* Number */ x_ifnum = 0; debug(F101,"dofor fz","",fz); s = atmbuf; /* Use it */ } if ((int)strlen(s) < 1) goto badfor; sprintf(tmpbuf,"%d",fz); /* (SAFE) Same deal */ s = tmpbuf; while ((*lp++ = *s++)) ; lp--; *lp++ = SP; #ifdef DEBUG *lp = NUL; debug(F110,"FOR C",line,0); #endif /* DEBUG */ /* Insert the appropriate comparison operator */ if (fz < 0) *lp++ = '<'; else *lp++ = '>'; *lp++ = SP; #ifdef DEBUG *lp = NUL; debug(F110,"FOR D",line,0); #endif /* DEBUG */ if (pp > 0) { /* If open paren given parse closing */ if ((y = cmfld("Closing parenthesis","",&s,NULL)) < 0) return(y); if (strcmp(atmbuf,")")) { printf("?Closing parenthesis required\n"); return(-9); } } if ((y = cmtxt("Command to execute","",&s,NULL)) < 0) return(y); if ((y = (int)strlen(s)) < 1) return(-2); if (s[0] != '{' && s[y-1] != '}') { /* Supply braces if missing */ ckmakmsg(tmpbuf,TMPBUFSIZ,"{ ",s," }",NULL); s = tmpbuf; } if (litcmd(&s,&lp,(LINBUFSIZ - (lp - (char *)line) - 2)) < 0) { printf("?Unbalanced braces\n"); return(0); } #ifdef DEBUG *lp = NUL; debug(F110,"FOR E",line,0); #endif /* DEBUG */ #ifdef COMMENT /* Too strict */ if (fz == 0) { printf("?Zero increment not allowed\n"); return(0); } #endif /* COMMENT */ /* In version 8.0 we decided to allow macro names anyplace a numeric-valed variable could appear. But this caused trouble for the FOR loops because the quoting in for_def[] assumed a \%i-style loop variable. We account for this here in the if (mustquote)...else logic by invoking separate FOR macro definitions in the two cases. */ if (mustquote) { /* \%i-style loop variable */ x = mlook(mactab,"_forx",nmac); /* Look up FOR macro definition */ if (x < 0) { /* Not there? */ addmmac("_forx",for_def); /* Put it back. */ if ((x = mlook(mactab,"_forx",nmac)) < 0) { /* Look it up again. */ printf("?FOR macro definition gone!\n"); return(success = 0); } } } else { /* Loop variable is a macro */ x = mlook(mactab,"_forz",nmac); if (x < 0) { addmmac("_forz",foz_def); if ((x = mlook(mactab,"_forz",nmac)) < 0) { printf("?FOR macro definition gone!\n"); return(success = 0); } } } debug(F010,"FOR command",line,0); /* Execute the FOR macro. */ return(success = dodo(x,ap,cmdstk[cmdlvl].ccflgs | CF_IMAC)); badfor: printf("?Incomplete FOR command\n"); return(-2); } #endif /* NOSPL */ #ifndef NOSPL /* T O D 2 S E C -- Convert time of day as hh:mm:ss to secs since midnite */ /* Call with a string hh:mm or hh:mm:ss. Returns a 0 to 86400 on success, or a negative number on failure. */ long tod2sec(t) char * t; { long t2; long hh = 0L, mm = 0L, ss = 0L; if (!t) t = ""; if (!*t) return(-3L); debug(F110,"tod2sec",t,0); if (isdigit(*t)) /* Get hours from argument */ hh = *t++ - '0'; else return(-1L); if (isdigit(*t)) hh = hh * 10 + *t++ - '0'; #ifdef COMMENT if (hh > 24L) return(-1L); #endif /* COMMENT */ if (*t == ':') t++; else if (!*t) goto xtod2sec; else return(-1L); if (isdigit(*t)) /* Minutes */ mm = *t++ - '0'; else return(-1L); if (isdigit(*t)) mm = mm * 10 + *t++ - '0'; if (mm > 60L) return(-1L); if (*t == ':') t++; else if (!*t) goto xtod2sec; else return(-1L); if (isdigit(*t)) /* Seconds */ ss = *t++ - '0'; else return(-1L); if (isdigit(*t)) ss = ss * 10 + *t++ - '0'; if (ss > 60L) return(-1L); if (*t > 32) /* No trailing junk allowed */ return(-1L); xtod2sec: t2 = hh * 3600L + mm * 60L + ss; /* Seconds since midnight from arg */ debug(F101,"tod2sec t2","",t2); return(t2); } int waitinterval = 1; #ifdef OLDWAIT #undef OLDWAIT #endif /* OLDWAIT */ int kbchar = NUL; int dopaus(cx) int cx; { long zz; extern int sleepcan; #ifdef OLDWAIT zz = -1L; x_ifnum = 1; /* Turn off internal complaints */ if (cx == XXWAI) y = cmnum("seconds to wait, or time of day hh:mm:ss","1",10,&x,xxstring); else if (cx == XXPAU) y = cmnum("seconds to pause, or time of day hh:mm:ss", "1",10,&x,xxstring); else y = cmnum("milliseconds to sleep, or time of day hh:mm:ss", "100",10,&x,xxstring); x_ifnum = 0; if (y < 0) { if (y == -2) { /* Invalid number or expression */ char *p = tmpbuf; /* Retrieve string from atmbuf */ int n = TMPBUFSIZ; *p = NUL; zzstring(atmbuf,&p,&n); /* Evaluate in case it's a variable */ zz = tod2sec(tmpbuf); /* Convert to secs since midnight */ if (zz < 0L) { printf("?Number, expression, or time of day required\n"); return(-9); } else { char now[32]; /* Current time */ char *p; long tnow; p = now; ztime(&p); tnow = atol(p+11) * 3600L + atol(p+14) * 60L + atol(p+17); if (zz < tnow) /* User's time before now */ zz += 86400L; /* So make it tomorrow */ zz -= tnow; /* Seconds from now. */ } } else return(y); } if (x < 0) x = 0; switch (cx) { case XXPAU: /* PAUSE */ case XXMSL: /* MSLEEP */ if ((y = cmcfm()) < 0) return(y); break; case XXWAI: /* WAIT */ z = 0; /* Modem signal mask */ while (1) { /* Read zero or more signal names */ y = cmkey(mstab,nms,"modem signal","",xxstring); if (y == -3) break; /* -3 means they typed CR */ if (y < 0) return(y); /* Other negatives are errors */ z |= y; /* OR the bit into the signal mask */ } if ((y = cmcfm()) < 0) return(y); break; default: /* Shouldn't happen */ return(-2); } /* Command is entered, now do it. */ if (zz > -1L) { /* Time of day given? */ x = zz; if (zz != (long) x) { printf( "Sorry, arithmetic overflow - hh:mm:ss not usable on this platform.\n" ); return(-9); } } if (cx == XXMSL) { /* Millisecond sleep */ msleep(zz < 0 ? x : x * 1000); return(success = 1); } if (cx == XXPAU && !sleepcan) { /* SLEEP CANCELLATION is OFF */ sleep(x); return(success = 1); } /* WAIT, or else SLEEP with cancellation allowed... */ do { /* Sleep loop */ int mdmsig; if (sleepcan) { /* Keyboard cancellation allowed? */ if (y = conchk()) { /* Did they type something? */ #ifdef COMMENT while (y--) coninc(0); /* Yes, gobble it all up */ #else /* There is a debate over whether PAUSE should absorb */ /* its cancelling character(s). There are several */ /* reasons why it should gobble at least one character: */ /* (1) MS-DOS Kermit does it */ /* (2) if not, subsequent PAUSE commands will terminate */ /* immediately */ /* (3) if not, subsequent ASK commands will use it as */ /* valid input. If \13, then it will get no input */ /* (4) if not, then the character appears on the command */ /* line after all enclosing macros are complete. */ kbchar = coninc(0); /* Gobble one up */ #endif /* COMMENT */ break; /* And quit PAUSing or WAITing */ } } if (cx == XXWAI) { /* WAIT (z == modem signal mask) */ debug(F101,"WAIT x","",x); if (z > 0) { /* Looking for any modem signals? */ mdmsig = ttgmdm(); /* Yes, get them */ if (mdmsig < 0) /* Failed */ return(success = 0); if ((mdmsig & z) == z) /* Got what we wanted? */ return(success = 1); /* Succeed */ } if (x == 0) /* WAIT 0 and didn't get our signals */ break; } sleep(1); /* No interrupt, sleep one second */ } while (--x > 0); if (cx == XXWAI) /* If WAIT and loop exhausted */ success = (z == 0); /* Fail. */ else /* */ success = (x == 0); /* Set SUCCESS/FAILURE for PAUSE. */ return(success); #else /* New code uses chained FDBs and allows FILE waits... */ char * m = ""; /* Help message */ struct FDB nu, fl; /* Parse function descriptor blocks */ int filewait = 0; int mdmsig = 0, fs = 0; char filedate[32]; kbchar = 0; switch (cx) { case XXWAI: m = "seconds to wait, or time of day hh:mm:ss"; break; case XXPAU: m = "seconds to pause, or time of day hh:mm:ss"; break; case XXMSL: m = "milliseconds to sleep, or time of day hh:mm:ss"; break; } zz = -1L; cmfdbi(&nu, _CMNUM, /* Number */ m, /* Help message */ (cx == XXMSL) ? "100" : "1", /* Default */ "", /* N/A */ 0, /* N/A */ 0, /* N/A */ xxstring, /* Processing function */ NULL, /* N/A */ &fl /* Next */ ); cmfdbi(&fl, /* Time of day */ _CMFLD, /* Field */ "", /* hlpmsg */ "", /* default */ "", /* addtl string data */ 0, /* addtl numeric data 1 */ 0, /* addtl numeric data 2 */ xxstring, /* processing func */ NULL, /* N/A */ NULL /* No next */ ); x = cmfdb(&nu); /* Parse a number or a field */ if (x < 0) { if (x == -3) x = -2; return(x); } switch (cmresult.fcode) { case _CMNUM: /* Number */ x = cmresult.nresult; break; case _CMFLD: /* Field */ zz = tod2sec(cmresult.sresult); /* Convert to secs since midnight */ if (zz < 0L) { printf("?Number, expression, or time of day required\n"); return(-9); } else { char now[32]; /* Current time */ char *p; long tnow; p = now; ztime(&p); tnow = atol(p+11) * 3600L + atol(p+14) * 60L + atol(p+17); if (zz < tnow) /* User's time before now */ zz += 86400L; /* So make it tomorrow */ zz -= tnow; /* Seconds from now. */ } } debug(F101,"PAUSE/WAIT/MSLEEP zz","",zz); switch (cx) { case XXPAU: /* PAUSE */ case XXMSL: /* MSLEEP */ if ((y = cmcfm()) < 0) return(y); break; case XXWAI: /* WAIT */ z = 0; /* Modem signal mask */ y = cmkey(waittab,nwaittab,"","",xxstring); if (y < 0) { if (y == -3) { if ((y = cmcfm()) < 0) return(y); break; } else return(y); } if (y == WAIT_FIL) { /* FILE */ int wild = 0; if ((z = cmkey(wfswi,nwfswi,"event","",xxstring)) < 0) return(z); filewait = z; if (filewait == WF_MOD || filewait == WF_DEL) z = cmifi("Filename","",&s,&wild,xxstring); else z = cmfld("Filename","",&s,xxstring); if (z < 0) return(z); if (wild || ((filewait == WF_CRE) && iswild(s))) { printf("?Wildcards not valid here\n"); return(-9); } ckstrncpy(tmpbuf,s,TMPBUFSIZ); if ((z = cmcfm()) < 0) return(z); break; } else if (y != WAIT_MDM) { /* A modem signal */ z |= y; /* OR the bit into the signal mask */ } if (!filewait) { /* Modem signals... */ while (1) { /* Get zero or more signal names */ y = cmkey(mstab,nms,"modem signal","",xxstring); if (y == -3) break; /* -3 means they typed CR */ if (y < 0) return(y); /* Other negatives are errors */ z |= y; /* OR the bit into the signal mask */ } if ((y = cmcfm()) < 0) return(y); break; } default: /* Shouldn't happen */ return(-2); } /* switch (cx) */ /* Command is entered, now do it. */ if (zz > -1L) { /* Time of day given? */ x = zz; if (zz != (long) x) { printf( "Sorry, arithmetic overflow - hh:mm:ss not usable on this platform.\n" ); return(-9); } } if (sleepcan) concb((char)escape); /* Ensure single-char wakeup */ if (cx == XXMSL) { /* Millisecond sleep */ msleep(zz < 0 ? x : x * 1000); return(success = 1); } if (cx == XXPAU && !sleepcan) { /* SLEEP CANCELLATION is OFF */ sleep(x); return(success = 1); } if (filewait) { /* FILE... */ fs = zchki(tmpbuf); /* Check if file exists */ switch (filewait) { case WF_DEL: if (fs == -1) return(success = 1); break; case WF_MOD: if (fs == -1) { printf("?File does not exit: %s\n",tmpbuf); return(-9); } s = zfcdat(tmpbuf); /* Get current modification date */ if (!s) s = ""; if (ckstrncpy(filedate,s,32) != 17) { printf("?Can't get modification time: %s\n",tmpbuf); return(-9); } break; case WF_CRE: if (fs > -1) return(success = 1); break; } } do { /* Polling loop */ if (sleepcan) { /* Keyboard cancellation allowed? */ if ((y = conchk()) > 0) { /* Did they type something? */ kbchar = coninc(0); /* Yes, get first char they typed */ debug(F000,"WAIT kbchar","",kbchar); #ifdef COMMENT while (--y > 0) /* Gobble the rest up */ coninc(0); #endif /* COMMENT */ return(success = 0); /* And quit PAUSing or WAITing */ } } if (filewait == 0) { if (cx == XXWAI) { /* WAIT for modem signals */ if (z != 0) { mdmsig = ttgmdm(); /* Get them. */ debug(F101,"WAIT ttgmdm","",mdmsig); if (mdmsig < 0) /* Failure to get them? */ return(success = 0); /* Fail. */ if ((mdmsig & z) == z) /* Got desired ones? */ return(success = 1); /* Succeed. */ } else if (x == 0) return(success = 0); } } else { /* FILE... */ fs = zchki(tmpbuf); /* Get file status */ if (filewait == WF_MOD) { /* Wait for modification */ if (fs == -1) /* Failure to get status */ return(success = 0); /* so WAIT fails. */ s = zfcdat(tmpbuf); /* Get current modification time */ if (!s) s = ""; /* And compare with the time */ if (strcmp(s,filedate)) /* when the WAIT started */ return(success = 1); } else if (filewait == WF_DEL) { /* Wait for deletion */ if (fs == -1) /* If file doesn't exist, */ return(success = 1); /* succeed. */ } else if (filewait == WF_CRE) { /* Wait for creation */ if (fs != -1) /* If file exists */ return(success = 1); /* succeed. */ } } if (x < 1) /* SLEEP/WAIT/PAUSE 0 */ break; sleep(waitinterval); /* No interrupt, sleep */ x -= waitinterval; /* Deduct sleep time */ } while (x > 0); if (cx == XXWAI) /* WAIT time expired */ success = (z == 0); /* Succeed if no modem signals */ else /* For SLEEP or PAUSE, success */ success = (x == 0); /* depends on whether it was */ return(success); /* interrupted from the keyboard. */ #endif /* OLDWAIT */ } #endif /* NOSPL */ #ifdef OS2ORUNIX _PROTOTYP(int zcmpfn,(char *, char *)); #endif /* OS2ORUNIX */ #ifndef NOFRILLS #ifdef NT int dolink() { /* Parse a file or a directory name */ int i, x, z, listing = 0, havename = 0, wild = 0, rc = 1; struct FDB sw, fi; cmfdbi(&sw, /* 2nd FDB - optional /PAGE switch */ _CMKEY, /* fcode */ "Filename or switch", /* hlpmsg */ "", /* default */ "", /* addtl string data */ nqvswtab, /* addtl numeric data 1: tbl size */ 4, /* addtl numeric data 2: 4 = cmswi */ xxstring, /* Processing function */ qvswtab, /* Keyword table */ &fi /* Pointer to next FDB */ ); cmfdbi(&fi, /* 1st FDB - file to type */ _CMIFI, /* fcode */ "", /* hlpmsg */ "", /* default */ "", /* addtl string data */ 3, /* addtl numeric data 1 */ 0, /* addtl numeric data 2 */ xxstring, NULL, NULL ); while (!havename) { x = cmfdb(&sw); /* Parse something */ if (x < 0) /* Error */ return(x); switch (cmresult.fcode) { case _CMKEY: switch (cmresult.nresult) { case DEL_LIS: case DEL_VRB: listing = 1; break; case DEL_NOL: case DEL_QUI: listing = 0; break; } break; case _CMIFI: s = cmresult.sresult; havename = 1; break; default: return(-2); } } wild = cmresult.nresult; /* Source specification wild? */ ckstrncpy(line,s,LINBUFSIZ); /* Make a safe copy of source name */ s = line; if (!wild) wild = iswild(line); p = tmpbuf; /* Place for new name */ if ((x = cmofi(wild ? "Target directory" : "New name", "",&s,xxstring)) < 0) { /* Get new name */ if (x == -3) { printf("?%s required\n", wild ? "Target directory" : "New name"); return(-9); } else return(x); } ckstrncpy(p,s,TMPBUFSIZ); /* Make a safe copy of the new name */ if ((y = cmcfm()) < 0) return(y); if (!wild) { /* Just one */ if (listing) printf("%s => %s ",line,p); if (zlink(line,p) < 0) { if (listing) printf("(FAILED: %s\n",ck_errstr()); rc = 0; } else { if (listing) printf("(OK)\n"); } return(success = rc); } if (!isdir(p)) { /* Multiple */ printf( /* if target is not a directory */ "?Multiple source files not allowed if target is not a directory.\n"); return(-9); } #ifdef COMMENT else { /* Show full path of target */ char buf[CKMAXPATH]; /* (too much) */ if (zfnqfp(p,CKMAXPATH,buf)) ckstrncpy(tmpbuf,buf,TMPBUFSIZ); } #endif /* COMMENT */ #ifdef VMS conres(); /* Let Ctrl-C work. */ #endif /* VMS */ debug(F110,"dolink line",line,0); #ifdef ZXREWIND z = zxrewind(); /* Rewind file list */ #else z = nzxpand(s,0); /* Expand file list */ #endif /* ZXREWIND */ debug(F111,"dolink p",p,z); #ifdef UNIX if (wild && z > 1) sh_sort(mtchs,NULL,z,0,0,filecase); /* Alphabetize the filename list */ #endif /* UNIX */ while (z-- > 0) { if (!(z == 0 && !wild)) znext(line); if (!line[0]) break; if (listing) printf("%s => %s ",line,p); if (zlink(line,p) < 0) { if (listing) printf("(FAILED: %s\n",ck_errstr()); rc = 0; } else { if (listing) printf("(OK)\n"); } } #ifdef VMS concb((char)escape); #endif /* VMS */ return(success = rc); } #endif /* NT */ #ifdef ZCOPY int docopy() { int i, x, listing = 0, nolist = 0, havename = 0, getval; char c; struct FDB sw, fi; int overwrite = OVW_ALWAYS; int targetisdir = 0; int targetlen = 0; int appending = 0; int preserve = 0; int swapping = 0; int fromb64 = 0; int tob64 = 0; int wild = 0; int rc = 1; char newname[CKMAXPATH], * nm; nm = newname; cmfdbi(&sw, /* 1st FDB - switches */ _CMKEY, /* fcode */ "Filename or switch", /* hlpmsg */ "", /* default */ "", /* addtl string data */ ncopytab, /* addtl numeric data 1: tbl size */ 4, /* addtl numeric data 2: 4 = cmswi */ xxstring, /* Processing function */ copytab, /* Keyword table */ &fi /* Pointer to next FDB */ ); cmfdbi(&fi, /* 2nd FDB - file to copy */ _CMIFI, /* fcode */ "", /* hlpmsg */ "", /* default */ "", /* addtl string data */ 0, /* addtl numeric data 1 */ 0, /* addtl numeric data 2 */ xxstring, NULL, NULL ); while (!havename) { x = cmfdb(&sw); /* Parse something */ if (x < 0) /* Error */ return(x); switch (cmresult.fcode) { case _CMKEY: c = cmgbrk(); /* Get break character */ if ((getval = (c == ':' || c == '=')) && !(cmgkwflgs() & CM_ARG)) { printf("?This switch does not take an argument\n"); rc = -9; return(rc); } if (!getval && (cmgkwflgs() & CM_ARG)) { printf("?This switch requires an argument\n"); rc = -9; return(rc); } switch (cmresult.nresult) { case DEL_LIS: case DEL_VRB: nolist = 0; listing = 1; break; case DEL_NOL: case DEL_QUI: nolist = 1; listing = 0; break; case 999: swapping = 1; break; case 998: appending = 1; break; case 995: preserve = 1; break; case 994: if ((x = cmkey(ovwtab,novwtab, "When to overwrite existing destination file", "",xxstring)) < 0) return(x); overwrite = x; break; #ifndef NOSPL case 997: fromb64 = 1; break; case 996: tob64 = 1; break; #endif /* NOSPL */ } break; case _CMIFI: s = cmresult.sresult; havename = 1; break; default: return(-2); } } wild = cmresult.nresult; ckstrncpy(line,s,LINBUFSIZ); /* Make a safe copy of source name */ s = line; p = tmpbuf; /* Place for new name */ /* Get destination name */ if ((x = cmofi("destination name and/or directory", #ifdef UNIX "." #else "" #endif /* UNIX */ ,&s,xxstring)) < 0) { if (x == -3) { printf("?Name for destination file required\n"); return(-9); } else return(x); } ckstrncpy(p,s,TMPBUFSIZ); /* Safe copy of destination name */ if ((y = cmcfm()) < 0) return(y); if (appending && swapping) { printf("?Sorry, /APPEND and /SWAP conflict\n"); return(-9); } #ifdef COMMENT /* This unreasonably prevented "COPY /APPEND *.* bigfile" from concatenating a bunch of files into one big file. */ if (appending && wild) { printf("?Sorry, /APPEND can be used only with single files\n"); return(-9); } #endif /* COMMENT */ targetisdir = isdir(p); x = strlen(p); if (targetisdir) { #ifdef UNIXOROSK if (p[x-1] != '/') { ckstrncat(p,"/",TMPBUFSIZ); x++; } #else #ifdef OS2 if (p[x-1] != '/') { ckstrncat(p,"/",TMPBUFSIZ); x++; } #else #ifdef STRATUS if (p[x-1] != '>') { ckstrncat(p,">",TMPBUFSIZ); x++; } #else #ifdef datageneral if (p[x-1] != ':') { ckstrncat(p,":",TMPBUFSIZ); x++; } #else if (p[x-1] != '/') { ckstrncat(p,"/",TMPBUFSIZ); x++; } #endif /* datageneral */ #endif /* STRATUS */ #endif /* OS2 */ #endif /* UNIXOROSK */ } targetlen = x; if (!appending) { /* If /APPEND not given */ if (wild && !targetisdir) { /* No wildcards allowed */ printf( /* if target is not a directory */ "?Multiple source files not allowed if target is not a directory.\n"); return(-9); } } #ifdef VMS conres(); /* Let Ctrl-C work. */ #endif /* VMS */ debug(F110,"docopy line",line,0); debug(F110,"docopy p",p,0); debug(F110,"docopy nm",nm,0); #ifdef ZXREWIND z = zxrewind(); /* Rewind file list */ #else z = nzxpand(s,0); /* Expand file list */ #endif /* ZXREWIND */ #ifdef UNIX if (wild) sh_sort(mtchs,NULL,z,0,0,filecase); /* Alphabetize the filename list */ #endif /* UNIX */ #ifdef IKSD if (!targetisdir && zchki(p) > -1) { /* Destination file exists? */ if (inserver && (!ENABLED(en_del) #ifdef CK_LOGIN || isguest #endif /* CK_LOGIN */ )) { printf("?Sorry, overwriting existing files is disabled\n"); return(-9); } } #endif /* IKSD */ if (tob64 && fromb64) { /* To and from B64 = no conversion */ tob64 = 0; fromb64 = 0; } debug(F110,"COPY dest",p,0); while (z > 0) { znext(line); if (!line[0]) break; errno = 0; /* Reset errno */ if (targetisdir) { zstrip(line,&nm); ckmakmsg(newname,CKMAXPATH,p,nm,NULL,NULL); nm = newname; } else { nm = p; } if (overwrite) { /* Overwrite checking? */ if (zchki(nm) >= (CK_OFF_T)0) { /* Destination file exists? */ char d1[20], * d2; char * n1, * n2; int i, skip = 0; i = strlen(line); /* Isolate source filename */ for (; i >= 0; i--) { if (ISDIRSEP(line[i])) { n1 = &line[i+1]; break; } } debug(F110,"COPY n1", n1, 0); i = strlen(nm); /* And destination filename */ for (; i >= 0; i--) { if (ISDIRSEP(nm[i])) { n2 = &nm[i+1]; break; } } debug(F110,"COPY n2", n2, 0); if (!strcmp(n1,n2)) { /* Same name? */ if (overwrite == OVW_NEVER) { /* Never overwrite? */ if (listing) /* Skip */ if (listing) printf("%s => %s (SKIPPED)\n",line,nm); continue; } ckstrncpy(d1,zfcdat(line),20); /* Source file timestamp */ d2 = zfcdat(nm); /* Timestamp of dest file */ x = strcmp(d1,d2); /* Compare them */ if (((overwrite == OVW_NEWER) && (x < 0)) || ((overwrite == OVW_OLDER) && (x > 0))) { if (listing) if (listing) printf("%s => %s (SKIPPED)\n",line,nm); continue; } } } } if (listing) printf("%s => %s ",line,nm); /* Straight copy */ if (!swapping && !appending && !fromb64 && !tob64) { debug(F110,"COPY zcopy",line,0); if ((x = zcopy(line,p)) < 0) { /* Let zcopy() do it. */ debug(F111,"COPY not OK",line,x); switch (x) { case -2: if (listing) printf("(FAILED: Not a regular file)\n"); else if (!nolist) printf("?Not a regular file - %s\n",line); rc = 0; break; case -3: if (listing) printf("(FAILED: Not found or not accessible)\n"); else if (!nolist) printf("?Not found or not accessible - %s\n",line); rc = 0; break; case -4: if (listing) printf("(FAILED: Permission denied)\n"); else if (!nolist) printf("?Permission denied - %s\n",line); rc = 0; break; case -5: if (listing) printf("(Source and destination are the same file)\n"); else if (!nolist) printf( "?Source and destination are the same file - %s\n", line ); break; case -6: if (listing) printf("(FAILED: Input/Output error)\n"); else if (!nolist) printf("?Input/Output error - %s\n",line); rc = 0; break; case -7: if (listing) printf("(FAILED: %s - %s)\n",p,ck_errstr()); else if (!nolist) printf("?%s - %s\n",ck_errstr(),p); rc = 0; break; default: if (listing) printf("(FAILED: %s)\n",ck_errstr()); else if (!nolist) printf("?%s\n",ck_errstr()); rc = 0; } } else { /* Regular copy succeeded */ debug(F110,"COPY OK..",newname,0); #ifndef NOXFER if (preserve) { /* Handle /PRESERVE */ char * pstr = ""; /* File permissions string */ struct zattr xx; /* File attribute structure */ extern char * cksysid; initattr(&xx); /* Initialize the struct */ xx.systemid.val = cksysid; /* Set our system ID */ xx.systemid.len = (int)strlen(cksysid); #ifdef CK_PERMS pstr = zgperm(line); /* Get source file's permissions */ #endif /* CK_PERMS */ xx.lprotect.val = pstr; xx.lprotect.len = (int)strlen(pstr); xx.date.val = zfcdat(line); /* Source file's timestamp */ xx.date.len = (int)strlen(xx.date.val); if (zstime(nm,&xx,0) < 0) { printf("?COPY /PRESERVE %s: %s\n",nm,ck_errstr()); rc = -9; } } #endif /* NOXFER */ if (listing && rc > -1) printf("(OK)\n"); } } else { /* Special options */ int prev, y, x = 0; /* Variables needed for them */ int i, t; char ibuf[100]; char obuf[200]; FILE * in = NULL; FILE * out = NULL; if ((in = fopen(line,"r")) == NULL) { /* Open input file */ if (listing) printf("(FAILED: %s)\n",ck_errstr()); else if (!nolist) printf("?%s - %s)\n",ck_errstr(),line); rc = 0; continue; } if (targetisdir) { /* Target is directory */ char * buf = NULL; /* so append this filename to it */ zstrip(line,&buf); p[targetlen] = NUL; if (buf) ckstrncat(p,buf,TMPBUFSIZ); } #ifdef OS2ORUNIX if (zcmpfn(line,p)) { /* Input and output are same file? */ if (listing) printf("(FAILED: Source and destination identical)\n"); else if (!nolist) printf("?Source and destination identical - %s\n", line); rc = 0; continue; } #endif /* OS2ORUNIX */ if ((out = fopen(p, (appending ? "a" : "w"))) == NULL) { fclose(in); if (listing) printf("(FAILED: %s - %s)\n",p,ck_errstr()); else if (!nolist) printf("?%s - %s\n",p,ck_errstr()); rc = 0; continue; } #ifndef NOSPL if (tob64) { /* Converting to Base-64 */ debug(F110,"COPY tob64",line,0); while (1) { /* Loop... */ prev = x; if ((x = fread(ibuf,1,54,in)) < 1) { /* EOF */ if (listing) printf("(OK)\n"); break; } if (prev % 3) { if (listing) printf("(FAILED: Phase error at %d)\n",prev); else if (!nolist) printf("?Phase error at %d\n",prev); rc = 0; break; } if (swapping) { if (x & 1) { if (listing) printf("(FAILED: Swap error)\n"); else if (!nolist) printf("?Swap error\n"); rc = 0; break; } for (i = 0; i < x; i+=2) { t = ibuf[i]; ibuf[i] = ibuf[i+1]; ibuf[i+1] = t; } } if ((y = b8tob64(ibuf,x,obuf,180)) < 0) { if (listing) printf("(FAILED: Encoding error)\n"); else if (!nolist) printf("?Encoding error\n"); rc = 0; break; } fprintf(out,"%s\n",obuf); } } else if (fromb64) { /* Converting from Base 64 */ debug(F110,"COPY fromb64",line,0); if ((out = fopen(p,appending ? "a" : "w")) == NULL) { fclose(in); if (listing) printf("(FAILED: %s - %s)\n",p,ck_errstr()); else if (!nolist) printf("?%s - %s\n",p,ck_errstr()); rc = 0; continue; } x = 1; while (x) { x = fread(ibuf,1,80,in); if ((y = b64tob8(ibuf,x,obuf,80)) < 0) { if (listing) printf("(FAILED: Decoding error)\n"); else if (!nolist) printf("?Decoding error\n"); rc = 0; break; } if (swapping) { if (x & 1) { if (listing) printf("(FAILED: Swap error)\n"); else if (!nolist) printf("?Swap error\n"); rc = 0; break; } for (i = 0; i < y; i+=2) { t = obuf[i]; obuf[i] = obuf[i+1]; obuf[i+1] = t; } } if (y > 0) { if (fwrite(obuf,1,y,out) < 1) { if (listing) printf("(FAILED: %s - %s)\n",p,ck_errstr()); else if (!nolist) printf("?%s - %s\n",p,ck_errstr()); rc = 0; break; } } } } else #endif /* NOSPL */ if (swapping) { /* Swapping bytes */ CHAR c[3]; c[2] = NUL; debug(F110,"COPY swapping",line,0); while (1) { x = fread((char *)c,1,2,in); if (x < 1) { if (listing) printf("(OK)\n"); break; } else if (x == 1) { c[1] = c[0]; c[0] = NUL; printf( "(WARNING: Odd byte count)"); if (!listing) printf("\n"); } if (fprintf(out,"%c%c",c[1],c[0]) == EOF) { if (listing) printf("(FAILED: %s - %s)\n",p,ck_errstr()); else if (!nolist) printf("?%s - %s\n",p,ck_errstr()); rc = 0; break; } } } else if (appending) { /* Appending to target file */ char c; debug(F110,"COPY appending",line,0); while (1) { x = fread(&c,1,1,in); if (x < 1) { if (listing) printf("(OK)\n"); break; } if (fwrite(&c,1,1,out) < 1) { if (listing) printf("(FAILED: %s - %s)\n",p,ck_errstr()); else if (!nolist) printf("?%s - %s\n",p,ck_errstr()); rc = 0; break; } } } if (out) fclose(out); if (in) fclose(in); } #ifdef VMSORUNIX concb((char)escape); #endif /* VMSORUNIX */ } if (rc > -1) success = rc; return(rc); } #endif /* ZCOPY */ #endif /* NOFRILLS */ #ifndef NOCSETS #ifndef NOUNICODE static struct keytab * xfcstab = NULL; /* For RENAME /CONVERT: */ static char cvtbufin[CKMAXPATH+8] = { NUL, NUL }; static char cvtbufout[CKMAXPATH+8] = { NUL, NUL }; static char * pcvtbufin = NULL; static char * pcvtbufout = NULL; static int /* Input function xgnbyte() */ cvtfnin() { CHAR c; c = *pcvtbufin++; return(c ? c : -1); } _PROTOTYP(int cvtfnout,(char)); /* Output function for xpnbyte() */ int #ifdef CK_ANSIC cvtfnout(char c) #else cvtfnout(c) char c; #endif /* CK_ANSIC */ { if (pcvtbufout - cvtbufout >= CKMAXPATH) return(-1); *pcvtbufout++ = c; *pcvtbufout = NUL; return(1); } /* Convert a string from any charset to any other charset */ char * cvtstring(s,csin,csout) char * s; int csin, csout; { int c; extern CK_OFF_T ffc; ckstrncpy(cvtbufin,s,CKMAXPATH); /* Put it in a public place */ pcvtbufin = cvtbufin; /* with public pointers */ pcvtbufout = cvtbufout; *pcvtbufout = NUL; if (csin == csout) /* If the two sets are the same */ return((char *)cvtbufin); /* don't bother converting */ initxlate(csin,csout); /* Initialize the translator */ while ((c = xgnbyte(FC_UCS2,csin,cvtfnin)) > -1) { /* Loop thru string */ if (xpnbyte(c,TC_UCS2,csout,cvtfnout) < 0) { ffc = (CK_OFF_T)0; return(""); } } /* ffc is touched by xgnbyte() but this is not file transfer */ /* so we have to undo it */ ffc = (CK_OFF_T)0; return((char *)cvtbufout); } #endif /* NOUNICODE */ #endif /* NOCSETS */ #ifndef NORENAME #ifndef NOFRILLS #ifdef ZRENAME /* The RENAME command - expanded and improved in 8.0.212 April 2006 */ static char * ren_sub[4] = { NULL,NULL,NULL,NULL }; /* For RENAME /REPLACE */ int ren_list = 0; /* Default listing action for RENAME */ int ren_coll = RENX_OVWR; /* Default collision action */ int shorename() { char * s; switch (ren_coll) { case RENX_FAIL: s = "fail"; break; case RENX_OVWR: s = "overwrite"; break; case RENX_SKIP: s = "proceed"; break; } printf(" rename collision: %s\n",s); printf(" rename list: %s\n",showoff(ren_list)); return(1); } int setrename() { /* Parse SET RENAME options */ int x, y; if ((x = cmkey(renamset,nrenamset,"","", xxstring)) < 0) return(x); switch (x) { case REN_OVW: /* COLLISION */ if ((x = cmkey(r_collision,nr_collision,"","", xxstring)) < 0) return(x); if ((y = cmcfm()) < 0) return(y); ren_coll = x; break; case DEL_LIS: /* LIST */ return(seton(&ren_list)); } return(success = 1); } /* Reverse a string - Assumes a single-byte character set */ int gnirts(s1, s2, len) char * s1, * s2; int len; { int n, m = 0; if (!s1) /* Null source pointer, fail */ return(0); n = (int) strlen(s1); if (n > len-1) /* Source longer than dest, fail */ return(0); s2[n--] = NUL; /* Deposit null byte at end of dest */ for (; n >= 0; n--) { /* Copy the rest backwards */ *s2++ = s1[n]; m++; } return(m); } /* r e n a m e o n e Worker function to rename one file for dorenam() (below). old = name of file or directory to be renamed new = new name (not required for /UPPER, /LOWER, and /REPLACE) replacing = 1 if doing string replacement on the name casing = 1 if converting name to lowercase, 2 if to uppercase all = if doing case conversion on all names, not just monocase ones converting = 1 if converting character sets cset1 = character set to convert from (File Character Set index) cset2 = character set to convert to (ditto, see ck?xla.h) listing = 1 to show results of rename nolist = 1 to be completely silent (don't even print error messages) op = 1 means simulate, 2 means check for collision, 0 means rename size = length of result buffer. collision = action to take if destination file already exists: 0 = fail 1 = overwrite and succeed 2 = skip and succeed Returns: 0: on failure to rename or when a forbidden collision would have occurred. 1: on success (file was renamed or did not need to be renamed). Note: If this code is ever built on any platform that is not Unix, Windows, VMS, or OS/2, this routine might need some adjustment. */ /* Opcodes for op... */ #define REN_OP_SIM 1 /* Simulate */ #define REN_OP_CHK 2 /* Check for collisions */ static int renameone(old,new, replacing,casing,all,converting,cset1,cset2, listing,nolist,op,size,collision) char * old, * new; int replacing,casing,all,converting,cset1,cset2, listing,nolist,op,size,collision; { char buf[CKMAXPATH]; /* Temporary filename buffer */ char out[CKMAXPATH]; /* Buffer for new name */ char dir[CKMAXPATH]; /* Destination directory */ char pat[CKMAXPATH]; /* Path segment on old filename */ char * destdir; /* Destination directory, if any */ char * srcpath; /* Source path, if any */ int rc = 1, flag = 0, skip = 0; /* Control */ int honorcase = 0, replaced = 0; int anchor = 0; /* 1 = beginning, 2 = end */ int occur = 0; /* Occurrence */ int minus = 0; /* Occurrence is negative */ int allbut = 0; /* Occurrence is "all but" */ int arg2isfile = 0; /* Arg2 ("new") is a filename */ debug(F110,"RENAMEONE old",old,0); debug(F110,"RENAMEONE new",new,0); debug(F110,"RENAMEONE ren_sub[0]",ren_sub[0],0); debug(F110,"RENAMEONE ren_sub[1]",ren_sub[1],0); debug(F110,"RENAMEONE ren_sub[2]",ren_sub[2],0); if (op == REN_OP_SIM && !nolist) /* For convenience */ listing = 1; #ifndef NOSPL honorcase = inpcas[cmdlvl]; /* Inherit SET CASE value */ #else #ifdef UNIX honorcase = 1; #else honorcase = 0; #endif /* UNIX */ #endif /* NOSPL */ if (!old) old = ""; /* In case of bad args */ if (!new) new = ""; if (!*old) return(success = 0); ckstrncpy(out,new,CKMAXPATH); /* So we don't write into */ new = out; /* our argument... */ size = CKMAXPATH; pat[0] = NUL; /* Assume no path in source file.. */ srcpath = pat; { int n; /* If the old name includes a path */ n = (int)strlen(old) - 1; /* put it in a separate place. */ for (; n >= 0; n--) { /* We are renaming the file only. */ if (ISDIRSEP(old[n])) { ckstrncpy(pat,old,CKMAXPATH); pat[n+1] = NUL; old = old+n+1; break; } } } debug(F110,"RENAMEONE old 2",old,0); debug(F110,"RENAMEONE pat 2",pat,0); dir[0] = NUL; /* Assume no destination directory */ destdir = dir; if (*new) { /* If Arg2 given */ if (isdir(new)) { /* If it's a directory */ ckstrncpy(dir,new,CKMAXPATH); /* put it here */ } else { /* otherwise */ arg2isfile++; /* flag that it's a filename */ } } if (!casing && !replacing && !converting) { if (!*new) return(success = 0); if (!arg2isfile) { /* Destination is a directory? */ if (!isdir(old)) { /* and source is not? */ #ifndef VMS int n, x = 0; /* Concatenate them */ if ((n = strlen(new)) > 0) /* so we can check for */ if (ISDIRSEP(new[n-1])) /* collisions. */ x++; ckmakmsg(buf,size,new,x ? "" : "/", old, ""); #else ckmakmsg(buf,size,new, old, NULL, NULL); #endif /* VMS */ debug(F110,"RENAMEONE new new",new,0); new = buf; size = CKMAXPATH; } } } else if (*new) { /* Directory to move file to */ int n, x = 0; /* after changing its name */ if (!isdir(new)) return(success = 0); #ifndef VMS if ((n = strlen(new)) > 0) if (ISDIRSEP(new[n-1])) x++; ckmakmsg(dir,CKMAXPATH,new,x ? "" : "/", "", ""); #else ckstrncpy(dir,new,CKMAXPATH); #endif /* VMS */ } #ifndef NOCSETS #ifndef NOUNICODE if (converting) { new = cvtstring(old,cset1,cset2); } #endif /* NOUNICODE */ #endif /* NOCSETS */ if (replacing) { /* Replacing strings */ int todo = 0; int len0, len1, len2; char c, *p, *s, *bp[3]; bp[0] = old; /* Original name */ bp[1] = ren_sub[0]; /* String to be replaced */ bp[2] = ren_sub[1]; /* What to replace it with */ if (!bp[2]) bp[2] = ""; len0 = (int)strlen(bp[0]); /* length of original filename */ len1 = (int)strlen(bp[1]); /* length of target substring */ len2 = (int)strlen(bp[2]); /* Length of replacement string */ if (ren_sub[2]) { /* Optional options */ p = ren_sub[2]; while ((c = *p++)) { switch (c) { case '^': anchor = 1; occur = 0; minus = 0; allbut = 0; break; case '$': anchor = 2; occur = 0; minus = 0; allbut = 0; break; case 'A': honorcase = 1; minus = 0; allbut = 0; break; case 'a': honorcase = 0; minus = 0; allbut = 0; break; case '-': minus = 1; break; case '~': allbut = 1; break; default: if (isdigit(c)) { occur = c - '0'; if (minus) occur = 0 - occur; anchor = 0; } minus = 0; } } } if (anchor) { /* Anchored replacement... */ y = len0 - len1; if (y > 0) { int x; switch (anchor) { case 1: /* Anchored at beginning */ if (!ckstrcmp(bp[1],bp[0],len1,honorcase)) { x = ckstrncpy(new,bp[2],size); (VOID) ckstrncpy(new+x,bp[0]+len1,size-x); replaced = 1; } break; case 2: /* Anchored at end */ if (!ckstrcmp(bp[1],bp[0]+y,len1,honorcase)) { x = ckstrncpy(new,bp[0],y+1); (VOID) ckstrncpy(new+y,bp[2],size-x); replaced = 1; } break; } } if (!replaced) { ckstrncpy(new,old,size); /* Keep old name */ replaced = 1; } } else { /* Replace all occurrences */ int j, n = 0; /* or a particular occurrence */ char c; int x = 0; char * s0 = NULL, * s1 = NULL, * s2 = NULL; p = new; /* Pointer to new name */ if (occur < 0) { /* nth occurrence from the right */ occur = 0 - occur; s0 = (char *)malloc(len0+1); /* Reverse original string */ if (s0) { (VOID) gnirts(bp[0],s0,len0+1); bp[0] = s0; } else return(0); s1 = (char *)malloc(len1+1); /* Reverse target string */ if (s1) { (VOID) gnirts(bp[1],s1,len1+1); bp[1] = s1; } else return(0); s2 = (char *)malloc(len2+1); /* Reverse replacement string */ if (s2) { (VOID) gnirts(bp[2],s2,len2+1); bp[2] = s2; } else return(0); debug(F111,"RENAMEONE s0",s0,len0); debug(F111,"RENAMEONE s1",s1,len1); debug(F111,"RENAMEONE s2",s2,len2); } s = bp[0]; /* Pointer to old name */ p = new; /* Pointer to new name */ j = len0 - len1 + 1; /* How much to scan */ while (j-- > 0) { /* For each character... */ if (!ckstrcmp(bp[1],s,len1,honorcase)) { /* Match? */ n++; /* Occurrence counter */ todo = (occur == 0) || (!allbut && n == occur) || (allbut && n != occur); if (!todo) { /* Desired occurrence? */ size -= ckstrncpy(p,bp[1],size); /* No... */ p += len1; /* Copy target string */ s += len1; /* instead of replacement string */ continue; } if (len2) { /* If replacement string not empty */ size -= ckstrncpy(p,bp[2],size); /* Copy it */ p += len2; } s += len1; /* Advance source position */ } else { /* No match */ *p++ = *s++; /* just copy this character */ size--; } } while ((*p++ = *s++)); /* Done copy the rest */ replaced = 1; /* Remember we changed the name */ if (s0) { /* Were we doing "all but"? */ debug(F110,"RENAMEONE new1",new,0); x = (int)strlen(new); /* Unreverse the result */ if ((p = (char *)malloc(x+2))) { (VOID) gnirts(new,p,x+2); debug(F110,"RENAMEONE new2",new,0); ckstrncpy(new,p,x+2); free(p); } if (s0) free(s0); /* Free the temporary strings */ if (s1) free(s1); if (s2) free(s2); debug(F110,"RENAMEONE new3",new,0); } } } if (casing) { /* Changing case? */ char c, * t; /* See if mixed case. */ if (!replaced) ckstrncpy(new,old,size); /* Copy old name to new name */ t = new; while ((c = *t++)) { if (islower(c)) flag |= 1; /* Have a lowercase letter */ else if (isupper(c)) flag |= 2; /* Have an uppercase letter */ if (flag == 3) break; /* Have a mixed-case name */ } if (all || flag < 3) { /* Not skipping or not mixed case */ if (casing == 1 && flag != 1) /* Change case to lower */ (VOID) cklower(new); else if (casing == 2 && flag != 2) /* Change case to upper */ (VOID) ckupper(new); } } debug(F110,"XXX 1 new",new,0); debug(F110,"XXX 1 old",old,0); debug(F110,"XXX 1 srcpath",srcpath,0); debug(F110,"XXX 1 destdir",destdir,0); if (*destdir && !arg2isfile) { /* Moving without renaming */ ckstrncat(srcpath,old,CKMAXPATH); old = srcpath; new = destdir; } else if (*destdir || *srcpath) { /* Were there any pathnames? */ char tmp[CKMAXPATH]; ckmakmsg(tmp,CKMAXPATH,srcpath,old,NULL,NULL); ckstrncpy(old,tmp,CKMAXPATH); if (*destdir) { /* Directory-to-move-to given? */ ckstrncat(destdir,new,CKMAXPATH); new = destdir; } else if (*srcpath && !arg2isfile) { /* Or was there a source path? */ ckstrncat(srcpath,new,CKMAXPATH); new = srcpath; } } debug(F110,"XXX 2",new,0); skip = 0; /* Can we skip this one? */ #ifdef COMMENT if (casing && !replaced) { skip = (((all == 0) && (flag == 3)) || (flag == casing)) ? 1 : 0; if (!skip && destdir) skip = 0; } #endif /* COMMENT */ if (!skip) { if (!ckstrcmp(old,new,-1,1)) skip = 1; } if (op == 0 && !skip && (collision != RENX_OVWR)) { if (zchki(new) > (CK_OFF_T)-1) { /* New file already exists? */ switch (collision) { /* Yes, take specified action */ case RENX_SKIP: /* Skip this one and proceed */ skip = 2; break; case RENX_FAIL: /* Or fail. */ skip = 3; if (!listing && !nolist) printf("?File already exists: %s\n",new); } } } debug(F110,"RENAMEONE new",new,0); debug(F101,"RENAMEONE flag","",flag); debug(F101,"RENAMEONE skip","",skip); debug(F100,"RENAMEONE ----------------","",0); if (skip == 3) { if (listing) printf("%s => %s (SKIPPED: %s already exists)\n", old,new,new); rc = 0; } else if (skip) { /* Skipping this one */ if (listing) printf("%s => %s (%s)\n", old,new, (skip == 2) ? "COLLISION: SKIPPED" : "SKIPPED"); } else { /* Have to rename this one */ if (op == REN_OP_CHK) { /* Checking for collisions */ return((zchki(new) > (CK_OFF_T)-1) ? 0 : 1 ); } else if (op == REN_OP_SIM) { /* Simulating */ if (listing) printf("%s => %s (SIMULATED)\n",old,new); } else { /* Really renaming */ if (listing) printf("%s => %s ",old,new); if (zrename(old,new) < 0) { rc = 0; if (listing) printf("(FAILED: %s)\n",ck_errstr()); else if (!nolist) printf("?%s\n",ck_errstr()); } else { if (listing) printf("(OK)\n"); } } } return(success = rc); /* Succeeds also if nothing needed to be renamed */ } int dorenam() { #ifndef NOCSETS #ifndef NOUNICODE extern int nfilc; extern struct keytab fcstab[]; extern struct csinfo fcsinfo[]; #endif /* NOUNICODE */ #endif /* NOCSETS */ int cset1 = 0, cset2 = 0; int i, x, z, fn, listing = 0, havename = 0, wild = 0, rc = 1, noarg = 0; int nolist = 0, all = 0, casing = 0, replacing = 0, getval = 0, sim = 0; int converting = 0, collision = 0; char c; struct FDB sw, fi; collision = ren_coll; /* Inherit SET RENAME COLLISION */ listing = ren_list; /* Inhereit SET RENAME LIST */ if (ren_sub[0]) makestr(&(ren_sub[0]),NULL); if (ren_sub[1]) makestr(&(ren_sub[1]),NULL); if (ren_sub[2]) makestr(&(ren_sub[2]),NULL); line[0] = NUL; tmpbuf[0] = NUL; cmfdbi(&sw, /* 1st FDB - switches */ _CMKEY, /* fcode */ "Filename or switch", /* hlpmsg */ "", /* default */ "", /* addtl string data */ nrenamsw, /* addtl numeric data 1: tbl size */ 4, /* addtl numeric data 2: 4 = cmswi */ xxstring, /* Processing function */ renamsw, /* Keyword table */ &fi /* Pointer to next FDB */ ); cmfdbi(&fi, /* 2nd FDB - file or directory name */ _CMIFI, /* fcode */ "", /* hlpmsg */ "", /* default */ "", /* addtl string data */ 3, /* Flags */ 0, /* 0 = Parse file or directory names */ xxstring, NULL, NULL ); if (cmflgs == 1) { printf("?File or directory name required\n"); return(-9); } while (!havename) { noarg = 0; x = cmfdb(&sw); /* Parse something */ if (x == -3) { /* They hit Enter prematurely */ printf("?Command incomplete\n"); return(-9); } else if (x < 0) { /* Other error */ if (x == -1) return(x); if (iswild(atmbuf) && nzxpand(atmbuf,nzxopts) == 0) printf("?No files match: %s\n",brstrip(atmbuf)); else if (zchki(atmbuf) == -1) printf("?File not found: %s\n",brstrip(atmbuf)); else printf("?Error with switch or filename: %s\n",brstrip(atmbuf)); return(-9); } fn = cmresult.nresult; /* For brevity */ switch (cmresult.fcode) { /* Handle each kind of field */ case _CMKEY: /* Keyword (switch) */ c = cmgbrk(); if ((getval = (c == ':' || c == '=')) && !(cmgkwflgs() & CM_ARG)) { printf("?This switch does not take an argument\n"); return(-9); } if (!getval && (cmgkwflgs() & CM_ARG)) noarg = 1; /* Remember arg is missing */ switch (cmresult.nresult) { /* Handle the switch */ case DEL_LIS: /* /LIST */ case DEL_VRB: /* /VERBOSE */ listing = 1; break; case DEL_NOL: /* /NOLIST */ case DEL_QUI: /* /QUIET */ nolist = 1; listing = 0; break; case DEL_SIM: /* /SIMULATE */ sim = 1; break; case REN_UPP: /* /UPPER: */ case REN_LOW: /* /LOWER */ all = 1; if (!noarg) { if ((x = cmkey((fn == REN_UPP) ? r_upper : r_lower, 2, "","all",xxstring)) < 0) { if (x == -3) x = 1; else return(x); } all = x; } /* 0 = don't convert; 1 = convert to lower; 2 = to upper */ casing = (fn == REN_UPP) ? 2 : 1; converting = 0; break; case REN_OVW: /* /COLLISION */ if (!noarg) { if ((x = cmkey(r_collision, nr_collision,"","overwrite",xxstring))<0) { if (x == -3) x = RENX_OVWR; else return(x); } collision = x; } break; case REN_RPL: /* /REPLACE: */ if (noarg) { printf("?This switch requires an argument\n"); return(-9); } if ((x = cmfld("String to remove, or {{String1}{String2}}", "",&s,xxstring)) < 0) { if (x == -3) { printf("?Target string required\n"); x = -9; } return(x); } if (s[0]) { if (s[0] == '{' && s[1] == '{') /* Get the list */ makelist(s,ren_sub,3); else makestr(&(ren_sub[0]),s); } converting = 0; break; case REN_SPA: /* /FIXSPACES: */ if (!noarg) if ((x = cmfld("Character or string to replace spaces with", "_",&s,xxstring)) < 0) if (x == -3) s = "_"; else return(x); makestr(&(ren_sub[0])," "); makestr(&(ren_sub[1]),noarg ? "_" : brstrip(s)); makestr(&(ren_sub[3]),NULL); converting = 0; break; #ifndef NOCSETS #ifndef NOUNICODE case REN_XLA: /* /CONVERT:cset1:cset2 */ if (!xfcstab) { /* Make a copy of the file charset */ int i, x; /* table with CM_ARG set for each */ x = (nfilc + 1) * sizeof(struct keytab); xfcstab = (struct keytab *)malloc(x); if (!xfcstab) { printf("?Memory allocation failure\n"); return(-9); } for (i = 0; i < nfilc; i++) { xfcstab[i].kwd = fcstab[i].kwd; xfcstab[i].kwval = fcstab[i].kwval; xfcstab[i].flgs = fcstab[i].flgs | CM_ARG; } } if ((x = cmswi(xfcstab,nfilc, "Character-set of old name","",xxstring)) < 0) { if (x == -3) { printf("?Pair of character-set names required\n"); return(-9); } else return(x); } cset1 = x; c = cmgbrk(); if (!getval) { printf("?Secondcharacter-set name required\n"); return(-9); } if ((x = cmkey(fcstab,nfilc, "Character-set of new name","",xxstring)) < 0) { if (x == -3) { printf("?Second character-set name required\n"); return(-9); } else return(x); } cset2 = x; if (casing) casing = 0; if (ren_sub[0]) makestr(&(ren_sub[0]),NULL); if (ren_sub[1]) makestr(&(ren_sub[1]),NULL); if (ren_sub[2]) makestr(&(ren_sub[2]),NULL); converting = 1; break; #endif /* NOUNICODE */ #endif /* NOCSETS */ } break; case _CMIFI: /* File or directory name */ s = cmresult.sresult; havename = 1; break; default: printf("?File or directory name required\n"); return(-9); } } if (havename) { ckstrncpy(line,s,LINBUFSIZ); /* Make a safe copy of source name */ } else { printf("?Internal error\n"); return(-9); /* Shouldn't happen */ } wild = cmresult.nresult; /* Source specification wild? */ if (!wild) wild = iswild(line); debug(F111,"RENAME WILD",line,wild); p = tmpbuf; /* Place for new name */ p[0] = NUL; replacing = ren_sub[0] ? 1 : 0; #ifdef COMMENT if (!(casing || replacing || converting)) { if ((x = cmofi(wild ? "Target directory" : "New name", "",&s,xxstring)) < 0) { /* Get new name */ if (x == -3) { printf("?%s required\n", wild ? "Target directory" : "New name"); return(-9); } else return(x); } ckstrncpy(p,s,TMPBUFSIZ); /* Make a safe copy of the new name */ } #else if ((x = cmofi(wild ? "Target directory" : "New name", "",&s,xxstring)) < 0) { /* Get new name */ if (x == -3) { if (casing || replacing || converting) { s = ""; } else { printf("?%s required\n", wild ? "Target directory" : "New name"); return(-9); } } else return(x); } ckstrncpy(p,s,TMPBUFSIZ); /* Make a safe copy of the new name */ #endif /* COMMENT */ if ((y = cmcfm()) < 0) return(y); /* Confirm the command */ #ifdef COMMENT #ifndef NOUNICODE if (converting) { printf(" From: %s\n",fcsinfo[cset1].keyword); printf(" To: %s\n",fcsinfo[cset2].keyword); } #endif /* NOUNICODE */ if (casing) { printf("CASING: %s\n", (casing == 1) ? "LOWER" : "UPPER"); } if (replacing) { printf("REPLACING: '%s' with '%s'\n", ren_sub[0], ren_sub[1] ? ren_sub[1] : ""); } #endif /* COMMENT */ s = line; if (!wild) /* Just one */ return(success = renameone(s,p, replacing,casing,all,converting,cset1,cset2, listing,nolist,sim,TMPBUFSIZ,collision)); if (!casing && !replacing && !converting) { /* Multiple files */ if (!isdir(p)) { printf( /* if target is not a directory */ "?Multiple source files not allowed if target is not a directory.\n"); return(-9); } } #ifdef COMMENT else { /* Show full path of target */ char buf[CKMAXPATH]; /* (too much) */ if (zfnqfp(p,CKMAXPATH,buf)) ckstrncpy(tmpbuf,buf,TMPBUFSIZ); } #endif /* COMMENT */ #ifdef VMS conres(); /* Let Ctrl-C work. */ #endif /* VMS */ debug(F110,"dorename line",line,0); #ifdef ZXREWIND z = zxrewind(); /* Rewind file list */ #else z = nzxpand(s,0); /* Expand file list */ #endif /* ZXREWIND */ debug(F111,"dorename p",p,z); #ifdef UNIX if (wild && z > 1) sh_sort(mtchs,NULL,z,0,0,filecase); /* Alphabetize the filename list */ #endif /* UNIX */ /* For /COLLISION:FAIL make a silent pass to see if there would be any */ if (collision == RENX_FAIL) { int n = 0; char line[CKMAXPATH+2]; while (z-- > 0) { if (!(z == 0 && !wild)) znext(line); if (!line[0]) break; if (!renameone((char *)line,p, replacing,casing,all,converting,cset1,cset2, 0,1,REN_OP_CHK,TMPBUFSIZ,RENX_FAIL)) n++; } if (n > 0) { printf("?Failed: %d file%s would be overwritten\n", n, (n != 1) ? "s" : ""); #ifdef VMS concb((char)escape); #endif /* VMS */ return(success = 0); } /* Get the file list back. */ #ifdef ZXREWIND z = zxrewind(); #else z = nzxpand(s,0); #endif /* ZXREWIND */ } while (z-- > 0 && rc > 0) { char line[CKMAXPATH+2]; if (!(z == 0 && !wild)) znext(line); if (!line[0]) break; rc = renameone((char *)line,p, replacing,casing,all,converting,cset1,cset2, listing,nolist,sim,TMPBUFSIZ,collision); } #ifdef VMS concb((char)escape); #endif /* VMS */ return(success = rc); } #endif /* ZRENAME */ #endif /* NOFRILLS */ #endif /* NORENAME */ #ifndef NOSPL /* Do the RETURN command */ int doreturn(s) char *s; { int x; extern int tra_asg; char * line, * lp; if (cmdlvl < 1) { printf("\n?Can't return from level %d\n",maclvl); return(success = 0); } line = malloc(LINBUFSIZ); if (line == NULL) return(success = 0); lp = line; /* Expand return value now */ x = LINBUFSIZ-1; if (!s) s = ""; debug(F110,"RETURN s",s,0); if (zzstring(s,&lp,&x) > -1) { s = line; debug(F110,"RETURN zzstring",s,0); } /* Pop from all FOR/WHILE/SWITCH/XIFs */ while ((maclvl > 0) && (m_arg[maclvl-1][0]) && (cmdstk[cmdlvl].src == CMD_MD) && (!strncmp(m_arg[maclvl-1][0],"_xif",4) || !strncmp(m_arg[maclvl-1][0],"_for",4) || !strncmp(m_arg[maclvl-1][0],"_swi",4) || !strncmp(m_arg[maclvl-1][0],"_whi",4))) { debug(F111,"RETURN IF/FOR/WHI/SWI pop",m_arg[maclvl-1][0],maclvl); dogta(XXPTA); /* Put args back */ popclvl(); /* Pop up two levels */ popclvl(); } if (tra_asg) { /* If tracing show return value */ if (*s) printf("<<< %s: \"%s\"\n", m_arg[maclvl][0], s); else printf("<<< %s: (null)\n", m_arg[maclvl][0]); } popclvl(); /* Pop from enclosing TAKE or macro */ debug(F111,"RETURN tolevel",s,maclvl); if (!s) s = ""; if (!*s) s = NULL; makestr(&(mrval[maclvl+1]),s); /* Set the RETURN value */ free(line); return(success = 1); /* Macro succeeds if we RETURN */ } #endif /* NOSPL */ #ifndef NOSPL /* Do the OPEN command */ int doopen() { /* OPEN { append, read, write } */ int x, y, z = 0; char *s; static struct filinfo fcb; /* (must be static) */ if ((x = cmkey(opntab,nopn,"mode","",xxstring)) < 0) { if (x == -3) { printf("?Mode required\n"); return(-9); } else return(x); } switch (x) { case OPN_FI_R: /* Old file (READ) */ if (chkfn(ZRFILE) > 0) { printf("?Read file already open\n"); return(-2); } if ((z = cmifi("File to read","",&s,&y,xxstring)) < 0) { if (z == -3) { printf("?Input filename required\n"); return(-9); } else return(z); } if (y) { /* No wildcards allowed */ printf("\n?Please specify a single file\n"); return(-2); } ckstrncpy(line,s,LINBUFSIZ); if ((int)strlen(line) < 1) return(-2); if ((z = cmnum("buffer size","4096",10,&y,xxstring)) < 0) return(z); if (y < 1) { printf("?Positive number required\n"); return(-9); } if ((z = cmcfm()) < 0) return(z); readblock = y; if (readbuf) free((char *)readbuf); if (!(readbuf = (CHAR *) malloc(readblock+1))) { printf("?Can't allocate read buffer\n"); return(-9); } return(success = zopeni(ZRFILE,line)); #ifndef MAC #ifndef NOPUSH case OPN_PI_R: /* Pipe/Process (!READ) */ if (nopush) { printf("?Read from pipe disabled\n"); return(success=0); } if (chkfn(ZRFILE) > 0) { printf("?Read file already open\n"); return(-2); } if ((y = cmtxt("System command to read from","",&s,xxstring)) < 0) { if (y == -3) { printf("?Command name required\n"); return(-9); } else return(y); } ckstrncpy(line,brstrip(s),LINBUFSIZ); if (!line[0]) return(-2); if ((y = cmcfm()) < 0) return(y); if (!readbuf) { if (!(readbuf = (CHAR *) malloc(readblock+1))) { printf("?Can't allocate read buffer\n"); return(-9); } } return(success = zxcmd(ZRFILE,line)); case OPN_PI_W: /* Write to pipe */ if (nopush) { printf("?Write to pipe disabled\n"); return(success=0); } if (chkfn(ZWFILE) > 0) { printf("?Write file already open\n"); return(-2); } if ((y = cmtxt("System command to write to","",&s,xxstring)) < 0) { if (y == -3) { printf("?Command name required\n"); return(-9); } else return(y); } ckstrncpy(line,brstrip(s),LINBUFSIZ); if (!line[0]) return(-2); if ((y = cmcfm()) < 0) return(y); success = zxcmd(ZWFILE,line); if (!success && msgflg) printf("Can't open process for writing: %s\n",line); return(success); #endif /* NOPUSH */ #endif /* MAC */ case OPN_FI_W: /* New file (WRITE) */ case OPN_FI_A: /* (APPEND) */ if ((z = cmofi("Name of local file to create","",&s,xxstring)) < 0) { if (z == -3) { printf("?Filename required\n"); return(-9); } else return(z); } if (z == 2) { printf("?Sorry, %s is a directory name\n",s); return(-9); } if (chkfn(ZWFILE) > 0) { printf("?Write/Append file already open\n"); return(-2); } fcb.bs = fcb.cs = fcb.rl = fcb.fmt = fcb.org = fcb.cc = fcb.typ = 0; fcb.lblopts = 0; fcb.dsp = (x == OPN_FI_W) ? XYFZ_N : XYFZ_A; /* Create or Append */ ckstrncpy(line,s,LINBUFSIZ); if ((int)strlen(line) < 1) return(-2); if ((y = cmcfm()) < 0) return(y); return(success = zopeno(ZWFILE,line,NULL,&fcb)); #ifndef NOLOCAL case OPN_SER: /* OPEN PORT or LINE */ case OPN_NET: { /* OPEN HOST */ extern int didsetlin, ttnproto; if (x == OPN_NET) { z = ttnproto; ttnproto = NP_NONE; } if ((y = setlin((x == OPN_SER) ? XYLINE : XYHOST, 1, 0)) < 0) { if (x == OPN_NET) ttnproto = z; success = 0; } didsetlin++; return(y); } #endif /* NOLOCAL */ default: printf("?Not implemented"); return(-2); } } #endif /* NOSPL */ #ifndef NOXFER /* D O X G E T -- GET command parser with switches */ #ifdef CK_LABELED int g_lf_opts = -1; extern int lf_opts; #endif /* CK_LABELED */ int doxget(cx) int cx; { extern int /* External variables we need */ #ifdef RECURSIVE recursive, #endif /* RECURSIVE */ xfermode, fdispla, protocol, usepipes, g_binary, g_xfermode, g_displa, g_rpath, g_usepipes; extern char * rcv_move; /* Directory to move new files to */ extern char * rcv_rename; /* What to rename new files to */ extern char * rcvexcept[]; /* RECEIVE / GET exception list */ int opkt = 0; /* Flag for O-Packet needed */ #ifdef PIPESEND extern int pipesend; extern char * rcvfilter; #endif /* PIPESEND */ extern struct keytab rpathtab[]; extern int nrpathtab; extern CK_OFF_T calibrate; int asname = 0; /* Flag for have as-name */ int konly = 0; /* Kermit-only function */ int c, i, n, confirmed = 0; /* Workers */ int getval = 0; /* Whether to get switch value */ int rcvcmd = 0; /* Whether it is the RECEIVE command */ int mget = 0; /* Whether it is the MGET command */ struct stringint pv[SND_MAX+1]; /* Temporary array for switch values */ struct FDB sw, fl, cm; /* FDBs for each parse function */ char * cmdstr = "this command"; #ifdef NEWFTP if (cx == XXGET || cx == XXREGET || cx == XXMGET || cx == XXRETR) { extern int ftpget; extern int ftpisopen(); if ((ftpget == 1) || ((ftpget == 2) && ftpisopen())) { int x; x = doftpget(cx,0); debug(F101,"doftpget return","",x); if (x > -1) success = x; debug(F101,"doftpget success","",success); return(x); } } #endif /* NEWFTP */ debug(F101,"xget cx","",cx); oopts = -1; omode = -1; for (i = 0; i <= SND_MAX; i++) { /* Initialize switch values */ pv[i].sval = NULL; pv[i].ival = -1; pv[i].wval = (CK_OFF_T)-1; } /* Preset switch values based on top-level command that called us */ switch (cx) { case XXREC: /* RECEIVE */ cmdstr = "RECEIVE"; rcvcmd = 1; break; case XXGET: /* GET */ cmdstr = "GET"; konly = 1; break; #ifdef CK_RESEND case XXREGET: /* REGET */ cmdstr = "REGET"; konly = 1; pv[SND_BIN].ival = 1; /* Implies /BINARY */ pv[SND_RES].ival = 1; break; #endif /* CK_RESEND */ case XXRETR: /* RETRIEVE */ cmdstr = "RETRIEVE"; konly = 1; pv[SND_DEL].ival = 1; break; #ifdef PIPESEND case XXCREC: /* CRECEIVE */ cmdstr = "CRECEIVE"; konly = 1; rcvcmd = 1; pv[SND_CMD].ival = 1; break; case XXCGET: /* CGET */ cmdstr = "CGET"; konly = 1; pv[SND_CMD].ival = 1; break; #endif /* PIPESEND */ #ifndef NOMGET case XXMGET: /* MGET */ cmdstr = "MGET"; konly = 1; mget = 1; break; #endif /* NOMGET */ } debug(F111,"xget rcvcmd",cmdstr,rcvcmd); debug(F101,"xget konly","",konly); #ifdef CK_XYZ if (!rcvcmd && protocol != PROTO_K) { printf("?Sorry, %s works only with Kermit protocol\n",cmdstr); return(-9); } #endif /* CK_XYZ */ /* Set up chained parse functions... */ cmfdbi(&sw, /* First FDB - command switches */ _CMKEY, /* fcode */ rcvcmd ? "Optional name/template to store incoming files under, or switch" : "Remote filename, or switch", /* hlpmsg */ "", /* default */ "", /* addtl string data */ rcvcmd ? nrcvtab : ngettab, /* addtl numeric data 1: tbl size */ 4, /* addtl numeric data 2: 4 = cmswi */ xxstring, /* Processing function */ rcvcmd ? rcvtab : gettab, /* Keyword table */ &fl /* Pointer to next FDB */ ); if (rcvcmd || mget) /* RECEIVE or MGET */ cmfdbi(&fl, _CMTXT, /* fcode */ rcvcmd ? /* hlpmsg */ "Output filename or Command" : /* Output filename */ "File(s) to GET", /* Files we are asking for */ "", /* default */ "", /* addtl string data */ 0, /* addtl numeric data 1 */ 0, /* addtl numeric data 2 */ #ifdef COMMENT #ifdef CK_XYZ (protocol == PROTO_X || protocol == PROTO_XC) ? xxstring : (rcvcmd ? (xx_strp)0 : xxstring) #else rcvcmd ? (xx_strp)0 : xxstring /* Processing function */ #endif /* CK_XYZ */ #else /* COMMENT */ xxstring /* Always evaluate - fdc 2006/02/01 */ #endif /* COMMENT */ , NULL, &cm ); else cmfdbi(&fl, /* Remote filename or command */ _CMFLD, /* fcode */ "Remote filename", /* hlpmsg */ "", /* default */ "", /* addtl string data */ 0, /* addtl numeric data 1 */ 0, /* addtl numeric data 2 */ xxstring, NULL, &cm ); cmfdbi(&cm, /* Confirmation */ _CMCFM, /* fcode */ "", /* hlpmsg */ "", /* default */ "", /* addtl string data */ 0, /* addtl numeric data 1 */ 0, /* addtl numeric data 2 */ NULL, NULL, NULL ); /* (See doxsend() for fuller commentary) */ while (1) { /* Parse 0 or more switches */ x = cmfdb(&sw); /* Parse something */ debug(F101,"xget cmfdb","",x); if (x < 0) /* Error */ goto xgetx; /* or reparse needed */ if (cmresult.fcode != _CMKEY) /* Break out if not a switch */ break; c = cmgbrk(); /* Get break character */ if ((getval = (c == ':' || c == '=')) && !(cmgkwflgs() & CM_ARG)) { printf("?This switch does not take an argument\n"); x = -9; goto xgetx; } if (!getval && (cmgkwflgs() & CM_ARG)) { printf("?This switch requires an argument\n"); x = -9; goto xgetx; } n = cmresult.nresult; /* Numeric result = switch value */ debug(F101,"xget switch","",n); switch (n) { /* Process the switch */ #ifdef PIPESEND case SND_CMD: /* These take no args */ if (nopush) { printf("?Sorry, system command access is disabled\n"); x = -9; goto xgetx; } else if (rcvfilter) { printf( "?Sorry, no GET /COMMAND when RECEIVE FILTER selected\n"); x = -9; goto xgetx; } if (rcvcmd) sw.hlpmsg = "Command, or switch"; /* Change help message */ /* Fall thru... */ #endif /* PIPESEND */ case SND_REC: /* /RECURSIVE */ pv[SND_PTH].ival = PATH_REL; /* Implies relative pathnames */ pv[n].ival = 1; /* Set the recursive flag */ break; case SND_RES: /* /RECOVER */ pv[SND_BIN].ival = 1; /* Implies /BINARY */ pv[n].ival = 1; /* Set the resend flag */ break; case SND_DEL: /* /DELETE */ case SND_SHH: /* /QUIET */ case SND_CAL: /* /CALIBRATE */ case SND_XPA: /* /TRANSPARENT */ pv[n].ival = 1; /* Just set the appropriate flag */ break; case SND_PIP: /* /PIPES:{ON,OFF} */ if (!getval) { pv[n].ival = 1; break; } if ((x = cmkey(onoff,2,"","on",xxstring)) < 0) goto xgetx; if (!nopush) pv[n].ival = x; break; /* File transfer modes - each undoes the others */ case SND_BIN: /* Binary */ case SND_TXT: /* Text */ case SND_IMG: /* Image */ case SND_LBL: /* Labeled */ pv[SND_BIN].ival = 0; /* Unset all */ pv[SND_TXT].ival = 0; pv[SND_IMG].ival = 0; pv[SND_LBL].ival = 0; pv[n].ival = 1; /* Set the requested one */ break; case SND_EXC: /* Excludes */ if (!getval) break; if ((x = cmfld("Pattern","",&s,xxstring)) < 0) { if (x == -3) { printf("?Pattern required\n"); x = -9; } goto xgetx; } if (pv[n].sval) free(pv[n].sval); y = strlen(s); if (y > 256) { printf("?Pattern too long - 256 max\n"); x = -9; goto xgetx; } pv[n].sval = malloc(y+1); if (pv[n].sval) { strcpy(pv[n].sval,s); /* safe */ pv[n].ival = 1; } break; #ifdef COMMENT /* Not implemented */ case SND_PRI: /* GET to printer */ pv[n].ival = 1; if (!getval) break; if ((x = cmfld("Print options","",&s,xxstring)) < 0) goto xgetx; pv[n].sval = malloc((int)strlen(s)+1); if (pv[n].sval) strcpy(pv[n].sval,s); /* safe */ break; #endif /* COMMENT */ case SND_MOV: /* MOVE after */ case SND_REN: /* RENAME after */ if (!getval) break; if ((x = cmfld(n == SND_MOV ? "device and/or directory for source file after sending" : "new name for source file after sending", "", &s, n == SND_MOV ? xxstring : NULL )) < 0) { if (x == -3) { printf("%s\n", n == SND_MOV ? "?Destination required" : "?New name required" ); x = -9; } goto xgetx; } if (pv[n].sval) { free(pv[n].sval); pv[n].sval = NULL; } s = brstrip(s); y = strlen(s); if (y > 0) { pv[n].sval = malloc(y+1); if (pv[n].sval) { strcpy(pv[n].sval,s); /* safe */ pv[n].ival = 1; } } break; case SND_ASN: /* As-name */ if (!getval) break; if (mget) { printf("?Sorry, as-name not allowed with MGET\n"); x = -9; goto xgetx; } if ( #ifdef COMMENT (x = cmfld("Name to store it under","",&s,NULL)) #else (x = cmfld("Name to store it under","",&s,xxstring)) #endif /* COMMENT */ < 0) goto xgetx; s = brstrip(s); if ((y = strlen(s)) > 0) { if (pv[n].sval) free(pv[n].sval); pv[n].sval = malloc(y+1); if (pv[n].sval) { strcpy(pv[n].sval,s); /* safe */ pv[n].ival = 1; } } break; #ifdef PIPESEND case SND_FLT: /* Filter */ debug(F101,"xget /filter getval","",getval); if (!getval) break; if ((x = cmfld("Filter program to receive through", "",&s,NULL)) < 0) { if (x == -3) s = ""; else goto xgetx; } if (*s) s = brstrip(s); y = strlen(s); for (x = 0; x < y; x++) { /* Make sure they included "\v(...)" */ if (s[x] != '\\') continue; if (s[x+1] == 'v') break; } if (x == y) { printf( "?Filter must contain a replacement variable for filename.\n" ); x = -9; goto xgetx; } pv[n].ival = 1; if (pv[n].sval) { free(pv[n].sval); pv[n].sval = NULL; } if ((y = strlen(s)) > 0) { if ((pv[n].sval = malloc(y+1))) strcpy(pv[n].sval,s); /* safe */ } break; #endif /* PIPESEND */ case SND_PTH: /* Pathnames */ if (!getval) { pv[n].ival = PATH_REL; break; } if ((x = cmkey(rpathtab,nrpathtab,"","on",xxstring)) < 0) goto xgetx; pv[n].ival = x; /* Ditto */ break; case SND_NAM: /* Filenames */ if (!getval) break; if ((x = cmkey(fntab,nfntab,"","converted",xxstring)) < 0) goto xgetx; pv[n].ival = x; break; case SND_PRO: /* Protocol to use */ if (!getval) break; if ((x = cmkey(protos,nprotos,"File-transfer protocol","", xxstring)) < 0) { if (x == -3) x = 0; else goto xgetx; } debug(F111,"xget /proto",atmbuf,x); pv[n].ival = x; if (konly && x != PROTO_K) { printf( "?Sorry, this command works only with Kermit protocol\n" ); x = -9; goto xgetx; } break; default: printf("?Unexpected switch value - %d\n",cmresult.nresult); x = -9; goto xgetx; } } debug(F101,"xget cmresult fcode","",cmresult.fcode); cmarg = line; /* Initialize string pointers */ cmarg2 = tmpbuf; asname = 0; line[0] = NUL; /* and buffers. */ tmpbuf[0] = NUL; switch (cmresult.fcode) { /* How did we get out of switch loop */ case _CMFLD: /* (3) Remote filespec */ ckstrncpy(line,cmresult.sresult,LINBUFSIZ); break; case _CMTXT: /* (4) As-name */ if (rcvcmd) { ckstrncpy(tmpbuf,cmresult.sresult,TMPBUFSIZ); if ((int)strlen(tmpbuf) > 0) asname = 1; } else { ckstrncpy(line,cmresult.sresult,LINBUFSIZ); } case _CMCFM: /* (6) Confirmation */ confirmed = 1; break; default: printf("?Unexpected function code: %d\n",cmresult.fcode); x = -9; goto xgetx; } debug(F110,"xget string",cmarg,0); debug(F101,"xget confirmed","",confirmed); cmarg = brstrip(cmarg); /* Strip any braces */ if (!confirmed) { /* CR not typed yet, get more fields */ if (pv[SND_CMD].ival > 0) { debug(F100,"xget calling cmtxt","",0); x = cmtxt("Local command to pipe into","",&s,NULL); if (x < 0 && x != -3) goto xgetx; if (x != -3) { ckstrncpy(tmpbuf,s,TMPBUFSIZ); asname = 1; } } else if (!rcvcmd) { #ifdef VMS /* cmofi() fails if you give it a directory name */ x = cmfld("Name or directory for incoming file","",&s,NULL); debug(F111,"xget cmfld",s,x); #else x = cmofi("Name or directory for incoming file","",&s,NULL); debug(F111,"xget cmofi",s,x); #endif /* VMS */ if (x < 0 && x != -3) goto xgetx; if (x != -3) { ckstrncpy(tmpbuf,s,TMPBUFSIZ); if ((x = cmcfm()) < 0) goto xgetx; asname = 1; } } } /* Arrive here with cmarg and cmarg2 all set */ debug(F111,"xget asname",cmarg2,asname); if (!asname) { if (pv[SND_ASN].sval) ckstrncpy(tmpbuf,pv[SND_ASN].sval,TMPBUFSIZ); else tmpbuf[0] = NUL; } cmarg2 = brstrip(cmarg2); /* Strip outer braces if any. */ debug(F110,"xget cmarg",cmarg,0); debug(F110,"xget cmarg2",cmarg2,0); if (!*cmarg && (cx == XXGET || cx == XXREGET || cx == XXCGET || cx == XXMGET)) { printf("?A remote file specification is required\n"); x = -9; goto xgetx; } #ifdef PIPESEND if (pv[SND_CMD].ival > 0) { /* /COMMAND sets pipesend flag */ x = -9; if (!*cmarg2) { printf("?Command required\n"); goto xgetx; } else if (nopush) { printf("?Sorry, system command access is disabled\n"); goto xgetx; } else if (rcvfilter) { printf("?Sorry, no GET /COMMAND while RECEIVE FILTER selected\n"); goto xgetx; } else pipesend = 1; } debug(F101,"xget /COMMAND pipesend","",pipesend); #endif /* PIPESEND */ #ifdef CK_RESEND if (pv[SND_RES].ival > 0) { /* REGET or GET /RECOVER */ #ifdef RECURSIVE if (pv[SND_REC].ival > 0) { /* RECURSIVE */ #ifdef COMMENT printf("?Unsupported option combination: /RECOVER /RECURSIVE\n"); x = -9; goto xgetx; #else opkt = 1; #endif /* COMMENT */ } #endif /* RECURSIVE */ if (pv[SND_DEL].ival > 0) { /* /DELETE */ #ifdef COMMENT printf("?Unsupported option combination: /RECOVER /DELETE\n"); x = -9; goto xgetx; #else opkt = 1; #endif /* COMMENT */ } } #endif /* CK_RESEND */ if (pv[SND_EXC].ival > 0) /* /EXCEPT */ makelist(pv[SND_EXC].sval,rcvexcept,NSNDEXCEPT); #ifdef IKS_OPTION if (!rcvcmd #ifdef CK_XYZ && protocol == PROTO_K #endif /* CK_XYZ */ ) { if (!iks_wait(KERMIT_REQ_START,1)) { printf( "?A Kermit Server is not available to process this command\n"); x = -9; /* correct the return code */ goto xgetx; } } #endif /* IKS_OPTION */ #ifdef CK_XYZ { int po, pg; /* (for clarity) */ po = pv[SND_PRO].ival; /* /PROTOCOL option */ pg = protocol; /* Protocol global */ if ((rcvcmd && !*cmarg2) && /* If no as-name was given */ /* and /PROTOCOL is XMODEM or global protocol is XMODEM... */ ((po < 0 && (pg == PROTO_X || pg == PROTO_XC)) || (po > -1 && (po == PROTO_X || po == PROTO_XC))) ) { printf( "Sorry, you must specify a name when receiving a file with XMODEM protocol\n" ); x = -9; goto xgetx; } } #endif /* CK_XYZ */ #ifdef RECURSIVE if (pv[SND_REC].ival > 0) { /* RECURSIVE */ recursive = 1; pv[SND_PTH].ival = PATH_REL; /* Implies relative pathnames too */ } #endif /* RECURSIVE */ if (pv[SND_PIP].ival > -1) { g_usepipes = usepipes; usepipes = pv[SND_PIP].ival; } /* Save global protocol parameters */ g_proto = protocol; #ifdef CK_LABELED g_lf_opts = lf_opts; /* Save labeled transfer options */ #endif /* CK_LABELED */ g_urpsiz = urpsiz; /* Receive packet length */ g_spsizf = spsizf; /* Send packet length flag */ g_spsiz = spsiz; /* Send packet length */ g_spsizr = spsizr; /* etc etc */ g_spmax = spmax; g_wslotr = wslotr; g_prefixing = prefixing; g_fncact = fncact; g_fncnv = fncnv; g_fnspath = fnspath; g_fnrpath = fnrpath; g_xfrxla = xfrxla; if (pv[SND_PRO].ival > -1) { /* Change according to switch */ protocol = pv[SND_PRO].ival; if (ptab[protocol].rpktlen > -1) /* copied from initproto() */ urpsiz = ptab[protocol].rpktlen; if (ptab[protocol].spktflg > -1) spsizf = ptab[protocol].spktflg; if (ptab[protocol].spktlen > -1) { spsiz = ptab[protocol].spktlen; if (spsizf) spsizr = spmax = spsiz; } if (ptab[protocol].winsize > -1) wslotr = ptab[protocol].winsize; if (ptab[protocol].prefix > -1) prefixing = ptab[protocol].prefix; if (ptab[protocol].fnca > -1) fncact = ptab[protocol].fnca; if (ptab[protocol].fncn > -1) fncnv = ptab[protocol].fncn; if (ptab[protocol].fnsp > -1) fnspath = ptab[protocol].fnsp; if (ptab[protocol].fnrp > -1) fnrpath = ptab[protocol].fnrp; } debug(F101,"xget protocol","",protocol); debug(F111,"xget cmarg2",cmarg2,xfermode); g_xfermode = xfermode; g_binary = binary; if (pv[SND_BIN].ival > 0) { /* Change according to switch */ xfermode = XMODE_M; binary = XYFT_B; /* FILE TYPE BINARY */ omode = GMOD_BIN; /* O-Packet mode */ debug(F101,"doxget /BINARY xfermode","",xfermode); } else if (pv[SND_TXT].ival > 0) { /* Ditto for /TEXT */ xfermode = XMODE_M; binary = XYFT_T; omode = GMOD_TXT; debug(F101,"doxget /TEXT xfermode","",xfermode); } else if (pv[SND_IMG].ival > 0) { xfermode = XMODE_M; #ifdef VMS binary = XYFT_I; #else binary = XYFT_B; #endif /* VMS */ omode = GMOD_TXT; debug(F101,"doxget /IMAGE xfermode","",xfermode); } #ifdef CK_LABELED else if (pv[SND_LBL].ival > 0) { xfermode = XMODE_M; binary = XYFT_L; omode = GMOD_LBL; debug(F101,"doxget /LABELED xfermode","",xfermode); } #endif /* CK_LABELED */ debug(F101,"xget binary","",binary); debug(F101,"xget omode","",omode); if (pv[SND_XPA].ival > 0) /* /TRANSPARENT */ xfrxla = 0; /* Don't translate character sets */ #ifdef PIPESEND if (pv[SND_FLT].ival > 0) makestr(&rcvfilter,pv[SND_FLT].sval); #endif /* PIPESEND */ #ifdef CK_TMPDIR if (pv[SND_MOV].ival > 0) { int len; char * p = pv[SND_MOV].sval; #ifdef CK_LOGIN if (isguest) { printf("?Sorry, /MOVE-TO not available to guests\n"); x = -9; goto xgetx; } #endif /* CK_LOGIN */ len = strlen(p); if (!isdir(p)) { /* Check directory */ #ifdef CK_MKDIR char * s = NULL; s = (char *)malloc(len + 4); if (s) { strcpy(s,p); /* safe */ #ifdef datageneral if (s[len-1] != ':') { s[len++] = ':'; s[len] = NUL; } #else if (s[len-1] != '/') { s[len++] = '/'; s[len] = NUL; } #endif /* datageneral */ s[len++] = 'X'; s[len] = NUL; x = zmkdir(s); free(s); if (x < 0) { printf("?Can't create \"%s\"\n",p); x = -9; goto xgetx; } } #else printf("?Directory \"%s\" not found\n",p); x = -9; goto xgetx; #endif /* CK_MKDIR */ } zfnqfp(p,LINBUFSIZ,line); makestr(&rcv_move,line); } #endif /* CK_TMPDIR */ if (pv[SND_REN].ival > 0) { /* /RENAME-TO:name */ char * p = pv[SND_REN].sval; #ifdef CK_LOGIN if (isguest) { printf("?Sorry, /RENAME-TO not available to guests\n"); x = -9; goto xgetx; } #endif /* CK_LOGIN */ if (!p) p = ""; if (!*p) { printf("?New name required for /RENAME\n"); x = -9; goto xgetx; } p = brstrip(p); makestr(&rcv_rename,p); debug(F110,"xget rcv_rename","",0); } #ifdef CALIBRATE if (pv[SND_CAL].ival > 0) calibrate = (CK_OFF_T)1; #endif /* CALIBRATE */ g_displa = fdispla; if (pv[SND_SHH].ival > 0) fdispla = 0; debug(F101,"xget display","",fdispla); if (pv[SND_NAM].ival > -1) { /* /FILENAMES */ g_fncnv = fncnv; /* Save global value */ fncnv = pv[SND_NAM].ival; debug(F101,"xsend fncnv","",fncnv); /* We should also handle O packet filename option here */ /* but we don't really need to since WHATAMI already handles it */ } if (pv[SND_PTH].ival > -1) { /* PATHNAMES */ g_rpath = fnrpath; /* Save global values */ fnrpath = pv[SND_PTH].ival; debug(F101,"xsend fnrpath","",fnrpath); #ifndef NZLTOR if (fnrpath != PATH_OFF) { g_fncnv = fncnv; fncnv = XYFN_L; debug(F101,"xsend fncnv","",fncnv); } /* We should also handle O packet pathname option here */ /* but we don't really need to since WHATAMI already handles it */ #endif /* NZLTOR */ } /* Set protocol start state */ if (opkt) { /* Extended GET Options*/ sstate = (CHAR) 'o'; oopts = 0; if (pv[SND_DEL].ival > 0) oopts |= GOPT_DEL; /* GET /DELETE */ if (pv[SND_RES].ival > 0) oopts |= GOPT_RES; /* GET /RECOVER */ if (pv[SND_REC].ival > 0) oopts |= GOPT_REC; /* GET /RECURSIVE */ } else if (rcvcmd) sstate = (CHAR) 'v'; /* RECEIVE or CRECEIVE */ else if (pv[SND_DEL].ival > 0) sstate = (CHAR) 'h'; /* GET /DELETE (= RETRIEVE) */ else if (pv[SND_RES].ival > 0) sstate = (CHAR) 'j'; /* GET /RECOVER (= REGET) */ else sstate = (CHAR) 'r'; /* Regular GET */ getcmd = 1; debug(F000,"xget sstate","",sstate); #ifdef MAC what = W_RECV; scrcreate(); #endif /* MAC */ if (local) { if (pv[SND_SHH].ival != 0) displa = 1; if (protocol == PROTO_K) /* fdc 20070108 */ ttflui(); } x = 0; #ifdef PIPESEND if (pipesend) goto xgetx; #endif /* PIPESEND */ #ifdef CK_TMPDIR /* cmarg2 is also allowed to be a device or directory name; even the name of a directory that doesn't exist. */ y = strlen(cmarg2); debug(F111,"xget strlen(cmarg2)",cmarg2,y); if ((y > 0) && #ifdef OS2 ((isalpha(cmarg2[0]) && cmarg2[1] == ':' && cmarg2[2] == NUL) || (cmarg[y-1] == '/' || cmarg[y-1] == '\\') || isdir(cmarg2)) #else #ifdef UNIXOROSK (cmarg2[y-1] == '/' || isdir(cmarg2)) #else #ifdef VMS (cmarg2[y-1] == ']' || cmarg2[y-1] == '>' || isdir(cmarg2)) #else #ifdef STRATUS (cmarg2[y-1] == '>' || isdir(cmarg2)) #else #ifdef datageneral (cmarg2[y-1] == ':' || cmarg[0] == ':' || isdir(cmarg2)) #else isdir(cmarg2) #endif /* datageneral */ #endif /* STRATUS */ #endif /* VMS */ #endif /* UNIXOROSK */ #endif /* OS2 */ ) { debug(F110,"doxget RECEIVE cmarg2 disk or dir",cmarg2,0); if (!f_tmpdir) { int x; s = zgtdir(); if (s) { ckstrncpy(savdir,s,TMPDIRLEN); /* remember old disk/dir */ f_tmpdir = 1; /* and that we did this */ } else { printf("?Can't get current directory\n"); cmarg2 = ""; f_tmpdir = 0; x = -9; goto xgetx; } #ifdef CK_MKDIR x = zchki(cmarg2); /* Does as-name exist? */ if (x == -1) { /* Doesn't exist */ char * p = NULL; /* Try to create it */ x = strlen(cmarg2); if ((p = (char *)malloc(x+4))) { sprintf(p,"%s%s",cmarg2,"x.x"); /* SAFE (prechecked) */ x = zmkdir(p); free(p); if (x < 0) { printf("?Can't create %s\n",cmarg2); x = -9; goto xgetx; } } } #endif /* CK_MKDIR */ if (!zchdir(cmarg2)) { /* change to given disk/directory, */ printf("?Can't access %s\n",cmarg2); x = -9; goto xgetx; } cmarg2 = ""; } } #endif /* CK_TMPDIR */ ckstrncpy(fspec,cmarg,CKMAXPATH); /* Note - this is a REMOTE filespec */ debug(F111,"xget fspec",fspec,fspeclen); debug(F110,"xget cmarg2",cmarg2,0); xgetx: for (i = 0; i < SND_MAX; i++) if (pv[i].sval) free(pv[i].sval); return(x); } #endif /* NOXFER */ #ifndef NOSPL /* D O G T A -- Do _GETARGS or _PUTARGS Command. Used by XIF, FOR, WHILE, and SWITCH, each of which are implemented as 2-level macros; the first level defines the macro, the second runs it. This routine hides the fact that they are macros by importing the macro arguments (if any) from two levels up, to make them available in the IF, FOR, SWITCH, and WHILE commands themselves; for example as loop indices, etc, and within the IF/FOR/WHILE/SWITCH body itself. _PUTARGS is in case we changed any of these variables or used SHIFT on them, so the new values won't be lost as we pop up the stack. */ int dogta(cx) int cx; { int i, n; char c, *p, mbuf[4]; extern int topargc, cmdint; extern char ** topxarg; if ((y = cmcfm()) < 0) return(y); debug(F101,"dogta cx","",cx); debug(F101,"dogta maclvl","",maclvl); if (cx == XXGTA) { debug(F101,"dogta _GETARGS maclvl","",maclvl); } else if (cx == XXPTA) { debug(F101,"dogta _PUTARGS maclvl","",maclvl); } else { return(-2); } if (maclvl < 1) return(success = 0); /* Make new copies of macro arguments /%0..9 */ mbuf[0] = '%'; mbuf[1] = '0'; mbuf[2] = NUL; /* Argument name buf */ if (cx == XXPTA) { /* Go NOINT because _PUTARGS */ if (cmdint) /* temporarily changes maclvl. */ connoi(); /* Interrupts OFF. */ } for (i = 0; i < 10; i++) { /* For all args */ c = (char) (i + '0'); /* Make name */ mbuf[1] = (char) c; /* Insert digit */ if (cx == XXGTA) { /* Get arg from level-minus-2 */ if (maclvl == 1) p = g_var[c]; /* If at level 1 use globals 0..9 */ else p = m_arg[maclvl-2][i]; /* Otherwise they're on the stack */ addmac(mbuf,p); #ifdef COMMENT if (maclvl > 1) makestr(&(m_line[maclvl]),m_line[maclvl-2]); #endif /* COMMENT */ } else if (cx == XXPTA) { /* Put args level+2 */ maclvl -= 2; /* This is gross, it's because we're */ addmac(mbuf,m_arg[maclvl+2][i]); /* adding macros two levels up */ maclvl += 2; /* and addmac() uses maclvl. */ count[cmdlvl - 2] = count[cmdlvl]; intime[cmdlvl - 2] = intime[cmdlvl]; inpcas[cmdlvl - 2] = inpcas[cmdlvl]; takerr[cmdlvl - 2] = takerr[cmdlvl]; merror[cmdlvl - 2] = merror[cmdlvl]; xquiet[cmdlvl - 2] = xquiet[cmdlvl]; xvarev[cmdlvl - 2] = xvarev[cmdlvl]; } else return(success = 0); /* Bad call to this routine */ } if (cx == XXPTA) { /* Restore interrupts if we */ if (cmdint) /* turned them off above. */ conint(trap,stptrap); } /* Now take care of the argument vector array \&_[], \v(return), */ /* and \v(argc) by just copying the pointers. */ if (cx == XXGTA) { /* GETARGS from 2 levels up */ if (maclvl == 1) { a_ptr[0] = topxarg; /* \&_[] array */ a_dim[0] = topargc - 1; /* Dimension doesn't include [0] */ m_xarg[maclvl] = topxarg; n_xarg[maclvl] = topargc; /* But \v(argc) does include \%0 */ macargc[maclvl] = topargc; makestr(&(mrval[maclvl+1]),mrval[0]); /* (see vnlook()) */ } else { a_ptr[0] = m_xarg[maclvl-2]; a_dim[0] = n_xarg[maclvl-2]; m_xarg[maclvl] = m_xarg[maclvl-2]; n_xarg[maclvl] = n_xarg[maclvl-2]; macargc[maclvl] = n_xarg[maclvl-2]; makestr(&(mrval[maclvl+1]),mrval[maclvl-1]); /* (see vnlook()) */ } } else { /* PUTARGS 2 levels up */ if (maclvl > 1) { a_ptr[0] = m_xarg[maclvl]; m_xarg[maclvl-2] = m_xarg[maclvl]; a_dim[0] = n_xarg[maclvl]; n_xarg[maclvl-2] = n_xarg[maclvl]; macargc[maclvl-2] = n_xarg[maclvl]; } } return(1); /* Internal command - don't change success */ } #endif /* NOSPL */ #ifndef NOSPL /* Do the GOTO and [_]FORWARD commands. s = Label to search for, cx = function code: XXGOTO, XXFWD, or XXXFWD. */ int dogoto(s, cx) char *s; int cx; { int i, j, x, y, z, bc; int empty = 0, stopflg = 0; char * cmd; /* Name of this command */ char tmplbl[LBLSIZ+1], *lp; /* Current label from command stream */ char tmp2[LBLSIZ+1]; /* SWITCH label conversion buffer */ char tmp3[LBLSIZ+1]; /* Target label */ stopflg = (cx == XXXFWD); /* _FORWARD (used in SWITCH) */ bc = 0; /* Brace counter */ cmd = (cx == XXGOTO) ? "GOTO" : ((cx == XXFWD) ? "FORWARD" : "_FORWARD"); if (!s) s = ""; if (!*s) empty = 1; #ifdef DEBUG if (deblog) { debug(F111,"GOTO command",cmd,cx); debug(F101,"GOTO cmdlvl","",cmdlvl); debug(F101,"GOTO maclvl","",maclvl); debug(F101,"GOTO tlevel","",tlevel); debug(F111,"GOTO target",s,empty); } #endif /* DEBUG */ debug(F110,cmd,s,0); x = ckstrncpy(tmp3+1,s,LBLSIZ); debug(F101,"GOTO target len","",x); debug(F101,"GOTO target at x","",s[x]); if (s[x]) { debug(F100,"GOTO target overflow","",0); printf("?GOTO target or SWITCH case label too long\n"); if (stopflg) dostop(); /* If in SWITCH return to prompt */ return(success = 0); } s = tmp3+1; if (*s != ':') { /* Make copy of label */ tmp3[0] = ':'; /* guaranteed to start with ":" */ s--; } if (!stopflg && !empty) { if (s[1] == '.' || s[1] == SP || s[1] == NUL) { printf("?Bad label syntax - '%s'\n",s); if (stopflg) dostop(); /* If in SWITCH return to prompt */ return(success = 0); } } if (cmdlvl == 0) { printf("?Sorry, %s only works in a command file or macro\n",cmd); return(success = 0); } y = strlen(s); /* y = length of target label */ debug(F111,cmd,s,y); while (cmdlvl > 0) { /* As long as not at top level... */ if (cmdstk[cmdlvl].src == CMD_MD) { /* GOTO inside macro */ int i, m, flag; char *xp, *tp; /* GOTO: rewind the macro; FORWARD: start at current position */ lp = (cx == XXGOTO) ? macx[maclvl] : macp[maclvl]; m = (int)strlen(lp); debug(F111,"GOTO in macro",lp,m); flag = 1; /* flag for valid label position */ for (i = 0; i < m; i++,lp++) { /* search for label in macro body */ if (*lp == '{') /* But only at this level */ bc++; /* Anything inside braces is off */ else if (*lp == '}') /* limits. */ bc--; if (stopflg && bc > 0) /* This is good for SWITCH */ continue; /* but interferes with WHILE, etc. */ if (*lp == ',') { flag = 1; continue; } if (flag) { /* If in valid label position */ if (*lp == SP) /* eat leading spaces */ continue; if (*lp != ':') { /* Look for label introducer */ flag = 0; /* this isn't it */ continue; /* keep looking */ } } if (!flag) /* We don't have a label */ continue; /* so keep looking... */ xp = lp; tp = tmplbl; /* Copy the label from the macro */ j = 0; /* to make it null-terminated */ while ((*tp = *xp)) { if (j++ > LBLSIZ-1) { /* j = length of word from macro */ printf("?GOTO target or SWITCH case label too long\n"); if (stopflg) dostop(); /* Return to prompt */ return(success = 0); } if (!*tp || *tp == ',') /* Look for end of word */ break; else tp++, xp++; /* Next character */ } *tp = NUL; /* In case we stopped early */ /* Now do caseless string comparison, using longest length */ debug(F111,"macro GOTO label",s,y); debug(F111,"macro target label",tmplbl,j); if (stopflg) { /* Allow variables as SWITCH labels */ int n = LBLSIZ - 1; char * p = tmp2; zzstring(tmplbl,&p,&n); if (n < 0) { printf("?GOTO target or SWITCH case label too long\n"); if (stopflg) dostop(); /* Return to prompt */ return(0); } ckstrncpy(tmplbl,tmp2,LBLSIZ); } debug(F111,"GOTO s",s,y); debug(F111,"GOTO tmplbl",tmplbl,j); debug(F111,"GOTO empty",ckitoa(stopflg),empty); if (empty) { /* Empty target */ z = (!strcmp(s,":") && /* String is empty */ /* and Label is ":" or ":*"... */ (!strcmp(tmplbl,":") || !strcmp(tmplbl,":*"))) ? 0 : 1; debug(F111,"GOTO","A",z); } else if (stopflg) { z = ckmatch(tmplbl,s,inpcas[cmdlvl],1) ? 0 : 1; debug(F111,"GOTO","B",z); } else { z = (stopflg && inpcas[cmdlvl]) ? strcmp(s,tmplbl) : ckstrcmp(s,tmplbl,(y > j) ? y : j, 0); debug(F111,"GOTO","C",z); } if (!z) { break; } else if (stopflg && !ckstrcmp(":default",tmplbl,(8 > j) ? 8 : j, 0)) { debug(F100,"GOTO DEFAULT","",0); break; } else { flag = 0; } } debug(F111,"GOTO macro i",cmd,i); debug(F111,"GOTO macro m",cmd,m); if (i >= m) { /* Didn't find the label */ debug(F101,"GOTO failed cmdlvl","",cmdlvl); #ifdef COMMENT /* MOVED TO AFTER POPCLVL ABOUT 20 LINES DOWN 5 AUG 2002 */ if (stopflg) return(0); #endif /* COMMENT */ if ((maclvl > 0) && (m_arg[maclvl-1][0]) && (cmdstk[cmdlvl].src == CMD_MD) && (!strncmp(m_arg[maclvl-1][0],"_xif",4) || !strncmp(m_arg[maclvl-1][0],"_for",4) || !strncmp(m_arg[maclvl-1][0],"_swi",4) || !strncmp(m_arg[maclvl-1][0],"_whi",4))) { dogta(XXPTA); /* Restore args */ debug(F101,"GOTO in XIF/FOR/WHI/SWI popping","",cmdlvl); popclvl(); /* Pop an extra level */ } debug(F101,"GOTO popping","",cmdlvl); if (!popclvl()) { /* pop up to next higher level */ printf("?Label '%s' not found\n",s); /* if none */ return(0); /* Quit */ } else if (stopflg) { /* SWITCH no case label match */ return(0); /* and no DEFAULT lable. */ } else { continue; /* otherwise look again */ } } debug(F110,"GOTO found macro label",tmplbl,0); macp[maclvl] = lp; /* set macro buffer pointer */ return(1); } else if (cmdstk[cmdlvl].src == CMD_TF) { x = 0; /* GOTO issued in take file */ debug(F111,"GOTO in TAKE file",cmd,cx); if (cx == XXGOTO) { /* If GOTO, but not FORWARD, */ rewind(tfile[tlevel]); /* search file from beginning */ tfline[tlevel] = 0; } while (! feof(tfile[tlevel])) { #ifdef COMMENT /* This is wrong - it lets us jump to labels inside inferior blocks */ tfline[tlevel]++; if (fgets(line,LINBUFSIZ,tfile[tlevel]) == NULL) /* Get line */ #else if (getnct(line,LINBUFSIZ,tfile[tlevel],0) < 0) #endif /* COMMENT */ break; /* If no more, done, label not found */ lp = line; /* Got line */ while (*lp == SP || *lp == HT) lp++; /* Strip leading whitespace */ if (*lp != ':') continue; /* Check for label introducer */ while (*(lp+1) == SP) { /* Remove space between : and name */ *(lp+1) = ':'; lp++; /* Strip leading whitespace */ } tp = lp; /* Get end of word */ j = 0; while (*tp) { /* And null-terminate it */ if (*tp < 33) { *tp = NUL; break; } else tp++, j++; } if (!ckstrcmp(lp,s,(y > j) ? y : j,0)) { /* Caseless compare */ x = 1; /* Got it */ break; /* done. */ } else if (stopflg && !ckstrcmp(":default",tmplbl,(8 > j) ? 8 : j,0)) { x = 1; break; } } if (x == 0) { /* If not found, print message */ debug(F101,"GOTO failed at cmdlvl","",cmdlvl); if (stopflg) return(0); if (!popclvl()) { /* pop up to next higher level */ printf("?Label '%s' not found\n",s); /* if none */ return(0); /* quit */ } else continue; /* otherwise look again */ } return(x); /* Send back return code */ } } printf("?Stack problem in GOTO %s\n",s); /* Shouldn't see this */ return(0); } #endif /* NOSPL */ /* Finish parsing and do the IF, XIF, and WHILE commands */ #ifndef NOSPL /* C H K V A R -- Check (if it's a) Variable */ #ifdef OLDCHKVAR /* Crude and disgusting, but needed for OS/2, DOS, and Windows, where filenames have backslashes in them. How do we know if a backslash in a filename is a directory separator, or if it's a Kermit backslash? This routine does a rough syntax check of the next few characters and if it looks like it MIGHT be a variable, then it tries to evaluate it, and if the result is not empty, we say it's a variable, although sometimes it might not be -- some cases are truly ambiguous. For example there might a DOS directory called \%a, and we also have a variable with the same name. This is all for the sake of not having to tell PC users that they have to double all backslashes in file and directory names. */ #else /* Somewhat less crude & disgusting. The previous method was nondeterministic and (worse) it interfered with macro argument passing. So now we only check the syntax of backslash-items to see if they are variables, but we do NOT check their values. */ #endif /* OLDCHKVAR */ /* Call with a string pointer pointing at the backslash of the purported variable. Returns 1 if it has the syntax of a variable, 0 if not. */ int chkvar(s) char *s; { int z = 0; /* Return code - assume failure. */ if (!s) s = ""; /* Watch our for null pointers. */ if (!*s) return(0); /* Empty arg so not a variable. */ if (*s == CMDQ) { /* Object begins with backslash. */ char c; c = s[1]; /* Character following backslash. */ if (c) { int t = 0; if (c == CMDQ) /* Quoted backslash */ return(1); c = (char) (islower(c) ? toupper(c) : c); /* Otherwise... */ if (c == '%') { /* Simple variable */ #ifdef OLDCHKVAR t = 1; #else return(1); #endif /* OLDCHKVAR */ } else if (c == '&') { /* Array */ if (!s[2]) return(0); if (s[3] == '[') t = ckindex("]",s,4,0,1); #ifndef OLDCHKVAR return((t > 0) ? 1 : 0); #endif /* OLDCHKVAR */ } else if (c == '$' || /* Environment variable */ c == 'V' || /* Built-in variable */ c == 'M') { /* Macro name */ t = (s[2] == '('); #ifndef OLDCHKVAR return((t > 0) ? 1 : 0); #endif /* OLDCHKVAR */ } else if (c == 'F') { /* Function reference */ /* Don't actually call it - it might have side effects */ int x; if ((x = ckindex("(",s,3,0,1))) /* Just check syntax */ if ((x = ckindex(")",s,x,0,1))) z = 1; /* Insert a better syntax check here if necessary */ } #ifdef OLDCHKVAR if (t) { t = 255; /* This lets us test \v(xxx) */ lp = line; /* and even \f...(xxx) */ zzstring(s,&lp,&t); /* Evaluate it, whatever it is. */ t = strlen(line); /* Get its length. */ debug(F111,"chkvar",line,t); z = t > 0; /* If length > 0, it's defined */ } #endif /* OLDCHKVAR */ } } return(z); } /* B O O L E X P -- Evaluate a Boolean expression */ #define BOOLLEN 1024 static char boolval[BOOLLEN]; int boolexp(cx) int cx; { int x, y, z; char *s, *p; int parens = 0, pcount = 0, ecount = 0; char *q, *bx; struct FDB kw, nu; #ifdef FNFLOAT struct FDB fl; CKFLOAT f1 = 0.0, f2 = 0.0; int f1flag = 0, f2flag = 0; #endif /* FNFLOAT */ #ifdef OS2 extern int keymac; #endif /* OS2 */ not = 0; /* Flag for whether "NOT" was seen */ z = 0; /* Initial IF condition */ ifargs = 0; /* Count of IF condition words */ bx = boolval; /* Initialize boolean value */ *bx = NUL; ifagain: cmfdbi(&kw, /* First FDB - command switches */ _CMKEY, /* fcode */ "Number, numeric-valued variable, Boolean expression, or keyword", "", /* default */ "", /* addtl string data */ nif, /* addtl numeric data 1: tbl size */ 0, /* addtl numeric data 2: 4 = silent */ xxstring, /* Processing function */ iftab, /* Keyword table */ &nu /* Pointer to next FDB */ ); cmfdbi(&nu, /* 2nd FDB - An integer */ _CMNUM, /* fcode */ "", /* hlpmsg */ "", /* Default */ "", /* addtl string data */ 0, 0, xxstring, NULL, #ifdef FNFLOAT &fl #else NULL #endif /* FNFLOAT */ ); #ifdef FNFLOAT cmfdbi(&fl, /* A floating-point number */ _CMFLD, /* fcode */ "", /* hlpmsg */ "", /* default */ "", /* addtl string data */ 0, /* addtl numeric data 1 */ 0, /* addtl numeric data 2 */ xxstring, NULL, NULL ); #endif /* FNFLOAT */ x = cmfdb(&kw); /* Parse a keyword or a number */ debug(F111,"boolval cmfdb","",x); if (x < 0) { if (x == -3) x = -2; return(x); } debug(F111,"boolval switch","",cmresult.fcode); switch (cmresult.fcode) { /* What did we get? */ case _CMFLD: { /* A "field" */ int i; char * s; s = cmresult.sresult; /* C-Kermit 9.0: This allows a macro name to serve as an IF condition without having to enclose it in \m(...). */ if ( #ifdef FNFLOAT !isfloat(cmresult.sresult,0) /* Not a number */ #else !chknum(cmresult.sresult) /* Not a number */ #endif /* FNFLOAT */ ) { i = mlook(mactab,cmresult.sresult,nmac); /* Look it up */ if (i > -1) /* in the macro table */ s = mactab[x].mval; /* and get its value */ else /* Otherwise if no such macro */ s = "0"; /* evaluate as FALSE. */ } #ifdef FNFLOAT if (isfloat(s,0)) { /* A floating-point number? */ f1 = floatval; /* Yes, get its value */ f1flag = 1; /* remember we did this */ ifc = 9999; /* Set special "if-code" */ } #else if (chknum(s)) { cmresult.nresult = atoi(s); ifc = 9999; break; } #endif /* FNFLOAT */ else return(-2); } case _CMNUM: /* A number... */ ifc = 9999; /* Set special "if-code" */ break; case _CMKEY: /* A keyword */ ifc = cmresult.nresult; /* Get if-code */ break; default: return(-2); } switch (ifc) { /* set z = 1 for true, 0 for false */ case 9999: /* Number */ #ifdef FNFLOAT if (f1flag) { z = (f1 == 0.0) ? 0 : 1; } else #endif /* FNFLOAT */ z = (cmresult.nresult == 0) ? 0 : 1; break; case XXIFLP: /* Left paren */ if (pcount == 0 && ifargs > 0) return(-2); parens = 1; pcount++; ifargs++; *bx++ = '('; goto ifagain; case XXIFRP: /* Right paren */ if (!parens) return(-2); if (--pcount < 0) return(-2); ifargs++; *bx++ = ')'; *bx = NUL; if (pcount == 0) goto ifend; goto ifagain; case XXIFAN: /* AND (&&) */ ifargs++; if (!ecount) return(-2); *bx++ = '&'; goto ifagain; case XXIFOR: /* OR (||) */ ifargs++; if (!ecount) return(-2); *bx++ = '|'; goto ifagain; case XXIFNO: /* IF NOT [ NOT [ NOT ... ] ] */ if (bx > boolval) { /* evala() doesn't like cascaded */ if (*(bx-1) == '!') { /* unary operators... */ *(bx-1) = NUL; /* So here, two wrongs make a right. */ bx--; } else { *bx++ = '!'; } } else { *bx++ = '!'; } ifargs++; goto ifagain; case XXIFTR: /* IF TRUE */ z = 1; debug(F101,"if true","",z); ifargs += 1; break; case XXIFNT: /* IF FALSE */ z = 0; debug(F101,"if true","",z); ifargs += 1; break; case XXIFSU: /* IF SUCCESS */ z = ( success != 0 ) ? 1 : 0; debug(F101,"if success","",z); ifargs += 1; break; case XXIFFA: /* IF FAILURE */ z = ( success == 0 ) ? 1 : 0; debug(F101,"if failure","",z); ifargs += 1; break; case XXIFDE: /* IF DEFINED */ if ((x = cmfld("Macro or variable name","",&s,NULL)) < 0) return((x == -3) ? -2 : x); if (*s == CMDQ) { char * lp; char line[256]; int t, x; if (*(s+1) == 'f' || *(s+1) == 'F') { /* Built-in function */ extern struct keytab fnctab[]; extern int nfuncs; ckstrncpy(line,s+2,256); if (line[0]) { lp = ckstrchr(line,'('); if (lp) *lp = NUL; x = lookup(fnctab,line,nfuncs,&t); z = x > -1; } debug(F111,"if defined function",line,z); } else if (*(s+1) == 'v' || *(s+1) == 'V') { /* 8.0.200 */ extern struct keytab vartab[]; extern int nvars; z = 0; if (*(s+2) == '(') { ckstrncpy(line,s+3,256); if (line[0]) { lp = ckstrchr(line,')'); if (lp) *lp = NUL; x = lookup(vartab,line,nvars,&t); z = x > -1; if (z) { /* 8.0.203 */ int t; /* It must have a value to succeed */ t = 255; /* as in C-Kermit 6.0 and 7.0 */ lp = line; /* (this was broken in 8.0.200-201) */ zzstring(s,&lp,&t); t = strlen(line); z = line[0] ? 1 : 0; } } } debug(F111,"if defined variable",line,z); } else { z = chkvar(s); /* Starts with backslash */ if (z > 0) { /* Yes... */ t = 255; /* than buffer so if zzstring fails */ lp = line; /* check for that -- overflow means */ line[0] = NUL; /* the quantity is defined. */ x = zzstring(s,&lp,&t); if ((x < 0 && t != 255) || !line[0]) z = 0; debug(F111,"if defined zzstring",line,z); debug(F101,"if defined zzstring t","",t); } } } else { z = (mxlook(mactab,s,nmac) > -1); /* Look for exact match */ } debug(F111,"if defined final",s,z); ifargs += 2; break; case XXIFDC: { /* IF DECLARED */ char * lp; char line[32]; int j, k, t, x; if ((x = cmfld("Array name","",&s,NULL)) < 0) return((x == -3) ? -2 : x); if (*s == CMDQ) { if (*(s+1) != '&') { t = 31; lp = line; line[0] = NUL; x = zzstring(s,&lp,&t); s = line; } } z = 0; if ((x = arraybounds(s,&j,&k)) > -1) { if (a_ptr[x]) { if (j < 1) z = 1; else if (j <= a_dim[x]) z = 1; if (z == 1 && k > a_dim[x]) z = 0; } } break; } case XXIFBG: /* IF BACKGROUND */ case XXIFFG: /* IF FOREGROUND */ bgchk(); /* Check background status */ if (ifc == XXIFFG) /* Foreground */ z = pflag ? 1 : 0; else z = pflag ? 0 : 1; /* Background */ ifargs += 1; break; case XXIFCO: /* IF COUNT */ z = ( --count[cmdlvl] > 0 ); if (cx == XXWHI) count[cmdlvl] += 2; /* Don't ask... */ debug(F101,"if count","",z); ifargs += 1; break; case XXIFEX: /* IF EXIST */ #ifdef CK_TMPDIR case XXIFDI: /* IF DIRECTORY */ #endif /* CK_TMPDIR */ case XXIFAB: /* IF ABSOLUTE */ case XXIFLN: /* IF LINK */ if ((x = cmfld( ((ifc == XXIFDI) ? "Directory name" : "File"), "",&s, #ifdef OS2 NULL /* This allows \'s in filenames */ #else xxstring #endif /* OS2 */ )) < 0) { if (x == -3) { extern int cmflgs; if (cmflgs == 1) { printf("?File or directory name required\n"); return(-9); } } else return(x); } s = brstrip(s); #ifdef UNIX if (ifc == XXIFLN) { z = isalink(s); } else #endif /* UNIX */ if (ifc == XXIFAB) { z = isabsolute(s); } else if (ifc == XXIFEX) { z = (zgetfs(s) > -1L); debug(F101,"if exist 1","",z); #ifdef OS2 if (!z) { /* File not found. */ int t; /* Try expanding variables */ t = LINBUFSIZ-1; /* and looking again. */ lp = line; zzstring(s,&lp,&t); s = line; z = ( zchki(s) > -1L ); debug(F101,"if exist 2","",z); } #endif /* OS2 */ #ifdef CK_TMPDIR } else { #ifdef VMS z = (zchki(s) == -2) #else /* Because this doesn't catch $DISK1:[FOO]BLAH.DIR;1 */ z = isdir(s) #ifdef OS2 || (isalpha(s[0]) && s[1] == ':' && s[2] == NUL) #endif /* OS2 */ #endif /* VMS */ ; debug(F101,"if directory 1","",z); if (!z) { /* File not found. */ int t; /* Try expanding variables */ t = LINBUFSIZ-1; /* and looking again. */ lp = line; zzstring(s,&lp,&t); s = line; z = isdir(s) #ifdef OS2 || (isalpha(s[0]) && s[1] == ':' && s[2] == NUL) #endif /* OS2 */ ; debug(F101,"if directory 2","",z); } #endif /* CK_TMPDIR */ } ifargs += 2; break; case XXIFEQ: /* IF EQUAL (string comparison) */ case XXIFLL: /* IF Lexically Less Than */ case XXIFLG: /* If Lexically Greater Than */ if ((x = cmfld("first word or variable name","",&s,xxstring)) < 0) { if (x == -3) { printf("?Text required\n"); return(-9); } else return(x); } s = brstrip(s); /* Strip braces */ x = (int)strlen(s); if (x > LINBUFSIZ-1) { printf("?IF: strings too long\n"); return(-2); } lp = line; /* lp points to first string */ ckstrncpy(line,s,LINBUFSIZ); if ((y = cmfld("second word or variable name","",&s,xxstring)) < 0) { if (y == -3) { printf("?Text required\n"); return(-9); } else return(y); } s = brstrip(s); y = (int)strlen(s); if (x + y + 2 > LINBUFSIZ) { printf("?IF: strings too long\n"); return(-2); } tp = lp + x + 2; /* tp points to second string */ strcpy(tp,s); /* safe (checked) */ x = ckstrcmp(lp,tp,-1,inpcas[cmdlvl]); /* Use longest length */ switch (ifc) { case XXIFEQ: /* IF EQUAL (string comparison) */ z = (x == 0); break; case XXIFLL: /* IF Lexically Less Than */ z = (x < 0); break; case XXIFLG: /* If Lexically Greater Than */ z = (x > 0); break; } debug(F101,"IF EQ result","",z); ifargs += 3; break; case XXIFVE: /* IF VERSION */ case XXIFAE: /* IF (arithmetically) = */ case XXIFNQ: /* IF != (not arithmetically equal) */ case XXIFLT: /* IF < */ case XXIFLE: /* IF <= */ case XXIFGE: /* IF >= */ case XXIFGT: { /* IF > */ /* July 2006 - converted to use CK_OFF_T rather than int to */ /* allow long integers on platforms that have ck_off_t > 32 bits */ int xx; CK_OFF_T n1 = (CK_OFF_T)0, n2 = (CK_OFF_T)0; if (ifc == XXIFVE) { n1 = (CK_OFF_T) vernum; } else { x = cmfld("first number or variable name","",&s,xxstring); if (x == -3) { printf("?Quantity required\n"); return(-9); } if (x < 0) return(x); debug(F101,"xxifgt cmfld","",x); ckstrncpy(line,s,LINBUFSIZ); lp = brstrip(line); debug(F110,"xxifgt exp1",lp,0); /* The following bit is for compatibility with old versions of MS-DOS Kermit */ if (!ckstrcmp(lp,"count",5,0)) { n1 = (CK_OFF_T)count[cmdlvl]; } else if (!ckstrcmp(lp,"version",7,0)) { n1 = (CK_OFF_T) vernum; } else if (!ckstrcmp(lp,"argc",4,0)) { n1 = (CK_OFF_T) macargc[maclvl]; } else { /* End of compatibility bit */ #ifdef FNFLOAT if (isfloat(lp,0) > 1) { /* Allow floating-point comparisons */ f1 = floatval; f1flag = 1; } else #endif /* FNFLOAT */ if (chknum(lp)) { n1 = ckatofs(lp); } else { /* Check for arithmetic expression */ q = evala(lp); /* cmnum() does this but ... */ if (chknum(q)) { /* we're not using cmnum(). */ n1 = ckatofs(q); } else { printf("?Value not numeric - %s", lp); return(-9); } } } } y = cmfld("number or variable name","",&s,xxstring); if (y == -3) { printf("?Quantity required\n"); return(-9); } if (y < 0) return(y); s = brstrip(s); if (!*s) return(-2); if (ifc == XXIFVE) { tp = line; } else { x = (int)strlen(lp); tp = line + x + 2; } ckstrncpy(tp,s,LINBUFSIZ-x-2); debug(F110,"xxifgt exp2",tp,0); if (!ckstrcmp(tp,"count",5,0)) { n2 = (CK_OFF_T) count[cmdlvl]; } else if (!ckstrcmp(tp,"version",7,0)) { n2 = (CK_OFF_T) vernum; } else if (!ckstrcmp(tp,"argc",4,0)) { n2 = (CK_OFF_T) macargc[maclvl]; } else { #ifdef FNFLOAT if (isfloat(tp,0) > 1) { f2 = floatval; f2flag = 1; } else #endif /* FNFLOAT */ if (chknum(tp)) { n2 = ckatofs(tp); } else { q = evala(tp); if (chknum(q)) { /* we're not using cmnum(). */ n2 = ckatofs(q); } else { printf("?Value not numeric - %s", tp); return(-9); } } } xx = (ifc == XXIFVE) ? XXIFGE : ifc; #ifdef FNFLOAT if (f1flag && !f2flag) { f2 = (CKFLOAT)n2; f2flag = 1; } if (f2flag && !f1flag) f1 = (CKFLOAT)n1; if (f1flag) z = ((f1 < f2 && xx == XXIFLT) || (f1 != f2 && xx == XXIFNQ) || (f1 <= f2 && xx == XXIFLE) || (f1 == f2 && xx == XXIFAE) || (f1 >= f2 && xx == XXIFGE) || (f1 > f2 && xx == XXIFGT)); else #endif /* FNFLOAT */ z = ((n1 < n2 && xx == XXIFLT) || (n1 != n2 && xx == XXIFNQ) || (n1 <= n2 && xx == XXIFLE) || (n1 == n2 && xx == XXIFAE) || (n1 >= n2 && xx == XXIFGE) || (n1 > n2 && xx == XXIFGT)); debug(F101,"xxifge z","",z); if (ifc == XXIFVE) ifargs += 2; else ifargs += 3; break; } case XXIFNU: /* IF NUMERIC */ x = cmfld("variable name or constant","",&s,NULL); if (x == -3) { extern int cmflgs; if (cmflgs == 1) { printf("?Quantity required\n"); return(-9); } } else if (x < 0) return(x); x = LINBUFSIZ-1; lp = line; zzstring(s,&lp,&x); lp = line; debug(F110,"xxifnu quantity",lp,0); z = chknum(lp); #ifdef COMMENT /* This works, but it's not wise -- IF NUMERIC is mostly used to see if a string really does contain only numeric characters. If they want to force evaluation, they can use \feval() on the argument string. */ if (!z) { /* Not a number */ x_ifnum = 1; /* Avoid "eval" error messages */ q = evala(lp); /* Maybe it's an expression */ z = chknum(q); /* that evaluates to a number */ x_ifnum = 0; /* Put eval messages back to normal */ if (z) debug(F110,"xxifnu exp",lp,0); } #endif /* COMMENT */ debug(F101,"xxifnu chknum","",z); ifargs += 2; break; #ifdef ZFCDAT case XXIFNE: { /* IF NEWER */ char d1[20], * d2; /* Buffers for 2 dates */ if ((z = cmifi("First file","",&s,&y,xxstring)) < 0) return(z); ckstrncpy(d1,zfcdat(s),20); if ((z = cmifi("Second file","",&s,&y,xxstring)) < 0) return(z); d2 = zfcdat(s); if ((int)strlen(d1) != 17 || (int)strlen(d2) != 17) { printf("?Failure to get file date\n"); return(-9); } debug(F110,"xxifnewer d1",d1,0); debug(F110,"xxifnewer d2",d2,0); z = (strcmp(d1,d2) > 0) ? 1 : 0; debug(F101,"xxifnewer","",z); ifargs += 2; break; } #endif /* ZFCDAT */ #ifdef CK_IFRO case XXIFRO: /* REMOTE-ONLY advisory */ ifargs++; #ifdef NOLOCAL z = 1; #else z = remonly; #endif /* NOLOCAL */ break; #endif /* CK_IFRO */ case XXIFAL: /* ALARM */ ifargs++; debug(F101,"IF ALARM ck_alarm","",ck_alarm); debug(F110,"IF ALARM alrm_date",alrm_date,0); debug(F110,"IF ALARM alrm_time",alrm_time,0); if (ck_alarm < 1L || alrm_date[0] < '0' || alrm_time[0] < '0') { z = 0; /* ALARM not SET */ break; /* so IF ALARM fails */ } /* Get current date and time */ ckstrncpy(tmpbuf,ckcvtdate("",1),TMPBUFSIZ); s = tmpbuf; s[8] = NUL; z = (int) strncmp(tmpbuf,alrm_date,8); /* Compare dates */ debug(F101,"IF ALARM date z","",z); if (z == 0) { /* Dates are the same */ /* Compare times */ z = (tod2sec(tmpbuf+9) >= atol(alrm_time)) ? 1 : -1; debug(F101,"IF ALARM time z","",z); } tmpbuf[0] = NUL; /* z >= 0 if alarm is passed */ z = ((z >= 0) ? 1 : 0); /* z < 0 otherwise */ debug(F101,"IF ALARM final z","",z); break; case XXIFOP: /* IF OPEN */ if ((x = cmkey(iotab,niot,"file or log","",xxstring)) < 0) return(x); if (x == 9999 || x == ZSTDIO) { bgchk(); /* Check background status */ z = pflag ? 1 : 0; } else if (x == 8888) { z = local ? ttchk() > -1 : 0; #ifdef CKLOGDIAL } else if (x == 7777) { extern int dialog; z = dialog ? 1 : 0; #endif /* CKLOGDIAL */ } else z = (chkfn(x) > 0) ? 1 : 0; ifargs += 1; break; case XXIFSD: /* Started-From-Dialer */ #ifdef OS2 z = StartedFromDialer; #else z = 0; #endif /* OS2 */ break; case XXIFTM: /* Terminal-Macro */ #ifdef OS2 z = cmdstk[cmdlvl].ccflgs & CF_KMAC; #else z = 0; #endif /* OS2 */ break; case XXIFEM: /* Emulation is active */ #ifdef OS2 z = 1; #else z = 0; #endif /* OS2 */ break; case XXIFIK: /* Running as IKSD? */ z = inserver; break; case XXIFTA: /* Connection is TAPI */ z = 0; #ifndef NODIAL #ifdef CK_TAPI if (local && !network && tttapi) z = 1; #endif /* CK_TAPI */ #endif /* NODIAL */ break; case XXIFMA: /* IF MATCH */ x = cmfld("String or variable","",&s,xxstring); if (x == -3) { extern int cmflgs; if (cmflgs == 1) { printf("?String required\n"); return(-9); } } else if (x < 0) return(x); ckstrncpy(line,s,LINBUFSIZ); s = brstrip(line); debug(F110,"xxifma string",line,0); x = cmfld("Pattern","",&p,xxstring); if (x == -3) { extern int cmflgs; if (cmflgs == 1) { printf("?Pattern required\n"); return(-9); } } else if (x < 0) return(x); ckstrncpy(tmpbuf,p,TMPBUFSIZ); p = brstrip(tmpbuf); debug(F110,"xxifma pattern",tmpbuf,0); z = ckmatch(p,s,inpcas[cmdlvl],1); break; case XXIFFL: { /* IF FLAG */ extern int ooflag; z = ooflag; break; } case XXIFAV: { /* IF AVAILABLE */ if ((x = cmkey(availtab,availtabn,"","",xxstring)) < 0) return(x); switch (x) { case AV_KRB4: z = ck_krb4_is_installed(); break; case AV_KRB5: z = ck_krb5_is_installed(); break; case AV_SRP: z = ck_srp_is_installed(); break; case AV_SSL: z = ck_ssleay_is_installed(); break; case AV_NTLM: z = ck_ntlm_is_installed(); break; case AV_CRYPTO: z = ck_crypt_is_installed(); break; case AV_SSH: z = ck_ssh_is_installed(); break; default: z = 0; } break; } case XXIFAT: /* IF ASKTIMEOUT */ z = asktimedout; break; case XXIFRD: /* IF READABLE */ case XXIFWR: /* IF WRITEABLE */ if ((x = cmfld("File or directory name", "", &s, #ifdef OS2 NULL /* This allows \'s in filenames */ #else xxstring #endif /* OS2 */ )) < 0) { if (x == -3) { extern int cmflgs; if (cmflgs == 1) { printf("?File or directory name required\n"); return(-9); } } else return(x); } s = brstrip(s); /* zchk[io]() do not do what we want here for directories, so we set a global flag telling it to behave specially in this case. Othewise we'd have to change the API and change all ck?fio.c modules accordingly. */ y = 0; /* Try-again control */ #ifdef OS2 ifrdagain: #endif /* OS2 */ if (ifc == XXIFRD) { /* IF READABLE */ zchkid = 1; z = zchki(s) > -1; zchkid = 0; } else if (ifc == XXIFWR) { /* IF WRITEABLE */ zchkod = 1; z = zchko(s) > -1; zchkod = 0; } #ifdef OS2 if (!z && !y) { /* File not found. */ int t; /* Try expanding variables */ t = LINBUFSIZ-1; /* and looking again. */ lp = line; zzstring(s,&lp,&t); s = line; z = zchko(s) > -1; y++; goto ifrdagain; } #endif /* OS2 */ ifargs += 2; break; case XXIFQU: /* IF QUIET */ z = quiet ? 1 : 0; debug(F101,"if quiet","",z); ifargs += 1; break; case XXIFWI: /* WILD */ if ((x = cmfld("File specification","",&s,xxstring)) < 0) return(x); z = iswild(s); break; case XXIFCK: /* C-KERMIT */ #ifdef OS2 z = 0; #else z = 1; #endif /* OS2 */ break; case XXIFK9: /* K-95 */ #ifdef OS2 z = 1; #else z = 0; #endif /* OS2 */ break; case XXIFGU: /* GUI */ #ifdef KUI z = 1; #else z = 0; #endif /* KUI */ break; case XXIFMS: /* MS-KERMIT */ z = 0; break; case XXIFLO: /* IF LOCAL */ z = local ? 1 : 0; break; case XXIFCM: { /* IF COMMAND */ extern struct keytab cmdtab[]; extern int ncmd; if ((x = cmfld("Word","",&s,xxstring)) < 0) return(x); z = lookup(cmdtab,s,ncmd,&y); z = (z == -2 || z > -1) ? 1 : 0; break; } #ifdef CKFLOAT case XXIFFP: /* IF FLOAT */ if ((x = cmfld("Number","",&s,xxstring)) < 0) if (x != -3) /* e.g. empty variable */ return(x); z = isfloat(s,0); break; #endif /* CKFLOAT */ case XXIFKB: /* KBHIT */ z = conchk(); if (z < 0) z = 0; if (z > 1) z = 1; break; case XXIFKG: { /* KERBANG */ extern int cfilef; #ifdef COMMENT z = (xcmdsrc == 0) ? 0 : (cfilef && cmdlvl == 1); #else z = (xcmdsrc == 0) ? 0 : (cfilef && tlevel == 0); #endif /* COMMENT */ break; } case XXIFDB: { /* IF DEBUG - 2010/03/16 */ extern int debmsg; z = debmsg; break; } default: /* Shouldn't happen */ return(-2); } /* end of switch */ if (z) *bx++ = '1'; else *bx++ = '0'; *bx = NUL; if (bx > boolval + BOOLLEN - 2) { printf("?Boolean expression too long"); return(-9); } ecount++; /* Expression counter */ debug(F101,"boolexp parens","",parens); debug(F101,"boolexp pcount","",pcount); if (parens && pcount > 0) goto ifagain; ifend: /* No more - done */ *bx = NUL; z = atoi(evalx(boolval)); debug(F111,"boolexp boolval",boolval,z); return(z); } /* D O I F -- Do the IF command */ int doif(cx) int cx; { int x, y, z; char *s, *p; char *q; #ifdef OS2 extern int keymac; #endif /* OS2 */ debug(F101,"doif cx","",cx); z = boolexp(cx); /* Evaluate the condition(s) */ debug(F010,"doif cmdbuf",cmdbuf,0); debug(F101,"doif boolexp","",z); if (z < 0) return(z); if (cx == XXIF) { /* Allow IF to have XIF semantics. */ char * p; p = cmpeek(); if (!p) p = ""; while (*p) { if (*p == SP || *p == HT) p++; else break; } if (*p == '{') cx = XXIFX; } switch (cx) { /* Separate handling for IF and XIF */ case XXASSER: /* And ASSERT */ if ((x = cmcfm()) < 0) return(x); return(success = z); case XXIF: /* This is IF... */ ifcmd[cmdlvl] = 1; /* We just completed an IF command */ debug(F101,"doif condition","",z); if (z) { /* Condition is true */ iftest[cmdlvl] = 1; /* Remember that IF succeeded */ if (maclvl > -1) { /* In macro, */ pushcmd(NULL); /* save rest of command. */ } else if (tlevel > -1) { /* In take file, */ debug(F100, "doif: pushing command", "", 0); pushcmd(NULL); /* save rest of command. */ } else { /* If interactive, */ cmini(ckxech); /* just start a new command */ printf("\n"); /* (like in MS-DOS Kermit) */ if (pflag) prompt(xxstring); } } else { /* Condition is false */ iftest[cmdlvl] = 0; /* Remember command failed. */ if ((y = cmtxt("command to be ignored","",&s,NULL)) < 0) return(y); /* Gobble up rest of line */ } return(0); case XXIFX: { /* This is XIF (Extended IF) */ char *p; char e[5]; int i; if ((y = cmtxt("Object command","",&s,NULL)) < 0) return(y); /* Get object command. */ p = s; lp = line; debug(F110,"doif THEN part",s,-54); if (litcmd(&p,&lp,LINBUFSIZ - 1) < 0) { /* Quote THEN-part */ return(-2); } debug(F111,"doif THEN part 2",line,z); while (*p == SP) p++; /* Strip trailing spaces */ ifcmd[cmdlvl] = 0; /* Assume ELSE part in same line */ iftest[cmdlvl] = z ? 1 : 0; if (*p) { /* At end? */ if (!z) { /* No, use ELSE-part, if any */ for (i = 0; i < 4; i++) e[i] = *p++; if (ckstrcmp(e,"else",4,0)) /* See if we have an ELSE */ return(-2); /* Something else - error. */ debug(F010,"doif ELSE line 1",p,0); while (*p == SP) p++; /* Skip spaces */ if (*p != '{') { /* Brace ELSE part if necessary */ ckmakmsg(tmpbuf,TMPBUFSIZ,"{",p," }",NULL); p = tmpbuf; debug(F010,"doif ELSE line 2",p,0); } lp = line; /* Write over THEN part... */ *lp = NUL; /* with ELSE part. */ if (litcmd(&p,&lp,LINBUFSIZ - 2) < 0) { return(-2); } while (*p == SP) p++; /* Strip trailing spaces */ if (*p) return(-2); /* Should be nothing here. */ debug(F010,"doif ELSE line 3",line,0); } } else { /* At end, treat like an IF command */ if (!z) line[0] = NUL; /* Condition not true and no ELSE */ ifcmd[cmdlvl] = 1; /* Allow ELSE on next line */ debug(F101,"IF condition","",z); } if (line[0]) { x = mlook(mactab,"_xif",nmac); /* Get index of "_xif" macro. */ if (x < 0) { /* Not there? */ addmmac("_xif",xif_def); /* Put it back. */ if (mlook(mactab,"_xif",nmac) < 0) { /* Look it up again. */ printf("?XIF macro gone!\n"); return(success = 0); } } dodo(x,line,cmdstk[cmdlvl].ccflgs | CF_IMAC); } return(0); } case XXWHI: { /* WHILE Command */ p = cmdbuf; /* Capture IF condition */ ifcond[0] = NUL; /* from command buffer */ while (*p == SP) p++; while (*p != SP) p++; ifcp = ifcond; ifcp += ckstrncpy(ifcp,"{ \\flit(if ( not ",IFCONDLEN); #ifdef COMMENT /* This doesn't work because it breaks on the first left brace, which does not necessarily start the command list, e.g. "while equal \%a {\35}". */ while (*p != '{' && *p != NUL) *ifcp++ = *p++; p = " ) goto _..bot) } "; while (*ifcp++ = *p++) ; #else /* The command parser sets cmbptr to the spot where it left off parsing in the command buffer. */ { extern char * cmbptr; if (cmbptr) { while (p < cmbptr && *p != NUL) *ifcp++ = *p++; p = " ) goto _..bot) } "; while ((*ifcp++ = *p++)) ; } else { printf("?Internal error parsing WHILE condition\n"); return(-9); } } #endif /* COMMENT */ debug(F110,"WHILE cmd",ifcond,0); if ((y = cmtxt("Object command","",&s,NULL)) < 0) return(y); /* Get object command. */ p = s; lp = line; if (litcmd(&p,&lp,LINBUFSIZ - 2) < 0) { /* Quote object command */ return(-2); } debug(F101,"WHILE body",line,-54); if (line[0]) { char *p; x = mlook(mactab,"_while",nmac); /* index of "_while" macro. */ if (x < 0) { /* Not there? */ addmmac("_while",whil_def); /* Put it back. */ if (mlook(mactab,"_while",nmac) < 0) { /* Look it up again */ printf("?WHILE macro definition gone!\n"); return(success = 0); } } p = malloc((int)strlen(ifcond) + (int)strlen(line) + 2); if (p) { strcpy(p,ifcond); /* safe (prechecked) */ strcat(p,line); /* safe (prechecked) */ debug(F010,"WHILE dodo",p,0); dodo(x,p,cmdstk[cmdlvl].ccflgs | CF_IMAC); free(p); p = NULL; } else { printf("?Can't allocate storage for WHILE command"); return(success = 0); } } return(0); } default: return(-2); } } #endif /* NOSPL */ /* Set up a TAKE command file */ int dotake(s) char *s; { #ifndef NOSPL extern int tra_cmd; #endif /* NOSPL */ #ifndef NOLOCAL #ifdef OS2 extern int term_io; int term_io_sav = term_io; #endif /* OS2 */ #endif /* NOLOCAL */ int slen; debug(F110,"dotake",s,0); if (!s) s = ""; if (!*s) return(success = 0); slen = strlen(s); debug(F101,"dotake len","",slen); if ((tfile[++tlevel] = fopen(s,"r")) == NULL) { perror(s); debug(F110,"dotake fail",s,0); tlevel--; return(success = 0); } else { tfline[tlevel] = 0; /* Line counter */ #ifdef VMS conres(); /* So Ctrl-C will work */ #endif /* VMS */ #ifndef NOLOCAL #ifdef OS2 term_io = 0; /* Disable Terminal Emulator I/O */ #endif /* OS2 */ #endif /* NOLOCAL */ #ifndef NOSPL cmdlvl++; /* Entering a new command level */ debug(F111,"CMD +F",s,cmdlvl); debug(F101,"dotake cmdlvl","",cmdlvl); debug(F101,"dotake tlevel","",tlevel); if (cmdlvl > CMDSTKL) { debug(F100,"dotake stack overflow","",0); cmdlvl--; debug(F111,"CMD*-F",s,cmdlvl); fclose(tfile[tlevel--]); printf("?TAKE files and/or DO commands nested too deeply\n"); return(success = 0); } if (tfnam[tlevel]) { /* Copy the filename */ free(tfnam[tlevel]); tfnam[tlevel] = NULL; } if ((tfnam[tlevel] = malloc(strlen(s) + 1))) { strcpy(tfnam[tlevel],s); /* safe */ } else { printf("?Memory allocation failure\n"); return(success = 0); } ifcmd[cmdlvl] = 0; /* Set variables for this cmd file */ iftest[cmdlvl] = 0; count[cmdlvl] = count[cmdlvl-1]; /* Inherit this */ intime[cmdlvl] = intime[cmdlvl-1]; /* Inherit this */ inpcas[cmdlvl] = inpcas[cmdlvl-1]; /* Inherit this */ takerr[cmdlvl] = takerr[cmdlvl-1]; /* Inherit this */ merror[cmdlvl] = merror[cmdlvl-1]; /* Inherit this */ xquiet[cmdlvl] = quiet; xvarev[cmdlvl] = vareval; xcmdsrc = CMD_TF; cmdstk[cmdlvl].src = CMD_TF; /* Say we're in a TAKE file */ cmdstk[cmdlvl].lvl = tlevel; /* nested at this level */ cmdstk[cmdlvl].ccflgs = cmdstk[cmdlvl-1].ccflgs; #else takerr[tlevel] = takerr[tlevel-1]; /* Inherit this */ #endif /* NOSPL */ } #ifndef NOSPL if (tra_cmd) printf("[%d] +F: \"%s\"\n",cmdlvl,s); #endif /* NOSPL */ #ifndef NOLOCAL #ifdef OS2 term_io = term_io_sav; #endif /* OS2 */ #endif /* NOLOCAL */ return(1); } #endif /* NOICP */ ckuus7.c0000644000015300001460000176533011600663027011266 0ustar fdckermit#include "ckcsym.h" /* C K U U S 7 -- "User Interface" for C-Kermit, part 7 */ /* Authors: Frank da Cruz , The Kermit Project, Columbia University, New York City Jeffrey E Altman Secure Endpoints Inc., New York City Copyright (C) 1985, 2011, Trustees of Columbia University in the City of New York. All rights reserved. See the C-Kermit COPYING.TXT file or the copyright text in the ckcmai.c module for disclaimer and permissions. */ /* This file created from parts of ckuus3.c, which became too big for Mark Williams Coherent compiler to handle. */ /* Definitions here supersede those from system include files. */ #include "ckcdeb.h" /* Debugging & compiler things */ #include "ckcasc.h" /* ASCII character symbols */ #include "ckcker.h" /* Kermit application definitions */ #include "ckcxla.h" /* Character set translation */ #include "ckcnet.h" /* Network symbols */ #include "ckuusr.h" /* User interface symbols */ #include "ckucmd.h" #include "ckclib.h" #ifdef VMS #ifndef TCPSOCKET #include #endif /* TCPSOCKET */ #endif /* VMS */ #ifdef OS2 #ifndef NT #define INCL_NOPM #define INCL_VIO /* Needed for ckocon.h */ #define INCL_DOSMODULEMGR #include #undef COMMENT #else /* NT */ #define APIRET ULONG #include #include #include "cknwin.h" #include "ckntap.h" #endif /* NT */ #include "ckowin.h" #include "ckocon.h" #include "ckodir.h" #ifdef OS2MOUSE #include "ckokey.h" #endif /* OS2MOUSE */ #ifdef KUI #include "ikui.h" #endif /* KUI */ #ifdef putchar #undef putchar #endif /* putchar */ #define putchar(x) conoc(x) extern int mskkeys; extern int mskrename; #endif /* OS2 */ #ifdef CK_AUTHENTICATION #include "ckuath.h" #endif /* CK_AUTHENTICATION */ #ifdef CK_SSL #include "ck_ssl.h" #endif /* CK_SSL */ #ifdef SSHBUILTIN #include "ckossh.h" #endif /* SSHBUILTIN */ #ifdef STRATUS /* Stratus Computer, Inc. VOS */ #ifdef putchar #undef putchar #endif /* putchar */ #define putchar(x) conoc(x) #ifdef getchar #undef getchar #endif /* getchar */ #define getchar(x) coninc(0) #endif /* STRATUS */ char * slmsg = NULL; static int x, y = 0, z; static char *s; extern CHAR feol; extern int g_matchdot, hints, xcmdsrc, rcdactive; extern char * k_info_dir; #ifdef CK_LOGIN #ifdef CK_PAM int gotemptypasswd = 0; /* distinguish empty passwd from none given */ #endif /* CK_PAM */ #endif /* CK_LOGIN */ #ifndef NOSPL extern int nmac; extern struct mtab *mactab; #endif /* NOSPL */ #ifndef NOXFER #ifdef CK_SPEED extern short ctlp[]; /* Control-char prefixing table */ #endif /* CK_SPEED */ #ifdef PIPESEND extern char * sndfilter, * g_sfilter; extern char * rcvfilter, * g_rfilter; #endif /* PIPESEND */ extern char * snd_move; extern char * snd_rename; extern char * g_snd_move; extern char * g_snd_rename; extern char * rcv_move; extern char * rcv_rename; extern char * g_rcv_move; extern char * g_rcv_rename; #ifdef PATTERNS extern char *binpatterns[], *txtpatterns[]; extern int patterns; #endif /* PATTERNS */ extern char * remdest; #ifdef CK_TMPDIR char * dldir = NULL; #endif /* CK_TMPDIR */ extern struct ck_p ptab[]; extern int protocol, remfile, rempipe, remappd, reliable, xreliable, fmask, fncnv, frecl, maxrps, wslotr, bigsbsiz, bigrbsiz, urpsiz, rpsiz, spsiz, bctr, npad, timef, timint, spsizr, spsizf, maxsps, spmax, nfils, displa, atcapr, pkttim, rtimo, fncact, mypadn, fdispla, f_save, pktpaus, setreliable, fnrpath, fnspath, atenci, atenco, atdati, atdato, atleni, atleno, atblki, atblko, attypi, attypo, atsidi, atsido, atsysi, atsyso, atdisi, atdiso; extern int stathack; extern int atfrmi, atfrmo; #ifdef STRATUS extern int atcrei, atcreo, atacti, atacto; #endif /* STRATUS */ #ifdef CK_PERMS extern int atlpri, atlpro, atgpri, atgpro; #endif /* CK_PERMS */ extern CHAR sstate, eol, seol, stchr, mystch, mypadc, padch, ctlq, myctlq; #ifdef IKSD extern int inserver; #ifdef IKSDCONF extern int iksdcf; #endif /* IKSDCONF */ #endif /* IKSD */ extern char *cmarg, *cmarg2; #ifndef NOFRILLS extern char optbuf[]; /* Buffer for MAIL or PRINT options */ extern int rprintf; /* REMOTE PRINT flag */ #endif /* NOFRILLS */ #endif /* NOXFER */ #ifdef CK_TRIGGER extern char * tt_trigger[]; #endif /* CK_TRIGGER */ extern int tcs_transp; #ifdef PCTERM extern int tt_pcterm; #endif /* PCTERM */ #ifdef NT extern int tt_vtnt; #endif /* NT */ #ifdef SSHBUILTIN int sl_ssh_xfw = 0; int sl_ssh_xfw_saved = 0; int sl_ssh_ver = 0; int sl_ssh_ver_saved = 0; #endif /* SSHBUILTIN */ #ifdef CK_AUTHENTICATION extern int auth_type_user[]; int sl_auth_type_user[AUTHTYPLSTSZ] = {AUTHTYPE_NULL, AUTHTYPE_NULL}; int sl_auth_saved = 0; int sl_topt_a_su = 0; int sl_topt_a_s_saved = 0; int sl_topt_a_cm = 0; int sl_topt_a_c_saved = 0; #endif /* CK_AUTHENTICATION */ #ifdef CK_ENCRYPTION extern int cx_type; int sl_cx_type = 0; int sl_cx_saved = 0; int sl_topt_e_su = 0; int sl_topt_e_sm = 0; int sl_topt_e_s_saved = 0; int sl_topt_e_cu = 0; int sl_topt_e_cm = 0; int sl_topt_e_c_saved = 0; #endif /* CK_ENCRYPTION */ extern char uidbuf[]; static int uidflag = 0; char sl_uidbuf[UIDBUFLEN] = { NUL, NUL }; int sl_uid_saved = 0; #ifdef TNCODE int sl_tn_wait = 0; int sl_tn_saved = 0; #endif /* TNCODE */ #ifdef TNCODE extern int tn_wait_flg; #endif /* TNCODE */ VOID slrestor() { #ifdef CK_AUTHENTICATION int x; if (sl_auth_saved) { for (x = 0; x < AUTHTYPLSTSZ; x++) auth_type_user[x] = sl_auth_type_user[x]; sl_auth_saved = 0; } if (sl_topt_a_s_saved) { TELOPT_DEF_S_U_MODE(TELOPT_AUTHENTICATION) = sl_topt_a_su; sl_topt_a_s_saved = 0; } if (sl_topt_a_c_saved) { TELOPT_DEF_C_ME_MODE(TELOPT_AUTHENTICATION) = sl_topt_a_cm; sl_topt_a_c_saved = 0; } #endif /* CK_AUTHENTICATION */ #ifdef CK_ENCRYPTION if (sl_cx_saved) { cx_type = sl_cx_type; sl_cx_saved = 0; } if (sl_topt_e_s_saved) { TELOPT_DEF_S_U_MODE(TELOPT_ENCRYPTION) = sl_topt_e_su; TELOPT_DEF_S_ME_MODE(TELOPT_ENCRYPTION) = sl_topt_e_sm; sl_topt_e_s_saved = 0; } if (sl_topt_e_c_saved) { TELOPT_DEF_C_U_MODE(TELOPT_ENCRYPTION) = sl_topt_e_cu; TELOPT_DEF_C_ME_MODE(TELOPT_ENCRYPTION) = sl_topt_e_cm; sl_topt_e_c_saved = 0; } #endif /* CK_ENCRYPTION */ if (sl_uid_saved) { ckstrncpy(uidbuf,sl_uidbuf,UIDBUFLEN); sl_uid_saved = 0; } #ifdef TNCODE if (sl_tn_saved) { tn_wait_flg = sl_tn_wait; sl_tn_saved = 0; } #endif /* TNCODE */ #ifdef SSHBUILTIN if (sl_ssh_xfw_saved) { ssh_xfw = sl_ssh_xfw; sl_ssh_xfw_saved = 0; } if (sl_ssh_ver_saved) { ssh_ver = sl_ssh_ver; sl_ssh_ver_saved = 0; } #endif /* SSHBUILTIN */ } int oldplex = -1; /* Duplex holder around network */ #ifndef NOICP #ifdef LOCUS extern int locus, autolocus; #endif /* LOCUS */ #ifndef NODIAL extern int dialsta; #endif /* NODIAL */ /* Note: gcc -Wall wants braces around each keyword table entry. */ static struct keytab psltab[] = { /* SET LINE/PORT command options */ { "/connect", SL_CNX, 0 }, #ifdef OS2ORVMS { "/noshare", SL_NSH, 0 }, #endif /* OS2ORVMS */ { "/server", SL_SRV, 0 }, #ifdef OS2ORVMS { "/share", SL_SHR, 0 }, #endif /* OS2ORVMS */ { "", 0, 0 } }; static int npsltab = sizeof(psltab)/sizeof(struct keytab) - 1; #ifdef NETCONN static struct keytab shtab[] = { /* SET HOST command options */ #ifdef NETCMD /* (COMMAND is also a network type) */ { "/command", SL_CMD, CM_INV }, #endif /* NETCMD */ { "/connect", SL_CNX, 0 }, { "/network-type", SL_NET, CM_ARG }, { "/nowait", SL_NOWAIT, 0 }, #ifndef NOSPL #ifdef CK_AUTHENTICATION { "/password", SL_PSW, CM_ARG }, #endif /* CK_AUTHENTICATION */ #endif /* NOSPL */ #ifdef NETCMD { "/pipe", SL_CMD, 0 }, #endif /* NETCMD */ #ifdef NETPTY { "/pty", SL_PTY, 0 }, #endif /* NETPTY */ { "/server", SL_SRV, 0 }, { "/timeout", SL_TMO, CM_ARG }, { "/userid", SL_UID, CM_ARG }, { "/wait", SL_WAIT, 0 }, { "", 0, 0 } }; static int nshtab = sizeof(shtab)/sizeof(struct keytab) - 1; static struct keytab shteltab[] = { /* TELNET command options */ #ifdef CK_AUTHENTICATION { "/auth", SL_AUTH, CM_ARG }, #endif /* CK_AUTHENTICATION */ #ifdef CK_ENCRYPTION { "/encrypt", SL_ENC, CM_ARG }, #endif /* CK_ENCRYPTION */ { "/nowait", SL_NOWAIT, 0 }, #ifndef NOSPL #ifdef CK_AUTHENTICATION { "/password", SL_PSW, CM_ARG }, #endif /* CK_AUTHENTICATION */ #endif /* NOSPL */ { "/timeout", SL_TMO, CM_ARG }, { "/userid", SL_UID, CM_ARG }, { "/wait", SL_WAIT, 0 }, { "", 0 ,0 } }; static int nshteltab = sizeof(shteltab)/sizeof(struct keytab) - 1; #ifdef RLOGCODE static struct keytab shrlgtab[] = { /* SET HOST RLOGIN command options */ #ifdef CK_KERBEROS #ifdef CK_ENCRYPTION { "/encrypt", SL_ENC, 0 }, #endif /* CK_ENCRYPTION */ { "/k4", SL_KRB4, CM_INV }, { "/k5", SL_KRB5, CM_INV }, { "/kerberos4", SL_KRB4, 0 }, { "/kerberos5", SL_KRB5, 0 }, { "/kerberos_iv", SL_KRB4, CM_INV }, { "/kerberos_v", SL_KRB5, CM_INV }, { "/krb4", SL_KRB4, CM_INV }, { "/krb5", SL_KRB5, CM_INV }, #endif /* CK_KERBEROS */ { "", 0 ,0 } }; static int nshrlgtab = sizeof(shrlgtab)/sizeof(struct keytab)-1; #endif /* RLOGCODE */ extern struct keytab netcmd[]; extern int nnets; #ifndef NODIAL extern int dirline; extern int nnetdir; /* Network services directory */ extern char *netdir[]; _PROTOTYP( VOID ndreset, (void) ); char *nh_p[MAXDNUMS + 1]; /* Network directory entry pointers */ char *nh_p2[MAXDNUMS + 1]; /* Network directory entry nettype */ char *nh_px[4][MAXDNUMS + 1]; /* Network-specific stuff... */ #endif /* NODIAL */ int nhcount = 0; int ndinited = 0; char * n_name = NULL; /* Network name pointer */ #endif /* NETCONN */ _PROTOTYP(int remtxt, (char **) ); _PROTOTYP(VOID rmsg, (void) ); _PROTOTYP(static int remcfm, (void) ); extern int nopush; int mdmsav = -1; /* Save modem type around network */ extern int isguest; /* Global flag for anonymous login */ extern xx_strp xxstring; extern int success, binary, b_save, ckwarn, msgflg, quiet, cmask, pflag, local, nettype, escape, mdmtyp, duplex, dfloc, network, cdtimo, autoflow, tnlm, sosi, tlevel, lf_opts, backgrd, flow, debses, parity, ttnproto, ckxech, x_ifnum, cmflgs, haveline, cxtype, cxflow[], maclvl; #ifdef DCMDBUF extern struct cmdptr *cmdstk; /* The command stack itself */ #else extern struct cmdptr cmdstk[]; /* The command stack itself */ #endif /* DCMDBUF */ extern FILE * tfile[]; extern char * macp[]; extern char psave[]; /* For saving & restoring prompt */ extern int sprmlen, rprmlen; #ifdef OS2 static struct keytab strmkeytab[] = { { "clear", 0, 0 }, { "default", 1, 0 } }; static int nstrmkeytab = sizeof(strmkeytab)/sizeof(struct keytab); static struct keytab strmswitab[] = { { "/literal", 0, 0 } }; static int nstrmswitab = sizeof(strmswitab)/sizeof(struct keytab); static struct keytab normrev[] = { { "dark-display", 0, 0 }, { "light-display", 1, 0 }, { "normal", 0, 0 }, { "reverse", 1, 0 } }; static struct keytab prnmtab[] = { { "auto", 1, 0 }, { "copy", 2, 0 }, { "off", 0, 0 }, { "on", 1, CM_INV }, /* Compatibility with XPRINT version */ { "user", 3, 0 }, { "transparent", 3, CM_INV } /* not really transparent */ }; static int nprnmtab = sizeof(prnmtab)/sizeof(struct keytab); extern int tt_diff_upd; #ifdef NT #define stricmp _stricmp extern int tt_attr_bug; #endif /* NT */ extern int tt_rows[], tt_cols[]; extern int tt_cols_usr; extern int tt_szchng[VNUM]; int tt_modechg = TVC_ENA; extern int tt_url_hilite, tt_url_hilite_attr; extern struct _vtG G[4]; extern int priority; extern bool send_c1; int send_c1_usr = FALSE; extern int sgrcolors; extern int marginbell, marginbellcol; extern int autoscroll, wy_autopage; extern int tt_sac; extern int dec_nrc, dec_lang, dec_kbd; #else /* OS2 */ extern int tt_rows, tt_cols; #endif /* OS2 */ extern int tt_escape; extern long speed; extern char *dftty; extern char *tp, *lp; /* Temporary buffer & pointers */ extern char ttname[]; #ifdef CK_TAPI int tttapi = 0; /* is Line TAPI? */ struct keytab * tapilinetab = NULL; struct keytab * _tapilinetab = NULL; int ntapiline = 0; #endif /* CK_TAPI */ #ifdef NETCONN /* Network items */ #ifdef ANYX25 extern int revcall, closgr, cudata, nx25; extern char udata[]; extern struct keytab x25tab[]; #ifndef IBMX25 extern int npadx3; extern CHAR padparms[]; extern struct keytab padx3tab[]; #endif /* IBMX25 */ #endif /* ANYX25 */ #ifdef OS2 extern bool ttshare; #ifndef NT extern bool ttslip,ttppp; #endif /* NT */ #endif /* OS2 */ #ifdef NPIPE extern char pipename[]; #endif /* NPIPE */ #ifdef TCPSOCKET static struct keytab tcprawtab[] = { /* SET HOST options */ { "/default", NP_DEFAULT, CM_INV }, #ifdef CK_AUTHENTICATION #ifdef CK_KERBEROS #ifdef RLOGCODE { "/ek4login", NP_EK4LOGIN, 0 }, { "/ek5login", NP_EK5LOGIN, 0 }, { "/k4login", NP_K4LOGIN, 0 }, { "/k5login", NP_K5LOGIN, 0 }, #endif /* RLOGCODE */ #ifdef KRB5_U2U { "/k5user2user", NP_K5U2U, 0 }, #endif /* KRB5_U2U */ #endif /* CK_KERBEROS */ #endif /* CK_AUTHENTICATION */ { "/no-telnet-init", NP_NONE, 0 }, { "/none", NP_NONE, CM_INV }, { "/raw-socket", NP_TCPRAW, 0 }, #ifdef RLOGCODE { "/rlogin", NP_RLOGIN, 0 }, #endif /* RLOGCODE */ #ifdef CK_SSL { "/ssl", NP_SSL, 0 }, { "/ssl-raw", NP_SSL_RAW, 0 }, { "/ssl-telnet", NP_SSL_TELNET, 0 }, #endif /* CK_SSL */ { "/telnet", NP_TELNET, 0 }, #ifdef CK_SSL { "/tls", NP_TLS, 0 }, { "/tls-raw", NP_TLS_RAW, 0 }, { "/tls-telnet", NP_TLS_TELNET, 0 }, #endif /* CK_SSL */ { "", 0, 0 } }; static int ntcpraw = (sizeof(tcprawtab) / sizeof(struct keytab)) - 1; #ifdef RLOGCODE _PROTOTYP( int rlog_naws, (void) ); #endif /* RLOGCODE */ #endif /* TCPSOCKET */ #ifdef SUPERLAT extern char slat_pwd[18]; #endif /* SUPERLAT */ #endif /* NETCONN */ #ifdef COMMENT #ifndef NOSETKEY extern KEY *keymap; #ifndef OS2 #define mapkey(x) keymap[x] #endif /* OS2 */ extern MACRO *macrotab; #ifndef NOKVERBS extern struct keytab kverbs[]; extern int nkverbs; #endif /* NOKVERBS */ #endif /* NOSETKEY */ #else #ifndef NOSETKEY extern KEY *keymap; extern MACRO *macrotab; #ifndef NOKVERBS extern struct keytab kverbs[]; extern int nkverbs; #endif /* NOKVERBS */ #endif /* NOSETKEY */ #endif /* COMMENT */ #ifdef OS2 /* AUTODOWNLOAD parameters */ extern int adl_kmode, adl_zmode; /* Match Packet to signal download */ extern char * adl_kstr; /* KERMIT Download String */ extern char * adl_zstr; /* ZMODEM Download String */ extern int adl_kc0, adl_zc0; /* Process ADL C0s in emulation */ #endif /* OS2 */ /* Keyword tables ... */ extern struct keytab onoff[], rltab[]; extern int nrlt; #ifndef NOCSETS static struct keytab fdfltab[] = { { "7bit-character-set", 7, 0 }, { "8bit-character-set", 8, 0 } }; static int nfdflt = (sizeof(fdfltab) / sizeof(struct keytab)); #endif /* NOCSETS */ /* SET FILE parameters */ static struct keytab filtab[] = { #ifndef NOXFER #ifdef PATTERNS { "binary-patterns", XYFIBP, 0 }, #endif /* PATTERNS */ { "bytesize", XYFILS, 0 }, #ifndef NOCSETS { "character-set", XYFILC, 0 }, #endif /* NOCSETS */ { "collision", XYFILX, 0 }, { "default", XYF_DFLT, 0 }, { "destination", XYFILY, 0 }, { "display", XYFILD, CM_INV }, #ifdef CK_TMPDIR { "download-directory", XYFILG, 0 }, #endif /* CK_TMPDIR */ #endif /* NOXFER */ { "end-of-line", XYFILA, 0 }, { "eol", XYFILA, CM_INV }, #ifdef CK_CTRLZ { "eof", XYFILV, 0 }, #endif /* CK_CTRLZ */ #ifndef NOXFER { "fastlookups", 9997, CM_INV }, { "incomplete", XYFILI, 0 }, #ifndef datageneral { "inspection", XYF_INSP, CM_INV }, #endif /* datageneral */ #ifdef CK_LABELED { "label", XYFILL, 0 }, #endif /* CK_LABELED */ #ifdef UNIX #ifdef DYNAMIC { "listsize", XYF_LSIZ, 0 }, #endif /* DYNAMIC */ #endif /* UNIX */ { "names", XYFILN, 0 }, #ifdef UNIX { "output", XYFILH, 0 }, #endif /* UNIX */ #ifdef PATTERNS { "patterns", XYFIPA, 0 }, #endif /* PATTERNS */ #ifdef COMMENT /* Not implemented (but see CHMOD) */ { "permissions", XYF_PRM, CM_INV }, { "protection", XYF_PRM, 0 }, #endif /* COMMENt */ #ifdef VMS { "record-length", XYFILR, 0 }, #endif /* VMS */ #ifndef datageneral { "scan", XYF_INSP, 0 }, #endif /* datageneral */ #ifdef UNIX #ifdef DYNAMIC { "stringspace", XYF_SSPA, 0 }, #endif /* DYNAMIC */ #endif /* UNIX */ #ifdef PATTERNS { "t", XYFILT, CM_INV|CM_ABR }, { "text-patterns", XYFITP, 0 }, #endif /* PATTERNS */ #endif /* NOXFER */ { "type", XYFILT, 0 }, #ifdef UNICODE { "ucs", XYFILU, 0 }, #endif /* UNICODE */ #ifndef NOXFER { "warning", XYFILW, CM_INV } #endif /* NOXFER */ }; static int nfilp = (sizeof(filtab) / sizeof(struct keytab)); struct keytab pathtab[] = { { "absolute", PATH_ABS, 0 }, { "none", PATH_OFF, CM_INV }, { "off", PATH_OFF, 0 }, { "on", PATH_ABS, CM_INV }, { "relative", PATH_REL, 0 } }; int npathtab = (sizeof(pathtab) / sizeof(struct keytab)); struct keytab rpathtab[] = { { "absolute", PATH_ABS, 0 }, { "auto", PATH_AUTO, 0 }, { "none", PATH_OFF, CM_INV }, { "off", PATH_OFF, 0 }, { "on", PATH_ABS, CM_INV }, { "relative", PATH_REL, 0 } }; int nrpathtab = (sizeof(rpathtab) / sizeof(struct keytab)); #ifdef CK_CTRLZ struct keytab eoftab[] = { /* EOF detection method */ { "ctrl-z", 1, 0 }, { "length", 0, 0 }, { "noctrl-z", 0, CM_INV } }; #endif /* CK_CTRLZ */ struct keytab fttab[] = { /* File types for SET FILE TYPE */ { "ascii", XYFT_T, CM_INV }, #ifdef VMS { "b", XYFT_B, CM_INV|CM_ABR }, #endif /* VMS */ { "binary", XYFT_B, 0 }, #ifdef VMS { "block", XYFT_I, CM_INV }, { "image", XYFT_I, 0 }, #endif /* VMS */ #ifdef CK_LABELED { "labeled", XYFT_L, 0 }, #endif /* CK_LABELED */ #ifdef MAC { "macbinary", XYFT_M, 0 }, #endif /* MAC */ { "text", XYFT_T, 0 } }; int nfttyp = (sizeof(fttab) / sizeof(struct keytab)); static struct keytab rfttab[] = { /* File types for REMOTE SET FILE */ { "ascii", XYFT_T, CM_INV }, { "binary", XYFT_B, 0 }, #ifdef VMS { "labeled", XYFT_L, 0 }, #else #ifdef OS2 { "labeled", XYFT_L, 0 }, #endif /* OS2 */ #endif /* VMS */ { "text", XYFT_T, 0 } }; static int nrfttyp = (sizeof(rfttab) / sizeof(struct keytab)); #ifdef OS2ORUNIX #define ZOF_BLK 0 #define ZOF_NBLK 1 #define ZOF_BUF 2 #define ZOF_NBUF 3 static struct keytab zoftab[] = { { "blocking", ZOF_BLK, 0 }, { "buffered", ZOF_BUF, 0 }, { "nonblocking", ZOF_NBLK, 0 }, { "unbuffered", ZOF_NBUF, 0 } }; static int nzoftab = (sizeof(zoftab) / sizeof(struct keytab)); #endif /* OS2ORUNIX */ extern int query; /* Global flag for QUERY active */ #ifndef NOSPL #ifndef NOXFER static struct keytab vartyp[] = { /* Variable types for REMOTE QUERY */ { "global", (int) 'G', CM_INV }, { "kermit", (int) 'K', 0 }, { "system", (int) 'S', 0 }, { "user", (int) 'G', 0 } }; static int nvartyp = (sizeof(vartyp) / sizeof(struct keytab)); #endif /* NOXFER */ #endif /* NOSPL */ #ifdef CK_TIMERS static struct keytab timotab[] = { /* Timer types */ { "dynamic", 1, 0 }, { "fixed", 0, 0 } }; #endif /* CK_TIMERS */ #ifdef DCMDBUF extern char *atxbuf, *atmbuf; /* Atom buffer */ extern char *cmdbuf; /* Command buffer */ extern char *line, *tmpbuf; /* Character buffers for anything */ extern int *intime; /* INPUT TIMEOUT */ #else /* Not DCMDBUF ... */ extern char atxbuf[], atmbuf[]; /* Atom buffer */ extern char cmdbuf[]; /* Command buffer */ extern char line[], tmpbuf[]; /* Character buffer for anything */ extern int intime[]; #endif /* DCMDBUF */ #ifndef NOCSETS extern struct keytab fcstab[]; /* For SET FILE CHARACTER-SET */ extern struct csinfo fcsinfo[]; /* File character set info. */ extern struct keytab ttcstab[]; extern int nfilc, fcharset, tcharset, ntermc, tcsr, tcsl, dcset7, dcset8; #ifdef CKOUNI extern int tt_utf8; #endif /* CKOUNI */ #ifdef OS2 _PROTOTYP( int os2setcp, (int) ); _PROTOTYP( int os2getcp, (void) ); _PROTOTYP( void os2debugoff, (void) ); #endif /* OS2 */ #endif /* NOCSETS */ extern int cmdlvl; /* Overall command level */ #ifndef NOSPL #ifdef DCMDBUF extern int *inpcas; /* INPUT CASE setting on cmd stack */ #else extern int inpcas[]; #endif /* DCMDBUF */ #endif /* NOSPL */ #ifdef CK_CURSES #ifndef VMS _PROTOTYP(int tgetent,(char *, char *)); #else #ifdef __DECC _PROTOTYP(int tgetent,(char *, char *)); #endif /* __DECC */ #endif /* VMS */ #endif /* CK_CURSES */ #ifndef NOXMIT #define XMITF 0 /* SET TRANSMIT values */ #define XMITL 1 /* (Local to this module) */ #define XMITP 2 #define XMITE 3 #define XMITX 4 #define XMITS 5 #define XMITW 6 #define XMITT 7 #define XMBUFL 50 extern int xmitf, xmitl, xmitp, xmitx, xmits, xmitw, xmitt; char xmitbuf[XMBUFL+1] = { NUL }; /* TRANSMIT eof string */ struct keytab xmitab[] = { /* SET TRANSMIT */ { "echo", XMITX, 0 }, { "eof", XMITE, 0 }, { "fill", XMITF, 0 }, { "linefeed", XMITL, 0 }, { "locking-shift", XMITS, 0 }, { "pause", XMITW, 0 }, { "prompt", XMITP, 0 }, { "timeout", XMITT, 0 } }; int nxmit = (sizeof(xmitab) / sizeof(struct keytab)); #endif /* NOXMIT */ /* For SET FILE COLLISION */ /* Some of the following may be possible for some C-Kermit implementations */ /* but not others. Those that are not possible for your implementation */ /* should be ifdef'd out. */ struct keytab colxtab[] = { /* SET FILE COLLISION options */ #ifndef MAC { "append", XYFX_A, 0 }, /* append to old file */ #endif /* MAC */ #ifdef COMMENT { "ask", XYFX_Q, 0 }, /* ask what to do (not implemented) */ #endif { "backup", XYFX_B, 0 }, /* rename old file */ #ifndef MAC /* This crashes Mac Kermit. */ { "discard", XYFX_D, CM_INV }, /* don't accept new file */ { "no-supersede", XYFX_D, CM_INV }, /* ditto (MSK compatibility) */ #endif /* MAC */ { "overwrite", XYFX_X, 0 }, /* overwrite the old file */ { "reject", XYFX_D, 0 }, /* (better word than discard) */ { "rename", XYFX_R, 0 }, /* rename the incoming file */ #ifndef MAC /* This crashes Mac Kermit. */ { "update", XYFX_U, 0 }, /* replace if newer */ #endif /* MAC */ { "", 0, 0 } }; int ncolx = (sizeof(colxtab) / sizeof(struct keytab)) - 1; static struct keytab rfiltab[] = { /* for REMOTE SET FILE */ #ifndef NOCSETS { "character-set", XYFILC, 0 }, #endif /* NOCSETS */ { "collision", XYFILX, 0 }, { "incomplete", XYFILI, 0 }, { "names", XYFILN, 0 }, { "record-length", XYFILR, 0 }, { "type", XYFILT, 0 } }; int nrfilp = (sizeof(rfiltab) / sizeof(struct keytab)); struct keytab eoltab[] = { /* File eof delimiters */ { "cr", XYFA_C, 0 }, { "crlf", XYFA_2, 0 }, { "lf", XYFA_L, 0 } }; static int neoltab = (sizeof(eoltab) / sizeof(struct keytab)); struct keytab fntab[] = { /* File naming */ { "converted", XYFN_C, 0 }, { "literal", XYFN_L, 0 }, { "standard", XYFN_C, CM_INV } }; int nfntab = (sizeof(fntab) / sizeof(struct keytab)); #ifndef NOLOCAL /* Terminal parameters table */ static struct keytab trmtab[] = { #ifdef OS2 { "answerback", XYTANS, 0 }, #endif /* OS2 */ #ifdef CK_APC { "apc", XYTAPC, 0 }, #endif /* CK_APC */ #ifdef OS2 { "arrow-keys", XYTARR, 0 }, #endif /* OS2 */ #ifdef NT { "at", XYTATTR, CM_INV|CM_ABR }, { "att", XYTATTR, CM_INV|CM_ABR }, { "attr", XYTATTR, CM_INV|CM_ABR }, { "attr-bug", XYTATTBUG, CM_INV }, #endif /* NT */ #ifdef OS2 { "attribute", XYTATTR, 0 }, #endif /* OS2 */ #ifdef CK_APC #ifdef CK_AUTODL { "autodownload", XYTAUTODL, 0, }, #endif /* CK_AUTODL */ #endif /* CK_APC */ #ifdef OS2 { "autopage", XYTAPAGE, 0 }, { "autoscroll", XYTASCRL, 0 }, { "bell", XYTBEL, CM_INV }, #endif /* OS2 */ { "bytesize", XYTBYT, 0 }, #ifndef NOCSETS { "character-set", XYTCS, 0 }, #endif /* NOCSETS */ #ifdef OS2 { "code-page", XYTCPG, 0 }, { "color", XYTCOL, 0 }, { "controls", XYTCTRL, 0 }, #endif /* OS2 */ { "cr-display", XYTCRD, 0 }, #ifdef OS2 { "cursor", XYTCUR, 0 }, #endif /* OS2 */ { "debug", XYTDEB, 0 }, #ifdef OS2 { "dg-unix-mode", XYTUNX, 0 }, #endif /* OS2 */ { "echo", XYTEC, 0 }, { "escape-character", XYTESC, 0 }, #ifdef OS2 #ifdef PCFONTS { "font", XYTFON, 0 }, #else #ifdef KUI { "font", XYTFON, 0 }, #endif /* KUI */ #endif /* PCFONTS */ #endif /* OS2 */ { "height", XYTHIG, 0 }, #ifdef CKTIDLE { "idle-action", XYTIACT, 0 }, { "idle-limit", XYTITMO, CM_INV }, { "idle-send", XYTIDLE, CM_INV }, { "idle-timeout", XYTITMO, 0 }, #endif /* CKTIDLE */ #ifdef OS2 #ifndef NOCSETS { "kbd-follows-gl/gr", XYTKBDGL, 0 }, #endif /* NOCSETS */ { "key", XYTKEY, 0 }, { "keyboard-mode", XYTKBMOD, 0 }, { "keypad-mode", XYTKPD, 0 }, #endif /* OS2 */ { "lf-display", XYTLFD, 0 }, #ifndef NOCSETS #ifdef OS2 #ifndef KUI { "line-spacing", XYTLSP, CM_INV }, { "local-character-set", XYTLCS, 0 }, #else { "line-spacing", XYTLSP, 0 }, { "local-character-set", XYTLCS, CM_INV }, #endif /* KUI */ #else { "local-character-set", XYTLCS, CM_INV }, #endif /* OS2 */ #endif /* NOCSETS */ { "locking-shift", XYTSO, 0 }, #ifdef OS2 { "margin-bell", XYTMBEL, 0 }, #endif /* OS2 */ #ifdef OS2MOUSE { "mouse", XYTMOU, CM_INV }, #endif /* OS2MOUSE */ { "newline-mode", XYTNL, 0 }, #ifdef OS2 { "output-pacing", XYTPAC, 0 }, #ifdef PCTERM { "pcterm", XYTPCTERM, 0 }, #endif /* PCTERM */ #endif /* OS2 */ #ifdef OS2ORUNIX { "print", XYTPRN, 0 }, #endif /* OS2ORUNIX */ #ifndef NOCSETS #ifdef OS2 { "remote-character-set", XYTRCS, 0 }, #else { "remote-character-set", XYTRCS, CM_INV }, #endif /* OS2 */ #endif /* NOCSETS */ #ifdef OS2 { "roll-mode", XYTROL, 0 }, { "s", XYTUPD, CM_ABR|CM_INV }, { "sc", XYTUPD, CM_ABR|CM_INV }, { "scr", XYTUPD, CM_ABR|CM_INV }, { "scree", XYTUPD, CM_ABR|CM_INV }, { "screen", XYTUPD, CM_ABR|CM_INV }, { "screen-", XYTUPD, CM_ABR|CM_INV }, { "screen-mode", XYTSCNM, 0 }, { "screen-optimize", XYTOPTI, 0 }, { "screen-update", XYTUPD, 0 }, { "scrollback", XYSCRS, 0 }, { "send-data", XYTSEND, 0 }, { "send-end-of-block", XYTSEOB, 0 }, { "sgr-colors", XYTSGRC, 0 }, { "sni-ch.code", XYTSNICC, 0 }, { "sni-firmware-versions", XYTSNIFV, 0 }, { "sni-language", XYTVTLNG, 0 }, { "sni-pagemode", XYTSNIPM, CM_INV }, { "sni-scrollmode", XYTSNISM, CM_INV }, { "spacing-attribute-character", XYTSAC, CM_INV }, { "statusline", XYTSTAT, 0 }, { "tra", XYTCTS, CM_INV|CM_ABR }, { "transmit-timeout", XYTCTS, 0 }, #endif /* OS2 */ #ifdef OS2ORUNIX { "transparent-print", XYTPRN, CM_INV }, #endif /* OS2ORUNIX */ #ifdef CK_TRIGGER { "trigger", XYTRIGGER,0 }, #endif /* CK_TRIGGER */ #ifdef OS2 { "type", XYTTYP, 0 }, #else { "type", XYTTYP, CM_INV }, #endif /* OS2 */ #ifndef NOCSETS #ifdef UNICODE #ifdef CKOUNI { "unicode", XYTUNI, CM_INV }, #endif /* CKOUNI */ #endif /* UNICODE */ #endif /* NOCSETS */ #ifdef OS2 { "unix-mode", XYTUNX, CM_INV }, { "url-highlight", XYTURLHI, 0 }, #ifdef NT { "video-change", XYTVCH, 0 }, #endif /* NT */ { "vt-language", XYTVTLNG, 0 }, { "vt-nrc-mode", XYTVTNRC, 0 }, #endif /* OS2 */ { "width", XYTWID, 0 }, #ifdef OS2 { "wrap", XYTWRP, 0 }, #endif /* OS2 */ { "", 0, 0 } }; int ntrm = (sizeof(trmtab) / sizeof(struct keytab)) - 1; #ifdef OS2 struct keytab termctrl[] = { /* SET TERM CONTROLS */ { "7", 7, 0 }, { "8", 8, 0 } }; int ntermctrl = (sizeof(termctrl) / sizeof(struct keytab)); struct keytab curontab[] = { /* SET TERM CURSOR */ #ifdef KUI { "noblink", 2, 0 }, #else { "noblink", 2, CM_INV }, #endif /* KUI */ { "off", 0, 0 }, { "on", 1, 0 } }; int ncuron = (sizeof(curontab) / sizeof(struct keytab)); struct keytab rolltab[] = { /* Set TERM Roll Options */ { "insert", TTR_INSERT, 0 }, { "keystrokes",TTR_KEYS, 0 }, { "off", TTR_OVER, CM_INV }, { "on", TTR_INSERT, CM_INV }, { "overwrite", TTR_OVER, 0 } }; int nroll = (sizeof(rolltab) / sizeof(struct keytab)); struct keytab rollkeytab[] = { /* Set TERM ROLL KEYSTROKES */ { "ignore", TTRK_IGN, 0 }, { "restore-and-send", TTRK_RST, 0 }, { "send", TTRK_SND, 0 } }; int nrollkey = (sizeof(rollkeytab) / sizeof(struct keytab)); #define TT_GR_ALL 4 #define TT_GR_G0 0 #define TT_GR_G1 1 #define TT_GR_G2 2 #define TT_GR_G3 3 #define TT_GR_KBD 4 struct keytab graphsettab[] = { /* DEC VT Graphic Sets */ { "all", TT_GR_ALL, 0 }, { "g0", TT_GR_G0, 0 }, { "g1", TT_GR_G1, 0 }, { "g2", TT_GR_G2, 0 }, { "g3", TT_GR_G3, 0 }, { "keyboard", TT_GR_KBD, 0 } }; int ngraphset = (sizeof(graphsettab) / sizeof(struct keytab)); #endif /* OS2 */ struct keytab adltab[] = { /* Autodownload Options */ { "ask", TAD_ASK, 0 }, { "error", TAD_ERR, 0 }, #ifdef OS2 { "kermit", TAD_K, 0 }, #endif /* OS2 */ { "off", TAD_OFF, 0 }, { "on", TAD_ON, 0 }, #ifdef OS2 { "zmodem", TAD_Z, 0 }, #endif /* OS2 */ { "", 0, 0 } }; int nadltab = (sizeof(adltab) / sizeof(struct keytab)) - 1; struct keytab adlerrtab[] = { /* Autodownload Error Options */ { "continue", 0, 0 }, { "go", 0, CM_INV }, { "stop", 1, 0 } }; int nadlerrtab = (sizeof(adlerrtab) / sizeof(struct keytab)); #ifdef OS2 struct keytab adlxtab[] = { /* Autodownload Options */ { "c0-conflicts", TAD_X_C0, 0 }, { "detection-method", TAD_X_DETECT, 0 }, { "string", TAD_X_STR, 0 } }; int nadlxtab = (sizeof(adlxtab) / sizeof(struct keytab)); struct keytab adldtab[] = { /* Auto-dl Detection Methods */ { "packet", ADL_PACK, 0 }, { "string", ADL_STR, 0 } }; int nadldtab = (sizeof(adldtab) / sizeof(struct keytab)); struct keytab adlc0tab[] = { /* Auto-dl Detection Methods */ { "ignored-by-emulator", 0, 0 }, { "processed-by-emulator", 1, 0 } }; int nadlc0tab = (sizeof(adlc0tab) / sizeof(struct keytab)); #ifndef NOCSETS struct keytab vtlangtab[] = { { "belgian", VTL_BELGIAN , 0 }, { "british", VTL_BRITISH , 0 }, { "canadian", VTL_CANADIAN, 0 }, { "czech", VTL_CZECH , 0 }, { "danish", VTL_DANISH , 0 }, { "dutch", VTL_DUTCH , 0 }, { "finnish", VTL_FINNISH , 0 }, { "french", VTL_FRENCH , 0 }, { "french-canadian",VTL_FR_CAN , 0 }, { "german", VTL_GERMAN , 0 }, { "greek", VTL_GREEK , 0 }, { "hebrew", VTL_HEBREW , 0 }, { "hungarian", VTL_HUNGARIA, 0 }, { "italian", VTL_ITALIAN , 0 }, { "latin-american", VTL_LATIN_AM, 0 }, { "north-american", VTL_NORTH_AM, 0 }, { "norwegian", VTL_NORWEGIA, 0 }, { "polish", VTL_POLISH , 0 }, { "portugese", VTL_PORTUGES, 0 }, { "romanian", VTL_ROMANIAN, 0 }, { "russian", VTL_RUSSIAN , 0 }, { "scs", VTL_SCS , CM_INV }, { "slovak", VTL_SLOVAK , 0 }, { "spanish", VTL_SPANISH , 0 }, { "swedish", VTL_SWEDISH , 0 }, { "swiss-french", VTL_SW_FR , 0 }, { "swiss-german", VTL_SW_GR , 0 }, { "turkish-f", VTL_TURK_F , CM_INV }, { "turkish-q", VTL_TURK_Q , CM_INV } }; int nvtlangtab = (sizeof(vtlangtab) / sizeof(struct keytab)); #endif /* NOCSETS */ #endif /* OS2 */ struct keytab crdtab[] = { /* Carriage-return display */ { "crlf", 1, 0 }, { "normal", 0, 0 } }; extern int tt_crd; /* Carriage-return display variable */ extern int tt_lfd; /* Linefeed display variable */ #ifdef CK_APC extern int apcstatus, apcactive; static struct keytab apctab[] = { /* Terminal APC parameters */ { "no-input", APC_ON|APC_NOINP,0 }, { "off", APC_OFF, 0 }, { "on", APC_ON, 0 }, { "unchecked", APC_ON|APC_UNCH, 0 }, { "unchecked-no-input", APC_ON|APC_NOINP|APC_UNCH, 0 } }; int napctab = (sizeof(apctab) / sizeof(struct keytab)); #endif /* CK_APC */ #endif /* NOLOCAL */ extern int autodl, adl_err, adl_ask; struct keytab beltab[] = { /* Terminal bell mode */ #ifdef OS2 { "audible", XYB_AUD, 0 }, { "none", XYB_NONE, 0 }, #else { "audible", XYB_AUD, CM_INV }, { "none", XYB_NONE, CM_INV }, #endif /* OS2 */ #ifdef OS2 { "off", XYB_NONE, CM_INV }, { "on", XYB_AUD, CM_INV }, #else { "off", XYB_NONE, 0 }, { "on", XYB_AUD, 0 }, #endif /* OS2 */ #ifdef OS2 { "visible", XYB_VIS, 0 }, #endif /* OS2 */ { "", 0, 0 } }; int nbeltab = sizeof(beltab)/sizeof(struct keytab) - 1; int tt_unicode = 1; /* Use Unicode if possible */ #ifdef CKTIDLE int tt_idlesnd_tmo = 0; /* Idle Send Timeout, disabled */ char * tt_idlesnd_str = NULL; /* Idle Send String, none */ char * tt_idlestr = NULL; extern int tt_idleact, tt_idlelimit; #endif /* CKTIDLE */ #ifdef OS2 #ifndef NOLOCAL /* OS/2 serial communication devices. */ struct keytab os2devtab[] = { { "1", 1, CM_INV }, /* Invisible synonyms, like */ { "2", 2, CM_INV }, /* "set port 1" */ { "3", 3, CM_INV }, { "4", 4, CM_INV }, { "5", 5, CM_INV }, { "6", 6, CM_INV }, { "7", 7, CM_INV }, { "8", 8, CM_INV }, { "com1", 1, 0 }, /* Real device names */ { "com2", 2, 0 }, { "com3", 3, 0 }, { "com4", 4, 0 }, { "com5", 5, 0 }, { "com6", 6, 0 }, { "com7", 7, 0 }, { "com8", 8, 0 }, #ifdef OS2ONLY { "slipcom1", 1, 0 }, /* For use with SLIP driver */ { "slipcom2", 2, 0 }, /* shared access */ { "slipcom3", 3, 0 }, { "slipcom4", 4, 0 }, { "slipcom5", 5, 0 }, { "slipcom6", 6, 0 }, { "slipcom7", 7, 0 }, { "slipcom8", 8, 0 }, { "pppcom1", 1, 0 }, /* For use with PPP driver */ { "pppcom2", 2, 0 }, /* shared access */ { "pppcom3", 3, 0 }, { "pppcom4", 4, 0 }, { "pppcom5", 5, 0 }, { "pppcom6", 6, 0 }, { "pppcom7", 7, 0 }, { "pppcom8", 8, 0 } #endif /* OS2ONLY */ }; int nos2dev = (sizeof(os2devtab) / sizeof(struct keytab)) - 1; #ifdef OS2ONLY struct keytab os2ppptab[] = { { "0", 0, CM_INV }, { "1", 1, CM_INV }, /* Invisible synonyms, like */ { "2", 2, CM_INV }, /* "set port 1" */ { "3", 3, CM_INV }, { "4", 4, CM_INV }, { "5", 5, CM_INV }, { "6", 6, CM_INV }, { "7", 7, CM_INV }, { "8", 8, CM_INV }, { "9", 9, CM_INV }, { "ppp0", 0, 0 }, { "ppp1", 1, 0 }, /* For use with PPP driver */ { "ppp2", 2, 0 }, /* shared access */ { "ppp3", 3, 0 }, { "ppp4", 4, 0 }, { "ppp5", 5, 0 }, { "ppp6", 6, 0 }, { "ppp7", 7, 0 }, { "ppp8", 8, 0 }, { "ppp9", 9, 0 } }; int nos2ppp = (sizeof(os2ppptab) / sizeof(struct keytab)); #endif /* OS2ONLY */ /* Terminal parameters that can be set by SET commands. Used by the ck?con.c terminal emulator code. For now, only used for #ifdef OS2. Should add these for Macintosh. */ int tt_arrow = TTK_NORM; /* Arrow key mode: normal (cursor) */ int tt_keypad = TTK_NORM; /* Keypad mode: normal (numeric) */ int tt_shift_keypad = 0; /* Keypad Shift mode: Off */ int tt_wrap = 1; /* Terminal wrap, 1 = On */ int tt_type = TT_VT220; /* Terminal type, initially VT220 */ int tt_type_mode = TT_VT220; /* Terminal type set by host command */ int tt_cursor = 0; /* Terminal cursor, 0 = Underline */ int tt_cursor_usr = 0; /* Users Terminal cursor type */ int tt_cursorena_usr = 1; /* Users Terminal cursor enabled */ int tt_cursor_blink = 1; /* Terminal Cursor Blink */ int tt_answer = 0; /* Terminal answerback (disabled) */ int tt_scrsize[VNUM] = {512,512,512,1}; /* Terminal scrollback buffer size */ int tt_roll[VNUM] = {1,1,1,1}; /* Terminal roll (on) */ int tt_rkeys[VNUM] = {1,1,1,1}; /* Terminal roll keys (send) */ int tt_pacing = 0; /* Terminal output-pacing (none) */ int tt_ctstmo = 15; /* Terminal transmit-timeout */ int tt_codepage = -1; /* Terminal code-page */ int tt_update = 100; /* Terminal screen-update interval */ int tt_updmode = TTU_FAST; /* Terminal screen-update mode FAST */ extern int updmode; #ifndef KUI int tt_status[VNUM] = {1,1,0,0}; /* Terminal status line displayed */ int tt_status_usr[VNUM] = {1,1,0,0}; #else /* KUI */ extern CKFLOAT floatval; CKFLOAT tt_linespacing[VNUM] = {1.0,1.0,1.0,1.0}; #ifdef K95G int tt_status[VNUM] = {1,1,0,0}; /* Terminal status line displayed */ int tt_status_usr[VNUM] = {1,1,0,0}; #else /* K95G */ int tt_status[VNUM] = {0,0,0,0}; /* Terminal status line displayed */ int tt_status_usr[VNUM] = {0,0,0,0}; #endif /* K95G */ #endif /* KUI */ int tt_senddata = 0; /* Let host read terminal data */ extern int wy_blockend; /* Terminal Send Data EOB type */ int tt_hidattr = 1; /* Attributes are hidden */ extern unsigned char colornormal, colorselect, colorunderline, colorstatus, colorhelp, colorborder, colorgraphic, colordebug, colorreverse, coloritalic; extern int trueblink, trueunderline, truereverse, trueitalic, truedim; extern int bgi, fgi; extern int scrninitialized[]; struct keytab audibletab[] = { /* Terminal Bell Audible mode */ { "beep", XYB_BEEP, 0 }, /* Values ORd with bell mode */ { "system-sounds", XYB_SYS, 0 } }; int naudibletab = sizeof(audibletab)/sizeof(struct keytab); struct keytab akmtab[] = { /* Arrow key mode */ { "application", TTK_APPL, 0 }, { "cursor", TTK_NORM, 0 } }; struct keytab kpmtab[] = { /* Keypad mode */ { "application", TTK_APPL, 0 }, { "numeric", TTK_NORM, 0 } }; struct keytab ttcolmodetab[] = { { "current-color", 0, 0 }, { "default-color", 1, 0 } }; int ncolmode = sizeof(ttcolmodetab)/sizeof(struct keytab); #define TTCOLNOR 0 #define TTCOLREV 1 #define TTCOLUND 2 #define TTCOLSTA 3 #define TTCOLHLP 4 #define TTCOLBOR 5 #define TTCOLSEL 6 #define TTCOLDEB 7 #define TTCOLGRP 8 #define TTCOLITA 9 #define TTCOLRES 10 #define TTCOLERA 11 struct keytab ttycoltab[] = { /* Terminal Screen coloring */ { "border", TTCOLBOR, 0 }, /* Screen border color */ { "debug-terminal", TTCOLDEB, 0 }, /* Debug color */ { "erase", TTCOLERA, 0 }, /* Erase mode */ { "graphic", TTCOLGRP, 0 }, /* Graphic Color */ { "help-text", TTCOLHLP, 0 }, /* Help screens */ { "italic", TTCOLITA, 0 }, /* Italic Color */ { "normal", TTCOLNOR, CM_INV }, /* Normal screen text */ { "reset-on-esc[0m", TTCOLRES, 0 }, /* Reset on ESC [ 0 m */ { "reverse-video", TTCOLREV, 0 }, /* Reverse video */ { "status-line", TTCOLSTA, 0 }, /* Status line */ { "selection", TTCOLSEL, 0 }, /* Selection color */ { "terminal-screen", TTCOLNOR, 0 }, /* Better name than "normal" */ { "underlined-text", TTCOLUND, 0 } /* Underlined text */ }; int ncolors = (sizeof(ttycoltab) / sizeof(struct keytab)); #define TTATTNOR 0 #define TTATTBLI 1 #define TTATTREV 2 #define TTATTUND 3 #define TTATTPRO 4 #define TTATTBLD 5 #define TTATTDIM 6 #define TTATTINV 7 #define TTATTITA 8 #define TTATTDONE 9 struct keytab ttyattrtab[] = { { "blink", TTATTBLI, 0 }, { "dim", TTATTDIM, 0 }, { "italic", TTATTITA, 0 }, { "protected", TTATTPRO, 0 }, { "reverse", TTATTREV, 0 }, { "underline", TTATTUND, 0 } }; int nattrib = (sizeof(ttyattrtab) / sizeof(struct keytab)); struct keytab ttyprotab[] = { { "blink", TTATTBLI, 0 }, { "bold", TTATTBLD, 0 }, { "dim", TTATTDIM, 0 }, { "done", TTATTDONE, CM_INV }, { "invisible", TTATTINV, 0 }, { "italic", TTATTITA, 0 }, { "normal", TTATTNOR, 0 }, { "reverse", TTATTREV, 0 }, { "underlined", TTATTUND, 0 } }; int nprotect = (sizeof(ttyprotab) / sizeof(struct keytab)); struct keytab ttyseobtab[] = { { "crlf_etx", 1, 0 }, { "us_cr", 0, 0 } }; struct keytab ttyclrtab[] = { /* Colors */ { "black", 0, 0 }, { "blue", 1, 0 }, { "brown", 6, 0 }, { "cyan", 3, 0 }, { "darkgray", 8, CM_INV }, { "dgray", 8, 0 }, { "green", 2, 0 }, { "lblue", 9, CM_INV }, { "lcyan", 11, CM_INV }, { "lgray", 7, CM_INV }, { "lgreen", 10, CM_INV }, { "lightblue", 9, 0 }, { "lightcyan", 11, 0 }, { "lightgray", 7, 0 }, { "lightgreen", 10, 0 }, { "lightmagenta", 13, 0 }, { "lightred", 12, 0 }, { "lmagenta", 13, CM_INV }, { "lred", 12, CM_INV }, { "magenta", 5, 0 }, { "red", 4, 0 }, { "white", 15, 0 }, { "yellow", 14, 0 } }; int nclrs = (sizeof (ttyclrtab) / sizeof (struct keytab)); struct keytab ttycurtab[] = { { "full", TTC_BLOCK, 0 }, { "half", TTC_HALF, 0 }, { "underline", TTC_ULINE, 0 } }; int ncursors = 3; struct keytab ttyptab[] = { { "aaa", TT_AAA, CM_INV }, /* AnnArbor */ { "adm3a", TT_ADM3A, 0 }, /* LSI ADM-3A */ { "adm5", TT_ADM5, 0 }, /* LSI ADM-5 */ { "aixterm", TT_AIXTERM, 0 }, /* IBM AIXterm */ { "annarbor", TT_AAA, 0 }, /* AnnArbor */ { "ansi-bbs", TT_ANSI, 0 }, /* ANSI.SYS (BBS) */ { "at386", TT_AT386, 0 }, /* Unixware ANSI */ { "avatar/0+",TT_ANSI, 0 }, /* AVATAR/0+ */ { "ba80", TT_BA80, 0 }, /* Nixdorf BA80 */ { "be", TT_BEOS, CM_INV|CM_ABR }, { "beos-ansi",TT_BEOS, CM_INV }, /* BeOS ANSI */ { "beterm", TT_BEOS, 0 }, /* BeOS Terminal (as of PR2 ) */ { "d200", TT_DG200, CM_INV|CM_ABR }, /* Data General DASHER 200 */ { "d210", TT_DG210, CM_INV|CM_ABR }, /* Data General DASHER 210 */ { "d217", TT_DG217, CM_INV|CM_ABR }, /* Data General DASHER 217 */ { "dg200", TT_DG200, 0 }, /* Data General DASHER 200 */ { "dg210", TT_DG210, 0 }, /* Data General DASHER 210 */ { "dg217", TT_DG217, 0 }, /* Data General DASHER 217 */ { "h1500", TT_HZL1500, CM_INV }, /* Hazeltine 1500 */ { "h19", TT_H19, CM_INV }, /* Heath-19 */ { "heath19", TT_H19, 0 }, /* Heath-19 */ { "hft", TT_HFT, 0 }, /* IBM High Function Terminal */ { "hp2621a", TT_HP2621, 0 }, /* HP 2621A */ { "hpterm", TT_HPTERM, 0 }, /* HP TERM */ { "hz1500", TT_HZL1500, 0 }, /* Hazeltine 1500 */ { "ibm3151", TT_IBM31, 0 }, /* IBM 3101-xx,3161 */ { "linux", TT_LINUX, 0 }, /* Linux */ { "qansi", TT_QANSI, 0 }, /* QNX ANSI */ { "qnx", TT_QNX, 0 }, /* QNX Console */ { "scoansi", TT_SCOANSI, 0 }, /* SCO ANSI */ { "sni-97801",TT_97801, 0 }, /* SNI 97801 */ { "sun", TT_SUN, 0 }, /* SUN Console */ /* The idea of NONE is to let the console driver handle the escape sequences, which, in theory at least, would give not only ANSI emulation, but also any other kind of emulation that might be provided by alternative console drivers, if any existed. For this to work, ckocon.c would need to be modified to make higher-level calls, like VioWrtTTY(), DosWrite(), or (simply) write(), rather than VioWrt*Cell() and similar, and it would also have to give up its rollback feature, and its status line and help screens would also have to be forgotten or else done in an ANSI way. As matters stand, we already have perfectly good ANSI emulation built in, and there are no alternative console drivers available, so there is no point in having a terminal type of NONE, so it is commented out. However, should you uncomment it, it will work like a "glass tty" -- no escape sequence interpretation at all; somewhat similar to debug mode, except without the debugging (no highlighting of control chars or escape sequences); help screens, status line, and rollback will still work. */ #ifdef OS2PM #ifdef COMMENT { "tek4014", TT_TEK40, 0 }, #endif /* COMMENT */ #endif /* OS2PM */ { "tty", TT_NONE, 0 }, { "tvi910+", TT_TVI910, 0 }, { "tvi925", TT_TVI925, 0 }, { "tvi950", TT_TVI950, 0 }, { "vc404", TT_VC4404, 0 }, { "vc4404", TT_VC4404, CM_INV }, { "vip7809", TT_VIP7809,0 }, { "vt100", TT_VT100, 0 }, { "vt102", TT_VT102, 0 }, { "vt220", TT_VT220, 0 }, { "vt220pc", TT_VT220PC,0 }, { "vt320", TT_VT320, 0 }, { "vt320pc", TT_VT320PC,0 }, { "vt52", TT_VT52, 0 }, #ifdef NT { "vtnt", TT_VTNT, 0 }, #else /* NT */ { "vtnt", TT_VTNT, CM_INV }, #endif /* NT */ { "wy160", TT_WY160, 0 }, { "wy30", TT_WY30, 0 }, { "wy370", TT_WY370, 0 }, { "wy50", TT_WY50, 0 }, { "wy60", TT_WY60, 0 }, { "wyse30", TT_WY30, CM_INV }, { "wyse370", TT_WY370, CM_INV }, { "wyse50", TT_WY50, CM_INV }, { "wyse60", TT_WY60, CM_INV } }; int nttyp = (sizeof(ttyptab) / sizeof(struct keytab)); struct keytab ttkeytab[] = { { "aaa", TT_AAA, CM_INV }, /* AnnArbor */ { "adm3a", TT_ADM3A, 0 }, /* LSI ADM-3A */ { "adm5", TT_ADM5, 0 }, /* LSI ADM-5 */ { "aixterm", TT_AIXTERM, 0 }, /* IBM AIXterm */ { "annarbor", TT_AAA, 0 }, /* AnnArbor */ { "ansi-bbs", TT_ANSI, 0 }, /* ANSI.SYS (BBS) */ { "at386", TT_AT386, 0 }, /* Unixware ANSI */ { "avatar/0+", TT_ANSI, 0 }, /* AVATAR/0+ */ { "ba80", TT_BA80, 0 }, /* Nixdorf BA80 */ { "be", TT_BEOS, CM_INV|CM_ABR }, { "beos-ansi", TT_BEOS, CM_INV }, /* BeOS ANSI */ { "beterm", TT_BEOS, 0 }, /* BeOS Terminal (DR2) */ { "d200", TT_DG200, CM_INV|CM_ABR }, /* DG DASHER 200 */ { "d210", TT_DG210, CM_INV|CM_ABR }, /* DG DASHER 210 */ { "d217", TT_DG217, CM_INV|CM_ABR }, /* DG DASHER 217 */ { "dg200", TT_DG200, 0 }, /* DG DASHER 200 */ { "dg210", TT_DG210, 0 }, /* DG DASHER 210 */ { "dg217", TT_DG217, 0 }, /* DG DASHER 217 */ { "emacs", TT_KBM_EMACS, 0 }, /* Emacs mode */ { "h19", TT_H19, CM_INV }, /* Heath-19 */ { "heath19", TT_H19, 0 }, /* Heath-19 */ { "hebrew", TT_KBM_HEBREW, 0 }, /* Hebrew mode */ { "hft", TT_HFT, 0 }, /* IBM High Function Term */ { "hp2621a", TT_HP2621, 0 }, /* HP 2621A */ { "hpterm", TT_HPTERM, 0 }, /* HP TERM */ { "hz1500", TT_HZL1500, 0 }, /* Hazeltine 1500 */ { "ibm3151", TT_IBM31, 0 }, /* IBM 3101-xx,3161 */ { "linux", TT_LINUX, 0 }, /* Linux */ { "qansi", TT_QANSI, 0 }, /* QNX ANSI */ { "qnx", TT_QNX, 0 }, /* QNX */ { "russian", TT_KBM_RUSSIAN,0 }, /* Russian mode */ { "scoansi", TT_SCOANSI, 0 }, /* SCO ANSI */ { "sni-97801", TT_97801, 0 }, /* SNI 97801 */ { "sun", TT_SUN, 0 }, /* SUN Console */ #ifdef OS2PM #ifdef COMMENT { "tek4014", TT_TEK40, 0 }, #endif /* COMMENT */ #endif /* OS2PM */ { "tty", TT_NONE, 0 }, { "tvi910+", TT_TVI910, 0 }, { "tvi925", TT_TVI925, 0 }, { "tvi950", TT_TVI950, 0 }, { "vc404", TT_VC4404, 0 }, { "vc4404", TT_VC4404, CM_INV }, { "vip7809", TT_VIP7809, 0 }, { "vt100", TT_VT100, 0 }, { "vt102", TT_VT102, 0 }, { "vt220", TT_VT220, 0 }, { "vt220pc", TT_VT220PC, 0 }, { "vt320", TT_VT320, 0 }, { "vt320pc", TT_VT320PC, 0 }, { "vt52", TT_VT52, 0 }, { "vtnt", TT_VTNT, CM_INV }, { "wp", TT_KBM_WP, 0 }, /* Word Perfect mode */ { "wy160", TT_WY160, 0 }, { "wy30", TT_WY30, 0 }, { "wy370", TT_WY370, 0 }, { "wy50", TT_WY50, 0 }, { "wy60", TT_WY60, 0 }, { "wyse30", TT_WY30, CM_INV }, { "wyse370", TT_WY370, CM_INV }, { "wyse50", TT_WY50, CM_INV }, { "wyse60", TT_WY60, CM_INV } }; int nttkey = (sizeof(ttkeytab) / sizeof(struct keytab)); #ifndef NOSETKEY struct keytab kbmodtab[] = { { "emacs", KBM_EM, 0 }, { "english", KBM_EN, CM_INV }, { "hebrew", KBM_HE, 0 }, { "normal", KBM_EN, 0 }, { "none", KBM_EN, CM_INV }, { "russian", KBM_RU, 0 }, { "wp", KBM_WP, 0 } }; int nkbmodtab = (sizeof(kbmodtab) / sizeof(struct keytab)); #endif /* NOSETKEY */ #endif /* NOLOCAL */ int tt_inpacing = 0; /* input-pacing (none) */ struct keytab prtytab[] = { /* OS/2 Priority Levels */ { "foreground-server", XYP_SRV, 0 }, { "idle", XYP_IDLE, CM_INV }, { "regular", XYP_REG, 0 }, { "time-critical", XYP_RTP, 0 } }; int nprty = (sizeof(prtytab) / sizeof(struct keytab)); #endif /* OS2 */ #ifdef NT struct keytab win95tab[] = { /* Win95 work-arounds */ { "8.3-filenames", XYW8_3, 0 }, { "alt-gr", XYWAGR, 0 }, { "horizontal-scan-line-substitutions", XYWHSL, 0 }, { "keyboard-translation", XYWKEY, 0 }, { "lucida-substitutions", XYWLUC, 0 }, { "overlapped-io", XYWOIO, 0 }, { "popups", XYWPOPUP, 0 }, { "select-bug", XYWSELECT, 0 } }; int nwin95 = (sizeof(win95tab) / sizeof(struct keytab)); #endif /* NT */ #ifdef OS2MOUSE extern int wideresult; int tt_mouse = 1; /* Terminal mouse on/off */ struct keytab mousetab[] = { /* Mouse items */ { "activate", XYM_ON, 0 }, { "button", XYM_BUTTON, 0 }, { "clear", XYM_CLEAR, 0 }, { "debug", XYM_DEBUG, 0 } }; int nmtab = (sizeof(mousetab)/sizeof(struct keytab)); struct keytab mousebuttontab[] = { /* event button */ { "1", XYM_B1, 0 }, { "2", XYM_B2, 0 }, { "3", XYM_B3, 0 }, { "one", XYM_B1, CM_INV }, { "three", XYM_B3, CM_INV }, { "two", XYM_B2, CM_INV } }; int nmbtab = (sizeof(mousebuttontab) / sizeof(struct keytab)); struct keytab mousemodtab[] = { /* event button key modifier */ { "alt", XYM_ALT, 0 }, { "alt-shift", XYM_SHIFT|XYM_ALT, 0 }, { "ctrl", XYM_CTRL, 0 }, { "ctrl-alt", XYM_CTRL|XYM_ALT, 0 }, { "ctrl-alt-shift", XYM_CTRL|XYM_SHIFT|XYM_ALT, 0 }, { "ctrl-shift", XYM_CTRL|XYM_SHIFT, 0 }, { "none", 0, 0 }, { "shift", XYM_SHIFT, 0 } }; int nmmtab = (sizeof(mousemodtab) / sizeof(struct keytab)); struct keytab mclicktab[] = { /* event button click modifier */ { "click", XYM_C1, 0 }, { "drag", XYM_DRAG, 0 }, { "double-click", XYM_C2, 0 } }; int nmctab = (sizeof(mclicktab) / sizeof(struct keytab)); #ifndef NOKVERBS extern int nkverbs; extern struct keytab kverbs[]; #endif /* NOKVERBS */ #endif /* OS2MOUSE */ /* #ifdef VMS */ struct keytab fbtab[] = { /* Binary record types for VMS */ { "fixed", XYFT_B, 0 }, /* Fixed is normal for binary */ { "undefined", XYFT_U, 0 } /* Undefined if they ask for it */ }; int nfbtyp = (sizeof(fbtab) / sizeof(struct keytab)); /* #endif */ #ifdef VMS struct keytab lbltab[] = { /* Labeled File info */ { "acl", LBL_ACL, 0 }, { "backup-date", LBL_BCK, 0 }, { "name", LBL_NAM, 0 }, { "owner", LBL_OWN, 0 }, { "path", LBL_PTH, 0 } }; int nlblp = (sizeof(lbltab) / sizeof(struct keytab)); #else #ifdef OS2 struct keytab lbltab[] = { /* Labeled File info */ { "archive", LBL_ARC, 0 }, { "extended", LBL_EXT, 0 }, { "hidden", LBL_HID, 0 }, { "read-only", LBL_RO, 0 }, { "system", LBL_SYS, 0 } }; int nlblp = (sizeof(lbltab) / sizeof(struct keytab)); #endif /* OS2 */ #endif /* VMS */ #ifdef CK_CURSES #ifdef CK_PCT_BAR static struct keytab fdftab[] = { /* SET FILE DISPLAY FULL options */ { "thermometer", 1, 0, }, { "no-thermometer", 0, 0 } }; extern int thermometer; #endif /* CK_PCT_BAR */ #endif /* CK_CURSES */ static struct keytab fdtab[] = { /* SET FILE DISPLAY options */ #ifdef MAC /* Macintosh */ { "fullscreen", XYFD_R, 0 }, /* Full-screen but not curses */ { "none", XYFD_N, 0 }, { "off", XYFD_N, CM_INV }, { "on", XYFD_R, CM_INV }, { "quiet", XYFD_N, CM_INV }, #else /* Not Mac */ { "brief", XYFD_B, 0 }, /* Brief */ { "crt", XYFD_S, 0 }, /* CRT display */ #ifdef CK_CURSES #ifdef COMMENT { "curses", XYFD_C, CM_INV }, /* Full-screen, curses */ #endif /* COMMENT */ { "fullscreen", XYFD_C, 0 }, /* Full-screen, whatever the method */ #endif /* CK_CURSES */ #ifdef KUI { "gui", XYFD_G, 0 }, /* GUI */ #endif /* KUI */ { "none", XYFD_N, 0 }, /* No display */ { "off", XYFD_N, CM_INV }, /* Ditto */ { "on", XYFD_R, CM_INV }, /* On = Serial */ { "quiet", XYFD_N, CM_INV }, /* No display */ { "serial", XYFD_R, 0 }, /* Serial */ #endif /* MAC */ { "", 0, 0 } }; int nfdtab = (sizeof(fdtab) / sizeof(struct keytab)) - 1; struct keytab rsrtab[] = { /* For REMOTE SET RECEIVE */ { "packet-length", XYLEN, 0 }, { "timeout", XYTIMO, 0 } }; int nrsrtab = (sizeof(rsrtab) / sizeof(struct keytab)); /* Send/Receive Parameters */ struct keytab srtab[] = { { "backup", XYBUP, 0 }, #ifndef NOCSETS { "character-set-selection", XYCSET, 0 }, #endif /* NOCSETS */ { "control-prefix", XYQCTL, 0 }, #ifdef CKXXCHAR { "double-character", XYDBL, 0 }, #endif /* CKXXCHAR */ { "end-of-packet", XYEOL, 0 }, #ifdef PIPESEND { "filter", XYFLTR, 0 }, #endif /* PIPESEND */ #ifdef CKXXCHAR { "ignore-character", XYIGN, 0 }, #endif /* CKXXCHAR */ { "i-packets", 993, 0 }, { "move-to", XYMOVE, 0 }, { "negotiation-string-max-length", XYINIL, CM_INV }, { "packet-length", XYLEN, 0 }, { "pad-character", XYPADC, 0 }, { "padding", XYNPAD, 0 }, { "pathnames", XYFPATH, 0 }, { "pause", XYPAUS, 0 }, #ifdef CK_PERMS { "permissions", 994, 0}, /* 206 */ #endif /* CK_PERMS */ { "quote", XYQCTL, CM_INV }, /* = CONTROL-PREFIX */ { "rename-to", XYRENAME, 0 }, { "start-of-packet", XYMARK, 0 }, { "timeout", XYTIMO, 0 }, #ifdef VMS { "version-numbers", 887, 0 }, /* VMS version numbers */ #endif /* VMS */ { "", 0, 0 } }; int nsrtab = (sizeof(srtab) / sizeof(struct keytab)) - 1; #ifdef UNICODE #define UCS_BOM 1 #define UCS_BYT 2 static struct keytab ucstab[] = { { "bom", UCS_BOM, 0 }, { "byte-order", UCS_BYT, 0 }, { "", 0, 0 } }; int nucstab = (sizeof(ucstab) / sizeof(struct keytab)) - 1; static struct keytab botab[] = { { "big-endian", 0, 0 }, { "little-endian", 1, 0 } }; static int nbotab = 2; #endif /* UNICODE */ /* REMOTE SET */ struct keytab rmstab[] = { { "attributes", XYATTR, 0 }, { "block-check", XYCHKT, 0 }, { "file", XYFILE, 0 }, { "incomplete", XYIFD, CM_INV }, /* = REMOTE SET FILE INCOMPLETE */ { "match", XYMATCH,0 }, { "receive", XYRECV, 0 }, { "retry", XYRETR, 0 }, { "server", XYSERV, 0 }, { "transfer", XYXFER, 0 }, { "window", XYWIND, 0 }, { "xfer", XYXFER, CM_INV } }; int nrms = (sizeof(rmstab) / sizeof(struct keytab)); struct keytab attrtab[] = { #ifdef STRATUS { "account", AT_ACCT, 0 }, #endif /* STRATUS */ { "all", AT_XALL, 0 }, #ifdef COMMENT { "blocksize", AT_BLKS, 0 }, /* (not used) */ #endif /* COMMENT */ #ifndef NOCSETS { "character-set", AT_ENCO, 0 }, #endif /* NOCSETS */ #ifdef STRATUS { "creator", AT_CREA, 0 }, #endif /* STRATUS */ { "date", AT_DATE, 0 }, { "disposition", AT_DISP, 0 }, { "encoding", AT_ENCO, CM_INV }, { "format", AT_RECF, CM_INV }, { "length", AT_LENK, 0 }, { "off", AT_ALLN, 0 }, { "on", AT_ALLY, 0 }, #ifdef COMMENT { "os-specific", AT_SYSP, 0 }, /* (not used by UNIX or VMS) */ #endif /* COMMENT */ #ifdef CK_PERMS { "protection", AT_LPRO, 0 }, { "permissions", AT_LPRO, CM_INV }, #endif /* CK_PERMS */ { "record-format", AT_RECF, 0 }, { "system-id", AT_SYSI, 0 }, { "type", AT_FTYP, 0 } }; int natr = (sizeof(attrtab) / sizeof(struct keytab)); /* how many attributes */ #ifdef CKTIDLE struct keytab idlacts[] = { { "exit", IDLE_EXIT, 0 }, { "hangup", IDLE_HANG, 0 }, { "output", IDLE_OUT, 0 }, { "return", IDLE_RET, 0 }, #ifdef TNCODE { "telnet-nop", IDLE_TNOP, 0 }, { "telnet-ayt", IDLE_TAYT, 0 }, #endif /* TNCODE */ { "", 0, 0 } }; int nidlacts = (sizeof(idlacts) / sizeof(struct keytab)) - 1; #endif /* CKTIDLE */ #ifndef NOSPL extern int indef, inecho, insilence, inbufsize, inautodl, inintr; #ifdef CKFLOAT extern CKFLOAT inscale; #endif /* CKFLOAT */ extern char * inpbuf, * inpbp; #ifdef OS2 extern int interm; #endif /* OS2 */ struct keytab inptab[] = { /* SET INPUT parameters */ #ifdef CK_AUTODL { "autodownload", IN_ADL, 0 }, #endif /* CK_AUTODL */ { "buffer-length", IN_BUF, 0 }, { "cancellation", IN_CAN, 0 }, { "case", IN_CAS, 0 }, { "default-timeout", IN_DEF, CM_INV }, /* There is no default timeout */ { "echo", IN_ECH, 0 }, #ifdef OS2 { "pacing", IN_PAC, CM_INV }, #endif /* OS2 */ { "scale-factor", IN_SCA, 0 }, { "silence", IN_SIL, 0 }, #ifdef OS2 { "terminal", IN_TRM, 0 }, #endif /* OS2 */ { "timeout-action", IN_TIM, 0 } }; int ninp = (sizeof(inptab) / sizeof(struct keytab)); struct keytab intimt[] = { /* SET INPUT TIMEOUT parameters */ { "proceed", 0, 0 }, /* 0 = proceed */ { "quit", 1, 0 } /* 1 = quit */ }; struct keytab incast[] = { /* SET INPUT CASE parameters */ { "ignore", 0, 0 }, /* 0 = ignore */ { "observe", 1, 0 } /* 1 = observe */ }; #endif /* NOSPL */ struct keytab nabltab[] = { /* For any command that needs */ { "disabled", 0, 0 }, { "enabled", 1, 0 }, { "off", 0, CM_INV }, /* these keywords... */ { "on", 1, CM_INV } }; int nnabltab = sizeof(nabltab) / sizeof(struct keytab); #ifdef OS2 struct keytab tvctab[] = { /* SET TERM VIDEO-CHANGE */ { "disabled", TVC_DIS, 0 }, { "enabled", TVC_ENA, 0 }, #ifdef NT { "win95-safe", TVC_W95, 0 }, #endif /* NT */ { "", 0, 0 } }; int ntvctab = (sizeof(tvctab) / sizeof(struct keytab)) - 1; struct keytab msktab[] = { /* SET MS-DOS KERMIT compatibilities */ #ifdef COMMENT { "color", MSK_COLOR, 0 }, #endif /* COMMENT */ { "file-renaming", MSK_REN, 0 }, { "keycodes", MSK_KEYS, 0 } }; int nmsk = (sizeof(msktab) / sizeof(struct keytab)); struct keytab scrnupd[] = { /* SET TERMINAL SCREEN-UPDATE */ { "fast", TTU_FAST, 0 }, { "smooth", TTU_SMOOTH, 0 } }; int nscrnupd = (sizeof(scrnupd) / sizeof(struct keytab)); #ifdef PCFONTS /* This definition of the term_font[] table is only for */ /* the OS/2 Full Screen Session and is not used on Windows */ struct keytab term_font[] = { /* SET TERMINAL FONT */ #ifdef COMMENT { "cp111", TTF_111, 0 }, { "cp112", TTF_112, 0 }, { "cp113", TTF_113, 0 }, #endif /* COMMENT */ { "cp437", TTF_437, 0 }, { "cp850", TTF_850, 0 }, #ifdef COMMENT { "cp851", TTF_851, 0 }, #endif /* COMMENT */ { "cp852", TTF_852, 0 }, #ifdef COMMENT { "cp853", TTF_853, 0 }, { "cp860", TTF_860, 0 }, { "cp861", TTF_861, 0 }, #endif /* COMMENT */ { "cp862", TTF_862, 0 }, #ifdef COMMENT { "cp863", TTF_863, 0 }, { "cp864", TTF_864, 0 }, { "cp865", TTF_865, 0 }, #endif /* COMMENT */ { "cp866", TTF_866, 0 }, #ifdef COMMENT { "cp880", TTF_880, 0 }, { "cp881", TTF_881, 0 }, { "cp882", TTF_882, 0 }, { "cp883", TTF_883, 0 }, { "cp884", TTF_884, 0 }, { "cp885", TTF_885, 0 }, #endif /* COMMENT */ { "default",TTF_ROM,0 } }; int ntermfont = (sizeof(term_font) / sizeof(struct keytab)); int tt_font = TTF_ROM; /* Terminal screen font */ #else /* PCFONTS */ #ifdef NT #ifdef KUI struct keytab * term_font = NULL; struct keytab * _term_font = NULL; char * tt_facename = NULL; int ntermfont = 0; int tt_font = 0; int tt_font_size = 0; #endif /* KUI */ #endif /* NT */ #endif /* PCFONTS */ struct keytab anbktab[] = { /* For any command that needs */ { "message", 2, 0 }, /* these keywords... */ { "off", 0, 0 }, { "on", 1, 0 }, { "unsafe-messag0", 99, CM_INV }, { "unsafe-message", 3, CM_INV } }; int nansbk = (sizeof(anbktab) / sizeof(struct keytab)); int win95_popup = 1; #ifdef NT #ifdef KUI int win95lucida = 0; int win95hsl = 1; #else /* KUI */ int win95lucida = 1; int win95hsl = 1; #endif /* KUI */ #else /* NT */ int win95lucida = 0; int win95hsl = 1; #endif /* NT */ #ifdef NT int win95altgr = 0; extern int win95selectbug; extern int win95_8_3; #ifdef COMMENT extern CHAR (*xlr[MAXTCSETS+1][MAXFCSETS+1])(CHAR); extern struct keytab tcstab[]; extern int ntcs; #endif /* COMMENT */ extern int maxow, maxow_usr; owwait; /* Overlapped I/O variables */ #endif /* NT */ #endif /* OS2 */ /* The following routines broken out of doprm() to give compilers a break. */ /* S E T O N -- Parse on/off (default on), set parameter to result */ int seton(prm) int *prm; { int x, y; if ((y = cmkey(onoff,2,"","on",xxstring)) < 0) return(y); if ((x = cmcfm()) < 0) return(x); *prm = y; return(1); } /* S E T O N A U T O -- Parse on/off/auto (default auto) & set result */ struct keytab onoffaut[] = { { "auto", SET_AUTO, 0 }, /* 2 */ { "off", SET_OFF, 0 }, /* 0 */ { "on", SET_ON, 0 } /* 1 */ }; int setonaut(prm) int *prm; { int x, y; if ((y = cmkey(onoffaut,3,"","auto",xxstring)) < 0) return(y); if ((x = cmcfm()) < 0) return(x); *prm = y; return(1); } /* S E T N U M -- Set parameter to result of cmnum() parse. */ /* Call with pointer to integer variable to be set, x = number from cnum parse, y = return code from cmnum, max = maximum value to accept, -1 if no maximum. Returns -9 on failure, after printing a message, or 1 on success. */ int setnum(prm,x,y,max) int x, y, *prm, max; { debug(F101,"setnum","",y); if (y == -3) { printf("\n?Value required\n"); return(-9); } if (y == -2) { printf("%s?Not a number: %s\n",cmflgs == 1 ? "" : "\n", atxbuf); return(-9); } if (y < 0) return(y); if (max > -1 && x > max) { printf("?Sorry, %d is the maximum\n",max); return(-9); } if ((y = cmcfm()) < 0) return(y); *prm = x; return(1); } /* S E T C C -- Set parameter var to an ASCII control character value. */ /* Parses a number, or a literal control character, or a caret (^) followed by an ASCII character whose value is 63-95 or 97-122, then gets confirmation, then sets the parameter to the code value of the character given. If there are any parse errors, they are returned, otherwise on success 1 is returned. */ int setcc(dflt,var) char *dflt; int *var; { int x, y; unsigned int c; char *hlpmsg = "Control character,\n\ numeric ASCII value,\n\ or in ^X notation,\n\ or preceded by a backslash and entered literally"; /* This is a hack to turn off complaints from expression evaluator. */ x_ifnum = 1; y = cmnum(hlpmsg, dflt, 10, &x, xxstring); /* Parse a number */ x_ifnum = 0; /* Allow complaints again */ if (y < 0) { /* Parse failed */ if (y != -2) /* Reparse needed or somesuch */ return(y); /* Pass failure back up the chain */ } /* Real control character or literal 8-bit character... */ for (c = strlen(atmbuf) - 1; c > 0; c--) /* Trim */ if (atmbuf[c] == SP) atmbuf[c] = NUL; if (y < 0) { /* It was not a number */ if (((c = atmbuf[0])) && !atmbuf[1]) { /* Literal character? */ c &= 0xff; if (((c > 31) && (c < 127)) || (c > 255)) { printf("\n?%d: Out of range - must be 0-31 or 127-255\n",c); return(-9); } else { if ((y = cmcfm()) < 0) /* Confirm */ return(y); *var = c; /* Set the variable */ return(1); } } else if (atmbuf[0] == '^' && !atmbuf[2]) { /* Or ^X notation? */ c = atmbuf[1]; if (islower((char) c)) /* Uppercase lowercase letters */ c = toupper(c); if (c > 62 && c < 96) { /* Check range */ if ((y = cmcfm()) < 0) return(y); *var = ctl(c); /* OK */ return(1); } else { printf("?Not a control character - %s\n", atmbuf); return(-9); } } else { /* Something illegal was typed */ printf("?Invalid - %s\n", atmbuf); return(-9); } } if (((x > 31) && (x < 127)) || (x > 255)) { /* They typed a number */ printf("\n?%d: Out of range - must be 0-31 or 127-255\n",x); return(-9); } if ((y = cmcfm()) < 0) /* In range, confirm */ return(y); *var = x; /* Set variable */ return(1); } #ifndef NOSPL /* The SORT command... */ static struct keytab srtswtab[] = { /* SORT command switches */ { "/case", SRT_CAS, CM_ARG }, { "/key", SRT_KEY, CM_ARG }, { "/numeric", SRT_NUM, 0 }, { "/range", SRT_RNG, CM_ARG }, { "/reverse", SRT_REV, 0 } }; static int nsrtswtab = sizeof(srtswtab)/sizeof(struct keytab); extern char **a_ptr[]; /* Array pointers */ extern int a_dim[]; /* Array dimensions */ int dosort() { /* Do the SORT command */ char c, *p = NULL, ** ap, ** xp = NULL; struct FDB sw, fl, cm; int hi, lo; int xn = 0, xr = -1, xk = -1, xc = -1, xs = 0; int getval = 0, range[2], confirmed = 0; cmfdbi(&sw, /* First FDB - command switches */ _CMKEY, /* fcode */ "Array name or switch", "", /* default */ "", /* addtl string data */ nsrtswtab, /* addtl numeric data 1: tbl size */ 4, /* addtl numeric data 2: 4 = cmswi */ NULL, /* Processing function */ srtswtab, /* Keyword table */ &fl /* Pointer to next FDB */ ); cmfdbi(&fl, /* Anything that doesn't match */ _CMFLD, /* fcode */ "Array name", /* hlpmsg */ "", /* default */ "", /* addtl string data */ 0, /* addtl numeric data 1 */ 0, /* addtl numeric data 2 */ NULL, NULL, &cm ); cmfdbi(&cm, /* Or premature confirmation */ _CMCFM, /* fcode */ "", /* hlpmsg */ "", /* default */ "", /* addtl string data */ 0, /* addtl numeric data 1 */ 0, /* addtl numeric data 2 */ NULL, NULL, NULL ); range[0] = -1; range[1] = -1; while (1) { /* Parse 0 or more switches */ x = cmfdb(&sw); if (x < 0) return(x); if (cmresult.fcode != _CMKEY) /* Break out if not a switch */ break; c = cmgbrk(); getval = (c == ':' || c == '='); if (getval && !(cmresult.kflags & CM_ARG)) { printf("?This switch does not take arguments\n"); return(-9); } switch (cmresult.nresult) { case SRT_REV: xr = 1; break; case SRT_KEY: if (getval) { if ((y = cmnum("Column for comparison (1-based)", "1",10,&x,xxstring)) < 0) return(y); xk = x - 1; } else xk = 0; break; case SRT_CAS: if (getval) { if ((y = cmkey(onoff,2,"","on",xxstring)) < 0) return(y); xc = y; } else xc = 1; break; case SRT_RNG: /* /RANGE */ if (getval) { char buf[32]; char buf2[16]; int i; char * p, * q; if ((y = cmfld("low:high element","1",&s,NULL)) < 0) return(y); s = brstrip(s); ckstrncpy(buf,s,32); p = buf; for (i = 0; *p && i < 2; i++) { /* Get low and high */ q = p; /* Start of this piece */ while (*p) { /* Find end of this piece */ if (*p == ':') { *p = NUL; p++; break; } p++; } y = 15; /* Evaluate this piece */ s = buf2; zzstring(q,&s,&y); s = evalx(buf2); if (s) if (*s) ckstrncpy(buf2,s,16); if (!rdigits(buf2)) { printf("?Not numeric: %s\n",buf2); return(-9); } range[i] = atoi(buf2); } } break; case SRT_NUM: /* /NUMERIC */ xn = 1; break; default: return(-2); } } switch (cmresult.fcode) { case _CMCFM: confirmed = 1; break; case _CMFLD: ckstrncpy(line,cmresult.sresult,LINBUFSIZ); /* Safe copy of name */ s = line; break; default: printf("?Unexpected function code: %d\n",cmresult.fcode); return(-9); } if (confirmed) { printf("?Array name required\n"); return(-9); } ckmakmsg(tmpbuf,TMPBUFSIZ, "Second array to sort according to ",s,NULL,NULL); if ((x = cmfld(tmpbuf,"",&p,NULL)) < 0) if (x != -3) return(x); tmpbuf[0] = NUL; ckstrncpy(tmpbuf,p,TMPBUFSIZ); p = tmpbuf; if ((x = cmcfm()) < 0) /* Get confirmation */ return(x); x = arraybounds(s,&lo,&hi); /* Get array index & bounds */ if (x < 0) { /* Check */ printf("?Bad array name: %s\n",s); return(-9); } if (lo > -1) range[0] = lo; /* Set range */ if (hi > -1) range[1] = hi; ap = a_ptr[x]; /* Get pointer to array element list */ if (!ap) { /* Check */ printf("?Array not declared: %s\n", s); return(-9); } if (range[0] < 0) /* Starting element */ range[0] = 1; if (range[1] < 0) /* Final element */ range[1] = a_dim[x]; if (range[1] > a_dim[x]) { printf("?range %d:%d exceeds array dimension %d\n", range[0],range[1],a_dim[x] ); return(-9); } ap += range[0]; xs = range[1] - range[0] + 1; /* Number of elements to sort */ if (xs < 1) { /* Check */ printf("?Bad range: %d:%d\n",range[0],range[1]); return(-9); } if (xk < 0) xk = 0; /* Key position */ if (xr < 0) xr = 0; /* Reverse flag */ if (xn) /* Numeric flag */ xc = 2; else if (xc < 0) /* Not numeric */ xc = inpcas[cmdlvl]; /* so alpha case option */ if (*p) { /* Parallel array given? */ y = xarray(p); /* Yes, get its index. */ if (y < 0) { printf("?Bad array name: %s\n", p); return(-9); } if (y != x) { /* If the 2 arrays are different */ xp = a_ptr[y]; /* Pointer to 2nd array element list */ if (!xp) { printf("?Array not declared: %s\n", p); return(-9); } if (a_dim[y] < range[1]) { printf("?Array %s smaller than %s\n", p, s); return(-9); } xp += range[0]; /* Set base to same as 1st array */ } } sh_sort(ap,xp,xs,xk,xr,xc); /* Sort the array(s) */ return(success = 1); /* Always succeeds */ } #endif /* NOSPL */ static struct keytab purgtab[] = { /* PURGE command switches */ { "/after", PU_AFT, CM_ARG }, { "/ask", PU_ASK, 0 }, { "/before", PU_BEF, CM_ARG }, { "/delete", PU_DELE, CM_INV }, #ifdef UNIXOROSK { "/dotfiles", PU_DOT, 0 }, #endif /* UNIXOROSK */ { "/except", PU_EXC, CM_ARG }, { "/heading", PU_HDG, 0 }, { "/keep", PU_KEEP, CM_ARG }, { "/larger-than", PU_LAR, CM_ARG }, { "/list", PU_LIST, 0 }, { "/log", PU_LIST, CM_INV }, { "/noask", PU_NASK, 0 }, { "/nodelete", PU_NODE, CM_INV }, #ifdef UNIXOROSK { "/nodotfiles", PU_NODOT,0 }, #endif /* UNIXOROSK */ { "/noheading", PU_NOH, 0 }, { "/nol", PU_NOLI, CM_INV|CM_ABR }, { "/nolist", PU_NOLI, 0 }, { "/nolog", PU_NOLI, CM_INV }, #ifdef CK_TTGWSIZ { "/nopage", PU_NOPA, 0 }, #endif /* CK_TTGWSIZ */ { "/not-after", PU_NAF, CM_ARG }, { "/not-before", PU_NBF, CM_ARG }, { "/not-since", PU_NAF, CM_INV|CM_ARG }, #ifdef CK_TTGWSIZ { "/page", PU_PAGE, 0 }, #endif /* CK_TTGWSIZ */ { "/quiet", PU_QUIE, CM_INV }, #ifdef RECURSIVE { "/recursive", PU_RECU, 0 }, #endif /* RECURSIVE */ { "/since", PU_AFT, CM_ARG|CM_INV }, { "/simulate", PU_NODE, 0 }, { "/smaller-than", PU_SMA, CM_ARG }, { "/verbose", PU_VERB, CM_INV } }; static int npurgtab = sizeof(purgtab)/sizeof(struct keytab); int bkupnum(s,i) char * s; int *i; { int k = 0, pos = 0; char * p = NULL, *q; *i = pos; if (!s) s = ""; if (!*s) return(-1); if ((k = strlen(s)) < 5) return(-1); if (s[k-1] != '~') return(-1); pos = k - 2; q = s + pos; while (q >= s && isdigit(*q)) { p = q--; pos--; } if (!p) return(-1); if (q < s+2) return(-1); if (*q != '~' || *(q-1) != '.') return(-1); pos--; *i = pos; debug(F111,"bkupnum",s+pos,pos); return(atoi(p)); } #ifdef CKPURGE /* Presently only for UNIX because we need direct access to the file array. */ /* Not needed for VMS anyway, because we don't make backup files there. */ #define MAXKEEP 32 /* Biggest /KEEP: value */ static int pu_keep = 0, pu_list = 0, pu_dot = 0, pu_ask = 0, pu_hdg = 0; #ifdef CK_TTGWSIZ static int pu_page = -1; #else static int pu_page = 0; #endif /* CK_TTGWSIZ */ #ifndef NOSHOW VOID showpurgopts() { /* SHOW PURGE command options */ int x = 0; extern int optlines; prtopt(&optlines,"PURGE"); if (pu_ask > -1) { x++; prtopt(&optlines, pu_ask ? "/ASK" : "/NOASK"); } #ifdef UNIXOROSK if (pu_dot > -1) { x++; prtopt(&optlines, pu_dot ? "/DOTFILES" : "/NODOTFILES"); } #endif /* UNIXOROSK */ if (pu_keep > -1) { x++; ckmakmsg(tmpbuf,TMPBUFSIZ,"/KEEP:",ckitoa(pu_keep),NULL,NULL); prtopt(&optlines,tmpbuf); } if (pu_list > -1) { x++; prtopt(&optlines, pu_list ? "/LIST" : "/NOLIST"); } if (pu_hdg > -1) { x++; prtopt(&optlines, pu_hdg ? "/HEADING" : "/NOHEADING"); } #ifdef CK_TTGWSIZ if (pu_page > -1) { x++; prtopt(&optlines, pu_page ? "/PAGE" : "/NOPAGE"); } #endif /* CK_TTGWSIZ */ if (!x) prtopt(&optlines,"(no options set)"); prtopt(&optlines,""); } #endif /* NOSHOW */ int setpurgopts() { /* Set PURGE command options */ int c, z, getval = 0; int x_keep = -1, x_list = -1, x_page = -1, x_hdg = -1, x_ask = -1, x_dot = -1; while (1) { if ((y = cmswi(purgtab,npurgtab,"Switch","",xxstring)) < 0) { if (y == -3) break; else return(y); } c = cmgbrk(); if ((getval = (c == ':' || c == '=')) && !(cmgkwflgs() & CM_ARG)) { printf("?This switch does not take an argument\n"); return(-9); } if (!getval && (cmgkwflgs() & CM_ARG)) { printf("?This switch requires an argument\n"); return(-9); } switch (y) { case PU_KEEP: z = 1; if (c == ':' || c == '=') if ((y = cmnum("How many backup files to keep", "1",10,&z,xxstring)) < 0) return(y); if (z < 0 || z > MAXKEEP) { printf("?Please specify a number between 0 and %d\n", MAXKEEP ); return(-9); } x_keep = z; break; case PU_LIST: case PU_VERB: x_list = 1; break; case PU_QUIE: case PU_NOLI: x_list = 0; break; #ifdef CK_TTGWSIZ case PU_PAGE: x_page = 1; break; case PU_NOPA: x_page = 0; break; #endif /* CK_TTGWSIZ */ case PU_HDG: x_hdg = 1; break; case PU_NOH: x_hdg = 0; break; case PU_ASK: x_ask = 1; break; case PU_NASK: x_ask = 0; break; #ifdef UNIXOROSK case PU_DOT: x_dot = 1; break; case PU_NODOT: x_dot = 0; break; #endif /* UNIXOROSK */ default: printf("?This option can not be set\n"); return(-9); } } if ((x = cmcfm()) < 0) /* Get confirmation */ return(x); if (x_keep > -1) /* Set PURGE defaults. */ pu_keep = x_keep; if (x_list > -1) pu_list = x_list; #ifdef CK_TTGWSIZ if (x_page > -1) pu_page = x_page; #endif /* CK_TTGWSIZ */ if (x_hdg > -1) pu_hdg = x_hdg; if (x_ask > -1) pu_ask = x_ask; if (x_dot > -1) pu_dot = x_dot; return(success = 1); } int dopurge() { /* Do the PURGE command */ extern char ** mtchs; extern int xaskmore, cmd_rows, recursive; int simulate = 0, asking = 0; int listing = 0, paging = -1, lines = 0, deleting = 1, errors = 0; struct FDB sw, sf, cm; int g, i, j, k, m = 0, n, x, y, z, done = 0, count = 0, flags = 0; int tokeep = 0, getval = 0, havename = 0, confirmed = 0; int xx[MAXKEEP+1]; /* Array of numbers to keep */ int min = -1; int x_hdg = 0, fs = 0, rc = 0; CK_OFF_T minsize = -1L, maxsize = -1L; char namebuf[CKMAXPATH+4]; char basebuf[CKMAXPATH+4]; char * pu_aft = NULL, * pu_bef = NULL, * pu_naf = NULL, * pu_nbf = NULL, * pu_exc = NULL; char * pxlist[8]; /* Exception list */ if (pu_keep > -1) /* Set PURGE defaults. */ tokeep = pu_keep; if (pu_list > -1) listing = pu_list; #ifdef CK_TTGWSIZ if (pu_page > -1) paging = pu_page; #endif /* CK_TTGWSIZ */ for (i = 0; i <= MAXKEEP; i++) /* Clear this number buffer */ xx[i] = 0; for (i = 0; i < 8; i++) /* Initialize these... */ pxlist[i] = NULL; g_matchdot = matchdot; /* Save these... */ cmfdbi(&sw, /* 1st FDB - PURGE switches */ _CMKEY, /* fcode */ "Filename or switch", /* hlpmsg */ "", /* default */ "", /* addtl string data */ npurgtab, /* addtl numeric data 1: tbl size */ 4, /* addtl numeric data 2: 4 = cmswi */ xxstring, /* Processing function */ purgtab, /* Keyword table */ &sf /* Pointer to next FDB */ ); cmfdbi(&sf, /* 2nd FDB - filespec to purge */ _CMIFI, /* fcode */ "", "", /* default */ "", /* addtl string data */ 0, /* addtl numeric data 1 */ 0, /* addtl numeric data 2 */ xxstring, NULL, &cm ); cmfdbi(&cm, /* Or premature confirmation */ _CMCFM, /* fcode */ "", /* hlpmsg */ "", /* default */ "", /* addtl string data */ 0, /* addtl numeric data 1 */ 0, /* addtl numeric data 2 */ NULL, NULL, NULL ); while (!havename && !confirmed) { x = cmfdb(&sw); /* Parse something */ if (x < 0) { /* Error */ rc = x; goto xpurge; } else if (cmresult.fcode == _CMKEY) { char c; c = cmgbrk(); if ((getval = (c == ':' || c == '=')) && !(cmgkwflgs() & CM_ARG)) { printf("?This switch does not take an argument\n"); rc = -9; goto xpurge; } if (!getval && (cmgkwflgs() & CM_ARG)) { printf("?This switch requires an argument\n"); rc = -9; goto xpurge; } switch (k = cmresult.nresult) { case PU_KEEP: z = 1; if (c == ':' || c == '=') { if ((y = cmnum("How many backup files to keep", "1",10,&z,xxstring)) < 0) { rc = y; goto xpurge; } } if (z < 0 || z > MAXKEEP) { printf("?Please specify a number between 0 and %d\n", MAXKEEP ); rc = -9; goto xpurge; } tokeep = z; break; case PU_LIST: listing = 1; break; case PU_NOLI: listing = 0; break; #ifdef CK_TTGWSIZ case PU_PAGE: paging = 1; break; case PU_NOPA: paging = 0; break; #endif /* CK_TTGWSIZ */ case PU_DELE: deleting = 1; break; case PU_NODE: deleting = 0; simulate = 1; listing = 1; break; case PU_ASK: asking = 1; break; case PU_NASK: asking = 0; break; case PU_AFT: case PU_BEF: case PU_NAF: case PU_NBF: if ((x = cmdate("File-time","",&s,0,xxstring)) < 0) { if (x == -3) { printf("?Date-time required\n"); rc = -9; } else rc = x; goto xpurge; } fs++; switch (k) { case PU_AFT: makestr(&pu_aft,s); break; case PU_BEF: makestr(&pu_bef,s); break; case PU_NAF: makestr(&pu_naf,s); break; case PU_NBF: makestr(&pu_nbf,s); break; } break; case PU_SMA: case PU_LAR: if ((x = cmnum("File size in bytes","0",10,&y,xxstring)) < 0) { rc = x; goto xpurge; } fs++; switch (cmresult.nresult) { case PU_SMA: minsize = y; break; case PU_LAR: maxsize = y; break; } break; case PU_DOT: matchdot = 1; break; case PU_NODOT: matchdot = 0; break; case PU_EXC: if ((x = cmfld("Pattern","",&s,xxstring)) < 0) { if (x == -3) { printf("?Pattern required\n"); rc = -9; } else rc = x; goto xpurge; } fs++; makestr(&pu_exc,s); break; case PU_HDG: x_hdg = 1; break; #ifdef RECURSIVE case PU_RECU: /* /RECURSIVE */ recursive = 2; break; #endif /* RECURSIVE */ default: printf("?Not implemented yet - \"%s\"\n",atmbuf); rc = -9; goto xpurge; } } else if (cmresult.fcode == _CMIFI) { havename = 1; } else if (cmresult.fcode == _CMCFM) { confirmed = 1; } else { rc = -2; goto xpurge; } } if (havename) { #ifdef CKREGEX ckmakmsg(line,LINBUFSIZ,cmresult.sresult,".~[1-9]*~",NULL,NULL); #else ckmakmsg(line,LINBUFSIZ,cmresult.sresult,".~*~",NULL,NULL); #endif /* CKREGEX */ } else { #ifdef CKREGEX ckstrncpy(line,"*.~[1-9]*~",LINBUFSIZ); #else ckstrncpy(line,"*.~*~",LINBUFSIZ); #endif /* CKREGEX */ } if (!confirmed) { if ((x = cmcfm()) < 0) { rc = x; goto xpurge; } } /* Parse finished - now action */ #ifdef CK_LOGIN if (isguest) { printf("?File deletion by guests not permitted.\n"); rc = -9; goto xpurge; } #endif /* CK_LOGIN */ #ifdef CK_TTGWSIZ if (paging < 0) /* /[NO]PAGE not given */ paging = xaskmore; /* so use prevailing */ #endif /* CK_TTGWSIZ */ lines = 0; if (x_hdg > 0) { printf("Purging %s, keeping %d...%s\n", s, tokeep, simulate ? " (SIMULATION)" : ""); lines += 2; } flags = ZX_FILONLY; if (recursive) flags |= ZX_RECURSE; n = nzxpand(line,flags); /* Get list of backup files */ if (tokeep < 1) { /* Deleting all of them... */ for (i = 0; i < n; i++) { if (fs) if (fileselect(mtchs[i], pu_aft,pu_bef,pu_naf,pu_nbf, minsize,maxsize,0,8,pxlist) < 1) { if (listing > 0) { printf(" %s (SKIPPED)\n",mtchs[i]); #ifdef CK_TTGWSIZ if (paging) if (++lines > cmd_rows - 3) { if (!askmore()) goto xpurge; else lines = 0; } #endif /* CK_TTGWSIZ */ } continue; } if (asking) { int x; ckmakmsg(tmpbuf,TMPBUFSIZ," Delete ",mtchs[i],"?",NULL); x = getyesno(tmpbuf,1); switch (x) { case 0: continue; case 1: break; case 2: goto xpurge; } } x = deleting ? zdelet(mtchs[i]) : 0; if (x > -1) { if (listing) printf(" %s (%s)\n", mtchs[i],deleting ? "OK" : "SELECTED"); count++; } else { errors++; if (listing) printf(" %s (FAILED)\n", mtchs[i]); } #ifdef CK_TTGWSIZ if (listing && paging) if (++lines > cmd_rows - 3) { if (!askmore()) goto xpurge; else lines = 0; } #endif /* CK_TTGWSIZ */ } goto xpurge; } if (n < tokeep) { /* Not deleting any */ count = 0; if (listing) printf(" Matches = %d: Not enough to purge.\n",n); goto xpurge; } /* General case - delete some but not others */ sh_sort(mtchs,NULL,n,0,0,filecase); /* Alphabetize the list (ESSENTIAL) */ g = 0; /* Start of current group */ for (i = 0; i < n; i++) { /* Go thru sorted file list */ x = znext(namebuf); /* Get next file */ if (x < 1 || !namebuf[0] || i == n - 1) /* No more? */ done = 1; /* NOTE: 'done' must be 0 or 1 only */ if (fs) if (fileselect(namebuf, pu_aft,pu_bef,pu_naf,pu_nbf, minsize,maxsize,0,8,pxlist) < 1) { if (listing > 0) { printf(" %s (SKIPPED)\n",namebuf); if (++lines > cmd_rows - 3) if (!askmore()) goto xpurge; else lines = 0; } continue; } if (x > 0) if ((m = bkupnum(namebuf,&z)) < 0) /* This file's backup number. */ continue; for (j = 0; j < tokeep; j++) { /* Insert in list. */ if (m > xx[j]) { for (k = tokeep - 1; k > j; k--) xx[k] = xx[k-1]; xx[j] = m; break; } } /* New group? */ if (done || (i > 0 && ckstrcmp(namebuf,basebuf,z,1))) { if (i + done - g > tokeep) { /* Do we have enough to purge? */ min = xx[tokeep-1]; /* Yes, lowest backup number to keep */ debug(F111,"dopurge group",basebuf,min); for (j = g; j < i + done; j++) { /* Go through this group */ x = bkupnum(mtchs[j],&z); /* Get file backup number */ if (x > 0 && x < min) { /* Below minimum? */ x = deleting ? zdelet(mtchs[j]) : 0; if (x < 0) errors++; if (listing) printf(" %s (%s)\n", mtchs[j], ((x < 0) ? "ERROR" : (deleting ? "DELETED" : "SELECTED")) ); count++; } else if (listing) /* Not below minimum - keep this one */ printf(" %s (KEPT)\n",mtchs[j]); #ifdef CK_TTGWSIZ if (listing && paging) if (++lines > cmd_rows - 3) { if (!askmore()) goto xpurge; else lines = 0; } #endif /* CK_TTGWSIZ */ } } else if (listing && paging) { /* Not enough to purge */ printf(" %s.~*~ (KEPT)\n",basebuf); #ifdef CK_TTGWSIZ if (++lines > cmd_rows - 3) { if (!askmore()) goto xpurge; else lines = 0; } #endif /* CK_TTGWSIZ */ } for (j = 0; j < tokeep; j++) /* Clear the backup number list */ xx[j] = 0; g = i; /* Reset the group pointer */ } if (done) /* No more files, done. */ break; strncpy(basebuf,namebuf,z); /* Set basename of this file */ basebuf[z] = NUL; } xpurge: /* Common exit point */ if (g_matchdot > -1) { matchdot = g_matchdot; /* Restore these... */ g_matchdot = -1; } if (rc < 0) return(rc); /* Parse error */ if (x_hdg) printf("Files purged: %d%s\n", count, deleting ? "" : " (not really)" ); return(success = count > 0 ? 1 : (errors > 0) ? 0 : 1); } #endif /* CKPURGE */ #ifndef NOXFER #ifndef NOLOCAL int doxdis(which) int which; { /* 1 = Kermit, 2 = FTP */ extern int nolocal; int x, y = 0, z; #ifdef NEWFTP extern int ftp_dis; #endif /* NEWFTP */ #ifdef COMMENT char *s; #endif /* COMMENT */ if ((x = cmkey(fdtab,nfdtab,"file transfer display style","", xxstring)) < 0) return(x); #ifdef CK_PCT_BAR if ((y = cmkey(fdftab,2,"","thermometer",xxstring)) < 0) return(y); #endif /* CK_PCT_BAR */ if ((z = cmcfm()) < 0) return(z); #ifdef CK_CURSES if (x == XYFD_C) { /* FULLSCREEN */ #ifdef COMMENT #ifndef MYCURSES extern char * trmbuf; /* Real curses */ int z; #endif /* MYCURSES */ #endif /* COMMENT */ if (nolocal) /* Nothing to do in this case */ return(success = 1); #ifdef COMMENT #ifndef MYCURSES #ifndef VMS s = getenv("TERM"); debug(F110,"doxdis TERM",s,0); if (!s) s = ""; fxdinit(x); if (*s && trmbuf) { /* Don't call tgetent */ z = tgetent(trmbuf,s); /* if trmbuf not allocated */ debug(F111,"doxdis tgetent",s,z); } else { z = 0; debug(F110,"doxdis tgetent skipped",s,0); } if (z < 1) { printf("Sorry, terminal type unknown: \"%s\"\n",s); return(success = 0); } #endif /* VMS */ #endif /* MYCURSES */ #else fxdinit(x); #endif /* COMMENT */ #ifdef CK_PCT_BAR thermometer = y; #endif /* CK_PCT_BAR */ line[0] = '\0'; /* (What's this for?) */ } #endif /* CK_CURSES */ if (which == 1) /* It's OK. */ fdispla = x; #ifdef NEWFTP else if (which == 2) ftp_dis = x; #endif /* NEWFTP */ return(success = 1); } #endif /* NOLOCAL */ #endif /* NOXFER */ int setfil(rmsflg) int rmsflg; { #ifdef COMMENT extern int en_del; #endif /* COMMENT */ #ifndef NOXFER if (rmsflg) { if ((y = cmkey(rfiltab,nrfilp,"Remote file parameter","", xxstring)) < 0) { if (y == -3) { printf("?Remote file parameter required\n"); return(-9); } else return(y); } } else { #endif /* NOXFER */ if ((y = cmkey(filtab,nfilp,"File parameter","",xxstring)) < 0) return(y); #ifndef NOXFER } #endif /* NOXFER */ switch (y) { #ifdef COMMENT /* Not needed */ case XYFILB: /* Blocksize */ if ((y = cmnum("file block size",ckitoa(DBLKSIZ),10,&z,xxstring)) < 0) return(y); if ((x = cmcfm()) < 0) return(x); if (rmsflg) { sstate = setgen('S', "311", ckitoa(z), ""); return((int) sstate); } else { fblksiz = z; return(success = 1); } #endif /* COMMENT */ #ifndef NOXFER case XYFILS: /* Byte size */ if ((y = cmnum("file byte size (7 or 8)","8",10,&z,xxstring)) < 0) return(y); if (z != 7 && z != 8) { printf("\n?The choices are 7 and 8\n"); return(0); } if ((y = cmcfm()) < 0) return(y); if (z == 7) fmask = 0177; else if (z == 8) fmask = 0377; return(success = 1); #ifndef NOCSETS case XYFILC: { /* Character set */ char * csetname = NULL; extern int r_cset, s_cset, afcset[]; /* SEND CHARACTER-SET AUTO or MANUAL */ struct FDB kw, fl; cmfdbi(&kw, /* First FDB - command switches */ _CMKEY, /* fcode */ rmsflg ? "server character-set name" : "", /* help */ "", /* default */ "", /* addtl string data */ nfilc, /* addtl numeric data 1: tbl size */ 0, /* addtl numeric data 2: 0 = keyword */ xxstring, /* Processing function */ fcstab, /* Keyword table */ rmsflg ? &fl : NULL /* Pointer to next FDB */ ); cmfdbi(&fl, /* Anything that doesn't match */ _CMFLD, /* fcode */ "", /* hlpmsg */ "", /* default */ "", /* addtl string data */ 0, /* addtl numeric data 1 */ 0, /* addtl numeric data 2 */ xxstring, NULL, NULL ); if ((x = cmfdb(&kw)) < 0) return(x); if (cmresult.fcode == _CMKEY) { x = cmresult.nresult; csetname = fcsinfo[x].keyword; } else { ckstrncpy(line,cmresult.sresult,LINBUFSIZ); csetname = line; } if ((z = cmcfm()) < 0) return(z); if (rmsflg) { sstate = setgen('S', "320", csetname, ""); return((int) sstate); } fcharset = x; if (s_cset == XMODE_A) /* If SEND CHARACTER-SET is AUTO */ if (x > -1 && x <= MAXFCSETS) if (afcset[x] > -1 && afcset[x] <= MAXTCSETS) tcharset = afcset[x]; /* Pick corresponding xfer charset */ setxlatype(tcharset,fcharset); /* Translation type */ /* If I say SET FILE CHARACTER-SET blah, I want to be blah! */ r_cset = XMODE_M; /* Don't switch incoming set! */ x = fcsinfo[fcharset].size; /* Also set default x-bit charset */ if (x == 128) /* 7-bit... */ dcset7 = fcharset; else if (x == 256) /* 8-bit... */ dcset8 = fcharset; return(success = 1); } #endif /* NOCSETS */ #ifndef NOLOCAL case XYFILD: /* Display */ return(doxdis(1)); /* 1 == kermit */ #endif /* NOLOCAL */ #endif /* NOXFER */ case XYFILA: /* End-of-line */ #ifdef NLCHAR s = ""; if (NLCHAR == 015) s = "cr"; else if (NLCHAR == 012) s = "lf"; if ((x = cmkey(eoltab, neoltab, "local text-file line terminator",s,xxstring)) < 0) return(x); #else if ((x = cmkey(eoltab, neoltab, "local text-file line terminator","crlf",xxstring)) < 0) return(x); #endif /* NLCHAR */ if ((z = cmcfm()) < 0) return(z); feol = (CHAR) x; return(success = 1); #ifndef NOXFER case XYFILN: /* Names */ if ((x = cmkey(fntab,nfntab,"how to handle filenames","converted", xxstring)) < 0) return(x); if ((z = cmcfm()) < 0) return(z); if (rmsflg) { sstate = setgen('S', "301", ckitoa(1 - x), ""); return((int) sstate); } else { ptab[protocol].fncn = x; /* Set structure */ fncnv = x; /* Set variable */ f_save = x; /* And set "permanent" variable */ return(success = 1); } case XYFILR: /* Record length */ if ((y = cmnum("file record length", ckitoa(DLRECL),10,&z,xxstring)) < 0) return(y); if ((x = cmcfm()) < 0) return(x); if (rmsflg) { sstate = setgen('S', "312", ckitoa(z), ""); return((int) sstate); } else { frecl = z; return(success = 1); } #ifdef COMMENT case XYFILO: /* Organization */ if ((x = cmkey(forgtab,nforg,"file organization","sequential", xxstring)) < 0) return(x); if ((y = cmcfm()) < 0) return(y); if (rmsflg) { sstate = setgen('S', "314", ckitoa(x), ""); return((int) sstate); } else { forg = x; return(success = 1); } #endif /* COMMENT */ #ifdef COMMENT /* Not needed */ case XYFILF: /* Format */ if ((x = cmkey(frectab,nfrec,"file record format","stream", xxstring)) < 0) return(x); if ((y = cmcfm()) < 0) return(y); if (rmsflg) { sstate = setgen('S', "313", ckitoa(x), ""); return((int) sstate); } else { frecfm = x; return(success = 1); } #endif /* COMMENT */ #ifdef COMMENT case XYFILP: /* Printer carriage control */ if ((x = cmkey(fcctab,nfcc,"file carriage control","newline", xxstring)) < 0) return(x); if ((y = cmcfm()) < 0) return(y); if (rmsflg) { sstate = setgen('S', "315", ckitoa(x), ""); return((int) sstate); } else { fcctrl = x; return(success = 1); } #endif /* COMMENT */ #endif /* NOXFER */ case XYFILT: /* Type */ if ((x = cmkey(rmsflg ? rfttab : fttab, rmsflg ? nrfttyp : nfttyp, "type of file transfer","text",xxstring)) < 0) return(x); #ifdef VMS /* Allow VMS users to choose record format for binary files */ if ((x == XYFT_B) && (rmsflg == 0)) { if ((x = cmkey(fbtab,nfbtyp,"VMS record format","fixed", xxstring)) < 0) return(x); } #endif /* VMS */ if ((y = cmcfm()) < 0) return(y); binary = x; b_save = x; #ifdef MAC (void) mac_setfildflg(binary); #endif /* MAC */ #ifndef NOXFER if (rmsflg) { /* Allow for LABELED in VMS & OS/2 */ sstate = setgen('S', "300", ckitoa(x), ""); return((int) sstate); } else { #endif /* NOXFER */ return(success = 1); #ifndef NOXFER } #endif /* NOXFER */ #ifndef NOXFER case XYFILX: /* Collision Action */ if ((x = cmkey(colxtab,ncolx,"Filename collision action","backup", xxstring)) < 0) return(x); if ((y = cmcfm()) < 0) return(y); #ifdef CK_LOGIN if (isguest) { /* Don't let guests change existing files */ printf("?This command not valid for guests\n"); return(-9); } #endif /* CK_LOGIN */ #ifdef COMMENT /* Not appropriate - DISABLE DELETE only refers to server */ if ((x == XYFX_X || x == XYFX_B || x == XYFX_U || x == XYFX_A) && (!ENABLED(en_del))) { printf("?Sorry, file deletion is disabled.\n"); return(-9); } #endif /* COMMENT */ fncact = x; ptab[protocol].fnca = x; if (rmsflg) { sstate = setgen('S', "302", ckitoa(fncact), ""); return((int) sstate); } else { if (fncact == XYFX_R) ckwarn = 1; /* FILE WARNING implications */ if (fncact == XYFX_X) ckwarn = 0; /* ... */ return(success = 1); } case XYFILW: /* Warning/Write-Protect */ if ((x = seton(&ckwarn)) < 0) return(x); if (ckwarn) fncact = XYFX_R; else fncact = XYFX_X; return(success = 1); #ifdef CK_LABELED case XYFILL: /* LABELED FILE parameters */ if ((x = cmkey(lbltab,nlblp,"Labeled file feature","", xxstring)) < 0) return(x); if ((success = seton(&y)) < 0) return(success); if (y) /* Set or reset the selected bit */ lf_opts |= x; /* in the options bitmask. */ else lf_opts &= ~x; return(success); #endif /* CK_LABELED */ case XYFILI: { /* INCOMPLETE */ extern struct keytab ifdatab[]; extern int keep; if ((y = cmkey(ifdatab,3,"","auto",xxstring)) < 0) return(y); if ((x = cmcfm()) < 0) return(x); if (rmsflg) { sstate = setgen('S', "310", y == 0 ? "0" : (y == 1 ? "1" : "2"), "" ); return((int) sstate); } else { keep = y; return(success = 1); } } #ifdef CK_TMPDIR case XYFILG: { /* Download directory */ int x; char *s; #ifdef ZFNQFP struct zfnfp * fnp; #endif /* ZFNQFP */ #ifdef MAC char temp[34]; #endif /* MAC */ #ifdef GEMDOS if ((x = cmdir("Name of local directory, or carriage return", "",&s, NULL)) < 0 ) { if (x != -3) return(x); } #else #ifdef OS2 if ((x = cmdir("Name of PC disk and/or directory,\n\ or press the Enter key to use current directory", "",&s,xxstring)) < 0 ) { if (x != -3) return(x); } #else #ifdef MAC x = ckstrncpy(temp,zhome(),32); if (x > 0) if (temp[x-1] != ':') { temp[x] = ':'; temp[x+1] = NUL; } if ((x = cmtxt("Name of Macintosh volume and/or folder,\n\ or press the Return key for the desktop on the boot disk", temp,&s, xxstring)) < 0 ) return(x); #else if ((x = cmdir("Name of local directory, or carriage return", "", &s, xxstring)) < 0 ) { if (x != -3) return(x); } #endif /* MAC */ #endif /* OS2 */ #endif /* GEMDOS */ debug(F110,"download dir",s,0); #ifndef MAC if (x == 2) { printf("?Wildcards not allowed in directory name\n"); return(-9); } #endif /* MAC */ #ifdef ZFNQFP if ((fnp = zfnqfp(s,TMPBUFSIZ - 1,tmpbuf))) { if (fnp->fpath) if ((int) strlen(fnp->fpath) > 0) s = fnp->fpath; } debug(F110,"download zfnqfp",s,0); #endif /* ZFNQFP */ ckstrncpy(line,s,LINBUFSIZ); /* Make a safe copy */ #ifndef MAC if ((x = cmcfm()) < 0) /* Get confirmation */ return(x); #endif /* MAC */ #ifdef CK_LOGIN if (isguest) { /* Don't let guests change existing files */ printf("?This command not valid for guests\n"); return(-9); } #endif /* CK_LOGIN */ x = strlen(s); if (x) { #ifdef datageneral /* AOS/VS */ if (s[x-1] == ':') /* homdir ends in colon, */ s[x-1] = NUL; /* and "dir" doesn't like that... */ #else #ifdef OS2ORUNIX /* Unix or K-95... */ if ((x < (LINBUFSIZ - 2)) && /* Add trailing dirsep */ (s[x-1] != '/')) { /* if none present. */ s[x] = '/'; /* Note that Windows path has */ s[x+1] = NUL; /* been canonicalized to forward */ } /* slashes at this point. */ #endif /* OS2ORUNIX */ #endif /* datageneral */ makestr(&dldir,s); } else makestr(&dldir,NULL); /* dldir is NULL when not assigned */ return(success = 1); } #endif /* CK_TMPDIR */ case XYFILY: return(setdest()); #endif /* NOXFER */ #ifdef CK_CTRLZ case XYFILV: { /* EOF */ extern int eofmethod; if ((x = cmkey(eoftab,3,"end-of-file detection method","", xxstring)) < 0) return(x); if ((y = cmcfm()) < 0) return(y); eofmethod = x; return(success = 1); } #endif /* CK_CTRLZ */ #ifndef NOXFER #ifdef UNIX case XYFILH: { /* OUTPUT */ extern int zofbuffer, zobufsize, zofblock; #ifdef DYNAMIC extern char * zoutbuffer; #endif /* DYNAMIC */ if ((x = cmkey(zoftab,nzoftab,"output file writing method","", xxstring)) < 0) return(x); if (x == ZOF_BUF || x == ZOF_NBUF) { if ((y = cmnum("output buffer size","32768",10,&z,xxstring)) < 0) return(y); if (z < 1) { printf("?Bad size - %d\n", z); return(-9); } } if ((y = cmcfm()) < 0) return(y); switch (x) { case ZOF_BUF: case ZOF_NBUF: zofbuffer = (x == ZOF_BUF); zobufsize = z; break; case ZOF_BLK: case ZOF_NBLK: zofblock = (x == ZOF_BLK); break; } #ifdef DYNAMIC if (zoutbuffer) free(zoutbuffer); if (!(zoutbuffer = (char *)malloc(z))) { printf("MEMORY ALLOCATION ERROR - FATAL\n"); doexit(BAD_EXIT,-1); } else zobufsize = z; #else if (z <= OBUFSIZE) { zobufsize = z; } else { printf("?Sorry, %d is too big - %d is the maximum\n",z,OBUFSIZE); return(-9); } #endif /* DYNAMIC */ return(success = 1); } #endif /* UNIX */ #ifdef PATTERNS case XYFIBP: /* BINARY-PATTERN */ case XYFITP: { /* TEXT-PATTERN */ char * tmp[FTPATTERNS]; int i, n = 0; while (n < FTPATTERNS) { tmp[n] = NULL; if ((x = cmfld("Pattern","",&s,xxstring)) < 0) break; ckstrncpy(line,s,LINBUFSIZ); s = brstrip(line); makestr(&(tmp[n++]),s); } if (x == -3) x = cmcfm(); for (i = 0; i <= n; i++) { if (x > -1) { if (y == XYFIBP) makestr(&(binpatterns[i]),tmp[i]); else makestr(&(txtpatterns[i]),tmp[i]); } free(tmp[i]); } if (y == XYFIBP) /* Null-terminate the list */ makestr(&(binpatterns[i]),NULL); else makestr(&(txtpatterns[i]),NULL); return(x); } case XYFIPA: /* PATTERNS */ if ((x = setonaut(&patterns)) < 0) return(x); return(success = 1); #endif /* PATTERNS */ #endif /* NOXFER */ #ifdef UNICODE case XYFILU: { /* UCS */ extern int ucsorder, ucsbom, byteorder; if ((x = cmkey(ucstab,nucstab,"","",xxstring)) < 0) return(x); switch (x) { case UCS_BYT: if ((y = cmkey(botab,nbotab, "Byte order", byteorder ? "little-endian" : "big-endian", xxstring ) ) < 0) return(y); if ((x = cmcfm()) < 0) return(x); ucsorder = y; return(success = 1); case UCS_BOM: if ((y = cmkey(onoff,2,"","on",xxstring)) < 0) return(y); if ((x = cmcfm()) < 0) return(x); ucsbom = y; return(success = 1); default: return(-2); } } #endif /* UNICODE */ #ifndef datageneral case XYF_INSP: { /* SCAN (INSPECTION) */ extern int filepeek, nscanfile; if ((x = cmkey(onoff,2,"","on",xxstring)) < 0) return(x); if (y) { if ((y = cmnum("How much to scan",ckitoa(SCANFILEBUF), 10,&z,xxstring)) < 0) return(y); } if ((y = cmcfm()) < 0) return(y); #ifdef VMS filepeek = 0; nscanfile = 0; return(success = 0); #else filepeek = x; nscanfile = z; return(success = 1); #endif /* VMS */ } #endif /* datageneral */ case XYF_DFLT: y = 0; #ifndef NOCSETS if ((y = cmkey(fdfltab,nfdflt,"","",xxstring)) < 0) return(y); if (y == 7 || y == 8) { if (y == 7) s = fcsinfo[dcset7].keyword; else s = fcsinfo[dcset8].keyword; if ((x = cmkey(fcstab,nfilc,"character-set",s,xxstring)) < 0) return(x); } ckstrncpy(line,fcsinfo[x].keyword,LINBUFSIZ); s = line; #endif /* NOCSETS */ if ((z = cmcfm()) < 0) return(z); switch (y) { #ifndef NOCSETS case 7: if (fcsinfo[x].size != 128) { printf("%s - Not a 7-bit set\n",s); return(-9); } dcset7 = x; break; case 8: if (fcsinfo[x].size != 256) { printf("%s - Not an 8-bit set\n",s); return(-9); } dcset8 = x; break; #endif /* NOCSETS */ default: return(-2); } return(success = 1); #ifndef NOXFER case 9997: /* FASTLOOKUPS */ return(success = seton(&stathack)); #endif /* NOXFER */ #ifdef UNIX #ifdef DYNAMIC case XYF_LSIZ: { /* LISTSIZE */ int zz; y = cmnum("Maximum number of filenames","",10,&x,xxstring); if ((x = setnum(&zz,x,y,-1)) < 0) return(x); if (zsetfil(zz,3) < 0) { printf("?Memory allocation failure\n"); return(-9); } return(success = 1); } case XYF_SSPA: { /* STRINGSPACE */ int zz; y = cmnum("Number of characters for filename list", "",10,&x,xxstring); if ((x = setnum(&zz,x,y,-1)) < 0) return(x); if (zsetfil(zz,1) < 0) { printf("?Memory allocation failure\n"); return(-9); } return(success = 1); } #endif /* DYNAMIC */ #endif /* UNIX */ default: printf("?unexpected file parameter\n"); return(-2); } } #ifdef UNIX #ifndef NOPUTENV #ifdef BIGBUFOK #define NPUTENVS 4096 #else #define NPUTENVS 128 #endif /* BIGBUFOK */ /* environment variables must be static, not automatic */ static char * putenvs[NPUTENVS]; /* Array of environment var strings */ static int nputenvs = -1; /* Pointer into array */ /* If anyone ever notices the limitation on the number of PUTENVs, the list can be made dynamic, we can recycle entries with the same name, etc. */ int doputenv(s1, s2) char * s1; char * s2; { char * s, * t = tmpbuf; /* Create or alter environment var */ if (nputenvs == -1) { /* Table not used yet */ int i; /* Initialize the pointers */ for (i = 0; i < NPUTENVS; i++) putenvs[i] = NULL; nputenvs = 0; } if (!s1) return(1); /* Nothing to do */ if (!*s1) return(1); /* ditto */ if (ckindex("=",s1,0,0,0)) { /* Does the name contain an '='? */ printf( /* putenv() does not allow this. */ /* This also catches the 'putenv name=value' case */ "?PUTENV - Equal sign in variable name - 'help putenv' for info.\n"); return(-9); } nputenvs++; /* Point to next free string */ debug(F111,"doputenv s1",s1,nputenvs); debug(F111,"doputenv s2",s2,nputenvs); if (nputenvs > NPUTENVS - 1) { /* Notice the end */ printf("?PUTENV - static buffer space exhausted\n"); return(-9); } /* Quotes are not needed but we allow them for familiarity */ /* but then we strip them, so syntax is same as for Unix shell */ if (s2) { s2 = brstrip(s2); } else { s2 = (char *)""; } ckmakmsg(t,TMPBUFSIZ,s1,"=",s2,NULL); debug(F111,"doputenv",t,nputenvs); (VOID) makestr(&(putenvs[nputenvs]),t); /* Make a safe permananent copy */ if (!putenvs[nputenvs]) { printf("?PUTENV - memory allocation failure\n"); return(-9); } if (putenv(putenvs[nputenvs])) { printf("?PUTENV - %s\n",ck_errstr()); return(-9); } else return(success = 1); } #endif /* NOPUTENV */ #endif /* UNIX */ int settrmtyp() { #ifdef OS2 #ifdef TNCODE extern int ttnum; /* Last Telnet Terminal Type sent */ extern int ttnumend; /* Has end of list been found */ #endif /* TNCODE */ if ((x = cmkey(ttyptab,nttyp,"","vt220",xxstring)) < 0) return(x); if ((y = cmcfm()) < 0) return(y); settermtype(x,1); #ifdef TNCODE /* So we send the correct terminal name to the host if it asks for it */ ttnum = -1; /* Last Telnet Terminal Type sent */ ttnumend = 0; /* end of list not found */ #endif /* TNCODE */ return(success = 1); #else /* Not OS2 */ #ifdef UNIX extern int fxd_inited; x = cmtxt("Terminal type name, case sensitive","",&s,NULL); #ifdef NOPUTENV success = 1; #else success = doputenv("TERM",s); /* Set the TERM variable */ #ifdef CK_CURSES fxd_inited = 0; /* Force reinitialization of curses database */ (void)doxdis(0); /* Re-initialize file transfer display */ concb((char)escape); /* Fix command terminal */ #endif /* CK_CURSES */ #endif /* NOPUTENV */ return(success); #else printf( "\n Sorry, this version of C-Kermit does not support the SET TERMINAL TYPE\n"); printf( " command. Type \"help set terminal\" for further information.\n"); return(success = 0); #endif /* UNIX */ #endif /* OS2 */ } #ifndef NOLOCAL #ifdef OS2 /* MS-DOS KERMIT compatibility modes */ int setmsk() { if ((y = cmkey(msktab,nmsk,"MS-DOS Kermit compatibility mode", "keycodes",xxstring)) < 0) return(y); switch ( y ) { #ifdef COMMENT case MSK_COLOR: return(seton(&mskcolors)); #endif /* COMMENT */ case MSK_KEYS: return(seton(&mskkeys)); case MSK_REN: return(seton(&mskrename)); default: /* Shouldn't get here. */ return(-2); } } #endif /* OS2 */ #ifdef CKTIDLE static char iactbuf[132]; char * getiact() { switch (tt_idleact) { case IDLE_RET: return("return"); case IDLE_EXIT: return("exit"); case IDLE_HANG: return("hangup"); #ifdef TNCODE case IDLE_TNOP: return("Telnet NOP"); case IDLE_TAYT: return("Telnet AYT"); #endif /* TNCODE */ case IDLE_OUT: { int c, k, n; char * p, * q, * t; k = ckstrncpy(iactbuf,"output ",132); n = k; q = &iactbuf[k]; p = tt_idlestr; if (!p) p = ""; if (!*p) return("output NUL"); while ((c = *p++) && n < 131) { c &= 0xff; if (c == '\\') { if (n > 130) break; *q++ = '\\'; *q++ = '\\'; *q = NUL; n += 2; } else if ((c > 32 && c < 127) || c > 159) { *q++ = c; *q = NUL; n++; } else { if (n > (131 - 6)) break; sprintf(q,"\\{%d}",c); k = strlen(q); q += k; n += k; *q = NUL; } } *q = NUL; #ifdef OS2 k = tt_cols[VTERM]; #else k = tt_cols; #endif /* OS2 */ if (n > k - 52) { n = k - 52; iactbuf[n-2] = '.'; iactbuf[n-1] = '.'; iactbuf[n] = NUL; } return(iactbuf); } default: return("unknown"); } } #endif /* CKTIDLE */ #ifndef NOCSETS VOID setlclcharset(x) int x; { int i; tcsl = y; /* Local character set */ #ifdef OS2 for (i = 0; i < 4; i++) { G[i].init = TRUE; x = G[i].designation; G[i].c1 = (x != tcsl) && cs_is_std(x); x = G[i].def_designation; G[i].def_c1 = (x != tcsl) && cs_is_std(x); } #endif /* OS2 */ } VOID setremcharset(x, z) int x, z; { int i; #ifdef KUI KuiSetProperty( KUI_TERM_REMCHARSET, (long) x, (long) z ) ; #endif /* KUI */ #ifdef UNICODE if (x == TX_TRANSP) #else /* UNICODE */ if (x == FC_TRANSP) #endif /* UNICODE */ { /* TRANSPARENT? */ #ifndef OS2 tcsr = tcsl; /* Make both sets the same */ #else /* OS2 */ #ifdef CKOUNI tt_utf8 = 0; /* Turn off UTF8 flag */ tcsr = tcsl = dec_kbd = TX_TRANSP; /* No translation */ tcs_transp = 1; if (!cs_is_nrc(tcsl)) { G[0].def_designation = G[0].designation = TX_ASCII; G[0].init = TRUE; G[0].def_c1 = G[0].c1 = FALSE; G[0].size = cs94; G[0].national = FALSE; } for (i = cs_is_nrc(tcsl) ? 0 : 1; i < 4; i++) { G[i].def_designation = G[i].designation = tcsl; G[i].init = TRUE; G[i].def_c1 = G[i].c1 = FALSE; switch (cs_size(G[i].designation)) { /* 94, 96, or 128 */ case 128: case 96: G[i].size = G[i].def_size = cs96; break; case 94: G[i].size = G[i].def_size = cs94; break; default: G[i].size = G[i].def_size = csmb; break; } G[i].national = cs_is_nrc(x); } #else /* CKOUNI */ tcsr = tcsl; /* Make both sets the same */ for (i = 0; i < 4; i++) { G[i].def_designation = G[i].designation = FC_TRANSP; G[i].init = FALSE; G[i].size = G[i].def_size = cs96; G[i].c1 = G[i].def_c1 = FALSE; G[i].rtoi = NULL; G[i].itol = NULL; G[i].ltoi = NULL; G[i].itor = NULL; G[i].national = FALSE; } #endif /* CKOUNI */ #endif /* OS2 */ return; } #ifdef OS2 #ifdef CKOUNI else if (x == TX_UTF8) { tcs_transp = 0; tt_utf8 = 1; /* Turn it on if we are UTF8 */ return; } #endif /* CKOUNI */ else { tcs_transp = 0; tcsr = x; /* Remote character set */ #ifdef CKOUNI tt_utf8 = 0; /* Turn off UTF8 flag */ #endif /* CKOUNI */ if (z == TT_GR_ALL) { int i; #ifdef UNICODE dec_kbd = x; #endif /* UNICODE */ for (i = 0; i < 4; i++) { G[i].init = TRUE; if ( i == 0 && !cs_is_nrc(x) ) { G[0].designation = G[0].def_designation = FC_USASCII; G[0].size = G[0].def_size = cs94; G[0].national = 1; } else { G[i].def_designation = G[i].designation = x; switch (cs_size(x)) { /* 94, 96, or 128 */ case 128: case 96: G[i].size = G[i].def_size = cs96; break; case 94: G[i].size = G[i].def_size = cs94; break; default: G[i].size = G[i].def_size = csmb; break; } G[i].national = cs_is_nrc(x); } G[i].c1 = G[i].def_c1 = x != tcsl && cs_is_std(x); } #ifdef UNICODE } else if (z == TT_GR_KBD) { /* Keyboard only */ dec_kbd = x; #endif /* UNICODE */ } else { /* Specific Gn */ G[z].def_designation = G[z].designation = x; G[z].init = TRUE; switch (cs_size(x)) { /* 94, 96, or 128 */ case 128: case 96: G[z].size = G[z].def_size = cs96; break; case 94: G[z].size = G[z].def_size = cs94; break; default: G[z].size = G[z].def_size = csmb; break; } G[z].c1 = G[z].def_c1 = x != tcsl && cs_is_std(x); G[z].national = cs_is_nrc(x); } } #else /* not OS2 */ tcsr = x; /* Remote character set */ #endif /* OS2 */ } #endif /* NOCSETS */ VOID setcmask(x) int x; { if (x == 7) { cmask = 0177; } else if (x == 8) { cmask = 0377; parity = 0; } #ifdef KUI KuiSetProperty(KUI_TERM_CMASK,x,0); #endif /* KUI */ } #ifdef CK_AUTODL VOID setautodl(x,y) int x,y; { autodl = x; adl_ask = y; #ifdef KUI KuiSetProperty(KUI_TERM_AUTODOWNLOAD,x?(y?2:1):0,0); #endif /* KUI */ } #endif /* CK_AUTODL */ #ifdef OS2 VOID seturlhl(int x) { tt_url_hilite = x; #ifdef KUI KuiSetProperty(KUI_TERM_URL_HIGHLIGHT,x,0); #endif /* KUI */ } VOID setaprint(int x) { extern int aprint; aprint = x; #ifdef KUI KuiSetProperty(KUI_TERM_PRINTERCOPY,x,0); #endif /* KUI */ } #endif /* OS2 */ int settrm() { int i = 0; #ifdef OS2 extern int colorreset, user_erasemode; #endif /* OS2 */ if ((y = cmkey(trmtab,ntrm,"", "",xxstring)) < 0) return(y); #ifdef MAC printf("\n?Sorry, not implemented yet. Please use the Settings menu.\n"); return(-9); #else #ifdef IKSD if (inserver) { if ((y = cmcfm()) < 0) return(y); printf("?Sorry, command disabled.\r\n"); return(success = 0); } #endif /* IKSD */ switch (y) { case XYTBYT: /* SET TERMINAL BYTESIZE */ if ((y = cmnum("bytesize for terminal connection","8",10,&x, xxstring)) < 0) return(y); if (x != 7 && x != 8) { printf("\n?The choices are 7 and 8\n"); return(success = 0); } if ((y = cmcfm()) < 0) return(y); setcmask(x); #ifdef OS2 if (IS97801(tt_type_mode)) SNI_bitmode(x); #endif /* OS2 */ return(success = 1); case XYTSO: /* SET TERMINAL LOCKING-SHIFT */ return(seton(&sosi)); case XYTNL: /* SET TERMINAL NEWLINE-MODE */ return(seton(&tnlm)); #ifdef OS2 case XYTCOL: if ((x = cmkey(ttycoltab,ncolors,"","terminal",xxstring)) < 0) return(x); else if (x == TTCOLRES) { if ((y = cmkey(ttcolmodetab,ncolmode, "","default-color",xxstring)) < 0) return(y); if ((z = cmcfm()) < 0) return(z); colorreset = y; return(success = 1); } else if (x == TTCOLERA) { if ((y = cmkey(ttcolmodetab,ncolmode,"", "current-color",xxstring)) < 0) return(y); if ((z = cmcfm()) < 0) return(z); user_erasemode = y; return(success=1); } else { /* No parse error */ int fg = 0, bg = 0; fg = cmkey(ttyclrtab, nclrs, (x == TTCOLBOR ? "color for screen border" : "foreground color and then background color"), "lgray", xxstring); if (fg < 0) return(fg); if (x != TTCOLBOR) { if ((bg = cmkey(ttyclrtab,nclrs, "background color","blue",xxstring)) < 0) return(bg); } if ((y = cmcfm()) < 0) return(y); switch (x) { case TTCOLNOR: colornormal = fg | bg << 4; fgi = fg & 0x08; bgi = bg & 0x08; break; case TTCOLREV: colorreverse = fg | bg << 4; break; case TTCOLITA: coloritalic = fg | bg << 4; break; case TTCOLUND: colorunderline = fg | bg << 4; break; case TTCOLGRP: colorgraphic = fg | bg << 4; break; case TTCOLDEB: colordebug = fg | bg << 4; break; case TTCOLSTA: colorstatus = fg | bg << 4; break; case TTCOLHLP: colorhelp = fg | bg << 4; break; case TTCOLBOR: colorborder = fg; break; case TTCOLSEL: colorselect = fg | bg << 4; break; default: printf("%s - invalid\n",cmdbuf); return(-9); break; } scrninitialized[VTERM] = 0; VscrnInit(VTERM); } return(success = 1); case XYTCUR: { /* SET TERMINAL CURSOR */ extern int cursorena[]; extern int cursoron[] ; /* Cursor state on/off */ if ((x = cmkey(ttycurtab,ncursors,"","underline",xxstring)) < 0) return(x); if ((z = cmkey(curontab,ncuron,"","on",xxstring)) < 0) return(z); if ((y = cmcfm()) < 0) return(y); tt_cursor = tt_cursor_usr = x; if ( z == 2 ) { cursorena[VTERM] = tt_cursorena_usr = 1; tt_cursor_blink = 0; } else { cursorena[VTERM] = tt_cursorena_usr = z;/* turn cursor on/off */ tt_cursor_blink = 1; } cursoron[VTERM] = FALSE; /* Force newcursor to restore the cursor */ return(success = 1); } #endif /* OS2 */ case XYTTYP: /* SET TERMINAL TYPE */ return(settrmtyp()); #ifdef OS2 case XYTARR: /* SET TERMINAL ARROW-KEYS */ if ((x = cmkey(akmtab,2,"","",xxstring)) < 0) return(x); if ((y = cmcfm()) < 0) return(y); tt_arrow = x; /* TTK_NORM / TTK_APPL; see ckuusr.h */ return(success = 1); case XYTKPD: /* SET TERMINAL KEYPAD-MODE */ if ((x = cmkey(kpmtab,2,"","",xxstring)) < 0) return(x); if ((y = cmcfm()) < 0) return(y); tt_keypad = x; /* TTK_NORM / TTK_APPL; see ckuusr.h */ return(success = 1); case XYTUNX: { /* SET TERM UNIX-MODE (DG) */ extern int dgunix,dgunix_usr; x = seton(&dgunix); dgunix_usr = dgunix; return(x); } case XYTKBMOD: { /* SET TERM KEYBOARD MODE */ extern int tt_kb_mode; if ((x = cmkey(kbmodtab, nkbmodtab, "normal", "special keyboard mode for terminal emulation", xxstring) ) < 0) return(x); if ((y = cmcfm()) < 0) return(y); tt_kb_mode = x; return(success = 1); } case XYTWRP: /* SET TERMINAL WRAP */ return(seton(&tt_wrap)); case XYSCRS: if ((y = cmnum("CONNECT scrollback buffer size, lines","2000",10,&x, xxstring)) < 0) return(y); /* The max number of lines is the RAM */ /* we can actually dedicate to a */ /* scrollback buffer given the maximum */ /* process memory space of 512MB */ if (x < 256 || x > 2000000L) { printf("\n?The size must be between 256 and 2,000,000.\n"); return(success = 0); } if ((y = cmcfm()) < 0) return(y); tt_scrsize[VTERM] = x; VscrnInit(VTERM); return(success = 1); #endif /* OS2 */ #ifndef NOCSETS case XYTCS: { /* SET TERMINAL CHARACTER-SET */ int eol; /* set terminal character-set */ if ((x = cmkey( #ifdef CKOUNI txrtab,ntxrtab, #else /* CKOUNI */ ttcstab,ntermc, #endif /* CKOUNI */ "remote terminal character-set","",xxstring)) < 0) return(x); #ifdef UNICODE if (x == TX_TRANSP #ifdef CKOUNI || x == TX_UTF8 #endif /* CKOUNI */ ) { if ((y = cmcfm()) < 0) /* Confirm the command */ return(y); #ifdef OS2 if ( isunicode() && x == TX_TRANSP ) { /* If we are in unicode display mode then transparent * only affects the output direction. We need to know * the actual remote character set in order to perform * the tcsr -> ucs2 translation for display. */ x = y = tcsl; } else #endif /* OS2 */ y = x; } #else /* UNICODE */ if (x == FC_TRANSP) { if ((y = cmcfm()) < 0) /* Confirm the command */ return(y); y = x; } #endif /* UNICODE */ /* Not transparent or UTF8, so get local set to translate it into */ s = ""; #ifdef OS2 y = os2getcp(); /* Default is current code page */ switch (y) { case 437: s = "cp437"; break; case 850: s = "cp850"; break; case 852: s = "cp852"; break; case 857: s = "cp857"; break; case 858: s = "cp858"; break; case 862: s = "cp862"; break; case 866: s = "cp866"; break; case 869: s = "cp869"; break; case 1250: s = "cp1250"; break; case 1251: s = "cp1251"; break; case 1252: s = "cp1252"; break; case 1253: s = "cp1253"; break; case 1254: s = "cp1254"; break; case 1255: s = "cp1255"; break; case 1256: s = "cp1256"; break; case 1257: s = "cp1257"; break; case 1258: s = "cp1258"; break; } #ifdef PCFONTS /* If the user has loaded a font with SET TERMINAL FONT then we want to change the default code page to the font that was loaded. */ if (tt_font != TTF_ROM) { for (y = 0; y < ntermfont; y++ ) { if (term_font[y].kwval == tt_font) { s = term_font[y].kwd; break; } } } #endif /* PCFONTS */ #else /* Not K95... */ s = fcsinfo[fcharset].keyword; #endif /* OS2 */ if ((y = cmkey( #ifdef CKOUNI txrtab,ntxrtab, #else /* CKOUNI */ ttcstab,ntermc, #endif /* CKOUNI */ "local character-set",s,xxstring)) < 0) return(y); #ifdef UNICODE if (y == TX_UTF8) { printf("?UTF8 may not be used as a local character set.\r\n"); return(-9); } #endif /* UNICODE */ #ifdef OS2 if ((z = cmkey(graphsettab,ngraphset, "DEC VT intermediate graphic set","all",xxstring)) < 0) return(z); #endif /* OS2 */ if ((eol = cmcfm()) < 0) return(eol); /* Confirm the command */ /* End of command parsing - actions begin */ setlclcharset(y); setremcharset(x,z); return(success = 1); } #endif /* NOCSETS */ #ifndef NOCSETS case XYTLCS: /* SET TERMINAL LOCAL-CHARACTER-SET */ /* set terminal character-set */ s = getdcset(); /* Get display character-set name */ if ((y = cmkey( #ifdef CKOUNI txrtab,ntxrtab, #else /* CKOUNI */ fcstab,nfilc, #endif /* CKOUNI */ "local character-set",s,xxstring)) < 0) return(y); #ifdef UNICODE if (y == TX_UTF8) { printf("?UTF8 may not be used as a local character set.\r\n"); return(-9); } #endif /* UNICODE */ if ((z = cmcfm()) < 0) return(z); /* Confirm the command */ /* End of command parsing - action begins */ setlclcharset(y); return(success = 1); #endif /* NOCSETS */ #ifndef NOCSETS #ifdef UNICODE case XYTUNI: /* SET TERMINAL UNICODE */ return(seton(&tt_unicode)); #endif /* UNICODE */ case XYTRCS: /* SET TERMINAL REMOTE-CHARACTER-SET */ /* set terminal character-set */ if ((x = cmkey( #ifdef CKOUNI txrtab, ntxrtab, #else /* CKOUNI */ ttcstab,ntermc, #endif /* CKOUNI */ "remote terminal character-set","",xxstring)) < 0) return(x); #ifdef UNICODE if (x == TX_TRANSP #ifdef CKOUNI || x == TX_UTF8 #endif /* CKOUNI */ ) { if ((y = cmcfm()) < 0) /* Confirm the command */ return(y); #ifdef OS2 if ( isunicode() && x == TX_TRANSP ) { /* If we are in unicode display mode then transparent * only affects the output direction. We need to know * the actual remote character set in order to perform * the tcsr -> ucs2 translation for display. */ x = tcsl; } #endif /* OS2 */ } #else /* UNICODE */ if (x == FC_TRANSP) { if ((y = cmcfm()) < 0) /* Confirm the command */ return(y); } #endif /* UNICODE */ else { #ifdef OS2 if ((z = cmkey(graphsettab,ngraphset, "DEC VT intermediate graphic set","all",xxstring)) < 0) return(z); #endif /* OS2 */ if ((y = cmcfm()) < 0) /* Confirm the command */ return(y); } /* Command parsing ends here */ setremcharset(x,z); return(success = 1); #endif /* NOCSETS */ case XYTEC: /* SET TERMINAL ECHO */ if ((x = cmkey(rltab,nrlt,"which side echos during CONNECT", "remote", xxstring)) < 0) return(x); if ((y = cmcfm()) < 0) return(y); #ifdef NETCONN oldplex = x; #endif /* NETCONN */ duplex = x; return(success = 1); case XYTESC: /* SET TERM ESC */ if ((x = cmkey(nabltab,nnabltab,"","enabled",xxstring)) < 0) return(x); if ((y = cmcfm()) < 0) return(y); tt_escape = x; return(1); case XYTCRD: /* SET TERMINAL CR-DISPLAY */ if ((x = cmkey(crdtab,2,"", "normal", xxstring)) < 0) return(x); if ((y = cmcfm()) < 0) return(y); tt_crd = x; return(success = 1); case XYTLFD: /* SET TERMINAL LF-DISPLAY */ if ((x = cmkey(crdtab,2,"", "normal", xxstring)) < 0) return(x); if ((y = cmcfm()) < 0) return(y); tt_lfd = x; return(success = 1); #ifdef OS2 case XYTANS: { /* SET TERMINAL ANSWERBACK */ /* NOTE: We let them enable and disable the answerback sequence, but we do NOT let them change it, and we definitely do not let the host set it. This is a security feature. As of 1.1.8 we allow the SET TERM ANSWERBACK MESSAGE to be used just as MS-DOS Kermit does. C0 and C1 controls as well as DEL are not allowed to be used as characters. They are translated to underscore. This may not be set by APC. */ if ((x = cmkey(anbktab,nansbk,"", "off", xxstring)) < 0) return(x); if (x < 2) { if ((y = cmcfm()) < 0) return(y); tt_answer = x; return(success = 1); } else if ( x == 2 || x == 3) { int len = 0; extern int safeanswerbk; extern char useranswerbk[]; if ((y = cmtxt("Answerback extension","",&s,xxstring)) < 0) return(y); if (apcactive == APC_LOCAL || (apcactive == APC_REMOTE && !(apcstatus & APC_UNCH))) return(success = 0); len = strlen(s); if (x == 2) { /* Safe Answerback's don't have C0/C1 chars */ for (z = 0; z < len; z++) { if ((s[z] & 0x7F) <= SP || (s[z] & 0x7F) == DEL) useranswerbk[z] = '_'; else useranswerbk[z] = s[z]; } useranswerbk[z] = '\0'; safeanswerbk = 1 ; /* TRUE */ } else { ckstrncpy(useranswerbk,s,60); /* (see ckocon.c) */ safeanswerbk = 0; /* FALSE */ } updanswerbk(); return(success = 1); } else return(success = 0); } #endif /* OS2 */ #ifdef CK_APC case XYTAPC: if ((y = cmkey(apctab,napctab, "application program command execution","", xxstring)) < 0) return(y); if ((x = cmcfm()) < 0) return(x); if (apcactive == APC_LOCAL || (apcactive == APC_REMOTE && !(apcstatus & APC_UNCH))) return(success = 0); apcstatus = y; return(success = 1); #ifdef CK_AUTODL case XYTAUTODL: /* AUTODOWNLOAD */ if ((y = cmkey(adltab,nadltab,"Auto-download options","", xxstring)) < 0) return(y); switch (y) { case TAD_ON: case TAD_OFF: if ((x = cmcfm()) < 0) return(x); setautodl(y,0); break; case TAD_ASK: if ((x = cmcfm()) < 0) return(x); setautodl(TAD_ON,1); break; case TAD_ERR: if ((y = cmkey(adlerrtab,nadlerrtab,"","", xxstring)) < 0) return(y); if ((x = cmcfm()) < 0) return(x); adl_err = y; break; #ifdef OS2 case TAD_K: if ((y = cmkey(adlxtab,nadlxtab,"","", xxstring)) < 0) return(y); switch (y) { case TAD_X_C0: if ((y = cmkey(adlc0tab,nadlc0tab,"", "processed-by-emulator",xxstring)) < 0) return(y); if ((x = cmcfm()) < 0) return(x); adl_kc0 = y; break; case TAD_X_DETECT: if ((y = cmkey(adldtab,nadldtab,"","packet",xxstring)) < 0) return(y); if ((x = cmcfm()) < 0) return(x); adl_kmode = y; break; case TAD_X_STR: if ((y = cmtxt("Kermit start string","KERMIT READY TO SEND...", &s,xxstring)) < 0) return(y); free(adl_kstr); adl_kstr = strdup(s); break; } break; case TAD_Z: if ((y = cmkey(adlxtab,nadlxtab,"","",xxstring)) < 0) return(y); switch (y) { case TAD_X_C0: if ((y = cmkey(adlc0tab,nadlc0tab,"", "processed-by-emulator",xxstring)) < 0) return(y); if ((x = cmcfm()) < 0) return(x); adl_zc0 = y; break; case TAD_X_DETECT: if ((y = cmkey(adldtab,nadldtab,"","packet",xxstring)) < 0) return(y); if ((x = cmcfm()) < 0) return(x); adl_zmode = y; break; case TAD_X_STR: if ((y = cmtxt("","rz\\{13}",&s,xxstring)) < 0) return(y); free(adl_zstr); adl_zstr = strdup(s); break; } break; #endif /* OS2 */ } return(success = 1); #endif /* CK_AUTODL */ #endif /* CK_APC */ #ifdef OS2 case XYTBEL: return(success = setbell()); case XYTMBEL: /* MARGIN-BELL */ if ((y = cmkey(onoff,2,"","on",xxstring)) < 0) return(y); if (y) { /* ON */ if ((z = cmnum("Column at which to set margin bell", "72",10,&x,xxstring)) < 0) return(z); } if ((z = cmcfm()) < 0) return(z); marginbell = y; marginbellcol = x; return(success = 1); #endif /* OS2 */ #ifdef CKTIDLE case XYTIDLE: /* IDLE-SEND */ case XYTITMO: /* IDLE-TIMEOUT */ if ((z = cmnum("seconds of idle time to wait, or 0 to disable", "0",10,&x,xxstring)) < 0) return(z); if (y == XYTIDLE) { if ((y = cmtxt("string to send, may contain kverbs and variables", "\\v(newline)",&s,xxstring)) < 0) return(y); tt_idlesnd_tmo = x; /* (old) */ tt_idlelimit = x; /* (new) */ makestr(&tt_idlestr,brstrip(s)); /* (new) */ tt_idlesnd_str = tt_idlestr; /* (old) */ tt_idleact = IDLE_OUT; /* (new) */ } else { if ((y = cmcfm()) < 0) return(y); tt_idlelimit = x; } #ifdef OS2 puterror(VTERM); #endif /* OS2 */ return(success = 1); case XYTIACT: { /* SET TERM IDLE-ACTION */ if ((y = cmkey(idlacts,nidlacts,"","",xxstring)) < 0) return(y); if (y == IDLE_OUT) { if ((x = cmtxt("string to send, may contain kverbs and variables" , "",&s,xxstring)) < 0) return(x); makestr(&tt_idlestr,brstrip(s)); /* (new) */ tt_idlesnd_str = tt_idlestr; /* (old) */ } else { if ((x = cmcfm()) < 0) return(x); } tt_idleact = y; return(success = 1); } #endif /* CKTIDLE */ case XYTDEB: /* TERMINAL DEBUG */ y = seton(&x); /* Go parse ON or OFF */ if (y > 0) /* Command succeeded? */ setdebses(x); return(y); #ifdef OS2 case XYTASCRL: /* SET TERMINAL AUTOSCROLL */ y = seton(&autoscroll); return(y); case XYTAPAGE: /* SET TERMINAL AUTOPAGE */ y = seton(&wy_autopage); return(y); case XYTROL: /* SET TERMINAL ROLL */ if ((y = cmkey(rolltab,nroll,"scrollback mode","insert",xxstring))<0) return(y); if (y == TTR_KEYS) { if ((x = cmkey(rollkeytab,nrollkey,"","send",xxstring))<0) return(x); if ((z = cmcfm()) < 0) return(z); tt_rkeys[VTERM] = x; } else { if ((x = cmcfm()) < 0) return(x); tt_roll[VTERM] = y; } return(success = 1); case XYTCTS: /* SET TERMINAL TRANSMIT-TIMEOUT */ y = cmnum("Maximum seconds to allow CTS off during CONNECT", "5",10,&x,xxstring); return(setnum(&tt_ctstmo,x,y,10000)); case XYTCPG: { /* SET TERMINAL CODE-PAGE */ int i; int cp = -1; y = cmnum("PC code page to use during terminal emulation", ckitoa(os2getcp()),10,&x,xxstring); if ((x = setnum(&cp,x,y,11000)) < 0) return(x); if (os2setcp(cp) != 1) { #ifdef NT if (isWin95()) printf( "Sorry, Windows 95 does not support code page switching\n"); else #endif /* NT */ printf( "Sorry, %d is not a valid code page for this system.\n",cp); return(-9); } /* Force the terminal character-sets conversions to be updated */ for ( i = 0; i < 4; i++ ) G[i].init = TRUE; return(1); } case XYTPAC: /* SET TERMINAL OUTPUT-PACING */ y = cmnum( "Pause between sending each character during CONNECT, milliseconds", "-1",10,&x,xxstring); return(setnum(&tt_pacing,x,y,10000)); #ifdef OS2MOUSE case XYTMOU: { /* SET TERMINAL MOUSE */ int old_mou = tt_mouse; if ((x = seton(&tt_mouse)) < 0) return(x); if (tt_mouse != old_mou) if (tt_mouse) os2_mouseon(); else os2_mouseoff(); return(1); } #endif /* OS2MOUSE */ #endif /* OS2 */ case XYTWID: { if ((y = cmnum( #ifdef OS2 "number of columns in display window during CONNECT", #else "number of columns on your screen", #endif /* OS2 */ "80",10,&x,xxstring)) < 0) return(y); if ((y = cmcfm()) < 0) return(y); #ifdef OS2 return(success = os2_settermwidth(x)); #else /* Not OS/2 */ tt_cols = x; return(success = 1); #endif /* OS2 */ } case XYTHIG: if ((y = cmnum( #ifdef OS2 "number of rows in display window during CONNECT, not including status line", tt_status[VTERM]?"24":"25", #else "24","number of rows on your screen", #endif /* OS2 */ 10,&x,xxstring)) < 0) return(y); if ((y = cmcfm()) < 0) return(y); #ifdef OS2 return (success = os2_settermheight(x)); #else /* Not OS/2 */ tt_rows = x; return(success = 1); #endif /* OS2 */ #ifdef OS2 case XYTPRN: { /* Print Mode */ extern bool xprint, aprint, cprint, uprint; if ((y = cmkey(prnmtab,nprnmtab,"","off", xxstring)) < 0) return(y); if ((x = cmcfm()) < 0) return(x); switch (y) { case 0: if (cprint || uprint || aprint || xprint) printeroff(); cprint = xprint = uprint = 0; setaprint(0); break; case 1: if (!(cprint || uprint || aprint || xprint)) printeron(); setaprint(1); cprint = xprint = uprint = 0; break; case 2: if (!(cprint || uprint || aprint || xprint)) printeron(); cprint = 1; setaprint(0); xprint = uprint = 0; break; case 3: if (!(cprint || uprint || aprint || xprint)) printeron(); uprint = 1; setaprint(0); xprint = cprint = 0; break; } return(1); } #else #ifdef XPRINT case XYTPRN: { extern int tt_print; if ((x = seton(&tt_print)) < 0) return(x); return(success = 1); } #endif /* XPRINT */ #endif /* OS2 */ #ifdef OS2 case XYTSCNM: { extern int decscnm, decscnm_usr; if ((y = cmkey(normrev,4,"", decscnm_usr?"reverse":"normal", xxstring) ) < 0) return(y); if ((x = cmcfm()) < 0) return(x); decscnm_usr = y; if (decscnm != decscnm_usr) flipscreen(VTERM); return(1); } case XYTOPTI: if ((y = cmkey(onoff,2,"",tt_diff_upd?"on":"off", xxstring)) < 0) return(y); if ((x = cmcfm()) < 0) return(x); tt_diff_upd = y; return(1); case XYTUPD: { int mode, delay; if ((mode = cmkey(scrnupd,nscrnupd,"","fast",xxstring)) < 0) { return(mode); } else { y = cmnum( "Pause between FAST screen updates in CONNECT mode, milliseconds", "100",10,&x,xxstring ); if (x < 0 || x > 1000 ) { printf( "\n?The update rate must be between 0 and 1000 milliseconds.\n" ); return(success = 0); } if ((y = cmcfm()) < 0) return(y); updmode = tt_updmode = mode; return(setnum(&tt_update,x,y,10000)); } } case XYTCTRL: if ((x = cmkey(termctrl,ntermctrl,"","7",xxstring)) < 0) { return(x); } else { if ((y = cmcfm()) < 0) return(y); switch ( x ) { case 8: send_c1 = send_c1_usr = TRUE; break; case 7: default: send_c1 = send_c1_usr = FALSE; break; } } return(success = TRUE); break; #ifdef PCFONTS case XYTFON: if ( !IsOS2FullScreen() ) { printf( "\n?SET TERMINAL FONT is only supported in Full Screen sessions.\n"); return(success = FALSE); } if ((x = cmkey(term_font,ntermfont,"","default",xxstring)) < 0) { return(x); } else { if ((y = cmcfm()) < 0) return(y); if ( !os2LoadPCFonts() ) { tt_font = x; return(success = TRUE); } else { printf( "\n?PCFONTS.DLL is not available in CKERMIT executable directory.\n"); return(success = FALSE); } } break; #else /* PCFONTS */ #ifdef NT #ifdef KUI case XYTFON: return(setguifont()); /* ckuus3.c */ #endif /* KUI */ #endif /* NT */ #endif /* PCFONTS */ case XYTVCH: { extern int pheight, marginbot, cmd_rows, cmd_cols; if ((x = cmkey(tvctab,ntvctab,"",isWin95()?"win95-safe":"enabled", xxstring)) < 0) return(x); if ((y = cmcfm()) < 0) return(y); #ifndef KUI if (x != tt_modechg) { switch (x) { case TVC_DIS: /* When disabled the heights of all of the virtual screens */ /* must be equal to the physical height of the console */ /* window and may not be changed. */ /* The width of the window may not be altered. */ tt_modechg = TVC_ENA; /* Temporary */ if (marginbot > pheight-(tt_status[VTERM]?1:0)) marginbot = pheight-(tt_status[VTERM]?1:0); tt_szchng[VCMD] = 1 ; tt_rows[VCMD] = pheight; VscrnInit(VCMD); SetCols(VCMD); cmd_rows = y; tt_szchng[VTERM] = 2 ; tt_rows[VTERM] = pheight - (tt_status[VTERM]?1:0); VscrnInit(VTERM); break; case TVC_ENA: /* When enabled the physical height of the console windows */ /* should be adjusted to the height of the virtual screen */ /* The width may be set to anything. */ /* nothing to do */ break; case TVC_W95: /* Win95-safe mode allows the physical height to change */ /* but restricts it to a width of 80 and a height equal to */ /* 25, 43, or 50. Must be adjusted now. */ /* The virtual heights must be equal to the above. */ if (pheight != 25 && pheight != 43 && pheight != 50) { if (pheight < 25) y = 25; else if (pheight < 43) y = 43; else y = 50; } else y = pheight; tt_modechg = TVC_ENA; /* Temporary */ tt_szchng[VCMD] = 1; tt_rows[VCMD] = y; tt_cols[VCMD] = 80; VscrnInit(VCMD); SetCols(VCMD); cmd_rows = y; cmd_cols = 80; marginbot = y-(tt_status[VTERM]?1:0); tt_szchng[VTERM] = 2; tt_rows[VTERM] = y - (tt_status[VTERM]?1:0); tt_cols[VTERM] = 80; VscrnInit(VTERM); break; } tt_modechg = x; } return(success = 1); #else return(success = 0); #endif /* KUI */ } case XYTSTAT: { extern int marginbot; if ((y = cmkey(onoff,2,"","on",xxstring)) < 0) return(y); if ((x = cmcfm()) < 0) return(x); if (y != tt_status[VTERM] || y != tt_status_usr[VTERM]) { /* Might need to fixup the margins */ if ( marginbot == VscrnGetHeight(VTERM)-(tt_status[VTERM]?1:0) ) if (y) { marginbot--; } else { marginbot++; } tt_status_usr[VTERM] = tt_status[VTERM] = y; if (y) { tt_szchng[VTERM] = 2; tt_rows[VTERM]--; VscrnInit(VTERM); /* Height set here */ #ifdef TNCODE if (TELOPT_ME(TELOPT_NAWS)) tn_snaws(); #endif /* TNCODE */ #ifdef RLOGCODE if (TELOPT_ME(TELOPT_NAWS)) rlog_naws(); #endif /* RLOGCODE */ #ifdef SSHBUILTIN if (TELOPT_ME(TELOPT_NAWS)) ssh_snaws(); #endif /* SSHBUILTIN */ } else { tt_szchng[VTERM] = 1; tt_rows[VTERM]++; VscrnInit(VTERM); /* Height set here */ #ifdef TNCODE if (TELOPT_ME(TELOPT_NAWS)) tn_snaws(); #endif /* TNCODE */ #ifdef RLOGCODE if (TELOPT_ME(TELOPT_NAWS)) rlog_naws(); #endif /* RLOGCODE */ #ifdef SSHBUILTIN if (TELOPT_ME(TELOPT_NAWS)) ssh_snaws(); #endif /* SSHBUILTIN */ } } return(1); } #endif /* OS2 */ #ifdef NT case XYTATTBUG: if ((y = cmkey(onoff,2,"","on",xxstring)) < 0) return(y); if ((x = cmcfm()) < 0) return(x); tt_attr_bug = y; return(1); #endif /* NT */ #ifdef OS2 case XYTSGRC: if ((y = cmkey(onoff,2,"","on",xxstring)) < 0) return(y); if ((x = cmcfm()) < 0) return(x); sgrcolors = y; return(1); case XYTSEND: if ((y = cmkey(onoff,2,"","on",xxstring)) < 0) return(y); if ((x = cmcfm()) < 0) return(x); tt_senddata = y; return(1); case XYTSEOB: if ((y = cmkey(ttyseobtab,2,"","us_cr",xxstring)) < 0) return(y); if ((x = cmcfm()) < 0) return(x); wy_blockend = y; return(1); case XYTURLHI: { int done = 0, attr = VT_CHAR_ATTR_NORMAL; if ((x = cmkey(onoff,2,"","on",xxstring)) < 0) return(x); if (x) { z = 0; while (!done) { if ((y = cmkey(ttyprotab,nprotect,"", z?"done":"reverse",xxstring)) < 0) return(y); switch (y) { case TTATTDONE: done = TRUE; break; case TTATTBLI: attr |= VT_CHAR_ATTR_BLINK; break; case TTATTREV: attr |= VT_CHAR_ATTR_REVERSE; break; case TTATTITA: attr |= VT_CHAR_ATTR_ITALIC; break; case TTATTUND: attr |= VT_CHAR_ATTR_UNDERLINE; break; case TTATTBLD: attr |= VT_CHAR_ATTR_BOLD; break; case TTATTDIM: attr |= VT_CHAR_ATTR_DIM; break; case TTATTINV: attr |= VT_CHAR_ATTR_INVISIBLE; break; case TTATTNOR: break; } z = 1; /* One attribute has been chosen */ } } if ((z = cmcfm()) < 0) return(z); seturlhl(x); if (x) tt_url_hilite_attr = attr; return(1); } case XYTATTR: if ((x = cmkey(ttyattrtab,nattrib,"","underline",xxstring)) < 0) return(x); switch (x) { case TTATTBLI: if ((y = cmkey(onoff,2,"","on",xxstring)) < 0) return(y); if ((x = cmcfm()) < 0) return(x); trueblink = y; #ifndef KUI if ( !trueblink && trueunderline ) { trueunderline = 0; printf("Warning: Underline being simulated by color.\n"); } #endif /* KUI */ break; case TTATTDIM: if ((y = cmkey(onoff,2,"","on",xxstring)) < 0) return(y); if ((x = cmcfm()) < 0) return(x); truedim = y; break; case TTATTREV: if ((y = cmkey(onoff,2,"","on",xxstring)) < 0) return(y); if ((x = cmcfm()) < 0) return(x); truereverse = y; break; case TTATTUND: if ((y = cmkey(onoff,2,"","on",xxstring)) < 0) return(y); if ((x = cmcfm()) < 0) return(x); trueunderline = y; #ifndef KUI if (!trueblink && trueunderline) { trueblink = 1; printf("Warning: True blink mode is active.\n"); } #endif /* KUI */ break; case TTATTITA: if ((y = cmkey(onoff,2,"","on",xxstring)) < 0) return(y); if ((x = cmcfm()) < 0) return(x); trueitalic = y; break; case TTATTPRO: { /* Set default Protected Character attribute */ extern vtattrib WPattrib; /* current WP Mode Attrib */ extern vtattrib defWPattrib; /* default WP Mode Attrib */ vtattrib wpa = {0,0,0,0,0,1,0,0,0,0,0}; /* Protected */ int done = 0; x = 0; while (!done) { if ((y = cmkey(ttyprotab,nprotect,"", x?"done":"dim",xxstring)) < 0) return(y); switch (y) { case TTATTNOR: break; case TTATTBLI: /* Blinking doesn't work */ wpa.blinking = TRUE; break; case TTATTREV: wpa.reversed = TRUE; break; case TTATTITA: wpa.italic = TRUE; break; case TTATTUND: wpa.underlined = TRUE; break; case TTATTBLD: wpa.bold = TRUE; break; case TTATTDIM: wpa.dim = TRUE; break; case TTATTINV: wpa.invisible = TRUE ; break; case TTATTDONE: done = TRUE; break; } x = 1; /* One attribute has been chosen */ } if ((x = cmcfm()) < 0) return(x); WPattrib = defWPattrib = wpa; break; } } return(1); case XYTKEY: { /* SET TERMINAL KEY */ int t, x, y; int clear = 0, deflt = 0; int confirmed = 0; int flag = 0; int kc = -1; /* Key code */ int litstr = 0; /* Literal String? */ char *s = NULL; /* Key binding */ #ifndef NOKVERBS char *p = NULL; /* Worker */ #endif /* NOKVERBS */ con_event defevt; extern int os2gks; extern int mskkeys; extern int initvik; struct FDB kw,sw,nu,cm; defevt.type = error; if ((t = cmkey(ttkeytab,nttkey,"","",xxstring)) < 0) return(t); cmfdbi(&nu, /* First FDB - command switches */ _CMNUM, /* fcode */ "/literal, keycode, or action", "", /* default */ "", /* addtl string data */ 10, /* addtl numeric data 1: radix */ 0, /* addtl numeric data 2: 0 */ xxstring, /* Processing function */ NULL, /* Keyword table */ &sw /* Pointer to next FDB */ ); /* */ cmfdbi(&sw, /* Second FDB - switches */ _CMKEY, /* fcode */ "", "", /* default */ "", /* addtl string data */ nstrmswitab, /* addtl numeric data 1: tbl size */ 4, /* addtl numeric data 2: 4 = cmswi */ xxstring, /* Processing function */ strmswitab, /* Keyword table */ &kw /* Pointer to next FDB */ ); cmfdbi(&kw, /* Third FDB - command switches */ _CMKEY, /* fcode */ "/literal, keycode, or action", "", /* default */ "", /* addtl string data */ nstrmkeytab, /* addtl numeric data 1: tbl size */ 0, /* addtl numeric data 2 */ xxstring, /* Processing function */ strmkeytab, /* Keyword table */ &cm /* Pointer to next FDB */ ); cmfdbi(&cm, /* Final FDB - Confirmation */ _CMCFM, /* fcode */ "", "", /* default */ "", /* addtl string data */ 0, /* addtl numeric data 1: tbl size */ 0, /* addtl numeric data 2: 4 = cmswi */ xxstring, /* Processing function */ NULL, /* Keyword table */ NULL /* Pointer to next FDB */ ); while (kc < 0) { x = cmfdb(&nu); /* Parse something */ if (x < 0) return(x); switch (cmresult.fcode) { case _CMCFM: printf(" Press key to be defined: "); conbin((char)escape); /* Put terminal in binary mode */ os2gks = 0; /* Turn off Kverb preprocessing */ kc = congks(0); /* Get character or scan code */ os2gks = 1; /* Turn on Kverb preprocessing */ concb((char)escape); /* Restore terminal to cbreak mode */ if (kc < 0) { /* Check for error */ printf("?Error reading key\n"); return(0); } shokeycode(kc,t); /* Show current definition */ flag = 1; /* Remember it's a multiline command */ break; case _CMNUM: kc = cmresult.nresult; break; case _CMKEY: if (cmresult.fdbaddr == &sw) { /* Switch */ if (cmresult.nresult == 0) litstr = 1; } else if (cmresult.fdbaddr == &kw) { /* Keyword */ if (cmresult.nresult == 0) clear = 1; else deflt = 1; if ((x = cmcfm()) < 0) return(x); if (clear) clearkeymap(t); else if (deflt) defaultkeymap(t); initvik = 1; return(1); } } } /* Normal SET TERMINAL KEY command... */ if (mskkeys) kc = msktock(kc); if (kc < 0 || kc >= KMSIZE) { printf("?key code must be between 0 and %d\n", KMSIZE - 1); return(-9); } if (kc == escape) { printf("Sorry, %d is the CONNECT-mode escape character\n",kc); return(-9); } wideresult = -1; if (flag) { cmsavp(psave,PROMPTL); cmsetp(" Enter new definition: "); cmini(ckxech); } def_again: if (flag) prompt(NULL); if ((y = cmtxt("key definition,\n\ or Ctrl-C to cancel this command,\n\ or Enter to restore default definition", "",&s,NULL)) < 0) { if (flag) /* Handle parse errors */ goto def_again; else return(y); } s = brstrip(s); #ifndef NOKVERBS p = s; /* Save this place */ #endif /* NOKVERBS */ /* If the definition included any \Kverbs, quote the backslash so the \Kverb will still be in the definition when the key is pressed. We don't do this in zzstring(), because \Kverbs are valid only in this context and nowhere else. We use this code active for all versions that support SET KEY, even if they don't support \Kverbs, because otherwise \K would behave differently for different versions. */ for (x = 0, y = 0; s[x]; x++, y++) { /* Convert \K to \\K */ if ((x > 0) && (s[x] == 'K' || s[x] == 'k') ) { /* Have K */ if ((x == 1 && s[x-1] == CMDQ) || (x > 1 && s[x-1] == CMDQ && s[x-2] != CMDQ)) { line[y++] = CMDQ; /* Make it \\K */ } if (x > 1 && s[x-1] == '{' && s[x-2] == CMDQ) { line[y-1] = CMDQ; /* Have \{K */ line[y++] = '{'; /* Make it \\{K */ } } line[y] = s[x]; } line[y++] = NUL; /* Terminate */ s = line + y + 1; /* Point to after it */ x = LINBUFSIZ - (int) strlen(line) - 1; /* Get remaining space */ if ((x < (LINBUFSIZ / 2)) || (zzstring(line, &s, &x) < 0)) { /* Expand variables, etc. */ printf("?Key definition too long\n"); if (flag) cmsetp(psave); return(-9); } s = line + y + 1; /* Point to result. */ #ifndef NOKVERBS /* Special case: see if the definition starts with a \Kverb. If it does, point to it with p, otherwise set p to NULL. */ p = s; if (*p++ == CMDQ) { if (*p == '{') p++; p = (*p == 'k' || *p == 'K') ? p + 1 : NULL; } #endif /* NOKVERBS */ switch (strlen(s)) { /* Action depends on length */ case 0: /* Clear individual key def */ deletekeymap(t,kc); break; case 1: if (!litstr) { defevt.type = key; /* Single character */ defevt.key.scancode = *s; break; } default: /* Character string */ #ifndef NOKVERBS if (p) { y = xlookup(kverbs,p,nkverbs,&x); /* Look it up */ /* Need exact match */ debug(F101,"set key kverb lookup",0,y); if (y > -1) { defevt.type = kverb; defevt.kverb.id = y; break; } } #endif /* NOKVERBS */ if (litstr) { defevt.type = literal; defevt.literal.string = (char *) malloc(strlen(s)+1); if (defevt.literal.string) strcpy(defevt.literal.string, s); /* safe */ } else { defevt.type = macro; defevt.macro.string = (char *) malloc(strlen(s)+1); if (defevt.macro.string) strcpy(defevt.macro.string, s); /* safe */ } break; } insertkeymap(t, kc, defevt); if (flag) cmsetp(psave); initvik = 1; /* Update VIK table */ return(1); } #ifdef PCTERM case XYTPCTERM: /* PCTERM Keyboard Mode */ if ((x = seton(&tt_pcterm)) < 0) return(x); return(success = 1); #endif /* PCTERM */ #endif /* OS2 */ #ifdef CK_TRIGGER case XYTRIGGER: if ((y = cmtxt("String to trigger automatic return to command mode", "",&s,xxstring)) < 0) return(y); makelist(s,tt_trigger,TRIGGERS); return(1); #endif /* CK_TRIGGER */ #ifdef OS2 case XYTSAC: if ((y = cmnum("ASCII value to use for spacing attributes", "32",10,&x,xxstring)) < 0) return(y); if ((y = cmcfm()) < 0) return(y); tt_sac = x; return(success = 1); case XYTKBDGL: { /* SET TERM KBD-FOLLOWS-GL/GR */ extern int tt_kb_glgr; /* from ckoco3.c */ if ((x = seton(&tt_kb_glgr)) < 0) return(x); return(success = 1); } #ifndef NOCSETS case XYTVTLNG: /* SET TERM DEC-LANGUAGE */ if ((y = cmkey(vtlangtab,nvtlangtab,"VT language", IS97801(tt_type_mode)?"german":"north-american", xxstring)) < 0) return(y); if ((x = cmcfm()) < 0) return(x); /* A real VT terminal would use the language to set the */ /* default keyboard language for both 8-bit multinational */ /* and 7-bit national modes. For 8-bit mode it would */ /* set the terminal character-set to the ISO set if it */ /* is not already set. */ /* Latin-1 can be replaced by DEC Multinational */ switch (y) { case VTL_NORTH_AM: /* North American */ /* Multinational: Latin-1 */ /* National: US_ASCII */ dec_lang = y; dec_nrc = TX_ASCII; dec_kbd = TX_8859_1; break; case VTL_BRITISH : /* Multinational: Latin-1 */ /* National: UK_ASCII */ dec_lang = y; dec_nrc = TX_BRITISH; dec_kbd = TX_8859_1; break; case VTL_FRENCH : case VTL_BELGIAN : case VTL_CANADIAN: /* Multinational: Latin-1 */ /* National: FR_ASCII */ dec_lang = y; dec_nrc = TX_FRENCH; dec_kbd = TX_8859_1; break; case VTL_FR_CAN : /* Multinational: Latin-1 */ /* National: FC_ASCII */ dec_lang = y; dec_nrc = TX_CN_FRENCH; dec_kbd = TX_8859_1; break; case VTL_DANISH : case VTL_NORWEGIA: /* Multinational: Latin-1 */ /* National: NO_ASCII */ dec_lang = y; dec_nrc = TX_NORWEGIAN; dec_kbd = TX_8859_1; break; case VTL_FINNISH : /* Multinational: Latin-1 */ /* National: FI_ASCII */ dec_lang = y; dec_nrc = TX_FINNISH; dec_kbd = TX_8859_1; break; case VTL_GERMAN : /* Multinational: Latin-1 */ /* National: GR_ASCII */ dec_lang = y; dec_nrc = TX_GERMAN; dec_kbd = TX_8859_1; break; case VTL_DUTCH : /* Multinational: Latin-1 */ /* National: DU_ASCII */ dec_lang = y; dec_nrc = TX_DUTCH; dec_kbd = TX_8859_1; break; case VTL_ITALIAN : /* Multinational: Latin-1 */ /* National: IT_ASCII */ dec_lang = y; dec_nrc = TX_ITALIAN; dec_kbd = TX_8859_1; break; case VTL_SW_FR : case VTL_SW_GR : /* Multinational: Latin-1 */ /* National: CH_ASCII */ dec_lang = y; dec_nrc = TX_SWISS; dec_kbd = TX_8859_1; break; case VTL_SWEDISH : /* Multinational: Latin-1 */ /* National: SW_ASCII */ dec_lang = y; dec_nrc = TX_SWEDISH; dec_kbd = TX_8859_1; break; case VTL_SPANISH : /* Multinational: Latin-1 */ /* National: SP_ASCII */ dec_lang = y; dec_nrc = TX_SPANISH; dec_kbd = TX_8859_1; break; case VTL_PORTUGES: /* Multinational: Latin-1 */ /* National: Portugese ASCII */ dec_lang = y; dec_nrc = TX_PORTUGUESE; dec_kbd = TX_8859_1; break; case VTL_HEBREW : /* Multinational: Latin-Hebrew / DEC-Hebrew */ /* National: DEC 7-bit Hebrew */ dec_lang = y; dec_nrc = TX_HE7; dec_kbd = TX_8859_8; break; case VTL_GREEK : /* Multinational: Latin-Greek / DEC-Greek */ /* National: DEC Greek NRC */ /* is ELOT927 equivalent to DEC Greek???? */ dec_lang = y; dec_nrc = TX_ELOT927; dec_kbd = TX_8859_7; break; #ifdef COMMENT case VTL_TURK_Q : case VTL_TURK_F : /* Multinational: Latin-Turkish / DEC-Turkish */ /* National: DEC 7-bit Turkish */ break; #endif /* COMMENT */ case VTL_HUNGARIA: /* Multinational: Latin-2 */ /* National: no national mode */ dec_lang = y; dec_nrc = TX_HUNGARIAN; dec_kbd = TX_8859_2; break; case VTL_SLOVAK : case VTL_CZECH : case VTL_POLISH : case VTL_ROMANIAN: /* Multinational: Latin-2 */ /* National: no national mode */ dec_lang = y; dec_nrc = TX_ASCII; dec_kbd = TX_8859_2; break; case VTL_RUSSIAN : /* Multinational: Latin-Cyrillic / KOI-8 */ /* National: DEC Russian NRC */ dec_lang = y; dec_nrc = TX_KOI7; dec_kbd = TX_8859_5; break; case VTL_LATIN_AM: /* Multinational: not listed in table */ /* National: not listed in table */ dec_lang = y; dec_nrc = TX_ASCII; dec_kbd = TX_8859_1; break; #ifdef COMMENT case VTL_SCS : /* Multinational: Latin-2 */ /* National: SCS NRC */ break; #endif /* COMMENT */ default: return(success = 0); } if (IS97801(tt_type_mode)) { SNI_bitmode(cmask == 0377 ? 8 : 7); } return(success = 1); #endif /* NOCSETS */ case XYTVTNRC: { /* SET TERM DEC-NRC-MODE */ extern int decnrcm_usr, decnrcm; /* from ckoco3.c */ if ((x = seton(&decnrcm_usr)) < 0) return(x); decnrcm = decnrcm_usr; return(success = 1); } case XYTSNIPM: { /* SET TERM SNI-PAGEMODE */ extern int sni_pagemode, sni_pagemode_usr; if ((y = cmkey(onoff,2,"","on",xxstring)) < 0) return(y); if ((x = cmcfm()) < 0) return(x); sni_pagemode_usr = sni_pagemode = y; return(success = 1); } case XYTSNISM: { /* SET TERM SNI-SCROLLMODE */ extern int sni_scroll_mode, sni_scroll_mode_usr; if ((y = cmkey(onoff,2,"","on",xxstring)) < 0) return(y); if ((x = cmcfm()) < 0) return(x); sni_scroll_mode_usr = sni_scroll_mode = y; return(success = 1); } case XYTSNICC: { /* SET TERM SNI-CH.CODE */ extern int sni_chcode_usr; if ((y = cmkey(onoff,2,"","on",xxstring)) < 0) return(y); if ((x = cmcfm()) < 0) return(x); sni_chcode_usr = y; SNI_chcode(y); return(success = 1); } case XYTSNIFV: { /* SET TERM SNI-FIRMWARE-VERSIONS */ extern CHAR sni_kbd_firmware[], sni_term_firmware[]; CHAR kbd[7],term[7]; if ((x = cmfld("Keyboard Firmware Version",sni_kbd_firmware, &s, xxstring)) < 0) return(x); if ((int)strlen(s) != 6) { printf("?Sorry - the firmware version must be 6 digits long\n"); return(-9); } for (i = 0; i < 6; i++) { if (!isdigit(s[i])) { printf("?Sorry - the firmware version can only contain digits [0-9]\n"); return(-9); } } ckstrncpy(kbd,s,7); if ((x = cmfld("Terminal Firmware Version",sni_term_firmware, &s, xxstring)) < 0) return(x); if ((int)strlen(s) != 6) { printf("?Sorry - the firmware version must be 6 digits long\n"); return(-9); } for (i = 0; i < 6; i++) { if (!isdigit(s[i])) { printf("?Sorry - the firmware version can only contain digits [0-9]\n"); return(-9); } } ckstrncpy(term,s,7); if ((x = cmcfm()) < 0) return(x); ckstrncpy(sni_kbd_firmware,kbd,7); ckstrncpy(sni_term_firmware,term,7); return(success = 1); } case XYTLSP: { /* SET TERM LINE-SPACING */ if ((x = cmfld("Line Spacing","1",&s, xxstring)) < 0) return(x); if (isfloat(s,0) < 1) { /* (sets floatval) */ printf("?Integer or floating-point number required\n"); return(-9); } if (floatval < 1.0 || floatval > 3.0) { printf("?Value must within the range 1.0 and 3.0 (inclusive)\n"); return(-9); } if ((x = cmcfm()) < 0) return(x); #ifdef KUI tt_linespacing[VCMD] = tt_linespacing[VTERM] = floatval; return(success = 1); #else /* KUI */ printf("?Sorry, Line-spacing is only supported in K95G.EXE.\n"); return(success = 0); #endif /* KUI */ } #endif /* OS2 */ default: /* Shouldn't get here. */ return(-2); } #endif /* MAC */ #ifdef COMMENT /* This was supposed to shut up picky compilers but instead it makes most compilers complain about "statement not reached". */ return(-2); #endif /* COMMENT */ #ifdef OS2 return(-2); #endif /* OS2 */ } #ifdef OS2 int settitle(void) { extern char usertitle[]; if ((y = cmtxt("title text","",&s,xxstring)) < 0) return(y); #ifdef IKSD if (inserver) { printf("?Sorry, command disabled.\r\n"); return(success = 0); } #endif /* IKSD */ s = brstrip(s); ckstrncpy(usertitle,s,64); os2settitle("",1); return(1); } static struct keytab dialertab[] = { /* K95 Dialer types */ "backspace", 0, 0, "enter", 1, 0 }; static int ndialer = 2; int setdialer(void) { int t, x, y; int clear = 0, deflt = 0; int kc; /* Key code */ char *s = NULL; /* Key binding */ #ifndef NOKVERBS char *p = NULL; /* Worker */ #endif /* NOKVERBS */ con_event defevt; extern int os2gks; extern int mskkeys; extern int initvik; defevt.type = error; if (( x = cmkey(dialertab, ndialer, "Kermit-95 dialer work-arounds", "", xxstring)) < 0 ) return(x); switch (x) { case 0: /* Backspace */ kc = 264; break; case 1: /* Enter */ kc = 269; break; default: printf("Illegal value in setdialer()\n"); return(-9); } if ((y = cmtxt("Key definition","",&s,xxstring)) < 0) return(y); #ifdef IKSD if (inserver) { printf("?Sorry, command disabled.\r\n"); return(success = 0); } #endif /* IKSD */ s = brstrip(s); #ifndef NOKVERBS p = s; /* Save this place */ #endif /* NOKVERBS */ /* If the definition included any \Kverbs, quote the backslash so the \Kverb will still be in the definition when the key is pressed. We don't do this in zzstring(), because \Kverbs are valid only in this context and nowhere else. We use this code active for all versions that support SET KEY, even if they don't support \Kverbs, because otherwise \K would behave differently for different versions. */ for (x = 0, y = 0; s[x]; x++, y++) { /* Convert \K to \\K */ if ((x > 0) && (s[x] == 'K' || s[x] == 'k') ) { /* Have K */ if ((x == 1 && s[x-1] == CMDQ) || (x > 1 && s[x-1] == CMDQ && s[x-2] != CMDQ)) { line[y++] = CMDQ; /* Make it \\K */ } if (x > 1 && s[x-1] == '{' && s[x-2] == CMDQ) { line[y-1] = CMDQ; /* Have \{K */ line[y++] = '{'; /* Make it \\{K */ } } line[y] = s[x]; } line[y++] = NUL; /* Terminate */ s = line + y + 1; /* Point to after it */ x = LINBUFSIZ - (int) strlen(line) - 1; /* Calculate remaining space */ if ((x < (LINBUFSIZ / 2)) || (zzstring(line, &s, &x) < 0)) { /* Expand variables, etc. */ printf("?Key definition too long\n"); return(-9); } s = line + y + 1; /* Point to result. */ #ifndef NOKVERBS /* Special case: see if the definition starts with a \Kverb. If it does, point to it with p, otherwise set p to NULL. */ p = s; if (*p++ == CMDQ) { if (*p == '{') p++; p = (*p == 'k' || *p == 'K') ? p + 1 : NULL; } #endif /* NOKVERBS */ /* Clear the definition for SET KEY */ if (macrotab[kc]) { /* Possibly free old macro from key. */ free((char *)macrotab[kc]); macrotab[kc] = NULL; } keymap[kc] = (KEY) kc; /* Now reprogram the default value for all terminal types */ /* remember to treat Wyse and Televideo terminals special */ /* because of their use of Kverbs for Backspace and Enter */ for (t = 0; t <= TT_MAX; t++) { if ( ISDG200(t) && kc == 264) { extern char * udkfkeys[] ; if (kc == 264) { /* \Kdgbs */ if (udkfkeys[83]) free(udkfkeys[83]); udkfkeys[83] = strdup(s); } } else if (ISWYSE(t) || ISTVI(t)) { extern char * udkfkeys[] ; if (kc == 264) { /* \Kwybs or \Ktvibs */ if (udkfkeys[32]) free(udkfkeys[32]); udkfkeys[32] = strdup(s); } if (kc == 269) { /* \Kwyenter and \Kwyreturn */ if (udkfkeys[39]) /* \Ktvienter and \Ktvireturn */ free(udkfkeys[39]); udkfkeys[39] = strdup(s); if (udkfkeys[49]) free(udkfkeys[49]); udkfkeys[49] = strdup(s); } } else { switch (strlen(s)) { /* Action depends on length */ case 0: /* Clear individual key def */ deletekeymap(t,kc); break; case 1: defevt.type = key; /* Single character */ defevt.key.scancode = *s; break; default: /* Character string */ #ifndef NOKVERBS if (p) { y = xlookup(kverbs,p,nkverbs,&x); /* Look it up */ /* Exact match req'd */ debug(F101,"set key kverb lookup",0,y); if (y > -1) { defevt.type = kverb; defevt.kverb.id = y; break; } } #endif /* NOKVERBS */ defevt.type = macro; defevt.macro.string = (char *) malloc(strlen(s)+1); if (defevt.macro.string) strcpy(defevt.macro.string, s); /* safe */ break; } insertkeymap( t, kc, defevt ) ; initvik = 1; /* Update VIK table */ } } return(1); } #endif /* OS2 */ #ifdef NT int setwin95( void ) { int x, y, z; if (( y = cmkey(win95tab, nwin95, "Windows 95 specific work-arounds", "keyboard-translation", xxstring)) < 0 ) return (y); switch (y) { case XYWPOPUP: if ((y = cmkey(onoff,2,"popups are used to prompt the user for data", "on",xxstring)) < 0) return(y); if ((x = cmcfm()) < 0) return(x); win95_popup = y; return(1); case XYW8_3: if ((y = cmkey(onoff,2,"8.3 FAT file names","off",xxstring)) < 0) return(y); if ((x = cmcfm()) < 0) return(x); win95_8_3 = y; return(1); case XYWSELECT: if ((y = cmkey(onoff,2,"\"select()\" fails on write","off", xxstring)) < 0) return(y); if ((x = cmcfm()) < 0) return(x); win95selectbug = y; return(1); case XYWAGR: if ((y = cmkey(onoff,2,"Right-Alt is Alt-Gr","off",xxstring)) < 0) return(y); if ((x = cmcfm()) < 0) return(x); win95altgr = y; return(1); case XYWOIO: if ((y = cmkey(onoff,2,"Use Overlapped I/O","on",xxstring)) < 0) return(y); if (y) { if ((x = cmnum("Maximum number of outstanding I/O requests", "10",10,&z,xxstring)) < 0) return(x); if (z < 1 || z > 7) { printf( "?Maximum outstanding I/O requests must be between 1 and 7.\n"); return(-9); } } else z = 1; if ((x = cmcfm()) < 0) return(x); owwait = !y; maxow = maxow_usr = z; return(1); case XYWKEY: #ifndef COMMENT printf("\n?\"Keyboard-Translation\" is no longer required.\n"); return(-9); #else /* COMMENT */ if (( z = cmkey(tcstab, ntcs, "Keyboard Character Set", "latin1-iso", xxstring)) < 0) return (z); if ((x = cmcfm()) < 0) return(x); win95kcsi = z; win95kl2 = (win95kcsi == TC_2LATIN); if (win95kcsi == TC_TRANSP) { win95kcs = NULL; } else { #ifdef UNICODE win95kcs = xlr[win95kcsi][tx2fc(tcsl)]; #else /* UNICODE */ win95kcs = xlr[win95kcsi][tcsl]; #endif /* UNICODE */ } return(1); #endif /* COMMENT */ case XYWLUC: if ((y = cmkey(onoff,2,"Unicode-to-Lucida-Console substitutions", "on",xxstring)) < 0) return(y); if ((x = cmcfm()) < 0) return(x); win95lucida = y; return(1); case XYWHSL: if ((y = cmkey(onoff,2,"Horizontal Scan Line substitutions", "on",xxstring)) < 0) return(y); if ((x = cmcfm()) < 0) return(x); win95hsl = y; return(1); default: printf("Illegal value in setwin95()\n"); return(-9); } } #endif /* NT */ #ifdef OS2 int setprty ( #ifdef CK_ANSIC void #endif /* CK_ANSIC */ /* setprty */ ) { int x, y, z; if (( y = cmkey(prtytab, nprty, "priority level of terminal and communication threads", "foreground-server", xxstring)) < 0 ) return (y); if ((x = cmcfm()) < 0) return (x); #ifdef IKSD if (inserver && #ifdef IKSDCONF iksdcf #else 1 #endif /* IKSDCONF */ ) { if ((y = cmcfm()) < 0) return(y); printf("?Sorry, command disabled.\r\n"); return(success = 0); } #endif /* IKSD */ priority = y; return(TRUE); } #endif /* OS2 */ int setbell() { int y, x; #ifdef OS2 int z; #endif /* OS2 */ if ((y = cmkey(beltab,nbeltab, #ifdef OS2 "how console and terminal bells should\nbe generated", "audible", #else "Whether Kermit should ring the terminal bell (beep)", "on", #endif /* OS2 */ xxstring)) < 0) return(y); #ifdef IKSD if (inserver) { if ((y = cmcfm()) < 0) return(y); printf("?Sorry, command disabled.\r\n"); return(success = 0); } #endif /* IKSD */ switch (y) { /* SET BELL */ case XYB_NONE: #ifdef OS2 case XYB_VIS: #endif /* OS2 */ if ((x = cmcfm()) < 0) return(x); #ifdef OS2 tt_bell = y; #else tt_bell = 0; #endif /* OS2 */ break; case XYB_AUD: #ifdef OS2 if ((x = cmkey(audibletab, naudibletab, "how audible console and terminal\nbells should be generated", "beep",xxstring))<0) return(x); if ((z = cmcfm()) < 0) return(z); tt_bell = y | x; #else /* This lets C-Kermit accept but ignore trailing K95 keywords */ if ((x = cmtxt("Confirm with carriage return","",&s,xxstring)) < 0) return(x); tt_bell = 1; #endif /* OS2 */ break; } return(1); } #ifdef OS2MOUSE int setmou( #ifdef CK_ANSIC void #endif /* CK_ANSIC */ /* setmou */ ) { extern int initvik; int button = 0, event = 0; char * p; if ((y = cmkey(mousetab,nmtab,"","",xxstring)) < 0) return(y); #ifdef IKSD if (inserver) { if ((y = cmcfm()) < 0) return(y); printf("?Sorry, command disabled.\r\n"); return(success = 0); } #endif /* IKSD */ if (y == XYM_ON) { /* MOUSE ACTIVATION */ int old_mou = tt_mouse; if ((x = seton(&tt_mouse)) < 0) return(x); if (tt_mouse != old_mou) if (tt_mouse) os2_mouseon(); else os2_mouseoff(); return(1); } if (y == XYM_DEBUG) { /* MOUSE DEBUG */ extern int MouseDebug; if ((x = seton(&MouseDebug)) < 0) return(x); return(1); } if (y == XYM_CLEAR) { /* Reset Mouse Defaults */ if ((x = cmcfm()) < 0) return(x); mousemapinit(-1,-1); initvik = 1; /* Update VIK Table */ return 1; } if (y != XYM_BUTTON) { /* Shouldn't happen. */ printf("Internal parsing error\n"); return(-9); } /* MOUSE EVENT ... */ if ((button = cmkey(mousebuttontab,nmbtab, "Button number","1", xxstring)) < 0) return(button); if ((y = cmkey(mousemodtab,nmmtab, "Keyboard modifier","none", xxstring)) < 0) return(y); event |= y; /* OR in the bits */ if ((y = cmkey(mclicktab,nmctab,"","click",xxstring)) < 0) return(y); /* Two bits are assigned, if neither are set then it is button one */ event |= y; /* OR in the bit */ wideresult = -1; if ((y = cmtxt("definition,\n\ or Ctrl-C to cancel this command,\n\ or Enter to restore default definition", "",&s,NULL)) < 0) { return(y); } s = brstrip(s); p = s; /* Save this place */ /* If the definition included any \Kverbs, quote the backslash so the \Kverb will still be in the definition when the key is pressed. We don't do this in zzstring(), because \Kverbs are valid only in this context and nowhere else. This code copied from SET KEY, q.v. for addt'l commentary. */ for (x = 0, y = 0; s[x]; x++, y++) { /* Convert \K to \\K */ if ((x > 0) && (s[x] == 'K' || s[x] == 'k') ) { /* Have K */ if ((x == 1 && s[x-1] == CMDQ) || (x > 1 && s[x-1] == CMDQ && s[x-2] != CMDQ)) { line[y++] = CMDQ; /* Make it \\K */ } if (x > 1 && s[x-1] == '{' && s[x-2] == CMDQ) { line[y-1] = CMDQ; /* Have \{K */ line[y++] = '{'; /* Make it \\{K */ } } line[y] = s[x]; } line[y++] = NUL; /* Terminate */ s = line + y + 1; /* Point to after it */ x = LINBUFSIZ - (int) strlen(line) - 1; /* Calculate remaining space */ if ((x < (LINBUFSIZ / 2)) || (zzstring(line, &s, &x) < 0)) { /* Expand variables, etc. */ printf("?Key definition too long\n"); return(-9); } s = line + y + 1; /* Point to result. */ #ifndef NOKVERBS /* Special case: see if the definition starts with a \Kverb. If it does, point to it with p, otherwise set p to NULL. */ p = s; if (*p++ == CMDQ) { if (*p == '{') p++; p = (*p == 'k' || *p == 'K') ? p + 1 : NULL; } #else p = NULL; #endif /* NOKVERBS */ /* free the old definition if necessary */ if (mousemap[button][event].type == macro) { free( mousemap[button][event].macro.string); mousemap[button][event].macro.string = NULL; } switch (strlen(s)) { /* Action depends on length */ case 0: /* Reset to default binding */ mousemapinit( button, event ); break; case 1: /* Single character */ mousemap[button][event].type = key; mousemap[button][event].key.scancode = *s; break; default: /* Character string */ #ifndef NOKVERBS if (p) { y = xlookup(kverbs,p,nkverbs,&x); /* Look it up */ debug(F101,"set mouse kverb lookup",0,y); /* need exact match */ if (y > -1) { /* Assign the kverb to the event */ mousemap[button][event].type = kverb; mousemap[button][event].kverb.id = F_KVERB | y; break; } } #endif /* NOKVERBS */ /* Otherwise, it's a macro, so assign the macro to the event */ mousemap[button][event].type = macro; mousemap[button][event].macro.string = (MACRO) malloc(strlen(s)+1); if (mousemap[button][event].macro.string) strcpy((char *) mousemap[button][event].macro.string, s); /* safe */ break; } initvik = 1; /* Update VIK Table */ if ( (button == XYM_B3) && (mousebuttoncount() < 3) && !quiet ) { printf("?Warning: this machine does not have a three button mouse.\n"); return(0); } return(1); } #endif /* OS2MOUSE */ #endif /* NOLOCAL */ #ifndef NOXFER int /* SET SEND/RECEIVE */ setsr(xx, rmsflg) int xx; int rmsflg; { if (xx == XYRECV) ckstrncpy(line,"Parameter for inbound packets",LINBUFSIZ); else ckstrncpy(line,"Parameter for outbound packets",LINBUFSIZ); if (rmsflg) { if ((y = cmkey(rsrtab,nrsrtab,line,"",xxstring)) < 0) { if (y == -3) { printf("?Remote receive parameter required\n"); return(-9); } else return(y); } } else { if ((y = cmkey(srtab,nsrtab,line,"",xxstring)) < 0) return(y); } switch (y) { case XYQCTL: /* CONTROL-PREFIX */ if ((x = cmnum("ASCII value of control prefix","",10,&y,xxstring)) < 0) return(x); if ((x = cmcfm()) < 0) return(x); if ((y > 32 && y < 63) || (y > 95 && y < 127)) { if (xx == XYRECV) ctlq = (CHAR) y; /* RECEIVE prefix, use with caution! */ else myctlq = (CHAR) y; /* SEND prefix, OK to change */ return(success = 1); } else { printf("?Illegal value for prefix character\n"); return(-9); } case XYEOL: if ((y = setcc("13",&z)) < 0) return(y); if (z > 31) { printf("Sorry, the legal values are 0-31\n"); return(-9); } if (xx == XYRECV) eol = (CHAR) z; else seol = (CHAR) z; return(success = y); case XYLEN: y = cmnum("Maximum number of characters in a packet","90",10,&x, xxstring); if (xx == XYRECV) { /* Receive... */ if ((y = setnum(&z,x,y,maxrps)) < 0) return(y); if (protocol != PROTO_K) { printf("?Sorry, this command does not apply to %s protocol.\n", ptab[protocol].p_name ); printf("Use SET SEND PACKET-LENGTH for XYZMODEM\n"); return(-9); } if (z < 10) { printf("Sorry, 10 is the minimum\n"); return(-9); } if (rmsflg) { sstate = setgen('S', "401", ckitoa(z), ""); return((int) sstate); } else { if (protocol == PROTO_K) { if (z > MAXRP) z = MAXRP; y = adjpkl(z,wslotr,bigrbsiz); if (y != z) { urpsiz = y; if (!xcmdsrc) if (msgflg) printf( " Adjusting receive packet-length to %d for %d window slots\n", y, wslotr); } urpsiz = y; ptab[protocol].rpktlen = urpsiz; rpsiz = (y > 94) ? 94 : y; } else { #ifdef CK_XYZ if ((protocol == PROTO_X || protocol == PROTO_XC) && z != 128 && z != 1024) { printf("Sorry, bad packet length for XMODEM.\n"); printf("Please use 128 or 1024.\n"); return(-9); } #endif /* CK_XYZ */ urpsiz = rpsiz = z; } } } else { /* Send... */ if ((y = setnum(&z,x,y,maxsps)) < 0) return(y); if (z < 10) { printf("Sorry, 10 is the minimum\n"); return(-9); } if (protocol == PROTO_K) { if (z > MAXSP) z = MAXSP; spsiz = z; /* Set it */ y = adjpkl(spsiz,wslotr,bigsbsiz); if (y != spsiz && !xcmdsrc) if (msgflg) printf("Adjusting packet size to %d for %d window slots\n", y,wslotr); } else y = z; #ifdef CK_XYZ if ((protocol == PROTO_X || protocol == PROTO_XC) && z != 128 && z != 1024) { printf("Sorry, bad packet length for XMODEM.\n"); printf("Please use 128 or 1024.\n"); return(-9); } #endif /* CK_XYZ */ spsiz = spmax = spsizr = y; /* Set it and flag that it was set */ spsizf = 1; /* to allow overriding Send-Init. */ ptab[protocol].spktflg = spsizf; ptab[protocol].spktlen = spsiz; } if (pflag && protocol == PROTO_K && !xcmdsrc) { if (z > 94 && !reliable && msgflg) { /* printf("Extended-length packets requested.\n"); */ if (bctr < 2 && z > 200) printf("\ Remember to SET BLOCK 2 or 3 for long packets.\n"); } if (speed <= 0L) speed = ttgspd(); #ifdef COMMENT /* Kermit does this now itself. */ if (speed <= 0L && z > 200 && msgflg) { printf("\ Make sure your timeout interval is long enough for %d-byte packets.\n",z); } #endif /* COMMENT */ } return(success = y); case XYMARK: #ifdef DOOMSDAY /* Printable start-of-packet works for UNIX and VMS only! */ x_ifnum = 1; y = cmnum("Code for packet-start character","1",10,&x,xxstring); x_ifnum = 0; if ((y = setnum(&z,x,y,126)) < 0) return(y); #else if ((y = setcc("1",&z)) < 0) return(y); #endif /* DOOMSDAY */ if (xx == XYRECV) stchr = (CHAR) z; else { mystch = (CHAR) z; #ifdef IKS_OPTION /* If IKS negotiation in use */ if (TELOPT_U(TELOPT_KERMIT) || TELOPT_ME(TELOPT_KERMIT)) tn_siks(KERMIT_SOP); /* Report change to other side */ #endif /* IKS_OPTION */ } return(success = y); case XYNPAD: /* PADDING */ y = cmnum("How many padding characters for inbound packets","0",10,&x, xxstring); if ((y = setnum(&z,x,y,94)) < 0) return(y); if (xx == XYRECV) mypadn = (CHAR) z; else npad = (CHAR) z; return(success = y); case XYPADC: /* PAD-CHARACTER */ if ((y = setcc("0",&z)) < 0) return(y); if (xx == XYRECV) mypadc = z; else padch = z; return(success = y); case XYTIMO: /* TIMEOUT */ if (xx == XYRECV) { y = cmnum("Packet timeout interval",ckitoa(URTIME),10,&x,xxstring); if ((y = setnum(&z,x,y,94)) < 0) return(y); if (rmsflg) { /* REMOTE SET RECEIVE TIMEOUT */ sstate = setgen('S', "402", ckitoa(z), ""); return((int) sstate); } else { /* SET RECEIVE TIMEOUT */ pkttim = z; /* Value to put in my negotiation */ } /* packet for other Kermit to use */ } else { /* SET SEND TIMEOUT */ #ifdef CK_TIMERS extern int rttflg, mintime, maxtime; int tmin = 0, tmax = 0; #endif /* CK_TIMERS */ y = cmnum("Packet timeout interval",ckitoa(DMYTIM),10,&x,xxstring); if (y == -3) { /* They cancelled a previous */ x = DMYTIM; /* SET SEND command, so restore */ timef = 0; /* and turn off the override flag */ y = cmcfm(); } #ifdef CK_TIMERS if (y < 0) return(y); if (x < 0) { printf("?Out of range - %d\n",x); return(-9); } if ((z = cmkey(timotab,2,"","dynamic",xxstring)) < 0) return(z); if (z) { if ((y = cmnum("Minimum timeout to allow", "1",10,&tmin,xxstring)) < 0) return(y); if (tmin < 1) { printf("?Out of range - %d\n",tmin); return(-9); } if ((y = cmnum("Maximum timeout to allow", "0",10,&tmax,xxstring)) < 0) return(y); /* 0 means let Kermit choose, < 0 means no maximum */ } if ((y = cmcfm()) < 0) return(y); rttflg = z; /* Round-trip timer flag */ z = x; #else if ((y = setnum(&z,x,y,94)) < 0) return(y); #endif /* CK_TIMERS */ timef = 1; /* Turn on the override flag */ timint = rtimo = z; /* Override value for me to use */ #ifdef CK_TIMERS if (rttflg) { /* Lower and upper bounds */ mintime = tmin; maxtime = tmax; } #endif /* CK_TIMERS */ } return(success = 1); case XYFPATH: /* PATHNAMES */ if (xx == XYRECV) { y = cmkey(rpathtab,nrpathtab,"","auto",xxstring); } else { y = cmkey(pathtab,npathtab,"","off",xxstring); } if (y < 0) return(y); if ((x = cmcfm()) < 0) return(x); if (xx == XYRECV) { /* SET RECEIVE PATHNAMES */ fnrpath = y; ptab[protocol].fnrp = fnrpath; } else { /* SET SEND PATHNAMES */ fnspath = y; ptab[protocol].fnsp = fnspath; } return(success = 1); /* Note: 0 = ON, 1 = OFF */ /* In other words, ON = leave pathnames ON, OFF = take them off. */ case XYPAUS: /* SET SEND/RECEIVE PAUSE */ y = cmnum("Milliseconds to pause between packets","0",10,&x,xxstring); if ((y = setnum(&z,x,y,15000)) < 0) return(y); pktpaus = z; return(success = 1); #ifdef CKXXCHAR /* SET SEND/RECEIVE IGNORE/DOUBLE */ case XYIGN: case XYDBL: { int i, zz; short *p; extern short dblt[]; extern int dblflag, ignflag; /* Make space for a temporary copy of the ignore/double table */ zz = y; #ifdef COMMENT if (zz == XYIGN && xx == XYSEND) { blah blah who cares } if (zz == XYDBL && xx == XYRECV) { blah blah } #endif /* COMMENT */ p = (short *)malloc(256 * sizeof(short)); if (!p) { printf("?Internal error - malloc failure\n"); return(-9); } for (i = 0; i < 256; i++) p[i] = dblt[i]; /* Copy current table */ while (1) { /* Collect a list of numbers */ #ifndef NOSPL x_ifnum = 1; /* Turn off complaints from eval() */ #endif /* NOSPL */ if ((x = cmnum(zz == XYDBL ? "Character to double" : "Character to ignore", "",10,&y,xxstring )) < 0) { #ifndef NOSPL x_ifnum = 0; #endif /* NOSPL */ if (x == -3) /* Done */ break; if (x == -2) { if (p) { free(p); p = NULL; } debug(F110,"SET S/R DOUBLE/IGNORE atmbuf",atmbuf,0); if (!ckstrcmp(atmbuf,"none",4,0) || !ckstrcmp(atmbuf,"non",3,0) || !ckstrcmp(atmbuf,"no",2,0) || !ckstrcmp(atmbuf,"n",1,0)) { if ((x = cmcfm()) < 0) /* Get confirmation */ return(x); for (y = 0; y < 256; y++) dblt[y] &= (zz == XYDBL) ? 1 : 2; if (zz == XYDBL) dblflag = 0; if (zz == XYIGN) ignflag = 0; return(success = 1); } else { printf( "?Please specify a number or the word NONE\n"); return(-9); } } else { free(p); p = NULL; return(x); } } #ifndef NOSPL x_ifnum = 0; #endif /* NOSPL */ if (y < 0 || y > 255) { printf("?Please enter a character code in range 0-255\n"); free(p); p = NULL; return(-9); } p[y] |= (zz == XYDBL) ? 2 : 1; if (zz == XYDBL) dblflag = 1; if (zz == XYIGN) ignflag = 1; } /* End of while loop */ if ((x = cmcfm()) < 0) return(x); /* Get here only if they have made no mistakes. Copy temporary table back to permanent one, then free temporary table and return successfully. */ if (p) { for (i = 0; i < 256; i++) dblt[i] = p[i]; free(p); p = NULL; } return(success = 1); } #endif /* CKXXCHAR */ #ifdef PIPESEND case XYFLTR: { /* SET { SEND, RECEIVE } FILTER */ if ((y = cmtxt((xx == XYSEND) ? "Filter program for sending files -\n\ use \\v(filename) to substitute filename" : "Filter program for receiving files -\n\ use \\v(filename) to substitute filename", "",&s,NULL)) < 0) return(y); if (!*s) { /* Removing a filter... */ if (xx == XYSEND && sndfilter) { makestr(&g_sfilter,NULL); makestr(&sndfilter,NULL); } else if (rcvfilter) { makestr(&g_rfilter,NULL); makestr(&rcvfilter,NULL); } return(success = 1); } /* Adding a filter... */ s = brstrip(s); /* Strip any braces */ y = strlen(s); if (xx == XYSEND) { /* For SEND filter... */ for (x = 0; x < y; x++) { /* make sure they included "\v(...)" */ if (s[x] != '\\') continue; if (s[x+1] == 'v') break; } if (x == y) { printf( "?Filter must contain a replacement variable for filename.\n" ); return(-9); } } if (xx == XYSEND) { makestr(&sndfilter,s); makestr(&g_sfilter,s); } else { makestr(&rcvfilter,s); makestr(&g_rfilter,s); } return(success = 1); } #endif /* PIPESEND */ case XYINIL: y = cmnum("Max length for protocol init string","-1",10,&x,xxstring); if ((y = setnum(&z,x,y,-1)) < 0) return(y); if (xx == XYSEND) sprmlen = z; else rprmlen = z; return(success = 1); case 993: { extern int sendipkts; if (xx == XYSEND) { if ((x = seton(&sendipkts)) < 0) return(x); } return(1); } #ifdef CK_PERMS case 994: switch(xx) { case XYSEND: if ((x = seton(&atlpro)) < 0) return(x); atgpro = atlpro; return(1); case XYRECV: if ((x = seton(&atlpri)) < 0) return(x); atgpri = atlpri; return(1); default: return(-2); } #endif /* CK_PERMS */ #ifndef NOCSETS case XYCSET: { /* CHARACTER-SET-SELECTION */ extern struct keytab xfrmtab[]; extern int r_cset, s_cset; if ((y = cmkey(xfrmtab,2,"","automatic",xxstring)) < 0) return(y); if ((x = cmcfm()) < 0) return(x); if (xx == XYSEND) s_cset = y; else r_cset = y; return(success = 1); } #endif /* NOCSETS */ case XYBUP: if ((y = cmkey(onoff,2,"","on",xxstring)) < 0) return(y); if ((x = cmcfm()) < 0) return(x); if (xx == XYSEND) { extern int skipbup; skipbup = (y == 0) ? 1 : 0; return(success = 1); } else { printf( "?Please use SET FILE COLLISION to choose the desired action\n"); return(-9); } case XYMOVE: #ifdef COMMENT y = cmdir("Directory to move file(s) to after successful transfer", "",&s,xxstring); #else y = cmtxt("Directory to move file(s) to after successful transfer", "",&s,xxstring); #endif /* COMMENT */ if (y < 0 && y != -3) return(y); ckstrncpy(line,s,LINBUFSIZ); s = brstrip(line); #ifdef COMMENT /* Only needed for cmdir() */ if ((x = cmcfm()) < 0) return(x); #endif /* COMMENT */ /* Check directory existence if absolute */ /* THIS MEANS IT CAN'T INCLUDE ANY DEFERRED VARIABLES! */ if (s) if (*s) { if (isabsolute(s) && !isdir(s)) { printf("?Directory does not exist - %s\n",s); return(-9); } } if (xx == XYSEND) { if (*s) { #ifdef COMMENT /* Allow it to be relative */ zfnqfp(s,LINBUFSIZ,line); #endif /* COMMENT */ makestr(&snd_move,line); makestr(&g_snd_move,line); } else { makestr(&snd_move,NULL); makestr(&g_snd_move,NULL); } } else { if (*s) { #ifdef COMMENT /* Allow it to be relative */ zfnqfp(s,LINBUFSIZ,line); #endif /* COMMENT */ makestr(&rcv_move,line); makestr(&g_rcv_move,line); } else { makestr(&rcv_move,NULL); makestr(&g_rcv_move,NULL); } } return(success = 1); case XYRENAME: y = cmtxt("Template to rename file(s) to after successful transfer", "",&s,NULL); /* NOTE: no xxstring */ if (y < 0 && y != -3) /* Evaluation is deferred */ return(y); ckstrncpy(line,s,LINBUFSIZ); s = brstrip(line); if ((x = cmcfm()) < 0) return(x); if (xx == XYSEND) { if (*s) { makestr(&snd_rename,s); makestr(&g_snd_rename,s); } else { makestr(&snd_rename,NULL); makestr(&g_snd_rename,NULL); } } else { if (*s) { makestr(&rcv_rename,s); makestr(&g_rcv_rename,s); } else { makestr(&rcv_rename,NULL); makestr(&g_rcv_rename,NULL); } } return(success = 1); #ifdef VMS case 887: /* VERSION-NUMBERS */ if (xx == XYSEND) { extern int vmssversions; return(seton(&vmssversions)); } else { extern int vmsrversions; return(seton(&vmsrversions)); } #endif /* VMS */ default: return(-2); } /* End of SET SEND/RECEIVE... */ } #endif /* NOXFER */ #ifndef NOXMIT int setxmit() { if ((y = cmkey(xmitab,nxmit,"","",xxstring)) < 0) return(y); switch (y) { case XMITE: /* EOF */ y = cmtxt("Characters to send at end of file,\n\ Use backslash codes for control characters","",&s,xxstring); if (y < 0) return(y); if ((int)strlen(s) > XMBUFL) { printf("?Too many characters, %d maximum\n",XMBUFL); return(-2); } ckstrncpy(xmitbuf,s,XMBUFL); return(success = 1); case XMITF: /* Fill */ y = cmnum("Numeric code for blank-line fill character","0",10,&x, xxstring); if ((y = setnum(&z,x,y,127)) < 0) return(y); xmitf = z; return(success = 1); case XMITL: /* Linefeed */ return(seton(&xmitl)); case XMITS: /* Locking-Shift */ return(seton(&xmits)); case XMITP: /* Prompt */ y = cmnum("Numeric code for host's prompt character, 0 for none", "10",10,&x,xxstring); if ((y = setnum(&z,x,y,127)) < 0) return(y); xmitp = z; return(success = 1); case XMITX: /* Echo */ return(seton(&xmitx)); case XMITW: /* Pause */ y = cmnum("Number of milliseconds to pause between binary characters\n\ or text lines during transmission","0",10,&x,xxstring); if ((y = setnum(&z,x,y,1000)) < 0) return(y); xmitw = z; return(success = 1); case XMITT: /* Timeout */ y = cmnum("Seconds to wait for each character to echo", "1",10,&x,xxstring); if ((y = setnum(&z,x,y,1000)) < 0) return(y); xmitt = z; return(success = 1); default: return(-2); } } #endif /* NOXMIT */ #ifndef NOXFER /* D O R M T -- Do a remote command */ VOID rmsg() { if (pflag && !quiet && fdispla != XYFD_N) printf( #ifdef CK_NEED_SIG " Type your escape character, %s, followed by X or E to cancel.\n", dbchr(escape) #else " Press the X or E key to cancel.\n" #endif /* CK_NEED_SIG */ ); } static int xzcmd = 0; /* Global copy of REMOTE cmd index */ /* R E M C F M -- Confirm a REMOTE command */ /* Like cmcfm(), but allows for a redirection indicator on the end, like "> filename" or "| command". Returns what cmcfm() would have returned: -1 if reparse needed, etc etc blah blah. On success, returns 1 with: char * remdest containing the name of the file or command. int remfile set to 1 if there is to be any redirection. int remappd set to 1 if output file is to be appended to. int rempipe set to 1 if remdest is a command, 0 if it is a file. */ static int remcfm() { int x; char *s; char c; remfile = 0; rempipe = 0; remappd = 0; if ((x = cmtxt( "> filename, | command,\n\ or type carriage return to confirm the command", "",&s,xxstring)) < 0) return(x); if (remdest) { free(remdest); remdest = NULL; } debug(F101,"remcfm local","",local); debug(F110,"remcfm s",s,0); debug(F101,"remcfm cmd","",xzcmd); /* This check was added in C-Kermit 6.0 or 7.0 but it turns out to be unhelpful in the situation where the remote is running a script that sends REMOTE commands to the local workstation. What happens is, the local server executes the command and sends the result back as screen text, which is indicated by using an X packet instead of an F packet as the file header. There are two parts to this: executing the command under control of the remote Kermit, which is desirable (and in fact some big applications depend on it, and therefore never installed any new C-Kermit versions after 5A), and displaying the result. Commenting out the check allows the command to be executed, but the result is still sent back to the remote in a file transfer, where it vanishes into the ether. Actually it's on the communication connection, mixed in with the packets. Pretty amazing that the file transfer still works, right? */ #ifdef COMMENT if (!*s) { /* No redirection indicator */ if (!local && (xzcmd == XZDIR || xzcmd == XZTYP || xzcmd == XZXIT || xzcmd == XZSPA || xzcmd == XZHLP || xzcmd == XZPWD || xzcmd == XZLGI || xzcmd == XZLGO || xzcmd == XZWHO || xzcmd == XZHOS)) { printf("?\"%s\" has no effect in remote mode\n",cmdbuf); return(-9); } else return(1); } #endif /* COMMENT */ c = *s; /* We have something */ if (c != '>' && c != '|') { /* Is it > or | ? */ printf("?Not confirmed\n"); /* No */ return(-9); } s++; /* See what follows */ if (c == '>' && *s == '>') { /* Allow for ">>" too */ s++; remappd = 1; /* Append to output file */ } while (*s == SP || *s == HT) s++; /* Strip intervening whitespace */ if (!*s) { printf("?%s missing\n", c == '>' ? "Filename" : "Command"); return(-9); } if (c == '>' && zchko(s) < 0) { /* Check accessibility */ printf("?Access denied - %s\n", s); return(-9); } remfile = 1; /* Set global results */ rempipe = (c == '|'); if (rempipe #ifndef NOPUSH && nopush #endif /* NOPUSH */ ) { printf("?Sorry, access to external commands is disabled.\n"); return(-9); } makestr(&remdest,s); #ifndef NODEBUG if (deblog) { debug(F101,"remcfm remfile","",remfile); debug(F101,"remcfm remappd","",remappd); debug(F101,"remcfm rempipe","",rempipe); debug(F110,"remcfm remdest",remdest, 0); } #endif /* NODEBUG */ return(1); } /* R E M T X T -- Like remcfm()... */ /* ... but for REMOTE commands that end with cmtxt(). Here we must decipher braces to discover whether the trailing redirection indicator is intended for local use, or to be sent out to the server, as in: remote host blah blah > file This end remote host { blah blah } > file This end remote host { blah blah > file } That end remote host { blah blah > file } > file Both ends Pipes too: remote host blah blah | cmd This end remote host { blah blah } | cmd This end remote host { blah blah | cmd } That end remote host { blah blah | cmd } | cmd Both ends Or both: remote host blah blah | cmd > file This end, etc etc... Note: this really only makes sense for REMOTE HOST, but why be picky? Call after calling cmtxt(), with pointer to string that cmtxt() parsed, as in "remtxt(&s);". Returns: 1 on success with braces & redirection things removed & pointer updated, -9 on failure (bad indirection), after printing error message. */ int remtxt(p) char ** p; { int i, x, bpos, ppos; char c, *s, *q; remfile = 0; /* Initialize global results */ rempipe = 0; remappd = 0; if (remdest) { free(remdest); remdest = NULL; } s = *p; if (!s) /* No redirection indicator */ s = ""; #ifdef COMMENT if (!*s) { /* Ditto */ if (!local && (xzcmd == XZDIR || xzcmd == XZTYP || xzcmd == XZXIT || xzcmd == XZSPA || xzcmd == XZHLP || xzcmd == XZPWD || xzcmd == XZLGI || xzcmd == XZLGO || xzcmd == XZWHO || xzcmd == XZHOS)) { printf("?\"%s\" has no effect in remote mode\n",cmdbuf); if (hints) { printf("Hint: Try again with an output redirector.\n"); } return(-9); } else return(1); } #endif /* COMMENT */ bpos = -1; /* Position of > (bracket) */ ppos = -1; /* Position of | (pipe) */ x = strlen(s); /* Length of cmtxt() string */ for (i = x-1; i >= 0; i--) { /* Search right to left. */ c = s[i]; if (c == '}') /* Break on first right brace */ break; /* Don't look at contents of braces */ else if (c == '>') /* Record position of > */ bpos = i; else if (c == '|') /* and of | */ ppos = i; } if (bpos < 0 && ppos < 0) { /* No redirectors. */ #ifdef COMMENT if (!local && (xzcmd == XZDIR || xzcmd == XZTYP || xzcmd == XZXIT || xzcmd == XZSPA || xzcmd == XZHLP || xzcmd == XZPWD || xzcmd == XZLGI || xzcmd == XZLGO || xzcmd == XZWHO || xzcmd == XZHOS)) { printf("?\"%s\" has no effect in remote mode\n",cmdbuf); if (hints) { printf("Hint: Try again with an output redirector.\n"); } return(-9); } #endif /* COMMENT */ s = brstrip(s); /* Remove outer braces if any. */ *p = s; /* Point to result */ return(1); /* and return. */ } remfile = 1; /* It's | or > */ i = -1; /* Get leftmost symbol */ if (bpos > -1) /* Bracket */ i = bpos; if (ppos > -1 && (ppos < bpos || bpos < 0)) { /* or pipe */ i = ppos; rempipe = 1; } if (rempipe #ifndef NOPUSH && nopush #endif /* NOPUSH */ ) { printf("?Sorry, access to external commands is disabled.\n"); return(-9); } c = s[i]; /* Copy of symbol */ if (c == '>' && s[i+1] == '>') /* ">>" for append? */ remappd = 1; /* It's not just a flag it's a number */ q = s + i + 1 + remappd; /* Point past symbol in string */ while (*q == SP || *q == HT) q++; /* and any intervening whitespace */ if (!*q) { printf("?%s missing\n", c == '>' ? "Filename" : "Command"); return(-9); } if (c == '>' && zchko(q) < 0) { /* (Doesn't work for | cmd > file) */ printf("?Access denied - %s\n", q); return(-9); } makestr(&remdest,q); /* Create the destination string */ q = s + i - 1; /* Point before symbol */ while (q > s && (*q == SP || *q == HT)) /* Strip trailing whitespace */ q--; *(q+1) = NUL; /* Terminate the string. */ s = brstrip(s); /* Remove any braces */ *p = s; /* Set return value */ #ifndef NODEBUG if (deblog) { debug(F101,"remtxt remfile","",remfile); debug(F101,"remtxt remappd","",remappd); debug(F101,"remtxt rempipe","",rempipe); debug(F110,"remtxt remdest",remdest, 0); debug(F110,"remtxt command",s,0); } #endif /* NODEBUG */ return(1); } int plogin(xx) int xx; { char *p1 = NULL, *p2 = NULL, *p3 = NULL; int psaved = 0, rc = 0; #ifdef CK_RECALL extern int on_recall; /* around Password prompting */ #endif /* CK_RECALL */ debug(F101,"plogin local","",local); if (!local || (network && ttchk() < 0)) { printf("?No connection\n"); return(-9); } if ((x = cmfld("User ID","",&s,xxstring)) < 0) { /* Get User ID */ if (x != -3) return(x); } y = strlen(s); if (y > 0) { if ((p1 = malloc(y + 1)) == NULL) { printf("?Internal error: malloc\n"); rc = -9; goto XZXLGI; } else strcpy(p1,s); /* safe */ if ((rc = cmfld("Password","",&s,xxstring)) < 0) if (rc != -3) goto XZXLGI; y = strlen(s); if (y > 0) { if ((p2 = malloc(y + 1)) == NULL) { printf("?Internal error: malloc\n"); rc = -9; goto XZXLGI; } else strcpy(p2,s); /* safe */ if ((rc = cmfld("Account","",&s,xxstring)) < 0) if (rc != -3) goto XZXLGI; y = strlen(s); if (y > 0) { if ((p3 = malloc(y + 1)) == NULL) { printf("?Internal error: malloc\n"); rc = -9; goto XZXLGI; } else strcpy(p3,s); /* safe */ } } } if ((rc = remtxt(&s)) < 0) /* Confirm & handle redirectors */ goto XZXLGI; if (!p1) { /* No Userid specified... */ debok = 0; /* Don't log this */ /* Prompt for username, password, and account */ #ifdef CK_RECALL on_recall = 0; #endif /* CK_RECALL */ cmsavp(psave,PROMPTL); /* Save old prompt */ psaved = 1; debug(F110,"REMOTE LOGIN saved",psave,0); cmsetp("Username: "); /* Make new prompt */ concb((char)escape); /* Put console in cbreak mode */ cmini(1); prompt(xxstring); rc = -9; for (x = -1; x < 0; ) { /* Prompt till they answer */ cmres(); /* Reset the parser */ x = cmtxt("","",&s,NULL); /* Get a literal line of text */ } y = strlen(s); if (y < 1) { printf("?Canceled\n"); goto XZXLGI; } if ((p1 = malloc(y + 1)) == NULL) { printf("?Internal error: malloc\n"); goto XZXLGI; } else strcpy(p1,s); /* safe */ cmsetp("Password: "); /* Make new prompt */ concb((char)escape); /* Put console in cbreak mode */ cmini(0); /* No echo */ prompt(xxstring); debok = 0; for (x = -1; x < 0 && x != -3; ) { /* Get answer */ cmres(); /* Reset the parser */ x = cmtxt("","",&s,NULL); /* Get literal line of text */ } if ((p2 = malloc((int)strlen(s) + 1)) == NULL) { printf("?Internal error: malloc\n"); goto XZXLGI; } else strcpy(p2,s); /* safe */ printf("\r\n"); if ((rc = cmcfm()) < 0) goto XZXLGI; } sstate = setgen('I',p1,p2,p3); /* Get here with at least user ID */ rc = 0; XZXLGI: /* Common exit point */ if (psaved) cmsetp(psave); /* Restore original prompt */ if (p3) { free(p3); p3 = NULL; } /* Free malloc'd storage */ if (p2) { free(p2); p2 = NULL; } if (p1) { free(p1); p1 = NULL; } if (rc > -1) { if (local && rc > -1) /* If local, flush tty input buffer */ ttflui(); } return(rc); } #ifdef OS2 #ifndef NOLOCAL int dormt(xx) int xx; { int rc = 0; extern int term_io; int term_io_sav = term_io; #ifdef NEWFTP extern int ftpget, ftpisopen(); if ((ftpget == 1) || ((ftpget == 2) && ftpisopen())) return(doftprmt(xx,0)); #endif /* NEWFTP */ term_io = 0; rc = xxdormt(xx); term_io = term_io_sav; return rc; } int xxdormt(xx) int xx; #else /* NOLOCAL */ int dormt(xx) int xx; #endif /* NOLOCAL */ #else /* OS2 */ int dormt(xx) int xx; #endif /* OS2 */ { /* REMOTE commands */ int x, y, retcode; char *s, sbuf[50], *s2; #ifdef NEWFTP extern int ftpget, ftpisopen(); if ((ftpget == 1) || ((ftpget == 2) && ftpisopen())) return(doftprmt(xx,0)); #endif /* NEWFTP */ remfile = 0; /* Clear these */ rempipe = 0; remappd = 0; if (xx < 0) return(xx); /* REMOTE what? */ xzcmd = xx; /* Make global copy of arg */ if (xx == XZSET) { /* REMOTE SET */ if ((y = cmkey(rmstab,nrms,"","",xxstring)) < 0) { if (y == -3) { printf("?Parameter name required\n"); return(-9); } else return(y); } return(doprm(y,1)); } switch (xx) { /* Others... */ case XZCDU: if ((x = cmcfm()) < 0) return(x); printf("?Sorry, REMOTE CDUP not supported yet\n"); return(-9); case XZCWD: /* CWD (CD) */ if ((x = cmtxt("Remote directory name","",&s,xxstring)) < 0) return(x); if ((x = remtxt(&s)) < 0) return(x); debug(F111,"XZCWD: ",s,x); *sbuf = NUL; s2 = sbuf; /* The following is commented out because since the disappearance of the DECSYSTEM-20 from the planet, no known computer requires a password for changing directory. */ #ifdef DIRPWDPR if (*s != NUL) { /* If directory name given, */ /* get password on separate line. */ if (tlevel > -1) { /* From take file... */ if (fgets(sbuf,50,tfile[tlevel]) == NULL) fatal("take file ends prematurely in 'remote cwd'"); debug(F110," pswd from take file",s2,0); for (x = (int)strlen(sbuf); x > 0 && (sbuf[x-1] == NL || sbuf[x-1] == CR); x--) sbuf[x-1] = '\0'; } else { /* From terminal... */ printf(" Password: "); /* get a password */ #ifdef IKSD if (!local && inserver) { x = coninc(0); } else #endif /* IKSD */ #ifdef OS2 x = is_a_tty(0) ? coninc(0) : /* with no echo ... */ getchar(); #else /* OS2 */ x = getchar(); #endif /* OS2 */ while ((x != NL) && (x != CR)) { if ((x &= 0177) == '?') { printf("? Password of remote directory\n Password: "); s2 = sbuf; *sbuf = NUL; } else if (x == ESC) /* Mini command line editor... */ bleep(BP_WARN); else if (x == BS || x == 0177) s2--; else if (x == 025) { /* Ctrl-U */ s2 = sbuf; *sbuf = NUL; } else *s2++ = x; /* Get the next character */ #ifdef IKSD if (!local && inserver) { x = coninc(0); } else #endif /* IKSD */ #ifdef OS2 x = is_a_tty(0) ? coninc(0) : /* with no echo ... */ getchar(); #else /* OS2 */ x = getchar(); #endif /* OS2 */ } *s2 = NUL; putchar('\n'); } s2 = sbuf; } else s2 = ""; #endif /* DIRPWDPR */ debug(F110," password",s2,0); rcdactive = 1; sstate = setgen('C',s,s2,""); retcode = 0; break; case XZDEL: /* Delete */ if ((x = cmtxt("Name of remote file(s) to delete", "",&s,xxstring)) < 0) { if (x == -3) { printf("?Name of remote file(s) required\n"); return(-9); } else return(x); } if ((x = remtxt(&s)) < 0) return(x); if (local) ttflui(); /* If local, flush tty input buffer */ retcode = sstate = rfilop(s,'E'); break; case XZDIR: /* Directory */ if ((x = cmtxt("Remote directory or file specification","",&s, xxstring)) < 0) return(x); if ((x = remtxt(&s)) < 0) return(x); if (local) ttflui(); /* If local, flush tty input buffer */ rmsg(); retcode = sstate = setgen('D',s,"",""); break; case XZHLP: /* Help */ if ((x = remcfm()) < 0) return(x); sstate = setgen('H',"","",""); retcode = 0; break; case XZHOS: /* Host */ if ((x = cmtxt("Command for remote system","",&s,xxstring)) < 0) return(x); if ((x = remtxt(&s)) < 0) return(x); if ((y = (int)strlen(s)) < 1) return(x); ckstrncpy(line,s,LINBUFSIZ); cmarg = line; rmsg(); retcode = sstate = 'c'; break; #ifndef NOFRILLS case XZKER: if ((x = cmtxt("Command for remote Kermit","",&s,xxstring)) < 0) return(x); if ((x = remtxt(&s)) < 0) return(x); if ((int)strlen(s) < 1) { if (x == -3) { printf("?Remote Kermit command required\n"); return(-9); } else return(x); } ckstrncpy(line,s,LINBUFSIZ); cmarg = line; retcode = sstate = 'k'; rmsg(); break; case XZLGI: /* Login */ rcdactive = 1; /* Suppress "Logged in" msg if quiet */ return(plogin(XXREM)); case XZLGO: { /* Logout */ extern int bye_active; if ((x = remcfm()) < 0) return(x); sstate = setgen('I',"","",""); retcode = 0; bye_active = 1; /* Close connection when done */ break; } case XZPRI: /* Print */ if (!atdiso || !atcapr) { /* Disposition attribute off? */ printf("?Disposition Attribute is Off\n"); return(-2); } cmarg = ""; cmarg2 = ""; if ((x = cmifi("Local file(s) to print on remote printer","",&s,&y, xxstring)) < 0) { if (x == -3) { printf("?Name of local file(s) required\n"); return(-9); } return(x); } ckstrncpy(line,s,LINBUFSIZ); /* Make a safe copy of filename */ *optbuf = NUL; /* Wipe out any old options */ if ((x = cmtxt("Options for remote print command","",&s,xxstring)) < 0) return(x); if ((x = remtxt(&s)) < 0) return(x); if ((int)strlen(optbuf) > 94) { /* Make sure this is legal */ printf("?Option string too long\n"); return(-9); } ckstrncpy(optbuf,s,OPTBUFLEN); /* Make a safe copy of options */ nfils = -1; /* Expand file list internally */ cmarg = line; /* Point to file list. */ rprintf = 1; /* REMOTE PRINT modifier for SEND */ sstate = 's'; /* Set start state to SEND */ if (local) displa = 1; retcode = 0; break; #endif /* NOFRILLS */ case XZSPA: /* Space */ if ((x = cmtxt("Confirm, or remote directory name", "",&s,xxstring)) < 0) return(x); if ((x = remtxt(&s)) < 0) return(x); retcode = sstate = setgen('U',s,"",""); break; case XZMSG: /* Message */ if ((x = cmtxt("Short text message for server","",&s,xxstring)) < 0) return(x); if ((x = remtxt(&s)) < 0) return(x); retcode = sstate = setgen('M',s,"",""); break; #ifndef NOFRILLS case XZTYP: /* Type */ if ((x = cmtxt("Remote file specification","",&s,xxstring)) < 0) return(x); if ((int)strlen(s) < 1) { printf("?Remote filename required\n"); return(-9); } if ((x = remtxt(&s)) < 0) return(x); rmsg(); retcode = sstate = rfilop(s,'T'); break; #endif /* NOFRILLS */ #ifndef NOFRILLS case XZWHO: if ((x = cmtxt("Remote user name, or carriage return", "",&s,xxstring)) < 0) return(x); if ((x = remtxt(&s)) < 0) return(x); retcode = sstate = setgen('W',s,"",""); break; #endif /* NOFRILLS */ case XZPWD: /* PWD */ if ((x = remcfm()) < 0) return(x); sstate = setgen('A',"","",""); retcode = 0; break; #ifndef NOSPL case XZQUE: { /* Query */ char buf[2]; extern char querybuf[], * qbufp; extern int qbufn; if ((y = cmkey(vartyp,nvartyp,"","",xxstring)) < 0) return(y); if ((x = cmtxt(y == 'F' ? "Remote function invocation" : ('K' ? "Remote variable name or function": "Remote variable name"), "", &s, (y == 'K') ? xxstring : NULL )) < 0) /* Don't evaluate */ return(x); if ((x = remtxt(&s)) < 0) return(x); query = 1; /* QUERY is active */ qbufp = querybuf; /* Initialize query response buffer */ qbufn = 0; querybuf[0] = NUL; buf[0] = (char) (y & 127); buf[1] = NUL; retcode = sstate = setgen('V',"Q",(char *)buf,s); break; } case XZASG: { /* Assign */ char buf[VNAML]; if ((y = cmfld("Remote variable name","",&s,NULL)) < 0) /* No eval */ return(y); if ((int)strlen(s) >= VNAML) { printf("?Too long\n"); return(-9); } ckstrncpy(buf,s,VNAML); if ((x = cmtxt("Assignment for remote variable", "",&s,xxstring)) < 0) /* Evaluate this one */ return(x); if ((x = remtxt(&s)) < 0) return(x); #ifdef COMMENT /* Server commands can't be long packets. In principle there's no reason why they shouldn't be, except that we don't know at this point if the server is capable of accepting long packets because we haven't started the protocol yet. In practice, allowing a long packet here breaks a lot of assumptions, causes buffer overruns and crashes, etc. To be fixed later. (But since this is commented out, evidently I fixed it later...) */ if ((int)strlen(s) > 85) { /* Allow for encoding expansion */ printf("?Sorry, value is too long - 85 characters max\n"); return(-9); } #endif /* COMMENT */ retcode = sstate = setgen('V',"S",(char *)buf,s); break; } #endif /* NOSPL */ case XZCPY: { /* COPY */ char buf[TMPBUFSIZ]; buf[TMPBUFSIZ-1] = '\0'; if ((x = cmfld("Name of remote file to copy","",&s,xxstring)) < 0) { if (x == -3) { printf("?Name of remote file required\n"); return(-9); } else return(x); } ckstrncpy(buf,s,TMPBUFSIZ); if ((x = cmfld("Name of remote destination file or directory", "",&s, xxstring)) < 0) { if (x == -3) { printf("?Name of remote file or directory required\n"); return(-9); } else return(x); } ckstrncpy(tmpbuf,s,TMPBUFSIZ); if ((x = remcfm()) < 0) return(x); if (local) ttflui(); /* If local, flush tty input buffer */ retcode = sstate = setgen('K',buf,tmpbuf,""); break; } case XZREN: { /* Rename */ char buf[TMPBUFSIZ]; buf[TMPBUFSIZ-1] = '\0'; if ((x = cmfld("Name of remote file to rename", "",&s,xxstring)) < 0) { if (x == -3) { printf("?Name of remote file required\n"); return(-9); } else return(x); } ckstrncpy(buf,s,TMPBUFSIZ); if ((x = cmfld("New name of remote file","",&s, xxstring)) < 0) { if (x == -3) { printf("?Name of remote file required\n"); return(-9); } else return(x); } ckstrncpy(tmpbuf,s,TMPBUFSIZ); if ((x = remcfm()) < 0) return(x); if (local) ttflui(); /* If local, flush device buffer */ retcode = sstate = setgen('R',buf,tmpbuf,""); break; } case XZMKD: /* mkdir */ case XZRMD: /* rmdir */ if ((x = cmtxt((xx == XZMKD) ? "Name of remote directory to create" : "Name of remote directory to delete", "", &s, xxstring )) < 0) { if (x == -3) { printf("?Name required\n"); return(-9); } else return(x); } if ((x = remtxt(&s)) < 0) return(x); if (local) ttflui(); /* If local, flush tty input buffer */ retcode = sstate = rfilop(s, (char)(xx == XZMKD ? 'm' : 'd')); break; case XZXIT: /* Exit */ if ((x = remcfm()) < 0) return(x); sstate = setgen('X',"","",""); retcode = 0; break; default: if ((x = remcfm()) < 0) return(x); printf("?Not implemented - %s\n",cmdbuf); return(-2); } if (local && retcode > -1) /* If local, flush tty input buffer */ ttflui(); return(retcode); } /* R F I L O P -- Remote File Operation */ CHAR #ifdef CK_ANSIC rfilop(char * s, char t) #else rfilop(s,t) char *s, t; #endif /* CK_ANSIC */ /* rfilop */ { if (*s == NUL) { printf("?File specification required\n"); return((CHAR) 0); } debug(F111,"rfilop",s,t); return(setgen(t,s,"","")); } #endif /* NOXFER */ #ifdef ANYX25 int setx25() { if ((y = cmkey(x25tab,nx25,"X.25 call options","",xxstring)) < 0) return(y); switch (y) { case XYUDAT: if ((z = cmkey(onoff,2,"X.25 call user data","",xxstring)) < 0) return(z); if (z == 0) { if ((z = cmcfm()) < 0) return(z); cudata = 0; /* disable call user data */ return (success = 1); } if ((x = cmtxt("X.25 call user data string","",&s,xxstring)) < 0) return(x); if ((int)strlen(s) == 0) { return (-3); } else if ((int)strlen(s) > MAXCUDATA) { printf("?The length must be > 0 and <= %d\n",MAXCUDATA); return(-2); } if ((y = cmcfm()) < 0) return(y); ckstrncpy(udata,s,MAXCUDATA); cudata = 1; /* X.25 call user data specified */ return (success = 1); case XYCLOS: if ((z = cmkey(onoff,2,"X.25 closed user group call","",xxstring)) < 0) return(z); if (z == 0) { if ((z = cmcfm()) < 0) return(z); closgr = -1; /* disable closed user group */ return (success = 1); } if ((y = cmnum("0 <= cug index >= 99","",10,&x,xxstring)) < 0) return(y); if (x < 0 || x > 99) { printf("?The choices are 0 <= cug index >= 99\n"); return(-2); } if ((y = cmcfm()) < 0) return(y); closgr = x; /* closed user group selected */ return (success = 1); case XYREVC: if((z = cmkey(onoff,2,"X.25 reverse charge call","",xxstring)) < 0) return(z); if ((x = cmcfm()) < 0) return(x); revcall = z; return (success = 1); } } #ifndef IBMX25 int setpadp() { if ((y = cmkey(padx3tab,npadx3,"PAD X.3 parameter name","",xxstring)) < 0) return(y); x = y; switch (x) { case PAD_BREAK_CHARACTER: if ((y = cmnum("PAD break character value","",10,&z,xxstring)) < 0) return(y); if ((y = cmcfm()) < 0) return(y); break; case PAD_ESCAPE: if ((y = cmnum("PAD escape","",10,&z,xxstring)) < 0) return(y); if (z != 0 && z != 1) { printf("?The choices are 0 or 1\n"); return(-2); } if ((y = cmcfm()) < 0) return(y); break; case PAD_ECHO: if ((y = cmnum("PAD echo","",10,&z,xxstring)) < 0) return(y); if (z != 0 && z != 1) { printf("?The choices are 0 or 1\n"); return(-2); } if ((y = cmcfm()) < 0) return(y); break; case PAD_DATA_FORWARD_CHAR: if ((y = cmnum("PAD data forward char","",10,&z,xxstring)) < 0) return(y); if (z != 0 && z != 2) { printf("?The choices are 0 or 2\n"); return(-2); } if ((y = cmcfm()) < 0) return(y); break; case PAD_DATA_FORWARD_TIMEOUT: if ((y = cmnum("PAD data forward timeout","",10,&z,xxstring)) < 0) return(y); if (z < 0 || z > 255) { printf("?The choices are 0 or 1 <= timeout <= 255\n"); return(-2); } if ((y = cmcfm()) < 0) return(y); break; case PAD_FLOW_CONTROL_BY_PAD: if ((y = cmnum("PAD pad flow control","",10,&z,xxstring)) < 0) return(y); if (z != 0 && z != 1) { printf("?The choices are 0 or 1\n"); return(-2); } if ((y = cmcfm()) < 0) return(y); break; case PAD_SUPPRESSION_OF_SIGNALS: if ((y = cmnum("PAD service","",10,&z,xxstring)) < 0) return(y); if (z != 0 && z != 1) { printf("?The choices are 0 or 1\n"); return(-2); } if ((y = cmcfm()) < 0) return(y); break; case PAD_BREAK_ACTION: if ((y = cmnum("PAD break action","",10,&z,xxstring)) < 0) return(y); if (z != 0 && z != 1 && z != 2 && z != 5 && z != 8 && z != 21) { printf("?The choices are 0, 1, 2, 5, 8 or 21\n"); return(-2); } if ((y = cmcfm()) < 0) return(y); break; case PAD_SUPPRESSION_OF_DATA: if ((y = cmnum("PAD data delivery","",10,&z,xxstring)) < 0) return(y); if (z != 0 && z != 1) { printf("?The choices are 0 or 1\n"); return(-2); } if ((y = cmcfm()) < 0) return(y); break; case PAD_PADDING_AFTER_CR: if ((y = cmnum("PAD crpad","",10,&z,xxstring)) < 0) return(y); if (z < 0 || z > 7) { printf("?The choices are 0 or 1 <= crpad <= 7\n"); return(-2); } if ((y = cmcfm()) < 0) return(y); break; case PAD_LINE_FOLDING: if ((y = cmnum("PAD linefold","",10,&z,xxstring)) < 0) return(y); if (z < 0 || z > 255) { printf("?The choices are 0 or 1 <= linefold <= 255\n"); return(-2); } if ((y = cmcfm()) < 0) return(y); break; case PAD_LINE_SPEED: if ((y = cmnum("PAD baudrate","",10,&z,xxstring)) < 0) return(y); if (z < 0 || z > 18) { printf("?The choices are 0 <= baudrate <= 18\n"); return(-2); } if ((y = cmcfm()) < 0) return(y); break; case PAD_FLOW_CONTROL_BY_USER: if ((y = cmnum("PAD terminal flow control","",10,&z,xxstring)) < 0) return(y); if (z != 0 && z != 1) { printf("?The choices are 0 or 1\n"); return(-2); } if ((y = cmcfm()) < 0) return(y); break; case PAD_LF_AFTER_CR: if ((y = cmnum("PAD crpad","",10,&z,xxstring)) < 0) return(y); if (z < 0 || z == 3 || z > 7) { printf("?The choices are 0, 1, 2, 4, 5, 6 or 7\n"); return(-2); } if ((y = cmcfm()) < 0) return(y); break; case PAD_PADDING_AFTER_LF: if ((y = cmnum("PAD lfpad","",10,&z,xxstring)) < 0) return(y); if (z < 0 || z > 7) { printf("?The choices are 0 or 1 <= lfpad <= 7\n"); return(-2); } if ((y = cmcfm()) < 0) return(y); break; case PAD_EDITING: if ((y = cmnum("PAD edit control","",10,&z,xxstring)) < 0) return(y); if (z != 0 && z != 1) { printf("?The choices are 0 or 1\n"); return(-2); } if ((y = cmcfm()) < 0) return(y); break; case PAD_CHAR_DELETE_CHAR: if ((y = cmnum("PAD char delete char","",10,&z,xxstring)) < 0) return(y); if (z < 0 || z > 127) { printf("?The choices are 0 or 1 <= chardelete <= 127\n"); return(-2); } if ((y = cmcfm()) < 0) return(y); break; case PAD_BUFFER_DELETE_CHAR: if ((y = cmnum("PAD buffer delete char","",10,&z,xxstring)) < 0) return(y); if (z < 0 || z > 127) { printf("?The choices are 0 or 1 <= bufferdelete <= 127\n"); return(-2); } if ((y = cmcfm()) < 0) return(y); break; case PAD_BUFFER_DISPLAY_CHAR: if ((y = cmnum("PAD display line char","",10,&z,xxstring)) < 0) return(y); if (z < 0 || z > 127) { printf("?The choices are 0 or 1 <= displayline <= 127\n"); return(-2); } if ((y = cmcfm()) < 0) return(y); break; } padparms[x] = z; return(success = 1); } #endif /* IBMX25 */ #endif /* ANYX25 */ #ifndef NOXFER int setat(rmsflg) int rmsflg; { int xx; if ((y = cmkey(attrtab,natr,"File Attribute packets","",xxstring)) < 0) return(y); if (y == AT_XALL) { /* ATTRIBUTES ALL ON or ALL OFF */ if ((z = seton(&xx)) < 0) return(z); if (rmsflg) { printf("Sorry, command not available\n"); return(-9); } else { atenci = xx; /* Encoding in */ atenco = xx; /* Encoding out */ atdati = xx; /* Date in */ atdato = xx; /* Date out */ atdisi = xx; /* Disposition in/out */ atdiso = xx; atleni = xx; /* Length in/out (both kinds) */ atleno = xx; atblki = xx; /* Blocksize in/out */ atblko = xx; attypi = xx; /* File type in/out */ attypo = xx; atsidi = xx; /* System ID in/out */ atsido = xx; atsysi = xx; /* System-dependent params in/out */ atsyso = xx; #ifdef CK_PERMS /* Protection */ atlpri = xx; /* Local in */ atlpro = xx; /* Local out */ atgpri = xx; /* Generic in */ atgpro = xx; /* Generic out */ #endif /* CK_PERMS */ #ifdef STRATUS atfrmi = xx; /* Format in/out */ atfrmo = xx; atcrei = xx; /* Creator id in/out */ atcreo = xx; atacti = xx; /* Account in/out */ atacto = xx; #endif /* STRATUS */ } return(z); } else if (y == AT_ALLY || y == AT_ALLN) { /* ATTRIBUTES ON or OFF */ if ((x = cmcfm()) < 0) return(x); atcapr = (y == AT_ALLY) ? 1 : 0; if (rmsflg) { sstate = setgen('S', "132", atcapr ? "1" : "0", ""); return((int) sstate); } else return(success = 1); } /* Otherwise, it's an individual attribute that wants turning off/on */ if ((z = cmkey(onoff,2,"","",xxstring)) < 0) return(z); if ((x = cmcfm()) < 0) return(x); /* There are better ways to do this... */ /* The real problem is that we're not separating the in and out cases */ /* and so we have to arbitrarily pick the "in" case, i.e tell the remote */ /* server to ignore incoming attributes of the specified type, rather */ /* than telling it not to send them. The protocol does not (yet) define */ /* codes for "in-and-out-at-the-same-time". */ switch (y) { #ifdef CK_PERMS /* We're lumping local and generic protection together for now... */ case AT_LPRO: case AT_GPRO: if (rmsflg) { sstate = setgen('S', "143", z ? "1" : "0", ""); return((int) sstate); } atlpri = atlpro = atgpri = atgpro = z; break; #endif /* CK_PERMS */ case AT_DISP: if (rmsflg) { sstate = setgen('S', "142", z ? "1" : "0", ""); return((int) sstate); } atdisi = atdiso = z; break; case AT_ENCO: if (rmsflg) { sstate = setgen('S', "141", z ? "1" : "0", ""); return((int) sstate); } atenci = atenco = z; break; case AT_DATE: if (rmsflg) { sstate = setgen('S', "135", z ? "1" : "0", ""); return((int) sstate); } atdati = atdato = z; break; case AT_LENB: case AT_LENK: if (rmsflg) { sstate = setgen('S', "133", z ? "1" : "0", ""); return((int) sstate); } atleni = atleno = z; break; case AT_BLKS: if (rmsflg) { sstate = setgen('S', "139", z ? "1" : "0", ""); return((int) sstate); } atblki = atblko = z; break; case AT_FTYP: if (rmsflg) { sstate = setgen('S', "134", z ? "1" : "0", ""); return((int) sstate); } attypi = attypo = z; break; #ifdef STRATUS case AT_CREA: if (rmsflg) { sstate = setgen('S', "136", z ? "1" : "0", ""); return((int) sstate); } atcrei = atcreo = z; break; case AT_ACCT: if (rmsflg) { sstate = setgen('S', "137", z ? "1" : "0", ""); return((int) sstate); } atacti = atacto = z; break; #endif /* STRATUS */ case AT_SYSI: if (rmsflg) { sstate = setgen('S', "145", z ? "1" : "0", ""); return((int) sstate); } atsidi = atsido = z; break; case AT_RECF: if (rmsflg) { sstate = setgen('S', "146", z ? "1" : "0", ""); return((int) sstate); } atfrmi = atfrmo = z; break; case AT_SYSP: if (rmsflg) { sstate = setgen('S', "147", z ? "1" : "0", ""); return((int) sstate); } atsysi = atsyso = z; break; default: printf("?Not available\n"); return(-2); } return(1); } #endif /* NOXFER */ #ifndef NOSPL int setinp() { if ((y = cmkey(inptab,ninp,"","",xxstring)) < 0) return(y); switch (y) { #ifdef OS2 case IN_PAC: /* SET INPUT PACING */ z = cmnum("milliseconds","0",10,&x,xxstring); return(setnum(&tt_inpacing,x,z,1000)); case IN_TRM: /* SET INPUT TERMINAL */ return(seton(&interm)); #endif /* OS2 */ case IN_DEF: /* SET INPUT DEFAULT-TIMEOUT */ z = cmnum("Positive number","",10,&x,xxstring); return(setnum(&indef,x,z,94)); #ifdef CKFLOAT case IN_SCA: /* SET INPUT SCALE-FACTOR */ if ((x = cmfld("Number such as 2 or 0.5","1.0",&s, xxstring)) < 0) return(x); if (isfloat(s,0)) { /* A floating-point number? */ extern char * inpscale; inscale = floatval; /* Yes, get its value */ makestr(&inpscale,s); /* Save it as \v(inscale) */ return(success = 1); } else { return(-2); } #endif /* CKFLOAT */ case IN_TIM: /* SET INPUT TIMEOUT-ACTION */ if ((z = cmkey(intimt,2,"","",xxstring)) < 0) return(z); if ((x = cmcfm()) < 0) return(x); intime[cmdlvl] = z; return(success = 1); case IN_CAS: /* SET INPUT CASE */ if ((z = cmkey(incast,2,"","",xxstring)) < 0) return(z); if ((x = cmcfm()) < 0) return(x); inpcas[cmdlvl] = z; return(success = 1); case IN_ECH: /* SET INPUT ECHO */ return(seton(&inecho)); case IN_SIL: /* SET INPUT SILENCE */ z = cmnum("Seconds of inactivity before INPUT fails","",10,&x, xxstring); return(setnum(&insilence,x,z,-1)); case IN_BUF: /* SET INPUT BUFFER-SIZE */ if ((z = cmnum("Number of bytes in INPUT buffer", ckitoa(INPBUFSIZ),10,&x, xxstring)) < 0) return(z); if ((y = cmcfm()) < 0) return(y); inbufsize = 0; if (inpbuf) { free(inpbuf); inpbuf = NULL; inpbp = NULL; } if (!(s = (char *)malloc(x + 1))) return(0); inpbuf = s; inpbp = s; inbufsize = x; for (x = 0; x <= inbufsize; x++) inpbuf[x] = NUL; return(success = 1); #ifdef CK_AUTODL case IN_ADL: /* AUTODOWNLOAD */ return(seton(&inautodl)); #endif /* CK_AUTODL */ case IN_CAN: /* SET INPUT INTERRUPTS */ return(seton(&inintr)); } return(0); } #endif /* NOSPL */ #ifdef NETCONN VOID ndreset() { #ifndef NODIAL /* This depends on DIAL... */ int i=0, j=0; if (!ndinited) /* Don't free garbage... */ return; for (i = 0; i < nhcount; i++) { /* Clean out previous list */ if (nh_p[i]) free(nh_p[i]); nh_p[i] = NULL; if (nh_p2[i]) free(nh_p2[i]); nh_p2[i] = NULL; for (j = 0; j < 4; j++) { if (nh_px[j][i]) free(nh_px[j][i]); nh_px[j][i] = NULL; } } #endif /* NODIAL */ } VOID ndinit() { /* Net directory pointers */ #ifndef NODIAL /* This depends on DIAL... */ int i, j; if (ndinited++) /* Don't do this more than once. */ return; for (i = 0; i < MAXDDIR; i++) { /* Init all pointers to NULL */ netdir[i] = NULL; } for (i = 0; i < MAXDNUMS; i++) { nh_p[i] = NULL; nh_p2[i] = NULL; for (j = 0; j < 4; j++) nh_px[j][i] = NULL; } #endif /* NODIAL */ } #ifndef NODIAL #ifdef NETCONN VOID /* Get net defaults from environment */ getnetenv() { char *p = NULL; makestr(&p,getenv("K_NET_DIRECTORY")); /* Dialing directories */ if (p) { int i; xwords(p,MAXDDIR,netdir,0); for (i = 0; i < MAXDDIR; i++) { /* Fill in any gaps... */ if (!netdir[i+1]) break; else netdir[i] = netdir[i+1]; debug(F111,"netdir[i]",netdir[i],i); } nnetdir = i; } } #endif /* NETCONN */ #endif /* NODIAL */ int #ifdef CK_ANSIC lunet(char *s) /* s = name to look up */ #else lunet(s) char *s; #endif /* CK_ANSIC */ /* lunet */ { #ifndef NODIAL /* This depends on DIAL... */ int n, n1, t, dd = 0; int ambiguous = 0; FILE * f; char *line = NULL; extern int dialdpy; int netdpy = dialdpy; char *info[8]; nhcount = 0; /* Set this before returning */ if (!s || nnetdir < 1) /* Validate arguments */ return(-1); if (isdigit(*s) || *s == '*' || *s == '.') return(0); if ((n1 = (int) strlen(s)) < 1) /* Length of string to look up */ return(-1); if (!(line = malloc(1024))) /* Allocate input buffer */ return(-1); lu_again: f = NULL; /* Network directory file descriptor */ t = nhcount = 0; /* Match count */ dd = 0; /* Directory counter */ dirline = 0; while (1) { /* We make one pass */ if (!f) { /* Directory not open */ if (dd >= nnetdir) /* No directories left? */ break; /* Done. */ if ((f = fopen(netdir[dd],"r")) == NULL) { /* Open it */ perror(netdir[dd]); /* Can't, print message saying why */ dd++; continue; /* But go on to next one. */ } if (netdpy) printf("Opening %s...\n",netdir[dd]); dd++; } line[0] = NUL; if (getnct(line,1023,f,1) < 0) { /* Read a line */ if (f) { /* f can be clobbered! */ fclose(f); /* Close the file */ f = NULL; /* Indicate next one needs opening */ } continue; } if (!line[0]) /* Empty line */ continue; xwords(line,7,info,0); /* Parse it */ if (!info[1] || !info[2] || !info[3]) /* Required fields */ continue; if (*info[1] == ';') /* Full-line comment */ continue; if ((n = (int) strlen(info[1])) < 1) /* Length of name-tag */ continue; if (n < n1) /* Search name is longer */ continue; /* Can't possibly match */ if (ambiguous && n != n1) continue; if (ckstrcmp(s,info[1],n1,0)) /* Compare using length of */ continue; /* search string s. */ /* Have a match */ makestr(&(nh_p[nhcount]), info[3]); /* address */ makestr(&(nh_p2[nhcount]),info[2]); /* net type */ makestr(&(nh_px[0][nhcount]),info[4]); /* net-specific stuff... */ makestr(&(nh_px[1][nhcount]),info[5]); makestr(&(nh_px[2][nhcount]),info[6]); makestr(&(nh_px[3][nhcount]),info[7]); nhcount++; /* Count this match */ if (nhcount > MAXDNUMS) { /* Watch out for too many */ printf("Warning: %d matches found, %d max\n", nhcount, MAXDNUMS ); nhcount = MAXDNUMS; break; } if (nhcount == 1) { /* First one - save entry name */ if (n_name) { /* Free the one from before if any */ free(n_name); n_name = NULL; } if (!(n_name = (char *)malloc(n + 1))) { /* Allocate new storage */ printf("?memory allocation error - lunet:3\n"); if (line) { free(line); line = NULL; } nhcount = 0; return(-1); } t = n; /* Remember its length */ strcpy(n_name,info[1]); /* safe */ } else { /* Second or subsequent one */ if ((int) strlen(info[1]) == t) /* Lengths compare */ if (!ckstrcmp(n_name,info[1],t,0)) /* Caseless compare OK */ continue; /* Name given by user matches entries with different names */ if (ambiguous) /* Been here before */ break; ambiguous = 1; /* Now an exact match is required */ ndreset(); /* Clear out previous list */ goto lu_again; /* Do it all over again. */ } } if (line) { free(line); line = NULL; } if (nhcount == 0 && ambiguous) printf("?\"%s\" - ambiguous in network directory\n",s); #else nhcount = 0; #endif /* NODIAL */ return(nhcount); } #endif /* NETCONN */ #ifndef NOLOCAL /* C L S C O N N X -- Close connection */ int clsconnx(ask) int ask; { int x, rc = 0; #ifdef NEWFTP extern int ftpget, ftpisopen(); if ((ftpget == 1) || ((ftpget == 2) && !local && ftpisopen())) return(success = ftpbye()); #endif /* NEWFTP */ debug(F101,"clsconnx local","",local); if (local) { x = ask ? hupok(1) : 1; /* Make sure it's OK to close */ if (!x) { rc = -1; debug(F101,"clsconnx hupok says no","",rc); return(rc); } ttflui(); /* Clear away buffered up junk */ #ifndef NODIAL #ifdef OS2ONLY /* Don't hangup a line that is shared with the SLIP or PPP driver */ if (!ttslip && !ttppp) #endif /* OS2ONLY */ mdmhup(); #endif /* NODIAL */ if (network && msgflg) printf(" Closing connection\n"); ttclos(0); /* Close old connection, if any */ rc = 1; { extern int wasclosed, whyclosed; if (wasclosed) { whyclosed = WC_CLOS; #ifndef NOSPL if (nmac) { /* Any macros defined? */ int k; /* Yes */ /* printf("ON_CLOSE CLSCONNX\n"); */ wasclosed = 0; k = mlook(mactab,"on_close",nmac); /* Look this up */ if (k >= 0) { /* If found, */ if (dodo(k,ckitoa(whyclosed),0) > -1) /* set it up, */ parser(1); /* and execute it */ } } #endif /* NOSPL */ whyclosed = WC_REMO; wasclosed = 0; } } } #ifdef VMS /* Or maybe #ifndef UNIX? */ else { /* Need to do this in VMS to */ ttclos(0); /* free the tty channel number */ rc = 1; /* obtained in ttopen() or else */ } /* subsequent ttopen's won't work */ #endif /* VMS */ dologend(); haveline = 0; if (mdmtyp < 0) { /* Switching from net to async? */ if (mdmsav > -1) /* Restore modem type from last */ mdmtyp = mdmsav; /* SET MODEM command, if any. */ else mdmtyp = 0; mdmsav = -1; } if (network) network = 0; #ifdef NETCONN if (oldplex > -1) { /* Restore previous duplex setting. */ duplex = oldplex; oldplex = -1; } #endif /* NETCONN */ #ifndef MAC ckstrncpy(ttname,dftty,TTNAMLEN); /* Restore default communication */ #endif /* MAC */ local = dfloc; /* device and local/remote status */ if (local) { cxtype = CXT_DIRECT; /* Something reasonable */ speed = ttgspd(); /* Get the current speed */ } else { cxtype = CXT_REMOTE; speed = -1L; } #ifndef NOXFER if (xreliable > -1 && !setreliable) { reliable = xreliable; debug(F101,"clsconnx reliable A","",reliable); } else if (!setreliable) { reliable = SET_AUTO; debug(F101,"clsconnx reliable B","",reliable); } #endif /* NOXFER */ setflow(); /* Revert flow control */ return(rc); } int clskconnx(x) int x; { /* Close Kermit connection only */ int t, rc; /* (not FTP) */ #ifdef NEWFTP extern int ftpget; t = ftpget; ftpget = 0; #endif /* NEWFTP */ rc = clsconnx(x); #ifdef NEWFTP ftpget = t; #endif /* NEWFTP */ return(rc); } /* May 2002: setlin() decomposition starts here ... */ #ifdef OS2 #define SRVBUFSIZ PIPENAML #else /* OS2 */ #define SRVBUFSIZ 63 #endif /* OS2 */ #define HOSTNAMLEN 15*65 int netsave = -1; static char * tmpstring = NULL; static char * tmpusrid = NULL; #ifdef SSHCMD char * sshcmd = NULL; char * defsshcmd = "ssh -e none"; #else #ifdef SSHBUILTIN char * sshrcmd = NULL; char * sshtmpcmd = NULL; #endif /* SSHBUILTIN */ #endif /* SSHCMD */ /* c x _ f a i l -- Common error exit routine for cx_net, cx_line */ int cx_fail(msg, text) int msg; char * text; { makestr(&slmsg,text); /* For the record (or GUI) */ if (msg) /* Not GUI, not quiet, etc */ printf("?%s\n",text); /* Print error message */ slrestor(); /* Restore LINE/HOST to known state */ return(msg ? -9 : (success = 0)); /* Return appropriate code */ } #ifdef NETCONN /* c x _ n e t -- Make a network connection */ /* Call with: net = network type protocol = protocol type host = string pointer to host name. svc = string pointer to service or port on host. username = username for connection password = password for connection command = command to execute param1 = Telnet: Authentication type SSH: Version param2 = Telnet: Encryption type SSH: Command as Subsystem param3 = Telnet: 1 to wait for negotiations, 0 otherwise SSH: X11 Forwarding cx = 1 to automatically enter Connect mode, 0 otherwise. sx = 1 to automatically enter Server mode, 0 otherwise. flag = if no host name given, 1 = close current connection, 0 = resume gui = 1 if called from GUI dialog, 0 otherwise. Returns: 1 on success 0 on failure and no message printed, slmsg set to failure message. -9 on failure and message printed, ditto. */ int #ifdef CK_ANSIC cx_net( int net, int protocol, char * xhost, char * svc, char * username, char * password, char * command, int param1, int param2, int param3, int cx, int sx, int flag, int gui) #else /* CK_ANSIC */ cx_net(net, protocol, xhost, svc, username, password, command, param1, param2, param3, cx, sx, flag, gui) char * xhost, * svc, * username, *password, *command; int net, protocol, cx, sx, flag, param1, param2, param3, gui; #endif /* CK_ANSIC */ /* cx_net */ { int i, n, x, msg; int _local = -1; extern char pwbuf[], * g_pswd; extern int pwflg, pwcrypt, g_pflg, g_pcpt, nolocal; char srvbuf[SRVBUFSIZ+1]; /* Service */ char hostbuf[HOSTNAMLEN]; /* Host buffer to manipulate */ char hostname[HOSTNAMLEN]; /* Copy of host parameter */ char * host = hostbuf; /* Pointer to copy of host param */ if (!xhost) xhost = ""; /* Watch out for null pointers */ if (!svc) svc = ""; ckstrncpy(host,xhost,HOSTNAMLEN); /* Avoid buffer confusion */ debug(F110,"cx_net host",host,0); debug(F111,"cx_net service",svc,SRVBUFSIZ); debug(F101,"cx_net network type","",net); msg = (gui == 0) && msgflg; /* Whether to print messages */ #ifndef NODIAL debug(F101,"cx_net nnetdir","",nnetdir); x = 0; /* Look up in network directory */ if (*host == '=') { /* If number starts with = sign */ host++; /* strip it */ while (*host == SP) host++; /* and any leading spaces */ debug(F110,"cx_net host 2",host,0); nhcount = 0; } else if (*host) { /* We want to look it up. */ if (nnetdir > 0) /* If there is a directory... */ x = lunet(host); /* (sets nhcount) */ else /* otherwise */ nhcount = 0; /* we didn't find any there */ if (x < 0) /* Internal error? */ return(cx_fail(msg,"Network directory lookup error")); debug(F111,"cx_net lunet nhcount",host,nhcount); } #endif /* NODIAL */ /* New connection wanted. Make a copy of the host name/address... */ if (clskconnx(1) < 0) /* Close current Kermit connection */ return(cx_fail(msg,"Error closing previous connection")); if (*host) { /* They gave a hostname */ _local = 1; /* Network connection always local */ if (mdmsav < 0) mdmsav = mdmtyp; /* Remember old modem type */ mdmtyp = -net; /* Special code for network */ } else { /* They just said "set host" */ host = dftty; /* So go back to normal */ _local = dfloc; /* default tty, location, */ if (flag) { /* Close current connection */ setflow(); /* Maybe change flow control */ haveline = 1; /* (* is this right? *) */ dologend(); #ifndef NODIAL dialsta = DIA_UNK; #endif /* NODIAL */ #ifdef LOCUS if (autolocus) { setlocus(1,1); } #endif /* LOCUS */ /* XXX - Is this right? */ /* Should we be returning without doing anything ? */ /* Yes it's right -- we closed the old connection just above. */ return(success = 1); } } success = 0; if (host != line) /* line[] is a global */ ckstrncpy(line,host,LINBUFSIZ); ckstrncpy(hostname,host,HOSTNAMLEN); ckstrncpy(srvbuf,svc,SRVBUFSIZ+1); #ifndef NODIAL if ((nhcount > 1) && msg) { int k; printf("%d entr%s found for \"%s\"%s\n", nhcount, (nhcount == 1) ? "y" : "ies", s, (nhcount > 0) ? ":" : "." ); for (i = 0; i < nhcount; i++) { printf("%3d. %-12s => %-9s %s", i+1,n_name,nh_p2[i],nh_p[i]); for (k = 0; k < 4; k++) { /* Also list net-specific items */ if (nh_px[k][i]) /* free format... */ printf(" %s",nh_px[k][i]); else break; } printf("\n"); } } if (nhcount == 0) n = 1; else n = nhcount; #else n = 1; nhcount = 0; #endif /* NODIAL */ for (i = 0; i < n; i++) { /* Loop for each entry found */ debug(F101,"cx_net loop i","",i); #ifndef NODIAL if (nhcount > 0) { /* If we found at least one entry... */ ckstrncpy(line,nh_p[i],LINBUFSIZ); /* Copy current entry */ if (lookup(netcmd,nh_p2[i],nnets,&x) > -1) { /* Net type */ int xx; xx = netcmd[x].kwval; /* User specified SSH so don't let net directory override */ if (net != NET_SSH || xx != NET_TCPB) { net = xx; mdmtyp = 0 - net; } } else { makestr(&slmsg,"Network type not supported"); if (msg) printf("Error - network type \"%s\" not supported\n", nh_p2[i] ); continue; } switch (net) { /* Net-specific directory things */ #ifdef SSHBUILTIN case NET_SSH: /* SSH */ /* Any SSH specific network directory stuff? */ break; /* NET_SSH */ #endif /* SSHBUILTIN */ case NET_TCPB: { /* TCP/IP TELNET,RLOGIN,... */ #ifdef TCPSOCKET char *q; int flag = 0; /* Extract ":service", if any, from host string */ debug(F110,"cx_net service 1",line,0); for (q = line; (*q != '\0') && (*q != ':'); q++) ; if (*q == ':') { *q++ = NUL; flag = 1; } debug(F111,"cx_net service 2",line,flag); /* Get service, if any, from directory entry */ if (!*srvbuf) { if (nh_px[0][i]) { ckstrncpy(srvbuf,nh_px[0][i],SRVBUFSIZ); debug(F110,"cx_net service 3",srvbuf,0); } if (flag) { ckstrncpy(srvbuf,q,SRVBUFSIZ); debug(F110,"cx_net service 4",srvbuf,0); } } ckstrncpy(hostname,line,HOSTNAMLEN); /* If we have a service, append to host name/address */ if (*srvbuf) { ckstrncat(line, ":", LINBUFSIZ); ckstrncat(line, srvbuf, LINBUFSIZ); debug(F110,"cx_net service 5",line,0); } #ifdef RLOGCODE /* If no service given but command was RLOGIN */ else if (ttnproto == NP_RLOGIN) { /* add this... */ ckstrncat(line, ":login",LINBUFSIZ); debug(F110,"cx_net service 6",line,0); } #ifdef CK_AUTHENTICATION #ifdef CK_KERBEROS else if (ttnproto == NP_K4LOGIN || ttnproto == NP_K5LOGIN) { /* add this... */ ckstrncat(line, ":klogin",LINBUFSIZ); debug(F110,"cx_net service 7",line,0); } else if (ttnproto == NP_EK4LOGIN || ttnproto == NP_EK5LOGIN) { /* add this... */ ckstrncat(line, ":eklogin",LINBUFSIZ); debug(F110,"cx_net service 8",line,0); } #endif /* CK_KERBEROS */ #endif /* CK_AUTHENTICATION */ #endif /* RLOGCODE */ else { /* Otherwise, add ":telnet". */ ckstrncat(line, ":telnet", LINBUFSIZ); debug(F110,"cx_net service 9",line,0); } if (username) { /* This is a parameter... */ ckstrncpy(uidbuf,username,UIDBUFLEN); uidflag = 1; } /* Fifth field, if any, is user ID (for rlogin) */ if (nh_px[1][i] && !uidflag) ckstrncpy(uidbuf,username,UIDBUFLEN); #ifdef RLOGCODE if (IS_RLOGIN() && !uidbuf[0]) return(cx_fail(msg,"Username required")); #endif /* RLOGCODE */ #endif /* TCPSOCKET */ break; } case NET_PIPE: /* Pipe */ #ifdef NPIPE if (!pipename[0]) { /* User didn't give a pipename */ if (nh_px[0][i]) { /* But directory entry has one */ if (strcmp(pipename,"\\pipe\\")) { ckstrncpy(pipename,"\\pipe\\",LINBUFSIZ); ckstrncat(srvbuf,nh_px[0][i],PIPENAML-6); } else { ckstrncpy(pipename,nh_px[0][i],PIPENAML); } debug(F110,"cx_net pipeneme",pipename,0); } } #endif /* NPIPE */ break; case NET_SLAT: /* LAT / CTERM */ #ifdef SUPERLAT if (!slat_pwd[0]) { /* User didn't give a password */ if (nh_px[0][i]) { /* But directory entry has one */ ckstrncpy(slat_pwd,nh_px[0][i],18); debug(F110,"cx_net SuperLAT password",slat_pwd,0); } } #endif /* SUPERLAT */ break; case NET_SX25: /* X.25 keyword parameters */ case NET_IX25: case NET_VX25: { #ifdef ANYX25 int k; /* Cycle through the four fields */ for (k = 0; k < 4; k++) { if (!nh_px[k][i]) /* Bail out if none left */ break; if (!ckstrcmp(nh_px[k][i],"cug=",4,0)) { closgr = atoi(nh_px[k][i]+4); debug(F101,"X25 CUG","",closgr); } else if (!ckstrcmp(nh_px[k][i],"cud=",4,0)) { cudata = 1; ckstrncpy(udata,nh_px[k][i]+4,MAXCUDATA); debug(F110,"X25 CUD",cudata,0); } else if (!ckstrcmp(nh_px[k][i],"rev=",4,0)) { revcall = !ckstrcmp(nh_px[k][i]+4,"=on",3,0); debug(F101,"X25 REV","",revcall); #ifndef IBMX25 } else if (!ckstrcmp(nh_px[k][i],"pad=",4,0)) { int x3par, x3val; char *s1, *s2; s1 = s2 = nh_px[k][i]+4; /* PAD parameters */ while (*s2) { /* Pick them apart */ if (*s2 == ':') { *s2 = NUL; x3par = atoi(s1); s1 = ++s2; continue; } else if (*s2 == ',') { *s2 = NUL; x3val = atoi(s1); s1 = ++s2; debug(F111,"X25 PAD",x3par,x3val); if (x3par > -1 && x3par <= MAXPADPARMS) padparms[x3par] = x3val; continue; } else s2++; } #endif /* IBMX25 */ } } #endif /* ANYX25 */ break; } default: /* Nothing special for other nets */ break; } } else #endif /* NODIAL */ { /* No directory entries found. */ ckstrncpy(line,hostname,LINBUFSIZ); /* Put this back... */ /* If the user gave a TCP service */ if (net == NET_TCPB || net == NET_SSH) if (*srvbuf) { /* Append it to host name/address */ ckstrncat(line, ":", LINBUFSIZ); ckstrncat(line, srvbuf,LINBUFSIZ); } } /* Get here with host name/address and all net-specific parameters set, ready to open the connection. */ mdmtyp = -net; /* This should have been done */ /* already but just in case ... */ debug(F110,"cx_net net line[] before ttopen",line,0); debug(F101,"cx_net net mdmtyp before ttopen","",mdmtyp); debug(F101,"cx_net net ttnproto","",ttnproto); #ifdef SSHBUILTIN if (net == NET_SSH) { makestr(&ssh_hst,hostname); /* Stash everything */ if (username) { if (!sl_uid_saved) { ckstrncpy(sl_uidbuf,uidbuf,UIDBUFLEN); sl_uid_saved = 1; } ckstrncpy(uidbuf,username,UIDBUFLEN); } if (srvbuf[0]) { makestr(&ssh_prt,srvbuf); } else makestr(&ssh_prt,NULL); if (command) { makestr(&ssh_cmd,brstrip(command)); ssh_cas = param2; } else makestr(&ssh_cmd,NULL); if (param1 > -1) { #ifndef SSHTEST if (!sl_ssh_ver_saved) { sl_ssh_ver = ssh_ver; sl_ssh_ver_saved = 1; } #endif /* SSHTEST */ ssh_ver = param1; } if (param3 > -1) { #ifndef SSHTEST if (!sl_ssh_xfw_saved) { sl_ssh_xfw = ssh_xfw; sl_ssh_xfw_saved = 1; } #endif /* SSHTEST */ ssh_xfw = param3; } } else /* NET_SSH */ #endif /* SSHBUILTIN */ #ifdef TCPSOCKET if (net == NET_TCPB) { switch (protocol) { #ifdef CK_SSL #ifdef COMMENT /* Jeff's version from 30 Dec 2006 - doesn't work - SSL/TLS_RAW still start Telnet negotions if a 0xff byte comes in. */ case NP_SSL_RAW: ttnproto = NP_SSL_RAW; debug(F101,"NP_SSL_RAW ttnproto","",ttnproto); ssl_only_flag = 1; tls_only_flag = 0; break; case NP_TLS_RAW: ttnproto = NP_TLS_RAW; debug(F101,"NP_TLS_RAW ttnproto","",ttnproto); ssl_only_flag = 0; tls_only_flag = 1; break; case NP_SSL: ttnproto = NP_SSL; debug(F101,"NP_SSL ttnproto","",ttnproto); ssl_only_flag = 1; tls_only_flag = 0; break; case NP_TLS: ttnproto = NP_TLS; debug(F101,"NP_TLS ttnproto","",ttnproto); ssl_only_flag = 0; tls_only_flag = 1; break; case NP_SSL_TELNET: ttnproto = NP_TELNET; debug(F101,"NP_SSL_TELNET ttnproto","",ttnproto); ssl_only_flag = 1; tls_only_flag = 0; break; case NP_TLS_TELNET: ttnproto = NP_TELNET; debug(F101,"NP_TLS_TELNET ttnproto","",ttnproto); ssl_only_flag = 0; tls_only_flag = 1; break; #else /* fdc version of 4 Dec 2006 works OK */ case NP_SSL_RAW: case NP_SSL: ssl_raw_flag = (protocol == NP_SSL_RAW) ? 1 : 0; ttnproto = protocol; debug(F101,protocol==NP_SSL ? "NP_SSL ttnproto" : "NP_SSL_RAW ttnproto", "",ttnproto); ssl_only_flag = 1; tls_only_flag = 0; break; case NP_TLS: case NP_TLS_RAW: tls_raw_flag = (protocol == NP_SSL_RAW) ? 1 : 0; ttnproto = protocol; debug(F101,protocol==NP_TLS ? "NP_TLS ttnproto" : "NP_TLS_RAW ttnproto", "",ttnproto); ssl_only_flag = 0; tls_only_flag = 1; break; case NP_SSL_TELNET: ssl_raw_flag = 0; ttnproto = NP_TELNET; debug(F101,"NP_SSL_TELNET ttnproto","",ttnproto); ssl_only_flag = 1; tls_only_flag = 0; break; case NP_TLS_TELNET: tls_raw_flag = 0; ttnproto = NP_TELNET; debug(F101,"NP_TLS_TELNET ttnproto","",ttnproto); ssl_only_flag = 0; tls_only_flag = 1; break; #endif /* COMMENT */ #endif /* CK_SSL */ case NP_NONE: case NP_TCPRAW: case NP_RLOGIN: case NP_K4LOGIN: case NP_K5LOGIN: case NP_EK4LOGIN: case NP_EK5LOGIN: case NP_TELNET: case NP_KERMIT: default: ttnproto = protocol; #ifdef CK_SSL #ifdef COMMENT /* Jeff version from 30 Dec 2006 */ ssl_only_flag = 0; tls_only_flag = 0; #else /* fdc version from 4 Dec 2006 */ ssl_raw_flag = 0; tls_raw_flag = 0; ssl_only_flag = 0; tls_only_flag = 0; #endif /* COMMENT */ #endif /* CK_SSL */ break; } #ifdef CK_AUTHENTICATION if ((ttnproto == NP_TELNET || ttnproto == NP_KERMIT) && param1 > -1) { if (!sl_auth_saved) { int x; for (x = 0; x < AUTHTYPLSTSZ; x++) sl_auth_type_user[x] = auth_type_user[x]; sl_auth_saved = 1; } if (!sl_topt_a_s_saved) { sl_topt_a_su = TELOPT_DEF_S_U_MODE(TELOPT_AUTHENTICATION); sl_topt_a_s_saved = 1; } if (!sl_topt_a_c_saved) { sl_topt_a_cm = TELOPT_DEF_C_ME_MODE(TELOPT_AUTHENTICATION); sl_topt_a_c_saved = 1; } switch (param1) { case AUTHTYPE_AUTO: auth_type_user[0] = AUTHTYPE_AUTO; TELOPT_DEF_S_U_MODE(TELOPT_AUTHENTICATION) = TN_NG_RQ; TELOPT_DEF_C_ME_MODE(TELOPT_AUTHENTICATION) = TN_NG_RQ; break; case AUTHTYPE_NULL: auth_type_user[0] = AUTHTYPE_NULL; TELOPT_DEF_S_U_MODE(TELOPT_AUTHENTICATION) = TN_NG_RF; TELOPT_DEF_C_ME_MODE(TELOPT_AUTHENTICATION) = TN_NG_RF; break; #ifdef CK_SRP case AUTHTYPE_SRP: auth_type_user[0] = AUTHTYPE_SRP; auth_type_user[1] = AUTHTYPE_NULL; TELOPT_DEF_S_U_MODE(TELOPT_AUTHENTICATION) = TN_NG_MU; TELOPT_DEF_C_ME_MODE(TELOPT_AUTHENTICATION) = TN_NG_MU; break; #endif /* CK_SRP */ #ifdef CK_SSL case AUTHTYPE_SSL: auth_type_user[0] = AUTHTYPE_SSL; auth_type_user[1] = AUTHTYPE_NULL; TELOPT_DEF_S_U_MODE(TELOPT_AUTHENTICATION) = TN_NG_MU; TELOPT_DEF_C_ME_MODE(TELOPT_AUTHENTICATION) = TN_NG_MU; break; #endif /* CK_SSL */ #ifdef NT case AUTHTYPE_NTLM: auth_type_user[0] = AUTHTYPE_NTLM; auth_type_user[1] = AUTHTYPE_NULL; TELOPT_DEF_S_U_MODE(TELOPT_AUTHENTICATION) = TN_NG_MU; TELOPT_DEF_C_ME_MODE(TELOPT_AUTHENTICATION) = TN_NG_MU; break; #endif /* NT */ #ifdef CK_KERBEROS case AUTHTYPE_KERBEROS_V4: auth_type_user[0] = AUTHTYPE_KERBEROS_V4; auth_type_user[1] = AUTHTYPE_NULL; TELOPT_DEF_S_U_MODE(TELOPT_AUTHENTICATION) = TN_NG_MU; TELOPT_DEF_C_ME_MODE(TELOPT_AUTHENTICATION) = TN_NG_MU; break; case AUTHTYPE_KERBEROS_V5: auth_type_user[0] = AUTHTYPE_KERBEROS_V5; auth_type_user[1] = AUTHTYPE_NULL; TELOPT_DEF_S_U_MODE(TELOPT_AUTHENTICATION) = TN_NG_MU; TELOPT_DEF_C_ME_MODE(TELOPT_AUTHENTICATION) = TN_NG_MU; break; #endif /* CK_KERBEROS */ } } /* If the user requires a particular type of Kerberos connection, make sure we have a valid TGT. */ makestr(&slmsg,"Authentication failure"); if ((ttnproto == NP_TELNET || ttnproto == NP_KERMIT) && (line[0] == '*' && TELOPT_DEF_S_U_MODE(TELOPT_AUTHENTICATION) == TN_NG_MU || line[0] != '*' && TELOPT_DEF_C_ME_MODE(TELOPT_AUTHENTICATION) == TN_NG_MU) ) { #ifdef CK_KERBEROS if ( auth_type_user[0] == AUTHTYPE_KERBEROS_V4 ) { extern int krb4_autoget; if (!ck_krb4_is_installed()) return(cx_fail(msg, "Required authentication method (Kerberos 4) is not installed")); #ifdef COMMENT /* This code results in false failures when using */ /* kerberos to machines in realms other than the */ /* default since we don't know the realm of the */ /* other machine until perform the reverse DNS */ /* lookup. */ else if (line[0] != '*' && !ck_krb4_is_tgt_valid() && (!krb4_autoget || krb4_autoget && !ck_krb4_autoget_TGT(NULL))) { return(cx_fail(msg, "Kerberos 4: Ticket Getting Ticket not valid")); } #endif /* COMMENT */ } else if (auth_type_user[0] == AUTHTYPE_KERBEROS_V5) { extern int krb5_autoget; if (!ck_krb5_is_installed()) { return(cx_fail(msg, "Required authentication method (Kerberos 5) is not installed")); } #ifdef COMMENT /* This code results in false failures when using */ /* kerberos to machines in realms other than the */ /* default since we don't know the realm of the */ /* other machine until perform the reverse DNS */ /* lookup. */ else if (line[0] != '*' && !ck_krb5_is_tgt_valid() && (!krb5_autoget || krb5_autoget && !ck_krb5_autoget_TGT(NULL))) { return(cx_fail(msg, "Kerberos 5: Ticket Getting Ticket not valid.")); } #endif /* COMMENT */ } #endif /* CK_KERBEROS */ #ifdef NT if (auth_type_user[0] == AUTHTYPE_NTLM) { if (!ck_ntlm_is_installed()) { return(cx_fail(msg, "Required authentication method (NTLM) is not installed")); } else if (line[0] != '*' && !ck_ntlm_is_valid(0)) { return(cx_fail(msg,"NTLM: Credentials are unavailable.")); } } #endif /* NT */ #ifdef CK_SSL if (auth_type_user[0] == AUTHTYPE_SSL) { if (!ck_ssleay_is_installed()) { return(cx_fail(msg, "Required authentication method (SSL) is not installed")); } } #endif /* CK_SSL */ #ifdef CK_SRP if (auth_type_user[0] == AUTHTYPE_SRP) { if (!ck_srp_is_installed()) { return(cx_fail(msg, "Required authentication method (SRP) is not installed")); } } #endif /* CK_SRP */ } #endif /* CK_AUTHENTICATION */ #ifdef CK_ENCRYPTION if ((ttnproto == NP_TELNET || ttnproto == NP_KERMIT) && param2 > -1) { if (!sl_cx_saved) { sl_cx_type = cx_type; sl_cx_saved = 1; } if (!sl_topt_e_s_saved) { sl_topt_e_su = TELOPT_DEF_S_U_MODE(TELOPT_ENCRYPTION); sl_topt_e_sm = TELOPT_DEF_S_ME_MODE(TELOPT_ENCRYPTION); sl_topt_e_s_saved = 1; } if (!sl_topt_e_c_saved) { sl_topt_e_cu = TELOPT_DEF_C_U_MODE(TELOPT_ENCRYPTION); sl_topt_e_cm = TELOPT_DEF_C_ME_MODE(TELOPT_ENCRYPTION); sl_topt_e_c_saved = 1; } cx_type = param2; if (cx_type == CX_AUTO) { TELOPT_DEF_S_U_MODE(TELOPT_ENCRYPTION) = TN_NG_RQ; TELOPT_DEF_S_ME_MODE(TELOPT_ENCRYPTION) = TN_NG_RQ; TELOPT_DEF_C_U_MODE(TELOPT_ENCRYPTION) = TN_NG_RQ; TELOPT_DEF_C_ME_MODE(TELOPT_ENCRYPTION) = TN_NG_RQ; } else if (cx_type == CX_NONE) { TELOPT_DEF_S_U_MODE(TELOPT_ENCRYPTION) = TN_NG_RF; TELOPT_DEF_S_ME_MODE(TELOPT_ENCRYPTION) = TN_NG_RF; TELOPT_DEF_C_U_MODE(TELOPT_ENCRYPTION) = TN_NG_RF; TELOPT_DEF_C_ME_MODE(TELOPT_ENCRYPTION) = TN_NG_RF; } else { TELOPT_DEF_S_U_MODE(TELOPT_ENCRYPTION) = TN_NG_MU; TELOPT_DEF_S_ME_MODE(TELOPT_ENCRYPTION) = TN_NG_MU; TELOPT_DEF_C_U_MODE(TELOPT_ENCRYPTION) = TN_NG_MU; TELOPT_DEF_C_ME_MODE(TELOPT_ENCRYPTION) = TN_NG_MU; } } if (ttnproto == NP_EK4LOGIN || ttnproto == NP_EK5LOGIN || (ttnproto == NP_TELNET || ttnproto == NP_KERMIT) && ((line[0] == '*' && TELOPT_DEF_S_U_MODE(TELOPT_ENCRYPTION) == TN_NG_MU && TELOPT_DEF_S_ME_MODE(TELOPT_ENCRYPTION) == TN_NG_MU) || (line[0] != '*' && TELOPT_DEF_C_U_MODE(TELOPT_ENCRYPTION) == TN_NG_MU && TELOPT_DEF_C_ME_MODE(TELOPT_ENCRYPTION) == TN_NG_MU)) ) { if (!ck_crypt_is_installed()) { return(cx_fail(msg, "Required Encryption methods are not installed")); } } #endif /* CK_ENCRYPTION */ #ifdef RLOGCODE #ifdef CK_KERBEROS #ifdef KRB4 if (ttnproto == NP_K4LOGIN || ttnproto == NP_EK4LOGIN) { extern int krb4_autoget; char tgt[256]; char * realm; /* We don't have the full hostname at yet so */ /* we do a DNS lookup before calling ttopen() */ realm = ck_krb4_realmofhost(ckgetfqhostname(hostname)); ckmakmsg(tgt,256,"krbtgt.",realm,"@",realm); if (!ck_krb4_is_installed()) { return(cx_fail(msg, "Required authentication method (Kerberos 4) is not installed" )); } else { if ((ck_krb4_tkt_isvalid(tgt) <= 0) && (!krb4_autoget || krb4_autoget && !ck_krb4_autoget_TGT(realm))) { return(cx_fail(msg, "Kerberos 4: Ticket Getting Ticket not valid")); } } } #endif /* KRB4 */ #ifdef KRB5 if (ttnproto == NP_K5LOGIN || ttnproto == NP_EK5LOGIN || ttnproto == NP_K5U2U) { extern int krb5_autoget; char tgt[256]; char * realm; /* Must get full hostname before calling ttopen() */ realm = ck_krb5_realmofhost(ckgetfqhostname(hostname)); ckmakmsg(tgt,256,"krbtgt/",realm,"@",realm); if (!ck_krb5_is_installed()) { return(cx_fail(msg, "Required authentication method (Kerberos 5) not installed")); } else if (!((ck_krb5_tkt_isvalid(NULL,tgt) > 0) || ck_krb5_is_tgt_valid()) && (!krb5_autoget || krb5_autoget && !ck_krb5_autoget_TGT(realm))) { return(cx_fail(msg, "Kerberos 5: Ticket Getting Ticket not valid.")); } } #endif /* KRB5 */ #endif /* CK_KERBEROS */ #endif /* RLOGCODE */ #ifndef NOSPL #ifdef RLOGCODE if (username) { if (!sl_uid_saved) { ckstrncpy(sl_uidbuf,uidbuf,UIDBUFLEN); sl_uid_saved = 1; } ckstrncpy(uidbuf,username,UIDBUFLEN); uidflag = 1; } #endif /* RLOGCODE */ #ifdef TNCODE if (!sl_tn_saved) { sl_tn_wait = tn_wait_flg; sl_tn_saved = 1; } tn_wait_flg = param3; #endif /* TNCODE */ #endif /* NOSPL */ } /* if (net == NET_TCPB) */ #endif /* TCPSOCKET */ #ifndef NOSPL #ifdef CK_SECURITY if (password) { if (password[0]) { ckstrncpy(pwbuf,password,PWBUFL+1); pwflg = 1; pwcrypt = 0; } else pwflg = 0; } #endif /* CK_SECURITY */ #endif /* NOSPL */ /* Try to open - network */ ckstrncpy(ttname,line,TTNAMLEN); y = ttopen(line, &_local, mdmtyp, 0 ); #ifndef NOHTTP /* If the connection failed and we are using an HTTP Proxy * and the reason for the failure was an authentication * error, then we need to give the user to ability to * enter a username and password, just like a browser. * * I tried to do all of this within the netopen() call * but it is much too much work. */ while (y < 0 && tcp_http_proxy != NULL ) { if (tcp_http_proxy_errno == 401 || tcp_http_proxy_errno == 407 ) { char uid[UIDBUFLEN]; char pwd[256]; struct txtbox tb[2]; int ok; tb[0].t_buf = uid; tb[0].t_len = UIDBUFLEN; tb[0].t_lbl = "Proxy Userid: "; tb[0].t_dflt = NULL; tb[0].t_echo = 1; tb[1].t_buf = pwd; tb[1].t_len = 256; tb[1].t_lbl = "Proxy Passphrase: "; tb[1].t_dflt = NULL; tb[1].t_echo = 2; ok = uq_mtxt("Proxy Server Authentication Required\n", NULL, 2, tb); if (ok && uid[0]) { char * proxy_user, * proxy_pwd; proxy_user = tcp_http_proxy_user; proxy_pwd = tcp_http_proxy_pwd; tcp_http_proxy_user = uid; tcp_http_proxy_pwd = pwd; ckstrncpy(ttname,line,TTNAMLEN); y = ttopen(line, &_local, mdmtyp, 0); memset(pwd,0,sizeof(pwd)); tcp_http_proxy_user = proxy_user; tcp_http_proxy_pwd = proxy_pwd; } else break; } else break; } #endif /* NOHTTP */ if (y < 0) { slrestor(); makestr(&slmsg,"Network connection failure"); #ifdef VMS if (msg && hints && !xcmdsrc && IS_RLOGIN()) { makestr(&slmsg,"RLOGIN failure"); if (socket_errno == EACCES) { printf("*************************\n"); printf( "Hint: RLOGIN requires privileges to open an outbound port.\n"); printf( "(Use SET HINTS OFF to suppress future hints.)\n"); printf("*************************\n"); } } #else /* Not VMS... */ if (errno) { int x; debug(F111,"set host line, errno","",errno); makestr(&slmsg,ck_errstr()); if (msg) { #ifdef OS2 printf("Can't connect to %s\n",line); #else /* OS2 */ #ifdef UNIX if (hints && !xcmdsrc && IS_RLOGIN()) { makestr(&slmsg,"RLOGIN failure"); printf("*************************\n"); printf( "Hint: RLOGIN requires privileges to open an outbound port.\n"); printf( "(Use SET HINTS OFF to suppress future hints.)\n"); printf("*************************\n"); } #endif /* UNIX */ #endif /* OS2 */ } else printf("Can't connect to %s\n",line); } else #endif /* VMS */ if (msg) printf("Can't open connection to %s\n",line); continue; } else { success = 1; #ifndef NODIAL dialsta = DIA_UNK; #endif /* NODIAL */ switch (net) { case NET_TCPA: case NET_TCPB: cxtype = CXT_TCPIP; #ifdef COMMENT /* This works but it messes up interactive anonymous login */ #ifndef NOXFER #ifdef IKS_OPTION /* If we have connected to an Internet Kermit service */ /* and a /USER: switch was given, then log in. */ if (TELOPT_U(TELOPT_KERMIT) || TELOPT_ME(TELOPT_KERMIT)) { debug(F111,"cx_net IKSD /USER:",uidbuf,haveuser); if (haveuser /* && cx == 0 */ ) { /* /USER: given */ char * psw = pwbuf; /* Do we have a password? */ if (!*psw) { /* No... */ if (!strcmp(uidbuf,"anonymous") || !strcmp(uidbuf,"ftp")) { extern char myhost[]; char * u = (char *)sl_uidbuf; char * h = (char *)myhost; if (!*u) u = "nobody"; if (!*h) h = "nowhere"; ckmakmsg(tmpbuf,TMPBUFSIZ,u,"@",h,NULL); psw = tmpbuf; debug(F110,"cx_net IKSD anon",psw,0); } else { readpass(" Password: ",pwbuf,PWBUFL); } } sstate = setgen('I',uidbuf,psw,""); } } #endif /* IKS_OPTION */ #endif /* NOXFER */ #endif /* COMMENT */ break; case NET_SSH: cxtype = CXT_SSH; duplex = 0; /* Remote echo */ break; case NET_SLAT: cxtype = CXT_LAT; break; case NET_SX25: case NET_IX25: case NET_HX25: case NET_VX25: cxtype = CXT_X25; break; case NET_BIOS: cxtype = CXT_NETBIOS; break; case NET_FILE: case NET_PIPE: case NET_CMD: case NET_DLL: case NET_PTY: cxtype = CXT_PIPE; break; default: cxtype = CXT_PIPE; break; } break; } } /* for-loop */ s = line; debug(F101,"cx_net post ttopen success","",success); if (!success) { local = dfloc; /* Go back to normal */ #ifndef MAC ckstrncpy(ttname,dftty,TTNAMLEN); /* Restore default tty name */ #endif /* MAC */ speed = ttgspd(); network = 0; /* No network connection active */ haveline = 0; if (mdmtyp < 0) { /* Switching from net to async? */ if (mdmsav > -1) /* Restore modem type from last */ mdmtyp = mdmsav; /* SET MODEM command, if any. */ else mdmtyp = 0; mdmsav = -1; } return(0); /* Return failure */ } if (_local > -1) local = _local; /* Opened ok, set local/remote. */ makestr(&slmsg,NULL); network = (mdmtyp < 0); /* Remember connection type. */ ckstrncpy(ttname,s,TTNAMLEN); /* Copy name into real place. */ debug(F110,"cx_net ok",ttname,0); debug(F101,"cx_net network","",network); #ifndef NOXFER if ((reliable != SET_OFF || !setreliable)) /* Assume not reliable. */ reliable = SET_OFF; #endif /* NOXFER */ if (!network || istncomport()) speed = ttgspd(); /* Get the current speed. */ debug(F101,"cx_net local","",local); if (network) { debug(F101,"cx_net net","",net); #ifndef NOXFER /* Force prefixing of 255 on TCP/IP connections... */ if (net == NET_TCPB #ifdef SSHBUILTIN || net == NET_SSH #endif /* SSHBUILTIN */ ) { debug(F101,"cx_net reliable A","",reliable); #ifdef CK_SPEED ctlp[(unsigned)255] = 1; #endif /* CK_SPEED */ if ((reliable != SET_OFF || !setreliable)) { #ifdef TN_COMPORT if (istncomport()) { /* Telnet communication port */ reliable = SET_OFF; /* Transport is not reliable */ debug(F101,"cx_net reliable istncomport()","",1); } else { reliable = SET_ON; /* Transport is reliable end to end */ debug(F101,"cx_net reliable istncomport()","",0); } #else reliable = SET_ON; /* Transport is reliable end to end */ #endif /* ifdef TN_COMPORT */ } debug(F101,"cx_net reliable B","",reliable); } else if (net == NET_SX25 || net == NET_VX25 || net == NET_IX25 || net == NET_HX25) { duplex = 1; /* Local echo for X.25 */ if (reliable != SET_OFF || !setreliable) reliable = SET_ON; /* Transport is reliable end to end */ } #endif /* NOXFER */ } #ifndef NOXFER debug(F101,"cx_net reliable","",reliable); #endif /* NOXFER */ #ifdef OS2 if (mdmtyp <= 0) /* Network or Direct Connection */ DialerSend(OPT_KERMIT_CONNECT, 0); #endif /* OS2 */ xcx_net: setflow(); /* Set appropriate flow control */ haveline = 1; #ifdef CKLOGDIAL dolognet(); #endif /* CKLOGDIAL */ #ifndef NOSPL if (local) { if (nmac) { /* Any macros defined? */ int k; /* Yes */ k = mlook(mactab,"on_open",nmac); /* Look this up */ if (k >= 0) { /* If found, */ if (dodo(k,ttname,0) > -1) /* set it up, */ parser(1); /* and execute it */ } } } #endif /* NOSPL */ if (local && (cx || sx)) { /* /CONNECT or /SERVER switch given */ if (cx) { /* /CONNECT */ if (!gui) { /* Command was confirmed so we can pre-pop command level. */ /* This is so CONNECT module won't think we're executing a */ /* script if CONNECT was the final command in the script. */ if (cmdlvl > 0) prepop(); } #ifndef NODIAL dialsta = DIA_UNK; #endif /* NODIAL */ #ifdef LOCUS if (autolocus) { setlocus(1,1); } #endif /* LOCUS */ success = doconect(0, cmdlvl == 0 ? 1 : 0); if (ttchk() < 0) dologend(); debug(F101,"cx_net post doconect success","",success); return(success); #ifndef NOXFER } else if (sx) { /* /SERVER */ sstate = 'x'; #ifdef MAC what = W_RECV; scrcreate(); #endif /* MAC */ if (local) displa = 1; #ifdef AMIGA reqoff(); /* No DOS requestors while server */ #endif /* AMIGA */ #endif /* NOXFER */ } } #ifndef NODIAL dialsta = DIA_UNK; #endif /* NODIAL */ #ifdef LOCUS if (autolocus) { setlocus(1,1); } #endif /* LOCUS */ return(success = 1); } #endif /* NETCONN */ /* c x _ s e r i a l -- Make a serial connection */ /* Call with: device = string pointer to device name. cx = 1 to automatically enter Connect mode, 0 otherwise. sx = 1 to automatically enter Server mode, 0 otherwise. shr = 1 if device should be opened in shareable mode, 0 otherwise. flag = if no dev name given: 1 = close current connection, 0 = resume. gui = 1 if called from GUI dialog, 0 otherwise. Returns: 1 on success 0 on failure and no message printed, slmsg set to failure message. -9 on failure and message printed, ditto. */ /* these are bit flags */ #define CX_TAPI 1 #define CX_PPP 2 #define CX_SLIP 4 int #ifdef CK_ANSIC cx_serial(char *device, int cx, int sx, int shr, int flag, int gui, int special) #else /* CK_ANSIC */ cx_serial(device, cx, sx, shr, flag, gui, special) char * device; int cx, sx, shr, flag, gui, special; #endif /* CK_ANSIC */ /* cx_serial */ { int i, n, x, y, msg; int _local = -1; char *s; debug(F110,"cx_serial device",device,0); s = device; msg = (gui == 0) && msgflg; /* Whether to print messages */ success = 0; #ifndef NODIAL dialsta = DIA_UNK; #endif /* NODIAL */ debug(F101,"cx_serial mdmtyp","",mdmtyp); if (clskconnx(1) < 0) /* Close the Kermit connection */ return(success = 0); if (*s) { /* They gave a device name */ _local = -1; /* Let ttopen decide about it */ } else { /* They just said "set line" */ s = dftty; /* so go back to normal tty */ _local = dfloc; /* and mode. */ } #ifdef VMS { extern int ok_to_share; ok_to_share = shr; } #endif /* VMS */ #ifdef OS2 /* Must wait until after ttclos() */ #ifdef NT /* to change these settings */ #ifdef CK_TAPI tttapi = special & CX_TAPI; #endif /* CK_TAPI */ #else ttslip = special & CX_SLIP; ttppp = special & CX_PPP; #endif /* NT */ ttshare = shr; /* Shareable device ? */ debug(F110,"OS2 SET PORT final s",s,""); #endif /* OS2 */ /* Open the new line */ ckstrncpy(ttname,s,TTNAMLEN); if ((y = ttopen(s,&_local,mdmtyp,cdtimo)) > -1) { cxtype = (mdmtyp > 0) ? CXT_MODEM : CXT_DIRECT; #ifndef NODIAL dialsta = DIA_UNK; #ifdef CK_TAPI /* if the line is a tapi device, then we need to auto-execute */ /* SET MODEM TYPE TAPI - which we do the equivalent of here. */ if (tttapi) { extern int usermdm; usermdm = 0; initmdm(38); /* From ckudia.c n_TAPI == 38 */ } #endif /* CK_TAPI */ #endif /* NODIAL */ success = 1; } else { /* Failed */ #ifdef OS2ONLY if (!strcmp(s,dftty)) /* Do not generate an error with dftty */ ; else if (y == -6 && ttslip) { makestr(&slmsg,"Can't access SLIP driver"); if (msg) printf("?%s\n",slmsg); } else if (y == -6 && ttppp) { makestr(&slmsg,"Can't access PPP driver"); if (msg) printf("?%s\n",slmsg); } else #endif /* OS2ONLY */ if (y == -2) { makestr(&slmsg,"Timed out - no carrier"); if (msg) { printf("?%s\n",slmsg); if (hints) { printf("\n*************************\n"); printf( "HINT (Use SET HINTS OFF to suppress future hints):\n"); printf( "Try SET CARRIER OFF and SET LINE again, or else\n"); printf("SET MODEM, SET LINE, and then DIAL.\n"); printf("*************************\n\n"); } } } else if (y == -3) { makestr(&slmsg,"Access to lock denied"); if (msg) { #ifdef UNIX printf( "Sorry, write access to UUCP lockfile directory denied.\n"); #ifndef NOHINTS if (hints) { printf("\n*************************\n"); printf( "HINT (Use SET HINTS OFF to suppress future hints):\n"); printf( "Please read the installation instructions file, %sckuins.txt,\n", k_info_dir ? k_info_dir : "" ); printf( "or the UNIX appendix of the manual, \"Using C-Kermit\"\n" ); printf( "or visit http://www.columbia.edu/kermit/ckuins.html \n" ); printf("*************************\n\n"); } #endif /* NOHINTS */ #else printf("Sorry, access to lock denied: %s\n",s); #endif /* UNIX */ } } else if (y == -4) { makestr(&slmsg,"Access to device denied"); if (msg) { printf("Sorry, access to device denied: %s\n",s); #ifdef UNIX #ifndef NOHINTS if (hints) { printf("\n*************************\n"); printf( "HINT (Use SET HINTS OFF to suppress future hints):\n"); printf( "Please read the installation instructions file, %sckuins.txt,\n", k_info_dir ? k_info_dir : "" ); printf( "or the UNIX appendix of the manual, \"Using C-Kermit\".\n" ); printf("*************************\n\n"); } #endif /* NOHINTS */ #endif /* UNIX */ } } else if (y == -5) { makestr(&slmsg,"Device is in use or unavailable"); if (msg) #ifdef VMS printf( "Sorry, device is in use or otherwise unavailable: %s\n",s); #else printf("Sorry, device is in use: %s\n",s); #endif /* VMS */ } else { /* Other error. */ makestr(&slmsg,"Device open failed"); if ( #ifdef VMS 1 #else errno #endif /* VMS */ ) { int x; /* Find a safe, long buffer */ makestr(&slmsg,ck_errstr()); #ifndef VMS debug(F111,"cx_serial serial errno",slmsg,errno); #endif /* VMS */ if (msg) printf("Connection to %s failed: %s\n",s,slmsg); } else if (msg) printf("Sorry, can't open connection: %s\n",s); } } network = 0; /* No network connection active */ speed = ttgspd(); if (!success) { local = dfloc; /* Go back to normal */ #ifndef MAC ckstrncpy(ttname,dftty,TTNAMLEN); /* Restore default tty name */ #endif /* MAC */ haveline = 0; if (mdmtyp < 0) { /* Switching from net to async? */ if (mdmsav > -1) /* Restore modem type from last */ mdmtyp = mdmsav; /* SET MODEM command, if any. */ else mdmtyp = 0; mdmsav = -1; } return(msg ? -9 : 0); /* Return failure */ } if (_local > -1) local = _local; /* Opened ok, set local/remote. */ makestr(&slmsg,NULL); /* Erase SET LINE message */ ckstrncpy(ttname,s,TTNAMLEN); /* Copy name into real place. */ debug(F110,"cx_serial ok",ttname,0); #ifndef NOXFER if ((reliable != SET_OFF || !setreliable)) /* Assume not reliable. */ reliable = SET_OFF; #endif /* NOXFER */ xcx_serial: setflow(); /* Set appropriate flow control */ haveline = 1; #ifdef CKLOGDIAL dologline(); #endif /* CKLOGDIAL */ #ifndef NOSPL if (local) { if (nmac) { /* Any macros defined? */ int k; /* Yes */ k = mlook(mactab,"on_open",nmac); /* Look this up */ if (k >= 0) { /* If found, */ if (dodo(k,ttname,0) > -1) /* set it up, */ parser(1); /* and execute it */ } } } #endif /* NOSPL */ if (local && (cx || sx)) { /* /CONNECT or /SERVER switch given */ extern int carrier; if (carrier != CAR_OFF) { /* Looking for carrier? */ /* Open() turns on DTR -- wait up to a second for CD to come up */ int i, x; for (i = 0; i < 10; i++) { /* WAIT 1 CD... */ x = ttgmdm(); if (x < 0 || x & BM_DCD) break; msleep(100); } } if (cx) { /* /CONNECT */ /* Command was confirmed so we can pre-pop command level. */ /* This is so CONNECT module won't think we're executing a */ /* script if CONNECT was the final command in the script. */ if (cmdlvl > 0) prepop(); #ifndef NODIAL dialsta = DIA_UNK; #endif /* NODIAL */ #ifdef LOCUS if (autolocus) { setlocus(1,1); } #endif /* LOCUS */ success = doconect(0, cmdlvl == 0 ? 1 : 0); if (ttchk() < 0) dologend(); return(success); #ifndef NOXFER } else if (sx) { /* /SERVER */ sstate = 'x'; #ifdef MAC what = W_RECV; scrcreate(); #endif /* MAC */ if (local) displa = 1; #ifdef AMIGA reqoff(); /* No DOS requestors while server */ #endif /* AMIGA */ #endif /* NOXFER */ } } #ifndef NODIAL dialsta = DIA_UNK; #endif /* NODIAL */ #ifdef LOCUS if (autolocus) { setlocus(1,1); } #endif /* LOCUS */ return(success = 1); } /* S E T L I N -- parse name of and then open communication device. */ /* Call with: xx == XYLINE for a serial (tty) line, XYHOST for a network host, zz == 0 means if user doesn't give a device name, continue current active connection (if any); zz != 0 means if user doesn't give a device name, then close the current connection and restore the default communication device. fc == 0 to just make the connection, 1 to also CONNECT (e.g. "telnet"). */ int setlin(xx, zz, fc) int xx, zz, fc; { extern char pwbuf[], * g_pswd; extern int pwflg, pwcrypt, g_pflg, g_pcpt, nolocal; int wait; /* int tn_wait_sv; */ int mynet; int _local = -1; int c, i, haveswitch = 0; int haveuser = 0; int getval = 0; int wild = 0; /* Filespec has wildcards */ int cx = 0; /* Connect after */ int sx = 0; /* Become server after */ int a_type = -1; /* Authentication type */ int e_type = -1; /* Telnet /ENCRYPT type */ #ifdef CK_ENCRYPTION int encrypt = 0; /* Encrypted? */ #endif /* CK_ENCRYPTION */ int shr = 0; /* Share serial device */ int confirmed = 0; /* Command has been entered */ struct FDB sw, tx, nx; #ifdef OS2 struct FDB fl; #endif /* OS2 */ char * ss; #ifdef TCPSOCKET int rawflg = 0; #endif /* TCPSOCKET */ char srvbuf[SRVBUFSIZ+1]; #ifdef OS2 #ifdef NT int xxtapi = 0; #else int xxslip = 0, xxppp = 0; #endif /* NT */ #endif /* OS2 */ int dossh = 0; debug(F101,"setlin fc","",fc); debug(F101,"setlin zz","",zz); debug(F101,"setlin xx","",xx); #ifdef SSHCMD if (xx == XXSSH) { /* SSH becomes PTY SSH ... */ dossh = 1; xx = XYHOST; } else if (!ckstrcmp("ssh ",line,4,0)) { /* 2010/03/01 */ dossh = 1; xx = XYHOST; } debug(F101,"setlin dossh","",dossh); #endif /* SSHCMD */ #ifdef TNCODE /* tn_wait_sv = tn_wait_flg; */ wait = tn_wait_flg; #else /* tn_wait_sv = 0; */ wait = 0; #endif /* TNCODE */ mynet = nettype; if (nolocal) { makestr(&slmsg,"Making connections is disabled"); printf("?Sorry, making connections is disabled\n"); return(-9); } if (netsave > -1) nettype = netsave; if (fc != 0 || zz == 0) /* Preset /CONNECT switch */ cx = 1; debug(F101,"setlin cx","",cx); *srvbuf = NUL; line[0] = NUL; s = line; #ifdef NETCONN #ifdef CK_SECURITY if (tmpstring) makestr(&tmpstring,NULL); #endif /* CK_SECURITY */ if (tmpusrid) makestr(&tmpusrid,NULL); #endif /* NETCONN */ autoflow = 1; /* Enable automatic flow setting */ if (xx == XYHOST) { /* SET HOST */ #ifndef NETCONN makestr(&slmsg,"Network connections not supported"); printf("?%s\n",slmsg); return(-9); #else /* NETCONN */ #ifndef NOPUSH if ((mynet == NET_CMD || mynet == NET_PTY || dossh) && nopush) { makestr(&slmsg,"Access to external commands is disabled"); printf("?Sorry, access to external commands is disabled\n"); return(-9); } #endif /* NOPUSH */ #ifdef SSHCMD if (dossh) { /* SSH connection via pty */ int k; extern int ttyfd; /* 2010/03/01 */ k = ckstrncpy(line, sshcmd ? sshcmd : defsshcmd, LINBUFSIZ); debug(F111,"setlin sshcmd 1",line,k); if ((x = cmtxt("Optional switches and hostname","",&s,xxstring))<0) return(x); /* 2010-03-30 */ if (!*s && ttyfd < 0 && !ckstrcmp("ssh ",ttname,4,0)) { x = ckstrncpy(line,ttname,LINBUFSIZ); } else { if (!*s) { printf("?SSH to where?\n"); return(-9); } if (k < LINBUFSIZ) { line[k++] = SP; line[k] = NUL; debug(F111,"setlin sshcmd 2",line,k); } if (k < LINBUFSIZ) { ckstrncpy(&line[k],s,LINBUFSIZ-k); debug(F111,"setlin sshcmd 3",line,k); } else { printf("?Too long\n"); return(-9); } } x = cx_net( NET_PTY, /* network type */ 0, /* protocol (not used) */ line, /* host */ NULL, /* service (not used) */ NULL, /* username (not used) */ NULL, /* password (not used) */ NULL, /* command (not used) */ -1,-1,-1, /* params 1-3 (not used) */ 1, /* connect immediately */ sx, /* server? */ zz, /* close current? */ 0); /* not gui */ debug(F111,"setlin cx_net",line,x); return(x); } #endif /* SSHCMD */ /* Here we parse optional switches and then the hostname or whatever, which depends on the network type. The tricky part is, the network type can be set by a switch. */ #ifndef NOSPL makestr(&g_pswd,pwbuf); /* Save global pwbuf */ g_pflg = pwflg; /* and flag */ g_pcpt = pwcrypt; #endif /* NOSPL */ confirmed = 0; haveswitch = 0; #ifdef NETFILE if (mynet != NET_FILE) { #endif /* NETFILE */ ss = (mynet == NET_CMD || mynet == NET_PTY) ? "Command, or switch" : (mynet == NET_TCPA || mynet == NET_TCPB || mynet == NET_SSH) ? "Hostname, ip-address, or switch" : "Host or switch"; if (fc) { if (mynet == NET_TCPB && (ttnproto == NP_TELNET || ttnproto == NP_KERMIT)) { if (nshteltab) { haveswitch++; cmfdbi(&sw,_CMKEY,ss,"","",nshteltab,4,xxstring, shteltab,&nx); } } #ifdef RLOGCODE else if (mynet == NET_TCPB && ttnproto == NP_RLOGIN) { if (nshrlgtab) { haveswitch++; cmfdbi(&sw,_CMKEY,ss,"","",nshrlgtab,4,xxstring, shrlgtab,&nx); } } #endif /* RLOGCODE */ } else { haveswitch++; cmfdbi(&sw,_CMKEY,ss,"","",nshtab,4,xxstring,shtab,&nx); } #ifdef NETFILE } #endif /* NETFILE */ if (mynet == NET_TCPB || mynet == NET_SLAT || mynet == NET_SSH || mynet == NET_DEC) { cmfdbi(&nx,_CMFLD,"Host","","",0,0,xxstring,NULL,NULL); #ifdef NETFILE } else if (mynet == NET_FILE) { cmfdbi(&nx,_CMIFI,"Filename","","",0,0,xxstring,NULL,NULL); #endif /* NETFILE */ #ifdef PTYORPIPE } else if (mynet == NET_CMD || mynet == NET_PTY) { cmfdbi(&nx,_CMTXT,"Command","","",0,0,xxstring,NULL,NULL); #endif /* PTYORPIPE */ } else { cmfdbi(&nx,_CMTXT,"Host","","",0,0,xxstring,NULL,NULL); } while (1) { x = cmfdb(haveswitch ? &sw : &nx); debug(F101,"setlin cmfdb","",x); if (x < 0) if (x != -3) return(x); if (x == -3) { if ((x = cmcfm()) < 0) { return(x); } else { confirmed = 1; break; } } if (cmresult.fcode != _CMKEY) { /* Not a switch */ ckstrncpy(line,cmresult.sresult,LINBUFSIZ); /* Save the data */ s = line; /* that was parsed... */ if (cmresult.fcode == _CMIFI) { wild = cmresult.nresult; } else if (cmresult.fcode == _CMTXT) { confirmed = 1; } break; /* and break out of this loop */ } c = cmgbrk(); /* Have switch - get break character */ getval = (c == ':' || c == '='); /* Must parse an agument? */ if (getval && !(cmresult.kflags & CM_ARG)) { printf("?This switch does not take arguments\n"); return(-9); } if (!getval && (cmgkwflgs() & CM_ARG)) { printf("?This switch requires an argument\n"); return(-9); } switch (cmresult.nresult) { /* It's a switch.. */ case SL_CNX: /* /CONNECT */ cx = 1; sx = 0; break; case SL_SRV: /* /SERVER */ cx = 0; sx = 1; break; #ifdef NETCMD case SL_CMD: /* /COMMAND */ netsave = mynet; mynet = NET_CMD; break; #endif /* NETCMD */ #ifdef NETPTY case SL_PTY: /* /PTY */ netsave = mynet; mynet = NET_PTY; break; #endif /* NETPTY */ case SL_NET: /* /NETWORK-TYPE */ if ((x = cmkey(netcmd,nnets,"","",xxstring)) < 0) return(x); mynet = x; break; #ifdef CK_SECURITY case SL_PSW: /* /PASSWORD: */ if (!getval) break; debok = 0; if ((x = cmfld("Password","",&s,xxstring)) < 0) { if (x == -3) { makestr(&tmpstring,""); } else { return(x); } } else { s = brstrip(s); if ((x = (int)strlen(s)) > PWBUFL) { makestr(&slmsg,"Internal error"); printf("?Sorry, too long - max = %d\n",PWBUFL); return(-9); } makestr(&tmpstring,s); } break; #endif /* CK_SECURITY */ case SL_UID: /* /USERID: */ if (!getval) break; if ((x = cmfld("Userid","",&s,xxstring)) < 0) { if (x == -3) { makestr(&tmpusrid,""); } else { return(x); } } else { s = brstrip(s); if ((x = (int)strlen(s)) > 63) { makestr(&slmsg,"Internal error"); printf("?Sorry, too long - max = %d\n",63); return(-9); } makestr(&tmpusrid,s); haveuser = 1; } break; #ifdef CK_AUTHENTICATION #ifdef CK_SRP case SL_SRP: a_type = AUTHTYPE_SRP; break; #endif /* CK_SRP */ #ifdef CK_SSL case SL_SSL: a_type = AUTHTYPE_SSL; break; #endif /* CK_SSL */ #ifdef NT case SL_NTLM: a_type = AUTHTYPE_NTLM; break; #endif /* NT */ #ifdef CK_KERBEROS case SL_KRB4: a_type = AUTHTYPE_KERBEROS_V4; if (ttnproto == NP_RLOGIN) ttnproto = #ifdef CK_ENCRYPTION encrypt ? NP_EK4LOGIN : #endif /* CK_ENCRYPTION */ NP_K4LOGIN; else if (ttnproto == NP_K5LOGIN) ttnproto = NP_K4LOGIN; #ifdef CK_ENCRYPTION else if (ttnproto == NP_EK5LOGIN) ttnproto = NP_EK4LOGIN; #endif /* CK_ENCRYPTION */ break; case SL_KRB5: a_type = AUTHTYPE_KERBEROS_V5; if (ttnproto == NP_RLOGIN) ttnproto = #ifdef CK_ENCRYPTION encrypt ? NP_EK5LOGIN : #endif /* CK_ENCRYPTION */ NP_K5LOGIN; else if (ttnproto == NP_K4LOGIN) ttnproto = NP_K5LOGIN; #ifdef CK_ENCRYPTION else if (ttnproto == NP_EK4LOGIN) ttnproto = NP_EK5LOGIN; #endif /* CK_ENCRYPTION */ break; #endif /* CK_KERBEROS */ case SL_AUTH: { extern struct keytab autyptab[]; extern int nautyp; if ((x = cmkey(autyptab,nautyp,"type of authentication", "automatic",xxstring)) < 0) return(x); a_type = x; break; } #endif /* CK_AUTHENTICATION */ #ifdef CK_ENCRYPTION case SL_ENC: switch (ttnproto) { case NP_K4LOGIN: ttnproto = NP_EK4LOGIN; encrypt = 1; break; case NP_K5LOGIN: ttnproto = NP_EK5LOGIN; encrypt = 1; break; case NP_KERMIT: case NP_TELNET: { static struct keytab * tnetbl = NULL; static int ntnetbl = 0; x = ck_get_crypt_table(&tnetbl,&ntnetbl); debug(F101,"ck_get_crypt_table x","",x); debug(F101,"ck_get_crypt_table n","",ntnetbl); if (x < 1 || !tnetbl || ntnetbl < 1) /* Didn't get it */ x = 0; if (!x) { makestr(&slmsg,"Internal error"); printf("?Oops, types not loaded\n"); return(-9); } if ((x = cmkey(tnetbl,ntnetbl,"type of encryption", "automatic",xxstring)) < 0) return(x); e_type = x; break; } } break; #endif /* CK_ENCRYPTION */ case SL_WAIT: wait = 1; break; case SL_NOWAIT: wait = 0; break; } } #ifdef NETFILE if (mynet == NET_FILE) { /* Parsed by cmifi() */ if ((x = cmcfm()) < 0) /* Needs confirmation */ return(x); x = cx_net(mynet, /* nettype */ 0, /* protocol (not used) */ line, /* host */ "", /* port */ NULL, /* alternate username */ NULL, /* password */ NULL, /* command to execute */ 0, /* param1 */ 0, /* param2 */ 0, /* param3 */ cx, /* enter CONNECT mode */ sx, /* enter SERVER mode */ zz, /* close connection if open */ 0 /* gui */ ); } #endif /* NETFILE */ #ifdef NETCMD if (mynet == NET_CMD || mynet == NET_PTY) { char *p = NULL; if (!confirmed) { if ((x = cmtxt("Rest of command","",&s,xxstring)) < 0) return(x); if (*s) { ckstrncat(line," ",LINBUFSIZ); ckstrncat(line,s,LINBUFSIZ); } s = line; } /* s == line - so we must protect the line buffer */ s = brstrip(s); makestr(&p,s); ckstrncpy(line,p,LINBUFSIZ); makestr(&p,NULL); x = cx_net( mynet, /* nettype */ 0, /* protocol (not used) */ line, /* host */ "", /* port */ NULL, /* alternate username */ NULL, /* password */ NULL, /* command to execute */ 0, /* param1 */ 0, /* param2 */ 0, /* param3 */ cx, /* enter CONNECT mode */ sx, /* enter SERVER mode */ zz, /* close connection if open */ 0 /* gui */ ); } #endif /* NETCMD */ #ifdef NPIPE /* Named pipe */ if (mynet == NET_PIPE) { /* Needs backslash twiddling */ if (line[0]) { if (strcmp(line,"*")) { /* If remote, begin with */ char * p = NULL; makestr(&p,line); ckstrncpy(line,"\\\\",LINBUFSIZ); /* server name */ ckstrncat(line,p,LINBUFSIZ); makestr(&p,NULL); } else { line[0]='\0'; } ckstrncat(line,"\\pipe\\", LINBUFSIZ); /* Make pipe name */ ckstrncat(line,pipename, LINBUFSIZ); /* Add name of pipe */ x = cx_net(mynet, /* nettype */ 0, /* protocol (not used) */ line, /* host */ "", /* port */ NULL, /* alternate username */ NULL, /* password */ NULL, /* command to execute */ 0, /* param1 */ 0, /* param2 */ 0, /* param3 */ cx, /* enter CONNECT mode */ sx, /* enter SERVER mode */ zz, /* close connection if open */ 0 /* gui */ ); } } #endif /* NPIPE */ #ifdef SUPERLAT if (mynet == NET_SLAT) { /* Needs password, etc. */ slat_pwd[0] = NUL; /* Erase any previous password */ debok = 0; if (*line) { /* If they gave a host name... */ if ((x = cmfld( "password,\n or carriage return if no password required", "", &s, xxstring )) < 0 && x != -3) return(x); ckstrncpy(slat_pwd,s,18); /* Set the password, if any */ } if ((x = cmcfm()) < 0) return(x); /* Confirm the command */ x = cx_net(mynet, /* nettype */ 0, /* protocol (not used) */ line, /* host */ "", /* port */ NULL, /* alternate username */ NULL, /* password */ NULL, /* command to execute */ 0, /* param1 */ 0, /* param2 */ 0, /* param3 */ cx, /* enter CONNECT mode */ sx, /* enter SERVER mode */ zz, /* close connection if open */ 0 /* gui */ ); } #endif /* SUPERLAT */ #ifdef DECNET if (mynet == NET_DEC) { if (!line[0]) { /* If they gave a host name... */ printf("?hostname required\n"); return(-3); } if ((x = cmcfm()) < 0) return(x); /* Confirm the command */ x = cx_net(mynet, /* nettype */ 0, /* protocol (not used) */ line, /* host */ "", /* port */ NULL, /* alternate username */ NULL, /* password */ NULL, /* command to execute */ 0, /* param1 */ 0, /* param2 */ 0, /* param3 */ cx, /* enter CONNECT mode */ sx, /* enter SERVER mode */ zz, /* close connection if open */ 0 /* gui */ ); } #endif /* DECNET */ #ifdef SSHBUILTIN if (mynet == NET_SSH) { /* SSH connection */ int k, havehost = 0, trips = 0; int tmpver = -1, tmpxfw = -1, tmpssh_cas; #ifndef SSHTEST extern int sl_ssh_xfw, sl_ssh_xfw_saved; extern int sl_ssh_ver, sl_ssh_ver_saved; #endif /* SSHTEST */ extern struct keytab sshopnsw[]; extern int nsshopnsw; extern char *ssh_tmpcmd, *ssh_tmpport; struct FDB sw, kw, fl; debug(F110,"setlin SSH service 0",srvbuf,0); debug(F110,"setlin SSH host s 2",s,0); if (*s) { /* If they gave a host name... */ debug(F110,"setlin SSH host s 1",s,0); if (*s == '*') { makestr(&slmsg,"Incoming connections not supported"); printf( "?Sorry, incoming connections not supported for SSH.\n" ); return(-9); } ckstrncpy(line,s,LINBUFSIZ); } else { printf("?hostname required\n"); return(-3); } /* Parse [ port ] [ switches ] */ cmfdbi(&kw, /* Switches */ _CMKEY, "Port number or service name,\nor switch", "", "", nsshopnsw, 4, xxstring, sshopnsw, &fl ); cmfdbi(&fl, /* Port number or service name */ _CMFLD, "", "", "", 0, 0, xxstring, NULL, NULL ); trips = 0; /* Explained below */ while (1) { /* Parse port and switches */ y = cmfdb(&kw); /* Get a field */ if (y == -3) /* User typed CR so quit from loop */ break; if (y < 0) /* Other parse error, pass it back */ return(y); switch (cmresult.fcode) { /* Field or Keyword? */ case _CMFLD: /* Field */ ckstrncpy(srvbuf,cmresult.sresult,SRVBUFSIZ); break; case _CMKEY: /* Keyword */ switch (cmresult.nresult) { /* Which one? */ case SSHSW_PWD: if (!cmgbrk()) { printf("?This switch requires an argument\n"); return(-9); } debok = 0; if ((y = cmfld("Password","",&s,xxstring)) < 0) { if (y == -3) { makestr(&tmpstring,""); } else { return(y); } } else { s = brstrip(s); if ((y = (int)strlen(s)) > PWBUFL) { makestr(&slmsg,"Internal error"); printf("?Sorry, too long - max = %d\n",PWBUFL); return(-9); } makestr(&tmpstring,s); } break; case SSHSW_USR: /* /USER: */ if (!cmgbrk()) { printf("?This switch requires an argument\n"); return(-9); } if ((y = cmfld("Username","",&s,xxstring)) < 0) return(y); s = brstrip(s); makestr(&tmpusrid,s); break; case SSHSW_VER: if ((y = cmnum("Number","",10,&z,xxstring)) < 0) return(y); if (z < 1 || z > 2) { printf("?Out of range: %d\n",z); return(-9); } tmpver = z; break; case SSHSW_CMD: case SSHSW_SUB: if ((y = cmfld("Text","",&s,xxstring)) < 0) return(y); makestr(&ssh_tmpcmd,s); tmpssh_cas = (cmresult.nresult == SSHSW_SUB); break; case SSHSW_X11: if ((y = cmkey(onoff,2,"","on",xxstring)) < 0) return(y); tmpxfw = y; break; default: return(-2); } } if (trips++ == 0) { /* After first time through */ cmfdbi(&kw, /* only parse switches, not port. */ _CMKEY, "Switch", "", "", nsshopnsw, 4, xxstring, sshopnsw, NULL ); } } if ((y = cmcfm()) < 0) /* Get confirmation */ return(y); debug(F110,"setlin pre-cx_net line",line,0); debug(F110,"setlin pre-cx_net srvbuf",srvbuf,0); x = cx_net( mynet, /* nettype */ 0, /* protocol (not used) */ line, /* host */ srvbuf, /* port */ tmpusrid, /* alternate username */ tmpstring, /* password */ ssh_tmpcmd, /* command to execute */ tmpver, /* param1 - ssh version */ tmpssh_cas, /* param2 - ssh cas */ tmpxfw, /* param3 - ssh x11fwd */ cx, /* enter CONNECT mode */ sx, /* enter SERVER mode */ zz, /* close connection if open */ 0 /* gui */ ); if (tmpusrid) makestr(&tmpusrid,NULL); if (ssh_tmpcmd) makestr(&ssh_tmpcmd,NULL); } #endif /* SSHBUILTIN */ #ifdef TCPSOCKET if (mynet == NET_TCPB) { /* TCP/IP connection */ debug(F110,"setlin service 0",srvbuf,0); debug(F110,"setlin host s 2",s,0); if (*s) { /* If they gave a host name... */ debug(F110,"setlin host s 1",s,0); #ifdef NOLISTEN if (*s == '*') { makestr(&slmsg,"Incoming connections not supported"); printf( "?Sorry, incoming connections not supported in this version of Kermit.\n" ); return(-9); } #endif /* NOLISTEN */ #ifdef RLOGCODE /* Allow a username if rlogin is requested */ if (mynet == NET_TCPB && (ttnproto == NP_RLOGIN || ttnproto == NP_K5LOGIN || ttnproto == NP_EK5LOGIN || ttnproto == NP_K4LOGIN || ttnproto == NP_EK4LOGIN )) { int y; uidflag = 0; /* Check for "host:service" */ for ( ; (*s != '\0') && (*s != ':'); s++) ; if (*s) { /* Service, save it */ *s = NUL; ckstrncpy(srvbuf,++s,SRVBUFSIZ); } else { /* No :service, then use default. */ #ifdef VMS switch (ttnproto) { case NP_RLOGIN: ckstrncpy(srvbuf,"513",SRVBUFSIZ); /* "login" */ break; case NP_K4LOGIN: case NP_K5LOGIN: ckstrncpy(srvbuf,"543",SRVBUFSIZ); /* "klogin" */ break; case NP_EK4LOGIN: case NP_EK5LOGIN: ckstrncpy(srvbuf,"2105",SRVBUFSIZ); /* "eklogin" */ break; } #else /* VMS */ switch (ttnproto) { case NP_RLOGIN: ckstrncpy(srvbuf,"login",SRVBUFSIZ); break; case NP_K4LOGIN: case NP_K5LOGIN: ckstrncpy(srvbuf,"klogin",SRVBUFSIZ); break; case NP_EK4LOGIN: case NP_EK5LOGIN: ckstrncpy(srvbuf,"eklogin",SRVBUFSIZ); break; } #endif /* VMS */ } if (!confirmed) { y = cmfld("Userid on remote system", uidbuf,&s,xxstring); if (y < 0 && y != -3) return(y); if ((int)strlen(s) > 63) { makestr(&slmsg,"Internal error"); printf("Sorry, too long\n"); return(-9); } makestr(&tmpusrid,s); } } else { /* TELNET or SET HOST */ #endif /* RLOGCODE */ /* Check for "host:service" */ for ( ; (*s != '\0') && (*s != ':'); s++) ; if (*s) { /* Service, save it */ *s = NUL; ckstrncpy(srvbuf,++s,SRVBUFSIZ); } else if (!confirmed) { /* No :service, let them type one. */ if (*line != '*') { /* Not incoming */ if (mynet == NET_TCPB && ttnproto == NP_KERMIT) { if ((x = cmfld( "TCP service name or number", "kermit",&s,xxstring) ) < 0 && x != -3) return(x); #ifdef RLOGCODE } else if (mynet == NET_TCPB && ttnproto == NP_RLOGIN) { if ((x = cmfld( "TCP service name or number,\n or carriage return for rlogin (513)", "login",&s,xxstring) ) < 0 && x != -3) return(x); #ifdef CK_AUTHENTICATION #ifdef CK_KERBEROS } else if (mynet == NET_TCPB && (ttnproto == NP_K4LOGIN || ttnproto == NP_K5LOGIN)) { if ((x = cmfld( "TCP service name or number,\n or carriage return for klogin (543)", "klogin",&s,xxstring) ) < 0 && x != -3) return(x); } else if (mynet == NET_TCPB && (ttnproto == NP_EK4LOGIN || ttnproto == NP_EK5LOGIN)) { if ((x = cmfld( "TCP service name or number,\n or carriage return for eklogin (2105)", "eklogin",&s,xxstring) ) < 0 && x != -3) return(x); #endif /* CK_KERBEROS */ #endif /* CK_AUTHENTICATION */ #endif /* RLOGCODE */ } else { /* Do not set a default value in this call */ /* If you do then it will prevent entries */ /* in the network directory from accessing */ /* alternate ports. */ if ((x = cmfld( "TCP service name or number", "",&s,xxstring) ) < 0 && x != -3) return(x); } } else { /* Incoming connection */ if ((x = cmfld("TCP service name or number", "",&s,xxstring) ) < 0 && x != -3) return(x); } if (*s) /* If they gave a service, */ ckstrncpy(srvbuf,s,SRVBUFSIZ); /* copy it */ debug(F110,"setlin service 0.5",srvbuf,0); } #ifdef RLOGCODE } #endif /* RLOGCODE */ if (!confirmed) { char * defproto; switch (ttnproto) { case NP_RLOGIN: defproto = "/rlogin"; break; case NP_K4LOGIN: defproto = "/k4login"; break; case NP_K5LOGIN: defproto = "/k5login"; break; case NP_EK4LOGIN: defproto = "/ek4login"; break; case NP_EK5LOGIN: defproto = "/ek5login"; break; case NP_KERMIT: case NP_TELNET: defproto = "/telnet"; break; default: defproto = "/default"; } if ((x = cmkey(tcprawtab,ntcpraw,"Switch",defproto, xxstring)) < 0) { if (x != -3) return(x); else if ((x = cmcfm()) < 0) return(x); } else { rawflg = x; if ((x = cmcfm()) < 0) return(x); } } } debug(F110,"setlin pre-cx_net line",line,0); debug(F110,"setlin pre-cx_net srvbuf",srvbuf,0); x = cx_net( mynet, /* nettype */ rawflg /* protocol */, line, /* host */ srvbuf, /* port */ tmpusrid, /* alternate username */ tmpstring, /* password */ NULL, /* command to execute */ a_type, /* param1 - telnet authtype */ e_type, /* param2 - telnet enctype */ wait, /* param3 - telnet wait */ cx, /* enter CONNECT mode */ sx, /* enter SERVER mode */ zz, /* close connection if open */ 0 /* gui */ ); } #endif /* TCPSOCKET */ #ifdef CK_SECURITY if (tmpstring) makestr(&tmpstring,NULL); #endif /* CK_SECURITY */ if (tmpusrid) makestr(&tmpusrid,NULL); debug(F111,"setlin cx_net",line,x); return(x); #endif /* NETCONN */ } /* Serial tty device, possibly modem, connection... */ #ifdef OS2 /* User can type: COM1..COM8 = Regular COM port 1..8 = Synonym for COM1..COM8, is translated to COM1..COM8 _n = (n is a number) = open file handle string = any text string = name of some other kind of device, taken literally, as given. */ s = "Communication device name"; #ifdef CK_TAPI if (TAPIAvail) cktapiBuildLineTable(&tapilinetab, &_tapilinetab, &ntapiline); if (!(tapilinetab && _tapilinetab && ntapiline > 0) && xx == XYTAPI_LIN ) { makestr(&slmsg,"TAPI device not configured"); printf("\nNo TAPI Line Devices are configured for this system\n"); return(-9); } if (xx == XYTAPI_LIN) { /* Default (first) TAPI line */ s = "tapi"; /* (whatever it is) */ } else { /* Query the user */ #endif /* CK_TAPI */ /* Now parse optional switches and then device name */ confirmed = 0; cmfdbi(&sw,_CMKEY,"Device name, or switch", "","",npsltab,4,xxstring,psltab,&fl); cmfdbi(&fl,_CMFLD,"",dftty,"",0,0,xxstring,NULL,NULL); while (1) { x = cmfdb(&sw); debug(F101,"setlin cmfdb","",x); if (x < 0) if (x != -3) return(x); if (x == -3) { if ((x = cmcfm()) < 0) { return(x); } else { confirmed = 1; break; } } if (cmresult.fcode == _CMFLD) { s = cmresult.sresult; break; } else if (cmresult.fcode == _CMKEY) { switch (cmresult.nresult) { case SL_CNX: /* /CONNECT */ cx = 1; sx = 0; break; case SL_SRV: /* /SERVER */ cx = 0; sx = 1; break; case SL_SHR: /* /SHARE */ shr = 1; break; case SL_NSH: /* /NOSHARE */ shr = 0; break; } } } #ifdef CK_TAPI } #endif /* CK_TAPI */ debug(F110,"OS2 SET PORT s",s,0); y = lookup(os2devtab,s,nos2dev,&x); /* Look up in keyword table */ debug(F101,"OS2 SET PORT x","",x); debug(F101,"OS2 SET PORT y","",y); if ((y > -1) && (x >= 0 && x < 8)) { /* User typed a digit 1..8 */ s = os2devtab[x+8].kwd; /* Substitite its real name */ #ifdef NT xxtapi = 0; #else /* NT */ xxslip = xxppp = 0; #endif /* NT */ debug(F110,"OS2 SET PORT subst s",s,""); #ifndef NT } else if ((y >-1) && (x >= 16 && x < 24)) { /* SLIP access */ s = os2devtab[x-8].kwd; /* Substitite its real name */ debug(F110,"OS2 SET PORT SLIP subst s",s,""); xxslip = 1; xxppp = 0; } else if ((y >-1) && (x >= 24 && x < 32)) { /* PPP access */ s = os2devtab[x-16].kwd; /* Substitite its real name */ debug(F110,"OS2 SET PORT PPP subst s",s,""); xxppp = 1; xxslip = 0; if ((y = cmkey(os2ppptab, nos2ppp, "PPP driver interface", "ppp0", xxstring) ) < 0) return(y); debug(F101,"OS2 SET PORT PPP INTERFACE y","",y); xxppp = (y % 10) + 1; #endif /* NT */ } else if (*s == '_') { /* User used "_" prefix */ s++; /* Remove it */ /* Rest must be numeric */ debug(F110,"OS2 SET PORT HANDLE _subst s",s,0); if (!rdigits(s)) { makestr(&slmsg,"Invalid file handle"); printf("?Invalid format for file handle\n"); return(-9); } #ifdef NT xxtapi = 0; #else /* NT */ xxslip = xxppp = 0; #endif /* NT */ } else { /* A normal COMx port or a string */ s = brstrip(s); /* Strip braces if any */ #ifdef NT #ifdef CK_TAPI /* Windows TAPI support - Look up in keyword table */ if (tapilinetab && _tapilinetab && ntapiline > 0) { if (!ckstrcmp(s,"tapi",4,0)) { /* Find out what the lowest numbered TAPI device is */ /* and use it as the default. */ int j = 9999, k = -1; for (i = 0; i < ntapiline; i++) { if (tapilinetab[i].kwval < j) { j = tapilinetab[i].kwval; k = i; } } if (k >= 0) s = _tapilinetab[k].kwd; else s = ""; if ((y = cmkey(_tapilinetab,ntapiline, "TAPI device name",s,xxstring)) < 0) return(y); xxtapi = 1; /* Get the non Underscored string */ for (i = 0; i < ntapiline; i++ ) { if (tapilinetab[i].kwval == y) { s = tapilinetab[i].kwd; break; } } } else xxtapi = 0; } #endif /* CK_TAPI */ #else /* NT */ /* not OS/2 SLIP or PPP */ xxslip = xxppp = 0; #endif /* NT */ } ckstrncpy(tmpbuf,s,TMPBUFSIZ); /* Copy to a safe place */ s = tmpbuf; if ((x = cmcfm()) < 0) return(x); #else /* !OS2 */ cmfdbi(&sw,_CMKEY,"Device name, or switch", "","",npsltab,4,xxstring,psltab,&tx); cmfdbi(&tx,_CMTXT,"",dftty,"",0,0,xxstring,NULL,NULL); while (!confirmed) { x = cmfdb(&sw); debug(F101,"setlin cmfdb","",x); if (x < 0) if (x != -3) return(x); if (x == -3) { if ((x = cmcfm()) < 0) { return(x); } else { confirmed = 1; break; } } switch (cmresult.fcode) { case _CMTXT: ckstrncpy(tmpbuf,cmresult.sresult,TMPBUFSIZ); s = tmpbuf; debug(F110,"setlin CMTXT",tmpbuf,0); confirmed = 1; break; case _CMKEY: /* Switch */ debug(F101,"setlin CMKEY",tmpbuf,cmresult.nresult); switch (cmresult.nresult) { case SL_CNX: /* /CONNECT */ cx = 1; sx = 0; break; case SL_SRV: /* /SERVER */ cx = 0; sx = 1; break; #ifdef VMS case SL_SHR: /* /SHARE */ shr = 1; break; case SL_NSH: /* /NOSHARE */ shr = 0; break; #endif /* VMS */ } continue; default: debug(F101,"setlin bad cmfdb result","",cmresult.fcode); makestr(&slmsg,"Internal error"); printf("?Internal parsing error\n"); return(-9); } } #endif /* OS2 */ if (!confirmed) if ((x = cmcfm()) < 0) return(x); debug(F110,"setlin pre-cx_serial s",s,0); debug(F110,"setlin pre-cx_serial line",line,0); x = cx_serial(s,cx,sx,shr,zz,0, #ifdef OS2 #ifdef NT (xxtapi ? CX_TAPI : 0) #else (xxslip ? CX_SLIP : 0) | (xxppp ? CX_PPP : 0) #endif /* NT */ #else /* OS2 */ 0 #endif /* OS2 */ ); debug(F111,"setlin cx_serial",line,x); return(x); } #endif /* NOLOCAL */ #ifdef CKCHANNELIO /* C-Library based file-i/o package for scripts. This should be portable to all C-Kermit versions since it uses the same APIs we have always used for processing command files. The entire channel i/o package is contained herein, apart from some keyword table entries in the main keyword table and the help text in the HELP command module. On platforms like VMS and VOS, this package handles only UNIX-style stream files. If desired, it can be replaced for those platforms by <#>ifdef'ing out this code and adding the equivalent replacement routines to the ck?fio.c module, e.g. for RMS-based file i/o in ckvfio.c. */ #ifndef NOSTAT #ifdef VMS /* 2010-03-09 SMS. VAX C needs help to find "sys". It's easier not to try. */ #include #else /* def VMS */ #include #endif /* def VMS [else] */ #endif /* NOSTAT */ #ifdef NLCHAR static int z_lt = 1; /* Length of line terminator */ #else static int z_lt = 2; #endif /* NLCHAR */ struct ckz_file { /* C-Kermit file struct */ FILE * z_fp; /* Includes the C-Lib file struct */ unsigned int z_flags; /* Plus C-Kermit mode flags, */ CK_OFF_T z_nline; /* current line number if known, */ char z_name[CKMAXPATH+2]; /* and the file's name. */ }; static struct ckz_file ** z_file = NULL; /* Array of C-Kermit file structs */ static int z_inited = 0; /* Flag for array initialized */ int z_maxchan = Z_MAXCHAN; /* Max number of C-Kermit channels */ int z_openmax = CKMAXOPEN; /* Max number of open files overall */ int z_nopen = 0; /* How many channels presently open */ int z_error = 0; /* Most recent error */ int z_filcount = -1; /* Most recent FILE COUNT result */ #define RD_LINE 0 /* FILE READ options */ #define RD_CHAR 1 #define RD_SIZE 2 #define RD_TRIM 8 /* Like Snobol &TRIM = 1 */ #define RD_UNTA 9 /* Untabify */ #define WR_LINE RD_LINE /* FILE WRITE options */ #define WR_CHAR RD_CHAR #define WR_SIZE RD_SIZE #define WR_STRI 3 #define WR_LPAD 4 #define WR_RPAD 5 #ifdef UNIX extern int ckmaxfiles; /* Filled in by sysinit(). */ #endif /* UNIX */ /* See ckcker.h for error numbers */ /* See ckcdeb.h for Z_MAXCHAN and CKMAXOPEN definitions */ /* NOTE: For VMS we might be able to fill in ckmaxfiles */ /* from FILLM and CHANNELCNT -- find out about these... */ static char * fopnargs[] = { /* Mode combinations for fopen() */ #ifdef COMMENT /* All combinations of rwa */ "", "r", "w", "rw", "a", "ra", "wa", "rwa", /* Text mode */ "b", "rb", "wb", "rwb", "ab", "rab", "wab", "rwab" /* Binary mode */ #else /* Combinations and syntax permitted by C libraries... */ "", "r", "w", "r+", "a", "", "a", "", /* Text mode */ #ifdef OS2 "", "rb", "wb", "r+b", "ab", "", "ab", "" /* Binary modes for K95 */ #else #ifdef VMS "", "rb", "wb", "r+b", "ab", "", "ab", "" /* Binary modes for VMS */ #else "", "r", "w", "r+", "a", "", "a", "" /* Binary modes for UNIX */ #endif /* VMS */ #endif /* OS2 */ #endif /* COMMENT */ }; static int nfopnargs = sizeof(fopnargs) / sizeof(char *); char * /* Error messages */ ckferror(n) int n; { switch (n) { case FX_NER: return("No error"); case FX_SYS: return(ck_errstr()); case FX_EOF: return("End of file"); case FX_NOP: return("File not open"); case FX_CHN: return("Channel out of range"); case FX_RNG: return("Parameter out of range"); case FX_NMF: return("Too many files open"); case FX_FOP: return("Operation conflicts with OPEN mode"); case FX_NYI: return("OPEN mode not supported"); case FX_BOM: return("Illegal combination of OPEN modes"); case FX_ACC: return("Access denied"); case FX_FNF: return("File not found"); case FX_OFL: return("Buffer overflow"); case FX_LNU: return("Current line number unknown"); case FX_ROO: return("Off limits"); case FX_UNK: return("Operation fails - reason unknown"); default: return("Error number out of range"); } } /* Z _ O P E N -- Open a file for the requested type of access. Call with: name: Name of file to be opened. flags: Any combination of FM_xxx values except FM_EOF (ckcker.h). Returns: >= 0 on success: The assigned channel number < 0 on failure: A negative FX_xxx error code (ckcker.h). */ int z_open(name, flags) char * name; int flags; { int i, n; FILE * t; char * mode; debug(F111,"z_open",name,flags); if (!name) name = ""; /* Check name argument */ if (!name[0]) return(z_error = FX_BFN); if (flags & FM_CMD) /* Opening pipes not implemented yet */ return(z_error = FX_NYI); /* (and not portable either) */ debug(F101,"z_open nfopnargs","",nfopnargs); if (flags < 0 || flags >= nfopnargs) /* Range check flags */ return(z_error = FX_RNG); mode = fopnargs[flags]; /* Get fopen() arg */ debug(F111,"z_open fopen args",mode,flags); if (!mode[0]) /* Check for illegal combinations */ return(z_error = FX_BOM); if (!z_inited) { /* If file structs not inited */ debug(F101,"z_open z_maxchan 1","",z_maxchan); #ifdef UNIX debug(F101,"z_open ckmaxfiles","",ckmaxfiles); if (ckmaxfiles > 0) { /* Set in ck?tio.c: sysinit() */ int x; x = ckmaxfiles - ZNFILS - 5; if (x > z_maxchan) /* sysconf() value greater than */ z_maxchan = x; /* value from header files. */ debug(F101,"z_open z_maxchan 2","",z_maxchan); } #endif /* UNIX */ if (z_maxchan < Z_MINCHAN) /* Allocate at least this many. */ z_maxchan = Z_MINCHAN; debug(F101,"z_open z_maxchan 3","",z_maxchan); /* Note: This could be a pretty big chunk of memory */ /* if z_maxchan is a big number. If this becomes a problem */ /* we'll need to malloc and free each element at open/close time */ #ifdef COMMENT /* May 2006 - it's time - in current Linux this about 3MB */ if (!(z_file = (struct ckz_file *) malloc(sizeof(struct ckz_file) * (z_maxchan + 1)))) return(z_error = FX_NMF); for (i = 0; i < z_maxchan; i++) { z_file[i].z_fp = NULL; z_file[i].z_flags = 0; z_file[i].z_nline = 0; *(z_file[i].z_name) = '\0'; } #else /* New economical way, allocate storage for each channel as needed */ if (!z_file) { z_file = (struct ckz_file **)malloc((z_maxchan + 1) * sizeof(struct ckz_file *)); if (!z_file) return(z_error = FX_NMF); for (i = 0; i < z_maxchan; i++) z_file[i] = NULL; } #endif /* COMMENT */ z_inited = 1; /* Remember we initialized */ } for (n = -1, i = 0; i < z_maxchan; i++) { /* Find a free channel */ #ifdef COMMENT if (!z_file[i].z_fp) { n = i; break; } #else if (!z_file[i]) { z_file[i] = (struct ckz_file *) malloc(sizeof(struct ckz_file)); if (!z_file[i]) return(z_error = FX_NMF); n = i; break; } #endif /* COMMENT */ } if (n < 0 || n >= z_maxchan) /* Any free channels? */ return(z_error = FX_NMF); /* No, fail. */ errno = 0; z_file[n]->z_flags = 0; /* In case of failure... */ z_file[n]->z_fp = NULL; /* Set file pointer to NULL */ t = fopen(name, mode); /* Try to open the file. */ if (!t) { /* Failed... */ debug(F111,"z_open error",name,errno); #ifdef EMFILE if (errno == EMFILE) return(z_error = FX_NMF); #endif /* EMFILE */ free(z_file[n]); z_file[n] = NULL; return(z_error = (errno ? FX_SYS : FX_UNK)); /* Return error code */ } #ifdef NT #ifdef O_SEQUENTIAL if (t) /* Caching hint for NT */ _setmode(_fileno(t),O_SEQUENTIAL); #endif /* O_SEQUENTIAL */ #endif /* NT */ z_nopen++; /* Open, count it. */ z_file[n]->z_fp = t; /* Stash the file pointer */ z_file[n]->z_flags = flags; /* and the flags */ z_file[n]->z_nline = 0; /* Current line number is 0 */ z_error = 0; zfnqfp(name,CKMAXPATH,z_file[n]->z_name); /* and the file's full name */ return(n); /* Return the channel number */ } int z_close(channel) int channel; { /* Close file on given channel */ int x; FILE * t; if (!z_inited) /* Called before any files are open? */ return(z_error = FX_NOP); if (channel >= z_maxchan) /* Channel out of range? */ return(z_error = FX_CHN); if (!z_file[channel]) return(z_error = FX_NOP); if (!(t = z_file[channel]->z_fp)) /* Channel wasn't open? */ return(z_error = FX_NOP); errno = 0; /* Set errno 0 to get a good reading */ x = fclose(t); /* Try to close */ if (x == EOF) /* On failure */ return(z_error = FX_SYS); /* indicate system error. */ z_nopen--; /* Closed OK, decrement open count */ z_file[channel]->z_fp = NULL; /* Set file pointer to NULL */ z_file[channel]->z_nline = 0; /* Current line number is 0 */ z_file[channel]->z_flags = 0; /* Set flags to 0 */ *(z_file[channel]->z_name) = '\0'; /* Clear name */ free(z_file[channel]); z_file[channel] = NULL; return(z_error = 0); } /* Z _ O U T -- Output string to channel. Call with: channel: Channel number to write to. s: String to write. length > -1: How many characters of s to write. length < 0: Write entire NUL-terminated string. flags == 0: Supply line termination. flags > 0: Don't supply line termination. flags < 0: Write 'length' NUL characters. Special case: If flags > -1 and s is empty or NULL and length == 1, write 1 NUL. Returns: Number of characters written to channel on success, or negative FX_xxx error code on failure. */ int z_out(channel,s,length,flags) int channel, flags, length; char * s; { FILE * t; int x, n; char c = '\0'; if (!s) s = ""; /* Guard against null pointer */ #ifdef DEBUG if (deblog) { debug(F111,"z_out",s,channel); debug(F101,"z_out length","",length); debug(F101,"z_out flags","",flags); } #endif /* DEBUG */ if (!z_inited) /* File i/o inited? */ return(z_error = FX_NOP); if (channel >= z_maxchan) /* Channel in range? */ return(z_error = FX_CHN); if (!z_file[channel]) return(z_error = FX_NOP); if (!(t = z_file[channel]->z_fp)) /* File open? */ return(z_error = FX_NOP); if (!((z_file[channel]->z_flags) & (FM_WRI|FM_APP))) /* In write mode? */ return(z_error = FX_FOP); n = length; /* Length of string to write */ if (n < 0) { /* Negative means get it ourselves */ if (flags < 0) /* Except when told to write NULs in */ return(z_error = FX_RNG); /* which case args are inconsistent */ n = strlen(s); /* Get length of string arg */ } errno = 0; /* Reset errno */ debug(F101,"z_out n","",n); if (flags < 0) { /* Writing NULs... */ int i; for (i = 0; i < n; i++) { x = fwrite(&c,1,1,t); if (x < 1) return(z_error = (errno ? FX_SYS : FX_UNK)); } z_file[channel]->z_nline = -1; /* Current line no longer known */ z_error = 0; return(i); } else { /* Writing string arg */ if (n == 1 && !s[0]) /* Writing one char but it's NUL */ x = fwrite(&c,1,1,t); else /* Writing non-NUL char or string */ x = fwrite(s,1,n,t); debug(F101,"z_out fwrite",ckitoa(x),errno); if (x < n) /* Failure to write requested amount */ return(z_error = (errno ? FX_SYS : FX_UNK)); /* Return error */ if (flags == 0) { /* If supplying line termination */ if (fwrite("\n",1,1,t)) /* do that */ x += z_lt; /* count the terminator */ if (z_file[channel]->z_nline > -1) /* count this line */ z_file[channel]->z_nline++; } else { z_file[channel]->z_nline = -1; /* Current line no longer known */ } } z_error = 0; return(x); } #define Z_INBUFLEN 64 /* Z _ I N -- Multichannel i/o file input function. Call with: channel number to read from. s = address of destination buffer. buflen = destination buffer length. length = Number of bytes to read, must be < buflen. flags: 0 = read a line; nonzero = read the given number of bytes. Returns: Number of bytes read into buffer or a negative error code. A terminating NUL is deposited after the last byte that was read. */ int z_in(channel,s,buflen,length,flags) int channel, buflen, length, flags; char * s; /* z_in */ { int i, j, x; FILE * t; char * p; if (!z_inited) /* Check everything... */ return(z_error = FX_NOP); if (channel >= z_maxchan) return(z_error = FX_CHN); if (!z_file[channel]) return(z_error = FX_NOP); if (!(t = z_file[channel]->z_fp)) return(z_error = FX_NOP); if (!((z_file[channel]->z_flags) & FM_REA)) return(z_error = FX_FOP); if (!s) /* Check destination */ return(z_error = FX_RNG); s[0] = NUL; if (length == 0) /* Read 0 bytes - easy. */ return(z_error = 0); debug(F101,"z_in channel","",channel); debug(F101,"z_in buflen","",buflen); debug(F101,"z_in length","",length); debug(F101,"z_in flags","",flags); if (length < 0 || buflen < 0) /* Check length args */ return(z_error = FX_RNG); if (buflen <= length) return(z_error = FX_RNG); errno = 0; /* Reset errno */ if (flags) { /* Read block or byte */ int n; /* 20050912 */ n = length; /* 20050912 */ i = 0; /* 20050912 */ while (n > 0) { /* 20050912 */ i = fread(s,1,n,t); /* 20050912 */ #ifdef DEBUG if (deblog) { debug(F111,"z_in block",s,i); debug(F101,"z_in block errno","",errno); debug(F101,"z_in block ferror","",ferror(t)); debug(F101,"z_in block feof","",feof(t)); } #endif /* DEBUG */ if (i == 0) break; /* 20050912 */ s += i; /* 20050912 */ n -= i; /* 20050912 */ } /* Current line no longer known */ z_file[channel]->z_nline = (CK_OFF_T)-1; } else { /* Read line */ #ifndef COMMENT /* This method is used because it's simpler than the others */ /* and also marginally faster. */ debug(F101,"z_in getc loop","",CKFTELL(t)); for (i = 0; i < length; i++) { if ((x = getc(t)) == EOF) { debug(F101,"z_in getc error","",CKFTELL(t)); s[i] = '\0'; break; } s[i] = x; if (s[i] == '\n') { s[i] = '\0'; break; } } debug(F111,"z_in line byte loop",ckitoa(errno),i); debug(F111,"z_in line got",s,z_file[channel]->z_nline); if (z_file[channel]->z_nline > -1) z_file[channel]->z_nline++; #else #ifdef COMMENT2 /* Straightforward but strlen() slows it down. */ s[0] = '\0'; i = 0; if (fgets(s,length,t)) { i = strlen(s); if (i > 0 && s[i-1] == '\n') i--; } debug(F111,"z_in line fgets",ckitoa(errno),i); if (z_file[channel]->z_nline > -1) z_file[channel]->z_nline++; #else /* This is a do-it-yourself fgets() with its own readahead and */ /* putback. It's a bit faster than real fgets() but not enough */ /* to justify the added complexity or the risk of the ftell() and */ /* fseek() calls failing. */ int k, flag = 0; CK_OFF_T pos; for (i = 0; !flag && i <= (length - Z_INBUFLEN); i += Z_INBUFLEN) { k = ((length - i) < Z_INBUFLEN) ? length - i : Z_INBUFLEN; if ((x = fread(s+i,1,k,t)) < 1) break; s[i+x] = '\0'; for (j = 0; j < x; j++) { if (s[i+j] == '\n') { s[i+j] = '\0'; flag ++; pos = CKFTELL(t); if (pos > -1) { pos -= (x - j - 1); x = CKFSEEK(t, pos, 0); i += j; break; } else return(z_error = FX_SYS); } } } if (z_file[channel]->z_nline > -1) z_file[channel]->z_nline++; debug(F111,"z_in line chunk loop",ckitoa(errno),i); #endif /* COMMENT2 */ #endif /* COMMENT */ } debug(F111,"z_in i",ckitoa(errno),i); if (i < 0) i = 0; /* NUL-terminate result */ s[i] = '\0'; if (i > 0) { z_error = 0; return(i); } if (i == 0 && feof(t)) /* EOF on reading? */ return(z_error = FX_EOF); /* Return EOF code */ return(errno ? (z_error = -1) : i); /* Return length or system error */ } int z_flush(channel) int channel; { /* Flush output channel */ FILE * t; int x; if (!z_inited) /* Regular checks */ return(z_error = FX_NOP); if (channel >= z_maxchan) return(z_error = FX_CHN); if (!z_file[channel]) return(z_error = FX_NOP); if (!(t = z_file[channel]->z_fp)) return(z_error = FX_NOP); if (!((z_file[channel]->z_flags) & (FM_WRI|FM_APP))) /* Write access? */ return(z_error = FX_FOP); errno = 0; /* Reset errno */ x = fflush(t); /* Try to flush */ return(x ? (z_error = FX_SYS) : 0); /* Return system error or 0 if OK */ } int #ifdef CK_ANSIC z_seek(int channel, CK_OFF_T pos) /* Move file pointer to byte */ #else z_seek(channel,pos) int channel; CK_OFF_T pos; /* (seek to given position) */ #endif /* CK_ANSIC */ { int i, x = 0, rc; FILE * t; if (!z_inited) /* Check... */ return(z_error = FX_NOP); if (channel >= z_maxchan) return(z_error = FX_CHN); if (!z_file[channel]) return(z_error = FX_NOP); if (!(t = z_file[channel]->z_fp)) return(z_error = FX_NOP); if (pos < 0L) { x = 2; pos = (pos == -2) ? -1L : 0L; } errno = 0; rc = CKFSEEK(t,pos,x); /* Try to seek */ debug(F111,"z_seek",ckitoa(errno),rc); if (rc < 0) /* OK? */ return(z_error = FX_SYS); /* No. */ z_file[channel]->z_nline = ((pos || x) ? -1 : 0); return(z_error = 0); } int #ifdef CK_ANSIC z_line(int channel, CK_OFF_T pos) /* Move file pointer to line */ #else z_line(channel,pos) int channel; CK_OFF_T pos; /* (seek to given position) */ #endif /* CK_ANSIC */ { int i, len, x = 0; CK_OFF_T current = (CK_OFF_T)0, prev = (CK_OFF_T)-1, old = (CK_OFF_T)-1; FILE * t; char tmpbuf[256]; if (!z_inited) /* Check... */ return(z_error = FX_NOP); if (channel >= z_maxchan) return(z_error = FX_CHN); if (!z_file[channel]) return(z_error = FX_NOP); if (!(t = z_file[channel]->z_fp)) return(z_error = FX_NOP); debug(F101,"z_line pos","",pos); if (pos < 0L) { /* EOF wanted */ CK_OFF_T n; n = z_file[channel]->z_nline; debug(F101,"z_line n","",n); if (n < 0 || pos < 0) { rewind(t); n = 0; } while (1) { /* This could take a while... */ if ((x = getc(t)) == EOF) break; if (x == '\n') { n++; if (pos == -2) { old = prev; prev = CKFTELL(t); } } } debug(F101,"z_line old","",old); debug(F101,"z_line prev","",prev); if (pos == -2) { if ((x = z_seek(channel,old)) < 0) return(z_error = x); else n--; } z_file[channel]->z_nline = n; return(z_error = 0); } if (pos == 0L) { /* Rewind wanted */ z_file[channel]->z_nline = 0L; rewind(t); debug(F100,"z_line rewind","",0); return(0L); } tmpbuf[255] = NUL; /* Make sure buf is NUL terminated */ current = z_file[channel]->z_nline; /* Current line */ /* If necessary the following could be optimized, e.g. for positioning to a previous line in a large file without starting over. */ if (current < 0 || pos < current) { /* Not known or behind us... */ debug(F101,"z_line rewinding","",pos); if ((x = z_seek(channel, 0L)) < 0) /* Rewind */ return(z_error = x); if (pos == 0) /* If 0th line wanted we're done */ return(z_error = 0); current = 0; } while (current < pos) { /* Search for specified line */ if (fgets(tmpbuf,255,t)) { len = strlen(tmpbuf); if (len > 0 && tmpbuf[len-1] == '\n') { current++; debug(F111,"z_line read",ckitoa(len),current); } else if (len == 0) { return(z_error = FX_UNK); } } else { z_file[channel]->z_nline = -1L; debug(F101,"z_line premature EOF","",current); return(z_error = FX_EOF); } } z_file[channel]->z_nline = current; debug(F101,"z_line result","",current); z_error = 0; return(current); } char * z_getname(channel) int channel; { /* Return name of file on channel */ FILE * t; if (!z_inited) { z_error = FX_NOP; return(NULL); } if (channel >= z_maxchan) { z_error = FX_CHN; return(NULL); } if (!z_file[channel]) { z_error = FX_NOP; return(NULL); } if (!(t = z_file[channel]->z_fp)) { z_error = FX_NOP; return(NULL); } return((char *)(z_file[channel]->z_name)); } int z_getmode(channel) int channel; { /* Return OPEN modes of channel */ FILE * t; /* 0 if file not open */ #ifndef NOSTAT #ifdef NT struct _stat statbuf; #else /* NT */ struct stat statbuf; #endif /* NT */ #endif /* NOSTAT */ int x; if (!z_inited) return(0); if (channel >= z_maxchan) return(z_error = FX_CHN); if (!z_file[channel]) return(0); if (!(t = z_file[channel]->z_fp)) return(0); x = z_file[channel]->z_flags; if (feof(t)) { /* This might not work for */ x |= FM_EOF; /* output files */ #ifndef NOSTAT /* But this does if we can use it. */ } else if (stat(z_file[channel]->z_name,&statbuf) > -1) { if (CKFTELL(t) == statbuf.st_size) x |= FM_EOF; #endif /* NOSTAT */ } return(x); } CK_OFF_T z_getpos(channel) int channel; { /* Get file pointer position */ FILE * t; /* on this channel */ CK_OFF_T x; if (!z_inited) return(z_error = FX_NOP); if (channel >= z_maxchan) return(z_error = FX_CHN); if (!z_file[channel]) return(z_error = FX_NOP); if (!(t = z_file[channel]->z_fp)) return(z_error = FX_NOP); x = CKFTELL(t); return((x < 0L) ? (z_error = FX_SYS) : x); } CK_OFF_T z_getline(channel) int channel; { /* Get current line number */ FILE * t; /* in file on this channel */ CK_OFF_T rc; if (!z_inited) return(z_error = FX_NOP); if (channel >= z_maxchan) return(z_error = FX_CHN); if (!z_file[channel]) return(z_error = FX_NOP); if (!(t = z_file[channel]->z_fp)) return(z_error = FX_NOP); debug(F101,"z_getline","",z_file[channel]->z_nline); rc = z_file[channel]->z_nline; return((rc < 0) ? (z_error = FX_LNU) : rc); } int z_getfnum(channel) int channel; { /* Get file number / handle */ FILE * t; /* for file on this channel */ if (!z_inited) return(z_error = FX_NOP); if (channel >= z_maxchan) return(z_error = FX_CHN); if (!z_file[channel]) return(z_error = FX_NOP); if (!(t = z_file[channel]->z_fp)) return(z_error = FX_NOP); z_error = 0; return(fileno(t)); } /* Line-oriented counts and seeks are as dumb as they can be at the moment. Later we can speed them up by building little indexes. */ CK_OFF_T z_count(channel, what) int channel, what; { /* Count bytes or lines in file */ FILE * t; int i, x; CK_OFF_T pos, count = (CK_OFF_T)0; if (!z_inited) /* Check stuff... */ return(z_error = FX_NOP); if (channel >= z_maxchan) return(z_error = FX_CHN); if (!z_file[channel]) return(z_error = FX_NOP); if (!(t = z_file[channel]->z_fp)) return(z_error = FX_NOP); pos = CKFTELL(t); /* Save current file pointer */ errno = 0; z_error = 0; if (what == RD_CHAR) { /* Size in bytes requested */ #ifdef COMMENT if (!CKFSEEK(t,0L,2)) { /* Seek to end */ count = CKFTELL(t); /* Get file pointer */ CKFSEEK(t,pos,0); /* Restore file file pointer */ return(count); } else /* Fallback in case seek fails */ #endif /* COMMENT */ return(zgetfs(z_file[channel]->z_name)); } rewind(t); /* Line count requested - rewind. */ while (1) { /* Count lines. */ if ((x = getc(t)) == EOF) /* Stupid byte loop */ break; /* but it works as well as anything */ if (x == '\n') /* else... */ count++; } x = CKFSEEK(t,pos,0); /* Restore file pointer */ return(count); } /* User interface for generalized channel-oriented file i/o */ struct keytab fctab[] = { /* FILE subcommands */ { "close", FIL_CLS, 0 }, { "count", FIL_COU, 0 }, { "flush", FIL_FLU, 0 }, { "list", FIL_LIS, 0 }, { "open", FIL_OPN, 0 }, { "read", FIL_REA, 0 }, { "rewind", FIL_REW, 0 }, { "seek", FIL_SEE, 0 }, { "status", FIL_STA, 0 }, { "write", FIL_WRI, 0 } }; int nfctab = (sizeof (fctab) / sizeof (struct keytab)); static struct keytab fcswtab[] = { /* OPEN modes */ { "/append", FM_APP, 0 }, { "/binary", FM_BIN, 0 }, #ifdef COMMENT { "/command", FM_CMD, 0 }, /* Not implemented */ #endif /* COMMENT */ { "/read", FM_REA, 0 }, { "/write", FM_WRI, 0 } }; static int nfcswtab = (sizeof (fcswtab) / sizeof (struct keytab)); static struct keytab fclkwtab[] = { /* CLOSE options */ { "all", 1, 0 } }; static struct keytab fsekwtab[] = { /* SEEK symbols */ { "eof", 1, 0 }, { "last", 2, 0 } }; static int nfsekwtab = (sizeof (fsekwtab) / sizeof (struct keytab)); #define SEE_LINE RD_LINE /* SEEK options */ #define SEE_CHAR RD_CHAR #define SEE_REL 3 #define SEE_ABS 4 #define SEE_FIND 5 static struct keytab fskswtab[] = { { "/absolute", SEE_ABS, 0 }, { "/byte", SEE_CHAR, 0 }, { "/character", SEE_CHAR, CM_INV }, { "/find", SEE_FIND, CM_ARG }, { "/line", SEE_LINE, 0 }, { "/relative", SEE_REL, 0 } }; static int nfskswtab = (sizeof (fskswtab) / sizeof (struct keytab)); #define COU_LINE RD_LINE /* COUNT options */ #define COU_CHAR RD_CHAR #define COU_LIS 3 #define COU_NOL 4 static struct keytab fcoswtab[] = { { "/bytes", COU_CHAR, 0 }, { "/characters",COU_CHAR, CM_INV }, { "/lines", COU_LINE, 0 }, { "/list", COU_LIS, 0 }, { "/nolist", COU_NOL, 0 }, { "/quiet", COU_NOL, CM_INV } }; static int nfcoswtab = (sizeof (fcoswtab) / sizeof (struct keytab)); static struct keytab frdtab[] = { /* READ types */ { "/block", RD_SIZE, CM_INV|CM_ARG }, { "/byte", RD_CHAR, CM_INV }, { "/character", RD_CHAR, 0 }, { "/line", RD_LINE, 0 }, { "/size", RD_SIZE, CM_ARG }, { "/trim", RD_TRIM, 0 }, { "/untabify", RD_UNTA, 0 } }; static int nfrdtab = (sizeof (frdtab) / sizeof (struct keytab)); static struct keytab fwrtab[] = { /* WRITE types */ { "/block", WR_SIZE, CM_INV|CM_ARG }, { "/byte", WR_CHAR, CM_INV }, { "/character", WR_CHAR, 0 }, { "/line", WR_LINE, 0 }, { "/lpad", WR_LPAD, CM_ARG }, { "/rpad", WR_RPAD, CM_ARG }, { "/size", WR_SIZE, CM_ARG }, { "/string", WR_STRI, 0 } }; static int nfwrtab = (sizeof (fwrtab) / sizeof (struct keytab)); static char blanks[] = "\040\040\040\040"; /* Some blanks for formatting */ static char * seek_target = NULL; int dofile(op) int op; { /* Do the FILE command */ char vnambuf[VNAML]; /* Buffer for variable names */ char *vnp = NULL; /* Pointer to same */ char zfilnam[CKMAXPATH+2]; char * p, * m; struct FDB fl, sw, nu; CK_OFF_T z; int rsize, filmode = 0, relative = -1, eofflg = 0; int rc, x, y, cx, n, getval, dummy, confirmed, listing = -1; int charflag = 0, sizeflag = 0; int pad = 32, wr_lpad = 0, wr_rpad = 0, rd_trim = 0, rd_untab = 0; makestr(&seek_target,NULL); if (op == XXFILE) { /* FILE command was given */ /* Get subcommand */ if ((cx = cmkey(fctab,nfctab,"Operation","",xxstring)) < 0) { if (cx == -3) { printf("?File operation required\n"); x = -9; } return(cx); } } else { /* Shorthand command was given */ switch (op) { case XXF_CL: cx = FIL_CLS; break; /* FCLOSE */ case XXF_FL: cx = FIL_FLU; break; /* FFLUSH */ case XXF_LI: cx = FIL_LIS; break; /* FLIST */ case XXF_OP: cx = FIL_OPN; break; /* etc... */ case XXF_RE: cx = FIL_REA; break; case XXF_RW: cx = FIL_REW; break; case XXF_SE: cx = FIL_SEE; break; case XXF_ST: cx = FIL_STA; break; case XXF_WR: cx = FIL_WRI; break; case XXF_CO: cx = FIL_COU; break; default: return(-2); } } switch (cx) { /* Do requested subcommand */ case FIL_OPN: /* OPEN */ cmfdbi(&sw, /* Switches */ _CMKEY, /* fcode */ "Variable or switch", /* hlpmsg */ "", /* default */ "", /* addtl string data */ nfcswtab, /* addtl numeric data 1: tbl size */ 4, /* addtl numeric data 2: 4 = cmswi */ xxstring, /* Processing function */ fcswtab, /* Keyword table */ &fl /* Pointer to next FDB */ ); cmfdbi(&fl, /* Anything that doesn't match */ _CMFLD, /* fcode */ "Variable", /* hlpmsg */ "", "", 0, 0, NULL, NULL, NULL ); while (1) { x = cmfdb(&sw); /* Parse something */ if (x < 0) { if (x == -3) { printf("?Variable name and file name required\n"); x = -9; } return(x); } if (cmresult.fcode == _CMFLD) break; else if (cmresult.fcode == _CMKEY) { char c; c = cmgbrk(); if ((getval = (c == ':' || c == '=')) && !(cmgkwflgs() & CM_ARG)) { printf("?This switch does not take an argument\n"); return(-9); } #ifdef COMMENT /* Uncomment if we add any switches here that take args */ if (!getval && (cmgkwflgs() & CM_ARG)) { printf("?This switch requires an argument\n"); return(-9); /* (none do...) */ } #endif /* COMMENT */ filmode |= cmresult.nresult; /* OR in the file mode */ } else return(-2); } /* Not a switch - get the string */ ckstrncpy(vnambuf,cmresult.sresult,VNAML); if (!vnambuf[0] || chknum(vnambuf)) { /* (if there is one...) */ printf("?Variable name required\n"); return(-9); } vnp = vnambuf; /* Check variable-name syntax */ if (vnambuf[0] == CMDQ && (vnambuf[1] == '%' || vnambuf[1] == '&')) vnp++; y = 0; if (*vnp == '%' || *vnp == '&') { if ((y = parsevar(vnp,&x,&dummy)) < 0) { printf("?Syntax error in variable name\n"); return(-9); } } /* Assign a negative channel number in case we fail */ addmac(vnambuf,"-1"); if (!(filmode & FM_RWA)) /* If no access mode specified */ filmode |= FM_REA; /* default to /READ. */ y = 0; /* Now parse the filename */ if ((filmode & FM_RWA) == FM_WRI) { x = cmofi("Name of new file","",&s,xxstring); } else if ((filmode & FM_RWA) == FM_REA) { x = cmifi("Name of existing file","",&s,&y,xxstring); } else { x = cmiofi("Filename","",&s,&y,xxstring); debug(F111,"fopen /append x",s,x); } if (x < 0) { if (x == -3) { printf("?Filename required\n"); x = -9; } return(x); } if (y) { /* No wildcards */ printf("?Wildcards not allowed here\n"); return(-9); } if (filmode & (FM_APP|FM_WRI)) { /* Check output access */ #ifndef VMS if (zchko(s) < 0) { /* and set error code if denied */ z_error = FX_ACC; printf("?Write access denied - \"%s\"\n",s); return(-9); } #endif /* VMS */ } ckstrncpy(zfilnam,s,CKMAXPATH); /* Is OK - make safe copy */ if ((x = cmcfm()) < 0) /* Get confirmation of command */ return(x); if ((n = z_open(zfilnam,filmode)) < 0) { printf("?OPEN failed - %s: %s\n",zfilnam,ckferror(n)); return(-9); } addmac(vnambuf,ckitoa(n)); /* Assign channel number to variable */ return(success = 1); case FIL_REW: /* REWIND */ if ((x = cmnum("Channel number","",10,&n, xxstring)) < 0) { if (x == -3) { printf("?Channel number required\n"); x = -9; } return(x); } if ((x = cmcfm()) < 0) return(x); if (n == -9) return(success = 0); if (n == -8) return(success = 1); if ((rc = z_seek(n,0L)) < 0) { printf("?REWIND failed - Channel %d: %s\n",n,ckferror(rc)); return(-9); } return(success = 1); case FIL_CLS: /* CLOSE */ #ifdef COMMENT /* fdc 20100804 - bad idea */ { int i, j, k; /* Supply default if only one open */ s = ""; for (k = 0, j = 0, i = 0; i < z_maxchan; i++) { if (z_file) if (z_file[i]) if (z_file[i]->z_fp) { k++; j = i; } } if (k == 1) s = ckitoa(j); } #endif /* COMMENT */ cmfdbi(&nu, /* Second FDB - channel number */ _CMNUM, /* fcode */ "Channel number or ALL", /* Help message */ s, /* default */ "", /* addtl string data */ 10, /* addtl numeric data 1: radix */ 0, /* addtl numeric data 2: 0 */ xxstring, /* Processing function */ NULL, /* Keyword table */ &sw /* Pointer to next FDB */ ); /* Pointer to next FDB */ cmfdbi(&sw, /* First FDB - command switches */ _CMKEY, /* fcode */ "", /* help message */ "", /* Default */ "", /* No addtl string data */ 1, /* addtl numeric data 1: tbl size */ 0, /* addtl numeric data 2: 4 = cmswi */ xxstring, /* Processing function */ fclkwtab, /* Keyword table */ NULL /* Last in chain */ ); x = cmfdb(&nu); /* Parse something */ if (x < 0) { if (x == -3) { printf("?Channel number or ALL required\n"); x = -9; } return(x); } if (cmresult.fcode == _CMNUM) n = cmresult.nresult; else if (cmresult.fcode == _CMKEY) n = -1; if ((x = cmcfm()) < 0) return(x); if (n == -9) return(success = 0); if (n == -8) return(success = 1); rc = 1; if (n < 0) { int count = 0; int i; for (i = 0; i < z_maxchan; i++) { x = z_close(i); if (x == FX_SYS) { printf("?CLOSE failed - Channel %d: %s\n",n,ckferror(x)); rc = 0; } else if (x > -1) count++; } debug(F101,"FILE CLOSE ALL","",count); } else if ((x = z_close(n)) < 0) { printf("?CLOSE failed - Channel %d: %s\n",n,ckferror(x)); return(-9); } return(success = rc); case FIL_REA: /* READ */ case FIL_WRI: /* WRITE */ rsize = 0; cmfdbi(&sw, /* Switches */ _CMKEY, /* fcode */ "Channel or switch", /* hlpmsg */ "", /* default */ "", /* addtl string data */ (cx == FIL_REA) ? nfrdtab : nfwrtab, 4, /* addtl numeric data 2: 4 = cmswi */ xxstring, /* Processing function */ (cx == FIL_REA) ? frdtab : fwrtab, /* Keyword table */ &nu /* Pointer to next FDB */ ); cmfdbi(&nu, /* Channel number */ _CMNUM, /* fcode */ "Channel", "", /* default */ "", /* addtl string data */ 10, /* addtl numeric data 1: radix */ 0, /* addtl numeric data 2: 0 */ xxstring, /* Processing function */ NULL, /* Keyword table */ NULL /* Pointer to next FDB */ ); do { x = cmfdb(&sw); /* Parse something */ if (x < 0) { if (x == -3) { printf("?Channel number required\n"); x = -9; } return(x); } if (cmresult.fcode == _CMNUM) /* Channel number */ break; else if (cmresult.fcode == _CMKEY) { /* Switch */ char c; c = cmgbrk(); if ((getval = (c == ':' || c == '=')) && !(cmgkwflgs() & CM_ARG)) { printf("?This switch does not take an argument\n"); return(-9); } if (!getval && (cmgkwflgs() & CM_ARG)) { printf("?This switch requires an argument\n"); return(-9); } switch (cmresult.nresult) { case WR_LINE: charflag = 0; sizeflag = 0; rsize = 0; break; case WR_CHAR: rsize = 1; charflag = 1; sizeflag = 1; break; case WR_SIZE: if ((x = cmnum("Bytes","",10,&rsize, xxstring)) < 0) { if (x == -3) { printf("?Number required\n"); x = -9; } return(x); } if (rsize > LINBUFSIZ) { printf("?Maximum FREAD/FWRITE size is %d\n",LINBUFSIZ); rsize = 0; return(-9); } charflag = 0; sizeflag = 1; break; case WR_STRI: rsize = 1; charflag = 0; sizeflag = 0; break; case WR_LPAD: case WR_RPAD: if ((x = cmnum("Numeric ASCII character value", "32",10,&pad, xxstring)) < 0) return(x); if (cmresult.nresult == WR_LPAD) wr_lpad = 1; else wr_rpad = 1; break; case RD_TRIM: rd_trim = 1; break; case RD_UNTA: rd_untab = 1; break; } debug(F101,"FILE READ rsize 2","",rsize); } else return(-2); } while (cmresult.fcode == _CMKEY); n = cmresult.nresult; /* Channel */ debug(F101,"FILE READ/WRITE channel","",n); if (cx == FIL_WRI) { /* WRITE */ int len = 0; if ((x = cmtxt("Text","",&s,xxstring)) < 0) return(x); if (n == -9) return(success = 0); if (n == -8) return(success = 1); ckstrncpy(line,s,LINBUFSIZ); /* Make a safe copy */ s = line; s = brstrip(s); /* Strip braces */ if (charflag) { /* Write one char */ len = 1; /* So length = 1 */ rsize = 1; /* Don't supply terminator */ } else if (!sizeflag) { /* Write a string */ len = -1; /* So length is unspecified */ } else { /* Write a block of given size */ int i, k, xx; if (rsize > TMPBUFSIZ) { z_error = FX_OFL; printf("?Buffer overflow\n"); return(-9); } len = rsize; /* rsize is really length */ rsize = 1; /* Don't supply a terminator */ xx = strlen(s); /* Size of given string */ if (xx >= len) { /* Bigger or equal */ s[len] = NUL; } else if (wr_lpad) { /* Smaller, left-padding requested */ for (i = 0; i < len - xx; i++) /* Must make a copy */ tmpbuf[i] = pad; ckstrncpy(tmpbuf+i,s,TMPBUFSIZ-i); tmpbuf[len] = NUL; s = tmpbuf; /* Redirect write source */ } else if (wr_rpad) { /* Smaller with right-padding */ for (i = xx; i < len; i++) s[i] = pad; s[len] = NUL; } } if ((rc = z_out(n,s,len,rsize)) < 0) { /* Try to write */ printf("?Channel %d WRITE error: %s\n",n,ckferror(rc)); return(-9); } } else { /* FIL_REA READ */ confirmed = 0; vnambuf[0] = NUL; x = cmfld("Variable name","",&s,NULL); debug(F111,"FILE READ cmfld",s,x); if (x < 0) { if (x == -3 || !*s) { if ((x = cmcfm()) < 0) return(x); else confirmed++; } else return(x); } ckstrncpy(vnambuf,s,VNAML); debug(F111,"FILE READ vnambuf",vnambuf,confirmed); if (vnambuf[0]) { /* Variable name given, check it */ if (!confirmed) { x = cmcfm(); if (x < 0) return(x); else confirmed++; } vnp = vnambuf; if (vnambuf[0] == CMDQ && (vnambuf[1] == '%' || vnambuf[1] == '&')) vnp++; y = 0; if (*vnp == '%' || *vnp == '&') { if ((y = parsevar(vnp,&x,&dummy)) < 0) { printf("?Syntax error in variable name\n"); return(-9); } } } debug(F111,"FILE READ variable",vnambuf,confirmed); if (!confirmed) if ((x = cmcfm()) < 0) return(x); if (n == -9) return(success = 0); if (n == -8) return(success = 1); line[0] = NUL; /* Clear destination buffer */ #ifdef COMMENT if (rsize >= LINBUFSIZ) /* Don't overrun it */ rsize = LINBUFSIZ - 1; #endif /* COMMENT */ if (rsize == 0) { /* Read a line */ rc = z_in(n,line,LINBUFSIZ,LINBUFSIZ-1,0); } else { rc = z_in(n,line,LINBUFSIZ,rsize,1); /* Read a block */ } if (rc < 0) { /* Error... */ debug(F101,"FILE READ error","",rc); debug(F101,"FILE READ errno","",errno); if (rc == FX_EOF) { /* EOF - fail but no error message */ return(success = 0); } else { /* Other error - fail and print msg */ printf("?READ error: %s\n",ckferror(rc)); return(-9); } } if (rsize == 0) { /* FREAD /LINE postprocessing */ if (rd_trim) { /* Trim */ int i, k; k = strlen(line); if (k > 0) { for (i = k-1; i > 0; i--) { if (line[i] == SP || line[i] == '\t') line[i] = NUL; else break; } } } if (rd_untab) { /* Untabify */ if (untabify(line,tmpbuf,TMPBUFSIZ) > -1) ckstrncpy(line,tmpbuf,LINBUFSIZ); } } debug(F110,"FILE READ data",line,0); if (vnambuf[0]) /* Read OK - If variable name given */ addmac(vnambuf,line); /* Assign result to variable */ else /* otherwise */ printf("%s\n",line); /* just print it */ } return(success = 1); case FIL_SEE: /* SEEK */ case FIL_COU: /* COUNT */ rsize = RD_CHAR; /* Defaults to /BYTE */ cmfdbi(&sw, /* Switches */ _CMKEY, /* fcode */ "Channel or switch", /* hlpmsg */ "", /* default */ "", /* addtl string data */ ((cx == FIL_SEE) ? nfskswtab : nfcoswtab), 4, /* addtl numeric data 2: 4 = cmswi */ xxstring, /* Processing function */ ((cx == FIL_SEE) ? fskswtab : fcoswtab), &nu /* Pointer to next FDB */ ); cmfdbi(&nu, /* Channel number */ _CMNUM, /* fcode */ "Channel", "", /* default */ "", /* addtl string data */ 10, /* addtl numeric data 1: radix */ 0, /* addtl numeric data 2: 0 */ xxstring, /* Processing function */ NULL, /* Keyword table */ NULL /* Pointer to next FDB */ ); do { x = cmfdb(&sw); /* Parse something */ if (x < 0) { if (x == -3) { printf("?Channel number required\n"); x = -9; } return(x); } if (cmresult.fcode == _CMNUM) /* Channel number */ break; else if (cmresult.fcode == _CMKEY) { /* Switch */ char c; c = cmgbrk(); if ((getval = (c == ':' || c == '=')) && !(cmgkwflgs() & CM_ARG)) { printf("?This switch does not take an argument\n"); return(-9); } if (cx == FIL_SEE) { switch (cmresult.nresult) { case SEE_REL: relative = 1; break; case SEE_ABS: relative = 0; break; case SEE_FIND: { if (getval) { y = cmfld("string or pattern","",&s,xxstring); if (y < 0) return(y); makestr(&seek_target,brstrip(s)); break; } } default: rsize = cmresult.nresult; } } else if (cx == FIL_COU) { switch (cmresult.nresult) { case COU_LIS: listing = 1; break; case COU_NOL: listing = 0; break; default: rsize = cmresult.nresult; } } } } while (cmresult.fcode == _CMKEY); n = cmresult.nresult; /* Channel */ debug(F101,"FILE SEEK/COUNT channel","",n); if (cx == FIL_COU) { if ((x = cmcfm()) < 0) return(x); if (n == -9) return(success = 0); if (n == -8) return(success = 1); z_filcount = z_count(n,rsize); if (z_filcount < 0) { rc = z_filcount; printf("?COUNT error: %s\n",ckferror(rc)); return(-9); } if (listing < 0) listing = !xcmdsrc; if (listing) printf(" %ld %s%s\n", z_filcount, ((rsize == RD_CHAR) ? "byte" : "line"), ((z_filcount == 1L) ? "" : "s") ); return(success = (z_filcount > -1) ? 1 : 0); } m = (rsize == RD_CHAR) ? "Number of bytes;\n or keyword" : "Number of lines;\n or keyword"; cmfdbi(&sw, /* SEEK symbolic targets (EOF) */ _CMKEY, /* fcode */ m, "", "", /* addtl string data */ nfsekwtab, /* addtl numeric data 1: table size */ 0, /* addtl numeric data 2: 4 = cmswi */ xxstring, /* Processing function */ fsekwtab, /* Keyword table */ &nu /* Pointer to next FDB */ ); cmfdbi(&nu, /* Byte or line number */ _CMNUW, /* fcode */ "", "", /* default */ "", /* addtl string data */ 10, /* addtl numeric data 1: radix */ 0, /* addtl numeric data 2: 0 */ xxstring, /* Processing function */ NULL, /* Keyword table */ NULL /* Pointer to next FDB */ ); x = cmfdb(&sw); /* Parse something */ if (x < 0) { if (x == -3) { printf("?Channel number or EOF required\n"); x = -9; } return(x); } if (cmresult.fcode == _CMNUW) { z = cmresult.wresult; debug(F110,"FILE SEEK atmbuf",atmbuf,0); if (relative < 0) { if (cx == FIL_SEE && (atmbuf[0] == '+' || atmbuf[0] == '-')) relative = 1; else relative = 0; } } else if (cmresult.fcode == _CMKEY) { eofflg = cmresult.nresult; relative = 0; y = 0 - eofflg; } if ((x = cmcfm()) < 0) return(x); if (n == -9) return(success = 0); if (n == -8) return(success = 1); y = 1; /* Recycle this */ z_flush(n); debug(F101,"FILE SEEK relative","",relative); debug(F101,"FILE SEEK rsize","",rsize); if (rsize == RD_CHAR) { /* Seek to byte position */ if (relative > 0) { CK_OFF_T pos; pos = z_getpos(n); if (pos < (CK_OFF_T)0) { rc = pos; printf("?Relative SEEK failed: %s\n",ckferror(rc)); return(-9); } z += pos; } else { if (z < 0 && !eofflg) { /* Negative arg but not relative */ y = 0; /* Remember this was bad */ z = 0; /* but substitute 0 */ } } debug(F101,"FILE SEEK /CHAR z","",z); if (z < 0 && !eofflg) { z_error = FX_RNG; return(success = 0); } if ((rc = z_seek(n,z)) < 0) { if (rc == FX_EOF) return(success = 0); printf("?SEEK /BYTE failed - Channel %d: %s\n",n,ckferror(rc)); return(-9); } } else { /* Seek to line */ if (relative > 0) { CK_OFF_T pos; pos = z_getline(n); debug(F101,"FILE SEEK /LINE pos","",pos); if (pos < 0) { rc = pos; printf("?Relative SEEK failed: %s\n",ckferror(rc)); return(-9); } z += pos; } debug(F101,"FILE SEEK /LINE z","",z); debug(F101,"FILE SEEK /LINE eofflg","",eofflg); if (z < 0 && !eofflg) { z_error = FX_RNG; return(success = 0); } if ((rc = z_line(n,z)) < 0) { if (rc == FX_EOF) return(success = 0); printf("?SEEK /LINE failed - Channel %d: %s\n",n,ckferror(rc)); return(-9); } } /* Now, having sought to the desired starting spot, if a /FIND: target was specified, look for it now. */ if (seek_target) { int flag = 0, ispat = 0, matchresult = 0; while (!flag) { y = z_in(n,line,LINBUFSIZ,LINBUFSIZ-1,0); if (y < 0) { y = 0; break; } if (ispattern(seek_target)) { matchresult = ckmatch(seek_target,line,inpcas[cmdlvl],1+4); } else { /* This is faster */ matchresult = ckindex(seek_target,line,0,0,inpcas[cmdlvl]); } if (matchresult) { flag = 1; break; } } if (flag) { debug(F111,"FSEEK HAVE MATCH",seek_target,z_getline(n)); /* Back up to beginning of line where target found */ if ((y = z_line(n,z_getline(n)-1)) < 0) { if (rc == FX_EOF) return(success = 0); printf("?SEEK /LINE failed - Channel %d: %s\n", n,ckferror(rc)); return(-9); } debug(F101,"FSEEK LINE","",y); } } return(success = (y < 0) ? 0 : 1); case FIL_LIS: { /* LIST open files */ #ifdef CK_TTGWSIZ extern int cmd_rows, cmd_cols; #endif /* CK_TTGWSIZ */ extern int xaskmore; int i, x, n = 0, paging = 0; char * s; if ((x = cmcfm()) < 0) return(x); #ifdef CK_TTGWSIZ if (cmd_rows > 0 && cmd_cols > 0) #endif /* CK_TTGWSIZ */ paging = xaskmore; printf("System open file limit:%5d\n", z_openmax); printf("Maximum for FILE OPEN: %5d\n", z_maxchan); printf("Files currently open: %5d\n\n", z_nopen); n = 4; for (i = 0; i < z_maxchan; i++) { s = z_getname(i); /* Got one? */ if (s) { /* Yes */ char m[8]; m[0] = NUL; printf("%2d. %s",i,s); /* Print name */ n++; /* Count it */ x = z_getmode(i); /* Get modes & print them */ if (x > 0) { if (x & FM_REA) ckstrncat(m,"R",8); if (x & FM_WRI) ckstrncat(m,"W",8); if (x & FM_APP) ckstrncat(m,"A",8); if (x & FM_BIN) ckstrncat(m,"B",8); if (m[0]) printf(" (%s)",m); if (x & FM_EOF) printf(" [EOF]"); else /* And file position too */ printf(" %s",ckfstoa(z_getpos(i))); } printf("\n"); #ifdef CK_TTGWSIZ if (paging > 0) { /* Pause at end of screen */ if (n > cmd_rows - 3) { if (!askmore()) break; else n = 0; } } #endif /* CK_TTGWSIZ */ } } return(success = 1); } case FIL_FLU: /* FLUSH */ if ((x = cmnum("Channel number","",10,&n, xxstring)) < 0) { if (x == -3) { printf("?Channel number required\n"); x = -9; } return(x); } if ((x = cmcfm()) < 0) return(x); if (n == -9) return(success = 0); if (n == -8) return(success = 1); if ((rc = z_flush(n)) < 0) { printf("?FLUSH failed - Channel %d: %s\n",n,ckferror(rc)); return(-9); } return(success = 1); case FIL_STA: /* STATUS */ { int i, j, k; /* Supply default if only one open */ s = ""; for (k = 0, j = 0, i = 0; i < z_maxchan; i++) { if (z_file) if (z_file[i]) if (z_file[i]->z_fp) { k++; j = i; } } if (k == 1) s = ckitoa(j); } if ((x = cmnum("Channel number",s,10,&n, xxstring)) < 0) { if (x == -3) { if (z_nopen > 1) { printf("?%d files open - please supply channel number\n", z_nopen); return(-9); } } else return(x); } if ((y = cmcfm()) < 0) return(y); if ((!z_file || z_nopen == 0) && x == -3) { printf("No files open\n"); return(success = 1); } p = blanks + 3; /* Tricky formatting... */ if (n < 1000) p--; if (n < 100) p--; if (n < 10) p--; if ((rc = z_getmode(n)) < 0) { printf("Channel %d:%s%s\n",n,p,ckferror(rc)); return(success = 0); } else if (!rc) { printf("Channel %d:%sNot open\n",n,p); return(success = 0); } else { CK_OFF_T xx; s = z_getname(n); if (!s) s = "(name unknown)"; printf("Channel %d:%sOpen\n",n,p); printf(" File: %s\n Modes: ",s); if (rc & FM_REA) printf(" /READ"); if (rc & FM_WRI) printf(" /WRITE"); if (rc & FM_APP) printf(" /APPEND"); if (rc & FM_BIN) printf(" /BINARY"); if (rc & FM_CMD) printf(" /COMMAND"); if (rc & FM_EOF) printf(" [EOF]"); printf("\n Size: %s\n",ckfstoa(z_count(n,RD_CHAR))); printf(" At byte: %s\n",ckfstoa(z_getpos(n))); xx = z_getline(n); if (xx > (CK_OFF_T)-1) printf(" At line: %s\n",ckfstoa(xx)); return(success = 1); } default: return(-2); } } #endif /* CKCHANNELIO */ #ifndef NOSETKEY /* Save Key maps and in OS/2 Mouse maps */ int savkeys(name,disp) char * name; int disp; { char *tp; static struct filinfo xx; int savfil, i, j, k; char buf[1024]; zclose(ZMFILE); if (disp) { xx.bs = 0; xx.cs = 0; xx.rl = 0; xx.org = 0; xx.cc = 0; xx.typ = 0; xx.dsp = XYFZ_A; xx.os_specific = ""; xx.lblopts = 0; savfil = zopeno(ZMFILE,name,NULL,&xx); } else savfil = zopeno(ZMFILE,name,NULL,NULL); if (savfil) { #ifdef OS2 ztime(&tp); zsout(ZMFILE, "; Kermit 95 SAVE KEYMAP file: "); zsoutl(ZMFILE,tp); if (mskkeys) { zsoutl(ZMFILE, "if eq \"\\v(program)\" \"C-Kermit\" set mskermit keycodes on"); } else { zsoutl(ZMFILE, "if NOT eq \"\\v(program)\" \"C-Kermit\" stop 1 C-Kermit required."); zsoutl(ZMFILE,"set mskermit keycodes off"); } zsoutl(ZMFILE,""); #else /* OS2 */ ztime(&tp); zsout(ZMFILE, "; C-Kermit SAVE KEYMAP file: "); zsoutl(ZMFILE,tp); #endif /* OS2 */ zsoutl(ZMFILE,"; Clear previous keyboard mappings "); zsoutl(ZMFILE,"set key clear"); #ifdef OS2 for (k = 0; k < nttkey; k++) { if (!ttkeytab[k].flgs) { ckmakmsg(buf,1024, "set terminal key ", ttkeytab[k].kwd, " clear", NULL ); zsoutl(ZMFILE,buf); } } #endif /* OS2 */ zsoutl(ZMFILE,""); for (i = 0; i < KMSIZE; i++) { if (macrotab[i]) { int len = strlen((char *)macrotab[i]); #ifdef OS2 ckmakmsg(buf, 1024, "set key \\", ckitoa(mskkeys ? cktomsk(i) : i), " ", NULL ); #else /* OS2 */ ckmakmsg(buf, 1024, "set key \\", ckitoa(i), NULL,NULL ); #endif /* OS2 */ zsout(ZMFILE,buf); for (j = 0; j < len; j++) { char ch = macrotab[i][j]; if (ch <= SP || ch >= DEL || ch == '-' || ch == ',' || ch == '{' || ch == '}' || ch == ';' || ch == '?' || ch == '.' || ch == '\'' || ch == '\\' || ch == '/' || ch == '#') { ckmakmsg(buf,1024,"\\{",ckitoa((int)ch),"}",NULL); zsout(ZMFILE,buf); } else { ckmakmsg(buf,1024,ckctoa((char)ch),NULL,NULL,NULL); zsout(ZMFILE,buf); } } #ifdef OS2 ckmakmsg(buf,1024,"\t; ",keyname(i),NULL,NULL); zsoutl(ZMFILE,buf); #else zsoutl(ZMFILE,""); #endif /* OS2 */ } else if ( keymap[i] != i ) { #ifndef NOKVERBS if (IS_KVERB(keymap[i])) { for (j = 0; j < nkverbs; j++) if (kverbs[j].kwval == (keymap[i] & ~F_KVERB)) break; if (j != nkverbs) { #ifdef OS2 #ifdef COMMENT sprintf(buf, "set key \\%d \\K%s\t; %s", mskkeys ? cktomsk(i) : i, kverbs[j].kwd, keyname(i) ); #else ckmakxmsg(buf, /* 12 string args */ 1024, "set key \\", ckitoa(mskkeys ? cktomsk(i) : i), " \\K", kverbs[j].kwd, "\t; ", keyname(i), NULL, NULL, NULL, NULL, NULL, NULL); #endif /* COMMENT */ zsoutl(ZMFILE,buf); #else #ifdef COMMENT sprintf(buf, "set key \\%d \\K%s", i, kverbs[j].kwd); #else ckmakmsg(buf,1024, "set key \\", ckitoa(i), " \\K", kverbs[j].kwd ); #endif /* COMMENT */ zsoutl(ZMFILE,buf); #endif } } else #endif /* NOKVERBS */ { #ifdef OS2 #ifdef COMMENT sprintf(buf, "set key \\%d \\{%d}\t; %s", mskkeys ? cktomsk(i) : i, keymap[i], keyname(i) ); #else ckmakxmsg(buf, /* 8 string args */ 1024, "set key \\", ckitoa(mskkeys ? cktomsk(i) : i), " \\{", ckitoa(keymap[i]), "}\t; ", keyname(i), NULL,NULL,NULL,NULL,NULL,NULL); #endif /* COMMENT */ zsoutl(ZMFILE,buf); #else #ifdef COMMENT sprintf(buf, "set key \\%d \\{%d}", i, keymap[i]); #else ckmakxmsg(buf,1024, "set key \\", ckitoa(i), " \\{", ckitoa(keymap[i]), "}", NULL,NULL,NULL,NULL,NULL,NULL,NULL); #endif /* COMMENT */ zsoutl(ZMFILE,buf); #endif /* OS2 */ } } } #ifdef OS2 /* OS/2 also has the SET TERMINAL KEY defines */ for (k = 0; k < nttkey; k++) { extern struct keynode * ttkeymap[]; struct keynode * pnode = NULL; if (ttkeytab[k].flgs) /* Don't process CM_INV or CM_ABR */ continue; zsoutl(ZMFILE,""); ckmakmsg(buf,1024,"; SET TERMINAL KEY ",ttkeytab[k].kwd,NULL,NULL); zsoutl(ZMFILE,buf); for (pnode = ttkeymap[ttkeytab[k].kwval]; pnode; pnode = pnode->next ) { switch (pnode->def.type) { case key: #ifdef COMMENT sprintf(buf, "set terminal key %s \\%d \\{%d}\t; %s", ttkeytab[k].kwd, mskkeys ? cktomsk(pnode->key) : pnode->key, pnode->def.key.scancode, keyname(pnode->key) ); #else ckmakxmsg(buf, 1024, "set terminal key ", ttkeytab[k].kwd, " \\", ckitoa(mskkeys ? cktomsk(pnode->key) : pnode->key), " \\{", ckitoa(pnode->def.key.scancode), "}\t; ", keyname(pnode->key), NULL,NULL,NULL,NULL ); #endif /* COMMENT */ zsoutl(ZMFILE,buf); break; case kverb: for (j = 0; j < nkverbs; j++) if (kverbs[j].kwval == (pnode->def.kverb.id & ~F_KVERB)) break; if (j != nkverbs) { #ifdef COMMENT sprintf(buf, "set terminal key %s \\%d \\K%s\t; %s", ttkeytab[k].kwd, mskkeys ? cktomsk(pnode->key) : pnode->key, kverbs[j].kwd, keyname(pnode->key) ); #else ckmakxmsg(buf, 1024, "set terminal key ", ttkeytab[k].kwd, " \\", ckitoa(mskkeys ? cktomsk(pnode->key) : pnode->key), " \\K", kverbs[j].kwd, "\t; ", keyname(pnode->key), NULL,NULL,NULL,NULL ); #endif /* COMMENT */ zsoutl(ZMFILE,buf); } break; case macro: { int len = strlen((char *)pnode->def.macro.string); #ifdef COMMENT sprintf(buf,"set terminal key %s \\%d ", ttkeytab[k].kwd, mskkeys ? cktomsk(pnode->key) : pnode->key); #else ckmakxmsg(buf, 1024, "set terminal key ", ttkeytab[k].kwd, " \\", ckitoa(mskkeys ? cktomsk(pnode->key) : pnode->key), " ", NULL,NULL,NULL,NULL,NULL,NULL,NULL ); #endif /* COMMENT */ zsout(ZMFILE,buf); for (j = 0; j < len; j++) { char ch = pnode->def.macro.string[j]; if (ch <= SP || ch >= DEL || ch == '-' || ch == ',' || ch == '{' || ch == '}' || ch == ';' || ch == '?' || ch == '.' || ch == '\'' || ch == '\\' || ch == '/' || ch == '#') { ckmakmsg(buf,1024, "\\{",ckitoa((int)ch),"}",NULL); zsout(ZMFILE,buf); } else { ckmakmsg(buf,1024, ckctoa((char)ch),NULL,NULL,NULL); zsout(ZMFILE,buf); } } ckmakmsg(buf,1024,"\t; ",keyname(pnode->key),NULL,NULL); zsoutl(ZMFILE,buf); break; } case literal: { int len = strlen((char *)pnode->def.literal.string); #ifdef COMMENT sprintf(buf,"set terminal key %s /literal \\%d ", ttkeytab[k].kwd, mskkeys ? cktomsk(pnode->key) : pnode->key); #else ckmakxmsg(buf, 1024, "set terminal key ", ttkeytab[k].kwd, " /literal \\", ckitoa(mskkeys ? cktomsk(pnode->key) : pnode->key), " ", NULL,NULL,NULL,NULL,NULL,NULL,NULL); #endif /* COMMENT */ zsout(ZMFILE,buf); for (j = 0; j < len; j++) { char ch = pnode->def.literal.string[j]; if (ch <= SP || ch >= DEL || ch == '-' || ch == ',' || ch == '{' || ch == '}' || ch == ';' || ch == '?' || ch == '.' || ch == '\'' || ch == '\\' || ch == '/' || ch == '#') { ckmakmsg(buf,1024, "\\{",ckitoa((int)ch),"}",NULL); zsout(ZMFILE,buf); } else { ckmakmsg(buf,1024, ckctoa((char)ch),NULL,NULL,NULL); zsout(ZMFILE,buf); } } ckmakmsg(buf,1024,"\t; ",keyname(pnode->key),NULL,NULL); zsoutl(ZMFILE,buf); break; } case esc: #ifdef COMMENT sprintf(buf, "set terminal key %s /literal \\%d \\{%d}\\{%d}\t; %s", ttkeytab[k].kwd, mskkeys ? cktomsk(pnode->key) : pnode->key, ISDG200(ttkeytab[k].kwval) ? 30 : 27, pnode->def.esc.key & ~F_ESC, keyname(pnode->key) ); #else ckmakxmsg(buf, 1024, "set terminal key ", ttkeytab[k].kwd, " /literal \\", ckitoa(mskkeys ? cktomsk(pnode->key) : pnode->key), " \\{", ckitoa(ISDG200(ttkeytab[k].kwval) ? 30 : 27), "}\\{", ckitoa(pnode->def.esc.key & ~F_ESC), "}\t; ", keyname(pnode->key), NULL,NULL ); #endif /* COMMENT */ zsoutl(ZMFILE,buf); break; case csi: #ifdef COMMENT sprintf(buf, "set terminal key %s /literal \\%d \\{27}[\\{%d}\t; %s", ttkeytab[k].kwd, mskkeys ? cktomsk(pnode->key) : pnode->key, pnode->def.csi.key & ~F_CSI, keyname(pnode->key) ); #else ckmakxmsg(buf, 1024, "set terminal key ", ttkeytab[k].kwd, " /literal \\", ckitoa(mskkeys ? cktomsk(pnode->key) : pnode->key), " \\{27}[\\{", ckitoa(pnode->def.csi.key & ~F_CSI), "}\t; ", keyname(pnode->key), NULL,NULL,NULL,NULL ); #endif /* COMMENT */ zsoutl(ZMFILE,buf); break; default: continue; } } } #endif /* OS2 */ zsoutl(ZMFILE,""); zsoutl(ZMFILE,"; End"); zclose(ZMFILE); return(success = 1); } else { return(success = 0); } } #endif /* NOSETKEY */ #define SV_SCRL 0 #define SV_HIST 1 #ifdef OS2 #ifndef NOLOCAL static struct keytab trmtrmopt[] = { { "scrollback", SV_SCRL, 0 } }; #endif /* NOLOCAL */ #endif /* OS2 */ static struct keytab cmdtrmopt[] = { #ifdef CK_RECALL { "history", SV_HIST, 0 }, #endif /* CK_RECALL */ #ifdef OS2 #ifndef NOLOCAL { "scrollback", SV_SCRL, 0 }, #endif /* NOLOCAL */ #endif /* OS2 */ { "", 0, 0 } }; static int ncmdtrmopt = (sizeof (cmdtrmopt) / sizeof (struct keytab)) - 1; #ifdef OS2 #ifndef NOLOCAL _PROTOTYP(int savscrbk, (int, char *, int)); #endif /* NOLOCAL */ #endif /* OS2 */ #ifdef CK_RECALL _PROTOTYP(int savhistory, (char *, int)); #endif /* CK_RECALL */ int dosave(xx) int xx; { int x, y = 0, disp; char * s = NULL; extern struct keytab disptb[]; #ifdef ZFNQFP struct zfnfp * fnp; #endif /* ZFNQFP */ #ifndef NOSETKEY if (xx == XSKEY) { /* SAVE KEYMAP.. */ z = cmofi("Name of Kermit command file","keymap.ksc",&s,xxstring); } else { #endif /* NOSETKEY */ switch (xx) { case XSCMD: /* SAVE COMMAND.. */ if ((y = cmkey(cmdtrmopt, ncmdtrmopt, "What to save", #ifdef OS2 "scrollback", #else "history", #endif /* OS2 */ xxstring)) < 0) return(y); break; #ifdef OS2 #ifndef NOLOCAL case XSTERM: /* SAVE TERMINAL.. */ if ((y = cmkey(trmtrmopt,1, "What to save","scrollback",xxstring)) < 0) return(y); break; #endif /* NOLOCAL */ #endif /* OS2 */ } z = cmofi("Filename", ((y == SV_SCRL) ? "scrollbk.txt" : "history.txt"), &s, xxstring ); #ifndef NOSETKEY } #endif /* NOSETKEY */ if (z < 0) /* Check output-file parse results */ return(z); if (z == 2) { printf("?Sorry, %s is a directory name\n",s); return(-9); } #ifdef ZFNQFP if ((fnp = zfnqfp(s,TMPBUFSIZ - 1,tmpbuf))) {/* Convert to full pathname */ if (fnp->fpath) if ((int) strlen(fnp->fpath) > 0) s = fnp->fpath; } #endif /* ZFNQFP */ ckstrncpy(line,s,LINBUFSIZ); /* Make safe copy of pathname */ s = line; #ifdef MAC z = 0; #else /* Get NEW/APPEND disposition */ if ((z = cmkey(disptb,2,"Disposition","new",xxstring)) < 0) return(z); #endif /* MAC */ disp = z; if ((x = cmcfm()) < 0) /* Get confirmation */ return(x); switch (xx) { /* Do action.. */ #ifndef NOSETKEY case XSKEY: /* SAVE KEYMAP */ return (savkeys(s,disp)); #endif /* NOSETKEY */ case XSCMD: /* SAVE COMMAND.. */ #ifdef OS2 #ifndef NOLOCAL if (y == SV_SCRL) /* .. SCROLLBACK */ return(success = savscrbk(VCMD,s,disp)); #endif /* NOLOCAL */ #endif /* OS2 */ #ifndef NORECALL if (y == SV_HIST) /* .. HISTORY */ return(success = savhistory(s,disp)); #endif /* NORECALL */ break; #ifdef OS2 #ifndef NOLOCAL case XSTERM: /* SAVE TERMINAL SCROLLBACK */ return(success = savscrbk(VTERM,s,disp)); #endif /* NOLOCAL */ #endif /* OS2 */ } success = 0; return(-2); } /* R E A D T E X T Read text with a custom prompt into given buffer using command parser but with no echoing or entry into recall buffer. */ int readtext(prmpt, buffer, bufsiz) char * prmpt; char * buffer; int bufsiz; { #ifdef CK_RECALL extern int on_recall; /* Around Password prompting */ #endif /* CK_RECALL */ int rc; #ifndef NOLOCAL #ifdef OS2 extern int vmode; extern int startflags; int vmode_sav = vmode; if (!prmpt) prmpt = ""; if (win95_popup && !(startflags & 96) #ifdef IKSD && !inserver #endif /* IKSD */ ) return(popup_readtext(vmode,NULL,prmpt,buffer,bufsiz,0)); if (vmode == VTERM) { vmode = VCMD; VscrnIsDirty(VTERM); VscrnIsDirty(VCMD); } #endif /* OS2 */ #endif /* NOLOCAL */ #ifdef CK_RECALL on_recall = 0; #endif /* CK_RECALL */ cmsavp(psave,PROMPTL); /* Save old prompt */ cmsetp(prmpt); /* Make new prompt */ concb((char)escape); /* Put console in cbreak mode */ cmini(1); /* and echo mode */ if (pflag) prompt(xxstring); /* Issue prompt if at top level */ cmres(); /* Reset the parser */ for (rc = -1; rc < 0; ) { /* Prompt till they answer */ rc = cmtxt("","",&s,NULL); /* Get a literal line of text */ cmres(); /* Reset the parser again */ } ckstrncpy(buffer,s,bufsiz); cmsetp(psave); /* Restore original prompt */ #ifndef NOLOCAL #ifdef OS2 if (vmode != vmode_sav) { vmode = VTERM; VscrnIsDirty(VCMD); VscrnIsDirty(VTERM); } #endif /* OS2 */ #endif /* NOLOCAL */ return(0); } #endif /* NOICP */ /* A general function to allow a Password or other information */ /* to be read from the command prompt without it going into */ /* the recall buffer or being echo'd. */ int readpass(prmpt, buffer, bufsiz) char * prmpt; char * buffer; int bufsiz; { int x; #ifdef NOICP if (!prmpt) prmpt = ""; printf("%s", prmpt); #ifdef COMMENT /* Some linkers won't allow this because it's unsafe */ gets(buffer); #else /* COMMENT */ { int c, i; char * p; p = buffer; for (i = 0; i < bufsiz-1; i++) { if ((c = getchar()) == EOF) break; if (c < SP) break; buffer[i] = c; } buffer[i] = NUL; } #endif /* COMMENT */ return(1); #else /* NOICP */ #ifdef CK_RECALL extern int on_recall; /* around Password prompting */ #endif /* CK_RECALL */ int rc; #ifndef NOLOCAL #ifdef OS2 extern int vmode; extern int startflags; int vmode_sav = vmode; #endif /* OS2 */ #endif /* NOLOCAL */ #ifdef CKSYSLOG int savlog; #endif /* CKSYSLOG */ if (!prmpt) prmpt = ""; #ifndef NOLOCAL debok = 0; /* Don't log */ #ifdef OS2 if (win95_popup && !(startflags & 96) #ifdef IKSD && !inserver #endif /* IKSD */ ) { x = popup_readpass(vmode,NULL,prmpt,buffer,bufsiz,0); debok = 1; return(x); } #endif /* OS2 */ #endif /* NOLOCAL */ #ifdef CKSYSLOG savlog = ckxsyslog; /* Save and turn off syslogging */ ckxsyslog = 0; #endif /* CKSYSLOG */ #ifndef NOLOCAL #ifdef OS2 if (vmode == VTERM) { vmode = VCMD; VscrnIsDirty(VTERM); VscrnIsDirty(VCMD); } #endif /* OS2 */ #endif /* NOLOCAL */ #ifdef CK_RECALL on_recall = 0; #endif /* CK_RECALL */ cmsavp(psave,PROMPTL); /* Save old prompt */ cmsetp(prmpt); /* Make new prompt */ concb((char)escape); /* Put console in cbreak mode */ cmini(0); /* and no-echo mode */ if (pflag) prompt(xxstring); /* Issue prompt if at top level */ cmres(); /* Reset the parser */ for (rc = -1; rc < 0; ) { /* Prompt till they answer */ rc = cmtxt("","",&s,NULL); /* Get a literal line of text */ cmres(); /* Reset the parser again */ } ckstrncpy(buffer,s,bufsiz); printf("\r\n"); /* Echo a CRLF */ cmsetp(psave); /* Restore original prompt */ cmini(1); /* Restore echo mode */ #ifndef NOLOCAL #ifdef OS2 if (vmode != vmode_sav) { vmode = VTERM; VscrnIsDirty(VCMD); VscrnIsDirty(VTERM); } #endif /* OS2 */ #endif /* NOLOCAL */ #ifdef CKSYSLOG ckxsyslog = savlog; /* Restore syslogging */ #endif /* CKSYSLOG */ debok = 1; return(0); #endif /* NOICP */ } #ifndef NOICP struct keytab authtab[] = { /* Available authentication types */ #ifdef CK_KERBEROS { "k4", AUTH_KRB4, CM_INV }, { "k5", AUTH_KRB5, CM_INV }, { "kerberos4", AUTH_KRB4, 0 }, { "kerberos5", AUTH_KRB5, 0 }, { "krb4", AUTH_KRB4, CM_INV }, { "krb5", AUTH_KRB5, CM_INV }, #endif /* CK_KERBEROS */ #ifdef NT { "ntlm", AUTH_NTLM, 0 }, #endif /* NT */ #ifdef CK_SRP { "srp", AUTH_SRP, 0 }, #endif /* CK_SRP */ #ifdef CK_SSL { "ssl", AUTH_SSL, 0 }, #endif /* CK_SSL */ { "", 0, 0 } }; int authtabn = sizeof(authtab)/sizeof(struct keytab)-1; #ifdef CK_KERBEROS struct keytab kerbtab[] = { /* Kerberos authentication types */ { "k4", AUTH_KRB4, CM_INV }, { "k5", AUTH_KRB5, CM_INV }, { "kerberos4", AUTH_KRB4, 0 }, { "kerberos5", AUTH_KRB5, 0 }, { "krb4", AUTH_KRB4, CM_INV }, { "krb5", AUTH_KRB5, CM_INV } }; int kerbtabn = sizeof(kerbtab)/sizeof(struct keytab); static struct keytab krb_s_tbl[] = { /* AUTHENTICATE command switches: */ { "/cache", KRB_S_CA, CM_ARG } }; static int krb_s_n = sizeof(krb_s_tbl)/sizeof(struct keytab); static struct keytab krb_v_tbl[] = { /* KERBEROS version values: */ { "4", 4, 0 }, { "5", 5, 0 }, /* (add others as needed...) */ { "auto", 0, 0 } /* Note: 0 = auto */ }; static int krb_v_n = sizeof(krb_v_tbl)/sizeof(struct keytab); static struct keytab krb_a_tbl[] = { /* KERBEROS actions: */ { "destroy", KRB_A_DE, 0 }, { "initialize", KRB_A_IN, 0 }, { "list-credentials", KRB_A_LC, 0 } }; static int krb_a_n = sizeof(krb_a_tbl)/sizeof(struct keytab); static struct keytab krb4_i_tbl[] = { /* KERBEROS 4 INITIALIZE switches: */ { "/brief", KRB_I_BR, 0 }, /* /BRIEF */ { "/instance", KRB_I_IN, CM_ARG }, /* /INSTANCE: */ { "/lifetime", KRB_I_LF, CM_ARG }, /* /LIFETIME: */ { "/not-preauth", KRB_I_NPA, 0 }, /* /NOT-PREAUTH */ { "/password", KRB_I_PW, CM_ARG }, /* /PASSWORD: */ #ifdef OS2 { "/popup", KRB_I_POP, 0 }, /* /POPUP */ #endif /* OS2 */ { "/preauth", KRB_I_PA, 0 }, /* /PREAUTH */ { "/realm", KRB_I_RL, CM_ARG }, /* /REALM: */ { "/verbose", KRB_I_VB, 0 }, /* /VERBOSE */ { "", 0, 0 } }; static int krb4_i_n = sizeof(krb4_i_tbl)/sizeof(struct keytab) - 1; static struct keytab krb5_i_tbl[] = { /* KERBEROS 5 INITIALIZE switches: */ { "/addresses", KRB_I_ADR, CM_ARG }, { "/forwardable", KRB_I_FW, 0 }, /* /FORWARDABLE */ { "/instance", KRB_I_IN, CM_ARG }, /* /INSTANCE: */ { "/k4", KRB_I_K4, CM_INV }, /* /KERBEROS4 */ { "/kerberos4", KRB_I_K4, 0 }, /* /KERBEROS4 */ { "/krb4", KRB_I_K4, CM_INV }, /* /KERBEROS4 */ { "/lifetime", KRB_I_LF, CM_ARG }, /* /LIFETIME: */ { "/no-addresses", KRB_I_NAD, 0 }, /* /NO-ADDRESSES */ { "/no-k4", KRB_I_NK4, CM_INV },/* /NO-KERBEROS4 */ { "/no-kerberos4", KRB_I_NK4, 0 }, /* /NO-KERBEROS4 */ { "/no-krb4", KRB_I_NK4, CM_INV },/* /NO-KERBEROS4 */ { "/not-forwardable", KRB_I_NFW, 0 }, /* /NOT-FORWARDABLE */ { "/not-proxiable", KRB_I_NPR, 0 }, /* /NOT-PROXIABLE */ { "/password", KRB_I_PW, CM_ARG }, /* /PASSWORD: */ #ifdef OS2 { "/popup", KRB_I_POP, 0 }, /* /POPUP */ #endif /* OS2 */ { "/postdate", KRB_I_PD, CM_ARG }, /* /POSTDATE: */ { "/pr", KRB_I_PR, CM_INV|CM_ABR }, /* to allow for */ { "/pro", KRB_I_PR, CM_INV|CM_ABR }, /* different spellings */ { "/prox", KRB_I_PR, CM_INV|CM_ABR }, { "/proxiable", KRB_I_PR, 0 }, /* /PROXIABLE */ { "/proxyable", KRB_I_PR, CM_INV }, /* /PROXYABLE */ { "/realm", KRB_I_RL, CM_ARG }, /* /REALM: */ { "/renew", KRB_I_RN, 0 }, /* /RENEW */ { "/renewable", KRB_I_RB, CM_ARG }, /* /RENEWABLE: */ { "/service", KRB_I_SR, CM_ARG }, /* /SERVICE: */ { "/validate", KRB_I_VA, 0 }, /* /VALIDATE */ { "", 0, 0 } }; static int krb5_i_n = sizeof(krb5_i_tbl)/sizeof(struct keytab) - 1; static struct keytab klctab[] = { /* List Credentials switches*/ { "/addresses", XYKLCAD, 0 }, { "/encryption", XYKLCEN, 0 }, { "/flags", XYKLCFL, 0 } }; static int nklctab = sizeof(klctab)/sizeof(struct keytab); extern int krb_action; extern struct krb_op_data krb_op; extern struct krb5_list_cred_data krb5_lc; extern struct krb5_init_data krb5_init; extern char * krb5_d_principal; /* Default principal */ extern char * krb5_d_instance; extern char * krb5_d_realm; /* Default realm */ extern char * krb5_d_cc; /* Default credentials cache */ extern char * krb5_d_srv; /* Default service name */ extern int krb5_d_lifetime; /* Default lifetime */ extern int krb5_d_forwardable; extern int krb5_d_proxiable; extern int krb5_d_renewable; extern int krb5_autoget; extern int krb5_autodel; extern int krb5_d_getk4; extern int krb5_d_no_addresses; extern int krb5_checkaddrs; extern char * krb5_d_addrs[]; extern char * k5_keytab; /* Keytab file */ extern struct krb4_init_data krb4_init; extern char * krb4_d_principal; /* Default principal */ extern char * krb4_d_realm; /* Default realm */ extern char * krb4_d_srv; /* Default service name */ extern int krb4_d_lifetime; /* Default lifetime */ extern int krb4_d_preauth; extern char * krb4_d_instance; extern int krb4_autoget; extern int krb4_autodel; extern int krb4_checkaddrs; extern char * k4_keytab; /* Keytab file */ #endif /* CK_KERBEROS */ #ifndef NOSHOW int sho_iks() { #ifdef IKSDCONF #ifdef CK_LOGIN extern int ckxsyslog, ckxwtmp, ckxanon; #ifdef UNIX extern int ckxpriv; #endif /* UNIX */ #ifdef CK_PERMS extern int ckxperms; #endif /* CK_PERMS */ extern char * anonfile, * userfile, * anonroot; #ifdef OS2 extern char * anonacct; #endif /* OS2 */ #ifdef NT extern char * iks_domain; #endif /* NT */ #endif /* CK_LOGIN */ #ifdef CKWTMP extern char * wtmpfile; #endif /* CKWTMP */ #ifdef IKSDB extern char * dbfile; extern int dbenabled; #endif /* IKSDB */ #ifdef CK_LOGIN extern int logintimo; #endif /* CK_LOGIN */ extern int srvcdmsg, success, iksdcf, noinit, arg_x; extern char * cdmsgfile[], * cdmsgstr, *kermrc; char * bannerfile = NULL; char * helpfile = NULL; extern int xferlog; extern char * xferfile; int i; if (isguest) { printf("?Command disabled\r\n"); return(success = 0); } printf("IKS Settings\r\n"); #ifdef CK_LOGIN #ifdef OS2 printf(" Anonymous Account: %s\r\n",anonacct?anonacct:""); #endif /* OS2 */ printf(" Anonymous Initfile: %s\r\n",anonfile?anonfile:""); printf(" Anonymous Login: %d\r\n",ckxanon); printf(" Anonymous Root: %s\r\n",anonroot?anonroot:""); #endif /* CK_LOGIN */ printf(" Bannerfile: %s\r\n",bannerfile?bannerfile:""); printf(" CDfile: %s\r\n",cdmsgfile[0]?cdmsgfile[0]:""); for ( i=1;i<16 && cdmsgfile[i];i++ ) printf(" CDfile: %s\r\n",cdmsgfile[i]); printf(" CDMessage: %d\r\n",srvcdmsg); #ifdef IKSDB printf(" DBfile: %s\r\n",dbfile?dbfile:""); printf(" DBenabled: %d\r\n",dbenabled); #endif /* IKSDB */ #ifdef CK_LOGIN #ifdef NT printf(" Default-domain: %s\r\n",iks_domain?iks_domain:"."); #endif /* NT */ #endif /* CK_LOGIN */ printf(" Helpfile: %s\r\n",helpfile?helpfile:""); printf(" Initfile: %s\r\n",kermrc?kermrc:""); printf(" No-Initfile: %d\r\n",noinit); #ifdef CK_LOGIN #ifdef CK_PERM printf(" Permission code: %0d\r\n",ckxperms); #endif /* CK_PERM */ #ifdef UNIX printf(" Privileged Login: %d\r\n",ckxpriv); #endif /* UNIX */ #endif /* CK_LOGIN */ printf(" Server-only: %d\r\n",arg_x); printf(" Syslog: %d\r\n",ckxsyslog); printf(" Timeout (seconds): %d\r\n",logintimo); printf(" Userfile: %s\r\n",userfile?userfile:""); #ifdef CK_LOGIN #ifdef CKWTMP printf(" Wtmplog: %d\r\n",ckxwtmp); printf(" Wtmpfile: %s\r\n",wtmpfile?wtmpfile:""); #endif /* CKWTMP */ #endif /* CK_LOGIN */ printf(" Xferfile: %s\r\n",xferfile?xferfile:""); printf(" Xferlog: %d\r\n",xferlog); #else /* IKSDCONF */ printf("?Nothing to show.\r\n"); #endif /* IKSDCONF */ return(success = 1); } #ifdef CK_AUTHENTICATION int sho_auth(cx) int cx; { extern int auth_type_user[], cmd_rows; int i; char * p; int kv = 0, all = 0, n = 0; #ifdef IKSD if (inserver && isguest) { printf("?Sorry, command disabled.\r\n"); return(success = 0); } #endif /* IKSD */ if (cx) { kv = cx; } else if (auth_type_user[0] != AUTHTYPE_AUTO) { kv = auth_type_user[0]; } else { all = 1; kv = AUTHTYPE_KERBEROS_V4; } while (kv) { switch (kv) { case AUTHTYPE_KERBEROS_V4: kv = all ? AUTHTYPE_KERBEROS_V5 : 0; if (ck_krb4_is_installed()) { printf(" Authentication: Kerberos 4\n"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; } else { printf(" Authentication: Kerberos 4 (not installed)\n"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; continue; } #ifdef CK_KERBEROS printf(" Keytab file: %s\n", k4_keytab ? k4_keytab : "(none)"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; if (krb_action < 0) { p = "(none)"; } else { for (p = "", i = 0; i < krb_a_n; i++) { if (krb_action == krb_a_tbl[i].kwval) { p = krb_a_tbl[i].kwd; break; } } } printf(" Action: %s\n", p); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" Default lifetime %d\n",krb4_d_lifetime); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" Lifetime: %d (minutes)\n",krb4_init.lifetime); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" Default preauth: %d\n",krb4_d_preauth); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" Preauth: %d\n",krb4_init.preauth); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" Default principal: \"%s\"\n", krb4_d_principal ? krb4_d_principal : ""); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" Principal: \"%s\"\n", krb4_init.principal ? krb4_init.principal : ""); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" Default realm: \"%s\"\n", krb4_d_realm ? krb4_d_realm : ""); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" Realm: \"%s\"\n", krb4_init.realm ? krb4_init.realm : ""); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" Default instance: \"%s\"\n", krb4_d_instance ? krb4_d_instance : ""); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" Instance: \"%s\"\n", krb4_init.instance ? krb4_init.instance : ""); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" Auto-Get TGTs: %d\n",krb4_autoget); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" Auto-Destroy TGTs: %s\n", krb4_autodel==KRB_DEL_NO?"never": krb4_autodel==KRB_DEL_CL?"on-close":"on-exit"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" Check IP Addresses: %d\n",krb4_checkaddrs); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; #ifdef COMMENT printf(" Password: \"%s\"\n", krb4_init.password ? krb4_init.password : ""); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; #endif /* COMMENT */ #endif /* CK_KERBEROS */ printf("\n"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; break; case AUTHTYPE_KERBEROS_V5: kv = all ? AUTHTYPE_SSL : 0; if (ck_krb5_is_installed()) { if (ck_gssapi_is_installed()) printf(" Authentication: Kerberos 5 plus GSSAPI\n"); else printf(" Authentication: Kerberos 5\n"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; } else { printf(" Authentication: Kerberos 5 (not installed)\n"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; continue; } #ifdef CK_KERBEROS printf(" Cache file: %s\n", krb_op.cache ? krb_op.cache : "(none)"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" Default cache: %s\n", krb5_d_cc ? krb5_d_cc : "(none)"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" Keytab file: %s\n", k5_keytab ? k5_keytab : "(none)"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; if (krb_action < 0) { p = "(none)"; } else { for (p = "", i = 0; i < krb_a_n; i++) { if (krb_action == krb_a_tbl[i].kwval) { p = krb_a_tbl[i].kwd; break; } } } printf(" Action: %s\n", p); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" Default forwardable %d\n",krb5_d_forwardable); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" Forwardable: %d\n",krb5_init.forwardable); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" Default lifetime %d\n",krb5_d_lifetime); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" Lifetime: %d (minutes)\n",krb5_init.lifetime); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" Postdate: \"%s\"\n", krb5_init.postdate ? krb5_init.postdate: ""); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" Default proxiable: %d\n",krb5_d_proxiable); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" Proxiable: %d\n",krb5_init.proxiable); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" Renew: %d\n",krb5_init.renew); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" Default renewable: %d (minutes)\n",krb5_d_renewable); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" Renewable: %d (minutes)\n",krb5_init.renewable); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" Service: \"%s\"\n", krb5_init.service ? krb5_init.service : ""); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" Validate: %d\n",krb5_init.validate); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" Default principal: \"%s\"\n", krb5_d_principal ? krb5_d_principal : ""); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" Principal: \"%s\"\n", krb5_init.principal ? krb5_init.principal : ""); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" Default instance: \"%s\"\n", krb5_d_instance ? krb5_d_instance : ""); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" Default realm: \"%s\"\n", krb5_d_realm ? krb5_d_realm : ""); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" Realm: \"%s\"\n", krb5_init.realm ? krb5_init.realm : ""); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" Auto-Get TGTs: %d\n",krb5_autoget); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" Auto-Destroy TGTs: %s\n", krb5_autodel==KRB_DEL_NO?"never": krb5_autodel==KRB_DEL_CL?"on-close":"on-exit"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" Default get K4 TGTs: %d\n",krb5_d_getk4); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" Get K4 TGTs: %d\n",krb5_init.getk4); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" Check IP Addresses: %d\n",krb5_checkaddrs); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" No IP Addresses: %d\n",krb5_d_no_addresses); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" IP-Addresses: "); if (krb5_init.addrs && krb5_init.addrs[0]) { for (i = 0; krb5_init.addrs[i]; i++) { if (i) printf(","); printf("%s",krb5_init.addrs[i]); } } else if (krb5_d_addrs[0]) { for (i = 0;i < KRB5_NUM_OF_ADDRS && krb5_d_addrs[i];i++) { if (i) printf(","); printf("%s",krb5_d_addrs[i]); } } else { printf("(use default)"); } printf("\n"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; #ifdef COMMENT printf(" Password: \"%s\"\n", krb5_init.password ? krb5_init.password : ""); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; #endif /* COMMENT */ #endif /* CK_KERBEROS */ printf("\n"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; break; #ifdef CK_SSL case AUTHTYPE_SSL: kv = all ? AUTHTYPE_SRP : 0; if (ck_ssleay_is_installed()) { printf(" Authentication: SSL/TLS (%s)\n", SSLeay_version(SSLEAY_VERSION)); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; } else { printf(" Authentication: SSL/TLS (not installed)\n"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; continue; } printf(" RSA Certs file: %s\n",ssl_rsa_cert_file? ssl_rsa_cert_file:"(none)"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" RSA Certs Chain file: %s\n",ssl_rsa_cert_chain_file? ssl_rsa_cert_chain_file:"(none)"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" RSA Key file: %s\n",ssl_rsa_key_file? ssl_rsa_key_file:"(none)"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" DSA Certs file: %s\n",ssl_dsa_cert_file? ssl_dsa_cert_file:"(none)"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" DSA Certs Chain file: %s\n",ssl_dsa_cert_chain_file? ssl_dsa_cert_chain_file:"(none)"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" DH Key file: %s\n",ssl_dh_key_file? ssl_dh_key_file:"(none)"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" DH Param file: %s\n",ssl_dh_param_file? ssl_dh_param_file:"(none)"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" CRL file: %s\n",ssl_crl_file? ssl_crl_file:"(none)"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" CRL dir: %s\n",ssl_crl_dir? ssl_crl_dir:"(none)"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" Random file: %s\n",ssl_rnd_file? ssl_rnd_file:"(none)"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" Verify file: %s\n",ssl_verify_file? ssl_verify_file:"(none)"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" Verify dir: %s\n",ssl_verify_dir? ssl_verify_dir:"(none)"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" Cipher list: %s\n",ssl_cipher_list ? ssl_cipher_list : DEFAULT_CIPHER_LIST); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; if (ssl_con == NULL) { SSL_library_init(); ssl_ctx = (SSL_CTX *) SSL_CTX_new((SSL_METHOD *)TLSv1_method()); if (ssl_ctx != NULL) ssl_con= (SSL *) SSL_new(ssl_ctx); } if (ssl_con != NULL) { CHAR * p = NULL; int i; for (i = 0; ; i++) { p = (CHAR *) SSL_get_cipher_list(ssl_con,i); if (p == NULL) break; printf(" %s\n",p); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; } } printf(" Certs OK? %s\n",ssl_certsok_flag? "yes" : "no"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" Debug mode: %s\n", ssl_debug_flag ? "on" : "off"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" Verbose mode: %s\n", ssl_verbose_flag ? "on" : "off"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" Verify mode: %s\n", ssl_verify_flag == SSL_VERIFY_NONE ? "none" : ssl_verify_flag == SSL_VERIFY_PEER ? "peer-cert" : "fail-if-no-peer-cert"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" SSL only? %s\n", ssl_only_flag ? "yes" : "no"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" TLS only? %s\n", tls_only_flag ? "yes" : "no"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; #endif /* CK_SSL */ break; case AUTHTYPE_NTLM: kv = 0; if (ck_ntlm_is_installed()) { printf(" Authentication: NTLM\n"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" No options\n"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; } else { printf(" Authentication: NTLM (not installed)\n"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; continue; } printf("\n"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; break; case AUTHTYPE_SRP: kv = all ? AUTHTYPE_NTLM : 0; if (ck_srp_is_installed()) { if (ck_krypto_is_installed()) printf(" Authentication: SRP plus Krypto API\n"); else printf(" Authentication: SRP\n"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf(" No options\n"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; } else { printf(" Authentication: SRP (not installed)\n"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; continue; } printf("\n"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; break; } } return(success = 1); } #endif /* CK_AUTHENTICATION */ #endif /* NOSHOW */ #ifdef CK_KERBEROS /* C P _ A U T H -- AUTHENTICATE command parsing */ int cp_auth() { /* Command_Parse AUTHENTICATE */ int c, i, n; /* Workers */ int rc = 0; /* Return code */ int getval; /* Parsing helpers */ int tmpauth = 0; /* Temporary authentication type */ int kv = 0; /* Temporary Kerberos version */ int tmp_action = -1; /* Temporary Kerberos action */ int tmp_klc = 0; /* Temporary list-credentials */ char tmphlp[256]; /* For building help message */ char * p; char * tmppswd = NULL; /* Password */ char * tmpprinz = NULL; /* Principal */ char * tmprealm = NULL; /* Realm */ char * tmpcache = NULL; /* Cache file */ char * tmpinst = NULL; /* K4 Instance */ char * tmpaddrs[KRB5_NUM_OF_ADDRS]; #ifdef CK_RECALL extern int on_recall; /* around Password prompting */ #endif /* CK_RECALL */ struct stringint pv[KRB_I_MAX+1]; /* Temporary array for switch values */ struct FDB kw, sw, fl; /* FDBs for each parse function */ krb_action = -1; /* Initialize Kerberos action. */ tmp_action = -1; /* And our local copy. */ for (i = 0; i < KRB5_NUM_OF_ADDRS; i++) tmpaddrs[i] = NULL; if ((y = cmkey(kerbtab,kerbtabn,"authentication type","",xxstring)) < 0) { if (y == -3) printf("?Authentication type not specified - nothing happens\n"); return(y); } tmpauth = y; debug(F101,"kerberos authentication","",tmpauth); switch (tmpauth) { case AUTH_KRB4: kv = 4; break; /* Don't assume values are the same */ case AUTH_KRB5: kv = 5; break; default: printf("?Authentication type not supported: \"%s\"\n",atmbuf); return(-9); } /* From here down is Kerberos */ ini_kerb(); /* Reset Init data to defaults */ if (kv == 4) { /* Set K4 defaults */ if (krb4_d_realm) makestr(&tmprealm,krb4_d_realm); if (krb4_d_principal) makestr(&tmpprinz,krb4_d_principal); if (krb4_d_instance) makestr(&tmpinst,krb4_d_instance); } else if (kv == 5) { /* Set K5 defaults */ if (krb5_d_cc) makestr(&tmpcache,krb5_d_cc); if (krb5_d_realm) makestr(&tmprealm,krb5_d_realm); if (krb5_d_principal) makestr(&tmpprinz,krb5_d_principal); if (krb5_d_instance) makestr(&tmpinst,krb5_d_instance); } for (i = 0; i <= KRB_I_MAX; i++) { /* Initialize switch values */ pv[i].sval = NULL; /* to null pointers */ pv[i].ival = 0; /* and 0 int values */ pv[i].wval = (CK_OFF_T)-1; /* and -1 wide values */ } if (kv == 4) { /* Kerberos 4 */ pv[KRB_I_LF].ival = krb4_d_lifetime; pv[KRB_I_PA].ival = krb4_d_preauth; if ((n = cmkey(krb_a_tbl,krb_a_n,"Kerberos 4 action","",xxstring)) < 0) { if (n == -3) printf("?Action not specified - nothing happens.\n"); return(n); } } else if (kv == 5) { /* Kerberos 5 */ pv[KRB_I_FW].ival = krb5_d_forwardable; pv[KRB_I_PR].ival = krb5_d_proxiable; pv[KRB_I_LF].ival = krb5_d_lifetime; pv[KRB_I_RB].ival = krb5_d_renewable; pv[KRB_I_K4].ival = krb5_d_getk4; pv[KRB_I_NAD].ival = krb5_d_no_addresses; /* Make help message that shows switches and action keywords */ ckstrncpy(tmphlp,"Kerberos 5 action, one of the following:\n ",256); for (i = 0; i < krb_a_n; i++) { ckstrncat(tmphlp,krb_a_tbl[i].kwd,sizeof(tmphlp)); if (i == krb_a_n - 1) ckstrncat(tmphlp,"\nor switch",sizeof(tmphlp)); else ckstrncat(tmphlp," ",sizeof(tmphlp)); } /* Set up first set of chained FDB's */ cmfdbi(&sw, /* First FDB - command switches */ _CMKEY, /* fcode */ tmphlp, /* hlpmsg */ "", /* default (none) */ "", /* addtl string data */ krb_s_n, /* Switch table size */ 4, /* addtl numeric data 2: 4 = cmswi */ xxstring, /* Processing function */ krb_s_tbl, /* Switch table */ &kw /* Pointer to next FDB */ ); cmfdbi(&kw, /* Second FDB - action keywords */ _CMKEY, /* fcode */ "Kerberos action", /* hlpmsg */ "", /* default (none) */ "", /* addtl string data */ krb_a_n, /* Switch table size */ 0, /* addtl num data (0 = NOT switch) */ xxstring, /* Processing function */ krb_a_tbl, /* Keyword table */ NULL /* Pointer to next FDB (none) */ ); /* Parse */ while (1) { /* Parse 0 or more switches */ rc = cmfdb(&sw); /* Parse something */ debug(F101,"kerberos cmfdb 1 rc","",rc); if (rc < 0) { /* Error */ if (rc == -3) printf("?Action not specified - nothing happens.\n"); return(rc); /* or reparse needed */ } if (cmresult.fdbaddr != &sw) /* Break out if not a switch */ break; c = cmgbrk(); /* Have switch - get break character */ getval = (c == ':' || c == '='); /* Must parse an agument? */ if (getval && !(cmresult.kflags & CM_ARG)) { printf("?This switch does not take arguments\n"); return(-9); /* OK because nothing malloc'd yet */ } if (!getval && (cmgkwflgs() & CM_ARG)) { printf("?This switch requires an argument\n"); return(-9); } n = cmresult.nresult; /* Numeric result = switch value */ debug(F101,"kerberos command switch","",n); switch (n) { /* Handle the switch */ case KRB_S_CA: /* /CACHE: */ p = krb5_d_cc ? krb5_d_cc : ""; if ((y = cmofi("Name of cache file",p,&s,xxstring)) < 0) { if (y == -3) s = NULL; else return(y); } makestr(&tmpcache,s); break; default: printf("?Unexpected switch value - internal error\n"); return(-9); /* (if) nothing malloc'd yet. */ } } if (cmresult.fdbaddr != &kw) { /* Checking... */ printf("?Unexpected result - internal error\n"); return(-9); /* Nothing malloc'd yet. */ } n = cmresult.nresult; /* Get keyword value */ } else { printf("?Unexpected Kerberos version - Internal error\n"); return(-9); } debug(F101,"kerberos action","",n); switch (n) { case KRB_A_IN: /* INITIALIZE */ case KRB_A_DE: /* DESTROY */ case KRB_A_LC: /* LIST-CREDENTIALS */ tmp_action = n; /* OK, set */ break; default: /* Not OK, punt. */ printf("?Unexpected action - internal error\n"); return(-9); } if (tmp_action == KRB_A_IN) { /* Action is INITIALIZE */ int x; cmfdbi(&sw, /* INITIALIZE switches */ _CMKEY, /* fcode */ "Principal,\n or optional INITIALIZE switch(es)", /* hlpmsg */ "", /* default (none) */ "", /* addtl string data */ kv == 4 ? krb4_i_n : krb5_i_n, /* Switch table size */ 4, /* addtl numeric data 2: 4 = cmswi */ xxstring, /* Processing function */ kv == 4 ? krb4_i_tbl : krb5_i_tbl, /* Switch table */ &fl /* Pointer to next FDB */ ); cmfdbi(&fl, /* 3rd FDB - command to send from */ _CMFLD, /* fcode */ "Principal", /* hlpmsg */ kv == 4 ? krb4_d_principal : krb5_d_principal, /* principal */ "", /* addtl string data */ 0, /* addtl numeric data 1 */ 0, /* addtl numeric data 2 */ xxstring, NULL, NULL ); while (1) { /* Parse INIT switches or principal */ rc = cmfdb(&sw); debug(F101,"kerberos cmfdb 2 rc","",rc); if (rc < 0) { if (rc == -3) printf("?Principal name required\n"); goto kerbx; } debug(F101,"kerberos cmfdb 2 fcode","",cmresult.fcode); if (cmresult.fcode != _CMKEY) /* Not a switch, quit switch loop */ break; c = cmgbrk(); /* Switch - get break character */ debug(F101,"kerberos cmfdb 2 cmgbrk","",c); getval = (c == ':' || c == '='); if (getval && !(cmresult.kflags & CM_ARG)) { printf("?This switch does not take arguments\n"); return(-9); /* OK because nothing malloc'd yet */ } if (!getval && (cmgkwflgs() & CM_ARG)) { printf("?This switch requires an argument\n"); return(-9); } n = cmresult.nresult; /* Numeric result = switch value */ switch (n) { /* These don't take args... */ case KRB_I_PA: /* /PREAUTH */ case KRB_I_FW: /* /FORWARDABLE */ case KRB_I_PR: /* /PROXIABLE */ case KRB_I_RN: /* /RENEW */ case KRB_I_VA: /* /VALIDATE */ case KRB_I_NPA: /* /NOT-PREAUTH */ case KRB_I_NFW: /* /NOT-FORWARDABLE */ case KRB_I_NPR: /* /NOT-PROXIABLE */ case KRB_I_VB: /* /VERBOSE */ case KRB_I_BR: /* /BRIEF */ case KRB_I_K4: /* /KERBEROS4 */ case KRB_I_NK4: /* /NO-KERBEROS4 */ case KRB_I_POP: /* /POPUP */ case KRB_I_NAD: /* /NO-ADDRESSES */ if (getval) { printf("?This switch does not take a value\n"); rc = -9; goto kerbx; } switch (n) { case KRB_I_NPA: pv[KRB_I_PA].ival = 0; break; case KRB_I_NFW: pv[KRB_I_FW].ival = 0; break; case KRB_I_NPR: pv[KRB_I_PR].ival = 0; break; case KRB_I_VB: pv[KRB_I_BR].ival = 0; break; case KRB_I_NK4: pv[KRB_I_K4].ival = 0; break; default: pv[n].ival = 1; } break; /* These do take arguments */ case KRB_I_RB: /* /RENEWABLE: */ pv[n].ival = 0; if (!getval) break; if ((rc = cmnum("Minutes",ckitoa(krb5_init.renewable), 10,&y, xxstring)) < 0) goto kerbx; pv[n].ival = y; break; case KRB_I_LF: /* /LIFETIME: */ pv[n].ival = 0; /* Default is previous value */ sprintf(tmpbuf,"%d", /* SAFE */ kv == 4 ? krb4_init.lifetime : krb5_init.lifetime ); if (!getval) break; if ((rc = cmnum("Minutes",tmpbuf,10,&y, xxstring)) < 0) goto kerbx; pv[n].ival = y; break; case KRB_I_PD: /* /POSTDATE: */ if (pv[n].sval) { free(pv[n].sval); pv[n].sval = NULL; } if (!getval) break; if ((rc = cmdate("date-time","",&s,0,xxstring)) < 0) goto kerbx; makestr(&(pv[n].sval),s); break; case KRB_I_SR: /* /SERVICE: */ if (pv[n].sval) { free(pv[n].sval); pv[n].sval = NULL; } if (!getval) break; if ((rc = cmfld("Service-name","",&s,xxstring)) < 0) goto kerbx; makestr(&(pv[n].sval),s); break; case KRB_I_RL: /* /REALM: */ if (pv[n].sval) { free(pv[n].sval); pv[n].sval = NULL; } if (!getval) break; if (kv == 4) p = krb4_d_realm ? krb4_d_realm : ""; else p = krb5_d_realm ? krb5_d_realm : ""; if ((rc = cmfld("Realm",p,&s,xxstring)) < 0) goto kerbx; makestr(&(pv[n].sval),s); break; case KRB_I_IN: /* /INSTANCE: */ if (pv[n].sval) { free(pv[n].sval); pv[n].sval = NULL; } if (!getval) break; if (kv == 4) p = krb4_d_instance ? krb4_d_instance : ""; else p = krb5_d_instance ? krb5_d_instance : ""; if ((rc = cmfld("Instance",p,&s,xxstring)) < 0) goto kerbx; makestr(&(pv[n].sval),s); break; case KRB_I_PW: /* /PASSWORD: */ debok = 0; if (pv[n].sval) { free(pv[n].sval); pv[n].sval = NULL; } if (!getval) break; if ((rc = cmfld("Password","",&s,xxstring)) < 0) if (rc != -3) goto kerbx; makestr(&(pv[n].sval),s); break; case KRB_I_ADR: /* /ADDRESSES:{} */ if (pv[n].sval) { free(pv[n].sval); pv[n].sval = NULL; } if (!getval) break; if ((rc = cmfld("List of IP addresses","",&s,xxstring)) < 0) goto kerbx; makelist(s,tmpaddrs,KRB5_NUM_OF_ADDRS); for (i = 0; i < KRB5_NUM_OF_ADDRS && tmpaddrs[i]; i++) { if (inet_addr(tmpaddrs[i]) == 0xffffffff) { printf("invalid ip address: %s\n",tmpaddrs[i]); rc = -9; goto kerbx; } } pv[KRB_I_NAD].ival = 0; break; default: printf("?Unexpected switch value - internal error\n"); rc = -9; goto kerbx; } } if (cmresult.fcode != _CMFLD) { printf("?Unexected result - internal error\n"); rc = -9; goto kerbx; } /* cmresult.sresult may be of the form PRINCIPAL@REALM */ i = ckindex("@",cmresult.sresult,0,0,0); if (i != 0) { makestr(&tmprealm,&cmresult.sresult[i]); cmresult.sresult[i-1] = '\0'; } makestr(&tmpprinz,cmresult.sresult); /* Principal (user) */ if ((rc = cmcfm()) < 0) { /* Now get confirmation */ if (rc == -3) { printf("?Principal name required\n"); } goto kerbx; } if (!tmpprinz || !tmpprinz[0]) { printf("?Principal name required\n"); goto kerbx; } if (!pv[KRB_I_RN].ival && !pv[KRB_I_VA].ival) { /* Don't use a password if Validating or Renewing */ if (pv[KRB_I_PW].sval) { /* If they gave a /PASSWORD switch */ makestr(&tmppswd,pv[KRB_I_PW].sval); /* use this value */ } #ifdef COMMENT /* Password prompting has been moved to ck_krb[45]_initTGT() */ else { /* Otherwise must prompt for it */ char prmpt[80]; if (pv[KRB_I_RL].sval) sprintf(prmpt,"%s@%s's Password: ", tmpprinz,pv[KRB_I_RL].sval); else if (tmprealm) sprintf(prmpt,"%s@%s's Password: ", tmpprinz,tmprealm); else sprintf(prmpt,"%s's Password: ",tmpprinz); #ifdef OS2 if (pv[KRB_I_POP].ival) { char passwd[80]=""; readpass(prmpt,passwd,80); makestr(&tmppswd,passwd); memset(passwd,0,80); } else #endif /* OS2 */ { #ifdef CK_RECALL on_recall = 0; #endif /* CK_RECALL */ cmsavp(psave,PROMPTL); /* Save old prompt */ cmsetp(prmpt); /* Make new prompt */ concb((char)escape); /* Put console in cbreak mode */ cmini(0); /* and no-echo mode */ /* Issue prompt if at top level */ if (pflag) prompt(xxstring); cmres(); /* Reset the parser */ for (rc = -1; rc < 0; ) { /* Prompt till they answer */ /* Get a literal line of text */ rc = cmtxt("","",&s,NULL); cmres(); /* Reset the parser again */ } makestr(&tmppswd,s); printf("\n"); /* Echo a CRLF */ cmsetp(psave); /* Restore original prompt */ } } x = 0; /* Check for password */ if (tmppswd) if (*tmppswd) x = 1; if (!x) { printf("?Password required\n"); goto kerbx; } #endif /* COMMENT */ } } else if (kv == 5 && tmp_action == KRB_A_LC) { /* LIST-CREDENTIALS */ tmp_klc = 0; while (1) { if ((x = cmkey(klctab,nklctab,"Switch","",xxstring)) < 0) { if (x == -3) { if ((rc = cmcfm()) < 0) goto kerbx; else break; } else { rc = x; goto kerbx; } } tmp_klc |= x; } } else if ((rc = cmcfm()) < 0) /* DESTROY, just confirm */ goto kerbx; /* Done - Move confirmed data to final locations */ krb_action = tmp_action; /* Action requested */ krb_op.version = kv; /* Kerberos version */ krb_op.cache = tmpcache; /* Cache file */ tmpcache = NULL; /* So we don't free it */ switch (krb_action) { case KRB_A_IN: /* INITIALIZE */ if (kv == 5) { krb5_init.forwardable = pv[KRB_I_FW].ival; krb5_init.proxiable = pv[KRB_I_PR].ival; krb5_init.lifetime = pv[KRB_I_LF].ival; krb5_init.renew = pv[KRB_I_RN].ival; krb5_init.renewable = pv[KRB_I_RB].ival; krb5_init.validate = pv[KRB_I_VA].ival; /* Here we just reassign the pointers and then set them to NULL */ /* so they won't be freed below. */ krb5_init.postdate = pv[KRB_I_PD].sval; pv[KRB_I_PD].sval = NULL; krb5_init.service = pv[KRB_I_SR].sval; pv[KRB_I_SR].sval = NULL; if (pv[KRB_I_RL].sval) { krb5_init.realm = pv[KRB_I_RL].sval; pv[KRB_I_RL].sval = NULL; } else if (tmprealm) { krb5_init.realm = tmprealm; tmprealm = NULL; } if (pv[KRB_I_IN].sval) { krb5_init.instance = pv[KRB_I_IN].sval; pv[KRB_I_IN].sval = NULL; } else if ( tmpinst ) { krb5_init.instance = tmpinst; tmpinst = NULL; } if (tmpprinz) { krb5_init.principal = tmpprinz; tmpprinz = NULL; } krb5_init.password = tmppswd; tmppswd = NULL; krb5_init.getk4 = pv[KRB_I_K4].ival; if (krb5_init.getk4) { krb4_init.lifetime = pv[KRB_I_LF].ival; if (krb5_init.realm) makestr(&krb4_init.realm,krb5_init.realm); krb4_init.preauth = krb4_d_preauth; krb4_init.verbose = pv[KRB_I_BR].ival ? 0 : 1; if (krb5_init.principal) makestr(&krb4_init.principal,krb5_init.principal); if (krb5_init.principal) makestr(&krb4_init.password,krb5_init.password); } krb5_init.no_addresses = pv[KRB_I_NAD].ival; if (tmpaddrs[0]) { for (i = 0; i < KRB5_NUM_OF_ADDRS; i++) { if (krb5_init.addrs[i]) { free(krb5_init.addrs[i]); krb5_init.addrs[i] = NULL; } krb5_init.addrs[i] = tmpaddrs[i]; tmpaddrs[i] = NULL; } } } else if (kv == 4) { /* Same deal for Kerberos 4 */ krb4_init.lifetime = pv[KRB_I_LF].ival; if (pv[KRB_I_RL].sval) { krb4_init.realm = pv[KRB_I_RL].sval; pv[KRB_I_RL].sval = NULL; } else if ( tmprealm ) { krb4_init.realm = tmprealm; tmprealm = NULL; } if (pv[KRB_I_IN].sval) { krb4_init.instance = pv[KRB_I_IN].sval; pv[KRB_I_IN].sval = NULL; } else if ( tmpinst ) { krb4_init.instance = tmpinst; tmpinst = NULL; } krb4_init.preauth = pv[KRB_I_PA].ival; krb4_init.verbose = pv[KRB_I_BR].ival ? 0 : 1; if (tmpprinz) { krb4_init.principal = tmpprinz; tmpprinz = NULL; } krb4_init.password = tmppswd; tmppswd = NULL; } break; case KRB_A_LC: /* List Credentials */ krb5_lc.encryption = tmp_klc & XYKLCEN; krb5_lc.flags = tmp_klc & XYKLCFL; krb5_lc.addr = tmp_klc & XYKLCAD; break; } /* Common exit - Free temporary storage */ kerbx: for (i = 0; i <= KRB_I_MAX; i++) { /* Free malloc'd switch data */ if (pv[i].sval) free(pv[i].sval); } for (i = 0; i < KRB5_NUM_OF_ADDRS; i++) { if (tmpaddrs[i]) free(tmpaddrs[i]); } if (tmpprinz) free(tmpprinz); /* And these too. */ if (tmppswd) free(tmppswd); if (tmpcache) free(tmpcache); if (tmprealm) free(tmprealm); if (tmpinst) free(tmpinst); return(rc); /* Return the return code */ } #endif /* CK_KERBEROS */ #ifdef CK_LOGIN int #ifdef CK_ANSIC ckxlogin(CHAR * userid, CHAR * passwd, CHAR * acct, int promptok) #else /* CK_ANSIC */ ckxlogin(userid, passwd, acct, promptok) CHAR * userid; CHAR * passwd; CHAR * acct; int promptok; #endif /* CK_ANSIC */ /* ckxlogin */ { #ifdef CK_RECALL extern int on_recall; /* around Password prompting */ #endif /* CK_RECALL */ #ifdef COMMENT extern int guest; #endif /* COMMENT */ int rprompt = 0; /* Restore prompt */ #ifdef CKSYSLOG int savlog; #endif /* CKSYSLOG */ extern int what, srvcdmsg; int x = 0, ok = 0, rc = 0; CHAR * _u = NULL, * _p = NULL, * _a = NULL; debug(F111,"ckxlogin userid",userid,promptok); debug(F110,"ckxlogin passwd",passwd,0); isguest = 0; /* Global "anonymous" flag */ if (!userid) userid = (CHAR *)""; if (!passwd) passwd = (CHAR *)""; debug(F111,"ckxlogin userid",userid,what); #ifdef CK_RECALL on_recall = 0; #endif /* CK_RECALL */ #ifdef CKSYSLOG savlog = ckxsyslog; /* Save and turn off syslogging */ #endif /* CKSYSLOG */ if ((!*userid || !*passwd) && /* Need to prompt for missing info */ promptok) { cmsavp(psave,PROMPTL); /* Save old prompt */ debug(F110,"ckxlogin saved",psave,0); rprompt = 1; } if (!*userid) { if (!promptok) return(0); cmsetp("Username: "); /* Make new prompt */ concb((char)escape); /* Put console in cbreak mode */ cmini(1); /* Flush typeahead */ #ifdef IKS_OPTION debug(F101, "ckxlogin TELOPT_SB(TELOPT_KERMIT).kermit.me_start", "", TELOPT_SB(TELOPT_KERMIT).kermit.me_start ); #endif /* IKS_OPTION */ while (ttchk() > 0) { x = ttinc(0); debug(F101,"ckxlogin flush user x","",x); if (x < 0) doexit(GOOD_EXIT,0); /* Connection lost */ #ifdef TNCODE if (sstelnet) { if (x == IAC) { x = tn_doop((CHAR)(x & 0xff),ckxech,ttinc); debug(F101,"ckxlogin user tn_doop","",x); #ifdef IKS_OPTION debug(F101, "ckxlogin user TELOPT_SB(TELOPT_KERMIT).kermit.me_start", "", TELOPT_SB(TELOPT_KERMIT).kermit.me_start ); #endif /* IKS_OPTION */ if (x < 0) goto XCKXLOG; switch (x) { case 1: ckxech = 1; break; /* Turn on echoing */ case 2: ckxech = 0; break; /* Turn off echoing */ #ifdef IKS_OPTION case 4: /* IKS event */ if (!TELOPT_SB(TELOPT_KERMIT).kermit.me_start) break; /* else fall thru... */ #endif /* IKS_OPTION */ case 6: /* Logout */ goto XCKXLOG; } } } #endif /* TNCODE */ } if (pflag) prompt(xxstring); /* Issue prompt if at top level */ cmres(); /* Reset the parser */ for (x = -1; x < 0;) { /* Prompt till they answer */ /* Get a literal line of text */ x=cmtxt("Your username, or \"ftp\", or \"anonymous\"","",&s,NULL); if (x == -4 || x == -10) { printf("\r\n%sLogin cancelled\n", x == -10 ? "Timed out: " : ""); #ifdef CKSYSLOG ckxsyslog = savlog; #endif /* CKSYSLOG */ doexit(GOOD_EXIT,0); } if (sstate) /* Did a packet come instead? */ goto XCKXLOG; cmres(); /* Reset the parser again */ } if ((_u = (CHAR *)malloc((int)strlen(s) + 1)) == NULL) { printf("?Internal error: malloc\n"); goto XCKXLOG; } else { strcpy((char *)_u,s); /* safe */ userid = _u; } } ok = zvuser((char *)userid); /* Verify username */ debug(F111,"ckxlogin zvuser",userid,ok); if (!*passwd && promptok #ifdef COMMENT && guest #endif /* COMMENT */ ) { char prmpt[80]; #ifdef CKSYSLOG savlog = ckxsyslog; /* Save and turn off syslogging */ ckxsyslog = 0; #endif /* CKSYSLOG */ /* Flush typeahead again */ while (ttchk() > 0) { x = ttinc(0); debug(F101,"ckxlogin flush user x","",x); #ifdef TNCODE if (sstelnet) { if (x == IAC) { x = tn_doop((CHAR)(x & 0xff),ckxech,ttinc); debug(F101,"ckxlogin pass tn_doop","",x); #ifdef IKS_OPTION debug(F101, "ckxlogin pass TELOPT_SB(TELOPT_KERMIT).kermit.me_start", "", TELOPT_SB(TELOPT_KERMIT).kermit.me_start ); #endif /* IKS_OPTION */ if (x < 0) goto XCKXLOG; switch (x) { case 1: ckxech = 1; break; /* Turn on echoing */ case 2: ckxech = 0; break; /* Turn off echoing */ case 4: /* IKS event */ if (!TELOPT_SB(TELOPT_KERMIT).kermit.me_start) break; /* else fall thru... */ case 6: /* Logout */ goto XCKXLOG; } } } #endif /* TNCODE */ } if (!strcmp((char *)userid,"anonymous") || !strcmp((char *)userid,"ftp")) { if (!ok) goto XCKXLOG; ckstrncpy(prmpt,"Enter e-mail address as Password: ",80); } else if (*userid && strlen((char *)userid) < 60) { #ifdef NT extern CHAR * pReferenceDomainName; if (pReferenceDomainName) ckmakxmsg(prmpt, 80, "Enter ", pReferenceDomainName, "\\\\", userid, "'s Password: ", NULL,NULL,NULL,NULL,NULL,NULL,NULL ); else #endif /* NT */ ckmakmsg(prmpt,80,"Enter ",(char *)userid,"'s Password: ",NULL); } else ckstrncpy(prmpt,"Enter Password: ",80); cmsetp(prmpt); /* Make new prompt */ concb((char)escape); /* Put console in cbreak mode */ if (strcmp((char *)userid,"anonymous") && strcmp((char *)userid,"ftp")) { /* and if not anonymous */ debok = 0; cmini(0); /* and no-echo mode */ } else { cmini(1); } if (pflag) prompt(xxstring); /* Issue prompt if at top level */ cmres(); /* Reset the parser */ for (x = -1; x < 0;) { /* Prompt till they answer */ #ifdef CK_PAM gotemptypasswd=0; #endif /* CK_PAM */ x = cmtxt("","",&s,NULL); /* Get a literal line of text */ if (x == -4 || x == -10) { printf("\r\n%sLogin cancelled\n", x == -10 ? "Timed out: " : ""); #ifdef CKSYSLOG ckxsyslog = savlog; #endif /* CKSYSLOG */ doexit(GOOD_EXIT,0); } #ifdef CK_PAM if (!*s) gotemptypasswd = 1; #endif /* CK_PAM */ if (sstate) /* In case of a Kermit packet */ goto XCKXLOG; cmres(); /* Reset the parser again */ } printf("\r\n"); /* Echo a CRLF */ if ((_p = (CHAR *)malloc((int)strlen(s) + 1)) == NULL) { printf("?Internal error: malloc\n"); goto XCKXLOG; } else { strcpy((char *)_p,s); /* safe */ passwd = _p; } } #ifdef CK_PAM else { cmres(); /* Reset the parser */ /* We restore the prompt now because the PAM engine will call */ /* readpass() which will overwrite psave. */ if (rprompt) { cmsetp(psave); /* Restore original prompt */ debug(F110,"ckxlogin restored",psave,0); rprompt = 0; } } #endif /* CK_PAM */ #ifdef CKSYSLOG ckxsyslog = savlog; #endif /* CKSYSLOG */ if (ok) { ok = zvpass((char *)passwd); /* Check password */ debug(F101,"ckxlogin zvpass","",ok); #ifdef CK_PAM } else { /* Fake pam password failure for nonexistent users */ sleep(1); printf("Authentication failure\n"); #endif /* CK_PAM */ } if (ok > 0 && isguest) { #ifndef NOPUSH nopush = 1; #endif /* NOPUSH */ srvcdmsg = 1; } rc = ok; /* Set the return code */ if ((char *)uidbuf != (char *)userid) ckstrncpy(uidbuf,(char *)userid,UIDBUFLEN); /* Remember username */ XCKXLOG: /* Common exit */ #ifdef CKSYSLOG ckxsyslog = savlog; /* In case of GOTO above */ #endif /* CKSYSLOG */ if (rprompt) { cmsetp(psave); /* Restore original prompt */ debug(F110,"ckxlogin restored",psave,0); } if (_u || _p || _a) { if (_u) free(_u); if (_p) free(_p); if (_a) free(_a); } return(rc); } int ckxlogout() { doexit(GOOD_EXIT,0); /* doexit calls zvlogout */ return(0); /* not reached */ } #endif /* CK_LOGIN */ #endif /* NOICP */ ckuusr.c0000644000015300001460000127135111574211145011354 0ustar fdckermit#ifdef SSHTEST #define SSHBUILTIN #endif /* SSHTEST */ #include "ckcsym.h" char *userv = "User Interface 9.0.299, 9 Jun 2011"; /* C K U U S R -- "User Interface" for C-Kermit (Part 1) */ /* Authors: Frank da Cruz , The Kermit Project, Columbia University, New York City Jeffrey E Altman Secure Endpoints Inc., New York City Copyright (C) 1985, 2011, Trustees of Columbia University in the City of New York. All rights reserved. See the C-Kermit COPYING.TXT file or the copyright text in the ckcmai.c module for disclaimer and permissions. */ /* Originally the entire user interface was in one module, ckuusr.c. Over the years it has been split into many modules: ckuus2.c, ckuus3.c, ..., ckuus7.c. ckuus2.c contains the HELP command parser and help-text strings; ckuusy.c contains the UNIX-style command-line interface; ckuusx.c contains routines needed by both the command-line interface and the interactive command parser. */ /* The ckuus*.c modules depend on the existence of C library features like fopen, fgets, feof, (f)printf, argv/argc, etc. Other functions that are likely to vary among different platforms -- like setting terminal modes or interrupts -- are invoked via calls to functions that are defined in the system- dependent modules, ck?[ft]io.c. The command line parser processes any arguments found on the command line, as passed to main() via argv/argc. The interactive parser uses the facilities of the cmd package (developed for this program, but usable by any program). Any command parser may be substituted for this one. The only requirements for the Kermit command parser are these: . Set parameters via global variables like duplex, speed, ttname, etc. See ckcmai.c for the declarations and descriptions of these variables. . If a command can be executed without the use of Kermit protocol, then execute the command directly and set the variable sstate to 0. Examples include 'set' commands, local directory listings, the 'connect' command. . If a command requires the Kermit protocol, set the following variables: sstate string data 'x' (enter server mode) (none) 'r' (send a 'get' command) cmarg, cmarg2 'v' (enter receive mode) cmarg2 'g' (send a generic command) cmarg 's' (send files) nfils, cmarg & cmarg2 OR cmlist 'c' (send a remote host command) cmarg cmlist is an array of pointers to strings. cmarg, cmarg2 are pointers to strings. nfils is an integer. cmarg can be a filename string (possibly wild), or a pointer to a prefabricated generic command string, or a pointer to a host command string. cmarg2 is an "as-name" - the name to send file(s) under, or the name under which to store incoming file(s); must not be wild. A null or empty value means to use the file's own name. cmlist is a list of filenames, such as passed via argv. nfils is an integer, interpreted as follows: -1: filespec (possibly wild) in cmarg, must be expanded internally. 0: send from stdin (standard input). >0: number of files to send, from cmlist. The screen() function is used to update the screen during file transfer. The tlog() function writes to a transaction log. The debug() function writes to a debugging log. The intmsg() and chkint() functions provide the user i/o for interrupting file transfers. */ /* Includes */ #ifdef MULTINET #define MULTINET_OLD_STYLE /* Leave select prototype undefined */ #endif /* MULTINET */ #include "ckcdeb.h" #include "ckcasc.h" #include "ckcker.h" #include "ckcnet.h" /* Network symbols */ #include "ckuusr.h" #include "ckcxla.h" int g_fncact = -1; /* Needed for NOICP builds */ int noinit = 0; /* Flag for skipping init file */ int nscanfile = SCANFILEBUF; int rcdactive = 0; /* RCD active */ int keepallchars = 0; /* See cmfld() */ int locus = 1; /* Current LOCUS is LOCAL */ #ifdef OS2 int autolocus = 2; /* Automatic LOCUS switching: ASK */ #else /* OS2 */ int autolocus = 1; /* Automatic LOCUS switching enabled */ #endif /* OS2 */ #ifndef NOICP #ifdef CKLEARN #ifdef VMS #include /* For CKLEARN */ #endif /* VMS */ #endif /* CKLEARN */ #ifdef OS2 #ifndef NT #define INCL_NOPM #define INCL_VIO /* Needed for ckocon.h */ #include #undef COMMENT #else #define APIRET ULONG #include #include #include "cknwin.h" #include "ckntap.h" /* CK_TAPI definition */ #endif /* NT */ #include "ckowin.h" #include "ckocon.h" extern int tcp_avail; extern bool viewonly; extern int k95stdout; extern int tt_scroll; #ifndef NOTERM extern tt_status[VNUM]; #endif /* NOTERM */ int display_demo = 1; #include "ckossh.h" #ifdef KUI #include "ikui.h" #endif /* KUI */ #endif /* OS2 */ int optlines = 0; int didsetlin = 0; #ifdef NEWFTP extern int ftpget, ftpisopen(), doftpres(); _PROTOTYP(int doftptyp,(int)); _PROTOTYP(VOID doftpglobaltype,(int)); #endif /* NEWFTP */ #ifdef VMS extern int batch; #endif /* VMS */ #ifdef datageneral #include #define fgets(stringbuf,max,fd) dg_fgets(stringbuf,max,fd) #endif /* datageneral */ extern int xcmdsrc, hints, cmflgs, whyclosed; char * hlptok = NULL; #ifdef CK_TTGWSIZ /* Whether to use more-prompting */ int xaskmore = 1; /* Momentary setting */ int saveask = 1; /* Permanent setting */ #else int xaskmore = 0; int saveask = 0; #endif /* CK_TTGWSIZ */ #ifndef NOCSETS extern int nfilc; extern struct keytab fcstab[]; extern int fcharset; #endif /* NOCSETS */ char * g_pswd = NULL; int g_pcpt = -1; int g_pflg = -1; extern int cmd_rows, cmd_cols; #ifdef CKROOT extern int ckrooterr; #endif /* CKROOT */ extern int inserver, filepeek; #ifdef CKLEARN FILE * learnfp = NULL; char * learnfile = NULL; int learning = 0; #endif /* CKLEARN */ #ifndef NOXFER extern int atcapr, atdiso, nfils, moving, protocol, sendmode, epktflg, size, sndsrc, server, displa, fncnv, fnspath, fnrpath, xfermode, urpsiz, spsizf, spsiz, spsizr, spmax, wslotr, prefixing, fncact, reliable, setreliable; #ifdef IKSDCONF extern int iksdcf; #endif /* IKSDCONF */ #ifdef CK_LOGIN extern int isguest; #endif /* CK_LOGIN */ extern CK_OFF_T sendstart; extern char *cmarg, *cmarg2, **cmlist, *dftty; extern struct keytab fntab[]; extern int nfntab; extern struct ck_p ptab[NPROTOS]; int sndcmd = 0; /* Last command was a SEND-class command. */ int g_xfermode = -1; int g_proto = -1; int g_urpsiz = -1; int g_spsizf = -1; int g_spsiz = -1; int g_spsizr = -1; int g_spmax = -1; int g_wslotr = -1; int g_prefixing = -1; int g_fncnv = -1; int g_fnspath = -1; int g_fnrpath = -1; int g_fnact = -1; int g_displa = -1; int g_spath = -1; int g_rpath = -1; char * g_sfilter = NULL; char * g_rfilter = NULL; extern int patterns; #ifdef PATTERNS extern char *txtpatterns[], *binpatterns[]; int g_patterns = -1; #endif /* PATTERNS */ int g_skipbup = -1; #ifdef PIPESEND extern int usepipes, pipesend; extern char * sndfilter; #endif /* PIPESEND */ #ifndef NOSPL extern int sndxlo, sndxhi, sndxin; #endif /* NOSPL */ extern char fspec[]; /* Most recent filespec */ extern int fspeclen; /* Length of fspec[] buffer */ #ifndef NOFRILLS extern int rmailf; /* MAIL command items */ extern char optbuf[]; #endif /* NOFRILLS */ extern int en_cpy, en_cwd, en_del, en_dir, en_fin, en_get, en_bye, en_mai, en_pri, en_hos, en_ren, en_sen, en_spa, en_set, en_typ, en_who, en_ret, en_xit, en_mkd, en_rmd, en_asg; #ifndef NOMSEND /* Multiple SEND */ extern char *msfiles[]; int filesinlist = 0; /* And ADD ... */ extern struct filelist * filehead; extern struct filelist * filetail; extern struct filelist * filenext; extern int addlist; #endif /* NOMSEND */ static struct keytab addtab[] = { #ifdef PATTERNS { "binary-patterns", ADD_BIN, 0 }, #endif /* PATTERNS */ #ifndef NOMSEND { "send-list", ADD_SND, 0 }, #endif /* NOMSEND */ #ifdef PATTERNS { "text-patterns", ADD_TXT, 0 }, #endif /* PATTERNS */ { "", 0, 0 } }; static int naddtab = sizeof(addtab)/sizeof(struct keytab) - 1; #ifndef NOCSETS struct keytab assoctab[] = { { "file-character-set", ASSOC_FC, 0 }, { "transfer-character-set", ASSOC_TC, 0 }, { "xfer-character-set", ASSOC_TC, CM_INV } }; static int nassoc = sizeof(assoctab)/sizeof(struct keytab); extern int afcset[MAXFCSETS+1]; /* Character-set associations */ extern int axcset[MAXTCSETS+1]; #endif /* NOCSETS */ #ifndef ADDCMD #ifndef NOMSEND #define ADDCMD #endif /* NOMSEND */ #ifndef ADDCMD #ifdef PATTERNS #define ADDCMD #endif /* PATTERNS */ #endif /* ADDCMD */ #endif /* ADDCMD */ #endif /* NOXFER */ /* External Kermit Variables, see ckmain.c for description. */ extern xx_strp xxstring; extern long xvernum; extern int local, xitsta, binary, msgflg, escape, duplex, quiet, tlevel, pflag, zincnt, ckxech, carrier, what, nopush, haveline, bye_active; #ifdef TNCODE extern int debses; extern char tn_msg[]; #endif /* TNCODE */ int sleepcan = 1; int g_binary = -1; int g_recursive = -1; int g_matchdot = -1; extern int nolinks; extern long vernum; extern char *versio, *copyright[]; extern char *ckxsys; #ifndef NOHELP extern char *introtxt[]; extern char *newstxt[]; #endif /* NOHELP */ #ifndef OS2 #ifndef UNIX extern char *PWDCMD; #endif /* UNIX */ extern char *WHOCMD; #endif /* OS2 */ extern char ttname[]; extern CHAR sstate; extern int network; /* Have active network connection */ extern int nettype; /* Type of network */ extern int ttnproto; /* NET_TCPB protocol */ #ifndef NODIAL extern int dialsta, dialatmo, dialcon, dialcq; /* DIAL status, etc. */ #endif /* NODIAL */ #ifdef CK_APC extern int apcactive, apcstatus; #endif /* CK_APC */ #ifndef NOPUSH #ifndef NOFRILLS extern char editor[]; extern char editopts[]; extern char editfile[]; #endif /* NOFRILLS */ #endif /* NOPUSH */ #ifdef BROWSER extern char browser[]; /* Web browser application */ extern char browsopts[]; /* Web browser options */ extern char browsurl[]; /* Most recent URL */ #endif /* BROWSER */ #ifndef NOFTP char ftpapp[CKMAXPATH+1] = { NUL, NUL }; /* ftp executable */ char ftpopts[128] = { NUL, NUL }; /* ftp command-line options */ #endif /* NOFTP */ extern struct keytab onoff[]; /* On/Off keyword table */ #ifdef CK_TMPDIR int f_tmpdir = 0; /* Directory changed temporarily */ char savdir[TMPDIRLEN]; /* For saving current directory */ #endif /* CK_TMPDIR */ int activecmd = -1; /* Keyword index of active command */ int doconx = -1; /* CONNECT-class command active */ int ooflag = 0; /* User-settable on/off flag */ int rcflag = 0; /* Pointer to home directory string */ int repars, /* Reparse needed */ techo = 0; /* Take echo */ int secho = 1; /* SCRIPT echo */ int xitwarn = /* Warn about open connection on exit */ #ifdef NOWARN 0 #else 1 #endif /* NOWARN */ ; struct keytab onoffsw[] = { { "/off", 0, 0 }, { "/on", 1, 0 } }; #ifdef CKEXEC struct keytab redirsw[] = { { "/redirect", 1, 0 } }; #endif /* CKEXEC */ #ifndef NOXMIT /* Variables for TRANSMIT command */ int xmitx = 1; /* Whether to echo during TRANSMIT */ int xmitf = 0; /* Character to fill empty lines */ int xmitl = 0; /* 0 = Don't send linefeed too */ int xmitp = LF; /* Host line prompt */ int xmits = 0; /* Use shift-in/shift-out, 0 = no */ int xmitw = 0; /* Milliseconds to pause during TRANSMIT */ int xmitt = 1; /* Seconds to wait for each char to echo */ int xmita = 1; /* Action upon timeout */ #define XMI_BIN 1 #define XMI_TXT 2 #define XMI_CMD 3 #define XMI_TRA 4 #define XMI_VRB 5 #define XMI_QUI 6 #define XMI_NOW 7 #define XMI_NOE 8 static struct keytab xmitsw[] = { /* TRANSMIT command options */ { "/binary", XMI_BIN, 0 }, #ifdef PIPESEND { "/command", XMI_CMD, CM_INV|CM_PSH }, #endif /* PIPESEND */ { "/noecho", XMI_NOE, 0 }, { "/nowait", XMI_NOW, 0 }, #ifdef PIPESEND { "/pipe", XMI_CMD, 0 }, #endif /* PIPESEND */ #ifdef COMMENT { "/quiet", XMI_QUI, 0 }, #endif /* COMMENT */ { "/text", XMI_TXT, 0 }, { "/transparent", XMI_TRA, 0 }, #ifdef COMMENT { "/verbose", XMI_VRB, 0 }, #endif /* COMMENT */ { "", 0, 0 } }; #define NXMITSW sizeof(xmitsw)/sizeof(struct keytab) - 1 static int nxmitsw = NXMITSW; #endif /* NOXMIT */ /* Declarations from ck?fio.c module */ extern char *SPACMD, *SPACM2; /* SPACE commands */ /* Command-oriented items */ #ifdef DCMDBUF extern char *cmdbuf; /* Command buffers */ extern char *atmbuf; extern char *line; /* Character buffer for anything */ extern char *tmpbuf; /* Short temporary string buffer */ extern int *ifcmd; extern int *intime; extern int *inpcas; #else extern char cmdbuf[]; /* Command buffers */ extern char atmbuf[]; extern char line[]; /* Character buffer for anything */ extern char tmpbuf[]; /* Temporary buffer */ extern int ifcmd[]; extern int intime[]; extern int inpcas[]; #endif /* DCMDBUF */ #ifndef NOSPL extern char * prstring[]; #endif /* NOSPL */ char *lp; /* Pointer to line buffer */ #ifndef NOSPL int vareval = 1; /* Evaluation method */ int unkmacro = 0; /* Flag for in ON_UNKNOWN_COMMAND */ int oldeval = 0; char evalbuf[33]; /* EVALUATE result */ extern char * inpbuf; /* Buffer for INPUT and REINPUT */ char *inpbp; /* And pointer to same */ int m_found; /* MINPUT result */ int i_active = 0; /* INPUT command is active */ char *ms[MINPMAX]; /* Pointers to MINPUT strings */ static int mpinited = 0; /* Flag they have been initialized */ static int mp[MINPMAX]; /* and MINPUT flags */ extern int fndiags, fnerror, fnsuccess; /* Function diagnostics */ #ifndef NOSEXP char * lastsexp = NULL; /* S-Expressions */ char * sexpval = NULL; int sexpecho = SET_AUTO; #endif /* NOSEXP */ #endif /* NOSPL */ char psave[PROMPTL] = { NUL }; /* For saving & restoring prompt */ extern int success; /* Command success/failure flag */ extern int cmdlvl; /* Current position in command stack */ #ifndef NOSPL int /* SET INPUT parameters. */ /* Note, INPUT TIMEOUT, intime[], is on the command-level stack. */ inbufsize = 0, /* INPUT buffer size */ indef = 1, /* default timeout, seconds */ inecho = 1, /* 1 = echo on */ inautodl = 0, /* INPUT autodownload */ inintr = 1, /* INPUT interrupion allowed */ insilence = 0; /* 0 = no silence constraint */ #ifdef CKFLOAT CKFLOAT inscale = 1.0; /* Timeout scale factor */ #endif /* CKFLOAT */ #ifdef OS2 int interm = 1; /* Terminal emulator displays input */ #endif /* OS2 */ int maclvl = -1; /* Macro nesting level */ int mecho = 0; /* Macro echo, 0 = don't */ char varnam[6]; /* For variable names */ extern int macargc[]; /* ARGC from macro invocation */ extern char *m_arg[MACLEVEL][NARGS]; /* Stack of macro arguments */ extern char *mrval[]; extern char **a_ptr[]; /* Array pointers */ extern int a_dim[]; /* Array dimensions */ extern int a_link[]; #ifdef DCMDBUF extern struct cmdptr *cmdstk; /* The command stack itself */ #else extern struct cmdptr cmdstk[]; /* The command stack itself */ #endif /* DCMDBUF */ long ck_alarm = 0; /* SET ALARM value */ char alrm_date[24] = { ' ',' ',' ',' ',' ',' ',' ',' ',' ' }; char alrm_time[24] = { ' ',' ',' ',' ',' ',' ',' ' }; struct keytab inputsw[] = { { "/clear", INPSW_CLR, 0 }, { "/count", INPSW_COU, CM_ARG }, { "/nomatch", INPSW_NOM, 0 }, { "/nowrap", INPSW_NOW, 0 } }; static int ninputsw = sizeof(inputsw)/sizeof(struct keytab); /* The following should be reconciled with the above */ #ifdef COMMENT /* INPUT switches not used yet... */ static struct keytab inswtab[] = { #ifdef COMMENT { "/assign", IN_ASG, CM_ARG }, #endif /* COMMENT */ { "/autodownload", IN_ADL, CM_ARG }, { "/case", IN_CAS, CM_ARG }, { "/echo", IN_ECH, CM_ARG }, { "/interrupts", IN_NOI, CM_ARG }, { "/silence", IN_SIL, CM_ARG }, #ifdef COMMENT { "/pattern", IN_PAT, CM_ARG }, #endif /* COMMENT */ { "", 0, 0 } }; static int ninswtab = (sizeof(inswtab) / sizeof(struct keytab)) - 1; #endif /* COMMENT */ #endif /* NOSPL */ static int x, y, z = 0; /* Local workers */ static char *s; #ifdef CK_MINPUT static char c1chars[] = { /* C1 control chars escept NUL */ 001,002,003,004,005,006,007,010,011,012,013,014,015,016,017,020, 021,022,023,024,025,026,027,030,031,032,033,034,035,036,037 }; #endif /* CK_MINPUT */ #define xsystem(s) zsyscmd(s) /* Top-Level Interactive Command Keyword Table */ /* Keywords must be in lowercase and in alphabetical order. */ struct keytab cmdtab[] = { #ifndef NOPUSH { "!", XXSHE, CM_INV|CM_PSH }, /* Shell escape */ #else { "!", XXNOTAV, CM_INV|CM_PSH }, #endif /* NOPUSH */ { "#", XXCOM, CM_INV }, /* Comment */ #ifndef NOSPL { "(", XXSEXP,CM_INV }, /* S-Expression */ { ".", XXDEF, CM_INV }, /* Assignment */ { ":", XXLBL, CM_INV }, /* Label */ #endif /* NOSPL */ #ifdef CK_REDIR #ifndef NOPUSH { "<", XXFUN, CM_INV|CM_PSH }, /* REDIRECT */ #else { "<", XXNOTAV, CM_INV|CM_PSH }, /* REDIRECT */ #endif /* NOPUSH */ #endif /* CK_REDIR */ #ifndef NOPUSH { "@", XXSHE, CM_INV|CM_PSH }, /* DCL escape */ #else { "@", XXNOTAV, CM_INV|CM_PSH }, /* DCL escape */ #endif /* NOPUSH */ #ifdef CK_RECALL { "^", XXREDO,CM_INV|CM_NOR }, /* Synonym for REDO */ #endif /* CK_RECALL */ #ifndef NOSPL { "_asg", XXASX, CM_INV }, /* Used internally by FOR, etc */ { "_assign", XXASX, CM_INV }, /* Used internally by FOR, etc */ { "_decrement", XX_DECR, CM_INV }, { "_define", XXDFX, CM_INV }, /* Used internally by FOR, etc */ { "_evaluate", XX_EVAL, CM_INV }, { "_forward", XXXFWD, CM_INV }, /* Used internally by SWITCH */ { "_getargs", XXGTA, CM_INV }, /* Used internally by FOR, etc */ { "_increment", XX_INCR, CM_INV }, { "_putargs", XXPTA, CM_INV }, /* Used internally by FOR, etc */ { "_undefine", XXUNDFX, CM_INV }, #endif /* NOSPL */ { "about", XXVER, CM_INV }, /* Synonym for VERSION */ #ifndef NOSPL #ifdef NEWFTP { "account", XXACCT, CM_INV }, /* (FTP) Account */ #endif /* NEWFTP */ #ifdef ADDCMD { "add", XXADD, 0 }, /* ADD */ #endif /* ADDCMD */ #ifndef NODIAL { "answer", XXANSW, CM_LOC }, /* ANSWER the phone */ #else { "answer", XXNOTAV, CM_INV|CM_LOC }, /* ANSWER the phone */ #endif /* NODIAL */ { "apc", XXAPC, 0 }, /* Application Program Command */ #ifndef NOSPL { "array", XXARRAY, 0 }, /* Array operations */ #endif /* NOSPL */ { "ascii", XXASC, CM_INV }, /* == SET FILE TYPE TEXT */ { "asg", XXASS, CM_INV }, /* Invisible synonym for ASSIGN */ { "ask", XXASK, 0 }, /* ASK for text, assign to variable */ { "askq", XXASKQ,0 }, /* ASK quietly (no echo) */ #ifndef NOSPL { "ass", XXASS, CM_INV|CM_ABR }, /* ASSIGN */ { "assert", XXASSER, CM_INV }, /* ASSERT */ { "assign", XXASS, 0 }, /* ASSIGN */ #endif /* NOSPL */ #ifndef NOXFER #ifndef NOCSETS { "associate", XXASSOC, 0 }, /* ASSOCIATE */ #else { "associate", XXNOTAV, CM_INV }, /* ASSOCIATE */ #endif /* NOCSETS */ #endif /* NOXFER */ #ifdef CK_KERBEROS #ifdef CK_AUTHENTICATION { "authenticate",XXAUTH, 0 }, /* Authentication */ #else { "authenticate",XXAUTH, CM_INV }, #endif /* CK_AUTHENTICATION */ #endif /* CK_KERBEROS */ #endif /* NOSPL */ #ifndef NOFRILLS { "back", XXBACK, 0 }, /* BACK to previous directory */ #else { "back", XXNOTAV,CM_INV }, #endif /* NOFRILLS */ { "beep", XXBEEP,CM_INV }, /* BEEP */ #ifndef NOXFER { "binary", XXBIN, CM_INV }, /* == SET FILE TYPE BINARY */ #endif /* NOXFER */ #ifndef NOFRILLS { "bug", XXBUG, CM_INV }, /* BUG report instructions */ #else { "bug", XXNOTAV, CM_INV }, #endif /* NOFRILLS */ #ifdef BROWSER { "browse", XXBROWS, CM_PSH|CM_LOC }, /* BROWSE (start browser) */ #else { "browse", XXNOTAV, CM_INV|CM_PSH|CM_LOC }, #endif /* BROWSER */ #ifndef NOXFER { "bye", XXBYE, 0 }, /* BYE to remote server */ #endif /* NOXFER */ #ifndef NOLOCAL { "c", XXCON, CM_INV|CM_ABR|CM_LOC }, /* (CONNECT) */ #endif /* NOLOCAL */ #ifndef NOFRILLS { "cat", XXCAT, CM_INV }, /* Invisible synonym for TYPE */ #endif /* NOFRILLS */ #ifndef NOSPL #ifndef NOXFER { "cautious", XXCAU, CM_INV }, #endif /* NOXFER */ #endif /* NOSPL */ { "cd", XXCWD, 0 }, /* Change Directory */ { "cdup", XXCDUP, CM_INV }, /* Change Directory Up */ #ifndef NOXFER #ifdef PIPESEND { "cget", XXCGET, CM_INV|CM_PSH }, /* CGET */ #else { "cget", XXNOTAV, CM_INV|CM_PSH }, /* CGET */ #endif /* PIPESEND */ #endif /* NOXFER */ { "ch", XXCHK, CM_INV|CM_ABR }, { "check", XXCHK, 0 }, /* CHECK for a feature */ #ifdef CK_PERMS #ifdef UNIX { "chmod", XXCHMOD, 0 }, /* CHMOD */ #else { "chmod", XXNOTAV, CM_INV }, #endif /* UNIX */ #else { "chmod", XXNOTAV, CM_INV }, #endif /* CK_PERMS */ #ifdef CKROOT { "chroot", XXCHRT, CM_INV }, /* CHROOT */ #endif /* CKROOT */ { "ckermit", XXKERMI, CM_INV }, /* CKERMIT (like KERMIT) */ { "cl", XXCLO, CM_ABR|CM_INV }, #ifndef NOFRILLS { "clear", XXCLE, 0 }, /* CLEAR input and/or device buffer */ #else { "clear", XXNOTAV, CM_INV }, #endif /* NOFRILLS */ { "close", XXCLO, 0 }, /* CLOSE a log or other file */ { "cls", XXCLS, CM_INV }, /* Clear Screen (CLS) */ { "comment", XXCOM, CM_INV }, /* Introduce a comment */ #ifndef NOLOCAL { "connect", XXCON, CM_LOC }, /* Begin terminal connection */ #else { "connect", XXNOTAV, CM_LOC }, #endif /* NOLOCAL */ { "continue", XXCONT, CM_INV }, /* CONTINUE */ #ifndef NOFRILLS #ifdef ZCOPY { "co", XXCPY, CM_INV|CM_ABR }, { "cop", XXCPY, CM_INV|CM_ABR }, { "copy", XXCPY, 0 }, /* COPY a file */ #else { "copy", XXNOTAV, CM_INV }, #endif /* ZCOPY */ { "copyright", XXCPR, CM_INV }, /* COPYRIGHT */ #ifdef ZCOPY { "cp", XXCPY, CM_INV }, /* COPY a file */ #endif /* ZCOPY */ #ifndef NOLOCAL #ifndef OS2 { "cq", XXCQ, CM_INV|CM_LOC }, /* CQ (connect quietly) */ #endif /* OS2 */ #endif /* NOLOCAL */ #ifndef NOXFER #ifdef PIPESEND { "creceive", XXCREC,CM_INV|CM_PSH }, /* RECEIVE to a command */ { "csend", XXCSEN,CM_INV|CM_PSH }, /* SEND from command */ #else { "creceive", XXNOTAV,CM_INV|CM_PSH }, { "csend", XXNOTAV,CM_INV|CM_PSH }, #endif /* PIPESEND */ #endif /* NOXFER */ #endif /* NOFRILLS */ { "cwd", XXCWD, CM_INV }, /* Traditional synonym for cd */ #ifndef NOSPL { "date", XXDATE, 0 }, /* DATE */ { "dcl", XXDCL, CM_INV }, /* DECLARE an array (see ARRAY) */ { "debug", XXDEBUG, 0 }, /* Print a debugging msg [9.0] */ { "declare", XXDCL, CM_INV }, /* DECLARE an array (see ARRAY) */ { "decrement", XXDEC, 0 }, /* DECREMENT a numeric variable */ { "define", XXDEF, 0 }, /* DEFINE a macro or variable */ #else { "date", XXNOTAV, CM_INV }, { "dcl", XXNOTAV, CM_INV }, { "declare", XXNOTAV, CM_INV }, { "decrement", XXNOTAV, CM_INV }, { "define", XXNOTAV, CM_INV }, #endif /* NOSPL */ #ifndef NOFRILLS { "delete", XXDEL, 0 }, /* DELETE a file */ #else { "delete", XXNOTAV, CM_INV }, #endif /* NOFRILLS */ #ifndef NODIAL { "dial", XXDIAL, CM_LOC }, /* DIAL a phone number */ #else { "dial", XXNOTAV, CM_INV|CM_LOC }, #endif /* NODIAL */ #ifdef NT { "dialer", XXDIALER, CM_INV }, /* K95 Dialer */ #endif /* NT */ { "directory", XXDIR, 0 }, /* DIRECTORY of files */ #ifndef NOFRILLS #ifndef NOSERVER { "disable", XXDIS, 0 }, /* DISABLE a server function */ #else { "disable", XXNOTAV, CM_INV }, #endif /* NOSERVER */ #endif /* NOFRILLS */ #ifndef NOSPL { "do", XXDO, 0 }, /* DO (execute) a macro */ #else { "do", XXNOTAV, CM_INV }, #endif /* NOSPL */ { "e", XXEXI, CM_INV|CM_ABR }, #ifndef NOFRILLS #ifndef NOXFER { "e-packet", XXERR, CM_INV }, /* Send an Error-Packet */ #endif /* NOXFER */ #endif /* NOFRILLS */ { "echo", XXECH, 0 }, /* ECHO text */ #ifndef NOFRILLS #ifndef NOPUSH { "edit", XXEDIT, CM_PSH }, /* EDIT */ #else { "edit", XXNOTAV, CM_INV|CM_PSH }, /* EDIT */ #endif /* NOPUSH */ #endif /* NOFRILLS */ { "eightbit", XXEIGHT, CM_INV }, /* EIGHTBIT */ #ifndef NOSPL { "else", XXELS, CM_INV }, /* ELSE part of IF statement */ #else { "else", XXNOTAV, CM_INV }, /* ELSE part of IF statement */ #endif /* NOSPL */ #ifndef NOSERVER #ifndef NOFRILLS { "enable", XXENA, 0 }, /* ENABLE a server function */ #else { "enable", XXNOTAV, CM_INV }, #endif /* NOFRILLS */ #endif /* NOSERVER */ #ifndef NOSPL { "end", XXEND, 0 }, /* END command file or macro */ #else { "end", XXNOTAV, CM_INV }, #endif /* NOSPL */ { "erase", XXDEL, CM_INV }, /* Synonym for DELETE */ #ifndef NOSPL { "evaluate", XXEVAL, 0 }, /* EVALUATE */ #else { "evaluate", XXNOTAV, CM_INV }, #endif /* NOSPL */ { "ex", XXEXI, CM_INV|CM_ABR }, /* Let "ex" still be EXIT */ #ifdef CKEXEC { "exec", XXEXEC, CM_INV|CM_LOC }, /* exec() */ #else { "exec", XXNOTAV, CM_INV|CM_LOC }, #endif /* CKEXEC */ { "exit", XXEXI, 0 }, /* EXIT from C-Kermit */ { "extended-options", XXXOPTS,CM_INV|CM_HLP }, /* Extended-Options */ #ifdef OS2 { "extproc", XXCOM, CM_INV }, /* Dummy command for OS/2 */ #endif /* OS2 */ #ifndef NOXFER { "f", XXFIN, CM_INV|CM_ABR }, /* Invisible abbrev for FIN */ #endif /* NOXFER */ #ifndef NOSPL { "fail", XXFAIL, CM_INV }, /* FAIL */ #ifndef NOXFER { "fast", XXFAST, CM_INV }, #endif /* NOXFER */ #ifdef CKCHANNELIO { "fclose", XXF_CL, CM_INV }, /* FCLOSE */ { "fcount", XXF_CO, CM_INV }, /* FCOUNT */ { "fflush", XXF_FL, CM_INV }, /* FFLUSH */ #endif /* CKCHANNELIO */ #ifndef NOXFER { "fi", XXFIN, CM_INV|CM_ABR }, /* FINISH */ #endif /* NOXFER */ #ifdef CKCHANNELIO { "file", XXFILE, 0 }, /* FILE */ #endif /* CKCHANNELIO */ #endif /* NOSPL */ #ifndef NOXFER { "fin", XXFIN, CM_INV|CM_ABR }, /* FINISH */ #endif /* NOXFER */ #ifndef UNIXOROSK { "find", XXGREP, 0 }, /* FIND (grep) */ #else { "find", XXGREP,CM_INV }, #endif /* UNIXOROSK */ #ifndef NOXFER { "finish", XXFIN, 0 }, /* FINISH */ #endif /* NOXFER */ #ifdef TCPSOCKET { "firewall", XXFIREW, CM_INV|CM_HLP }, #endif /* TCPSOCKET */ #ifdef CKCHANNELIO { "flist", XXF_LI, CM_INV }, /* FLIST */ { "fopen", XXF_OP, CM_INV }, /* FOPEN */ #endif /* CKCHANNELIO */ #ifndef NOSPL { "fo", XXFOR, CM_INV|CM_ABR }, /* Invisible abbrev for... */ { "for", XXFOR, 0 }, /* FOR loop */ { "forward", XXFWD, CM_INV }, /* FORWARD */ #endif /* NOSPL */ #ifndef NOFRILLS { "fot", XXDIR, CM_INV }, /* "fot" = "dir" (for Chris) */ #endif /* NOFRILLS */ #ifdef CKCHANNELIO { "fread", XXF_RE, CM_INV }, /* FREAD */ { "frewind", XXF_RW, CM_INV }, /* FREWIND */ { "fseek", XXF_SE, CM_INV }, /* FSEEK */ { "fstatus", XXF_ST, CM_INV }, /* FSTATUS */ #endif /* CKCHANNELIO */ #ifdef TCPSOCKET #ifndef NOFTP #ifdef SYSFTP #ifndef NOPUSH { "ftp", XXFTP, CM_INV|CM_PSH|CM_LOC }, /* System FTP */ #else { "ftp", XXNOTAV, CM_INV|CM_PSH|CM_LOC }, #endif /* NOPUSH */ #else /* SYSFTP */ { "ftp", XXFTP, 0 }, /* Built-in FTP */ #endif /* SYSFTP */ #else /* NOFTP */ { "ftp", XXNOTAV, CM_INV }, /* No FTP */ #endif /* NOFTP */ #endif /* TCPSOCKET */ #ifndef NOSPL { "function", XXFUNC, CM_INV|CM_HLP }, /* (for HELP FUNCTION) */ #endif /* NOSPL */ #ifdef CKCHANNELIO { "fwrite", XXF_WR, CM_INV }, /* FWRITE */ #endif /* CKCHANNELIO */ #ifndef NOXFER { "g", XXGET, CM_INV|CM_ABR }, /* Invisible abbrev for GET */ #ifndef NOSPL { "ge", XXGET, CM_INV|CM_ABR }, /* Ditto */ #endif /* NOSPL */ { "get", XXGET, 0 }, /* GET */ #endif /* NOXFER */ #ifndef NOSPL { "getc", XXGETC, 0 }, /* GETC */ #ifdef OS2 { "getkeycode", XXGETK, 0 }, /* GETKEYCODE */ #endif /* OS2 */ #ifndef NOFRILLS { "getok", XXGOK, 0 }, /* GETOK (ask for Yes/No/OK) */ #endif /* NOFRILLS */ #endif /* NOSPL */ #ifndef NOSPL { "goto", XXGOTO,0 }, /* GOTO label in take file or macro */ #endif /* NOSPL */ #ifdef UNIXOROSK { "grep", XXGREP,0 }, /* GREP (find) */ #else { "grep", XXGREP,CM_INV }, /* GREP (find) */ #endif /* UNIXOROSK */ { "h", XXHLP, CM_INV|CM_ABR }, /* Invisible synonym for HELP */ { "he", XXHLP, CM_INV|CM_ABR }, /* Invisible synonym for HELP */ #ifndef NOFRILLS { "head", XXHEAD, 0 }, #endif /* NOFRILLS */ #ifndef NOLOCAL { "hangup", XXHAN, CM_LOC }, /* HANGUP the connection */ #endif /* NOLOCAL */ { "hdirectory", XXHDIR, CM_INV }, /* DIR sorted by size biggest first */ { "HELP", XXHLP, 0 }, /* Display HELP text */ #ifndef NOHTTP #ifdef TCPSOCKET { "http", XXHTTP, 0 }, /* HTTP operations */ #endif /* TCPSOCKET */ #endif /* NOHTTP */ #ifndef NOSPL { "i", XXINP, CM_INV|CM_ABR }, /* Invisible synonym for INPUT */ { "if", XXIF, 0 }, /* IF ( condition ) command */ #ifdef TCPSOCKET { "iksd", XXIKSD, CM_INV }, /* Make connection to IKSD */ #else { "iksd", XXNOTAV, CM_INV }, #endif /* TCPSOCKET */ { "in", XXINP, CM_INV|CM_ABR }, /* Invisible synonym for INPUT */ { "increment", XXINC, 0 }, /* Increment a numeric variable */ { "input", XXINP, 0 }, /* INPUT text from comm device */ #endif /* NOSPL */ #ifndef NOHELP { "int", XXINT, CM_INV|CM_ABR }, { "intr", XXINT, CM_INV|CM_ABR }, { "INTRO", XXINT, 0 }, { "introduction",XXINT, CM_INV }, /* Print introductory text */ #else { "intro", XXNOTAV, CM_INV }, { "introduction",XXNOTAV, CM_INV }, #endif /* NOHELP */ #ifdef OS2 { "k95", XXKERMI, CM_INV }, /* Hmmm what's this... */ #endif /* OS2 */ #ifndef NOSPL { "kcd", XXKCD, 0 }, #endif /* NOSPL */ { "kermit", XXKERMI, CM_INV }, #ifdef OS2 #ifndef NOKVERBS { "kverb", XXKVRB, CM_INV|CM_HLP }, /* Keyboard verb */ #endif /* NOKVERBS */ #endif /* OS2 */ #ifndef NOFRILLS { "l", XXLOG, CM_INV|CM_ABR }, /* Invisible synonym for log */ #endif /* NOFRILLS */ { "lcd", XXLCWD, CM_INV }, { "lcdup", XXLCDU, CM_INV }, { "lcwd", XXLCWD, CM_INV }, { "ldelete", XXLDEL, CM_INV }, { "ldirectory", XXLDIR, CM_INV }, #ifdef CKLEARN { "learn", XXLEARN, 0 }, /* LEARN - automatic script writing */ #else { "learn", XXNOTAV, CM_INV }, #endif /* CKLEARN */ { "li", XXLNOUT, CM_INV|CM_ABR }, { "LICENSE", XXCPR, 0 }, /* LICENSE */ #ifndef NOSPL { "lineout", XXLNOUT, 0 }, /* LINEOUT = OUTPUT + eol */ #endif /* NOSPL */ #ifdef NT { "link", XXLINK, 0 }, /* LINK source destination */ #endif /* NT */ { "lmkdir", XXLMKD, CM_INV }, { "lmv", XXLREN, CM_INV }, #ifndef NOFRILLS { "lo", XXLOG, CM_INV|CM_ABR }, /* Invisible synonym for log */ #endif /* NOFRILLS */ #ifndef NOSPL { "local", XXLOCAL, CM_INV }, /* LOCAL variable declaration */ #else { "local", XXNOTAV, CM_INV }, #endif /* NOSPL */ { "locus", XXLOCU, CM_INV|CM_HLP }, /* "help locus" */ { "log", XXLOG, 0 }, /* Open a log file */ { "login", XXLOGIN, 0 }, /* (REMOTE) LOGIN to server or IKSD */ { "logout", XXLOGOUT, 0 }, /* LOGOUT from server or IKSD */ #ifndef NOFRILLS #ifndef NODIAL { "lookup", XXLOOK, 0 }, /* LOOKUP */ #else { "lookup", XXNOTAV, CM_INV }, #endif /* NODIAL */ { "lpwd", XXLPWD, CM_INV }, { "lrename", XXLREN, CM_INV }, { "lrmdir", XXLRMD, CM_INV }, #ifdef UNIXOROSK { "ls", XXLS, CM_INV|CM_PSH }, /* UNIX ls command */ #else { "ls", XXDIR, CM_INV }, /* Invisible synonym for DIR */ #endif /* UNIXOROSK */ #ifndef NOXFER { "mail", XXMAI, 0 }, /* Send a file as e-mail */ #endif /* NOXFER */ #ifndef NOHELP { "manual", XXMAN, CM_PSH }, /* MAN(UAL) */ #else { "manual", XXNOTAV, CM_INV|CM_PSH }, #endif /* NOHELP */ #endif /* NOFRILLS */ #ifdef CK_MKDIR { "md", XXMKDIR, CM_INV }, /* Synonym for MKDIR */ #endif /* CK_MKDIR */ { "message", XXMSG, 0 }, /* Print debugging message */ #ifdef CK_MINPUT { "minput", XXMINP, 0 }, /* MINPUT */ #else { "minput", XXNOTAV, CM_INV }, #endif /* CK_MINPUT */ #ifndef NOMSEND { "mget", XXMGET, 0 }, /* MGET */ #else { "mget", XXNOTAV, CM_INV }, #endif /* NOMSEND */ #ifdef CK_MKDIR { "mkdir", XXMKDIR, 0 }, /* MKDIR */ #else { "mkdir", XXNOTAV, CM_INV }, #endif /* CK_MKDIR */ #ifndef NOXFER #ifndef NOMSEND { "mmove", XXMMOVE, 0 }, /* MMOVE */ #else { "mmove", XXNOTAV, CM_INV }, #endif /* NOMSEND */ #endif /* NOXFER */ #ifndef NOFRILLS { "more", XXMORE, CM_INV }, /* MORE */ #endif /* NOFRILLS */ #ifndef NOXFER { "move", XXMOVE, 0 }, /* MOVE */ #endif /* NOXFER */ #ifndef NOSPL { "mpause", XXMSL, CM_INV }, /* Millisecond sleep */ #else { "mpause", XXNOTAV, CM_INV }, #endif /* NOSPL */ #ifndef NOXFER #ifndef NOMSEND { "mput", XXMSE, CM_INV }, /* MPUT = MSEND */ { "ms", XXMSE, CM_INV|CM_ABR }, { "msend", XXMSE, 0 }, /* Multiple SEND */ #else { "mput", XXNOTAV, CM_INV }, { "msend", XXNOTAV, CM_INV }, #endif /* NOMSEND */ #endif /* NOXFER */ { "msg", XXMSG, CM_INV }, /* Print debugging message */ #ifndef NOSPL { "msleep", XXMSL, 0 }, /* Millisecond sleep */ #else { "msleep", XXNOTAV, CM_INV }, #endif /* NOSPL */ #ifndef NOFRILLS { "mv", XXREN, CM_INV }, /* Synonym for rename */ #endif /* NOFRILLS */ #ifndef NOHELP { "news", XXNEW, CM_INV }, /* Display NEWS of new features */ #else { "news", XXNOTAV, CM_INV }, #endif /* NOHELP */ { "nolocal", XXNLCL, CM_INV }, /* Disable SET LINE / SET HOST */ { "nopush", XXNPSH, CM_INV }, /* Disable PUSH command/features */ #ifdef OS2 { "noscroll", XXNSCR, CM_INV }, /* Disable scroll operations */ #endif /* OS2 */ #ifndef NOSPL { "o", XXOUT, CM_INV|CM_ABR }, /* Invisible synonym for OUTPUT */ { "open", XXOPE, 0 }, /* OPEN file for reading or writing */ #else { "open", XXOPE, CM_INV }, /* OPEN */ #endif /* NOSPL */ #ifndef NOHELP { "options", XXOPTS,CM_INV|CM_HLP }, /* Options */ #endif /* NOHELP */ { "orientation", XXORIE, 0 }, #ifndef NOSPL { "output", XXOUT, 0 }, /* OUTPUT text to comm device */ #else { "output", XXNOTAV, CM_INV }, #endif /* NOSPL */ #ifdef ANYX25 #ifndef IBMX25 { "pad", XXPAD, CM_LOC }, /* X.3 PAD commands */ #endif /* IBMX25 */ #endif /* ANYX25 */ #ifdef NEWFTP { "passive", XXPASV, CM_INV }, /* (FTP) PASSIVE */ #endif /* NEWFTP */ #ifndef NOHELP { "patterns", XXPAT,CM_INV|CM_HLP }, /* Pattern syntax */ #endif /* NOHELP */ #ifndef NOSPL { "pause", XXPAU, 0 }, /* Sleep for specified interval */ #else { "pause", XXNOTAV, CM_INV }, #endif /* NOSPL */ #ifndef NODIAL { "pdial", XXPDIA, CM_LOC }, /* PDIAL (partial dial) */ #else { "pdial", XXNOTAV, CM_INV|CM_LOC }, #endif /* NODIAL */ #ifdef TCPSOCKET #ifndef NOPUSH { "ping", XXPNG, CM_INV|CM_PSH|CM_LOC }, /* PING */ #else { "ping", XXNOTAV, CM_INV|CM_PSH|CM_LOC }, #endif /* NOPUSH */ #endif /* TCPSOCKET */ #ifdef NETCMD #ifndef NOPUSH { "pipe", XXPIPE, CM_PSH }, /* PIPE */ #else { "pipe", XXNOTAV, CM_INV|CM_PSH }, /* PIPE */ #endif /* NOPUSH */ #endif /* NETCMD */ #ifndef NOSPL { "pop", XXEND, CM_INV }, /* Invisible synonym for END */ #endif /* NOSPL */ #ifndef NOFRILLS { "print", XXPRI, 0 }, /* PRINT a file locally */ #endif /* NOFRILLS */ { "prompt", XXPROMP, CM_INV }, /* Go interactive (from script) */ #ifndef NOXFER #ifdef CK_RESEND { "psend", XXPSEN, CM_INV }, /* PSEND */ #else { "psend", XXNOTAV, CM_INV }, #endif /* CK_RESEND */ #endif /* NOXFER */ #ifdef NETPTY { "pty", XXPTY, CM_PSH }, /* PTY */ #else { "pty", XXNOTAV, CM_INV|CM_PSH }, #endif /* NETPTY */ #ifndef NOPUSH { "pu", XXSHE, CM_INV|CM_ABR|CM_PSH }, /* PU = PUSH */ #endif /* NOPUSH */ #ifdef CKPURGE { "purge", XXPURGE, 0 }, /* PURGE (real) */ #else #ifdef VMS { "purge", XXPURGE, 0 }, /* PURGE (fake) */ #else { "purge", XXNOTAV, CM_INV }, #endif /* VMS */ #endif /* CKPURGE */ #ifndef NOPUSH { "push", XXSHE, CM_PSH }, /* PUSH command (like RUN, !) */ #else { "push", XXNOTAV, CM_INV|CM_PSH }, #endif /* NOPUSH */ #ifndef NOXFER { "put", XXSEN, CM_INV }, /* PUT = SEND */ #endif /* NOXFER */ #ifdef UNIX #ifndef NOPUTENV { "putenv", XXPUTE, CM_INV }, /* PUTENV */ #endif /* NOPUTENV */ #endif /* UNIX */ { "pwd", XXPWD, 0 }, /* Print Working Directory */ { "q", XXQUI, CM_INV|CM_ABR }, /* Invisible synonym for QUIT */ #ifndef NOXFER { "query", XXRQUE,CM_INV }, /* (= REMOTE QUERY) */ #endif /* NOXFER */ { "quit", XXQUI, 0 }, /* QUIT from program = EXIT */ #ifndef NOXFER { "r", XXREC, CM_INV|CM_ABR }, /* Inv synonym for RECEIVE */ #endif /* NOXFER */ #ifndef NOXFER { "rasg", XXRASG, CM_INV }, /* REMOTE ASSIGN */ { "rassign", XXRASG, CM_INV }, /* ditto */ { "rcd", XXRCWD, CM_INV }, /* REMOTE CD */ { "rcdup", XXRCDUP,CM_INV }, /* REMOTE CD */ { "rcopy", XXRCPY, CM_INV }, /* REMOTE COPY */ { "rcwd", XXRCWD, CM_INV }, /* REMOTE CWD */ { "rdelete", XXRDEL, CM_INV }, /* REMOTE DELETE */ { "rdirectory", XXRDIR, CM_INV }, /* REMODE DIRECTORY */ #endif /* NOXFER */ #ifndef NOSPL { "read", XXREA, 0 }, /* READ a line from a file */ #else { "read", XXNOTAV, CM_INV }, #endif /* NOSPL */ #ifndef NOXFER { "receive", XXREC, 0 }, /* RECEIVE files */ #endif /* NOXFER */ #ifndef NODIAL { "red", XXRED, CM_INV|CM_ABR|CM_LOC }, /* Inv syn for REDIAL */ { "redi", XXRED, CM_INV|CM_ABR|CM_LOC }, /* ditto */ { "redial", XXRED, CM_LOC }, /* REDIAL last DIAL number */ #else { "red", XXNOTAV, CM_INV|CM_LOC }, { "redi", XXNOTAV, CM_INV|CM_LOC }, { "redial", XXNOTAV, CM_INV|CM_LOC }, #endif /* NODIAL */ #ifdef CK_REDIR #ifdef OS2 #ifndef NOPUSH { "redirect", XXFUN, CM_INV|CM_PSH }, /* REDIRECT */ #else { "redirect", XXNOTAV, CM_INV|CM_PSH }, #endif /* NOPUSH */ #else /* OS2 */ #ifndef NOPUSH { "redirect", XXFUN, CM_PSH }, /* REDIRECT */ #else { "redirect", XXNOTAV, CM_INV|CM_PSH }, #endif /* NOPUSH */ #endif /* OS2 */ #endif /* CK_REDIR */ #ifdef CK_RECALL { "redo", XXREDO, CM_NOR }, /* REDO */ #else { "redo", XXNOTAV, CM_INV }, #endif /* CK_RECALL */ #ifndef NOXFER #ifdef CK_RESEND { "reget", XXREGET, 0 }, /* REGET */ #else { "reget", XXNOTAV, CM_INV }, #endif /* CK_RESEND */ #endif /* NOXFER */ #ifndef NOSPL { "reinput", XXREI, CM_INV }, /* REINPUT (from INPUT buffer) */ #else { "reinput", XXNOTAV, CM_INV }, #endif /* NOSPL */ #ifndef NOXFER #ifdef ADDCMD { "rem", XXREM, CM_INV|CM_ABR }, { "remo", XXREM, CM_INV|CM_ABR }, #endif /* ADDCMD */ { "remote", XXREM, 0 }, /* Send REMOTE command to server */ #endif /* NOXFER */ #ifdef ADDCMD { "remove", XXREMV,0 }, /* REMOVE (something from a list) */ #else { "remove", XXNOTAV, CM_INV }, #endif /* ADDCMD */ #ifndef NOFRILLS #ifndef NORENAME { "rename", XXREN, 0 }, /* RENAME a local file */ #else { "rename", XXNOTAV, CM_INV }, #endif /* NORENAME */ { "replay", XXTYP, CM_INV }, /* REPLAY (for now, just type) */ #endif /* NOFRILLS */ #ifndef NOXFER #ifdef CK_RESEND { "rep", XXTYP, CM_INV|CM_ABR }, /* REPLAY abbreviation */ { "reput", XXRSEN, CM_INV }, /* REPUT = RESEND */ { "res", XXRSEN, CM_INV|CM_ABR }, /* RESEND */ { "rese", XXRSEN, CM_INV|CM_ABR }, /* RESEND */ { "resend", XXRSEN, 0 }, /* RESEND */ #else { "reput", XXNOTAV, CM_INV }, { "res", XXNOTAV, CM_INV }, { "rese", XXNOTAV, CM_INV }, { "resend", XXNOTAV, CM_INV }, #endif /* CK_RESEND */ #endif /* NOXFER */ { "reset", XXRESET, CM_INV }, /* RESET */ #ifdef CK_RESEND #ifndef NOSPL { "ret", XXRET, CM_INV|CM_ABR }, #endif /* NOSPL */ #endif /* CK_RESEND */ #ifndef NOXFER { "retrieve", XXRETR, CM_INV }, /* RETRIEVE */ #endif /* NOXFER */ #ifndef NOSPL { "return", XXRET, 0 }, /* RETURN from a function */ #else { "return", XXNOTAV, CM_INV }, #endif /* NOSPL */ #ifndef NOXFER { "rexit", XXRXIT, CM_INV }, /* REMOTE EXIT */ #endif /* NOXFER */ #ifdef CK_REXX #ifndef NOPUSH { "rexx", XXREXX, CM_PSH }, /* Execute a Rexx command */ #else { "rexx", XXNOTAV, CM_INV|CM_PSH }, #endif /* NOPUSH */ #endif /* CK_REXX */ #ifndef NOXFER { "rhelp", XXRHLP, CM_INV }, /* REMOTE HELP */ { "rhost", XXRHOS, CM_INV }, /* REMOTE HOST */ { "rkermit", XXRKER, CM_INV }, /* REMOTE KERMIT */ #endif /* NOXFER */ #ifdef TCPSOCKET { "rlogin", XXRLOG, CM_LOC }, /* Make an Rlogin connection */ #else { "rlogin", XXNOTAV, CM_INV|CM_LOC }, #endif /* TCPSOCKET */ #ifndef NOFRILLS { "rm", XXDEL, CM_INV }, /* Invisible synonym for delete */ #endif /* NOFRILLS */ #ifdef CK_MKDIR { "rmdir", XXRMDIR, 0 }, /* RMDIR */ #else { "rmdir", XXNOTAV, CM_INV }, #endif /* CK_MKDIR */ #ifndef NOXFER { "rmessage", XXRMSG, CM_INV }, /* REMOTE MESSAGE */ { "rmkdir", XXRMKD, CM_INV }, /* REMOTE MKDIR */ { "rmsg", XXRMSG, CM_INV }, /* REMOTE MESSAGE */ #ifndef NOSPL { "robust", XXROB, CM_INV }, #else { "robust", XXNOTAV, CM_INV }, #endif /* NOSPL */ { "rprint", XXRPRI, CM_INV }, /* REMOTE PRINT */ { "rpwd", XXRPWD, CM_INV }, /* REMOTE PWD */ { "rquery", XXRQUE, CM_INV }, /* REMOTE QUERY */ #endif /* NOXFER */ #ifdef CK_RECALL { "rr", XXREDO, CM_INV|CM_NOR }, #endif /* CK_RECALL */ #ifndef NOXFER { "rrename", XXRREN, CM_INV }, /* REMOTE RENAME */ { "rrmdir", XXRRMD, CM_INV }, /* REMOTE REMDIR */ { "rset", XXRSET, CM_INV }, /* REMOTE SET */ { "rspace", XXRSPA, CM_INV }, /* REMOTE SPACE */ { "rtype", XXRTYP, CM_INV }, /* REMOTE TYPE */ #endif /* NOXFER */ #ifndef NOPUSH { "run", XXSHE, CM_PSH }, /* RUN a program or command */ #else { "run", XXNOTAV, CM_INV|CM_PSH }, #endif /* NOPUSH */ #ifndef NOXFER { "rwho", XXRWHO, CM_INV }, /* REMOTE WHO */ { "s", XXSEN, CM_INV|CM_ABR }, /* Invisible synonym for send */ #endif /* NOXFER */ #ifndef NOSETKEY #ifdef OS2 { "save", XXSAVE, 0 }, /* SAVE something */ #else { "save", XXSAVE, CM_INV }, #endif /* OS2 */ #else { "save", XXNOTAV, CM_INV }, #endif /* NOSETKEY */ #ifndef NOSCRIPT { "sc", XXLOGI, CM_INV|CM_ABR|CM_LOC }, { "scr", XXLOGI, CM_INV|CM_ABR|CM_LOC }, #endif /* NOSCRIPT */ { "screen", XXSCRN, 0 }, /* SCREEN actions */ #ifndef NOSCRIPT { "script", XXLOGI, CM_LOC }, /* Expect-Send-style script line */ #else { "script", XXNOTAV, CM_INV|CM_LOC }, #endif /* NOSCRIPT */ { "search", XXGREP,CM_INV }, /* Synonym for GREP and FIND */ #ifndef NOXFER { "send", XXSEN, 0 }, /* Send (a) file(s) */ #ifndef NOSERVER { "server", XXSER, 0 }, /* Be a SERVER */ #else { "server", XXNOTAV, CM_INV }, #endif /* NOSERVER */ #endif /* NOXFER */ { "set", XXSET, 0 }, /* SET parameters */ #ifndef NOSPL #ifndef NOSEXP { "sexpression", XXSEXP, CM_INV|CM_HLP }, /* SEXPR */ #endif /* NOSEXP */ #ifdef SFTP_BUILTIN { "sftp", XXSFTP, 0 }, /* SFTP */ #endif /* SFTP_BUILTIN */ #ifndef NOSHOW { "sh", XXSHO, CM_INV|CM_ABR }, /* SHOW parameters */ #endif /* NOSHOW */ { "shift", XXSHIFT, 0 }, /* SHIFT args */ #else { "shift", XXNOTAV, CM_INV }, #endif /* NOSPL */ #ifndef NOSHOW { "show", XXSHO, 0 }, /* SHOW parameters */ #else { "show", XXNOTAV, CM_INV }, #endif /* NOSHOW */ #ifdef NEWFTP { "site", XXSITE, CM_INV }, /* (FTP) SITE */ #endif /* NEWFTP */ #ifdef SSHBUILTIN { "skermit", XXSKRM, 0 }, /* SKERMIT */ #endif /* SSHBUILTIN */ #ifndef NOSPL #ifndef NOFRILLS { "sleep", XXPAU, CM_INV }, /* SLEEP for specified interval */ #endif /* NOFRILLS */ #endif /* NOSPL */ #ifndef NOSPL { "sort", XXSORT, CM_INV }, /* (see ARRAY) */ #else { "sort", XXNOTAV, CM_INV }, #endif /* NOSPL */ #ifndef MAC #ifndef NOFRILLS { "sp", XXSPA, CM_INV|CM_ABR }, { "spa", XXSPA, CM_INV|CM_ABR }, #endif /* NOFRILLS */ { "space", XXSPA, 0 }, /* Show available disk SPACE */ #endif /* MAC */ #ifndef NOFRILLS #ifndef NOPUSH { "spawn", XXSHE, CM_INV|CM_PSH }, /* Synonym for PUSH, RUN */ #else { "spawn", XXNOTAV, CM_INV|CM_PSH }, /* Synonym for PUSH, RUN */ #endif /* NOPUSH */ #endif /* NOFRILLS */ #ifdef ANYSSH { "ssh", XXSSH, 0 }, #endif /* ANYSSH */ #ifndef NOXFER { "sta", XXSTA, CM_INV|CM_ABR }, { "stat", XXSTA, CM_INV|CM_ABR }, { "statistics", XXSTA, 0 }, /* Display file transfer stats */ #endif /* NOXFER */ { "status", XXSTATUS,0 }, /* Show status of previous command */ #ifndef NOSPL { "stop", XXSTO, 0 }, /* STOP all take files and macros */ { "succeed", XXSUCC, CM_INV }, /* SUCCEED */ #else { "stop", XXNOTAV, CM_INV }, { "succeed", XXNOTAV, CM_INV }, #endif /* NOSPL */ #ifndef NOFRILLS { "SUPPORT", XXBUG, 0 }, /* Tech support instructions */ #else { "support", XXNOTAV, CM_INV }, #endif /* NOFRILLS */ #ifndef NOJC { "suspend", XXSUS, CM_PSH }, /* SUSPEND C-Kermit (UNIX only) */ #else { "suspend", XXNOTAV, CM_INV|CM_PSH }, #endif /* NOJC */ #ifndef NOSPL { "switch", XXSWIT, 0 }, /* SWITCH */ #else { "switch", XXNOTAV, CM_INV }, #endif /* NOSPL */ #ifdef CK_TAPI { "ta", XXTAK, CM_INV|CM_ABR }, /* (because of TAPI) */ #endif /* CK_TAPI */ #ifndef NOFRILLS { "tail", XXTAIL, 0 }, /* Display end of a local file */ #endif /* NOFRILLS */ { "take", XXTAK, 0 }, /* TAKE commands from a file */ #ifdef CK_TAPI { "tapi", XXTAPI, CM_LOC }, /* Microsoft TAPI commands */ #else { "tapi", XXNOTAV, CM_INV|CM_LOC }, #endif /* CK_TAPI */ #ifndef NOFRILLS #ifdef TCPSOCKET { "tel", XXTEL, CM_INV|CM_ABR|CM_LOC }, { "telnet", XXTEL, CM_LOC }, /* TELNET (TCP/IP only) */ { "telopt", XXTELOP, CM_INV }, /* TELOPT (ditto) */ #else { "tel", XXNOTAV, CM_INV|CM_LOC }, { "telnet", XXNOTAV, CM_INV|CM_LOC }, { "telopt", XXNOTAV, CM_INV }, #endif /* TCPSOCKET */ #ifdef OS2 { "terminal", XXTERM, CM_INV|CM_LOC }, /* == SET TERMINAL TYPE */ #else { "terminal", XXTERM, CM_INV }, #endif /* OS2 */ #endif /* NOFRILLS */ #ifndef NOXFER { "text", XXASC, CM_INV }, /* == SET FILE TYPE TEXT */ #endif /* NOXFER */ { "touch", XXTOUC, 0 }, /* TOUCH */ #ifndef NOSPL { "trace", XXTRACE, 0 }, /* TRACE */ #else { "trace", XXNOTAV, CM_INV }, #endif /* NOSPL */ #ifndef NOCSETS { "translate", XXXLA, 0 }, /* TRANSLATE local file char sets */ #else { "translate", XXNOTAV, CM_INV }, #endif /* NOCSETS */ #ifndef NOXMIT { "transmit", XXTRA, 0 }, /* Send (upload) a file, no protocol */ #else { "transmit", XXNOTAV, CM_INV }, #endif /* NOXMIT */ #ifndef NOFRILLS { "type", XXTYP, 0 }, /* Display a local file */ #endif /* NOFRILLS */ #ifndef NOSPL { "undcl", XXUNDCL, CM_INV }, { "undeclare", XXUNDCL, 0 }, /* UNDECLARE an array */ { "undefine", XXUNDEF, 0 }, /* UNDEFINE a variable or macro */ #else { "undcl", XXNOTAV, CM_INV }, { "undeclare", XXNOTAV, CM_INV }, { "undefine", XXNOTAV, CM_INV }, #endif /* NOSPL */ #ifdef NEWFTP { "user", XXUSER, CM_INV }, /* (FTP) USER */ #endif /* NEWFTP */ { "version", XXVER, 0 }, /* VERSION-number display */ #ifdef OS2 { "viewonly", XXVIEW, CM_LOC }, /* VIEWONLY Terminal Mode */ #endif /* OS2 */ { "void", XXVOID, 0 }, /* VOID */ #ifndef NOSPL { "wait", XXWAI, 0 }, /* WAIT */ #else { "wait", XXNOTAV, CM_INV }, #endif /* NOSPL */ { "wdirectory", XXWDIR, CM_INV }, /* Like TOPS-20, reverse chron order */ { "wermit", XXKERMI, CM_INV }, #ifndef NOXFER { "where", XXWHERE, 0 }, /* WHERE (did my file go?) */ #endif /* NOXFER */ #ifndef NOSPL { "while", XXWHI, 0 }, /* WHILE loop */ #else { "while", XXNOTAV, CM_INV }, #endif /* NOSPL */ #ifndef OS2 #ifndef MAC #ifndef NOFRILLS { "who", XXWHO, CM_PSH }, /* WHO's logged in? */ #endif /* NOFRILLS */ #endif /* MAC */ #endif /* OS2 */ #ifndef NOHELP { "wildcards", XXWILD,CM_INV|CM_HLP }, /* Wildcard syntax */ #endif /* NOHELP */ #ifndef NOSPL { "wr", XXWRI, CM_INV|CM_ABR }, { "wri", XXWRI, CM_INV|CM_ABR }, { "writ", XXWRI, CM_INV|CM_ABR }, { "write", XXWRI, 0 }, /* WRITE characters to a file */ { "write-line", XXWRL, CM_INV }, /* WRITE a line to a file */ { "writeln", XXWRL, CM_INV }, /* Pascalisch synonym for write-line */ #else { "wr", XXNOTAV, CM_INV }, { "wri", XXNOTAV, CM_INV }, { "writ", XXNOTAV, CM_INV }, { "write", XXNOTAV, CM_INV }, { "write-line", XXNOTAV, CM_INV }, { "writeln", XXNOTAV, CM_INV }, #endif /* NOSPL */ #ifndef NOFRILLS { "xecho", XXXECH,0 }, /* XECHO */ #endif /* NOFRILLS */ #ifndef NOSPL { "xif", XXIFX, CM_INV }, /* Extended IF command (obsolete) */ #else { "xif", XXNOTAV, CM_INV }, #endif /* NOSPL */ #ifndef NOCSETS { "xlate", XXXLA, CM_INV }, /* Synonym for TRANSLATE */ #else { "xlate", XXNOTAV, CM_INV }, #endif /* NOCSETS */ #ifndef NOXMIT { "xm", XXTRA, CM_INV|CM_ABR }, /* Avoid conflict with XMESSAGE */ #else { "xm", XXNOTAV, CM_INV|CM_ABR }, /* Synonym for TRANSMIT */ #endif /* NOXMIT */ { "xmessage", XXXMSG, 0 }, /* Print debugging message */ #ifndef NOXMIT { "xmit", XXTRA, CM_INV }, /* Synonym for TRANSMIT */ #else { "xmit", XXNOTAV, CM_INV }, #endif /* NOXMIT */ #ifndef OS2 #ifndef NOJC { "z", XXSUS, CM_INV|CM_PSH }, /* Synonym for SUSPEND */ #else { "z", XXNOTAV, CM_INV|CM_PSH }, #endif /* NOJC */ #endif /* OS2 */ #ifndef NOSPL { "{", XXMACRO, CM_INV }, /* Immediate macro */ #endif /* NOSPL */ { "", 0, 0 } }; int ncmd = (sizeof(cmdtab) / sizeof(struct keytab)) - 1; /* NOTE: Tokens must also be entered above into cmdtab[]. */ char toktab[] = { #ifndef NOPUSH '!', /* Shell escape */ #endif /* NOPUSH */ '#', /* Comment */ #ifndef NOSPL '(', /* S-Expression */ '.', /* Assignment */ #endif /* NOSPL */ ';', /* Comment */ #ifndef NOSPL ':', /* Label */ #endif /* NOSPL */ #ifndef NOPUSH #ifdef CK_REDIR '<', /* REDIRECT */ #endif /* CK_REDIR */ '@', /* DCL escape */ #endif /* NOPUSH */ #ifdef CK_RECALL '^', /* Command recall */ #endif /* CK_RECALL */ #ifndef NOSPL '{', /* Immediate macro */ #endif /* NOSPL */ '\0' /* End of this string */ }; int xxdot = 0; /* Used with "." token */ struct keytab yesno[] = { /* Yes/No keyword table */ { "no", 0, 0 }, { "ok", 1, 0 }, { "yes", 1, 0 } }; int nyesno = (sizeof(yesno) / sizeof(struct keytab)); /* Save keyword table */ struct keytab savtab[] = { #ifdef OS2 { "command", XSCMD, 0 }, #else #ifdef CK_RECALL { "command", XSCMD, 0 }, #endif /* CK_RECALL */ #endif /* OS2 */ #ifndef NOSETKEY { "keymap", XSKEY, 0 }, #endif /* NOSETKEY */ #ifdef OS2 { "terminal", XSTERM, 0 }, #endif /* OS2 */ { "", 0, 0 } }; int nsav = (sizeof(savtab) / sizeof(struct keytab)) - 1; /* Parameter keyword table */ struct keytab prmtab[] = { { "alarm", XYALRM, 0 }, #ifdef COMMENT /* SET ANSWER not implemented yet */ #ifndef NODIAL { "answer", XYANSWER,0 }, #endif /* NODIAL */ #endif /* COMMENT */ { "ask-timer", XYTIMER, 0 }, #ifndef NOXFER { "attributes", XYATTR, 0 }, #endif /* NOXFER */ #ifdef CK_AUTHENTICATION { "authentication", XYAUTH, 0 }, #else /* CK_AUTHENTICATION */ #ifdef CK_SSL { "authentication", XYAUTH, 0 }, #endif /* CK_SSL */ #endif /* CK_AUTHENTICATION */ { "b", XYBACK, CM_INV|CM_ABR|CM_PSH }, { "ba", XYBACK, CM_INV|CM_ABR|CM_PSH }, #ifdef VMS { "background", XYBACK, CM_INV|CM_PSH }, { "batch", XYBACK, CM_PSH }, #else { "background", XYBACK, CM_PSH }, { "batch", XYBACK, CM_INV|CM_PSH }, #endif /* VMS */ #ifndef NOLOCAL { "baud", XYSPEE, CM_INV|CM_LOC }, #endif /* NOLOCAL */ { "bell", XYBELL, 0 }, #ifndef NOXFER { "block-check", XYCHKT, 0 }, #endif /* NOXFER */ #ifdef OS2 #ifdef BPRINT { "bprinter", XYBDCP, CM_INV }, #endif /* BPRINT */ #endif /* OS2 */ #ifdef BROWSER { "browser", XYBROWSE,CM_PSH|CM_LOC }, #endif /* BROWSER */ #ifndef NOXFER #ifdef DYNAMIC { "buffers", XYBUF, 0 }, #endif /* DYNAMIC */ #endif /* NOXFER */ #ifndef NOLOCAL #ifndef MAC { "carrier-watch", XYCARR, CM_LOC }, #endif /* MAC */ #endif /* NOLOCAL */ #ifndef NOSPL { "case", XYCASE, 0 }, #endif /* NOSPL */ { "cd", XYCD, 0 }, #ifndef NOXFER { "cl", XYCLEAR, CM_INV|CM_ABR }, { "cle", XYCLEAR, CM_INV|CM_ABR }, { "clea", XYCLEAR, CM_INV|CM_ABR }, { "clear", XYCLEAR, CM_INV|CM_ABR }, { "clear-channel", XYCLEAR, 0 }, { "clearchannel", XYCLEAR, CM_INV }, #endif /* NOXFER */ #ifndef NOLOCAL { "close-on-disconnect", XYDISC, CM_INV|CM_LOC }, #endif /* NOLOCAL */ { "cmd", XYCMD, CM_INV }, { "command", XYCMD, 0 }, #ifdef CK_SPEED { "con", XYQCTL, CM_INV|CM_ABR }, #endif /* CK_SPEED */ { "console", XYCMD, CM_INV }, #ifdef CK_SPEED { "control-character",XYQCTL, 0 }, #endif /* CK_SPEED */ #ifndef NOSPL { "count", XYCOUN, 0 }, #endif /* NOSPL */ #ifndef NOXFER { "d", XYDELA, CM_INV|CM_ABR }, { "de", XYDELA, CM_INV|CM_ABR }, #endif /* NOXFER */ { "debug", XYDEBU, 0 }, #ifdef VMS { "default", XYDFLT, 0 }, #else #ifndef MAC { "default", XYDFLT, CM_INV }, #endif /* MAC */ #endif /* VMS */ #ifndef NOXFER { "delay", XYDELA, 0 }, { "destination", XYDEST, 0 }, #endif /* NOXFER */ #ifndef NODIAL { "di", XYDIAL, CM_INV|CM_ABR|CM_LOC }, { "dia", XYDIAL, CM_INV|CM_ABR|CM_LOC }, { "dial", XYDIAL, CM_LOC }, #endif /* NODIAL */ #ifdef OS2 { "dialer", XYDLR, CM_INV }, #endif /* OS2 */ #ifndef NOLOCAL { "disconnect", XYDISC, CM_LOC }, { "duplex", XYDUPL, CM_LOC }, #endif /* NOLOCAL */ #ifndef NOPUSH #ifndef NOFRILLS { "editor", XYEDIT, CM_PSH }, #endif /* NOFRILLS */ #endif /* NOPUSH */ #ifdef CK_CTRLZ { "eof", XYEOF, CM_INV }, #endif /* CK_CTRLZ */ #ifndef NOLOCAL { "escape-character", XYESC, CM_LOC }, #endif /* NOLOCAL */ #ifndef NOSPL { "evaluate", XYEVAL, CM_INV }, #endif /* NOSPL */ { "exit", XYEXIT, 0 }, #ifndef NOXFER #ifdef CK_XYZ #ifndef NOPUSH #ifndef XYZ_INTERNAL { "external-protocol",XYEXTRN, 0 }, #endif /* XYZ_INTERNAL */ #endif /* NOPUSH */ #endif /* CK_XYZ */ { "f-ack-bug", XYFACKB, CM_INV }, { "f-ack-path", XYFACKP, CM_INV }, #endif /* NOXFER */ { "file", XYFILE, 0 }, { "fl", XYFLOW, CM_INV|CM_ABR }, #ifndef NOSPL { "flag", XYFLAG, 0 }, #endif /* NOSPL */ #ifdef TCPSOCKET #ifndef SYSFTP #ifndef NOFTP { "ft", XYFTPX, CM_INV|CM_ABR }, { "ftp", XYFTPX, 0 }, #endif /* NOFTP */ #endif /* SYSFTP */ #endif /* TCPSOCKET */ #ifdef BROWSER { "ftp-client", XYFTP, CM_PSH }, #endif /* BROWSER */ { "flow-control", XYFLOW, 0 }, #ifndef NOSPL { "function", XYFUNC, 0 }, #endif /* NOSPL */ #ifdef NEWFTP { "get-put-remote", XYGPR, 0 }, #endif /* NEWFTP */ #ifdef KUI { "gui", XYGUI, 0 }, #endif /* KUI */ { "handshake", XYHAND, 0 }, { "hints", XYHINTS, 0 }, #ifdef NETCONN { "host", XYHOST, CM_LOC }, #endif /* NETCONN */ #ifndef NOSPL { "i", XYINPU, CM_INV|CM_ABR }, #endif /* NOSPL */ #ifdef IKSD { "iks", XYIKS, 0 }, #else { "iks", XYIKS, CM_INV }, #endif /* IKSD */ #ifndef NOSPL { "in", XYINPU, CM_INV|CM_ABR }, #endif /* NOSPL */ #ifndef NOXFER { "incomplete", XYIFD, CM_INV }, #endif /* NOXFER */ #ifndef NOSPL { "input", XYINPU, 0 }, #endif /* NOSPL */ #ifndef NOSETKEY { "key", XYKEY, 0 }, #endif /* NOSETKEY */ { "l", XYLINE, CM_INV|CM_ABR }, #ifndef NOCSETS { "language", XYLANG, 0 }, #endif /* NOCSETS */ #ifndef NOLOCAL { "line", XYLINE, CM_LOC }, { "local-echo", XYLCLE, CM_INV|CM_LOC }, #endif /* NOLOCAL */ #ifdef LOCUS { "locus", XYLOCUS, 0 }, #endif /* LOCUS */ #ifndef NOSPL { "login", XYLOGIN, CM_LOC }, #endif /* NOSPL */ #ifndef NOSPL { "macro", XYMACR, 0 }, #endif /* NOSPL */ { "match", XYMATCH, 0 }, #ifdef COMMENT #ifdef VMS { "messages", XYMSGS, 0 }, #endif /* VMS */ #endif /* COMMENT */ #ifndef NODIAL { "modem", XYMODM, CM_LOC }, #endif /* NODIAL */ #ifndef NOLOCAL #ifdef OS2MOUSE { "mouse", XYMOUSE, 0 }, #endif /* OS2MOUSE */ #endif /* NOLOCAL */ #ifdef OS2 { "mskermit", XYMSK, 0 }, #endif /* OS2 */ #ifdef NETCONN { "network", XYNET, CM_LOC }, #endif /* NETCONN */ #ifndef NOSPL { "output", XYOUTP, 0 }, #endif /* NOSPL */ { "options", XYOPTS, 0 }, { "pause", XYSLEEP, CM_INV }, #ifdef ANYX25 #ifndef IBMX25 { "pad", XYPAD, CM_LOC }, #endif /* IBMX25 */ #endif /* ANYX25 */ { "parity", XYPARI, 0 }, #ifndef NOLOCAL #ifdef OS2 { "port", XYLINE, CM_LOC }, #else { "port", XYLINE, CM_INV|CM_LOC }, #endif /* OS2 */ #endif /* NOLOCAL */ #ifndef NOFRILLS { "pr", XYPROM, CM_INV|CM_ABR }, { "printer", XYPRTR, 0 }, #endif /* NOFRILLS */ #ifdef OS2 { "priority", XYPRTY, 0 }, #endif /* OS2 */ #ifdef CK_SPEED { "prefixing", XYPREFIX, 0 }, #endif /* CK_SPEED */ #ifndef NOFRILLS { "prompt", XYPROM, 0 }, #endif /* NOFRILLS */ #ifndef NOXFER { "protocol", XYPROTO, 0 }, #endif /* NOXFER */ { "q", XYQUIE, CM_INV|CM_ABR }, #ifndef NOXFER { "q8flag", XYQ8FLG, CM_INV }, #endif /* NOXFER */ #ifdef QNX { "qnx-port-lock", XYQNXPL, 0 }, #else { "qnx-port-lock", XYQNXPL, CM_INV }, #endif /* QNX */ { "quiet", XYQUIE, 0 }, #ifndef NOXFER { "rec", XYRECV, CM_INV|CM_ABR }, { "receive", XYRECV, 0 }, { "recv", XYRECV, CM_INV }, #endif /* NOXFER */ { "reliable", XYRELY, 0 }, { "rename", XY_REN, 0 }, #ifndef NOXFER { "repeat", XYREPT, 0 }, { "retry-limit", XYRETR, 0 }, #endif /* NOXFER */ #ifdef CKROOT { "root", XYROOT, 0 }, #endif /* CKROOT */ #ifndef NOSCRIPT { "script", XYSCRI, CM_LOC }, #endif /* NOSCRIPT */ #ifndef NOXFER { "send", XYSEND, 0 }, #ifndef NOLOCAL #ifndef NOSERVER { "ser", XYSERV, CM_INV|CM_ABR }, #endif /* NOSERVER */ #endif /* NOXFER */ { "serial", XYSERIAL,CM_LOC }, #endif /* NOLOCAL */ #ifndef NOSERVER { "server", XYSERV, 0 }, #endif /* NOSERVER */ #ifdef SESLIMIT #ifndef NOLOCAL { "session-l", XYSESS, CM_INV|CM_ABR }, #endif /* NOLOCAL */ { "session-limit", XYLIMIT, CM_INV|CM_LOC }, /* Session Limit */ #endif /* SESLIMIT */ #ifndef NOLOCAL { "session-log", XYSESS, CM_LOC }, #endif /* NOLOCAL */ #ifndef NOSPL #ifndef NOSEXP { "sexpression", XYSEXP, CM_INV }, #endif /* NOSEXP */ #endif /* NOSPL */ { "sleep", XYSLEEP, 0 }, #ifndef NOLOCAL { "speed", XYSPEE, CM_LOC }, #endif /* NOLOCAL */ #ifdef ANYSSH { "ssh", XYSSH, 0 }, #endif /* ANYSSH */ #ifndef NOSPL { "startup-file", XYSTARTUP, CM_INV }, #endif /* NOSPL */ #ifndef NOLOCAL #ifdef HWPARITY { "stop-bits", XYSTOP, CM_LOC }, #else #ifdef TN_COMPORT { "stop-bits", XYSTOP, CM_LOC }, #endif /* TN_COMPORT */ #endif /* HWPARITY */ #endif /* NOLOCAL */ #ifndef NOXFER #ifdef STREAMING { "streaming", XYSTREAM, 0 }, #endif /* STREAMING */ #endif /* NOXFER */ #ifndef NOJC { "suspend", XYSUSP, CM_PSH }, #endif /* NOJC */ #ifdef CKSYSLOG { "syslog", XYSYSL, CM_INV }, #endif /* CKSYSLOG */ { "take", XYTAKE, 0 }, #ifdef CK_TAPI { "tapi", XYTAPI, CM_LOC }, #endif /* CK_TAPI */ #ifndef NOTCPOPTS #ifdef TCPSOCKET { "tcp", XYTCP, CM_LOC }, #endif /* TCPSOCKET */ #endif /* NOTCPOPTS */ #ifdef TNCODE { "tel", XYTEL, CM_INV|CM_ABR }, { "telnet", XYTEL, 0 }, { "telopt", XYTELOP, 0 }, #endif /* TNCODE */ #ifndef NOSPL { "temp-directory", XYTMPDIR,0 }, #endif /* NOSPL */ #ifndef NOLOCAL { "terminal", XYTERM, CM_LOC }, #endif /* NOLOCAL */ #ifdef OS2 { "title", XYTITLE, CM_LOC }, #endif /* OS2 */ #ifdef TLOG { "transaction-log", XYTLOG, 0 }, #endif /* TLOG */ #ifndef NOXFER { "transfer", XYXFER, 0 }, #endif /* NOXFER */ #ifndef NOXMIT { "transmit", XYXMIT, 0 }, #endif /* NOXMIT */ #ifndef NOXFER #ifndef NOCSETS { "unknown-char-set", XYUNCS, 0 }, #endif /* NOCSETS */ #ifndef NOSPL { "variable-evaluation", XYVAREV, CM_INV }, #endif /* NOSPL */ #endif /* NOXFER */ { "wait", XYSLEEP, CM_INV }, #ifndef NOPUSH #ifdef UNIX { "wildcard-expansion", XYWILD, 0 }, #endif /* UNIX */ #endif /* NOPUSH */ #ifdef NT { "w", XYWIND, CM_INV|CM_ABR }, { "wi", XYWIND, CM_INV|CM_ABR }, { "win", XYWIND, CM_INV|CM_ABR }, #endif /* NT */ { "window-size", XYWIND, 0 }, #ifdef NT { "win95", XYWIN95, 0 }, #endif /* NT */ #ifdef ANYX25 { "x.25", XYX25, CM_LOC }, { "x25", XYX25, CM_INV|CM_LOC }, #endif /* ANYX25 */ { "xfer", XYXFER, CM_INV }, #ifndef NOXMIT { "xmit", XYXMIT, CM_INV }, #endif /* NOXMIT */ { "", 0, 0 } }; int nprm = (sizeof(prmtab) / sizeof(struct keytab)) - 1; /* How many */ struct keytab scntab[] = { /* Screen commands */ { "clear", SCN_CLR, 0 }, { "cleol", SCN_CLE, 0 }, { "move-to", SCN_MOV, 0 } }; int nscntab = (sizeof(scntab) / sizeof(struct keytab)); /* How many */ #ifdef ANYSSH /* SSH command table */ #ifdef SSHBUILTIN int ssh_pf_lcl_n = 0, ssh_pf_rmt_n = 0; struct ssh_pf ssh_pf_lcl[32] = { 0, NULL, 0 }; /* SSH Port Forwarding */ struct ssh_pf ssh_pf_rmt[32] = { 0, NULL, 0 }; /* structs... */ extern char * ssh_hst, * ssh_cmd, * ssh_prt; extern int ssh_ver, ssh_xfw; char * ssh_tmpuid = NULL, *ssh_tmpcmd = NULL, *ssh_tmpport = NULL, * ssh_tmpstr = NULL; int sshk_type = SSHKT_2D, /* SSH KEY CREATE /TYPE:x */ sshk_bits = 1024, /* SSH KEY CREATE /BITS:n */ sshk_din = SKDF_OSSH, /* SSH KEY DISPLAY /IN-FORMAT: */ sshk_dout = SKDF_OSSH; /* SSH KEY DISPLAY /OUT-FORMAT: */ char * sshk1_comment = NULL, /* SSH V1 COMMENT */ * sshkp_old = NULL, /* Old key passphrase */ * sshkp_new = NULL, /* New key passphrase */ * sshkc_pass = NULL, /* KEY CREATE /PASS:xxx */ * sshkc_comm = NULL, /* KEY CREATE /V1-RSA-COMMENT:xxx */ * sshd_file = NULL, /* DISPLAY file */ * sshk_file = NULL; /* SSH CREATE KEY file */ static struct keytab sshclr[] = { { "local-port-forward", SSHC_LPF, 0 }, { "remote-port-forward", SSHC_RPF, 0 }, { "", 0, 0 } }; static int nsshclr = (sizeof(sshclr) / sizeof(struct keytab)) - 1; struct keytab sshopnsw[] = { { "/command", SSHSW_CMD, CM_ARG }, { "/password", SSHSW_PWD, CM_ARG }, { "/subsystem", SSHSW_SUB, CM_ARG }, { "/user", SSHSW_USR, CM_ARG }, { "/version", SSHSW_VER, CM_ARG }, { "/x11-forwarding", SSHSW_X11, CM_ARG }, { "", 0, 0 } }; int nsshopnsw = (sizeof(sshopnsw) / sizeof(struct keytab)) - 1; static struct keytab sshkwtab[] = { { "add", XSSH_ADD, 0 }, { "agent", XSSH_AGT, 0 }, { "clear", XSSH_CLR, 0 }, { "forward-local-port", XSSH_FLP, CM_INV }, { "forward-remote-port", XSSH_FRP, CM_INV }, { "key", XSSH_KEY, 0 }, { "open", XSSH_OPN, 0 }, { "v2", XSSH_V2, 0 }, { "", 0, 0 } }; static int nsshcmd = (sizeof(sshkwtab) / sizeof(struct keytab)) - 1; static struct keytab ssh2tab[] = { { "rekey", XSSH2_RKE, 0 }, { "", 0, 0 } }; static int nssh2tab = (sizeof(ssh2tab) / sizeof(struct keytab)); static struct keytab addfwd[] = { /* SET SSH ADD command table */ { "local-port-forward", SSHF_LCL, 0 }, { "remote-port-forward", SSHF_RMT, 0 }, { "", 0, 0 } }; static int naddfwd = (sizeof(addfwd) / sizeof(struct keytab)) - 1; static struct keytab sshagent[] = { /* SET SSH AGENT command table */ { "add", SSHA_ADD, 0 }, { "delete", SSHA_DEL, 0 }, { "list", SSHA_LST, 0 }, { "", 0, 0 } }; static int nsshagent = (sizeof(sshagent) / sizeof(struct keytab)) - 1; static struct keytab sshagtsw[] = { /* SET SSH AGENT LIST switch table */ { "/fingerprint", SSHASW_FP, 0 }, { "", 0, 0 } }; static int nsshagtsw = (sizeof(sshagtsw) / sizeof(struct keytab)) - 1; static struct keytab sshkey[] = { /* SET SSH KEY command table */ { "change-passphrase", SSHK_PASS, 0 }, { "create", SSHK_CREA, 0 }, { "display", SSHK_DISP, 0 }, { "v1", SSHK_V1, 0 }, { "", 0, 0 } }; static int nsshkey = (sizeof(sshkey) / sizeof(struct keytab)) - 1; static struct keytab sshkv1[] = { /* SET SSH KEY V1 command table */ { "set-comment", 1, 0 } }; static struct keytab sshkpsw[] = { /* SET SSH KEY PASSPHRASE table */ { "/new-passphrase", 2, CM_ARG }, { "/old-passphrase", 1, CM_ARG } }; static struct keytab sshkcrea[] = { /* SSH KEY CREATE table */ { "/bits", SSHKC_BI, CM_ARG }, { "/passphrase", SSHKC_PP, CM_ARG }, { "/type", SSHKC_TY, CM_ARG }, { "/v1-rsa-comment", SSHKC_1R, CM_ARG } }; static int nsshkcrea = (sizeof(sshkcrea) / sizeof(struct keytab)); static struct keytab sshkcty[] = { /* SSH KEY CREATE /TYPE:xxx */ { "srp", SSHKT_SRP, 0 }, { "v1-rsa", SSHKT_1R, 0 }, { "v2-dsa", SSHKT_2D, 0 }, { "v2-rsa", SSHKT_2R, 0 } }; static int nsshkcty = (sizeof(sshkcty) / sizeof(struct keytab)); static struct keytab sshdswi[] = { /* SET SSH KEY DISPLAY /switches */ { "/format", SSHKD_OUT, CM_ARG } }; static int nsshdswi = (sizeof(sshdswi) / sizeof(struct keytab)); #ifdef COMMENT static struct keytab sshdifmt[] = { /* SSH KEY DISPLAY /IN-FORMAT: */ { "openssh", SKDF_OSSH, 0 }, { "ssh.com", SKDF_SSHC, 0 } }; static int nsshdifmt = (sizeof(sshdifmt) / sizeof(struct keytab)); #endif /* COMMENT */ static struct keytab sshdofmt[] = { /* SSH KEY DISPLAY /IN-FORMAT: */ { "fingerprint", SKDF_FING, 0 }, { "ietf", SKDF_IETF, 0 }, { "openssh", SKDF_OSSH, 0 }, { "ssh.com", SKDF_SSHC, 0 } }; static int nsshdofmt = (sizeof(sshdofmt) / sizeof(struct keytab)); static struct keytab sshkermit[] = { /* SKERMIT */ { "open", SKRM_OPN, 0 } }; static int nsshkermit = (sizeof(sshkermit) / sizeof(struct keytab)); struct keytab sshkrmopnsw[] = { { "/password", SSHSW_PWD, CM_ARG }, { "/user", SSHSW_USR, CM_ARG }, { "/version", SSHSW_VER, CM_ARG }, { "", 0, 0 } }; int nsshkrmopnsw = (sizeof(sshkrmopnsw) / sizeof(struct keytab)) - 1; #endif /* SSHBUILTIN */ #ifdef SFTP_BUILTIN static struct keytab sftpkwtab[] = { /* SFTP */ { "cd", SFTP_CD, 0 }, { "chgrp", SFTP_CHGRP, 0 }, { "chmod", SFTP_CHMOD, 0 }, { "chown", SFTP_CHOWN, 0 }, { "delete", SFTP_RM, 0 }, { "dir", SFTP_DIR, 0 }, { "get", SFTP_GET, 0 }, { "mkdir", SFTP_MKDIR, 0 }, { "open", SFTP_OPN, 0 }, { "put", SFTP_PUT, 0 }, { "pwd", SFTP_PWD, 0 }, { "rename", SFTP_REN, 0 }, { "rm", SFTP_RM, CM_INV }, { "rmdir", SFTP_RMDIR, 0 }, { "symlink", SFTP_LINK, 0 }, { "version", SFTP_VER, 0 } }; static int nsftpkwtab = (sizeof(sftpkwtab) / sizeof(struct keytab)); #endif /* SFTP_BUILTIN */ #endif /* ANYSSH */ #ifdef NETCONN struct keytab netkey[] = { /* SET NETWORK table */ { "directory", XYNET_D, 0 }, { "type", XYNET_T, 0 } }; int nnetkey = (sizeof(netkey) / sizeof(struct keytab)); struct keytab netcmd[] = { /* These are the network types. */ #ifdef NETCMD { "command", NET_CMD, CM_INV }, /* Command */ #endif /* NETCMD */ #ifdef DECNET /* DECnet / PATHWORKS */ { "decnet", NET_DEC, 0 }, #endif /* DECNET */ #ifdef NETDLL { "dll", NET_DLL, CM_INV }, /* DLL to be loaded */ #endif /* NETDLL */ #ifdef NETFILE { "file", NET_FILE, CM_INV }, /* FILE (real crude) */ #endif /* NETFILE */ #ifdef NPIPE /* Named Pipes */ { "named-pipe", NET_PIPE, 0 }, #endif /* NPIPE */ #ifdef CK_NETBIOS { "netbios", NET_BIOS, 0 }, /* NETBIOS */ #endif /* CK_NETBIOS */ #ifdef DECNET /* DECnet / PATHWORKS (alias) */ { "pathworks", NET_DEC, CM_INV }, #endif /* DECNET */ #ifdef NETCMD { "pipe", NET_CMD, 0 }, /* Pipe */ #endif /* NETCMD */ #ifdef NETPTY { "pseudoterminal",NET_PTY, 0 }, /* Pseudoterminal */ #endif /* NETPTY */ #ifdef NETPTY { "pty", NET_PTY, CM_INV }, /* Inv syn for pseudoterm */ #endif /* NETPTY */ #ifdef SSHBUILTIN { "ssh", NET_SSH, 0 }, #endif /* SSHBUILTIN */ #ifdef SUPERLAT { "superlat", NET_SLAT, 0 }, /* Meridian Technologies' SuperLAT */ #endif /* SUPERLAT */ #ifdef TCPSOCKET /* TCP/IP sockets library */ { "tcp/ip", NET_TCPB, 0 }, #endif /* TCPSOCKET */ #ifdef SUPERLAT { "tes32", NET_SLAT, 0 }, /* Emulux TES32 */ #endif /* SUPERLAT */ #ifdef ANYX25 /* X.25 */ #ifdef SUNX25 { "x", NET_SX25, CM_INV|CM_ABR }, { "x.25", NET_SX25, 0 }, { "x25", NET_SX25, CM_INV }, #else #ifdef STRATUSX25 { "x", NET_VX25, CM_INV|CM_ABR }, { "x.25", NET_VX25, 0 }, { "x25", NET_VX25, CM_INV }, #endif /* STRATUSX25 */ #endif /* SUNX25 */ #ifdef IBMX25 { "x", NET_IX25, CM_INV|CM_ABR }, { "x.25", NET_IX25, CM_INV }, { "x25", NET_IX25, CM_INV }, #endif /* IBMX25 */ #ifdef HPX25 { "x", NET_IX25, CM_INV|CM_ABR }, { "x.25", NET_IX25, 0 }, { "x25", NET_IX25, CM_INV }, #endif /* HPX25 */ #endif /* ANYX25 */ { "", 0, 0 } }; int nnets = (sizeof(netcmd) / sizeof(struct keytab)); #ifndef NOTCPOPTS #ifdef TCPSOCKET /* TCP options */ struct keytab tcpopt[] = { { "address", XYTCP_ADDRESS, 0 }, #ifdef CK_DNS_SRV { "dns-service-records", XYTCP_DNS_SRV, 0 }, #endif /* CK_DNS_SRV */ #ifdef SO_DONTROUTE { "dontroute", XYTCP_DONTROUTE, 0 }, #endif /* SO_DONTROUTE */ #ifndef NOHTTP { "http-proxy", XYTCP_HTTP_PROXY, 0 }, #endif /* NOHTTP */ #ifdef SO_KEEPALIVE { "keepalive", XYTCP_KEEPALIVE, 0 }, #endif /* SO_KEEPALIVE */ #ifdef SO_LINGER { "linger", XYTCP_LINGER, 0 }, #endif /* SO_LINGER */ #ifdef TCP_NODELAY { "nagle", XYTCP_NAGLE, CM_INV }, { "nodelay", XYTCP_NODELAY, 0 }, #endif /* TCP_NODELAY */ { "reverse-dns-lookup", XYTCP_RDNS, 0 }, #ifdef SO_RCVBUF { "recvbuf", XYTCP_RECVBUF, 0 }, #endif /* SO_RCVBUF */ #ifdef SO_SNDBUF { "sendbuf", XYTCP_SENDBUF, 0 }, #endif /* SO_SNDBUF */ #ifdef NT #ifdef CK_SOCKS { "socks-server", XYTCP_SOCKS_SVR, 0 }, #endif /* CK_SOCKS */ #endif /* NT */ #ifdef VMS #ifdef DEC_TCPIP { "ucx-port-bug", XYTCP_UCX, 0 }, #endif /* DEC_TCPIP */ #endif /* VMS */ { "",0,0 } }; int ntcpopt = (sizeof(tcpopt) / sizeof(struct keytab)); #endif /* TCPSOCKET */ #endif /* NOTCPOPTS */ #endif /* NETCONN */ #ifdef OS2 /* K95 Manual Chapter Table -- Keep these two tables in sync! */ static char * linktbl[] = { /* Internal links in k95.htm */ "#top", /* 00 */ "#what", /* 01 */ "#install", /* 02 */ "#start", /* 03 */ "#dialer", /* 04 */ "#entries", /* 05 */ "#command", /* 06 */ "#terminal", /* 07 */ "#transfer", /* 08 */ "#hostmode" /* 09 */ }; static struct keytab chaptbl[] = { { "Command-Screen", 6, 0 }, { "Contents", 0, 0 }, { "Dialer-Entries", 5, 0 }, { "File-Transfer", 8, 0 }, { "Getting-Started", 3, 0 }, { "Host-Mode", 9, 0 }, { "Installation", 2, 0 }, { "Terminal-Emulation", 7, 0 }, { "Using-The-Dialer", 4, 0 }, { "What-Is-K95", 1, 0 }, { "", 0, 0 } }; static int nchaptbl = (sizeof(chaptbl) / sizeof(struct keytab) - 1); #endif /* OS2 */ #ifndef NOXFER /* Remote Command Table */ struct keytab remcmd[] = { #ifndef NOSPL { "as", XZASG, CM_INV|CM_ABR }, { "asg", XZASG, CM_INV }, { "assign", XZASG, 0 }, #endif /* NOSPL */ { "cd", XZCWD, 0 }, { "cdup", XZCDU, CM_INV }, { "copy", XZCPY, 0 }, { "cwd", XZCWD, CM_INV }, { "delete", XZDEL, 0 }, { "directory", XZDIR, 0 }, { "e", XZXIT, CM_ABR|CM_INV }, { "erase", XZDEL, CM_INV }, { "exit", XZXIT, 0 }, { "help", XZHLP, 0 }, #ifndef NOPUSH { "host", XZHOS, 0 }, #endif /* NOPUSH */ #ifndef NOFRILLS { "kermit", XZKER, 0 }, { "l", XZLGI, CM_ABR|CM_INV }, { "lo", XZLGI, CM_ABR|CM_INV }, { "log", XZLGI, CM_ABR|CM_INV }, { "login", XZLGI, 0 }, { "logout", XZLGO, 0 }, { "message", XZMSG, 0 }, { "mkdir", XZMKD, 0 }, { "print", XZPRI, 0 }, #endif /* NOFRILLS */ { "pwd", XZPWD, 0 }, #ifndef NOSPL { "query", XZQUE, 0 }, #endif /* NOSPL */ { "rename", XZREN, 0 }, { "rmdir", XZRMD, 0 }, { "set", XZSET, 0 }, { "space", XZSPA, 0 }, #ifndef NOFRILLS { "type", XZTYP, 0 }, { "who", XZWHO, 0 }, #endif /* NOFRILLS */ { "", 0, 0} }; int nrmt = (sizeof(remcmd) / sizeof(struct keytab)) - 1; #endif /* NOXFER */ struct keytab logtab[] = { #ifdef CKLOGDIAL { "connections", LOGM, CM_INV }, { "cx", LOGM, 0 }, #endif /* CKLOGDIAL */ #ifdef DEBUG { "debugging", LOGD, 0 }, #endif /* DEBUG */ { "packets", LOGP, 0 }, #ifndef NOLOCAL { "session", LOGS, 0 }, #endif /* NOLOCAL */ #ifdef TLOG { "transactions", LOGT, 0 }, #endif /* TLOG */ { "", 0, 0 } }; int nlog = (sizeof(logtab) / sizeof(struct keytab)) - 1; struct keytab writab[] = { #ifndef NOSPL { "append-file", LOGW, CM_INV }, #endif /* NOSPL */ { "debug-log", LOGD, 0 }, { "error", LOGE, 0 }, #ifndef NOSPL { "file", LOGW, 0 }, #endif /* NOSPL */ { "packet-log", LOGP, 0 }, { "screen", LOGX, 0 }, #ifndef NOLOCAL { "session-log", LOGS, 0 }, #endif /* NOLOCAL */ { "sys$output", LOGX, CM_INV }, { "t", LOGT, CM_ABR|CM_INV }, /* Because of a typo in */ { "tr", LOGT, CM_ABR|CM_INV }, /* the book... */ { "tra", LOGT, CM_ABR|CM_INV }, { "tran", LOGT, CM_ABR|CM_INV }, { "trans", LOGT, CM_ABR|CM_INV }, { "transa", LOGT, CM_ABR|CM_INV }, { "transac", LOGT, CM_ABR|CM_INV }, { "transact", LOGT, CM_ABR|CM_INV }, { "transacti", LOGT, CM_ABR|CM_INV }, { "transactio", LOGT, CM_ABR|CM_INV }, { "transaction", LOGT, CM_ABR|CM_INV }, { "transaction-log", LOGT, 0 }, { "transactions", LOGT, CM_INV } }; int nwri = (sizeof(writab) / sizeof(struct keytab)); static struct keytab clrtab[] = { /* Keywords for CLEAR command */ #ifndef NOSPL { "alarm", CLR_ALR, 0 }, #ifdef CK_APC { "apc", CLR_APC, 0 }, #endif /* CK_APC */ #ifdef PATTERNS { "binary-patterns", CLR_BIN, 0 }, #endif /* PATTERNS */ { "both", CLR_DEV|CLR_INP, CM_INV }, #endif /* NOSPL */ #ifdef OS2 { "command-screen", CLR_CMD, 0 }, #endif /* OS2 */ #ifndef NOSPL { "device", CLR_DEV, CM_INV|CM_ABR }, { "device-and-input", CLR_DEV|CLR_INP, 0 }, #endif /* NOSPL */ { "device-buffer", CLR_DEV, 0 }, #ifndef NODIAL { "dial-status", CLR_DIA, 0 }, #endif /* NODIAL */ #ifndef NOSPL { "input-buffer", CLR_INP, 0 }, #endif /* NOSPL */ { "keyboard-buffer", CLR_KBD, 0 }, { "send-list", CLR_SFL, 0 }, #ifdef OS2 { "scr", CLR_SCL, CM_INV|CM_ABR }, #endif /* OS2 */ { "screen", CLR_SCR, 0 }, #ifdef OS2 { "scrollback", CLR_SCL, CM_INV }, { "terminal-screen", CLR_TRM, 0 }, #endif /* OS2 */ #ifdef PATTERNS { "text-patterns", CLR_TXT, 0 }, #endif /* PATTERNS */ { "", 0, 0 } }; int nclear = (sizeof(clrtab) / sizeof(struct keytab)) - 1; struct keytab clstab[] = { /* Keywords for CLOSE command */ #ifndef NOSPL { "!read", LOGR, CM_INV }, { "!write", LOGW, CM_INV }, #ifndef NOPUSH #endif /* NOPUSH */ #endif /* NOSPL */ #ifndef NOSPL { "append-file", LOGW, CM_INV }, #endif /* NOSPL */ #ifndef NOLOCAL { "connection", 9999, 0 }, #endif /* NOLOCAL */ #ifdef CKLOGDIAL { "cx-log", LOGM, 0 }, #endif /* CKLOGDIAL */ #ifdef DEBUG { "debug-log", LOGD, 0 }, #endif /* DEBUG */ { "host", 9999, CM_INV }, /* Synonym for CLOSE CONNECTION */ { "line", 9999, CM_INV }, /* Synonym for CLOSE CONNECTION */ { "p", LOGP, CM_INV|CM_ABR }, { "packet-log", LOGP, 0 }, { "port", 9999, CM_INV }, /* Synonym for CLOSE CONNECTION */ #ifndef NOSPL { "read-file", LOGR, 0 }, #endif /* NOSPL */ #ifndef NOLOCAL { "session-log", LOGS, 0 }, #endif /* NOLOCAL */ #ifdef TLOG { "t", LOGT, CM_ABR|CM_INV }, /* Because of a typo in */ { "tr", LOGT, CM_ABR|CM_INV }, /* the book... */ { "tra", LOGT, CM_ABR|CM_INV }, { "tran", LOGT, CM_ABR|CM_INV }, { "trans", LOGT, CM_ABR|CM_INV }, { "transa", LOGT, CM_ABR|CM_INV }, { "transac", LOGT, CM_ABR|CM_INV }, { "transact", LOGT, CM_ABR|CM_INV }, { "transacti", LOGT, CM_ABR|CM_INV }, { "transactio", LOGT, CM_ABR|CM_INV }, { "transaction", LOGT, CM_ABR|CM_INV }, { "transaction-log", LOGT, 0 }, { "transactions", LOGT, CM_INV }, #endif /* TLOG */ #ifndef NOSPL { "write-file", LOGW, 0 }, #endif /* NOSPL */ { "", 0, 0 } }; int ncls = (sizeof(clstab) / sizeof(struct keytab)) - 1; /* SHOW command arguments */ #ifndef NOSHOW struct keytab shotab[] = { #ifndef NOSPL { "alarm", SHALRM, 0 }, { "arg", SHARG, CM_INV|CM_ABR }, { "arguments", SHARG, 0 }, { "args", SHARG, CM_INV }, { "arrays", SHARR, 0 }, #endif /* NOSPL */ #ifndef NOCSETS { "associations", SHASSOC, 0 }, #endif /* NOCSETS */ #ifndef NOXFER { "attributes", SHATT, 0 }, #endif /* NOXFER */ #ifdef CK_AUTHENTICATION { "authentication", SHOAUTH, CM_INV }, #endif /* CK_AUTHENTICATION */ #ifndef NOPUSH #ifdef BROWSER { "browser", SHBROWSE, CM_PSH|CM_LOC }, #endif /* BROWSER */ #endif /* NOPUSH */ { "cd", SHCD, 0 }, { "character-sets", SHCSE, 0 }, { "cmd", SHCMD, CM_INV }, #ifndef NOLOCAL { "com", SHCOM, CM_INV|CM_ABR }, { "comm", SHCOM, CM_INV|CM_ABR }, { "communications", SHCOM, 0 }, #endif /* NOLOCAL */ { "command", SHCMD, 0 }, { "connection", SHCONNX, 0 }, #ifdef CK_SPEED { "control-prefixing", SHCTL, 0 }, #endif /* CK_SPEED */ #ifdef CKLOGDIAL { "cx", SHCONNX, CM_INV }, #endif /* CKLOGDIAL */ #ifndef NOSPL { "count", SHCOU, 0 }, #endif /* NOSPL */ { "d", SHDIA, CM_INV|CM_ABR }, #ifdef VMS { "default", SHDFLT, 0 }, #else { "default", SHDFLT, CM_INV }, #endif /* VMS */ #ifndef NODIAL { "dial", SHDIA, CM_LOC }, #endif /* NODIAL */ { "double/ignore",SHDBL, 0 }, #ifndef NOPUSH #ifndef NOFRILLS { "editor", SHEDIT, CM_PSH }, #endif /* NOFRILLS */ #endif /* NOPUSH */ #ifndef NOLOCAL { "escape", SHESC, CM_LOC }, #endif /* NOLOCAL */ { "exit", SHEXI, 0 }, { "extended-options", SHXOPT, CM_INV }, { "features", SHFEA, 0 }, { "file", SHFIL, 0 }, #ifndef NOLOCAL { "flow-control", SHOFLO, 0 }, #endif /* NOLOCAL */ #ifdef BROWSER { "ftp", SHOFTP, CM_PSH|CM_LOC }, #else #ifndef NOFTP #ifndef SYSFTP #ifdef TCPSOCKET { "ftp", SHOFTP, 0 }, /* (built-in ftp) */ #endif /* TCPSOCKET */ #endif /* SYSFTP */ #endif /* NOFTP */ #endif /* BROWSER */ #ifndef NOSPL { "functions", SHFUN, 0 }, { "globals", SHVAR, 0 }, #endif /* NOSPL */ #ifdef KUI { "gui", SHOGUI, 0 }, #endif /* KUI */ #ifdef CK_RECALL { "history", SHHISTORY, 0 }, #endif /* CK_RECALL */ { "ignore/double",SHDBL, CM_INV }, { "iksd", SHOIKS, CM_INV }, #ifndef NOSPL { "input", SHINP, 0 }, #endif /* NOSPL */ #ifndef NOSETKEY { "k", SHKEY, CM_INV|CM_ABR }, { "key", SHKEY, 0 }, #ifndef NOKVERBS { "kverbs", SHKVB, 0 }, #endif /* NOKVERBS */ #endif /* NOSETKEY */ #ifdef CK_LABELED { "labeled-file-info", SHLBL, 0 }, #endif /* CK_LABELED */ #ifndef NOCSETS { "languages", SHLNG, 0 }, #endif /* NOCSETS */ { "logs", SHLOG, 0 }, #ifndef NOSPL { "macros", SHMAC, 0 }, #endif /* NOSPL */ #ifndef NODIAL { "modem", SHMOD, CM_LOC }, #else { "modem-signals",SHCOM, CM_INV|CM_LOC }, #endif /* NODIAL */ #ifndef NOLOCAL #ifdef OS2MOUSE { "mouse", SHMOU, CM_LOC }, #endif /* OS2MOUSE */ #endif /* NOLOCAL */ #ifdef NETCONN { "network", SHNET, CM_LOC }, #else { "network", SHNET, CM_INV|CM_LOC }, #endif /* NETCONN */ { "options", SHOPTS, 0 }, #ifndef NOSPL { "output", SHOUTP, CM_INV }, #endif /* NOSPL */ #ifdef ANYX25 #ifndef IBMX25 { "pad", SHPAD, CM_LOC }, #endif /* IBMX25 */ #endif /* ANYX25 */ { "parameters", SHPAR, CM_INV }, #ifdef PATTERNS { "patterns", SHOPAT, 0 }, #endif /* PATTERNS */ { "printer", SHPRT, 0 }, #ifdef CK_SPEED { "prefixing", SHCTL, CM_INV }, #endif /* CK_SPEED */ #ifndef NOXFER { "protocol", SHPRO, 0 }, #endif /* NOXFER */ { "rename", SHOREN, 0 }, #ifndef NOSPL { "scripts", SHSCR, CM_LOC }, #endif /* NOSPL */ { "send-list", SHSFL, 0 }, #ifndef NOSERVER { "server", SHSER, 0 }, #endif /* NOSERVER */ #ifndef NOSEXP { "sexpression", SHSEXP, 0 }, #endif /* NOSEXP */ #ifdef ANYSSH { "ssh", SHOSSH, 0 }, #endif /* ANYSSH */ { "stack", SHSTK, 0 }, { "status", SHSTA, 0 }, #ifdef STREAMING { "streaming", SHOSTR, 0 }, #endif /* STREAMING */ #ifndef NOLOCAL #ifdef OS2 { "tabs", SHTAB, CM_INV|CM_LOC }, #endif /* OS2 */ #ifdef CK_TAPI { "tapi", SHTAPI, CM_LOC }, { "tapi-comm", SHTAPI_C, CM_INV|CM_LOC }, { "tapi-location", SHTAPI_L, CM_INV|CM_LOC }, { "tapi-modem", SHTAPI_M, CM_INV|CM_LOC }, #endif /* CK_TAPI */ { "tcp", SHTCP, CM_LOC }, #ifdef TNCODE { "tel", SHTEL, CM_INV|CM_ABR }, { "telnet", SHTEL, 0 }, { "telopt", SHTOPT, 0 }, #endif /* TNCODE */ { "terminal", SHTER, CM_LOC }, #endif /* NOLOCAL */ #ifndef NOXMIT { "tr", SHXMI, CM_INV|CM_ABR }, { "tra", SHXMI, CM_INV|CM_ABR }, { "tran", SHXMI, CM_INV|CM_ABR }, { "trans", SHXMI, CM_INV|CM_ABR }, #endif /* NOXMIT */ #ifndef NOXFER { "transfer", SHOXFER, 0 }, #endif /* NOXFER */ #ifndef NOXMIT { "transmit", SHXMI, 0 }, #endif /* NOXMIT */ #ifdef CK_TRIGGER { "trigger", SHTRIG, CM_LOC }, #endif /* CK_TRIGGER */ #ifndef NOSETKEY #ifndef NOKVERBS #ifdef OS2 { "udk", SHUDK, CM_LOC }, #endif /* OS2 */ #endif /* NOKVERBS */ #endif /* NOSETKEY */ #ifndef NOSPL { "variables", SHBUI, 0 }, #endif /* NOSPL */ #ifndef NOFRILLS { "versions", SHVER, 0 }, #endif /* NOFRILLS */ #ifdef OS2 { "vscrn", SHVSCRN, CM_INV|CM_LOC }, #endif /* OS2 */ { "xfer", SHOXFER, CM_INV }, #ifndef NOXMIT { "xmit", SHXMI, CM_INV }, #endif /* NOXMIT */ { "", 0, 0 } }; int nsho = (sizeof(shotab) / sizeof(struct keytab)) - 1; #endif /* NOSHOW */ #ifdef ANYX25 #ifndef IBMX25 struct keytab padtab[] = { /* PAD commands */ { "clear", XYPADL, 0 }, { "interrupt", XYPADI, 0 }, { "reset", XYPADR, 0 }, { "status", XYPADS, 0 } }; int npadc = (sizeof(padtab) / sizeof(struct keytab)); #endif /* IBMX25 */ #endif /* ANYX25 */ #ifndef NOSERVER static struct keytab kmstab[] = { { "both", 3, 0 }, { "local", 1, 0 }, { "remote", 2, 0 } }; static struct keytab enatab[] = { /* ENABLE commands */ { "all", EN_ALL, 0 }, #ifndef NOSPL { "as", EN_ASG, CM_INV|CM_ABR }, { "asg", EN_ASG, CM_INV }, { "assign", EN_ASG, 0 }, #endif /* NOSPL */ #ifndef datageneral { "bye", EN_BYE, 0 }, #endif /* datageneral */ { "cd", EN_CWD, 0 }, #ifdef ZCOPY { "copy", EN_CPY, 0 }, #endif /* ZCOPY */ { "cwd", EN_CWD, CM_INV }, { "delete", EN_DEL, 0 }, { "directory", EN_DIR, 0 }, { "enable", EN_ENA, CM_INV }, { "exit", EN_XIT, 0 }, { "finish", EN_FIN, 0 }, { "get", EN_GET, 0 }, { "host", EN_HOS, 0 }, { "mail", EN_MAI, 0 }, { "mkdir", EN_MKD, 0 }, { "print", EN_PRI, 0 }, #ifndef NOSPL { "query", EN_QUE, 0 }, #endif /* NOSPL */ { "rename", EN_REN, 0 }, { "retrieve", EN_RET, CM_INV }, { "rmdir", EN_RMD, 0 }, { "send", EN_SEN, 0 }, { "set", EN_SET, 0 }, { "space", EN_SPA, 0 }, { "type", EN_TYP, 0 }, { "who", EN_WHO, 0 } }; static int nena = (sizeof(enatab) / sizeof(struct keytab)); #endif /* NOSERVER */ struct keytab txtbin[] = { { "all", 2, 0 }, { "binary", 1, 0 }, { "text", 0, 0 } }; #ifndef NOXFER static struct keytab sndtab[] = { /* SEND command options */ { "/after", SND_AFT, CM_ARG }, #ifndef NOSPL { "/array", SND_ARR, CM_ARG }, #endif /* NOSPL */ { "/as-name", SND_ASN, CM_ARG }, { "/b", SND_BIN, CM_INV|CM_ABR }, { "/before", SND_BEF, CM_ARG }, { "/binary", SND_BIN, 0 }, #ifdef CALIBRATE { "/c", SND_CMD, CM_INV|CM_ABR }, { "/calibrate", SND_CAL, CM_INV|CM_ARG }, #endif /* CALIBRATE */ { "/command", SND_CMD, CM_PSH }, { "/delete", SND_DEL, 0 }, #ifdef UNIXOROSK { "/dotfiles", SND_DOT, 0 }, #endif /* UNIXOROSK */ { "/except", SND_EXC, CM_ARG }, #ifdef PIPESEND { "/filter", SND_FLT, CM_ARG|CM_PSH }, #endif /* PIPESEND */ { "/filenames", SND_NAM, CM_ARG }, #ifdef CKSYMLINK { "/followlinks", SND_LNK, 0 }, #endif /* CKSYMLINK */ #ifdef VMS { "/image", SND_IMG, 0 }, #else { "/image", SND_BIN, CM_INV }, #endif /* VMS */ #ifdef CK_LABELED { "/labeled", SND_LBL, 0 }, #endif /* CK_LABELED */ { "/larger-than", SND_LAR, CM_ARG }, { "/listfile", SND_FIL, CM_ARG }, #ifndef NOFRILLS { "/mail", SND_MAI, CM_ARG }, #endif /* NOFRILLS */ #ifdef CK_TMPDIR { "/move-to", SND_MOV, CM_ARG }, #endif /* CK_TMPDIR */ { "/nobackupfiles", SND_NOB, 0 }, #ifdef UNIXOROSK { "/nodotfiles", SND_NOD, 0 }, #endif /* UNIXOROSK */ #ifdef CKSYMLINK { "/nofollowlinks", SND_NLK, 0 }, #endif /* CKSYMLINK */ { "/not-after", SND_NAF, CM_ARG }, { "/not-before", SND_NBE, CM_ARG }, { "/pathnames", SND_PTH, CM_ARG }, { "/print", SND_PRI, CM_ARG }, #ifdef CK_XYZ { "/protocol", SND_PRO, CM_ARG }, #else { "/protocol", SND_PRO, CM_ARG|CM_INV }, #endif /* CK_XYZ */ { "/quiet", SND_SHH, 0 }, { "/recover", SND_RES, 0 }, #ifdef RECURSIVE /* Systems where we do recursion */ { "/recursive", SND_REC, 0 }, #else #ifdef VMS /* Systems that do recursion themselves without our assistance */ /* if we give them the right kind of wildcard */ { "/recursive", SND_REC, 0 }, #else #ifdef datageneral { "/recursive", SND_REC, 0 }, #else { "/recursive", SND_REC, CM_INV }, #endif /* datageneral */ #endif /* VMS */ #endif /* RECURSIVE */ { "/rename-to", SND_REN, CM_ARG }, { "/since", SND_AFT, CM_INV|CM_ARG }, { "/smaller-than", SND_SMA, CM_ARG }, { "/starting-at", SND_STA, CM_ARG }, #ifndef NOFRILLS { "/su", SND_ASN, CM_ARG|CM_INV|CM_ABR }, { "/sub", SND_ASN, CM_ARG|CM_INV|CM_ABR }, { "/subject", SND_ASN, CM_ARG }, #endif /* NOFRILLS */ #ifdef RECURSIVE { "/subdirectories", SND_REC, CM_INV }, #endif /* RECURSIVE */ { "/text", SND_TXT, 0 }, { "/transparent", SND_XPA, 0 }, { "/type", SND_TYP, CM_ARG } }; #define NSNDTAB sizeof(sndtab)/sizeof(struct keytab) static int nsndtab = NSNDTAB; #ifndef NOMSEND static struct keytab msndtab[] = { /* MSEND options */ { "/after", SND_AFT, CM_ARG }, { "/before", SND_BEF, CM_ARG }, { "/binary", SND_BIN, 0 }, { "/delete", SND_DEL, 0 }, { "/except", SND_EXC, CM_ARG }, { "/filenames", SND_NAM, CM_ARG }, #ifdef CKSYMLINK { "/followlinks", SND_LNK, 0 }, #endif /* CKSYMLINK */ #ifdef VMS { "/image", SND_IMG, 0 }, #else { "/image", SND_BIN, CM_INV }, #endif /* VMS */ #ifdef CK_LABELED { "/labeled", SND_LBL, 0 }, #endif /* CK_LABELED */ { "/larger-than", SND_LAR, CM_ARG }, { "/list", SND_FIL, CM_ARG }, #ifndef NOFRILLS { "/mail", SND_MAI, CM_ARG }, #endif /* NOFRILLS */ #ifdef CK_TMPDIR { "/move-to", SND_MOV, CM_ARG }, #endif /* CK_TMPDIR */ #ifdef CKSYMLINK { "/nofollowlinks", SND_NLK, 0 }, #endif /* CKSYMLINK */ { "/not-after", SND_NAF, CM_ARG }, { "/not-before", SND_NBE, CM_ARG }, { "/pathnames", SND_PTH, CM_ARG }, { "/print", SND_PRI, CM_ARG }, #ifdef CK_XYZ { "/protocol", SND_PRO, CM_ARG }, #endif /* CK_XYZ */ { "/quiet", SND_SHH, 0 }, { "/recover", SND_RES, 0 }, { "/rename-to", SND_REN, CM_ARG }, { "/since", SND_AFT, CM_INV|CM_ARG }, { "/smaller-than", SND_SMA, CM_ARG }, { "/starting-at", SND_STA, CM_ARG }, #ifndef NOFRILLS { "/subject", SND_ASN, CM_ARG }, #endif /* NOFRILLS */ { "/text", SND_TXT, 0 }, { "/transparent", SND_XPA, 0 }, { "/type", SND_TYP, CM_ARG } }; #define NMSNDTAB sizeof(msndtab)/sizeof(struct keytab) static int nmsndtab = NMSNDTAB; #endif /* NOMSEND */ #endif /* NOXFER */ /* CONNECT command switches */ #define CONN_II 0 /* Idle interval */ #define CONN_IS 1 /* Idle string */ #define CONN_IL 2 /* Idle limit */ #define CONN_NV 3 /* Non-Verbose */ #define CONN_TL 4 /* Time limit */ #define CONN_TS 5 /* Trigger string */ #define CONN_AS 6 /* Asynchronous */ #define CONN_SY 7 /* Synchronous */ #define CONN_MAX 7 /* Number of CONNECT switches */ #ifndef NOLOCAL static struct keytab conntab[] = { #ifdef OS2 { "/asynchronous", CONN_AS, CM_INV }, #endif /* OS2 */ #ifdef XLIMITS { "/idle-interval", CONN_II, CM_ARG }, { "/idle-limit", CONN_IL, CM_ARG }, { "/idle-string", CONN_IS, CM_ARG }, { "/quietly", CONN_NV, CM_INV }, #else { "/quietly", CONN_NV, 0 }, #endif /* XLIMITS */ #ifdef OS2 { "/synchronous", CONN_SY, CM_INV }, #endif /* OS2 */ #ifdef XLIMITS { "/time-limit", CONN_TL, CM_ARG }, #endif /* XLIMITS */ #ifdef CK_TRIGGER { "/trigger", CONN_TS, CM_ARG }, #endif /* CK_TRIGGER */ { "",0,0 } }; #define NCONNTAB sizeof(conntab)/sizeof(struct keytab) static int nconntab = NCONNTAB; #endif /* NOLOCAL */ #ifndef NOXFER static struct keytab stattab[] = { /* STATISTICS command switches */ { "/brief", 1, 0 }, { "/verbose", 0, 0 } }; #endif /* NOXFER */ #ifndef NOSPL #ifdef COMMENT struct mtab mactab[MAC_MAX] = { /* Preinitialized macro table */ { NULL, NULL, 0 } }; #else struct mtab *mactab; /* Dynamically allocated macro table */ #endif /* COMMENT */ int nmac = 0; struct keytab mackey[MAC_MAX]; /* Macro names as command keywords */ #endif /* NOSPL */ #ifndef NOSPL #ifdef OS2 struct keytab beeptab[] = { /* Beep options */ { "error", BP_FAIL, 0 }, { "information", BP_NOTE, 0 }, { "warning", BP_WARN, 0 } }; int nbeeptab = sizeof(beeptab)/sizeof(struct keytab); /* CLEAR COMMMAND-SCREEN options */ #define CLR_C_ALL 0 #define CLR_C_BOL 1 #define CLR_C_BOS 2 #define CLR_C_EOL 3 #define CLR_C_EOS 4 #define CLR_C_LIN 5 #define CLR_C_SCR 6 struct keytab clrcmdtab[] = { { "all", CLR_C_ALL, 0 }, { "bol", CLR_C_BOL, 0 }, { "bos", CLR_C_BOS, 0 }, { "eol", CLR_C_EOL, 0 }, { "eos", CLR_C_EOS, 0 }, { "line", CLR_C_LIN, 0 }, { "scrollback", CLR_C_SCR, 0 } }; int nclrcmd = sizeof(clrcmdtab)/sizeof(struct keytab); #endif /* OS2 */ #endif /* NOSPL */ #ifdef COMMENT /* Not used at present */ static struct keytab pagetab[] = { { "/more", 1, CM_INV }, { "/nopage", 0, 0 }, { "/page", 1, 0 } }; int npagetab = sizeof(pagetab)/sizeof(struct keytab); #endif /* COMMENT */ #define TYP_NOP 0 /* /NOPAGE */ #define TYP_PAG 1 /* /PAGE */ #define TYP_HEA 2 /* /HEAD:n */ #define TYP_TAI 3 /* /TAIL:n */ #define TYP_PAT 4 /* /MATCH:pattern */ #define TYP_WID 5 /* /WIDTH:cols */ #define TYP_COU 6 /* /COUNT */ #define TYP_OUT 7 /* /OUTPUT:file */ #define TYP_PFX 8 /* /PREFIX:string */ #ifdef UNICODE #define TYP_XIN 9 /* /TRANSLATE-FROM:charset */ #define TYP_XUT 10 /* /TRANSLATE-TO:charset */ #define TYP_XPA 11 /* /TRANSPARENT */ #endif /* UNICODE */ #ifdef KUI #define TYP_GUI 12 /* /GUI:title */ #define TYP_HIG 13 /* /HEIGHT:rows */ #endif /* KUI */ #define TYP_NUM 14 /* /NUMBER */ static struct keytab typetab[] = { /* TYPE command switches */ { "/count", TYP_COU, 0 }, #ifdef UNICODE { "/character-set", TYP_XIN, CM_ARG }, #endif /* UNICODE */ #ifdef KUI { "/gui", TYP_GUI, CM_ARG }, #endif /* KUI */ { "/head", TYP_HEA, CM_ARG }, #ifdef KUI { "/height", TYP_HIG, CM_ARG }, #endif /* KUI */ { "/match", TYP_PAT, CM_ARG }, #ifdef CK_TTGWSIZ { "/more", TYP_PAG, CM_INV }, { "/nopage", TYP_NOP, 0 }, { "/number", TYP_NUM, 0 }, { "/output", TYP_OUT, CM_ARG }, { "/page", TYP_PAG, 0 }, #endif /* CK_TTGWSIZ */ { "/prefix", TYP_PFX, CM_ARG }, { "/tail", TYP_TAI, CM_ARG }, #ifdef UNICODE { "/translate-to", TYP_XUT, CM_ARG }, { "/transparent", TYP_XPA, 0 }, #endif /* UNICODE */ { "/width", TYP_WID, CM_ARG }, #ifdef UNICODE { "/xlate-to", TYP_XUT, CM_INV|CM_ARG }, #endif /* UNICODE */ { "", 0, 0 } }; int ntypetab = sizeof(typetab)/sizeof(struct keytab) - 1; int typ_page = -1; /* TYPE /[NO]PAGE default */ int typ_wid = -1; #ifndef NOSPL #define TRA_ALL 999 /* TRACE command */ #define TRA_ASG 0 #define TRA_CMD 1 int tra_asg = 0; int tra_cmd = 0; static struct keytab tracetab[] = { /* TRACE options */ { "all", TRA_ALL, 0 }, { "assignments", TRA_ASG, 0 }, { "command-level", TRA_CMD, 0 } }; static int ntracetab = sizeof(tracetab)/sizeof(struct keytab); #endif /* NOSPL */ #ifndef NOSHOW VOID showtypopts() { printf(" TYPE "); if (typ_page > -1) { prtopt(&optlines,typ_page ? "/PAGE" : "/NOPAGE"); } else prtopt(&optlines,"(no options set)"); if (typ_wid > -1) { ckmakmsg(tmpbuf,TMPBUFSIZ,"/WIDTH:",ckitoa(typ_wid),NULL,NULL); prtopt(&optlines,tmpbuf); } prtopt(&optlines,""); } #endif /* NOSHOW */ #ifdef LOCUS /* isauto == 1 if locus is being switched automatically */ VOID setlocus(x, isauto) int x, isauto; { extern int quitting; if (x) x = 1; if (x && locus) return; if (!x && !locus) return; /* Get here if it actually needs to be changed */ #ifdef OS2 if (isauto && /* Automatically switching */ !quitting && /* not exiting */ autolocus == 2) { /* and AUTOLOCUS is set to ASK */ char locmsg[300]; ckmakmsg(locmsg,300, "Switching Locus to ", x ? "LOCAL" : "REMOTE", " for file management commands\n" "such as CD, DIRECTORY, DELETE, RENAME. Type HELP SET\n" "LOCUS at the K-95> prompt for further info. Use the\n" #ifdef KUI "Actions menu or SET LOCUS command to disable automatic\n" "Locus switching or to disable these queries.", #else /* KUI */ "SET LOCUS command to disable automatic locus switching\n" "or to disable these queries.", #endif /* KUI */ NULL); if (uq_ok(locmsg,"OK to switch Locus?",3,NULL,1)) { locus = x; #ifdef KUI KuiSetProperty(KUI_LOCUS,x,0); #endif /* KUI */ return; } } else { #endif /* OS2 */ if (isauto && msgflg && !quitting) printf("Switching LOCUS for file-management commands to %s %s.\n", x ? "LOCAL" : "REMOTE", "(HELP LOCUS for info)" ); locus = x; #ifdef OS2 #ifdef KUI KuiSetProperty(KUI_LOCUS,x,0); #endif /* KUI */ } #endif /* OS2 */ } VOID setautolocus(x) int x; { autolocus = x; #ifdef KUI KuiSetProperty(KUI_AUTO_LOCUS,x,0); #endif /* KUI */ } #endif /* LOCUS */ int settypopts() { /* Set TYPE option defaults */ int xp = -1; int c, getval; while (1) { if ((y = cmswi(typetab,ntypetab,"Switch","",xxstring)) < 0) { if (y == -3) break; else return(y); } c = cmgbrk(); if ((getval = (c == ':' || c == '=')) && !(cmgkwflgs() & CM_ARG)) { printf("?This switch does not take an argument\n"); return(-9); } switch (y) { case TYP_NOP: xp = 0; break; case TYP_PAG: xp = 1; break; case TYP_WID: if (getval) if ((x = cmnum("Column at which to truncate", ckitoa(cmd_cols),10,&y,xxstring)) < 0) return(x); typ_wid = y; break; default: printf("?Sorry, this option can not be set\n"); return(-9); } } if ((x = cmcfm()) < 0) /* Get confirmation */ return(x); if (xp > -1) typ_page = xp; /* Confirmed, save defaults */ return(success = 1); } /* Forward declarations of functions local to this module */ #ifdef UNIX _PROTOTYP (int douchmod, ( void ) ); #endif /* UNIX */ #ifdef CKPURGE _PROTOTYP (int dopurge, ( void ) ); #endif /* CKPURGE */ #ifndef NOSPL _PROTOTYP (int doundef, ( int ) ); _PROTOTYP (int doask, ( int ) ); _PROTOTYP (int dodef, ( int ) ); _PROTOTYP (int doelse, ( void ) ); _PROTOTYP (int dofor, ( void ) ); #endif /* NOSPL */ #ifndef NODIAL _PROTOTYP (int dodial, ( int ) ); #endif /* NODIAL */ _PROTOTYP (int dodel, ( void ) ); _PROTOTYP (int dopaus, ( int ) ); #ifndef NOPUSH #ifdef TCPSOCKET _PROTOTYP (int doping, ( void ) ); _PROTOTYP (int doftp, ( void ) ); #endif /* TCPSOCKET */ #endif /* NOPUSH */ #ifndef NORENAME #ifndef NOFRILLS _PROTOTYP (int dorenam, ( void ) ); #endif /* NOFRILLS */ #endif /* NORENAME */ #ifdef ZCOPY _PROTOTYP (int docopy, ( void ) ); #endif /* ZCOPY */ #ifdef NT _PROTOTYP (int dolink, ( void )); #endif /* NT */ #ifdef CK_REXX _PROTOTYP (int dorexx, ( void ) ); #endif /* CK_REXX */ #ifdef TNCODE static struct keytab telcmd[] = { { "abort", TN_ABORT, CM_INV }, /* Emotionally toned - don't show */ { "ao", TN_AO, 0 }, { "ayt", TN_AYT, 0 }, { "break", BREAK, 0 }, { "cancel",TN_ABORT, 0 }, { "dmark", TN_DM, 0 }, { "do", DO, 0 }, { "dont", DONT, 0 }, { "ec", TN_EC, 0 }, { "el", TN_EL, 0 }, { "eof", TN_EOF, 0 }, { "eor", TN_EOR, 0 }, #ifdef CK_KERBEROS #ifdef KRB5 #define TN_FWD 1 { "forward", TN_FWD, CM_INV }, #endif /* KRB5 */ #endif /* CK_KERBEROS */ { "ga", TN_GA, 0 }, { "ip", TN_IP, 0 }, { "nop", TN_NOP, 0 }, { "sak", TN_SAK, CM_INV }, { "sb", SB, 0 }, { "se", SE, 0 }, { "susp", TN_SUSP, 0 }, { "will", WILL, 0 }, { "wont", WONT, 0 } }; static int ntelcmd = (sizeof(telcmd) / sizeof(struct keytab)); static struct keytab tnopts[] = { #ifdef CK_AUTHENTICATION { "auth", TELOPT_AUTHENTICATION, 0 }, #else { "auth", TELOPT_AUTHENTICATION, CM_INV }, #endif /* CK_AUTHENTICATION */ { "binary", TELOPT_BINARY, 0 }, #ifdef TN_COMPORT { "c", TELOPT_COMPORT, CM_INV|CM_ABR}, { "co", TELOPT_COMPORT, CM_INV|CM_ABR}, { "com", TELOPT_COMPORT, CM_INV|CM_ABR}, { "com-port-control", TELOPT_COMPORT, 0 }, { "comport-control", TELOPT_COMPORT, CM_INV}, #else /* TN_COMPORT */ { "com-port-control", TELOPT_COMPORT, CM_INV }, { "comport-control", TELOPT_COMPORT, CM_INV}, #endif /* TN_COMPORT */ { "echo", TELOPT_ECHO, 0 }, #ifdef CK_ENCRYPTION { "encrypt", TELOPT_ENCRYPTION, 0 }, #else { "encrypt", TELOPT_ENCRYPTION, CM_INV }, #endif /* CK_ENCRYPTION */ #ifdef CK_FORWARD_X { "forward-x", TELOPT_FORWARD_X, 0 }, #else { "forward-x", TELOPT_FORWARD_X, CM_INV }, #endif /* CK_FORWARD_X */ #ifdef IKS_OPTION { "kermit", TELOPT_KERMIT, 0 }, #else { "kermit", TELOPT_KERMIT, CM_INV }, #endif /* IKS_OPTION */ { "lflow", TELOPT_LFLOW, CM_INV }, { "logout", TELOPT_LOGOUT, CM_INV }, #ifdef CK_NAWS { "naws", TELOPT_NAWS, 0 }, #else { "naws", TELOPT_NAWS, CM_INV }, #endif /* CK_NAWS */ #ifdef CK_ENVIRONMENT { "new-environment", TELOPT_NEWENVIRON, 0 }, #else { "new-environment", TELOPT_NEWENVIRON, CM_INV }, #endif /* CK_ENVIRONMENT */ { "pragma-heartbeat",TELOPT_PRAGMA_HEARTBEAT, CM_INV }, { "pragma-logon", TELOPT_PRAGMA_LOGON, CM_INV }, { "pragma-sspi", TELOPT_SSPI_LOGON, CM_INV }, { "sak", TELOPT_IBM_SAK, CM_INV }, #ifdef CK_SNDLOC { "send-location", TELOPT_SNDLOC, 0 }, #else { "send-location", TELOPT_SNDLOC, CM_INV }, #endif /* CK_SNDLOC */ { "sga", TELOPT_SGA, 0 }, #ifdef CK_SSL { "start-tls", TELOPT_START_TLS, 0 }, #else { "start-tls", TELOPT_START_TLS, CM_INV }, #endif /* CK_SSL */ { "ttype", TELOPT_TTYPE, 0 }, #ifdef CK_ENVIRONMENT { "xdisplay-location", TELOPT_XDISPLOC, 0 }, #else { "xdisplay-location", TELOPT_XDISPLOC, CM_INV }, #endif /* CK_ENVIRONMENT */ { "", 0, 0 } }; static int ntnopts = (sizeof(tnopts) / sizeof(struct keytab)) - 1; static struct keytab tnsbopts[] = { #ifdef CK_NAWS { "naws", TELOPT_NAWS, 0 }, #endif /* CK_NAWS */ { "", 0, 0 } }; static int ntnsbopts = (sizeof(tnsbopts) / sizeof(struct keytab)) - 1; #endif /* TNCODE */ #ifdef TCPSOCKET #ifndef NOPUSH #ifdef SYSFTP int doftp() { /* (External) FTP command */ char *p, *f; /* (See doxftp() for internal one) */ int x; if (network) /* If we have a current connection */ ckstrncpy(line,ttname,LINBUFSIZ); /* get the host name */ else *line = '\0'; /* as default host */ for (p = line; *p; p++) /* Remove ":service" from end. */ if (*p == ':') { *p = '\0'; break; } if ((x = cmtxt("IP host name or number", line, &s, xxstring)) < 0) return(x); if (nopush) { printf("?Sorry, FTP command disabled\n"); return(success = 0); } /* Construct FTP command */ #ifdef VMS #ifdef MULTINET /* TGV MultiNet */ ckmakmsg(line,LINBUFSIZ,"multinet ftp ",s,NULL,NULL); #else ckmakmsg(line,LINBUFSIZ,"ftp ",s,NULL,NULL); #endif /* MULTINET */ #else /* Not VMS */ #ifdef OS2ORUNIX #ifndef NOFTP f = ftpapp; if (!f) f = ""; if (!f[0]) f = "ftp"; ckmakmsg(line,LINBUFSIZ,f," ",s,NULL); #ifdef OS2 p = line + strlen(ftpapp); while (p != line) { if (*p == '/') *p = '\\'; p--; } #endif /* OS2 */ #else /* NOFTP */ ckmakmsg(line,LINBUFSIZ,"ftp ",s,NULL,NULL); #endif /* NOFTP */ #else /* OS2ORUNIX */ ckmakmsg(line,LINBUFSIZ,"ftp ",s,NULL,NULL); #endif /* OS2ORUNIX */ #endif /* VMS */ conres(); /* Make console normal */ #ifdef DEC_TCPIP printf("\n"); /* Prevent prompt-stomping */ #endif /* DEC_TCPIP */ x = zshcmd(line); concb((char)escape); return(success = x); } #endif /* SYSFTP */ int doping() { /* PING command */ char *p; /* just runs ping program */ int x; if (network) /* If we have a current connection */ ckstrncpy(line,ttname,LINBUFSIZ); /* get the host name */ else *line = '\0'; /* as default host to be pinged. */ for (p = line; *p; p++) /* Remove ":service" from end. */ if (*p == ':') { *p = '\0'; break; } if ((x = cmtxt("IP host name or number", line, &s, xxstring)) < 0) return(x); if (nopush) { printf("?Sorry, PING command disabled\n"); return(success = 0); } /* Construct PING command */ #ifdef VMS #ifdef MULTINET /* TGV MultiNet */ ckmakmsg(line,LINBUFSIZ,"multinet ping ",s," /num=1",NULL); #else ckmakmsg(line,LINBUFSIZ,"ping ",s," 56 1",NULL); /* Other VMS TCP/IP's */ #endif /* MULTINET */ #else /* Not VMS */ ckmakmsg(line,LINBUFSIZ,"ping ",s,NULL,NULL); #endif /* VMS */ conres(); /* Make console normal */ #ifdef DEC_TCPIP printf("\n"); /* Prevent prompt-stomping */ #endif /* DEC_TCPIP */ x = zshcmd(line); concb((char)escape); return(success = x); } #endif /* NOPUSH */ #endif /* TCPSOCKET */ static VOID doend(x) int x; { #ifndef NOSPL /* Pop from all FOR/WHILE/XIF/SWITCH's */ debug(F101,"doend maclvl 1","",maclvl); while ((maclvl > 0) && (m_arg[maclvl-1][0]) && (cmdstk[cmdlvl].src == CMD_MD) && (!strncmp(m_arg[maclvl-1][0],"_xif",4) || !strncmp(m_arg[maclvl-1][0],"_for",4) || !strncmp(m_arg[maclvl-1][0],"_whi",4) || !strncmp(m_arg[maclvl-1][0],"_swi",4))) { debug(F110,"END popping",m_arg[maclvl-1][0],0); dogta(XXPTA); /* Put args back */ popclvl(); /* Pop up two levels */ popclvl(); debug(F101,"doend maclvl 2","",maclvl); } if (maclvl > -1) { if (mrval[maclvl]) /* Free previous retval if any */ free(mrval[maclvl]); mrval[maclvl] = malloc(16); /* Room for up to 15 digits */ if (mrval[maclvl]) /* Record current retval */ ckmakmsg(mrval[maclvl],16,ckitoa(x),NULL,NULL,NULL); } #endif /* NOSPL */ popclvl(); /* Now pop out of macro or TAKE file */ #ifndef NOSPL #ifdef DEBUG if (deblog) { debug(F101,"END maclvl 3","",maclvl); debug(F111,"END mrval[maclvl]",mrval[maclvl],maclvl); debug(F111,"END mrval[maclvl+1]",mrval[maclvl+1],maclvl+1); } #endif /* DEBUG */ #endif /* NOSPL */ } #ifdef CKROOT int dochroot() { if ((x = cmdir("Name of new root directory","",&s,xxstring)) < 0) { if (x == -3) { printf("?Directory name required\n"); return(-9); } return(x); } ckstrncpy(line,s,LINBUFSIZ); s = line; if ((x = cmcfm()) < 0) return(x); s = brstrip(s); x = zsetroot(s); if (x < 0) { char * m = NULL; switch (x) { case -1: case -2: m = "Not a directory"; break; case -3: m = "Internal error"; break; case -4: m = "Access denied"; break; case -5: m = "Off limits"; break; } if (m) printf("%s: \"%s\"\n", m, s); return(m ? -9 : -2); } else { nopush = 1; return(success = 1); } } #endif /* CKROOT */ #ifndef NOXFER static char * asnbuf = NULL; /* As-name buffer pointer */ char sndxnam[] = { "_array_x_" }; /* (with replaceable x!) */ /* The new SEND command, replacing BSEND, CSEND, PSEND, etc etc. Call with cx = top-level keyword value. Returns: < 0 On parse error. 0 On other type of failure (e.g. requested operation not allowed). 1 On success with sstate set to 's' so protocol will begin. */ /* D O X S E N D -- Parse SEND and related commands with switches */ int doxsend(cx) int cx; { int c, i, n, wild, confirmed = 0; /* Workers */ int x, y; /* of the world... */ int getval = 0; /* Whether to get switch value */ extern char * snd_move; /* Directory to move sent files to */ extern char * snd_rename; /* What to rename sent files to */ extern char * filefile; /* File containing filenames to send */ extern int xfiletype; /* Send only text (or binary) files */ extern struct keytab pathtab[]; /* PATHNAMES option keywords */ extern int npathtab; /* How many of them */ extern int recursive; /* Recursive directory traversal */ extern int rprintf; /* REMOTE PRINT flag */ extern int fdispla; /* TRANSFER DISPLAY setting */ extern int skipbup; /* Skip backup files when sending */ struct stringint pv[SND_MAX+1]; /* Temporary array for switch values */ struct FDB sf, sw, fl, cm; /* FDBs for each parse function */ int mlist = 0; /* Flag for MSEND or MMOVE */ char * m; /* For making help messages */ extern struct keytab protos[]; /* File transfer protocols */ extern int xfrxla, g_xfrxla, nprotos; extern char sndbefore[], sndafter[], *sndexcept[]; /* Selection criteria */ extern char sndnbefore[], sndnafter[]; extern CK_OFF_T sndsmaller, sndlarger, calibrate; #ifndef NOSPL int range[2]; /* Array range */ char ** ap = NULL; /* Array pointer */ int arrayx = -1; /* Array index */ #endif /* NOSPL */ #ifdef NEWFTP if ((ftpget == 1) || ((ftpget == 2) && ftpisopen())) { if (cx == XXMAI) { printf("?Sorry, No MAIL with FTP\n"); return(-9); } return(doftpput(cx,0)); } #endif /* NEWFTP */ for (i = 0; i <= SND_MAX; i++) { /* Initialize switch values */ pv[i].sval = NULL; /* to null pointers */ pv[i].ival = -1; /* and -1 int values */ pv[i].wval = (CK_OFF_T)-1; /* and -1 wide values */ } #ifndef NOSPL range[0] = -1; range[1] = -1; sndxin = -1; /* Array index */ #endif /* NOSPL */ sndarray = NULL; /* Array pointer */ #ifdef UNIXOROSK g_matchdot = matchdot; /* Match dot files */ #endif /* UNIXOROSK */ g_recursive = recursive; /* Recursive sending */ recursive = 0; /* Save global value, set local */ debug(F101,"xsend entry fncnv","",fncnv); /* Preset switch values based on top-level command that called us */ switch (cx) { case XXMSE: /* MSEND */ mlist = 1; break; case XXCSEN: /* CSEND */ pv[SND_CMD].ival = 1; break; case XXMMOVE: /* MMOVE */ mlist = 1; case XXMOVE: /* MOVE */ pv[SND_DEL].ival = 1; break; case XXRSEN: /* RESEND */ pv[SND_BIN].ival = 1; /* Implies /BINARY */ pv[SND_RES].ival = 1; break; case XXMAI: /* MAIL */ pv[SND_MAI].ival = 1; break; } /* Set up chained parse functions... */ cmfdbi(&sw, /* First FDB - command switches */ _CMKEY, /* fcode */ "Filename, or switch", /* hlpmsg */ "", /* default */ "", /* addtl string data */ #ifdef NOMSEND nsndtab, /* addtl numeric data 1: tbl size */ #else mlist ? nmsndtab : nsndtab, /* addtl numeric data 1: tbl size */ #endif /* NOMSEND */ 4, /* addtl numeric data 2: 4 = cmswi */ xxstring, /* Processing function */ #ifdef NOMSEND sndtab, /* Keyword table */ #else mlist ? msndtab : sndtab, #endif /* NOMSEND */ &sf /* Pointer to next FDB */ ); cmfdbi(&sf, /* 2nd FDB - file to send */ _CMIFI, /* fcode */ "File(s) to send", /* hlpmsg */ "", /* default */ "", /* addtl string data */ nolinks, /* addtl numeric data 1 */ 0, /* addtl numeric data 2 */ xxstring, NULL, mlist ? &cm : &fl ); cmfdbi(&fl, /* 3rd FDB - command to send from */ _CMFLD, /* fcode */ "Command", /* hlpmsg */ "", /* default */ "", /* addtl string data */ 0, /* addtl numeric data 1 */ 0, /* addtl numeric data 2 */ xxstring, NULL, &cm ); cmfdbi(&cm, /* 4th FDB - Confirmation */ _CMCFM, /* fcode */ "", /* hlpmsg */ "", /* default */ "", /* addtl string data */ 0, /* addtl numeric data 1 */ 0, /* addtl numeric data 2 */ NULL, NULL, NULL ); while (1) { /* Parse 0 or more switches */ x = cmfdb(&sw); /* Parse something */ debug(F101,"xsend cmfdb","",x); if (x < 0) /* Error */ goto xsendx; /* or reparse needed */ if (cmresult.fcode != _CMKEY) /* Break out if not a switch */ break; /* They gave a switch, but let's see how they terminated it. If they ended it with : or =, then we must parse a value. If they ended it with anything else, then we must NOT parse a value. */ c = cmgbrk(); /* Get break character */ getval = (c == ':' || c == '='); /* to see how they ended the switch */ if (getval && !(cmresult.kflags & CM_ARG)) { printf("?This switch does not take arguments\n"); x = -9; goto xsendx; } if (!getval && (cmgkwflgs() & CM_ARG)) { printf("?This switch requires an argument\n"); x = -9; goto xsendx; } n = cmresult.nresult; /* Numeric result = switch value */ debug(F101,"xsend switch","",n); switch (n) { /* Process the switch */ case SND_CMD: /* These take no args */ if (nopush) { printf("?Sorry, system command access is disabled\n"); x = -9; goto xsendx; } #ifdef PIPESEND else if (sndfilter) { printf( "?Sorry, no SEND /COMMAND or CSEND when SEND FILTER selected\n"); x = -9; goto xsendx; } #endif /* PIPESEND */ sw.hlpmsg = "Command, or switch"; /* Change help message */ pv[n].ival = 1; /* Just set the flag */ pv[SND_ARR].ival = 0; break; case SND_REC: /* /RECURSIVE */ recursive = 2; /* Set the real variable */ pv[SND_PTH].ival = PATH_REL; /* Give them relative pathnames */ pv[n].ival = 1; /* Just set the flag */ break; case SND_RES: /* /RECOVER (resend) */ pv[SND_ARR].ival = 0; pv[SND_BIN].ival = 1; /* Implies /BINARY */ case SND_NOB: /* /NOBACKUP */ case SND_DEL: /* /DELETE */ case SND_SHH: /* /QUIET */ pv[n].ival = 1; /* Just set the flag */ break; #ifdef UNIXOROSK /* Like recursive, these are set immediately because they affect cmifi() */ case SND_DOT: /* /DOTFILES */ matchdot = 1; break; case SND_NOD: /* /NODOTFILES */ matchdot = 0; break; #endif /* UNIXOROSK */ /* File transfer modes - each undoes the others */ case SND_BIN: /* Binary */ case SND_TXT: /* Text */ case SND_IMG: /* Image */ case SND_LBL: /* Labeled */ pv[SND_BIN].ival = 0; pv[SND_TXT].ival = 0; pv[SND_IMG].ival = 0; pv[SND_LBL].ival = 0; pv[n].ival = 1; break; #ifdef CKSYMLINK case SND_LNK: case SND_NLK: nolinks = (n == SND_NLK) ? 2 : 0; cmfdbi(&sf, /* Redo cmifi() */ _CMIFI, /* fcode */ "File(s) to send", /* hlpmsg */ "", /* default */ "", /* addtl string data */ nolinks, /* addtl numeric data 1 */ 0, /* addtl numeric data 2 */ xxstring, NULL, mlist ? &cm : &fl ); break; #endif /* CKSYMLINK */ case SND_EXC: /* Excludes */ if (!getval) break; if ((x = cmfld("Pattern","",&s,xxstring)) < 0) { if (x == -3) { printf("?Pattern required\n"); x = -9; } goto xsendx; } if (pv[n].sval) free(pv[n].sval); y = strlen(s); if (y > 256) { printf("?Pattern too long - 256 max\n"); x = -9; goto xsendx; } pv[n].sval = malloc(y+1); if (pv[n].sval) { strcpy(pv[n].sval,s); /* safe */ pv[n].ival = 1; } break; case SND_MOV: /* MOVE after */ case SND_REN: /* RENAME after */ if (!getval) break; if ((x = cmfld(n == SND_MOV ? "device and/or directory for source file after sending" : "new name for source file after sending", "", &s, n == SND_MOV ? xxstring : NULL )) < 0) { if (x == -3) { printf("%s\n", n == SND_MOV ? "?Destination required" : "?New name required" ); x = -9; } goto xsendx; } if (pv[n].sval) free(pv[n].sval); s = brstrip(s); y = strlen(s); if (y > 0) { pv[n].sval = malloc(y+1); if (pv[n].sval) { strcpy(pv[n].sval,s); /* safe */ pv[n].ival = 1; } } break; case SND_SMA: /* Smaller / larger than */ case SND_LAR: { CK_OFF_T w; if (!getval) break; if ((x = cmnumw("Size in bytes","0",10,&w,xxstring)) < 0) goto xsendx; pv[n].wval = w; break; } case SND_AFT: /* Send /AFTER:date-time */ case SND_BEF: /* Send /BEFORE:date-time */ case SND_NAF: /* Send /NOT-AFTER:date-time */ case SND_NBE: /* Send /NOT-BEFORE:date-time */ if (!getval) break; if ((x = cmdate("File date-time","",&s,0,xxstring)) < 0) { if (x == -3) { printf("?Date-time required\n"); x = -9; } goto xsendx; } if (pv[n].sval) free(pv[n].sval); pv[n].sval = malloc((int)strlen(s)+1); if (pv[n].sval) { strcpy(pv[n].sval,s); /* safe */ pv[n].ival = 1; } break; case SND_MAI: /* Send as mail (= MAIL) */ #ifdef IKSD if (inserver && !ENABLED(en_mai)) { printf("?Sorry, sending files as mail is disabled\n"); return(-9); } #endif /* IKSD */ pv[n].ival = 1; if (!getval) break; if ((x = cmfld("e-mail address","",&s,xxstring)) < 0) { if (x == -3) { printf("?address required\n"); x = -9; } goto xsendx; } s = brstrip(s); if (pv[n].sval) free(pv[n].sval); pv[n].sval = malloc((int)strlen(s)+1); if (pv[n].sval) strcpy(pv[n].sval,s); /* safe */ break; case SND_PRI: /* Send to be printed (REMOTE PRINT) */ #ifdef IKSD if (inserver && !ENABLED(en_mai)) { printf("?Sorry, sending files for printing is disabled\n"); return(-9); } #endif /* IKSD */ pv[n].ival = 1; if (!getval) break; if ((x = cmfld("Print options","",&s,xxstring)) < 0) if (x != -3) goto xsendx; s = brstrip(s); if (pv[n].sval) free(pv[n].sval); pv[n].sval = malloc((int)strlen(s)+1); if (pv[n].sval) strcpy(pv[n].sval,s); /* safe */ break; case SND_ASN: /* As-name */ debug(F101,"xsend /as-name getval","",getval); if (!getval) break; if ((x = cmfld("Name to send under","",&s,NULL)) < 0) { if (x == -3) { printf("?name required\n"); x = -9; } goto xsendx; } s = brstrip(s); if ((y = strlen(s)) > 0) { if (pv[n].sval) free(pv[n].sval); pv[n].sval = malloc(y+1); if (pv[n].sval) { strcpy(pv[n].sval,s); /* safe */ pv[n].ival = 1; } } break; case SND_STA: { /* Starting position (= PSEND) */ CK_OFF_T w; if (!getval) break; if ((x = cmnumw("0-based position","0",10,&w,xxstring)) < 0) goto xsendx; pv[n].wval = w; break; } case SND_PRO: /* Protocol to use */ if (!getval) break; if ((x = cmkey(protos,nprotos,"File-transfer protocol","", xxstring)) < 0) { if (x == -3) { printf("?name of protocol required\n"); x = -9; } goto xsendx; } pv[n].ival = x; break; #ifdef PIPESEND case SND_FLT: /* Filter */ debug(F101,"xsend /filter getval","",getval); if (!getval) break; if ((x = cmfld("Filter program to send through","",&s,NULL)) < 0) { if (x == -3) s = ""; else goto xsendx; } if (*s) s = brstrip(s); y = strlen(s); for (x = 0; x < y; x++) { /* Make sure they included "\v(...)" */ if (s[x] != '\\') continue; if (s[x+1] == 'v') break; } if (x == y) { printf( "?Filter must contain a replacement variable for filename.\n" ); x = -9; goto xsendx; } pv[n].ival = 1; if (pv[n].sval) { free(pv[n].sval); pv[n].sval = NULL; } if ((y = strlen(s)) > 0) { if ((pv[n].sval = malloc(y+1))) strcpy(pv[n].sval,s); /* safe */ } break; #endif /* PIPESEND */ case SND_PTH: /* Pathnames */ if (!getval) { pv[n].ival = PATH_REL; break; } if ((x = cmkey(pathtab,npathtab,"","absolute",xxstring)) < 0) goto xsendx; pv[n].ival = x; break; case SND_NAM: /* Filenames */ if (!getval) break; if ((x = cmkey(fntab,nfntab,"","converted",xxstring)) < 0) goto xsendx; debug(F101,"xsend /filenames","",x); pv[n].ival = x; break; #ifdef CALIBRATE case SND_CAL: { /* /CALIBRATE */ CK_OFF_T w; if (getval) { if ((x = cmnumw("number of Kbytes to send", "1024",10,&w,xxstring)) < 0) goto xsendx; } else w = (CK_OFF_T)1024; pv[n].wval = w; pv[SND_ARR].ival = 0; break; } #endif /* CALIBRATE */ case SND_FIL: /* Name of file containing filnames */ if (!getval) break; if ((x = cmifi("Name of file containing list of filenames", "",&s,&y,xxstring)) < 0) { if (x == -3) { printf("?Filename required\n"); x = -9; } goto xsendx; } else if (y) { printf("?Wildcards not allowed\n"); x = -9; goto xsendx; } if (pv[n].sval) free(pv[n].sval); if (s) if (*s) { if ((pv[n].sval = malloc((int)strlen(s)+1))) { strcpy(pv[n].sval,s); pv[n].ival = 1; pv[SND_ARR].ival = 0; } } break; #ifndef NOSPL case SND_ARR: /* SEND /ARRAY: */ if (!getval) break; ap = NULL; if ((x = cmfld("Array name (a single letter will do)", "", &s, NULL )) < 0) { if (x == -3) break; else return(x); } if ((x = arraybounds(s,&(range[0]),&(range[1]))) < 0) { printf("?Bad array: %s\n",s); return(-9); } if (!(ap = a_ptr[x])) { printf("?No such array: %s\n",s); return(-9); } pv[n].ival = 1; pv[SND_CMD].ival = 0; /* Undo any conflicting ones... */ pv[SND_RES].ival = 0; pv[SND_CAL].ival = 0; pv[SND_FIL].ival = 0; arrayx = x; break; #endif /* NOSPL */ case SND_XPA: /* /TRANSPARENT */ pv[n].ival = 1; break; case SND_TYP: /* Only files of given type */ if (!getval) break; if ((x = cmkey(txtbin,3,"","all",xxstring)) < 0) goto xsendx; pv[n].ival = (x == 2) ? -1 : x; break; default: printf("?Unexpected switch value - %d\n",cmresult.nresult); x = -9; goto xsendx; } } debug(F101,"xsend cmresult fcode","",cmresult.fcode); #ifdef COMMENT /* List switch parsing results in debug log */ for (i = 0; i <= SND_MAX; i++) { ckmakmsg(line,LINBUFSIZ,"xsend switch ",ckitoa(i),NULL,NULL); debug(F111,line, pv[i].sval, pv[i].ival); } #endif /* COMMENT */ /* Now we have all switches, plus maybe a filename or command, or nothing */ #ifdef PIPESEND if (protocol != PROTO_K && pv[SND_CMD].ival > 0) { printf("?Sorry, %s works only with Kermit protocol\n", (cx == XXCSEN) ? "CSEND" : "SEND /COMMAND"); x = -9; goto xsendx; } if (pv[SND_RES].ival > 0 || /* /RECOVER */ pv[SND_STA].wval > 0) { /* or /STARTING */ if (sndfilter || pv[SND_FLT].ival > 0) { printf("?Sorry, no /RECOVER or /START if SEND FILTER selected\n"); x = -9; goto xsendx; } } #endif /* PIPESEND */ cmarg = ""; cmarg2 = ""; line[0] = NUL; s = line; wild = 0; switch (cmresult.fcode) { /* How did we get out of switch loop */ case _CMIFI: /* Input filename */ ckstrncpy(line,cmresult.sresult,LINBUFSIZ); /* Name */ if (pv[SND_ARR].ival > 0) cmarg2 = line; else wild = cmresult.nresult; /* Wild flag */ if (!recursive && !wild) nolinks = 0; break; case _CMFLD: /* Field */ /* Only allowed with /COMMAND and /ARRAY */ if (pv[SND_CMD].ival < 1 && pv[SND_ARR].ival < 1) { #ifdef CKROOT if (ckrooterr) printf("?Off limits: %s\n",cmresult.sresult); else #endif /* CKROOT */ printf("?%s - \"%s\"\n", iswild(cmresult.sresult) ? "No files match" : "File not found", cmresult.sresult ); x = -9; goto xsendx; } ckstrncpy(line,cmresult.sresult,LINBUFSIZ); if (pv[SND_ARR].ival > 0) cmarg2 = line; break; case _CMCFM: /* Confirmation */ /* s = ""; */ confirmed = 1; break; default: printf("?Unexpected function code: %d\n",cmresult.fcode); x = -9; goto xsendx; } debug(F110,"xsend string",s,0); debug(F101,"xsend confirmed","",confirmed); /* Save and change protocol and transfer mode */ /* Global values are restored in main parse loop */ g_proto = protocol; /* Save current global protocol */ g_urpsiz = urpsiz; g_spsizf = spsizf; g_spsiz = spsiz; g_spsizr = spsizr; g_spmax = spmax; g_wslotr = wslotr; g_prefixing = prefixing; g_fncact = fncact; g_fncnv = fncnv; g_fnspath = fnspath; g_fnrpath = fnrpath; g_xfrxla = xfrxla; if (pv[SND_PRO].ival > -1) { /* Change according to switch */ protocol = pv[SND_PRO].ival; if (ptab[protocol].rpktlen > -1) /* copied from initproto() */ urpsiz = ptab[protocol].rpktlen; if (ptab[protocol].spktflg > -1) spsizf = ptab[protocol].spktflg; if (ptab[protocol].spktlen > -1) { spsiz = ptab[protocol].spktlen; if (spsizf) spsizr = spmax = spsiz; } if (ptab[protocol].winsize > -1) wslotr = ptab[protocol].winsize; if (ptab[protocol].prefix > -1) prefixing = ptab[protocol].prefix; if (ptab[protocol].fnca > -1) fncact = ptab[protocol].fnca; if (ptab[protocol].fncn > -1) fncnv = ptab[protocol].fncn; if (ptab[protocol].fnsp > -1) fnspath = ptab[protocol].fnsp; if (ptab[protocol].fnrp > -1) fnrpath = ptab[protocol].fnrp; } debug(F101,"xsend protocol","",protocol); if (pv[SND_NOB].ival > -1) { /* /NOBACKUP (skip backup file) */ g_skipbup = skipbup; skipbup = 1; } if (pv[SND_REC].ival > 0) /* /RECURSIVE */ recursive = 2; if (pv[SND_TYP].ival > -1) { /* /TYPE */ xfiletype = pv[SND_TYP].ival; if (xfiletype == 2) xfiletype = -1; } g_binary = binary; /* Save global transfer mode */ #ifdef PATTERNS g_patterns = patterns; /* Save FILE PATTERNS setting */ #endif /* PATTERNS */ if (pv[SND_BIN].ival > 0) { /* Change according to switch */ /* If they said /BINARY they mean /BINARY */ patterns = 0; /* So no pattern-based switching */ g_xfermode = xfermode; /* or automatic transfer mode */ xfermode = XMODE_M; binary = XYFT_B; debug(F101,"doxsend /BINARY xfermode","",xfermode); } else if (pv[SND_TXT].ival > 0) { /* Ditto for /TEXT */ patterns = 0; g_xfermode = xfermode; xfermode = XMODE_M; binary = XYFT_T; debug(F101,"doxsend /TEXT xfermode","",xfermode); } else if (pv[SND_IMG].ival > 0) { #ifdef VMS binary = XYFT_I; #else binary = XYFT_B; #endif /* VMS */ } #ifdef CK_LABELED else if (pv[SND_LBL].ival > 0) { binary = XYFT_L; } #endif /* CK_LABELED */ debug(F101,"xsend binary","",binary); if (pv[SND_XPA].ival > 0) /* /TRANSPARENT */ xfrxla = 0; /* Don't translate character sets */ /* Check for legal combinations of switches, filenames, etc */ #ifdef PIPESEND if (pv[SND_CMD].ival > 0) { /* COMMAND - strip any braces */ debug(F110,"SEND /COMMAND before stripping",s,0); s = brstrip(s); debug(F110,"SEND /COMMAND after stripping",s,0); if (!*s) { printf("?Sorry, a command to send from is required\n"); x = -9; goto xsendx; } cmarg = s; } #endif /* PIPESEND */ /* Set up /MOVE and /RENAME */ if (pv[SND_DEL].ival > 0 && (pv[SND_MOV].ival > 0 || pv[SND_REN].ival > 0)) { printf("?Sorry, /DELETE conflicts with /MOVE or /RENAME\n"); x = -9; goto xsendx; } #ifdef CK_TMPDIR if (pv[SND_MOV].ival > 0) { int len; char * p = pv[SND_MOV].sval; #ifdef CK_LOGIN if (isguest) { printf("?Sorry, /MOVE-TO not available to guests\n"); x = -9; goto xsendx; } #endif /* CK_LOGIN */ len = strlen(p); if (!isdir(p)) { /* Check directory */ #ifdef CK_MKDIR char * s = NULL; s = (char *)malloc(len + 4); if (s) { strcpy(s,p); /* safe */ #ifdef datageneral if (s[len-1] != ':') { s[len++] = ':'; s[len] = NUL; } #else if (s[len-1] != '/') { s[len++] = '/'; s[len] = NUL; } #endif /* datageneral */ s[len++] = 'X'; s[len] = NUL; x = zmkdir(s); free(s); if (x < 0) { printf("?Can't create \"%s\"\n",p); x = -9; goto xsendx; } } #else printf("?Directory \"%s\" not found\n",p); x = -9; goto xsendx; #endif /* CK_MKDIR */ } zfnqfp(p,LINBUFSIZ,tmpbuf); makestr(&snd_move,tmpbuf); } #endif /* CK_TMPDIR */ if (pv[SND_REN].ival > 0) { /* /RENAME */ char * p = pv[SND_REN].sval; #ifdef CK_LOGIN if (isguest) { printf("?Sorry, /RENAME-TO not available to guests\n"); x = -9; goto xsendx; } #endif /* CK_LOGIN */ if (!p) p = ""; if (!*p) { printf("?New name required for /RENAME\n"); x = -9; goto xsendx; } p = brstrip(p); #ifndef NOSPL /* If name given is wild, rename string must contain variables */ if (wild) { char * s = tmpbuf; x = TMPBUFSIZ; zzstring(p,&s,&x); if (!strcmp(tmpbuf,p)) { printf( "?/RENAME for file group must contain variables such as \\v(filename)\n" ); x = -9; goto xsendx; } } #endif /* NOSPL */ makestr(&snd_rename,p); } /* Handle /RECOVER and /START */ #ifdef CK_RESEND if (pv[SND_RES].ival > 0 && binary != XYFT_B && !filepeek #ifdef PATTERNS && !patterns #else #ifdef VMS /* VMS sets text/binary automatically later when it opens the file */ && 0 #endif /* VMS */ #endif /* PATTERNS */ ) { printf("?Sorry, /BINARY required\n"); x = -9; goto xsendx; } if (pv[SND_STA].wval > 0) { /* /START */ if (wild) { printf("?Sorry, wildcards not permitted with /START\n"); x = -9; goto xsendx; } if (sizeof(int) < 4) { printf("?Sorry, this command needs at least 32-bit integers\n"); x = -9; goto xsendx; } #ifdef CK_XYZ if (protocol != PROTO_K) { printf("?Sorry, SEND /START works only with Kermit protocol\n"); x = -9; goto xsendx; } #endif /* CK_XYZ */ } #ifdef CK_XYZ if (pv[SND_RES].ival > 0) { if (protocol != PROTO_K && protocol != PROTO_Z) { printf( "Sorry, /RECOVER is possible only with Kermit or ZMODEM protocol\n" ); x = -9; goto xsendx; } } #endif /* CK_XYZ */ #endif /* CK_RESEND */ if (protocol == PROTO_K) { if ((pv[SND_MAI].ival > 0 || /* MAIL */ pv[SND_PRI].ival > 0 || /* PRINT */ pv[SND_RES].ival > 0 /* RESEND */ ) && (!atdiso || !atcapr)) { /* Disposition attribute off? */ printf("?Sorry, ATTRIBUTE DISPOSITION must be ON\n"); x = -9; goto xsendx; } } #ifdef CK_XYZ if (wild && (protocol == PROTO_X || protocol == PROTO_XC)) { printf( "Sorry, you can only send one file at a time with XMODEM protocol\n" ); x = -9; goto xsendx; } #endif /* CK_XYZ */ if (!confirmed) { /* CR not typed yet, get more fields */ char *m; if (mlist) { /* MSEND or MMOVE */ nfils = 0; /* We already have the first one */ #ifndef NOMSEND msfiles[nfils++] = line; /* Store pointer */ lp = line + (int)strlen(line) + 1; /* Point past it */ debug(F111,"xsend msend",msfiles[nfils-1],nfils-1); while (1) { /* Get more filenames */ char *p; if ((x = cmifi("Names of files to send, separated by spaces", "", &s,&y,xxstring)) < 0) { if (x != -3) goto xsendx; if ((x = cmcfm()) < 0) goto xsendx; break; } msfiles[nfils++] = lp; /* Got one, count it, point to it, */ p = lp; /* remember pointer, */ while ((*lp++ = *s++)) /* and copy it into buffer */ if (lp > (line + LINBUFSIZ)) { /* Avoid memory leak */ printf("?MSEND list too long\n"); line[0] = NUL; x = -9; goto xsendx; } debug(F111,"xsend msend",msfiles[nfils-1],nfils-1); if (nfils == 1) fspec[0] = NUL; /* Take care of \v(filespec) */ #ifdef ZFNQFP zfnqfp(p,TMPBUFSIZ,tmpbuf); p = tmpbuf; #endif /* ZFNQFP */ if (((int)strlen(fspec) + (int)strlen(p) + 1) < fspeclen) { strcat(fspec,p); /* safe */ strcat(fspec," "); /* safe */ } else #ifdef COMMENT printf("WARNING - \\v(filespec) buffer overflow\n"); #else debug(F101,"doxsend filespec buffer overflow","",0); #endif /* COMMENT */ } #endif /* NOMSEND */ } else { /* Regular SEND */ char *p; int y; nfils = -1; if (pv[SND_MAI].ival > 0) m = (pv[SND_MAI].sval) ? "e-mail address (optional)" : "e-mail address (required)"; else if (pv[SND_PRI].ival > 0) m = "printer options (optional)"; else if (wild) m = "\nOptional as-name template containing replacement variables \ like \\v(filename)"; else m = "Optional name to send it with"; if ((x = cmtxt(m,"",&p,NULL)) < 0) goto xsendx; if (!p) p = ""; if (*p) { /* If some text was given... */ p = brstrip(p); /* Replace /AS-NAME: value if any */ if ((y = strlen(p)) > 0) { if (pv[SND_MAI].ival > 0) { makestr(&pv[SND_MAI].sval, p); } else { if (pv[SND_ASN].sval) free(pv[SND_ASN].sval); pv[SND_ASN].sval = malloc(y+1); if (pv[SND_ASN].sval) { strcpy(pv[SND_ASN].sval,p); /* safe */ pv[SND_ASN].ival = 1; } } } } } } /* Set cmarg2 from as-name, however we got it. */ if (pv[SND_ASN].ival > 0 && pv[SND_ASN].sval && !*cmarg2) { int x; x = strlen(line); ckstrncpy(line+x+2,pv[SND_ASN].sval,LINBUFSIZ-x-1); cmarg2 = line+x+2; debug(F110,"doxsend cmarg2",cmarg2,0); } #ifndef NOFRILLS if ((pv[SND_MAI].ival > 0) && (pv[SND_PRI].ival > 0)) { printf("Sorry, /MAIL and /PRINT are conflicting options\n"); x = -9; goto xsendx; } n = 0; /* /MAIL or /PRINT? */ if (pv[SND_MAI].ival > 0) n = SND_MAI; else if (pv[SND_PRI].ival > 0) n = SND_PRI; if (n) { /* Yes... */ #ifdef DEBUG char * p; if (n == SND_MAI) p = "/MAIL"; else p = "/PRINT"; debug(F111,"xsend",p,n); #endif /* DEBUG */ #ifdef CK_XYZ if (protocol != PROTO_K) { printf("Sorry, %s available only with Kermit protocol\n", (n == SND_MAI) ? "/MAIL" : "/PRINT" ); x = -9; goto xsendx; } #endif /* CK_XYZ */ debug(F101,"xsend print/mail wild","",wild); *optbuf = NUL; /* Wipe out any old options */ s = pv[n].sval; /* mail address or print switch val */ if (!s) s = ""; debug(F110,"doxsend mail address or printer options",s,0); if (n == SND_MAI && !*s) { printf("?E-mail address required\n"); x = -9; goto xsendx; } else if ((int)strlen(s) > 94) { /* Ensure legal size */ printf("?%s too long\n", (n == SND_MAI) ? "E-mail address" : "Print option string" ); x = -9; goto xsendx; } ckstrncpy(optbuf,s,OPTBUFLEN); /* OK, copy to option buffer */ cmarg = line; /* File to send */ if (n == SND_MAI) { debug(F110,"xsend mailing",cmarg,0); debug(F110,"xsend address:",optbuf,0); rmailf = 1; } else { debug(F110,"xsend printing",cmarg,0); debug(F110,"xsend options",optbuf,0); rprintf = 1; } } #endif /* NOFRILLS */ #ifdef CALIBRATE if (pv[SND_CAL].wval > 0) { /* Handle /CALIBRATE */ if (confirmed) { calibrate = pv[SND_CAL].wval * (CK_OFF_T)1024; sndsrc = -9; nfils = 1; wild = 0; #ifndef NOMSEND addlist = 0; #endif /* NOMSEND */ ckstrncpy(line,"CALIBRATION",LINBUFSIZ); s = cmarg = line; if (!cmarg2) cmarg2 = ""; debug(F110,"doxsend cmarg2 calibrate",cmarg2,0); } else if (line[0]) { calibrate = 0; pv[SND_CAL].ival = 0; pv[SND_CAL].wval = 0; } } #endif /* CALIBRATE */ if (pv[SND_FIL].ival > 0) { if (confirmed && !calibrate) { if (zopeni(ZMFILE,pv[SND_FIL].sval) < 1) { debug(F110,"xsend can't open",pv[SND_FIL].sval,0); printf("?Failure to open %s\n",filefile); x = -9; goto xsendx; } makestr(&filefile,pv[SND_FIL].sval); /* Open, remember name */ debug(F110,"xsend opened",filefile,0); wild = 1; } } /* SEND alone... */ #ifndef NOSPL if (confirmed && pv[SND_ARR].ival > 0) { if (!*cmarg2) { sndxnam[7] = (char)((arrayx == 1) ? 64 : arrayx + ARRAYBASE); cmarg2 = sndxnam; } cmarg = ""; goto sendend; } #endif /* NOSPL */ if (confirmed && !line[0] && !filefile && !calibrate) { #ifndef NOMSEND if (filehead) { /* OK if we have a SEND-LIST */ nfils = filesinlist; sndsrc = nfils; /* Like MSEND */ addlist = 1; /* But using a different list... */ filenext = filehead; goto sendend; } #endif /* NOMSEND */ printf("?Filename required but not given\n"); x = -9; goto xsendx; } /* Not send-list or array */ #ifndef NOMSEND addlist = 0; /* Don't use SEND-LIST. */ filenext = NULL; #endif /* NOMSEND */ if (mlist) { /* MSEND or MMOVE */ #ifndef NOMSEND cmlist = msfiles; /* List of files to send */ sndsrc = nfils; cmarg2 = ""; sendstart = (CK_OFF_T)0; #endif /* NOMSEND */ #ifdef PIPESEND pipesend = 0; #endif /* PIPESEND */ } else if (filefile) { /* File contains list of filenames */ s = ""; cmarg = ""; cmarg2 = ""; line[0] = NUL; nfils = 1; sndsrc = 1; } else if (!calibrate && pv[SND_ARR].ival < 1 && pv[SND_CMD].ival < 1) { nfils = sndsrc = -1; /* Not MSEND, MMOVE, /LIST, or /ARRAY */ if ( /* or /COMMAND */ #ifndef NOFRILLS !rmailf && !rprintf /* Not MAIL or PRINT */ #else 1 #endif /* NOFRILLS */ ) { CK_OFF_T y = (CK_OFF_T)1; if (!wild) y = zchki(s); if (y < (CK_OFF_T)0) { printf("?Read access denied - \"%s\"\n", s); x = -9; goto xsendx; } if (s != line) /* We might already have done this. */ ckstrncpy(line,s,LINBUFSIZ); /* Copy of string just parsed. */ else debug(F110,"doxsend line=s",line,0); cmarg = line; /* File to send */ } zfnqfp(cmarg,fspeclen,fspec); } if (!mlist) { /* For all but MSEND... */ #ifdef PIPESEND if (pv[SND_CMD].ival > 0) /* /COMMAND sets pipesend flag */ pipesend = 1; debug(F101,"xsend /COMMAND pipesend","",pipesend); if (pipesend && filefile) { printf("?Invalid switch combination\n"); x = -9; goto xsendx; } #endif /* PIPESEND */ #ifndef NOSPL /* If as-name given and filespec is wild, as-name must contain variables */ debug(F111,"doxsend cmarg2 wild",cmarg2,wild); if (wild && *cmarg2) { char * s = tmpbuf; x = TMPBUFSIZ; zzstring(cmarg2,&s,&x); if (!strcmp(tmpbuf,cmarg2)) { printf( "?As-name for file group must contain variables such as \\v(filename)\n" ); x = -9; goto xsendx; } } #endif /* NOSPL */ /* Strip braces from as-name */ debug(F110,"xsend cmarg2 before stripping",cmarg2,0); cmarg2 = brstrip(cmarg2); debug(F110,"xsend filename",cmarg,0); debug(F110,"xsend as-name",cmarg2,0); /* Copy as-name to a safe place */ if (asnbuf) { free(asnbuf); asnbuf = NULL; } if ((y = strlen(cmarg2)) > 0) { asnbuf = (char *) malloc(y + 1); if (asnbuf) { strcpy(asnbuf,cmarg2); /* safe */ cmarg2 = asnbuf; } else cmarg2 = ""; } #ifdef CK_RESEND debug(F111,"xsend pv[SND_STA].ival","",pv[SND_STA].ival); if (pv[SND_STA].wval > (CK_OFF_T)-1) { /* /START position */ if (wild) { printf("?/STARTING-AT may not be used with multiple files.\n"); x = -9; goto xsendx; } else sendstart = pv[SND_STA].wval; } else sendstart = (CK_OFF_T)0; debug(F101,"xsend /STARTING","",sendstart); #endif /* CK_RESEND */ } sendend: /* Common successful exit */ moving = 0; if (pv[SND_SHH].ival > 0) { /* SEND /QUIET... */ g_displa = fdispla; fdispla = 0; debug(F101,"xsend display","",fdispla); } #ifndef NOSPL /* SEND /ARRAY... */ if (pv[SND_ARR].ival > 0) { if (!ap) { x = -2; goto xsendx; } /* (shouldn't happen) */ if (range[0] == -1) /* If low end of range not specified */ range[0] = 1; /* default to 1 */ if (range[1] == -1) /* If high not specified */ range[1] = a_dim[arrayx]; /* default to size of array */ if ((range[0] < 0) || /* Check range */ (range[0] > a_dim[arrayx]) || (range[1] < range[0]) || (range[1] > a_dim[arrayx])) { printf("?Bad array range - [%d:%d]\n",range[0],range[1]); x = -9; goto xsendx; } sndarray = ap; /* Array pointer */ sndxin = arrayx; /* Array index */ sndxlo = range[0]; /* Array range */ sndxhi = range[1]; sndxnam[7] = (char)((sndxin == 1) ? 64 : sndxin + ARRAYBASE); #ifdef COMMENT printf("SENDING FROM ARRAY: &%c[]...\n", /* debugging */ (sndxin == 1) ? 64 : sndxin + ARRAYBASE); printf("Lo=%d\nHi=%d\n", sndxlo, sndxhi); printf("cmarg=[%s]\ncmarg2=[%s]\n", cmarg, cmarg2); while ((x = agnbyte()) > -1) { putchar((char)x); } return(1); #endif /* COMMENT */ } #endif /* NOSPL */ if (pv[SND_ARR].ival < 1) { /* File selection & disposition... */ if (pv[SND_DEL].ival > 0) /* /DELETE was specified */ moving = 1; debug(F101,"xsend /DELETE","",moving); if (pv[SND_AFT].ival > 0) /* Copy SEND criteria */ ckstrncpy(sndafter,pv[SND_AFT].sval,19); if (pv[SND_BEF].ival > 0) ckstrncpy(sndbefore,pv[SND_BEF].sval,19); if (pv[SND_NAF].ival > 0) ckstrncpy(sndnafter,pv[SND_NAF].sval,19); if (pv[SND_NBE].ival > 0) ckstrncpy(sndnbefore,pv[SND_NBE].sval,19); if (pv[SND_EXC].ival > 0) makelist(pv[SND_EXC].sval,sndexcept,NSNDEXCEPT); if (pv[SND_SMA].wval > (CK_OFF_T)-1) sndsmaller = pv[SND_SMA].wval; if (pv[SND_LAR].wval > (CK_OFF_T)-1) sndlarger = pv[SND_LAR].wval; if (pv[SND_NAM].ival > -1) { g_fncnv = fncnv; /* Save global value */ fncnv = pv[SND_NAM].ival; debug(F101,"xsend fncnv","",fncnv); } if (pv[SND_PTH].ival > -1) { g_spath = fnspath; /* Save global values */ fnspath = pv[SND_PTH].ival; #ifndef NZLTOR if (fnspath != PATH_OFF) { g_fncnv = fncnv; /* Bad bad... */ fncnv = XYFN_C; } #endif /* NZLTOR */ debug(F101,"xsend fnspath","",fnspath); debug(F101,"xsend fncnv","",fncnv); } } #ifdef PIPESEND if (pv[SND_FLT].ival > 0) { makestr(&sndfilter,pv[SND_FLT].sval); debug(F110,"xsend /FILTER", sndfilter, 0); } #endif /* PIPESEND */ #ifdef CK_APC /* MOVE not allowed in APCs */ if (moving && (apcactive == APC_LOCAL || apcactive == APC_REMOTE) && !(apcstatus & APC_UNCH)) return(success = 0); #endif /* CK_APC */ #ifdef IKS_OPTION if ( #ifdef CK_XYZ protocol == PROTO_K && #endif /* CK_XYZ */ !iks_wait(KERMIT_REQ_START,1)) { printf("?A Kermit Server is not available to process this command.\n"); printf("?Start a RECEIVE command to complement this command.\n"); } #endif /* IKS_OPTION */ #ifdef IKSD #ifdef CK_LOGIN if (moving && inserver && isguest) { printf("?File deletion not allowed for guests.\n"); return(-9); } #endif /* CK_LOGIN */ #endif /* IKSD */ sstate = 's'; /* Set start state to SEND */ sndcmd = 1; #ifdef CK_RESEND if (pv[SND_RES].ival > 0) /* Send sendmode appropriately */ sendmode = SM_RESEND; else if (pv[SND_STA].ival > 0) sendmode = SM_PSEND; else #endif /* CK_RESEND */ if (mlist) sendmode = SM_MSEND; else sendmode = SM_SEND; #ifdef MAC what = W_SEND; scrcreate(); #endif /* MAC */ if (local && pv[SND_SHH].ival != 0) { /* If in local mode, */ displa = 1; /* turn on file transfer display */ } x = 0; xsendx: /* Common exit, including failure */ debug(F101,"doxsend sndsrc","",sndsrc); for (i = 0; i <= SND_MAX; i++) { /* Free malloc'd memory */ if (pv[i].sval) free(pv[i].sval); } return(x); } #endif /* NOXFER */ #ifndef NOLOCAL /* D O X C O N N -- CONNECT command parsing with switches */ #ifdef XLIMITS #define XLIMORTRIGGER #else #ifdef CK_TRIGGER #define XLIMORTRIGGER #endif /* CK_TRIGGER */ #endif /* XLIMITS */ #ifdef CKTIDLE int tt_idlelimit = 0; /* Terminal idle limit */ int tt_idleact = IDLE_RET; /* Terminal idle action */ #endif /* CKTIDLE */ #ifdef OS2 /* K95 only: */ extern int tt_idlesnd_tmo; /* Idle interval */ int tt_timelimit = 0; /* Time limit, 0 = none */ extern char * /* Parse results - strings: */ tt_idlesnd_str; /* Idle string */ #endif /* OS2 */ #ifdef CK_TRIGGER extern char *tt_trigger[]; extern CHAR *tt_trmatch[]; extern char *triggerval; static char *g_tt_trigger[TRIGGERS]; #endif /* CK_TRIGGER */ #ifdef OS2 static int g_tt_idlesnd_tmo, g_tt_timelimit; /* For saving and restoring */ static int g_tt_idlelimit, g_tt_saved = 0; static char * g_tt_idlesnd_str; /* global settings */ #endif /* OS2 */ static struct stringint pv[CONN_MAX+1]; VOID resconn() { int i; #ifdef OS2 if ( g_tt_saved ) { tt_idlelimit = g_tt_idlelimit; tt_idlesnd_tmo = g_tt_idlesnd_tmo; tt_timelimit = g_tt_timelimit; tt_idlesnd_str = g_tt_idlesnd_str; g_tt_saved = 0; } #endif /* OS2 */ #ifdef CK_TRIGGER for (i = 0; i < TRIGGERS; i++) tt_trigger[i] = g_tt_trigger[i]; #endif /* CK_TRIGGER */ for (i = 0; i <= CONN_MAX; i++) { /* Free malloc'd memory */ if (pv[i].sval) free(pv[i].sval); pv[i].sval = NULL; } } int doxconn(cx) int cx; { int c, i, n; /* Workers */ int x, y; int getval = 0; /* Whether to get switch value */ int async = 0; /* Make an async connect */ struct FDB sw, cm; /* FDBs for each parse function */ extern FILE * tfile[]; extern char * macp[]; #ifdef OS2 g_tt_idlesnd_tmo = tt_idlesnd_tmo; /* Save global settings */ g_tt_timelimit = tt_timelimit; g_tt_idlelimit = tt_idlelimit; g_tt_idlesnd_str = tt_idlesnd_str; g_tt_saved = 1; #endif /* OS2 */ #ifdef CK_TRIGGER if (!tt_trigger[0]) { /* First initialization */ for (i = 1; i < TRIGGERS; i++) tt_trigger[i] = NULL; } for (i = 0; i < TRIGGERS; i++) g_tt_trigger[i] = tt_trigger[i]; if (triggerval) { free(triggerval); triggerval = NULL; } #endif /* CK_TRIGGER */ for (i = 0; i <= CONN_MAX; i++) { /* Initialize switch values */ pv[i].sval = NULL; /* to null pointers */ pv[i].ival = -1; /* and -1 int values */ pv[i].wval = (CK_OFF_T)-1; } if (cx == XXCQ) /* CQ == CONNECT /QUIETLY */ pv[CONN_NV].ival = 1; /* Set up chained parse functions... */ cmfdbi(&sw, /* First FDB - command switches */ _CMKEY, /* fcode */ "Switch", /* hlpmsg */ "", /* default */ "", /* addtl string data */ nconntab, /* addtl numeric data 1: tbl size */ 4, /* addtl numeric data 2: 4 = cmswi */ xxstring, /* Processing function */ conntab, /* Keyword table */ &cm /* Pointer to next FDB */ ); cmfdbi(&cm, /* 2nd FDB - Confirmation */ _CMCFM, /* fcode */ "", /* hlpmsg */ "", /* default */ "", /* addtl string data */ 0, /* addtl numeric data 1 */ 0, /* addtl numeric data 2 */ NULL, NULL, NULL ); while (1) { /* Parse 0 or more switches */ x = cmfdb(&sw); /* Parse switch or confirmation */ debug(F101,"doxconn cmfdb","",x); if (x < 0) { /* Error */ if (x == -9 || x == -2) printf("?No switches match - \"%s\"\n",atmbuf); goto xconnx; /* or reparse needed */ } if (cmresult.fcode != _CMKEY) /* Break out if not a switch */ break; c = cmgbrk(); /* Get break character */ getval = (c == ':' || c == '='); /* to see how they ended the switch */ if (getval && !(cmresult.kflags & CM_ARG)) { printf("?This switch does not take arguments\n"); x = -9; goto xconnx; } if (!getval && (cmgkwflgs() & CM_ARG)) { printf("?This switch requires an argument\n"); return(-9); } n = cmresult.nresult; /* Numeric result = switch value */ debug(F101,"doxconn switch","",n); switch (n) { /* Process the switch */ #ifdef OS2 case CONN_AS: /* Asynchronous */ pv[CONN_AS].ival = 1; pv[CONN_SY].ival = 0; break; case CONN_SY: /* Synchronous */ pv[CONN_SY].ival = 1; pv[CONN_AS].ival = 0; break; #endif /* OS2 */ case CONN_NV: /* Non-verbal */ pv[n].ival = 1; break; #ifdef XLIMITS case CONN_II: /* Idle-interval */ case CONN_IL: /* Idle-limit */ case CONN_TL: /* Time-limit */ if (!getval) break; if ((x = cmnum("Seconds","0",10,&y,xxstring)) < 0) goto xconnx; pv[n].ival = y; break; case CONN_IS: /* Idle-string */ #endif /* XLIMITS */ #ifdef CK_TRIGGER case CONN_TS: /* Trigger-string */ #endif /* CK_TRIGGER */ #ifdef XLIMORTRIGGER if (!getval) break; if ((x = cmfld("String (enclose in braces if it contains spaces)", "",&s,xxstring)) < 0) { if (x == -3) { printf("?String required\n"); x = -9; } goto xconnx; } if (n != CONN_TS) s = brstrip(s); if ((y = strlen(s)) > 0) { if (pv[n].sval) free(pv[n].sval); pv[n].sval = malloc(y+1); if (pv[n].sval) { strcpy(pv[n].sval,s); /* safe */ pv[n].ival = 1; } } break; #endif /* XLIMORTRIGGER */ default: printf("?Unexpected switch value - %d\n",cmresult.nresult); x = -9; goto xconnx; } } debug(F101,"doxconn cmresult.fcode","",cmresult.fcode); if (cmresult.fcode != _CMCFM) { printf("?Unexpected function code: %d\n",cmresult.fcode); x = -9; goto xconnx; } /* Command was confirmed so we can pre-pop command level. */ /* This is so CONNECT module won't think we're executing a script */ /* if CONNECT was the final command in the script. */ if (cmdlvl > 0) prepop(); #ifdef OS2 /* Make results available globally */ if (pv[CONN_IL].ival > -1) /* Idle limit */ tt_idlelimit = pv[CONN_IL].ival; if (pv[CONN_II].ival > -1) /* Idle limit */ tt_idlesnd_tmo = pv[CONN_II].ival; if (pv[CONN_IS].sval) /* Idle string */ if (tt_idlesnd_str = (char *)malloc((int)strlen(pv[CONN_IS].sval)+1)) strcpy(tt_idlesnd_str,pv[CONN_IS].sval); /* safe */ if (pv[CONN_TL].ival > -1) /* Session limit */ tt_timelimit = pv[CONN_TL].ival; async = (pv[CONN_AS].ival > 0 || pv[CONN_SY].ival <= 0 && cmdlvl == 0) ? 1 : 0; #endif /* OS2 */ #ifdef CK_TRIGGER if (pv[CONN_TS].sval) /* Trigger strings */ makelist(pv[CONN_TS].sval,tt_trigger,TRIGGERS); for (i = 0; i < TRIGGERS; i++) /* Trigger match pointers */ tt_trmatch[i] = NULL; if (triggerval) { /* Reset trigger value */ free(triggerval); triggerval = NULL; } #endif /* CK_TRIGGER */ #ifdef SSHCMD /* 2010/03/01... The previous connection was through the external ssh client and now, with that connection closed, the user says "connect" and expects a new connection to be made to the same host, because that's how all the other connection methods work, so (and this is quite a hack)... */ if (!ckstrcmp("ssh ",ttname,4,0)) { /* Previous "host" was "ssh blah" */ _PROTOTYP (int redossh, ( void ) ); extern int ttyfd; if (ttyfd < 0) { /* And connection is no longer open */ int xx; xx = redossh(); /* So redo the SSH connection */ if (xx < 0) return(xx); goto xconnx; } } #endif /* SSHCMD */ x = doconect((pv[CONN_NV].ival > 0) ? 1 : 0, async); { int xx; debug(F101,"doxconn doconect returns","",x); if ((xx = ttchk()) < 0) dologend(); debug(F101,"doxconn ttchk returns","",xx); } #ifdef CK_TRIGGER debug(F111,"doxconn doconect triggerval",triggerval,x); #endif /* CK_TRIGGER */ xconnx: /* Back from CONNECT -- Restore global settings */ if (!async) resconn(); success = (x > 0) ? 1 : 0; return(x); } #endif /* NOLOCAL */ #ifdef ADDCMD /* cx == XXADD or XXREMV */ /* fc == ADD_BIN or ADD_TXT */ static int doadd(cx,fc) int cx, fc; { #ifdef PATTERNS char * tmp[FTPATTERNS]; char **p = NULL; int i, j, k, n = 0, x = 0, last; #endif /* PATTERNS */ if (cx != XXADD && cx != XXREMV) { printf("?Unexpected function code: %d\n",cx); return(-9); } #ifdef PATTERNS while (n < FTPATTERNS) { /* Collect new patterns */ tmp[n] = NULL; if ((x = cmfld("Pattern","",&s,xxstring)) < 0) break; ckstrncpy(line,s,LINBUFSIZ); s = brstrip(line); makestr(&(tmp[n++]),s); } if (x == -3) x = cmcfm(); if (x < 0) goto xdoadd; p = (fc == ADD_BIN) ? binpatterns : txtpatterns; /* Which list */ last = 0; for (i = 0; i < FTPATTERNS; i++) { /* Find last one in list */ if (!p[i]) { last = i; break; } } if (cx == XXADD) { /* Adding */ if (last + n > FTPATTERNS) { /* Check if too many */ printf("?Too many patterns - %d is the maximum\n", FTPATTERNS); goto xdoadd; } for (i = 0; i < n; i++) { /* Copy in the new ones. */ for (j = 0, x = 0; x == 0 && j < last ; j++ ) x = !ckstrcmp(tmp[i],p[j],-1,filecase); /* match */ if (x == 0) makestr(&(p[last++]),tmp[i]); } makestr(&(p[last]),NULL); /* Null-terminate the list */ x = 1; goto xdoadd; /* Done */ } else if (cx == XXREMV) { /* Remove something(s) */ int j, k; if (last == 0) /* List is empty */ goto xdoadd; /* Nothing to remove */ for (i = 0; i < n; i++) { /* i = Patterns they typed */ for (j = 0; j < last; j++) { /* j = Patterns in list */ /* Change this to ckstrcmp()... */ if (filecase) x = !ckstrcmp(tmp[i],p[j],-1,filecase); /* match */ else x = ckstrcmp(tmp[i],p[j],-1,0); /* Case-independent match */ if (x) { /* This one matches */ makestr(&(p[j]),NULL); /* Free it */ for (k = j; k < last; k++) /* Move the rest up */ p[k] = p[k+1]; p[k] = NULL; /* Erase last one */ if (!p[k]) break; } } } } xdoadd: /* Common exit */ for (i = 0; i < n; i++) if (tmp[i]) free(tmp[i]); return(x); #endif /* PATTERNS */ } /* ADD SEND-LIST */ static int addsend(cx) int cx; { #ifndef NOMSEND extern struct keytab fttab[]; extern int nfttyp; struct filelist * flp; char * fmode = ""; int xmode = 0; int xbinary = 0; #endif /* NOMSEND */ #ifdef NOMSEND printf("?Sorry, ADD/REMOVE SEND-LIST not available.\n"); return(-9); #endif /* NOMSEND */ if (cx == XXREMV) { printf("?Sorry, REMOVE SEND-LIST not implemented yet.\n"); return(-9); } #ifndef NOMSEND #ifndef XYZ_INTERNAL if (protocol != PROTO_K) { printf("?Sorry, ADD SEND-LIST does not work with external protocols\n"); return(-9); } #endif /* XYZ_INTERNAL */ x = cmifi("File specification to add","", &s,&y,xxstring); if (x < 0) { if (x == -3) { printf("?A file specification is required\n"); return(-9); } else return(x); } ckstrncpy(tmpbuf,s,TMPBUFSIZ); s = tmpbuf; if (filesinlist == 0) /* Take care of \v(filespec) */ fspec[0] = NUL; zfnqfp(s,LINBUFSIZ,line); s = line; if (((int)strlen(fspec) + (int)strlen(s) + 1) < fspeclen) { strcat(fspec,s); /* safe */ strcat(fspec," "); /* safe */ } else printf("WARNING - \\v(filespec) buffer overflow\n"); xbinary = binary; if ((patterns || filepeek) /* FILE PATTERNS or SCAN is ON */ #ifdef CK_LABELED && binary != XYFT_L /* And not if FILE TYPE LABELED */ #endif /* CK_LABELED */ #ifdef VMS && binary != XYFT_I /* or FILE TYPE IMAGE */ #endif /* VMS */ ) { int k, x; x = -1; k = scanfile(line,&x,nscanfile); if (k > 0) xbinary = (k == FT_BIN) ? XYFT_B : XYFT_T; } fmode = gfmode(xbinary,0); if ((x = cmkey(fttab,nfttyp, "type of file transfer", fmode, xxstring)) < 0) return(x); xmode = x; cmarg2 = ""; if ((x = cmfld(y ? "\nAs-name template containing replacement variables such as \\v(filename)" : "Name to send it with", "",&s,NULL)) < 0) if (x != -3) return(x); #ifndef NOSPL if (y && *s) { char * p = tmpbuf; x = TMPBUFSIZ; zzstring(s,&p,&x); if (!strcmp(tmpbuf,s)) { printf( "?As-name for file group must contain variables such as \\v(filename)\n" ); return(-9); } } #endif /* NOSPL */ ckstrncpy(tmpbuf,s,TMPBUFSIZ); cmarg2 = tmpbuf; if ((x = cmcfm()) < 0) return(x); flp = (struct filelist *) malloc(sizeof(struct filelist)); if (flp) { if (filetail) filetail->fl_next = flp; filetail = flp; if (!filehead) filehead = flp; x = (int) strlen(line); /* Length of filename */ s = (char *) malloc(x + 1); if (s) { strcpy(s,line); /* safe */ flp->fl_name = s; flp->fl_mode = xmode; x = (int) strlen(cmarg2); /* Length of as-name */ if (x < 1) { flp->fl_alias = NULL; } else { s = (char *) malloc(x + 1); if (s) { strcpy(s,cmarg2); /* safe */ flp->fl_alias = s; } else { printf("Sorry, can't allocate space for as-name"); return(-9); } } flp->fl_next = NULL; filesinlist++; /* Count this node */ return(success = 1); /* Finished adding this node */ } else { printf("Sorry, can't allocate space for name"); return(-9); } } else { printf("Sorry, can't allocate file list node"); return(-9); } #endif /* NOMSEND */ } #endif /* ADDCMD */ #ifndef NOHTTP /* HTTP ops... */ #ifdef TCPSOCKET #define HTTP_GET 0 /* GET */ #define HTTP_PUT 1 /* PUT */ #define HTTP_POS 2 /* POST */ #define HTTP_IDX 3 /* INDEX */ #define HTTP_HED 4 /* HEAD */ #define HTTP_DEL 5 /* DELETE */ #define HTTP_CON 6 /* CONNECT */ #define HTTP_OPN 7 /* OPEN */ #define HTTP_CLS 8 /* CLOSE */ static struct keytab httptab[] = { { "close", HTTP_CLS, 0 }, { "connect", HTTP_CON, 0 }, { "delete", HTTP_DEL, 0 }, { "get", HTTP_GET, 0 }, { "head", HTTP_HED, 0 }, { "index", HTTP_IDX, 0 }, { "open", HTTP_OPN, 0 }, { "put", HTTP_PUT, 0 }, { "post", HTTP_POS, 0 } }; static int nhttptab = sizeof(httptab)/sizeof(struct keytab); /* HTTP switches */ #define HT_SW_AG 0 /* /AGENT */ #define HT_SW_HD 1 /* /HEADER */ #define HT_SW_US 2 /* /USER */ #define HT_SW_PW 3 /* /PASSWORD */ #define HT_SW_AR 4 /* /ARRAY */ #define HT_SW_TP 5 /* /TOSCREEN */ static struct keytab httpswtab[] = { { "/agent", HT_SW_AG, CM_ARG }, #ifndef NOSPL { "/array", HT_SW_AR, CM_ARG }, #endif /* NOSPL */ { "/header", HT_SW_HD, CM_ARG }, { "/password", HT_SW_PW, CM_ARG }, { "/toscreen", HT_SW_TP, 0 }, { "/user", HT_SW_US, CM_ARG }, { "", 0, 0 } }; static int nhttpswtab = sizeof(httpswtab)/sizeof(struct keytab) - 1; /* HTTP PUT/POST switches */ #define HT_PP_MT 0 /* /MIME-TYPE */ static struct keytab httpptab[] = { { "/mime-type", HT_PP_MT, CM_ARG }, { "", 0, 0 } }; static int nhttpptab = sizeof(httpptab)/sizeof(struct keytab) - 1; #define HTTP_MAXHDR 8 static int xdohttp(action, lfile, rf, dfile, agent, hdr, user, pass, mime, array, type) int action; char *lfile, *rf, *dfile, *agent, *hdr, *user, *pass, *mime, array; int type; /* xdohttp */ { int i, rc = 0; char * hdrlist[HTTP_MAXHDR]; char rfile[CKMAXPATH+1]; extern int httpfd; /* Check for a valid state to execute the command */ if (inserver) { printf("?The HTTP command may not be used from the IKS\r\n"); } else if (httpfd == -1) { if (http_reopen() < 0) printf("?No connection\n"); else rc = 1; } else { rc = 1; } /* If the command is not valid, exit with failure */ if (rc == 0) return(success = 0); if (action != HTTP_CON && rf[0] != '/') { rfile[0] = '/'; ckstrncpy(&rfile[1],rf,CKMAXPATH); } else { ckstrncpy(rfile,rf,CKMAXPATH); } for (i = 0; i < HTTP_MAXHDR; i++) /* Initialize header list */ hdrlist[i] = NULL; makelist(hdr,hdrlist,HTTP_MAXHDR); /* Make header list */ #ifdef BETADEBUG for (i = 0; i < nhttptab; i++) /* Find action keyword */ if (httptab[i].kwval == action) break; if (i == nhttptab) { /* Shouldn't happen... */ printf("?Invalid action - %d\n",action); return(0); /* Failure */ } printf("HTTP action: %s\n",httptab[i].kwd); printf(" Agent: %s\n",agent ? agent : "(null)"); if (hdrlist[1]) { printf(" Header list: 1. %s\n",hdrlist[0]); for (i = 1; i < HTTP_MAXHDR && hdrlist[i]; i++) printf("%15d. %s\n",i+1,hdrlist[i]); } else printf(" Header: %s\n",hdrlist[0] ? hdrlist[0] : "(null)"); printf(" User: %s\n",user ? user : "(null)"); #ifdef COMMENT printf(" Password: %s\n",pass ? pass : "(null)"); #endif /* COMMENT */ #ifndef NOSPL if (array) printf(" Array: \\%%%c[]\n", array); else printf(" Array: (none)\n"); #endif /* NOSPL */ if (action == HTTP_PUT || action == HTTP_POS) printf(" Mime-type: %s\n",mime ? mime : "(null)"); printf(" Local file: %s\n",lfile ? lfile : "(null)"); printf(" Remote file: %s\n",rfile ? rfile : "(null)"); printf(" Destination file: %s\n",dfile ? dfile : "(null)"); #endif /* BETADEBUG */ /* The http_xxxx() functions return 0 on success, -1 on failure */ switch (action) { case HTTP_CON: { extern int ttyfd; rc = http_connect(httpfd,agent,hdrlist,user,pass,array,rfile); break; } case HTTP_DEL: rc = http_delete(agent,hdrlist,user,pass,array,rfile); break; case HTTP_GET: rc = http_get(agent,hdrlist,user,pass,array,lfile,rfile,type); break; case HTTP_HED: rc = http_head(agent,hdrlist,user,pass,array,lfile,rfile,type); break; case HTTP_PUT: rc = http_put(agent,hdrlist,mime,user,pass,array,lfile,rfile,dfile, type); break; case HTTP_POS: rc = http_post(agent,hdrlist,mime,user,pass,array,lfile,rfile,dfile, type); break; case HTTP_IDX: rc = http_index(agent,hdrlist,user,pass,array,lfile,rfile,type); break; default: rc = -1; } return(rc == 0 ? 1 : 0); /* Success is set by caller */ } #endif /* TCPSOCKET */ #endif /* NOHTTP */ #ifndef NOSPL /* ARRAY ops... */ static struct keytab arraytab[] = { { "clear", ARR_CLR, 0 }, { "copy", ARR_CPY, 0 }, { "dcl", ARR_DCL, CM_INV }, { "declare", ARR_DCL, 0 }, { "destroy", ARR_DST, CM_INV }, { "equate", ARR_EQU, CM_INV }, { "link", ARR_EQU, 0 }, { "resize", ARR_RSZ, 0 }, { "set", ARR_SET, 0 }, #ifndef NOSHOW { "show", ARR_SHO, 0 }, #endif /* NOSHOW */ { "sort", ARR_SRT, 0 }, { "undeclare", ARR_DST, 0 }, { "", 0, 0 } }; static int narraytab = sizeof(arraytab)/sizeof(struct keytab) - 1; #ifdef CKLEARN static struct keytab learnswi[] = { { "/close", 2, 0 }, { "/off", 0, 0 }, { "/on", 1, 0 } }; #endif /* CKLEARN */ int arrayitoa(x) int x; { /* Array index to array letter */ if (x == 1) return(64); else if (x < 0 || x > (122 - ARRAYBASE)) return(-1); else return(x + ARRAYBASE); } int arrayatoi(c) int c; { /* Array letter to array index */ if (c == 64) c = 96; if (c > 63 && c < 91) c += 32; if (c < ARRAYBASE || c > 122) return(-1); return(c - ARRAYBASE); } static int /* Declare an array */ dodcl(cx) int cx; { int i, n, v, lo, hi, rc = 0; int isdynamic = 0; char tmpbuf[64]; char ** p = NULL; char tmp[64]; /* Local temporary string buffer */ if ((y = cmfld("Array name","",&s,NULL)) < 0) { /* Parse array name */ if (y == -3) { printf("?Array name required\n"); return(-9); } else return(y); } ckstrncpy(line,s,LINBUFSIZ); s = line; x = arraybounds(s,&lo,&hi); /* Check syntax and get bounds */ debug(F111,"dodcl arraybounds",s,x); if (x < 0) { /* Error - Maybe it's a variable */ char * p; /* whose value is an array name */ int n; p = tmpbuf; n = 63; p[0] = NUL; if (s[0] == CMDQ && s[1] == '&') s++; if (zzstring(s,&p,&n) > -1) { s = tmpbuf; x = arraybounds(s,&lo,&hi); debug(F111,"dodcl arraybounds 2",s,x); } if (x < 0) { printf("?Bad array name - \"%s\"\n",s); return(-9); } } debug(F101,"dodcl hi","",hi); debug(F101,"dodcl lo","",lo); debug(F101,"dodcl lo+1","",lo+1); if (lo == -1 && hi == -1) { /* Have good array name and bounds */ isdynamic = 1; n = CMDBL / 5; } else if (hi > -1) { printf("?Segment notation not allowed in array declarations\n"); return(-9); } else if ((lo+1) < 0) { debug(F101,"dodcl underflow","",lo+1); printf("?Dimension underflow\n"); return(-9); } else n = lo; x = arrayitoa(x); if (cx == XXUNDCL) { n = 0; v = 0; if ((y = cmcfm()) < 0) return(y); } else { p = (char **)malloc(sizeof(char **)*(n+1)); if (!p) { printf("?Memory allocation error\n"); return(-9); } v = 0; /* Highest initialized member */ p[0] = NULL; /* Element 0 */ keepallchars = 1; while (n > 0 && v < n) { /* Parse initializers */ p[v+1] = NULL; ckmakxmsg(tmp, 64, "Initial value for \\&", ckctoa((char)x), "[", ckitoa(v+1), "]", NULL,NULL,NULL,NULL,NULL,NULL,NULL ); rc = cmfld((char *)tmp,"",&s,xxstring); /* Get field */ if (rc < 0) { /* Error... */ if (rc == -3) { /* Empty element */ if (cmflgs == 1) /* because end of line? */ break; /* Yes, done initializing */ else /* No, it's just empty */ continue; /* Go on to next one. */ } else { /* Other parse error */ goto dclx; /* Go free temp pointers */ } } rc = 1; if (v == 0 && !strcmp(s,"=")) /* Skip the = sign. */ continue; s = brstrip(s); /* Strip any braces */ makestr(&(p[++v]),s); } keepallchars = 0; if ((y = cmtxt("Carriage return to confirm","",&s,NULL)) < 0) return(y); if (isdynamic) n = v; } if (dclarray((char)x,n) < 0) { /* Declare the array */ printf("?Declare failed\n"); goto dclx; } for (i = 1; i <= v; i++) { /* Add any initial values */ tmp[0] = '&'; ckmakmsg(&tmp[1],63,ckctoa((char)x),"[",ckitoa(i),"]"); if (addmac(tmp,p[i]) < 0) { printf("Array initialization error: %s %s\n",tmp,p[i]); rc = -9; goto dclx; } } dclx: if (p) { for (i = 1; i <= v; i++) if (p[i]) free(p[i]); free((char *)p); } debug(F101,"DCL rc","",rc); return(success = rc); } static int rszarray() { int i, x, y, n, lo, hi, islink = -1; char c, * s, ** ap = NULL; if ((x = cmfld("Array name","",&s,NULL)) < 0) { /* Parse array name */ if (x == -3) { printf("?Array name required\n"); return(-9); } else return(x); } ckstrncpy(line,s,LINBUFSIZ); /* Make safe copy of name */ s = line; x = arraybounds(s,&lo,&hi); if (x < 0) { /* Parse the name, get index */ printf("?Bad array reference - \"%s\"\n", s); return(-9); } if (lo < 0 && hi < 0) { y = cmnum("New size","",10,&lo,xxstring); if (y < 0) { if (y == -3) printf("?New size required\n"); return(y); } } if ((y = cmcfm()) < 0) return(y); if (a_link[x] > -1) { /* Link? */ islink = x; /* Yes follow it */ x = a_link[x]; /* and remember */ } if (!a_ptr[x]) { printf("?Array not declared - \"%s\"\n", s); return(-9); } if (lo < 0) { printf("?New size required\n"); return(-9); } if (hi > -1) { printf("?Array segments not allowed for this operation\n"); return(-9); } c = arrayitoa(x); /* Get array letter */ if (c == '@') { /* Argument vector array off limits */ printf("?Sorry, \\&@[] is read-only\n"); return(-9); } if (lo == 0) { /* If new size is 0... */ dclarray(c,0); /* Undeclare the array */ return(success = 1); } n = a_dim[x]; /* Current size */ ap = (char **) malloc((lo+1) * sizeof(char *)); /* New array */ y = (n < lo) ? n : lo; for (i = 0; i <= y; i++) /* Copy the part that fits */ ap[i] = a_ptr[x][i]; if (n < lo) { /* If original array smaller */ for (; i <= lo; i++) /* initialize extra elements in */ ap[i] = NULL; /* new array to NULL. */ } else if (n > lo) { /* If new array smaller */ for (; i <= lo; i++) /* deallocate leftover elements */ makestr(&(a_ptr[x][i]),NULL); /* from original array. */ } free((char *)a_ptr[x]); /* Free original array list */ a_ptr[x] = ap; /* Replace with new one */ a_dim[x] = lo; /* Record the new dimension */ if (islink > -1) { /* Was this a link? */ a_ptr[islink] = ap; /* If so point to the resized array */ a_dim[islink] = lo; } else { /* If not are there links to here? */ for (i = 0; i < (int) 'z' - ARRAYBASE; i++) { /* Any linked arrays? */ if (i != x && a_link[i] == x) { /* Find and update them */ a_ptr[i] = ap; a_dim[i] = lo; } } } return(success = 1); } static int copyarray() { int i, j, x1, lo1, hi1, x2, lo2, hi2, whole = 0; char c1, c2, * a1, * a2; if ((y = cmfld("Name of source array","",&s,NULL)) < 0) return(y); ckstrncpy(line,s,LINBUFSIZ); a1 = line; if ((x1 = arraybounds(a1,&lo1,&hi1)) < 0) { printf("?Bad array reference - \"%s\"\n", a1); return(-9); } else if (!a_ptr[x1]) { printf("?Array not declared - \"%s\"\n", a1); return(-9); } c1 = arrayitoa(x1); if ((y = cmfld("Name of destination array","",&s,NULL)) < 0) return(y); ckstrncpy(tmpbuf,s,TMPBUFSIZ); a2 = tmpbuf; if ((x2 = arraybounds(a2,&lo2,&hi2)) < 0) { printf("?Bad array reference - \"%s\"\n", a2); return(-9); } c2 = arrayitoa(x2); if ((x = cmcfm()) < 0) return(x); if (c2 == '@') { /* Argument vector array off limits */ printf("?Sorry, \\&@[] is read-only\n"); return(-9); } if (lo1 < 0 && lo2 < 0 && hi1 < 0 && hi2 < 0) /* Special case for */ whole = 1; /* whole array... */ if (lo1 < 0) lo1 = whole ? 0 : 1; /* Supply lower bound of source */ if (hi1 < 0) hi1 = a_dim[x1]; /* Supply upper bound of source */ if (lo2 < 0) lo2 = whole ? 0 : 1; /* Lower bound of target */ if (hi2 < 0) hi2 = lo2 + hi1 - lo1; /* Upper bound of target */ if (a_ptr[x2]) { /* Target array is already declared? */ if (hi2 > a_dim[x2]) /* If upper bound out of range */ hi2 = a_dim[x2]; /* shrink to fit */ } else { /* Otherwise... */ x2 = dclarray(c2, hi2); /* declare the target array */ } for (i = lo1, j = lo2; i <= hi1 && j <= hi2; i++,j++) { /* Copy */ makestr(&(a_ptr[x2][j]),a_ptr[x1][i]); } return(success = 1); } static int /* Undeclare an array */ unarray() { int x, y, n, rc = 0; char c, * s; if ((y = cmfld("Array name","",&s,NULL)) < 0) { /* Parse array name */ if (y == -3) { printf("?Array name required\n"); return(-9); } else return(y); } ckstrncpy(line,s,LINBUFSIZ); /* Make safe copy of name */ s = line; if ((y = cmcfm()) < 0) return(y); if ((x = arraybounds(s,&y,&n)) < 0) { /* Parse the name, get index */ printf("?Bad array reference - \"%s\"\n", s); return(-9); } if (y > 0 || n > 0) { printf("?Partial arrays can not be destroyed\n"); return(-9); } c = arrayitoa(x); /* Get array letter */ if (a_ptr[x]) { /* If array is declared */ if (c == '@') { /* Argument vector array off limits */ printf("?Sorry, \\&@[] is read-only\n"); return(-9); } rc = dclarray(c,-1); /* Undeclare the array */ } else /* It wasn't declared */ rc = 1; if (rc > -1) { /* Set return code and success */ success = 1; rc = 1; } else { success = 0; printf("?Failed - destroy \"\\&%c[]\"\n", c); rc = -9; } return(rc); } static int clrarray(cx) int cx; { int i, x, lo, hi; char c, * s, * val = NULL; if ((x = cmfld("Array name","",&s,NULL)) < 0) { /* Parse array name */ if (x == -3) { printf("?Array name required\n"); return(-9); } else return(x); } ckstrncpy(line,s,LINBUFSIZ); /* Make safe copy of name */ s = line; if (cx == ARR_SET) { /* SET */ if ((x = cmtxt("Value","",&val,xxstring)) < 0) return(x); ckstrncpy(tmpbuf,val,TMPBUFSIZ); /* Value to set */ val = tmpbuf; if (!*val) val = NULL; } else if ((x = cmcfm()) < 0) /* CLEAR */ return(x); if ((x = arraybounds(s,&lo,&hi)) < 0) { /* Parse the name */ printf("?Bad array reference - \"%s\"\n", s); return(-9); } c = arrayitoa(x); /* Get array letter */ if (!a_ptr[x]) { /* If array is declared */ printf("?Array %s is not declared\n", s); return(-9); } else if (c == '@') { /* Argument vector array off limits */ printf("?Sorry, \\&@[] is read-only\n"); return(-9); } if (lo < 0) lo = 0; if (hi < 0) hi = a_dim[x]; for (i = lo; i <= hi; i++) /* Clear/Set selected range */ makestr(&(a_ptr[x][i]),val); return(success = 1); } extern char **aa_ptr[CMDSTKL][28]; extern int aa_dim[CMDSTKL][28]; static int /* Create symbolic link to an array */ linkarray() { int i = 0, x, y, lo, hi, flag = 0; char c, * s, * p; if ((x = cmfld("Array name not currently in use","",&s,NULL)) < 0) { if (x == -3) { printf("?Array name required\n"); return(-9); } else return(x); } ckstrncpy(line,s,LINBUFSIZ); /* Make safe copy of link name */ s = line; if ((x = cmfld("Name of existing array","",&p,xxstring)) < 0) { if (x == -3) { printf("?Array name required\n"); return(-9); } else return(x); } ckstrncpy(tmpbuf,p,TMPBUFSIZ); /* Make safe copy of array name */ p = tmpbuf; if ((x = cmcfm()) < 0) return(x); if ((x = arraybounds(s,&lo,&hi)) < 0) { /* Parse the link name */ printf("?Bad array reference - \"%s\"\n", s); return(-9); } if (a_ptr[x]) { /* Must not already exist */ c = arrayitoa(x); printf("?Array already exists: \\&%c[]\n", c); return(-9); } if (lo > -1 || hi > -1) { printf("?Sorry, whole arrays only: %s\n",s); return(-9); } if ((y = arraybounds(p,&lo,&hi)) < 0) { /* Parse the array name */ printf("?Bad array reference - \"%s\"\n", s); return(-9); } if (lo > -1 || hi > -1) { printf("?Sorry, whole arrays only: %s\n",p); return(-9); } if (x == y) { for (i = cmdlvl; i >= 0; i--) if (aa_ptr[i][x]) { flag++; break; } } if (flag) { a_ptr[x] = aa_ptr[i][y]; /* Link to saved copy */ a_dim[x] = aa_dim[i][y]; } else { /* Otherwise... */ c = arrayitoa(y); /* Check if it's declared */ if (!a_ptr[y]) { printf("?Array is not declared: \\&%c[]\n", c); return(-9); } if (a_link[y] > -1) { /* And if it's a link itself */ printf("?Links to links not allowed: \\&%c[]\n", c); return(-9); } a_ptr[x] = a_ptr[y]; /* All OK, make the link */ a_dim[x] = a_dim[y]; } a_link[x] = y; return(success = 1); } #endif /* NOSPL */ #ifndef NOCSETS static char * dcsetname = NULL; /* Get Display Character-Set Name */ char * getdcset() { char * s; int y; #ifdef PCFONTS extern int tt_font, ntermfont; extern struct keytab term_font[]; #endif /* PCFONTS */ s = ""; #ifdef OS2 y = os2getcp(); /* Default is current code page */ switch (y) { case 437: s = "cp437"; break; case 850: s = "cp850"; break; case 852: s = "cp852"; break; case 857: s = "cp857"; break; case 858: s = "cp858"; break; case 862: s = "cp862"; break; case 866: s = "cp866"; break; case 869: s = "cp869"; break; case 1250: s = "cp1250"; break; case 1251: s = "cp1251"; break; case 1252: s = "cp1252"; break; case 1253: s = "cp1253"; break; case 1254: s = "cp1254"; break; case 1255: s = "cp1255"; break; case 1256: s = "cp1256"; break; case 1257: s = "cp1257"; break; case 1258: s = "cp1258"; break; } #ifdef PCFONTS /* If the user has loaded a font with SET TERMINAL FONT then we want to change the default code page to the font that was loaded. */ if (tt_font != TTF_ROM) { for (y = 0; y < ntermfont; y++ ) { if (term_font[y].kwval == tt_font) { s = term_font[y].kwd; break; } } } #endif /* PCFONTS */ #else /* OS2 */ #ifdef COMMENT /* Hack not needed as of C-Kermit 7.1 */ if (fcharset == FC_1LATIN) { s = "latin1-iso"; /* Hack to avoid reporting "cp1252" */ } else { /* Report current file character set */ #endif /* COMMENT */ for (y = 0; y <= nfilc; y++) if (fcstab[y].kwval == fcharset) { s = fcstab[y].kwd; break; } #ifdef COMMENT } #endif /* COMMENT */ #endif /* OS2 */ makestr(&dcsetname,s); /* Return stable pointer */ return((char *)dcsetname); } #endif /* NOCSETS */ #ifndef NOFRILLS static int doclear() { if ((x = cmkey(clrtab,nclear,"item to clear", #ifdef NOSPL "device-buffer" #else "device-and-input" #endif /* NOSPL */ ,xxstring)) < 0) return(x); #ifndef NOSPL #ifdef OS2 if (x == CLR_CMD || x == CLR_TRM) { if ((z = cmkey(clrcmdtab,nclrcmd,"how much screen to clear\n", "all",xxstring)) < 0) return(z); } #endif /* OS2 */ #endif /* NOSPL */ if ((y = cmcfm()) < 0) return(y); /* Clear device input buffer if requested */ y = (x & CLR_DEV) ? ttflui() : 0; if (x & CLR_SCR) /* CLEAR SCREEN */ y = ck_cls(); /* (= SCREEN CLEAR = CLS) */ if (x & CLR_KBD) { /* CLEAR KEYBOARD */ int n; n = conchk(); y = 0; while (n-- > 0 && (y = coninc(0) > -1)) ; y = (y > -1) ? 0 : -1; } #ifndef NOSPL /* Clear INPUT command buffer if requested */ if (x & CLR_INP) { for (z = 0; z < inbufsize; z++) inpbuf[z] = NUL; inpbp = inpbuf; y = 0; } #ifdef CK_APC if (x & CLR_APC) { debug(F101,"Executing CLEAR APC","",apcactive); apcactive = 0; y = 0; } #endif /* CK_APC */ if (x & CLR_ALR) { setalarm(0L); y = 0; } #endif /* NOSPL */ #ifdef PATTERNS if (x & (CLR_TXT|CLR_BIN)) { int i; for (i = 0; i < FTPATTERNS; i++) { if (x & CLR_TXT) makestr(&txtpatterns[i],NULL); if (x & CLR_BIN) makestr(&binpatterns[i],NULL); } y = 0; } #endif /* PATTERNS */ #ifndef NODIAL if (x & CLR_DIA) { dialsta = DIA_UNK; y = 0; } #endif /* NODIAL */ #ifndef NOMSEND if (x & CLR_SFL) { /* CLEAR SEND-LIST */ if (filehead) { struct filelist * flp, * next; flp = filehead; while (flp) { if (flp->fl_name) free(flp->fl_name); if (flp->fl_alias) free(flp->fl_alias); next = flp->fl_next; free((char *)flp); flp = next; } } filesinlist = 0; filehead = NULL; filetail = NULL; addlist = 0; y = 0; } #endif /* NOMSEND */ #ifdef OS2 #ifndef NOLOCAL switch (x) { case CLR_SCL: clearscrollback(VTERM); break; case CLR_CMD: switch ( z ) { case CLR_C_ALL: clear(); break; case CLR_C_BOS: clrboscr_escape(VCMD,SP); break; case CLR_C_BOL: clrbol_escape(VCMD,SP); break; case CLR_C_EOL: clrtoeoln(VCMD,SP); break; case CLR_C_EOS: clreoscr_escape(VCMD,SP); break; case CLR_C_LIN: clrline_escape(VCMD,SP); break; case CLR_C_SCR: clearscrollback(VCMD); break; default: printf("Not implemented yet, sorry.\n"); break; } break; #ifndef NOTERM case CLR_TRM: switch ( z ) { case CLR_C_ALL: if (VscrnGetBufferSize(VTERM) > 0 ) { VscrnScroll(VTERM, UPWARD, 0, VscrnGetHeight(VTERM)-(tt_status[VTERM]?2:1), VscrnGetHeight(VTERM) - (tt_status[VTERM]?1:0), TRUE, SP ); cleartermscreen(VTERM); } break; case CLR_C_BOS: clrboscr_escape(VTERM,SP); break; case CLR_C_BOL: clrbol_escape(VTERM,SP); break; case CLR_C_EOL: clrtoeoln(VTERM,SP); break; case CLR_C_EOS: clreoscr_escape(VTERM,SP); break; case CLR_C_LIN: clrline_escape(VTERM,SP); break; case CLR_C_SCR: clearscrollback(VTERM); break; default: printf("Not implemented yet, sorry.\n"); break; } break; #endif /* NOTERM */ } y = 0; #endif /* NOLOCAL */ #endif /* OS2 */ return(success = (y == 0)); } #endif /* NOFRILLS */ #ifndef NOSPL static int doeval(cx) int cx; { char *p; char vnambuf[VNAML], * vnp = NULL; /* These must be on the stack */ if (!oldeval) { if ((y = cmfld("Variable name","",&s, ((cx == XX_EVAL) ? xxstring : NULL))) < 0) { if (y == -3) { printf("?Variable name required\n"); return(-9); } else return(y); } ckstrncpy(vnambuf,s,VNAML); /* Make a copy. */ vnp = vnambuf; if (vnambuf[0] == CMDQ && (vnambuf[1] == '%' || vnambuf[1] == '&')) vnp++; y = 0; if (*vnp == '%' || *vnp == '&') { if ((y = parsevar(vnp,&x,&z)) < 0) return(y); } } if ((x = cmtxt("Integer arithmetic expression","",&s,xxstring)) < 0) return(x); p = evala(s); if (!p) p = ""; if (oldeval && *p) printf("%s\n", p); ckstrncpy(evalbuf,p,32); if (!oldeval) return(success = addmac(vnambuf,p)); else return(success = *p ? 1 : 0); } #endif /* NOSPL */ #ifdef TNCODE static int dotelopt() { if ((x = cmkey(telcmd, ntelcmd, "TELNET command", "", xxstring)) < 0 ) return(x); switch (x) { case WILL: case WONT: case DO: case DONT: if ((y = cmkey(tnopts,ntnopts,"TELNET option","",xxstring)) < 0) return(y); if ((z = cmcfm()) < 0) return(z); switch (x) { case WILL: if (TELOPT_UNANSWERED_WILL(y)) return(success = 0); break; case WONT: if (TELOPT_UNANSWERED_WONT(y)) return(success = 0); break; case DO: if (TELOPT_UNANSWERED_DO(y)) return(success = 0); break; case DONT: if (TELOPT_UNANSWERED_DONT(y)) return(success = 0); break; } if (local) { success = ((tn_sopt(x,y) > -1) ? 1 : 0); } else { printf("ff%02x%02x\n",x,y); success = 1; } if (success) { switch (x) { case WILL: TELOPT_UNANSWERED_WILL(y) = 1; break; case WONT: if ( TELOPT_ME(y) ) TELOPT_UNANSWERED_WONT(y) = 1; break; case DO: TELOPT_UNANSWERED_DO(y) = 1; break; case DONT: if ( TELOPT_ME(y) ) TELOPT_UNANSWERED_DONT(y) = 1; break; } if (tn_wait("XXTELOP") < 0) { tn_push(); success = 0; } } return(success); case SB: if ((y=cmkey(tnsbopts,ntnsbopts,"TELNET option","",xxstring)) < 0) return(y); switch (y) { case TELOPT_NAWS: /* Some compilers require switch() to have at least 1 case */ #ifdef CK_NAWS TELOPT_SB(TELOPT_NAWS).naws.x = 0; TELOPT_SB(TELOPT_NAWS).naws.y = 0; if (local) return(success = ((tn_snaws() > -1) ? 1 : 0)); else return(success = 0); #else return(success = 0); #endif /* CK_NAWS */ } return(success = 0); #ifdef CK_KERBEROS #ifdef KRB5 case TN_FWD: success = (kerberos5_forward() == AUTH_SUCCESS); return(success); #endif /* KRB5 */ #endif /* CK_KERBEROS */ default: if ((z = cmcfm()) < 0) return(z); #ifndef NOLOCAL if (local) { CHAR temp[3]; if (network && IS_TELNET()) { /* TELNET */ temp[0] = (CHAR) IAC; temp[1] = x; temp[2] = NUL; success = (ttol((CHAR *)temp,2) > -1 ? 1 : 0); if (tn_deb || debses || deblog) { /* TN_MSG_LEN is in ckctel.h */ ckmakmsg(tn_msg,256,"TELNET SENT ",TELCMD(x),NULL,NULL); debug(F101,tn_msg,"",x); if (debses || tn_deb) tn_debug(tn_msg); } return(success); } return(success = 0); } else { #endif /* NOLOCAL */ printf("ff%02x\n",x); return(success = 1); #ifndef NOLOCAL } #endif /* NOLOCAL */ } } #endif /* TNCODE */ #ifndef NOPUSH #ifndef NOFRILLS static int doedit() { #ifdef OS2 char * p = NULL; #endif /* OS2 */ if (!editor[0]) { s = getenv("EDITOR"); if (s) ckstrncpy(editor,s,CKMAXPATH); editor[CKMAXPATH] = NUL; if (!editor[0]) { printf("?Editor not defined - use SET EDITOR to define\n"); return(-9); } } ckstrncpy(tmpbuf,editfile,TMPBUFSIZ); /* cmiofi() lets us parse the name of an existing file, or the name of a nonexistent file to be created. */ x = cmiofi("File to edit", (char *)tmpbuf, &s, &y, xxstring); debug(F111,"edit",s,x); if (x < 0 && x != -3) return(x); if (x == -3) { tmpbuf[0] = NUL; } else { ckstrncpy(tmpbuf,s,TMPBUFSIZ); } if ((z = cmcfm()) < 0) return(z); if (y) { printf("?A single file please\n"); return(-9); } if (nopush) { printf("?Sorry, editing not allowed\n"); return(success = 0); } if (tmpbuf[0]) { /* Get full path in case we change directories between EDIT commands */ zfnqfp(tmpbuf, CKMAXPATH, editfile); editfile[CKMAXPATH] = NUL; #ifdef OS2 p = editfile; /* Flip the stupid slashes */ while (*p) { if (*p == '/') *p = '\\'; p++; } #endif /* OS2 */ } else editfile[0] = NUL; if (editfile[0]) { if (zchki(editfile) < (CK_OFF_T)0 && zchko(editfile) < 0) { printf("?Access denied: %s\n",editfile); return(-9); } } x = 0; if (editopts[0]) { #ifdef OS2 x = ckindex("%1",(char *)editopts,0,0,1); if (x > 0) editopts[x] = 's'; else #endif /* OS2 */ x = ckindex("%s",(char *)editopts,0,0,1); } if (((int)strlen(editopts) + (int)strlen(editfile) + 1) < TMPBUFSIZ) { if (x) sprintf(tmpbuf,editopts,editfile); else sprintf(tmpbuf,"%s %s",editopts,editfile); } s = line; ckmakmsg(s,LINBUFSIZ,editor," ",tmpbuf,NULL); #ifdef OS2 p = s + strlen(editor); /* And again with the slashes */ while (p != s) { if (*p == '/') *p = '\\'; p--; } #endif /* OS2 */ conres(); x = zshcmd(s); concb((char)escape); return(x); } #endif /* NOFRILLS */ #endif /* NOPUSH */ #ifdef BROWSER static int dobrowse() { #ifdef OS2 char * p = NULL; #endif /* OS2 */ if (nopush) { printf("?Sorry, browsing not allowed\n"); return(success = 0); } #ifndef NT /* Windows lets the Shell Execute the URL if no Browser is defined */ if (!browser[0]) { s = getenv("BROWSER"); if (s) ckstrncpy(browser,s,CKMAXPATH); browser[CKMAXPATH] = NUL; if (!browser[0]) { printf("?Browser not defined - use SET BROWSER to define\n"); return(-9); } } #endif /* NT */ ckstrncpy(tmpbuf,browsurl,TMPBUFSIZ); if ((x = cmtxt("URL",(char *)browsurl,&s,xxstring)) < 0) return(x); ckstrncpy(browsurl,s,4096); x = 0; if (browsopts[0]) { #ifdef OS2 x = ckindex("%1",(char *)browsopts,0,0,1); if (x > 0) browsopts[x] = 's'; else #endif /* OS2 */ x = ckindex("%s",(char *)browsopts,0,0,1); } if (((int)strlen(browsopts) + (int)strlen(browsurl) + 1) < TMPBUFSIZ) { if (x) sprintf(tmpbuf,browsopts,browsurl); else sprintf(tmpbuf,"%s %s",browsopts,browsurl); } #ifdef NT if (!browser[0]) return(success = Win32ShellExecute(browsurl)); #endif /* NT */ s = line; ckmakmsg(s,LINBUFSIZ,browser," ",tmpbuf,NULL); #ifdef OS2 p = line + strlen(browser); /* Flip slashes */ while (p != line) { if (*p == '/') *p = '\\'; p--; } #endif /* OS2 */ conres(); x = zshcmd(s); concb((char)escape); return(x); } #endif /* BROWSER */ #ifdef CK_RECALL static int doredo() { /* Find a previous cmd and redo it */ extern int on_recall, in_recall; int x; char * p; if ((x = cmtxt( "pattern, or first few characters of a previous command", "*",&s,xxstring)) < 0) return(x); ckstrncpy(line,s,LINBUFSIZ); x = strlen(s); s = line; if (*s == '{') { /* Braces disable adding * to end */ if (s[x-1] == '}') { s[x-1] = NUL; s++; x--; } } else { /* No braces, add * to end. */ s[x] = '*'; s[x+1] = NUL; } while (x > 0 && s[x] == '*' && s[x-1] == '*') s[x--] = NUL; if (!on_recall || !in_recall) { printf("?Sorry, command recall can't be used now.\n"); return(-9); } if ((p = cmgetcmd(s))) { /* Look for it history buffer */ ckmakmsg(cmdbuf,CMDBL,p,"\r",NULL,NULL); /* Copy to command buffer */ if (!quiet) /* Echo it */ printf("%s\n",cmdbuf); cmaddnext(); /* Force re-add to history buffer */ return(cmflgs = -1); /* Force reparse */ } else { printf("?Sorry - \"%s\" not found\n", s); return(-9); } } #endif /* CK_RECALL */ #ifndef NOXFER #ifndef NOCSETS static int doassoc() { /* ASSOCIATE */ extern struct keytab tcstab[]; extern int ntcs; if ((x = cmkey(assoctab, nassoc, "", "", xxstring)) < 0 ) return(x); switch (x) { /* Associate what? */ case ASSOC_TC: /* Transfer character-set... */ if ((x = cmkey(tcstab, ntcs, "transfer character-set name","",xxstring)) < 0) return(x); if ((y = cmkey(fcstab, nfilc, "with file character-set","", xxstring)) < 0) if (y != -3) return(y); if ((z = cmcfm()) < 0) return(z); axcset[x] = y; return(success = 1); case ASSOC_FC: /* File character-set... */ if ((x = cmkey(fcstab, nfilc, "file character-set name","",xxstring)) < 0) return(x); if ((y = cmkey(tcstab, ntcs, "with transfer character-set","", xxstring)) < 0) if (y != -3) return(y); if ((z = cmcfm()) < 0) return(z); afcset[x] = y; return(success = 1); default: return(-2); } } #endif /* NOCSETS */ #endif /* NOXFER */ #ifndef NOHELP static int domanual() { #ifdef OS2 if ((x = cmcfm()) < 0) return(x); if (nopush) { printf("?Sorry, access to system commands is disabled.\n"); return(-9); } y = mxlook(mactab,"manual",nmac); if (y > -1) { z = maclvl; /* Save the current maclvl */ dodo(y,NULL,cmdstk[cmdlvl].ccflgs); /* Run the macro */ while (maclvl > z) { debug(F101,"XXMAN loop maclvl 1","",maclvl); sstate = (CHAR) parser(1); debug(F101,"XXMAN loop maclvl 2","",maclvl); if (sstate) proto(); } debug(F101,"XXMAN loop exit maclvl","",maclvl); return(success); } return(success = 0); #else if ((x = cmtxt( #ifdef UNIX "Carriage return to confirm the command, or manual topic", #else "Carriage return to confirm the command, or help topic", #endif /* UNIX */ "kermit", &s, xxstring ) ) < 0) return(x); #endif /* OS2 */ #ifdef UNIX ckmakmsg(tmpbuf,TMPBUFSIZ,"man ",s,NULL,NULL); #else ckmakmsg(tmpbuf,TMPBUFSIZ,"help ",s,NULL,NULL); #endif /* UNIX */ debug(F110,"MANUAL",tmpbuf,0); if (nopush) { printf("?Sorry, access to system commands is disabled.\n"); return(-9); } else { conres(); /* Restore the console */ success = zshcmd(tmpbuf); concb((char)escape); /* Restore CBREAK mode */ return(success); } } #endif /* NOHELP */ #ifndef NOHTTP #ifdef TCPSOCKET static struct keytab sslswtab[] = { { "/ssl", 1, 0 }, { "/tls", 1, 0 } }; #ifndef NOURL struct urldata http_url = {NULL,NULL,NULL,NULL,NULL,NULL,NULL}; #endif /* NOURL */ static int dohttp() { /* HTTP */ struct FDB sw, kw, fi; int n, getval, allinone = 0; char c, * p; char rdns[128]; char * http_agent = NULL; /* Parse results */ char * http_hdr = NULL; char * http_user = NULL; char * http_pass = NULL; char * http_mime = NULL; char * http_lfile = NULL; char * http_rfile = NULL; char * http_dfile = NULL; char http_array = NUL; int http_action = -1; char * http_host = NULL; char * http_srv = NULL; int http_ssl = 0; static char * http_d_agent = NULL; static char * http_d_user = NULL; static char * http_d_pass = NULL; static int http_d_type = 0; int http_type = http_d_type; #ifdef OS2 p = "Kermit 95"; /* Default user agent */ #else p = "C-Kermit"; #endif /* OS2 */ makestr(&http_agent,p); makestr(&http_mime,"text/HTML"); /* MIME type default */ rdns[0] = '\0'; cmfdbi(&sw, /* 1st FDB - general switches */ _CMKEY, /* fcode */ "OPEN, CLOSE, GET, HEAD, PUT, INDEX, or POST,\n or switch", /* hlpmsg */ "", /* default */ "", /* addtl string data */ nhttpswtab, /* addtl numeric data 1: tbl size */ 4, /* addtl numeric data 2: 4 = cmswi */ xxstring, /* Processing function */ httpswtab, /* Keyword table */ &kw /* Pointer to next FDB */ ); cmfdbi(&kw, /* 2nd FDB - commands */ _CMKEY, /* fcode */ "Command", /* hlpmsg */ "", /* default */ "", /* addtl string data */ nhttptab, /* addtl numeric data 1: tbl size */ 0, /* addtl numeric data 2: 0 = keyword */ xxstring, /* Processing function */ httptab, /* Keyword table */ NULL /* Pointer to next FDB */ ); while (1) { x = cmfdb(&sw); /* Parse something */ if (x < 0) /* Error */ goto xhttp; n = cmresult.nresult; if (cmresult.fdbaddr == &kw) /* Command - exit this loop */ break; c = cmgbrk(); /* Switch... */ getval = (c == ':' || c == '='); x = -9; if (getval && !(cmgkwflgs() & CM_ARG)) { printf("?This switch does not take an argument\n"); goto xhttp; } switch (cmresult.nresult) { /* Handle each switch */ case HT_SW_TP: /* /TOSCREEN */ http_type = 1; break; case HT_SW_AG: /* /AGENT */ if (getval) { if ((x = cmfld("User agent",p,&s,xxstring)) < 0) goto xhttp; } else { s = p; } makestr(&http_agent,s); break; case HT_SW_HD: /* /HEADER */ s = NULL; if (getval) { if ((x = cmfld("Header line","",&s,xxstring)) < 0) { if (x == -3) s = NULL; else goto xhttp; } } makestr(&http_hdr,s); break; case HT_SW_US: /* /USER */ s = NULL; if (getval) { if ((x = cmfld("User ID","",&s,xxstring)) < 0) { if (x == -3) s = ""; else goto xhttp; } } makestr(&http_user,s); break; case HT_SW_PW: /* /PASSWORD */ debok = 0; s = NULL; if (getval) { if ((x = cmfld("Password","",&s,xxstring)) < 0) goto xhttp; } makestr(&http_pass,s); break; #ifndef NOSPL case HT_SW_AR: { /* /ARRAY: */ char * s2, array = NUL; if (!getval) { printf("?This switch requires an argument\n"); x = -9; goto xhttp; } if ((x = cmfld("Array name (a single letter will do)", "", &s, NULL )) < 0) { if (x == -3) { printf("?Array name required\n"); x = -9; goto xhttp; } else goto xhttp; } if (!*s) { printf("?Array name required\n"); x = -9; goto xhttp; } s2 = s; if (*s == CMDQ) s++; if (*s == '&') s++; if (!isalpha(*s)) { printf("?Bad array name - \"%s\"\n",s2); x = -9; goto xhttp; } array = *s++; if (isupper(array)) array = tolower(array); if (*s && (*s != '[' || *(s+1) != ']')) { printf("?Bad array name - \"%s\"\n",s2); http_array = NUL; x = -9; goto xhttp; } http_array = array; break; } #endif /* NOSPL */ default: x = -2; goto xhttp; } } http_action = n; /* Save the action */ if (http_action == HTTP_PUT || http_action == HTTP_POS) { cmfdbi(&sw, /* 1st FDB - switch */ _CMKEY, /* fcode */ "Local filename\n Or switch", /* help */ "", /* default */ "", /* addtl string data */ nhttpptab, /* keyword table size */ 4, /* addtl numeric data 2: 4 = cmswi */ xxstring, /* Processing function */ httpptab, /* Keyword table */ &fi /* Pointer to next FDB */ ); cmfdbi(&fi, /* 2nd FDB - filename */ _CMIFI, /* fcode */ "Local filename", /* hlpmsg */ "", /* default */ "", /* addtl string data */ 0, /* addtl numeric data 1 */ 0, /* addtl numeric data 2 */ xxstring, NULL, NULL ); while (1) { x = cmfdb(&sw); if (x < 0) goto xhttp; /* Free any malloc'd temp strings */ n = cmresult.nresult; if (cmresult.fcode != _CMKEY) break; c = cmgbrk(); /* Switch... */ getval = (c == ':' || c == '='); if (getval && !(cmgkwflgs() & CM_ARG)) { printf("?This switch does not take an argument\n"); x = -9; goto xhttp; } switch (n) { case HT_PP_MT: s = "text/HTML"; if (getval) { if ((x = cmfld("MIME type", "text/HTML",&s,xxstring)) < 0) goto xhttp; } makestr(&http_mime,s); break; default: x = -2; goto xhttp; } } makestr(&http_lfile,cmresult.sresult); n = ckindex("/",http_lfile,-1,1,0); if (n) p = &http_lfile[n]; else p = http_lfile; if ((x = cmfld("URL or remote filename",p,&s,xxstring)) < 0) { if (x == -3) { printf("?%s what?\n",(http_action == HTTP_PUT) ? "Put" : "Post"); x = -9; } goto xhttp; } if (!*s) s = NULL; makestr(&http_rfile,s); if ((x = cmtxt("Response filename","",&s,xxstring)) < 0) { if (x != -3) goto xhttp; } if (*s) makestr(&http_dfile,s); } switch (http_action) { case HTTP_DEL: /* DELETE */ if ((x = cmfld("URL or remote source file","",&s,xxstring)) < 0) { if (x == -3) { printf("?Delete what?\n"); x = -9; } goto xhttp; } makestr(&http_rfile,s); break; case HTTP_CON: /* CONNECT */ if ((x = cmfld("Remote host[:port]","",&s,xxstring)) < 0) { if (x == -3) { printf("?Remote host[:port] is required\n"); x = -9; } goto xhttp; } makestr(&http_rfile,s); break; case HTTP_HED: { /* HEAD */ char buf[CKMAXPATH+1]; if ((x = cmfld("URL or remote source file","",&s,xxstring)) < 0) { if (x == -3) { printf("?Head of what?\n"); x = -9; } goto xhttp; } makestr(&http_rfile,s); if (http_array || http_type) { /* Default result filename */ p = ""; /* None if /ARRAY or /TOSCREEN */ } else { n = ckindex("/",http_rfile,-1,1,0); /* Otherwise strip path */ if (n) /* and add ".head" */ p = &http_rfile[n]; else p = http_rfile; ckmakmsg(buf,CKMAXPATH,p,".head",NULL,NULL); p = buf; } if ((x = cmofi("Local filename",p,&s,xxstring)) < 0) { if (x != -3) goto xhttp; } makestr(&http_lfile,s); break; } case HTTP_GET: /* GET */ case HTTP_IDX: { /* INDEX */ extern int wildena; int tmp; char * lfile = ""; if ((x = cmfld("URL or remote source file","",&s,xxstring)) < 0) { if (x == -3) { printf("?Get what?\n"); x = -9; } goto xhttp; } makestr(&http_rfile,s); if (http_action == HTTP_GET && !http_type) zstrip(http_rfile,&lfile); /* URLs often contain question marks or other metacharacters */ /* cmofi() doesn't like them */ tmp = wildena; wildena = 0; if ((x = cmofi("Local filename",lfile,&s,xxstring)) < 0) { wildena = tmp; if (x != -3) goto xhttp; } wildena = tmp; makestr(&http_lfile,s); break; } case HTTP_OPN: { int sslswitch = 0; #ifdef CK_SSL struct FDB sw, fl; cmfdbi(&sw, _CMKEY, /* fcode */ "IP host name or address, or switch", /* hlpmsg */ "", /* default */ "", /* addtl string data */ 2, /* addtl numeric data 1: tbl size */ 4, /* addtl numeric data 2: 4 = cmswi */ xxstring, /* Processing function */ sslswtab, /* Keyword table */ &fl /* Pointer to next FDB */ ); cmfdbi(&fl, /* 2nd FDB - host */ _CMFLD, /* fcode */ "", /* hlpmsg */ "", /* default */ "", /* addtl string data */ 0, /* addtl numeric data 1 */ 0, /* addtl numeric data 2 */ xxstring, NULL, NULL ); x = cmfdb(&sw); /* Parse switch or host */ if (x < 0) /* Error */ goto xhttp; if (cmresult.fcode == _CMFLD) { /* Host */ s = cmresult.sresult; /* Set up expected pointer */ goto havehost; /* Go parse rest of command */ } sslswitch = 1; /* /SSL or /TLS switch - set flag */ #endif /* CK_SSL */ /* Parse host */ if ((x = cmfld("URL, hostname, or ip-address","",&s,xxstring)) < 0) { if (x == -3) { printf("?Open what?\n"); x = -9; } goto xhttp; } havehost: /* Come here with s -> host */ #ifdef CK_URL x = urlparse(s,&http_url); /* Was a URL given? */ if (x < 1) { /* Not a URL */ #endif /* CK_URL */ makestr(&http_host,s); if ((x = cmfld("Service name or port number", sslswitch ? "https" : "http",&s,xxstring)) < 0) goto xhttp; else makestr(&http_srv,s); #ifdef CK_URL } else if (ckstrcmp(http_url.svc,"http",-1,0) && /* Non-HTTP URL */ ckstrcmp(http_url.svc,"https",-1,0)) { printf("?Non-HTTP URL\n"); x = -9; goto xhttp; } else { /* Have HTTP URL */ makestr(&http_srv, http_url.svc); makestr(&http_user,http_url.usr); makestr(&http_pass,http_url.psw); makestr(&http_host,http_url.hos); if (http_url.por) makestr(&http_srv,http_url.por); makestr(&http_rfile,http_url.pth); } if (http_rfile) { /* Open, GET, and Close */ printf("?Directory/file path not allowed in HTTP OPEN URL\n"); x = -9; goto xhttp; } if (!ckstrcmp("https",http_srv,-1,0) || sslswitch || !ckstrcmp("443",http_srv,-1,0)) http_ssl = 1; #endif /* CK_URL */ break; } case HTTP_CLS: break; } if ((x = cmcfm()) < 0) goto xhttp; if (http_action == HTTP_OPN) { x = (http_open(http_host,http_srv,http_ssl,rdns,128,http_agent) == 0); if (x) { if (!quiet) { if (rdns[0]) printf("Connected to %s [%s]\r\n",http_host,rdns); else printf("Connected to %s\r\n",http_host); } if (http_agent) { if (http_d_agent) free(http_d_agent); http_d_agent = http_agent; http_agent = NULL; } if (http_user) { if (http_d_user) free(http_d_user); http_d_user = http_user; http_user = NULL; } if (http_pass) { if (http_d_pass) { memset(http_d_pass,0,strlen(http_d_pass)); free(http_d_pass); } http_d_pass = http_pass; http_pass = NULL; } http_d_type = http_type; } else { if (!quiet) printf("?HTTP Connection failed.\r\n"); } } else if (http_action == HTTP_CLS) { if (http_d_agent) { free(http_d_agent); http_d_agent = NULL; } if (http_d_user) { free(http_d_user); http_d_user = NULL; } if (http_d_pass) { memset(http_d_pass,0,strlen(http_d_pass)); free(http_d_pass); http_d_pass = NULL; } http_d_type = 0; x = (http_close() == 0); } if ((http_action != HTTP_CLS) && (http_action != HTTP_CON) && http_rfile) { /* Remote file is URL? */ /* All-in-one actions when a URL is given... */ #ifdef CK_URL if (urlparse(http_rfile,&http_url) > 0) { /* Have URL? */ if (ckstrcmp(http_url.svc,"http",-1,0) && /* It's an HTTP URL? */ ckstrcmp(http_url.svc,"https",-1,0)) { printf("?Non-HTTP URL\n"); x = -9; goto xhttp; } else { /* Yes, collect the pieces */ makestr(&http_srv, http_url.svc); makestr(&http_user,http_url.usr); makestr(&http_pass,http_url.psw); makestr(&http_host,http_url.hos); if (http_url.por) makestr(&http_srv,http_url.por); makestr(&http_rfile,http_url.pth); } if (!http_rfile) { /* Still have a path? */ makestr(&http_rfile,"/"); } if (!ckstrcmp("https",http_srv,-1,0) || /* Check for SSL/TLS */ !ckstrcmp("443",http_srv,-1,0)) http_ssl = 1; if (http_isconnected()) /* Close any open HTTP connection */ http_close(); if (http_pass == NULL && http_d_pass != NULL) makestr(&http_pass,http_d_pass); x = (http_open(http_host, http_srv,http_ssl,rdns,128,http_d_agent) == 0); if (x < 0) { x = 0; goto xhttp; } allinone = 1; } #endif /* CK_URL */ if (http_pass == NULL && http_d_pass != NULL) makestr(&http_pass,http_d_pass); if (http_action == HTTP_OPN && allinone) { http_action = HTTP_GET; } x = xdohttp(http_action, http_lfile, http_rfile, http_dfile, http_agent ? http_agent : http_d_agent, http_hdr, http_user ? http_user : http_d_user, http_pass ? http_pass : http_d_pass, http_mime, http_array, http_type ); if (allinone) x = (http_close() == 0); } xhttp: if (http_agent) free(http_agent); if (http_hdr) free(http_hdr); if (http_user) free(http_user); if (http_pass) { memset(http_pass,0,strlen(http_pass)); free(http_pass); } if (http_mime) free(http_mime); if (http_lfile) free(http_lfile); if (http_rfile) free(http_rfile); if (http_dfile) free(http_dfile); if (http_host) free(http_host); if (http_srv) free(http_srv); if (x > -1) success = x; return(x); } #endif /* TCPSOCKET */ #endif /* NOHTTP */ #ifndef NOSPL static int dotrace() { int on = 1; struct FDB sw, kw; cmfdbi(&sw, /* 1st FDB - switch */ _CMKEY, /* fcode */ "Trace object;\n Or switch", /* help */ "", /* default */ "", /* addtl string data */ 2, /* keyword table size */ 4, /* addtl numeric data 2: 4 = cmswi */ xxstring, /* Processing function */ onoffsw, /* Keyword table */ &kw /* Pointer to next FDB */ ); cmfdbi(&kw, /* 2nd FDB - Trace object */ _CMKEY, /* fcode */ "Trace object", /* help */ "all", /* default */ "", /* addtl string data */ ntracetab, /* keyword table size */ 0, /* addtl numeric data 2: 0 = keyword */ xxstring, /* Processing function */ tracetab, /* Keyword table */ NULL /* Pointer to next FDB */ ); if ((x = cmfdb(&sw)) < 0) return(x); if (cmresult.fdbaddr == &sw) { on = cmresult.nresult; if ((x = cmkey(tracetab, ntracetab,"","all",xxstring)) < 0) return(x); } else { x = cmresult.nresult; } if ((y = cmcfm()) < 0) return(y); switch (x) { case TRA_ASG: tra_asg = on; break; case TRA_CMD: tra_cmd = on; break; case TRA_ALL: tra_asg = on; tra_cmd = on; break; default: return(-2); } printf("TRACE %s\n", on ? "ON" : "OFF"); return(success = 1); } #endif /* NOSPL */ static int doprompt() { extern int xcmdsrc; if ((x = cmtxt("Optional message","",&s,xxstring)) < 0) return(x); #ifdef NOSPL printf("?Sorry, PROMPT requires script programming language\n"); return(-9); #else debug(F101,"Prompt cmdlvl","",cmdlvl); cmdlvl++; if (cmdlvl > CMDSTKL) { printf("?Command stack overflow: %d\n",cmdlvl); cmdlvl--; return(-9); } xcmdsrc = CMD_KB; cmdstk[cmdlvl].src = CMD_KB; /* Say we're at the prompt */ cmdstk[cmdlvl].lvl = 0; cmdstk[cmdlvl].ccflgs = cmdstk[cmdlvl-1].ccflgs; if (tra_cmd) printf("[%d] +P: \"(prompt)\"\n",cmdlvl); concb((char)escape); if (!quiet) printf( "(Recursive command prompt: Resume script with CONTINUE, STOP to stop...)\n" ); if (*s) { /* If prompt given */ makestr(&(prstring[cmdlvl-1]),cmgetp()); /* Save current prompt */ cmsetp(s); /* Set new one */ } return(success = 1); #endif /* NOSPL */ } #ifdef CKLEARN VOID learncmd(s) char *s; { /* Record commands in learned script */ char buf[64]; int i, k; if (learnfp && learning) { /* Only if open and on */ k = ckstrncpy(buf,s,64); for (i = 0; i < k; i++) { /* Get top-level command keyword */ if (buf[i] <= SP) { buf[i] = NUL; break; } } k = lookup(cmdtab,buf,ncmd,NULL); /* Look it up */ if (k == XXCON || k == XXLEARN) /* Don't record CONNECT or LEARN */ return; if (k == XXTEL) { fputs("SET HOST /NETWORK:TCP",learnfp); fputs(&s[i],learnfp); fputs(" TELNET /TELNET",learnfp); fputs("\nIF FAIL STOP 1 Connection failed\n",learnfp); } else { fputs(s,learnfp); fputs("\n",learnfp); } } } #endif /* CKLEARN */ #ifdef SSHCMD /* 2010/03/01... Reopen a connection that was made with an external ssh client after it has been closed. */ int redossh() { int x, netsave; x = nettype; debug(F111,"redossh nettype",ttname,nettype); if ((y = setlin(XXSSH,0,1)) < 0) { if (errno) printf("?%s\n",ck_errstr()); else return(y); nettype = x; /* Failed, restore net type. */ success = 0; return(y); } netsave = x; return(y); } #endif /* SSHCMD */ /* Like hmsga() in ckuus2.c but takes a single substitution parameter, s2, which replaces every occurrence of "%s" in the first argument. Added to print text containing the copyright year, so the year doesn't have to be hardwired into lots of scattered text strings. */ int /* Print an array of lines, */ #ifdef CK_ANSIC hmsgaa(char *s[], char *s2) /* pausing at end of each screen. */ #else hmsgaa(s,s2) char *s[]; char *s2; #endif /* CK_ANSIC */ { extern int hmtopline; #ifdef OS2 extern int tt_rows[], tt_cols[]; #else /* OS2 */ extern int tt_rows, tt_cols; #endif /* OS2 */ int x, y, i, j, k, n; if ((x = cmcfm()) < 0) return(x); #ifdef CK_TTGWSIZ #ifdef OS2 ttgcwsz(); #else /* OS2 */ /* Check whether window size changed */ if (ttgwsiz() > 0) { if (tt_rows > 0 && tt_cols > 0) { cmd_rows = tt_rows; cmd_cols = tt_cols; } } #endif /* OS2 */ #endif /* CK_TTGWSIZ */ printf("\n"); /* Start off with a blank line */ n = (hmtopline > 0) ? hmtopline : 1; /* Line counter */ for (i = 0; *s[i]; i++) { printf((char *)s[i],s2); /* Print a line. */ printf("\n"); y = (int)strlen(s[i]); k = 1; for (j = 0; j < y; j++) /* See how many newlines were */ if (s[i][j] == '\n') k++; /* in the string... */ n += k; if (n > (cmd_rows - 3) && *s[i+1]) /* After a screenful, give them */ if (!askmore()) return(0); /* a "more?" prompt. */ else n = 0; } printf("\n"); return(0); } /* D O C M D -- Do a command */ /* Returns: -2: user typed an illegal command -1: reparse needed 0: parse was successful (even tho command may have failed). */ #ifdef DEBUG int cmdstats[256] = { -1, -1 }; #endif /* DEBUG */ int docmd(cx) int cx; { extern int nolocal, cmkwflgs; debug(F101,"docmd entry, cx","",cx); activecmd = cx; doconx = ((activecmd == XXCON) || (activecmd == XXTEL) || (activecmd == XXRLOG) || (activecmd == XXPIPE) || (activecmd == XXIKSD) || (activecmd == XXPTY)); /* Originally all commands were handled with a big switch() statement, but eventually this started blowing up compilers. Now we have a series of separate if statements and small switches, with the commands that are most commonly executed in scipts and loops coming first, to speed up compute-bound scripts. */ #ifdef DEBUG if (cmdstats[0] == -1) { /* Count commands */ int i; /* for tuning... */ for (i = 0; i < 256; i++) cmdstats[i] = 0; } #endif /* DEBUG */ switch (cx) { case -4: /* EOF */ #ifdef OSK if (msgflg) printf("\n"); #else if (msgflg) printf("\r\n"); #endif /* OSK */ doexit(GOOD_EXIT,xitsta); case -3: /* Null command */ return(0); case -9: /* Like -2, but errmsg already done */ case -1: /* Reparse needed */ return(cx); case -6: /* Special */ case -2: /* Error, maybe */ #ifndef NOSPL /* Maybe they typed a macro name. Let's look it up and see. */ if (cx == -6) /* If they typed CR */ ckstrncat(cmdbuf,"\015",CMDBL); /* add it back to command buffer. */ if (ifcmd[cmdlvl] == 2) /* Watch out for IF commands. */ ifcmd[cmdlvl]--; repars = 1; /* Force reparse */ cmres(); cx = XXDO; /* Try DO command */ #else return(cx); #endif /* NOSPL */ default: if (cx < 0) return(cx); break; } #ifdef DEBUG if (cx < 256) cmdstats[cx]++; #endif /* DEBUG */ if ((cmkwflgs & CM_PSH) #ifndef NOPUSH && nopush #endif /* NOPUSH */ ) { printf("?Access to system disabled\n"); return(-9); } if ((cmkwflgs & CM_LOC) #ifndef NOLOCAL && nolocal #endif /* NOLOCAL */ ) { printf("?Connections disabled\n"); return(-9); } #ifndef NOSPL /* Used in FOR loops */ if (cx == XX_INCR || cx == XXINC || /* _INCREMENT, INCREMENT */ cx == XX_DECR || cx == XXDEC) /* _DECREMENT, DECREMENT */ return(doincr(cx)); /* Define (or change the definition of) a macro or variable */ if (cx == XXUNDEF || cx == XXUNDFX) { #ifdef IKSD if (inserver && !ENABLED(en_asg)) { printf("?Sorry, DEFINE/ASSIGN disabled\n"); return(-9); } #endif /* IKSD */ return(doundef(cx)); /* [_]UNDEFINE */ } if (cx == XXDEF || cx == XXASS || cx == XXDFX || cx == XXASX) { #ifdef IKSD if (inserver && !ENABLED(en_asg)) { printf("?Sorry, DEFINE/ASSIGN disabled\n"); return(-9); } #endif /* IKSD */ if (atmbuf[0] == '.' && !atmbuf[1]) /* "." entered as keyword */ xxdot = 1; /* i.e. with space after it... */ return(dodef(cx)); /* DEFINE, ASSIGN, etc... */ } /* IF, WHILE, and friends */ if (cx == XXIF || cx == XXIFX || cx == XXWHI || cx == XXASSER) { return(doif(cx)); } if (cx == XXSWIT) { /* SWITCH */ return(doswitch()); } /* GOTO, FORWARD, and _FORWARD (used internally by FOR, WHILE, etc) */ if (cx == XXGOTO || cx == XXFWD || cx == XXXFWD) { /* GOTO or FORWARD */ /* Note, here we don't set SUCCESS/FAILURE flag */ #ifdef COMMENT if ((y = cmfld("label","",&s,xxstring)) < 0) { if (y == -3) { if (cx != XXXFWD) { printf("?Label name required\n"); return(-9); } } else return(y); } ckstrncpy(tmpbuf,s,TMPBUFSIZ); if ((x = cmcfm()) < 0) return(x); #else if ((y = cmtxt("label","",&s,xxstring)) < 0) { if (y == -3) { if (cx != XXXFWD) { printf("?GOTO: Label name required: \"%s\" \"%s\"\n", atmbuf, cmdbuf); return(-9); } } else return(y); } ckstrncpy(tmpbuf,brstrip(s),TMPBUFSIZ); #endif /* COMMENT */ s = tmpbuf; debug(F111,"GOTO target",s,cx); return(dogoto(s,cx)); } if (cx == XXDO || cx == XXMACRO) { /* DO (a macro) */ char mnamebuf[16]; /* (buffer for controlled temp name) */ struct FDB kw, fl; int mx; /* Macro index (on stack!) */ debug(F101,"XXMACRO 0",line,cx); if (cx == XXDO) { if (nmac == 0) { printf("\n?No macros defined\n"); return(-9); } for (y = 0; y < nmac; y++) { /* copy the macro table into a */ mackey[y].kwd = mactab[y].kwd; /* regular keyword table */ mackey[y].kwval = y; /* with value = pointer to macro tbl */ mackey[y].flgs = mactab[y].flgs; } cmfdbi(&kw, /* First FDB - macro name */ _CMKEY, /* fcode */ "Macro", /* hlpmsg */ "", /* default */ "", /* addtl string data */ nmac, /* addtl numeric data 1: tbl size */ 0, /* addtl numeric data 2: 0 = cmkey */ xxstring, /* Processing function */ mackey, /* Keyword table */ &fl /* Pointer to next FDB */ ); cmfdbi(&fl, /* 2nd FDB - for "{" */ _CMFLD, /* fcode */ "", /* hlpmsg */ "", "", /* addtl string data */ 0, /* addtl numeric data 1 */ 0, /* addtl numeric data 2 */ xxstring, NULL, NULL ); x = cmfdb(&kw); /* Parse something */ if (x < 0) { /* Error */ if (x == -3) { printf("?Macro name required\n"); return(-9); } else return(x); } if (cmresult.fcode == _CMKEY) { extern int mtchanged; char * macroname = NULL; /* In case args include an \fexec() that changes the macro table */ mx = x; /* Save macro index on stack */ mtchanged = 0; /* Mark state of macro table */ makestr(¯oname,mactab[mx].kwd); /* Save name */ if ((y = cmtxt("optional arguments","",&s,xxstring)) < 0) return(y); /* Get macro args */ if (mtchanged) { /* Macro table changed? */ mx = mlook(mactab,macroname,nmac); /* Look up name again */ } if (macroname) free(macroname); return(dodo(mx,s,cmdstk[cmdlvl].ccflgs) < 1 ? (success = 0) : 1); } ckstrncpy(line,cmresult.sresult,LINBUFSIZ); /* _CMFLD */ if (atmbuf[0] == '{') { if ((y = cmcfm()) < 0) return(y); } } else { /* XXMACRO ("immediate macro") */ int k = 0; line[k++] = '{'; line[k++] = SP; line[k] = NUL; debug(F111,"XXMACRO A",line,k); if ((y = cmtxt("Braced list of commands","",&s,xxstring)) < 0) return(y); k = ckstrncpy(line+k,s,LINBUFSIZ-k); debug(F111,"XXMACRO B",line,k); } x = strlen(line); if ((line[0] == '{' && line[x-1] != '}') || line[0] == '}') return(-2); if (line[0] != '{' && line[x-1] != '}') { /* Unknown command. If ON_UNKNOWN_COMMAND macro is defined, */ /* parse args and then execute it, but only if it is not */ /* already active. */ int k = -1; if (!unkmacro) { k = mxlook(mactab,"on_unknown_command",nmac); } if (k > -1) { ckstrncpy(tmpbuf,atmbuf,TMPBUFSIZ); z = maclvl; /* Save the current maclvl */ if ((y = cmtxt("text","",&s,xxstring)) < 0) return(y); ckstrncat(tmpbuf," ",TMPBUFSIZ); ckstrncat(tmpbuf,s,TMPBUFSIZ); unkmacro = 1; debug(F110,"ON_UNKNOWN_COMMAND",s,0); dodo(k,tmpbuf,cmdstk[cmdlvl].ccflgs); /* Run the macro */ while (maclvl > z) { sstate = (CHAR) parser(1); if (sstate) proto(); } debug(F101,"UNKMAC loop exit maclvl","",maclvl); unkmacro = 0; return(success); } if (x > 0) printf("?Not a command or macro name: \"%s\"\n",line); else printf("?Not a command or macro name.\n"); return(-9); } s = brstrip(line); sprintf(mnamebuf," ..tmp:%03d",cmdlvl); /* safe (16) */ x = addmac(mnamebuf,s); return(dodo(x,NULL,cmdstk[cmdlvl].ccflgs) < 1 ? (success = 0) : 1); } if (cx == XXLBL) { /* LABEL */ if ((x = cmfld("label","",&s,xxstring)) < 0) { if (x == -3) { #ifdef COMMENT printf("?LABEL: Label name required: \"%s\"\n", cmdbuf); return(-9); #else s = ""; #endif /* COMMENT */ } else return(x); } debug(F111,"LABEL",s,x); if ((x = cmcfm()) < 0) return(x); return(0); } if (cx == XXEVAL || cx == XX_EVAL) /* _EVALUATE, EVALUATE */ return(doeval(cx)); #ifndef NOSEXP if (cx == XXSEXP) { /* Lisp-like S-Expression */ struct stringarray * q; char /* *p, *r, */ *tmp, *m; int i, k, n, quote = 0, contd = 0, size = 0, len = 0; extern int sexprc, sexppv; tmp = tmpbuf; /* Buffer to collect SEXP */ tmpbuf[0] = NUL; /* Clear it */ size = TMPBUFSIZ; /* Capacity of buffer */ sexprc = -1; /* Assume bad input */ n = 0; /* Paren balance counter */ while (1) { /* Allow SEXP on multiple lines */ m = contd ? "Continuation of S-Expression" : "S-Expression (\"help sexp\" for details)"; x = cmtxt(m,"",&s,xxstring); if (x < 0) return(x); if (!*s) /* Needed for (=) and (:) */ s = cmdbuf+1; /* I can't explain why. */ k = ckmakmsg(tmp, size, contd ? " " : "(", s, NULL, NULL); if (k < 1) { printf("?SEXP too long - %d max\n",TMPBUFSIZ); return(-9); } debug(F111,contd ? "sexp contd" : "sexp",s,k); for (i = len; i < len+k; i++) { /* Check balance */ if (!quote && tmpbuf[i] == CMDQ) { quote = 1; continue; } if (quote) { quote = 0; continue; } if (tmpbuf[i] == '(') n++; else if (tmpbuf[i] == ')') n--; } if (n == 0) { /* Break when balanced */ break; } if (n < 0) { /* Too many right parens */ printf("?Unbalanced S-Expression: \"%s\"\n",tmpbuf); return(-9); } contd++; /* Need more right parens */ cmini(ckxech); /* so keep parsing */ tmp += k; /* adjust buffer pointer */ size -= k; /* and capacity */ len += k; /* and length so far */ } s = tmpbuf; makestr(&lastsexp,s); q = cksplit(1,SEXPMAX,s,NULL,NULL,8,0,0); /* Precheck for > 1 SEXP */ debug(F101,"sexp split","",q->a_size); if (q->a_size == 1) { /* We should get exactly one back */ char * result, * dosexp(); sexprc = 0; /* Reset out-of-band return code */ result = dosexp(s); /* Get result */ debug(F111,"sexp result",result,sexprc); if (sexprc == 0) { /* Success */ /* Echo the result if desired */ if ((!xcmdsrc && sexpecho != SET_OFF) || sexpecho == SET_ON) printf(" %s\n",result ? result : ""); makestr(&sexpval,result); success = sexppv > -1 ? sexppv : 1; return(success); } } if (sexprc < 0) printf("?Invalid S-Expression: \"%s\"\n",lastsexp); return(-9); } #endif /* NOSEXP */ #endif /* NOSPL */ if (cx == XXECH || cx == XXXECH || cx == XXVOID #ifndef NOSPL || cx == XXAPC #endif /* NOSPL */ ) { /* ECHO or APC */ if ((x = cmtxt((cx == XXECH || cx == XXXECH) ? "Text to be echoed" : ((cx == XXVOID) ? "Text" : "Application Program Command text"), "", &s, xxstring ) ) < 0) return(x); if (!s) s = ""; #ifdef COMMENT /* This is to preserve the pre-8.0 behavior but it's too confusing */ x = strlen(s); x = (x > 1) ? ((s[0] == '"' && s[x-1] == '"') ? 1 : 0) : 0; #endif /* COMMENT */ s = brstrip(s); /* Strip braces and doublequotes */ if (cx == XXECH) { /* ECHO */ #ifndef NOSPL if (!fndiags || fnsuccess) { #endif /* NOSPL */ #ifdef COMMENT /* The "if (x)" business preserves previous behavior */ /* by putting back the doublequotes if they were included. */ if (x) printf("\"%s\"\n",s); else #endif /* COMMENT */ printf("%s\n",s); #ifndef NOSPL } #endif /* NOSPL */ } else if (cx == XXXECH) { /* XECHO */ if (x) printf("\"%s\"",s); else printf("%s",s); #ifdef UNIX fflush(stdout); #endif /* UNIX */ } else if (cx == XXAPC) { /* APC */ #ifdef CK_APC if (apcactive == APC_LOCAL || (apcactive == APC_REMOTE && !(apcstatus & APC_UNCH))) return(success = 0); #endif /* CK_APC */ if (!local) { printf("%c_%s%c\\",ESC,s,ESC); #ifdef UNIX fflush(stdout); #endif /* UNIX */ } else { /* Local mode - have connection */ #ifndef NOSPL if (ckmakxmsg(tmpbuf, /* Form APC string in buffer */ TMPBUFSIZ, ckctoa((char)ESC), ckctoa('_'), s, ckctoa((char)ESC), ckctoa('\\'), NULL,NULL,NULL,NULL,NULL,NULL,NULL ) > 0) return(success = dooutput(tmpbuf, XXOUT)); printf("?Too long\n"); return(-9); #else printf("%c_%s%c\\",ESC,s,ESC); #endif /* NOSPL */ } } return(success = 1); } #ifndef NOSPL /* Copy macro args from/to two levels up, used internally by _floop et al. */ if (cx == XXGTA || cx == XXPTA) { /* _GETARGS, _PUTARGS */ int x; debug(F101,"docmd XXGTA","",XXGTA); debug(F101,"docmd cx","",cx); debug(F101,"docmd XXGTA maclvl","",maclvl); x = dogta(cx); debug(F101,"docmd dogta returns","",x); debug(F101,"docmd dogta maclvl","",maclvl); return(x); } #endif /* NOSPL */ #ifndef NOSPL #ifdef CKCHANNELIO if (cx == XXFILE) return(dofile(cx)); else if (cx == XXF_RE || cx == XXF_WR || cx == XXF_OP || cx == XXF_CL || cx == XXF_SE || cx == XXF_RW || cx == XXF_FL || cx == XXF_LI || cx == XXF_ST || cx == XXF_CO) return(dofile(cx)); #endif /* CKCHANNELIO */ /* ASK, ASKQ, READ */ if (cx == XXASK || cx == XXASKQ || cx == XXREA || cx == XXRDBL || cx == XXGETC || cx == XXGETK) { return(doask(cx)); } #endif /* NOSPL */ #ifndef NOFRILLS #ifndef NOHELP if (cx == XXBUG) { /* BUG */ if ((x = cmcfm()) < 0) return(x); return(dobug()); } #endif /* NOHELP */ #endif /* NOFRILLS */ #ifndef NOXFER if (cx == XXBYE) { /* BYE */ extern int ftp_cmdlin; if ((x = cmcfm()) < 0) return(x); #ifdef NEWFTP if ((ftpget == 1) || ((ftpget == 2) && ftpisopen())) { extern int stayflg, ftp_fai; success = ftpbye(); if (ftp_cmdlin && !stayflg && !local) doexit(ftp_fai ? BAD_EXIT : GOOD_EXIT,-1); else return(success); } #endif /* NEWFTP */ if (!local) { printf("?No connection - use EXIT to quit.\n"); return(-9); } #ifdef CK_XYZ if (protocol != PROTO_K) { printf("?Sorry, BYE only works with Kermit protocol\n"); return(-9); } #endif /* CK_XYZ */ #ifdef IKS_OPTION if ( #ifdef CK_XYZ protocol == PROTO_K && #endif /* CK_XYZ */ !iks_wait(KERMIT_REQ_START,1)) { printf( "?A Kermit Server is not available to process this command\n"); return(-9); /* Correct the return code */ } #endif /* IKS_OPTION */ bye_active = 1; sstate = setgen('L',"","",""); if (local) ttflui(); /* If local, flush tty input buffer */ return(0); } #endif /* NOXFER */ if (cx == XXBEEP) { /* BEEP */ int x; #ifdef OS2 int y; if ((y = cmkey(beeptab, nbeeptab, "which kind of beep", "information", xxstring)) < 0 ) return (y); if ((x = cmcfm()) < 0) return(x); bleep((short)y); /* y is one of the BP_ values */ #else /* OS2 */ if ((x = cmcfm()) < 0) return(x); #ifndef NOSPL bleep(BP_NOTE); #else putchar('\07'); #endif /* NOSPL */ #endif /* OS2 */ return(0); } #ifndef NOFRILLS if (cx == XXCLE) /* CLEAR */ return(success = doclear()); #endif /* NOFRILLS */ if (cx == XXCOM) { /* COMMENT */ if ((x = cmtxt("Text of comment line","",&s,NULL)) < 0) return(x); /* Don't change SUCCESS flag for this one */ return(0); } #ifndef NOLOCAL if (cx == XXCON || cx == XXCQ) /* CONNECT or CONNECT /QUIETLY */ return(doxconn(cx)); #endif /* NOLOCAL */ #ifndef NOFRILLS #ifdef ZCOPY if (cx == XXCPY) { /* COPY a file */ #ifdef IKSD if (inserver && !ENABLED(en_cpy)) { printf("?Sorry, COPY is disabled\n"); return(-9); } #endif /* IKSD */ #ifdef CK_APC if (apcactive == APC_LOCAL || (apcactive == APC_REMOTE && !(apcstatus & APC_UNCH)) ) return(success = 0); #endif /* CK_APC */ return(docopy()); } #endif /* ZCOPY */ #ifdef NT if ( cx == XXLINK ) { #ifdef IKSD if (inserver && !ENABLED(en_cpy)) { printf("?Sorry, LINK (COPY) is disabled\n"); return(-9); } #endif /* IKSD */ #ifdef CK_APC if (apcactive == APC_LOCAL || (apcactive == APC_REMOTE && !(apcstatus & APC_UNCH)) ) return(success = 0); #endif /* CK_APC */ return(dolink()); } #endif /* NT */ #endif /* NOFRILLS */ /* CD and friends */ if (cx == XXCWD || cx == XXCDUP || cx == XXBACK || cx == XXLCWD || cx == XXLCDU || cx == XXKCD) { #ifdef LOCUS if (!locus) { if (cx == XXCWD) { #ifdef NOXFER return(-2); #else return(dormt(XZCWD)); #endif /* NOXFER */ } else if (cx == XXCDUP) { #ifdef NOXFER return(-2); #else return(dormt(XZCDU)); #endif /* NOXFER */ } } #endif /* LOCUS */ #ifdef IKSD if (inserver && !ENABLED(en_cwd)) { printf("?Sorry, changing directories is disabled\n"); return(-9); } #endif /* IKSD */ return(success = docd(cx)); } if (cx == XXCHK) /* CHECK */ return(success = dochk()); if (cx == XXCLO) { /* CLOSE */ x = cmkey(clstab,ncls,"\"CONNECTION\", or log or file to close", "connection",xxstring); if (x == -3) { printf("?You must say which file or log\n"); return(-9); } if (x < 0) return(x); if ((y = cmcfm()) < 0) return(y); #ifndef NOLOCAL if (x == 9999) { /* CLOSE CONNECTION */ x = clsconnx(0); switch (x) { case 0: if (msgflg) printf("?Connection was not open\n"); case -1: return(0); case 1: whyclosed = WC_CLOS; return(1); } return(0); } #endif /* NOLOCAL */ y = doclslog(x); success = (y == 1); return(success); } #ifndef NOSPL if (cx == XXDCL || cx == XXUNDCL) { /* DECLARE an array */ return(dodcl(cx)); } #endif /* NOSPL */ #ifndef NODIAL if (cx == XXRED || cx == XXDIAL || cx == XXPDIA || cx == XXANSW || cx == XXLOOK) { /* DIAL, REDIAL etc */ #ifdef VMS extern int batch; #else #ifdef UNIXOROSK extern int backgrd; #endif /* UNIXOROSK */ #endif /* VMS */ x = dodial(cx); debug(F101,"dodial returns","",x); if ((cx == XXDIAL || cx == XXRED || cx == XXANSW) && (x > 0) && /* If DIAL or REDIAL succeeded */ (dialsta != DIA_PART) && /* and it wasn't partial */ (dialcon > 0)) { if ((dialcon == 1 || /* And DIAL CONNECT is ON, */ ((dialcon == 2) && /* or DIAL CONNECT is AUTO */ !xcmdsrc /* and we're at top level... */ #ifdef VMS && !batch /* Not if running from batch */ #else #ifdef UNIXOROSK && !backgrd /* Not if running in background */ #endif /* UNIXOROSK */ #endif /* VMS */ ))) /* Or AUTO */ x = doconect(dialcq, /* Then also CONNECT */ cmdlvl == 0 ? 1 : 0 ); if (ttchk() < 0) dologend(); } return(success = x); } #endif /* NODIAL */ #ifndef NOPUSH #ifdef CK_REXX if (cx == XXREXX) { /* REXX */ extern int nopush; if ( nopush ) return(success=0); return(dorexx()); } #endif /* CK_REXX */ #endif /* NOPUSH */ #ifndef NOFRILLS if (cx == XXDEL || cx == XXLDEL) { /* DELETE */ #ifdef LOCUS if (!locus && cx != XXLDEL) { #ifdef NOXFER return(-2); #else return(dormt(XZDEL)); #endif /* NOXFER */ } #endif /* LOCUS */ #ifdef IKSD if (inserver && (!ENABLED(en_del) #ifdef CK_LOGIN || isguest #endif /* CK_LOGIN */ )) { printf("?Sorry, DELETE is disabled\n"); return(-9); } #endif /* IKSD */ #ifdef CK_APC if ((apcactive == APC_LOCAL) || ((apcactive == APC_REMOTE) && (!(apcstatus & APC_UNCH)))) return(success = 0); #endif /* CK_APC */ return(dodel()); } #endif /* NOFRILLS */ if (cx == XXTOUC) /* TOUCH */ return(dodir(cx)); /* DIRECTORY commands */ if (cx == XXDIR || cx == XXLS || cx == XXLDIR || cx == XXWDIR || cx == XXHDIR) { #ifdef LOCUS if (!locus && cx != XXLDIR) { #ifdef NOXFER return(-2); #else return(dormt(XZDIR)); #endif /* NOXFER */ } #endif /* LOCUS */ #ifdef IKSD if (inserver && !ENABLED(en_dir)) { printf("?Sorry, DIRECTORY is disabled\n"); return(-9); } #endif /* IKSD */ return(dodir(cx)); } #ifndef NOSPL if (cx == XXELS) /* ELSE */ return(doelse()); #endif /* NOSPL */ #ifndef NOSERVER #ifndef NOFRILLS if (cx == XXENA || cx == XXDIS) { /* ENABLE, DISABLE */ s = (cx == XXENA) ? "Server function to enable" : "Server function to disable"; if ((x = cmkey(enatab,nena,s,"",xxstring)) < 0) { if (x == -3) { printf("?Name of server function required\n"); return(-9); } else return(x); } if ((y = cmkey(kmstab,3,"mode","both",xxstring)) < 0) { if (y == -3) { printf("?Please specify remote, local, or both\n"); return(-9); } else return(y); } if (cx == XXDIS) /* Disabling, not enabling */ y = 3 - y; if ((z = cmcfm()) < 0) return(z); #ifdef CK_APC if ((apcactive == APC_LOCAL) || ((apcactive == APC_REMOTE) && (!(apcstatus & APC_UNCH)))) return(success = 0); #endif /* CK_APC */ #ifdef IKSD /* This may seem like it duplicates the work in doenable() */ /* but this code returns failure whereas doenable() returns */ /* success. */ if (inserver && #ifdef IKSDCONF iksdcf && #endif /* IKSDCONF */ (x == EN_HOS || x == EN_PRI || x == EN_MAI || x == EN_WHO || isguest)) return(success = 0); #endif /* IKSD */ return(doenable(y,x)); } #endif /* NOFRILLS */ #endif /* NOSERVER */ #ifndef NOSPL if (cx == XXRET) { /* RETURN */ if ((x = cmtxt("Optional return value","",&s,NULL)) < 0) return(x); s = brstrip(s); /* Strip braces */ if (cmdlvl == 0) /* At top level, nothing happens... */ return(success = 1); switch (cmdstk[cmdlvl].src) { /* Action depends on command source */ case CMD_TF: /* Command file */ popclvl(); /* Pop command level */ return(success = 1); /* always succeeds */ case CMD_MD: /* Macro */ case CMD_KB: /* Prompt */ return(doreturn(s)); /* Trailing text is return value. */ default: /* Shouldn't happen */ return(-2); } } #endif /* NOSPL */ #ifndef NOSPL if (cx == XXOPE) /* OPEN */ return(doopen()); #endif /* NOSPL */ #ifndef NOSPL if (cx == XXOUT || cx == XXLNOUT) { /* OUTPUT or LINEOUT */ if ((x = cmtxt("Text to be output","",&s,NULL)) < 0) return(x); #ifdef CK_APC if ((apcactive == APC_LOCAL) || ((apcactive == APC_REMOTE) && (!(apcstatus & APC_UNCH)))) return(success = 0); #endif /* CK_APC */ debug(F110,"OUTPUT 1",s,0); s = brstrip(s); /* Strip enclosing braces, */ debug(F110,"OUTPUT 2",s,0); /* I don't think I could ever fully explain this in a million years... We have read the user's string without calling the variable-expander function. Now, before we call it, we have to double backslashes that appear before \N, \B, \L, and \ itself, so the expander function will reduce them back to single backslashes, so when we call dooutput()... But it's more complicated than that. */ if (cmdgquo()) { /* Only if COMMAND QUOTING ON ... */ for (x = 0, y = 0; s[x]; x++, y++) { if (s[x] == CMDQ) { char c = s[x+1]; if (c == 'n' || c == 'N' || c == 'b' || c == 'B' || c == 'l' || c == 'L' || c == CMDQ) line[y++] = CMDQ; } line[y] = s[x]; } line[y++] = '\0'; /* Now expand variables, etc. */ debug(F110,"OUTPUT 3",line,0); s = line+y+1; x = LINBUFSIZ - (int) strlen(line) - 1; debug(F101,"OUTPUT size","",x); if (zzstring(line,&s,&x) < 0) return(success = 0); s = line+y+1; debug(F110,"OUTPUT 4",s,0); } success = dooutput(s,cx); return(success); } #endif /* NOSPL */ #ifdef ANYX25 #ifndef IBMX25 if (cx == XXPAD) { /* PAD commands */ x = cmkey(padtab,npadc,"PAD command","",xxstring); if (x == -3) { printf("?You must specify a PAD command to execute\n"); return(-9); } if (x < 0) return(x); switch (x) { case XYPADL: if (x25stat() < 0) printf("Sorry, you must 'set network' & 'set host' first\r\n"); else { x25clear(); initpad(); } break; case XYPADS: if (x25stat() < 0) printf("Not connected\r\n"); else { extern int linkid, lcn; conol("Connected thru "); conol(ttname); printf(", Link id %d, Logical channel number %d\r\n", linkid,lcn); } break; case XYPADR: if (x25stat() < 0) printf("Sorry, you must 'set network' & 'set host' first\r\n"); else x25reset(0,0); break; case XYPADI: if (x25stat() < 0) printf("Sorry, you must 'set network' & 'set host' first\r\n"); else x25intr(0); } return(0); } #endif /* IBMX25 */ #endif /* ANYX25 */ #ifndef NOSPL if (cx == XXPAU || cx == XXWAI || cx == XXMSL) /* PAUSE, WAIT, etc */ return(dopaus(cx)); #endif /* NOSPL */ #ifndef NOFRILLS if (cx == XXPRI) { #ifdef IKSD #ifdef CK_LOGIN if (inserver && (isguest || !ENABLED(en_pri))) { printf("?Sorry, printing is disabled\n"); return(-9); } #endif /* CK_LOGIN */ #endif /* IKSD */ if ((x = cmifi("File to print","",&s,&y,xxstring)) < 0) { if (x == -3) { printf("?A file specification is required\n"); return(-9); } else return(x); } if (y != 0) { printf("?Wildcards not allowed\n"); return(-9); } ckstrncpy(line,s,LINBUFSIZ); s = ""; #ifndef NT if ((x = cmtxt("Local print command options, or carriage return","",&s, xxstring)) < 0) return(x); #endif /* NT */ if ((x = cmcfm()) < 0) return(x); return(success = (zprint(s,line) == 0) ? 1 : 0); } #endif /* NOFRILLS */ #ifdef TCPSOCKET #ifndef NOPUSH if (cx == XXPNG) /* PING an IP host */ return(doping()); #endif /* NOPUSH */ #ifndef NOFTP if (cx == XXFTP) /* FTP */ #ifdef SYSFTP #ifndef NOPUSH return(doftp()); /* Just runs system's ftp program */ #else return(-2); #endif /* NOPUSH */ #else return(doxftp()); #endif /* SYSFTP */ #endif /* NOFTP */ #endif /* TCPSOCKET */ if (cx == XXPWD || cx == XXLPWD) { /* PWD */ #ifdef OS2 char *pwp; #endif /* OS2 */ if ((x = cmcfm()) < 0) return(x); #ifdef LOCUS if (!locus && cx != XXLPWD) { #ifdef NOXFER return(-2); #else return(dormt(XZPWD)); #endif /* NOXFER */ } #endif /* LOCUS */ #ifndef MAC #ifndef OS2 #ifdef UNIX printf("%s\n",zgtdir()); #else xsystem(PWDCMD); #endif /* UNIX */ return(success = 1); /* Blind faith */ #else /* OS2 */ if (pwp = zgtdir()) { if (*pwp) { #ifdef NT line[0] = NUL; ckGetLongPathName(pwp,line,LINBUFSIZ); line[LINBUFSIZ-1] = NUL; tmpbuf[0] = NUL; GetShortPathName(pwp,tmpbuf,TMPBUFSIZ); tmpbuf[TMPBUFSIZ-1] = NUL; pwp = line; if (!strcmp(line,tmpbuf)) { #endif /* NT */ printf("%s\n",pwp); #ifdef NT } else { printf(" Long name: %s\n",line); printf(" Short name: %s\n",tmpbuf); } #endif /* NT */ } return(success = ((int)strlen(pwp) > 0)); } else return(success = 0); #endif /* OS2 */ #else /* MAC */ if (pwp = zgtdir()) { printf("%s\n",pwp); return(success = ((int)strlen(pwp) > 0)); } else return(success = 0); #endif /* MAC */ } if (cx == XXQUI || cx == XXEXI) { /* EXIT, QUIT */ extern int quitting; if ((y = cmnum("exit status code",ckitoa(xitsta),10,&x,xxstring)) < 0) return(y); if ((y = cmtxt("Optional EXIT message","",&s,xxstring)) < 0) return(y); s = brstrip(s); ckstrncpy(line,s,LINBUFSIZ); if (!hupok(0)) /* Check if connection still open */ return(success = 0); if (line[0]) /* Print EXIT message if given */ printf("%s\n",(char *)line); quitting = 1; /* Flag that we are quitting. */ #ifdef VMS doexit(GOOD_EXIT,x); #else #ifdef OSK /* Returning any codes here makes the OS-9 shell print an error message. */ doexit(GOOD_EXIT,-1); #else #ifdef datageneral doexit(GOOD_EXIT,x); #else doexit(x,-1); #endif /* datageneral */ #endif /* OSK */ #endif /* VMS */ } #ifndef NOXFER #ifndef NOFRILLS if (cx == XXERR) { /* ERROR */ #ifdef CK_XYZ if (protocol != PROTO_K) { printf("Sorry, E-PACKET only works with Kermit protocol\n"); return(-9); } #endif /* CK_XYZ */ if ((x = cmcfm()) < 0) return(x); ttflui(); epktflg = 1; sstate = 'a'; return(0); } #endif /* NOFRILLS */ if (cx == XXFIN) { /* FINISH */ #ifdef NEWFTP if ((ftpget == 1) || ((ftpget == 2) && ftpisopen())) return(ftpbye()); #endif /* NEWFTP */ #ifdef CK_XYZ if (protocol != PROTO_K) { printf("Sorry, FINISH only works with Kermit protocol\n"); return(-9); } #endif /* CK_XYZ */ if ((x = cmcfm()) < 0) return(x); #ifdef IKS_OPTION if ( #ifdef CK_XYZ protocol == PROTO_K && #endif /* CK_XYZ */ !iks_wait(KERMIT_REQ_START,1)) { printf( "?A Kermit Server is not available to process this command\n"); return(-9); /* Correct the return code */ } #endif /* IKS_OPTION */ sstate = setgen('F',"","",""); if (local) ttflui(); /* If local, flush tty input buffer */ return(0); } #endif /* NOXFER */ #ifndef NOSPL if (cx == XXFOR) /* FOR loop */ return(dofor()); #endif /* NOSPL */ #ifndef NOXFER /* GET MGET REGET RETRIEVE etc */ if (cx == XXGET || cx == XXMGET || cx == XXREGET || cx == XXRETR) { #ifdef IKSD if (inserver && !ENABLED(en_sen)) { printf("?Sorry, reception of files is disabled\n"); return(-9); } #endif /* IKSD */ return(doxget(cx)); } #endif /* NOXFER */ #ifndef NOSPL #ifndef NOFRILLS if (cx == XXGOK) { /* GETOK */ return(success = doask(cx)); } #endif /* NOFRILLS */ #endif /* NOSPL */ if (cx == XXHLP) { /* HELP */ #ifdef NOHELP return(dohlp(XXHLP)); #else x = cmkey2(cmdtab, ncmd,"\nCommand or topic","help",toktab,xxstring,1+2+8); debug(F111,"HELP command x",cmdbuf,x); if (x == -5) { y = chktok(toktab); debug(F101,"HELP cmkey token","",y); /* ungword(); */ switch (y) { #ifndef NOPUSH case '!': case '@': x = XXSHE; break; case '<': x = XXFUN; break; #endif /* NOPUSH */ case '#': x = XXCOM; break; case ';': x = XXCOM; break; #ifndef NOSPL case '.': x = XXDEF; break; case ':': x = XXLBL; break; #ifndef NOSEXP case '(': x = XXSEXP; break; #endif /* NOSEXP */ #endif /* NOSPL */ #ifdef CK_RECALL case '^': x = XXREDO; break; #endif /* CK_RECALL */ case '&': x = XXECH; break; /* (what is this?) */ default: printf("\n?Invalid - %s\n",cmdbuf); x = -2; } } makestr(&hlptok,atmbuf); debug(F111,"HELP token",hlptok,x); return(dohlp(x)); #endif /* NOHELP */ } #ifndef NOHELP if (cx == XXINT) /* INTRO */ return(hmsga(introtxt)); if (cx == XXNEW) { /* NEWS */ int x; extern char * k_info_dir; x = hmsga(newstxt); return(x); } #ifdef OS2ONLY if (cx == XXUPD) { /* View UPDATE file */ extern char exedir[]; char * pTopic; char updstr[2048]; if ((x = cmtxt("topic name","",&pTopic,xxstring)) < 0) return x; #ifdef COMMENT sprintf(updstr, "start view %s\\docs\\k2.inf+%s\\docs\\using_ck.inf+\ %s\\docs\\dialing.inf+%s\\docs\\modems.inf %s", exedir,exedir,exedir,exedir,pTopic ); #else if (ckmakxmsg(updstr, 2048, "start view ", exedir, "\\docs\\k2.inf+", exedir, "\\docs\\using_ck.inf+", exedir, "\\docs\\dialing.inf+", exedir, "\\docs\\modems.inf ", pTopic, NULL, NULL ) > 0) #endif /* COMMENT */ system(updstr); return(success = 1); } #endif /* OS2ONLY */ #endif /* NOHELP */ #ifndef NOLOCAL if (cx == XXHAN) { /* HANGUP */ if ((x = cmcfm()) < 0) return(x); #ifdef NEWFTP if ((ftpget == 1) || ((ftpget == 2) && !local && ftpisopen())) return(success = ftpbye()); #endif /* NEWFTP */ #ifndef NODIAL if ((x = mdmhup()) < 1) { debug(F101,"HANGUP mdmup","",x); #endif /* NODIAL */ x = tthang(); debug(F101,"HANGUP tthang","",x); x = (x > -1); #ifndef NODIAL } dialsta = DIA_UNK; #endif /* NODIAL */ whyclosed = WC_CLOS; ttchk(); /* In case of CLOSE-ON-DISCONNECT */ dologend(); #ifdef OS2 if (x) DialerSend(OPT_KERMIT_HANGUP, 0); #endif /* OS2 */ if (x) haveline = 0; return(success = x); } #endif /* NOLOCAL */ #ifndef NOSPL /* INPUT, REINPUT, and MINPUT */ if (cx == XXINP || cx == XXREI || cx == XXMINP) { long zz; int flags = 0, incount = 0; extern int itsapattern, isjoin, isinbuflen; int c, getval; struct FDB sw, nu, fl; int fc, havetime = 0; char * m; if (cx == XXREI) { m = "Timeout in seconds (ignored)"; } else { m = "Seconds to wait for input,\n or time of day hh:mm:ss,\ or switch"; } cmfdbi(&sw, /* First FDB - command switches */ _CMKEY, /* fcode */ m, /* helpmsg */ ckitoa(indef), /* default */ "", /* addtl string data */ ninputsw, /* addtl numeric data 1: tbl size */ 4, /* addtl numeric data 2: 4 = cmswi */ xxstring, /* Processing function */ inputsw, /* Keyword table */ &nu /* Pointer to next FDB */ ); cmfdbi(&nu, _CMNUM, /* Number */ m, /* Help message */ ckitoa(indef), /* default */ "", /* N/A */ 10, /* Radix = 10 */ 0, /* N/A */ xxstring, /* Processing function */ NULL, /* N/A */ &fl /* Next */ ); cmfdbi(&fl, /* Time of day hh:mm:ss */ _CMFLD, /* fcode */ "", /* hlpmsg */ "", "", /* addtl string data */ 0, /* addtl numeric data 1 */ 0, /* addtl numeric data 2 */ xxstring, NULL, NULL ); fc = (cx == XXREI) ? cmfdb(&nu) : cmfdb(&sw); /* Parse something */ for (y = 0; y < MINPMAX; y++) { /* Initialize search strings */ mp[y] = 0; /* Assume it's not a pattern */ if (!mpinited) { ms[y] = NULL; } if (ms[y]) { free(ms[y]); /* Free old strings, if any */ ms[y] = NULL; } } mpinited = 1; while (!havetime) { if (fc < 0) { /* Error */ if (fc == -3) { printf("?Syntax error in INPUT-class command\n"); return(-9); } else return(fc); } switch (cmresult.fcode) { case _CMKEY: /* Switch */ c = cmgbrk(); if ((getval = (c == ':' || c == '=')) && !(cmgkwflgs() & CM_ARG)) { printf("?This switch does not take an argument\n"); return(-9); } if (getval && cmresult.nresult == INPSW_COU) { if ((y = cmnum("Number of bytes to read", "",10,&x,xxstring)) < 0) return(y); incount = x; } flags |= cmresult.nresult; fc = cmfdb(&sw); /* Maybe parse more switches */ continue; case _CMNUM: /* Seconds to time out */ x = cmresult.nresult; #ifdef CKFLOAT if (inscale != 1.0) /* Scale */ x *= inscale; #endif /* CKFLOAT */ havetime++; break; case _CMFLD: zz = tod2sec(atmbuf); /* Convert to secs since midnight */ if (zz < 0L) { printf("?Number, expression, or time of day required\n"); return(-9); } else { char now[32]; /* Current time */ char *p; long tnow; p = now; ztime(&p); tnow = atol(p+11) * 3600L + atol(p+14) * 60L + atol(p+17); if (zz < tnow) /* User's time before now */ zz += 86400L; /* So make it tomorrow */ zz -= tnow; /* Seconds from now. */ if (zz > -1L) { x = zz; if (zz != (long) x) { printf( "Sorry, arithmetic overflow - hh:mm:ss not usable on this platform.\n" ); return(-9); } } havetime++; } break; default: printf("?Internal error\n"); return(-9); } } /* Now parse the search text */ #ifdef CK_MINPUT if (cx == XXMINP) { /* MINPUT */ int i, k = 0, n = 0; struct stringarray * q; keepallchars = 1; while (k < MINPMAX) { if ((y = cmfld("String or pattern","",&s,xxstring)) < 0) { if (y == -3) { if ((y = cmcfm()) < 0) return(y); break; } else { return(y); } } debug(F111,"MINPUT field",s,k); if (isjoin) { if ((q = cksplit(1,0,s," ",(char *)c1chars,3,0,0))) { char ** ap = q->a_head; n = q->a_size; debug(F101,"minput cksplit size","",n); for (i = 1; i <= n && k < MINPMAX; i++) { if (!ap[i]) /* Add non-empty elements */ continue; if (!*(ap[i])) continue; makestr(&(ms[k]),ap[i]); debug(F111,"MINPUT JOIN",ms[k],k); k++; } } } else { if (s) if (*s) { makestr(&(ms[k]),brstrip(s)); if (itsapattern) mp[k] = 1; debug(F111,"MINPUT",ms[k],itsapattern); k++; } } } keepallchars = 0; } else { #endif /* CK_MINPUT */ /* INPUT or REINPUT */ if (flags & INPSW_COU) { if ((y = cmcfm()) < 0) return(y); } else { if ((y = cmtxt("Material to be input","",&s,xxstring)) < 0) return(y); } mp[0] = itsapattern ? 1 : 0; makestr(&(ms[0]),brstrip(s)); ms[1] = NULL; #ifdef CK_MINPUT } #endif /* CK_MINPUT */ if (incount > 0) /* No searching if /COUNT: given */ makestr(&(ms[0]),NULL); if (cx == XXINP || cx == XXMINP) { /* Not REINPUT... */ i_active = 1; /* Go try to input the search string */ success = doinput(x,ms,mp,flags,incount); i_active = 0; } else { /* REINPUT */ success = doreinp(x,ms[0],itsapattern); } if (intime[cmdlvl] && !success) { /* TIMEOUT-ACTION = QUIT? */ popclvl(); /* If so, pop command level. */ if (pflag && cmdlvl == 0) { if (cx == XXINP) printf("?INPUT timed out\n"); if (cx == XXMINP) printf("?MINPUT timed out\n"); if (cx == XXREI) printf("?REINPUT failed\n"); } } return(success); /* Return do(re)input's return code */ } #endif /* NOSPL */ if (cx == XXLOG) { /* LOG */ x = cmkey(logtab,nlog,"What to log","",xxstring); if (x == -3) { printf("?Type of log required\n"); return(-9); } if (x < 0) return(x); x = dolog(x); if (x < 0) return(x); else return(success = x); } if (cx == XXLOGIN) { /* (REMOTE) LOGIN */ #ifdef NEWFTP if ((ftpget == 1) || ((ftpget == 2) && ftpisopen())) return(success = doftpusr()); #endif /* NEWFTP */ #ifdef IKSD if (inserver) { printf("?Already logged in\n"); return(-9); } else #endif /* IKSD */ { #ifdef NOXFER return(-2); #else return(dormt(XZLGI)); #endif /* NOXFER */ } } if (cx == XXLOGOUT) { /* (REMOTE) LOGOUT */ #ifdef NEWFTP if ((ftpget == 1) || ((ftpget == 2) && ftpisopen())) return(success = doftpres()); #endif /* NEWFTP */ #ifdef IKSD if (inserver) { if ((x = cmcfm()) < 0) return(x); doexit(GOOD_EXIT,xitsta); } else #endif /* IKSD */ if (!local || (network && ttchk() < 0)) { printf("?No connection.\n"); return(-9); } else { #ifdef NOXFER return(-2); #else return(dormt(XZLGO)); #endif /* NOXFER */ } } #ifndef NOSCRIPT if (cx == XXLOGI) { /* UUCP-style script */ if ((x = cmtxt("expect-send expect-send ...","",&s,xxstring)) < 0) return(x); #ifdef CK_APC if ((apcactive == APC_LOCAL) || ((apcactive == APC_REMOTE) && (!(apcstatus & APC_UNCH)))) return(success = 0); #endif /* CK_APC */ #ifdef VMS conres(); /* For Ctrl-C to work... */ #endif /* VMS */ return(success = dologin(s)); /* Return 1=completed, 0=failed */ } #endif /* NOSCRIPT */ #ifndef NOXFER #ifdef PIPESEND if (cx == XXCREC) { /* CRECEIVE */ if (protocol != PROTO_K) { printf("?Sorry, CRECEIVE works only with Kermit protocol\n"); return(-9); } else return(doxget(cx)); } if (cx == XXCGET) { /* CGET */ return(doxget(cx)); } #endif /* PIPESEND */ if (cx == XXREC) /* RECEIVE */ return(doxget(cx)); #endif /* NOXFER */ #ifndef NOXFER if (cx == XXREM) { /* REMOTE */ #ifdef NEWFTP if ((ftpget == 1) || ((ftpget == 2) && ftpisopen())) return(doftprmt(0,0)); #endif /* NEWFTP */ #ifdef CK_XYZ if (protocol != PROTO_K) { printf("Sorry, REMOTE commands only work with Kermit protocol\n"); return(-9); } #endif /* CK_XYZ */ x = cmkey(remcmd,nrmt,"Remote Kermit server command","",xxstring); if (x == -3) { printf("?You must specify a command for the remote server\n"); return(-9); } return(dormt(x)); } #endif /* NOXFER */ #ifndef NORENAME #ifndef NOFRILLS if (cx == XXREN || cx == XXLREN) { /* RENAME */ #ifdef LOCUS if (!locus && cx != XXLREN) { #ifdef NOXFER return(-2); #else return(dormt(XZREN)); #endif /* NOXFER */ } #endif /* LOCUS */ #ifdef IKSD if (inserver && (!ENABLED(en_ren) #ifdef CK_LOGIN || isguest #endif /* CK_LOGIN */ )) { printf("?Sorry, renaming of files is disabled\n"); return(-9); } #endif /* IKSD */ #ifdef CK_APC if ((apcactive == APC_LOCAL) || ((apcactive == APC_REMOTE) && (!(apcstatus & APC_UNCH)))) return(success = 0); #endif /* CK_APC */ return(dorenam()); } #endif /* NOFRILLS */ #endif /* NORENAME */ if (cx == XXEIGHT) { /* EIGHTBIT */ extern int parity, cmask, cmdmsk; if ((x = cmcfm()) < 0) return(x); parity = 0; cmask = 0xff; cmdmsk = 0xff; return(success = 1); } #ifndef NOXFER /* SEND, CSEND, MOVE, MAIL, and RESEND use the new common code */ if (cx == XXSEN /* SEND */ #ifdef PIPESEND || cx == XXCSEN /* CSEND */ #endif /* PIPESEND */ || cx == XXMOVE /* MOVE */ || cx == XXMAI /* MAIL */ #ifdef CK_RESEND || cx == XXRSEN /* RESEND */ #endif /* CK_RESEND */ ) { #ifdef IKSD if (inserver && !ENABLED(en_get)) { printf("?Sorry, sending files is disabled\n"); return(-9); } #endif /* IKSD */ return(doxsend(cx)); } /* PSEND, ADD, and REMOVE use special parsing */ #ifdef ADDCMD /* ADD and REMOVE */ if (cx == XXADD || cx == XXREMV) { char * m; m = (cx == XXADD) ? "Add to which list?" : "Remove from which list?"; x = cmkey(addtab,naddtab,m,"",xxstring); if (x < 0) return(x); #ifndef NOMSEND if (x == ADD_SND) return(addsend(cx)); else #endif /* NOMSEND */ return(doadd(cx,x)); } #endif /* ADDCMD */ #ifdef CK_RESEND if (cx == XXPSEN) { /* PSEND */ int seekto = 0; /* FIX THIS */ cmarg = cmarg2 = ""; x = cmifi("File to partially send", "", &s, &y, xxstring); if (x < 0) { if (x == -3) { printf("?A file specification is required\n"); return(-9); } else return(x); } nfils = -1; /* Files come from internal list. */ #ifndef NOMSEND addlist = 0; /* Don't use SEND-LIST. */ filenext = NULL; #endif /* NOMSEND */ ckstrncpy(line,s,LINBUFSIZ); /* Save copy of string just parsed. */ debug(F110,"PSEND line",line,0); if (y != 0) { printf("?Sorry, wildcards not permitted in this command\n"); return(-9); } if (sizeof(int) < 4) { printf("?Sorry, this command needs 32-bit integers\n"); return(-9); } x = cmnum("starting position (byte number)", "",10,&seekto,xxstring); if (x < 0) return(x); zfnqfp(s,fspeclen,fspec); /* Get full path */ if ((x = cmtxt("Name to send it with","",&s,NULL)) < 0) return(x); ckstrncpy(tmpbuf,s,TMPBUFSIZ); #ifdef IKSD if (inserver && !ENABLED(en_get)) { printf("?Sorry, sending files is disabled\n"); return(-9); } #endif /* IKSD */ #ifdef PIPESEND if (sndfilter) { printf("?Sorry, no PSEND while SEND FILTER selected\n"); return(-9); } #endif /* PIPESEND */ #ifdef CK_XYZ if ((protocol == PROTO_X || protocol == PROTO_XC)) { printf("Sorry, PSEND works only with Kermit protocol\n"); return(-9); } #endif /* CK_XYZ */ cmarg2 = brstrip(tmpbuf); /* Strip braces */ cmarg = line; /* File to send */ debug(F110,"PSEND filename",cmarg,0); debug(F110,"PSEND as-name",cmarg2,0); sendstart = seekto; sstate = 's'; /* Set start state to SEND */ #ifndef NOMSEND addlist = 0; filenext = NULL; #endif /* NOMSEND */ sendmode = SM_PSEND; #ifdef MAC what = W_SEND; scrcreate(); #endif /* MAC */ if (local) { /* If in local mode, */ displa = 1; /* enable file transfer display */ } return(0); } #endif /* CK_RESEND */ #endif /* NOXFER */ #ifndef NOXFER #ifndef NOMSEND if (cx == XXMSE || cx == XXMMOVE) { #ifdef NEWFTP if ((ftpget == 1) || ((ftpget == 2) && ftpisopen())) return(doftpput(cx,0)); #endif /* NEWFTP */ #ifdef CK_XYZ if (protocol == PROTO_X || protocol == PROTO_XC) { printf( "Sorry, you can only send one file at a time with XMODEM protocol\n" ); return(-9); } #endif /* CK_XYZ */ return(doxsend(cx)); } #ifdef COMMENT /* (moved to doxsend) */ if (cx == XXMSE || cx == XXMMOVE) { /* MSEND and MMOVE commands */ nfils = 0; /* Like getting a list of */ lp = line; /* files on the command line */ addlist = 0; /* Do not use SEND-LIST */ filenext = NULL; /* Ditto ! */ while (1) { char *p; if ((x = cmifi("Names of files to send, separated by spaces","", &s,&y,xxstring)) < 0) { if (x == -3) { if (nfils <= 0) { printf("?A file specification is required\n"); return(-9); } else break; } return(x); } msfiles[nfils++] = lp; /* Got one, count it, point to it, */ p = lp; /* remember pointer, */ while (*lp++ = *s++) /* and copy it into buffer */ if (lp > (line + LINBUFSIZ)) { /* Avoid memory leak */ printf("?MSEND list too long\n"); line[0] = NUL; return(-9); } debug(F111,"msfiles",msfiles[nfils-1],nfils-1); if (nfils == 1) *fspec = NUL; /* Take care of \v(filespec) */ #ifdef ZFNQFP zfnqfp(p,TMPBUFSIZ,tmpbuf); p = tmpbuf; #endif /* ZFNQFP */ if (((int)strlen(fspec) + (int)strlen(p) + 1) < fspeclen) { strcat(fspec,p); /* safe */ strcat(fspec," "); /* safe */ } else printf("WARNING - \\v(filespec) buffer overflow\n"); } cmlist = msfiles; /* Point cmlist to pointer array */ cmarg2 = ""; /* No internal expansion list (yet) */ sndsrc = nfils; /* Filenames come from cmlist */ sendmode = SM_MSEND; /* Remember this kind of SENDing */ sstate = 's'; /* Set start state for SEND */ if (cx == XXMMOVE) /* If MMOVE'ing, */ moving = 1; /* set this flag. */ #ifdef MAC what = W_SEND; scrcreate(); #endif /* MAC */ if (local) { /* If in local mode, */ displa = 1; /* turn on file transfer display */ ttflui(); /* and flush tty input buffer. */ } return(0); } #endif /* COMMENT */ #endif /* NOMSEND */ #endif /* NOXFER */ #ifndef NOSERVER if (cx == XXSER) { /* SERVER */ #ifdef CK_XYZ if (protocol != PROTO_K) { printf("Sorry, SERVER only works with Kermit protocol\n"); return(-9); } #endif /* CK_XYZ */ #ifdef COMMENT /* Parse for time limit, but since we don't use it yet, the parsing is commented out. */ x_ifnum = 1; /* Turn off internal complaints */ y = cmnum("optional time limit, seconds, or time of day as hh:mm:ss", "0", 10, &x, xxstring ); x_ifnum = 0; if (y < 0) { if (y == -2) { /* Invalid number or expression */ zz = tod2sec(atmbuf); /* Convert to secs since midnight */ if (zz < 0L) { printf("?Number, expression, or time of day required\n"); return(-9); } else { char now[32]; /* Current time */ char *p; long tnow; p = now; ztime(&p); tnow = atol(p+11) * 3600L + atol(p+14) * 60L + atol(p+17); if (zz < tnow) /* User's time before now */ zz += 86400L; /* So make it tomorrow */ zz -= tnow; /* Seconds from now. */ } } else return(y); } if (zz > -1L) { x = zz; if (zz != (long) x) { printf( "Sorry, arithmetic overflow - hh:mm:ss not usable on this platform.\n" ); return(-9); } } if (x < 0) x = 0; #endif /* COMMENT */ if ((x = cmcfm()) < 0) return(x); sstate = 'x'; #ifdef MAC what = W_RECV; scrcreate(); #endif /* MAC */ if (local) displa = 1; #ifdef AMIGA reqoff(); /* No DOS requestors while server */ #endif /* AMIGA */ return(0); } #endif /* NOSERVER */ if (cx == XXSAVE) { /* SAVE command */ x = cmkey(savtab,nsav,"option","keymap",xxstring); if (x == -3) { printf("?You must specify an option to save\n"); return(-9); } if (x < 0) return(x); /* have to set success separately for each item in doprm()... */ /* actually not really, could have just had doprm return 0 or 1 */ /* and set success here... */ y = dosave(x); if (y == -3) { printf("?More fields required\n"); return(-9); } else return(y); } if (cx == XXSET) { /* SET command */ x = cmkey(prmtab,nprm,"Parameter","",xxstring); if (x == -3) { printf("?You must specify a parameter to set\n"); return(-9); } if (x < 0) return(x); /* have to set success separately for each item in doprm()... */ /* actually not really, could have just had doprm return 0 or 1 */ /* and set success here... */ y = doprm(x,0); if (y == -3) { printf("?More fields required\n"); return(-9); } else return(y); } #ifndef NOPUSH if (cx == XXSHE /* SHELL (system) command */ || cx == XXEXEC /* exec() */ ) { int rx = 0; char * p = NULL; int i /* ,n */ ; #ifdef UNIXOROSK char * args[256]; #endif /* UNIXOROSK */ #ifdef IKSD if (inserver && (nopush || !ENABLED(en_hos))) { printf("?Sorry, host command access is disabled\n"); return(-9); } #endif /* IKSD */ #ifdef CKEXEC if (cx == XXEXEC) { /* EXEC (overlay ourselves) */ struct FDB sw, fl; cmfdbi(&sw, /* First FDB - command switches */ _CMKEY, /* fcode */ "Command to overlay C-Kermit\n or switch", /* hlpmsg */ "", /* default */ "", /* addtl string data */ 1, /* addtl numeric data 1: tbl size */ 4, /* addtl numeric data 2: 4 = cmswi */ xxstring, /* Processing function */ redirsw, /* Keyword table */ &fl /* Pointer to next FDB */ ); cmfdbi(&fl, /* 2nd FDB - command to exec */ _CMFLD, /* fcode */ "Command to overlay C-Kermit", /* hlpmsg */ "", /* default */ "", /* addtl string data */ 0, /* addtl numeric data 1 */ 0, /* addtl numeric data 2 */ xxstring, NULL, NULL /* No more after this */ ); while (1) { x = cmfdb(&sw); /* Parse something */ debug(F101,"exec cmfdb","",x); if (x < 0) return(x); /* Generalize this if we add more switches */ if (cmresult.fcode == _CMKEY) { rx = 1; continue; } if (cmresult.fcode == _CMFLD) break; return(-2); } ckstrncpy(tmpbuf,cmresult.sresult,TMPBUFSIZ); if (!tmpbuf[0]) { printf("?Command required\n"); return(-9); } p = brstrip(tmpbuf); args[0] = NULL; /* Set argv[0] to it */ makestr(&args[0],p); for (i = 1; i < 255; i++) { /* Get arguments for command */ if ((x = cmfld("Argument","",&s,xxstring)) < 0) { if (x == -3) { if ((x = cmcfm()) < 0) return(x); break; } else return(x); } args[i] = NULL; s = brstrip(s); makestr(&args[i],s); } args[i] = NULL; } else { #endif /* CKEXEC */ if ((x = cmtxt("System command to execute","",&s,xxstring)) < 0) return(x); #ifdef CKEXEC } #endif /* CKEXEC */ if (nopush) return(success = 0); #ifdef CK_APC if (apcactive == APC_REMOTE && !(apcstatus & APC_UNCH)) return(success = 0); #endif /* CK_APC */ conres(); /* Make console normal */ #ifdef OS2 if (!(s && *s)) { os2push(); return(success = 1); } else #endif /* OS2 */ if (cx == XXSHE) { x = zshcmd(s); debug(F101,"RUN zshcmd code","",x); concb((char)escape); return(success = x); #ifdef CKEXEC } else { #ifdef DEBUG if (deblog) { debug(F111,"EXEC cmd",p,0); for (i = 0; i < 256 && args[i]; i++) debug(F111,"EXEC arg",args[i],i); } #endif /* DEBUG */ if (p) { z_exec(p,args,rx); /* Overlay ourself */ debug(F100,"EXEC fails","",0); concb((char)escape); /* In case it returns */ } return(success = 0); #endif /* CKEXEC */ } } #ifdef CK_REDIR if (cx == XXFUN) { /* REDIRECT */ #ifdef CK_APC if ((apcactive == APC_LOCAL) || ((apcactive == APC_REMOTE) && (!(apcstatus & APC_UNCH)))) return(success = 0); #endif /* CK_APC */ ckmakmsg(tmpbuf, TMPBUFSIZ, "Local command to run,\n", "with its standard input/output redirected to ", local ? ttname : "the communications connection", "\n" ); if ((x = cmtxt(tmpbuf,"",&s,xxstring)) < 0) return(x); if (nopush) { printf("?REDIRECT disabled\n"); return(success=0); } if (!local) { printf("?SET LINE or SET HOST required first\n"); return(-9); } if (!*s) { printf("?REDIRECT requires a command to redirect\n"); return(-9); } return(success = ttruncmd(s)); } #endif /* CK_REDIR */ #endif /* NOPUSH */ #ifndef NOSHOW if (cx == XXSHO) { /* SHOW */ x = cmkey(shotab,nsho,"","parameters",xxstring); if (x < 0) return(x); return(doshow(x)); } #endif /* NOSHOW */ #ifndef MAC if (cx == XXSPA) { /* SPACE */ #ifdef IKSD if (inserver && !ENABLED(en_spa)) { printf("?Sorry, SPACE command disabled\n"); return(-9); } #endif /* IKSD */ #ifdef datageneral /* AOS/VS can take an argument after its "space" command. */ if ((x = cmtxt("Confirm, or local directory name","",&s,xxstring)) < 0) return(x); if (nopush) { printf("?Sorry, SPACE command disabled\n"); return(-9); } else if (*s == NUL) { xsystem(SPACMD); } else { ckmakmsg(line,LINBUFSIZ,"space ",s,NULL,NULL); xsystem(line); } #else #ifdef OS2 if ((x = cmtxt("Press Enter for current disk,\n\ or specify a disk letter like A:","",&s,xxstring)) < 0) return(x); if (*s == NUL) { /* Current disk */ unsigned long space = zdskspace(0); if (space > 0 && space < 1024) printf(" Free space: unknown\n"); else printf(" Free space: %ldK\n", space/1024L); } else { int drive = toupper(*s); unsigned long space = zdskspace(drive - 'A' + 1); if (space > 0 && space < 1024) printf(" Drive %c: unknown free\n"); else printf(" Drive %c: %ldK free\n", drive,space / 1024L); } #else #ifdef UNIXOROSK x = cmdir("Confirm for current disk,\n\ or specify a disk device or directory","",&s,xxstring); if (x == -3) s = ""; else if (x < 0) return(x); ckstrncpy(tmpbuf,s,TMPBUFSIZ); s = tmpbuf; if ((x = cmcfm()) < 0) return(x); if (nopush) { printf("?Sorry, SPACE command disabled\n"); return(-9); } if (!*s) { /* Current disk */ xsystem(SPACMD); } else { /* Specified disk */ ckmakmsg(line,LINBUFSIZ,SPACM2," ",s,NULL); xsystem(line); } #else if ((x = cmcfm()) < 0) return(x); if (nopush) { printf("?Sorry, SPACE command disabled\n"); return(-9); } xsystem(SPACMD); #endif /* UNIXOROSK */ #endif /* OS2 */ #endif /* datageneral */ return(success = 1); /* Pretend it worked */ } #endif /* MAC */ #ifndef NOXFER if (cx == XXSTA) { /* STATISTICS */ if ((x = cmkey(stattab,2,"Carriage return, or option", "/brief",xxstring)) < 0) return(x); if ((y = cmcfm()) < 0) return(y); return(success = dostat(x)); } #endif /* NOXFER */ if (cx == XXSTO || cx == XXEND) { /* STOP, END, or POP */ if ((y = cmnum("exit status code","0",10,&x,xxstring)) < 0) return(y); if ((y = cmtxt("Message to print","",&s,xxstring)) < 0) return(y); s = brstrip(s); if (*s) printf("%s\n",s); if (cx == XXSTO) { dostop(); } else { doend(x); } return(success = (x == 0)); } if (cx == XXSUS) { /* SUSPEND */ if ((y = cmcfm()) < 0) return(y); #ifdef NOJC printf("Sorry, this version of Kermit cannot be suspended\n"); #else #ifdef IKSD if (inserver) { printf("?Sorry, IKSD can not be suspended\n"); return(-9); } else #endif /* IKSD */ if (nopush) { printf("?Sorry, access to system is disabled\n"); return(-9); } stptrap(0); #endif /* NOJC */ return(0); } if (cx == XXTAK) { /* TAKE */ char * scriptenv = NULL; #ifdef OS2 char * GetAppData(int); extern char startupdir[],exedir[],inidir[]; char * keymapenv = NULL; char * appdata0 = NULL, *appdata1 = NULL; int xx; #define TAKEPATHLEN 4096 #else /* OS2 */ #define TAKEPATHLEN 1024 #endif /* OS2 */ char takepath[TAKEPATHLEN]; if (tlevel >= MAXTAKE-1) { printf("?Take files nested too deeply\n"); return(-9); } #ifdef OS2 #ifdef NT scriptenv = getenv("K95SCRIPTS"); keymapenv = getenv("K95KEYMAPS"); makestr(&appdata0,(char *)GetAppData(0)); makestr(&appdata1,(char *)GetAppData(1)); #else /* NT */ scriptenv = getenv("K2SCRIPTS"); keymapenv = getenv("K2KEYMAPS"); #endif /* NT */ #endif /* OS2 */ if (!scriptenv) /* Let this work for Unix etc too */ scriptenv = getenv("CK_SCRIPTS"); /* Use this if defined */ #ifndef OS2 if (!scriptenv) /* Otherwise use home directory */ scriptenv = homepath(); #endif /* OS2 */ if (!scriptenv) scriptenv = ""; ckstrncpy(takepath,scriptenv,TAKEPATHLEN); debug(F110,"TAKE initial takepath",takepath,0); #ifdef OS2 if (!keymapenv) keymapenv = getenv("CK_KEYMAPS"); if (!keymapenv) keymapenv = ""; ckstrncat(takepath, (scriptenv && scriptenv[strlen(scriptenv)-1]==';')?"":";", TAKEPATHLEN ); ckstrncat(takepath,keymapenv?keymapenv:"",TAKEPATHLEN); ckstrncat(takepath, (keymapenv && keymapenv[strlen(keymapenv)-1]==';')?"":";", TAKEPATHLEN ); ckstrncat(takepath,startupdir,TAKEPATHLEN); ckstrncat(takepath,";",TAKEPATHLEN); ckstrncat(takepath,startupdir,TAKEPATHLEN); ckstrncat(takepath,"SCRIPTS/;",TAKEPATHLEN); ckstrncat(takepath,startupdir,TAKEPATHLEN); ckstrncat(takepath,"KEYMAPS/;",TAKEPATHLEN); ckstrncat(takepath,appdata1,TAKEPATHLEN); ckstrncat(takepath,"Kermit 95/;",TAKEPATHLEN); ckstrncat(takepath,appdata1,TAKEPATHLEN); ckstrncat(takepath,"Kermit 95/SCRIPTS/;",TAKEPATHLEN); ckstrncat(takepath,appdata1,TAKEPATHLEN); ckstrncat(takepath,"Kermit 95/KEYMAPS/;",TAKEPATHLEN); ckstrncat(takepath,appdata0,TAKEPATHLEN); ckstrncat(takepath,"Kermit 95/;",TAKEPATHLEN); ckstrncat(takepath,appdata0,TAKEPATHLEN); ckstrncat(takepath,"Kermit 95/SCRIPTS/;",TAKEPATHLEN); ckstrncat(takepath,appdata0,TAKEPATHLEN); ckstrncat(takepath,"Kermit 95/KEYMAPS/;",TAKEPATHLEN); ckstrncat(takepath,inidir,TAKEPATHLEN); ckstrncat(takepath,";",TAKEPATHLEN); ckstrncat(takepath,inidir,TAKEPATHLEN); ckstrncat(takepath,"SCRIPTS/;",TAKEPATHLEN); ckstrncat(takepath,inidir,TAKEPATHLEN); ckstrncat(takepath,"KEYMAPS/;",TAKEPATHLEN); ckstrncat(takepath,zhome(),TAKEPATHLEN); ckstrncat(takepath,";",TAKEPATHLEN); ckstrncat(takepath,zhome(),TAKEPATHLEN); ckstrncat(takepath,"SCRIPTS/;",TAKEPATHLEN); ckstrncat(takepath,zhome(),TAKEPATHLEN); ckstrncat(takepath,"KEYMAPS/;",TAKEPATHLEN); ckstrncat(takepath,exedir,TAKEPATHLEN); ckstrncat(takepath,";",TAKEPATHLEN); ckstrncat(takepath,exedir,TAKEPATHLEN); ckstrncat(takepath,"SCRIPTS/;",TAKEPATHLEN); ckstrncat(takepath,exedir,TAKEPATHLEN); ckstrncat(takepath,"KEYMAPS/;",TAKEPATHLEN); #endif /* OS2 */ debug(F110,"TAKE final takepath",takepath,0); if ((y = cmifip("Commands from file", "",&s,&x,0,takepath,xxstring)) < 0) { if (y == -3) { printf("?A file name is required\n"); return(-9); } else return(y); } if (x != 0) { printf("?Wildcards not allowed in command file name\n"); return(-9); } ckstrncpy(line,s,LINBUFSIZ); debug(F110,"TAKE file",s,0); if (isdir(s)) { printf("?Can't execute a directory - \"%s\"\n", s); return(-9); } #ifndef NOTAKEARGS { char * p; x = strlen(line); debug(F111,"TAKE args",line,x); p = line + x + 1; if ((y = cmtxt("Optional arguments","",&s,xxstring)) < 0) return(y); if (*s) { /* Args given? */ ckstrncpy(p,s,LINBUFSIZ-x-1); #ifdef ZFNQFP zfnqfp(line,TMPBUFSIZ,tmpbuf); s = tmpbuf; #else s = line; #endif /* ZFNQFP */ debug(F110,"TAKE filename",s,0); x = strlen(s); debug(F101,"TAKE new len",s,x); #ifdef COMMENT /* This was added in C-Kermit 7.0 to allow args to be passed from the TAKE command to the command file. But it overwrites the current argument vector, which is at best surprising, and at worst unsafe. */ addmac("%0",s); /* Define %0 = name of file */ varnam[0] = '%'; varnam[2] = '\0'; debug(F110,"take arg 0",s,0); debug(F110,"take args",p,0); for (y = 1; y < 10; y++) { /* Clear current args %1..%9 */ varnam[1] = (char) (y + '0'); delmac(varnam,0); } xwords(p,MAXARGLIST,NULL,0); /* Assign new args */ debug(F110,"take args",p,0); #else /* This method is used in 8.0. If the TAKE command includes arguments, we insert an intermediate temporary macro between the current level; we pass the arguments to the macro and then the macro TAKEs the command file. If the user Ctrl-C's out of the TAKE file, some temporary macro definitions and other small malloc'd bits might be left behind. */ { char * q = NULL; char * r = NULL; int k, m; m = maclvl; q = (char *)malloc(x+24); if (q) { r = (char *)malloc(x+24); if (r) { sprintf(q,"_file[%s](%d)",s,cmdlvl); /* safe */ sprintf(r,"take %s",s); /* safe */ k = addmac(q,r); if (k > -1) { dodo(k,p,0); while (maclvl > m) { sstate = (CHAR) parser(1); if (sstate) proto(); } } k = delmac(q,0); free(q); free(r); return(success); } } } return(success = 0); #endif /* COMMENT */ } } #else if ((y = cmcfm()) < 0) return(y); #endif /* NOTAKEARGS */ return(success = dotake(line)); } #ifndef NOLOCAL #ifdef OS2 if (cx == XXVIEW) { /* VIEW Only Terminal mode */ viewonly = TRUE; success = doconect(0, 0); viewonly = FALSE; return success; } #endif /* OS2 */ #ifdef NETCONN if (cx == XXTEL || cx == XXIKSD) { /* TELNET */ int x,z; #ifdef OS2 if (!tcp_avail) { printf("?Sorry, either TCP/IP is not available on this system or\n\ necessary DLLs did not load. Use SHOW NETWORK to check network status.\n"); success = 0; return(-9); } else #endif /* OS2 */ { x = nettype; /* Save net type in case of failure */ z = ttnproto; /* Save protocol in case of failure */ nettype = NET_TCPB; ttnproto = (cx == XXTEL) ? NP_TELNET : NP_KERMIT; if ((y = setlin(XYHOST,0,1)) <= 0) { nettype = x; /* Failed, restore net type. */ ttnproto = z; /* and protocol */ success = 0; } didsetlin++; } return(y); } #ifndef PTYORPIPE #ifdef NETCMD #define PTYORPIPE #else #ifdef NETPTY #define PTYORPIPE #endif /* NETPTY */ #endif /* NETCMD */ #endif /* PTYORPIPE */ #ifdef PTYORPIPE if (cx == XXPIPE || cx == XXPTY) { /* PIPE or PTY */ int x; extern int netsave; x = nettype; /* Save net type in case of failure */ nettype = (cx == XXPIPE) ? NET_CMD : NET_PTY; if ((y = setlin(XYHOST,0,1)) < 0) { nettype = x; /* Failed, restore net type. */ ttnproto = z; /* and protocol */ success = 0; } didsetlin++; netsave = x; return(y); } #endif /* PTYORPIPE */ #ifdef ANYSSH if (cx == XXSSH) { /* SSH (Secure Shell) */ extern int netsave; #ifdef SSHBUILTIN int k, x, havehost = 0, trips = 0; int tmpver = -1, tmpxfw = -1; #ifndef SSHTEST extern int sl_ssh_xfw, sl_ssh_xfw_saved; extern int sl_ssh_ver, sl_ssh_ver_saved; #endif /* SSHTEST */ extern int mdmtyp, mdmsav, cxtype, sl_uid_saved; extern char * slmsg; extern char uidbuf[], sl_uidbuf[]; extern char pwbuf[], * g_pswd; extern int pwflg, pwcrypt, g_pflg, g_pcpt, nolocal; struct FDB sw, kw, fl; if (ssh_tmpstr) memset(ssh_tmpstr,0,strlen(ssh_tmpstr)); makestr(&ssh_tmpstr,NULL); makestr(&ssh_tmpuid,NULL); makestr(&ssh_tmpcmd,NULL); makestr(&ssh_tmpport,NULL); cmfdbi(&kw, /* 1st FDB - commands */ _CMKEY, /* fcode */ "host [ port ],\n or action", /* hlpmsg */ "", /* default */ "", /* addtl string data */ nsshcmd, /* addtl numeric data 1: tbl size */ 0, /* addtl numeric data 2: 0 = keyword */ xxstring, /* Processing function */ sshkwtab, /* Keyword table */ &fl /* Pointer to next FDB */ ); cmfdbi(&fl, /* Host */ _CMFLD, /* fcode */ "", /* hlpmsg */ "", /* default */ "", /* addtl string data */ 0, /* addtl numeric data 1 */ 0, /* addtl numeric data 2 */ xxstring, NULL, NULL ); x = cmfdb(&kw); if (x == -3) { printf("?ssh what?\n"); return(-9); } if (x < 0) return(x); havehost = 0; if (cmresult.fcode == _CMFLD) { havehost = 1; ckstrncpy(line,cmresult.sresult,LINBUFSIZ); /* Hostname */ cmresult.nresult = XSSH_OPN; } switch (cmresult.nresult) { /* SSH keyword */ case XSSH_OPN: /* SSH OPEN */ if (!havehost) { if ((x = cmfld("Host","",&s,xxstring)) < 0) return(x); ckstrncpy(line,s,LINBUFSIZ); } /* Parse [ port ] [ switches ] */ cmfdbi(&kw, /* Switches */ _CMKEY, "Port number or service name,\nor switch", "", "", nsshopnsw, 4, xxstring, sshopnsw, &fl ); cmfdbi(&fl, /* Port number or service name */ _CMFLD, "", "", "", 0, 0, xxstring, NULL, NULL ); trips = 0; /* Explained below */ while (1) { /* Parse port and switches */ x = cmfdb(&kw); /* Get a field */ if (x == -3) /* User typed CR so quit from loop */ break; if (x < 0) /* Other parse error, pass it back */ return(x); switch (cmresult.fcode) { /* Field or Keyword? */ case _CMFLD: /* Field */ makestr(&ssh_tmpport,cmresult.sresult); break; case _CMKEY: /* Keyword */ switch (cmresult.nresult) { /* Which one? */ case SSHSW_USR: /* /USER: */ if (!cmgbrk()) { printf("?This switch requires an argument\n"); return(-9); } if ((y = cmfld("Username","",&s,xxstring)) < 0) return(y); s = brstrip(s); makestr(&ssh_tmpuid,s); break; case SSHSW_PWD: if (!cmgbrk()) { printf("?This switch requires an argument\n"); return(-9); } debok = 0; if ((x = cmfld("Password","",&s,xxstring)) < 0) { if (x == -3) { makestr(&ssh_tmpstr,""); } else { return(x); } } else { s = brstrip(s); if ((x = (int)strlen(s)) > PWBUFL) { makestr(&slmsg,"Internal error"); printf("?Sorry, too long - max = %d\n",PWBUFL); return(-9); } makestr(&ssh_tmpstr,s); } break; case SSHSW_VER: if ((x = cmnum("Number","",10,&z,xxstring)) < 0) return(x); if (z < 1 || z > 2) { printf("?Out of range: %d\n",z); return(-9); } tmpver = z; break; case SSHSW_CMD: case SSHSW_SUB: if ((x = cmfld("Text","",&s,xxstring)) < 0) return(x); makestr(&ssh_tmpcmd,s); ssh_cas = (cmresult.nresult == SSHSW_SUB); break; case SSHSW_X11: if ((x = cmkey(onoff,2,"","on",xxstring)) < 0) return(x); tmpxfw = x; break; default: return(-2); } } if (trips++ == 0) { /* After first time through */ cmfdbi(&kw, /* only parse switches, not port. */ _CMKEY, "Switch", "", "", nsshopnsw, 4, xxstring, sshopnsw, NULL ); } } if ((x = cmcfm()) < 0) /* Get confirmation */ return(x); if (clskconnx(1) < 0) { /* Close current Kermit connection */ if ( ssh_tmpstr ) { memset(ssh_tmpstr,0,strlen(ssh_tmpstr)); makestr(&ssh_tmpstr,NULL); } return(success = 0); } makestr(&ssh_hst,line); /* Stash everything */ if (ssh_tmpuid) { if (!sl_uid_saved) { ckstrncpy(sl_uidbuf,uidbuf,UIDBUFLEN); sl_uid_saved = 1; } ckstrncpy(uidbuf,ssh_tmpuid,UIDBUFLEN); makestr(&ssh_tmpuid,NULL); } if (ssh_tmpport) { makestr(&ssh_prt,ssh_tmpport); makestr(&ssh_tmpport,NULL); } else makestr(&ssh_prt,NULL); if (ssh_tmpcmd) { makestr(&ssh_cmd,brstrip(ssh_tmpcmd)); makestr(&ssh_tmpcmd,NULL); } else makestr(&ssh_cmd,NULL); if (tmpver > -1) { #ifndef SSHTEST if (!sl_ssh_ver_saved) { sl_ssh_ver = ssh_ver; sl_ssh_ver_saved = 1; } #endif /* SSHTEST */ ssh_ver = tmpver; } if (tmpxfw > -1) { #ifndef SSHTEST if (!sl_ssh_xfw_saved) { sl_ssh_xfw = ssh_xfw; sl_ssh_xfw_saved = 1; } #endif /* SSHTEST */ ssh_xfw = tmpxfw; } if (ssh_tmpstr) { if (ssh_tmpstr[0]) { ckstrncpy(pwbuf,ssh_tmpstr,PWBUFL+1); pwflg = 1; pwcrypt = 0; } else pwflg = 0; makestr(&ssh_tmpstr,NULL); } nettype = NET_SSH; if (mdmsav < 0) mdmsav = mdmtyp; mdmtyp = -nettype; x = 1; #ifndef NOSPL makestr(&g_pswd,pwbuf); /* Save global pwbuf */ g_pflg = pwflg; /* and flag */ g_pcpt = pwcrypt; #endif /* NOSPL */ /* Line parameter to ttopen() is ignored */ debug(F110,"SSH line",line,0); k = ttopen(line,&x,mdmtyp, 0); if (k < 0) { printf("?Unable to connect to %s\n",ssh_hst); mdmtyp = mdmsav; slrestor(); return(success = 0); } duplex = 0; /* Remote echo */ ckstrncpy(ttname,line,TTNAMLEN); /* Record the command */ debug(F110,"ssh ttname",ttname,0); makestr(&slmsg,NULL); /* No SET LINE error message */ cxtype = CXT_SSH; #ifndef NODIAL dialsta = DIA_UNK; #endif /* NODIAL */ success = 1; /* SET LINE succeeded */ network = 1; /* Network connection (not serial) */ local = 1; /* Local mode (not remote) */ if ((reliable != SET_OFF || !setreliable)) reliable = SET_ON; /* Transport is reliable end to end */ #ifdef OS2 DialerSend(OPT_KERMIT_CONNECT, 0); #endif /* OS2 */ setflow(); /* Set appropriate flow control */ haveline = 1; #ifdef CKLOGDIAL #ifdef NETCONN dolognet(); #endif /* NETCONN */ #endif /* CKLOGDIAL */ #ifndef NOSPL if (local) { if (nmac) { /* Any macros defined? */ int k; /* Yes */ k = mlook(mactab,"on_open",nmac); /* Look this up */ if (k >= 0) { /* If found, */ if (dodo(k,ssh_hst,0) > -1) /* set it up, */ parser(1); /* and execute it */ } } } #endif /* NOSPL */ #ifdef LOCUS if (autolocus) setlocus(1,1); #endif /* LOCUS */ /* Command was confirmed so we can pre-pop command level. */ /* This is so CONNECT module won't think we're executing a */ /* script if CONNECT was the final command in the script. */ if (cmdlvl > 0) prepop(); success = doconect(0,cmdlvl == 0 ? 1 : 0); if (ttchk() < 0) dologend(); return(success); case XSSH_CLR: if ((y = cmkey(sshclr,nsshclr,"","", xxstring)) < 0) { if (y == -3) { printf("?clear what?\n"); return(-9); } return(y); } if ((x = cmcfm()) < 0) return(x); switch (y) { case SSHC_LPF: ssh_pf_lcl_n = 0; break; case SSHC_RPF: ssh_pf_rmt_n = 0; break; default: return(-2); } return(success = 1); /* or whatever */ case XSSH_AGT: { /* SSH AGENT */ int doeach = 0; if ((y = cmkey(sshagent,nsshagent,"","",xxstring)) < 0) return(y); switch (y) { case SSHA_ADD: /* SSH AGENT ADD ... */ if ((x = cmifi("Identity file","",&s,&y,xxstring)) < 0) { #ifndef SSHTEST if (x == -3) /* No name given */ doeach = 1; /* so do them all */ else #endif /* SSHTEST */ return(x); } ckstrncpy(line,s,LINBUFSIZ); if ((x = cmcfm()) < 0) return(x); #ifdef SSHTEST x = 0; #else if (doeach) { int i; x = 0; for (i = 0; i < ssh_idf_n; i++) x += ssh_agent_add_file(ssh_idf[i]); } else x = ssh_agent_add_file(line); #endif /* SSHTEST */ return(success = (x == 0)); case SSHA_DEL: { /* SSH AGENT DELETE ... */ int doall = 0; if ((x = cmifi("Identity file","",&s,&y,xxstring)) < 0) { #ifndef SSHTEST if (x == -3) /* No name given */ doall = 1; /* so do them all */ else #endif /* SSHTEST */ return(x); } ckstrncpy(line,s,LINBUFSIZ); if ((x = cmcfm()) < 0) return(x); #ifdef SSHTEST x = 0; #else if (doall) x = ssh_agent_delete_all(); else x = ssh_agent_delete_file(line); #endif /* SSHTEST */ return(success = (x == 0)); } case SSHA_LST: { int fingerprint = 0; if ((y = cmswi(sshagtsw,nsshagtsw,"","",xxstring)) < 0) { if (y != -3) return(y); } else if (cmgbrk() > SP) { printf("?This switch does not take an argument\n"); return(-9); } else if (y == SSHASW_FP) { fingerprint = 1; } if ((x = cmcfm()) < 0) return(x); #ifdef SSHTEST return(success = 1); #else return(success = (ssh_agent_list_identities(fingerprint) == 0)); #endif /* SSHTEST */ } default: return(-2); } } case XSSH_ADD: { /* SSH ADD */ /* ssh add { local, remote } port host port */ int cx, i, j, k; char * h; if ((cx = cmkey(addfwd,naddfwd,"","", xxstring)) < 0) return(cx); if ((x = cmnum((cx == SSHF_LCL) ? "Local port number" : "Remote port number", "",10,&j,xxstring)) < 0) return(x); if ((x = cmfld("Host","",&s,xxstring)) < 0) return(x); makestr(&h,s); if ((x = cmnum("Port","",10,&k,xxstring)) < 0) return(x); if ((x = cmcfm()) < 0) return(x); switch(cx) { case SSHF_LCL: if (ssh_pf_lcl_n == 32) { printf( "?Maximum number of local port forwardings already specified\n" ); free(h); return(success = 0); } ssh_pf_lcl[ssh_pf_lcl_n].p1 = j; makestr(&(ssh_pf_lcl[ssh_pf_lcl_n].host),h); makestr(&h,NULL); ssh_pf_lcl[ssh_pf_lcl_n].p2 = k; ssh_pf_lcl_n++; break; case SSHF_RMT: if (ssh_pf_rmt_n == 32) { printf( "?Maximum number of remote port forwardings already specified\n" ); free(h); return(success = 0); } ssh_pf_rmt[ssh_pf_rmt_n].p1 = j; makestr(&(ssh_pf_rmt[ssh_pf_rmt_n].host),h); makestr(&h,NULL); ssh_pf_rmt[ssh_pf_rmt_n].p2 = k; ssh_pf_rmt_n++; } return(success = 1); } /* Not supporting arbitrary forwarding yet */ case XSSH_FLP: /* SSH FORWARD-LOCAL-PORT */ case XSSH_FRP: { /* SSH FORWARD-REMOTE-PORT */ int li_port = 0; int to_port = 0; char * fw_host = NULL; int n; if ((x = cmnum(cmresult.nresult == XSSH_FLP ? "local-port":"remote-port", "",10,&li_port,xxstring)) < 0) return(x); if (li_port < 1 || li_port > 65535) { printf("?Out range - min: 1, max: 65535\n"); return(-9); } if ((x = cmfld("host",ssh_hst?ssh_hst:"",&s,xxstring)) < 0) return(x); n = ckstrncpy(tmpbuf,s,TMPBUFSIZ); fw_host = tmpbuf; if ((x = cmnum("host-port",ckuitoa(li_port),10, &to_port,xxstring)) < 0) return(x); if (to_port < 1 || to_port > 65535) { printf("?Out range - min: 1, max: 65535\n"); return(-9); } if ((x = cmcfm()) < 0) return(x); switch (cmresult.nresult) { case XSSH_FLP: /* SSH FORWARD-LOCAL-PORT */ #ifndef SSHTEST ssh_fwd_local_port(li_port,fw_host,to_port); #endif /* SSHTEST */ return(success = 1); case XSSH_FRP: /* SSH FORWARD-REMOTE-PORT */ #ifndef SSHTEST ssh_fwd_remote_port(li_port,fw_host,to_port); #endif /* SSHTEST */ return(success = 1); } return(success = 1); } case XSSH_V2: /* SSH V2 */ if ((cx = cmkey(ssh2tab,nssh2tab,"","", xxstring)) < 0) return(cx); switch (cx) { case XSSH2_RKE: if ((x = cmcfm()) < 0) return(x); #ifndef SSHTEST ssh_v2_rekey(); #endif /* SSHTEST */ return(success = 1); default: return(-2); } case XSSH_KEY: if ((cx = cmkey(sshkey,nsshkey,"","", xxstring)) < 0) return(cx); switch (cx) { case SSHK_PASS: { /* Change passphrase */ char * oldp = NULL, * newp = NULL; struct FDB df, sw; cmfdbi(&sw, _CMKEY, /* fcode */ "Filename, or switch", /* hlpmsg */ "", /* default */ "", /* addtl string data */ 2, /* addtl numeric data 1: tbl size */ 4, /* addtl numeric data 2: 4 = cmswi */ xxstring, /* Processing function */ sshkpsw, /* Keyword table */ &df /* Pointer to next FDB */ ); cmfdbi(&df, /* 2nd FDB - file for display */ _CMIFI, /* output file */ "", /* hlpmsg */ "", /* default */ "", /* addtl string data */ 0, /* addtl numeric data 1 */ 0, /* addtl numeric data 2 */ xxstring, NULL, NULL ); line[0] = NUL; while (1) { x = cmfdb(&sw); if (x == -3) break; if (x < 0) return(x); if (cmresult.fcode != _CMKEY) break; if (!cmgbrk()) { printf("?This switch requires an argument\n"); return(-9); } if ((y = cmfld("Passphrase","",&s,xxstring)) < 0) return(y); switch (cmresult.nresult) { case 1: /* Old */ makestr(&oldp,s); break; case 2: /* New */ makestr(&newp,s); } } if (cmresult.fcode == _CMIFI) { /* Filename */ ckstrncpy(line,cmresult.sresult,LINBUFSIZ); if (zfnqfp(line,TMPBUFSIZ,tmpbuf)) ckstrncpy(line,tmpbuf,LINBUFSIZ); } if ((x = cmcfm()) < 0) return(x); #ifndef SSHTEST x = sshkey_change_passphrase(line[0] ? line : NULL, oldp, newp); #endif /* SSHTEST */ makestr(&oldp,NULL); makestr(&newp,NULL); success = (x == 0); return(success); } case SSHK_CREA: { /* SSH KEY CREATE /switches... */ int bits = 1024, keytype = SSHKT_2R; char * pass = NULL, * comment = NULL; struct FDB df, sw; /* * char * sshkey_default_file(int keytype) * will provide the default filename for a given keytype * is it possible to have the default value for the 2nd * FDB set and changed when a /TYPE switch is provided? * Would this allow for tab completion of the filename? */ cmfdbi(&sw, _CMKEY, /* fcode */ "Filename, or switch", /* hlpmsg */ "", /* default */ "", /* addtl string data */ nsshkcrea, /* addtl numeric data 1: tbl size */ 4, /* addtl numeric data 2: 4 = cmswi */ xxstring, /* Processing function */ sshkcrea, /* Keyword table */ &df /* Pointer to next FDB */ ); cmfdbi(&df, /* 2nd FDB - file for display */ _CMOFI, /* output file */ "", /* hlpmsg */ "", /* default */ "", /* addtl string data */ 0, /* addtl numeric data 1 */ 0, /* addtl numeric data 2 */ xxstring, NULL, NULL ); line[0] = NUL; while (1) { x = cmfdb(&sw); if (x == -3) break; if (x < 0) return(x); if (cmresult.fcode != _CMKEY) break; if (!cmgbrk()) { printf("?This switch requires an argument\n"); return(-9); } switch (cmresult.nresult) { case SSHKC_BI: /* /BITS:n */ if ((y = cmnum("","1024",10,&z,xxstring)) < 0) return(y); if (z < 512 || z > 4096) { printf("?Out range - min: 512, max: 4096\n"); return(-9); } bits = z; break; case SSHKC_PP: /* /PASSPHRASE:blah */ if ((y = cmfld("Passphrase","",&s,xxstring)) < 0) return(y); makestr(&pass,s); break; case SSHKC_TY: /* /TYPE:keyword */ if ((y = cmkey(sshkcty,nsshkcty,"", "v2-rsa",xxstring)) < 0) return(y); keytype = y; break; case SSHKC_1R: /* /COMMENT */ if ((y = cmfld("Text","",&s,xxstring)) < 0) return(y); makestr(&comment,s); break; } } if (cmresult.fcode == _CMOFI) { /* Filename */ if (cmresult.sresult) { ckstrncpy(line,cmresult.sresult,LINBUFSIZ); if (zfnqfp(line,TMPBUFSIZ,tmpbuf)) ckstrncpy(line,tmpbuf,LINBUFSIZ); } } if ((y = cmcfm()) < 0) /* Confirm */ return(y); #ifndef SSHTEST x = sshkey_create(line[0] ? line : NULL, bits, pass, keytype, comment); if (pass) memset(pass,0,strlen(pass)); #endif /* SSHTEST */ makestr(&pass,NULL); makestr(&comment,NULL); return(success = (x == 0)); } case SSHK_DISP: { /* SSH KEY DISPLAY /switches... */ char c; int infmt = 0, outfmt = 0; struct FDB df, sw; cmfdbi(&sw, _CMKEY, /* fcode */ "Filename, or switch", /* hlpmsg */ "", /* default */ "", /* addtl string data */ nsshdswi, /* addtl numeric data 1: tbl size */ 4, /* addtl numeric data 2: 4 = cmswi */ xxstring, /* Processing function */ sshdswi, /* Keyword table */ &df /* Pointer to next FDB */ ); cmfdbi(&df, /* 2nd FDB - file for display */ _CMIFI, /* fcode */ "", /* hlpmsg */ "", /* default */ "", /* addtl string data */ 0, /* addtl numeric data 1 */ 0, /* addtl numeric data 2 */ xxstring, NULL, NULL ); line[0] = NUL; while (1) { x = cmfdb(&sw); if (x == -3) break; if (x < 0) return(x); if (cmresult.fcode != _CMKEY) break; if (!cmgbrk()) { printf("?This switch requires an argument\n"); return(-9); } switch (cmresult.nresult) { #ifdef COMMENT case SSHKD_IN: /* /IN-FORMAT: */ if ((y = cmkey(sshdifmt,nsshdifmt, "","",xxstring)) < 0) return(y); infmt = y; break; #endif /* COMMENT */ case SSHKD_OUT: /* /FORMAT: */ if ((y = cmkey(sshdofmt,nsshdofmt, "","",xxstring)) < 0) return(y); outfmt = y; break; } } if (cmresult.fcode == _CMIFI) { /* Filename */ ckstrncpy(line,cmresult.sresult,LINBUFSIZ); if (zfnqfp(line,TMPBUFSIZ,tmpbuf)) ckstrncpy(line,tmpbuf,LINBUFSIZ); } #ifdef COMMENT if (!line[0]) { printf("?Key filename required\n"); return(-9); } #endif /* COMMENT */ if ((y = cmcfm()) < 0) /* Confirm */ return(y); #ifndef SSHTEST switch (outfmt) { case SKDF_OSSH: /* 2nd param is optional passphrase */ x = sshkey_display_public(line[0] ? line : NULL, NULL); break; case SKDF_SSHC: /* 2nd param is optional passphrase */ x = sshkey_display_public_as_ssh2(line[0] ? line : NULL, NULL); break; case SKDF_IETF: x = sshkey_display_fingerprint(line[0] ? line : NULL, 1); break; case SKDF_FING: x = sshkey_display_fingerprint(line[0] ? line : NULL, 0); break; } #endif /* SSHTEST */ return(success = (x == 0)); } case SSHK_V1: /* SSH KEY V1 SET-COMMENT */ if ((x = cmkey(sshkv1,1,"","set-comment", xxstring)) < 0) return(x); if (x != 1) return(-2); if ((x = cmifi("Key file name","",&s,&y,xxstring)) < 0) { if (x == -3) { printf("?Name of key file required\n"); return(-9); } } ckstrncpy(line,s,LINBUFSIZ); if ((x = cmtxt("Comment text","",&s,xxstring)) < 0) return(x); #ifndef SSHTEST x = sshkey_v1_change_comment(line, /* filename */ s, /* new comment */ NULL /* passphrase */ ); #endif /* SSHTEST */ success = (x == 0); return(success); } default: return(-2); } #else /* SSHBUILTIN */ #ifdef SSHCMD x = nettype; if ((y = setlin(XXSSH,0,1)) < 0) { if (errno) printf("?%s\n",ck_errstr()); else #ifdef COMMENT /* This isn't right either because it catches command editing */ printf("?Sorry, pseudoterminal open failed\n"); if (hints) printf("Hint: Try \"ssh -t %s\"\n",line); #else return(y); #endif /* COMMENT */ nettype = x; /* Failed, restore net type. */ ttnproto = z; /* and protocol */ success = 0; } didsetlin++; netsave = x; return(y); #endif /* SSHCMD */ #endif /* SSHBUILTIN */ } #endif /* ANYSSH */ #ifdef SSHBUILTIN if (cx == XXSKRM) { /* SKERMIT (Secure Shell Kermit) */ extern int netsave; int k, x, havehost = 0, trips = 0; int tmpver = -1, tmpxfw = -1; #ifndef SSHTEST extern int sl_ssh_xfw, sl_ssh_xfw_saved; extern int sl_ssh_ver, sl_ssh_ver_saved; #endif /* SSHTEST */ extern int mdmtyp, mdmsav, cxtype, sl_uid_saved; extern char * slmsg; extern char uidbuf[], sl_uidbuf[]; extern char pwbuf[], * g_pswd; extern int pwflg, pwcrypt, g_pflg, g_pcpt, nolocal; struct FDB sw, kw, fl; if (ssh_tmpstr) memset(ssh_tmpstr,0,strlen(ssh_tmpstr)); makestr(&ssh_tmpstr,NULL); makestr(&ssh_tmpuid,NULL); makestr(&ssh_tmpcmd,NULL); makestr(&ssh_tmpport,NULL); cmfdbi(&kw, /* 1st FDB - commands */ _CMKEY, /* fcode */ "host [ port ],\n or action", /* hlpmsg */ "", /* default */ "", /* addtl string data */ nsshkermit, /* addtl numeric data 1: tbl size */ 0, /* addtl numeric data 2: 0 = keyword */ xxstring, /* Processing function */ sshkermit, /* Keyword table */ &fl /* Pointer to next FDB */ ); cmfdbi(&fl, /* Host */ _CMFLD, /* fcode */ "", /* hlpmsg */ "", /* default */ "", /* addtl string data */ 0, /* addtl numeric data 1 */ 0, /* addtl numeric data 2 */ xxstring, NULL, NULL ); x = cmfdb(&kw); if (x == -3) { printf("?skermit what?\n"); return(-9); } if (x < 0) return(x); havehost = 0; if (cmresult.fcode == _CMFLD) { havehost = 1; ckstrncpy(line,cmresult.sresult,LINBUFSIZ); /* Hostname */ cmresult.nresult = SKRM_OPN; } switch (cmresult.nresult) { /* SSH keyword */ case SKRM_OPN: /* SSH OPEN */ if (!havehost) { if ((x = cmfld("Host","",&s,xxstring)) < 0) return(x); ckstrncpy(line,s,LINBUFSIZ); } /* Parse [ port ] [ switches ] */ cmfdbi(&kw, /* Switches */ _CMKEY, "Port number or service name,\nor switch", "", "", nsshkrmopnsw, 4, xxstring, sshkrmopnsw, &fl ); cmfdbi(&fl, /* Port number or service name */ _CMFLD, "", "", "", 0, 0, xxstring, NULL, NULL ); trips = 0; /* Explained below */ while (1) { /* Parse port and switches */ x = cmfdb(&kw); /* Get a field */ if (x == -3) /* User typed CR so quit from loop */ break; if (x < 0) /* Other parse error, pass it back */ return(x); switch (cmresult.fcode) { /* Field or Keyword? */ case _CMFLD: /* Field */ makestr(&ssh_tmpport,cmresult.sresult); break; case _CMKEY: /* Keyword */ switch (cmresult.nresult) { /* Which one? */ case SSHSW_USR: /* /USER: */ if (!cmgbrk()) { printf("?This switch requires an argument\n"); return(-9); } if ((y = cmfld("Username","",&s,xxstring)) < 0) return(y); s = brstrip(s); makestr(&ssh_tmpuid,s); break; case SSHSW_PWD: if (!cmgbrk()) { printf("?This switch requires an argument\n"); return(-9); } debok = 0; if ((x = cmfld("Password","",&s,xxstring)) < 0) { if (x == -3) { makestr(&ssh_tmpstr,""); } else { return(x); } } else { s = brstrip(s); if ((x = (int)strlen(s)) > PWBUFL) { makestr(&slmsg,"Internal error"); printf("?Sorry, too long - max = %d\n",PWBUFL); return(-9); } makestr(&ssh_tmpstr,s); } break; case SSHSW_VER: if ((x = cmnum("Number","",10,&z,xxstring)) < 0) return(x); if (z < 1 || z > 2) { printf("?Out of range: %d\n",z); return(-9); } tmpver = z; break; default: return(-2); } } if (trips++ == 0) { /* After first time through */ cmfdbi(&kw, /* only parse switches, not port. */ _CMKEY, "Switch", "", "", nsshkrmopnsw, 4, xxstring, sshkrmopnsw, NULL ); } } if ((x = cmcfm()) < 0) /* Get confirmation */ return(x); if (clskconnx(1) < 0) { /* Close current Kermit connection */ if ( ssh_tmpstr ) { memset(ssh_tmpstr,0,strlen(ssh_tmpstr)); makestr(&ssh_tmpstr,NULL); } return(success = 0); } makestr(&ssh_hst,line); /* Stash everything */ if (ssh_tmpuid) { if (!sl_uid_saved) { ckstrncpy(sl_uidbuf,uidbuf,UIDBUFLEN); sl_uid_saved = 1; } ckstrncpy(uidbuf,ssh_tmpuid,UIDBUFLEN); makestr(&ssh_tmpuid,NULL); } if (ssh_tmpport) { makestr(&ssh_prt,ssh_tmpport); makestr(&ssh_tmpport,NULL); } else makestr(&ssh_prt,NULL); /* Set the Subsystem to Kermit */ ssh_cas = 1; makestr(&ssh_cmd,"kermit"); if (tmpver > -1) { #ifndef SSHTEST if (!sl_ssh_ver_saved) { sl_ssh_ver = ssh_ver; sl_ssh_ver_saved = 1; } #endif /* SSHTEST */ ssh_ver = tmpver; } /* Disable X11 Forwarding */ #ifndef SSHTEST if (!sl_ssh_xfw_saved) { sl_ssh_xfw = ssh_xfw; sl_ssh_xfw_saved = 1; } #endif /* SSHTEST */ ssh_xfw = 0; if (ssh_tmpstr) { if (ssh_tmpstr[0]) { ckstrncpy(pwbuf,ssh_tmpstr,PWBUFL+1); pwflg = 1; pwcrypt = 0; } else pwflg = 0; makestr(&ssh_tmpstr,NULL); } nettype = NET_SSH; if (mdmsav < 0) mdmsav = mdmtyp; mdmtyp = -nettype; x = 1; #ifndef NOSPL makestr(&g_pswd,pwbuf); /* Save global pwbuf */ g_pflg = pwflg; /* and flag */ g_pcpt = pwcrypt; #endif /* NOSPL */ /* Line parameter to ttopen() is ignored */ k = ttopen(line,&x,mdmtyp, 0); if (k < 0) { printf("?Unable to connect to %s\n",ssh_hst); mdmtyp = mdmsav; slrestor(); return(success = 0); } duplex = 0; /* Remote echo */ ckstrncpy(ttname,line,TTNAMLEN); /* Record the command */ debug(F110,"ssh ttname",ttname,0); makestr(&slmsg,NULL); /* No SET LINE error message */ cxtype = CXT_SSH; #ifndef NODIAL dialsta = DIA_UNK; #endif /* NODIAL */ success = 1; /* SET LINE succeeded */ network = 1; /* Network connection (not serial) */ local = 1; /* Local mode (not remote) */ if ((reliable != SET_OFF || !setreliable)) reliable = SET_ON; /* Transport is reliable end to end */ #ifdef OS2 DialerSend(OPT_KERMIT_CONNECT, 0); #endif /* OS2 */ setflow(); /* Set appropriate flow control */ haveline = 1; #ifdef CKLOGDIAL #ifdef NETCONN dolognet(); #endif /* NETCONN */ #endif /* CKLOGDIAL */ #ifndef NOSPL if (local) { if (nmac) { /* Any macros defined? */ int k; /* Yes */ k = mlook(mactab,"on_open",nmac); /* Look this up */ if (k >= 0) { /* If found, */ if (dodo(k,ssh_hst,0) > -1) /* set it up, */ parser(1); /* and execute it */ } } } #endif /* NOSPL */ #ifdef LOCUS if (autolocus) setlocus(1,1); #endif /* LOCUS */ /* Command was confirmed so we can pre-pop command level. */ /* This is so CONNECT module won't think we're executing a */ /* script if CONNECT was the final command in the script. */ if (cmdlvl > 0) prepop(); return(success = 1); default: return(-2); } } #endif /* SSHBUILTIN */ #ifdef SFTP_BUILTIN if (cx == XXSFTP) { /* SFTP (Secure Shell File Transfer) */ extern int netsave; int k, x, havehost = 0, trips = 0; int tmpver = -1, tmpxfw = -1; #ifndef SSHTEST extern int sl_ssh_xfw, sl_ssh_xfw_saved; extern int sl_ssh_ver, sl_ssh_ver_saved; #endif /* SSHTEST */ extern int mdmtyp, mdmsav, cxtype, sl_uid_saved; extern char * slmsg; extern char uidbuf[], sl_uidbuf[]; extern char pwbuf[], * g_pswd; extern int pwflg, pwcrypt, g_pflg, g_pcpt, nolocal; struct FDB sw, kw, fl; if (ssh_tmpstr) memset(ssh_tmpstr,0,strlen(ssh_tmpstr)); makestr(&ssh_tmpstr,NULL); makestr(&ssh_tmpuid,NULL); makestr(&ssh_tmpcmd,NULL); makestr(&ssh_tmpport,NULL); cmfdbi(&kw, /* 1st FDB - commands */ _CMKEY, /* fcode */ "host [ port ],\n or action", /* hlpmsg */ "", /* default */ "", /* addtl string data */ nsftpkwtab, /* addtl numeric data 1: tbl size */ 0, /* addtl numeric data 2: 0 = keyword */ xxstring, /* Processing function */ sftpkwtab, /* Keyword table */ &fl /* Pointer to next FDB */ ); cmfdbi(&fl, /* Host */ _CMFLD, /* fcode */ "", /* hlpmsg */ "", /* default */ "", /* addtl string data */ 0, /* addtl numeric data 1 */ 0, /* addtl numeric data 2 */ xxstring, NULL, NULL ); x = cmfdb(&kw); if (x == -3) { printf("?sftp what?\n"); return(-9); } if (x < 0) return(x); havehost = 0; if (cmresult.fcode == _CMFLD) { havehost = 1; ckstrncpy(line,cmresult.sresult,LINBUFSIZ); /* Hostname */ cmresult.nresult = SFTP_OPN; } switch (cmresult.nresult) { /* SFTP keyword */ case SFTP_OPN: /* SFTP OPEN */ if (!havehost) { if ((x = cmfld("Host","",&s,xxstring)) < 0) return(x); ckstrncpy(line,s,LINBUFSIZ); } /* Parse [ port ] [ switches ] */ cmfdbi(&kw, /* Switches */ _CMKEY, "Port number or service name,\nor switch", "", "", nsshkrmopnsw, 4, xxstring, sshkrmopnsw, &fl ); cmfdbi(&fl, /* Port number or service name */ _CMFLD, "", "", "", 0, 0, xxstring, NULL, NULL ); trips = 0; /* Explained below */ while (1) { /* Parse port and switches */ x = cmfdb(&kw); /* Get a field */ if (x == -3) /* User typed CR so quit from loop */ break; if (x < 0) /* Other parse error, pass it back */ return(x); switch (cmresult.fcode) { /* Field or Keyword? */ case _CMFLD: /* Field */ makestr(&ssh_tmpport,cmresult.sresult); break; case _CMKEY: /* Keyword */ switch (cmresult.nresult) { /* Which one? */ case SSHSW_USR: /* /USER: */ if (!cmgbrk()) { printf("?This switch requires an argument\n"); return(-9); } if ((y = cmfld("Username","",&s,xxstring)) < 0) return(y); s = brstrip(s); makestr(&ssh_tmpuid,s); break; case SSHSW_PWD: if (!cmgbrk()) { printf("?This switch requires an argument\n"); return(-9); } debok = 0; if ((x = cmfld("Password","",&s,xxstring)) < 0) { if (x == -3) { makestr(&ssh_tmpstr,""); } else { return(x); } } else { s = brstrip(s); if ((x = (int)strlen(s)) > PWBUFL) { makestr(&slmsg,"Internal error"); printf("?Sorry, too long - max = %d\n",PWBUFL); return(-9); } makestr(&ssh_tmpstr,s); } break; case SSHSW_VER: if ((x = cmnum("Number","",10,&z,xxstring)) < 0) return(x); if (z < 1 || z > 2) { printf("?Out of range: %d\n",z); return(-9); } tmpver = z; break; default: return(-2); } } if (trips++ == 0) { /* After first time through */ cmfdbi(&kw, /* only parse switches, not port. */ _CMKEY, "Switch", "", "", nsshkrmopnsw, 4, xxstring, sshkrmopnsw, NULL ); } } if ((x = cmcfm()) < 0) /* Get confirmation */ return(x); if (clskconnx(1) < 0) { /* Close current Kermit connection */ if ( ssh_tmpstr ) { memset(ssh_tmpstr,0,strlen(ssh_tmpstr)); makestr(&ssh_tmpstr,NULL); } return(success = 0); } makestr(&ssh_hst,line); /* Stash everything */ if (ssh_tmpuid) { if (!sl_uid_saved) { ckstrncpy(sl_uidbuf,uidbuf,UIDBUFLEN); sl_uid_saved = 1; } ckstrncpy(uidbuf,ssh_tmpuid,UIDBUFLEN); makestr(&ssh_tmpuid,NULL); } if (ssh_tmpport) { makestr(&ssh_prt,ssh_tmpport); makestr(&ssh_tmpport,NULL); } else makestr(&ssh_prt,NULL); /* Set the Subsystem to Kermit */ ssh_cas = 1; makestr(&ssh_cmd,"sftp"); if (tmpver > -1) { #ifndef SSHTEST if (!sl_ssh_ver_saved) { sl_ssh_ver = ssh_ver; sl_ssh_ver_saved = 1; } #endif /* SSHTEST */ ssh_ver = tmpver; } /* Disable X11 Forwarding */ #ifndef SSHTEST if (!sl_ssh_xfw_saved) { sl_ssh_xfw = ssh_xfw; sl_ssh_xfw_saved = 1; } #endif /* SSHTEST */ ssh_xfw = 0; if (ssh_tmpstr) { if (ssh_tmpstr[0]) { ckstrncpy(pwbuf,ssh_tmpstr,PWBUFL+1); pwflg = 1; pwcrypt = 0; } else pwflg = 0; makestr(&ssh_tmpstr,NULL); } nettype = NET_SSH; if (mdmsav < 0) mdmsav = mdmtyp; mdmtyp = -nettype; x = 1; #ifndef NOSPL makestr(&g_pswd,pwbuf); /* Save global pwbuf */ g_pflg = pwflg; /* and flag */ g_pcpt = pwcrypt; #endif /* NOSPL */ /* Line parameter to ttopen() is ignored */ k = ttopen(line,&x,mdmtyp, 0); if (k < 0) { printf("?Unable to connect to %s\n",ssh_hst); mdmtyp = mdmsav; slrestor(); return(success = 0); } duplex = 0; /* Remote echo */ ckstrncpy(ttname,line,TTNAMLEN); /* Record the command */ debug(F110,"ssh ttname",ttname,0); makestr(&slmsg,NULL); /* No SET LINE error message */ cxtype = CXT_SSH; #ifndef NODIAL dialsta = DIA_UNK; #endif /* NODIAL */ success = 1; /* SET LINE succeeded */ network = 1; /* Network connection (not serial) */ local = 1; /* Local mode (not remote) */ if ((reliable != SET_OFF || !setreliable)) reliable = SET_ON; /* Transport is reliable end to end */ #ifdef OS2 DialerSend(OPT_KERMIT_CONNECT, 0); #endif /* OS2 */ setflow(); /* Set appropriate flow control */ haveline = 1; #ifdef CKLOGDIAL #ifdef NETCONN dolognet(); #endif /* NETCONN */ #endif /* CKLOGDIAL */ #ifndef NOSPL if (local) { if (nmac) { /* Any macros defined? */ int k; /* Yes */ k = mlook(mactab,"on_open",nmac); /* Look this up */ if (k >= 0) { /* If found, */ if (dodo(k,ssh_hst,0) > -1) /* set it up, */ parser(1); /* and execute it */ } } } #endif /* NOSPL */ #ifdef LOCUS if (autolocus) setlocus(1,1); #endif /* LOCUS */ /* Command was confirmed so we can pre-pop command level. */ /* This is so CONNECT module won't think we're executing a */ /* script if CONNECT was the final command in the script. */ if (cmdlvl > 0) prepop(); success = sftp_do_init(); return(success = 1); case SFTP_CD: case SFTP_CHGRP: case SFTP_CHMOD: case SFTP_CHOWN: case SFTP_RM: case SFTP_DIR: case SFTP_GET: case SFTP_MKDIR: case SFTP_PUT: case SFTP_PWD: case SFTP_REN: case SFTP_RMDIR: case SFTP_LINK: case SFTP_VER: if ((y = cmtxt("command parameters","",&s,xxstring)) < 0) return(y); if (ssh_tchk() < 0 || !ssh_cas || strcmp(ssh_cmd,"sftp")) { printf("?Not connected to SFTP Service\n"); return(success = 0); } success = sftp_do_cmd(cmresult.nresult,s); return(success); default: return(-2); } } #endif /* SFTP_BUILTIN */ if (cx == XXRLOG) { /* RLOGIN */ #ifdef RLOGCODE int x,z; #ifdef OS2 if (!tcp_avail) { printf("?Sorry, either TCP/IP is not available on this system or\n\ necessary DLLs did not load. Use SHOW NETWORK to check network status.\n" ); success = 0; return(-9); } else { #endif /* OS2 */ x = nettype; /* Save net type in case of failure */ z = ttnproto; /* Save protocol in case of failure */ nettype = NET_TCPB; ttnproto = NP_RLOGIN; if ((y = setlin(XYHOST,0,1)) <= 0) { nettype = x; /* Failed, restore net type. */ ttnproto = z; /* and protocol */ success = 0; } didsetlin++; #ifdef OS2 } #endif /* OS2 */ return(y); #else printf("?Sorry, RLOGIN is not configured in this copy of C-Kermit.\n"); return(-9); #endif /* RLOGCODE */ } #endif /* NETCONN */ #endif /* NOLOCAL */ #ifndef NOXMIT if (cx == XXTRA) { /* TRANSMIT */ extern int xfrxla; int i, n, xpipe = 0, xbinary = 0, xxlate = 1, xxnowait = 0, getval; int xxecho = 0; int scan = 1; char c; struct FDB sf, sw, tx; /* FDBs for parse functions */ #ifndef NOCSETS extern int tcs_transp; /* Term charset is transparent */ #else int tcs_transp = 1; #endif /* NOCSETS */ #ifdef COMMENT xbinary = binary; /* Default text/binary mode */ #else xbinary = 0; /* Default is text */ #endif /* COMMENT */ xxecho = xmitx; cmfdbi(&sw, /* First FDB - command switches */ _CMKEY, /* fcode */ "Filename, or switch", /* hlpmsg */ "", /* default */ "", /* addtl string data */ nxmitsw, /* addtl numeric data 1: tbl size */ 4, /* addtl numeric data 2: 4 = cmswi */ xxstring, /* Processing function */ xmitsw, /* Keyword table */ &sf /* Pointer to next FDB */ ); cmfdbi(&sf, /* 2nd FDB - file to send */ _CMIFI, /* fcode */ "File to transmit", /* hlpmsg */ "", /* default */ "", /* addtl string data */ 0, /* addtl numeric data 1 */ 0, /* addtl numeric data 2 */ xxstring, NULL, #ifdef PIPESEND &tx #else NULL #endif /* PIPESEND */ ); #ifdef PIPESEND cmfdbi(&tx, _CMTXT, /* fcode */ "Command", /* hlpmsg */ "", /* default */ "", /* addtl string data */ 0, /* addtl numeric data 1 */ 0, /* addtl numeric data 2 */ xxstring, NULL, NULL ); #endif /* PIPESEND */ while (1) { x = cmfdb(&sw); if (x < 0) return(x); if (cmresult.fcode != _CMKEY) break; c = cmgbrk(); /* Have switch, get break character */ if ((getval = (c == ':' || c == '=')) && !(cmgkwflgs() & CM_ARG)) { printf("?This switch does not take an argument\n"); return(-9); } if (!getval && (cmgkwflgs() & CM_ARG)) { printf("?This switch requires an argument\n"); return(-9); } n = cmresult.nresult; /* Numeric result = switch ID */ switch (n) { /* Process the switch */ #ifdef PIPESEND case XMI_CMD: /* Transmit from a command */ if (nopush) { printf("?Sorry, system command access is disabled\n"); return(-9); } sw.hlpmsg = "Command, or switch"; /* Change help message */ xpipe = 1; /* (No way to undo this one) */ break; #endif /* PIPESEND */ case XMI_BIN: /* Binary */ xbinary = 1; xxlate = 0; /* Don't translate charsets */ scan = 0; break; case XMI_TXT: /* Text */ xbinary = 0; xxlate = !tcs_transp; /* Translate if TERM CHAR not TRANSP */ scan = 0; break; case XMI_TRA: /* Transparent text */ xbinary = 0; xxlate = 0; /* But don't translate charsets */ scan = 0; break; #ifdef COMMENT case XMI_VRB: /* /VERBOSE */ case XMI_QUI: /* /QUIET */ break; /* (not implemented yet) */ #endif /* COMMENT */ case XMI_NOW: /* /NOWAIT */ xxnowait = 1; break; case XMI_NOE: /* /NOWAIT */ xxecho = 0; break; default: return(-2); } } if (cmresult.fcode != _CMIFI && cmresult.fcode != _CMTXT) return(-2); ckstrncpy(line,cmresult.sresult,LINBUFSIZ); /* Filename */ if (zfnqfp(line,TMPBUFSIZ,tmpbuf)) ckstrncpy(line,tmpbuf,LINBUFSIZ); s = line; if ((y = cmcfm()) < 0) /* Confirm */ return(y); #ifdef CK_APC if ((apcactive == APC_LOCAL) || ((apcactive == APC_REMOTE) && (!(apcstatus & APC_UNCH)))) return(success = 0); #endif /* CK_APC */ if (cmresult.nresult != 0) { printf("?Only a single file may be transmitted\n"); return(-9); } #ifdef PIPESEND if (xpipe) { s = brstrip(s); if (!*s) { printf("?Sorry, a command to send from is required\n"); return(-9); } pipesend = 1; } #endif /* PIPESEND */ if (scan && (filepeek #ifndef NOXFER || patterns #endif /* NOXFER */ )) { /* If user didn't specify type */ int k, x; /* scan the file to see */ x = -1; k = scanfile(s,&x,nscanfile); if (k > 0) xbinary = (k == FT_BIN) ? XYFT_B : XYFT_T; } if (!xfrxla) xxlate = 0; success = transmit(s, (char) (xxnowait ? '\0' : (char)xmitp), xxlate, xbinary, xxecho ); return(success); } #endif /* NOXMIT */ #ifndef NOFRILLS if (cx == XXTYP || cx == XXCAT || cx == XXMORE || cx == XXHEAD || cx == XXTAIL) { int paging = 0, havename = 0, head = 0, width = 0; int height = 0, count = 0; char pfxbuf[64], * prefix = NULL; char outfile[CKMAXPATH+1]; struct FDB sf, sw; char * pat = NULL; int incs = 0, outcs = 0, cset = -1, number = 0; #ifdef UNICODE char * tocs = ""; extern int fileorder; #ifdef OS2 #ifdef NT char guibuf[128], * gui_title = NULL; int gui = 0; #endif /* NT */ #ifndef NOCSETS extern int tcsr, tcsl; #endif /* NOCSETS */ #endif /* OS2 */ #endif /* UNICODE */ outfile[0] = NUL; if (cx == XXMORE) paging = 1; else if (cx == XXCAT) paging = 0; else paging = (typ_page < 0) ? xaskmore : typ_page; if (paging < 0) paging = saveask; if (cx == XXHEAD) { head = 10; cx = XXTYP; } else if (cx == XXTAIL) { head = -10; cx = XXTYP; } #ifdef IKSD if (inserver && !ENABLED(en_typ)) { printf("?Sorry, TYPE command disabled\n"); return(-9); } #endif /* IKSD */ cmfdbi(&sw, /* 2nd FDB - optional /PAGE switch */ _CMKEY, /* fcode */ "Filename or switch", /* hlpmsg */ "", /* default */ "", /* addtl string data */ ntypetab, /* addtl numeric data 1: tbl size */ 4, /* addtl numeric data 2: 4 = cmswi */ xxstring, /* Processing function */ typetab, /* Keyword table */ &sf /* Pointer to next FDB */ ); cmfdbi(&sf, /* 1st FDB - file to type */ _CMIFI, /* fcode */ "", /* hlpmsg */ "", /* default */ "", /* addtl string data */ 0, /* addtl numeric data 1 */ 0, /* addtl numeric data 2 */ xxstring, NULL, NULL ); while (!havename) { x = cmfdb(&sw); /* Parse something */ debug(F101,"type cmfdb","",x); debug(F101,"type cmresult.fcode","",cmresult.fcode); debug(F101,"type cmresult.nresult","",cmresult.nresult); if (x < 0) { /* Error */ if (x == -3) { x = -9; printf("?Filename required\n"); } return(x); } else if (cmresult.fcode == _CMKEY) { char c; int getval; c = cmgbrk(); getval = (c == ':' || c == '='); if (getval && !(cmgkwflgs() & CM_ARG)) { printf("?This switch does not take an argument\n"); return(-9); } #ifdef COMMENT if (!getval && (cmgkwflgs() & CM_ARG)) { printf("?This switch requires an argument\n"); /* Not if it has a default! */ return(-9); } #endif /* COMMENT */ switch (cmresult.nresult) { #ifdef CK_TTGWSIZ case TYP_PAG: paging = 1; break; case TYP_NOP: paging = 0; break; #endif /* CK_TTGWSIZ */ case TYP_COU: paging = 0; count = 1; break; case TYP_HEA: case TYP_TAI: y = 10; if (getval) if ((x = cmnum("Number of lines", "10",10,&y,xxstring)) < 0) return(x); head = (cmresult.nresult == TYP_TAI) ? -y : y; break; case TYP_WID: y = typ_wid > -1 ? typ_wid : cmd_cols; if (getval) if ((x = cmnum("Column at which to truncate", ckitoa(y),10,&y,xxstring)) < 0) return(x); width = y; break; #ifdef KUI case TYP_HIG: if (getval) if ((x = cmnum("Height of GUI dialog", ckitoa(y),10,&y,xxstring)) < 0) return(x); height = y; break; #endif /* KUI */ case TYP_PAT: if (!getval && (cmgkwflgs() & CM_ARG)) { printf("?This switch requires an argument\n"); return(-9); } if ((x = cmfld("pattern","",&s,xxstring)) < 0) return(x); ckstrncpy(tmpbuf,s,TMPBUFSIZ); pat = tmpbuf; break; case TYP_PFX: if (!getval && (cmgkwflgs() & CM_ARG)) { printf("?This switch requires an argument\n"); return(-9); } if ((x = cmfld("prefix for each line","",&s,xxstring)) < 0) return(x); if ((int)strlen(s) > 63) { printf("?Too long - 63 max\n"); return(-9); } ckstrncpy(pfxbuf,s,64); prefix = brstrip(pfxbuf); number = 0; break; #ifdef KUI case TYP_GUI: if (!getval && (cmgkwflgs() & CM_ARG)) { printf("?This switch requires an argument\n"); return(-9); } if ((x = cmfld("Dialog box title","",&s,xxstring)) < 0) { if (x != -3) return(x); } else { if ((int)strlen(s) > 127) { printf("?Too long - 127 max\n"); return(-9); } ckstrncpy(guibuf,s,128); gui_title = brstrip(guibuf); } gui = 1; break; #endif /* KUI */ case TYP_NUM: /* /NUMBER */ number = 1; prefix = NULL; break; #ifdef UNICODE case TYP_XPA: /* /TRANSPARENT */ incs = 0; cset = 0; outcs = -1; break; case TYP_XIN: /* /CHARACTER-SET: */ if (!getval && (cmgkwflgs() & CM_ARG)) { printf("?This switch requires an argument\n"); return(-9); } if ((incs = cmkey(fcstab,nfilc, "character-set name","",xxstring)) < 0) { if (incs == -3) /* Note: No default */ incs = -2; return(incs); } cset = incs; break; case TYP_XUT: /* /TRANSLATE-TO: */ if (!getval && (cmgkwflgs() & CM_ARG)) { printf("?This switch requires an argument\n"); return(-9); } #ifdef OS2 if (!inserver && !k95stdout) { tocs = "ucs2"; } else { #ifdef CKOUNI tocs = rlookup(txrtab,ntxrtab,tcsl); #else /* CKOUNI */ extern struct keytab ttcstab[]; extern int ntxrtab; tocs = rlookup(ttcstab,ntermc,tocs); if (!tocs) tocs = getdcset(); #endif /* CKOUNI */ } #else /* OS2 */ tocs = getdcset(); #endif /* OS2 */ if ((outcs = cmkey(fcstab,nfilc, "character-set",tocs,xxstring)) < 0) return(outcs); break; #endif /* UNICODE */ case TYP_OUT: if ((x = cmofi("File for result lines","", &s,xxstring)) < 0) return(x); ckstrncpy(outfile,s,CKMAXPATH); break; } } else if (cmresult.fcode == _CMIFI) havename = 1; else return(-2); } if (havename) { ckstrncpy(line,cmresult.sresult,LINBUFSIZ); y = cmresult.nresult; } else { if ((x = cmifi("Filename","",&s,&y,xxstring)) < 0) { if (x == -3) { printf("?Name of an existing file required\n"); return(-9); } else return(x); } ckstrncpy(line,s,LINBUFSIZ); } if (y != 0) { printf("?A single file please\n"); return(-9); } #ifdef KUI if ( outfile[0] && gui ) { printf("?/GUI and /OUTPUT are incompatible\n"); return(-9); } #endif /* KUI */ if ((y = cmcfm()) < 0) /* Confirm the command */ return(y); #ifdef UNICODE fileorder = -1; if (cset < 0 && filepeek) { /* If no charset switches given */ int k, x = -1; k = scanfile(line,&x,nscanfile); /* Call file analyzer */ debug(F111,"type scanfile",line,k); debug(F101,"type scanfile flag","",x); switch(k) { case FT_UTF8: /* which can detect UTF-8... */ cset = 0; incs = FC_UTF8; break; case FT_UCS2: /* and UCS-2... */ cset = 0; incs = FC_UCS2; fileorder = x; /* even if there is no BOM. */ debug(F101,"type fileorder","",fileorder); break; } } #ifdef OS2 if (cset < 0) { /* If input charset still not known */ #ifdef CKOUNI tocs = rlookup(txrtab,ntxrtab,tcsl); #else /* CKOUNI */ extern struct keytab ttcstab[]; extern int ntxrtab; tocs = rlookup(ttcstab,ntermc,incs); if (!tocs) tocs = getdcset(); #endif /* CKOUNI */ incs = lookup(fcstab,tocs,nfilc,&x); } #endif /* OS2 */ if (outcs == 0 && incs != 0) { /* Supply default target charset */ int x = 0; /* if switch not given. */ tocs = getdcset(); outcs = lookup(fcstab,tocs,nfilc,&x); } #else /* !UNICODE */ if (cset < 0) incs = outcs = 0; #endif /* UNICODE */ if (outfile[0] && paging) /* This combination makes no sense */ paging = 0; /* so turn off paging */ #ifdef KUI /* No paging when dialog is used */ if ( gui && paging ) paging = 0; if ( !gui && height ) { printf("?The /HEIGHT switch is not supported without /GUI\n"); return(-9); } #endif /* KUI */ if (count) paging = -1; debug(F111,"type",line,paging); #ifdef KUI if ( gui ) { s = (char *)1; /* ok, its an ugly hack */ if (gui_text_popup_create(gui_title ? gui_title : line, height,width) < 0) { printf("?/GUI not supported on this system\n"); gui = 0; return(-9); } width = 0; } else #endif /* KUI */ s = outfile; success = dotype(line,paging,0,head,pat,width,prefix,incs,outcs,s,number); return(success); } #endif /* NOFRILLS */ #ifndef NOCSETS if (cx == XXXLA) { /* TRANSLATE file's charset */ _PROTOTYP (int doxlate, ( void ) ); return(doxlate()); } #endif /* NOCSETS */ if (cx == XXVER) { /* VERSION */ int n = 0; extern char * ck_patch, * ck_s_test; #ifdef COMMENT extern int hmtopline; #endif /* COMMENT */ if ((y = cmcfm()) < 0) return(y); #ifdef CK_64BIT printf("\n%s, for%s (64-bit)\n Numeric: %ld",versio,ckxsys,vernum); #else printf("\n%s, for%s\n Numeric: %ld",versio,ckxsys,vernum); #endif /* CK_64BIT */ printf("\n\n"); printf("Authors:\n"); printf(" Frank da Cruz, Columbia University\n"); printf(" Jeffrey Eric Altman, Secure Endpoints, Inc. %s\n", "" ); printf(" Contributions from many others.\n"); n = 7; if (*ck_s_test) { printf("\nTHIS IS A TEST VERSION, NOT FOR PRODUCTION USE.\n"); n += 2; } if (*ck_patch) { printf(" Patches: %s\n", ck_patch); n++; } printf(" Type COPYRIGHT for copyright and license.\n\n"); #ifdef OS2 shoreg(); #else #ifdef COMMENT hmtopline = n+1; hmsga(copyright); hmtopline = 0; #endif /* COMMENT */ #endif /* OS2 */ return(success = 1); } if (cx == XXCPR) { /* COPYRIGHT or LICENSE */ _PROTOTYP( int hmsgaa, (char * [], char *) ); extern char * ck_cryear; if ((y = cmcfm()) < 0) return(y); hmsgaa(copyright,ck_cryear); return(success = 1); } #ifndef MAC /* Only for multiuser systems */ #ifndef OS2 #ifndef NOFRILLS if (cx == XXWHO) { /* WHO */ char *wc; #ifdef IKSD if (inserver && !ENABLED(en_who)) { printf("?Sorry, WHO command disabled\n"); return(-9); } #endif /* IKSD */ #ifdef datageneral if ((z = cmcfm()) < 0) return(z); if (nopush) { printf("?Sorry, who not allowed\n"); return(success = 0); } xsystem(WHOCMD); #else if ((y = cmtxt("user name","",&s,xxstring)) < 0) return(y); if (nopush) { printf("?Sorry, WHO command disabled\n"); return(success = 0); } if (!(wc = getenv("CK_WHO"))) wc = WHOCMD; if (wc) if ((int) strlen(wc) > 0) { ckmakmsg(line,LINBUFSIZ,wc," ",s,NULL); xsystem(line); } #endif /* datageneral */ return(success = 1); } #endif /* NOFRILLS */ #endif /* OS2 */ #endif /* MAC */ #ifndef NOFRILLS if (cx == XXWRI || cx == XXWRL || cx == XXWRBL) { /* WRITE */ int x,y; /* On stack in case of \fexec() */ if ((x = cmkey(writab,nwri,"to file or log","",xxstring)) < 0) { if (x == -3) printf("?Write to what?\n"); return(x); } if ((y = cmtxt("text","",&s,xxstring)) < 0) return(y); s = brstrip(s); switch (x) { case LOGD: y = ZDFILE; break; case LOGP: y = ZPFILE; break; #ifndef NOLOCAL case LOGS: y = ZSFILE; break; #endif /* NOLOCAL */ case LOGT: y = ZTFILE; break; #ifndef NOSPL case LOGW: y = ZWFILE; break; #endif /* NOSPL */ case LOGX: /* SCREEN (stdout) */ case LOGE: /* ERROR (stderr) */ if (x == LOGE) { debug(F110, (cx == XXWRL) ? "WRITELN ERROR" : "WRITE ERROR", s,0); fprintf(stderr,"%s%s",s,(cx == XXWRL) ? "\n" : ""); } else { debug(F110, (cx == XXWRL) ? "WRITELN SCREEN" : "WRITE SCREEN", s,0); printf("%s%s",s,(cx == XXWRL) ? "\n" : ""); } return(success = 1); default: return(-2); } if (chkfn(y) > 0) { x = (cx == XXWRI) ? zsout(y,s) : zsoutl(y,s); debug(F111,"WRITE", (cx == XXWRI) ? "zsout" : "zsoutl", x); if (x < 0) printf("?Write error\n"); } else { x = -1; printf("?File or log not open\n"); } debug(F101,"WRITE x","",x); return(success = (x == 0) ? 1 : 0); } #endif /* NOFRILLS */ #ifndef NOXFER if (cx == XXASC || cx == XXBIN) { if ((x = cmcfm()) < 0) return(x); #ifdef NEWFTP /* Make C-Kermit work like other ftp clients, where the ASCII (TEXT) and BINARY commands are global settings. */ if (ftpisopen()) { doftpglobaltype((cx == XXASC) ? XYFT_T : XYFT_B); /* Fall thru--the command it should apply to both FTP and Kermit */ /* return(success = 1); */ } #endif /* NEWFTP */ xfermode = XMODE_M; /* Set manual Kermit transfer mode */ binary = (cx == XXASC) ? XYFT_T : XYFT_B; return(success = 1); } #endif /* NOXFER */ if (cx == XXCLS) { if ((x = cmcfm()) < 0) return(x); y = ck_cls(); return(success = (y > -1) ? 1 : 0); } #ifdef CK_MKDIR if (cx == XXMKDIR || cx == XXLMKD) { char *p; #ifdef LOCUS if (!locus && cx != XXLMKD) { #ifdef NOXFER return(-2); #else return(dormt(XZMKD)); #endif /* NOXFER */ } #endif /* LOCUS */ #ifdef IKSD if (inserver && !ENABLED(en_mkd)) { printf("?Sorry, directory creation is disabled\n"); return(-9); } #endif /* IKSD */ if ((x = cmfld("Name for new directory","",&s,xxstring)) < 0) { if (x != -3) { return(x); } else { printf("?Directory name required\n"); return(-9); } } ckstrncpy(line,s,LINBUFSIZ); s = line; if ((x = cmcfm()) < 0) return(x); s = brstrip(s); bgchk(); /* Set msgflg */ x = ckmkdir(0,s,&p,msgflg,0); #ifdef COMMENT if (msgflg && x == 0) printf("?Directory already exists\n"); #endif /* COMMENT */ return(success = (x < 0) ? 0 : 1); } if (cx == XXRMDIR || cx == XXLRMD) { /* RMDIR */ char *p; #ifdef LOCUS if (!locus && cx != XXLRMD) { #ifdef NOXFER return(-2); #else return(dormt(XZRMD)); #endif /* NOXFER */ } #endif /* LOCUS */ #ifdef IKSD if (inserver && !ENABLED(en_rmd)) { printf("?Sorry, directory removal is disabled\n"); return(-9); } #endif /* IKSD */ if ((x = cmdir("Name of directory to be removed","",&s,xxstring)) < 0) return(x); ckstrncpy(line,s,LINBUFSIZ); s = line; if ((x = cmcfm()) < 0) return(x); s = brstrip(s); x = ckmkdir(1,s,&p,msgflg,0); return(success = (x < 0) ? 0 : 1); } #endif /* CK_MKDIR */ #ifdef TNCODE if (cx == XXTELOP) return(dotelopt()); #endif /* TNCODE */ #ifndef NOPUSH if (cx == XXNPSH) { if ((z = cmcfm()) < 0) return(z); nopush = 1; #ifndef NOSERVER en_hos = 0; #endif /* NOSERVER */ #ifdef PIPESEND usepipes = 0; #endif /* PIPESEND */ return(success = 1); } #endif /* NOPUSH */ #ifdef OS2 if (cx == XXNSCR) { if ((z = cmcfm()) < 0) return(z); tt_scroll = 0; return(success = 1); } #endif /* OS2 */ #ifndef NOSPL if (cx == XXLOCAL) /* LOCAL variable declarations */ return(success = dolocal()); #endif /* NOSPL */ if (cx == XXKERMI) { /* The KERMIT command */ char * list[65]; extern char **xargv; extern int xargc; int i; if ((y = cmtxt("kermit command-line arguments, -h for help", "",&s,xxstring)) < 0) return(y); ckstrncpy(line,"kermit ",LINBUFSIZ); ckstrncat(line,s,LINBUFSIZ-8); xwords(line,64,list,0); for (i = 1; i < 64; i++) { if (!list[i]) break; } i--; xargc = i; xargv = list; xargv++; sstate = cmdlin(); if (sstate) { extern int justone; debug(F000,"KERMIT sstate","",sstate); justone = 1; /* Force return to command mode */ proto(); /* after protocol */ return(success); } else { debug(F101,"KERMIT sstate","",sstate); return(success = 1); /* Not exactly right, but... */ } } if (cx == XXDATE) { /* DATE command */ extern char cmdatebuf[], * cmdatemsg; #ifndef COMMENT char * dp; if ((y = cmtxt("date and/or time, or carriage return for current", "",&s,xxstring)) < 0) return(y); s = brstrip(s); dp = cmcvtdate(s,1); if (!dp) { printf("?%s\n",cmdatemsg ? cmdatemsg : "Date conversion error"); success = 0; } else { printf("%s\n",dp); success = 1; } #else /* This works fine but messes up my "dates" torture-test script */ if ((x = cmdate("Date and/or time, or carriage return for current", "",&s,0,xxstring)) < 0) { return(x); } else { printf("%s\n",cmdatebuf); success = 1; } #endif /* COMMENT */ return(success); } #ifndef NOPUSH #ifndef NOFRILLS if (cx == XXEDIT) return(doedit()); #endif /* NOFRILLS */ #endif /* NOPUSH */ #ifdef BROWSER /* Defined only ifndef NOPUSH */ if (cx == XXBROWS) return(dobrowse()); #endif /* BROWSER */ #ifdef CK_TAPI if (cx == XXTAPI) { /* Microsoft TAPI */ return (success = dotapi()); } #endif /* CK_TAPI */ #ifndef NOXFER if (cx == XXWHERE) { extern char * rfspec, * sfspec, * srfspec, * rrfspec; if ((x = cmcfm()) < 0) return(x); printf("\nFile most recently...\n\n"); printf(" Sent: %s\n", sfspec ? sfspec : "(none)"); if (sfspec && srfspec) { printf(" Stored as: %s\n", srfspec); printf("\n"); } printf(" Received: %s\n", rrfspec ? rrfspec : "(none)"); if (rfspec && rrfspec) printf(" Stored as: %s\n", rfspec); printf( "\nIf the full path is not shown, then the file is probably in your current\n" ); printf( "directory or your download directory (if any - SHOW FILE to find out).\n\n" ); return(success = 1); } #endif /* NOXFER */ #ifdef CK_RECALL if (cx == XXREDO) return(doredo()); #endif /* CK_RECALL */ #ifdef CKROOT if (cx == XXCHRT) /* Change Kermit's root directory */ return(dochroot()); #endif /* CKROOT */ #ifdef CK_KERBEROS if (cx == XXAUTH) { /* KERBEROS */ x = cp_auth(); /* Parse it */ #ifdef IKSD if (inserver) { printf("?Command disabled in IKSD.\r\n"); return(success = 0); } #endif /* IKSD */ if (x < 0) /* Pass parse errors back */ return(x); return(success = doauth(cx)); } #endif /* CK_KERBEROS */ #ifndef NOLOCAL if (cx == XXTERM) { return(settrmtyp()); } #endif /* NOLOCAL */ if (cx == XXSTATUS) { if ((x = cmcfm()) < 0) return(x); printf( " %s\n", success ? "SUCCESS" : "FAILURE" ); return(0); /* Don't change it */ } if (cx == XXFAIL) { if ((x = cmcfm()) < 0) return(x); return(success = 0); } if (cx == XXSUCC) { if ((x = cmcfm()) < 0) return(x); return(success = 1); } if (cx == XXNLCL) { extern int nolocal; if ((x = cmcfm()) < 0) return(x); nolocal = 1; return(success = 1); } #ifndef NOXFER if (cx == XXRASG) /* Shortcuts for REMOTE commands */ return(dormt(XZASG)); if (cx == XXRCWD) return(dormt(XZCWD)); if (cx == XXRCPY) return(dormt(XZCPY)); if (cx == XXRDEL) return(dormt(XZDEL)); if (cx == XXRDIR) return(dormt(XZDIR)); if (cx == XXRXIT) return(dormt(XZXIT)); if (cx == XXRHLP) return(dormt(XZHLP)); if (cx == XXRHOS) return(dormt(XZHOS)); if (cx == XXRKER) return(dormt(XZKER)); if (cx == XXRPWD) return(dormt(XZPWD)); if (cx == XXRQUE) return(dormt(XZQUE)); if (cx == XXRREN) return(dormt(XZREN)); if (cx == XXRMKD) return(dormt(XZMKD)); if (cx == XXRMSG) return(dormt(XZMSG)); if (cx == XXRRMD) return(dormt(XZRMD)); if (cx == XXRSET) return(dormt(XZSET)); if (cx == XXRSPA) return(dormt(XZSPA)); if (cx == XXRTYP) return(dormt(XZTYP)); if (cx == XXRWHO) return(dormt(XZWHO)); if (cx == XXRCDUP) return(dormt(XZCDU)); if (cx == XXRPRI) return(dormt(XZPRI)); #endif /* NOXFER */ if (cx == XXRESET) { /* RESET */ if ((x = cmcfm()) < 0) return(x); concb((char)escape); /* Make command echoing to normal */ doclean(0); /* Close all files */ return(success = 1); } #ifndef NOXFER #ifndef NOCSETS if (cx == XXASSOC) /* ASSOCIATE */ return(doassoc()); #endif /* NOCSETS */ #endif /* NOXFER */ #ifndef NOSPL if (cx == XXSHIFT) { /* SHIFT */ if ((y = cmnum("Number of arguments to shift","1",10,&x,xxstring)) < 0) return(y); if ((z = cmcfm()) < 0) return(z); return(success = doshift(x)); } #endif /* NOSPL */ #ifndef NOHELP if (cx == XXMAN) return(domanual()); #endif /* NOHELP */ #ifndef NOSPL if (cx == XXSORT) /* SORT an array */ return(dosort()); #endif /* NOSPL */ if (cx == XXPURGE) { #ifdef IKSD if (inserver && (!ENABLED(en_del) #ifdef CK_LOGIN || isguest #endif /* CK_LOGIN */ )) { printf("?Sorry, DELETE is disabled\n"); return(-9); } #endif /* IKSD */ #ifdef CK_APC if ((apcactive == APC_LOCAL) || ((apcactive == APC_REMOTE) && (!(apcstatus & APC_UNCH)))) return(success = 0); #endif /* CK_APC */ #ifdef CKPURGE return(dopurge()); #else #ifdef VMS if ((x = cmtxt("optional switches followed by filespec", "",&s,xxstring)) < 0) return(x); if (nopush) { printf("?Sorry, DCL access is disabled\n"); return(-9); } ckstrncpy(line,s,LINBUFSIZ); s = line; x = mlook(mactab,"purge",nmac); return(success = dodo(x,s,cmdstk[cmdlvl].ccflgs)); #else return(-2); #endif /* VMS */ #endif /* CKPURGE */ } #ifndef NOSPL if (cx == XXFAST) { if ((x = cmcfm()) < 0) return(x); x = mlook(mactab,"fast",nmac); return(success = dodo(x,NULL,cmdstk[cmdlvl].ccflgs)); } if (cx == XXCAU) { if ((x = cmcfm()) < 0) return(x); x = mlook(mactab,"cautious",nmac); return(success = dodo(x,NULL,cmdstk[cmdlvl].ccflgs)); } if (cx == XXROB) { if ((x = cmcfm()) < 0) return(x); x = mlook(mactab,"robust",nmac); return(success = dodo(x,NULL,cmdstk[cmdlvl].ccflgs)); } #endif /* NOSPL */ if (cx == XXSCRN) { /* SCREEN */ int row, col; if ((x = cmkey(scntab, nscntab,"screen action","", xxstring)) < 0) return(x); switch (x) { /* MOVE-TO (cursor position) */ case SCN_MOV: if ((y = cmnum("Row (1-based)","",10,&z,xxstring)) < 0) return(y); row = z; y = cmnum("Column (1-based)","1",10,&z,xxstring); if (y < 0) return(y); col = z; if ((y = cmcfm()) < 0) return(y); if (row < 0 || col < 0) { printf("?Row and Column must be 1 or greater\n"); return(-9); } if (cmd_rows > 0 && row > cmd_rows) row = cmd_rows; if (cmd_cols > 0 && col > cmd_cols) col = cmd_cols; y = ck_curpos(row,col); return(success = (y > -1) ? 1 : 0); case SCN_CLR: /* CLEAR */ if ((y = cmcfm()) < 0) return(y); debug(F100,"screen calling ck_cls()","",0); y = ck_cls(); return(success = (y > -1) ? 1 : 0); case SCN_CLE: /* CLEOL */ if ((y = cmcfm()) < 0) return(y); y = ck_cleol(); return(success = (y > -1) ? 1 : 0); } } #ifndef NOHTTP #ifdef TCPSOCKET if (cx == XXHTTP) return(dohttp()); #endif /* TCPSOCKET */ #endif /* NOHTTP */ #ifndef NOSPL if (cx == XXARRAY) { /* ARRAY */ #ifndef NOSHOW extern int showarray(); #endif /* NOSHOW */ if ((x = cmkey(arraytab, narraytab,"Array operation","",xxstring)) < 0) return(x); switch (x) { case ARR_DCL: return(dodcl(XXDCL)); case ARR_SRT: return(dosort()); #ifndef NOSHOW case ARR_SHO: return(showarray()); #endif /* NOSHOW */ case ARR_CPY: return(copyarray()); case ARR_SET: case ARR_CLR: return(clrarray(x)); case ARR_DST: return(unarray()); case ARR_RSZ: return(rszarray()); case ARR_EQU: return(linkarray()); default: printf("?Sorry, not implemented yet - \"%s\"\n",cmdbuf); return(-9); } } if (cx == XXTRACE) /* TRACE */ return(dotrace()); #endif /* NOSPL */ #ifdef CK_PERMS #ifdef UNIX if (cx == XXCHMOD) return(douchmod()); /* Do Unix chmod */ #endif /* UNIX */ #endif /* CK_PERMS */ if (cx == XXPROMP) return(doprompt()); if (cx == XXGREP) return(dogrep()); if (cx == XXDEBUG) { /* DEBUG */ #ifndef DEBUG int dummy = 0; return(seton(&dummy)); #else return(seton(&deblog)); #endif /* DEBUG */ } if (cx == XXMSG || cx == XXXMSG) { /* MESSAGE */ extern int debmsg; /* Script debugging messages */ if ((x = cmtxt("Message to print if SET DEBUG MESSAGE is ON or STDERR", "",&s,xxstring)) < 0) return(x); if (!s) /* Watch out for null result */ s = ""; /* Make it an empty string */ else /* Not null */ s = brstrip(s); /* Strip braces and doublequotes */ switch (debmsg) { /* Not debugging - don't print */ case 0: break; case 1: printf("%s",s); /* Print to stdout */ if (cx == XXMSG) printf("\n"); break; case 2: fprintf(stderr,"%s",s); /* Ditto but print to stderr */ if (cx == XXMSG) fprintf(stderr,"\n"); break; } return(0); /* Return without affecting SUCCESS */ } #ifdef CKLEARN if (cx == XXLEARN) { /* LEARN */ struct FDB of, sw, cm; int closing = 0, off = 0, on = 0, confirmed = 0; char c; cmfdbi(&sw, /* 2nd FDB - optional /PAGE switch */ _CMKEY, /* fcode */ "Script file name, or switch", /* hlpmsg */ "", /* default */ "", /* addtl string data */ 3, /* addtl numeric data 1: tbl size */ 4, /* addtl numeric data 2: 4 = cmswi */ xxstring, /* Processing function */ learnswi, /* Keyword table */ &of /* Pointer to next FDB */ ); cmfdbi(&of,_CMOFI,"","","",0,0,xxstring,NULL,&cm); cmfdbi(&cm,_CMCFM,"","","",0,0,NULL,NULL,NULL); line[0] = NUL; while (!confirmed) { x = cmfdb(&sw); /* Parse something */ if (x < 0) return(x); switch (cmresult.fcode) { /* What was it? */ case _CMOFI: /* Output file name */ ckstrncpy(line,cmresult.sresult,LINBUFSIZ); break; case _CMKEY: /* Switch */ c = cmgbrk(); if ((c == ':' || c == '=') && !(cmgkwflgs() & CM_ARG)) { printf("?This switch does not take an argument\n"); return(-9); } switch (cmresult.nresult) { case 2: /* /CLOSE */ closing = 1; /* Fall thru on purpose */ case 0: /* /OFF */ off = 1; on = 0; break; case 1: /* /ON */ on = 1; off = 0; break; } break; case _CMCFM: /* Confirmation */ confirmed++; break; } } if (closing) { if (learnfp) { fclose(learnfp); learnfp = NULL; } makestr(&learnfile,NULL); } if (line[0]) { if (!on && !off) on = 1; if (learnfp) { fclose(learnfp); learnfp = NULL; } makestr(&learnfile,line); if (learnfile) { char * modes = "w"; learnfp = fopen(learnfile,modes); if (!learnfp) { debug(F110,"LEARN file open error",learnfile,0); perror(learnfile); return(-9); } else { #ifdef ZFNQFP if (zfnqfp(learnfile,TMPBUFSIZ,tmpbuf)) makestr(&learnfile,tmpbuf); #endif /* ZFNQFP */ debug(F110,"LEARN file open ok",learnfile,0); if (!quiet) { printf("Recording to %s...\n\n",learnfile); printf( " WARNING: If you type your password during script recording, it will appear\n\ in the file. Be sure to edit it or take other appropriate precautions.\n\n" ); } fputs( "; Scriptfile: ",learnfp); fputs(learnfile,learnfp); fputs("\n; Directory: ",learnfp); fputs(zgtdir(),learnfp); fputs("\n; Recorded: ",learnfp); fputs(ckdate(),learnfp); fputs("\n",learnfp); } } } if (on) { learning = 1; } else if (off) { learning = 0; } debug(F101,"LEARN learning","",learning); return(success = 1); } #endif /* CKLEARN */ #ifdef NEWFTP if (cx == XXUSER || cx == XXACCT) { if (!ftpisopen()) { printf("?FTP connection is not open\n"); return(-9); } return(success = (cx == XXUSER) ? doftpusr() : doftpacct()); } if (cx == XXSITE || cx == XXPASV) { if (!ftpisopen()) { printf("?FTP connection is not open\n"); return(-9); } return(success = (cx == XXSITE) ? doftpsite() : dosetftppsv()); } #endif /* NEWFTP */ if (cx == XXORIE) { /* ORIENTATION */ extern char * myname; int i, y, n = 0; char * s, *p, vbuf[32]; char * vars[16]; char * legend[16]; if ((y = cmcfm()) < 0) return(y); printf("\nProgram name:\n %s\n\n",myname); n += 4; #ifdef NT vars[0] = "home"; legend[0] = "Your home directory"; vars[1] = "directory"; legend[1] = "K95's current directory"; vars[2] = "exedir"; legend[2] = "K95 Program directory"; vars[3] = "inidir"; legend[3] = "K95 Initialization file directory"; vars[4] = "startup"; legend[4] = "Current directory when started"; vars[5] = "common"; legend[5] = "K95 data for all users and K95SITE.INI file"; vars[6] = "personal"; legend[6] = "Your personal data directory tree"; vars[7] = "desktop"; legend[7] = "Your deskop directory tree"; vars[8] = "appdata"; legend[8] = "Your personal K95 data tree and K95CUSTOM.INI file"; vars[9] = "download"; legend[9] = "Your K95 download directory"; vars[10] = "tmpdir"; legend[10] = "Your TEMP directory"; vars[11] = NULL; legend[11] = NULL; for (i = 0; i < 16 && vars[i]; i++) { printf("%s:\n",legend[i]); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; ckmakmsg(vbuf,32,"\\v(",vars[i],")",NULL); printf(" Variable: %s\n",vbuf); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; y = TMPBUFSIZ; s = tmpbuf; zzstring(vbuf,&s,&y); line[0] = NUL; ckGetLongPathName(tmpbuf,line,LINBUFSIZ); printf(" Long name: %s\n",line); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; line[0] = NUL; GetShortPathName(tmpbuf,line,LINBUFSIZ); printf(" Short name: %s\n",line); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf("\n"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; } #else /* NT */ vars[0] = "home"; legend[0] = "Your home directory"; vars[1] = "directory"; legend[1] = "Kermit's current directory"; vars[2] = "exedir"; legend[2] = "Kermit's program directory"; vars[3] = "inidir"; legend[3] = "Initialization file directory"; vars[4] = "startup"; legend[4] = "Current directory when started"; vars[5] = "download"; legend[5] = "Kermit download directory"; vars[6] = NULL; legend[6] = NULL; for (i = 0; i < 16 && vars[i]; i++) { printf("%s:\n",legend[i]); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; ckmakmsg(vbuf,32,"\\v(",vars[i],")",NULL); printf(" Variable: %s\n",vbuf); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; y = TMPBUFSIZ; s = tmpbuf; zzstring(vbuf,&s,&y); printf(" Value: %s\n",tmpbuf); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; printf("\n"); if (++n > cmd_rows - 3) if (!askmore()) return(0); else n = 0; } #endif /* NT */ return(success = 1); } #ifdef NT if (cx == XXDIALER) { StartDialer(); return(success = 1); } #endif /* NT */ if (cx == XXCONT) { /* CONTINUE */ if ((x = cmcfm()) < 0) return(x); if (!xcmdsrc) { /* At prompt: continue script */ if (cmdlvl > 0) popclvl(); /* Pop command level */ return(success = 1); /* always succeeds */ #ifndef NOSPL } else { /* In script: whatever... */ x = mlook(mactab,"continue",nmac); /* Don't set success */ return(dodo(x,NULL,cmdstk[cmdlvl].ccflgs)); #endif /* NOSPL */ } } #ifdef UNIX #ifndef NOPUTENV /* NOTE: Syntax is PUTENV name value, not PUTENV name=value. I could check for this but it would be too much magic. */ if (cx == XXPUTE) { /* PUTENV */ char * t = tmpbuf; /* Create or alter environment var */ char * s1 = NULL, * s2 = NULL; if ((x = cmfld("Variable name","",&s,xxstring)) < 0) return(x); if (s) if (s == "") s = NULL; (VOID) makestr(&s1,s); if (s && !s1) { printf("?PUTENV - memory allocation failure\n"); return(-9); } if ((x = cmtxt("Value","",&s,xxstring)) < 0) return(x); if (s) if (s == "") s = NULL; (VOID) makestr(&s2,s); success = doputenv(s1,s2); (VOID) makestr(&s1,NULL); (VOID) makestr(&s2,NULL); return(success); } #endif /* NOPUTENV */ #endif /* UNIX */ if (cx == XXNOTAV) { /* Command in table not available */ ckstrncpy(tmpbuf,atmbuf,TMPBUFSIZ); if ((x = cmtxt("Rest of command","",&s,NULL)) < 0) return(x); printf("Sorry, \"%s\" not configured in this version of Kermit.\n", tmpbuf ); return(success = 0); } return(-2); /* None of the above */ } /* end of docmd() */ #endif /* NOICP */ ckuusr.h0000644000015300001460000033531511574210106011355 0ustar fdckermit/* C K U U S R . H -- Symbol definitions for C-Kermit ckuus*.c modules */ /* Author: Frank da Cruz , Columbia University Academic Information Systems, New York City. Copyright (C) 1985, 2010, Trustees of Columbia University in the City of New York. All rights reserved. See the C-Kermit COPYING.TXT file or the copyright text in the ckcmai.c module for disclaimer and permissions. */ #ifndef CKUUSR_H #define CKUUSR_H #include "ckucmd.h" /* Get symbols from command package */ #ifndef NOLUCACHE /* Use lookup() cache */ #ifndef NOSPL /* To speed up script programs */ #ifndef USE_LUCACHE #define USE_LUCACHE #endif /* USE_LUCACHE */ #endif /* NOSPL */ #endif /* NOLUCACHE */ #ifndef NODOUBLEQUOTING /* New to 8.0 */ #define DOUBLEQUOTING /* Allow fields to be enclosed in */ #endif /* NODOUBLEQUOTING */ /* doublequotes. */ #ifndef NOLOCUS /* SET LOCUS */ #define LOCUS #endif /* NOLOCUS */ /* Sizes of things - FNVALL and MAXARGLEN increased from 8K 20050912 */ #ifdef BIGBUFOK #define FNVALL CMDBL /* Function return value length */ #define MAXARGLEN CMDBL /* Max func arg length after eval */ #define MAXARGLIST 1024 /* Max number of args for a macro */ #define FSPECL CMDBL /* Max length for MSEND/GET string */ #define MSENDMAX 1024 /* Number of filespecs for MSEND */ #define MAC_MAX 16384 /* Maximum number of macros */ #else /* Same as above but for smaller builds... */ #define FNVALL 1022 #define MAXARGLEN 1023 #define MAXARGLIST 64 #define FSPECL 300 #define MSENDMAX 128 #define MAC_MAX 1024 #endif /* BIGBUFOK */ #define GVARS 127 /* Highest global var allowed */ #ifdef BIGBUFOK #define VNAML 4096 /* Max length for variable name */ #define ARRAYREFLEN 1024 /* Max length for array reference */ #define FORDEPTH 32 /* Maximum depth of nested FOR loops */ #define MAXTAKE 54 /* Maximum nesting of TAKE files */ #define MACLEVEL 128 /* Maximum nesting for macros */ #define INPBUFSIZ 4096 /* Size of INPUT buffer */ #define PROMPTL 1024 /* Max length for prompt */ #define LBLSIZ 8192 /* Maximum length for a GOTO label */ #else #define VNAML 256 /* Max length for variable name */ #define ARRAYREFLEN 128 /* Max length for array reference */ #define FORDEPTH 10 /* Maximum depth of nested FOR loops */ #define MAXTAKE 32 /* Maximum nesting of TAKE files */ #define MACLEVEL 64 /* Maximum nesting for macros */ #define INPBUFSIZ 256 #define PROMPTL 256 /* Max length for prompt */ #define LBLSIZ 128 /* Maximum length for a GOTO label */ #endif /* BIGBUFOK */ #define NARGS 10 /* Max number of macro arguments */ #define LINBUFSIZ (CMDBL + 10) /* Size of line[] buffer */ #define TMPBUFSIZ (CMDBL + 10) /* Size of temporary buffer */ #define CMDSTKL ( MACLEVEL + MAXTAKE + 2) /* Command stack depth */ #ifndef NOPURGE /* PURGE command */ #ifdef UNIX #define CKPURGE #endif /* UNIX */ #endif /* NOPURGE */ #ifndef NOMINPUT /* MINPUT command */ #ifndef NOSPL #define CK_MINPUT #ifndef MINPMAX #ifdef BIGBUFOK #define MINPMAX 96 /* Max number of MINPUT strings */ #else #define MINPMAX 16 #endif /* BIGBUFOK */ #endif /* MINPMAX */ #define MINPBUFL 256 /* Size of MINPUT temp buffer */ #endif /* NOSPL */ #endif /* NOMINPUT */ #define ARRAYBASE 95 /* Lowest array-name character */ #ifndef NOKERBANG #ifndef KERBANG #define KERBANG #endif /* KERBANG */ #endif /* NOKERBANG */ /* Bit values (1, 2, 4, 8, ...) for the ccflgs field */ #define CF_APC 1 /* Executing an APC command */ #define CF_KMAC 2 /* Executing a \Kmacro */ #define CF_CMDL 4 /* Macro from -C "blah" command line */ #define CF_REXX 8 /* Macro from REXX interpreter */ #define CF_IMAC 16 /* Internal macro like FOR, WHILE... */ struct cmdptr { /* Command stack structure */ int src; /* Command Source */ int lvl; /* Current TAKE or DO level */ int ccflgs; /* Flags */ }; struct mtab { /* Macro table, like keyword table */ char *kwd; /* But with pointers for vals */ char *mval; /* instead of ints. */ short flgs; }; struct localvar { /* Local variable structure. */ char * lv_name; char * lv_value; struct localvar * lv_next; }; struct stringlist { /* General purpose string list */ char * sl_name; struct stringlist * sl_next; }; struct stringint { /* String and (wide) integer */ char * sval; /* used mainly with command switches */ int ival; CK_OFF_T wval; }; #ifndef NOICP /* C-Kermit Initialization file... System-dependent defaults are built into the program, see below. These can be overridden in either of two ways: 1. CK_DSYSINI is defined at compile time, in which case a default system-wide initialization file name is chosen from below, or: 2: CK_SYSINI is defined to be a string, which lets the program builder choose the initialization filespec at compile time. These can be given on the CC command line, so the source code need not be changed. */ #ifndef CK_SYSINI /* If no initialization file given, */ #ifdef CK_DSYSINI /* but CK_DSYSINI is defined... */ /* Supply system-dependent "default default" initialization file */ #ifdef UNIX /* For UNIX, system-wide */ /* This allows one copy of the standard init file on the whole system, */ /* rather than a separate copy in each user's home directory. */ #ifdef HPUX10 #define CK_SYSINI "/usr/share/lib/kermit/ckermit.ini" #else #ifdef CU_ACIS #define CK_SYSINI "/usr/share/lib/kermit/ckermit.ini" #else #ifdef __linux__ #define CK_SYSINI "/usr/share/kermit/ckermit.ini" #else #define CK_SYSINI "/usr/local/bin/ckermit.ini" #endif /* linux */ #endif /* CU_ACIS */ #endif /* HPUX10 */ /* Fill in #else..#ifdef's here for VMS, OS/2, etc. */ /* Fill in matching #endif's here. */ #endif /* UNIX */ #endif /* CK_DSYSINI */ #endif /* CK_SYSINI */ #ifdef CK_SYSINI /* Init-file precedence */ #ifndef CK_INI_A /* A means system-wide file first */ #ifndef CK_INI_B /* B means user's first */ #define CK_INI_A /* A is default */ #endif /* CK_INI_B */ #endif /* CK_INI_A */ #else #ifdef CK_INI_A /* Otherwise */ #undef CK_INI_A /* make sure these aren't defined */ #endif /* CK_INI_A */ #ifdef CK_INI_B #undef CK_INI_B #endif /* CK_INI_B */ #endif /* CK_SYSINI */ #ifdef CK_SYSINI /* One more check... */ #ifdef CK_INI_A /* Make sure they're not both */ #ifdef CK_INI_B /* defined. */ #undef CK_INI_B #endif /* CK_INI_B */ #endif /* CK_INI_A */ #endif /* CK_SYSINI */ /* If neither CK_DSYSINI nor CK_SYSINI are defined, these are the built-in defaults for each platform. USE_CUSTOM means to execute the customization file automatically if the initialization file is not found. */ #ifndef NOCUSTOM #ifndef USE_CUSTOM #define USE_CUSTOM #endif /* USE_CUSTOM */ #endif /* NOCUSTOM */ #ifndef KERMRCL /* Path length for init file */ #define KERMRCL CKMAXPATH #endif /* KERMRCL */ #ifdef vms #define KERMRC "CKERMIT.INI" /* Init file name */ #define MYCUSTOM "CKERMOD.INI" /* Customization file name */ #else #ifdef OS2 #ifdef NT #define KERMRC "k95.ini" #define MYCUSTOM "k95custom.ini" #else #define KERMRC "k2.ini" #define MYCUSTOM "k2custom.ini" #endif /* NT */ #else #ifdef UNIXOROSK #define KERMRC ".kermrc" #define MYCUSTOM ".mykermrc" #else #ifdef STRATUS #define KERMRC "ckermit.ini" #define MYCUSTOM "ckermod.ini" #else #define KERMRC "CKERMIT.INI" #define MYCUSTOM "ckermod.ini" #endif /* STRATUS */ #endif /* UNIXOROSK */ #endif /* OS2 */ #endif /* vms */ #ifndef KERMRCL #define KERMRCL 256 #endif /* KERMRCL */ #endif /* NOICP */ /* User interface features */ #ifdef CK_CURSES /* Thermometer */ #ifndef NO_PCT_BAR #ifndef CK_PCT_BAR #define CK_PCT_BAR #endif /* NO_PCT_BAR */ #endif /* CK_PCT_BAR */ #endif /* CK_CURSES */ #ifdef KUI /* KUI requires the Thermometer code */ #ifndef NO_PCT_BAR #ifndef CK_PCT_BAR #define CK_PCT_BAR #endif /* NO_PCT_BAR */ #endif /* CK_PCT_BAR */ #endif /* KUI */ /* Includes */ #ifdef MINIX /* why? */ #include #endif /* MINIX */ /* Symbols for command source */ #define CMD_KB 0 /* KeyBoard or standard input */ #define CMD_TF 1 /* TAKE command File */ #define CMD_MD 2 /* Macro Definition */ /* SET TRANSFER CANCELLATION command should be available in all versions. But for now... */ #ifdef UNIX /* UNIX has it */ #ifndef XFRCAN #define XFRCAN #endif /* XFRCAN */ #endif /* UNIX */ #ifdef VMS /* VMS has it */ #ifndef XFRCAN #define XFRCAN #endif /* XFRCAN */ #endif /* VMS */ #ifdef datageneral /* DG AOS/VS has it */ #ifndef XFRCAN #define XFRCAN #endif /* XFRCAN */ #endif /* datageneral */ #ifdef STRATUS /* Stratus VOS has it */ #ifndef XFRCAN #define XFRCAN #endif /* XFRCAN */ #endif /* STRATUS */ #ifdef OSK /* OS-9 */ #ifndef XFRCAN #define XFRCAN #endif /* XFRCAN */ #endif /* OSK */ #ifndef NOCMDL /* Extended Command-Line Option Codes (keep alphabetical by keyword) */ #define XA_ANON 0 /* --anonymous */ #define XA_BAFI 1 /* --bannerfile */ #define XA_CDFI 2 /* --cdfile */ #define XA_CDMS 3 /* --cdmessage */ #define XA_HELP 4 /* --help */ #define XA_HEFI 5 /* --helpfile */ #define XA_IKFI 6 /* --xferfile */ #define XA_IKLG 7 /* --xferlog */ #define XA_ANFI 8 /* --initfile */ #define XA_PERM 9 /* --permissions */ #define XA_ROOT 10 /* --root */ #define XA_SYSL 11 /* --syslog */ #define XA_USFI 12 /* --userfile */ #define XA_WTFI 13 /* --wtmpfile */ #define XA_WTMP 14 /* --wtmplog */ #define XA_TIMO 15 /* --timeout */ #define XA_NOIN 16 /* --nointerrupts */ #define XA_DBAS 17 /* --database */ #define XA_DBFI 18 /* --dbfile */ #define XA_PRIV 19 /* --privid */ #define XA_VERS 20 /* --version */ #define XA_NPRM 21 /* --noperms */ #define XA_XPOS 22 /* Window position X coordinate */ #define XA_YPOS 23 /* Window position Y coordinate */ #define XA_FNAM 24 /* Font Facename */ #define XA_FSIZ 25 /* Font Size */ #define XA_TERM 26 /* Terminal type */ #define XA_CSET 27 /* Remote Character Set */ #define XA_ROWS 28 /* Screen rows (height) */ #define XA_COLS 29 /* Screen columns (width) */ #define XA_TEL 30 /* Make a Telnet connection */ #define XA_FTP 31 /* Make an FTP connection */ #define XA_SSH 32 /* Make an SSH connection */ #define XA_USER 33 /* Username for login */ #define XA_PASS 34 /* Password for login */ #define XA_TITL 35 /* Window Title */ #define XA_NOMN 36 /* No GUI Menu Bar */ #define XA_NOTB 37 /* No GUI Tool Bar */ #define XA_NOSB 38 /* No GUI Status Bar */ #define XA_NOPUSH 39 /* Disable external commands */ #define XA_NOSCROLL 40 /* Disable scrollback operations */ #define XA_NOESCAPE 41 /* Disable escape from connect mode */ #define XA_LOCK 42 /* All lockdown options */ #define XA_NOBAR 43 /* No GUI Bars */ #define XA_WMAX 44 #define XA_WMIN 45 #define XA_SCALE 46 /* GUI Scale Font */ #define XA_CHGD 47 /* GUI Change Dimensions */ #define XA_NOCLOSE 48 /* GUI Disable Close Window */ #define XA_UNBUF 49 /* UNIX unbuffered console */ #define XA_MAX 49 /* Highest extended option number */ #endif /* NOCMDL */ #ifndef NOICP /* Top Level Commands */ /* Values associated with top-level commands must be 0 or greater. */ #define XXBYE 0 /* BYE */ #define XXCLE 1 /* CLEAR */ #define XXCLO 2 /* CLOSE */ #define XXCON 3 /* CONNECT */ #define XXCPY 4 /* COPY */ #define XXCWD 5 /* CWD (Change Working Directory) */ #define XXDEF 6 /* DEFINE (a macro or variable) */ #define XXDEL 7 /* (Local) DELETE */ #define XXDIR 8 /* (Local) DIRECTORY */ /* DIRECTORY Command options... */ #define DIR_BRF 1 /* BRIEF */ #define DIR_VRB 2 /* VERBOSE */ #define DIR_PAG 3 /* PAGE */ #define DIR_NOP 4 /* NOPAGE */ #define DIR_ISO 5 /* ISODATE */ #define DIR_DAT 6 /* ENGLISHDATE */ #define DIR_HDG 7 /* HEADINGS */ #define DIR_NOH 8 /* NOHEADINGS */ #define DIR_SRT 9 /* SORT */ #define DIR_NOS 10 /* NOSORT */ #define DIR_ASC 11 /* ASCENDING */ #define DIR_DSC 12 /* DESCENDING */ #define DIR_REC 13 /* RECURSIVE */ #define DIR_NOR 14 /* NORECURIVE */ #define DIR_DOT 15 /* DOTFILES */ #define DIR_NOD 16 /* NODOTFILES */ #define DIR_DIR 17 /* DIRECTORIES */ #define DIR_FIL 18 /* FILES */ #define DIR_ALL 19 /* ALL */ #define DIR_NAM 20 /* NAMES: */ #define DIR_TYP 21 /* FILETYPES */ #define DIR_NOT 22 /* NOFILETYPES */ #define DIR_BUP 23 /* BACKUP */ #define DIR_NOB 24 /* NOBACKUP */ #define DIR_MSG 25 /* MESSAGE */ #define DIR_NOM 26 /* NOMESSAGE */ #define DIR_ARR 27 /* ARRAY: */ #define DIR_NAR 28 /* NOARRAY */ #define DIR_EXC 29 /* EXCEPT: */ #define DIR_LAR 30 /* LARGER-THAN: */ #define DIR_SMA 31 /* SMALLER-THAN: */ #define DIR_AFT 32 /* AFTER: */ #define DIR_NAF 33 /* NOT-AFTER: */ #define DIR_BEF 34 /* BEFORE: */ #define DIR_NBF 35 /* NOT-BEFORE: */ #define DIR_SUM 36 /* SUMMARY */ #define DIR_BIN 37 /* TYPE (only show binary or text) */ #define DIR_LNK 38 /* follow symlinks */ #define DIR_NLK 39 /* don't follow symlinks */ #define DIR_OUT 40 /* Output file for listing */ #define DIR_TOP 41 /* Top n lines */ #define DIR_COU 42 /* COUNT:var */ #define DIR_NOL 43 /* NOLINKS (don't show symlinks at at all) */ #define DIRS_NM 0 /* Sort directory by NAME */ #define DIRS_DT 1 /* Sort directory by DATE */ #define DIRS_SZ 2 /* Sort directory by SIZE */ #define XXDIS 9 /* DISABLE */ #define XXECH 10 /* ECHO */ #define XXEXI 11 /* EXIT */ #define XXFIN 12 /* FINISH */ #define XXGET 13 /* GET */ #define XXHLP 14 /* HELP */ #define XXINP 15 /* INPUT */ #define XXLOC 16 /* LOCAL */ #define XXLOG 17 /* LOG */ #define XXMAI 18 /* MAIL */ #define XXMOU 19 /* (Local) MOUNT */ #define XXMSG 20 /* (Local) MESSAGE */ #define XXOUT 21 /* OUTPUT */ #define XXPAU 22 /* PAUSE */ #define XXPRI 23 /* (Local) PRINT */ #define XXQUI 24 /* QUIT */ #define XXREC 25 /* RECEIVE */ #define XXREM 26 /* REMOTE */ #define XXREN 27 /* (Local) RENAME */ #define XXSEN 28 /* SEND */ /* SEND switches */ #define SND_BIN 0 /* Binary mode */ #define SND_DEL 1 /* Delete after */ #define SND_EXC 2 /* Except */ #define SND_LAR 3 /* Larger than */ #define SND_MAI 4 /* Mail */ #define SND_BEF 5 /* Before */ #define SND_AFT 6 /* After */ #define SND_PRI 7 /* Print */ #define SND_SHH 8 /* Quiet */ #define SND_REC 9 /* Recursive */ #define SND_SMA 10 /* Smaller than */ #define SND_STA 11 /* Starting-from */ #define SND_TXT 12 /* Text mode */ #define SND_CMD 13 /* From command (pipe) */ #define SND_RES 14 /* Resend/Recover */ #define SND_PRO 15 /* Protocol */ #define SND_ASN 16 /* As-name */ #define SND_IMG 17 /* Image */ #define SND_LBL 18 /* Labeled */ #define SND_NAF 19 /* Not-After */ #define SND_NBE 20 /* Not-Before */ #define SND_FLT 21 /* Filter */ #define SND_PTH 22 /* Pathnames */ #define SND_NAM 23 /* Filenames */ #define SND_MOV 24 /* MOVE to another directory */ #define SND_REN 25 /* RENAME after sending */ #define SND_CAL 26 /* Calibrate */ #define SND_FIL 27 /* File containing list of files to send */ #define SND_NOB 28 /* Skip backup files */ #define SND_DOT 29 /* Include dot-files */ #define SND_NOD 30 /* Exclude dot-files */ #define SND_ARR 31 /* Send from array */ #define SND_TYP 32 /* TYPE (only send text (or binary)) */ #define SND_XPA 33 /* TRANSPARENT */ #define SND_PIP 34 /* PIPES */ #define SND_ERR 35 /* ERROR */ #define SND_CSL 36 /* Local character set */ #define SND_CSR 37 /* Remote character set */ #define SND_UPD 38 /* Update */ #define SND_COL 39 /* Collision */ #define SND_NML 40 /* Namelist */ #define SND_SRN 41 /* Server-Rename */ #define SND_LNK 42 /* Follow links */ #define SND_NLK 43 /* Don't follow links */ #define SND_SIM 44 /* Simulate */ #define SND_DIF 45 /* If dates Differ */ #define SND_PAT 46 /* Pattern to use locally when GET'ing */ #define SND_NLS 47 /* (FTP only) MGET forces NLST */ #define SND_MLS 48 /* (FTP only) MGET forces MLSD */ #define SND_MAX 48 /* Highest SEND switch */ #define XXSER 29 /* SERVER */ #define XXSET 30 /* SET */ #define XXSHE 31 /* Command for SHELL */ #define XXSHO 32 /* SHOW */ #define XXSPA 33 /* (Local) SPACE */ #define XXSTA 34 /* STATISTICS */ #define XXSUB 35 /* (Local) SUBMIT */ #define XXTAK 36 /* TAKE */ #define XXTRA 37 /* TRANSMIT */ #define XXTYP 38 /* (Local) TYPE */ #define XXWHO 39 /* (Local) WHO */ #define XXDIAL 40 /* (Local) DIAL */ #define XXLOGI 41 /* (Local) SCRIPT */ #define XXCOM 42 /* Comment */ #define XXHAN 43 /* HANGUP */ #define XXXLA 44 /* TRANSLATE */ #define XXIF 45 /* IF */ #define XXLBL 46 /* label */ #define XXGOTO 47 /* GOTO */ #define XXEND 48 /* END */ #define XXSTO 49 /* STOP */ #define XXDO 50 /* DO */ #define XXPWD 51 /* PWD */ #define XXTES 52 /* TEST */ #define XXASK 53 /* ASK */ #define XXASKQ 54 /* ASKQ */ #define XXASS 55 /* ASSIGN */ #define XXREI 56 /* REINPUT */ #define XXINC 57 /* INCREMENT */ #define XXDEC 59 /* DECREMENT */ #define XXELS 60 /* ELSE */ #define XXEXE 61 /* EXECUTE */ #define XXWAI 62 /* WAIT */ #define XXVER 63 /* VERSION */ #define XXENA 64 /* ENABLE */ #define XXWRI 65 /* WRITE */ #define XXCLS 66 /* CLS (clear screen) */ #define XXRET 67 /* RETURN */ #define XXOPE 68 /* OPEN */ #define XXREA 69 /* READ */ #define XXON 70 /* ON */ #define XXDCL 71 /* DECLARE */ #define XXBEG 72 /* BEGIN (not used) */ #define XXFOR 72 /* FOR */ #define XXWHI 73 /* WHILE */ #define XXIFX 74 /* Extended IF */ #define XXCMS 75 /* SEND from command output (not yet) */ #define XXCMR 76 /* RECEIVE to a command's input (not yet) */ #define XXCMG 77 /* GET to a command's input (not yet) */ #define XXSUS 78 /* SUSPEND */ #define XXERR 79 /* ERROR */ #define XXMSE 80 /* MSEND */ #define XXBUG 81 /* BUG */ #define XXPAD 82 /* PAD (as in X.25 PAD) ANYX25 */ #define XXRED 83 /* REDIAL */ #define XXGTA 84 /* _getargs (invisible internal) */ #define XXPTA 85 /* _putargs (invisible internal) */ #define XXGOK 86 /* GETOK - Ask for YES/NO */ #define XXTEL 87 /* TELNET */ #define XXASX 88 /* _ASSIGN (evaluates var name) */ #define XXDFX 89 /* _DEFINE (evaluates var name) */ #define XXPNG 90 /* PING (for TCP/IP) */ #define XXINT 91 /* INTRODUCTION */ #define XXCHK 92 /* CHECK (a feature) */ #define XXMSL 93 /* MSLEEP, MPAUSE (millisecond sleep) */ #define XXNEW 94 /* NEWS */ #define XXAPC 95 /* APC */ #define XXFUN 96 /* REDIRECT */ #define XXWRL 97 /* WRITE-LINE */ #define XXREXX 98 /* Rexx */ #define XXMINP 100 /* MINPUT */ #define XXRSEN 101 /* RESEND */ #define XXPSEN 102 /* PSEND */ #define XXGETC 103 /* GETC */ #define XXEVAL 104 /* EVALUATE */ #define XXFWD 105 /* FORWARD */ #define XXUPD 106 /* UPDATES */ #define XXBEEP 107 /* BEEP */ #define XXMOVE 108 /* MOVE */ #define XXMMOVE 109 /* MMOVE */ #define XXREGET 110 /* REGET */ #define XXLOOK 111 /* LOOKUP */ #define XXVIEW 112 /* VIEW (terminal buffer) */ #define XXANSW 113 /* ANSWER (the phone) */ #define XXPDIA 114 /* PDIAL (partial dial) */ #define XXASC 115 /* ASCII / TEXT */ #define XXBIN 116 /* BINARY */ #define XXFTP 117 /* FTP */ #define XXMKDIR 118 /* MKDIR */ #define XXRMDIR 119 /* RMDIR */ #define XXTELOP 120 /* TELOPT */ #define XXRLOG 121 /* RLOGIN */ #define XXUNDEF 122 /* UNDEFINE */ #define XXNPSH 123 /* NOPUSH */ #define XXADD 124 /* ADD */ #define ADD_SND 0 /* ADD SEND-LIST */ #define ADD_BIN 1 /* ADD BINARY-PATTERNS */ #define ADD_TXT 2 /* ADD TEXT-PATTERNS */ #define XXLOCAL 125 /* LOCAL */ #define XXKERMI 126 /* KERMIT */ #define XXDATE 127 /* DATE */ #define XXSWIT 128 /* SWITCH */ #define XXXFWD 129 /* _FORWARD */ #define XXSAVE 130 /* SAVE */ #define XXXECH 131 /* XECHO */ #define XXRDBL 132 /* READBLOCK */ #define XXWRBL 133 /* WRITEBLOCK */ #define XXRETR 134 /* RETRIEVE */ #define XXEIGHT 135 /* EIGHTBIT */ #define XXEDIT 136 /* EDIT */ #define XXCSEN 137 /* CSEND */ #define XXCREC 138 /* CRECEIVE */ #define XXCQ 139 /* CQ */ #define XXTAPI 140 /* TAPI actions such as dialogs */ #define XXRES 141 /* RESET */ #define XXCGET 142 /* CGET */ #define XXFUNC 143 /* Function (help-only) */ #define XXKVRB 144 /* Kverb (help-only) */ #define XXBROWS 145 /* BROWSE */ #define XXMGET 146 /* MGET */ #define XXBACK 147 /* BACK */ #define XXWHERE 148 /* WHERE */ #define XXREDO 149 /* REDO */ #define XXEXCH 150 /* EXCHANGE */ #define XXREMV 151 /* REMOVE */ #define XXCHRT 152 /* CHROOT */ #define XXOPTS 153 /* Options (help-only) */ #define XXAUTH 154 /* AUTHORIZE */ #define XXPIPE 155 /* PIPE */ #define XXSSH 156 /* SSH */ #define XXTERM 157 /* TERMINAL */ #define XXSTATUS 158 /* STATUS */ #define XXCPR 159 /* COPYRIGHT */ #define XXASSER 160 /* ASSERT */ #define XXSUCC 161 /* SUCCEED */ #define XXFAIL 162 /* FAIL */ #define XXLOGIN 163 /* LOGIN */ #define XXLOGOUT 164 /* LOGOUT */ #define XXNLCL 165 /* NOLOCAL */ #define XXWILD 166 /* WILDCARDS (help-only) */ /* One-word synonyms for REMOTE commands */ #define XXRCPY 167 /* REMOTE COPY */ #define XXRCWD 168 /* Change Working Directory */ #define XXRDEL 169 /* Delete */ #define XXRDIR 170 /* Directory */ #define XXRHLP 171 /* Help */ #define XXRHOS 172 /* Host */ #define XXRKER 173 /* Kermit */ #define XXRMSG 174 /* Message */ #define XXRPRI 175 /* Print */ #define XXRREN 176 /* Rename */ #define XXRSET 177 /* Set */ #define XXRSPA 178 /* Space */ #define XXRSUB 179 /* Submit */ #define XXRTYP 180 /* Type */ #define XXRWHO 181 /* Who */ #define XXRPWD 182 /* Print Working Directory */ #define XXRQUE 183 /* Query */ #define XXRASG 184 /* Assign */ #define XXRMKD 185 /* mkdir */ #define XXRRMD 186 /* rmdir */ #define XXRXIT 187 /* Exit */ /* Top-Level commands, cont'd... */ #define XXGETK 188 /* GETKEYCODE */ #define XXMORE 189 /* MORE */ #define XXXOPTS 190 /* Extended-Options (help-only) */ #define XXIKSD 191 /* IKSD */ #define XXRESET 192 /* RESET */ #define XXASSOC 193 /* ASSOCIATE */ #define ASSOC_FC 0 /* ASSOCIATE FILE-CHARACTER-SET */ #define ASSOC_TC 1 /* ASSOCIATE TRANSFER-CHARACTER-SET */ #define XXSHIFT 194 /* SHIFT */ #define XXMAN 195 /* MANUAL */ #define XXLS 196 /* LS */ #define XXSORT 197 /* SORT */ #define XXPURGE 198 /* PURGE */ #define XXFAST 199 /* FAST */ #define XXCAU 200 /* CAUTIOUS */ #define XXROB 201 /* ROBUST */ #define XXMACRO 202 /* Immediate macro */ #define XXSCRN 203 /* SCREEN */ #define XXLNOUT 204 /* LINEOUT */ #define XX_INCR 205 /* _INCREMENT */ #define XX_DECR 206 /* _DECREMENT */ #define XX_EVAL 207 /* _EVALUATE */ #define XXARRAY 208 /* ARRAY */ #define XXPARSE 209 /* PARSE */ #define XXHTTP 210 /* HTTP */ #ifdef CKCHANNELIO #define XXFILE 211 /* FILE */ #define XXF_CL 212 /* FCLOSE */ #define XXF_FL 213 /* FFLUSH */ #define XXF_LI 214 /* FLIST */ #define XXF_OP 215 /* FOPEN */ #define XXF_RE 216 /* FREAD */ #define XXF_SE 217 /* FSEEK */ #define XXF_ST 218 /* FSTATUS */ #define XXF_WR 219 /* FWRITE */ #define XXF_RW 220 /* FREWIND */ #define XXF_CO 221 /* FCOUNT */ #endif /* CKCHANNELIO */ #define XXEXEC 222 /* exec() */ #define XXTRACE 223 /* TRACE */ #define XXNOTAV 224 /* The "not available" command */ #define XXPTY 225 /* PTY (like PIPE) */ #define XXCHMOD 226 /* CHMOD */ #define XXPROMP 227 /* PROMPT */ #define XXFEACH 228 /* FOREACH */ #define XXGREP 229 /* GREP */ #define XXSEXP 230 /* S-Expression */ #define XXUNDCL 231 /* UNDECLARE */ #define XXVOID 232 /* VOID */ #define XXPUT 233 /* PUT */ #define XXUNDFX 234 /* _UNDEFINE */ #define XXHEAD 235 /* HEAD */ #define XXTAIL 236 /* TAIL */ #define XXDEBUG 237 /* DEBUG */ #define XXLEARN 238 /* LEARN */ #define XXPAT 239 /* PATTERNS (help only) */ #define XXCDUP 240 /* CDUP (Change working directory upwards) */ #define XXRCDUP 241 /* REMOTE CDUP */ #define XXCAT 242 /* CAT (= TYPE /NOPAGE) */ #define XXFIREW 243 /* FIREWALL (help only) */ #define XXLCWD 244 /* Local C(W)D */ #define XXLCDU 245 /* Local CDUP */ #define XXLPWD 246 /* Local PWD */ #define XXLDEL 247 /* Local DELETE */ #define XXLDIR 248 /* Local DIRECTORY */ #define XXLREN 249 /* Local RENAME */ #define XXLMKD 250 /* Local MKDIR */ #define XXLRMD 251 /* Local RMDIR */ #define XXUSER 252 /* (FTP) USER */ #define XXACCT 253 /* (FTP) ACCOUNT */ #define XXLINK 254 /* LINK source destination */ #define XXORIE 255 /* ORIENT(ATION) */ #define XXDIALER 256 /* DIALER */ #define XXKCD 257 /* KCD */ #define XXSITE 258 /* (FTP) SITE */ #define XXPASV 259 /* (FTP) PASSIVE */ #define XXCONT 260 /* CONTINUE */ #define XXNSCR 261 /* NOSCROLL */ #define XXSFTP 262 /* SFTP */ #define XXSKRM 263 /* SKERMIT */ #define XXWDIR 264 /* WDIRECTORY */ #define XXHDIR 265 /* HDIRECTORY */ #define XXTOUC 266 /* TOUCH */ #define XXLOCU 267 /* LOCUS (for HELP) */ #define XXPUTE 268 /* PUTENV */ #define XXXMSG 269 /* XMESSAGE */ /* End of Top-Level Commands */ #define SCN_CLR 0 /* SCREEN CLEAR */ #define SCN_CLE 1 /* SCREEN CLEOL */ #define SCN_MOV 2 /* SCREEN MOVE */ /* ARRAY operations */ #define ARR_DCL 0 /* Declare */ #define ARR_CPY 1 /* Copy */ #define ARR_RSZ 2 /* Resize */ #define ARR_SRT 3 /* Sort */ #define ARR_CLR 4 /* Clear */ #define ARR_SEA 5 /* Search */ #define ARR_DST 6 /* Destroy */ #define ARR_SHO 7 /* Show */ #define ARR_SET 8 /* Set */ #define ARR_EQU 9 /* Equate */ /* SORT options */ #define SRT_CAS 0 /* /CASE */ #define SRT_KEY 1 /* /KEY:n */ #define SRT_REV 2 /* /REVERSE */ #define SRT_RNG 3 /* /RANGE:n:m */ #define SRT_NUM 4 /* /NUMERIC */ /* PURGE command options */ #define PU_KEEP 0 /* /KEEP: */ #define PU_LIST 1 /* /LIST */ #define PU_PAGE 2 /* /PAGE */ #define PU_NOPA 3 /* /NOPAGE */ #define PU_NODE 4 /* /SIMULATE */ #define PU_DELE 5 /* /DELETE */ #define PU_NOLI 6 /* /NOLIST */ #define PU_QUIE 7 /* /QUIET (= NOLIST) */ #define PU_VERB 8 /* /VERBOSE (= LIST) */ #define PU_ASK 9 /* /ASK */ #define PU_NASK 10 /* /NOASK */ #define PU_LAR 11 /* /LARGER-THAN: */ #define PU_SMA 12 /* /SMALLER-THAN: */ #define PU_AFT 13 /* /AFTER: */ #define PU_NAF 14 /* /NOT-AFTER: */ #define PU_BEF 15 /* /BEFORE: */ #define PU_NBF 16 /* /NOT-BEFORE: */ #define PU_EXC 17 /* /EXCEPT: */ #define PU_RECU 18 /* /RECURSIVE */ #define PU_DOT 19 /* /DOTFILES */ #define PU_NODOT 20 /* /NODOTFILES */ #define PU_HDG 21 /* /HEADING */ #define PU_NOH 22 /* /NOHEADING */ /* DELETE command options */ #define DEL_NOL 0 /* /NOLIST */ #define DEL_LIS 1 /* /LIST */ #define DEL_HDG 2 /* /HEADINGS */ #define DEL_NOH 2 /* /NOHEADINGS */ #define DEL_BEF 3 /* /BEFORE: */ #define DEL_NBF 4 /* /NOT-BEFORE: */ #define DEL_AFT 5 /* /AFTER: */ #define DEL_NAF 6 /* /NOT-AFTER: */ #define DEL_DOT 7 /* /DOTFILES */ #define DEL_NOD 8 /* /NODOTFILES */ #define DEL_EXC 9 /* /EXCEPT:*/ #define DEL_PAG 10 /* /PAGE */ #define DEL_NOP 11 /* /NOPAGE */ #define DEL_REC 12 /* /RECURSIVE */ #define DEL_NOR 13 /* /NORECURSIVE */ #define DEL_VRB 14 /* /VERBOSE */ #define DEL_QUI 15 /* /QUIET */ #define DEL_SMA 16 /* /SMALLER-THAN: */ #define DEL_LAR 17 /* /LARGER-THAN: */ #define DEL_SIM 18 /* /SIMULATE */ #define DEL_ASK 19 /* /ASK */ #define DEL_NAS 20 /* /NOASK */ #define DEL_SUM 21 /* /SUMMARY */ #define DEL_DIR 22 /* /DIRECTORY */ #define DEL_ALL 23 /* /ALL */ #define DEL_TYP 24 /* /TYPE: */ #define DEL_LNK 25 /* /FOLLOWLINKS */ #define DEL_NLK 26 /* /NOFOLLOWLINKS */ /* RENAME switches that can be used in the same table as the DEL switches */ #define REN_LOW 100 /* Convert to lowercase */ #define REN_UPP 101 /* Converto to uppercase */ #define REN_RPL 102 /* String replacement */ #define REN_OVW 103 /* Overwrite file with same name */ #define REN_XLA 104 /* Translate character sets */ #define REN_SPA 105 /* Fix spaces */ /* FILE operations */ #define FIL_OPN 0 /* OPEN */ #define FIL_CLS 1 /* CLOSE */ #define FIL_REA 2 /* READ */ #define FIL_GET 3 /* GET */ #define FIL_WRI 4 /* WRITE */ #define FIL_REW 5 /* REWIND */ #define FIL_LIS 6 /* LIST */ #define FIL_FLU 7 /* FLUSH */ #define FIL_SEE 8 /* SEEK */ #define FIL_STA 9 /* STATUS */ #define FIL_COU 10 /* COUNT */ /* OPEN / CLOSE items */ #define OPN_FI_R 1 /* FILE READ */ #define OPN_FI_W 2 /* FILE WRITE */ #define OPN_FI_A 3 /* FILE APPEND */ #define OPN_PI_R 4 /* PIPE READ */ #define OPN_PI_W 5 /* PIPE WRITE */ #define OPN_PT_R 6 /* PTY READ */ #define OPN_PT_W 7 /* PTY WRITE */ #define OPN_SER 8 /* PORT or LINE */ #define OPN_NET 9 /* HOST */ /* KERBEROS command switches */ #define KRB_S_VE 0 /* /VERSION */ #define KRB_S_CA 1 /* /CACHE: */ #define KRB_S_MAX 1 /* Highest KERBEROS switch number */ #ifdef CK_KERBEROS /* KERBEROS actions */ #define KRB_A_IN 0 /* INITIALIZE */ #define KRB_A_DE 1 /* DESTROY */ #define KRB_A_LC 2 /* LIST-CREDENTIALS */ /* KERBEROS INIT switches */ #define KRB_I_FW 0 /* /FORWARDABLE */ #define KRB_I_LF 1 /* /LIFETIME: */ #define KRB_I_PD 2 /* /POSTDATE: */ #define KRB_I_PR 3 /* /PROXIABLE */ #define KRB_I_RB 4 /* /RENEWABLE: */ #define KRB_I_RN 5 /* /RENEW */ #define KRB_I_SR 6 /* /SERVICE: */ #define KRB_I_VA 7 /* /VALIDATE */ #define KRB_I_RL 8 /* /REALM: */ #define KRB_I_IN 9 /* /INSTANCE: */ #define KRB_I_PW 10 /* /PASSWORD: */ #define KRB_I_PA 11 /* /PREAUTH */ #define KRB_I_VB 12 /* /VERBOSE */ #define KRB_I_BR 13 /* /BRIEF */ #define KRB_I_NFW 14 /* /NOT-FORWARDABLE */ #define KRB_I_NPR 15 /* /NOT-PROXIABLE */ #define KRB_I_NPA 16 /* /NOT-PREAUTH */ #define KRB_I_K4 17 /* /KERBEROS4 (should k5 get k4 as well) */ #define KRB_I_NK4 18 /* /NO-KERBEROS4 */ #define KRB_I_POP 19 /* /POPUP */ #define KRB_I_ADR 20 /* /ADDRESSES: */ #define KRB_I_NAD 21 /* /NO-ADDRESSES */ #define KRB_I_MAX 21 /* Highest KERBEROS INIT switch number */ #endif /* CK_KERBEROS */ /* SET parameters */ #define XYBREA 0 /* BREAK simulation */ #define XYCHKT 1 /* Block check type */ #define XYDEBU 2 /* Debugging */ #define XYDELA 3 /* Delay */ #define XYDUPL 4 /* Duplex */ #define XYEOL 5 /* End-Of-Line (packet terminator) */ #define XYESC 6 /* Escape character */ #define XYFILE 7 /* File Parameters (see ckcker.h for values) */ /* (this space available) */ #define XYFLOW 9 /* Flow Control */ #define XYHAND 10 /* Handshake */ #define XYIFD 11 /* Incomplete File Disposition */ #define XYIMAG 12 /* "Image Mode" */ #define XYINPU 13 /* INPUT command parameters */ #define XYLEN 14 /* Maximum packet length to send */ #define XYLINE 15 /* Communication line to use */ /* SET LINE / SET HOST command switches */ #define SL_CNX 0 /* /CONNECT */ #define SL_SRV 1 /* /SERVER */ #define SL_SHR 2 /* /SHARE */ #define SL_NSH 3 /* /NOSHARE */ #define SL_BEE 4 /* /BEEP */ #define SL_ANS 5 /* /ANSWER */ #define SL_DIA 6 /* /DIAL:xxx */ #define SL_SPD 7 /* /SPEED:xxx */ #define SL_FLO 8 /* /FLOW:xxx */ #define SL_TMO 9 /* /TIMEOUT:xxx */ #define SL_CMD 10 /* /COMMAND */ #define SL_PSW 11 /* /PASSWORD:xxx */ #define SL_IKS 12 /* /KERMIT-SERVICE */ #define SL_NET 13 /* /NETWORK-TYPE:xxx */ #define SL_ENC 14 /* /ENCRYPT:type (telnet) /ENCRYPT (rlogin) */ #define SL_KRB4 15 /* /KERBEROS 4 (rlogin/telnet) */ #define SL_KRB5 16 /* /KERBEROS 5 (rlogin/telnet) */ #define SL_SRP 17 /* /SRP (telnet) */ #define SL_NTLM 18 /* /NTLM (telnet) */ #define SL_SSL 19 /* /SSL (telnet) */ #define SL_UID 20 /* /USERID:xxxx */ #define SL_AUTH 21 /* /AUTH:type */ #define SL_WAIT 22 /* /WAIT */ #define SL_NOWAIT 23 /* /NOWAIT */ #define SL_PTY 24 /* /PTY */ #define XYLOG 16 /* Log file */ #define XYMARK 17 /* Start of Packet mark */ #define XYNPAD 18 /* Amount of padding */ #define XYPADC 19 /* Pad character */ #define XYPARI 20 /* Parity */ #define XYPAUS 21 /* Interpacket pause */ #define XYPROM 22 /* Program prompt string */ #define XYQBIN 23 /* 8th-bit prefix */ #define XYQCTL 24 /* Control character prefix */ #define XYREPT 25 /* Repeat count prefix */ #define XYRETR 26 /* Retry limit */ #define XYSPEE 27 /* Line speed (baud rate) */ #define XYTACH 28 /* Character to be doubled */ #define XYTIMO 29 /* Timeout interval */ #define XYMODM 30 /* Modem - also see XYDIAL */ #define XYSEND 31 /* SET SEND parameters */ #define XYRECV 32 /* SET RECEIVE parameters */ #define XYTERM 33 /* SET TERMINAL parameters */ #define XYTBYT 0 /* Terminal Bytesize (7 or 8) */ #define XYTTYP 1 /* Terminal emulation Type */ #define TT_NONE 0 /* NONE, no emulation */ #ifdef OS2 /* Note, the symbols for VT and VT-like terminals should be in ascending numerical order, so that higher ones can be treated as supersets of lower ones with respect to capabilities. This is no longer the case with the influx of new terminal types. Just make sure that the ISXXXXX() macros include the proper family groups. */ #define TT_DG200 1 /* Data General 200 */ #define TT_DG210 2 /* Data General 210 */ #define TT_DG217 3 /* Data General 217 */ #define TT_HP2621 4 /* Hewlett-Packard 2621A */ #define TT_HPTERM 5 /* Hewlett-Packard Console */ #define TT_HZL1500 6 /* Hazeltine 1500 */ #define TT_VC4404 7 /* Volker Craig VC4404/404 */ #define TT_WY30 8 /* WYSE-30/30+ */ #define TT_WY50 9 /* WYSE-50/50+ */ #define TT_WY60 10 /* WYSE-60 */ #define TT_WY160 11 /* WYSE-160 */ #define TT_QNX 12 /* QNX */ #define TT_QANSI 13 /* QNX Ansi emulation */ #define TT_VT52 14 /* DEC VT-52 */ #define TT_H19 15 /* Heath-19 */ #define TT_IBM31 16 /* IBM 31xx */ #define TT_SCOANSI 17 /* SCOANSI (Unix mode) */ #define TT_AT386 18 /* Unixware AT386 (Unix mode) */ #define TT_ANSI 19 /* IBM ANSI.SYS (BBS) */ #define TT_VIP7809 20 /* Honeywell VIP7809 */ #define TT_LINUX 21 /* Linux Console */ #define TT_HFT 22 /* IBM High Function Terminal */ #define TT_AIXTERM 23 /* IBM AIXterm */ #define TT_SUN 24 /* SUN Console */ #define TT_BA80 25 /* Nixdorf BA80 */ #define TT_BEOS 26 /* BeOS Ansi */ #define TT_VT100 27 /* DEC VT-100 */ #define TT_VT102 28 /* DEC VT-102 */ #define TT_VT220 29 /* DEC VT-220 */ #define TT_VT220PC 30 /* DEC VT-220 with PC keyboard */ #define TT_VT320 31 /* DEC VT-320 */ #define TT_VT320PC 32 /* DEC VT-320 with PC keyboard */ #define TT_WY370 33 /* WYSE 370 ANSI Terminal */ #define TT_97801 34 /* Sinix 97801-5xx terminal */ #define TT_AAA 35 /* Ann Arbor Ambassador */ #define TT_TVI910 36 /* TVI 910+ */ #define TT_TVI925 37 /* TVI 925 */ #define TT_TVI950 38 /* TVI950 */ #define TT_ADM3A 39 /* LSI ADM 3A */ #define TT_ADM5 40 /* LSI ADM 5 */ #define TT_VTNT 41 /* Microsoft NT Virtual Terminal */ #define TT_MAX TT_VTNT #define TT_VT420 96 /* DEC VT-420 */ #define TT_VT520 97 /* DEC VT-520/525 */ #define TT_TEK40 99 /* Tektronix 401x */ #define TT_KBM_EMACS TT_MAX+1 #define TT_KBM_HEBREW TT_MAX+2 #define TT_KBM_RUSSIAN TT_MAX+3 #define TT_KBM_WP TT_MAX+4 #define ISAAA(x) (x == TT_AAA) #define ISANSI(x) (x >= TT_SCOANSI && x <= TT_ANSI) #define ISBA80(x) (x == TT_BA80) #define ISBEOS(x) (x == TT_BEOS) #define ISQNX(x) (x == TT_QNX) #define ISQANSI(x) (x == TT_QANSI) #define ISLINUX(x) (x == TT_LINUX) #define ISSCO(x) (x == TT_SCOANSI) #define ISAT386(x) (x == TT_AT386) #define ISAVATAR(x) (x == TT_ANSI) #define ISSUN(x) (x == TT_SUN) #define ISUNIXCON(x) (x == TT_SCOANSI || x == TT_AT386 || \ x == TT_LINUX || x == TT_SUN) #define ISDG200(x) (x >= TT_DG200 && x <= TT_DG217) #define ISHZL(x) (x == TT_HZL1500) #define ISH19(x) (x == TT_H19) #define ISIBM31(x) (x == TT_IBM31) #define ISTVI(x) (x >= TT_TVI910 && x <= TT_TVI950) #define ISTVI910(x) (x == TT_TVI910) #define ISTVI925(x) (x == TT_TVI925) #define ISTVI950(x) (x == TT_TVI950) #define ISVT52(x) (x == TT_VT52 || x == TT_H19) #ifdef COMMENT #define ISVT520(x) (x == TT_VT520) #define ISVT420(x) (x >= TT_VT420 && x <= TT_VT520) #else /* COMMENT */ /* Since we do not yet support 420/520 extend 320 */ #define ISVT520(x) (ISVT320(x)) #define ISVT420(x) (ISVT320(x)) #endif /* COMMENT */ #define ISVT320(x) (x >= TT_VT320 && x <= TT_AAA) #define ISVT220(x) (x >= TT_VT220 && x <= TT_AAA || \ ISBEOS(x) || ISQANSI(x) || \ ISLINUX(x) || ISSUN(x)) #define ISVT102(x) (x >= TT_VIP7809 && x <= TT_BA80 || \ x == TT_VT102 || ISVT220(x)) #define ISVT100(x) (x == TT_VT100 || ISVT102(x)) #define ISWY30(x) (x == TT_WY30) #define ISWYSE(x) (x >= TT_WY30 && x <= TT_WY160) #define ISWY50(x) (x == TT_WY50) #define ISWY60(x) (x == TT_WY60 || x == TT_WY160) #define ISWY160(x) (x == TT_WY160) #define ISWY370(x) (x == TT_WY370) #define ISVC(x) (x == TT_VC4404) #define ISHP(x) (x == TT_HPTERM || x == TT_HP2621) #define ISHPTERM(x) (x == TT_HPTERM) #define ISVIP(x) (x == TT_VIP7809) #define IS97801(x) (x == TT_97801) #define ISHFT(x) (x == TT_HFT || x == TT_AIXTERM) #define ISAIXTERM(x) (x == TT_AIXTERM) #define ISTEK(x) (x == TT_TEK40) #define ISVTNT(x) (x == TT_VTNT) #define ISADM3A(x) (x == TT_ADM3A) #define ISADM5(x) (x == TT_ADM5) #endif /* OS2 */ #define XYTCS 2 /* Terminal Character Set */ #define XYTSO 3 /* Terminal Shift-In/Shift-Out */ #define XYTNL 4 /* Terminal newline mode */ #ifdef OS2 #define XYTCOL 5 /* Terminal colors */ #endif /* OS2 */ #define XYTEC 6 /* Terminal echo = duplex = local-echo */ #ifdef OS2 #define XYTCUR 7 /* Terminal cursor */ #define TTC_ULINE 0 #define TTC_HALF 1 #define TTC_BLOCK 2 #define XYTARR 8 /* Terminal arrow-key mode */ #define XYTKPD 9 /* Terminal keypad mode */ #define TTK_NORM 0 /* Normal mode for arrow / keyad keys */ #define TTK_APPL 1 /* Application mode for arrow / keyad keys */ #define XYTWRP 10 /* Terminal wrap */ #endif /* OS2 */ #define XYTCRD 11 /* Terminal CR-display */ #define XYTANS 12 /* Terminal answerback */ #ifdef OS2 #define XYSCRS 13 /* Terminal scrollback buffer size */ #endif /* OS2 */ #define XYTAPC 14 /* Terminal APC */ #ifdef OS2 #define XYTBEL 15 /* Terminal Bell */ #endif /* OS2 */ #define XYTDEB 16 /* Terminal Debug */ #ifdef OS2 #define XYTROL 17 /* Terminal Rollback */ #define TTR_OVER 0 /* Rollback Overwrite */ #define TTR_INSERT 1 /* Rollback Insert */ #define TTR_KEYS 2 /* Keystrokes */ #define TTRK_IGN 0 /* Ignore */ #define TTRK_RST 2 /* Restore and Send */ #define TTRK_SND 1 /* Send */ #define XYTCTS 18 /* Terminal Transmit-Timeout */ #define XYTCPG 19 /* Terminal Code Page */ #ifdef COMMENT #define XYTHCU 20 /* Terminal Hide-Cursor */ #endif /* COMMENT */ #define XYTPAC 21 /* Terminal Output-Pacing */ #define XYTMOU 22 /* Terminal Mouse */ #endif /* OS2 */ #define XYTHIG 23 /* Terminal Width */ #define XYTWID 24 /* Terminal Height */ #ifdef OS2 #define XYTUPD 25 /* Terminal Screen-update */ #define TTU_FAST 0 /* FAST but jerky */ #define TTU_SMOOTH 1 /* SMOOTH but slow */ #define XYTFON 26 /* Terminal Full screen Font */ #define TTF_ROM 0 /* ROM font */ #define TTF_CY1 1 /* CYRILL1 font */ #define TTF_CY2 2 /* CYRILL2 font */ #define TTF_CY3 3 /* CYRILL3 font */ #define TTF_111 111 /* CP111 font */ #define TTF_112 112 /* CP112 font */ #define TTF_113 113 /* CP113 font */ #define TTF_437 437 /* CP437 font */ #define TTF_850 850 /* CP850 font */ #define TTF_851 851 /* CP851 font */ #define TTF_852 852 /* CP852 font */ #define TTF_853 853 /* CP853 font */ #define TTF_860 860 /* CP860 font */ #define TTF_861 861 /* CP861 font */ #define TTF_862 862 /* CP862 font */ #define TTF_863 863 /* CP863 font */ #define TTF_864 864 /* CP864 font */ #define TTF_865 865 /* CP865 font */ #define TTF_866 866 /* CP866 font */ #define TTF_880 880 /* CP880 font */ #define TTF_881 881 /* CP881 font */ #define TTF_882 882 /* CP882 font */ #define TTF_883 883 /* CP883 font */ #define TTF_884 884 /* CP884 font */ #define TTF_885 885 /* CP885 font */ #define XYTVCH 27 /* SET TERMINAL VIDEO-CHANGE */ #define TVC_DIS 0 /* DISABLED */ #define TVC_ENA 1 /* ENABLED */ #define TVC_W95 2 /* WIN95-SAFE */ #endif /* OS2 */ #define XYTAUTODL 28 /* SET TERMINAL AUTODOWNLOAD */ #define TAD_OFF 0 /* OFF */ #define TAD_ON 1 /* ON */ #define TAD_K 2 /* KERMIT */ #define TAD_Z 3 /* ZMODEM */ #define TAD_X_STR 0 /* STRING */ #define TAD_X_DETECT 1 /* DETECTION ( PACKET, STRING ) */ #define TAD_X_C0 2 /* C0 CONFLICTS */ #define TAD_ERR 4 /* ERROR { STOP, CONTINUE } */ #define TAD_ASK 5 /* ASK (dialog) */ #define XYTAUTOUL 29 /* SET TERMINAL AUTOUPLOAD */ #ifdef OS2 #define XYTATTBUG 30 /* SET TERM ATTR-BUG */ #define XYTSTAT 31 /* SET TERM STATUSLINE */ #endif /* OS2 */ #define XYTESC 32 /* SET TERM ESCAPE-CHARACTER */ #define XYTCTRL 33 /* SET TERM CONTROLS */ #ifdef OS2 #define XYTATTR 34 /* SET TERM ATTRIBUTE representation */ #define XYTSGRC 35 /* SET TERM SGRCOLORS */ #endif /* OS2 */ #define XYTLCS 36 /* SET TERM LOCAL-CHARACTER-SET */ #define XYTRCS 37 /* SET TERM REMOTE-CHARACTER-SET */ #define XYTUNI 38 /* SET TERM UNICODE */ #define XYTKEY 39 /* SET TERM KEY */ #ifdef OS2 #define XYTSEND 40 /* SET TERM SEND-DATA */ #define XYTSEOB 41 /* SET TERM SEND-END-OF-BLOCK */ #define XYTMBEL 42 /* SET TERM MARGIN-BELL */ #endif /* OS2 */ #define XYTIDLE 43 /* SET TERM IDLE-SEND */ #ifdef OS2 #define XYTKBMOD 44 /* SET TERM KEYBOARD-MODE */ #define XYTUNX 45 /* SET TERM UNIX-MODE (DG) */ #define XYTASCRL 46 /* SET TERM AUTOSCROLL */ #define XYTAPAGE 47 /* SET TERM AUTOPAGE */ #endif /* OS2 */ #define XYTRIGGER 48 /* SET TERM TRIGGER */ #ifdef OS2 #define XYTPCTERM 49 /* SET TERM PCTERM */ #define XYTOPTI 50 /* SET TERM SCREEN-OPTIMIZE */ #define XYTSCNM 51 /* SET TERM SCREEN-MODE (DECSCNM) */ #endif /* OS2 */ #define XYTPRN 52 /* SET TERM PRINT {AUTO, COPY, OFF} */ #ifdef OS2 #define XYTSAC 53 /* SET TERM SPACING-ATTRIBUTE-CHARACTER (inv) */ #define XYTSNIPM 54 /* SET TERM SNI-AUTOROLL */ #define XYTSNISM 55 /* SET TERM SNI-SCROLLMODE */ #define XYTKBDGL 56 /* SET TERM KBD-FOLLOWS-GL/GR */ #define XYTVTLNG 57 /* SET TERM VT-LANGUAGE */ #define VTL_NORTH_AM 1 #define VTL_BRITISH 2 #define VTL_BELGIAN 3 #define VTL_FR_CAN 4 #define VTL_DANISH 5 #define VTL_FINNISH 6 #define VTL_GERMAN 7 #define VTL_DUTCH 8 #define VTL_ITALIAN 9 #define VTL_SW_FR 10 #define VTL_SW_GR 11 #define VTL_SWEDISH 12 #define VTL_NORWEGIA 13 #define VTL_FRENCH 14 #define VTL_SPANISH 15 #define VTL_PORTUGES 16 #define VTL_HEBREW 19 #define VTL_GREEK 22 #define VTL_CANADIAN 28 #define VTL_TURK_Q 29 #define VTL_TURK_F 30 #define VTL_HUNGARIA 31 #define VTL_SLOVAK 33 #define VTL_CZECH 34 #define VTL_POLISH 35 #define VTL_ROMANIAN 36 #define VTL_SCS 38 #define VTL_RUSSIAN 39 #define VTL_LATIN_AM 40 #define XYTVTNRC 58 /* SET TERM VT-NRC-MODE */ #define XYTSNICC 59 /* SET TERM SNI-CH.CODE */ #define XYTSNIFV 60 /* SET TERM SNI-FIRMWARE-VERSIONS */ #define XYTURLHI 61 /* SET TERM URL-HIGHLIGHT */ #endif /* OS2 */ #define XYTITMO 62 /* SET TERM IDLE-TIMEOUT */ #define XYTIACT 63 /* SET TERM IDLE-ACTION */ #define XYTLSP 64 /* SET TERM LINE-SPACING */ #define XYTLFD 65 /* SET TERM LF-DISPLAY */ #define XYATTR 34 /* Attribute packets */ #define XYSERV 35 /* Server parameters */ #define XYSERT 0 /* Server timeout */ #define XYSERD 1 /* Server display */ #define XYSERI 2 /* Server idle */ #define XYSERP 3 /* Server get-path */ #define XYSERL 4 /* Server login */ #define XYSERC 5 /* Server CD-Message */ #define XYSERK 6 /* Server keepalive */ #define XYWIND 36 /* Window size */ #define XYXFER 37 /* Transfer */ #define XYX_CAN 0 /* Cancellation */ #define XYX_CSE 1 /* Character-Set */ #define XYX_LSH 2 /* Locking-Shift */ #define XYX_PRO 3 /* Protocol */ #define XYX_MOD 4 /* Mode */ #define XYX_DIS 5 /* Display */ #define XYX_SLO 6 /* Slow-start */ #define XYX_CRC 7 /* CRC calculation */ #define XYX_BEL 8 /* Bell */ #define XYX_PIP 9 /* Pipes */ #define XYX_INT 10 /* Interruption */ #define XYX_XLA 11 /* (character-set) Translation On/Off */ #define XYX_MSG 12 /* Message */ #define XYX_RPT 13 /* Report */ #define XYLANG 38 /* Language */ #define XYCOUN 39 /* Count */ #define XYTAKE 40 /* Take */ #define XYUNCS 41 /* Unknown-character-set */ #define XYKEY 42 /* Key */ #define XYMACR 43 /* Macro */ #define XYHOST 44 /* Hostname on network */ #define XYNET 45 /* SET NETWORK things */ #define XYNET_D 99 /* NETWORK DIRECTORY */ #define XYNET_T 100 /* NETWORK TYPE */ #define XYCARR 46 /* Carrier */ #define XYXMIT 47 /* Transmit */ #define XYDIAL 48 /* Dial options */ /* And now we interrupt the flow to bring you lots of stuff about dialing */ #ifndef MAXTOLLFREE /* Maximum number of toll-free area codes */ #define MAXTOLLFREE 8 #endif /* MAXTOLLFREE */ #ifndef MAXTPCC /* Maximum Tone or Pulse dialing countries */ #define MAXTPCC 160 #endif /* MAXTPCC */ #ifndef MAXPBXEXCH /* Maximum number of PBX exchanges */ #define MAXPBXEXCH 8 #endif /* MAXPBXEXCH */ #ifndef MAXLOCALAC #define MAXLOCALAC 32 #endif /* MAXLOCALAC */ #ifndef MAXDNUMS #ifdef BIGBUFOK #define MAXDDIR 32 /* Maximum number of dialing directories */ #define MAXDNUMS 4095 /* Max numbers to fetch from dialing directories */ #else #define MAXDDIR 12 #define MAXDNUMS 1024 #endif /* BIGBUFOK */ #endif /* MAXDNUMS */ /* IMPORTANT: In 5A(192), the old SET DIAL command was split into two commands: SET MODEM (for modem-related parameters) and SET DIAL (for dialing items). To preserve the old formats, etc, invisibly we keep one symbol space for both commands. */ #define XYDHUP 0 /* Dial Hangup */ #define XYDINI 1 /* MODEM (dial) Initialization string */ #define XYDKSP 2 /* MODEM (dial) Kermit-Spoof */ #define XYDTMO 3 /* Dial Timeout */ #define XYDDPY 4 /* Dial Display */ #define XYDSPD 5 /* Dial Speed matching */ #define XYDMNP 6 /* MODEM (dial) MNP negotiation enabled (obsolete) */ #define XYDEC 7 /* MODEM (dial) error correction enabled */ #define XYDDC 8 /* MODEM (dial) compression enabled */ #define XYDHCM 9 /* MODEM (dial) hangup-string (moved elsewhere) */ #define XYDDIR 10 /* Dial directory */ #define XYDDIA 11 /* MODEM (dial) dial-command */ #define XYDMHU 12 /* MODEM HANGUP (dial modem-hangup) */ #ifndef DEFMDHUP /* Default MODEM HANGUP-METHOD */ #define DEFMDMHUP 1 /* 0 = RS232, 1 = modem command */ #endif /* DEFMDMHUP */ #define XYDNPR 13 /* Dial PREFIX */ #define XYDSTR 14 /* MODEM COMMAND (dial string) ... */ #define XYDS_DC 0 /* Data compression */ #define XYDS_EC 1 /* Error correction */ #define XYDS_HU 2 /* Hangup command */ #define XYDS_HW 3 /* Hardware flow control */ #define XYDS_IN 4 /* Init-string */ #define XYDS_NF 5 /* No flow control */ #define XYDS_PX 6 /* Prefix (no, this goes in SET DIAL) */ #define XYDS_SW 7 /* Software flow control */ #define XYDS_DT 8 /* Tone dialing command */ #define XYDS_DP 9 /* Pulse dialing command */ #define XYDS_AN 10 /* Autoanswer */ #define XYDS_RS 11 /* Reset */ #define XYDS_MS 12 /* Dial mode string */ #define XYDS_MP 13 /* Dial mode prompt */ #define XYDS_SP 14 /* Modem speaker */ #define XYDS_VO 15 /* Modem speaker volume */ #define XYDS_ID 16 /* Ignore dialtone */ #define XYDS_I2 17 /* Init string #2 */ #define XYDM_A 9 /* Method: Auto */ #define XYDM_D 0 /* Default */ #define XYDM_T 2 /* Tone */ #define XYDM_P 3 /* Pulse */ #define XYDFC 15 /* MODEM (dial) flow-control */ #define XYDMTH 16 /* Dial method */ #define XYDESC 17 /* MODEM (dial) escape-character */ #define XYDMAX 18 /* MODEM (dial) maximum interface speed */ #define XYDCAP 19 /* MODEM (dial) capabilities */ #define XYDTYP 20 /* MODEM TYPE */ #define XYDINT 21 /* DIAL retries */ #define XYDRTM 22 /* DIAL time between retries */ #define XYDNAM 23 /* MODEM NAME */ #define XYDLAC 24 /* DIAL (LOCAL-)AREA-CODE */ #define XYDMCD 25 /* MODEM CARRIER */ #define XYDCNF 26 /* DIAL CONFIRMATION */ #define XYDCVT 27 /* DIAL CONVERT-DIRECTORY */ #define XYDIXP 28 /* DIAL INTERNATIONAL-PREFIX */ #define XYDIXS 29 /* DIAL INTERNATIONAL-SUFFIX */ #define XYDLCC 30 /* DIAL LOCAL-COUNTRY-CODE */ #define XYDLDP 31 /* DIAL LONG-DISTANCE-PREFIX */ #define XYDLDS 32 /* DIAL LONG-DISTANCE-SUFFIX */ #define XYDPXX 33 /* DIAL PBX-EXCHANGE */ #define XYDPXI 34 /* DIAL PBX-INTERNAL-PREFIX */ #define XYDPXO 35 /* DIAL PBX-OUTSIDE-PREFIX */ #define XYDSFX 36 /* DIAL SUFFIX */ #define XYDSRT 37 /* DIAL SORT */ #define XYDTFC 38 /* DIAL TOLL-FREE-AREA-CODE */ #define XYDTFP 39 /* DIAL TOLL-FREE-PREFIX */ #define XYDTFS 40 /* DIAL TOLL-FREE-SUFFIX */ #define XYDCON 41 /* DIAL CONNECT */ #define XYDRSTR 42 /* DIAL RESTRICT */ #define XYDRSET 43 /* MODEM RESET */ #define XYDLCP 44 /* DIAL LOCAL-PREFIX */ #define XYDLCS 45 /* DIAL LOCAL-SUFFIX */ #define XYDLLAC 46 /* DIAL LC-AREA-CODES */ #define XYDFLD 47 /* DIAL FORCE LONG-DISTANCE */ #define XYDSPK 48 /* MODEM SPEAKER */ #define XYDVOL 49 /* MODEM VOLUME */ #define XYDIDT 50 /* IGNORE DIALTONE */ #define XYDPAC 51 /* PACING */ #define XYDMAC 52 /* MACRO */ #define XYDPUCC 53 /* PULSE-COUNTRIES */ #define XYDTOCC 54 /* TONE-COUNTRIES */ #define XYDTEST 55 /* TEST */ #define XYA_CID 1 /* SET ANSWER CALLER-ID */ #define XYA_RNG 2 /* SET ANSWER RINGS */ #define XYSESS 49 /* SET SESSION options */ #define XYBUF 50 /* Buffer length */ #define XYBACK 51 /* Background */ #define XYDFLT 52 /* Default */ #define XYDBL 53 /* Double */ #define XYCMD 54 /* COMMAND */ #define XYCASE 55 /* Case */ #define XYCOMP 56 /* Compression */ #define XYX25 57 /* X.25 parameter (ANYX25) */ #define XYPAD 58 /* X.3 PAD parameter (ANYX25) */ #define XYWILD 59 /* Wildcard expansion method */ #define WILD_OFF 0 /* Wildcard expansion off */ #define WILD_ON 1 /* Wildcard expansion on */ #define WILD_KER 2 /* Wildcard expansion by Kermit */ #define WILD_SHE 3 /* Wildcard expansion by Shell */ #define XYSUSP 60 /* Suspend */ #define XYMAIL 61 /* Mail-Command */ #define XYPRIN 62 /* Print-Command */ #define XYQUIE 63 /* Quiet */ #define XYLCLE 64 /* Local-echo */ #define XYSCRI 65 /* SCRIPT command parameters */ #define XYMSGS 66 /* MESSAGEs ON/OFF */ #ifdef TNCODE #define XYTEL 67 /* SET TELNET parameters */ #define CK_TN_EC 0 /* TELNET ECHO */ #define CK_TN_TT 1 /* TELNET TERMINAL-TYPE */ #define CK_TN_NL 2 /* TELNET NEWLINE-MODE */ #define CK_TN_BM 3 /* TELNET BINARY-MODE */ #define CK_TN_BUG 4 /* TELNET BUG */ #define CK_TN_ENV 5 /* TELNET ENVIRONMENT */ #define TN_ENV_USR 0 /* VAR USER */ #define TN_ENV_JOB 1 /* VAR JOB */ #define TN_ENV_ACCT 2 /* VAR ACCT */ #define TN_ENV_PRNT 3 /* VAR PRINTER */ #define TN_ENV_SYS 4 /* VAR SYSTEMTYPE */ #define TN_ENV_DISP 5 /* VAR DISPLAY */ #define TN_ENV_UVAR 6 /* USERVAR */ #define TN_ENV_LOC 7 /* USERVAR LOCATION */ #define TN_ENV_ON 98 /* ON (enabled) */ #define TN_ENV_OFF 99 /* OFF (disabled) */ #define CK_TN_LOC 6 /* TELNET LOCATION */ #define CK_TN_AU 7 /* TELNET AUTHENTICATION */ #define TN_AU_FWD 4 /* AUTH FORWARD */ #define TN_AU_TYP 5 /* AUTH TYPE */ #define AUTH_NONE 0 /* AUTH NONE */ #define AUTH_KRB4 1 /* AUTH Kerberos IV */ #define AUTH_KRB5 2 /* AUTH Kerberos V */ #define AUTH_SSL 7 /* AUTH Secure Sockets Layer */ #define AUTH_TLS 98 /* AUTH Transport Layer Security */ #define AUTH_SRP 5 /* AUTH Secure Remote Password */ #define AUTH_NTLM 15 /* AUTH NT Lan Manager */ #define AUTH_AUTO 99 /* AUTH AUTOMATIC */ #define TN_AU_HOW 8 /* AUTH HOW FLAG */ #define TN_AU_ENC 9 /* AUTH ENCRYPT FLAG */ #define CK_TN_ENC 8 /* TELNET ENCRYPTION */ #define TN_EN_TYP 4 /* ENCRYPT TYPE */ #define TN_EN_START 5 /* ENCRYPT START */ #define TN_EN_STOP 6 /* ENCRYPT STOP */ #define CK_TN_IKS 9 /* TELNET KERMIT-SERVER */ #define CK_TN_RE 10 /* TELNET REMOTE-ECHO */ #define CK_TN_TLS 11 /* TELNET START_TLS */ #define CK_TN_XD 12 /* TELNET XDISPLOC */ #define CK_TN_NAWS 13 /* TELNET NAWS */ #define CK_TN_WAIT 14 /* TELNET WAIT-FOR-NEGOTIATIONS */ #define CK_TN_SGA 15 /* TELNET SGA */ #define CK_TN_CLIENT 16 /* TELNET CLIENT */ #define CK_TN_SERVER 17 /* TELNET SERVER */ #define CK_TN_PHR 18 /* TELNET PRAGMA-HEARTBEAT */ #define CK_TN_PLG 19 /* TELNET PRAGMA-LOGON */ #define CK_TN_PSP 20 /* TELNET PRAGMA-SSPI */ #define CK_TN_SAK 21 /* TELNET IBM SAK */ #define CK_TN_FLW 22 /* TELNET LFLOW */ #define CK_TN_XF 23 /* TELNET TRANSFER-MODE */ #define CK_TN_PUID 24 /* TELNET PROMPT-FOR-USERID */ #define CK_TN_NE 25 /* TELNET NO-ENCRYPT-DURING-XFER */ #define CK_TN_CPC 26 /* TELNET COM-PORT-CONTROL */ #define CK_TN_DB 27 /* TELNET DEBUG */ #define CK_TN_FX 28 /* TELNET FORWARD_X */ #define CK_TN_DL 29 /* TELNET DELAY-SB */ #define CK_TN_SFU 30 /* TELNET SFU-COMPATIBILITY */ #define CK_TN_LOG 31 /* TELNET LOGOUT */ #endif /* TNCODE */ #define XYOUTP 68 /* OUTPUT command parameters */ #define OUT_PAC 0 /* OUTPUT PACING */ #define OUT_ESC 1 /* OUTPUT SPECIAL-ESCAPES */ #define XYEXIT 69 /* SET EXIT */ #define XYPRTR 70 /* SET PRINTER */ #define XYFPATH 71 /* PATHNAME */ #ifdef OS2 #define XYMOUSE 72 /* MOUSE SUPPORT */ #define XYM_ON 0 /* Mouse ON/OFF */ #define XYM_BUTTON 1 /* Define Mouse Events */ #define XYM_CLEAR 2 /* Clear Mouse Events */ #define XYM_DEBUG 3 /* Debug Mode ON/OFF */ /* These must match similar definitions in ckokey.h */ #define XYM_B1 0 /* Mouse Button One */ #define XYM_B2 1 /* Mouse Button Two */ #define XYM_B3 2 /* Mouse Button Three */ #define XYM_ALT 1 /* Alt */ #define XYM_CTRL 2 /* Ctrl */ #define XYM_SHIFT 4 /* Shift */ #define XYM_C1 0 /* Single Click */ #define XYM_C2 8 /* Double Click */ #define XYM_DRAG 16 /* Drag Event */ #endif /* OS2 */ #define XYBELL 73 /* BELL */ #ifdef OS2 #define XYPRTY 74 /* Thread Priority Level */ #define XYP_IDLE 1 #define XYP_REG 2 #define XYP_SRV 4 #define XYP_RTP 3 #endif /* OS2 */ #define XYALRM 75 /* SET ALARM */ #define XYPROTO 76 /* SET PROTOCOL */ #define XYPREFIX 77 /* SET PREFIXING */ #define XYLOGIN 78 /* Login info for script programs... */ #define LOGI_UID 0 /* User ID */ #define LOGI_PSW 1 /* Password */ #define LOGI_PRM 2 /* Prompt */ #define XYSTARTUP 79 /* Startup file */ #define XYTMPDIR 80 /* Temporary directory */ #ifdef OS2 #define XYTAPI 81 /* Microsoft Telephone API options */ #define XYTAPI_CFG 1 /* TAPI Configure-Line Dialog */ #define XYTAPI_DIAL 2 /* TAPI Dialing-Properties Dialog */ #define XYTAPI_LIN 3 /* TAPI Line */ #define XYTAPI_LOC 4 /* TAPI Location */ #define XYTAPI_PASS 5 /* TAPI Passthrough */ #define XYTAPI_CON 6 /* TAPI Conversions */ #define XYTAPI_LGHT 7 /* TAPI Modem Lights */ #define XYTAPI_PRE 8 /* TAPI Pre-dialing Terminal */ #define XYTAPI_PST 9 /* TAPI Post-dialing Terminal */ #define XYTAPI_INA 10 /* TAPI Inactivity Timeout */ #define XYTAPI_BNG 11 /* TAPI Wait for Credit Card Tone */ #define XYTAPI_MAN 12 /* TAPI Manual Dialing */ #define XYTAPI_USE 13 /* TAPI Use Line Config settings */ #endif /* OS2 */ #ifdef TCPSOCKET #define XYTCP 82 /* TCP options */ #define XYTCP_NODELAY 1 /* No Delay */ #define XYTCP_SENDBUF 2 /* Send Buffer Size */ #define XYTCP_LINGER 3 /* Linger */ #define XYTCP_RECVBUF 4 /* Receive Buffer Size */ #define XYTCP_KEEPALIVE 5 /* Keep Alive packets */ #define XYTCP_UCX 6 /* UCX 2.0 port swabbing bug */ #define XYTCP_NAGLE 7 /* Delay - inverse of 1 */ #define XYTCP_RDNS 8 /* Reverse DNS lookup */ #define XYTCP_ADDRESS 9 /* Set preferred IP Address */ #define XYTCP_DNS_SRV 10 /* Use DNS Service Records */ #define XYTCP_DONTROUTE 11 /* Dont Route */ #define XYTCP_SOCKS_SVR 12 /* Name of Socks Server */ #define XYTCP_HTTP_PROXY 13 /* Name/Port of HTTP Proxy Server */ #define XYTCP_SOCKS_NS 14 /* Name of Socks Name Server */ #endif /* TCPSOCKET */ #ifdef OS2 #define XYMSK 83 /* MS-DOS Kermit compatibility options */ #define MSK_COLOR 0 /* Terminal color handling */ #define MSK_KEYS 1 /* SET KEY uses MSK keycodes */ #define MSK_REN 2 /* File renaming uses 8.3 notation always */ #endif /* OS2 */ #define XYDEST 84 /* SET DESTINATION as in MS-DOS Kermit */ #ifdef OS2 #define XYWIN95 85 /* SET WIN95 work arounds */ #define XYWKEY 0 /* Keyboard translation */ #define XYWAGR 1 /* Alt-Gr */ #define XYWOIO 2 /* Overlapped I/O */ #define XYWLUC 3 /* Lucida Console substitutions */ #define XYWSELECT 4 /* Select on Write Bug */ #define XYW8_3 5 /* Use 8.3 filenames? */ #define XYWPOPUP 6 /* Use Popups? */ #define XYWHSL 7 /* Horz Scan Line substitutions */ #define XYDLR 86 /* SET K95 DIALER work arounds */ #define XYTITLE 87 /* SET TITLE of window */ #endif /* OS2 */ #define XYIGN 88 /* SET IGNORE-CHARACTER */ #define XYEDIT 89 /* SET EDITOR */ #define XYFLTR 90 /* SET { SEND, RECEIVE } FILTER */ #define XYBROWSE 91 /* SET BROWSER */ #define XYEOF 92 /* EOF (= FILE EOF) */ #ifdef OS2 #define XYBDCP 93 /* BPRINTER */ #endif /* OS2 */ #define XYFLAG 94 /* FLAG */ #define XYLIMIT 95 /* SESSION-LIMIT */ #define XYINIL 96 /* Protocol negotiation string max length */ #define XYRELY 97 /* RELIABLE */ #define XYSTREAM 98 /* STREAMING */ #define XYTLOG 99 /* TRANSACTION-LOG */ #define XYCLEAR 100 /* CLEARCHANNEL */ #define XYAUTH 101 /* AUTHENTICATION */ #ifdef TNCODE #define XYKRBPR 0 /* Kerberos Principal */ #define XYKRBRL 1 /* Kerberos Realm */ #define XYKRBCC 2 /* Kerberos 5 Credentials-Cache */ #define XYKRBSRV 3 /* Kerberos Service Name */ #define XYKRBDBG 4 /* Kerberos Debugging */ #define XYKRBLIF 5 /* Kerberos Lifetime */ #define XYKRBPRE 6 /* Kerberos 4 Preauth */ #define XYKRBINS 7 /* Kerberos 4 Instance */ #define XYKRBFWD 8 /* Kerberos 5 Forwardable */ #define XYKRBPRX 9 /* Kerberos 5 Proxiable */ #define XYKRBRNW 10 /* Kerberos 5 Renewable lifetime */ #define XYKRBGET 11 /* Kerberos Auto-Get-TGTs */ #define XYKRBDEL 12 /* Kerberos Auto-Destroy-TGTs */ #define KRB_DEL_NO 0 /* Kerberos No Auto Destroy */ #define KRB_DEL_CL 1 /* Kerberos Auto Destory on Close */ #define KRB_DEL_EX 2 /* Kerberos Auto Destroy on Exit */ #define XYKRBK5K4 13 /* Kerberos 5 Get K4 Tickets */ #define XYKRBPRM 14 /* Kerberos 4/5 Prompt */ #define XYKRBADR 15 /* Kerberos 4/5 CheckAddrs */ #define XYKRBNAD 16 /* Kerberos 5 No Addresses */ #define XYKRBADD 17 /* Kerberos 5 Address List */ #define XYKRBKTB 18 /* Kerberos 4/5 Key Table */ #define XYSRPPRM 0 /* SRP Prompt */ #define XYSSLRCFL 0 /* SSL/TLS RSA Certs file */ #define XYSSLCOK 1 /* SSL/TLS Certs-Ok flag */ #define XYSSLCRQ 2 /* SSL/TLS Certs-Required flag */ #define XYSSLCL 3 /* SSL/TLS Cipher List */ #define XYSSLDBG 4 /* SSL/TLS Debug flag */ #define XYSSLRKFL 5 /* SSL/TLS RSA Key File */ #define XYSSLLFL 6 /* SSL/TLS Log File */ #define XYSSLON 7 /* SSL/TLS Only flag */ #define XYSSLSEC 8 /* SSL/TLS Secure flag */ #define XYSSLVRB 9 /* SSL/TLS Verbose flag */ #define XYSSLVRF 10 /* SSL/TLS Verify flag */ #define XYSSLDUM 11 /* SSL/TLS Dummy flag */ #define XYSSLDCFL 12 /* SSL/TLS DSA Certs file */ #define XYSSLDKFL 13 /* SSL/TLS DH Certs file */ #define XYSSLDPFL 14 /* SSL/TLS DH Param file */ #define XYSSLCRL 15 /* SSL/TLS CRL file */ #define XYSSLCRLD 16 /* SSL/TLS CRL dir */ #define XYSSLVRFF 17 /* SSL/TLS Verify file */ #define XYSSLVRFD 18 /* SSL/TLS Verify dir */ #define XYSSLRND 19 /* SSL/TLS Random file */ #define XYSSLDCCF 20 /* SSL/TLS DSA Certs Chain File */ #define XYSSLRCCF 21 /* SSL/TLS RSA Certs Chain File */ /* The following must be powers of 2 for a bit mask */ #define XYKLCEN 1 /* Kerberos List Credentials: Encryption */ #define XYKLCFL 2 /* Kerberos List Credentials: Flags */ #define XYKLCAD 4 /* Kerberos List Credentials: Addresses */ #endif /* TNCODE */ #define XYFUNC 102 /* SET FUNCTION */ #define FUNC_DI 0 /* FUNCTION DIAGNOSTICS */ #define FUNC_ER 1 /* FUNCTION ERROR */ #define XYFTP 103 /* FTP application */ #define XYSLEEP 104 /* SLEEP / PAUSE options */ #define XYSSH 105 /* SSH options */ #define XYTELOP 106 /* TELNET OPTIONS (TELOPT) */ #define XYCD 107 /* SET CD */ #define XYCSET 108 /* CHARACTER-SET */ #define XYSTOP 109 /* STOP-BITS */ #define XYSERIAL 110 /* SERIAL */ #define XYDISC 111 /* CLOSE-ON-DISCONNECT */ #define XYOPTS 112 /* OPTIONS */ #define XYQ8FLG 113 /* Q8FLAG (invisible) */ #define XYTIMER 114 /* TIMER */ #define XYFACKB 115 /* F-ACK-BUG */ #define XYBUP 116 /* SET SEND/RECEIVE BACKUP */ #define XYMOVE 117 /* SET SEND/RECEIVE MOVE-TO */ #define XYRENAME 118 /* SET SEND/RECEIVE RENAME-TO */ #define XYHINTS 119 /* SET HINTS */ #define XYEVAL 120 /* SET EVALUATE */ #define XYFACKP 121 /* F-ACK-PATH */ #define XYSYSL 122 /* SysLog */ #define XYQNXPL 123 /* QNX Port Lock */ #define XYIKS 124 /* SET IKS ... */ #define XYROOT 125 /* SET ROOT */ #define XYFTPX 126 /* SET FTP */ #define XYSEXP 127 /* SET SEXP */ #define XYGPR 128 /* SET GET-PUT-REMOTE */ #define XYLOCUS 129 /* SET LOCUS */ #define XYGUI 130 /* SET GUI */ #define XYANSWER 131 /* SET ANSWER */ #define XYMATCH 132 /* SET MATCHDOT */ #define XYSFTP 133 /* SET SFTP */ #define XY_REN 134 /* SET RENAME */ #define XYEXTRN 135 /* SET EXTERNAL-PROTOCOL */ #define XYVAREV 136 /* SET VARIABLE-EVALUATION */ /* End of SET commands */ /* S-Expressions -- floating-point support required */ #ifndef CKFLOAT #ifndef NOSEXP #define NOSEXP #endif /* NOSEXP */ #endif /* CKFLOAT */ /* Maximum number of elements in an S-Expression */ #ifndef NOSEXP #ifndef SEXPMAX #ifdef BIGBUFOK #define SEXPMAX 1024 #else #define SEXPMAX 32 #endif /* BIGBUFOK */ #endif /* SEXPMAX */ #endif /* NOSEXP */ #ifdef ANYX25 /* PAD command parameters */ #define XYPADL 0 /* clear virtual call */ #define XYPADS 1 /* status of virtual call */ #define XYPADR 2 /* reset of virtual call */ #define XYPADI 3 /* send an interrupt packet */ /* Used with XYX25... */ #define XYUDAT 0 /* X.25 call user data */ #define XYCLOS 1 /* X.25 closed user group call */ #define XYREVC 2 /* X.25 reverse charge call */ #endif /* ANYX25 */ #ifdef OS2 /* SET PRINTER switches */ #define PRN_OUT 0 /* Output only */ #define PRN_BID 1 /* Bidirectional */ #define PRN_DOS 2 /* DOS device */ #define PRN_WIN 3 /* Windows queue */ #define PRN_TMO 4 /* Timeout */ #define PRN_TRM 5 /* Terminator */ #define PRN_SEP 6 /* Separator */ #define PRN_SPD 7 /* COM-port speed */ #define PRN_FLO 8 /* COM-port flow control */ #define PRN_PAR 9 /* COM-port parity */ #define PRN_NON 10 /* No printer */ #define PRN_FIL 11 /* Filename */ #define PRN_PIP 12 /* Pipename */ #define PRN_PS 13 /* Text to PS */ #define PRN_WID 14 /* PS Width */ #define PRN_LEN 15 /* PS Length */ #define PRN_RAW 16 /* Non-PS */ #define PRN_CS 17 /* Character Set */ #define PRN_MAX 17 /* Number of switches defined */ /* Printer types */ #define PRT_DOS 0 /* DOS */ #define PRT_WIN 1 /* Windows Queue */ #define PRT_FIL 2 /* File */ #define PRT_PIP 3 /* Pipe */ #define PRT_NON 4 /* None */ #define PRINTSWI #endif /* OS2 */ #endif /* NOICP */ #ifndef NODIAL /* Symbols for modem types, moved here from ckudia.c, May 1997, because now they are also used in some other modules. The numbers MUST correspond to the ordering of entries within the modemp[] array. */ #ifdef MINIDIAL /* Minimum dialer support */ #define n_DIRECT 0 /* Direct connection -- no modem */ #define n_CCITT 1 /* CCITT/ITU-T V.25bis */ #define n_HAYES 2 /* Hayes 2400 */ #define n_UNKNOWN 3 /* Unknown */ #define n_UDEF 4 /* User-Defined */ #define n_GENERIC 5 /* Generic High Speed */ #define n_ITUTV250 6 /* ITU-T V.250 */ #define MAX_MDM 6 /* Number of modem types */ #else /* Full-blown dialer support */ #define n_DIRECT 0 /* Direct connection -- no modem */ #define n_ATTDTDM 1 #define n_ATTISN 2 #define n_ATTMODEM 3 #define n_CCITT 4 #define n_CERMETEK 5 #define n_DF03 6 #define n_DF100 7 #define n_DF200 8 #define n_GDC 9 #define n_HAYES 10 #define n_PENRIL 11 #define n_RACAL 12 #define n_UNKNOWN 13 #define n_VENTEL 14 #define n_CONCORD 15 #define n_ATTUPC 16 /* aka UNIX PC and ATT7300 */ #define n_ROLM 17 /* Rolm CBX DCM */ #define n_MICROCOM 18 /* Microcoms in SX command mode */ #define n_USR 19 /* Modern USRs */ #define n_TELEBIT 20 /* Telebits of all kinds */ #define n_DIGITEL 21 /* Digitel DT-22 (CCITT variant) */ #define n_H_1200 22 /* Hayes 1200 */ #define n_H_ULTRA 23 /* Hayes Ultra and maybe Optima */ #define n_H_ACCURA 24 /* Hayes Accura and maybe Optima */ #define n_PPI 25 /* Practical Peripherals */ #define n_DATAPORT 26 /* AT&T Dataport */ #define n_BOCA 27 /* Boca */ #define n_MOTOROLA 28 /* Motorola Fastalk or Lifestyle */ #define n_DIGICOMM 29 /* Digicomm Connection */ #define n_DYNALINK 30 /* Dynalink 1414VE */ #define n_INTEL 31 /* Intel 14400 Faxmodem */ #define n_UCOM_AT 32 /* Microcoms in AT mode */ #define n_MULTI 33 /* Multitech MT1432 */ #define n_SUPRA 34 /* SupraFAXmodem */ #define n_ZOLTRIX 35 /* Zoltrix */ #define n_ZOOM 36 /* Zoom */ #define n_ZYXEL 37 /* ZyXEL */ #define n_TAPI 38 /* TAPI Line modem - whatever it is */ #define n_TBNEW 39 /* Newer Telebit models */ #define n_MAXTECH 40 /* MaxTech XM288EA */ #define n_UDEF 41 /* User-Defined */ #define n_RWV32 42 /* Generic Rockwell V.32 */ #define n_RWV32B 43 /* Generic Rockwell V.32bis */ #define n_RWV34 44 /* Generic Rockwell V.34 */ #define n_MWAVE 45 /* IBM Mwave Adapter */ #define n_TELEPATH 46 /* Gateway Telepath */ #define n_MICROLINK 47 /* MicroLink modems */ #define n_CARDINAL 48 /* Cardinal modems */ #define n_GENERIC 49 /* Generic high-speed */ #define n_XJACK 50 /* Megahertz X-Jack */ #define n_SPIRITII 51 /* Quickcomm Spirit II */ #define n_MONTANA 52 /* Motorola Montana */ #define n_COMPAQ 53 /* Compaq Data+Fax Modem */ #define n_FUJITSU 54 /* Fujitsu Fax/Modem Adpater */ #define n_MHZATT 55 /* Megahertz AT&T V.34 */ #define n_SUPRASON 56 /* SupraSonic */ #define n_BESTDATA 57 /* Best Data */ #define n_ATT1900 58 /* AT&T STU III Model 1900 */ #define n_ATT1910 59 /* AT&T STU III Model 1910 */ #define n_KEEPINTOUCH 60 /* AT&T KeepinTouch */ #define n_USRX2 61 /* USR XJ-1560 X2 56K */ #define n_ROLMAT 62 /* Rolm with AT command set */ #define n_ATLAS 63 /* Atlas / Newcom ixfC 33.6 */ #define n_CODEX 64 /* Motorola Codex 326X Series */ #define n_MT5634ZPX 65 /* Multitech MT5634ZPX */ #define n_ULINKV250 66 /* Microlink ITU-T V.250 56K */ #define n_ITUTV250 67 /* Generic ITU-T V.250 */ #define n_RWV90 68 /* Generic Rockwell V.90 */ #define n_SUPRAX 69 /* Diamond Supra Express V.90 */ #define n_LUCENT 70 /* Lucent Venus chipset */ #define n_PCTEL 71 /* PCTel chipset */ #define n_CONEXANT 72 /* Conexant modem family */ #define n_ZOOMV34 73 /* Zoom */ #define n_ZOOMV90 74 /* Zoom */ #define n_ZOOMV92 75 /* ZOOM V.92 */ #define n_MOTSM56 76 /* Motorola SM56 chipset */ #define MAX_MDM 76 /* Number of modem types */ #endif /* MINIDIAL */ #endif /* NODIAL */ #ifndef NOICP /* SHOW command symbols */ #define SHPAR 0 /* Parameters */ #define SHVER 1 /* Versions */ #define SHCOM 2 /* Communications */ #define SHPRO 3 /* Protocol */ #define SHFIL 4 /* File */ #define SHLNG 5 /* Language */ #define SHCOU 6 /* Count */ #define SHMAC 7 /* Macros */ #define SHKEY 8 /* Key */ #define SHSCR 9 /* Scripts */ #define SHSPD 10 /* Speed */ #define SHSTA 11 /* Status */ #define SHSER 12 /* Server */ #define SHXMI 13 /* Transmit */ #define SHATT 14 /* Attributes */ #define SHMOD 15 /* Modem */ #define SHDFLT 16 /* Default (as in VMS) */ #define SHVAR 17 /* Show global variables */ #define SHARG 18 /* Show macro arguments */ #define SHARR 19 /* Show arrays */ #define SHBUI 20 /* Show builtin variables */ #define SHFUN 21 /* Show functions */ #define SHPAD 22 /* Show (X.25) PAD */ #define SHTER 23 /* Show terminal settings */ #define SHESC 24 /* Show escape character */ #define SHDIA 25 /* Show DIAL parameters */ #define SHNET 26 /* Show network parameters */ #define SHLBL 27 /* Show VMS labeled file parameters */ #define SHSTK 28 /* Show stack, MAC debugging */ #define SHCSE 29 /* Show character sets */ #define SHFEA 30 /* Show features */ #define SHCTL 31 /* Show control-prefix table */ #define SHEXI 32 /* Show EXIT items */ #define SHPRT 33 /* Show printer */ #define SHCMD 34 /* Show command parameters */ #define SHKVB 35 /* Show \Kverbs */ #define SHMOU 36 /* Show Mouse (like Show Key) */ #define SHTAB 37 /* Show Tabs (OS/2) */ #define SHVSCRN 38 /* Show Virtual Screen (OS/2) */ #define SHALRM 39 /* ALARM */ #define SHSFL 40 /* SEND-LIST */ #define SHUDK 41 /* DEC VT UDKs (OS/2) */ #define SHDBL 42 /* DOUBLE/IGNORE characters */ #define SHEDIT 43 /* EDITOR */ #define SHBROWSE 44 /* BROWSER */ #define SHTAPI 45 /* TAPI */ #define SHTAPI_L 46 /* TAPI Location */ #define SHTAPI_M 47 /* TAPI Modem Properties */ #define SHTAPI_C 48 /* TAPI Comm Properties */ #define SHTEL 49 /* SHOW TELNET */ #define SHINP 50 /* SHOW INPUT */ #define SHTRIG 51 /* SHOW TRIGGER */ #define SHLOG 52 /* SHOW LOGS */ #define SHOUTP 53 /* SHOW OUTPUT */ #define SHOPAT 54 /* SHOW PATTERNS */ #define SHOSTR 55 /* SHOW STREAMING */ #define SHOAUTH 56 /* SHOW AUTHENTICATION */ #define SHOFTP 57 /* SHOW FTP */ #define SHTOPT 58 /* SHOW TELOPT */ #define SHXOPT 59 /* SHOW EXTENDED-OPTIONS */ #define SHCD 60 /* SHOW CD */ #define SHASSOC 61 /* SHOW ASSOCIATIONS */ #define SHCONNX 62 /* SHOW CONNECTION */ #define SHOPTS 63 /* SHOW OPTIONS */ #define SHOFLO 64 /* SHOW FLOW-CONTROL */ #define SHOXFER 65 /* SHOW TRANSFER */ #define SHTCP 66 /* SHOW TCP */ #define SHHISTORY 67 /* SHOW (command) HISTORY */ #define SHSEXP 68 /* SHOW SEXPRESSIONS */ #define SHOSSH 69 /* SHOW SSH */ #define SHOIKS 70 /* SHOW IKS */ #define SHOGUI 71 /* SHOW GUI (K95) */ #define SHOREN 72 /* SHOW RENAME */ /* REMOTE command symbols */ #define XZCPY 0 /* Copy */ #define XZCWD 1 /* Change Working Directory */ #define XZDEL 2 /* Delete */ #define XZDIR 3 /* Directory */ #define XZHLP 4 /* Help */ #define XZHOS 5 /* Host */ #define XZKER 6 /* Kermit */ #define XZLGI 7 /* Login */ #define XZLGO 8 /* Logout */ #define XZMAI 9 /* Mail <-- wrong, this should be top-level */ #define XZMOU 10 /* Mount */ #define XZMSG 11 /* Message */ #define XZPRI 12 /* Print */ #define XZREN 13 /* Rename */ #define XZSET 14 /* Set */ #define XZSPA 15 /* Space */ #define XZSUB 16 /* Submit */ #define XZTYP 17 /* Type */ #define XZWHO 18 /* Who */ #define XZPWD 19 /* Print Working Directory */ #define XZQUE 20 /* Query */ #define XZASG 21 /* Assign */ #define XZMKD 22 /* mkdir */ #define XZRMD 23 /* rmdir */ #define XZXIT 24 /* Exit */ #define XZCDU 25 /* CDUP */ /* SET INPUT command parameters */ #define IN_DEF 0 /* Default timeout */ #define IN_TIM 1 /* Timeout action */ #define IN_CAS 2 /* Case (matching) */ #define IN_ECH 3 /* Echo */ #define IN_SIL 4 /* Silence */ #define IN_BUF 5 /* Buffer size */ #define IN_PAC 6 /* Input Pacing (debug) */ #define IN_TRM 7 /* Input Terminal Display */ #define IN_ADL 8 /* Input autodownload */ #define IN_PAT 9 /* Pattern to match */ #define IN_ASG 10 /* Assign matching text to variable */ #define IN_CAN 11 /* Keyboard cancellation of INPUT */ #define IN_SCA 12 /* Timeout scaling */ /* ENABLE/DISABLE command parameters */ #define EN_ALL 0 /* ALL */ #define EN_CWD 1 /* CD/CWD */ #define EN_DIR 2 /* DIRECTORY */ #define EN_FIN 3 /* FINISH */ #define EN_GET 4 /* GET */ #define EN_HOS 5 /* HOST command */ #define EN_KER 6 /* KERMIT command */ #define EN_LOG 7 /* LOGIN */ #define EN_SEN 8 /* SEND */ #define EN_SET 9 /* SET */ #define EN_SPA 10 /* SPACE */ #define EN_TYP 11 /* TYPE */ #define EN_WHO 12 /* WHO, finger */ #define EN_DEL 13 /* Delete */ #define EN_BYE 14 /* BYE (as opposed to FINISH) */ #define EN_QUE 15 /* QUERY */ #define EN_ASG 16 /* ASSIGN */ #define EN_CPY 17 /* COPY */ #define EN_REN 18 /* RENAME */ #define EN_RET 19 /* RETRIEVE */ #define EN_MAI 20 /* MAIL */ #define EN_PRI 21 /* PRINT */ #define EN_MKD 22 /* MKDIR */ #define EN_RMD 23 /* RMDIR */ #define EN_XIT 24 /* EXIT */ #define EN_ENA 25 /* ENABLE */ #endif /* NOICP */ #ifndef NOICP /* CLEAR command symbols */ #define CLR_DEV 1 /* Clear Device Buffers */ #define CLR_INP 2 /* Clear Input Buffers */ #define CLR_BTH CLR_DEV|CLR_INP /* Clear Device and Input */ #define CLR_SCL 4 /* Clear Scrollback buffer */ #define CLR_CMD 8 /* Clear Command Screen */ #define CLR_TRM 16 /* Clear Terminal Screen */ #define CLR_DIA 32 /* Clear Dial Status */ #define CLR_SFL 64 /* Clear Send-File-List */ #define CLR_APC 128 /* Clear APC */ #define CLR_ALR 256 /* Clear Alarm */ #define CLR_TXT 512 /* Clear text-patterns */ #define CLR_BIN 1024 /* Clear binary-patterns */ #define CLR_SCR 2048 /* Clear screen */ #define CLR_KBD 4096 /* Clear keyboard buffer */ #endif /* NOICP */ /* Symbols for logs */ #define LOGD 0 /* Debugging */ #define LOGP 1 /* Packets */ #define LOGS 2 /* Session */ #define LOGT 3 /* Transaction */ #define LOGX 4 /* Screen */ #define LOGR 5 /* The "open read" file */ #define LOGW 6 /* The "open write/append" file */ #define LOGE 7 /* Error (e.g. stderr) */ #define LOGM 8 /* The dialing log */ #ifndef NOSPL /* Symbols for builtin variables */ #define VN_ARGC 0 /* ARGC */ #define VN_COUN 1 /* COUNT */ #define VN_DATE 2 /* DATE */ #define VN_DIRE 3 /* DIRECTORY */ #define VN_ERRO 4 /* ERRORLEVEL */ #define VN_TIME 5 /* TIME */ #define VN_VERS 6 /* VERSION */ #define VN_IBUF 7 /* INPUT buffer */ #define VN_SUCC 8 /* SUCCESS flag */ #define VN_LINE 9 /* LINE */ #define VN_ARGS 10 /* Program command-line arg count */ #define VN_SYST 11 /* System type */ #define VN_SYSV 12 /* System version */ #define VN_RET 13 /* RETURN value */ #define VN_FILE 14 /* Most recent filespec */ #define VN_NDAT 15 /* Numeric date yyyy/mm/dd */ #define VN_HOME 16 /* Home directory */ #define VN_SPEE 17 /* Transmission speed */ #define VN_HOST 18 /* Host name */ #define VN_TTYF 19 /* TTY file descriptor (UNIX only) */ #define VN_PROG 20 /* Program name */ #define VN_NTIM 21 /* NTIME */ #define VN_FFC 22 /* Characters in last file xferred */ #define VN_TFC 23 /* Chars in last file group xferred */ #define VN_CPU 24 /* CPU type */ #define VN_CMDL 25 /* Command level */ #define VN_DAY 26 /* Day of week, string */ #define VN_NDAY 27 /* Day of week, numeric */ #define VN_LCL 28 /* Local (vs) remote mode */ #define VN_CMDS 29 /* Command source */ #define VN_CMDF 30 /* Command file name */ #define VN_MAC 31 /* Macro name */ #define VN_EXIT 32 /* Exit status */ #define VN_ICHR 33 /* INPUT character */ #define VN_ICNT 34 /* INPUT count */ #define VN_PRTY 35 /* Current parity */ #define VN_DIAL 36 /* DIAL status */ #define VN_KEYB 37 /* Keyboard type */ #define VN_CPS 38 /* Chars per second, last transfer */ #define VN_RPL 39 /* Receive packet length */ #define VN_SPL 40 /* Send packet length */ #define VN_MODE 41 /* Transfer mode (text, binary) */ #define VN_REXX 42 /* Rexx return value */ #define VN_NEWL 43 /* Newline character or sequence */ #define VN_COLS 44 /* Columns on console screen */ #define VN_ROWS 45 /* Rows on console screen */ #define VN_TTYP 46 /* Terminal type */ #define VN_MINP 47 /* MINPUT result */ #define VN_CONN 48 /* Connection type */ #define VN_SYSI 49 /* System ID */ #define VN_TZ 50 /* Timezone */ #define VN_SPA 51 /* Space */ #define VN_QUE 52 /* Query */ #define VN_STAR 53 /* Startup directory */ #define VN_CSET 54 /* Local character set */ #define VN_MDM 55 /* Modem type */ #define VN_EVAL 56 /* Most recent EVALUATE result */ #define VN_D_CC 57 /* DIAL COUNTRY-CODE */ #define VN_D_AC 58 /* DIAL AREA-CODE */ #define VN_D_IP 59 /* DIAL INTERNATIONAL-PREFIX */ #define VN_D_LP 60 /* DIAL LD-PREFIX */ #define VN_UID 61 #define VN_PWD 62 #define VN_PRM 63 #define VN_PROTO 64 /* Protocol */ #define VN_DLDIR 65 /* Download directory */ #define VN_M_AAA 66 /* First MODEM one */ #define VN_M_INI 66 /* Modem init string */ #define VN_M_DCM 67 /* Modem dial command */ #define VN_M_DCO 68 /* Modem data compression on */ #define VN_M_DCX 69 /* Modem data compression off */ #define VN_M_ECO 70 /* Modem error correction on */ #define VN_M_ECX 71 /* Modem error correction off */ #define VN_M_AAO 72 /* Modem autoanswer on */ #define VN_M_AAX 73 /* Modem autoanswer off */ #define VN_M_HUP 74 /* Modem hangup command */ #define VN_M_HWF 75 /* Modem hardware flow command */ #define VN_M_SWF 76 /* Modem software flow command */ #define VN_M_NFC 77 /* Modem no flow-control command */ #define VN_M_PDM 78 /* Modem pulse dialing mode */ #define VN_M_TDM 79 /* Modem tone dialing mode */ #define VN_M_ZZZ 79 /* Last MODEM one */ #define VN_SELCT 80 /* Selected Text from Mark Mode */ #define VN_TEMP 81 /* Temporary directory */ #define VN_ISTAT 82 /* INPUT command status */ #define VN_INI 83 /* INI (kermrc) directory */ #define VN_EXEDIR 84 /* EXE directory */ #define VN_ERRNO 85 /* Value of errno */ #define VN_ERSTR 86 /* Corresponding error message */ #define VN_TFLN 87 /* TAKE file line number */ #define VN_XVNUM 88 /* Product-specific version number */ #define VN_RPSIZ 89 /* Receive packet length */ #define VN_WINDO 90 /* Window size */ #define VN_MDMSG 91 /* Modem message */ #define VN_DNUM 92 /* Dial number */ #define VN_APC 93 /* APC active */ #define VN_IPADDR 94 /* My IP address */ #define VN_CRC16 95 /* CRC-16 of most recent file group */ #define VN_TRMK 96 /* Macro executed from Terminal Mode */ #define VN_PID 97 /* Process ID */ #define VN_FNAM 98 /* Name of file being transferred */ #define VN_FNUM 99 /* Number of file being transferred */ #define VN_PEXIT 100 /* Process exit status */ #define VN_P_CTL 101 /* Control Prefix */ #define VN_P_8BIT 102 /* 8-bit prefix */ #define VN_P_RPT 103 /* Repeat count prefix */ #define VN_D_LCP 104 /* DIAL LOCAL-PREFIX */ #define VN_URL 105 /* Last URL selected */ #define VN_REGN 106 /* Registration Name */ #define VN_REGO 107 /* Registration Organization */ #define VN_REGS 108 /* Registration Serial number */ #define VN_XPROG 109 /* xprogram (like xversion) */ #define VN_EDITOR 110 /* Editor */ #define VN_EDOPT 111 /* Editor options */ #define VN_EDFILE 112 /* Editor file */ #define VN_BROWSR 113 /* Browser */ #define VN_BROPT 114 /* Browser options */ #define VN_HERALD 115 /* Program herald */ #define VN_TEST 116 /* Program test level or "0" */ #define VN_XFSTAT 117 /* File-Transfer status */ #define VN_XFMSG 119 /* File-Transfer message */ #define VN_SNDL 120 /* Send-list status */ #define VN_TRIG 121 /* Trigger value */ #define VN_MOU_X 122 /* OS/2 Mouse Cursor X */ #define VN_MOU_Y 123 /* OS/2 Mouse Cursor Y */ #define VN_PRINT 124 /* Printer */ #define VN_ESC 125 /* Escape character */ #define VN_INTIME 126 /* INPUT elapsed time */ #define VN_K4RLM 127 /* Kerberos 4 Realm */ #define VN_K5RLM 128 /* Kerberos 5 Realm */ #define VN_K4PRN 129 /* Kerberos 4 Principal */ #define VN_K5PRN 130 /* Kerberos 5 Principal */ #define VN_K4CC 131 /* Kerberos 4 Credentials Cache */ #define VN_K5CC 132 /* Kerberos 5 Credentials Cache */ #define VN_OSNAM 133 /* OS name */ #define VN_OSVER 134 /* OS version */ #define VN_OSREL 135 /* OS release */ #define VN_NAME 136 /* Name I was called by */ #define VN_MODL 137 /* CPU model */ #define VN_X25LA 138 /* X.25 local address */ #define VN_X25RA 139 /* X.25 remote address */ #define VN_K4SRV 140 /* Kerberos 4 Service Name */ #define VN_K5SRV 141 /* Kerberos 5 Service Name */ #define VN_PDSFX 142 /* PDIAL suffix */ #define VN_DTYPE 143 /* DIAL type */ #define VN_LCKPID 144 /* Lockfile PID (UNIX) */ #define VN_BLK 145 /* Block check */ #define VN_TFTIM 146 /* File transfer elapsed time */ #define VN_D_PXX 147 /* DIAL PBX-EXCHANGE */ #define VN_HWPAR 148 /* Hardware Parity */ #define VN_SERIAL 149 /* SET SERIAL value */ #define VN_LCKDIR 150 /* Lockfile directory (UNIX) */ #define VN_K4ENO 151 /* Kerberos 4 Last Errno */ #define VN_K4EMSG 152 /* Kerberos 4 Last Err Msg */ #define VN_K5ENO 153 /* Kerberos 5 Last Errno */ #define VN_K5EMSG 154 /* Kerberos 5 Last Err Msg */ #define VN_INTMO 155 /* Input timeout */ #define VN_AUTHS 156 /* Authentication State */ #define VN_DM_LP 157 /* Dial Modifier: Long Pause */ #define VN_DM_SP 158 /* Dial Modifier: Short Pause */ #define VN_DM_PD 159 /* Dial Modifier: Pulse Dial */ #define VN_DM_TD 160 /* Dial Modifier: Tone Dial */ #define VN_DM_WA 161 /* Dial Modifier: Wait for Answer */ #define VN_DM_WD 162 /* Dial Modifier: Wait for Dialtone */ #define VN_DM_RC 163 /* Dial Modifier: Return to Command */ /* (more below...) */ #define VN_TY_LN 164 /* TYPE command line number */ #define VN_TY_LC 165 /* TYPE command line count */ #define VN_TY_LM 166 /* TYPE command match count */ #define VN_MACLVL 167 /* \v(maclevel) */ #define VN_XF_BC 168 /* Transfer blockcheck errors */ #define VN_XF_TM 169 /* Transfer timeouts */ #define VN_XF_RX 170 /* Transfer retransmissions */ #define VN_M_NAM 171 /* Modem full name */ #define VN_MS_CD 172 /* Modem signal CD */ #define VN_MS_CTS 173 /* Modem signal CTS */ #define VN_MS_DSR 174 /* Modem signal DSR */ #define VN_MS_DTR 175 /* Modem signal DTR */ #define VN_MS_RI 176 /* Modem signal RI */ #define VN_MS_RTS 177 /* Modem signal RTS */ #define VN_MATCH 178 /* Most recent pattern match */ #define VN_SLMSG 179 /* SET LINE (error) message */ #define VN_TXTDIR 180 /* Kermit text-file directory */ #define VN_MA_PI 181 /* Math constant Pi */ #define VN_MA_E 182 /* Math constant e */ #define VN_MA_PR 183 /* Math precision (digits) */ #define VN_CMDBL 184 /* Command buffer length */ #define VN_AUTHT 185 /* Authentication Type */ #ifdef CKCHANNELIO #define VN_FERR 186 /* FILE error */ #define VN_FMAX 187 /* FILE max */ #define VN_FCOU 188 /* Result of last FILE COUNT */ #endif /* CKCHANNELIO */ #define VN_DRTR 189 /* DIAL retry counter */ #define VN_CXTIME 190 /* Elapsed time in session */ #define VN_BYTE 191 /* Byte order */ #define VN_AUTHN 192 /* Authentication Name */ #define VN_KBCHAR 193 /* kbchar */ #define VN_TTYNAM 194 /* Name of controlling terminal */ #define VN_X509_S 195 /* X.509 Certificate Subject */ #define VN_X509_I 196 /* X.509 Certificate Issuer */ #define VN_PROMPT 197 /* C-Kermit's prompt */ #define VN_BUILD 198 /* Build ID string */ #define VN_SEXP 199 /* Last S-Expression */ #define VN_VSEXP 200 /* Value of last S-Expression */ #define VN_LSEXP 201 /* SEXP depth */ #define VN_FTIME 202 /* Time as floating-poing number */ #define VN_FTP_C 203 /* FTP Reply Code */ #define VN_FTP_M 204 /* FTP Reply Message */ #define VN_FTP_S 205 /* FTP Server type */ #define VN_FTP_H 206 /* FTP Host */ #define VN_FTP_X 207 /* FTP Connected */ #define VN_FTP_L 208 /* FTP Logged in */ #define VN_FTP_G 209 /* FTP GET-PUT-REMOTE setting */ #define VN_SECURE 210 /* Encrypted connection 0 or 1 */ #define VN_DM_HF 211 /* Dial Modifier: Hook Flash */ #define VN_DM_WB 212 /* Dial Modifier: Wait for Bong */ #define VN_CX_STA 213 /* CX_STATUS */ #define VN_FTP_B 214 /* FTP CPL */ #define VN_FTP_D 215 /* FTP DPL */ #define VN_FTP_Z 216 /* FTP SECURITY */ #define VN_HTTP_C 217 /* HTTP Code */ #define VN_HTTP_N 218 /* HTTP Connected */ #define VN_HTTP_H 219 /* HTTP Host */ #define VN_HTTP_M 220 /* HTTP Message */ #define VN_HTTP_S 221 /* HTTP Security */ #define VN_NOW 222 /* Timestamp yyyymmdd hh:mm:ss */ #define VN_HOUR 223 /* Current hour of the day 0-23 */ #define VN_CI_DA 224 /* Caller ID date */ #define VN_CI_TI 225 /* Caller ID time */ #define VN_CI_NA 226 /* Caller ID name */ #define VN_CI_NU 227 /* Caller ID number */ #define VN_CI_ME 228 /* Caller ID message */ #define VN_PERSONAL 229 /* Personal Directory on Windows */ #define VN_APPDATA 230 /* User AppData directory */ #define VN_COMMON 231 /* Common AppData directory */ #define VN_DESKTOP 232 /* User Desktop directory */ #define VN_TNC_SIG 233 /* RFC 2717 Signature */ #ifdef KUI #define VN_GUI_XP 234 /* GUI Window X position */ #define VN_GUI_YP 235 /* GUI Window Y position */ #define VN_GUI_XR 236 /* GUI Window X resolution */ #define VN_GUI_YR 237 /* GUI Window Y resolution */ #define VN_GUI_RUN 238 /* GUI Window Run mode */ #define VN_GUI_FNM 239 /* GUI Window Font Name */ #define VN_GUI_FSZ 240 /* GUI Window Font Size */ #endif /* KUI */ #define VN_LOG_PKT 241 /* Packet Log Filename */ #define VN_LOG_TRA 242 /* Transaction Log Filename */ #define VN_LOG_SES 243 /* Session Log Filename */ #define VN_LOG_DEB 244 /* Debug Log Filename */ #define VN_LOG_CON 245 /* Connection Log Filename */ #define VN_ISCALE 246 /* INPUT scale factor */ #define VN_BITS 247 /* Bits of this build (16, 32, 64) */ #define VN_LASTFIL 248 /* Last input filespec */ #define VN_LASTKWV 249 /* Last \fkeywordvalue() keyword */ #define VN_DMSG 250 /* Msg corresponding to dialstatus */ #define VN_HOSTIP 251 /* IP address of remote host */ #define VN_INPMSG 252 /* Msg corresponding to instatus */ #define VN_VAREVAL 253 /* SET VARIABLE-EVALUATION setting */ #define VN_PREVCMD 254 /* Previous command */ #endif /* NOSPL */ /* INPUT status values */ #define INP_OK 0 /* Succeeded */ #define INP_TO 1 /* Timed out */ #define INP_UI 2 /* User interrupted */ #define INP_IE 3 /* Internal error */ #define INP_IO 4 /* I/O error or connection lost */ #define INP_IKS 5 /* Kermit Server Active */ #define INP_BF 6 /* Buffer full */ /* INPUT switch values */ #define INPSW_NOM 1 /* /NOMATCH */ #define INPSW_CLR 2 /* /CLEAR */ #define INPSW_NOW 4 /* /NOWRAP */ #define INPSW_COU 8 /* /COUNT */ #ifndef NOSPL /* Symbols for builtin functions */ #define FNARGS 6 /* Maximum number of function args */ #define FN_IND 0 /* Index (of string 1 in string 2) */ #define FN_LEN 1 /* Length (of string) */ #define FN_LIT 2 /* Literal (don't expand the string) */ #define FN_LOW 3 /* Lower (convert to lowercase) */ #define FN_MAX 4 /* Max (maximum) */ #define FN_MIN 5 /* Min (minimum) */ #define FN_MOD 6 /* Mod (modulus) */ #define FN_EVA 7 /* Eval (evaluate arith expression) */ #define FN_SUB 8 /* Substr (substring) */ #define FN_UPP 9 /* Upper (convert to uppercase) */ #define FN_REV 10 /* Reverse (a string) */ #define FN_REP 11 /* Repeat (a string) */ #define FN_EXE 12 /* Execute (a macro) */ #define FN_VAL 13 /* Return value (of a macro) */ #define FN_LPA 14 /* LPAD (left pad) */ #define FN_RPA 15 /* RPAD (right pad) */ #define FN_DEF 16 /* Definition of a macro, unexpanded */ #define FN_CON 17 /* Contents of a variable, ditto */ #define FN_FIL 18 /* File list */ #define FN_FC 19 /* File count */ #define FN_CHR 20 /* Character (like BASIC CHR$()) */ #define FN_RIG 21 /* Right (like BASIC RIGHT$()) */ #define FN_COD 22 /* Code value of character */ #define FN_RPL 23 /* Replace */ #define FN_FD 24 /* File date */ #define FN_FS 25 /* File size */ #define FN_RIX 26 /* Rindex (index from right) */ #define FN_VER 27 /* Verify */ #define FN_IPA 28 /* Find and return IP address */ #define FN_CRY 39 /* ... */ #define FN_OOX 40 /* ... */ #define FN_HEX 41 /* Hexify */ #define FN_UNH 42 /* Unhexify */ #define FN_BRK 43 /* Break */ #define FN_SPN 44 /* Span */ #define FN_TRM 45 /* Trim */ #define FN_LTR 46 /* Left-Trim */ #define FN_CAP 47 /* Capitalize */ #define FN_TOD 48 /* Time-of-day-to-secs-since-midnite */ #define FN_SEC 49 /* Secs-since-midnite-to-time-of-day */ #define FN_FFN 50 /* Full file name */ #define FN_CHK 51 /* Checksum of text */ #define FN_CRC 52 /* CRC-16 of text */ #define FN_BSN 53 /* Basename of file */ #define FN_CMD 54 /* Output of a command (cooked) */ #define FN_RAW 55 /* Output of a command (raw) */ #define FN_STX 56 /* Strip from right */ #define FN_STL 57 /* Strip from left */ #define FN_STN 58 /* Strip n chars */ #define FN_SCRN_CX 59 /* Screen Cursor X Pos */ #define FN_SCRN_CY 60 /* Screen Cursor Y Pos */ #define FN_SCRN_STR 61 /* Screen String */ #define FN_2HEX 62 /* Number (not string) to hex */ #define FN_2OCT 63 /* Number (not string) to octal */ #define FN_RFIL 64 /* Recursive file list */ #define FN_DIR 65 /* Directory list */ #define FN_RDIR 66 /* Recursive directory list */ #define FN_DNAM 67 /* Directory part of filename */ #define FN_RAND 68 /* Random number */ #define FN_WORD 69 /* Word extraction */ #define FN_SPLIT 70 /* Split string into words */ #define FN_KRB_TK 71 /* Kerberos tickets */ #define FN_KRB_NX 72 /* Kerberos next ticket */ #define FN_KRB_IV 73 /* Kerberos ticket is valid */ #define FN_KRB_TT 74 /* Kerberos ticket time */ #define FN_ERRMSG 75 /* Error code to message */ #ifndef UNIX #ifndef VMS #undef FN_ERRMSG #endif /* VMS */ #endif /* UNIX */ #define FN_DIM 76 /* Dimension of array */ #define FN_DTIM 77 /* Convert to standard date/time */ #define FN_JDATE 78 /* Regular date to day of year */ #define FN_PNCVT 79 /* Convert phone number for dialing */ #define FN_DATEJ 80 /* Day of year to date */ #define FN_MJD 81 /* Date to modified Julian date */ #define FN_MJD2 82 /* Modified Julian date to date */ #define FN_DAY 83 /* Day of week of given date */ #define FN_NDAY 84 /* Numeric day of week of given date */ #define FN_TIME 85 /* Convert to hh:mm:ss */ #define FN_NTIM 86 /* Convert to seconds since midnite */ #define FN_N2TIM 87 /* Sec since midnite to hh:mm:ss */ #define FN_PERM 88 /* Permissions of file */ #define FN_KRB_FG 89 /* Kerberos Ticket Flags */ #define FN_SEARCH 90 /* Search for pattern in string */ #define FN_RSEARCH 91 /* Ditto, but right to left */ #define FN_XLATE 92 /* Translate string charset */ #define FN_ALOOK 93 /* Array lookup */ #define FN_TLOOK 94 /* Table lookup */ #define FN_TOB64 95 /* Encode into Base64 */ #define FN_FMB64 96 /* Decode from Base64 */ #define FN_ABS 97 /* Absolute value */ #ifdef CKFLOAT #define FN_FPADD 98 /* Floating-point add */ #define FN_FPSUB 99 /* Floating-point substract */ #define FN_FPMUL 100 /* Floating-point multiply */ #define FN_FPDIV 101 /* Floating-point divide */ #define FN_FPEXP 102 /* Floating-point e to the x */ #define FN_FPLN 103 /* Floating-point natural log */ #define FN_FPLOG 104 /* Floating-point base-10 log */ #define FN_FPPOW 105 /* Floating-point raise to power */ #define FN_FPSQR 106 /* Floating-point square root */ #define FN_FPABS 107 /* Floating-point absolute value */ #define FN_FPMOD 108 /* Floating-point modulus */ #define FN_FPMAX 109 /* Floating-point maximum */ #define FN_FPMIN 110 /* Floating-point minimum*/ #define FN_FPINT 111 /* Floating-point to integer */ #define FN_FPROU 112 /* Floating-point round */ #define FN_FPSIN 113 /* FP sine */ #define FN_FPCOS 114 /* FP cosine */ #define FN_FPTAN 115 /* FP tangent */ #endif /* CKFLOAT */ #ifdef CKCHANNELIO #define FN_FSTAT 116 /* File status */ #define FN_FPOS 117 /* File position */ #define FN_FEOF 118 /* File eof */ #define FN_FILNO 119 /* File number / handle */ #define FN_FGCHAR 120 /* File getchar */ #define FN_FGLINE 121 /* File getline */ #define FN_FGBLK 122 /* File getblock */ #define FN_FPCHAR 123 /* File putchar */ #define FN_FPLINE 124 /* File putline */ #define FN_FPBLK 125 /* File putblock */ #define FN_NLINE 126 /* File get current line number */ #define FN_FERMSG 127 /* File error message */ #endif /* CKCHANNELIO */ #define FN_LEF 128 /* Left (= substr starting on left) */ #define FN_AADUMP 129 /* Associative Array Dump */ #define FN_STB 130 /* \fstripb() */ #define FN_PATTERN 131 /* \fpattern() */ #define FN_HEX2N 132 /* \fhexton() */ #define FN_OCT2N 133 /* \foctton() */ #define FN_HEX2IP 134 /* \fhextoip() */ #define FN_IP2HEX 135 /* \fiptohex() */ #define FN_RADIX 136 /* \fradix() */ #define FN_JOIN 137 /* \fjoin() */ #define FN_SUBST 138 /* \fsubstitute() */ #define FN_SEXP 139 /* \fsexpression() */ #define FN_CMDSTK 140 /* \fcmdstack() */ #define FN_TOGMT 141 /* \ftogmt() */ #define FN_CMPDATE 142 /* \fcmpdates() */ #define FN_DIFDATE 143 /* \fdiffdates() */ #ifdef TCPSOCKET #define FN_HSTADD 144 /* \faddr2name() */ #define FN_HSTNAM 145 /* \fname2addr() */ #endif /* TCPSOCKET */ #define FN_DELSEC 146 /* \fdelta2sec() */ #define FN_PC_DU 147 /* Path conversion DOS to Unix */ #define FN_PC_VU 148 /* Path conversion VMS to Unix */ #define FN_PC_UD 149 /* Path conversion Unix to DOS */ #define FN_PC_UV 150 /* Path conversion Unix to VMS */ #define FN_KWVAL 151 /* \fkeywordvalue() */ #define FN_SLEEP 152 /* \fsleep() */ #define FN_MSLEEP 153 /* \fmsleep() */ #define FN_LNAME 154 /* \fLongPathName() (Windows) */ #define FN_SNAME 155 /* \fShortPathName() (Windows) */ #define FN_UNTAB 156 /* \funtabify() */ #define FN_LOPX 157 /* \flopx() */ #define FN_EMAIL 158 /* \femailaddress() */ #define FN_PICTURE 159 /* \fpictureinfo() */ #define FN_PID 160 /* \fpidinfo() */ #define FN_COUNT 161 /* \fcount() */ #define FN_FUNC 162 /* \ffunction() */ #define FN_RECURSE 163 /* \frecurse() */ #define FN_SQUEEZE 164 /* \fsqueeze() */ #define FN_UNPCT 165 /* \fdecodehex() */ #define FN_STRINGT 166 /* \fstringtype() */ #define FN_STRCMP 167 /* \fstrcmp() */ #endif /* NOSPL */ /* Time Units */ #define TU_DAYS 0 #define TU_WEEKS 1 #define TU_MONTHS 2 #define TU_YEARS 3 #ifdef CK_CURSES /* Screen line numbers for fullscreen file-transfer display */ #define CW_BAN 0 /* Curses Window Banner */ #define CW_DIR 2 /* Current directory */ #define CW_LIN 3 /* Communication device */ #define CW_SPD 4 /* Communication speed */ #define CW_PAR 5 /* Parity */ #define CW_TMO 6 #define CW_NAM 7 /* Filename */ #define CW_TYP 8 /* File type */ #define CW_SIZ 9 /* File size */ #define CW_PCD 10 /* Percent done */ #ifndef CK_PCT_BAR #define CW_TR 11 /* Time remaining */ #define CW_CP 12 /* Characters per second */ #define CW_WS 13 /* Window slots */ #define CW_PT 14 /* Packet type */ #define CW_PC 15 /* Packet count */ #define CW_PL 16 /* Packet length */ #define CW_PR 17 /* Packet retry */ #ifdef COMMENT #define CW_PB 17 /* Packet block check */ #endif /* COMMENT */ #else /* CK_PCT_BAR */ #define CW_BAR 11 /* Percent Bar Scale */ #define CW_TR 12 /* Time remaining */ #define CW_CP 13 /* Chars per sec */ #define CW_WS 14 /* Window slots */ #define CW_PT 15 /* Packet type */ #define CW_PC 16 /* Packet count */ #define CW_PL 17 /* Packet length */ #define CW_PR 18 /* Packet retry */ #ifdef COMMENT #define CW_PB 18 /* Packet block check */ #endif /* COMMENT */ #endif /* CK_PCT_BAR */ #define CW_ERR 19 /* Error message */ #define CW_MSG 20 /* Info message */ #define CW_INT 22 /* Instructions */ #define CW_FFC 99 /* File Characters Sent/Received */ #endif /* CK_CURSES */ #ifndef NOICP /* Save Commands */ #define XSKEY 0 /* Key map file */ #define XSCMD 1 /* Command mode */ #define XSTERM 2 /* Terminal mode */ #endif /* NOICP */ #ifndef NODIAL /* Dial routine sort priorities */ #define DN_INTERN 0 #define DN_FREE 1 #define DN_LOCAL 2 #define DN_UNK 3 #define DN_LONG 4 #define DN_INTL 5 #endif /* NODIAL */ #ifdef SSHBUILTIN #define XSSH_OPN 1 #define XSSH_V2 2 #define XSSH_FLP 3 #define XSSH_FRP 4 #define XSSH_ADD 5 #define XSSH_KEY 6 #define XSSH_CLR 7 #define XSSH_AGT 8 #define SSHKT_1R 0 /* SSH KEY TYPE symbols */ #define SSHKT_2R 1 /* must match ssh/key.h values */ #define SSHKT_2D 2 #define SSHKT_SRP 3 #define SSHKD_IN 1 /* SSH KEY DISPLAY /IN-FORMAT */ #define SSHKD_OUT 2 /* SSH KEY DISPLAY /OUT-FORMAT */ #define SKDF_OSSH 1 /* Key display format OpenSSH */ #define SKDF_SSHC 2 /* Key display format SSH.COM */ #define SKDF_IETF 3 /* Key display format IETF */ #define SKDF_FING 4 /* Key display format FINGERPRINT */ #define SSHSW_USR 1 #define SSHSW_VER 2 #define SSHSW_CMD 3 #define SSHSW_X11 4 #define SSHSW_PWD 5 #define SSHSW_SUB 6 #define SSHC_LPF 1 #define SSHC_RPF 2 #define XSSH2_RKE 1 #define SSHF_LCL 1 #define SSHF_RMT 2 #define SSHA_ADD 1 #define SSHA_DEL 2 #define SSHA_LST 3 #define SSHASW_FP 1 #define SSHK_PASS 1 #define SSHK_CREA 2 #define SSHK_DISP 3 #define SSHK_V1 4 #define SSHKC_BI 1 #define SSHKC_PP 2 #define SSHKC_TY 3 #define SSHKC_1R 4 #define SKRM_OPN 1 #endif /* SSHBUILTIN */ #ifdef SFTP_BUILTIN #define SFTP_OPN 1 #define SFTP_CD 2 #define SFTP_CHGRP 3 #define SFTP_CHMOD 4 #define SFTP_CHOWN 5 #define SFTP_DIR 6 #define SFTP_GET 7 #define SFTP_MKDIR 8 #define SFTP_PWD 9 #define SFTP_PUT 10 #define SFTP_REN 11 #define SFTP_RM 12 #define SFTP_RMDIR 13 #define SFTP_LINK 14 #define SFTP_VER 15 #define XY_SFTP_RCS 1 #define XY_SFTP_EOL 2 #endif /* SFTP_BUILTIN */ /* ANSI-C prototypes for user interface functions */ #ifdef UNIX _PROTOTYP( int doputenv, ( char *, char * ) ); #endif /* UNIX */ _PROTOTYP( int chkaes, ( char, int ) ); #ifndef NOICP _PROTOTYP( int matchname, ( char *, int, int ) ); _PROTOTYP( int ck_cls, ( void ) ); _PROTOTYP( int ck_cleol, ( void ) ); _PROTOTYP( int ck_curpos, ( int, int ) ); _PROTOTYP( int cmdsrc, ( void ) ); _PROTOTYP( int parser, ( int ) ); _PROTOTYP( int chkvar, (char *) ); _PROTOTYP( int zzstring, (char *, char **, int *) ); #ifndef NOFRILLS _PROTOTYP( int yystring, (char *, char **) ); #endif /* NOFRILLS */ _PROTOTYP( int getncm, (char *, int) ); _PROTOTYP( int getnct, (char *, int, FILE *, int) ); #endif /* NOICP */ _PROTOTYP( VOID bgchk, (void) ); _PROTOTYP( char * nvlook, (char *) ); _PROTOTYP( int xarray, (char *) ); _PROTOTYP( int arraynam, (char *, int *, int *) ); _PROTOTYP( int arraybounds, (char *, int *, int *) ); _PROTOTYP( int boundspair, (char *, char *, int *, int *, char *) ); _PROTOTYP( int arrayitoa, (int) ); _PROTOTYP( int arrayatoi, (int) ); _PROTOTYP( char * bldlen, (char *, char *) ); _PROTOTYP( int chkarray, (int, int) ); _PROTOTYP( int dclarray, (char, int) ); _PROTOTYP( int pusharray, (int, int) ); _PROTOTYP( int parsevar, (char *, int *, int *) ); _PROTOTYP( int macini, (void) ); _PROTOTYP( VOID initmac, (void) ); _PROTOTYP( int delmac, (char *, int) ); _PROTOTYP( int addmac, (char *, char *) ); _PROTOTYP( int domac, (char *, char *, int) ); _PROTOTYP( int addmmac, (char *, char *[]) ); _PROTOTYP( int dobug, (void) ); _PROTOTYP( int docd, (int) ); _PROTOTYP( int doclslog, (int) ); _PROTOTYP( int docmd, (int) ); _PROTOTYP( int dodir, (int) ); _PROTOTYP( int dodo, (int, char *, int) ); _PROTOTYP( int doenable, (int, int) ); _PROTOTYP( int dogoto, (char *, int) ); _PROTOTYP( int dogta, (int) ); _PROTOTYP( int dohlp, (int) ); _PROTOTYP (int doincr, (int) ); _PROTOTYP( int dohrmt, (int) ); _PROTOTYP( int doif, (int) ); _PROTOTYP( int doinput, (int, char *[], int[], int, int) ); _PROTOTYP( int doreinp, (int, char *, int) ); _PROTOTYP( int dolog, (int) ); _PROTOTYP( int dologin, (char *) ); _PROTOTYP( int doopen, (void) ); _PROTOTYP( int doprm, (int, int) ); _PROTOTYP( int doreturn, (char *) ); _PROTOTYP( int dormt, (int) ); _PROTOTYP( int dosort, (void) ); _PROTOTYP( int dostat, (int) ); _PROTOTYP( int dostop, (void) ); _PROTOTYP( int dotype, (char *, int, int, int, char *, int, char *, int, int, char *, int)); _PROTOTYP( int transmit, (char *, char, int, int, int) ); _PROTOTYP( int xlate, (char *, char *, int, int) ); _PROTOTYP( int litcmd, (char **, char **, int) ); _PROTOTYP( int incvar, (char *, CK_OFF_T, int) ); _PROTOTYP( int ckdial, (char *, int, int, int, int) ); _PROTOTYP( int hmsg, (char *) ); _PROTOTYP( int hmsga, (char * []) ); _PROTOTYP( int mlook, (struct mtab [], char *, int) ); _PROTOTYP( int mxlook, (struct mtab [], char *, int) ); _PROTOTYP( int mxxlook, (struct mtab [], char *, int) ); _PROTOTYP( int prtopt, (int *, char *) ); _PROTOTYP( CHAR rfilop, (char *, char) ); _PROTOTYP( int setcc, (char *, int *) ); _PROTOTYP( int setnum, (int *, int, int, int) ); _PROTOTYP( int seton, (int *) ); _PROTOTYP( int setonaut, (int *) ); _PROTOTYP( VOID shmdmlin, (void) ); _PROTOTYP( VOID initmdm, (int) ); _PROTOTYP( char * showoff, (int) ); _PROTOTYP( char * showooa, (int) ); _PROTOTYP( char * showstring, (char *) ); _PROTOTYP( int pktopn, (char *,int) ); _PROTOTYP( int traopn, (char *,int) ); _PROTOTYP( int sesopn, (char *,int) ); _PROTOTYP( int debopn, (char *,int) ); _PROTOTYP( int diaopn, (char *,int,int) ); _PROTOTYP( int prepop, (void) ); _PROTOTYP( int popclvl, (void) ); _PROTOTYP( int varval, (char *, CK_OFF_T *) ); _PROTOTYP( char * evala, (char *) ); _PROTOTYP( char * evalx, (char *) ); _PROTOTYP( int setalarm, (long) ); _PROTOTYP( int setat, (int) ); _PROTOTYP( int setinp, (void) ); _PROTOTYP( VOID dolognet, (void) ); _PROTOTYP( VOID dologline, (void) ); _PROTOTYP( int setlin, (int, int, int) ); _PROTOTYP( int setmodem, (void) ); _PROTOTYP( int setfil, (int) ); _PROTOTYP( char * homepath, (void) ); #ifdef OS2 _PROTOTYP( int settapi, (void) ) ; #ifdef OS2MOUSE _PROTOTYP( int setmou, (void) ); #endif /* OS2MOUSE */ #endif /* OS2 */ #ifdef LOCUS _PROTOTYP( VOID setlocus, (int,int) ); _PROTOTYP( VOID setautolocus, (int) ); #endif /* LOCUS */ _PROTOTYP( int setbell, (void) ); _PROTOTYP( VOID setcmask, (int)); _PROTOTYP( VOID setautodl, (int,int)); _PROTOTYP( VOID setdebses, (int)); _PROTOTYP( VOID setseslog, (int)); _PROTOTYP( VOID setaprint, (int)); _PROTOTYP( int settrm, (void) ); _PROTOTYP( int settrmtyp, (void) ); _PROTOTYP( int setsr, (int, int) ); _PROTOTYP( int setxmit, (void) ); _PROTOTYP( int dosetkey, (void) ); _PROTOTYP( int dochk, (void) ); _PROTOTYP( int ludial, (char *, int) ); _PROTOTYP( char * getdnum, (int) ); _PROTOTYP( VOID getnetenv, (void) ); _PROTOTYP( int getyesno, (char *, int) ); _PROTOTYP( VOID xwords, (char *, int, char *[], int) ); #ifdef OS2 _PROTOTYP( VOID keynaminit, (void) ); #endif /* OS2 */ _PROTOTYP( int xlookup, (struct keytab[], char *, int, int *) ); _PROTOTYP( char * rlookup, (struct keytab[], int, int) ); _PROTOTYP( int hupok, (int) ); _PROTOTYP( char * zzndate, (void) ); _PROTOTYP( char * zjdate, (char *) ); _PROTOTYP( char * jzdate, (char *) ); _PROTOTYP( char * ckdate, (void) ); _PROTOTYP( char * chk_ac, (int, char[]) ); _PROTOTYP( char * gmdmtyp, (void) ); _PROTOTYP( char * gfmode, (int, int) ); _PROTOTYP( int setdest, (void) ); _PROTOTYP( VOID ndinit, (void) ); _PROTOTYP( int doswitch, (void) ); _PROTOTYP( int dolocal, (void) ); _PROTOTYP( long tod2sec, (char *) ); _PROTOTYP( int lunet, (char *) ); _PROTOTYP( int doxdis, (int) ); _PROTOTYP( int dosave, (int) ); _PROTOTYP( int doxsend, (int) ); _PROTOTYP( int doxget, (int) ); _PROTOTYP( int doxconn, (int) ); _PROTOTYP( int clsconnx, (int) ); _PROTOTYP( VOID ftreset, (void) ); #ifdef CK_KERBEROS _PROTOTYP (int cp_auth, ( void ) ); #endif /* CK_KERBEROS */ _PROTOTYP( long mjd, (char *) ); _PROTOTYP( char * mjd2date, (long) ); _PROTOTYP( char * ckgetpid, (void) ); _PROTOTYP( int dogrep, (void) ); #ifndef NOFTP #ifndef SYSFTP _PROTOTYP( int doxftp, (void) ); _PROTOTYP( int doftphlp, (void) ); _PROTOTYP( int dosetftp, (void) ); _PROTOTYP( int dosetftphlp, (void) ); _PROTOTYP( int shoftp, (int) ); #endif /* SYSFTP */ #endif /* NOFTP */ _PROTOTYP( VOID cmhistory, (void) ); _PROTOTYP( char * getdcset, (void) ); _PROTOTYP( char * ttgtpn, (void) ); #ifndef NOSHOW _PROTOTYP( int doshow, (int) ); _PROTOTYP( int shotcp, (int) ); _PROTOTYP( VOID shopar, (void) ); _PROTOTYP( VOID shofil, (void) ); _PROTOTYP( VOID shoparp, (void) ); _PROTOTYP( int shoatt, (void) ); _PROTOTYP( VOID shover, (void) ); _PROTOTYP( VOID shoctl, (void) ); _PROTOTYP( VOID shodbl, (void) ); #ifndef NOSPL _PROTOTYP( int shomac, (char *, char *) ); _PROTOTYP( int doshift, (int) ); #endif /* NOSPL */ #ifndef NOCSETS _PROTOTYP( VOID shocharset, (void) ); _PROTOTYP( VOID shoparl, (void) ); _PROTOTYP( VOID shotcs, (int, int) ); #endif /* NOCSETS */ #ifndef NOLOCAL _PROTOTYP( VOID shoparc, (void) ); _PROTOTYP( int shomodem, (void) ); #ifndef NODIAL _PROTOTYP( VOID shods, (char *) ); _PROTOTYP( VOID shodial, (void) ); _PROTOTYP( int doshodial, (void) ); #endif /* NODIAL */ #ifndef NONET _PROTOTYP( int shonet, (void) ); _PROTOTYP( int shotopt, (int) ); _PROTOTYP( int shotel, (int) ); #ifdef CK_AUTHENTICATION _PROTOTYP (int sho_auth,( int ) ); #endif /* CK_AUTHENTICATION */ #endif /* NONET */ _PROTOTYP( VOID shomdm, (void) ); #endif /* NOLOCAL */ #ifdef OS2 _PROTOTYP( VOID shokeycode, (int,int) ); #else _PROTOTYP( VOID shokeycode, (int) ); #endif /* OS2 */ _PROTOTYP( VOID showassoc, (void) ); _PROTOTYP( VOID showdiropts, (void) ); _PROTOTYP( VOID showdelopts, (void) ); _PROTOTYP( VOID showtypopts, (void) ); _PROTOTYP( VOID showpurgopts, (void) ); _PROTOTYP( VOID shoflow, (void) ); _PROTOTYP( VOID shoxfer, (void) ); #ifdef ANYSSH _PROTOTYP( VOID shossh, (void) ); #endif /* ANYSSH */ #endif /* NOSHOW */ _PROTOTYP( VOID shostrdef, (CHAR *) ); #ifndef NOSPL _PROTOTYP( int addlocal, (char *) ); #endif /* NOSPL */ _PROTOTYP( int setdelopts, (void) ); #ifdef VMS _PROTOTYP( int cvtdir, (char *, char *, int) ); #endif /* VMS */ #ifdef FNFLOAT _PROTOTYP( VOID initfloat, (void) ); #endif /* FNFLOAT */ #ifdef CKCHANNELIO _PROTOTYP( int dofile, (int) ); #endif /* CKCHANNELIO */ #ifdef CKROOT _PROTOTYP( int dochroot, (void) ); #endif /* CKROOT */ #ifdef NEWFTP _PROTOTYP( int doftpusr, (void) ); _PROTOTYP( int doftpput, (int,int) ); _PROTOTYP( int doftpget, (int,int) ); _PROTOTYP( int doftprmt, (int,int) ); _PROTOTYP( int ftpopen, (char *, char *, int) ); _PROTOTYP( int cmdlinget, (int) ); _PROTOTYP( int cmdlinput, (int) ); _PROTOTYP( int doftparg, (char) ); _PROTOTYP( int doftpacct, (void) ); _PROTOTYP( int doftpsite, (void) ); _PROTOTYP( int dosetftppsv, (void) ); _PROTOTYP( int ftpbye, (void) ); #endif /* NEWFTP */ #ifdef COMMENT /* These prototypes are no longer used */ _PROTOTYP( char * getdws, (int) ); _PROTOTYP( char * getdcs, (int) ); _PROTOTYP( int doget, (int) ); _PROTOTYP( char * arrayval, (int, int) ); #endif /* COMMENT */ #ifdef KUI _PROTOTYP(int BuildFontTable, (struct keytab ** pTable, struct keytab ** pTable2, int * pN)); #endif /* KUI */ _PROTOTYP(int cx_net, (int net, int protocol, char * xhost, char * svc, char * username, char * password, char * command, int param1, int param2, int param3, int cx, int sx, int flag, int gui)); _PROTOTYP(int cx_serial, (char *device, int cx, int sx, int shr, int flag, int gui, int special)); #endif /* CKUUSR_H */ /* End of ckuusr.h */ ckuusx.c0000644000015300000120000105350411576660226011172 0ustar fdcstaff#include "ckcsym.h" /* C K U U S X -- "User Interface" common functions. */ /* Authors: Frank da Cruz , The Kermit Project, Columbia University, New York City Jeffrey E Altman Secure Endpoints Inc., New York City Copyright (C) 1985, 2011, Trustees of Columbia University in the City of New York. All rights reserved. See the C-Kermit COPYING.TXT file or the copyright text in the ckcmai.c module for disclaimer and permissions. */ /* This module contains user interface functions needed by both the interactive user interface and the command-line-only user interface, as well as the screen-control routines (curses and equivalent). */ /* Includes */ #include "ckcdeb.h" #include "ckcasc.h" #include "ckcker.h" #include "ckuusr.h" #include "ckcxla.h" #ifndef NOHTERMCAP #ifdef NOTERMCAP #define NOHTERMCAP #else #ifndef BSD44 #define NOHTERMCAP #else #ifdef __bsdi__ #define NOHTERMCAP #else #ifdef OPENBSD #define NOHTERMCAP #else #ifdef MACOSX #define NOHTERMCAP #endif /* MACOSX */ #endif /* OPENBSD */ #endif /* __bsdi__ */ #endif /* BSD44 */ #endif /* NOTERMCAP */ #endif /* NOHTERMCAP */ #ifndef NOTERMCAP #ifdef BSD44 #ifndef NOHTERMCAP #include #endif /* NOHTERMCAP */ #endif /* BSD44 */ #else /* !BSD44 */ #ifdef linux #include #endif /* linux */ #endif /* NOTERMCAP */ #ifdef OS2 #include _PROTOTYP(char * os2_gethostname, (void)); #define getpid _getpid #endif /* OS2 */ #ifdef BSD44 #include #endif /* BSD44 */ extern xx_strp xxstring; #ifdef OS2 #include "ckcnet.h" #else /* OS2 */ _PROTOTYP( char * ckgetpeer, (VOID)); _PROTOTYP(int getlocalipaddr, (void)); _PROTOTYP(int istncomport, (void)); #ifndef NOCKGETFQHOST _PROTOTYP( char * ckgetfqhostname,(char *)); #endif /* NOCKGETFQHOST */ #ifndef NETCONN /* We should just pull in ckcnet.h here, but it causes a conflict with curses.h. */ #ifdef TCPSOCKET #define NETCONN #else #ifdef SUNX25 #define NETCONN #else #ifdef STRATUSX25 #define NETCONN #else #ifdef IBMX25 #define NETCONN #else #ifdef HPX25 #define NETCONN #else #ifdef DECNET #define NETCONN #else #ifdef NPIPE #define NETCONN #else #ifdef CK_NETBIOS #define NETCONN #ifdef SUPERLAT #define NETCONN #else #endif /* SUPERLAT */ #endif /* TCPSOCKET */ #endif /* SUNX25 */ #endif /* STRATUSX25 */ #endif /* IBMX25 */ #endif /* HPX25 */ #endif /* DECNET */ #endif /* NPIPE */ #endif /* CK_NETBIOS */ #endif /* NETCONN */ #endif /* OS2 */ #ifndef TCPSOCKET #ifdef MULTINET #define TCPSOCKET #endif /* MULTINET */ #ifdef DEC_TCPIP #define TCPSOCKET #endif /* DEC_TCPIP */ #ifdef WINTCP #define TCPSOCKET #endif /* WINTCP */ #ifdef TCPWARE #define TCPSOCKET #endif /* TCPWARE */ #endif /* TCPSOCKET */ #ifdef OS2 #ifdef NT #include #include #include "ckntap.h" #else /* NT */ #define INCL_VIO #include #endif /* NT */ #ifdef COMMENT /* Would you believe */ #undef COMMENT /* defines this ? */ #endif /* COMMENT */ #ifdef CK_NETBIOS #include "ckonbi.h" #endif /* CK_NETBIOS */ #include "ckocon.h" extern ascreen commandscreen; #ifdef KUI #include "ikui.h" #endif /* KUI */ #endif /* OS2 */ #ifdef NT #include "cknwin.h" #endif /* NT */ #ifdef OS2 #include "ckowin.h" #include "ckosyn.h" #endif /* OS2 */ #ifdef CK_TAPI extern int tttapi; extern int tapipass; #endif /* CK_TAPI */ #ifdef CK_KERBEROS #include "ckuath.h" #endif /* CK_KERBEROS */ #ifndef WINTCP #include #endif /* WINTCP */ #ifdef VMS #include #include #include #ifndef OLD_VMS #include /* Not for VAX C 2.3 */ #else #include #endif /* OLD_VMS */ #ifdef WINTCP #include #endif /* WINTCP */ #endif /* VMS */ #ifdef DCLFDOPEN /* fdopen() needs declaring because it's not declared in */ _PROTOTYP( FILE * fdopen, (int, char *) ); #endif /* DCLFDOPEN */ #ifdef DCLPOPEN /* popen() needs declaring because it's not declared in */ _PROTOTYP( FILE * popen, (char *, char *) ); #endif /* DCLPOPEN */ int tt_crd = 0; /* Carriage return display */ int tt_lfd = 0; /* Linefeed display */ int interrupted = 0; /* Interrupted from keyboard flag */ int fxd_inited = 0; /* Fullscreen stuff initialized */ #ifdef DEBUG char debfil[CKMAXPATH+1]; /* Debugging log file name */ #endif /* DEBUG */ #ifdef TLOG char trafil[CKMAXPATH+1]; /* Transaction log file name */ #endif /* TLOG */ char sesfil[CKMAXPATH+1]; /* Session log file name */ #ifdef CKLOGDIAL char diafil[CKMAXPATH+1]; /* Connection log file name */ char cxlogbuf[CXLOGBUFL+1]; /* Connection log record buffer */ int cx_active = 0; /* Connection is active */ extern int dialog; #endif /* CKLOGDIAL */ #ifdef DYNAMIC static char *cmdstr = NULL; /* Place to build generic command */ #else #ifdef pdp11 static char cmdstr[256]; #else static char cmdstr[4096]; #endif /* pdp11 */ #endif /* DYNAMIC */ #ifndef NOMSEND char fspec[CMDBL+4]; /* Filename string for \v(filespec) */ int fspeclen = CMDBL; #else char fspec[CKMAXPATH+4]; int fspeclen = CKMAXPATH; #endif /* NOMSEND */ char * rfspec = NULL; /* Received filespec: local */ char * prfspec = NULL; /* Preliminary rfspec */ char * sfspec = NULL; /* Sent filespec: local */ char * psfspec = NULL; /* Preliminary sfspec */ char * srfspec = NULL; /* Received filespec: remote */ char * psrfspec = NULL; /* Preliminary srfspec */ char * rrfspec = NULL; /* Sent filespec: remote */ char * prrfspec = NULL; /* Preliminary rrfspec */ int success = 1, /* Command success/failure flag */ cmdlvl = 0, /* Command level */ action = 0, /* Action selected on command line */ slogts = 0, /* Session-log timestamps on/off */ slognul = 0, /* Session-log null-terminated lines */ #ifdef UNIX sessft = XYFT_T, /* Session log file type */ #else sessft = XYFT_B, /* (text for UNIX binary for others) */ #endif /* UNIX */ pflag = 1, /* Print prompt */ msgflg = 1; /* Print informational messages */ extern int xaskmore, saveask; /* More-prompting */ #ifdef CK_APC extern int apcactive; #endif /* CK_APC */ /* External variables */ extern int local, quiet, binary, network, what, parity, xitsta, escape, tlevel, bgset, backgrd, xsuspend, cmdint, nettype, seslog, dfloc; extern int cmd_rows, cmd_cols, xcmdsrc; extern char cmdfil[]; #ifdef VMS extern int batch; #endif /* VMS */ #ifdef datageneral /* 2/12/92 ENH */ #include extern int con_reads_mt, conint_ch, conint_avl; #endif /* datageneral */ extern long speed; extern char ttname[], *dftty, *cmarg, **cmlist, *versio, myhost[]; #ifndef NOCSETS extern int fcharset, tcharset, xfrxla; extern struct csinfo fcsinfo[], tcsinfo[]; #endif /* NOCSETS */ #ifdef OS2 extern unsigned char colorcmd; #endif /* OS2 */ #ifdef NOXFER int fdispla = XYFD_N; #else /* NOXFER is not defined */ #ifdef OS2 /* File transfer display type */ int fdispla = XYFD_C; /* Curses (fullscreen) if we have it */ #else #ifdef CK_CURSES int fdispla = XYFD_C; #else int fdispla = XYFD_S; /* Otherwise CRT */ #endif /* CK_CURSES */ #endif /* OS2 */ extern struct ck_p ptab[]; extern int protocol, xfrbel, xfrint; #ifdef STREAMING extern int streaming, streamok; #endif /* STREAMING */ /* Used internally */ #ifdef KUI _PROTOTYP( VOID screeng, (int, char, long, char *) ); #endif /* KUI */ _PROTOTYP( VOID screenc, (int, char, CK_OFF_T, char *) ); #ifdef CK_CURSES #ifndef DYNAMIC static char xtrmbuf[TRMBUFL]; /* tgetent() buffer */ char * trmbuf = xtrmbuf; #else char * trmbuf = NULL; #endif /* DYNAMIC */ _PROTOTYP( static VOID dpyinit, (void) ); _PROTOTYP( static long shocps, (int, CK_OFF_T, CK_OFF_T) ); _PROTOTYP( static CK_OFF_T shoetl, (CK_OFF_T, long, CK_OFF_T, CK_OFF_T) ); #endif /* CK_CURSES */ static int ft_win = 0; /* Fullscreen file transfer display window is active */ /* Variables declared here */ static char * skreason[] = { "", /* 0 */ "Remote file not older", /* SKP_DAT */ "Identical modification times", /* SKP_EQU */ "Type", /* SKP_TYP */ "Size", /* SKP_SIZ */ "Name collision", /* SKP_NAM */ "Exception List", /* SKP_EXL */ "Dot file", /* SKP_DOT */ "Backup file", /* SKP_BKU */ "Recovery not needed", /* SKP_RES */ "Access denied", /* SKP_ACC */ "Not a regular file", /* SKP_NRF */ "Simulated", /* SKP_SIM */ "Simulated - Remote file older", /* SKP_XUP */ "Simulated - No remote file", /* SKP_XNX */ }; static int nskreason = (sizeof(skreason) / sizeof(char *)); char * gskreason(n) int n; { return((n > 0 && n < nskreason) ? skreason[n] : ""); } char pktfil[CKMAXPATH+1]; /* Packet log file name */ #ifndef NOMSEND /* Multiple SEND */ char *msfiles[MSENDMAX]; #endif /* NOMSEND */ #ifdef CK_TIMERS extern long rttdelay; extern int rttflg; #endif /* CK_TIMERS */ extern int rcvtimo; #ifdef CK_RESEND extern int sendmode; extern CK_OFF_T sendstart, rs_len; #endif /* CK_RESEND */ #ifdef CK_PCT_BAR /* File transfer thermometer */ int thermometer = 1; /* ON by default */ #endif /* CK_PCT_BAR */ #ifdef GFTIMER CKFLOAT gtv = -1.0, oldgtv = -1.0; #else #ifndef OS2 static #endif /* OS2 */ long gtv = -1L, oldgtv = -1L; #endif /* GFTIMER */ extern int server, bctu, rptflg, ebqflg, spsiz, urpsiz, wmax, czseen, cxseen, winlo, displa, timint, npad, ebq, bctr, rptq, atcapu, lpcapu, swcapu, wslotn, wslotr, rtimo, mypadn, sq, capas, rpsiz, tsecs, pktlog, lscapu, dest, srvdis, wslots, spackets, spktl, rpktl, retrans, wcur, numerrs, fsecs, whatru, crunched, timeouts, rpackets, fncnv, bye_active, discard, inserver, diractive, cdactive; extern long filcnt, filrej, rptn, filcps, tfcps, cps, peakcps; extern CK_OFF_T ffc, tfc, fsize; long oldcps = 0L; extern CHAR *rdatap, padch, seol, ctlq, mypadc, eol, *epktmsg; extern char *xfrmsg; #ifdef IKSDB FILE * dbfp = NULL; /* File pointer to database file */ int dbenabled = 1; /* Flag for database is enabled */ extern int ikdbopen; /* Flag for database is open */ unsigned long mydbseek = 0L; /* Seek pointer to my record */ int mydbslot = 0; /* My slot number */ unsigned long myflags = 0L; /* My flags */ unsigned long myatype = 0L; /* My authorization type */ unsigned long myamode = 0L; /* My authorization mode */ unsigned long mystate = 0L; /* My state (SEND, RECEIVE, etc) */ unsigned long mypid = 0L; /* My PID */ unsigned long myip = 0L; /* My IP address */ unsigned long peerip = 0L; /* My peer's IP address */ unsigned long dbip = 0L; /* IP address in db record */ unsigned long dbpid = 0L; /* PID in db record */ unsigned long dbflags = 0L; /* Flags field in db record */ unsigned long dblastused = 0L; /* Last in-use record in db */ char dbrec[DB_RECL]; /* Database record buffer */ char * dbdir = NULL; /* Database directory */ char * dbfile = NULL; /* Database file full pathname */ char myhexip[33] = { NUL, NUL }; /* My IP address in hex */ char peerhexip[33] = { NUL, NUL }; /* Client's IP address in hex */ #endif /* IKSDB */ #ifdef GFTIMER extern CKFLOAT fpfsecs, fptsecs, fpxfsecs; #else extern long xfsecs; #endif /* GFTIMER */ #endif /* NOXFER */ #ifdef TCPSOCKET #ifdef NEWFTP extern char * ftp_host, ftp_srvtyp[]; extern int ftp_csx, ftp_csl, ftp_deb; #endif /* NEWFTP */ extern char myipaddr[]; #endif /* TCPSOCKET */ #ifndef NOICP #ifndef NOSPL extern struct mtab *mactab; /* For ON_EXIT macro. */ extern int nmac; #endif /* NOSPL */ #ifdef DCMDBUF extern char *cmdbuf; /* Command buffer */ #else extern char cmdbuf[]; /* Command buffer */ #endif /* DCMDBUF */ extern int cmd_quoting; #endif /* NOICP */ #ifndef NOCCTRAP #ifdef NT #include #else /* NT */ #include #endif /* NT */ #include "ckcsig.h" extern ckjmpbuf cmjbuf; #endif /* NOCCTRAP */ extern int xfiletype, nscanfile; int shoesc(escape) int escape; { extern char * ccntab[]; /* C0 control character name table */ extern int tt_escape; if ((escape > 0 && escape < 32) || (escape == 127)) { printf(" Escape character: Ctrl-%c (ASCII %d, %s): %s\r\n", ctl(escape), escape, (escape == 127 ? "DEL" : ccntab[escape]), tt_escape ? "enabled" : "disabled" ); } else { printf(" Escape character: Code %d",escape); if (escape > 160 && escape < 256) printf(" (%c)",escape); printf(": %s\r\n", tt_escape ? "enabled" : "disabled"); } return(0); } #ifndef NOXFER /* P R E S E T -- Reset global protocol variables */ extern int recursive; #ifdef PATTERNS int patterns = SET_AUTO; /* Whether to use filename patterns */ extern int g_patterns; /* For saving and restoring */ #else int patterns = SET_OFF; #endif /* PATTERNS */ #ifndef NOICP #ifdef CK_LABELED extern int g_lf_opts, lf_opts; #endif /* CK_LABELED */ extern int g_matchdot, g_usepipes, usepipes; extern int g_binary, g_proto, g_displa, g_spath, g_rpath, g_fncnv; extern int g_recursive; extern int g_xfermode, xfermode; extern int g_urpsiz, g_spsizf, g_spsiz; extern int g_spsizr, g_spmax, g_wslotr, g_prefixing, g_fncact; extern int g_fnspath, g_fnrpath, g_skipbup; extern int nolinks; #ifdef CKSYMLINK extern int zgfs_link; #endif /* CKSYMLINK */ #ifndef NOSPL extern int g_pflg, pwflg, g_pcpt, pwcrypt; extern char * g_pswd, pwbuf[]; #endif /* NOSPL */ #endif /* NOICP */ extern int spsizf, spsizr, spmax, prefixing, fncact, fnspath, fnrpath; extern int moving; /* SEND criteria */ extern char sndafter[], sndbefore[], *sndexcept[], *rcvexcept[]; extern CK_OFF_T sndlarger, sndsmaller, calibrate; extern int rmailf, rprintf, skipbup; extern char optbuf[]; #ifdef PIPESEND extern char * g_sfilter, * g_rfilter; extern char * sndfilter, * rcvfilter; #endif /* PIPESEND */ extern char ** sndarray; VOID ftreset() { #ifndef NOICP int i; extern char * filefile; extern int reliable, xreliable, c_save, ss_save, slostart, urclear; extern int oopts, omode, oname, opath, kactive, autopath; extern char * snd_move; /* Directory to move sent files to */ extern char * snd_rename; /* What to rename sent files to */ extern char * rcv_move; extern char * rcv_rename; extern char * g_snd_move; extern char * g_snd_rename; extern char * g_rcv_move; extern char * g_rcv_rename; #ifdef CK_TMPDIR extern int f_tmpdir; extern char savdir[]; #endif /* CK_TMPDIR */ #ifdef CK_SPEED #ifdef COMMENT extern int f_ctlp; extern short s_ctlp[], ctlp[]; #endif /* COMMENT */ #endif /* CK_SPEED */ #ifndef NOCSETS extern int fcs_save, tcs_save; extern int g_xfrxla, xfrxla; #endif /* NOCSETS */ /* Restore / reset per-command file-transfer switches */ makestr(&snd_move,g_snd_move); makestr(&rcv_move,g_rcv_move); makestr(&snd_rename,g_snd_rename); makestr(&rcv_rename,g_rcv_rename); kactive = 0; /* Kermit protocol no longer active */ oopts = -1; /* O-Packet Options */ omode = -1; /* O-Packet Transfer Mode */ oname = -1; /* O-Packet Filename Options */ opath = -1; /* O-Packet Pathname Options */ #ifdef CK_RESEND rs_len = 0L; /* REGET position */ #endif /* CK_RESEND */ #ifdef COMMENT #ifdef CK_SPEED if (f_ctlp) { for (i = 0; i < 256; i++) ctlp[i] = s_ctlp[i]; f_ctlp = 0; } #endif /* CK_SPEED */ #endif /* COMMENT */ #ifdef CK_TMPDIR if (f_tmpdir) { /* If we changed to download dir */ zchdir((char *) savdir); /* Go back where we came from */ f_tmpdir = 0; } #endif /* CK_TMPDIR */ calibrate = 0L; /* Calibration run */ if (xreliable > -1) { reliable = xreliable; debug(F101,"ftreset reliable","",reliable); } urclear = 0; if (autopath) { /* SET RECEIVE PATHNAMES AUTO */ fnrpath = PATH_AUTO; autopath = 0; } if (filefile) { /* File list */ zclose(ZMFILE); makestr(&filefile,NULL); } if (c_save > -1) { /* Block Check Type */ bctr = c_save; c_save = -1; } if (ss_save > -1) { /* Slow Start */ slostart = ss_save; ss_save = -1; } #ifdef CK_LABELED if (g_lf_opts > -1) { lf_opts = g_lf_opts; /* Restore labeled transfer options */ g_lf_opts = -1; } #endif /* CK_LABELED */ #ifndef NOCSETS if (tcs_save > -1) { /* Character sets */ tcharset = tcs_save; tcs_save = -1; } if (fcs_save > -1) { fcharset = fcs_save; fcs_save = -1; } if (g_xfrxla > -1) { xfrxla = g_xfrxla; g_xfrxla = -1; } setxlatype(tcharset,fcharset); /* Translation type */ #endif /* NOCSETS */ #ifdef NETCONN #ifndef NOSPL if (g_pswd) { ckstrncpy(pwbuf,g_pswd,PWBUFL); makestr(&g_pswd,NULL); } if (g_pflg > -1) { pwflg = g_pflg; g_pflg = -1; } if (g_pcpt > -1) { pwcrypt = g_pcpt; g_pcpt = -1; } #endif /* NOSPL */ #endif /* NETCONN */ if (g_binary > -1) { /* File type */ binary = g_binary; g_binary = -1; } if (g_xfermode > -1) { /* Transfer mode */ xfermode = g_xfermode; g_xfermode = -1; } #ifdef PATTERNS if (g_patterns > -1) { /* Filename patterns */ patterns = g_patterns; g_patterns = -1; } #endif /* PATTERNS */ if (g_usepipes > -1) { usepipes = g_usepipes; g_usepipes = -1; } if (g_matchdot > -1) { matchdot = g_matchdot; g_matchdot = -1; } if (g_proto > -1) { /* Protocol */ protocol = g_proto; g_proto = -1; } if (g_urpsiz > -1) { urpsiz = g_urpsiz; debug(F101,"ftreset restoring urpsiz","",urpsiz); g_urpsiz = -1; } if (g_spsizf > -1) { spsizf = g_spsizf; debug(F101,"ftreset restoring spsizf","",spsizf); g_spsizf = -1; } if (g_spsiz > -1) { spsiz = g_spsiz; debug(F101,"ftreset restoring spsiz","",spsiz); g_spsiz = -1; } if (g_spsizr > -1) { spsizr = g_spsizr; debug(F101,"ftreset restoring spsizr","",spsizr); g_spsizr = -1; } if (g_spmax > -1) { spmax = g_spmax; g_spmax = -1; } if (g_wslotr > -1) { wslotr = g_wslotr; g_wslotr = -1; } if (g_prefixing > -1) { prefixing = g_prefixing; g_prefixing = -1; } if (g_fncact > -1) { fncact = g_fncact; g_fncact = -1; } if (g_fncnv > -1) { fncnv = g_fncnv; g_fncnv = -1; } if (g_fnspath > -1) { fnspath = g_fnspath; g_fnspath = -1; } if (g_fnrpath > -1) { fnrpath = g_fnrpath; g_fnrpath = -1; } if (g_skipbup > -1) { skipbup = g_skipbup; g_skipbup = -1; } nolinks = 2; /* /FOLLOWLINKS is never global */ recursive = 0; /* /RECURSIVE can never be global */ xfiletype = -1; if (g_displa > -1) { /* File transfer display */ fdispla = g_displa; g_displa = -1; } if (g_spath > -1) { /* Send pathnames */ fnspath = g_spath; g_spath = -1; } if (g_rpath > -1) { /* Receive pathnames */ fnrpath = g_rpath; g_rpath = -1; } if (g_fncnv > -1) { /* Filename conversion */ fncnv = g_fncnv; g_fncnv = -1; } #ifdef PIPESEND makestr(&sndfilter,g_sfilter); /* Send filter */ makestr(&rcvfilter,g_rfilter); /* Receive filter */ #endif /* PIPESEND */ #ifndef NOFRILLS rmailf = rprintf = 0; /* MAIL and PRINT modifiers for SEND */ optbuf[0] = NUL; /* MAIL and PRINT options */ #endif /* NOFRILLS */ moving = 0; /* Reset delete-after-send indicator */ sndafter[0] = NUL; /* Reset SEND selection switches */ sndbefore[0] = NUL; for (i = 0; i < NSNDEXCEPT; i++) { if (sndexcept[i]) free(sndexcept[i]); sndexcept[i] = NULL; if (rcvexcept[i]) free(rcvexcept[i]); rcvexcept[i] = NULL; } sndlarger = (CK_OFF_T)-1; sndsmaller = (CK_OFF_T)-1; debug(F101,"present sndsmaller","",sndsmaller); #ifdef GFTIMER gtv = -1.0; oldgtv = -1.0; #else gtv = -1L; oldgtv = -1L; #endif /* GFTIMER */ #endif /* NOICP */ } #endif /* NOXFER */ char * ttgtpn() { /* Get typical port name */ /* Ideally this routine would be implemented in each of the cku?io.* modules, but that requires changing the API definition. */ return( #ifdef OS2 #ifdef OS2ONLY "COM1" #else /* OS2ONLY */ "TAPI [ name ] or COM1" #endif /* OS2ONLY */ #else /* OS2 */ #ifdef VMS "TXA0:, TTA0:, or LTA0:" #else /* VMS */ #ifdef SOLARIS "/dev/cua/a" #else /* SOLARIS */ #ifdef HPUX10 "/dev/cua0p0" #else /* HPUX10 */ #ifdef HPUX "/dev/cua00" #else /* HPUX */ #ifdef __FreeBSD__ "/dev/cuaa0" #else /* __FreeBSD__ */ #ifdef __linux__ "/dev/ttyS0" #else /* __linux__ */ #ifdef BSD44 "/dev/tty00" #else /* BSD44 */ #ifdef OSK "/t1" #else /* OSK */ #ifdef QNX "/dev/ser1" #else /* QNX */ #ifdef QNX6 "/dev/ser1" #else /* QNX6 */ #ifdef UNIXWARE "/dev/term/00 or /dev/tty00" #else /* UNIXWARE */ #ifdef CK_SCOV5 "/dev/tty1A" #else /* CK_SCOV5 */ #ifdef CK_SCO32V4 "/dev/tty1A" #else /* CK_SCO32V4 */ #ifdef M_XENIX "/dev/tty1A" #else /* M_XENIX */ #ifdef AIXRS "/dev/tty0" #else /* AIXRS */ #ifdef DGUX "/dev/tty00" #else /* DGUX */ #ifdef datageneral "@con1" #else /* datageneral */ #ifdef IRIX "/dev/ttym0" #else /* IRIX */ #ifdef SUNOS4 "/dev/ttyh0" #else /* SUNOS4 */ #ifdef SV68R3V6 "/dev/scc0" #else /* SV68R3V6 */ #ifdef MOTSV88R4 "/dev/contty00" #else /* MOTSV88R4 */ #ifdef NEXT "/dev/cufa" #else #ifdef OSF "/dev/ttyd1" #else #ifdef SINIX "/dev/ttyc1" #else #ifdef UNIX "/dev/cua, /dev/acu, /dev/tty0, etc" #else /* UNIX */ "(sorry no example available)" #endif /* UNIX */ #endif /* SINIX */ #endif /* OSF */ #endif /* NEXT */ #endif /* MOTSV88R4 */ #endif /* SV68R3V6 */ #endif /* SUNOS4 */ #endif /* IRIX */ #endif /* datageneral */ #endif /* DGUX */ #endif /* AIX */ #endif /* M_XENIX */ #endif /* CK_SCO32V4 */ #endif /* CK_SCOV5 */ #endif /* UNIXWARE */ #endif /* QNX6 */ #endif /* QNX */ #endif /* OSK */ #endif /* BSD44 */ #endif /* __linux__ */ #endif /* __FreeBSD__ */ #endif /* HPUX */ #endif /* HPUX10 */ #endif /* SOLARIS */ #endif /* VMS */ #endif /* OS2 */ ); } /* C K _ E R R S T R -- Return message from most recent system error */ #ifdef CKROOT extern int ckrooterr; #endif /* CKROOT */ char * ck_errstr() { #ifdef USE_STRERROR #ifndef CK_ANSILIBS /* Should have been declared in */ _PROTOTYP( char * strerror, (int) ); #endif /* CK_ANSILIBS */ #ifdef CKROOT if (ckrooterr) return("Off limits"); #endif /* CKROOT */ return(strerror(errno)); #else /* !USE_STRERROR */ #ifdef VMS extern char * ckvmserrstr(unsigned long); #ifdef CKROOT if (ckrooterr) return("Off limits"); #endif /* CKROOT */ return(ckvmserrstr(0L)); #else /* !VMS */ #ifdef BSD44 #ifdef __386BSD__ #ifndef NDSYSERRLIST extern int sys_nerr; extern char *sys_errlist[]; #endif /* NDSYSERRLIST */ #else /* !__386BSD__ */ #ifndef __bsdi__ #ifndef NDSYSERRLIST extern int sys_nerr; extern const char *const sys_errlist[]; #endif /* NDSYSERRLIST */ #endif /* __bsdi__ */ #endif /* __386BSD__ */ #ifdef CKROOT if (ckrooterr) return("Off limits"); else #endif /* CKROOT */ if (errno >= sys_nerr) return("Error number out of range"); else return((char *) sys_errlist[errno]); #else /* !BSD44 */ #ifdef ATTSV #ifndef NDSYSERRLIST extern int sys_nerr; extern char *sys_errlist[]; #endif /* NDSYSERRLIST */ #ifdef CKROOT if (ckrooterr) return("Off limits"); else #endif /* CKROOT */ if (errno >= sys_nerr) return("Error number out of range"); else return((char *) sys_errlist[errno]); #else /* !ATTSV */ #ifdef BSD4 #ifndef NDSYSERRLIST extern int sys_nerr; extern char *sys_errlist[]; #endif /* NDSYSERRLIST */ #ifdef CKROOT if (ckrooterr) return("Off limits"); else #endif /* CKROOT */ if (errno >= sys_nerr) return("Error number out of range"); else return((char *) sys_errlist[errno]); #else #ifdef OS2 #ifndef NDSYSERRLIST extern char *sys_errlist[]; #endif /* NDSYSERRLIST */ #ifdef NT extern int_sys_nerr; #endif /* NT */ char *e; #ifdef CKROOT if (ckrooterr) return("Off limits"); #endif /* CKROOT */ e = (errno > -1 #ifdef NT && errno <= _sys_nerr #endif /* NT */ ) ? #ifdef NT (char *) sys_errlist[errno] #else /* NT */ /* I don't know how to get a CLIB error string in OS/2 */ strerror(errno) #endif /* NT */ : ""; return(e ? e : ""); #else /* OS2 */ return(""); #endif /* OS2 */ #endif /* BSD4 */ #endif /* ATTSV */ #endif /* BSD44 */ #endif /* VMS */ #endif /* USE_STRERROR */ } #ifdef PATTERNS /* Filename pattern recognition lists for automatic text/binary switching. These are somewhat passe after the addition of scanfile() (7.0). But with the addition of FTP [M]GET, they're back in style (8.0). Although, with FTP the lists need to be used in the reverse. With Kermit the list is used to imply the types of the local system. Whereas with FTP, the list must be used to imply the type of the remote system. Therefore, all platforms must now support all of the lists. */ char *txtpatterns[FTPATTERNS+1] = { NULL, NULL }; char *binpatterns[FTPATTERNS+1] = { NULL, NULL }; /* Default pattern lists for each platform... NOTE: In most cases we leave ".hlp", ".ini", and ".scr" alone; although they are traditionally text types, they are binary in Windows. So they are handled by the prevailing SET FILE TYPE, rather than automatically. Similarly for ".dat", ".inf", and so on. Also ".ps" since PostScript files are not always text. ".log" is omitted since logs can be text or binary, except in VMS they are usually text, etc etc. Later (Sep 2003): Add PostScript to binary patterns. */ static char *txtp[SYS_MAX][FTPATTERNS] = { /* UNKNOWN */ { NULL, NULL }, { /* UNIX */ "*.txt","*.c","*.h","*.r","*.w","*.cpp","*.cc","*.ksc","*.bwr","*.upd", "*.html","*.htm","*.mss","*.tex","*.nr","[Mm]akefile", "*.hex", "*.hqx", "*.for","*.f77","*.f","*.F","*.s","*.pas","*.java","*.el","*.lisp","*.sh", "*.m4","*.perl","*.pl","*.pod","*.pm","*.awk","*.sno","*.spt","*.sed", "*.ksc","*.TXT", "*read.me", "*READ.ME", ".*", "*/.*", "*.mem","*.mac", NULL }, { /* WIN32 */ "*.txt","*.ksc","*.htm","*.html","*.bat","*.cmd","*.jav","*.asm", "*.hex", "*.hqx", "*.c", "*.h", "*.cpp", "*.hpp", "*.cxx", "*.cxx", "*.w", "*.java", "*.bwr", "*.upd", "*.mak", "read.me", "*.map", "makefile", "*.mem","*.mac","*.cc","*.pl","*.pod","*.pm","*.m4",NULL }, { /* VMS */ "*.com","*.txt","*.c", "*.for","*.pas","*.rno","*.rnh","*.mar","*.bli", "*.hlp","*.mss","*.doc","*.bwr","*.cld","*.hex","*.bas","*.ini","*.log", "*.mms","*.opt","*.ksc","*.perl","*.pl","*.pod","*.pm","*.sno","*.spt", "*.mem",NULL }, { /* OS2 */ "*.txt","*.ksc","*.htm","*.html","*.bat","*.cmd","*.jav","*.asm", "*.hex", "*.hqx", "*.c", "*.h", "*.cpp", "*.hpp", "*.cxx", "*.cxx", "*.w", "*.java", "*.bwr", "*.upd", "*.mak", "read.me", "*.map", "makefile", NULL }, { /* DOS */ "*.txt","*.ksc","*.htm","*.bat","*.cmd","*.jav","*.asm", "*.hex", "*.hqx", "*.c", "*.h", "*.cpp", "*.hpp", "*.cxx", "*.cxx", "*.w", "*.bwr", "*.upd", "*.mak", "read.me", "*.map", "makefile", NULL }, { /* TOPS-10 */ "*.cmd","*.hlp","*.doc","*.ini","*.txt","*.mac","*.for","*.sai","*.bli", "*.pas","*.sno","*.spt","*.pcl","*.mss","*.rno","*.b36","*.tex","*.pub", "*.req","*.r36","*.mem","*.bwr","*.ccl","*.ctl","*.rnh","*.ksc",NULL }, { /* TOPS-20 */ "*.cmd","*.hlp","*.doc","*.ini","*.txt","*.mac","*.for","*.sai","*.bli", "*.pas","*.sno","*.spt","*.pcl","*.mss","*.rno","*.b36","*.tex","*.pub", "*.req","*.r36","*.mem","*.bwr","*.ccl","*.ctl","*.rnh","*.ksc",NULL }, { /* STRATUS VOS */ "*.txt","*.ksc","*.htm","*.html","*.bat", "*.cmd","*.jav","*.asm","*.hex", "*.hqx","*.c", "*.h", "*.w", "*.java","*.bwr","*.upd","*.ttp","*.cm", "*.pl1","*.emacs", "read.me", "*.pl", "makefile", NULL }, { /* DG AOS/VS */ "*.txt", "*.c", "*.h", "*.w", "*.er", "*.bwr", "*.upd", "read.me", "*.cli", "*.ksc", NULL }, { /* OSK */ "*.c","*.cpp","*.h","*.a","*akefile", /* program sources */ "*.for","*.f77","*.f","*.F","*.s","*.pas","*.java","*.el","*.lisp", "*.sh","*.perl","*.awk","*.sno","*.spt","*.sed", "*.txt","*.w", /* general text */ "*.ksc","*.bwr","*.upd", "*.html","*.htm","*.mss","*.tex","*.nr","*.hex", "*.hqx", "*.TXT", "*read.me", "*READ.ME", ".*", "*/.*", NULL } }; /* Note: .DOC added to (some) binary patterns June 1998... Microsoft wins. */ static char *binp[SYS_MAX][FTPATTERNS] = { { /* UNKNOWN */ NULL, NULL }, { /* UNIX */ "*.gz","*.Z","*.tgz","*.gif", "*.tar","*.zip","*.o","*.so","*.a","*.out", "*.exe", "*.jpg", "*.jpeg", "*.tif","*.tiff", "*.pdf", "*.so.*", "*.class", "*.rpm", "*.bmp", "*.bz2", "*.BMP", "*.dll", "*.doc", "*.vxd", "*.dcx", "*.xl*", "*.lzh", "*.lhz", "*.au", "*.voc", "*.mpg", "*.mpeg","[wk]ermit", "*.ps", NULL }, { /* WIN32 */ "*.exe", "*.zip", "*.obj", "*.com", "*.gif", "*.jpg", "*.wav", "*.ram", "*.class","*.cla","*.dll", "*.drv", "*.ocx", "*.vbx", "*.lib", "*.ico", "*.bmp", "*.tif", "*.tar", "*.gz", "*.tgz", "*.xl*", "*.doc", "*.vxd", "*.pdf", "*.lzh", "*.vxd", "*.snd", "*.au", "* .voc", "*.mpg", "*.mpeg", "*.ps", NULL }, { /* VMS */ "*.exe","*.obj","*.bak","*.bin","*.adf","*.stb","*.mai","*.sys","*.dmp", "*.ps", "*.dat","*.par", NULL }, { /* OS2 */ "*.exe", "*.zip", "*.obj", "*.com", "*.gif", "*.jpg", "*.wav", "*.ram", "*.class", "*.cla", "*.dll", "*.drv", "*.ocx", "*.vbx", "*.lib", "*.ico", "*.bmp", "*.tif", "*.tar", "*.gz", "*.tgz", "*.xl*", "*.doc", "*.vxd", "*.pdf", "*.ps", "*.lzh", NULL }, { /* DOS */ "*.exe", "*.zip", "*.obj", "*.com", "*.gif", "*.jpg", "*.wav", "*.ram", "*.cla", "*.dll", "*.drv", "*.ocx", "*.vbx", "*.lib", "*.ico", "*.bmp", "*.tif", "*.tar", "*.gz", "*.tgz", "*.xl*", "*.doc", "*.vxd", "*.pdf", "*.ps", "*.lzh", NULL }, { /* TOPS10 */ "*.exe","*.sav","*.bin","*.rim","*.rel","*.unv","*.lib","*.tap","*.dvi", "*.ps", NULL }, { /* TOPS20 */ "*.exe","*.sav","*.bin","*.rim","*.rel","*.unv","*.lib","*.tap","*.dvi", "*.ps", NULL }, { /* STRATUS VOS */ "*.exe", "*.zip", "*.obj", "*.com", "*.gif", "*.jpg", "*.wav", "*.ram", "*.class", "*.cla", "*.dll", "*.drv", "*.ocx", "*.vbx", "*.lib", "*.ico", "*.bmp", "*.tif", "*.tar", "*.gz", "*.tgz", "*.xl*", "*.doc", "*.vxd", "*.pdf", "*.ps", "*.lzh", "*.pm", NULL }, { /* DG */ "*.ob", "*.pr", "*.dmp", "*.ps", NULL }, { /* OSK */ "*.gz","*.Z","*.z","*.tgz","*.lhz","*.tar", /* archivers */ "*.zip","*.ar","*.zoo","*.rpm","*.lzh", /* object files, libraries, executables */ "*.r","*.l","*.exe", "*.dll", "*.so.*", "*.class", /* images */ "*.gif", "*.jpg", "*.jpeg", "*.tif","*.tiff", "*.pdf", "*.ps", "*.bmp", "*.bz2", "*.BMP","*.pcx", NULL } }; /* Set up default pattern lists so they can be freed and re-malloc'd. Each pattern list must terminated by a null element. */ VOID initpat() { int i; for (i = 0; i < FTPATTERNS; i++) { txtpatterns[i] = NULL; binpatterns[i] = NULL; } for (i = 0; i < FTPATTERNS; i++) { #ifdef UNIX makestr(&(txtpatterns[i]),txtp[SYS_UNIX][i]); #else /* UNIX */ #ifdef OS2 #ifdef NT makestr(&(txtpatterns[i]),txtp[SYS_WIN32][i]); #else /* NT */ makestr(&(txtpatterns[i]),txtp[SYS_OS2][i]); #endif /* NT */ #else /* OS2 */ #ifdef VMS makestr(&(txtpatterns[i]),txtp[SYS_VMS][i]); #else /* VMS */ #ifdef STRATUS makestr(&(txtpatterns[i]),txtp[SYS_VOS][i]); #else /* STRATUS */ #ifdef datageneral makestr(&(txtpatterns[i]),txtp[SYS_DG][i]); #else /* datageneral */ #ifdef OSK makestr(&(txtpatterns[i]),txtp[SYS_OSK][i]); #else /* OSK */ makestr(&(txtpatterns[i]),txtp[SYS_UNK][i]); #endif /* OSK */ #endif /* datageneral */ #endif /* STRATUS */ #endif /* VMS */ #endif /* OS2 */ #endif /* UNIX */ if (!txtp[i]) break; } for (i = 0; i < FTPATTERNS; i++) { #ifdef UNIX makestr(&(binpatterns[i]),binp[SYS_UNIX][i]); #else /* UNIX */ #ifdef OS2 #ifdef NT makestr(&(binpatterns[i]),binp[SYS_WIN32][i]); #else /* NT */ makestr(&(binpatterns[i]),binp[SYS_OS2][i]); #endif /* NT */ #else /* OS2 */ #ifdef VMS makestr(&(binpatterns[i]),binp[SYS_VMS][i]); #else /* VMS */ #ifdef STRATUS makestr(&(binpatterns[i]),binp[SYS_VOS][i]); #else /* STRATUS */ #ifdef datageneral makestr(&(binpatterns[i]),binp[SYS_DG][i]); #else /* datageneral */ #ifdef OSK makestr(&(binpatterns[i]),binp[SYS_OSK][i]); #else /* OSK */ makestr(&(binpatterns[i]),binp[SYS_UNK][i]); #endif /* OSK */ #endif /* datageneral */ #endif /* STRATUS */ #endif /* VMS */ #endif /* OS2 */ #endif /* UNIX */ if (!binp[i]) break; } } /* m a t c h n a m e -- Compare filename with text & binary name patterns. Returns: 0 if name matches a text pattern but not a binary pattern. 1 if name matches a binary pattern but not a text pattern. -1 if name matches no patterns. -2 if name matches a binary pattern and a text pattern. */ int matchname(filename, local, os) char * filename; int local; int os; { int rc = -1; /* Return code */ char * name, * p; #ifdef OS2ORUNIX char tmpbuf[CKMAXPATH+1]; #endif /* OS2ORUNIX */ name = filename ? filename : ""; /* Copy of original arg */ if (patterns && *name) { /* If PATTERNS ON... */ int i; #ifdef OS2ORUNIX if (ckmatch("*.~[1-9]*~",name,1,1)) { /* Name has backup suffix? */ int k; k = ckstrncpy(tmpbuf,name,CKMAXPATH+1); /* Yes, copy and strip */ for (i = k - 3; i > 4; i--) { if (tmpbuf[i] == '~' && tmpbuf[i-1] == '.') { tmpbuf[i-1] = NUL; break; } } name = tmpbuf; /* And point to stripped copy */ } #endif /* OS2ORUNIX */ zstrip(name,&p); /* Strip pathname too */ name = p; if (local) { if (txtpatterns[0]) { /* Search text patterns */ for (i = 0; i < FTPATTERNS && txtpatterns[i]; i++) { if (ckmatch(txtpatterns[i],name,filecase,1)) { rc = 0; break; } } } if (binpatterns[0]) { /* And search binary patterns */ for (i = 0; i < FTPATTERNS && binpatterns[i]; i++) { if (ckmatch(binpatterns[i],name,filecase,1)) { rc = (rc > -1) ? -2 : 1; break; } } } } else { if (os >= 0 && os < SYS_MAX) { if (txtp[os][0]) { for (i = 0; i < FTPATTERNS && txtp[os][i]; i++) { if (ckmatch(txtp[os][i],name,filecase,1)) { rc = 0; break; } } } if (binp[os][0]) { for (i = 0; i < FTPATTERNS && binp[os][i]; i++) { if (ckmatch(binp[os][i],name,filecase,1)) { rc = (rc > -1) ? -2 : 1; break; } } } } } } debug(F111,"matchname",name,rc); return(rc); } #endif /* PATTERNS */ #ifdef UNICODE #ifndef NOEVENMAX #define EVENMAX #endif /* NOEVENMAX */ #endif /* UNICODE */ /* S C A N F I L E -- Analyze a file's contents */ /* Call with: name: Pointer to name of existing file. flag: Pointer to int in which to return additional numeric data. Returns: -1 on failure (to open file or to read from it). Integer, 0..4, on success indicating file type: 0 = 7-bit text (flag = -1) 1 = UTF-8 text (flag = -1) 2 = UCS-2 text (flag = 0: big-endian; flag = 1: little-endian) 3 = 8-bit text (flag = 0: no C1 bytes; flag = 1: includes C1 bytes) 4 = Text (type unknown) 5 = binary (flag = -1) If UNICODE is defined: 1. If file begins with a valid BOM, it is believed. Otherwise we read the first 4K of the file (since it might be email with verbose headers) and analyze it: 2. If file contains only valid UTF-8 sequences, we call it UTF-8; otherwise: 3. If the file contains lots of alternate 0 bytes, we call it UCS-2, and set the polarity according to whether the preponderance of them are in even or odd positions; otherwise: 4. If EVENMAX is defined and the file contains lots of alternate bytes that are identical, even if they aren't zero, and the number of such bytes is at least four times the length of the maximum run of alternating identical bytes of the opposite polarity, we call it UCS-2; otherwise: 5. If the file contained no bytes with their 8th bits on and no controls other than CR, LF, HT, and FF, we call it ASCII; otherwise: 6. If it contains C0 control characters other than CR, LF, HT, and FF, we call it binary; otherwise: 7. We call it 8-bit text, character set unknown (could be Latin-1 or anything else). Note that malformed UTF-8 is not diagnosed as UTF-8. If UNICODE is not defined: 1. If the file contains C0 control characters other than CR, LF, HT, and FF, we call it binary; otherwise: 2. If the file contains any 8-bit bytes, we call it 8-bit text; otherwise: 3. We call it 7-bit text. In the non-Unicode case, UCS-2 is diagnosed as binary, but UTF-8 as 8-bit text. There is no significant speed difference between the Unicode and non-Unicode cases. */ int scanfile(name,flag,nscanfile) char * name; int * flag, nscanfile; { FILE * fp; /* File pointer */ unsigned char buf[SCANFILEBUF]; /* File data buffer for analysis */ int x, val = -1, count = 0; /* Workers */ int rc = -1; /* Return code */ int pv = -1; /* Pattern-match value */ int eof = 0; /* Flag for file EOF encountered */ int bytes = 0; /* Total byte count */ #ifdef UNICODE unsigned int c0, c1; /* First 2 file bytes (for BOM) */ #endif /* UNICODE */ extern int pipesend, filepeek; register int i; /* Loop control */ int readsize = 0; /* How much to read */ int eightbit = 0; /* Number of bytes with 8th bit on */ int c0controls = 0; /* C0 non-text control-char counter */ int c0noniso = 0; /* C0 non-ISO control-char counter */ int c1controls = 0; /* C1 control-character counter */ unsigned int c; /* Current character */ int runmax = 0; /* Longest run of 0 bytes */ int runzero = 0; /* Run of 0 bytes */ int pctzero = 0; /* Percentage of 0 bytes */ int txtcz = 0; #ifdef CK_CTRLZ extern int eofmethod; #endif /* CK_CTRLZ */ #ifdef UNICODE int notutf8 = 0; /* Nonzero if definitely not UTF-8 */ int utf8state = 0; /* UTF-8 recognizer state */ int oddzero = 0; /* Number of 0 bytes in odd postions */ int evenzero = 0; /* and in even positions */ int lfnul = 0; /* Number of sequences */ int crlf = 0; /* Number of sequences */ #else int notutf8 = 1; #endif /* UNICODE */ #ifdef COMMENT #ifdef EVENMAX int oddrun = 0, oddmax = 0, oddbyte = 0, oddmaxbyte = 0; int evenrun = 0, evenmax = 0, evenbyte = 0, evenmaxbyte = 0; #endif /* EVENMAX */ #endif /* COMMENT */ #ifndef NOXFER if (pipesend || calibrate || sndarray) /* Only for real files */ return(-1); #endif /* NOXFER */ debug(F111,"scanfile",name,nscanfile); #ifdef PATTERNS if (!filepeek) { pv = matchname(name,1,-1); if (pv < 0) rc = -1; else rc = (pv == 1) ? FT_BIN : FT_TEXT; debug(F111,"scanfile !filepeek result",name,rc); return(rc); } #endif /* PATTERNS */ #ifdef VMS /* We don't scan in VMS where text files have various record formats in */ /* which record headers contain seemingly non-text bytes. So the best */ /* we can do in VMS is tell whether the file is text or binary, period. */ { int b, x; b = binary; /* Save current binary setting */ if (zopeni(ZIFILE,name) > 0) { /* In VMS this sets binary */ x = binary; /* Get result */ zclose(ZIFILE); /* Close the file */ binary = b; /* Restore previous binary setting */ rc = x ? FT_BIN : FT_TEXT; val = 0; goto xscanfile; } } #endif /* VMS */ eof = 0; /* End-of-file reached indicator */ #ifdef OS2 fp = fopen(name, "rb"); /* Open the file in binary mode */ #else fp = fopen(name, "r"); #endif /* OS2 */ if (!fp) /* Failed? */ return(-1); while (1) { /* One or more gulps from file */ if (eof) { /* EOF from last time? */ debug(F111,"scanfile at EOF",name,bytes); if (runzero > runmax) runmax = runzero; break; } if (nscanfile < 0) { /* Reading whole file */ readsize = SCANFILEBUF; } else { /* Reading first nscanfilee bytes */ readsize = nscanfile - bytes; if (readsize < 1) break; if (readsize > SCANFILEBUF) readsize = SCANFILEBUF; } debug(F101,"scanfile readsize","",readsize); count = fread(buf,1,readsize,fp); /* Read a buffer */ if (count == EOF || count == 0) { debug(F111,"scanfile EOF",name,count); break; } debug(F111,"scanfile buffer ok",name,count); if (bytes == 0 && count > 8) { /* PDF files can look like text in the beginning. */ if (!ckstrcmp((char *)buf,"%PDF-1.",7,1)) { if (isdigit(buf[7])) { if (buf[8] == '\015' || count > 9 && buf[8] == SP && buf[9] == '\015') { #ifdef DEBUG buf[8] = NUL; debug(F110,"scanfile PDF",buf,0); #endif /* DEBUG */ binary = 1; /* But they are binary. */ break; } } } else if (!ckstrcmp((char *)buf,"%!PS-Ado",8,1)) { /* Ditto for PostScript */ #ifdef DEBUG int i; for (i = 8; i < count; i++) { if (buf[i] < '!') { buf[i] = NUL; break; } } debug(F110,"scanfile PostScript",buf,0); #endif /* DEBUG */ binary = 1; break; #ifndef NOPCLSCAN } else if (!ckstrcmp((char *)buf,") HP-PCL",8,1)) { /* HP PCL printer language */ #ifdef DEBUG int i; for (i = 8; i < count; i++) { if (buf[i] < '!') { buf[i] = NUL; break; } } debug(F110,"scanfile PCL",buf,0); #endif /* DEBUG */ binary = 1; break; } #endif /* NOPCLSCAN */ #ifndef NOPJLSCAN else if (buf[0] == '\033' && (buf[1] == 'E' || buf[1] == '%')) { /* Ditto for PJL Job printer header */ #ifdef DEBUG int i; for (i = 2; i < count; i++) { if (buf[i] < '!') { buf[i] = NUL; break; } } debug(F110,"scanfile PJL Job printer header",buf,0); #endif /* DEBUG */ binary = 1; break; #endif /* NOPJLSCAN */ } } #ifdef UNICODE if (bytes == 0 && count > 1) { int incl_cnt = 0; /* First look for BOM */ c0 = (unsigned)((unsigned)buf[0]&0xFF); /* First file byte */ c1 = (unsigned)((unsigned)buf[1]&0xFF); /* Second byte */ if (c0 == 0xFE && c1 == 0xFF) { /* UCS-2 BE */ rc = FT_UCS2; val = 0; debug(F111,"scanfile UCS2 BOM BE",ckitoa(val),rc); incl_cnt++; } else if (c0 == 0xFF && c1 == 0xFE) { /* UCS-2 LE */ rc = FT_UCS2; val = 1; debug(F111,"scanfile UCS2 BOM LE",ckitoa(val),rc); incl_cnt++; } else if (count > 2) if (c0 == 0xEF && c1 == 0xBB && (unsigned)((unsigned)buf[2]&0xFF) == 0xBF) { rc = FT_UTF8; debug(F111,"scanfile UTF8 BOM",ckitoa(val),rc); incl_cnt++; } if (incl_cnt) { /* Have BOM */ bytes += count; goto xscanfile; } } #endif /* UNICODE */ bytes += count; /* Count bytes read */ eof = feof(fp); /* Flag for at EOF */ for (i = 0; i < count; i++) { /* For each byte... */ c = (unsigned)buf[i]; /* For ease of reference */ if (!c) { /* Zero byte? */ #ifdef EVENMAX if (i&1) /* In odd position */ oddzero++; else evenzero++; /* In even position */ #endif /* EVENMAX */ runzero++; } else { /* Not a zero byte */ if (runzero > runmax) runmax = runzero; if (runmax > 2) /* That's all we need to be certain */ break; /* it's a binary file. */ runzero = 0; } #ifdef COMMENT #ifdef EVENMAX /* This is to catch UCS-2 with a non-ASCII, non-Latin-1 repertoire */ if (i > 1) { /* Look for runs of alternating chars */ if (i&1) { if (c == buf[i-2]) { /* In odd positions */ oddrun++; oddbyte = c; } else { oddmax = oddrun; oddmaxbyte = oddbyte; } } else { /* and even positions */ if (c == buf[i-2]) { evenrun++; evenbyte = c; } else { evenmax = evenrun; evenmaxbyte = evenbyte; } } } #endif /* EVENMAX */ #endif /* COMMENT */ if ((c & 0x80) == 0) { /* We have a 7-bit byte */ #ifdef UNICODE if (i > 0 && c == 10) { /* Linefeed */ if (buf[i-1] == 0) lfnul++; /* Preceded by NUL */ else if (buf[i-1] == 13) crlf++; /* or by CR... */ } #endif /* UNICODE */ if (c < ' ') { /* Check for CO controls */ if (c != LF && c != CR && c != HT && c != FF) { c0controls++; if (c != ESC && c != SO && c != SI) c0noniso++; } if ((c == '\032') /* Ctrl-Z */ #ifdef COMMENT && eof && (i >= count - 2) #endif /* COMMENT */ ) { c0controls--; c0noniso--; #ifdef CK_CTRLZ if (eofmethod == XYEOF_Z && txtcz == 0) { if (c0controls == 0) /* All text prior to Ctrl-Z */ txtcz = 1; } #endif /* CK_CTRLZ */ } } #ifdef UNICODE if (!notutf8 && utf8state) { /* In UTF-8 sequence? */ utf8state = 0; debug(F000,"scanfile","7-bit byte in UTF8 sequence",c); notutf8++; /* Then it's not UTF-8 */ continue; } #endif /* UNICODE */ } else { /* We have an 8-bit byte */ eightbit++; /* Count it */ if (c >= 0x80 && c < 0xA0) /* Check for C1 controls */ c1controls++; #ifdef UNICODE if (!notutf8) { /* If it might still be UTF8... */ switch (utf8state) { /* Enter the UTF-8 state machine */ case 0: /* First byte... */ if ((c & 0xE0) == 0xC0) { /* Tells number of */ utf8state = 1; /* subsequent bytes */ } else if ((c & 0xF0) == 0xE0) { utf8state = 2; } else if ((c & 0xF8) == 0xF0) { utf8state = 3; } else { notutf8++; } break; case 1: /* Subsequent byte */ case 2: case 3: if ((c & 0xC0) != 0x80) { /* Must start with 10 */ debug(F000,"scanfile", "bad byte in UTF8 sequence",c); notutf8++; break; } utf8state--; /* Good, one less in this sequence */ break; default: /* Shouldn't happen */ debug(F111,"scanfile","bad UTF8 state",utf8state); notutf8++; } } #endif /* UNICODE */ } } } fclose(fp); /* Close the file */ debug(F101,"scanfile bytes","",bytes); if (bytes == 0) /* If nothing was read */ return(-1); /* we're done. */ #ifdef EVENMAX /* In case we had a run that never broke... */ #ifdef COMMENT if (oddmax == 0) { oddmax = oddrun; oddmaxbyte = oddbyte; } if (evenmax == 0) { evenmax = evenrun; evenmaxbyte = evenbyte; } #endif /* COMMENT */ if (runmax == 0) { runmax = runzero; } #endif /* EVENMAX */ #ifdef UNICODE if (bytes > 100) /* Bytes is not 0 */ pctzero = (evenzero + oddzero) / (bytes / 100); else pctzero = ((evenzero + oddzero) * 100) / bytes; #endif /* UNICODE */ #ifdef DEBUG if (deblog) { /* If debugging, dump statistics */ debug(F101,"scanfile c0controls ","",c0controls); debug(F101,"scanfile c0noniso ","",c0noniso); debug(F101,"scanfile c1controls ","",c1controls); debug(F101,"scanfile eightbit ","",eightbit); #ifdef UNICODE debug(F101,"scanfile crlf ","",crlf); debug(F101,"scanfile lfnul ","",lfnul); debug(F101,"scanfile notutf8 ","",notutf8); debug(F101,"scanfile evenzero ","",evenzero); debug(F101,"scanfile oddzero ","",oddzero); debug(F101,"scanfile even/odd ","",(evenzero / (oddzero + 1))); debug(F101,"scanfile odd/even ","",(oddzero / (evenzero + 1))); debug(F101,"scanfile pctzero ","",pctzero); #endif /* UNICODE */ #ifdef COMMENT #ifdef EVENMAX debug(F101,"scanfile oddmax ","",oddmax); debug(F101,"scanfile oddmaxbyte ","",oddmaxbyte); debug(F101,"scanfile evenmax ","",evenmax); debug(F101,"scanfile evenmaxbyte","",evenmaxbyte); #endif /* EVENMAX */ #endif /* COMMENT */ debug(F101,"scanfile runmax ","",runmax); } #endif /* DEBUG */ #ifdef UNICODE x = eightbit ? bytes / 20 : bytes / 4; /* For UCS-2... */ if (runmax > 2) { /* File has run of more than 2 NULs */ debug(F100,"scanfile BIN runmax","",0); rc = FT_BIN; /* so it can't be any kind of text. */ goto xscanfile; } else if (rc == FT_UCS2 || (rc == FT_UTF8 && runmax == 0)) { goto xscanfile; /* File starts with a BOM */ } else if (eightbit > 0 && !notutf8) { /* File has 8-bit data */ if (runmax > 0) { /* and runs of NULs */ debug(F100,"scanfile BIN (nnUTF8) runmax","",0); rc = FT_BIN; /* UTF-8 doesn't have NULs */ } else { /* No NULs */ debug(F100,"scanfile UTF8 (nnUTF8 + runmax == 0)","",0); rc = FT_UTF8; /* and not not UTF-8, so is UTF-8 */ } goto xscanfile; } /* For UCS-2 detection, see if the text contains lines delimited by ASCII controls and containing spaces, ASCII digits, or other ASCII characters, thus forcing the presence of a certain percentage of zero bytes. For this purpose require 20% zero bytes, with at least six times as many in even (odd) positions as in odd (even) positions. */ if ((evenzero >= x && oddzero == 0) || ((((evenzero / (oddzero + 1)) > 6) && (pctzero > 20)) && (crlf == 0) && (lfnul > 1)) ) { debug(F100,"scanfile UCS2 noBOM BE (even/oddzero)","",0); rc = FT_UCS2; val = 0; } else if ((evenzero == 0 && oddzero >= x) || ((((oddzero / (evenzero + 1)) > 6) && (pctzero > 20)) && (crlf == 0) && (lfnul > 1)) ) { debug(F100,"scanfile UCS2 noBOM LE (even/oddzero)","",0); rc = FT_UCS2; val = 1; #ifdef COMMENT #ifdef EVENMAX /* If the tests above fail, we still might have UCS-2 if there are significant runs of identical bytes in alternating positions, but only if it also has unusual C0 controls (otherwise we'd pick up hex files here). NOTE: We don't actually do this -- EVENMAX is not defined (see comments above at first occurrence of EVENMAX). */ } else if (c0noniso && evenmax > bytes / 4) { debug(F100,"scanfile UCS2 BE (evenmax)","",0); rc = FT_UCS2; val = 0; } else if (c0noniso && oddmax > bytes / 4) { debug(F100,"scanfile UCS2 LE (evenmax)","",0); rc = FT_UCS2; val = 1; #endif /* EVENMAX */ #endif /* COMMENT */ } /* It seems to be UCS-2 but let's be more certain since there is no BOM... If the number of 7- and 8-bit characters is approximately equal, it might be a compressed file. In this case we decide based on the name. */ if (rc == FT_UCS2) { if (eightbit > 0) { int j, k; j = (c1controls * 100) / (c0controls + 1); debug(F101,"scanfile c1/c0 ","",j); k = (bytes * 100) / eightbit; debug(F101,"scanfile pct 8bit ","",k); if (k > 40 && k < 60 && j > 60) { if (ckmatch("{*.Z,*.gz,*.zip,*.ZIP}",name,1,1)) { debug(F110,"scanfile 8-bit BIN compressed",name,0); rc = FT_BIN; goto xscanfile; } } } /* Small file - not enough evidence unless ... */ if (bytes < 100) { if (oddzero != 0 && evenzero != 0) { debug(F100,"scanfile small UCS2 doubtful","",0); rc = FT_BIN; goto xscanfile; } else if (oddzero == 0 && evenzero == 0) { rc = eightbit ? FT_8BIT : FT_7BIT; } } goto xscanfile; /* Seems to be UCS-2 */ } /* If none of the above, it's probably not Unicode. */ if (!eightbit) { /* It's 7-bit */ if (c0controls) { /* This would be strange */ if ((c0noniso > 0) && (txtcz == 0)) { debug(F100,"scanfile 7-bit BIN (c0coniso)","",0); rc = FT_BIN; } else { debug(F100,"scanfile 7-bit ISO2022 TEXT (no c0noniso)","",0); rc = FT_7BIT; } } else { /* 7-bit text */ debug(F100,"scanfile 7-bit TEXT (no c0controls)","",0); rc = FT_7BIT; } } else if (!c0noniso || txtcz) { /* 8-bit text */ debug(F100,"scanfile 8-bit TEXT (no c0noniso)","",0); rc = FT_8BIT; val = c1controls ? 1 : 0; } else { /* 8-bit binary */ debug(F100,"scanfile 8-bit BIN (c0noniso)","",0); rc = FT_BIN; } #else /* !UNICODE */ if (c0noniso) { debug(F100,"scanfile 8-bit BIN (c0noniso)","",0); rc = FT_BIN; } else if (eightbit) { debug(F100,"scanfile 8-bit TEXT (no c0noniso)","",0); rc = FT_8BIT; val = c1controls ? 1 : 0; } else { debug(F100,"scanfile 7-bit TEXT (no c0noniso)","",0); rc = FT_7BIT; } #endif /* UNICODE */ xscanfile: if (flag) *flag = val; debug(F101,"scanfile result ","",rc); return(rc); } /* scanstring - like scan file but for a string. This is just a quick butchery of scanfile without thinking too much. */ int scanstring(s) char * s; { int x, val = -1, count = 0; /* Workers */ int rc = -1; /* Return code */ int pv = -1; /* Pattern-match value */ int bytes = 0; /* Total byte count */ #ifdef UNICODE unsigned int c0, c1; /* First 2 file bytes (for BOM) */ #endif /* UNICODE */ extern int pipesend, filepeek; register int i; /* Loop control */ int readsize = 0; /* How much to read */ int eightbit = 0; /* Number of bytes with 8th bit on */ int c0controls = 0; /* C0 non-text control-char counter */ int c0noniso = 0; /* C0 non-ISO control-char counter */ int c1controls = 0; /* C1 control-character counter */ unsigned int c; /* Current character */ int runmax = 0; /* Longest run of 0 bytes */ int runzero = 0; /* Run of 0 bytes */ int pctzero = 0; /* Percentage of 0 bytes */ int txtcz = 0; #ifdef UNICODE int notutf8 = 0; /* Nonzero if definitely not UTF-8 */ int utf8state = 0; /* UTF-8 recognizer state */ int oddzero = 0; /* Number of 0 bytes in odd postions */ int evenzero = 0; /* and in even positions */ int lfnul = 0; /* Number of sequences */ int crlf = 0; /* Number of sequences */ #else int notutf8 = 1; #endif /* UNICODE */ char * buf = s; if (!s) s = ""; count = strlen(s); #ifdef UNICODE if (bytes == 0 && count > 1) { int incl_cnt = 0; /* First look for BOM */ c0 = (unsigned)((unsigned)buf[0]&0xFF); /* First file byte */ c1 = (unsigned)((unsigned)buf[1]&0xFF); /* Second byte */ if (c0 == 0xFE && c1 == 0xFF) { /* UCS-2 BE */ rc = FT_UCS2; val = 0; debug(F111,"scanstring UCS2 BOM BE",ckitoa(val),rc); incl_cnt++; } else if (c0 == 0xFF && c1 == 0xFE) { /* UCS-2 LE */ rc = FT_UCS2; val = 1; debug(F111,"scanstring UCS2 BOM LE",ckitoa(val),rc); incl_cnt++; } else if (count > 2) if (c0 == 0xEF && c1 == 0xBB && (unsigned)((unsigned)buf[2]&0xFF) == 0xBF) { rc = FT_UTF8; debug(F111,"scanstring UTF8 BOM",ckitoa(val),rc); incl_cnt++; } if (incl_cnt) { /* Have BOM */ bytes += count; goto xscanstring; } } #endif /* UNICODE */ bytes += count; /* Count bytes read */ for (i = 0; i < count; i++) { /* For each byte... */ c = (unsigned)buf[i]; /* For ease of reference */ if (!c) { /* Zero byte? */ goto xscanstring; /* Null terminated string */ } if ((c & 0x80) == 0) { /* We have a 7-bit byte */ #ifdef UNICODE if (i > 0 && c == 10) { /* Linefeed */ if (buf[i-1] == 0) lfnul++; /* Preceded by NUL */ else if (buf[i-1] == 13) crlf++; /* or by CR... */ } #endif /* UNICODE */ if (c < ' ') { /* Check for CO controls */ if (c != LF && c != CR && c != HT && c != FF) { c0controls++; if (c != ESC && c != SO && c != SI) c0noniso++; } if ((c == '\032') /* Ctrl-Z */ ) { c0controls--; c0noniso--; } } #ifdef UNICODE if (!notutf8 && utf8state) { /* In UTF-8 sequence? */ utf8state = 0; debug(F000,"scanstring","7-bit byte in UTF8 sequence",c); notutf8++; /* Then it's not UTF-8 */ continue; } #endif /* UNICODE */ } else { /* We have an 8-bit byte */ eightbit++; /* Count it */ if (c >= 0x80 && c < 0xA0) /* Check for C1 controls */ c1controls++; #ifdef UNICODE if (!notutf8) { /* If it might still be UTF8... */ switch (utf8state) { /* Enter the UTF-8 state machine */ case 0: /* First byte... */ if ((c & 0xE0) == 0xC0) { /* Tells number of */ utf8state = 1; /* subsequent bytes */ } else if ((c & 0xF0) == 0xE0) { utf8state = 2; } else if ((c & 0xF8) == 0xF0) { utf8state = 3; } else { notutf8++; } break; case 1: /* Subsequent byte */ case 2: case 3: if ((c & 0xC0) != 0x80) { /* Must start with 10 */ debug(F000,"scanstring", "bad byte in UTF8 sequence",c); notutf8++; break; } utf8state--; /* Good, one less in this sequence */ break; default: /* Shouldn't happen */ debug(F111,"scanstring","bad UTF8 state",utf8state); notutf8++; } } #endif /* UNICODE */ } } if (bytes == 0) /* If nothing was read */ return(-1); /* we're done. */ #ifdef UNICODE if (bytes > 100) /* Bytes is not 0 */ pctzero = (evenzero + oddzero) / (bytes / 100); else pctzero = ((evenzero + oddzero) * 100) / bytes; #endif /* UNICODE */ #ifdef UNICODE x = eightbit ? bytes / 20 : bytes / 4; /* For UCS-2... */ if (runmax > 2) { /* File has run of more than 2 NULs */ debug(F100,"scanstring BIN runmax","",0); rc = FT_BIN; /* so it can't be any kind of text. */ goto xscanstring; } else if (rc == FT_UCS2 || (rc == FT_UTF8 && runmax == 0)) { goto xscanstring; /* File starts with a BOM */ } else if (eightbit > 0 && !notutf8) { /* File has 8-bit data */ if (runmax > 0) { /* and runs of NULs */ debug(F100,"scanstring BIN (nnUTF8) runmax","",0); rc = FT_BIN; /* UTF-8 doesn't have NULs */ } else { /* No NULs */ debug(F100,"scanstring UTF8 (nnUTF8 + runmax == 0)","",0); rc = FT_UTF8; /* and not not UTF-8, so is UTF-8 */ } goto xscanstring; } /* It seems to be UCS-2 but let's be more certain since there is no BOM... If the number of 7- and 8-bit characters is approximately equal, it might be a compressed file. In this case we decide based on the name. */ if (rc == FT_UCS2) { if (bytes < 100) { if (oddzero != 0 && evenzero != 0) { debug(F100,"scanstring small UCS2 doubtful","",0); rc = FT_BIN; goto xscanstring; } else if (oddzero == 0 && evenzero == 0) { rc = eightbit ? FT_8BIT : FT_7BIT; } } goto xscanstring; /* Seems to be UCS-2 */ } /* If none of the above, it's probably not Unicode. */ if (!eightbit) { /* It's 7-bit */ if (c0controls) { /* This would be strange */ if ((c0noniso > 0) && (txtcz == 0)) { debug(F100,"scanstring 7-bit BIN (c0coniso)","",0); rc = FT_BIN; } else { debug(F100,"scanstring 7-bit ISO2022 TEXT (no c0noniso)","",0); rc = FT_7BIT; } } else { /* 7-bit text */ debug(F100,"scanstring 7-bit TEXT (no c0controls)","",0); rc = FT_7BIT; } } else if (!c0noniso || txtcz) { /* 8-bit text */ debug(F100,"scanstring 8-bit TEXT (no c0noniso)","",0); rc = FT_8BIT; val = c1controls ? 1 : 0; } else { /* 8-bit binary */ debug(F100,"scanstring 8-bit BIN (c0noniso)","",0); rc = FT_BIN; } #else /* !UNICODE */ if (c0noniso) { debug(F100,"scanstring 8-bit BIN (c0noniso)","",0); rc = FT_BIN; } else if (eightbit) { debug(F100,"scanstring 8-bit TEXT (no c0noniso)","",0); rc = FT_8BIT; val = c1controls ? 1 : 0; } else { debug(F100,"scanstring 7-bit TEXT (no c0noniso)","",0); rc = FT_7BIT; } #endif /* UNICODE */ xscanstring: debug(F101,"scanstring result ","",rc); return(rc); } /* F I L E S E L E C T -- Select this file for sending */ int #ifdef CK_ANSIC fileselect( char *f, char *sa, char *sb, char *sna, char *snb, CK_OFF_T minsiz, CK_OFF_T maxsiz, int nbu, int nxlist, char ** xlist ) #else fileselect(f,sa,sb,sna,snb,minsiz,maxsiz,nbu,nxlist,xlist) char *f,*sa,*sb,*sna,*snb; CK_OFF_T minsiz,maxsiz; int nbu,nxlist; char ** xlist; #endif /* CK_ANSIC */ /* fileselect */ { char *fdate; int n; CK_OFF_T z; debug(F111,"fileselect minsiz",ckfstoa(minsiz),minsiz); debug(F111,"fileselect maxsiz",ckfstoa(maxsiz),maxsiz); debug(F111,"fileselect (CK_OFF_T)-1",ckfstoa((CK_OFF_T)-1),(CK_OFF_T)-1); if (!sa) sa = ""; if (!sb) sb = ""; if (!sna) sna = ""; if (!snb) snb = ""; #ifdef CKSYMLINK #ifndef NOICP #ifndef NOXFER if (nolinks) { CK_OFF_T zz; zz = zgetfs(f); debug(F111,"fileselect NOLINKS zgetfs",f,zz); if (zz < (CK_OFF_T)0) return(0); debug(F111,"fileselect NOLINKS zgfs_link",f,zgfs_link); if (zgfs_link) return(0); } #endif /* NOXFER */ #endif /* NOICP */ #endif /* CKSYMLINK */ debug(F110,"fileselect",f,0); if (*sa || *sb || *sna || *snb) { fdate = zfcdat(f); /* Date/time of this file */ if (!fdate) fdate = ""; n = strlen(fdate); debug(F111,"fileselect fdate",fdate,n); if (n != 17) /* Failed to get it */ return(1); /* /AFTER: */ if (sa[0] && (strcmp(fdate,(char *)sa) <= 0)) { debug(F110,"fileselect sa",sa,0); /* tlog(F110,"Skipping (too old)",f,0); */ return(0); } /* /BEFORE: */ if (sb[0] && (strcmp(fdate,(char *)sb) >= 0)) { debug(F110,"fileselect sb",sb,0); /* tlog(F110,"Skipping (too new)",f,0); */ return(0); } /* /NOT-AFTER: */ if (sna[0] && (strcmp(fdate,(char *)sna) > 0)) { debug(F110,"fileselect sna",sna,0); /* tlog(F110,"Skipping (too new)",f,0); */ return(0); } /* /NOT-BEFORE: */ if (snb[0] && (strcmp(fdate,(char *)snb) < 0)) { debug(F110,"fileselect snb",snb,0); /* tlog(F110,"Skipping (too old)",f,0); */ return(0); } } /* Smaller or larger */ if (minsiz > (CK_OFF_T)-1 || maxsiz > (CK_OFF_T)-1) { z = zchki(f); /* Get size */ debug(F101,"fileselect filesize","",z); if (z < (CK_OFF_T)0) return(1); if ((minsiz > (CK_OFF_T)-1) && (z >= minsiz)) { debug(F111,"fileselect minsiz skipping",f,minsiz); /* tlog(F111,"Skipping (too big)",f,z); */ return(0); } if ((maxsiz > (CK_OFF_T)-1) && (z <= maxsiz)) { debug(F111,"fileselect maxsiz skipping",f,maxsiz); /* tlog(F110,"Skipping (too small)",f,0); */ return(0); } } if (nbu) { /* Skipping backup files? */ if (ckmatch( #ifdef CKREGEX "*.~[0-9]*~" /* Not perfect but close enough. */ #else "*.~*~" /* Less close. */ #endif /* CKREGEX */ ,f,filecase,1)) { debug(F110,"fileselect skipping backup",f,0); return(0); } } for (n = 0; xlist && n < nxlist; n++) { if (!xlist[n]) { debug(F101,"fileselect xlist empty",0,n); break; } if (ckmatch(xlist[n],f,filecase,1)) { debug(F111,"fileselect xlist",xlist[n],n); debug(F110,"fileselect skipping",f,0); return(0); } } if (xfiletype > -1) { n = scanfile(f,NULL,nscanfile); if (n < 0) { n = binary ? 1 : 0; } else { n = (n == FT_BIN) ? 1 : 0; } if (n != xfiletype) return(0); } debug(F110,"fileselect selecting",f,0); return(1); } #ifdef TCPSOCKET #ifdef NT extern int WSASafeToCancel; #endif /* NT */ #endif /* TCPSOCKET */ VOID setflow() { extern int flow, autoflow, mdmtyp, cxtype, cxflow[]; #ifndef NODIAL extern int dialcapas, dialfc; extern MDMINF * modemp[]; MDMINF * p = NULL; long bits = 0; #endif /* NODIAL */ debug(F101,"setflow autoflow","",autoflow); /* #ifdef COMMENT */ /* WHY WAS THIS COMMENTED OUT? */ if (!autoflow) /* Only if FLOW is AUTO */ return; /* #endif */ /* COMMENT */ debug(F101,"setflow local","",local); debug(F101,"setflow network","",network); debug(F101,"setflow cxtype","",cxtype); #ifdef TN_COMPORT if (network && istncomport()) { flow = cxflow[CXT_MODEM]; debug(F101,"setflow TN_COMPORT flow","",flow); return; } #endif /* TN_COMPORT */ if (network || !local || cxtype == CXT_DIRECT) { flow = cxflow[cxtype]; /* Set appropriate flow control */ debug(F101,"setflow flow","",flow); return; } if (cxtype != CXT_MODEM) /* Connection type should be modem */ return; #ifndef NODIAL bits = dialcapas; /* Capability bits */ if (!bits) { /* No bits? */ p = modemp[mdmtyp]; /* Look in modem info structure */ if (p) bits = p->capas; } if (dialfc == FLO_AUTO) { /* If DIAL flow is AUTO */ #ifdef CK_RTSCTS /* If we can do RTS/CTS flow control */ if (bits & CKD_HW) /* and modem can do it too */ flow = FLO_RTSC; /* then switch to RTS/CTS */ else /* otherwise */ flow = FLO_XONX; /* use Xon/Xoff. */ #else #ifndef NEXT #ifndef IRIX flow = FLO_XONX; /* Use Xon/Xoff. */ #endif /* IRIX */ #endif /* NEXT */ #endif /* CK_RTSCTS */ } #endif /* NODIAL */ debug(F101,"setflow modem flow","",flow); return; } #ifndef NOLOCAL #ifdef CK_TRIGGER /* A U T O E X I T C H K -- Check for CONNECT-mode trigger string */ /* Returns -1 if trigger not found, or else the trigger index, 0 or greater. (Replace with fancier and more efficient matcher later...) NOTE: to prevent unnecessary function call overhead, call this way: x = tt_trigger[0] ? autoexitchk(c) : -1; */ int #ifdef CK_ANSIC autoexitchk(CHAR c) #else autoexitchk(c) CHAR c; #endif /* CK_ANSIC */ /* autoexitchk */ { extern CHAR * tt_trmatch[]; extern char * tt_trigger[]; int i; for (i = 0; i < TRIGGERS; i++) { if (!tt_trigger[i]) { /* No more triggers in list */ break; } else if (*tt_trigger[i]) { if (!tt_trmatch[i]) /* Just starting? */ tt_trmatch[i] = (CHAR *)tt_trigger[i]; /* Set match pointer */ if (c == *tt_trmatch[i]) { /* Compare this character */ tt_trmatch[i]++; /* It matches */ if (!*tt_trmatch[i]) { /* End of match string? */ tt_trmatch[i] = (CHAR *) tt_trigger[i]; /* Yes, rewind, */ debug(F101,"autoexitchk",tt_trigger[i],i); /* log, */ return(i); /* and return success */ } } else /* No match */ tt_trmatch[i] = (CHAR *) tt_trigger[i]; /* Rewind match string */ } /* and go on the next match string */ } return(-1); /* No match found */ } #endif /* CK_TRIGGER */ #ifndef NOSHOW /* S H O M D M -- Show modem signals */ VOID shomdm() { /* Note use of "\r\n" to make sure this report prints right, even when called during CONNECT mode. */ int y; y = ttgmdm(); switch (y) { case -3: printf( "Modem signals unavailable in this version of Kermit\r\n"); break; case -2: printf("No modem control for this device\r\n"); break; case -1: printf("Modem signals unavailable\r\n"); break; default: #ifndef MAC printf( " Carrier Detect (CD): %s\r\n",(y & BM_DCD) ? "On": "Off"); printf( " Dataset Ready (DSR): %s\r\n",(y & BM_DSR) ? "On": "Off"); #endif /* MAC */ printf( " Clear To Send (CTS): %s\r\n",(y & BM_CTS) ? "On": "Off"); #ifndef STRATUS #ifndef MAC printf( " Ring Indicator (RI): %s\r\n",(y & BM_RNG) ? "On": "Off"); #endif /* MAC */ printf( " Data Terminal Ready (DTR): %s\r\n", #ifdef NT "(unknown)" #else /* NT */ (y & BM_DTR) ? "On": "Off" #endif /* NT */ ); #ifndef MAC printf( " Request To Send (RTS): %s\r\n", #ifdef NT "(unknown)" #else /* NT */ (y & BM_RTS) ? "On": "Off" #endif /* NT */ ); #endif /* MAC */ #endif /* STRATUS */ } #ifdef BETADEBUG #ifdef CK_TAPI if (tttapi && !tapipass) { LPDEVCFG lpDevCfg = NULL; LPCOMMCONFIG lpCommConfig = NULL; LPMODEMSETTINGS lpModemSettings = NULL; DCB * lpDCB = NULL; if (cktapiGetModemSettings(&lpDevCfg,&lpModemSettings, &lpCommConfig,&lpDCB)) { printf("\n"); cktapiDisplayModemSettings(lpDevCfg,lpModemSettings, lpCommConfig,lpDCB); } } #endif /* CK_TAPI */ #endif /* BETADEBUG */ } #endif /* NOSHOW */ #endif /* NOLOCAL */ #ifndef NOXFER /* S D E B U -- Record spar results in debugging log */ VOID sdebu(len) int len; { debug(F111,"spar: data",(char *) rdatap,len); debug(F101," spsiz ","", spsiz); debug(F101," timint","",timint); debug(F101," npad ","", npad); debug(F101," padch ","", padch); debug(F101," seol ","", seol); debug(F101," ctlq ","", ctlq); debug(F101," ebq ","", ebq); debug(F101," ebqflg","",ebqflg); debug(F101," bctr ","", bctr); debug(F101," rptq ","", rptq); debug(F101," rptflg","",rptflg); debug(F101," lscapu","",lscapu); debug(F101," atcapu","",atcapu); debug(F101," lpcapu","",lpcapu); debug(F101," swcapu","",swcapu); debug(F101," wslotn","", wslotn); debug(F101," whatru","", whatru); } /* R D E B U -- Debugging display of rpar() values */ VOID rdebu(d,len) CHAR *d; int len; { debug(F111,"rpar: data",d,len); debug(F101," rpsiz ","", xunchar(d[0])); debug(F101," rtimo ","", rtimo); debug(F101," mypadn","",mypadn); debug(F101," mypadc","",mypadc); debug(F101," eol ","", eol); debug(F101," ctlq ","", ctlq); debug(F101," sq ","", sq); debug(F101," ebq ","", ebq); debug(F101," ebqflg","",ebqflg); debug(F101," bctr ","", bctr); debug(F101," rptq ","", d[8]); debug(F101," rptflg","",rptflg); debug(F101," capas ","", capas); debug(F101," bits ","",d[capas]); debug(F101," lscapu","",lscapu); debug(F101," atcapu","",atcapu); debug(F101," lpcapu","",lpcapu); debug(F101," swcapu","",swcapu); debug(F101," wslotr","", wslotr); debug(F101," rpsiz(extended)","",rpsiz); } #ifdef COMMENT /* C H K E R R -- Decide whether to exit upon a protocol error */ VOID chkerr() { if (backgrd && !server) fatal("Protocol error"); } #endif /* COMMENT */ #endif /* NOXFER */ /* F A T A L -- Fatal error message */ VOID fatal(msg) char *msg; { extern int initflg; static int initing = 0; if (!msg) msg = ""; debug(F111,"fatal",msg,initflg); if (!initflg) { /* If called from prescan */ if (initing) /* or called from sysinit() */ exit(253); initing = 1; sysinit(); } debug(F111,"fatal",msg,xitsta); tlog(F110,"Fatal:",msg,0L); #ifdef VMS if (strncmp(msg,"%CKERMIT",8)) conol("%CKERMIT-E-FATAL, "); conoll(msg); #else /* !VMS */ conoll(msg); #endif /* VMS */ #ifdef OS2 #ifndef NOXFER if (xfrbel) { bleep(BP_FAIL); sleep(1); bleep(BP_FAIL); } #endif /* NOXFER */ #endif /* OS2 */ doexit(BAD_EXIT,xitsta | 1); /* Exit indicating failure */ } #ifndef NOXFER /* B L D L E N -- Make length-encoded copy of string */ char * bldlen(str,dest) char *str, *dest; { int len; len = (int)strlen(str); if (len > 94) *dest = SP; else *dest = (char) tochar(len); strcpy(dest+1,str); /* Checked below in setgen() */ return(dest+len+1); } /* S E T G E N -- Construct a generic command */ /* Call with Generic command character followed by three string arguments. Trailing strings are allowed to be empty (""). Each string except the last non-empty string must be less than 95 characters long. The final nonempty string is allowed to be longer. */ CHAR #ifdef CK_ANSIC setgen(char type, char * arg1, char * arg2, char * arg3) #else setgen(type,arg1,arg2,arg3) char type, *arg1, *arg2, *arg3; #endif /* CK_ANSIC */ /* setgen */ { char *upstr, *cp; #ifdef DYNAMIC if (!cmdstr) if (!(cmdstr = malloc(MAXSP + 1))) fatal("setgen: can't allocate memory"); #endif /* DYNAMIC */ cp = cmdstr; *cp++ = type; *cp = NUL; if (!arg1) arg1 = ""; if (!arg2) arg2 = ""; if (!arg3) arg3 = ""; if (((int)strlen(arg1)+(int)strlen(arg2)+(int)strlen(arg3)+4) < MAXSP) { if (*arg1 != NUL) { upstr = bldlen(arg1,cp); if (*arg2 != NUL) { upstr = bldlen(arg2,upstr); if (*arg3 != NUL) bldlen(arg3,upstr); } } cmarg = cmdstr; debug(F110,"setgen",cmarg,0); return('g'); } return('E'); } #endif /* NOXFER */ #ifndef NOMSEND static char *mgbufp = NULL; /* F N P A R S E -- */ /* Argument is a character string containing one or more filespecs. This function breaks the string apart into an array of pointers, one to each filespec, and returns the number of filespecs. Used by server when it receives a GET command to allow it to process multiple file specifications in one transaction. Sets cmlist to point to a list of file pointers, exactly as if they were command line arguments. This version of fnparse treats spaces as filename separators. If your operating system allows spaces in filenames, you'll need a different separator. This version of fnparse mallocs a string buffer to contain the names. It cannot assume that the string that is pointed to by the argument is safe. */ int fnparse(string) char *string; { char *p, *s, *q; int r = 0, x; /* Return code */ #ifdef RECURSIVE debug(F111,"fnparse",string,recursive); #endif /* RECURSIVE */ if (mgbufp) free(mgbufp); /* Free this from last time. */ mgbufp = malloc((int)strlen(string)+2); if (!mgbufp) { debug(F100,"fnparse malloc error","",0); return(0); } #ifndef NOICP #ifndef NOSPL ckstrncpy(fspec,string,fspeclen); /* Make copy for \v(filespec) */ #endif /* NOSPL */ #endif /* NOICP */ s = string; /* Input string */ p = q = mgbufp; /* Point to the copy */ r = 0; /* Initialize our return code */ while (*s == SP || *s == HT) /* Skip leading spaces and tabs */ s++; for (x = strlen(s); /* Strip trailing spaces */ (x > 1) && (s[x-1] == SP || s[x-1] == HT); x--) s[x-1] = NUL; while (1) { /* Loop through rest of string */ if (*s == CMDQ) { /* Backslash (quote character)? */ if ((x = xxesc(&s)) > -1) { /* Go interpret it. */ *q++ = (char) x; /* Numeric backslash code, ok */ } else { /* Just let it quote next char */ s++; /* get past the backslash */ *q++ = *s++; /* deposit next char */ } continue; } else if (*s == SP || *s == NUL) { /* Unquoted space or NUL? */ *q++ = NUL; /* End of output filename. */ msfiles[r] = p; /* Add this filename to the list */ debug(F111,"fnparse",msfiles[r],r); r++; /* Count it */ if (*s == NUL) break; /* End of string? */ while (*s == SP) s++; /* Skip repeated spaces */ p = q; /* Start of next name */ continue; } else *q++ = *s; /* Otherwise copy the character */ s++; /* Next input character */ } debug(F101,"fnparse r","",r); msfiles[r] = ""; /* Put empty string at end of list */ cmlist = msfiles; return(r); } #endif /* NOMSEND */ char * /* dbchr() for DEBUG SESSION */ dbchr(c) int c; { static char s[8]; char *cp = s; c &= 0xff; if (c & 0x80) { /* 8th bit on */ *cp++ = '~'; c &= 0x7f; } if (c < SP) { /* Control character */ *cp++ = '^'; *cp++ = (char) ctl(c); } else if (c == DEL) { *cp++ = '^'; *cp++ = '?'; } else { /* Printing character */ *cp++ = (char) c; } *cp = '\0'; /* Terminate string */ cp = s; /* Return pointer to it */ return(cp); } /* C K H O S T -- Get name of local host (where C-Kermit is running) */ /* Call with pointer to buffer to put hostname in, and length of buffer. Copies hostname into buffer on success, puts null string in buffer on failure. */ #ifdef BSD44 #define BSD4 #undef ATTSV #endif /* BSD44 */ #ifdef SVORPOSIX #ifndef BSD44 #ifndef apollo #include #endif /* apollo */ #endif /* BSD44 */ #else #ifdef BELLV10 #include #endif /* BELLV10 */ #endif /* SVORPOSIX*/ #ifdef CKSYSLOG extern char uidbuf[], * clienthost; #endif /* CKSYSLOG */ VOID ckhost(vvbuf,vvlen) char * vvbuf; int vvlen; { #ifndef NOPUSH extern int nopush; #ifndef NOSERVER extern int en_hos; #endif /* NOSERVER */ #endif /* NOPUSH */ #ifdef pdp11 *vvbuf = NUL; #else /* Everything else - rest of this routine */ char *g; int havefull = 0; #ifdef VMS int x; #endif /* VMS */ #ifdef SVORPOSIX #ifndef BSD44 #ifndef _386BSD #ifndef APOLLOSR10 struct utsname hname; #endif /* APOLLOSR10 */ #endif /* _386BSD */ #endif /* BSD44 */ #endif /* SVORPOSIX */ #ifdef datageneral int ac0 = (char *) vvbuf, ac1 = -1, ac2 = 0; #endif /* datageneral */ #ifndef NOPUSH if (getenv("CK_NOPUSH")) { /* No shell access allowed */ nopush = 1; /* on this host... */ #ifndef NOSERVER en_hos = 0; #endif /* NOSERVER */ } #endif /* NOPUSH */ *vvbuf = NUL; /* How let's get our host name ... */ #ifndef BELLV10 /* Does not have gethostname() */ #ifndef OXOS #ifdef SVORPOSIX #ifdef APOLLOSR10 ckstrncpy(vvbuf,"Apollo",vvlen); #else #ifdef BSD44 if (gethostname(vvbuf,vvlen) < 0) *vvbuf = NUL; #else #ifdef _386BSD if (gethostname(vvbuf,vvlen) < 0) *vvbuf = NUL; #else #ifdef QNX #ifdef TCPSOCKET if (gethostname(vvbuf,vvlen) < 0) *vvbuf = NUL; #else if (uname(&hname) > -1) ckstrncpy(vvbuf,hname.nodename,vvlen); #endif /* TCPSOCKET */ #else /* SVORPOSIX but not _386BSD or BSD44 */ #ifdef __ia64__ if (uname(&hname) > -1) ckstrncpy(vvbuf,hname.nodename,vvlen); #else if (uname(&hname) > -1) { char * p; p = hname.nodename; #ifdef TCPSOCKET #ifndef NOCKGETFQHOST if (!ckstrchr(p,'.')) p = (char *)ckgetfqhostname(p); #endif /* NOCKGETFQHOST */ #endif /* TCPSOCKET */ if (!p) p = ""; if (!*p) p = "(unknown)"; ckstrncpy(vvbuf,p,vvlen); } #endif /* __ia64__ */ #endif /* QNX */ #endif /* _386BSD */ #endif /* BSD44 */ #endif /* APOLLOSR10 */ #else /* !SVORPOSIX */ #ifdef BSD4 if (gethostname(vvbuf,vvlen) < 0) *vvbuf = NUL; #else /* !BSD4 */ #ifdef VMS g = getenv("SYS$NODE"); if (g) ckstrncpy(vvbuf,g,vvlen); x = (int)strlen(vvbuf); if (x > 1 && vvbuf[x-1] == ':' && vvbuf[x-2] == ':') vvbuf[x-2] = NUL; #else #ifdef datageneral if (sys($HNAME,&ac0,&ac1,&ac2) == 0) /* successful */ vvlen = ac2 + 1; /* enh - have to add one */ #else #ifdef OS2 /* OS/2 */ g = os2_gethostname(); if (g) ckstrncpy(vvbuf,g,vvlen); #else /* OS2 */ #ifdef OSK #ifdef TCPSOCKET if (gethostname(vvbuf, vvlen) < 0) *vvbuf = NUL; #endif /* TCPSOCKET */ #endif /* OSK */ #endif /* OS2 */ #endif /* datageneral */ #endif /* VMS */ #endif /* BSD4 */ #endif /* SVORPOSIX */ #else /* OXOS */ /* If TCP/IP is not installed, gethostname() fails, use uname() */ if (gethostname(vvbuf,vvlen) < 0) { if (uname(&hname) > -1) ckstrncpy(vvbuf,hname.nodename,vvlen); else *vvbuf = NUL; } #endif /* OXOS */ #endif /* BELLV10 */ if (*vvbuf == NUL) { /* If it's still empty */ g = getenv("HOST"); /* try this */ if (g) ckstrncpy(vvbuf,g,vvlen); } vvbuf[vvlen-1] = NUL; /* Make sure result is terminated. */ #endif /* pdp11 */ } #ifdef BSD44 #undef BSD4 #define ATTSV #endif /* BSD44 */ /* A S K M O R E -- Poor person's "more". Returns 0 if no more, 1 if more wanted. */ int askmore() { char c; int rv, cx; #ifdef IKSD extern int timelimit; #endif /* IKSD */ #ifdef IKSDCONF extern int iksdcf; #endif /* IKSDCONF */ #ifdef CK_APC extern int apcstatus, apcactive; #endif /* CK_APC */ #ifdef NOICP return(1); #else if (!xaskmore) return(1); #ifdef IKSDCONF if (inserver && !iksdcf) return(1); #endif /* IKSDCONF */ #ifdef CK_APC if (apcactive == APC_LOCAL || (apcactive == APC_REMOTE && (apcstatus & APC_NOINP))) return(1); #endif /* CK_APC */ #ifdef VMS if (batch) return(1); #else #ifdef UNIX if (backgrd) return(1); #endif /* UNIX */ #endif /* VMS */ #ifndef VMS concb((char)escape); /* Force CBREAK mode. */ #endif /* VMS */ rv = -1; while (rv < 0) { #ifndef OS2 printf("more? "); #ifdef UNIX #ifdef NOSETBUF fflush(stdout); #endif /* NOSETBUF */ #endif /* UNIX */ #else printf("more? "); fflush(stdout); #endif /* OS2 */ #ifdef IKSD if (inserver) { cx = cmdgetc(timelimit); if (cx < -1 && timelimit) { printf("\n?IKS idle timeout - Goodbye.\n"); doexit(GOOD_EXIT,0); } else if (cx == -1) { /* Connection lost */ doexit(BAD_EXIT,0); } c = (char) cx; } else { #endif /* IKSD */ #ifdef VMS conbin((char)escape); /* Protect against Ctrl-Z */ cx = coninc(0); concb((char)escape); #else cx = cmdgetc(0); #endif /* VMS */ debug(F101,"askmore cmdgetc","",cx); if (cx == EOF) { debug(F100,"askmore EOF","",0); #ifdef VMS c = '\032'; #else c = 'n'; #endif /* VMS */ } else { c = (char)cx; } debug(F101,"askmore c","",c); #ifdef IKSD } #endif /* IKSD */ switch (c) { /* Yes */ case 'p': case 'P': case 'g': case 'G': /* Proceed or Go */ xaskmore = 0; /* fall thru on purpose */ case SP: case 'y': case 'Y': case 012: case 015: #ifdef OSK write(1, "\015 \015", sizeof "\015 \015" - 1); #else printf("\015 \015"); #endif /* OSK */ rv = 1; break; /* No */ case 'n': case 'N': case 'q': case 'Q': #ifdef OSK printf("\n"); #else printf("\015\012"); #endif /* OSK */ rv = 0; break; case '\003': case '\004': case '\032': #ifdef OSK printf("^%c...\n", (c + 0100)); #else printf("^%c...\015\012", (c + 0100)); #endif /* OSK */ rv = 0; break; /* Invalid answer */ default: debug(F111,"askmore","invalid answer",c); printf("Y or space-bar for yes, N for no, G to show the rest\n"); continue; } #ifdef OS2 printf("\r \r"); fflush(stdout); #endif /* OS2 */ } return(rv); #endif /* NOICP */ } /* T R A P -- Terminal interrupt handler */ SIGTYP #ifdef CK_ANSIC trap(int sig) #else trap(sig) int sig; #endif /* CK_ANSIC */ /* trap */ { extern int b_save, f_save; #ifndef NOICP extern int timelimit; #endif /* NOICP */ #ifdef OS2 extern unsigned long startflags; #ifndef NOSETKEY extern int os2gks; #endif /* NOSETKEY */ int i; #endif /* OS2 */ #ifndef NOSPL extern int i_active, instatus; #endif /* NOSPL */ #ifdef VMS int i; FILE *f; #endif /* VMS */ extern int zchkod, zchkid; #ifndef NOSPL extern int unkmacro; #endif /* NOSPL */ debok = 1; #ifdef NTSIG connoi(); #endif /* NTSIG */ #ifdef __EMX__ signal(SIGINT, SIG_ACK); #endif #ifdef GEMDOS /* GEM is not reentrant, no i/o from interrupt level */ cklongjmp(cmjbuf,1); /* Jump back to parser now! */ #endif /* GEMDOS */ #ifdef DEBUG if (deblog) { debug(F100,"*********************","",0); if (sig == SIGINT) debug(F101,"trap caught SIGINT","",sig); else debug(F101,"trap caught signal","",sig); debug(F100,"*********************","",0); } #endif /* DEBUG */ #ifdef OS2 if ( sig == SIGBREAK && (startflags & 128) ) { debug(F101,"trap ignoring SIGBREAK","",sig); return; } #endif /* OS2 */ #ifndef NOICP timelimit = 0; /* In case timed ASK interrupted */ #ifndef NOSPL unkmacro = 0; /* Or ON_UNKNOWN_MACRO interrupted.. */ #endif /* NOSPL */ #endif /* NOICP */ zchkod = 0; /* Or file expansion interrupted... */ zchkid = 0; interrupted = 1; if (what & W_CONNECT) { /* Are we in CONNECT mode? */ /* The HP workstation Reset key sends some kind of ueber-SIGINT that can not be SIG_IGNored, so we wind up here somehow (even though this is *not* the current SIGINT handler). Just return. */ debug(F101,"trap: SIGINT caught during CONNECT","",sig); SIGRETURN; } #ifndef NOSPL if (i_active) { /* INPUT command was active? */ i_active = 0; /* Not any more... */ instatus = INP_UI; /* INPUT status = User Interrupted */ } #endif /* NOSPL */ #ifndef NOXFER ftreset(); /* Restore global protocol settings */ binary = b_save; /* Then restore these */ fncnv = f_save; bye_active = 0; diractive = 0; cdactive = 0; #endif /* NOXFER */ zclose(ZIFILE); /* If we were transferring a file, */ zclose(ZOFILE); /* close it. */ #ifndef NOICP cmdsquo(cmd_quoting); /* If command quoting was turned off */ #ifdef CKLEARN { extern FILE * learnfp; extern int learning; if (learnfp) { fclose(learnfp); learnfp = NULL; learning = 0; } } #endif /* CKLEARN */ #endif /* NOICP */ #ifdef CK_APC delmac("_apc_commands",1); apcactive = APC_INACTIVE; #endif /* CK_APC */ #ifdef VMS /* Fix terminal. */ if (ft_win) { /* If curses window open */ debug(F100,"^C trap() curses","",0); xxscreen(SCR_CW,0,0L,""); /* Close it */ conres(); /* Restore terminal */ i = printf("^C..."); /* Echo ^C to standard output */ } else { conres(); i = printf("^C...\n"); /* Echo ^C to standard output */ } if (i < 1 && ferror(stdout)) { /* If there was an error */ debug(F100,"^C trap() error","",0); fclose(stdout); /* close standard output */ f = fopen(dftty, "w"); /* open the controlling terminal */ if (f) stdout = f; /* and make it standard output */ printf("^C...\n"); /* and echo the ^C again. */ } #else /* Not VMS */ #ifdef STRATUS conres(); /* Set console back to normal mode */ #endif /* STRATUS */ #ifndef NOXFER if (ft_win) { /* If curses window open, */ debug(F100,"^C trap() curses","",0); xxscreen(SCR_CW,0,0L,""); /* close it. */ printf("^C..."); /* Echo ^C to standard output */ } else { #endif /* NOXFER */ printf("^C...\n"); #ifndef NOXFER } #endif /* NOXFER */ #endif /* VMS */ #ifdef datageneral connoi_mt(); /* Kill asynch task that listens to */ ttimoff(); conres(); /* the keyboard */ #endif /* datageneral */ #ifndef NOCCTRAP /* This is stupid -- every version should have ttimoff()... */ #ifdef UNIX ttimoff(); /* Turn off any timer interrupts */ #else #ifdef OSK ttimoff(); /* Turn off any timer interrupts */ #else #ifdef STRATUS ttimoff(); /* Turn off any timer interrupts */ #else #ifdef OS2 #ifndef NOSETKEY os2gks = 1; /* Turn back on keycode mapping */ #endif /* NOSETKEY */ #ifndef NOLOCAL for (i = 0; i < VNUM; i++) VscrnResetPopup(i); #endif /* NOLOCAL */ #ifdef TCPSOCKET #ifdef NT /* WSAIsBlocking() returns FALSE in Win95 during a blocking accept call */ if ( WSASafeToCancel /* && WSAIsBlocking() */ ) { WSACancelBlockingCall(); } #endif /* NT */ #endif /* TCPSOCKET */ #ifdef CK_NETBIOS NCBCancelOutstanding(); #endif /* CK_NETBIOS */ ttimoff(); /* Turn off any timer interrupts */ #else #ifdef VMS ttimoff(); /* Turn off any timer interrupts */ #endif /* VMS */ #endif /* OS2 */ #endif /* STRATUS */ #endif /* OSK */ #endif /* UNIX */ #ifdef NETPTY /* Clean up Ctrl-C out of REDIRECT or external protocol */ { extern PID_T pty_fork_pid; extern int pty_master_fd, pty_slave_fd; int x; signal(SIGCHLD,SIG_IGN); /* We don't want this any more */ debug(F101,"trap pty_master_fd","",pty_master_fd); if (pty_master_fd > 2) { x = close(pty_master_fd); debug(F101,"trap pty_master_fd close","",x); } pty_master_fd = -1; debug(F101,"trap pty_slave_fd","",pty_slave_fd); if (pty_slave_fd > 2) { x = close(pty_slave_fd); debug(F101,"trap pty_slave_fd close","",x); } pty_slave_fd = -1; debug(F101,"trap pty_fork_pid","",pty_fork_pid); if (pty_fork_pid > 0) { x = kill(pty_fork_pid,0); /* See if the fork is really there */ debug(F111,"trap pty_fork_pid kill 0 errno",ckitoa(x),errno); if (x == 0) { /* Seems to be active */ x = kill(pty_fork_pid,SIGHUP); /* Ask it to clean up & exit */ debug(F101,"trap pty_fork_pid kill SIGHUP","",x); msleep(100); errno = 0; x = kill(pty_fork_pid,0); /* Is it still there? */ if (x == 0 #ifdef ESRCH /* This module is not always exposed to */ || errno != ESRCH #endif /* ESRCH */ ) { x = kill(pty_fork_pid,SIGKILL); debug(F101,"trap pty_fork_pid kill SIGKILL","",x); } } pty_fork_pid = -1; } } #endif /* NETPTY */ #ifdef OSK sigmask(-1); /* We are in an intercept routine but do not perform a F$RTE (done implicitly by rts). We have to decrement the sigmask as F$RTE does. Warning: longjump only restores the cpu registers, NOT the fpu registers. So don't use fpu at all or at least don't use common fpu (double or float) register variables. */ #endif /* OSK */ #ifdef NTSIG PostCtrlCSem(); #else /* NTSIG */ debug(F100,"trap about to longjmp","",0); #ifdef NT cklongjmp(ckjaddr(cmjbuf),1); #else /* NT */ cklongjmp(cmjbuf,1); #endif /* NT */ #endif /* NTSIG */ #else /* NOCCTRAP */ /* No Ctrl-C trap, just exit. */ #ifdef CK_CURSES /* Curses support? */ xxscreen(SCR_CW,0,0L,""); /* Close curses window */ #endif /* CK_CURSES */ doexit(BAD_EXIT,what); /* Exit poorly */ #endif /* NOCCTRAP */ SIGRETURN; } /* C K _ T I M E -- Returns pointer to current time. */ char * ck_time() { static char tbuf[10]; char *p; int x; ztime(&p); /* "Thu Feb 8 12:00:00 1990" */ if (!p) /* like asctime()! */ return(""); if (*p) { for (x = 11; x < 19; x++) /* copy hh:mm:ss */ tbuf[x - 11] = p[x]; /* to tbuf */ tbuf[8] = NUL; /* terminate */ } return(tbuf); /* and return it */ } /* C C _ C L E A N -- Cleanup after terminal interrupt handler */ #ifdef GEMDOS int cc_clean() { zclose(ZIFILE); /* If we were transferring a file, */ zclose(ZOFILE); /* close it. */ printf("^C...\n"); /* Not VMS, no problem... */ } #endif /* GEMDOS */ /* S T P T R A P -- Handle SIGTSTP (suspend) signals */ SIGTYP #ifdef CK_ANSIC stptrap(int sig) #else stptrap(sig) int sig; #endif /* CK_ANSIC */ /* stptrap */ { #ifndef NOJC int x; extern int cmflgs; debug(F101,"stptrap() caught signal","",sig); if (!xsuspend) { printf("\r\nsuspend disabled\r\n"); #ifndef NOICP if (what & W_COMMAND) { /* If we were parsing commands */ prompt(xxstring); /* reissue the prompt and partial */ if (!cmflgs) /* command (if any) */ printf("%s",cmdbuf); } #endif /* NOICP */ } else { conres(); /* Reset the console */ #ifndef OS2 /* Flush pending output first, in case we are continued */ /* in the background, which could make us block */ fflush(stdout); x = psuspend(xsuspend); /* Try to suspend. */ if (x < 0) #endif /* OS2 */ printf("Job control not supported\r\n"); conint(trap,stptrap); /* Rearm the trap. */ debug(F100,"stptrap back from suspend","",0); switch (what) { case W_CONNECT: /* If suspended during CONNECT? */ conbin((char)escape); /* put console back in binary mode */ debug(F100,"stptrap W_CONNECT","",0); break; #ifndef NOICP case W_COMMAND: /* Suspended in command mode */ debug(F101,"stptrap W_COMMAND pflag","",pflag); concb((char)escape); /* Put back CBREAK tty mode */ if (pflag) { /* If command parsing was */ prompt(xxstring); /* reissue the prompt and partial */ if (!cmflgs) /* command (if any) */ printf("%s",cmdbuf); } break; #endif /* NOICP */ default: /* All other cases... */ debug(F100,"stptrap default","",0); concb((char)escape); /* Put it back in CBREAK mode */ break; } } #endif /* NOJC */ SIGRETURN; } #ifdef TLOG #define TBUFL 300 /* T L O G -- Log a record in the transaction file */ /* Call with a format and 3 arguments: two strings and a number: f - Format, a bit string in range 0-7, bit x is on, arg #x is printed. s1,s2 - String arguments 0 and 1. n - Long, argument 2. */ VOID #ifdef CK_ANSIC dotlog(int f, char *s1, char *s2, CK_OFF_T n) #else dotlog(f,s1,s2,n) int f; CK_OFF_T n; char *s1, *s2; #endif /* CK_ANSIC */ /* dotlog */ { static char s[TBUFL]; extern int tlogfmt; char *sp = s; int x; if (!s1) s1 = ""; if (!s2) s2 = ""; if (!tralog) return; /* If no transaction log, don't */ if (tlogfmt != 1) return; switch (f) { case F000: /* 0 (special) "s1 n s2" */ if ((int)strlen(s1) + (int)strlen(s2) + 15 > TBUFL) sprintf(sp,"?T-Log string too long"); else sprintf(sp,"%s %s %s",s1,ckfstoa(n),s2); if (zsoutl(ZTFILE,s) < 0) tralog = 0; break; case F001: /* 1, " n" */ sprintf(sp," %s",ckfstoa(n)); if (zsoutl(ZTFILE,s) < 0) tralog = 0; break; case F010: /* 2, "[s2]" */ x = (int)strlen(s2); if (s2[x] == '\n') s2[x] = '\0'; if (x + 6 > TBUFL) sprintf(sp,"?String too long"); else sprintf(sp,"[%s]",s2); if (zsoutl(ZTFILE,"") < 0) tralog = 0; break; case F011: /* 3, "[s2] n" */ x = (int)strlen(s2); if (s2[x] == '\n') s2[x] = '\0'; if (x + 6 > TBUFL) sprintf(sp,"?String too long"); else sprintf(sp,"[%s] %s",s2,ckfstoa(n)); if (zsoutl(ZTFILE,s) < 0) tralog = 0; break; case F100: /* 4, "s1" */ if (zsoutl(ZTFILE,s1) < 0) tralog = 0; break; case F101: /* 5, "s1: n" */ if ((int)strlen(s1) + 15 > TBUFL) sprintf(sp,"?String too long"); else sprintf(sp,"%s: %s",s1,ckfstoa(n)); if (zsoutl(ZTFILE,s) < 0) tralog = 0; break; case F110: /* 6, "s1 s2" */ x = (int)strlen(s2); if (s2[x] == '\n') s2[x] = '\0'; if ((int)strlen(s1) + x + 4 > TBUFL) sprintf(sp,"?String too long"); else sprintf(sp,"%s%s%s",s1,((*s2 == ':') ? "" : " "),s2); if (zsoutl(ZTFILE,s) < 0) tralog = 0; break; case F111: /* 7, "s1 s2: n" */ x = (int)strlen(s2); if (s2[x] == '\n') s2[x] = '\0'; if ((int)strlen(s1) + x + 15 > TBUFL) sprintf(sp,"?String too long"); else sprintf(sp,"%s%s%s: %s",s1,((*s2 == ':') ? "" : " "),s2,ckfstoa(n)); if (zsoutl(ZTFILE,s) < 0) tralog = 0; break; default: sprintf(sp,"?Invalid format for tlog() - %d",f); if (zsoutl(ZTFILE,s) < 0) tralog = 0; } } /* D O X L O G This is the transaction-log writer for BRIEF format. The idea is produce one record (line) per file. Each record has the following delimited fields: Date (yyyymmdd) Time (hh:mm:ss) Action: SEND or RECV File name File size Transfer mode (text, binary, image, labeled, etc). Status: OK or FAILED Free-form comments in doublequotes The default separator is comma. If a field contains the separator, it is enclosed in doublequotes. */ VOID #ifdef CK_ANSIC doxlog(int x, char * fn, CK_OFF_T fs, int fm, int status, char * msg) #else doxlog(x, fn, fs, fm, status, msg) int x; char * fn; CK_OFF_T fs; int fm; int status; char * msg; #endif /* CK_ANSIC */ /* doxlog */ { extern int tlogsep; char sep[2]; char buf[CKMAXPATH+256], * bufp; char tmpbuf[32]; char * s, * p; int len, left, ftp = 0, k; if (!tralog) return; /* If no transaction log, don't */ if (!fn) fn = ""; /* Protect against null pointers */ if (!msg) msg = ""; if (x & W_FTP) ftp++; sep[0] = (char) tlogsep; sep[1] = NUL; if (!sep[0]) sep[0] = ','; bufp = buf; left = sizeof(buf); debug(F101,"XXX doxlog left 1","",left); p = zzndate(); /* Date */ ckmakmsg(buf, left, p ? p : "00000000", sep, NULL, NULL); bufp += 9; left -= 9; debug(F111,"XXX doxlog left 2",buf,left); ztime(&p); ckstrncpy(bufp,p+11,left); bufp += 8; left -= 8; debug(F111,"XXX doxlog left 3",buf,left); if (ftp) { if (!(x & (W_SEND|W_RECV))) return; s = (x & W_SEND) ? "PUT" : "GET"; k = 3; } else { s = (x & W_SEND) ? "SEND" : "RECV"; k = 4; } ckmakmsg(bufp,left,sep,s,sep,NULL); bufp += k + 2; left -= (k + 2); debug(F111,"XXX doxlog left 4",buf,left); s = ""; if (ckstrchr(fn,sep[0])) /* Filename */ s = "\""; ckmakmsg(bufp,left,s,fn,s,sep); sprintf(tmpbuf,"%s",ckfstoa(fs)); /* Size */ ckstrncat(buf,tmpbuf,CKMAXPATH); ckstrncat(buf,sep,CKMAXPATH); debug(F110,"doxlog 4",buf,0); #ifdef NOICP /* Transfer mode */ ckstrncpy(tmpbuf, (binary ? "binary" : "text"), TMPBUFSIZ); #else ckstrncpy(tmpbuf,gfmode(fm,0),TMPBUFSIZ); #endif /* NOICP */ if (ckstrchr(tmpbuf,sep[0])) { /* Might contain spaces */ ckstrncat(buf,"\"",CKMAXPATH); ckstrncat(buf,tmpbuf,CKMAXPATH); ckstrncat(buf,"\"",CKMAXPATH); } else ckstrncat(buf,tmpbuf,CKMAXPATH); ckstrncat(buf,sep,CKMAXPATH); debug(F110,"doxlog 5",buf,0); ckstrncat(buf, status ? "FAILED" : "OK",CKMAXPATH); len = strlen(buf); left = CKMAXPATH+256 - len; if (left < 2) fatal("doxlog buffer overlow"); debug(F111,"XXX doxlog left 5",buf,left); debug(F110,"doxlog buf 1", buf, len); s = buf + len; if (status == 0 && left > 32) { long cps = 0L; #ifdef GFTIMER debug(F101,"DOXLOG fpxfsecs","",(long)(fpxfsecs * 1000)); if (fpxfsecs) cps = (long)((CKFLOAT) fs / fpxfsecs); sprintf(s,"%s\"%0.3fsec %ldcps\"",sep,fpxfsecs,cps); #else if (xfsecs) cps = fs / xfsecs; sprintf(s,"%s\"%ldsec %ldcps\"",sep,xfsecs,cps); #endif /* GFTIMER */ } else if ((int)strlen(msg) + 4 < left) { sprintf(s,"%s\"%s\"",sep,msg); } debug(F111,"XXX doxlog left 5",buf,left); debug(F110,"doxlog 5",buf,0); x = zsoutl(ZTFILE,buf); debug(F101,"doxlog zsoutl","",x); if (x < 0) tralog = 0; } #endif /* TLOG */ #ifndef MAC /* The rest of this file is for all implementations but the Macintosh. */ #ifdef CK_CURSES static int repaint = 0; /* Transfer display needs repainting */ #endif /* CK_CURSES */ #ifndef NOXFER /* C H K I N T -- Check for console interrupts */ /* Used during file transfer in local mode only: . If user has not touched the keyboard, returns 0 with no side effects. . If user typed S or A (etc, see below) prints status message and returns 0. . If user typed X or F (etc, see below) returns 0 with cxseen set to 1. . If user typed Z or B (etc, see below) returns 0 with czseen set to 1. . If user typed E or C (etc, see below) returns -1. */ int chkint() { int ch, cn, ofd; long zz; if (!xfrint) return(0); if ((!local) || (quiet)) return(0); /* Only do this if local & not quiet */ #ifdef datageneral if (con_reads_mt) /* if conint_mt task is active */ if (conint_avl) { /* and there's an interrupt pending */ cn = 1; /* process it */ ch = conint_ch; conint_avl = 0; /* turn off flag so conint_mt can */ } else /* proceed */ return(0); else /* if conint_mt not active */ if ((ch = coninc(2)) < 0) /* try to get char manually */ return(0); /* I/O error, or no data */ else /* if successful, set cn so we */ cn = 1; /* know we got one */ debug(F101,"chkint got keyboard character",ch,cn); #else /* !datageneral */ #ifdef NTSIG { extern int TlsIndex; struct _threadinfo * threadinfo; threadinfo = (struct _threadinfo *) TlsGetValue(TlsIndex); if (threadinfo) { if (!WaitSem(threadinfo->DieSem,0)) return -1; /* Cancel Immediately */ } } #endif /* NTSIG */ cn = conchk(); /* Any input waiting? */ debug(F101,"conchk","",cn); if (cn < 1) return(0); ch = coninc(5) ; debug(F101,"coninc","",ch); if (ch < 0) return(0); #endif /* datageneral */ ch &= 0177; switch (ch) { case 'A': case 'a': case 0001: /* Status report */ case 'S': case 's': if (fdispla != XYFD_R && fdispla != XYFD_S && fdispla != XYFD_N) return(0); /* Only for serial, simple or none */ ofd = fdispla; /* [MF] Save file display type */ if (fdispla == XYFD_N) fdispla = XYFD_R; /* [MF] Pretend serial if no display */ xxscreen(SCR_TN,0,0l,"Status report:"); xxscreen(SCR_TN,0,0l," file type: "); if (binary) { switch(binary) { case XYFT_L: xxscreen(SCR_TZ,0,0l,"labeled"); break; case XYFT_I: xxscreen(SCR_TZ,0,0l,"image"); break; case XYFT_U: xxscreen(SCR_TZ,0,0l,"binary undefined"); break; default: case XYFT_B: xxscreen(SCR_TZ,0,0l,"binary"); break; } } else { #ifdef NOCSETS xxscreen(SCR_TZ,0,0l,"text"); #else xxscreen(SCR_TU,0,0l,"text, "); if (tcharset == TC_TRANSP || xfrxla == 0) { xxscreen(SCR_TZ,0,0l,"transparent"); } else { if (what & W_SEND) { xxscreen(SCR_TZ,0,0l,tcsinfo[tcharset].keyword); xxscreen(SCR_TU,0,0l," => "); xxscreen(SCR_TZ,0,0l,fcsinfo[fcharset].keyword); } else { xxscreen(SCR_TZ,0,0l,fcsinfo[fcharset].keyword); xxscreen(SCR_TU,0,0l," => "); xxscreen(SCR_TZ,0,0l,tcsinfo[tcharset].keyword); } } #endif /* NOCSETS */ } xxscreen(SCR_QE,0,filcnt," file number"); if (fsize) xxscreen(SCR_QE,0,fsize," size"); xxscreen(SCR_QE,0,ffc," characters so far"); if (fsize > 0L) { #ifdef CK_RESEND zz = what & W_SEND ? sendstart : what & W_RECV ? rs_len : 0; zz = ( (ffc + zz) * 100L ) / fsize; #else zz = ( ffc * 100L ) / fsize; #endif /* CK_RESEND */ xxscreen(SCR_QE,0,zz, " percent done"); } if (bctu == 4) { /* Block check */ xxscreen(SCR_TU,0,0L," block check: "); xxscreen(SCR_TZ,0,0L,"blank-free-2"); } else xxscreen(SCR_QE,0,(long)bctu, " block check"); xxscreen(SCR_QE,0,(long)rptflg," compression"); xxscreen(SCR_QE,0,(long)ebqflg," 8th-bit prefixing"); xxscreen(SCR_QE,0,(long)lscapu," locking shifts"); if (!network) xxscreen(SCR_QE,0, speed, " speed"); if (what & W_SEND) xxscreen(SCR_QE,0,spsiz, " packet length"); else if (what & W_RECV || what & W_REMO) xxscreen(SCR_QE,0,urpsiz," packet length"); xxscreen(SCR_QE,0,wslots, " window slots"); fdispla = ofd; /* [MF] Restore file display type */ return(0); case 'B': case 'b': case 0002: /* Cancel batch */ case 'Z': case 'z': case 0032: czseen = 1; interrupted = 1; xxscreen(SCR_ST,ST_MSG,0l, (((what & W_RECV) && (wslots > 1)) ? "Canceling batch, wait... " : "Canceling batch... ") ); return(0); case 'F': case 'f': case 0006: /* Cancel file */ case 'X': case 'x': case 0030: cxseen = 1; interrupted = 1; xxscreen(SCR_ST,ST_MSG,0l, (((what & W_RECV) && (wslots > 1)) ? "Canceling file, wait... " : "Canceling file... ") ); return(0); case 'R': case 'r': case 0022: /* Resend packet */ case 0015: case 0012: #ifdef STREAMING if (streaming) return(0); #endif /* STREAMING */ xxscreen(SCR_ST,ST_MSG,0l,"Resending packet... "); numerrs++; resend(winlo); return(0); #ifdef datageneral case '\03': /* We're not trapping ^C's with */ trap(0); /* signals, so we check here */ #endif /* datageneral */ case 'C': case 'c': /* Ctrl-C */ #ifndef datageneral case '\03': #endif /* datageneral */ case 'E': case 'e': /* Send error packet */ case 0005: interrupted = 1; return(-1); #ifdef CK_CURSES case 0014: /* Ctrl-L to refresh screen */ case 'L': case 'l': /* Also accept L (upper, lower) */ case 0027: /* Ctrl-W synonym for VMS & Ingres */ repaint = 1; return(0); #endif /* CK_CURSES */ case 'T': case 't': /* Turn on debug-log timestamps */ #ifdef DEBUG { extern int debtim; if (ch == 'T') { debtim = 1; xxscreen(SCR_ST,ST_MSG,0l, "Debug timestamps On... "); } else { debtim = 1; xxscreen(SCR_ST,ST_MSG,0l, "Debug timestamps Off... "); } } #endif /* DEBUG */ return(0); case 'D': #ifdef DEBUG if (!deblog) { debopn("debug.log",0); if (deblog) { xxscreen(SCR_ST,ST_MSG,0l,"debug.log open... "); } else { xxscreen(SCR_ST,ST_MSG,0l,"debug.log open FAILED... "); } } else { xxscreen(SCR_ST,ST_MSG,0l,"Debug log On... "); } if (deblog) debok = 1; #endif /* DEBUG */ return(0); case 'd': /* Turn off debugging */ #ifdef DEBUG if (deblog) xxscreen(SCR_ST,ST_MSG,0l,"Debug log Off... "); debok = 0; #endif /* DEBUG */ return(0); default: /* Anything else, print message */ intmsg(1L); return(0); } } /* I N T M S G -- Issue message about terminal interrupts */ VOID #ifdef CK_ANSIC intmsg(long n) #else intmsg(n) long n; #endif /* CK_ANSIC */ /* intmsg */ { #ifdef CK_NEED_SIG char buf[80]; #endif /* CK_NEED_SIG */ if (!displa || quiet) /* Not if we're being quiet */ return; if (server && (!srvdis || n > -1L)) /* Special for server */ return; #ifdef CK_NEED_SIG buf[0] = NUL; /* Keep compilers happy */ #endif /* CK_NEED_SIG */ #ifndef OXOS #ifdef SVORPOSIX conchk(); /* Clear out pending escape-signals */ #endif /* SVORPOSIX */ #endif /* ! OXOS */ #ifdef VMS conres(); /* So Ctrl-C will work */ #endif /* VMS */ if ((!server && n == 1L) || (server && n < 0L)) { #ifdef CK_NEED_SIG if (xfrint) { ckmakmsg(buf, 80, "Type escape character (", dbchr(escape), ") followed by:", NULL ); xxscreen(SCR_TN,0,0l,buf); } #endif /* CK_NEED_SIG */ if (xfrint) { if (protocol == PROTO_K) { xxscreen(SCR_TN,0,0l,"X to cancel file, CR to resend current packet"); xxscreen(SCR_TN,0,0l,"Z to cancel group, A for status report"); xxscreen(SCR_TN,0,0l,"E to send Error packet, Ctrl-C to quit immediately: "); } else { xxscreen(SCR_TN,0,0l,"Ctrl-C to cancel file transfer: "); } } else { xxscreen(SCR_TN,0,0l,"Transfer interruption disabled. "); } } else xxscreen(SCR_TU,0,0l," "); } #ifndef NODISPLAY static int newdpy = 0; /* New display flag */ static char fbuf[80]; /* Filename buffer */ static char abuf[80]; /* As-name buffer */ static char a2buf[80]; /* Second As-name buffer */ static CK_OFF_T oldffc = 0L; static CK_OFF_T dots = 0L; static int hpos = 0; static VOID /* Initialize Serial or CRT display */ dpyinit() { int m = 0, n = 0; char * s = ""; newdpy = 0; /* Don't do this again */ oldffc = (CK_OFF_T)0; /* Reset this */ dots = (CK_OFF_T)0; /* and this.. */ oldcps = cps = 0L; conoll(""); /* New line */ if (what & W_SEND) s = "Sending: "; /* Action */ else if (what & W_RECV) s = "Receiving: "; n = (int)strlen(s) + (int)strlen(fbuf); conol(fbuf); m = (int)strlen(abuf) + 4; if (n + m > cmd_cols) { conoll(""); n = 0; } else n += m; if (*abuf) { conol(" => "); conol(abuf); } m = (int)strlen(a2buf) + 4; if (n + m > cmd_cols) { conoll(""); n = 0; } else n += m; if (*a2buf) { conol(" => "); conol(a2buf); } *fbuf = NUL; *abuf = NUL; *a2buf = NUL; conoll(""); if (fsize > (CK_OFF_T)-1) { /* Size */ sprintf(fbuf,"Size: %s, Type: ",ckfstoa(fsize)); /* SAFE (80) */ conol(fbuf); *fbuf = NUL; } else conol("Size: unknown, Type: "); if (binary) { /* Type */ switch(binary) { case XYFT_L: conol("labeled"); break; case XYFT_I: conol("image"); break; case XYFT_U: conol("binary undefined"); break; default: case XYFT_B: conol("binary"); break; } } else { #ifdef NOCSETS conol("text"); #else conol("text, "); if (tcharset == TC_TRANSP || xfrxla == 0) { conol("transparent"); } else { if (what & W_SEND) { conol(fcsinfo[fcharset].keyword); conol(" => "); conol(tcsinfo[tcharset].keyword); } else { conol(tcsinfo[tcharset].keyword); conol(" => "); conol(fcsinfo[fcharset].keyword); } } #endif /* NOCSETS */ } #ifdef STREAMING if (streaming) conol(", STREAMING"); #endif /* STREAMING */ conoll(""); if (fdispla == XYFD_S) { /* CRT field headings */ /* Define CK_CPS to show current transfer rate. Leave it undefined to show estimated time remaining. Estimated-time-remaining code from Andy Fyfe, not tested on pathological cases. */ #define CK_CPS #ifdef CK_CPS conoll(" File Percent Packet"); conoll(" Bytes Done CPS Length"); #else conoll(" File Percent Secs Packet"); conoll(" Bytes Done Left Length"); #endif /* CK_CPS */ newdpy = 0; } hpos = 0; } /* showpkt(c) c = completion code: 0 means transfer in progress, nonzero means it's done. Show the file transfer progress counter and perhaps verbose packet type. */ VOID #ifdef CK_ANSIC showpkt(char c) #else showpkt(c) char c; #endif /* CK_ANSIC */ /* showpkt */ { #ifndef GFTIMER long et; /* Elapsed time, entire batch */ #endif /* GFTIMER */ CK_OFF_T howfar; /* How far into file */ long pd; /* Percent done, this file */ long tp; /* Transfer rate, entire batch */ long ps; /* Packet size, current packet */ CK_OFF_T mytfc; /* Local copy of byte counter */ #ifdef GFTIMER CKFLOAT tnow; #endif /* GFTIMER */ if (newdpy) /* Put up filenames, etc, */ dpyinit(); /* if they're not there already. */ howfar = ffc; /* How far */ /* Calculate CPS rate even if not displaying on screen for use in file transfer statistics. */ #ifdef GFTIMER tnow = gftimer(); /* Time since we started */ ps = (what & W_RECV) ? rpktl : spktl; /* Packet size */ #ifdef CK_RESEND if (what & W_SEND) /* In case we didn't start at */ howfar += sendstart; /* the beginning... */ else if (what & W_RECV) howfar += rs_len; #endif /* CK_RESEND */ pd = -1; /* Percent done. */ if (c == NUL) { /* Still going, figure % done */ if (!fsize) return; /* Empty file, don't bother */ pd = (fsize > 99) ? (howfar / (fsize / (CK_OFF_T)100)) : 0; if (pd > 100) pd = 100; /* Expansion */ } if (c != NUL) if (!cxseen && !discard && !czseen) pd = 100; /* File complete, so 100%. */ mytfc = (pd < 100) ? tfc + ffc : tfc; /* CPS */ tp = (long)((tnow > 0.0) ? (CKFLOAT) mytfc / tnow : 0); if (c && (tp == 0)) tp = ffc; cps = tp; /* Set global variable */ if (cps > peakcps && /* Peak transfer rate */ ((what & W_SEND && spackets > wslots + 4) || (!(what & W_SEND) && spackets > 10))) { peakcps = cps; } #else /* Not GFTIMER */ et = gtimer(); /* Elapsed time */ ps = (what & W_RECV) ? rpktl : spktl; /* Packet length */ #ifdef CK_RESEND if (what & W_SEND) /* And if we didn't start at */ howfar += sendstart; /* the beginning... */ else if (what & W_RECV) howfar += rs_len; #endif /* CK_RESEND */ pd = -1; /* Percent done. */ if (c == NUL) { /* Still going, figure % done */ if (fsize == 0L) return; /* Empty file, don't bother */ pd = (fsize > 99) ? (howfar / (fsize / (CK_OFF_T)100)) : 0; if (pd > 100) pd = 100; /* Expansion */ } if (c != NUL) if (!cxseen && !discard && !czseen) pd = 100; /* File complete, so 100%. */ #ifndef CK_CPS /* fsecs = time (from gtimer) that this file started (set in sfile()). Rate so far is ffc / (et - fsecs), estimated time for remaining bytes is (fsize - ffc) / (ffc / (et - fsecs)). */ tp = (howfar > 0) ? (fsize - howfar) * (et - fsecs) / howfar : 0; #endif /* CK_CPS */ #ifdef CK_CPS mytfc = (pd < 100) ? tfc + ffc : tfc; tp = (et > 0) ? mytfc / et : 0; /* Transfer rate */ if (c && (tp == 0)) /* Watch out for subsecond times */ tp = ffc; cps = tp; /* Set global variable */ if (cps > peakcps && /* Peak transfer rate */ ((what & W_SEND && spackets > wslots + 4) || (!(what & W_SEND) && spackets > 10))) { peakcps = cps; } #endif /* CK_CPS */ #endif /* GFTIMER */ if (fdispla == XYFD_S) { /* CRT display */ char buffer[128]; /* These sprintfs should be safe until we have 32-digit numbers */ if (pd > -1L) sprintf(buffer, "%c%9s%5ld%%%8ld%8ld ", CR,ckfstoa(howfar),pd,tp,ps); else sprintf(buffer, "%c%9s %8ld%8ld ", CR,ckfstoa(howfar),tp,ps); conol(buffer); hpos = 31; } else if (fdispla == XYFD_R) { /* SERIAL */ long i, k; if (howfar - oldffc < 1024) /* Update display every 1K */ return; oldffc = howfar; /* Time for new display */ k = (howfar / 1024L) - dots; /* How many K so far */ for (i = 0L; i < k; i++) { if (hpos++ > (cmd_cols - 3)) { /* Time to wrap? */ conoll(""); hpos = 0; } conoc('.'); /* Print a dot for this K */ dots++; /* Count it */ } } } /* C K S C R E E N -- Screen display function */ /* ckscreen(f,c,n,s) f - argument descriptor c - a character or small integer n - a long integer s - a string. and global fdispla = SET FILE DISPLAY value: XYFD_N = NONE XYFD_R = SERIAL: Dots, etc, works on any terminal, even hardcopy. XYFD_S = CRT: Works on any CRT, writes over current line. XYFD_C = FULLSCREEN: Requires terminal-dependent screen control. XYFD_B = BRIEF: Like SERIAL but only filename & completion status. XYFD_G = GUI; Windows GUI, same behavior as FULLSCREEN */ VOID #ifdef CK_ANSIC ckscreen(int f, char c,CK_OFF_T n,char *s) #else ckscreen(f,c,n,s) int f; char c; CK_OFF_T n; char *s; #endif /* CK_ANSIC */ /* screen */ { char buf[80]; int len; /* Length of string */ #ifdef UNIX #ifndef NOJC int obg; _PROTOTYP( VOID conbgt, (int) ); #endif /* NOJC */ #endif /* UNIX */ int ftp = 0; ftp = (what & W_FTP) ? 1 : 0; /* FTP or Kermit? */ if (!local && !ftp) /* In remote mode - don't do this */ return; if (!s) s = ""; if (!fxd_inited) /* Initialize if necessary */ fxdinit(fdispla); #ifdef UNIX #ifndef NOJC obg = backgrd; /* Previous background status */ conbgt(1); /* See if running in background */ if (!backgrd && obg) { /* Just came into foreground? */ concb((char)escape); /* Put console back in CBREAK mode */ setint(); /* Restore interrupts */ } #endif /* NOJC */ #endif /* UNIX */ if ((f != SCR_WM) && (f != SCR_EM)) /* Always update warnings & errors */ if (!displa || (backgrd && bgset) || fdispla == XYFD_N || (server && !srvdis) ) return; #ifdef VMS if (f == SCR_FN) /* VMS - shorten the name */ s = zrelname(s,zgtdir()); #endif /* VMS */ if (dest == DEST_S) /* SET DESTINATION SCREEN */ return; /* would interfere... */ #ifdef KUI if (fdispla == XYFD_G) { /* If gui display selected */ screeng(f,c,n,s); /* call the gui version */ return; } #endif /* KUI */ #ifdef CK_CURSES if (fdispla == XYFD_C) { /* If fullscreen display selected */ screenc(f,c,n,s); /* call the fullscreen version */ return; } #endif /* CK_CURSES */ len = (int)strlen(s); /* Length of string */ switch (f) { /* Handle our function code */ case SCR_FN: /* Filename */ if (fdispla == XYFD_B) { #ifdef NEWFTP if (ftp) printf(" %s %s", what & W_SEND ? "PUT" : "GET", s); else #endif /* NEWFTP */ printf(" %s %s", what & W_SEND ? "SEND" : "RECV", s); #ifdef UNIX fflush(stdout); #endif /* UNIX */ return; } #ifdef MAC conoll(""); conol(s); conoc(SP); hpos = len + 1; #else ckstrncpy(fbuf,s,80); abuf[0] = a2buf[0] = NUL; newdpy = 1; /* New file so refresh display */ #endif /* MAC */ return; case SCR_AN: /* As-name */ if (fdispla == XYFD_B) { #ifdef COMMENT printf("(as %s) ",s); #endif /* COMMENT */ return; } #ifdef MAC if (hpos + len > 75) { conoll(""); hpos = 0; } conol("=> "); conol(s); if ((hpos += (len + 3)) > 78) { conoll(""); hpos = 0; } #else if (abuf[0]) { ckstrncpy(a2buf,s,80); } else { ckstrncpy(abuf,s,80); } #endif /* MAC */ return; case SCR_FS: /* File-size */ if (fdispla == XYFD_B) { printf(" (%s) (%s byte%s)", #ifdef NOICP (binary ? "binary" : "text") #else gfmode(binary,0) #endif /* NOICP */ , ckfstoa(n), n == 1 ? "" : "s"); #ifdef UNIX fflush(stdout); #endif /* UNIX */ return; } #ifdef MAC sprintf(buf,", Size: %s",ckfstoa(n)); conoll(buf); hpos = 0; #endif /* MAC */ return; case SCR_XD: /* X-packet data */ if (fdispla == XYFD_B) return; #ifdef MAC conoll(""); conoll(s); hpos = 0; #else ckstrncpy(fbuf,s,80); abuf[0] = a2buf[0] = NUL; #endif /* MAC */ return; case SCR_ST: /* File status */ switch (c) { case ST_OK: /* Transferred OK */ showpkt('Z'); /* Update numbers one last time */ if (fdispla == XYFD_B) { #ifdef GFTIMER if (fpxfsecs) printf(": OK (%0.3f sec, %ld cps)",fpxfsecs, (long)((CKFLOAT)ffc / fpxfsecs)); #else if (xfsecs) printf(": OK (%d sec, %ld cps)",xfsecs,ffc/xfsecs); #endif /* GFTIMER */ printf("\n"); return; } if ((hpos += 5) > 78) conoll(""); /* Wrap screen line. */ conoll(" [OK]"); hpos = 0; /* Print OK message. */ if (fdispla == XYFD_S) { /* We didn't show Z packet when */ conoc('Z'); /* it came, so show it now. */ hpos = 1; } return; case ST_DISC: /* Discarded */ if (fdispla == XYFD_B) { printf(": DISCARDED\n"); return; } if ((hpos += 12) > 78) conoll(""); conoll(" [discarded]"); hpos = 0; return; case ST_INT: /* Interrupted */ if (fdispla == XYFD_B) { printf(": INTERRUPTED\n"); return; } if ((hpos += 14) > 78) conoll(""); conoll(" [interrupted]"); hpos = 0; return; case ST_SIM: if (fdispla == XYFD_B) { if (n == SKP_XNX) printf(": WOULD BE TRANSFERRED (New file)\n"); else if (n == SKP_XUP) printf(": WOULD BE TRANSFERRED (Remote file older)\n"); else if (n == SKP_SIM) printf(": WOULD BE TRANSFERRED\n"); else if (n > 0 && n < nskreason) printf(": SKIPPED (%s)\n",skreason[n]); else printf(": SKIPPED\n"); return; } else if (fdispla == XYFD_S) { if (fdispla == XYFD_S && fbuf[0]) { /* CRT display */ conoll(""); /* New line */ if (what & W_SEND) conol("Would Send: "); /* Action */ else if (what & W_RECV) conol("Would Receive: "); conol(fbuf); if (*abuf) conol(" => "); conol(abuf); /* Names */ if (*a2buf) conol(" => "); conol(a2buf); /* Names */ *fbuf = NUL; *abuf = NUL; *a2buf = NUL; } conoll(" [simulated]"); return; } if ((hpos += 10) > 78) conoll(""); conol(" [simulated]"); hpos = 0; return; case ST_SKIP: /* Skipped */ if (fdispla == XYFD_B) { if (n == SKP_XNX) printf(": WOULD BE TRANSFERRED (New file)\n"); else if (n == SKP_XUP) printf(": WOULD BE TRANSFERRED (Remote file older)\n"); else if (n == SKP_SIM) printf(": WOULD BE TRANSFERRED\n"); else if (n > 0 && n < nskreason) printf(": SKIPPED (%s)\n",skreason[n]); else printf(": SKIPPED\n"); return; } else if (fdispla == XYFD_S) { if (fdispla == XYFD_S && fbuf[0]) { /* CRT display */ conoll(""); /* New line */ if (what & W_SEND) conol("Sending: "); /* Action */ else if (what & W_RECV) conol("Receiving: "); conol(fbuf); if (*abuf) conol(" => "); conol(abuf); /* Names */ if (*a2buf) conol(" => "); conol(a2buf); /* Names */ *fbuf = NUL; *abuf = NUL; *a2buf = NUL; } conoll(" [skipped]"); return; } if ((hpos += 10) > 78) conoll(""); conol(" "); conol(fbuf); conoll(" [skipped]"); hpos = 0; return; case ST_ERR: /* Error */ if (fdispla == XYFD_B) { printf(": ERROR: %s\n",s); return; } conoll(""); conol("Error: "); conoll(s); hpos = 0; return; case ST_MSG: /* Message */ #ifdef NEWFTP if (fdispla == XYFD_B) { if (ftp && ftp_deb) printf(": MESSAGE: %s\n",s); return; } #endif /* NEWFTP */ conoll(""); conol("Message: "); conoll(s); hpos = 0; return; case ST_REFU: /* Refused */ if (fdispla == XYFD_B) { printf(": REFUSED\n"); return; } else if (fdispla == XYFD_S) { if (fdispla == XYFD_S && fbuf[0]) { /* CRT display */ conoll(""); /* New line */ if (what & W_SEND) conol("Sending: "); /* Action */ else if (what & W_RECV) conol("Receiving: "); conol(fbuf); if (*abuf) conol(" => "); conol(abuf); /* Names */ if (*a2buf) conol(" => "); conol(a2buf); /* Names */ *fbuf = NUL; *abuf = NUL; *a2buf = NUL; conoll(""); } conol("Refused: "); conoll(s); return; } conoll(""); conol("Refused: "); conoll(s); hpos = 0; return; case ST_INC: /* Incomplete */ if (fdispla == XYFD_B) { printf(": INCOMPLETE\n"); return; } if ((hpos += 12) > 78) conoll(""); conoll(" [incomplete]"); hpos = 0; return; default: conoll("*** screen() called with bad status ***"); hpos = 0; return; } #ifdef MAC case SCR_PN: /* Packet number */ if (fdispla == XYFD_B) { return; } ckmakmsg(buf,80,s,": ",ckltoa(n),NULL); conol(buf); hpos += (int)strlen(buf); return; #endif /* MAC */ case SCR_PT: /* Packet type or pseudotype */ if (fdispla == XYFD_B) return; if (c == 'Y') return; /* Don't bother with ACKs */ if (c == 'D') { /* In data transfer phase, */ showpkt(NUL); /* show progress. */ return; } #ifndef AMIGA if (hpos++ > 77) { /* If near right margin, */ conoll(""); /* Start new line */ hpos = 0; /* and reset counter. */ } #endif /* AMIGA */ if (c == 'Z' && fdispla == XYFD_S) return; else conoc(c); /* Display the packet type. */ #ifdef AMIGA if (c == 'G') conoll(""); /* New line after G packets */ #endif /* AMIGA */ return; case SCR_TC: /* Transaction complete */ if (xfrbel) bleep(BP_NOTE); if (fdispla == XYFD_B) { /* Brief display... */ if (filcnt > 1) { long fx; fx = filcnt - filrej; printf(" SUMMARY: %ld file%s", fx, ((fx == 1) ? "" : "s")); printf(", %s byte%s", ckfstoa(tfc), ((tfc == 1) ? "" : "s")); #ifdef GFTIMER printf(", %0.3f sec, %ld cps", fptsecs, tfcps); #else printf(", %ld sec, %ld cps", tsecs, tfcps); #endif /* GFTIMER */ printf(".\n"); } } else { conoll(""); } #ifdef UNIX fflush(stdout); #endif /* UNIX */ return; case SCR_EM: /* Error message */ if (fdispla == XYFD_B) { printf(" ERROR: %s\n",s); return; } conoll(""); conoc('?'); conoll(s); hpos = 0; return; case SCR_WM: /* Warning message */ if (fdispla == XYFD_B) { printf(" WARNING: %s\n",s); return; } conoll(""); conoll(s); hpos = 0; return; case SCR_MS: /* Message from other Kermit */ if (fdispla == XYFD_B) { printf(" MESSAGE: %s\n",s); return; } conoll(""); conoll(s); hpos = 0; return; case SCR_TU: /* Undelimited text */ if (fdispla == XYFD_B) return; if ((hpos += len) > 77) { conoll(""); hpos = len; } conol(s); return; case SCR_TN: /* Text delimited at beginning */ if (fdispla == XYFD_B) return; conoll(""); conol(s); hpos = len; return; case SCR_TZ: /* Text delimited at end */ if (fdispla == XYFD_B) return; if ((hpos += len) > 77) { conoll(""); hpos = len; } conoll(s); return; case SCR_QE: /* Quantity equals */ if (fdispla == XYFD_B) return; ckmakmsg(buf,80,s,": ",ckltoa(n),NULL); conoll(buf); hpos = 0; return; case SCR_CW: /* Close fullscreen window */ return; /* No window to close */ case SCR_CD: return; default: conoll("*** screen() called with bad object ***"); hpos = 0; return; } } #endif /* NODISPLAY */ /* E R M S G -- Nonfatal error message */ /* Should be used only for printing the message text from an Error packet. */ VOID ermsg(msg) char *msg; { /* Print error message */ debug(F110,"ermsg",msg,0); if (local) xxscreen(SCR_EM,0,0L,msg); tlog(F110,"Protocol Error:",msg,0L); } #endif /* NOXFER */ VOID setseslog(x) int x; { seslog = x; #ifdef KUI KuiSetProperty(KUI_TERM_CAPTURE,x,0); #endif /* KUI */ } VOID doclean(fc) int fc; { /* General cleanup */ #ifdef OS2ORUNIX extern int ttyfd; #endif /* OS2ORUNIX */ extern int keep; extern int exithangup; #ifndef NOXFER extern char filnam[]; #endif /* NOXFER */ #ifndef NOICP int x; if (fc > 0) dostop(); /* Stop all command files and end macros */ #endif /* NOICP */ #ifndef NOXFER if (pktlog) { *pktfil = '\0'; pktlog = 0; zclose(ZPFILE); } #endif /* NOXFER */ if (seslog) { *sesfil = '\0'; setseslog(0); zclose(ZSFILE); } #ifdef TLOG if (tralog) { tlog(F100,"Transaction Log Closed","",0L); *trafil = '\0'; tralog = 0; zclose(ZTFILE); } #endif /* TLOG */ debug(F100,"doclean calling dologend","",0); dologend(); /* End current log record if any */ #ifdef COMMENT if (dialog) { /* If connection log open */ dialog = 0; *diafil = '\0'; /* close it. */ zclose(ZDIFIL); } #endif /* COMMENT */ #ifndef NOICP #ifndef NOSPL zclose(ZRFILE); /* READ and WRITE files, if any. */ zclose(ZWFILE); #ifndef NOXFER zclose(ZIFILE); /* And other files too */ x = chkfn(ZOFILE); /* Download in progress? */ debug(F111,"doclean chkfn ZOFILE",filnam,x); debug(F111,"doclean keep","",keep); zclose(ZOFILE); /* Close output file */ if (x > 0 && !keep) { /* If it was being downloaded */ if (filnam[0]) x = zdelet(filnam); /* Delete if INCOMPLETE = DISCARD */ debug(F111,"doclean download filename",filnam,x); } #endif /* NOXFER */ zclose(ZSYSFN); zclose(ZMFILE); if (fc < 1) { /* RESETing, not EXITing */ #ifdef DEBUG if (deblog) { /* Close the debug log. */ *debfil = '\0'; deblog = 0; zclose(ZDFILE); } #endif /* DEBUG */ return; } #endif /* NOSPL */ #endif /* NOICP */ #ifndef NOLOCAL debug(F101,"doclean exithangup","",exithangup); if (local && exithangup) { /* Close communication connection */ extern int haslock; int x; x = ttchk(); debug(F101,"doclean ttchk()","",x); #ifdef OS2ORUNIX debug(F101,"doclean ttyfd","",ttyfd); #endif /* OS2ORUNIX */ if (x >= 0 #ifdef OS2 || ttyfd != -1 #else #ifdef UNIX || haslock /* Make sure we get lockfile! */ || (!network && ttyfd > -1) #endif /* UNIX */ #endif /* OS2 */ ) { extern int wasclosed, whyclosed; debug(F100,"doclean hanging up and closing","",0); if (msgflg) { #ifdef UNIX fflush(stdout); #endif /* UNIX */ printf("Closing %s...",ttname); } #ifndef NODIAL mdmhup(); /* Hangup the modem??? */ #endif /* NODIAL */ ttclos(0); /* Close external line, if any */ if (msgflg) { printf("OK\n"); #ifdef UNIX fflush(stdout); #endif /* UNIX */ } if (wasclosed) { whyclosed = WC_CLOS; #ifndef NOSPL if (nmac) { /* Any macros defined? */ int k; /* Yes */ k = mlook(mactab,"on_close",nmac); /* Look this up */ if (k >= 0) { /* If found, */ wasclosed = 0; /* printf("ON_CLOSE DOCLEAN\n"); */ *(mactab[k].kwd) = NUL; /* See comment below */ if (dodo(k,ckitoa(whyclosed),0) > -1) /* set it up, */ parser(1); /* and execute it */ } } #endif /* NOSPL */ wasclosed = 0; } } ckstrncpy(ttname,dftty,TTNAMLEN); /* Restore default tty */ local = dfloc; /* And default remote/local status */ } #ifdef DEBUG else if (local) debug(F100,"doclean hangup/close skipped","",0); #endif /* DEBUG */ #endif /* NOLOCAL */ #ifdef NEWFTP ftpbye(); /* If FTP connection open, close it */ #endif /* NEWFTP */ #ifdef IKSD if (inserver) ttclos(0); /* If IKSD, close socket */ #endif /* IKSD */ #ifndef NOSPL /* If a macro named "on_exit" is defined, execute it. Also remove it from the macro table, in case its definition includes an EXIT or QUIT command, which would cause much recursion and would prevent the program from ever actually EXITing. */ if (nmac) { /* Any macros defined? */ int k; /* Yes */ char * cmd = "on_exit"; /* MSVC 2.x compiler error */ k = mlook(mactab,cmd,nmac); /* Look up "on_exit" */ if (k >= 0) { /* If found, */ #ifdef COMMENT /* This makes a mess if ON_EXIT itself executes macros */ *(mactab[k].kwd) = NUL; /* poke its name from the table, */ #else /* Replace the keyword with something that doesn't wreck the */ /* order of the keyword table */ ckstrncpy(mactab[k].kwd,"on_exxx",8); #endif /* COMMENT */ if (dodo(k,"",0) > -1) /* set it up, */ parser(1); /* and execute it */ } } #endif /* NOSPL */ /* Put console terminal back to normal. This is done here because the ON_EXIT macro calls the parser, which meddles with console terminal modes. */ conres(); /* Restore console terminal. */ #ifdef COMMENT /* Should be no need for this, and maybe it's screwing things up? */ connoi(); /* Turn off console interrupt traps */ #endif /* COMMENT */ /* Delete the Startup File if we are supposed to. */ #ifndef NOICP { extern int DeleteStartupFile; debug(F111,"doclean DeleteStartupFile",cmdfil,DeleteStartupFile); if (DeleteStartupFile) { int rc = zdelet(cmdfil); debug(F111,"doclean zdelet",cmdfil,rc); } } #endif /* NOICP */ syscleanup(); /* System-dependent cleanup, last */ } /* D O E X I T -- Exit from the program. */ /* First arg is general, system-independent symbol: GOOD_EXIT or BAD_EXIT. If second arg is -1, take 1st arg literally. If second arg is not -1, work it into the exit code. */ VOID doexit(exitstat,code) int exitstat, code; { extern int x_logged, quitting; #ifdef OS2 extern int display_demo; extern int SysInited; #endif /* OS2 */ #ifdef CK_KERBEROS #ifdef KRB4 extern int krb4_autodel; #endif /* KRB4 */ #ifdef KRB5 extern int krb5_autodel; #endif /* KRB5 */ #endif /* CK_KERBEROS */ #ifdef VMS char envstr[64]; static $DESCRIPTOR(symnam,"CKERMIT_STATUS"); static struct dsc$descriptor_s symval; #endif /* VMS */ int i; #ifdef DEBUG #ifdef USE_LUCACHE extern long lucalls, luhits, xxhits, luloop; extern int lusize; #endif /* USE_LUCACHE */ #ifndef NOSPL extern int cmdstats[]; #endif /* NOSPL */ quitting++; #ifdef OS2 if ( !SysInited ) { static int initing = 0; if ( initing ) exit(253); initing = 1; sysinit(); } #endif /* OS2 */ if (deblog) { #ifdef USE_LUCACHE debug(F101,"lookup cache size","",lusize); debug(F101,"lookup calls ....","",lucalls); debug(F101,"lookup cache hits","",luhits); debug(F101,"lookup start hits","",xxhits); debug(F101,"lookup loop iterations","",luloop); #endif /* USE_LUCACHE */ #ifndef NOSPL for (i = 0; i < 256; i++) { if (cmdstats[i]) debug(F111,"CMSTATS",ckitoa(i),cmdstats[i]); } #endif /* NOSPL */ debug(F101,"doexit exitstat","",exitstat); debug(F101,"doexit code","",code); debug(F101,"doexit xitsta","",xitsta); } #endif /* DEBUG */ #ifdef CK_KERBEROS /* If we are automatically destroying Kerberos credentials on Exit */ /* do it now. */ #ifdef KRB4 if (krb4_autodel == KRB_DEL_EX) { extern struct krb_op_data krb_op; krb_op.version = 4; krb_op.cache = NULL; ck_krb4_destroy(&krb_op); } #endif /* KRB4 */ #ifdef KRB5 if (krb5_autodel == KRB_DEL_EX) { extern struct krb_op_data krb_op; extern char * krb5_d_cc; krb_op.version = 5; krb_op.cache = krb5_d_cc; ck_krb5_destroy(&krb_op); } #endif /* KRB5 */ #endif /* CK_KERBEROS */ #ifndef NOLOCAL #ifdef OS2 if (SysInited) { #ifdef DCMDBUF extern struct cmdptr *cmdstk; #else extern struct cmdptr cmdstk[]; #endif /* DCMDBUF */ extern int tt_status[]; extern int vmode; /* If there is a demo screen to be displayed, display it */ if (display_demo) { demoscrn(VCMD); display_demo = 0; } #ifndef KUI /* This is going to be hideous. If we have a status line */ /* in the command window turn it off before we exit. */ if ( tt_status[VCMD] && vmode == VCMD ) { domac("_clear_statusline","set command statusline off", cmdstk[cmdlvl].ccflgs); delmac("_clear_statusline",1); RequestScreenMutex(-1); VscrnIsDirty(vmode); ReleaseScreenMutex(); while ( IsVscrnDirty(vmode) ) msleep(200); RequestScreenMutex(-1); ReleaseScreenMutex(); } #endif /* KUI */ DialerSend(OPT_KERMIT_EXIT,exitstat); #ifndef KUI debug(F100,"doexit about to msleep","",0); if ( isWin95() ) msleep(250); #endif /* KUI */ } #endif /* OS2 */ #endif /* NOLOCAL */ #ifdef IKSD #ifdef CK_LOGIN if (inserver && x_logged) { #ifndef NOSPL /* If a macro named "on_logout" is defined, execute it. Also remove it from the macro table, in case its definition includes an EXIT or QUIT command, which would cause much recursion and would prevent the program from ever actually EXITing. */ if (nmac) { /* Any macros defined? */ int k; /* Yes */ char * cmd = "on_logout"; /* MSVC 2.x compiler error */ k = mlook(mactab,cmd,nmac); /* Look up "on_logout" */ if (k >= 0) { /* If found, */ *(mactab[k].kwd) = NUL; /* poke its name from the table, */ if (dodo(k,"",0) > -1) /* set it up, */ parser(1); /* and execute it */ } } #endif /* NOSPL */ zvlogout(); } #endif /* CK_LOGIN */ #endif /* IKSD */ debug(F100,"doexit about to doclean","",0); doclean(1); /* Clean up most things */ #ifdef VMS if (code == -1) code = 0; /* Since we set two different items */ sprintf(envstr,"%d", exitstat | code); /* SAFE */ symval.dsc$w_length = (int)strlen(envstr); symval.dsc$a_pointer = envstr; symval.dsc$b_class = DSC$K_CLASS_S; symval.dsc$b_dtype = DSC$K_DTYPE_T; i = 2; /* Store in global table */ #ifdef COMMENT /* Martin Zinser */ LIB$SET_SYMBOL(&symnam, &symval, &i); #else lib$set_symbol(&symnam, &symval, &i); #endif /* COMMENT */ if (exitstat == BAD_EXIT) exitstat = SS$_ABORT | STS$M_INHIB_MSG; if (exitstat == GOOD_EXIT) exitstat = SS$_NORMAL | STS$M_INHIB_MSG; #else /* Not VMS */ if (code != -1) /* Take 1st arg literally */ exitstat |= code; #endif /* VMS */ #ifdef IKSD #ifdef IKSDB debug(F101,"doexit ikdbopen","",ikdbopen); if (ikdbopen && dbfp) { /* If IKSD database open */ int x; x = freeslot(mydbslot); /* Free our slot... */ debug(F101,"doexit freeslot","",x); fclose(dbfp); /* and close it. */ } #endif /* IKSDB */ #endif /* IKSD */ /* We have put this off till the very last moment... */ #ifdef DEBUG if (deblog) { /* Close the debug log. */ debug(F101,"C-Kermit EXIT status","",exitstat); *debfil = '\0'; deblog = 0; zclose(ZDFILE); } #endif /* DEBUG */ #ifdef OS2 _exit(exitstat); /* Exit from C-Kermit (no matter what) */ #else /* OS2 */ exit(exitstat); /* Exit from C-Kermit */ #endif /* OS2 */ } VOID bgchk() { /* Check background status */ if (bgset < 0) { /* They didn't type SET BACKGROUND */ #ifdef VMS /* Set prompt flag based on */ pflag = !batch; /* what we detected at startup. */ #else pflag = !backgrd; #endif /* VMS */ } else { /* Otherwise SET BACKGROUND value */ pflag = (bgset == 0 ? 1 : 0); } #ifndef NOICP /* Message flag on only if at top level, pflag is on, and QUIET is OFF */ if (!xcmdsrc) msgflg = (pflag == 0) ? 0 : !quiet; else msgflg = 0; #else msgflg = 0; #endif /* NOICP */ } /* Set console interrupts */ VOID setint() { /* According to SET COMMAND INTERRUP */ int x = 0; if (cmdint) x |= 1; if (xsuspend) x |= 2; debug(F101,"setint","",x); switch (x) { /* Set the desired combination */ case 0: connoi(); break; /* No interrupts */ case 1: conint(trap,SIG_IGN); break; case 2: conint(SIG_IGN,stptrap); break; case 3: conint(trap,stptrap); break; } bgchk(); /* Check background status */ } #ifdef DEBUG /* D E B U G -- Enter a record in the debugging log */ /* Call with a format, two strings, and a number: f - Format, a bit string in range 0-7. If bit x is on, then argument number x is printed. s1 - String, argument number 1. If selected, printed as is. s2 - String, argument number 2. If selected, printed in brackets. n - Long int, argument 3. If selected, printed preceded by equals sign. f=0 is special: print s1,s2, and interpret n as a char. f=F011 (3) is also special; in this case s2 is interpeted as a counted string that might contain NULs. n is the length. If n is negative, this means the string has been truncated and ".." should be printed after the first n bytes. NUL and LF bytes are printed as "" and "". Globals: deblog: nonzero if debug log open. debok: nonzero if ok to write entries. */ /* WARNING: Don't change DEBUFL without changing sprintf() formats below, accordingly. */ #define DBUFL 4000 /* WARNING: This routine is not thread-safe, especially when Kermit is executing on multiple CPUs -- as different threads write to the same static buffer, the debug statements are all interleaved. To be fixed later... */ static char *dbptr = (char *)0; int #ifdef CK_ANSIC dodebug(int f, char *s1, char *s2, CK_OFF_T n) #else dodebug(f,s1,s2,n) int f; char *s1, *s2; CK_OFF_T n; #endif /* CK_ANSIC */ /* dodebug */ { char *sp; int len1, len2; extern int debtim; #ifdef OS2 extern int SysInited; #endif /* OS2 */ if (!deblog || !debok) return(0); #ifdef COMMENT /* expensive... */ if (!chkfn(ZDFILE)) /* Debug log not open, don't. */ return(0); #endif /* COMMENT */ if (!dbptr) { /* Allocate memory buffer */ dbptr = malloc(DBUFL+4); /* This only happens once */ if (!dbptr) { zclose(ZDFILE); return(0); } } /* This prevents infinite recursion in case we accidentally put a debug() call in this routine, or call another routine that contains debug() calls. From this point on, all returns from this return must be via goto xdebug, which sets deblog back to 1. */ #ifdef OS2 if (SysInited) { if (RequestDebugMutex(30000)) goto xdebug; } #else /* OS2 */ deblog = 0; /* Prevent infinite recursion */ #endif /* OS2 */ if (debtim) { /* Timestamp */ char *tb, tsbuf[48]; ztime(&tb); ckstrncpy(tsbuf,tb,32); if (ztmsec > -1L) { sprintf(tsbuf+19,".%03ld ",ztmsec); /* SAFE */ } else { tsbuf[19] = ':'; tsbuf[20] = SP; tsbuf[21] = NUL; } zsout(ZDFILE,tsbuf+11); } if (!s1) s1="(NULL)"; if (!s2) s2="(NULL)"; len1 = strlen(s1); len2 = strlen(s2); #ifdef COMMENT /* This should work, but it doesn't. So instead we'll cope with overflow via sprintf formats. N.B.: UNFORTUNATELY, this means we have to put constants in the sprintf formats. */ if (f != F011 && (!f || (f & 6))) { /* String argument(s) included? */ x = (int) strlen(s1) + (int) strlen(s2) + 18; if (x > dbufl) { /* Longer than buffer? */ if (dbptr) /* Yes, free previous buffer */ free(dbptr); dbptr = (char *) malloc(x + 2); /* Allocate a new one */ if (!dbptr) { zsoutl(ZDFILE,"DEBUG: Memory allocation failure"); deblog = 0; zclose(ZDFILE); goto xdebug; } else { dbufl = x; sprintf(dbptr,"DEBUG: Buffer expanded to %d\n", x + 18); zsoutl(ZDFILE,dbptr); } } } #endif /* COMMENT */ #ifdef COMMENT /* The aforementioned sprintf() formats were like this: */ if (n > 31 && n < 127) sprintf(sp,"%.100s%.2000s:%c\n",s1,s2,(CHAR) n); else if (n < 32 || n == 127) sprintf(sp,"%.100s%.2000s:^%c\n",s1,s2,(CHAR) ((n+64) & 0x7F)); else if (n > 127 && n < 160) sprintf(sp,"%.100s%.2000s:~^%c\n",s1,s2,(CHAR)((n-64) & 0x7F)); else if (n > 159 && n < 256) sprintf(sp,"%.100s%.2000s:~%c\n",s1,s2,(CHAR) (n & 0x7F)); else sprintf(sp,"%.100s%.2000s:%ld\n",s1,s2,n); /* But, naturally, it turns out these are not portable either, so now we do the stupidest possible thing. */ #endif /* COMMENT */ #ifdef BIGBUFOK /* Need to accept longer strings when debugging authenticated connections */ if (f == F010) { if (len2 + 2 >= DBUFL) s2 = "(string too long)"; } else if (f != F011 && f != F100) { if (len1 > 100) s1 = "(string too long)"; if (len2 + 101 >= DBUFL) s2 = "(string too long)"; } #else if (f != F011) { if (len1 > 100) s1 = "(string too long)"; if (len2 + 101 >= DBUFL) s2 = "(string too long)"; } #endif /* BIGBUFOK */ sp = dbptr; switch (f) { /* Write log record according to format. */ case F000: /* 0 = print both strings, and n as a char. */ if (len2 > 0) { if ((n > 31 && n < 127) || (n > 159 && n < 256)) sprintf(sp,"%s[%s]=%c\n",s1,s2,(CHAR) n); else if (n < 32 || n == 127) sprintf(sp,"%s[%s]=^%c\n",s1,s2,(CHAR) ((n+64) & 0x7F)); else if (n > 127 && n < 160) sprintf(sp,"%s[%s]=~^%c\n",s1,s2,(CHAR)((n-64) & 0x7F)); else sprintf(sp,"%s[%s]=0x%lX\n",s1,s2,(long)n); } else { if ((n > 31 && n < 127) || (n > 159 && n < 256)) sprintf(sp,"%s=%c\n",s1,(CHAR) n); else if (n < 32 || n == 127) sprintf(sp,"%s=^%c\n",s1,(CHAR) ((n+64) & 0x7F)); else if (n > 127 && n < 160) sprintf(sp,"%s=~^%c\n",s1,(CHAR)((n-64) & 0x7F)); else sprintf(sp,"%s=0x%lX\n",s1,(long)n); } if (zsout(ZDFILE,dbptr) < 0) { deblog = 0; zclose(ZDFILE); } #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_DB && ckxlogging) { cksyslog(SYSLG_DB,1,"debug",dbptr,NULL); } #endif /* CKSYSLOG */ break; case F001: /* 1, "=n" */ #ifdef COMMENT /* This was never used */ sprintf(sp,"=%s\n",ckfstoa(n)); #else /* Like F111, but shows number n in hex */ ckmakxmsg(sp,DBUFL, s1, (*s1 ? ":" : ""), s2, (*s2 ? ":" : ""), ckltox(n), "\n", NULL,NULL,NULL,NULL,NULL,NULL ); #endif /* COMMENT */ if (zsout(ZDFILE,dbptr) < 0) { deblog = 0; zclose(ZDFILE); } #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_DB && ckxlogging) { cksyslog(SYSLG_DB,1,"debug",dbptr,NULL); } #endif /* CKSYSLOG */ break; /* This one was never used so (October 2000) we now use it like F011, except in this case we treat s2 as NUL terminated. */ case F010: n = -debxlen; /* This one treats n as the length of the string s2, which may contain NULs. It's good for logging NUL-bearing data in the debug log. */ case F011: { int i, j, contd = 0; char * p = s2, *pbuf = NULL; /* p = source pointer */ int m; /* pbuf = destination pointer */ if (f == F011) { if (n < 0) { /* n = size of source */ n = 0 - n; /* Negative means to add "..." */ contd = 1; } } else { int x, flag = 0; x = strlen(s2); if (n < 0) { flag = 1; n = 0 - n; } if (x < n) n = x; } if (n == 0) /* 0 means do nothing */ goto xdebug; m = DBUFL - 8; /* Get size for interpreted part */ if (n > m) /* Ensure requested size not too big */ n = m; pbuf = dbptr; /* Construction pointer */ i = 0; pbuf[i++] = '['; /* Interpret the string into it */ for (j = 0; j < n && i < m-4; p++,j++) { /* char by char... */ if (*p == LF) { if (i >= m-4) break; pbuf[i++] = '<'; pbuf[i++] = 'L'; pbuf[i++] = 'F'; pbuf[i++] = '>'; continue; } else if (*p == CR) { if (i >= m-4) break; pbuf[i++] = '<'; pbuf[i++] = 'C'; pbuf[i++] = 'R'; pbuf[i++] = '>'; continue; } else if (*p == HT) { if (i >= m-5) break; pbuf[i++] = '<'; pbuf[i++] = 'T'; pbuf[i++] = 'A'; pbuf[i++] = 'B'; pbuf[i++] = '>'; continue; } else if (*p) { pbuf[i++] = *p; continue; } else { if (i >= m-5) break; pbuf[i++] = '<'; pbuf[i++] = 'N'; pbuf[i++] = 'U'; pbuf[i++] = 'L'; pbuf[i++] = '>'; continue; } } if (i < m-2 && (*p || contd)) { pbuf[i++] = '.'; pbuf[i++] = '.'; } pbuf[i++] = ']'; pbuf[i] = NUL; if (zsout(ZDFILE,s1) < 0) { deblog = 0; zclose(ZDFILE); } if (zsoutl(ZDFILE,pbuf) < 0) { deblog = 0; zclose(ZDFILE); } #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_DB && ckxlogging) { cksyslog(SYSLG_DB,1,"debug",s1,pbuf); } #endif /* CKSYSLOG */ } break; case F100: /* 4, "s1" */ if (zsoutl(ZDFILE,s1) < 0) { deblog = 0; zclose(ZDFILE); } #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_DB && ckxlogging) { cksyslog(SYSLG_DB,1,"debug",s1,NULL); } #endif /* CKSYSLOG */ break; case F101: /* 5, "s1=n" */ sprintf(sp,"%s=%s\n",s1,ckfstoa(n)); if (zsout(ZDFILE,dbptr) < 0) { deblog = 0; zclose(ZDFILE); } #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_DB && ckxlogging) { cksyslog(SYSLG_DB,1,"debug",dbptr,NULL); } #endif /* CKSYSLOG */ break; case F110: /* 6, "s1[s2]" */ sprintf(sp,"%s[%s]\n",s1,s2); if (zsout(ZDFILE,dbptr) < 0) { deblog = 0; zclose(ZDFILE); } #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_DB && ckxlogging) { cksyslog(SYSLG_DB,1,"debug",dbptr,NULL); } #endif /* CKSYSLOG */ break; case F111: /* 7, "s1[s2]=n" */ sprintf(sp,"%s[%s]=%s\n",s1,s2,ckfstoa(n)); if (zsout(ZDFILE,dbptr) < 0) { deblog = 0; zclose(ZDFILE); } #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_DB && ckxlogging) { cksyslog(SYSLG_DB,1,"debug",dbptr,NULL); } #endif /* CKSYSLOG */ break; default: sprintf(sp,"\n?Invalid format for debug() - %d\n",f); if (zsout(ZDFILE,dbptr) < 0) { deblog = 0; zclose(ZDFILE); } #ifdef CKSYSLOG if (ckxsyslog >= SYSLG_DB && ckxlogging) { cksyslog(SYSLG_DB,1,"debug",dbptr,NULL); } #endif /* CKSYSLOG */ break; } xdebug: /* Common exit point */ #ifdef OS2 if (SysInited) ReleaseDebugMutex(); #else /* OS2 */ deblog = 1; /* Restore this */ #endif /* OS2 */ return(0); } int #ifdef CK_ANSIC dohexdump(CHAR *msg, CHAR *st, int cnt) #else dohexdump(msg,st,cnt) CHAR *msg; CHAR *st; int cnt; #endif /* CK_ANSIC */ /* dohexdump */ { int i = 0, j = 0, k = 0; char tmp[8]; #ifdef OS2 extern int SysInited; #endif /* OS2 */ if (!deblog) return(0); /* If no debug log, don't. */ if (!dbptr) { /* Allocate memory buffer */ dbptr = malloc(DBUFL+1); /* This only happens once */ if (!dbptr) { deblog = 0; zclose(ZDFILE); return(0); } } #ifdef OS2 if (SysInited) { if (RequestDebugMutex(30000)) goto xdebug; } #else /* OS2 */ deblog = 0; /* Prevent infinite recursion */ #endif /* OS2 */ if (msg != NULL) { ckmakxmsg(dbptr, DBUFL, "HEXDUMP: ", (char *)msg, " (", ckitoa(cnt), " bytes)\n", NULL,NULL,NULL,NULL,NULL,NULL,NULL ); if (zsout(ZDFILE,dbptr) < 0) { deblog = 0; zclose(ZDFILE); goto xdebug; } } else { ckmakmsg(dbptr, DBUFL, "HEXDUMP: (", ckitoa(cnt), " bytes)\n", NULL ); zsout(ZDFILE,dbptr); if (zsout(ZDFILE,dbptr) < 0) { deblog = 0; zclose(ZDFILE); goto xdebug; } } for (i = 0; i < cnt; i++) { dbptr[0] = '\0'; for (j = 0 ; (j < 16); j++) { if ((i + j) < cnt) sprintf(tmp, "%s%02x ", (j == 8 ? "| " : ""), (CHAR) st[i + j] ); else sprintf(tmp, "%s ", (j == 8 ? "| " : "") ); ckstrncat(dbptr,tmp,DBUFL+1); } ckstrncat(dbptr," ",DBUFL+1); for (k = 0; (k < 16) && ((i + k) < cnt); k++) { sprintf(tmp, "%s%c", (k == 8 ? " " : ""), isprint(st[i + k]) ? st[i + k] : '.' ); ckstrncat(dbptr,tmp,DBUFL+1); } ckstrncat(dbptr,"\n",DBUFL+1); i += j - 1; if (zsout(ZDFILE,dbptr) < 0) { deblog = 0; zclose(ZDFILE); goto xdebug; } } /* end for */ xdebug: #ifdef OS2 if (SysInited) ReleaseDebugMutex(); #else /* OS2 */ deblog = 1; #endif /* OS2 */ return(0); } #endif /* DEBUG */ /* Session Log... */ int tsstate = 0; VOID #ifdef OS2 logchar(unsigned short c) #else /* OS2 */ #ifdef CK_ANSIC logchar(char c) #else logchar(c) char c; #endif /* CK_ANSIC */ #endif /* OS2 */ /* logchar */ { /* Log character c to session log */ extern int slognul; int oktolog = 0; #ifndef NOLOCAL if (!seslog) return; if ((sessft != XYFT_T) || ( #ifdef UNIX c != '\r' && #else #ifdef datageneral c != '\r' && #else #ifdef STRATUS c != '\r' && #else #ifdef AMIGA c != '\r' && #else #ifdef GEMDOS c != '\r' && #endif /* GEMDOS */ #endif /* AMIGA */ #endif /* STRATUS */ #endif /* datageneral */ #endif /* UNIX */ #ifdef OSK c != '\n' && #else #ifdef MAC c != '\n' && #endif /* MAC */ #endif /* OSK */ c != XON && c != XOFF)) oktolog = 1; if (c == '\0' && !sessft) /* NUL in text mode */ if (slognul) oktolog = 1; /* only if padding (2009/10/22) */ if (!oktolog) return; if (slogts) { /* Log is timestamped */ if (tsstate == 0) { /* State = between-lines */ char * p; /* zstime() pointer */ char ts[48]; /* timestamp buffer */ ztime(&p); /* Get asctime() string */ ckstrncpy(ts,p,32); /* Make safe copy */ if (ztmsec > -1L) { /* Add msecs if we have them */ sprintf(&ts[19],".%03ld: ",ztmsec); /* SAFE */ } else { ts[19] = ':'; ts[20] = SP; ts[21] = NUL; } if (zsout(ZSFILE,&ts[11]) < 0) goto xlogchar; } } if (c == '\n') /* At end of line? */ tsstate = 0; /* yes */ else tsstate = 1; /* no */ if (zchout(ZSFILE,(CHAR)(c & 0xFF)) < 0) /* Log the character */ goto xlogchar; if (tsstate == 0 && slognul != 0) { /* Null-terminating lines? */ if (zchout(ZSFILE,(CHAR)0) < 0) /* Add a NUL */ goto xlogchar; } return; xlogchar: conoll(""); conoll("ERROR WRITING SESSION LOG, LOG CLOSED!"); setseslog(0); zclose(ZSFILE); #endif /* NOLOCAL */ } VOID logstr(s, len) char * s; int len; { /* Log string to session log */ #ifndef NOLOCAL int n = 0; if (!s) return; while (seslog && (n < len)) logchar(s[n++]); #endif /* NOLOCAL */ } #ifdef CK_CURSES int ck_repaint() { repaint = 1; return(0); } #ifdef STRATUS /* VOS has curses but no tgetent() */ int tgetent(s1, s2) char * s1, * s2; { return(1); } #endif /* STRATUS */ #ifdef VMS #ifdef __DECC _PROTOTYP(int tgetent,(char *, char *)); #endif /* __DECC */ #endif /* VMS */ /* There are three different ways to do fullscreen on VMS. 1. Use the real curses library, VAXCCURSE. 2. Use do-it-yourself code. 3. Use the Screen Manager, SMG$. Method 1 doesn't work quite right; you can't call endwin(), so once you've started curses mode, you can never leave. Method 2 doesn't optimize the screen, and so much more time is spent in screen writes. This actually causes file transfers to fail because the tty device input buffer can be overrun while the screen is being updated, especially on a slow MicroVAX that has small typeahead buffers. In the following #ifdef block, #define one of them and #undef the other 2. So now let's try method 3... */ #ifdef VMS #define CK_SMG /* Screen Manager */ #undef MYCURSES /* Do-it-yourself */ #undef VMSCURSE /* VAXCCURSE library */ #endif /* VMS */ /* But just before New Years, 2000, the SMG library seemed to break on both VMS systems we have here (an Alpha with VMS 7.1 and a VAX with 5.5). So back to MYCURSES, which works fine. */ #ifdef VMS #undef CK_SMG #define MYCURSES #endif /* VMS */ #ifdef MYCURSES #define stdscr 0 #ifdef CK_WREFRESH #undef CK_WREFRESH #endif /* CK_WREFRESH */ #endif /* MYCURSES */ /* S C R E E N C -- Screen display function, uses curses */ /* Idea for curses display contributed by Chris Pratt of APV Baker, UK */ /* Avoid conficts with curses.h */ #ifdef QNX /* Same as ckcasc.h, but in a different radix... */ #ifdef ESC #undef ESC #endif /* ESC */ #endif /* QNX */ #ifndef MYCURSES #undef VOID /* This was defined in ckcdeb.h */ #endif /* MYCURSES */ #undef BS /* These were defined in ckcasc.h */ #undef CR #undef NL #undef SO #ifdef US #undef US #endif /* US */ #undef SP /* Used in ncurses */ #define CHR_SP 32 /* Use this instead */ #ifdef VMS /* VMS fullscreen display */ #ifdef MYCURSES /* Do-it-yourself method */ extern int isvt52; /* From CKVTIO.C */ #define printw printf #else #ifdef VMSCURSE /* VMS curses library VAXCCURSE */ #include /* Note: Screen manager doesn't need a header file */ #endif /* VMSCURSE */ #endif /* MYCURSES */ #else /* Not VMS */ #ifdef MYCURSES /* Do-it-yourself method */ #define isvt52 0 /* Used by OS/2, VT-100/ANSI always */ #ifdef CKXPRINTF #define printw ckxprintf #else /* CKXPRINTF */ #ifdef KUI #define printw Vscrnprintw #else /* KUI */ #define printw printf #endif /* KUI */ #endif /* CKXPRINTF */ #else /* Use real curses */ #ifdef CK_NCURSES /* or ncurses... */ #ifdef CKXPRINTF /* Our printf macro conflicts with */ #undef printf /* use of "printf" in ncurses.h */ #endif /* CKXPRINTF */ #include #ifdef CKXPRINTF #define printf ckxprintf #endif /* CKXPRINTF */ #else /* Not ncurses */ #ifdef CKXPRINTF /* Our printf macro conflicts with */ #undef printf /* use of "printf" in curses.h */ #endif /* CKXPRINTF */ #ifdef M_XENIX /* SCO XENIX... */ #ifdef M_TERMCAP #undef M_TERMCAP #endif /* M_TERMCAP */ #ifndef M_TERMINFO #define M_TERMINFO #endif /* M_TERMINFO */ #endif /* M_XENIX */ #ifdef RTAIX #undef NLS /* Avoid 'redeclaration of free'. */ #endif /* RTAIX */ #include #ifdef CKXPRINTF #define printf ckxprintf #endif /* CKXPRINTF */ #endif /* CK_NCURSES */ #endif /* MYCURSES */ #endif /* VMS */ #ifdef BUG999 _PROTOTYP(int tgetent,(char *, char *)); _PROTOTYP(char *tgetstr,(char *, char **)); _PROTOTYP(int tputs,(char *, int, int (*)())); _PROTOTYP(char *tgoto,(const char *, int, int)); #endif /* BUG999 */ #endif /* CK_CURSES */ /* F X D I N I T -- File Xfer Display Initialization */ #ifdef CK_CURSES #ifndef MYCURSES #ifndef CK_SMG static #ifdef CK_ANSIC /* Can't use VOID because of curses.h */ void ck_termset(int); #else ck_termset(); #endif /* CK_ANSIC */ #endif /* CK_SMG */ #endif /* MYCURSES */ #endif /* CK_CURSES */ #ifdef NOTERMCAP static int notermcap = 1; #else static int notermcap = 0; #endif /* NOTERMCAP */ #ifndef NODISPLAY CKVOID fxdinit(xdispla) int xdispla; { #ifndef COHERENT #ifndef OS2 #ifndef STRATUS char *s; int x, dummy; debug(F101,"fxdinit xdispla","",xdispla); debug(F101,"fxdinit fxd_inited","",fxd_inited); #ifdef IKSD #ifndef NOXFER /* No curses for IKSD */ if (inserver) { fdispla = XYFD_N; return; } if (fxd_inited) /* Only do this once */ return; #endif /* NOXFER */ #endif /* IKSD */ if (xdispla == XYFD_R || xdispla == XYFD_S || xdispla == XYFD_B) { if (xfrmsg) { printf("%s\n",xfrmsg); makestr(&xfrmsg,NULL); } } #ifdef CK_CURSES #ifdef VMS /* Force BRIEF in Batch logs */ if (batch && (xdispla == XYFD_C || xdispla == XYFD_S)) xdispla = XYFD_B; #else if (xdispla == XYFD_C || xdispla == 9999) { #ifdef DYNAMIC if (!trmbuf) { /* Allocate tgetent() buffer. Make it big -- some termcaps can be huge; tgetent() merrily writes past the end of the buffer, causing core dumps or worse. */ trmbuf = (char *)malloc(TRMBUFL); if (!trmbuf) { notermcap = 1; debug(F101,"fxdinit malloc trmbuf","FAILED",TRMBUFL); fdispla = XYFD_S; return; } #ifdef COMMENT debug(F111,"fxdinit malloc trmbuf","OK",TRMBUFL); debug(F001,"fxdinit trmbuf","",trmbuf); memset(trmbuf,'\0',(size_t)TRMBUFL); debug(F100,"fxdinit memset OK","",0); #endif /* COMMENT */ } #endif /* DYNAMIC */ debug(F100,"fxdinit before getenv(TERM)","",0); s = getenv("TERM"); debug(F110,"fxdinit after getenv(TERM)",s,0); if (!s) s = ""; if (*s) { debug(F110,"fxdinit before tgetent()",s,0); x = tgetent(trmbuf,s); debug(F111,"fxdinit tgetent",s,x); } else { x = 0; notermcap = 1; debug(F110,"fxdinit TERM null - no tgetent",s,0); } if (x < 1 && !quiet && !backgrd #ifdef VMS && !batch #endif /* VMS */ ) { printf("Warning: terminal type unknown: \"%s\"\n",s); #ifdef COMMENT /* Confusing - nobody knows what this means */ printf("SCREEN command will use ANSI sequences.\n"); #endif /* COMMENT */ if (local) printf("Fullscreen file transfer display disabled.\n"); fdispla = XYFD_S; } #ifndef MYCURSES #ifndef CK_SMG ck_termset(x); #endif /* CK_SMG */ #endif /* MYCURSES */ fxd_inited = 1; } #endif /* CK_CURSES */ #endif /* VMS */ #endif /* STRATUS */ #endif /* OS2 */ #endif /* COHERENT */ } #endif /* NODISPLAY */ #ifdef CK_CURSES #ifdef CK_SMG /* Long section for Screen Manager starts here... By William Bader. */ #include "ckvvms.h" #ifdef OLD_VMS #include /* use this on VAX C 2.4 */ /* #include */ #else #include /* Martin Zinser */ #endif /* OLD_VMS */ extern unsigned int vms_status; /* Used for system service return status */ static long smg_pasteboard_id = -1; /* pasteboard identifier */ static long smg_display_id = -1; /* display identifier */ static int smg_open = 0; /* flag if smg current open */ static int smg_inited = 0; /* flag if smg initialized */ #ifdef COMMENT #define clrtoeol() SMG$ERASE_LINE(&smg_display_id, 0, 0) #define clear() SMG$ERASE_DISPLAY(&smg_display_id, 0, 0, 0, 0) #define touchwin(scr) SMG$REPAINT_SCREEN(&smg_pasteboard_id) #else /* Not COMMENT */ #define clrtoeol() smg$erase_line(&smg_display_id, 0, 0) #define clear() smg$erase_display(&smg_display_id, 0, 0, 0, 0) #define touchwin(scr) smg$repaint_screen(&smg_pasteboard_id) #endif /* COMMENT */ #define clearok(curscr,ok) /* Let wrefresh() do the work */ #define wrefresh(cursrc) touchwin(scr) static void move(row, col) int row, col; { /* Change from 0-based for curses to 1-based for SMG */ if (!smg_open) return; ++row; ++col; debug(F111,"VMS smg move",ckitoa(row),col); #ifdef COMMENT /* Martin Zinser */ CHECK_ERR("move: smg$set_cursor_abs", SMG$SET_CURSOR_ABS(&smg_display_id, &row, &col)); #else CHECK_ERR("move: smg$set_cursor_abs", smg$set_cursor_abs(&smg_display_id, &row, &col)); #endif /* COMMENT */ debug(F101,"VMS smg move vms_status","",vms_status); } #ifdef VMS_V40 #define OLD_VMS #endif /* VMS_V40 */ #ifdef VMS_V42 #define OLD_VMS #endif /* VMS_V42 */ #ifdef VMS_V44 #define OLD_VMS #endif /* VMS_V44 */ static int initscr() { int rows = 24, cols = 80; int row = 1, col = 1; debug(F101,"VMS initscr smg_pasteboard_id A","",smg_pasteboard_id); if (smg_pasteboard_id == -1) { /* Open the screen */ #ifdef OLD_VMS /* Note: Routine calls lowercased 9/96 */ CHECK_ERR("initscr: smg$create_pasteboard", smg$create_pasteboard(&smg_pasteboard_id, 0, 0, 0, 0)); #else /* For VMS V5, not tested */ CHECK_ERR("initscr: smg$create_pasteboard", smg$create_pasteboard(&smg_pasteboard_id, 0, 0, 0, 0, 0)); #endif /* OLD_VMS */ } debug(F101,"VMS initscr smg_pasteboard_id B","",smg_pasteboard_id); if (smg_pasteboard_id == -1) { printf("?Error initializing fullscreen display\n"); fdispla = XYFD_S; dpyinit(); return(0); } debug(F101,"VMS initscr smg_display_id","",smg_display_id); if (smg_display_id == -1) { /* Create a display window */ #ifdef COMMENT /* Martin Zinser */ CHECK_ERR("initscr: smg$create_virtual_display", SMG$CREATE_VIRTUAL_DISPLAY(&rows, &cols, &smg_display_id, 0, 0, 0)); /* Connect the display window to the screen */ CHECK_ERR("initscr: smg$paste_virtual_display", SMG$PASTE_VIRTUAL_DISPLAY(&smg_display_id,&smg_pasteboard_id, &row,&col)); #else CHECK_ERR("initscr: smg$create_virtual_display", smg$create_virtual_display(&rows, &cols, &smg_display_id, 0, 0, 0)); /* Connect the display window to the screen */ CHECK_ERR("initscr: smg$paste_virtual_display", smg$paste_virtual_display(&smg_display_id,&smg_pasteboard_id, &row,&col)); #endif /* COMMENT */ } debug(F101,"VMS initscr smg_open A","",smg_open); if (!smg_open) { /* Start a batch update */ smg_open = 1; #ifdef COMMENT CHECK_ERR("initscr: smg$begin_pasteboard_update", SMG$BEGIN_PASTEBOARD_UPDATE(&smg_pasteboard_id)); #else CHECK_ERR("initscr: smg$begin_pasteboard_update", smg$begin_pasteboard_update(&smg_pasteboard_id)); #endif /* COMMENT */ debug(F101,"VMS initscr smg$begin_pasteboard_update","",vms_status); } debug(F101,"VMS initscr smg_open B","",smg_open); smg_inited = 1; return(1); } static void refresh() { debug(F101,"refresh smg_pasteboard_id","",smg_pasteboard_id); if (smg_open == 0 || smg_pasteboard_id == -1) return; #ifdef COMMENT /* Martin Zinser */ CHECK_ERR("refresh: smg$end_pasteboard_update", SMG$END_PASTEBOARD_UPDATE(&smg_pasteboard_id)); CHECK_ERR("refresh: smg$begin_pasteboard_update", SMG$BEGIN_PASTEBOARD_UPDATE(&smg_pasteboard_id)); #else CHECK_ERR("refresh: smg$end_pasteboard_update", smg$end_pasteboard_update(&smg_pasteboard_id)); CHECK_ERR("refresh: smg$begin_pasteboard_update", smg$begin_pasteboard_update(&smg_pasteboard_id)); #endif /* COMMENT */ } static void endwin() { if (!smg_open) return; smg_open = 0; #ifdef COMMENT CHECK_ERR("endwin: smg$end_pasteboard_update", SMG$END_PASTEBOARD_UPDATE(&smg_pasteboard_id)); #else CHECK_ERR("endwin: smg$end_pasteboard_update", smg$end_pasteboard_update(&smg_pasteboard_id)); #endif /* COMMENT */ move(22, 0); #ifdef COMMENT /* These calls clear the screen. (convert routine calls to lowercase - Martin Zinser) */ CHECK_ERR("endwin: smg$delete_virtual_display", SMG$DELETE_VIRTUAL_DISPLAY(&smg_display_id)); smg_display_id = -1; CHECK_ERR("endwin: smg$delete_pasteboard", SMG$DELETE_PASTEBOARD(&smg_pasteboard_id, 0)); smg_pasteboard_id = -1; #endif /* COMMENT */ } #ifdef COMMENT /* DECC 6.2 screams bloody murder about printw ("not enough args") */ /* but adding the following prototype only makes it holler louder. */ #ifdef __DECC /* "varargs" prototype for printw */ _PROTOTYP(static int printw,(char *, ...)); #endif /* __DECC */ #endif /* COMMENT */ #ifdef __DECC #include _PROTOTYP(static void printw,(char *, ...)); static void printw(char *str,...) { char buf[255]; va_list ap; $DESCRIPTOR(text_dsc, 0); text_dsc.dsc$a_pointer=buf; if (!smg_open) return; va_start(ap,str); text_dsc.dsc$w_length = vsprintf(buf, str, ap); va_end(ap); CHECK_ERR("printw: smg$put_chars", smg$put_chars(&smg_display_id, &text_dsc, 0, 0, 0, 0, 0)); } #else static void printw(str, a1, a2, a3, a4, a5, a6, a7, a8) char *str; long a1, a2, a3, a4, a5, a6, a7, a8; /* printw */ { char buf[255]; $DESCRIPTOR(text_dsc, 0); if (!smg_open) return; text_dsc.dsc$a_pointer=buf; text_dsc.dsc$w_length = sprintf(buf, str, a1, a2, a3, a4, a5, a6, a7, a8); CHECK_ERR("printw: smg$put_chars", smg$put_chars(&smg_display_id, &text_dsc, 0, 0, 0, 0, 0)); } #endif /* __DECC */ #define CK_CURPOS int ck_curpos(row, col) { debug(F111,"VMS smg ck_curpos",ckitoa(row),col); if (!smg_inited || !smg_open) { initscr(); } debug(F101,"VMS smg curpos smg_open","",smg_open); if (!smg_open) return(0); debug(F111,"VMS smg ck_curpos",ckitoa(row-1),col-1); move(row - 1, col - 1); /* SMG is 0-based */ refresh(); /* endwin(); */ return(0); } int ck_cls() { debug(F101,"VMS smg ck_cls smg_inited","",smg_inited); if (!smg_inited || !smg_open) { initscr(); } debug(F101,"VMS smg ck_cls smg_open","",smg_open); if (!smg_open) return(0); clear(); refresh(); /* endwin(); */ return(0); } int ck_cleol() { debug(F101,"VMS smg ck_cleol smg_inited","",smg_inited); if (!smg_inited || !smg_open) { initscr(); } debug(F101,"VMS smg ck_cleol smg_open","",smg_open); if (!smg_open) return(0); clrtoeol(); refresh(); /* endwin(); */ return(0); } #endif /* CK_SMG */ #ifdef MYCURSES /* Do-it-yourself curses implementation for VMS, OS/2 and other ANSI/VT-100's. Supports only the VT52 and VT1xx (and later VT2xx/3xx/4xx) terminals. By Terry Kennedy, St Peters College. First, some stuff we can just ignore: */ static int touchwin(x) int x; { return(0); } static int initscr() { return(0); } static int refresh() { return(0); } static int endwin() { return(0); } /* * Now, some stuff we need to do: */ _PROTOTYP( int move, (int, int) ); #ifndef OS2 int move(row, col) int row, col; { if (isvt52) printf("\033Y%c%c", row + 037, col + 037); else printf("\033[%d;%dH", row + 1, col + 1); return(0); } int clear() { move(0,0); if (isvt52) printf("\033J"); else printf("\033[J"); return(0); } int clrtoeol() { if (isvt52) printf("\033K"); else printf("\033[K"); return(0); } #define CK_CURPOS int ck_cls() { return(clear()); } int ck_cleol() { return(clrtoeol()); } int ck_curpos(row, col) int row, col; { move(row, col); return(0); } #else /* OS2 */ /* Windows NT and Windows 95 do not provide ANSI emulation */ /* Therefore we might as well not use it for OS/2 either */ int move(row, col) int row, col; { #ifndef ONETERMUPD SetCurPos(row, col); #endif /* ONETERMUPD */ lgotoxy( VCMD, col+1, row+1); VscrnIsDirty(VCMD); return(0); } int clear() { viocell cell; move(0,0); #ifdef ONETERMUPD if (VscrnGetBufferSize(VCMD) > 0) { VscrnScroll(VCMD, UPWARD, 0, VscrnGetHeight(VCMD)-(1), VscrnGetHeight(VCMD)-(0), TRUE, CHR_SP); cleartermscreen(VCMD); } #else cell.c = ' '; cell.a = colorcmd; WrtNCell(cell, cmd_rows * cmd_cols, 0, 0); #endif /* ONETERMUPD */ return(0); } int clrtoeol() { USHORT row, col; viocell cell; cell.c = ' '; cell.a = colorcmd; #ifndef ONETERMUPD GetCurPos(&row, &col ); WrtNCell(cell, cmd_cols - col -1, row, col); #endif /* ONETERMUPD */ clrtoeoln(VCMD,CHR_SP); return(0); } #define CK_CURPOS int ck_curpos(row, col) int row, col; { move(row, col); return(0); } int ck_cls() { return(clear()); } int ck_cleol() { return(clrtoeol()); } #endif /* OS2 */ #endif /* MYCURSES */ #ifndef NOTERMCAP #ifndef CK_CURPOS #define CK_CURPOS /* Termcap/Terminfo section */ static char cur_cls[32] = { NUL, NUL }; static char cur_cleol[32] = { NUL, NUL }; static char cur_cm[64] = { NUL, NUL }; static char tgsbuf[128] = { NUL, NUL }; static #ifdef CK_ANSIC void #endif /* CK_ANSIC */ ck_termset(x) int x; { cur_cls[0] = NUL; cur_cleol[0] = NUL; cur_cm[0] = NUL; #ifdef tgetent debug(F100,"tgetent is a macro","",0); #endif /* tgetent */ #ifdef tgetstr debug(F100,"tgetstr is a macro","",0); #endif /* tgetstr */ #ifdef tputs debug(F100,"tputs is a macro","",0); #endif /* tputs */ #ifdef tgoto debug(F100,"tgoto is a macro","",0); #endif /* tgoto */ #ifdef NOTERMCAP /* tgetstr() gets a segmentation fault on OSF/1 */ debug(F100,"ck_termset NOTERMCAP","",0); #else if (notermcap) { debug(F100,"ck_termset notermcap","",0); return; } debug(F101,"ck_termset x","",x); if (x > 0) { char * bp; bp = tgsbuf; *bp = NUL; debug(F110,"ck_termset calling tgetstr","cl",0); if (tgetstr("cl", &bp)) { /* Get clear-screen code */ debug(F110,"ck_termset tgetstr cl",tgsbuf,""); if ((int)strlen(tgsbuf) < 32) ckstrncpy(cur_cls,tgsbuf,32); } else return; bp = tgsbuf; if (tgetstr("ce", &bp)) { /* Get clear-to-end-of-line code */ debug(F110,"ck_termset tgetstr ce",tgsbuf,""); if ((int)strlen(tgsbuf) < 32) ckstrncpy(cur_cleol,tgsbuf,32); } else return; bp = tgsbuf; if (tgetstr("cm", &bp)) { /* Get cursor-movement code */ debug(F110,"ck_termset tgetstr cm",tgsbuf,""); if ((int)strlen(tgsbuf) < 64) ckstrncpy(cur_cm,tgsbuf,64); } else return; } #endif /* NOTERMCAP */ } #ifndef TPUTSFNTYPE #ifdef TPUTSISVOID #define TPUTSFNTYPE void #else #define TPUTSFNTYPE int #endif /* TPUTSISVOID */ #endif /* TPUTSFNTYPE */ #ifndef TPUTSARGTYPE #ifdef HPUX9 #define TPUTSARGTYPE char #else #ifdef HPUX10 #define TPUTSARGTYPE char #else #define TPUTSARGTYPE int #endif /* HPUX10 */ #endif /* HPUX9 */ #endif /* TPUTSARGTYPE */ static TPUTSFNTYPE #ifdef CK_ANSIC ck_outc(TPUTSARGTYPE x) #else ck_outc(x) TPUTSARGTYPE x; #endif /* CK_ANSIC */ { /* To satisfy tputs() arg3 prototype */ int rc; char c; c = (char) x; rc = (inserver) ? ttoc(c) : conoc(c); #ifndef TPUTSISVOID return(rc); #endif /* TPUTSISVOID */ } int ck_curpos(row, col) int row, col; { #ifdef CK_ANSIC TPUTSFNTYPE (*fn)(TPUTSARGTYPE); #else TPUTSFNTYPE (*fn)(); #endif /* CK_ANSIC */ if (!fxd_inited) fxdinit(9999); if (!cur_cm[0]) { /* We don't have escape sequences */ #ifdef COMMENT return(-1); /* Do nothing */ #else /* Both C-Kermit's SCREEN command and ANSI/VT100 are 1-based */ printf("\033[%d;%dH", row, col); /* Or default to ANSI */ #endif /* COMMENT */ } else { fn = ck_outc; /* termcap/terminfo is 0-based */ tputs( #ifdef TPUTSARG1CONST (const char *) #endif /* TPUTSARG1CONST */ tgoto(cur_cm,col-1,row-1),1,fn); } return(0); } int ck_cls() { #ifdef CK_ANSIC TPUTSFNTYPE (*fn)(TPUTSARGTYPE); #else TPUTSFNTYPE (*fn)(); #endif /* CK_ANSIC */ if (!fxd_inited) fxdinit(9999); if (!cur_cls[0]) { /* If we don't have escape sequences */ #ifdef COMMENT return(-1); /* Do nothing */ #else printf("\033[;H\033[2J"); /* Or default to ANSI */ #endif /* COMMENT */ } else { fn = ck_outc; debug(F111,"ck_cls 2",cur_cls,fxd_inited); tputs(cur_cls,cmd_rows,fn); } return(0); } int ck_cleol() { #ifdef CK_ANSIC TPUTSFNTYPE (*fn)(TPUTSARGTYPE); #else TPUTSFNTYPE (*fn)(); #endif /* CK_ANSIC */ if (!fxd_inited) fxdinit(9999); if (!cur_cleol[0]) { /* If we don't have escape sequences */ #ifdef COMMENT return(-1); /* Do nothing */ #else printf("\033[K"); /* Or use ANSI */ #endif /* COMMENT */ } else { fn = ck_outc; tputs(cur_cleol,1,fn); } return(0); } #endif /* CK_CURPOS */ #else static void ck_termset(x) int x; { if (x) return; } #endif /* NOTERMCAP */ #ifndef CK_CURPOS #define CK_CURPOS int ck_cls() { printf("\033[;H\033[2J"); return(0); } int ck_cleol() { printf("\033[K"); return(0); } int ck_curpos(row, col) int row, col; { printf("\033[%d;%dH", row, col); return(0); } #endif /* CK_CURPOS */ #ifndef NOXFER static int cinit = 0; /* Flag for curses init'd */ static int cendw = 0; /* endwin() was called */ static #ifdef CK_ANSIC /* Because VOID used by curses.h */ void #else #ifdef MYCURSES VOID #else int #endif /* MYCURSES */ #endif /* CK_ANSIC */ #ifdef CK_ANSIC /* Update % transfered and % bar */ updpct(long old, long new) #else /* CK_ANSIC */ updpct(old, new) long old, new; #endif /* CK_ANSIC */ /* updpct */ { #ifdef COMMENT int m, n; move(CW_PCD,22); printw("%ld", new); #ifdef KUI #ifndef K95G KuiSetProperty(KUI_FILE_TRANSFER, (long) CW_PCD, (long) new); #endif /* K95G */ #endif /* KUI */ #ifdef CK_PCT_BAR if (thermometer) { if (old > new) { old = 0; move(CW_PCD, 26); clrtoeol(); } m = old/2; move(CW_PCD, 26 + m); n = new / 2 - m; #ifndef OS2 while (n > 0) { if ((m + 1) % 5 == 0) printw("*"); else printw("="); m++; n--; } if (new % 2 != 0) printw("-"); /* move(CW_PCD, 22+53); */ #else /* OS2 */ while (n > 0) { printw("%c", '\333'); m++; n--; } if (new % 2 != 0) printw("%c", '\261'); #endif /* OS2 */ } #endif /* CK_PCT_BAR */ /* clrtoeol(); */ #else /* !COMMENT */ #ifdef OS2 #define CHAR1 '\333' /* OS2 - CP437 */ #define CHAR2 '\261' #else #define CHAR1 '/' /* Default */ #define CHAR2 '-' #endif /* OS2 */ debug(F101,"updpct old","",old); debug(F101,"updpct new","",new); move(CW_PCD,22); printw("%-3ld", new); /* (was) printw("%ld", new); */ #ifdef KUI #ifndef K95G KuiSetProperty( KUI_FILE_TRANSFER, (long) CW_PCD, (long) new ); #endif /* K95G */ #endif /* KUI */ #ifdef CK_PCT_BAR if (thermometer) { int m, n; if (old > new) { old = 0 ; move(CW_PCD, 26); clrtoeol(); } if (new <= 100L) { m = old / 2; n = new / 2 - m; move(CW_PCD, 26+m); while (n-- > 0) printw("%c", CHAR1); if (new % 2 != 0) printw("%c", CHAR2); } } #endif /* CK_PCT_BAR */ #endif /* COMMENT */ } static CK_OFF_T old_tr = (CK_OFF_T)-1; /* Time remaining previously */ static CK_OFF_T #ifdef CK_ANSIC shoetl(CK_OFF_T old_tr, long cps, CK_OFF_T fsiz, CK_OFF_T howfar) #else shoetl(old_tr, cps, fsiz, howfar) long cps; CK_OFF_T old_tr, fsiz, howfar; #endif /* CK_ANSIC */ /* shoetl */ { /* Estimated time left in transfer */ CK_OFF_T tr; /* Time remaining, seconds */ #ifdef GFTIMER if (fsiz > 0L && cps > 0L) tr = (CK_OFF_T)((CKFLOAT)(fsiz - howfar) / (CKFLOAT)cps); else tr = (CK_OFF_T)-1; #else tr = (fsiz > 0L && cps > 0L) ? ((fsiz - howfar) / cps) : (CK_OFF_T)-1; #endif /* GFTIMER */ move(CW_TR,22); if (tr > (CK_OFF_T)-1) { if (tr != old_tr) { printw("%s",hhmmss(tr)); #ifdef KUI #ifndef K95G KuiSetProperty(KUI_FILE_TRANSFER, (long)CW_TR, (long)hhmmss(tr)); #endif /* K95G */ #endif /* KUI */ clrtoeol(); } } else { printw("(unknown)"); #ifdef KUI #ifndef K95G KuiSetProperty( KUI_FILE_TRANSFER, (long) CW_TR, (long) "(unknown)" ); #endif /* K95G */ #endif /* KUI */ clrtoeol(); } return(tr); } static long #ifdef CK_ANSIC shocps(int pct, CK_OFF_T fsiz, CK_OFF_T howfar) #else shocps(pct, fsiz, howfar) int pct; CK_OFF_T fsiz, howfar; #endif /* CK_ANSIC */ /* shocps */ { #ifdef CPS_WEIGHTED static CK_OFF_T oldffc = 0L; #endif /* CPS_WEIGHTED */ #ifdef GFTIMER CKFLOAT secs, xx; #else CK_OFF_T secs, xx; #endif /* GFTIMER */ #ifdef GFTIMER xx = (gtv >= 0.0) ? gtv : 0.0; /* Floating-point version */ gtv = gftimer(); if ((gtv - oldgtv) < (CKFLOAT) 1.0) /* Only do this once per second */ return(oldcps); oldgtv = xx; #else xx = (gtv >= 0) ? gtv : 0; /* Whole-number version */ gtv = gtimer(); if ((gtv - oldgtv) < 1) return(oldcps); oldgtv = xx; #endif /* GFTIMER */ #ifdef CPS_WEIGHTED /* debug(F100,"SHOCPS: WEIGHTED","",0); */ if (gtv != oldgtv) { /* The first packet is ignored */ if (ffc < oldffc) oldffc = ffc; oldcps = cps; if (oldcps && oldgtv > #ifdef GFTIMER 1.0 #else 1 #endif /* GFTIMER */ ) { /* The first second is ignored */ /* This version of shocps() produces a weighted average that some people like, but most people find it disconcerting and bombard us with questions and complaints about why the CPS figure fluctuates so wildly. So now you only get the weighted average if you build the program yourself with CPS_WEIGHTED defined. */ #ifndef CPS_VINCE #ifdef GFTIMER cps = (long)((((CKFLOAT)oldcps * 3.0) + (CKFLOAT)(ffc - oldffc) / (gtv-oldgtv) ) / 4.0); #else cps = ( (oldcps * 3) + (ffc - oldffc) / (gtv-oldgtv) ) / 4; #endif /* GFTIMER */ #else /* And an alternate weighting scheme from Vincent Fatica... */ cps = (3 * ((1+pct/300)*oldffc/oldgtv+(1-pct/100)*(ffc-oldffc)/(gtv-oldgtv))) / 4; #endif /* CPS_VINCE */ } else { /* No weighted average since there is nothing to weigh */ #ifdef GFTIMER cps = (long)(gtv != 0.0 ? (CKFLOAT)(ffc - oldffc) / (gtv - oldgtv) : (ffc - oldffc)) ; #else cps = gtv ? (ffc - oldffc) / (gtv - oldgtv) : (ffc - oldffc) ; #endif /* GFTIMER */ } #ifdef COMMENT #ifdef DEBUG if (deblog) { debug(F101,"SHOCPS: pct ","",pct); debug(F101,"SHOCPS: gtv ","",gtv); debug(F101,"SHOCPS: oldgtv","",oldgtv); debug(F101,"SHOCPS: dgtv ","",(long)(gtv-oldgtv)); debug(F101,"SHOCPS: ffc ","",ffc); debug(F101,"SHOCPS: oldffc","",oldffc); debug(F101,"SHOCPS: dffc ","",ffc-oldffc); debug(F101,"SHOCPS: cps ","",cps); } #endif /* DEBUG */ #endif /* COMMENT */ move(CW_CP,22); printw("%ld", cps); #ifdef KUI #ifndef K95G KuiSetProperty( KUI_FILE_TRANSFER, (long) CW_CP, (long) cps ); #endif /* K95G */ #endif /* KUI */ clrtoeol(); oldffc = ffc; } #else /* !CPS_WEIGHTED */ #ifdef COMMENT #ifdef DEBUG if (deblog) { debug(F100,"SHOCPS: NOT WEIGHTED","",0); debug(F101,"SHOCPS: pct ","",pct); debug(F101,"SHOCPS: gtv ","",gtv); debug(F101,"SHOCPS: oldgtv ","",oldgtv); debug(F101,"SHOCPS: dgtv ","",(long)gtv - (long)oldgtv); debug(F101,"SHOCPS: ffc ","",ffc); debug(F101,"SHOCPS: oldffc ","",oldffc); debug(F101,"SHOCPS: dffc ","",ffc-oldffc); debug(F101,"SHOCPS: cps ","",cps); debug(F101,"SHOCPS: filcnt ","",filcnt); #ifdef GFTIMER debug(F101,"SHOCPS: fpfsecs","",fpfsecs); #endif /* GFTIMER */ } debug(F101,"shocps gtv","",gtv); #endif /* DEBUG */ #ifdef GFTIMER #endif /* COMMENT */ /* debug(F101,"shocps fpfsecs","",fpfsecs); */ secs = gtv - fpfsecs; /* debug(F101,"shocps secs","",(long)secs); */ if (secs > 0.0) { cps = (long)((CKFLOAT) ffc / secs); /* debug(F101,"shocps cps","",cps); */ move(CW_CP,22); #ifdef KUI #ifndef K95G KuiSetProperty( KUI_FILE_TRANSFER, (long) CW_CP, (long) cps ); #endif /* K95G */ #endif /* KUI */ printw("%ld", cps); clrtoeol(); } #else /* Not GFTIMER */ if ((secs = gtv - fsecs) > 0) { cps = (secs < 1L) ? ffc : ffc / secs; move(CW_CP,22); #ifdef KUI #ifndef K95G KuiSetProperty( KUI_FILE_TRANSFER, (long) CW_CP, (long) cps ); #endif /* K95G */ #endif /* KUI */ printw("%ld", cps); clrtoeol(); } #endif /* GFTIMER */ #endif /* CPS_WEIGHTED */ if (cps > peakcps && /* Peak transfer rate */ ((what & W_SEND && spackets > wslots + 4) || (!(what & W_SEND) && spackets > 10))) { peakcps = cps; } old_tr = shoetl(old_tr, cps, fsiz, howfar); return(cps); } static #ifdef CK_ANSIC /* Because VOID used by curses.h */ void #else #ifdef MYCURSES VOID #else int #endif /* MYCURSES */ #endif /* CK_ANSIC */ scrft() { /* Display file type */ char xferstr[256]; xferstr[0] = NUL; debug(F101,"scrft binary","",binary); if (binary) { switch(binary) { case XYFT_L: ckstrncpy(xferstr,"LABELED",256); break; case XYFT_I: ckstrncpy(xferstr,"IMAGE",256); break; case XYFT_U: ckstrncpy(xferstr,"BINARY UNDEFINED",256); break; case XYFT_M: ckstrncpy(xferstr,"MACBINARY",256); break; case XYFT_X: ckstrncpy(xferstr,"TENEX",256); break; default: case XYFT_B: ckstrncpy(xferstr,"BINARY",256); break; } #ifdef CK_RESEND if (what & W_SEND && sendstart > 0L) { if (sendmode == SM_PSEND) { ckstrncat(xferstr, " / partial", 256); } else if (sendmode == SM_RESEND) { ckstrncat(xferstr, " / resend", 256); } } else if (what & W_RECV && rs_len > 0L) { ckstrncat(xferstr, " / resend", 256); } #endif /* CK_RESEND */ } else { #ifndef NOCSETS ckstrncpy(xferstr,"TEXT",256); #ifdef NEWFTP #ifndef NOUNICODE if (what & W_FTP) { if (ftp_csx < 0) ckstrncat(xferstr," (no translation)", 256); else ckmakxmsg(&xferstr[4],252, " (", fcsinfo[(what & W_SEND) ? ftp_csl : ftp_csx].keyword, " => ", fcsinfo[(what & W_SEND) ? ftp_csx : ftp_csl].keyword, ")", NULL,NULL,NULL,NULL,NULL,NULL,NULL ); } else #endif /* NOUNICODE */ #endif /* NEWFTP */ if (tcharset == TC_TRANSP) { ckstrncat(xferstr, " (no translation)", 256); } else { if (what & W_SEND) { sprintf( &xferstr[strlen(xferstr)], /* safe */ " (%s => %s)", fcsinfo[fcharset].keyword, /* built-in keywords */ tcsinfo[tcharset].keyword /* lengths are controlled */ ); } else { sprintf( &xferstr[strlen(xferstr)], /* safe */ " (%s => %s)", tcsinfo[tcharset].keyword, /* built-in keywords */ fcsinfo[fcharset].keyword); /* lengths controlled */ } } #endif /* NOCSETS */ } move(CW_TYP,22); printw("%s", xferstr); clrtoeol(); #ifdef KUI #ifndef K95G KuiSetProperty( KUI_FILE_TRANSFER, (long) CW_TYP, (long) xferstr ); #endif /* K95G */ #endif /* KUI */ return; } #ifdef CK_NEWTERM static FILE *ck_stdout = NULL; static int ck_fd = -1; #endif /* CK_NEWTERM */ static long pct = 0L, oldpct = 0L, oldrtt = -1L; static int oldtyp = 0, oldwin = -1, oldtry = -1, oldlen = -1, oldtim = -1; #ifdef NETCONN static char *netname[] = { "none", /* 00 */ "TCP/IP", /* 01 TCP (Sockets) */ "TCP/IP", /* 02 TCP (Streams) */ "X.25", /* 03 SunLink X.24 */ "DECnet", /* 04 DECnet */ "VAX PSI", /* 05 VAX PSI */ "Named Pipes", /* 06 LAN Manager Named Pipe */ "X.25", /* 07 Stratus VOS X.25 */ "NetBIOS", /* 08 IBM NETBIOS */ "SuperLAT", /* 07 Meridian SuperLAT */ "File", /* 10 File */ "Command", /* 11 Subprocess (pipe) */ "DLL", /* 12 DLL does i/o */ "X.25", /* 13 IBM AIXLink X.25 */ "X.25", /* 14 HP-UX X.25 */ "PTY", /* 15 Pseudoterminal */ "SSH", /* 16 SSH */ "", /* 17 In case new types are added */ "", /* 18 but nobody remembers to update */ "", /* 19 this table ... */ NULL /* 20 */ }; static int nnetname = (sizeof(netname) / sizeof(char *)); #endif /* NETCONN */ #ifdef CK_ANSIC void screenc(int f, char c,CK_OFF_T n,char *s) #else CKVOID screenc(f,c,n,s) int f; /* argument descriptor */ char c; /* a character or small integer */ CK_OFF_T n; /* a long integer */ char *s; /* a string */ #endif /* CK_ANSIC */ /* screenc() */ { #ifdef CK_SSL extern int tls_active_flag, ssl_active_flag; #endif /* CK_SSL */ #ifdef RLOGCODE extern int ttnproto; #endif /* RLOGCODE */ static int q = 0; static long fcnt = 0L; /* Number of files transferred */ static CK_OFF_T fsiz = (CK_OFF_T)-1; /* Copy of file size */ static CK_OFF_T fbyt = 0L; /* Total file bytes of all files transferred */ static CK_OFF_T howfar = 0L; /* How much of current file has been xfer'd */ static int pctlbl = 0L; /* Percent done vs Bytes so far */ long cps = 0L; int net = 0; int xnet = 0; int ftp = 0; int len; /* Length of string */ int errors = 0; /* Error counter */ int x; /* Worker */ debug(F101,"screenc cinit","",cinit); debug(F101,"screenc cendw","",cendw); if (!s) s = ""; /* Always do this. */ ftp = (what & W_FTP) ? 1 : 0; /* FTP or Kermit */ net = network || ftp; xnet = ftp ? 1 : nettype; /* NET_TCPB == 1 */ if (cinit == 0 || cendw > 0) { /* Handle borderline cases... */ if (f == SCR_CW) { /* Close window, but it's not open */ ft_win = 0; return; } debug(F111,"screenc A",s,f); if (f == SCR_EM || (f == SCR_PT && c == 'E')) { /* Fatal error before window open */ conoll(""); conoc('?'); conoll(s); return; /* Regular display */ } } if (cinit == 0) { /* Only call initscr() once */ char * s; /* Check these now -- if they are defined but not numeric */ /* they can crash curses */ s = getenv("LINES"); if (s) if (!rdigits(s)) { printf("?LINES variable not numeric: \"%s\".\n",s); printf("(Fullscreen display disabled)\n"); fdispla = XYFD_S; return; } s = getenv("COLUMNS"); if (s) if (!rdigits(s)) { printf("?COLUMNS variable not numeric: \"%s\".\n",s); printf("(Fullscreen display disabled)\n"); fdispla = XYFD_S; return; } cendw = 1; /* New window needs repainting */ #ifdef COMMENT if (!initscr()) { /* Oops, can't initialize window? */ /* In fact, this doesn't happen. "man curses" says initscr() halts the entire program if it fails, which is true on the systems where I've tested it. It will fail if your terminal type is not known to it. That's why SET FILE DISPLAY FULLSCREEN calls tgetent() to make sure the terminal type is known before allowing a curses display. */ fprintf(stderr,"CURSES INITSCR ERROR\r\n"); fdispla = XYFD_S; /* Fall back to CRT display */ return; } else { cinit++; /* Window initialized ok */ debug(F100,"CURSES INITSCR OK","",0); } #else /* Save some memory. */ #ifdef CK_NEWTERM /* (From Andy Fyfe ) System V curses seems to reserve the right to alter the buffering on the output FILE* without restoring it. Fortunately System V curses provides newterm(), an alternative to initscr(), that allows us to specify explicitly the terminal type and input and output FILE pointers. Thus we duplicate stdout, and let curses have the copy. The original remains unaltered. Unfortunately, newterm() seems to be particular to System V. */ s = getenv("TERM"); if (ck_fd < 0) { ck_fd = dup(fileno(stdout)); ck_stdout = (ck_fd >= 0) ? (FILE *)fdopen(ck_fd, "w") : NULL; } debug(F100,"screenc newterm...","",0); /* NOTE: It might be necessary to do this with stdin too! */ /* This would have been the case in FreeBSD 4.1 but they fixed the */ /* problem by restoring the buffering of stdin before the final release. */ /* (But T.E. Dickey says stdin is not buffered?) */ if (ck_stdout == NULL || newterm(s, ck_stdout, stdin) == 0) { fprintf(stderr, "Fullscreen display not supported for terminal type: %s\r\n",s); fdispla = XYFD_S; /* Use CRT instead */ return; } debug(F100,"screenc newterm ok","",0); #else debug(F100,"screen calling initscr","",0); initscr(); /* Initialize curses. */ debug(F100,"screen initscr ok","",0); #endif /* CK_NEWTERM */ cinit++; /* Remember curses was initialized. */ #endif /* COMMENT */ } ft_win = 1; /* Window is open */ if (repaint) { #ifdef CK_WREFRESH /* This totally repaints the screen, just what we want, but we can only do this with real curses, and then only if clearok() and wrefresh() are provided in the curses library. */ #ifdef OS2 RestoreCmdMode(); #else #ifdef QNX #ifndef QNX16 clearok(stdscr, 1); /* QNX doesn't have curscr */ #endif /* QNX16 */ wrefresh(stdscr); #else wrefresh(curscr); #endif /* QNX */ #endif /* OS2 */ #else /* No CK_WREFRESH */ /* Kermit's do-it-yourself method, works with all types of fullscreen support, but does not repaint all the fields. For example, the filename is lost, because it arrives at a certain time and never comes again, and Kermit presently does not save it anywhere. Making this method work for all fields would be a rather major recoding task, duplicating what curses already does, and would add a lot of complexity and storage space. */ cendw = 1; #endif /* CK_WREFRESH */ repaint = 0; } if (cendw) { /* endwin() was called previously */ #ifdef VMS initscr(); /* (or should have been!) */ clear(); touchwin(stdscr); refresh(); #else #ifdef QNX /* In QNX, if we don't call initscr() here we core dump. I don't have any QNX curses documentation, but other curses manuals say that initscr() should be called only once per application, and experience shows that on other systems, calling initscr() here generally results in a core dump. */ debug(F100,"screenc re-calling initscr QNX","",0); initscr(); clear(); refresh(); #ifdef COMMENT /* But even so, second and subsequent curses displays are messed up. Calling touchwin, refresh, etc, doesn't make any difference. */ debug(F100,"screenc calling touchwin QNX","",0); touchwin(stdscr); debug(F100,"screenc calling refresh QNX","",0); refresh(); #endif /* COMMENT */ #else /* All others... */ debug(F100,"screenc calling clear","",0); clear(); debug(F100,"screenc clear ok","",0); #endif /* QNX */ #endif /* VMS */ debug(F100,"screenc setup ok","",0); debug(F100,"screenc doing first move","",0); move(CW_BAN,0); /* Display the banner */ debug(F110,"screenc myhost",myhost,0); #ifdef TCPSOCKET debug(F110,"screenc myipaddr",myipaddr,0); #endif /* TCPSOCKET */ #ifdef HPUX1010 debug(F100,"screenc calling first printw...","",0); /* Right here is where HP-UX 10.10 libxcurse.1 Rev 76.20 hangs... */ #endif /* HPUX1010 */ if (myhost[0]) { #ifdef TCPSOCKET if (!myipaddr[0] #ifdef OS2 /* We need to perform this test because on non-TCP/IP */ /* systems the call to getlocalipaddr() results in a */ /* DNS Lookup which takes several minutes to time out */ && net && (xnet == NET_TCPA || xnet == NET_TCPB #ifdef SSHBUILTIN || xnet == NET_SSH #endif /* SSHBUILTIN */ ) #endif /* OS2 */ ) getlocalipaddr(); if (myipaddr[0] && strcmp((char *)myhost,(char *)myipaddr)) printw("%s, %s [%s]",versio,(char *)myhost,(char *)myipaddr); else #endif /* TCPSOCKET */ printw("%s, %s",versio,(char *)myhost); } else { printw("%s",versio); } #ifdef HPUX1010 debug(F100,"screenc first printw returns","",0); #endif /* HPUX1010 */ move(CW_DIR,3); printw("Current Directory: %s",zgtdir()); #ifdef KUI #ifndef K95G KuiSetProperty( KUI_FILE_TRANSFER, (long) CW_DIR, (long) zgtdir() ); #endif /* K95G */ #endif /* KUI */ if (net) { move(CW_LIN,8); printw("Network Host: %s", #ifdef NEWFTP ftp ? (ftp_host ? ftp_host : "(unknown)") : #endif /* NEWFTP */ ttname ); } else { move(CW_LIN,0); printw("Communication Device: %s",ttname); } #ifdef KUI #ifndef K95G KuiSetProperty( KUI_FILE_TRANSFER, (long) CW_LIN, (long) ttname ); #endif /* K95G */ #endif /* KUI */ if (net) { move(CW_SPD,8); printw("Network Type: "); } else { move(CW_SPD,1); printw("Communication Speed: "); } move(CW_SPD,22); /* Serial speed or network type */ if (net) { #ifdef NETCONN int secure = 0; char * xname; if (xnet > nnetname) xname = "[ERROR]"; else xname = netname[xnet]; #ifdef NEWFTP if (ftp) { if (ftpissecure()) secure = 1; } else #endif /* NEWFTP */ if (0 #ifdef SSHBUILTIN || IS_SSH() #endif /* SSHBUILTIN */ #ifdef CK_ENCRYPTION || ck_tn_encrypting() && ck_tn_decrypting() #endif /* CK_ENCRYPTION */ #ifdef CK_SSL || tls_active_flag || ssl_active_flag #endif /* CK_SSL */ #ifdef RLOGCODE #ifdef CK_KERBEROS #ifdef CK_ENCRYPTION || ttnproto == NP_EK4LOGIN || ttnproto == NP_EK5LOGIN #endif /* CK_ENCRYPTION */ #endif /* CK_KERBEROS */ #endif /* RLOGCODE */ ) { secure = 1; } if (secure) { #ifdef KUI #ifndef K95G char buf[30]; sprintf(buf,"%s (SECURE)",xname); KuiSetProperty(KUI_FILE_TRANSFER, (long) CW_SPD, (long) buf ); #endif /* K95G */ #endif /* KUI */ printw("%s (SECURE)",xname); } else { printw("%s",xname); #ifdef KUI #ifndef K95G KuiSetProperty(KUI_FILE_TRANSFER, (long) CW_SPD, (long) xname ); #endif /* K95G */ #endif /* KUI */ } #else printw("(network)"); #ifdef KUI #ifndef K95G KuiSetProperty(KUI_FILE_TRANSFER, (long) CW_SPD, (long) "(network)" ); #endif /* K95G */ #endif /* KUI */ #endif /* NETCONN */ } else { if (speed < 0L) speed = ttgspd(); if (speed > 0L) { if (speed == 8880) { printw("75/1200"); #ifdef KUI #ifndef K95G KuiSetProperty(KUI_FILE_TRANSFER, (long) CW_SPD, (long) "75/1200" ); #endif /* K95G */ #endif /* KUI */ } else { char speedbuf[64] ; sprintf(speedbuf, "%ld", speed); printw("%s",speedbuf); #ifdef KUI #ifndef K95G KuiSetProperty(KUI_FILE_TRANSFER, (long) CW_SPD, (long) speedbuf ); #endif /* K95G */ #endif /* KUI */ } } else { printw("unknown"); #ifdef KUI #ifndef K95G KuiSetProperty(KUI_FILE_TRANSFER, (long) CW_SPD, (long) "(unknown)" ); #endif /* K95G */ #endif /* KUI */ } } move(CW_PAR,14); printw("Parity: %s",ftp ? "none" : parnam((char)parity)); #ifdef KUI #ifndef K95G KuiSetProperty(KUI_FILE_TRANSFER, (long) CW_PAR, (long) parnam((char)parity) ); #endif /* K95G */ #endif /* KUI */ #ifdef CK_TIMERS if (/* rttflg && */ protocol == PROTO_K) { move(CW_TMO, 9); printw("RTT/Timeout:"); } #endif /* CK_TIMERS */ move(CW_TYP,11); printw("File Type:"); move(CW_SIZ,11); printw("File Size:"); move(CW_PCD, 8); clrtoeol(); pctlbl = (what & W_SEND); printw("%s:", pctlbl ? "Percent Done" : "Bytes So Far"); #ifdef XYZ_INTERNAL move(CW_BAR, 1); printw("%10s Protocol:", ftp ? "FTP" : ptab[protocol].p_name); #endif /* XYZ_INTERNAL */ #ifdef CK_PCT_BAR if (thermometer) { oldpct = pct = 0; move(CW_BAR,22); printw(" ...10...20...30...40...50...60...70...80...90..100"); move(CW_BAR,22+56); } #endif /* CK_PCT_BAR */ move(CW_TR, 1); printw("Estimated Time Left:"); move(CW_CP, 2); printw("Transfer Rate, CPS:"); move(CW_WS, 8); printw("Window Slots:%s", ((protocol == PROTO_K) && !ftp) ? "" : " N/A" ); move(CW_PT, 9); printw("Packet Type:"); if (ftp || protocol != PROTO_K) { move(CW_PT,22); printw("%s", "N/A"); move(CW_PC, 11); printw("I/O Count:"); move(CW_PL, 10); printw("I/O Length:"); } else { move(CW_PC, 8); printw("Packet Count:"); move(CW_PL, 7); printw("Packet Length:"); } #ifndef COMMENT move(CW_PR, 9); printw("Error Count:"); #else move(CW_PR, 2); printw("Packet Retry Count:"); #endif #ifdef COMMENT move(CW_PB, 2); printw("Packet Block Check:"); #endif /* COMMENT */ move(CW_ERR,10); printw("Last Error:"); move(CW_MSG, 8); printw("Last Message:"); if (xfrmsg) { move(CW_MSG, 22); printw("%s",xfrmsg); makestr(&xfrmsg,NULL); } move(CW_INT, 0); if (!xfrint) { printw("(Transfer interruption is disabled)"); } else { #ifdef CK_NEED_SIG printw( "<%s>X to cancel file, <%s>Z to cancel group, <%s> to resend last packet", dbchr(escape), dbchr(escape), dbchr(escape) ); move(CW_INT + 1, 0); printw( "<%s>E to send Error packet, ^C to quit immediately, <%s>L to refresh screen.", dbchr(escape), dbchr(escape) ); #else /* !CK_NEED_SIG */ move(CW_INT, 0); #ifdef OS2 if (protocol == PROTO_K) { printw( "X to cancel file, Z to cancel group, to resend last packet," ); } #else /* !OS2 */ #ifdef VMS /* In VMS avoid bottom line */ printw( "X: Cancel this file; E: Cancel transfer; ^C: Quit now; ^W: Refresh screen." ); #else printw( "X to cancel file, Z to cancel group, to resend last packet," ); #endif /* VMS */ #endif /* OS2 */ #ifndef VMS move(CW_INT + 1, 0); if (protocol == PROTO_K) { printw( "E to send Error packet, ^C to quit immediately, ^L to refresh screen." ); } else { printw("^C to cancel file transfer."); } #endif /* VMS */ #endif /* CK_NEED_SIG */ } refresh(); cendw = 0; } debug(F101,"SCREENC switch","",f); debug(F000,"SCREENC c","",c); debug(F101,"SCREENC n","",n); len = strlen(s); /* Length of argument string */ switch (f) { /* Handle our function code */ case SCR_FN: /* Filename */ oldpct = pct = 0L; /* Reset percents */ #ifdef GFTIMER gtv = (CKFLOAT) -1.0; /* oldgtv = (CKFLOAT) -1.0; */ #else gtv = -1L; /* oldgtv = -1L; */ #endif /* GFTIMER */ oldwin = -1; fsiz = (CK_OFF_T)-1; /* Invalidate previous file size */ move(CW_PCD,22); /* Erase percent done from last time */ #ifdef KUI #ifndef K95G KuiSetProperty( KUI_FILE_TRANSFER, (long) CW_PCD, (long) 0 ); KuiSetProperty( KUI_FILE_TRANSFER, (long) CW_FFC, (long) 0 ); #endif /* K95G */ #endif /* KUI */ clrtoeol(); move(CW_SIZ,22); /* Erase file size from last time */ #ifdef KUI #ifndef K95G KuiSetProperty( KUI_FILE_TRANSFER, (long) CW_SIZ, (long) 0 ); #endif /* K95G */ #endif /* KUI */ clrtoeol(); move(CW_ERR,22); /* And last error message */ #ifdef KUI #ifndef K95G KuiSetProperty( KUI_FILE_TRANSFER, (long) CW_ERR, (long) "" ); #endif /* K95G */ #endif /* KUI */ clrtoeol(); #ifdef COMMENT #ifdef STREAMING if (protocol == PROTO_K && streamok) { move(CW_BAR, 1); #ifdef XYZ_INTERNAL printw(" Kermit STREAMING:"); #else printw(" STREAMING:"); #endif /* XYZ_INTERNAL */ } #endif /* STREAMING */ #endif /* COMMENT */ if (what & W_SEND) { /* If we're sending... */ #ifdef NEWFTP if (what & W_FTP) { /* FTP */ move(CW_NAM,10); printw(" FTP PUT:"); } else #endif /* NEWFTP */ #ifdef CK_RESEND switch (sendmode) { /* Kermit */ case SM_RESEND: move(CW_NAM,10); printw(" RESENDING:"); break; default: move(CW_NAM,10); printw(" SENDING:"); break; } #else move(CW_NAM,10); printw(" SENDING:"); #endif /* CK_RESEND */ } else if (what & W_RECV) { /* If we're receiving... */ #ifdef NEWFTP if (what & W_FTP) { /* FTP */ move(CW_NAM,10); printw(" FTP GET:"); } else { #endif /* NEWFTP */ move(CW_NAM,10); printw(" RECEIVING:"); #ifdef NEWFTP } } else if (what == (W_FTP|W_FT_DELE)) { move(CW_NAM,10); printw("FTP DELETE:"); #endif /* NEWFTP */ } else { /* If we don't know... */ move(CW_NAM,10); /* (should never see this) */ printw(" File Name:"); } move(CW_NAM,22); /* Display the filename */ if (len > 57) { printw("%.55s..",s); len = 57; } else printw("%s",s); #ifdef KUI #ifndef K95G KuiSetProperty( KUI_FILE_TRANSFER, (long) CW_NAM, (long) s ); #endif /* K95G */ #endif /* KUI */ q = len; /* Remember name length for later */ clrtoeol(); scrft(); /* Display file type (can change) */ refresh(); #ifdef OS2 SaveCmdMode(0, 0); #endif /* OS2 */ return; case SCR_AN: /* File as-name */ if (q + len + 4 < 58) { /* Will fit */ move(CW_NAM, 22 + q); printw(" => %s",s); #ifdef KUI #ifndef K95G KuiSetProperty( KUI_FILE_TRANSFER, (long) CW_NAM, (long) s ); #endif /* K95G */ #endif /* KUI */ } else { /* Too long */ move(CW_NAM, 22); /* Overwrite previous name */ q = 0; if (len + 4 > 57) { /* wg15 */ printw(" => %.51s..",s); /* wg15 */ len = 53; /* wg15 */ } else printw(" => %s",s); /* wg15 */ #ifdef KUI #ifndef K95G KuiSetProperty( KUI_FILE_TRANSFER, (long) CW_NAM, (long) s ); #endif /* K95G */ #endif /* KUI */ } q += len + 4; /* Remember horizontal position */ clrtoeol(); refresh(); #ifdef OS2 SaveCmdMode(0, 0); #endif /* OS2 */ return; case SCR_FS: /* File size */ fsiz = n; move(CW_SIZ,22); if (fsiz > (CK_OFF_T)-1) { #ifdef KUI #ifndef K95G KuiSetProperty( KUI_FILE_TRANSFER, (long) CW_SIZ, (long) n ); #endif /* K95G */ #endif /* KUI */ printw("%s",ckfstoa(n)); } if (fsiz == -2L) { printw("POSSIBLY EXCEEDS LOCAL FILE SIZE LIMIT"); } clrtoeol(); #ifdef COMMENT move(CW_PCD, 8); if (fsiz > (CK_OFF_T)-1) { /* Put up percent label */ pctlbl = 1; clrtoeol(); printw("Percent Done:"); } #else move(CW_PCD, 8); clrtoeol(); if (fsiz > (CK_OFF_T)-1) { /* Put up percent label */ pctlbl = 1; printw("Percent Done:"); } else { pctlbl = 0; printw("Bytes So Far:"); } #endif /* COMMENT */ clrtoeol(); scrft(); /* File type */ refresh(); #ifdef OS2 SaveCmdMode(0, 0); #endif /* OS2 */ return; case SCR_PT: /* Packet type or pseudotype */ if (spackets < 5) { extern int sysindex; extern struct sysdata sysidlist[]; /* Things that won't change after the 4th packet */ move(CW_PAR,22); printw("%s",parnam((char)parity)); #ifdef KUI #ifndef K95G KuiSetProperty( KUI_FILE_TRANSFER, (long) CW_PAR, (long) parnam((char)parity) ); #endif /* K95G */ #endif /* KUI */ clrtoeol(); #ifdef COMMENT move(CW_PB, 22); /* Block check on this packet */ if (bctu == 4) printw("B"); else printw("%d",bctu); clrtoeol(); #endif /* COMMENT */ if ( #ifdef NEWFTP (ftp && (spackets == 1 || rpackets == 1)) || #endif /* NEWFTP */ spackets == 4 ) { move(CW_LIN,8); if ( #ifdef NEWFTP ftp || #endif /* NEWFTP */ ((protocol == PROTO_K) && (sysindex > -1)) ) { if (net) { move(CW_LIN,8); printw("Network Host: %s (%s)", #ifdef NEWFTP ftp ? (ftp_host ? ftp_host : "") : #endif /* NEWFTP */ ttname, #ifdef NEWFTP ftp ? ftp_srvtyp : #endif /* NEWFTP */ sysidlist[sysindex].sid_name ); } else { move(CW_LIN,0); printw("Communication Device: %s (remote host is %s)", ttname, sysidlist[sysindex].sid_name ); } clrtoeol(); } } } #ifdef CK_TIMERS if (/* rttflg && */ protocol == PROTO_K) { long xx; if ( #ifdef STREAMING streaming && oldwin != -2 #else 0 #endif /* STREAMING */ ) { move(CW_TMO, 22); printw("00 / 00"); clrtoeol(); } else { xx = (rttdelay + 500) / 1000; if (xx != oldrtt || rcvtimo != oldtim) { move(CW_TMO, 22); printw("%02ld / %02d", xx, rcvtimo); oldrtt = xx; oldtim = rcvtimo; clrtoeol(); } } } #endif /* CK_TIMERS */ x = (what & W_RECV) ? /* Packet length */ rpktl+(protocol==PROTO_K?1:0) : spktl; if (x != oldlen) { /* But only if it changed. */ move(CW_PL, 22); printw("%d",x); #ifdef KUI #ifndef K95G KuiSetProperty( KUI_FILE_TRANSFER, (long) CW_PL, (long) x ); #endif /* K95G */ #endif /* KUI */ clrtoeol(); oldlen = x; } move(CW_PC, 22); /* Packet count (always). */ printw("%d", (what & W_RECV) ? rpackets : spackets); #ifdef KUI #ifndef K95G KuiSetProperty( KUI_FILE_TRANSFER, (long) CW_PC, (long) spackets ); #endif /* K95G */ #endif /* KUI */ clrtoeol(); if (protocol == PROTO_K && !ftp) { /* Window slots */ char ws[16]; int flag; flag = 0; #ifdef STREAMING if (streaming) { if (oldwin != -2) { sprintf(ws,"STREAMING"); flag = 1; oldwin = -2; } } else #endif /* STREAMING */ if (wcur != oldwin) { sprintf(ws, "%d of %d", wcur < 1 ? 1 : wcur, wslotn); flag = 1; oldwin = wcur; } if (flag) { move(CW_WS, 22); printw("%s", ws); clrtoeol(); #ifdef KUI #ifndef K95G KuiSetProperty( KUI_FILE_TRANSFER, (long) CW_WS, (long) ws ); #endif /* K95G */ #endif /* KUI */ } } errors = retrans + crunched + timeouts; if (errors != oldtry) { /* Retry count, if changed */ move(CW_PR, 22); printw("%d",errors); #ifdef KUI #ifndef K95G KuiSetProperty( KUI_FILE_TRANSFER, (long) CW_PR, (long) errors ); #endif /* K95G */ #endif /* KUI */ clrtoeol(); oldtry = errors; } /* Sender's packet type */ if (!ftp && (c != oldtyp && c != 'Y' && c != 'N')) { char type[2]; sprintf(type, "%c",c); move(CW_PT,22); printw("%s", type); #ifdef KUI #ifndef K95G KuiSetProperty( KUI_FILE_TRANSFER, (long) CW_PT, (long) type ); #endif /* K95G */ #endif /* KUI */ clrtoeol(); oldtyp = c; } switch (c) { /* Now handle specific packet types */ case 'S': /* Beginning of transfer */ fcnt = fbyt = 0L; /* Clear counters */ #ifdef GFTIMER gtv = -1.0; #else /* GFTIMER */ gtv = -1L; /* And old/new things... */ #endif /* GFTIMER */ oldpct = pct = 0L; break; case 'Z': /* or EOF */ debug(F101,"screenc SCR_PT Z pktnum","",n); debug(F101,"screenc SCR_PT Z oldpct","",oldpct); debug(F101,"screenc SCR_PT Z pct","",pct); case 'D': /* Data packet */ if (fsiz > 0L) { /* Show percent done if known */ oldpct = pct; /* Remember previous percent */ howfar = ffc; #ifdef CK_RESEND if (what & W_SEND) /* Account for PSEND or RESEND */ howfar += sendstart; else if (what & W_RECV) howfar += rs_len; #endif /* CK_RESEND */ /* Percent done, to be displayed... */ if (c == 'Z') { if (!discard && !cxseen && !czseen) pct = 100L; } else pct = (fsiz > 99L) ? (howfar / (fsiz / 100L)) : 0L; if (pct > 100L || /* Allow for expansion and */ (oldpct == 99L && pct < 0L)) /* other boundary conditions */ pct = 100L; if (pct != oldpct) /* Only do this 100 times per file */ updpct(oldpct, pct); } else { move(CW_PCD,22); printw("%s", ckfstoa(ffc)); } #ifdef KUI #ifndef K95G KuiSetProperty(KUI_FILE_TRANSFER, (long) CW_FFC, (long) howfar); #endif /* K95G */ #endif /* KUI */ cps = shocps((int) pct, fsiz, howfar); /* old_tr = shoetl(old_tr, cps, fsiz, howfar); */ break; case '%': /* Timeouts, retransmissions */ cps = shocps((int) pct, fsiz, howfar); /* old_tr = shoetl(old_tr, cps, fsiz, howfar); */ errors = retrans + crunched + timeouts; if (errors != oldtry) { /* Error count, if changed */ move(CW_PR, 22); printw("%d",errors); clrtoeol(); #ifdef KUI #ifndef K95G KuiSetProperty(KUI_FILE_TRANSFER, (long) CW_PR, (long) errors ); #endif /* K95G */ #endif /* KUI */ } oldtry = errors; if (s) if (*s) { move(CW_ERR,22); printw("%s",s); clrtoeol(); #ifdef KUI #ifndef K95G KuiSetProperty(KUI_FILE_TRANSFER, (long) CW_ERR, (long) s); #endif /* K95G */ #endif /* KUI */ } break; case 'E': /* Error packet */ #ifdef COMMENT move(CW_ERR,22); /* Print its data field */ if (*s) { printw("%s",s); #ifdef KUI #ifndef K95G KuiSetProperty( KUI_FILE_TRANSFER, (long) CW_ERR, (long) s ); #endif /* K95G */ #endif /* KUI */ } clrtoeol(); #endif /* COMMENT */ fcnt = fbyt = 0L; /* So no bytes for this file */ break; case 'Q': /* Crunched packet */ cps = shocps((int) pct, fsiz, howfar); /* old_tr = shoetl(old_tr, cps, fsiz, howfar); */ move(CW_ERR,22); printw("Damaged Packet"); #ifdef KUI #ifndef K95G KuiSetProperty(KUI_FILE_TRANSFER, (long) CW_ERR, (long) "Damaged Packet" ); #endif /* K95G */ #endif /* KUI */ clrtoeol(); break; case 'q': /* Ctrl-C or connection lost */ move(CW_MSG,22); clrtoeol(); if (!s) s = ""; printw(*s ? s : "User interruption or connection lost"); #ifdef KUI #ifndef K95G KuiSetProperty(KUI_FILE_TRANSFER, (long) CW_MSG, (long) s ); #endif /* K95G */ #endif /* KUI */ break; case 'T': /* Timeout */ cps = shocps((int) pct, fsiz, howfar); /* old_tr = shoetl(old_tr, cps, fsiz, howfar); */ move(CW_ERR,22); printw("Timeout %d sec",rcvtimo); #ifdef KUI #ifndef K95G KuiSetProperty(KUI_FILE_TRANSFER, (long) CW_ERR, (long) "Timeout" ); #endif /* K95G */ #endif /* KUI */ clrtoeol(); errors = retrans + crunched + timeouts; if (errors != oldtry) { /* Error count, if changed */ move(CW_PR, 22); printw("%d",errors); #ifdef KUI #ifndef K95G KuiSetProperty(KUI_FILE_TRANSFER, (long) CW_PR, (long) errors ); #endif /* K95G */ #endif /* KUI */ clrtoeol(); oldtry = errors; } break; default: /* Others, do nothing */ break; } refresh(); #ifdef OS2 SaveCmdMode(0, 0); #endif /* OS2 */ return; case SCR_ST: /* File transfer status */ debug(F101,"screenc SCR_ST c","",c); debug(F101,"screenc SCR_ST success","",success); debug(F101,"screenc SCR_ST cxseen","",cxseen); #ifdef COMMENT move(CW_PCD,22); /* Update percent done */ if (c == ST_OK) { /* OK, print 100 % */ if (pctlbl) updpct(oldpct,100); else printw("%s", ckfstoa(ffc)); #ifdef KUI #ifndef K95G KuiSetProperty(KUI_FILE_TRANSFER, (long) CW_FFC, (long) ffc); #endif /* K95G */ #endif /* KUI */ pct = 100; oldpct = 0; } else if (fsiz > 0L) /* Not OK, update final percent */ /* The else part writes all over the screen -- howfar and/or fsiz have been reset as a consequence of the not-OKness of the transfer. */ if (pctlbl) updpct(oldpct, (howfar * 100L) / fsiz); clrtoeol(); #else if (c == ST_OK) { /* OK, print 100 % */ move(CW_PCD,22); /* Update percent done */ if (pctlbl) { if (oldpct == 0) /* Switching from "bytes so far" */ clrtoeol(); /* to "percent done"... */ updpct(oldpct,100); } else printw("%s", ckfstoa(ffc)); #ifdef KUI #ifndef K95G KuiSetProperty(KUI_FILE_TRANSFER, (long) CW_FFC, (long) ffc); #endif /* K95G */ #endif /* KUI */ #ifdef COMMENT pct = 100; oldpct = 0; #endif /* COMMENT */ clrtoeol(); } #endif /* COMMENT */ #ifdef COMMENT /* No, leave it there so they can read it */ move(CW_MSG,22); /* Remove any previous message */ #ifdef KUI #ifndef K95G KuiSetProperty( KUI_FILE_TRANSFER, (long) CW_MSG, (long) "" ); #endif /* K95G */ #endif /* KUI */ clrtoeol(); refresh(); #endif /* COMMENT */ move(CW_TR, 22); #ifdef KUI #ifndef K95G KuiSetProperty( KUI_FILE_TRANSFER, (long) CW_TR, (long) "" ); #endif /* K95G */ #endif /* KUI */ clrtoeol(); refresh(); switch (c) { /* Print new status message */ case ST_OK: /* Transfer OK */ fcnt++; /* Count this file */ if (what == (W_FTP|W_FT_DELE)) { move(CW_MSG,22); clrtoeol(); printw("Delete OK"); } else { fbyt += ffc; move(CW_MSG,22); clrtoeol(); printw("Transfer OK"); } #ifdef KUI #ifndef K95G KuiSetProperty(KUI_FILE_TRANSFER, (long) CW_MSG, (long) "Transfer OK" ); #endif /* K95G */ #endif /* KUI */ clrtoeol(); refresh(); return; case ST_DISC: /* Discarded */ move(CW_ERR,22); printw("File discarded"); #ifdef KUI #ifndef K95G KuiSetProperty(KUI_FILE_TRANSFER, (long) CW_ERR, (long) "File discarded" ); #endif /* K95G */ #endif /* KUI */ #ifdef COMMENT pct = oldpct = 0; #endif /* COMMENT */ clrtoeol(); refresh(); return; case ST_INT: /* Interrupted */ move(CW_ERR,22); printw("Transfer interrupted"); #ifdef KUI #ifndef K95G KuiSetProperty(KUI_FILE_TRANSFER, (long) CW_ERR, (long) "Transfer interrupted" ); #endif /* K95G */ #endif /* KUI */ #ifdef COMMENT pct = oldpct = 0; #endif /* COMMENT */ clrtoeol(); refresh(); return; case ST_SKIP: /* Skipped */ move(CW_ERR,22); if (n > 0 && n < nskreason) printw("File skipped (%s)",skreason[n]); else printw("File skipped"); #ifdef KUI #ifndef K95G KuiSetProperty(KUI_FILE_TRANSFER, (long) CW_ERR, (long) "File skipped" ); #endif /* K95G */ #endif /* KUI */ #ifdef COMMENT pct = oldpct = 0; #endif /* COMMENT */ clrtoeol(); refresh(); return; case ST_ERR: /* Error message */ move(CW_ERR,22); if (!s) s = (char *)epktmsg; printw("%s",s); #ifdef KUI #ifndef K95G KuiSetProperty( KUI_FILE_TRANSFER, (long) CW_ERR, (long) s ); #endif /* K95G */ #endif /* KUI */ #ifdef COMMENT pct = oldpct = 0; #endif /* COMMENT */ clrtoeol(); refresh(); return; case ST_REFU: /* Refused */ move(CW_ERR,22); if (*s) { char errbuf[64] ; sprintf( errbuf, "Refused, %s", s ) ; printw("%s", errbuf); #ifdef KUI #ifndef K95G KuiSetProperty(KUI_FILE_TRANSFER,(long) CW_ERR,(long) errbuf); #endif /* K95G */ #endif /* KUI */ } else { printw("Refused"); #ifdef KUI #ifndef K95G KuiSetProperty(KUI_FILE_TRANSFER,(long)CW_ERR,(long)"Refused"); #endif /* K95G */ #endif /* KUI */ } #ifdef COMMENT pct = oldpct = 0; #endif /* COMMENT */ clrtoeol(); refresh(); return; case ST_INC: move(CW_ERR,22); printw("Incomplete"); #ifdef KUI #ifndef K95G KuiSetProperty(KUI_FILE_TRANSFER,(long)CW_ERR,(long)"Incomplete"); #endif /* K95G */ #endif /* KUI */ #ifdef COMMENT pct = oldpct = 0; #endif /* COMMENT */ clrtoeol(); refresh(); return; case ST_MSG: move(CW_MSG,22); printw("%s",s); #ifdef KUI #ifndef K95G KuiSetProperty(KUI_FILE_TRANSFER,(long)CW_MSG,(long)s); #endif /* K95G */ #endif /* KUI */ clrtoeol(); refresh(); return; default: /* Bad call */ move(CW_ERR,22); printw("*** screen() called with bad status ***"); #ifdef KUI #ifndef K95G KuiSetProperty( KUI_FILE_TRANSFER, (long) CW_ERR, (long) "*** screen() called with bad status ***" ); #endif /* K95G */ #endif /* KUI */ clrtoeol(); refresh(); return; } case SCR_TC: { /* Transaction complete */ char msgbuf[128]; move(CW_CP,22); /* Overall transfer rate */ #ifdef KUI #ifndef K95G KuiSetProperty( KUI_FILE_TRANSFER, (long) CW_CP, tfcps); #endif /* K95G */ #endif /* KUI */ printw("%s", ckfstoa(tfcps)); clrtoeol(); if (success) { move(CW_MSG,22); /* Print statistics in message line */ clrtoeol(); } if (success) { sprintf(msgbuf, "SUCCESS. Files: %ld, Bytes: %s, %ld CPS", filcnt - filrej, ckfstoa(fbyt), tfcps ); printw("%s", msgbuf); #ifdef KUI #ifndef K95G KuiSetProperty(KUI_FILE_TRANSFER, (long) CW_MSG, (long) msgbuf ); #endif /* K95G */ #endif /* KUI */ clrtoeol(); } move(CW_TR, 1); printw(" Elapsed Time: %s",hhmmss((long) #ifdef GFTIMER (fptsecs + 0.5) #else tsecs #endif /* GFTIMER */ )); #ifdef KUI #ifndef K95G KuiSetProperty(KUI_FILE_TRANSFER, (long) CW_TR, (long) hhmmss((long) #ifdef GFTIMER (fptsecs + 0.5) #else tsecs #endif /* GFTIMER */ )); #endif /* K95G */ #endif /* KUI */ clrtoeol(); move(23,0); clrtoeol(); /* Clear instructions lines */ move(22,0); clrtoeol(); /* to make room for prompt. */ refresh(); #ifdef GFTIMER oldgtv = (CKFLOAT) -1.0; #else oldgtv = -1L; #endif /* GFTIMER */ #ifndef VMSCURSE debug(F100,"screenc endwin A","",0); endwin(); #ifdef COMMENT /* Why and when was this call to conres() added? It makes no sense, and it breaks echoing on Solaris 8. */ #ifdef SOLARIS conres(); #endif /* SOLARIS */ #endif /* COMMENT */ #endif /* VMSCURSE */ #ifdef COMMENT pct = 100; oldpct = 0; /* Reset these for next time. */ #endif /* COMMENT */ oldtyp = 0; oldrtt = -1L; oldtry = -1; oldlen = -1; oldtim = -1; cendw = 1; if (xfrbel) bleep(BP_NOTE); /* Close window, then beep. */ #ifdef UNIX fflush(stdout); #endif /* UNIX */ ft_win = 0; /* Window closed. */ return; } case SCR_EM: /* Error packet (fatal) */ move (CW_ERR,22); printw("FAILURE: %s",s); #ifdef KUI #ifndef K95G KuiSetProperty( KUI_FILE_TRANSFER, (long) CW_ERR, (long) s ); #endif /* K95G */ #endif /* KUI */ if (xfrbel) bleep(BP_FAIL); #ifdef COMMENT pct = oldpct = 0; #endif /* COMMENT */ clrtoeol(); refresh(); return; case SCR_QE: /* Quantity equals */ case SCR_TU: /* Undelimited text */ case SCR_TN: /* Text delimited at start */ case SCR_TZ: /* Text delimited at end */ return; /* (ignored in fullscreen display) */ case SCR_MS: /* Message from Kermit partner */ move(CW_MSG,22); printw("%s",s); clrtoeol(); refresh(); return; case SCR_XD: /* X-packet data */ pct = oldpct = 0; move(CW_NAM,22); printw("%s",s); #ifdef KUI #ifndef K95G KuiSetProperty( KUI_FILE_TRANSFER, (long) CW_NAM, (long) s ); #endif /* K95G */ #endif /* KUI */ clrtoeol(); refresh(); return; case SCR_CW: /* Close Window */ clrtoeol(); move(23,0); clrtoeol(); move(22,0); clrtoeol(); refresh(); #ifdef COMMENT pct = 100; oldpct = 0; /* Reset these for next time. */ #endif /* COMMENT */ oldtyp = 0; oldrtt = -1L; oldtry = -1; oldlen = -1; oldtim = -1; #ifndef VMSCURSE debug(F100,"screenc endwin B","",0); endwin(); #endif /* VMSCURSE */ ft_win = 0; /* Flag that window is closed. */ cendw = 1; return; case SCR_CD: /* Display current directory */ move(CW_DIR,22); printw("%s", s); #ifdef KUI #ifndef K95G KuiSetProperty( KUI_FILE_TRANSFER, (long) CW_DIR, (long) s ); #endif /* K95G */ #endif /* KUI */ clrtoeol(); refresh(); #ifdef OS2 SaveCmdMode(0, 0); #endif /* OS2 */ return; default: /* Bad call */ move (CW_ERR,22); #ifdef KUI #ifndef K95G KuiSetProperty(KUI_FILE_TRANSFER, (long) CW_ERR, (long) "*** screen() called with bad function code ***" ); #endif /* K95G */ #endif /* KUI */ printw("*** screen() called with bad function code ***"); clrtoeol(); refresh(); return; } } #endif /* CK_CURSES */ #ifdef KUI #ifdef CK_ANSIC void screeng(int f, char c,long n,char *s) #else CKVOID screeng(f,c,n,s) int f; /* argument descriptor */ char c; /* a character or small integer */ long n; /* a long integer */ char *s; /* a string */ #endif /* CK_ANSIC */ /* screeng() */ { #ifdef CK_SSL extern int tls_active_flag, ssl_active_flag; #endif /* CK_SSL */ #ifdef RLOGCODE extern int ttnproto; #endif /* RLOGCODE */ static int q = 0; static CK_OFF_T fsiz = (CK_OFF_T)-1; /* Copy of file size */ static long fcnt = 0L; /* Number of files transferred */ static long fbyt = 0L; /* Total file bytes of all files transferred */ static CK_OFF_T howfar = (CK_OFF_T)0; /* How much of file xfer'd so far */ static int pctlbl = 0L; /* Percent done vs Bytes so far */ long cps = 0L; int net = 0; int xnet = 0; int ftp = 0; int len; /* Length of string */ int errors = 0; /* Error counter */ int x; /* Worker */ debug(F101,"screeng cinit","",cinit); debug(F101,"screeng cendw","",cendw); if (!s) s = ""; /* Always do this. */ ftp = (what & W_FTP) ? 1 : 0; /* FTP or Kermit */ net = network || ftp; xnet = ftp ? 1 : nettype; /* NET_TCPB == 1 */ if (cinit == 0 || cendw > 0) { /* Handle borderline cases... */ if (f == SCR_CW) { /* Close window, but it's not open */ ft_win = 0; return; } debug(F111,"screeng A",s,f); if (f == SCR_EM || (f == SCR_PT && c == 'E')) { /* Fatal error before window open */ conoll(""); conoc('?'); conoll(s); return; /* Regular display */ } } if (cinit == 0) { /* Only call initscr() once */ /* Check these now -- if they are defined but not numeric */ /* they can crash curses */ cendw = 1; /* New window needs repainting */ debug(F100,"screeng calling initscr","",0); initscr(); /* Initialize curses. */ debug(F100,"screeng initscr ok","",0); cinit++; /* Remember curses was initialized. */ } ft_win = 1; /* Window is open */ if (repaint) { #ifdef CK_WREFRESH /* This totally repaints the screen, just what we want, but we can only do this with real curses, and then only if clearok() and wrefresh() are provided in the curses library. */ RestoreCmdMode(); #else /* No CK_WREFRESH */ /* Kermit's do-it-yourself method, works with all types of fullscreen support, but does not repaint all the fields. For example, the filename is lost, because it arrives at a certain time and never comes again, and Kermit presently does not save it anywhere. Making this method work for all fields would be a rather major recoding task, duplicating what curses already does, and would add a lot of complexity and storage space. */ cendw = 1; #endif /* CK_WREFRESH */ repaint = 0; } if (cendw) { /* endwin() was called previously */ debug(F100,"screeng setup ok","",0); KuiSetProperty( KUI_FILE_TRANSFER, (long) CW_DIR, (long) zgtdir() ); KuiSetProperty( KUI_FILE_TRANSFER, (long) CW_LIN, (long) ( #ifdef NEWFTP ftp ? (ftp_host ? ftp_host : "(unknown)") : #endif /* NEWFTP */ ttname) ); if (net) { #ifdef NETCONN int secure = 0; char * xname; if (xnet > nnetname) xname = "[ERROR]"; else xname = netname[xnet]; #ifdef NEWFTP if (ftp) { if (ftpissecure()) secure = 1; } else #endif /* NEWFTP */ if (0 #ifdef SSHBUILTIN || IS_SSH() #endif /* SSHBUILTIN */ #ifdef CK_ENCRYPTION || ck_tn_encrypting() && ck_tn_decrypting() #endif /* CK_ENCRYPTION */ #ifdef CK_SSL || tls_active_flag || ssl_active_flag #endif /* CK_SSL */ #ifdef RLOGCODE #ifdef CK_KERBEROS #ifdef CK_ENCRYPTION || ttnproto == NP_EK4LOGIN || ttnproto == NP_EK5LOGIN #endif /* CK_ENCRYPTION */ #endif /* CK_KERBEROS */ #endif /* RLOGCODE */ ) { secure = 1; } if (secure) { char buf[30]; sprintf(buf,"%s (SECURE)",xname); KuiSetProperty(KUI_FILE_TRANSFER, (long) CW_SPD, (long) buf ); } else { KuiSetProperty(KUI_FILE_TRANSFER, (long) CW_SPD, (long) xname ); } #else KuiSetProperty(KUI_FILE_TRANSFER, (long) CW_SPD, (long) "(network)" ); #endif /* NETCONN */ } else { if (speed < 0L) speed = ttgspd(); if (speed > 0L) { if (speed == 8880) { KuiSetProperty(KUI_FILE_TRANSFER, (long) CW_SPD, (long) "75/1200" ); } else { char speedbuf[64] ; sprintf(speedbuf, "%ld", speed); KuiSetProperty(KUI_FILE_TRANSFER, (long) CW_SPD, (long) speedbuf ); } } else { KuiSetProperty(KUI_FILE_TRANSFER, (long) CW_SPD, (long) "(unknown)" ); } } KuiSetProperty(KUI_FILE_TRANSFER, (long) CW_PAR, (long) parnam((char)parity) ); pctlbl = (what & W_SEND); cendw = 0; } debug(F101,"SCREENC switch","",f); debug(F000,"SCREENC c","",c); debug(F101,"SCREENC n","",n); len = strlen(s); /* Length of argument string */ switch (f) { /* Handle our function code */ case SCR_FN: /* Filename */ oldpct = pct = 0L; /* Reset percents */ #ifdef GFTIMER gtv = (CKFLOAT) -1.0; /* oldgtv = (CKFLOAT) -1.0; */ #else gtv = -1L; /* oldgtv = -1L; */ #endif /* GFTIMER */ oldwin = -1; fsiz = (CK_OFF_T)-1L; /* Invalidate previous file size */ KuiSetProperty( KUI_FILE_TRANSFER, (long) CW_PCD, (long) 0 ); KuiSetProperty( KUI_FILE_TRANSFER, (long) CW_FFC, (long) 0 ); KuiSetProperty( KUI_FILE_TRANSFER, (long) CW_SIZ, (long) 0 ); KuiSetProperty( KUI_FILE_TRANSFER, (long) CW_ERR, (long) "" ); KuiSetProperty( KUI_FILE_TRANSFER, (long) CW_NAM, (long) s ); q = len; /* Remember name length for later */ scrft(); /* Display file type (can change) */ #ifdef OS2 SaveCmdMode(0, 0); #endif /* OS2 */ return; case SCR_AN: /* File as-name */ KuiSetProperty( KUI_FILE_TRANSFER, (long) CW_NAM, (long) s ); #ifdef OS2 SaveCmdMode(0, 0); #endif /* OS2 */ return; case SCR_FS: /* File size */ fsiz = n; if (fsiz > (CK_OFF_T)-1) { KuiSetProperty( KUI_FILE_TRANSFER, (long) CW_SIZ, (long) n ); } if (fsiz > (CK_OFF_T)-1) { /* Put up percent label */ pctlbl = 1; } else { pctlbl = 0; } scrft(); /* File type */ #ifdef OS2 SaveCmdMode(0, 0); #endif /* OS2 */ return; case SCR_PT: /* Packet type or pseudotype */ if (spackets < 5) { extern int sysindex; extern struct sysdata sysidlist[]; /* Things that won't change after the 4th packet */ KuiSetProperty( KUI_FILE_TRANSFER, (long) CW_PAR, (long) parnam((char)parity) ); if ( #ifdef NEWFTP (ftp && (spackets == 1 || rpackets == 1)) || #endif /* NEWFTP */ spackets == 4 ) { if ( #ifdef NEWFTP ftp || #endif /* NEWFTP */ ((protocol == PROTO_K) && (sysindex > -1)) ) { char msgbuf[128]; if (net) { sprintf(msgbuf,"Network Host: %s (%s)", #ifdef NEWFTP ftp ? (ftp_host ? ftp_host : "") : #endif /* NEWFTP */ ttname, #ifdef NEWFTP ftp ? ftp_srvtyp : #endif /* NEWFTP */ sysidlist[sysindex].sid_name ); } else { sprintf(msgbuf, "Communication Device: %s (remote host is %s)", ttname, sysidlist[sysindex].sid_name ); } KuiSetProperty( KUI_FILE_TRANSFER, (long) CW_LIN, (long) msgbuf ); } } } #ifdef CK_TIMERS if (/* rttflg && */ protocol == PROTO_K) { long xx; if ( #ifdef STREAMING streaming && oldwin != -2 #else 0 #endif /* STREAMING */ ) { char msgbuf[64]; sprintf(msgbuf,"00 / 00"); KuiSetProperty( KUI_FILE_TRANSFER, (long) CW_TMO, (long) msgbuf ); } else { xx = (rttdelay + 500) / 1000; if (xx != oldrtt || rcvtimo != oldtim) { char msgbuf[64]; sprintf(msgbuf,"%02ld / %02d", xx, rcvtimo); KuiSetProperty( KUI_FILE_TRANSFER, (long) CW_TMO, (long) msgbuf ); oldrtt = xx; oldtim = rcvtimo; clrtoeol(); } } } #endif /* CK_TIMERS */ x = (what & W_RECV) ? /* Packet length */ rpktl+(protocol==PROTO_K?1:0) : spktl; if (x != oldlen) { /* But only if it changed. */ KuiSetProperty( KUI_FILE_TRANSFER, (long) CW_PL, (long) x ); oldlen = x; } KuiSetProperty( KUI_FILE_TRANSFER, (long) CW_PC, (long) spackets ); if (protocol == PROTO_K && !ftp) { /* Window slots */ char ws[16]; int flag; flag = 0; #ifdef STREAMING if (streaming) { if (oldwin != -2) { sprintf(ws,"STREAMING"); flag = 1; oldwin = -2; } } else #endif /* STREAMING */ if (wcur != oldwin) { sprintf(ws, "%d of %d", wcur < 1 ? 1 : wcur, wslotn); flag = 1; oldwin = wcur; } if (flag) { KuiSetProperty( KUI_FILE_TRANSFER, (long) CW_WS, (long) ws ); } } errors = retrans + crunched + timeouts; if (errors != oldtry) { /* Retry count, if changed */ KuiSetProperty( KUI_FILE_TRANSFER, (long) CW_PR, (long) errors ); oldtry = errors; } /* Sender's packet type */ if (!ftp && (c != oldtyp && c != 'Y' && c != 'N')) { char type[2]; sprintf(type, "%c",c); KuiSetProperty( KUI_FILE_TRANSFER, (long) CW_PT, (long) type ); oldtyp = c; } switch (c) { /* Now handle specific packet types */ case 'S': /* Beginning of transfer */ fcnt = fbyt = 0L; /* Clear counters */ #ifdef GFTIMER gtv = -1.0; #else /* GFTIMER */ gtv = -1L; /* And old/new things... */ #endif /* GFTIMER */ oldpct = pct = 0L; break; case 'Z': /* or EOF */ debug(F101,"screeng SCR_PT Z pktnum","",n); debug(F101,"screeng SCR_PT Z oldpct","",oldpct); debug(F101,"screeng SCR_PT Z pct","",pct); case 'D': /* Data packet */ if (fsiz > 0L) { /* Show percent done if known */ oldpct = pct; /* Remember previous percent */ howfar = ffc; #ifdef CK_RESEND if (what & W_SEND) /* Account for PSEND or RESEND */ howfar += sendstart; else if (what & W_RECV) howfar += rs_len; #endif /* CK_RESEND */ /* Percent done, to be displayed... */ if (c == 'Z') { if (!discard && !cxseen && !czseen) pct = 100L; } else pct = (fsiz > 99L) ? (howfar / (fsiz / 100L)) : 0L; if (pct > 100L || /* Allow for expansion and */ (oldpct == 99L && pct < 0L)) /* other boundary conditions */ pct = 100L; if (pct != oldpct) /* Only do this 100 times per file */ updpct(oldpct, pct); } else { KuiSetProperty(KUI_FILE_TRANSFER, (long) CW_FFC, (long) ffc); } KuiSetProperty(KUI_FILE_TRANSFER, (long) CW_FFC, (long) howfar); cps = shocps((int) pct, fsiz, howfar); /* old_tr = shoetl(old_tr, cps, fsiz, howfar); */ break; case '%': /* Timeouts, retransmissions */ cps = shocps((int) pct, fsiz, howfar); /* old_tr = shoetl(old_tr, cps, fsiz, howfar); */ errors = retrans + crunched + timeouts; if (errors != oldtry) { /* Error count, if changed */ KuiSetProperty(KUI_FILE_TRANSFER, (long) CW_PR, (long) errors ); } oldtry = errors; if (s) if (*s) { KuiSetProperty(KUI_FILE_TRANSFER, (long) CW_ERR, (long) s); } break; case 'E': /* Error packet */ if (*s) { KuiSetProperty( KUI_FILE_TRANSFER, (long) CW_ERR, (long) s ); } fcnt = fbyt = 0L; /* So no bytes for this file */ break; case 'Q': /* Crunched packet */ cps = shocps((int) pct, fsiz, howfar); /* old_tr = shoetl(old_tr, cps, fsiz, howfar); */ KuiSetProperty(KUI_FILE_TRANSFER, (long) CW_ERR, (long) "Damaged Packet" ); break; case 'q': /* Ctrl-C or connection lost */ if (!s) s = ""; if (!*s) s = "User interruption or connection lost"; KuiSetProperty(KUI_FILE_TRANSFER, (long) CW_MSG, (long) s ); break; case 'T': /* Timeout */ cps = shocps((int) pct, fsiz, howfar); /* old_tr = shoetl(old_tr, cps, fsiz, howfar); */ KuiSetProperty(KUI_FILE_TRANSFER, (long) CW_ERR, (long) "Timeout" ); errors = retrans + crunched + timeouts; if (errors != oldtry) { /* Error count, if changed */ KuiSetProperty(KUI_FILE_TRANSFER, (long) CW_PR, (long) errors ); oldtry = errors; } break; default: /* Others, do nothing */ break; } #ifdef OS2 SaveCmdMode(0, 0); #endif /* OS2 */ return; case SCR_ST: /* File transfer status */ debug(F101,"screeng SCR_ST c","",c); debug(F101,"screeng SCR_ST success","",success); debug(F101,"screeng SCR_ST cxseen","",cxseen); #ifdef COMMENT if (c == ST_OK) { /* OK, print 100 % */ if (pctlbl) updpct(oldpct,100); else KuiSetProperty(KUI_FILE_TRANSFER, (long) CW_FFC, (long) ffc); pct = 100; oldpct = 0; } else if (fsiz > 0L) /* Not OK, update final percent */ /* The else part writes all over the screen -- howfar and/or fsiz have been reset as a consequence of the not-OKness of the transfer. */ if (pctlbl) updpct(oldpct, (howfar * 100L) / fsiz); #else if (c == ST_OK) { /* OK, print 100 % */ if (pctlbl) { updpct(oldpct,100); } else KuiSetProperty(KUI_FILE_TRANSFER, (long) CW_FFC, (long) ffc); #ifdef COMMENT pct = 100; oldpct = 0; #endif /* COMMENT */ } #endif /* COMMENT */ KuiSetProperty( KUI_FILE_TRANSFER, (long) CW_TR, (long) "" ); switch (c) { /* Print new status message */ case ST_OK: /* Transfer OK */ fcnt++; /* Count this file */ if (what == (W_FTP|W_FT_DELE)) { KuiSetProperty(KUI_FILE_TRANSFER, (long) CW_MSG, (long) "Delete OK" ); } else { fbyt += ffc; KuiSetProperty(KUI_FILE_TRANSFER, (long) CW_MSG, (long) "Transfer OK" ); } return; case ST_DISC: /* Discarded */ KuiSetProperty(KUI_FILE_TRANSFER, (long) CW_ERR, (long) "File discarded" ); #ifdef COMMENT pct = oldpct = 0; #endif /* COMMENT */ return; case ST_INT: /* Interrupted */ KuiSetProperty(KUI_FILE_TRANSFER, (long) CW_ERR, (long) "Transfer interrupted" ); #ifdef COMMENT pct = oldpct = 0; #endif /* COMMENT */ return; case ST_SKIP: { /* Skipped */ char errbuf[64] ; if (n > 0 && n < nskreason) sprintf( errbuf, "File skipped, (%s)", skreason[n] ) ; else sprintf( errbuf, "File skipped" ) ; KuiSetProperty(KUI_FILE_TRANSFER, (long) CW_ERR, (long) errbuf ); #ifdef COMMENT pct = oldpct = 0; #endif /* COMMENT */ return; } case ST_ERR: /* Error message */ if (!s) s = (char *)epktmsg; KuiSetProperty( KUI_FILE_TRANSFER, (long) CW_ERR, (long) s ); #ifdef COMMENT pct = oldpct = 0; #endif /* COMMENT */ return; case ST_REFU: /* Refused */ if (*s) { char errbuf[64] ; sprintf( errbuf, "Refused, %s", s ) ; KuiSetProperty(KUI_FILE_TRANSFER,(long) CW_ERR,(long) errbuf); } else { KuiSetProperty(KUI_FILE_TRANSFER,(long)CW_ERR,(long)"Refused"); } #ifdef COMMENT pct = oldpct = 0; #endif /* COMMENT */ return; case ST_INC: KuiSetProperty(KUI_FILE_TRANSFER,(long)CW_ERR,(long)"Incomplete"); #ifdef COMMENT pct = oldpct = 0; #endif /* COMMENT */ return; case ST_MSG: KuiSetProperty(KUI_FILE_TRANSFER,(long)CW_MSG,(long)s); return; default: /* Bad call */ KuiSetProperty( KUI_FILE_TRANSFER, (long) CW_ERR, (long) "*** screen() called with bad status ***" ); return; } case SCR_TC: { /* Transaction complete */ char msgbuf[128]; KuiSetProperty( KUI_FILE_TRANSFER, (long) CW_CP, tfcps); if (success) { sprintf(msgbuf, "SUCCESS. Files: %s, Bytes: %ld, %ld CPS", filcnt - filrej, ckfstoa(fbyt), tfcps ); KuiSetProperty(KUI_FILE_TRANSFER, (long) CW_MSG, (long) msgbuf ); } KuiSetProperty(KUI_FILE_TRANSFER, (long) CW_TR, (long) hhmmss((long) #ifdef GFTIMER (fptsecs + 0.5) #else tsecs #endif /* GFTIMER */ )); #ifdef GFTIMER oldgtv = (CKFLOAT) -1.0; #else oldgtv = -1L; #endif /* GFTIMER */ #ifdef COMMENT pct = 100; oldpct = 0; /* Reset these for next time. */ #endif /* COMMENT */ oldtyp = 0; oldrtt = -1L; oldtry = -1; oldlen = -1; oldtim = -1; cendw = 1; if (xfrbel) bleep(BP_NOTE); /* Close window, then beep. */ ft_win = 0; /* Window closed. */ return; } case SCR_EM: /* Error packet (fatal) */ KuiSetProperty( KUI_FILE_TRANSFER, (long) CW_ERR, (long) s ); if (xfrbel) bleep(BP_FAIL); #ifdef COMMENT pct = oldpct = 0; #endif /* COMMENT */ return; case SCR_QE: /* Quantity equals */ case SCR_TU: /* Undelimited text */ case SCR_TN: /* Text delimited at start */ case SCR_TZ: /* Text delimited at end */ return; /* (ignored in fullscreen display) */ case SCR_XD: /* X-packet data */ pct = oldpct = 0; KuiSetProperty( KUI_FILE_TRANSFER, (long) CW_NAM, (long) s ); return; case SCR_CW: /* Close Window */ #ifdef COMMENT pct = 100; oldpct = 0; /* Reset these for next time. */ #endif /* COMMENT */ oldtyp = 0; oldrtt = -1L; oldtry = -1; oldlen = -1; oldtim = -1; ft_win = 0; /* Flag that window is closed. */ cendw = 1; return; case SCR_CD: /* Display current directory */ KuiSetProperty( KUI_FILE_TRANSFER, (long) CW_DIR, (long) s ); #ifdef OS2 SaveCmdMode(0, 0); #endif /* OS2 */ return; default: /* Bad call */ KuiSetProperty(KUI_FILE_TRANSFER, (long) CW_ERR, (long) "*** screen() called with bad function code ***" ); return; } } #endif /* KUI */ #endif /* MAC */ #endif /* NOXFER */ #ifndef CK_CURPOS /* Dummies for when cursor control is not supported */ int ck_curpos(row, col) { return(-1); } int ck_cls() { return(-1); } int ck_cleol() { return(-1); } #endif /* CK_CURPOS */ #ifndef NOIKSD #ifdef IKSDB struct iksdbfld dbfld[] = { /* Offset Length Type */ { DB_FLAGS, dB_FLAGS, DBT_HEX }, /* 0 db_FLAGS Flags */ { DB_ATYPE, dB_ATYPE, DBT_HEX }, /* 1 db_ATYPE Auth type */ { DB_AMODE, dB_AMODE, DBT_HEX }, /* 3 db_AMODE Auth mode */ { DB_STATE, dB_STATE, DBT_HEX }, /* 2 db_STATE State */ { DB_MYPID, dB_MYPID, DBT_HEX }, /* 5 db_MYPID PID */ { DB_SADDR, dB_SADDR, DBT_HEX }, /* 4 db_SADDR Server address */ { DB_CADDR, dB_CADDR, DBT_HEX }, /* 6 db_CADDR Client address */ { DB_START, dB_START, DBT_DAT }, /* 7 db_START Session start */ { DB_LASTU, dB_LASTU, DBT_DAT }, /* 8 db_LASTU Last update */ { DB_ULEN, dB_ULEN, DBT_HEX }, /* 9 db_ULEN Username length */ { DB_DLEN, dB_DLEN, DBT_HEX }, /* 10 db_DLEN Directory name length */ { DB_ILEN, dB_ILEN, DBT_HEX }, /* 11 db_ILEN Info length */ { DB_PAD1, dB_PAD1, DBT_UND }, /* 12 db_PAD1 (Reserved) */ { DB_USER, dB_USER, DBT_STR }, /* 13 db_USER Username */ { DB_DIR, dB_DIR, DBT_STR }, /* 14 db_DIR Current Directory */ { DB_INFO, dB_INFO, DBT_STR } /* 15 db_INFO State-specific info */ }; static char lcknam[CKMAXPATH+1]; /* Lockfile pathname */ static char tmplck[CKMAXPATH+1]; /* Temporary lockfile name */ static char * updmode = /* Update mode for fopen() */ #ifdef OS2 "r+b" #else #ifdef VMS "r+b" #else "r+" #endif /* VMS */ #endif /* OS2 */ ; /* D B I N I T -- Initialize the IKSD database... */ int dbinit() { extern int dbinited; int x = 0; debug(F110,"dbinit dbdir 1",dbdir,0); debug(F110,"dbinit dbfile 1",dbfile,0); if (dbinited) return(0); #ifdef OS2 if (!dbdir) { #ifdef NT char * p = NULL; if (!isWin95()) { p = getenv("SystemRoot"); } else { p = getenv("winbootdir"); if (!p) p = getenv("windir"); } if (!p) p = "C:/"; dbdir = malloc(strlen(p)+2); strcpy(dbdir,p); /* safe */ p = dbdir; while (*p) { if (*p == '\\') *p = '/'; p++; } if (*(p-1) != '/' ) { *p++ = '/'; *p = '\0'; } #else /* NT */ makestr(&dbdir,"C:/"); #endif /* NT */ } #else /* OS2 */ if (!dbdir) makestr(&dbdir,IK_DBASEDIR); #endif /* OS2 */ if (!dbfile) { char * s = ""; x = strlen(dbdir); if (dbdir[x-1] != '/') { s = "/"; x++; } x += (int)strlen(IK_DBASEFIL); dbfile = (char *)malloc(x+1); sprintf(dbfile,"%s%s%s",dbdir,s,IK_DBASEFIL); } debug(F110,"dbinit dbdir 2",dbdir,0); debug(F110,"dbinit dbfile 2",dbfile,0); mypid = getpid(); /* Get my pid */ debug(F101,"dbinit mypid","",mypid); if (!myhexip[0]) { /* Set my hex IP address */ #ifdef TCPSOCKET extern unsigned long myxipaddr; if (getlocalipaddr() > -1) { myip = myxipaddr; sprintf(myhexip,"%08lx",myip); /* (Needs fixing for IPv6) */ } else #endif /* TCPSOCKET */ ckstrncpy(myhexip,"00000000",9); } debug(F111,"dbinit myip",myhexip,myip); if (!peerhexip[0]) { /* Get peer's hex IP address */ #ifdef TCPSOCKET extern unsigned long peerxipaddr; if (ckgetpeer()) { peerip = peerxipaddr; sprintf(peerhexip,"%08lx",peerip); /* (Needs fixing for IPv6) */ debug(F111,"dbinit peerip",peerhexip,peerip); } else { debug(F101,"dbinit ckgetpeer failure","",errno); ckstrncpy(peerhexip,"00000000",9); } #else ckstrncpy(peerhexip,"00000000",9); #endif /* TCPSOCKET */ } debug(F111,"dbinit peerip",peerhexip,peerip); debug(F101,"dbinit dbenabled","",dbenabled); if (dbenabled && inserver) { mydbslot = getslot(); debug(F111,"dbinit getslot",ckitoa(ikdbopen),x); if (ikdbopen) dbinited = 1; } return(0); } /* U P D S L O T -- Update slot n */ /* Opens the database if necessary, seeks to slot n, writes current record and adds current time to last-update field. n is the record sequence number (0, 1, 2, ...), not the seek pointer. Returns -1 on failure, 0 on success. */ int updslot(n) int n; { /* Update our slot */ int rc = 0; CK_OFF_T position; debug(F111,"updslot","ikdbopen",ikdbopen); if (!ikdbopen) /* Not if not ok */ return(0); if (!dbfp) { /* Open database if not open */ dbfp = fopen(dbfile,updmode); /* In update no-truncate mode */ if (!dbfp) { debug(F110,"updslot fopen failed",dbfile,0); ikdbopen = 0; return(-1); } } /* debug(F111,"updslot dbfile",dbfile,dbfp); */ position = n * DB_RECL; if (CKFSEEK(dbfp,position,0) < 0) { /* Seek to desired slot */ debug(F111,"updslot fseek failed",dbfile,mydbseek); ikdbopen = 0; rc = -1; } else { /* Update the update time */ strncpy(&dbrec[dbfld[db_LASTU].off], ckdate(), dbfld[db_LASTU].len ); if (fwrite(dbrec,1,DB_RECL,dbfp) < DB_RECL) { /* Write the record */ debug(F110,"updslot fwrite failed",dbfile,0); ikdbopen = 0; rc = -1; } else { /* Flush the write */ fflush(dbfp); } } return(rc); } /* I N I T S L O T -- Initialize slot n with my info */ int initslot(n) int n; { /* Initialize slot */ int k; #ifdef TCPSOCKET extern unsigned long peerxipaddr; #endif /* TCPSOCKET */ debug(F101,"initslot","",n); #ifdef USE_MEMCPY memset(dbrec,32,DB_RECL); #else for (k = 0; k < DB_RECL; k++) dbrec[k] = '\040'; #endif /* USE_MEMCPY */ myflags = DBF_INUSE; /* Set in-use flag */ mystate = W_NOTHING; myatype = 0L; myamode = 0L; k = dbfld[db_FLAGS].len; /* Length of flags field */ strncpy(&dbrec[dbfld[db_FLAGS].off],ulongtohex(myflags,k),k); k = dbfld[db_ATYPE].len; strncpy(&dbrec[dbfld[db_ATYPE].off],ulongtohex(myatype,k),k); k = dbfld[db_AMODE].len; strncpy(&dbrec[dbfld[db_AMODE].off],ulongtohex(myamode,k),k); k = dbfld[db_STATE].len; strncpy(&dbrec[dbfld[db_STATE].off],ulongtohex(mystate,k),k); k = dbfld[db_SADDR].len; strncpy(&dbrec[dbfld[db_SADDR].off],ulongtohex(myip,k),k); #ifdef TCPSOCKET ckgetpeer(); k = dbfld[db_CADDR].len; strncpy(&dbrec[dbfld[db_CADDR].off],ulongtohex(peerxipaddr,k),k); #else k = dbfld[db_CADDR].len; strncpy(&dbrec[dbfld[db_CADDR].off],ulongtohex(0L,k),k); #endif /* TCPSOCKET */ k = dbfld[db_MYPID].len; strncpy(&dbrec[dbfld[db_MYPID].off],ulongtohex(mypid,k),k); k = dbfld[db_START].len; strncpy(&dbrec[dbfld[db_START].off],ckdate(),k); k = dbfld[db_ULEN].len; strncpy(&dbrec[dbfld[db_ULEN].off],"0000",4); k = dbfld[db_DLEN].len; strncpy(&dbrec[dbfld[db_DLEN].off],"0000",4); k = dbfld[db_ILEN].len; strncpy(&dbrec[dbfld[db_ILEN].off],"0000",4); strncpy(&dbrec[dbfld[db_INFO].off],"INIT",4); return(updslot(n)); } int slotstate(x,s1,s2,s3) int x; char *s1, *s2, *s3; { int k, l1, l2, l3, z; mystate = x; debug(F101,"slotstate ikdbopen","",ikdbopen); if (!ikdbopen) return(-1); if (!s1) s1 = ""; l1 = strlen(s1); if (!s2) s2 = ""; l2 = strlen(s2); if (!s3) s3 = ""; l3 = strlen(s3); strncpy(&dbrec[DB_STATE],ulongtohex(mystate,4),4); k = dbfld[db_ILEN].len; z = l1 + l2 + l3 + 2; if (z > dB_INFO) z = dB_INFO; strncpy(&dbrec[DB_ILEN],ulongtohex((unsigned long)z,k),k); k = dbfld[db_INFO].len; z = dbfld[db_INFO].off; if (l1 <= k) { lset(&dbrec[z],s1,l1+1,32); z += l1+1; k -= l1+1; if (l2 <= k) { lset(&dbrec[z],s2,l2+1,32); z += l2+1; k -= l2+1; if (l3 <= k) lset(&dbrec[z],s3,k,32); } } #ifdef DEBUG if (deblog) { char buf[128]; int i; strncpy(buf,&dbrec[DB_INFO],127); buf[127] = NUL; for (i = 126; i > 0 && buf[i] == 32; i--) buf[i] = 0; debug(F111,"slotstate",buf,mystate); } #endif /* DEBUG */ z = updslot(mydbslot); debug(F101,"slotstate updslot","",z); return(z); } int slotdir(s1,s2) char * s1, * s2; { /* Update current directory */ int k, len1, len2; if (!ikdbopen) return(-1); if (!s1) s1 = ""; if (!s2) s2 = ""; len1 = strlen(s1); len2 = strlen(s2); k = dbfld[db_DLEN].len; strncpy(&dbrec[DB_DLEN],ulongtohex((unsigned long)(len1+len2),k),k); k = dbfld[db_DIR].len; if (len1 > 0) { lset(&dbrec[dbfld[db_DIR].off],s1,len1,32); lset(&dbrec[dbfld[db_DIR].off+len1],s2,k-len1,32); } else { lset(&dbrec[dbfld[db_DIR].off],s2,k,32); } return(updslot(mydbslot)); } /* F R E E S L O T -- Free slot n */ int freeslot(n) int n; { int k; if (!ikdbopen) return(0); dbflags = 0L; if (n == mydbslot) { dbflags = myflags & ~DBF_INUSE; dbflags &= ~DBF_LOGGED; } k = dbfld[db_FLAGS].len; strncpy(&dbrec[dbfld[db_FLAGS].off],ulongtohex(dbflags,k),k); return(updslot(n)); } /* G E T S L O T -- Find a free database slot; returns slot number */ #ifdef UNIX #include /* For creat() */ #endif /* UNIX */ int getslot() { /* Find a free slot for us */ FILE * rfp = NULL; /* Returns slot number (0, 1, ...) */ char idstring[64]; /* PID string buffer (decimal) */ char pidbuf[64], * s; int j, k, n, x, rc = -1; int lockfd, tries, haveslot = 0; long lockpid; CK_OFF_T i; /* char ipbuf[17]; */ if (!myhexip[0]) /* Set my hex IP address if not set */ ckstrncpy((char *)myhexip,"7F000001",33); sprintf(idstring,"%08lx:%010ld\n",myip,mypid); debug(F110,"getslot idstring", idstring, 0); /* Make temporary lockfile name IP.PID (hex.hex) */ /* This should fit in 14 chars -- huge PIDs are usually not possible */ /* on 14-char filename systems. */ sprintf(tmplck,"%s%08lx.%lx",dbdir,myip,mypid); debug(F110,"getslot tempfile",tmplck,0); /* Make a temporary file */ lockfd = creat(tmplck, 0600); /* BUT THIS ISN'T PORTABLE */ if (lockfd < 0) { debug(F111,"getslock temp lockfile create failure", tmplck, errno); return(-1); } /* Write my (decimal) PID into the temp file */ write(lockfd,idstring,(int)strlen(idstring)); if (close(lockfd) < 0) { /* Close lockfile */ debug(F101,"getslot error closing temp lockfile", "", errno); return(-1); } sprintf(lcknam,"%s%s",dbdir,IK_LOCKFILE); /* Build lockfile name */ debug(F110,"getslot lockfile",lcknam,0); rfp = fopen(lcknam,"r"); /* See if lockfile exists */ if (rfp) { /* If so... */ rset(pidbuf,"",64,0); x = fread(pidbuf,1,63,rfp); /* Read ID string from it */ fclose(rfp); /* and close it quickly */ debug(F110,"getslot lock exists",pidbuf,0); if (x > 0) { /* If we have a PID, check it */ char * s = pidbuf; while (*s) { if (islower(*s)) *s = toupper(*s); if (*s == ':') { *s = NUL; debug(F110,"getslot lock IP",pidbuf,0); debug(F110,"gteslot my IP",myhexip,0); if (!strcmp(pidbuf,myhexip)) { /* Same IP address? */ lockpid = atol(s+1); /* Yes, now get PID */ debug(F101,"getslot lockpid","",lockpid); /* Check if PID lockpid on this computer is alive */ x = zchkpid(lockpid); if (!x) { debug(F100,"getslot PID stale,removing lock","",0); unlink(lcknam); } break; } } s++; } } else { debug(F111,"getslot lockfile open failure",lcknam,errno); } } /* Try IK_LCKTRIES (16) times to rename temp file to lockfile */ for (tries = IK_LCKTRIES; tries > 0; tries--) { if (zrename(tmplck,lcknam) == 0) break; debug(F101,"getslot database locked by pid", "", dbpid); sleep(IK_LCKSLEEP); } if (tries < 1) { /* Couldn't */ debug(F110,"getslot create lock failure",lcknam,0); return(-1); } /* Have lock, open database */ debug(F110,"getslot has lock",lcknam,0); /* Have lock */ if (!dbfile) return(-1); /* If database doesn't exist, create it. */ debug(F110,"getslot dbfile",dbfile,0); if (zchki(dbfile) < 0) { debug(F110,"getslot creating new database",dbfile,0); x = creat(dbfile,0660); if (x < 0) { debug(F111,"getslot creat() failed", dbfile, errno); goto xslot; } close(x); } dbfp = fopen(dbfile,updmode); /* Open it in update mode */ if (!dbfp) { debug(F111,"getslot fopen failed",dbfile,errno); goto xslot; } /* Now find a free (or new) slot... */ dblastused = 0L; /* Seek pointer to last record inuse */ mydbseek = 0L; /* Seek pointer for my record */ /* Quickly read the whole database; n = record counter, i = seek pointer */ for (n = 0, i = 0; !feof(dbfp); i += DB_RECL, n++) { x = fread(dbrec,1,DB_RECL,dbfp); /* Read a record */ if (x < 1) /* EOF not caught by feof() */ break; #ifndef NOFTRUNCATE if (x != DB_RECL) { /* Watch out for trailing junk */ debug(F101,"getslot bad size","",x); /* (Shouldn't happen...) */ #ifdef COHERENT chsize(fileno(dbfp),i); #else ftruncate(fileno(dbfp),(CK_OFF_T)i); #endif /* COHERENT */ x = 0; CKFSEEK(dbfp,i,0); break; } #endif /* NOFTRUNCATE */ debug(F101,"getslot record","",n); k = dbfld[db_FLAGS].off; j = dbfld[db_FLAGS].len; dbflags = hextoulong(&dbrec[k],j); debug(F001,"getslot dbflags","",dbflags); k = dbfld[db_MYPID].off; j = dbfld[db_MYPID].len; dbpid = hextoulong(&dbrec[k],j); debug(F001,"getslot dbpid","",dbpid); k = dbfld[db_SADDR].off; j = dbfld[db_SADDR].len; dbip = hextoulong(&dbrec[k],j); debug(F001,"getslot dbip","",dbip); if (dbflags & DBF_INUSE) { /* Remember last slot in use */ x = 0; /* Make sure it's REALLY in use */ if (dbpid == mypid && dbip == myip) { /* Check for PID == my PID */ x = 1; debug(F101,"getslot record pid","",dbpid); } else { /* Or for stale PID */ x = zchkpid(dbpid); debug(F101,"getslot zchkpid()","",x); } if (!x) { /* Bogus record */ x = freeslot(n); debug(F101,"getslot stale record pid: freeslot()","",x); if (x > -1 && !haveslot) dbflags = 0; } else { /* It's really in use */ dblastused = i; } } if (!haveslot) { /* If I don't have a slot yet */ if (!(dbflags & DBF_INUSE)) { /* Claim this one */ debug(F101,"getslot free slot", "", n); haveslot = 1; mydbseek = i; mydbslot = n; /* But keep going... */ } } } /* Come here with i == seek pointer to first record after eof */ if (!haveslot) { /* Found no free slot so add to end */ debug(F101,"getslot new slot","",n); haveslot = 1; mydbseek = i; mydbslot = n; } ikdbopen = 1; /* OK to make database entries */ debug(F101,"getslot records","",n); debug(F101,"getslot dblastused","",dblastused); debug(F101,"getslot i","",i); /* Trim stale records from end */ #ifndef NOFTRUNCATE if (i > dblastused+DB_RECL) { debug(F101,"getslot truncating at","",dblastused+DB_RECL); #ifdef COHERENT x = chsize(fileno(dbfp),dblastused+DB_RECL); #else x = ftruncate(fileno(dbfp),(CK_OFF_T)(dblastused+DB_RECL)); #endif /* COHERENT */ if (x < 0) /* (Not fatal) */ debug(F101,"getslot ftruncate failed", "", errno); } #endif /* NOFTRUNCATE */ /* Initialize my record */ if (initslot(mydbslot) < 0) { debug(F101,"getslot initslot() error","",n); ikdbopen = 0; goto xslot; } debug(F101,"getslot OK","",mydbslot); rc = mydbslot; /* OK return code */ xslot: /* Unlock the database and return */ if (unlink(lcknam) < 0) { debug(F111,"getslot lockfile removal failed",lcknam,errno); rc = -1; } return(rc); } #endif /* IKSDB */ #endif /* NOIKSD */ ckuusy.c0000644000015300001460000040543311301551412011352 0ustar fdckermit#include "ckcsym.h" #define XFATAL fatal /* C K U U S Y -- "User Interface" for C-Kermit Kermit, part Y */ /* Command-Line Argument Parser */ /* Authors: Frank da Cruz , The Kermit Project, Columbia University, New York City Jeffrey E Altman Secure Endpoints Inc., New York City Copyright (C) 1985, 2009, Trustees of Columbia University in the City of New York. All rights reserved. See the C-Kermit COPYING.TXT file or the copyright text in the ckcmai.c module for disclaimer and permissions. */ #include "ckcdeb.h" char * bannerfile = NULL; char * helpfile = NULL; extern int xferlog, filepeek, nolinks; extern char * xferfile; extern int debtim; #include "ckcasc.h" #include "ckcker.h" #include "ckucmd.h" #include "ckcnet.h" #include "ckuusr.h" #include "ckcxla.h" #ifdef CK_SSL #include "ck_ssl.h" #endif /* CK_SSL */ #include #ifdef OS2 #include #ifdef KUI #include "ikui.h" extern struct _kui_init kui_init; #endif /* KUI */ #endif /* OS2 */ extern int inserver, fncnv, f_save, xfermode; #ifdef PATTERNS extern int patterns; #endif /* PATTERNS */ #ifndef NOICP extern int cmdint; #endif /* NOICP */ extern int xsuspend; #ifdef NETCONN #ifdef ANYX25 extern int revcall, closgr, cudata; extern char udata[]; extern int x25fd; #endif /* ANYX25 */ #ifndef VMS #ifndef OS2 #ifndef OSK extern #endif /* OSK */ #endif /* OS2 */ #endif /* VMS */ int telnetfd; extern struct keytab netcmd[]; extern int tn_exit; #ifndef NOICP #ifndef NODIAL extern int nnets, nnetdir; /* Network services directory */ extern char *netdir[]; extern char *nh_p[]; /* Network directory entry pointers */ extern char *nh_p2[]; /* Network directory entry nettype */ extern char *nh_px[4][MAXDNUMS + 1]; #endif /* NODIAL */ extern int nhcount; extern char * n_name; /* Network name pointer */ #endif /* NOICP */ #endif /* NETCONN */ #ifndef NOSPL extern int nmac; extern struct mtab *mactab; #endif /* NOSPL */ extern char uidbuf[]; #ifdef CK_LOGIN extern int logintimo; #endif /* CK_LOGIN */ extern char * myname, * dftty; extern int howcalled; extern char *ckxsys, *ckzsys, **xargv, *xarg0, **cmlist, *clcmds; extern int action, cflg, xargc, cnflg, local, quiet, escape, network, mdmtyp, bgset, backgrd, xargs, binary, parity, turn, turnch, duplex, flow, clfils, noinit, stayflg, nettype, cfilef, noherald, cmask, cmdmsk, exitonclose, haveline, justone, cxtype, xfinish, ttnproto; extern long speed; extern char ttname[]; extern char * pipedata, * cmdfil; #ifndef NOXFER extern char *cmarg, *cmarg2; extern int nfils, stdouf, stdinf, displa, maxrps, rpsiz, ckwarn, urpsiz, wslotr, swcapr, ckdelay, recursive, reliable, xreliable, fnspath, fncact, clearrq, setreliable; #ifdef PIPESEND extern int usepipes, pipesend; #endif /* PIPESEND */ extern int protocol; #endif /* NOXFER */ #ifndef NOPUSH extern int nopush; #endif /* NOPUSH */ #ifdef OS2 extern struct keytab os2devtab[]; extern int nos2dev; extern int ttslip; extern int tt_scroll, tt_escape; #ifdef OS2PM extern int os2pm; #endif /* OS2PM */ #endif /* OS2 */ #ifdef CK_NETBIOS extern unsigned char NetBiosAdapter; #endif /* CK_NETBIOS */ #ifdef XFATAL #undef XFATAL #endif /* XFATAL */ #ifdef TNCODE _PROTOTYP(static int dotnarg, (char x) ); #endif /* TNCODE */ #ifdef RLOGCODE _PROTOTYP(static int dorlgarg, (char x) ); #endif /* RLOGCODE */ #ifdef SSHBUILTIN _PROTOTYP(static int dossharg, (char x) ); #endif /* SSHBUILTIN */ int haveftpuid = 0; /* Have FTP user ID */ static int have_cx = 0; /* Have connection */ static char * failmsg = NULL; /* Failure message */ #ifdef NEWFTP extern char * ftp_host; #endif /* NEWFTP */ extern int what; #ifndef NOICP #ifndef NODIAL extern int nmdm, telephony; extern struct keytab mdmtab[]; extern int usermdm, dialudt; #endif /* NODIAL */ _PROTOTYP(static int pmsg, (char *) ); _PROTOTYP(static int fmsg, (char *) ); static int pmsg(s) char *s; { printf("%s\n", s); return(0); } static int fmsg(s) char *s; { fatal(s); return(0); } #define XFATAL(s) return(what==W_COMMAND?pmsg(s):fmsg(s)) #else #define XFATAL fatal #endif /* NOICP */ #ifndef NOHTTP #define HTTP_GET 1 #define HTTP_PUT 2 #define HTTP_HED 3 #endif /* NOHTTP */ #ifdef CK_URL /* URLs we recognize */ #define URL_FTP 1 #define URL_HTTP 2 #define URL_HTTPS 3 #define URL_IKSD 4 #define URL_TELNET 5 #define URL_LOGIN 6 struct keytab urltab[] = { #ifdef NEWFTP "ftp", URL_FTP, 0, #endif /* NEWFTP */ #ifndef NOHTTP "http", URL_HTTP, 0, "https", URL_HTTPS, 0, #endif /* NOHTTP */ "iksd", URL_IKSD, 0, "kermit", URL_IKSD, 0, "telnet", URL_TELNET, 0, "", 0, 0 }; int nurltab = sizeof(urltab)/sizeof(struct keytab) - 1; #ifndef URLBUFLEN #define URLBUFLEN 1024 #endif /* URLBUFLEN */ static char urlbuf[URLBUFLEN]; struct urldata g_url = {NULL,NULL,NULL,NULL,NULL,NULL,NULL}; /* u r l p a r s e -- Parse a possible URL */ /* Returns 0 if the candidate does not seem to be a URL. Returns 1 if it might be a URL, with the above pointers set to its pieces: service : [ // ] [ user [ : password ] @ ] host [ : service ] [ / path ] - [ ? name [ = value ]] Example: ftp://ds.internic.net:21/rfc/rfc1234.txt url.svc = [ftp] url.usr = [(NULL)] url.psw = [(NULL)] url.hos = [ds.internic.net] url.por = [21] url.pth = [rfc/rfc1234.txt] It might be a URL if it contains a possible service name followed by a a colon (:). Thus "telnet:xyzcorp.com" is a minimal URL, whereas a full-blown example would be: ftp://olga:secret@ftp.xyzcorp.com/public/oofa.txt The caller must verify the results, i.e. that the service string is a real TCP service, etc. This routine just parses the fields. struct urldata defined in ckcker.h */ int urlparse(s,url) char *s; struct urldata * url; { char * p = NULL, * urlbuf = NULL; int x; if (!s || !url) return(0); if (!*s) return(0); makestr(&urlbuf,s); if (url->sav) { /* In case we were called before... */ free(url->sav); url->sav = NULL; } if (url->svc) { free(url->svc); url->svc = NULL; } if (url->hos) { free(url->hos); url->hos = NULL; } if (url->por) { free(url->por); url->por = NULL; } if (url->usr) { free(url->usr); url->usr = NULL; } if (url->psw) { free(url->psw); url->psw = NULL; } if (url->pth) { free(url->pth); url->pth = NULL; } if (url->nopts) { int i; for (i = 0; i < url->nopts && i < MAX_URL_OPTS; i++ ) { if (url->opt[i].nam) { free(url->opt[i].nam); url->opt[i].nam = NULL; } if (url->opt[i].val) { free(url->opt[i].val); url->opt[i].val = NULL; } } url->nopts = 0; } p = urlbuf; /* Was a service requested? */ while (*p && *p != ':') /* Look for colon */ p++; if (*p == ':') { /* Have a colon */ *p++ = NUL; /* Get service name or number */ if (*p == ':') /* a second colon */ *p++ = NUL; /* get rid of that one too */ while (*p == '/') *p++ = NUL; /* and slashes */ #ifdef COMMENT /* Trailing slash is part of path - leave it - jaltman */ x = strlen(p); /* Length of remainder */ if (p[x-1] == '/') /* If there is a trailing slash */ p[x-1] = NUL; /* remove it. */ #endif /* COMMENT */ if (urlbuf[0]) { /* Anything left? */ char *q = p, *r = p, *w = p; makestr(&url->svc,urlbuf); while (*p != NUL && *p != '@') /* look for @ */ p++; if (*p == '@') { /* Signifies user ID, maybe password */ *p++ = NUL; url->hos = p; while (*w != NUL && *w != ':') w++; if (*w == ':') *w++ = NUL; url->usr = r; /* Username */ if (*w) url->psw = w; /* Password */ q = p; } else { /* No username or password */ p = q; url->hos = p; } #ifdef COMMENT debug(F111,"urlparse url->usr",url->usr,url->usr); debug(F111,"urlparse url->psw",url->usr,url->psw); debug(F111,"urlparse url->hos",url->usr,url->hos); #endif /* COMMENT */ while (*p != NUL && *p != ':' && *p != '/') /* Port? */ p++; if (*p == ':') { /* TCP port */ *p++ = NUL; r = p; url->por = r; while (*p != NUL && *p != '/') p++; /* '/' is part of path, leave it until we can copy */ if (*p == '/') { makestr(&url->pth,p); /* Path */ *p = NUL; } } else { /* No port */ /* '/' is part of path, leave it */ if (*p == '/') { makestr(&url->pth,p); /* Path */ *p = NUL; } } } /* Copy non-NULL result strings */ if (url->svc) if (*url->svc) { p = url->svc; url->svc = NULL; makestr(&url->svc,p); } if (url->hos) if (*url->hos) { p = url->hos; url->hos = NULL; makestr(&url->hos,p); } if (url->por) if (*url->por) { p = url->por; url->por = NULL; makestr(&url->por,p); } /* WARNING (Wed Oct 9 16:09:03 2002): We now allow the username and password to be empty strings. These are treated differently from null pointers: an empty string means the URL included username and/or password fields that were empty, e.g. ftp://:@ftp.xyzcorp.com/somepath/somefile, which causes the client to prompt for the username and/or password. */ if (url->usr) /* if (*url->usr) */ { p = url->usr; url->usr = NULL; makestr(&url->usr,p); } if (url->psw) /* if (*url->psw) */ { p = url->psw; url->psw = NULL; makestr(&url->psw,p); } /* Save a copy of the full url if one was found. */ if (url->svc) makestr(&url->sav,s); free(urlbuf); return(url->svc ? 1 : 0); } return(0); } #endif /* CK_URL */ #ifndef NOCMDL char *hlp1[] = { #ifndef NOICP " [filename] [-x arg [-x arg]...[-yyy]..] [ = text ] ]\n", #else "[-x arg [-x arg]...[-yyy]..]\n", #endif /* NOICP */ "\n", " -x is an option requiring an argument, -y an option with no argument.\n", " If the first command-line argument is the name of a file, interactive-\n", " mode commands are executed from the file. The '=' argument tells Kermit\n", " not to parse the remainder of the command line, but to make the words\n", " following '=' available as \\%1, \\%2, ... \\%9. The command file \ (if any)\n", " is executed before the command-line options.\n", "\n", #ifndef NOICP "If no action command is included, or -S is, then after the command line is\n", "executed, Kermit issues its prompt and waits for you to type commands.\n", #else "Operation by command-line options only.\n", #endif /* NOICP */ "" }; static char *hlp2[] = { " [option-list] host[:port] [port]\n", " The option-list consists of zero, one, or more of:\n", " -8 Negotiate Telnet Binary in both directions\n", " -a Require use of Telnet authentication\n", " -d Turn on debug mode\n", " -E No escape character\n", " -K Refuse use of authentication; do not send username\n", " -l user Set username and request Telnet authentication\n", " -L Negotiate Telnet Binary Output only\n", " -x Require Encryption\n", " -D Disable forward-X\n", " -T cert=file Use certificate in file\n", " -T key=file Use private key in file\n", " -T crlfile=file Use CRL in file\n", " -T crldir=dir Use CRLs in directory\n", " -T cipher=string Use only ciphers in string\n", " -f Forward credentials to host\n", " -k realm Set default Kerberos realm\n", "" }; static char *hlp3[] = { /* rlogin */ " [option-list] host[:port] [port]\n", " The option-list consists of zero, one, or more of:\n", " -d Turn on debug mode\n", " -l user Set username\n", "" }; static char *hlp4[] = { /* ssh */ " [option-list] host[:port] [port]\n", " The option-list consists of zero, one, or more of:\n", #ifdef OS2 " -# flags Kermit 95 Startup Flags\n", " 1 turn off Win95 special fixes\n", " 2 do not load optional network dlls\n", " 4 do not load optional tapi dlls\n", " 8 do not load optional kerberos dlls\n", " 16 do not load optional zmodem dlls\n", " 32 use stdin for input instead of the console\n", " 64 use stdout for output instead of the console\n", " 128 do not terminate process in response to Session Logoff\n", #endif /* OS2 */ " -d Turn on debug mode\n", " -Y Disable init file processing\n", " -l user Set username\n", "" }; /* Command-line option help lines. Update this when adding new options! */ char * opthlp[128]; /* Option help */ char * arghlp[128]; /* Argument for option */ int optact[128]; /* Action-option flag */ VOID fatal2(msg1,msg2) char *msg1, *msg2; { char buf[256]; if (!msg1) msg1 = ""; if (!msg2) msg2 = ""; ckmakmsg(buf,256,"\"",msg1,"\" - ",msg2); #ifndef NOICP if (what == W_COMMAND) printf("%s\n",buf); else #endif /* NOICP */ fatal((char *)buf); } static SIGTYP #ifdef CK_ANSI cl_int(int dummy) #else /* CK_ANSI */ cl_int(dummy) int dummy; #endif /* CK_ANSI */ { /* Command-line interrupt handler */ doexit(BAD_EXIT,1); SIGRETURN; } #ifdef NEWFTP extern int ftp_action, ftp_cmdlin; static int xx_ftp(host, port) char * host, * port; { #ifdef CK_URL extern int haveurl; #endif /* CK_URL */ extern char * ftp_logname; int use_tls = 0; char * p; if (port) if (!*port) port = NULL; if (!host) return(0); if (!*host) return(0); debug(F111,"ftp xx_ftp host",ftp_host,haveftpuid); debug(F111,"ftp xx_ftp uidbuf 1",uidbuf,haveftpuid); ftp_cmdlin = 1; /* 1 = FTP started from command line */ if (nfils > 0) ftp_cmdlin++; /* 2 = same plus file transfer */ #ifndef NOURL /* debug(F111,"ftp xx_ftp g_url.usr",g_url.usr,g_url.usr); */ if (haveurl && g_url.usr) { /* Wed Oct 9 15:15:22 2002 */ if (!*(g_url.usr)) { /* Force username prompt if */ haveftpuid = 0; /* "ftp://:@host" given. */ uidbuf[0] = NUL; makestr(&ftp_logname,NULL); } debug(F111,"ftp xx_ftp uidbuf 2",uidbuf,haveftpuid); } #endif /* NOURL */ debug(F111,"ftp xx_ftp uidbuf 3",uidbuf,haveftpuid); if (haveftpuid) { makestr(&ftp_logname,uidbuf); debug(F111,"ftp_logname",ftp_logname,haveftpuid); } if (!port) { if ((p = ckstrchr(ftp_host,':'))) *p++ = NUL; port = p; } if (!port) { #ifdef CK_URL if (haveurl) { if (g_url.por) port = g_url.por; else if (g_url.svc) port = g_url.svc; else port = "ftp"; } else #endif /* CK_URL */ port = "ftp"; } #ifdef CK_SSL if (haveurl && g_url.svc) use_tls = !ckstrcmp("ftps",g_url.svc,-1,0); #endif /* CK_SSL */ if (ftpopen(ftp_host,port,use_tls) < 1) return(-1); debug(F111,"ftp xx_ftp action",ckctoa((char)ftp_action),nfils); if (nfils > 0) { switch (ftp_action) { case 'g': return(cmdlinget(stayflg)); case 'p': case 's': return(cmdlinput(stayflg)); } } return(1); } #endif /* NEWFTP */ #ifndef NOHTTP static char * http_hlp[] = { " -h This message.\n", " -d Debug to debug.log.\n", " -S Stay (issue command prompt when done).\n", " -Y Do not execute Kermit initialization file.\n", " -q Quiet (suppress most messages).\n", " -u name Username.\n", " -P password Password.\n", " -g pathname Get remote pathname.\n", " -p pathname Put remote pathname.\n", " -H pathname Head remote pathname.\n", " -l pathname Local path for -g, -p, and -H.\n", #ifdef CK_SSL " -z opt[=value] Security options...\n", " cert=file Client certificate file\n", " certsok Accept all certificates\n", " key=file Client private key file\n", " secure Use SSL\n", " verify=n 0 = none, 1 = peer , 2 = certificate required\n", #endif /* CK_SSL */ "" }; #define HT_CERTFI 0 #define HT_OKCERT 1 #define HT_KEY 2 #define HT_SECURE 3 #define HT_VERIFY 4 static struct keytab httpztab[] = { { "cert", HT_CERTFI, CM_ARG }, { "certsok", HT_OKCERT, 0 }, { "key", HT_KEY, CM_ARG }, { "secure", HT_SECURE, 0 }, { "verify", HT_VERIFY, CM_ARG }, { "", 0, 0 } }; static int nhttpztab = sizeof(httpztab) / sizeof(struct keytab) - 1; #endif /* NOHTTP */ /* U S A G E */ VOID usage() { #ifdef MINIX conol("Usage: "); conol(xarg0); conol(" [-x arg [-x arg]...[-yyy]..] ]\n"); #else conol("Usage: "); conol(xarg0); if (howcalled == I_AM_KERMIT || howcalled == I_AM_IKSD || howcalled == I_AM_SSHSUB) conola(hlp1); else if (howcalled == I_AM_TELNET) conola(hlp2); else if (howcalled == I_AM_RLOGIN) conola(hlp3); else if (howcalled == I_AM_SSH) conola(hlp4); if (howcalled == I_AM_KERMIT || howcalled == I_AM_IKSD || howcalled == I_AM_SSHSUB) { int c; conoll(""); conoll("Complete listing of command-line options:"); conoll(""); for (c = 31; c < 128; c++) { if (!opthlp[c]) continue; if (arghlp[c]) { printf(" -%c %s\n", (char)c, (optact[c] ? " (action option)" : "") ); printf(" %s\n",opthlp[c]); printf(" Argument: %s\n\n",arghlp[c]); } else { /* Option without arg */ printf(" -%c %s%s\n", (char)c, opthlp[c], (optact[c]?" (action option)":"") ); printf(" Argument: (none)\n\n"); } } #ifdef OS2ORUNIX printf("To prevent this message from scrolling, use '%s -h | more'.\n", xarg0); #endif /* OS2ORUNIX */ printf("For a list of extended options use '%s --help'.\n", xarg0); } #endif /* MINIX */ } /* C M D L I N -- Get arguments from command line */ int cmdlin() { char x; /* Local general-purpose char */ extern int haveurl; #ifdef NEWFTP char * port = NULL; #endif /* NEWFTP */ #ifndef NOXFER cmarg = ""; /* Initialize globals */ cmarg2 = ""; #endif /* NOXFER */ action = 0; cflg = 0; signal(SIGINT,cl_int); /* Here we handle different "Command Line Personalities" */ #ifdef TCPSOCKET #ifndef NOHTTP if (howcalled == I_AM_HTTP) { /* If I was called as HTTP... */ char rdns[128]; #ifdef OS2 char * agent = "Kermit 95"; #else char * agent = "C-Kermit"; #endif /* OS2 */ debug(F100,"http personality","",0); #ifdef CK_URL if (haveurl) { int type; char * lfile; type = lookup(urltab,g_url.svc,nurltab,NULL); if (!(type == URL_HTTP || type == URL_HTTPS)) { printf("?Internal Error: HTTP command line processing\n"); debug(F100,"Error: HTTP command line processing","",0); doexit(BAD_EXIT,1); } rdns[0] = '\0'; lfile = ""; x = (http_open(g_url.hos,g_url.por ? g_url.por : g_url.svc, type == URL_HTTPS, rdns,128,NULL) == 0); if (x) { #ifdef KUI char asname[CKMAXPATH+1]; #endif /* KUI */ if (!quiet) { if (rdns[0]) printf("Connected to %s [%s]\r\n",g_url.hos,rdns); else printf("Connected to %s\r\n",g_url.hos); } if (g_url.pth) zstrip(g_url.pth,&lfile); else g_url.pth = "/"; if (!*lfile) lfile = "index.html"; #ifdef KUI if (uq_file(NULL, /* K95 GUI: Put up file box. */ NULL, /* (not tested...) */ 4, NULL, lfile, asname, CKMAXPATH+1 ) > 0) lfile = asname; #endif /* KUI */ x = http_get(agent, NULL, /* hdrlist */ g_url.usr, g_url.psw, 0, lfile, g_url.pth, 0 /* stdio */ ); x = (http_close() == 0); } else { if (!quiet) printf("?HTTP Connection failed.\r\n"); } doexit(x ? GOOD_EXIT : BAD_EXIT, -1); } else #endif /* CK_URL */ { int http_action = 0; char * host = NULL, * svc = NULL, * lpath = NULL; char * user = NULL, * pswd = NULL, * path = NULL; char * xp; while (--xargc > 0) { /* Go through command line words */ xargv++; debug(F111,"cmdlin http xargv",*xargv,xargc); xp = *xargv+1; if (**xargv == '-') { /* Got an option */ int xx; x = *(*xargv+1); /* Get the option letter */ switch (x) { case 'd': /* Debug */ #ifdef DEBUG if (deblog) { debtim = 1; } else { deblog = debopn("debug.log",0); } #endif /* DEBUG */ break; case 'S': /* Stay */ case 'Y': /* No initialization file */ break; /* (already done in prescan) */ case 'q': /* Quiet */ quiet = 1; break; case 'u': /* Options that require arguments */ case 'P': case 'g': case 'p': case 'H': case 'l': if (*(xp+1)) { XFATAL("Invalid argument bundling"); } xargv++, xargc--; if ((xargc < 1) || (**xargv == '-')) { XFATAL("Missing argument"); } switch (x) { case 'u': user = *xargv; break; case 'P': pswd = *xargv; break; case 'l': if (http_action != HTTP_PUT) lpath = *xargv; break; case 'g': http_action = HTTP_GET; path = *xargv; debug(F111,"cmdlin http GET",path,http_action); break; case 'p': http_action = HTTP_PUT; path = *xargv; break; case 'H': http_action = HTTP_HED; path = *xargv; } break; #ifdef CK_SSL case 'z': { /* *xargv contains a value of the form tag=value */ /* we need to lookup the tag and save the value */ int x,y,z; char * p, * q; makestr(&p,*xargv); y = ckindex("=",p,0,0,1); if (y > 0) p[y-1] = '\0'; x = lookup(httpztab,p,nhttpztab,&z); if (x < 0) { printf("?Invalid security option: \"%s\"\n",p); } else { printf("Security option: \"%s",p); if (httpztab[z].flgs & CM_ARG) { q = &p[y]; if (!*q) fatal("?Missing required value"); } /* -z options w/args */ switch (httpztab[z].kwval) { case HT_CERTFI: makestr(&ssl_rsa_cert_file,q); break; case HT_OKCERT: ssl_certsok_flag = 1; break; case HT_KEY: makestr(&ssl_rsa_key_file,q); break; case HT_SECURE: svc="https"; break; case HT_VERIFY: if (!rdigits(q)) printf("?Bad number: %s\n",q); ssl_verify_flag = atoi(q); break; } } free(p); break; } #endif /* CK_SSL */ case 'h': /* Help */ default: printf("Usage: %s host [ options... ]\n",xarg0); conola(http_hlp); doexit(GOOD_EXIT,-1); } } else { /* No dash - must be hostname */ host = *xargv; if (xargc > 1) { svc = *(xargv+1); if (svc) if (*svc == '-' || !*svc) svc = NULL; if (svc) { xargv++; xargc--; } } } } if (!svc) svc = ""; if (!*svc) svc = "http"; if (!host) XFATAL("No http host given"); /* Check action args before opening the connection */ if (http_action) { if (http_action == HTTP_PUT) { if (!lpath) XFATAL("No local path for http PUT"); } if (!path) XFATAL("No remote path for http action"); } /* Now it's OK to open the connection */ rdns[0] = NUL; x = (http_open(host, svc,!ckstrcmp("https",svc,-1,0),rdns,128,NULL ) == 0); if (!x) { if (!quiet) printf("?HTTP Connection failed.\r\n"); doexit(BAD_EXIT,-1); } if (!quiet) { if (rdns[0]) printf("Connected to %s [%s]\r\n",host,rdns); else printf("Connected to %s\r\n",host); } if (http_action) { int pcpy = 0; if (http_action != HTTP_PUT) { /* Supply default */ if (!lpath) { /* local path... */ zstrip(path,&lpath); if (!lpath) lpath = ""; if (!*lpath) lpath = "index.html"; } } if (*path != '/') { char * p = (char *) malloc(strlen(path)+2); if (!p) fatal("?Memory allocation error\n"); *p = '/'; strcpy(&p[1],path); /* safe */ path = p; pcpy = 1; } switch (http_action) { case HTTP_GET: x = http_get(agent,NULL,user,pswd,0,lpath,path,0); break; case HTTP_PUT: x = http_put(agent,NULL,"text/HTML", user,pswd,0,lpath,path,NULL,0); break; case HTTP_HED: x = http_head(agent,NULL,user,pswd,0,lpath,path,0); break; } debug(F101,"cmdline http result","",x); x = (http_close() == 0); if (pcpy) free(path); doexit(x ? GOOD_EXIT : BAD_EXIT, -1); } return(0); } } else #endif /* NOHTTP */ #ifdef NEWFTP if (howcalled == I_AM_FTP) { /* If I was called as FTP... */ debug(F100,"ftp personality","",0); #ifdef CK_URL if (haveurl) doftparg('U'); else #endif /* CK_URL */ { while (--xargc > 0) { /* Go through command line words */ xargv++; debug(F111,"cmdlin ftp xargv",*xargv,xargc); if (**xargv == '-') { /* Got an option */ int xx; x = *(*xargv+1); /* Get the option letter */ xx = doftparg(x); if (xx < 0) { if (what == W_COMMAND) return(0); else doexit(BAD_EXIT,1); } } else { /* No dash - must be hostname */ makestr(&ftp_host,*xargv); if (xargc > 1) { port = *(xargv+1); if (port) if (*port == '-' || !*port) port = NULL; if (port) { xargv++; xargc--; } } debug(F110,"cmdlin ftp host",ftp_host,0); debug(F110,"cmdlin ftp port",port,0); } } /* while */ } /* if (haveurl) */ if (ftp_host) { int xx; #ifdef NODIAL xx = xx_ftp(ftp_host,port); if (xx < 0 && (haveurl || ftp_cmdlin > 1)) doexit(BAD_EXIT,-1); #else #ifdef NOICP xx = xx_ftp(ftp_host,port); if (xx < 0 && (haveurl || ftp_cmdlin > 1)) doexit(BAD_EXIT,-1); #else if (*ftp_host == '=') { /* Skip directory lookup */ xx = xx_ftp(&ftp_host[1],port); if (xx < 0 && (haveurl || ftp_cmdlin > 1)) doexit(BAD_EXIT,-1); } else { /* Want lookup */ int i; nhcount = 0; /* Check network directory */ debug(F101,"cmdlin nnetdir","",nnetdir); if (nnetdir > 0) /* If there is a directory... */ lunet(ftp_host); /* Look up the name */ else /* If no directory */ nhcount = 0; /* we didn't find anything there */ #ifdef DEBUG if (deblog) { debug(F101,"cmdlin lunet nhcount","",nhcount); if (nhcount > 0) { debug(F110,"cmdlin lunet nh_p[0]",nh_p[0],0); debug(F110,"cmdlin lunet nh_p2[0]",nh_p2[0],0); debug(F110,"cmdlin lunet nh_px[0][0]", nh_px[0][0],0); } } #endif /* DEBUG */ if (nhcount == 0) { xx = xx_ftp(ftp_host,port); if (xx < 0 && (haveurl || ftp_cmdlin > 1)) doexit(BAD_EXIT,-1); } else { for (i = 0; i < nhcount; i++) { if (ckstrcmp(nh_p2[i],"tcp/ip",6,0)) continue; makestr(&ftp_host,nh_p[i]); debug(F110,"cmdlin calling xx_ftp",ftp_host,0); if (!quiet) printf("Trying %s...\n",ftp_host); if (xx_ftp(ftp_host,port) > -1) break; } } } #endif /* NODIAL */ #endif /* NOICP */ if (!ftpisconnected()) doexit(BAD_EXIT,-1); } return(0); } #endif /* NEWFTP */ #ifdef TNCODE if (howcalled == I_AM_TELNET) { /* If I was called as Telnet... */ while (--xargc > 0) { /* Go through command line words */ xargv++; debug(F111,"cmdlin telnet xargv",*xargv,xargc); if (**xargv == '=') return(0); if (!strcmp(*xargv,"--")) /* getopt() conformance */ return(0); #ifdef VMS else if (**xargv == '/') continue; #endif /* VMS */ else if (**xargv == '-') { /* Got an option (begins with dash) */ int xx; x = *(*xargv+1); /* Get the option letter */ debug(F111,"cmdlin telnet args 1",*xargv,xargc); xx = dotnarg(x); debug(F101,"cmdlin telnet doarg","",xx); debug(F111,"cmdlin telnet args 2",*xargv,xargc); if (xx < 0) { #ifndef NOICP if (what == W_COMMAND) return(0); else #endif /* NOICP */ { #ifdef OS2 sleep(1); /* Give it a chance... */ #endif /* OS2 */ doexit(BAD_EXIT,1); /* Go handle option */ } } } else { /* No dash must be hostname */ ckstrncpy(ttname,*xargv,TTNAMLEN+1); debug(F110,"cmdlin telnet host",ttname,0); #ifndef NOICP #ifndef NODIAL nhcount = 0; /* Check network directory */ debug(F101,"cmdlin telnet nnetdir","",nnetdir); if (nnetdir > 0) /* If there is a directory... */ lunet(*xargv); /* Look up the name */ else /* If no directory */ nhcount = 0; /* we didn't find anything there */ #ifdef DEBUG if (deblog) { debug(F101,"cmdlin telnet lunet nhcount","",nhcount); if (nhcount > 0) { debug(F110,"cmdlin telnet lunet nh_p[0]",nh_p[0],0); debug(F110,"cmdlin telnet lunet nh_p2[0]",nh_p2[0],0); debug(F110,"cmdlin telnet lunet nh_px[0][0]", nh_px[0][0],0); } } #endif /* DEBUG */ if (nhcount > 0 && nh_p2[0]) /* If network type specified */ if (ckstrcmp(nh_p2[0],"tcp/ip",6,0)) /* it must be TCP/IP */ nhcount = 0; if (nhcount == 1) { /* Still OK, so make substitution */ ckstrncpy(ttname,nh_p[0],TTNAMLEN+1); debug(F110,"cmdlin telnet lunet substitution",ttname,0); } #endif /* NODIAL */ #endif /* NOICP */ if (--xargc > 0 && !haveurl) { /* Service from command line? */ xargv++; ckstrncat(ttname,":",TTNAMLEN+1); ckstrncat(ttname,*xargv,TTNAMLEN+1); debug(F110,"cmdlin telnet host2",ttname,0); } #ifndef NOICP #ifndef NODIAL else if (nhcount) { /* No - how about in net directory? */ if (nh_px[0][0]) { ckstrncat(ttname,":",TTNAMLEN+1); ckstrncat(ttname,nh_px[0][0],TTNAMLEN+1); } } #endif /* NODIAL */ #endif /* NOICP */ local = 1; /* Try to open the connection */ nettype = NET_TCPB; mdmtyp = -nettype; if (ttopen(ttname,&local,mdmtyp,0) < 0) { XFATAL("can't open host connection"); } network = 1; /* It's open */ #ifdef CKLOGDIAL dolognet(); #endif /* CKLOGDIAL */ #ifndef NOXFER reliable = 1; /* It's reliable */ xreliable = 1; /* ... */ setreliable = 1; #endif /* NOXFER */ cflg = 1; /* Connect */ stayflg = 1; /* Stay */ tn_exit = 1; /* Telnet-like exit condition */ quiet = 1; exitonclose = 1; /* Exit when connection closes */ #ifndef NOSPL if (local) { if (nmac) { /* Any macros defined? */ int k; /* Yes */ k = mlook(mactab,"on_open",nmac); /* Look this up */ if (k >= 0) { /* If found, */ if (dodo(k,ttname,0) > -1) /* set it up, */ parser(1); /* and execute it */ } } } #endif /* NOSPL */ break; } } return(0); } #endif /* TNCODE */ #ifdef RLOGCODE else if (howcalled == I_AM_RLOGIN) { /* If I was called as Rlogin... */ while (--xargc > 0) { /* Go through command line words */ xargv++; debug(F111,"cmdlin rlogin xargv",*xargv,xargc); if (**xargv == '=') return(0); if (!strcmp(*xargv,"--")) /* getopt() conformance */ return(0); #ifdef VMS else if (**xargv == '/') continue; #endif /* VMS */ else if (**xargv == '-') { /* Got an option (begins with dash) */ int xx; x = *(*xargv+1); /* Get the option letter */ debug(F111,"cmdlin rlogin args 1",*xargv,xargc); xx = dorlgarg(x); debug(F101,"cmdlin rlogin doarg","",xx); debug(F111,"cmdlin rlogin args 2",*xargv,xargc); if (xx < 0) { #ifndef NOICP if (what == W_COMMAND) return(0); else #endif /* NOICP */ { #ifdef OS2 sleep(1); /* Give it a chance... */ #endif /* OS2 */ doexit(BAD_EXIT,1); /* Go handle option */ } } } else { /* No dash must be hostname */ ckstrncpy(ttname,*xargv,TTNAMLEN+1); debug(F110,"cmdlin rlogin host",ttname,0); #ifndef NOICP #ifndef NODIAL nhcount = 0; /* Check network directory */ debug(F101,"cmdlin rlogin nnetdir","",nnetdir); if (nnetdir > 0) /* If there is a directory... */ lunet(*xargv); /* Look up the name */ else /* If no directory */ nhcount = 0; /* we didn't find anything there */ #ifdef DEBUG if (deblog) { debug(F101,"cmdlin rlogin lunet nhcount","",nhcount); if (nhcount > 0) { debug(F110,"cmdlin rlogin lunet nh_p[0]",nh_p[0],0); debug(F110,"cmdlin rlogin lunet nh_p2[0]",nh_p2[0],0); debug(F110,"cmdlin rlogin lunet nh_px[0][0]", nh_px[0][0],0); } } #endif /* DEBUG */ if (nhcount > 0 && nh_p2[0]) /* If network type specified */ if (ckstrcmp(nh_p2[0],"tcp/ip",6,0)) /* it must be TCP/IP */ nhcount = 0; if (nhcount == 1) { /* Still OK, so make substitution */ ckstrncpy(ttname,nh_p[0],TTNAMLEN+1); debug(F110,"cmdlin rlogin lunet substitution",ttname,0); } #endif /* NODIAL */ #endif /* NOICP */ if (!haveurl) { /* Service from command line? */ ckstrncat(ttname,":login",TTNAMLEN+1); debug(F110,"cmdlin rlogin host2",ttname,0); } local = 1; /* Try to open the connection */ nettype = NET_TCPB; mdmtyp = -nettype; if (ttopen(ttname,&local,mdmtyp,0) < 0) { XFATAL("can't open host connection"); } network = 1; /* It's open */ #ifdef CKLOGDIAL dolognet(); #endif /* CKLOGDIAL */ #ifndef NOXFER reliable = 1; /* It's reliable */ xreliable = 1; /* ... */ setreliable = 1; #endif /* NOXFER */ cflg = 1; /* Connect */ stayflg = 1; /* Stay */ tn_exit = 1; /* Telnet-like exit condition */ quiet = 1; exitonclose = 1; /* Exit when connection closes */ #ifndef NOSPL if (local) { if (nmac) { /* Any macros defined? */ int k; /* Yes */ k = mlook(mactab,"on_open",nmac); /* Look this up */ if (k >= 0) { /* If found, */ if (dodo(k,ttname,0) > -1) /* set it up, */ parser(1); /* and execute it */ } } } #endif /* NOSPL */ break; } } return(0); } #endif /* RLOGCODE */ #endif /* TCPSOCKET */ #ifdef SSHBUILTIN if (howcalled == I_AM_SSH) { /* If I was called as SSH... */ extern char * ssh_hst, * ssh_cmd, * ssh_prt; debug(F100,"ssh personality","",0); #ifdef CK_URL if (haveurl) { makestr(&ssh_hst,g_url.hos); makestr(&ssh_prt,g_url.svc); ckstrncpy(ttname,ssh_hst,TTNAMLEN+1); ckstrncat(ttname,":",TTNAMLEN+1); ckstrncat(ttname,ssh_prt,TTNAMLEN+1); } else #endif /* CK_URL */ { while (--xargc > 0) { /* Go through command line words */ xargv++; debug(F111,"cmdlin ssh xargv",*xargv,xargc); if (**xargv == '=') return(0); if (!strcmp(*xargv,"--")) /* getopt() conformance */ return(0); #ifdef VMS else if (**xargv == '/') continue; #endif /* VMS */ /* Got an option (begins with dash) */ else if (**xargv == '-') { int xx; x = *(*xargv+1); /* Get the option letter */ debug(F111,"cmdlin args 1",*xargv,xargc); xx = dossharg(x); debug(F101,"cmdlin doarg","",xx); debug(F111,"cmdlin args 2",*xargv,xargc); if (xx < 0) { #ifndef NOICP if (what == W_COMMAND) return(0); else #endif /* NOICP */ { #ifdef OS2 sleep(1); /* Give it a chance... */ #endif /* OS2 */ doexit(BAD_EXIT,1); /* Go handle option */ } } } else { /* No dash must be hostname */ ckstrncpy(ttname,*xargv,TTNAMLEN+1); makestr(&ssh_hst,ttname); debug(F110,"cmdlin ssh host",ttname,0); #ifndef NOICP #ifndef NODIAL nhcount = 0; /* Check network directory */ debug(F101,"cmdlin nnetdir","",nnetdir); if (nnetdir > 0) /* If there is a directory... */ lunet(*xargv); /* Look up the name */ else /* If no directory */ nhcount = 0; /* we didn't find anything there */ #ifdef DEBUG if (deblog) { debug(F101,"cmdlin lunet nhcount","",nhcount); if (nhcount > 0) { debug(F110,"cmdlin lunet nh_p[0]",nh_p[0],0); debug(F110,"cmdlin lunet nh_p2[0]",nh_p2[0],0); debug(F110, "cmdlin lunet nh_px[0][0]",nh_px[0][0],0); } } #endif /* DEBUG */ /* If network type specified */ /* it must be TCP/IP */ if (nhcount > 0 && nh_p2[0]) if (ckstrcmp(nh_p2[0],"tcp/ip",6,0)) nhcount = 0; if (nhcount == 1) { /* Still OK, so make substitution */ ckstrncpy(ttname,nh_p[0],TTNAMLEN+1); makestr(&ssh_hst,ttname); debug(F110,"cmdlin lunet substitution",ttname,0); } #endif /* NODIAL */ #endif /* NOICP */ /* Service from command line? */ if (--xargc > 0 && !haveurl) { xargv++; ckstrncat(ttname,":",TTNAMLEN+1); ckstrncat(ttname,*xargv,TTNAMLEN+1); makestr(&ssh_prt,*xargv); debug(F110,"cmdlin telnet host2",ttname,0); } #ifdef COMMENT /* Do not substitute net dir service for ssh port */ #ifndef NOICP #ifndef NODIAL /* No - how about in net directory? */ else if (nhcount) { if (nh_px[0][0]) { ckstrncat(ttname,":",TTNAMLEN+1); ckstrncat(ttname,nh_px[0][0],TTNAMLEN+1); makestr(&ssh_prt,nh_px[0][0]); } } #endif /* NODIAL */ #endif /* NOICP */ #endif /* COMMENT */ break; } } } local = 1; /* Try to open the connection */ nettype = NET_SSH; mdmtyp = -nettype; if (ttopen(ttname,&local,mdmtyp,0) < 0) { XFATAL("can't open host connection"); } network = 1; /* It's open */ #ifdef CKLOGDIAL dolognet(); #endif /* CKLOGDIAL */ #ifndef NOXFER reliable = 1; /* It's reliable */ xreliable = 1; /* ... */ setreliable = 1; #endif /* NOXFER */ cflg = 1; /* Connect */ stayflg = 1; /* Stay */ tn_exit = 1; /* Telnet-like exit condition */ quiet = 1; exitonclose = 1; /* Exit when connection closes */ #ifndef NOSPL if (local) { if (nmac) { /* Any macros defined? */ int k; /* Yes */ k = mlook(mactab,"on_open",nmac); /* Look this up */ if (k >= 0) { /* If found, */ if (dodo(k,ttname,0) > -1) /* set it up, */ parser(1); /* and execute it */ } } } #endif /* NOSPL */ return(0); } #endif /* SSHBUILTIN */ if (howcalled == I_AM_SSHSUB) return(0); /* From here down: We were called as "kermit" or "iksd". If we were started directly from a Kermit script file, the filename of the script is in argv[1], so skip past it. */ if (xargc > 1) { int n = 1; if (*xargv[1] != '-') { #ifdef KERBANG /* If we were started with a Kerbang script, the script */ /* arguments were already picked up in prescan / cmdini() */ /* and there is nothing here for us anyway. */ if (!strcmp(xargv[1],"+")) return(0); #endif /* KERBANG */ if (cfilef) { /* Command file found in prescan() */ xargc -= n; /* Skip past it */ xargv += n; cfilef = 0; debug(F101,"cmdlin cfilef set to 0","",cfilef); } } } /* Regular Unix-style command line parser, mostly conforming with 'A Proposed Command Syntax Standard for Unix Systems', Hemenway & Armitage, Unix/World, Vol.1, No.3, 1984. */ while (--xargc > 0) { /* Go through command line words */ xargv++; debug(F111,"cmdlin xargv",*xargv,xargc); if (**xargv == '=') return(0); if (!strcmp(*xargv,"--")) /* getopt() conformance */ return(0); #ifdef VMS else if (**xargv == '/') continue; #endif /* VMS */ else if (**xargv == '-') { /* Got an option (begins with dash) */ int xx; x = *(*xargv+1); /* Get the option letter */ debug(F111,"cmdlin args 1",*xargv,xargc); xx = doarg(x); debug(F101,"cmdlin doarg","",xx); debug(F111,"cmdlin args 2",*xargv,xargc); if (xx < 0) { #ifndef NOICP if (what == W_COMMAND) return(0); else #endif /* NOICP */ { #ifdef OS2 sleep(1); /* Give it a chance... */ #endif /* OS2 */ doexit(BAD_EXIT,1); /* Go handle option */ } } } else if (!haveurl) { /* No dash where expected */ char xbuf[32]; char buf[128]; int k; k = ckstrncpy(xbuf,*xargv,40); if (k > 30) { xbuf[30] = '.'; xbuf[29] = '.'; xbuf[28] = '.'; } xbuf[31] = NUL; ckmakmsg(buf, 128, "invalid command-line option, type \"", myname, " -h\" for help", NULL ); fatal2(xbuf,buf); } } #ifdef DEBUG if (deblog) { #ifndef NOICP debug(F101,"cmdlin what","",what); #endif /* NOICP */ debug(F101,"cmdlin action","",action); #ifndef NOXFER debug(F101,"cmdlin stdouf","",stdouf); #endif /* NOXFER */ } #endif /* DEBUG */ #ifdef NOICP if (!action && !cflg && !cnflg) { debug(F100,"cmdlin NOICP fatal no action","",0); XFATAL("?No actions specified on command line"); } #else if (inserver && what == 0) { /* Internet Kermit server checks */ if (local || (action != 0 && action != 'x')) { if (local) printf("local\r\n"); if (action) printf("action=%c\r\n",action); debug(F100,"cmdlin fatal 1","",0); XFATAL("No actions or connections allowed with -A"); } } #endif /* NOICP */ #ifndef NOLOCAL if (!local) { if ((action == 'c') || (cflg != 0)) { debug(F100,"cmdlin fatal 2","",0); XFATAL("-l or -j or -X required"); } } #endif /* NOLOCAL */ #ifndef NOXFER if (*cmarg2 != 0) { if ((action != 's') && (action != 'r') && (action != 'v')) { debug(F100,"cmdlin fatal 3","",0); XFATAL("-a without -s, -r, or -g"); } if (action == 'r' || action == 'v') { #ifdef CK_TMPDIR if (isdir(cmarg2)) { /* -a is a directory */ if (!zchdir(cmarg2)) { /* try to change to it */ debug(F100,"cmdlin fatal 4","",0); XFATAL("can't change to '-a' directory"); } else cmarg2 = ""; } else #endif /* CK_TMPDIR */ if (zchko(cmarg2) < 0) { debug(F100,"cmdlin fatal 5","",0); XFATAL("write access to -a file denied"); } } } if ((action == 'v') && (stdouf) && (!local)) { if (is_a_tty(1)) { debug(F100,"cmdlin fatal 6","",0); XFATAL("unredirected -k can only be used in local mode"); } } if ((action == 's') || (action == 'v') || (action == 'r') || (action == 'x')) { if (local) displa = 1; if (stdouf) { displa = 0; quiet = 1; } } if (quiet) displa = 0; /* No display if quiet requested */ #endif /* NOXFER */ #ifdef DEBUG if (action) debug(F000,"cmdlin returns action","",action); else debug(F101,"cmdlin returns action","",action); #endif /* DEBUG */ return(action); /* Then do any requested protocol */ } /* Extended argument parsing: --keyword[:value] (or =value) */ /* XA_xxxx symbols are defined in ckuusr.h. If you add a new one, also remember to update doshow(), SHXOPT section, in ckuus5.c. */ struct keytab xargtab[] = { #ifdef CK_LOGIN { "anonymous", XA_ANON, CM_ARG|CM_PRE }, #endif /* CK_LOGIN */ { "bannerfile", XA_BAFI, CM_ARG }, { "cdfile", XA_CDFI, CM_ARG }, { "cdmessage", XA_CDMS, CM_ARG }, { "cdmsg", XA_CDMS, CM_ARG|CM_INV }, #ifdef KUI { "changedim", XA_CHGD, CM_PRE }, #endif /* KUI */ #ifndef NOCSETS { "charset", XA_CSET, CM_ARG|CM_PRE }, #endif /* NOCSETS */ #ifdef IKSDB { "database", XA_DBAS, CM_ARG|CM_PRE }, { "dbfile", XA_DBFI, CM_ARG|CM_PRE }, #endif /* IKSDB */ #ifdef KUI { "facename", XA_FNAM, CM_ARG|CM_PRE|CM_INV }, { "fontname", XA_FNAM, CM_ARG|CM_PRE }, { "fontsize", XA_FSIZ, CM_ARG|CM_PRE }, #endif /* KUI */ #ifdef COMMENT #ifdef NEWFTP { "ftp", XA_FTP, CM_ARG }, #endif /* NEWFTP */ #endif /* COMMENT */ #ifndef NOLOCAL #ifdef OS2 { "height", XA_ROWS, CM_ARG|CM_PRE }, #endif /* OS2 */ #endif /* NOLOCAL */ { "help", XA_HELP, 0 }, #ifndef NOHELP { "helpfile", XA_HEFI, CM_ARG }, #endif /* NOHELP */ #ifdef CK_LOGIN { "initfile", XA_ANFI, CM_ARG|CM_PRE }, #endif /* CK_LOGIN */ #ifdef OS2 { "lockdown", XA_LOCK, CM_PRE }, #ifdef KUI { "maximize", XA_WMAX, CM_PRE }, { "minimize", XA_WMIN, CM_PRE }, { "nobars", XA_NOBAR, CM_PRE }, { "noclose" , XA_NOCLOSE, CM_PRE }, #endif /* KUI */ { "noescape", XA_NOESCAPE, CM_PRE }, #endif /* OS2 */ { "nointerrupts",XA_NOIN, CM_PRE }, #ifdef KUI { "nomenubar", XA_NOMN, CM_PRE }, #endif /* KUI */ { "noperms", XA_NPRM, 0 }, #ifndef NOPUSH { "nopush", XA_NOPUSH, CM_PRE }, #endif /* NOPUSH */ #ifdef OS2 { "noscroll", XA_NOSCROLL, CM_PRE }, #endif /* OS2 */ #ifdef KUI { "nostatusbar", XA_NOSB, CM_PRE }, { "notoolbar", XA_NOTB, CM_PRE }, #endif /* KUI */ #ifdef COMMENT { "password", XA_PASS, CM_ARG|CM_INV }, #endif /* COMMENT */ #ifdef CK_LOGIN #ifndef NOXFER #ifdef CK_PERM { "permissions", XA_PERM, CM_ARG|CM_PRE }, { "perms", XA_PERM, CM_ARG|CM_PRE|CM_INV }, #endif /* CK_PERM */ #endif /* NOXFER */ #ifdef UNIX { "privid", XA_PRIV, CM_ARG|CM_PRE }, #endif /* UNIX */ #ifndef NOLOCAL #ifndef NOCSETS { "rcharset", XA_CSET, CM_ARG|CM_PRE|CM_INV }, #endif /* NOCSETS */ #endif /* NOLOCAL */ #ifdef UNIX { "root", XA_ROOT, CM_ARG|CM_PRE }, #else /* UNIX */ #ifdef CKROOT { "root", XA_ROOT, CM_ARG|CM_PRE }, #endif /* CKROOT */ #endif /* UNIX */ #ifdef KUI { "scalefont", XA_SCALE, CM_PRE }, #endif /* KUI */ #ifdef COMMENT #ifdef SSHBUILTIN { "ssh", XA_SSH, CM_ARG }, #endif /* SSHBUILTIN */ #endif /* COMMENT */ #ifdef CKSYSLOG { "syslog", XA_SYSL, CM_ARG|CM_PRE }, #endif /* CKSYSLOG */ #ifndef NOLOCAL #ifdef COMMENT #ifdef TNCODE { "telnet", XA_TEL, CM_ARG }, #endif /* TNCODE */ #endif /* COMMENT */ { "termtype", XA_TERM, CM_ARG|CM_PRE }, #endif /* NOLOCAL */ { "timeout", XA_TIMO, CM_ARG|CM_PRE }, #ifndef NOLOCAL #ifdef OS2 { "title", XA_TITL, CM_ARG }, #endif /* OS2 */ #ifdef UNIX { "unbuffered", XA_UNBUF, 0 }, #endif /* UNIX */ #ifndef NOSPL { "user", XA_USER, CM_ARG }, #endif /* NOSPL */ #endif /* NOLOCAL */ { "userfile", XA_USFI, CM_ARG|CM_PRE }, { "version", XA_VERS, 0 }, #ifndef NOLOCAL #ifdef OS2 { "width", XA_COLS, CM_ARG|CM_PRE }, #endif /* OS2 */ #endif /* NOLOCAL */ #ifdef CKWTMP { "wtmpfile", XA_WTFI, CM_ARG|CM_PRE }, { "wtmplog", XA_WTMP, CM_ARG|CM_PRE }, #endif /* CKWTMP */ #endif /* CK_LOGIN */ { "xferfile", XA_IKFI, CM_ARG|CM_PRE }, { "xferlog", XA_IKLG, CM_ARG|CM_PRE }, #ifndef NOLOCAL #ifdef KUI { "xpos", XA_XPOS, CM_ARG|CM_PRE }, { "ypos", XA_YPOS, CM_ARG|CM_PRE }, #endif /* KUI */ #endif /* NOLOCAL */ {"", 0, 0 } }; int nxargs = sizeof(xargtab)/sizeof(struct keytab) - 1; static struct keytab oktab[] = { { "0", 0, 0 }, { "1", 1, 0 }, { "2", 2, 0 }, { "3", 3, 0 }, { "4", 4, 0 }, { "5", 5, 0 }, { "6", 6, 0 }, { "7", 7, 0 }, { "8", 8, 0 }, { "9", 9, 0 }, { "false", 0, 0 }, { "no", 0, 0 }, { "off", 0, 0 }, { "ok", 1, 0 }, { "on", 1, 0 }, { "true", 1, 0 }, { "yes", 1, 0 } }; static int noktab = sizeof(oktab)/sizeof(struct keytab); #define XARGBUFL 32 char * xopthlp[XA_MAX+1]; /* Extended option help */ char * xarghlp[XA_MAX+1]; /* Extended argument for option */ static VOID inixopthlp() { int i, j; for (i = 0; i <= XA_MAX; i++) { /* Initialize all to null */ xopthlp[i] = NULL; xarghlp[i] = NULL; } for (i = 0; i < nxargs; i++) { /* Then for each defined keyword */ j = xargtab[i].kwval; /* index by associated value */ if (j < 0 || j > XA_MAX) continue; switch (j) { #ifdef CK_LOGIN case XA_ANON: /* "--anonymous" */ xopthlp[j] = "--anonymous:{on,off} [IKSD only]"; xarghlp[j] = "Whether to allow anonymous IKSD logins"; break; #ifdef UNIX case XA_PRIV: xopthlp[j] = "--privid:{on,off} [IKSD only]"; xarghlp[j] = "Whether to allow privileged IDs to login to IKSD"; break; #endif /* UNIX */ #endif /* CK_LOGIN */ case XA_BAFI: /* "--bannerfile" */ xopthlp[j] = "--bannerfile:"; xarghlp[j] = "File to display upon startup or IKSD login"; break; case XA_CDFI: /* "--cdfile" */ xopthlp[j] = "--cdfile:"; xarghlp[j] = "File to display when server changes directory"; break; case XA_CDMS: /* "--cdmessage" */ xopthlp[j] = "--cdmessage:{on,off}"; xarghlp[j] = "Whether to display CD message file"; break; case XA_HELP: /* "--help" */ xopthlp[j] = "--help"; xarghlp[j] = "Print this help text about extended options"; break; case XA_HEFI: /* "--help" */ xopthlp[j] = "--helpfile:"; xarghlp[j] = "File containing custom info for HELP command"; break; case XA_IKFI: /* "--xferfile" */ xopthlp[j] = "--xferfile: [IKSD only]"; xarghlp[j] = "Name of ftpd-like logfile."; break; case XA_IKLG: /* "--xferlog" */ xopthlp[j] = "--xferlog:{on,off} [IKSD only]"; xarghlp[j] = "Whether to keep an ftpd-like logfile."; break; #ifdef CK_LOGIN case XA_ANFI: /* "--initfile" */ xopthlp[j] = "--initfile: [IKSD only]"; xarghlp[j] = "Initialization file for anonymous users."; break; #ifdef CK_PERM case XA_PERM: /* "--permissions" */ xopthlp[j] = "--permissions: [IKSD only]"; xarghlp[j] = "Permissions for files uploaded by anonymous users."; break; #endif /* CK_PERM */ #ifdef UNIX case XA_ROOT: /* "--root" */ xopthlp[j] = "--root: [IKSD only]"; xarghlp[j] = "File-system root for anonymous users."; break; #else /* UNIX */ #ifdef CKROOT case XA_ROOT: /* "--root" */ xopthlp[j] = "--root: [IKSD only]"; xarghlp[j] = "File-system root for anonymous users."; break; #endif /* CKROOT */ #endif /* UNIX */ #endif /* CK_LOGIN */ #ifdef CKSYSLOG case XA_SYSL: /* "--syslog" */ xopthlp[j] = "--syslog: [IKSD only]"; xarghlp[j] = "Syslog recording level, 0-6."; break; #endif /* CKSYSLOG */ case XA_USFI: /* "--userfile" */ xopthlp[j] = "--userfile: [IKSD only]"; xarghlp[j] = "Forbidden user file."; break; #ifdef CKWTMP case XA_WTFI: /* "--wtmpfile" */ xopthlp[j] = "--wtmpfile: [IKSD only]"; xarghlp[j] = "Name of wtmp logfile."; break; case XA_WTMP: /* "--wtmplog" */ xopthlp[j] = "--wtmplog:{on,off} [IKSD only]"; xarghlp[j] = "Whether to keep a wtmp logfile."; break; #endif /* CKWTMP */ #ifdef CK_LOGIN case XA_TIMO: /* "--timeout" */ xopthlp[j] = "--timeout: [IKSD only]"; xarghlp[j] = "How long to wait for login before closing the connection."; break; #endif /* CK_LOGIN */ case XA_NOIN: xopthlp[j] = "--nointerrupts"; xarghlp[j] = "Disable keyboard interrupts."; break; #ifdef UNIX case XA_UNBUF: xopthlp[j] = "--unbuffered"; xarghlp[j] = "Force unbuffered console i/o."; break; #endif /* UNIX */ #ifdef IKSDB case XA_DBAS: xopthlp[j] = "--database:{on,off}"; xarghlp[j] = "Enable/Disable IKSD database (IKSD only)"; break; case XA_DBFI: xopthlp[j] = "--dbfile:"; xarghlp[j] = "Specify IKSD database file (IKSD only)"; break; #endif /* IKSDB */ #ifdef CK_PERMS case XA_NPRM: xopthlp[j] = "--noperms"; xarghlp[j] = "Disable file-transfer Permissions attribute."; break; #endif /* CK_PERMS */ #ifdef KUI case XA_CHGD: xopthlp[j] = "--changedim"; xarghlp[j] = "Change Dimension on Window Resize"; case XA_SCALE: xopthlp[j] = "--scalefont"; xarghlp[j] = "Scale Font on Window Resize"; case XA_WMAX: xopthlp[j] = "--maximize"; xarghlp[j] = "start K95G window maximized."; break; case XA_WMIN: xopthlp[j] = "--minimize"; xarghlp[j] = "start K95G window minimized."; break; case XA_XPOS: xopthlp[j] = "--xpos:n"; xarghlp[j] = "X-coordinate of window position (number)."; break; case XA_YPOS: xopthlp[j] = "--ypos:n"; xarghlp[j] = "Y-coordinate of window position (number)."; break; case XA_FNAM: xopthlp[j] = "--fontname:s (or --facename:s)"; xarghlp[j] = "Font/typeface name: string with _ replacing blank."; break; case XA_FSIZ: xopthlp[j] = "--fontsize:n"; xarghlp[j] = "Font point size (number)."; break; case XA_NOMN: xopthlp[j] = "--nomenubar"; xarghlp[j] = "No Menu Bar"; break; case XA_NOTB: xopthlp[j] = "--notoolbar"; xarghlp[j] = "No Tool Bar"; break; case XA_NOSB: xopthlp[j] = "--nostatusbar"; xarghlp[j] = "No Status Bar"; break; case XA_NOBAR: xopthlp[j] = "--nobars"; xarghlp[j] = "No Menu, Status, or Tool Bars"; break; #endif /* KUI */ #ifndef NOPUSH case XA_NOPUSH: xopthlp[j] = "--nopush"; xarghlp[j] = "Disable external command execution."; break; #endif /* NOPUSH */ #ifdef OS2 case XA_LOCK: xopthlp[j] = "--lockdown"; xarghlp[j] = "Enable all lockdown options."; break; case XA_NOCLOSE: xopthlp[j] = "--noclose"; xarghlp[j] = "Disable Close Window and Menu Exit."; break; case XA_NOSCROLL: xopthlp[j] = "--noscroll"; xarghlp[j] = "Disable scrollback operations."; break; case XA_NOESCAPE: xopthlp[j] = "--noescape"; xarghlp[j] = "Disable escape from connect mode."; break; case XA_ROWS: xopthlp[j] = "--height:n"; xarghlp[j] = "Screen height (number of rows)."; break; case XA_COLS: xopthlp[j] = "--width:n"; xarghlp[j] = "Screen width (number of columns)."; break; case XA_TITL: xopthlp[j] = "--title:string"; xarghlp[j] = "Window Title."; break; #endif /* OS2 */ case XA_CSET: xopthlp[j] = "--rcharset:name"; xarghlp[j] = "Name of remote terminal character set."; break; case XA_TERM: xopthlp[j] = "--termtype:name"; #ifdef OS2 xarghlp[j] = "Choose terminal emulation."; #else xarghlp[j] = "Choose terminal type."; #endif /* OS2 */ break; case XA_USER: xopthlp[j] = "--user:name"; #ifndef NETCONN xarghlp[j] = "Username (for network login)"; #else xarghlp[j] = "Username."; #endif /* NETCONN */ break; } } } VOID iniopthlp() { int i; for (i = 0; i < 128; i++) { optact[i] = 0; switch(i) { #ifdef OS2 case '#': /* K95 Startup Flags */ opthlp[i] = "Kermit 95 Startup Flags"; arghlp[i] = "\n"\ " 1 - turn off Win95 special fixes\n"\ " 2 - do not load optional network dlls\n"\ " 4 - do not load optional tapi dlls\n"\ " 8 - do not load optional kerberos dlls\n"\ " 16 - do not load optional zmodem dlls\n"\ " 32 - use stdin for input instead of the console\n"\ " 64 - use stdout for output instead of the console\n"\ " 128 - do not terminate process in response to Session Logoff"; break; #endif /* OS2 */ case '0': /* In the middle */ opthlp[i] = "100% transparent CONNECT mode for \"in-the-middle\" operation"; arghlp[i] = NULL; break; case '8': opthlp[i] = "Connection is 8-bit clean"; arghlp[i] = NULL; break; #ifdef NEWFTP case '9': opthlp[i] = "Make a connection to an FTP server"; arghlp[i] = "IP-address-or-hostname[:optional-TCP-port]"; break; #endif /* NEWFTP */ #ifdef IKSD case 'A': opthlp[i] = "Kermit is to be started as an Internet service"; #ifdef NT arghlp[i] = " socket handle of incoming connection"; #else /* NT */ arghlp[i] = NULL; #endif /* NT */ break; #endif /* IKSD */ case 'B': opthlp[i] = "Kermit is running in Batch or Background (no controlling terminal)"; break; #ifndef NOSPL case 'C': opthlp[i] = "Interactive-mode Commands to be executed"; arghlp[i] = "Commands separated by commas, list in doublequotes"; break; #endif /* NOSPL */ case 'D': opthlp[i] = "Delay before starting to send"; arghlp[i] = "Number of seconds"; break; case 'E': opthlp[i] = "Exit automatically when connection closes"; arghlp[i] = NULL; break; #ifdef TCPSOCKET case 'F': opthlp[i] = "Use an existing TCP connection"; arghlp[i] = "Numeric file descriptor of open TCP connection"; break; #endif /* TCPSOCKET */ case 'G': opthlp[i] = "GET from server, send to standard output"; arghlp[i] = "Remote file specification"; optact[i] = 1; break; case 'H': opthlp[i] = "Suppress program startup Herald and greeting"; arghlp[i] = NULL; break; case 'I': opthlp[i] = "Connection is reliable, streaming is allowed"; arghlp[i] = NULL; break; #ifdef TCPSOCKET case 'J': opthlp[i] = "'Be like Telnet'"; arghlp[i] = "IP hostname/address optionally followed by service"; break; #endif /* TCPSOCKET */ case 'L': opthlp[i] = "Recursive directory descent for files in -s option"; arghlp[i] = NULL; break; case 'M': opthlp[i] = "My user name (for use with Telnet, Rlogin, etc)"; arghlp[i] = "Username string"; break; #ifdef NETBIOS case 'N': opthlp[i] = "NETBIOS adapter number"; arghlp[i] = "Number"; break; #endif /* NETBIOS */ case 'O': /* Be a server for One command only */ opthlp[i] = "Be a server for One command only"; arghlp[i] = NULL; optact[i] = 1; break; case 'P': opthlp[i] = "Don't convert file (Path) names"; arghlp[i] = NULL; break; case 'Q': opthlp[i] = "Quick (FAST) Kermit protocol settings"; arghlp[i] = NULL; break; case 'R': /* Remote-Only */ opthlp[i] = "Remote-only (makes IF REMOTE true)"; arghlp[i] = NULL; break; case 'S': /* "Stay" - enter interactive */ opthlp[i] = "Stay (enter command parser after action options)"; arghlp[i] = NULL; break; case 'T': /* Text file transfer mode */ opthlp[i] = "Transfer files in Text mode"; arghlp[i] = NULL; break; #ifdef ANYX25 case 'U': /* X.25 call user data */ opthlp[i] = "X.25 call User data"; arghlp[i] = "Call-user-data string"; break; #endif /* ANYX25 */ case 'V': /* No automatic filetype switching */ opthlp[i] = "Disable automatic per-file text/binary switching"; arghlp[i] = NULL; break; #ifdef COMMENT #ifdef OS2 case 'W': /* Win32 Window Handle */ opthlp[i] = ""; arghlp[i] = NULL; break; #endif /* OS2 */ #endif /* COMMENT */ #ifdef ANYX25 case 'X': /* SET HOST to X.25 address */ opthlp[i] = "Make an X.25 connection"; arghlp[i] = "X.25 or X.121 address"; break; #endif /* ANYX25 */ case 'Y': /* No initialization file */ opthlp[i] = "Skip initialization file"; arghlp[i] = NULL; break; #ifdef ANYX25 case 'Z': /* SET HOST to X.25 file descriptor */ opthlp[i] = "Make an X.25 connection"; arghlp[i] = "Numeric file descriptor of open X.25 connection"; break; #endif /* ANYX25 */ case 'a': /* as-name */ opthlp[i] = "As-name for file(s) in -s, -r, or -g"; arghlp[i] = "As-name string (alternative filename)"; break; case 'b': /* Set bits-per-second for serial */ opthlp[i] = "Speed for serial device"; arghlp[i] = "Numeric Bits per second"; break; case 'c': /* Connect before */ optact[i] = 1; opthlp[i] = "CONNECT before transferring files"; arghlp[i] = NULL; break; case 'd': /* DEBUG */ opthlp[i] = "Create debug.log file (a second -d adds timestamps)"; arghlp[i] = NULL; break; case 'e': /* Extended packet length */ opthlp[i] = "Maximum length for incoming file-transfer packets"; arghlp[i] = "Length in bytes"; break; case 'f': /* finish */ optact[i] = 1; opthlp[i] = "Send Finish command to a Kermit server"; arghlp[i] = NULL; break; case 'g': /* get */ optact[i] = 1; opthlp[i] = "GET file(s) from a Kermit server"; arghlp[i] = "Remote file specification"; break; case 'h': /* help */ optact[i] = 1; #ifdef OS2ORUNIX opthlp[i] = "Print this message (pipe thru 'more' to prevent scrolling)"; #else "Print this message"; #endif /* OS2ORUNIX */ arghlp[i] = NULL; break; case 'i': /* Treat files as binary */ opthlp[i] ="Transfer files in binary mode"; arghlp[i] = NULL; break; #ifdef TCPSOCKET case 'j': /* SET HOST (TCP/IP socket) */ opthlp[i] = "Make a TCP connection"; arghlp[i] = "TCP host name/address and optional service name or number"; break; #endif /* TCPSOCKET */ case 'k': /* receive to stdout */ optact[i] = 1; opthlp[i] = "RECEIVE file(s) to standard output"; arghlp[i] = NULL; break; case 'l': /* SET LINE */ opthlp[i] = "Make connection on serial communications device"; arghlp[i] = "Serial device name"; break; case 'm': /* Modem type */ opthlp[i] = "Modem type for use with -l device"; arghlp[i] = "Modem name as in SET MODEM TYPE command"; break; case 'n': /* connect after */ optact[i] = 1; opthlp[i] = "CONNECT after transferring files"; arghlp[i] = NULL; break; #ifdef ANYX25 case 'o': /* X.25 closed user group */ opthlp[i] = "X.25 closed user group"; arghlp[i] = "User group string"; break; #endif /* ANYX25 */ case 'p': /* SET PARITY */ opthlp[i] = "Parity"; arghlp[i] = "One of the following: even, odd, mark, none, space"; break; case 'q': /* Quiet */ opthlp[i] = "Quiet (suppress most messages)"; arghlp[i] = NULL; break; case 'r': /* receive */ optact[i] = 1; opthlp[i] = "RECEIVE file(s)"; arghlp[i] = NULL; break; case 's': /* send */ optact[i] = 1; opthlp[i] = "SEND file(s)"; arghlp[i] = "One or more file specifications"; break; case 't': /* Line turnaround handshake */ opthlp[i] = "XON Turnaround character for half-duplex connections"; arghlp[i] = NULL; break; #ifdef ANYX25 case 'u': /* X.25 reverse charge call */ opthlp[i] = "X.25 reverse charge call"; arghlp[i] = NULL; break; #endif /* ANYX25 */ case 'v': /* Vindow size */ opthlp[i] = "Window size"; arghlp[i] = "Number, 1 to 32"; break; case 'w': /* Writeover */ opthlp[i] = "Incoming files Write over existing files"; arghlp[i] = NULL; break; case 'x': /* Server */ optact[i] = 1; opthlp[i] = "Be a Kermit SERVER"; arghlp[i] = NULL; break; case 'y': /* Alternate init-file name */ opthlp[i] = "Alternative initialization file"; arghlp[i] = "File specification"; break; case 'z': /* Not background */ opthlp[i] = "Force foreground behavior"; arghlp[i] = NULL; break; default: opthlp[i] = NULL; arghlp[i] = NULL; } } inixopthlp(); } int doxarg(s,pre) char ** s; int pre; { #ifdef IKSD #ifdef CK_LOGIN extern int ckxsyslog, ckxwtmp, ckxanon; #ifdef UNIX extern int ckxpriv; #endif /* UNIX */ #ifdef CK_PERMS extern int ckxperms; #endif /* CK_PERMS */ extern char * anonfile, * userfile, * anonroot; #endif /* CK_LOGIN */ #ifdef CKWTMP extern char * wtmpfile; #endif /* CKWTMP */ #endif /* IKSD */ extern int srvcdmsg; extern char * cdmsgfile[], * cdmsgstr; char tmpbuf[CKMAXPATH+1]; int i, x, y, z, havearg = 0; char buf[XARGBUFL], c, * p; if (nxargs < 1) return(-1); c = *(*s + 1); /* Hyphen or Plus sign */ p = *s + 2; for (i = 0; *p && i < XARGBUFL; i++) { buf[i] = *p++; if (buf[i] == '=' || buf[i] == ':') { havearg = 1; buf[i] = NUL; break; } else if (buf[i] < ' ') { buf[i] = NUL; break; } } if (i > XARGBUFL - 1) return(-1); buf[i] = NUL; x = lookup(xargtab,buf,nxargs,&z); /* Lookup the option keyword */ if (x < 0) /* On any kind of error */ return(-1); /* fail. */ /* Handle prescan versus post-initialization file */ if (((xargtab[z].flgs & CM_PRE) || (c == '+')) && !pre) return(0); else if (pre && !(xargtab[z].flgs & CM_PRE) && (c != '+')) return(0); /* Ensure that argument is given if and only if required */ p = havearg ? *s + i + 3 : NULL; if ((xargtab[z].flgs & CM_ARG) && !havearg) return(-1); else if ((!(xargtab[z].flgs & CM_ARG)) && havearg) return(-1); switch (x) { /* OK to process this option... */ #ifdef CKSYSLOG case XA_SYSL: /* IKS: Syslog level */ y = 0; if (isdigit(*p)) { while (*p) { if (*p < '0' || *p > '9') return(-1); y = y * 10 + (*p++ - '0'); } } else { y = lookup(oktab,p,noktab,&z); if (y > 0) y = SYSLG_DF; /* Yes = default logging level */ } #ifndef SYSLOGLEVEL /* If specified on cc command line, user can't change it. */ if (!inserver) /* Don't allow voluminous syslogging */ if (y > SYSLG_FA) /* by ordinary users. */ y = SYSLG_FA; #endif /* SYSLOGLEVEL */ if (y < 0) return(-1); #ifdef DEBUG if (y >= SYSLG_DB) if (!deblog) deblog = debopn("debug.log",0); #endif /* DEBUG */ #ifdef SYSLOGLEVEL /* If specified on cc command line, user can't change it. */ y = SYSLOGLEVEL; #endif /* SYSLOGLEVEL */ ckxsyslog = y; /* printf("ckxsyslog=%d\n",ckxsyslog); */ break; #endif /* CKSYSLOG */ #ifdef CK_LOGIN #ifdef CKWTMP case XA_WTMP: /* IKS: wtmp log */ y = lookup(oktab,p,noktab,&z); if (y < 0) return(-1); ckxwtmp = y; /* printf("ckxwtmp=%d\n",ckxwtmp); */ break; case XA_WTFI: /* IKS: wtmp logfile */ if (zfnqfp(p,CKMAXPATH,tmpbuf)) p = tmpbuf; makestr(&wtmpfile,p); /* printf("wtmpfile=%s\n",wtmpfile); */ break; #endif /* CKWTMP */ case XA_ANON: /* IKS: Anonymous login allowed */ y = lookup(oktab,p,noktab,&z); if (y < 0) return(-1); ckxanon = y; /* printf("ckxanon=%d\n",ckxanon); */ break; #ifdef UNIX case XA_PRIV: /* IKS: Priv'd login allowed */ y = lookup(oktab,p,noktab,&z); if (y < 0) return(-1); ckxpriv = y; /* printf("ckxpriv=%d\n",ckxpriv); */ break; #endif /* UNIX */ #ifdef CK_PERMS case XA_PERM: /* IKS: Anonymous Upload Permissions */ y = 0; while (*p) { if (*p < '0' || *p > '7') return(-1); y = y * 8 + (*p++ - '0'); } ckxperms = y; /* printf("ckxperms=%04o\n",ckxperms); */ break; #endif /* CK_PERMS */ case XA_ANFI: /* Anonymous init file */ if (!isabsolute(p)) if (zfnqfp(p,CKMAXPATH,tmpbuf)) p = tmpbuf; makestr(&anonfile,p); /* printf("anonfile=%s\n",anonfile); */ break; case XA_USFI: /* IKS: Forbidden user file */ if (!isabsolute(p)) if (zfnqfp(p,CKMAXPATH,tmpbuf)) p = tmpbuf; makestr(&userfile,p); /* printf("userfile=%s\n",userfile); */ break; case XA_ROOT: /* IKS: Anonymous root */ if (!isabsolute(p)) if (zfnqfp(p,CKMAXPATH,tmpbuf)) p = tmpbuf; makestr(&anonroot,p); /* printf("anonroot=%s\n",anonroot); */ break; #endif /* CK_LOGIN */ case XA_CDFI: /* CD filename */ #ifdef COMMENT /* Do NOT expand this one! */ if (zfnqfp(p,CKMAXPATH,tmpbuf)) p = tmpbuf; #endif /* COMMENT */ makelist(p,cdmsgfile,16); makestr(&cdmsgstr,p); /* printf("cdmsgstr=%s\n",cdmsgstr); */ break; case XA_CDMS: /* CD messages */ y = lookup(oktab,p,noktab,&z); if (y < 0) return(-1); srvcdmsg = y; /* printf("srvcdmsg=%d\n",srvcdmsg); */ break; #ifndef NOXFER case XA_IKLG: /* Transfer log on/off */ y = lookup(oktab,p,noktab,&z); if (y < 0) return(-1); xferlog = y; /* printf("xferlog=%d\n",xferlog); */ break; case XA_IKFI: /* Transfer log file */ if (!isabsolute(p)) if (zfnqfp(p,CKMAXPATH,tmpbuf)) p = tmpbuf; makestr(&xferfile,p); xferlog = 1; /* printf("xferfile=%s\n",xferfile); */ break; case XA_BAFI: /* IKS: banner (greeting) file */ if (!isabsolute(p)) if (zfnqfp(p,CKMAXPATH,tmpbuf)) p = tmpbuf; makestr(&bannerfile,p); /* printf("bannerfile=%s\n",bannerfile); */ break; #endif /* NOXFER */ #ifndef NOHELP case XA_HELP: /* Help */ /* printf("help\n"); */ for (i = 0; i <= XA_MAX; i++) if (xopthlp[i]) printf("%s\n %s\n\n",xopthlp[i],xarghlp[i]); if (stayflg || what == W_COMMAND) break; else doexit(GOOD_EXIT,-1); #endif /* NOHELP */ #ifndef NOHELP case XA_HEFI: /* IKS: custom help file */ if (!isabsolute(p)) if (zfnqfp(p,CKMAXPATH,tmpbuf)) p = tmpbuf; makestr(&helpfile,p); /* printf("helpfile=%s\n",helpfile); */ break; #endif /* NOHELP */ #ifdef CK_LOGIN case XA_TIMO: if (!rdigits(p)) return(-1); logintimo = atoi(p); /* printf("logintimo=%d\n",p); */ break; #endif /* CK_LOGIN */ case XA_NOIN: /* No interrupts */ #ifndef NOICP cmdint = 0; #endif /* NOICP */ xsuspend = 0; break; #ifdef UNIX case XA_UNBUF: /* Unbuffered console i/o*/ break; /* This one is handled in ckcmai.c */ #endif /* UNIX */ #ifdef IKSDB case XA_DBFI: { extern char * dbdir, * dbfile; extern int dbenabled; struct zfnfp * zz; if ((zz = zfnqfp(p,CKMAXPATH,tmpbuf))) { char *s, *s2 = NULL; makestr(&dbdir,zz->fpath); makestr(&dbfile,zz->fpath); for (s = dbdir; *s; s++) { if (ISDIRSEP(*s)) s2 = s+1; } if (s2) *s2 = NUL; debug(F110,"XA_DBFI dbdir",dbdir,0); debug(F110,"XA_DBFI dbfile",dbfile,0); dbenabled = 1; } break; } case XA_DBAS: { extern int dbenabled; y = lookup(oktab,p,noktab,&z); if (y < 0) return(-1); dbenabled = y; break; } #endif /* IKSDB */ case XA_VERS: { extern char * ck_s_ver, * ck_s_xver; printf("%s",ck_s_ver); if (*ck_s_xver) printf(" [%s]\n",ck_s_xver); printf("\n"); if (stayflg || what == W_COMMAND) break; else doexit(GOOD_EXIT,-1); } #ifndef NOXFER #ifdef CK_PERMS case XA_NPRM: { extern int atlpri, atlpro, atgpri, atgpro; atlpri = 0; atlpro = 0; atgpri = 0; atgpro = 0; break; } #endif /* CK_PERMS */ #endif /* NOXFER */ #ifdef KUI case XA_SCALE: kui_init.resizeMode = 1; break; case XA_CHGD: kui_init.resizeMode = 2; break; case XA_WMAX: kui_init.nCmdShow = SW_MAXIMIZE; break; case XA_WMIN: kui_init.nCmdShow = SW_MINIMIZE; break; case XA_XPOS: if (!rdigits(p)) return(-1); kui_init.pos_init++; kui_init.pos_x = atoi(p); break; case XA_YPOS: if (!rdigits(p)) return(-1); kui_init.pos_init++; kui_init.pos_y = atoi(p); break; case XA_FNAM: { extern struct _kui_init kui_init; extern struct keytab * term_font; extern struct keytab * _term_font; extern int tt_font, ntermfont; int x, z; if (ntermfont == 0) BuildFontTable(&term_font, &_term_font, &ntermfont); if (!(term_font && _term_font && ntermfont > 0)) { printf("?Unable to construct Font Facename Table\n"); return(0); } x = lookup(term_font,p,ntermfont,&z); if (x < 0) { x = lookup(_term_font,p,ntermfont,&z); if (x < 0) { printf("?Invalid Font Facename: %s\n",p); return(0); } } tt_font = x; kui_init.face_init++; makestr(&kui_init.facename,term_font[z].kwd); break; } case XA_FSIZ: { extern struct _kui_init kui_init; extern int tt_font_size; char * q; int halfpoint = 0; kui_init.font_init++; for ( q=p ; *q ; q++ ) { if ( *q == '.') { *q++ = '\0'; if (!rdigits(q)) return(-1); if (!*q || atoi(q) == 0) break; /* no halfpoint */ halfpoint = 1; if (atoi(q) != 5) printf("? Font sizes are treated in half-point increments\n"); break; } } if (!rdigits(p)) return(-1); tt_font_size = kui_init.font_size = 2 * atoi(p) + halfpoint; break; } case XA_NOMN: kui_init.nomenubar = 1; break; case XA_NOTB: kui_init.notoolbar = 1; break; case XA_NOSB: kui_init.nostatusbar = 1; break; case XA_NOBAR: kui_init.nomenubar = 1; kui_init.notoolbar = 1; kui_init.nostatusbar = 1; break; #endif /* KUI */ #ifndef NOPUSH case XA_NOPUSH: nopush = 1; break; #endif /* NOPUSH */ #ifdef OS2 case XA_LOCK: tt_scroll = 0; tt_escape = 0; #ifndef NOPUSH nopush = 1; #endif #ifdef KUI kui_init.nomenubar = 1; kui_init.notoolbar = 1; kui_init.nostatusbar = 1; #endif break; #ifdef KUI case XA_NOCLOSE: kui_init.noclose = 1; break; #endif /* KUI */ case XA_NOSCROLL: tt_scroll = 0; break; case XA_NOESCAPE: tt_escape = 0; break; #endif /* OS2 */ #ifndef NOLOCAL case XA_TERM: { /* Terminal type */ extern struct keytab ttyptab[]; extern int nttyp; #ifdef TNCODE extern char * tn_term; #endif /* TNCODE */ #ifdef OS2 int x, z; extern int tt_type, tt_type_mode; x = lookup(ttyptab,p,nttyp,&z); if (x < 0) return(-1); tt_type_mode = tt_type = x; #endif /* OS2 */ #ifdef TNCODE makestr(&tn_term,p); #endif /* TNCODE */ break; } case XA_CSET: { /* Remote Character Set */ #ifndef NOCSETS #ifdef CKOUNI extern struct keytab txrtab[]; extern int ntxrtab; x = lookup(txrtab,p,ntxrtab,&z); #else /* CKOUNI */ extern struct keytab ttcstab[]; extern int ntermc; x = lookup(ttcstab,p,ntermc,&z); #endif /* CKOUNI */ if (x < 0) return(-1); setremcharset(z,4 /* TT_GR_ALL (in ckuus7.c) */); #else /* NOCSETS */ return(-1); #endif /* NOCSETS */ break; } case XA_ROWS: { /* Screen rows (height) */ #ifdef OS2 extern int row_init; #else /* OS2 */ extern int tt_rows; #endif /* OS2 */ if (!rdigits(p)) return(-1); #ifdef OS2 if (!os2_settermheight(atoi(p))) return(-1); row_init++; #else /* Not OS/2 */ tt_rows = atoi(p); #endif /* OS2 */ break; } case XA_COLS: { /* Screen columns (width) */ #ifdef OS2 extern int col_init; #else /* OS2 */ extern int tt_cols; #endif /* OS2 */ if (!rdigits(p)) return(-1); #ifdef OS2 if (!os2_settermwidth(atoi(p))) return(-1); col_init++; #else /* Not OS/2 */ tt_cols = atoi(p); #endif /* OS2 */ break; } #ifdef OS2 case XA_TITL: { extern char usertitle[]; ckstrncpy(usertitle,p,64); os2settitle("",1); break; } #endif /* OS2 */ #ifdef COMMENT /* TO BE FILLED IN ... */ case XA_TEL: /* Make a Telnet connection */ case XA_FTP: /* Make an FTP connection */ case XA_SSH: /* Make an SSH connection */ #endif /* COMMENT */ #ifndef NOSPL case XA_USER: /* Username for login */ #ifdef IKSD if (!inserver) #endif /* IKSD */ { ckstrncpy(uidbuf,*xargv,UIDBUFLEN); haveftpuid = 1; } break; #endif /* NOSPL */ #endif /* NOLOCAL */ default: return(-1); } return(0); } #ifdef IKSD #ifdef IKSDCONF #define IKS_ANON 0 #define IKS_BAFI 1 #define IKS_CDFI 2 #define IKS_CDMS 3 #define IKS_HEFI 4 #define IKS_ANFI 5 #define IKS_USFI 6 #define IKS_IKLG 7 #define IKS_IKFI 8 #define IKS_DBAS 9 #define IKS_DBFI 10 #define IKS_PERM 11 #define IKS_PRIV 12 #define IKS_ROOT 13 #define IKS_TIMO 14 #define IKS_WTFI 15 #define IKS_WTMP 16 #define IKS_SRVR 17 #define IKS_NOIN 18 #define IKS_INIT 19 #define IKS_ANLG 20 #define IKS_ACCT 21 #define IKS_NTDOM 22 #define IKS_SYSL 23 #ifdef CK_LOGIN static struct keytab iksantab[] = { #ifdef OS2 { "account", IKS_ACCT, 0 }, #endif /* OS2 */ { "initfile", IKS_ANFI, 0 }, { "login", IKS_ANLG, 0 }, #ifdef UNIX { "root", IKS_ROOT, 0 }, #else #ifdef CKROOT { "root", IKS_ROOT, 0 }, #endif /* CKROOT */ #endif /* UNIX */ { "", 0, 0 } }; static int niksantab = sizeof(iksantab) / sizeof(struct keytab) - 1; #endif /* CK_LOGIN */ static struct keytab ikstab[] = { #ifdef CK_LOGIN { "anonymous", IKS_ANON, 0 }, #endif /* CK_LOGIN */ { "bannerfile", IKS_BAFI, 0 }, { "cdfile", IKS_CDFI, 0 }, { "cdmessage", IKS_CDMS, 0 }, { "cdmsg", IKS_CDMS, CM_INV }, #ifdef IKSDB { "database", IKS_DBAS, 0 }, { "dbfile", IKS_DBFI, 0 }, #endif /* IKSDB */ #ifdef CK_LOGIN #ifdef NT { "default-domain", IKS_NTDOM, 0 }, #endif /* NT */ #endif /* CK_LOGIN */ #ifndef NOHELP { "helpfile", IKS_HEFI, 0 }, #endif /* NOHELP */ { "initfile", IKS_INIT, 0 }, { "no-initfile", IKS_NOIN, 0 }, #ifdef CK_LOGIN #ifdef CK_PERM { "permissions", IKS_PERM, 0 }, { "perms", IKS_PERM, CM_INV }, #endif /* CK_PERM */ #ifdef UNIX { "privid", IKS_PRIV, 0 }, #endif /* UNIX */ { "server-only", IKS_SRVR, 0 }, #ifdef CKSYSLOG { "syslog", IKS_SYSL, 0 }, #endif /* CKSYSLOG */ { "timeout", IKS_TIMO, 0 }, { "userfile", IKS_USFI, 0 }, #ifdef CKWTMP { "wtmpfile", IKS_WTFI, 0 }, { "wtmplog", IKS_WTMP, 0 }, #endif /* CKWTMP */ #endif /* CK_LOGIN */ { "xferfile", IKS_IKFI, 0 }, { "xferlog", IKS_IKLG, 0 } }; static int nikstab = sizeof(ikstab) / sizeof(struct keytab); #endif /* IKSDCONF */ #ifndef NOICP int setiks() { /* SET IKS */ #ifdef IKSDCONF #ifdef CK_LOGIN extern int ckxsyslog, ckxwtmp, ckxanon; #ifdef UNIX extern int ckxpriv; #endif /* UNIX */ #ifdef CK_PERMS extern int ckxperms; #endif /* CK_PERMS */ extern char * anonfile, * userfile, * anonroot; #ifdef OS2 extern char * anonacct; #endif /* OS2 */ #ifdef NT extern char * iks_domain; #endif /* NT */ #endif /* CK_LOGIN */ #ifdef CKWTMP extern char * wtmpfile; #endif /* CKWTMP */ extern int srvcdmsg, success, iksdcf, rcflag, noinit, arg_x; extern char * cdmsgfile[], * cdmsgstr, *kermrc; extern xx_strp xxstring; int x, y, z; char *s; char tmpbuf[CKMAXPATH+1]; if ((y = cmkey(ikstab,nikstab,"","",xxstring)) < 0) return(y); #ifdef CK_LOGIN if (y == IKS_ANON) { if ((y = cmkey(iksantab,niksantab,"","",xxstring)) < 0) return(y); } #endif /* CK_LOGIN */ switch (y) { #ifdef CKSYSLOG case IKS_SYSL: /* IKS: Syslog level */ if ((z = cmkey(oktab,noktab,"","",xxstring)) < 0) return(z); if ((x = cmcfm()) < 0) return(x); if (iksdcf) return(success = 0); #ifndef SYSLOGLEVEL /* If specified on cc command line, user can't change it. */ if (!inserver) /* Don't allow voluminous syslogging */ if (y > SYSLG_FA) /* by ordinary users. */ y = SYSLG_FA; #endif /* SYSLOGLEVEL */ if (y < 0) return(-1); #ifdef DEBUG if (y >= SYSLG_DB) if (!deblog) deblog = debopn("debug.log",0); #endif /* DEBUG */ #ifdef SYSLOGLEVEL /* If specified on cc command line, user can't change it. */ y = SYSLOGLEVEL; #endif /* SYSLOGLEVEL */ ckxsyslog = y; /* printf("ckxsyslog=%d\n",ckxsyslog); */ break; #endif /* CKSYSLOG */ #ifdef CK_LOGIN #ifdef NT case IKS_NTDOM: if ((z = cmtxt( "DOMAIN to be used for user authentication when none is specified", "", &s,xxstring)) < 0) return(z); if (iksdcf) return(success = 0); if (!*s) s= NULL; makestr(&iks_domain,s); break; #endif /* NT */ #ifdef OS2 case IKS_ACCT: if ((z = cmtxt("Name of local account to use for anonymous logins", "GUEST", &s,xxstring)) < 0) return(z); if (iksdcf) return(success = 0); if (*s) { makestr(&anonacct,s); } else if ( anonacct ) { free(anonacct); anonacct = NULL; } break; #endif /* OS2 */ case IKS_ANLG: if ((z = cmkey(oktab,noktab,"","no",xxstring)) < 0) return(z); if ((x = cmcfm()) < 0) return(x); if (iksdcf) return(success = 0); ckxanon = z; #ifdef OS2 if (ckxanon && !anonacct) makestr(&anonacct,"GUEST"); #endif /* OS2 */ break; #endif /* CK_LOGIN */ case IKS_BAFI: if ((z = cmifi("Filename","",&s,&x,xxstring)) < 0) return(z); if (x) { printf("?Wildcards not allowed\n"); return(-9); } debug(F110,"bannerfile before zfnqfp()",s,0); if (zfnqfp(s,CKMAXPATH,tmpbuf)) { debug(F110,"bannerfile after zfnqfp()",tmpbuf,0); s = tmpbuf; } if ((x = cmcfm()) < 0) return(x); if (iksdcf) return(success = 0); if (*s) makestr(&bannerfile,s); break; case IKS_CDFI: if ((z = cmtxt("list of cd message file names","READ.ME", &s,xxstring)) < 0) return(z); if (iksdcf) return(success = 0); if (*s) { makelist(s,cdmsgfile,16); makestr(&cdmsgstr,s); } break; case IKS_CDMS: if ((z = cmkey(oktab,noktab,"","no",xxstring)) < 0) return(z); if ((x = cmcfm()) < 0) return(x); if (iksdcf) return(success = 0); srvcdmsg = z; break; case IKS_HEFI: if ((z = cmifi("Filename","",&s,&x,xxstring)) < 0) return(z); if (x) { printf("?Wildcards not allowed\n"); return(-9); } if (zfnqfp(s,CKMAXPATH,tmpbuf)) s = tmpbuf; if ((x = cmcfm()) < 0) return(x); if (iksdcf) return(success = 0); if (*s) makestr(&helpfile,s); break; case IKS_ANFI: if ((z = cmifi("Filename","",&s,&x,xxstring)) < 0) return(z); if (x) { printf("?Wildcards not allowed\n"); return(-9); } if (zfnqfp(s,CKMAXPATH,tmpbuf)) s = tmpbuf; if ((x = cmcfm()) < 0) return(x); if (iksdcf) return(success = 0); if (*s) makestr(&anonfile,s); break; case IKS_USFI: if ((z = cmifi("Filename","",&s,&x,xxstring)) < 0) return(z); if (x) { printf("?Wildcards not allowed\n"); return(-9); } if (zfnqfp(s,CKMAXPATH,tmpbuf)) s = tmpbuf; if ((x = cmcfm()) < 0) return(x); if (iksdcf) return(success = 0); if (*s) makestr(&userfile,s); break; case IKS_IKFI: if ((z = cmifi("Filename","",&s,&x,xxstring)) < 0) return(z); if (x) { printf("?Wildcards not allowed\n"); return(-9); } if (zfnqfp(s,CKMAXPATH,tmpbuf)) s = tmpbuf; if ((x = cmcfm()) < 0) return(x); if (iksdcf) return(success = 0); if (*s) { makestr(&xferfile,s); xferlog = 1; } break; case IKS_IKLG: if ((z = cmkey(oktab,noktab,"","no",xxstring)) < 0) return(z); if ((x = cmcfm()) < 0) return(x); if (iksdcf) return(success = 0); xferlog = z; break; #ifdef CK_LOGIN #ifdef CK_PERM case IKS_PERM: if ((z = cmtxt("Octal file permssion code","000", &s,xxstring)) < 0) return(z); if (z < 0) return(z); if (iksdcf) return(success = 0); y = 0; while (*s) { if (*s < '0' || *s > '7') return(-9); y = y * 8 + (*s++ - '0'); } ckxperms = y; break; #endif /* CK_PERM */ #ifdef UNIX case IKS_PRIV: /* IKS: Priv'd login allowed */ if ((z = cmkey(oktab,noktab,"","no",xxstring)) < 0) return(z); if ((x = cmcfm()) < 0) return(x); if (iksdcf) return(success = 0); ckxpriv = z; break; #endif /* UNIX */ case IKS_ROOT: /* IKS: Anonymous root */ if ((z = cmdir("Name of disk and/or directory","",&s, xxstring)) < 0 ) { if (z != -3) return(z); } if (*s) { if (zfnqfp(s,CKMAXPATH,tmpbuf)) s = tmpbuf; } else s = ""; if ((x = cmcfm()) < 0) return(x); if (iksdcf) return(success = 0); if (*s) makestr(&anonroot,s); /* printf("anonroot=%s\n",anonroot); */ break; case IKS_TIMO: z = cmnum("login timeout, seconds","0",10,&x,xxstring); if (z < 0) return(z); if (x < 0 || x > 7200) { printf("?Value must be between 0 and 7200\r\n"); return(-9); } if ((z = cmcfm()) < 0) return(z); if (iksdcf) return(success = 0); logintimo = x; break; #ifdef CKWTMP case IKS_WTMP: /* IKS: wtmp log */ if ((z = cmkey(oktab,noktab,"","no",xxstring)) < 0) return(z); if ((x = cmcfm()) < 0) return(x); if (iksdcf) return(success = 0); ckxwtmp = z; break; case IKS_WTFI: /* IKS: wtmp logfile */ if ((z = cmifi("Filename","",&s,&x,xxstring)) < 0) return(z); if (x) { printf("?Wildcards not allowed\n"); return(-9); } if (zfnqfp(s,CKMAXPATH,tmpbuf)) s = tmpbuf; if ((x = cmcfm()) < 0) return(x); if (iksdcf) return(success = 0); if (*s) makestr(&wtmpfile,s); break; #endif /* CKWTMP */ #endif /* CK_LOGIN */ #ifdef IKSDB case IKS_DBFI: { extern char * dbdir, * dbfile; extern int dbenabled; struct zfnfp * zz; if ((z = cmifi("Filename","",&s,&x,xxstring)) < 0) return(z); if (x) { printf("?Wildcards not allowed\n"); return(-9); } zz = zfnqfp(s,CKMAXPATH,tmpbuf); if ((x = cmcfm()) < 0) return(x); if (iksdcf) return(success = 0); if (zz) { makestr(&dbdir,zz->fpath); makestr(&dbfile,(char *)tmpbuf); dbenabled = 1; } else return(success = 0); break; } case IKS_DBAS: { extern int dbenabled; if ((z = cmkey(oktab,noktab,"","no",xxstring)) < 0) return(z); if ((x = cmcfm()) < 0) return(x); if (iksdcf) return(success = 0); dbenabled = z; break; } #endif /* IKSDB */ case IKS_INIT: if ((z = cmtxt("Alternate init file specification","", &s,xxstring)) < 0) return(z); if (z < 0) return(z); if (iksdcf) return(success = 0); ckstrncpy(kermrc,s,KERMRCL); rcflag = 1; /* Flag that this has been done */ break; case IKS_NOIN: if ((z = cmkey(oktab,noktab,"","no",xxstring)) < 0) return(z); if ((x = cmcfm()) < 0) return(x); if (iksdcf) return(success = 0); noinit = z; break; case IKS_SRVR: if ((z = cmkey(oktab,noktab,"","no",xxstring)) < 0) return(z); if ((x = cmcfm()) < 0) return(x); if (iksdcf) return(success = 0); arg_x = z; break; default: return(-9); } return(success = (inserver ? 1 : 0)); #else /* IKSDCONF */ if ((x = cmcfm()) < 0) return(x); return(success = 0); #endif /* IKSDCONF */ } #endif /* NOICP */ #endif /* IKSD */ /* D O A R G -- Do a command-line argument. */ int #ifdef CK_ANSIC doarg(char x) #else doarg(x) char x; #endif /* CK_ANSIC */ /* doarg */ { int i, n, y, z, xx; long zz; char *xp; #ifdef NETCONN extern char *line, *tmpbuf; /* Character buffers for anything */ #endif /* NETCONN */ #ifdef IKSD /* Internet Kermit Server set some way besides -A... */ if (inserver) dofast(); #endif /* IKSD */ xp = *xargv+1; /* Pointer for bundled args */ debug(F111,"doarg entry",xp,xargc); while (x) { debug(F000,"doarg arg","",x); switch (x) { /* Big switch on arg */ #ifndef COMMENT case '-': /* Extended commands... */ if (doxarg(xargv,0) < 0) { XFATAL("Extended option error"); } /* Full thru... */ case '+': /* Extended command for prescan() */ return(0); #else /* NOICP */ case '-': case '+': XFATAL("Extended options not configured"); #endif /* NOICP */ #ifndef NOSPL case 'C': { /* Commands for parser */ char * s; xargv++, xargc--; if ((xargc < 1) || (**xargv == '-')) { XFATAL("No commands given for -C"); } s = *xargv; /* Get the argument (must be quoted) */ if (!*s) /* If empty quotes */ s = NULL; /* ignore this option */ if (s) { makestr(&clcmds,s); /* Make pokeable copy */ s = clcmds; /* Change tabs to spaces */ while (*s) { if (*s == '\t') *s = ' '; s++; } } break; } #endif /* NOSPL */ #ifndef NOXFER case 'D': /* Delay */ if (*(xp+1)) { XFATAL("invalid argument bundling"); } xargv++, xargc--; if ((xargc < 1) || (**xargv == '-')) { XFATAL("missing delay value"); } z = atoi(*xargv); /* Convert to number */ if (z > -1) /* If in range */ ckdelay = z; /* set it */ else { XFATAL("bad delay value"); } break; #endif /* NOXFER */ case 'E': /* Exit on close */ #ifdef NETCONN tn_exit = 1; #endif /* NETCONN */ exitonclose = 1; break; #ifndef NOICP case 'S': /* "Stay" - enter interactive */ stayflg = 1; /* command parser after executing */ xfinish = 0; /* command-line actions. */ break; #endif /* NOICP */ case 'T': /* File transfer mode = text */ binary = XYFT_T; xfermode = XMODE_M; /* Transfer mode manual */ filepeek = 0; #ifdef PATTERNS patterns = 0; #endif /* PATTERNS */ break; case '7': break; #ifdef IKSD case 'A': { /* Internet server */ /* Already done in prescan() */ /* but implies 'x' && 'Q' */ #ifdef OS2 char * p; if (*(xp+1)) { XFATAL("invalid argument bundling"); } #ifdef NT /* Support for Pragma Systems Telnet/Terminal Servers */ p = getenv("PRAGMASYS_INETD_SOCK"); if (!(p && atoi(p) != 0)) { xargv++, xargc--; if (xargc < 1 || **xargv == '-') { XFATAL("missing socket handle"); } } #else /* NT */ xargv++, xargc--; if (xargc < 1 || **xargv == '-') { XFATAL("missing socket handle"); } #endif /* NT */ #endif /* OS2 */ #ifdef NOICP /* If no Interactive Command Parser */ action = 'x'; /* -A implies -x. */ #endif /* NOICP */ #ifndef NOXFER dofast(); #endif /* NOXFER */ break; } #endif /* IKSD */ #ifndef NOXFER case 'Q': /* Quick (i.e. FAST) */ dofast(); break; #endif /* NOXFER */ case 'R': /* Remote-Only */ break; /* This is handled in prescan(). */ #ifndef NOSERVER case 'x': /* server */ case 'O': /* (for One command only) */ if (action) { XFATAL("conflicting actions"); } if (x == 'O') justone = 1; xfinish = 1; action = 'x'; break; #endif /* NOSERVER */ #ifndef NOXFER case 'f': /* finish */ if (action) { XFATAL("conflicting actions"); } action = setgen('F',"","",""); break; #endif /* NOXFER */ case 'r': { /* receive */ if (action) { XFATAL("conflicting actions"); } action = 'v'; break; } #ifndef NOXFER case 'k': /* receive to stdout */ if (action) { XFATAL("conflicting actions"); } stdouf = 1; action = 'v'; break; case 's': { /* send */ int fil2snd, rc; if (!recursive) nolinks = 0; /* Follow links by default */ if (action) { XFATAL("conflicting actions"); } if (*(xp+1)) { XFATAL("invalid argument bundling after -s"); } nfils = 0; /* Initialize file counter */ fil2snd = 0; /* Assume nothing to send */ z = 0; /* Flag for stdin */ cmlist = xargv + 1; /* Remember this pointer */ while (++xargv, --xargc > 0) { /* Traverse the list */ #ifdef PIPESEND if (usepipes && protocol == PROTO_K && **xargv == '!') { cmarg = *xargv; cmarg++; debug(F110,"doarg pipesend",cmarg,0); nfils = -1; z = 1; pipesend = 1; } else #endif /* PIPESEND */ if (**xargv == '-') { /* Check for sending stdin */ if (strcmp(*xargv,"-") != 0) /* next option? */ break; z++; /* "-" alone means send from stdin. */ #ifdef RECURSIVE } else if (!strcmp(*xargv,".")) { fil2snd = 1; nfils++; recursive = 1; nolinks = 2; #endif /* RECURSIVE */ } else /* Check if file exists */ if ((rc = zchki(*xargv)) > -1 || (rc == -2)) { if (rc != -2) fil2snd = 1; nfils++; /* Bump file counter */ } else if (iswild(*xargv) && nzxpand(*xargv,0) > 0) { /* or contains wildcard characters matching real files */ fil2snd = 1; nfils++; } else { if (!failmsg) failmsg = (char *)malloc(2000); if (failmsg) { ckmakmsg(failmsg,2000, #ifdef VMS "%CKERMIT-E-SEARCHFAIL " #else "kermit -s " #endif /* VMS */ , *xargv, ": ", ck_errstr() ); } } } xargc++, xargv--; /* Adjust argv/argc */ if (!fil2snd && z == 0) { if (!failmsg) { #ifdef VMS failmsg = "%CKERMIT-E-SEARCHFAIL, no files for -s"; #else failmsg = "No files for -s"; #endif /* VMS */ } XFATAL(failmsg); } if (z > 1) { XFATAL("kermit -s: too many -'s"); } if (z == 1 && fil2snd) { XFATAL("invalid mixture of filenames and '-' in -s"); } debug(F101,"doarg s nfils","",nfils); debug(F101,"doarg s z","",z); if (nfils == 0) { /* no file parameters were specified */ if (is_a_tty(0)) { /* (used to be is_a_tty(1) - why?) */ XFATAL("sending from terminal not allowed"); } else stdinf = 1; } debug(F101,"doarg s stdinf","",stdinf); debug(F111,"doarg",*xargv,nfils); action = 's'; break; } case 'g': /* get */ case 'G': /* get to stdout */ if (action) { XFATAL("conflicting actions"); } if (*(xp+1)) { XFATAL("invalid argument bundling after -g"); } xargv++, xargc--; if ((xargc == 0) || (**xargv == '-')) { XFATAL("missing filename for -g"); } if (x == 'G') stdouf = 1; cmarg = *xargv; action = 'r'; break; #endif /* NOXFER */ #ifndef NOLOCAL case 'c': /* connect before */ cflg = 1; break; case 'n': /* connect after */ cnflg = 1; break; #endif /* NOLOCAL */ case 'h': /* help */ usage(); #ifndef NOICP if (stayflg || what == W_COMMAND) break; else #endif /* NOICP */ doexit(GOOD_EXIT,-1); #ifndef NOXFER case 'a': /* "as" */ if (*(xp+1)) { XFATAL("invalid argument bundling after -a"); } xargv++, xargc--; if ((xargc < 1) || (**xargv == '-')) { XFATAL("missing name in -a"); } cmarg2 = *xargv; debug(F111,"doarg a",cmarg2,xargc); break; #endif /* NOXFER */ #ifndef NOICP case 'Y': /* No initialization file */ noinit = 1; break; case 'y': /* Alternate init-file name */ noinit = 0; if (*(xp+1)) { XFATAL("invalid argument bundling after -y"); } xargv++, xargc--; if (xargc < 1) { XFATAL("missing filename in -y"); } /* strcpy(kermrc,*xargv); ... already done in prescan()... */ break; #endif /* NOICP */ #ifndef NOXFER case 'I': /* Assume we have an "Internet" */ reliable = 1; /* or other reliable connection */ xreliable = 1; setreliable = 1; /* I'm not so sure about this -- what about VMS? (next comment) */ clearrq = 1; /* therefore the channel is clear */ #ifndef VMS /* Since this can trigger full control-character unprefixing, we need to ensure that our terminal or pty driver is not doing Xon/Xoff; otherwise we can become deadlocked the first time we receive a file that contains Xoff. */ flow = FLO_NONE; #endif /* VMS */ break; #endif /* NOXFER */ #ifndef NOLOCAL case 'l': /* SET LINE */ #ifdef NETCONN #ifdef ANYX25 case 'X': /* SET HOST to X.25 address */ #ifdef SUNX25 case 'Z': /* SET HOST to X.25 file descriptor */ #endif /* SUNX25 */ #endif /* ANYX25 */ #ifdef TCPSOCKET case 'J': case 'j': /* SET HOST (TCP/IP socket) */ #endif /* TCPSOCKET */ #endif /* NETCONN */ #ifndef NOXFER if (x == 'j' || x == 'J' || x == 'X' || x == 'Z') { reliable = 1; /* or other reliable connection */ xreliable = 1; setreliable = 1; } #endif /* NOXFER */ network = 0; if (*(xp+1)) { XFATAL("invalid argument bundling after -l or -j"); } xargv++, xargc--; if ((xargc < 1) || (**xargv == '-')) { XFATAL("communication line device name missing"); } #ifdef NETCONN if (x == 'J') { cflg = 1; /* Connect */ stayflg = 1; /* Stay */ tn_exit = 1; /* Telnet-like exit condition */ exitonclose = 1; } #endif /* NETCONN */ ckstrncpy(ttname,*xargv,TTNAMLEN+1); local = (strcmp(ttname,CTTNAM) != 0); if (local && strcmp(ttname,"0") == 0) local = 0; /* NOTE: We really do not need to call ttopen here, since it should be called again later, automatically, when we first try to condition the device via ttpkt or ttvt. Calling ttopen here has the bad side effect of making the order of the -b and -l options significant when the order of command-line options should not matter. However, the network cases immediately below complicate matters a bit, so we'll settle this in a future edit. */ if (x == 'l') { if (ttopen(ttname,&local,mdmtyp,0) < 0) { XFATAL("can't open device"); } #ifdef CKLOGDIAL dologline(); #endif /* CKLOGDIAL */ debug(F101,"doarg speed","",speed); cxtype = (mdmtyp > 0) ? CXT_MODEM : CXT_DIRECT; speed = ttgspd(); /* Get the speed. */ setflow(); /* Do something about flow control. */ #ifndef NOSPL if (local) { if (nmac) { /* Any macros defined? */ int k; /* Yes */ k = mlook(mactab,"on_open",nmac); /* Look this up */ if (k >= 0) { /* If found, */ if (dodo(k,ttname,0) > -1) /* set it up, */ parser(1); /* and execute it */ } } } #endif /* NOSPL */ #ifdef NETCONN } else { if (x == 'j' || x == 'J') { /* IP network host name */ char * s = line; char * service = tmpbuf; if (xargc > 0) { /* Check if it's followed by */ /* A service name or number */ if (*(xargv+1) && *(*(xargv+1)) != '-') { xargv++, xargc--; ckstrncat(ttname,":",TTNAMLEN+1); ckstrncat(ttname,*xargv,TTNAMLEN+1); } } nettype = NET_TCPB; mdmtyp = -nettype; /* Perhaps already set in init file */ telnetfd = 1; /* Or maybe an open file descriptor */ ckstrncpy(line, ttname, LINBUFSIZ); /* Working copy */ for (s = line; *s != NUL && *s != ':'; s++); if (*s) { *s++ = NUL; ckstrncpy(service, s, TMPBUFSIZ); } else *service = NUL; s = line; #ifndef NODIAL #ifndef NOICP /* Look up in network directory */ x = 0; if (*s == '=') { /* If number starts with = sign */ s++; /* strip it */ while (*s == SP) /* and also any leading spaces */ s++; ckstrncpy(line,s,LINBUFSIZ); /* Do this again. */ nhcount = 0; } else if (!isdigit(line[0])) { /* nnetdir will be greater than 0 if the init file has been processed and it contained a SET NETWORK DIRECTORY command. */ xx = 0; /* Initialize this */ if (nnetdir > 0) /* If there is a directory... */ xx = lunet(line); /* Look up the name */ else /* If no directory */ nhcount = 0; /* we didn't find anything there */ if (xx < 0) { /* Lookup error: */ ckmakmsg(tmpbuf, TMPBUFSIZ, "?Fatal network directory lookup error - ", line, "\n", NULL ); XFATAL(tmpbuf); } } #endif /* NOICP */ #endif /* NODIAL */ /* Add service to line specification for ttopen() */ if (*service) { /* There is a service specified */ ckstrncat(line, ":",LINBUFSIZ); ckstrncat(line, service,LINBUFSIZ); ttnproto = NP_DEFAULT; } else { ckstrncat(line, ":telnet",LINBUFSIZ); ttnproto = NP_TELNET; } #ifndef NOICP #ifndef NODIAL if ((nhcount > 1) && !quiet && !backgrd) { printf("%d entr%s found for \"%s\"%s\n", nhcount, (nhcount == 1) ? "y" : "ies", s, (nhcount > 0) ? ":" : "." ); for (i = 0; i < nhcount; i++) printf("%3d. %s %-12s => %s\n", i+1, n_name, nh_p2[i], nh_p[i] ); } if (nhcount == 0) n = 1; else n = nhcount; #else n = 1; nhcount = 0; #endif /* NODIAL */ for (i = 0; i < n; i++) { #ifndef NODIAL if (nhcount >= 1) { /* Copy the current entry to line */ ckstrncpy(line,nh_p[i],LINBUFSIZ); /* Check to see if the network entry contains a service */ for (s = line ; (*s != NUL) && (*s != ':'); s++) ; /* If directory does not have a service ... */ /* and the user specified one */ if (!*s && *service) { ckstrncat(line, ":",LINBUFSIZ); ckstrncat(line, service,LINBUFSIZ); } if (lookup(netcmd,nh_p2[i],nnets,&z) > -1) { mdmtyp = 0 - netcmd[z].kwval; } else { printf( "Error - network type \"%s\" not supported\n", nh_p2[i] ); continue; } } #endif /* NODIAL */ } #endif /* NOICP */ ckstrncpy(ttname, line,TTNAMLEN+1); cxtype = CXT_TCPIP; /* Set connection type */ setflow(); /* Set appropriate flow control. */ #ifdef SUNX25 } else if (x == 'X') { /* X.25 address */ nettype = NET_SX25; mdmtyp = -nettype; } else if (x == 'Z') { /* Open X.25 file descriptor */ nettype = NET_SX25; mdmtyp = -nettype; x25fd = 1; #endif /* SUNX25 */ #ifdef STRATUSX25 } else if (x == 'X') { /* X.25 address */ nettype = NET_VX25; mdmtyp = -nettype; #endif /* STRATUSX25 */ #ifdef IBMX25 } else if (x == 'X') { /* X.25 address */ nettype = NET_IX25; mdmtyp = -nettype; #endif /* IBMX25 */ #ifdef HPX25 } else if (x == 'X') { /* X.25 address */ nettype = NET_HX25; mdmtyp = -nettype; #endif /* HPX25 */ } if (ttopen(ttname,&local,mdmtyp,0) < 0) { XFATAL("can't open host connection"); } network = 1; #ifdef CKLOGDIAL dolognet(); #endif /* CKLOGDIAL */ cxtype = CXT_X25; /* Set connection type */ setflow(); /* Set appropriate flow control. */ #ifndef NOSPL if (local) { if (nmac) { /* Any macros defined? */ int k; /* Yes */ k = mlook(mactab,"on_open",nmac); /* Look this up */ if (k >= 0) { /* If found, */ if (dodo(k,ttname,0) > -1) /* set it up, */ parser(1); /* and execute it */ } } } #endif /* NOSPL */ #endif /* NETCONN */ } /* add more here -- decnet, etc... */ haveline = 1; break; #ifdef ANYX25 case 'U': /* X.25 call user data */ if (*(xp+1)) { XFATAL("invalid argument bundling"); } xargv++, xargc--; if ((xargc < 1) || (**xargv == '-')) { XFATAL("missing call user data string"); } ckstrncpy(udata,*xargv,MAXCUDATA); if ((int)strlen(udata) <= MAXCUDATA) { cudata = 1; } else { XFATAL("Invalid call user data"); } break; case 'o': /* X.25 closed user group */ if (*(xp+1)) { XFATAL("invalid argument bundling"); } xargv++, xargc--; if ((xargc < 1) || (**xargv == '-')) { XFATAL("missing closed user group index"); } z = atoi(*xargv); /* Convert to number */ if (z >= 0 && z <= 99) { closgr = z; } else { XFATAL("Invalid closed user group index"); } break; case 'u': /* X.25 reverse charge call */ revcall = 1; break; #endif /* ANYX25 */ #endif /* NOLOCAL */ case 'b': /* Bits-per-second for serial device */ if (*(xp+1)) { XFATAL("invalid argument bundling"); } xargv++, xargc--; if ((xargc < 1) || (**xargv == '-')) { XFATAL("missing bps"); } zz = atol(*xargv); /* Convert to long int */ i = zz / 10L; #ifndef NOLOCAL if (ttsspd(i) > -1) /* Check and set it */ #endif /* NOLOCAL */ speed = ttgspd(); /* and read it back. */ #ifndef NOLOCAL else { XFATAL("unsupported transmission rate"); } #endif /* NOLOCAL */ break; #ifndef NODIAL #ifndef NOICP case 'm': /* Modem type */ if (*(xp+1)) { XFATAL("invalid argument bundling after -m"); } xargv++, xargc--; if ((xargc < 1) || (**xargv == '-')) { XFATAL("modem type missing"); } y = lookup(mdmtab,*xargv,nmdm,&z); if (y < 0) { XFATAL("unknown modem type"); } usermdm = 0; usermdm = (y == dialudt) ? x : 0; initmdm(y); break; #endif /* NOICP */ #endif /* NODIAL */ #ifndef NOXFER case 'e': /* Extended packet length */ if (*(xp+1)) { XFATAL("invalid argument bundling after -e"); } xargv++, xargc--; if ((xargc < 1) || (**xargv == '-')) { XFATAL("missing length"); } z = atoi(*xargv); /* Convert to number */ if (z > 10 && z <= maxrps) { rpsiz = urpsiz = z; if (z > 94) rpsiz = 94; /* Fallback if other Kermit can't */ } else { XFATAL("Unsupported packet length"); } break; case 'v': /* Vindow size */ if (*(xp+1)) { XFATAL("invalid argument bundling"); } xargv++, xargc--; if ((xargc < 1) || (**xargv == '-')) { XFATAL("missing or bad window size"); } z = atoi(*xargv); /* Convert to number */ if (z < 32) { /* If in range */ wslotr = z; /* set it */ if (z > 1) swcapr = 1; /* Set capas bit if windowing */ } else { XFATAL("Unsupported packet length"); } break; #endif /* NOXFER */ case 'i': /* Treat files as binary */ binary = XYFT_B; xfermode = XMODE_M; /* Transfer mode manual */ filepeek = 0; #ifdef PATTERNS patterns = 0; #endif /* PATTERNS */ break; #ifndef NOXFER case 'w': /* Writeover */ ckwarn = 0; fncact = XYFX_X; break; #endif /* NOXFER */ case 'q': /* Quiet */ quiet = 1; break; #ifdef DEBUG case 'd': /* DEBUG */ break; /* Handled in prescan() */ #endif /* DEBUG */ case '0': { /* In the middle */ extern int tt_escape, lscapr; tt_escape = 0; /* No escape character */ flow = 0; /* No Xon/Xoff (what about hwfc?) */ #ifndef NOXFER lscapr = 0; /* No locking shifts */ #endif /* NOXFER */ #ifdef CK_APC { extern int apcstatus; /* No APCs */ apcstatus = APC_OFF; } #endif /* CK_APC */ #ifndef NOLOCAL #ifdef CK_AUTODL setautodl(0,0); /* No autodownload */ #endif /* CK_AUTODL */ #endif /* NOLOCAL */ #ifndef NOCSETS { extern int tcsr, tcsl; /* No character-set translation */ tcsr = 0; tcsl = tcsr; /* Make these equal */ } #endif /* NOCSETS */ #ifdef TNCODE TELOPT_DEF_C_U_MODE(TELOPT_KERMIT) = TN_NG_RF; TELOPT_DEF_C_ME_MODE(TELOPT_KERMIT) = TN_NG_RF; TELOPT_DEF_S_U_MODE(TELOPT_KERMIT) = TN_NG_RF; TELOPT_DEF_S_ME_MODE(TELOPT_KERMIT) = TN_NG_RF; #endif /* TNCODE */ } /* Fall thru... */ case '8': /* 8-bit clean */ parity = 0; cmdmsk = 0xff; cmask = 0xff; break; case 'V': { extern int xfermode; #ifdef PATTERNS extern int patterns; patterns = 0; /* No patterns */ #endif /* PATTERNS */ xfermode = XMODE_M; /* Manual transfer mode */ filepeek = 0; break; } case 'p': /* SET PARITY */ if (*(xp+1)) { XFATAL("invalid argument bundling"); } xargv++, xargc--; if ((xargc < 1) || (**xargv == '-')) { XFATAL("missing parity"); } switch(x = **xargv) { case 'e': case 'o': case 'm': case 's': parity = x; break; case 'n': parity = 0; break; default: { XFATAL("invalid parity"); } } break; case 't': /* Line turnaround handshake */ turn = 1; turnch = XON; /* XON is turnaround character */ duplex = 1; /* Half duplex */ flow = 0; /* No flow control */ break; case 'B': bgset = 1; /* Force background (batch) */ backgrd = 1; break; case 'z': /* Force foreground */ bgset = 0; backgrd = 0; break; #ifndef NOXFER #ifdef RECURSIVE case 'L': recursive = 2; nolinks = 2; fnspath = PATH_REL; break; #endif /* RECURSIVE */ #endif /* NOXFER */ #ifndef NOSPL case 'M': /* My User Name */ if (*(xp+1)) { XFATAL("invalid argument bundling"); } xargv++, xargc--; if ((xargc < 1) || (**xargv == '-')) { XFATAL("missing username"); } if ((int)strlen(*xargv) > 63) { XFATAL("username too long"); } #ifdef IKSD if (!inserver) #endif /* IKSD */ { ckstrncpy(uidbuf,*xargv,UIDBUFLEN); haveftpuid = 1; } break; #endif /* NOSPL */ #ifdef CK_NETBIOS case 'N': /* NetBios Adapter Number follows */ if (*(xp+1)) { XFATAL("invalid argument bundling after -N"); } xargv++, xargc--; if ((xargc < 1) || (**xargv == '-')) { XFATAL("missing NetBios Adapter number"); } if ((strlen(*xargv) != 1) || (*xargv)[0] != 'X' && (atoi(*xargv) < 0) && (atoi(*xargv) > 9)) { XFATAL("Invalid NetBios Adapter - Adapters 0 to 9 are valid"); } break; #endif /* CK_NETBIOS */ #ifdef NETCONN case 'F': network = 1; if (*(xp+1)) { XFATAL("invalid argument bundling after -F"); } xargv++, xargc--; if ((xargc < 1) || (**xargv == '-')) { XFATAL("network file descriptor missing"); } ckstrncpy(ttname,*xargv,TTNAMLEN+1); nettype = NET_TCPB; mdmtyp = -nettype; telnetfd = 1; local = 1; break; #endif /* NETCONN */ #ifdef COMMENT #ifdef OS2PM case 'P': /* OS/2 Presentation Manager */ if (*(xp+1)) { XFATAL("invalid argument bundling after -P"); } xargv++, xargc--; if ((xargc < 1) || (**xargv == '-')) { XFATAL("pipe data missing"); } pipedata = *xargv; break; #endif /* OS2PM */ #else case 'P': /* Filenames literal */ fncnv = XYFN_L; f_save = XYFN_L; break; #endif /* COMMENT */ #ifndef NOICP case 'H': noherald = 1; break; #endif /* NOICP */ #ifdef OS2 case 'W': if (*(xp+1)) { XFATAL("invalid argument bundling after -W"); } xargv++, xargc--; if ((xargc < 1)) { /* could be negative */ XFATAL("Window handle missing"); } xargv++, xargc--; if ((xargc < 1) || (**xargv == '-')) { XFATAL("Kermit Instance missing"); } /* Action done in prescan */ break; case '#': /* K95 stdio threads */ xargv++, xargc--; /* Skip past argument */ break; /* Action done in prescan */ #endif /* OS2 */ #ifdef NEWFTP case '9': /* FTP */ if (*(xp+1)) { XFATAL("invalid argument bundling after -9"); } xargv++, xargc--; if ((xargc < 1) || (**xargv == '-')) { XFATAL("FTP server address missing"); } makestr(&ftp_host,*xargv); break; #endif /* NEWFTP */ default: fatal2(*xargv, #ifdef NT "invalid command-line option, type \"k95 -h\" for help" #else #ifdef OS2 "invalid command-line option, type \"k2 -h\" for help" #else "invalid command-line option, type \"kermit -h\" for help" #endif /* OS2 */ #endif /* NT */ ); } if (!xp) break; x = *++xp; /* See if options are bundled */ } return(0); } #ifdef TNCODE /* D O T N A R G -- Do a telnet command-line argument. */ static int #ifdef CK_ANSIC dotnarg(char x) #else dotnarg(x) char x; #endif /* CK_ANSIC */ /* dotnarg */ { char *xp; xp = *xargv+1; /* Pointer for bundled args */ debug(F111,"dotnarg entry",xp,xargc); while (x) { debug(F000,"dotnarg arg","",x); switch (x) { /* Big switch on arg */ #ifndef COMMENT case '-': /* Extended commands... */ if (doxarg(xargv,0) < 0) { XFATAL("Extended option error"); } /* Full thru... */ case '+': /* Extended command for prescan() */ return(0); #else /* COMMENT */ case '-': case '+': XFATAL("Extended options not configured"); #endif /* COMMENT */ /* * -# Kermit 95 Startup Flags * -8 Negotiate Telnet Binary in both directions * -a Require use of Telnet authentication * -c Do not read the .telnetrc file * -d Turn on debug mode * -E No escape character * -f Forward credentials to host * -K Refuse use of authentication; do not send username * -k realm Set default realm * -l user Set username and request Telnet authentication * -L Negotiate Telnet Binary Output only * -q Quiet mode (suppress messages) * -S tos Use the IP type-of-service tos * -x Require Encryption * -D Disable forward-X * -T cert=file Use certificate in file * -T key=file Use private key in file * -T crlfile=file Use CRL in file * -T crldir=dir Use CRLs in directory * -T cipher=string Use only ciphers in string * -X atype Disable use of atype authentication * -Y Disable init file processing * */ case 'h': /* help */ usage(); doexit(GOOD_EXIT,-1); break; case '8': /* Telnet Binary in both directions */ TELOPT_DEF_C_U_MODE(TELOPT_BINARY) = TN_NG_MU; TELOPT_DEF_C_ME_MODE(TELOPT_BINARY) = TN_NG_MU; parity = 0; cmdmsk = 0xff; cmask = 0xff; break; case 'a': /* Require Telnet Auth */ TELOPT_DEF_C_ME_MODE(TELOPT_AUTHENTICATION) = TN_NG_MU; break; case 'Y': xargv++, xargc--; /* Skip past argument */ break; /* Action done in prescan */ #ifdef OS2 case '#': /* K95 stdio threads */ xargv++, xargc--; /* Skip past argument */ break; /* Action done in prescan */ #endif /* OS2 */ case 'q': /* Quiet */ quiet = 1; break; case 'd': #ifdef DEBUG if (deblog) { debtim = 1; } else { deblog = debopn("debug.log",0); } #endif /* DEBUG */ break; case 'E': { /* No Escape character */ extern int tt_escape; tt_escape = 0; } break; case 'K': TELOPT_DEF_C_ME_MODE(TELOPT_AUTHENTICATION) = TN_NG_RF; uidbuf[0] = NUL; break; case 'l': /* Set username and request telnet authentication */ if (*(xp+1)) { XFATAL("invalid argument bundling"); } xargv++, xargc--; if ((xargc < 1) || (**xargv == '-')) { XFATAL("missing username"); } if ((int)strlen(*xargv) > 63) { XFATAL("username too long"); } ckstrncpy(uidbuf,*xargv,UIDBUFLEN); TELOPT_DEF_C_ME_MODE(TELOPT_AUTHENTICATION) = TN_NG_MU; break; case 'L': /* Require BINARY mode outbound only */ TELOPT_DEF_C_ME_MODE(TELOPT_BINARY) = TN_NG_MU; break; case 'x': /* Require Encryption */ TELOPT_DEF_C_U_MODE(TELOPT_ENCRYPTION) = TN_NG_MU; TELOPT_DEF_C_ME_MODE(TELOPT_ENCRYPTION) = TN_NG_MU; break; case 'D': /* Disable use of Forward X */ TELOPT_DEF_C_U_MODE(TELOPT_FORWARD_X) = TN_NG_RF; break; case 'f': /* Forward credentials to host */ { #ifdef CK_AUTHENTICATION extern int forward_flag; forward_flag = 1; #endif break; } case 'k': { #ifdef CK_KERBEROS extern char * krb5_d_realm, * krb4_d_realm; #endif /* CK_KERBEROS */ if (*(xp+1)) { XFATAL("invalid argument bundling"); } xargv++, xargc--; if ((xargc < 1) || (**xargv == '-')) { XFATAL("missing realm"); } #ifdef CK_KERBEROS if ((int)strlen(*xargv) > 63) { XFATAL("realm too long"); } makestr(&krb5_d_realm,*xargv); makestr(&krb4_d_realm,*xargv); #endif /* CK_KERBEROS */ break; } case 'T': { if (*(xp+1)) { XFATAL("invalid argument bundling"); } xargv++, xargc--; if ((xargc < 1) || (**xargv == '-')) { XFATAL("missing cert=, key=, crlfile=, crldir=, or cipher="); } #ifdef CK_SSL if (!strncmp(*xargv,"cert=",5)) { extern char * ssl_rsa_cert_file; makestr(&ssl_rsa_cert_file,&(*xargv[5])); } else if ( !strncmp(*xargv,"key=",4) ) { extern char * ssl_rsa_key_file; makestr(&ssl_rsa_key_file,&(*xargv[4])); } else if ( !strncmp(*xargv,"crlfile=",8) ) { extern char * ssl_crl_file; makestr(&ssl_crl_file,&(*xargv[8])); } else if ( !strncmp(*xargv,"crldir=",7) ) { extern char * ssl_crl_dir; makestr(&ssl_crl_dir,&(*xargv[7])); } else if ( !strncmp(*xargv,"cipher=",7) ) { extern char * ssl_cipher_list; makestr(&ssl_cipher_list,&(*xargv[7])); } else { XFATAL("invalid parameter"); } #endif /* CK_SSL */ break; } default: fatal2(*xargv, "invalid command-line option, type \"telnet -h\" for help" ); } if (!xp) break; x = *++xp; /* See if options are bundled */ } return(0); } #endif /* TNCODE */ #ifdef RLOGCODE /* D O R L G A R G -- Do a rlogin command-line argument. */ static int #ifdef CK_ANSIC dorlgarg(char x) #else dorlgarg(x) char x; #endif /* CK_ANSIC */ /* dorlgarg */ { char *xp; xp = *xargv+1; /* Pointer for bundled args */ debug(F111,"dorlgarg entry",xp,xargc); while (x) { debug(F000,"dorlgarg arg","",x); switch (x) { /* Big switch on arg */ #ifndef COMMENT case '-': /* Extended commands... */ if (doxarg(xargv,0) < 0) { XFATAL("Extended option error"); } /* Full thru... */ case '+': /* Extended command for prescan() */ return(0); #else /* COMMENT */ case '-': case '+': XFATAL("Extended options not configured"); #endif /* COMMENT */ /* * -d Debug * -l user Set username * */ case 'h': /* help */ usage(); doexit(GOOD_EXIT,-1); break; case 'Y': xargv++, xargc--; /* Skip past argument */ break; /* Action done in prescan */ #ifdef OS2 case '#': /* K95 stdio threads */ xargv++, xargc--; /* Skip past argument */ break; /* Action done in prescan */ #endif /* OS2 */ case 'q': /* Quiet */ quiet = 1; break; case 'd': #ifdef DEBUG if (deblog) { debtim = 1; } else { deblog = debopn("debug.log",0); } #endif /* DEBUG */ break; case 'l': /* Set username and request telnet authentication */ if (*(xp+1)) { XFATAL("invalid argument bundling"); } xargv++, xargc--; if ((xargc < 1) || (**xargv == '-')) { XFATAL("missing username"); } if ((int)strlen(*xargv) > 63) { XFATAL("username too long"); } ckstrncpy(uidbuf,*xargv,UIDBUFLEN); break; default: fatal2(*xargv, "invalid command-line option, type \"rlogin -h\" for help" ); } if (!xp) break; x = *++xp; /* See if options are bundled */ } return(0); } #endif /* RLOGCODE */ #ifdef SSHBUILTIN /* D O S S H A R G -- Do a ssh command-line argument. */ static int #ifdef CK_ANSIC dossharg(char x) #else dossharg(x) char x; #endif /* CK_ANSIC */ /* dossharg */ { char *xp; xp = *xargv+1; /* Pointer for bundled args */ debug(F111,"dossharg entry",xp,xargc); while (x) { debug(F000,"dossharg arg","",x); switch (x) { /* Big switch on arg */ #ifndef COMMENT case '-': /* Extended commands... */ if (doxarg(xargv,0) < 0) { XFATAL("Extended option error"); } /* Full thru... */ case '+': /* Extended command for prescan() */ return(0); #else /* COMMENTP */ case '-': case '+': XFATAL("Extended options not configured"); #endif /* COMMENT */ /* * -d Debug * -# args Init * -Y no init file * -l user Set username * */ case 'h': /* help */ usage(); doexit(GOOD_EXIT,-1); break; case 'Y': xargv++, xargc--; /* Skip past argument */ break; /* Action done in prescan */ #ifdef OS2 case '#': /* K95 stdio threads */ xargv++, xargc--; /* Skip past argument */ break; /* Action done in prescan */ #endif /* OS2 */ case 'q': /* Quiet */ quiet = 1; break; case 'd': #ifdef DEBUG if (deblog) { debtim = 1; } else { deblog = debopn("debug.log",0); } #endif /* DEBUG */ break; case 'l': /* Set username and request telnet authentication */ if (*(xp+1)) { XFATAL("invalid argument bundling"); } xargv++, xargc--; if ((xargc < 1) || (**xargv == '-')) { XFATAL("missing username"); } if ((int)strlen(*xargv) > 63) { XFATAL("username too long"); } ckstrncpy(uidbuf,*xargv,UIDBUFLEN); break; default: fatal2(*xargv, "invalid command-line option, type \"ssh -h\" for help" ); } if (!xp) break; x = *++xp; /* See if options are bundled */ } return(0); } #endif /* SSHBUILTIN */ #else /* No command-line interface... */ int cmdlin() { extern int xargc; if (xargc > 1) { XFATAL("Sorry, command-line options disabled."); } } #endif /* NOCMDL */ ckuver.h0000644000015300001460000005651211355135753011352 0ustar fdckermit/* ckuver.h -- C-Kermit UNIX Version heralds */ /* Author: Frank da Cruz , Columbia University Academic Information Systems, New York City. Copyright (C) 1985, 2010, Trustees of Columbia University in the City of New York. All rights reserved. See the C-Kermit COPYING.TXT file or the copyright text in the ckcmai.c module for disclaimer and permissions. */ #ifndef CKUVER_H #define CKUVER_H /* Arranged more or less alphabetically by compiler symbol */ /* Must be included AFTER ckcdeb.h. */ #ifdef BEOS #ifdef BEOS45 #define HERALD " BeOS 4.5" #else #define HERALD " BeOS" #endif /* BEOS45 */ #else #ifdef BEBOX #ifdef BE_DR_7 #define HERALD " BeBox DR7" #else #define HERALD " BeBox" #endif /* BE_DR_7 */ #endif /* BEBOX */ #endif /* BEOS */ #ifdef BELLV10 #define HERALD " Bell Labs Research UNIX V10" #endif /* BELLV10 */ #ifdef APOLLOSR10 #define HERALD " Apollo SR10" #endif /* APOLLOSR10 */ #ifdef MAC #define HERALD " Apple Macintosh" #endif /* MAC */ #ifdef A986 #define HERALD " Altos 986 / Xenix 3.0" #endif /* A986 */ #ifdef AS400 #define HERALD " AS/400" #endif /* AS400 */ #ifdef aegis #ifdef BSD4 #define HERALD " Apollo DOMAIN/IX 4.2 BSD" #else #ifdef ATTSV #define HERALD " Apollo DOMAIN/IX System V" #else #define HERALD " Apollo Aegis" #endif /* BSD4 */ #endif /* ATTSV */ #endif /* aegis */ #ifndef HERALD #ifdef AIXRS #ifdef AIX53 #define HERALD " IBM AIX 5.3" #else #ifdef AIX52 #define HERALD " IBM AIX 5.2" #else #ifdef AIX51 #define HERALD " IBM AIX 5.1" #else #ifdef AIX45 #define HERALD " IBM AIX 5.0" #else #ifdef AIX45 #define HERALD " IBM AIX 4.5" #else #ifdef AIX44 #define HERALD " IBM AIX 4.4" #else #ifdef AIX43 #define HERALD " IBM AIX 4.3" #else #ifdef AIX42 #define HERALD " IBM AIX 4.2" #else #ifdef SVR4 #ifdef AIX41 #define HERALD " IBM AIX 4.1" #else #define HERALD " IBM RS/6000 AIX 3.2" #endif /* AIX41 */ #else #define HERALD " IBM RS/6000 AIX 3.0/3.1" #endif /* SVR4 */ #endif /* AIX42 */ #endif /* AIX43 */ #endif /* AIX44 */ #endif /* AIX45 */ #endif /* AIX50 */ #endif /* AIX51 */ #endif /* AIX52 */ #endif /* AIX53 */ #endif /* AIXRS */ #ifdef PS2AIX10 #define HERALD " IBM PS/2 AIX 1.x" #endif /* PS2AIX10 */ #ifdef AIXPS2 #define HERALD " IBM PS/2 AIX 3.x" #endif /* AIXPS2 */ #ifdef AIX370 #ifndef HERALD #define HERALD " IBM System/370 AIX/370" #endif #endif /* AIX370 */ #ifdef AIXESA #ifndef HERALD #define HERALD " IBM AIX/ESA version 2.1" #endif #endif /* AIXESA */ #ifdef ATT6300 #define HERALD " AT&T 6300" #endif /* ATT6300 */ #ifdef ATT7300 #ifdef UNIX351M #define HERALD " AT&T 7300 UNIX PC UNIX 3.51m" #else #define HERALD " AT&T 7300 UNIX PC" #endif /* UNIX351M */ #endif /* ATT7300 */ #ifdef AUX #define HERALD " Apple Macintosh AUX" #endif /* AUX */ #ifdef BSD44 #ifdef MACOSX #define HERALD " Mac OS X" #else #ifdef __OpenBSD__ #define HERALD " OpenBSD" #else #ifdef __bsdi__ #ifdef BSDI4 #define HERALD " BSDI BSD/OS 4.0" #else #ifdef BSDI3 #define HERALD " BSDI BSD/OS 3.0" #else #ifdef BSDI2 #define HERALD " BSDI BSD/OS 2.0" /* 1.1++ name... */ #else #define HERALD " BSDI BSD/386" /* Original 1.0 name */ #endif /* BSDI2 */ #endif /* BSDI3 */ #endif /* BSDI4 */ #else /* __bsdi__ */ #ifdef __NetBSD__ #ifndef HERALD #ifdef NETBSD16 #define HERALD " NetBSD 1.6" #else #ifdef NETBSD15 #define HERALD " NetBSD 1.5" #else #define HERALD " NetBSD" #endif /* NETBSD15 */ #endif /* NETBSD16 */ #endif /* HERALD */ #else /* __NetBSD__ */ #ifdef __FreeBSD__ #ifdef FREEBSD51 #define HERALD " FreeBSD 5.1" #else #ifdef FREEBSD50 #define HERALD " FreeBSD 5.0" #else #ifdef FREEBSD49 #define HERALD " FreeBSD 4.9" #else #ifdef FREEBSD48 #define HERALD " FreeBSD 4.8" #else #ifdef FREEBSD47 #define HERALD " FreeBSD 4.7" #else #ifdef FREEBSD46 #define HERALD " FreeBSD 4.6" #else #ifdef FREEBSD45 #define HERALD " FreeBSD 4.5" #else #ifdef FREEBSD44 #define HERALD " FreeBSD 4.4" #else #ifdef FREEBSD43 #define HERALD " FreeBSD 4.3" #else #ifdef FREEBSD42 #define HERALD " FreeBSD 4.2" #else #ifdef FREEBSD41 #define HERALD " FreeBSD 4.1" #else #ifdef FREEBSD4 #define HERALD " FreeBSD 4.0" #else #ifdef FREEBSD3 #define HERALD " FreeBSD 3.0" #else #ifdef FREEBSD2 #define HERALD " FreeBSD 2.0" #else #define HERALD " FreeBSD" #endif /* FREEBSD2 */ #endif /* FREEBSD3 */ #endif /* FREEBSD4 */ #endif /* FREEBSD41 */ #endif /* FREEBSD42 */ #endif /* FREEBSD43 */ #endif /* FREEBSD44 */ #endif /* FREEBSD45 */ #endif /* FREEBSD46 */ #endif /* FREEBSD47 */ #endif /* FREEBSD48 */ #endif /* FREEBSD49 */ #endif /* FREEBSD50 */ #endif /* FREEBSD51 */ #else #ifdef __386BSD__ #define HERALD " 386BSD" #else #define HERALD " 4.4BSD" #endif /* __386BSD__ */ #endif /* __FreeBSD__ */ #endif /* __NetBSD__ */ #endif /* __bsdi__ */ #endif /* __OpenBSD__ */ #endif /* MACOSX */ #endif /* BSD44 */ #ifdef ENCORE #ifdef BSD43 #define HERALD " Encore Multimax UMAX 4.3" #else #define HERALD " Encore Multimax UMAX 4.2" #endif #endif /* ENCORE */ #ifdef BSD29 #define HERALD " 2.9 BSD" #endif /* BSD29 */ #ifdef BSD41 #define HERALD " 4.1 BSD" #endif /* BSD41 */ #ifdef C70 #define HERALD " BBN C/70" #endif /* c70 */ #ifdef CIE #define HERALD " CIE Systems 680/20 Regulus" #endif /* CIE */ #ifdef COHERENT #ifdef _I386 #define HERALD " MWC Coherent 386 4.x" #ifndef i386 #define i386 #endif /* i386 */ #else #define HERALD " PC/AT MWC Coherent 286 3.x" #ifndef i286 #define i286 #endif /* i286 */ #endif /* _I386 */ #endif /* COHERENT */ #ifdef CONVEX9 #define HERALD " Convex/OS" #endif /* CONVEX9 */ #ifdef CONVEX10 #define HERALD " Convex/OS 10.1" #endif /* CONVEX10 */ #ifdef _CRAY #ifdef _CRAYCOM #define HERALD " Cray CSOS" #else /* _CRAYCOM */ #define HERALD " Cray UNICOS" #endif /* _CRAYCOM */ #endif /* _CRAY */ #ifdef DGUX #ifdef DGUX54420 #define HERALD " Data General DG/UX R4.20" #else #ifdef DGUX54411 #define HERALD " Data General DG/UX R4.11" #else #ifdef DGUX54410 #define HERALD " Data General DG/UX R4.10" #else #ifdef DGUX54310 #define HERALD " Data General DG/UX 5.4R3.10" #else #ifdef DGUX543 #define HERALD " Data General DG/UX 5.4R3.00" #else #ifdef DGUX540 #define HERALD " Data General DG/UX 5.4" #else #ifdef DGUX430 #define HERALD " Data General DG/UX 4.30" #else #define HERALD " Data General DG/UX" #endif /* DGUX430 */ #endif /* DGUX540 */ #endif /* DGUX543 */ #endif /* DGUX54310 */ #endif /* DGUX54410 */ #endif /* DGUX54411 */ #endif /* DGUX54420 */ #endif /* DGUX */ #ifdef datageneral #ifndef HERALD #define HERALD " Data General AOS/VS" #endif /* HERALD */ #endif /* datageneral */ #ifdef SINIX #ifdef SNI544 #define HERALD " Siemens Nixdorf Reliant UNIX V5.44" #else #ifdef SNI543 #define HERALD " Siemens Nixdorf Reliant UNIX V5.43" #else #ifdef SNI541 #define HERALD " Siemens Nixdorf SINIX V5.41" #else #define HERALD " Siemens Nixdorf SINIX V5.42" #endif /* SNI541 */ #endif /* SNI543 */ #endif /* SNI544 */ #endif /* SINIX */ #ifdef POWERMAX #define HERALD " Concurrent PowerMAX OS" #endif /* POWERMAX */ #ifdef DELL_SVR4 #define HERALD " Dell System V R4" #endif /* DELL_SVR4 */ #ifdef NCRMPRAS #define HERALD " NCR MP-RAS" #endif /* NCRMPRAS */ #ifdef UNIXWARE #define HERALD " UnixWare" #else #ifdef OLD_UNIXWARE #define HERALD " UnixWare" #endif /* OLD_UNIXWARE */ #endif /* UNIXWARE */ #ifdef ICL_SVR4 #define HERALD " ICL System V R4 DRS N/X" #endif /* ICL_SVR4 */ #ifdef FT18 #ifdef FT21 #define HERALD " Fortune For:Pro 2.1" #else #define HERALD " Fortune For:Pro 1.8" #endif /* FT21 */ #endif /* FT18 */ #ifdef GEMDOS #define HERALD " Atari ST GEM 1.0" #endif /* GEMDOS */ #ifdef XF68R3V6 #define HERALD " Motorola UNIX System V/68 R3V6" #endif /* XF68R3V6 */ #ifdef XF88R32 #define HERALD " Motorola UNIX System V/88 R32" #endif /* XF88R32 */ #ifdef I386IX #ifdef SVR3JC #define HERALD " Interactive UNIX System V/386 R3.2" #else #define HERALD " Interactive Systems Corp 386/ix" #endif /* SVR3JC */ #endif /* I386IX */ #ifdef IRIX65 #define HERALD " Silicon Graphics IRIX 6.5" #else #ifdef IRIX64 #define HERALD " Silicon Graphics IRIX 6.4" #else #ifdef IRIX63 #define HERALD " Silicon Graphics IRIX 6.3" #else #ifdef IRIX62 #define HERALD " Silicon Graphics IRIX 6.2" #else #ifdef IRIX60 #define HERALD " Silicon Graphics IRIX 6.0" #else #ifdef IRIX53 #define HERALD " Silicon Graphics IRIX 5.3" #else #ifdef IRIX52 #define HERALD " Silicon Graphics IRIX 5.2" #else #ifdef IRIX51 #define HERALD " Silicon Graphics IRIX 5.1" #else #ifdef IRIX40 #define HERALD " Silicon Graphics IRIX 4.0" #endif /* IRIX40 */ #endif /* IRIX51 */ #endif /* IRIX52 */ #endif /* IRIX53 */ #endif /* IRIX60 */ #endif /* IRIX62 */ #endif /* IRIX63 */ #endif /* IRIX64 */ #endif /* IRIX65 */ #ifdef ISIII #define HERALD " Interactive Systems Corp System III" #endif /* ISIII */ #ifdef IX370 #define HERALD " IBM IX/370" #endif /* IX370 */ #ifdef HPUX #ifdef HPUX5 #define HERALD " HP-UX 5.00" #else #ifdef HPUX6 #define HERALD " HP-UX 6.00" #else #ifdef HPUX7 #define HERALD " HP-UX 7.00" #else #ifdef HPUX8 #define HERALD " HP-UX 8.00" #else #ifdef HPUX9 #define HERALD " HP-UX 9.00" #else #ifdef HPUX1100 #define HERALD " HP-UX 11.00" #else #ifdef HPUX10 #ifdef HPUX1030 #define HERALD " HP-UX 10.30" #else #ifdef HPUX1020 #define HERALD " HP-UX 10.20" #else #ifdef HPUX1010 #define HERALD " HP-UX 10.10" #else #ifdef HPUX10xx #define HERALD " HP-UX 10.xx" #else #define HERALD " HP-UX 10.00" #endif /* HPUX10XX */ #endif /* HPUX1010 */ #endif /* HPUX1020 */ #endif /* HPUX1030 */ #else #define HERALD " HP-UX" #endif /* HPUX10 */ #endif /* HPUX1100 */ #endif /* HPUX9 */ #endif /* HPUX8 */ #endif /* HPUX7 */ #endif /* HPUX6 */ #endif /* HPUX5 */ #endif /* HPUX */ #ifndef MINIX #ifdef MINIX315 #define MINIX #endif /* MINIX315 */ #endif /* MINIX */ #ifndef MINIX #ifdef MINIX3 #define MINIX #endif /* MINIX3 */ #endif /* MINIX */ #ifdef MINIX #ifdef MINIX315 #define HERALD " Minix 3.1.5" #ifndef MINIX3 #define MINIX3 #endif /* MINIX3 */ #endif /* MINIX315 */ #ifdef MINIX3 #ifndef MINIX2 #define MINIX2 #endif /* MINIX2 */ #ifndef HERALD #define HERALD " Minix 3.0" #endif /* HERALD */ #else #ifdef MINIX2 #define HERALD " Minix 2.0" #else #define HERALD " Minix 1.0" #endif /* MINIX3 */ #endif /* MINIX2 */ #endif /* MINIX */ #ifdef MIPS #define HERALD " MIPS RISC/OS SVR3" #endif /* MIPS */ #ifdef NEXT #ifdef OPENSTEP42 #define HERALD " OPENSTEP 4.2" #else #ifdef NEXT33 #define HERALD " NeXTSTEP 3.3" #else #define HERALD " NeXTSTEP" #endif /* NEXT33 */ #endif /* OPENSTEP42 */ #endif /* NEXT */ #ifdef OSF #ifdef i386 #define HERALD " DECpc OSF/1" #ifdef __GNUC #define OSFPC #endif /* __GNUC */ #else /* Not i386 so Alpha */ #ifdef TRU64 #ifdef OSF51B #define HERALD " Compaq Tru64 UNIX 5.1B" #else #ifdef OSF51A #define HERALD " Compaq Tru64 UNIX 5.1A" #else #ifdef OSF50 #define HERALD " Compaq Tru64 UNIX 5.0A" #else #ifdef OSF40G #define HERALD " Compaq Tru64 UNIX 4.0G" #else #ifdef OSF40F #define HERALD " Compaq Tru64 UNIX 4.0F" #else #ifdef OSF40E #define HERALD " Compaq Tru64 UNIX 4.0E" #endif /* OSF40E */ #endif /* OSF40F */ #endif /* OSF40G */ #endif /* OSF50 */ #endif /* OSF51A */ #endif /* OSF51B */ #else /* Not TRU64 */ #ifdef OSF40 #define HERALD " Digital UNIX 4.0" #else #ifdef OSF32 #define HERALD " Digital UNIX 3.2" #else #define HERALD " DEC OSF/1 Alpha" #endif /* OSF40 */ #endif /* OSF32 */ #endif /* TRU64 */ #endif /* i386 */ #endif /* OSF */ #ifdef PCIX #define HERALD " PC/IX" #endif /* PCIX */ #ifdef sxaE50 #define HERALD " PFU SX/A V10/L50" #endif /* sxaE50 */ #ifdef PROVX1 #define HERALD " DEC Professional 300 (Venix 1.0)" #endif /* PROVX1 */ #ifdef PYRAMID #ifdef SVR4 #define HERALD " Pyramid DC/OSx" #else #define HERALD " Pyramid Dual Port OSx" #endif /* SVR4 */ #endif /* PYRAMID */ #ifdef RTAIX #define HERALD " IBM RT PC (AIX 2.2)" #endif /* RTAIX */ #ifdef RTU #define HERALD " Masscomp/Concurrent RTU" #endif /* RTU */ #ifdef sony_news #define HERALD " SONY NEWS" #endif /* sony_news */ #ifdef SOLARIS24 #define HERALD " Solaris 2.4" #else #ifdef SOLARIS23 #define HERALD " Solaris 2.3" #else #ifdef SOLARIS #define HERALD " Solaris 2.x" #endif /* SOLARIS */ #endif /* SOLARIS23 */ #endif /* SOLARIS24 */ #ifdef SUNOS4 #ifdef BSD4 #ifdef SUNOS41 #define HERALD " SunOS 4.1" #else #define HERALD " SunOS 4.0" #endif /* SUNOS41 */ #endif /* BSD4 */ #endif /* SUNOS4 */ #ifdef SUN4S5 #ifdef HDBUUCP #define HERALD " SunOS 4.1 (SVR3)" #else #define HERALD " SunOS 4.0 (SVR3)" #endif /* HDBUUCP */ #endif /* SUN4S5 */ #ifdef STRATUS #define HERALD " Stratus VOS" #endif /* STRATUS */ #ifdef TOWER1 #define HERALD " NCR Tower 1632 OS 1.02" #endif /* TOWER1 */ #ifdef TRS16 #define HERALD " Tandy 16/6000 Xenix 3.0" #ifndef CKCPU #define CKCPU "mc68000" #endif /* CKCPU */ #endif /* TRS16 */ #ifdef u3b2 #ifndef HERALD #ifdef SVR3 #define HERALD " AT&T 3B2 System V R3" #else #define HERALD " AT&T 3B2 System V" #endif /* SVR3 */ #endif /* HERALD */ #endif /* u3b2 */ #ifdef ultrix #ifdef vax #ifdef ULTRIX3 #define HERALD " VAX/ULTRIX 3.0" #else #define HERALD " VAX/ULTRIX" #endif /* ULTRIX3 */ #else #ifdef mips #ifdef ULTRIX43 #define HERALD " DECstation/ULTRIX 4.3" #else #ifdef ULTRIX44 #define HERALD " DECstation/ULTRIX 4.4" #else #ifdef ULTRIX45 #define HERALD " DECstation/ULTRIX 4.5" #else #define HERALD " DECstation/ULTRIX" #endif /* ULTRIX45 */ #endif /* ULTRIX44 */ #endif /* ULTRIX43 */ #else #define HERALD " ULTRIX" #endif /* mips */ #endif /* vax */ #endif /* ultrix */ #ifdef OXOS #define HERALD " Olivetti X/OS" #endif /* OXOS */ #ifdef _386BSD #define HERALD " 386BSD" #endif /* _386BSD */ #ifdef POSIX #ifdef PTX #ifdef PTX4 #define HERALD " DYNIX/ptx V4" #else #define HERALD " DYNIX/ptx" #endif /* PTX4 */ #else /* PTX */ #ifndef OSF /* Let OSF -DPOSIX keep previously defined HERALD */ #ifdef HERALD #undef HERALD #endif /* HERALD */ #endif /* OSF */ #ifdef OU8 #define HERALD " OpenUNIX 8" #else #ifdef UW7 #define HERALD " Unixware 7" #else #ifdef QNX #ifdef QNX16 #define HERALD " QNX 16-bit" #else #define HERALD " QNX 32-bit" #endif /* QNX16 */ #else #ifdef NEUTRINO #define HERALD " QNX Neutrino 2" #else /* NEUTRINO */ #ifdef QNX6 #define HERALD " QNX6" #else /* QNX6 */ #ifdef __linux__ #ifdef ZSL5500 #define HERALD " Sharp Zaurus SL-5500" #else #ifdef RH90 #define HERALD " Red Hat Linux 9.0" #else #ifdef RH80 #define HERALD " Red Hat Linux 8.0" #else #ifdef RH73 #define HERALD " Red Hat Linux 7.3" #else #ifdef RH72 #define HERALD " Red Hat Linux 7.2" #else #ifdef RH71 #define HERALD " Red Hat Linux 7.1" #else #define HERALD " Linux" #endif /* RH71 */ #endif /* RH72 */ #endif /* RH73 */ #endif /* RH80 */ #endif /* RH90 */ #endif /* ZSL5500 */ #else /* __linux__ */ #ifdef _386BSD /* 386BSD Jolix */ #define HERALD " 386BSD" #else #ifdef LYNXOS /* Lynx OS 2.2 */ #define HERALD " Lynx OS" #else #ifdef Plan9 #define HERALD " Plan 9 from Bell Labs" #else #ifdef SOLARIS11 #define HERALD " Solaris 11" #else #ifdef SOLARIS10 #define HERALD " Solaris 10" #else #ifdef SOLARIS9 #define HERALD " Solaris 9" #else #ifdef SOLARIS8 #define HERALD " Solaris 8" #else #ifdef SOLARIS7 #define HERALD " Solaris 7" #else #ifdef SOLARIS26 #define HERALD " Solaris 2.6" #else #ifdef SOLARIS25 #define HERALD " Solaris 2.5" #else #ifdef SOLARIS24 #define HERALD " Solaris 2.4" #else #ifdef SOLARIS #define HERALD " Solaris 2.x" #endif /* SOLARIS */ #endif /* SOLARIS24 */ #endif /* SOLARIS25 */ #endif /* SOLARIS26 */ #endif /* SOLARIS7 */ #endif /* SOLARIS8 */ #endif /* SOLARIS9 */ #endif /* SOLARIS10 */ #endif /* SOLARIS11 */ #endif /* Plan9 */ #endif /* LYNXOS */ #endif /* _386BSD */ #endif /* __linux__ */ #endif /* QNX6 */ #endif /* NEUTRINO */ #endif /* QNX */ #endif /* UW7 */ #endif /* OU8 */ #endif /* PTX */ #endif /* POSIX */ #ifdef UTS24 #define HERALD " Amdahl UTS 2.4" #endif /* UTS24 */ #ifdef UTSV #define HERALD " Amdahl UTS V" #endif /* UTSV */ #ifdef VXVE #define HERALD " CDC VX/VE 5.2.1 System V" #endif /* VXVE */ #ifdef SCO234 #ifdef HERALD #undef HERALD #endif /* HERALD */ #define HERALD " SCO XENIX 2.3.4" #else #ifdef CK_SCO32V4 #ifdef HERALD #undef HERALD #endif /* HERALD */ #ifdef ODT30 #define HERALD " SCO ODT 3.0" #else #define HERALD " SCO UNIX/386 V4" #endif /* ODT30 */ #else #ifdef CK_SCOV5 #ifdef HERALD #undef HERALD #endif /* HERALD */ #ifdef SCO_OSR507 #define HERALD " SCO OpenServer R5.0.7" #else #ifdef SCO_OSR506A #define HERALD " SCO OpenServer R5.0.6a" #else #ifdef SCO_OSR506 #define HERALD " SCO OpenServer R5.0.6" #else #ifdef SCO_OSR505 #define HERALD " SCO OpenServer R5.0.5" #else #ifdef SCO_OSR504 #define HERALD " SCO OpenServer R5.0.4" #else #ifdef SCO_OSR502 #define HERALD " SCO OpenServer R5.0.2" #else #define HERALD " SCO OpenServer R5.0" #endif /* SCO_OSR502 */ #endif /* SCO_OSR504 */ #endif /* SCO_OSR505 */ #endif /* SCO_OSR506 */ #endif /* SCO_OSR506A */ #endif /* SCO_OSR507 */ #else #ifdef XENIX #ifdef HERALD #undef HERALD #endif /* HERALD */ #ifdef M_UNIX #define HERALD " SCO UNIX/386" #else #ifdef M_I386 #define HERALD " Xenix/386" #else #ifdef M_I286 #define HERALD " Xenix/286" #else #define HERALD " Xenix" #endif /* M_I286 */ #endif /* M_I386 */ #endif /* M_UNIX */ #endif /* XENIX */ #endif /* CK_SCOV5 */ #endif /* CK_SCOV32V4 */ #endif /* SCO234 */ #ifdef ZILOG #define HERALD " Zilog S8000 Zeus 3.21+" #endif /* ZILOG */ #ifdef UTEK #define HERALD " UTek" #endif /* UTEK */ /* Catch-alls for anything not defined explicitly above */ #ifndef HERALD #ifdef SVR4 #ifdef i386 #define HERALD " AT&T System V/386 R4" #else #ifdef AMIX #define HERALD " Commodore Amiga System V/m68k R4" #else #define HERALD " AT&T System V R4" #endif /* AMIX */ #endif /* i386 */ #else #ifdef SVR3 #define HERALD " AT&T System V R3" #else #ifdef ATTSV #define HERALD " AT&T System III / System V" #else #ifdef BSD43 #ifdef pdp11 #define HERALD " 2.10 BSD PDP-11" #else #ifdef vax #define HERALD " 4.3 BSD VAX" #else #define HERALD " 4.3 BSD" #endif /* vax */ #endif /* pdp11 */ #else #ifdef BSD4 #ifdef vax #define HERALD " 4.2 BSD VAX" #else #define HERALD " 4.2 BSD" #endif /* vax */ #else #ifdef V7 #define HERALD " UNIX Version 7" #endif /* V7 */ #endif /* BSD4 */ #endif /* BSD43 */ #endif /* ATTSV */ #endif /* SVR3 */ #endif /* SVR4 */ #endif /* HERALD */ #endif /* HERALD */ #ifdef OS2 #ifdef HERALD #undef HERALD #endif /* HERALD */ #ifdef NT #define HERALD " 32-bit Windows" #else /* NT */ #define HERALD " 32-bit OS/2" #endif /* NT */ #endif /* OS/2 */ #ifndef HERALD #define HERALD " Unknown Version" #endif /* HERALD */ /* Hardware type */ #ifdef vax /* DEC VAX */ #ifndef CKCPU #define CKCPU "vax" #endif /* CKCPU */ #endif /* vax */ #ifdef pdp11 /* DEC PDP-11 */ #ifndef CKCPU #define CKCPU "pdp11" #endif /* CKCPU */ #endif /* pdp11 */ #ifdef __ALPHA /* DEC Alpha */ #ifndef CKCPU #define CKCPU "Alpha" #endif /* CKCPU */ #endif /* __ALPHA */ #ifdef __alpha /* OSF/1 uses lowercase... */ #ifndef CKCPU #define CKCPU "Alpha" #endif /* CKCPU */ #endif /* __alpha */ #ifdef DGUX /* Override Motorola 88k assumption */ #ifndef CKCPU /* New AViiONs are Intel based... */ #ifdef i586 #define CKCPU "i586" #else #ifdef i486 #define CKCPU "i486" #else #ifdef i386 #define CKCPU "i386" #endif /* i386 */ #endif /* i486 */ #endif /* i586 */ #endif /* CKCPU */ #endif /* DGUX */ /* HP 9000 */ #ifdef __hp9000s700 #ifndef CKCPU #define CKCPU "hp9000s700" #endif /* CKCPU */ #endif /* __hp9000s700 */ #ifdef __hp9000s800 #ifndef CKCPU #define CKCPU "hp9000s800" #endif /* CKCPU */ #endif /* __hp9000s800 */ #ifdef __hp9000s500 #ifndef CKCPU #define CKCPU "hp9000s500" #endif /* CKCPU */ #endif /* __hp9000s500 */ #ifdef __hp9000s400 #ifndef CKCPU #define CKCPU "hp9000s400" #endif /* CKCPU */ #endif /* __hp9000s400 */ #ifdef __hp9000s300 #ifndef CKCPU #define CKCPU "hp9000s300" #endif /* CKCPU */ #endif /* __hp9000s300 */ #ifdef __hp9000s200 #ifndef CKCPU #define CKCPU "hp9000s200" #endif /* CKCPU */ #endif /* __hp9000s200 */ #ifdef m88000 /* Motorola 88000 */ #ifndef CKCPU #define CKCPU "mc88000" #endif /* CKCPU */ #endif /* m88000 */ #ifdef __using_M88KBCS /* DG symbol for Motorola 88000 */ #ifndef CKCPU #define CKCPU "mc88000" #endif /* CKCPU */ #endif /* __using_M88KBCS */ #ifdef m88k /* Motorola symbol for 88000 */ #ifndef CKCPU #define CKCPU "mc88000" #endif /* CKCPU */ #endif /* m88k */ #ifdef mc68040 /* Motorola 68040 */ #ifndef CKCPU #define CKCPU "mc68040" #endif /* CKCPU */ #endif /* mc68040 */ #ifdef mc68030 /* Motorola 68030 */ #ifndef CKCPU #define CKCPU "mc68030" #endif /* CKCPU */ #endif /* mc68030 */ #ifdef mc68020 /* Motorola 68020 */ #ifndef CKCPU #define CKCPU "mc68020" #endif /* CKCPU */ #endif /* mc68020 */ #ifdef mc68010 /* Motorola 68010 */ #ifndef CKCPU #define CKCPU "mc68010" #endif /* CKCPU */ #endif /* mc68010 */ #ifdef mc68000 /* Motorola 68000 */ #ifndef CKCPU #define CKCPU "mc68000" #endif /* CKCPU */ #endif /* mc68000 */ #ifdef mc68k /* Ditto (used by DIAB DS90) */ #ifndef CKCPU #define CKCPU "mc68000" #endif /* CKCPU */ #endif /* mc68k */ #ifdef m68 /* Ditto */ #ifndef CKCPU #define CKCPU "mc68000" #endif /* CKCPU */ #endif /* m68 */ #ifdef m68k /* Ditto */ #ifndef CKCPU #define CKCPU "mc68000" #endif /* CKCPU */ #endif /* m68k */ #ifdef ia64 /* IA64 / Itanium */ #ifndef CKCPU #define CKCPU "ia64" #endif /* CKCPU */ #endif /* i686 */ #ifdef i686 /* Intel 80686 */ #ifndef CKCPU #define CKCPU "i686" #endif /* CKCPU */ #endif /* i686 */ #ifdef i586 /* Intel 80586 */ #ifndef CKCPU #define CKCPU "i586" #endif /* CKCPU */ #endif /* i586 */ #ifdef i486 /* Intel 80486 */ #ifndef CKCPU #define CKCPU "i486" #endif /* CKCPU */ #endif /* i80486 */ #ifdef i386 /* Intel 80386 */ #ifndef CKCPU #define CKCPU "i386" #endif /* CKCPU */ #endif /* i80386 */ #ifdef i286 /* Intel 80286 */ #ifndef CKCPU #define CKCPU "i286" #endif /* CKCPU */ #endif /* i286 */ #ifdef i186 /* Intel 80186 */ #ifndef CKCPU #define CKCPU "i186" #endif /* CKCPU */ #endif /* i186 */ #ifdef M_I586 /* Intel 80586 */ #ifndef CKCPU #define CKCPU "i586" #endif /* CKCPU */ #endif /* M_I586 */ #ifdef M_I486 /* Intel 80486 */ #ifndef CKCPU #define CKCPU "i486" #endif /* CKCPU */ #endif /* M_I486 */ #ifdef _M_I386 /* Intel 80386 */ #ifndef CKCPU #define CKCPU "i386" #endif /* CKCPU */ #endif /* _M_I386 */ #ifdef M_I286 /* Intel 80286 */ #ifndef CKCPU #define CKCPU "i286" #endif /* CKCPU */ #endif /* M_I286 */ #ifdef M_I86 /* Intel 80x86 */ #ifndef CKCPU #define CKCPU "ix86" #endif /* CKCPU */ #endif /* M_I86 */ #ifdef sparc /* SUN SPARC */ #ifndef CKCPU #define CKCPU "sparc" #endif /* CKCPU */ #endif /* sparc */ #ifdef mips /* MIPS RISC processor */ #ifndef CKCPU #define CKCPU "mips" #endif /* CKCPU */ #endif /* mips */ #ifdef _IBMR2 /* IBM RS/6000 */ #ifndef CKCPU /* (what do they call the chip?) */ #define CKCPU "rs6000" #endif /* CKCPU */ #endif /* rs6000 */ #ifdef u3b5 /* WE32000 MAC-32, AT&T 3Bx */ #ifndef CKCPU #define CKCPU "u3b5" #endif /* CKCPU */ #endif /* u3b5 */ #ifdef n3b #ifndef CKCPU #define CKCPU "n3b" #endif /* CKCPU */ #endif /* n3b */ #ifdef u3b #ifndef CKCPU #define CKCPU "u3b" #endif /* CKCPU */ #endif /* u3b */ #ifdef n16 /* Encore Multimax */ #ifndef CKCPU #define CKCPU "n16" #endif /* CKCPU */ #endif /* n16 */ #ifdef u370 /* IBM 370 */ #ifndef CKCPU #define CKCPU "u370" #endif /* CKCPU */ #endif /* u370 */ #ifdef MAC /* Macintosh catch-all */ #ifndef CKCPU #define CKCPU "mc68000" #endif /* CKCPU */ #endif /* MAC */ #ifdef STRATUS #ifndef CKCPU #ifdef __I860__ #define CKCPU "I860 Family" #else #ifdef __MC68K__ #define CKCPU "MC680x0 Family" #else #define CKCPU "Stratus unknown processor" #endif /* __MC68K__ */ #endif /* __I860__ */ #endif /* CKCPU */ #endif /* STRATUS */ #ifdef COMMENT #ifndef CKCPU /* All others */ #define CKCPU "unknown" #endif /* CKCPU */ #endif /* COMMENT */ #endif /* CKUVER_H */ ckuxla.c0000644000015300001460000103337111571775460011341 0ustar fdckermit#include "ckcsym.h" #include "ckcdeb.h" /* Includes... */ #include "ckcker.h" #include "ckucmd.h" #include "ckcxla.h" #ifdef NOXFER #define zdstuff(a) #endif /* NOXFER */ #ifndef NOCSETS char *xlav = "Character Set Translation 9.0.044, 2 Jun 2011"; /* C K U X L A */ /* C-Kermit tables and functions supporting character set translation. */ /* Author: Frank da Cruz , Columbia University Academic Information Systems, New York City. Copyright (C) 1985, 2011, Trustees of Columbia University in the City of New York. All rights reserved. See the C-Kermit COPYING.TXT file or the copyright text in the ckcmai.c module for disclaimer and permissions. */ /* Character set translation data and functions */ extern int zincnt; /* File i/o macros and variables */ extern char *zinptr; extern int zoutcnt; extern char *zoutptr; extern int byteorder; extern int xfrxla; int tslevel = TS_L0; /* Transfer syntax level (0,1,2) */ int tcharset = TC_TRANSP; /* Transfer syntax character set */ int tcs_save = -1; /* For save/restore term charset */ int tcs_transp = 0; /* Term charset is TRANSPARENT flag */ #ifdef CKOUNI int tcsr = TX_8859_1; /* Remote terminal character set */ #else /* CKOUNI */ int tcsr = FC_USASCII; #endif /* CKOUNI */ int language = L_USASCII; /* Language */ #ifdef UNICODE int ucsbom = 1; /* Add BOM to new UCS files? */ int ucsorder = -1; /* Default byte order for UCS files */ int fileorder = -1; /* Byte order of current file */ /* 0 = BE, 1 = LE */ #endif /* UNICODE */ /* Default local file and terminal character set. Normally ASCII, but for some systems we know otherwise. */ int fcs_save = -1; #ifdef datageneral /* Data General AOS/VS */ int fcharset = FC_DGMCS; /* uses the DG International set */ int dcset8 = FC_DGMCS; int dcset7 = FC_USASCII; int tcsl = FC_DGMCS; #else #ifdef NEXT /* The NeXT workstation */ int fcharset = FC_NEXT; /* uses its own 8-bit set */ int dcset8 = FC_NEXT; int dcset7 = FC_USASCII; int tcsl = FC_NEXT; #else #ifdef MAC /* The Macintosh */ int fcharset = FC_APPQD; /* uses an extended version of */ int dcset8 = FC_APPQD; int dcset7 = FC_USASCII; int tcsl = FC_APPQD; /* Apple Quickdraw */ #else #ifdef AUX int fcharset = FC_APPQD; /* Ditto for Apple A/UX */ int dcset8 = FC_APPQD; int dcset7 = FC_USASCII; int tcsl = FC_APPQD; #else #ifdef AMIGA /* The Commodore Amiga */ int fcharset = FC_1LATIN; /* uses Latin-1 */ int dcset8 = FC_1LATIN; int dcset7 = FC_USASCII; int tcsl = FC_1LATIN; #else /* All others */ #ifdef CKOUNI /* OS/2 Unicode */ int fcharset = FC_1LATIN; int dcset8 = FC_1LATIN; int dcset7 = FC_USASCII; int tcsl = TX_8859_1; int prncs = TX_CP437; #else /* All others */ int fcharset = FC_USASCII; /* use ASCII by default */ int dcset8 = FC_1LATIN; /* But default 8-bit set is Latin-1 */ int dcset7 = FC_USASCII; int tcsl = FC_USASCII; int prncs = FC_CP437; #endif /* CKOUNI */ #endif /* AMIGA */ #endif /* AUX */ #endif /* MAC */ #endif /* NEXT */ #endif /* datageneral */ int s_cset = XMODE_A; /* SEND charset selection = AUTO */ int r_cset = XMODE_A; /* RECV charset selection = AUTO */ int afcset[MAXFCSETS+1]; /* Character-set associations */ int axcset[MAXTCSETS+1]; int xlatype = XLA_NONE; /* Translation type */ #ifdef UNICODE #ifdef CK_ANSIC extern int (*xl_utc[MAXTCSETS+1])(USHORT); /* Unicode to TCS */ extern int (*xl_ufc[MAXFCSETS+1])(USHORT); /* Unicode to FCS */ extern USHORT (*xl_tcu[MAXTCSETS+1])(CHAR); /* TCS to Unicode */ extern USHORT (*xl_fcu[MAXFCSETS+1])(CHAR); /* FCS to Unicode */ #else extern int (*xl_utc[MAXTCSETS+1])(); extern int (*xl_ufc[MAXFCSETS+1])(); extern USHORT (*xl_tcu[MAXTCSETS+1])(); extern USHORT (*xl_fcu[MAXFCSETS+1])(); #endif /* CK_ANSIC */ #endif /* UNICODE */ /* Bureaucracy section */ _PROTOTYP( CHAR xnel1, (CHAR c) ); /* NeXT to Latin-1 */ _PROTOTYP( CHAR xl143, (CHAR c) ); /* Latin-1 to IBM CP437 */ _PROTOTYP( CHAR xl1as, (CHAR c) ); /* Latin-1 to US ASCII */ _PROTOTYP( CHAR zl1as, (CHAR c) ); /* Latin-1 to US ASCII */ #ifdef CYRILLIC _PROTOTYP( CHAR xassk, (CHAR c) ); /* ASCII to Short KOI */ _PROTOTYP( CHAR xskcy, (CHAR c) ); /* Short KOI to Latin/Cyrillic */ #endif /* CYRILLIC */ #ifdef LATIN2 _PROTOTYP( CHAR xnel2, (CHAR c) ); /* NeXT to Latin-2 */ _PROTOTYP( CHAR xl243, (CHAR c) ); /* Latin-2 to IBM CP437 */ _PROTOTYP( CHAR xl2as, (CHAR c) ); /* Latin-2 to US ASCII */ _PROTOTYP( CHAR zl2as, (CHAR c) ); /* Latin-2 to US ASCII */ _PROTOTYP( CHAR xl2r8, (CHAR c) ); /* Latin-2 to HP */ _PROTOTYP( CHAR xl2l9, (CHAR c) ); /* Latin-2 to Latin-9 */ _PROTOTYP( CHAR xl9l2, (CHAR c) ); /* Latin-9 to Latin-2 */ _PROTOTYP( CHAR xl2mz, (CHAR c) ); /* Latin-2 to Mazovia */ _PROTOTYP( CHAR xmzl2, (CHAR c) ); /* Mazovia to Latin-2 */ _PROTOTYP( CHAR xl1mz, (CHAR c) ); /* Latin-1 to Mazovia */ _PROTOTYP( CHAR xmzl1, (CHAR c) ); /* Mazovia to Latin-1 */ _PROTOTYP( CHAR xmzl9, (CHAR c) ); /* Latin-9 to Mazovia */ _PROTOTYP( CHAR xl9mz, (CHAR c) ); /* Mazovia to Latin-9 */ #endif /* LATIN2 */ /* Transfer character-set info */ struct csinfo tcsinfo[] = { /* Name size code designator alphabet keyword */ "TRANSPARENT", 256,TC_TRANSP, "", AL_UNK, "transparent", /* 0 */ "ASCII", 128,TC_USASCII,"", AL_ROMAN,"ascii", /* 1 */ "ISO 8859-1 Latin-1",256,TC_1LATIN, "I6/100",AL_ROMAN,"latin1-iso", /* 2 */ #ifdef LATIN2 "ISO 8859-2 Latin-2",256,TC_2LATIN, "I6/101",AL_ROMAN,"latin2-iso", /* 3 */ #endif /* LATIN2 */ #ifdef CYRILLIC /* 4 */ "ISO 8859-5 Latin/Cyrillic",256,TC_CYRILL,"I6/144",AL_CYRIL,"cyrillic-iso", #endif /* CYRILLIC */ #ifdef KANJI "Japanese EUC",16384,TC_JEUC, "I14/87/13", AL_JAPAN, "euc-jp", /* 5 */ #endif /* KANJI */ #ifdef HEBREW /* 6 */ "ISO 8859-8 Latin/Hebrew",256,TC_HEBREW,"I6/138",AL_HEBREW,"hebrew-iso", #endif /* HEBREW */ #ifdef GREEK "ISO 8859-7 Latin/Greek",256,TC_GREEK,"I6/126",AL_GREEK,"greek-iso", /* 7 */ #endif /* GREEK */ "ISO 8859-15 Latin-9",256,TC_9LATIN,"I6/203",AL_ROMAN,"latin9-iso", /* 8 */ "ISO 10646 / Unicode UCS-2",64000,TC_UCS2,"I162",AL_UNIV,"ucs2", /* 9 */ "ISO 10646 / Unicode UTF-8",64000,TC_UTF8,"I190",AL_UNIV,"utf8", /* 10 */ "",0,0,"",0,"" }; int ntcsets = (sizeof(tcsinfo) / sizeof(struct csinfo)) - 1; struct keytab tcstab[] = { /* Keyword table for */ "ascii", TC_USASCII, 0, /* SET TRANSFER CHARACTER-SET */ #ifdef CYRILLIC "cyrillic-iso", TC_CYRILL, 0, #endif /* CYRILLIC */ #ifdef GREEK "elot928-greek", TC_GREEK, CM_INV, #endif /* GREEK */ #ifdef KANJI "euc-jp", TC_JEUC, 0, #endif /* KANJI */ #ifdef GREEK "greek-iso", TC_GREEK, 0, #endif /* GREEK */ #ifdef HEBREW "hebrew-iso", TC_HEBREW, 0, #endif /* HEBREW */ #ifdef UNICODE "iso-10646-ucs-2", TC_UCS2, CM_INV, /* ISO 10646 / Unicode UCS-2 */ #endif /* UNICODE */ "iso-8859-1", TC_1LATIN, CM_INV, /* ISO Latin Alphabet 1 */ "iso-8859-15", TC_9LATIN, CM_INV, /* ISO Latin Alphabet 9 (yes) */ "iso-8859-2", TC_2LATIN, CM_INV, /* ISO Latin Alphabet 2 */ #ifdef CYRILLIC "iso-8859-5", TC_CYRILL, CM_INV, /* ISO Latin/Cyrillic Alphabet */ #endif /* CYRILLIC */ #ifdef GREEK "iso-8859-7", TC_GREEK, CM_INV, /* ISO 8859-7 Latin/Greek */ #endif /* GREEK */ #ifdef HEBREW "iso-8859-8", TC_HEBREW, CM_INV, /* ISO Latin/Hebrew */ #endif /* HEBREW */ #ifdef KANJI "japanese-euc", TC_JEUC, CM_INV, #endif /* KANJI */ "l", TC_1LATIN, CM_ABR|CM_INV, "la", TC_1LATIN, CM_ABR|CM_INV, "lat", TC_1LATIN, CM_ABR|CM_INV, "lati", TC_1LATIN, CM_ABR|CM_INV, "latin", TC_1LATIN, CM_ABR|CM_INV, "latin1-iso", TC_1LATIN, 0, #ifdef LATIN2 "latin2-iso", TC_2LATIN, 0, #endif /* LATIN2 */ "latin9-iso", TC_9LATIN, 0, "transparent", TC_TRANSP, 0, #ifdef UNICODE "ucs2", TC_UCS2, 0, #endif /* UNICODE */ "us-ascii", TC_USASCII, CM_INV, "usascii", TC_USASCII, CM_INV, #ifdef UNICODE "utf-8", TC_UTF8, CM_INV, "utf8", TC_UTF8, 0, #endif /* UNICODE */ "", 0, 0 }; int ntcs = (sizeof(tcstab) / sizeof(struct keytab)) - 1; /* File character set information structure, indexed by character set code, */ /* as defined in ckuxla.h. This table must be in order of file character */ /* set number! */ struct csinfo fcsinfo[] = { /* File character set information... */ /* Descriptive Name Size Designator */ "US ASCII", 128, FC_USASCII, NULL, AL_ROMAN, "ascii", "British/UK ISO-646", 128, FC_UKASCII, NULL, AL_ROMAN, "british", "Dutch ISO-646", 128, FC_DUASCII, NULL, AL_ROMAN, "dutch", "Finnish ISO-646", 128, FC_FIASCII, NULL, AL_ROMAN, "finnish", "French ISO-646", 128, FC_FRASCII, NULL, AL_ROMAN, "french", "Canadian-French NRC", 128, FC_FCASCII, NULL, AL_ROMAN, "canadian-french", "German ISO-646", 128, FC_GEASCII, NULL, AL_ROMAN, "german", "Hungarian ISO-646", 128, FC_HUASCII, NULL, AL_ROMAN, "hungarian", "Italian ISO-646", 128, FC_ITASCII, NULL, AL_ROMAN, "italian", "Norwegian/Danish ISO-646",128,FC_NOASCII,NULL,AL_ROMAN,"norwegian/danish", "Portuguese ISO-646", 128, FC_POASCII, NULL, AL_ROMAN,"portuguese", "Spanish ISO-646", 128, FC_SPASCII, NULL, AL_ROMAN, "spanish", "Swedish ISO-646", 128, FC_SWASCII, NULL, AL_ROMAN, "swedish", "Swiss NRC", 128, FC_CHASCII, NULL, AL_ROMAN, "swiss", "ISO 8859-1 Latin-1", 256, FC_1LATIN, NULL, AL_ROMAN,"latin1-iso", "ISO 8859-2 Latin-2", 256, FC_2LATIN, NULL, AL_ROMAN,"latin2-iso", "DEC Multinational", 256, FC_DECMCS, NULL,AL_ROMAN,"dec-multinational", "NeXT Multinational", 256, FC_NEXT, NULL,AL_ROMAN,"next-multinational", "PC Code Page 437", 256, FC_CP437, NULL, AL_ROMAN,"cp437", "PC Code Page 850", 256, FC_CP850, NULL, AL_ROMAN,"cp850", "PC Code Page 852", 256, FC_CP852, NULL, AL_ROMAN,"cp852", "Apple Macintosh Latin", 256, FC_APPQD, NULL, AL_ROMAN,"macintosh-latin", "Data General International",256,FC_DGMCS,NULL,AL_ROMAN,"dg-international", "Hewlett Packard Roman8", 256, FC_HPR8, NULL, AL_ROMAN, "hp-roman8", "ISO 8859-5 Latin/Cyrillic", 256, FC_CYRILL, NULL, AL_CYRIL,"cyrillic-iso", "CP866 Cyrillic", 256, FC_CP866, NULL, AL_CYRIL,"cp866", "Short KOI", 128, FC_KOI7, NULL, AL_CYRIL,"short-koi", "Old KOI-8 Cyrillic", 256, FC_KOI8, NULL, AL_CYRIL,"koi8-cyrillic", "Japanese JIS7", 16384, FC_JIS7, NULL, AL_JAPAN, "jis7-kanji", "Japanese Shift JIS", 16384, FC_SHJIS, NULL, AL_JAPAN, "shift-jis-kanji", "Japanese EUC", 16384, FC_JEUC, NULL, AL_JAPAN, "euc-jp", "Japanese DEC Kanji", 16384, FC_JDEC, NULL, AL_JAPAN, "dec-kanji", "Hebrew-7 DEC", 128, FC_HE7, NULL, AL_HEBREW, "hebrew-7", "ISO 8859-8 Latin/Hebrew",256, FC_HEBREW, NULL, AL_HEBREW, "hebrew-iso", "CP862 Hebrew", 256, FC_CP862, NULL, AL_HEBREW, "cp862-hebrew", "ELOT 927 Greek", 128, FC_ELOT, NULL, AL_GREEK, "elot927-greek", "ISO 8859-7 Latin/Greek", 256, FC_GREEK, NULL, AL_GREEK, "greek-iso", "CP869 Greek", 256, FC_CP869, NULL, AL_GREEK, "cp869-greek", "ISO 8859-15 Latin-9", 256, FC_9LATIN, NULL, AL_ROMAN, "latin9-iso", "PC Code Page 858", 256, FC_CP850, NULL, AL_ROMAN, "cp858", "PC Code Page 855", 256, FC_CP855, NULL, AL_CYRIL, "cp855-cyrillic", "Windows Code Page 1251", 256, FC_CP1251, NULL, AL_CYRIL, "cp1251-cyrillic", "Bulgarian PC Code Page", 256, FC_BULGAR, NULL, AL_CYRIL, "bulgaria-pc", "Windows Code Page 1250", 256, FC_CP1250, NULL, AL_ROMAN, "cp1250", "Polish Mazovia PC Code Page", 256, FC_MAZOVIA, NULL, AL_ROMAN, "mazovia-pc", "ISO 10646 / Unicode UCS-2", 64000, FC_UCS2, NULL, AL_UNIV, "ucs2", "ISO 10646 / Unicode UTF-8", 64000, FC_UCS2, NULL, AL_UNIV, "utf8", "KOI8-R Russian+Boxdrawing",256, FC_KOI8R, NULL, AL_CYRIL,"koi8r", "KOI8-U Ukrainian+Boxdrawing",256,FC_KOI8U, NULL, AL_CYRIL,"koi8u", "Windows Code Page 1252", 256, FC_CP1252, NULL, AL_ROMAN, "cp1252", "",0,0,NULL,0,NULL }; /* Local file character sets */ /* Includes 7-bit National Replacement Character Sets of ISO 646 */ /* Plus ISO Latin-1, DEC Multinational Character Set (MCS), NeXT char set, */ /* Various PC and Windows code pages, etc. */ /* As of C-Kermit 9.0 MIME names are included as invisible synomyms for */ /* those character sets that have MIME names. */ struct keytab fcstab[] = { /* Keyword table for 'set file character-set' */ /* IMPORTANT: This table is replicated below as ttcstab (terminal character set table). The only differences are the addition of TRANSPARENT and the removal of the Kanji sets, which are not supported for terminal emulation. If you make changes to this table, also change ttcstab. */ /* Keyword Value Flags */ "apple-quickdraw", FC_APPQD, CM_INV, /* Apple Quickdraw */ "ascii", FC_USASCII, 0, /* ASCII */ "british", FC_UKASCII, 0, /* British NRC */ "bulgaria-pc", FC_BULGAR, 0, /* Bulgarian PC Code Page */ "canadian-french", FC_FCASCII, 0, /* French Canadian NRC */ #ifdef LATIN2 "cp1250", FC_CP1250, 0, /* Windows CP 1250 */ #endif /* LATIN2 */ #ifdef CYRILLIC "cp1251-cyrillic", FC_CP1251, 0, /* Windows CP 1251 */ #endif /* CYRILLIC */ "cp1252", FC_CP1252, 0, /* Windows CP 1252 */ "cp437", FC_CP437, 0, /* PC CP437 */ "cp850", FC_CP850, 0, /* PC CP850 */ #ifdef LATIN2 "cp852", FC_CP852, 0, /* PC CP852 */ #endif /* LATIN2 */ #ifdef CYRILLIC "cp855-cyrillic", FC_CP855, 0, /* PC CP855 */ #endif /* CYRILLIC */ "cp858", FC_CP858, 0, /* PC CP858 */ #ifdef HEBREW "cp862-hebrew", FC_CP862, 0, /* PC CP862 */ #endif /* HEBREW */ #ifdef CYRILLIC "cp866-cyrillic", FC_CP866, 0, /* CP866 Cyrillic */ #endif /* CYRILLIC */ #ifdef GREEK "cp869-greek", FC_CP869, 0, /* CP869 Greek */ #endif /* GREEK */ #ifdef CYRILLIC "cyrillic-iso", FC_CYRILL, 0, /* ISO Latin/Cyrillic Alphabet */ #endif /* CYRILLIC */ "danish", FC_NOASCII, 0, /* Norwegian and Danish NRC */ #ifdef KANJI "dec-kanji", FC_JDEC, 0, /* Japanese DEC Kanji */ #endif /* KANJI */ "dec-mcs", FC_DECMCS, CM_INV, /* DEC multinational char set */ "dec-multinational", FC_DECMCS, 0, /* DEC multinational character set */ "dg-international", FC_DGMCS, 0, /* Data General multinational */ "dutch", FC_DUASCII, 0, /* Dutch NRC */ #ifdef GREEK "elot927-greek", FC_ELOT, 0, /* ELOT 927 Greek */ "elot928-greek", FC_GREEK, 0, /* Same as ISO 8859-7 Latin/Greek */ #endif /* GREEK */ #ifdef KANJI "euc-jp", FC_JEUC, 0, /* Japanese EUC */ #endif /* KANJI */ "finnish", FC_FIASCII, 0, /* Finnish NRC */ "french", FC_FRASCII, 0, /* French NRC */ "fr-canadian", FC_FCASCII, CM_INV, /* French Canadian NRC */ "german", FC_GEASCII, 0, /* German NRC */ #ifdef GREEK "greek-iso", FC_GREEK, 0, /* ISO 8859-7 Latin/Greek */ #endif /* GREEK */ #ifdef HEBREW "he", FC_HEBREW, CM_ABR|CM_INV, "heb", FC_HEBREW, CM_ABR|CM_INV, "hebr", FC_HEBREW, CM_ABR|CM_INV, "hebre", FC_HEBREW, CM_ABR|CM_INV, "hebrew", FC_HEBREW, CM_ABR|CM_INV, "hebrew-7", FC_HE7, 0, /* DEC 7-Bit Hebrew */ "hebrew-iso", FC_HEBREW, 0, /* ISO Latin/Hebrew */ #endif /* HEBREW */ "hp-roman8", FC_HPR8, 0, /* Hewlett Packard Roman8 */ "hungarian", FC_HUASCII, 0, /* Hungarian NRC */ "ibm437", FC_CP437, CM_INV, /* PC CP437 */ "ibm850", FC_CP850, CM_INV, /* PC CP850 (not in MIME) */ #ifdef LATIN2 "ibm852", FC_CP852, CM_INV, /* PC CP852 */ #endif /* LATIN2 */ #ifdef CYRILLIC "ibm855", FC_CP855, CM_INV, /* PC CP855 */ #endif /* CYRILLIC */ "ibm858", FC_CP858, CM_INV, /* PC CP858 (not in MIME) */ #ifdef HEBREW "ibm862", FC_CP862, CM_INV, /* PC CP862 (not in MIME) */ #endif /* HEBREW */ #ifdef CYRILLIC "ibm866", FC_CP866, CM_INV, /* CP866 Cyrillic */ #endif /* CYRILLIC */ #ifdef GREEK "ibm869", FC_CP869, CM_INV, /* CP869 Greek */ #endif /* GREEK */ #ifdef UNICODE "iso-10646-ucs-2", FC_UCS2, CM_INV, /* ISO 10646 / Unicode UCS-2 */ #endif /* UNICODE */ "iso-8859-1", FC_1LATIN, CM_INV, /* ISO Latin Alphabet 1 */ "iso-8859-15", FC_9LATIN, CM_INV, /* ISO Latin Alphabet 9 (yes) */ "iso-8859-2", FC_2LATIN, CM_INV, /* ISO Latin Alphabet 2 */ #ifdef CYRILLIC "iso-8859-5", FC_CYRILL, CM_INV, /* ISO Latin/Cyrillic Alphabet */ #endif /* CYRILLIC */ #ifdef GREEK "iso-8859-7", FC_GREEK, CM_INV, /* ISO 8859-7 Latin/Greek */ #endif /* GREEK */ #ifdef HEBREW "iso-8859-8", FC_HEBREW, CM_INV, /* ISO Latin/Hebrew */ #endif /* HEBREW */ #ifdef KANJI "iso2022jp-kanji", FC_JIS7, CM_INV, /* Synonym for JIS-7 */ #endif /* KANJI */ "iso646-gb", FC_UKASCII, CM_INV, /* British NRC */ "iso646-ca", FC_FCASCII, CM_INV, /* French Canadian NRC */ "iso646-de", FC_GEASCII, CM_INV, /* German NRC */ "iso646-dk", FC_NOASCII, CM_INV, /* Norwegian and Danish NRC */ "iso646-es", FC_SPASCII, CM_INV, /* Spanish NRC */ "iso646-fi", FC_FIASCII, CM_INV, /* Finnish NRC */ "iso646-fr", FC_FRASCII, CM_INV, /* French NRC */ "iso646-hu", FC_HUASCII, CM_INV, /* Hungarian NRC */ "iso646-it", FC_ITASCII, CM_INV, /* Italian NRC */ "iso646-no", FC_NOASCII, CM_INV, /* Norwegian and Danish NRC */ "iso646-po", FC_POASCII, CM_INV, /* Portuguese NRC */ "iso646-se", FC_SWASCII, CM_INV, /* Swedish NRC */ "italian", FC_ITASCII, CM_INV, /* Italian NRC */ #ifdef KANJI "japanese-euc", FC_JEUC, CM_INV, /* Japanese EUC */ "jis7-kanji", FC_JIS7, 0, /* Japanese JIS7 7bit code */ #endif /* KANJI */ #ifdef CYRILLIC "k", FC_KOI8, CM_ABR|CM_INV, "ko", FC_KOI8, CM_ABR|CM_INV, "koi", FC_KOI8, CM_ABR|CM_INV, "koi7", FC_KOI7, 0, /* Short KOI Cyrillic */ "koi8", FC_KOI8, 0, /* Old KOI-8 Cyrillic */ "koi8-e", FC_KOI8, CM_INV, /* Old KOI-8 Cyrillic */ "koi8-cyrillic", FC_KOI8, CM_INV, "koi8-r", FC_KOI8R, CM_INV, /* KOI8-R RFC1489 */ "koi8-u", FC_KOI8U, CM_INV, /* KOI8-U RFC2319 */ "koi8r", FC_KOI8R, 0, /* KOI8-R RFC1489 */ "koi8u", FC_KOI8U, 0, /* KOI8-U RFC2319 */ #endif /* CYRILLIC */ "l", FC_1LATIN, CM_ABR|CM_INV, "la", FC_1LATIN, CM_ABR|CM_INV, "lat", FC_1LATIN, CM_ABR|CM_INV, "lati", FC_1LATIN, CM_ABR|CM_INV, "latin", FC_1LATIN, CM_ABR|CM_INV, "latin1-iso", FC_1LATIN, 0, /* ISO Latin Alphabet 1 */ #ifdef LATIN2 "latin2-iso", FC_2LATIN, 0, /* ISO Latin Alphabet 2 */ #endif /* LATIN2 */ "latin9-iso", FC_9LATIN, 0, /* ISO Latin Alphabet 9 */ "macintosh-latin", FC_APPQD, 0, /* "Extended Mac Latin" */ #ifdef LATIN2 "mazovia-pc", FC_MAZOVIA, 0, /* Polish Mazovia PC code page */ #endif /* LATIN2 */ "next-multinational", FC_NEXT, 0, /* NeXT workstation */ "norwegian", FC_NOASCII, 0, /* Norwegian and Danish NRC */ "portuguese", FC_POASCII, 0, /* Portuguese NRC */ #ifdef KANJI "shift-jis-kanji", FC_SHJIS, 0, /* Japanese Kanji Shift-JIS */ "shift_jis", FC_SHJIS, CM_INV, /* Japanese Kanji Shift-JIS */ #endif /* KANJI */ #ifdef CYRILLIC "short-koi", FC_KOI7, 0, /* Short KOI Cyrillic */ #endif /* CYRILLIC */ "spanish", FC_SPASCII, 0, /* Spanish NRC */ "swedish", FC_SWASCII, 0, /* Swedish NRC */ "swiss", FC_CHASCII, 0, /* Swiss NRC */ #ifdef UNICODE "ucs2", FC_UCS2, 0, /* ISO 10646 / Unicode UCS-2 */ #endif /* UNICODE */ "us-ascii", FC_USASCII, CM_INV, /* MIME */ "usascii", FC_USASCII, CM_INV, #ifdef UNICODE "utf-8", FC_UTF8, CM_INV, /* ISO 10646 / Unicode UTF-8 */ "utf8", FC_UTF8, 0, /* ISO 10646 / Unicode UTF-8 */ #endif /* UNICODE */ #ifdef LATIN2 "windows-1250", FC_CP1250, CM_INV, /* Windows CP 1250 */ #endif /* LATIN2 */ #ifdef CYRILLIC "windows-1251", FC_CP1251, CM_INV, /* Windows CP 1251 */ #endif /* CYRILLIC */ "windows-1252", FC_CP1252, CM_INV, /* Windows CP 1252 */ "", 0, 0 }; int nfilc = (sizeof(fcstab) / sizeof(struct keytab)) - 1; struct keytab ttcstab[] = { /* Keyword table for SET TERMINAL CHARACTER-SET */ /* IMPORTANT: This table is a replica of fcstab, immediately above, with the addition of TRANSPARENT and deletion of the Japanese sets. If you make changes to this table, make the corresponding changes to fcstab. */ /* Keyword Value Flags */ "apple-quickdraw", FC_APPQD, CM_INV, /* Apple Quickdraw */ "ascii", FC_USASCII, 0, /* ASCII */ "british", FC_UKASCII, 0, /* British NRC */ "bulgaria-pc", FC_BULGAR, 0, /* Bulgarian PC Code Page */ "canadian-french", FC_FCASCII, 0, /* French Canadian NRC */ #ifdef LATIN2 "cp1250", FC_CP1250, 0, /* Windows CP 1250 */ #endif /* LATIN2 */ #ifdef CYRILLIC "cp1251-cyrillic", FC_CP1251, 0, /* Windows CP 1251 */ #endif /* CYRILLIC */ "cp1252", FC_CP1252, 0, /* Windows CP 1252 */ "cp437", FC_CP437, 0, /* PC CP437 */ "cp850", FC_CP850, 0, /* PC CP850 */ #ifdef LATIN2 "cp852", FC_CP852, 0, /* PC CP852 */ #endif /* LATIN2 */ #ifdef CYRILLIC "cp855-cyrillic", FC_CP855, 0, /* PC CP855 */ #endif /* CYRILLIC */ "cp858", FC_CP858, 0, /* PC CP858 */ #ifdef HEBREW "cp862-hebrew", FC_CP862, 0, /* PC CP862 */ #endif /* HEBREW */ #ifdef CYRILLIC "cp866-cyrillic", FC_CP866, 0, /* CP866 Cyrillic */ #endif /* CYRILLIC */ #ifdef GREEK "cp869-greek", FC_CP869, 0, /* CP869 Greek */ #endif /* GREEK */ #ifdef CYRILLIC "cyrillic-iso", FC_CYRILL, 0, /* ISO Latin/Cyrillic Alphabet */ #endif /* CYRILLIC */ "danish", FC_NOASCII, 0, /* Norwegian and Danish NRC */ "dec-mcs", FC_DECMCS, CM_INV, /* DEC multinational char set */ "dec-multinational", FC_DECMCS, 0, /* DEC multinational character set */ "dg-international", FC_DGMCS, 0, /* Data General multinational */ "dutch", FC_DUASCII, 0, /* Dutch NRC */ #ifdef GREEK "elot927-greek", FC_ELOT, 0, /* ELOT 927 Greek */ "elot928-greek", FC_GREEK, 0, /* Same as ISO 8859-7 Latin/Greek */ #endif /* GREEK */ "finnish", FC_FIASCII, 0, /* Finnish NRC */ "french", FC_FRASCII, 0, /* French NRC */ "fr-canadian", FC_FCASCII, CM_INV, /* French Canadian NRC */ "german", FC_GEASCII, 0, /* German NRC */ #ifdef GREEK "greek-iso", FC_GREEK, 0, /* ISO 8859-7 Latin/Greek */ #endif /* GREEK */ #ifdef HEBREW "he", FC_HEBREW, CM_ABR|CM_INV, "heb", FC_HEBREW, CM_ABR|CM_INV, "hebr", FC_HEBREW, CM_ABR|CM_INV, "hebre", FC_HEBREW, CM_ABR|CM_INV, "hebrew", FC_HEBREW, CM_ABR|CM_INV, "hebrew-7", FC_HE7, 0, /* DEC 7-Bit Hebrew */ "hebrew-iso", FC_HEBREW, 0, /* ISO Latin/Hebrew */ #endif /* HEBREW */ "hp-roman8", FC_HPR8, 0, /* Hewlett Packard Roman8 */ "hungarian", FC_HUASCII, 0, /* Hungarian NRC */ "ibm437", FC_CP437, CM_INV, /* PC CP437 */ "ibm850", FC_CP850, CM_INV, /* PC CP850 (not in MIME) */ #ifdef LATIN2 "ibm852", FC_CP852, CM_INV, /* PC CP852 */ #endif /* LATIN2 */ #ifdef CYRILLIC "ibm855", FC_CP855, CM_INV, /* PC CP855 */ #endif /* CYRILLIC */ "ibm858", FC_CP858, CM_INV, /* PC CP858 (not in MIME) */ #ifdef HEBREW "ibm862", FC_CP862, CM_INV, /* PC CP862 (not in MIME) */ #endif /* HEBREW */ #ifdef CYRILLIC "ibm866", FC_CP866, CM_INV, /* CP866 Cyrillic */ #endif /* CYRILLIC */ #ifdef GREEK "ibm869", FC_CP869, CM_INV, /* CP869 Greek */ #endif /* GREEK */ #ifdef UNICODE "iso-10646-ucs-2", FC_UCS2, CM_INV, /* ISO 10646 / Unicode UCS-2 */ #endif /* UNICODE */ "iso-8859-1", FC_1LATIN, CM_INV, /* ISO Latin Alphabet 1 */ "iso-8859-15", FC_9LATIN, CM_INV, /* ISO Latin Alphabet 9 (yes) */ "iso-8859-2", FC_2LATIN, CM_INV, /* ISO Latin Alphabet 2 */ #ifdef CYRILLIC "iso-8859-5", FC_CYRILL, CM_INV, /* ISO Latin/Cyrillic Alphabet */ #endif /* CYRILLIC */ #ifdef GREEK "iso-8859-7", FC_GREEK, CM_INV, /* ISO 8859-7 Latin/Greek */ #endif /* GREEK */ #ifdef HEBREW "iso-8859-8", FC_HEBREW, CM_INV, /* ISO Latin/Hebrew */ #endif /* HEBREW */ "iso646-gb", FC_UKASCII, CM_INV, /* British NRC */ "iso646-ca", FC_FCASCII, CM_INV, /* French Canadian NRC */ "iso646-de", FC_GEASCII, CM_INV, /* German NRC */ "iso646-dk", FC_NOASCII, CM_INV, /* Norwegian and Danish NRC */ "iso646-es", FC_SPASCII, CM_INV, /* Spanish NRC */ "iso646-fi", FC_FIASCII, CM_INV, /* Finnish NRC */ "iso646-fr", FC_FRASCII, CM_INV, /* French NRC */ "iso646-hu", FC_HUASCII, CM_INV, /* Hungarian NRC */ "iso646-it", FC_ITASCII, CM_INV, /* Italian NRC */ "iso646-no", FC_NOASCII, CM_INV, /* Norwegian and Danish NRC */ "iso646-po", FC_POASCII, CM_INV, /* Portuguese NRC */ "iso646-se", FC_SWASCII, CM_INV, /* Swedish NRC */ "italian", FC_ITASCII, CM_INV, /* Italian NRC */ #ifdef CYRILLIC "k", FC_KOI8, CM_ABR|CM_INV, "ko", FC_KOI8, CM_ABR|CM_INV, "koi", FC_KOI8, CM_ABR|CM_INV, "koi7", FC_KOI7, 0, /* Short KOI Cyrillic */ "koi8", FC_KOI8, 0, /* Old KOI-8 Cyrillic */ "koi8-e", FC_KOI8, CM_INV, /* Old KOI-8 Cyrillic */ "koi8-cyrillic", FC_KOI8, CM_INV, "koi8-r", FC_KOI8R, CM_INV, /* KOI8-R RFC1489 */ "koi8-u", FC_KOI8U, CM_INV, /* KOI8-U RFC2319 */ "koi8r", FC_KOI8R, 0, /* KOI8-R RFC1489 */ "koi8u", FC_KOI8U, 0, /* KOI8-U RFC2319 */ #endif /* CYRILLIC */ "l", FC_1LATIN, CM_ABR|CM_INV, "la", FC_1LATIN, CM_ABR|CM_INV, "lat", FC_1LATIN, CM_ABR|CM_INV, "lati", FC_1LATIN, CM_ABR|CM_INV, "latin", FC_1LATIN, CM_ABR|CM_INV, "latin1-iso", FC_1LATIN, 0, /* ISO Latin Alphabet 1 */ #ifdef LATIN2 "latin2-iso", FC_2LATIN, 0, /* ISO Latin Alphabet 2 */ #endif /* LATIN2 */ "latin9-iso", FC_9LATIN, 0, /* ISO Latin Alphabet 9 */ "macintosh-latin", FC_APPQD, 0, /* "Extended Mac Latin" */ #ifdef LATIN2 "mazovia-pc", FC_MAZOVIA, 0, /* Polish Mazovia PC code page */ #endif /* LATIN2 */ "next-multinational", FC_NEXT, 0, /* NeXT workstation */ "norwegian", FC_NOASCII, 0, /* Norwegian and Danish NRC */ "portuguese", FC_POASCII, 0, /* Portuguese NRC */ #ifdef CYRILLIC "short-koi", FC_KOI7, 0, /* Short KOI Cyrillic */ #endif /* CYRILLIC */ "spanish", FC_SPASCII, 0, /* Spanish NRC */ "swedish", FC_SWASCII, 0, /* Swedish NRC */ "swiss", FC_CHASCII, 0, /* Swiss NRC */ "transparent", FC_TRANSP, 0, /* Transparent */ #ifdef UNICODE "ucs2", FC_UCS2, 0, /* ISO 10646 / Unicode UCS-2 */ #endif /* UNICODE */ "us-ascii", FC_USASCII, CM_INV, /* MIME */ "usascii", FC_USASCII, CM_INV, #ifdef UNICODE "utf-8", FC_UTF8, CM_INV, /* ISO 10646 / Unicode UTF-8 */ "utf8", FC_UTF8, 0, /* ISO 10646 / Unicode UTF-8 */ #endif /* UNICODE */ #ifdef LATIN2 "windows-1250", FC_CP1250, CM_INV, /* Windows CP 1250 */ #endif /* LATIN2 */ #ifdef CYRILLIC "windows-1251", FC_CP1251, CM_INV, /* Windows CP 1251 */ #endif /* CYRILLIC */ "windows-1252", FC_CP1252, CM_INV, /* Windows CP 1252 */ "", 0, 0 }; int ntermc = (sizeof(ttcstab) / sizeof(struct keytab)) - 1; /* This table contains the equivalent FCS number for each TCS. */ /* If the TC_xxx symbol definitions are ever changed, fix this table. */ /* Ditto if another TCS is added. */ int cseqtab[MAXTCSETS+1] = { /* TCS/FCS equivalency table */ -1, /* 0 = Transparent */ FC_USASCII, /* 1 = ASCII */ FC_1LATIN, /* 2 = Latin-1 */ FC_2LATIN, /* 3 = Latin-2 */ FC_CYRILL, /* 4 = Latin/Cyrillic */ FC_JEUC, /* 5 = Japanese EUC */ FC_HEBREW, /* 6 = Latin/Hebrew */ FC_GREEK, /* 7 = Latin/Greek */ FC_9LATIN, /* 8 = Latin-9 */ FC_UCS2, /* 9 = UCS-2 */ FC_UTF8 /* 10 = UTF-8 */ }; /* Languages: This table allows C-Kermit to have a SET LANGUAGE command to apply special language-specific rules when translating from a character set that contains national characters into plain ASCII, like German umlaut-a becomes ae. Originally, I thought it would be a good idea to let SET LANGUAGE also select an appropriate FILE CHARACTER-SET and TRANSFER CHARACTER-SET automatically, and these are included in the langinfo structure. Later I realized that this was a bad idea. Any particular language (e.g. Dutch) can be represented by many different and incompatible character sets. (But we could use the new (1998) ASSOCIATE command for this...) */ struct langinfo langs[] = { /* Language code File Charset Xfer Charset Name */ L_USASCII, FC_USASCII, TC_USASCII, "ASCII (American English)", L_DANISH, FC_NOASCII, TC_1LATIN, "Danish", L_DUTCH, FC_DUASCII, TC_1LATIN, "Dutch", L_FINNISH, FC_FIASCII, TC_1LATIN, "Finnish", L_FRENCH, FC_FRASCII, TC_1LATIN, "French", L_GERMAN, FC_GEASCII, TC_1LATIN, "German", #ifdef GREEK L_GREEK, FC_GREEK, TC_GREEK, "Greek", #endif /* GREEK */ #ifdef HEBREW L_HEBREW, FC_HEBREW, TC_HEBREW, "Hebrew", #endif /* HEBREW */ L_HUNGARIAN, FC_HUASCII, TC_2LATIN, "Hungarian", L_ICELANDIC, FC_USASCII, TC_1LATIN, "Icelandic", L_ITALIAN, FC_ITASCII, TC_1LATIN, "Italian", #ifdef KANJI L_JAPANESE, FC_JEUC, TC_JEUC, "Japanese", #endif /* KANJI */ L_NORWEGIAN, FC_NOASCII, TC_1LATIN, "Norwegian", L_PORTUGUESE, FC_POASCII, TC_1LATIN, "Portuguese", #ifdef CYRILLIC L_RUSSIAN, FC_CP866, TC_CYRILL, "Russian", #endif /* CYRILLIC */ L_SPANISH, FC_SPASCII, TC_1LATIN, "Spanish", L_SWEDISH, FC_SWASCII, TC_1LATIN, "Swedish", L_SWISS, FC_CHASCII, TC_1LATIN, "Swiss" }; int nlangs = (sizeof(langs) / sizeof(struct langinfo)); /* Keyword table for the SET LANGUAGE command. Only a few of these (German, Scandinavian, etc) actually do anything. The language is used to invoke special translation rules when converting from an 8-bit character set to ASCII; for example, German u-diaeresis becomes "ue", Dutch y-diaeresis becomes "ij". Languages without associated rules are invisible (CM_INV). */ struct keytab lngtab[] = { "ascii", L_USASCII, CM_INV, "danish", L_DANISH, 0, "dutch", L_DUTCH, 0, "english", L_USASCII, CM_INV, "finnish", L_FINNISH, 0, "french", L_FRENCH, 0, "german", L_GERMAN, 0, #ifdef GREEK "greek", L_GREEK, CM_INV, #endif /* GREEK */ #ifdef HEBREW "hebrew", L_HEBREW, CM_INV, #endif /* HEBREW */ "hungarian", L_HUNGARIAN, CM_INV, "icelandic", L_ICELANDIC, 0, "italian", L_ITALIAN, CM_INV, #ifdef KANJI "japanese", L_JAPANESE, CM_INV, #endif /* KANJI */ "norwegian", L_NORWEGIAN, 0, "none", L_USASCII, 0, "portuguese", L_PORTUGUESE, CM_INV, #ifdef CYRILLIC "russian", L_RUSSIAN, 0, #endif /* CYRILLIC */ "spanish", L_SPANISH, CM_INV, "swedish", L_SWEDISH, 0, #ifdef CYRILLIC "ukrainian", L_RUSSIAN, 0, #endif /* CYRILLIC */ "", 0, 0 }; int nlng = (sizeof(lngtab) / sizeof(struct keytab)) - 1; /* how many */ /* Translation tables ... */ /* For each pair of (transfer,file) character sets, we need two translation functions, one for sending, one for receiving. */ /* Here is the first table, Latin-1 to ASCII, fully annotated... This one is absolutely NOT invertible, since we're going from an 8-bit set to a 7-bit set. Accented letters are mapped to unaccented equivalents, C1 control characters are all translated to "?", etc. */ CONST CHAR yl1as[] = { /* ISO 8859-1 Latin Alphabet 1 to US ASCII */ /* Source character Description => Translation */ /* Dec row/col Set */ 0, /* 000 00/00 C0 NUL Ctrl-@ => (self) */ 1, /* 001 00/01 C0 SOH Ctrl-A => (self) */ 2, /* 002 00/02 C0 STX Ctrl-B => (self) */ 3, /* 003 00/03 C0 ETX Ctrl-C => (self) */ 4, /* 004 00/04 C0 EOT Ctrl-D => (self) */ 5, /* 005 00/05 C0 ENQ Ctrl-E => (self) */ 6, /* 006 00/06 C0 ACK Ctrl-F => (self) */ 7, /* 007 00/07 C0 BEL Ctrl-G => (self) */ 8, /* 008 00/08 C0 BS Ctrl-H => (self) */ 9, /* 009 00/09 C0 HT Ctrl-I => (self) */ 10, /* 010 00/10 C0 LF Ctrl-J => (self) */ 11, /* 011 00/11 C0 VT Ctrl-K => (self) */ 12, /* 012 00/12 C0 FF Ctrl-L => (self) */ 13, /* 013 00/13 C0 CR Ctrl-M => (self) */ 14, /* 014 00/14 C0 SO Ctrl-N => (self) */ 15, /* 015 00/15 C0 SI Ctrl-O => (self) */ 16, /* 016 01/00 C0 DLE Ctrl-P => (self) */ 17, /* 017 01/01 C0 DC1 Ctrl-Q => (self) */ 18, /* 018 01/02 C0 DC2 Ctrl-R => (self) */ 19, /* 019 01/03 C0 DC3 Ctrl-S => (self) */ 20, /* 020 01/04 C0 DC4 Ctrl-T => (self) */ 21, /* 021 01/05 C0 NAK Ctrl-U => (self) */ 22, /* 022 01/06 C0 SYN Ctrl-V => (self) */ 23, /* 023 01/07 C0 ETB Ctrl-W => (self) */ 24, /* 024 01/08 C0 CAN Ctrl-X => (self) */ 25, /* 025 01/09 C0 EM Ctrl-Y => (self) */ 26, /* 026 01/10 C0 SUB Ctrl-Z => (self) */ 27, /* 027 01/11 C0 ESC Ctrl-[ => (self) */ 28, /* 028 01/12 C0 FS Ctrl-\ => (self) */ 29, /* 029 01/13 C0 GS Ctrl-] => (self) */ 30, /* 030 01/14 C0 RS Ctrl-^ => (self) */ 31, /* 031 01/15 C0 US Ctrl-_ => (self) */ 32, /* 032 02/00 SP Space => (self) */ 33, /* 033 02/01 G0 ! Exclamation mark => (self) */ 34, /* 034 02/02 G0 " Doublequote => (self) */ 35, /* 035 02/03 G0 # Number sign => (self) */ 36, /* 036 02/04 G0 $ Dollar sign => (self) */ 37, /* 037 02/05 G0 % Percent sign => (self) */ 38, /* 038 02/06 G0 & Ampersand => (self) */ 39, /* 039 02/07 G0 ' Apostrophe => (self) */ 40, /* 040 02/08 G0 ( Left parenthesis => (self) */ 41, /* 041 02/09 G0 ) Right parenthesis => (self) */ 42, /* 042 02/10 G0 * Asterisk => (self) */ 43, /* 043 02/11 G0 + Plus sign => (self) */ 44, /* 044 02/12 G0 , Comma => (self) */ 45, /* 045 02/13 G0 - Hyphen, minus sign => (self) */ 46, /* 046 02/14 G0 . Period, full stop => (self) */ 47, /* 047 02/15 G0 / Slash, solidus => (self) */ 48, /* 048 03/00 G0 0 Digit 0 => (self) */ 49, /* 049 03/01 G0 1 Digit 1 => (self) */ 50, /* 050 03/02 G0 2 Digit 2 => (self) */ 51, /* 051 03/03 G0 3 Digit 3 => (self) */ 52, /* 052 03/04 G0 4 Digit 4 => (self) */ 53, /* 053 03/05 G0 5 Digit 5 => (self) */ 54, /* 054 03/06 G0 6 Digit 6 => (self) */ 55, /* 055 03/07 G0 7 Digit 7 => (self) */ 56, /* 056 03/08 G0 8 Digit 8 => (self) */ 57, /* 057 03/09 G0 9 Digit 9 => (self) */ 58, /* 058 03/10 G0 : Colon => (self) */ 59, /* 059 03/11 G0 ; Semicolon => (self) */ 60, /* 060 03/12 G0 < Less-than sign => (self) */ 61, /* 061 03/13 G0 = Equals sign => (self) */ 62, /* 062 03/14 G0 > Greater-than sign => (self) */ 63, /* 063 03/15 G0 ? Question mark => (self) */ 64, /* 064 04/00 G0 @ Commercial at sign => (self) */ 65, /* 065 04/01 G0 A Letter A => (self) */ 66, /* 066 04/02 G0 B Letter B => (self) */ 67, /* 067 04/03 G0 C Letter C => (self) */ 68, /* 068 04/04 G0 D Letter D => (self) */ 69, /* 069 04/05 G0 E Letter E => (self) */ 70, /* 070 04/06 G0 F Letter F => (self) */ 71, /* 071 04/07 G0 G Letter G => (self) */ 72, /* 072 04/08 G0 H Letter H => (self) */ 73, /* 073 04/09 G0 I Letter I => (self) */ 74, /* 074 04/10 G0 J Letter J => (self) */ 75, /* 075 04/11 G0 K Letter K => (self) */ 76, /* 076 04/12 G0 L Letter L => (self) */ 77, /* 077 04/13 G0 M Letter M => (self) */ 78, /* 078 04/14 G0 N Letter N => (self) */ 79, /* 079 04/15 G0 O Letter O => (self) */ 80, /* 080 05/00 G0 P Letter P => (self) */ 81, /* 081 05/01 G0 Q Letter Q => (self) */ 82, /* 082 05/02 G0 R Letter R => (self) */ 83, /* 083 05/03 G0 S Letter S => (self) */ 84, /* 084 05/04 G0 T Letter T => (self) */ 85, /* 085 05/05 G0 U Letter U => (self) */ 86, /* 086 05/06 G0 V Letter V => (self) */ 87, /* 087 05/07 G0 W Letter W => (self) */ 88, /* 088 05/08 G0 X Letter X => (self) */ 89, /* 089 05/09 G0 Y Letter Y => (self) */ 90, /* 090 05/10 G0 Z Letter Z => (self) */ 91, /* 091 05/11 G0 [ Left square bracket => (self) */ 92, /* 092 05/12 G0 \ Reverse slash => (self) */ 93, /* 093 05/13 G0 ] Right square bracket => (self) */ 94, /* 094 05/14 G0 ^ Circumflex accent => (self) */ 95, /* 095 05/15 G0 _ Underline, low line => (self) */ 96, /* 096 06/00 G0 ` Grave accent => (self) */ 97, /* 097 06/01 G0 a Letter a => (self) */ 98, /* 098 06/02 G0 b Letter b => (self) */ 99, /* 099 06/03 G0 c Letter c => (self) */ 100, /* 100 06/04 G0 d Letter d => (self) */ 101, /* 101 06/05 G0 e Letter e => (self) */ 102, /* 102 06/06 G0 f Letter f => (self) */ 103, /* 103 06/07 G0 g Letter g => (self) */ 104, /* 104 06/08 G0 h Letter h => (self) */ 105, /* 105 06/09 G0 i Letter i => (self) */ 106, /* 106 06/10 G0 j Letter j => (self) */ 107, /* 107 06/11 G0 k Letter k => (self) */ 108, /* 108 06/12 G0 l Letter l => (self) */ 109, /* 109 06/13 G0 m Letter m => (self) */ 110, /* 110 06/14 G0 n Letter n => (self) */ 111, /* 111 06/15 G0 o Letter o => (self) */ 112, /* 112 07/00 G0 p Letter p => (self) */ 113, /* 113 07/01 G0 q Letter q => (self) */ 114, /* 114 07/02 G0 r Letter r => (self) */ 115, /* 115 07/03 G0 s Letter s => (self) */ 116, /* 116 07/04 G0 t Letter t => (self) */ 117, /* 117 07/05 G0 u Letter u => (self) */ 118, /* 118 07/06 G0 v Letter v => (self) */ 119, /* 119 07/07 G0 w Letter w => (self) */ 120, /* 120 07/08 G0 x Letter x => (self) */ 121, /* 121 07/09 G0 y Letter y => (self) */ 122, /* 122 07/10 G0 z Letter z => (self) */ 123, /* 123 07/11 G0 { Left curly bracket => (self) */ 124, /* 124 07/12 G0 | Vertical bar => (self) */ 125, /* 125 07/13 G0 } Right curly bracket => (self) */ 126, /* 126 07/14 G0 ~ Tilde => (self) */ 127, /* 127 07/15 DEL Delete, Rubout => (self) */ UNK, /* 128 08/00 C1 => UNK */ UNK, /* 129 08/01 C1 => UNK */ UNK, /* 130 08/02 C1 => UNK */ UNK, /* 131 08/03 C1 => UNK */ UNK, /* 132 08/04 C1 IND => UNK */ UNK, /* 133 08/05 C1 NEL => UNK */ UNK, /* 134 08/06 C1 SSA => UNK */ UNK, /* 135 08/07 C1 ESA => UNK */ UNK, /* 136 08/08 C1 HTS => UNK */ UNK, /* 137 08/09 C1 => UNK */ UNK, /* 138 08/10 C1 => UNK */ UNK, /* 139 08/11 C1 => UNK */ UNK, /* 140 08/12 C1 => UNK */ UNK, /* 141 08/13 C1 RI => UNK */ UNK, /* 142 08/14 C1 SS2 => UNK */ UNK, /* 143 08/15 C1 SS3 => UNK */ UNK, /* 144 09/00 C1 DCS => UNK */ UNK, /* 145 09/01 C1 => UNK */ UNK, /* 146 09/02 C1 => UNK */ UNK, /* 147 09/03 C1 STS => UNK */ UNK, /* 148 09/04 C1 => UNK */ UNK, /* 149 09/05 C1 => UNK */ UNK, /* 150 09/06 C1 SPA => UNK */ UNK, /* 151 09/07 C1 EPA => UNK */ UNK, /* 152 09/08 C1 => UNK */ UNK, /* 153 09/09 C1 => UNK */ UNK, /* 154 09/10 C1 => UNK */ UNK, /* 155 09/11 C1 CSI => UNK */ UNK, /* 156 09/12 C1 ST => UNK */ UNK, /* 157 09/13 C1 OSC => UNK */ UNK, /* 158 09/14 C1 PM => UNK */ UNK, /* 159 09/15 C1 APC => UNK */ 32, /* 160 10/00 G1 No-break space => SP */ 33, /* 161 10/01 G1 Inverted exclamation => ! */ 99, /* 162 10/02 G1 Cent sign => c */ 35, /* 163 10/03 G1 Pound sign => # */ 36, /* 164 10/04 G1 Currency sign => $ */ 89, /* 165 10/05 G1 Yen sign => Y */ 124, /* 166 10/06 G1 Broken bar => | */ 80, /* 167 10/07 G1 Paragraph sign => P */ 34, /* 168 10/08 G1 Diaeresis => " */ 67, /* 169 10/09 G1 Copyright sign => C */ 97, /* 170 10/10 G1 Feminine ordinal => a */ 34, /* 171 10/11 G1 Left angle quotation => " */ 126, /* 172 10/12 G1 Not sign => ~ */ 45, /* 173 10/13 G1 Soft hyphen => - */ 82, /* 174 10/14 G1 Registered trade mark => R */ 95, /* 175 10/15 G1 Macron => _ */ 111, /* 176 11/00 G1 Degree sign, ring above => o */ UNK, /* 177 11/01 G1 Plus-minus sign => UNK */ 50, /* 178 11/02 G1 Superscript two => 2 */ 51, /* 179 11/03 G1 Superscript three => 3 */ 39, /* 180 11/04 G1 Acute accent => ' */ 117, /* 181 11/05 G1 Micro sign => u */ 45, /* 182 11/06 G1 Pilcrow sign => - */ 45, /* 183 11/07 G1 Middle dot => - */ 44, /* 184 11/08 G1 Cedilla => , */ 49, /* 185 11/09 G1 Superscript one => 1 */ 111, /* 186 11/10 G1 Masculine ordinal => o */ 34, /* 187 11/11 G1 Right angle quotation => " */ UNK, /* 188 11/12 G1 One quarter => UNK */ UNK, /* 189 11/13 G1 One half => UNK */ UNK, /* 190 11/14 G1 Three quarters => UNK */ 63, /* 191 11/15 G1 Inverted question mark => ? */ 65, /* 192 12/00 G1 A grave => A */ 65, /* 193 12/01 G1 A acute => A */ 65, /* 194 12/02 G1 A circumflex => A */ 65, /* 195 12/03 G1 A tilde => A */ 65, /* 196 12/04 G1 A diaeresis => A */ 65, /* 197 12/05 G1 A ring above => A */ 65, /* 198 12/06 G1 A with E => A */ 67, /* 199 12/07 G1 C Cedilla => C */ 69, /* 200 12/08 G1 E grave => E */ 69, /* 201 12/09 G1 E acute => E */ 69, /* 202 12/10 G1 E circumflex => E */ 69, /* 203 12/11 G1 E diaeresis => E */ 73, /* 204 12/12 G1 I grave => I */ 73, /* 205 12/13 G1 I acute => I */ 73, /* 206 12/14 G1 I circumflex => I */ 73, /* 207 12/15 G1 I diaeresis => I */ 68, /* 208 13/00 G1 Icelandic Eth => D */ 78, /* 209 13/01 G1 N tilde => N */ 79, /* 210 13/02 G1 O grave => O */ 79, /* 211 13/03 G1 O acute => O */ 79, /* 212 13/04 G1 O circumflex => O */ 79, /* 213 13/05 G1 O tilde => O */ 79, /* 214 13/06 G1 O diaeresis => O */ 120, /* 215 13/07 G1 Multiplication sign => x */ 79, /* 216 13/08 G1 O oblique stroke => O */ 85, /* 217 13/09 G1 U grave => U */ 85, /* 218 13/10 G1 U acute => U */ 85, /* 219 13/11 G1 U circumflex => U */ 85, /* 220 13/12 G1 U diaeresis => U */ 89, /* 221 13/13 G1 Y acute => Y */ 84, /* 222 13/14 G1 Icelandic Thorn => T */ 115, /* 223 13/15 G1 German sharp s => s */ 97, /* 224 14/00 G1 a grave => a */ 97, /* 225 14/01 G1 a acute => a */ 97, /* 226 14/02 G1 a circumflex => a */ 97, /* 227 14/03 G1 a tilde => a */ 97, /* 228 14/04 G1 a diaeresis => a */ 97, /* 229 14/05 G1 a ring above => a */ 97, /* 230 14/06 G1 a with e => a */ 99, /* 231 14/07 G1 c cedilla => c */ 101, /* 232 14/08 G1 e grave => e */ 101, /* 233 14/09 G1 e acute => e */ 101, /* 234 14/10 G1 e circumflex => e */ 101, /* 235 14/11 G1 e diaeresis => e */ 105, /* 236 14/12 G1 i grave => i */ 105, /* 237 14/13 G1 i acute => i */ 105, /* 238 14/14 G1 i circumflex => i */ 105, /* 239 14/15 G1 i diaeresis => i */ 100, /* 240 15/00 G1 Icelandic eth => d */ 110, /* 241 15/01 G1 n tilde => n */ 111, /* 242 15/02 G1 o grave => o */ 111, /* 243 15/03 G1 o acute => o */ 111, /* 244 15/04 G1 o circumflex => o */ 111, /* 245 15/05 G1 o tilde => o */ 111, /* 246 15/06 G1 o diaeresis => o */ 47, /* 247 15/07 G1 Division sign => / */ 111, /* 248 15/08 G1 o oblique stroke => o */ 117, /* 249 15/09 G1 u grave => u */ 117, /* 250 15/10 G1 u acute => u */ 117, /* 251 15/11 G1 u circumflex => u */ 117, /* 252 15/12 G1 u diaeresis => u */ 121, /* 253 15/13 G1 y acute => y */ 116, /* 254 15/14 G1 Icelandic thorn => t */ 121 /* 255 15/15 G1 y diaeresis => y */ }; /* Translation tables for ISO Latin Alphabet 1 to local file character sets */ /* Most of the remaining tables are not annotated like the one above, because the size of the resulting source file would be ridiculous. Each row in the following tables corresponds to a column of ISO 8859-1. */ CONST CHAR yl185[] = { /* ISO 8859-1 Latin Alphabet 1 (Latin-1) to IBM Code Page 850 */ /* This is based on IBM's official invertible translation. Reference: IBM Character Data Representation Architecture (CDRA), Level 1, Registry, SC09-1291-00 (1990), p.152. (Note: Latin-1 is IBM Code Page 00819.) Note: IBM's bizarre rearrangement of C0 controls and DEL has been undone in this table. */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 186, 205, 201, 187, 200, 188, 204, 185, 203, 202, 206, 223, 220, 219, 254, 242, 179, 196, 218, 191, 192, 217, 195, 180, 194, 193, 197, 176, 177, 178, 213, 159, 255, 173, 189, 156, 207, 190, 221, 245, 249, 184, 166, 174, 170, 240, 169, 238, 248, 241, 253, 252, 239, 230, 244, 250, 247, 251, 167, 175, 172, 171, 243, 168, 183, 181, 182, 199, 142, 143, 146, 128, 212, 144, 210, 211, 222, 214, 215, 216, 209, 165, 227, 224, 226, 229, 153, 158, 157, 235, 233, 234, 154, 237, 232, 225, 133, 160, 131, 198, 132, 134, 145, 135, 138, 130, 136, 137, 141, 161, 140, 139, 208, 164, 149, 162, 147, 228, 148, 246, 155, 151, 163, 150, 129, 236, 231, 152 }; CONST CHAR y85l1[] = { /* IBM Code Page 850 to Latin-1 */ /* This is from IBM CDRA page 153. It is the inverse of yl185[]. As of edit 183, this table is no longer pure CDRA. The translations involving C0 controls and DEL have been removed. */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 199, 252, 233, 226, 228, 224, 229, 231, 234, 235, 232, 239, 238, 236, 196, 197, 201, 230, 198, 244, 246, 242, 251, 249, 255, 214, 220, 248, 163, 216, 215, 159, 225, 237, 243, 250, 241, 209, 170, 186, 191, 174, 172, 189, 188, 161, 171, 187, 155, 156, 157, 144, 151, 193, 194, 192, 169, 135, 128, 131, 133, 162, 165, 147, 148, 153, 152, 150, 145, 154, 227, 195, 132, 130, 137, 136, 134, 129, 138, 164, 240, 208, 202, 203, 200, 158, 205, 206, 207, 149, 146, 141, 140, 166, 204, 139, 211, 223, 212, 210, 245, 213, 181, 254, 222, 218, 219, 217, 253, 221, 175, 180, 173, 177, 143, 190, 182, 167, 247, 184, 176, 168, 183, 185, 179, 178, 142, 160 }; #ifdef COMMENT CONST CHAR yl1r8[] = { /* Latin-1 to Hewlett Packard Roman8 */ /* This is HP's official translation, straight from iconv */ /* It is NOT invertible. */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 184, 191, 187, 186, 188, 124, 189, 171, 99, 249, 251, 126, 45, 82, 176, 179, 254, 50, 51, 168, 243, 244, 242, 44, 49, 250, 253, 247, 248, 245, 185, 161, 224, 162, 225, 216, 208, 211, 180, 163, 220, 164, 165, 230, 229, 166, 167, 227, 182, 232, 231, 223, 233, 218, 120, 210, 173, 237, 174, 219, 177, 240, 222, 200, 196, 192, 226, 204, 212, 215, 181, 201, 197, 193, 205, 217, 213, 209, 221, 228, 183, 202, 198, 194, 234, 206, 47, 214, 203, 199, 195, 207, 178, 241, 239 }; CONST CHAR yr8l1[] = { /* Hewlett Packard Roman8 to Latin-1 */ /* This is HP's official translation, straight from iconv */ /* It is NOT invertible. */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 192, 194, 200, 202, 203, 206, 207, 180, 96, 94, 168, 126, 217, 219, 163, 175, 221, 253, 176, 199, 231, 209, 241, 161, 191, 164, 163, 165, 167, 102, 162, 226, 234, 244, 251, 225, 233, 243, 250, 224, 232, 242, 249, 228, 235, 246, 252, 197, 238, 216, 198, 229, 237, 248, 230, 196, 236, 214, 220, 201, 239, 223, 212, 193, 195, 227, 208, 240, 205, 204, 211, 210, 213, 245, 83, 115, 218, 89, 255, 222, 254, 183, 181, 182, 190, 45, 188, 189, 170, 186, 171, 42, 187, 177, 160 }; #else /* !COMMENT */ /* This is an invertible mapping, approved by HP in January 1994. */ CONST CHAR yl1r8[] = { /* ISO Latin-1 to HP Roman8, Invertible */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 184, 191, 187, 186, 188, 169, 189, 171, 170, 249, 251, 172, 175, 190, 176, 179, 254, 235, 236, 168, 243, 244, 242, 238, 246, 250, 253, 247, 248, 245, 185, 161, 224, 162, 225, 216, 208, 211, 180, 163, 220, 164, 165, 230, 229, 166, 167, 227, 182, 232, 231, 223, 233, 218, 252, 210, 173, 237, 174, 219, 177, 240, 222, 200, 196, 192, 226, 204, 212, 215, 181, 201, 197, 193, 205, 217, 213, 209, 221, 228, 183, 202, 198, 194, 234, 206, 255, 214, 203, 199, 195, 207, 178, 241, 239 }; CONST CHAR yr8l1[] = { /* HP Roman8 to ISO Latin-1, Invertible */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 192, 194, 200, 202, 203, 206, 207, 180, 166, 169, 168, 172, 217, 219, 173, 175, 221, 253, 176, 199, 231, 209, 241, 161, 191, 164, 163, 165, 167, 174, 162, 226, 234, 244, 251, 225, 233, 243, 250, 224, 232, 242, 249, 228, 235, 246, 252, 197, 238, 216, 198, 229, 237, 248, 230, 196, 236, 214, 220, 201, 239, 223, 212, 193, 195, 227, 208, 240, 205, 204, 211, 210, 213, 245, 178, 179, 218, 184, 255, 222, 254, 183, 181, 182, 190, 185, 188, 189, 170, 186, 171, 215, 187, 177, 247 }; #endif /* COMMENT */ CONST CHAR yl143[] = { /* Latin-1 to IBM Code Page 437 */ /* Although the IBM CDRA does not include an official translation between CP437 and ISO Latin Alphabet 1, it does include an official, invertible translation between CP437 and CP850 (page 196), and another from CP850 to Latin-1 (CP819) (page 153). This translation was obtained with a two-step process based on those tables. As of edit 183, the translation is modified to leave C0 controls alone. */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 186, 205, 201, 187, 200, 188, 204, 185, 203, 202, 206, 223, 220, 219, 254, 242, 179, 196, 218, 191, 192, 217, 195, 180, 194, 193, 197, 176, 177, 178, 213, 159, 255, 173, 155, 156, 207, 157, 221, 245, 249, 184, 166, 174, 170, 240, 169, 238, 248, 241, 253, 252, 239, 230, 244, 250, 247, 251, 167, 175, 172, 171, 243, 168, 183, 181, 182, 199, 142, 143, 146, 128, 212, 144, 210, 211, 222, 214, 215, 216, 209, 165, 227, 224, 226, 229, 153, 158, 190, 235, 233, 234, 154, 237, 232, 225, 133, 160, 131, 198, 132, 134, 145, 135, 138, 130, 136, 137, 141, 161, 140, 139, 208, 164, 149, 162, 147, 228, 148, 246, 189, 151, 163, 150, 129, 236, 231, 152 }; CONST CHAR y43l1[] = { /* IBM Code Page 437 to Latin-1 */ /* This table is the inverse of yl143[]. */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 199, 252, 233, 226, 228, 224, 229, 231, 234, 235, 232, 239, 238, 236, 196, 197, 201, 230, 198, 244, 246, 242, 251, 249, 255, 214, 220, 162, 163, 165, 215, 159, 225, 237, 243, 250, 241, 209, 170, 186, 191, 174, 172, 189, 188, 161, 171, 187, 155, 156, 157, 144, 151, 193, 194, 192, 169, 135, 128, 131, 133, 248, 216, 147, 148, 153, 152, 150, 145, 154, 227, 195, 132, 130, 137, 136, 134, 129, 138, 164, 240, 208, 202, 203, 200, 158, 205, 206, 207, 149, 146, 141, 140, 166, 204, 139, 211, 223, 212, 210, 245, 213, 181, 254, 222, 218, 219, 217, 253, 221, 175, 180, 173, 177, 143, 190, 182, 167, 247, 184, 176, 168, 183, 185, 179, 178, 142, 160 }; CONST CHAR yl1aq[] = { /* Latin-1 to Extended Mac Latin (based on Apple QuickDraw) */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 182, 183, 184, 185, 189, 196, 197, 198, 206, 207, 210, 211, 217, 218, 195, 212, 209, 215, 213, 226, 227, 228, 240, 245, 246, 247, 249, 250, 251, 253, 254, 255, 202, 193, 162, 163, 219, 180, 201, 164, 172, 169, 187, 199, 194, 208, 168, 248, 161, 177, 170, 173, 171, 181, 166, 225, 252, 176, 188, 200, 178, 179, 186, 192, 203, 231, 229, 204, 128, 129, 174, 130, 233, 131, 230, 232, 237, 234, 235, 236, 220, 132, 241, 238, 239, 205, 133, 165, 175, 244, 242, 243, 134, 160, 222, 167, 136, 135, 137, 139, 138, 140, 190, 141, 143, 142, 144, 145, 147, 146, 148, 149, 221, 150, 152, 151, 153, 155, 154, 214, 191, 157, 156, 158, 159, 224, 223, 216 }; CONST CHAR yl1du[] = { /* Latin-1 to Dutch ISO 646 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, UNK, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, UNK, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, UNK, UNK, UNK, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, UNK, UNK, UNK, 39, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 32, 33, UNK, 35, 124, UNK, UNK, 93, 123, 67, UNK, 34, UNK, 45, 82, UNK, 91, UNK, UNK, UNK, 126, 117, UNK, UNK, 44, UNK, UNK, 34, 125, 92, 64, 63, 65, 65, 65, 65, 91, 65, 65, 67, 69, 69, 69, 69, 73, 73, 73, 73, UNK, 78, 79, 79, 79, 79, 79, 120, 79, 85, 85, 85, 85, 89, UNK, 115, 97, 97, 97, 97, 97, 97, 97, 99, 101, 101, 101, 101, 105, 105, 105, 105, UNK, 110, 111, 111, 111, 111, 111, 47, 111, 117, 117, 117, 117, 121, UNK, 91 }; CONST CHAR yl1fi[] = { /* Latin-1 to Finnish ISO NRC (*not* ISO 646) */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, UNK, UNK, UNK, UNK, 95, UNK, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, UNK, UNK, UNK, UNK, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 32, 33, UNK, UNK, UNK, UNK, UNK, UNK, 34, 67, UNK, 34, UNK, 45, 82, UNK, UNK, UNK, UNK, UNK, 39, 117, UNK, UNK, 44, UNK, UNK, 34, UNK, UNK, UNK, 63, 65, 65, 65, 65, 91, 93, 65, 67, 69, 69, 69, 69, 73, 73, 73, 73, UNK, 78, 79, 79, 79, 79, 92, 120, 79, 85, 85, 85, 94, 89, UNK, 115, 97, 97, 97, 97, 123, 125, 97, 99, 101, 96, 101, 101, 105, 105, 105, 105, UNK, 110, 111, 111, 111, 111, 124, 47, 111, 117, 117, 117, 126, 121, UNK, 121 }; CONST CHAR yl1fr[] = { /* Latin-1 to French ISO 646 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, UNK, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, UNK, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, UNK, UNK, UNK, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, UNK, UNK, UNK, UNK, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 32, 33, UNK, 35, UNK, UNK, UNK, 93, 34, 67, UNK, 34, UNK, 45, 82, UNK, 91, UNK, UNK, UNK, 39, 117, UNK, UNK, 44, UNK, UNK, 34, UNK, UNK, UNK, 63, 65, 65, 65, 65, 65, 65, 65, 67, 69, 69, 69, 69, 73, 73, 73, 73, UNK, 78, 79, 79, 79, 79, 79, 120, 79, 85, 85, 85, 85, 89, UNK, 115, 64, 97, 97, 97, 97, 97, 97, 92, 125, 123, 101, 101, 105, 105, 105, 105, UNK, 110, 111, 111, 111, 111, 111, 47, 111, 124, 117, 117, 117, 121, UNK, 121 }; CONST CHAR yl1fc[] = { /* Latin-1 to French-Canadian ISO 646 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, UNK, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, UNK, UNK, UNK, UNK, 95, UNK, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, UNK, UNK, UNK, UNK, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 32, 33, UNK, UNK, UNK, UNK, UNK, UNK, 34, 67, UNK, 34, UNK, 45, 82, UNK, UNK, UNK, UNK, UNK, 39, 117, UNK, UNK, 44, UNK, UNK, 34, UNK, UNK, UNK, 63, 65, 65, 65, 65, 65, 65, 65, 67, 69, 69, 69, 69, 73, 73, 73, 73, UNK, 78, 79, 79, 79, 79, 79, 120, 79, 85, 85, 85, 85, 89, UNK, 115, 64, 97, 91, 97, 97, 97, 97, 92, 125, 123, 93, 101, 105, 105, 94, 105, UNK, 110, 111, 111, 96, 111, 111, 47, 111, 124, 117, 126, 117, 121, UNK, 121 }; CONST CHAR yl1ge[] = { /* Latin-1 to German ISO 646 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, UNK, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, UNK, UNK, UNK, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, UNK, UNK, UNK, UNK, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 32, 33, UNK, UNK, UNK, UNK, UNK, 64, 34, 67, UNK, 34, UNK, 45, 82, UNK, UNK, UNK, UNK, UNK, 39, 117, UNK, UNK, 44, UNK, UNK, 34, UNK, UNK, UNK, 63, 65, 65, 65, 65, 91, 65, 65, 67, 69, 69, 69, 69, 73, 73, 73, 73, UNK, 78, 79, 79, 79, 79, 92, 120, 79, 85, 85, 85, 93, 89, UNK, 126, 97, 97, 97, 97, 123, 97, 97, 99, 101, 101, 101, 101, 105, 105, 105, 105, UNK, 110, 111, 111, 111, 111, 124, 47, 111, 117, 117, 117, 125, 121, UNK, 121 }; CONST CHAR yl1hu[] = { /* Latin-1 to Hungarian ISO-646 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 32, 33, UNK, UNK, 36, UNK, UNK, UNK, 34, 67, UNK, 34, UNK, 45, 82, UNK, UNK, 64, UNK, UNK, 39, 117, UNK, UNK, 44, UNK, UNK, 34, UNK, UNK, UNK, 63, 65, 65, 65, 65, 65, 65, 65, 67, 69, 91, 69, 69, 73, 73, 73, 73, UNK, 78, 79, 79, 79, 79, 92, 120, 79, 85, 85, 85, 93, 89, UNK, 115, 97, 96, 97, 97, 97, 97, 97, 99, 101, 123, 101, 101, 105, 105, 105, 105, UNK, 110, 111, 111, 111, 111, 124, 47, 111, 117, 117, 117, 125, 121, UNK, 121 }; CONST CHAR yl1it[] = { /* Latin-1 to Italian ISO 646 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, UNK, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, UNK, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, UNK, UNK, UNK, 94, 95, UNK, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, UNK, UNK, UNK, UNK, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 32, 33, UNK, 35, UNK, UNK, UNK, 64, 34, 67, UNK, 34, UNK, 45, 82, UNK, 91, UNK, UNK, UNK, 39, 117, UNK, UNK, 44, UNK, UNK, 34, UNK, UNK, UNK, 63, 65, 65, 65, 65, 65, 65, 65, 67, 69, 69, 69, 69, 73, 73, 73, 73, UNK, 78, 79, 79, 79, 79, 79, 120, 79, 85, 85, 85, 85, 89, UNK, 115, 123, 97, 97, 97, 97, 97, 97, 92, 125, 93, 101, 101, 126, 105, 105, 105, UNK, 110, 124, 111, 111, 111, 111, 47, 111, 96, 117, 117, 117, 121, UNK, 121 }; CONST CHAR yl1ne[] = { /* Latin-1 to NeXT */ /* NEED TO MAKE THIS ONE INVERTIBLE, LIKE CP850 */ /* Which means finding all the graphic characters in the NeXT set that have no equivalent in Latin-1 and assigning them to the UNK positions (mostly Latin-1 C1 controls). Then make the ynel1[] table be the inverse of this one. But first we should try to get an official Latin-1/NeXT translation table from NeXT, Inc. */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, 32, 161, 162, 163, 168, 165, 181, 167, 200, 160, 227, 171, 190, UNK, 176, 197, 202, 209, 201, 204, 194, 157, 182, 183, 203, 192, 235, 187, 210, 211, 212, 191, 129, 130, 131, 132, 133, 134, 225, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 158, 233, 151, 152, 153, 154, 155, 156, 251, 213, 214, 215, 216, 217, 218, 241, 219, 220, 221, 222, 223, 224, 226, 228, 229, 230, 231, 236, 237, 238, 239, 240, 159, 249, 242, 243, 244, 246, 247, 252, 253 }; CONST CHAR yl1no[] = { /* Latin-1 to Norwegian/Danish ISO 646 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, UNK, UNK, UNK, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, UNK, UNK, UNK, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 32, 33, UNK, UNK, UNK, UNK, UNK, UNK, 34, 67, UNK, 34, UNK, 45, 82, UNK, UNK, UNK, UNK, UNK, 39, 117, UNK, UNK, 44, UNK, UNK, 34, UNK, UNK, UNK, 63, 65, 65, 65, 65, 65, 93, 91, 67, 69, 69, 69, 69, 73, 73, 73, 73, UNK, 78, 79, 79, 79, 79, 79, 120, 92, 85, 85, 85, 85, 89, UNK, 115, 97, 97, 97, 97, 97, 125, 123, 99, 101, 101, 101, 101, 105, 105, 105, 105, UNK, 110, 111, 111, 111, 111, 111, 47, 124, 117, 117, 117, 117, 121, UNK, 121 }; CONST CHAR yl1po[] = { /* Latin-1 to Portuguese ISO 646 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, UNK, UNK, UNK, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, UNK, UNK, UNK, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 32, 33, UNK, UNK, UNK, UNK, UNK, UNK, 34, 67, UNK, 34, UNK, 45, 82, UNK, UNK, UNK, UNK, UNK, 39, 117, UNK, UNK, 44, UNK, UNK, 34, UNK, UNK, UNK, 63, 65, 65, 65, 91, 65, 65, 65, 92, 69, 69, 69, 69, 73, 73, 73, 73, UNK, 78, 79, 79, 79, 93, 79, 120, 79, 85, 85, 85, 85, 89, UNK, 115, 97, 97, 97, 123, 97, 97, 97, 124, 101, 101, 101, 101, 105, 105, 105, 105, UNK, 110, 111, 111, 111, 125, 111, 47, 111, 117, 117, 117, 117, 121, UNK, 121 }; CONST CHAR yl1sp[] = { /* Latin-1 to Spanish ISO 646 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, UNK, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, UNK, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, UNK, UNK, UNK, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 96, UNK, UNK, 126, 127, 126, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 32, 91, UNK, 35, UNK, UNK, UNK, 64, 34, 67, UNK, 34, UNK, 45, 82, UNK, 123, UNK, UNK, UNK, 39, 117, UNK, UNK, 44, UNK, UNK, 34, UNK, UNK, UNK, 93, 65, 65, 65, 65, 65, 65, 65, 67, 69, 69, 69, 69, 73, 73, 73, 73, UNK, 92, 79, 79, 79, 79, 79, 120, 79, 85, 85, 85, 85, 89, UNK, 115, 124, 97, 97, 97, 97, 97, 97, 125, 101, 101, 101, 101, 105, 105, 105, 105, UNK, 124, 111, 111, 111, 111, 111, 47, 111, 117, 117, 117, 117, 121, UNK, 121 }; CONST CHAR yl1sw[] = { /* Latin-1 to Swedish ISO 646 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, UNK, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, UNK, UNK, UNK, UNK, 95, UNK, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, UNK, UNK, UNK, UNK, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 32, 33, UNK, UNK, UNK, UNK, UNK, UNK, 34, 67, UNK, 34, UNK, 45, 82, UNK, UNK, UNK, UNK, UNK, 39, 117, UNK, UNK, 44, UNK, UNK, 34, UNK, UNK, UNK, 63, 65, 65, 65, 65, 91, 93, 65, 67, 69, 64, 69, 69, 73, 73, 73, 73, UNK, 78, 79, 79, 79, 79, 92, 120, 79, 85, 85, 85, 94, 89, UNK, 115, 97, 97, 97, 97, 123, 125, 97, 99, 101, 96, 101, 101, 105, 105, 105, 105, UNK, 110, 111, 111, 111, 111, 124, 47, 111, 117, 117, 117, 126, 121, UNK, 121 }; CONST CHAR yl1ch[] = { /* Latin-1 to Swiss ISO 646 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, UNK, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, UNK, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, UNK, UNK, UNK, UNK, UNK, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, UNK, UNK, UNK, UNK, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 32, 33, UNK, UNK, UNK, UNK, UNK, UNK, 34, 67, UNK, 34, UNK, 45, 82, UNK, UNK, UNK, UNK, UNK, 39, 117, UNK, UNK, 44, UNK, UNK, 34, UNK, UNK, UNK, 63, 65, 65, 65, 65, 65, 65, 65, 67, 69, 69, 69, 69, 73, 73, 73, 73, UNK, 78, 79, 79, 79, 79, 79, 120, 79, 85, 85, 85, 85, 89, UNK, 115, 64, 97, 97, 97, 123, 97, 97, 92, 95, 91, 93, 101, 105, 105, 94, 105, UNK, 110, 111, 111, 96, 111, 124, 47, 111, 35, 117, 126, 125, 121, UNK, 121 }; CONST CHAR yl1dm[] = { /* Latin-1 to DEC Multinational Character Set */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 32, 161, 162, 163, 168, 165, 124, 167, 34, 169, 170, 171, 126, UNK, 82, UNK, 176, 177, 178, 179, 39, 181, 182, 183, 44, 185, 186, 187, 188, 189, UNK, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, UNK, 209, 210, 211, 212, 213, 214, 120, 216, 217, 218, 219, 220, 221, UNK, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, UNK, 241, 242, 243, 244, 245, 246, 47, 248, 249, 250, 251, 252, UNK, UNK, 253 }; CONST CHAR yl1dg[] = { /* Latin-1 to Data General International Character Set */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 171, 167, 168, 166, 181, 191, 187, 189, 173, 169, 177, 161, 255, 174, 175, 188, 182, 164, 165, 190, 163, 178, 185, 186, 179, 170, 176, 223, 162, 220, 172, 193, 192, 194, 196, 195, 197, 198, 199, 201, 200, 202, 203, 205, 204, 206, 207, 184, 208, 210, 209, 211, 213, 212, 215, 214, 217, 216, 218, 219, 221, 222, 252, 225, 224, 226, 228, 227, 229, 230, 231, 233, 232, 234, 235, 237, 236, 238, 239, 183, 240, 242, 241, 243, 245, 244, 247, 246, 249, 248, 250, 251, 180, 254, 253 }; /* Local file character sets to ISO Latin Alphabet 1 */ #ifdef NOTUSED CONST CHAR yasl1[] = { /* ASCII to Latin-1 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127 }; #endif /* NOTUSED */ CONST CHAR yaql1[] = { /* Extended Mac Latin (based on Apple Quickdraw) to Latin-1 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 196, 197, 199, 201, 209, 214, 220, 225, 224, 226, 228, 227, 229, 231, 233, 232, 234, 235, 237, 236, 238, 239, 241, 243, 242, 244, 246, 245, 250, 249, 251, 252, 221, 176, 162, 163, 167, 215, 182, 223, 174, 169, 178, 180, 168, 179, 198, 216, 185, 177, 188, 189, 165, 181, 128, 129, 130, 131, 190, 170, 186, 132, 230, 248, 191, 161, 172, 142, 133, 134, 135, 171, 187, 166, 160, 192, 195, 213, 136, 137, 173, 144, 138, 139, 143, 146, 247, 145, 255, 140, 141, 164, 208, 240, 222, 254, 253, 183, 147, 148, 149, 194, 202, 193, 203, 200, 205, 206, 207, 204, 211, 212, 150, 210, 218, 219, 217, 151, 152, 153, 175, 154, 155, 156, 184, 157, 158, 159 }; CONST CHAR ydul1[] = { /* Dutch ISO 646 to Latin-1 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 163, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 190, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 255, 189, 124, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 168, 164, 188, 39, 127 }; CONST CHAR yfil1[] = { /* Finnish NRC (*not* ISO-646) to Latin-1 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 196, 214, 197, 220, 95, 233, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 228, 246, 229, 252, 127 }; CONST CHAR yfrl1[] = { /* French ISO 646 to Latin-1 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 163, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 224, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 176, 231, 167, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 233, 249, 232, 168, 127 }; CONST CHAR yfcl1[] = { /* French-Canadian ISO 646 to Latin-1 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 224, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 226, 231, 234, 238, 95, 244, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 233, 249, 232, 251, 127 }; CONST CHAR ygel1[] = { /* German ISO 646 to Latin-1 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 167, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 196, 214, 220, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 228, 246, 252, 223, 127 }; CONST CHAR yitl1[] = { /* Italian ISO 646 to Latin-1 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 163, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 167, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 176, 231, 233, 94, 95, 249, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 224, 242, 232, 236, 127 }; CONST CHAR ynel1[] = { /* NeXT to Latin-1 */ /* NEED TO MAKE THIS ONE INVERTIBLE */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 160, 192, 193, 194, 195, 196, 197, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 217, 218, 219, 220, 221, 222, 181, 215, 247, 169, 161, 162, 163, UNK, 165, UNK, 167, 164, UNK, UNK, 171, UNK, UNK, UNK, UNK, 174, UNK, UNK, UNK, 183, 166, 182, UNK, UNK, UNK, UNK, 187, UNK, UNK, 172, 191, 185, 96, 180, 94, 126, 175, UNK, UNK, 168, 178, 176, 184, 179, UNK, UNK, UNK, UNK, 177, 188, 189, 190, 224, 225, 226, 227, 228, 229, 231, 232, 233, 234, 235, 236, 198, 237, 170, 238, 239, 240, 241, UNK, 216, UNK, 186, 242, 243, 244, 245, 246, 230, 249, 250, 251, UNK, 252, 253, UNK, 248, UNK, 223, 254, 255, UNK, UNK }; CONST CHAR ynol1[] = { /* Norwegian/Danish ISO 646 to Latin-1 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 198, 216, 197, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 230, 248, 229, 126, 127 }; CONST CHAR ypol1[] = { /* Portuguese ISO 646 to Latin-1 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 195, 199, 213, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 227, 231, 245, 126, 127 }; CONST CHAR yspl1[] = { /* Spanish ISO 646 to Latin-1 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 163, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 167, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 161, 209, 191, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 176, 241, 231, 126, 127 }; CONST CHAR yswl1[] = { /* Swedish ISO 646 to Latin-1 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 201, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 196, 214, 197, 220, 95, 233, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 228, 246, 229, 252, 127 }; CONST CHAR ychl1[] = { /* Swiss ISO 646 to Latin-1 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 249, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 224, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 233, 231, 234, 238, 232, 244, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 228, 246, 252, 251, 127 }; CONST CHAR yhul1[] = { /* Hungarian ISO 646 to Latin-1 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 164, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 193, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 201, 214, 220, 94, 95, 225, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 233, 246, 252, 34, 127 }; CONST CHAR ydml1[] = { /* DEC Multinational Character Set to Latin-1 */ /* Note: This is a null translation */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 }; CONST CHAR ydgl1[] = { /* Data General International to Latin-1 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 172, 189, 181, 178, 179, 164, 162, 163, 170, 186, 161, 191, 169, 174, 175, 187, 171, 182, 185, 253, 165, 177, 240, 208, 183, 184, 167, 176, 168, 180, 166, 193, 192, 194, 196, 195, 197, 198, 199, 201, 200, 202, 203, 205, 204, 206, 207, 209, 211, 210, 212, 214, 213, 216, 215, 218, 217, 219, 220, 190, 221, 222, 188, 225, 224, 226, 228, 227, 229, 230, 231, 233, 232, 234, 235, 237, 236, 238, 239, 241, 243, 242, 244, 246, 245, 248, 247, 250, 249, 251, 252, 223, 255, 254, 173 }; /* Translation tables for Cyrillic character sets */ #ifdef CYRILLIC CONST CHAR ylcac[] = { /* Latin/Cyrillic to CP866 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 208, 209, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 196, 179, 192, 217, 191, 218, 195, 193, 180, 194, 197, 176, 177, 178, 211, 216, 205, 186, 200, 188, 187, 201, 204, 202, 185, 203, 206, 223, 220, 219, 254, UNK, 255, 240, 132, 131, 242, 83, 73, 244, 74, 139, 141, 151, 138, 45, 246, 135, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 252, 241, 164, 163, 243, 115, 105, 245, 106, 171, 173, 231, 170, 21, 247, 167 }; CONST CHAR ylc55[] = { /* Latin/Cyrillic to CP855 (inverse of y55lc) */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 174, 175, 176, 177, 178, 179, 180, 185, 186, 187, 188, 191, 192, 193, 194, 195, 196, 197, 200, 201, 202, 203, 204, 205, 206, 207, 217, 218, 219, 220, 223, 254, 255, 133, 129, 131, 135, 137, 139, 141, 143, 145, 147, 149, 151, 240, 153, 155, 161, 163, 236, 173, 167, 169, 234, 244, 184, 190, 199, 209, 211, 213, 215, 221, 226, 228, 230, 232, 171, 182, 165, 252, 246, 250, 159, 242, 238, 248, 157, 224, 160, 162, 235, 172, 166, 168, 233, 243, 183, 189, 198, 208, 210, 212, 214, 216, 225, 227, 229, 231, 170, 181, 164, 251, 245, 249, 158, 241, 237, 247, 156, 222, 239, 132, 128, 130, 134, 136, 138, 140, 142, 144, 146, 148, 150, 253, 152, 154 }; CONST CHAR ylc1251[] = { /* Latin/Cyrillic to CP1251 (inverse of y1251lc) */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 130, 132, 133, 134, 135, 136, 137, 139, 145, 146, 147, 148, 149, 150, 151, 152, 153, 155, 164, 165, 166, 169, 171, 172, 174, 176, 177, 180, 181, 182, 183, 187, 160, 168, 128, 129, 170, 189, 178, 175, 163, 138, 140, 142, 141, 173, 161, 143, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 185, 184, 144, 131, 186, 190, 179, 191, 188, 154, 156, 158, 157, 167, 162, 159 }; CONST CHAR ylcbu[] = { /* Latin/Cyrillic to Bulgarian PC Code Page */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 255, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 213, 207, 208, 209, 210, 211, 212, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 214, 253, 254 }; CONST CHAR ylck8[] = { /* Latin/Cyrillic to Old KOI-8 Cyrillic */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, UNK, 229, UNK, UNK, UNK, 83, 73, 73, 74, UNK, UNK, UNK, 235, UNK, 245, UNK, 225, 226, 247, 231, 228, 229, 246, 250, 233, 234, 235, 236, 237, 238, 239, 240, 242, 243, 244, 245, 230, 232, 227, 254, 251, 253, 255, 249, 248, 252, 224, 241, 193, 194, 215, 199, 196, 197, 214, 218, 201, 202, 203, 204, 205, 206, 207, 208, 210, 211, 212, 213, 198, 200, 195, 222, 219, 221, 223, 217, 216, 220, 192, 209, UNK, 197, UNK, UNK, UNK, 115, 105, 105, 106, UNK, UNK, UNK, 203, UNK, 213, UNK }; CONST CHAR yaclc[] = { /* CP866 to Latin/Cyrillic */ /* NEED TO MAKE THIS ONE INVERTIBLE */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 161, 241, 164, 244, 167, 247, 174, 254, UNK, UNK, UNK, UNK, 240, UNK, UNK, UNK }; CONST CHAR y55lc[] = { /* CP855 to Latin/Cyrillic (inverse of ylc55) */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 242, 162, 243, 163, 241, 161, 244, 164, 245, 165, 246, 166, 247, 167, 248, 168, 249, 169, 250, 170, 251, 171, 252, 172, 254, 174, 255, 175, 238, 206, 234, 202, 208, 176, 209, 177, 230, 198, 212, 180, 213, 181, 228, 196, 211, 179, 128, 129, 130, 131, 132, 133, 134, 229, 197, 216, 184, 135, 136, 137, 138, 217, 185, 139, 140, 141, 142, 143, 144, 145, 218, 186, 146, 147, 148, 149, 150, 151, 152, 153, 219, 187, 220, 188, 221, 189, 222, 190, 223, 154, 155, 156, 157, 191, 239, 158, 207, 224, 192, 225, 193, 226, 194, 227, 195, 214, 182, 210, 178, 236, 204, 240, 173, 235, 203, 215, 183, 232, 200, 237, 205, 233, 201, 231, 199, 253, 159, 160 }; CONST CHAR y1251lc[] = { /* CP1251 to Latin/Cyrillic (inverse of ylc1251) */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 162, 163, 128, 243, 129, 130, 131, 132, 133, 134, 169, 135, 170, 172, 171, 175, 242, 136, 137, 138, 139, 140, 141, 142, 143, 144, 249, 145, 250, 252, 251, 255, 160, 174, 254, 168, 146, 147, 148, 253, 161, 149, 164, 150, 151, 173, 152, 167, 153, 154, 166, 246, 155, 156, 157, 158, 241, 240, 244, 159, 248, 165, 245, 247, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239 }; CONST CHAR ybulc[] = { /* Bulgarian PC Code Page to Latin/Cyrillic */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 209, 210, 211, 212, 213, 214, 208, 253, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 254, 255, 128 }; CONST CHAR yk8lc[] = { /* Old KOI-8 Cyrillic to Latin/Cyrillic */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, 238, 208, 209, 230, 212, 213, 228, 211, 229, 216, 217, 218, 219, 220, 221, 222, 223, 239, 224, 225, 226, 227, 214, 210, 236, 235, 215, 232, 237, 233, 231, 234, 206, 176, 177, 198, 180, 181, 196, 179, 197, 184, 185, 186, 187, 188, 189, 190, 191, 207, 192, 193, 194, 195, 182, 178, 204, 203, 183, 200, 205, 201, 199, 127 }; CONST CHAR ylcsk[] = { /* Latin/Cyrillic to Short KOI */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 127, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 101, UNK, UNK, UNK, 83, 73, 73, 74, UNK, UNK, UNK, 107, 45, 117, UNK, 97, 98, 119, 103, 100, 101, 118, 122, 105, 106, 107, 108, 109, 110, 111, 112, 114, 115, 116, 117, 102, 104, 99, 126, 123, 125, 39, 121, 120, 124, 96, 113, 97, 98, 119, 103, 100, 101, 118, 122, 105, 106, 107, 108, 109, 110, 111, 112, 114, 115, 116, 117, 102, 104, 99, 126, 123, 125, 39, 121, 120, 124, 96, 113, UNK, 101, UNK, UNK, UNK, 83, 73, 73, 74, UNK, UNK, UNK, 107, UNK, 117, UNK }; CONST CHAR yskcy[] = { /* Short KOI to Latin/Cyrillic */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 206, 176, 177, 198, 180, 181, 196, 179, 197, 184, 185, 186, 187, 188, 189, 190, 191, 207, 192, 193, 194, 195, 182, 178, 204, 203, 183, 200, 205, 201, 199, 127 }; #endif /* CYRILLIC */ #ifdef LATIN2 /* Latin-2 tables */ CONST CHAR yl252[] = { /* Latin-2 to Code Page 852 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 174, 175, 176, 177, 178, 179, 180, 185, 186, 187, 188, 191, 192, 193, 194, 195, 196, 197, 200, 201, 202, 203, 204, 205, 206, 217, 218, 219, 220, 223, 240, 254, 255, 164, 244, 157, 207, 149, 151, 245, 249, 230, 184, 155, 141, 170, 166, 189, 248, 165, 242, 136, 239, 150, 152, 243, 247, 231, 173, 156, 171, 241, 167, 190, 232, 181, 182, 198, 142, 145, 143, 128, 172, 144, 168, 211, 183, 214, 215, 210, 209, 227, 213, 224, 226, 138, 153, 158, 252, 222, 233, 235, 154, 237, 221, 225, 234, 160, 131, 199, 132, 146, 134, 135, 159, 130, 169, 137, 216, 161, 140, 212, 208, 228, 229, 162, 147, 139, 148, 246, 253, 133, 163, 251, 129, 236, 238, 250 }; CONST CHAR y52l2[] = { /* Code Page 852 to Latin-2 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 199, 252, 233, 226, 228, 249, 230, 231, 179, 235, 213, 245, 238, 172, 196, 198, 201, 197, 229, 244, 246, 165, 181, 166, 182, 214, 220, 171, 187, 163, 215, 232, 225, 237, 243, 250, 161, 177, 174, 190, 202, 234, 173, 188, 200, 186, 128, 129, 130, 131, 132, 133, 134, 193, 194, 204, 170, 135, 136, 137, 138, 175, 191, 139, 140, 141, 142, 143, 144, 145, 195, 227, 146, 147, 148, 149, 150, 151, 152, 164, 240, 208, 207, 203, 239, 210, 205, 206, 236, 153, 154, 155, 156, 222, 217, 157, 211, 223, 212, 209, 241, 242, 169, 185, 192, 218, 224, 219, 253, 221, 254, 180, 158, 189, 178, 183, 162, 167, 247, 184, 176, 168, 255, 251, 216, 248, 159, 160 }; CONST CHAR yl21250[] = { /* Latin-2 to Code Page 1250 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 139, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 155, 166, 169, 171, 172, 174, 177, 181, 182, 183, 187, 160, 165, 162, 163, 164, 188, 140, 167, 168, 138, 170, 141, 143, 173, 142, 175, 176, 185, 178, 179, 180, 190, 156, 161, 184, 154, 186, 157, 159, 189, 158, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 }; CONST CHAR y1250l2[] = { /* Code Page 1250 to Latin-2 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 169, 138, 166, 171, 174, 172, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 185, 149, 182, 187, 190, 188, 160, 183, 162, 163, 164, 161, 150, 167, 168, 151, 170, 152, 153, 173, 154, 175, 176, 155, 178, 179, 180, 156, 157, 158, 184, 177, 186, 159, 165, 189, 181, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 }; CONST CHAR yl2mz[] = { /* Latin-2 to Mazovia (NOT invertible) */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 255, 143, UNK, 156, 155, 76, 152, 21, 34, 83, 83, 84, 160, 45, 90, 161, 248, 134, 44, 146, 39, 108, 158, UNK, 44, 115, 115, 116, 166, 34, 122, 167, 82, 65, 65, 65, 142, 76, 149, 128, 67, 69, 144, 69, 69, 73, 73, 68, 68, 165, 78, 163, 79, 153, 153, 250, 82, 85, 85, 154, 154, 89, 84, 225, 114, 97, 131, 97, 132, 108, 141, 135, 99, 130, 145, 137, 101, 105, 140, 101, 100, 164, 110, 162, 147, 148, 148, 246, 114, 117, 117, 129, 129, 121, 116, 249 }; CONST CHAR ymzl2[] = { /* Mazovia to Latin-2 (NOT INVERTIBLE) */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 252, 233, 226, 228, 97, 177, 231, 101, 235, 101, 105, 238, 230, 196, 161, 202, 234, 179, 244, 246, 198, 117, 117, 166, 214, 220, 164, 163, 89, 182, 102, 172, 175, 243, 211, 242, 210, 188, 191, 63, UNK, UNK, UNK, UNK, 33, 34, 34, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, 247, UNK, 176, 255, 215, UNK, UNK, UNK, UNK, 160 }; CONST CHAR yl2l1[] = { /* Latin-2 to Latin-1 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 'A', UNK, 'L', 164, 'L', 'S', 167, 168, 'S', 'S', 'T', 'Z', 173, 'Z', 'Z', 176, 'a', UNK, 'l', 180, 'l', 's', UNK, 184, 's', 's', 't', 'z', UNK, 'z', 'z', 'R', 193, 194, 'A', 196, 'L', 'C', 199, 'C', 201, 'E', 203, 'E', 205, 'I', 'D', 208, 'N', 'N', 211, 212, 'O', 214, 215, 'R', 'U', 218, 'U', 220, 221, 'T', 223, 'r', 225, 226, 'a', 228, 'l', 'c', 231, 'c', 233, 'e', 235, 'e', 237, 'i', 'd', 240, 'n', 'n', 243, 244, 'o', 246, 247, 'r', 'u', 250, 'u', 252, 253, 't', '.' }; CONST CHAR yl1l2[] = { /* Latin-1 to Latin-2 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 'A', UNK, 'L', 164, UNK, UNK, 167, 168, 'C', 'a', '<', '>', 173, 'R', UNK, 176, UNK, UNK, UNK, 180, UNK, UNK, UNK, 184, UNK, 'o', '>', UNK, UNK, UNK, UNK, 'A', 193, 194, 'A', 196, 'A', 'A', 199, 'E', 201, 'E', 203, 'I', 205, 'I', 'I', 208, 'N', 'O', 211, 212, 'O', 214, 215, 'O', 'U', 218, 'U', 220, 221, UNK, 223, 'a', 225, 226, 'a', 228, 'a', 'a', 231, 'e', 233, 'e', 235, 'i', 237, 'i', 'i', 240, 'n', 'o', 243, 244, 'o', 246, 247, 'o', 'u', 250, 'u', 252, 253, UNK, 'y' }; CONST CHAR yl2as[] = { /* Latin-2 to ASCII */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, 32, 'A', UNK, 'L', UNK, 'L', 'S', UNK, 34, 'S', 'S', 'T', 'Z', '-', 'Z', 'Z', UNK, 'a', UNK, 'l', 39, 'l', 's', UNK, 44, 's', 's', 't', 'z', UNK, 'z', 'z', 'R', 'A', 'A', 'A', 'A', 'L', 'C', 'C', 'C', 'E', 'E', 'E', 'E', 'I', 'I', 'D', 'D', 'N', 'N', 'O', 'O', 'O', 'O', 'x', 'R', 'U', 'U', 'U', 'U', 'Y', 'T', 's', 'r', 'a', 'a', 'a', 'a', 'l', 'c', 'c', 'c', 'e', 'e', 'e', 'e', 'i', 'i', 'd', 'd', 'n', 'n', 'o', 'o', 'o', 'o', '/', 'r', 'u', 'u', 'u', 'u', 'y', 't', '.' }; #endif /* LATIN2 */ #ifdef HEBREW /* 8-bit Tables providing invertible translation between Latin/Hebrew and CP862. */ CONST CHAR y62lh[] = { /* PC Code Page 862 to ISO 8859-8 Latin/Hebrew */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 162, 163, 165, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 172, 189, 188, 140, 171, 187, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 161, 164, 166, 167, 168, 169, 170, 173, 174, 175, 223, 179, 180, 182, 184, 185, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 181, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 177, 219, 220, 221, 222, 186, 251, 176, 183, 252, 253, 254, 178, 255, 160 }; CONST CHAR ylh62[] = { /* ISO 8859-8 Latin/Hebrew to PC Code Page 862 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 173, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 255, 195, 155, 156, 196, 157, 197, 198, 199, 200, 201, 174, 170, 202, 203, 204, 248, 241, 253, 206, 207, 230, 208, 249, 209, 210, 246, 175, 172, 171, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 242, 243, 244, 245, 205, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 247, 250, 251, 252, 254 }; /* 7-bit table providing readable translation from DEC Hebrew-7 to CP862. */ CONST CHAR yh762[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, UNK, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 123, 124, 125, 126, 127 }; /* 8-bit table providing readable translation from CP862 to Hebrew-7. */ CONST CHAR y62h7[] = { /* PC Code Page 862 to Hebrew-7 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 123, 124, 125, 126, 127, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK }; /* 7-bit table providing readable translation from Hebrew-7 to ISO Latin/Hebrew. */ CONST CHAR yh7lh[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 123, 124, 125, 126, 127 }; /* 8-bit table providing readable translation from ISO Latin/Hebrew to Hebrew-7. */ CONST CHAR ylhh7[] = { /* Latin/Hebrew to Hebrew-7 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 123, 124, 125, 126, 127, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, UNK, UNK, UNK, UNK, UNK }; #endif /* HEBREW */ #ifdef GREEK /* 8-bit Tables providing invertible translation between Latin/Greek and CP869. */ CONST CHAR ylg69[] = { /* ISO 8859-7 Latin/Greek to PC Code Page 869 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 135, 147, 148, 176, 177, 178, 179, 180, 185, 186, 187, 188, 191, 192, 193, 194, 195, 196, 197, 200, 201, 202, 203, 204, 205, 206, 217, 218, 219, 220, 223, 254, 255, 139, 140, 156, 128, 129, 138, 245, 249, 151, 130, 174, 137, 240, 131, 142, 248, 241, 153, 154, 239, 247, 134, 136, 141, 143, 144, 175, 146, 171, 149, 152, 161, 164, 165, 166, 167, 168, 169, 170, 172, 173, 181, 182, 183, 184, 189, 190, 198, 199, 132, 207, 208, 209, 210, 211, 212, 213, 145, 150, 155, 157, 158, 159, 252, 214, 215, 216, 221, 222, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 237, 236, 238, 242, 243, 244, 246, 250, 160, 251, 162, 163, 253, 133 }; CONST CHAR y69lg[] = { /* PC Code Page 869 to ISO 8859-7 Latin/Greek */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 164, 165, 170, 174, 210, 255, 182, 128, 183, 172, 166, 161, 162, 184, 175, 185, 186, 218, 188, 129, 130, 190, 219, 169, 191, 178, 179, 220, 163, 221, 222, 223, 250, 192, 252, 253, 193, 194, 195, 196, 197, 198, 199, 189, 200, 201, 171, 187, 131, 132, 133, 134, 135, 202, 203, 204, 205, 136, 137, 138, 139, 206, 207, 140, 141, 142, 143, 144, 145, 146, 208, 209, 147, 148, 149, 150, 151, 152, 153, 211, 212, 213, 214, 215, 216, 217, 225, 226, 227, 154, 155, 156, 157, 228, 229, 158, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 243, 242, 244, 180, 173, 177, 245, 246, 247, 167, 248, 181, 176, 168, 249, 251, 224, 254, 159, 160 }; /* 7-bit table providing readable translation from ELOT 927 to CP869. */ CONST CHAR yeg69[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 164, 165, 166, 167, 168, 169, 170, 172, 173, 181, 182, 183, 184, 189, 190, 198, 199, 207, 208, 209, 210, 211, 212, 213, 32, 32, 23, 124, 125, 126, 127 }; /* 8-bit table providing readable translation from CP869 to ELOT 927. */ CONST CHAR y69eg[] = { /* PC Code Page 869 to ELOT 927 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 123, 124, 125, 126, 127, UNK, UNK, UNK, UNK, UNK, UNK, 97, UNK, 46, UNK, 124, 39, 39, 101, 45, 103, 105, 105, 111, UNK, UNK, 116, 116, UNK, 120, 50, 51, 97, UNK, 101, 103, 105, 105, 105, 111, 116, 97, 98, 99, 100, 101, 102, 103, UNK, 104, 105, 34, 34, UNK, UNK, UNK, UNK, UNK, 106, 107, 108, 109, UNK, UNK, UNK, UNK, 110, 111, UNK, UNK, UNK, UNK, UNK, UNK, UNK, 112, 113, UNK, UNK, UNK, UNK, UNK, UNK, UNK, 114, 115, 116, 117, 118, 119, 120, 97, 98, 99, UNK, UNK, UNK, UNK, 100, 101, UNK, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 114, 115, 39, 45, UNK, 116, 117, 118, UNK, 119, UNK, UNK, UNK, 120, 116, 116, 120, UNK, 32 }; /* 7-bit table providing readable translation from ELOT 927 to ISO Latin/Greek. */ CONST CHAR yeglg[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 211, 212, 213, 214, 215, 216, 217, 32, 32, 123, 124, 125, 126, 127 }; /* 8-bit table providing readable translation from ISO Latin/Greek to ELOT 927. */ CONST CHAR ylgeg[] = { /* Latin/Greek to ELOT 927 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 123, 124, 125, 126, 127, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, 32, 39, 39, UNK, UNK, UNK, 124, UNK, 34, UNK, UNK, 34, UNK, 45, UNK, 45, UNK, UNK, 50, 51, 39, UNK, 97, 46, 101, 103, 105, 34, 111, UNK, 116, 120, UNK, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, UNK, 114, 115, 116, 117, 118, 119, 120, 105, 116, 97, 101, 103, 105, 116, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 114, 115, 116, 117, 118, 119, 120, 105, 116, 111, 116, 120, UNK }; #endif /* GREEK */ /* Translation functions ... */ CHAR /* The identity function... */ #ifdef CK_ANSIC ident(CHAR c) /* (no longer used) */ #else ident(c) CHAR c; #endif /* CK_ANSIC */ { /* ident */ return(c); /* Instead, enter NULL in the */ } /* table of functions to avoid */ /* needless function calls. */ CHAR #ifdef CK_ANSIC xleft128(CHAR c) #else xleft128(c) CHAR c; #endif /* CK_ANSIC */ { /* xleft128 */ return((c < 128) ? c : '?'); } CHAR #ifdef CK_ANSIC xleft160(CHAR c) #else xleft160(c) CHAR c; #endif /* CK_ANSIC */ { /* xleft160 */ return((c < 160) ? c : '?'); } CHAR #ifdef CK_ANSIC xl1as(CHAR c) #else xl1as(c) CHAR c; #endif /* CK_ANSIC */ { /* xl1as */ /* Latin-1 to US ASCII... */ switch(langs[language].id) { case L_DUTCH: if (c == 255) { /* Dutch umlaut-y */ zmstuff('j'); /* becomes ij */ return('i'); } else return(yl1as[c]); /* all others by the book */ case L_GERMAN: switch (c) { /* German, special rules. */ case 196: /* umlaut-A -> Ae */ zmstuff('e'); return('A'); case 214: /* umlaut-O -> Oe */ zmstuff('e'); return('O'); case 220: /* umlaut-U -> Ue */ zmstuff('e'); return('U'); case 228: /* umlaut-a -> ae */ zmstuff('e'); return('a'); case 246: /* umlaut-o -> oe */ zmstuff('e'); return('o'); case 252: /* umlaut-u -> ue */ zmstuff('e'); return('u'); case 223: /* ess-zet -> ss */ zmstuff('s'); return('s'); default: return(yl1as[c]); /* all others by the book */ } case L_DANISH: case L_FINNISH: case L_NORWEGIAN: case L_SWEDISH: switch (c) { /* Scandanavian languages. */ case 196: /* umlaut-A -> Ae */ case 198: /* AE ligature also -> Ae */ zmstuff('e'); return('A'); case 214: /* umlaut-O -> Oe */ case 216: /* O-slash -> Oe */ zmstuff('e'); return('O'); case 220: /* umlaut-U -> Ue */ /* return('Y'); replaced by "Ue" by popular demand. */ /* Y for Umlaut-U is only used in German names. */ zmstuff('e'); return('U'); case 228: /* umlaut-a -> ae */ case 230: /* ditto for ae ligature */ zmstuff('e'); return('a'); case 246: /* umlaut-o -> oe */ case 248: /* o-slash -> oe */ zmstuff('e'); return('o'); case 252: /* umlaut-u -> ue */ /* return('y'); replaced by "ue" by popular demand. */ zmstuff('e'); return('u'); case 197: /* A-ring -> Aa */ zmstuff('a'); return('A'); case 229: /* a-ring -> aa */ zmstuff('a'); return('a'); default: return(yl1as[c]); /* All others by the book */ } case L_ICELANDIC: /* Icelandic. */ switch (c) { case 198: /* uppercase AE -> AE */ zmstuff('e'); return('A'); case 208: /* uppercase Eth -> D */ return('D'); case 214: /* uppercase O-diaeresis -> Oe */ zmstuff('e'); return('O'); case 222: /* uppercase Thorn -> Th */ zmstuff('h'); return('T'); case 230: /* lowercase ae -> ae */ zmstuff('e'); return('a'); case 240: /* lowercase Eth -> d */ return('d'); case 246: /* lowercase O-diaeresis -> oe */ zmstuff('e'); return('o'); case 254: /* lowercase Thorn -> th */ zmstuff('h'); return('t'); default: return(yl1as[c]); /* All others by the book */ } default: return(yl1as[c]); /* None of the above, by the table. */ } } CHAR /* CP1252 to ASCII */ #ifdef CK_ANSIC xw1as(CHAR c) #else xw1as(c) CHAR c; #endif /* CK_ANSIC */ { /* xw1as */ switch(c) { /* Microsoft name... */ case 0x80: return('?'); /* Euro Sign */ case 0x82: return('\047'); /* Single Low-9 Quotation Mark */ case 0x83: return('f'); /* Latin Small Letter f with Hook */ case 0x84: return('"'); /* Double low-9 Quotation Mark */ case 0x85: return('-'); /* Horizontal Ellipsis */ case 0x86: return('+'); /* Dagger */ case 0x87: return('+'); /* Double Dagger */ case 0x88: return('^'); /* Modifier Letter Circumflex Accent */ case 0x89: return('?'); /* Per Mille Sign */ case 0x8A: return('S'); /* Latin Capital Letter S with Caron */ case 0x8B: return('<'); /* Single Left Angle Quotation Mark */ case 0x8C: return('O'); /* Latin Capital Ligature OE */ case 0x8E: return('Z'); /* Latin Capital Letter Z with Caron */ case 0x91: return('\047'); /* Left Single Quotation Mark */ case 0x92: return('\047'); /* Right Single Quotation Mark */ case 0x93: return('"'); /* Left Double Quotation Mark */ case 0x94: return('"'); /* Right Double Quotation Mark */ case 0x95: return('.'); /* Bullet */ case 0x96: return('-'); /* En Dash */ case 0x97: return('-'); /* Em Dash */ case 0x98: return('~'); /* Small Tilde */ case 0x99: return('T'); /* Trade Mark Sign */ case 0x9A: return('s'); /* Latin Small Letter s with Caron */ case 0x9B: return('>'); /* Single Right Angle Quotation Mark */ case 0x9C: return('o'); /* Latin Small Ligature OE */ case 0x9E: return('z'); /* Latin Small Letter z with Caron */ case 0x9F: return('Y'); /* Latin Capital Letter Y Diaeresis */ default: if (c > 0x80 && c < 0xa0) return('?'); else return(yl1as[c]); } } CHAR /* CP1252 to Latin-1 */ #ifdef CK_ANSIC xw1l1(CHAR c) #else xw1l1(c) CHAR c; #endif /* CK_ANSIC */ { /* xw1l1 */ if (c == 0x95) return(0xb7); /* Middle dot */ return((c < 160) ? xw1as(c) : c); } CHAR /* Latin-1 to German */ #ifdef CK_ANSIC xl1ge(CHAR c) #else xl1ge(c) CHAR c; #endif /* CK_ANSIC */ { /* xl1ge */ return(yl1ge[c]); } CHAR /* German to Latin-1 */ #ifdef CK_ANSIC xgel1(CHAR c) #else xgel1(c) CHAR c; #endif /* CK_ANSIC */ { /* xgel1 */ if (c & 0x80) return(UNK); return(ygel1[c]); } CHAR #ifdef CK_ANSIC xgeas(CHAR c) #else xgeas(c) CHAR c; #endif /* CK_ANSIC */ { /* xgeas */ /* German ISO 646 to ASCII */ if (c & 0x80) return(UNK); switch (c) { case 91: /* umlaut-A -> Ae */ zmstuff('e'); return('A'); case 92: /* umlaut-O -> Oe */ zmstuff('e'); return('O'); case 93: /* umlaut-U -> Ue */ zmstuff('e'); return('U'); case 123: /* umlaut-a -> ae */ zmstuff('e'); return('a'); case 124: /* umlaut-o -> oe */ zmstuff('e'); return('o'); case 125: /* umlaut-u -> ue */ zmstuff('e'); return('u'); case 126: /* ess-zet -> ss */ zmstuff('s'); return('s'); default: return(c); /* all others stay the same */ } } CHAR #ifdef CK_ANSIC xl1w1(CHAR c) #else xl1w1(c) CHAR c; #endif /* CK_ANSIC */ { /* xl1w1 */ /* Latin-1 to CP1252 (Windows L1) */ if (c > 127 && c < 160) return(UNK); else return(c); } CHAR #ifdef CK_ANSIC xduas(CHAR c) #else xduas(c) CHAR c; #endif /* CK_ANSIC */ { /* xduas */ /* Dutch ISO 646 to US ASCII */ if (c & 0x80) return(UNK); switch (c) { case 64: return(UNK); /* 3/4 */ case 91: /* y-diaeresis */ zmstuff('j'); return('i'); case 92: return(UNK); /* 1/2 */ case 93: return(124); /* vertical bar */ case 123: return(34); /* diaeresis */ case 124: return(UNK); /* Florin */ case 125: return(UNK); /* 1/4 */ case 126: return(39); /* Apostrophe */ default: return(c); } } CHAR #ifdef CK_ANSIC xfias(CHAR c) #else xfias(c) CHAR c; #endif /* CK_ANSIC */ { /* xfias */ /* Finnish ISO 646 to US ASCII */ if (c & 0x80) return(UNK); switch (c) { case 91: /* A-diaeresis */ zmstuff('e'); return('A'); case 92: /* O-diaeresis */ zmstuff('e'); return('O'); case 93: /* A-ring */ zmstuff('a'); return('A'); case 94: /* U-diaeresis */ /* return('Y'); */ zmstuff('e'); return('U'); case 96: /* e-acute */ return('e'); case 123: /* a-diaeresis */ zmstuff('e'); return('a'); case 124: /* o-diaeresis */ zmstuff('e'); return('o'); case 125: /* a-ring */ zmstuff('a'); return('a'); case 126: /* u-diaeresis */ /* return('y'); */ zmstuff('e'); return('U'); default: return(c); } } CHAR #ifdef CK_ANSIC xfras(CHAR c) #else xfras(c) CHAR c; #endif /* CK_ANSIC */ { /* xfras */ /* French ISO 646 to US ASCII */ if (c & 0x80) return(UNK); switch (c) { case 64: return(97); /* a grave */ case 91: return(UNK); /* degree sign */ case 92: return(99); /* c cedilla */ case 93: return(UNK); /* paragraph sign */ case 123: return(101); /* e acute */ case 124: return(117); /* u grave */ case 125: return(101); /* e grave */ case 126: return(34); /* diaeresis */ default: return(c); } } CHAR #ifdef CK_ANSIC xfcas(CHAR c) #else xfcas(c) CHAR c; #endif /* CK_ANSIC */ { /* xfcas */ /* French Canadian ISO 646 to ASCII */ if (c & 0x80) return(UNK); switch (c) { case 64: return('a'); /* a grave */ case 91: return('a'); /* a circumflex */ case 92: return('c'); /* c cedilla */ case 93: return('e'); /* e circumflex */ case 94: return('i'); /* i circumflex */ case 96: return('o'); /* o circumflex */ case 123: return('e'); /* e acute */ case 124: return('u'); /* u grave */ case 125: return('e'); /* e grave */ case 126: return('u'); /* u circumflex */ default: return(c); } } CHAR #ifdef CK_ANSIC xitas(CHAR c) #else xitas(c) CHAR c; #endif /* CK_ANSIC */ { /* xitas */ /* Italian ISO 646 to ASCII */ if (c & 0x80) return(UNK); switch (c) { case 91: return(UNK); /* degree */ case 92: return('c'); /* c cedilla */ case 93: return('e'); /* e acute */ case 96: return('u'); /* u grave */ case 123: return('a'); /* a grave */ case 124: return('o'); /* o grave */ case 125: return('e'); /* e grave */ case 126: return('i'); /* i grave */ default: return(c); } } CHAR #ifdef CK_ANSIC xneas(CHAR c) #else xneas(c) CHAR c; #endif /* CK_ANSIC */ { /* xneas */ /* NeXT to ASCII */ if (langs[language].id == L_FRENCH) { /* If SET LANGUAGE FRENCH */ if (c == 234) { /* handle OE digraph. */ zmstuff('E'); return('O'); } else if (c == 250) { /* Also lowercase oe. */ zmstuff('e'); return('o'); } } c = xnel1(c); /* Convert to Latin-1 */ return(yl1as[c]); /* Convert Latin-1 to ASCII */ } CHAR #ifdef CK_ANSIC xnoas(CHAR c) #else xnoas(c) CHAR c; #endif /* CK_ANSIC */ { /* xnoas */ /* Norge/Danish ISO 646 to ASCII */ if (c & 0x80) return(UNK); switch (c) { case 91: zmstuff('E'); /* AE digraph */ return('A'); case 92: return('O'); /* O slash */ case 93: /* A ring */ zmstuff('a'); return('A'); case 123: /* ae digraph */ zmstuff('e'); return('a'); case 124: return('o'); /* o slash */ case 125: /* a ring */ zmstuff('a'); return('a'); default: return(c); } } CHAR #ifdef CK_ANSIC xpoas(CHAR c) #else xpoas(c) CHAR c; #endif /* CK_ANSIC */ { /* xpoas */ /* Portuguese ISO 646 to ASCII */ if (c & 0x80) return(UNK); switch (c) { case 91: return('A'); /* A tilde */ case 92: return('C'); /* C cedilla */ case 93: return('O'); /* O tilde */ case 123: return('a'); /* a tilde */ case 124: return('c'); /* c cedilla */ case 125: return('o'); /* o tilde */ default: return(c); } } CHAR #ifdef CK_ANSIC xspas(CHAR c) #else xspas(c) CHAR c; #endif /* CK_ANSIC */ { /* xspas */ /* Spanish ISO 646 to ASCII */ if (c & 0x80) return(UNK); switch (c) { case 91: return(33); /* Inverted exclamation */ case 92: return('N'); /* N tilde */ case 93: return(63); /* Inverted question mark */ case 123: return(UNK); /* degree */ case 124: return('n'); /* n tilde */ case 125: return('c'); /* c cedilla */ default: return(c); } } CHAR #ifdef CK_ANSIC xswas(CHAR c) #else xswas(c) CHAR c; #endif /* CK_ANSIC */ { /* xswas */ /* Swedish ISO 646 to ASCII */ if (c & 0x80) return(UNK); switch (c) { case 64: return('E'); /* E acute */ case 91: /* A diaeresis */ zmstuff('e'); return('A'); case 92: /* O diaeresis */ zmstuff('e'); return('O'); case 93: /* A ring */ zmstuff('a'); return('A'); case 94: /* U diaeresis */ /* return('Y'); */ zmstuff('e'); return('U'); case 96: return('e'); /* e acute */ case 123: /* a diaeresis */ zmstuff('e'); return('a'); case 124: /* o diaeresis */ zmstuff('e'); return('o'); case 125: /* a ring */ zmstuff('a'); return('a'); case 126: /* u diaeresis */ /* return('y'); */ zmstuff('e'); return('u'); default: return(c); } } CHAR #ifdef CK_ANSIC xchas(CHAR c) #else xchas(c) CHAR c; #endif /* CK_ANSIC */ { /* xchas */ /* Swiss ISO 646 to ASCII */ if (c & 0x80) return(UNK); switch (c) { case 35: return('u'); /* u grave */ case 64: return('a'); /* a grave */ case 91: return('e'); /* e acute */ case 92: return('c'); /* c cedilla */ case 93: return('e'); /* e circumflex */ case 94: return('i'); /* i circumflex */ case 95: return('e'); /* e grave */ case 96: return('o'); /* o circumflex */ case 123: /* a diaeresis */ zmstuff('e'); return('a'); case 124: /* o diaeresis */ zmstuff('e'); return('o'); case 125: /* u diaeresis */ zmstuff('e'); return('u'); case 126: return('u'); /* u circumflex */ default: return(c); } } CHAR #ifdef CK_ANSIC xhuas(CHAR c) #else xhuas(c) CHAR c; #endif /* CK_ANSIC */ { /* xhuas */ /* Hungarian ISO 646 to ASCII */ if (c & 0x80) return(UNK); switch (c) { case 64: return('A'); /* A acute */ case 91: return('E'); /* E acute */ case 92: return('O'); /* O diaeresis */ case 93: return('U'); /* U diaeresis */ case 96: return('a'); /* a acute */ case 123: return('e'); /* e acute */ case 124: return('o'); /* o acute */ case 125: return('u'); /* u acute */ case 126: return(34); /* double acute accent */ default: return(c); } } CHAR #ifdef CK_ANSIC xdmas(CHAR c) #else xdmas(c) CHAR c; #endif /* CK_ANSIC */ { /* xdmas */ /* DEC MCS to ASCII */ if (langs[language].id == L_FRENCH) { /* If SET LANGUAGE FRENCH */ if (c == 215) { /* handle OE digraph. */ zmstuff('E'); return('O'); } else if (c == 247) { /* Also lowercase oe. */ zmstuff('e'); return('o'); } } return(yl1as[c]); /* Otherwise treat like Latin-1 */ } CHAR #ifdef CK_ANSIC xdgas(CHAR c) #else xdgas(c) CHAR c; #endif /* CK_ANSIC */ { /* xdgas */ /* Data General to ASCII */ switch(c) { case 180: return('f'); /* Florin */ case 183: return('<'); /* Less-equal */ case 184: return('>'); /* Greater-equal */ case 186: return(96); /* Grave accent */ case 191: return('^'); /* Uparrow */ case 215: if (langs[language].id == L_FRENCH) { /* OE digraph */ zmstuff('E'); return('O'); } else return('O'); case 247: if (langs[language].id == L_FRENCH) { /* oe digraph */ zmstuff('e'); return('o'); } else return('o'); case 175: case 179: case 220: case 222: case 223: case 254: case 255: return(UNK); default: /* The rest, convert to Latin-1 */ return(yl1as[ydgl1[c]]); /* and from there to ASCII */ } } CHAR #ifdef CK_ANSIC xr8as(CHAR c) #else xr8as(c) CHAR c; #endif /* CK_ANSIC */ { /* xr8as */ /* Hewlett Packard Roman8 to ASCII */ switch(c) { case 175: return('L'); /* Lira */ case 190: return('f'); /* Florin */ case 235: return('S'); /* S caron */ case 236: return('s'); /* s caron */ case 246: return('-'); /* Horizontal bar */ case 252: return('*'); /* Solid box */ default: /* The rest, convert to Latin-1 */ return(yl1as[yr8l1[c]]); /* and from there to ASCII */ } } CHAR #ifdef CK_ANSIC xukl1(CHAR c) #else xukl1(c) CHAR c; #endif /* CK_ANSIC */ { /* xukl1 */ /* UK ASCII to Latin-1 */ if (c & 0x80) return(UNK); if (c == 35) return(163); else return(c); } CHAR #ifdef CK_ANSIC xl1uk(CHAR c) #else xl1uk(c) CHAR c; #endif /* CK_ANSIC */ { /* xl1uk */ /* Latin-1 to UK ASCII */ if (c == 163) return(35); else return(yl1as[c]); } CHAR /* Latin-1 to French ISO 646 */ #ifdef CK_ANSIC xl1fr(CHAR c) #else xl1fr(c) CHAR c; #endif /* CK_ANSIC */ { /* xl1fr */ return(yl1fr[c]); } CHAR /* French ISO 646 to Latin-1 */ #ifdef CK_ANSIC xfrl1(CHAR c) #else xfrl1(c) CHAR c; #endif /* CK_ANSIC */ { /* xfrl1 */ if (c & 0x80) return(UNK); return(yfrl1[c]); } CHAR /* Latin-1 to Dutch ASCII */ #ifdef CK_ANSIC xl1du(CHAR c) #else xl1du(c) CHAR c; #endif /* CK_ANSIC */ { /* xl1du */ return(yl1du[c]); } CHAR #ifdef CK_ANSIC xdul1(CHAR c) #else xdul1(c) CHAR c; #endif /* CK_ANSIC */ { /* xdul1 */ /* Dutch ISO 646 to Latin-1 */ if (c & 0x80) return(UNK); return(ydul1[c]); } CHAR #ifdef CK_ANSIC xfil1(CHAR c) #else xfil1(c) CHAR c; #endif /* CK_ANSIC */ { /* xfil1 */ /* Finnish ISO 646 to Latin-1 */ if (c & 0x80) return(UNK); return(yfil1[c]); } CHAR #ifdef CK_ANSIC xl1fi(CHAR c) #else xl1fi(c) CHAR c; #endif /* CK_ANSIC */ { /* xl1fi */ /* Latin-1 to Finnish ISO 646 */ return(yl1fi[c]); } CHAR #ifdef CK_ANSIC xfcl1(CHAR c) #else xfcl1(c) CHAR c; #endif /* CK_ANSIC */ { /* xfcl1 */ /* French Canadian ISO646 to Latin-1 */ if (c & 0x80) return(UNK); return(yfcl1[c]); } CHAR #ifdef CK_ANSIC xl1fc(CHAR c) #else xl1fc(c) CHAR c; #endif /* CK_ANSIC */ { /* xl1fc */ /* Latin-1 to French Canadian ISO646 */ return(yl1fc[c]); } CHAR #ifdef CK_ANSIC xitl1(CHAR c) #else xitl1(c) CHAR c; #endif /* CK_ANSIC */ { /* xitl1 */ /* Italian ISO 646 to Latin-1 */ if (c & 0x80) return(UNK); return(yitl1[c]); } CHAR #ifdef CK_ANSIC xl1it(CHAR c) #else xl1it(c) CHAR c; #endif /* CK_ANSIC */ { /* xl1it */ /* Latin-1 to Italian ISO 646 */ return(yl1it[c]); } CHAR #ifdef CK_ANSIC xnel1(CHAR c) #else xnel1(c) CHAR c; #endif /* CK_ANSIC */ { /* xnel1 */ /* NeXT to Latin-1 */ if (langs[language].id == L_FRENCH) { /* If SET LANGUAGE FRENCH */ if (c == 234) { /* handle OE digraph. */ zmstuff('E'); return('O'); } else if (c == 250) { /* Also lowercase oe. */ zmstuff('e'); return('o'); } } return(ynel1[c]); } CHAR #ifdef CK_ANSIC xnel9(CHAR c) #else xnel9(c) CHAR c; #endif /* CK_ANSIC */ { /* xnel9 */ /* NeXT to Latin-9 */ switch (c) { case 234: return(188); /* OE */ case 250: return(189); /* oe */ case 188: return(234); /* keep it invertible... */ case 189: return(250); /* oe */ default: return(ynel1[c]); } } CHAR #ifdef CK_ANSIC xl1ne(CHAR c) #else xl1ne(c) CHAR c; #endif /* CK_ANSIC */ { /* xl1ne */ /* Latin-1 to NeXT */ return(yl1ne[c]); } CHAR #ifdef CK_ANSIC xl9ne(CHAR c) #else xl9ne(c) CHAR c; #endif /* CK_ANSIC */ { /* xl9ne */ /* Latin-9 to NeXT */ switch (c) { case 188: return(234); /* OE */ case 189: return(250); /* oe */ case 234: return(188); /* OE */ case 250: return(189); /* oe */ default: return(yl1ne[c]); } } CHAR #ifdef CK_ANSIC xnol1(CHAR c) #else xnol1(c) CHAR c; #endif /* CK_ANSIC */ { /* xnol1 */ /* Norway/Denmark ISO 646 to Latin-1 */ if (c & 0x80) return(UNK); return(ynol1[c]); } CHAR #ifdef CK_ANSIC xl1no(CHAR c) #else xl1no(c) CHAR c; #endif /* CK_ANSIC */ { /* xl1no */ /* Latin-1 to Norway/Denmark ISO 646 */ return(yl1no[c]); } CHAR #ifdef CK_ANSIC xpol1(CHAR c) #else xpol1(c) CHAR c; #endif /* CK_ANSIC */ { /* xpol1 */ /* Portuguese ISO 646 to Latin-1 */ if (c & 0x80) return(UNK); return(ypol1[c]); } CHAR #ifdef CK_ANSIC xl1po(CHAR c) #else xl1po(c) CHAR c; #endif /* CK_ANSIC */ { /* xl1po */ /* Latin-1 to Portuguese ISO 646 */ return(yl1po[c]); } CHAR #ifdef CK_ANSIC xspl1(CHAR c) #else xspl1(c) CHAR c; #endif /* CK_ANSIC */ { /* xspl1 */ /* Spanish ISO 646 to Latin-1 */ if (c & 0x80) return(UNK); return(yspl1[c]); } CHAR #ifdef CK_ANSIC xl1sp(CHAR c) #else xl1sp(c) CHAR c; #endif /* CK_ANSIC */ { /* xl1sp */ /* Latin-1 to Spanish ISO 646 */ return(yl1sp[c]); } CHAR #ifdef CK_ANSIC xswl1(CHAR c) #else xswl1(c) CHAR c; #endif /* CK_ANSIC */ { /* xswl1 */ /* Swedish ISO 646 to Latin-1 */ if (c & 0x80) return(UNK); return(yswl1[c]); } CHAR #ifdef CK_ANSIC xl1sw(CHAR c) #else xl1sw(c) CHAR c; #endif /* CK_ANSIC */ { /* xl1sw */ /* Latin-1 to Swedish ISO 646 */ return(yl1sw[c]); } CHAR #ifdef CK_ANSIC xchl1(CHAR c) #else xchl1(c) CHAR c; #endif /* CK_ANSIC */ { /* xchl1 */ /* Swiss ISO 646 to Latin-1 */ if (c & 0x80) return(UNK); return(ychl1[c]); } CHAR #ifdef CK_ANSIC xl1ch(CHAR c) #else xl1ch(c) CHAR c; #endif /* CK_ANSIC */ { /* xl1ch */ /* Latin-1 to Swiss ISO 646 */ return(yl1ch[c]); } CHAR #ifdef CK_ANSIC xhul1(CHAR c) #else xhul1(c) CHAR c; #endif /* CK_ANSIC */ { /* xhul1 */ /* Hungarian ISO 646 to Latin-1 */ if (c & 0x80) return(UNK); return(yhul1[c]); } CHAR #ifdef CK_ANSIC xl1hu(CHAR c) #else xl1hu(c) CHAR c; #endif /* CK_ANSIC */ { /* xl1hu */ /* Latin-1 to Hungarian ISO 646 */ return(yl1hu[c]); } CHAR #ifdef CK_ANSIC xl1dm(CHAR c) #else xl1dm(c) CHAR c; #endif /* CK_ANSIC */ { /* xl1dm */ /* Latin-1 to DEC MCS */ return(yl1dm[c]); } CHAR #ifdef CK_ANSIC xl9dm(CHAR c) #else xl9dm(c) CHAR c; #endif /* CK_ANSIC */ { /* xl9dm */ /* Latin-9 to DEC MCS */ switch (c) { case 188: return(215); case 189: return(247); case 215: return(188); case 247: return(189); default: return(yl1dm[c]); } } CHAR #ifdef CK_ANSIC xl9w1(CHAR c) #else xl9w1(c) CHAR c; #endif /* CK_ANSIC */ { /* xl9w1 */ /* Latin-9 to CP1252 */ if (c < 128) return(c); else if (c < 160) return('?'); switch (c) { case 0xa4: return(0x80); /* Euro */ case 0xa6: return(0x8a); /* S-caron */ case 0xa8: return(0x9a); /* s-caron */ case 0xb4: return(0x8e); /* Z-caron */ case 0xb8: return(0x9e); /* z-caron */ case 0xbc: return(0x8c); /* OE */ case 0xbd: return(0x9c); /* oe */ case 0xbe: return(0x9f); /* Y-diaeresis */ default: return(c); } } CHAR #ifdef CK_ANSIC xl1dg(CHAR c) #else xl1dg(c) CHAR c; #endif /* CK_ANSIC */ { /* xl1dg */ /* Latin-1 to DG ICS */ return(yl1dg[c]); } CHAR #ifdef CK_ANSIC xdml1(CHAR c) #else xdml1(c) CHAR c; #endif /* CK_ANSIC */ { /* xdml1 */ /* DEC MCS to Latin-1 */ if (langs[language].id == L_FRENCH) { /* If SET LANGUAGE FRENCH */ if (c == 215) { /* handle OE digraph. */ zmstuff('E'); return('O'); } else if (c == 247) { /* Also lowercase oe. */ zmstuff('e'); return('o'); } } return(ydml1[c]); } CHAR #ifdef CK_ANSIC xdml9(CHAR c) #else xdml9(c) CHAR c; #endif /* CK_ANSIC */ { /* xdml9 */ /* DEC MCS to Latin-9 */ switch (c) { case 215: return(188); /* OE */ case 247: return(189); /* oe */ case 188: return(215); /* and swap the other two... */ case 189: return(247); /* (1/4 and 1/2) */ default: /* to keep it invertible */ return(ydml1[c]); } } CHAR #ifdef CK_ANSIC xdgl1(CHAR c) #else xdgl1(c) CHAR c; #endif /* CK_ANSIC */ { /* xdgl1 */ /* DG International CS to Latin-1 */ if (langs[language].id == L_FRENCH) { /* If SET LANGUAGE FRENCH */ if (c == 215) { /* handle OE digraph. */ zmstuff('E'); return('O'); } else if (c == 247) { /* Also lowercase oe. */ zmstuff('e'); return('o'); } } return(ydgl1[c]); } CHAR #ifdef CK_ANSIC xr8l1(CHAR c) #else xr8l1(c) CHAR c; #endif /* CK_ANSIC */ { /* xr8l1 */ /* Hewlett Packard Roman8 to Latin-1 */ return(yr8l1[c]); } CHAR #ifdef CK_ANSIC xl1r8(CHAR c) #else xl1r8(c) CHAR c; #endif /* CK_ANSIC */ { /* xl1r8 */ /* Latin-1 to Hewlett Packard Roman8 */ return(yl1r8[c]); } /* Translation functions for receiving files and translating them into ASCII */ CHAR #ifdef CK_ANSIC zl1as(CHAR c) #else zl1as(c) CHAR c; #endif /* CK_ANSIC */ { /* zl1as */ switch(langs[language].id) { case L_DUTCH: if (c == 255) { /* Dutch umlaut-y */ zdstuff('j'); /* becomes ij */ return('i'); } else return(yl1as[c]); /* all others by the book */ case L_GERMAN: switch (c) { /* German, special rules. */ case 196: /* umlaut-A -> Ae */ zdstuff('e'); return('A'); case 214: /* umlaut-O -> Oe */ zdstuff('e'); return('O'); case 220: /* umlaut-U -> Ue */ zdstuff('e'); return('U'); case 228: /* umlaut-a -> ae */ zdstuff('e'); return('a'); case 246: /* umlaut-o -> oe */ zdstuff('e'); return('o'); case 252: /* umlaut-u -> ue */ zdstuff('e'); return('u'); case 223: /* ess-zet -> ss */ zdstuff('s'); return('s'); default: return(yl1as[c]); /* all others by the book */ } case L_DANISH: case L_FINNISH: case L_NORWEGIAN: case L_SWEDISH: switch (c) { /* Scandanavian languages. */ case 196: /* umlaut-A -> Ae */ zdstuff('e'); return('A'); case 214: /* umlaut-O -> Oe */ case 216: /* O-slash -> Oe */ zdstuff('e'); return('O'); case 220: /* umlaut-U -> Y */ /* return('Y'); */ zdstuff('e'); return('U'); case 228: /* umlaut-a -> ae */ zdstuff('e'); return('a'); case 246: /* umlaut-o -> oe */ case 248: /* o-slash -> oe */ zdstuff('e'); return('o'); case 252: /* umlaut-u -> y */ /* return('y'); */ zdstuff('e'); return('u'); case 197: /* A-ring -> Aa */ zdstuff('a'); return('A'); case 229: /* a-ring -> aa */ zdstuff('a'); return('a'); default: return(yl1as[c]); /* All others by the book */ } default: return(yl1as[c]); /* No language, go by the table. */ } } CHAR /* IBM CP437 to Latin-1 */ #ifdef CK_ANSIC x43l1(CHAR c) #else x43l1(c) CHAR c; #endif /* CK_ANSIC */ { /* x43l1 */ return(y43l1[c]); } CHAR /* IBM CP850 to Latin-1 */ #ifdef CK_ANSIC x85l1(CHAR c) #else x85l1(c) CHAR c; #endif /* CK_ANSIC */ { /* x85l1 */ return(y85l1[c]); } CHAR /* Latin-1 to IBM CP437 */ #ifdef CK_ANSIC xl143(CHAR c) #else xl143(c) CHAR c; #endif /* CK_ANSIC */ { /* xl143 */ return(yl143[c]); } CHAR /* Latin-1 to CP850 */ #ifdef CK_ANSIC xl185(CHAR c) #else xl185(c) CHAR c; #endif /* CK_ANSIC */ { /* xl185 */ return(yl185[c]); } CHAR #ifdef CK_ANSIC x43as(CHAR c) #else x43as(c) CHAR c; #endif /* CK_ANSIC */ { /* x43as */ /* CP437 to ASCII */ c = y43l1[c]; /* Translate to Latin-1 */ return(xl143(c)); /* and from Latin-1 to ASCII. */ } CHAR #ifdef CK_ANSIC x85as(CHAR c) #else x85as(c) CHAR c; #endif /* CK_ANSIC */ { /* x85as */ /* CP850 to ASCII */ c = y85l1[c]; /* Translate to Latin-1 */ return(xl1as(c)); /* and from Latin-1 to ASCII. */ } CHAR /* Macintosh Latin to Latin-1 */ #ifdef CK_ANSIC xaql1(CHAR c) #else xaql1(c) CHAR c; #endif /* CK_ANSIC */ { /* xaql1 */ if (langs[language].id == L_FRENCH) { /* If SET LANGUAGE FRENCH */ if (c == 206) { /* handle OE digraph. */ zmstuff('E'); return('O'); } else if (c == 207) { /* Also lowercase oe. */ zmstuff('e'); return('o'); } } return(yaql1[c]); } CHAR /* Macintosh Latin to ASCII */ #ifdef CK_ANSIC xaqas(CHAR c) #else xaqas(c) CHAR c; #endif /* CK_ANSIC */ { /* xaqas */ if (langs[language].id == L_FRENCH) { /* If SET LANGUAGE FRENCH */ if (c == 206) { /* handle OE digraph. */ zmstuff('E'); return('O'); } else if (c == 207) { /* Also lowercase oe. */ zmstuff('e'); return('o'); } } c = yaql1[c]; /* Translate to Latin-1 */ return(xl1as(c)); /* then to ASCII. */ } CHAR /* Latin-1 to Macintosh Latin */ #ifdef CK_ANSIC xl1aq(CHAR c) #else xl1aq(c) CHAR c; #endif /* CK_ANSIC */ { /* xl1aq */ return(yl1aq[c]); } #ifdef LATIN2 /* Translation functions for Latin Alphabet 2 */ CHAR /* Latin-2 to Latin-1 */ #ifdef CK_ANSIC xl2l1(CHAR c) #else xl2l1(c) CHAR c; #endif /* CK_ANSIC */ { /* xll2l1 */ return(yl2l1[c]); } CHAR #ifdef CK_ANSIC xl2w1(CHAR c) #else xl2w1(c) CHAR c; #endif /* CK_ANSIC */ { /* xl2w1 */ /* Latin-2 to CP1252 (Windows L1) */ if (c > 127 && c < 160) return(UNK); else return(yl2l1[c]); } CHAR /* Latin-1 to Latin-2 */ #ifdef CK_ANSIC xl1l2(CHAR c) #else xl1l2(c) CHAR c; #endif /* CK_ANSIC */ { /* xll1l2 */ return(yl1l2[c]); } CHAR /* CP1252 to Latin-1 */ #ifdef CK_ANSIC xw1l2(CHAR c) #else xw1l2(c) CHAR c; #endif /* CK_ANSIC */ { /* xw1l2 */ switch (c) { case 0x8a: return(0xa9); /* S caron */ case 0x8e: return(0xae); /* Z caron */ case 0x9a: return(0xb9); /* s caron */ case 0x9e: return(0xbe); /* z caron */ default: return((c < 160) ? xw1as(c) : xl1l2(c)); } } CHAR /* Latin-2 to ASCII */ #ifdef CK_ANSIC xl2as(CHAR c) #else xl2as(c) CHAR c; #endif /* CK_ANSIC */ { /* xll2as */ return(yl2as[c]); } CHAR /* Latin-2 to CP852 */ #ifdef CK_ANSIC xl252(CHAR c) #else xl252(c) CHAR c; #endif /* CK_ANSIC */ { /* xll252 */ return(yl252[c]); } CHAR /* Latin-2 to Mazovia */ #ifdef CK_ANSIC xl2mz(CHAR c) #else xl2mz(c) CHAR c; #endif /* CK_ANSIC */ { /* xll2mz */ return(yl2mz[c]); } CHAR /* Latin-1 to Mazovia */ #ifdef CK_ANSIC xl1mz(CHAR c) #else xl1mz(c) CHAR c; #endif /* CK_ANSIC */ { /* xll1mz */ return(yl2mz[yl1l2[c]]); } CHAR /* Mazovia to Latin-1 */ #ifdef CK_ANSIC xmzl1(CHAR c) #else xmzl1(c) CHAR c; #endif /* CK_ANSIC */ { /* xmzl1 */ return(yl2l1[ymzl2[c]]); } CHAR /* Mazovia to Latin-9 */ #ifdef CK_ANSIC xmzl9(CHAR c) #else xmzl9(c) CHAR c; #endif /* CK_ANSIC */ { /* xmzl9 */ return(xl2l9(ymzl2[c])); } CHAR /* CP852 to Latin-2 */ #ifdef CK_ANSIC x52l2(CHAR c) #else x52l2(c) CHAR c; #endif /* CK_ANSIC */ { /* x52l2 */ return(y52l2[c]); } CHAR /* Mazovia to Latin-2 */ #ifdef CK_ANSIC xmzl2(CHAR c) #else xmzl2(c) CHAR c; #endif /* CK_ANSIC */ { /* xmzl2 */ return(ymzl2[c]); } CHAR /* Latin-2 to CP1250 */ #ifdef CK_ANSIC xl21250(CHAR c) #else xl21250(c) CHAR c; #endif /* CK_ANSIC */ { /* xll21250 */ return(yl21250[c]); } CHAR /* CP1250 to Latin-2 */ #ifdef CK_ANSIC x1250l2(CHAR c) #else x1250l2(c) CHAR c; #endif /* CK_ANSIC */ { /* x1250l2 */ return(y1250l2[c]); } CHAR /* CP852 to ASCII */ #ifdef CK_ANSIC x52as(CHAR c) #else x52as(c) CHAR c; #endif /* CK_ANSIC */ { /* xl52as */ return(yl2as[y52l2[c]]); /* CP852 -> Latin-2 -> ASCII */ } CHAR /* CP1250 to ASCII */ #ifdef CK_ANSIC x1250as(CHAR c) #else x1250as(c) CHAR c; #endif /* CK_ANSIC */ { /* xl1250as */ return(yl2as[y1250l2[c]]); /* CP81250 -> Latin-2 -> ASCII */ } CHAR /* CP852 to Latin-1 */ #ifdef CK_ANSIC x52l1(CHAR c) #else x52l1(c) CHAR c; #endif /* CK_ANSIC */ { /* xl52l1 */ return(yl2l1[y52l2[c]]); /* CP852 -> Latin-2 -> Latin-1 */ } CHAR /* CP1250 to Latin-1 */ #ifdef CK_ANSIC x1250l1(CHAR c) #else x1250l1(c) CHAR c; #endif /* CK_ANSIC */ { /* xl1250l1 */ return(yl2l1[y1250l2[c]]); /* CP1250 -> Latin-2 -> Latin-1 */ } CHAR /* CP1250 to Latin-9 */ #ifdef CK_ANSIC x1250l9(CHAR c) #else x1250l9(c) CHAR c; #endif /* CK_ANSIC */ { /* x1250l9 */ if (c == (CHAR)128) /* Euro */ return((CHAR)164); else return(xl2l9(y1250l2[c])); /* CP1250 -> Latin-2 -> Latin-9 */ } CHAR /* Latin-1 to CP852 */ #ifdef CK_ANSIC xl152(CHAR c) #else xl152(c) CHAR c; #endif /* CK_ANSIC */ { /* xll152 */ return(yl252[yl1l2[c]]); /* Latin-1 -> Latin-2 -> CP852 */ } CHAR /* Latin-1 to CP1250 */ #ifdef CK_ANSIC xl11250(CHAR c) #else xl11250(c) CHAR c; #endif /* CK_ANSIC */ { /* xll11250 */ return(yl21250[yl1l2[c]]); /* Latin-1 -> Latin-2 -> CP1250 */ } CHAR /* Latin-9 to CP1250 */ #ifdef CK_ANSIC xl91250(CHAR c) #else xl91250(c) CHAR c; #endif /* CK_ANSIC */ { /* xll91250 */ if (c == (CHAR)164) /* Euro */ return((CHAR)128); else return(yl21250[xl9l2(c)]); /* Latin-9 -> Latin-2 -> CP1250 */ } CHAR /* Latin-9 to Mazovia */ #ifdef CK_ANSIC xl9mz(CHAR c) #else xl9mz(c) CHAR c; #endif /* CK_ANSIC */ { /* xll9mz */ return(yl2mz[xl9l2(c)]); /* Latin-9 -> Latin-2 -> Mazovia */ } CHAR /* Latin-9 to Mazovia */ #ifdef CK_ANSIC xmzas(CHAR c) #else xmzas(c) CHAR c; #endif /* CK_ANSIC */ { /* xmzas */ return(yl2as[xmzl2(c)]); /* Mazovia -> Latin-2 -> ASCII */ } CHAR /* Latin-2 to NeXT */ #ifdef CK_ANSIC xl2ne(CHAR c) #else xl2ne(c) CHAR c; #endif /* CK_ANSIC */ { /* xll2ne */ switch(c) { case 162: return(198); /* Breve */ case 163: return(232); /* L with stroke */ case 178: return(206); /* Ogonek */ case 179: return(248); /* l with stroke */ case 183: return(207); /* Caron */ case 189: return(205); /* Double acute */ case 208: return(144); /* D stroke = Eth */ case 240: return(230); /* d stroke = eth */ case 255: return(199); /* Dot above */ default: return(yl1ne[yl2l1[c]]); } } CHAR /* Latin-2 to CP437 */ #ifdef CK_ANSIC xl243(CHAR c) #else xl243(c) CHAR c; #endif /* CK_ANSIC */ { /* xll243 */ return(yl1l2[y43l1[c]]); } CHAR /* Latin-2 to CP850 */ #ifdef CK_ANSIC xl285(CHAR c) #else xl285(c) CHAR c; #endif /* CK_ANSIC */ { /* xll285 */ return(yl1l2[y85l1[c]]); } CHAR /* Latin-2 to Apple */ #ifdef CK_ANSIC xl2aq(CHAR c) #else xl2aq(c) CHAR c; #endif /* CK_ANSIC */ { /* xl2aq */ return(yl1aq[yl2l1[c]]); /* Could do more... */ } CHAR /* Latin-2 to DGI */ #ifdef CK_ANSIC xl2dg(CHAR c) #else xl2dg(c) CHAR c; #endif /* CK_ANSIC */ { /* xll2dg */ return(ydgl1[yl1l2[c]]); } CHAR /* Latin-2 to Short KOI */ #ifdef CK_ANSIC xl2sk(CHAR c) #else xl2sk(c) CHAR c; #endif /* CK_ANSIC */ { /* xll2sk */ return(islower(c) ? toupper(c) : c); } CHAR /* NeXT to Latin-2 */ #ifdef CK_ANSIC xnel2(CHAR c) #else xnel2(c) CHAR c; #endif /* CK_ANSIC */ { /* xnel2 */ switch (c) { case 144: return(208); /* D stroke = Eth */ case 198: return(162); /* Breve */ case 199: return(255); /* Dot above */ case 205: return(189); /* Double acute */ case 206: return(178); /* Ogonek */ case 207: return(183); /* Caron */ case 230: return(240); /* d stroke = eth */ case 232: return(163); /* L with stroke */ case 248: return(179); /* l with stroke */ default: return(yl1l2[ynel1[c]]); /* Others, go thru Latin-1 */ } } CHAR /* CP437 to Latin-2 */ #ifdef CK_ANSIC x43l2(CHAR c) #else x43l2(c) CHAR c; #endif /* CK_ANSIC */ { /* xl43l2 */ return(yl1l2[y43l1[c]]); } CHAR /* CP850 to Latin-2 */ #ifdef CK_ANSIC x85l2(CHAR c) #else x85l2(c) CHAR c; #endif /* CK_ANSIC */ { /* xl85l2 */ return(yl1l2[y85l1[c]]); } CHAR /* Apple to Latin-2 */ #ifdef CK_ANSIC xaql2(CHAR c) #else xaql2(c) CHAR c; #endif /* CK_ANSIC */ { /* xlaql2 */ switch (c) { case 249: return(162); /* Breve accent */ case 250: return(255); /* Dot accent */ case 253: return(189); /* Double acute */ default: return(yl1l2[yaql1[c]]); } } CHAR /* DGI to Latin-2 */ #ifdef CK_ANSIC xdgl2(CHAR c) #else xdgl2(c) CHAR c; #endif /* CK_ANSIC */ { /* xldgl2 */ return(yl1l2[ydgl1[c]]); /* (for now) */ } CHAR /* Short KOI to Latin-2 */ #ifdef CK_ANSIC xskl2(CHAR c) #else xskl2(c) CHAR c; #endif /* CK_ANSIC */ { /* xlskl2 */ return(islower(c) ? toupper(c) : c); } CHAR /* Latin-2 to German */ #ifdef CK_ANSIC xl2ge(CHAR c) #else xl2ge(c) CHAR c; #endif /* CK_ANSIC */ { /* xll2ge */ switch(c) { case 167: return(64); /* Paragraph sign */ case 196: return(91); /* A-diaeresis */ case 214: return(92); /* O-diaeresis */ case 220: return(93); /* U-diaeresis */ case 223: return(126); /* double-s */ case 228: return(123); /* a-diaeresis */ case 246: return(124); /* o-diaeresis */ case 252: return(125); /* u-diaeresis */ default: return(yl2as[c]); /* Others */ } } CHAR /* German to Latin-2 */ #ifdef CK_ANSIC xgel2(CHAR c) #else xgel2(c) CHAR c; #endif /* CK_ANSIC */ { /* xlgel2 */ if (c & 0x80) return(UNK); switch(c) { case 64: return(167); /* Paragraph sign */ case 91: return(196); /* A-diaeresis */ case 92: return(214); /* O-diaeresis */ case 93: return(220); /* U-diaeresis */ case 123: return(228); /* a-diaeresis */ case 126: return(223); /* double-s */ case 124: return(246); /* o-diaeresis */ case 125: return(252); /* u-diaeresis */ default: return(c); /* Others */ } } CHAR /* Latin-2 to Hungarian */ #ifdef CK_ANSIC xl2hu(CHAR c) #else xl2hu(c) CHAR c; #endif /* CK_ANSIC */ { /* xll2hu */ switch(c) { case 164: return(36); /* Currency symbol */ case 189: return(126); /* Double acute accent */ case 193: return(64); /* A-acute */ case 201: return(91); /* E-acute */ case 214: return(92); /* O-diaeresis */ case 220: return(93); /* U-diaeresis */ case 225: return(96); /* a-acute */ case 233: return(123); /* e-acute */ case 246: return(124); /* o-diaeresis */ case 252: return(125); /* u-diaeresis */ default: return(yl2as[c]); /* Others */ } } CHAR /* Hungarian to Latin-2 */ #ifdef CK_ANSIC xhul2(CHAR c) #else xhul2(c) CHAR c; #endif /* CK_ANSIC */ { /* xlhul2 */ if (c & 0x80) return(UNK); switch(c) { case 36: return(164); /* Currency symbol */ case 64: return(193); /* A-acute */ case 91: return(201); /* E-acute */ case 92: return(214); /* O-diaeresis */ case 93: return(220); /* U-diaeresis */ case 96: return(225); /* a-acute */ case 123: return(233); /* e-acute */ case 124: return(246); /* o-diaeresis */ case 125: return(252); /* u-diaeresis */ case 126: return(189); /* Double acute accent */ default: return(c); /* Others */ } } CHAR #ifdef CK_ANSIC xr8l2(CHAR c) #else xr8l2(c) CHAR c; #endif /* CK_ANSIC */ { /* xr8l2 */ /* Hewlett Packard Roman8 to Latin-2 */ switch (c) { case 235: return(169); /* S caron */ case 236: return(185); /* s caron */ default: return(yl1l2[yr8l1[c]]); } } CHAR #ifdef CK_ANSIC xl2r8(CHAR c) #else xl2r8(c) CHAR c; #endif /* CK_ANSIC */ { /* xl2r8 */ /* Latin-2 to Hewlett Packard Roman8 Character Set */ switch (c) { case 169: return(235); /* S caron */ case 185: return(236); /* s caron */ default: return(yr8l1[yl1l2[c]]); } } #else /* NOLATIN2 */ #define xl1mz NULL #define xmzl1 NULL #define xl2mz NULL #define xmzl2 NULL #define xl9mz NULL #define xmzl9 NULL #define xmzas NULL #define xl11250 NULL #define xl21250 NULL #define xl91250 NULL #define x1250as NULL #define x1250l1 NULL #define x1250l2 NULL #define x1250l9 NULL #define xl2l1 NULL #define xl2w1 NULL #define xl1l2 NULL #define xw1l2 NULL #define xl2as NULL #define xl252 NULL #define x52l2 NULL #define x52as NULL #define x52l1 NULL #define xl152 NULL #define xl2ne NULL #define xl243 NULL #define xl285 NULL #define xl2aq NULL #define xl2dg NULL #define xl2sk NULL #define xnel2 NULL #define x43l2 NULL #define x85l2 NULL #define xaql2 NULL #define xdgl2 NULL #define xskl2 NULL #define xl2ge NULL #define xgel2 NULL #define xl2hu NULL #define xhul2 NULL #define xl2r8 NULL #define xr8l2 NULL #endif /* LATIN2 */ /* This one can also be used for ELOT 927, Hebrew 7, etc */ CHAR #ifdef CK_ANSIC xassk(CHAR c) #else xassk(c) CHAR c; #endif /* CK_ANSIC */ { /* xassk */ /* ASCII to Short KOI */ if (c & 0x80) return(UNK); return((c > 95) ? (c - 32) : c); /* Fold columns 6-7 to 4-5 */ } #ifdef CYRILLIC /* Translation functions for Cyrillic character sets */ CHAR /* Latin/Cyrillic to CP866 */ #ifdef CK_ANSIC xlcac(CHAR c) #else xlcac(c) CHAR c; #endif /* CK_ANSIC */ { /* xlcac */ /* PC Code Page 866 */ return(ylcac[c]); } CHAR /* Latin/Cyrillic to */ #ifdef CK_ANSIC xlc55(CHAR c) #else xlc55(c) CHAR c; #endif /* CK_ANSIC */ { /* xlc55 */ /* PC Code Page 855 */ return(ylc55[c]); } CHAR /* Latin/Cyrillic to */ #ifdef CK_ANSIC xlc1251(CHAR c) #else xlc1251(c) CHAR c; #endif /* CK_ANSIC */ { /* xlc1251 */ /* PC Code Page 1251 */ return(ylc1251[c]); } CHAR /* Latin/Cyrillic to... */ #ifdef CK_ANSIC xlcbu(CHAR c) #else xlcbu(c) CHAR c; #endif /* CK_ANSIC */ { /* xlcbu */ /* Bulgarian PC Code Page */ return(ylcbu[c]); } CHAR /* Latin/Cyrillic to Old KOI-8 */ #ifdef CK_ANSIC xlck8(CHAR c) #else xlck8(c) CHAR c; #endif /* CK_ANSIC */ { /* xlck8 */ return(ylck8[c]); } CHAR /* Latin/Cyrillic to KOI8-R */ #ifdef CK_ANSIC xlckr(CHAR c) #else xlckr(c) CHAR c; #endif /* CK_ANSIC */ { /* xlckr */ switch(c) { case 0xa1: return(0xb3); /* Io */ case 0xf1: return(0xa3); /* io */ default: if (c > 0x7f && c < 0xc0) return(UNK); return(ylck8[c]); } } CHAR /* Latin/Cyrillic to KOI8-U */ #ifdef CK_ANSIC xlcku(CHAR c) #else xlcku(c) CHAR c; #endif /* CK_ANSIC */ { /* xlcku */ switch(c) { case 0xa1: return(0xb3); /* Io */ case 0xf1: return(0xa3); /* io */ case 0xf4: return(0xa4); /* Ukrainian ie */ case 0xf6: return(0xa6); /* Ukrainian i */ case 0xf7: return(0xa7); /* Ukrainian yi */ case 0xf3: return(0xad); /* Ukrainian ghe with upturn */ case 0xa4: return(0xb4); /* Ukrainian Ie */ case 0xa6: return(0xb6); /* Ukrainian I */ case 0xa7: return(0xb7); /* Ukrainian Yi */ case 0xa3: return(0xbd); /* Ukrainian Ghe with upturn */ default: if (c > 0x7f && c < 0xc0) return(UNK); return(ylck8[c]); } } CHAR #ifdef CK_ANSIC xlcsk(CHAR c) #else xlcsk(c) CHAR c; #endif /* CK_ANSIC */ { /* xlcsk */ /* Latin/Cyrillic to Short KOI */ return(ylcsk[c]); } CHAR #ifdef CK_ANSIC xlcas(CHAR c) #else xlcas(c) CHAR c; #endif /* CK_ANSIC */ { /* xlcas */ /* Latin/Cyrillic to ASCII */ if (langs[language].id == L_RUSSIAN) return(ylcsk[c]); else return((c > 127) ? '?' : c); } CHAR /* CP866 */ #ifdef CK_ANSIC xaclc(CHAR c) #else xaclc(c) CHAR c; #endif /* CK_ANSIC */ { /* xaclc */ /* to Latin/Cyrillic */ return(yaclc[c]); } CHAR /* CP855 */ #ifdef CK_ANSIC x55lc(CHAR c) #else x55lc(c) CHAR c; #endif /* CK_ANSIC */ { /* x55lc */ /* to Latin/Cyrillic */ return(y55lc[c]); } CHAR /* Bulgarian PC Code Page ... */ #ifdef CK_ANSIC xbulc(CHAR c) #else xbulc(c) CHAR c; #endif /* CK_ANSIC */ { /* xbulc */ /* to Latin/Cyrillic */ return(ybulc[c]); } CHAR /* CP1251 */ #ifdef CK_ANSIC x1251lc(CHAR c) #else x1251lc(c) CHAR c; #endif /* CK_ANSIC */ { /* x1251lc */ /* to Latin/Cyrillic */ return(y1251lc[c]); } CHAR /* Old KOI-8 to Latin/Cyrillic */ #ifdef CK_ANSIC xk8lc(CHAR c) #else xk8lc(c) CHAR c; #endif /* CK_ANSIC */ { /* xk8lc */ return(yk8lc[c]); } CHAR /* KOI8-R to Latin/Cyrillic */ #ifdef CK_ANSIC xkrlc(CHAR c) #else xkrlc(c) CHAR c; #endif /* CK_ANSIC */ { /* xkrlc */ if (c == 0xb3) return(0xa1); else if (c == 0xa3) return(0xf1); else if (c > 0x7f && c < 0xc0) return(UNK); return(yk8lc[c]); } CHAR /* KOI8-U to Latin/Cyrillic */ #ifdef CK_ANSIC xkulc(CHAR c) #else xkulc(c) CHAR c; #endif /* CK_ANSIC */ { /* xkulc */ switch (c) { case 0xb3: return(0xa1); /* Io */ case 0xa3: return(0xf1); /* io */ case 0xa4: return(0xf4); /* Ukrainian ie */ case 0xa6: return(0xf6); /* Ukrainian i */ case 0xa7: return(0xf7); /* Ukrainian yi */ case 0xad: return(0xf3); /* Ukrainian ghe with upturn */ case 0xb4: return(0xa4); /* Ukrainian Ie */ case 0xb6: return(0xa6); /* Ukrainian I */ case 0xb7: return(0xa7); /* Ukrainian Yi */ case 0xbd: return(0xa3); /* Ukrainian Ghe with upturn */ /* Note substitution of Gje for Ghe-Upturn, which is not in 8859-5 */ default: if (c > 0x7f && c < 0xc0) return(UNK); return(yk8lc[c]); } } CHAR #ifdef CK_ANSIC xskcy(CHAR c) #else xskcy(c) CHAR c; #endif /* CK_ANSIC */ { /* xskcy */ /* Short KOI to Latin/Cyrillic */ return(yskcy[c & 0x7f]); } CHAR #ifdef CK_ANSIC xascy(CHAR c) #else xascy(c) CHAR c; #endif /* CK_ANSIC */ { /* xascy */ /* ASCII to Latin/Cyrillic */ if (langs[language].id == L_RUSSIAN) { /* If LANGUAGE == RUSSIAN */ return(yskcy[c & 0x7f]); /* treat ASCII as Short KOI */ } else return((c > 127) ? '?' : c); } CHAR #ifdef CK_ANSIC xacas(CHAR c) #else xacas(c) CHAR c; #endif /* CK_ANSIC */ { /* xacas */ /* CP866 to ASCII */ if (langs[language].id == L_RUSSIAN) { c = yaclc[c]; /* First to Latin/Cyrillic */ return(ylcsk[c]); /* Then to Short KOI */ } else return((c > 127) ? '?' : c); } CHAR #ifdef CK_ANSIC x55as(CHAR c) #else x55as(c) CHAR c; #endif /* CK_ANSIC */ { /* x55as */ /* CP855 to ASCII */ if (langs[language].id == L_RUSSIAN) { c = y55lc[c]; /* First to Latin/Cyrillic */ return(ylcsk[c]); /* Then to Short KOI */ } else return((c > 127) ? '?' : c); } CHAR #ifdef CK_ANSIC x1251as(CHAR c) #else x1251as(c) CHAR c; #endif /* CK_ANSIC */ { /* x1251as */ /* CP81251 to ASCII */ if (langs[language].id == L_RUSSIAN) { c = y1251lc[c]; /* First to Latin/Cyrillic */ return(ylcsk[c]); /* Then to Short KOI */ } else return((c > 127) ? '?' : c); } CHAR #ifdef CK_ANSIC xskas(CHAR c) #else xskas(c) CHAR c; #endif /* CK_ANSIC */ { /* xskas */ /* Short KOI to ASCII */ return((c > 95) ? '?' : c); } CHAR #ifdef CK_ANSIC xk8as(CHAR c) #else xk8as(c) CHAR c; #endif /* CK_ANSIC */ { /* xk8as */ /* Old KOI-8 Cyrillic to ASCII */ if (langs[language].id == L_RUSSIAN) { c = yk8lc[c]; /* First to Latin/Cyrillic */ return(ylcsk[c]); /* Then to Short KOI */ } else return((c > 127) ? '?' : c); } CHAR #ifdef CK_ANSIC xl1sk(CHAR c) #else xl1sk(c) CHAR c; #endif /* CK_ANSIC */ { /* xl1sk */ /* Latin-1 to Short KOI */ c = zl1as(c); /* Convert to ASCII */ return(c = xassk(c)); /* Convert ASCII to Short KOI */ } CHAR #ifdef CK_ANSIC xw1lc(CHAR c) #else xw1lc(c) CHAR c; #endif /* CK_ANSIC */ { /* xw1lc */ /* CP1252 to Latin/Cyrillic */ return((c < 160) ? xw1as(c) : zl1as(c)); } CHAR #ifdef CK_ANSIC xaslc(CHAR c) #else xaslc(c) CHAR c; #endif /* CK_ANSIC */ { /* xaslc */ /* ASCII to Latin/Cyrillic */ if (langs[language].id == L_RUSSIAN) return(yskcy[c & 0x7f]); else return(c & 0x7f); } CHAR #ifdef CK_ANSIC xasac(CHAR c) #else xasac(c) CHAR c; #endif /* CK_ANSIC */ { /* xasac */ /* ASCII to CP866 */ if (c & 0x80) return(UNK); if (langs[language].id == L_RUSSIAN) { /* Use Short KOI */ c = xskcy(c); /* Translate to Latin/Cyrillic */ return(ylcac[c]); /* Then to CP866 */ } else return(c & 0x7f); } CHAR #ifdef CK_ANSIC xas55(CHAR c) #else xas55(c) CHAR c; #endif /* CK_ANSIC */ { /* xas55 */ /* ASCII to CP855 */ if (c & 0x80) return(UNK); if (langs[language].id == L_RUSSIAN) { /* Use Short KOI */ c = xskcy(c); /* Translate to Latin/Cyrillic */ return(ylc55[c]); /* Then to CP866 */ } else return(c & 0x7f); } CHAR #ifdef CK_ANSIC xas1251(CHAR c) #else xas1251(c) CHAR c; #endif /* CK_ANSIC */ { /* xas1251 */ /* ASCII to CP81251 */ if (c & 0x80) return(UNK); if (langs[language].id == L_RUSSIAN) { /* Use Short KOI */ c = xskcy(c); /* Translate to Latin/Cyrillic */ return(ylc1251[c]); /* Then to CP866 */ } else return(c & 0x7f); } CHAR #ifdef CK_ANSIC xask8(CHAR c) #else xask8(c) CHAR c; #endif /* CK_ANSIC */ { /* xask8 */ /* ASCII to KOI-8 */ if (c & 0x80) return(UNK); if (langs[language].id == L_RUSSIAN) { /* Use Short KOI */ c = xskcy(c); /* Translate to Latin/Cyrillic */ return(ylck8[c]); /* Then to KOI-8 */ } else return(c & 0x7f); } #else /* No Cyrillic */ #define xacas NULL #define x55as NULL #define x1251as NULL #define xaclc NULL #define x55lc NULL #define x1251lc NULL #define xasac NULL #define xas55 NULL #define xas1251 NULL #define xascy NULL #define xask8 NULL #define xaslc NULL #define xassk NULL #define xk8as NULL #define xk8lc NULL #define xkrlc NULL #define xkulc NULL #define xl1sk NULL #define xw1lc NULL #define xlcac NULL #define xlc55 NULL #define xlc1251 NULL #define xlcas NULL #define xlck8 NULL #define xlckr NULL #define xlcku NULL #define xlch7 NULL #define xlcsk NULL #define xskas NULL #define xskcy NULL #define xbulc NULL #define xlcbu NULL #endif /* CYRILLIC */ /* Translation functions for Hebrew character sets */ #ifdef HEBREW CHAR #ifdef CK_ANSIC xash7(CHAR c) #else xash7(c) CHAR c; #endif /* CK_ANSIC */ { /* xash7 */ /* ASCII to Hebrew-7 */ if (c & 0x80) return(UNK); if (c == 96) return('?'); if (c > 96 && c < 123) return(c - 32); else return(c); } CHAR #ifdef CK_ANSIC xl1h7(CHAR c) #else xl1h7(c) CHAR c; #endif /* CK_ANSIC */ { /* xl1h7 */ /* Latin-1 to Hebrew-7 */ return(xash7(xl1as(c))); } CHAR #ifdef CK_ANSIC xl1lh(CHAR c) #else xl1lh(c) CHAR c; #endif /* CK_ANSIC */ { /* xl1lh */ /* Latin-1 to Latin/Hebrew */ switch(c) { case 170: return('a'); /* Feminine ordinal */ case 186: return('o'); /* Masculine ordinal */ case 215: return(170); /* Times */ case 247: return(186); /* Divide */ default: return( (c > 190) ? xl1as(c) : c ); } } CHAR #ifdef CK_ANSIC xw1lh(CHAR c) #else xw1lh(c) CHAR c; #endif /* CK_ANSIC */ { /* xw1lh */ /* CP1252 to Latin/Hebrew */ switch(c) { case 170: return('a'); /* Feminine ordinal */ case 186: return('o'); /* Masculine ordinal */ case 215: return(170); /* Times */ case 247: return(186); /* Divide */ default: if (c < 160) return(xw1as(c)); else return((c > 190) ? xl1as(c) : c); } } #ifdef LATIN2 CHAR #ifdef CK_ANSIC xl2h7(CHAR c) #else xl2h7(c) CHAR c; #endif /* CK_ANSIC */ { /* xl2h7 */ /* Latin-2 to Hebrew-7 */ return(xash7(xl2as(c))); } #else #define xl2h7 NULL #endif /* LATIN2 */ #ifndef NOCYRIL CHAR #ifdef CK_ANSIC xlch7(CHAR c) #else xlch7(c) CHAR c; #endif /* CK_ANSIC */ { /* xlch7 */ /* Latin/Cyrillic to Hebrew-7 */ return(xash7(xlcas(c))); } #endif /* NOCYRIL */ CHAR #ifdef CK_ANSIC xlhas(CHAR c) #else xlhas(c) CHAR c; #endif /* CK_ANSIC */ { /* xlhas */ /* Latin/Hebrew to ASCII */ return( (c > 127) ? '?' : c ); } CHAR #ifdef CK_ANSIC xlhl1(CHAR c) #else xlhl1(c) CHAR c; #endif /* CK_ANSIC */ { /* xlhl1 */ /* Latin/Hebrew to Latin-1 */ switch (c) { case 170: return(215); case 186: return(247); default: return( (c > 190) ? '?' : c ); } } CHAR #ifdef CK_ANSIC xlhw1(CHAR c) #else xlhw1(c) CHAR c; #endif /* CK_ANSIC */ { /* xlhw1 */ /* Latin/Hebrew to CP1252 */ if (c > 127 && c < 160) return('?'); switch (c) { case 170: return(215); case 186: return(247); default: return( (c > 190) ? '?' : c ); } } CHAR #ifdef CK_ANSIC xlh62(CHAR c) #else xlh62(c) CHAR c; #endif /* CK_ANSIC */ { /* xlh62 */ /* Latin/Hebrew to CP862 */ return(ylh62[c]); } CHAR #ifdef CK_ANSIC xl162(CHAR c) #else xl162(c) CHAR c; #endif /* CK_ANSIC */ { /* xl162 */ /* Latin-1 to CP862 */ return(xlh62(xl1lh(c))); /* Via Latin/Hebrew */ } CHAR #ifdef CK_ANSIC xlhh7(CHAR c) #else xlhh7(c) CHAR c; #endif /* CK_ANSIC */ { /* xlhh7 */ /* Latin/Hebrew to Hebrew-7 */ return(ylhh7[c]); } CHAR #ifdef CK_ANSIC xh7as(CHAR c) #else xh7as(c) CHAR c; #endif /* CK_ANSIC */ { /* xh7as */ /* Hebrew-7 to ASCII */ if (c & 0x80) return(UNK); return( (c > 95 && c < 123) ? '?' : c ); } CHAR #ifdef CK_ANSIC x62lh(CHAR c) #else x62lh(c) CHAR c; #endif /* CK_ANSIC */ { /* x62lh */ /* CP862 to Latin/Hebrew */ return(y62lh[c]); } CHAR #ifdef CK_ANSIC x62as(CHAR c) #else x62as(c) CHAR c; #endif /* CK_ANSIC */ { /* x62as */ /* CP862 to ASCII */ return( xlhas(x62lh(c)) ); } CHAR #ifdef CK_ANSIC x62l1(CHAR c) #else x62l1(c) CHAR c; #endif /* CK_ANSIC */ { /* x62l1 */ /* CP862 to Latin-1 */ return( xlhl1(x62lh(c)) ); } CHAR #ifdef CK_ANSIC xh7lh(CHAR c) #else xh7lh(c) CHAR c; #endif /* CK_ANSIC */ { /* xh7lh */ /* Hebrew-7 to Latin/Hebrew */ if (c & 0x80) return(UNK); return(yh7lh[c]); } #else /* No Hebrew */ #define xash7 NULL #define xl1h7 NULL #define xl2h7 NULL #define xlch7 NULL #define xl1lh NULL #define xw1lh NULL #define xlhas NULL #define xlhl1 NULL #define xlhw1 NULL #define xl162 NULL #define xlhh7 NULL #define xlh62 NULL #define xh7as NULL #define x62as NULL #define x62l1 NULL #define xh7lh NULL #define x62lh NULL #endif /* HEBREW */ /* Translation functions for Greek character sets */ #ifdef GREEK CHAR #ifdef CK_ANSIC xaseg(CHAR c) #else xaseg(c) CHAR c; #endif /* CK_ANSIC */ { /* xaseg */ /* ASCII to ELOT 927 */ if (c & 0x80) return(UNK); if (c > 96 && c < 123) return(c - 32); else return(c); } CHAR #ifdef CK_ANSIC xl1eg(CHAR c) #else xl1eg(c) CHAR c; #endif /* CK_ANSIC */ { /* xl1ge */ /* Latin-1 to ELOT 927 */ return(xaseg(xl1as(c))); } CHAR #ifdef CK_ANSIC xl2lg(CHAR c) #else xl2lg(c) CHAR c; #endif /* CK_ANSIC */ { /* xl2lg */ /* Latin-1 to Latin/Greek */ if (c < 160) return(c); else if (c == 160 || c == 168 || c == 173 || c == 174) return(c); else return('?'); } CHAR #ifdef CK_ANSIC xl1lg(CHAR c) #else xl1lg(c) CHAR c; #endif /* CK_ANSIC */ { /* xl1lg */ /* Latin-1 to Latin/Greek */ if (c < 160) return(c); switch(c) { case 160: /* Themselves */ case 164: case 166: case 167: case 168: case 169: case 171: case 172: case 173: case 176: case 177: case 178: case 179: case 180: case 187: case 189: return(c); case 181: /* Lowercase mu */ return(236); default: return(UNK); } } CHAR #ifdef CK_ANSIC xw1lg(CHAR c) #else xw1lg(c) CHAR c; #endif /* CK_ANSIC */ { /* xw1lg */ /* CP1252 to Latin/Greek */ return((c < 160) ? xw1as(c) : xl1lg(c)); } #ifdef LATIN2 CHAR #ifdef CK_ANSIC xl2eg(CHAR c) #else xl2eg(c) CHAR c; #endif /* CK_ANSIC */ { /* xl2eg */ /* Latin-2 to ELOT 927 */ return(xaseg(xl2as(c))); } #else #define xl2eg NULL #endif /* LATIN2 */ #ifndef NOCYRIL CHAR #ifdef CK_ANSIC xlceg(CHAR c) #else xlceg(c) CHAR c; #endif /* CK_ANSIC */ { /* xlceg */ /* Latin/Cyrillic to ELOT 927 */ return(xaseg(xlcas(c))); } #endif /* NOCYRIL */ CHAR #ifdef CK_ANSIC xlgas(CHAR c) #else xlgas(c) CHAR c; #endif /* CK_ANSIC */ { /* xlgas */ /* Latin/Greek to ASCII */ return( (c > 127) ? '?' : c ); } CHAR #ifdef CK_ANSIC xlgl1(CHAR c) #else xlgl1(c) CHAR c; #endif /* CK_ANSIC */ { /* xlgl1 */ /* Latin/Greek to Latin-1 */ if (c == 236) return(181); else return(xl1lg(c)); } CHAR #ifdef CK_ANSIC xlgw1(CHAR c) #else xlgw1(c) CHAR c; #endif /* CK_ANSIC */ { /* xlgw1 */ /* Latin/Greek to Latin-1 */ if (c > 127 && c < 160) return('?'); return(xlgl1(c)); } CHAR #ifdef CK_ANSIC xlg69(CHAR c) #else xlg69(c) CHAR c; #endif /* CK_ANSIC */ { /* xlg69 */ /* Latin/Greek to CP869 */ return(ylg69[c]); } CHAR #ifdef CK_ANSIC xl169(CHAR c) #else xl169(c) CHAR c; #endif /* CK_ANSIC */ { /* xl169 */ /* Latin-1 to CP869 */ return(xlg69(xl1lg(c))); /* Via Latin/Greek */ } CHAR #ifdef CK_ANSIC xlgeg(CHAR c) #else xlgeg(c) CHAR c; #endif /* CK_ANSIC */ { /* xlgeg */ /* Latin/Greek to ELOT 927 */ return(ylgeg[c]); } CHAR #ifdef CK_ANSIC xegas(CHAR c) #else xegas(c) CHAR c; #endif /* CK_ANSIC */ { /* xegas */ /* ELOT 927 to ASCII */ if (c & 0x80) return(UNK); return( (c > 96 && c < 123) ? '?' : c ); } CHAR #ifdef CK_ANSIC x69lg(CHAR c) #else x69lg(c) CHAR c; #endif /* CK_ANSIC */ { /* x69lg */ /* CP869 to Latin/Greek */ return(y69lg[c]); } CHAR #ifdef CK_ANSIC x69as(CHAR c) #else x69as(c) CHAR c; #endif /* CK_ANSIC */ { /* x69as */ /* CP869 to ASCII */ return( xlgas(x69lg(c)) ); } CHAR #ifdef CK_ANSIC x69l1(CHAR c) #else x69l1(c) CHAR c; #endif /* CK_ANSIC */ { /* x69l1 */ /* CP869 to Latin-1 */ return( xlgl1(x69lg(c)) ); } CHAR #ifdef CK_ANSIC xeglg(CHAR c) #else xeglg(c) CHAR c; #endif /* CK_ANSIC */ { /* xeglg */ /* ELOT 927 to Latin/Greek */ return(yeglg[c]); } #else /* No Greek */ #define x69as NULL #define x69l1 NULL #define x69lg NULL #define xaseg NULL #define xegas NULL #define xeglg NULL #define xl169 NULL #define xl1eg NULL #define xl1lg NULL #define xw1lg NULL #define xl2ge NULL #define xl2lg NULL #define xlcge NULL #define xlg69 NULL #define xlgas NULL #define xlgeg NULL #define xlgge NULL #define xlgl1 NULL #define xlgw1 NULL #endif /* GREEK */ /* Translation functions for Japanese Kanji character sets */ #ifdef KANJI /* Translate Kanji Transfer Character Set (EUC) to local file character set, contributed by Dr. Hirofumi Fujii, Japan High Energy Research Laboratory (KEK), Tokyo, Japan. a is a byte to be translated, which may be a single-byte character, the Katakana prefix, the first byte of a two-byte Kanji character, or the second byte of 2-byte Kanji character. fn is the output function. Returns 0 on success, -1 on failure. */ _PROTOTYP(static int jpnxas, (int, int[]) ); _PROTOTYP(static int jpnxkt, (int, int[]) ); _PROTOTYP(static int jpnxkn, (int[], int[]) ); static int jpncnt; /* Byte count for Japanese */ static int jpnlst; /* Last status (for JIS7) */ static int jpnxas(a, obuf) int a; int obuf[]; { /* Translate ASCII to local file code */ int r; r = 0; if (fcharset == FC_JIS7) { switch (jpnlst) { case 1: obuf[0] = 0x0f; obuf[1] = a; r = 2; break; case 2: obuf[0] = 0x1b; obuf[1] = 0x28; obuf[2] = 0x4a; obuf[3] = a; r = 4; break; default: obuf[0] = a; r = 1; break; } } else { obuf[0] = a; r = 1; } return(r); } static int jpnxkt(a, obuf) int a; int obuf[]; { /* Translate JIS X 201 Katakana to local code */ int r; r = 0; if (fcharset == FC_JIS7) { switch (jpnlst) { case 2: /* from Kanji */ obuf[r++] = 0x1b; obuf[r++] = 0x28; obuf[r++] = 0x4a; case 0: /* from Roman */ obuf[r++] = 0x0e; default: obuf[r++] = (a & 0x7f); break; } } else { if (fcharset == FC_JEUC) obuf[r++] = 0x8e; obuf[r++] = (a | 0x80); } return(r); } static int jpnxkn(ibuf, obuf) int ibuf[], obuf[]; { /* Translate JIS X 0208 Kanji to local code */ int c1, c2; int r; c1 = ibuf[0] & 0x7f; c2 = ibuf[1] & 0x7f; if (fcharset == FC_SHJIS) { if (c1 & 1) c2 += 0x1f; else c2 += 0x7d; if (c2 >= 0x7f) c2++; c1 = ((c1 - 0x21) >> 1) + 0x81; if (c1 > 0x9f) c1 += 0x40; obuf[0] = c1; obuf[1] = c2; r = 2; } else if (fcharset == FC_JIS7) { r = 0; switch (jpnlst) { case 1: obuf[r++] = 0x0f; /* From Katakana */ case 0: obuf[r++] = 0x1b; obuf[r++] = 0x24; obuf[r++] = 0x42; default: obuf[r++] = c1; obuf[r++] = c2; break; } } else { obuf[0] = (c1 | 0x80); obuf[1] = (c2 | 0x80); r = 2; } return(r); } int xkanjf() { /* Initialize parameters for xkanji */ /* This function should be called when F/X-packet is received */ jpncnt = jpnlst = 0; return(0); } int #ifdef CK_ANSIC xkanjz(int (*fn)(char)) #else xkanjz(fn) int (*fn)(); #endif /* CK_ANSIC */ { /* xkanjz */ /* Terminate xkanji This function must be called when Z-packet is received (before closing the file). */ static int obuf[6]; int r, i, c; if (fcharset == FC_JIS7) { c = 'A'; /* Dummy Roman character */ r = jpnxas(c, obuf) - 1; /* -1 removes Dummy character */ if (r > 0) { for (i = 0; i < r; i++) if (((*fn)((char) obuf[i])) < 0) return(-1); } } return(0); } int #ifdef CK_ANSIC xkanji(int a, int (*fn)(char)) #else xkanji(a, fn) int a; int (*fn)(); #endif /* CK_ANSIC */ { /* xkanji */ static int xbuf[2]; static int obuf[8]; int i, r; int c7; int state=0; r = 0; if (jpncnt == 0) { /* 1st byte */ if ((a & 0x80) == 0) { /* 8th bit is 0, i.e., single-byte code */ r = jpnxas(a, obuf); state = 0; } else { /* 8th bit is 1, check the range */ c7 = a & 0x7f; if (((c7 > 0x20) && (c7 < 0x7f)) || (c7 == 0x0e)) { /* double byte code */ xbuf[jpncnt++] = a; } else { /* single byte code */ r = jpnxas(a, obuf); state = 0; } } } else { /* not the 1st byte */ xbuf[jpncnt++] = a; if (xbuf[0] == 0x8e) { r = jpnxkt(xbuf[1], obuf); state = 1; } else { r = jpnxkn(xbuf, obuf); state = 2; } } if (r > 0) { for (i = 0; i < r; i++ ) if (((*fn)((char) obuf[i])) < 0) return(-1); jpnlst = state; jpncnt = 0; } return(0); } /* Function for translating from Japanese file character set to Japanese EUC transfer character set. Returns a pointer to a string containing 0, 1, or 2 bytes. */ /* zkanji */ static int jpnstz; /* status for JIS-7 */ static int jpnpnd; /* number of pending bytes */ static int jpnpnt; /* pending buffer index */ static int jpnpbf[8]; /* pending buffer */ /* There is some duplication here between the old and new JIS-7 parsers */ /* to be cleaned up later... */ VOID j7init() { /* Initialize JIS-7 parser */ jpnstz = 0; jpnpnd = 0; jpnpnt = 0; } int getj7() { /* Reads JIS-7 returns next EUC byte */ int x; if (jpnpnd > 0) { /* If something is pending */ x = (unsigned) jpnpbf[jpnpnt++]; /* Get it */ jpnpnd--; if (jpnpnd < 0) jpnpnd = 0; return((unsigned)x); } jpnpnt = 0; if ((x = zminchar()) < 0) return(x); while (jpnpnd == 0) { /* While something is pending... */ if ((x > 0x20) && (x < 0x7f)) { /* 7-bit graphic character */ switch (jpnstz) { case 1: /* Katakana */ #ifdef COMMENT /* This can't be right... */ jpnpbf[jpnpnd++] = 0x80; /* Insert flag (NOT SS2???) */ #else jpnpbf[jpnpnd++] = 0x8e; /* Insert SS2 */ #endif /* COMMENT */ jpnpbf[jpnpnd++] = (x | 0x80); /* Insert Kana + 8th bit */ break; case 2: /* Kanji */ jpnpbf[jpnpnd++] = (x | 0x80); /* Get another byte */ if ((x = zminchar()) < 0) return(x); jpnpbf[jpnpnd++] = (x | 0x80); break; default: /* ASCII / JIS Roman */ jpnpbf[jpnpnd++] = x; break; } } else if (x == 0x0e) { /* ^N = SO */ jpnstz = 1; /* Katakana */ if ((x = zminchar()) < 0) return(x); } else if (x == 0x0f) { /* ^O = SI */ jpnstz = 0; /* ASCII / JIS Roman */ if ((x = zminchar()) < 0) return(x); } else if (x == 0x1b) { /* Escape */ jpnpbf[jpnpnd++] = x; /* Save in buffer */ if ((x = zminchar()) < 0) return(x); jpnpbf[jpnpnd++] = x; /* Save in buffer */ if (x == '$') { /* $ */ if ((x = zminchar()) < 0) return(x); jpnpbf[jpnpnd++] = x; if ((x == '@') || (x == 'B')) { /* Kanji */ jpnstz = 2; jpnpnt = jpnpnd = 0; if ((x = zminchar()) < 0) return(x); } } else if (x == '(') { /* ( == 94-byte single-byte set */ if ((x = zminchar()) < 0) return(x); jpnpbf[jpnpnd++] = x; if ((x == 'B') || (x == 'J')) { /* ASCII or JIS Roman */ jpnstz = 0; /* Set state */ jpnpnt = jpnpnd = 0; /* Reset pointers */ if ((x = zminchar()) < 0) return(x); } } else if (x == 0x1b) { /* */ jpnpnt = jpnpnd = 0; /* Reset pointers, stay in state */ if ((x = zminchar()) < 0) return(x); } } else { /* Not - just save it */ jpnpbf[jpnpnd++] = x; } } jpnpnt = 0; x = (unsigned)jpnpbf[jpnpnt++]; jpnpnd--; return((unsigned)x); } USHORT #ifdef CK_ANSIC eu_to_sj(USHORT eu) /* EUC-JP to Shift-JIS */ #else eu_to_sj(eu) USHORT eu; #endif /* CK_ANSIC */ { int c1, c2; union ck_short jcode,scode; jcode.x_short = eu; c1 = (jcode.x_char[byteorder] & 0x7f); c2 = (jcode.x_char[1-byteorder] & 0x7f); if (c1 & 1) c2 += 0x1f; else c2 += 0x7d; if (c2 >= 0x7f) c2++; c1 = ((c1 - 0x21) >> 1) + 0x81; if (c1 > 0x9f) c1 += 0x40; scode.x_char[byteorder] = c1; scode.x_char[1-byteorder] = c2; return(scode.x_short); } USHORT #ifdef CK_ANSIC sj_to_eu(USHORT sj) /* Shift-JIS to EUC-JP */ #else sj_to_eu(sj) USHORT sj; #endif /* CK_ANSIC */ { union ck_short jcode, scode; int c0, c1; scode.x_short = sj; c0 = scode.x_char[byteorder]; /* Left (hi order) byte */ c1 = scode.x_char[1-byteorder]; /* Right (lo order) byte */ if (((c0 >= 0x81) && (c0 <= 0x9f)) || /* High order byte has 8th bit set */ ((c0 >= 0xe0) && (c0 <= 0xfc))) { /* Kanji */ if (c0 <= 0x9f) /* Two bytes in */ c0 -= 0x71; /* Do the shifting... */ else c0 -= 0xb1; c0 = c0 * 2 + 1; if (c1 > 0x7f) c1 -= 1; if (c1 >= 0x9e) { c1 -= 0x7d; c0 += 1; } else { c1 -= 0x1f; } jcode.x_char[byteorder] = (c0 | 0x80); /* Two bytes out */ jcode.x_char[1-byteorder] = (c1 | 0x80); } else if (c0 == 0) { /* Single byte */ if (c1 >= 0xa1 && c1 <= 0xdf) { /* Katakana */ jcode.x_char[byteorder] = 0x8e; /* SS2 */ jcode.x_char[1-byteorder] = c1; /* Kana code */ } else { /* ASCII or C0 */ jcode.x_short = c1; } } else { /* Something bad */ debug(F001,"sj_to_eu bad sj","",sj); jcode.x_short = 0xffff; } return(jcode.x_short); } int zkanjf() { /* Initialize */ jpnstz = jpnpnd = jpnpnt = 0; return(0); } int zkanjz() { return(0); } int #ifdef CK_ANSIC zkanji(int (*fn)(void)) #else zkanji(fn) int (*fn)(); #endif /* CK_ANSIC */ { /* zkanji */ /* Read Japanese local code and translate to Japanese EUC */ int a; int sc[3]; /* No pending characters */ if (fcharset == FC_SHJIS) { /* Translating from Shift-JIS */ if (jpnpnd) { jpnpnd--; return(jpnpbf[jpnpnt++]); } a = (*fn)(); jpnpnd = jpnpnt = 0; if (((a >= 0x81) && (a <= 0x9f)) || ((a >= 0xe0) && (a <= 0xfc))) { /* 2-byte Kanji code */ sc[0] = a; if ((sc[1] = (*fn)()) < 0) /* Get second byte */ return(sc[1]); if (sc[0] <= 0x9f) sc[0] -= 0x71; else sc[0] -= 0xb1; sc[0] = sc[0] * 2 + 1; if (sc[1] > 0x7f) sc[1]--; if (sc[1] >= 0x9e) { sc[1] -= 0x7d; sc[0]++; } else { sc[1] -= 0x1f; } a = (sc[0] | 0x80); jpnpbf[0] = (sc[1] | 0x80); jpnpnd = 1; jpnpnt = 0; } else if ((a >= 0xa1) && (a <= 0xdf)) { /* Katakana */ jpnpbf[0] = a; jpnpnd = 1; jpnpnt = 0; a = 0x8e; } return(a); } else if (fcharset == FC_JIS7 ) { /* 7-bit JIS X 0208 */ if (jpnpnd) { a = jpnpbf[jpnpnt++]; jpnpnd--; return(a); } jpnpnt = 0; if ((a = (*fn)()) < 0) return(a); while (jpnpnd == 0) { if ((a > 0x20) && (a < 0x7f)) { switch (jpnstz) { case 1: jpnpbf[jpnpnd++] = 0x80; /* Katakana */ jpnpbf[jpnpnd++] = (a | 0x80); break; case 2: jpnpbf[jpnpnd++] = (a | 0x80); /* Kanji */ if ((a = (*fn)()) < 0) return(a); jpnpbf[jpnpnd++] = (a | 0x80); break; default: jpnpbf[jpnpnd++] = a; /* Single byte */ break; } } else if (a == 0x0e) { jpnstz = 1; if ((a = (*fn)()) < 0) return(a); } else if (a == 0x0f) { jpnstz = 0; if ((a = (*fn)()) < 0) return(a); } else if (a == 0x1b) { jpnpbf[jpnpnd++] = a; /* Escape */ if ((a = (*fn)()) < 0) return(a); jpnpbf[jpnpnd++] = a; if (a == '$') { if ((a = (*fn)()) < 0) return(a); jpnpbf[jpnpnd++] = a; if ((a == '@') || (a == 'B')) { jpnstz = 2; jpnpnt = jpnpnd = 0; if ((a = (*fn)()) < 0) return(a); } } else if (a == '(') { if ((a = (*fn)()) < 0) return(a); jpnpbf[jpnpnd++] = a; if ((a == 'B') || (a == 'J')) { jpnstz = 0; jpnpnt = jpnpnd = 0; if ((a = (*fn)()) < 0) return(a); } } else if (a == 0x1b) { jpnpnt = jpnpnd = 0; if ((a = (*fn)()) < 0) return(a); } } else { jpnpbf[jpnpnd++] = a; } } jpnpnt = 0; a = jpnpbf[jpnpnt++]; jpnpnd--; return(a); } else { a = (*fn)(); return(a); } } #endif /* KANJI */ /* Euro functions */ #ifdef LATIN2 CHAR #ifdef CK_ANSIC xl2l9(CHAR c) #else xl2l9(c) CHAR c; #endif /* CK_ANSIC */ { /* xl2l9 */ /* Latin-2 to Latin-9... */ switch (c) { case 169: return((CHAR)166); /* S caron */ case 185: return((CHAR)168); /* s caron */ case 174: return((CHAR)180); /* Z caron */ case 190: return((CHAR)184); /* z caron */ default: return(yl2l1[c]); } } _PROTOTYP( CHAR xl258, ( CHAR ) ); CHAR #ifdef CK_ANSIC xl258(CHAR c) #else xl258(c) CHAR c; #endif /* CK_ANSIC */ { /* xl258 */ /* Latin-2 to CP858... */ return(c); } #else #define xl2l9 NULL #define xl258 NULL #endif /* LATIN2 */ CHAR #ifdef CK_ANSIC zl9as(CHAR c) #else zl9as(c) CHAR c; #endif /* CK_ANSIC */ { /* zl9as */ /* Latin-9 to US ASCII... */ if (c < (CHAR)0x80) return(c); /* Save a function call */ switch (c) { case 0xa4: return(UNK); /* Euro */ case 0xa6: return('S'); /* S Caron */ case 0xa8: return('s'); /* s Caron */ case 0xb4: return('Z'); /* Z Caron */ case 0xbc: return('O'); /* OE digraph */ case 0xbd: return('o'); /* oe digraph */ case 0xbe: return('Y'); /* Y diaeresis */ default: return(zl1as(c)); /* The rest is like Latin-1 */ } } _PROTOTYP( CHAR xl9as, ( CHAR ) ); CHAR #ifdef CK_ANSIC xl9as(CHAR c) #else xl9as(c) CHAR c; #endif /* CK_ANSIC */ { /* xl9as */ /* Latin-9 to US ASCII... */ if (c < (CHAR)0x80) return(c); /* Save a function call */ switch (c) { case 0xa4: return(UNK); /* Euro */ case 0xa6: return('S'); /* S Caron */ case 0xa8: return('s'); /* s Caron */ case 0xb4: return('Z'); /* Z Caron */ case 0xb8: return('z'); /* z Caron */ case 0xbc: return('O'); /* OE digraph */ case 0xbd: return('o'); /* oe digraph */ case 0xbe: return('Y'); /* Y diaeresis */ default: return(xl1as(c)); /* The rest is like Latin-1 */ } } CHAR /* CP1252 to Latin-9 */ #ifdef CK_ANSIC xw1l9(CHAR c) #else xw1l9(c) CHAR c; #endif /* CK_ANSIC */ { /* xw1l9 */ switch (c) { case 0x80: return(0xa4); /* Euro sign */ case 0x8a: return(0xa6); /* S caron */ case 0x8c: return(0xbc); /* OE */ case 0x8e: return(0xb4); /* Z caron */ case 0x9a: return(0xa8); /* s caron */ case 0x9c: return(0xbd); /* oe */ case 0x9e: return(0xb8); /* z caron */ case 0x9f: return(0xbe); /* Y diaeresis */ case 0xa4: /* Currency sign */ case 0xa6: /* Broken vertical bar */ case 0xa8: /* Diaeresis */ case 0xb4: /* Acute accent */ case 0xb8: /* Cedilla */ case 0xbc: /* 1/4 */ case 0xbd: /* 1/2 */ case 0xbe: return('?'); /* 3/4 */ default: return((c < 160) ? xw1as(c) : c); } } #ifdef LATIN2 CHAR #ifdef CK_ANSIC xl9l2(CHAR c) #else xl9l2(c) CHAR c; #endif /* CK_ANSIC */ { /* xl9l2 */ /* Latin-9 to Latin-2... */ if (c < (CHAR)0x80) return(c); /* Save a function call */ switch (c) { case 0xa4: return(UNK); /* Euro */ case 0xa6: return((CHAR)0xa9); /* S Caron */ case 0xa8: return((CHAR)0xb9); /* s Caron */ case 0xb4: return((CHAR)0xae); /* Z Caron */ case 0xb8: return((CHAR)0xaf); /* z Caron */ case 0xbc: return('O'); /* OE digraph */ case 0xbd: return('o'); /* oe digraph */ case 0xbe: return('Y'); /* Y diaeresis */ default: return(xl1l2(c)); /* The rest is like Latin-1 */ } } #else #define xl9l2 NULL #endif /* LATIN2 */ CHAR #ifdef CK_ANSIC xl958(CHAR c) #else xl958(c) CHAR c; #endif /* CK_ANSIC */ { /* xl958 */ /* Latin-9 to CP858... */ if (c == 0xa4) /* Euro Symbol */ return((CHAR)0xd5); else if (c == 0x9e) /* This was currency symbol */ return((CHAR)0xcf); c = yl185[c]; return(c); } CHAR #ifdef CK_ANSIC x58as(CHAR c) #else x58as(c) CHAR c; #endif /* CK_ANSIC */ { /* x58as */ /* CP858 to US ASCII... */ if (c == 0xd5) /* Euro rather than dotless i */ return(UNK); else return(x85as(c)); /* The rest is like CP850 */ } CHAR #ifdef CK_ANSIC x58l1(CHAR c) #else x58l1(c) CHAR c; #endif /* CK_ANSIC */ { /* x58l1 */ /* CP858 to Latin-1... */ if (c == 0xd5) /* Euro rather than dotless i */ return((CHAR)0xa4); /* Return currency symbol */ else if (c == 0xcf) /* This keeps it invertible */ return((CHAR)0x9e); else return(x85l1(c)); } #ifdef LATIN2 CHAR #ifdef CK_ANSIC x58l2(CHAR c) #else x58l2(c) CHAR c; #endif /* CK_ANSIC */ { /* x58l2 */ /* CP858 to Latin-2... */ if (c == 0xd5) /* Euro rather than dotless i */ return((CHAR)0xa4); /* Return currency symbol */ else if (c == 0xcf) /* This keeps it invertible */ return((CHAR)0x9e); /* (if it ever was...) */ else /* Otherwise like CP850 */ return(x85l2(c)); } #else #define x58l2 NULL #endif /* LATIN2 */ CHAR #ifdef CK_ANSIC x58l9(CHAR c) #else x58l9(c) CHAR c; #endif /* CK_ANSIC */ { /* x58l9 */ /* CP-858 to Latin-9... */ if (c == 0xd5) /* Euro rather than dotless i */ return((CHAR)0xa4); /* Return currency symbol */ else if (c == 0xcf) /* This keeps it invertible */ return((CHAR)0x9e); /* (if it ever was...) */ else /* Otherwise like CP850 */ return(x85l1(c)); /* to Latin-1 */ } /* End Euro functions */ /* TABLES OF TRANSLATION FUNCTIONS */ /* First, the table of translation functions for RECEIVING files. That is, *from* the TRANSFER character set *to* the FILE character set, an array of pointers to functions. The first index is the TRANSFER CHARACTER-SET number, the second index is the FILE CHARACTER-SET number. These arrays must be fully populated, even if (as is the case with Kanji character sets), all the entries are NULL. Otherwise, subscript calculations will be wrong and we'll use the wrong functions. */ /* Pointers to byte-for-byte translation functions */ _PROTOTYP( CHAR (*rx), (CHAR) ); _PROTOTYP( CHAR (*sx), (CHAR) ); #ifdef UNICODE _PROTOTYP( int (*xut), (USHORT) ); /* Translation function UCS to TCS */ _PROTOTYP( int (*xuf), (USHORT) ); /* Translation function UCS to FCS */ _PROTOTYP( USHORT (*xtu), (CHAR) ); /* Translation function TCS to UCS */ _PROTOTYP( USHORT (*xfu), (CHAR) ); /* Translation function FCS to UCS */ #endif /* UNICODE */ #ifdef CK_ANSIC CHAR (*xlr[MAXTCSETS+1][MAXFCSETS+1])(CHAR) = #else CHAR (*xlr[MAXTCSETS+1][MAXFCSETS+1])() = #endif /* CK_ANSIC */ { NULL, /* 0,0 transparent to us ascii */ NULL, /* 0,1 transparent to uk ascii */ NULL, /* 0,2 transparent to dutch nrc */ NULL, /* 0,3 transparent to finnish nrc */ NULL, /* 0,4 transparent to french nrc */ NULL, /* 0,5 transparent to fr-canadian nrc */ NULL, /* 0,6 transparent to german nrc */ NULL, /* 0,7 transparent to hungarian nrc */ NULL, /* 0,8 transparent to italian nrc */ NULL, /* 0,9 transparent to norge/danish nrc */ NULL, /* 0,10 transparent to portuguese nrc */ NULL, /* 0,11 transparent to spanish nrc */ NULL, /* 0,12 transparent to swedish nrc */ NULL, /* 0,13 transparent to swiss nrc */ NULL, /* 0,14 transparent to latin-1 */ NULL, /* 0,15 transparent to latin-2 */ NULL, /* 0,16 transparent to DEC MCS */ NULL, /* 0,17 transparent to NeXT */ NULL, /* 0,18 transparent to CP437 */ NULL, /* 0,19 transparent to CP850 */ NULL, /* 0,20 transparent to CP852 */ NULL, /* 0,21 transparent to Macintosh Latin */ NULL, /* 0,22 transparent to DGI */ NULL, /* 0,23 transparent to HP */ NULL, /* 0,24 transparent to Latin/Cyrillic */ NULL, /* 0,25 transparent to CP866 */ NULL, /* 0,26 transparent to Short KOI-7 */ NULL, /* 0,27 transparent to Old KOI-8 Cyrillic */ NULL, /* 0,28 transparent to JIS-7 */ NULL, /* 0,29 transparent to Shift-JIS */ NULL, /* 0,30 transparent to J-EUC */ NULL, /* 0,31 transparent to DEC Kanji */ NULL, /* 0,32 transparent to Hebrew-7 */ NULL, /* 0,33 transparent to Latin/Hebrew */ NULL, /* 0,34 transparent to CP862 Hebrew */ NULL, /* 0,35 transparent to ELOT 927 Greek */ NULL, /* 0,36 transparent to Latin/Greek */ NULL, /* 0,37 transparent to CP869 */ NULL, /* 0,38 transparent to Latin-9 */ NULL, /* 0,39 transparent to CP858 */ NULL, /* 0,40 transparent to CP855 */ NULL, /* 0,41 transparent to CP1251 */ NULL, /* 0,42 transparent to Bulgarian */ NULL, /* 0,43 transparent to CP1250 */ NULL, /* 0,44 transparent to Mazovia */ NULL, /* 0,45 transparent to UCS-2 */ NULL, /* 0,46 transparent to UTF-8 */ NULL, /* 0,47 transparent to KOI8R */ NULL, /* 0,48 transparent to KOI8U */ NULL, /* 0,49 transparent to CP1252 */ NULL, /* 1,0 ascii to us ascii */ NULL, /* 1,1 ascii to uk ascii */ NULL, /* 1,2 ascii to dutch nrc */ NULL, /* 1,3 ascii to finnish nrc */ NULL, /* 1,4 ascii to french nrc */ NULL, /* 1,5 ascii to fr-canadian nrc */ NULL, /* 1,6 ascii to german nrc */ NULL, /* 1,7 ascii to hungarian nrc */ NULL, /* 1,8 ascii to italian nrc */ NULL, /* 1,9 ascii to norge/danish nrc */ NULL, /* 1,10 ascii to portuguese nrc */ NULL, /* 1,11 ascii to spanish nrc */ NULL, /* 1,12 ascii to swedish nrc */ NULL, /* 1,13 ascii to swiss nrc */ NULL, /* 1,14 ascii to latin-1 */ NULL, /* 1,15 ascii to latin-2 */ NULL, /* 1,16 ascii to DEC MCS */ NULL, /* 1,17 ascii to NeXT */ NULL, /* 1,18 ascii to CP437 */ NULL, /* 1,19 ascii to CP850 */ NULL, /* 1,20 ascii to CP852 */ NULL, /* 1,21 ascii to Macintosh Latin */ NULL, /* 1,22 ascii to DGI */ NULL, /* 1,23 ascii to HP */ xaslc, /* 1,24 ascii to Latin/Cyrillic */ xasac, /* 1,25 ascii to CP866 */ xassk, /* 1,26 ascii to Short KOI */ xask8, /* 1,27 ascii to Old KOI-8 Cyrillic */ NULL, /* 1,28 ascii to JIS-7 */ NULL, /* 1,29 ascii to Shift-JIS */ NULL, /* 1,30 ascii to J-EUC */ NULL, /* 1,31 ascii to DEC Kanji */ xash7, /* 1,32 ascii to Hebrew-7 */ NULL, /* 1,33 ascii to Latin/Hebrew */ NULL, /* 1,34 ascii to CP862 Hebrew */ xaseg, /* 1,35 ascii to ELOT 927 Greek */ NULL, /* 1,36 ascii to Latin/Greek */ NULL, /* 1,37 ascii to CP869 */ NULL, /* 1,38 ascii to Latin-9 */ NULL, /* 1,39 ascii to CP858 */ xas55, /* 1,40 ascii to CP855 */ xas1251, /* 1,41 ascii to CP1251 */ xleft128, /* 1,42 ascii to bulgarian */ xleft128, /* 1,43 ascii to CP1250 */ xleft128, /* 1,44 ascii to Mazovia */ NULL, /* 1,45 ascii to UCS-2 */ NULL, /* 1,46 ascii to UTF-8 */ NULL, /* 1,47 ascii to KOI8-R */ NULL, /* 1,48 ascii to KOI8-U */ NULL, /* 1,49 ascii to CP1252 */ zl1as, /* 2,0 latin-1 to us ascii */ xl1uk, /* 2,1 latin-1 to uk ascii */ xl1du, /* 2,2 latin-1 to dutch nrc */ xl1fi, /* 2,3 latin-1 to finnish nrc */ xl1fr, /* 2,4 latin-1 to french nrc */ xl1fc, /* 2,5 latin-1 to fr-canadian nrc */ xl1ge, /* 2,6 latin-1 to german nrc */ xl1it, /* 2,7 latin-1 to italian nrc */ xl1hu, /* 2,8 latin-1 to hungarian nrc */ xl1no, /* 2,9 latin-1 to norge/danish nrc */ xl1po, /* 2,10 latin-1 to portuguese nrc */ xl1sp, /* 2,11 latin-1 to spanish nrc */ xl1sw, /* 2,12 latin-1 to swedish nrc */ xl1ch, /* 2,13 latin-1 to swiss nrc */ NULL, /* 2,14 latin-1 to latin-1 */ xl1l2, /* 2,15 latin-1 to latin-2 */ xl1dm, /* 2,16 latin-1 to DEC MCS */ xl1ne, /* 2,17 latin-1 to NeXT */ xl143, /* 2,18 latin-1 to CP437 */ xl185, /* 2,19 latin-1 to CP850 */ xl152, /* 2,20 latin-1 to CP852 */ xl1aq, /* 2,21 latin-1 to Macintosh Latin */ xl1dg, /* 2,22 latin-1 to DGI */ xl1r8, /* 2,23 latin-1 to HP Roman8 */ zl1as, /* 2,24 latin-1 to Latin/Cyrillic */ zl1as, /* 2,25 latin-1 to CP866 */ xl1sk, /* 2,26 latin-1 to Short KOI */ zl1as, /* 2,27 latin-1 to Old KOI-8 Cyrillic */ NULL, /* 2,28 latin-1 to JIS-7 */ NULL, /* 2,29 latin-1 to Shift-JIS */ NULL, /* 2,30 latin-1 to J-EUC */ NULL, /* 2,31 latin-1 to DEC Kanji */ xl1h7, /* 2,32 latin-1 to Hebrew-7 */ xl1lh, /* 2,33 latin-1 to Latin/Hebrew */ xl162, /* 2,34 latin-1 to CP862 Hebrew */ xl1eg, /* 2,35 latin-1 to ELOT 927 Greek */ xl1lg, /* 2,36 latin-1 to Latin/Greek */ xl169, /* 2,37 latin-1 to CP869 */ NULL, /* 2,38 latin-1 to Latin9 */ xl185, /* 2,39 latin-1 to CP858 */ zl1as, /* 2,40 latin-1 to CP855 */ zl1as, /* 2,41 latin-1 to CP1251 */ zl1as, /* 2,42 latin-1 to Bulgarian */ xl11250, /* 2,43 latin-1 to CP1250 */ xl1mz, /* 2,44 latin-1 to Mazovia */ NULL, /* 2,45 latin-1 to UCS-2 */ NULL, /* 2,46 latin-1 to UTF-8 */ zl1as, /* 2,47 latin-1 to KOI8R */ zl1as, /* 2,48 latin-1 to KOI8U */ xl1w1, /* 2,49 latin-1 to CP1252 */ xl2as, /* 3,0 latin-2 to us ascii */ xl2as, /* 3,1 latin-2 to uk ascii */ xl2as, /* 3,2 latin-2 to dutch nrc */ xl2as, /* 3,3 latin-2 to finnish nrc */ xl2as, /* 3,4 latin-2 to french nrc */ xl2as, /* 3,5 latin-2 to fr-canadian nrc */ xl2as, /* 3,6 latin-2 to german nrc */ xl2as, /* 3,7 latin-2 to italian nrc */ xl2as, /* 3,8 latin-2 to hungarian nrc */ xl2as, /* 3,9 latin-2 to norge/danish nrc */ xl2as, /* 3,10 latin-2 to portuguese nrc */ xl2as, /* 3,11 latin-2 to spanish nrc */ xl2as, /* 3,12 latin-2 to swedish nrc */ xl2as, /* 3,13 latin-2 to swiss nrc */ xl2l1, /* 3,14 latin-2 to latin-1 */ NULL, /* 3,15 latin-2 to latin-2 */ xl2l1, /* 3,16 latin-2 to DEC MCS */ xl2ne, /* 3,17 latin-2 to NeXT */ xl243, /* 3,18 latin-2 to CP437 */ xl285, /* 3,19 latin-2 to CP850 */ xl252, /* 3,20 latin-2 to CP852 */ xl2aq, /* 3,21 latin-2 to Macintosh Latin */ xl2dg, /* 3,22 latin-2 to DGI */ xl2r8, /* 3,23 latin-2 to HP */ xl2as, /* 3,24 latin-2 to Latin/Cyrillic */ xl2as, /* 3,25 latin-2 to CP866 */ xl2sk, /* 3,26 latin-2 to Short KOI */ xl2as, /* 3,27 latin-2 to Old KOI-8 Cyrillic */ NULL, /* 3,28 latin-2 to JIS-7 */ NULL, /* 3,29 latin-2 to Shift-JIS */ NULL, /* 3,30 latin-2 to J-EUC */ NULL, /* 3,31 latin-2 to DEC Kanji */ xl2h7, /* 3,32 latin-2 to Hebrew-7 */ xl2as, /* 3,33 latin-2 to Latin/Hebrew */ xl2as, /* 3,34 latin-2 to CP862 Hebrew */ xassk, /* 3,35 latin-2 to ELOT 927 Greek */ xl2as, /* 3,36 latin-2 to Latin/Greek */ xl2as, /* 3,37 latin-2 to CP869 */ xl2l9, /* 3,38 latin-2 to Latin-9 */ xl258, /* 3,39 latin-2 to CP858 */ xl2as, /* 3,40 latin-2 to CP855 */ xl2as, /* 3,41 latin-2 to CP1251 */ xl2as, /* 3,42 latin-2 to Bulgarian */ xl21250, /* 3,43 latin-2 to CP1250 */ xl2mz, /* 3,44 latin-2 to Mazovia */ NULL, /* 3,45 latin-2 to UCS-2 */ NULL, /* 3,46 latin-2 to UTF-8 */ xl2as, /* 3,47 latin-2 to KOI8R */ xl2as, /* 3,48 latin-2 to KOI8U */ xl2w1, /* 3,49 latin-2 to CP1252 */ xlcas, /* 4,0 latin/cyrillic to us ascii */ xlcas, /* 4,1 latin/cyrillic to uk ascii */ xlcas, /* 4,2 latin/cyrillic to dutch nrc */ xlcas, /* 4,3 latin/cyrillic to finnish ascii */ xlcas, /* 4,4 latin/cyrillic to french nrc */ xlcas, /* 4,5 latin/cyrillic to fr-canadian nrc */ xlcas, /* 4,6 latin/cyrillic to german nrc */ xlcas, /* 4,7 latin/cyrillic to italian nrc */ xlcas, /* 4,8 latin/cyrillic to hungarian nrc */ xlcas, /* 4,9 latin/cyrillic to norge/danish nrc */ xlcas, /* 4,10 latin/cyrillic to portuguese nrc */ xlcas, /* 4,11 latin/cyrillic to spanish nrc */ xlcas, /* 4,12 latin/cyrillic to swedish nrc */ xlcas, /* 4,13 latin/cyrillic to swiss nrc */ xlcas, /* 4,14 latin/cyrillic to latin-1 */ xlcas, /* 4,15 latin/cyrillic to latin-2 */ xlcas, /* 4,16 latin/cyrillic to DEC MCS */ xlcas, /* 4,17 latin/cyrillic to NeXT */ xlcas, /* 4,18 latin/cyrillic to CP437 */ xlcas, /* 4,19 latin/cyrillic to CP850 */ xlcas, /* 4,20 latin/cyrillic to CP852 */ xlcas, /* 4,21 latin/cyrillic to Macintosh Latin */ xlcas, /* 4,22 latin/cyrillic to DGI */ xlcas, /* 4,23 latin/cyrillic to HP */ NULL, /* 4,24 latin/cyrillic to Latin/Cyrillic */ xlcac, /* 4,25 latin/cyrillic to CP866 */ xlcsk, /* 4,26 latin/cyrillic to Short KOI */ xlck8, /* 4,27 latin/cyrillic to Old KOI-8 Cyrillic */ NULL, /* 4,28 latin/cyril to JIS-7 */ NULL, /* 4,29 latin/cyril to Shift-JIS */ NULL, /* 4,30 latin/cyril to J-EUC */ NULL, /* 4,31 latin/cyril to DEC Kanji */ xlch7, /* 4,32 latin/cyril to Hebrew-7 */ xlcas, /* 4,33 latin/cyril to Latin/Hebrew */ xlcas, /* 4,34 latin/cyril to CP862 Hebrew */ xassk, /* 4,35 latin/cyril to ELOT 927 Greek */ xleft160, /* 4,36 latin/cyril to Latin/Greek */ xleft128, /* 4,37 latin/cyril to CP869 */ xlcas, /* 4,38 latin/cyril to latin-9 */ xlcas, /* 4,39 latin/cyril to CP858 */ xlc55, /* 4,40 latin/cyril to CP855 */ xlc1251, /* 4,41 latin/cyril to CP1251 */ xlcbu, /* 4,42 latin/cyril to Bulgarian */ xlcas, /* 4,43 latin/cyril to CP1250 */ xlcas, /* 4,44 latin/cyril to Mazovia */ NULL, /* 4,45 latin/cyril to UCS-2 */ NULL, /* 4,46 latin/cyril to UTF-8 */ xlckr, /* 4,47 latin/cyril to KOI8R */ xlcku, /* 4,48 latin/cyril to KOI8U */ xlcas, /* 4,49 latin/cyril to CP1252 */ NULL, /* 5,00 */ NULL, /* 5,01 */ NULL, /* 5,02 */ NULL, /* 5,03 */ NULL, /* 5,04 */ NULL, /* 5,05 */ NULL, /* 5,06 */ NULL, /* 5,07 */ NULL, /* 5,08 */ NULL, /* 5,09 */ NULL, /* 5,10 */ NULL, /* 5,11 */ NULL, /* 5,12 */ NULL, /* 5,13 */ NULL, /* 5,14 */ NULL, /* 5,15 */ NULL, /* 5,16 */ NULL, /* 5,17 */ NULL, /* 5,18 */ NULL, /* 5,19 */ NULL, /* 5,20 */ NULL, /* 5,21 */ NULL, /* 5,22 */ NULL, /* 5,23 */ NULL, /* 5,24 */ NULL, /* 5,25 */ NULL, /* 5,26 */ NULL, /* 5,27 */ NULL, /* 5,28 */ NULL, /* 5,29 */ NULL, /* 5,30 */ NULL, /* 5,31 */ NULL, /* 5,32 */ NULL, /* 5,33 */ NULL, /* 5,34 */ NULL, /* 5,35 */ NULL, /* 5,36 */ NULL, /* 5,37 */ NULL, /* 5,38 */ NULL, /* 5,39 */ NULL, /* 5,40 */ NULL, /* 5,41 */ NULL, /* 5,42 */ NULL, /* 5,43 */ NULL, /* 5,44 */ NULL, /* 5,45 */ NULL, /* 5,46 */ NULL, /* 5,47 */ NULL, /* 5,48 */ NULL, /* 5,49 */ xlhas, /* 6,0 latin/hebrew to us ascii */ xlhas, /* 6,1 latin/hebrew to uk ascii */ xlhas, /* 6,2 latin/hebrew to dutch nrc */ xlhas, /* 6,3 latin/hebrew to finnish ascii */ xlhas, /* 6,4 latin/hebrew to french nrc */ xlhas, /* 6,5 latin/hebrew to fr-canadian nrc */ xlhas, /* 6,6 latin/hebrew to german nrc */ xlhas, /* 6,7 latin/hebrew to italian nrc */ xlhas, /* 6,8 latin/hebrew to hungarian nrc */ xlhas, /* 6,9 latin/hebrew to norge/danish nrc */ xlhas, /* 6,10 latin/hebrew to portuguese nrc */ xlhas, /* 6,11 latin/hebrew to spanish nrc */ xlhas, /* 6,12 latin/hebrew to swedish nrc */ xlhas, /* 6,13 latin/hebrew to swiss nrc */ xlhl1, /* 6,14 latin/hebrew to latin-1 */ xlhas, /* 6,15 latin/hebrew to latin-2 */ xlhl1, /* 6,16 latin/hebrew to DEC MCS */ xlhas, /* 6,17 latin/hebrew to NeXT */ xlhas, /* 6,18 latin/hebrew to CP437 */ xlhas, /* 6,19 latin/hebrew to CP850 */ xlhas, /* 6,20 latin/hebrew to CP852 */ xlhas, /* 6,21 latin/hebrew to Macintosh Latin */ xlhas, /* 6,22 latin/hebrew to DGI */ xlhas, /* 6,23 latin/hebrew to HP */ xlhas, /* 6,24 latin/hebrew to Latin/Cyrillic */ xlhas, /* 6,25 latin/hebrew to CP866 */ NULL, /* 6,26 latin/hebrew to Short KOI */ xlhas, /* 6,27 latin/hebrew to Old KOI-8 Cyrillic */ NULL, /* 6,28 latin/hebrew to JIS-7 */ NULL, /* 6,29 latin/hebrew to Shift-JIS */ NULL, /* 6,30 latin/hebrew to J-EUC */ NULL, /* 6,31 latin/hebrew to DEC Kanji */ xlhh7, /* 6,32 latin/hebrew to Hebrew-7 */ NULL, /* 6,33 latin/hebrew to Latin/Hebrew */ xlh62, /* 6,34 latin/hebrew to CP862 Hebrew */ NULL, /* 6,35 latin/hebrew to ELOT 927 Greek */ xlhas, /* 6,36 latin/hebrew to Latin/Greek */ xlhas, /* 6,37 latin/hebrew to CP869 */ xlhas, /* 6,38 latin/hebrew to Latin-9 */ xlhas, /* 6,39 latin/hebrew to CP858 */ xlhas, /* 6,40 latin/hebrew to CP855 */ xlhas, /* 6,41 latin/hebrew to CP1251 */ xlhas, /* 6,42 latin/hebrew to Bulgarian */ xlhas, /* 6,43 latin/hebrew to CP1250 */ xlhas, /* 6,44 latin/hebrew to Mazovia */ NULL, /* 6,45 latin/hebrew to UCS-2 */ NULL, /* 6,46 latin/hebrew to UTF-8 */ NULL, /* 6,47 latin/hebrew to KOI8R */ NULL, /* 6,48 latin/hebrew to KOI8U */ xlhw1, /* 6,49 latin/hebrew to CP1252 */ xlgas, /* 7,0 latin/greek to us ascii */ xlgas, /* 7,1 latin/greek to uk ascii */ xlgas, /* 7,2 latin/greek to dutch nrc */ xlgas, /* 7,3 latin/greek to finnish ascii */ xlgas, /* 7,4 latin/greek to french nrc */ xlgas, /* 7,5 latin/greek to fr-canadian nrc */ xlgas, /* 7,6 latin/greek to german nrc */ xlgas, /* 7,7 latin/greek to italian nrc */ xlgas, /* 7,8 latin/greek to hungarian nrc */ xlgas, /* 7,9 latin/greek to norge/danish nrc */ xlgas, /* 7,10 latin/greek to portuguese nrc */ xlgas, /* 7,11 latin/greek to spanish nrc */ xlgas, /* 7,12 latin/greek to swedish nrc */ xlgas, /* 7,13 latin/greek to swiss nrc */ xlgas, /* 7,14 latin/greek to latin-1 */ xlgas, /* 7,15 latin/greek to latin-2 */ xlgas, /* 7,16 latin/greek to DEC MCS */ xlgas, /* 7,17 latin/greek to NeXT */ xlgas, /* 7,18 latin/greek to CP437 */ xlgas, /* 7,19 latin/greek to CP850 */ xlgas, /* 7,20 latin/greek to CP852 */ xlgas, /* 7,21 latin/greek to Macintosh Latin */ xlgas, /* 7,22 latin/greek to DGI */ xlgas, /* 7,23 latin/greek to HP */ xleft160, /* 7,24 latin/greek to Latin/Cyrillic */ xleft128, /* 7,25 latin/greek to CP866 */ xassk, /* 7,26 latin/greek to Short KOI */ xleft160, /* 7,27 latin/greek to Old KOI-8 Greek */ NULL, /* 7,28 latin/greek to JIS-7 */ NULL, /* 7,29 latin/greek to Shift-JIS */ NULL, /* 7,30 latin/greek to J-EUC */ NULL, /* 7,31 latin/greek to DEC Kanji */ NULL, /* 7,32 latin/greek to Hebrew-7 */ xlgas, /* 7,33 latin/greek to Latin/Hebrew */ xlgas, /* 7,34 latin/greek to CP862 Hebrew */ xlgeg, /* 7,35 latin/greek to ELOT 927 Greek */ NULL, /* 7,36 latin/greek to Latin/Greek */ xlg69, /* 7,37 latin/greek to CP869 */ xlgas, /* 7,38 latin/greek to Latin-9 */ xlgas, /* 7,39 latin/greek to CP858 */ xleft128, /* 7,40 latin/greek to CP855 */ xleft128, /* 7,41 latin/greek to CP1251 */ xleft128, /* 7,42 latin/greek to Bulgarian */ xleft128, /* 7,43 latin/greek to CP1250 */ xleft128, /* 7,44 latin/greek to Mazovia */ NULL, /* 7,45 latin/greek to UCS-2 */ NULL, /* 7,46 latin/greek to UTF-8 */ NULL, /* 7,47 latin/greek to KOI8R */ NULL, /* 7,48 latin/greek to KOI8U */ xlgw1, /* 7,49 latin/greek to CP1252 */ zl9as, /* 8,0 latin-9 to us ascii */ xl1uk, /* 8,1 latin-9 to uk ascii */ xl1du, /* 8,2 latin-9 to dutch nrc */ xl1fi, /* 8,3 latin-9 to finnish nrc */ xl1fr, /* 8,4 latin-9 to french nrc */ xl1fc, /* 8,5 latin-9 to fr-canadian nrc */ xl1ge, /* 8,6 latin-9 to german nrc */ xl1it, /* 8,7 latin-9 to italian nrc */ xl1hu, /* 8,8 latin-9 to hungarian nrc */ xl1no, /* 8,9 latin-9 to norge/danish nrc */ xl1po, /* 8,10 latin-9 to portuguese nrc */ xl1sp, /* 8,11 latin-9 to spanish nrc */ xl1sw, /* 8,12 latin-9 to swedish nrc */ xl1ch, /* 8,13 latin-9 to swiss nrc */ NULL, /* 8,14 latin-9 to latin-1 */ xl1l2, /* 8,15 latin-9 to latin-2 */ xl9dm, /* 8,16 latin-9 to DEC MCS */ xl9ne, /* 8,17 latin-9 to NeXT */ xl143, /* 8,18 latin-9 to CP437 */ xl185, /* 8,19 latin-9 to CP850 */ xl152, /* 8,20 latin-9 to CP852 */ xl1aq, /* 8,21 latin-9 to Macintosh Latin */ xl1dg, /* 8,22 latin-9 to DGI */ xl1r8, /* 8,23 latin-9 to HP Roman8 */ zl1as, /* 8,24 latin-9 to Latin/Cyrillic */ zl1as, /* 8,25 latin-9 to CP866 */ xl1sk, /* 8,26 latin-9 to Short KOI */ zl1as, /* 8,27 latin-9 to Old KOI-8 Cyrillic */ NULL, /* 8,28 latin-9 to JIS-7 */ NULL, /* 8,29 latin-9 to Shift-JIS */ NULL, /* 8,30 latin-9 to J-EUC */ NULL, /* 8,31 latin-9 to DEC Kanji */ xl1h7, /* 8,32 latin-9 to Hebrew-7 */ xl1lh, /* 8,33 latin-9 to Latin/Hebrew */ xl162, /* 8,34 latin-9 to CP862 Hebrew */ xl1eg, /* 8,35 latin-9 to ELOT 927 Greek */ xl1lg, /* 8,36 latin-9 to Latin/Greek */ xl169, /* 8,37 latin-9 to CP869 */ NULL, /* 8,38 latin-9 to Latin9 */ xl958, /* 8,39 latin-9 to CP858 */ zl1as, /* 8,40 latin-9 to CP855 */ zl1as, /* 8,41 latin-9 to CP1251 */ xl1as, /* 8,42 latin-9 to Bulgarian */ xl91250, /* 8,43 latin-9 to CP1250 */ xl9mz, /* 8,44 latin-9 to Mazovia */ NULL, /* 8,45 latin-9 to UCS-2 */ NULL, /* 8,46 latin-9 to UTF-8 */ zl1as, /* 8,47 latin-9 to KOI8-R */ zl1as, /* 8,48 latin-9 to KOI8-U */ xl9w1, /* 8,49 latin-9 to CP1252 */ NULL, /* 9,00 Unicode... */ NULL, /* 9,01 */ NULL, /* 9,02 */ NULL, /* 9,03 */ NULL, /* 9,04 */ NULL, /* 9,05 */ NULL, /* 9,06 */ NULL, /* 9,07 */ NULL, /* 9,08 */ NULL, /* 9,09 */ NULL, /* 9,10 */ NULL, /* 9,11 */ NULL, /* 9,12 */ NULL, /* 9,13 */ NULL, /* 9,14 */ NULL, /* 9,15 */ NULL, /* 9,16 */ NULL, /* 9,17 */ NULL, /* 9,18 */ NULL, /* 9,19 */ NULL, /* 9,20 */ NULL, /* 9,21 */ NULL, /* 9,22 */ NULL, /* 9,23 */ NULL, /* 9,24 */ NULL, /* 9,25 */ NULL, /* 9,26 */ NULL, /* 9,27 */ NULL, /* 9,28 */ NULL, /* 9,29 */ NULL, /* 9,30 */ NULL, /* 9,31 */ NULL, /* 9,32 */ NULL, /* 9,33 */ NULL, /* 9,34 */ NULL, /* 9,35 */ NULL, /* 9,36 */ NULL, /* 9,37 */ NULL, /* 9,38 */ NULL, /* 9,39 */ NULL, /* 9,40 */ NULL, /* 9,41 */ NULL, /* 9,42 */ NULL, /* 9,43 */ NULL, /* 9,44 */ NULL, /* 9,45 */ NULL, /* 9,46 */ NULL, /* 9,47 */ NULL, /* 9,48 */ NULL, /* 9,49 */ NULL, /* 10,00 */ NULL, /* 10,01 */ NULL, /* 10,02 */ NULL, /* 10,03 */ NULL, /* 10,04 */ NULL, /* 10,05 */ NULL, /* 10,06 */ NULL, /* 10,07 */ NULL, /* 10,08 */ NULL, /* 10,09 */ NULL, /* 10,10 */ NULL, /* 10,11 */ NULL, /* 10,12 */ NULL, /* 10,13 */ NULL, /* 10,14 */ NULL, /* 10,15 */ NULL, /* 10,16 */ NULL, /* 10,17 */ NULL, /* 10,18 */ NULL, /* 10,19 */ NULL, /* 10,20 */ NULL, /* 10,21 */ NULL, /* 10,22 */ NULL, /* 10,23 */ NULL, /* 10,24 */ NULL, /* 10,25 */ NULL, /* 10,26 */ NULL, /* 10,27 */ NULL, /* 10,28 */ NULL, /* 10,29 */ NULL, /* 10,30 */ NULL, /* 10,31 */ NULL, /* 10,32 */ NULL, /* 10,33 */ NULL, /* 10,34 */ NULL, /* 10,35 */ NULL, /* 10,36 */ NULL, /* 10,37 */ NULL, /* 10,38 */ NULL, /* 10,39 */ NULL, /* 10,40 */ NULL, /* 10,41 */ NULL, /* 10,42 */ NULL, /* 10,43 */ NULL, /* 10,44 */ NULL, /* 10,45 */ NULL, /* 10,46 */ NULL, /* 10,47 */ NULL, /* 10,48 */ NULL /* 10,49 */ }; int nxlr = (sizeof(xlr) / sizeof(CHAR *)); /* Translation function table for sending files. Array of pointers to functions for translating from the local file character set to the transfer character set. Indexed in the same way as the xlr array above, but with the indices reversed. */ #ifdef CK_ANSIC CHAR (*xls[MAXTCSETS+1][MAXFCSETS+1])(CHAR) = #else CHAR (*xls[MAXTCSETS+1][MAXFCSETS+1])() = #endif /* CK_ANSIC */ { NULL, /* 0,0 us ascii to transparent */ NULL, /* 0,1 uk ascii to transparent */ NULL, /* 0,2 dutch nrc to transparent */ NULL, /* 0,3 finnish nrc to transparent */ NULL, /* 0,4 french nrc to transparent */ NULL, /* 0,5 fr-canadian nrc to transparent */ NULL, /* 0,6 german nrc to transparent */ NULL, /* 0,7 hungarian nrc to transparent */ NULL, /* 0,8 italian nrc to transparent */ NULL, /* 0,9 norge/danish nrc to transparent */ NULL, /* 0,10 portuguese nrc to transparent */ NULL, /* 0,11 spanish nrc to transparent */ NULL, /* 0,12 swedish nrc to transparent */ NULL, /* 0,13 swiss nrc to transparent */ NULL, /* 0,14 latin-1 to transparent */ NULL, /* 0,15 latin-2 to transparent */ NULL, /* 0,16 DEC MCS to transparent */ NULL, /* 0,17 NeXT to transparent */ NULL, /* 0,18 CP437 to transparent */ NULL, /* 0,19 CP850 to transparent */ NULL, /* 0,20 CP852 to transparent */ NULL, /* 0,21 Macintosh Latin to transparent */ NULL, /* 0,22 DGI to transparent */ NULL, /* 0,23 HP to transparent */ NULL, /* 0,24 Latin/Cyrillic to transparent */ NULL, /* 0,25 CP866 to transparent */ NULL, /* 0,26 Short KOI to transparent */ NULL, /* 0,27 Old KOI-8 to transparent */ NULL, /* 0,28 JIS-7 to transparent */ NULL, /* 0,29 Shift JIS to transparent */ NULL, /* 0,30 Japanese EUC to transparent */ NULL, /* 0,31 DEC Kanji to transparent */ NULL, /* 0,32 Hebrew-7 to transparent */ NULL, /* 0,33 Latin/Hebrew to transparent */ NULL, /* 0,34 CP862 Hebrew to transparent */ NULL, /* 0,35 ELOT 927 Greek to transparent */ NULL, /* 0,36 Latin/Greek to transparent */ NULL, /* 0,37 CP869 to transparent */ NULL, /* 0,38 Latin-9 to transparent */ NULL, /* 0,39 CP858 to transparent */ NULL, /* 0,40 CP855 to transparent */ NULL, /* 0,41 CP1251 to transparent */ NULL, /* 0,42 Bulgarian to transparent */ NULL, /* 0,43 CP1250 to transparent */ NULL, /* 0,44 Mazovia to transparent */ NULL, /* 0,45 UCS-2 to transparent */ NULL, /* 0,46 UTF-8 to transparent */ NULL, /* 0,47 KOI8R to transparent */ NULL, /* 0,48 KOI8U to transparent */ NULL, /* 0,49 CP1252 to transparent */ NULL, /* 1,0 us ascii to ascii */ NULL, /* 1,1 uk ascii to ascii */ xduas, /* 1,2 dutch nrc to ascii */ xfias, /* 1,3 finnish nrc to ascii */ xfras, /* 1,4 french nrc to ascii */ xfcas, /* 1,5 french canadian nrc to ascii */ xgeas, /* 1,6 german nrc to ascii */ xhuas, /* 1,7 hungarian nrc to ascii */ xitas, /* 1,8 italian nrc to ascii */ xnoas, /* 1,9 norwegian/danish nrc to ascii */ xpoas, /* 1,10 portuguese nrc to ascii */ xspas, /* 1,11 spanish nrc to ascii */ xswas, /* 1,12 swedish nrc to ascii */ xchas, /* 1,13 swiss nrc to ascii */ xl1as, /* 1,14 latin-1 to ascii */ xl2as, /* 1,15 latin-2 to ascii */ xdmas, /* 1,16 dec mcs to ascii */ xneas, /* 1,17 NeXT to ascii */ x43as, /* 1,18 CP437 to ascii */ x85as, /* 1,19 CP850 to ascii */ x52as, /* 1,20 CP850 to ascii */ xaqas, /* 1,21 Macintosh Latin to ascii */ xdgas, /* 1,22 DGI to ascii */ xr8as, /* 1,23 HP to ASCII */ xlcas, /* 1,24 Latin/Cyrillic to ASCII */ xacas, /* 1,25 CP866 to ASCII */ xskas, /* 1,26 Short KOI to ASCII */ xk8as, /* 1,27 Old KOI-8 Cyrillic to ASCII */ NULL, /* 1,28 */ NULL, /* 1,29 */ NULL, /* 1,30 */ NULL, /* 1,31 */ xh7as, /* 1,32 Hebrew-7 to ASCII */ xlhas, /* 1,33 Latin/Hebrew to ASCII */ x62as, /* 1,34 CP862 Hebrew to ASCII */ xegas, /* 1,35 ELOT 927 Greek to ASCII */ xlgas, /* 1,36 Latin/Greek to ASCII */ x69as, /* 1,37 CP869 to ASCII */ xl9as, /* 1,38 Latin-9 to ASCII */ x58as, /* 1,39 CP858 to ASCII */ x55as, /* 1,40 CP855 to ASCII */ x1251as, /* 1,41 CP1251 to ASCII */ xleft128, /* 1,42 Bulgarian to ASCII */ x1250as, /* 1,43 CP1250 to ASCII */ xmzas, /* 1,44 Mazovia to ASCII */ NULL, /* 1,45 UCS-2 to ASCII */ NULL, /* 1,46 UTF-8 to ASCII */ xk8as, /* 1,47 KOI8R to ASCII */ xk8as, /* 1,48 KOI8U to ASCII */ xw1as, /* 1,49 CP1252 to ASCII */ NULL, /* 2,0 us ascii to latin-1 */ xukl1, /* 2,1 uk ascii to latin-1 */ xdul1, /* 2,2 dutch nrc to latin-1 */ xfil1, /* 2,3 finnish nrc to latin-1 */ xfrl1, /* 2,4 french nrc to latin-1 */ xfcl1, /* 2,5 french canadian nrc to latin-1 */ xgel1, /* 2,6 german nrc to latin-1 */ xhul1, /* 2,7 hungarian nrc to latin-1 */ xitl1, /* 2,8 italian nrc to latin-1 */ xnol1, /* 2,9 norwegian/danish nrc to latin-1 */ xpol1, /* 2,10 portuguese nrc to latin-1 */ xspl1, /* 2,11 spanish nrc to latin-1 */ xswl1, /* 2,12 swedish nrc to latin-1 */ xchl1, /* 2,13 swiss nrc to latin-1 */ NULL, /* 2,14 latin-1 to latin-1 */ xl2l1, /* 2,15 latin-2 to latin-1 */ xdml1, /* 2,16 dec mcs to latin-1 */ xnel1, /* 2,17 NeXT to Latin-1 */ x43l1, /* 2,18 CP437 to Latin-1 */ x85l1, /* 2,19 CP850 to Latin-1 */ x52l1, /* 2,20 CP852 to Latin-1 */ xaql1, /* 2,21 Macintosh Latin to Latin-1 */ xdgl1, /* 2,22 DGI to Latin-1 */ xr8l1, /* 2,23 HP to Latin-1 */ xlcas, /* 2,24 Latin/Cyrillic to Latin-1 */ xacas, /* 2,25 CP866 to Latin-1 */ xskas, /* 2,26 Short KOI to Latin-1 */ xk8as, /* 2,27 Old KOI-8 Cyrillic to Latin-1 */ NULL, /* 2,28 Kanji ... */ NULL, /* 2,29 */ NULL, /* 2,30 */ NULL, /* 2,31 */ xh7as, /* 2,32 Hebrew-7 to Latin-1 */ xlhl1, /* 2,33 Latin/Hebrew to Latin-1 */ x62l1, /* 2,34 CP862 Hebrew to Latin-1 */ xegas, /* 2,35 ELOT 927 Greek to Latin-1 */ xlgl1, /* 2,36 Latin/Greek to Latin-1 */ NULL, /* 2,37 CP869 to Latin-1 */ NULL, /* 2,38 Latin-9 to Latin-1 */ x58l1, /* 2,39 CP858 to Latin-1 */ x55as, /* 2,40 CP855 to Latin-1 */ x1251as, /* 2,41 CP1251 to Latin-1 */ xleft128, /* 2,42 Bulgarian to Latin-1 */ x1250l1, /* 2,43 CP1250 to Latin-1 */ xmzl1, /* 2,44 Mazovia to Latin-1 */ NULL, /* 2,45 UCS-2 to Latin-1 */ NULL, /* 2,46 UTF-8 to Latin-1 */ xk8as, /* 2,47 KOI8R to Latin-1 */ xk8as, /* 2,48 KOI8U to Latin-1 */ xw1l1, /* 2,49 CP1252 to Latin-1 */ NULL, /* 3,0 us ascii to latin-2 */ NULL, /* 3,1 uk ascii to latin-2 */ xduas, /* 3,2 dutch nrc to latin-2 */ xfias, /* 3,3 finnish nrc to latin-2 */ xfras, /* 3,4 french nrc to latin-2 */ xfcas, /* 3,5 french canadian nrc to latin-2 */ xgel2, /* 3,6 german nrc to latin-2 */ xhul2, /* 3,7 hungarian nrc to latin-2 */ xitas, /* 3,8 italian nrc to latin-2 */ xnoas, /* 3,9 norwegian/danish nrc to latin-2 */ xpoas, /* 3,10 portuguese nrc to latin-2 */ xspas, /* 3,11 spanish nrc to latin-2 */ xswas, /* 3,12 swedish nrc to latin-2 */ xchas, /* 3,13 swiss nrc to latin-2 */ xl1l2, /* 3,14 latin-1 to latin-2 */ NULL, /* 3,15 latin-2 to latin-2 */ xl1l2, /* 3,16 dec mcs to latin-2 */ xnel2, /* 3,17 NeXT to Latin-2 */ x43l2, /* 3,18 CP437 to Latin-2 */ x85l2, /* 3,19 CP850 to Latin-2 */ x52l2, /* 3,20 CP852 to Latin-2 */ xaql2, /* 3,21 Macintosh Latin to Latin-2 */ xdgl2, /* 3,22 DGI to Latin-2 */ xr8l2, /* 3,23 HP to Latin-2 */ xlcas, /* 3,24 Latin/Cyrillic to Latin-2 */ xacas, /* 3,25 CP866 to Latin-2 */ xskas, /* 3,26 Short KOI to Latin-2 */ xk8as, /* 3,27 Old KOI-8 Cyrillic to Latin-2 */ NULL, /* 3,28 Kanji ... */ NULL, /* 3,29 */ NULL, /* 3,30 */ NULL, /* 3,31 */ xh7as, /* 3,32 Hebrew-7 to Latin-2 */ xlhas, /* 3,33 Latin/Hebrew to Latin-2 */ x62as, /* 3,34 CP862 Hebrew to Latin-2 */ xegas, /* 3,35 ELOT 927 Greek to Latin-2 */ xl2lg, /* 3,36 Latin/Greek to Latin-2 */ xleft128, /* 3,37 CP869 to Latin-2 */ xl9l2, /* 3,38 Latin-9 to Latin-2 */ x58l2, /* 3,39 CP858 to Latin-2 */ x55as, /* 3,40 CP855 to Latin-2 */ x1251as, /* 3,41 CP1251 to Latin-2 */ xleft128, /* 3,42 Bulgarian to Latin-2 */ x1250l2, /* 3,43 CP1250 to Latin-2 */ xmzl2, /* 3,44 Mazovia to Latin-2 */ NULL, /* 3,45 UCS-2 to Latin-2 */ NULL, /* 3,46 UTF-8 to Latin-2 */ xk8as, /* 3,47 KOI8R to Latin-2 */ xk8as, /* 3,48 KOI8U to Latin-2 */ xw1l2, /* 3,49 CP1252 to latin-2 */ xaslc, /* 4,0 us ascii to latin/cyrillic */ xaslc, /* 4,1 uk ascii to latin/cyrillic */ xduas, /* 4,2 dutch nrc to latin/cyrillic */ xfias, /* 4,3 finnish nrc to latin/cyrillic */ xfras, /* 4,4 french nrc to latin/cyrillic */ xfcas, /* 4,5 french canadian nrc to latin/cyrillic */ xgeas, /* 4,6 german nrc to latin/cyrillic */ xhuas, /* 4,7 hungarian nrc to latin/cyrillic */ xitas, /* 4,8 italian nrc to latin/cyrillic */ xnoas, /* 4,9 norge/danish nrc to latin/cyrillic */ xpoas, /* 4,10 portuguese nrc to latin/cyrillic */ xspas, /* 4,11 spanish nrc to latin/cyrillic */ xswas, /* 4,12 swedish nrc to latin/cyrillic */ xchas, /* 4,13 swiss nrc to latin/cyrillic */ xl1as, /* 4,14 latin-1 to latin/cyrillic */ xl2as, /* 4,15 latin-2 to latin/cyrillic */ xdmas, /* 4,16 dec mcs to latin/cyrillic */ xneas, /* 4,17 NeXT to latin/cyrillic */ x43as, /* 4,18 CP437 to latin/cyrillic */ x85as, /* 4,19 CP850 to latin/cyrillic */ x52as, /* 4,20 CP852 to latin/cyrillic */ xaqas, /* 4,21 Macintosh Latin to latin/cyrillic */ xdgas, /* 4,22 DGI to Latin/Cyrillic */ xr8as, /* 4,23 HP to Latin/Cyrillic */ NULL, /* 4,24 Latin/Cyrillic to Latin/Cyrillic */ xaclc, /* 4,25 CP866 to Latin/Cyrillic */ xskcy, /* 4,26 Short KOI to Latin/Cyrillic */ xk8lc, /* 4,27 Old KOI-8 Cyrillic to Latin/Cyrillic */ NULL, /* 4,28 Kanji... */ NULL, /* 4,29 */ NULL, /* 4,30 */ NULL, /* 4,31 */ xh7as, /* 4,32 Hebrew-7 to Latin/Cyrillic */ xlhas, /* 4,33 Latin/Hebrew to Latin/Cyrillic */ x62as, /* 4,34 CP862 Hebrew to Latin/Cyrillic */ xegas, /* 4,35 ELOT 927 Greek to Latin/Cyrillic */ xleft160, /* 4,36 Latin/Greek to Latin/Cyrillic */ xleft128, /* 4,37 CP869 to Latin/Cyrillic */ xl1as, /* 4,38 latin-9 to latin/cyrillic */ xleft128, /* 4,39 CP858 to Latin/Cyrillic */ x55lc, /* 4,40 CP855 to Latin/Cyrillic */ x1251lc, /* 4,41 CP1251 to Latin/Cyrillic */ xbulc, /* 4,42 Bulgarian to Latin/Cyrillic */ x1250as, /* 4,43 CP1250 to Latin/Cyrillic */ xmzas, /* 4,44 Mazovia to Latin/Cyrillic */ NULL, /* 4,45 UCS-2 to Latin/Cyrillic */ NULL, /* 4,46 UTF-8 to Latin/Cyrillic */ xkrlc, /* 4,47 KOI8R to Latin/Cyrillic */ xkulc, /* 4,48 KOI8U to Latin/Cyrillic */ xw1lc, /* 4,49 CP1252 to Latin/Cyrillic */ NULL, /* 5,00 */ NULL, /* 5,01 */ NULL, /* 5,02 */ NULL, /* 5,03 */ NULL, /* 5,04 */ NULL, /* 5,05 */ NULL, /* 5,06 */ NULL, /* 4.07 */ NULL, /* 5,08 */ NULL, /* 5,09 */ NULL, /* 5,10 */ NULL, /* 5,11 */ NULL, /* 5,12 */ NULL, /* 5,13 */ NULL, /* 5,14 */ NULL, /* 5,15 */ NULL, /* 5,16 */ NULL, /* 5,17 */ NULL, /* 5,18 */ NULL, /* 5,19 */ NULL, /* 5,20 */ NULL, /* 5,21 */ NULL, /* 5,22 */ NULL, /* 5,23 */ NULL, /* 5,24 */ NULL, /* 5,25 */ NULL, /* 5,26 */ NULL, /* 5,27 */ NULL, /* 5,28 */ NULL, /* 5,29 */ NULL, /* 5,30 */ NULL, /* 5,31 */ NULL, /* 5,32 */ NULL, /* 5,33 */ NULL, /* 5,34 */ NULL, /* 5,35 */ NULL, /* 5,36 */ NULL, /* 5,37 */ NULL, /* 5,38 */ NULL, /* 5,39 */ NULL, /* 5,40 */ NULL, /* 5,41 */ NULL, /* 5,42 */ NULL, /* 5,43 */ NULL, /* 5,44 */ NULL, /* 5,45 */ NULL, /* 5,46 */ NULL, /* 5,47 */ NULL, /* 5,48 */ NULL, /* 5,49 */ NULL, /* 6,0 us ascii to Latin/Hebrew */ NULL, /* 6,1 uk ascii to Latin/Hebrew */ xduas, /* 6,2 dutch nrc to Latin/Hebrew */ xfias, /* 6,3 finnish nrc to Latin/Hebrew */ xfras, /* 6,4 french nrc to Latin/Hebrew */ xfcas, /* 6,5 french canadian nrc to Latin/Hebrew */ xgeas, /* 6,6 german nrc to Latin/Hebrew */ xhuas, /* 6,7 hungarian nrc to Latin/Hebrew */ xitas, /* 6,8 italian nrc to Latin/Hebrew */ xnoas, /* 6,9 norge/danish nrc to Latin/Hebrew */ xpoas, /* 6,10 portuguese nrc to Latin/Hebrew */ xspas, /* 6,11 spanish nrc to Latin/Hebrew */ xswas, /* 6,12 swedish nrc to Latin/Hebrew */ xchas, /* 6,13 swiss nrc to Latin/Hebrew */ xl1lh, /* 6,14 latin-1 to Latin/Hebrew */ xl2as, /* 6,15 latin-2 to Latin/Hebrew */ xdmas, /* 6,16 dec mcs to Latin/Hebrew */ xneas, /* 6,17 NeXT to Latin/Hebrew */ x43as, /* 6,18 CP437 to Latin/Hebrew */ x85as, /* 6,19 CP850 to Latin/Hebrew */ x52as, /* 6,20 CP852 to Latin/Hebrew */ xaqas, /* 6,21 Macintosh Latin to Latin/Hebrew */ xdgas, /* 6,22 DGI to Latin/Hebrew */ xr8as, /* 6,23 HP to Latin/Hebrew */ xlcas, /* 6,24 Latin/Cyrillic to Latin/Hebrew */ xacas, /* 6,25 CP866 to Latin/Hebrew */ xskas, /* 6,26 Short KOI to Latin/Hebrew */ xk8as, /* 6,27 Old KOI-8 Cyrillic to Latin/Hebrew */ NULL, /* 6,28 Kanji... */ NULL, /* 6,29 */ NULL, /* 6,30 */ NULL, /* 6,31 */ xh7lh, /* 6,32 Hebrew-7 to Latin/Hebrew */ NULL, /* 6,33 Latin/Hebrew to Latin/Hebrew */ x62lh, /* 6,34 CP862 Hebrew to Latin/Hebrew */ xegas, /* 6,35 ELOT 927 Greek to Latin/Hebrew */ xlgas, /* 6,36 Latin/Greek to Latin/Hebrew */ x69as, /* 6,37 CP869 to Latin/Hebrew */ xl1as, /* 6,38 latin-9 to Latin/Hebrew */ x58as, /* 6,39 CP858 to Latin/Hebrew */ x55as, /* 6,40 CP855 to Latin/Hebrew */ x1251as, /* 6,41 CP1251 to Latin/Hebrew */ xleft128, /* 6,42 Bulgarian to Latin/Hebrew */ x1250as, /* 6,43 CP1250 to Latin/Hebrew */ xmzas, /* 6,44 Mazovia to Latin/Hebrew */ NULL, /* 6,45 UCS-2 to Latin/Hebrew */ NULL, /* 6,46 UTF-8 to Latin/Hebrew */ NULL, /* 6,47 KOI8R to Latin/Hebrew */ NULL, /* 6,48 KOI8U to Latin/Hebrew */ xw1lh, /* 6,49 CP1252 to Latin/Hebrew */ NULL, /* 7,0 us ascii to Latin/Greek */ NULL, /* 7,1 uk ascii to Latin/Greek */ xduas, /* 7,2 dutch nrc to Latin/Greek */ xfias, /* 7,3 finnish nrc to Latin/Greek */ xfras, /* 7,4 french nrc to Latin/Greek */ xfcas, /* 7,5 french canadian nrc to Latin/Greek */ xgeas, /* 7,6 german nrc to Latin/Greek */ xhuas, /* 7,7 hungarian nrc to Latin/Greek */ xitas, /* 7,8 italian nrc to Latin/Greek */ xnoas, /* 7,9 norge/danish nrc to Latin/Greek */ xpoas, /* 7,10 portuguese nrc to Latin/Greek */ xspas, /* 7,11 spanish nrc to Latin/Greek */ xswas, /* 7,12 swedish nrc to Latin/Greek */ xchas, /* 7,13 swiss nrc to Latin/Greek */ xl1lg, /* 7,14 latin-1 to Latin/Greek */ xl2lg, /* 7,15 latin-2 to Latin/Greek */ xl1lg, /* 7,16 dec mcs to Latin/Greek */ xneas, /* 7,17 NeXT to Latin/Greek */ xleft128, /* 7,18 CP437 to Latin/Greek */ x85as, /* 7,19 CP850 to Latin/Greek */ x52as, /* 7,20 CP852 to Latin/Greek */ xaqas, /* 7,21 Macintosh Latin to Latin/Greek */ xdgas, /* 7,22 DGI to Latin/Greek */ xr8as, /* 7,23 HP to Latin/Greek */ xleft160, /* 7,24 Latin/Cyrillic to Latin/Greek */ xleft128, /* 7,25 CP866 to Latin/Greek */ xskas, /* 7,26 Short KOI to Latin/Greek */ xk8as, /* 7,27 Old KOI-8 Cyrillic to Latin/Greek */ NULL, /* 7,28 Kanji... */ NULL, /* 7,29 */ NULL, /* 7,30 */ NULL, /* 7,31 */ xh7as, /* 7,32 Hebrew-7 to Latin/Greek */ NULL, /* 7,33 Latin/Hebrew to Latin/Greek */ x62as, /* 7,34 CP862 Hebrew to Latin/Greek */ xeglg, /* 7,35 ELOT 927 Greek to Latin/Greek */ NULL, /* 7,36 Latin/Greek to Latin/Greek */ x69lg, /* 7,37 CP869 to Latin/Greek */ xl1as, /* 7,38 latin-9 to Latin/Greek */ xl1as, /* 7,39 latin-9 to Latin/Hebrew*/ xleft128, /* 7,40 CP855 to Latin/Greek */ xleft128, /* 7,41 CP1251 to Latin/Greek */ xleft128, /* 7,42 Bulgarian to Latin/Greek */ x1250as, /* 7,43 CP1250 to Latin/Greek */ xmzas, /* 7,44 Mazovia to Latin/Greek */ NULL, /* 7,45 UCS-2 to Latin/Greek */ NULL, /* 7,46 UTF-8 to Latin/Greek */ NULL, /* 7,47 KOI8R to Latin/Greek */ NULL, /* 7,48 KOI8U to Latin/Greek */ xw1lg, /* 7,49 CP1252 to Latin/Greek */ NULL, /* 8,0 us ascii to latin-9 */ xukl1, /* 8,1 uk ascii to latin-9 */ xdul1, /* 8,2 dutch nrc to latin-9 */ xfil1, /* 8,3 finnish nrc to latin-9 */ xfrl1, /* 8,4 french nrc to latin-9 */ xfcl1, /* 8,5 french canadian nrc to latin-9 */ xgel1, /* 8,6 german nrc to latin-9 */ xhul1, /* 8,7 hungarian nrc to latin-9 */ xitl1, /* 8,8 italian nrc to latin-9 */ xnol1, /* 8,9 norwegian/danish nrc to latin-9 */ xpol1, /* 8,10 portuguese nrc to latin-9 */ xspl1, /* 8,11 spanish nrc to latin-9 */ xswl1, /* 8,12 swedish nrc to latin-9 */ xchl1, /* 8,13 swiss nrc to latin-9 */ NULL, /* 8,14 latin-1 to latin-9 */ xl2l9, /* 8,15 latin-2 to latin-9 */ xdml9, /* 8,16 dec mcs to latin-9 */ xnel9, /* 8,17 NeXT To Latin-9 */ x43l1, /* 8,18 CP437 To Latin-9 */ x85l1, /* 8,19 CP850 To Latin-9 */ x52l1, /* 8,20 CP852 To Latin-9 */ xaql1, /* 8,21 Macintosh Latin To Latin-9 */ xdgl1, /* 8,22 DGI To Latin-9 */ xr8l1, /* 8,23 HP To Latin-9 */ xlcas, /* 8,24 Latin/Cyrillic To Latin-9 */ xacas, /* 8,25 CP866 To Latin-9 */ xskas, /* 8,26 Short KOI To Latin-9 */ xk8as, /* 8,27 Old KOI-8 Cyrillic To Latin-9 */ NULL, /* 8,28 Kanji ... */ NULL, /* 8,29 */ NULL, /* 8,30 */ NULL, /* 8,31 */ xh7as, /* 8,32 Hebrew-7 To Latin-9 */ xlhl1, /* 8,33 Latin/Hebrew To Latin-9 */ x62l1, /* 8,34 CP862 Hebrew To Latin-9 */ xegas, /* 8,35 ELOT 927 Greek To Latin-9 */ xlgl1, /* 8,36 Latin/Greek To Latin-9 */ xl169, /* 8,37 CP869 To Latin-9 */ NULL, /* 8,38 Latin-9 To Latin-9 */ x58l9, /* 8,39 cp858 To Latin-9 */ x55as, /* 8,40 cp855 To Latin-9 */ x55as, /* 8,41 cp1251 To Latin-9 */ xleft128, /* 8,42 Bulgarian To Latin-9 */ x1250l9, /* 8,43 CP1250 To Latin-9 */ xmzl9, /* 8,44 Mazovia To Latin-9 */ NULL, /* 8,45 UCS-2 to Latin-9 */ NULL, /* 8,46 UTF-8 to Latin-9 */ NULL, /* 8,47 KOI8R to Latin-9 */ NULL, /* 8,48 KOI8U to Latin-9 */ xw1l9, /* 8,49 CP1252 to Latin-9 */ NULL, /* 9,00 Unicode... */ NULL, /* 9,01 */ NULL, /* 9,02 */ NULL, /* 9,03 */ NULL, /* 9,04 */ NULL, /* 9,05 */ NULL, /* 9,06 */ NULL, /* 9,07 */ NULL, /* 9,08 */ NULL, /* 9,09 */ NULL, /* 9,10 */ NULL, /* 9,11 */ NULL, /* 9,12 */ NULL, /* 9,13 */ NULL, /* 9,14 */ NULL, /* 9,15 */ NULL, /* 9,16 */ NULL, /* 9,17 */ NULL, /* 9,18 */ NULL, /* 9,19 */ NULL, /* 9,20 */ NULL, /* 9,21 */ NULL, /* 9,22 */ NULL, /* 9,23 */ NULL, /* 9,24 */ NULL, /* 9,25 */ NULL, /* 9,26 */ NULL, /* 9,27 */ NULL, /* 9,28 */ NULL, /* 9,29 */ NULL, /* 9,30 */ NULL, /* 9,31 */ NULL, /* 9,32 */ NULL, /* 9,33 */ NULL, /* 9,34 */ NULL, /* 9,35 */ NULL, /* 9,36 */ NULL, /* 9,37 */ NULL, /* 9,38 */ NULL, /* 9,39 */ NULL, /* 9,40 */ NULL, /* 9,41 */ NULL, /* 9,42 */ NULL, /* 9,43 */ NULL, /* 9,44 */ NULL, /* 9,45 */ NULL, /* 9,46 */ NULL, /* 9,47 */ NULL, /* 9,48 */ NULL, /* 9,49 */ NULL, /* 10,00 */ NULL, /* 10,01 */ NULL, /* 10,02 */ NULL, /* 10,03 */ NULL, /* 10,04 */ NULL, /* 10,05 */ NULL, /* 10,06 */ NULL, /* 10,07 */ NULL, /* 10,08 */ NULL, /* 10,09 */ NULL, /* 10,10 */ NULL, /* 10,11 */ NULL, /* 10,12 */ NULL, /* 10,13 */ NULL, /* 10,14 */ NULL, /* 10,15 */ NULL, /* 10,16 */ NULL, /* 10,17 */ NULL, /* 10,18 */ NULL, /* 10,19 */ NULL, /* 10,20 */ NULL, /* 10,21 */ NULL, /* 10,22 */ NULL, /* 10,23 */ NULL, /* 10,24 */ NULL, /* 10,25 */ NULL, /* 10,26 */ NULL, /* 10,27 */ NULL, /* 10,28 */ NULL, /* 10,29 */ NULL, /* 10,30 */ NULL, /* 10,31 */ NULL, /* 10,32 */ NULL, /* 10,33 */ NULL, /* 10,34 */ NULL, /* 10,35 */ NULL, /* 10,36 */ NULL, /* 10,37 */ NULL, /* 10,38 */ NULL, /* 10,39 */ NULL, /* 10,40 */ NULL, /* 10,41 */ NULL, /* 10,42 */ NULL, /* 10,43 */ NULL, /* 10,44 */ NULL, /* 10,45 */ NULL, /* 10,46 */ NULL, /* 10,47 */ NULL, /* 10,48 */ NULL /* 10,49 */ }; int nxls = (sizeof(xls) / sizeof(CHAR *)); #ifndef NOLOCAL /* The following routines are useful only for terminal character sets, and so ifdef'd out for NOLOCAL compilations. */ /* C S _ I S _ N R C Returns nonzero if argument indicates a 7-bit national character set, zero otherwise. */ int cs_is_nrc(x) int x; { #ifdef UNICODE if (x == TX_J201R || x == TX_DECSPEC || x == TX_DECTECH || txrinfo[x] == NULL) return(0); else return(txrinfo[x]->flags & X2U_STD && txrinfo[x]->size == 94); #else /* UNICODE */ if ((cs_size(x) == 94)) return(1); else return(0); #endif /* UNICODE */ } /* C S _ I S _ S T D Returns nonzero if argument indicates an ISO 4873-standard-format character set, i.e. one in which the control region is NOT used for graphics; zero otherwise. */ int cs_is_std(x) int x; { #ifdef UNICODE if (!txrinfo[x]) /* Even more safety */ return(0); else if (txrinfo[x]->size == 128) /* Just for safety */ return(0); else return(txrinfo[x]->flags & X2U_STD); /* Only this should be needed */ #else switch (x) { case FC_CP437: /* Code pages use C1 graphics */ case FC_CP850: case FC_CP852: case FC_CP862: case FC_CP866: case FC_CP869: case FC_CP858: case FC_APPQD: /* So do Apple and NeXTSTEP */ case FC_NEXT: return(0); default: /* Others behave */ return(1); } #endif /* CKOUINI */ } int cs_size(x) int x; { #ifdef UNICODE if (!txrinfo[x]) return(128); return(txrinfo[x]->size); #else switch(x) { case FC_USASCII: case FC_UKASCII: case FC_DUASCII: case FC_FIASCII: case FC_FRASCII: case FC_FCASCII: case FC_GEASCII: case FC_HUASCII: case FC_ITASCII: case FC_NOASCII: case FC_POASCII: case FC_SPASCII: case FC_SWASCII: case FC_CHASCII: case FC_KOI7: case FC_HE7: case FC_ELOT: return(94); case FC_1LATIN: case FC_2LATIN: case FC_DECMCS: case FC_DGMCS: case FC_HPR8: case FC_CYRILL: case FC_KOI8: case FC_HEBREW: case FC_GREEK: case FC_9LATIN: return(96); case FC_NEXT: case FC_CP437: case FC_CP850: case FC_CP852: case FC_CP855: case FC_CP862: case FC_CP866: case FC_CP1251: case FC_APPQD: return(128); #ifdef KANJI case FC_JIS7: return(-94); case FC_SHJIS: return(-128); case FC_JEUC: case FC_JDEC: return(-96); #endif /* KANJI */ case FC_CP858: default: return(-1); } #endif /* UNICODE */ } #endif /* NOLOCAL */ /* S E T X L A T Y P E -- Set Translation Type Sets global xlatype to indicate which kind of translation: XLA_NONE No translation XLA_BYTE Byte-for-Byte translation XLA_JAPAN Japanese Kanji translation XLA_UNICODE Unicode translations And sets up the appropriate translation function pointers as follows: For no translation: All function pointers are NULL. For Byte-for-Byte transation: rx = TCS to FCS (these functions are in this module...) sx = FCS to TCS For Unicode translations: xfu = FCS to UCS (these functions are in ckcuni.c...) xtu = TCS to UCS xuf = UCS to FCS xut = UCS to TCS */ VOID setxlatype(tcs, fcs) int tcs, fcs; { #ifdef UNICODE xfu = NULL; /* Unicode <-> TCS/FCS functions */ xtu = NULL; xuf = NULL; xut = NULL; #endif /* UNICODE */ rx = sx = NULL; debug(F101,"setxlatype fcs","",fcs); debug(F101,"setxlatype tcs","",tcs); if (tcs < 0 || tcs > MAXTCSETS) { debug(F101,"setxlatype bad tcs","",tcs); return; } if (fcs < 0 || fcs > MAXFCSETS) { debug(F101,"setxlatype bad fcs","",fcs); return; } if (tcs == TC_TRANSP || xfrxla == 0) { /* Transfer charset TRANSPARENT */ debug(F101,"setxlatype transparent because TCS==Transparent","",tcs); xlatype = XLA_NONE; /* Translation type is None */ #ifdef UNICODE /* If any of our charsets is Unicode we use Unicode functions */ /* even if TCS and FCS are the same because of BOM and byte swapping */ } else if (tcs == TC_UCS2 || tcs == TC_UTF8 || fcs == FC_UCS2 || fcs == FC_UTF8) { debug(F101,"setxlatype Unicode tcs","",tcs); debug(F101,"setxlatype Unicode fcs","",fcs); /* Unicode <-> TCS/FCS functions */ xfu = xl_fcu[fcs]; /* FCS -> UCS */ xtu = xl_tcu[tcs]; /* TCS -> UCS */ xuf = xl_ufc[fcs]; /* UCS -> FCS */ xut = xl_utc[tcs]; /* UCS -> TCS */ xlatype = XLA_UNICODE; /* Translation type is Unicode */ #ifdef COMMENT /* These make trouble in 64-bit land */ debug(F001,"setxlatype Unicode xfu","",(unsigned)xfu); debug(F001,"setxlatype Unicode xuf","",(unsigned)xuf); #endif /* COMMENT */ #endif /* UNICODE */ } else if (cseqtab[tcs] == fcs) { /* Or if TCS == FCS */ debug(F101,"setxlatype transparent because TCS==FCS","",tcs); xlatype = XLA_NONE; /* translation type is also None */ #ifdef KANJI /* Otherwise if any of them is Japanese, we use Kanji functions */ } else if (tcs == TC_JEUC || fcsinfo[fcs].alphabet == AL_JAPAN) { debug(F101,"setxlatype Japanese tcs","",tcs); debug(F101,"setxlatype Japanese fcs","",fcs); xlatype = XLA_JAPAN; /* Translation type is Japanese */ #endif /* KANJI */ /* Otherwise we use byte functions */ } else { /* Otherwise... */ rx = xlr[tcs][fcs]; /* Input translation function */ sx = xls[tcs][fcs]; /* Output translation function */ debug(F101,"setxlatype Byte tcs","",tcs); debug(F101,"setxlatype Byte fcs","",fcs); xlatype = XLA_BYTE; /* Translation type is Byte */ } debug(F101,"setxlatype xlatype","",xlatype); } /* Set up translation between two file character sets with UCS intermediate */ #ifdef UNICODE VOID initxlate(csin, csout) int csin, csout; { xfu = NULL; xtu = NULL; xuf = NULL; xut = NULL; debug(F101,"initxlate csin","",csin); debug(F101,"initxlate csout","",csout); if (csin < 0 || csin > MAXFCSETS) { debug(F101,"initxlate bad csin","",csin); return; } if (csout < 0 || csout > MAXFCSETS) { debug(F101,"initxlate bad csout","",csout); return; } if (csin == csout && csin != FC_UCS2) { xlatype = XLA_NONE; /* Translation type is None */ return; } xlatype = XLA_UNICODE; /* Translation type is Unicode */ xfu = xl_fcu[csin]; /* FCS -> UCS */ xuf = xl_ufc[csout]; /* UCS -> FCS */ xpnbyte(-1,0,0,NULL); /* Reset UCS-2 */ #ifdef COMMENT debug(F001,"initxlate Unicode xfu","",(unsigned)xfu); debug(F001,"initxlate Unicode xuf","",(unsigned)xuf); #endif /* COMMENT */ debug(F101,"initxlate xlatype","",xlatype); } #endif /* UNICODE */ int csetsinited = 0; VOID initcsets() { /* Routine to reset or initialize */ int i; /* character-set associations. */ #ifdef UNICODE if (ucsorder < 0) /* For creating UCS-2 files. */ ucsorder = byteorder; if (ucsorder < 0) ucsorder = 0; if (fileorder < 0) /* For reading UCS-2 files. */ fileorder = ucsorder; #endif /* UNICODE */ debug(F101,"initcsets nxls","",nxls); debug(F101,"initcsets nxlr","",nxlr); debug(F101,"initcsets TERM LOCAL CSET","",tcsl); debug(F101,"initcsets TERM REMOTE CSET","",tcsr); for (i = 0; i <= MAXFCSETS; i++) /* First clear them all... */ afcset[i] = -1; for (i = 0; i <= MAXTCSETS; i++) axcset[i] = -1; /* Now set specific defaults for incoming files */ xlatype = XLA_NONE; axcset[TC_TRANSP] = FC_TRANSP; axcset[TC_USASCII] = FC_USASCII; #ifdef OS2 switch (fcharset) { case FC_CP850: case FC_CP858: case FC_CP437: case FC_1LATIN: axcset[TC_1LATIN] = fcharset; break; default: axcset[TC_1LATIN] = FC_CP850; } #else #ifdef HPUX axcset[TC_1LATIN] = FC_HPR8; #else #ifdef VMS axcset[TC_1LATIN] = FC_DECMCS; #else #ifdef NEXT axcset[TC_1LATIN] = FC_NEXT; #else #ifdef datageneral axcset[TC_1LATIN] = FC_DGMCS; #else /* Should we use code pages on some PC based UNIXes? */ axcset[TC_1LATIN] = FC_1LATIN; #endif /* datageneral */ #endif /* NEXT */ #endif /* VMS */ #endif /* HPUX */ #endif /* OS2 */ #ifdef OS2 axcset[TC_2LATIN] = FC_CP852; axcset[TC_CYRILL] = FC_CP866; axcset[TC_JEUC] = FC_SHJIS; axcset[TC_HEBREW] = FC_CP862; axcset[TC_GREEK] = FC_CP869; axcset[TC_9LATIN] = FC_CP858; axcset[TC_UCS2] = FC_UCS2; axcset[TC_UTF8] = FC_UCS2; #else axcset[TC_2LATIN] = FC_2LATIN; axcset[TC_CYRILL] = FC_CYRILL; axcset[TC_JEUC] = FC_JEUC; axcset[TC_HEBREW] = FC_HEBREW; axcset[TC_GREEK] = FC_GREEK; axcset[TC_9LATIN] = FC_9LATIN; axcset[TC_UCS2] = FC_UTF8; axcset[TC_UTF8] = FC_UTF8; #endif /* OS2 */ /* And for outbound files */ afcset[FC_USASCII] = TC_USASCII; afcset[FC_UKASCII] = TC_1LATIN; afcset[FC_DUASCII] = TC_1LATIN; afcset[FC_FIASCII] = TC_1LATIN; afcset[FC_FRASCII] = TC_1LATIN; afcset[FC_FCASCII] = TC_1LATIN; afcset[FC_GEASCII] = TC_1LATIN; afcset[FC_HUASCII] = TC_2LATIN; afcset[FC_ITASCII] = TC_1LATIN; afcset[FC_NOASCII] = TC_1LATIN; afcset[FC_POASCII] = TC_1LATIN; afcset[FC_SPASCII] = TC_1LATIN; afcset[FC_SWASCII] = TC_1LATIN; afcset[FC_CHASCII] = TC_1LATIN; afcset[FC_1LATIN] = TC_1LATIN; afcset[FC_2LATIN] = TC_2LATIN; afcset[FC_DECMCS] = TC_1LATIN; afcset[FC_NEXT] = TC_1LATIN; afcset[FC_CP437] = TC_1LATIN; afcset[FC_CP850] = TC_1LATIN; afcset[FC_CP852] = TC_2LATIN; afcset[FC_APPQD] = TC_1LATIN; afcset[FC_DGMCS] = TC_1LATIN; afcset[FC_HPR8] = TC_1LATIN; afcset[FC_CYRILL] = TC_CYRILL; afcset[FC_CP866] = TC_CYRILL; afcset[FC_KOI7] = TC_CYRILL; afcset[FC_KOI8] = TC_CYRILL; afcset[FC_JIS7] = TC_JEUC; afcset[FC_SHJIS] = TC_JEUC; afcset[FC_JEUC] = TC_JEUC; afcset[FC_JDEC] = TC_JEUC; afcset[FC_HE7] = TC_HEBREW; afcset[FC_HEBREW] = TC_HEBREW; afcset[FC_CP862] = TC_HEBREW; afcset[FC_ELOT] = TC_GREEK; afcset[FC_GREEK] = TC_GREEK; afcset[FC_CP869] = TC_GREEK; afcset[FC_9LATIN] = TC_9LATIN; afcset[FC_CP923] = TC_9LATIN; afcset[FC_CP858] = TC_9LATIN; afcset[FC_CP855] = TC_CYRILL; afcset[FC_CP1251] = TC_CYRILL; afcset[FC_BULGAR] = TC_CYRILL; afcset[FC_CP1250] = TC_2LATIN; afcset[FC_MAZOVIA] = TC_2LATIN; afcset[FC_UCS2] = TC_UTF8; afcset[FC_UTF8] = TC_UTF8; afcset[FC_KOI8R] = TC_CYRILL; afcset[FC_KOI8U] = TC_CYRILL; afcset[FC_CP1252] = TC_1LATIN; csetsinited++; return; } #endif /* NOCSETS */ ckuxla.h0000644000015300001460000000742511266111514011327 0ustar fdckermit/* File CKUXLA.H C-Kermit language and character-set support for UNIX, VMS, OS/2, AOS/VS, and other systems. This file should be used as a template for the language support files for other C-Kermit implementations -- Macintosh, etc. */ /* Author: Frank da Cruz , Columbia University Academic Information Systems, New York City. Copyright (C) 1985, 2009, Trustees of Columbia University in the City of New York. All rights reserved. See the C-Kermit COPYING.TXT file or the copyright text in the ckcmai.c module for disclaimer and permissions. */ #ifndef CKUXLA_H #define CKUXLA_H /* Codes for file character sets */ /* ISO 646 and other ISO-646-like 7-bit sets */ #define FC_USASCII 0 /* US ASCII */ #define FC_UKASCII 1 /* United Kingdom ASCII */ #define FC_DUASCII 2 /* Dutch ISO 646 NRC */ #define FC_FIASCII 3 /* Finnish ISO 646 NRC */ #define FC_FRASCII 4 /* French ISO 646 NRC */ #define FC_FCASCII 5 /* French Canadian ISO 646 NRC */ #define FC_GEASCII 6 /* German ISO 646 NRC */ #define FC_HUASCII 7 /* Hungarian ISO 646 NRC */ #define FC_ITASCII 8 /* Italian *ISO 646 NRC */ #define FC_NOASCII 9 /* Norwegian and Danish ISO 646 NRC */ #define FC_POASCII 10 /* Portuguese ISO 646 NRC */ #define FC_SPASCII 11 /* Spanish ISO 646 NRC */ #define FC_SWASCII 12 /* Swedish ISO 646 NRC */ #define FC_CHASCII 13 /* Swiss ISO 646 NRC */ /* 8-bit Roman character sets */ #define FC_1LATIN 14 /* ISO 8859-1 Latin Alphabet 1 */ #define FC_2LATIN 15 /* ISO 8859-2 Latin Alphabet 2 */ #define FC_DECMCS 16 /* DEC Multinational Character Set */ #define FC_NEXT 17 /* NeXT workstation character set */ #define FC_CP437 18 /* IBM PC Code Page 437 */ #define FC_CP850 19 /* IBM PC Code Page 850 */ #define FC_CP852 20 /* IBM PC Code Page 852 */ #define FC_APPQD 21 /* Apple Quickdraw */ #define FC_DGMCS 22 /* Data General International Character Set */ #define FC_HPR8 23 /* HP Roman8 */ /* Cyrillic sets */ #define FC_CYRILL 24 /* ISO 8859-5 Latin/Cyrillic */ #define FC_CP866 25 /* PC Code Page 866 Cyrillic */ #define FC_KOI7 26 /* KOI-7 = Short KOI */ #define FC_KOI8 27 /* KOI-8 */ /* Japanese sets */ #define FC_JIS7 28 /* JIS-7 */ #define FC_SHJIS 29 /* Shifted JIS = CP932 */ #define FC_JEUC 30 /* Japanese EUC (JAE) */ #define FC_JDEC 31 /* Japanese DEC Kanji */ /* Hebrew sets */ #define FC_HE7 32 /* 7-Bit DEC Hebrew */ #define FC_HEBREW 33 /* 8-Bit ISO 8859-8 Latin/Hebrew */ #define FC_CP862 34 /* Hebrew PC Code Page */ /* Greek sets */ #define FC_ELOT 35 /* 7-Bit ELOT 927 Greek */ #define FC_GREEK 36 /* 8-Bit ISO 8859-7 Latin/Greek */ #define FC_CP869 37 /* Greek PC Code Page */ /* New Roman sets with Euro symbol */ #define FC_9LATIN 38 /* ISO 8859-15 Latin Alphabet 9 */ #define FC_CP923 38 /* Same as Latin-9 */ #define FC_CP858 39 /* Western Europe with Euro */ /* Other new additions */ #define FC_CP855 40 /* Cyrillic PC Code Page */ #define FC_CP1251 41 /* Cyrillic Windows */ #define FC_BULGAR 42 /* Bulgarian PC code page */ #define FC_CP1250 43 /* Latin 2 Windows (different from Latin-2)*/ #define FC_MAZOVIA 44 /* Polish Mazovia PC code page */ /* Unicode */ #define FC_UCS2 45 /* ISO-10646 / Unicode UCS-2 */ #define FC_UTF8 46 /* ISO-10646 / Unicode UTF-8 */ /* Recent additions */ #define FC_KOI8R 47 /* KOI8-R (RFC1489) - Russian + boxdrawing */ #define FC_KOI8U 48 /* KOI8-U (RFC2319) - Ukrainian + boxdrawing */ #define FC_CP1252 49 /* Latin 1 Windows */ #define MAXFCSETS 49 /* Highest file character set number */ #ifdef OS2 #define FC_DECSPEC 253 /* Not real character-sets */ #define FC_DECTECH 252 #endif /* OS2 */ #ifdef UNICODE _PROTOTYP( VOID initxlate, (int, int) ); #endif /* UNICODE */ #endif /* CKUXLA_H */ ckwart.c0000644000015300001460000004035711266107733011337 0ustar fdckermit#include "ckcsym.h" char *wartv = "Wart Version 2.14, 10 Nov 1999"; #define CKWART_C #ifdef MDEBUG /* Use the real ones in this module only */ #ifdef malloc #undef malloc #endif /* malloc */ #ifdef calloc #undef calloc #endif /* calloc */ #ifdef realloc #undef realloc #endif /* realloc */ #ifdef free #undef free #endif /* free */ #endif /* MDEBUG */ #ifdef MAC #define VOID void #endif /* MAC */ /* W A R T */ /* A small subset of "lex". Authors: Jeff Damens, Frank da Cruz Columbia University Center for Computing Activites. First released November 1984. Copyright (C) 1984, 2009, Trustees of Columbia University in the City of New York. All rights reserved. See the C-Kermit COPYING.TXT file or the copyright text in the ckcmai.c module for disclaimer and permissions. */ /* * input format is: * lines to be copied | %state * %% * | CHAR { actions } * ... * %% * more lines to be copied */ #include "ckcdeb.h" /* Includes */ #ifdef STRATUS /* Actually call printf, not our printf-catcher for Kermit */ #ifdef printf #undef printf #endif /* printf */ #ifdef fprintf #undef fprintf #endif /* fprintf */ #endif /* STRATUS */ #ifdef MAC /* Same deal for Macintosh */ #ifdef printf #undef printf #endif /* printf */ #ifdef fprintf #undef fprintf #endif /* fprintf */ #endif /* MAC */ #ifdef UNIX /* And UNIX */ #ifdef printf #undef printf #endif /* printf */ #ifdef fprintf #undef fprintf #endif /* fprintf */ #endif /* UNIX */ /* The following "char" should be changed to "short", "int", or "long" if your wart program will generate more than 127 states. Since wart is used mainly with C-Kermit, which has about 80 states, "char" is adequate. This keeps the program about 3K-4K smaller, which can be critical on 16-bit architectures. */ #ifdef IRIX60 /* Also use short or int if your compiler complains inordinately about "integer conversion resulted in a change of sign"... */ #define TBL_TYPE "short" /* C data type of state table */ #else #define TBL_TYPE "char" /* C data type of state table */ #endif /* IRIX60 */ #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 wart function in generated program */ #ifndef FNAME #define FNAME "wart" #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 */ _PROTOTYP( VOID setwstate, (int, trans) ); _PROTOTYP( int teststate, (int, trans) ); _PROTOTYP( trans rdinput, (FILE *, FILE *) ); _PROTOTYP( VOID initial, (FILE *, FILE *) ); _PROTOTYP( int isin, (char *, int) ); _PROTOTYP( int isword, (int) ); _PROTOTYP( VOID rdword, (FILE *, char *) ); _PROTOTYP( VOID rdstates, (FILE *, FILE *) ); _PROTOTYP( trans newtrans, (void) ); _PROTOTYP( trans rdrules, (FILE *, FILE *) ); _PROTOTYP( VOID statelist, (FILE *, trans) ); _PROTOTYP( VOID copyact, (FILE *, FILE *, int) ); _PROTOTYP( int faction, (trans, int, int) ); _PROTOTYP( VOID emptytbl, (void) ); _PROTOTYP( VOID addaction, (int, int, int) ); _PROTOTYP( VOID writetbl, (FILE *) ); _PROTOTYP( VOID warray, (FILE *, char *, int [], int, char *) ); _PROTOTYP( VOID prolog, (FILE *) ); _PROTOTYP( VOID epilogue, (FILE *) ); _PROTOTYP( VOID copyrest, (FILE *, FILE *) ); _PROTOTYP( int gettoken, (FILE *) ); _PROTOTYP( VOID rdcmnt, (FILE *) ); _PROTOTYP( VOID clrhash, (void) ); _PROTOTYP( int hash, (char *) ); _PROTOTYP( VOID enter, (char *, int) ); _PROTOTYP( int lkup, (char *) ); _PROTOTYP( static char* copy, (char *s) ); /* Variables and tables */ /* lt 1992-10-08 Begin * provide definition for deblog variable * ckcdeb.h declares as extern. DECC AXP is strict about ref/def model * Variable is unused herein, to the best of my knowledge. */ #ifdef VMS int deblog; #endif /* VMS */ /* lt 1992-10-08 End */ static int lines, nstates, nacts; static char tokval[MAXWORD]; static int tbl[MAXSTATES*96]; 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\ debug(F000,\"PROTO input\",ckitoa(state),c+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,"Wart preprocessor. */\n"); fprintf(outfp,"%c* Do not edit this file; edit the Wart-format ",'/'); fprintf(outfp,"source file instead, */\n"); fprintf(outfp,"%c* and then run it through Wart to produce a new ",'/'); fprintf(outfp,"C source file. */\n\n"); fprintf(outfp,"%c* Wart Version Info: */\n",'/'); fprintf(outfp,"char *wartv = \"%s\";\n\n",wartv); 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(GOOD_EXIT); } /* * fatal error handler * */ VOID fatal(msg) char *msg; { fprintf(stderr,"error in line %d: %s\n",lines,msg); exit(BAD_EXIT); } 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 wart * * 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); }